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

Revision 10932, 44.4 kB (checked in by robert, 4 years ago)

From Sukender, "ReaderWriter?3DS.cpp fixes:
- Fixed creation of useless intermediate nodes

WriterNodeVisitor?.cpp fixes:
- Fixed naming of textures (path and extension)
"

Note from Robert Osfield, this submission also came with changes to use of ref_ptr<> and removal of delete[]'s, but these were not merged as they didn't actually fix any memory leaks, and in once instance introduced one.

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