Пример #1
0
        protected void QuakeVertexToBspVertex(InternalBspVertex src, out BspVertex dest, out TextureLightMap texLightMap)
        {
            dest = new BspVertex();

            dest.position  = new Vector3(src.point[0], src.point[1], src.point[2]);
            dest.normal    = new Vector3(src.normal[0], src.normal[1], src.normal[2]);
            dest.texCoords = new Vector2(src.texture[0], src.texture[1]);
            dest.lightMap  = new Vector2(src.lightMap[0], src.lightMap[1]);

            texLightMap       = new TextureLightMap();
            texLightMap.color = src.color;
        }
Пример #2
0
		/// <summary>
		///		Caches a face group and calculates texture lighting coordinates.
		/// </summary>
		protected int CacheLightGeometry( TextureLight light, BufferBase pIndexesBuf, BufferBase pTexLightMapsBuf,
		                                  BufferBase pVerticesBuf, BspStaticFaceGroup faceGroup )
		{
#if !AXIOM_SAFE_ONLY
			unsafe
#endif
			{
				// Skip sky always
				if ( faceGroup.isSky )
				{
					return 0;
				}

				int idxStart = 0;
				int numIdx = 0;
				int vertexStart = 0;

				if ( faceGroup.type == FaceGroup.FaceList )
				{
					idxStart = faceGroup.elementStart;
					numIdx = faceGroup.numElements;
					vertexStart = faceGroup.vertexStart;
				}
				else if ( faceGroup.type == FaceGroup.Patch )
				{
					idxStart = faceGroup.patchSurf.IndexOffset;
					numIdx = faceGroup.patchSurf.CurrentIndexCount;
					vertexStart = faceGroup.patchSurf.VertexOffset;
				}
				else
				{
					// Unsupported face type
					return 0;
				}

				var idxSize = this.level.Indexes.IndexSize;
				var idxSrc = this.level.Indexes.Lock( idxStart*idxSize, numIdx*idxSize, BufferLocking.ReadOnly );
#if SILVERLIGHT
				var src = idxSrc.ToUShortPointer();
#else
				var src = idxSrc.ToUIntPointer();
#endif

				int maxIndex = 0;
				for ( int i = 0; i < numIdx; i++ )
				{
					var index = (int)src[ i ];
					if ( index > maxIndex )
					{
						maxIndex = index;
					}
				}

				var vertexPos = new Vector3[maxIndex + 1];
				var vertexIsStored = new bool[maxIndex + 1];

				for ( int i = 0; i < numIdx; i++ )
				{
					var index = (int)src[ i ];
					var pVertices = pVerticesBuf.ToBspVertexPointer();
					if ( !vertexIsStored[ index ] )
					{
						vertexPos[ index ] = pVertices[ vertexStart + index ].position;
						vertexIsStored[ index ] = true;
					}
					pVerticesBuf.UnPin();
				}

				Vector2[] texCoors;
				ColorEx[] colors;

				bool res = light.CalculateTexCoordsAndColors( faceGroup.plane, vertexPos, out texCoors, out colors );

				if ( res )
				{
					var pTexLightMaps = pTexLightMapsBuf.ToTextureLightMapPointer();
					for ( int i = 0; i <= maxIndex; i++ )
					{
						pTexLightMaps[ vertexStart + i ] = new TextureLightMap
						                                   {
						                                   	color = Root.Instance.RenderSystem.ConvertColor( colors[ i ] ),
						                                   	textureLightMap = texCoors[ i ]
						                                   };
					}
					pTexLightMapsBuf.UnPin();

					// Offset the indexes here
					// we have to do this now rather than up-front because the
					// indexes are sometimes reused to address different vertex chunks
					if ( this.level.Indexes.Type == IndexType.Size16 )
					{
						var pIndexes = pIndexesBuf.ToUShortPointer();
						for ( int i = 0; i < numIdx; i++ )
						{
							pIndexes[ i ] = (ushort)( src[ i ] + vertexStart );
						}
					}
					else
					{
						var pIndexes = pIndexesBuf.ToUIntPointer();
						for ( int i = 0; i < numIdx; i++ )
						{
							pIndexes[ i ] = (uint)( src[ i ] + vertexStart );
						}
					}
					this.level.Indexes.Unlock();

					// return number of elements
					return numIdx;
				}
				else
				{
					this.level.Indexes.Unlock();

					return 0;
				}
			}
		}
Пример #3
0
        private void LoadLevelVertices(Quake3Level q3lvl)
        {
            //-----------------------------------------------------------------------
            // 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
            InitQuake3Patches(q3lvl, (VertexDeclaration) decl.Clone());

            // 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);

            // Create the vertex buffer, allow space for patches
            HardwareVertexBuffer vbuf = HardwareBufferManager.Instance.CreateVertexBuffer(
                Marshal.SizeOf(typeof(BspVertex)),
                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(
                Marshal.SizeOf(typeof(TextureLightMap)),
                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;
        }
Пример #4
0
        protected void QuakeVertexToBspVertex(InternalBspVertex src, out BspVertex dest, out TextureLightMap texLightMap)
        {
            dest = new BspVertex();

            dest.position = new Vector3(src.point[0], src.point[1], src.point[2]);
            dest.normal = new Vector3(src.normal[0], src.normal[1], src.normal[2]);
            dest.texCoords = new Vector2(src.texture[0], src.texture[1]);
            dest.lightMap = new Vector2(src.lightMap[0], src.lightMap[1]);

            texLightMap = new TextureLightMap();
            texLightMap.color = src.color;
        }
Пример #5
0
		/// <summary>
		///		Caches a face group and calculates texture lighting coordinates.
		/// </summary>
		unsafe protected int CacheLightGeometry( TextureLight light, uint* pIndexes, TextureLightMap* pTexLightMaps, BspVertex* pVertices, BspStaticFaceGroup faceGroup )
		{
			// Skip sky always
			if ( faceGroup.isSky )
				return 0;

			int idxStart = 0;
			int numIdx = 0;
			int vertexStart = 0;

			if ( faceGroup.type == FaceGroup.FaceList )
			{
				idxStart = faceGroup.elementStart;
				numIdx = faceGroup.numElements;
				vertexStart = faceGroup.vertexStart;
			}
			else if ( faceGroup.type == FaceGroup.Patch )
			{
				idxStart = faceGroup.patchSurf.IndexOffset;
				numIdx = faceGroup.patchSurf.CurrentIndexCount;
				vertexStart = faceGroup.patchSurf.VertexOffset;
			}
			else
			{
				// Unsupported face type
				return 0;
			}

			uint* src = (uint*)level.Indexes.Lock(
				idxStart * sizeof( uint ),
				numIdx * sizeof( uint ),
				BufferLocking.ReadOnly );

			int maxIndex = 0;
			for ( int i = 0; i < numIdx; i++ )
			{
				int index = (int)*( src + i );
				if ( index > maxIndex )
					maxIndex = index;
			}

			Vector3[] vertexPos = new Vector3[ maxIndex + 1 ];
			bool[] vertexIsStored = new bool[ maxIndex + 1 ];

			for ( int i = 0; i < numIdx; i++ )
			{
				uint index = *( src + i );
				if ( !vertexIsStored[ index ] )
				{
					vertexPos[ index ] = ( *( pVertices + vertexStart + index ) ).position;
					vertexIsStored[ index ] = true;
				}
			}

			Vector2[] texCoors;
			ColorEx[] colors;

			bool res = light.CalculateTexCoordsAndColors( faceGroup.plane, vertexPos, out texCoors, out colors );

			if ( res )
			{
				for ( int i = 0; i <= maxIndex; i++ )
				{
					pTexLightMaps[ vertexStart + i ].color = Root.Instance.RenderSystem.ConvertColor( colors[ i ] );
					pTexLightMaps[ vertexStart + i ].textureLightMap = texCoors[ i ];
				}

				// Offset the indexes here
				// we have to do this now rather than up-front because the
				// indexes are sometimes reused to address different vertex chunks
				for ( int i = 0; i < numIdx; i++ )
					*pIndexes++ = (uint)( *src++ + vertexStart );

				level.Indexes.Unlock();

				// return number of elements
				return numIdx;
			}
			else
			{
				level.Indexes.Unlock();

				return 0;
			}
		}
Пример #6
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 );
		}
Пример #7
0
        private void LoadLevelVertices(Quake3Level q3lvl)
        {
            //-----------------------------------------------------------------------
            // 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
            InitQuake3Patches(q3lvl, (VertexDeclaration)decl.Clone());

            // 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);


            // Create the vertex buffer, allow space for patches
            HardwareVertexBuffer vbuf = HardwareBufferManager.Instance.CreateVertexBuffer(
                Marshal.SizeOf(typeof(BspVertex)),
                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(
                Marshal.SizeOf(typeof(TextureLightMap)),
                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;
        }