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> /// 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; // Initialize 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> /// Tells the caster to perform the tasks necessary to update the /// edge data's light listing. Can be overridden if the subclass needs /// to do additional things. /// </summary> /// <param name="edgeData">The edge information to update.</param> /// <param name="lightPosition">4D vector representing the light, a directional light has w=0.0.</param> protected virtual void UpdateEdgeListLightFacing( EdgeData edgeData, Vector4 lightPosition ) { edgeData.UpdateTriangleLightFacing( lightPosition ); }
/// <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> /// Tells the caster to perform the tasks necessary to update the /// edge data's light listing. Can be overridden if the subclass needs /// to do additional things. /// </summary> /// <param name="edgeData">The edge information to update.</param> /// <param name="lightPosition">4D vector representing the light, a directional light has w=0.0.</param> protected virtual void UpdateEdgeListLightFacing(EdgeData edgeData, Vector4 lightPosition) { edgeData.UpdateTriangleLightFacing(lightPosition); }
public void Build( bool stencilShadows, int logLevel ) { // Create a node this.node = this.sceneMgr.RootSceneNode.CreateChildSceneNode( name, this.center ); this.node.AttachObject( this ); // We need to create enough LOD buckets to deal with the highest LOD // we encountered in all the meshes queued for ( ushort lod = 0; lod < this.lodValues.Count; ++lod ) { var lodBucket = new LODBucket( this, lod, (float)this.lodValues[ lod ] ); this.lodBucketList.Add( lodBucket ); // Now iterate over the meshes and assign to LODs // LOD bucket will pick the right LOD to use IEnumerator iter = this.queuedSubMeshes.GetEnumerator(); while ( iter.MoveNext() ) { var qsm = (QueuedSubMesh)iter.Current; lodBucket.Assign( qsm, lod ); } // now build lodBucket.Build( stencilShadows, logLevel ); } // Do we need to build an edge list? if ( stencilShadows ) { var eb = new EdgeListBuilder(); //int vertexSet = 0; foreach ( var lod in this.lodBucketList ) { foreach ( var mat in lod.MaterialBucketMap.Values ) { // Check if we have vertex programs here var t = mat.Material.GetBestTechnique(); if ( null != t ) { var p = t.GetPass( 0 ); if ( null != p ) { if ( p.HasVertexProgram ) { this.vertexProgramInUse = true; } } } foreach ( var geom in mat.GeometryBucketList ) { // Check we're dealing with 16-bit indexes here // Since stencil shadows can only deal with 16-bit // More than that and stencil is probably too CPU-heavy // in any case if ( geom.IndexData.indexBuffer.Type != IndexType.Size16 ) { throw new AxiomException( "Only 16-bit indexes allowed when using stencil shadows" ); } eb.AddVertexData( geom.VertexData ); eb.AddIndexData( geom.IndexData ); } } } this.edgeList = eb.Build(); } }
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")); }
// Returns the number of geometry buckets public int Build(bool stencilShadows, bool logDetails) { int bucketCount = 0; // Create a node node = sceneMgr.RootSceneNode.CreateChildSceneNode(name, center); node.AttachObject(this); // We need to create enough LOD buckets to deal with the highest LOD // we encountered in all the meshes queued for(ushort lod = 0; lod < lodSquaredDistances.Count; ++lod) { LODBucket lodBucket = new LODBucket(this, lod, (float)lodSquaredDistances[lod]); lodBucketList.Add(lodBucket); // Now iterate over the meshes and assign to LODs // LOD bucket will pick the right LOD to use foreach (QueuedSubMesh qsm in queuedSubMeshes) lodBucket.Assign(qsm, lod); // now build bucketCount += lodBucket.Build(stencilShadows, logDetails); } // Do we need to build an edge list? if(stencilShadows) { EdgeListBuilder eb = new EdgeListBuilder(); foreach (LODBucket lod in lodBucketList) { foreach(MaterialBucket mat in lod.MaterialBucketMap.Values) { // Check if we have vertex programs here Technique t = mat.Material.GetBestTechnique(); if(null != t) { Pass p = t.GetPass(0); if(null != p) { if(p.HasVertexProgram) { vertexProgramInUse = true; } } } foreach (GeometryBucket geom in mat.GeometryBucketList) { bucketCount++; // Check we're dealing with 16-bit indexes here // Since stencil shadows can only deal with 16-bit // More than that and stencil is probably too CPU-heavy // in any case if(geom.IndexData.indexBuffer.Type != IndexType.Size16) throw new AxiomException("Only 16-bit indexes allowed when using stencil shadows"); eb.AddVertexData(geom.VertexData); eb.AddIndexData(geom.IndexData); } } } edgeList = eb.Build(); } return bucketCount; }