Changeset 10853

Show
Ignore:
Timestamp:
12/04/09 12:31:04 (4 years ago)
Author:
robert
Message:

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

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

Location:
OpenSceneGraph/trunk/src/osgPlugins/3ds
Files:
29 added
41 removed
3 modified

Legend:

Unmodified
Added
Removed
  • OpenSceneGraph/trunk/src/osgPlugins/3ds/AUTHORS

    r8 r10853  
    11J.E. Hoffmann <je-h@gmx.net> 
     2Sukender (Benoit Neil) <suky0001 at free dot fr> 
     3Thibault Caporal <> 
  • OpenSceneGraph/trunk/src/osgPlugins/3ds/CMakeLists.txt

    r9998 r10853  
     1# List of C files to be compiled as C++ (else CMake sets ".c" to be compiled as pure C) 
     2SET(C_FILES 
     3    lib3ds/lib3ds_io.c        # Modified to support OSG endianness 
     4) 
     5 
    16SET(TARGET_SRC 
    27    ReaderWriter3DS.cpp 
    3     atmosphere.cpp 
    4     background.cpp 
    5     camera.cpp 
    6     chunk.cpp 
    7     ease.cpp 
    8     file.cpp 
    9     lib3ds_float.cpp 
    10     light.cpp 
    11     material.cpp 
    12     matrix.cpp 
    13     mesh.cpp 
    14     node.cpp 
    15     quat.cpp 
    16     readwrite.cpp 
    17     shadow.cpp 
    18     tcb.cpp 
    19     tracks.cpp 
    20     vector.cpp 
    21     viewport.cpp 
     8    WriterNodeVisitor.cpp 
     9    WriterCompareTriangle.cpp 
     10 
     11    ${C_FILES} 
     12    lib3ds/lib3ds_atmosphere.c 
     13    lib3ds/lib3ds_background.c 
     14    lib3ds/lib3ds_camera.c 
     15    lib3ds/lib3ds_chunk.c 
     16    lib3ds/lib3ds_chunktable.c 
     17    lib3ds/lib3ds_file.c 
     18    lib3ds/lib3ds_light.c 
     19    lib3ds/lib3ds_material.c 
     20    lib3ds/lib3ds_math.c 
     21    lib3ds/lib3ds_matrix.c 
     22    lib3ds/lib3ds_mesh.c 
     23    lib3ds/lib3ds_node.c 
     24    lib3ds/lib3ds_quat.c 
     25    lib3ds/lib3ds_shadow.c 
     26    lib3ds/lib3ds_track.c 
     27    lib3ds/lib3ds_util.c 
     28    lib3ds/lib3ds_vector.c 
     29    lib3ds/lib3ds_viewport.c 
    2230) 
    2331SET(TARGET_H 
    24     atmosphere.h 
    25     background.h 
    26     camera.h 
    27     chunk.h 
    28     chunktable.h 
    29     config.h 
    30     ease.h 
    31     file.h 
    32     lib3ds_float.h 
    33     light.h 
    34     material.h 
    35     matrix.h 
    36     mesh.h 
    37     node.h 
    38     quat.h 
    39     readwrite.h 
    40     shadow.h 
    41     tcb.h 
    42     tracks.h 
    43     types.h 
    44     vector.h 
    45     viewport.h 
     32    WriterNodeVisitor.h 
     33    WriterCompareTriangle.h 
     34    lib3ds/lib3ds.h 
     35    lib3ds/lib3ds_impl.h 
    4636) 
    4737#### end var setup  ### 
    4838SETUP_PLUGIN(3ds) 
     39ADD_DEFINITIONS( -DLIB3DS_STATIC )        # lib3ds is included, so we need the flag 
     40SET_SOURCE_FILES_PROPERTIES(${C_FILES} PROPERTIES LANGUAGE "CXX")        # Force some files to be compiled as C++ 
  • OpenSceneGraph/trunk/src/osgPlugins/3ds/ReaderWriter3DS.cpp

    r10805 r10853  
     1#define ENABLE_3DS_WRITER      1            // Enables the 3DS writer (the define should be removed when the writer will be stable and tested enough) 
     2 
    13#include <osg/Notify> 
    24#include <osg/Group> 
     
    1921#include <osg/NodeVisitor> 
    2022 
    21  
    22 #include "file.h" 
    23 #include "mesh.h" 
    24 #include "material.h" 
    25 #include "vector.h" 
    26 #include "matrix.h" 
    27 #include "node.h" 
    28 #include "quat.h" 
    29 #include "readwrite.h" 
    30  
     23#ifdef ENABLE_3DS_WRITER 
     24    #include "WriterNodeVisitor.h" 
     25#endif 
     26#include "lib3ds/lib3ds.h" 
    3127#include <stdlib.h> 
    3228#include <string.h> 
     
    3632#include <iostream> 
    3733#include <sstream> 
     34#include <assert.h> 
    3835 
    3936using namespace std; 
    4037using namespace osg; 
    4138 
     39 
     40void copyLib3dsMatrixToOsgMatrix(osg::Matrix& osg_matrix, const Lib3dsMatrix lib3ds_matrix) 
     41{ 
     42    osg_matrix.set( 
     43        lib3ds_matrix[0][0],lib3ds_matrix[0][1],lib3ds_matrix[0][2],lib3ds_matrix[0][3], 
     44        lib3ds_matrix[1][0],lib3ds_matrix[1][1],lib3ds_matrix[1][2],lib3ds_matrix[1][3], 
     45        lib3ds_matrix[2][0],lib3ds_matrix[2][1],lib3ds_matrix[2][2],lib3ds_matrix[2][3], 
     46        lib3ds_matrix[3][0],lib3ds_matrix[3][1],lib3ds_matrix[3][2],lib3ds_matrix[3][3]); 
     47} 
     48 
     49osg::Matrix copyLib3dsMatrixToOsgMatrix(const Lib3dsMatrix mat) 
     50{ 
     51    osg::Matrix osgMatrix; 
     52    copyLib3dsMatrixToOsgMatrix(osgMatrix, mat); 
     53    return osgMatrix; 
     54} 
     55 
     56void copyLib3dsVec3ToOsgVec3(osg::Vec3f osgVec, const float vertices[3]) { 
     57    return osgVec.set(vertices[0], vertices[1], vertices[2]); 
     58} 
     59 
     60osg::Vec3f copyLib3dsVec3ToOsgVec3(const float vertices[3]) { 
     61    return osg::Vec3f(vertices[0], vertices[1], vertices[2]); 
     62} 
     63 
     64osg::Quat copyLib3dsQuatToOsgQuat(const float quat[4]) { 
     65    return osg::Quat(quat[0], quat[1], quat[2], quat[3]); 
     66} 
     67 
     68 
     69 
    4270class PrintVisitor : public NodeVisitor 
    4371{ 
    44  
    45    public: 
    46     
    47         PrintVisitor(std::ostream& out): 
    48             NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN), 
    49             _out(out) 
    50         { 
    51             _indent = 0; 
    52             _step = 4; 
    53         } 
    54          
    55         inline void moveIn() { _indent += _step; } 
    56         inline void moveOut() { _indent -= _step; } 
    57         inline void writeIndent()  
    58         { 
    59             for(int i=0;i<_indent;++i) _out << " "; 
    60         } 
    61                  
    62         virtual void apply(Node& node) 
    63         { 
    64             moveIn(); 
    65             writeIndent(); _out << node.className() <<std::endl; 
    66             traverse(node); 
    67             moveOut(); 
    68         } 
    69  
    70         virtual void apply(Geode& node)         { apply((Node&)node); } 
    71         virtual void apply(Billboard& node)     { apply((Geode&)node); } 
    72         virtual void apply(LightSource& node)   { apply((Group&)node); } 
    73         virtual void apply(ClipNode& node)      { apply((Group&)node); } 
    74          
    75         virtual void apply(Group& node)         { apply((Node&)node); } 
    76         virtual void apply(Transform& node)     { apply((Group&)node); } 
    77         virtual void apply(Projection& node)    { apply((Group&)node); } 
    78         virtual void apply(Switch& node)        { apply((Group&)node); } 
    79         virtual void apply(LOD& node)           { apply((Group&)node); } 
    80  
    81    protected: 
    82  
    83         PrintVisitor& operator = (const PrintVisitor&) { return *this; } 
    84  
    85         std::ostream& _out; 
    86         int _indent; 
    87         int _step; 
     72public: 
     73    PrintVisitor(std::ostream& out): 
     74      NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN), 
     75          _out(out) 
     76      { 
     77          _indent = 0; 
     78          _step = 4; 
     79      } 
     80 
     81      inline void moveIn() { _indent += _step; } 
     82      inline void moveOut() { _indent -= _step; } 
     83      inline void writeIndent() 
     84      { 
     85          for(int i=0;i<_indent;++i) _out << " "; 
     86      } 
     87 
     88      virtual void apply(Node& node) 
     89      { 
     90          moveIn(); 
     91          writeIndent(); _out << node.className() <<std::endl; 
     92          traverse(node); 
     93          moveOut(); 
     94      } 
     95 
     96      virtual void apply(Geode& node)         { apply((Node&)node); } 
     97      virtual void apply(Billboard& node)     { apply((Geode&)node); } 
     98      virtual void apply(LightSource& node)   { apply((Group&)node); } 
     99      virtual void apply(ClipNode& node)      { apply((Group&)node); } 
     100 
     101      virtual void apply(Group& node)         { apply((Node&)node); } 
     102      virtual void apply(Transform& node)     { apply((Group&)node); } 
     103      virtual void apply(Projection& node)    { apply((Group&)node); } 
     104      virtual void apply(Switch& node)        { apply((Group&)node); } 
     105      virtual void apply(LOD& node)           { apply((Group&)node); } 
     106 
     107protected: 
     108 
     109    PrintVisitor& operator = (const PrintVisitor&) { return *this; } 
     110 
     111    std::ostream& _out; 
     112    int _indent; 
     113    int _step; 
    88114}; 
    89115 
     116/// Possible options: 
     117///             - noMatrixTransforms: set the plugin to apply matrices into the mesh vertices ("old behaviour") instead of restoring them ("new behaviour"). You may use this option to avoid a few rounding errors. 
     118///             - checkForEspilonIdentityMatrices: if noMatrixTransforms is \b not set, then consider "almost identity" matrices to be identity ones (in case of rounding errors). 
     119///             - restoreMatrixTransformsNoMeshes: makes an exception to the behaviour when 'noMatrixTransforms' is \b not set for mesh instances. When a mesh instance has a transform on it, the reader creates a MatrixTransform above the Geode. If you don't want the hierarchy to be modified, then you can use this option to merge the transform into vertices. 
    90120class ReaderWriter3DS : public osgDB::ReaderWriter 
    91121{ 
     122public: 
     123 
     124    ReaderWriter3DS(); 
     125 
     126    virtual const char* className() const { return "3DS Auto Studio Reader/Writer"; } 
     127 
     128    virtual ReadResult readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const; 
     129    virtual ReadResult readNode(std::istream& fin, const Options* options) const; 
     130#if ENABLE_3DS_WRITER 
     131    virtual WriteResult writeNode(const osg::Node& /*node*/,const std::string& /*fileName*/,const Options* =NULL) const; 
     132    virtual WriteResult writeNode(const osg::Node& /*node*/,std::ostream& /*fout*/,const Options* =NULL) const; 
     133#endif 
     134 
     135protected: 
     136    ReadResult constructFrom3dsFile(Lib3dsFile *f,const std::string& filename, const Options* options) const; 
     137 
     138#if ENABLE_3DS_WRITER 
     139    bool createFileObject(const osg::Node& node, Lib3dsFile * file3ds,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const; 
     140#endif 
     141 
     142    class ReaderObject 
     143    { 
    92144    public: 
    93  
    94         ReaderWriter3DS(); 
    95  
    96         virtual const char* className() const { return "3DS Auto Studio Reader"; } 
    97  
    98         virtual ReadResult readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const; 
    99         virtual ReadResult readNode(std::istream& fin, const Options* options) const; 
    100  
    101     protected: 
    102         ReadResult constructFrom3dsFile(Lib3dsFile *f,const std::string& filename, const Options* options) const; 
    103  
    104  
    105         class ReaderObject 
    106         { 
    107         public: 
    108             ReaderObject(); 
    109          
    110             typedef std::map<std::string,osg::StateSet*> StateSetMap; 
    111             typedef std::vector<int> FaceList; 
    112             typedef std::map<std::string,osg::StateSet*> GeoStateMap; 
    113  
    114             osg::Texture2D* createTexture(Lib3dsTextureMap *texture,const char* label,bool& transparancy, const osgDB::ReaderWriter::Options* options); 
    115             osg::StateSet* createStateSet(Lib3dsMaterial *materials, const osgDB::ReaderWriter::Options* options); 
    116             osg::Drawable* createDrawable(Lib3dsMesh *meshes,FaceList& faceList, Lib3dsMatrix* matrix); 
    117  
    118             std::string _directory; 
    119             bool _useSmoothingGroups; 
    120             bool _usePerVertexNormals; 
    121  
    122             // MIKEC 
    123             osg::Node* processMesh(StateSetMap& drawStateMap,osg::Group* parent,Lib3dsMesh* mesh, Lib3dsMatrix* matrix); 
    124             osg::Node* processNode(StateSetMap drawStateMap,Lib3dsFile *f,Lib3dsNode *node); 
    125         }; 
     145        ReaderObject(const osgDB::ReaderWriter::Options* options); 
     146 
     147        typedef std::vector<osg::StateSet*> StateSetMap; 
     148        typedef std::vector<int> FaceList; 
     149        typedef std::map<std::string,osg::StateSet*> GeoStateMap; 
     150 
     151        osg::Texture2D* createTexture(Lib3dsTextureMap *texture,const char* label,bool& transparancy); 
     152        osg::StateSet* createStateSet(Lib3dsMaterial *materials); 
     153        osg::Drawable* createDrawable(Lib3dsMesh *meshes,FaceList& faceList, const osg::Matrix * matrix); 
     154 
     155        std::string _directory; 
     156        bool _useSmoothingGroups; 
     157        bool _usePerVertexNormals; 
     158 
     159        // MIKEC 
     160        osg::Node* processMesh(StateSetMap& drawStateMap,osg::Group* parent,Lib3dsMesh* mesh, const osg::Matrix * matrix); 
     161        osg::Node* processNode(StateSetMap drawStateMap,Lib3dsFile *f,Lib3dsNode *node); 
     162    private: 
     163        const osgDB::ReaderWriter::Options* options; 
     164        bool noMatrixTransforms;            ///< Should the plugin apply matrices into the mesh vertices ("old behaviour"), instead of restoring matrices ("new behaviour")? 
     165        bool checkForEspilonIdentityMatrices; 
     166        bool restoreMatrixTransformsNoMeshes; 
     167        typedef std::map<unsigned int,FaceList> SmoothingFaceMap; 
     168        void addDrawableFromFace(osg::Geode * geode, FaceList & faceList, Lib3dsMesh * mesh, const osg::Matrix * matrix, osg::StateSet * stateSet); 
     169    }; 
    126170}; 
    127171 
     
    133177{ 
    134178    supportsExtension("3ds","3D Studio model format"); 
    135  
     179    supportsOption("OutputTextureFiles","Write out the texture images to file"); 
    136180    setByteOrder(); 
    137181 
     
    157201} 
    158202 
    159 ReaderWriter3DS::ReaderObject::ReaderObject() 
    160 { 
    161     _useSmoothingGroups = true; 
    162     _usePerVertexNormals = true; 
     203ReaderWriter3DS::ReaderObject::ReaderObject(const osgDB::ReaderWriter::Options* options) : 
     204    _useSmoothingGroups(true), 
     205    _usePerVertexNormals(true), 
     206    options(options), 
     207    noMatrixTransforms(false), 
     208    checkForEspilonIdentityMatrices(false), 
     209    restoreMatrixTransformsNoMeshes(false) 
     210{ 
     211    std::istringstream iss(options->getOptionString()); 
     212    std::string opt;  
     213    while (iss >> opt)  
     214    { 
     215        if (opt == "noMatrixTransforms") 
     216            noMatrixTransforms = true; 
     217        if (opt == "checkForEspilonIdentityMatrices") 
     218            checkForEspilonIdentityMatrices = true; 
     219        if (opt == "restoreMatrixTransformsNoMeshes") 
     220            restoreMatrixTransformsNoMeshes = true; 
     221    } 
    163222} 
    164223 
     
    172231void print(Lib3dsMesh *mesh,int level); 
    173232void print(Lib3dsUserData *user,int level); 
    174 void print(Lib3dsNodeData *user,int level); 
    175 void print(Lib3dsObjectData *object,int level); 
     233void print(Lib3dsMeshInstanceNode *object,int level); 
    176234void print(Lib3dsNode *node, int level); 
    177235 
     
    198256    } 
    199257} 
    200 void print(Lib3dsNodeData *node,int level) { 
    201     if (node) { 
    202         pad(level); cout << "node data:" << endl; 
    203         // nodedata->object is a union of many types 
    204         print((Lib3dsObjectData *)&node->object,level+1); 
    205     } else { 
    206         pad(level); cout << "no user data" << endl; 
    207     } 
    208 } 
    209 void print(Lib3dsObjectData *object,int level) { 
     258 
     259void print(Lib3dsMeshInstanceNode *object,int level) { 
    210260    if (object) { 
    211         pad(level); cout << "objectdata instance [" << object->instance << "]" << endl; 
     261        pad(level); cout << "objectdata instance [" << object->instance_name << "]" << endl; 
    212262        pad(level); cout << "pivot     " << object->pivot[0] <<" "<< object->pivot[1] <<" "<< object->pivot[2] << endl; 
    213263        pad(level); cout << "pos       " << object->pos[0] <<" "<< object->pos[1] <<" "<< object->pos[2] << endl; 
     
    220270 
    221271void print(Lib3dsNode *node, int level) { 
    222      
     272 
    223273    pad(level); cout << "node name [" << node->name << "]" << endl; 
    224     pad(level); cout << "node id    " << node->node_id << endl; 
    225     pad(level); cout << "node parent id " << node->parent_id << endl; 
     274    pad(level); cout << "node id    " << node->user_id << endl; 
     275    pad(level); cout << "node parent id " << (node->parent ? static_cast<int>(node->parent->user_id) : -1) << endl; 
    226276    pad(level); cout << "node matrix:" << endl; 
    227277    print(node->matrix,level+1); 
    228     print(&node->data,level); 
    229     print(&node->user,level); 
    230      
     278 
     279    if (node->type == LIB3DS_NODE_MESH_INSTANCE) { 
     280        pad(level); cout << "mesh instance data:" << endl; 
     281        print(reinterpret_cast<Lib3dsMeshInstanceNode *>(node),level+1); 
     282    } else { 
     283        pad(level); cout << "node is not a mesh instance (not handled)" << endl; 
     284    } 
     285 
     286    print(&node->user_ptr,level); 
    231287 
    232288    for(Lib3dsNode *child=node->childs; child; child=child->next) { 
     
    236292} 
    237293 
    238 void copyLib3dsMatrixToOsgMatrix(osg::Matrix& osg_matrix, const Lib3dsMatrix lib3ds_matrix) 
    239 { 
    240     osg_matrix.set( 
    241         lib3ds_matrix[0][0],lib3ds_matrix[0][1],lib3ds_matrix[0][2],lib3ds_matrix[0][3], 
    242         lib3ds_matrix[1][0],lib3ds_matrix[1][1],lib3ds_matrix[1][2],lib3ds_matrix[1][3], 
    243         lib3ds_matrix[2][0],lib3ds_matrix[2][1],lib3ds_matrix[2][2],lib3ds_matrix[2][3], 
    244         lib3ds_matrix[3][0],lib3ds_matrix[3][1],lib3ds_matrix[3][2],lib3ds_matrix[3][3]);  
    245 } 
     294void ReaderWriter3DS::ReaderObject::addDrawableFromFace(osg::Geode * geode, FaceList & faceList, 
     295                                                        Lib3dsMesh * mesh, 
     296                                                        const osg::Matrix * matrix, 
     297                                                        osg::StateSet * stateSet) 
     298{ 
     299    if (_useSmoothingGroups) 
     300    { 
     301        SmoothingFaceMap smoothingFaceMap; 
     302        for (FaceList::iterator flitr=faceList.begin(); 
     303            flitr!=faceList.end(); 
     304            ++flitr) 
     305        { 
     306            smoothingFaceMap[mesh->faces[*flitr].smoothing_group].push_back(*flitr); 
     307        } 
     308 
     309        for(SmoothingFaceMap::iterator sitr=smoothingFaceMap.begin(); 
     310            sitr!=smoothingFaceMap.end(); 
     311            ++sitr) 
     312        { 
     313            // each smoothing group to have its own geom 
     314            // to ensure the vertices on adjacent groups 
     315            // don't get shared. 
     316            FaceList& smoothFaceMap = sitr->second; 
     317            osg::ref_ptr<osg::Drawable> drawable = createDrawable(mesh,smoothFaceMap,matrix); 
     318            if (drawable) 
     319            { 
     320                if (stateSet) 
     321                    drawable->setStateSet(stateSet); 
     322                geode->addDrawable(drawable); 
     323            } 
     324        } 
     325    } 
     326    else // ignore smoothing groups. 
     327    { 
     328        osg::ref_ptr<osg::Drawable> drawable = createDrawable(mesh,faceList,matrix); 
     329        if (drawable) 
     330        { 
     331            if (stateSet) 
     332                drawable->setStateSet(stateSet); 
     333            geode->addDrawable(drawable); 
     334        } 
     335    } 
     336} 
     337 
    246338 
    247339// Transforms points by matrix if 'matrix' is not NULL 
    248340// Creates a Geode and Geometry (as parent,child) and adds the Geode to 'parent' parameter iff 'parent' is non-NULL 
    249341// Returns ptr to the Geode 
    250 osg::Node* ReaderWriter3DS::ReaderObject::processMesh(StateSetMap& drawStateMap,osg::Group* parent,Lib3dsMesh* mesh, Lib3dsMatrix* matrix) { 
    251     typedef std::vector<int> FaceList; 
    252     typedef std::map<std::string,FaceList> MaterialFaceMap; 
     342osg::Node* ReaderWriter3DS::ReaderObject::processMesh(StateSetMap& drawStateMap,osg::Group* parent,Lib3dsMesh* mesh, const osg::Matrix * matrix) { 
     343    typedef std::vector<FaceList> MaterialFaceMap; 
    253344    MaterialFaceMap materialFaceMap; 
    254     for (unsigned int i=0; i<mesh->faces; ++i) 
    255     { 
    256         materialFaceMap[mesh->faceL[i].material].push_back(i); 
    257     } 
    258  
    259     if (materialFaceMap.empty()) 
     345    unsigned int numMaterials = drawStateMap.size(); 
     346    materialFaceMap.insert(materialFaceMap.begin(), numMaterials, FaceList());        // Setup the map 
     347    FaceList defaultMaterialFaceList; 
     348    for (unsigned int i=0; i<mesh->nfaces; ++i) 
     349    { 
     350        if (mesh->faces[i].material>=0) { 
     351            //std::stringstream materialStr; 
     352            //materialStr << mesh->faces[i].material; 
     353            //materialFaceMap[materialStr.str()].push_back(i); 
     354            materialFaceMap[mesh->faces[i].material].push_back(i); 
     355        } 
     356        else 
     357            defaultMaterialFaceList.push_back(i); 
     358    } 
     359    if (materialFaceMap.empty() && defaultMaterialFaceList.empty()) 
    260360    { 
    261361        osg::notify(osg::NOTICE)<<"Warning : no triangles assigned to mesh '"<<mesh->name<<"'"<< std::endl; 
     362        //osg::notify(osg::INFO) << "No material assigned to mesh '" << mesh->name << "'" << std::endl; 
    262363        return NULL; 
    263364    } 
    264365    else 
    265366    { 
    266  
    267367        osg::Geode* geode = new osg::Geode; 
    268368        geode->setName(mesh->name); 
    269  
    270         for(MaterialFaceMap::iterator itr=materialFaceMap.begin(); 
    271             itr!=materialFaceMap.end(); 
    272             ++itr) 
    273         { 
    274             FaceList& faceList = itr->second; 
    275              
    276             if (_useSmoothingGroups) 
    277             { 
    278  
    279                 typedef std::map<int,FaceList> SmoothingFaceMap; 
    280                 SmoothingFaceMap smoothingFaceMap; 
    281                 for (FaceList::iterator flitr=faceList.begin(); 
    282                      flitr!=faceList.end(); 
    283                      ++flitr) 
    284                 { 
    285                     smoothingFaceMap[mesh->faceL[*flitr].smoothing].push_back(*flitr); 
    286                 } 
    287  
    288                 for(SmoothingFaceMap::iterator sitr=smoothingFaceMap.begin(); 
    289                     sitr!=smoothingFaceMap.end(); 
    290                     ++sitr) 
    291                 { 
    292                     // each smoothing group to have its own geom  
    293                     // to ensure the vertices on adjacent groups 
    294                     // don't get shared. 
    295                     FaceList& smoothFaceMap = sitr->second; 
    296  
    297                     osg::Drawable* drawable = createDrawable(mesh,smoothFaceMap,matrix); 
    298                     if (drawable) 
    299                     { 
    300                         drawable->setStateSet(drawStateMap[itr->first]); 
    301                         geode->addDrawable(drawable); 
    302                     } 
    303                 } 
    304             } 
    305             else // ignore smoothing groups. 
    306             { 
    307                 osg::Drawable* drawable = createDrawable(mesh,faceList,matrix); 
    308                 if (drawable) 
    309                 { 
    310                     drawable->setStateSet(drawStateMap[itr->first]); 
    311                     geode->addDrawable(drawable); 
    312                 } 
    313             } 
    314         } 
    315  
     369        if (!defaultMaterialFaceList.empty()) 
     370        { 
     371            addDrawableFromFace(geode, defaultMaterialFaceList, mesh, matrix, NULL); 
     372        } 
     373        for(unsigned int imat=0; imat<numMaterials; ++imat) 
     374        { 
     375            addDrawableFromFace(geode, materialFaceMap[imat], mesh, matrix, drawStateMap[imat]); 
     376        } 
    316377        if (parent) parent->addChild(geode); 
    317378        return geode; 
    318379    } 
     380} 
     381 
     382 
     383/// Returns true if a matrix is 'almost' identity, meaning that the difference between each value and the corresponding identity value is less than an epsilon value. 
     384bool isIdentityEquivalent(const osg::Matrix & mat, osg::Matrix::value_type epsilon=1e-6) 
     385{ 
     386    return osg::equivalent(mat(0,0), 1, epsilon) && osg::equivalent(mat(0,1), 0, epsilon) && osg::equivalent(mat(0,2), 0, epsilon) &&  osg::equivalent(mat(0,3), 0, epsilon) && 
     387           osg::equivalent(mat(1,0), 0, epsilon) && osg::equivalent(mat(1,1), 1, epsilon) && osg::equivalent(mat(1,2), 0, epsilon) &&  osg::equivalent(mat(1,3), 0, epsilon) && 
     388           osg::equivalent(mat(2,0), 0, epsilon) && osg::equivalent(mat(2,1), 0, epsilon) && osg::equivalent(mat(2,2), 1, epsilon) &&  osg::equivalent(mat(2,3), 0, epsilon) && 
     389           osg::equivalent(mat(3,0), 0, epsilon) && osg::equivalent(mat(3,1), 0, epsilon) && osg::equivalent(mat(3,2), 0, epsilon) &&  osg::equivalent(mat(3,3), 1, epsilon); 
    319390} 
    320391 
     
    332403    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. 
    333404 
    334   */ 
    335 osg::Node* ReaderWriter3DS::ReaderObject::processNode(StateSetMap drawStateMap,Lib3dsFile *f,Lib3dsNode *node) { 
    336      
    337     osg::Group* group=NULL;// created on demand if we find we have children to group together 
    338      
    339  
    340     // Handle all children of this node for hierarchical assemblies 
    341     Lib3dsNode *p; 
    342     for (p=node->childs; p!=0; p=p->next) { 
    343         if (!group) { 
    344             group =new osg::Group; 
    345             if (strcmp(node->name, "$$$DUMMY") == 0) { 
    346                 group->setName(node->data.object.instance); 
     405*/ 
     406osg::Node* ReaderWriter3DS::ReaderObject::processNode(StateSetMap drawStateMap,Lib3dsFile *f,Lib3dsNode *node) 
     407{ 
     408    // Get mesh 
     409    Lib3dsMeshInstanceNode * object = (node->type == LIB3DS_NODE_MESH_INSTANCE) ? reinterpret_cast<Lib3dsMeshInstanceNode *>(node) : NULL; 
     410    Lib3dsMesh * mesh = lib3ds_file_mesh_for_node(f,node); 
     411    assert(!(mesh && !object));         // Node must be a LIB3DS_NODE_MESH_INSTANCE if a mesh exists 
     412 
     413    // Retreive LOCAL transform 
     414    static const osg::Matrix::value_type MATRIX_EPSILON = 1e-10; 
     415    osg::Matrix osgNodeMatrix( copyLib3dsMatrixToOsgMatrix(node->matrix) ); 
     416 
     417    if (node->parent) 
     418    { 
     419        // Matrices evaluated by lib3DS are multiplied by parents' ones 
     420        osgNodeMatrix *= osg::Matrix::inverse( copyLib3dsMatrixToOsgMatrix(node->parent->matrix) ); 
     421    } 
     422 
     423    // Test if we should create an intermediate Group (or MatrixTransform) and which matrix we should apply to the vertices 
     424    osg::Group* group = NULL; 
     425 
     426    // Get pivot point 
     427    osg::Vec3 pivot( object ? copyLib3dsVec3ToOsgVec3(object->pivot) : osg::Vec3() ); 
     428    bool pivoted = pivot.x()!=0 || pivot.y()!=0 || pivot.z()!=0; 
     429 
     430    osg::Matrix meshMat; 
     431    if (mesh) { 
     432         if (!noMatrixTransforms) { 
     433            // There can be a transform directly on a mesh instance (= as if a osg::MatrixTransform and a osg::Geode were merged together) in object->pos/rot/scl 
     434            if (!pivoted) { 
     435                                meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)); 
    347436            } else { 
    348                 group->setName(node->name); 
     437                meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * osg::Matrix::translate(-pivot); 
    349438            } 
    350439        } 
    351         group->addChild(processNode(drawStateMap,f,p)); 
    352     } 
    353      
    354     // MIKEC: possible BUG - 3ds files APPEAR to enforce unqiue names, so this is OK, but I am not 100% sure 
    355     // failed to find any alternative way to do it in lib3ds though, and lib3ds player.c application uses this method 
    356     Lib3dsMesh *mesh=lib3ds_file_mesh_by_name(f,node->name); 
     440        else { 
     441                        if (pivoted) { 
     442                                meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * osg::Matrix::translate(-pivot) * copyLib3dsMatrixToOsgMatrix(node->matrix); 
     443                        } else { 
     444                                meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * copyLib3dsMatrixToOsgMatrix(node->matrix); 
     445                        } 
     446                        osgNodeMatrix = osg::Matrix::identity();                // Not sure it's useful, but it's harmless ;) 
     447        } 
     448    } 
     449 
     450    bool isOsgNodeMatrixIdentity = false; 
     451    if (osgNodeMatrix.isIdentity() || (checkForEspilonIdentityMatrices && isIdentityEquivalent(osgNodeMatrix, MATRIX_EPSILON))) { 
     452        isOsgNodeMatrixIdentity = true; 
     453    } 
     454 
     455 
     456    if (node->childs != NULL || pivoted || (!isOsgNodeMatrixIdentity && !noMatrixTransforms)) { 
     457        if (isOsgNodeMatrixIdentity || noMatrixTransforms) { 
     458            group = new osg::Group; 
     459        } else { 
     460            group = new osg::MatrixTransform(osgNodeMatrix); 
     461        } 
     462    } 
     463 
     464    if (group) { 
     465        if (strcmp(node->name, "$$$DUMMY") == 0)  
     466        { 
     467            if (node->type == LIB3DS_NODE_MESH_INSTANCE) 
     468                group->setName(reinterpret_cast<Lib3dsMeshInstanceNode *>(node)->instance_name);  
     469        } 
     470        else 
     471            group->setName(node->name); 
     472 
     473        // Handle all children of this node for hierarchical assemblies 
     474        for (Lib3dsNode *p=node->childs; p!=NULL; p=p->next) { 
     475            group->addChild(processNode(drawStateMap,f,p)); 
     476        } 
     477    } else { 
     478        assert(node->childs == NULL);           // Else we must have a group to put childs into 
     479    } 
     480 
     481    // Handle mesh 
    357482    if (mesh) { 
    358         Lib3dsObjectData* object=&node->data.object; 
    359         Lib3dsMatrix mesh_inverse; 
    360         osg::Matrix osgmatrix; 
    361          
    362         lib3ds_matrix_copy(mesh_inverse,mesh->matrix);  
    363         lib3ds_matrix_inv(mesh_inverse); 
    364          
    365         Lib3dsMatrix M,N; 
    366         lib3ds_matrix_identity(M); 
    367         lib3ds_matrix_identity(N); 
    368         lib3ds_matrix_copy(M,node->matrix); 
    369         N[3][0]=-object->pivot[0]; 
    370         N[3][1]=-object->pivot[1]; 
    371         N[3][2]=-object->pivot[2]; 
    372  
    373         bool pivoted=false; 
    374         if ( (object->pivot[0]!=0.0) || (object->pivot[1]!=0.0) || (object->pivot[2]!=0.0) ) { 
    375             pivoted=true; // there is a pivot point, so we must use it 
    376         } 
    377  
    378         /*cout<<"M"<<node->name<<endl; 
    379         print(M,0); 
    380         cout<<"N"<<endl; 
    381         print(N,0);*/ 
    382  
    383         if (pivoted) { 
    384             // Transform object's pivot point to the world origin 
    385             osg::MatrixTransform* T=new osg::MatrixTransform; 
    386             copyLib3dsMatrixToOsgMatrix(osgmatrix, N); 
    387             T->setMatrix(osgmatrix); 
    388             T->setName("3DSPIVOTPOINT: Translate pivotpoint to (world) origin"); 
    389             //cout<<"Translation for "<<node->name<<" is "<<osgmatrix<<endl; 
    390  
    391             // rotate about "origin" (after the transform this is the world origin) 
    392             // BUG this matrix also contains the translation to the pivot point - we should plit that out (maybe) 
    393             osg::MatrixTransform* R=new osg::MatrixTransform; 
    394             copyLib3dsMatrixToOsgMatrix(osgmatrix, M); 
    395             R->setMatrix(osgmatrix); 
    396             R->setName("3DSPIVOTPOINT: Rotate"); 
    397                  
    398             /* 
    399             cout<<"Rotation for "<<node->name<<" is "<<osgmatrix<<endl; 
    400             osg::Quat quat; 
    401             quat.set(osgmatrix); 
    402             osg::Vec3 axis; 
    403             float angle; 
    404             quat.getRotate(angle,axis); 
    405             cout<<"which is "<<osg::RadiansToDegrees(angle)<<" degrees around "<<axis<<endl; 
    406             */ 
    407             /* 
    408             printf("%s---------------\n",node->name); 
    409             printf("mesh matrix :\n");         print(mesh->matrix,1); 
    410             printf("mesh inverse:\n");         print(mesh_inverse,1); 
    411             printf("node matrix :\n");         print(matrix,1); 
    412             printf("pivot=%f,%f,%f pos=%f,%f,%f\n",object->pivot[0],object->pivot[1],object->pivot[2],object->pos[0],object->pos[1],object->pos[2]); 
    413             */ 
    414  
    415             if (group) { 
    416                 // Always in reverse order... 
    417                 group->addChild(R);  
    418                 R->addChild(T); 
    419                 processMesh(drawStateMap,T,mesh,&mesh_inverse); // creates geometry under modifier node 
    420                 return group; 
    421             } else { 
    422                 // We are a pivoted node with no children 
    423                 R->addChild(T); 
    424                 processMesh(drawStateMap,T,mesh,&mesh_inverse); // creates geometry under modifier node 
    425                 return R; 
    426             } 
     483        osg::Matrix * meshAppliedMatPtr = NULL; 
     484        if (!meshMat.isIdentity() && !(checkForEspilonIdentityMatrices && isIdentityEquivalent(meshMat, MATRIX_EPSILON))) { 
     485            meshAppliedMatPtr = &meshMat; 
     486        } 
     487 
     488        if(group) { 
     489            // add our geometry to group (where our children already are) 
     490            // creates geometry under modifier node 
     491            processMesh(drawStateMap,group,mesh,meshAppliedMatPtr); 
     492            return group; 
    427493        } else { 
    428             if(group) { 
    429                 // add our geometry to group (where our children already are) 
    430                 processMesh(drawStateMap,group,mesh,NULL); // creates geometry under modifier node 
    431                 return group; 
    432             } else { 
    433                 // didnt use group for children 
    434                 // return a ptr directly to the Geode for this mesh 
    435                 return processMesh(drawStateMap,NULL,mesh,NULL);  
    436             }     
     494            // didnt use group for children 
     495            // return a ptr directly to the Geode for this mesh 
     496            return processMesh(drawStateMap,NULL,mesh,meshAppliedMatPtr); 
    437497        } 
    438498 
     
    444504} 
    445505 
     506 
     507static long filei_seek_func(void *self, long offset, Lib3dsIoSeek origin) { 
     508    std::istream *f = reinterpret_cast<std::istream*>(self); 
     509    ios_base::seekdir o = ios_base::beg; 
     510    if (origin == LIB3DS_SEEK_CUR) o = ios_base::cur; 
     511    else if (origin == LIB3DS_SEEK_END) o = ios_base::end; 
     512 
     513    f->seekg(offset, o); 
     514    return f->fail() ? -1 : 0; 
     515} 
     516 
     517#if ENABLE_3DS_WRITER 
     518static long fileo_seek_func(void *self, long offset, Lib3dsIoSeek origin) { 
     519    std::ostream *f = reinterpret_cast<std::ostream*>(self); 
     520    ios_base::seekdir o = ios_base::beg; 
     521    if (origin == LIB3DS_SEEK_CUR) o = ios_base::cur; 
     522    else if (origin == LIB3DS_SEEK_END) o = ios_base::end; 
     523 
     524    f->seekp(offset, o); 
     525    return f->fail() ? -1 : 0; 
     526} 
     527#endif 
     528 
     529static long filei_tell_func(void *self) { 
     530    std::istream *f = reinterpret_cast<std::istream*>(self); 
     531    return f->tellg(); 
     532} 
     533 
     534#if ENABLE_3DS_WRITER 
     535static long fileo_tell_func(void *self) { 
     536    std::ostream *f = reinterpret_cast<std::ostream*>(self); 
     537    return f->tellp(); 
     538} 
     539#endif 
     540 
     541 
     542static size_t filei_read_func(void *self, void *buffer, size_t size) { 
     543    std::istream *f = reinterpret_cast<std::istream*>(self); 
     544    f->read(reinterpret_cast<char*>(buffer), size); 
     545    return f->gcount(); 
     546} 
     547 
     548#if ENABLE_3DS_WRITER 
     549static size_t fileo_write_func(void *self, const void *buffer, size_t size) { 
     550    std::ostream *f = reinterpret_cast<std::ostream*>(self); 
     551    f->write(static_cast<const char*>(buffer), size); 
     552    return f->fail() ? 0 : size; 
     553} 
     554#endif 
     555 
     556static void fileio_log_func(void *self, Lib3dsLogLevel level, int indent, const char *msg) 
     557{ 
     558    osg::NotifySeverity l = osg::INFO; 
     559    if (level == LIB3DS_LOG_ERROR) l = osg::FATAL; 
     560    else if (level == LIB3DS_LOG_WARN) l = osg::WARN; 
     561    else if (level == LIB3DS_LOG_INFO) l = osg::INFO; 
     562    else if (level == LIB3DS_LOG_DEBUG) l = osg::DEBUG_INFO; 
     563    osg::notify(l) << msg << std::endl; 
     564} 
     565 
     566 
    446567osgDB::ReaderWriter::ReadResult ReaderWriter3DS::readNode(std::istream& fin,  const osgDB::ReaderWriter::Options* options) const 
    447568{ 
     
    452573    { 
    453574        optFileName = options->getPluginStringData("STREAM_FILENAME"); 
    454     } 
    455  
    456     Lib3dsFile *f = lib3ds_stream_load((iostream *) &fin); 
    457     if (f) 
    458     { 
    459         result = constructFrom3dsFile(f,optFileName,options); 
    460         lib3ds_file_free(f); 
     575        if (optFileName.empty()) optFileName = options->getPluginStringData("filename"); 
     576    } 
     577 
     578    // Prepare io structure to tell how to read the stream 
     579    Lib3dsIo io; 
     580    io.self = &fin; 
     581    io.seek_func = filei_seek_func; 
     582    io.tell_func = filei_tell_func; 
     583    io.read_func = filei_read_func; 
     584    io.write_func = NULL; 
     585    io.log_func = fileio_log_func; 
     586 
     587    Lib3dsFile * file3ds = lib3ds_file_new(); 
     588    if (lib3ds_file_read(file3ds, &io) != 0) 
     589    { 
     590        result = constructFrom3dsFile(file3ds,optFileName,options); 
     591        lib3ds_file_free(file3ds); 
    461592    } 
    462593 
     
    474605    if (fileName.empty()) return ReadResult::FILE_NOT_FOUND; 
    475606 
    476     Lib3dsFile *f = lib3ds_file_load(fileName.c_str(),options); 
     607    Lib3dsFile *f = lib3ds_file_open(fileName.c_str() /*,options*/); 
    477608 
    478609    if (f) 
     
    498629    lib3ds_file_eval(f,0.0f); // second param is time 't' for animated files 
    499630 
    500     ReaderObject reader; 
     631    ReaderObject reader(options); 
    501632 
    502633    reader._directory = ( options && !options->getDatabasePathList().empty() ) ? options->getDatabasePathList().front() : osgDB::getFilePath(fileName); 
    503634 
    504     osg::Group* group = new osg::Group; 
    505     group->setName(fileName); 
    506  
    507635    ReaderObject::StateSetMap drawStateMap; 
    508  
    509     for (Lib3dsMaterial *mat=f->materials; mat; mat=mat->next) 
    510     { 
    511         drawStateMap[mat->name] = reader.createStateSet(mat, options); 
    512     } 
    513      
     636    unsigned int numMaterials = f->nmaterials; 
     637    drawStateMap.insert(drawStateMap.begin(), numMaterials, NULL);        // Setup the map 
     638    for (unsigned int imat=0; imat<numMaterials; ++imat) 
     639    { 
     640        Lib3dsMaterial * mat = f->materials[imat]; 
     641        drawStateMap[imat] = reader.createStateSet(mat); 
     642    } 
     643 
    514644    if (osg::getNotifyLevel()>=osg::INFO) 
    515645    { 
     
    520650        } 
    521651        std::cout << "MESH TRAVERSAL of 3ds file "<<f->name<<std::endl; 
    522         for(Lib3dsMesh *mesh=f->meshes; mesh; mesh=mesh->next) { 
    523             print(mesh,level+1); 
     652        for (int imesh=0; imesh<f->nmeshes; ++imesh) { 
     653            print(f->meshes[imesh],level+1); 
    524654        } 
    525655    } 
     
    529659    // send me the model 
    530660    bool traverse_nodes=false; 
    531      
     661 
    532662    // MIKEC: have found 3ds files with NO node structure - only meshes, for this case we fall back to the old traverse-by-meshes code 
    533663    // Loading and re-exporting these files from 3DS produces a file with correct node structure, so perhaps these are not 100% conformant? 
     
    537667    } 
    538668 
     669    osg::Node* group = NULL; 
     670 
    539671    if (traverse_nodes) { // old method 
    540         for (Lib3dsMesh *mesh=f->meshes; mesh; mesh=mesh->next) { 
    541             reader.processMesh(drawStateMap,group,mesh,NULL); 
     672        group = new osg::Group(); 
     673        for (int imesh=0; imesh<f->nmeshes; ++imesh) { 
     674            reader.processMesh(drawStateMap,group->asGroup(),f->meshes[imesh],NULL); 
    542675        } 
    543676    } else { // new method 
    544         for(Lib3dsNode *node=f->nodes; node; node=node->next) { 
    545             group->addChild(reader.processNode(drawStateMap,f,node)); 
    546         } 
    547     }  
     677        Lib3dsNode *node=f->nodes; 
     678        if (!node->next) 
     679            group = reader.processNode(drawStateMap,f,node); 
     680        else 
     681        { 
     682            group = new osg::Group(); 
     683            for(; node; node=node->next) { 
     684                group->asGroup()->addChild(reader.processNode(drawStateMap,f,node)); 
     685            } 
     686        } 
     687    } 
     688    if (group && group->getName().empty()) group->setName(fileName); 
    548689 
    549690    if (osg::getNotifyLevel()>=osg::INFO) 
     
    552693        PrintVisitor pv(osg::notify(osg::NOTICE)); 
    553694        group->accept(pv); 
    554     }     
    555      
     695    } 
     696 
    556697    return group; 
    557698} 
     
    560701use matrix to pretransform geometry, or NULL to do nothing 
    561702*/ 
    562 osg::Drawable*   ReaderWriter3DS::ReaderObject::createDrawable(Lib3dsMesh *m,FaceList& faceList,Lib3dsMatrix *matrix) 
     703osg::Drawable* ReaderWriter3DS::ReaderObject::createDrawable(Lib3dsMesh *m,FaceList& faceList, const osg::Matrix * matrix) 
    563704{ 
    564705 
    565706    osg::Geometry* geom = new osg::Geometry; 
    566  
    567707    unsigned int i; 
    568      
     708 
    569709    std::vector<int> orig2NewMapping; 
    570     for(i=0;i<m->points;++i) orig2NewMapping.push_back(-1); 
     710    orig2NewMapping.reserve(m->nvertices); 
     711    for(i=0;i<m->nvertices;++i) orig2NewMapping.push_back(-1); 
    571712 
    572713    unsigned int noVertex=0; 
     
    577718    { 
    578719 
    579         Lib3dsFace& face = m->faceL[*fitr]; 
    580  
    581         if (orig2NewMapping[face.points[0]]<0) 
    582             orig2NewMapping[face.points[0]] = noVertex++; 
    583  
    584         if (orig2NewMapping[face.points[1]]<0) 
    585             orig2NewMapping[face.points[1]] = noVertex++; 
    586  
    587         if (orig2NewMapping[face.points[2]]<0) 
    588             orig2NewMapping[face.points[2]] = noVertex++; 
     720        Lib3dsFace& face = m->faces[*fitr]; 
     721 
     722        if (orig2NewMapping[face.index[0]]<0) 
     723            orig2NewMapping[face.index[0]] = noVertex++; 
     724 
     725        if (orig2NewMapping[face.index[1]]<0) 
     726            orig2NewMapping[face.index[1]] = noVertex++; 
     727 
     728        if (orig2NewMapping[face.index[2]]<0) 
     729            orig2NewMapping[face.index[2]] = noVertex++; 
    589730 
    590731    } 
    591732 
    592733    // create vertices. 
    593      
     734 
    594735    osg::Vec3Array* osg_coords = new osg::Vec3Array(noVertex); 
    595736    geom->setVertexArray(osg_coords); 
    596737 
    597     Lib3dsVector c; 
    598         
    599     for (i=0; i<m->points; ++i) 
     738    for (i=0; i<m->nvertices; ++i) 
    600739    { 
    601740        if (orig2NewMapping[i]>=0) 
     
    603742            if (matrix) 
    604743            { 
    605                 lib3ds_vector_transform(c,*matrix, m->pointL[i].pos); 
    606                 (*osg_coords)[orig2NewMapping[i]].set(c[0],c[1],c[2]); 
     744                (*osg_coords)[orig2NewMapping[i]].set( copyLib3dsVec3ToOsgVec3(m->vertices[i]) * (*matrix) ); 
    607745            } 
    608746            else 
    609747            { 
    610748                // original no transform code. 
    611                 (*osg_coords)[orig2NewMapping[i]].set(m->pointL[i].pos[0],m->pointL[i].pos[1],m->pointL[i].pos[2]); 
     749                (*osg_coords)[orig2NewMapping[i]].set( copyLib3dsVec3ToOsgVec3(m->vertices[i]) ); 
    612750            } 
    613751        } 
     
    615753 
    616754    // create texture coords if needed. 
    617     if (m->texels>0) 
    618     { 
    619         if (m->texels==m->points) 
    620         { 
    621             osg::Vec2Array* osg_tcoords = new osg::Vec2Array(noVertex); 
    622             geom->setTexCoordArray(0,osg_tcoords); 
    623             for (i=0; i<m->texels; ++i) 
    624             { 
    625                 if (orig2NewMapping[i]>=0) (*osg_tcoords)[orig2NewMapping[i]].set(m->texelL[i][0],m->texelL[i][1]); 
    626             } 
    627         } 
    628         else 
    629         { 
    630             osg::notify(osg::WARN)<<"Warning: in 3ds loader m->texels ("<<m->texels<<") != m->points ("<<m->points<<")"<< std::endl; 
    631         } 
    632     } 
    633  
    634     // create normals. 
     755    if (m->texcos) 
     756    { 
     757        osg::Vec2Array* osg_tcoords = new osg::Vec2Array(noVertex); 
     758        geom->setTexCoordArray(0,osg_tcoords); 
     759        for (i=0; i<m->nvertices; ++i) 
     760        { 
     761            if (orig2NewMapping[i]>=0) (*osg_tcoords)[orig2NewMapping[i]].set(m->texcos[i][0],m->texcos[i][1]); 
     762        } 
     763    } 
     764 
     765    // create normals 
     766    // Sukender: 3DS file format doesn't store normals (that is to say they're recomputed each time). 
     767    // When using per vertex normals, we could use either vertex computation, or face computation (and copy the normal to each vertex). Here we use the latter one. 
    635768    if (_usePerVertexNormals) 
    636769    { 
     770        //Lib3dsVector * normals = new Lib3dsVector[m->nfaces*3]; 
     771        //lib3ds_mesh_calculate_vertex_normals(m, normals); 
     772        Lib3dsVector * normals = new Lib3dsVector[m->nfaces]; 
     773        lib3ds_mesh_calculate_face_normals(m, normals); 
    637774        osg::Vec3Array* osg_normals = new osg::Vec3Array(noVertex); 
    638          
     775 
    639776        // initialize normal list to zero's. 
    640777        for (i=0; i<noVertex; ++i) 
     
    647784            ++fitr) 
    648785        { 
    649             Lib3dsFace& face = m->faceL[*fitr]; 
    650  
    651             (*osg_normals)[orig2NewMapping[face.points[0]]] += osg::Vec3(face.normal[0],face.normal[1],face.normal[2]);; 
    652             (*osg_normals)[orig2NewMapping[face.points[1]]] += osg::Vec3(face.normal[0],face.normal[1],face.normal[2]);; 
    653             (*osg_normals)[orig2NewMapping[face.points[2]]] += osg::Vec3(face.normal[0],face.normal[1],face.normal[2]);; 
    654  
    655         } 
    656  
    657         if (matrix) 
    658         { 
    659             osg::Matrix osg_matrix; 
    660             copyLib3dsMatrixToOsgMatrix(osg_matrix, *matrix); 
    661             for (i=0; i<noVertex; ++i) 
    662             { 
    663                 (*osg_normals)[i] = osg::Matrix::transform3x3((*osg_normals)[i], osg_matrix); 
    664             } 
    665         } 
    666  
    667         // normalize the normal list to unit length normals. 
    668         for (i=0; i<noVertex; ++i) 
    669         { 
    670             (*osg_normals)[i].normalize(); 
     786            Lib3dsFace& face = m->faces[*fitr]; 
     787            osg::Vec3f osgNormal( copyLib3dsVec3ToOsgVec3(normals[*fitr]) ); 
     788            if (matrix) osgNormal = osg::Matrix::transform3x3(osgNormal, *matrix); 
     789            osgNormal.normalize(); 
     790            (*osg_normals)[orig2NewMapping[face.index[0]]] = osgNormal; 
     791            (*osg_normals)[orig2NewMapping[face.index[1]]] = osgNormal; 
     792            (*osg_normals)[orig2NewMapping[face.index[2]]] = osgNormal; 
    671793        } 
    672794 
     
    675797 
    676798    } 
    677     else  
    678     { 
     799    else 
     800    { 
     801        Lib3dsVector * normals = new Lib3dsVector[m->nfaces]; 
     802        lib3ds_mesh_calculate_face_normals(m, normals); 
    679803        osg::Vec3Array* osg_normals = new osg::Vec3Array(faceList.size()); 
    680804        osg::Vec3Array::iterator normal_itr = osg_normals->begin(); 
     
    683807            ++fitr) 
    684808        { 
    685             Lib3dsFace& face = m->faceL[*fitr]; 
    686             *(normal_itr++) =  osg::Vec3(face.normal[0],face.normal[1],face.normal[2]); 
     809            osg::Vec3f osgNormal( copyLib3dsVec3ToOsgVec3(normals[*fitr]) ); 
     810            if (matrix) osgNormal = osg::Matrix::transform3x3(osgNormal, *matrix); 
     811            osgNormal.normalize(); 
     812            *(normal_itr++) = osgNormal; 
    687813        } 
    688814        geom->setNormalArray(osg_normals); 
    689815        geom->setNormalBinding(osg::Geometry::BIND_PER_PRIMITIVE); 
    690816    } 
    691      
     817 
    692818    osg::Vec4ubArray* osg_colors = new osg::Vec4ubArray(1); 
    693819    (*osg_colors)[0].set(255,255,255,255); 
    694820    geom->setColorArray(osg_colors); 
    695821    geom->setColorBinding(osg::Geometry::BIND_OVERALL); 
    696      
     822 
    697823 
    698824    // create primitives 
     
    705831        ++fitr) 
    706832    { 
    707         Lib3dsFace& face = m->faceL[*fitr]; 
    708  
    709         *(index_itr++) = orig2NewMapping[face.points[0]]; 
    710         *(index_itr++) = orig2NewMapping[face.points[1]]; 
    711         *(index_itr++) = orig2NewMapping[face.points[2]]; 
    712     } 
    713     
     833        Lib3dsFace& face = m->faces[*fitr]; 
     834 
     835        *(index_itr++) = orig2NewMapping[face.index[0]]; 
     836        *(index_itr++) = orig2NewMapping[face.index[1]]; 
     837        *(index_itr++) = orig2NewMapping[face.index[2]]; 
     838    } 
     839 
    714840    geom->addPrimitiveSet(elements); 
    715841 
     
    723849 
    724850 
    725 osg::Texture2D*  ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap *texture,const char* label,bool& transparancy, const osgDB::ReaderWriter::Options* options) 
     851osg::Texture2D*  ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap *texture,const char* label,bool& transparancy) 
    726852{ 
    727853    if (texture && *(texture->name)) 
     
    729855        osg::notify(osg::NOTICE)<<"texture->name="<<texture->name<<", _directory="<<_directory<<std::endl; 
    730856 
    731  
    732857        std::string fileName = osgDB::findFileInDirectory(texture->name,_directory,osgDB::CASE_INSENSITIVE); 
    733         if (fileName.empty())  
     858        if (fileName.empty()) 
    734859        { 
    735860            // file not found in .3ds file's directory, so we'll look in the datafile path list. 
    736861            fileName = osgDB::findDataFile(texture->name,options, osgDB::CASE_INSENSITIVE); 
     862            osg::notify(osg::NOTICE)<<"texture->name="<<texture->name<<", _directory="<<_directory<<std::endl; 
    737863        } 
    738864 
     
    753879        osg::notify(osg::DEBUG_INFO) << " '"<<texture->name<<"'"<< std::endl; 
    754880        osg::notify(osg::DEBUG_INFO) << "    texture flag        "<<texture->flags<< std::endl; 
    755         osg::notify(osg::DEBUG_INFO) << "    LIB3DS_DECALE       "<<((texture->flags)&LIB3DS_DECALE)<< std::endl; 
    756         osg::notify(osg::DEBUG_INFO) << "    LIB3DS_MIRROR       "<<((texture->flags)&LIB3DS_MIRROR)<< std::endl; 
    757         osg::notify(osg::DEBUG_INFO) << "    LIB3DS_NEGATE       "<<((texture->flags)&LIB3DS_NEGATE)<< std::endl; 
    758         osg::notify(osg::DEBUG_INFO) << "    LIB3DS_NO_TILE      "<<((texture->flags)&LIB3DS_NO_TILE)<< std::endl; 
    759         osg::notify(osg::DEBUG_INFO) << "    LIB3DS_SUMMED_AREA  "<<((texture->flags)&LIB3DS_SUMMED_AREA)<< std::endl; 
    760         osg::notify(osg::DEBUG_INFO) << "    LIB3DS_ALPHA_SOURCE "<<((texture->flags)&LIB3DS_ALPHA_SOURCE)<< std::endl; 
    761         osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TINT         "<<((texture->flags)&LIB3DS_TINT)<< std::endl; 
    762         osg::notify(osg::DEBUG_INFO) << "    LIB3DS_IGNORE_ALPHA "<<((texture->flags)&LIB3DS_IGNORE_ALPHA)<< std::endl; 
    763         osg::notify(osg::DEBUG_INFO) << "    LIB3DS_RGB_TINT     "<<((texture->flags)&LIB3DS_RGB_TINT)<< std::endl; 
    764  
    765         osg::ref_ptr<osg::Image> osg_image = osgDB::readRefImageFile(fileName.c_str()); 
     881        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_DECALE       "<<((texture->flags)&LIB3DS_TEXTURE_DECALE)<< std::endl; 
     882        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_MIRROR       "<<((texture->flags)&LIB3DS_TEXTURE_MIRROR)<< std::endl; 
     883        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_NEGATE       "<<((texture->flags)&LIB3DS_TEXTURE_NEGATE)<< std::endl; 
     884        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_NO_TILE      "<<((texture->flags)&LIB3DS_TEXTURE_NO_TILE)<< std::endl; 
     885        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_SUMMED_AREA  "<<((texture->flags)&LIB3DS_TEXTURE_SUMMED_AREA)<< std::endl; 
     886        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_ALPHA_SOURCE "<<((texture->flags)&LIB3DS_TEXTURE_ALPHA_SOURCE)<< std::endl; 
     887        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_TINT         "<<((texture->flags)&LIB3DS_TEXTURE_TINT)<< std::endl; 
     888        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_IGNORE_ALPHA "<<((texture->flags)&LIB3DS_TEXTURE_IGNORE_ALPHA)<< std::endl; 
     889        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TEXTURE_RGB_TINT     "<<((texture->flags)&LIB3DS_TEXTURE_RGB_TINT)<< std::endl; 
     890 
     891        bool noTexture = false; 
     892        if (options) 
     893        { 
     894            std::istringstream iss(options->getOptionString()); 
     895            std::string opt; 
     896            while (iss >> opt) 
     897            { 
     898                if (opt == "noTexture") 
     899                    noTexture = true; 
     900            } 
     901        } 
     902 
     903        osg::ref_ptr<osg::Image> osg_image = NULL; 
     904        if(noTexture) 
     905            osg_image = new osg::Image(); 
     906        else 
     907            osg_image = osgDB::readRefImageFile(fileName.c_str(), options); //Absolute Path 
    766908        if (!osg_image) 
    767909        { 
     
    769911            return NULL; 
    770912        } 
    771  
     913        if (osg_image->getFileName().empty()) // it should be done in OSG with osgDB::readRefImageFile(fileName.c_str()); 
     914            osg_image->setFileName(fileName); 
    772915        osg::Texture2D* osg_texture = new osg::Texture2D; 
    773916        osg_texture->setImage(osg_image.get()); 
    774  
     917        osg_texture->setName(texture->name); 
    775918        // does the texture support transparancy? 
    776         transparancy = ((texture->flags)&LIB3DS_ALPHA_SOURCE)!=0; 
     919        transparancy = ((texture->flags)&LIB3DS_TEXTURE_ALPHA_SOURCE)!=0; 
    777920 
    778921        // what is the wrap mode of the texture. 
    779         osg::Texture2D::WrapMode wm = ((texture->flags)&LIB3DS_NO_TILE) ? 
     922        osg::Texture2D::WrapMode wm = ((texture->flags)&LIB3DS_TEXTURE_NO_TILE) ? 
    780923                osg::Texture2D::CLAMP : 
    781924                osg::Texture2D::REPEAT; 
     
    793936 
    794937 
    795 osg::StateSet* ReaderWriter3DS::ReaderObject::createStateSet(Lib3dsMaterial *mat, const osgDB::ReaderWriter::Options* options) 
     938osg::StateSet* ReaderWriter3DS::ReaderObject::createStateSet(Lib3dsMaterial *mat) 
    796939{ 
    797940    if (mat==NULL) return NULL; 
     
    819962 
    820963    bool textureTransparancy=false; 
    821     osg::Texture2D* texture1_map = createTexture(&(mat->texture1_map),"texture1_map",textureTransparancy, options); 
     964    osg::Texture2D* texture1_map = createTexture(&(mat->texture1_map),"texture1_map",textureTransparancy); 
    822965    if (texture1_map) 
    823966    { 
    824967        stateset->setTextureAttributeAndModes(0,texture1_map,osg::StateAttribute::ON); 
    825          
     968 
    826969        if (!textureTransparancy) 
    827         {         
     970        { 
    828971            // from an email from Eric Hamil, September 30, 2003. 
    829972            // According to the 3DS spec, and other 
     
    831974            // a non-white diffuse base color and a 100% opaque bitmap texture, will show the 
    832975            // texture with no influence from the base color. 
    833              
     976 
    834977            // so we'll override material back to white. 
    835978            // and no longer require the decal hack below... 
     
    846989            material->setDiffuse(osg::Material::FRONT_AND_BACK,osg::Vec4(0.8f,0.8f,0.8f,alpha)); 
    847990            material->setSpecular(osg::Material::FRONT_AND_BACK,osg::Vec4(0.0f,0.0f,0.0f,alpha)); 
    848 #endif             
    849         } 
    850          
    851 // no longer required...         
     991#endif 
     992        } 
     993 
     994// no longer required... 
    852995//         bool decal = false; 
    853 //          
     996// 
    854997//         // not sure exactly how to interpret what is best for .3ds 
    855998//         // but the default text env MODULATE doesn't work well, and 
     
    8931036} 
    8941037 
     1038 
     1039 
     1040#if ENABLE_3DS_WRITER 
     1041osgDB::ReaderWriter::WriteResult ReaderWriter3DS::writeNode(const osg::Node& node,const std::string& fileName,const Options* options) const { 
     1042    std::string ext = osgDB::getLowerCaseFileExtension(fileName); 
     1043    if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED; 
     1044 
     1045    //osg::notify(osg::WARN) << "!!WARNING!! 3DS write support is incomplete" << std::endl; 
     1046 
     1047    bool ok = true; 
     1048    Lib3dsFile * file3ds = lib3ds_file_new(); 
     1049    if (!file3ds) return WriteResult(WriteResult::ERROR_IN_WRITING_FILE); 
     1050 
     1051    try { 
     1052        osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options; 
     1053        local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileName)); 
     1054 
     1055        if (!createFileObject(node, file3ds, fileName, local_opt)) ok = false; 
     1056        if (ok && !lib3ds_file_save(file3ds, fileName.c_str())) ok = false; 
     1057    } catch (...) { 
     1058        lib3ds_file_free(file3ds); 
     1059        throw; 
     1060    } 
     1061    lib3ds_file_free(file3ds); 
     1062 
     1063    return ok ? WriteResult(WriteResult::FILE_SAVED) : WriteResult(WriteResult::ERROR_IN_WRITING_FILE); 
     1064    //return ok ? WriteResult(WriteResult::FILE_SAVED) : WriteResult(WriteResult::FILE_NOT_HANDLED); 
     1065} 
     1066 
     1067 
     1068osgDB::ReaderWriter::WriteResult ReaderWriter3DS::writeNode(const osg::Node& node,std::ostream& fout,const Options* options) const { 
     1069    //osg::notify(osg::WARN) << "!!WARNING!! 3DS write support is incomplete" << std::endl; 
     1070    std::string optFileName = ""; 
     1071    if (options) 
     1072    { 
     1073        optFileName = options->getPluginStringData("STREAM_FILENAME"); 
     1074    } 
     1075 
     1076    Lib3dsIo io; 
     1077    io.self = &fout; 
     1078    io.seek_func = fileo_seek_func; 
     1079    io.tell_func = fileo_tell_func; 
     1080    io.read_func = NULL; 
     1081    io.write_func = fileo_write_func; 
     1082    io.log_func = fileio_log_func; 
     1083     
     1084    Lib3dsFile * file3ds = lib3ds_file_new(); 
     1085    bool ok = true; 
     1086    try { 
     1087        osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options; 
     1088        local_opt->getDatabasePathList().push_front(osgDB::getFilePath(optFileName)); 
     1089 
     1090        if (!createFileObject(node, file3ds, optFileName, local_opt)) ok = false; 
     1091        if (ok && !lib3ds_file_write(file3ds, &io)) ok = false; 
     1092         
     1093    } catch (...) { 
     1094        lib3ds_file_free(file3ds); 
     1095        throw; 
     1096    } 
     1097    lib3ds_file_free(file3ds); 
     1098 
     1099    return ok ? WriteResult(WriteResult::FILE_SAVED) : WriteResult(WriteResult::ERROR_IN_WRITING_FILE); 
     1100    //return ok ? WriteResult(WriteResult::FILE_SAVED) : WriteResult(WriteResult::FILE_NOT_HANDLED); 
     1101} 
     1102 
     1103const std::string getParent(const std::string & pathBad) 
     1104{ 
     1105    const std::string & path = osgDB::convertFileNameToNativeStyle(pathBad); 
     1106 
     1107    std::string parent = ""; 
     1108    std::string tmp = ""; 
     1109    for(std::string::const_iterator itPath = path.begin();; ++itPath) 
     1110    { 
     1111        if (!parent.empty()) 
     1112            parent += '\\'; 
     1113        parent += tmp; 
     1114        tmp.clear(); 
     1115        for(;itPath != path.end() && *itPath != '\\'; ++itPath) 
     1116            tmp += *itPath; 
     1117        if (itPath == path.end()) 
     1118            break; 
     1119    } 
     1120    return parent; 
     1121} 
     1122 
     1123bool ReaderWriter3DS::createFileObject(const osg::Node& node, Lib3dsFile * file3ds,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const { 
     1124    WriterNodeVisitor w(file3ds, fileName, options, getParent(node.getName())); 
     1125    const_cast<osg::Node &>(node).accept(w);                // TODO Remove that ugly const_cast<>. Any idea? 
     1126    if (!w.suceedLastApply()) 
     1127        return false; 
     1128    w.writeMaterials(); 
     1129    return true;        //w.good(); 
     1130} 
     1131#endif    // ENABLE_3DS_WRITER