#### Archived

This topic is now archived and is closed to further replies.

# Desperate Quaternions

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

## Recommended Posts

I have recently written a quaternion implementation due to the fact that the tutorials here do not allow rotation about any vector. I don't know if it indeed works because there is a warning that says I should not multiply quats with different coordinate systems but that, I think, is precisely what I did but it seemed to run well! I have the code below; could anyone please evaluate? By the way, I would like to ask if there is any good camera classes available? I would very much like to know about it before I start my first project.

//== CODE ======================================================

//======== based on Lesson 01 ==================================

//I call, in DrawGLScene():

//---

// c is the CCamera class below

c.Rotate(1, 0, 0, -0.02);
c.Rotate(1, 0, 0, 0.02);
c.Rotate(0, 1, 0, 0.02);
c.Rotate(0, 1, 0, -0.02);
c.Rotate(0, 0, 1, 0.02);
c.Rotate(0, 0, 1, -0.02);
c.Translate(0, 0, -0.1);
if (keys[VK_SUBTRACT])
c.Translate(0, 0, 0.1);

// ...

c.Apply()

//---

//And the related definitions:

//---

// CCamera.h

#ifndef CCAMERA_H
#define CCAMERA_H

#include "CVector3.h"
#include "CQuat.h"

class CCamera {
private:
CQuaternion m_q;										// Rotation

CVector3 m_pos;											// Position

CVector3 m_ia, m_ja, m_ka;								// Unit Axes

static const CVector3 I_AXIS, J_AXIS, K_AXIS;
static const double PI;

public:
CCamera();												// Constructor

// Transformations

void Rotate(double, double, double, double);			// Rotate about a vector

void Translate(double, double, double, bool = false);	// Translate orthogonally

// Effect changes

void Apply() const;										// Apply changes

void LookAt(double, double, double) const;				// Look at a point

};

{
return angle * 180 / PI;
}

#endif // CCAMERA_H

-----------------------------------------

// CCamera.cpp

// Camera control in OpenGL

#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>

#include <math.h>
#include "CVector3.h"
#include "CQuat.h"
#include "CCamera.h"

const CVector3 CCamera::I_AXIS(1, 0, 0);
const CVector3 CCamera::J_AXIS(0, 1, 0);
const CVector3 CCamera::K_AXIS(0, 0, 1);

const double CCamera::PI = 3.141592653589793238462643383279502884197;

CCamera::CCamera()
{
m_q = CQuaternion(1, 0, 0, 0);
m_pos = CVector3(0, 0, 0);
m_ia = I_AXIS;
m_ja = J_AXIS;
m_ka = K_AXIS;
}

void CCamera::Rotate(double i, double j, double k, double angle)
{
m_q = m_q * CQuaternion(CVector3(i, j, k), angle);
m_ia = m_q.Rotate(I_AXIS);
m_ja = m_q.Rotate(J_AXIS);
m_ka = m_q.Rotate(K_AXIS);
}

void CCamera::Translate(double i, double j, double k, bool global)
{
if (global)
m_pos = m_pos + CVector3(i, j, k);
else
m_pos = m_pos + m_ia * i + m_ja * j + m_ka * k;
}

void CCamera::Apply() const
{
double dSinHalf = sqrt(1 - (m_q.s) * (m_q.s));
glMatrixMode(GL_MODELVIEW);
glRotated(RadToDeg(-acos(m_q.s)) * 2, m_q.i / dSinHalf, m_q.j / dSinHalf, m_q.k / dSinHalf);
glTranslated(-m_pos.i, -m_pos.j, -m_pos.k);
}

void CCamera::LookAt(double i, double j, double k) const
{
glMatrixMode(GL_MODELVIEW);
gluLookAt(m_pos.i, m_pos.j, m_pos.k, i, j, k, m_ja.i, m_ja.j, m_ja.k);
}

---------------------------------------

/********************************

CVector3 and CQuaternion are just
basic 3d vectors and quaternions

********************************/

// CVector3.h

#ifndef CVECTOR3_H
#define CVECTOR3_H

class CVector3
{
public:
double i, j, k;										// Components

CVector3();											// Zero Vector

CVector3(double, double, double);					// Vector

const CVector3 operator + (const CVector3&) const;	// Addition

const CVector3 operator - (const CVector3&) const;	// Subtraction

const double operator * (const CVector3&) const;	// Dot product

const CVector3 operator % (const CVector3&) const;	// Cross product

const CVector3 operator * (const double&) const;	// Scalar Product

const CVector3 operator / (const double&) const;	// Scalar Division

const double Norm() const;							// Norm

const CVector3 Unit() const;						// Unit Vector

};

#endif // CVECTOR3_H

-----------------------------------------

// CVector3.cpp

// Three-dimensional vector

#include <math.h>
#include "CVector3.h"

CVector3::CVector3()
{
i = j = k = 0.0;
}

CVector3::CVector3(double i, double j, double k)
{
this->i = i;
this->j = j;
this->k = k;
}

const CVector3 CVector3::operator + (const CVector3 &v) const
{
return CVector3(i + v.i, j + v.j, k + v.k);
}

const CVector3 CVector3::operator - (const CVector3 &v) const
{
return CVector3(i - v.i, j - v.j, k - v.k);
}

const double CVector3::operator * (const CVector3 &v) const
{
return i * v.i + j * v.j + k * v.k;
}

const CVector3 CVector3::operator % (const CVector3 &v) const
{
return CVector3(j * v.k - k * v.j, k * v.i - i * v.k, i * v.j - j * v.i);
}

const CVector3 CVector3::operator * (const double &d) const
{
return CVector3(i * d, j * d, k * d);
}

const CVector3 CVector3::operator / (const double &d) const
{
return CVector3(i / d, j / d, k / d);
}

const double CVector3::Norm() const
{
return sqrt(i * i + j * j + k * k);
}

const CVector3 CVector3::Unit() const
{
return *this / this->Norm();
}

-----------------------------------------

// CQuat.h

#ifndef CQUAT_H
#define CQUAT_H

#include "CVector3.h"

class CQuaternion
{
public:
double s, i, j, k;											// Components

CQuaternion();												// Multiplicative Identity

CQuaternion(double, double, double, double);				// Direct initialisation

CQuaternion(double, CVector3);								// Initialise with scalar and vector

CQuaternion(CVector3, double);								// Rotation about unit vector

const CQuaternion operator + (const CQuaternion&) const;	// Addition

const CQuaternion operator - (const CQuaternion&) const;	// Subtraction

const CQuaternion operator * (const CQuaternion&) const;	// Multiplication

const CQuaternion operator / (const CQuaternion&) const;	// Division

const CQuaternion operator * (const double&) const;			// Scalar Multiplication

const CQuaternion operator / (const double&) const;			// Scalar Division

const double Norm() const;									// Norm

const CQuaternion Conjugate() const;						// Conjugate

const CQuaternion Inverse() const;							// Inverse

const CVector3 Rotate(const CVector3) const;				// Rotate a point

};

#endif // CQUAT_H

-----------------------------------------

// CQuat.cpp

// A quaternion

#include <math.h>
#include "CVector3.h"
#include "CQuat.h"

CQuaternion::CQuaternion()
{
s = 1;
i = j = k = 0;
}

CQuaternion::CQuaternion(double s, double i, double j, double k)
{
this->s = s;
this->i = i;
this->j = j;
this->k = k;
}

CQuaternion::CQuaternion(double s, CVector3 v)
{
this->s = s;
i = v.i;
j = v.j;
k = v.k;
}

CQuaternion::CQuaternion(CVector3 v, double angle)
{
CVector3 uv = v.Unit();
double dSinHalf = (double) sin(angle / 2);
s = (double) cos(angle / 2);
i = uv.i * dSinHalf;
j = uv.j * dSinHalf;
k = uv.k * dSinHalf;
}

const CQuaternion CQuaternion::operator + (const CQuaternion &q) const
{
return CQuaternion(s + q.s, i + q.i, j + q.j, k + q.k);
}

const CQuaternion CQuaternion::operator - (const CQuaternion &q) const
{
return CQuaternion(s - q.s, i - q.i, j - q.j, k - q.k);
}

const CQuaternion CQuaternion::operator * (const CQuaternion &q) const
{
return CQuaternion(
s * q.s - i * q.i - j * q.j - k * q.k,
s * q.i + i * q.s + j * q.k - k * q.j,
s * q.j - i * q.k + j * q.s + k * q.i,
s * q.k + i * q.j - j * q.i + k * q.s);
}

const CQuaternion CQuaternion::operator / (const CQuaternion &q) const
{
return (*this) * q.Inverse();
}

const CQuaternion CQuaternion::operator * (const double &d) const
{
return CQuaternion(s * d, i * d, j * d, k * d);
}

const CQuaternion CQuaternion::operator / (const double &d) const
{
return CQuaternion(s / d, i / d, j / d, k / d);
}

const double CQuaternion::Norm() const
{
return (double) sqrt(s * s + i * i + j * j + k * k);
}

const CQuaternion CQuaternion::Inverse() const
{
return this->Conjugate() / (this->Norm() * this->Norm());
}

const CQuaternion CQuaternion::Conjugate() const
{
return CQuaternion(s, -i, -j, -k);
}

const CVector3 CQuaternion::Rotate(const CVector3 pt) const
{
CQuaternion q = (*this) * CQuaternion(0, pt) * this->Conjugate();
return CVector3(q.i, q.j, q.k);
}

===========================================================


Sorry if I am wordy but the code is long Thanks in advance [edited by - singulab on January 7, 2004 5:11:50 AM] [edited by - singulab on January 7, 2004 9:20:42 AM]

##### Share on other sites
Hey I don''t have time to look at this now, but when posing code its nice if you put it between some [*source] [*/source] (dont use the *''s.

##### Share on other sites
Following an afterthought this is proposed:

// global: true if rotating in global coordinate systemvoid CCamera::Rotate(double i, double j, double k, double angle, bool global){	if (global)		m_q = m_q * CQuaternion(CVector3(i, j, k), angle);	else		m_q = m_q * CQuaternion(m_ia * i + m_ja * j + m_ka * k, angle);	m_ia = m_q.Rotate(I_AXIS);	m_ja = m_q.Rotate(J_AXIS);	m_ka = m_q.Rotate(K_AXIS);}

Please kindly evaluate it and tell me which one is correct.

Thanks

##### Share on other sites
One thing you are missing in your multiply code is the normalisation of the quaternion. You need to make sure that you keep the quaternion normalised at all times otherwise the quaternion is not representing a rotation(you are allowing an extra Degree of freedom which is eliminated by keeping the quaternion as a unit quaternion).

James

##### Share on other sites
Actually my quat is clever enough to convert all vectors to unit ones . cf.

CQuaternion::CQuaternion(CVector3 v, double angle){	CVector3 uv = v.Unit();	double dSinHalf = (double) sin(angle / 2);	s = (double) cos(angle / 2);	i = uv.i * dSinHalf;	j = uv.j * dSinHalf;	k = uv.k * dSinHalf;}

##### Share on other sites
quote:
Original post by singulab
Actually my quat is clever enough to convert all vectors to unit ones . cf.

CQuaternion::CQuaternion(CVector3 v, double angle){	CVector3 uv = v.Unit();	double dSinHalf = (double) sin(angle / 2);	s = (double) cos(angle / 2);	i = uv.i * dSinHalf;	j = uv.j * dSinHalf;	k = uv.k * dSinHalf;}

But that''s not the constructor you are using to create the result of the product is it? I would suggest that you modify the constructor that taks 4 doubles to normalize the result i.e. divide each of the components by the sqrt of the sum of the squares of the components (your Norm() function). I think you''ll find that this is where the quaternions are becoming denormalised and hence not representing the rotations you''d expect.

##### Share on other sites
jamessharpe,

I hope I understand that you mean I should have unit quaternions instead of unit vectors? See below; the problem seems to persist.

I have another point to make. If you have a flight simulator (I have only one by the name of Orbiter), the result of using global is like what you would normally expect when flying, but I expect it to be like rotating always w.r.t. the three global axes. If global is false then after you pitch a bit then bank, the camera will trace out a mystical curve and keeps rolling. Don't know what caused it; high school math does not include quaternions.

It is hard to impress on the effect unless with an executable. Maybe you can try http://personal.nbnet.nb.ca/daveg/opengl/quatdemo/index.html, I believe the effect is similar to the rotations without quats - very unsmooth with pauses and rolls, not at all like an aircraft.

CQuaternion::CQuaternion(CVector3 v, double angle){	CVector3 uv = v.Unit();	double dSinHalf = (double) sin(angle / 2);	s = (double) cos(angle / 2);	i = uv.i * dSinHalf;	j = uv.j * dSinHalf;	k = uv.k * dSinHalf;	*this = this->Unit();}const CQuaternion CQuaternion::Unit() const{	return *this / this->Norm();}void CCamera::Rotate(double i, double j, double k, double angle, bool global){	if (global)		m_q = m_q * CQuaternion(CVector3(i, j, k), angle);	else		m_q = m_q * CQuaternion(m_ia * i + m_ja * j + m_ka * k, angle);	m_ia = m_q.Rotate(I_AXIS);	m_ja = m_q.Rotate(J_AXIS);	m_ka = m_q.Rotate(K_AXIS);}

singulab

[edited by - singulab on January 10, 2004 7:59:30 AM]

##### Share on other sites
Actually, should the code be like

void CCamera::Rotate(double i, double j, double k, double angle, bool global){	if (global)		m_q = m_q * CQuaternion(m_ia * i + m_ja * j + m_ka * k, angle);	else	{		CVector3 v(i, j, k);		// rotate the internal vector to global representation		// IMPORTANT: I don''t know how to do this. The only method I have		// is to solve 3 simultaneous equations, which I cannot handle.		v = m_q.Conjugate().Rotate(v);		// Maybe it should be		// 	v = m_q.Inverse().Rotate(v);		m_q = m_q * CQuaternion(v, angle);	}	m_ia = m_q.Rotate(I_AXIS);	m_ja = m_q.Rotate(J_AXIS);	m_ka = m_q.Rotate(K_AXIS);}

It is just a wild guess that doesn''t work but something similar may work.

1. 1
2. 2
Rutin
25
3. 3
4. 4
JoeJ
18
5. 5

• 14
• 22
• 11
• 11
• 9
• ### Forum Statistics

• Total Topics
631764
• Total Posts
3002210
×