   Universal Camera! Lesson #49
  Hi, I am trying to make an opengl game where the user can rotate in *any* direction and then move in the direction he is pointing. In other words, a game where the user *is* the camera, as in a first person shooter, but where the user can rotate on any axis fully (and not with just the usual limiation of 180 degrees on the y-axis and 0 degrees on the z-axis). I have done some research which seems to indicate that the best way this is to use quaternions so that the camera avoids gimbal lock. I have read various articles on how to create a quaternion camera. But I think it would be *very* useful if there were a basic tutorial on the Nehe site which explained how to do this properly. I have talked to a half dozen different people who have wanted to implement this, and *theoretically* know how to do it, but have never taken the time to do it because it was not explained in a simple way with good *working* code to help guide them. Anyhow, it seems like it would be a perfect tutorial for the Nehe site, and would inspire many programmers. It's seems like it might even be a simple extension to the ArcBall lesson-- I'm sure there are lots of people who could whip a tutorial up in a couple of hours. You will be famous and revered if you make these lessons described below!*Lesson 49A*As an example the lesson could have a spaceship/camera flying through space, where there is no limiting concept of up or down. Six keys on the keyboard could map to rotating the camera on the x, y, and z axes. The space bar could "thrust" the spaceship/camera in the direction he is facing toward different planets or whatever.*Lesson 49B*Also, a good and I think important example (and probably a simple one for someone who knows what they are doing!) would be to have a camera/user be facing a particular point (say, the origin) and have keys on the keyboard mapped to rotating/orbiting the camera to the left/right and up/down directions *around a sphere* at some distance from the origin. Two other keys could control the distance toward or away from the center of sphere (the origin). In this example, the camera would *always* be facing the origin, and the camera would always be oribiting/rotating on the surface of a sphere.I would be grateful if this lesson existed, and if it does actually exist somewhere, please direct me to it! Also, if I described the lessons unclearly let me know.-spiral_jetty   
  Oooh, very interesting challenge. Spent a bit of time on it, but eventually I whipped out a way to track the camera mathematically, before I realized I can just retrieve the matrix stored by OGL (d'oh!).Here's my version, which stores a matrix corresponding to the axis of the camera, and moving forward just takes an arbitrary vector (0 0 1 1) to move forward by, and moves the position.The class has a function rotatef(double, double, double, double) which works just as the normal glRotatef functions - call it as rotatef(90,0,1,0) and it will rotate the camera 90 degrees on the y-axis.The move(double) function will move the camera that many units in the direction the camera is currently pointing.And yes, all this can be easily optimized.MATH_Structures.h:#ifndef MATH_STRUCTURES#define MATH_STRUCTURES#include <math.h>const double piover180 = 0.017453292519943295769236907684886;class MPoint{public:double x;double y;double z;double t;MPoint operator+(const MPoint c);MPoint operator-(const MPoint c);MPoint operator*(const double c);MPoint operator ~(); /* Normalize */double operator !(); /* length */double& operator[](const int i);MPoint();};#endifMATH_Object.h:#ifndef MATHOBJECT#define MATHOBJECT#include "MATH_Structures.h"#include <math.h>typedef enum { IDENTITY, ROTATE } tType;class MTransformation{public:double data[16];MTransformation(); // Initialize to I_4MTransformation(tType type, double a = 0.0, double b = 0.0, double c = 0.0, double d = 0.0);MPoint apply(MPoint a); // Apply transformation to pointMTransformation apply(MTransformation A);// Apply transformation to transformation// if this is B, result is BAdouble& operator[](const int i);};class MObject{public:MPoint position;MTransformation axis;MPoint direction();MPoint upvector();MPoint rightvector();void move(double t);void rotate(double degrees, double x, double y, double z);};#endifMATH_Structures.cpp#include "Math_Structures.h"MPoint::MPoint(){x = 0;y = 0;z = 0;t = 1;}MPoint MPoint::operator +(const MPoint c){MPoint p;p.x = x + c.x;p.y = y + c.y;p.z = z + c.z;return p;}MPoint MPoint::operator -(const MPoint c){MPoint p;p.x = x - c.x;p.y = y - c.y;p.z = z - c.z;return p;}MPoint MPoint::operator *(const double c){MPoint p;p.x = x * c;p.y = y * c;p.z = z * c;return p;}MPoint MPoint::operator ~(){MPoint p;MPoint c = *this;double l = c.x * c.x + c.y * c.y + c.z * c.z;l = sqrt(l);p.x = c.x / l;p.y = c.y / l;p.z = c.z / l;return p;}double MPoint::operator ! (){double l = x * x + y * y + z * z;l = sqrt(l);return l;}double& MPoint::operator [](int i){if (i == 0)return x;if (i == 1)return y;if (i == 2)return z;return t;}MATH_Object.cpp:#include "MATH_Object.h"MTransformation::MTransformation(){for (int i = 0; i < 16; i++){data = ((i % 4) == (i / 4));}}MTransformation::MTransformation(tType type, double a, double b, double c, double d){if (type == IDENTITY){for (int i = 0; i < 16; i++){data = ((i % 4) == (i / 4));}}else if (type == ROTATE){double cD = cos(a);double sD = sin(a);double x = b;double y = c;double z = d;data[0] = cD + (1 - cD) * x * x;data[1] = (1 - cD) * x * y - sD * z;data[2] = (1 - cD) * x * z + sD * y;data[3] = 0;data[4] = (1 - cD) * y * x + sD * z;data[5] = cD + (1 - cD) * y * y;data[6] = (1 - cD) * y * z - sD * x;data[7] = 0;data[8] = (1 - cD) * z * x - sD * y;data[9] = (1 - cD) * z * y + sD * x;data[10] = cD + (1 - cD) * z * z;data[11] = 0;data[12] = 0;data[13] = 0;data[14] = 0;data[15] = 1;}}double& MTransformation::operator [](int i){return data;}MPoint MTransformation::apply(MPoint a){MPoint m;m[0] =data[0] * a[0] + data[1] * a[1] +data[2] * a[2] + data[3] * a[3];m[1] =data[4] * a[0] + data[5] * a[1] +data[6] * a[2] + data[7] * a[3];m[2] =data[8] * a[0] + data[9] * a[1] +data[10] * a[2] + data[11] * a[3];m[3] =data[12] * a[0] + data[13] * a[1] +data[14] * a[2] + data[15] * a[3];return m;}MTransformation MTransformation::apply(MTransformation A){MTransformation m;m[0] = data[0] * A[0] + data[1] * A[4] + data[2] * A[8] + data[3] * A[12];m[1] = data[0] * A[1] + data[1] * A[5] + data[2] * A[9] + data[3] * A[13];m[2] = data[0] * A[2] + data[1] * A[6] + data[2] * A[10] + data[3] * A[14];m[3] = data[0] * A[3] + data[1] * A[7] + data[2] * A[11] + data[3] * A[15];m[4] = data[4] * A[0] + data[5] * A[4] + data[6] * A[8] + data[7] * A[12];m[5] = data[4] * A[1] + data[5] * A[5] + data[6] * A[9] + data[7] * A[13];m[6] = data[4] * A[2] + data[5] * A[6] + data[6] * A[10] + data[7] * A[14];m[7] = data[4] * A[3] + data[5] * A[7] + data[6] * A[11] + data[7] * A[15];m[8] = data[8] * A[0] + data[9] * A[4] + data[10] * A[8] + data[11] * A[12];m[9] = data[8] * A[1] + data[9] * A[5] + data[10] * A[9] + data[11] * A[13];m[10] = data[8] * A[2] + data[9] * A[6] + data[10] * A[10] + data[11] * A[14];m[11] = data[8] * A[3] + data[9] * A[7] + data[10] * A[11] + data[11] * A[15];m[12] = data[12] * A[0] + data[13] * A[4] + data[14] * A[8] + data[15] * A[12];m[13] = data[12] * A[1] + data[13] * A[5] + data[14] * A[9] + data[15] * A[13];m[14] = data[12] * A[2] + data[13] * A[6] + data[14] * A[10] + data[15] * A[14];m[15] = data[12] * A[3] + data[13] * A[7] + data[14] * A[11] + data[15] * A[15];return m;}void MObject::move(double t){MPoint forward;forward[2] = 1.0;forward = axis.apply(forward);position = position + (forward * t);}void MObject::rotate(double degrees, double x, double y, double z){MTransformation t(ROTATE, degrees * piover180, x,y,z);axis = axis.apply(t);}MPoint MObject::direction(){MPoint forward;forward[2] = 1.0;forward = axis.apply(forward);return forward;}MPoint MObject::upvector(){MPoint forward;forward[1] = 1.0;forward = axis.apply(forward);return forward;}MPoint MObject::rightvector(){MPoint forward;forward[0] = 1.0;forward = axis.apply(forward);return forward;}