#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "VBSPReader.h" #include "VBSPEntity.h" using namespace bsp; using namespace osg; using namespace osgDB; // strcasecmp for MSVC #ifdef _MSC_VER #define strcasecmp _stricmp #endif VBSPReader::VBSPReader() { // Start with no root node root_node = NULL; // Create the map data object bsp_data = new VBSPData(); // No string table yet texdata_string = NULL; texdata_string_table = NULL; num_texdata_string_table_entries = 0; } VBSPReader::~VBSPReader() { // Clean up the texdata strings and such delete [] texdata_string; delete [] texdata_string_table; } void VBSPReader::processEntities(std::istream & str, int offset, int length) { char * entities; char * startPtr; char * endPtr; int numEntities; int i; std::string entityStr; size_t entityLen; // Create the string entities = new char[length]; memset(entities, 0, length * sizeof(char)); // Seek to the Entities lump str.seekg(offset); // Read the entities string str.read((char *) entities, sizeof(char) * length); // Count the number of entities startPtr = entities; endPtr = strchr(entities, '}'); numEntities = 0; while ((startPtr != NULL) && (endPtr != NULL)) { // Increment the count numEntities++; // Advance the pointers startPtr = strchr(endPtr, '{'); if (startPtr != NULL) endPtr = strchr(startPtr, '}'); } // Parse the entities startPtr = entities; endPtr = strchr(entities, '}'); for (i = 0; i < numEntities; i++) { // Get the length of this entity entityLen = endPtr - startPtr + 1; // Create the entity list entry and copy the entity information entityStr = std::string(startPtr, entityLen); bsp_data->addEntity(entityStr); // Advance the pointers startPtr = strchr(endPtr, '{'); if (startPtr != NULL) endPtr = strchr(startPtr, '}'); } // Free up the original entities string delete [] entities; } void VBSPReader::processModels(std::istream & str, int offset, int length) { int numModels; int i; Model * models; // Calculate the number of models numModels = length / sizeof(Model); // Seek to the Models lump str.seekg(offset); // Read the models models = new Model[numModels]; str.read((char *) models, sizeof(Model) * numModels); // Add the models to the model list for (i = 0; i < numModels; i++) bsp_data->addModel(models[i]); // Clean up delete [] models; } void VBSPReader::processPlanes(std::istream & str, int offset, int length) { int numPlanes; int i; Plane * planes; // Calculate the number of planes numPlanes = length / sizeof(Plane); // Seek to the Planes lump str.seekg(offset); // Read the planes planes = new Plane[numPlanes]; str.read((char *) planes, sizeof(Plane) * numPlanes); // Add the planes to the plane list for (i = 0; i < numPlanes; i++) bsp_data->addPlane(planes[i]); // Clean up delete [] planes; } void VBSPReader::processVertices(std::istream & str, int offset, int length) { int numVertices; int i; Vec3f * vertices; // Calculate the number of vertices numVertices = length / 3 / sizeof(float); // Seek to the Vertices lump str.seekg(offset); // Read the vertex vertices = new Vec3f[numVertices]; str.read((char *) vertices, sizeof(Vec3f) * numVertices); // Add it the vertices to the list for (i = 0; i < numVertices; i++) bsp_data->addVertex(vertices[i]); // Clean up delete [] vertices; } void VBSPReader::processEdges(std::istream & str, int offset, int length) { int numEdges; int i; Edge * edges; // Calculate the number of edges numEdges = length / sizeof(Edge); // Seek to the Edges lump str.seekg(offset); // Read the edges edges = new Edge[numEdges]; str.read((char *) edges, sizeof(Edge) * numEdges); // Add the edges to the edge list for (i = 0; i < numEdges; i++) bsp_data->addEdge(edges[i]); // Clean up delete [] edges; } void VBSPReader::processSurfEdges(std::istream & str, int offset, int length) { int numSurfEdges; int i; int * surfEdges; // Calculate the number of edges numSurfEdges = length / sizeof(int); // Seek to the SurfEdges lump str.seekg(offset); // Read the surface edges surfEdges = new int[numSurfEdges]; str.read((char *) surfEdges, sizeof(int) * numSurfEdges); // Add the surface edges to the surface edge list for (i = 0; i < numSurfEdges; i++) bsp_data->addSurfaceEdge(surfEdges[i]); // Clean up delete [] surfEdges; } void VBSPReader::processFaces(std::istream & str, int offset, int length) { int numFaces; int i; Face * faces; // Calculate the number of faces numFaces = length / sizeof(Face); // Seek to the Faces lump str.seekg(offset); // Read the faces faces = new Face[numFaces]; str.read((char *) faces, sizeof(Face) * numFaces); // Add the faces to the face list for (i = 0; i < numFaces; i++) bsp_data->addFace(faces[i]); // Clean up delete [] faces; } void VBSPReader::processTexInfo(std::istream & str, int offset, int length) { int numTexInfos; int i; TexInfo * texinfos; // Calculate the number of texinfos numTexInfos = length / sizeof(TexInfo); // Seek to the TexInfo lump str.seekg(offset); // Read in the texinfo entries texinfos = new TexInfo[numTexInfos]; str.read((char *) texinfos, sizeof(TexInfo) * numTexInfos); // Add the texinfo entries to the texinfo list for (i = 0; i < numTexInfos; i++) bsp_data->addTexInfo(texinfos[i]); // Clean up delete [] texinfos; } void VBSPReader::processTexData(std::istream & str, int offset, int length) { int numTexDatas; int i; TexData * texdatas; // Calculate the number of texdatas numTexDatas = length / sizeof(TexData); // Seek to the TexData lump str.seekg(offset); // Read in the texdata entries texdatas = new TexData[numTexDatas]; str.read((char *) texdatas, sizeof(TexData) * numTexDatas); // Add the texdata entries to the texdata list for (i = 0; i < numTexDatas; i++) bsp_data->addTexData(texdatas[i]); // Clean up delete [] texdatas; } void VBSPReader::processTexDataStringTable(std::istream & str, int offset, int length) { int i; int index; std::string texStr; // Calculate the number of table entries num_texdata_string_table_entries = length / sizeof(int); // Create the texdata string table texdata_string_table = new int[num_texdata_string_table_entries]; // Seek to the TexDataStringTable lump str.seekg(offset); // Read in the texdata_string_table str.read((char *) texdata_string_table, sizeof(int) * num_texdata_string_table_entries); // If we have a texdata string loaded, parse the texdata strings now if (texdata_string != NULL) { for (i = 0; i < num_texdata_string_table_entries; i++) { // Add the strings from the string data, using the string table // to index it index = texdata_string_table[i]; texStr = std::string(&texdata_string[index]); bsp_data->addTexDataString(texStr); } } } void VBSPReader::processTexDataStringData(std::istream & str, int offset, int length) { int i; int index; std::string texStr; // Create the buffer to hold the texdata string texdata_string = new char[length]; memset(texdata_string, 0, length * sizeof(char)); // Seek to the TexDataStringData lump str.seekg(offset); // Read the entire texdata string (this string is actually a // NULL-delimited list of strings) str.read((char *) texdata_string, sizeof(char) * length); // If we have a string table loaded, parse the texdata strings now // (if not, num_texdata_string_table_entries will be zero and we'll // skip this loop) for (i = 0; i < num_texdata_string_table_entries; i++) { // Add the strings from the string data, using the string table // to index it index = texdata_string_table[i]; texStr = std::string(&texdata_string[index]); bsp_data->addTexDataString(texStr); } } void VBSPReader::processDispInfo(std::istream & str, int offset, int length) { int numDispInfos; int i; DisplaceInfo * dispinfos; // Calculate the number of dispinfos numDispInfos = length / sizeof(DisplaceInfo); // Seek to the DisplaceInfo lump str.seekg(offset); // Read in the dispinfo entries dispinfos = new DisplaceInfo[numDispInfos]; str.read((char *) dispinfos, sizeof(DisplaceInfo) * numDispInfos); // Add the dispinfo entries to the displace info list for (i = 0; i < numDispInfos; i++) bsp_data->addDispInfo(dispinfos[i]); // Clean up delete [] dispinfos; } void VBSPReader::processDispVerts(std::istream & str, int offset, int length) { int numDispVerts; int i; DisplacedVertex * dispverts; // Calculate the number of displaced vertices numDispVerts = length / sizeof(DisplacedVertex); // Seek to the DispVert lump str.seekg(offset); // Read in the displaced vertices dispverts = new DisplacedVertex[numDispVerts]; str.read((char *) dispverts, sizeof(DisplacedVertex) * numDispVerts); // Add the displaced vertices to the displaced vertex list for (i = 0; i < numDispVerts; i++) bsp_data->addDispVertex(dispverts[i]); // Clean up delete [] dispverts; } void VBSPReader::processGameData(std::istream & str, int offset, int length) { GameHeader gameHeader; GameLump * gameLumps; int i; // Read the header str.seekg(offset); str.read((char *) &gameHeader, sizeof(GameHeader)); // Create and read in the game lump list gameLumps = new GameLump[gameHeader.num_lumps]; str.read((char *) gameLumps, sizeof(GameLump) * gameHeader.num_lumps); // Iterate over the game lumps for (i = 0; i < gameHeader.num_lumps; i++) { // See if this is a lump we're interested in if (gameLumps[i].lump_id == STATIC_PROP_ID) { processStaticProps(str, gameLumps[i].lump_offset, gameLumps[i].lump_length, gameLumps[i].lump_version); } } // Clean up delete [] gameLumps; } void VBSPReader::processStaticProps(std::istream & str, int offset, int length, int lumpVersion) { StaticPropModelNames sprpModelNames; char modelName[130]; std::string modelStr; int i; StaticPropLeaves sprpLeaves; StaticProps sprpHeader; StaticPropV4 sprp4; StaticProp sprp5; // First, read the static prop models dictionary str.seekg(offset); str.read((char *) &sprpModelNames, sizeof(StaticPropModelNames)); for (i = 0; i < sprpModelNames.num_model_names; i++) { str.read(modelName, 128); modelName[128] = 0; modelStr = std::string(modelName); bsp_data->addStaticPropModel(modelStr); } // Next, skip over the static prop leaf array str.read((char *) &sprpLeaves, sizeof(StaticPropLeaves)); str.seekg(sprpLeaves.num_leaf_entries * sizeof(unsigned short), std::istream::cur); // Finally, read in the static prop entries str.read((char *) &sprpHeader, sizeof(StaticProps)); for (i = 0; i < sprpHeader.num_static_props; i++) { // The version number determines how much we read for each prop if (lumpVersion == 4) { // Read the static prop and add it to the bsp data str.read((char *) &sprp4, sizeof(StaticPropV4)); bsp_data->addStaticProp(sprp4); } else if (lumpVersion == 5) { // Read the static prop and add it to the bsp data str.read((char *) &sprp5, sizeof(StaticProp)); bsp_data->addStaticProp(sprp5); } } } std::string VBSPReader::getToken(std::string str, const char * delim, size_t & index) { size_t start, end; std::string token; // Look for the first non-occurrence of the delimiters start = str.find_first_not_of(delim, index); if (start != std::string::npos) { // From there, look for the first occurrence of a delimiter end = str.find_first_of(delim, start+1); if (end != std::string::npos) { // Found a delimiter, so grab the string in between token = str.substr(start, end-start); } else { // Ran off the end of the string, so just grab everything from // the first good character token = str.substr(start); } } else { // No token to be found token = ""; } // Update the index (in case we want to keep looking for tokens in this // string) if (end != std::string::npos) index = end+1; else index = std::string::npos; // Return the token return token; } ref_ptr VBSPReader::readTextureFile(std::string textureName) { int i; std::string texFile; std::string texPath; Image * texImage; Texture * texture; // Find the texture's image file texFile = std::string(textureName) + ".vtf"; texPath = findDataFile(texFile, CASE_INSENSITIVE); // If we don't find it right away, check in a "materials" subdirectory if (texPath.empty()) { texFile = "materials/" + std::string(textureName) + ".vtf"; texPath = findDataFile(texFile, CASE_INSENSITIVE); // Check up one directory if we don't find it here (the map file is // usually located in the "maps" directory, adjacent to the materials // directory) if (texPath.empty()) { texFile = "../materials/" + std::string(textureName) + ".vtf"; texPath = findDataFile(texFile, CASE_INSENSITIVE); } } // If we found the file, read it, otherwise bail if (!texPath.empty()) { texImage = readImageFile(texPath); // If we got the image, create the texture attribute if (texImage != NULL) { // Create the texture if (texImage->t() == 1) { texture = new Texture1D(); ((Texture1D *)texture)->setImage(texImage); } else if (texImage->r() == 1) { texture = new Texture2D(); ((Texture2D *)texture)->setImage(texImage); } else { texture = new Texture3D(); ((Texture3D *)texture)->setImage(texImage); } // Set texture attributes texture->setWrap(Texture::WRAP_S, Texture::REPEAT); texture->setWrap(Texture::WRAP_T, Texture::REPEAT); texture->setWrap(Texture::WRAP_R, Texture::REPEAT); texture->setFilter(Texture::MAG_FILTER, Texture::LINEAR); texture->setFilter(Texture::MIN_FILTER, Texture::LINEAR_MIPMAP_LINEAR); } else { // We were unable to find the texture file notify(WARN) << "Couldn't find texture " << textureName; notify(WARN) << std::endl; // No texture texture = NULL; } } else { // We were unable to find the texture file notify(WARN) << "Couldn't find texture " << textureName; notify(WARN) << std::endl; // No texture texture = NULL; } return texture; } ref_ptr VBSPReader::createBlendShader(Texture * tex1, Texture * tex2) { const char * blendVtxShaderCode = { "attribute float vBlendParam;\n" "\n" "varying float fBlendParam;\n" "\n" "void main(void)\n" "{\n" " vec3 normal, lightDir;\n" " vec4 ambient, diffuse;\n" " float nDotL;\n" "\n" " // Simple directional lighting (for now). We're assuming a\n" " // single light source\n" " // TODO: This is only used for terrain geometry, so it should be\n" " // lightmapped\n" " normal = normalize(gl_NormalMatrix * gl_Normal);\n" " lightDir = normalize(vec3(gl_LightSource[0].position));\n" " nDotL = max(dot(normal, lightDir), 0.0);\n" " ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;\n" " diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;\n" "\n" " // Calculate the vertex color\n" " gl_FrontColor = 0.1 + ambient + nDotL * diffuse;\n" "\n" " // Pass the texture blend parameter through to the fragment\n" " // shader\n" " fBlendParam = vBlendParam;\n" "\n" " // The basic transforms\n" " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" " gl_TexCoord[0] = vec4(gl_MultiTexCoord0.st, 0.0, 0.0);\n" "}\n" }; const char * blendFrgShaderCode = { "uniform sampler2D tex1;\n" "uniform sampler2D tex2;\n" "\n" "varying float fBlendParam;\n" "\n" "void main(void)\n" "{\n" " vec4 tex1Color;\n" " vec4 tex2Color;\n" "\n" " tex1Color = texture2D(tex1, gl_TexCoord[0].st) * fBlendParam;\n" " tex2Color = texture2D(tex2, gl_TexCoord[0].st) *\n" " (1.0 - fBlendParam);\n" "\n" " gl_FragColor = gl_Color * (tex1Color + tex2Color);\n" "}\n" }; // Create the stateset StateSet * stateSet = new StateSet(); // Add the two textures stateSet->setTextureAttributeAndModes(0, tex1, StateAttribute::ON); stateSet->setTextureAttributeAndModes(1, tex2, StateAttribute::ON); // Create the vertex and fragment shaders Shader * blendVtxShader = new Shader(Shader::VERTEX); blendVtxShader->setShaderSource(blendVtxShaderCode); Shader * blendFrgShader = new Shader(Shader::FRAGMENT); blendFrgShader->setShaderSource(blendFrgShaderCode); // Create the two texture uniforms Uniform * tex1Sampler = new Uniform(Uniform::SAMPLER_2D, "tex1"); tex1Sampler->set(0); Uniform * tex2Sampler = new Uniform(Uniform::SAMPLER_2D, "tex2"); tex2Sampler->set(1); // Create the program Program * blendProgram = new Program(); blendProgram->addShader(blendVtxShader); blendProgram->addShader(blendFrgShader); // The texture blending parameter will be on vertex attribute 1 blendProgram->addBindAttribLocation("vBlendParam", (GLuint) 1); // Add everything to the StateSet stateSet->addUniform(tex1Sampler); stateSet->addUniform(tex2Sampler); stateSet->setAttributeAndModes(blendProgram, StateAttribute::ON); // Return the StateSet return stateSet; } ref_ptr VBSPReader::readMaterialFile(std::string materialName) { std::string mtlFileName; std::string mtlPath; osgDB::ifstream * mtlFile; std::string line; std::string::size_type start = std::string::npos; std::string token; bool found = false; ref_ptr stateSet; std::string shaderName; osg::Image * texImage = 0; std::string texName; std::string tex2Name; ref_ptr texture; ref_ptr texture2; ref_ptr blend; bool translucent; // Find the material file mtlFileName = std::string(materialName) + ".vmt"; mtlPath = findDataFile(mtlFileName, CASE_INSENSITIVE); // If we don't find it right away, check in a "materials" subdirectory if (mtlPath.empty()) { mtlFileName = "materials/" + std::string(materialName) + ".vmt"; mtlPath = findDataFile(mtlFileName, CASE_INSENSITIVE); // Check up one directory if we don't find it here (the map file is // usually located in the "maps" directory, adjacent to the materials // directory) if (mtlPath.empty()) { mtlFileName = "../materials/" + std::string(materialName) + ".vmt"; mtlPath = findDataFile(mtlFileName, CASE_INSENSITIVE); } } // See if we found the file if (!mtlPath.empty()) { // Try to open the file, bail out if we fail mtlFile = new osgDB::ifstream(mtlPath.c_str(), std::ifstream::in); if (!mtlFile) return NULL; } else { // Didn't find the material file, so return NULL notify(WARN) << "Can't find material " << materialName << std::endl; return NULL; } // First, look for the shader name found = false; while ((!found) && (!mtlFile->eof())) { // Read a line from the file std::getline(*mtlFile, line); // Try to find the shader name start = 0; token = getToken(line, " \t\n\r\"", start); // If we got something, it must be the shader if ((!token.empty()) && (token.compare(0, 2, "//") != 0)) { shaderName = token; found = true; } } // If we didn't find a shader, this isn't a valid material file if (!found) { mtlFile->close(); notify(WARN) << "Material " << materialName << " isn't valid."; notify(WARN) << std::endl; return NULL; } // No textures loaded yet texture = NULL; texture2 = NULL; // Assume no transparency unless the properties say otherwise translucent = false; // Read the material properties next while (!mtlFile->eof()) { // Get the next line std::getline(*mtlFile, line); // Look for tokens starting at the beginning start = 0; token = getToken(line, " \t\n\r\"", start); while ((!token.empty()) && (token.compare(0, 2, "//") != 0)) { if (equalCaseInsensitive(token, "$basetexture")) { // Get the base texture name token = getToken(line, " \t\n\r\"", start); // Read the texture if (!token.empty()) texture = readTextureFile(token); } else if (equalCaseInsensitive(token, "$basetexture2")) { // Get the second base texture name token = getToken(line, " \t\n\r\"", start); // Read the texture if (!token.empty()) texture2 = readTextureFile(token); } else if ((equalCaseInsensitive(token, "$translucent")) || (equalCaseInsensitive(token, "$alphatest"))) { // Get the translucency setting token = getToken(line, " \t\n\r\"", start); // Interpret the setting if ((token == "1") || (token == "true")) translucent = true; } // Try the next token token = getToken(line, " \t\n\r\"", start); } } // Start with no StateSet (in case the stuff below fails) stateSet = NULL; // Check the shader's name if (equalCaseInsensitive(shaderName, "WorldVertexTransition")) { // This shader blends between two textures based on a per-vertex // attribute. This is used for displaced terrain surfaces in HL2 maps. stateSet = createBlendShader(texture.get(), texture2.get()); } else if (equalCaseInsensitive(shaderName, "UnlitGeneric")) { // Create the StateSet stateSet = new StateSet(); // Disable lighting on this StateSet stateSet->setMode(GL_LIGHTING, StateAttribute::OFF); // Add the texture attribute (or disable texturing if no base texture) if (texture != NULL) { stateSet->setTextureAttributeAndModes(0, texture.get(), StateAttribute::ON); // See if the material is translucent if (translucent) { // Create and apply a blend function attribute to the // state set blend = new BlendFunc(BlendFunc::SRC_ALPHA, BlendFunc::ONE_MINUS_SRC_ALPHA); stateSet->setAttributeAndModes(blend.get(), StateAttribute::ON); // Set the state set's rendering hint to transparent stateSet->setRenderingHint(StateSet::TRANSPARENT_BIN); } } else { notify(WARN) << "No base texture for material " << materialName; notify(WARN) << std::endl; stateSet->setTextureMode(0, GL_TEXTURE_2D, StateAttribute::OFF); } } else { // All other shaders fall back to fixed function // TODO: LightMappedGeneric shader // Create the StateSet stateSet = new StateSet(); // Add the texture attribute (or disable texturing if no base texture) if (texture != NULL) { stateSet->setTextureAttributeAndModes(0, texture.get(), StateAttribute::ON); // See if the material is translucent if (translucent) { // Create and apply a blend function attribute to the // state set blend = new BlendFunc(BlendFunc::SRC_ALPHA, BlendFunc::ONE_MINUS_SRC_ALPHA); stateSet->setAttributeAndModes(blend.get(), StateAttribute::ON); // Set the state set's rendering hint to transparent stateSet->setRenderingHint(StateSet::TRANSPARENT_BIN); } } else { notify(WARN) << "No base texture for material " << materialName; notify(WARN) << std::endl; stateSet->setTextureMode(0, GL_TEXTURE_2D, StateAttribute::OFF); } } // Close the file mtlFile->close(); // Return the resulting StateSet return stateSet; } void VBSPReader::createScene() { ref_ptr group; ref_ptr subGroup; Face currentFace; TexInfo currentTexInfo; TexData currentTexData; const char * texName; char currentTexName[256]; char prefix[64]; char * mtlPtr; char * tmpPtr; char tempTex[256]; std::string entityText; VBSPEntity * currentEntity; int i; ref_ptr stateSet; StaticProp staticProp; Matrixf transMat, rotMat; Quat yaw, pitch, roll; ref_ptr propXform; std::string propModel; std::string propFile; ref_ptr propNode; // Load the materials and create a StateSet for each one for (i = 0; i < bsp_data->getNumTexDatas(); i++) { // Get the texdata entry and texture name currentTexData = bsp_data->getTexData(i); texName = bsp_data-> getTexDataString(currentTexData.name_string_table_id).c_str(); strcpy(currentTexName, texName); // See if this is referring to an environment mapped material (we don't // handle this yet) sprintf(prefix, "maps/%s/", map_name.c_str()); if (strncmp(currentTexName, prefix, strlen(prefix)) == 0) { // This texture is referring to this map's PAK file, so it could // be an environment mapped texture (an existing material that is // modified by a cube map of the scene). If so, we just need to // get the base material name mtlPtr = currentTexName; mtlPtr += strlen(prefix); // Now, we're pointing at the path to the material itself, so copy // what we've got so far strcpy(tempTex, mtlPtr); // Now, we just need to trim the two or three cube map coordinates // from the end. // This isn't a perfect solution, but it should catch most cases. // The right way to do this would be to read the .vmt file from the // map's PAKFILE lump, and make use of the basetexture parameter in // it tmpPtr = strrchr(tempTex, '/'); mtlPtr = strrchr(tempTex, '_'); if ((mtlPtr != NULL) && (mtlPtr > tmpPtr)) *mtlPtr = 0; mtlPtr = strrchr(tempTex, '_'); if ((mtlPtr != NULL) && (mtlPtr > tmpPtr)) *mtlPtr = 0; mtlPtr = strrchr(tempTex, '_'); if ((mtlPtr != NULL) && (mtlPtr > tmpPtr)) *mtlPtr = 0; // That should be it, so make it the texture name strcpy(currentTexName, tempTex); } // Read the material for this geometry stateSet = readMaterialFile(currentTexName); // Whether we successfully created a StateSet or not, add it to the // bsp data list now bsp_data->addStateSet(stateSet.get()); } // Create the root group for the scene group = new Group(); // Iterate through the list of entities, and try to convert all the // visible entities to geometry for (i = 0; i < bsp_data->getNumEntities(); i++) { // Get the entity entityText = bsp_data->getEntity(i); currentEntity = new VBSPEntity(entityText, bsp_data); // See if the entity is visible if (currentEntity->isVisible()) { // Create geometry for the entity subGroup = currentEntity->createGeometry(); // If the entity's geometry is valid, add it to the main group if (subGroup.valid()) group->addChild(subGroup.get()); } // Done with this entity delete currentEntity; } // Iterate through the list of static props, and add them to the scene // as well for (i = 0; i < bsp_data->getNumStaticProps(); i++) { // Get the static prop staticProp = bsp_data->getStaticProp(i); // Create a MatrixTransform for this prop (scale the position from // inches to meters) transMat.makeTranslate(staticProp.prop_origin * 0.0254); pitch.makeRotate(osg::DegreesToRadians(staticProp.prop_angles.x()), Vec3f(0.0, 1.0, 0.0)); yaw.makeRotate(osg::DegreesToRadians(staticProp.prop_angles.y()), Vec3f(0.0, 0.0, 1.0)); roll.makeRotate(osg::DegreesToRadians(staticProp.prop_angles.z()), Vec3f(1.0, 0.0, 0.0)); rotMat.makeRotate(roll * pitch * yaw); propXform = new MatrixTransform(); propXform->setMatrix(rotMat * transMat); // Load the prop's model propModel = bsp_data->getStaticPropModel(staticProp.prop_type); propNode = osgDB::readNodeFile(propModel); // If we loaded the prop correctly, add it to the scene if (propNode.valid()) { propXform->addChild(propNode.get()); group->addChild(propXform.get()); } else { notify(WARN) << "Couldn't find static prop \"" << propModel; notify(WARN) << "\"." << std::endl; // Couldn't find the prop, so get rid of the transform node propXform = NULL; } } // Set the root node to the result root_node = group.get(); } bool VBSPReader::readFile(const std::string & file) { osgDB::ifstream * mapFile = 0; Header header; int i = 0; // Remember the map name map_name = getStrippedName(file); mapFile = new osgDB::ifstream(file.c_str(), std::ios::binary); if (!mapFile) return false; // Read the header mapFile->read((char *) &header, sizeof(Header)); // Load the bsp file lumps that we care about for (i = 0; i < MAX_LUMPS; i++) { if ((header.lump_table[i].file_offset != 0) && (header.lump_table[i].lump_length != 0)) { // Process the lump switch (i) { case ENTITIES_LUMP: processEntities(*mapFile, header.lump_table[i].file_offset, header.lump_table[i].lump_length); break; case PLANES_LUMP: processPlanes(*mapFile, header.lump_table[i].file_offset, header.lump_table[i].lump_length); break; case VERTICES_LUMP: processVertices(*mapFile, header.lump_table[i].file_offset, header.lump_table[i].lump_length); break; case EDGES_LUMP: processEdges(*mapFile, header.lump_table[i].file_offset, header.lump_table[i].lump_length); break; case SURFEDGES_LUMP: processSurfEdges(*mapFile, header.lump_table[i].file_offset, header.lump_table[i].lump_length); break; case MODELS_LUMP: processModels(*mapFile, header.lump_table[i].file_offset, header.lump_table[i].lump_length); break; case FACES_LUMP: processFaces(*mapFile, header.lump_table[i].file_offset, header.lump_table[i].lump_length); break; case TEXINFO_LUMP: processTexInfo(*mapFile, header.lump_table[i].file_offset, header.lump_table[i].lump_length); break; case TEXDATA_LUMP: processTexData(*mapFile, header.lump_table[i].file_offset, header.lump_table[i].lump_length); break; case TEXDATA_STRING_TABLE_LUMP: processTexDataStringTable(*mapFile, header.lump_table[i].file_offset, header.lump_table[i].lump_length); break; case TEXDATA_STRING_DATA_LUMP: processTexDataStringData(*mapFile, header.lump_table[i].file_offset, header.lump_table[i].lump_length); break; case DISPINFO_LUMP: processDispInfo(*mapFile, header.lump_table[i].file_offset, header.lump_table[i].lump_length); break; case DISP_VERTS_LUMP: processDispVerts(*mapFile, header.lump_table[i].file_offset, header.lump_table[i].lump_length); break; case GAME_LUMP: processGameData(*mapFile, header.lump_table[i].file_offset, header.lump_table[i].lump_length); break; } } } // Create the OSG scene from the BSP data createScene(); return true; } ref_ptr VBSPReader::getRootNode() { return root_node; }