# Bone-animation | Offset-matrix

This topic is 1177 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Well met!

I'm still having trouble with bone-based animations ... with the Offset-matrix to be exact. Let me briefly break down what I am doing:

- Export/Import the mesh and the bones in bindpos (vertices "as-they-are", untransformed by bones)

(bones as transformation-matrices in their parents coord-system, the "local matrix")

- Compute "inverse bindpos matrices", beeing the inverse of each bones "local matrix" in bindposition

- For each frame, animate the current "local matrix"  of each bone, still in their parents coord-system

- For each bone, compute a "final matrix" with

D3DXMATRIX CalcCombinedMatrix( int jointID )
{
if(joints[jointID].parentID == -1){
return joints[jointID].localTransMatrix * joints[jointID].inverseBindposMatrix;
}else{
return joints[jointID].localTransMatrix * joints[jointID].inverseBindposMatrix *
CalcCombinedMatrix(joints[jointID].parentID);
}
}

What I get is what I expect: The model is overall right, but all the rotations are happening around the model-center, instead of each bones parent.

What I need, according to thisthis and this, is an "offset-matrix" I multiply to every bone (after my CalcCombinedMatrix), to move the bone transformations into their parents space, instead of the models center.

But I can not seem to get that matrix right. I am somehow supposed to use the center of the model for computations, and somewhat multiply every bones local matrix with it. I can't seem to get it working thought.

Could you try to make it clear to me how the offset-matrix is supposed to be computed? I can't really seem to grasp/visualize how I have to put the matrix I need together.

Thank you very much!

-gnomgrol

Edited by gnomgrol

##### Share on other sites

One of the links you posted provides a method, and two equations describing how to calculate the offset matrix. See the section titled Back into Initialization - The Offset Matrix

I.e., one of the equations -

offsetMatrix = MeshFrame.ToRoot * Inverse( bone.ToParent * parent.ToParent * ... * root.ToParent );

Each bone frame has a matrix transforming that bone into it's parent's space (called in that article the ToParent matrix). That sounds like your bone "local matrix." Combine each bone's ToParent matrix with it's parent's ToParent recursively done to the root. Take the inverse of that matrix to form the offset matrix.

Note: that method also accounts for the transform for the mesh vertices (MeshFrame.ToRoot) to transform the vertices from world space into root frame space, if required. Be sure to check whether the mesh data that you import has vertices in world space, or in root frame space. IF, and only IF, the root frame is at the world origin, then MeshFrame.ToRoot can be omitted (or set to the Identity matrix.)

Compute "inverse bindpos matrices", beeing the inverse of each bones "local matrix" in bindposition

It's not clear what you mean by in bindposition. If, by in bindposition, you mean each bone's local matrix combined with it's parent's "local matrix" combined with the parent's parent's "local matrix," etc., then it appears your "inverse bindpos matrix" is the offset matrix. Compare how you calculate your "inverse bindpos matrix" with the method for calculating the "offset matrix" posted above.

If your inverse bindpos matrix is just the inverse of the bone's local matrix (not combined with its parent's local matrix), that's incorrect. The local matrices (from bone to parent to parent..to root) must be combined before you take the inverse.

Also, in that same article, the Final Matrix is calculated as:

FinalMatrix[ boneIndex ] = OffsetMatrix[ boneIndex ] * boneFrame.TransformationMatrix;

where boneFrame.TransformationMatrix is the combined animation transforms (bone.localAnimMat * parent.localAnimMat * parent's-parent.localAnimMat..)

It appears that's what you're trying to do with your CalcCombinedMatrix function, but, as noted above and below, your code is incorrect.

Note the order of the matrix multiplication of offset * TransformationMatrix.The order is important. It appears you have the order reversed in your posted code.

Edited by Buckeye

##### Share on other sites

My "inverse bindpos matrix" is needed to make the "local Matrix" be the delta of the current animation state and the one the skeleton was exported in. You could just render the mesh without the bones. The bindposition is just the untransformed mesh and the transformation of the bones in that state.

Let me explain my model-format on the example of a human model:

The bindposition is the position the model "rests" when not animated - legs a little open, arms to the sides (I'm sure you have seen those before).

The vertices I export are those, in modelspace. The origin(0/0/0) of the modelspace would be between the models feet.

I also export the skeleton in this position (each bones "local Matrix"). Inversing those gives me my "inverse bindpos matrices".

Therefore, each of my local matrices has to be multiplyed by its inverse bindpos matrix first, to get the delta transformation to the bindposition,

If I understand you correctly, what the article refers to as the "ToParent Matrix" is the same as my "local Matrix". But should it not be the inverse, since it is suppsed to transform from the bone back to the parents space?

Also, I am not sure what the "MeshFrame.ToRoot Matrix" is supposed to be. Lets assume my root bone is between the shoulders, is it supposed to transform the mesh so that the spot the root-bone is in ends up in the origin (of modelspace)? If so, it would just be the inverse of the root-bones "local Matrix", right?

Besides all that, I have done everything you metioned in your answer (using my not inverted local Matrix as ToParent Matrix).

The only thing I am missing is the (right?) "MeshFrame.ToRoot Matrix" to use in the offset-matrix.

When I use the identity-matrix for that, the rotations appear to be happening around the somewhat right points, but the transformations are messed up. Is that what you'd expect?

Thanks for your time, have nice day!

-gnomgrol

##### Share on other sites

My "inverse bindpos matrix" is needed to make the "local Matrix" be the delta of the current animation state and the one the skeleton was exported in. You could just render the mesh without the bones. The bindposition is just the untransformed mesh and the transformation of the bones in that state.

Let me explain my model-format on the example of a human model:

The bindposition is the position the model "rests" when not animated - legs a little open, arms to the sides (I'm sure you have seen those before).

The vertices I export are those, in modelspace. The origin(0/0/0) of the modelspace would be between the models feet.

I also export the skeleton in this position (each bones "local Matrix"). Inversing those gives me my "inverse bindpos matrices".

Therefore, each of my local matrices has to be multiplyed by its inverse bindpos matrix first, to get the delta transformation to the bindposition,

You are right here, the inverse bind position tranforms the mesh vertices from the T-pose (rest pose) to the bone local space, then the final bone matrix transformes the vertex from local bone space to (animation) object space. The steps to do it should look like this:

// 1. calculate the local space bone matrix for each bone
local_matrix_i = interpolate(frame_j,frame_j+1);
//2. calculate the final space bone matrix by considering the final space bone matrix of its parent
final_matrix_i = final_matrix_(parent_i) * local_matrix_i;
//3. apply inverse bone matrix, so that each vertex will transform like this
//  rest_pose_object_space  ==inv_bone_matrix==>  bone_local_space  ==final_bone_matrix==>  final object space
final_render_matrix_i = final_matrix_i * inv_bone_matrix_i;


Edited by Ashaman73

##### Share on other sites
As an additional tip, if you are using assimp to import/export your model then it already calculated offset matrices for you and stored them in mBones[]

##### Share on other sites

Are you sure that I have to apply the inverse bindpos matrix after I multiplied all the parents matrices together, instead of to the local matrix right in the beginning? If so, do I have to use the inverse of the local matrix or of the combined Matrix?

I am using my own export-scripts.

The second half of my questions still apply.

Edited by gnomgrol

##### Share on other sites

Don't know if you're still having problems, but one thing you said leads me to believe you may not be interpreting things correctly. Specifically, I'm curious if you're confusing what a ToParent matrix is, and how a ToParent matrix is applied.

what the article refers to as the "ToParent Matrix" is the same as my "local Matrix". But should it not be the inverse, since it is supp[o]sed to transform from the bone back to the parents space?

When you say "should it not be the inverse..?" what you refer to as it, isn't clear. However, if you're referring to the ToParent matrix, the answer is "no." If you're referring to your "local matrix," and it is the same as the ToParent matrix, then the answer is still "no."

That is, the ToParent matrix transforms from bone space to the parent's space. I.e., that's why it's termed "to parent."

An example of a ToParent matrx:

1. Assume that the root frame is the origin of the skeletal hierarchy.

2. Assume that the root frame has a child frame Hip, located 6 units in the +Y direction from the root frame.

The Hip frame's ToParent matrix is, in row-major order:

1 0 0 0
0 1 0 0
0 0 1 0
0 6 0 0


That is a matrix with no rotation, and a translation of +6 along the Y axis.

Assume a mesh vertex is positioned in space coincident with the origin of the Hip frame. By definition, origin is ( 0, 0, 0 ) in the Hip reference frame. To determine that vertex position in the root reference frame, transform the vertex position as defined in Hip space by the Hip's ToParent matrix to transform the vertex from Hip space to root space. I.e.,

( 0, 0, 0) * Hip-ToParent-matrix = ( 0, 6, 0)

As mentioned, a vertex positioned at the origin of the Hip frame (that frame already specified to be located at +6 along the Y axis), is also positioned at +6 along the Y axis in the root reference frame.

Now consider a vertex positioned at ( 0, 6, 0) in the root reference frame. To correctly apply rotations in the Hip reference frame, the vertex must be transformed from root frame space to Hip reference frame space. See the section of the article titled "A Slight Diversion from the Initialization Process." To transform from root frame to Hip frame, a ToChild matrix is needed. That matrix can be calculated as the inverse of the ToParent matrix.

The inverse of the Hip's ToParent matrix is:

1  0 0 0
0  1 0 0
0  0 1 0
0 -6 0 1


So, root-frame-position * ToChild-matrix = Hip-frame-position

I.e.,  ( 0, 6, 0) * ToChild-matrix = ( 0, 0, 0)

And, indeed, the vertex at ( 0, 6, 0) in the root reference frame is located at ( 0, 0, 0) in the Hip reference frame.

##### Share on other sites

Thank you so much! I just managed to get it working!!!

First of, I had all the matrix stuff right. Despite me not really understand it (I do now, I think), it was correct. I apologise for waisting your time.

Your foolproof explaination helped, because you mentioned that

 anim-pose-vertex = model-space-vertex-position * offset-matrix * anim-pose-matrix

The problem was the follwing line in the shader that does the final skinning:

Output.Pos += mul( Pos, boneMatrices[boneID] );

Notice the "+=". I'm about to kill myself.

Literally weeks waisted because I forgot to check that.

Again, thanks so much. Without you mentioning what I was actually trying to compute I'd have waisted who-knows-how-much more time.

Thank you.

Edited by gnomgrol

##### Share on other sites

First: thanks for posting the exact resolution to your problem. [ thumbs up ] Not enough people do that.

I apologise for waisting your time.

Not a problem. Skinned mesh animation is one of the more difficult concepts to understand and implement, and your responses indicated that you really wanted to find the problem.