root/OpenSceneGraph/trunk/src/osgPlugins/fbx/fbxRNode.cpp @ 11262

Revision 11262, 17.9 kB (checked in by mplatings, 5 years ago)

Workaround for files exported from SoftImage? that don't tag skeleton nodes correctly.

RevLine 
[11109]1#include <cassert>
[10780]2#include <memory>
[11109]3#include <sstream>
[10780]4
[11109]5#include <osg/io_utils>
[10780]6#include <osg/Notify>
7#include <osg/MatrixTransform>
8#include <osg/Material>
9#include <osg/Texture2D>
10
11#include <osgDB/FileNameUtils>
12#include <osgDB/ReadFile>
13
14#include <osgAnimation/AnimationManagerBase>
[11109]15#include <osgAnimation/Bone>
[10780]16#include <osgAnimation/Skeleton>
[11109]17#include <osgAnimation/StackedMatrixElement>
18#include <osgAnimation/StackedQuaternionElement>
19#include <osgAnimation/StackedScaleElement>
20#include <osgAnimation/StackedTranslateElement>
21#include <osgAnimation/UpdateBone>
[10780]22
23#if defined(_MSC_VER)
24    #pragma warning( disable : 4505 )
25#endif
26#include <fbxsdk.h>
27
28#include "fbxRAnimation.h"
29#include "fbxRCamera.h"
30#include "fbxRLight.h"
31#include "fbxRMesh.h"
[11109]32#include "fbxRNode.h"
33#include "fbxMaterialToOsgStateSet.h"
[10780]34
35osg::Quat makeQuat(const fbxDouble3& degrees, ERotationOrder fbxRotOrder)
36{
37    double radiansX = osg::DegreesToRadians(degrees[0]);
38    double radiansY = osg::DegreesToRadians(degrees[1]);
39    double radiansZ = osg::DegreesToRadians(degrees[2]);
40
41    switch (fbxRotOrder)
42    {
43    case eEULER_XYZ:
44        return osg::Quat(
45            radiansX, osg::Vec3d(1,0,0),
46            radiansY, osg::Vec3d(0,1,0),
47            radiansZ, osg::Vec3d(0,0,1));
48    case eEULER_XZY:
49        return osg::Quat(
[11141]50            radiansX, osg::Vec3d(1,0,0),
51            radiansZ, osg::Vec3d(0,0,1),
52            radiansY, osg::Vec3d(0,1,0));
[10780]53    case eEULER_YZX:
[11141]54        return osg::Quat(
55            radiansY, osg::Vec3d(0,1,0),
56            radiansZ, osg::Vec3d(0,0,1),
57            radiansX, osg::Vec3d(1,0,0));
[10780]58    case eEULER_YXZ:
[11141]59        return osg::Quat(
60            radiansY, osg::Vec3d(0,1,0),
61            radiansX, osg::Vec3d(1,0,0),
62            radiansZ, osg::Vec3d(0,0,1));
[10780]63    case eEULER_ZXY:
64        return osg::Quat(
[11139]65            radiansZ, osg::Vec3d(0,0,1),
66            radiansX, osg::Vec3d(1,0,0),
67            radiansY, osg::Vec3d(0,1,0));
[10780]68    case eEULER_ZYX:
[11141]69        return osg::Quat(
70            radiansZ, osg::Vec3d(0,0,1),
71            radiansY, osg::Vec3d(0,1,0),
72            radiansX, osg::Vec3d(1,0,0));
[10780]73    case eSPHERIC_XYZ:
74        {
75            //I don't know what eSPHERIC_XYZ means, so this is a complete guess.
76            osg::Quat quat;
77            quat.makeRotate(osg::Vec3d(1.0, 0.0, 0.0), osg::Vec3d(degrees[0], degrees[1], degrees[2]));
78            return quat;
79        }
80    default:
81        osg::notify(osg::WARN) << "Invalid FBX rotation mode." << std::endl;
82        return osg::Quat();
83    }
84}
85
86void makeLocalMatrix(const KFbxNode* pNode, osg::Matrix& m)
87{
88    /*From http://area.autodesk.com/forum/autodesk-fbx/fbx-sdk/the-makeup-of-the-local-matrix-of-an-kfbxnode/
89
90    Local Matrix = LclTranslation * RotationOffset * RotationPivot *
91      PreRotation * LclRotation * PostRotation * RotationPivotInverse *
92      ScalingOffset * ScalingPivot * LclScaling * ScalingPivotInverse
93
94    LocalTranslation : translate (xform -query -translation)
95    RotationOffset: translation compensates for the change in the rotate pivot point (xform -q -rotateTranslation)
96    RotationPivot: current rotate pivot position (xform -q -rotatePivot)
97    PreRotation : joint orientation(pre rotation)
98    LocalRotation: rotate transform (xform -q -rotation & xform -q -rotateOrder)
99    PostRotation : rotate axis (xform -q -rotateAxis)
100    RotationPivotInverse: inverse of RotationPivot
101    ScalingOffset: translation compensates for the change in the scale pivot point (xform -q -scaleTranslation)
102    ScalingPivot: current scale pivot position (xform -q -scalePivot)
103    LocalScaling: scale transform (xform -q -scale)
104    ScalingPivotInverse: inverse of ScalingPivot
105    */
106
[11144]107    // When this flag is set to false, the RotationOrder, the Pre/Post rotation
108    // values and the rotation limits should be ignored.
[11141]109    bool rotationActive = pNode->RotationActive.Get();
[10780]110
[11141]111    ERotationOrder fbxRotOrder = rotationActive ? pNode->RotationOrder.Get() : eEULER_XYZ;
112
[10780]113    fbxDouble3 fbxLclPos = pNode->LclTranslation.Get();
114    fbxDouble3 fbxRotOff = pNode->RotationOffset.Get();
115    fbxDouble3 fbxRotPiv = pNode->RotationPivot.Get();
116    fbxDouble3 fbxPreRot = pNode->PreRotation.Get();
117    fbxDouble3 fbxLclRot = pNode->LclRotation.Get();
118    fbxDouble3 fbxPostRot = pNode->PostRotation.Get();
119    fbxDouble3 fbxSclOff = pNode->ScalingOffset.Get();
120    fbxDouble3 fbxSclPiv = pNode->ScalingPivot.Get();
121    fbxDouble3 fbxLclScl = pNode->LclScaling.Get();
122
123    m.makeTranslate(osg::Vec3d(
124        fbxLclPos[0] + fbxRotOff[0] + fbxRotPiv[0],
125        fbxLclPos[1] + fbxRotOff[1] + fbxRotPiv[1],
126        fbxLclPos[2] + fbxRotOff[2] + fbxRotPiv[2]));
[11141]127    if (rotationActive)
128    {
129        m.preMultRotate(
130            makeQuat(fbxPostRot, fbxRotOrder) *
131            makeQuat(fbxLclRot, fbxRotOrder) *
132            makeQuat(fbxPreRot, fbxRotOrder));
133    }
134    else
135    {
136        m.preMultRotate(makeQuat(fbxLclRot, fbxRotOrder));
137    }
[10780]138    m.preMultTranslate(osg::Vec3d(
139        fbxSclOff[0] + fbxSclPiv[0] - fbxRotPiv[0],
140        fbxSclOff[1] + fbxSclPiv[1] - fbxRotPiv[1],
141        fbxSclOff[2] + fbxSclPiv[2] - fbxRotPiv[2]));
142    m.preMultScale(osg::Vec3d(fbxLclScl[0], fbxLclScl[1], fbxLclScl[2]));
143    m.preMultTranslate(osg::Vec3d(
144        -fbxSclPiv[0],
145        -fbxSclPiv[1],
146        -fbxSclPiv[2]));
147}
148
[11109]149void readTranslationElement(KFbxTypedProperty<fbxDouble3>& prop,
150                            osgAnimation::UpdateMatrixTransform* pUpdate,
151                            osg::Matrix& staticTransform)
152{
153    fbxDouble3 fbxPropValue = prop.Get();
154    osg::Vec3d val(
155        fbxPropValue[0],
156        fbxPropValue[1],
157        fbxPropValue[2]);
[10780]158
[11109]159    if (prop.GetKFCurve(KFCURVENODE_T_X) ||
160        prop.GetKFCurve(KFCURVENODE_T_Y) ||
161        prop.GetKFCurve(KFCURVENODE_T_Z))
162    {
163        if (!staticTransform.isIdentity())
164        {
165            pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedMatrixElement(staticTransform));
166            staticTransform.makeIdentity();
167        }
168        pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedTranslateElement("translate", val));
169    }
170    else
171    {
172        staticTransform.preMultTranslate(val);
173    }
[10780]174}
175
[11109]176void readRotationElement(KFbxTypedProperty<fbxDouble3>& prop,
177                         ERotationOrder fbxRotOrder,
178                         osgAnimation::UpdateMatrixTransform* pUpdate,
179                         osg::Matrix& staticTransform)
[10780]180{
[11109]181    osg::Quat quat = makeQuat(prop.Get(), fbxRotOrder);
[10780]182
[11109]183    if (prop.GetKFCurve(KFCURVENODE_R_X) ||
184        prop.GetKFCurve(KFCURVENODE_R_Y) ||
185        prop.GetKFCurve(KFCURVENODE_R_Z))
186    {
187        if (!staticTransform.isIdentity())
188        {
189            pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedMatrixElement(staticTransform));
190            staticTransform.makeIdentity();
191        }
192        pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedQuaternionElement("quaternion", quat));
193    }
194    else
195    {
196        staticTransform.preMultRotate(quat);
197    }
198}
[10780]199
[11109]200void readScaleElement(KFbxTypedProperty<fbxDouble3>& prop,
201                      osgAnimation::UpdateMatrixTransform* pUpdate,
202                      osg::Matrix& staticTransform)
203{
204    fbxDouble3 fbxPropValue = prop.Get();
205    osg::Vec3d val(
206        fbxPropValue[0],
207        fbxPropValue[1],
208        fbxPropValue[2]);
[10780]209
[11109]210    if (prop.GetKFCurve(KFCURVENODE_S_X) ||
211        prop.GetKFCurve(KFCURVENODE_S_Y) ||
212        prop.GetKFCurve(KFCURVENODE_S_Z))
213    {
214        if (!staticTransform.isIdentity())
215        {
216            pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedMatrixElement(staticTransform));
217            staticTransform.makeIdentity();
218        }
219        pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedScaleElement("scale", val));
220    }
221    else
222    {
223        staticTransform.preMultScale(val);
224    }
[10780]225}
226
[11109]227void readUpdateMatrixTransform(osgAnimation::UpdateMatrixTransform* pUpdate, KFbxNode* pNode)
[10780]228{
[11109]229    osg::Matrix staticTransform;
230
231    readTranslationElement(pNode->LclTranslation, pUpdate, staticTransform);
232
233    fbxDouble3 fbxRotOffset = pNode->RotationOffset.Get();
234    fbxDouble3 fbxRotPiv = pNode->RotationPivot.Get();
235    staticTransform.preMultTranslate(osg::Vec3d(
236        fbxRotPiv[0] + fbxRotOffset[0],
237        fbxRotPiv[1] + fbxRotOffset[1],
238        fbxRotPiv[2] + fbxRotOffset[2]));
239
[11144]240    // When this flag is set to false, the RotationOrder, the Pre/Post rotation
241    // values and the rotation limits should be ignored.
[11141]242    bool rotationActive = pNode->RotationActive.Get();
[11109]243
[11141]244    ERotationOrder fbxRotOrder = (rotationActive && pNode->RotationOrder.IsValid()) ?
245        pNode->RotationOrder.Get() : eEULER_XYZ;
[11109]246
[11141]247    if (rotationActive)
248    {
249        staticTransform.preMultRotate(makeQuat(pNode->PreRotation.Get(), fbxRotOrder));
250    }
251
[11109]252    readRotationElement(pNode->LclRotation, fbxRotOrder, pUpdate, staticTransform);
253
[11141]254    if (rotationActive)
255    {
256        staticTransform.preMultRotate(makeQuat(pNode->PostRotation.Get(), fbxRotOrder));
257    }
[11109]258
259    fbxDouble3 fbxSclOffset = pNode->ScalingOffset.Get();
260    fbxDouble3 fbxSclPiv = pNode->ScalingPivot.Get();
261    staticTransform.preMultTranslate(osg::Vec3d(
262        fbxSclOffset[0] + fbxSclPiv[0] - fbxRotPiv[0],
263        fbxSclOffset[1] + fbxSclPiv[1] - fbxRotPiv[1],
264        fbxSclOffset[2] + fbxSclPiv[2] - fbxRotPiv[2]));
265
266    readScaleElement(pNode->LclScaling, pUpdate, staticTransform);
267
268    staticTransform.preMultTranslate(osg::Vec3d(
269        -fbxSclPiv[0],
270        -fbxSclPiv[1],
271        -fbxSclPiv[2]));
272
273    if (!staticTransform.isIdentity())
[10780]274    {
[11109]275        pUpdate->getStackedTransforms().push_back(new osgAnimation::StackedMatrixElement(staticTransform));
[10780]276    }
277}
278
279osg::Group* createGroupNode(KFbxSdkManager& pSdkManager, KFbxNode* pNode,
[11139]280    const std::string& animName, const osg::Matrix& localMatrix, bool bNeedSkeleton,
[11141]281    std::map<KFbxNode*, osg::Node*>& nodeMap)
[10780]282{
283    if (bNeedSkeleton)
284    {
285        osgAnimation::Bone* osgBone = new osgAnimation::Bone;
286        osgBone->setDataVariance(osg::Object::DYNAMIC);
287        osgBone->setName(pNode->GetName());
[11109]288        osgAnimation::UpdateBone* pUpdate = new osgAnimation::UpdateBone(animName);
289        readUpdateMatrixTransform(pUpdate, pNode);
290        osgBone->setUpdateCallback(pUpdate);
[10780]291
[11141]292        nodeMap.insert(std::pair<KFbxNode*, osg::Node*>(pNode, osgBone));
[10780]293
294        return osgBone;
295    }
296    else
297    {
298        bool bAnimated = !animName.empty();
299        if (!bAnimated && localMatrix.isIdentity())
300        {
301            osg::Group* pGroup = new osg::Group;
302            pGroup->setName(pNode->GetName());
303            return pGroup;
304        }
305
306        osg::MatrixTransform* pTransform = new osg::MatrixTransform(localMatrix);
307        pTransform->setName(pNode->GetName());
[11109]308
[10780]309        if (bAnimated)
310        {
[11109]311            osgAnimation::UpdateMatrixTransform* pUpdate = new osgAnimation::UpdateMatrixTransform(animName);
312            readUpdateMatrixTransform(pUpdate, pNode);
[10780]313            pTransform->setUpdateCallback(pUpdate);
314        }
[11109]315
[10780]316        return pTransform;
317    }
318}
319
320osgDB::ReaderWriter::ReadResult readFbxNode(
321    KFbxSdkManager& pSdkManager, KFbxNode* pNode,
322    osg::ref_ptr<osgAnimation::AnimationManagerBase>& pAnimationManager,
[11139]323    bool& bIsBone, int& nLightCount,
[11109]324    FbxMaterialToOsgStateSet& fbxMaterialToOsgStateSet,
[11141]325    std::map<KFbxNode*, osg::Node*>& nodeMap,
[11153]326    BindMatrixMap& boneBindMatrices,
[11262]327    const std::set<const KFbxNode*>& fbxSkeletons,
[11141]328    std::map<KFbxNode*, osgAnimation::Skeleton*>& skeletonMap,
[11109]329    const osgDB::Options* options)
[10780]330{
331    if (KFbxNodeAttribute* lNodeAttribute = pNode->GetNodeAttribute())
332    {
333        if (lNodeAttribute->GetAttributeType() == KFbxNodeAttribute::eNURB ||
334            lNodeAttribute->GetAttributeType() == KFbxNodeAttribute::ePATCH)
335        {
336            KFbxGeometryConverter lConverter(&pSdkManager);
337            lConverter.TriangulateInPlace(pNode);
338        }
339    }
340
[11141]341    bIsBone = false;
342    bool bCreateSkeleton = false;
[11139]343
[10780]344    KFbxNodeAttribute::EAttributeType lAttributeType = KFbxNodeAttribute::eUNIDENTIFIED;
345    if (pNode->GetNodeAttribute())
346    {
347        lAttributeType = pNode->GetNodeAttribute()->GetAttributeType();
348        if (lAttributeType == KFbxNodeAttribute::eSKELETON)
349        {
[11139]350            bIsBone = true;
[10780]351        }
352    }
353
[11262]354    if (!bIsBone && fbxSkeletons.find(pNode) != fbxSkeletons.end())
355    {
356        bIsBone = true;
357    }
358
[10780]359    unsigned nMaterials = pNode->GetMaterialCount();
[11109]360    std::vector<StateSetContent > stateSetList;
[10780]361
362    for (unsigned i = 0; i < nMaterials; ++i)
363    {
[11109]364        KFbxSurfaceMaterial* fbxMaterial = pNode->GetMaterial(i);
365        assert(fbxMaterial);
366        stateSetList.push_back(fbxMaterialToOsgStateSet.convert(fbxMaterial));
[10780]367    }
368
369    osg::NodeList skeletal, children;
370
371    int nChildCount = pNode->GetChildCount();
372    for (int i = 0; i < nChildCount; ++i)
373    {
374        KFbxNode* pChildNode = pNode->GetChild(i);
375
376        if (pChildNode->GetParent() != pNode)
377        {
378            //workaround for bug that occurs in some files exported from Blender
379            continue;
380        }
381
[11139]382        bool bChildIsBone = false;
[10780]383        osgDB::ReaderWriter::ReadResult childResult = readFbxNode(
[11109]384            pSdkManager, pChildNode, pAnimationManager,
[11139]385            bChildIsBone, nLightCount, fbxMaterialToOsgStateSet, nodeMap,
[11262]386            boneBindMatrices, fbxSkeletons, skeletonMap, options);
[10780]387        if (childResult.error())
388        {
389            return childResult;
390        }
391        else if (osg::Node* osgChild = childResult.getNode())
392        {
[11139]393            if (bChildIsBone)
[10780]394            {
[11141]395                if (!bIsBone) bCreateSkeleton = true;
[10780]396                skeletal.push_back(osgChild);
397            }
398            else
399            {
400                children.push_back(osgChild);
401            }
402        }
403    }
404
[11109]405    std::string animName = readFbxAnimation(pNode, pAnimationManager, pNode->GetName());
[10780]406
407    osg::Matrix localMatrix;
408    makeLocalMatrix(pNode, localMatrix);
409    bool bLocalMatrixIdentity = localMatrix.isIdentity();
410
411    osg::ref_ptr<osg::Group> osgGroup;
412
[11139]413    bool bEmpty = children.empty() && !bIsBone;
[10780]414
415    switch (lAttributeType)
416    {
417    case KFbxNodeAttribute::eUNIDENTIFIED:
[11109]418        if (bLocalMatrixIdentity && children.size() + skeletal.size() == 1)
[10780]419        {
420            if (children.size() == 1)
421            {
422                return osgDB::ReaderWriter::ReadResult(children.front().get());
423            }
424            else
425            {
426                return osgDB::ReaderWriter::ReadResult(skeletal.front().get());
427            }
428        }
429        break;
430    case KFbxNodeAttribute::eMESH:
431        {
[11141]432            size_t bindMatrixCount = boneBindMatrices.size();
[11139]433            osgDB::ReaderWriter::ReadResult meshRes = readFbxMesh(pSdkManager,
[11262]434                pNode, pAnimationManager, stateSetList, boneBindMatrices,
435                fbxSkeletons, skeletonMap);
[10780]436            if (meshRes.error())
437            {
438                return meshRes;
439            }
440            else if (osg::Node* node = meshRes.getNode())
441            {
[11141]442                bEmpty = false;
[11139]443
[11141]444                if (bindMatrixCount != boneBindMatrices.size())
445                {
446                    //The mesh is skinned therefore the bind matrix will handle all transformations.
447                    localMatrix.makeIdentity();
448                    bLocalMatrixIdentity = true;
449                }
[11139]450
[10780]451                if (animName.empty() &&
452                    children.empty() &&
453                    skeletal.empty() &&
454                    bLocalMatrixIdentity)
455                {
456                    return osgDB::ReaderWriter::ReadResult(node);
457                }
458
[11141]459                children.insert(children.begin(), node);
[10780]460            }
461        }
462        break;
463    case KFbxNodeAttribute::eCAMERA:
464    case KFbxNodeAttribute::eLIGHT:
465        {
466            osgDB::ReaderWriter::ReadResult res =
467                lAttributeType == KFbxNodeAttribute::eCAMERA ?
468                readFbxCamera(pNode) : readFbxLight(pNode, nLightCount);
469            if (res.error())
470            {
471                return res;
472            }
473            else if (osg::Group* resGroup = dynamic_cast<osg::Group*>(res.getObject()))
474            {
475                bEmpty = false;
476                if (animName.empty() &&
477                    bLocalMatrixIdentity)
478                {
479                    osgGroup = resGroup;
480                }
481                else
482                {
[11141]483                    children.insert(children.begin(), resGroup);
[10780]484                }
485            }
486        }
487        break;
488    }
489
490    if (bEmpty)
491    {
492        osgDB::ReaderWriter::ReadResult(0);
493    }
494
[11139]495    if (!osgGroup) osgGroup = createGroupNode(pSdkManager, pNode, animName, localMatrix, bIsBone, nodeMap);
496
[11141]497    osg::Group* pAddChildrenTo = osgGroup.get();
498    if (bCreateSkeleton)
499    {
[11262]500        osgAnimation::Skeleton* osgSkeleton = getSkeleton(pNode, fbxSkeletons, skeletonMap);
[11141]501        osgSkeleton->setDefaultUpdateCallback();
502        pAddChildrenTo->addChild(osgSkeleton);
503        pAddChildrenTo = osgSkeleton;
504    }
[11139]505
[10780]506    for (osg::NodeList::iterator it = skeletal.begin(); it != skeletal.end(); ++it)
507    {
[11139]508        pAddChildrenTo->addChild(it->get());
[10780]509    }
510    for (osg::NodeList::iterator it = children.begin(); it != children.end(); ++it)
511    {
[11139]512        pAddChildrenTo->addChild(it->get());
[10780]513    }
514
[11139]515
[10780]516    return osgDB::ReaderWriter::ReadResult(osgGroup.get());
517}
[11139]518
519osgAnimation::Skeleton* getSkeleton(KFbxNode* fbxNode,
[11262]520    const std::set<const KFbxNode*>& fbxSkeletons,
[11141]521    std::map<KFbxNode*, osgAnimation::Skeleton*>& skeletonMap)
[11139]522{
[11141]523    //Find the first non-skeleton ancestor of the node.
524    while (fbxNode &&
[11262]525        ((fbxNode->GetNodeAttribute() &&
526        fbxNode->GetNodeAttribute()->GetAttributeType() == KFbxNodeAttribute::eSKELETON) ||
527        fbxSkeletons.find(fbxNode) != fbxSkeletons.end()))
[11141]528    {
529        fbxNode = fbxNode->GetParent();
530    }
[11139]531
[11141]532    std::map<KFbxNode*, osgAnimation::Skeleton*>::const_iterator it = skeletonMap.find(fbxNode);
533    if (it == skeletonMap.end())
534    {
535        osgAnimation::Skeleton* skel = new osgAnimation::Skeleton;
536        skel->setDefaultUpdateCallback();
537        skeletonMap.insert(std::pair<KFbxNode*, osgAnimation::Skeleton*>(fbxNode, skel));
538        return skel;
539    }
540    else
541    {
542        return it->second;
543    }
[11139]544}
Note: See TracBrowser for help on using the browser.