Ultimately we would like our animations to be system independent (assuming the user's system has sufficient graphics capabilities) by rendering based on time, e.g. at a fixed frame rate. Fortunately OpenGL provides a timer callback for time-based events, or alternatively we can use the idle callback with a time check, to do time-based rendering.

0. Getting Started

Download CS370_Lab09.zip, saving it into the labs directory.

Double-click on CS370_Lab09.zip and extract the contents of the archive into a subdirectory called CS370_Lab09

Navigate into the CS370_Lab09 directory and double-click on CS370_Lab09.sln (the file with the little Visual Studio icon with the 12 on it).

If the source file is not already open in the main window, open the source file by expanding the Source Files item in the Solution Explorer window and double-clicking timeCube.cpp.

1. Time-based Rendering I - Timer Callback

One method of accomplishing time-based rendering is to use the GLUT timer callback instead of the idle callback. This callback is registered like the others using:

glutTimerFunc(delay, mytimer, v);

where delay is the time (in milliseconds) to wait before entering the timer callback, mytimer is the name of the timer callback function, and v is an int value that will be passed to the parameter of the call when the timer expires. The timer callback has the prototype:

static void mytimer(int v);

The timer is a one-shot event, i.e. only triggers once. Therefore to produce a recurring timer event, simply issue the same registration call as initially but at the end of the timer callback itself. Thus a typical timer callback has the form

static void mytimer(int v)
{
    // Timer code

    glutPostRedisplay();
    glutTimerFunc(delay, mytimer, v);
}

with possibly a different delay from the original timer. The v parameter is often used as a flag to start/stop the timer events as desired, e.g. based on user input.

Tasks

2. Time-based Rendering II - Idle Callback

While the GLUT timer callback is a relatively easy way to produce time-based rendering, the timer is not very accurate as it only guarantees that the timer callback will not execute until at least delay milliseconds has elapsed (frequently being significantly longer than that time interval). An alternative way that is more reliable is to use the idle callback (which is essentially as fast as possible), retrieve an elapsed clock time, and only update the global variables (along with refreshing the screen) when a desired amount of time has passed. The function to get the elapsed clock time is:

time = glutGet(GLUT_ELAPSED_TIME);

where time is an unsigned int variable that will contain the elapsed time (in milliseconds) since glutInit() was called, i.e. since GLUT was initialized in main(). By using two variables to store the current and previous time, the difference can be compared to a desired interval to determine when to perform an update. This difference can also be used to perform an accurate update of the object states.

Tasks

3. OpenGL Fonts

Adding text to a scene is a nice way of providing the user with various bits of information. It is performed as a raster operation (similar to billboarding and texture mapping) within the fragment processor. We first must specify where the character is to be drawn in world-coordinates using:

glRasterPos2f(x,y);

where x and y are the world-coordinates based on the current projection matrix. Usually it is recommended to set the projection matrix using gluOrtho2D() prior to rendering fonts. Fortunately when a character is rendered, the raster position is updated to where the next character should begin - thus rendering a string is simply a matter of setting the initial raster position of the first character.

Rendering a (raster) character using GLUT is done with:

glutBitmapCharacter(font, ch);

where font is the name of the font to use (e.g. GLUT_BITMAP_HELVETICA_18) and ch is the character to draw. NOTE: OpenGL does not render strings, but rather must render character by character. However, a simple loop can be used to render a string at a desired location

glRasterPos2f(x,y);
for (int i=0; i<strlen(str); i++)
{
    glutBitmapCharacter(font, str[i]);
}

NOTE: You will need to include <string.h> to use the string functions in C. A nice way of creating formatted strings is using sprintf() which works similarly to printf() except that it places the formatted string into a character array.

Tasks

NOTE: Observe that around the text drawing code in draw_text() the current projection and modelview matrices are stored using glPushMatrix(), reset to an appropriate 2D projection for rendering the text, and then restored to the original matrices using glPopMatrix() once the text has been drawn. Also since the text is drawn using a color, lighting must be disabled/enabled around the text drawing as well.

The '+' and '-' keys increase/decrease the desired fps. Observe the discrepancy between the actual and desired frame rates for the two timing methods. In either case, the rotation speed of the cube should roughly remain constant.

Compiling and running the program

Once you have completed typing in the code, you can build and run the program in one of two ways:

(On Linux/OSX: In a terminal window, navigate to the directory containing the source file and simply type make. To run the program type ./timeCube.exe)

The output should look similar to below

image

To quit the program simply close the window.