Search code examples
copengltransformationmeshfbx

Mesh aren't displayed in their correct positions for a FBX model of multiple meshes


I have imported an FBX model that composes of multiple meshes. Unfortunately, I am not able to display each mesh in their correct positions. For each mesh I multiply a geometric transformation of the mesh with a local transformation of the mesh and then pass it to shader. How can I fix this problem?

OpenGL Shader

gl_Position = modelViewProjectionMatrix *TransformationMatrix*vertexPositionsOfMesh;

Creating Transformation Matrix

GLKMatrix4 LcLTransformation = createTransformationMatrix(
   Mesh->LclRotation,
   Mesh->LclScaling,
   Mesh->LclTranslation);
GLKMatrix4 GeoTransformation = createTransformationMatrix(
   Mesh->GeometricRotation,
   Mesh->GeometricScaling,
   Mesh->GeometricTranslation);
TransformationMatrix=GLKMatrix4Transpose(GLKMatrix4Multiply(LcLTransformation,
                                                            GeoTransformation));

createTransformationMatrix

GLKMatrix4 createTransformationMatrix(float* _rotation, float* _scaling, float* _translation)
{
  GLKMatrix4 Rx = GLKMatrix4Make(1, 0,                 0,                  0,
                                 0, cos(_rotation[0]), -sin(_rotation[0]), 0,
                                 0, sin(_rotation[0]), cos(_rotation[0]),  0,
                                 0, 0,                 0,                  1
                                 );

  GLKMatrix4 Ry = GLKMatrix4Make(cos(_rotation[1]),  0, sin(_rotation[1]), 0,
                                 0,                  1, 0,                 0,
                                 -sin(_rotation[1]), 0, cos(_rotation[1]), 0,
                                 0,                  0, 0,                 1
                                 );

  GLKMatrix4 Rz = GLKMatrix4Make(cos(_rotation[2]), -sin(_rotation[2]), 0, 0,
                                 sin(_rotation[2]), cos(_rotation[2]),  0, 0,
                                 0,                 0,                  1, 0,
                                 0,                 0,                  0, 1
                                 );

  GLKMatrix4 Translation = GLKMatrix4Make(1, 0, 0, _translation[0],
                                          0, 1, 0, _translation[1],
                                          0, 0, 1, _translation[2],
                                          0, 0, 0, 1
                                          );
  GLKMatrix4 Scaling = GLKMatrix4Identity;

  Scaling.m00 = _scaling[0];
  Scaling.m11 = _scaling[1];
  Scaling.m22 = _scaling[2];

  GLKMatrix4 Rotation = GLKMatrix4Multiply(GLKMatrix4Multiply(Rx, Ry), Rz);
  Transformation = GLKMatrix4Multiply(Scaling, GLKMatrix4Multiply(Rotation, Translation));
  return Transformation;
}

Solution

  • According to the Transformations example code in Autodesk's official SDK, a node's global position in the world space is recursively calculated by the CalculateGlobalTransform(FbxNode* pNode) function as in the sample code below. Very important things to notice are this function takes into account not just pre and post rotations, but also pivot location and offsets. Also depending on the node's transformation inheritance type, the transformation formula changes.

    In case you import the model from 3ds Max, after calculating a node's global transformation information, you still have to multiply it with geometric transformations in order to find the position of node attribute in global coordinates.

    /*
        Copyright (C) 2013 Autodesk, Inc.
        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
    */
    
    FbxAMatrix CalculateGlobalTransform(FbxNode* pNode)
    {
        FbxAMatrix lTranlationM, lScalingM, lScalingPivotM, lScalingOffsetM, lRotationOffsetM, lRotationPivotM, \
                    lPreRotationM, lRotationM, lPostRotationM, lTransform;
    
        FbxAMatrix lParentGX, lGlobalT, lGlobalRS;
    
        if(!pNode)
        {
            lTransform.SetIdentity();
            return lTransform;
        }
    
        // Construct translation matrix
        FbxVector4 lTranslation = pNode->LclTranslation.Get();
        lTranlationM.SetT(lTranslation);
    
        // Construct rotation matrices
        FbxVector4 lRotation = pNode->LclRotation.Get();
        FbxVector4 lPreRotation = pNode->PreRotation.Get();
        FbxVector4 lPostRotation = pNode->PostRotation.Get();
        lRotationM.SetR(lRotation);
        lPreRotationM.SetR(lPreRotation);
        lPostRotationM.SetR(lPostRotation);
    
        // Construct scaling matrix
        FbxVector4 lScaling = pNode->LclScaling.Get();
        lScalingM.SetS(lScaling);
    
        // Construct offset and pivot matrices
        FbxVector4 lScalingOffset = pNode->ScalingOffset.Get();
        FbxVector4 lScalingPivot = pNode->ScalingPivot.Get();
        FbxVector4 lRotationOffset = pNode->RotationOffset.Get();
        FbxVector4 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
        FbxNode* lParentNode = pNode->GetParent();
        if(lParentNode)
        {
            lParentGX = CalculateGlobalTransform(lParentNode);
        }
        else
        {
            lParentGX.SetIdentity();
        }
    
        //Construct Global Rotation
        FbxAMatrix lLRM, lParentGRM;
        FbxVector4 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
        FbxAMatrix lLSM, lParentGSM, lParentGRSM, lParentTM;
        FbxVector4 lParentGT = lParentGX.GetT();
        lParentTM.SetT(lParentGT);
        lParentGRSM = lParentTM.Inverse() * lParentGX;
        lParentGSM = lParentGRM.Inverse() * lParentGRSM;
        lLSM = lScalingM;
    
        //Do not consider translation now
        FbxTransform::EInheritType lInheritType = pNode->InheritType.Get();
        if(lInheritType == FbxTransform::eInheritRrSs)
        {
            lGlobalRS = lParentGRM * lLRM * lParentGSM * lLSM;
        }
        else if(lInheritType == FbxTransform::eInheritRSrs)
        {
            lGlobalRS = lParentGRM * lParentGSM * lLRM * lLSM;
        }
        else if(lInheritType == FbxTransform::eInheritRrs)
        {
            FbxAMatrix lParentLSM;
            FbxVector4 lParentLS = lParentNode->LclScaling.Get();
            lParentLSM.SetS(lParentLS);
    
            FbxAMatrix lParentGSM_noLocal = lParentGSM * lParentLSM.Inverse();
            lGlobalRS = lParentGRM * lLRM * lParentGSM_noLocal * lLSM;
        }
        else
        {
            FBXSDK_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();
        FbxVector4 lLocalTWithAllPivotAndOffsetInfo = lTransform.GetT();
        // Calculate global translation vector according to:
        // GlobalTranslation = ParentGlobalTransform * LocalTranslationWithPivotAndOffsetInfo
        FbxVector4 lGlobalTranslation = lParentGX.MultT(lLocalTWithAllPivotAndOffsetInfo);
        lGlobalT.SetT(lGlobalTranslation);
    
        //Construct the whole global transform
        lTransform = lGlobalT * lGlobalRS;
    
        return lTransform;
    }