public void InitQuake3Patches(Quake3Level q3lvl, VertexDeclaration decl) { int face; patchVertexCount = 0; patchIndexCount = 0; // We're just building the patch here to get a hold on the size of the mesh // although we'll reuse this information later face = q3lvl.NumFaces; while (face-- > 0) { InternalBspFace src = q3lvl.Faces[face]; if (src.type == BspFaceType.Patch) { // Seems to be some crap in the Q3 level where vertex count = 0 or num control points = 0? if ((src.vertCount == 0) || (src.meshCtrl[0] == 0)) { continue; } PatchSurface ps = new PatchSurface(); // Set up control points & format. // Reuse the vertex declaration. // Copy control points into a buffer so we can convert their format. BspVertex[] controlPoints = new BspVertex[src.vertCount]; TextureLightMap texLightMap; for (int v = 0; v < src.vertCount; v++) { QuakeVertexToBspVertex(q3lvl.Vertices[src.vertStart + v], out controlPoints[v], out texLightMap); } // Define the surface, but don't build it yet (no vertex / index buffer) ps.DefineSurface( controlPoints, decl, src.meshCtrl[0], src.meshCtrl[1] ); // Get stats patchVertexCount += ps.RequiredVertexCount; patchIndexCount += ps.RequiredIndexCount; // Save the surface for later patches.Add(face, ps); } } }
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; }
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 = | 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; }
public void InitQuake3Patches(Quake3Level q3lvl, VertexDeclaration decl) { int face; patchVertexCount = 0; patchIndexCount = 0; // We're just building the patch here to get a hold on the size of the mesh // although we'll reuse this information later face = q3lvl.NumFaces; while(face-- > 0) { InternalBspFace src = q3lvl.Faces[face]; if(src.type == BspFaceType.Patch) { // Seems to be some crap in the Q3 level where vertex count = 0 or num control points = 0? if((src.vertCount == 0) || (src.meshCtrl[0] == 0)) continue; PatchSurface ps = new PatchSurface(); // Set up control points & format. // Reuse the vertex declaration. // Copy control points into a buffer so we can convert their format. BspVertex[] controlPoints = new BspVertex[src.vertCount]; TextureLightMap texLightMap; for(int v = 0; v < src.vertCount; v++) QuakeVertexToBspVertex(q3lvl.Vertices[src.vertStart + v], out controlPoints[v], out texLightMap); // Define the surface, but don't build it yet (no vertex / index buffer) ps.DefineSurface( controlPoints, decl, src.meshCtrl[0], src.meshCtrl[1] ); // Get stats patchVertexCount += ps.RequiredVertexCount; patchIndexCount += ps.RequiredIndexCount; // Save the surface for later patches.Add(face, ps); } } }
/// <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; } }
/// <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 = | 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 ); }
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 = | 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; }