Area :: Header
Sweat, Swearing & Tears
Meet the team behind Cupcakes & Critters in the latest chapter of Autodesk’s documentary series on indie game makers.
Discussion Groups

FBX SDK

Reply
Active Member
GoGo-Robot
Posts: 7
Registered: ‎03-27-2012

Skinned animation rotated 90 degrees

181 Views, 4 Replies
04-17-2012 03:50 AM
We're having some problems getting our skinned mesh animation to work correctly when converting an FBX to our own format. Whenever animation is applied, the model seems to rotate 90 degrees about the X axis. I'm sure it has to be something to do with either the PreRotation or the coordinate system conversion, but I'm not sure exactly what. The steps we go through are:

The FBX is saved out with Y-Up from 3DS Max. It contains a mesh which is skinned using CAT.

Each node has its original transformation stored as Translation, Rotation Quaternion and Scale, using the following code:

KTime kTime;
kTime.SetSecondDouble(0);
FbxAMatrix Transform = Node->EvaluateLocalTransform(kTime);
FbxVector4 Translation = Transform.GetT();
FbxQuaternion QuatRotation = Transform.GetQ();
FbxVector4 Angles = Transform.GetR();
FbxVector4 Scale = Transform.GetS();
NewNode->Translation = Translation;
NewNode->Translation = Translation;
NewNode->Translation = Translation;

NewNode->Rotation = QuatRotation;
NewNode->Rotation = QuatRotation;
NewNode->Rotation = QuatRotation;
NewNode->Rotation = QuatRotation;

NewNode->Scale = Scale;
NewNode->Scale = Scale;
NewNode->Scale = Scale;


So the transformations for each node are stored relative to the parent.

When evaluating the transformations for each node for the animations, we do the following:

// Evaluate the node at the given time
KTime kTime;
kTime.SetSecondDouble(Time);
FbxAMatrix Transform = m_pFBXNode->EvaluateLocalTransform(kTime);
FbxVector4 Translation = Transform.GetT();
FbxQuaternion QuatRotation = Transform.GetQ();
FbxVector4 Scale = Transform.GetS();

// Make the values relative to time of 0
kTime.SetSecondDouble(0);
FbxAMatrix Frame0Transform = m_pFBXNode->EvaluateLocalTransform(kTime);
FbxVector4 Frame0Translation = Frame0Transform.GetT();
FbxQuaternion Frame0QuatRotation = Frame0Transform.GetQ();
FbxVector4 Frame0Scale = Frame0Transform.GetS();

FbxAMatrix InverseFrame0Transform = Frame0Transform.Inverse();

FbxAMatrix RelativeTransform = InverseFrame0Transform * Transform;
Translation = RelativeTransform.GetT();
QuatRotation = RelativeTransform.GetQ();
Scale = Transform.GetS() - Frame0Scale;

Keyframe = (float)Translation;
Keyframe = (float)Translation;
Keyframe = (float)Translation;

Keyframe = (float)QuatRotation;
Keyframe = (float)QuatRotation;
Keyframe = (float)QuatRotation;
Keyframe = (float)QuatRotation;

Keyframe = (float)Scale;
Keyframe = (float)Scale;
Keyframe = (float)Scale;


This gets us the relative transformation from the frame 0 one. We do this so we can use additive blending on multiple animations.

We also store the inverse bind pose matrices for the bones as follows:


// Store the inverse bind pose matrix
FbxAMatrix globalMatrix;
Cluster->GetTransformMatrix(globalMatrix);

const FbxVector4 GeomTranslation = Mesh->GetNode()->GetGeometricTranslation(FbxNode::eSourcePivot);
const FbxVector4 GeomRotation = Mesh->GetNode()->GetGeometricRotation(FbxNode::eSourcePivot);
const FbxVector4 GeomScaling = Mesh->GetNode()->GetGeometricScaling(FbxNode::eSourcePivot);
FbxAMatrix geomMatrix = FbxAMatrix(GeomTranslation, GeomRotation, GeomScaling);
globalMatrix *= geomMatrix;

FbxAMatrix bindPoseMatrix;
Cluster->GetTransformLinkMatrix(bindPoseMatrix);
bindPoseMatrix = bindPoseMatrix.Inverse() * globalMatrix;

// We need to transpose the matrix to get it into engine format
FbxMatrix tempMatrix(bindPoseMatrix);
FbxMatrix transposedBindPoseMatrix = tempMatrix.Transpose();

double* bindPoseMatrixData = (double*)transposedBindPoseMatrix;
array<float>^ invBindPoseMatrix = gcnew array<float>(16);
for(int arrayIndex = 0; arrayIndex < 16; ++arrayIndex)
{
invBindPoseMatrix = (float)bindPoseMatrixData;
}
NewBoneData->SetInvBindPoseMatrix(invBindPoseMatrix);


Then when we are rendering, we calculate the skinning matrices like so:

// Get the transformation for the bone
const MathsNS::Matrix4x4& boneTransform = pNodeInstance->GetTransform();

// Get the inverse transform for the original bone node
const MathsNS::Matrix4x4& invBoneTransform = CurrentMesh->m_BoneInvBindPoseMatrices;

// Set the shader parameter for this bone
boneMatrices.push_back(boneTransform * invBoneTransform);


Like I mentioned, the whole thing seems to work fine, and animates OK, but the mesh is rotated 90 degrees on the X axis. If we replace the last line with:


boneMatrices.push_back(Matrix4x4::Identity);


The mesh faces the correct way, so my thinking is that the inverse bind pose matrices and everything are OK, but the animation transformations or maybe the default pose transformations are not retrieved properly. When rendering, all mesh / bone node transformations are calculated by constructing the matrix from the transforms retrieved in the first code segment and multiplying by the relative transformation matrices constructed from the data in the second code segment.

Does anyone have any idea what we're doing wrong, or why we might be seeing this 90 degree rotation? I thought using EvaluateLocalTransform was supposed to take all of the Pre/Post rotation stuff into consideration. Maybe there's a step we've missed or something?

Thanks
Please use plain text.
Distinguished Contributor
jiayang.xu
Posts: 504
Registered: ‎04-21-2008

Re: Skinned animation rotated 90 degrees

04-17-2012 04:56 AM in reply to: GoGo-Robot
There are two things looks suspicious:
1. "The FBX is saved out with Y-Up from 3DS Max." The default setting of 3DS Max is Z-up, so save it out with Y-up will introduce a 90 degree rotation on X axis, does that one considered properly?
2. "all mesh / bone node transformations are calculated by constructing the matrix from the transforms retrieved in the first code segment". Here the code calls EvaluateLocalTransform, then extract TRSQ from the matrix, and then combine them back to a matrix later when necessary. This matrix extraction and combination process looks vulnerable. Can you use the matrix from EvaluateLocalTransform directly?
Please use plain text.
Active Member
GoGo-Robot
Posts: 7
Registered: ‎03-27-2012

Re: Skinned animation rotated 90 degrees

04-17-2012 06:21 AM in reply to: GoGo-Robot
I thought the FBX SDK was supposed to take the whole Y-Up / Z-Up conversion into account when using EvaluateLocalTransform. Does it not do this? It does seem to rotate everything when we export using Z-Up

I'll try using the matrix directly, but that will take a bit of time to modify the pipeline. If anyone has any other ideas, please let me know.

Cheers
Please use plain text.
Active Member
GoGo-Robot
Posts: 7
Registered: ‎03-27-2012

Re: Skinned animation rotated 90 degrees

04-17-2012 08:01 AM in reply to: GoGo-Robot
I've tried exporting the matrix directly, and it still applies the 90 degree rotation, so I don't think it's that. The code is now:


// Evaluate the node at the given time
KTime kTime;
kTime.SetSecondDouble(Time);
FbxAMatrix Transform = m_pFBXNode->EvaluateLocalTransform(kTime);

for(int componentIndex = 0; componentIndex < 16; ++componentIndex)
{
Keyframe = (float)(((FbxDouble*)Transform));
}
Please use plain text.
Active Member
GoGo-Robot
Posts: 7
Registered: ‎03-27-2012

Re: Skinned animation rotated 90 degrees

04-17-2012 12:38 PM in reply to: GoGo-Robot
I think I've found the problem. The engine was multiplying by the nodes transformation matrix as well as the skinning matrices. We needed to ignore the transformations on the mesh nodes that were skinned.

Thanks for your help.
Please use plain text.