From 9da669cbd89796e1196d25d3ac295da5a691ecea Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Fri, 11 Mar 2011 11:25:19 +0000 Subject: [PATCH] From Sukender, "Added support for texcoords scale and offset (UV scaling and offset in Lib3DS Materials), for texture unit 0." --- src/osgPlugins/3ds/ReaderWriter3DS.cpp | 68 +++++-- src/osgPlugins/3ds/WriterNodeVisitor.cpp | 249 +++++++++++++---------- src/osgPlugins/3ds/WriterNodeVisitor.h | 13 +- 3 files changed, 204 insertions(+), 126 deletions(-) diff --git a/src/osgPlugins/3ds/ReaderWriter3DS.cpp b/src/osgPlugins/3ds/ReaderWriter3DS.cpp index 8cc49bc5b..3956ceb3a 100644 --- a/src/osgPlugins/3ds/ReaderWriter3DS.cpp +++ b/src/osgPlugins/3ds/ReaderWriter3DS.cpp @@ -184,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; @@ -203,14 +214,14 @@ 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; @@ -368,7 +379,7 @@ void print(Lib3dsNode *node, int level) void ReaderWriter3DS::ReaderObject::addDrawableFromFace(osg::Geode * geode, FaceList & faceList, Lib3dsMesh * mesh, const osg::Matrix * matrix, - osg::StateSet * stateSet) + StateSetInfo & ssi) { if (_useSmoothingGroups) { @@ -388,22 +399,22 @@ void ReaderWriter3DS::ReaderObject::addDrawableFromFace(osg::Geode * geode, Face // to ensure the vertices on adjacent groups // don't get shared. FaceList& smoothFaceMap = sitr->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()); } } @@ -443,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; @@ -746,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]; @@ -821,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; @@ -876,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); } } @@ -1049,9 +1076,9 @@ 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; @@ -1151,6 +1178,7 @@ osg::StateSet* ReaderWriter3DS::ReaderObject::createStateSet(Lib3dsMaterial *mat 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); } @@ -1172,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); } diff --git a/src/osgPlugins/3ds/WriterNodeVisitor.cpp b/src/osgPlugins/3ds/WriterNodeVisitor.cpp index 26711238f..3752be8df 100644 --- a/src/osgPlugins/3ds/WriterNodeVisitor.cpp +++ b/src/osgPlugins/3ds/WriterNodeVisitor.cpp @@ -82,13 +82,6 @@ inline void copyOsgQuatToLib3dsQuat(float lib3ds_vector[4], const osg::Quat& osg lib3ds_vector[3] = static_cast(-angle); } -std::string getFileName(const std::string & path) -{ - size_t 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). @@ -107,23 +100,20 @@ 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) +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 >= 64 || len == 0) return false; - if (extendedFilePaths) return true; // Extended paths are simply those that fits the 64 bytes buffer! - - // For each subdirectory - size_t tokenLen; - for (size_t 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; + 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); } @@ -397,7 +387,7 @@ WriterNodeVisitor::Material::Material(WriterNodeVisitor & writerNodeVisitor, osg //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) { @@ -501,7 +491,7 @@ void WriterNodeVisitor::writeMaterials() found = true; assert(mat.index>=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); @@ -510,36 +500,40 @@ 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 = osgDB::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 = osgDB::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, _options); - } // 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; @@ -581,92 +575,143 @@ std::string utf8TruncateBytes(const std::string & s, std::string::size_type byte # define truncateFilenameBytes(str, size) std::string(str, 0, size) #endif -std::string WriterNodeVisitor::getUniqueName(const std::string& _defaultValue, const std::string & _defaultPrefix, bool nameIsPath) +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) : ""); - ext = truncateFilenameBytes(ext, 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 = truncateFilenameBytes(defaultValue, 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 = truncateFilenameBytes(defaultValue, 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()); + parentPath = truncateFilenameBytes(parentPath, MAX_PREFIX_LENGTH - (filename.length() + ext.length() + 1)); // +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 = osgDB::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, truncateFilenameBytes(defaultPrefix, 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; @@ -809,7 +854,7 @@ 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) @@ -855,7 +900,7 @@ 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; @@ -1043,11 +1088,11 @@ void WriterNodeVisitor::apply3DSMatrixNode(osg::Node &node, const osg::Matrix * copyOsgVectorToLib3dsVector(pos, osgPos); copyOsgVectorToLib3dsVector(scl, osgScl); copyOsgQuatToLib3dsQuat(rot, osgRot); - node3ds = lib3ds_node_new_mesh_instance(NULL, getUniqueName(node.getName().empty() ? node.className() : node.getName(), prefix).c_str(), pos, scl, rot); + 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(), prefix).c_str(), NULL, NULL, NULL); + node3ds = lib3ds_node_new_mesh_instance(NULL, getUniqueName(node.getName().empty() ? node.className() : node.getName(), true, prefix).c_str(), NULL, NULL, NULL); } lib3ds_file_append_node(_file3ds, reinterpret_cast(node3ds), reinterpret_cast(parent)); diff --git a/src/osgPlugins/3ds/WriterNodeVisitor.h b/src/osgPlugins/3ds/WriterNodeVisitor.h index ebf7471ad..80f369449 100644 --- a/src/osgPlugins/3ds/WriterNodeVisitor.h +++ b/src/osgPlugins/3ds/WriterNodeVisitor.h @@ -190,7 +190,7 @@ class WriterNodeVisitor: public osg::NodeVisitor int processStateSet(osg::StateSet* stateset); - std::string getUniqueName(const std::string& defaultvalue="", const std::string & defaultPrefix = "", bool nameIsPath = false); + 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; @@ -204,8 +204,12 @@ class WriterNodeVisitor: public osg::NodeVisitor 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; + 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; @@ -213,7 +217,8 @@ class WriterNodeVisitor: public osg::NodeVisitor const osgDB::ReaderWriter::Options* _options; unsigned int _imageCount; bool _extendedFilePaths; - std::set _imageSet; + typedef std::map ImageSet; + ImageSet _imageSet; ///< Map used to avoid renaming and writing twice an image }; // end namespace plugin3ds