FLTK 1.4.4: Using OpenGL

FLTK 1.4.4

Loading...
Searching...
No Matches
Using OpenGL

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 (see Using OpenGL in Normal FLTK Windows below). 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>` (on macOS: `<OpenGL/gl.h>`), define some extra drawing functions provided by FLTK, and include the `<windows.h>` header file needed by Windows applications.

Some simple coding rules (see OpenGL and support of HighDPI displays) allow to write cross-platform code that will support OpenGL run on HighDPI displays (including the 'retina' displays of Apple hardware).

# 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()`.

## Defining the Subclass

To define the subclass you just subclass the Fl_Gl_Window class:

class MyWindow : public Fl_Gl_Window {
void draw();
int handle(int);
public:
MyWindow(int X, int Y, int W, int H, const char *L)
: Fl_Gl_Window(X, Y, W, H, L) {}
};
Fl_Gl_Window
The Fl_Gl_Window widget sets things up so OpenGL works.
Definition Fl_Gl_Window.H:56
Fl_Gl_Window::draw
void draw() FL_OVERRIDE
Draws the Fl_Gl_Window.
Definition Fl_Gl_Window.cxx:502
Fl_Gl_Window::handle
int handle(int) FL_OVERRIDE
Handle some FLTK events as needed.
Definition Fl_Gl_Window.cxx:511

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) {
case FL_PUSH:
... mouse down event ...
... position in Fl::event_x() and Fl::event_y()
return 1;
case FL_DRAG:
... mouse moved while down event ...
return 1;
case FL_RELEASE:
... mouse up event ...
return 1;
case FL_FOCUS :
case FL_UNFOCUS :
... Return 1 if you want keyboard events, 0 otherwise
return 1;
case FL_KEYBOARD:
... 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;
case FL_SHORTCUT:
... 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:
// pass other events to the base class...
return Fl_Gl_Window::handle(event);
}
}
FL_KEYBOARD
@ FL_KEYBOARD
Equivalent to FL_KEYDOWN.
Definition Enumerations.H:299
FL_PUSH
@ FL_PUSH
A mouse button has gone down with the mouse pointing at this widget.
Definition Enumerations.H:220
FL_RELEASE
@ FL_RELEASE
A mouse button has been released.
Definition Enumerations.H:228
FL_SHORTCUT
@ FL_SHORTCUT
If the Fl::focus() widget is zero or ignores an FL_KEYBOARD event then FLTK tries sending this event ...
Definition Enumerations.H:333
FL_UNFOCUS
@ FL_UNFOCUS
This event is sent to the previous Fl::focus() widget when another widget gets the focus or the windo...
Definition Enumerations.H:272
FL_DRAG
@ FL_DRAG
The mouse has moved with a button held down.
Definition Enumerations.H:252
FL_FOCUS
@ FL_FOCUS
This indicates an attempt to give a widget the keyboard focus.
Definition Enumerations.H:267
Fl
The Fl is the FLTK global (static) class containing state information and global methods for the curr...
Definition Fl.H:140
Fl::event_x
static int event_x()
Returns the mouse position of the event relative to the Fl_Window it was passed to.
Definition Fl.H:631

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:

case FL_PUSH:
make_current(); // make OpenGL context current
if (!valid()) {
... set up projection exactly the same as draw ...
valid(1); // stop it from doing this next time
}
... 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:

1. Putting your class definition in a `MyWindow.H` file.
2. Creating a Fl_Box widget in FLUID.
3. In the widget panel fill in the "class" field with `MyWindow`. This will make FLUID produce constructors for your new class.
4. 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.

# OpenGL and support of HighDPI displays

HighDPI displays (including the so-called 'retina' displays of Apple hardware) are supported by FLTK in such a way that 1 unit of an FLTK quantity (say, the value given by Fl_Gl_Window::w()) corresponds to more than 1 pixel on the display. Conversely, when a program specifies the width and height of the OpenGL viewport, it is necessary to use an API that returns quantities expressed in pixels. That can be done as follows:

Fl_Gl_Window *glw = ...;
glViewport(0, 0, glw->pixel_w(), glw->pixel_h());

which makes use of the Fl_Gl_Window::pixel_w() and Fl_Gl_Window::pixel_h() methods giving the size in pixels of an Fl_Gl_Window that is potentially mapped to a HighDPI display. Method Fl_Gl_Window::pixels_per_unit() can also be useful in this context.

Note
  A further coding rule is necessary to properly support retina displays and OpenGL under macOS (see OpenGL and 'retina' displays)

# Using OpenGL in Normal FLTK Windows

Note
  Drawing both with OpenGL and Quartz in a normal FLTK window is not possible with the macOS platform. This technique is therefore not useful under macOS because it permits nothing more than what is possible with class Fl_Gl_Window.

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:

Fl::gl_visual(FL_RGB);
Fl::gl_visual
static int gl_visual(int, int *alist=0)
This does the same thing as Fl::visual(int) but also requires OpenGL drawing to work.

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, Windows, or FLTK functions.

gl_start();
... put your OpenGL code here ...
gl_finish();
gl_start
void gl_start()
Creates an OpenGL context.
Definition gl_start.cxx:58

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:

- You must choose a default visual with Fl::gl_visual().

- You cannot pass `FL_DOUBLE` to Fl::gl_visual().

- You cannot use Fl_Double_Window or Fl_Overlay_Window.

Do not call `gl_start()` or `gl_finish()` when drawing into an Fl_Gl_Window !

# Using FLTK widgets in OpenGL Windows

FLTK widgets can be added to Fl_Gl_Windows just as they would be added to Fl_Windows. They are rendered as an overlay over the user defined OpenGL graphics using 'fl_..' graphics calls that are implemented in GL.

Fl_Gl_Window does not add subsequent widgets as children by default as Fl_Window does. Call myGlWindow->begin() after creating the GL window to automatically add following widgets. Remember to call myGlWindow->end().

class My_Gl_Window : public Fl_Gl_Window {
...
void draw();
...
};
...
myGlWindow = new My_Gl_Window(0, 0, 500, 500);
myGlWindow->begin();
myButton = new Fl_Button(10, 10, 120, 24, "Hello!");
myGlWindow->end();
...
void My_Gl_Window::draw() {
// ... user GL drawing code
Fl_Gl_Window::draw(); // Draw FLTK child widgets.
}
Fl_Button
Buttons generate callbacks when they are clicked by the user.
Definition Fl_Button.H:76

Users can draw into the overlay by using GL graphics calls as well as all fl_... graphics calls from the "Drawing Fast Shapes" section.

void My_Gl_Window::draw() {
// ... user GL drawing code
Fl_Gl_Window::draw_begin(); // Set up 1:1 projection
Fl_Window::draw(); // Draw FLTK children
fl_color(FL_RED);
fl_rect(10, 10, 100, 100);
Fl_Gl_Window::draw_end(); // Restore GL state
}
Fl_Gl_Window::draw_end
void draw_end()
To be used as a match for a previous call to Fl_Gl_Window::draw_begin().
Definition Fl_Gl_Window.cxx:421
Fl_Gl_Window::draw_begin
void draw_begin()
Supports drawing to an Fl_Gl_Window with the FLTK 2D drawing API.
Definition Fl_Gl_Window.cxx:382
Fl_Window::draw
void draw() FL_OVERRIDE
Draws the widget.
Definition Fl_Window.cxx:491
fl_color
Fl_Color fl_color()
Return the last fl_color() that was set.
Definition fl_draw.H:74
fl_rect
void fl_rect(int x, int y, int w, int h)
Draw a border inside the given bounding box.
Definition fl_draw.H:298

Widgets can be drawn with transparencies by assigning an alpha value to a colormap entry and using that color in the widget.

Fl::set_color(FL_FREE_COLOR, 255, 255, 0, 127); // 50% transparent yellow
myGlWindow = new My_Gl_Window(0, 0, 500, 500);
myGlWindow->begin();
myButton = new Fl_Button(10, 10, 120, 24, "Hello!");
myButton->box(FL_BORDER_BOX);
myButton->color(FL_FREE_COLOR);
myGlWindow->end();
FL_FREE_COLOR
#define FL_FREE_COLOR
Colors numbered between FL_FREE_COLOR and FL_FREE_COLOR + FL_NUM_FREE_COLOR - 1 are free for the user...
Definition Enumerations.H:1162
FL_BORDER_BOX
@ FL_BORDER_BOX
see figure Standard Box Types
Definition Enumerations.H:660
Fl::set_color
static void set_color(Fl_Color, uchar, uchar, uchar)
Sets an entry in the fl_color index table.
Definition fl_color.cxx:62

Transparencies can also be set directly when drawing. This can be used to create custom box types and RGB overlay drawings with an alpha channel.

fl_color(0, 255, 0, 127); // 50% transparent green
fl_rectf(10, 10, 100, 100);
fl_color(FL_RED); // back to opaque red
fl_rect(20, 20, 80, 80);
fl_rectf
void fl_rectf(int x, int y, int w, int h)
Color with current color a rectangle that exactly fills the given bounding box.
Definition fl_draw.H:339

# 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:

class OptimizerWindow : public Fl_Gl_Window {
csContext *context_; // Initialized to 0 and set by draw()...
csDrawAction *draw_action_; // Draw action...
csGroup *scene_; // Scene to draw...
csCamara *camera_; // Viewport for scene...
void draw();
public:
OptimizerWindow(int X, int Y, int W, int H, const char *L)
: Fl_Gl_Window(X, Y, W, H, 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_);
redraw();
}
}
};
Fl_Widget::redraw
void redraw()
Schedules the drawing of the widget.
Definition Fl.cxx:1668

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_) {
// This is the first time we've been asked to draw; create the
// Optimizer context for the scene...
#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 // _WIN32
... perform other context setup as desired ...
// Then create the draw action to handle drawing things...
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 // _WIN32
}
if (!valid()) {
// Update the viewport for this context...
context_->setViewport(0, 0, w(), h());
}
// Clear the window...
context_->clear(csContext::COLOR_CLEAR | csContext::DEPTH_CLEAR,
0.0f, // Red
0.0f, // Green
0.0f, // Blue
1.0f); // Alpha
// Then draw the scene (if any)...
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 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 (but not below) with it.

GLEW installation (Linux and Windows platforms)
  FLTK needs a header file, `GL/glew.h`, and a library, `libGLEW`.* or equivalent, to support OpenGL 3 and above.
  These can be obtained for most Linux distributions by installing package libglew-dev.
  For the Windows platform :
  - the header and a Visual Studio static library (glew32.lib) can be downloaded from http://glew.sourceforge.net/ ;
  - a MinGW-style static library (libglew32.a) can be built from source (same web site) with the make command. Alternatively, pre-built files are available for these architectures :
    - x86: download files glew.h and libglew32.a;
    - x86_64: install GLEW as an MSYS2 package with command :
      pacman -S mingw-w64-x86_64-glew

Source-level changes for OpenGL 3:
  - Put this in all OpenGL-using source files (instead of, or before if needed, #include <FL/gl.h>, and before #include <FL/glut.h> if you use GLUT):
    #if defined(__APPLE__)
    # include <OpenGL/gl3.h> // defines OpenGL 3.0+ functions
    #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(); // defines pters to functions of OpenGL V 1.2 and above
    }
    #endif
    FL_SHOW
    @ FL_SHOW
    This widget is visible again, due to Fl_Widget::show() being called on it or one of its parents,...
    Definition Enumerations.H:361
  - Alternatively, if you use GLUT, put
    #ifndef __APPLE__
    glewInit(); // defines pters to functions of OpenGL V 1.2 and above
    #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.

Testing for success of the glewInit() call
  Testing whether the glewInit() call is successful is to be done as follows:
  #include <FL/platform.H> // defines FLTK_USE_WAYLAND under the Wayland platform
  #include <FL/Fl.H> // for Fl::warning()
  #ifndef __APPLE__
  # if defined(_WIN32)
  # define GLEW_STATIC 1
  # endif
  # include <GL/glew.h>
  GLenum err = glewInit(); // defines pters to functions of OpenGL V 1.2 and above
  # ifdef FLTK_USE_WAYLAND
  // glewInit returns GLEW_ERROR_NO_GLX_DISPLAY with Wayland
  if (fl_wl_display() && err == GLEW_ERROR_NO_GLX_DISPLAY) err = GLEW_OK;
  # endif
  if (err != GLEW_OK) Fl::warning("glewInit() failed returning %u", err);
  #endif // ! __APPLE__
  Fl.H
  Fl static class.
  Fl::warning
  static void(* warning)(const char *,...)
  FLTK calls Fl::warning() to output a warning message.
  Definition Fl.H:531
  fl_wl_display
  struct wl_display * fl_wl_display()
  Returns the Wayland display in use.

Changes in the build process
  Link with libGLEW.so (with X11 or Wayland), libglew32.a (with MinGW) or glew32.lib (with MS Visual Studio); no change is needed on the Mac OS platform.

[Prev] Adding and Extending Widgets | [Index] | FLTK Runtime Options [Next]