Hi Robert,

Sorry about that. I didn't read the submissions protocol first....you can take 
a horse to water and all that ;-(

Anyway, please find attached the modified file. I tested the change in OSG1.2 
and it corrected the issue I was having, but as per the submissions protocol, 
I've downloaded the latest release version - OSG2.8 - of that file and made the 
same modification (but not tested in 2.8) , and it is this that I've attached. 

Hope this helps.

Kind regards

Neil.






---- Robert Osfield <[email protected]> wrote: 
> Hi Neil,
> 
> Could you send me the whole modified file as I'm afraid the mail
> client just blitz's code copy and pasted into emails making it
> impossible to do a reliable merge.
> 
> Thanks,
> Robert.
> 
> On Mon, Mar 2, 2009 at 12:38 PM,  <[email protected]> wrote:
> > Hi Robert,
> >
> > I've spent the past four days playing with PNG files on 3DS objects, and I 
> > think I may have come across a small issue with the 3DS loader.
> >
> > At the company where I work we model literally thousands of 3DS models for 
> > all types of objects. Most don't have transparent textures applied to them, 
> > but a few do. The issue is that a texture may have an alpha channel 
> > defined, yet its 3DS material definition is not marked as being an alpha 
> > source, or transparent. This can happen for any number of modelling reasons 
> > (so I'm told) but the net effect is that when a 3DS object is loaded with 
> > such a material applied, the stateset creation for the 3DS geometry omits 
> > to add the GL_BLEND attribute as neither of the conditions that it 
> > currently tests for are satisfied under this scenario.
> >
> > However, one thing that is apparently an indication of transparency/alpha 
> > information that we do make use of when modelling, but that the 3DS reader 
> > omits to test, is that we assign to the transparent texture to the 
> > opacity_map of the 3DS material structure.
> >
> > Now, having discussed with our modellers the theory behind the opacity map, 
> > it seems that it is safe to say that if an opacity map is specified, then 
> > the reader should really apply the GL_BLEND attribute as it is an 
> > indication of some form of blending being required between materials.
> >
> > With this in mind, I have made a minor change to the function 
> > createStateSet below, from the ReaderWriter3DS.cpp file, that checks for an 
> > opacity map having been specified, and in such a scenario the GL_BLEND 
> > attribute is applied to the geometry. This fixed our issue, and thought it 
> > might be helpful to others.
> >
> > Kind regards
> >
> > Neil Hughes.
> >
> > osg::StateSet* ReaderWriter3DS::ReaderObject::createStateSet(Lib3dsMaterial 
> > *mat, const osgDB::ReaderWriter::Options* options)
> > {
> >    if (mat==NULL) return NULL;
> >
> >    osg::StateSet* stateset = new osg::StateSet;
> >
> >    osg::Material* material = new osg::Material;
> >
> >    float transparency = mat->transparency;
> >    float alpha = 1.0f-transparency;
> >
> >    osg::Vec4 ambient(mat->ambient[0],mat->ambient[1],mat->ambient[2],alpha);
> >    osg::Vec4 diffuse(mat->diffuse[0],mat->diffuse[1],mat->diffuse[2],alpha);
> >    osg::Vec4 
> > specular(mat->specular[0],mat->specular[1],mat->specular[2],alpha);
> >    specular *= mat->shin_strength;
> >
> >    float shininess = mat->shininess;
> >        material->setName(mat->name);
> >    material->setAmbient(osg::Material::FRONT_AND_BACK,ambient);
> >    material->setDiffuse(osg::Material::FRONT_AND_BACK,diffuse);
> >    material->setSpecular(osg::Material::FRONT_AND_BACK,specular);
> >    material->setShininess(osg::Material::FRONT_AND_BACK,shininess*128.0f);
> >
> >    stateset->setAttribute(material);
> >
> >    bool textureTransparancy=false;
> >    osg::Texture2D* texture1_map = 
> > createTexture(&(mat->texture1_map),"texture1_map",textureTransparancy, 
> > options);
> >    if (texture1_map)
> >    {
> >        
> > stateset->setTextureAttributeAndModes(0,texture1_map,osg::StateAttribute::ON);
> >
> >        if (!textureTransparancy)
> >        {
> >            // from an email from Eric Hamil, September 30, 2003.
> >            // According to the 3DS spec, and other
> >            // software (like Max, Lightwave, and Deep Exploration) a 3DS 
> > material that has
> >            // a non-white diffuse base color and a 100% opaque bitmap 
> > texture, will show the
> >            // texture with no influence from the base color.
> >
> >            // so we'll override material back to white.
> >            // and no longer require the decal hack below...
> > #if 0
> >            // Eric orignal fallback
> >            osg::Vec4 white(1.0f,1.0f,1.0f,alpha);
> >            material->setAmbient(osg::Material::FRONT_AND_BACK,white);
> >            material->setDiffuse(osg::Material::FRONT_AND_BACK,white);
> >            material->setSpecular(osg::Material::FRONT_AND_BACK,white);
> > #else
> >            // try alternative to avoid staturating with white
> >            // setting white as per OpenGL defaults.
> >            
> > material->setAmbient(osg::Material::FRONT_AND_BACK,osg::Vec4(0.2f,0.2f,0.2f,alpha));
> >            
> > material->setDiffuse(osg::Material::FRONT_AND_BACK,osg::Vec4(0.8f,0.8f,0.8f,alpha));
> >            
> > material->setSpecular(osg::Material::FRONT_AND_BACK,osg::Vec4(0.0f,0.0f,0.0f,alpha));
> > #endif
> >        }
> >
> > // no longer required...
> > //         bool decal = false;
> > //
> > //         // not sure exactly how to interpret what is best for .3ds
> > //         // but the default text env MODULATE doesn't work well, and
> > //         // DECAL seems to work better.
> > //         osg::TexEnv* texenv = new osg::TexEnv;
> > //         if (decal)
> > //         {
> > //             texenv->setMode(osg::TexEnv::DECAL);
> > //         }
> > //         else
> > //         {
> > //             texenv->setMode(osg::TexEnv::MODULATE);
> > //         }
> > //        stateset->setTextureAttribute(0,texenv);
> >    }
> >
> >        //if (transparency>0.0f || textureTransparancy)
> >    if (transparency>0.0f || textureTransparancy || 
> > mat->opacity_map.flags!=0)
> >    {
> >        stateset->setMode(GL_BLEND,osg::StateAttribute::ON);
> >        stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
> >    }
> >
> > /*
> >    osg::ref_ptr<osg::Texture> texture1_mask = 
> > createTexture(&(mat->texture1_mask),"texture1_mask",textureTransparancy);
> >    osg::ref_ptr<osg::Texture> texture2_map = 
> > createTexture(&(mat->texture2_map),"texture2_map",textureTransparancy);
> >    osg::ref_ptr<osg::Texture> texture2_mask = 
> > createTexture(&(mat->texture2_mask),"texture2_mask",textureTransparancy);
> >    osg::ref_ptr<osg::Texture> opacity_map = 
> > createTexture(&(mat->opacity_map),"opacity_map",textureTransparancy);
> >    osg::ref_ptr<osg::Texture> opacity_mask = 
> > createTexture(&(mat->opacity_mask),"opacity_mask",textureTransparancy);
> >    osg::ref_ptr<osg::Texture> bump_map = 
> > createTexture(&(mat->bump_map),"bump_map",textureTransparancy);
> >    osg::ref_ptr<osg::Texture> bump_mask = 
> > createTexture(&(mat->bump_mask),"bump_mask",textureTransparancy);
> >    osg::ref_ptr<osg::Texture> specular_map = 
> > createTexture(&(mat->specular_map),"specular_map",textureTransparancy);
> >    osg::ref_ptr<osg::Texture> specular_mask = 
> > createTexture(&(mat->specular_mask),"specular_mask",textureTransparancy);
> >    osg::ref_ptr<osg::Texture> shininess_map = 
> > createTexture(&(mat->shininess_map),"shininess_map",textureTransparancy);
> >    osg::ref_ptr<osg::Texture> shininess_mask = 
> > createTexture(&(mat->shininess_mask),"shininess_mask",textureTransparancy);
> >    osg::ref_ptr<osg::Texture> self_illum_map = 
> > createTexture(&(mat->self_illum_map),"self_illum_map",textureTransparancy);
> >    osg::ref_ptr<osg::Texture> self_illum_mask = 
> > createTexture(&(mat->self_illum_mask),"self_illum_mask",textureTransparancy);
> >    osg::ref_ptr<osg::Texture> reflection_map = 
> > createTexture(&(mat->reflection_map),"reflection_map",textureTransparancy);
> >    osg::ref_ptr<osg::Texture> reflection_mask = 
> > createTexture(&(mat->reflection_mask),"reflection_mask",textureTransparancy);
> > */
> >    return stateset;
> > }
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
> > _______________________________________________
> > osg-submissions mailing list
> > [email protected]
> > http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org
> >
> _______________________________________________
> osg-submissions mailing list
> [email protected]
> http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org
#include <osg/Notify>
#include <osg/Group>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Texture2D>
#include <osg/Material>
#include <osg/TexEnv>
#include <osg/ref_ptr>
#include <osg/MatrixTransform>

#include <osgDB/Registry>
#include <osgDB/FileUtils>
#include <osgDB/FileNameUtils>
#include <osgDB/ReadFile>

#include <osgUtil/TriStripVisitor>

//MIKEC debug only for PrintVisitor
#include <osg/NodeVisitor>


#include "file.h"
#include "mesh.h"
#include "material.h"
#include "vector.h"
#include "matrix.h"
#include "node.h"
#include "quat.h"
#include "readwrite.h"

#include <stdlib.h>
#include <string.h>

#include <set>
#include <map>
#include <iostream>

using namespace std;
using namespace osg;

class PrintVisitor : public NodeVisitor
{

   public:
   
        PrintVisitor(std::ostream& out):
            NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
            _out(out)
        {
            _indent = 0;
            _step = 4;
        }
        
        inline void moveIn() { _indent += _step; }
        inline void moveOut() { _indent -= _step; }
        inline void writeIndent() 
        {
            for(int i=0;i<_indent;++i) _out << " ";
        }
                
        virtual void apply(Node& node)
        {
            moveIn();
            writeIndent(); _out << node.className() <<std::endl;
            traverse(node);
            moveOut();
        }

        virtual void apply(Geode& node)         { apply((Node&)node); }
        virtual void apply(Billboard& node)     { apply((Geode&)node); }
        virtual void apply(LightSource& node)   { apply((Group&)node); }
        virtual void apply(ClipNode& node)      { apply((Group&)node); }
        
        virtual void apply(Group& node)         { apply((Node&)node); }
        virtual void apply(Transform& node)     { apply((Group&)node); }
        virtual void apply(Projection& node)    { apply((Group&)node); }
        virtual void apply(Switch& node)        { apply((Group&)node); }
        virtual void apply(LOD& node)           { apply((Group&)node); }

   protected:

        PrintVisitor& operator = (const PrintVisitor&) { return *this; }

        std::ostream& _out;
        int _indent;
        int _step;
};

class ReaderWriter3DS : public osgDB::ReaderWriter
{
    public:

        ReaderWriter3DS();

        virtual const char* className() const { return "3DS Auto Studio 
Reader"; }

        virtual ReadResult readNode(const std::string& file, const 
osgDB::ReaderWriter::Options* options) const;

    protected:


        class ReaderObject
        {
        public:
            ReaderObject();
        
            typedef std::map<std::string,osg::StateSet*> StateSetMap;
            typedef std::vector<int> FaceList;
            typedef std::map<std::string,osg::StateSet*> GeoStateMap;

            osg::Texture2D* createTexture(Lib3dsTextureMap *texture,const char* 
label,bool& transparancy, const osgDB::ReaderWriter::Options* options);
            osg::StateSet* createStateSet(Lib3dsMaterial *materials, const 
osgDB::ReaderWriter::Options* options);
            osg::Drawable* createDrawable(Lib3dsMesh *meshes,FaceList& 
faceList, Lib3dsMatrix* matrix);

            std::string _directory;
            bool _useSmoothingGroups;
            bool _usePerVertexNormals;

            // MIKEC
            osg::Node* processMesh(StateSetMap& drawStateMap,osg::Group* 
parent,Lib3dsMesh* mesh, Lib3dsMatrix* matrix);
            osg::Node* processNode(StateSetMap drawStateMap,Lib3dsFile 
*f,Lib3dsNode *node);
        };
};

// now register with Registry to instantiate the above
// reader/writer.
REGISTER_OSGPLUGIN(3ds, ReaderWriter3DS)

ReaderWriter3DS::ReaderWriter3DS()
{
    supportsExtension("3ds","3D Studio model format");

    setByteOrder();

#if 0
    osg::notify(osg::NOTICE)<<"3DS reader sizes:"<<std::endl;
    osg::notify(osg::NOTICE)<<"  
sizeof(Lib3dsBool)="<<sizeof(Lib3dsBool)<<std::endl;
    osg::notify(osg::NOTICE)<<"  
sizeof(Lib3dsByte)="<<sizeof(Lib3dsByte)<<std::endl;
    osg::notify(osg::NOTICE)<<"  
sizeof(Lib3dsWord)="<<sizeof(Lib3dsWord)<<std::endl;
    osg::notify(osg::NOTICE)<<"  
sizeof(Lib3dsDword)="<<sizeof(Lib3dsDword)<<std::endl;
    osg::notify(osg::NOTICE)<<"  
sizeof(Lib3dsIntb)="<<sizeof(Lib3dsIntb)<<std::endl;
    osg::notify(osg::NOTICE)<<"  
sizeof(Lib3dsIntw)="<<sizeof(Lib3dsIntw)<<std::endl;
    osg::notify(osg::NOTICE)<<"  
sizeof(Lib3dsIntd)="<<sizeof(Lib3dsIntd)<<std::endl;
    osg::notify(osg::NOTICE)<<"  
sizeof(Lib3dsFloat)="<<sizeof(Lib3dsFloat)<<std::endl;
    osg::notify(osg::NOTICE)<<"  
sizeof(Lib3dsDouble)="<<sizeof(Lib3dsDouble)<<std::endl;
    osg::notify(osg::NOTICE)<<"  
sizeof(Lib3dsVector)="<<sizeof(Lib3dsVector)<<std::endl;
    osg::notify(osg::NOTICE)<<"  
sizeof(Lib3dsTexel)="<<sizeof(Lib3dsTexel)<<std::endl;
    osg::notify(osg::NOTICE)<<"  
sizeof(Lib3dsQuat)="<<sizeof(Lib3dsQuat)<<std::endl;
    osg::notify(osg::NOTICE)<<"  
sizeof(Lib3dsMatrix)="<<sizeof(Lib3dsMatrix)<<std::endl;
    osg::notify(osg::NOTICE)<<"  
sizeof(Lib3dsRgb)="<<sizeof(Lib3dsRgb)<<std::endl;
    osg::notify(osg::NOTICE)<<"  
sizeof(Lib3dsRgba)="<<sizeof(Lib3dsRgba)<<std::endl;
#endif

}

ReaderWriter3DS::ReaderObject::ReaderObject()
{
    _useSmoothingGroups = true;
    _usePerVertexNormals = true;
}


/**
    These print methods for 3ds hacking
*/
void pad(int level) {
    for(int i=0;i<level;i++) std::cout<<"  ";
}
void print(Lib3dsMesh *mesh,int level);
void print(Lib3dsUserData *user,int level);
void print(Lib3dsNodeData *user,int level);
void print(Lib3dsObjectData *object,int level);
void print(Lib3dsNode *node, int level);

void print(Lib3dsMatrix matrix,int level) {
    pad(level); cout << matrix[0][0] <<" "<< matrix[0][1] <<" "<< matrix[0][2] 
<<" "<< matrix[0][3] << endl;
    pad(level); cout << matrix[1][0] <<" "<< matrix[1][1] <<" "<< matrix[1][2] 
<<" "<< matrix[1][3] << endl;
    pad(level); cout << matrix[2][0] <<" "<< matrix[2][1] <<" "<< matrix[2][2] 
<<" "<< matrix[2][3] << endl;
    pad(level); cout << matrix[3][0] <<" "<< matrix[3][1] <<" "<< matrix[3][2] 
<<" "<< matrix[3][3] << endl;
}
void print(Lib3dsMesh *mesh,int level) {
    if (mesh) {
        pad(level); cout << "mesh name " << mesh->name  << endl;
        print(mesh->matrix,level);
    } else {
        pad(level); cout << "no mesh " << endl;
    }
}
void print(Lib3dsUserData *user,int level) {
    if (user) {
        pad(level); cout << "user data" << endl;
        //print(user->mesh,level+1);
    } else {
        pad(level); cout << "no user data" << endl;
    }
}
void print(Lib3dsNodeData *node,int level) {
    if (node) {
        pad(level); cout << "node data:" << endl;
        // nodedata->object is a union of many types
        print((Lib3dsObjectData *)&node->object,level+1);
    } else {
        pad(level); cout << "no user data" << endl;
    }
}
void print(Lib3dsObjectData *object,int level) {
    if (object) {
        pad(level); cout << "objectdata instance [" << object->instance << "]" 
<< endl;
        pad(level); cout << "pivot     " << object->pivot[0] <<" "<< 
object->pivot[1] <<" "<< object->pivot[2] << endl;
        pad(level); cout << "pos       " << object->pos[0] <<" "<< 
object->pos[1] <<" "<< object->pos[2] << endl;
        pad(level); cout << "scl       " << object->scl[0] <<" "<< 
object->scl[1] <<" "<< object->scl[2] << endl;
        pad(level); cout << "rot       " << object->rot[0] <<" "<< 
object->rot[1] <<" "<< object->rot[2] <<" "<< object->rot[3] << endl;
    } else {
        pad(level); cout << "no object data" << endl;
    }
}

void print(Lib3dsNode *node, int level) {
    
    pad(level); cout << "node name [" << node->name << "]" << endl;
    pad(level); cout << "node id    " << node->node_id << endl;
    pad(level); cout << "node parent id " << node->parent_id << endl;
    pad(level); cout << "node matrix:" << endl;
    print(node->matrix,level+1);
    print(&node->data,level);
    print(&node->user,level);
    

    for(Lib3dsNode *child=node->childs; child; child=child->next) {
        print(child,level+1);
    }

}

void copyLib3dsMatrixToOsgMatrix(osg::Matrix& osg_matrix, const Lib3dsMatrix 
lib3ds_matrix)
{
    osg_matrix.set(
        
lib3ds_matrix[0][0],lib3ds_matrix[0][1],lib3ds_matrix[0][2],lib3ds_matrix[0][3],
        
lib3ds_matrix[1][0],lib3ds_matrix[1][1],lib3ds_matrix[1][2],lib3ds_matrix[1][3],
        
lib3ds_matrix[2][0],lib3ds_matrix[2][1],lib3ds_matrix[2][2],lib3ds_matrix[2][3],
        
lib3ds_matrix[3][0],lib3ds_matrix[3][1],lib3ds_matrix[3][2],lib3ds_matrix[3][3]);
 
}

// Transforms points by matrix if 'matrix' is not NULL
// Creates a Geode and Geometry (as parent,child) and adds the Geode to 
'parent' parameter iff 'parent' is non-NULL
// Returns ptr to the Geode
osg::Node* ReaderWriter3DS::ReaderObject::processMesh(StateSetMap& 
drawStateMap,osg::Group* parent,Lib3dsMesh* mesh, Lib3dsMatrix* matrix) {
    typedef std::vector<int> FaceList;
    typedef std::map<std::string,FaceList> MaterialFaceMap;
    MaterialFaceMap materialFaceMap;
    for (unsigned int i=0; i<mesh->faces; ++i)
    {
        materialFaceMap[mesh->faceL[i].material].push_back(i);
    }

    if (materialFaceMap.empty())
    {
        osg::notify(osg::NOTICE)<<"Warning : no triangles assigned to mesh 
'"<<mesh->name<<"'"<< std::endl;
        return NULL;
    }
    else
    {

        osg::Geode* geode = new osg::Geode;
        geode->setName(mesh->name);

        for(MaterialFaceMap::iterator itr=materialFaceMap.begin();
            itr!=materialFaceMap.end();
            ++itr)
        {
            FaceList& faceList = itr->second;
            
            if (_useSmoothingGroups)
            {

                typedef std::map<int,FaceList> SmoothingFaceMap;
                SmoothingFaceMap smoothingFaceMap;
                for (FaceList::iterator flitr=faceList.begin();
                     flitr!=faceList.end();
                     ++flitr)
                {
                    
smoothingFaceMap[mesh->faceL[*flitr].smoothing].push_back(*flitr);
                }

                for(SmoothingFaceMap::iterator sitr=smoothingFaceMap.begin();
                    sitr!=smoothingFaceMap.end();
                    ++sitr)
                {
                    // each smoothing group to have its own geom 
                    // to ensure the vertices on adjacent groups
                    // don't get shared.
                    FaceList& smoothFaceMap = sitr->second;

                    osg::Drawable* drawable = 
createDrawable(mesh,smoothFaceMap,matrix);
                    if (drawable)
                    {
                        drawable->setStateSet(drawStateMap[itr->first]);
                        geode->addDrawable(drawable);
                    }
                }
            }
            else // ignore smoothing groups.
            {
                osg::Drawable* drawable = createDrawable(mesh,faceList,matrix);
                if (drawable)
                {
                    drawable->setStateSet(drawStateMap[itr->first]);
                    geode->addDrawable(drawable);
                }
            }
        }

        if (parent) parent->addChild(geode);
        return geode;
    }
}


/**
How to cope with pivot points in 3ds (short version)

  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
  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?)

  Objects with pivot points have a position relative to their PP, so they have 
to undergo this transform:

    invert the mesh matrix, apply this matrix to the object. This puts the 
object back at the origin
    Transform the object by the nodes (negative) pivot point coords, this puts 
the PP at the origin
    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.

  */
osg::Node* ReaderWriter3DS::ReaderObject::processNode(StateSetMap 
drawStateMap,Lib3dsFile *f,Lib3dsNode *node) {
    
    osg::Group* group=NULL;// created on demand if we find we have children to 
group together
    

    // Handle all children of this node for hierarchical assemblies
    Lib3dsNode *p;
    for (p=node->childs; p!=0; p=p->next) {
        if (!group) {
            group =new osg::Group;
            if (strcmp(node->name, "$$$DUMMY") == 0) {
                group->setName(node->data.object.instance);
            } else {
                group->setName(node->name);
            }
        }
        group->addChild(processNode(drawStateMap,f,p));
    }
    
    // MIKEC: possible BUG - 3ds files APPEAR to enforce unqiue names, so this 
is OK, but I am not 100% sure
    // failed to find any alternative way to do it in lib3ds though, and lib3ds 
player.c application uses this method
    Lib3dsMesh *mesh=lib3ds_file_mesh_by_name(f,node->name);
    if (mesh) {
        Lib3dsObjectData* object=&node->data.object;
        Lib3dsMatrix mesh_inverse;
        osg::Matrix osgmatrix;
        
        lib3ds_matrix_copy(mesh_inverse,mesh->matrix); 
        lib3ds_matrix_inv(mesh_inverse);
        
        Lib3dsMatrix M,N;
        lib3ds_matrix_identity(M);
        lib3ds_matrix_identity(N);
        lib3ds_matrix_copy(M,node->matrix);
        N[3][0]=-object->pivot[0];
        N[3][1]=-object->pivot[1];
        N[3][2]=-object->pivot[2];

        bool pivoted=false;
        if ( (object->pivot[0]!=0.0) || (object->pivot[1]!=0.0) || 
(object->pivot[2]!=0.0) ) {
            pivoted=true; // there is a pivot point, so we must use it
        }

        /*cout<<"M"<<node->name<<endl;
        print(M,0);
        cout<<"N"<<endl;
        print(N,0);*/

        if (pivoted) {
            // Transform object's pivot point to the world origin
            osg::MatrixTransform* T=new osg::MatrixTransform;
            copyLib3dsMatrixToOsgMatrix(osgmatrix, N);
            T->setMatrix(osgmatrix);
            T->setName("3DSPIVOTPOINT: Translate pivotpoint to (world) origin");
            //cout<<"Translation for "<<node->name<<" is "<<osgmatrix<<endl;

            // rotate about "origin" (after the transform this is the world 
origin)
            // BUG this matrix also contains the translation to the pivot point 
- we should plit that out (maybe)
            osg::MatrixTransform* R=new osg::MatrixTransform;
            copyLib3dsMatrixToOsgMatrix(osgmatrix, M);
            R->setMatrix(osgmatrix);
            R->setName("3DSPIVOTPOINT: Rotate");
                
            /*
            cout<<"Rotation for "<<node->name<<" is "<<osgmatrix<<endl;
            osg::Quat quat;
            quat.set(osgmatrix);
            osg::Vec3 axis;
            float angle;
            quat.getRotate(angle,axis);
            cout<<"which is "<<osg::RadiansToDegrees(angle)<<" degrees around 
"<<axis<<endl;
            */
            /*
            printf("%s---------------\n",node->name);
            printf("mesh matrix :\n");         print(mesh->matrix,1);
            printf("mesh inverse:\n");         print(mesh_inverse,1);
            printf("node matrix :\n");         print(matrix,1);
            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]);
            */

            if (group) {
                // Always in reverse order...
                group->addChild(R); 
                R->addChild(T);
                processMesh(drawStateMap,T,mesh,&mesh_inverse); // creates 
geometry under modifier node
                return group;
            } else {
                // We are a pivoted node with no children
                R->addChild(T);
                processMesh(drawStateMap,T,mesh,&mesh_inverse); // creates 
geometry under modifier node
                return R;
            }
        } else {
            if(group) {
                // add our geometry to group (where our children already are)
                processMesh(drawStateMap,group,mesh,NULL); // creates geometry 
under modifier node
                return group;
            } else {
                // didnt use group for children
                // return a ptr directly to the Geode for this mesh
                return processMesh(drawStateMap,NULL,mesh,NULL); 
            }    
        }

    } else {
        // no mesh for this node - probably a camera or something of that 
persuasion
        //cout << "no mesh for object " << node->name << endl;
        return group; // we have no mesh, but we might have children
    }
}


osgDB::ReaderWriter::ReadResult ReaderWriter3DS::readNode(const std::string& 
file, const osgDB::ReaderWriter::Options* options) const
{

    std::string ext = osgDB::getLowerCaseFileExtension(file);
    if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;

    std::string fileName = osgDB::findDataFile( file, options );
    if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;

    Lib3dsFile *f = lib3ds_file_load(fileName.c_str());
    if (f==NULL) return ReadResult::FILE_NOT_HANDLED;

    // MIKEC
    // This appears to build the matrix structures for the 3ds structure
    // It wasn't previously necessary because all the meshes are stored in 
world coordinates
    // but is VERY necessary if you want to use pivot points...
    lib3ds_file_eval(f,0.0f); // second param is time 't' for animated files

    ReaderObject reader;

    reader._directory = osgDB::getFilePath(fileName);

    osg::Group* group = new osg::Group;
    group->setName(fileName);

    ReaderObject::StateSetMap drawStateMap;

    for (Lib3dsMaterial *mat=f->materials; mat; mat=mat->next)
    {
        drawStateMap[mat->name] = reader.createStateSet(mat, options);
    }
    
    if (osg::getNotifyLevel()>=osg::INFO)
    {
        int level=0;
        std::cout << "NODE TRAVERSAL of 3ds file "<<f->name<<std::endl;
        for(Lib3dsNode *node=f->nodes; node; node=node->next) {
            print(node,level+1);
        }
        std::cout << "MESH TRAVERSAL of 3ds file "<<f->name<<std::endl;
        for(Lib3dsMesh *mesh=f->meshes; mesh; mesh=mesh->next) {
            print(mesh,level+1);
        }
    }

    // We can traverse by meshes (old method, broken for pivot points, but 
otherwise works), or by nodes (new method, not so well tested yet)
    // if your model is broken, especially wrt object positions try setting 
this flag. If that fixes it,
    // send me the model
    bool traverse_nodes=false;
    
    // MIKEC: have found 3ds files with NO node structure - only meshes, for 
this case we fall back to the old traverse-by-meshes code
    // Loading and re-exporting these files from 3DS produces a file with 
correct node structure, so perhaps these are not 100% conformant?
    if (f->nodes == NULL) {
        osg::notify(osg::WARN)<<"Warning: in 3ds loader: file has no nodes, 
traversing by meshes instead"<< std::endl;
        traverse_nodes=true;
    }

    if (traverse_nodes) { // old method
        for (Lib3dsMesh *mesh=f->meshes; mesh; mesh=mesh->next) {
            reader.processMesh(drawStateMap,group,mesh,NULL);
        }
    } else { // new method
        for(Lib3dsNode *node=f->nodes; node; node=node->next) {
            group->addChild(reader.processNode(drawStateMap,f,node));
        }
    } 

    if (osg::getNotifyLevel()>=osg::INFO)
    {
        osg::notify(osg::NOTICE) << "Final OSG node structure looks like 
this:"<< endl;
        PrintVisitor pv(osg::notify(osg::NOTICE));
        group->accept(pv);
    }    
    
    lib3ds_file_free(f);

    return group;
}

/**
use matrix to pretransform geometry, or NULL to do nothing
*/
osg::Drawable*   ReaderWriter3DS::ReaderObject::createDrawable(Lib3dsMesh 
*m,FaceList& faceList,Lib3dsMatrix *matrix)
{

    osg::Geometry* geom = new osg::Geometry;

    unsigned int i;
    
    std::vector<int> orig2NewMapping;
    for(i=0;i<m->points;++i) orig2NewMapping.push_back(-1);

    unsigned int noVertex=0;
    FaceList::iterator fitr;
    for (fitr=faceList.begin();
        fitr!=faceList.end();
        ++fitr)
    {

        Lib3dsFace& face = m->faceL[*fitr];

        if (orig2NewMapping[face.points[0]]<0)
            orig2NewMapping[face.points[0]] = noVertex++;

        if (orig2NewMapping[face.points[1]]<0)
            orig2NewMapping[face.points[1]] = noVertex++;

        if (orig2NewMapping[face.points[2]]<0)
            orig2NewMapping[face.points[2]] = noVertex++;

    }

    // create vertices.
    
    osg::Vec3Array* osg_coords = new osg::Vec3Array(noVertex);
    geom->setVertexArray(osg_coords);

    Lib3dsVector c;
       
    for (i=0; i<m->points; ++i)
    {
        if (orig2NewMapping[i]>=0)
        {
            if (matrix)
            {
                lib3ds_vector_transform(c,*matrix, m->pointL[i].pos);
                (*osg_coords)[orig2NewMapping[i]].set(c[0],c[1],c[2]);
            }
            else
            {
                // original no transform code.
                
(*osg_coords)[orig2NewMapping[i]].set(m->pointL[i].pos[0],m->pointL[i].pos[1],m->pointL[i].pos[2]);
            }
        }
    }

    // create texture coords if needed.
    if (m->texels>0)
    {
        if (m->texels==m->points)
        {
            osg::Vec2Array* osg_tcoords = new osg::Vec2Array(noVertex);
            geom->setTexCoordArray(0,osg_tcoords);
            for (i=0; i<m->texels; ++i)
            {
                if (orig2NewMapping[i]>=0) 
(*osg_tcoords)[orig2NewMapping[i]].set(m->texelL[i][0],m->texelL[i][1]);
            }
        }
        else
        {
            osg::notify(osg::WARN)<<"Warning: in 3ds loader m->texels 
("<<m->texels<<") != m->points ("<<m->points<<")"<< std::endl;
        }
    }

    // create normals.
    if (_usePerVertexNormals)
    {
        osg::Vec3Array* osg_normals = new osg::Vec3Array(noVertex);
        
        // initialize normal list to zero's.
        for (i=0; i<noVertex; ++i)
        {
            (*osg_normals)[i].set(0.0f,0.0f,0.0f);
        }

        for (fitr=faceList.begin();
            fitr!=faceList.end();
            ++fitr)
        {
            Lib3dsFace& face = m->faceL[*fitr];

            (*osg_normals)[orig2NewMapping[face.points[0]]] += 
osg::Vec3(face.normal[0],face.normal[1],face.normal[2]);;
            (*osg_normals)[orig2NewMapping[face.points[1]]] += 
osg::Vec3(face.normal[0],face.normal[1],face.normal[2]);;
            (*osg_normals)[orig2NewMapping[face.points[2]]] += 
osg::Vec3(face.normal[0],face.normal[1],face.normal[2]);;

        }

        if (matrix)
        {
            osg::Matrix osg_matrix;
            copyLib3dsMatrixToOsgMatrix(osg_matrix, *matrix);
            for (i=0; i<noVertex; ++i)
            {
                (*osg_normals)[i] = 
osg::Matrix::transform3x3((*osg_normals)[i], osg_matrix);
            }
        }

        // normalize the normal list to unit length normals.
        for (i=0; i<noVertex; ++i)
        {
            (*osg_normals)[i].normalize();
        }

        geom->setNormalArray(osg_normals);
        geom->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);

    }
    else 
    {
        osg::Vec3Array* osg_normals = new osg::Vec3Array(faceList.size());
        osg::Vec3Array::iterator normal_itr = osg_normals->begin();
        for (fitr=faceList.begin();
            fitr!=faceList.end();
            ++fitr)
        {
            Lib3dsFace& face = m->faceL[*fitr];
            *(normal_itr++) =  
osg::Vec3(face.normal[0],face.normal[1],face.normal[2]);
        }
        geom->setNormalArray(osg_normals);
        geom->setNormalBinding(osg::Geometry::BIND_PER_PRIMITIVE);
    }
    
    osg::Vec4ubArray* osg_colors = new osg::Vec4ubArray(1);
    (*osg_colors)[0].set(255,255,255,255);
    geom->setColorArray(osg_colors);
    geom->setColorBinding(osg::Geometry::BIND_OVERALL);
    

    // create primitives
    int numIndices = faceList.size()*3;
    DrawElementsUShort* elements = new 
osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES,numIndices);
    DrawElementsUShort::iterator index_itr = elements->begin();

    for (fitr=faceList.begin();
        fitr!=faceList.end();
        ++fitr)
    {
        Lib3dsFace& face = m->faceL[*fitr];

        *(index_itr++) = orig2NewMapping[face.points[0]];
        *(index_itr++) = orig2NewMapping[face.points[1]];
        *(index_itr++) = orig2NewMapping[face.points[2]];
    }
   
    geom->addPrimitiveSet(elements);

#if 0
    osgUtil::TriStripVisitor tsv;
    tsv.stripify(*geom);
#endif

    return geom;
}


osg::Texture2D*  ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap 
*texture,const char* label,bool& transparancy, const 
osgDB::ReaderWriter::Options* options)
{
    if (texture && *(texture->name))
    {
        std::string fileName = 
osgDB::findFileInDirectory(texture->name,_directory,osgDB::CASE_INSENSITIVE);
        if (fileName.empty()) 
        {
            // file not found in .3ds file's directory, so we'll look in the 
datafile path list.
            fileName = osgDB::findDataFile(texture->name,options, 
osgDB::CASE_INSENSITIVE);
        }
        
        if (fileName.empty())
        {
            osg::notify(osg::WARN) << "texture '"<<texture->name<<"' not 
found"<< std::endl;
            return NULL;
        }

        if (label) osg::notify(osg::DEBUG_INFO) << label;
        else osg::notify(osg::DEBUG_INFO) << "texture name";
        osg::notify(osg::DEBUG_INFO) << " '"<<texture->name<<"'"<< std::endl;
        osg::notify(osg::DEBUG_INFO) << "    texture flag        
"<<texture->flags<< std::endl;
        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_DECALE       
"<<((texture->flags)&LIB3DS_DECALE)<< std::endl;
        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_MIRROR       
"<<((texture->flags)&LIB3DS_MIRROR)<< std::endl;
        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_NEGATE       
"<<((texture->flags)&LIB3DS_NEGATE)<< std::endl;
        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_NO_TILE      
"<<((texture->flags)&LIB3DS_NO_TILE)<< std::endl;
        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_SUMMED_AREA  
"<<((texture->flags)&LIB3DS_SUMMED_AREA)<< std::endl;
        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_ALPHA_SOURCE 
"<<((texture->flags)&LIB3DS_ALPHA_SOURCE)<< std::endl;
        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_TINT         
"<<((texture->flags)&LIB3DS_TINT)<< std::endl;
        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_IGNORE_ALPHA 
"<<((texture->flags)&LIB3DS_IGNORE_ALPHA)<< std::endl;
        osg::notify(osg::DEBUG_INFO) << "    LIB3DS_RGB_TINT     
"<<((texture->flags)&LIB3DS_RGB_TINT)<< std::endl;

        osg::ref_ptr<osg::Image> osg_image = 
osgDB::readRefImageFile(fileName.c_str());
        if (!osg_image)
        {
            osg::notify(osg::NOTICE) << "Warning: Cannot create texture 
"<<texture->name<< std::endl;
            return NULL;
        }

        osg::Texture2D* osg_texture = new osg::Texture2D;
        osg_texture->setImage(osg_image.get());

        // does the texture support transparancy?
        transparancy = ((texture->flags)&LIB3DS_ALPHA_SOURCE)!=0;

        // what is the wrap mode of the texture.
        osg::Texture2D::WrapMode wm = ((texture->flags)&LIB3DS_NO_TILE) ?
                osg::Texture2D::CLAMP :
                osg::Texture2D::REPEAT;
        osg_texture->setWrap(osg::Texture2D::WRAP_S,wm);
        osg_texture->setWrap(osg::Texture2D::WRAP_T,wm);
        osg_texture->setWrap(osg::Texture2D::WRAP_R,wm);
                                 // bilinear.
        
osg_texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR_MIPMAP_NEAREST);

        return osg_texture;
    }
    else
        return NULL;
}


osg::StateSet* ReaderWriter3DS::ReaderObject::createStateSet(Lib3dsMaterial 
*mat, const osgDB::ReaderWriter::Options* options)
{
    if (mat==NULL) return NULL;

    osg::StateSet* stateset = new osg::StateSet;

    osg::Material* material = new osg::Material;

    float transparency = mat->transparency;
    float alpha = 1.0f-transparency;

    osg::Vec4 ambient(mat->ambient[0],mat->ambient[1],mat->ambient[2],alpha);
    osg::Vec4 diffuse(mat->diffuse[0],mat->diffuse[1],mat->diffuse[2],alpha);
    osg::Vec4 
specular(mat->specular[0],mat->specular[1],mat->specular[2],alpha);
    specular *= mat->shin_strength;

    float shininess = mat->shininess;
    material->setName(mat->name);
    material->setAmbient(osg::Material::FRONT_AND_BACK,ambient);
    material->setDiffuse(osg::Material::FRONT_AND_BACK,diffuse);
    material->setSpecular(osg::Material::FRONT_AND_BACK,specular);
    material->setShininess(osg::Material::FRONT_AND_BACK,shininess*128.0f);

    stateset->setAttribute(material);

    bool textureTransparancy=false;
    osg::Texture2D* texture1_map = 
createTexture(&(mat->texture1_map),"texture1_map",textureTransparancy, options);
    if (texture1_map)
    {
        
stateset->setTextureAttributeAndModes(0,texture1_map,osg::StateAttribute::ON);
        
        if (!textureTransparancy)
        {        
            // from an email from Eric Hamil, September 30, 2003.
            // According to the 3DS spec, and other
            // software (like Max, Lightwave, and Deep Exploration) a 3DS 
material that has
            // a non-white diffuse base color and a 100% opaque bitmap texture, 
will show the
            // texture with no influence from the base color.
            
            // so we'll override material back to white.
            // and no longer require the decal hack below...
#if 0
            // Eric original fallback
            osg::Vec4 white(1.0f,1.0f,1.0f,alpha);
            material->setAmbient(osg::Material::FRONT_AND_BACK,white);
            material->setDiffuse(osg::Material::FRONT_AND_BACK,white);
            material->setSpecular(osg::Material::FRONT_AND_BACK,white);
#else
            // try alternative to avoid saturating with white
            // setting white as per OpenGL defaults.
            
material->setAmbient(osg::Material::FRONT_AND_BACK,osg::Vec4(0.2f,0.2f,0.2f,alpha));
            
material->setDiffuse(osg::Material::FRONT_AND_BACK,osg::Vec4(0.8f,0.8f,0.8f,alpha));
            
material->setSpecular(osg::Material::FRONT_AND_BACK,osg::Vec4(0.0f,0.0f,0.0f,alpha));
#endif            
        }
        
// no longer required...        
//         bool decal = false;
//         
//         // not sure exactly how to interpret what is best for .3ds
//         // but the default text env MODULATE doesn't work well, and
//         // DECAL seems to work better.
//         osg::TexEnv* texenv = new osg::TexEnv;
//         if (decal)
//         {
//             texenv->setMode(osg::TexEnv::DECAL);
//         }
//         else
//         {
//             texenv->setMode(osg::TexEnv::MODULATE);
//         }
//        stateset->setTextureAttribute(0,texenv);
    }

    if (transparency>0.0f || textureTransparancy || mat->opacity_map.flags!=0)
    {
        stateset->setMode(GL_BLEND,osg::StateAttribute::ON);
        stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
    }

/*
    osg::ref_ptr<osg::Texture> texture1_mask = 
createTexture(&(mat->texture1_mask),"texture1_mask",textureTransparancy);
    osg::ref_ptr<osg::Texture> texture2_map = 
createTexture(&(mat->texture2_map),"texture2_map",textureTransparancy);
    osg::ref_ptr<osg::Texture> texture2_mask = 
createTexture(&(mat->texture2_mask),"texture2_mask",textureTransparancy);
    osg::ref_ptr<osg::Texture> opacity_map = 
createTexture(&(mat->opacity_map),"opacity_map",textureTransparancy);
    osg::ref_ptr<osg::Texture> opacity_mask = 
createTexture(&(mat->opacity_mask),"opacity_mask",textureTransparancy);
    osg::ref_ptr<osg::Texture> bump_map = 
createTexture(&(mat->bump_map),"bump_map",textureTransparancy);
    osg::ref_ptr<osg::Texture> bump_mask = 
createTexture(&(mat->bump_mask),"bump_mask",textureTransparancy);
    osg::ref_ptr<osg::Texture> specular_map = 
createTexture(&(mat->specular_map),"specular_map",textureTransparancy);
    osg::ref_ptr<osg::Texture> specular_mask = 
createTexture(&(mat->specular_mask),"specular_mask",textureTransparancy);
    osg::ref_ptr<osg::Texture> shininess_map = 
createTexture(&(mat->shininess_map),"shininess_map",textureTransparancy);
    osg::ref_ptr<osg::Texture> shininess_mask = 
createTexture(&(mat->shininess_mask),"shininess_mask",textureTransparancy);
    osg::ref_ptr<osg::Texture> self_illum_map = 
createTexture(&(mat->self_illum_map),"self_illum_map",textureTransparancy);
    osg::ref_ptr<osg::Texture> self_illum_mask = 
createTexture(&(mat->self_illum_mask),"self_illum_mask",textureTransparancy);
    osg::ref_ptr<osg::Texture> reflection_map = 
createTexture(&(mat->reflection_map),"reflection_map",textureTransparancy);
    osg::ref_ptr<osg::Texture> reflection_mask = 
createTexture(&(mat->reflection_mask),"reflection_mask",textureTransparancy);
*/
    return stateset;
}

_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org

Reply via email to