To main page | 3dengine.org
Spectator
Spectator code or
free-fly camera is code that let's you look around, it's when you move mouse right to look right, up - to look up and use W-S keys to move back and forwards and A-D to strafe right and left.
The controls can be different, but idea stays the same.
There are quite a few approaches to this. Most of approaches rely on storing some angles and then loading
identity matrix each frame and via series of glRotate/glTranslate and vector/matrix math - setting the camera. I have another approach, which doesn't require resetting (loading identity) of camera each frame.
gluLookAt won't help us here as there's a lot of math to calculate looking right or up with mouse.
Looking around
With my approach you need only to store x,y,z of camera. OpenGL will keep track of the rest.
So, let's say mouse moved
dx pixels to the right and
dy pixels up. (If dx is negative, then mouse moved left, so is with dy).
glRotate(model, -dx, 0,1,0);
glRotate(model, dy, 1,0,0);
update_matrix();
update_matrix is a function which I will describe later. You can change the signs (- or +) to find which feels more natural for you (see
Inverting Y of mouse). These two commands are rotating around LOCAL axes of camera - dx around local up-axis, dy around local right-axis.
Move forwards-backwards (W-S keys)
Again supply
dx as the amount we want to move forward (negative dx for backward)
GLfloat model[16];
glGetFloatv(GL_MODELVIEW_MATRIX, &model);
x = x - model[2]*dx;
y = y - model[6]*dx;
z = z - model[10]*dx;
update_matrix();
model[2],model[6],model[10] is actually camera's back vector in global coordinates, see
extracting right, up and back vectors from modelview matrix for explanation.
model is the
modelview matrix, which can be read via glGetFloatv(GL_MODELVIEW_MATRIX, &model) in C++ or model = glGetFloat(GL_MODELVIEW_MATRIX) in PyOpenGL.
x,y,z are the variables we need to store (camera position, actually the center of
image plane).
Strafe (A-D keys)
How do we strafe?
GLfloat model[16];
glGetFloatv(GL_MODELVIEW_MATRIX, &model);
x = x - model[0]*dx;
y = y - model[4]*dx;
z = z - model[8]*dx;
x = x - model[1]*dy;
y = y - model[5]*dy;
z = z - model[9]*dy;
update_matrix();
update_matrix()
update_matrix is a function that recalculates position in new coordinates.
GLfloat model[16];
glGetFloatv(GL_MODELVIEW_MATRIX, &model);
model[12]=0;
model[13]=0;
model[14]=0;
model[15]=1;
glTranslate(model, -x,-y,-z);
// compensate roll
glLoadMatrix(model);
Compensating roll
The last thing we need to do is to make sure camera is vertical always (which is a major annoyance and the above method will induce roll). Luckily it's easy to fix. See the "
Roll" article, part "Compensating roll" and make corrections to "update_matrix" function above.
Basically you need to:
glRotate(atan2(-model[4], model[5]) * K, 0,0,1);
K can usually be set to 1.0, but depending on the programming language you might need to set it to
radians-to-degrees constant.
Full code
You can see example implementation in
Spectator (PyOpenGL) page.
Further work
The only "problem" is when camera approaches pitch -90 or 90 (looking exactly up or down) - the swings would become wild around vertical axis if user tries to move mouse further down. You can try to limit him to pitch of -89 to 89 - that won't be very noticeable, but relieve him of headaches. (See
pitch for calculation of pitch).