Jump to content
  • Advertisement
Sign in to follow this  
Coldon

OpenGL Quaternion Camera Implementation

This topic is 3898 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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
	//------------------------------------------------------------------------------

	//addition
	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);
	}

	//addition
	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]

Share this post


Link to post
Share on other sites
Advertisement
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?



Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


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

Share this post


Link to post
Share on other sites
@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)...

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!