Thursday 11 November 2010

Multiple transformations in OpenGL

I bet a lot of you remembers “SuperOff-Road”, magnificient game for 8 and 16-bit consoles, in which you were driving Jeeps over a track. It was rendered from the top view, meaning that you were forced to see the whole track in third person. Your car used to accelerate with the D-pad up arrow (or a button, I can't remeber anything) and steer with the left and right ones.
The sprite was rotated and then translated following the newly created axis. Same story for the next frame. We were used to steer in a much simpler way at the time (I was maybe 7 when I played the first time and I hadn't played 'Super sprint'), so the new control system took a lot to be mastered, but was much much more actual. The game was fun, the multiplayer was a blast, the satisfaction reached new highs for the racing game genre.
How can we do it in an openGL environment?
We have a car seen from the top. We want to rotate it and translate it. Trivial. Not in OpenGL, that's why I'm writing this entry.
OpenGL works with an axis-oriented system, which means you'll be drawing shapes after having determined where their center is located. In case of multiple transformations, we first rotate the axis and then translate it, before drawing the car. The whole code is encapsulated between push and pop, to restrict the movement to that specific car.

glPushMatrix();
    glTranslatef(0,y,0.0);
    glRotatef(angle,0,0,1.0);
    drawCar():
glPopMatrix();

This way, unfortunately, the translation is from the center of the screen, not around our new car axis, which means the movement is not the actual one.
Before the push, for each frame, we have the axis centered back into the orygin, and that implies our translation will be calculated on that and not on the new axis.
Rotation is in this case correct, because happens after the axis has been translated.

To have the axis coming to the push exactly the same as the one after the car drawing, we have two ways of proceeding: apply an inverse transformation (much more painful than what you might think) or apply trigonometry.
In fact, the translation from the screen centered axis (in 0,0) to the new car axis can be defined in terms of cosin or sin. Imagine to translate your axis from the original one to the new one for each frame. This distance will be a vector of length l, whose x and y components are dx and dy, following the angle alpha you provide for rotation.

dx = sin(alpha) * speed                                             dy = cos(alpha) * speed

speed is l based on each frame. If in F0 the car is in (0,0) and in F1 it is in (0,1), this means that speed = 1.
So, we have to simply pass dx and dy instead of 0 and y to the translation, to have it happening in the new axis based on the car position.

glTranslatef(dx,dy,0.0);

Just remember to convert the angle from degrees to radius since openGl uses radius!
A solution like this is not present on the Internet, at least at this simple level, as far as I know, so I hope you find it useful. I know it's not very well documented, but I'll be updating it as well as granting answers to who wants to ask something about it, provided I can answer properly...