Rotations using quaternions - still experiencing gimbal lock?

Hi all,

I’m working on a 3D skeletal animation app,
I’m rather confused as to the best way to perfrom bone rotations,
at first I just had the rotation angles stored in the bone struct and I calculated a matrix from that, this doesn’t work correctly as depending in what order rotations are made it doesn’t always turn out the same…

After much searching I came to the conclusion that my app was suffering from “gimbal lock” and that the correct way to solve the problem is to use quaternions to calculate the rotations…

I have tried quaternions with no improvment … unless I’m just using them incorrectly.

Basically I have a bone struct something like:

struct bone {
    float rang[3];
    float rquat[4];
    float matrix[16];
    /* Some other stuff here (parent, children, position etc..) */

}

Now when a user rotates a bone I simply adjust the appropriate angle - rang[0] being X and so on…
Then I calculate the rotation quaternions the usual way, and multiply them by each other, this becomes the quaternion rquat in the bone struct,
I also multiply it by the parent bone’s rquat.
This is then converted to a matrix which represents the bones final rotation…

After all this it makes no difference to my original system which used only matrices? :confused:

I have also worked out another system,
I modifiy the bone struct a bit:

struct bone {
    /*float rang[3];*/
    float rquat[4];
    float quat[4];
    float matrix[16];
    /* Some other stuff here (parent, children, position etc..) */

}

I now have two quaternions in a bone, when the bone is initialised I set the quaternion rquat to the identity, and every time the user rotates the bone I multiply rquat by a rotation quaternion,
on rendering this gets multiplied by the parent bone’s quat and becomes quat which is converted into a matrix…

Ok now this system works perfectly - no gimbal lock, rotations always occur exactly as they ought.
But the problem with this system is I don’t know what angles are represented by this quaternion?
And therfore how to present them to the user?

It must be possible as most modelling/animation apps are able to present the user with a simple set of angles to modify.

Any ideas or clues?
Thanks!

Well after some more searching, I’ve actually come across the formula to convert a quaternion into euler angles, I’ve created a function like this:

#define RADDEG (M_PI/180.0)

void quat_to_euler(float quat[4], float *x, float *y, float *z)
{
        float sqx = quat[0] * quat[0];
        float sqy = quat[1] * quat[1];
        float sqz = quat[2] * quat[2];
        float sqw = quat[3] * quat[3];

        float unit = sqx + sqy + sqz + sqw;
        float test = quat[0]*quat[1] + quat[2]*quat[3];

        if (test > 0.499*unit) {
                *x = 0;
                *y = (2 * atan2(quat[0], quat[3]) / RADDEG);
                *z = (M_PI/2) / RADDEG;
        } else if (test < -0.499*unit) {
                *x = 0;
                *y = (-2 * atan2(quat[0], quat[3])) / RADDEG;
                *z = (-M_PI/2) / RADDEG;
        } else {
                *x = atan2(2*quat[0]*quat[3] - 2*quat[1]*quat[2], -sqx + sqy - sqz + sqw) / RADDEG;
                *y = atan2(2*quat[1]*quat[3] - 2*quat[0]*quat[2], sqx - sqy - sqz + sqw) / RADDEG;
                *z = asin(2*test/unit) / RADDEG;
        }

        if (x < 0) x += 360.0;
        if (y < 0) y += 360.0;
        if (z < 0) z += 360.0;
}

This seems to work OK, except that the reported angles are a bit odd,

If I rotate any one angle (alone) it reports the angle from 0 up to 360 degrees correctly.
But say I rotate x by 45, and then I rotate y by -90 degrees it reports: x = 0, y = 270, z = 315.

Which I suppose is actually the same thing… :smiley:
So it doesn’t really matter… but it might confuse the users a bit…