Loading skinning information from FBX
Asked Answered
S

1

11

I'm trying to write a parser for FBX files to my custom model format that can be used in my game engine but I'm currently stuck on extracting the matrices needed for each bone. I think I might be lost in the theory of skinning as well...

I've managed to extract the bone hierarchy of a mesh and attach the weights and indexes of deformation-clusters to my vertices. Now I need to extract the (two?) matrices I need to do skinning in my engine.

Basically, in my format I would like it too look something like this:

//BoneID

//ParentBoneID

//Matrix1

//Matrix2

Now, to the matrices... Am I right to assume that at least one of them should be the "Bind pose-matrix", ie the default position of my skeleton? How do I extract this from FBX? Is this matrix uniform for the entire mesh?

As for the second one, I don't really know, I've tried looking at the ViewScene and ImportScene examples but I don't get it. I read something about "Local space matrix", I'm guessing this is the individual position, rotation and scale of each bone?

Any help or suggestions is appreciated, I'm at a loss right now, probably from staring myself blind at this. Almost at the point of abandoning FBX in favor of COLLADA.

Edit1: The engine doesn't have drawing capabilites yet as I wanted to get this done before moving on. I found an example that I think I understand, perhaps someone here can confirm if it's correct or not.

//TEST CODE
        //This lFbxLinkMatrix is the skeleton's transform when the binding happened. 
        //It is the same as the matrix in bindpose if the bindpose is complete.
        // Multiply lClusterGlobalInitPosition by Geometric Transformation
        FbxAMatrix clusterGlobalInitPosition;
        cluster->GetTransformLinkMatrix(clusterGlobalInitPosition);

        FbxAMatrix clusterGeometry = GetGeometry(cluster->GetLink());
        clusterGlobalInitPosition *= clusterGeometry;

        skeleton->at(boneListPosition).bindMatrix = clusterGlobalInitPosition;

        // Compute the shift of the link relative to the reference.
        //lVertexTransformMatrix = RGCP.Inverse() * CGCP * CGIP.Inverse() * RGIP * RG;
        // CGCP = position of bone
        // RGCP = mesh position
        FbxAMatrix offsetMatrix;
        FbxNode* boneNode = cluster->GetLink();

        FbxAMatrix CGIP, RGIP, vertexTransformMatrix;

        cluster->GetTransformLinkMatrix(CGIP);
        cluster->GetTransformMatrix(RGIP);

        vertexTransformMatrix = CGIP.Inverse() * RGIP;

        skeleton->at(boneListPosition).localBoneMatrix = vertexTransformMatrix;

So basically, when I want to calculate my animation, I get the inverse of mesh's world matrix, multiply it with the matrix representing my animation frame, multiplied with the inverse of my bind matrix, that bone's parent bind matrix and the final parent transform matrix?

Steelwork answered 26/11, 2012 at 14:13 Comment(0)
S
10

First of all, you need to know that the original vertex position of mesh is not in the world space. (For example: You will see your character stands up in the 3ds max, but it lies down if you exported the mesh data directly.)

The same thing also happens to all transformation nodes of skeleton tree. That means, even there is only one bone with identity matrix, the mesh also need to be transformed by the root node of FBX scene, then the local matrix of bone node. ( because the root node is also the parent node of the bone node ),

By the way, if you used the function FbxAxisSystem::ConvertScene() to convert the axis system of your mesh, that operation was only applied to the transformation matrix of root node too, not to the vertices of mesh.

To calcualte the position of vertices correctly, you need to find the FbxNode where the mesh belonged to, then call its EvaluateGlobalTransform() function to get the global transformation matrix, and use it to transform the position of mesh vertices. (Inversed transposed matrix for normal.)

I don't know what exactly you do in the function GetGeometry() of your code: FbxAMatrix clusterGeometry = GetGeometry(cluster->GetLink());

But this is my way to get the bind pose matrix of the bone: FbxAMatrix BoneBindPoseMatrix; pCluster->GetTransformLinkMatrix(BoneBindPoseMatrix).

To retrieve the node of bone, I also use FbxNode* pBoneNode = pCluster->GetLink(), and call pBoneNode->EvaluateLocalTransform( CurrentTime ) to get the local transformation matrix of the bone at current time. But there is a special thing I did: remember that I converted the mesh by the global transformation matrix of root node right? So for the root node of skeleton, I need to get its global transformation matrix by calling FbxAMatrix BoneInitPoseGlobalTransform = pBoneRootNode->EvaluateGlobalTransform(), then inverse it. And the local transform matrix of my root bone will be: BoneLocalTransform = InvBoneInitPoseGlobalTransform * BoneLocalTransform; We only need to do it for the root node of bone.

When animate the skin, I traverse the whole skeleton tree, for each bone node I calculate its global transformation matrix by: BoneGlobalTransform = BoneLocalTransform * ParentBoneGlobalTransform; But before I pass the matrix into the shader, I need to do: BlendMatrix = Bone.InvBindPoseMatrix * BoneGlobalTransform; Don't forget that I have already converted the postion of vertices by the root node.

That's all, the code above works well both under D3D and OpenGL. I hope it can help you. :D

Selene answered 22/1, 2013 at 3:5 Comment(5)
While this answer is very useful; it doesn't answer the question of extracting the raw transformation information. Calling EvaluateLocalTransform(currentTime) requires the FBX SDK, but the author is wondering if it's possible to retrieve the raw "skin" information so that they're able to write their own EvaluateLocalTransform function. Do you know if that's possible with the FBX SDK?Thorny
Sorry, I maybe misunderstood the question. Yep, you can calculate local transform yourself, but it's pretty complicated. At first, you need to retrieve all of the animation curves that bound with the properties of node listed below: LclTranslation, LclRotation, PreRotation, PostRotation, RotationPivot, RotationOffset, LclScaling, ScalingPivot, ScalingOffset.Selene
Then you need to turn them into matrices, and multiply them by the order: InvScalPivot * Scal * ScalPivot * ScalOffset * InvRotaPivot * PostRota * Rota * PreRota * RotaPivot * RotaOffset * Translation. You could search for class FbxProperty and FbxAnimCurve for more information.Selene
Well this answer helped me a lot ( searched for axis conversion ), fbx is really bad documented!! Its a pain to find the right methods for doing something.Godolphin
@Selene seems that you are describing Maya Style FBX matrices ( [Sp]x[S]x[Sh]x[Sp]x[St]x[Rp]x[Ro]x[R]x[Rp]x[Rt]x[T] ), but in other 3d modeling tools like 3DS Max, the World Transform is only (ParentWorldTransform * T * R * S * OT * OR * OS).Salient

© 2022 - 2024 — McMap. All rights reserved.