View Full Version : Quaternions with a 6DOF camera?

mikeynovemberoscar

01-12-2010, 01:35 PM

Hello. I am working on a Wing Commander (http://www.youtube.com/watch?v=GDQVnb6lqyg&feature=related) style game in C, but have run into a problem: gimbal lock. I think my best option (?) is to use Quaternions.

I was looking for a tutorial, but all I could find was a few advanced maths ones or nehe's tutorial (http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=Quaternion_Camera_Class) : written in C++, and he doesn't include the class, so it will be impossible to port to C.

Can anyone please help me use Quaternions? Or suggest a better option? Or point me to a tutorial?

Thanks

Yomboprime

01-12-2010, 02:51 PM

A good article, but a bit old. Perhaps someone has a better one?:

http://www.gamasutra.com/view/feature/3278/rotating_objects_using_quaternions.php

BTW I'm making a 3d space game too, in Java. I did learn quaternions from that article. See my firm for the link to my game's blog.

mikeynovemberoscar

01-13-2010, 09:14 AM

Thanks, I don't have the time to check it now but I hope it works. Also, I like your space game, the look around the cockpit is cool. :) Good luck with it

Yomboprime

01-13-2010, 09:37 AM

I've answered yor other post about quaternions

http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=270027#Post2700 27

Glad to know you like my game :)

mikeynovemberoscar

01-13-2010, 12:54 PM

Yeah thanks but I don't really understand. Can I just check:

Here are my two functions:

eulerToQuat(float qroll, float qpitch, float qyaw, QUAT * quat)

{

float cr, cp, cy, sr, sp, sy, cpcy, spsy;

// calculate trig identities

cr = cos(qroll/2);

cp = cos(qpitch/2);

cy = cos(qyaw/2);

sr = sin(qroll/2);

sp = sin(qpitch/2);

sy = sin(qyaw/2);

cpcy = cp * cy;

spsy = sp * sy;

quat->w = cr * cpcy + sr * spsy;

quat->x = sr * cpcy - cr * spsy;

quat->y = cr * sp * cy + sr * cp * sy;

quat->z = cr * cp * sy - sr * sp * cy;

}

quatToMatrix(QUAT * quat)

{

float x2 = quat->x * quat->x;

float y2 = quat->y * quat->y;

float z2 = quat->z * quat->z;

float xy = quat->x * quat->y;

float xz = quat->x * quat->z;

float yz = quat->y * quat->z;

float wx = quat->w * quat->x;

float wy = quat->w * quat->y;

float wz = quat->w * quat->z;

// This calculation would be a lot more complicated for non-unit length quaternions

// Note: The constructor of Matrix4 expects the Matrix in column-major format like expected by

// OpenGL

return Matrix4( 1.0f - 2.0f * (y2 + z2), 2.0f * (xy - wz), 2.0f * (xz + wy), 0.0f,

2.0f * (xy + wz), 1.0f - 2.0f * (x2 + z2), 2.0f * (yz - wx), 0.0f,

2.0f * (xz - wy), 2.0f * (yz + wx), 1.0f - 2.0f * (x2 + y2), 0.0f,

0.0f, 0.0f, 0.0f, 1.0f)

}

And what I should do:

1. Make quaternion from PITCH, ROLL, and YAW

2. Multiply quaternion to the current one

3. glMultMatrixf(newquaternion);

???

Should I translate before I rotate? Or after?

Yomboprime

01-15-2010, 04:23 PM

Note that you also need the function for quaternion multiplication, from the same article.

And what I should do:

1. Make quaternion from PITCH, ROLL, and YAW

2. Multiply quaternion to the current one

3. glMultMatrixf(newquaternion);

1 and 2 are ok, but in 3 you obtain the matrix from the current quaternion and then use that matrix in the glMultMatrixf call.

Should I translate before I rotate? Or after?

After.

mikeynovemberoscar

01-16-2010, 04:43 AM

Well, thanks for answering, but I have two more (rather stupid) questions:

1.

After.

So I just need to do glTranslate3f(0.0f,.0.0f,AIRSPEED) after I have rotated?

2. What is the storage for matrixes? float matrix[4][4]? How do I return arrays from my matrix functions?

Yomboprime

01-16-2010, 02:33 PM

1- No, you want to translate in the direction of movement. It is the z vector of the matrix

2- Yes. In C, to return an array you declare the function

float * functionName() { ...

kowal

01-16-2010, 03:02 PM

All OpenGL fixed-function stores matrices as vectors (column major ordering)

(float matrix[16] not float matrix[4][4])

Dark Photon

01-16-2010, 06:38 PM

2. What is the storage for matrixes?

Column-major matrix order. So an array of columns.

float matrix[4][4]?

You could use that, but you have to use a trick. OpenGL matrices are column-major order and OpenGL follows the operator-on-the-left convention (M*v1=v2). C/C++ arrays are row-major. You can use row-major and get the same exact values in exactly the same matrix cells as column-major if you reverse the multiplication order -- that is, use operator-on-the-right convention (v1*M=v2) with row-major C/C++.

How do I return arrays from my matrix functions?

With C/C++, arrays are weird. You can't return them by value, and it sounds like that's what you want. Easiest is to put float m[4][4] inside a class, and then it works. This is good for a number of other reasons too -- you can assign methods to the matrix storage.

For a tiny bit more efficiency, you can pass the target matrix along the the source matrices as arguments of your matrix functions. E.g. multMatrix( Mat4 &result, const Mat4 &a, const Mat4 &b );. This may eliminate a copy to your matrix result.

mikeynovemberoscar

01-21-2010, 09:52 AM

OK my euler to quat function doesn't work but I will try to rewrite them so I can return quaternions.

mikeynovemberoscar

01-23-2010, 03:52 AM

Hmmm. So I now have my functions working (new ones I stole borrowed developed :whistle: ) but strange things are happening. It's hard to describe exactly but It seems that every time I change the matrix (move the mouse or rotate the ship) the world shears and rotates and scales in all different manners. I think I have reversed the row-column stuff. Here's my code:

//glRotatef(pitch, 1.0f, 0.0f, 0.0f);

//glRotatef(heading, 0.0f, 1.0f, 0.0f);

xaxis.x = 1.0f;

yaxis.y = 1.0f;

zaxis.z = 1.0f;

q1 = quatMult( eulerToQuat(xaxis, pitch), eulerToQuat(yaxis, heading));

q1 = quatMult( eulerToQuat(zaxis, roll) , q1);

//quatMultiply(q1, q2, q2);

shipMatrix = quatToMatrix(q1);

glMultMatrixf( shipMatrix.m );

glTranslatef(-xp,0.0f,0.0f);

glTranslatef(0.0f,-yp,0.0f);

glTranslatef(0.0f,0.0f,-zp);

drawSpace();

And the respective functions:

quaternion eulerToQuat(vec3D axis, float angle)

{

quaternion quat;

float sinAngle;

angle *= 0.5f;

normVec3D(&axis);

sinAngle = sin(angle);

quat.x = (axis.x * sinAngle);

quat.y = (axis.y * sinAngle);

quat.z = (axis.z * sinAngle);

quat.w = cos(angle);

return quat;

}

quaternion quatMult(quaternion quat2, quaternion quat3)

{

vec3D vector1, vector2, cross;

quaternion quat1;

float angle;

vector1.x = quat1.x;

vector1.y = quat1.y;

vector1.z = quat1.z;

vector2.x = quat2.x;

vector2.y = quat2.y;

vector2.z = quat2.z;

angle = (quat1.w * quat2.w) - dotprodVec3D(vector1, vector2);

cross = crossprodVec3D(vector1, vector2);

vector1.x *= quat2.w;

vector1.y *= quat2.w;

vector1.z *= quat2.w;

vector2.x *= quat1.w;

vector2.y *= quat1.w;

vector2.z *= quat1.w;

quat1.x = vector1.x + vector2.x + cross.x;

quat1.y = vector1.y + vector2.y + cross.y;

quat1.z = vector1.z + vector2.z + cross.z;

quat1.w = angle;

return quat1;

}

matrix quatToMatrix(quaternion quat) {

matrix qmatrix;

qmatrix.m[0] = (1.0f - (2.0f * ((quat.y * quat.y) + (quat.z * quat.z))));

qmatrix.m[1] = (2.0f * ((quat.x * quat.y) + (quat.z * quat.w)));

qmatrix.m[2] = (2.0f * ((quat.x * quat.z) - (quat.y * quat.w)));

qmatrix.m[3] = 0.0f;

qmatrix.m[4] = (2.0f * ((quat.x * quat.y) - (quat.z * quat.w)));

qmatrix.m[5] = (1.0f - (2.0f * ((quat.x * quat.x) + (quat.z * quat.z))));

qmatrix.m[6] = (2.0f * ((quat.y * quat.z) + (quat.x * quat.w)));

qmatrix.m[7] = 0.0f;

qmatrix.m[8] = (2.0f * ((quat.x * quat.z) + (quat.y * quat.w)));

qmatrix.m[9] = (2.0f * ((quat.y * quat.z) - (quat.x * quat.w)));

qmatrix.m[10] = (1.0f - (2.0f * ((quat.x * quat.x) + (quat.y * quat.y))));

qmatrix.m[11] = 0.0f;

qmatrix.m[12] = 0.0f;

qmatrix.m[13] = 0.0f;

qmatrix.m[14] = 0.0f;

qmatrix.m[15] = 1.0f;

return qmatrix;

}

I have a feeling I am almost there, but while I'm here, I might as well ask if you think my translation functions are OK:

xp += cos(DEG_TO_RAD * (heading - 90.0f ) ) * airspeed * dt;

zp += sin(DEG_TO_RAD * (heading - 90.0f) ) * airspeed * dt;

yp += sin(DEG_TO_RAD * -pitch ) * airspeed * dt;

mikeynovemberoscar

01-27-2010, 01:19 PM

Just in case anyone reads this, I got it working!!!!!!!! Yipee!

Thankyou Yomboprime and everyone who helped me!

mikeynovemberoscar

02-15-2010, 03:16 AM

Yes I added the constant DEG_TO_RAD to multiply pitch, heading and roll before I send them to the quaternion functions.

Could you help with translating along the z axis of a quaternion/matrix? (http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=271377#Post2713 77)

Powered by vBulletin® Version 4.2.3 Copyright © 2017 vBulletin Solutions, Inc. All rights reserved.