Quaternion Rotation - Rotating on Self Axis

For testing/learning purposes I am trying to use quaternions in place of euler angles when doing the rotation/translation for the view camera. I managed to get everything to work with the exception that the camera is now rotating around the center-point axis rather than on it’s own axis. What do I need to do to achieve rotation on the camera’s own axis rather than rotating around the center axis?

Below is my code used for this test:

Any questions regarding to the code please let me know. I hope I’ve been descriptive enough for anyone to understand my situation.



void Draw(void)
{
    vec4_t vec1;
    vec4_t vec2;
    vec4_t vec3;
    double m[16];

....
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glPushMatrix();

    M_SetQuaternion(&vec1, R_ViewAngleToEuler(cam_yaw), 0, 1, 0);
    M_SetQuaternion(&vec2, R_ViewAngleToEuler(cam_pitch), 1, 0, 0);
    M_MultiplyQuaternions(&vec3, &vec1, &vec2);
    M_CalcRotationMatrix(&vec3, m);
    M_CalcTranslationMatrix(m, cam_x, cam_y, cam_z);

    // the stuff above is suppose to behave like this below:

    // glRotatef(R_ViewAngleToEuler(cam_pitch), 1.0f, 0.0f, 0.0f);
    // glRotatef(R_ViewAngleToEuler(cam_yaw), 0.0f, 1.0f, 0.0f);
    // glTranslatef(cam_x, cam_y, cam_z);

    glMultMatrixd(m);
....

    glPopMatrix();
....

}


// here's the utilities used in case anyone wants to look

typedef struct
{
	double x;
	double y;
	double z;
	double w;
} vec4_t;

#define M_PI    3.1415926535897932384626433832795
#define M_RAD   (M_PI / 180.0)
#define M_DEG   (180.0 / M_PI)

float R_ViewAngleToEuler(float angle)
{
    return angle * 0.0439453125f; // should probably not worry about this. This converts the game's angle value into euler degrees. Game's angle value ranges from -4096 to 4096
}

void M_CalcRotationMatrix(vec4_t* vec, double *m)
{
    double xx = vec->x * vec->x;
    double yx = vec->y * vec->x;
    double zx = vec->z * vec->x;
    double wx = vec->w * vec->x;
    double yy = vec->y * vec->y;
    double zy = vec->z * vec->y;
    double wy = vec->w * vec->y;
    double zz = vec->z * vec->z;
    double wz = vec->w * vec->z;
    double ww = vec->w * vec->w;

    m[ 0]   = ((ww + xx) - yy) - zz;
    m[ 1]   = (wz + wz) + (yx + yx);
    m[ 2]   = (zx + zx) - (wy + wy);
    m[ 3]   = 0.0;
    m[ 4]   = (yx + yx) - (wz + wz);
    m[ 5]   = (yy + (ww - xx)) - zz;
    m[ 6]   = (wx + wx) + (zy + zy);
    m[ 7]   = 0.0;
    m[ 8]   = (wy + wy + zx + zx);
    m[ 9]   = (zy + zy) - (wx + wx);
    m[10]   = ((ww - xx) - yy) + zz;
    m[11]   = 0.0;
    m[12]   = 0.0;
    m[13]   = 0.0;
    m[14]   = 0.0;
    m[15]   = 1.0;
}

void M_CalcTranslationMatrix(double *m, double x, double y, double z)
{
    m[12] = x + m[12];
    m[13] = y + m[13];
    m[14] = z + m[14];
}

void M_MultiplyQuaternions(vec4_t *out, vec4_t *q1, vec4_t *q2)
{
    out->x = q1->x * q2->w - q1->y * q2->z + q2->x * q1->w + q2->y * q1->z;
    out->y = q1->x * q2->z + q1->y * q2->w - q2->x * q1->z + q1->w * q2->y;
    out->z = q2->x * q1->y + q1->w * q2->z + q1->z * q2->w - q1->x * q2->y;
    out->w = q1->w * q2->w - (q2->y * q1->y + q1->z * q2->z + q2->x * q1->x);
}

void M_Normalize4(vec4_t *out)
{
    long double d;

    d = sqrt(out->y * out->y + out->z * out->z + out->w * out->w + out->x * out->x);

    if(d != 0.0)
    {
        out->x  = out->x * 1.0 / d;
        out->y  = out->y * 1.0 / d;
        out->z  = out->z * 1.0 / d;
        out->w  = out->w * 1.0 / d;
    }
}

void M_SetQuaternion(vec4_t* vec, double angle, double x, double y, double z)
{
    double sin_a = sin((angle * M_RAD) * 0.5);
    double cos_a = cos((angle * M_RAD) * 0.5);

    vec->x  = x * sin_a;
    vec->y  = y * sin_a;
    vec->z  = z * sin_a;
    vec->w  = cos_a;

    M_Normalize4(vec);
}


For testing/learning purposes I am trying to use quaternions in place of euler angles when doing the rotation/translation for the view camera.

No you’re not. You are using quaternions instead of rotation matrices, but you’re still using Euler angles.

This is your quaternion code:


    M_SetQuaternion(&vec1, R_ViewAngleToEuler(cam_yaw), 0, 1, 0);
    M_SetQuaternion(&vec2, R_ViewAngleToEuler(cam_pitch), 1, 0, 0);
    M_MultiplyQuaternions(&vec3, &vec1, &vec2);

And this is your matrix code:


    // glRotatef(R_ViewAngleToEuler(cam_pitch), 1.0f, 0.0f, 0.0f);
    // glRotatef(R_ViewAngleToEuler(cam_yaw), 0.0f, 1.0f, 0.0f);

It’s the same thing. The only difference is that in the first one, you’re using quaternions to represent rotations and the second you’re using matrices. You’re still computing the orientation as a succession of rotations about the cardinal axes, so it will still be Euler angles.

Well actually, there is another difference: you reversed the order of the transforms. I suspect that’s why you’re getting different results. Try this and see if it fixes your problem:


    M_MultiplyQuaternions(&vec3, &vec2, &vec1);

the center-point axis

What is the “center-point axis”?

I’ve thought that was what it was at first but it’s not. Still gives undesired results. I am assuming that I may need to do additional arithmetic with the translation values or something.

Coordinates 0, 0, 0. It appears that it’s orbiting around that rather than actually rotating around the camera’s position.

I am assuming that I may need to do additional arithmetic with the translation values or something.

I’d guess it also has something to do with the fact that your “M_CalcTranslationMatrix” doesn’t do a proper matrix multiply. It just stuff the translation into the translation component of the given matrix.

In any case, have you tried looking at the two matrices? Generate both of them and use glGetFloat(GL_MODELVIEW) to get OpenGL’s matrix. What is different about them?

Unfortunately I am at work right now so I’ll need to post the results later today.

On a side note, when I was messing around with it earlier today, I’ve tried doing something like this below which resulted in exactly what I was expecting, but it kinda seems intuitive to use two matrices just to rotate around the camera’s axis:



    // matrix 1 - do the actual rotation
    glPushMatrix();

    M_SetQuaternion(&vec1, R_ViewAngleToEuler(cam_yaw), 0, 1, 0);
    M_SetQuaternion(&vec2, R_ViewAngleToEuler(cam_pitch), 1, 0, 0);
    M_MultiplyQuaternions(&vec3, &vec1, &vec2);
    M_CalcRotationMatrix(&vec3, m);

    glMultMatrixd(m);

    // matrix 2 - do the actual translation
    glPushMatrix();

    M_SetQuaternion(&vec1, 0, 0, 0, 0); // no rotation - just get quaternion identity (default)
    M_CalcRotationMatrix(&vec3, m);
    M_CalcTranslationMatrix(m, cam_x, cam_y, cam_z);

    glMultMatrixd(m);

    glPopMatrix();
    glPopMatrix();


Maybe this is the right way to do it, I don’t know but that seems much more expensive than doing two glRotatef calls and one glTranslatef call.

but it kinda seems intuitive to use two matrices just to rotate around the camera’s axis:

First of all, the camera doesn’t have an axis (or rather, it doesn’t have just one axis). You’re talking about rotating around the camera’s position.

Second, you were using two matrices before. What do you think glTranslatef does? It creates a translation matrix and multiplies it with the current matrix on the stack.

The only difference is that you’re doing all the work now.

And this is why I feel that people are ill-served by learning graphics programming with the fixed function pipeline. It makes you think you know what’s going on, but really, all you know is that if you put one thing here, and another thing there, it all works out.

By doing it manually, you have to actually understand how it works.

Maybe this is the right way to do it, I don’t know but that seems much more expensive than doing two glRotatef calls and one glTranslatef call.

There is zero difference between your current code and the GL matrix code.

All glRotatef and glTranslatef are doing is creating matrices and multiplying them with the current matrix on the stack. Just like you’re doing. The only difference is that you’re making it explicit, while before, you were just hiding it behind OpenGL’s interface.

Indeed, odds are this is faster than the old code (not that it matters, of course, since camera setup is something you do once per frame). Each glRotatef call had to do a matrix multiplication. Since you combined those two rotations as quaternions, that requires only quaternion multiplication which is far less expensive. The quaternion-to-matrix function is virtually identical to what glRotatef has to do, but you only call it once.

If the OpenGL functions are faster, it would only be because they’re using SSE intrinsics and so forth.

Well, it’s not like I am going to school for this stuff. I pretty much learn things the hard way and personally it’s the best way to better understand things. This is the first time that I am looking into doing things manually like calculating the matrix myself and this is exactly why I am working with quaternions just so I can better understand what’s going on.

I never really knew what was going on when calling glrotate/gltranslate functions as I always thought it was applying the rotations/translation to the current modelview matrix so it’s good that I am better understanding this stuff now.

And that’s exactly why I wrote the tutorial you can find in my signature. So that people who are trying to learn graphics programming don’t have to learn “the hard way”. You can learn things the correct way. This is more effective than if you learned them incorrectly and have to figure out what went wrong in code that you don’t understand.