root/OpenSceneGraph/branches/OpenSceneGraph-2.8/src/osgPlugins/mdl/MDLReader.cpp @ 11264

Revision 11264, 20.9 kB (checked in by paulmartz, 4 years ago)

2.8 branch: Mergine recent changes to FBX. Revisions in this commit: r11251, r11252, r11262.

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