Advertisement

Quaternion camera - STRANGE problem !

Started by July 31, 2002 04:49 AM
5 comments, last by moromete 22 years, 6 months ago
Hello all I am having trouble using my quaternion-based camera. A few days ago I searched through the forum''s archive for the quaternion topics and after i read all of them, i decided it will be best if i took a quaternion-math-class from NewDeal (he is/was a member of Nehe''s forum) and implement my camera over it. Everything worked like a charm... My camera would yaw, pitch and roll, it would move forward and backward. The problem i am reffering to appear into my eyes after i implemented mouse look and i started to move the camera in complete circles. My entire world appears to be slowly rolling into view altough i am not pressing any roll button and the rotation around Z axis is 0 ! This is a strange one ... I double-checked my calculations and everything seems to be OK. I am just hoping that there is a problem in Quaternion-math class and maybe one of you guys can spot it out. I am going crazy with this Please help me if you can... Moromete PS. I am posting here the source of quaternion-class and the source of my camera class

#ifndef QUATERNION_H
#define QUATERNION_H

#include <iostream.h>
#include <stdio.h>
#include <math.h>
using namespace std;

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();
};

//--------------------------------------------------------------

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;
	if (scale != 0) {
   		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];
	} else {
 		result[0] = 0;
  		result[1] = 1;
   		result[2] = 0;
		result[3] = 0;
	}
}
 
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;
}

#endif
 

#ifndef CAMERA_H
#define CAMERA_H

#include <iostream.h>
#include <stdio.h>
#include "quaternion.h"
using namespace std;

class CCamera {
    float speed;
	float X, Y, Z;
	float dirX, dirY, dirZ;
	float upX, upY, upZ;
	float leftX, leftY, leftZ;
	float posX, posY, posZ;
    float result[4];
    Quaternion Qold, Qtemp;
public:
	void Yaw(float yawval);
	void RollLeft();
	void RollRight();
	void Pitch(float pitchval);
	
    void RotateCamera();
    void Forward();
    void Backward();
    void MousePosition(int widthX, int heightY);
	void Reset();
    CCamera();
    ~CCamera();
};

//---------------------------------------------------------

CCamera::CCamera() {
	Reset();
	posX = 0.0f;
	posY = 0.0f;
	posZ = 0.0f;
    };

void CCamera::Reset() {
	X = 0.0f;
	Y = 0.0f;
	Z = 0.0f;
}

CCamera::~CCamera() {
    };

void CCamera::Yaw(float yawval) {
	Y = yawval;
}

void CCamera::RollLeft() {
	Z = 0.017453293f;
}

void CCamera::RollRight() {
	Z = -0.017453293f;
}

void CCamera:itch(float pitchval) {
	X = pitchval;
}

void CCamera::Forward() {
    speed = 0.01f;
}

void CCamera::Backward() {
    speed = -0.01f;
}

void CCamera::RotateCamera() {
		Qtemp.LoadIdentity();
		Qtemp.EulerToQuat(X, Y, Z);
		Qtemp.Normalize();
		Qtemp.MultQuat(&Qold);
		Qtemp.toAxisAngle(result);
		Qold.x = Qtemp.x;
		Qold.y = Qtemp.y;
		Qold.z = Qtemp.z;
		Qold.w = Qtemp.w;
		Qold.qLength = Qtemp.qLength;

		// Am inmultit noua orientare cu vechea si am salvat rezultatul

	    glRotatef((result[0]*180)/3.14159265359, result[1], result[2], result[3]);

	    // Acum am rotit si camera (lumea in jurul camerei)

	    dirX = 2.0 * (Qold.x * Qold.z - Qold.w * Qold.y);
	    dirY = 2.0 * (Qold.y * Qold.z + Qold.w * Qold.x);
	    dirZ = 1.0 - 2.0 * (Qold.x * Qold.x + Qold.y * Qold.y);
	    upX = 2 * (Qold.x * Qold.y - Qold.w * Qold.z);
	    upY = 1 - 2 * (Qold.x * Qold.x + Qold.z * Qold.z);
	    upZ = 2 * (Qold.y * Qold.z + Qold.w * Qold.x);
	    leftX = 1 - 2 * (Qold.y * Qold.y + Qold.z * Qold.z);
	    leftY = 2 * (Qold.x * Qold.y + Qold.w * Qold.z);
	    leftZ = 2 * (Qold.x * Qold.z - Qold.w * Qold.y);

	    // In dir... am vectorul orientare extras din Quaternion
	    // In up... am orientarea vectorului "sus"
	    // In left... am orientarea vectorului "stanga"

	    posX += dirX * speed;
	    posY += dirY * speed;
	    posZ += dirZ * speed;
	    glTranslatef(posX, posY, posZ);
	    
	    speed = 0.0f;

	    result[0] = 0;
	    result[1] = 0;
	    result[2] = 0;
	    result[3] = 0;
	    Qold.Normalize();
		Reset();
    };

void CCamera::MousePosition(int widthX, int heightY) {
	POINT mousePos;
	int middleX = widthX  >> 1;
	int middleY = heightY >> 1;

	GetCursorPos(&mousePos);						
	if( (mousePos.x == middleX) && (mousePos.y == middleY) ) return;
	SetCursorPos(middleX, middleY);
	Yaw (-(float)( (middleX - mousePos.x) ) / 500.0f);		
	Pitch (-(float)( (middleY - mousePos.y) ) / 500.0f);		
};

#endif
 
Hmmm...I sat down in my corner and i found out that this behaviour
is actually a correct one from one point of view !
I have done the folowing :
0) look straight in front of me
1) turn my head so that i will look to my right
2) raise my head up untin i can see the ceiling
3) turn my head so that i can see the first image which was
at point 0) in front of me.

After 3) i found myself with my left ear pointing down to the floor !
Which is, the entire "world" was rotated in front of me by 90 degrees
clockwise. This is like i have rolled the camera by 90 degrees counter-clockwise
in the first place.

This looks quite normal, but now i will have to fix my camera motion
The question is how?
Have any of you encountered something like this before ?
Advertisement
Hmm, I''m not sure I got the idea of what your problem was exactly, and to be completely honest I was too lazy to read through all that code. It looked quite complex.

I wild guess would be that your rotations are not exactly as you would like them to be because of differences between the coordinate system assumed in your math and the coordinate system used by OpenGL.

In mathematics, a coordinate system is usually defined with the first axis (X) pointing "outwards" (from the blackboard); the second axis (Y) pointing to the right and the third axis (Z) pointing upwards (a so-called right-handed coordinate system). The "roll" is around the third axis, the "pitch" around the second axis and the "yaw" is around the first axis.

But OpenGL has rotated this mathematical coordinate system 90 degrees around the second axis and then 90 degrees around the third axis. That is, the third axis (Z) in OpenGL is pointing outwards from the screen towards the viewer, the second axis (Y) is now pointing upwards and the first axis (X) is pointing to the right. The coordinate system is still right-handed, but the axises has changed position.

The result is that if you try to do a "mathematical pitch," that is pitching by rotating around the second axis as before, it will look like a roll because the Y axis is now standing where the Z axis used to be. And so on and so forth with "taw" and "roll" too.

Did that actually make any sense at all???

The simplest way (that I can think of) to make a FPS-like camera would be to use just four camera variables:


position = (x, y, z)
roty = rotation around OpenGL''s Y axis (defines the orientation, like the angle of a compass needle)
rotx = rotation around OpenGL''s X axis (defines how much you are looking up/down)
rotz = rotation around OpenGL''s Z axis (defines how much you are leaning to either side)


And then draw your scene as follows:


  bool RenderOpenGLScene(){  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  glLoadIdentity();  /* Apply camera to scene... */  camera->applyToScene();  // draw stuff......}  


Where camera->applyToScene() is defined as follows:


  Camera::applyToScene(){void FPSCamera::applyToScene(){  glRotatef(this->rotz, 0.0, 0.0, 1.0);  glRotatef(this->rotx, 1.0, 0.0, 0.0);  glRotatef(this->roty, 0.0, 1.0, 0.0);  glTranslatef(this->position[0],                this->position[1],                this->position[2]);}  


Rotation is then really simple: simply +/- the amount of rotation to one of the three rotation angles (like the up/down displacement of the mouse determine how much you add/substract to the rotx variable). Moving the camera in the X,Z plane is only a bit more complex: Add or substract to the x position and the z position, taking the roty (compass needle-like rotation) into account with a couple of cos() and sin() calls. If you want to have full 3D movement, you need to adjust the y position of the camera too, using the rotx variable to tell you how much.
Thank you very much for your answer
I managedto solve my problem Because I am not doing
a space simulator, i really don''t need a quaternion-powered
camera. I need camera morion which suffer from Gimbal Lock
which in my case is quite usefull (i don''t know if anyone
said something like this before but this is the real truth )

What i explained in my original post is actually the correct
way in wich the camera will move in space. My code was OK
The values for my left and up vectors were wrong though. I
recalculated the Quat2Matrix and i discovered that the original
code misplaced some signs (+/-).

I implemented a camera motion wich suffers from Gimbal Lock problem and now my motion is correct.

Thank you again for your answer.

moromete
i''m just happy for you that you problem got solved.

---
shurcool
wwdev
Even though your problem is solved, i thought you might like to know that it looks although you have a memory leak in EulerToQuat() - there are two new''s without deletes.
Advertisement
Yup...i know...
It was fixed in the mean time...i ran a check on all the code and i came up with this
Thank you anyway

moromete

This topic is closed to new replies.

Advertisement