root/OpenSceneGraph/trunk/src/osgPlugins/3ds/ReaderWriter3DS.cpp @ 10805

Revision 10805, 33.9 kB (checked in by robert, 5 years ago)

From Colin McDonald?, "Reading from a stream the 3ds plugin crashes if the options are null."

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
RevLine 
[8]1#include <osg/Notify>
2#include <osg/Group>
3#include <osg/Geode>
[799]4#include <osg/Geometry>
[1078]5#include <osg/Texture2D>
[8]6#include <osg/Material>
7#include <osg/TexEnv>
8#include <osg/ref_ptr>
[868]9#include <osg/MatrixTransform>
[8]10
11#include <osgDB/Registry>
12#include <osgDB/FileUtils>
13#include <osgDB/FileNameUtils>
14#include <osgDB/ReadFile>
15
[2535]16#include <osgUtil/TriStripVisitor>
17
[445]18//MIKEC debug only for PrintVisitor
19#include <osg/NodeVisitor>
20
21
[1563]22#include "file.h"
23#include "mesh.h"
24#include "material.h"
25#include "vector.h"
26#include "matrix.h"
27#include "node.h"
28#include "quat.h"
[5453]29#include "readwrite.h"
[8]30
31#include <stdlib.h>
32#include <string.h>
33
34#include <set>
35#include <map>
[2772]36#include <iostream>
[10076]37#include <sstream>
[8]38
[445]39using namespace std;
40using namespace osg;
41
42class PrintVisitor : public NodeVisitor
43{
44
45   public:
46   
[2772]47        PrintVisitor(std::ostream& out):
48            NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
49            _out(out)
[445]50        {
51            _indent = 0;
52            _step = 4;
53        }
54       
55        inline void moveIn() { _indent += _step; }
56        inline void moveOut() { _indent -= _step; }
57        inline void writeIndent()
58        {
[2772]59            for(int i=0;i<_indent;++i) _out << " ";
[445]60        }
61               
62        virtual void apply(Node& node)
63        {
64            moveIn();
[2772]65            writeIndent(); _out << node.className() <<std::endl;
[445]66            traverse(node);
67            moveOut();
68        }
69
70        virtual void apply(Geode& node)         { apply((Node&)node); }
71        virtual void apply(Billboard& node)     { apply((Geode&)node); }
[2772]72        virtual void apply(LightSource& node)   { apply((Group&)node); }
73        virtual void apply(ClipNode& node)      { apply((Group&)node); }
[445]74       
75        virtual void apply(Group& node)         { apply((Node&)node); }
76        virtual void apply(Transform& node)     { apply((Group&)node); }
[2772]77        virtual void apply(Projection& node)    { apply((Group&)node); }
[445]78        virtual void apply(Switch& node)        { apply((Group&)node); }
79        virtual void apply(LOD& node)           { apply((Group&)node); }
80
81   protected:
[9637]82
83        PrintVisitor& operator = (const PrintVisitor&) { return *this; }
84
[2772]85        std::ostream& _out;
[445]86        int _indent;
87        int _step;
88};
89
[8]90class ReaderWriter3DS : public osgDB::ReaderWriter
91{
92    public:
93
94        ReaderWriter3DS();
95
[3539]96        virtual const char* className() const { return "3DS Auto Studio Reader"; }
[8]97
[3694]98        virtual ReadResult readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const;
[10076]99        virtual ReadResult readNode(std::istream& fin, const Options* options) const;
[8]100
101    protected:
[10076]102        ReadResult constructFrom3dsFile(Lib3dsFile *f,const std::string& filename, const Options* options) const;
[8]103
104
[3691]105        class ReaderObject
106        {
107        public:
108            ReaderObject();
109       
110            typedef std::map<std::string,osg::StateSet*> StateSetMap;
111            typedef std::vector<int> FaceList;
112            typedef std::map<std::string,osg::StateSet*> GeoStateMap;
[445]113
[3691]114            osg::Texture2D* createTexture(Lib3dsTextureMap *texture,const char* label,bool& transparancy, const osgDB::ReaderWriter::Options* options);
115            osg::StateSet* createStateSet(Lib3dsMaterial *materials, const osgDB::ReaderWriter::Options* options);
116            osg::Drawable* createDrawable(Lib3dsMesh *meshes,FaceList& faceList, Lib3dsMatrix* matrix);
[445]117
[3691]118            std::string _directory;
119            bool _useSmoothingGroups;
120            bool _usePerVertexNormals;
121
122            // MIKEC
123            osg::Node* processMesh(StateSetMap& drawStateMap,osg::Group* parent,Lib3dsMesh* mesh, Lib3dsMatrix* matrix);
124            osg::Node* processNode(StateSetMap drawStateMap,Lib3dsFile *f,Lib3dsNode *node);
125        };
[8]126};
127
128// now register with Registry to instantiate the above
129// reader/writer.
[7076]130REGISTER_OSGPLUGIN(3ds, ReaderWriter3DS)
[8]131
132ReaderWriter3DS::ReaderWriter3DS()
133{
[8578]134    supportsExtension("3ds","3D Studio model format");
135
[5453]136    setByteOrder();
137
138#if 0
139    osg::notify(osg::NOTICE)<<"3DS reader sizes:"<<std::endl;
140    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsBool)="<<sizeof(Lib3dsBool)<<std::endl;
141    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsByte)="<<sizeof(Lib3dsByte)<<std::endl;
142    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsWord)="<<sizeof(Lib3dsWord)<<std::endl;
143    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsDword)="<<sizeof(Lib3dsDword)<<std::endl;
144    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsIntb)="<<sizeof(Lib3dsIntb)<<std::endl;
145    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsIntw)="<<sizeof(Lib3dsIntw)<<std::endl;
146    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsIntd)="<<sizeof(Lib3dsIntd)<<std::endl;
147    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsFloat)="<<sizeof(Lib3dsFloat)<<std::endl;
148    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsDouble)="<<sizeof(Lib3dsDouble)<<std::endl;
149    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsVector)="<<sizeof(Lib3dsVector)<<std::endl;
150    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsTexel)="<<sizeof(Lib3dsTexel)<<std::endl;
151    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsQuat)="<<sizeof(Lib3dsQuat)<<std::endl;
152    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsMatrix)="<<sizeof(Lib3dsMatrix)<<std::endl;
153    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsRgb)="<<sizeof(Lib3dsRgb)<<std::endl;
154    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsRgba)="<<sizeof(Lib3dsRgba)<<std::endl;
155#endif
156
[3691]157}
158
159ReaderWriter3DS::ReaderObject::ReaderObject()
160{
[8]161    _useSmoothingGroups = true;
162    _usePerVertexNormals = true;
163}
164
165
[445]166/**
167    These print methods for 3ds hacking
168*/
169void pad(int level) {
[2772]170    for(int i=0;i<level;i++) std::cout<<"  ";
[445]171}
172void print(Lib3dsMesh *mesh,int level);
173void print(Lib3dsUserData *user,int level);
174void print(Lib3dsNodeData *user,int level);
175void print(Lib3dsObjectData *object,int level);
176void print(Lib3dsNode *node, int level);
[8]177
[445]178void print(Lib3dsMatrix matrix,int level) {
179    pad(level); cout << matrix[0][0] <<" "<< matrix[0][1] <<" "<< matrix[0][2] <<" "<< matrix[0][3] << endl;
180    pad(level); cout << matrix[1][0] <<" "<< matrix[1][1] <<" "<< matrix[1][2] <<" "<< matrix[1][3] << endl;
181    pad(level); cout << matrix[2][0] <<" "<< matrix[2][1] <<" "<< matrix[2][2] <<" "<< matrix[2][3] << endl;
182    pad(level); cout << matrix[3][0] <<" "<< matrix[3][1] <<" "<< matrix[3][2] <<" "<< matrix[3][3] << endl;
183}
184void print(Lib3dsMesh *mesh,int level) {
185    if (mesh) {
186        pad(level); cout << "mesh name " << mesh->name  << endl;
187        print(mesh->matrix,level);
188    } else {
189        pad(level); cout << "no mesh " << endl;
190    }
191}
192void print(Lib3dsUserData *user,int level) {
193    if (user) {
194        pad(level); cout << "user data" << endl;
195        //print(user->mesh,level+1);
196    } else {
197        pad(level); cout << "no user data" << endl;
198    }
199}
200void print(Lib3dsNodeData *node,int level) {
201    if (node) {
202        pad(level); cout << "node data:" << endl;
203        // nodedata->object is a union of many types
204        print((Lib3dsObjectData *)&node->object,level+1);
205    } else {
206        pad(level); cout << "no user data" << endl;
207    }
208}
209void print(Lib3dsObjectData *object,int level) {
210    if (object) {
211        pad(level); cout << "objectdata instance [" << object->instance << "]" << endl;
212        pad(level); cout << "pivot     " << object->pivot[0] <<" "<< object->pivot[1] <<" "<< object->pivot[2] << endl;
213        pad(level); cout << "pos       " << object->pos[0] <<" "<< object->pos[1] <<" "<< object->pos[2] << endl;
214        pad(level); cout << "scl       " << object->scl[0] <<" "<< object->scl[1] <<" "<< object->scl[2] << endl;
215        pad(level); cout << "rot       " << object->rot[0] <<" "<< object->rot[1] <<" "<< object->rot[2] <<" "<< object->rot[3] << endl;
216    } else {
217        pad(level); cout << "no object data" << endl;
218    }
219}
[8]220
[445]221void print(Lib3dsNode *node, int level) {
222   
223    pad(level); cout << "node name [" << node->name << "]" << endl;
224    pad(level); cout << "node id    " << node->node_id << endl;
225    pad(level); cout << "node parent id " << node->parent_id << endl;
226    pad(level); cout << "node matrix:" << endl;
227    print(node->matrix,level+1);
228    print(&node->data,level);
229    print(&node->user,level);
230   
[8]231
[445]232    for(Lib3dsNode *child=node->childs; child; child=child->next) {
233        print(child,level+1);
234    }
[8]235
[445]236}
[8]237
[6518]238void copyLib3dsMatrixToOsgMatrix(osg::Matrix& osg_matrix, const Lib3dsMatrix lib3ds_matrix)
239{
240    osg_matrix.set(
241        lib3ds_matrix[0][0],lib3ds_matrix[0][1],lib3ds_matrix[0][2],lib3ds_matrix[0][3],
242        lib3ds_matrix[1][0],lib3ds_matrix[1][1],lib3ds_matrix[1][2],lib3ds_matrix[1][3],
243        lib3ds_matrix[2][0],lib3ds_matrix[2][1],lib3ds_matrix[2][2],lib3ds_matrix[2][3],
244        lib3ds_matrix[3][0],lib3ds_matrix[3][1],lib3ds_matrix[3][2],lib3ds_matrix[3][3]);
245}
246
[445]247// Transforms points by matrix if 'matrix' is not NULL
[799]248// Creates a Geode and Geometry (as parent,child) and adds the Geode to 'parent' parameter iff 'parent' is non-NULL
[528]249// Returns ptr to the Geode
[3691]250osg::Node* ReaderWriter3DS::ReaderObject::processMesh(StateSetMap& drawStateMap,osg::Group* parent,Lib3dsMesh* mesh, Lib3dsMatrix* matrix) {
[445]251    typedef std::vector<int> FaceList;
252    typedef std::map<std::string,FaceList> MaterialFaceMap;
253    MaterialFaceMap materialFaceMap;
254    for (unsigned int i=0; i<mesh->faces; ++i)
[8]255    {
[445]256        materialFaceMap[mesh->faceL[i].material].push_back(i);
[8]257    }
258
[445]259    if (materialFaceMap.empty())
[8]260    {
[445]261        osg::notify(osg::NOTICE)<<"Warning : no triangles assigned to mesh '"<<mesh->name<<"'"<< std::endl;
[528]262        return NULL;
[445]263    }
264    else
265    {
[8]266
[445]267        osg::Geode* geode = new osg::Geode;
268        geode->setName(mesh->name);
[8]269
[445]270        for(MaterialFaceMap::iterator itr=materialFaceMap.begin();
271            itr!=materialFaceMap.end();
272            ++itr)
[8]273        {
[445]274            FaceList& faceList = itr->second;
275           
276            if (_useSmoothingGroups)
277            {
[8]278
[445]279                typedef std::map<int,FaceList> SmoothingFaceMap;
280                SmoothingFaceMap smoothingFaceMap;
281                for (FaceList::iterator flitr=faceList.begin();
282                     flitr!=faceList.end();
283                     ++flitr)
284                {
285                    smoothingFaceMap[mesh->faceL[*flitr].smoothing].push_back(*flitr);
286                }
[8]287
[445]288                for(SmoothingFaceMap::iterator sitr=smoothingFaceMap.begin();
289                    sitr!=smoothingFaceMap.end();
290                    ++sitr)
[8]291                {
[799]292                    // each smoothing group to have its own geom
[445]293                    // to ensure the vertices on adjacent groups
294                    // don't get shared.
295                    FaceList& smoothFaceMap = sitr->second;
[8]296
[799]297                    osg::Drawable* drawable = createDrawable(mesh,smoothFaceMap,matrix);
298                    if (drawable)
[8]299                    {
[799]300                        drawable->setStateSet(drawStateMap[itr->first]);
301                        geode->addDrawable(drawable);
[8]302                    }
303                }
304            }
[445]305            else // ignore smoothing groups.
306            {
[799]307                osg::Drawable* drawable = createDrawable(mesh,faceList,matrix);
308                if (drawable)
[445]309                {
[799]310                    drawable->setStateSet(drawStateMap[itr->first]);
311                    geode->addDrawable(drawable);
[445]312                }
313            }
314        }
[8]315
[528]316        if (parent) parent->addChild(geode);
317        return geode;
[8]318    }
319}
320
321
[445]322/**
323How to cope with pivot points in 3ds (short version)
[8]324
[528]325  All object coordinates in 3ds are stored in world space, this is why you can just rip out the meshes and use/draw them without meddeling further
326  Unfortunately, this gets a bit wonky with objects with pivot points (conjecture: PP support is retro fitted into the .3ds format and so doesn't fit perfectly?)
[8]327
[445]328  Objects with pivot points have a position relative to their PP, so they have to undergo this transform:
[8]329
[445]330    invert the mesh matrix, apply this matrix to the object. This puts the object back at the origin
[7648]331    Transform the object by the nodes (negative) pivot point coords, this puts the PP at the origin
332    Transform the node by the node matrix, which does the orientation about the pivot point, (and currently) transforms the object back by a translation to the PP.
[8]333
[445]334  */
[3691]335osg::Node* ReaderWriter3DS::ReaderObject::processNode(StateSetMap drawStateMap,Lib3dsFile *f,Lib3dsNode *node) {
[445]336   
[528]337    osg::Group* group=NULL;// created on demand if we find we have children to group together
338   
[8]339
[445]340    // Handle all children of this node for hierarchical assemblies
341    Lib3dsNode *p;
342    for (p=node->childs; p!=0; p=p->next) {
[528]343        if (!group) {
344            group =new osg::Group;
[4801]345            if (strcmp(node->name, "$$$DUMMY") == 0) {
346                group->setName(node->data.object.instance);
347            } else {
348                group->setName(node->name);
349            }
[528]350        }
351        group->addChild(processNode(drawStateMap,f,p));
[445]352    }
353   
354    // MIKEC: possible BUG - 3ds files APPEAR to enforce unqiue names, so this is OK, but I am not 100% sure
355    // failed to find any alternative way to do it in lib3ds though, and lib3ds player.c application uses this method
356    Lib3dsMesh *mesh=lib3ds_file_mesh_by_name(f,node->name);
357    if (mesh) {
358        Lib3dsObjectData* object=&node->data.object;
359        Lib3dsMatrix mesh_inverse;
360        osg::Matrix osgmatrix;
361       
362        lib3ds_matrix_copy(mesh_inverse,mesh->matrix);
363        lib3ds_matrix_inv(mesh_inverse);
364       
365        Lib3dsMatrix M,N;
366        lib3ds_matrix_identity(M);
367        lib3ds_matrix_identity(N);
368        lib3ds_matrix_copy(M,node->matrix);
369        N[3][0]=-object->pivot[0];
370        N[3][1]=-object->pivot[1];
371        N[3][2]=-object->pivot[2];
[8]372
[528]373        bool pivoted=false;
374        if ( (object->pivot[0]!=0.0) || (object->pivot[1]!=0.0) || (object->pivot[2]!=0.0) ) {
375            pivoted=true; // there is a pivot point, so we must use it
376        }
[445]377
[528]378        /*cout<<"M"<<node->name<<endl;
379        print(M,0);
380        cout<<"N"<<endl;
381        print(N,0);*/
[445]382
[528]383        if (pivoted) {
384            // Transform object's pivot point to the world origin
[868]385            osg::MatrixTransform* T=new osg::MatrixTransform;
[6518]386            copyLib3dsMatrixToOsgMatrix(osgmatrix, N);
[528]387            T->setMatrix(osgmatrix);
388            T->setName("3DSPIVOTPOINT: Translate pivotpoint to (world) origin");
389            //cout<<"Translation for "<<node->name<<" is "<<osgmatrix<<endl;
[445]390
[528]391            // rotate about "origin" (after the transform this is the world origin)
392            // BUG this matrix also contains the translation to the pivot point - we should plit that out (maybe)
[868]393            osg::MatrixTransform* R=new osg::MatrixTransform;
[6518]394            copyLib3dsMatrixToOsgMatrix(osgmatrix, M);
[528]395            R->setMatrix(osgmatrix);
396            R->setName("3DSPIVOTPOINT: Rotate");
397               
398            /*
399            cout<<"Rotation for "<<node->name<<" is "<<osgmatrix<<endl;
400            osg::Quat quat;
401            quat.set(osgmatrix);
402            osg::Vec3 axis;
403            float angle;
404            quat.getRotate(angle,axis);
405            cout<<"which is "<<osg::RadiansToDegrees(angle)<<" degrees around "<<axis<<endl;
406            */
407            /*
408            printf("%s---------------\n",node->name);
409            printf("mesh matrix :\n");         print(mesh->matrix,1);
410            printf("mesh inverse:\n");         print(mesh_inverse,1);
411            printf("node matrix :\n");         print(matrix,1);
412            printf("pivot=%f,%f,%f pos=%f,%f,%f\n",object->pivot[0],object->pivot[1],object->pivot[2],object->pos[0],object->pos[1],object->pos[2]);
413            */
414
415            if (group) {
416                // Always in reverse order...
417                group->addChild(R);
418                R->addChild(T);
419                processMesh(drawStateMap,T,mesh,&mesh_inverse); // creates geometry under modifier node
420                return group;
421            } else {
422                // We are a pivoted node with no children
423                R->addChild(T);
424                processMesh(drawStateMap,T,mesh,&mesh_inverse); // creates geometry under modifier node
425                return R;
426            }
427        } else {
428            if(group) {
429                // add our geometry to group (where our children already are)
430                processMesh(drawStateMap,group,mesh,NULL); // creates geometry under modifier node
431                return group;
432            } else {
433                // didnt use group for children
434                // return a ptr directly to the Geode for this mesh
435                return processMesh(drawStateMap,NULL,mesh,NULL);
436            }   
437        }
438
[445]439    } else {
440        // no mesh for this node - probably a camera or something of that persuasion
441        //cout << "no mesh for object " << node->name << endl;
[528]442        return group; // we have no mesh, but we might have children
[8]443    }
444}
445
[10076]446osgDB::ReaderWriter::ReadResult ReaderWriter3DS::readNode(std::istream& fin,  const osgDB::ReaderWriter::Options* options) const
447{
448    osgDB::ReaderWriter::ReadResult result = ReadResult::FILE_NOT_HANDLED;
[8]449
[10076]450    std::string optFileName = "";
451    if (options)
452    {
453        optFileName = options->getPluginStringData("STREAM_FILENAME");
454    }
455
456    Lib3dsFile *f = lib3ds_stream_load((iostream *) &fin);
457    if (f)
458    {
459        result = constructFrom3dsFile(f,optFileName,options);
460        lib3ds_file_free(f);
461    }
462
463    return(result);
464}
465
[3694]466osgDB::ReaderWriter::ReadResult ReaderWriter3DS::readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const
[8]467{
[10076]468    osgDB::ReaderWriter::ReadResult result = ReadResult::FILE_NOT_HANDLED;
[5453]469
[2501]470    std::string ext = osgDB::getLowerCaseFileExtension(file);
471    if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
472
[3691]473    std::string fileName = osgDB::findDataFile( file, options );
[2501]474    if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
475
[10076]476    Lib3dsFile *f = lib3ds_file_load(fileName.c_str(),options);
477
478    if (f)
479    {
480        osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
481        local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileName));
482
483        result = constructFrom3dsFile(f,file,local_opt.get());
484        lib3ds_file_free(f);
485    }
486
487    return result;
488}
489
490osgDB::ReaderWriter::ReadResult ReaderWriter3DS::constructFrom3dsFile(Lib3dsFile *f,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
491{
[445]492    if (f==NULL) return ReadResult::FILE_NOT_HANDLED;
[8]493
[445]494    // MIKEC
495    // This appears to build the matrix structures for the 3ds structure
496    // It wasn't previously necessary because all the meshes are stored in world coordinates
497    // but is VERY necessary if you want to use pivot points...
498    lib3ds_file_eval(f,0.0f); // second param is time 't' for animated files
[8]499
[3691]500    ReaderObject reader;
[8]501
[10805]502    reader._directory = ( options && !options->getDatabasePathList().empty() ) ? options->getDatabasePathList().front() : osgDB::getFilePath(fileName);
[3691]503
[445]504    osg::Group* group = new osg::Group;
505    group->setName(fileName);
[8]506
[3691]507    ReaderObject::StateSetMap drawStateMap;
[8]508
[445]509    for (Lib3dsMaterial *mat=f->materials; mat; mat=mat->next)
[8]510    {
[3691]511        drawStateMap[mat->name] = reader.createStateSet(mat, options);
[445]512    }
513   
[5453]514    if (osg::getNotifyLevel()>=osg::INFO)
515    {
[445]516        int level=0;
[5453]517        std::cout << "NODE TRAVERSAL of 3ds file "<<f->name<<std::endl;
[445]518        for(Lib3dsNode *node=f->nodes; node; node=node->next) {
519            print(node,level+1);
[8]520        }
[5453]521        std::cout << "MESH TRAVERSAL of 3ds file "<<f->name<<std::endl;
[445]522        for(Lib3dsMesh *mesh=f->meshes; mesh; mesh=mesh->next) {
523            print(mesh,level+1);
[8]524        }
[5453]525    }
[8]526
[445]527    // We can traverse by meshes (old method, broken for pivot points, but otherwise works), or by nodes (new method, not so well tested yet)
528    // if your model is broken, especially wrt object positions try setting this flag. If that fixes it,
529    // send me the model
530    bool traverse_nodes=false;
531   
532    // MIKEC: have found 3ds files with NO node structure - only meshes, for this case we fall back to the old traverse-by-meshes code
533    // Loading and re-exporting these files from 3DS produces a file with correct node structure, so perhaps these are not 100% conformant?
534    if (f->nodes == NULL) {
535        osg::notify(osg::WARN)<<"Warning: in 3ds loader: file has no nodes, traversing by meshes instead"<< std::endl;
536        traverse_nodes=true;
[8]537    }
538
[445]539    if (traverse_nodes) { // old method
540        for (Lib3dsMesh *mesh=f->meshes; mesh; mesh=mesh->next) {
[3691]541            reader.processMesh(drawStateMap,group,mesh,NULL);
[445]542        }
543    } else { // new method
544        for(Lib3dsNode *node=f->nodes; node; node=node->next) {
[3691]545            group->addChild(reader.processNode(drawStateMap,f,node));
[445]546        }
547    }
548
[6518]549    if (osg::getNotifyLevel()>=osg::INFO)
[5453]550    {
551        osg::notify(osg::NOTICE) << "Final OSG node structure looks like this:"<< endl;
552        PrintVisitor pv(osg::notify(osg::NOTICE));
553        group->accept(pv);
554    }   
[528]555   
[445]556    return group;
[8]557}
558
[445]559/**
560use matrix to pretransform geometry, or NULL to do nothing
561*/
[3691]562osg::Drawable*   ReaderWriter3DS::ReaderObject::createDrawable(Lib3dsMesh *m,FaceList& faceList,Lib3dsMatrix *matrix)
[8]563{
564
[799]565    osg::Geometry* geom = new osg::Geometry;
[8]566
567    unsigned int i;
[270]568   
[8]569    std::vector<int> orig2NewMapping;
570    for(i=0;i<m->points;++i) orig2NewMapping.push_back(-1);
571
572    unsigned int noVertex=0;
573    FaceList::iterator fitr;
574    for (fitr=faceList.begin();
575        fitr!=faceList.end();
576        ++fitr)
577    {
578
579        Lib3dsFace& face = m->faceL[*fitr];
580
581        if (orig2NewMapping[face.points[0]]<0)
582            orig2NewMapping[face.points[0]] = noVertex++;
583
584        if (orig2NewMapping[face.points[1]]<0)
585            orig2NewMapping[face.points[1]] = noVertex++;
586
587        if (orig2NewMapping[face.points[2]]<0)
588            orig2NewMapping[face.points[2]] = noVertex++;
589
590    }
591
[814]592    // create vertices.
[2339]593   
[799]594    osg::Vec3Array* osg_coords = new osg::Vec3Array(noVertex);
[814]595    geom->setVertexArray(osg_coords);
596
[270]597    Lib3dsVector c;
[445]598       
[8]599    for (i=0; i<m->points; ++i)
600    {
[814]601        if (orig2NewMapping[i]>=0)
602        {
[445]603            if (matrix)
[270]604            {
[445]605                lib3ds_vector_transform(c,*matrix, m->pointL[i].pos);
[799]606                (*osg_coords)[orig2NewMapping[i]].set(c[0],c[1],c[2]);
[270]607            }
608            else
609            {
610                // original no transform code.
[799]611                (*osg_coords)[orig2NewMapping[i]].set(m->pointL[i].pos[0],m->pointL[i].pos[1],m->pointL[i].pos[2]);
[270]612            }
613        }
[8]614    }
615
[814]616    // create texture coords if needed.
[8]617    if (m->texels>0)
618    {
619        if (m->texels==m->points)
620        {
[814]621            osg::Vec2Array* osg_tcoords = new osg::Vec2Array(noVertex);
622            geom->setTexCoordArray(0,osg_tcoords);
[8]623            for (i=0; i<m->texels; ++i)
624            {
[799]625                if (orig2NewMapping[i]>=0) (*osg_tcoords)[orig2NewMapping[i]].set(m->texelL[i][0],m->texelL[i][1]);
[8]626            }
627        }
628        else
629        {
[211]630            osg::notify(osg::WARN)<<"Warning: in 3ds loader m->texels ("<<m->texels<<") != m->points ("<<m->points<<")"<< std::endl;
[8]631        }
632    }
633
[814]634    // create normals.
[8]635    if (_usePerVertexNormals)
636    {
[4461]637        osg::Vec3Array* osg_normals = new osg::Vec3Array(noVertex);
[8]638       
639        // initialize normal list to zero's.
640        for (i=0; i<noVertex; ++i)
641        {
[799]642            (*osg_normals)[i].set(0.0f,0.0f,0.0f);
[8]643        }
644
[814]645        for (fitr=faceList.begin();
646            fitr!=faceList.end();
647            ++fitr)
[8]648        {
[814]649            Lib3dsFace& face = m->faceL[*fitr];
650
[799]651            (*osg_normals)[orig2NewMapping[face.points[0]]] += osg::Vec3(face.normal[0],face.normal[1],face.normal[2]);;
652            (*osg_normals)[orig2NewMapping[face.points[1]]] += osg::Vec3(face.normal[0],face.normal[1],face.normal[2]);;
653            (*osg_normals)[orig2NewMapping[face.points[2]]] += osg::Vec3(face.normal[0],face.normal[1],face.normal[2]);;
[814]654
[8]655        }
656
[6518]657        if (matrix)
658        {
659            osg::Matrix osg_matrix;
660            copyLib3dsMatrixToOsgMatrix(osg_matrix, *matrix);
661            for (i=0; i<noVertex; ++i)
662            {
663                (*osg_normals)[i] = osg::Matrix::transform3x3((*osg_normals)[i], osg_matrix);
664            }
665        }
666
[8]667        // normalize the normal list to unit length normals.
668        for (i=0; i<noVertex; ++i)
669        {
[799]670            (*osg_normals)[i].normalize();
[8]671        }
672
[799]673        geom->setNormalArray(osg_normals);
674        geom->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
[8]675
676    }
[814]677    else 
[8]678    {
[814]679        osg::Vec3Array* osg_normals = new osg::Vec3Array(faceList.size());
680        osg::Vec3Array::iterator normal_itr = osg_normals->begin();
681        for (fitr=faceList.begin();
682            fitr!=faceList.end();
683            ++fitr)
684        {
685            Lib3dsFace& face = m->faceL[*fitr];
686            *(normal_itr++) =  osg::Vec3(face.normal[0],face.normal[1],face.normal[2]);
687        }
[799]688        geom->setNormalArray(osg_normals);
689        geom->setNormalBinding(osg::Geometry::BIND_PER_PRIMITIVE);
[8]690    }
[814]691   
[4390]692    osg::Vec4ubArray* osg_colors = new osg::Vec4ubArray(1);
[2339]693    (*osg_colors)[0].set(255,255,255,255);
694    geom->setColorArray(osg_colors);
695    geom->setColorBinding(osg::Geometry::BIND_OVERALL);
[814]696   
[2339]697
[814]698    // create primitives
699    int numIndices = faceList.size()*3;
[1166]700    DrawElementsUShort* elements = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES,numIndices);
701    DrawElementsUShort::iterator index_itr = elements->begin();
[8]702
[814]703    for (fitr=faceList.begin();
704        fitr!=faceList.end();
705        ++fitr)
706    {
707        Lib3dsFace& face = m->faceL[*fitr];
[8]708
[814]709        *(index_itr++) = orig2NewMapping[face.points[0]];
710        *(index_itr++) = orig2NewMapping[face.points[1]];
711        *(index_itr++) = orig2NewMapping[face.points[2]];
712    }
713   
[1186]714    geom->addPrimitiveSet(elements);
[8]715
[5453]716#if 0
[2535]717    osgUtil::TriStripVisitor tsv;
718    tsv.stripify(*geom);
[5453]719#endif
[2535]720
[799]721    return geom;
[8]722}
[445]723
724
[3691]725osg::Texture2D*  ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap *texture,const char* label,bool& transparancy, const osgDB::ReaderWriter::Options* options)
[445]726{
727    if (texture && *(texture->name))
728    {
[10076]729        osg::notify(osg::NOTICE)<<"texture->name="<<texture->name<<", _directory="<<_directory<<std::endl;
730
731
[2449]732        std::string fileName = osgDB::findFileInDirectory(texture->name,_directory,osgDB::CASE_INSENSITIVE);
733        if (fileName.empty())
734        {
735            // file not found in .3ds file's directory, so we'll look in the datafile path list.
[3691]736            fileName = osgDB::findDataFile(texture->name,options, osgDB::CASE_INSENSITIVE);
[2449]737        }
[10076]738
[445]739        if (fileName.empty())
740        {
[10076]741            if (osgDB::containsServerAddress(_directory))
742            {
743                // if 3DS file is loaded from http, just attempt to load texture from same location.
744                fileName = _directory + "/" + texture->name;
745            } else {
746                osg::notify(osg::WARN) << "texture '"<<texture->name<<"' not found"<< std::endl;
747                return NULL;
748            }
[445]749        }
750
751        if (label) osg::notify(osg::DEBUG_INFO) << label;
752        else osg::notify(osg::DEBUG_INFO) << "texture name";
753        osg::notify(osg::DEBUG_INFO) << " '"<<texture->name<<"'"<< std::endl;
754        osg::notify(osg::DEBUG_INFO) << "    texture flag        "<<texture->flags<< std::endl;
755        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_DECALE       "<<((texture->flags)&LIB3DS_DECALE)<< std::endl;
756        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_MIRROR       "<<((texture->flags)&LIB3DS_MIRROR)<< std::endl;
757        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_NEGATE       "<<((texture->flags)&LIB3DS_NEGATE)<< std::endl;
758        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_NO_TILE      "<<((texture->flags)&LIB3DS_NO_TILE)<< std::endl;
759        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_SUMMED_AREA  "<<((texture->flags)&LIB3DS_SUMMED_AREA)<< std::endl;
760        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_ALPHA_SOURCE "<<((texture->flags)&LIB3DS_ALPHA_SOURCE)<< std::endl;
761        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TINT         "<<((texture->flags)&LIB3DS_TINT)<< std::endl;
762        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_IGNORE_ALPHA "<<((texture->flags)&LIB3DS_IGNORE_ALPHA)<< std::endl;
763        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_RGB_TINT     "<<((texture->flags)&LIB3DS_RGB_TINT)<< std::endl;
764
[9527]765        osg::ref_ptr<osg::Image> osg_image = osgDB::readRefImageFile(fileName.c_str());
766        if (!osg_image)
[445]767        {
768            osg::notify(osg::NOTICE) << "Warning: Cannot create texture "<<texture->name<< std::endl;
769            return NULL;
770        }
771
[1078]772        osg::Texture2D* osg_texture = new osg::Texture2D;
[9527]773        osg_texture->setImage(osg_image.get());
[445]774
775        // does the texture support transparancy?
776        transparancy = ((texture->flags)&LIB3DS_ALPHA_SOURCE)!=0;
777
778        // what is the wrap mode of the texture.
[1078]779        osg::Texture2D::WrapMode wm = ((texture->flags)&LIB3DS_NO_TILE) ?
[4461]780                osg::Texture2D::CLAMP :
781                osg::Texture2D::REPEAT;
[1078]782        osg_texture->setWrap(osg::Texture2D::WRAP_S,wm);
783        osg_texture->setWrap(osg::Texture2D::WRAP_T,wm);
784        osg_texture->setWrap(osg::Texture2D::WRAP_R,wm);
[445]785                                 // bilinear.
[1078]786        osg_texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR_MIPMAP_NEAREST);
[445]787
788        return osg_texture;
789    }
790    else
791        return NULL;
792}
793
794
[3691]795osg::StateSet* ReaderWriter3DS::ReaderObject::createStateSet(Lib3dsMaterial *mat, const osgDB::ReaderWriter::Options* options)
[445]796{
797    if (mat==NULL) return NULL;
798
799    osg::StateSet* stateset = new osg::StateSet;
800
801    osg::Material* material = new osg::Material;
802
803    float transparency = mat->transparency;
804    float alpha = 1.0f-transparency;
805
806    osg::Vec4 ambient(mat->ambient[0],mat->ambient[1],mat->ambient[2],alpha);
807    osg::Vec4 diffuse(mat->diffuse[0],mat->diffuse[1],mat->diffuse[2],alpha);
808    osg::Vec4 specular(mat->specular[0],mat->specular[1],mat->specular[2],alpha);
[2421]809    specular *= mat->shin_strength;
[445]810
811    float shininess = mat->shininess;
[7623]812    material->setName(mat->name);
[445]813    material->setAmbient(osg::Material::FRONT_AND_BACK,ambient);
814    material->setDiffuse(osg::Material::FRONT_AND_BACK,diffuse);
815    material->setSpecular(osg::Material::FRONT_AND_BACK,specular);
[1020]816    material->setShininess(osg::Material::FRONT_AND_BACK,shininess*128.0f);
[445]817
818    stateset->setAttribute(material);
819
820    bool textureTransparancy=false;
[3691]821    osg::Texture2D* texture1_map = createTexture(&(mat->texture1_map),"texture1_map",textureTransparancy, options);
[445]822    if (texture1_map)
823    {
[845]824        stateset->setTextureAttributeAndModes(0,texture1_map,osg::StateAttribute::ON);
[445]825       
[2339]826        if (!textureTransparancy)
827        {       
828            // from an email from Eric Hamil, September 30, 2003.
829            // According to the 3DS spec, and other
830            // software (like Max, Lightwave, and Deep Exploration) a 3DS material that has
831            // a non-white diffuse base color and a 100% opaque bitmap texture, will show the
832            // texture with no influence from the base color.
833           
834            // so we'll override material back to white.
835            // and no longer require the decal hack below...
[2872]836#if 0
[7648]837            // Eric original fallback
[2339]838            osg::Vec4 white(1.0f,1.0f,1.0f,alpha);
839            material->setAmbient(osg::Material::FRONT_AND_BACK,white);
840            material->setDiffuse(osg::Material::FRONT_AND_BACK,white);
841            material->setSpecular(osg::Material::FRONT_AND_BACK,white);
[2872]842#else
[7648]843            // try alternative to avoid saturating with white
[2872]844            // setting white as per OpenGL defaults.
845            material->setAmbient(osg::Material::FRONT_AND_BACK,osg::Vec4(0.2f,0.2f,0.2f,alpha));
846            material->setDiffuse(osg::Material::FRONT_AND_BACK,osg::Vec4(0.8f,0.8f,0.8f,alpha));
847            material->setSpecular(osg::Material::FRONT_AND_BACK,osg::Vec4(0.0f,0.0f,0.0f,alpha));
848#endif           
[445]849        }
[2339]850       
851// no longer required...       
852//         bool decal = false;
853//         
854//         // not sure exactly how to interpret what is best for .3ds
855//         // but the default text env MODULATE doesn't work well, and
856//         // DECAL seems to work better.
857//         osg::TexEnv* texenv = new osg::TexEnv;
858//         if (decal)
859//         {
860//             texenv->setMode(osg::TexEnv::DECAL);
861//         }
862//         else
863//         {
864//             texenv->setMode(osg::TexEnv::MODULATE);
865//         }
866//        stateset->setTextureAttribute(0,texenv);
[445]867    }
868
[10076]869    if (transparency>0.0f || textureTransparancy)
[445]870    {
871        stateset->setMode(GL_BLEND,osg::StateAttribute::ON);
872        stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
873    }
874
875/*
876    osg::ref_ptr<osg::Texture> texture1_mask = createTexture(&(mat->texture1_mask),"texture1_mask",textureTransparancy);
877    osg::ref_ptr<osg::Texture> texture2_map = createTexture(&(mat->texture2_map),"texture2_map",textureTransparancy);
878    osg::ref_ptr<osg::Texture> texture2_mask = createTexture(&(mat->texture2_mask),"texture2_mask",textureTransparancy);
879    osg::ref_ptr<osg::Texture> opacity_map = createTexture(&(mat->opacity_map),"opacity_map",textureTransparancy);
880    osg::ref_ptr<osg::Texture> opacity_mask = createTexture(&(mat->opacity_mask),"opacity_mask",textureTransparancy);
881    osg::ref_ptr<osg::Texture> bump_map = createTexture(&(mat->bump_map),"bump_map",textureTransparancy);
882    osg::ref_ptr<osg::Texture> bump_mask = createTexture(&(mat->bump_mask),"bump_mask",textureTransparancy);
883    osg::ref_ptr<osg::Texture> specular_map = createTexture(&(mat->specular_map),"specular_map",textureTransparancy);
884    osg::ref_ptr<osg::Texture> specular_mask = createTexture(&(mat->specular_mask),"specular_mask",textureTransparancy);
885    osg::ref_ptr<osg::Texture> shininess_map = createTexture(&(mat->shininess_map),"shininess_map",textureTransparancy);
886    osg::ref_ptr<osg::Texture> shininess_mask = createTexture(&(mat->shininess_mask),"shininess_mask",textureTransparancy);
887    osg::ref_ptr<osg::Texture> self_illum_map = createTexture(&(mat->self_illum_map),"self_illum_map",textureTransparancy);
888    osg::ref_ptr<osg::Texture> self_illum_mask = createTexture(&(mat->self_illum_mask),"self_illum_mask",textureTransparancy);
889    osg::ref_ptr<osg::Texture> reflection_map = createTexture(&(mat->reflection_map),"reflection_map",textureTransparancy);
890    osg::ref_ptr<osg::Texture> reflection_mask = createTexture(&(mat->reflection_mask),"reflection_mask",textureTransparancy);
891*/
892    return stateset;
893}
Note: See TracBrowser for help on using the browser.