original in en Miguel A Sepulveda
For design reasons the OpenGL specification was isolated from any window system dependencies. The resulting interface is a portable, streamlined and efficient 2D and 3D rendering library. It is up to the native window system to open and render windows. The OpenGL library communicates with the native system through additional auxiliary libraries. For example, the GLX auxiliary library describes the interaction between OpenGL and the X window System.
The OpenGL Utility Toolkit (GLUT) is a programming interface with ANSI C and FORTRAN bindings for writing window system independent OpenGL programs. It was written by Mark J. Kilgard and covers a great hole left by the OpenGL specification. Thanks to GLUT developers we can use a common window system interface independently of the target platform. OpenGL applications using GLUT can be easily ported between platforms without having to introduce numerous changes to the source code. GLUT definitely simplifies the production of OpenGL code and it complements the OpenGL library.
GLUT is relatively small and easy to learn. It is well designed and in fact, its author has already written wonderful documentation for it. Therefore starting a series of articles here in LinuxFocus seems redundant. We encourage any serious developer to read Mark's documentation. Our purpose for writing this regular GLUT column is to introduce the GLUT library and its usage step by step with examples as a companion reading with the OpenGL series of this magazine. We hope this will make a useful contribution and motivate more programmers to join the OpenGL-linux wagon. In any case, get your own copy of Mark's documentation as a good reference.
The GLUT API is a state machine like OpenGL. This means that GLUT has a number of state variables that live during the execution of the application. The initial states of the GLUT machine has been reasonably chosen to fit most applications. The program can modify the values of the state variables as it sees fit. Whenever a GLUT function is invoked its action is modified according to the values of the state variables. GLUT functions are simple, they take few parameters. No pointers are returned and the only pointers passed to GLUT functions are pointers to character strings and opaque font handles.
GLUT functions can be classified into several sub-APIs according to their functionality:
Every OpenGL program using GLUT must begin by initializing
the GLUT state machine. The glut initialization functions are
prefixed by glutInit-. The main initialization routine
is glutInit:
Usage:
glutInit(int **argcp, char
**argv);
argcp is a pointer to
the program's unmodified argc variable from main. Upon return,
the value pointed to by argcp is updated because glutInit
extracts any command line options relevant for the GLUT
library, for example: under the X Window System environment,
any options relevant for the X window associated to the GLUT
window.
argv is the program's unmodified argv
variable for main.
glutInit takes care of initializing the GLUT state
variables and negotiating a session with the window system.
There are a few routines that could appear before
glutInit; only routines prefixed by
glutInit-. These routines can be used to set the
default window initialization state. For example:
Usage:
glutInitWindowPosition(int x,
int **y);
glutInitWindowSize(int width,
int **height);
x,y = screen position in pixels of the
window (upper left corner)
width,height in pixels of the window.
There is another initialization routine omni-present in
every OpenGL application, glutInitDisplayMode():
Usage:
glutInitDisplayMode(unsigned int mode);
mode is
the Display mode, a bitwise OR-ing of GLUT display mode bit
masks. The possible bitmask values are:
GLUT_RGBA | Select an RGBA mode window. This is the default if neither GLUT_RGBA nor GLUT_INDEX are specified. |
GLUT_RGB | same as GLUT_RGBA. |
GLUT_INDEX | Select color index window mode. This overrides GLUT_RGBA. |
GLUT_SINGLE | Select a single buffered window. This is the default. |
GLUT_DOUBLE | Select a double buffered window. This overrides GLUT_SINGLE. |
GLUT_ACCUM | Select a window with an accumulation buffer. |
GLUT_ALPHA | Select a window with an alpha component to the color buffer(s). |
GLUT_DEPTH | Select a window with a depth buffer. |
GLUT_STENCIL | Select a window with a stencil buffer. |
GLUT_MULTISAMPLE | Select a window with multismapling support. |
GLUT_STEREO | Select a stereo window. |
GLUT_LUMINANCE | Select a stereo window with a "luminance" color model. |
#include <GL/glut.h>
void main(int argcp, char **argv){
/* Set window size and location */
glutInitWindowSize(640, 480);
glutInitWindowPosition(0, 0);
/* Select type of Display mode:
Single buffer & RGBA color */
glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE);
/* Initialize GLUT state */
glutInit(&argcp, argv);
.....more code
};
Second an example of an animation program:
#include <GL/glut.h>
void main(int argcp, char **argv){
/* Set window size and location */
glutInitWindowSize(640, 480);
glutInitWindowPosition(0, 0);
/* Select type of Display mode:
Double buffer & RGBA color */
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
/* Initialize GLUT state */
glutInit(&argcp, argv);
.....more code
};
We will come back to these two examples as we continue to learn more about GLUT. The main difference is that in the second case the display is initialized in a double buffer mode, ideal for animations because it eliminates flickering effects while changing frames in the animation sequence.
As mentioned before, GLUT is a state machine. Now we will
learn it is also designed as an event driven engine. This means
that there is a "timer" or continuous loop that gets started
after the proper initializations and that processes, one by
one, all the events declared to GLUT during initialization.
Events are: a mouse being clicked, a window closed, a window
reshape, a cursor moved, keyboard keys pressed, and even more
curiously the "idle" event, i.e. nothing happens! Each one of
the possible events must be registered in one of the GLUT state
variables for the "timer" or event processing loop of GLUT to
periodically check whether that event has been triggered by the
user.
For example, we could register "click mouse button" as an event
for GLUT to watch out for. Events are registered through
callback registration routines. All have the syntax
glut[someEvent]Func, in the case of the mouse clicking
it would be glutMouseFunc. A callback registration
tells the GLUT engine which user-defined function is to be
called if the corresponding event is triggered. So, if I write
my own routine MyMouse which specifies what to do if
the left mouse button is clicked, (or the right, etc.) then I
can register my callback function after the glutInit()
in main() using the statement
"glutMouseFunc(MyMouse);" .
Let us leave for later which callback functions and events are permitted in GLUT. The important thing now is that after all the important events in our application have been registered we must invoke the event processing routine of GLUT, namely glutMainLoop(). The function never comes back, our program basically enters an infinite loop. It will call as necessary any callbacks that have been previously registered. Every main() for an OpenGL application must then end in a glutMainLoop() statement. So in the case of our animation template:
#include <GL/glut.h>
void main(int argcp, char **argv){
/* Initialize GLUT state */
glutInit(&argcp, argv);
glutInitWindowSize(640, 480);
glutInitWindowPosition(0, 0);
/* Open a window */
glutCreateWindow("My OpenGL Application");
/* Select type of Display mode:
Double buffer & RGBA color */
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
/* Register Callback Functions */
.....
/* Start Event Processing Engine */
glutMainLoop();
};
Notice I have added some extra code we never mentioned before. It is one of GLUT's window management routines, glutCreateWindow(char **name). This is what I like so much about OpenGL & GLUT design philosophy, it is pretty clear what the routine does by just looking at the name!. It also takes care of actually passing the order to the underlying window system to open a window for our OpenGL application. The window will have the name "name" passed as a character string. In the X Window Environment this name is written on the upper left corner of the window. The window management section of GLUT has many other functions that we will eventually have a look at. For now, this one is sufficient. I have also rearranged the initialization routines to show that they can be placed after glutInit().
Back to events... I want now to introduce two callback registration functions that are very fundamental in any animation program. The glutDisplayFunc which sets the display function for the current window and the glutIdleFunc which sets the idle callback. Both registration routines expect a function of type void *(void). Say we write two additional callback functions to our animation template, void MyDisplay(void) which takes care of invoking the OpenGL instructions that actually draw our scene onto the window, and void MyIdle(void) which is a function that gets called whenever there is no other user input, that is, each time the event processing machine of GLUT goes once around the infinite loop (glutMainLoop()) and does not find any new event triggered, it processes MyIdle. Why do I need to register an Idle callback function in an animation program? Because if we wish to modify each one of the images (frames) shown during the animation independently of any user input, there has to be a function (the idle callback function) that gets called every so often during the life of the OpenGL program and changes the frames before they get drawn by Mydisplay().
Finally here is a simple template for an animation
program:
#include <GL/glut.h>
void MyIdle(void){
/* Some code to modify the variables defining next frame
*/
....
};
void MyDisplay(void){
/* Some OpenGL code that draws a frame */
....
/* After drawing the frame we swap the buffers */
glutSwapBuffers();
};
void main(int argcp, char **argv){
/* Initialize GLUT state */
glutInit(&argcp, argv);
glutInitWindowSize(640, 480);
glutInitWindowPosition(0, 0);
/* Open a window */
glutCreateWindow("My OpenGL Application");
/* Select type of Display mode:
Double buffer & RGBA color */
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
/* Register Callback Functions */
glutDisplayFunc(MyDisplay)
glutIdleFunc(MyIdle)
/* Start Event Processing Engine */
glutMainLoop();
};
Notice that at the end of MyDisplay I have added a new GLUT routine, glutSwapBuffers(). This is very useful in animations. We are using a window in DOUBLE buffer mode, one shown and one hidden. The drawing OpenGL instructions in this case always render into the hidden buffer. The glutSwapBuffers call, exchanges the buffers, showing in the window at once what was drawn. This technique is common in computer animations because it prevents the human eye from seeing the frame being constructed line by line.
There is already enough material to start writing OpenGL applications. The only things missing are the OpenGL instructions in MyDisplay that actually do the drawing...but that is another story ;-).
In the next article on GLUT programming we will explore in
more depth the functionality available to us in the Window
Management section of GLUT, and how to open multiple scenes
inside the same window. We will also learn about using menus,
including the pros and cons for their portability.