Bones Animation - Matrices and calculations.

Started by
6 comments, last by RobM 11 years, 11 months ago
Bones Animation - Matrices and calculations.

We are 'on final' when it comes to finishing the project, but just before implementing the animation system.
Our Client decided to choose "Bones Animation" - which is that i should export each Bone Transformation Matrix ( Quaternion for rotation, Vector3 for translation ) for every frame.
Objects in our game are to be animated with 3DS Max Physique Modifier, so we will have bones / weighting data per vertex.

I would like to split this post into 2 points, where :

1 - Exporting bones matrices for every frame - treats about the correct method of exporting the bones positions for animation
2 - Calculating final vertex position - treats about proper matrices operations to calculate new vertex position according to bone transformation in frame X.




1. EXPORTING BONES MATRICES FOR EVERY FRAME

Now do i understand correctly that while exporting the animated object i should:


1.
Grab BONE transformation matrix at frame 0
Invert this matrix

2.
Grab BONE transformation matrix at FRAMEx

3.
Multiply 1 * 2 to get the transformation offset of the BONE at FRAMEx
[/quote]



[pseudocode]
// Animation export

// For each frame, export bone transformation offset
for(int iFrame = 0; iFrame < vFrames.size(); iFrame++)
{
// For every bone in the object
for(int iBone = 0; iBone < vBones.size(); iBone++)
{

// Grab transformation matrix for this bone at frame 0 and inverse it
Matrix3 matBoneMatrixAtStart = pNode->GetObjectTMAfterWSM( 0 );
matBoneMatrixAtStart.Inverse();

// Grab transformation matrix for this bone at frame iFrame
Matrix3 matBoneMatrixAtCurrentFrame = pNode->GetObjectTMAfterWSM( iFrame );

// Multiply Inversed Transformation Matrix of this bone at frame 0 - with current frame transformation matrix
Matrix3 matBoneTransformationOffset = matBoneMatrixAtStart * matBoneMatrixAtCurrentFrame ;

// Save matBoneTransformationOffset - vertex will be multiplied by this matrix for animation purposes
fwrite(.....)
}

}

[/pseudocode]


Will that be enough ? Or there is something i am missing here?
What results should i expect after exporting this?





2. CALCULATING NEW VERTICES POSITIONS ( FINAL VERTEX POSITION AT FRAME X)


Later on, when rendering, object vertices are going to be multiplied by exported bone transformation matrix for actual animation frame, and then multiplied by
this whole model transformation matrix to place the object in correct position inside the level :



[pseudocode]

Update()
{
// The model transformation matrix describing the position of the model in the level
matModelTransformationMatrix


// Calculate new vertex position according to it's bone transformation offset
NewVertexPosition = ( OriginalVertexPosition * matBoneTransformationOffset[iFrame] ) * matModelTransformationMatrix;

// Increment the frame for testing purposes
iFrame++;
}

[/pseudocode]



Am i thinking correct here?

So,
Having bone transformation offset for frame X,
multiplying every vertex affected by this bone by this offset
should result in a vertex transformed exactly as this bone right?

Thanks for all your help.
perfection.is.the.key
Advertisement
Any chance for a bit of help please?
perfection.is.the.key

Any chance for a bit of help please?


What are you using to export the scene from 3ds max? If you're using something like COLLADA, the inverse bind matrices for each joint are exported for you.

The inverse bind matrices are what move vertices back to the space in which the mesh was bound to the skeleton (after they have been rotated). You'll need to import the following for your 'engine':

Bind pose matrix - this is the matrix which moves the model to it's bind pose - during skinning, each vertex needs to be transformed by this.

Skeleton containing Local bind pose matrix for each bone - each bone has a local bind pose matrix which, when multiplied down the skeleton hierarchy, puts your skeleton in its bind pose.

Inverse Bind Pose Matrix - each bone has an inverse bind pose matrix which takes the vertices back into the space in which the mesh was bound to the skeleton.

Any animation matrices per joint - for an animation 'frame' or 'keyframe', you'll have a matrix per joint. These are, like the local bind pose matrices, multiplied together down the skeleton hierarchy to create a 'pose'.

Once you have all these, the calculation to each vertex is as follows:
[source code='c++']
class Skeleton
{
vector<Matrix> desiredPose; // contains an animation matrix for each bone in the skeleton for the desired pose/keyframe - each bone has a numeric Id which acts as the index for this vector
};

void Skeleton::CalculatePose(Bone* currentBone)
{
if (currentBone->HasParent == false)
{
// this is the root bone so just set the world matrix to the matrix from the pose set
currentBone->worldMatrix = desiredPose[currentBone->Id];
MatrixMultiply(currentBone->skinningMatrix, currentBone->inverseBindMatrix, desiredPose[currentBone->Id]);
}
else
{
MatrixMultiply(currentBone->worldMatrix, desiredPose[currentBone->Id], currentBone->parentBone->worldMatrix);
MatrixMultiply(currentBone->skinningMatrix, currentBone->inverseBindMatrix, currentBone->worldMatrix);
}

for(int i = 0; i < currentBone->childBones.size(); i++)
{
CalculatePose(currentBone->childBones);
}
}
[/source]

Then to calculate the vertices, you do the following calculation (be it in the shader or on the CPU):

[source code='c++']
float4 vertexPos(0.0f, 0.0f, 0.0f, 0.0f);

for (int i = 0; i < bonesPerVertex; i++)
{
vertexPos += vertexBoneWeights * mul(float4(vertexIn, 1.0f), skinningMatrix[vertexBoneIndices]);
}
[/source]

This is assuming (in the shader) you have passed in all your skinningMatrices and your vertex declaration includes bone indices and bone weights. You can save a bit of bandwidth by just storing and passing one less weight and using (for the last bone that affects this vertex) 1- sum(previous weights).

It's also assuming you have pre-transformed your vertices by the BindPoseMatrix - this step is key and can be done in the shader if required but you'll need to pass another matrix in.

That's it...
Rob, sorry for a late reply - i was off at client site to finally decide about the animation format.


What are you using to export the scene from 3ds max? If you're using something like COLLADA, the inverse bind matrices for each joint are exported for you.
[/quote]

We were asked to code our own level format so we have created our own exporter, everything gets exported well - mesh, textures, normals, blah blah blah, but we're stuck with animation as there is something we just can't understand.

Ok, we don't go into any 'hierarchy' as we don't need ragdoll or inverse-kinematic animations, what we need is bone animation system.
And it might be that we are doing something wrong, but let's go over again and try to spot if there is something wrong in my understanding.


What we do is
- we have a "Player" object somewhere on the MAX scene

- we have our "Player" mesh vertices exporter in 'local space'
- then we export "Player" transformation matrix - which is the position and rotation and scale of "Player" in the 'world space'
vertices are multiplied by this matrix in the vertex shader when object gets rendered[/quote]


So far this is correct, as player gets rendered properly in the right place with the right scale and rotation.

At that point we started implementing animation export, which theoretically should look like:

- Grab a list of bones this "Player" object has
- Iterate through that list of bones and calculate weighting / blending data[/quote]

Ok, we have it.



Now here comes the tricky part, having all that information :

we need to export Bones positions/rotations for every animation frame for "Player" object [/quote]

Now is this correct understanding ? Do we 'get' it ?
Let's see,


- I have Vertex1 and it's position ( CVector3 )
- I have a [bone1 matrix at frame 10] ( CMatrix4x4 )

- i multiply vertex with this [bone1 matrix at frame 10]
- and i multiply vertex with "Player" transformation matrix ( position/rotation/scale in world space )

[/quote]


I should get this vertex moved/rotated as per [bone1 at frame10] at "Player's" world space position - right?
Damn, those calcs are complicated...





Ps. i'll ilustrate that later on ( as soon as our VM will be available ).
perfection.is.the.key
I think you might be slightly confusing the issue. I assume you have some form of character with a skeleton that you would like to animate.

For this, you need a hierarchy - it would be tricky to get it working without one (if you can do it at all). The reason you need a hierarchy can be visualised if you take your arm as an example. You know that each bone in your arm moves relative to its parent bone, so your forearm moves relative to your upper arm, etc. If there is no change to the matrix in your forearm and your upper arm moves, your forearm will stay in the same position RELATIVE to your upper arm. Its world transform moves, but its local transform does not.

In modelling tools, the animations are generally stored/exported as matrix transformations local to each bone, that is if you keep your upper arm still and move your forearm 90 degrees (like Popeye), the local animation matrix for that bone will be 90 degrees on some axis.

So why do you need a hierarchy? If you start at the root bone (likely to be something like the pelvis), each 'child' bone will have a local transform from the space of its parent. Combining these as you go down the hierarchy will position your skeleton in its desired pose, be it the bind pose (the pose you originally bound the skeleton to the mesh) or a frame of animation.

So let's say your left forearm bone has an index of, 15 and its parent is left upper arm bone and has an index of 14 (these won't necessarily be linear). A few of the vertices around the left elbow have bone indices of 14 and 15 and weights of 0.5 and 0.5 - meaning they are affected by both the upper arm and forearm. As per my post above, before rendering, you work your way through the hierarchy combining the animation matrices together to move the bones to the desired position. Each bone's inverse bind pose matrix is what moves the affected vertex(ices) (once its been transformed by the animation), back into the right position relative to when the vertex(ices) was originally bound to the skeleton.

If you don't have a hierarchy, you can't automatically move the forearm when the upper arm moves.

Does that make more sense?
Yes, thank you very much. Now it makes more sense.
Thank's again for your help, much appreciated!
perfection.is.the.key
Ok, just replying to let you know i have finally solved the issue.
The problem wasn't the export, it was due to the fact i wasn't rotating vertices around the bones positions.. seems silly, but simple things fail most often..

Happy now, taking a weekend break :)

Thanks Rob.
perfection.is.the.key
No worries, glad you got it sorted...

This topic is closed to new replies.

Advertisement