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

Revision 10483, 20.8 kB (checked in by robert, 5 years ago)

Added .get() to fix build

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