7. OpenGL Rotation and Translation (Version 2.0)

Introduction

Placing an object into your scene, in a 3D OpenGL environment can often be quite the achievement when you have everything in place. But just placing an object in your scene can be a little boring, so to get some character movement, or object interaction, it is necessary to both translate and rotate objects, which OpenGL makes quite simple for us to do.

Behind the extremely simple OpenGL calls that we are going to use, lays some pretty intense mathematics which I am not going to go into during these tutorials. In short, it all revolves around matrices and there is a matrix known as a transformation matrix. By multiplying together different transformation matrices, you get your final transformation. Now don’t get transformation confused with translation though, a translation moves the position of an object while a transformation is a combination of translate and rotate.

Rotation

What is rotation? May be something you are asking yourself if you are totally new to computer graphics, or even new to maths. It’s a little hard to describe without a picture or even an animation, but picture an object sitting still, not moving at all. And then it turns on the spot. This turning on the spot is called rotation, and is measured by the number of degrees (or radians) you have turned from your starting position along any three axis in a 3D scene. With the three main axis being the X, Y and Z axis.

Translation

Now that you know what rotation is, comes the question as to what exactly is translation? Translation is movement along any of the three axis in a 3D scene, for example, moving something the left is a translation on the X axis if you are looking straight on. In OpenGL, the length of translation is called units, and a unit has no definitive length, which can be confusing, but bear with me. On OpenGL.org, the question as to what the length of a unit is has been asked, and the official response is “Whatever you want it to be”. Depending on your implementation of your scene, a unit can be a centimetre, metre or a kilometre (or if you work in Imperial measurements, inches, yards and miles), it’s entirely up to you. Unfortunately this does have its downside, if you ever find yourself having to merge several OpenGL applications, they may be using different measurement systems, and you may find things are the wrong size. Trust me, it can be a pain and there are times when I wish there was an unofficial standard. Personally I tend to use 1 unit is equal to 1 metre. In OpenGL, movements either up, left or towards the camera are negative values in respect to the origin. While movements down, right or back away from the camera are all positive in respect to the origin.

Code

Compared to Direct3D, it is actually a lot simpler to do rotation and translation in OpenGL; you just have to keep your head around your current states of your transformation matrices at all times. Let’s start off with the code for a cube from our last tutorial.

Keep in mind that the animation in this tutorial may not be the smoothest, and I will address as to why and how to fix it in the double buffering tutorial.

Now let’s get started. I’m going to aim for a cube that bounces up and down in our window, while rotating around on the spot. To do this, we are going to need three variables. The first, which is a Boolean called movingUp, is going to determine if our cube is moving up, or moving down. Next up we need something to store the location of our object along the y axis (up and down), I am going to create a float called yLocation, and initialize it to 0.0f. It is important that you initialize it to 0.0f as anything drawn is originally drawn at the location (0,0,0) unless you have called glTranslate before placing it. Finally I am going to use another float, this one called yRotationAngle to store the angle in degrees of rotation for our cube along the y axis. These variables are just examples for this tutorial, and so far have nothing to do with OpenGL itself.

bool movingUp = false; // Whether or not we are moving up or down
float yLocation = 0.0f; // Keep track of our position on the y axis.

float yRotationAngle = 0.0f; // The angle of rotation for our object

Next, jump down to your “main” method where we are going to need to add a new line which allows variables inside of the display method to be updated. Because we are using GLUT, GLUT likes us to place all of our variables to be updated into an “idle” method, which handles nothing but calculations, but this isn’t as efficient as it is led out to be. Whilst it is not written in bold anywhere, it is possible to use your “display” method as GLUT’s “idle” method, and if you read the GLUT API in detail, you can see that this is possible as rendering in the “idle” method is possible. But what does this mean? Well it means that not only are all our variables updated every time our “display” method is called as if they were in an “idle” method, but we can also get the maximum frame rate possible by utilizing the “idle” methods resources. It may not give too much of an improvement, but I like to push out every last frame possible on my system.

Anyway, enough talking, lets add this one line of code:

int main (int argc, char **argv) {
…
glutDisplayFunc(display); // Tell GLUT to use the method "display" for rendering

glutIdleFunc(display); // Tell GLUT to use the method "display" as our idle method as well

glutReshapeFunc(reshape); // Tell GLUT to use the method "reshape" for reshaping
…
}

Good, now let’s get to updating our variables, so that we can then use them. We will do this at the end of our “display” method, just as a preference of mine, some people do all their processing at the start of their “display” method so that they can get their head around “these updates apply to this frame”, I look at it this way: “I have just done this, next I’m going to do this”. This code is quite basic, all we are doing is checking if the cube has gone below 3 units down or move above 3 units up, and toggling our Boolean variable to say if we need to move up or down, and then depending on this, we are either incrementing or decrementing our yLocation variable. I am also constantly increment my rotation angle, and when it gets above 360 degrees, or a full rotation, subtracting 360 degrees to reset it.

Just before the end of our “display” method, and after our glFlush method, place the lines:

if (movingUp) // If we are moving up
yLocation -= 0.005f; // Move up along our yLocation
else  // Otherwise
yLocation += 0.005f; // Move down along our yLocation

if (yLocation < -3.0f) // If we have gone up too far
movingUp = false; // Reverse our direction so we are moving down
else if (yLocation > 3.0f) // Else if we have gone down too far
movingUp = true; // Reverse our direction so we are moving up

yRotationAngle += 0.005f; // Increment our rotation value
if (yRotationAngle > 360.0f) // If we have rotated beyond 360 degrees (a full rotation)
yRotationAngle -= 360.0f; // Subtract 360 degrees off of our rotation

Now we are ready to start rotating and translating our object. This is extremely complicated, so give me some time to build it up.

Go back into your display method, and look for the lines glTranslatef(0.0f, 0.0f, -5.0f); and glutWireCube(2.0f); and make some space.

void display (void) {
…
glTranslatef(0.0f, 0.0f, -5.0f); // Push eveything 5 units back into the scene, otherwise we won't see the primitive

// Space for translation and rotation code

glutWireCube(2.0f); // Render the primitive
…
}

Great, make sure you have plenty of room, because the next two lines… Wait, the next *two* lines? Yes, I’m not joking, it only takes two lines, one the translation and the other for our rotation.

The only thing you need to make sure of here is that you do these calls in the correct order. If you translate and then rotation, the object will move into position and then rotate on the spot. If you rotate first, and then translate, your object will translate to its new position, but will then rotate in a giant circle around its starting position. To get the movement we described, add the following lines into the space you created above:

glTranslatef(0.0f, yLocation, 0.0f); // Translate our object along the y axis

glRotatef(yRotationAngle, 0.0f, 1.0f, 0.0f); // Rotate our object around the y axis

In OpenGL, this is all it takes to making your object rotate on the spot, and move, in this case up and down. It’s quite simple; the only tricky thing is keeping track of all your objects when you start working with fairly large complex scenes. If you try adding several objects in at the moment and moving them separately, you may have some trouble as OpenGL works on states and each translation and rotation is added to the previous. For getting around this, look at my tutorial on popping and pushing matrices J

If you have any questions, as usual you can always contact me at swiftless@gmail.com

Tutorial Code

#include <GL/glew.h> // Include the GLEW header file
#include <GL/glut.h> // Include the GLUT header file

bool* keyStates = new bool[256]; // Create an array of boolean values of length 256 (0-255)

bool movingUp = false; // Whether or not we are moving up or down
float yLocation = 0.0f; // Keep track of our position on the y axis.

float yRotationAngle = 0.0f; // The angle of rotation for our object

void keyOperations (void) {
if (keyStates[GLUT_KEY_LEFT]) { // If the left arrow key has been pressed
// Perform left arrow key operations
}
}

void display (void) {
keyOperations();

glClearColor(1.0f, 0.0f, 0.0f, 1.0f); // Clear the background of our window to red
glClear(GL_COLOR_BUFFER_BIT); //Clear the colour buffer (more buffers later on)
glLoadIdentity(); // Load the Identity Matrix to reset our drawing locations

glTranslatef(0.0f, 0.0f, -5.0f); // Push eveything 5 units back into the scene, otherwise we won't see the primitive

glTranslatef(0.0f, yLocation, 0.0f); // Translate our object along the y axis

glRotatef(yRotationAngle, 0.0f, 1.0f, 0.0f); // Rotate our object around the y axis

glutWireCube(2.0f); // Render the primitive

glFlush(); // Flush the OpenGL buffers to the window

if (movingUp) // If we are moving up
yLocation -= 0.005f; // Move up along our yLocation
else  // Otherwise
yLocation += 0.005f; // Move down along our yLocation

if (yLocation < -3.0f) // If we have gone up too far
movingUp = false; // Reverse our direction so we are moving down
else if (yLocation > 3.0f) // Else if we have gone down too far
movingUp = true; // Reverse our direction so we are moving up

yRotationAngle += 0.005f; // Increment our rotation value

if (yRotationAngle > 360.0f) // If we have rotated beyond 360 degrees (a full rotation)
yRotationAngle -= 360.0f; // Subtract 360 degrees off of our rotation
}

void reshape (int width, int height) {
glViewport(0, 0, (GLsizei)width, (GLsizei)height); // Set our viewport to the size of our window
glMatrixMode(GL_PROJECTION); // Switch to the projection matrix so that we can manipulate how our scene is viewed
glLoadIdentity(); // Reset the projection matrix to the identity matrix so that we don't get any artifacts (cleaning up)

gluPerspective(60, (GLfloat)width / (GLfloat)height, 1.0, 100.0); // Set the Field of view angle (in degrees), the aspect ratio of our window, and the new and far planes

glMatrixMode(GL_MODELVIEW); // Switch back to the model view matrix, so that we can start drawing shapes correctly
}

void keyPressed (unsigned char key, int x, int y) {
keyStates[key] = true; // Set the state of the current key to pressed
}

void keyUp (unsigned char key, int x, int y) {
keyStates[key] = false; // Set the state of the current key to not pressed
}

int main (int argc, char **argv) {
glutInit(&argc, argv); // Initialize GLUT
glutInitDisplayMode (GLUT_SINGLE); // Set up a basic display buffer (only single buffered for now)
glutInitWindowSize (500, 500); // Set the width and height of the window
glutInitWindowPosition (100, 100); // Set the position of the window
glutCreateWindow ("Your first OpenGL Window"); // Set the title for the window

glutDisplayFunc(display); // Tell GLUT to use the method "display" for rendering

glutIdleFunc(display); // Tell GLUT to use the method "display" as our idle method as well

glutReshapeFunc(reshape); // Tell GLUT to use the method "reshape" for reshaping

glutKeyboardFunc(keyPressed); // Tell GLUT to use the method "keyPressed" for key presses
glutKeyboardUpFunc(keyUp); // Tell GLUT to use the method "keyUp" for key up events

glutMainLoop(); // Enter GLUT's main loop
}
  • March 25, 2010
  • 29