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

Revision 11203, 47.2 kB (checked in by robert, 5 years ago)

From Sukender, "I had some conflicts when updating but they seem resolved now. Attached files (four files) are against rev. 11200. Changelog:
- Replaced exceptions with assert() or OSG_NOTIFY
- Replaced osg::notify() with OSG_NOTIFY
- Changed braces and tabs to fit OSG coding convention
- Cleaned a few things in code (names, added deallocations upon error)"

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