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

Revision 10942, 45.5 kB (checked in by robert, 3 years ago)

Added missing .get()

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#define ENABLE_3DS_WRITER      1            // Enables the 3DS writer (the define should be removed when the writer will be stable and tested enough)
2
3#include <osg/Notify>
4#include <osg/Group>
5#include <osg/Geode>
6#include <osg/Geometry>
7#include <osg/Texture2D>
8#include <osg/Material>
9#include <osg/TexEnv>
10#include <osg/ref_ptr>
11#include <osg/MatrixTransform>
12
13#include <osgDB/Registry>
14#include <osgDB/FileUtils>
15#include <osgDB/FileNameUtils>
16#include <osgDB/ReadFile>
17
18#include <osgUtil/TriStripVisitor>
19
20//MIKEC debug only for PrintVisitor
21#include <osg/NodeVisitor>
22
23#ifdef ENABLE_3DS_WRITER
24    #include "WriterNodeVisitor.h"
25#endif
26#include "lib3ds/lib3ds.h"
27#include <stdlib.h>
28#include <string.h>
29
30#include <set>
31#include <map>
32#include <iostream>
33#include <sstream>
34#include <assert.h>
35
36using namespace std;
37using namespace osg;
38
39/// Implementation borrowed from boost and slightly modified
40template<class T> class scoped_array // noncopyable
41{
42private:
43    T * px;
44    scoped_array(scoped_array const &);
45    scoped_array & operator=(scoped_array const &);
46    typedef scoped_array<T> this_type;
47
48    void operator==( scoped_array const& ) const;
49    void operator!=( scoped_array const& ) const;
50
51public:
52    typedef T element_type;
53    explicit scoped_array( T * p = 0 ) : px( p ) {}
54
55    ~scoped_array() { delete[] px; }
56
57    void reset(T * p = 0) {
58        assert( p == 0 || p != px ); // catch self-reset errors
59        this_type(p).swap(*this);
60    }
61
62    T & operator[](std::ptrdiff_t i) const // never throws
63    {
64        assert( px != 0 );
65        assert( i >= 0 );
66        return px[i];
67    }
68
69    T * get() const // never throws
70    {
71        return px;
72    }
73
74    void swap(scoped_array & b) // never throws
75    {
76        T * tmp = b.px;
77        b.px = px;
78        px = tmp;
79    }
80};
81
82
83
84void copyLib3dsMatrixToOsgMatrix(osg::Matrix& osg_matrix, const Lib3dsMatrix lib3ds_matrix)
85{
86    osg_matrix.set(
87        lib3ds_matrix[0][0],lib3ds_matrix[0][1],lib3ds_matrix[0][2],lib3ds_matrix[0][3],
88        lib3ds_matrix[1][0],lib3ds_matrix[1][1],lib3ds_matrix[1][2],lib3ds_matrix[1][3],
89        lib3ds_matrix[2][0],lib3ds_matrix[2][1],lib3ds_matrix[2][2],lib3ds_matrix[2][3],
90        lib3ds_matrix[3][0],lib3ds_matrix[3][1],lib3ds_matrix[3][2],lib3ds_matrix[3][3]);
91}
92
93osg::Matrix copyLib3dsMatrixToOsgMatrix(const Lib3dsMatrix mat)
94{
95    osg::Matrix osgMatrix;
96    copyLib3dsMatrixToOsgMatrix(osgMatrix, mat);
97    return osgMatrix;
98}
99
100void copyLib3dsVec3ToOsgVec3(osg::Vec3f osgVec, const float vertices[3]) {
101    return osgVec.set(vertices[0], vertices[1], vertices[2]);
102}
103
104osg::Vec3f copyLib3dsVec3ToOsgVec3(const float vertices[3]) {
105    return osg::Vec3f(vertices[0], vertices[1], vertices[2]);
106}
107
108osg::Quat copyLib3dsQuatToOsgQuat(const float quat[4]) {
109    return osg::Quat(quat[0], quat[1], quat[2], quat[3]);
110}
111
112
113
114class PrintVisitor : public NodeVisitor
115{
116public:
117    PrintVisitor(std::ostream& out):
118      NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
119          _out(out)
120      {
121          _indent = 0;
122          _step = 4;
123      }
124
125      inline void moveIn() { _indent += _step; }
126      inline void moveOut() { _indent -= _step; }
127      inline void writeIndent()
128      {
129          for(int i=0;i<_indent;++i) _out << " ";
130      }
131
132      virtual void apply(Node& node)
133      {
134          moveIn();
135          writeIndent(); _out << node.className() <<std::endl;
136          traverse(node);
137          moveOut();
138      }
139
140      virtual void apply(Geode& node)         { apply((Node&)node); }
141      virtual void apply(Billboard& node)     { apply((Geode&)node); }
142      virtual void apply(LightSource& node)   { apply((Group&)node); }
143      virtual void apply(ClipNode& node)      { apply((Group&)node); }
144
145      virtual void apply(Group& node)         { apply((Node&)node); }
146      virtual void apply(Transform& node)     { apply((Group&)node); }
147      virtual void apply(Projection& node)    { apply((Group&)node); }
148      virtual void apply(Switch& node)        { apply((Group&)node); }
149      virtual void apply(LOD& node)           { apply((Group&)node); }
150
151protected:
152
153    PrintVisitor& operator = (const PrintVisitor&) { return *this; }
154
155    std::ostream& _out;
156    int _indent;
157    int _step;
158};
159
160/// Possible options:
161///        - noMatrixTransforms: set the plugin to apply matrices into the mesh vertices ("old behaviour") instead of restoring them ("new behaviour"). You may use this option to avoid a few rounding errors.
162///        - checkForEspilonIdentityMatrices: if noMatrixTransforms is \b not set, then consider "almost identity" matrices to be identity ones (in case of rounding errors).
163///        - restoreMatrixTransformsNoMeshes: makes an exception to the behaviour when 'noMatrixTransforms' is \b not set for mesh instances. When a mesh instance has a transform on it, the reader creates a MatrixTransform above the Geode. If you don't want the hierarchy to be modified, then you can use this option to merge the transform into vertices.
164class ReaderWriter3DS : public osgDB::ReaderWriter
165{
166public:
167
168    ReaderWriter3DS();
169
170    virtual const char* className() const { return "3DS Auto Studio Reader/Writer"; }
171
172    virtual ReadResult readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const;
173    virtual ReadResult readNode(std::istream& fin, const Options* options) const;
174#if ENABLE_3DS_WRITER
175    virtual WriteResult writeNode(const osg::Node& /*node*/,const std::string& /*fileName*/,const Options* =NULL) const;
176    virtual WriteResult writeNode(const osg::Node& /*node*/,std::ostream& /*fout*/,const Options* =NULL) const;
177#endif
178
179protected:
180    ReadResult constructFrom3dsFile(Lib3dsFile *f,const std::string& filename, const Options* options) const;
181
182#if ENABLE_3DS_WRITER
183    bool createFileObject(const osg::Node& node, Lib3dsFile * file3ds,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const;
184#endif
185
186    class ReaderObject
187    {
188    public:
189        ReaderObject(const osgDB::ReaderWriter::Options* options);
190
191        typedef std::vector<osg::StateSet*> StateSetMap;
192        typedef std::vector<int> FaceList;
193        typedef std::map<std::string,osg::StateSet*> GeoStateMap;
194
195        osg::Texture2D* createTexture(Lib3dsTextureMap *texture,const char* label,bool& transparancy);
196        osg::StateSet* createStateSet(Lib3dsMaterial *materials);
197        osg::Drawable* createDrawable(Lib3dsMesh *meshes,FaceList& faceList, const osg::Matrix * matrix);
198
199        std::string _directory;
200        bool _useSmoothingGroups;
201        bool _usePerVertexNormals;
202
203        // MIKEC
204        osg::Node* processMesh(StateSetMap& drawStateMap,osg::Group* parent,Lib3dsMesh* mesh, const osg::Matrix * matrix);
205        osg::Node* processNode(StateSetMap drawStateMap,Lib3dsFile *f,Lib3dsNode *node);
206    private:
207        const osgDB::ReaderWriter::Options* options;
208        bool noMatrixTransforms;            ///< Should the plugin apply matrices into the mesh vertices ("old behaviour"), instead of restoring matrices ("new behaviour")?
209        bool checkForEspilonIdentityMatrices;
210        bool restoreMatrixTransformsNoMeshes;
211        typedef std::map<unsigned int,FaceList> SmoothingFaceMap;
212        void addDrawableFromFace(osg::Geode * geode, FaceList & faceList, Lib3dsMesh * mesh, const osg::Matrix * matrix, osg::StateSet * stateSet);
213    };
214};
215
216// now register with Registry to instantiate the above
217// reader/writer.
218REGISTER_OSGPLUGIN(3ds, ReaderWriter3DS)
219
220ReaderWriter3DS::ReaderWriter3DS()
221{
222    supportsExtension("3ds","3D Studio model format");
223    supportsOption("OutputTextureFiles","Write out the texture images to file");
224    setByteOrder();
225
226#if 0
227    osg::notify(osg::NOTICE)<<"3DS reader sizes:"<<std::endl;
228    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsBool)="<<sizeof(Lib3dsBool)<<std::endl;
229    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsByte)="<<sizeof(Lib3dsByte)<<std::endl;
230    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsWord)="<<sizeof(Lib3dsWord)<<std::endl;
231    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsDword)="<<sizeof(Lib3dsDword)<<std::endl;
232    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsIntb)="<<sizeof(Lib3dsIntb)<<std::endl;
233    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsIntw)="<<sizeof(Lib3dsIntw)<<std::endl;
234    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsIntd)="<<sizeof(Lib3dsIntd)<<std::endl;
235    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsFloat)="<<sizeof(Lib3dsFloat)<<std::endl;
236    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsDouble)="<<sizeof(Lib3dsDouble)<<std::endl;
237    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsVector)="<<sizeof(Lib3dsVector)<<std::endl;
238    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsTexel)="<<sizeof(Lib3dsTexel)<<std::endl;
239    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsQuat)="<<sizeof(Lib3dsQuat)<<std::endl;
240    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsMatrix)="<<sizeof(Lib3dsMatrix)<<std::endl;
241    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsRgb)="<<sizeof(Lib3dsRgb)<<std::endl;
242    osg::notify(osg::NOTICE)<<"  sizeof(Lib3dsRgba)="<<sizeof(Lib3dsRgba)<<std::endl;
243#endif
244
245}
246
247ReaderWriter3DS::ReaderObject::ReaderObject(const osgDB::ReaderWriter::Options* options) :
248    _useSmoothingGroups(true),
249    _usePerVertexNormals(true),
250    options(options),
251    noMatrixTransforms(false),
252    checkForEspilonIdentityMatrices(false),
253    restoreMatrixTransformsNoMeshes(false)
254{
255    std::istringstream iss(options->getOptionString());
256    std::string opt;
257    while (iss >> opt)
258    {
259        if (opt == "noMatrixTransforms")
260            noMatrixTransforms = true;
261        if (opt == "checkForEspilonIdentityMatrices")
262            checkForEspilonIdentityMatrices = true;
263        if (opt == "restoreMatrixTransformsNoMeshes")
264            restoreMatrixTransformsNoMeshes = true;
265    }
266}
267
268
269/**
270    These print methods for 3ds hacking
271*/
272void pad(int level) {
273    for(int i=0;i<level;i++) std::cout<<"  ";
274}
275void print(Lib3dsMesh *mesh,int level);
276void print(Lib3dsUserData *user,int level);
277void print(Lib3dsMeshInstanceNode *object,int level);
278void print(Lib3dsNode *node, int level);
279
280void print(Lib3dsMatrix matrix,int level) {
281    pad(level); cout << matrix[0][0] <<" "<< matrix[0][1] <<" "<< matrix[0][2] <<" "<< matrix[0][3] << endl;
282    pad(level); cout << matrix[1][0] <<" "<< matrix[1][1] <<" "<< matrix[1][2] <<" "<< matrix[1][3] << endl;
283    pad(level); cout << matrix[2][0] <<" "<< matrix[2][1] <<" "<< matrix[2][2] <<" "<< matrix[2][3] << endl;
284    pad(level); cout << matrix[3][0] <<" "<< matrix[3][1] <<" "<< matrix[3][2] <<" "<< matrix[3][3] << endl;
285}
286void print(Lib3dsMesh *mesh,int level) {
287    if (mesh) {
288        pad(level); cout << "mesh name " << mesh->name  << endl;
289        print(mesh->matrix,level);
290    } else {
291        pad(level); cout << "no mesh " << endl;
292    }
293}
294void print(Lib3dsUserData *user,int level) {
295    if (user) {
296        pad(level); cout << "user data" << endl;
297        //print(user->mesh,level+1);
298    } else {
299        pad(level); cout << "no user data" << endl;
300    }
301}
302
303void print(Lib3dsMeshInstanceNode *object,int level) {
304    if (object) {
305        pad(level); cout << "objectdata instance [" << object->instance_name << "]" << endl;
306        pad(level); cout << "pivot     " << object->pivot[0] <<" "<< object->pivot[1] <<" "<< object->pivot[2] << endl;
307        pad(level); cout << "pos       " << object->pos[0] <<" "<< object->pos[1] <<" "<< object->pos[2] << endl;
308        pad(level); cout << "scl       " << object->scl[0] <<" "<< object->scl[1] <<" "<< object->scl[2] << endl;
309        pad(level); cout << "rot       " << object->rot[0] <<" "<< object->rot[1] <<" "<< object->rot[2] <<" "<< object->rot[3] << endl;
310    } else {
311        pad(level); cout << "no object data" << endl;
312    }
313}
314
315void print(Lib3dsNode *node, int level) {
316
317    pad(level); cout << "node name [" << node->name << "]" << endl;
318    pad(level); cout << "node id    " << node->user_id << endl;
319    pad(level); cout << "node parent id " << (node->parent ? static_cast<int>(node->parent->user_id) : -1) << endl;
320    pad(level); cout << "node matrix:" << endl;
321    print(node->matrix,level+1);
322
323    if (node->type == LIB3DS_NODE_MESH_INSTANCE) {
324        pad(level); cout << "mesh instance data:" << endl;
325        print(reinterpret_cast<Lib3dsMeshInstanceNode *>(node),level+1);
326    } else {
327        pad(level); cout << "node is not a mesh instance (not handled)" << endl;
328    }
329
330    print(&node->user_ptr,level);
331
332    for(Lib3dsNode *child=node->childs; child; child=child->next) {
333        print(child,level+1);
334    }
335
336}
337
338void ReaderWriter3DS::ReaderObject::addDrawableFromFace(osg::Geode * geode, FaceList & faceList,
339                                                        Lib3dsMesh * mesh,
340                                                        const osg::Matrix * matrix,
341                                                        osg::StateSet * stateSet)
342{
343    if (_useSmoothingGroups)
344    {
345        SmoothingFaceMap smoothingFaceMap;
346        for (FaceList::iterator flitr=faceList.begin();
347            flitr!=faceList.end();
348            ++flitr)
349        {
350            smoothingFaceMap[mesh->faces[*flitr].smoothing_group].push_back(*flitr);
351        }
352
353        for(SmoothingFaceMap::iterator sitr=smoothingFaceMap.begin();
354            sitr!=smoothingFaceMap.end();
355            ++sitr)
356        {
357            // each smoothing group to have its own geom
358            // to ensure the vertices on adjacent groups
359            // don't get shared.
360            FaceList& smoothFaceMap = sitr->second;
361            osg::ref_ptr<osg::Drawable> drawable = createDrawable(mesh,smoothFaceMap,matrix);
362            if (drawable.valid())
363            {
364                if (stateSet)
365                    drawable->setStateSet(stateSet);
366                geode->addDrawable(drawable.get());
367            }
368        }
369    }
370    else // ignore smoothing groups.
371    {
372        osg::ref_ptr<osg::Drawable> drawable = createDrawable(mesh,faceList,matrix);
373        if (drawable.valid())
374        {
375            if (stateSet)
376                drawable->setStateSet(stateSet);
377            geode->addDrawable(drawable.get());
378        }
379    }
380}
381
382
383// Transforms points by matrix if 'matrix' is not NULL
384// Creates a Geode and Geometry (as parent,child) and adds the Geode to 'parent' parameter iff 'parent' is non-NULL
385// Returns ptr to the Geode
386osg::Node* ReaderWriter3DS::ReaderObject::processMesh(StateSetMap& drawStateMap,osg::Group* parent,Lib3dsMesh* mesh, const osg::Matrix * matrix) {
387    typedef std::vector<FaceList> MaterialFaceMap;
388    MaterialFaceMap materialFaceMap;
389    unsigned int numMaterials = drawStateMap.size();
390    materialFaceMap.insert(materialFaceMap.begin(), numMaterials, FaceList());        // Setup the map
391    FaceList defaultMaterialFaceList;
392    for (unsigned int i=0; i<mesh->nfaces; ++i)
393    {
394        if (mesh->faces[i].material>=0) {
395            materialFaceMap[mesh->faces[i].material].push_back(i);
396        }
397        else
398            defaultMaterialFaceList.push_back(i);
399    }
400    if (materialFaceMap.empty() && defaultMaterialFaceList.empty())
401    {
402        osg::notify(osg::NOTICE)<<"Warning : no triangles assigned to mesh '"<<mesh->name<<"'"<< std::endl;
403        //osg::notify(osg::INFO) << "No material assigned to mesh '" << mesh->name << "'" << std::endl;
404        return NULL;
405    }
406    else
407    {
408        osg::Geode* geode = new osg::Geode;
409        geode->setName(mesh->name);
410        if (!defaultMaterialFaceList.empty())
411        {
412            addDrawableFromFace(geode, defaultMaterialFaceList, mesh, matrix, NULL);
413        }
414        for(unsigned int imat=0; imat<numMaterials; ++imat)
415        {
416            addDrawableFromFace(geode, materialFaceMap[imat], mesh, matrix, drawStateMap[imat]);
417        }
418        if (parent) parent->addChild(geode);
419        return geode;
420    }
421}
422
423
424/// Returns true if a matrix is 'almost' identity, meaning that the difference between each value and the corresponding identity value is less than an epsilon value.
425bool isIdentityEquivalent(const osg::Matrix & mat, osg::Matrix::value_type epsilon=1e-6)
426{
427    return osg::equivalent(mat(0,0), 1, epsilon) && osg::equivalent(mat(0,1), 0, epsilon) && osg::equivalent(mat(0,2), 0, epsilon) &&  osg::equivalent(mat(0,3), 0, epsilon) &&
428           osg::equivalent(mat(1,0), 0, epsilon) && osg::equivalent(mat(1,1), 1, epsilon) && osg::equivalent(mat(1,2), 0, epsilon) &&  osg::equivalent(mat(1,3), 0, epsilon) &&
429           osg::equivalent(mat(2,0), 0, epsilon) && osg::equivalent(mat(2,1), 0, epsilon) && osg::equivalent(mat(2,2), 1, epsilon) &&  osg::equivalent(mat(2,3), 0, epsilon) &&
430           osg::equivalent(mat(3,0), 0, epsilon) && osg::equivalent(mat(3,1), 0, epsilon) && osg::equivalent(mat(3,2), 0, epsilon) &&  osg::equivalent(mat(3,3), 1, epsilon);
431}
432
433
434/**
435How to cope with pivot points in 3ds (short version)
436
437  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
438  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?)
439
440  Objects with pivot points have a position relative to their PP, so they have to undergo this transform:
441
442    invert the mesh matrix, apply this matrix to the object. This puts the object back at the origin
443    Transform the object by the nodes (negative) pivot point coords, this puts the PP at the origin
444    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.
445
446*/
447osg::Node* ReaderWriter3DS::ReaderObject::processNode(StateSetMap drawStateMap,Lib3dsFile *f,Lib3dsNode *node)
448{
449    // Get mesh
450    Lib3dsMeshInstanceNode * object = (node->type == LIB3DS_NODE_MESH_INSTANCE) ? reinterpret_cast<Lib3dsMeshInstanceNode *>(node) : NULL;
451    Lib3dsMesh * mesh = lib3ds_file_mesh_for_node(f,node);
452    assert(!(mesh && !object));        // Node must be a LIB3DS_NODE_MESH_INSTANCE if a mesh exists
453
454    // Retreive LOCAL transform
455    static const osg::Matrix::value_type MATRIX_EPSILON = 1e-10;
456    osg::Matrix osgNodeMatrix( copyLib3dsMatrixToOsgMatrix(node->matrix) );
457
458    if (node->parent)
459    {
460        // Matrices evaluated by lib3DS are multiplied by parents' ones
461        osgNodeMatrix *= osg::Matrix::inverse( copyLib3dsMatrixToOsgMatrix(node->parent->matrix) );
462    }
463
464    // Test if we should create an intermediate Group (or MatrixTransform) and which matrix we should apply to the vertices
465    osg::Group* group = NULL;
466
467    // Get pivot point
468    osg::Vec3 pivot( object ? copyLib3dsVec3ToOsgVec3(object->pivot) : osg::Vec3() );
469    bool pivoted = pivot.x()!=0 || pivot.y()!=0 || pivot.z()!=0;
470
471    osg::Matrix meshMat;
472    if (mesh) {
473         if (!noMatrixTransforms) {
474            // There can be a transform directly on a mesh instance (= as if a osg::MatrixTransform and a osg::Geode were merged together) in object->pos/rot/scl
475            if (!pivoted) {
476                meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix));
477            } else {
478                meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * osg::Matrix::translate(-pivot);
479            }
480        }
481        else {
482            if (pivoted) {
483                meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * osg::Matrix::translate(-pivot) * copyLib3dsMatrixToOsgMatrix(node->matrix);
484            } else {
485                meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * copyLib3dsMatrixToOsgMatrix(node->matrix);
486            }
487            osgNodeMatrix = osg::Matrix::identity();        // Not sure it's useful, but it's harmless ;)
488        }
489    }
490
491    bool isOsgNodeMatrixIdentity = false;
492    if (osgNodeMatrix.isIdentity() || (checkForEspilonIdentityMatrices && isIdentityEquivalent(osgNodeMatrix, MATRIX_EPSILON))) {
493        isOsgNodeMatrixIdentity = true;
494    }
495
496
497    //if (node->childs != NULL || pivoted || (!isOsgNodeMatrixIdentity && !noMatrixTransforms)) {
498    if (node->childs != NULL || (!isOsgNodeMatrixIdentity && !noMatrixTransforms)) {
499        if (isOsgNodeMatrixIdentity || noMatrixTransforms) {
500            group = new osg::Group;
501        } else {
502            group = new osg::MatrixTransform(osgNodeMatrix);
503        }
504    }
505
506    if (group) {
507        if (strcmp(node->name, "$$$DUMMY") == 0)
508        {
509            if (node->type == LIB3DS_NODE_MESH_INSTANCE)
510                group->setName(reinterpret_cast<Lib3dsMeshInstanceNode *>(node)->instance_name);
511        }
512        else
513            group->setName(node->name);
514
515        // Handle all children of this node for hierarchical assemblies
516        for (Lib3dsNode *p=node->childs; p!=NULL; p=p->next) {
517            group->addChild(processNode(drawStateMap,f,p));
518        }
519    } else {
520        assert(node->childs == NULL);        // Else we must have a group to put childs into
521    }
522
523    // Handle mesh
524    if (mesh) {
525        osg::Matrix * meshAppliedMatPtr = NULL;
526        if (!meshMat.isIdentity() && !(checkForEspilonIdentityMatrices && isIdentityEquivalent(meshMat, MATRIX_EPSILON))) {
527            meshAppliedMatPtr = &meshMat;
528        }
529
530        if(group) {
531            // add our geometry to group (where our children already are)
532            // creates geometry under modifier node
533            processMesh(drawStateMap,group,mesh,meshAppliedMatPtr);
534            return group;
535        } else {
536            // didnt use group for children
537            // return a ptr directly to the Geode for this mesh
538            return processMesh(drawStateMap,NULL,mesh,meshAppliedMatPtr);
539        }
540
541    } else {
542        // no mesh for this node - probably a camera or something of that persuasion
543        //cout << "no mesh for object " << node->name << endl;
544        return group; // we have no mesh, but we might have children
545    }
546}
547
548
549static long filei_seek_func(void *self, long offset, Lib3dsIoSeek origin) {
550    std::istream *f = reinterpret_cast<std::istream*>(self);
551    ios_base::seekdir o = ios_base::beg;
552    if (origin == LIB3DS_SEEK_CUR) o = ios_base::cur;
553    else if (origin == LIB3DS_SEEK_END) o = ios_base::end;
554
555    f->seekg(offset, o);
556    return f->fail() ? -1 : 0;
557}
558
559#if ENABLE_3DS_WRITER
560static long fileo_seek_func(void *self, long offset, Lib3dsIoSeek origin) {
561    std::ostream *f = reinterpret_cast<std::ostream*>(self);
562    ios_base::seekdir o = ios_base::beg;
563    if (origin == LIB3DS_SEEK_CUR) o = ios_base::cur;
564    else if (origin == LIB3DS_SEEK_END) o = ios_base::end;
565
566    f->seekp(offset, o);
567    return f->fail() ? -1 : 0;
568}
569#endif
570
571static long filei_tell_func(void *self) {
572    std::istream *f = reinterpret_cast<std::istream*>(self);
573    return f->tellg();
574}
575
576#if ENABLE_3DS_WRITER
577static long fileo_tell_func(void *self) {
578    std::ostream *f = reinterpret_cast<std::ostream*>(self);
579    return f->tellp();
580}
581#endif
582
583
584static size_t filei_read_func(void *self, void *buffer, size_t size) {
585    std::istream *f = reinterpret_cast<std::istream*>(self);
586    f->read(reinterpret_cast<char*>(buffer), size);
587    return f->gcount();
588}
589
590#if ENABLE_3DS_WRITER
591static size_t fileo_write_func(void *self, const void *buffer, size_t size) {
592    std::ostream *f = reinterpret_cast<std::ostream*>(self);
593    f->write(static_cast<const char*>(buffer), size);
594    return f->fail() ? 0 : size;
595}
596#endif
597
598static void fileio_log_func(void *self, Lib3dsLogLevel level, int indent, const char *msg)
599{
600    osg::NotifySeverity l = osg::INFO;
601    if (level == LIB3DS_LOG_ERROR) l = osg::FATAL;
602    else if (level == LIB3DS_LOG_WARN) l = osg::WARN;
603    else if (level == LIB3DS_LOG_INFO) l = osg::INFO;
604    else if (level == LIB3DS_LOG_DEBUG) l = osg::DEBUG_INFO;
605    osg::notify(l) << msg << std::endl;
606}
607
608
609osgDB::ReaderWriter::ReadResult ReaderWriter3DS::readNode(std::istream& fin,  const osgDB::ReaderWriter::Options* options) const
610{
611    osgDB::ReaderWriter::ReadResult result = ReadResult::FILE_NOT_HANDLED;
612
613    std::string optFileName = "";
614    if (options)
615    {
616        optFileName = options->getPluginStringData("STREAM_FILENAME");
617        if (optFileName.empty()) optFileName = options->getPluginStringData("filename");
618    }
619
620    // Prepare io structure to tell how to read the stream
621    Lib3dsIo io;
622    io.self = &fin;
623    io.seek_func = filei_seek_func;
624    io.tell_func = filei_tell_func;
625    io.read_func = filei_read_func;
626    io.write_func = NULL;
627    io.log_func = fileio_log_func;
628
629    Lib3dsFile * file3ds = lib3ds_file_new();
630    if (lib3ds_file_read(file3ds, &io) != 0)
631    {
632        result = constructFrom3dsFile(file3ds,optFileName,options);
633        lib3ds_file_free(file3ds);
634    }
635
636    return(result);
637}
638
639osgDB::ReaderWriter::ReadResult ReaderWriter3DS::readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const
640{
641    osgDB::ReaderWriter::ReadResult result = ReadResult::FILE_NOT_HANDLED;
642
643    std::string ext = osgDB::getLowerCaseFileExtension(file);
644    if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
645
646    std::string fileName = osgDB::findDataFile( file, options );
647    if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
648
649    Lib3dsFile *f = lib3ds_file_open(fileName.c_str() /*,options*/);
650
651    if (f)
652    {
653        osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
654        local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileName));
655
656        result = constructFrom3dsFile(f,file,local_opt.get());
657        lib3ds_file_free(f);
658    }
659
660    return result;
661}
662
663osgDB::ReaderWriter::ReadResult ReaderWriter3DS::constructFrom3dsFile(Lib3dsFile *f,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
664{
665    if (f==NULL) return ReadResult::FILE_NOT_HANDLED;
666
667    // MIKEC
668    // This appears to build the matrix structures for the 3ds structure
669    // It wasn't previously necessary because all the meshes are stored in world coordinates
670    // but is VERY necessary if you want to use pivot points...
671    lib3ds_file_eval(f,0.0f); // second param is time 't' for animated files
672
673    ReaderObject reader(options);
674
675    reader._directory = ( options && !options->getDatabasePathList().empty() ) ? options->getDatabasePathList().front() : osgDB::getFilePath(fileName);
676
677    ReaderObject::StateSetMap drawStateMap;
678    unsigned int numMaterials = f->nmaterials;
679    drawStateMap.insert(drawStateMap.begin(), numMaterials, NULL);        // Setup the map
680    for (unsigned int imat=0; imat<numMaterials; ++imat)
681    {
682        Lib3dsMaterial * mat = f->materials[imat];
683        drawStateMap[imat] = reader.createStateSet(mat);
684    }
685
686    if (osg::getNotifyLevel()>=osg::INFO)
687    {
688        int level=0;
689        std::cout << "NODE TRAVERSAL of 3ds file "<<f->name<<std::endl;
690        for(Lib3dsNode *node=f->nodes; node; node=node->next) {
691            print(node,level+1);
692        }
693        std::cout << "MESH TRAVERSAL of 3ds file "<<f->name<<std::endl;
694        for (int imesh=0; imesh<f->nmeshes; ++imesh) {
695            print(f->meshes[imesh],level+1);
696        }
697    }
698
699    // We can traverse by meshes (old method, broken for pivot points, but otherwise works), or by nodes (new method, not so well tested yet)
700    // if your model is broken, especially wrt object positions try setting this flag. If that fixes it,
701    // send me the model
702    bool traverse_nodes=false;
703
704    // MIKEC: have found 3ds files with NO node structure - only meshes, for this case we fall back to the old traverse-by-meshes code
705    // Loading and re-exporting these files from 3DS produces a file with correct node structure, so perhaps these are not 100% conformant?
706    if (f->nodes == NULL) {
707        osg::notify(osg::WARN)<<"Warning: in 3ds loader: file has no nodes, traversing by meshes instead"<< std::endl;
708        traverse_nodes=true;
709    }
710
711    osg::Node* group = NULL;
712
713    if (traverse_nodes) { // old method
714        group = new osg::Group();
715        for (int imesh=0; imesh<f->nmeshes; ++imesh) {
716            reader.processMesh(drawStateMap,group->asGroup(),f->meshes[imesh],NULL);
717        }
718    } else { // new method
719        Lib3dsNode *node=f->nodes;
720        if (!node->next)
721            group = reader.processNode(drawStateMap,f,node);
722        else
723        {
724            group = new osg::Group();
725            for(; node; node=node->next) {
726                group->asGroup()->addChild(reader.processNode(drawStateMap,f,node));
727            }
728        }
729    }
730    if (group && group->getName().empty()) group->setName(fileName);
731
732    if (osg::getNotifyLevel()>=osg::INFO)
733    {
734        osg::notify(osg::NOTICE) << "Final OSG node structure looks like this:"<< endl;
735        PrintVisitor pv(osg::notify(osg::NOTICE));
736        group->accept(pv);
737    }
738
739    return group;
740}
741
742/**
743use matrix to pretransform geometry, or NULL to do nothing
744*/
745osg::Drawable* ReaderWriter3DS::ReaderObject::createDrawable(Lib3dsMesh *m,FaceList& faceList, const osg::Matrix * matrix)
746{
747    osg::Geometry * geom = new osg::Geometry;
748    unsigned int i;
749
750    std::vector<int> orig2NewMapping;
751    orig2NewMapping.reserve(m->nvertices);
752    for(i=0;i<m->nvertices;++i) orig2NewMapping.push_back(-1);
753
754    unsigned int noVertex=0;
755    FaceList::iterator fitr;
756    for (fitr=faceList.begin();
757        fitr!=faceList.end();
758        ++fitr)
759    {
760
761        Lib3dsFace& face = m->faces[*fitr];
762
763        if (orig2NewMapping[face.index[0]]<0)
764            orig2NewMapping[face.index[0]] = noVertex++;
765
766        if (orig2NewMapping[face.index[1]]<0)
767            orig2NewMapping[face.index[1]] = noVertex++;
768
769        if (orig2NewMapping[face.index[2]]<0)
770            orig2NewMapping[face.index[2]] = noVertex++;
771
772    }
773
774    // create vertices.
775
776    osg::ref_ptr<osg::Vec3Array> osg_coords = new osg::Vec3Array(noVertex);
777    geom->setVertexArray(osg_coords.get());
778
779    for (i=0; i<m->nvertices; ++i)
780    {
781        if (orig2NewMapping[i]>=0)
782        {
783            if (matrix)
784            {
785                (*osg_coords)[orig2NewMapping[i]].set( copyLib3dsVec3ToOsgVec3(m->vertices[i]) * (*matrix) );
786            }
787            else
788            {
789                // original no transform code.
790                (*osg_coords)[orig2NewMapping[i]].set( copyLib3dsVec3ToOsgVec3(m->vertices[i]) );
791            }
792        }
793    }
794
795    // create texture coords if needed.
796    if (m->texcos)
797    {
798        osg::ref_ptr<osg::Vec2Array> osg_tcoords = new osg::Vec2Array(noVertex);
799        geom->setTexCoordArray(0, osg_tcoords.get());
800        for (i=0; i<m->nvertices; ++i)
801        {
802            if (orig2NewMapping[i]>=0) (*osg_tcoords)[orig2NewMapping[i]].set(m->texcos[i][0],m->texcos[i][1]);
803        }
804    }
805
806    // create normals
807    // Sukender: 3DS file format doesn't store normals (that is to say they're recomputed each time).
808    // When using per vertex normals, we could use either vertex computation, or face computation (and copy the normal to each vertex). Here we use the latter one.
809    if (_usePerVertexNormals)
810    {
811        //Lib3dsVector * normals = new Lib3dsVector[m->nfaces*3];
812        //lib3ds_mesh_calculate_vertex_normals(m, normals);
813        scoped_array<Lib3dsVector> normals( new Lib3dsVector[m->nfaces] );        // Temporary array
814        lib3ds_mesh_calculate_face_normals(m, normals.get());
815        osg::ref_ptr<osg::Vec3Array> osg_normals = new osg::Vec3Array(noVertex);
816
817        // initialize normal list to zero's.
818        for (i=0; i<noVertex; ++i)
819        {
820            (*osg_normals)[i].set(0.0f,0.0f,0.0f);
821        }
822
823        for (fitr=faceList.begin();
824            fitr!=faceList.end();
825            ++fitr)
826        {
827            Lib3dsFace& face = m->faces[*fitr];
828            osg::Vec3f osgNormal( copyLib3dsVec3ToOsgVec3(normals[*fitr]) );
829            if (matrix) osgNormal = osg::Matrix::transform3x3(osgNormal, *matrix);
830            osgNormal.normalize();
831            (*osg_normals)[orig2NewMapping[face.index[0]]] = osgNormal;
832            (*osg_normals)[orig2NewMapping[face.index[1]]] = osgNormal;
833            (*osg_normals)[orig2NewMapping[face.index[2]]] = osgNormal;
834        }
835
836        geom->setNormalArray(osg_normals.get());
837        geom->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
838    }
839    else
840    {
841        scoped_array<Lib3dsVector> normals ( new Lib3dsVector[m->nfaces] );
842        lib3ds_mesh_calculate_face_normals(m, normals.get());
843        osg::ref_ptr<osg::Vec3Array> osg_normals = new osg::Vec3Array(faceList.size());
844        osg::Vec3Array::iterator normal_itr = osg_normals->begin();
845        for (fitr=faceList.begin();
846            fitr!=faceList.end();
847            ++fitr)
848        {
849            osg::Vec3f osgNormal( copyLib3dsVec3ToOsgVec3(normals[*fitr]) );
850            if (matrix) osgNormal = osg::Matrix::transform3x3(osgNormal, *matrix);
851            osgNormal.normalize();
852            *(normal_itr++) = osgNormal;
853        }
854        geom->setNormalArray(osg_normals.get());
855        geom->setNormalBinding(osg::Geometry::BIND_PER_PRIMITIVE);
856    }
857
858    osg::ref_ptr<osg::Vec4ubArray> osg_colors = new osg::Vec4ubArray(1);
859    (*osg_colors)[0].set(255,255,255,255);
860    geom->setColorArray(osg_colors.get());
861    geom->setColorBinding(osg::Geometry::BIND_OVERALL);
862
863    // create primitives
864    int numIndices = faceList.size()*3;
865    osg::ref_ptr<DrawElementsUShort> elements = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES,numIndices);
866    DrawElementsUShort::iterator index_itr = elements->begin();
867
868    for (fitr=faceList.begin();
869        fitr!=faceList.end();
870        ++fitr)
871    {
872        Lib3dsFace& face = m->faces[*fitr];
873        *(index_itr++) = orig2NewMapping[face.index[0]];
874        *(index_itr++) = orig2NewMapping[face.index[1]];
875        *(index_itr++) = orig2NewMapping[face.index[2]];
876    }
877
878    geom->addPrimitiveSet(elements.get());
879
880#if 0
881    osgUtil::TriStripVisitor tsv;
882    tsv.stripify(*geom);
883#endif
884
885    return geom;
886}
887
888
889osg::Texture2D*  ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap *texture,const char* label,bool& transparancy)
890{
891    if (texture && *(texture->name))
892    {
893        osg::notify(osg::NOTICE)<<"texture->name="<<texture->name<<", _directory="<<_directory<<std::endl;
894
895        std::string fileName = osgDB::findFileInDirectory(texture->name,_directory,osgDB::CASE_INSENSITIVE);
896        if (fileName.empty())
897        {
898            // file not found in .3ds file's directory, so we'll look in the datafile path list.
899            fileName = osgDB::findDataFile(texture->name,options, osgDB::CASE_INSENSITIVE);
900            osg::notify(osg::NOTICE)<<"texture->name="<<texture->name<<", _directory="<<_directory<<std::endl;
901        }
902
903        if (fileName.empty())
904        {
905            if (osgDB::containsServerAddress(_directory))
906            {
907                // if 3DS file is loaded from http, just attempt to load texture from same location.
908                fileName = _directory + "/" + texture->name;
909            } else {
910                osg::notify(osg::WARN) << "texture '"<<texture->name<<"' not found"<< std::endl;
911                return NULL;
912            }
913        }
914
915        if (label) osg::notify(osg::DEBUG_INFO) << label;
916        else osg::notify(osg::DEBUG_INFO) << "texture name";
917        osg::notify(osg::DEBUG_INFO) << " '"<<texture->name<<"'"<< std::endl;
918        osg::notify(osg::DEBUG_INFO) << "    texture flag        "<<texture->flags<< std::endl;
919        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_DECALE       "<<((texture->flags)&LIB3DS_TEXTURE_DECALE)<< std::endl;
920        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_MIRROR       "<<((texture->flags)&LIB3DS_TEXTURE_MIRROR)<< std::endl;
921        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_NEGATE       "<<((texture->flags)&LIB3DS_TEXTURE_NEGATE)<< std::endl;
922        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_NO_TILE      "<<((texture->flags)&LIB3DS_TEXTURE_NO_TILE)<< std::endl;
923        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_SUMMED_AREA  "<<((texture->flags)&LIB3DS_TEXTURE_SUMMED_AREA)<< std::endl;
924        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_ALPHA_SOURCE "<<((texture->flags)&LIB3DS_TEXTURE_ALPHA_SOURCE)<< std::endl;
925        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_TINT         "<<((texture->flags)&LIB3DS_TEXTURE_TINT)<< std::endl;
926        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_IGNORE_ALPHA "<<((texture->flags)&LIB3DS_TEXTURE_IGNORE_ALPHA)<< std::endl;
927        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_RGB_TINT     "<<((texture->flags)&LIB3DS_TEXTURE_RGB_TINT)<< std::endl;
928
929        osg::ref_ptr<osg::Image> osg_image = osgDB::readRefImageFile(fileName.c_str(), options); //Absolute Path
930        if (!osg_image.valid())
931        {
932            osg::notify(osg::NOTICE) << "Warning: Cannot create texture "<<texture->name<< std::endl;
933            return NULL;
934        }
935        if (osg_image->getFileName().empty()) // it should be done in OSG with osgDB::readRefImageFile(fileName.c_str());
936            osg_image->setFileName(fileName);
937        osg::Texture2D* osg_texture = new osg::Texture2D;
938        osg_texture->setImage(osg_image.get());
939        osg_texture->setName(texture->name);
940        // does the texture support transparancy?
941        transparancy = ((texture->flags)&LIB3DS_TEXTURE_ALPHA_SOURCE)!=0;
942
943        // what is the wrap mode of the texture.
944        osg::Texture2D::WrapMode wm = ((texture->flags)&LIB3DS_TEXTURE_NO_TILE) ?
945                osg::Texture2D::CLAMP :
946                osg::Texture2D::REPEAT;
947        osg_texture->setWrap(osg::Texture2D::WRAP_S,wm);
948        osg_texture->setWrap(osg::Texture2D::WRAP_T,wm);
949        osg_texture->setWrap(osg::Texture2D::WRAP_R,wm);
950                                 // bilinear.
951        osg_texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR_MIPMAP_NEAREST);
952
953        return osg_texture;
954    }
955    else
956        return NULL;
957}
958
959
960osg::StateSet* ReaderWriter3DS::ReaderObject::createStateSet(Lib3dsMaterial *mat)
961{
962    if (mat==NULL) return NULL;
963
964    osg::StateSet* stateset = new osg::StateSet;
965
966    osg::Material* material = new osg::Material;
967
968    float transparency = mat->transparency;
969    float alpha = 1.0f-transparency;
970
971    osg::Vec4 ambient(mat->ambient[0],mat->ambient[1],mat->ambient[2],alpha);
972    osg::Vec4 diffuse(mat->diffuse[0],mat->diffuse[1],mat->diffuse[2],alpha);
973    osg::Vec4 specular(mat->specular[0],mat->specular[1],mat->specular[2],alpha);
974    specular *= mat->shin_strength;
975
976    float shininess = mat->shininess;
977    material->setName(mat->name);
978    material->setAmbient(osg::Material::FRONT_AND_BACK,ambient);
979    material->setDiffuse(osg::Material::FRONT_AND_BACK,diffuse);
980    material->setSpecular(osg::Material::FRONT_AND_BACK,specular);
981    material->setShininess(osg::Material::FRONT_AND_BACK,shininess*128.0f);
982
983    stateset->setAttribute(material);
984
985    bool textureTransparancy=false;
986    osg::Texture2D* texture1_map = createTexture(&(mat->texture1_map),"texture1_map",textureTransparancy);
987    if (texture1_map)
988    {
989        stateset->setTextureAttributeAndModes(0,texture1_map,osg::StateAttribute::ON);
990
991        if (!textureTransparancy)
992        {
993            // from an email from Eric Hamil, September 30, 2003.
994            // According to the 3DS spec, and other
995            // software (like Max, Lightwave, and Deep Exploration) a 3DS material that has
996            // a non-white diffuse base color and a 100% opaque bitmap texture, will show the
997            // texture with no influence from the base color.
998
999            // so we'll override material back to white.
1000            // and no longer require the decal hack below...
1001#if 0
1002            // Eric original fallback
1003            osg::Vec4 white(1.0f,1.0f,1.0f,alpha);
1004            material->setAmbient(osg::Material::FRONT_AND_BACK,white);
1005            material->setDiffuse(osg::Material::FRONT_AND_BACK,white);
1006            material->setSpecular(osg::Material::FRONT_AND_BACK,white);
1007#else
1008            // try alternative to avoid saturating with white
1009            // setting white as per OpenGL defaults.
1010            material->setAmbient(osg::Material::FRONT_AND_BACK,osg::Vec4(0.2f,0.2f,0.2f,alpha));
1011            material->setDiffuse(osg::Material::FRONT_AND_BACK,osg::Vec4(0.8f,0.8f,0.8f,alpha));
1012            material->setSpecular(osg::Material::FRONT_AND_BACK,osg::Vec4(0.0f,0.0f,0.0f,alpha));
1013#endif
1014        }
1015
1016// no longer required...
1017//         bool decal = false;
1018//
1019//         // not sure exactly how to interpret what is best for .3ds
1020//         // but the default text env MODULATE doesn't work well, and
1021//         // DECAL seems to work better.
1022//         osg::TexEnv* texenv = new osg::TexEnv;
1023//         if (decal)
1024//         {
1025//             texenv->setMode(osg::TexEnv::DECAL);
1026//         }
1027//         else
1028//         {
1029//             texenv->setMode(osg::TexEnv::MODULATE);
1030//         }
1031//        stateset->setTextureAttribute(0,texenv);
1032    }
1033
1034    if (transparency>0.0f || textureTransparancy)
1035    {
1036        stateset->setMode(GL_BLEND,osg::StateAttribute::ON);
1037        stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1038    }
1039
1040/*
1041    osg::ref_ptr<osg::Texture> texture1_mask = createTexture(&(mat->texture1_mask),"texture1_mask",textureTransparancy);
1042    osg::ref_ptr<osg::Texture> texture2_map = createTexture(&(mat->texture2_map),"texture2_map",textureTransparancy);
1043    osg::ref_ptr<osg::Texture> texture2_mask = createTexture(&(mat->texture2_mask),"texture2_mask",textureTransparancy);
1044    osg::ref_ptr<osg::Texture> opacity_map = createTexture(&(mat->opacity_map),"opacity_map",textureTransparancy);
1045    osg::ref_ptr<osg::Texture> opacity_mask = createTexture(&(mat->opacity_mask),"opacity_mask",textureTransparancy);
1046    osg::ref_ptr<osg::Texture> bump_map = createTexture(&(mat->bump_map),"bump_map",textureTransparancy);
1047    osg::ref_ptr<osg::Texture> bump_mask = createTexture(&(mat->bump_mask),"bump_mask",textureTransparancy);
1048    osg::ref_ptr<osg::Texture> specular_map = createTexture(&(mat->specular_map),"specular_map",textureTransparancy);
1049    osg::ref_ptr<osg::Texture> specular_mask = createTexture(&(mat->specular_mask),"specular_mask",textureTransparancy);
1050    osg::ref_ptr<osg::Texture> shininess_map = createTexture(&(mat->shininess_map),"shininess_map",textureTransparancy);
1051    osg::ref_ptr<osg::Texture> shininess_mask = createTexture(&(mat->shininess_mask),"shininess_mask",textureTransparancy);
1052    osg::ref_ptr<osg::Texture> self_illum_map = createTexture(&(mat->self_illum_map),"self_illum_map",textureTransparancy);
1053    osg::ref_ptr<osg::Texture> self_illum_mask = createTexture(&(mat->self_illum_mask),"self_illum_mask",textureTransparancy);
1054    osg::ref_ptr<osg::Texture> reflection_map = createTexture(&(mat->reflection_map),"reflection_map",textureTransparancy);
1055    osg::ref_ptr<osg::Texture> reflection_mask = createTexture(&(mat->reflection_mask),"reflection_mask",textureTransparancy);
1056*/
1057    return stateset;
1058}
1059
1060
1061
1062#if ENABLE_3DS_WRITER
1063osgDB::ReaderWriter::WriteResult ReaderWriter3DS::writeNode(const osg::Node& node,const std::string& fileName,const Options* options) const {
1064    std::string ext = osgDB::getLowerCaseFileExtension(fileName);
1065    if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
1066
1067    //osg::notify(osg::WARN) << "!!WARNING!! 3DS write support is incomplete" << std::endl;
1068
1069    bool ok = true;
1070    Lib3dsFile * file3ds = lib3ds_file_new();
1071    if (!file3ds) return WriteResult(WriteResult::ERROR_IN_WRITING_FILE);
1072
1073    try {
1074        osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
1075        local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileName));
1076
1077        if (!createFileObject(node, file3ds, fileName, local_opt.get())) ok = false;
1078        if (ok && !lib3ds_file_save(file3ds, fileName.c_str())) ok = false;
1079    } catch (...) {
1080        lib3ds_file_free(file3ds);
1081        throw;
1082    }
1083    lib3ds_file_free(file3ds);
1084
1085    return ok ? WriteResult(WriteResult::FILE_SAVED) : WriteResult(WriteResult::ERROR_IN_WRITING_FILE);
1086    //return ok ? WriteResult(WriteResult::FILE_SAVED) : WriteResult(WriteResult::FILE_NOT_HANDLED);
1087}
1088
1089
1090osgDB::ReaderWriter::WriteResult ReaderWriter3DS::writeNode(const osg::Node& node,std::ostream& fout,const Options* options) const {
1091    //osg::notify(osg::WARN) << "!!WARNING!! 3DS write support is incomplete" << std::endl;
1092    std::string optFileName = "";
1093    if (options)
1094    {
1095        optFileName = options->getPluginStringData("STREAM_FILENAME");
1096    }
1097
1098    Lib3dsIo io;
1099    io.self = &fout;
1100    io.seek_func = fileo_seek_func;
1101    io.tell_func = fileo_tell_func;
1102    io.read_func = NULL;
1103    io.write_func = fileo_write_func;
1104    io.log_func = fileio_log_func;
1105   
1106    Lib3dsFile * file3ds = lib3ds_file_new();
1107    bool ok = true;
1108    try {
1109        osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
1110        local_opt->getDatabasePathList().push_front(osgDB::getFilePath(optFileName));
1111
1112        if (!createFileObject(node, file3ds, optFileName, local_opt.get())) ok = false;
1113        if (ok && !lib3ds_file_write(file3ds, &io)) ok = false;
1114       
1115    } catch (...) {
1116        lib3ds_file_free(file3ds);
1117        throw;
1118    }
1119    lib3ds_file_free(file3ds);
1120
1121    return ok ? WriteResult(WriteResult::FILE_SAVED) : WriteResult(WriteResult::ERROR_IN_WRITING_FILE);
1122    //return ok ? WriteResult(WriteResult::FILE_SAVED) : WriteResult(WriteResult::FILE_NOT_HANDLED);
1123}
1124
1125const std::string getParent(const std::string & pathBad)
1126{
1127    const std::string & path = osgDB::convertFileNameToNativeStyle(pathBad);
1128
1129    std::string parent = "";
1130    std::string tmp = "";
1131    for(std::string::const_iterator itPath = path.begin();; ++itPath)
1132    {
1133        if (!parent.empty())
1134            parent += '\\';
1135        parent += tmp;
1136        tmp.clear();
1137        for(;itPath != path.end() && *itPath != '\\'; ++itPath)
1138            tmp += *itPath;
1139        if (itPath == path.end())
1140            break;
1141    }
1142    return parent;
1143}
1144
1145bool ReaderWriter3DS::createFileObject(const osg::Node& node, Lib3dsFile * file3ds,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const {
1146    WriterNodeVisitor w(file3ds, fileName, options, getParent(node.getName()));
1147    const_cast<osg::Node &>(node).accept(w);                // TODO Remove that ugly const_cast<>. Any idea?
1148    if (!w.suceedLastApply())
1149        return false;
1150    w.writeMaterials();
1151    return true;    //w.good();
1152}
1153#endif    // ENABLE_3DS_WRITER
Note: See TracBrowser for help on using the browser.