root/OpenSceneGraph/branches/OpenSceneGraph-2.8/src/osgPlugins/3ds/ReaderWriter3DS.cpp @ 11264

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

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

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