diff --git a/src/osgPlugins/3ds/ReaderWriter3DS.cpp b/src/osgPlugins/3ds/ReaderWriter3DS.cpp index 6e716c5da..b721f1210 100644 --- a/src/osgPlugins/3ds/ReaderWriter3DS.cpp +++ b/src/osgPlugins/3ds/ReaderWriter3DS.cpp @@ -221,10 +221,10 @@ 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."); + 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."); setByteOrder(); #if 0 diff --git a/src/osgPlugins/3ds/WriterNodeVisitor.cpp b/src/osgPlugins/3ds/WriterNodeVisitor.cpp index daa89737d..68e3aa936 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 @@ -731,16 +738,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_NOTIFY(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) @@ -749,19 +767,18 @@ 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); @@ -778,16 +795,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; } @@ -813,6 +821,7 @@ WriterNodeVisitor::buildFaces(osg::Geode & geo, 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 @@ -874,48 +883,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 @@ -946,8 +935,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(); @@ -966,7 +959,9 @@ void WriterNodeVisitor::apply( osg::Billboard &node ) ListTriangle listTriangles; bool texcoords = false; OSG_NOTIFY(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(); @@ -980,9 +975,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; } } @@ -999,7 +1000,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; @@ -1010,37 +1016,41 @@ 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) - - // 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); + 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(), 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); + } 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 d55377cf0..f11a77536 100644 --- a/src/osgPlugins/3ds/WriterNodeVisitor.h +++ b/src/osgPlugins/3ds/WriterNodeVisitor.h @@ -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 @@ -193,7 +193,7 @@ class WriterNodeVisitor: public osg::NodeVisitor 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); + void apply3DSMatrixNode(osg::Node &node, const osg::Matrix * m, const char * const prefix); bool _succeeded; std::string _directory;