#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class ReaderWriter3DS : public osgDB::ReaderWriter { public: ReaderWriter3DS(); virtual const char* className() { return "3DS Auto Studio Reader"; } virtual bool acceptsExtension(const std::string& extension) { return extension=="3ds"; } virtual ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options*); typedef std::vector FaceList; typedef std::map GeoStateMap; protected: osg::Texture* createTexture(Lib3dsTextureMap *texture,const char* label,bool& transparancy); osg::StateSet* createStateSet(Lib3dsMaterial *materials); osg::GeoSet* createGeoSet(Lib3dsMesh *meshes,FaceList& faceList); std::string _directory; bool _useSmoothingGroups; bool _usePerVertexNormals; }; // now register with Registry to instantiate the above // reader/writer. osgDB::RegisterReaderWriterProxy g_readerWriter_3DS_Proxy; ReaderWriter3DS::ReaderWriter3DS() { _useSmoothingGroups = true; _usePerVertexNormals = true; } osgDB::ReaderWriter::ReadResult ReaderWriter3DS::readNode(const std::string& fileName, const osgDB::ReaderWriter::Options*) { Lib3dsFile *f = lib3ds_file_load(fileName.c_str()); if (f==NULL) return ReadResult::FILE_NOT_HANDLED; _directory = osgDB::getFilePath(fileName); osg::Group* group = new osg::Group; group->setName(fileName); typedef std::map StateSetMap; StateSetMap drawStateMap; for (Lib3dsMaterial *mat=f->materials; mat; mat=mat->next) { drawStateMap[mat->name] = createStateSet(mat); } for (Lib3dsMesh *mesh=f->meshes; mesh; mesh=mesh->next) { typedef std::vector FaceList; typedef std::map MaterialFaceMap; MaterialFaceMap materialFaceMap; for (unsigned int i=0; ifaces; ++i) { materialFaceMap[mesh->faceL[i].material].push_back(i); } if (materialFaceMap.empty()) { osg::notify(osg::NOTICE)<<"Warning : no triangles assigned to mesh '"<name<<"'"<< std::endl; } 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 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 geoset // to ensure the vertices on adjacent groups // don't get shared. FaceList& smoothFaceMap = sitr->second; osg::GeoSet* geoset = createGeoSet(mesh,smoothFaceMap); if (geoset) { geoset->setStateSet(drawStateMap[itr->first]); geode->addDrawable(geoset); } } } else // ignore smoothing groups. { osg::GeoSet* geoset = createGeoSet(mesh,faceList); if (geoset) { geoset->setStateSet(drawStateMap[itr->first]); geode->addDrawable(geoset); } } } group->addChild(geode); } } lib3ds_file_free(f); return group; } osg::Texture* ReaderWriter3DS::createTexture(Lib3dsTextureMap *texture,const char* label,bool& transparancy) { if (texture && *(texture->name)) { std::string fileName = osgDB::findFileInDirectory(texture->name,_directory,true); if (fileName.empty()) { osg::notify(osg::WARN) << "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) << " '"<name<<"'"<< std::endl; osg::notify(osg::DEBUG_INFO) << " texture flag "<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::Image* osg_image = osgDB::readImageFile(fileName.c_str()); if (osg_image==NULL) { osg::notify(osg::NOTICE) << "Warning: Cannot create texture "<name<< std::endl; return NULL; } osg::Texture* osg_texture = new osg::Texture; osg_texture->setImage(osg_image); // does the texture support transparancy? transparancy = ((texture->flags)&LIB3DS_ALPHA_SOURCE)!=0; // what is the wrap mode of the texture. osg::Texture::WrapMode wm = ((texture->flags)&LIB3DS_NO_TILE) ? osg::Texture::CLAMP : wm=osg::Texture::REPEAT; osg_texture->setWrap(osg::Texture::WRAP_S,wm); osg_texture->setWrap(osg::Texture::WRAP_T,wm); osg_texture->setWrap(osg::Texture::WRAP_R,wm); // bilinear. osg_texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR_MIPMAP_NEAREST); return osg_texture; } else return NULL; } osg::StateSet* ReaderWriter3DS::createStateSet(Lib3dsMaterial *mat) { 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); float shininess = mat->shininess; 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); stateset->setAttribute(material); bool decal = false; bool textureTransparancy=false; osg::Texture* texture1_map = createTexture(&(mat->texture1_map),"texture1_map",textureTransparancy); if (texture1_map) { stateset->setAttributeAndModes(texture1_map,osg::StateAttribute::ON); // 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->setAttribute(texenv); } if (transparency>0.0f || textureTransparancy) { stateset->setMode(GL_BLEND,osg::StateAttribute::ON); stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); } /* osg::ref_ptr texture1_mask = createTexture(&(mat->texture1_mask),"texture1_mask",textureTransparancy); osg::ref_ptr texture2_map = createTexture(&(mat->texture2_map),"texture2_map",textureTransparancy); osg::ref_ptr texture2_mask = createTexture(&(mat->texture2_mask),"texture2_mask",textureTransparancy); osg::ref_ptr opacity_map = createTexture(&(mat->opacity_map),"opacity_map",textureTransparancy); osg::ref_ptr opacity_mask = createTexture(&(mat->opacity_mask),"opacity_mask",textureTransparancy); osg::ref_ptr bump_map = createTexture(&(mat->bump_map),"bump_map",textureTransparancy); osg::ref_ptr bump_mask = createTexture(&(mat->bump_mask),"bump_mask",textureTransparancy); osg::ref_ptr specular_map = createTexture(&(mat->specular_map),"specular_map",textureTransparancy); osg::ref_ptr specular_mask = createTexture(&(mat->specular_mask),"specular_mask",textureTransparancy); osg::ref_ptr shininess_map = createTexture(&(mat->shininess_map),"shininess_map",textureTransparancy); osg::ref_ptr shininess_mask = createTexture(&(mat->shininess_mask),"shininess_mask",textureTransparancy); osg::ref_ptr self_illum_map = createTexture(&(mat->self_illum_map),"self_illum_map",textureTransparancy); osg::ref_ptr self_illum_mask = createTexture(&(mat->self_illum_mask),"self_illum_mask",textureTransparancy); osg::ref_ptr reflection_map = createTexture(&(mat->reflection_map),"reflection_map",textureTransparancy); osg::ref_ptr reflection_mask = createTexture(&(mat->reflection_mask),"reflection_mask",textureTransparancy); */ return stateset; } osg::GeoSet* ReaderWriter3DS::createGeoSet(Lib3dsMesh *m,FaceList& faceList) { osg::GeoSet* geoset = new osg::GeoSet; unsigned int i; std::vector orig2NewMapping; for(i=0;ipoints;++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++; } osg::Vec3* osg_coords = new osg::Vec3[noVertex]; Lib3dsVector c; // Note for Ben, 1st Jan 2002 from Robert. // added code to use the matrix attached with each lib3dsMesh to // multiply each coord on the mesh before passing to the OSG. // however, this caused plenty of problems as applying the matrices // moved the objects away from the original positions. bool useMatrix = false; if (useMatrix) { lib3ds_matrix_dump(m->matrix); } for (i=0; ipoints; ++i) { // lib3ds_vector_transform(pos, m->matrix, m->pointL[i].pos); if (orig2NewMapping[i]>=0) { if (useMatrix) { lib3ds_vector_transform(c,m->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]); } } } osg::Vec2* osg_tcoords = NULL; if (m->texels>0) { if (m->texels==m->points) { osg_tcoords = new osg::Vec2[noVertex]; for (i=0; itexels; ++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 ("<texels<<") != m->points ("<points<<")"<< std::endl; } } // handle normals. osg::Vec3* osg_normals; if (_usePerVertexNormals) { osg_normals = new osg::Vec3[noVertex]; // initialize normal list to zero's. for (i=0; ifaceL[*fitr]; *(index_ptr++) = orig2NewMapping[face.points[0]]; *(index_ptr++) = orig2NewMapping[face.points[1]]; *(index_ptr++) = orig2NewMapping[face.points[2]]; if (_usePerVertexNormals) { 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]);; } else { *(normal_ptr++) = osg::Vec3(face.normal[0],face.normal[1],face.normal[2]); } } if (_usePerVertexNormals) { // normalize the normal list to unit length normals. for (i=0; isetNormals(osg_normals,osg_indices); geoset->setNormalBinding(osg::GeoSet::BIND_PERVERTEX); } else { geoset->setNormals(osg_normals); geoset->setNormalBinding(osg::GeoSet::BIND_PERPRIM); } geoset->setCoords(osg_coords,osg_indices); if (osg_tcoords) { geoset->setTextureBinding(osg::GeoSet::BIND_PERVERTEX); geoset->setTextureCoords(osg_tcoords,osg_indices); } geoset->setPrimType(osg::GeoSet::TRIANGLES); geoset->setNumPrims(faceList.size()); return geoset; }