Monday, October 27, 2008

OpenGL and Gtk+ with GtkGlExt

UPDATE: source files now on gitorious

Lately I've been really impressed with the performance and simplicity of Gtk+. After using it to get fullscreen video with Gstreamer, I thought I would check out using Gtk+ in place of GLUT to write OpenGL programs.

The program teapot.c is a heavily commented example that creates a window and draws a mesh teapot. The accompanying Makefile will compile it under GNU/Linux provided the following packages (in Ubuntu) are installed:
gtk+-2.0 gtkglext-1.0 gtkglext-x11-1.0

GtkGlExt is an API extension to the Gtk+ API that allows developers to use OpenGL calls on standard Gtk+ widgets or on new custom widgets. There is also a C++ API,
gtkglextmm, for Gtk+'s C++ counterpart, gtkmm.

The program is broken down into initialization functions to set up the window and OpenGL context, as well as callbacks that are used to do the heavy-lifting. The callbacks are registered in the initialization functions, which means they are attached to specific events (or signals) and are triggered when these events are fired off asynchronously.

The expose callback (expose_cb) is where we do our OpenGL drawing. It will be called in response to "expose-event" signals, fired whenever the window needs to be redrawn, i.e. if it is resized, exposed (where regions become visible that previously were not), or moved.

The idle callback (idle_cb) is the only callback that is not attached to an event. It is registered using g_timeout_add, meaning that it will be called at a fixed interval. Its job is to flag the drawable area of our GtkWindow as needing to be redrawn. It is also where we would update any control or data parameters that the expose callback needs to take into account. For example, say we were stretching our teapot, we could have the scaling factor incremented in our idle callback. It's important to note, however, that g_timeout_add does not guarantee that the timeout interval will be respected, as explained in the GLib documentation:

Note that timeout functions may be delayed, due to the processing of other event sources. Thus they should not be relied on for precise timing. After each call to the timeout function, the time of the next timeout is recalculated based on the current time and the given interval (it does not try to 'catch up' time lost in delays).
The constraints of real-time computing are beyond the scope of this entry, but the animation subsection in Chapter 1 of the OpenGL Red Book offers a good overview.

But lo and behold, the wonder of the Utah Teapot: