Describes the graphics API independent functionality required by a hardware index buffer.
NB subclasses should override lock, unlock, readData, writeData
Наследование: Axiom.Graphics.HardwareBuffer
Пример #1
0
        private void LoadLevelFaces(Quake3Level q3lvl)
        {
            //-----------------------------------------------------------------------
            // Faces
            //-----------------------------------------------------------------------
            leafFaceGroups = new int[q3lvl.LeafFaces.Length];
            Array.Copy(q3lvl.LeafFaces, 0, leafFaceGroups, 0, leafFaceGroups.Length);

            faceGroups = new BspStaticFaceGroup[q3lvl.Faces.Length];

            // Set up index buffer
            // NB Quake3 indexes are 32-bit
            // Copy the indexes into a software area for staging
            numIndexes = q3lvl.NumElements + patchIndexCount;

            // Create an index buffer manually in system memory, allow space for patches
            indexes = HardwareBufferManager.Instance.CreateIndexBuffer(
                IndexType.Size32,
                numIndexes,
                BufferUsage.Dynamic
                );

            // Write main indexes
            indexes.WriteData(0, Marshal.SizeOf(typeof(uint)) * q3lvl.NumElements, q3lvl.Elements, true);
        }
Пример #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!" );
		}
        /// <summary>
        ///     Tells the system to build the mesh relating to the surface into externally created buffers.
        /// </summary>
        /// <remarks>
        ///     The VertexDeclaration of the vertex buffer must be identical to the one passed into
        ///     <see cref="DefineSurface"/>.  In addition, there must be enough space in the buffer to 
        ///     accommodate the patch at full detail level; you should check <see cref="RequiredVertexCount"/>
        ///     and <see cref="RequiredIndexCount"/> to determine this. This method does not create an internal
        ///     mesh for this patch and so GetMesh will return null if you call it after building the
        ///     patch this way.
        /// </remarks>
        /// <param name="destVertexBuffer">The destination vertex buffer in which to build the patch.</param>
        /// <param name="vertexStart">The offset at which to start writing vertices for this patch.</param>
        /// <param name="destIndexBuffer">The destination index buffer in which to build the patch.</param>
        /// <param name="indexStart">The offset at which to start writing indexes for this patch.</param>
        public void Build(HardwareVertexBuffer destVertexBuffer, int vertexStart, HardwareIndexBuffer destIndexBuffer, int indexStart)
        {
            if(controlPoints.Count == 0) {
                return;
            }

            vertexBuffer = destVertexBuffer;
            vertexOffset = vertexStart;
            indexBuffer = destIndexBuffer;
            indexOffset = indexStart;

            // lock just the region we are interested in
            IntPtr lockedBuffer = vertexBuffer.Lock(
                vertexOffset * declaration.GetVertexSize(0),
                requiredVertexCount * declaration.GetVertexSize(0),
                BufferLocking.NoOverwrite);

            DistributeControlPoints(lockedBuffer);

            // subdivide the curves to the max
            // Do u direction first, so need to step over v levels not done yet
            int vStep = 1 << maxVLevel;
            int uStep = 1 << maxULevel;

            // subdivide this row in u
            for(int v = 0; v < meshHeight; v += vStep) {
                SubdivideCurve(lockedBuffer, v * meshWidth, uStep, meshWidth / uStep, uLevel);
            }

            // Now subdivide in v direction, this time all the u direction points are there so no step
            for(int u = 0; u < meshWidth; u++) {
                SubdivideCurve(lockedBuffer, u, vStep * meshWidth, meshHeight / vStep, vLevel);
            }

            // don't forget to unlock!
            vertexBuffer.Unlock();

            // Make triangles from mesh at this current level of detail
            MakeTriangles();
        }
Пример #4
0
	    /// <summary>
	    ///		Gets an iterator over the renderables required to render the shadow volume.
	    /// </summary>
	    /// <remarks>
	    ///		Shadowable geometry should ideally be designed such that there is only one
	    ///		ShadowRenderable required to render the the shadow; however this is not a necessary
	    ///		limitation and it can be exceeded if required.
	    /// </remarks>
	    /// <param name="technique">The technique being used to generate the shadow.</param>
	    /// <param name="light">The light to generate the shadow from.</param>
	    /// <param name="indexBuffer">The index buffer to build the renderables into,
	    /// the current contents are assumed to be disposable.</param>
	    /// <param name="extrudeVertices">If true, this means this class should extrude
	    /// the vertices of the back of the volume in software. If false, it
	    /// will not be done (a vertex program is assumed).</param>
	    /// <param name="extrusionDistance"></param>
	    /// <param name="flags">Technique-specific flags, see <see cref="ShadowRenderableFlags"/></param>
	    /// <returns>An iterator that will allow iteration over all renderables for the full shadow volume.</returns>
	    public abstract IEnumerator GetShadowVolumeRenderableEnumerator( ShadowTechnique technique, Light light,
			HardwareIndexBuffer indexBuffer, bool extrudeVertices, float extrusionDistance, int flags );
Пример #5
0
		public IEnumerator GetShadowVolumeRenderableEnumerator( ShadowTechnique technique, Light light,
			HardwareIndexBuffer indexBuffer, float extrusionDistance, bool extrudeVertices )
		{
			return GetShadowVolumeRenderableEnumerator( technique, light, indexBuffer, extrudeVertices, extrusionDistance, 0 );
		}
Пример #6
0
			public EntityShadowRenderable( Entity parent, HardwareIndexBuffer indexBuffer, VertexData vertexData,
			                               bool createSeperateLightCap, SubEntity subEntity )
				: this( parent, indexBuffer, vertexData, createSeperateLightCap, subEntity, false )
			{
			}
        /// <summary>
        ///   Populate the index buffer with the information in the data array
        /// </summary>
        /// <param name="idxBuffer">HardwareIndexBuffer to populate</param>
        /// <param name="indexCount">the number of indices</param>
        /// <param name="indexType">the type of index (e.g. IndexType.Size16)</param>
        /// <param name="data">the data to fill the buffer</param>
        internal void FillBuffer( HardwareIndexBuffer idxBuffer, int indexCount, IndexType indexType, int[ , ] data )
        {
            int faceCount = data.GetLength( 0 );
            int count = data.GetLength( 1 );

            IntPtr indices = idxBuffer.Lock( BufferLocking.Discard );

            if( indexType == IndexType.Size32 )
            {
                // read the ints into the buffer data
                unsafe
                {
                    int* pInts = (int*) indices.ToPointer();
                    for( int i = 0; i < faceCount; ++i )
                        for( int j = 0; j < count; ++j )
                        {
                            Debug.Assert( i * count + j < indexCount, "Wrote off end of index buffer" );
                            pInts[ i * count + j ] = data[ i, j ];
                        }
                }
            }
            else
            {
                // read the shorts into the buffer data
                unsafe
                {
                    short* pShorts = (short*) indices.ToPointer();
                    for( int i = 0; i < faceCount; ++i )
                        for( int j = 0; j < count; ++j )
                        {
                            Debug.Assert( i * count + j < indexCount, "Wrote off end of index buffer" );
                            pShorts[ i * count + j ] = (short) data[ i, j ];
                        }
                }
            }
            // unlock the buffer to commit
            idxBuffer.Unlock();
        }
        private static void ReadBuffer(HardwareIndexBuffer idxBuffer, int maxIndex, IndexType indexType,
						ref int[,] data)
        {
            IntPtr indices = idxBuffer.Lock(BufferLocking.ReadOnly);

            int faceCount = data.GetLength(0);

            if (indexType == IndexType.Size32) {
                // read the ints from the buffer data
                unsafe {
                    int* pInts = (int*)indices.ToPointer();
                    for (int i = 0; i < faceCount; ++i)
                        for (int j = 0; j < 3; ++j) {
                            Debug.Assert(i * 3 + j < maxIndex, "Read off end of index buffer");
                            data[i, j] = pInts[i * 3 + j];
                        }
                }
            } else {
                // read the shorts from the buffer data
                unsafe {
                    short* pShorts = (short*)indices.ToPointer();
                    for (int i = 0; i < faceCount; ++i)
                        for (int j = 0; j < 3; ++j) {
                            Debug.Assert(i * 3 + j < maxIndex, "Read off end of index buffer");
                            data[i, j] = pShorts[i * 3 + j];
                        }
                }
            }

            // unlock the buffer
            idxBuffer.Unlock();
        }
Пример #9
0
 /// <summary>
 ///		Gets an iterator over the renderables required to render the shadow volume.
 /// </summary>
 /// <remarks>
 ///		Shadowable geometry should ideally be designed such that there is only one
 ///		ShadowRenderable required to render the the shadow; however this is not a necessary
 ///		limitation and it can be exceeded if required.
 /// </remarks>
 /// <param name="technique">The technique being used to generate the shadow.</param>
 /// <param name="light">The light to generate the shadow from.</param>
 /// <param name="indexBuffer">The index buffer to build the renderables into,
 /// the current contents are assumed to be disposable.</param>
 /// <param name="extrudeVertices">If true, this means this class should extrude
 /// the vertices of the back of the volume in software. If false, it
 /// will not be done (a vertex program is assumed).</param>
 /// <param name="flags">Technique-specific flags, see <see cref="ShadowRenderableFlags"/></param>
 /// <returns>An iterator that will allow iteration over all renderables for the full shadow volume.</returns>
 public abstract IEnumerator GetShadowVolumeRenderableEnumerator(ShadowTechnique technique, Light light,
                                                                 HardwareIndexBuffer indexBuffer, bool extrudeVertices, float extrusionDistance, int flags);
Пример #10
0
				public RegionShadowRenderable( Region parent, HardwareIndexBuffer indexBuffer, VertexData vertexData,
				                               bool createSeparateLightCap )
					: this( parent, indexBuffer, vertexData, createSeparateLightCap, false )
				{
				}
        public override IEnumerator GetShadowVolumeRenderableEnumerator(ShadowTechnique technique, Light light, 
			HardwareIndexBuffer indexBuffer, bool extrudeVertices, float extrusionDistance, int flags)
        {
            return dummyList.GetEnumerator();
        }
Пример #12
0
				public RegionShadowRenderable( Region parent, HardwareIndexBuffer indexBuffer, VertexData vertexData,
				                               bool createSeparateLightCap, bool isLightCap )
				{
					throw new NotImplementedException();
				}
Пример #13
0
			public IEnumerator GetShadowVolumeRenderableIterator( ShadowTechnique shadowTechnique, Light light,
			                                                      HardwareIndexBuffer indexBuffer, bool extrudeVertices,
			                                                      float extrusionDistance, ulong flags )
			{
				Debug.Assert( indexBuffer != null, "Only external index buffers are supported right now" );
				Debug.Assert( indexBuffer.Type == IndexType.Size16, "Only 16-bit indexes supported for now" );

				// Calculate the object space light details
				var lightPos = light.GetAs4DVector();
				var world2Obj = parentNode.FullTransform.Inverse();
				lightPos = world2Obj*lightPos;

				// We need to search the edge list for silhouette edges
				if ( this.edgeList == null )
				{
					throw new Exception( "You enabled stencil shadows after the buid process!  In " +
					                     "Region.GetShadowVolumeRenderableIterator" );
				}

				// Init shadow renderable list if required
				var init = this.shadowRenderables.Count == 0;

				RegionShadowRenderable esr = null;
				//bool updatedSharedGeomNormals = false;
				for ( var i = 0; i < this.edgeList.EdgeGroups.Count; i++ )
				{
					var group = (EdgeData.EdgeGroup)this.edgeList.EdgeGroups[ i ];
					if ( init )
					{
						// Create a new renderable, create a separate light cap if
						// we're using a vertex program (either for this model, or
						// for extruding the shadow volume) since otherwise we can
						// get depth-fighting on the light cap
						esr = new RegionShadowRenderable( this, indexBuffer, group.vertexData, this.vertexProgramInUse || !extrudeVertices );
						this.shadowRenderables.Add( esr );
					}
					else
					{
						esr = (RegionShadowRenderable)this.shadowRenderables[ i ];
					}
					// Extrude vertices in software if required
					if ( extrudeVertices )
					{
						ExtrudeVertices( esr.PositionBuffer, group.vertexData.vertexCount, lightPos, extrusionDistance );
					}
				}
				return (IEnumerator)this.shadowRenderables;
			}
Пример #14
0
		/// <summary>
		///		/** Internal utility function for loading data from Quake3.
		/// </summary>
		protected void LoadQuake3Level( Quake3Level q3lvl )
		{
			ResourceGroupManager rgm = ResourceGroupManager.Instance;
			rgm.notifyWorldGeometryStageStarted( "Parsing entities" );
			LoadEntities( q3lvl );
			rgm.notifyWorldGeometryStageEnded();

			rgm.notifyWorldGeometryStageStarted( "Extracting lightmaps" );
			q3lvl.ExtractLightmaps();
			rgm.notifyWorldGeometryStageEnded();

			//-----------------------------------------------------------------------
			// Vertices
			//-----------------------------------------------------------------------
			// Allocate memory for vertices & copy
			vertexData = new VertexData();

			// Create vertex declaration
			VertexDeclaration decl = vertexData.vertexDeclaration;
			int offset = 0;
			int lightTexOffset = 0;
			decl.AddElement( 0, offset, VertexElementType.Float3, VertexElementSemantic.Position );
			offset += VertexElement.GetTypeSize( VertexElementType.Float3 );
			decl.AddElement( 0, offset, VertexElementType.Float3, VertexElementSemantic.Normal );
			offset += VertexElement.GetTypeSize( VertexElementType.Float3 );
			decl.AddElement( 0, offset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 0 );
			offset += VertexElement.GetTypeSize( VertexElementType.Float2 );
			decl.AddElement( 0, offset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 1 );

			// Build initial patches - we need to know how big the vertex buffer needs to be
			// to accommodate the subdivision
			// we don't want to include the elements for texture lighting, so we clone it
			rgm.notifyWorldGeometryStageStarted( "Initializing patches" );
			InitQuake3Patches( q3lvl, (VertexDeclaration)decl.Clone() );
			rgm.notifyWorldGeometryStageEnded();

			// this is for texture lighting color and alpha
			decl.AddElement( 1, lightTexOffset, VertexElementType.Color, VertexElementSemantic.Diffuse );
			lightTexOffset += VertexElement.GetTypeSize( VertexElementType.Color );
			// this is for texture lighting coords
			decl.AddElement( 1, lightTexOffset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 2 );

			rgm.notifyWorldGeometryStageStarted( "Setting up vertex data" );
			// Create the vertex buffer, allow space for patches
			HardwareVertexBuffer vbuf = HardwareBufferManager.Instance.CreateVertexBuffer( decl.Clone(0), q3lvl.NumVertices + patchVertexCount,	BufferUsage.StaticWriteOnly /* the vertices will be read often for texture lighting, use shadow buffer */, true );

			// Create the vertex buffer for texture lighting, allow space for patches
			HardwareVertexBuffer texLightBuf = HardwareBufferManager.Instance.CreateVertexBuffer( decl.Clone(1), q3lvl.NumVertices + patchVertexCount, BufferUsage.DynamicWriteOnly, false );

			// COPY static vertex data - Note that we can't just block-copy the vertex data because we have to reorder
			// our vertex elements; this is to ensure compatibility with older cards when using
			// hardware vertex buffers - Direct3D requires that the buffer format maps onto a
			// FVF in those older drivers.
			// Lock just the non-patch area for now.

			unsafe
			{
				BspVertex vert = new BspVertex();
				TextureLightMap texLightMap = new TextureLightMap();

				// Keep another base pointer for use later in patch building
				for ( int v = 0; v < q3lvl.NumVertices; v++ )
				{
					QuakeVertexToBspVertex( q3lvl.Vertices[ v ], out vert, out texLightMap );

					BspVertex* bvptr = &vert;
					TextureLightMap* tlptr = &texLightMap;

					vbuf.WriteData(
						v * sizeof( BspVertex ),
						sizeof( BspVertex ),
						(IntPtr)bvptr
						);

					texLightBuf.WriteData(
						v * sizeof( TextureLightMap ),
						sizeof( TextureLightMap ),
						(IntPtr)tlptr
						);

				}
			}

			// Setup binding
			vertexData.vertexBufferBinding.SetBinding( 0, vbuf );

			// Setup texture lighting binding
			vertexData.vertexBufferBinding.SetBinding( 1, texLightBuf );

			// Set other data
			vertexData.vertexStart = 0;
			vertexData.vertexCount = q3lvl.NumVertices + patchVertexCount;
			rgm.notifyWorldGeometryStageEnded();

			//-----------------------------------------------------------------------
			// Faces
			//-----------------------------------------------------------------------
			rgm.notifyWorldGeometryStageStarted( "Setting up face data" );
			leafFaceGroups = new int[ q3lvl.LeafFaces.Length ];
			Array.Copy( q3lvl.LeafFaces, 0, leafFaceGroups, 0, leafFaceGroups.Length );

			faceGroups = new BspStaticFaceGroup[ q3lvl.Faces.Length ];

			// Set up index buffer
			// NB Quake3 indexes are 32-bit
			// Copy the indexes into a software area for staging
			numIndexes = q3lvl.NumElements + patchIndexCount;

			// Create an index buffer manually in system memory, allow space for patches
			indexes = HardwareBufferManager.Instance.CreateIndexBuffer(
				IndexType.Size32,
				numIndexes,
				BufferUsage.Dynamic
				);

			// Write main indexes
			indexes.WriteData( 0, Marshal.SizeOf( typeof( uint ) ) * q3lvl.NumElements, q3lvl.Elements, true );
			rgm.notifyWorldGeometryStageEnded();

			// now build patch information
			rgm.notifyWorldGeometryStageStarted( "Building patches" );
			BuildQuake3Patches( q3lvl.NumVertices, q3lvl.NumElements );
			rgm.notifyWorldGeometryStageEnded();

			//-----------------------------------------------------------------------
			// Create materials for shaders
			//-----------------------------------------------------------------------
			// NB this only works for the 'default' shaders for now
			// i.e. those that don't have a .shader script and thus default
			// to just texture + lightmap
			// TODO: pre-parse all .shader files and create lookup for next stage (use ROGL shader_file_t)

			// Material names are shadername#lightmapnumber
			// This is because I like to define materials up front completely
			// rather than combine lightmap and shader dynamically (it's
			// more generic). It results in more materials, but they're small
			// beer anyway. Texture duplication is prevented by infrastructure.
			// To do this I actually need to parse the faces since they have the
			// shader/lightmap combo (lightmap number is not in the shader since
			// it can be used with multiple lightmaps)
			string shaderName;
			int face = q3lvl.Faces.Length;
			int progressCountdown = 100;
			int progressCount = 0;

			while ( face-- > 0 )
			{
				// Progress reporting
				if ( progressCountdown == 100 )
				{
					++progressCount;
					String str = String.Format( "Loading materials (phase {0})", progressCount );
					rgm.notifyWorldGeometryStageStarted( str );
				}
				else if ( progressCountdown == 0 )
				{
					// stage report
					rgm.notifyWorldGeometryStageEnded();
					progressCountdown = 100 + 1;
				}

				// Check to see if existing material
				// Format shader#lightmap
				int shadIdx = q3lvl.Faces[ face ].shader;

				shaderName = String.Format( "{0}#{1}", q3lvl.Shaders[ shadIdx ].name, q3lvl.Faces[ face ].lmTexture );
				Material shadMat = (Material)MaterialManager.Instance.GetByName( shaderName );

				if ( shadMat == null && !bspOptions.useLightmaps )
				{
					// try the no-lightmap material
					shaderName = String.Format( "{0}#n", q3lvl.Shaders[ shadIdx ].name );
					shadMat = (Material)MaterialManager.Instance.GetByName( shaderName );
				}

				if ( shadMat == null )
				{
					// Color layer
					// NB no extension in Q3A(doh), have to try shader, .jpg, .tga
					string tryName = q3lvl.Shaders[ shadIdx ].name;

					// Try shader first
					Quake3Shader shader = (Quake3Shader)Quake3ShaderManager.Instance.GetByName( tryName );

					if ( shader != null )
					{
						shadMat = shader.CreateAsMaterial( q3lvl.Faces[ face ].lmTexture );
					}
					else
					{
						// No shader script, try default type texture
						shadMat = (Material)MaterialManager.Instance.Create( shaderName, rgm.WorldResourceGroupName );
						Pass shadPass = shadMat.GetTechnique( 0 ).GetPass( 0 );

						// Try jpg
						TextureUnitState tex = null;
						if ( ResourceGroupManager.Instance.ResourceExists( rgm.WorldResourceGroupName, tryName + ".jpg" ) )
						{
							tex = shadPass.CreateTextureUnitState( tryName + ".jpg" );
						}
						if ( ResourceGroupManager.Instance.ResourceExists( rgm.WorldResourceGroupName, tryName + ".tga" ) )
						{
							tex = shadPass.CreateTextureUnitState( tryName + ".tga" );
						}

						if ( tex != null )
						{
							// Set replace on all first layer textures for now
							tex.SetColorOperation( LayerBlendOperation.Replace );
                            tex.SetTextureAddressingMode( TextureAddressing.Wrap );
							// for ambient lighting
							tex.ColorBlendMode.source2 = LayerBlendSource.Manual;
						}

						if ( bspOptions.useLightmaps && q3lvl.Faces[ face ].lmTexture != -1 )
						{
							// Add lightmap, additive blending
							tex = shadPass.CreateTextureUnitState( String.Format( "@lightmap{0}", q3lvl.Faces[ face ].lmTexture ) );

							// Blend
							tex.SetColorOperation( LayerBlendOperation.Modulate );

							// Use 2nd texture co-ordinate set
							tex.TextureCoordSet = 1;

							// Clamp
                            tex.SetTextureAddressingMode( TextureAddressing.Clamp );
						}

						shadMat.CullingMode = CullingMode.None;
						shadMat.Lighting = false;
					}
				}

				shadMat.Load();

				// Copy face data
				BspStaticFaceGroup dest = new BspStaticFaceGroup();
				InternalBspFace src = q3lvl.Faces[ face ];

				if ( ( q3lvl.Shaders[ src.shader ].surfaceFlags & SurfaceFlags.Sky ) == SurfaceFlags.Sky )
					dest.isSky = true;
				else
					dest.isSky = false;

				dest.materialHandle = shadMat.Handle;
				dest.elementStart = src.elemStart;
				dest.numElements = src.elemCount;
				dest.numVertices = src.vertCount;
				dest.vertexStart = src.vertStart;
				dest.plane = new Plane();

				if ( Quake3ShaderManager.Instance.GetByName( q3lvl.Shaders[ shadIdx ].name ) != null )
				{
					// it's a quake shader
					dest.isQuakeShader = true;
				}

				if ( src.type == BspFaceType.Normal )
				{
					dest.type = FaceGroup.FaceList;

					// Assign plane
					dest.plane.Normal = new Vector3(
						src.normal[ 0 ],
						src.normal[ 1 ],
						src.normal[ 2 ]
						);
					dest.plane.D = -dest.plane.Normal.Dot(
						new Vector3(
						src.org[ 0 ],
						src.org[ 1 ],
						src.org[ 2 ]
						)
						);

					// Don't rebase indexes here - Quake3 re-uses some indexes for multiple vertex
					// groups eg repeating small details have the same relative vertex data but
					// use the same index data.
				}
				else if ( src.type == BspFaceType.Patch )
				{
					// Seems to be some crap in the Q3 level where vertex count = 0 or num control points = 0?
					if ( ( dest.numVertices == 0 ) || ( src.meshCtrl[ 0 ] == 0 ) )
					{
						dest.type = FaceGroup.Unknown;
					}
					else
					{
						// Set up patch surface
						dest.type = FaceGroup.Patch;

						// Locate the patch we already built
						if ( !patches.ContainsKey( face ) )
							throw new AxiomException( "Patch not found from previous built state." );

						dest.patchSurf = (PatchSurface)patches[ face ];
					}
				}
				else if ( src.type == BspFaceType.Mesh )
				{
					dest.type = FaceGroup.FaceList;

					// Assign plane
					dest.plane.Normal = new Vector3( src.normal[ 0 ], src.normal[ 1 ], src.normal[ 2 ] );
					dest.plane.D = -dest.plane.Normal.Dot( new Vector3( src.org[ 0 ], src.org[ 1 ], src.org[ 2 ] ) );
				}
				else
				{
					LogManager.Instance.Write( "!!! Unknown face type !!!" );
				}

				faceGroups[ face ] = dest;
			}

			//-----------------------------------------------------------------------
			// Nodes
			//-----------------------------------------------------------------------
			// Allocate memory for all nodes (leaves and splitters)
			nodes = new BspNode[ q3lvl.NumNodes + q3lvl.NumLeaves ];
			numLeaves = q3lvl.NumLeaves;
			leafStart = q3lvl.NumNodes;

			// Run through and initialize the array so front/back node pointers
			// aren't null.
			for ( int i = 0; i < nodes.Length; i++ )
				nodes[ i ] = new BspNode();

			// Convert nodes
			// In our array, first q3lvl.NumNodes are non-leaf, others are leaves
			for ( int i = 0; i < q3lvl.NumNodes; i++ )
			{
				BspNode node = nodes[ i ];
				InternalBspNode q3node = q3lvl.Nodes[ i ];

				node.IsLeaf = false;
				node.Owner = this;

				Plane splitPlane = new Plane();

				// Set plane
				splitPlane.Normal = new Vector3(
					q3lvl.Planes[ q3node.plane ].normal[ 0 ],
					q3lvl.Planes[ q3node.plane ].normal[ 1 ],
					q3lvl.Planes[ q3node.plane ].normal[ 2 ]
					);
				splitPlane.D = -q3lvl.Planes[ q3node.plane ].distance;

				node.SplittingPlane = splitPlane;

				// Set bounding box
				node.BoundingBox = new AxisAlignedBox(
					new Vector3(
						q3node.bbox[ 0 ],
						q3node.bbox[ 1 ],
						q3node.bbox[ 2 ]
					),
					new Vector3(
						q3node.bbox[ 3 ],
						q3node.bbox[ 4 ],
						q3node.bbox[ 5 ]
					)
				);

				// Set back pointer
				// Negative indexes in Quake3 mean leaves.
				if ( q3node.back < 0 )
					node.BackNode = nodes[ leafStart + ( ~( q3node.back ) ) ];
				else
					node.BackNode = nodes[ q3node.back ];

				// Set front pointer
				// Negative indexes in Quake3 mean leaves
				if ( q3node.front < 0 )
					node.FrontNode = nodes[ leafStart + ( ~( q3node.front ) ) ];
				else
					node.FrontNode = nodes[ q3node.front ];
			}

			//-----------------------------------------------------------------------
			// Brushes
			//-----------------------------------------------------------------------
			// Reserve enough memory for all brushes, solid or not (need to maintain indexes)
			brushes = new BspBrush[ q3lvl.NumBrushes ];

			for ( int i = 0; i < q3lvl.NumBrushes; i++ )
			{
				InternalBspBrush q3brush = q3lvl.Brushes[ i ];

				// Create a new OGRE brush
				BspBrush brush = new BspBrush();
				int numBrushSides = q3brush.numSides;
				int brushSideIdx = q3brush.firstSide;

				// Iterate over the sides and create plane for each
				while ( numBrushSides-- > 0 )
				{
					InternalBspPlane side = q3lvl.Planes[ q3lvl.BrushSides[ brushSideIdx ].planeNum ];

					// Notice how we normally invert Q3A plane distances, but here we do not
					// Because we want plane normals pointing out of solid brushes, not in.
					Plane brushSide = new Plane(
						new Vector3(
							q3lvl.Planes[ q3lvl.BrushSides[ brushSideIdx ].planeNum ].normal[ 0 ],
							q3lvl.Planes[ q3lvl.BrushSides[ brushSideIdx ].planeNum ].normal[ 1 ],
							q3lvl.Planes[ q3lvl.BrushSides[ brushSideIdx ].planeNum ].normal[ 2 ]
						), q3lvl.Planes[ q3lvl.BrushSides[ brushSideIdx ].planeNum ].distance );

					brush.Planes.Add( brushSide );
					brushSideIdx++;
				}

				// Build world fragment
				brush.Fragment.FragmentType = WorldFragmentType.PlaneBoundedRegion;
				brush.Fragment.Planes = brush.Planes;

				brushes[ i ] = brush;
			}

			//-----------------------------------------------------------------------
			// Leaves
			//-----------------------------------------------------------------------
			for ( int i = 0; i < q3lvl.NumLeaves; ++i )
			{
				BspNode node = nodes[ i + this.LeafStart ];
				InternalBspLeaf q3leaf = q3lvl.Leaves[ i ];

				node.IsLeaf = true;
				node.Owner = this;

				// Set bounding box
				node.BoundingBox.Minimum = new Vector3(
					q3leaf.bbox[ 0 ],
					q3leaf.bbox[ 1 ],
					q3leaf.bbox[ 2 ]
					);
				node.BoundingBox.Maximum = new Vector3(
					q3leaf.bbox[ 3 ],
					q3leaf.bbox[ 4 ],
					q3leaf.bbox[ 5 ]
					);

				// Set faces
				node.FaceGroupStart = q3leaf.faceStart;
				node.NumFaceGroups = q3leaf.faceCount;
				node.VisCluster = q3leaf.cluster;

				// Load Brushes for this leaf
				int realBrushIdx = 0, solidIdx = 0;
				int brushCount = q3leaf.brushCount;
				int brushIdx = q3leaf.brushStart;

				node.SolidBrushes = new BspBrush[ brushCount ];

				while ( brushCount-- > 0 )
				{
					realBrushIdx = q3lvl.LeafBrushes[ brushIdx ];
					InternalBspBrush q3brush = q3lvl.Brushes[ realBrushIdx ];

					// Only load solid ones, we don't care about any other types
					// Shader determines this.
					InternalBspShader brushShader = q3lvl.Shaders[ q3brush.shaderIndex ];

					if ( ( brushShader.contentFlags & ContentFlags.Solid ) == ContentFlags.Solid )
						node.SolidBrushes[ solidIdx ] = brushes[ realBrushIdx ];

					brushIdx++;
					solidIdx++;
				}
			}

			// Vis - just copy
			visData.numClusters = q3lvl.VisData.clusterCount;
			visData.rowLength = q3lvl.VisData.rowSize;
			visData.tableData = new byte[ q3lvl.VisData.rowSize * q3lvl.VisData.clusterCount ];

			Array.Copy( q3lvl.VisData.data, 0, visData.tableData, 0, visData.tableData.Length );
		}
Пример #15
0
		public override void NotifyIndexBufferDestroyed( HardwareIndexBuffer buffer )
		{
			_baseInstance.NotifyIndexBufferDestroyed( buffer );
		}
Пример #16
0
 public IEnumerator GetShadowVolumeRenderableEnumerator(ShadowTechnique technique, Light light,
                                                        HardwareIndexBuffer indexBuffer, float extrusionDistance, bool extrudeVertices)
 {
     return(GetShadowVolumeRenderableEnumerator(technique, light, indexBuffer, extrudeVertices, extrusionDistance, 0));
 }
Пример #17
0
		public override IEnumerator GetShadowVolumeRenderableEnumerator( ShadowTechnique technique, Light light,
		                                                                 HardwareIndexBuffer indexBuffer, bool extrudeVertices,
		                                                                 float extrusionDistance, int flags )
		{
			Debug.Assert( indexBuffer != null, "Only external index buffers are supported right now" );
			Debug.Assert( indexBuffer.Type == IndexType.Size16, "Only 16-bit indexes supported for now" );

			// Potentially delegate to LOD entity
			if ( this.meshLodIndex > 0 && this.mesh.IsLodManual )
			{
				Debug.Assert( this.meshLodIndex - 1 < this.lodEntityList.Count,
				              "No LOD EntityList - did you build the manual LODs after creating the entity?" );

				var lodEnt = this.lodEntityList[ this.meshLodIndex - 1 ];

				// index - 1 as we skip index 0 (original LOD)
				if ( HasSkeleton && lodEnt.HasSkeleton )
				{
					// Copy the animation state set to lod entity, we assume the lod
					// entity only has a subset animation states
					CopyAnimationStateSubset( lodEnt.animationState, this.animationState );
				}

				return lodEnt.GetShadowVolumeRenderableEnumerator( technique, light, indexBuffer, extrudeVertices, extrusionDistance,
				                                                   flags );
			}

			// Prep mesh if required
			// NB This seems to result in memory corruptions, having problems
			// tracking them down. For now, ensure that shadows are enabled
			// before any entities are created
			if ( !this.mesh.IsPreparedForShadowVolumes )
			{
				this.mesh.PrepareForShadowVolume();
				// reset frame last updated to force update of buffers
				this.frameAnimationLastUpdated = 0;
				// re-prepare buffers
				PrepareTempBlendedBuffers();
			}

			// Update any animation
			UpdateAnimation();

			// Calculate the object space light details
			var lightPos = light.GetAs4DVector();

			// Only use object-space light if we're not doing transforms
			// Since when animating the positions are already transformed into
			// world space so we need world space light position
			var isAnimated = HasSkeleton || this.mesh.HasVertexAnimation;
			if ( !isAnimated )
			{
				var world2Obj = parentNode.FullTransform.Inverse();

				lightPos = world2Obj*lightPos;
			}

			// We need to search the edge list for silhouette edges
			var edgeList = GetEdgeList();

			// Init shadow renderable list if required
			var init = ( this.shadowRenderables.Count == 0 );

			if ( init )
			{
				this.shadowRenderables.Capacity = edgeList.edgeGroups.Count;
			}

			var updatedSharedGeomNormals = false;

			EntityShadowRenderable esr = null;
			EdgeData.EdgeGroup egi;

			// note: using capacity for the loop since no items are in the list yet.
			// capacity is set to how large the collection will be in the end
			for ( var i = 0; i < this.shadowRenderables.Capacity; i++ )
			{
				egi = (EdgeData.EdgeGroup)edgeList.edgeGroups[ i ];
				var data = ( isAnimated ? FindBlendedVertexData( egi.vertexData ) : egi.vertexData );
				if ( init )
				{
					// Try to find corresponding SubEntity; this allows the
					// linkage of visibility between ShadowRenderable and SubEntity
					var subEntity = FindSubEntityForVertexData( egi.vertexData );

					// Create a new renderable, create a separate light cap if
					// we're using hardware skinning since otherwise we get
					// depth-fighting on the light cap
					esr = new EntityShadowRenderable( this, indexBuffer, data, subEntity.VertexProgramInUse || !extrudeVertices,
					                                  subEntity );

					this.shadowRenderables.Add( esr );
				}
				else
				{
					esr = (EntityShadowRenderable)this.shadowRenderables[ i ];

					if ( HasSkeleton )
					{
						// If we have a skeleton, we have no guarantee that the position
						// buffer we used last frame is the same one we used last frame
						// since a temporary buffer is requested each frame
						// therefore, we need to update the EntityShadowRenderable
						// with the current position buffer
						esr.RebindPositionBuffer( data, isAnimated );
					}
				}

				// For animated entities we need to recalculate the face normals
				if ( isAnimated )
				{
					if ( egi.vertexData != this.mesh.SharedVertexData || !updatedSharedGeomNormals )
					{
						// recalculate face normals
						edgeList.UpdateFaceNormals( egi.vertexSet, esr.PositionBuffer );

						// If we're not extruding in software we still need to update
						// the latter part of the buffer (the hardware extruded part)
						// with the latest animated positions
						if ( !extrudeVertices )
						{
							var srcPtr = esr.PositionBuffer.Lock( BufferLocking.Normal );
							var destPtr = srcPtr + ( egi.vertexData.vertexCount*12 );

							// 12 = sizeof(float) * 3
							Memory.Copy( srcPtr, destPtr, 12*egi.vertexData.vertexCount );

							esr.PositionBuffer.Unlock();
						}

						if ( egi.vertexData == this.mesh.SharedVertexData )
						{
							updatedSharedGeomNormals = true;
						}
					}
				}
				// Extrude vertices in software if required
				if ( extrudeVertices )
				{
					ExtrudeVertices( esr.PositionBuffer, egi.vertexData.vertexCount, lightPos, extrusionDistance );
				}

				// Stop suppressing hardware update now, if we were
				esr.PositionBuffer.SuppressHardwareUpdate( false );
			}

			// Calc triangle light facing
			UpdateEdgeListLightFacing( edgeList, lightPos );

			// Generate indexes and update renderables
			GenerateShadowVolume( edgeList, indexBuffer, light, this.shadowRenderables, flags );

			return this.shadowRenderables.GetEnumerator();
		}
Пример #18
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;

                    // 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!");
        }
Пример #19
0
			public EntityShadowRenderable( Entity parent, HardwareIndexBuffer indexBuffer, VertexData vertexData,
			                               bool createSeparateLightCap, SubEntity subEntity, bool isLightCap )
			{
				this.parent = parent;

				// Save link to vertex data
				this.currentVertexData = vertexData;

				// Initialize render op
				renderOperation.indexData = new IndexData();
				renderOperation.indexData.indexBuffer = indexBuffer;
				renderOperation.indexData.indexStart = 0;
				// index start and count are sorted out later

				// Create vertex data which just references position component (and 2 component)
				renderOperation.vertexData = new VertexData();
				renderOperation.vertexData.vertexDeclaration = HardwareBufferManager.Instance.CreateVertexDeclaration();
				renderOperation.vertexData.vertexBufferBinding = HardwareBufferManager.Instance.CreateVertexBufferBinding();

				// Map in position data
				renderOperation.vertexData.vertexDeclaration.AddElement( 0, 0, VertexElementType.Float3,
				                                                         VertexElementSemantic.Position );
				this.originalPosBufferBinding =
					vertexData.vertexDeclaration.FindElementBySemantic( VertexElementSemantic.Position ).Source;

				this.positionBuffer = vertexData.vertexBufferBinding.GetBuffer( this.originalPosBufferBinding );
				renderOperation.vertexData.vertexBufferBinding.SetBinding( 0, this.positionBuffer );

				// Map in w-coord buffer (if present)
				if ( vertexData.hardwareShadowVolWBuffer != null )
				{
					renderOperation.vertexData.vertexDeclaration.AddElement( 1, 0, VertexElementType.Float1,
					                                                         VertexElementSemantic.TexCoords, 0 );
					this.wBuffer = vertexData.hardwareShadowVolWBuffer;
					renderOperation.vertexData.vertexBufferBinding.SetBinding( 1, this.wBuffer );
				}

				// Use same vertex start as input
				renderOperation.vertexData.vertexStart = vertexData.vertexStart;

				if ( isLightCap )
				{
					// Use original vertex count, no extrusion
					renderOperation.vertexData.vertexCount = vertexData.vertexCount;
				}
				else
				{
					// Vertex count must take into account the doubling of the buffer,
					// because second half of the buffer is the extruded copy
					renderOperation.vertexData.vertexCount = vertexData.vertexCount*2;

					if ( createSeparateLightCap )
					{
						// Create child light cap
						lightCap = new EntityShadowRenderable( parent, indexBuffer, vertexData, false, subEntity, true );
					}
				}
			}
Пример #20
0
        private void CreateBuffers(ushort[] indices, short[] vertices)
        {
            var numIndices = indices.Length;
            var numVertices = vertices.Length;

            _vertexDeclaration = HardwareBufferManager.Instance.CreateVertexDeclaration();
            _vertexDeclaration.AddElement(0, 0, VertexElementType.Short2, VertexElementSemantic.Position);
            _ib = HardwareBufferManager.Instance.CreateIndexBuffer(IndexType.Size16, numIndices, BufferUsage.WriteOnly);
            _vb = HardwareBufferManager.Instance.CreateVertexBuffer(_vertexDeclaration, numVertices, BufferUsage.WriteOnly, false);

            _ib.WriteData(0, numIndices * sizeof(ushort), indices, true);
            _vb.WriteData(0, numVertices * sizeof(ushort), vertices, true);

            var binding = new VertexBufferBinding();
            binding.SetBinding(0, _vb);

            VertexData = new VertexData();
            VertexData.vertexDeclaration = _vertexDeclaration;
            VertexData.vertexBufferBinding = binding;
            VertexData.vertexCount = numVertices;
            VertexData.vertexStart = 0;

            IndexData = new IndexData();
            IndexData.indexBuffer = _ib;
            IndexData.indexCount = numIndices;
            IndexData.indexStart = 0;
        }