Transformations/main.cxx

/**************************************************************************************

 Copyright (C) 2010 Autodesk, Inc. and/or its licensors.
 All Rights Reserved.

 The coded instructions, statements, computer programs, and/or related material 
 (collectively the "Data") in these files contain unpublished information 
 proprietary to Autodesk, Inc. and/or its licensors, which is protected by 
 Canada and United States of America federal copyright law and by international 
 treaties. 
 
 The Data may not be disclosed or distributed to third parties, in whole or in
 part, without the prior written consent of Autodesk, Inc. ("Autodesk").

 THE DATA IS PROVIDED "AS IS" AND WITHOUT WARRANTY.
 ALL WARRANTIES ARE EXPRESSLY EXCLUDED AND DISCLAIMED. AUTODESK MAKES NO
 WARRANTY OF ANY KIND WITH RESPECT TO THE DATA, EXPRESS, IMPLIED OR ARISING
 BY CUSTOM OR TRADE USAGE, AND DISCLAIMS ANY IMPLIED WARRANTIES OF TITLE, 
 NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE OR USE. 
 WITHOUT LIMITING THE FOREGOING, AUTODESK DOES NOT WARRANT THAT THE OPERATION
 OF THE DATA WILL BE UNINTERRUPTED OR ERROR FREE. 
 
 IN NO EVENT SHALL AUTODESK, ITS AFFILIATES, PARENT COMPANIES, LICENSORS
 OR SUPPLIERS ("AUTODESK GROUP") BE LIABLE FOR ANY LOSSES, DAMAGES OR EXPENSES
 OF ANY KIND (INCLUDING WITHOUT LIMITATION PUNITIVE OR MULTIPLE DAMAGES OR OTHER
 SPECIAL, DIRECT, INDIRECT, EXEMPLARY, INCIDENTAL, LOSS OF PROFITS, REVENUE
 OR DATA, COST OF COVER OR CONSEQUENTIAL LOSSES OR DAMAGES OF ANY KIND),
 HOWEVER CAUSED, AND REGARDLESS OF THE THEORY OF LIABILITY, WHETHER DERIVED
 FROM CONTRACT, TORT (INCLUDING, BUT NOT LIMITED TO, NEGLIGENCE), OR OTHERWISE,
 ARISING OUT OF OR RELATING TO THE DATA OR ITS USE OR ANY OTHER PERFORMANCE,
 WHETHER OR NOT AUTODESK HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSS
 OR DAMAGE. 

**************************************************************************************/

// Illustrates the followings:
// 1. How to use KFbxXMatrix in transformation calculation.
// 2. What EvaluateGlobalTransform() and EvaluateLocalTransform() actually do.
// 3. How to get global and local transform of each joint in a joint hierarchy.
//
// Steps:
//  1. Initialize FBX SDK Manager and FBX Scene.
//  2. Load the input file to scene.
//  3. Calculate global and local transform by EvaluateGlobalTransform() and EvaluateLocalTransform().
//  4. Alternative way to calculate global and local transform from scratch by node's properties.
//  5. Compare, the above two results should be the same. 
//  6. Display the joint's global and local transformation.
//  7. Destroy the FBX SDK Manager and FBX Scene.

#include <fbxsdk.h>
#include "../Common/Common.h"
#include "DisplayCommon.h"

const char * SAMPLE_FILENAME = "JointHierarchy.fbx";

KFbxXMatrix CalculateGlobalTransform(KFbxNode* pNode);
void CompareTransformations( KFbxNode* pNode, KFbxScene* pScene );

int main(int argc, char** argv)
{
    KFbxSdkManager* lSdkManager = NULL;
    KFbxScene* lScene = NULL;
    bool lResult;

    // Prepare the FBX SDK.
    InitializeSdkObjects(lSdkManager, lScene);    

    // Load the scene. 
    // The example can take a FBX file as an argument.
    const char* lFileName = "";
    if(argc <= 1)
    {
        lFileName = SAMPLE_FILENAME;
    }
    else
    {
        lFileName = argv[1];
    }

    printf("\n\nFile: %s\n\n", lFileName);
    lResult = LoadScene(lSdkManager, lScene, lFileName);

    if(lResult == false)
    {
        printf("\n\nAn error occurred while loading the scene...");
    }
    else
    {
        CompareTransformations( lScene->GetRootNode(), lScene );
    }

    // Destroy all objects created by the FBX SDK.
    DestroySdkObjects(lSdkManager);

    return 0;
}

void CompareTransformations(KFbxNode* pNode, KFbxScene * pScene)
{
    KFbxAnimEvaluator* lEvaluator = pScene->GetEvaluator();

    if( pNode != pScene->GetRootNode())
    {
        DisplayString(pNode->GetName());
        KFbxNode* lParentNode = pNode->GetParent();

        // The first way: calculate global and local transform by EvaluateGlobalTransform() and EvaluateLocalTransform().
        KFbxXMatrix lGlobal, lLocal;
        lGlobal= lEvaluator->GetNodeGlobalTransform( pNode);
        lLocal = lEvaluator->GetNodeLocalTransform(pNode);

        // The second way: calculate global and local transform from scratch by the node's properties.
        KFbxXMatrix lParentTransform,lLocalTransform, lGlobalTransform;
        lGlobalTransform = CalculateGlobalTransform(pNode);
        if(lParentNode)
        {
            // Get parent global transform.
            lParentTransform = CalculateGlobalTransform(lParentNode);
            // Calculate local transform according to: LocalTransform = ParentGlobalInverse * GlobalTransform.
            lLocalTransform = lParentTransform.Inverse() * lGlobalTransform;
        }
        else
            lLocalTransform = lGlobalTransform;

        // Compare, the results are the same. Display the global and local transformation of each joint.
        if(lGlobal == lGlobalTransform)
        {
            for(int i = 0; i<4; ++i)
            {
                KString lHeader("GlobalTransform Row_");
                KString lIndex(i);
                lHeader += lIndex;
                lHeader += ": ";

                Display4DVector(lHeader, lGlobal.GetRow(i));
            }            
            printf("\n");
        }
        else
        {
            printf("Error: The two global transform results are not equal!\n");
            for(int i = 0; i<4; ++i)
            {
                KString lHeader("KFbxEvaluatorGlobalTransform Row_");
                KString lIndex(i);
                lHeader += lIndex;
                lHeader += ": ";

                Display4DVector(lHeader, lGlobal.GetRow(i));
            }            
            printf("\n");

            for(int i = 0; i<4; ++i)
            {
                KString lHeader("FromScratchGlobalTransform Row_");
                KString lIndex(i);
                lHeader += lIndex;
                lHeader += ": ";

                Display4DVector(lHeader, lGlobalTransform.GetRow(i));
            }            
            printf("\n");
        }

        if(lLocal == lLocalTransform)
        {
            for(int i = 0; i<4; ++i)
            {
                KString lHeader("LocalTransform Row_");
                KString lIndex(i);
                lHeader += lIndex;
                lHeader += ": ";

                Display4DVector(lHeader, lLocal.GetRow(i));
            }            
            printf("\n");
        }
        else
        {
            printf("Error: The two local transform results are not equal!\n");
            for(int i = 0; i<4; ++i)
            {
                KString lHeader("KFbxEvaluatorLocalTransform Row_");
                KString lIndex(i);
                lHeader += lIndex;
                lHeader += ": ";

                Display4DVector(lHeader, lLocal.GetRow(i));
            }            
            printf("\n");

            for(int i = 0; i<4; ++i)
            {
                KString lHeader("FromScratchLocalTransform Row_");
                KString lIndex(i);
                lHeader += lIndex;
                lHeader += ": ";

                Display4DVector(lHeader, lLocalTransform.GetRow(i));
            }            
            printf("\n");
        }
    }

    int lChildCount = pNode->GetChildCount();
    for( int i = 0; i<lChildCount; i++)
    {
        CompareTransformations(pNode->GetChild(i), pScene);
    }
}

/*
Terminology:
Suffix "M" means this is a matrix, suffix "V" means it is a vector.
T is translation.
R is rotation.
S is scaling.
SH is shear.
GlobalRM(x) means the Global Rotation Matrix of node "x".
GlobalRM(P(x)) means the Global Rotation Matrix of the parent node of node "x".
All other transforms are described in the similar way.

The algorithm description:
To calculate global transform of a node x according to different InheritType, 
we need to calculate GlobalTM(x) and [GlobalRM(x) * (GlobalSHM(x) * GlobalSM(x))] separately.
GlobalM(x) = GlobalTM(x) * [GlobalRM(x) * (GlobalSHM(x) * GlobalSM(x))];

InhereitType = RrSs:
GlobalRM(x) * (GlobalSHM(x) * GlobalSM(x)) = GlobalRM(P(x)) * LocalRM(x) * [GlobalSHM(P(x)) * GlobalSM(P(x))] * LocalSM(x);

InhereitType = RSrs:
GlobalRM(x) * (GlobalSHM(x) * GlobalSM(x)) = GlobalRM(P(x)) * [GlobalSHM(P(x)) * GlobalSM(P(x))] * LocalRM(x) * LocalSM(x);

InhereitType = Rrs:
GlobalRM(x) * (GlobalSHM(x) * GlobalSM(x)) = GlobalRM(P(x)) * LocalRM(x) * LocalSM(x);

LocalM(x)= TM(x) * RoffsetM(x)  * RpivotM(x) * RpreM(x) * RM(x) * RpostM(x) * RpivotM(x)^-1 * SoffsetM(x) *SpivotM(x) * SM(x) * SpivotM(x)^-1
LocalTWithAllPivotAndOffsetInformationV(x) = Local(x).GetT();
GlobalTV(x) = GlobalM(P(x)) * LocalTWithAllPivotAndOffsetInformationV(x);

Notice: FBX SDK does not support shear yet, so all local transform won't have shear.
However, global transform might bring in shear by combine the global transform of node in higher hierarchy.
For example, if you scale the parent by a non-uniform scale and then rotate the child node, then a shear will
be generated on the child node's global transform.
In this case, we always compensates shear and store it in the scale matrix too according to following formula:
Shear*Scaling = RotationMatrix.Inverse * TranslationMatrix.Inverse * WholeTranformMatrix
*/
KFbxXMatrix CalculateGlobalTransform(KFbxNode* pNode) 
{
    KFbxXMatrix lTranlationM, lScalingM, lScalingPivotM, lScalingOffsetM, lRotationOffsetM, lRotationPivotM, \
                lPreRotationM, lRotationM, lPostRotationM, lTransform;

    KFbxXMatrix lParentGX, lGlobalT, lGlobalRS;

    // Construct translation matrix
    KFbxVector4 lTranslation = pNode->LclTranslation.Get();
    lTranlationM.SetT(lTranslation);

    // Construct rotation matrices
    KFbxVector4 lRotation = pNode->LclRotation.Get();
    KFbxVector4 lPreRotation = pNode->PreRotation.Get();
    KFbxVector4 lPostRotation = pNode->PostRotation.Get();
    lRotationM.SetR(lRotation);
    lPreRotationM.SetR(lPreRotation);
    lPostRotationM.SetR(lPostRotation);

    KFbxVector4 lPostRotationNew = pNode->GetPostRotation(KFbxNode::eSOURCE_SET);

    // Construct scaling matrix
    KFbxVector4 lScaling = pNode->LclScaling.Get();
    lScalingM.SetS(lScaling);

    // Construct offset and pivot matrices
    KFbxVector4 lScalingOffset = pNode->ScalingOffset.Get();
    KFbxVector4 lScalingPivot = pNode->ScalingPivot.Get();
    KFbxVector4 lRotationOffset = pNode->RotationOffset.Get();
    KFbxVector4 lRotationPivot = pNode->RotationPivot.Get();
    lScalingOffsetM.SetT(lScalingOffset);
    lScalingPivotM.SetT(lScalingPivot);
    lRotationOffsetM.SetT(lRotationOffset);
    lRotationPivotM.SetT(lRotationPivot);

    // Calculate the global transform matrix of the parent node
    KFbxNode* lParentNode = pNode->GetParent();
    if(lParentNode)
        lParentGX = CalculateGlobalTransform(lParentNode);

    //Construct Global Rotation
    KFbxXMatrix lLRM, lParentGRM;
    KFbxVector4 lParentGR = lParentGX.GetR();
    lParentGRM.SetR(lParentGR);
    lLRM = lPreRotationM * lRotationM * lPostRotationM;

    //Construct Global Shear*Scaling
    //FBX SDK does not support shear, to patch this, we use:
    //Shear*Scaling = RotationMatrix.Inverse * TranslationMatrix.Inverse * WholeTranformMatrix
    KFbxXMatrix lLSM, lParentGSM, lParentGRSM, lParentTM;
    KFbxVector4 lParentGT = lParentGX.GetT();
    lParentTM.SetT(lParentGT);
    lParentGRSM = lParentTM.Inverse() * lParentGX;
    lParentGSM = lParentGRM.Inverse() * lParentGRSM;
    lLSM = lScalingM;

    //Do not consider translation now
    ETransformInheritType lInheritType = pNode->InheritType.Get();
    if(lInheritType == eINHERIT_RrSs)
    {
        lGlobalRS = lParentGRM * lLRM * lParentGSM * lLSM;
    }
    else if(lInheritType == eINHERIT_RSrs)
    {
        lGlobalRS = lParentGRM * lParentGSM * lLRM * lLSM;
    }
    else if(lInheritType == eINHERIT_Rrs)
    {
        lGlobalRS = lParentGRM * lLRM * lLSM;
    }
    else
    {
        printf("error, unknown inherit type! \n");
    }

    // Construct translation matrix
    // Calculate the local transform matrix
    lTransform = lTranlationM * lRotationOffsetM * lRotationPivotM * lPreRotationM * lRotationM * lPostRotationM * lRotationPivotM.Inverse()\
        * lScalingOffsetM * lScalingPivotM * lScalingM * lScalingPivotM.Inverse();
    KFbxVector4 lLocalTWithAllPivotAndOffsetInfo = lTransform.GetT();
    // Calculate global translation vector according to: 
    // GlobalTranslation = ParentGlobalTransform * LocalTranslationWithPivotAndOffsetInfo
    KFbxVector4 lGlobalTranslation = lParentGX.MultT(lLocalTWithAllPivotAndOffsetInfo);
    lGlobalT.SetT(lGlobalTranslation);

    //Construct the whole global transform
    lTransform = lGlobalT * lGlobalRS;

    return lTransform;
}