Skeletal animation and gimbal lock

Recommended Posts

Arkion    122
Certain bones (eg. arms) of my animated character rotate around wrong axis because of gimbal lock when I run the animation in my engine, while other bones rotate as expected. Vertices seem to be properly transformed to each bone's local space, so the culprit must be the actual animation routines. I'm currently using Euler angles. In previous threads on this board concerning gimbal lock, they say you should use incremental rotations rather than setting the anim matrix to identity each frame, but how is this done with keyframe animation and hierarchial system of bones?

Share on other sites
Skizz    794
Euler angles do leave you prone to Gimbal lock and are best avoided at all cost. Use matrices instead, or, if you can get your head round the maths and memory is tight, use quaternions.

If you do use matrices or quats then you'll need to look into Slerps (spherical linear interpolations) to do the animation.

Skizz

Share on other sites
Arkion    122
Using matrices with incremental rotations is what I'm trying to do. The problem is that for each bone you need to initialize it's final animation matrix to it's parent's final animation matrix first to account for the hierarchial behavior, like so (Euler angles):

for i = 0 to bones.size(){  // Calculate animation matrix here, eg.:  A.Identity();  A.MultiplyRotate(0, 1, 0, prevKey[2] + (curKey[2] - prevKey[2])*fraction); // Roll  A.MultiplyRotate(1, 0, 0, prevKey[1] + (curKey[1] - prevKey[1])*fraction); // Pitch  A.MultiplyRotate(0, 0, 1, prevKey[0] + (curKey[0] - prevKey[0])*fraction); // Yaw  // Concatenate transforms (last multiplied gets applied first to the vertices)  bones[i].finalMatrix = bones[bones[i].parent].finalMatrix;  bones[i].finalMatrix.Multiply(bones[i].localMatrix);  bones[i].finalMatrix.Multiply(A);}

To get incremental rotations, I would have to initialize final matrix of each bone to it's absolute matrix at model load time and when animating, apply just rotation increments to each bone's final matrix each frame;

for i = 0 to bones.size(){  // Calculate rotation delta matrix   // A = ...  // Apply just the increment  bones[i].finalMatrix.Multiply(A);}

But then bone's final matrix doesn't accomodate it's parent's updated final matrix anymore? This is the part I don't understand in the equation.

Share on other sites
Skizz    794
As you progress down the hierarchy of bones (they form a tree like structure) you multiply the current matrix with the parent's matrix. So, if you had:
body|-left upper arm| |-left lower arm|   |-left hand|-right upper arm  and so on

then the matrix for the left hand = interpolated left hand matrix * left lower arm * left upper arm * body.

Which is what you're doing.

One thing to check is that the bones don't forward reference parents, i.e ensure that index of child > index of parent.

I'm not sure what you mean by 'incremental' rotations? If you mean 'rotate left hand 2 degrees about it's at axis' then all you need to do is have two matrices per bone: its local matrix and its object matrix (i.e. the product of all the hierarchy matrices) and use a dirty flag when they are out of sync - so when you request a parent's object matrix, it'll be recalculated if the parent's local matrix has changed.

Skizz

Share on other sites
Lokken    100
Thought of using quaternion interpolation?

Share on other sites
Arkion    122
Replacing bones' final animation matrices with quaternions might work. If the quaternions are constructed initially from Euler angles, do they still have the gimbal lock problem? Eg.:

// cr, cp, cy are the cosines of roll, pitch and yaw angles.
// sr, sp, sy are the same but sines.

q.w = cr*cp*cy + sr*sp*sy;
q.x = cr*sp*cy + sr*cp*sy;
q.y = sr*cp*cy - cr*sp*sy;
q.z = cr*cp*sy - sr*sp*cy;

Euler angles is the format in which my 3D modeling software of choice, LightWave 3D, stores bone orientations. I wonder how skeletal animation is implemented in LightWave 3D - it always works reliably with no problems.