Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loading skinning information from FBX

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?

like image 328
Mr Dudeface Avatar asked Nov 26 '12 14:11

Mr Dudeface


1 Answers

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

like image 121
SeaStar Avatar answered Nov 04 '22 07:11

SeaStar