Open GL Super Bible

Previous Table of Contents Next


Listing 3-4 Animated bouncing square

// bounce.c
// Bouncing square

#include <windows.h>   // Standard windows include
#include <gl\gl.h>     // OpenGL library
#include <gl\glaux.h>  // AUX library

// Initial square position and size
GLfloat x1 = 100.0f;
GLfloat y1 = 150.0f;
GLsizei rsize = 50;

// Step size in x and y directions
// (number of pixels to move each time)
GLfloat xstep = 1.0f;
GLfloat ystep = 1.0f;

// Keep track of windowís changing width and height
GLfloat windowWidth;
GLfloat windowHeight;

// Called by AUX library when the window has changed size
void CALLBACK ChangeSize(GLsizei w, GLsizei h)
        {
        // Prevent a divide by zero, when window is too short
        // (you canít make a window of zero width)
        if(h == 0)
                h = 1;

        // Set the viewport to be the entire window
        glViewport(0, 0, w, h);

        // Reset the coordinate system before modifying
        glLoadIdentity();

        // Keep the square square, this time, save calculated
        // width and height for later use
        if (w <= h)
                {
                windowHeight = 250.0f*h/w;
                windowWidth = 250.0f;
                }
else
                {
                windowWidth = 250.0f*w/h;
                windowHeight = 250.0f;
                }

        // Set the clipping volume
        glOrtho(0.0f, windowWidth, 0.0f, windowHeight, 1.0f, -1.0f);
        }

// Called by AUX library to update window
void CALLBACK RenderScene(void)
        {
        // Set background clearing color to blue
        glClearColor(0.0f, 0.0f, 1.0f, 1.0f);

        // Clear the window with current clearing color
        glClear(GL_COLOR_BUFFER_BIT);

        // Set drawing color to red, and draw rectangle at
        // current position.
        glColor3f(1.0f, 0.0f, 0.0f);
        glRectf(x1, y1, x1+rsize, y1+rsize);

        glFlush();
        }

// Called by AUX library when idle (window not being
// resized or moved)
void CALLBACK IdleFunction(void)
        {
        // Reverse direction when you reach left or right edge
        if(x1 > windowWidth-rsize || x1 < 0)
                xstep = -xstep;

        // Reverse direction when you reach top or bottom edge
        if(y1 > windowHeight-rsize || y1 < 0)
                ystep = -ystep;

        // Check bounds.  This is in case the window is made
        // smaller and the rectangle is outside the new
        // clipping volume
        if(x1 > windowWidth-rsize)
                x1 = windowWidth-rsize-1;

        if(y1 > windowHeight-rsize)
                y1 = windowHeight-rsize-1;

        // Actually move the square
        x1 += xstep;
        y1 += ystep;

        // Redraw the scene with new coordinates
        RenderScene();
        }

// Main body of program
void main(void)
        {
        // AUX window setup and initialization
        auxInitDisplayMode(AUX_SINGLE | AUX_RGBA);
        auxInitPosition(100,100,250,250);
        auxInitWindow("Simple 2D Animation");

        // Set function to call when window is resized
        auxReshapeFunc(ChangeSize);

        // Set function to call when program is idle
        auxIdleFunc(IdleFunction);

        // Start main loop
        auxMainLoop(RenderScene);
        }

The animation produced by this example is very poor, even on very fast hardware. Because the window is being cleared each time before drawing the square, it flickers the entire time itís moving about, and you can easily see the square actually being drawn as two triangles. To produce smoother animation, you need to employ a feature known as double buffering.

Double Buffering

One of the most important features of any graphics packages is support for double buffering. This feature allows you to execute your drawing code while rendering to an off-screen buffer. Then a swap command places your drawing on screen instantly.

Double buffering can serve two purposes. The first is that some complex drawings may take a long time to draw and you may not want each step of the image composition to be visible. Using double buffering, you can compose an image and display it only after it is complete. The user never sees a partial image; only after the entire image is ready is it blasted to the screen.

A second use for double buffering is for animation. Each frame is drawn in the off-screen buffer and then swapped quickly to the screen when ready. The AUX library supports double-buffered windows. We need to make only two changes to the bounce.c program to produce a much smoother animation. First, change the line in main() that initializes the display mode to indicate that it should use double buffering:

     auxInitDisplayMode(AUX_DOUBLE | AUX_RGBA);

This will cause all the drawing code to render in an off-screen buffer.

Next, add a single line to the end of the Render() function:

     auxSwapBuffers();

The auxSwapBuffers() function causes the off-screen buffer used for drawing to be swapped to the screen. (The complete code for this is in the BOUNCE2 example on the CD.) This produces a very smooth animation of the red square bouncing around inside the window. See Figure 3-11.


Figure 3-11  Bouncing square


Previous Table of Contents Next