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:
MATH_Object.h:
MATH_Structures.cpp
MATH_Object.cpp:
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();};#endif
MATH_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_4 MTransformation(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 point MTransformation apply(MTransformation A); // Apply transformation to transformation // if this is B, result is BA double& 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);};#endif
MATH_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;}
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement