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

Revision 11180, 47.1 kB (checked in by robert, 4 years ago)

From Stephan Huber, "attached you'll find a small bugfix for the 3ds-reader. It allows
reading files with relative paths again. (Hard to explain, easy to see
in the diff)"

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