Hi Robert,
Here is a fix for the FBX writer:
"
FBX writer was writing an empty file when the scene had a root node being a
Geode.
This was caused be the change in Drawable (now derived from Node) and Geode
(now derived from Group).
This fix simply sticks with previous behaviour. Another change could be to
adapt WriterNodeVisitor.
"
Base revision : SVN trunk 14422 / 381c1b73cdd94ef0f81f77ea3fb1ee4c1ae378d9
Cheers,
--
Sukender
#include <sstream>
#include <memory>
#ifndef WIN32
#include <strings.h>//for strncasecmp
#endif
#include <osg/Notify>
#include <osg/MatrixTransform>
#include <osg/Material>
#include <osg/PositionAttitudeTransform>
#include <osg/Texture2D>
#include <osg/Version>
#include <osgDB/ConvertUTF>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgDB/ReadFile>
#include <osgDB/Registry>
#include <osgAnimation/AnimationManagerBase>
#include <osgAnimation/Bone>
#include <osgAnimation/RigGeometry>
#include <osgAnimation/Skeleton>
#include <osgAnimation/VertexInfluence>
#if defined(_MSC_VER)
#pragma warning( disable : 4505 )
#pragma warning( default : 4996 )
#endif
#include <fbxsdk.h>
#include "ReaderWriterFBX.h"
#include "fbxReader.h"
#include "WriterNodeVisitor.h"
/// Returns true if the given node is a basic root group with no special information.
/// Used in conjunction with UseFbxRoot option.
/// Identity transforms are considered as basic root nodes.
bool isBasicRootNode(const osg::Node& node)
{
const osg::Group* osgGroup = node.asGroup();
if (!osgGroup || node.asGeode()) // WriterNodeVisitor handles Geodes the "old way" (= Derived from Node, not Group as for now). Geodes may be considered "basic root nodes" when WriterNodeVisitor will be adapted.
{
// Geodes & such are not basic root nodes
return false;
}
// Test if we've got an empty transform (= a group!)
const osg::Transform* transform = osgGroup->asTransform();
if (transform)
{
if (const osg::MatrixTransform* matrixTransform = transform->asMatrixTransform())
{
if (!matrixTransform->getMatrix().isIdentity())
{
// Non-identity matrix transform
return false;
}
}
else if (const osg::PositionAttitudeTransform* pat = transform->asPositionAttitudeTransform())
{
if (pat->getPosition() != osg::Vec3d() ||
pat->getAttitude() != osg::Quat() ||
pat->getScale() != osg::Vec3d(1.0f, 1.0f, 1.0f) ||
pat->getPivotPoint() != osg::Vec3d())
{
// Non-identity position attribute transform
return false;
}
}
else
{
// Other transform (not identity or not predefined type)
return false;
}
}
// Test the presence of a non-empty stateset
if (node.getStateSet())
{
osg::ref_ptr<osg::StateSet> emptyStateSet = new osg::StateSet;
if (node.getStateSet()->compare(*emptyStateSet, true) != 0)
{
return false;
}
}
return true;
}
class CleanUpFbx
{
FbxManager* m_pSdkManager;
public:
explicit CleanUpFbx(FbxManager* pSdkManager) : m_pSdkManager(pSdkManager)
{}
~CleanUpFbx()
{
m_pSdkManager->Destroy();
}
};
//Some files don't correctly mark their skeleton nodes, so this function infers
//them from the nodes that skin deformers linked to.
void findLinkedFbxSkeletonNodes(FbxNode* pNode, std::set<const FbxNode*>& fbxSkeletons)
{
if (const FbxGeometry* pMesh = FbxCast<FbxGeometry>(pNode->GetNodeAttribute()))
{
for (int i = 0; i < pMesh->GetDeformerCount(FbxDeformer::eSkin); ++i)
{
const FbxSkin* pSkin = (const FbxSkin*)pMesh->GetDeformer(i, FbxDeformer::eSkin);
for (int j = 0; j < pSkin->GetClusterCount(); ++j)
{
const FbxNode* pSkeleton = pSkin->GetCluster(j)->GetLink();
fbxSkeletons.insert(pSkeleton);
}
}
}
for (int i = 0; i < pNode->GetChildCount(); ++i)
{
findLinkedFbxSkeletonNodes(pNode->GetChild(i), fbxSkeletons);
}
}
void resolveBindMatrices(
osg::Node& root,
const BindMatrixMap& boneBindMatrices,
const std::map<FbxNode*, osg::Node*>& nodeMap)
{
std::set<std::string> nodeNames;
for (std::map<FbxNode*, osg::Node*>::const_iterator it = nodeMap.begin(); it != nodeMap.end(); ++it)
{
nodeNames.insert(it->second->getName());
}
for (BindMatrixMap::const_iterator it = boneBindMatrices.begin();
it != boneBindMatrices.end();)
{
FbxNode* const fbxBone = it->first.first;
std::map<FbxNode*, osg::Node*>::const_iterator nodeIt = nodeMap.find(fbxBone);
if (nodeIt != nodeMap.end())
{
const osg::Matrix bindMatrix = it->second;
osgAnimation::Bone& osgBone = dynamic_cast<osgAnimation::Bone&>(*nodeIt->second);
osgBone.setInvBindMatrixInSkeletonSpace(bindMatrix);
++it;
for (; it != boneBindMatrices.end() && it->first.first == fbxBone; ++it)
{
if (it->second != bindMatrix)
{
std::string name;
for (int i = 0;; ++i)
{
std::stringstream ss;
ss << osgBone.getName() << '_' << i;
name = ss.str();
if (nodeNames.insert(name).second)
{
break;
}
}
osgAnimation::Bone* newBone = new osgAnimation::Bone(name);
newBone->setDefaultUpdateCallback();
newBone->setInvBindMatrixInSkeletonSpace(it->second);
osgBone.addChild(newBone);
osgAnimation::RigGeometry* pRigGeometry = it->first.second;
osgAnimation::VertexInfluenceMap* vertexInfluences = pRigGeometry->getInfluenceMap();
osgAnimation::VertexInfluenceMap::iterator vimIt = vertexInfluences->find(osgBone.getName());
if (vimIt != vertexInfluences->end())
{
osgAnimation::VertexInfluence vi;
vi.swap(vimIt->second);
vertexInfluences->erase(vimIt);
osgAnimation::VertexInfluence& vi2 = (*vertexInfluences)[name];
vi.swap(vi2);
vi2.setName(name);
}
else
{
OSG_WARN << "No vertex influences found for \"" << osgBone.getName() << "\"" << std::endl;
}
}
}
}
else
{
OSG_WARN << "No bone found for \"" << fbxBone->GetName() << "\"" << std::endl;
++it;
}
}
}
osgDB::ReaderWriter::ReadResult
ReaderWriterFBX::readNode(const std::string& filenameInit,
const Options* options) const
{
try
{
std::string ext(osgDB::getLowerCaseFileExtension(filenameInit));
if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
std::string filename(osgDB::findDataFile(filenameInit, options));
if (filename.empty()) return ReadResult::FILE_NOT_FOUND;
FbxManager* pSdkManager = FbxManager::Create();
if (!pSdkManager)
{
return ReadResult::ERROR_IN_READING_FILE;
}
CleanUpFbx cleanUpFbx(pSdkManager);
pSdkManager->SetIOSettings(FbxIOSettings::Create(pSdkManager, IOSROOT));
FbxScene* pScene = FbxScene::Create(pSdkManager, "");
// The FBX SDK interprets the filename as UTF-8
#ifdef OSG_USE_UTF8_FILENAME
const std::string& utf8filename(filename);
#else
std::string utf8filename(osgDB::convertStringFromCurrentCodePageToUTF8(filename));
#endif
FbxImporter* lImporter = FbxImporter::Create(pSdkManager, "");
if (!lImporter->Initialize(utf8filename.c_str(), -1, pSdkManager->GetIOSettings()))
{
#if FBXSDK_VERSION_MAJOR < 2014
return std::string(lImporter->GetLastErrorString());
#else
return std::string(lImporter->GetStatus().GetErrorString());
#endif
}
if (!lImporter->IsFBX())
{
return ReadResult::ERROR_IN_READING_FILE;
}
for (int i = 0; FbxTakeInfo* lTakeInfo = lImporter->GetTakeInfo(i); i++)
{
lTakeInfo->mSelect = true;
}
if (!lImporter->Import(pScene))
{
#if FBXSDK_VERSION_MAJOR < 2014
return std::string(lImporter->GetLastErrorString());
#else
return std::string(lImporter->GetStatus().GetErrorString());
#endif
}
//FbxAxisSystem::OpenGL.ConvertScene(pScene); // Doesn't work as expected. Still need to transform vertices.
if (FbxNode* pNode = pScene->GetRootNode())
{
bool useFbxRoot = false;
bool lightmapTextures = false;
bool tessellatePolygons = false;
bool zUp = false;
if (options)
{
std::istringstream iss(options->getOptionString());
std::string opt;
while (iss >> opt)
{
if (opt == "UseFbxRoot")
{
useFbxRoot = true;
}
if (opt == "LightmapTextures")
{
lightmapTextures = true;
}
if (opt == "TessellatePolygons")
{
tessellatePolygons = true;
}
if (opt == "ZUp")
{
zUp = true;
}
}
}
bool bIsBone = false;
int nLightCount = 0;
osg::ref_ptr<Options> localOptions = NULL;
if (options)
localOptions = options->cloneOptions();
else
localOptions = new osgDB::Options();
localOptions->setObjectCacheHint(osgDB::ReaderWriter::Options::CACHE_IMAGES);
std::string filePath = osgDB::getFilePath(filename);
FbxMaterialToOsgStateSet fbxMaterialToOsgStateSet(filePath, localOptions.get(), lightmapTextures);
std::set<const FbxNode*> fbxSkeletons;
findLinkedFbxSkeletonNodes(pNode, fbxSkeletons);
OsgFbxReader::AuthoringTool authoringTool = OsgFbxReader::UNKNOWN;
if (FbxDocumentInfo* pDocInfo = pScene->GetDocumentInfo())
{
struct ToolName
{
const char* name;
OsgFbxReader::AuthoringTool tool;
};
ToolName authoringTools[] = {
{"OpenSceneGraph", OsgFbxReader::OPENSCENEGRAPH},
{"3ds Max", OsgFbxReader::AUTODESK_3DSTUDIO_MAX}
};
FbxString appName = pDocInfo->LastSaved_ApplicationName.Get();
for (unsigned int i = 0; i < sizeof(authoringTools) / sizeof(authoringTools[0]); ++i)
{
if (0 ==
#ifdef WIN32
_strnicmp
#else
strncasecmp
#endif
(appName, authoringTools[i].name, strlen(authoringTools[i].name)))
{
authoringTool = authoringTools[i].tool;
break;
}
}
}
OsgFbxReader reader(*pSdkManager,
*pScene,
fbxMaterialToOsgStateSet,
fbxSkeletons,
*localOptions,
authoringTool,
lightmapTextures,
tessellatePolygons);
ReadResult res = reader.readFbxNode(pNode, bIsBone, nLightCount);
if (res.success())
{
fbxMaterialToOsgStateSet.checkInvertTransparency();
resolveBindMatrices(*res.getNode(), reader.boneBindMatrices, reader.nodeMap);
osg::Node* osgNode = res.getNode();
osgNode->getOrCreateStateSet()->setMode(GL_RESCALE_NORMAL,osg::StateAttribute::ON);
osgNode->getOrCreateStateSet()->setMode(GL_NORMALIZE,osg::StateAttribute::ON);
if (reader.pAnimationManager.valid())
{
if (osgNode->getUpdateCallback())
{
osg::Group* osgGroup = new osg::Group;
osgGroup->addChild(osgNode);
osgNode = osgGroup;
}
//because the animations may be altered after registering
reader.pAnimationManager->buildTargetReference();
osgNode->setUpdateCallback(reader.pAnimationManager.get());
}
FbxAxisSystem fbxAxis = pScene->GetGlobalSettings().GetAxisSystem();
// some reminder: http://www.realtimerendering.com/blog/left-handed-vs-right-handed-world-coordinates/
int upSign;
FbxAxisSystem::EUpVector eUp = fbxAxis.GetUpVector(upSign);
bool bLeftHanded = fbxAxis.GetCoorSystem() == FbxAxisSystem::eLeftHanded;
float fSign = upSign < 0 ? 1.0f : -1.0f;
float HorizSign = bLeftHanded ? -1.0f : 1.0f;
bool refCoordSysChange = false;
osg::Matrix mat;
if (zUp)
{
if (eUp != FbxAxisSystem::eZAxis || fSign != 1.0 || upSign != 1.0)
{
switch (eUp)
{
case FbxAxisSystem::eXAxis:
mat.set(0,fSign,0,0,-fSign,0,0,0,0,0,HorizSign,0,0,0,0,1);
break;
case FbxAxisSystem::eYAxis:
mat.set(1,0,0,0,0,0,-fSign*HorizSign,0,0,fSign,0,0,0,0,0,1);
break;
case FbxAxisSystem::eZAxis:
mat.set(1,0,0,0,0,fSign,0,0,0,0,fSign*HorizSign,0,0,0,0,1);
break;
}
refCoordSysChange = true;
}
}
else if (fbxAxis != FbxAxisSystem::OpenGL)
{
switch (eUp)
{
case FbxAxisSystem::eXAxis:
mat.set(0,fSign,0,0,-fSign,0,0,0,0,0,HorizSign,0,0,0,0,1);
break;
case FbxAxisSystem::eYAxis:
mat.set(1,0,0,0,0,fSign,0,0,0,0,fSign*HorizSign,0,0,0,0,1);
break;
case FbxAxisSystem::eZAxis:
mat.set(1,0,0,0,0,0,-fSign*HorizSign,0,0,fSign,0,0,0,0,0,1);
break;
}
refCoordSysChange = true;
}
if (refCoordSysChange)
{
osg::Transform* pTransformTemp = osgNode->asTransform();
osg::MatrixTransform* pMatrixTransform = pTransformTemp ?
pTransformTemp->asMatrixTransform() : NULL;
if (pMatrixTransform)
{
pMatrixTransform->setMatrix(pMatrixTransform->getMatrix() * mat);
}
else
{
pMatrixTransform = new osg::MatrixTransform(mat);
if (useFbxRoot && isBasicRootNode(*osgNode))
{
// If root node is a simple group, put all FBX elements under the OSG root
osg::Group* osgGroup = osgNode->asGroup();
for(unsigned int i = 0; i < osgGroup->getNumChildren(); ++i)
{
pMatrixTransform->addChild(osgGroup->getChild(i));
}
pMatrixTransform->setName(osgGroup->getName());
}
else
{
pMatrixTransform->addChild(osgNode);
}
}
osgNode = pMatrixTransform;
}
osgNode->setName(filenameInit);
return osgNode;
}
}
}
catch (...)
{
OSG_WARN << "Exception thrown while importing \"" << filenameInit << '\"' << std::endl;
}
return ReadResult::ERROR_IN_READING_FILE;
}
osgDB::ReaderWriter::WriteResult ReaderWriterFBX::writeNode(
const osg::Node& node,
const std::string& filename,
const Options* options) const
{
try
{
std::string ext = osgDB::getLowerCaseFileExtension(filename);
if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
osg::ref_ptr<Options> localOptions = options ?
static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
localOptions->getDatabasePathList().push_front(osgDB::getFilePath(filename));
FbxManager* pSdkManager = FbxManager::Create();
if (!pSdkManager)
{
return WriteResult::ERROR_IN_WRITING_FILE;
}
CleanUpFbx cleanUpFbx(pSdkManager);
pSdkManager->SetIOSettings(FbxIOSettings::Create(pSdkManager, IOSROOT));
bool useFbxRoot = false;
if (options)
{
std::istringstream iss(options->getOptionString());
std::string opt;
while (iss >> opt)
{
if (opt == "Embedded")
{
pSdkManager->GetIOSettings()->SetBoolProp(EXP_FBX_EMBEDDED, true);
}
else if (opt == "UseFbxRoot")
{
useFbxRoot = true;
}
}
}
FbxScene* pScene = FbxScene::Create(pSdkManager, "");
pluginfbx::WriterNodeVisitor writerNodeVisitor(pScene, pSdkManager, filename,
options, osgDB::getFilePath(node.getName().empty() ? filename : node.getName()));
if (useFbxRoot && isBasicRootNode(node))
{
// If root node is a simple group, put all elements under the FBX root
const osg::Group * osgGroup = node.asGroup();
for (unsigned int child = 0; child < osgGroup->getNumChildren(); ++child)
{
const_cast<osg::Node *>(osgGroup->getChild(child))->accept(writerNodeVisitor);
}
}
else {
// Normal scene
const_cast<osg::Node&>(node).accept(writerNodeVisitor);
}
FbxDocumentInfo* pDocInfo = pScene->GetDocumentInfo();
bool needNewDocInfo = pDocInfo != NULL;
if (needNewDocInfo)
{
pDocInfo = FbxDocumentInfo::Create(pSdkManager, "");
}
pDocInfo->LastSaved_ApplicationName.Set(FbxString("OpenSceneGraph"));
pDocInfo->LastSaved_ApplicationVersion.Set(FbxString(osgGetVersion()));
if (needNewDocInfo)
{
pScene->SetDocumentInfo(pDocInfo);
}
FbxExporter* lExporter = FbxExporter::Create(pSdkManager, "");
pScene->GetGlobalSettings().SetAxisSystem(FbxAxisSystem::eOpenGL);
// Ensure the directory exists or else the FBX SDK will fail
if (!osgDB::makeDirectoryForFile(filename)) {
OSG_NOTICE << "Can't create directory for file '" << filename << "'. FBX SDK may fail creating the file." << std::endl;
}
// The FBX SDK interprets the filename as UTF-8
#ifdef OSG_USE_UTF8_FILENAME
const std::string& utf8filename(filename);
#else
std::string utf8filename(osgDB::convertStringFromCurrentCodePageToUTF8(filename));
#endif
if (!lExporter->Initialize(utf8filename.c_str()))
{
#if FBXSDK_VERSION_MAJOR < 2014
return std::string(lExporter->GetLastErrorString());
#else
return std::string(lExporter->GetStatus().GetErrorString());
#endif
}
if (!lExporter->Export(pScene))
{
#if FBXSDK_VERSION_MAJOR < 2014
return std::string(lExporter->GetLastErrorString());
#else
return std::string(lExporter->GetStatus().GetErrorString());
#endif
}
return WriteResult::FILE_SAVED;
}
catch (const std::string& s)
{
return s;
}
catch (const char* s)
{
return std::string(s);
}
catch (...)
{
}
return WriteResult::ERROR_IN_WRITING_FILE;
}
///////////////////////////////////////////////////////////////////////////
// Add ourself to the Registry to instantiate the reader/writer.
REGISTER_OSGPLUGIN(fbx, ReaderWriterFBX)
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org