/// <summary> /// /// </summary> protected void ConnectEdges() { int triIndex = 0; for (int i = 0; i < edgeData.triangles.Count; i++, triIndex++) { EdgeData.Triangle tri = (EdgeData.Triangle)edgeData.triangles[i]; EdgeData.Edge e = null; if (tri.sharedVertIndex[0] > tri.sharedVertIndex[1]) { e = FindEdge(tri.sharedVertIndex[1], tri.sharedVertIndex[0]); if (e != null) { e.triIndex[1] = triIndex; e.isDegenerate = false; } } if (tri.sharedVertIndex[1] > tri.sharedVertIndex[2]) { // Find the existing edge (should be reversed order) e = FindEdge(tri.sharedVertIndex[2], tri.sharedVertIndex[1]); if (e != null) { e.triIndex[1] = triIndex; e.isDegenerate = false; } } if (tri.sharedVertIndex[2] > tri.sharedVertIndex[0]) { e = FindEdge(tri.sharedVertIndex[0], tri.sharedVertIndex[2]); if (e != null) { e.triIndex[1] = triIndex; e.isDegenerate = false; } } } }
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!"); }
/// <summary> /// /// </summary> /// <param name="indexSet"></param> /// <param name="vertexSet"></param> protected void BuildTrianglesEdges(int indexSet, int vertexSet) { var indexData = (IndexData)indexDataList[indexSet]; var opType = operationTypes[indexSet]; var iterations = 0; switch (opType) { case OperationType.TriangleList: iterations = indexData.indexCount / 3; break; case OperationType.TriangleFan: case OperationType.TriangleStrip: iterations = indexData.indexCount - 2; break; } // locate postion element & the buffer to go with it var vertexData = (VertexData)vertexDataList[vertexSet]; var posElem = vertexData.vertexDeclaration.FindElementBySemantic(VertexElementSemantic.Position); var posBuffer = vertexData.vertexBufferBinding.GetBuffer(posElem.Source); var posPtr = posBuffer.Lock(BufferLocking.ReadOnly); var idxPtr = indexData.indexBuffer.Lock(BufferLocking.ReadOnly); #if !AXIOM_SAFE_ONLY unsafe #endif { var pBaseVertex = posPtr; var p16Idx = idxPtr.ToShortPointer(); var p32Idx = idxPtr.ToIntPointer(); // counters used for pointer indexing var count16 = 0; var count32 = 0; var triStart = this.edgeData.triangles.Count; // iterate over all the groups of 3 indices this.edgeData.triangles.Capacity = triStart + iterations; for (var t = 0; t < iterations; t++) { var tri = new EdgeData.Triangle(); tri.indexSet = indexSet; tri.vertexSet = vertexSet; var index = new int[3]; var v = new Vector3[3]; for (var i = 0; i < 3; i++) { // Standard 3-index read for tri list or first tri in strip / fan if (opType == OperationType.TriangleList || t == 0) { if (indexData.indexBuffer.Type == IndexType.Size32) { index[i] = p32Idx[count32++]; } else { index[i] = p16Idx[count16++]; } } else { // Strips and fans are formed from last 2 indexes plus the // current one for triangles after the first if (indexData.indexBuffer.Type == IndexType.Size32) { index[i] = p32Idx[i - 2]; } else { index[i] = p16Idx[i - 2]; } // Perform single-index increment at the last tri index if (i == 2) { if (indexData.indexBuffer.Type == IndexType.Size32) { count32++; } else { count16++; } } } // populate tri original vertex index tri.vertIndex[i] = index[i]; // Retrieve the vertex position var pVertex = pBaseVertex + (index[i] * posBuffer.VertexSize); var pReal = (pVertex + posElem.Offset).ToFloatPointer(); v[i].x = pReal[0]; v[i].y = pReal[1]; v[i].z = pReal[2]; // find this vertex in the existing vertex map, or create it tri.sharedVertIndex[i] = FindOrCreateCommonVertex(v[i], vertexSet, indexSet, index[i]); } // Calculate triangle normal (NB will require recalculation for // skeletally animated meshes) tri.normal = CalculateFaceNormal(v[0], v[1], v[2]); // Add triangle to list this.edgeData.triangles.Add(tri); try { // create edges from common list if (tri.sharedVertIndex[0] < tri.sharedVertIndex[1]) { CreateEdge(vertexSet, triStart + t, tri.vertIndex[0], tri.vertIndex[1], tri.sharedVertIndex[0], tri.sharedVertIndex[1]); } if (tri.sharedVertIndex[1] < tri.sharedVertIndex[2]) { CreateEdge(vertexSet, triStart + t, tri.vertIndex[1], tri.vertIndex[2], tri.sharedVertIndex[1], tri.sharedVertIndex[2]); } if (tri.sharedVertIndex[2] < tri.sharedVertIndex[0]) { CreateEdge(vertexSet, triStart + t, tri.vertIndex[2], tri.vertIndex[0], tri.sharedVertIndex[2], tri.sharedVertIndex[0]); } } catch (Exception ex) { //Debug.WriteLine(ex.ToString()); //Debug.WriteLine(ex.StackTrace); // unlock those buffers! indexData.indexBuffer.Unlock(); posBuffer.Unlock(); throw ex; } } // for iterations } // unsafe // unlock those buffers! indexData.indexBuffer.Unlock(); posBuffer.Unlock(); }
/// <summary> /// /// </summary> /// <param name="indexSet"></param> /// <param name="vertexSet"></param> protected void BuildTrianglesEdges(int indexSet, int vertexSet) { IndexData indexData = (IndexData)indexDataList[indexSet]; OperationType opType = operationTypes[indexSet]; int iterations = 0; switch (opType) { case OperationType.TriangleList: iterations = indexData.indexCount / 3; break; case OperationType.TriangleFan: case OperationType.TriangleStrip: iterations = indexData.indexCount - 2; break; } // locate postion element & the buffer to go with it VertexData vertexData = (VertexData)vertexDataList[vertexSet]; VertexElement posElem = vertexData.vertexDeclaration.FindElementBySemantic(VertexElementSemantic.Position); HardwareVertexBuffer posBuffer = vertexData.vertexBufferBinding.GetBuffer(posElem.Source); IntPtr posPtr = posBuffer.Lock(BufferLocking.ReadOnly); IntPtr idxPtr = indexData.indexBuffer.Lock(BufferLocking.ReadOnly); unsafe { byte* pBaseVertex = (byte*)posPtr.ToPointer(); short* p16Idx = null; int* p32Idx = null; // counters used for pointer indexing int count16 = 0; int count32 = 0; if(indexData.indexBuffer.Type == IndexType.Size16) { p16Idx = (short*)idxPtr.ToPointer(); } else { p32Idx = (int*)idxPtr.ToPointer(); } float* pReal = null; int triStart = edgeData.triangles.Count; // iterate over all the groups of 3 indices edgeData.triangles.Capacity = triStart + iterations; for(int t = 0; t < iterations; t++) { EdgeData.Triangle tri = new EdgeData.Triangle(); tri.indexSet = indexSet; tri.vertexSet = vertexSet; int[] index = new int[3]; Vector3[] v = new Vector3[3]; for(int i = 0; i < 3; i++) { // Standard 3-index read for tri list or first tri in strip / fan if (opType == OperationType.TriangleList || t == 0) { if (indexData.indexBuffer.Type == IndexType.Size32) { index[i] = p32Idx[count32++]; } else { index[i] = p16Idx[count16++]; } } else { // Strips and fans are formed from last 2 indexes plus the // current one for triangles after the first if (indexData.indexBuffer.Type == IndexType.Size32) { index[i] = p32Idx[i - 2]; } else { index[i] = p16Idx[i - 2]; } // Perform single-index increment at the last tri index if (i == 2) { if (indexData.indexBuffer.Type == IndexType.Size32) { count32++; } else { count16++; } } } // populate tri original vertex index tri.vertIndex[i] = index[i]; // Retrieve the vertex position byte* pVertex = pBaseVertex + (index[i] * posBuffer.VertexSize); pReal = (float*)(pVertex + posElem.Offset); v[i].x = *pReal++; v[i].y = *pReal++; v[i].z = *pReal++; // find this vertex in the existing vertex map, or create it tri.sharedVertIndex[i] = FindOrCreateCommonVertex(v[i], vertexSet, indexSet, index[i]); } // Calculate triangle normal (NB will require recalculation for // skeletally animated meshes) tri.normal = MathUtil.CalculateFaceNormal(v[0], v[1], v[2]); // Add triangle to list edgeData.triangles.Add(tri); try { // create edges from common list if (tri.sharedVertIndex[0] < tri.sharedVertIndex[1]) { CreateEdge(vertexSet, triStart + t, tri.vertIndex[0], tri.vertIndex[1], tri.sharedVertIndex[0], tri.sharedVertIndex[1]); } if (tri.sharedVertIndex[1] < tri.sharedVertIndex[2]) { CreateEdge(vertexSet, triStart + t, tri.vertIndex[1], tri.vertIndex[2], tri.sharedVertIndex[1], tri.sharedVertIndex[2]); } if (tri.sharedVertIndex[2] < tri.sharedVertIndex[0]) { CreateEdge(vertexSet, triStart + t, tri.vertIndex[2], tri.vertIndex[0], tri.sharedVertIndex[2], tri.sharedVertIndex[0]); } } catch (Exception ex) { //Debug.WriteLine(ex.ToString()); //Debug.WriteLine(ex.StackTrace); // unlock those buffers! indexData.indexBuffer.Unlock(); posBuffer.Unlock(); throw ex; } } // for iterations } // unsafe // unlock those buffers! indexData.indexBuffer.Unlock(); posBuffer.Unlock(); }