From 9434b764a87f42e1e59f8605be97f0289dfee4ea Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Thu, 11 Feb 2010 11:56:43 +0000 Subject: [PATCH] From Sukender, "- Added support for extended filenames (=not 8.3) for images: reads without crashing, optionnally write extended filenames (correctly truncate names if option is OFF). Write option is OFF by default. - Improved identifiers generation in duplicate name handling (was limited to 1000 name collisions, which can be very short for some usages). - Set all read/write operations use a custom log function that will redirect lib3DS log to osg::notify() (was only used for streams) - Removed custom code (now uses osgDB::getFilePath()) - Added missing supportsOption() calls - Cleaned a few minor things" --- src/osgPlugins/3ds/ReaderWriter3DS.cpp | 138 +++++----- src/osgPlugins/3ds/WriterCompareTriangle.cpp | 270 +++++++++---------- src/osgPlugins/3ds/WriterCompareTriangle.h | 102 +++---- src/osgPlugins/3ds/WriterNodeVisitor.cpp | 157 +++++++---- src/osgPlugins/3ds/WriterNodeVisitor.h | 25 +- 5 files changed, 369 insertions(+), 323 deletions(-) diff --git a/src/osgPlugins/3ds/ReaderWriter3DS.cpp b/src/osgPlugins/3ds/ReaderWriter3DS.cpp index 3da51f2a6..26e20c99c 100644 --- a/src/osgPlugins/3ds/ReaderWriter3DS.cpp +++ b/src/osgPlugins/3ds/ReaderWriter3DS.cpp @@ -1,4 +1,3 @@ -#define ENABLE_3DS_WRITER 1 // Enables the 3DS writer (the define should be removed when the writer will be stable and tested enough) #include #include @@ -20,9 +19,7 @@ //MIKEC debug only for PrintVisitor #include -#ifdef ENABLE_3DS_WRITER - #include "WriterNodeVisitor.h" -#endif +#include "WriterNodeVisitor.h" #include "lib3ds/lib3ds.h" #include #include @@ -171,17 +168,16 @@ public: virtual ReadResult readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const; virtual ReadResult readNode(std::istream& fin, const Options* options) const; -#if ENABLE_3DS_WRITER + virtual ReadResult doReadNode(std::istream& fin, const Options* options, const std::string & fileNamelib3ds) const; ///< Subfunction of readNode()s functions. + virtual WriteResult writeNode(const osg::Node& /*node*/,const std::string& /*fileName*/,const Options* =NULL) const; virtual WriteResult writeNode(const osg::Node& /*node*/,std::ostream& /*fout*/,const Options* =NULL) const; -#endif + virtual WriteResult doWriteNode(const osg::Node& /*node*/,std::ostream& /*fout*/,const Options*, const std::string & fileNamelib3ds) const; protected: ReadResult constructFrom3dsFile(Lib3dsFile *f,const std::string& filename, const Options* options) const; -#if ENABLE_3DS_WRITER bool createFileObject(const osg::Node& node, Lib3dsFile * file3ds,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const; -#endif class ReaderObject { @@ -220,7 +216,12 @@ REGISTER_OSGPLUGIN(3ds, ReaderWriter3DS) ReaderWriter3DS::ReaderWriter3DS() { supportsExtension("3ds","3D Studio model format"); - supportsOption("OutputTextureFiles","Write out the texture images to file"); + //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(); #if 0 @@ -252,16 +253,18 @@ ReaderWriter3DS::ReaderObject::ReaderObject(const osgDB::ReaderWriter::Options* checkForEspilonIdentityMatrices(false), restoreMatrixTransformsNoMeshes(false) { - std::istringstream iss(options->getOptionString()); - std::string opt; - while (iss >> opt) - { - if (opt == "noMatrixTransforms") - noMatrixTransforms = true; - if (opt == "checkForEspilonIdentityMatrices") - checkForEspilonIdentityMatrices = true; - if (opt == "restoreMatrixTransformsNoMeshes") - restoreMatrixTransformsNoMeshes = true; + if (options) { + std::istringstream iss(options->getOptionString()); + std::string opt; + while (iss >> opt) + { + if (opt == "noMatrixTransforms") + noMatrixTransforms = true; + if (opt == "checkForEspilonIdentityMatrices") + checkForEspilonIdentityMatrices = true; + if (opt == "restoreMatrixTransformsNoMeshes") + restoreMatrixTransformsNoMeshes = true; + } } } @@ -556,7 +559,6 @@ static long filei_seek_func(void *self, long offset, Lib3dsIoSeek origin) { return f->fail() ? -1 : 0; } -#if ENABLE_3DS_WRITER static long fileo_seek_func(void *self, long offset, Lib3dsIoSeek origin) { std::ostream *f = reinterpret_cast(self); ios_base::seekdir o = ios_base::beg; @@ -566,19 +568,16 @@ static long fileo_seek_func(void *self, long offset, Lib3dsIoSeek origin) { f->seekp(offset, o); return f->fail() ? -1 : 0; } -#endif static long filei_tell_func(void *self) { std::istream *f = reinterpret_cast(self); return f->tellg(); } -#if ENABLE_3DS_WRITER static long fileo_tell_func(void *self) { std::ostream *f = reinterpret_cast(self); return f->tellp(); } -#endif static size_t filei_read_func(void *self, void *buffer, size_t size) { @@ -587,19 +586,18 @@ static size_t filei_read_func(void *self, void *buffer, size_t size) { return f->gcount(); } -#if ENABLE_3DS_WRITER static size_t fileo_write_func(void *self, const void *buffer, size_t size) { std::ostream *f = reinterpret_cast(self); f->write(static_cast(buffer), size); return f->fail() ? 0 : size; } -#endif static void fileio_log_func(void *self, Lib3dsLogLevel level, int indent, const char *msg) { osg::NotifySeverity l = osg::INFO; - if (level == LIB3DS_LOG_ERROR) l = osg::FATAL; - else if (level == LIB3DS_LOG_WARN) l = osg::WARN; + // Intentionally NOT mapping 3DS levels with OSG levels + if (level == LIB3DS_LOG_ERROR) l = osg::WARN; + 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; @@ -608,14 +606,21 @@ static void fileio_log_func(void *self, Lib3dsLogLevel level, int indent, const osgDB::ReaderWriter::ReadResult ReaderWriter3DS::readNode(std::istream& fin, const osgDB::ReaderWriter::Options* options) const { - osgDB::ReaderWriter::ReadResult result = ReadResult::FILE_NOT_HANDLED; - - std::string optFileName = ""; + std::string optFileName; if (options) { optFileName = options->getPluginStringData("STREAM_FILENAME"); if (optFileName.empty()) optFileName = options->getPluginStringData("filename"); } + return doReadNode(fin, options, optFileName); +} + +osgDB::ReaderWriter::ReadResult ReaderWriter3DS::doReadNode(std::istream& fin, const osgDB::ReaderWriter::Options* options, const std::string & fileNamelib3ds) const +{ + osg::ref_ptr local_opt = options ? static_cast(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options; + local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileNamelib3ds)); + + osgDB::ReaderWriter::ReadResult result = ReadResult::FILE_NOT_HANDLED; // Prepare io structure to tell how to read the stream Lib3dsIo io; @@ -629,7 +634,7 @@ osgDB::ReaderWriter::ReadResult ReaderWriter3DS::readNode(std::istream& fin, co Lib3dsFile * file3ds = lib3ds_file_new(); if (lib3ds_file_read(file3ds, &io) != 0) { - result = constructFrom3dsFile(file3ds,optFileName,options); + result = constructFrom3dsFile(file3ds,fileNamelib3ds,options); lib3ds_file_free(file3ds); } @@ -638,15 +643,21 @@ osgDB::ReaderWriter::ReadResult ReaderWriter3DS::readNode(std::istream& fin, co osgDB::ReaderWriter::ReadResult ReaderWriter3DS::readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const { - osgDB::ReaderWriter::ReadResult result = ReadResult::FILE_NOT_HANDLED; - std::string ext = osgDB::getLowerCaseFileExtension(file); if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED; std::string fileName = osgDB::findDataFile( file, options ); if (fileName.empty()) return ReadResult::FILE_NOT_FOUND; - Lib3dsFile *f = lib3ds_file_open(fileName.c_str() /*,options*/); + // 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(file.c_str(), std::ios_base::in | std::ios_base::binary); + if (!fin.good()) return ReadResult::ERROR_IN_READING_FILE; + return doReadNode(fin, options, fileName); +/* + osgDB::ReaderWriter::ReadResult result = ReadResult::FILE_NOT_HANDLED; + Lib3dsFile *f = lib3ds_file_open(fileName.c_str()); // ,options if (f) { @@ -658,6 +669,7 @@ osgDB::ReaderWriter::ReadResult ReaderWriter3DS::readNode(const std::string& fil } return result; +*/ } osgDB::ReaderWriter::ReadResult ReaderWriter3DS::constructFrom3dsFile(Lib3dsFile *f,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const @@ -1058,14 +1070,15 @@ osg::StateSet* ReaderWriter3DS::ReaderObject::createStateSet(Lib3dsMaterial *mat } - -#if ENABLE_3DS_WRITER osgDB::ReaderWriter::WriteResult ReaderWriter3DS::writeNode(const osg::Node& node,const std::string& fileName,const Options* options) const { std::string ext = osgDB::getLowerCaseFileExtension(fileName); if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED; - //osg::notify(osg::WARN) << "!!WARNING!! 3DS write support is incomplete" << std::endl; - + osgDB::makeDirectoryForFile(fileName.c_str()); + std::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); +/* bool ok = true; Lib3dsFile * file3ds = lib3ds_file_new(); if (!file3ds) return WriteResult(WriteResult::ERROR_IN_WRITING_FILE); @@ -1074,7 +1087,7 @@ osgDB::ReaderWriter::WriteResult ReaderWriter3DS::writeNode(const osg::Node& nod osg::ref_ptr local_opt = options ? static_cast(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options; local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileName)); - if (!createFileObject(node, file3ds, fileName, local_opt.get())) ok = false; + if (!createFileObject(node, file3ds, fileName, local_opt)) ok = false; if (ok && !lib3ds_file_save(file3ds, fileName.c_str())) ok = false; } catch (...) { lib3ds_file_free(file3ds); @@ -1083,18 +1096,24 @@ osgDB::ReaderWriter::WriteResult ReaderWriter3DS::writeNode(const osg::Node& nod lib3ds_file_free(file3ds); return ok ? WriteResult(WriteResult::FILE_SAVED) : WriteResult(WriteResult::ERROR_IN_WRITING_FILE); - //return ok ? WriteResult(WriteResult::FILE_SAVED) : WriteResult(WriteResult::FILE_NOT_HANDLED); +*/ } 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; - std::string optFileName = ""; - if (options) - { + std::string optFileName; + if (options) { optFileName = options->getPluginStringData("STREAM_FILENAME"); } + return doWriteNode(node, fout, options, optFileName); +} + +osgDB::ReaderWriter::WriteResult ReaderWriter3DS::doWriteNode(const osg::Node& node,std::ostream& fout, const Options* options, const std::string & fileNamelib3ds) const { + osg::ref_ptr local_opt = options ? static_cast(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options; + local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileNamelib3ds)); + Lib3dsIo io; io.self = &fout; io.seek_func = fileo_seek_func; @@ -1104,12 +1123,11 @@ osgDB::ReaderWriter::WriteResult ReaderWriter3DS::writeNode(const osg::Node& nod io.log_func = fileio_log_func; Lib3dsFile * file3ds = lib3ds_file_new(); + if (!file3ds) return WriteResult(WriteResult::ERROR_IN_WRITING_FILE); + bool ok = true; try { - osg::ref_ptr local_opt = options ? static_cast(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options; - local_opt->getDatabasePathList().push_front(osgDB::getFilePath(optFileName)); - - if (!createFileObject(node, file3ds, optFileName, local_opt.get())) ok = false; + if (!createFileObject(node, file3ds, fileNamelib3ds, local_opt.get())) ok = false; if (ok && !lib3ds_file_write(file3ds, &io)) ok = false; } catch (...) { @@ -1122,32 +1140,12 @@ osgDB::ReaderWriter::WriteResult ReaderWriter3DS::writeNode(const osg::Node& nod //return ok ? WriteResult(WriteResult::FILE_SAVED) : WriteResult(WriteResult::FILE_NOT_HANDLED); } -const std::string getParent(const std::string & pathBad) -{ - const std::string & path = osgDB::convertFileNameToNativeStyle(pathBad); - - std::string parent = ""; - std::string tmp = ""; - for(std::string::const_iterator itPath = path.begin();; ++itPath) - { - if (!parent.empty()) - parent += '\\'; - parent += tmp; - tmp.clear(); - for(;itPath != path.end() && *itPath != '\\'; ++itPath) - tmp += *itPath; - if (itPath == path.end()) - break; - } - return parent; -} - bool ReaderWriter3DS::createFileObject(const osg::Node& node, Lib3dsFile * file3ds,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const { - WriterNodeVisitor w(file3ds, fileName, options, getParent(node.getName())); - const_cast(node).accept(w); // TODO Remove that ugly const_cast<>. Any idea? + WriterNodeVisitor w(file3ds, fileName, options, osgDB::getFilePath(node.getName())); + const_cast(node).accept(w); // Ugly const_cast<> for visitor... if (!w.suceedLastApply()) return false; w.writeMaterials(); return true; //w.good(); } -#endif // ENABLE_3DS_WRITER + diff --git a/src/osgPlugins/3ds/WriterCompareTriangle.cpp b/src/osgPlugins/3ds/WriterCompareTriangle.cpp index e4230f760..e0f900357 100644 --- a/src/osgPlugins/3ds/WriterCompareTriangle.cpp +++ b/src/osgPlugins/3ds/WriterCompareTriangle.cpp @@ -1,135 +1,135 @@ -#include "WriterCompareTriangle.h" - -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 cut scene in too many blocs - - osg::notify(osg::ALWAYS) << "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; //This 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; - } - } - throw "Point is not in any blocs"; -} - -int WriterCompareTriangle::inWhichBox(const osg::BoundingBox::vec_type & point) const { - return inWhichBox(point.x(), point.y(), point.z()); -} +#include "WriterCompareTriangle.h" + +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 cut scene in too many blocs + + osg::notify(osg::ALWAYS) << "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; //This 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; + } + } + throw "Point is not in any blocs"; +} + +int WriterCompareTriangle::inWhichBox(const osg::BoundingBox::vec_type & point) const { + return inWhichBox(point.x(), point.y(), point.z()); +} diff --git a/src/osgPlugins/3ds/WriterCompareTriangle.h b/src/osgPlugins/3ds/WriterCompareTriangle.h index c399038d5..f1a8db9c6 100644 --- a/src/osgPlugins/3ds/WriterCompareTriangle.h +++ b/src/osgPlugins/3ds/WriterCompareTriangle.h @@ -1,51 +1,51 @@ -#ifndef _3DS_WRITER_COMPARE_TRIANGLE_HEADER__ -#define _3DS_WRITER_COMPARE_TRIANGLE_HEADER__ - -#include -#include -#include - -struct Triangle -{ - unsigned int t1; - unsigned int t2; - unsigned int t3; - unsigned int material; -}; - -class WriterCompareTriangle { -public: - WriterCompareTriangle(const osg::Geode & geode, unsigned int nbVertices); - - bool operator()(const std::pair & t1, - const std::pair & t2) const; -private: - void // This function prevent from cut scene in too many blocs - setMaxMin(unsigned int & nbVerticesX, - unsigned int & nbVerticesY, - unsigned int & nbVerticesZ) const; - - /** - * Cut the scene in different bloc to sort. - * \param nbVertices is the number of vertice in mesh. - * \param sceneBox contain the size of the scene. - */ - void - cutscene(int nbVertices, - const osg::BoundingBox & sceneBox); - - /** - * Find in which box those points are. - * \return the place of the box in the vector. - * \sa See cutScene() about the definition of the boxes for faces sorting. - */ - int inWhichBox(const osg::BoundingBox::value_type x, - const osg::BoundingBox::value_type y, - const osg::BoundingBox::value_type z) const; - int inWhichBox(const osg::BoundingBox::vec_type & point) const; - - const osg::Geode & geode; - std::vector boxList; -}; - -#endif // _3DS_WRITER_COMPARE_TRIANGLE_HEADER__ +#ifndef _3DS_WRITER_COMPARE_TRIANGLE_HEADER__ +#define _3DS_WRITER_COMPARE_TRIANGLE_HEADER__ + +#include +#include +#include + +struct Triangle +{ + unsigned int t1; + unsigned int t2; + unsigned int t3; + unsigned int material; +}; + +class WriterCompareTriangle { +public: + WriterCompareTriangle(const osg::Geode & geode, unsigned int nbVertices); + + bool operator()(const std::pair & t1, + const std::pair & t2) const; +private: + void // This function prevent from cut scene in too many blocs + setMaxMin(unsigned int & nbVerticesX, + unsigned int & nbVerticesY, + unsigned int & nbVerticesZ) const; + + /** + * Cut the scene in different bloc to sort. + * \param nbVertices is the number of vertice in mesh. + * \param sceneBox contain the size of the scene. + */ + void + cutscene(int nbVertices, + const osg::BoundingBox & sceneBox); + + /** + * Find in which box those points are. + * \return the place of the box in the vector. + * \sa See cutScene() about the definition of the boxes for faces sorting. + */ + int inWhichBox(const osg::BoundingBox::value_type x, + const osg::BoundingBox::value_type y, + const osg::BoundingBox::value_type z) const; + int inWhichBox(const osg::BoundingBox::vec_type & point) const; + + const osg::Geode & geode; + std::vector boxList; +}; + +#endif // _3DS_WRITER_COMPARE_TRIANGLE_HEADER__ diff --git a/src/osgPlugins/3ds/WriterNodeVisitor.cpp b/src/osgPlugins/3ds/WriterNodeVisitor.cpp index f2c9f5628..9ee3564a6 100644 --- a/src/osgPlugins/3ds/WriterNodeVisitor.cpp +++ b/src/osgPlugins/3ds/WriterNodeVisitor.cpp @@ -91,9 +91,10 @@ bool is83(const std::string & s) { } /// Tests if the given string is a path supported by 3DS format (8.3, 63 chars max). -bool is3DSpath(const std::string & s) { +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! unsigned int tokenBegin = 0; for (unsigned int tokenEnd=0; tokenEnd != std::string::npos; tokenBegin = tokenEnd+1) { @@ -145,7 +146,7 @@ public: triangle.t2 = i2; triangle.t3 = i3; triangle.material = _material; - _listTriangles.push_back(std::make_pair(triangle, _drawable_n)); + _listTriangles.push_back(std::pair(triangle, _drawable_n)); } virtual void begin(GLenum mode) { @@ -334,7 +335,7 @@ void PrimitiveIndexWriter::drawArrays(GLenum mode,GLint first,GLsizei count) case(GL_LINE_LOOP): //break; default: - osg::notify(osg::WARN) << "WriterNodeVisitor :: can't handle mode " << mode << std::endl; + osg::notify(osg::WARN) << "3DS WriterNodeVisitor: can't handle mode " << mode << std::endl; break; } } @@ -399,35 +400,54 @@ WriterNodeVisitor::Material::Material(WriterNodeVisitor & writerNodeVisitor, osg } -std::string -getPathRelative(const std::string & srcBad, - const std::string & dstBad) +// 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*/) { - if(srcBad.empty()) - return osgDB::getSimpleFileName(dstBad); - const std::string & src = osgDB::convertFileNameToNativeStyle(srcBad); - const std::string & dst = osgDB::convertFileNameToNativeStyle(dstBad); - std::string::const_iterator itDst = dst.begin(); - std::string::const_iterator itSrc = src.begin(); - std::string result = ""; - - while(itDst != dst.end()) + std::string::size_type slash = to.find_last_of('/'); + std::string::size_type backslash = to.find_last_of('\\'); + if (slash == std::string::npos) { - if (itSrc != src.end() && *itDst == *itSrc) - ++itSrc; - else if (!result.empty() || *itDst != '\\') - result += *itDst; - ++itDst; + if (backslash == std::string::npos) return to; + slash = backslash; } - if (itSrc != src.end()) - result = osgDB::getSimpleFileName(dst); - return result; + 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) +std::string convertExt(const std::string & path, bool extendedFilePaths) { + if (extendedFilePaths) return path; // Extensions are not truncated for extended filenames + std::string ext = osgDB::getFileExtensionIncludingDot(path); if (ext == ".tiff") ext = ".tif"; else if (ext == ".jpeg") ext = ".jpg"; @@ -435,6 +455,37 @@ std::string convertExt(const std::string & path) return osgDB::getNameLessExtension(path) + ext; } + +WriterNodeVisitor::WriterNodeVisitor(Lib3dsFile * file3ds, const std::string & fileName, + const osgDB::ReaderWriter::Options* options, + const std::string & srcDirectory) : + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), + _suceedLastApply(true), + _srcDirectory(srcDirectory), + file3ds(file3ds), + _currentStateSet(new osg::StateSet()), + _lastMaterialIndex(0), + _lastMeshIndex(0), + _cur3dsNode(NULL), + options(options), + _imageCount(0), + _extendedFilePaths(false) +{ + if (!fileName.empty()) + _directory = options->getDatabasePathList().empty() ? osgDB::getFilePath(fileName) : options->getDatabasePathList().front(); + + if (options) { + std::istringstream iss(options->getOptionString()); + std::string opt; + while (iss >> opt) + { + if (opt == "extended3dsFilePaths" || opt == "extended3DSFilePaths") + _extendedFilePaths = true; + } + } +} + + void WriterNodeVisitor::writeMaterials() { unsigned int nbMat = _materialMap.size(); @@ -470,9 +521,9 @@ void WriterNodeVisitor::writeMaterials() else { path = getPathRelative(_srcDirectory, mat.image->getFileName()); } - path = convertExt(path); + path = convertExt(path, _extendedFilePaths); - if(!is3DSpath(path)) { + if(!is3DSpath(path, _extendedFilePaths)) { path = getUniqueName(path, "", true); //path = osgDB::getSimpleFileName(path); } @@ -482,7 +533,11 @@ void WriterNodeVisitor::writeMaterials() osgDB::makeDirectoryForFile(path); //if (mat.image->valid()) osgDB::writeImageFile(*(mat.image), path); - osgDB::writeImageFile(*(mat.image), path); + if(_imageSet.find(mat.image.get()) == _imageSet.end()) + { + _imageSet.insert(mat.image.get()); + osgDB::writeImageFile(*(mat.image), path); + } if (mat.texture_transparency) tex.flags |= LIB3DS_TEXTURE_ALPHA_SOURCE; if (mat.texture_no_tile) tex.flags |= LIB3DS_TEXTURE_NO_TILE; } @@ -497,26 +552,28 @@ void WriterNodeVisitor::writeMaterials() std::string WriterNodeVisitor::getUniqueName(const std::string& _defaultValue, const std::string & _defaultPrefix, bool nameIsPath) { - if (_defaultPrefix.length()>=4) throw "Default prefix is too long"; // Arbitrarily defined to 3 chars. You can modify this, but you may have to change the code so that finding a number is okay, even when changing the default prefix length. + static const unsigned int MAX_PREFIX_LEGNTH = 4; + if (_defaultPrefix.length()>MAX_PREFIX_LEGNTH) throw "Default prefix is too long"; // Arbitrarily defined to 4 chars. // Tests if default name is valid and unique bool defaultIs83 = is83(_defaultValue); - bool defaultIsValid = nameIsPath ? is3DSpath(_defaultValue) : defaultIs83; + 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. + // 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); unsigned int max_val = 0; std::string truncDefaultValue = ""; - for (unsigned int i = 0; i < std::min(defaultValue.size(), 4); ++i) + for (unsigned int i = 0; i < std::min(defaultValue.size(), MAX_PREFIX_LEGNTH); ++i) { if (defaultValue[i] == '.') { @@ -525,47 +582,53 @@ std::string WriterNodeVisitor::getUniqueName(const std::string& _defaultValue, c } } if (truncDefaultValue.empty()) - truncDefaultValue = defaultValue.substr(0, std::min(defaultValue.size(), 4)); + 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)) -1; // defaultPrefix.length()-1 because we add an underscore ("_") + max_val = static_cast(pow(10., 8. - truncDefaultValue.length())) -1; pairPrefix = _mapPrefix.find(truncDefaultValue); } - if (defaultIs83 && (_mapPrefix.end() == pairPrefix || pairPrefix->second <= max_val)) + if (defaultIs83 && (pairPrefix == _mapPrefix.end() || pairPrefix->second <= max_val)) { defaultPrefix = truncDefaultValue; } else { - max_val = static_cast(pow(10., 8. - defaultPrefix.length() - 1)) - 1; // defaultPrefix.length()-1 because we add an underscore ("_") + max_val = static_cast(pow(10., 8. - defaultPrefix.length())) -1; pairPrefix = _mapPrefix.find(defaultPrefix); } unsigned int searchStart = 0; - if (pairPrefix != _mapPrefix.end()) + if (pairPrefix != _mapPrefix.end()) { searchStart = pairPrefix->second; + } for(unsigned int i = searchStart; i <= max_val; ++i) { std::stringstream ss; - ss << defaultPrefix << "_" << i; + ss << defaultPrefix << i; const std::string & res = ss.str(); if (_nameMap.find(res) == _nameMap.end()) { - if (pairPrefix != _mapPrefix.end()) - { + if (pairPrefix != _mapPrefix.end()) { pairPrefix->second = i + 1; - } - else - { - _mapPrefix.insert(std::make_pair(defaultPrefix, i + 1)); + } else { + _mapPrefix.insert(std::pair(defaultPrefix, i + 1)); } _nameMap.insert(res); return res + ext; } } - if (defaultPrefix == "_") _lastGeneratedNumberedName = max_val; + + // 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); + // Try with default prefix if not arleady done + if (defaultPrefix != std::string("_")) return getUniqueName(_defaultValue, "_", nameIsPath); throw "No more names available! Is default prefix too long?"; } @@ -602,10 +665,10 @@ WriterNodeVisitor::getMeshIndexForGeometryIndex(MapIndices & index_vert, unsigned int index, unsigned int drawable_n) { - MapIndices::iterator itIndex = index_vert.find(std::make_pair(index, drawable_n)); + MapIndices::iterator itIndex = index_vert.find(std::pair(index, drawable_n)); if (itIndex == index_vert.end()) { unsigned int indexMesh = index_vert.size(); - index_vert.insert(std::make_pair(std::make_pair(index, drawable_n), indexMesh)); + index_vert.insert(std::make_pair(std::pair(index, drawable_n), indexMesh)); return indexMesh; } return itIndex->second; @@ -692,7 +755,7 @@ WriterNodeVisitor::buildFaces(osg::Geode & geo, if (listTriangles.size() >= MAX_FACES-2 || ((nbVertices) >= MAX_VERTICES-2)) { - osg::notify(osg::ALWAYS) << "Sorting elements..." << std::endl; + osg::notify(osg::INFO) << "Sorting elements..." << std::endl; WriterCompareTriangle cmp(geo, nbVertices); std::sort(listTriangles.begin(), listTriangles.end(), cmp); } diff --git a/src/osgPlugins/3ds/WriterNodeVisitor.h b/src/osgPlugins/3ds/WriterNodeVisitor.h index 6508739ca..f402ffd8b 100644 --- a/src/osgPlugins/3ds/WriterNodeVisitor.h +++ b/src/osgPlugins/3ds/WriterNodeVisitor.h @@ -41,6 +41,7 @@ #include "lib3ds/lib3ds.h" #include "WriterCompareTriangle.h" +#include void copyOsgMatrixToLib3dsMatrix(Lib3dsMatrix lib3ds_matrix, const osg::Matrix& osg_matrix); @@ -49,30 +50,13 @@ typedef std::vector > ListTriangle; //the int is the dr 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) : - osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), - _suceedLastApply(true), - _srcDirectory(srcDirectory), - file3ds(file3ds), - _currentStateSet(new osg::StateSet()), - _lastGeneratedNumberedName(0), - _lastMaterialIndex(0), - _lastMeshIndex(0), - _cur3dsNode(NULL), - options(options), - _imageCount(0) - { - //supportsOption("flipTexture", "flip texture upside-down"); - if (!fileName.empty()) - _directory = options->getDatabasePathList().empty() ? osgDB::getFilePath(fileName) : options->getDatabasePathList().front(); - } + const std::string & srcDirectory); bool suceedLastApply() const; void failedApply(); @@ -215,15 +199,16 @@ class WriterNodeVisitor: public osg::NodeVisitor Lib3dsFile * file3ds; StateSetStack _stateSetStack; osg::ref_ptr _currentStateSet; - std::map _mapPrefix; + std::map _mapPrefix; ///< List of next number to use in unique name generation, for each prefix std::set _nameMap; MaterialMap _materialMap; - unsigned int _lastGeneratedNumberedName; unsigned int _lastMaterialIndex; unsigned int _lastMeshIndex; Lib3dsMeshInstanceNode * _cur3dsNode; const osgDB::ReaderWriter::Options* options; unsigned int _imageCount; + bool _extendedFilePaths; + std::set _imageSet; }; #endif