This class contains the information required to describe the edge connectivity of a given set of vertices and indexes.
Exemple #1
0
        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"));
        }
Exemple #2
0
		/// <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!" );
		}
Exemple #3
0
		/// <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);
 }
Exemple #6
0
			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;
        }