From Sukender, "After discussion with Paul Martz, here is a fix for 3DS reader/writer (on rev. 11315) + some changes. Changelog:
- Fixed handling of MatrixTransforms (still doesn't support other Transforms types). Fixes things for OSG, DeepExploration, 3DSMax... - Added support for writing double precision vertices by converting them. - Added base code for future compatibility option (3rd-party apps that don't read animation data). See "DISABLE_3DS_ANIMATION" compile flag."
This commit is contained in:
parent
31ec835002
commit
3700263e08
@ -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
|
||||
|
@ -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 <osg/io_utils>
|
||||
#include <osg/CullFace>
|
||||
#include <osg/Billboard>
|
||||
@ -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<const osg::Vec3Array *>(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<const osg::Vec3dArray *>(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<osg::Vec3Array *>(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<osg::Vec2Array *>(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<const osg::Vec2Array *>(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<osg::Vec3Array *>(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<osg::Vec3Array *>(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<osg::Vec2Array *>(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<Lib3dsNode*>(node3ds), reinterpret_cast<Lib3dsNode*>(parent));
|
||||
_cur3dsNode = node3ds;
|
||||
|
@ -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<osg::ref_ptr<osg::StateSet> > StateSetStack;
|
||||
typedef std::map< osg::ref_ptr<osg::StateSet>, 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;
|
||||
|
Loading…
Reference in New Issue
Block a user