From Sukender, "Added support for texcoords scale and offset (UV scaling and offset in Lib3DS Materials), for texture unit 0."

This commit is contained in:
Robert Osfield
2011-03-11 11:25:19 +00:00
parent a81b2341c6
commit 9da669cbd8
3 changed files with 204 additions and 126 deletions

View File

@@ -82,13 +82,6 @@ inline void copyOsgQuatToLib3dsQuat(float lib3ds_vector[4], const osg::Quat& osg
lib3ds_vector[3] = static_cast<float>(-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<int>(_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<int>(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<unsigned int>(_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<unsigned int>(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<NameMap::iterator, bool> 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<unsigned int>(defaultValue.size(), MAX_PREFIX_LEGNTH));
assert(truncDefaultValue.size() <= MAX_PREFIX_LEGNTH);
std::map<std::string, unsigned int>::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<unsigned int>(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<unsigned int>(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<NameMap::iterator, bool> 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<unsigned int>(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<std::string, unsigned int>(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<std::string, unsigned int>(defaultPrefix, i + 1));
prefixMap.insert(std::pair<std::string, unsigned int>(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<Lib3dsNode*>(node3ds), reinterpret_cast<Lib3dsNode*>(parent));