Sign in to follow this  
Arkion

Quaternions but still gimbal lock?

Recommended Posts

Arkion    122
Ok, I changed my character animation system to use quaternions instead of Euler angles/matrices directly, but I still have exact same gimbal lock problem (ie. certain body parts rotate around wrong axes, while others work just fine). Here's the code:
// --- FOR EACH BONE ---
// seqtime is how long the animation sequence has lasted, in seconds.
// prevKey and curKey are the rotation keys (Euler angles) between which we are currently interpolating.
// bones[i]->rotation quaternion contains bone's reference rotation relative to it's parent.
// bones[i]->translation -- a matrix containing bone's translation relative to it's parent.

Quaternion q0, q1, qInt;
Matrix     A;

fraction = (seqtime - prevKey[TIME])/(curKey[TIME] - prevKey[TIME]);

q0.FromEuler(prevKey[YAW], prevKey[PITCH], prevKey[ROLL]);
q1.FromEuler(curKey[YAW], curKey[PITCH], curKey[ROLL]);

qInt = Quaternion::Slerp(q0, q1, fraction);
qBoneAnim[i] = bones[i]->rotation * qInt;

// Construct final animation matrix for this bone
qBoneAnim[i].ToMatrix(A);

if(bones[i]->parent == -1)
  animMatrices[i].Identity();
else
  animMatrices[i] = animMatrices[bones[i]->parent];

animMatrices[i].MultiplyMatrix(bones[i]->translation);
animMatrices[i].MultiplyMatrix(A);



It seems that if you construct quaternion from Euler angles, it's still prone to gimbal lock. How then should I do the transformations, if not this way? As quats can't represent translation, I'm forced to convert them to matrices here and there.

Share this post


Link to post
Share on other sites
Drazgal    368
qBoneAnim[i] = bones[i]->rotation * qInt;

I dont see what this line is here for. Once you SLERP between the previous frame of animation and the current one then you have your rotations and should convert it to a matrix then. Ofcourse this might be due to your animation file (I use Milkshape animations) so might not be a problem.

Also are you Interpolating the translation between the previous frame of animation and this one? (I assume you just havent shown tha thappening).

If thats not your problem then I cant see anything wrong with the code you have posted.

If possible you could try using the DirectX maths helper functions to make sure your maths work is correct.

If you are still stuck and you think it might help I can email you some working animation code and you can see where you are going wrong if you want.

Share this post


Link to post
Share on other sites
Palidine    1315
the problem is that at the root you are still just using euler angles. if on each frame you take eulers and convert them to quaternions you'll still get gimbal lock. it makes sense. you're essentially just doing a lot of extra complicate math to end up with a euler representation of your object's orientation.

the fix is to not use eulers _at all_ for the current rotational state of your model. you will only store the delta eulers in the animation. convert the deltas to quaternions and post multiply them into your object's rotational quaternions and you should be fine. so from your code, i believe, you will need to get rid of the curKey variable and replace it with a member variable in the bone object/struct that will persist the bones rotation between frames. then you'll need to do some quick math to calculate the delta rotation that this frame represents.

-me

Share this post


Link to post
Share on other sites
Arkion    122
Drazgal: I don't support translation keyframes for bones because there's no need to use them in character animation (except bone reference pose translation).

Palidine:
As a matter of a fact, I have also tried storing the current rotation state as quaternion rather than Euler angles. To get yaw, pitch and roll deltas per frame, I subtract current fraction value from previous frame's fraction, construct the delta quaternion, and post-multiply current rotation by the delta. Is this what you mean? The problem is that bones dont follow their keyframes, they just spin around uncontrollably. Here's the code:


for each bone i
{
fraction = (seqtime - prevKey[TIME])/(curKey[TIME] - prevKey[TIME]);

q.FromEuler((curKey[YAW] - prevKey[YAW])*(fraction - prevFraction[i]),
(curKey[PITCH] - prevKey[PITCH])*(fraction - prevFraction[i]),
(curKey[ROLL] - prevKey[ROLL])*(fraction - prevFraction[i]));

prevFraction[i] = fraction;
qBoneAnim[i] = qBoneAnim[i] * q;

// Construct animMatrices as usual
// ...
}

// Increment seqtime by frame time
seqtime += frametime;


Share this post


Link to post
Share on other sites
davepermen    1047
you can't linear interpolate between your euler angles, wich is what you do in your last posted code. this will of course result in huge chaotic issues.

but converting both ends first to quaternions, and then spherical linear interpolate should lead to the correct result.

Share this post


Link to post
Share on other sites
Arkion    122
Ok, here's an image showing the problem:



The leftmost image is from the animation program - this is how the model should look like.
In middle image I used quaternions constructed from Euler angles and SLERP. You can see that guy's arms wave erraneously from left side to right side - so there's gimbal lock in the arm bones.
Rightmost image shows what happens if I use the incremental rotation code from my last post. Guy gets into pretty bad shape.

Share this post


Link to post
Share on other sites
b34r    365
The problem with your Euler Angles version really looks like a problem of rotation order... Make sure you are applying each axis rotation in the same order as Lightwave (the rotation order field in the scene)...

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this