diff --git a/src/osgPlugins/fbx/ReaderWriterFBX.cpp b/src/osgPlugins/fbx/ReaderWriterFBX.cpp index 86dcc0b32..51ea6bfbe 100644 --- a/src/osgPlugins/fbx/ReaderWriterFBX.cpp +++ b/src/osgPlugins/fbx/ReaderWriterFBX.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -133,26 +134,6 @@ public: } }; -class ConvertBindMatrixVisitor : public osg::NodeVisitor -{ -public: - ConvertBindMatrixVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) {} - - virtual void apply(osg::MatrixTransform& node) - { - if (osgAnimation::Bone* bone = dynamic_cast(&node)) - { - bone->setInvBindMatrixInSkeletonSpace(osg::Matrix::inverse(bone->getMatrixInBoneSpace())); - if (const osgAnimation::Bone* parent = bone->getBoneParent()) - { - bone->setInvBindMatrixInSkeletonSpace(parent->getInvBindMatrixInSkeletonSpace() * bone->getInvBindMatrixInSkeletonSpace()); - } - } - - traverse(node); - } -}; - osgDB::ReaderWriter::ReadResult ReaderWriterFBX::readNode(const std::string& filenameInit, const Options* options) const @@ -189,9 +170,8 @@ ReaderWriterFBX::readNode(const std::string& filenameInit, return ReadResult::FILE_NOT_HANDLED; } KFbxImporter* lImporter = KFbxImporter::Create(pSdkManager, ""); - lImporter->SetFileFormat(fileFormat); - if (!lImporter->Initialize(utf8filename.c_str())) + if (!lImporter->Initialize(utf8filename.c_str(), fileFormat)) { return std::string(lImporter->GetLastErrorString()); } @@ -217,7 +197,9 @@ ReaderWriterFBX::readNode(const std::string& filenameInit, if (KFbxNode* pNode = pScene->GetRootNode()) { - bool useFbxRoot = false; + pScene->SetCurrentTake(pScene->GetCurrentTakeName()); + + bool useFbxRoot = false; if (options) { std::istringstream iss(options->getOptionString()); @@ -232,7 +214,7 @@ ReaderWriterFBX::readNode(const std::string& filenameInit, } osg::ref_ptr pAnimationManager; - bool bNeedSkeleton = false; + bool bIsBone = false; int nLightCount = 0; osg::ref_ptr localOptions = NULL; if (options) @@ -243,22 +225,35 @@ ReaderWriterFBX::readNode(const std::string& filenameInit, std::string filePath = osgDB::getFilePath(filename); FbxMaterialToOsgStateSet fbxMaterialToOsgStateSet(filePath, localOptions.get()); - + + std::map nodeMap; + std::map boneBindMatrices; + std::map skeletonMap; ReadResult res = readFbxNode(*pSdkManager, pNode, pAnimationManager, - bNeedSkeleton, nLightCount, fbxMaterialToOsgStateSet, localOptions.get()); + bIsBone, nLightCount, fbxMaterialToOsgStateSet, nodeMap, + boneBindMatrices, skeletonMap, localOptions.get()); if (res.success()) { - osg::Node* osgNode = res.getNode(); - if (bNeedSkeleton) - { - ConvertBindMatrixVisitor convertBindMatrixVisitor; - osgNode->accept(convertBindMatrixVisitor); - osgAnimation::Skeleton* osgSkeleton = new osgAnimation::Skeleton; - osgSkeleton->setDefaultUpdateCallback(); - osgSkeleton->addChild(osgNode); - osgNode = osgSkeleton; - } + for (std::map::const_iterator it = boneBindMatrices.begin(); + it != boneBindMatrices.end(); ++it) + { + std::map::iterator nodeIt = nodeMap.find(it->first); + if (nodeIt != nodeMap.end()) + { + osgAnimation::Bone& osgBone = dynamic_cast(*nodeIt->second); + osgBone.setInvBindMatrixInSkeletonSpace(it->second); + } + else + { + assert(0); + } + } + + osg::Node* osgNode = res.getNode(); + osgNode->getOrCreateStateSet()->setMode(GL_RESCALE_NORMAL,osg::StateAttribute::ON); + osgNode->getOrCreateStateSet()->setMode(GL_NORMALIZE,osg::StateAttribute::ON); + if (pAnimationManager.valid()) { if (osgNode->getUpdateCallback()) @@ -332,6 +327,7 @@ ReaderWriterFBX::readNode(const std::string& filenameInit, } catch (...) { + osg::notify(osg::WARN) << "Exception thrown while importing \"" << filenameInit << '\"' << std::endl; } return ReadResult::ERROR_IN_READING_FILE; diff --git a/src/osgPlugins/fbx/fbxRMesh.cpp b/src/osgPlugins/fbx/fbxRMesh.cpp index 953a87007..501a7e470 100644 --- a/src/osgPlugins/fbx/fbxRMesh.cpp +++ b/src/osgPlugins/fbx/fbxRMesh.cpp @@ -19,6 +19,7 @@ #include #include "fbxRMesh.h" +#include "fbxRNode.h" enum GeometryType { @@ -259,10 +260,13 @@ void readAnimation(KFbxNode* pNode, const std::string& targetName, } } -osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh, +osgDB::ReaderWriter::ReadResult readMesh(KFbxSdkManager& pSdkManager, + KFbxNode* pNode, KFbxMesh* fbxMesh, osg::ref_ptr& pAnimationManager, std::vector& stateSetList, - const char* szName) + const char* szName, + std::map& boneBindMatrices, + std::map& skeletonMap) { GeometryMap geometryMap; @@ -408,7 +412,8 @@ osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh, for (int i = 0; i < pGeode->getNumDrawables(); ++i) { osg::Geometry* pGeometry = pGeode->getDrawable(i)->asGeometry(); - osgAnimation::RigGeometry* pRig = new osgAnimation::RigGeometry; + + osgAnimation::RigGeometry* pRig = new osgAnimation::RigGeometry; pRig->setSourceGeometry(pGeometry); pRig->copyFrom(*pGeometry); old2newGeometryMap.insert(GeometryRigGeometryMap::value_type( @@ -428,8 +433,18 @@ osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh, for (int j = 0; j < nClusters; ++j) { KFbxCluster* pCluster = (KFbxCluster*)pSkin->GetCluster(j); + //assert(KFbxCluster::eNORMALIZE == pCluster->GetLinkMode()); KFbxNode* pBone = pCluster->GetLink(); + KFbxXMatrix transformLink; + pCluster->GetTransformLinkMatrix(transformLink); + KFbxXMatrix transformLinkInverse = transformLink.Inverse(); + const double* pTransformLinkInverse = transformLinkInverse; + if (!boneBindMatrices.insert(std::pair(pBone, osg::Matrix(pTransformLinkInverse))).second) + { + osg::notify(osg::WARN) << "Multiple meshes attached to a bone - bind matrices may be incorrect." << std::endl; + } + int nIndices = pCluster->GetControlPointIndicesCount(); int* pIndices = pCluster->GetControlPointIndices(); double* pWeights = pCluster->GetControlPointWeights(); @@ -542,27 +557,55 @@ osgDB::ReaderWriter::ReadResult readMesh(KFbxNode* pNode, KFbxMesh* fbxMesh, KFbxXMatrix fbxVertexTransform; fbxVertexTransform.SetTRS( - pNode->GetGeometricTranslation(KFbxNode::eSOURCE_SET), - pNode->GetGeometricRotation(KFbxNode::eSOURCE_SET), - pNode->GetGeometricScaling(KFbxNode::eSOURCE_SET)); + pNode->GeometricTranslation.Get(), + pNode->GeometricRotation.Get(), + pNode->GeometricScaling.Get()); const double* pVertexMat = fbxVertexTransform; osg::Matrix vertexMat(pVertexMat); - if (vertexMat.isIdentity()) - { - return osgDB::ReaderWriter::ReadResult(pGeode); - } - else + // Bind shape matrix + // For some models this is necessary, however for other models (such as + // those exported from Blender) it shouldn't be applied. + // TODO: Figure out how to make this work in all cases. + KArrayTemplate pPoseList; + KArrayTemplate pIndex; + if (KFbxPose::GetBindPoseContaining( + pSdkManager, pNode, pPoseList, pIndex)) + { + const double* pBindShapeMat = pPoseList[0]->GetMatrix(pIndex[0]); + vertexMat.postMult(osg::Matrix(pBindShapeMat)); + } + + osg::Node* pResult = pGeode; + + if (!vertexMat.isIdentity()) { osg::MatrixTransform* pMatTrans = new osg::MatrixTransform(vertexMat); pMatTrans->addChild(pGeode); - return osgDB::ReaderWriter::ReadResult(pMatTrans); + pResult = pMatTrans; } + + if (geomType == GEOMETRY_RIG) + { + //Add the geometry to the skeleton ancestor of one of the bones. + KFbxSkin* pSkin = (KFbxSkin*)fbxMesh->GetDeformer(0, KFbxDeformer::eSKIN); + if (pSkin->GetClusterCount()) + { + osgAnimation::Skeleton* pSkeleton = getSkeleton(pSkin->GetCluster(0)->GetLink(), skeletonMap); + pSkeleton->addChild(pResult); + return osgDB::ReaderWriter::ReadResult::FILE_LOADED; + } + } + + return osgDB::ReaderWriter::ReadResult(pResult); } -osgDB::ReaderWriter::ReadResult readFbxMesh(KFbxNode* pNode, +osgDB::ReaderWriter::ReadResult readFbxMesh(KFbxSdkManager& pSdkManager, + KFbxNode* pNode, osg::ref_ptr& pAnimationManager, - std::vector& stateSetList) + std::vector& stateSetList, + std::map& boneBindMatrices, + std::map& skeletonMap) { KFbxMesh* lMesh = dynamic_cast(pNode->GetNodeAttribute()); @@ -571,5 +614,6 @@ osgDB::ReaderWriter::ReadResult readFbxMesh(KFbxNode* pNode, return osgDB::ReaderWriter::ReadResult::ERROR_IN_READING_FILE; } - return readMesh(pNode, lMesh, pAnimationManager, stateSetList, pNode->GetName()); + return readMesh(pSdkManager, pNode, lMesh, pAnimationManager, stateSetList, + pNode->GetName(), boneBindMatrices, skeletonMap); } diff --git a/src/osgPlugins/fbx/fbxRMesh.h b/src/osgPlugins/fbx/fbxRMesh.h index f2d73aa29..bcb4fdea2 100644 --- a/src/osgPlugins/fbx/fbxRMesh.h +++ b/src/osgPlugins/fbx/fbxRMesh.h @@ -6,8 +6,11 @@ #include #include "fbxMaterialToOsgStateSet.h" osgDB::ReaderWriter::ReadResult readFbxMesh( - FBXFILESDK_NAMESPACE::KFbxNode* pNode, + FBXFILESDK_NAMESPACE::KFbxSdkManager& pSdkManager, + FBXFILESDK_NAMESPACE::KFbxNode* pNode, osg::ref_ptr& pAnimationManager, - std::vector &); + std::vector&, + std::map& boneBindMatrices, + std::map& skeletonMap); #endif diff --git a/src/osgPlugins/fbx/fbxRNode.cpp b/src/osgPlugins/fbx/fbxRNode.cpp index 9ab9b04da..f4d896638 100644 --- a/src/osgPlugins/fbx/fbxRNode.cpp +++ b/src/osgPlugins/fbx/fbxRNode.cpp @@ -47,29 +47,29 @@ osg::Quat makeQuat(const fbxDouble3& degrees, ERotationOrder fbxRotOrder) radiansZ, osg::Vec3d(0,0,1)); case eEULER_XZY: return osg::Quat( - radiansX, osg::Vec3d(1,0,0), - radiansY, osg::Vec3d(0,0,1), - radiansZ, osg::Vec3d(0,1,0)); + radiansX, osg::Vec3d(1,0,0), + radiansZ, osg::Vec3d(0,0,1), + radiansY, osg::Vec3d(0,1,0)); case eEULER_YZX: - return osg::Quat( - radiansX, osg::Vec3d(0,1,0), - radiansY, osg::Vec3d(0,0,1), - radiansZ, osg::Vec3d(1,0,0)); + return osg::Quat( + radiansY, osg::Vec3d(0,1,0), + radiansZ, osg::Vec3d(0,0,1), + radiansX, osg::Vec3d(1,0,0)); case eEULER_YXZ: - return osg::Quat( - radiansX, osg::Vec3d(0,1,0), - radiansY, osg::Vec3d(1,0,0), - radiansZ, osg::Vec3d(0,0,1)); + return osg::Quat( + radiansY, osg::Vec3d(0,1,0), + radiansX, osg::Vec3d(1,0,0), + radiansZ, osg::Vec3d(0,0,1)); case eEULER_ZXY: return osg::Quat( - radiansX, osg::Vec3d(0,0,1), - radiansY, osg::Vec3d(1,0,0), - radiansZ, osg::Vec3d(0,1,0)); + radiansZ, osg::Vec3d(0,0,1), + radiansX, osg::Vec3d(1,0,0), + radiansY, osg::Vec3d(0,1,0)); case eEULER_ZYX: - return osg::Quat( - radiansX, osg::Vec3d(0,0,1), - radiansY, osg::Vec3d(0,1,0), - radiansZ, osg::Vec3d(1,0,0)); + return osg::Quat( + radiansZ, osg::Vec3d(0,0,1), + radiansY, osg::Vec3d(0,1,0), + radiansX, osg::Vec3d(1,0,0)); case eSPHERIC_XYZ: { //I don't know what eSPHERIC_XYZ means, so this is a complete guess. @@ -135,22 +135,6 @@ void makeLocalMatrix(const KFbxNode* pNode, osg::Matrix& m) -fbxSclPiv[2])); } -bool readBindPose(KFbxSdkManager& pManager, KFbxNode* pNode, - osgAnimation::Bone* osgBone) -{ - KArrayTemplate pPoseList; - KArrayTemplate pIndex; - if (!pNode || !KFbxPose::GetBindPoseContaining( - pManager, pNode, pPoseList, pIndex)) - { - return false; - } - - const double* pMat = pPoseList[0]->GetMatrix(pIndex[0]); - osgBone->setMatrix(osg::Matrix(pMat)); - return true; -} - void readTranslationElement(KFbxTypedProperty& prop, osgAnimation::UpdateMatrixTransform* pUpdate, osg::Matrix& staticTransform) @@ -271,7 +255,8 @@ void readUpdateMatrixTransform(osgAnimation::UpdateMatrixTransform* pUpdate, KFb } osg::Group* createGroupNode(KFbxSdkManager& pSdkManager, KFbxNode* pNode, - const std::string& animName, const osg::Matrix& localMatrix, bool bNeedSkeleton) + const std::string& animName, const osg::Matrix& localMatrix, bool bNeedSkeleton, + std::map& nodeMap) { if (bNeedSkeleton) { @@ -282,7 +267,7 @@ osg::Group* createGroupNode(KFbxSdkManager& pSdkManager, KFbxNode* pNode, readUpdateMatrixTransform(pUpdate, pNode); osgBone->setUpdateCallback(pUpdate); - readBindPose(pSdkManager, pNode, osgBone); + nodeMap.insert(std::pair(pNode, osgBone)); return osgBone; } @@ -313,8 +298,11 @@ osg::Group* createGroupNode(KFbxSdkManager& pSdkManager, KFbxNode* pNode, osgDB::ReaderWriter::ReadResult readFbxNode( KFbxSdkManager& pSdkManager, KFbxNode* pNode, osg::ref_ptr& pAnimationManager, - bool& bNeedSkeleton, int& nLightCount, + bool& bIsBone, int& nLightCount, FbxMaterialToOsgStateSet& fbxMaterialToOsgStateSet, + std::map& nodeMap, + std::map& boneBindMatrices, + std::map& skeletonMap, const osgDB::Options* options) { if (KFbxNodeAttribute* lNodeAttribute = pNode->GetNodeAttribute()) @@ -327,13 +315,16 @@ osgDB::ReaderWriter::ReadResult readFbxNode( } } + bIsBone = false; + bool bCreateSkeleton = false; + KFbxNodeAttribute::EAttributeType lAttributeType = KFbxNodeAttribute::eUNIDENTIFIED; if (pNode->GetNodeAttribute()) { lAttributeType = pNode->GetNodeAttribute()->GetAttributeType(); if (lAttributeType == KFbxNodeAttribute::eSKELETON) { - bNeedSkeleton = true; + bIsBone = true; } } @@ -360,19 +351,20 @@ osgDB::ReaderWriter::ReadResult readFbxNode( continue; } - bool bChildNeedSkeleton = false; + bool bChildIsBone = false; osgDB::ReaderWriter::ReadResult childResult = readFbxNode( pSdkManager, pChildNode, pAnimationManager, - bChildNeedSkeleton, nLightCount, fbxMaterialToOsgStateSet, options); + bChildIsBone, nLightCount, fbxMaterialToOsgStateSet, nodeMap, + boneBindMatrices, skeletonMap, options); if (childResult.error()) { return childResult; } else if (osg::Node* osgChild = childResult.getNode()) { - if (bChildNeedSkeleton) + if (bChildIsBone) { - bNeedSkeleton = true; + if (!bIsBone) bCreateSkeleton = true; skeletal.push_back(osgChild); } else @@ -390,7 +382,7 @@ osgDB::ReaderWriter::ReadResult readFbxNode( osg::ref_ptr osgGroup; - bool bEmpty = children.empty() && !bNeedSkeleton; + bool bEmpty = children.empty() && !bIsBone; switch (lAttributeType) { @@ -409,15 +401,24 @@ osgDB::ReaderWriter::ReadResult readFbxNode( break; case KFbxNodeAttribute::eMESH: { - osgDB::ReaderWriter::ReadResult meshRes = readFbxMesh(pNode, - pAnimationManager, stateSetList); + size_t bindMatrixCount = boneBindMatrices.size(); + osgDB::ReaderWriter::ReadResult meshRes = readFbxMesh(pSdkManager, + pNode, pAnimationManager, stateSetList, boneBindMatrices, skeletonMap); if (meshRes.error()) { return meshRes; } else if (osg::Node* node = meshRes.getNode()) { - bEmpty = false; + bEmpty = false; + + if (bindMatrixCount != boneBindMatrices.size()) + { + //The mesh is skinned therefore the bind matrix will handle all transformations. + localMatrix.makeIdentity(); + bLocalMatrixIdentity = true; + } + if (animName.empty() && children.empty() && skeletal.empty() && @@ -425,11 +426,8 @@ osgDB::ReaderWriter::ReadResult readFbxNode( { return osgDB::ReaderWriter::ReadResult(node); } - osgGroup = createGroupNode(pSdkManager, pNode, animName, localMatrix, bNeedSkeleton); - assert(osgGroup->getStateSet() == NULL); - osgGroup->getOrCreateStateSet()->setMode(GL_RESCALE_NORMAL,osg::StateAttribute::ON); - osgGroup->addChild(node); + children.insert(children.begin(), node); } } break; @@ -453,9 +451,7 @@ osgDB::ReaderWriter::ReadResult readFbxNode( } else { - osgGroup = createGroupNode(pSdkManager, pNode, animName, - localMatrix, bNeedSkeleton); - osgGroup->addChild(resGroup); + children.insert(children.begin(), resGroup); } } } @@ -467,15 +463,51 @@ osgDB::ReaderWriter::ReadResult readFbxNode( osgDB::ReaderWriter::ReadResult(0); } - if (!osgGroup) osgGroup = createGroupNode(pSdkManager, pNode, animName, localMatrix, bNeedSkeleton); + if (!osgGroup) osgGroup = createGroupNode(pSdkManager, pNode, animName, localMatrix, bIsBone, nodeMap); + + osg::Group* pAddChildrenTo = osgGroup.get(); + if (bCreateSkeleton) + { + osgAnimation::Skeleton* osgSkeleton = getSkeleton(pNode, skeletonMap); + osgSkeleton->setDefaultUpdateCallback(); + pAddChildrenTo->addChild(osgSkeleton); + pAddChildrenTo = osgSkeleton; + } + for (osg::NodeList::iterator it = skeletal.begin(); it != skeletal.end(); ++it) { - osgGroup->addChild(it->get()); + pAddChildrenTo->addChild(it->get()); } for (osg::NodeList::iterator it = children.begin(); it != children.end(); ++it) { - osgGroup->addChild(it->get()); + pAddChildrenTo->addChild(it->get()); } + return osgDB::ReaderWriter::ReadResult(osgGroup.get()); } + +osgAnimation::Skeleton* getSkeleton(KFbxNode* fbxNode, + std::map& skeletonMap) +{ + //Find the first non-skeleton ancestor of the node. + while (fbxNode && + fbxNode->GetNodeAttribute() && + fbxNode->GetNodeAttribute()->GetAttributeType() == KFbxNodeAttribute::eSKELETON) + { + fbxNode = fbxNode->GetParent(); + } + + std::map::const_iterator it = skeletonMap.find(fbxNode); + if (it == skeletonMap.end()) + { + osgAnimation::Skeleton* skel = new osgAnimation::Skeleton; + skel->setDefaultUpdateCallback(); + skeletonMap.insert(std::pair(fbxNode, skel)); + return skel; + } + else + { + return it->second; + } +} diff --git a/src/osgPlugins/fbx/fbxRNode.h b/src/osgPlugins/fbx/fbxRNode.h index 225c8cd8a..50cda219f 100644 --- a/src/osgPlugins/fbx/fbxRNode.h +++ b/src/osgPlugins/fbx/fbxRNode.h @@ -7,13 +7,18 @@ namespace osgAnimation class AnimationManagerBase; } +osgAnimation::Skeleton* getSkeleton(KFbxNode*, std::map&); + osgDB::ReaderWriter::ReadResult readFbxNode( FBXFILESDK_NAMESPACE::KFbxSdkManager& pSdkManager, FBXFILESDK_NAMESPACE::KFbxNode* pNode, osg::ref_ptr& pAnimationManager, - bool& bNeedSkeleton, + bool& bIsBone, int& nLightCount, FbxMaterialToOsgStateSet& fbxMaterialToOsgStateSet, + std::map& nodeMap, + std::map& boneBindMatrices, + std::map& skeletonMap, const osgDB::Options* options = NULL); #endif