View Full Version : EDIT: Now with a picture : Skeletal animation basics

04-20-2004, 02:55 PM
I'm having a difficult time writing a skinned mesh exporter for Maya, although for once it's not Maya's API that's causing the problem. This'll be an API-free post. :)

I clearly don't have a good enough grasp of the theory behind skinning.

I'm not sure what information I should be exporting for my joints at the bind pose:
World-space matrix, Inverse world-space, Local-space ( bone space ), etc

I'm somewhat comfortable with the actual deformation of vertices at render-time; I've written some small skeletal animation prototypes using existing formats.

I'm not very clear on how to create the initial data.

I have a skeleton in a rest pose.
I have the mesh at that rest pose.

Do I store the rest pose bones local matrices, or do I recursively multiply them with their parents' matrices, and then store them?

Do I store the model-space (not bone-space) vertices of the rest pose, and call this the bind pose?

If not, how do I determine the bind pose coordinates?

Unforunately my employer has given me a week to implement this; third-party libraries and existing formats are out of the question.

04-20-2004, 04:17 PM
Given the bind-pose vertices of the mesh, the bind-pose matrices are such that they exert no influence on the vertices..or so I've gathered.

Does this mean that animation data stores the difference from the bind-pose matrices?
It seems that if only one bone influenced each vertex, the bind-pose vertices would be stored in the space of the bones that influenced them.

What happens when more than one bone influences a vertex? Do you store a vertex position PER WEIGHT per vertex that's transformed into the weight's bone's space?

04-21-2004, 07:55 AM
Another fine spring morning, and a fresh start.

Looking at the file format of an unreleased game that uses skeletal animation, there is actually a vertex position stored per weight.

The general algorithm for show the model in its bind-pose is :

vertex v = 0,0,0

for( number of bones influencing this vertex ) {
v += weight.position * boneMatrix * weight.bias

Given the simple situation where only one bone influences a vertex, the bind pose is extracted

v = boneMatrix * weight.position;

and the weight.position to store is

weight.position = bind_pose_vertex * inverse(boneMatrix);

This seems correct to me, but I'm doing something wrong when I calculate the weight.position.

Buggy bind-pose picture (http://www.cs.virginia.edu/~csz9v/skeletal_animation/buggy_bindpose.jpg)

The model is stretched in the x and y directions (taller and wider)..but the head, arms, and feet appear correct.

The root of this skeleton is at the hips, and only one bone influences a vertex.

Christian Schüler
04-23-2004, 03:37 AM
Hi cat, try this:

(1) export the inverse of the bind matrix of each bone in object space, ie. inverse( bind matrix in object space )

(2) export the mesh in bind pose normally

(3) export animation for each bone in parent-space, ie. the transfrom from parent-to-child.

(4) during playback, recursively reconstruct the animation to form a skeleton in object space

(5) multiply this with the stores inverse bind matrix from (1)

(6) now you have a matrix palette that you can use for softskinning, ie, vertex = vertex * matrix[index1] * weight1 + vertex * matrix[index2] * weight2 + ...

hope this helps


In maya, the skinCluster has an attribute "geomMatrix" (obj2world) and "bindPreMatrix" (inverse of bind in world space). To get the inverse bind in object space multiply

inversebind_objspace = geomMatrix * bindPreMatrix

04-23-2004, 02:27 PM

I had a similar problem like this. It turned out to be an incorrect coordinate export. I used something like during exporting:

vertex.x = MDistance::internalToUI( vertex.x ); The steps I used for exporting are:

- get all the joints orientation with
joint.getOrientation(); - get mesh data using MDistance::internalToUI() for vertex coordinates, etc.

- get animation data with

val = animCurve.evaluate( time ); The steps for the skinning are:

- build the local animation transformation matrix

LocalOrientationMatrix = Scale * Rotation * Translation

- build the hierarchy matrices, starting from the root joint and store the inverse of that for skinning later on

HierarchyMatrix[i] = OrientationMatrix[i] * HierachyMatrix[i-1]

InverseHierarchyMatrix[i] = inverse( HierarchyMatrix)

- during animation, for each joint multiply the local animation transformation with the local orientation matrix and with parent's final animation matrix

FinalAnimMatrix[i] = LocalAnimMatrix[i] * LocalOrientationMatrix[i] * FinalAnimMatrix[i-1]

- for skinning matrix, it's just the inverse hierarchy matrix multiply by the final animation matrix.

SkinMatrix[i] = FinalAnimMatrix[i] * InverseHierarchyMatrix[i]

- final vertex position output would be

for i = 0 to num_joint_influences
VertexPos = VertexPos * SkinMatrix[i]

Hope this helps.

04-23-2004, 08:26 PM
Thanks for the responses. Most of my confusion was resulting from row and column major differences within Maya's API. Incredibly frustrating, and stupid.

I chose what I thought would be a relatively flexible and debuggable format (md5mesh and md5anim) but I should have started with a more well-known system, I think.

At least I thoroughly understand the different spaces and general transforms involved.