AREA forums upgrade
Read more about the planned upgrade of our forums
  • 1/3
You are here: Forum Home / Autodesk® FBX® / FBX SDK / Skinned animation rotated 90 degrees
IMPORTANT ANNOUNCEMENT ABOUT AREA FORUMS
  RSS 2.0 ATOM  

Skinned animation rotated 90 degrees
Rate this thread
 
66200
 
Permlink of this thread  
avatar
  • Total Posts: 7
  • Joined: 27 March 2012 10:42 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[0] Translation[0];
NewNode->Translation[1] Translation[1];
NewNode->Translation[2] Translation[2];

NewNode->Rotation[0] QuatRotation[0];
NewNode->Rotation[1] QuatRotation[1];
NewNode->Rotation[2] QuatRotation[2];
NewNode->Rotation[3] QuatRotation[3];

NewNode->Scale[0] Scale[0];
NewNode->Scale[1] Scale[1];
NewNode->Scale[2] Scale[2];

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[0] = (float)Translation[0];
Keyframe[1] = (float)Translation[1];
Keyframe[2] = (float)Translation[2];

Keyframe[3] = (float)QuatRotation[0];
Keyframe[4] = (float)QuatRotation[1];
Keyframe[5] = (float)QuatRotation[2];
Keyframe[6] = (float)QuatRotation[3];

Keyframe[7] = (float)Scale[0];
Keyframe[8] = (float)Scale[1];
Keyframe[9] = (float)Scale[2];

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(GeomTranslationGeomRotationGeomScaling);
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();

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

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

// Get the transformation for the bone
const MathsNS::Matrix4x4boneTransform pNodeInstance->GetTransform();

// Get the inverse transform for the original bone node
const MathsNS::Matrix4x4invBoneTransform CurrentMesh->m_BoneInvBindPoseMatrices[boneIndex];

// 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



Replies: 1
/userdata/avatar/vx3501hqr_small.jpg

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?

Author: Jiayang Xu

Replied: 17 April 2012 12:56 AM  
avatar

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



Replies: 0
avatar

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 0componentIndex 16; ++componentIndex)
{
 Keyframe[componentIndex] 
= (float)(((FbxDouble*)Transform)[componentIndex]);
}


Replies: 0
avatar

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.



Replies: 0