Hi Paul,

I have now implemented what I think the code should look like to properly
handle the case when the texture extension is provided.  The code I've
implemented should default to using .vtf when no extension is otherwise
provided, but if one is provided then use it's extension regardless of it's
type, so if it's .vtf then it'll behave as you'd expect, but also will
handle .jpg etc.

Could you please try out the attached modified MDLReader.cpp.

If things work fine I'll check the changes into svn/trunk and OSG-3.2
branch.

-- The diffs are:

$ svn diff
Index: MDLReader.cpp
===================================================================
--- MDLReader.cpp       (revision 13761)
+++ MDLReader.cpp       (working copy)
@@ -148,20 +148,20 @@

 ref_ptr<Texture> MDLReader::readTextureFile(std::string textureName)
 {
-    std::string             texFile;
-    std::string             texPath;
-    osg::ref_ptr<Image>     texImage;
-    osg::ref_ptr<Texture>   texture;
-
     // Find the texture's image file
-    texFile = std::string(textureName) + ".vtf";
-    texPath = findDataFile(texFile, CASE_INSENSITIVE);
+    std::string texExtension =
osgDB::getFileExtensionIncludingDot(textureName);
+    std::string texBaseName = osgDB::getNameLessExtension(textureName);

+    if (texExtension.empty()) texExtension = ".vtf";
+
+    std::string texFile = texBaseName + texExtension;
+    std::string 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
-        texPath = findFileInPath("materials", textureName, ".vtf");
+        texPath = findFileInPath("materials", texBaseName, texExtension);

         // 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
@@ -169,18 +169,20 @@
         if (texPath.empty())
         {
             // Check for a leading slash and concatenate appropriately
-            texPath = findFileInPath("../materials", textureName, ".vtf");
+            texPath = findFileInPath("../materials", texBaseName,
texExtension);
         }
     }

     // If we found the file, read it, otherwise bail
     if (!texPath.empty())
     {
-        texImage = readRefImageFile(texPath);
+        osg::ref_ptr<Image> texImage = readRefImageFile(texPath);

         // If we got the image, create the texture attribute
         if (texImage.valid())
         {
+            osg::ref_ptr<Texture> texture;
+
             // Create the texture
             if (texImage->t() == 1)
             {
@@ -202,6 +204,8 @@
             texture->setFilter(Texture::MAG_FILTER, Texture::LINEAR);
             texture->setFilter(Texture::MIN_FILTER,
                                Texture::LINEAR_MIPMAP_LINEAR);
+
+            return texture;
         }
         else
         {
@@ -209,7 +213,7 @@
             OSG_WARN << "Couldn't find texture " << textureName <<
std::endl;

             // No texture
-            texture = NULL;
+            return NULL;
         }
     }
     else
@@ -218,10 +222,9 @@
         OSG_WARN << "Couldn't find texture " << textureName << std::endl;

         // No texture
-        texture = NULL;
+        return NULL;
     }

-    return texture;
 }
#include <osg/BlendFunc>
#include <osg/BoundingSphere>
#include <osg/Geometry>
#include <osg/Group>
#include <osg/Object>
#include <osg/Material>
#include <osg/Math>
#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;


namespace
{
    // Turn any '\' into '/', so that the path is usable on Windows and Unix.
    void sanitizePath(std::string& path)
    {
        size_t pos = 0;
        while ((pos = path.find_first_of("\\", pos)) != std::string::npos)
        {
            path[pos] = '/';
            ++pos;
        }
    }

    // Try to find 'searchPath'/'filename'.'extension' in 'materials' directory.
    std::string findFileInPath(const std::string& searchPath,
            const std::string& filename,
            const std::string& extension)
    {
        std::string filepath;

        if ((filename[0] == '\\') || (filename[0] == '/'))
        {
            filepath = searchPath + filename + extension;
        }
        else
        {
            filepath = searchPath + "/" + filename + extension;
        }

        // Look for the texture at this location
        //std::cerr << "findFileInPath trying '" << filepath << "'\n";
        filepath = findDataFile(filepath, CASE_INSENSITIVE);
        //std::cerr << "findFileInPath found '" << filepath << "'\n";

        return filepath;
    }

    // Try to find 'searchPath'/'path'/'filename'.'extension' in 'materials' directory.
    std::string findFileInPath(const std::string& searchPath,
            const std::string& path,
            const std::string& filename,
            const std::string& extension)
    {
        std::string filepath;

        if ((path[0] == '\\') || (path[0] == '/'))
        {
            filepath = searchPath + path + filename + extension;
        }
        else
        {
            filepath = searchPath + "/" + path + filename + extension;
        }

        // Look for the texture at this location
        //std::cerr << "findFileInPath trying '" << filepath << "'\n";
        filepath = findDataFile(filepath, CASE_INSENSITIVE);
        //std::cerr << "findFileInPath found '" << filepath << "'\n";

        return filepath;
    }
}

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;
    size_t end = std::string::npos;
    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)
{
    // Find the texture's image file
    std::string texExtension = osgDB::getFileExtensionIncludingDot(textureName);
    std::string texBaseName = osgDB::getNameLessExtension(textureName);

    if (texExtension.empty()) texExtension = ".vtf";

    std::string texFile = texBaseName + texExtension;
    std::string 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
        texPath = findFileInPath("materials", texBaseName, texExtension);

        // 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
            texPath = findFileInPath("../materials", texBaseName, texExtension);
        }
    }

    // If we found the file, read it, otherwise bail
    if (!texPath.empty())
    {
        osg::ref_ptr<Image> texImage = readRefImageFile(texPath);

        // If we got the image, create the texture attribute
        if (texImage.valid())
        {
            osg::ref_ptr<Texture> texture;

            // Create the texture
            if (texImage->t() == 1)
            {
                texture = new Texture1D(texImage.get());
            }
            else if (texImage->r() == 1)
            {
                texture = new Texture2D(texImage.get());
            }
            else
            {
                texture = new Texture3D(texImage.get());
            }

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

            return texture;
        }
        else
        {
            // We were unable to find the texture file
            OSG_WARN << "Couldn't find texture " << textureName << std::endl;

            // No texture
            return NULL;
        }
    }
    else
    {
        // We were unable to find the texture file
        OSG_WARN << "Couldn't find texture " << textureName << std::endl;

        // No texture
        return NULL;
    }

}


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;
    std::string              texName;
    std::string              tex2Name;
    ref_ptr<Texture>         texture;
    ref_ptr<Texture>         texture2;
    ref_ptr<Material>        material;
    ref_ptr<BlendFunc>       blend;
    bool                     translucent;
    double                   alpha;

    // 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()))
        {
            std::string path = *searchItr;
            sanitizePath(path);

            // 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
            mtlPath = findFileInPath("materials", path, materialName, ".vmt");

            // 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()))
            {
                std::string path = *searchItr;
                sanitizePath(path);

                // The search paths assume that materials are always located in
                // a "materials" subdirectory, but this time try going up one
                // level first
                mtlPath = findFileInPath("../materials", path, materialName, ".vmt");

                // Next path
                ++searchItr;
            }
        }
    }

    // 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
        OSG_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();
        OSG_WARN << "Material " << materialName << " isn't valid." << std::endl;
        return NULL;
    }

    // No textures loaded yet
    texture = NULL;
    texture2 = NULL;

    // Assume not translucent unless the properties say otherwise
    translucent = false;

    // Assume full opacity
    alpha = 1.0;

    // 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;
                }
            }
            else if (equalCaseInsensitive(token, "$alpha"))
            {
                // Get the translucency setting
                token = getToken(line, " \t\n\r\"", start);

                // Interpret the setting
                if (!token.empty())
                {
                   alpha = osg::asciiToDouble(token.c_str());
                }
            }

            // 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
        {
            OSG_WARN << "No base texture for material " << materialName << 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);
            stateSet->setMode(GL_BLEND, 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

        // Create the StateSet
        stateSet = new StateSet();

        // Add a material to the state set
        material = new Material();
        material->setAmbient(Material::FRONT_AND_BACK,
                             Vec4(1.0, 1.0, 1.0, 1.0) );
        material->setDiffuse(Material::FRONT_AND_BACK,
                             Vec4(1.0, 1.0, 1.0, 1.0) );
        material->setSpecular(Material::FRONT_AND_BACK,
                             Vec4(0.0, 0.0, 0.0, 1.0) );
        material->setShininess(Material::FRONT_AND_BACK, 1.0);
        material->setEmission(Material::FRONT_AND_BACK,
                              Vec4(0.0, 0.0, 0.0, 1.0) );
        material->setAlpha(Material::FRONT_AND_BACK, alpha);
        stateSet->setAttributeAndModes(material.get(), StateAttribute::ON);

        // 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) || (alpha < 1.0))
            {
                // Add the blending attribute as well
                blend = new BlendFunc(BlendFunc::SRC_ALPHA,
                                      BlendFunc::ONE_MINUS_SRC_ALPHA);
                stateSet->setAttributeAndModes(blend.get(),
                                               StateAttribute::ON);
                stateSet->setMode(GL_BLEND, StateAttribute::ON);

                // Set the rendering hint for this stateset to transparent
                stateSet->setRenderingHint(StateSet::TRANSPARENT_BIN);
            }
        }
        else
        {
            OSG_WARN << "No base texture for material " << materialName << 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)
{
    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;
    unsigned int      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 osgDB::ifstream(fileName.c_str(), std::ios::binary);
    if (!mdlFile)
    {
        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_NOTICE << "This is not a valid .mdl file" << 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 (vertex data) file that goes with this model
    vvdFile = findDataFile(getNameLessExtension(file) + ".vvd",
                           CASE_INSENSITIVE);
    vvdReader = new VVDReader();
    vvdReader->readFile(vvdFile);

    // Open the VTX file (index and primitive data) 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;
}


_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org

Reply via email to