From 66b31b677a9cd55b67b7fcd460cf6e51c2bef05b Mon Sep 17 00:00:00 2001 From: Paul MARTZ Date: Wed, 1 Jun 2011 00:58:43 +0000 Subject: [PATCH] 2.8 branch: Backport of 3ds plugin from trunk. --- src/osgPlugins/3ds/ReaderWriter3DS.cpp | 317 +++++---- src/osgPlugins/3ds/WriterCompareTriangle.cpp | 276 ++++---- src/osgPlugins/3ds/WriterNodeVisitor.cpp | 675 ++++++++++++------- src/osgPlugins/3ds/WriterNodeVisitor.h | 449 ++++++------ src/osgPlugins/3ds/lib3ds/lib3ds.h | 2 - src/osgPlugins/3ds/lib3ds/lib3ds_file.c | 5 +- src/osgPlugins/3ds/lib3ds/lib3ds_impl.h | 4 + src/osgPlugins/3ds/lib3ds/lib3ds_io.c | 81 +-- src/osgPlugins/3ds/lib3ds/lib3ds_node.c | 2 +- src/osgPlugins/3ds/lib3ds/lib3ds_track.c | 3 +- 10 files changed, 995 insertions(+), 819 deletions(-) diff --git a/src/osgPlugins/3ds/ReaderWriter3DS.cpp b/src/osgPlugins/3ds/ReaderWriter3DS.cpp index 364fb9e55..3956ceb3a 100644 --- a/src/osgPlugins/3ds/ReaderWriter3DS.cpp +++ b/src/osgPlugins/3ds/ReaderWriter3DS.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include @@ -182,18 +184,29 @@ protected: bool createFileObject(const osg::Node& node, Lib3dsFile * file3ds,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const; + /// An OSG state set with the original 3DS material attached (used to get info such as UV scaling & offset) + struct StateSetInfo + { + StateSetInfo(osg::StateSet * stateset=NULL, Lib3dsMaterial * lib3dsmat=NULL) : stateset(stateset), lib3dsmat(lib3dsmat) {} + StateSetInfo(const StateSetInfo & v) : stateset(v.stateset), lib3dsmat(v.lib3dsmat) {} + StateSetInfo & operator=(const StateSetInfo & v) { stateset=v.stateset; lib3dsmat=v.lib3dsmat; return *this; } + + osg::StateSet * stateset; + Lib3dsMaterial * lib3dsmat; + }; + class ReaderObject { public: ReaderObject(const osgDB::ReaderWriter::Options* options); - typedef std::vector StateSetMap; + typedef std::vector StateSetMap; typedef std::vector FaceList; typedef std::map GeoStateMap; osg::Texture2D* createTexture(Lib3dsTextureMap *texture,const char* label,bool& transparancy); - osg::StateSet* createStateSet(Lib3dsMaterial *materials); - osg::Drawable* createDrawable(Lib3dsMesh *meshes,FaceList& faceList, const osg::Matrix * matrix); + StateSetInfo createStateSet(Lib3dsMaterial *materials); + osg::Drawable* createDrawable(Lib3dsMesh *meshes,FaceList& faceList, const osg::Matrix * matrix, StateSetInfo & ssi); std::string _directory; bool _useSmoothingGroups; @@ -201,15 +214,18 @@ protected: // MIKEC osg::Node* processMesh(StateSetMap& drawStateMap,osg::Group* parent,Lib3dsMesh* mesh, const osg::Matrix * matrix); - osg::Node* processNode(StateSetMap drawStateMap,Lib3dsFile *f,Lib3dsNode *node); + osg::Node* processNode(StateSetMap& drawStateMap,Lib3dsFile *f,Lib3dsNode *node); private: const osgDB::ReaderWriter::Options* options; bool noMatrixTransforms; ///< Should the plugin apply matrices into the mesh vertices ("old behaviour"), instead of restoring matrices ("new behaviour")? bool checkForEspilonIdentityMatrices; bool restoreMatrixTransformsNoMeshes; typedef std::map SmoothingFaceMap; - void addDrawableFromFace(osg::Geode * geode, FaceList & faceList, Lib3dsMesh * mesh, const osg::Matrix * matrix, osg::StateSet * stateSet); - }; + void addDrawableFromFace(osg::Geode * geode, FaceList & faceList, Lib3dsMesh * mesh, const osg::Matrix * matrix, StateSetInfo & ssi); + + typedef std::map > TexturesMap; // Should be an unordered map (faster) + TexturesMap texturesMap; +}; }; // now register with Registry to instantiate the above @@ -221,29 +237,28 @@ ReaderWriter3DS::ReaderWriter3DS() supportsExtension("3ds","3D Studio model format"); //supportsOption("OutputTextureFiles","Write out the texture images to file"); //supportsOption("flipTexture", "flip texture upside-down"); - supportsOption("extended3dsFilePaths", "Keeps long texture filenames (not 8.3) when exporting 3DS, but can lead to compatibility problems."); - supportsOption("noMatrixTransforms", "Set the plugin to apply matrices into the mesh vertices (\"old behaviour\") instead of restoring them (\"new behaviour\"). You may use this option to avoid a few rounding errors."); - supportsOption("checkForEspilonIdentityMatrices", "If not set, then consider \"almost identity\" matrices to be identity ones (in case of rounding errors)."); - supportsOption("restoreMatrixTransformsNoMeshes", "Makes an exception to the behaviour when 'noMatrixTransforms' is not set for mesh instances. When a mesh instance has a transform on it, the reader creates a MatrixTransform above the Geode. If you don't want the hierarchy to be modified, then you can use this option to merge the transform into vertices."); - setByteOrder(); + supportsOption("extended3dsFilePaths", "(Write option) Keeps long texture filenames (not 8.3) when exporting 3DS, but can lead to compatibility problems."); + supportsOption("noMatrixTransforms", "(Read option) 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."); + supportsOption("checkForEspilonIdentityMatrices", "(Read option) If not set, then consider \"almost identity\" matrices to be identity ones (in case of rounding errors)."); + supportsOption("restoreMatrixTransformsNoMeshes", "(Read option) Makes an exception to the behaviour when 'noMatrixTransforms' is not set for mesh instances. When a mesh instance has a transform on it, the reader creates a MatrixTransform above the Geode. If you don't want the hierarchy to be modified, then you can use this option to merge the transform into vertices."); #if 0 - osg::notify(osg::NOTICE)<<"3DS reader sizes:"<second; - osg::ref_ptr drawable = createDrawable(mesh,smoothFaceMap,matrix); + osg::ref_ptr drawable = createDrawable(mesh,smoothFaceMap,matrix,ssi); if (drawable.valid()) { - if (stateSet) - drawable->setStateSet(stateSet); + if (ssi.stateset) + drawable->setStateSet(ssi.stateset); geode->addDrawable(drawable.get()); } } } else // ignore smoothing groups. { - osg::ref_ptr drawable = createDrawable(mesh,faceList,matrix); + osg::ref_ptr drawable = createDrawable(mesh,faceList,matrix,ssi); if (drawable.valid()) { - if (stateSet) - drawable->setStateSet(stateSet); + if (ssi.stateset) + drawable->setStateSet(ssi.stateset); geode->addDrawable(drawable.get()); } } @@ -429,8 +444,8 @@ osg::Node* ReaderWriter3DS::ReaderObject::processMesh(StateSetMap& drawStateMap, } if (materialFaceMap.empty() && defaultMaterialFaceList.empty()) { - osg::notify(osg::NOTICE)<<"Warning : no triangles assigned to mesh '"<name<<"'"<< std::endl; - //osg::notify(osg::INFO) << "No material assigned to mesh '" << mesh->name << "'" << std::endl; + OSG_NOTICE<<"Warning : no triangles assigned to mesh '"<name<<"'"<< std::endl; + //OSG_INFO << "No material assigned to mesh '" << mesh->name << "'" << std::endl; return NULL; } else @@ -439,7 +454,8 @@ osg::Node* ReaderWriter3DS::ReaderObject::processMesh(StateSetMap& drawStateMap, geode->setName(mesh->name); if (!defaultMaterialFaceList.empty()) { - addDrawableFromFace(geode, defaultMaterialFaceList, mesh, matrix, NULL); + StateSetInfo emptySSI; + addDrawableFromFace(geode, defaultMaterialFaceList, mesh, matrix, emptySSI); } for(unsigned int imat=0; imattype == LIB3DS_NODE_MESH_INSTANCE) ? reinterpret_cast(node) : NULL; @@ -483,13 +499,14 @@ osg::Node* ReaderWriter3DS::ReaderObject::processNode(StateSetMap drawStateMap,L // Retreive LOCAL transform static const osg::Matrix::value_type MATRIX_EPSILON = 1e-10; - osg::Matrix osgNodeMatrix( copyLib3dsMatrixToOsgMatrix(node->matrix) ); - + osg::Matrix osgWorldToNodeMatrix( copyLib3dsMatrixToOsgMatrix(node->matrix) ); + osg::Matrix osgWorldToParentNodeMatrix; if (node->parent) { // Matrices evaluated by lib3DS are multiplied by parents' ones - osgNodeMatrix *= osg::Matrix::inverse( copyLib3dsMatrixToOsgMatrix(node->parent->matrix) ); + osgWorldToParentNodeMatrix = copyLib3dsMatrixToOsgMatrix(node->parent->matrix); } + osg::Matrix osgNodeMatrix( osgWorldToNodeMatrix * osg::Matrix::inverse(osgWorldToParentNodeMatrix) ); // Test if we should create an intermediate Group (or MatrixTransform) and which matrix we should apply to the vertices osg::Group* group = NULL; @@ -501,20 +518,20 @@ osg::Node* ReaderWriter3DS::ReaderObject::processNode(StateSetMap drawStateMap,L osg::Matrix meshMat; if (mesh) { - if (!noMatrixTransforms) - { + if (!noMatrixTransforms) + { // 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 - if (!pivoted) { - meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)); - } else { + if (pivoted) { meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * osg::Matrix::translate(-pivot); + } else { + meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)); } } else { if (pivoted) { - meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * osg::Matrix::translate(-pivot) * copyLib3dsMatrixToOsgMatrix(node->matrix); + meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * osg::Matrix::translate(-pivot) * osgWorldToNodeMatrix; } else { - meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * copyLib3dsMatrixToOsgMatrix(node->matrix); + meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * osgWorldToNodeMatrix; // ==Identity when not pivoted? } osgNodeMatrix = osg::Matrix::identity(); // Not sure it's useful, but it's harmless ;) } @@ -547,6 +564,8 @@ osg::Node* ReaderWriter3DS::ReaderObject::processNode(StateSetMap drawStateMap,L if (node->type == LIB3DS_NODE_MESH_INSTANCE) group->setName(reinterpret_cast(node)->instance_name); } + else if (node->type == LIB3DS_NODE_MESH_INSTANCE && strlen(reinterpret_cast(node)->instance_name) != 0) + group->setName(reinterpret_cast(node)->instance_name); else group->setName(node->name); @@ -651,7 +670,7 @@ static void fileio_log_func(void *self, Lib3dsLogLevel level, int indent, const else if (level == LIB3DS_LOG_WARN) l = osg::NOTICE; else if (level == LIB3DS_LOG_INFO) l = osg::INFO; else if (level == LIB3DS_LOG_DEBUG) l = osg::DEBUG_INFO; - osg::notify(l) << msg << std::endl; + OSG_NOTIFY(l) << msg << std::endl; } @@ -703,7 +722,7 @@ osgDB::ReaderWriter::ReadResult ReaderWriter3DS::readNode(const std::string& fil // Do not use the lib3ds_file_open() as: // 1. It relies on FILE* instead of iostreams (less safe) // 2. It doesn't allow us to set a custom log output - std::ifstream fin(fileName.c_str(), std::ios_base::in | std::ios_base::binary); + osgDB::ifstream fin(fileName.c_str(), std::ios_base::in | std::ios_base::binary); if (!fin.good()) return ReadResult::ERROR_IN_READING_FILE; return doReadNode(fin, options, fileName); /* @@ -739,7 +758,7 @@ osgDB::ReaderWriter::ReadResult ReaderWriter3DS::constructFrom3dsFile(Lib3dsFile ReaderObject::StateSetMap drawStateMap; unsigned int numMaterials = f->nmaterials; - drawStateMap.insert(drawStateMap.begin(), numMaterials, NULL); // Setup the map + drawStateMap.insert(drawStateMap.begin(), numMaterials, StateSetInfo()); // Setup the map for (unsigned int imat=0; imatmaterials[imat]; @@ -769,7 +788,7 @@ osgDB::ReaderWriter::ReadResult ReaderWriter3DS::constructFrom3dsFile(Lib3dsFile // 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; + OSG_WARN<<"Warning: in 3ds loader: file has no nodes, traversing by meshes instead"<< std::endl; traverse_nodes=true; } @@ -803,8 +822,8 @@ osgDB::ReaderWriter::ReadResult ReaderWriter3DS::constructFrom3dsFile(Lib3dsFile if (osg::getNotifyLevel()>=osg::INFO) { - osg::notify(osg::NOTICE) << "Final OSG node structure looks like this:"<< endl; - PrintVisitor pv(osg::notify(osg::NOTICE)); + OSG_INFO << "Final OSG node structure looks like this:"<< endl; + PrintVisitor pv(osg::notify(osg::INFO)); group->accept(pv); } @@ -814,7 +833,7 @@ osgDB::ReaderWriter::ReadResult ReaderWriter3DS::constructFrom3dsFile(Lib3dsFile /** use matrix to pretransform geometry, or NULL to do nothing */ -osg::Drawable* ReaderWriter3DS::ReaderObject::createDrawable(Lib3dsMesh *m,FaceList& faceList, const osg::Matrix * matrix) +osg::Drawable* ReaderWriter3DS::ReaderObject::createDrawable(Lib3dsMesh *m,FaceList& faceList, const osg::Matrix * matrix, StateSetInfo & ssi) { osg::Geometry * geom = new osg::Geometry; unsigned int i; @@ -869,9 +888,24 @@ osg::Drawable* ReaderWriter3DS::ReaderObject::createDrawable(Lib3dsMesh *m,FaceL { osg::ref_ptr osg_tcoords = new osg::Vec2Array(noVertex); geom->setTexCoordArray(0, osg_tcoords.get()); + + // Texture 0 parameters (only one texture supported for now) + float scaleU(1.f), scaleV(1.f); + float offsetU(0.f), offsetV(0.f); + if (ssi.lib3dsmat && *(ssi.lib3dsmat->texture1_map.name)) // valid texture = name not empty + { + Lib3dsTextureMap & tex3ds = ssi.lib3dsmat->texture1_map; + scaleU = tex3ds.scale[0]; + scaleV = tex3ds.scale[1]; + offsetU = tex3ds.offset[0]; + offsetV = tex3ds.offset[1]; + if (tex3ds.rotation != 0) OSG_NOTICE << "3DS texture rotation not supported yet" << std::endl; + //TODO: tint_1, tint_2, tint_r, tint_g, tint_b + } + for (i=0; invertices; ++i) { - if (orig2NewMapping[i]>=0) (*osg_tcoords)[orig2NewMapping[i]].set(m->texcos[i][0],m->texcos[i][1]); + if (orig2NewMapping[i]>=0) (*osg_tcoords)[orig2NewMapping[i]].set(m->texcos[i][0]*scaleU + offsetU, m->texcos[i][1]*scaleV + offsetV); } } @@ -958,18 +992,26 @@ osg::Drawable* ReaderWriter3DS::ReaderObject::createDrawable(Lib3dsMesh *m,FaceL } -osg::Texture2D* ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap *texture,const char* label,bool& transparancy) +osg::Texture2D* ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap *texture,const char* label,bool& transparency) { if (texture && *(texture->name)) { - osg::notify(osg::INFO)<<"texture->name="<name<<", _directory="<<_directory<name="<name<<", _directory="<<_directory<name); + if (itTex != texturesMap.end()) { + OSG_DEBUG << "Texture '" << texture->name << "' found in cache." << std::endl; + return itTex->second.get(); + } + + // Texture not in cache: locate and load. 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); - osg::notify(osg::INFO)<<"texture->name="<name<<", _directory="<<_directory<name="<name<<", _directory="<<_directory<name<<"' not found"<< std::endl; + 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"; } + if (label) { OSG_DEBUG << label; } + else { OSG_DEBUG << "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_TEXTURE_DECALE "<<((texture->flags)&LIB3DS_TEXTURE_DECALE)<< std::endl; - osg::notify(osg::DEBUG_INFO) << " LIB3DS_TEXTURE_MIRROR "<<((texture->flags)&LIB3DS_TEXTURE_MIRROR)<< std::endl; - osg::notify(osg::DEBUG_INFO) << " LIB3DS_TEXTURE_NEGATE "<<((texture->flags)&LIB3DS_TEXTURE_NEGATE)<< std::endl; - osg::notify(osg::DEBUG_INFO) << " LIB3DS_TEXTURE_NO_TILE "<<((texture->flags)&LIB3DS_TEXTURE_NO_TILE)<< std::endl; - osg::notify(osg::DEBUG_INFO) << " LIB3DS_TEXTURE_SUMMED_AREA "<<((texture->flags)&LIB3DS_TEXTURE_SUMMED_AREA)<< std::endl; - osg::notify(osg::DEBUG_INFO) << " LIB3DS_TEXTURE_ALPHA_SOURCE "<<((texture->flags)&LIB3DS_TEXTURE_ALPHA_SOURCE)<< std::endl; - osg::notify(osg::DEBUG_INFO) << " LIB3DS_TEXTURE_TINT "<<((texture->flags)&LIB3DS_TEXTURE_TINT)<< std::endl; - osg::notify(osg::DEBUG_INFO) << " LIB3DS_TEXTURE_IGNORE_ALPHA "<<((texture->flags)&LIB3DS_TEXTURE_IGNORE_ALPHA)<< std::endl; - osg::notify(osg::DEBUG_INFO) << " LIB3DS_TEXTURE_RGB_TINT "<<((texture->flags)&LIB3DS_TEXTURE_RGB_TINT)<< std::endl; + OSG_DEBUG << " '"<name<<"'"<< std::endl; + OSG_DEBUG << " texture flag "<flags<< std::endl; + OSG_DEBUG << " LIB3DS_TEXTURE_DECALE "<<((texture->flags)&LIB3DS_TEXTURE_DECALE)<< std::endl; + OSG_DEBUG << " LIB3DS_TEXTURE_MIRROR "<<((texture->flags)&LIB3DS_TEXTURE_MIRROR)<< std::endl; + OSG_DEBUG << " LIB3DS_TEXTURE_NEGATE "<<((texture->flags)&LIB3DS_TEXTURE_NEGATE)<< std::endl; + OSG_DEBUG << " LIB3DS_TEXTURE_NO_TILE "<<((texture->flags)&LIB3DS_TEXTURE_NO_TILE)<< std::endl; + OSG_DEBUG << " LIB3DS_TEXTURE_SUMMED_AREA "<<((texture->flags)&LIB3DS_TEXTURE_SUMMED_AREA)<< std::endl; + OSG_DEBUG << " LIB3DS_TEXTURE_ALPHA_SOURCE "<<((texture->flags)&LIB3DS_TEXTURE_ALPHA_SOURCE)<< std::endl; + OSG_DEBUG << " LIB3DS_TEXTURE_TINT "<<((texture->flags)&LIB3DS_TEXTURE_TINT)<< std::endl; + OSG_DEBUG << " LIB3DS_TEXTURE_IGNORE_ALPHA "<<((texture->flags)&LIB3DS_TEXTURE_IGNORE_ALPHA)<< std::endl; + OSG_DEBUG << " LIB3DS_TEXTURE_RGB_TINT "<<((texture->flags)&LIB3DS_TEXTURE_RGB_TINT)<< std::endl; osg::ref_ptr osg_image = osgDB::readRefImageFile(fileName.c_str(), options); //Absolute Path if (!osg_image.valid()) { - osg::notify(osg::NOTICE) << "Warning: Cannot create texture "<name<< std::endl; + OSG_NOTICE << "Warning: Cannot create texture "<name<< std::endl; return NULL; } if (osg_image->getFileName().empty()) // it should be done in OSG with osgDB::readRefImageFile(fileName.c_str()); @@ -1013,7 +1055,7 @@ osg::Texture2D* ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap * osg_texture->setImage(osg_image.get()); osg_texture->setName(texture->name); // does the texture support transparancy? - transparancy = ((texture->flags)&LIB3DS_TEXTURE_ALPHA_SOURCE)!=0; + //transparency = ((texture->flags)&LIB3DS_TEXTURE_ALPHA_SOURCE)!=0; // what is the wrap mode of the texture. osg::Texture2D::WrapMode wm = ((texture->flags)&LIB3DS_TEXTURE_NO_TILE) ? @@ -1025,6 +1067,8 @@ osg::Texture2D* ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap * // bilinear. osg_texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR_MIPMAP_NEAREST); + // Insert in cache map + texturesMap.insert(TexturesMap::value_type(texture->name, osg_texture)); return osg_texture; } else @@ -1032,38 +1076,42 @@ osg::Texture2D* ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap * } -osg::StateSet* ReaderWriter3DS::ReaderObject::createStateSet(Lib3dsMaterial *mat) +ReaderWriter3DS::StateSetInfo ReaderWriter3DS::ReaderObject::createStateSet(Lib3dsMaterial *mat) { - if (mat==NULL) return NULL; + if (mat==NULL) return StateSetInfo(); + + bool textureTransparency=false; + bool transparency = false; + float alpha = 1.0f - mat->transparency; + int unit = 0; 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); + osg::Vec3 ambient(mat->ambient[0],mat->ambient[1],mat->ambient[2]); + osg::Vec3 diffuse(mat->diffuse[0],mat->diffuse[1],mat->diffuse[2]); + osg::Vec3 specular(mat->specular[0],mat->specular[1],mat->specular[2]); specular *= mat->shin_strength; + float shininess = mat->shininess*128.0f; - 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); + // diffuse + osg::Texture2D* texture1_map = createTexture(&(mat->texture1_map),"texture1_map",textureTransparency); if (texture1_map) { - stateset->setTextureAttributeAndModes(0,texture1_map,osg::StateAttribute::ON); + stateset->setTextureAttributeAndModes(unit, texture1_map, osg::StateAttribute::ON); - if (!textureTransparancy) + double factor = mat->texture1_map.percent; + if(factor < 1.0) + { + osg::TexEnvCombine* texenv = new osg::TexEnvCombine(); + texenv->setCombine_RGB(osg::TexEnvCombine::MODULATE); + texenv->setSource0_RGB(osg::TexEnvCombine::TEXTURE); + texenv->setSource1_RGB(osg::TexEnvCombine::PREVIOUS); + texenv->setSource2_RGB(osg::TexEnvCombine::CONSTANT); + texenv->setConstantColor(osg::Vec4(factor, factor, factor, factor)); + stateset->setTextureAttributeAndModes(unit, texenv, osg::StateAttribute::ON); + } + else { // from an email from Eric Hamil, September 30, 2003. // According to the 3DS spec, and other @@ -1082,32 +1130,55 @@ osg::StateSet* ReaderWriter3DS::ReaderObject::createStateSet(Lib3dsMaterial *mat #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)); + ambient.set(0.2f,0.2f,0.2f); + diffuse.set(0.8f,0.8f,0.8f); + specular.set(0.0f,0.0f,0.0f); #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); + unit++; } - if (transparency>0.0f || textureTransparancy) + // opacity + osg::Texture* opacity_map = createTexture(&(mat->opacity_map),"opacity_map", textureTransparency); + if (opacity_map) { + if(texture1_map->getImage()->isImageTranslucent()) + { + transparency = true; + + stateset->setTextureAttributeAndModes(unit, opacity_map, osg::StateAttribute::ON); + + double factor = mat->opacity_map.percent; + + osg::TexEnvCombine* texenv = new osg::TexEnvCombine(); + texenv->setCombine_Alpha(osg::TexEnvCombine::INTERPOLATE); + texenv->setSource0_Alpha(osg::TexEnvCombine::TEXTURE); + texenv->setSource1_Alpha(osg::TexEnvCombine::PREVIOUS); + texenv->setSource2_Alpha(osg::TexEnvCombine::CONSTANT); + texenv->setConstantColor(osg::Vec4(factor, factor, factor, 1.0 - factor)); + stateset->setTextureAttributeAndModes(unit, texenv, osg::StateAttribute::ON); + + unit++; + } + else + { + osg::notify(WARN)<<"The plugin does not support images without alpha channel for opacity"<setName(mat->name); + material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4(ambient, alpha)); + material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(diffuse, alpha)); + material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4(specular, alpha)); + material->setShininess(osg::Material::FRONT_AND_BACK, shininess); + + stateset->setAttribute(material); + + if ((alpha < 1.0f) || transparency) + { + //stateset->setAttributeAndModes(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); stateset->setMode(GL_BLEND,osg::StateAttribute::ON); stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); } @@ -1129,7 +1200,7 @@ osg::StateSet* ReaderWriter3DS::ReaderObject::createStateSet(Lib3dsMaterial *mat 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; + return StateSetInfo(stateset, mat); } @@ -1139,7 +1210,7 @@ osgDB::ReaderWriter::WriteResult ReaderWriter3DS::writeNode(const osg::Node& nod if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED; osgDB::makeDirectoryForFile(fileName.c_str()); - std::ofstream fout(fileName.c_str(), std::ios_base::out | std::ios_base::binary); + osgDB::ofstream fout(fileName.c_str(), std::ios_base::out | std::ios_base::binary); if (!fout.good()) return WriteResult::ERROR_IN_WRITING_FILE; return doWriteNode(node, fout, options, fileName); /* @@ -1160,7 +1231,7 @@ osgDB::ReaderWriter::WriteResult ReaderWriter3DS::writeNode(const osg::Node& nod osgDB::ReaderWriter::WriteResult ReaderWriter3DS::writeNode(const osg::Node& node,std::ostream& fout,const Options* options) const { - //osg::notify(osg::WARN) << "!!WARNING!! 3DS write support is incomplete" << std::endl; + //OSG_WARN << "!!WARNING!! 3DS write support is incomplete" << std::endl; std::string optFileName; if (options) { diff --git a/src/osgPlugins/3ds/WriterCompareTriangle.cpp b/src/osgPlugins/3ds/WriterCompareTriangle.cpp index 3830cd459..e8c3332e0 100644 --- a/src/osgPlugins/3ds/WriterCompareTriangle.cpp +++ b/src/osgPlugins/3ds/WriterCompareTriangle.cpp @@ -1,138 +1,138 @@ -#include "WriterCompareTriangle.h" -#include - -WriterCompareTriangle::WriterCompareTriangle(const osg::Geode & geode, unsigned int nbVertices) : geode(geode) -{ - cutscene(nbVertices, geode.getBoundingBox()); -} - -bool -WriterCompareTriangle::operator()(const std::pair & t1, - const std::pair & t2) const -{ - const osg::Geometry *g = geode.getDrawable( t1.second )->asGeometry(); - - const osg::Vec3Array * vecs= static_cast(g->getVertexArray()); - const osg::BoundingBox::vec_type v1( (*vecs)[t1.first.t1] ); - - if (t1.second != t2.second) - { - const osg::Geometry *g = geode.getDrawable( t2.second )->asGeometry(); - vecs = static_cast(g->getVertexArray()); - }; - const osg::BoundingBox::vec_type v2( (*vecs)[t2.first.t1] ); - int val1 = inWhichBox(v1); - int val2 = inWhichBox(v2); - - return (val1 < val2); -} - -void -WriterCompareTriangle::setMaxMin(unsigned int & nbVerticesX, - unsigned int & nbVerticesY, - unsigned int & nbVerticesZ) const -{ - static const unsigned int min = 1; - static const unsigned int max = 5; // Number of blocks used to divide the scene (arbitrary but seems ok) - nbVerticesX = osg::clampBetween(nbVerticesX, min, max); - nbVerticesY = osg::clampBetween(nbVerticesY, min, max); - nbVerticesZ = osg::clampBetween(nbVerticesZ, min, max); -} - -void WriterCompareTriangle::cutscene(int nbVertices, const osg::BoundingBox & sceneBox) -{ - osg::BoundingBox::vec_type length = sceneBox._max - sceneBox._min; - - static const float k = 1.3f; // Arbitrary constant multiplier for density computation ("simulates" non-uniform point distributions) - // Computes "density" of points, and thus the number of blocks to divide the mesh into - unsigned int nbVerticesX = static_cast( (nbVertices * k) / (length.z() * length.y()) ); - unsigned int nbVerticesY = static_cast( (nbVertices * k) / (length.z() * length.x()) ); - unsigned int nbVerticesZ = static_cast( (nbVertices * k) / (length.x() * length.y()) ); - - setMaxMin (nbVerticesX, nbVerticesY, nbVerticesZ); // This function prevent from cutting the scene in too many blocs - - osg::notify(osg::INFO) - << "Cutting x by " << nbVerticesX << std::endl - << "Cutting y by " << nbVerticesY << std::endl - << "Cutting z by " << nbVerticesZ << std::endl; - - osg::BoundingBox::value_type blocX = length.x() / nbVerticesX; // These 3 lines set the size of a bloc in x, y and z - osg::BoundingBox::value_type blocY = length.y() / nbVerticesY; - osg::BoundingBox::value_type blocZ = length.z() / nbVerticesZ; - - boxList.reserve(nbVerticesX * nbVerticesY * nbVerticesZ); - short yinc = 1; - short xinc = 1; - unsigned int y = 0; - unsigned int x = 0; - for (unsigned int z = 0; z < nbVerticesZ; ++z) - { - while (x < nbVerticesX && x >= 0) - { - while (y < nbVerticesY && y >= 0) - { - osg::BoundingBox::value_type xMin = sceneBox.xMin() + x * blocX; - if (x == 0) //to prevent from mesh with no case - xMin -= 10; - - osg::BoundingBox::value_type yMin = sceneBox.yMin() + y * blocY; - if (y == 0) //to prevent from mesh with no case - yMin -= 10; - - osg::BoundingBox::value_type zMin = sceneBox.zMin() + z * blocZ; - if (z == 0) //to prevent from mesh with no case - zMin -= 10; - - osg::BoundingBox::value_type xMax = sceneBox.xMin() + (x + 1) * blocX; - if (x == nbVerticesX - 1) //to prevent from mesh with no case - xMax += 10; - - osg::BoundingBox::value_type yMax = sceneBox.yMin() + (y + 1) * blocY; - if (y == nbVerticesY - 1) //to prevent from mesh with no case - yMax += 10; - - osg::BoundingBox::value_type zMax = sceneBox.zMin() + (z + 1) * blocZ; - if (z == nbVerticesZ - 1) //to prevent from mesh with no case - zMax += 10; - - boxList.push_back(osg::BoundingBox(xMin, // Add a bloc to the list - yMin, - zMin, - xMax, - yMax, - zMax)); - y += yinc; - } - yinc = -yinc; - y += yinc; - x += xinc; - } - xinc = -xinc; - x += xinc; - } -} - -int -WriterCompareTriangle::inWhichBox(const osg::BoundingBox::value_type x, - const osg::BoundingBox::value_type y, - const osg::BoundingBox::value_type z) const -{ - for (unsigned int i = 0; i < boxList.size(); ++i) - { - if (x >= boxList[i].xMin() && - x < boxList[i].xMax() && - y >= boxList[i].yMin() && - y < boxList[i].yMax() && - z >= boxList[i].zMin() && - z < boxList[i].zMax()) - { - return i; - } - } - assert(false && "Point is not in any blocs"); - return 0; -} - -int WriterCompareTriangle::inWhichBox(const osg::BoundingBox::vec_type & point) const { - return inWhichBox(point.x(), point.y(), point.z()); -} +#include "WriterCompareTriangle.h" +#include + +WriterCompareTriangle::WriterCompareTriangle(const osg::Geode & geode, unsigned int nbVertices) : geode(geode) +{ + cutscene(nbVertices, geode.getBoundingBox()); +} + +bool +WriterCompareTriangle::operator()(const std::pair & t1, + const std::pair & t2) const +{ + const osg::Geometry *g = geode.getDrawable( t1.second )->asGeometry(); + + const osg::Vec3Array * vecs= static_cast(g->getVertexArray()); + const osg::BoundingBox::vec_type v1( (*vecs)[t1.first.t1] ); + + if (t1.second != t2.second) + { + const osg::Geometry *g = geode.getDrawable( t2.second )->asGeometry(); + vecs = static_cast(g->getVertexArray()); + }; + const osg::BoundingBox::vec_type v2( (*vecs)[t2.first.t1] ); + int val1 = inWhichBox(v1); + int val2 = inWhichBox(v2); + + return (val1 < val2); +} + +void +WriterCompareTriangle::setMaxMin(unsigned int & nbVerticesX, + unsigned int & nbVerticesY, + unsigned int & nbVerticesZ) const +{ + static const unsigned int min = 1; + static const unsigned int max = 5; // Number of blocks used to divide the scene (arbitrary but seems ok) + nbVerticesX = osg::clampBetween(nbVerticesX, min, max); + nbVerticesY = osg::clampBetween(nbVerticesY, min, max); + nbVerticesZ = osg::clampBetween(nbVerticesZ, min, max); +} + +void WriterCompareTriangle::cutscene(int nbVertices, const osg::BoundingBox & sceneBox) +{ + osg::BoundingBox::vec_type length = sceneBox._max - sceneBox._min; + + static const float k = 1.3f; // Arbitrary constant multiplier for density computation ("simulates" non-uniform point distributions) + // Computes "density" of points, and thus the number of blocks to divide the mesh into + unsigned int nbVerticesX = static_cast( (nbVertices * k) / (length.z() * length.y()) ); + unsigned int nbVerticesY = static_cast( (nbVertices * k) / (length.z() * length.x()) ); + unsigned int nbVerticesZ = static_cast( (nbVertices * k) / (length.x() * length.y()) ); + + setMaxMin (nbVerticesX, nbVerticesY, nbVerticesZ); // This function prevent from cutting the scene in too many blocs + + OSG_INFO + << "Cutting x by " << nbVerticesX << std::endl + << "Cutting y by " << nbVerticesY << std::endl + << "Cutting z by " << nbVerticesZ << std::endl; + + osg::BoundingBox::value_type blocX = length.x() / nbVerticesX; // These 3 lines set the size of a bloc in x, y and z + osg::BoundingBox::value_type blocY = length.y() / nbVerticesY; + osg::BoundingBox::value_type blocZ = length.z() / nbVerticesZ; + + boxList.reserve(nbVerticesX * nbVerticesY * nbVerticesZ); + short yinc = 1; + short xinc = 1; + unsigned int y = 0; + unsigned int x = 0; + for (unsigned int z = 0; z < nbVerticesZ; ++z) + { + while (x < nbVerticesX && x >= 0) + { + while (y < nbVerticesY && y >= 0) + { + osg::BoundingBox::value_type xMin = sceneBox.xMin() + x * blocX; + if (x == 0) //to prevent from mesh with no case + xMin -= 10; + + osg::BoundingBox::value_type yMin = sceneBox.yMin() + y * blocY; + if (y == 0) //to prevent from mesh with no case + yMin -= 10; + + osg::BoundingBox::value_type zMin = sceneBox.zMin() + z * blocZ; + if (z == 0) //to prevent from mesh with no case + zMin -= 10; + + osg::BoundingBox::value_type xMax = sceneBox.xMin() + (x + 1) * blocX; + if (x == nbVerticesX - 1) //to prevent from mesh with no case + xMax += 10; + + osg::BoundingBox::value_type yMax = sceneBox.yMin() + (y + 1) * blocY; + if (y == nbVerticesY - 1) //to prevent from mesh with no case + yMax += 10; + + osg::BoundingBox::value_type zMax = sceneBox.zMin() + (z + 1) * blocZ; + if (z == nbVerticesZ - 1) //to prevent from mesh with no case + zMax += 10; + + boxList.push_back(osg::BoundingBox(xMin, // Add a bloc to the list + yMin, + zMin, + xMax, + yMax, + zMax)); + y += yinc; + } + yinc = -yinc; + y += yinc; + x += xinc; + } + xinc = -xinc; + x += xinc; + } +} + +int +WriterCompareTriangle::inWhichBox(const osg::BoundingBox::value_type x, + const osg::BoundingBox::value_type y, + const osg::BoundingBox::value_type z) const +{ + for (unsigned int i = 0; i < boxList.size(); ++i) + { + if (x >= boxList[i].xMin() && + x < boxList[i].xMax() && + y >= boxList[i].yMin() && + y < boxList[i].yMax() && + z >= boxList[i].zMin() && + z < boxList[i].zMax()) + { + return i; + } + } + assert(false && "Point is not in any blocs"); + return 0; +} + +int WriterCompareTriangle::inWhichBox(const osg::BoundingBox::vec_type & point) const { + return inWhichBox(point.x(), point.y(), point.z()); +} diff --git a/src/osgPlugins/3ds/WriterNodeVisitor.cpp b/src/osgPlugins/3ds/WriterNodeVisitor.cpp index e6d51285c..c99ea52d3 100644 --- a/src/osgPlugins/3ds/WriterNodeVisitor.cpp +++ b/src/osgPlugins/3ds/WriterNodeVisitor.cpp @@ -5,7 +5,7 @@ * * Copyright (C) ??? * - * Writing support added 2007 by Sukender (Benoit Neil), http://sukender.free.fr, + * Writing support added 2009 by Sukender (Benoit Neil), http://sukender.free.fr, * strongly inspired by the OBJ writer object by Stephan Huber * * The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for @@ -13,6 +13,13 @@ * The OSG homepage is http://www.openscenegraph.org/ */ + +/// [EXPERIMENTAL] Disables animation data (and matrix transforms) for compatibility with some 3rd party apps. +/// Animations are not read by all 3DS importers. Thus disabling them may allow some 3rd-party apps, such as Rhinoceros (tested with 4.0) to correctly import 3DS files. +/// However, having proper hierarchy with matrix transforms will become impossible. +///\warning This is still experimental, hence the compile flag. This should become a reader/writer option as soon as it works as intented (maybe "noMatrixTransforms" could become a read/write option?). +#define DISABLE_3DS_ANIMATION 0 // Default = 0 + #include #include #include @@ -22,7 +29,6 @@ #include #include - void copyOsgMatrixToLib3dsMatrix(Lib3dsMatrix lib3ds_matrix, const osg::Matrix& osg_matrix) { for(int row=0; row<4; ++row) @@ -75,15 +81,9 @@ inline void copyOsgQuatToLib3dsQuat(float lib3ds_vector[4], const osg::Quat& osg lib3ds_vector[3] = static_cast(-angle); } -std::string getFileName(const std::string & path) -{ - unsigned int slashPos = path.find_last_of("/\\"); - if (slashPos == std::string::npos) return path; - return path.substr(slashPos+1); -} - /// Checks if a filename (\b not path) is 8.3 (an empty name is never 8.3, and a path is never 8.3). +/// Please note the '8' and '3' limitations are in \b bytes, not in characters (which is different when using UTF8). bool is83(const std::string & s) { // 012345678901 @@ -91,7 +91,7 @@ bool is83(const std::string & s) if (s.find_first_of("/\\") != std::string::npos) return false; // It should not be a path, but a filename unsigned int len = s.length(); if (len > 12 || len == 0) return false; - unsigned int pointPos = s.rfind('.'); + size_t pointPos = s.rfind('.'); if (pointPos == std::string::npos) return len <= 8; // Without point // With point if (pointPos > 8) return false; @@ -99,25 +99,138 @@ bool is83(const std::string & s) return true; } -/// Tests if the given string is a path supported by 3DS format (8.3, 63 chars max). -bool is3DSpath(const std::string & s, bool extendedFilePaths) -{ - unsigned int len = s.length(); - if (len >= 64 || len == 0) return false; - if (extendedFilePaths) return true; // Extended paths are simply those that fits the 64 bytes buffer! - - // For each subdirectory - unsigned int tokenLen; - for (unsigned int tokenBegin=0, tokenEnd=0; tokenEnd == std::string::npos; tokenBegin = tokenEnd+1) - { - tokenEnd = s.find_first_of("/\\", tokenBegin); - if (tokenEnd != std::string::npos) tokenLen = tokenEnd-tokenBegin-1; // -1 to avoid reading the separator - else tokenLen = len-tokenBegin; - if ( tokenLen>0 && !is83(s.substr(tokenBegin, tokenLen)) ) return false; - } - return true; +inline std::string::size_type maxNameLen(bool extendedFilePaths, bool isNodeName) { + if (extendedFilePaths) return 63; + return isNodeName ? 8 : (8+1+3); } +/// Tests if the given string is a path supported by 3DS format (8.3, 63 chars max, non empty). +inline bool is3DSName(const std::string & s, bool extendedFilePaths, bool isNodeName) +{ + unsigned int len = s.length(); + if (len > maxNameLen(extendedFilePaths, isNodeName) || len == 0) return false; + // Extended paths are simply those that fits the 64 bytes buffer. + if (extendedFilePaths) return true; + // Short paths simply must have no subdirectory. + return is83(s); +} + +// ************************************************************************** +// ************************************************************************** +// The following block of code was taken from the osgDB 2.9.x trunk code +// (FileNameUtils) so that the 3ds plugin will be completely self-contained +// in this release. + +static const char * const PATH_SEPARATORS = "/\\"; +static unsigned int PATH_SEPARATORS_LEN = 2; + +/** Helper to iterate over elements of a path (including Windows' root, if any). **/ +class PathIterator { +public: + PathIterator(const std::string & v); + bool valid() const { return start!=end; } + PathIterator & operator++(); + std::string operator*(); + +protected: + std::string::const_iterator end; ///< End of path string + std::string::const_iterator start; ///< Points to the first char of an element, or ==end() if no more + std::string::const_iterator stop; ///< Points to the separator after 'start', or ==end() + + /// Iterate until 'it' points to something different from a separator + std::string::const_iterator skipSeparators(std::string::const_iterator it); + std::string::const_iterator next(std::string::const_iterator it); +}; + +PathIterator::PathIterator(const std::string & v) : end(v.end()), start(v.begin()), stop(v.begin()) { operator++(); } +PathIterator & PathIterator::operator++() +{ + if (!valid()) return *this; + start = skipSeparators(stop); + if (start != end) stop = next(start); + return *this; +} +std::string PathIterator::operator*() +{ + if (!valid()) return std::string(); + return std::string(start, stop); +} + +std::string::const_iterator PathIterator::skipSeparators(std::string::const_iterator it) +{ + for (; it!=end && std::find_first_of(it, it+1, PATH_SEPARATORS, PATH_SEPARATORS+PATH_SEPARATORS_LEN) != it+1; ++it) {} + return it; +} + +std::string::const_iterator PathIterator::next(std::string::const_iterator it) +{ + return std::find_first_of(it, end, PATH_SEPARATORS, PATH_SEPARATORS+PATH_SEPARATORS_LEN); +} + +static std::string getPathRoot(const std::string& path) { + // Test for unix root + if (path.empty()) return ""; + if (path[0] == '/') return "/"; + // Now test for Windows root + if (path.length()<2) return ""; + if (path[1] == ':') return path.substr(0, 2); // We should check that path[0] is a letter, but as ':' is invalid in paths in other cases, that's not a problem. + return ""; +} + +static std::string getPathRelative(const std::string& from, const std::string& to) +{ + // This implementation is not 100% robust, and should be replaced with C++0x "std::path" as soon as possible. + + // Definition: an "element" is a part between slashes. Ex: "/a/b" has two elements ("a" and "b"). + // Algorithm: + // 1. If paths are neither both absolute nor both relative, then we cannot do anything (we need to make them absolute, but need additionnal info on how to make it). Return. + // 2. If both paths are absolute and root isn't the same (for Windows only, as roots are of the type "C:", "D:"), then the operation is impossible. Return. + // 3. Iterate over two paths elements until elements are equal + // 4. For each remaining element in "from", add ".." to result + // 5. For each remaining element in "to", add this element to result + + // 1 & 2 + const std::string root = getPathRoot(from); + if (root != getPathRoot(to)) { + OSG_INFO << "Cannot relativise paths. From=" << from << ", To=" << to << ". Returning 'to' unchanged." << std::endl; + //return to; + return osgDB::getSimpleFileName(to); + } + + // 3 + PathIterator itFrom(from), itTo(to); + // Iterators may point to Windows roots. As we tested they are equal, there is no need to ++itFrom and ++itTo. + // However, if we got an Unix root, we must add it to the result. + std::string res(root == "/" ? "/" : ""); + for(; itFrom.valid() && itTo.valid() && *itFrom==*itTo; ++itFrom, ++itTo) {} + + // 4 + for(; itFrom.valid(); ++itFrom) res += "../"; + + // 5 + for(; itTo.valid(); ++itTo) res += *itTo + "/"; + + // Remove trailing slash before returning + if (!res.empty() && std::find_first_of(res.rbegin(), res.rbegin()+1, PATH_SEPARATORS, PATH_SEPARATORS+PATH_SEPARATORS_LEN) != res.rbegin()+1) + { + return res.substr(0, res.length()-1); + } + return res; +} + +// strip all extensions from the filename. +static std::string getNameLessAllExtensions(const std::string& fileName) +{ + // Finds start serach position: from last slash, or the begining of the string if none found + std::string::size_type startPos = fileName.find_last_of(PATH_SEPARATORS); // Finds forward slash *or* back slash + if (startPos == std::string::npos) startPos = 0; + std::string::size_type dot = fileName.find_first_of('.', startPos); // Finds *FIRST* dot from start pos + if (dot==std::string::npos) return fileName; + return std::string(fileName.begin(),fileName.begin()+dot); +} + +// ************************************************************************** +// ************************************************************************** // Use namespace qualification to avoid static-link symbol collitions // from multiply defined symbols. @@ -166,7 +279,7 @@ public: triangle.t2 = i2; triangle.t3 = i3; triangle.material = _material; - _listTriangles.push_back(std::pair(triangle, _drawable_n)); + _listTriangles.push_back(ListTriangle::value_type(triangle, _drawable_n)); } virtual void begin(GLenum mode) { @@ -355,11 +468,13 @@ void PrimitiveIndexWriter::drawArrays(GLenum mode,GLint first,GLsizei count) case(GL_LINE_LOOP): //break; default: - osg::notify(osg::WARN) << "3DS WriterNodeVisitor: can't handle mode " << mode << std::endl; + OSG_WARN << "3DS WriterNodeVisitor: can't handle mode " << mode << std::endl; break; } } + + WriterNodeVisitor::Material::Material(WriterNodeVisitor & writerNodeVisitor, osg::StateSet * stateset, osg::Material* mat, osg::Texture* tex, int index) : index(index), diffuse(1,1,1,1), @@ -370,7 +485,7 @@ WriterNodeVisitor::Material::Material(WriterNodeVisitor & writerNodeVisitor, osg double_sided(false), image(NULL), texture_transparency(false), - texture_no_tile(false) + texture_no_tile(true) // matches lib3ds_material.cpp initialize_texture_map(..) default flag setting { //static unsigned int s_objmaterial_id = 0; //++s_objmaterial_id; @@ -380,9 +495,14 @@ WriterNodeVisitor::Material::Material(WriterNodeVisitor & writerNodeVisitor, osg diffuse = mat->getDiffuse(osg::Material::FRONT); ambient = mat->getAmbient(osg::Material::FRONT); specular = mat->getSpecular(osg::Material::FRONT); - shininess = mat->getShininess(osg::Material::FRONT); + shininess = mat->getShininess(osg::Material::FRONT) / 128.f; + // OpenGL shininess = pow(2, 10.0*mat->shininess); (As in lib3ds example) + // => mat->shininess = log.2( OpenGL shininess ) /10 (if values are >0) + // => mat->shininess = log( OpenGL shininess ) / log(2) /10 + //shininess = mat->getShininess(osg::Material::FRONT) <= 0 ? 0 : log( mat->getShininess(osg::Material::FRONT) ) / log(2.f) / 10.f; + transparency = 1-diffuse.w(); - name = writerNodeVisitor.getUniqueName(mat->getName(),"mat"); + name = writerNodeVisitor.getUniqueName(mat->getName(),true,"mat"); osg::StateAttribute * attribute = stateset->getAttribute(osg::StateAttribute::CULLFACE); if (!attribute) { @@ -395,13 +515,13 @@ WriterNodeVisitor::Material::Material(WriterNodeVisitor & writerNodeVisitor, osg if (mode == osg::CullFace::BACK) double_sided = false; else if (mode == osg::CullFace::FRONT) { - osg::notify(osg::WARN) << "3DS Writer: Reversed face (culled FRONT) not supported yet." << std::endl; + OSG_WARN << "3DS Writer: Reversed face (culled FRONT) not supported yet." << std::endl; double_sided = false; } else { assert(mode == osg::CullFace::FRONT_AND_BACK); - osg::notify(osg::WARN) << "3DS Writer: Invisible face (culled FRONT_AND_BACK) not supported yet." << std::endl; + OSG_WARN << "3DS Writer: Invisible face (culled FRONT_AND_BACK) not supported yet." << std::endl; double_sided = false; } } @@ -412,7 +532,8 @@ WriterNodeVisitor::Material::Material(WriterNodeVisitor & writerNodeVisitor, osg if(img) { texture_transparency = (stateset->getMode(GL_BLEND) == osg::StateAttribute::ON); - texture_no_tile = (tex->getWrap(osg::Texture2D::WRAP_S) == osg::Texture2D::CLAMP); + osg::Texture::WrapMode wrapS = tex->getWrap(osg::Texture2D::WRAP_S); + texture_no_tile = !(wrapS == osg::Texture2D::REPEAT || wrapS == osg::Texture2D::MIRROR); image = img; } } @@ -426,49 +547,6 @@ WriterNodeVisitor::Material::Material(WriterNodeVisitor & writerNodeVisitor, osg } -// If 'to' is in a subdirectory of 'from' then this function returns the -// subpath. Otherwise it just returns the file name. -// (Same as in FBX plugin) -std::string getPathRelative(const std::string& from/*directory*/, - const std::string& to/*file path*/) -{ - - std::string::size_type slash = to.find_last_of('/'); - std::string::size_type backslash = to.find_last_of('\\'); - if (slash == std::string::npos) - { - if (backslash == std::string::npos) return to; - slash = backslash; - } - else if (backslash != std::string::npos && backslash > slash) - { - slash = backslash; - } - - if (from.empty() || from.length() > to.length()) - return osgDB::getSimpleFileName(to); - - std::string::const_iterator itTo = to.begin(); - for (std::string::const_iterator itFrom = from.begin(); - itFrom != from.end(); ++itFrom, ++itTo) - { - char a = tolower(*itFrom), b = tolower(*itTo); - if (a == '\\') a = '/'; - if (b == '\\') b = '/'; - if (a != b || itTo == to.begin() + slash + 1) - { - return osgDB::getSimpleFileName(to); - } - } - - while (itTo != to.end() && (*itTo == '\\' || *itTo == '/')) - { - ++itTo; - } - - return std::string(itTo, to.end()); -} - /// Converts an extension to a 3-letters long one equivalent. std::string convertExt(const std::string & path, bool extendedFilePaths) { @@ -488,12 +566,12 @@ WriterNodeVisitor::WriterNodeVisitor(Lib3dsFile * file3ds, const std::string & f osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _succeeded(true), _srcDirectory(srcDirectory), - file3ds(file3ds), + _file3ds(file3ds), _currentStateSet(new osg::StateSet()), _lastMaterialIndex(0), _lastMeshIndex(0), _cur3dsNode(NULL), - options(options), + _options(options), _imageCount(0), _extendedFilePaths(false) { @@ -516,7 +594,7 @@ WriterNodeVisitor::WriterNodeVisitor(Lib3dsFile * file3ds, const std::string & f void WriterNodeVisitor::writeMaterials() { unsigned int nbMat = _materialMap.size(); - lib3ds_file_reserve_materials(file3ds, nbMat, 1); + lib3ds_file_reserve_materials(_file3ds, nbMat, 1); // Ugly thing: it seems lib3ds_file_insert_material() doesn't support insertion in a random order (else materials are not assigned the right way) for (unsigned int iMat=0; iMat=0 && mat.index < static_cast(_materialMap.size())); - Lib3dsMaterial * mat3ds = lib3ds_material_new(getFileName(mat.name).c_str()); + Lib3dsMaterial * mat3ds = lib3ds_material_new(osgDB::getSimpleFileName(mat.name).c_str()); copyOsgColorToLib3dsColor(mat3ds->ambient, mat.ambient); copyOsgColorToLib3dsColor(mat3ds->diffuse, mat.diffuse); copyOsgColorToLib3dsColor(mat3ds->specular, mat.specular); @@ -537,137 +615,223 @@ void WriterNodeVisitor::writeMaterials() mat3ds->two_sided = mat.double_sided ? 1 : 0; if (mat.image) { - Lib3dsTextureMap & tex = mat3ds->texture1_map; std::string path; - if(mat.image->getFileName().empty()) + ImageSet::const_iterator itImage( _imageSet.find(mat.image.get()) ); + if (itImage != _imageSet.end()) { - std::ostringstream oss; - oss << "Image_" << _imageCount++ << ".rgb"; - path = oss.str(); + // Image has been already used + path = itImage->second; } else { - path = getPathRelative(_srcDirectory, mat.image->getFileName()); - } - path = convertExt(path, _extendedFilePaths); + // First time we 'see' this image + if (mat.image->getFileName().empty()) + { + std::ostringstream oss; + oss << "Image_" << _imageCount++ << ".rgb"; + path = oss.str(); + } + else + { + path = getPathRelative(_srcDirectory, mat.image->getFileName()); + } + path = convertExt(path, _extendedFilePaths); + path = getUniqueName(path, false, ""); - if(!is3DSpath(path, _extendedFilePaths)) - { - path = getUniqueName(path, "", true); - //path = osgDB::getSimpleFileName(path); + // Write + const std::string fullPath( osgDB::concatPaths(_directory, path) ); + osgDB::makeDirectoryForFile(fullPath); + osgDB::writeImageFile(*(mat.image), fullPath, _options); + + // Insert in map + _imageSet.insert(ImageSet::value_type(mat.image.get(), path)); } + Lib3dsTextureMap & tex = mat3ds->texture1_map; strcpy(tex.name, path.c_str()); - path = osgDB::concatPaths(_directory, path); - osgDB::makeDirectoryForFile(path); - - //if (mat.image->valid()) osgDB::writeImageFile(*(mat.image), path); - if(_imageSet.find(mat.image.get()) == _imageSet.end()) - { - _imageSet.insert(mat.image.get()); - osgDB::writeImageFile(*(mat.image), path); - } + // Here we don't assume anything about initial flags state (actually it is set to LIB3DS_TEXTURE_NO_TILE by lib3DS, but this is subject to change) if (mat.texture_transparency) tex.flags |= LIB3DS_TEXTURE_ALPHA_SOURCE; + else tex.flags &= ~LIB3DS_TEXTURE_ALPHA_SOURCE; if (mat.texture_no_tile) tex.flags |= LIB3DS_TEXTURE_NO_TILE; + else tex.flags &= ~LIB3DS_TEXTURE_NO_TILE; } if (!succeeded()) return; - lib3ds_file_insert_material(file3ds, mat3ds, itr->second.index); + lib3ds_file_insert_material(_file3ds, mat3ds, itr->second.index); break; // Ugly thing (3) } assert(found); // Ugly thing (4) - Implementation error if !found } } +/// Truncates an UTF8 string so that it does not takes more than a given \b bytes amount (\b excluding the potential NULL end character). +/// The function assumes the UTF8 string is valid. +///\return A valid UTF8 string which size is less or equal to \c byteLimit. +// May be moved in osgDB/ConvertUTF? +std::string utf8TruncateBytes(const std::string & s, std::string::size_type byteLimit) { + // Untruncated strings + if (s.size() <= byteLimit) return s; -std::string WriterNodeVisitor::getUniqueName(const std::string& _defaultValue, const std::string & _defaultPrefix, bool nameIsPath) + // Truncated strings + std::string::const_iterator it=s.begin(), itEnd=s.begin()+byteLimit; + std::string::const_iterator itStop=it; + // Note: itEnd is < s.end(), so that we can always write "it+1" + for(; it!=itEnd; ++it) { + unsigned char c = static_cast(*it); + if ((c & 0x80) == 0) itStop=it+1; // 7 bits ANSI. itStop must then point after that character. + else if ((c & 0x40) != 0) itStop=it; // UTF8 sequence start: this is also past-the-end for the previous character (ANSI or UTF8) + } + return std::string(s.begin(), itStop); +} + +#ifdef OSG_USE_UTF8_FILENAME +# define truncateFilenameBytes(str, size) utf8TruncateBytes(str, size) +#else +# define truncateFilenameBytes(str, size) std::string(str, 0, size) +#endif + +std::string WriterNodeVisitor::getUniqueName(const std::string& _defaultValue, bool isNodeName, const std::string & _defaultPrefix, int currentPrefixLen) { - static const unsigned int MAX_PREFIX_LEGNTH = 4; // Arbitrarily defined to 4 chars - assert(_defaultPrefix.length()<=MAX_PREFIX_LEGNTH); // Default prefix is too long (implementation error) + //const unsigned int MAX_LENGTH = maxNameLen(_extendedFilePaths); + const unsigned int MAX_PREFIX_LENGTH = _extendedFilePaths ? 52 : 6; // Arbitrarily defined for short names, kept enough room for displaying UINT_MAX (10 characters) for long names. + assert(_defaultPrefix.length()<=4); // Default prefix is too long (implementation error) + const std::string defaultPrefix(_defaultPrefix.empty() ? "_" : _defaultPrefix); + if (currentPrefixLen<0) currentPrefixLen = osg::maximum(_defaultPrefix.length(), _defaultValue.length()); + currentPrefixLen = osg::clampBelow(currentPrefixLen, static_cast(MAX_PREFIX_LENGTH)); // Tests if default name is valid and unique - bool defaultIs83 = is83(_defaultValue); - bool defaultIsValid = nameIsPath ? is3DSpath(_defaultValue, _extendedFilePaths) : defaultIs83; - if (defaultIsValid && _nameMap.find(_defaultValue) == _nameMap.end()) - { - _nameMap.insert(_defaultValue); - return _defaultValue; - } - - // Handling of paths is not well done yet. Defaulting to something very simple. - // We should actually ensure each component is 8 chars long, and final filename is 8.3, and total is <64 chars, or simply ensure total length for extended 3DS paths. - std::string defaultValue(nameIsPath ? osgDB::getSimpleFileName(_defaultValue) : _defaultValue); - std::string ext(nameIsPath ? osgDB::getFileExtensionIncludingDot(_defaultValue).substr(0, std::min(_defaultValue.size(), 4)) : ""); // 4 chars = dot + 3 chars - if (ext == ".") ext = ""; - - std::string defaultPrefix(_defaultPrefix.empty() ? "_" : _defaultPrefix); + NameMap & nameMap = isNodeName ? _nodeNameMap : _imageNameMap; + PrefixMap & prefixMap = isNodeName ? _nodePrefixMap : _imagePrefixMap; + // Handling of paths is simple. Algorithm: + // - For short names, subdirectories are simply forbidden. Use the simple file name. + // - Else, the whole (relative) path must simply be <64 chars. + // After this, begin enumeration. + std::string parentPath, filename, ext, namePrefix; unsigned int max_val = 0; - std::string truncDefaultValue = ""; - for (unsigned int i = 0; i < std::min(defaultValue.size(), MAX_PREFIX_LEGNTH); ++i) + + // TODO Move the two parts of this giant if/else into two separate functions for better readability. + if (_extendedFilePaths) { - if (defaultValue[i] == '.') + // Tests if default name is valid and unique + if (is3DSName(_defaultValue, _extendedFilePaths, isNodeName)) { - truncDefaultValue = defaultValue.substr(0, i); - break; + std::pair insertion( nameMap.insert(_defaultValue) ); + if (insertion.second) return _defaultValue; // Return if element is newly inserted in the map (else there is a naming collision) } - } - if (truncDefaultValue.empty()) - truncDefaultValue = defaultValue.substr(0, std::min(defaultValue.size(), MAX_PREFIX_LEGNTH)); - assert(truncDefaultValue.size() <= MAX_PREFIX_LEGNTH); - std::map::iterator pairPrefix; - // TODO - Handle the case of extended 3DS paths and allow more than 8 chars - defaultIs83 = is83(truncDefaultValue); - if (defaultIs83) - { - max_val = static_cast(pow(10., 8. - truncDefaultValue.length())) -1; - pairPrefix = _mapPrefix.find(truncDefaultValue); - } + // Simply separate name and last extension + filename = osgDB::getNameLessExtension(osgDB::getSimpleFileName(_defaultValue)); + if (!isNodeName) + { + ext = osgDB::getFileExtensionIncludingDot(_defaultValue); + if (ext == ".") ext = ""; + } - if (defaultIs83 && (pairPrefix == _mapPrefix.end() || pairPrefix->second <= max_val)) - { - defaultPrefix = truncDefaultValue; + // Compute parent path + // Pre-condition: paths are supposed to be relative. + // If full path is too long (>MAX_PREFIX_LENGTH), cut path to let enough space for simple file name. + // Do not cut in the middle of a name, but at path separators. + parentPath = osgDB::getFilePath(_defaultValue); + if (_defaultValue.length() >MAX_PREFIX_LENGTH) // Not parentPath but _defaultValue! + { + // Nodes names: keep last directories (used only for the root name, generally named after the full file path) + // Images names: keep first directories (for images) + if (isNodeName) std::reverse(parentPath.begin(), parentPath.end()); + unsigned lenToDelete(filename.length() + ext.length() + 1); + lenToDelete = osg::clampBelow(lenToDelete, MAX_PREFIX_LENGTH); + parentPath = truncateFilenameBytes(parentPath, MAX_PREFIX_LENGTH - lenToDelete); // +1 for the path separator + std::string::size_type separator = parentPath.find_last_of("/\\"); + if (separator != std::string::npos) parentPath = parentPath.substr(0, separator); + if (isNodeName) std::reverse(parentPath.begin(), parentPath.end()); + } + + // Assert "MAX_PREFIX_LENGTH - parent path length - extension length -1" is >=0 and truncate name to this length to get our new prefix. + assert(parentPath.length() + ext.length() <= MAX_PREFIX_LENGTH); + const unsigned int len = MAX_PREFIX_LENGTH - (parentPath.length() + ext.length() +1); + namePrefix = truncateFilenameBytes(filename, len); + if (namePrefix.empty()) namePrefix = defaultPrefix; + + // Truncate the filename to get our new prefix + namePrefix = truncateFilenameBytes(filename, currentPrefixLen); + + // Enough space has been reserved for UINT_MAX values + max_val = UINT_MAX; } else { - max_val = static_cast(pow(10., 8. - defaultPrefix.length())) -1; - pairPrefix = _mapPrefix.find(defaultPrefix); - } + // Get last extension, and make filename have no extension + filename = getNameLessAllExtensions(osgDB::getSimpleFileName(_defaultValue)); + if (!isNodeName) + { + ext = truncateFilenameBytes(osgDB::getFileExtensionIncludingDot(_defaultValue), 4); // 4 chars = dot + 3 chars + if (ext == ".") ext = ""; + } - unsigned int searchStart = 0; - if (pairPrefix != _mapPrefix.end()) + // Tests if STRIPPED default name is valid and unique + const std::string strippedName( filename + ext ); + if (is3DSName(strippedName, _extendedFilePaths, isNodeName)) + { + std::pair insertion( nameMap.insert(strippedName) ); + if (insertion.second) return strippedName; // Return if element is newly inserted in the map (else there is a naming collision) + } + + namePrefix = filename; + if (namePrefix.empty()) namePrefix = defaultPrefix; + // Truncate the filename to get our new prefix + namePrefix = truncateFilenameBytes(namePrefix, currentPrefixLen); + + // Compute the maximum enumeration value + max_val = static_cast(pow(10., 8. - namePrefix.length())) -1; + } + assert(namePrefix.size() <= MAX_PREFIX_LENGTH); + + // Find the current enumeration value (searchStart) + unsigned int searchStart(0); + PrefixMap::iterator pairPrefix( prefixMap.find(namePrefix) ); + if (pairPrefix != prefixMap.end()) { searchStart = pairPrefix->second; } + else + { + // Check if truncated name is ok + const std::string res( osgDB::concatPaths(parentPath, namePrefix + ext) ); + if (nameMap.find(res) == nameMap.end()) { + prefixMap.insert(std::pair(namePrefix, 0)); + nameMap.insert(res); + return res; + } + } + // Search for a free value for(unsigned int i = searchStart; i <= max_val; ++i) { std::stringstream ss; - ss << defaultPrefix << i; - const std::string & res = ss.str(); - if (_nameMap.find(res) == _nameMap.end()) + ss << namePrefix << i; + const std::string res( osgDB::concatPaths(parentPath, ss.str() + ext) ); + if (nameMap.find(res) == nameMap.end()) { - if (pairPrefix != _mapPrefix.end()) + if (pairPrefix != prefixMap.end()) { pairPrefix->second = i + 1; } else { - _mapPrefix.insert(std::pair(defaultPrefix, i + 1)); + prefixMap.insert(std::pair(namePrefix, i + 1)); } - _nameMap.insert(res); - return res + ext; + nameMap.insert(res); + return res; } } // Failed finding a name // Try with a shorter prefix if possible - if (defaultPrefix.length()>1) return getUniqueName(_defaultValue, defaultPrefix.substr(0, defaultPrefix.length()-1), nameIsPath); + if (currentPrefixLen>1) return getUniqueName(_defaultValue, isNodeName, defaultPrefix, currentPrefixLen-1); // Try with default prefix if not arleady done - if (defaultPrefix != std::string("_")) return getUniqueName(_defaultValue, "_", nameIsPath); + if (defaultPrefix != std::string("_")) return getUniqueName(_defaultValue, isNodeName, "_", 1); // No more names - osg::notify(osg::FATAL) << "No more names available!" << std::endl; + OSG_NOTIFY(osg::FATAL) << "No more names available!" << std::endl; _succeeded = false; return "ERROR"; } @@ -724,7 +888,7 @@ WriterNodeVisitor::buildMesh(osg::Geode & geo, bool texcoords, Lib3dsMesh * mesh) { - osg::notify(osg::DEBUG_INFO) << "Building Mesh" << std::endl; + OSG_DEBUG << "Building Mesh" << std::endl; assert(mesh); // Write points @@ -734,16 +898,27 @@ WriterNodeVisitor::buildMesh(osg::Geode & geo, for(MapIndices::iterator it = index_vert.begin(); it != index_vert.end();++it) { osg::Geometry *g = geo.getDrawable( it->first.second )->asGeometry(); - assert(g->getVertexArray()); - if (g->getVertexArray()->getType() != osg::Array::Vec3ArrayType) + const osg::Array * basevecs = g->getVertexArray(); + assert(basevecs); + if (!basevecs || basevecs->getNumElements()==0) continue; + if (basevecs->getType() == osg::Array::Vec3ArrayType) { - // TODO Handle double presision vertices by converting them to float with a warning - osg::notify(osg::FATAL) << "Vertex array is not Vec3. Not implemented" << std::endl; + const osg::Vec3Array & vecs= *static_cast(basevecs); + copyOsgVectorToLib3dsVector(mesh->vertices[it->second], vecs[it->first.first]*mat); + } + else if (basevecs->getType() == osg::Array::Vec3dArrayType) + { + // Handle double presision vertices by converting them to float with a warning + OSG_NOTICE << "3DS format only supports single precision vertices. Converting double precision to single." << std::endl; + const osg::Vec3dArray & vecs= *static_cast(basevecs); + copyOsgVectorToLib3dsVector(mesh->vertices[it->second], vecs[it->first.first]*mat); + } + else + { + OSG_NOTIFY(osg::FATAL) << "Vertex array is not Vec3 or Vec3d. Not implemented" << std::endl; _succeeded = false; return; } - const osg::Vec3Array & vecs= *static_cast(g->getVertexArray()); - copyOsgVectorToLib3dsVector(mesh->vertices[it->second], vecs[it->first.first]*mat); } // Write texture coords (Texture 0 only) @@ -752,26 +927,25 @@ WriterNodeVisitor::buildMesh(osg::Geode & geo, for(MapIndices::iterator it = index_vert.begin(); it != index_vert.end(); ++it) { osg::Geometry *g = geo.getDrawable( it->first.second )->asGeometry(); - osg::Array * texarray = g->getTexCoordArray(0); - if (texarray) + const osg::Array * texarray = g->getNumTexCoordArrays()>=1 ? g->getTexCoordArray(0) : NULL; + if (!texarray || texarray->getNumElements()==0) continue; + + if (g->getTexCoordArray(0)->getType() != osg::Array::Vec2ArrayType) { - if (g->getTexCoordArray(0)->getType() != osg::Array::Vec2ArrayType) - { - osg::notify(osg::FATAL) << "Texture coords array is not Vec2. Not implemented" << std::endl; - _succeeded = false; - return; - } - const osg::Vec2Array & vecs= *static_cast(texarray); - mesh->texcos[it->second][0] = vecs[it->first.first][0]; - mesh->texcos[it->second][1] = vecs[it->first.first][1]; + OSG_NOTIFY(osg::FATAL) << "Texture coords array is not Vec2. Not implemented" << std::endl; + _succeeded = false; + return; } + const osg::Vec2Array & vecs= *static_cast(texarray); + mesh->texcos[it->second][0] = vecs[it->first.first][0]; + mesh->texcos[it->second][1] = vecs[it->first.first][1]; } } - lib3ds_file_insert_mesh(file3ds, mesh, _lastMeshIndex); + lib3ds_file_insert_mesh(_file3ds, mesh, _lastMeshIndex); ++_lastMeshIndex; Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance(mesh, mesh->name, NULL, NULL, NULL); - lib3ds_file_append_node(file3ds, reinterpret_cast(node3ds), reinterpret_cast(_cur3dsNode)); + lib3ds_file_append_node(_file3ds, reinterpret_cast(node3ds), reinterpret_cast(_cur3dsNode)); } unsigned int @@ -781,16 +955,7 @@ WriterNodeVisitor::calcVertices(osg::Geode & geo) for (unsigned int i = 0; i < geo.getNumDrawables(); ++i) { osg::Geometry *g = geo.getDrawable( i )->asGeometry(); - assert(g->getVertexArray()); - if (g->getVertexArray()->getType() != osg::Array::Vec3ArrayType) - { - // TODO Handle double presision vertices by converting them to float with a warning - osg::notify(osg::FATAL) << "Vertex array is not Vec3. Not implemented" << std::endl; - _succeeded = false; - return 0; - } - const osg::Vec3Array & vecs= *static_cast(g->getVertexArray()); - numVertice += vecs.getNumElements(); + if (g->getVertexArray()) numVertice += g->getVertexArray()->getNumElements(); } return numVertice; } @@ -806,23 +971,24 @@ WriterNodeVisitor::buildFaces(osg::Geode & geo, unsigned int nbVerticesRemaining = calcVertices(geo); // May set _succeded to false if (!succeeded()) return; - std::string name( getUniqueName(geo.getName().empty() ? geo.className() : geo.getName(), "geo") ); + std::string name( getUniqueName(geo.getName().empty() ? geo.className() : geo.getName(), true, "geo") ); if (!succeeded()) return; Lib3dsMesh *mesh = lib3ds_mesh_new( name.c_str() ); if (!mesh) { - osg::notify(osg::FATAL) << "Allocation error" << std::endl; + OSG_NOTIFY(osg::FATAL) << "Allocation error" << std::endl; _succeeded = false; return; } + //copyOsgMatrixToLib3dsMatrix(mesh->matrix, mat); lib3ds_mesh_resize_faces (mesh, osg::minimum(nbTrianglesRemaining, MAX_FACES)); lib3ds_mesh_resize_vertices(mesh, osg::minimum(nbVerticesRemaining, MAX_VERTICES), texcoords ? 0 : 1, 0); // Not mandatory but will allocate once a big block // Test if the mesh will be split and needs sorting if (nbVerticesRemaining >= MAX_VERTICES || nbTrianglesRemaining >= MAX_FACES) { - osg::notify(osg::INFO) << "Sorting elements..." << std::endl; + OSG_INFO << "Sorting elements..." << std::endl; WriterCompareTriangle cmp(geo, nbVerticesRemaining); std::sort(listTriangles.begin(), listTriangles.end(), cmp); } @@ -851,10 +1017,10 @@ WriterNodeVisitor::buildFaces(osg::Geode & geo, // We can't call a thing like "nbVerticesRemaining -= ...;" because points may be used multiple times. // [Sukender: An optimisation here would take too much time I think.] - mesh = lib3ds_mesh_new( getUniqueName(geo.getName().empty() ? geo.className() : geo.getName(), "geo").c_str()); + mesh = lib3ds_mesh_new( getUniqueName(geo.getName().empty() ? geo.className() : geo.getName(), true, "geo").c_str()); if (!mesh) { - osg::notify(osg::FATAL) << "Allocation error" << std::endl; + OSG_NOTIFY(osg::FATAL) << "Allocation error" << std::endl; _succeeded = false; return; } @@ -877,48 +1043,28 @@ WriterNodeVisitor::buildFaces(osg::Geode & geo, } void -WriterNodeVisitor::createListTriangle(osg::Geometry * geo, - ListTriangle & listTriangles, - bool & texcoords, - unsigned int & drawable_n) +WriterNodeVisitor::createListTriangle(osg::Geometry * geo, + ListTriangle & listTriangles, + bool & texcoords, + unsigned int & drawable_n) { - unsigned int nbVertices = 0; + const osg::Array * basevecs = geo->getVertexArray(); + if (!basevecs || basevecs->getNumElements()==0) return; + + // Texture coords + const osg::Array * basetexvecs = geo->getNumTexCoordArrays()>=1 ? geo->getTexCoordArray(0) : NULL; + if (basetexvecs) { - if (geo->getVertexArray() && geo->getVertexArray()->getType() != osg::Array::Vec3ArrayType) + unsigned int nb = basetexvecs->getNumElements(); + if (nb != geo->getVertexArray()->getNumElements()) { - // TODO Handle double presision vertices by converting them to float with a warning - osg::notify(osg::FATAL) << "Vertex array is not Vec3. Not implemented" << std::endl; + OSG_NOTIFY(osg::FATAL) << "There are more/less texture coords than vertices (corrupted geometry)" << std::endl; _succeeded = false; return; } - const osg::Vec3Array * vecs = geo->getVertexArray() ? static_cast(geo->getVertexArray()) : NULL; - if (vecs) - { - nbVertices = geo->getVertexArray()->getNumElements(); - // Texture coords - if (geo->getTexCoordArray(0) && geo->getTexCoordArray(0)->getType() != osg::Array::Vec2ArrayType) - { - osg::notify(osg::FATAL) << "Texture coords array is not Vec2. Not implemented" << std::endl; - _succeeded = false; - return; - } - const osg::Vec2Array * texvecs = geo->getTexCoordArray(0) ? static_cast(geo->getTexCoordArray(0)) : NULL; - if (texvecs) - { - unsigned int nb = geo->getTexCoordArray(0)->getNumElements(); - if (nb != geo->getVertexArray()->getNumElements()) - { - osg::notify(osg::FATAL) << "There are more/less texture coords than vertices (corrupted geometry)" << std::endl; - _succeeded = false; - return; - } - texcoords = true; - } - } + texcoords = true; } - if (nbVertices==0) return; - int material = processStateSet(_currentStateSet.get()); for(unsigned int i = 0; i < geo->getNumPrimitiveSets(); ++i) //Fill the Triangle List @@ -949,8 +1095,12 @@ void WriterNodeVisitor::apply( osg::Geode &node ) } if (succeeded() && count > 0) { +#if DISABLE_3DS_ANIMATION osg::Matrix mat( osg::computeLocalToWorld(getNodePath()) ); buildFaces(node, mat, listTriangles, texcoords); // May set _succeded to false +#else + buildFaces(node, osg::Matrix(), listTriangles, texcoords); // May set _succeded to false +#endif } popStateSet(node.getStateSet()); //_nameStack.pop_back(); @@ -968,8 +1118,10 @@ void WriterNodeVisitor::apply( osg::Billboard &node ) unsigned int count = node.getNumDrawables(); ListTriangle listTriangles; bool texcoords = false; - osg::notify(osg::NOTICE) << "Warning: 3DS writer is incomplete for Billboards (rotation not implemented)." << std::endl; + OSG_NOTICE << "Warning: 3DS writer is incomplete for Billboards (rotation not implemented)." << std::endl; +#if DISABLE_3DS_ANIMATION osg::Matrix m( osg::computeLocalToWorld(getNodePath()) ); +#endif for ( unsigned int i = 0; i < count; i++ ) { osg::Geometry *g = node.getDrawable( i )->asGeometry(); @@ -983,9 +1135,15 @@ void WriterNodeVisitor::apply( osg::Billboard &node ) popStateSet(g->getStateSet()); // May set _succeded to false if (!succeeded()) break; - osg::Matrix currentBillBoardMat(osg::Matrix::translate(node.getPosition(i)) * m); // TODO handle rotation - apply3DSMatrixNode(node, currentBillBoardMat, "bil"); // Add a 3DS matrix node - buildFaces(node, currentBillBoardMat, listTriangles, texcoords); // May set _succeded to false + osg::Matrix pointLocalMat(osg::Matrix::translate(node.getPosition(i))); // TODO handle rotation +#if DISABLE_3DS_ANIMATION + osg::Matrix currentBillboardWorldMat(pointLocalMat * m); + apply3DSMatrixNode(node, &pointLocalMat, "bil"); // Add a 3DS matrix node (with local matrix) + buildFaces(node, currentBillboardWorldMat, listTriangles, texcoords); // May set _succeded to false +#else + apply3DSMatrixNode(node, &pointLocalMat, "bil"); // Add a 3DS matrix node (with local matrix) + buildFaces(node, osg::Matrix(), listTriangles, texcoords); // May set _succeded to false +#endif if (!succeeded()) break; } } @@ -1002,7 +1160,12 @@ void WriterNodeVisitor::apply(osg::Group &node) { pushStateSet(node.getStateSet()); Lib3dsMeshInstanceNode * parent = _cur3dsNode; - apply3DSMatrixNode(node, osg::computeLocalToWorld(getNodePath()), "grp"); +#if DISABLE_3DS_ANIMATION + osg::Matrix mat( osg::computeLocalToWorld(getNodePath()) ); + apply3DSMatrixNode(node, &mat, "grp"); +#else + apply3DSMatrixNode(node, NULL, "grp"); +#endif if (succeeded()) traverse(node); _cur3dsNode = parent; @@ -1013,39 +1176,43 @@ void WriterNodeVisitor::apply(osg::MatrixTransform &node) { pushStateSet(node.getStateSet()); Lib3dsMeshInstanceNode * parent = _cur3dsNode; - apply3DSMatrixNode(node, osg::computeLocalToWorld(getNodePath()), "mtx"); +#if DISABLE_3DS_ANIMATION + osg::Matrix mat( osg::computeLocalToWorld(getNodePath()) ); +#else + osg::Matrix mat( node.getMatrix() ); +#endif + apply3DSMatrixNode(node, &mat, "mtx"); if (succeeded()) traverse(node); _cur3dsNode = parent; popStateSet(node.getStateSet()); } -void WriterNodeVisitor::apply3DSMatrixNode(osg::Node &node, const osg::Matrix & m, const char * const prefix) +void WriterNodeVisitor::apply3DSMatrixNode(osg::Node &node, const osg::Matrix * m, const char * const prefix) { + // Note: Creating a mesh instance with no transform and then copying the matrix doesn't work (matrix seems to be a temporary/computed value) Lib3dsMeshInstanceNode * parent = _cur3dsNode; + Lib3dsMeshInstanceNode * node3ds = NULL; + if (m) + { + osg::Vec3 osgScl, osgPos; + osg::Quat osgRot, osgSo; + m->decompose(osgPos, osgRot, osgScl, osgSo); - //const osg::Matrix & m = node.getMatrix(); - //const osg::Matrix m( osg::computeLocalToWorld(nodePath) ); // [NEEDS TESTING!] 3DS matrices always contain world to local transformation (not local transform; ie. from parent) + float pos[3]; + float scl[3]; + float rot[4]; + copyOsgVectorToLib3dsVector(pos, osgPos); + copyOsgVectorToLib3dsVector(scl, osgScl); + copyOsgQuatToLib3dsQuat(rot, osgRot); + node3ds = lib3ds_node_new_mesh_instance(NULL, getUniqueName(node.getName().empty() ? node.className() : node.getName(), true, prefix).c_str(), pos, scl, rot); + } + else + { + node3ds = lib3ds_node_new_mesh_instance(NULL, getUniqueName(node.getName().empty() ? node.className() : node.getName(), true, prefix).c_str(), NULL, NULL, NULL); + } - // Transform data used to be given to lib3ds_node_new_mesh_instance(), but it seems buggy (pivot problem? bug in conversion?). - float pos[3]; - float scl[3]; - float rot[4]; - osg::Vec3 osgScl, osgPos; - osg::Quat osgRot, osgSo; - m.decompose(osgPos, osgRot, osgScl, osgSo); - copyOsgVectorToLib3dsVector(pos, osgPos); - copyOsgVectorToLib3dsVector(scl, osgScl); - copyOsgQuatToLib3dsQuat(rot, osgRot); - Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance - (NULL, getUniqueName(node.getName().empty() ? node.className() : node.getName(), prefix).c_str(), pos, scl, rot); - - //// Create a mesh instance with no transform and then copy the matrix (doesn't work) - //Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance - // (NULL, getUniqueName(node.getName().empty() ? node.className() : node.getName(), "mtx").c_str(), NULL, NULL, NULL); - // copyOsgMatrixToLib3dsMatrix(node3ds->base.matrix, m); - - lib3ds_file_append_node(file3ds, reinterpret_cast(node3ds), reinterpret_cast(parent)); + lib3ds_file_append_node(_file3ds, reinterpret_cast(node3ds), reinterpret_cast(parent)); _cur3dsNode = node3ds; } diff --git a/src/osgPlugins/3ds/WriterNodeVisitor.h b/src/osgPlugins/3ds/WriterNodeVisitor.h index 8fe3552c5..9c34eeca0 100644 --- a/src/osgPlugins/3ds/WriterNodeVisitor.h +++ b/src/osgPlugins/3ds/WriterNodeVisitor.h @@ -1,222 +1,227 @@ -// -*-c++-*- - -/* -* 3DS reader/writer for Open Scene Graph -* -* Copyright (C) ??? -* -* Writing support added 2007 by Sukender (Benoit Neil), http://sukender.free.fr, -* strongly inspired by the OBJ writer object by Stephan Huber -* -* The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for -* real-time rendering of large 3D photo-realistic models. -* The OSG homepage is http://www.openscenegraph.org/ -*/ - -#ifndef _3DS_WRITER_NODE_VISITOR_HEADER__ -#define _3DS_WRITER_NODE_VISITOR_HEADER__ - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include "lib3ds/lib3ds.h" -#include "WriterCompareTriangle.h" -#include - -void copyOsgMatrixToLib3dsMatrix(Lib3dsMatrix lib3ds_matrix, const osg::Matrix& osg_matrix); - -typedef std::map, unsigned int> MapIndices; -typedef std::vector > ListTriangle; //the int is the drawable of the triangle - -namespace plugin3ds -{ - -class WriterNodeVisitor: public osg::NodeVisitor -{ - public: - static const unsigned int MAX_VERTICES = 65000; - static const unsigned int MAX_FACES = MAX_VERTICES; - - WriterNodeVisitor(Lib3dsFile * file3ds, const std::string & fileName, - const osgDB::ReaderWriter::Options* options, - const std::string & srcDirectory); - - bool succeeded() const { return _succeeded; } - virtual void apply(osg::Geode &node); - virtual void apply(osg::Billboard &node); - - virtual void apply(osg::Group &node); - virtual void apply(osg::MatrixTransform &node); - - void traverse (osg::Node &node) - { - pushStateSet(node.getStateSet()); - osg::NodeVisitor::traverse( node ); - popStateSet(node.getStateSet()); - } - - void pushStateSet(osg::StateSet* ss) - { - if (NULL!=ss) { - // Save our current stateset - _stateSetStack.push(_currentStateSet.get()); - - // merge with node stateset - _currentStateSet = static_cast(_currentStateSet->clone(osg::CopyOp::SHALLOW_COPY)); - _currentStateSet->merge(*ss); - } - } - - - void popStateSet(osg::StateSet* ss) - { - if (NULL!=ss) { - // restore the previous stateset - _currentStateSet = _stateSetStack.top(); - _stateSetStack.pop(); - } - } - - - void writeMaterials(); - - - - ///\todo Add support for 2nd texture, opacity_map, bump_map, specular_map, shininess_map, self_illum_map, reflection_map. - class Material { - public: - Material(WriterNodeVisitor & writerNodeVisitor, osg::StateSet * stateset, osg::Material* mat, osg::Texture* tex, int index=-1); - - int index; ///< Index in the 3DS file - osg::Vec4 diffuse, ambient, specular; - float shininess; - float transparency; - bool double_sided; - std::string name; - osg::ref_ptr image; - bool texture_transparency; - bool texture_no_tile; - protected: - Material() : index(-1) {} - - }; - - protected: - struct CompareStateSet - { - bool operator()(const osg::ref_ptr& ss1, const osg::ref_ptr& ss2) const - { - return *ss1 < *ss2; - } - }; - - - private: - WriterNodeVisitor& operator = (const WriterNodeVisitor&) { return *this; } - - /** - * Fill the faces field of the mesh and call buildMesh(). - * \param geo is the geode who contain vertice and faces. - * \param mat Local to world matrix applied to the geode - * \param listTriangles contain all the meshs faces. - * \param texcoords tell us if we have to treat texture coord. - */ - void buildFaces(osg::Geode & geo, const osg::Matrix & mat, ListTriangle & listTriangles, bool texcoords); - - /** - * Calculate the number of vertices in the geode. - * \return the number of vertices in the geode. - */ - unsigned int calcVertices(osg::Geode & geo); - - /** - * Build a mesh - * \param geo is the geode who contain vertice and faces - * \param mat Local to world matrix applied to the geode - * \param index_vert is the index used to build the new mesh - * \param texcoords tell us if we have to treat texture coord - * \param mesh is the mesh with faces filled - * \sa See cutScene() about the definition of the boxes for faces sorting. - */ - void - buildMesh(osg::Geode & geo, - const osg::Matrix & mat, - MapIndices & index_vert, - bool texcoords, - Lib3dsMesh *mesh); - - /** - * Add a vertice to the index and link him with the Triangle index and the drawable. - * \param index_vert is the map where the vertice are stored. - * \param index is the indice of the vertice's position in the vec3. - * \param drawable_n is the number of the drawable. - * \return the position of the vertice in the final mesh. - */ - unsigned int - getMeshIndexForGeometryIndex(MapIndices & index_vert, - unsigned int index, - unsigned int drawable_n); - /** - * Create the list of faces from the geode. - * \param geo is the geode to study. - * \param listTriangles is the list to fill. - * \param texcoords tell us if we have to treat texture coord. - * \param drawable_n tell us which drawable we are building. - */ - void createListTriangle(osg::Geometry * geo, - ListTriangle & listTriangles, - bool & texcoords, - unsigned int & drawable_n); - - int processStateSet(osg::StateSet* stateset); - - std::string getUniqueName(const std::string& defaultvalue="", const std::string & defaultPrefix = "", bool nameIsPath = false); - std::string export3DSTexture(const osg::Image * image, const std::string & fileName); - - typedef std::stack > StateSetStack; - typedef std::map< osg::ref_ptr, Material, CompareStateSet> MaterialMap; - - void apply3DSMatrixNode(osg::Node &node, const osg::Matrix & m, const char * const prefix); - - bool _succeeded; - std::string _directory; - std::string _srcDirectory; - Lib3dsFile * file3ds; - StateSetStack _stateSetStack; - osg::ref_ptr _currentStateSet; - std::map _mapPrefix; ///< List of next number to use in unique name generation, for each prefix - std::set _nameMap; - MaterialMap _materialMap; - unsigned int _lastMaterialIndex; - unsigned int _lastMeshIndex; - Lib3dsMeshInstanceNode * _cur3dsNode; - const osgDB::ReaderWriter::Options* options; - unsigned int _imageCount; - bool _extendedFilePaths; - std::set _imageSet; -}; - -// end namespace plugin3ds -} - -#endif +// -*-c++-*- + +/* +* 3DS reader/writer for Open Scene Graph +* +* Copyright (C) ??? +* +* Writing support added 2009 by Sukender (Benoit Neil), http://sukender.free.fr, +* strongly inspired by the OBJ writer object by Stephan Huber +* +* The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for +* real-time rendering of large 3D photo-realistic models. +* The OSG homepage is http://www.openscenegraph.org/ +*/ + +#ifndef _3DS_WRITER_NODE_VISITOR_HEADER__ +#define _3DS_WRITER_NODE_VISITOR_HEADER__ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "lib3ds/lib3ds.h" +#include "WriterCompareTriangle.h" +#include + +void copyOsgMatrixToLib3dsMatrix(Lib3dsMatrix lib3ds_matrix, const osg::Matrix& osg_matrix); + +typedef std::map, unsigned int> MapIndices; +typedef std::vector > ListTriangle; //the int is the drawable of the triangle + +namespace plugin3ds +{ + +class WriterNodeVisitor: public osg::NodeVisitor +{ + public: + static const unsigned int MAX_VERTICES = 65000; + static const unsigned int MAX_FACES = MAX_VERTICES; + + WriterNodeVisitor(Lib3dsFile * file3ds, const std::string & fileName, + const osgDB::ReaderWriter::Options* options, + const std::string & srcDirectory); + + bool succeeded() const { return _succeeded; } + virtual void apply(osg::Geode &node); + virtual void apply(osg::Billboard &node); + + virtual void apply(osg::Group &node); + virtual void apply(osg::MatrixTransform &node); + + void traverse (osg::Node &node) + { + pushStateSet(node.getStateSet()); + osg::NodeVisitor::traverse( node ); + popStateSet(node.getStateSet()); + } + + void pushStateSet(osg::StateSet* ss) + { + if (NULL!=ss) { + // Save our current stateset + _stateSetStack.push(_currentStateSet.get()); + + // merge with node stateset + _currentStateSet = static_cast(_currentStateSet->clone(osg::CopyOp::SHALLOW_COPY)); + _currentStateSet->merge(*ss); + } + } + + + void popStateSet(osg::StateSet* ss) + { + if (NULL!=ss) { + // restore the previous stateset + _currentStateSet = _stateSetStack.top(); + _stateSetStack.pop(); + } + } + + + void writeMaterials(); + + + + ///\todo Add support for 2nd texture, opacity_map, bump_map, specular_map, shininess_map, self_illum_map, reflection_map. + class Material { + public: + Material(WriterNodeVisitor & writerNodeVisitor, osg::StateSet * stateset, osg::Material* mat, osg::Texture* tex, int index=-1); + + int index; ///< Index in the 3DS file + osg::Vec4 diffuse, ambient, specular; + float shininess; + float transparency; + bool double_sided; + std::string name; + osg::ref_ptr image; + bool texture_transparency; + bool texture_no_tile; + protected: + Material() : index(-1) {} + + }; + + protected: + struct CompareStateSet + { + bool operator()(const osg::ref_ptr& ss1, const osg::ref_ptr& ss2) const + { + return *ss1 < *ss2; + } + }; + + + private: + WriterNodeVisitor& operator = (const WriterNodeVisitor&) { return *this; } + + /** + * Fill the faces field of the mesh and call buildMesh(). + * \param geo is the geode who contain vertice and faces. + * \param mat Local to world matrix applied to the geode + * \param listTriangles contain all the meshs faces. + * \param texcoords tell us if we have to treat texture coord. + */ + void buildFaces(osg::Geode & geo, const osg::Matrix & mat, ListTriangle & listTriangles, bool texcoords); + + /** + * Calculate the number of vertices in the geode. + * \return the number of vertices in the geode. + */ + unsigned int calcVertices(osg::Geode & geo); + + /** + * Build a mesh + * \param geo is the geode who contain vertice and faces + * \param mat Local to world matrix applied to the geode + * \param index_vert is the index used to build the new mesh + * \param texcoords tell us if we have to treat texture coord + * \param mesh is the mesh with faces filled + * \sa See cutScene() about the definition of the boxes for faces sorting. + */ + void + buildMesh(osg::Geode & geo, + const osg::Matrix & mat, + MapIndices & index_vert, + bool texcoords, + Lib3dsMesh *mesh); + + /** + * Add a vertice to the index and link him with the Triangle index and the drawable. + * \param index_vert is the map where the vertice are stored. + * \param index is the indice of the vertice's position in the vec3. + * \param drawable_n is the number of the drawable. + * \return the position of the vertice in the final mesh. + */ + unsigned int + getMeshIndexForGeometryIndex(MapIndices & index_vert, + unsigned int index, + unsigned int drawable_n); + /** + * Create the list of faces from the geode. + * \param geo is the geode to study. + * \param listTriangles is the list to fill. + * \param texcoords tell us if we have to treat texture coord. + * \param drawable_n tell us which drawable we are building. + */ + void createListTriangle(osg::Geometry * geo, + ListTriangle & listTriangles, + bool & texcoords, + unsigned int & drawable_n); + + int processStateSet(osg::StateSet* stateset); + + std::string getUniqueName(const std::string& defaultvalue, bool isNodeName, const std::string & defaultPrefix = "", int currentPrefixLen = -1); + std::string export3DSTexture(const osg::Image * image, const std::string & fileName); + + typedef std::stack > StateSetStack; + typedef std::map< osg::ref_ptr, Material, CompareStateSet> MaterialMap; + + void apply3DSMatrixNode(osg::Node &node, const osg::Matrix * m, const char * const prefix); + + bool _succeeded; + std::string _directory; + std::string _srcDirectory; + Lib3dsFile * _file3ds; + StateSetStack _stateSetStack; + osg::ref_ptr _currentStateSet; + typedef std::map PrefixMap; + PrefixMap _nodePrefixMap; ///< List of next number to use in unique name generation, for each prefix + PrefixMap _imagePrefixMap; + typedef std::set NameMap; + NameMap _nodeNameMap; + NameMap _imageNameMap; + MaterialMap _materialMap; + unsigned int _lastMaterialIndex; + unsigned int _lastMeshIndex; + Lib3dsMeshInstanceNode * _cur3dsNode; + const osgDB::ReaderWriter::Options* _options; + unsigned int _imageCount; + bool _extendedFilePaths; + typedef std::map ImageSet; + ImageSet _imageSet; ///< Map used to avoid renaming and writing twice an image +}; + +// end namespace plugin3ds +} + +#endif diff --git a/src/osgPlugins/3ds/lib3ds/lib3ds.h b/src/osgPlugins/3ds/lib3ds/lib3ds.h index c92667f78..c414906f2 100644 --- a/src/osgPlugins/3ds/lib3ds/lib3ds.h +++ b/src/osgPlugins/3ds/lib3ds/lib3ds.h @@ -788,8 +788,6 @@ extern LIB3DSAPI void lib3ds_matrix_rotate(float m[4][4], float angle, float ax, extern LIB3DSAPI void lib3ds_matrix_camera(float m[4][4], float pos[3], float tgt[3], float roll); /* --- Code for OpenSceneGraph --- */ -extern LIB3DSAPI void setByteOrder(); - /* Definitions for compatibility with previous lib3DS used: */ typedef float Lib3dsMatrix[4][4]; typedef float Lib3dsVector[3]; diff --git a/src/osgPlugins/3ds/lib3ds/lib3ds_file.c b/src/osgPlugins/3ds/lib3ds/lib3ds_file.c index 4a54f0539..8c7536d67 100644 --- a/src/osgPlugins/3ds/lib3ds/lib3ds_file.c +++ b/src/osgPlugins/3ds/lib3ds/lib3ds_file.c @@ -446,13 +446,14 @@ mdata_read(Lib3dsFile *file, Lib3dsIo *io) { static int compare_node_id( const void *a, const void *b ) { - return (*((Lib3dsNode**)a))->node_id - (*((Lib3dsNode**)b))->node_id; + return (int)((*((Lib3dsNode**)a))->node_id) - (int)((*((Lib3dsNode**)b))->node_id); } static int compare_node_id2( const void *a, const void *b ) { - return *((unsigned short*)a) - (*((Lib3dsNode**)b))->node_id; + // not a is a pointer in the calling bsearch routine the user_id is an unsigned, while the node_id is an unsigned short?! + return (int)(*((unsigned*)a)) - (int)((*((Lib3dsNode**)b))->node_id); } diff --git a/src/osgPlugins/3ds/lib3ds/lib3ds_impl.h b/src/osgPlugins/3ds/lib3ds/lib3ds_impl.h index 62d15f923..57ab446b7 100644 --- a/src/osgPlugins/3ds/lib3ds/lib3ds_impl.h +++ b/src/osgPlugins/3ds/lib3ds/lib3ds_impl.h @@ -35,7 +35,11 @@ #endif #ifndef _MSC_VER +#if defined __sun || defined __hpux +#include +#else #include +#endif #else typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; diff --git a/src/osgPlugins/3ds/lib3ds/lib3ds_io.c b/src/osgPlugins/3ds/lib3ds/lib3ds_io.c index a3b1fa317..d0da1180d 100644 --- a/src/osgPlugins/3ds/lib3ds/lib3ds_io.c +++ b/src/osgPlugins/3ds/lib3ds/lib3ds_io.c @@ -17,16 +17,6 @@ */ #include "lib3ds_impl.h" -/* --- Code for OpenSceneGraph --- */ -#include "lib3ds.h" // For setByteOrder() -#include - -static bool s_requiresByteSwap = false; -extern LIB3DSAPI void setByteOrder() -{ - s_requiresByteSwap = osg::getCpuByteOrder()==osg::BigEndian; -} -/* --- (end) Code for OpenSceneGraph --- */ typedef union { uint32_t dword_value; @@ -171,12 +161,6 @@ lib3ds_io_read_word(Lib3dsIo *io) { lib3ds_io_read(io, b, 2); w = ((uint16_t)b[1] << 8) | ((uint16_t)b[0]); - /* --- Code for OpenSceneGraph --- */ - if (s_requiresByteSwap) - { - osg::swapBytes2((char*)&w); - } - /* --- (end) Code for OpenSceneGraph --- */ return(w); } @@ -195,12 +179,6 @@ lib3ds_io_read_dword(Lib3dsIo *io) { ((uint32_t)b[2] << 16) | ((uint32_t)b[1] << 8) | ((uint32_t)b[0]); - /* --- Code for OpenSceneGraph --- */ - if (s_requiresByteSwap) - { - osg::swapBytes4((char*)&d); - } - /* --- (end) Code for OpenSceneGraph --- */ return(d); } @@ -230,12 +208,6 @@ lib3ds_io_read_intw(Lib3dsIo *io) { lib3ds_io_read(io, b, 2); w = ((uint16_t)b[1] << 8) | ((uint16_t)b[0]); - /* --- Code for OpenSceneGraph --- */ - if (s_requiresByteSwap) - { - osg::swapBytes2((char*)&w); - } - /* --- (end) Code for OpenSceneGraph --- */ return((int16_t)w); } @@ -254,12 +226,6 @@ lib3ds_io_read_intd(Lib3dsIo *io) { ((uint32_t)b[2] << 16) | ((uint32_t)b[1] << 8) | ((uint32_t)b[0]); - /* --- Code for OpenSceneGraph --- */ - if (s_requiresByteSwap) - { - osg::swapBytes4((char*)&d); - } - /* --- (end) Code for OpenSceneGraph --- */ return((int32_t)d); } @@ -278,12 +244,6 @@ lib3ds_io_read_float(Lib3dsIo *io) { ((uint32_t)b[2] << 16) | ((uint32_t)b[1] << 8) | ((uint32_t)b[0]); - /* --- Code for OpenSceneGraph --- */ - if (s_requiresByteSwap) - { - osg::swapBytes4((char*)&(d.dword_value)); - } - /* --- (end) Code for OpenSceneGraph --- */ return d.float_value; } @@ -360,18 +320,11 @@ lib3ds_io_write_byte(Lib3dsIo *io, uint8_t b) { */ void lib3ds_io_write_word(Lib3dsIo *io, uint16_t w) { - /* --- Code for OpenSceneGraph --- */ - if (s_requiresByteSwap) - { - osg::swapBytes2((char*)&w); - } - /* --- (end) Code for OpenSceneGraph --- */ - uint8_t b[2]; assert(io); - b[1] = ((uint16_t)w & 0xFF00) >> 8; - b[0] = ((uint16_t)w & 0x00FF); + b[1] = uint8_t((w & 0xFF00) >> 8); + b[0] = uint8_t(w & 0x00FF); if (lib3ds_io_write(io, b, 2) != 2) { lib3ds_io_write_error(io); } @@ -383,12 +336,6 @@ lib3ds_io_write_word(Lib3dsIo *io, uint16_t w) { */ void lib3ds_io_write_dword(Lib3dsIo *io, uint32_t d) { - /* --- Code for OpenSceneGraph --- */ - if (s_requiresByteSwap) - { - osg::swapBytes4((char*)&d); - } - /* --- (end) Code for OpenSceneGraph --- */ uint8_t b[4]; assert(io); @@ -419,17 +366,11 @@ lib3ds_io_write_intb(Lib3dsIo *io, int8_t b) { */ void lib3ds_io_write_intw(Lib3dsIo *io, int16_t w) { - /* --- Code for OpenSceneGraph --- */ - if (s_requiresByteSwap) - { - osg::swapBytes2((char*)&w); - } - /* --- (end) Code for OpenSceneGraph --- */ uint8_t b[2]; assert(io); - b[1] = ((uint16_t)w & 0xFF00) >> 8; - b[0] = ((uint16_t)w & 0x00FF); + b[1] = uint8_t((w & 0xFF00) >> 8); + b[0] = uint8_t(w & 0x00FF); if (lib3ds_io_write(io, b, 2) != 2) { lib3ds_io_write_error(io); } @@ -441,12 +382,6 @@ lib3ds_io_write_intw(Lib3dsIo *io, int16_t w) { */ void lib3ds_io_write_intd(Lib3dsIo *io, int32_t d) { - /* --- Code for OpenSceneGraph --- */ - if (s_requiresByteSwap) - { - osg::swapBytes4((char*)&d); - } - /* --- (end) Code for OpenSceneGraph --- */ uint8_t b[4]; assert(io); @@ -465,17 +400,11 @@ lib3ds_io_write_intd(Lib3dsIo *io, int32_t d) { */ void lib3ds_io_write_float(Lib3dsIo *io, float l) { - uint8_t b[4]; + uint8_t b[4]; Lib3dsDwordFloat d; assert(io); d.float_value = l; - /* --- Code for OpenSceneGraph --- */ - if (s_requiresByteSwap) - { - osg::swapBytes4((char*)&d.dword_value); - } - /* --- (end) Code for OpenSceneGraph --- */ b[3] = (uint8_t)(((uint32_t)d.dword_value & 0xFF000000) >> 24); b[2] = (uint8_t)(((uint32_t)d.dword_value & 0x00FF0000) >> 16); b[1] = (uint8_t)(((uint32_t)d.dword_value & 0x0000FF00) >> 8); diff --git a/src/osgPlugins/3ds/lib3ds/lib3ds_node.c b/src/osgPlugins/3ds/lib3ds/lib3ds_node.c index 9db3c7487..b49fe78fb 100644 --- a/src/osgPlugins/3ds/lib3ds/lib3ds_node.c +++ b/src/osgPlugins/3ds/lib3ds/lib3ds_node.c @@ -166,7 +166,7 @@ lib3ds_node_new_mesh_instance(Lib3dsMesh *mesh, const char *instance_name, float Lib3dsCameraNode* lib3ds_node_new_camera(Lib3dsCamera *camera) { - Lib3dsNode *node = lib3ds_node_new(LIB3DS_NODE_CAMERA); + Lib3dsNode *node; Lib3dsCameraNode *n; assert(camera); diff --git a/src/osgPlugins/3ds/lib3ds/lib3ds_track.c b/src/osgPlugins/3ds/lib3ds/lib3ds_track.c index 83bfc71bd..58c97ae08 100644 --- a/src/osgPlugins/3ds/lib3ds/lib3ds_track.c +++ b/src/osgPlugins/3ds/lib3ds/lib3ds_track.c @@ -235,7 +235,7 @@ setup_segment(Lib3dsTrack *track, int index, Lib3dsKey *pp, Lib3dsKey *p0, Lib3d *pp = track->keys[track->nkeys - 2]; pp->frame = track->keys[track->nkeys - 2].frame - (track->keys[track->nkeys - 1].frame - track->keys[0].frame); } - else ip = -1; // Avoids a compiler warning + else ip = -1; // Avoids a compiler warning } *p0 = track->keys[index - 1]; @@ -250,6 +250,7 @@ setup_segment(Lib3dsTrack *track, int index, Lib3dsKey *pp, Lib3dsKey *p0, Lib3d *pn = track->keys[1]; pn->frame = track->keys[1].frame + (track->keys[track->nkeys-1].frame - track->keys[0].frame); } + else in = -1; // Avoids a compiler warning } if (track->type == LIB3DS_TRACK_QUAT) {