From Jason Daly, "'ve been busy working on the Source engine plugins. There are several contributions in this submission:
osgDB/FileUtils.cpp: Needed this extra code to allow a true case-insensitive search. This is because the HL2 map and model files are often sloppy with case. For example, the file might look for materials/models/alyx/alyx_sheet.vtf, but the file is actually in materials/Models/Alyx/alyx_sheet.vtf. In case-insensitive mode, the new code recursively disassembles the path and checks each path element without regard to case. In case-sensitive mode, the code behaves exactly as it used to. The new code is also mostly skipped on Windows because of the case-insensitive file system. Previously, I did all of this with custom search code in the .bsp plugin, but this allows the user to tailor the search using OSGFILEPATH. There are some instructions in the plugins' README files about this. osgPlugins/mdl: This is a new plug-in for Half-Life 2 models (as opposed to maps). This allows you to load Source models individually, as well as allowing the .bsp plugin to load models (props) that are embedded into maps. Mdl files can contain simple object (crates, barrels, bottles), as well as fully articulated characters with skeletal animations. Currently, it can load the simple objects. It can also load the characters, but it can't load the skeletons or animations. osgPlugins/bsp: This contains all of the changes needed to load props along with the basic map geometry. There are also several bugs fixed. osgPlugins/vtf: This is the loader for Valve's texture format. Previously, we had agreed to put this in with the bsp plugin, but I didn't think of the .mdl plugin at that time. It's conceivable that a user might want to load models individually (not as part of a map), so the vtf reader does have to be separate. I also fixed a rather significant bug. I tested all of this code on RHEL 5.2 (32-bit), and Fedora 9 (64-bit). I'll be testing on Windows soon. I also attached a simple .mdl file, along with it's associated files and textures. Just extract the tarball into it's own directory, set your OSGFILEPATH to point at that directory, and load the model like this: osgviewer models/props_junk/gascan001a.mdl"
This commit is contained in:
677
src/osgPlugins/mdl/MDLReader.cpp
Normal file
677
src/osgPlugins/mdl/MDLReader.cpp
Normal file
@@ -0,0 +1,677 @@
|
||||
#include <osg/BlendFunc>
|
||||
#include <osg/BoundingSphere>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Group>
|
||||
#include <osg/Object>
|
||||
#include <osg/Material>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osg/Node>
|
||||
#include <osg/Notify>
|
||||
#include <osg/StateSet>
|
||||
#include <osg/Texture1D>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/Texture3D>
|
||||
#include <osgDB/Registry>
|
||||
#include <osgDB/FileUtils>
|
||||
#include <osgDB/ReadFile>
|
||||
#include <osg/io_utils>
|
||||
#include <iostream>
|
||||
|
||||
#include "MDLReader.h"
|
||||
#include "VVDReader.h"
|
||||
#include "VTXReader.h"
|
||||
|
||||
|
||||
using namespace mdl;
|
||||
using namespace osg;
|
||||
using namespace osgDB;
|
||||
|
||||
|
||||
MDLReader::MDLReader()
|
||||
{
|
||||
// Start with no root node
|
||||
root_node = NULL;
|
||||
}
|
||||
|
||||
|
||||
MDLReader::~MDLReader()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
std::string MDLReader::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(" \t\n\r\"", index);
|
||||
if (start != std::string::npos)
|
||||
{
|
||||
// From there, look for the first occurrence of a delimiter
|
||||
end = str.find_first_of(" \t\n\r\"", 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<Texture> MDLReader::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())
|
||||
{
|
||||
// Check for a leading slash and concatenate appropriately
|
||||
if ((textureName[0] == '\\') || (textureName[0] == '/'))
|
||||
texFile = "materials" + std::string(textureName) + ".vtf";
|
||||
else
|
||||
texFile = "materials/" + std::string(textureName) + ".vtf";
|
||||
|
||||
// Look for the texture at this location
|
||||
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())
|
||||
{
|
||||
// Check for a leading slash and concatenate appropriately
|
||||
if ((textureName[0] == '\\') || (textureName[0] == '/'))
|
||||
texFile = "../materials" + std::string(textureName) + ".vtf";
|
||||
else
|
||||
texFile = "../materials/" + std::string(textureName) + ".vtf";
|
||||
|
||||
// Look for the texture at this location
|
||||
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<StateSet> MDLReader::readMaterialFile(std::string materialName)
|
||||
{
|
||||
std::string mtlFileName;
|
||||
std::string mtlPath;
|
||||
StringList::iterator searchItr;
|
||||
std::ifstream * mtlFile;
|
||||
std::string line;
|
||||
size_t start;
|
||||
std::string token;
|
||||
bool found;
|
||||
ref_ptr<StateSet> stateSet;
|
||||
std::string shaderName;
|
||||
osg::Image * texImage;
|
||||
std::string texName;
|
||||
std::string tex2Name;
|
||||
ref_ptr<Texture> texture;
|
||||
ref_ptr<Texture> texture2;
|
||||
ref_ptr<BlendFunc> 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, search the texture file search paths
|
||||
if (mtlPath.empty())
|
||||
{
|
||||
searchItr = texture_paths.begin();
|
||||
while ((mtlPath.empty()) && (searchItr != texture_paths.end()))
|
||||
{
|
||||
// The search paths assume that materials are always located in
|
||||
// a "materials" subdirectory. Also, check to see if there is
|
||||
// a leading slash and concatenate appropriately
|
||||
if (((*searchItr)[0] == '\\') || ((*searchItr)[0] == '/'))
|
||||
mtlFileName = "materials" + *searchItr +
|
||||
std::string(materialName) + ".vmt";
|
||||
else
|
||||
mtlFileName = "materials/" + *searchItr +
|
||||
std::string(materialName) + ".vmt";
|
||||
|
||||
// Try to find the file in this path
|
||||
mtlPath = findDataFile(mtlFileName, CASE_INSENSITIVE);
|
||||
|
||||
// Next path
|
||||
searchItr++;
|
||||
}
|
||||
|
||||
// If we still haven't found it, check up one directory level (the
|
||||
// model file is usually located in the "models" directory, adjacent
|
||||
// to the "materials" directory)
|
||||
if (mtlPath.empty())
|
||||
{
|
||||
searchItr = texture_paths.begin();
|
||||
while ((mtlPath.empty()) && (searchItr != texture_paths.end()))
|
||||
{
|
||||
// The search paths assume that materials are always located in
|
||||
// a "materials" subdirectory, but this time try going up one
|
||||
// level first
|
||||
if (((*searchItr)[0] == '\\') || ((*searchItr)[0] == '/'))
|
||||
mtlFileName = "../materials" + *searchItr +
|
||||
std::string(materialName) + ".vmt";
|
||||
else
|
||||
mtlFileName = "../materials/" + *searchItr +
|
||||
std::string(materialName) + ".vmt";
|
||||
|
||||
// Try to find the file in this path
|
||||
mtlPath = findDataFile(mtlFileName, CASE_INSENSITIVE);
|
||||
|
||||
// Next path
|
||||
searchItr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See if we found the file
|
||||
if (!mtlPath.empty())
|
||||
{
|
||||
// Try to open the file, bail out if we fail
|
||||
mtlFile = new std::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 not translucent 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.empty())
|
||||
{
|
||||
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, "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);
|
||||
}
|
||||
else
|
||||
{
|
||||
notify(WARN) << "No base texture for material " << materialName;
|
||||
notify(WARN) << std::endl;
|
||||
stateSet->setTextureMode(0, GL_TEXTURE_2D, StateAttribute::OFF);
|
||||
}
|
||||
|
||||
// See if the material is translucent
|
||||
if (translucent)
|
||||
{
|
||||
// Add the blending attribute as well
|
||||
blend = new BlendFunc(BlendFunc::SRC_ALPHA,
|
||||
BlendFunc::ONE_MINUS_SRC_ALPHA);
|
||||
stateSet->setAttributeAndModes(blend.get(), StateAttribute::ON);
|
||||
|
||||
// Set the rendering hint for this stateset to transparent
|
||||
stateSet->setRenderingHint(StateSet::TRANSPARENT_BIN);
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
// Add the blending attribute as well
|
||||
blend = new BlendFunc(BlendFunc::SRC_ALPHA,
|
||||
BlendFunc::ONE_MINUS_SRC_ALPHA);
|
||||
stateSet->setAttributeAndModes(blend.get(),
|
||||
StateAttribute::ON);
|
||||
|
||||
// Set the rendering hint for this stateset 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;
|
||||
}
|
||||
|
||||
|
||||
BodyPart * MDLReader::processBodyPart(std::istream * str, int offset)
|
||||
{
|
||||
int i;
|
||||
MDLBodyPart * part;
|
||||
BodyPart * partNode;
|
||||
Model * modelNode;
|
||||
|
||||
// Seek to the body part
|
||||
str->seekg(offset);
|
||||
|
||||
// Read it
|
||||
part = new MDLBodyPart;
|
||||
str->read((char *) part, sizeof(MDLBodyPart));
|
||||
|
||||
// Create the body part node
|
||||
partNode = new BodyPart(part);
|
||||
|
||||
// Process the models
|
||||
for (i = 0; i < part->num_models; i++)
|
||||
{
|
||||
// Process the model
|
||||
modelNode = processModel(str, offset + part->model_offset +
|
||||
(i * sizeof(MDLModel)));
|
||||
|
||||
// Add the model to the body part
|
||||
partNode->addModel(modelNode);
|
||||
}
|
||||
|
||||
// Return the new node
|
||||
return partNode;
|
||||
}
|
||||
|
||||
|
||||
Model * MDLReader::processModel(std::istream * str, int offset)
|
||||
{
|
||||
int i;
|
||||
MDLModel * model;
|
||||
Model * modelNode;
|
||||
Mesh * meshNode;
|
||||
|
||||
// Seek to the model
|
||||
str->seekg(offset);
|
||||
|
||||
// Read it
|
||||
model = new MDLModel;
|
||||
str->read((char *) model, sizeof(MDLModel));
|
||||
|
||||
// Create the model node
|
||||
modelNode = new Model(model);
|
||||
|
||||
// Process the meshes
|
||||
for (i = 0; i < model->num_meshes; i++)
|
||||
{
|
||||
// Process the mesh
|
||||
meshNode = processMesh(str, offset + model->mesh_offset +
|
||||
(i * sizeof(MDLMesh)));
|
||||
|
||||
// Add the mesh to the model
|
||||
modelNode->addMesh(meshNode);
|
||||
}
|
||||
|
||||
// Return the model node
|
||||
return modelNode;
|
||||
}
|
||||
|
||||
|
||||
Mesh * MDLReader::processMesh(std::istream * str, int offset)
|
||||
{
|
||||
int i;
|
||||
MDLMesh * mesh;
|
||||
Mesh * meshNode;
|
||||
|
||||
// Seek to the mesh
|
||||
str->seekg(offset);
|
||||
|
||||
// Read it
|
||||
mesh = new MDLMesh;
|
||||
str->read((char *) mesh, sizeof(MDLMesh));
|
||||
|
||||
// Create the mesh node
|
||||
meshNode = new Mesh(mesh);
|
||||
|
||||
// Set the mesh's state set based on the material id
|
||||
meshNode->setStateSet((state_sets[mesh->material_index]).get());
|
||||
|
||||
// Return the mesh node
|
||||
return meshNode;
|
||||
}
|
||||
|
||||
|
||||
bool MDLReader::readFile(const std::string & file)
|
||||
{
|
||||
std::string baseName;
|
||||
std::string fileName;
|
||||
std::ifstream * mdlFile;
|
||||
MDLHeader header;
|
||||
int i, j;
|
||||
int offset;
|
||||
MDLRoot * mdlRoot;
|
||||
BodyPart * partNode;
|
||||
std::string vvdFile;
|
||||
VVDReader * vvdReader;
|
||||
std::string vtxFile;
|
||||
VTXReader * vtxReader;
|
||||
|
||||
// Remember the model name
|
||||
mdl_name = getStrippedName(file);
|
||||
|
||||
// Try to open the file
|
||||
fileName = findDataFile(file, CASE_INSENSITIVE);
|
||||
mdlFile = new std::ifstream(fileName.c_str(), std::ios::binary);
|
||||
if (!mdlFile)
|
||||
{
|
||||
osg::notify(osg::NOTICE) << "MDL file not found" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the header
|
||||
mdlFile->read((char *) &header, sizeof(MDLHeader));
|
||||
|
||||
// Make sure the file is a valid Valve MDL file
|
||||
if (header.magic_number != MDL_MAGIC_NUMBER)
|
||||
{
|
||||
osg::notify(osg::NOTICE) << "This is not a valid .mdl file";
|
||||
osg::notify(osg::NOTICE) << std::endl;
|
||||
|
||||
// Close the file before we quit
|
||||
mdlFile->close();
|
||||
delete mdlFile;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the version is one that we handle
|
||||
// TODO: Not sure which versions are valid yet
|
||||
|
||||
// Get the texture paths from the file (we'll have to search these paths
|
||||
// for each texture that we load)
|
||||
for (i = 0; i < header.num_texture_paths; i++)
|
||||
{
|
||||
int texPathBase;
|
||||
int texPathOffset;
|
||||
char texPath[256];
|
||||
|
||||
texPathBase = header.texture_path_offset + (i * sizeof(int));
|
||||
mdlFile->seekg(texPathBase);
|
||||
mdlFile->read((char *) &texPathOffset, sizeof(int));
|
||||
mdlFile->seekg(texPathOffset);
|
||||
|
||||
// Read characters from the file until we reach the end of this path
|
||||
j = 0;
|
||||
do
|
||||
{
|
||||
mdlFile->get(texPath[j]);
|
||||
j++;
|
||||
}
|
||||
while ((j < sizeof(texPath)) && (texPath[j-1] != 0));
|
||||
|
||||
// Store this path
|
||||
texture_paths.push_back(texPath);
|
||||
}
|
||||
|
||||
// Read the texture info from the file, and create a StateSet for each
|
||||
// one
|
||||
for (i = 0; i < header.num_textures; i++)
|
||||
{
|
||||
int texBase;
|
||||
MDLTexture tempTex;
|
||||
char texName[256];
|
||||
ref_ptr<StateSet> stateSet;
|
||||
|
||||
texBase = header.texture_offset + (i * sizeof(MDLTexture));
|
||||
mdlFile->seekg(texBase);
|
||||
mdlFile->read((char *) &tempTex, sizeof(MDLTexture));
|
||||
mdlFile->seekg(texBase + tempTex.tex_name_offset);
|
||||
j = 0;
|
||||
do
|
||||
{
|
||||
mdlFile->get(texName[j]);
|
||||
j++;
|
||||
}
|
||||
while ((j < sizeof(texName)) && (texName[j-1] != 0));
|
||||
|
||||
// Load this texture
|
||||
stateSet = readMaterialFile(texName);
|
||||
|
||||
// Add it to our list
|
||||
state_sets.push_back(stateSet);
|
||||
}
|
||||
|
||||
// Create the root node of the MDL tree
|
||||
mdlRoot = new MDLRoot();
|
||||
|
||||
// Process the main model's body parts
|
||||
for (i = 0; i < header.num_body_parts; i++)
|
||||
{
|
||||
// Calculate the offset to the next body part
|
||||
offset = header.body_part_offset + (i * sizeof(MDLBodyPart));
|
||||
|
||||
// Process the body part and get the part's node
|
||||
partNode = processBodyPart(mdlFile, offset);
|
||||
|
||||
// Add the body part to the MDL root node
|
||||
mdlRoot->addBodyPart(partNode);
|
||||
}
|
||||
|
||||
// Open the VVD file that goes with this model
|
||||
vvdFile = findDataFile(getNameLessExtension(file) + ".vvd",
|
||||
CASE_INSENSITIVE);
|
||||
vvdReader = new VVDReader();
|
||||
vvdReader->readFile(vvdFile);
|
||||
|
||||
// Open the VTX file that goes with this model (at this point, I don't
|
||||
// see a reason not to always just use the DX9 version)
|
||||
vtxFile = findDataFile(getNameLessExtension(file) + ".dx90.vtx",
|
||||
CASE_INSENSITIVE);
|
||||
vtxReader = new VTXReader(vvdReader, mdlRoot);
|
||||
vtxReader->readFile(vtxFile);
|
||||
|
||||
// Get the root group from the VTX reader
|
||||
root_node = vtxReader->getModel();
|
||||
|
||||
// Close the .mdl file
|
||||
mdlFile->close();
|
||||
delete mdlFile;
|
||||
|
||||
// Close the two auxiliary readers
|
||||
delete vvdReader;
|
||||
delete vtxReader;
|
||||
|
||||
// Clean up the MDL tree (we don't need its data anymore)
|
||||
delete mdlRoot;
|
||||
|
||||
// Return true to indicate success
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
ref_ptr<Node> MDLReader::getRootNode()
|
||||
{
|
||||
return root_node;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user