Using OpenGL in Fltk
#include <FL/gl.h>

The easiest way to make an OpenGL display is to subclass Fl_Gl_Window. Your subclass should implement a draw() method which uses OpenGL calls to draw the display. Your main program should call w->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 is mostly useful because you can access Gourand 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>, plus it defines some extra drawing functions provided by fltk, and also gets around a horrid screwup by our friends in Seattle.

Sample code for subclassing Fl_Gl_Window

  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) {}
  };

  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 ...
  }

  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_KEYBOARD:
      ... keypress, key is in Fl::event_key(), ascii in Fl::event_text()
      return 1;
    default:
      // tell fltk that I don't understand other events
      return 0;
    }
  }

When handle() is called, the glx context is not set up! If your display changes, you should call redraw() and let draw() do the work. Don't call any gl functions from inside handle()!

This may mean you cannot call some OpenGl stuff like hit detection. You can fix this by doing:

    case FL_PUSH:
      make_current(); // make glx 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 ...

Your main program can now create one of your windows by doing "new MyWindow(...)". You can also use fluid:

  1. Put your class definition in a MyWindow.H file.
  2. In fluid create a box object, resize & place where you want.
  3. In the control panel, fill in the "class" field with MyWindow.H. This will make fluid produce constructors for your new class.
  4. In the "extra code" 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 gl window.


class Fl_Gl_Window : public Fl_Window

An Fl_Gl_Window sets things up so OpenGL works, and also keeps an OpenGL "context" for that window, so that changes to the lighting and projection may be reused between redraws. Fl_Gl_Window also flushes the OpenGL streams and swaps buffers after draw() returns.

Fl_Gl_Window::draw() is a pure virtual method. You must subclass Fl_Gl_Window and provide an implementation for draw(). You may also provide an implementation of draw_overlay() if you want to draw into the overlay planes. You can avoid reinitializing the viewport and lights and other things by checking valid() at the start of draw() and only doing the initialization if it is false.

The draw() method can only use OpenGL calls. Do not attempt to call X, any of the functions in <FL/fl_draw.H>, or glX directly. Do not call gl_start() or gl_finish().

Methods:

Fl_Gl_Window::Fl_Gl_Window(int W, int H, const char *l=0);
Fl_Gl_Window::Fl_Gl_Window(int X, int Y, int W, int H, const char *l=0)

const int Fl_Gl_Window::mode() const;
int Fl_Gl_Window::mode(int);

int Fl_Gl_Window::mode(const int *);

static int Fl_Gl_Window::can_do(int);
static int Fl_Gl_Window::can_do(const int *mode);
int Fl_Gl_Window::can_do() const;

char Fl_Gl_Window::valid() const;
void Fl_Gl_Window::invalidate();
void Fl_Gl_Window::valid(char i);

void Fl_Gl_Window::ortho();

void Fl_Gl_Window::make_current();
void Fl_Gl_Window::make_overlay_current();
void Fl_Gl_Window::swap_buffers();

void Fl_Gl_Window::hide();
Fl_Gl_Window::~Fl_Gl_Window();

Fl_Gl_Window overlay

GL hardware typically provides some overlay bit planes, which are very useful for drawing UI controls atop your 3D graphics. If the overlay hardware is not provided, fltk tries to simulate the overlay, this works pretty well if your graphics are double buffered, but not very well for single-buffered.

int Fl_Gl_Window::can_do_overlay();

void Fl_Gl_Window::redraw_overlay();

virtual void Fl_Gl_Window::draw_overlay();


Using OpenGL in normal Fltk windows

You can put OpenGL code into an Fl_Widget::draw() method or into the code for a boxtype or other places, with some care.

Most important, before you show any windows (including those that don't have OpenGL drawing) you must initialize fltk/X 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:

You can then put OpenGL drawing code anywhere you can draw normally by surrounding it with:

gl_start() and gl_finish() set up a GL 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 scissor commands. These also synchronize the OpenGL graphics stream with the drawing done by other X or fltk functions.

The same context is reused each time. If your code changes the projection transformation or anything else you should use glPush/glPop to put the state back before calling gl_finish().

You may want to use Fl_Window::current()->h() to get the drawable height so you can flip the coordinate system.

Unfortunately there are a bunch of limitations you must adhere to for maximum portability:

Do not call gl_start()/gl_finish() when drawing an Fl_Gl_Window!


OpenGL drawing functions
#include <FL/gl_draw.H>

Fltk provides some useful gl 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);

void gl_rect(int x,int y,int w,int h);
void gl_rectf(int x,int y,int w,int h);

void gl_font(Fl_Font fontid, int size);

int gl_height();
int gl_descent();
float gl_width(const char *);
float gl_width(const char *, int n);
float gl_width(uchar);

void gl_draw(const char *);
void gl_draw(const char *, int n);

void gl_draw(const char *, int x, int y);
void gl_draw(const char *, int n, int x, int y);

void gl_draw(const char *, int x, int y, int w, int h, Fl_Align);

(back to contents)