private void AttemptBuild() { // reset this.vertices.Clear(); this.uniqueEdges.Clear(); this.edgeData = new EdgeData(); // resize the edge group list to equal the number of vertex sets this.edgeData.edgeGroups.Capacity = vertexDataList.Count; // Initialize edge group data for (var i = 0; i < vertexDataList.Count; i++) { var group = new EdgeData.EdgeGroup(); group.vertexSet = i; group.vertexData = (VertexData)vertexDataList[i]; this.edgeData.edgeGroups.Add(group); } // Stage 1: Build triangles and initial edge list. for (int i = 0, indexSet = 0; i < indexDataList.Count; i++, indexSet++) { var vertexSet = (int)indexDataVertexDataSetList[i]; BuildTrianglesEdges(indexSet, vertexSet); } // Stage 2: Link edges. ConnectEdges(); //EdgeData.DebugLog(LogManager.Instance.CreateLog("EdgeListBuilder.log")); //DebugLog(LogManager.Instance.CreateLog("EdgeData.log")); }
/// <summary> /// /// </summary> /// <param name="sharedIndex1"></param> /// <param name="sharedIndex2"></param> /// <returns></returns> protected EdgeData.Edge FindEdge(int sharedIndex1, int sharedIndex2) { // Iterate over the existing edges for (int i = 0; i < edgeData.edgeGroups.Count; i++) { EdgeData.EdgeGroup edgeGroup = (EdgeData.EdgeGroup)edgeData.edgeGroups[i]; for (int j = 0; j < edgeGroup.edges.Count; j++) { EdgeData.Edge edge = (EdgeData.Edge)edgeGroup.edges[j]; if (edge.sharedVertIndex[0] == sharedIndex1 && edge.sharedVertIndex[1] == sharedIndex2) { return(edge); } } } // no edge found return(null); }
protected override void ReadEdgeList( BinaryReader reader ) { if ( !IsEOF( reader ) ) { var chunkID = ReadChunk( reader ); while ( !IsEOF( reader ) && chunkID == MeshChunkID.EdgeListLOD ) { // process single LOD var lodIndex = ReadShort( reader ); // If manual, no edge data here, loaded from manual mesh var isManual = ReadBool( reader ); // Only load in non-manual levels; others will be connected up by Mesh on demand if ( !isManual ) { var usage = mesh.GetLodLevel( lodIndex ); usage.EdgeData = new EdgeData(); var triCount = ReadInt( reader ); var edgeGroupCount = ReadInt( reader ); // TODO: Resize triangle list // TODO: Resize edge groups for ( var i = 0; i < triCount; i++ ) { var tri = new EdgeData.Triangle(); tri.indexSet = ReadInt( reader ); tri.vertexSet = ReadInt( reader ); tri.vertIndex[ 0 ] = ReadInt( reader ); tri.vertIndex[ 1 ] = ReadInt( reader ); tri.vertIndex[ 2 ] = ReadInt( reader ); tri.sharedVertIndex[ 0 ] = ReadInt( reader ); tri.sharedVertIndex[ 1 ] = ReadInt( reader ); tri.sharedVertIndex[ 2 ] = ReadInt( reader ); tri.normal = ReadVector4( reader ); usage.EdgeData.triangles.Add( tri ); } for ( var eg = 0; eg < edgeGroupCount; eg++ ) { chunkID = ReadChunk( reader ); if ( chunkID != MeshChunkID.EdgeListGroup ) { throw new AxiomException( "Missing EdgeListGroup chunk." ); } var edgeGroup = new EdgeData.EdgeGroup(); edgeGroup.vertexSet = ReadInt( reader ); var edgeCount = ReadInt( reader ); // TODO: Resize the edge group list for ( var e = 0; e < edgeCount; e++ ) { var edge = new EdgeData.Edge(); edge.triIndex[ 0 ] = ReadInt( reader ); edge.triIndex[ 1 ] = ReadInt( reader ); edge.vertIndex[ 0 ] = ReadInt( reader ); edge.vertIndex[ 1 ] = ReadInt( reader ); edge.sharedVertIndex[ 0 ] = ReadInt( reader ); edge.sharedVertIndex[ 1 ] = ReadInt( reader ); edge.isDegenerate = ReadBool( reader ); // add the edge to the list edgeGroup.edges.Add( edge ); } // Populate edgeGroup.vertexData references // If there is shared vertex data, vertexSet 0 is that, // otherwise 0 is first dedicated if ( mesh.SharedVertexData != null ) { if ( edgeGroup.vertexSet == 0 ) { edgeGroup.vertexData = mesh.SharedVertexData; } else { edgeGroup.vertexData = mesh.GetSubMesh( edgeGroup.vertexSet - 1 ).vertexData; } } else { edgeGroup.vertexData = mesh.GetSubMesh( edgeGroup.vertexSet ).vertexData; } // add the edge group to the list usage.EdgeData.edgeGroups.Add( edgeGroup ); } } // grab the next chunk if ( !IsEOF( reader ) ) { chunkID = ReadChunk( reader ); } } // grab the next chunk if ( !IsEOF( reader ) ) { // backpedal to the start of chunk Seek( reader, -ChunkOverheadSize ); } } mesh.IsEdgeListBuilt = true; }
/// <summary> /// Generates the indexes required to render a shadow volume into the /// index buffer which is passed in, and updates shadow renderables to use it. /// </summary> /// <param name="edgeData">The edge information to use.</param> /// <param name="indexBuffer">The buffer into which to write data into; current /// contents are assumed to be discardable.</param> /// <param name="light">The light, mainly for type info as silhouette calculations /// should already have been done in <see cref="UpdateEdgeListLightFacing"/></param> /// <param name="shadowRenderables">A list of shadow renderables which has /// already been constructed but will need populating with details of /// the index ranges to be used.</param> /// <param name="flags">Additional controller flags, see <see cref="ShadowRenderableFlags"/>.</param> protected virtual void GenerateShadowVolume(EdgeData edgeData, HardwareIndexBuffer indexBuffer, Light light, ShadowRenderableList shadowRenderables, int flags) { // Edge groups should be 1:1 with shadow renderables Debug.Assert(edgeData.edgeGroups.Count == shadowRenderables.Count); LightType lightType = light.Type; bool extrudeToInfinity = (flags & (int)ShadowRenderableFlags.ExtrudeToInfinity) > 0; // Lock index buffer for writing IntPtr idxPtr = indexBuffer.Lock(BufferLocking.Discard); int indexStart = 0; unsafe { // TODO: Will currently cause an overflow for 32 bit indices, revisit short *pIdx = (short *)idxPtr.ToPointer(); int count = 0; // Iterate over the groups and form renderables for each based on their // lightFacing for (int groupCount = 0; groupCount < edgeData.edgeGroups.Count; groupCount++) { EdgeData.EdgeGroup eg = (EdgeData.EdgeGroup)edgeData.edgeGroups[groupCount]; ShadowRenderable si = (ShadowRenderable)shadowRenderables[groupCount]; RenderOperation lightShadOp = null; // Initialise the index bounds for this shadow renderable RenderOperation shadOp = si.GetRenderOperationForUpdate(); shadOp.indexData.indexCount = 0; shadOp.indexData.indexStart = indexStart; // original number of verts (without extruded copy) int originalVertexCount = eg.vertexData.vertexCount; bool firstDarkCapTri = true; int darkCapStart = 0; for (int edgeCount = 0; edgeCount < eg.edges.Count; edgeCount++) { EdgeData.Edge edge = (EdgeData.Edge)eg.edges[edgeCount]; EdgeData.Triangle t1 = (EdgeData.Triangle)edgeData.triangles[edge.triIndex[0]]; EdgeData.Triangle t2 = edge.isDegenerate ? (EdgeData.Triangle)edgeData.triangles[edge.triIndex[0]] : (EdgeData.Triangle)edgeData.triangles[edge.triIndex[1]]; if (t1.lightFacing && (edge.isDegenerate || !t2.lightFacing)) { /* Silhouette edge, first tri facing the light * Also covers degenerate tris where only tri 1 is valid * Remember verts run anticlockwise along the edge from * tri 0 so to point shadow volume tris outward, light cap * indexes have to be backwards * * We emit 2 tris if light is a point light, 1 if light * is directional, because directional lights cause all * points to converge to a single point at infinity. * * First side tri = near1, near0, far0 * Second tri = far0, far1, near1 * * 'far' indexes are 'near' index + originalVertexCount * because 'far' verts are in the second half of the * buffer */ pIdx[count++] = (short)edge.vertIndex[1]; pIdx[count++] = (short)edge.vertIndex[0]; pIdx[count++] = (short)(edge.vertIndex[0] + originalVertexCount); shadOp.indexData.indexCount += 3; if (!(lightType == LightType.Directional && extrudeToInfinity)) { // additional tri to make quad pIdx[count++] = (short)(edge.vertIndex[0] + originalVertexCount); pIdx[count++] = (short)(edge.vertIndex[1] + originalVertexCount); pIdx[count++] = (short)edge.vertIndex[1]; shadOp.indexData.indexCount += 3; } // Do dark cap tri // Use McGuire et al method, a triangle fan covering all silhouette // edges and one point (taken from the initial tri) if ((flags & (int)ShadowRenderableFlags.IncludeDarkCap) > 0) { if (firstDarkCapTri) { darkCapStart = edge.vertIndex[0] + originalVertexCount; firstDarkCapTri = false; } else { pIdx[count++] = (short)darkCapStart; pIdx[count++] = (short)(edge.vertIndex[1] + originalVertexCount); pIdx[count++] = (short)(edge.vertIndex[0] + originalVertexCount); shadOp.indexData.indexCount += 3; } } } else if (!t1.lightFacing && (edge.isDegenerate || t2.lightFacing)) { // Silhouette edge, second tri facing the light // Note edge indexes inverse of when t1 is light facing pIdx[count++] = (short)edge.vertIndex[0]; pIdx[count++] = (short)edge.vertIndex[1]; pIdx[count++] = (short)(edge.vertIndex[1] + originalVertexCount); shadOp.indexData.indexCount += 3; if (!(lightType == LightType.Directional && extrudeToInfinity)) { // additional tri to make quad pIdx[count++] = (short)(edge.vertIndex[1] + originalVertexCount); pIdx[count++] = (short)(edge.vertIndex[0] + originalVertexCount); pIdx[count++] = (short)edge.vertIndex[0]; shadOp.indexData.indexCount += 3; } // Do dark cap tri // Use McGuire et al method, a triangle fan covering all silhouette // edges and one point (taken from the initial tri) if ((flags & (int)ShadowRenderableFlags.IncludeDarkCap) > 0) { if (firstDarkCapTri) { darkCapStart = edge.vertIndex[1] + originalVertexCount; firstDarkCapTri = false; } else { pIdx[count++] = (short)darkCapStart; pIdx[count++] = (short)(edge.vertIndex[0] + originalVertexCount); pIdx[count++] = (short)(edge.vertIndex[1] + originalVertexCount); shadOp.indexData.indexCount += 3; } } } } // Do light cap if ((flags & (int)ShadowRenderableFlags.IncludeLightCap) > 0) { ShadowRenderable lightCapRend = null; if (si.IsLightCapSeperate) { // separate light cap lightCapRend = si.LightCapRenderable; lightShadOp = lightCapRend.GetRenderOperationForUpdate(); lightShadOp.indexData.indexCount = 0; // start indexes after the current total // NB we don't update the total here since that's done below lightShadOp.indexData.indexStart = indexStart + shadOp.indexData.indexCount; } for (int triCount = 0; triCount < edgeData.triangles.Count; triCount++) { EdgeData.Triangle t = (EdgeData.Triangle)edgeData.triangles[triCount]; // Light facing, and vertex set matches if (t.lightFacing && t.vertexSet == eg.vertexSet) { pIdx[count++] = (short)t.vertIndex[0]; pIdx[count++] = (short)t.vertIndex[1]; pIdx[count++] = (short)t.vertIndex[2]; if (lightShadOp != null) { lightShadOp.indexData.indexCount += 3; } else { shadOp.indexData.indexCount += 3; } } } } // update next indexStart (all renderables sharing the buffer) indexStart += shadOp.indexData.indexCount; // add on the light cap too if (lightShadOp != null) { indexStart += lightShadOp.indexData.indexCount; } } } // Unlock index buffer indexBuffer.Unlock(); Debug.Assert(indexStart <= indexBuffer.IndexCount, "Index buffer overrun while generating shadow volume!"); }
private void AttemptBuild() { // reset vertices.Clear(); uniqueEdges.Clear(); edgeData = new EdgeData(); // resize the edge group list to equal the number of vertex sets edgeData.edgeGroups.Capacity = vertexDataList.Count; // Initialize edge group data for (int i = 0; i < vertexDataList.Count; i++) { EdgeData.EdgeGroup group = new EdgeData.EdgeGroup(); group.vertexSet = i; group.vertexData = (VertexData)vertexDataList[i]; edgeData.edgeGroups.Add(group); } // Stage 1: Build triangles and initial edge list. for (int i = 0, indexSet = 0; i < indexDataList.Count; i++, indexSet++) { int vertexSet = (int)indexDataVertexDataSetList[i]; BuildTrianglesEdges(indexSet, vertexSet); } // Stage 2: Link edges. ConnectEdges(); //edgeData.DebugLog(LogManager.Instance.CreateLog("EdgeListBuilder.log")); //DebugLog(LogManager.Instance.CreateLog("EdgeData.log")); }