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).

**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)

**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?

## update_matrix()

update_matrix is a function that recalculates position in new coordinates.

## 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:

## 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).

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.

So, let's say mouse moved

glRotate(model, -dx, 0,1,0); glRotate(model, dy, 1,0,0); update_matrix();

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.

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

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

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.