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); }
private void CreateLeaves(Quake3Level q3lvl) { 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++; } } }
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> /// Generic load - called by <see cref="Plugin_BSPSceneManager.BspResourceManager"/>. /// </summary> public override void Load() { Hashtable options = SceneManagerEnumerator.Instance.GetSceneManager(SceneType.Interior).Options; if (options.ContainsKey("SetYAxisUp")) { bspOptions.setYAxisUp = (bool)options["SetYAxisUp"]; } if (options.ContainsKey("Scale")) { bspOptions.scale = (float)options["Scale"]; } if (options.ContainsKey("Move")) { bspOptions.move = (Vector3)options["Move"]; } if (options.ContainsKey("UseLightmaps")) { bspOptions.useLightmaps = (bool)options["UseLightmaps"]; } if (options.ContainsKey("AmbientEnabled")) { bspOptions.ambientEnabled = (bool)options["AmbientEnabled"]; } if (options.ContainsKey("AmbientRatio")) { bspOptions.ambientRatio = (float)options["AmbientRatio"]; } Quake3Level q3 = new Quake3Level(bspOptions); Stream chunk = BspResourceManager.Instance.FindResourceData(name); q3.LoadFromStream(chunk); LoadQuake3Level(q3); chunk.Close(); isLoaded = true; }
/// <summary> /// /** Internal utility function for loading data from Quake3. /// </summary> protected void LoadQuake3Level(Quake3Level q3lvl) { SceneManager sm = SceneManagerEnumerator.Instance.GetSceneManager(SceneType.Interior); LoadEntities(q3lvl); Quake3ShaderManager.Instance.ParseAllSources(".shader"); q3lvl.ExtractLightmaps(); LoadLevelVertices(q3lvl); LoadLevelFaces(q3lvl); // now build patch information BuildQuake3Patches(q3lvl.NumVertices, q3lvl.NumElements); //----------------------------------------------------------------------- // Create materials for shaders //----------------------------------------------------------------------- CreateShaderMaterials(q3lvl, sm); //----------------------------------------------------------------------- // Nodes //----------------------------------------------------------------------- CreateNodes(q3lvl); //----------------------------------------------------------------------- // Brushes //----------------------------------------------------------------------- CreateBrushes(q3lvl); //----------------------------------------------------------------------- // Leaves //----------------------------------------------------------------------- CreateLeaves(q3lvl); // 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 CreateBrushes(Quake3Level q3lvl) { // 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; } }
private BspStaticFaceGroup CopyShaderFaceData(Quake3Level q3lvl, int face, Material shadMat, int shadIdx) { 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 !!!"); } return dest; }
/// <summary> /// /** Internal utility function for loading data from Quake3. /// </summary> protected void LoadQuake3Level(Quake3Level q3lvl) { SceneManager sm = SceneManagerEnumerator.Instance.GetSceneManager(SceneType.Interior); LoadEntities(q3lvl); Quake3ShaderManager.Instance.ParseAllSources(".shader"); q3lvl.ExtractLightmaps(); LoadLevelVertices(q3lvl); LoadLevelFaces(q3lvl); // now build patch information BuildQuake3Patches(q3lvl.NumVertices, q3lvl.NumElements); //----------------------------------------------------------------------- // Create materials for shaders //----------------------------------------------------------------------- CreateShaderMaterials(q3lvl, sm); //----------------------------------------------------------------------- // Nodes //----------------------------------------------------------------------- CreateNodes(q3lvl); //----------------------------------------------------------------------- // Brushes //----------------------------------------------------------------------- CreateBrushes(q3lvl); //----------------------------------------------------------------------- // Leaves //----------------------------------------------------------------------- CreateLeaves(q3lvl); // 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); }
/// <summary> /// Internal method for parsing chosen entities. /// </summary> protected void LoadEntities(Quake3Level q3lvl) { Vector3 origin = new Vector3(); float angle = 0; bool isPlayerStart = false; string[] entities = q3lvl.Entities.Split('\n'); for(int i = 0; i < entities.Length; i++) { // Remove whitespace and quotes. entities[i] = entities[i].Trim().Replace("\"", ""); if(entities[i].Length == 0) continue; string[] paramList = entities[i].Split(' '); if(paramList[0] == "origin") { float[] vector = new float[3]; for (int v = 0; v < 3; v++) vector[v] = StringConverter.ParseFloat(paramList[v + 1]); q3lvl.TransformVector(vector); origin = new Vector3(vector[0], vector[1], vector[2]); } if(paramList[0] == "angle") angle = StringConverter.ParseFloat(paramList[1]); if((paramList[0] == "classname") && (paramList[1] == "info_player_deathmatch")) isPlayerStart = true; if(paramList[0] == "}") { if(isPlayerStart) { ViewPoint vp = new ViewPoint(); vp.position = origin; if (q3lvl.Options.setYAxisUp) vp.orientation = Quaternion.FromAngleAxis(MathUtil.DegreesToRadians(angle), Vector3.UnitY); else vp.orientation = Quaternion.FromAngleAxis(MathUtil.DegreesToRadians(angle), Vector3.UnitZ); playerStarts.Add(vp); } isPlayerStart = false; } } }
/// <summary> /// Generic load - called by <see cref="Plugin_BSPSceneManager.BspResourceManager"/>. /// </summary> public override void Load() { Hashtable options = SceneManagerEnumerator.Instance.GetSceneManager(SceneType.Interior).Options; if (options.ContainsKey("SetYAxisUp")) bspOptions.setYAxisUp = (bool)options["SetYAxisUp"]; if (options.ContainsKey("Scale")) bspOptions.scale = (float)options["Scale"]; if (options.ContainsKey("Move")) bspOptions.move = (Vector3)options["Move"]; if (options.ContainsKey("UseLightmaps")) bspOptions.useLightmaps = (bool)options["UseLightmaps"]; if (options.ContainsKey("AmbientEnabled")) bspOptions.ambientEnabled = (bool)options["AmbientEnabled"]; if (options.ContainsKey("AmbientRatio")) bspOptions.ambientRatio = (float)options["AmbientRatio"]; Quake3Level q3 = new Quake3Level(bspOptions); Stream chunk = BspResourceManager.Instance.FindResourceData(name); q3.LoadFromStream(chunk); LoadQuake3Level(q3); chunk.Close(); isLoaded = true; }
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> /// /** 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 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); }
private void CreateNodes(Quake3Level q3lvl) { // 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]; } }
private void CreateBrushes(Quake3Level q3lvl) { // 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; } }
/// <summary> /// Internal method for parsing chosen entities. /// </summary> protected void LoadEntities(Quake3Level q3lvl) { Vector3 origin = new Vector3(); float angle = 0; bool isPlayerStart = false; string[] entities = q3lvl.Entities.Split('\n'); for (int i = 0; i < entities.Length; i++) { // Remove whitespace and quotes. entities[i] = entities[i].Trim().Replace("\"", ""); if (entities[i].Length == 0) { continue; } string[] paramList = entities[i].Split(' '); if (paramList[0] == "origin") { float[] vector = new float[3]; for (int v = 0; v < 3; v++) { vector[v] = StringConverter.ParseFloat(paramList[v + 1]); } q3lvl.TransformVector(vector); origin = new Vector3(vector[0], vector[1], vector[2]); } if (paramList[0] == "angle") { angle = StringConverter.ParseFloat(paramList[1]); } if ((paramList[0] == "classname") && (paramList[1] == "info_player_deathmatch")) { isPlayerStart = true; } if (paramList[0] == "}") { if (isPlayerStart) { ViewPoint vp = new ViewPoint(); vp.position = origin; if (q3lvl.Options.setYAxisUp) { vp.orientation = Quaternion.FromAngleAxis(MathUtil.DegreesToRadians(angle), Vector3.UnitY); } else { vp.orientation = Quaternion.FromAngleAxis(MathUtil.DegreesToRadians(angle), Vector3.UnitZ); } playerStarts.Add(vp); } isPlayerStart = false; } } }
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; }
private BspStaticFaceGroup CopyShaderFaceData(Quake3Level q3lvl, int face, Material shadMat, int shadIdx) { 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 !!!"); } return(dest); }
private void CreateShaderMaterials(Quake3Level q3lvl, SceneManager sm) { // 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; while (face-- > 0) { // 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 = sm.GetMaterial(shaderName); if (shadMat == null && !bspOptions.useLightmaps) { // try the no-lightmap material shaderName = String.Format("{0}#n", q3lvl.Shaders[shadIdx].name); shadMat = sm.GetMaterial(shaderName); } if (shadMat == null) { // Colour 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(sm, q3lvl.Faces[face].lmTexture); } else { // No shader script, try default type texture shadMat = sm.CreateMaterial(shaderName); Pass shadPass = shadMat.GetTechnique(0).GetPass(0); // Try jpg TextureUnitState tex = shadPass.CreateTextureUnitState(tryName + ".jpg"); tex.Load(); if (tex.IsBlank) { // Try tga tex.SetTextureName(tryName + ".tga"); } // Set replace on all first layer textures for now tex.SetColorOperation(LayerBlendOperation.Replace); tex.TextureAddressing = 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.TextureAddressing = TextureAddressing.Clamp; } shadMat.CullingMode = CullingMode.None; shadMat.Lighting = false; } } shadMat.Load(); // Copy face data BspStaticFaceGroup dest = CopyShaderFaceData(q3lvl, face, shadMat, shadIdx); faceGroups[face] = dest; } }
private void CreateNodes(Quake3Level q3lvl) { // 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]; } } }
private void CreateLeaves(Quake3Level q3lvl) { 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++; } } }
/// <summary> /// Generic load - called by <see cref="Plugin_BSPSceneManager.BspResourceManager"/>. /// </summary> protected override void load() { //if ( createParam.ContainsKey( "SetYAxisUp" ) ) // bool.TryParse( createParam[ "SetYAxisUp" ], out bspOptions.setYAxisUp); //if ( createParam.ContainsKey( "Scale" ) ) // float.TryParse( createParam[ "Scale" ], out bspOptions.scale ); //Vector3 move = Vector3.Zero; //if ( createParam.ContainsKey( "MoveX" ) ) // float.TryParse( createParam[ "MoveX" ], out move.x ); //if ( createParam.ContainsKey("MoveY" ) ) // float.TryParse( createParam["MoveY"], out move.y ); //if ( createParam.ContainsKey( "MoveZ" ) ) // float.TryParse( createParam["MoveZ"], out move.z ); //if ( createParam.ContainsKey( "UseLightmaps" ) ) // bool.TryParse( createParam[ "UseLightmaps" ], out bspOptions.useLightmaps ); //if ( createParam.ContainsKey( "AmbientEnabled" ) ) // bool.TryParse( createParam[ "AmbientEnabled" ], out bspOptions.ambientEnabled ); //if ( createParam.ContainsKey( "AmbientRatio") ) // float.TryParse( createParam[ "AmbientRatio" ], out bspOptions.ambientRatio ); Quake3Level q3 = new Quake3Level( bspOptions ); Stream chunk = ResourceGroupManager.Instance.OpenResource( Name, ResourceGroupManager.Instance.WorldResourceGroupName ); q3.LoadFromStream( chunk ); LoadQuake3Level( q3 ); chunk.Close(); }
private void CreateShaderMaterials(Quake3Level q3lvl, SceneManager sm) { // 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; while(face-- > 0) { // 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 = sm.GetMaterial(shaderName); if (shadMat == null && !bspOptions.useLightmaps) { // try the no-lightmap material shaderName = String.Format("{0}#n", q3lvl.Shaders[shadIdx].name); shadMat = sm.GetMaterial(shaderName); } if(shadMat == null) { // Colour 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(sm, q3lvl.Faces[face].lmTexture); } else { // No shader script, try default type texture shadMat = sm.CreateMaterial(shaderName); Pass shadPass = shadMat.GetTechnique(0).GetPass(0); // Try jpg TextureUnitState tex = shadPass.CreateTextureUnitState(tryName + ".jpg"); tex.Load(); if(tex.IsBlank) { // Try tga tex.SetTextureName(tryName + ".tga"); } // Set replace on all first layer textures for now tex.SetColorOperation(LayerBlendOperation.Replace); tex.TextureAddressing = 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.TextureAddressing = TextureAddressing.Clamp; } shadMat.CullingMode = CullingMode.None; shadMat.Lighting = false; } } shadMat.Load(); // Copy face data BspStaticFaceGroup dest = CopyShaderFaceData(q3lvl, face, shadMat, shadIdx); faceGroups[face] = dest; } }
//----------------------------------------------------------------------- private static int calculateLoadingStages( Stream stream ) { Quake3Level q3 = new Quake3Level( null ); // Load header only q3.LoadHeaderFromStream( stream ); // Ok, count up the things that we will report int stages = 0; // loadEntities (1 stage) ++stages; // extractLightmaps (external, 1 stage) ++stages; // initQuake3Patches ++stages; // vertex setup ++stages; // face setup ++stages; // patch building ++stages; // material setup // this is not strictly based on load, since we only know the number // of faces, not the number of materials // raise one event for every 50 faces, plus one at the end //stages += (q3.Faces.Length / NUM_FACES_PER_PROGRESS_REPORT) + 1; // node setup //stages += (q3.Nodes.Length / NUM_NODES_PER_PROGRESS_REPORT) + 1; // brush setup //stages += (q3.Brushes.Length / NUM_BRUSHES_PER_PROGRESS_REPORT) + 1; // leaf setup //stages += (q3.Leaves.Length / NUM_LEAVES_PER_PROGRESS_REPORT) + 1; // vis ++stages; return stages; }
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 Load( Stream stream ) { if ( createParam.ContainsKey( "SetYAxisUp" ) ) bool.TryParse( createParam[ "SetYAxisUp" ], out bspOptions.setYAxisUp ); if ( createParam.ContainsKey( "Scale" ) ) float.TryParse( createParam[ "Scale" ], out bspOptions.scale ); Vector3 move = Vector3.Zero; if ( createParam.ContainsKey( "MoveX" ) ) Real.TryParse( createParam[ "MoveX" ], out move.x ); if ( createParam.ContainsKey( "MoveY" ) ) Real.TryParse( createParam[ "MoveY" ], out move.y ); if ( createParam.ContainsKey( "MoveZ" ) ) Real.TryParse( createParam[ "MoveZ" ], out move.z ); if ( createParam.ContainsKey( "UseLightmaps" ) ) bool.TryParse( createParam[ "UseLightmaps" ], out bspOptions.useLightmaps ); if ( createParam.ContainsKey( "AmbientEnabled" ) ) bool.TryParse( createParam[ "AmbientEnabled" ], out bspOptions.ambientEnabled ); if ( createParam.ContainsKey( "AmbientRatio" ) ) float.TryParse( createParam[ "AmbientRatio" ], out bspOptions.ambientRatio ); Quake3Level q3 = new Quake3Level( bspOptions ); q3.LoadFromStream( stream ); LoadQuake3Level( q3 ); }