PDA

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&amp;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

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 &amp;result, const Mat4 &amp;a, const Mat4 &amp;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);

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(&amp;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&amp;Number=271377#Post2713 77)