Introduction
This article continues our series on GLUT, the GL Utility
Toolkit written by Mark Kilgard for OpenGL applications. As
we mentioned in our previous article (Windows and
Animations) GLUT is a very interesting and useful toolkit for
any OpenGL developer because it lets you write portable code. GLUT
hides from the developer the dirty details of the underlying
windows manager and GUI interface.
GLUT is divided into a number of subAPI's. In todays
issue we will describe the Windows Management
subAPI. As its name indicates, it takes care of tasks
related to the windows used by your OpenGL application:
creating, destroying, iconifying a window; pushing, popping,
hiding, moving; and setting titles, positions, etc..
Windows Management Sub-Api
Here is a full list of the functions supported by the windows management in GLUT (As of version 3.6):
int glutCreateWindow(char *name) |
Creates a new top-level window |
int glutCreateSubWindow(int win,
int x, int y, int width, int height) |
Creates a sub-window |
void glutSetWindow(int winId) |
Set the window with ID winId as current window |
int glutGetWindow(void) |
Requests the identifier for the current window |
void glutDestroyWindow(int winId) |
Destroys the window specified by winId |
void glutPostRedisplay(void) |
Tells the GLUT event processor that the current
window needs to be redisplayed |
void glutSwapBuffers(void) |
Swaps the buffers for the current window |
void glutPositionWindow(int x, int y) |
Requests a change in the position of the window |
void glutReshapeWindow(int width, int height) |
Requests a change in the size of the window |
void glutFullScreen() |
Requests that the current window be made full screen |
void glutPopWindow(void)
void glutPushWindow(void) |
Push or Pop the current window relative to others in the stack |
void glutShowWindow(void)
void glutHideWindow(void)
void glutIconifyWindow(void) |
Show, hide or iconify the current window |
void glutSetWindowTitle(char *name)
void glutSetIconTitle(char *name) |
Set title bar in window or iconified window |
Using Sub-Windows
The use of most of the functions above is very
simple. Some were reviewed in our first article:
glutPostRedisplay, glutCreateWindow, glutPositionWindow,
glutSwapBuffers,..etc. Others although new are trivial to
use and the above description pretty much say what they do,
like glutSetIconTitle, glutFullScreen, ...etc. However
the use of sub-Windows is not that simple so we decided to
give you next a simple example and discuss shortly the details
of its implementation.
Here is the source code for a little OpenGL-GLUT demo (../../common/March1998/example1.c, ../../common/March1998/Makefile). Its purpose is to show you:
(a) How to handle a subwindow, (b) How to use your keyboard to
interact with your OpenGL application (c) How to render text in an OpenGL window.
(Please print out or have the source code for ../../common/March1998/example1.c at hand while we
follow the discussion.)
First let us take a look at the main()
function. It starts as any other GLUT application with
initializations: parsing the command line options,
selecting the display mode and setting the position and
size of the initial window. Since our application is going to
handle more than one window it is necessary to store the
integer window ID returned by the glutCreateWindow
statement. The variable winIdMain is a window handle.
Then it is time to set up the callback functions for the
events associated window winIdMain; several callback
functions are defined and all perform a task on the main
window: a display function (mainDisplay) that draws the scene,
a reshape function (mainReshape) that handles any transformation
of the window frame, keyboard that handles the actions triggered by the keyboard
and idle for managing the animation when no other events are pending
(see Windows and Animations for a
more detailed description of idle's role);
The first important thing to know is that in a GLUT application
there can only be one idle callback function. The idle function
is global for all the windows in the application.
So take this into account when designing your idle()
functions, they must take care of refreshing all windows and subwindows in your
application.
Next in the code comes the creation a subwindow
(winIDSub). To make a sub-window you must provide the
ID for the top level window, in the present case
winIDMain, the x and y coordinates in pixels for the
subwindow relative to the internal coordinates of
winIDMain, and the width and height in pixels of the requested sub-window. After
creating the subwindow GLUT returns a handle to our program and we are ready to set
up the appropriate callback functions for winIDSub. In our demo we set two callback
functions: a display (subDisplay) and a reshape (subReshape).
When GLUT opens a subwindow it provides it with a full OpenGl context. There is then
a performance penalty on using suubwindows because the driver of the graphics card has to
refresh the memory area for each of the windows on separate passes. Thanks to having
independent OpenGL context each window has its own system of coordinates, for example. In ../../common/March1998/example1.c
the coordinate systems are set in mainDisplay() and subDisplay() respectively.
Now go and examine these two display functions, they are quite simple and if you followed
our January article on OpenGL ("Simple Polygon Rendering")
you will have no trouble understanding it.
The function mainDisplay() draws a triangle with
Red, Green and Blue vertices. OpenGL interpolates colors
between the three vertices to fill the polygon. Before
rendering the triangle we have added a glRotate
statement that rotates the triangle around the z-axis
(perpendicular to the window screen), the angle of rotation
(spin) is slowly increased in idle() to give
the illusion of a rotating figure.
As for the display function associated with winIdSub
it is also very straight forward. First it clears the
background with a grey color, then it draws a green border
around the subwindow and finally it renders some text. Later
we will explain how text rendering works under GLUT. For the
moment just notice that glRasterPos2f(x, y) sets the
position where text will be drawn, also notice that the x, y
coordinates used are relative to the coordinate system of the
sub-window (defined in subReshape()).
The subwindow is acting as a
text board for data coming out of the animation. It is a silly
application, we could have just draw the text board on the main
window and achieved the same result (even more efficient). Under certain
circumstances however it makes sense to open a subwindow for text output. For example
when the animation is in 3D with lights and environmental effects and
you do not wish to deform your text board with annoying lights, perspective effects,
shadows or fog etc. Under these circumstances a subwindow comes handy because
it is completely isolated from the 3D animation.
There is crucial difference beteween the reshape callback function for a top
level window and for a subwindow. When a reshape event is triggered only the
top level window reshape callback function gets invoked, in our example
mainReshape(). We must call the reshape callback function subReshape
from within the mainReshape. It clearly makes sense because the location and shape of
the subwindows is conditioned to the size and shape of its top level window. So if you
read now the code for mainReshape() you will see that we first set
the projection matrix for the top level window, then switch to the winIDsub
subwindow and subsequently invoke the subwindow's reshape function with the width and
height relative to winIDMain we wish to use.
Earlier it was already mentioned that the idle() callback function must
update all the top level and subwindows in the OpenGL application. In our example
idle() first updates the animation state variables (time and spin)
and then it requests both main and subwindow to be redisplayed.
The keyboard
I have added two active keys to the program. By pushing the "i"-key you can
switch on or off the text board and with the "q"-key you quit the application. Try them :)
Any time you strike a key in your keyboard, GLUT's event processing driver registers
a keyboard event. These events are handled by the keyboard callback functions. In principle
every window has its own callback function. When the mouse is in location (x, y) within a
given window (or subwindow) and a keyboard event is triggered then the keyboard callback
function associated with that window is invoked. This callback function takes as arguments
the ASCII unsigned char associated with the key and the x, y location of the cursor
at that moment. In ../../common/March1998/example2.c there is no use for x, y but I am sure you can think of applications
where you can take advantage of this nice feature.
Only the top level window in our demo has a keyboard callback. If you try to press the keys "i"
or "q" while the cursor is inside the subwindow you will notice nothing happens. By default when a window is created and no keyboard callback is registered then all key strokes are
ignored. Have this in mind in the future if you use multiple windows and want and active keyboard.
Finally mention that the generation of keyboard callbacks can be disable by passing NULL to the
glutKeyBoardFunc().
Rendering Text
Rendering text under OpenGL and GLUT sucks!. Sorry to say it but it is true. I am not completely
sure why text rendering has been neglected from the OpenGL library. The old GL library from SGI had
a few high level functions to handle text rendering in graphics mode and there was an aditional
auxiliary library for changing fonts. OpenGL only provides very primitive directives for
rendering bitmaps, and that means you have to make your own library off bitmaps for every character,
take care of resolation, scaling of fonts....you name it!.
GLUT solves the dilemma of using text with
OpenGL somewhat. It provides glutBitmapCharacter that renders a single character onto screen
at the location specified by the glRasterPos. I have added a couple of functions, drawString(),
and drawStringBig() that make your life a bit easier rendering character string.
Conclusion
This concludes a very simple introduction to the use of GLUT subwindows. At this point I have to
mention that although it is good to experiment with them and test them under various platforms unfortunately
GLUT subwidows are not fully operational in all environments. Users with 3Dfx based cards will find
that subwindows are not functional yet due to hardware limitations. Also I have found a big performance penalty when using subwindows under
certain platforms. For instance, under my Linux Alpha with a 2Mb Matrox Millenium a subwindow make the application
run about twice slower, probably because unfortunately the X server for Alpha's still doesn't support any
hardware acceleration. On the other hand the same application under on a windows 95, and a 2Mb ATI RageII
with SGI's OpenGL driver runs wonderfully.
Since Linux development moves so fast it is possible that in the near future many of this performance
problems and incompatibilities will go away. For the moment simply be aware they exist, so use multiple
windows cautiously.
Of course, advanced users can always find a way around using subwindows by playing with
the matrix stack but since we haven't studied that yet forgive me if I leave for latter.. ;)).
|