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

Revision 10853, 44.7 kB (checked in by robert, 4 years ago)

From Sukender,
"Here is our freshly baked 3DS reader/writer (named 'v0.5' to differentiate from previous one). Changes are against trunk rev. 10819.
Short changelog (from rev 10819):
- Added 3DS writer
- Sync'd with latest lib3DS
- Added options, especially "flattenMatrixTransforms" to get the "old" behaviour (else the reader correctly maps to OSG the transforms from the 3DS file).

What should be done:
- Check with pivot points, with and without "flattenMatrixTransforms" option.
- We ran tests on it, but we can never be 100% sure there is no bug. Testing from the community would of course be helpful."

  • 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:
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            //std::stringstream materialStr;
352            //materialStr << mesh->faces[i].material;
353            //materialFaceMap[materialStr.str()].push_back(i);
354            materialFaceMap[mesh->faces[i].material].push_back(i);
355        }
356        else
357            defaultMaterialFaceList.push_back(i);
[8]358    }
[10853]359    if (materialFaceMap.empty() && defaultMaterialFaceList.empty())
[8]360    {
[445]361        osg::notify(osg::NOTICE)<<"Warning : no triangles assigned to mesh '"<<mesh->name<<"'"<< std::endl;
[10853]362        //osg::notify(osg::INFO) << "No material assigned to mesh '" << mesh->name << "'" << std::endl;
[528]363        return NULL;
[445]364    }
365    else
366    {
367        osg::Geode* geode = new osg::Geode;
368        geode->setName(mesh->name);
[10853]369        if (!defaultMaterialFaceList.empty())
[8]370        {
[10853]371            addDrawableFromFace(geode, defaultMaterialFaceList, mesh, matrix, NULL);
[445]372        }
[10853]373        for(unsigned int imat=0; imat<numMaterials; ++imat)
374        {
375            addDrawableFromFace(geode, materialFaceMap[imat], mesh, matrix, drawStateMap[imat]);
376        }
[528]377        if (parent) parent->addChild(geode);
378        return geode;
[8]379    }
380}
381
382
[10853]383/// 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.
384bool isIdentityEquivalent(const osg::Matrix & mat, osg::Matrix::value_type epsilon=1e-6)
385{
386    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) &&
387           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) &&
388           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) &&
389           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);
390}
391
392
[445]393/**
394How to cope with pivot points in 3ds (short version)
[8]395
[528]396  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
397  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]398
[445]399  Objects with pivot points have a position relative to their PP, so they have to undergo this transform:
[8]400
[445]401    invert the mesh matrix, apply this matrix to the object. This puts the object back at the origin
[7648]402    Transform the object by the nodes (negative) pivot point coords, this puts the PP at the origin
403    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]404
[10853]405*/
406osg::Node* ReaderWriter3DS::ReaderObject::processNode(StateSetMap drawStateMap,Lib3dsFile *f,Lib3dsNode *node)
407{
408    // Get mesh
409    Lib3dsMeshInstanceNode * object = (node->type == LIB3DS_NODE_MESH_INSTANCE) ? reinterpret_cast<Lib3dsMeshInstanceNode *>(node) : NULL;
410    Lib3dsMesh * mesh = lib3ds_file_mesh_for_node(f,node);
411    assert(!(mesh && !object));         // Node must be a LIB3DS_NODE_MESH_INSTANCE if a mesh exists
[8]412
[10853]413    // Retreive LOCAL transform
414    static const osg::Matrix::value_type MATRIX_EPSILON = 1e-10;
415    osg::Matrix osgNodeMatrix( copyLib3dsMatrixToOsgMatrix(node->matrix) );
416
417    if (node->parent)
418    {
419        // Matrices evaluated by lib3DS are multiplied by parents' ones
420        osgNodeMatrix *= osg::Matrix::inverse( copyLib3dsMatrixToOsgMatrix(node->parent->matrix) );
421    }
422
423    // Test if we should create an intermediate Group (or MatrixTransform) and which matrix we should apply to the vertices
424    osg::Group* group = NULL;
425
426    // Get pivot point
427    osg::Vec3 pivot( object ? copyLib3dsVec3ToOsgVec3(object->pivot) : osg::Vec3() );
428    bool pivoted = pivot.x()!=0 || pivot.y()!=0 || pivot.z()!=0;
429
430    osg::Matrix meshMat;
431    if (mesh) {
432         if (!noMatrixTransforms) {
433            // 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
434            if (!pivoted) {
435                                meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix));
[4801]436            } else {
[10853]437                meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * osg::Matrix::translate(-pivot);
[4801]438            }
[528]439        }
[10853]440        else {
441                        if (pivoted) {
442                                meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * osg::Matrix::translate(-pivot) * copyLib3dsMatrixToOsgMatrix(node->matrix);
443                        } else {
444                                meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * copyLib3dsMatrixToOsgMatrix(node->matrix);
445                        }
446                        osgNodeMatrix = osg::Matrix::identity();                // Not sure it's useful, but it's harmless ;)
447        }
[445]448    }
[8]449
[10853]450    bool isOsgNodeMatrixIdentity = false;
451    if (osgNodeMatrix.isIdentity() || (checkForEspilonIdentityMatrices && isIdentityEquivalent(osgNodeMatrix, MATRIX_EPSILON))) {
452        isOsgNodeMatrixIdentity = true;
453    }
454
455
456    if (node->childs != NULL || pivoted || (!isOsgNodeMatrixIdentity && !noMatrixTransforms)) {
457        if (isOsgNodeMatrixIdentity || noMatrixTransforms) {
458            group = new osg::Group;
459        } else {
460            group = new osg::MatrixTransform(osgNodeMatrix);
[528]461        }
[10853]462    }
[445]463
[10853]464    if (group) {
465        if (strcmp(node->name, "$$$DUMMY") == 0)
466        {
467            if (node->type == LIB3DS_NODE_MESH_INSTANCE)
468                group->setName(reinterpret_cast<Lib3dsMeshInstanceNode *>(node)->instance_name);
469        }
470        else
471            group->setName(node->name);
[445]472
[10853]473        // Handle all children of this node for hierarchical assemblies
474        for (Lib3dsNode *p=node->childs; p!=NULL; p=p->next) {
475            group->addChild(processNode(drawStateMap,f,p));
476        }
477    } else {
478        assert(node->childs == NULL);           // Else we must have a group to put childs into
479    }
[445]480
[10853]481    // Handle mesh
482    if (mesh) {
483        osg::Matrix * meshAppliedMatPtr = NULL;
484        if (!meshMat.isIdentity() && !(checkForEspilonIdentityMatrices && isIdentityEquivalent(meshMat, MATRIX_EPSILON))) {
485            meshAppliedMatPtr = &meshMat;
486        }
[528]487
[10853]488        if(group) {
489            // add our geometry to group (where our children already are)
490            // creates geometry under modifier node
491            processMesh(drawStateMap,group,mesh,meshAppliedMatPtr);
492            return group;
[528]493        } else {
[10853]494            // didnt use group for children
495            // return a ptr directly to the Geode for this mesh
496            return processMesh(drawStateMap,NULL,mesh,meshAppliedMatPtr);
[528]497        }
498
[445]499    } else {
500        // no mesh for this node - probably a camera or something of that persuasion
501        //cout << "no mesh for object " << node->name << endl;
[528]502        return group; // we have no mesh, but we might have children
[8]503    }
504}
505
[10853]506
507static long filei_seek_func(void *self, long offset, Lib3dsIoSeek origin) {
508    std::istream *f = reinterpret_cast<std::istream*>(self);
509    ios_base::seekdir o = ios_base::beg;
510    if (origin == LIB3DS_SEEK_CUR) o = ios_base::cur;
511    else if (origin == LIB3DS_SEEK_END) o = ios_base::end;
512
513    f->seekg(offset, o);
514    return f->fail() ? -1 : 0;
515}
516
517#if ENABLE_3DS_WRITER
518static long fileo_seek_func(void *self, long offset, Lib3dsIoSeek origin) {
519    std::ostream *f = reinterpret_cast<std::ostream*>(self);
520    ios_base::seekdir o = ios_base::beg;
521    if (origin == LIB3DS_SEEK_CUR) o = ios_base::cur;
522    else if (origin == LIB3DS_SEEK_END) o = ios_base::end;
523
524    f->seekp(offset, o);
525    return f->fail() ? -1 : 0;
526}
527#endif
528
529static long filei_tell_func(void *self) {
530    std::istream *f = reinterpret_cast<std::istream*>(self);
531    return f->tellg();
532}
533
534#if ENABLE_3DS_WRITER
535static long fileo_tell_func(void *self) {
536    std::ostream *f = reinterpret_cast<std::ostream*>(self);
537    return f->tellp();
538}
539#endif
540
541
542static size_t filei_read_func(void *self, void *buffer, size_t size) {
543    std::istream *f = reinterpret_cast<std::istream*>(self);
544    f->read(reinterpret_cast<char*>(buffer), size);
545    return f->gcount();
546}
547
548#if ENABLE_3DS_WRITER
549static size_t fileo_write_func(void *self, const void *buffer, size_t size) {
550    std::ostream *f = reinterpret_cast<std::ostream*>(self);
551    f->write(static_cast<const char*>(buffer), size);
552    return f->fail() ? 0 : size;
553}
554#endif
555
556static void fileio_log_func(void *self, Lib3dsLogLevel level, int indent, const char *msg)
557{
558    osg::NotifySeverity l = osg::INFO;
559    if (level == LIB3DS_LOG_ERROR) l = osg::FATAL;
560    else if (level == LIB3DS_LOG_WARN) l = osg::WARN;
561    else if (level == LIB3DS_LOG_INFO) l = osg::INFO;
562    else if (level == LIB3DS_LOG_DEBUG) l = osg::DEBUG_INFO;
563    osg::notify(l) << msg << std::endl;
564}
565
566
[10076]567osgDB::ReaderWriter::ReadResult ReaderWriter3DS::readNode(std::istream& fin,  const osgDB::ReaderWriter::Options* options) const
568{
569    osgDB::ReaderWriter::ReadResult result = ReadResult::FILE_NOT_HANDLED;
[8]570
[10076]571    std::string optFileName = "";
572    if (options)
573    {
574        optFileName = options->getPluginStringData("STREAM_FILENAME");
[10853]575        if (optFileName.empty()) optFileName = options->getPluginStringData("filename");
[10076]576    }
577
[10853]578    // Prepare io structure to tell how to read the stream
579    Lib3dsIo io;
580    io.self = &fin;
581    io.seek_func = filei_seek_func;
582    io.tell_func = filei_tell_func;
583    io.read_func = filei_read_func;
584    io.write_func = NULL;
585    io.log_func = fileio_log_func;
586
587    Lib3dsFile * file3ds = lib3ds_file_new();
588    if (lib3ds_file_read(file3ds, &io) != 0)
[10076]589    {
[10853]590        result = constructFrom3dsFile(file3ds,optFileName,options);
591        lib3ds_file_free(file3ds);
[10076]592    }
593
594    return(result);
595}
596
[3694]597osgDB::ReaderWriter::ReadResult ReaderWriter3DS::readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const
[8]598{
[10076]599    osgDB::ReaderWriter::ReadResult result = ReadResult::FILE_NOT_HANDLED;
[5453]600
[2501]601    std::string ext = osgDB::getLowerCaseFileExtension(file);
602    if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
603
[3691]604    std::string fileName = osgDB::findDataFile( file, options );
[2501]605    if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
606
[10853]607    Lib3dsFile *f = lib3ds_file_open(fileName.c_str() /*,options*/);
[10076]608
609    if (f)
610    {
611        osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
612        local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileName));
613
614        result = constructFrom3dsFile(f,file,local_opt.get());
615        lib3ds_file_free(f);
616    }
617
618    return result;
619}
620
621osgDB::ReaderWriter::ReadResult ReaderWriter3DS::constructFrom3dsFile(Lib3dsFile *f,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
622{
[445]623    if (f==NULL) return ReadResult::FILE_NOT_HANDLED;
[8]624
[445]625    // MIKEC
626    // This appears to build the matrix structures for the 3ds structure
627    // It wasn't previously necessary because all the meshes are stored in world coordinates
628    // but is VERY necessary if you want to use pivot points...
629    lib3ds_file_eval(f,0.0f); // second param is time 't' for animated files
[8]630
[10853]631    ReaderObject reader(options);
[8]632
[10805]633    reader._directory = ( options && !options->getDatabasePathList().empty() ) ? options->getDatabasePathList().front() : osgDB::getFilePath(fileName);
[3691]634
635    ReaderObject::StateSetMap drawStateMap;
[10853]636    unsigned int numMaterials = f->nmaterials;
637    drawStateMap.insert(drawStateMap.begin(), numMaterials, NULL);        // Setup the map
638    for (unsigned int imat=0; imat<numMaterials; ++imat)
[8]639    {
[10853]640        Lib3dsMaterial * mat = f->materials[imat];
641        drawStateMap[imat] = reader.createStateSet(mat);
[445]642    }
[10853]643
[5453]644    if (osg::getNotifyLevel()>=osg::INFO)
645    {
[445]646        int level=0;
[5453]647        std::cout << "NODE TRAVERSAL of 3ds file "<<f->name<<std::endl;
[445]648        for(Lib3dsNode *node=f->nodes; node; node=node->next) {
649            print(node,level+1);
[8]650        }
[5453]651        std::cout << "MESH TRAVERSAL of 3ds file "<<f->name<<std::endl;
[10853]652        for (int imesh=0; imesh<f->nmeshes; ++imesh) {
653            print(f->meshes[imesh],level+1);
[8]654        }
[5453]655    }
[8]656
[445]657    // We can traverse by meshes (old method, broken for pivot points, but otherwise works), or by nodes (new method, not so well tested yet)
658    // if your model is broken, especially wrt object positions try setting this flag. If that fixes it,
659    // send me the model
660    bool traverse_nodes=false;
[10853]661
[445]662    // MIKEC: have found 3ds files with NO node structure - only meshes, for this case we fall back to the old traverse-by-meshes code
663    // Loading and re-exporting these files from 3DS produces a file with correct node structure, so perhaps these are not 100% conformant?
664    if (f->nodes == NULL) {
665        osg::notify(osg::WARN)<<"Warning: in 3ds loader: file has no nodes, traversing by meshes instead"<< std::endl;
666        traverse_nodes=true;
[8]667    }
668
[10853]669    osg::Node* group = NULL;
670
[445]671    if (traverse_nodes) { // old method
[10853]672        group = new osg::Group();
673        for (int imesh=0; imesh<f->nmeshes; ++imesh) {
674            reader.processMesh(drawStateMap,group->asGroup(),f->meshes[imesh],NULL);
[445]675        }
676    } else { // new method
[10853]677        Lib3dsNode *node=f->nodes;
678        if (!node->next)
679            group = reader.processNode(drawStateMap,f,node);
680        else
681        {
682            group = new osg::Group();
683            for(; node; node=node->next) {
684                group->asGroup()->addChild(reader.processNode(drawStateMap,f,node));
685            }
[445]686        }
[10853]687    }
688    if (group && group->getName().empty()) group->setName(fileName);
[445]689
[6518]690    if (osg::getNotifyLevel()>=osg::INFO)
[5453]691    {
692        osg::notify(osg::NOTICE) << "Final OSG node structure looks like this:"<< endl;
693        PrintVisitor pv(osg::notify(osg::NOTICE));
694        group->accept(pv);
[10853]695    }
696
[445]697    return group;
[8]698}
699
[445]700/**
701use matrix to pretransform geometry, or NULL to do nothing
702*/
[10853]703osg::Drawable* ReaderWriter3DS::ReaderObject::createDrawable(Lib3dsMesh *m,FaceList& faceList, const osg::Matrix * matrix)
[8]704{
705
[799]706    osg::Geometry* geom = new osg::Geometry;
[10853]707    unsigned int i;
[8]708
709    std::vector<int> orig2NewMapping;
[10853]710    orig2NewMapping.reserve(m->nvertices);
711    for(i=0;i<m->nvertices;++i) orig2NewMapping.push_back(-1);
[8]712
713    unsigned int noVertex=0;
714    FaceList::iterator fitr;
715    for (fitr=faceList.begin();
716        fitr!=faceList.end();
717        ++fitr)
718    {
719
[10853]720        Lib3dsFace& face = m->faces[*fitr];
[8]721
[10853]722        if (orig2NewMapping[face.index[0]]<0)
723            orig2NewMapping[face.index[0]] = noVertex++;
[8]724
[10853]725        if (orig2NewMapping[face.index[1]]<0)
726            orig2NewMapping[face.index[1]] = noVertex++;
[8]727
[10853]728        if (orig2NewMapping[face.index[2]]<0)
729            orig2NewMapping[face.index[2]] = noVertex++;
[8]730
731    }
732
[814]733    // create vertices.
[10853]734
[799]735    osg::Vec3Array* osg_coords = new osg::Vec3Array(noVertex);
[814]736    geom->setVertexArray(osg_coords);
737
[10853]738    for (i=0; i<m->nvertices; ++i)
[8]739    {
[814]740        if (orig2NewMapping[i]>=0)
741        {
[445]742            if (matrix)
[270]743            {
[10853]744                (*osg_coords)[orig2NewMapping[i]].set( copyLib3dsVec3ToOsgVec3(m->vertices[i]) * (*matrix) );
[270]745            }
746            else
747            {
748                // original no transform code.
[10853]749                (*osg_coords)[orig2NewMapping[i]].set( copyLib3dsVec3ToOsgVec3(m->vertices[i]) );
[270]750            }
751        }
[8]752    }
753
[814]754    // create texture coords if needed.
[10853]755    if (m->texcos)
[8]756    {
[10853]757        osg::Vec2Array* osg_tcoords = new osg::Vec2Array(noVertex);
758        geom->setTexCoordArray(0,osg_tcoords);
759        for (i=0; i<m->nvertices; ++i)
[8]760        {
[10853]761            if (orig2NewMapping[i]>=0) (*osg_tcoords)[orig2NewMapping[i]].set(m->texcos[i][0],m->texcos[i][1]);
[8]762        }
763    }
764
[10853]765    // create normals
766    // Sukender: 3DS file format doesn't store normals (that is to say they're recomputed each time).
767    // 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]768    if (_usePerVertexNormals)
769    {
[10853]770        //Lib3dsVector * normals = new Lib3dsVector[m->nfaces*3];
771        //lib3ds_mesh_calculate_vertex_normals(m, normals);
772        Lib3dsVector * normals = new Lib3dsVector[m->nfaces];
773        lib3ds_mesh_calculate_face_normals(m, normals);
[4461]774        osg::Vec3Array* osg_normals = new osg::Vec3Array(noVertex);
[10853]775
[8]776        // initialize normal list to zero's.
777        for (i=0; i<noVertex; ++i)
778        {
[799]779            (*osg_normals)[i].set(0.0f,0.0f,0.0f);
[8]780        }
781
[814]782        for (fitr=faceList.begin();
783            fitr!=faceList.end();
784            ++fitr)
[8]785        {
[10853]786            Lib3dsFace& face = m->faces[*fitr];
787            osg::Vec3f osgNormal( copyLib3dsVec3ToOsgVec3(normals[*fitr]) );
788            if (matrix) osgNormal = osg::Matrix::transform3x3(osgNormal, *matrix);
789            osgNormal.normalize();
790            (*osg_normals)[orig2NewMapping[face.index[0]]] = osgNormal;
791            (*osg_normals)[orig2NewMapping[face.index[1]]] = osgNormal;
792            (*osg_normals)[orig2NewMapping[face.index[2]]] = osgNormal;
[8]793        }
794
[799]795        geom->setNormalArray(osg_normals);
796        geom->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
[8]797
798    }
[10853]799    else
[8]800    {
[10853]801        Lib3dsVector * normals = new Lib3dsVector[m->nfaces];
802        lib3ds_mesh_calculate_face_normals(m, normals);
[814]803        osg::Vec3Array* osg_normals = new osg::Vec3Array(faceList.size());
804        osg::Vec3Array::iterator normal_itr = osg_normals->begin();
805        for (fitr=faceList.begin();
806            fitr!=faceList.end();
807            ++fitr)
808        {
[10853]809            osg::Vec3f osgNormal( copyLib3dsVec3ToOsgVec3(normals[*fitr]) );
810            if (matrix) osgNormal = osg::Matrix::transform3x3(osgNormal, *matrix);
811            osgNormal.normalize();
812            *(normal_itr++) = osgNormal;
[814]813        }
[799]814        geom->setNormalArray(osg_normals);
815        geom->setNormalBinding(osg::Geometry::BIND_PER_PRIMITIVE);
[8]816    }
[10853]817
[4390]818    osg::Vec4ubArray* osg_colors = new osg::Vec4ubArray(1);
[2339]819    (*osg_colors)[0].set(255,255,255,255);
820    geom->setColorArray(osg_colors);
821    geom->setColorBinding(osg::Geometry::BIND_OVERALL);
822
[10853]823
[814]824    // create primitives
825    int numIndices = faceList.size()*3;
[1166]826    DrawElementsUShort* elements = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES,numIndices);
827    DrawElementsUShort::iterator index_itr = elements->begin();
[8]828
[814]829    for (fitr=faceList.begin();
830        fitr!=faceList.end();
831        ++fitr)
832    {
[10853]833        Lib3dsFace& face = m->faces[*fitr];
[8]834
[10853]835        *(index_itr++) = orig2NewMapping[face.index[0]];
836        *(index_itr++) = orig2NewMapping[face.index[1]];
837        *(index_itr++) = orig2NewMapping[face.index[2]];
[814]838    }
[10853]839
[1186]840    geom->addPrimitiveSet(elements);
[8]841
[5453]842#if 0
[2535]843    osgUtil::TriStripVisitor tsv;
844    tsv.stripify(*geom);
[5453]845#endif
[2535]846
[799]847    return geom;
[8]848}
[445]849
850
[10853]851osg::Texture2D*  ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap *texture,const char* label,bool& transparancy)
[445]852{
853    if (texture && *(texture->name))
854    {
[10076]855        osg::notify(osg::NOTICE)<<"texture->name="<<texture->name<<", _directory="<<_directory<<std::endl;
856
[2449]857        std::string fileName = osgDB::findFileInDirectory(texture->name,_directory,osgDB::CASE_INSENSITIVE);
[10853]858        if (fileName.empty())
[2449]859        {
860            // file not found in .3ds file's directory, so we'll look in the datafile path list.
[3691]861            fileName = osgDB::findDataFile(texture->name,options, osgDB::CASE_INSENSITIVE);
[10853]862            osg::notify(osg::NOTICE)<<"texture->name="<<texture->name<<", _directory="<<_directory<<std::endl;
[2449]863        }
[10076]864
[445]865        if (fileName.empty())
866        {
[10076]867            if (osgDB::containsServerAddress(_directory))
868            {
869                // if 3DS file is loaded from http, just attempt to load texture from same location.
870                fileName = _directory + "/" + texture->name;
871            } else {
872                osg::notify(osg::WARN) << "texture '"<<texture->name<<"' not found"<< std::endl;
873                return NULL;
874            }
[445]875        }
876
877        if (label) osg::notify(osg::DEBUG_INFO) << label;
878        else osg::notify(osg::DEBUG_INFO) << "texture name";
879        osg::notify(osg::DEBUG_INFO) << " '"<<texture->name<<"'"<< std::endl;
880        osg::notify(osg::DEBUG_INFO) << "    texture flag        "<<texture->flags<< std::endl;
[10853]881        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_DECALE       "<<((texture->flags)&LIB3DS_TEXTURE_DECALE)<< std::endl;
882        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_MIRROR       "<<((texture->flags)&LIB3DS_TEXTURE_MIRROR)<< std::endl;
883        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_NEGATE       "<<((texture->flags)&LIB3DS_TEXTURE_NEGATE)<< std::endl;
884        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_NO_TILE      "<<((texture->flags)&LIB3DS_TEXTURE_NO_TILE)<< std::endl;
885        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_SUMMED_AREA  "<<((texture->flags)&LIB3DS_TEXTURE_SUMMED_AREA)<< std::endl;
886        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_ALPHA_SOURCE "<<((texture->flags)&LIB3DS_TEXTURE_ALPHA_SOURCE)<< std::endl;
887        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_TINT         "<<((texture->flags)&LIB3DS_TEXTURE_TINT)<< std::endl;
888        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_IGNORE_ALPHA "<<((texture->flags)&LIB3DS_TEXTURE_IGNORE_ALPHA)<< std::endl;
889        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_RGB_TINT     "<<((texture->flags)&LIB3DS_TEXTURE_RGB_TINT)<< std::endl;
[445]890
[10853]891        bool noTexture = false;
892        if (options)
893        {
894            std::istringstream iss(options->getOptionString());
895            std::string opt;
896            while (iss >> opt)
897            {
898                if (opt == "noTexture")
899                    noTexture = true;
900            }
901        }
902
903        osg::ref_ptr<osg::Image> osg_image = NULL;
904        if(noTexture)
905            osg_image = new osg::Image();
906        else
907            osg_image = osgDB::readRefImageFile(fileName.c_str(), options); //Absolute Path
[9527]908        if (!osg_image)
[445]909        {
910            osg::notify(osg::NOTICE) << "Warning: Cannot create texture "<<texture->name<< std::endl;
911            return NULL;
912        }
[10853]913        if (osg_image->getFileName().empty()) // it should be done in OSG with osgDB::readRefImageFile(fileName.c_str());
914            osg_image->setFileName(fileName);
[1078]915        osg::Texture2D* osg_texture = new osg::Texture2D;
[9527]916        osg_texture->setImage(osg_image.get());
[10853]917        osg_texture->setName(texture->name);
[445]918        // does the texture support transparancy?
[10853]919        transparancy = ((texture->flags)&LIB3DS_TEXTURE_ALPHA_SOURCE)!=0;
[445]920
921        // what is the wrap mode of the texture.
[10853]922        osg::Texture2D::WrapMode wm = ((texture->flags)&LIB3DS_TEXTURE_NO_TILE) ?
[4461]923                osg::Texture2D::CLAMP :
924                osg::Texture2D::REPEAT;
[1078]925        osg_texture->setWrap(osg::Texture2D::WRAP_S,wm);
926        osg_texture->setWrap(osg::Texture2D::WRAP_T,wm);
927        osg_texture->setWrap(osg::Texture2D::WRAP_R,wm);
[445]928                                 // bilinear.
[1078]929        osg_texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR_MIPMAP_NEAREST);
[445]930
931        return osg_texture;
932    }
933    else
934        return NULL;
935}
936
937
[10853]938osg::StateSet* ReaderWriter3DS::ReaderObject::createStateSet(Lib3dsMaterial *mat)
[445]939{
940    if (mat==NULL) return NULL;
941
942    osg::StateSet* stateset = new osg::StateSet;
943
944    osg::Material* material = new osg::Material;
945
946    float transparency = mat->transparency;
947    float alpha = 1.0f-transparency;
948
949    osg::Vec4 ambient(mat->ambient[0],mat->ambient[1],mat->ambient[2],alpha);
950    osg::Vec4 diffuse(mat->diffuse[0],mat->diffuse[1],mat->diffuse[2],alpha);
951    osg::Vec4 specular(mat->specular[0],mat->specular[1],mat->specular[2],alpha);
[2421]952    specular *= mat->shin_strength;
[445]953
954    float shininess = mat->shininess;
[7623]955    material->setName(mat->name);
[445]956    material->setAmbient(osg::Material::FRONT_AND_BACK,ambient);
957    material->setDiffuse(osg::Material::FRONT_AND_BACK,diffuse);
958    material->setSpecular(osg::Material::FRONT_AND_BACK,specular);
[1020]959    material->setShininess(osg::Material::FRONT_AND_BACK,shininess*128.0f);
[445]960
961    stateset->setAttribute(material);
962
963    bool textureTransparancy=false;
[10853]964    osg::Texture2D* texture1_map = createTexture(&(mat->texture1_map),"texture1_map",textureTransparancy);
[445]965    if (texture1_map)
966    {
[845]967        stateset->setTextureAttributeAndModes(0,texture1_map,osg::StateAttribute::ON);
[10853]968
[2339]969        if (!textureTransparancy)
[10853]970        {
[2339]971            // from an email from Eric Hamil, September 30, 2003.
972            // According to the 3DS spec, and other
973            // software (like Max, Lightwave, and Deep Exploration) a 3DS material that has
974            // a non-white diffuse base color and a 100% opaque bitmap texture, will show the
975            // texture with no influence from the base color.
[10853]976
[2339]977            // so we'll override material back to white.
978            // and no longer require the decal hack below...
[2872]979#if 0
[7648]980            // Eric original fallback
[2339]981            osg::Vec4 white(1.0f,1.0f,1.0f,alpha);
982            material->setAmbient(osg::Material::FRONT_AND_BACK,white);
983            material->setDiffuse(osg::Material::FRONT_AND_BACK,white);
984            material->setSpecular(osg::Material::FRONT_AND_BACK,white);
[2872]985#else
[7648]986            // try alternative to avoid saturating with white
[2872]987            // setting white as per OpenGL defaults.
988            material->setAmbient(osg::Material::FRONT_AND_BACK,osg::Vec4(0.2f,0.2f,0.2f,alpha));
989            material->setDiffuse(osg::Material::FRONT_AND_BACK,osg::Vec4(0.8f,0.8f,0.8f,alpha));
990            material->setSpecular(osg::Material::FRONT_AND_BACK,osg::Vec4(0.0f,0.0f,0.0f,alpha));
[10853]991#endif
[445]992        }
[10853]993
994// no longer required...
[2339]995//         bool decal = false;
[10853]996//
[2339]997//         // not sure exactly how to interpret what is best for .3ds
998//         // but the default text env MODULATE doesn't work well, and
999//         // DECAL seems to work better.
1000//         osg::TexEnv* texenv = new osg::TexEnv;
1001//         if (decal)
1002//         {
1003//             texenv->setMode(osg::TexEnv::DECAL);
1004//         }
1005//         else
1006//         {
1007//             texenv->setMode(osg::TexEnv::MODULATE);
1008//         }
1009//        stateset->setTextureAttribute(0,texenv);
[445]1010    }
1011
[10076]1012    if (transparency>0.0f || textureTransparancy)
[445]1013    {
1014        stateset->setMode(GL_BLEND,osg::StateAttribute::ON);
1015        stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1016    }
1017
1018/*
1019    osg::ref_ptr<osg::Texture> texture1_mask = createTexture(&(mat->texture1_mask),"texture1_mask",textureTransparancy);
1020    osg::ref_ptr<osg::Texture> texture2_map = createTexture(&(mat->texture2_map),"texture2_map",textureTransparancy);
1021    osg::ref_ptr<osg::Texture> texture2_mask = createTexture(&(mat->texture2_mask),"texture2_mask",textureTransparancy);
1022    osg::ref_ptr<osg::Texture> opacity_map = createTexture(&(mat->opacity_map),"opacity_map",textureTransparancy);
1023    osg::ref_ptr<osg::Texture> opacity_mask = createTexture(&(mat->opacity_mask),"opacity_mask",textureTransparancy);
1024    osg::ref_ptr<osg::Texture> bump_map = createTexture(&(mat->bump_map),"bump_map",textureTransparancy);
1025    osg::ref_ptr<osg::Texture> bump_mask = createTexture(&(mat->bump_mask),"bump_mask",textureTransparancy);
1026    osg::ref_ptr<osg::Texture> specular_map = createTexture(&(mat->specular_map),"specular_map",textureTransparancy);
1027    osg::ref_ptr<osg::Texture> specular_mask = createTexture(&(mat->specular_mask),"specular_mask",textureTransparancy);
1028    osg::ref_ptr<osg::Texture> shininess_map = createTexture(&(mat->shininess_map),"shininess_map",textureTransparancy);
1029    osg::ref_ptr<osg::Texture> shininess_mask = createTexture(&(mat->shininess_mask),"shininess_mask",textureTransparancy);
1030    osg::ref_ptr<osg::Texture> self_illum_map = createTexture(&(mat->self_illum_map),"self_illum_map",textureTransparancy);
1031    osg::ref_ptr<osg::Texture> self_illum_mask = createTexture(&(mat->self_illum_mask),"self_illum_mask",textureTransparancy);
1032    osg::ref_ptr<osg::Texture> reflection_map = createTexture(&(mat->reflection_map),"reflection_map",textureTransparancy);
1033    osg::ref_ptr<osg::Texture> reflection_mask = createTexture(&(mat->reflection_mask),"reflection_mask",textureTransparancy);
1034*/
1035    return stateset;
1036}
1037
[10853]1038
1039
1040#if ENABLE_3DS_WRITER
1041osgDB::ReaderWriter::WriteResult ReaderWriter3DS::writeNode(const osg::Node& node,const std::string& fileName,const Options* options) const {
1042    std::string ext = osgDB::getLowerCaseFileExtension(fileName);
1043    if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
1044
1045    //osg::notify(osg::WARN) << "!!WARNING!! 3DS write support is incomplete" << std::endl;
1046
1047    bool ok = true;
1048    Lib3dsFile * file3ds = lib3ds_file_new();
1049    if (!file3ds) return WriteResult(WriteResult::ERROR_IN_WRITING_FILE);
1050
1051    try {
1052        osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
1053        local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileName));
1054
1055        if (!createFileObject(node, file3ds, fileName, local_opt)) ok = false;
1056        if (ok && !lib3ds_file_save(file3ds, fileName.c_str())) ok = false;
1057    } catch (...) {
1058        lib3ds_file_free(file3ds);
1059        throw;
1060    }
1061    lib3ds_file_free(file3ds);
1062
1063    return ok ? WriteResult(WriteResult::FILE_SAVED) : WriteResult(WriteResult::ERROR_IN_WRITING_FILE);
1064    //return ok ? WriteResult(WriteResult::FILE_SAVED) : WriteResult(WriteResult::FILE_NOT_HANDLED);
1065}
1066
1067
1068osgDB::ReaderWriter::WriteResult ReaderWriter3DS::writeNode(const osg::Node& node,std::ostream& fout,const Options* options) const {
1069    //osg::notify(osg::WARN) << "!!WARNING!! 3DS write support is incomplete" << std::endl;
1070    std::string optFileName = "";
1071    if (options)
1072    {
1073        optFileName = options->getPluginStringData("STREAM_FILENAME");
1074    }
1075
1076    Lib3dsIo io;
1077    io.self = &fout;
1078    io.seek_func = fileo_seek_func;
1079    io.tell_func = fileo_tell_func;
1080    io.read_func = NULL;
1081    io.write_func = fileo_write_func;
1082    io.log_func = fileio_log_func;
1083   
1084    Lib3dsFile * file3ds = lib3ds_file_new();
1085    bool ok = true;
1086    try {
1087        osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
1088        local_opt->getDatabasePathList().push_front(osgDB::getFilePath(optFileName));
1089
1090        if (!createFileObject(node, file3ds, optFileName, local_opt)) ok = false;
1091        if (ok && !lib3ds_file_write(file3ds, &io)) ok = false;
1092       
1093    } catch (...) {
1094        lib3ds_file_free(file3ds);
1095        throw;
1096    }
1097    lib3ds_file_free(file3ds);
1098
1099    return ok ? WriteResult(WriteResult::FILE_SAVED) : WriteResult(WriteResult::ERROR_IN_WRITING_FILE);
1100    //return ok ? WriteResult(WriteResult::FILE_SAVED) : WriteResult(WriteResult::FILE_NOT_HANDLED);
1101}
1102
1103const std::string getParent(const std::string & pathBad)
1104{
1105    const std::string & path = osgDB::convertFileNameToNativeStyle(pathBad);
1106
1107    std::string parent = "";
1108    std::string tmp = "";
1109    for(std::string::const_iterator itPath = path.begin();; ++itPath)
1110    {
1111        if (!parent.empty())
1112            parent += '\\';
1113        parent += tmp;
1114        tmp.clear();
1115        for(;itPath != path.end() && *itPath != '\\'; ++itPath)
1116            tmp += *itPath;
1117        if (itPath == path.end())
1118            break;
1119    }
1120    return parent;
1121}
1122
1123bool ReaderWriter3DS::createFileObject(const osg::Node& node, Lib3dsFile * file3ds,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const {
1124    WriterNodeVisitor w(file3ds, fileName, options, getParent(node.getName()));
1125    const_cast<osg::Node &>(node).accept(w);                // TODO Remove that ugly const_cast<>. Any idea?
1126    if (!w.suceedLastApply())
1127        return false;
1128    w.writeMaterials();
1129    return true;        //w.good();
1130}
1131#endif    // ENABLE_3DS_WRITER
Note: See TracBrowser for help on using the browser.