root/OpenSceneGraph/trunk/src/osgPlugins/mdl/MDLReader.cpp @ 13041

Revision 13041, 21.4 kB (checked in by robert, 2 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
Line 
1#include <osg/BlendFunc>
2#include <osg/BoundingSphere>
3#include <osg/Geometry>
4#include <osg/Group>
5#include <osg/Object>
6#include <osg/Material>
7#include <osg/Math>
8#include <osg/MatrixTransform>
9#include <osg/Node>
10#include <osg/Notify>
11#include <osg/StateSet>
12#include <osg/Texture1D>
13#include <osg/Texture2D>
14#include <osg/Texture3D>
15#include <osgDB/Registry>
16#include <osgDB/FileUtils>
17#include <osgDB/ReadFile>
18#include <osg/io_utils>
19#include <iostream>
20
21#include "MDLReader.h"
22#include "VVDReader.h"
23#include "VTXReader.h"
24
25
26using namespace mdl;
27using namespace osg;
28using namespace osgDB;
29
30
31namespace
32{
33    // Turn any '\' into '/', so that the path is usable on Windows and Unix.
34    void sanitizePath(std::string& path)
35    {
36        size_t pos = 0;
37        while ((pos = path.find_first_of("\\", pos)) != std::string::npos)
38        {
39            path[pos] = '/';
40            ++pos;
41        }
42    }
43
44    // Try to find 'searchPath'/'filename'.'extension' in 'materials' directory.
45    std::string findFileInPath(const std::string& searchPath,
46            const std::string& filename,
47            const std::string& extension)
48    {
49        std::string filepath;
50
51        if ((filename[0] == '\\') || (filename[0] == '/'))
52        {
53            filepath = searchPath + filename + extension;
54        }
55        else
56        {
57            filepath = searchPath + "/" + filename + extension;
58        }
59
60        // Look for the texture at this location
61        //std::cerr << "findFileInPath trying '" << filepath << "'\n";
62        filepath = findDataFile(filepath, CASE_INSENSITIVE);
63        //std::cerr << "findFileInPath found '" << filepath << "'\n";
64
65        return filepath;
66    }
67
68    // Try to find 'searchPath'/'path'/'filename'.'extension' in 'materials' directory.
69    std::string findFileInPath(const std::string& searchPath,
70            const std::string& path,
71            const std::string& filename,
72            const std::string& extension)
73    {
74        std::string filepath;
75
76        if ((path[0] == '\\') || (path[0] == '/'))
77        {
78            filepath = searchPath + path + filename + extension;
79        }
80        else
81        {
82            filepath = searchPath + "/" + path + filename + extension;
83        }
84
85        // Look for the texture at this location
86        //std::cerr << "findFileInPath trying '" << filepath << "'\n";
87        filepath = findDataFile(filepath, CASE_INSENSITIVE);
88        //std::cerr << "findFileInPath found '" << filepath << "'\n";
89
90        return filepath;
91    }
92}
93
94MDLReader::MDLReader()
95{
96    // Start with no root node
97    root_node = NULL;
98}
99
100
101MDLReader::~MDLReader()
102{
103}
104
105
106std::string MDLReader::getToken(std::string str, const char * delim,
107                                size_t & index)
108{
109    size_t start;
110    size_t end = std::string::npos;
111    std::string   token;
112
113    // Look for the first non-occurrence of the delimiters
114    start = str.find_first_not_of(" \t\n\r\"", index);
115    if (start != std::string::npos)
116    {
117        // From there, look for the first occurrence of a delimiter
118        end = str.find_first_of(" \t\n\r\"", start+1);
119        if (end != std::string::npos)
120        {
121            // Found a delimiter, so grab the string in between
122            token = str.substr(start, end-start);
123        }
124        else
125        {
126            // Ran off the end of the string, so just grab everything from
127            // the first good character
128            token = str.substr(start);
129        }
130    }
131    else
132    {
133        // No token to be found
134        token = "";
135    }
136
137    // Update the index (in case we want to keep looking for tokens in this
138    // string)
139    if (end != std::string::npos)
140        index = end+1;
141    else
142        index = std::string::npos;
143
144    // Return the token
145    return token;
146}
147
148
149ref_ptr<Texture> MDLReader::readTextureFile(std::string textureName)
150{
151    std::string             texFile;
152    std::string             texPath;
153    osg::ref_ptr<Image>     texImage;
154    osg::ref_ptr<Texture>   texture;
155
156    // Find the texture's image file
157    texFile = std::string(textureName) + ".vtf";
158    texPath = findDataFile(texFile, CASE_INSENSITIVE);
159
160    // If we don't find it right away, check in a "materials" subdirectory
161    if (texPath.empty())
162    {
163        // Check for a leading slash and concatenate appropriately
164        texPath = findFileInPath("materials", textureName, ".vtf");
165
166        // Check up one directory if we don't find it here (the map file is
167        // usually located in the "maps" directory, adjacent to the materials
168        // directory)
169        if (texPath.empty())
170        {
171            // Check for a leading slash and concatenate appropriately
172            texPath = findFileInPath("../materials", textureName, ".vtf");
173        }
174    }
175
176    // If we found the file, read it, otherwise bail
177    if (!texPath.empty())
178    {
179        texImage = readRefImageFile(texPath);
180
181        // If we got the image, create the texture attribute
182        if (texImage.valid())
183        {
184            // Create the texture
185            if (texImage->t() == 1)
186            {
187                texture = new Texture1D(texImage.get());
188            }
189            else if (texImage->r() == 1)
190            {
191                texture = new Texture2D(texImage.get());
192            }
193            else
194            {
195                texture = new Texture3D(texImage.get());
196            }
197
198            // Set texture attributes
199            texture->setWrap(Texture::WRAP_S, Texture::REPEAT);
200            texture->setWrap(Texture::WRAP_T, Texture::REPEAT);
201            texture->setWrap(Texture::WRAP_R, Texture::REPEAT);
202            texture->setFilter(Texture::MAG_FILTER, Texture::LINEAR);
203            texture->setFilter(Texture::MIN_FILTER,
204                               Texture::LINEAR_MIPMAP_LINEAR);
205        }
206        else
207        {
208            // We were unable to find the texture file
209            OSG_WARN << "Couldn't find texture " << textureName << std::endl;
210
211            // No texture
212            texture = NULL;
213        }
214    }
215    else
216    {
217        // We were unable to find the texture file
218        OSG_WARN << "Couldn't find texture " << textureName << std::endl;
219
220        // No texture
221        texture = NULL;
222    }
223
224    return texture;
225}
226
227
228ref_ptr<StateSet> MDLReader::readMaterialFile(std::string materialName)
229{
230    std::string              mtlFileName;
231    std::string              mtlPath;
232    StringList::iterator     searchItr;
233    std::ifstream *          mtlFile;
234    std::string              line;
235    size_t                   start;
236    std::string              token;
237    bool                     found;
238    ref_ptr<StateSet>        stateSet;
239    std::string              shaderName;
240    std::string              texName;
241    std::string              tex2Name;
242    ref_ptr<Texture>         texture;
243    ref_ptr<Texture>         texture2;
244    ref_ptr<Material>        material;
245    ref_ptr<BlendFunc>       blend;
246    bool                     translucent;
247    double                   alpha;
248
249    // Find the material file
250    mtlFileName = std::string(materialName) + ".vmt";
251    mtlPath = findDataFile(mtlFileName, CASE_INSENSITIVE);
252
253    // If we don't find it right away, search the texture file search paths
254    if (mtlPath.empty())
255    {
256        searchItr = texture_paths.begin();
257        while ((mtlPath.empty()) && (searchItr != texture_paths.end()))
258        {
259            std::string path = *searchItr;
260            sanitizePath(path);
261
262            // The search paths assume that materials are always located in
263            // a "materials" subdirectory.  Also, check to see if there is
264            // a leading slash and concatenate appropriately
265            mtlPath = findFileInPath("materials", path, materialName, ".vmt");
266
267            // Next path
268            ++searchItr;
269        }
270
271        // If we still haven't found it, check up one directory level (the
272        // model file is usually located in the "models" directory, adjacent
273        // to the "materials" directory)
274        if (mtlPath.empty())
275        {
276            searchItr = texture_paths.begin();
277            while ((mtlPath.empty()) && (searchItr != texture_paths.end()))
278            {
279                std::string path = *searchItr;
280                sanitizePath(path);
281
282                // The search paths assume that materials are always located in
283                // a "materials" subdirectory, but this time try going up one
284                // level first
285                mtlPath = findFileInPath("../materials", path, materialName, ".vmt");
286
287                // Next path
288                ++searchItr;
289            }
290        }
291    }
292
293    // See if we found the file
294    if (!mtlPath.empty())
295    {
296        // Try to open the file, bail out if we fail
297        mtlFile = new osgDB::ifstream(mtlPath.c_str(), std::ifstream::in);
298        if (!mtlFile)
299            return NULL;
300    }
301    else
302    {
303        // Didn't find the material file, so return NULL
304        OSG_WARN << "Can't find material " << materialName << std::endl;
305        return NULL;
306    }
307
308    // First, look for the shader name
309    found = false;
310    while ((!found) && (!mtlFile->eof()))
311    {
312        // Read a line from the file
313        std::getline(*mtlFile, line);
314
315        // Try to find the shader name
316        start = 0;
317        token = getToken(line, " \t\n\r\"", start);
318
319        // If we got something, it must be the shader
320        if ((!token.empty()) && (token.compare(0, 2, "//") != 0))
321        {
322            shaderName = token;
323            found = true;
324        }
325    }
326
327    // If we didn't find a shader, this isn't a valid material file
328    if (!found)
329    {
330        mtlFile->close();
331        OSG_WARN << "Material " << materialName << " isn't valid." << std::endl;
332        return NULL;
333    }
334
335    // No textures loaded yet
336    texture = NULL;
337    texture2 = NULL;
338
339    // Assume not translucent unless the properties say otherwise
340    translucent = false;
341
342    // Assume full opacity
343    alpha = 1.0;
344
345    // Read the material properties next
346    while (!mtlFile->eof())
347    {
348        // Get the next line
349        std::getline(*mtlFile, line);
350
351        // Look for tokens starting at the beginning
352        start = 0;
353        token = getToken(line, " \t\n\r\"", start);
354
355        while ((!token.empty()) && (token.compare(0, 2, "//") != 0))
356        {
357            if (equalCaseInsensitive(token, "$basetexture"))
358            {
359                // Get the base texture name
360                token = getToken(line, " \t\n\r\"", start);
361
362                // Read the texture
363                if (!token.empty())
364                    texture = readTextureFile(token);
365            }
366            else if (equalCaseInsensitive(token, "$basetexture2"))
367            {
368                // Get the second base texture name
369                token = getToken(line, " \t\n\r\"", start);
370
371                // Read the texture
372                if (!token.empty())
373                    texture2 = readTextureFile(token);
374            }
375            else if ((equalCaseInsensitive(token, "$translucent")) ||
376                     (equalCaseInsensitive(token, "$alphatest")))
377            {
378                // Get the translucency setting
379                token = getToken(line, " \t\n\r\"", start);
380
381                // Interpret the setting
382                if (!token.empty())
383                {
384                    if ((token == "1") || (token == "true"))
385                        translucent = true;
386                }
387            }
388            else if (equalCaseInsensitive(token, "$alpha"))
389            {
390                // Get the translucency setting
391                token = getToken(line, " \t\n\r\"", start);
392
393                // Interpret the setting
394                if (!token.empty())
395                {
396                   alpha = osg::asciiToDouble(token.c_str());
397                }
398            }
399
400            // Try the next token
401            token = getToken(line, " \t\n\r\"", start);
402        }
403    }
404
405    // Start with no StateSet (in case the stuff below fails)
406    stateSet = NULL;
407
408    // Check the shader's name
409    if (equalCaseInsensitive(shaderName, "UnlitGeneric"))
410    {
411        // Create the StateSet
412        stateSet = new StateSet();
413
414        // Disable lighting on this StateSet
415        stateSet->setMode(GL_LIGHTING, StateAttribute::OFF);
416
417        // Add the texture attribute (or disable texturing if no base texture)
418        if (texture != NULL)
419        {
420            stateSet->setTextureAttributeAndModes(0, texture.get(),
421                                                  StateAttribute::ON);
422        }
423        else
424        {
425            OSG_WARN << "No base texture for material " << materialName << std::endl;
426            stateSet->setTextureMode(0, GL_TEXTURE_2D, StateAttribute::OFF);
427        }
428
429        // See if the material is translucent
430        if (translucent)
431        {
432            // Add the blending attribute as well
433            blend = new BlendFunc(BlendFunc::SRC_ALPHA,
434                                  BlendFunc::ONE_MINUS_SRC_ALPHA);
435            stateSet->setAttributeAndModes(blend.get(), StateAttribute::ON);
436            stateSet->setMode(GL_BLEND, StateAttribute::ON);
437
438            // Set the rendering hint for this stateset to transparent
439            stateSet->setRenderingHint(StateSet::TRANSPARENT_BIN);
440        }
441    }
442    else
443    {
444        // All other shaders fall back to fixed function
445
446        // Create the StateSet
447        stateSet = new StateSet();
448
449        // Add a material to the state set
450        material = new Material();
451        material->setAmbient(Material::FRONT_AND_BACK,
452                             Vec4(1.0, 1.0, 1.0, 1.0) );
453        material->setDiffuse(Material::FRONT_AND_BACK,
454                             Vec4(1.0, 1.0, 1.0, 1.0) );
455        material->setSpecular(Material::FRONT_AND_BACK,
456                             Vec4(0.0, 0.0, 0.0, 1.0) );
457        material->setShininess(Material::FRONT_AND_BACK, 1.0);
458        material->setEmission(Material::FRONT_AND_BACK,
459                              Vec4(0.0, 0.0, 0.0, 1.0) );
460        material->setAlpha(Material::FRONT_AND_BACK, alpha);
461        stateSet->setAttributeAndModes(material.get(), StateAttribute::ON);
462
463        // Add the texture attribute (or disable texturing if no base texture)
464        if (texture != NULL)
465        {
466            stateSet->setTextureAttributeAndModes(0, texture.get(),
467                                                  StateAttribute::ON);
468
469            // See if the material is translucent
470            if ((translucent) || (alpha < 1.0))
471            {
472                // Add the blending attribute as well
473                blend = new BlendFunc(BlendFunc::SRC_ALPHA,
474                                      BlendFunc::ONE_MINUS_SRC_ALPHA);
475                stateSet->setAttributeAndModes(blend.get(),
476                                               StateAttribute::ON);
477                stateSet->setMode(GL_BLEND, StateAttribute::ON);
478
479                // Set the rendering hint for this stateset to transparent
480                stateSet->setRenderingHint(StateSet::TRANSPARENT_BIN);
481            }
482        }
483        else
484        {
485            OSG_WARN << "No base texture for material " << materialName << std::endl;
486            stateSet->setTextureMode(0, GL_TEXTURE_2D, StateAttribute::OFF);
487        }
488    }
489
490    // Close the file
491    mtlFile->close();
492
493    // Return the resulting StateSet
494    return stateSet;
495}
496
497
498BodyPart * MDLReader::processBodyPart(std::istream * str, int offset)
499{
500    int              i;
501    MDLBodyPart *    part;
502    BodyPart *       partNode;
503    Model *          modelNode;
504
505    // Seek to the body part
506    str->seekg(offset);
507
508    // Read it
509    part = new MDLBodyPart;
510    str->read((char *) part, sizeof(MDLBodyPart));
511
512    // Create the body part node
513    partNode = new BodyPart(part);
514
515    // Process the models
516    for (i = 0; i < part->num_models; i++)
517    {
518        // Process the model
519        modelNode = processModel(str, offset + part->model_offset +
520                                      (i * sizeof(MDLModel)));
521
522        // Add the model to the body part
523        partNode->addModel(modelNode);
524    }
525
526    // Return the new node
527    return partNode;
528}
529
530
531Model * MDLReader::processModel(std::istream * str, int offset)
532{
533    int            i;
534    MDLModel *     model;
535    Model *        modelNode;
536    Mesh *         meshNode;
537
538    // Seek to the model
539    str->seekg(offset);
540
541    // Read it
542    model = new MDLModel;
543    str->read((char *) model, sizeof(MDLModel));
544
545    // Create the model node
546    modelNode = new Model(model);
547
548    // Process the meshes
549    for (i = 0; i < model->num_meshes; i++)
550    {
551        // Process the mesh
552        meshNode = processMesh(str, offset + model->mesh_offset +
553                                    (i * sizeof(MDLMesh)));
554
555        // Add the mesh to the model
556        modelNode->addMesh(meshNode);
557    }
558
559    // Return the model node
560    return modelNode;
561}
562
563
564Mesh * MDLReader::processMesh(std::istream * str, int offset)
565{
566    MDLMesh *      mesh;
567    Mesh *         meshNode;
568
569    // Seek to the mesh
570    str->seekg(offset);
571
572    // Read it
573    mesh = new MDLMesh;
574    str->read((char *) mesh, sizeof(MDLMesh));
575
576    // Create the mesh node
577    meshNode = new Mesh(mesh);
578
579    // Set the mesh's state set based on the material id
580    meshNode->setStateSet((state_sets[mesh->material_index]).get());
581
582    // Return the mesh node
583    return meshNode;
584}
585
586
587bool MDLReader::readFile(const std::string & file)
588{
589    std::string       baseName;
590    std::string       fileName;
591    std::ifstream *   mdlFile;
592    MDLHeader         header;
593    int               i;
594    unsigned int      j;
595    int               offset;
596    MDLRoot *         mdlRoot;
597    BodyPart *        partNode;
598    std::string       vvdFile;
599    VVDReader *       vvdReader;
600    std::string       vtxFile;
601    VTXReader *       vtxReader;
602
603    // Remember the model name
604    mdl_name = getStrippedName(file);
605
606    // Try to open the file
607    fileName = findDataFile(file, CASE_INSENSITIVE);
608    mdlFile = new osgDB::ifstream(fileName.c_str(), std::ios::binary);
609    if (!mdlFile)
610    {
611        OSG_NOTICE << "MDL file not found" << std::endl;
612        return false;
613    }
614
615    // Read the header
616    mdlFile->read((char *) &header, sizeof(MDLHeader));
617
618    // Make sure the file is a valid Valve MDL file
619    if (header.magic_number != MDL_MAGIC_NUMBER)
620    {
621        OSG_NOTICE << "This is not a valid .mdl file" << std::endl;
622
623        // Close the file before we quit
624        mdlFile->close();
625        delete mdlFile;
626
627        return false;
628    }
629
630    // Make sure the version is one that we handle
631    // TODO:  Not sure which versions are valid yet
632
633    // Get the texture paths from the file (we'll have to search these paths
634    // for each texture that we load)
635    for (i = 0; i < header.num_texture_paths; i++)
636    {
637        int               texPathBase;
638        int               texPathOffset;
639        char              texPath[256];
640
641        texPathBase = header.texture_path_offset + (i * sizeof(int));
642        mdlFile->seekg(texPathBase);
643        mdlFile->read((char *) &texPathOffset, sizeof(int));
644        mdlFile->seekg(texPathOffset);
645
646        // Read characters from the file until we reach the end of this path
647        j = 0;
648        do
649        {
650            mdlFile->get(texPath[j]);
651            j++;
652        }
653        while ((j < sizeof(texPath)) && (texPath[j-1] != 0));
654
655        // Store this path
656        texture_paths.push_back(texPath);
657    }
658
659    // Read the texture info from the file, and create a StateSet for each
660    // one
661    for (i = 0; i < header.num_textures; i++)
662    {
663        int                  texBase;
664        MDLTexture           tempTex;
665        char                 texName[256];
666        ref_ptr<StateSet>    stateSet;
667
668        texBase = header.texture_offset + (i * sizeof(MDLTexture));
669        mdlFile->seekg(texBase);
670        mdlFile->read((char *) &tempTex, sizeof(MDLTexture));
671        mdlFile->seekg(texBase + tempTex.tex_name_offset);
672        j = 0;
673        do
674        {
675            mdlFile->get(texName[j]);
676            j++;
677        }
678        while ((j < sizeof(texName)) && (texName[j-1] != 0));
679
680        // Load this texture
681        stateSet = readMaterialFile(texName);
682
683        // Add it to our list
684        state_sets.push_back(stateSet);
685    }
686
687    // Create the root node of the MDL tree
688    mdlRoot = new MDLRoot();
689
690    // Process the main model's body parts
691    for (i = 0; i < header.num_body_parts; i++)
692    {
693        // Calculate the offset to the next body part
694        offset = header.body_part_offset + (i * sizeof(MDLBodyPart));
695
696        // Process the body part and get the part's node
697        partNode = processBodyPart(mdlFile, offset);
698
699        // Add the body part to the MDL root node
700        mdlRoot->addBodyPart(partNode);
701    }
702
703    // Open the VVD (vertex data) file that goes with this model
704    vvdFile = findDataFile(getNameLessExtension(file) + ".vvd",
705                           CASE_INSENSITIVE);
706    vvdReader = new VVDReader();
707    vvdReader->readFile(vvdFile);
708
709    // Open the VTX file (index and primitive data) that goes with this model
710    // (at this point, I don't see a reason not to always just use the DX9
711    // version)
712    vtxFile = findDataFile(getNameLessExtension(file) + ".dx90.vtx",
713                           CASE_INSENSITIVE);
714    vtxReader = new VTXReader(vvdReader, mdlRoot);
715    vtxReader->readFile(vtxFile);
716
717    // Get the root group from the VTX reader
718    root_node = vtxReader->getModel();
719
720    // Close the .mdl file
721    mdlFile->close();
722    delete mdlFile;
723
724    // Close the two auxiliary readers
725    delete vvdReader;
726    delete vtxReader;
727
728    // Clean up the MDL tree (we don't need its data anymore)
729    delete mdlRoot;
730
731    // Return true to indicate success
732    return true;
733}
734
735
736ref_ptr<Node> MDLReader::getRootNode()
737{
738    return root_node;
739}
740
Note: See TracBrowser for help on using the browser.