Quaternions with a 6DOF camera?

Hello. I am working on a Wing Commander 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 : 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

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.

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. :slight_smile: Good luck with it

I’ve answered yor other post about quaternions
http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=270027#Post270027

Glad to know you like my game :slight_smile:

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?

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

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.

After.

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

After.

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

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

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() { ...

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

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

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 (Mv1=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 (v1M=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.

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

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;

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

Thankyou Yomboprime and everyone who helped me!

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?