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

Revision 10941, 45.5 kB (checked in by robert, 5 years ago)

From Sukender, introduced usage of ref_ptr<> and local scoped_array to address more robust memory management.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
RevLine 
[10853]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
[8]3#include <osg/Notify>
4#include <osg/Group>
5#include <osg/Geode>
[799]6#include <osg/Geometry>
[1078]7#include <osg/Texture2D>
[8]8#include <osg/Material>
9#include <osg/TexEnv>
10#include <osg/ref_ptr>
[868]11#include <osg/MatrixTransform>
[8]12
13#include <osgDB/Registry>
14#include <osgDB/FileUtils>
15#include <osgDB/FileNameUtils>
16#include <osgDB/ReadFile>
17
[2535]18#include <osgUtil/TriStripVisitor>
19
[445]20//MIKEC debug only for PrintVisitor
21#include <osg/NodeVisitor>
22
[10853]23#ifdef ENABLE_3DS_WRITER
24    #include "WriterNodeVisitor.h"
25#endif
26#include "lib3ds/lib3ds.h"
[8]27#include <stdlib.h>
28#include <string.h>
29
30#include <set>
31#include <map>
[2772]32#include <iostream>
[10076]33#include <sstream>
[10853]34#include <assert.h>
[8]35
[445]36using namespace std;
37using namespace osg;
38
[10941]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;
[10853]47
[10941]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
[10853]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
[445]114class PrintVisitor : public NodeVisitor
115{
[10853]116public:
117    PrintVisitor(std::ostream& out):
118      NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
119          _out(out)
120      {
121          _indent = 0;
122          _step = 4;
123      }
[445]124
[10853]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      }
[445]131
[10853]132      virtual void apply(Node& node)
133      {
134          moveIn();
135          writeIndent(); _out << node.className() <<std::endl;
136          traverse(node);
137          moveOut();
138      }
[445]139
[10853]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); }
[9637]144
[10853]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); }
[9637]150
[10853]151protected:
152
153    PrintVisitor& operator = (const PrintVisitor&) { return *this; }
154
155    std::ostream& _out;
156    int _indent;
157    int _step;
[445]158};
159
[10853]160/// Possible options:
[10914]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.
[8]164class ReaderWriter3DS : public osgDB::ReaderWriter
165{
[10853]166public:
[8]167
[10853]168    ReaderWriter3DS();
[8]169
[10853]170    virtual const char* className() const { return "3DS Auto Studio Reader/Writer"; }
[8]171
[10853]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
[8]178
[10853]179protected:
180    ReadResult constructFrom3dsFile(Lib3dsFile *f,const std::string& filename, const Options* options) const;
[8]181
[10853]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
[8]185
[10853]186    class ReaderObject
187    {
188    public:
189        ReaderObject(const osgDB::ReaderWriter::Options* options);
[445]190
[10853]191        typedef std::vector<osg::StateSet*> StateSetMap;
192        typedef std::vector<int> FaceList;
193        typedef std::map<std::string,osg::StateSet*> GeoStateMap;
[445]194
[10853]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);
[3691]198
[10853]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    };
[8]214};
215
216// now register with Registry to instantiate the above
217// reader/writer.
[7076]218REGISTER_OSGPLUGIN(3ds, ReaderWriter3DS)
[8]219
220ReaderWriter3DS::ReaderWriter3DS()
221{
[8578]222    supportsExtension("3ds","3D Studio model format");
[10853]223    supportsOption("OutputTextureFiles","Write out the texture images to file");
[5453]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
[3691]245}
246
[10853]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)
[3691]254{
[10853]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    }
[8]266}
267
268
[445]269/**
270    These print methods for 3ds hacking
271*/
272void pad(int level) {
[2772]273    for(int i=0;i<level;i++) std::cout<<"  ";
[445]274}
275void print(Lib3dsMesh *mesh,int level);
276void print(Lib3dsUserData *user,int level);
[10853]277void print(Lib3dsMeshInstanceNode *object,int level);
[445]278void print(Lib3dsNode *node, int level);
[8]279
[445]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}
[10853]302
303void print(Lib3dsMeshInstanceNode *object,int level) {
[445]304    if (object) {
[10853]305        pad(level); cout << "objectdata instance [" << object->instance_name << "]" << endl;
[445]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}
[8]314
[445]315void print(Lib3dsNode *node, int level) {
[10853]316
[445]317    pad(level); cout << "node name [" << node->name << "]" << endl;
[10853]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;
[445]320    pad(level); cout << "node matrix:" << endl;
321    print(node->matrix,level+1);
[8]322
[10853]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
[445]332    for(Lib3dsNode *child=node->childs; child; child=child->next) {
333        print(child,level+1);
334    }
[8]335
[445]336}
[8]337
[10853]338void ReaderWriter3DS::ReaderObject::addDrawableFromFace(osg::Geode * geode, FaceList & faceList,
339                                                        Lib3dsMesh * mesh,
340                                                        const osg::Matrix * matrix,
341                                                        osg::StateSet * stateSet)
[6518]342{
[10853]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);
[10941]362            if (drawable.valid())
[10853]363            {
364                if (stateSet)
365                    drawable->setStateSet(stateSet);
[10941]366                geode->addDrawable(drawable.get());
[10853]367            }
368        }
369    }
370    else // ignore smoothing groups.
371    {
372        osg::ref_ptr<osg::Drawable> drawable = createDrawable(mesh,faceList,matrix);
[10941]373        if (drawable.valid())
[10853]374        {
375            if (stateSet)
376                drawable->setStateSet(stateSet);
[10941]377            geode->addDrawable(drawable.get());
[10853]378        }
379    }
[6518]380}
381
[10853]382
[445]383// Transforms points by matrix if 'matrix' is not NULL
[799]384// Creates a Geode and Geometry (as parent,child) and adds the Geode to 'parent' parameter iff 'parent' is non-NULL
[528]385// Returns ptr to the Geode
[10853]386osg::Node* ReaderWriter3DS::ReaderObject::processMesh(StateSetMap& drawStateMap,osg::Group* parent,Lib3dsMesh* mesh, const osg::Matrix * matrix) {
387    typedef std::vector<FaceList> MaterialFaceMap;
[445]388    MaterialFaceMap materialFaceMap;
[10853]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)
[8]393    {
[10853]394        if (mesh->faces[i].material>=0) {
395            materialFaceMap[mesh->faces[i].material].push_back(i);
396        }
397        else
398            defaultMaterialFaceList.push_back(i);
[8]399    }
[10853]400    if (materialFaceMap.empty() && defaultMaterialFaceList.empty())
[8]401    {
[445]402        osg::notify(osg::NOTICE)<<"Warning : no triangles assigned to mesh '"<<mesh->name<<"'"<< std::endl;
[10853]403        //osg::notify(osg::INFO) << "No material assigned to mesh '" << mesh->name << "'" << std::endl;
[528]404        return NULL;
[445]405    }
406    else
407    {
408        osg::Geode* geode = new osg::Geode;
409        geode->setName(mesh->name);
[10853]410        if (!defaultMaterialFaceList.empty())
[8]411        {
[10853]412            addDrawableFromFace(geode, defaultMaterialFaceList, mesh, matrix, NULL);
[445]413        }
[10853]414        for(unsigned int imat=0; imat<numMaterials; ++imat)
415        {
416            addDrawableFromFace(geode, materialFaceMap[imat], mesh, matrix, drawStateMap[imat]);
417        }
[528]418        if (parent) parent->addChild(geode);
419        return geode;
[8]420    }
421}
422
423
[10853]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
[445]434/**
435How to cope with pivot points in 3ds (short version)
[8]436
[528]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?)
[8]439
[445]440  Objects with pivot points have a position relative to their PP, so they have to undergo this transform:
[8]441
[445]442    invert the mesh matrix, apply this matrix to the object. This puts the object back at the origin
[7648]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.
[8]445
[10853]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);
[10914]452    assert(!(mesh && !object));        // Node must be a LIB3DS_NODE_MESH_INSTANCE if a mesh exists
[8]453
[10853]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) {
[10914]476                meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix));
[4801]477            } else {
[10853]478                meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * osg::Matrix::translate(-pivot);
[4801]479            }
[528]480        }
[10853]481        else {
[10914]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 ;)
[10853]488        }
[445]489    }
[8]490
[10853]491    bool isOsgNodeMatrixIdentity = false;
492    if (osgNodeMatrix.isIdentity() || (checkForEspilonIdentityMatrices && isIdentityEquivalent(osgNodeMatrix, MATRIX_EPSILON))) {
493        isOsgNodeMatrixIdentity = true;
494    }
495
496
[10932]497    //if (node->childs != NULL || pivoted || (!isOsgNodeMatrixIdentity && !noMatrixTransforms)) {
498    if (node->childs != NULL || (!isOsgNodeMatrixIdentity && !noMatrixTransforms)) {
[10853]499        if (isOsgNodeMatrixIdentity || noMatrixTransforms) {
500            group = new osg::Group;
501        } else {
502            group = new osg::MatrixTransform(osgNodeMatrix);
[528]503        }
[10853]504    }
[445]505
[10853]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);
[445]514
[10853]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 {
[10914]520        assert(node->childs == NULL);        // Else we must have a group to put childs into
[10853]521    }
[445]522
[10853]523    // Handle mesh
524    if (mesh) {
525        osg::Matrix * meshAppliedMatPtr = NULL;
526        if (!meshMat.isIdentity() && !(checkForEspilonIdentityMatrices && isIdentityEquivalent(meshMat, MATRIX_EPSILON))) {
527            meshAppliedMatPtr = &meshMat;
528        }
[528]529
[10853]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;
[528]535        } else {
[10853]536            // didnt use group for children
537            // return a ptr directly to the Geode for this mesh
538            return processMesh(drawStateMap,NULL,mesh,meshAppliedMatPtr);
[528]539        }
540
[445]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;
[528]544        return group; // we have no mesh, but we might have children
[8]545    }
546}
547
[10853]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
[10076]609osgDB::ReaderWriter::ReadResult ReaderWriter3DS::readNode(std::istream& fin,  const osgDB::ReaderWriter::Options* options) const
610{
611    osgDB::ReaderWriter::ReadResult result = ReadResult::FILE_NOT_HANDLED;
[8]612
[10076]613    std::string optFileName = "";
614    if (options)
615    {
616        optFileName = options->getPluginStringData("STREAM_FILENAME");
[10853]617        if (optFileName.empty()) optFileName = options->getPluginStringData("filename");
[10076]618    }
619
[10853]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)
[10076]631    {
[10853]632        result = constructFrom3dsFile(file3ds,optFileName,options);
633        lib3ds_file_free(file3ds);
[10076]634    }
635
636    return(result);
637}
638
[3694]639osgDB::ReaderWriter::ReadResult ReaderWriter3DS::readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const
[8]640{
[10076]641    osgDB::ReaderWriter::ReadResult result = ReadResult::FILE_NOT_HANDLED;
[5453]642
[2501]643    std::string ext = osgDB::getLowerCaseFileExtension(file);
644    if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
645
[3691]646    std::string fileName = osgDB::findDataFile( file, options );
[2501]647    if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
648
[10853]649    Lib3dsFile *f = lib3ds_file_open(fileName.c_str() /*,options*/);
[10076]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{
[445]665    if (f==NULL) return ReadResult::FILE_NOT_HANDLED;
[8]666
[445]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
[8]672
[10853]673    ReaderObject reader(options);
[8]674
[10805]675    reader._directory = ( options && !options->getDatabasePathList().empty() ) ? options->getDatabasePathList().front() : osgDB::getFilePath(fileName);
[3691]676
677    ReaderObject::StateSetMap drawStateMap;
[10853]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)
[8]681    {
[10853]682        Lib3dsMaterial * mat = f->materials[imat];
683        drawStateMap[imat] = reader.createStateSet(mat);
[445]684    }
[10853]685
[5453]686    if (osg::getNotifyLevel()>=osg::INFO)
687    {
[445]688        int level=0;
[5453]689        std::cout << "NODE TRAVERSAL of 3ds file "<<f->name<<std::endl;
[445]690        for(Lib3dsNode *node=f->nodes; node; node=node->next) {
691            print(node,level+1);
[8]692        }
[5453]693        std::cout << "MESH TRAVERSAL of 3ds file "<<f->name<<std::endl;
[10853]694        for (int imesh=0; imesh<f->nmeshes; ++imesh) {
695            print(f->meshes[imesh],level+1);
[8]696        }
[5453]697    }
[8]698
[445]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;
[10853]703
[445]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;
[8]709    }
710
[10853]711    osg::Node* group = NULL;
712
[445]713    if (traverse_nodes) { // old method
[10853]714        group = new osg::Group();
715        for (int imesh=0; imesh<f->nmeshes; ++imesh) {
716            reader.processMesh(drawStateMap,group->asGroup(),f->meshes[imesh],NULL);
[445]717        }
718    } else { // new method
[10853]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            }
[445]728        }
[10853]729    }
730    if (group && group->getName().empty()) group->setName(fileName);
[445]731
[6518]732    if (osg::getNotifyLevel()>=osg::INFO)
[5453]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);
[10853]737    }
738
[445]739    return group;
[8]740}
741
[445]742/**
743use matrix to pretransform geometry, or NULL to do nothing
744*/
[10853]745osg::Drawable* ReaderWriter3DS::ReaderObject::createDrawable(Lib3dsMesh *m,FaceList& faceList, const osg::Matrix * matrix)
[8]746{
[10941]747    osg::Geometry * geom = new osg::Geometry;
[10853]748    unsigned int i;
[8]749
750    std::vector<int> orig2NewMapping;
[10853]751    orig2NewMapping.reserve(m->nvertices);
752    for(i=0;i<m->nvertices;++i) orig2NewMapping.push_back(-1);
[8]753
754    unsigned int noVertex=0;
755    FaceList::iterator fitr;
756    for (fitr=faceList.begin();
757        fitr!=faceList.end();
758        ++fitr)
759    {
760
[10853]761        Lib3dsFace& face = m->faces[*fitr];
[8]762
[10853]763        if (orig2NewMapping[face.index[0]]<0)
764            orig2NewMapping[face.index[0]] = noVertex++;
[8]765
[10853]766        if (orig2NewMapping[face.index[1]]<0)
767            orig2NewMapping[face.index[1]] = noVertex++;
[8]768
[10853]769        if (orig2NewMapping[face.index[2]]<0)
770            orig2NewMapping[face.index[2]] = noVertex++;
[8]771
772    }
773
[814]774    // create vertices.
[10853]775
[10941]776    osg::ref_ptr<osg::Vec3Array> osg_coords = new osg::Vec3Array(noVertex);
777    geom->setVertexArray(osg_coords.get());
[814]778
[10853]779    for (i=0; i<m->nvertices; ++i)
[8]780    {
[814]781        if (orig2NewMapping[i]>=0)
782        {
[445]783            if (matrix)
[270]784            {
[10853]785                (*osg_coords)[orig2NewMapping[i]].set( copyLib3dsVec3ToOsgVec3(m->vertices[i]) * (*matrix) );
[270]786            }
787            else
788            {
789                // original no transform code.
[10853]790                (*osg_coords)[orig2NewMapping[i]].set( copyLib3dsVec3ToOsgVec3(m->vertices[i]) );
[270]791            }
792        }
[8]793    }
794
[814]795    // create texture coords if needed.
[10853]796    if (m->texcos)
[8]797    {
[10941]798        osg::ref_ptr<osg::Vec2Array> osg_tcoords = new osg::Vec2Array(noVertex);
799        geom->setTexCoordArray(0, osg_tcoords.get());
[10853]800        for (i=0; i<m->nvertices; ++i)
[8]801        {
[10853]802            if (orig2NewMapping[i]>=0) (*osg_tcoords)[orig2NewMapping[i]].set(m->texcos[i][0],m->texcos[i][1]);
[8]803        }
804    }
805
[10853]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.
[8]809    if (_usePerVertexNormals)
810    {
[10853]811        //Lib3dsVector * normals = new Lib3dsVector[m->nfaces*3];
812        //lib3ds_mesh_calculate_vertex_normals(m, normals);
[10941]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);
[10853]816
[8]817        // initialize normal list to zero's.
818        for (i=0; i<noVertex; ++i)
819        {
[799]820            (*osg_normals)[i].set(0.0f,0.0f,0.0f);
[8]821        }
822
[814]823        for (fitr=faceList.begin();
824            fitr!=faceList.end();
825            ++fitr)
[8]826        {
[10853]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;
[8]834        }
835
[10941]836        geom->setNormalArray(osg_normals.get());
[799]837        geom->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
[8]838    }
[10853]839    else
[8]840    {
[10941]841        scoped_array<Lib3dsVector> normals ( new Lib3dsVector[m->nfaces] );
[10853]842        lib3ds_mesh_calculate_face_normals(m, normals);
[10941]843        osg::ref_ptr<osg::Vec3Array> osg_normals = new osg::Vec3Array(faceList.size());
[814]844        osg::Vec3Array::iterator normal_itr = osg_normals->begin();
845        for (fitr=faceList.begin();
846            fitr!=faceList.end();
847            ++fitr)
848        {
[10853]849            osg::Vec3f osgNormal( copyLib3dsVec3ToOsgVec3(normals[*fitr]) );
850            if (matrix) osgNormal = osg::Matrix::transform3x3(osgNormal, *matrix);
851            osgNormal.normalize();
852            *(normal_itr++) = osgNormal;
[814]853        }
[10941]854        geom->setNormalArray(osg_normals.get());
[799]855        geom->setNormalBinding(osg::Geometry::BIND_PER_PRIMITIVE);
[8]856    }
[10853]857
[10941]858    osg::ref_ptr<osg::Vec4ubArray> osg_colors = new osg::Vec4ubArray(1);
[2339]859    (*osg_colors)[0].set(255,255,255,255);
[10941]860    geom->setColorArray(osg_colors.get());
[2339]861    geom->setColorBinding(osg::Geometry::BIND_OVERALL);
862
[814]863    // create primitives
864    int numIndices = faceList.size()*3;
[10941]865    osg::ref_ptr<DrawElementsUShort> elements = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES,numIndices);
[1166]866    DrawElementsUShort::iterator index_itr = elements->begin();
[8]867
[814]868    for (fitr=faceList.begin();
869        fitr!=faceList.end();
870        ++fitr)
871    {
[10853]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]];
[814]876    }
[10853]877
[10941]878    geom->addPrimitiveSet(elements.get());
[8]879
[5453]880#if 0
[2535]881    osgUtil::TriStripVisitor tsv;
882    tsv.stripify(*geom);
[5453]883#endif
[2535]884
[799]885    return geom;
[8]886}
[445]887
888
[10853]889osg::Texture2D*  ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap *texture,const char* label,bool& transparancy)
[445]890{
891    if (texture && *(texture->name))
892    {
[10076]893        osg::notify(osg::NOTICE)<<"texture->name="<<texture->name<<", _directory="<<_directory<<std::endl;
894
[2449]895        std::string fileName = osgDB::findFileInDirectory(texture->name,_directory,osgDB::CASE_INSENSITIVE);
[10853]896        if (fileName.empty())
[2449]897        {
898            // file not found in .3ds file's directory, so we'll look in the datafile path list.
[3691]899            fileName = osgDB::findDataFile(texture->name,options, osgDB::CASE_INSENSITIVE);
[10853]900            osg::notify(osg::NOTICE)<<"texture->name="<<texture->name<<", _directory="<<_directory<<std::endl;
[2449]901        }
[10076]902
[445]903        if (fileName.empty())
904        {
[10076]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            }
[445]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;
[10853]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;
[445]928
[10932]929        osg::ref_ptr<osg::Image> osg_image = osgDB::readRefImageFile(fileName.c_str(), options); //Absolute Path
930        if (!osg_image.valid())
[10853]931        {
[445]932            osg::notify(osg::NOTICE) << "Warning: Cannot create texture "<<texture->name<< std::endl;
933            return NULL;
934        }
[10853]935        if (osg_image->getFileName().empty()) // it should be done in OSG with osgDB::readRefImageFile(fileName.c_str());
936            osg_image->setFileName(fileName);
[1078]937        osg::Texture2D* osg_texture = new osg::Texture2D;
[9527]938        osg_texture->setImage(osg_image.get());
[10853]939        osg_texture->setName(texture->name);
[445]940        // does the texture support transparancy?
[10853]941        transparancy = ((texture->flags)&LIB3DS_TEXTURE_ALPHA_SOURCE)!=0;
[445]942
943        // what is the wrap mode of the texture.
[10853]944        osg::Texture2D::WrapMode wm = ((texture->flags)&LIB3DS_TEXTURE_NO_TILE) ?
[4461]945                osg::Texture2D::CLAMP :
946                osg::Texture2D::REPEAT;
[1078]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);
[445]950                                 // bilinear.
[1078]951        osg_texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR_MIPMAP_NEAREST);
[445]952
953        return osg_texture;
954    }
955    else
956        return NULL;
957}
958
959
[10853]960osg::StateSet* ReaderWriter3DS::ReaderObject::createStateSet(Lib3dsMaterial *mat)
[445]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);
[2421]974    specular *= mat->shin_strength;
[445]975
976    float shininess = mat->shininess;
[7623]977    material->setName(mat->name);
[445]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);
[1020]981    material->setShininess(osg::Material::FRONT_AND_BACK,shininess*128.0f);
[445]982
983    stateset->setAttribute(material);
984
985    bool textureTransparancy=false;
[10853]986    osg::Texture2D* texture1_map = createTexture(&(mat->texture1_map),"texture1_map",textureTransparancy);
[445]987    if (texture1_map)
988    {
[845]989        stateset->setTextureAttributeAndModes(0,texture1_map,osg::StateAttribute::ON);
[10853]990
[2339]991        if (!textureTransparancy)
[10853]992        {
[2339]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.
[10853]998
[2339]999            // so we'll override material back to white.
1000            // and no longer require the decal hack below...
[2872]1001#if 0
[7648]1002            // Eric original fallback
[2339]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);
[2872]1007#else
[7648]1008            // try alternative to avoid saturating with white
[2872]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));
[10853]1013#endif
[445]1014        }
[10853]1015
1016// no longer required...
[2339]1017//         bool decal = false;
[10853]1018//
[2339]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);
[445]1032    }
1033
[10076]1034    if (transparency>0.0f || textureTransparancy)
[445]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
[10853]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
[10941]1077        if (!createFileObject(node, file3ds, fileName, local_opt.get())) ok = false;
[10853]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
[10941]1112        if (!createFileObject(node, file3ds, optFileName, local_opt.get())) ok = false;
[10853]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();
[10914]1151    return true;    //w.good();
[10853]1152}
1153#endif    // ENABLE_3DS_WRITER
Note: See TracBrowser for help on using the browser.