#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(_MSC_VER) #pragma warning( disable : 4505 ) #endif #include #include "fbxReader.h" osg::Quat makeQuat(const fbxDouble3& degrees, ERotationOrder fbxRotOrder) { double radiansX = osg::DegreesToRadians(degrees[0]); double radiansY = osg::DegreesToRadians(degrees[1]); double radiansZ = osg::DegreesToRadians(degrees[2]); switch (fbxRotOrder) { case eEULER_XYZ: return osg::Quat( radiansX, osg::Vec3d(1,0,0), radiansY, osg::Vec3d(0,1,0), radiansZ, osg::Vec3d(0,0,1)); case eEULER_XZY: return osg::Quat( radiansX, osg::Vec3d(1,0,0), radiansZ, osg::Vec3d(0,0,1), radiansY, osg::Vec3d(0,1,0)); case eEULER_YZX: 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( radiansY, osg::Vec3d(0,1,0), radiansX, osg::Vec3d(1,0,0), radiansZ, osg::Vec3d(0,0,1)); case eEULER_ZXY: return osg::Quat( radiansZ, osg::Vec3d(0,0,1), radiansX, osg::Vec3d(1,0,0), radiansY, osg::Vec3d(0,1,0)); case eEULER_ZYX: 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. osg::Quat quat; quat.makeRotate(osg::Vec3d(1.0, 0.0, 0.0), osg::Vec3d(degrees[0], degrees[1], degrees[2])); return quat; } default: OSG_WARN << "Invalid FBX rotation mode." << std::endl; return osg::Quat(); } } void makeLocalMatrix(const KFbxNode* pNode, osg::Matrix& m) { /*From http://area.autodesk.com/forum/autodesk-fbx/fbx-sdk/the-makeup-of-the-local-matrix-of-an-kfbxnode/ Local Matrix = LclTranslation * RotationOffset * RotationPivot * PreRotation * LclRotation * PostRotation * RotationPivotInverse * ScalingOffset * ScalingPivot * LclScaling * ScalingPivotInverse LocalTranslation : translate (xform -query -translation) RotationOffset: translation compensates for the change in the rotate pivot point (xform -q -rotateTranslation) RotationPivot: current rotate pivot position (xform -q -rotatePivot) PreRotation : joint orientation(pre rotation) LocalRotation: rotate transform (xform -q -rotation & xform -q -rotateOrder) PostRotation : rotate axis (xform -q -rotateAxis) RotationPivotInverse: inverse of RotationPivot ScalingOffset: translation compensates for the change in the scale pivot point (xform -q -scaleTranslation) ScalingPivot: current scale pivot position (xform -q -scalePivot) LocalScaling: scale transform (xform -q -scale) ScalingPivotInverse: inverse of ScalingPivot */ // When this flag is set to false, the RotationOrder, the Pre/Post rotation // values and the rotation limits should be ignored. bool rotationActive = pNode->RotationActive.Get(); ERotationOrder fbxRotOrder = rotationActive ? pNode->RotationOrder.Get() : eEULER_XYZ; fbxDouble3 fbxLclPos = pNode->LclTranslation.Get(); fbxDouble3 fbxRotOff = pNode->RotationOffset.Get(); fbxDouble3 fbxRotPiv = pNode->RotationPivot.Get(); fbxDouble3 fbxPreRot = pNode->PreRotation.Get(); fbxDouble3 fbxLclRot = pNode->LclRotation.Get(); fbxDouble3 fbxPostRot = pNode->PostRotation.Get(); fbxDouble3 fbxSclOff = pNode->ScalingOffset.Get(); fbxDouble3 fbxSclPiv = pNode->ScalingPivot.Get(); fbxDouble3 fbxLclScl = pNode->LclScaling.Get(); m.makeTranslate(osg::Vec3d( fbxLclPos[0] + fbxRotOff[0] + fbxRotPiv[0], fbxLclPos[1] + fbxRotOff[1] + fbxRotPiv[1], fbxLclPos[2] + fbxRotOff[2] + fbxRotPiv[2])); if (rotationActive) { m.preMultRotate( makeQuat(fbxPostRot, fbxRotOrder) * makeQuat(fbxLclRot, fbxRotOrder) * makeQuat(fbxPreRot, fbxRotOrder)); } else { m.preMultRotate(makeQuat(fbxLclRot, fbxRotOrder)); } m.preMultTranslate(osg::Vec3d( fbxSclOff[0] + fbxSclPiv[0] - fbxRotPiv[0], fbxSclOff[1] + fbxSclPiv[1] - fbxRotPiv[1], fbxSclOff[2] + fbxSclPiv[2] - fbxRotPiv[2])); m.preMultScale(osg::Vec3d(fbxLclScl[0], fbxLclScl[1], fbxLclScl[2])); m.preMultTranslate(osg::Vec3d( -fbxSclPiv[0], -fbxSclPiv[1], -fbxSclPiv[2])); } void readTranslationElement(KFbxTypedProperty& prop, osgAnimation::UpdateMatrixTransform* pUpdate, osg::Matrix& staticTransform) { fbxDouble3 fbxPropValue = prop.Get(); osg::Vec3d val( fbxPropValue[0], fbxPropValue[1], fbxPropValue[2]); if (prop.GetKFCurve(KFCURVENODE_T_X) || prop.GetKFCurve(KFCURVENODE_T_Y) || prop.GetKFCurve(KFCURVENODE_T_Z)) { if (!staticTransform.isIdentity()) { pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedMatrixElement(staticTransform)); staticTransform.makeIdentity(); } pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedTranslateElement("translate", val)); } else { staticTransform.preMultTranslate(val); } } void getRotationOrder(ERotationOrder fbxRotOrder, int order[/*3*/]) { switch (fbxRotOrder) { case eEULER_XZY: order[0] = 0; order[1] = 2; order[2] = 1; break; case eEULER_YZX: order[0] = 1; order[1] = 2; order[2] = 0; break; case eEULER_YXZ: order[0] = 1; order[1] = 0; order[2] = 2; break; case eEULER_ZXY: order[0] = 2; order[1] = 0; order[2] = 1; break; case eEULER_ZYX: order[0] = 2; order[1] = 1; order[2] = 0; break; default: order[0] = 0; order[1] = 1; order[2] = 2; } } void readRotationElement(KFbxTypedProperty& prop, ERotationOrder fbxRotOrder, bool quatInterpolate, osgAnimation::UpdateMatrixTransform* pUpdate, osg::Matrix& staticTransform) { if (prop.GetKFCurve(KFCURVENODE_R_X) || prop.GetKFCurve(KFCURVENODE_R_Y) || prop.GetKFCurve(KFCURVENODE_R_Z)) { if (quatInterpolate) { if (!staticTransform.isIdentity()) { pUpdate->getStackedTransforms().push_back( new osgAnimation::StackedMatrixElement(staticTransform)); staticTransform.makeIdentity(); } pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedQuaternionElement( "quaternion", makeQuat(prop.Get(), fbxRotOrder))); } else { char* curveNames[3] = {KFCURVENODE_R_X, KFCURVENODE_R_Y, KFCURVENODE_R_Z}; osg::Vec3 axes[3] = {osg::Vec3(1,0,0), osg::Vec3(0,1,0), osg::Vec3(0,0,1)}; fbxDouble3 fbxPropValue = prop.Get(); fbxPropValue[0] = osg::DegreesToRadians(fbxPropValue[0]); fbxPropValue[1] = osg::DegreesToRadians(fbxPropValue[1]); fbxPropValue[2] = osg::DegreesToRadians(fbxPropValue[2]); int order[3] = {0, 1, 2}; getRotationOrder(fbxRotOrder, order); for (int i = 0; i < 3; ++i) { int j = order[2-i]; if (prop.GetKFCurve(curveNames[j])) { if (!staticTransform.isIdentity()) { pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedMatrixElement(staticTransform)); staticTransform.makeIdentity(); } pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedRotateAxisElement( std::string("rotate") + curveNames[j], axes[j], fbxPropValue[j])); } else { staticTransform.preMultRotate(osg::Quat(fbxPropValue[j], axes[j])); } } } } else { staticTransform.preMultRotate(makeQuat(prop.Get(), fbxRotOrder)); } } void readScaleElement(KFbxTypedProperty& prop, osgAnimation::UpdateMatrixTransform* pUpdate, osg::Matrix& staticTransform) { fbxDouble3 fbxPropValue = prop.Get(); osg::Vec3d val( fbxPropValue[0], fbxPropValue[1], fbxPropValue[2]); if (prop.GetKFCurve(KFCURVENODE_S_X) || prop.GetKFCurve(KFCURVENODE_S_Y) || prop.GetKFCurve(KFCURVENODE_S_Z)) { if (!staticTransform.isIdentity()) { pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedMatrixElement(staticTransform)); staticTransform.makeIdentity(); } pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedScaleElement("scale", val)); } else { staticTransform.preMultScale(val); } } void readUpdateMatrixTransform(osgAnimation::UpdateMatrixTransform* pUpdate, KFbxNode* pNode) { osg::Matrix staticTransform; readTranslationElement(pNode->LclTranslation, pUpdate, staticTransform); fbxDouble3 fbxRotOffset = pNode->RotationOffset.Get(); fbxDouble3 fbxRotPiv = pNode->RotationPivot.Get(); staticTransform.preMultTranslate(osg::Vec3d( fbxRotPiv[0] + fbxRotOffset[0], fbxRotPiv[1] + fbxRotOffset[1], fbxRotPiv[2] + fbxRotOffset[2])); // When this flag is set to false, the RotationOrder, the Pre/Post rotation // values and the rotation limits should be ignored. bool rotationActive = pNode->RotationActive.Get(); ERotationOrder fbxRotOrder = (rotationActive && pNode->RotationOrder.IsValid()) ? pNode->RotationOrder.Get() : eEULER_XYZ; if (rotationActive) { staticTransform.preMultRotate(makeQuat(pNode->PreRotation.Get(), fbxRotOrder)); } readRotationElement(pNode->LclRotation, fbxRotOrder, pNode->QuaternionInterpolate.IsValid() && pNode->QuaternionInterpolate.Get(), pUpdate, staticTransform); if (rotationActive) { staticTransform.preMultRotate(makeQuat(pNode->PostRotation.Get(), fbxRotOrder)); } fbxDouble3 fbxSclOffset = pNode->ScalingOffset.Get(); fbxDouble3 fbxSclPiv = pNode->ScalingPivot.Get(); staticTransform.preMultTranslate(osg::Vec3d( fbxSclOffset[0] + fbxSclPiv[0] - fbxRotPiv[0], fbxSclOffset[1] + fbxSclPiv[1] - fbxRotPiv[1], fbxSclOffset[2] + fbxSclPiv[2] - fbxRotPiv[2])); readScaleElement(pNode->LclScaling, pUpdate, staticTransform); staticTransform.preMultTranslate(osg::Vec3d( -fbxSclPiv[0], -fbxSclPiv[1], -fbxSclPiv[2])); if (!staticTransform.isIdentity()) { pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedMatrixElement(staticTransform)); } } osg::Group* createGroupNode(KFbxSdkManager& pSdkManager, KFbxNode* pNode, const std::string& animName, const osg::Matrix& localMatrix, bool bNeedSkeleton, std::map& nodeMap) { if (bNeedSkeleton) { osgAnimation::Bone* osgBone = new osgAnimation::Bone; osgBone->setDataVariance(osg::Object::DYNAMIC); osgBone->setName(pNode->GetName()); osgAnimation::UpdateBone* pUpdate = new osgAnimation::UpdateBone(animName); readUpdateMatrixTransform(pUpdate, pNode); osgBone->setUpdateCallback(pUpdate); nodeMap.insert(std::pair(pNode, osgBone)); return osgBone; } else { bool bAnimated = !animName.empty(); if (!bAnimated && localMatrix.isIdentity()) { osg::Group* pGroup = new osg::Group; pGroup->setName(pNode->GetName()); return pGroup; } osg::MatrixTransform* pTransform = new osg::MatrixTransform(localMatrix); pTransform->setName(pNode->GetName()); if (bAnimated) { osgAnimation::UpdateMatrixTransform* pUpdate = new osgAnimation::UpdateMatrixTransform(animName); readUpdateMatrixTransform(pUpdate, pNode); pTransform->setUpdateCallback(pUpdate); } return pTransform; } } osgDB::ReaderWriter::ReadResult OsgFbxReader::readFbxNode( KFbxNode* pNode, bool& bIsBone, int& nLightCount) { if (KFbxNodeAttribute* lNodeAttribute = pNode->GetNodeAttribute()) { KFbxNodeAttribute::EAttributeType attrType = lNodeAttribute->GetAttributeType(); switch (attrType) { case KFbxNodeAttribute::eNURB: case KFbxNodeAttribute::ePATCH: case KFbxNodeAttribute::eNURBS_CURVE: case KFbxNodeAttribute::eNURBS_SURFACE: { KFbxGeometryConverter lConverter(&pSdkManager); if (!lConverter.TriangulateInPlace(pNode)) { OSG_WARN << "Unable to triangulate FBX NURBS " << pNode->GetName() << std::endl; } } break; default: break; } } bIsBone = false; bool bCreateSkeleton = false; KFbxNodeAttribute::EAttributeType lAttributeType = KFbxNodeAttribute::eUNIDENTIFIED; if (pNode->GetNodeAttribute()) { lAttributeType = pNode->GetNodeAttribute()->GetAttributeType(); if (lAttributeType == KFbxNodeAttribute::eSKELETON) { bIsBone = true; } } if (!bIsBone && fbxSkeletons.find(pNode) != fbxSkeletons.end()) { bIsBone = true; } unsigned nMaterials = pNode->GetMaterialCount(); std::vector stateSetList; for (unsigned i = 0; i < nMaterials; ++i) { KFbxSurfaceMaterial* fbxMaterial = pNode->GetMaterial(i); assert(fbxMaterial); stateSetList.push_back(fbxMaterialToOsgStateSet.convert(fbxMaterial)); } osg::NodeList skeletal, children; int nChildCount = pNode->GetChildCount(); for (int i = 0; i < nChildCount; ++i) { KFbxNode* pChildNode = pNode->GetChild(i); if (pChildNode->GetParent() != pNode) { //workaround for bug that occurs in some files exported from Blender continue; } bool bChildIsBone = false; osgDB::ReaderWriter::ReadResult childResult = readFbxNode( pChildNode, bChildIsBone, nLightCount); if (childResult.error()) { return childResult; } else if (osg::Node* osgChild = childResult.getNode()) { if (bChildIsBone) { if (!bIsBone) bCreateSkeleton = true; skeletal.push_back(osgChild); } else { children.push_back(osgChild); } } } std::string animName = readFbxAnimation(pNode, pNode->GetName()); osg::Matrix localMatrix; makeLocalMatrix(pNode, localMatrix); bool bLocalMatrixIdentity = localMatrix.isIdentity(); osg::ref_ptr osgGroup; bool bEmpty = children.empty() && !bIsBone; switch (lAttributeType) { case KFbxNodeAttribute::eMESH: { size_t bindMatrixCount = boneBindMatrices.size(); osgDB::ReaderWriter::ReadResult meshRes = readFbxMesh(pNode, stateSetList); if (meshRes.error()) { return meshRes; } else if (osg::Node* node = meshRes.getNode()) { 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() && bLocalMatrixIdentity) { return osgDB::ReaderWriter::ReadResult(node); } children.insert(children.begin(), node); } } break; case KFbxNodeAttribute::eCAMERA: case KFbxNodeAttribute::eLIGHT: { osgDB::ReaderWriter::ReadResult res = lAttributeType == KFbxNodeAttribute::eCAMERA ? readFbxCamera(pNode) : readFbxLight(pNode, nLightCount); if (res.error()) { return res; } else if (osg::Group* resGroup = dynamic_cast(res.getObject())) { bEmpty = false; if (animName.empty() && bLocalMatrixIdentity) { osgGroup = resGroup; } else { children.insert(children.begin(), resGroup); } } } break; } if (bEmpty) { osgDB::ReaderWriter::ReadResult(0); } if (!osgGroup) osgGroup = createGroupNode(pSdkManager, pNode, animName, localMatrix, bIsBone, nodeMap); osg::Group* pAddChildrenTo = osgGroup.get(); if (bCreateSkeleton) { osgAnimation::Skeleton* osgSkeleton = getSkeleton(pNode, fbxSkeletons, skeletonMap); osgSkeleton->setDefaultUpdateCallback(); pAddChildrenTo->addChild(osgSkeleton); pAddChildrenTo = osgSkeleton; } for (osg::NodeList::iterator it = skeletal.begin(); it != skeletal.end(); ++it) { pAddChildrenTo->addChild(it->get()); } for (osg::NodeList::iterator it = children.begin(); it != children.end(); ++it) { pAddChildrenTo->addChild(it->get()); } return osgDB::ReaderWriter::ReadResult(osgGroup.get()); } osgAnimation::Skeleton* getSkeleton(KFbxNode* fbxNode, const std::set& fbxSkeletons, std::map& skeletonMap) { //Find the first non-skeleton ancestor of the node. while (fbxNode && ((fbxNode->GetNodeAttribute() && fbxNode->GetNodeAttribute()->GetAttributeType() == KFbxNodeAttribute::eSKELETON) || fbxSkeletons.find(fbxNode) != fbxSkeletons.end())) { 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; } }