root/OpenSceneGraph/trunk/src/osgPlugins/obj/ReaderWriterOBJ.cpp @ 13041

Revision 13041, 32.4 kB (checked in by robert, 3 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1// -*-c++-*-
2
3/*
4 * Wavefront OBJ loader for Open Scene Graph
5 *
6 * Copyright (C) 2001,2007 Ulrich Hertlein <u.hertlein@sandbox.de>
7 *
8 * Modified by Robert Osfield to support per Drawable coord, normal and
9 * texture coord arrays, bug fixes, and support for texture mapping.
10 *
11 * Writing support added 2007 by Stephan Huber, http://digitalmind.de,
12 * some ideas taken from the dae-plugin
13 *
14 * The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for
15 * real-time rendering of large 3D photo-realistic models.
16 * The OSG homepage is http://www.openscenegraph.org/
17 */
18
19#if defined(_MSC_VER)
20    #pragma warning( disable : 4786 )
21#endif
22
23#include <stdlib.h>
24#include <string>
25
26#include <osg/Notify>
27#include <osg/Node>
28#include <osg/MatrixTransform>
29#include <osg/Geode>
30#include <osg/Vec3f>
31
32#include <osg/Geometry>
33#include <osg/StateSet>
34#include <osg/Material>
35#include <osg/Texture2D>
36#include <osg/TexGen>
37#include <osg/TexMat>
38
39#include <osgDB/Registry>
40#include <osgDB/ReadFile>
41#include <osgDB/FileUtils>
42#include <osgDB/FileNameUtils>
43
44#include <osgUtil/TriStripVisitor>
45#include <osgUtil/SmoothingVisitor>
46#include <osgUtil/Tessellator>
47
48#include "obj.h"
49#include "OBJWriterNodeVisitor.h"
50
51#include <map>
52#include <set>
53
54class ReaderWriterOBJ : public osgDB::ReaderWriter
55{
56public:
57    ReaderWriterOBJ()
58    {
59        supportsExtension("obj","Alias Wavefront OBJ format");
60        supportsOption("noRotation","Do not do the default rotate about X axis");
61        supportsOption("noTesselateLargePolygons","Do not do the default tesselation of large polygons");
62        supportsOption("noTriStripPolygons","Do not do the default tri stripping of polygons");
63        supportsOption("generateFacetNormals","generate facet normals for verticies without normals");
64
65        supportsOption("DIFFUSE=<unit>", "Set texture unit for diffuse texture");
66        supportsOption("AMBIENT=<unit>", "Set texture unit for ambient texture");
67        supportsOption("SPECULAR=<unit>", "Set texture unit for specular texture");
68        supportsOption("SPECULAR_EXPONENT=<unit>", "Set texture unit for specular exponent texture");
69        supportsOption("OPACITY=<unit>", "Set texture unit for opacity/dissolve texture");
70        supportsOption("BUMP=<unit>", "Set texture unit for bumpmap texture");
71        supportsOption("DISPLACEMENT=<unit>", "Set texture unit for displacement texture");
72        supportsOption("REFLECTION=<unit>", "Set texture unit for reflection texture");
73
74    }
75
76    virtual const char* className() const { return "Wavefront OBJ Reader"; }
77
78    virtual ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const;
79
80    virtual ReadResult readNode(std::istream& fin, const Options* options) const;
81
82    virtual WriteResult writeObject(const osg::Object& obj,const std::string& fileName,const Options* options=NULL) const
83    {
84        const osg::Node* node = dynamic_cast<const osg::Node*>(&obj);
85        if (node)
86            return writeNode(*node, fileName, options);
87        else
88            return WriteResult(WriteResult::FILE_NOT_HANDLED);
89    }
90
91    virtual WriteResult writeNode(const osg::Node& node,const std::string& fileName,const Options* options =NULL) const
92    {
93        if (!acceptsExtension(osgDB::getFileExtension(fileName)))
94            return WriteResult(WriteResult::FILE_NOT_HANDLED);
95
96        osgDB::ofstream f(fileName.c_str());
97        std::string materialFile = osgDB::getNameLessExtension(fileName) + ".mtl";
98        OBJWriterNodeVisitor nv(f, osgDB::getSimpleFileName(materialFile));
99
100        // we must cast away constness
101        (const_cast<osg::Node*>(&node))->accept(nv);
102
103        osgDB::ofstream mf(materialFile.c_str());
104        nv.writeMaterials(mf);
105
106        return WriteResult(WriteResult::FILE_SAVED);
107    }
108
109
110    virtual WriteResult writeObject(const osg::Object& obj,std::ostream& fout,const Options* options=NULL) const
111    {
112        const osg::Node* node = dynamic_cast<const osg::Node*>(&obj);
113        if (node)
114            return writeNode(*node, fout, options);
115        else
116            return WriteResult(WriteResult::FILE_NOT_HANDLED);
117    }
118
119    virtual WriteResult writeNode(const osg::Node& node,std::ostream& fout,const Options* =NULL) const
120    {
121        // writing to a stream does not support materials
122
123        OBJWriterNodeVisitor nv(fout);
124
125        // we must cast away constness
126        (const_cast<osg::Node*>(&node))->accept(nv);
127
128        return WriteResult(WriteResult::FILE_SAVED);
129    }
130
131
132
133protected:
134
135     struct ObjOptionsStruct {
136        bool rotate;
137        bool noTesselateLargePolygons;
138        bool noTriStripPolygons;
139        bool generateFacetNormals;
140        bool fixBlackMaterials;
141        // This is the order in which the materials will be assigned to texture maps, unless
142        // otherwise overriden
143        typedef std::vector< std::pair<int,obj::Material::Map::TextureMapType> > TextureAllocationMap;
144        TextureAllocationMap textureUnitAllocation;
145    };
146
147    typedef std::map< std::string, osg::ref_ptr<osg::StateSet> > MaterialToStateSetMap;
148
149    void buildMaterialToStateSetMap(obj::Model& model, MaterialToStateSetMap& materialToSetSetMapObj, ObjOptionsStruct& localOptions, const Options* options) const;
150
151    osg::Geometry* convertElementListToGeometry(obj::Model& model, obj::Model::ElementList& elementList, ObjOptionsStruct& localOptions) const;
152
153    osg::Node* convertModelToSceneGraph(obj::Model& model, ObjOptionsStruct& localOptions, const Options* options) const;
154
155    inline osg::Vec3 transformVertex(const osg::Vec3& vec, const bool rotate) const ;
156    inline osg::Vec3 transformNormal(const osg::Vec3& vec, const bool rotate) const ;
157
158    ObjOptionsStruct parseOptions(const Options* options) const;
159
160
161};
162
163inline osg::Vec3 ReaderWriterOBJ::transformVertex(const osg::Vec3& vec, const bool rotate) const
164{
165    if(rotate==true)
166    {
167        return osg::Vec3(vec.x(),-vec.z(),vec.y());
168    }
169    else
170    {
171        return vec;
172    }
173}
174
175inline osg::Vec3 ReaderWriterOBJ::transformNormal(const osg::Vec3& vec, const bool rotate) const
176{
177    if(rotate==true)
178    {
179        return osg::Vec3(vec.x(),-vec.z(),vec.y());
180    }
181    else
182    {
183        return vec;
184    }
185}
186
187
188// register with Registry to instantiate the above reader/writer.
189REGISTER_OSGPLUGIN(obj, ReaderWriterOBJ)
190
191static void load_material_texture(    obj::Model &model,
192                                    obj::Material::Map &map,
193                                    osg::StateSet *stateset,
194                                    const unsigned int texture_unit,
195                                    const osgDB::Options* options)
196{
197    std::string filename = map.name;
198    if (!filename.empty())
199    {
200        osg::ref_ptr< osg::Image > image;
201        if ( !model.getDatabasePath().empty() )
202        {
203            // first try with database path of parent.
204            image = osgDB::readRefImageFile(model.getDatabasePath()+'/'+filename, options);
205        }
206
207        if ( !image.valid() )
208        {
209            // if not already set then try the filename as is.
210            image = osgDB::readRefImageFile(filename, options);
211        }
212
213        if ( image.valid() )
214        {
215            osg::Texture2D* texture = new osg::Texture2D( image.get() );
216            osg::Texture::WrapMode textureWrapMode;
217            if(map.clamp == true)
218            {
219                textureWrapMode = osg::Texture::CLAMP_TO_BORDER;
220                texture->setBorderColor(osg::Vec4(0.0,0.0,0.0,0.0));    // transparent
221                //stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
222                //stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
223            }
224            else
225            {
226                textureWrapMode = osg::Texture::REPEAT;
227            }
228
229            texture->setWrap(osg::Texture2D::WRAP_R, textureWrapMode);
230            texture->setWrap(osg::Texture2D::WRAP_S, textureWrapMode);
231            texture->setWrap(osg::Texture2D::WRAP_T, textureWrapMode);
232            stateset->setTextureAttributeAndModes( texture_unit, texture,osg::StateAttribute::ON );
233
234            if ( map.type == obj::Material::Map::REFLECTION )
235            {
236                osg::TexGen* texgen = new osg::TexGen;
237                texgen->setMode(osg::TexGen::SPHERE_MAP);
238                stateset->setTextureAttributeAndModes( texture_unit,texgen,osg::StateAttribute::ON );
239            }
240
241            if  ( image->isImageTranslucent())
242            {
243                OSG_INFO<<"Found transparent image"<<std::endl;
244                stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
245                stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
246            }
247        }
248    }
249
250    if (map.uScale != 1.0f || map.vScale != 1.0f ||
251            map.uOffset != 0.0f || map.vOffset != 0.0f)
252    {
253        osg::Matrix mat;
254        if (map.uScale != 1.0f || map.vScale != 1.0f)
255        {
256            OSG_DEBUG << "Obj TexMat scale=" << map.uScale << "," << map.vScale << std::endl;
257            mat *= osg::Matrix::scale(map.uScale, map.vScale, 1.0);
258        }
259        if (map.uOffset != 0.0f || map.vOffset != 0.0f)
260        {
261            OSG_DEBUG << "Obj TexMat offset=" << map.uOffset << "," << map.uOffset << std::endl;
262            mat *= osg::Matrix::translate(map.uOffset, map.vOffset, 0.0);
263        }
264
265        osg::TexMat* texmat = new osg::TexMat;
266        texmat->setMatrix(mat);
267        stateset->setTextureAttributeAndModes( texture_unit,texmat,osg::StateAttribute::ON );
268    }
269}
270
271
272void ReaderWriterOBJ::buildMaterialToStateSetMap(obj::Model& model, MaterialToStateSetMap& materialToStateSetMap, ObjOptionsStruct& localOptions, const Options* options) const
273{
274    if (localOptions.fixBlackMaterials)
275    {
276        // hack to fix Maya exported models that contian all black materials.
277        int numBlack = 0;
278        int numNotBlack = 0;
279        obj::Model::MaterialMap::iterator itr;
280        for(itr = model.materialMap.begin();
281            itr != model.materialMap.end();
282            ++itr)
283        {
284            obj::Material& material = itr->second;
285            if (material.ambient==osg::Vec4(0.0f,0.0f,0.0f,1.0f) &&
286                material.diffuse==osg::Vec4(0.0f,0.0f,0.0f,1.0f))
287            {
288                ++numBlack;
289            }
290            else
291            {
292                ++numNotBlack;
293            }
294        }
295
296        if (numNotBlack==0 && numBlack!=0)
297        {
298            for(itr = model.materialMap.begin();
299                itr != model.materialMap.end();
300                ++itr)
301            {
302                obj::Material& material = itr->second;
303                if (material.ambient==osg::Vec4(0.0f,0.0f,0.0f,1.0f) &&
304                    material.diffuse==osg::Vec4(0.0f,0.0f,0.0f,1.0f))
305                {
306                    material.ambient.set(0.3f,0.3f,0.3f,1.0f);
307                    material.diffuse.set(1.0f,1.0f,1.0f,1.0f);
308                }
309            }
310        }
311    }
312
313    for(obj::Model::MaterialMap::iterator itr = model.materialMap.begin();
314        itr != model.materialMap.end();
315        ++itr)
316    {
317        obj::Material& material = itr->second;
318
319        osg::ref_ptr< osg::StateSet > stateset = new osg::StateSet;
320
321        bool isTransparent = false;
322
323        // handle material colors
324        // http://java3d.j3d.org/utilities/loaders/obj/sun.html
325        if (material.illum != 0)
326        {
327            osg::Material* osg_material = new osg::Material;
328            stateset->setAttribute(osg_material);
329            osg_material->setName(material.name);
330            osg_material->setAmbient(osg::Material::FRONT_AND_BACK,material.ambient);
331            osg_material->setDiffuse(osg::Material::FRONT_AND_BACK,material.diffuse);
332            osg_material->setEmission(osg::Material::FRONT_AND_BACK,material.emissive);
333
334            if (material.illum == 2) {
335                osg_material->setSpecular(osg::Material::FRONT_AND_BACK,material.specular);
336            } else {
337                osg_material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4(0,0,0,1));
338            }
339            osg_material->setShininess(osg::Material::FRONT_AND_BACK,(material.Ns/1000.0f)*128.0f ); // note OBJ shiniess is 0..1000.
340
341            if (material.ambient[3]!=1.0 ||
342                material.diffuse[3]!=1.0 ||
343                material.specular[3]!=1.0||
344                material.emissive[3]!=1.0)
345            {
346                OSG_INFO<<"Found transparent material"<<std::endl;
347                isTransparent = true;
348            }
349        }
350
351        // If the user has explicitly set the required texture type to unit map via the options
352        // string, then we load ONLY those textures that are in the map.
353        if(localOptions.textureUnitAllocation.size()>0)
354        {
355            for(unsigned int i=0;i<localOptions.textureUnitAllocation.size();i++)
356            {
357                // firstly, get the option set pair
358                int unit = localOptions.textureUnitAllocation[i].first;
359                obj::Material::Map::TextureMapType type = localOptions.textureUnitAllocation[i].second;
360                // secondly, see if this texture type (e.g. DIFFUSE) is one of those in the material
361                int index = -1;
362                for(unsigned int j=0;j<material.maps.size();j++)
363                {
364                    if(material.maps[j].type == type)
365                    {
366                        index = (int) j;
367                        break;
368                    }
369                }
370                if(index>=0) load_material_texture( model, material.maps[index], stateset.get(), unit, options );
371            }
372        }
373        // If the user has set no options, then we load them up in the order contained in the enum. This
374        // latter method is an attempt not to break user's existing code
375        else
376        {
377            int unit = 0;
378            for(int i=0;i<(int) obj::Material::Map::UNKNOWN;i++) // for each type
379            {
380                obj::Material::Map::TextureMapType type = (obj::Material::Map::TextureMapType) i;
381                // see if this texture type (e.g. DIFFUSE) is one of those in the material
382                int index = -1;
383                for(unsigned int j=0;j<material.maps.size();j++)
384                {
385                    if(material.maps[j].type == type)
386                    {
387                        index = (int) j;
388                        break;
389                    }
390                }
391                if(index>=0)
392                {
393                    load_material_texture( model, material.maps[index], stateset.get(), unit, options );
394                    unit++;
395                }
396            }
397        }
398
399        if (isTransparent)
400        {
401            stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
402            stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
403        }
404
405        materialToStateSetMap[material.name] = stateset.get();
406    }
407}
408
409osg::Geometry* ReaderWriterOBJ::convertElementListToGeometry(obj::Model& model, obj::Model::ElementList& elementList, ObjOptionsStruct& localOptions) const
410{
411
412    unsigned int numVertexIndices = 0;
413    unsigned int numNormalIndices = 0;
414    unsigned int numTexCoordIndices = 0;
415
416    unsigned int numPointElements = 0;
417    unsigned int numPolylineElements = 0;
418    unsigned int numPolygonElements = 0;
419
420    obj::Model::ElementList::iterator itr;
421
422    if (localOptions.generateFacetNormals == true) {
423        for(itr=elementList.begin();
424                itr!=elementList.end();
425                    ++itr)
426        {
427            obj::Element& element = *(*itr);
428            if (element.dataType==obj::Element::POINTS || element.dataType==obj::Element::POLYLINE)
429                continue;
430
431            if (element.normalIndices.size() == 0) {
432                // fill in the normals
433                int a = element.vertexIndices[0];
434                int b = element.vertexIndices[1];
435                int c = element.vertexIndices[2];
436
437                osg::Vec3f ab(model.vertices[b]);
438                osg::Vec3f ac(model.vertices[c]);
439
440                ab -= model.vertices[a];
441                ac -= model.vertices[a];
442
443                osg::Vec3f Norm( ab ^ ac );
444                Norm.normalize();
445                int normal_idx = model.normals.size();
446                model.normals.push_back(Norm);
447
448                for (unsigned i=0 ; i < element.vertexIndices.size() ; i++)
449                    element.normalIndices.push_back(normal_idx);
450            }
451        }
452    }
453
454
455
456    for(itr=elementList.begin();
457        itr!=elementList.end();
458        ++itr)
459    {
460        obj::Element& element = *(*itr);
461
462        numVertexIndices += element.vertexIndices.size();
463        numNormalIndices += element.normalIndices.size();
464        numTexCoordIndices += element.texCoordIndices.size();
465
466        numPointElements += (element.dataType==obj::Element::POINTS) ? 1 : 0;
467        numPolylineElements += (element.dataType==obj::Element::POLYLINE) ? 1 : 0;
468        numPolygonElements += (element.dataType==obj::Element::POLYGON) ? 1 : 0;
469
470    }
471
472    if (numVertexIndices==0) return 0;
473
474    if (numNormalIndices!=0 && numNormalIndices!=numVertexIndices)
475    {
476        OSG_NOTICE<<"Incorrect number of normals, ignore them"<<std::endl;
477        numNormalIndices = 0;
478    }
479
480    if (numTexCoordIndices!=0 && numTexCoordIndices!=numVertexIndices)
481    {
482        OSG_NOTICE<<"Incorrect number of normals, ignore them"<<std::endl;
483        numTexCoordIndices = 0;
484    }
485
486    osg::Vec3Array* vertices = numVertexIndices ? new osg::Vec3Array : 0;
487    osg::Vec3Array* normals = numNormalIndices ? new osg::Vec3Array : 0;
488    osg::Vec2Array* texcoords = numTexCoordIndices ? new osg::Vec2Array : 0;
489
490    if (vertices) vertices->reserve(numVertexIndices);
491    if (normals) normals->reserve(numNormalIndices);
492    if (texcoords) texcoords->reserve(numTexCoordIndices);
493
494    osg::Geometry* geometry = new osg::Geometry;
495    if (vertices) geometry->setVertexArray(vertices);
496    if (normals)
497    {
498        geometry->setNormalArray(normals);
499        geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
500    }
501    if (texcoords)
502    {
503        geometry->setTexCoordArray(0,texcoords);
504    }
505
506
507    if (numPointElements>0)
508    {
509        unsigned int startPos = vertices->size();
510        unsigned int numPoints = 0;
511        for(itr=elementList.begin();
512            itr!=elementList.end();
513            ++itr)
514        {
515            obj::Element& element = *(*itr);
516            if (element.dataType==obj::Element::POINTS)
517            {
518                for(obj::Element::IndexList::iterator index_itr = element.vertexIndices.begin();
519                    index_itr != element.vertexIndices.end();
520                    ++index_itr)
521                {
522                    vertices->push_back(transformVertex(model.vertices[*index_itr],localOptions.rotate));
523                    ++numPoints;
524                }
525                if (numNormalIndices)
526                {
527                    for(obj::Element::IndexList::iterator index_itr = element.normalIndices.begin();
528                        index_itr != element.normalIndices.end();
529                        ++index_itr)
530                    {
531                        normals->push_back(transformNormal(model.normals[*index_itr],localOptions.rotate));
532                    }
533                }
534                if (numTexCoordIndices)
535                {
536                    for(obj::Element::IndexList::iterator index_itr = element.texCoordIndices.begin();
537                        index_itr != element.texCoordIndices.end();
538                        ++index_itr)
539                    {
540                        texcoords->push_back(model.texcoords[*index_itr]);
541                    }
542                }
543            }
544        }
545
546        osg::DrawArrays* drawArrays = new osg::DrawArrays(GL_POINTS,startPos,numPoints);
547        geometry->addPrimitiveSet(drawArrays);
548    }
549
550    if (numPolylineElements>0)
551    {
552        unsigned int startPos = vertices->size();
553        osg::DrawArrayLengths* drawArrayLengths = new osg::DrawArrayLengths(GL_LINES,startPos);
554
555        for(itr=elementList.begin();
556            itr!=elementList.end();
557            ++itr)
558        {
559            obj::Element& element = *(*itr);
560            if (element.dataType==obj::Element::POLYLINE)
561            {
562                drawArrayLengths->push_back(element.vertexIndices.size());
563
564                for(obj::Element::IndexList::iterator index_itr = element.vertexIndices.begin();
565                    index_itr != element.vertexIndices.end();
566                    ++index_itr)
567                {
568                    vertices->push_back(transformVertex(model.vertices[*index_itr],localOptions.rotate));
569                }
570                if (numNormalIndices)
571                {
572                    for(obj::Element::IndexList::iterator index_itr = element.normalIndices.begin();
573                        index_itr != element.normalIndices.end();
574                        ++index_itr)
575                    {
576                        normals->push_back(transformNormal(model.normals[*index_itr],localOptions.rotate));
577                    }
578                }
579                if (numTexCoordIndices)
580                {
581                    for(obj::Element::IndexList::iterator index_itr = element.texCoordIndices.begin();
582                        index_itr != element.texCoordIndices.end();
583                        ++index_itr)
584                    {
585                        texcoords->push_back(model.texcoords[*index_itr]);
586                    }
587                }
588            }
589        }
590
591        geometry->addPrimitiveSet(drawArrayLengths);
592
593    }
594
595    // #define USE_DRAWARRAYLENGTHS
596
597    if (numPolygonElements>0)
598    {
599        unsigned int startPos = vertices->size();
600
601        #ifdef USE_DRAWARRAYLENGTHS
602            osg::DrawArrayLengths* drawArrayLengths = new osg::DrawArrayLengths(GL_POLYGON,startPos);
603            geometry->addPrimitiveSet(drawArrayLengths);
604        #endif
605
606        for(itr=elementList.begin();
607            itr!=elementList.end();
608            ++itr)
609        {
610            obj::Element& element = *(*itr);
611            if (element.dataType==obj::Element::POLYGON)
612            {
613
614
615
616
617
618
619                #ifdef USE_DRAWARRAYLENGTHS
620                    drawArrayLengths->push_back(element.vertexIndices.size());
621                #else
622                    if (element.vertexIndices.size()>4)
623                    {
624                        osg::DrawArrays* drawArrays = new osg::DrawArrays(GL_POLYGON,startPos,element.vertexIndices.size());
625                        startPos += element.vertexIndices.size();
626                        geometry->addPrimitiveSet(drawArrays);
627                    }
628                    else
629                    {
630                        osg::DrawArrays* drawArrays = new osg::DrawArrays(GL_TRIANGLE_FAN,startPos,element.vertexIndices.size());
631                        startPos += element.vertexIndices.size();
632                        geometry->addPrimitiveSet(drawArrays);
633                    }
634                #endif
635
636
637                if (model.needReverse(element))
638                {
639                    // need to reverse so add to OSG arrays in same order as in OBJ, as OSG assume anticlockwise ordering.
640                    for(obj::Element::IndexList::reverse_iterator index_itr = element.vertexIndices.rbegin();
641                        index_itr != element.vertexIndices.rend();
642                        ++index_itr)
643                    {
644                        vertices->push_back(transformVertex(model.vertices[*index_itr],localOptions.rotate));
645                    }
646                    if (numNormalIndices)
647                    {
648                        for(obj::Element::IndexList::reverse_iterator index_itr = element.normalIndices.rbegin();
649                            index_itr != element.normalIndices.rend();
650                            ++index_itr)
651                        {
652                            normals->push_back(transformNormal(model.normals[*index_itr],localOptions.rotate));
653                        }
654                    }
655
656
657                    if (numTexCoordIndices)
658                    {
659                        for(obj::Element::IndexList::reverse_iterator index_itr = element.texCoordIndices.rbegin();
660                            index_itr != element.texCoordIndices.rend();
661                            ++index_itr)
662                        {
663                            texcoords->push_back(model.texcoords[*index_itr]);
664                        }
665                    }
666                }
667                else
668                {
669                    // no need to reverse so add to OSG arrays in same order as in OBJ.
670                    for(obj::Element::IndexList::iterator index_itr = element.vertexIndices.begin();
671                        index_itr != element.vertexIndices.end();
672                        ++index_itr)
673                    {
674                        vertices->push_back(transformVertex(model.vertices[*index_itr],localOptions.rotate));
675                    }
676                    if (numNormalIndices)
677                    {
678                        for(obj::Element::IndexList::iterator index_itr = element.normalIndices.begin();
679                            index_itr != element.normalIndices.end();
680                            ++index_itr)
681                        {
682                            normals->push_back(transformNormal(model.normals[*index_itr],localOptions.rotate));
683                        }
684                    }
685                    if (numTexCoordIndices)
686                    {
687                        for(obj::Element::IndexList::iterator index_itr = element.texCoordIndices.begin();
688                            index_itr != element.texCoordIndices.end();
689                            ++index_itr)
690                        {
691                            texcoords->push_back(model.texcoords[*index_itr]);
692                        }
693                    }
694                }
695            }
696        }
697
698
699    }
700
701    return geometry;
702}
703
704osg::Node* ReaderWriterOBJ::convertModelToSceneGraph(obj::Model& model, ObjOptionsStruct& localOptions, const Options* options) const
705{
706
707    if (model.elementStateMap.empty()) return 0;
708
709    osg::Group* group = new osg::Group;
710
711    // set up the materials
712    MaterialToStateSetMap materialToStateSetMap;
713    buildMaterialToStateSetMap(model, materialToStateSetMap, localOptions, options);
714
715    // go through the groups of related elements and build geometry from them.
716    for(obj::Model::ElementStateMap::iterator itr=model.elementStateMap.begin();
717        itr!=model.elementStateMap.end();
718        ++itr)
719    {
720
721        const obj::ElementState& es = itr->first;
722        obj::Model::ElementList& el = itr->second;
723
724        osg::Geometry* geometry = convertElementListToGeometry(model,el,localOptions);
725
726        if (geometry)
727        {
728            MaterialToStateSetMap::const_iterator it = materialToStateSetMap.find(es.materialName);
729            if (it == materialToStateSetMap.end())
730            {
731                OSG_WARN << "Obj unable to find material '" << es.materialName << "'" << std::endl;
732            }
733
734            osg::StateSet* stateset = materialToStateSetMap[es.materialName].get();
735            geometry->setStateSet(stateset);
736
737            // tesseleate any large polygons
738            if (!localOptions.noTesselateLargePolygons)
739            {
740                osgUtil::Tessellator tessellator;
741                tessellator.retessellatePolygons(*geometry);
742            }
743
744            // tri strip polygons to improve graphics peformance
745            if (!localOptions.noTriStripPolygons)
746            {
747                osgUtil::TriStripVisitor tsv;
748                tsv.stripify(*geometry);
749            }
750
751            // if no normals present add them.
752            if (localOptions.generateFacetNormals==false && (!geometry->getNormalArray() || geometry->getNormalArray()->getNumElements()==0))
753            {
754                osgUtil::SmoothingVisitor sv;
755                sv.smooth(*geometry);
756            }
757
758
759            osg::Geode* geode = new osg::Geode;
760            geode->addDrawable(geometry);
761
762            if (es.objectName.empty())
763            {
764                geode->setName(es.groupName);
765            }
766            else if (es.groupName.empty())
767            {
768                geode->setName(es.objectName);
769            }
770            else
771            {
772                geode->setName(es.groupName + std::string(":") + es.objectName);
773            }
774
775            group->addChild(geode);
776
777        }
778    }
779
780    return group;
781}
782
783ReaderWriterOBJ::ObjOptionsStruct ReaderWriterOBJ::parseOptions(const osgDB::ReaderWriter::Options* options) const
784{
785    ObjOptionsStruct localOptions;
786    localOptions.rotate = true;
787    localOptions.noTesselateLargePolygons = false;
788    localOptions.noTriStripPolygons = false;
789    localOptions.generateFacetNormals = false;
790    localOptions.fixBlackMaterials = true;
791
792    if (options!=NULL)
793    {
794        std::istringstream iss(options->getOptionString());
795        std::string opt;
796        while (iss >> opt)
797        {
798            // split opt into pre= and post=
799            std::string pre_equals;
800            std::string post_equals;
801
802            size_t found = opt.find("=");
803            if(found!=std::string::npos)
804            {
805                pre_equals = opt.substr(0,found);
806                post_equals = opt.substr(found+1);
807            }
808            else
809            {
810                pre_equals = opt;
811            }
812
813            if (pre_equals == "noRotation")
814            {
815                localOptions.rotate = false;
816            }
817            else if (pre_equals == "noTesselateLargePolygons")
818            {
819                localOptions.noTesselateLargePolygons = true;
820            }
821            else if (pre_equals == "noTriStripPolygons")
822            {
823                localOptions.noTriStripPolygons = true;
824            }
825            else if (pre_equals == "generateFacetNormals")
826            {
827                localOptions.generateFacetNormals = true;
828            }
829            else if (post_equals.length()>0)
830            {
831                obj::Material::Map::TextureMapType type = obj::Material::Map::UNKNOWN;
832                // Now we check to see if we have anything forcing a texture allocation
833                if        (pre_equals == "DIFFUSE")            type = obj::Material::Map::DIFFUSE;
834                else if (pre_equals == "AMBIENT")            type = obj::Material::Map::AMBIENT;
835                else if (pre_equals == "SPECULAR")            type = obj::Material::Map::SPECULAR;
836                else if (pre_equals == "SPECULAR_EXPONENT") type = obj::Material::Map::SPECULAR_EXPONENT;
837                else if (pre_equals == "OPACITY")            type = obj::Material::Map::OPACITY;
838                else if (pre_equals == "BUMP")                type = obj::Material::Map::BUMP;
839                else if (pre_equals == "DISPLACEMENT")        type = obj::Material::Map::DISPLACEMENT;
840                else if (pre_equals == "REFLECTION")        type = obj::Material::Map::REFLECTION;
841
842                if (type!=obj::Material::Map::UNKNOWN)
843                {
844                    int unit = atoi(post_equals.c_str());    // (probably should use istringstream rather than atoi)
845                    localOptions.textureUnitAllocation.push_back(std::make_pair(unit,(obj::Material::Map::TextureMapType) type));
846                    OSG_NOTICE<<"Obj Found map in options, ["<<pre_equals<<"]="<<unit<<std::endl;
847                }
848            }
849        }
850    }
851    return localOptions;
852}
853
854
855// read file and convert to OSG.
856osgDB::ReaderWriter::ReadResult ReaderWriterOBJ::readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const
857{
858    std::string ext = osgDB::getLowerCaseFileExtension(file);
859    if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
860
861    std::string fileName = osgDB::findDataFile( file, options );
862    if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
863
864
865    osgDB::ifstream fin(fileName.c_str());
866    if (fin)
867    {
868
869        // code for setting up the database path so that internally referenced file are searched for on relative paths.
870        osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
871        local_opt->setDatabasePath(osgDB::getFilePath(fileName));
872
873        obj::Model model;
874        model.setDatabasePath(osgDB::getFilePath(fileName.c_str()));
875        model.readOBJ(fin, local_opt.get());
876
877        ObjOptionsStruct localOptions = parseOptions(options);
878
879        osg::Node* node = convertModelToSceneGraph(model, localOptions, options);
880        return node;
881    }
882
883    return ReadResult::FILE_NOT_HANDLED;
884}
885
886osgDB::ReaderWriter::ReadResult ReaderWriterOBJ::readNode(std::istream& fin, const Options* options) const
887{
888    if (fin)
889    {
890        fin.imbue(std::locale::classic());
891
892        obj::Model model;
893        model.readOBJ(fin, options);
894
895        ObjOptionsStruct localOptions = parseOptions(options);
896
897        osg::Node* node = convertModelToSceneGraph(model, localOptions, options);
898        return node;
899    }
900
901    return ReadResult::FILE_NOT_HANDLED;
902}
Note: See TracBrowser for help on using the browser.