• FEATURED
• FEATURED
• FEATURED
• FEATURED
• FEATURED

View more

View more

View more

### Image of the Day Submit

IOTD | Top Screenshots

### The latest, straight to your Inbox.

Subscribe to GameDev.net Direct to receive the latest updates and exclusive content.

# Quaternion Camera Implementation

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

5 replies to this topic

### #1Coldon  Members

Posted 16 October 2007 - 07:55 PM

I'm trying to get a quaternion camera working but i'm struggling a bit, strangely enough not with the maths behind the quaternion but rather how to implement it in opengl. The tutorials i've found all use different methods. Some directly modify the modelview matrix while others make using of transformations via glRotate/glTranslate's and finally some use the gluLookAt() function (i know this is a shortcut to the transforms). My quaternion maths is right and my class can produce anything needed for any of the above methods. can you give me a brief overview of where i need to start and how to implement the camera? like should i use heading and pitch variables (kinda like euler angles), should i use a heading vector and work from that? Whats the difference between these methods? Is there a prefered solution? here are my vector, quaternion and camera classes, i've implemented a basic rotate method (from the NEHE tutorial) which doesnt work as intended, it seems to rotate spirally ?!? Point and Vector Classes :
class point3f
{
public:

//members
//------------------------------------------------------------------------------

float x, y, z;

//constructors
//------------------------------------------------------------------------------

point3f() : x(0),y(0),z(0) {}
point3f(float ix, float iy, float iz) : x(ix), y(iy), z(iz) {}

//methods
//------------------------------------------------------------------------------

point3f operator +(point3f p)
{
return point3f(x + p.x, y + p.y, z + p.z);
}

//subtraction
point3f operator -(point3f p)
{
return point3f(x -p.x, y - p.y, z - p.z);
}

//convert to array
void toArray(float* a)
{
a[0] = x;
a[1] = y;
a[2] = z;
}
};

//vector class
class vector3f
{

public:

//members
//------------------------------------------------------------------------------

float x, y, z;

//constructors
//------------------------------------------------------------------------------

//default constructor
vector3f() : x(0),y(0),z(0) {}

//create vector from point
vector3f(point3f p) : x(p.x), y(p.y), z(p.z) {}

vector3f operator =(point3f p)
{
return vector3f(p);
}

//create vector with co-ordinates
vector3f(float ix, float iy, float iz) : x(ix), y(iy), z(iz) {}

//methods
//------------------------------------------------------------------------------

//cross product
vector3f crossProduct(const vector3f &v)
{
vector3f cp;

//cross product
cp.x =	y * v.z - z * v.y;
cp.y =	-( x * v.z - z * v.x );
cp.z =	x * v.y - y * v.x;

return cp;
}

//normalize
void normalize()
{
float sumSquares = x*x + y*y + z*z;

//dont normalise if vector is close enough
if ( abs(sumSquares - 1.0) > VECTOR_TOLERANCE )
{
float L = sqrt(sumSquares);

x /= L;
y /= L;
z /= L;
}
}

//return normalize copy of current vector
vector3f getNormalized()
{
//copy current quarternion and return conjugate
vector3f v(*this);
v.normalize();

return v;
}

//dot product
float dotProduct(const vector3f &v)
{
return x * v.x + y * v.y + z * v.z;
}

//shortcut for cross product
vector3f operator *(vector3f &v)
{
return crossProduct(v);
}

vector3f operator +(vector3f &v)
{
return vector3f(x + v.x, y + v.y, z + v.z);
}

//subtraction
vector3f operator -(vector3f &v)
{
return vector3f(x - v.x, y - v.y, z - v.z);
}
};

Quaternion Class:
class quaternion
{
public:

//members
//------------------------------------------------------------------------------

float x,y,z,w;

public:

//constructors
//------------------------------------------------------------------------------

//empty constructor
quaternion() : x(0), y(0), z(0), w(0) {}

//specific constructor
quaternion(float xi, float yi, float zi, float wi) : x(xi), y(yi), z(zi), w(wi) {}

//from vector
quaternion(vector3f v) : x(v.x), y(v.y), z(v.z), w(0) {}

//create from an angle and an arbitrary axis
quaternion(float degrees, vector3f axis)
{
float theta = (degrees / 180.0f) * PI;
float sinTheta = sin( theta / 2.0f );
float cosTheta = sin( theta / 2.0f );

//calculate quaternion values
x = axis.x * sinTheta;
y = axis.y * sinTheta;
z = axis.z * sinTheta;
w = cosTheta;
}

//methods
//------------------------------------------------------------------------------

//define quaternion multiplication operator
quaternion operator *(const quaternion &b)
{
quaternion result;

result.x = w * b.x + x * b.w + y * b.z - z * b.y;
result.y = w * b.y - x * b.z + y * b.w + z * b.x;
result.z = w * b.z + x * b.y - y * b.x + z * b.w;
result.w = w * b.w - x * b.x - y * b.y - z * b.z;

//return new quaterion
return(result);
}

//define vector multiplication operator
vector3f operator *(const vector3f &vi)
{
//create quaternion from normalized vector
vector3f vn(vi);
vn.normalize();
quaternion v(vn), result;

//apply multiplications
result = v * getConjugate();
result = *this * result;

return vector3f(result.x, result.y, result.z);
}

//normalise quaterion
void normalize()
{
float sumSquares = x * x + y * y + z * z + w * w;

if ( abs(sumSquares - 1.0) > QUATERNION_TOLERANCE )
{
float L = sqrt(sumSquares);

x /= L;
y /= L;
z /= L;
w /= L;
}
}

//return normalized copy of current quaternion
quaternion getNormalized()
{
//copy current quarternion and return conjugate
quaternion q(*this);
q.normalize();

return q;
}

//conjugate quaterion
void conjugate()
{
x = -x;
y = -y;
z = -z;
}

//return conjugate copy of current quaternion
quaternion getConjugate()
{
//copy current quarternion and return conjugate
quaternion q(*this);
q.conjugate();

return q;
}

//to vector
vector3f toVector()
{
return vector3f(x, y, z);
}

//to matrix (homogenous 4x4 matrix)
void toMatrix(float* matrix)
{
// First row
matrix[0]	= 1.0f - 2.0f * ( y * y + z * z );
matrix[1]	= 2.0f * (x * y + z * w);
matrix[2]	= 2.0f * (x * z - y * w);
matrix[3]	= 0.0f;

// Second row
matrix[4]	= 2.0f * ( x * y - z * w );
matrix[5]	= 1.0f - 2.0f * ( x * x + z * z );
matrix[6]	= 2.0f * (z * y + x * w );
matrix[7]	= 0.0f;

// Third row
matrix[8]	= 2.0f * ( x * z + y * w );
matrix[9]	= 2.0f * ( y * z - x * w );
matrix[10]	= 1.0f - 2.0f * ( x * x + y * y );
matrix[11]	= 0.0f;

// Fourth row
matrix[12]	= 0;
matrix[13]	= 0;
matrix[14]	= 0;
matrix[15]	= 1.0f;
}

};

Camera Class:
class camera
{
public:

//constructors
//------------------------------------------------------------------------------

camera(vector3f p, vector3f v, vector3f u) : position(p), view(v), up(u) {}

//methods
//------------------------------------------------------------------------------

void rotate(float h, float p)
{
GLfloat m[16];

// Make the Quaternions that will represent our rotations
quaternion qh(h,vector3f(1,0,0));
quaternion qp(p,vector3f(0,1,0));

// Combine the pitch and heading rotations and store the results in q
quaternion result = qp * qh;
result.toMatrix(m);

// Let OpenGL set our new prespective on the world!
glMultMatrixf(m);
}

void changePosition()
{

}
};

[Edited by - Coldon on October 17, 2007 3:32:14 AM]

### #2nicmenz  Members

Posted 17 October 2007 - 03:35 AM

use Cos if you write Cos ;)

float cosTheta = sin( theta / 2.0f ); // SHOULD BE COS

I don't think that's your only problem here :/ your methods look well implemented, though. could you describe the artifacts?

### #3nicmenz  Members

Posted 17 October 2007 - 03:42 AM

again it's me.

I never implemented a quat-based camera, but like the trackball it can be imagined as the task to rotate one starting point P to a target point P' on the unit sphere. to compute the quaternion you are looking for, you have to
a) compute the rotation axis (P.cross(P'))
b) the rotation angle (acos(P.dot(P'))

you can then use your constructor "quaternion(float degrees, vector3f axis)" to build the rotation Q that moves P into P'. last step should be to convert Q into a 4x4 matrix (I use glLoadMatrix()).

I don't know if it's necessary for a camera-implementation, but for the trackball you have to store the last computed quaternion as starting point for the next rotation...but not sure here.

greetx,
me.

### #4Coldon  Members

Posted 17 October 2007 - 07:14 AM

hey, lol - that was a dumb mistake with the cos function, dangers of cutting and pasting. I'll try what you suggest and get back to you tomorrow.

### #5dpoon  Members

Posted 17 October 2007 - 12:34 PM

I have a quaternion camera implementation in opengl and direct3d over on my site that might be of help to you.

### #6Coldon  Members

Posted 20 October 2007 - 10:33 PM

@dpoon:

your camera examples are excellent! I really like your coding style as its really similar to my own and its neat clear and concise!

That stupid typo was causing the problems, once i fixed it the quaternion rotations work as intended, i'm busy getting movement working but its proving to be a little tricky but i'll get it (eventually)...

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.