This chapter discusses using FLTK for your OpenGL applications.
Using OpenGL in FLTK
The easiest way to make an OpenGL display is to subclass Fl_Gl_Window. Your subclass must implement a draw()
method which uses OpenGL calls to draw the display. Your main program should call redraw()
when the display needs to change, and (somewhat later) FLTK will call draw()
.
With a bit of care you can also use OpenGL to draw into normal FLTK windows. This allows you to use Gouraud shading for drawing your widgets. To do this you use the gl_start() and gl_finish() functions around your OpenGL code.
You must include FLTK's <FL/gl.h>
header file. It will include the file <GL/gl.h>
, define some extra drawing functions provided by FLTK, and include the <windows.h>
header file needed by WIN32 applications.
Some simple coding rules (see OpenGL and 'retina' displays) allow to write cross-platform code that will draw high resolution OpenGL graphics if run on 'retina' displays with Mac OS X.
Making a Subclass of Fl_Gl_Window
To make a subclass of Fl_Gl_Window, you must provide:
- A class definition.
- A
draw()
method.
- A
handle()
method if you need to receive input from the user.
If your subclass provides static controls in the window, they must be redrawn whenever the FL_DAMAGE_ALL
bit is set in the value returned by damage()
. For double-buffered windows you will need to surround the drawing code with the following code to make sure that both buffers are redrawn:
#ifndef MESA
glDrawBuffer(GL_FRONT_AND_BACK);
#endif
... draw stuff here ...
#ifndef MESA
glDrawBuffer(GL_BACK);
#endif
Note:
If you are using the Mesa graphics library, the call to glDrawBuffer() is not required and will slow down drawing considerably. The preprocessor instructions shown above will optimize your code based upon the graphics library used.
|
Defining the Subclass
To define the subclass you just subclass the Fl_Gl_Window class:
public:
MyWindow(int X, int Y, int W, int H, const char *L)
};
The Fl_Gl_Window widget sets things up so OpenGL works.
Definition: Fl_Gl_Window.H:56
virtual void draw()
Draws the Fl_Gl_Window.
Definition: Fl_Gl_Window.cxx:543
int handle(int)
Handle some FLTK events as needed.
Definition: Fl_Gl_Window.cxx:551
The draw()
and handle()
methods are described below. Like any widget, you can include additional private and public data in your class (such as scene graph information, etc.)
The draw() Method
The draw()
method is where you actually do your OpenGL drawing:
void MyWindow::draw() {
if (!valid()) {
... set up projection, viewport, etc ...
... window size is in w() and h().
... valid() is turned on by FLTK after draw() returns
}
... draw ...
}
The handle() Method
The handle()
method handles mouse and keyboard events for the window:
int MyWindow::handle(int event) {
switch(event) {
... mouse down event ...
return 1;
... mouse moved while down event ...
return 1;
... mouse up event ...
return 1;
... Return 1 if you want keyboard events, 0 otherwise
return 1;
... keypress, key is in
Fl::event_key(), ascii in
Fl::event_text()
... Return 1 if you understand/use the keyboard event, 0 otherwise...
return 1;
... shortcut, key is in
Fl::event_key(), ascii in
Fl::event_text()
... Return 1 if you understand/use the shortcut event, 0 otherwise...
return 1;
default:
}
}
@ FL_KEYBOARD
Equivalent to FL_KEYDOWN.
Definition: Enumerations.H:313
@ FL_PUSH
A mouse button has gone down with the mouse pointing at this widget.
Definition: Enumerations.H:234
@ FL_RELEASE
A mouse button has been released.
Definition: Enumerations.H:242
@ FL_SHORTCUT
If the Fl::focus() widget is zero or ignores an FL_KEYBOARD event then FLTK tries sending this event ...
Definition: Enumerations.H:347
@ FL_UNFOCUS
This event is sent to the previous Fl::focus() widget when another widget gets the focus or the windo...
Definition: Enumerations.H:286
@ FL_DRAG
The mouse has moved with a button held down.
Definition: Enumerations.H:266
@ FL_FOCUS
This indicates an attempt to give a widget the keyboard focus.
Definition: Enumerations.H:281
The Fl is the FLTK global (static) class containing state information and global methods for the curr...
Definition: Fl.H:135
static int event_x()
Returns the mouse position of the event relative to the Fl_Window it was passed to.
Definition: Fl.H:598
When handle()
is called, the OpenGL context is not set up! If your display changes, you should call redraw()
and let draw()
do the work. Don't call any OpenGL drawing functions from inside handle()!
You can call some OpenGL stuff like hit detection and texture loading functions by doing:
make_current();
if (!valid()) {
... set up projection exactly the same as draw ...
valid(1);
}
... ok to call NON-DRAWING OpenGL code here, such as hit
detection, loading textures, etc...
Your main program can now create one of your windows by doing new MyWindow(...)
.
You can also use your new window class in FLUID by:
- Putting your class definition in a
MyWindow.H
file.
- Creating a Fl_Box widget in FLUID.
- In the widget panel fill in the "class" field with
MyWindow
. This will make FLUID produce constructors for your new class.
- In the "Extra Code" field put
#include "MyWindow.H"
, so that the FLUID output file will compile.
You must put glwindow->show()
in your main code after calling show()
on the window containing the OpenGL window.
Using OpenGL in Normal FLTK Windows
You can put OpenGL code into the draw()
method, as described in Drawing the Widget in the previous chapter, or into the code for a boxtype or other places with some care.
Most importantly, before you show any windows, including those that don't have OpenGL drawing, you must initialize FLTK so that it knows it is going to use OpenGL. You may use any of the symbols described for Fl_Gl_Window::mode()
to describe how you intend to use OpenGL:
static int gl_visual(int, int *alist=0)
This does the same thing as Fl::visual(int) but also requires OpenGL drawing to work.
Definition: gl_start.cxx:105
You can then put OpenGL drawing code anywhere you can draw normally by surrounding it with gl_start() and gl_finish() to set up, and later release, an OpenGL context with an orthographic projection so that 0,0 is the lower-left corner of the window and each pixel is one unit. The current clipping is reproduced with OpenGL glScissor()
commands. These functions also synchronize the OpenGL graphics stream with the drawing done by other X, WIN32, or FLTK functions.
... put your OpenGL code here ...
gl_finish();
FL_EXPORT void gl_start()
Creates an OpenGL context.
Definition: gl_start.cxx:56
The same context is reused each time. If your code changes the projection transformation or anything else you should use glPushMatrix()
and glPopMatrix()
functions to put the state back before calling gl_finish()
.
You may want to use Fl_Window::current()->h()
to get the drawable height so that you can flip the Y coordinates.
Unfortunately, there are a bunch of limitations you must adhere to for maximum portability:
Do not call gl_start()
or gl_finish()
when drawing into an Fl_Gl_Window !
OpenGL Drawing Functions
FLTK provides some useful OpenGL drawing functions. They can be freely mixed with any OpenGL calls, and are defined by including <FL/gl.h>
which you should include instead of the OpenGL header <GL/gl.h>
.
void gl_color(Fl_Color)
- Sets the current OpenGL color to a FLTK color. For color-index modes it will use
fl_xpixel(c)
, which is only right if this window uses the default colormap!
void gl_rect(int x, int y, int w, int h)
void gl_rectf(int x, int y, int w, int h)
- Outlines or fills a rectangle with the current color. If Fl_Gl_Window::ortho() has been called, then the rectangle will exactly fill the pixel rectangle passed.
void gl_font(Fl_Font fontid, int size)
- Sets the current OpenGL font to the same font you get by calling fl_font().
int gl_height()
int gl_descent()
float gl_width(const char *s)
float gl_width(const char *s, int n)
float gl_width(uchar c)
- Returns information about the current OpenGL font.
void gl_draw(const char *s)
void gl_draw(const char *s, int n)
- Draws a nul-terminated string or an array of
n
characters in the current OpenGL font at the current raster position.
void gl_draw(const char *s, int x, int y)
void gl_draw(const char *s, int n, int x, int y)
void gl_draw(const char *s, float x, float y)
void gl_draw(const char *s, int n, float x, float y)
- Draws a nul-terminated string or an array of
n
characters in the current OpenGL font at the given position.
void gl_draw(const char *s, int x, int y, int w, int h, Fl_Align)
- Draws a string formatted into a box, with newlines and tabs expanded, other control characters changed to ^X, and aligned with the edges or center. Exactly the same output as fl_draw().
Speeding up OpenGL
Performance of Fl_Gl_Window may be improved on some types of OpenGL implementations, in particular MESA and other software emulators, by setting the GL_SWAP_TYPE
environment variable. This variable declares what is in the backbuffer after you do a swapbuffers.
setenv GL_SWAP_TYPE COPY
This indicates that the back buffer is copied to the front buffer, and still contains its old data. This is true of many hardware implementations. Setting this will speed up emulation of overlays, and widgets that can do partial update can take advantage of this as damage()
will not be cleared to -1.
setenv GL_SWAP_TYPE NODAMAGE
This indicates that nothing changes the back buffer except drawing into it. This is true of MESA and Win32 software emulation and perhaps some hardware emulation on systems with lots of memory.
- All other values for
GL_SWAP_TYPE
, and not setting the variable, cause FLTK to assume that the back buffer must be completely redrawn after a swap.
This is easily tested by running the gl_overlay demo program and seeing if the display is correct when you drag another window over it or if you drag the window off the screen and back on. You have to exit and run the program again for it to see any changes to the environment variable.
Using OpenGL Optimizer with FLTK
OpenGL Optimizer is a scene graph toolkit for OpenGL available from Silicon Graphics for IRIX and Microsoft Windows. It allows you to view large scenes without writing a lot of OpenGL code.
- OptimizerWindow Class Definition
- To use OpenGL Optimizer with FLTK you'll need to create a subclass of Fl_Gl_Widget that includes several state variables:
csContext *context_;
csDrawAction *draw_action_;
csGroup *scene_;
csCamara *camera_;
public:
OptimizerWindow(int X, int Y, int W, int H, const char *L)
context_ = (csContext *)0;
draw_action_ = (csDrawAction *)0;
scene_ = (csGroup *)0;
camera_ = (csCamera *)0;
}
void scene(csGroup *g) { scene_ = g;
redraw(); }
void camera(csCamera *c) {
camera_ = c;
if (context_) {
draw_action_->setCamera(camera_);
camera_->draw(draw_action_);
}
}
};
- The camera() Method
- The
camera()
method sets the camera (projection and viewpoint) to use when drawing the scene. The scene is redrawn after this call.
- The draw() Method
- The
draw()
method performs the needed initialization and does the actual drawing:
void OptimizerWindow::draw() {
if (!context_) {
#ifdef WIN32
context_ = new csContext((HDC)fl_getHDC());
context_->ref();
context_->makeCurrent((HDC)fl_getHDC());
#else
context_ = new csContext(fl_display, fl_visual);
context_->ref();
context_->makeCurrent(fl_display, fl_window);
#endif
... perform other context setup as desired ...
draw_action_ = new csDrawAction;
if (camera_) {
draw_action_->setCamera(camera_);
camera_->draw(draw_action_);
}
} else {
#ifdef WIN32
context_->makeCurrent((HDC)fl_getHDC());
#else
context_->makeCurrent(fl_display, fl_window);
#endif
}
if (!valid()) {
context_->setViewport(0, 0, w(), h());
}
context_->clear(csContext::COLOR_CLEAR | csContext::DEPTH_CLEAR,
0.0f,
0.0f,
0.0f,
1.0f);
if (scene_)
draw_action_->apply(scene_);
}
- The scene() Method
- The
scene()
method sets the scene to be drawn. The scene is a collection of 3D objects in a csGroup
. The scene is redrawn after this call.
Using OpenGL 3.0 (or higher versions)
The examples subdirectory contains OpenGL3test.cxx, a toy program showing how to use OpenGL 3.0 (or higher versions) with FLTK in a cross-platform fashion. It contains also OpenGL3-glut-test.cxx which shows how to use FLTK's GLUT compatibility and OpenGL 3.
To access OpenGL 3.0 (or higher versions), use the FL_OPENGL3
flag when calling Fl_Gl_Window::mode(int a) or glutInitDisplayMode().
On the Windows and Unix/Linux platforms, FLTK creates contexts implementing the highest OpenGL version supported by the hardware. Such contexts may also be compatible with lower OpenGL versions. Access to functions from OpenGL versions above 1.1 requires to load function pointers at runtime on these platforms. FLTK recommends to use the GLEW library to perform this. It is therefore necessary to install the GLEW library (see below).
On the macOS platform, MacOS 10.7 or above is required; GLEW is possible but not necessary. FLTK creates contexts for OpenGL versions 1 and 2 without the FL_OPENGL3 flag and for OpenGL versions 3.2 and above with it.
- GLEW installation (Unix/Linux and MSWindows platforms)
- GLEW is available as a package for most Linux distributions and in source form at http://glew.sourceforge.net/. For the MSWindows platform, a Visual Studio static library (glew32.lib) can be downloaded from the same web site; a MinGW-style static library (libglew32.a) can be built from source with the make command.
- Source-level changes for OpenGL 3:
- Put this in all OpenGL-using source files (instead of #include <FL/gl.h>, and before #include <FL/glut.h> if you use GLUT):
#if defined(__APPLE__)
# include <OpenGL/gl3.h>
#else
# if defined(WIN32)
# define GLEW_STATIC 1
# endif
# include <GL/glew.h>
#endif
- Add the
FL_OPENGL3
flag when calling Fl_Gl_Window::mode(int a) or glutInitDisplayMode().
- Put this in the
handle(int event)
member function of the first to be created among your Fl_Gl_Window-derived classes: #ifndef __APPLE__
static int first = 1;
if (first && event ==
FL_SHOW && shown()) {
first = 0;
make_current();
glewInit();
}
#endif
@ FL_SHOW
This widget is visible again, due to Fl_Widget::show() being called on it or one of its parents,...
Definition: Enumerations.H:375
- Alternatively, if you use GLUT, put
#ifndef __APPLE__
glewInit();
#endif
after the first glutCreateWindow() call.
If GLEW is installed on the Mac OS development platform, it is possible to use the same code for all platforms, with one exception: put #ifdef __APPLE__
glewExperimental = GL_TRUE;
#endif
before the glewInit() call.
- Changes in the build process
- Link with libGLEW.so (on Unix/Linux), libglew32.a (with MinGW) or glew32.lib (with MS Visual Studio); no change is needed on the Mac OS platform.