From e70c3045334a16c977a75bd01e66a192895c1017 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Wed, 10 Mar 2010 16:05:52 +0000 Subject: [PATCH] From Sukender, "I've fixed positions for 3DS writer. Points in 3DS must be in world coordinates and I added what was missing. And by refactoring a bit of code, I may have fixed some StateSets related bugs (was ignoring StateSets for osg::Groups). I also added support for Billboard's points, so now "osgconv lz.osg lz.3ds" has an acceptable output. However, there is no rotation depending on billboards' axis, hence the notice "Warning: 3DS writer is incomplete for Billboards (rotation not implemented).". You may want to remove this notice (or lower the notify severity) if you feel 3DS doesn't have to handle such rotations. The attached archive contains 3 files from 3DS plugin, against rev. 11162. Please note there is still the textures issue for cow.osg. I guess it's because it's not a "flat, dummy and standard" texture in slot 0... That is to say the only thing the writer can handle at the moment. I guess I won't address this soon. " and "I've detected and fixed another bug in 3DS writer: support for automatic splitting of meshes having >65k faces/points was buggy (was deleting faces). Here is my four 3DS modified files (in a ZIP), against rev. 11193, including previous fixes AND Stephan's fix about relative filenames." --- src/osgPlugins/3ds/ReaderWriter3DS.cpp | 4 +- src/osgPlugins/3ds/WriterCompareTriangle.cpp | 7 +- src/osgPlugins/3ds/WriterNodeVisitor.cpp | 142 +++++++++++++------ src/osgPlugins/3ds/WriterNodeVisitor.h | 14 +- 4 files changed, 114 insertions(+), 53 deletions(-) diff --git a/src/osgPlugins/3ds/ReaderWriter3DS.cpp b/src/osgPlugins/3ds/ReaderWriter3DS.cpp index 617b94d74..1cbc7bfa8 100644 --- a/src/osgPlugins/3ds/ReaderWriter3DS.cpp +++ b/src/osgPlugins/3ds/ReaderWriter3DS.cpp @@ -902,14 +902,14 @@ osg::Texture2D* ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap * { if (texture && *(texture->name)) { - osg::notify(osg::NOTICE)<<"texture->name="<name<<", _directory="<<_directory<name="<name<<", _directory="<<_directory<name,_directory,osgDB::CASE_INSENSITIVE); if (fileName.empty()) { // file not found in .3ds file's directory, so we'll look in the datafile path list. fileName = osgDB::findDataFile(texture->name,options, osgDB::CASE_INSENSITIVE); - osg::notify(osg::NOTICE)<<"texture->name="<name<<", _directory="<<_directory<name="<name<<", _directory="<<_directory<( (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 + setMaxMin (nbVerticesX, nbVerticesY, nbVerticesZ); // This function prevent from cutting the scene in too many blocs - osg::notify(osg::ALWAYS) << "Cutting x by " << nbVerticesX << std::endl + osg::notify(osg::INFO) + << "Cutting x by " << nbVerticesX << std::endl << "Cutting y by " << nbVerticesY << std::endl << "Cutting z by " << nbVerticesZ << std::endl; - osg::BoundingBox::value_type blocX = length.x() / nbVerticesX; //This 3 lines set the size of a bloc in x, y and z + osg::BoundingBox::value_type blocX = length.x() / nbVerticesX; // These 3 lines set the size of a bloc in x, y and z osg::BoundingBox::value_type blocY = length.y() / nbVerticesY; osg::BoundingBox::value_type blocZ = length.z() / nbVerticesZ; diff --git a/src/osgPlugins/3ds/WriterNodeVisitor.cpp b/src/osgPlugins/3ds/WriterNodeVisitor.cpp index 7059387a4..6707b534a 100644 --- a/src/osgPlugins/3ds/WriterNodeVisitor.cpp +++ b/src/osgPlugins/3ds/WriterNodeVisitor.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include "WriterNodeVisitor.h" @@ -658,7 +659,7 @@ int WriterNodeVisitor::processStateSet(osg::StateSet* ss) } /** -* Add a vertice to the index and link him with the Triangle index and the drawable. +* Add a vertice to the index and link it with the Triangle index and the drawable. * \param index_vert is the map where the vertice are stored. * \param index is the indice of the vertice's position in the vec3. * \param drawable_n is the number of the drawable. @@ -680,17 +681,19 @@ WriterNodeVisitor::getMeshIndexForGeometryIndex(MapIndices & index_vert, void -WriterNodeVisitor::buildMesh(osg::Geode & geo, - MapIndices & index_vert, - bool texcoords, - Lib3dsMesh * mesh) +WriterNodeVisitor::buildMesh(osg::Geode & geo, + const osg::Matrix & mat, + MapIndices & index_vert, + bool texcoords, + Lib3dsMesh * mesh) { osg::notify(osg::DEBUG_INFO) << "Building Mesh" << std::endl; if (!mesh) throw "Allocation error"; // TODO - lib3ds_mesh_resize_vertices(mesh, index_vert.size(), texcoords ? 1 : 0, 0); // Write points + assert(index_vert.size() <= MAX_VERTICES); + lib3ds_mesh_resize_vertices(mesh, index_vert.size(), texcoords ? 1 : 0, 0); for(MapIndices::iterator it = index_vert.begin(); it != index_vert.end();++it) { @@ -699,7 +702,7 @@ WriterNodeVisitor::buildMesh(osg::Geode & geo, if (g->getVertexArray()->getType() != osg::Array::Vec3ArrayType) throw "Vertex array is not Vec3. Not implemented"; // TODO const osg::Vec3Array & vecs= *static_cast(g->getVertexArray()); - copyOsgVectorToLib3dsVector(mesh->vertices[it->second], vecs[it->first.first]); + copyOsgVectorToLib3dsVector(mesh->vertices[it->second], vecs[it->first.first]*mat); } // Write texture coords (Texture 0 only) @@ -744,50 +747,59 @@ WriterNodeVisitor::calcVertices(osg::Geode & geo) void -WriterNodeVisitor::buildFaces(osg::Geode & geo, - ListTriangle & listTriangles, +WriterNodeVisitor::buildFaces(osg::Geode & geo, + const osg::Matrix & mat, + ListTriangle & listTriangles, bool texcoords) { MapIndices index_vert; - unsigned int nbFace = 0; Lib3dsMesh *mesh = lib3ds_mesh_new( getUniqueName(geo.getName().empty() ? geo.className() : geo.getName(), "geo").c_str() ); - unsigned int nbTriangles = listTriangles.size(); + if (!mesh) throw "Allocation error"; - lib3ds_mesh_resize_faces(mesh, nbTriangles); + unsigned int nbTrianglesRemaining = listTriangles.size(); + unsigned int nbVerticesRemaining = calcVertices(geo); - unsigned int nbVertices = calcVertices(geo); - if (listTriangles.size() >= MAX_FACES-2 || - ((nbVertices) >= MAX_VERTICES-2)) + lib3ds_mesh_resize_faces (mesh, osg::minimum(nbTrianglesRemaining, MAX_FACES)); + lib3ds_mesh_resize_vertices(mesh, osg::minimum(nbVerticesRemaining, MAX_VERTICES), texcoords ? 0 : 1, 0); // Not mandatory but will allocate once a big block + + // Test if the mesh will be split and needs sorting + if (nbVerticesRemaining >= MAX_VERTICES || nbTrianglesRemaining >= MAX_FACES) { osg::notify(osg::INFO) << "Sorting elements..." << std::endl; - WriterCompareTriangle cmp(geo, nbVertices); + WriterCompareTriangle cmp(geo, nbVerticesRemaining); std::sort(listTriangles.begin(), listTriangles.end(), cmp); } + unsigned int numFace = 0; // Current face index for (ListTriangle::iterator it = listTriangles.begin(); it != listTriangles.end(); ++it) //Go through the triangle list to define meshs { - // Using -2 due to the fact that we treat 3 faces in one time (=the algorithm may overrun the limit by 2). - if ((index_vert.size() >= MAX_VERTICES-2 || // If mesh is full - nbFace >= MAX_FACES-2)) + // Test if the mesh will be full after adding a face + if (index_vert.size()+3 >= MAX_VERTICES || numFace+1 >= MAX_FACES) { - // Finnishing mesh - lib3ds_mesh_resize_faces(mesh, nbFace); - buildMesh(geo, index_vert, texcoords, mesh); + // Finnish mesh + lib3ds_mesh_resize_faces (mesh, numFace); + //lib3ds_mesh_resize_vertices() will be called in buildMesh() + buildMesh(geo, mat, index_vert, texcoords, mesh); - // Creating a new mesh + // "Reset" values and start over a new mesh index_vert.clear(); + nbTrianglesRemaining -= numFace; + numFace = 0; + // 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()); - nbTriangles -= nbFace; - nbFace = 0; - lib3ds_mesh_resize_faces(mesh, nbTriangles); + if (!mesh) throw "Allocation error"; + 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 } - Lib3dsFace & face = mesh->faces[nbFace++]; + Lib3dsFace & face = mesh->faces[numFace++]; face.index[0] = getMeshIndexForGeometryIndex(index_vert, it->first.t1, it->second); face.index[1] = getMeshIndexForGeometryIndex(index_vert, it->first.t2, it->second); face.index[2] = getMeshIndexForGeometryIndex(index_vert, it->first.t3, it->second); face.material = it->first.material; } - buildMesh(geo, index_vert, texcoords, mesh); //When a Mesh is completed without restriction of vertices number + buildMesh(geo, mat, index_vert, texcoords, mesh); //When a Mesh is completed without restriction of vertices number } void @@ -840,11 +852,9 @@ void WriterNodeVisitor::failedApply() osg::notify(osg::NOTICE) << "Error going through node" << std::endl; } -void WriterNodeVisitor::apply( osg::Geode &node ) -{ +void WriterNodeVisitor::apply( osg::Geode &node ) { pushStateSet(node.getStateSet()); //_nameStack.push_back(node.getName()); - //osg::Matrix m = osg::computeLocalToWorld(getNodePath()); unsigned int count = node.getNumDrawables(); ListTriangle listTriangles; bool texcoords = false; @@ -860,7 +870,8 @@ void WriterNodeVisitor::apply( osg::Geode &node ) } if (count > 0) { - buildFaces(node, listTriangles, texcoords); + osg::Matrix mat( osg::computeLocalToWorld(getNodePath()) ); + buildFaces(node, mat, listTriangles, texcoords); } popStateSet(node.getStateSet()); //_nameStack.pop_back(); @@ -868,23 +879,71 @@ void WriterNodeVisitor::apply( osg::Geode &node ) traverse(node); } -void WriterNodeVisitor::apply(osg::Group &node) -{ +void WriterNodeVisitor::apply( osg::Billboard &node ) { + // TODO Does not handle Billboards' points yet + + pushStateSet(node.getStateSet()); Lib3dsMeshInstanceNode * parent = _cur3dsNode; - Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance(NULL, getUniqueName(node.getName().empty() ? node.className() : getFileName(node.getName()), "grp").c_str(), NULL, NULL, NULL); - lib3ds_file_append_node(file3ds, reinterpret_cast(node3ds), reinterpret_cast(parent)); - _cur3dsNode = node3ds; + + unsigned int count = node.getNumDrawables(); + ListTriangle listTriangles; + bool texcoords = false; + osg::notify(osg::NOTICE) << "Warning: 3DS writer is incomplete for Billboards (rotation not implemented)." << std::endl; + osg::Matrix m( osg::computeLocalToWorld(getNodePath()) ); + for ( unsigned int i = 0; i < count; i++ ) + { + osg::Geometry *g = node.getDrawable( i )->asGeometry(); + if ( g != NULL ) + { + listTriangles.clear(); + _cur3dsNode = parent; + + pushStateSet(g->getStateSet()); + createListTriangle(g, listTriangles, texcoords, i); + popStateSet(g->getStateSet()); + + 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); + } + } + if (suceedLastApply()) traverse(node); _cur3dsNode = parent; + popStateSet(node.getStateSet()); +} + + + +void WriterNodeVisitor::apply(osg::Group &node) +{ + pushStateSet(node.getStateSet()); + Lib3dsMeshInstanceNode * parent = _cur3dsNode; + apply3DSMatrixNode(node, osg::computeLocalToWorld(getNodePath()), "grp"); + if (suceedLastApply()) + traverse(node); + _cur3dsNode = parent; + popStateSet(node.getStateSet()); } void WriterNodeVisitor::apply(osg::MatrixTransform &node) +{ + pushStateSet(node.getStateSet()); + Lib3dsMeshInstanceNode * parent = _cur3dsNode; + apply3DSMatrixNode(node, osg::computeLocalToWorld(getNodePath()), "mtx"); + if (suceedLastApply()) + traverse(node); + _cur3dsNode = parent; + popStateSet(node.getStateSet()); +} + +void WriterNodeVisitor::apply3DSMatrixNode(osg::Node &node, const osg::Matrix & m, const char * const prefix) { Lib3dsMeshInstanceNode * parent = _cur3dsNode; - const osg::Matrix & m = node.getMatrix(); - //const osg::Matrix m( osg::computeWorldToLocal(getNodePath()) ); // [NEEDS TESTING!] 3DS matrices always contain world to local transformation (not local transform; ie. from parent) + //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]; @@ -897,7 +956,7 @@ void WriterNodeVisitor::apply(osg::MatrixTransform &node) copyOsgVectorToLib3dsVector(scl, osgScl); copyOsgQuatToLib3dsQuat(rot, osgRot); Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance - (NULL, getUniqueName(node.getName().empty() ? node.className() : node.getName(), "mtx").c_str(), pos, scl, rot); + (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 @@ -906,7 +965,4 @@ void WriterNodeVisitor::apply(osg::MatrixTransform &node) lib3ds_file_append_node(file3ds, reinterpret_cast(node3ds), reinterpret_cast(parent)); _cur3dsNode = node3ds; - if (suceedLastApply()) - traverse(node); - _cur3dsNode = parent; } diff --git a/src/osgPlugins/3ds/WriterNodeVisitor.h b/src/osgPlugins/3ds/WriterNodeVisitor.h index f402ffd8b..ac3cd7097 100644 --- a/src/osgPlugins/3ds/WriterNodeVisitor.h +++ b/src/osgPlugins/3ds/WriterNodeVisitor.h @@ -61,6 +61,7 @@ class WriterNodeVisitor: public osg::NodeVisitor bool suceedLastApply() const; void failedApply(); virtual void apply(osg::Geode &node); + virtual void apply(osg::Billboard &node); virtual void apply(osg::Group &node); virtual void apply(osg::MatrixTransform &node); @@ -134,10 +135,11 @@ class WriterNodeVisitor: public osg::NodeVisitor /** * Fill the faces field of the mesh and call buildMesh(). * \param geo is the geode who contain vertice and faces. + * \param mat Local to world matrix applied to the geode * \param listTriangles contain all the meshs faces. * \param texcoords tell us if we have to treat texture coord. */ - void buildFaces(osg::Geode & geo, ListTriangle & listTriangles, bool texcoords); + void buildFaces(osg::Geode & geo, const osg::Matrix & mat, ListTriangle & listTriangles, bool texcoords); /** * Calculate the number of vertices in the geode. @@ -149,16 +151,17 @@ class WriterNodeVisitor: public osg::NodeVisitor /** * Build a mesh * \param geo is the geode who contain vertice and faces + * \param mat Local to world matrix applied to the geode * \param index_vert is the index used to build the new mesh * \param texcoords tell us if we have to treat texture coord * \param mesh is the mesh with faces filled - * \return the place of the box in the vector. * \sa See cutScene() about the definition of the boxes for faces sorting. */ void - buildMesh(osg::Geode & geo, - MapIndices & index_vert, - bool texcoords, + buildMesh(osg::Geode & geo, + const osg::Matrix & mat, + MapIndices & index_vert, + bool texcoords, Lib3dsMesh *mesh); /** @@ -192,6 +195,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); bool _suceedLastApply; std::string _directory;