Advertisement

Quaternions: A Real Pisser

Started by July 16, 2001 05:09 PM
20 comments, last by Dave the Embalmer 23 years, 7 months ago
I am writing a little space sim (for my personal game development education only) and I know Quaternions are diffinately the way to go...I''ve read just about every tutorial I can find on them, and I''ve written a class that can do all the mathmatical functions with Quaternions, but I have no idea how to actually use it all =). I''m tired of tutorials saying "...and so the unit vector can do blah blah here''s how to do blah blah but we''re not going to tell you this and that." This and that being how to actually rotate the quaternion, and get that rotation to rotate an object or camera. Can anyone tell me, or point me to a tutorial that will tell me "So, to rotate a quaternion using pitch, yaw, and roll..." and "Now use glLoadMatrix/translate+rotate on this..." I''d very much appreciate it. Thanks!
I''ll second that!

It seems we''re both doing very similar thing - i''m doing a kind of Elite clone, just to learn OpenGL and C ... and i''ve had much the same problems!

One suggestion ... try mailing Propuslor propulsor@web-discovery.net ... from the GLHorizon site ... worth checking out! He sent me some stuff, which i haven''t had time to look at yet ... thanks for that Propuslor!!!

Regards

Shag
Advertisement
Thanks Shag, I''ll see if he emails me some stuff to =)
Once upona a time I was working on a programming site but the lack of interest in it caused me to abandon it. I do however have some info on quaternions there, including there use in a flight simulation type environment...
The main page is at
http://tannara.net/
the page I''m specifically referancing is here...
http://tannara.net/Graphics/Theory/3d_viewing_in_a_flight_simulations.htm
------------------------------Piggies, I need more piggies![pig][pig][pig][pig][pig][pig]------------------------------Do not invoke the wrath of the Irken elite. [flaming]
Assuming you have the quat-math functions and that they work:

what i do is the following:

I have 2 quats defined at all times. One holds the current orientation in the univers and the other holds what i call dOrientation (ie the difference in orientation from the last frame to the current).

Between frames i find out how much rotation is needed around the 3 axis (Euler). I create dOrientation from these 3 angles. Perform the multiplication: dOrientation*Orientation (in that specific order). Convert the dOrientation to AxisAngle and use glRotate to perform the AxisAngle-rotation.
Finally copy the values from dOrientation to Orientation.
Repeat each frame.

I had some problems because, first, i didnt use the dOrientation to hold the temporary rotation (still got that damn Gimbal-lock), and second i was multiplying in the wrong order (quat-multiplication is not commutative(called that in english ?)).

If you still have problems let me know and ill post some code.

Good luck
Another thing which caused me some problems was finding the vector of translation from a quaternion. What i used is the following set of equations.

dirX = 2.0 * (x * z - w * y)
dirY = 2.0 * (y * z + w * x)
dirZ = 1.0 - 2.0 * (x * x + y * y);

Where w, x, y, z are the components of the Orientation quat.

Hope that helped .
Advertisement
Darn, I have how no idea how to perform Quaternion-to-Euler conversion...I have Quaternion-to-Matrix Conversion, and thats what I''ve been using, but when I rotate the Quaternion, some really gnarly things happen to my skybox (the only object in the game right now). Things like two oppisite sides of the skybox coming closer together, and eventually going through eachother only to go back to normal, then back to almost touching eachother...Anyone else had this problem?
Here's my quat functions. feel free to use them even though they could use some optimization.

  class Quaternion{public:	float w,x,y,z;	float qLength;	Quaternion(float newW, float newX, float newY, float newZ);	Quaternion();	void Normalize();	void toAxisAngle(float *result);	void MultQuat(Quaternion *q);	void EulerToQuat(float aX, float aY, float aZ);	void LoadIdentity();};  


    #include "stdafx.h"Quaternion::Quaternion(float newW, float newX, float newY, float newZ){	this->x = newX;	this->y = newY;	this->z = newZ;	this->w = newW;	this->qLength = (float)sqrt(this->x*this->x + this->y*this->y + this->z*this->z + this->w*this->w);}Quaternion::Quaternion(){	this->x = 0.0; // create mult identity quaternion	this->y = 0.0;	this->z = 0.0;	this->w = 1.0;	this->qLength = 1.0;}void Quaternion::Normalize(){	float l;	l = (float)1.0/this->qLength;	this->x *= l; // mult by inverse length to normalize	this->y *= l;	this->z *= l;	this->w *= l;	this->qLength = 1.0f;}void Quaternion::toAxisAngle(float *result){	float AxisAngle[4];	float scale;	scale = this->x*this->x + this->y*this->y + this->z*this->z;	AxisAngle[0] = (float)(2*acos(this->w)); // angle to rotate	AxisAngle[1] = this->x / scale; // next 3 lines is x,y,z of vector to rotate about	AxisAngle[2] = this->y / scale;	AxisAngle[3] = this->z / scale;	result[0] = AxisAngle[0];	result[1] = AxisAngle[1];	result[2] = AxisAngle[2];	result[3] = AxisAngle[3];}void Quaternion::MultQuat(Quaternion *q){	float tempx, tempy, tempz, tempw;	tempw = this->w*q->w - this->x*q->x - this->y*q->y - this->z*q->z;	tempx = this->w*q->x + this->x*q->w + this->y*q->z - this->z*q->y;	tempy = this->w*q->y + this->y*q->w + this->z*q->x - this->x*q->z;	tempz = this->w*q->z + this->z*q->w + this->x*q->y - this->y*q->x;		this->x = tempx;	this->y = tempy;	this->z = tempz;	this->w = tempw;	this->qLength = (float)sqrt(this->x*this->x + this->y*this->y + this->z*this->z + this->w*this->w);}void Quaternion::EulerToQuat(float aX, float aY, float aZ){	this->y = 0.0; //lets make sure that quat is initialized	this->z = 0.0;	Quaternion* qY = new Quaternion();	Quaternion* qZ = new Quaternion();		this->x = sin(aX/2);	this->w = cos(aX/2);	qY->y = sin(aY/2);	qY->w = cos(aY/2);	qZ->z = sin(aZ/2);	qZ->w = cos(aZ/2);		this->MultQuat(qY);	this->MultQuat(qZ);	this->qLength = (float)sqrt(this->x*this->x + this->y*this->y + this->z*this->z + this->w*this->w);}void Quaternion::LoadIdentity(){	this->w = 1.0;	this->x = 0.0;	this->y = 0.0;	this->z = 0.0;}    


Edited by - newdeal on July 17, 2001 1:50:04 PM
Dave: quaternion to euler, like matrix to euler, doesn''t really work because you have to use inverse trig functions - because their domain is limited you aren''t guaranteed to get out the values that went in. It hasn''t mattered to me though, as I''ve yet to find a use for quaternion to euler conversion.

NewDeal: Similarly if you assume an object''s initial up vector (0,1,0), and left vector (1,0,0) then after being rotated by a quaternion:

up.x = 2 * (x*y - w*z)
up.y = 1 - 2 * (x*x + z*z)
up.z = 2 * (y*z + w*x)

left.x = 1 - 2 * (y*y + z*z)
left.y = 2 * (x*y + w*z)
left.z = 2 * (x*z - w*y)

You can derive those from standard quaternion to matrix conversion code.
A couple things I forgot.

Like NewDeal suggested, try converting from quaternion to axis-angle, instead of quaternion to matrix. I find it much easier, and you''ll probably save some operations.

Ok now for how exactly you rotate stuff with quaternions. Each frame you decide how much you want your object to yaw, pitch, and roll based on user input. For example if the user moves the mouse 5 units left you could say that''s a yaw of 5 degrees. Anyway, you convert these yaw/pitch/roll angles (euler angles) to a quaternion. To actually rotate the object you just multiply this yaw/pitch/roll quaternion with the object''s current orientation.

quaternion temp = eulerAnglesToQuaternion(yaw, pitch, roll);
ship.orientation = temp * ship.orientation;

To display an object...

glTranslatef(ship.position);
vector axis;
float angle;
ship.orientation.quaternionToAxisAngle(&axis, ∠);
glRotatef(angle, axis.x, axis.y, axis.z);
//render the object here

How''s that?
For nice, smooth camera control with quaternions, look up articles on quaternion interpolation, also called slerp. There was also an interesting article in the Game Programming Gems book about a quaternion "shortest rotation arc" or something like that, I found it quite useful.

This topic is closed to new replies.

Advertisement