On the X11/MSWindows platforms, this requires external installation of the GLEW library. This fixes STR#3198 and STR#3257. Added two new examples programs. git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@10876 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
544 lines
16 KiB
Plaintext
544 lines
16 KiB
Plaintext
/**
|
|
|
|
\page opengl Using OpenGL
|
|
|
|
This chapter discusses using FLTK for your OpenGL applications.
|
|
|
|
\section opengl_using Using OpenGL in FLTK
|
|
|
|
The easiest way to make an OpenGL display is to subclass
|
|
Fl_Gl_Window.
|
|
Your subclass must implement a \p draw() method which uses
|
|
OpenGL calls to draw the display. Your main program should call
|
|
\p redraw() when the display needs to change, and
|
|
(somewhat later) FLTK will call \p 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
|
|
\ref opengl_gl_start "gl_start()" and
|
|
\ref opengl_gl_finish "gl_finish()"
|
|
functions around your OpenGL code.
|
|
|
|
You must include FLTK's \p <FL/gl.h> header
|
|
file. It will include the file \p <GL/gl.h>, define
|
|
some extra drawing functions provided by FLTK, and include the
|
|
\p <windows.h> header file needed by WIN32
|
|
applications.
|
|
|
|
Some simple coding rules (see \ref osissues_retina) allow to write cross-platform code that will draw high resolution
|
|
OpenGL graphics if run on 'retina' displays with Mac OS X.
|
|
|
|
\section opengl_subclass Making a Subclass of Fl_Gl_Window
|
|
|
|
To make a subclass of Fl_Gl_Window, you must provide:
|
|
|
|
\li A class definition.
|
|
\li A \p draw() method.
|
|
\li A \p 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 \p FL_DAMAGE_ALL bit is set
|
|
in the value returned by \p 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:
|
|
|
|
\code
|
|
#ifndef MESA
|
|
glDrawBuffer(GL_FRONT_AND_BACK);
|
|
#endif // !MESA
|
|
... draw stuff here ...
|
|
#ifndef MESA
|
|
glDrawBuffer(GL_BACK);
|
|
#endif // !MESA
|
|
\endcode
|
|
|
|
<CENTER><TABLE WIDTH="80%" BORDER="1" CELLPADDING="5" CELLSPACING="0" BGCOLOR="#cccccc">
|
|
<TR>
|
|
<TD><B>Note:</B>
|
|
|
|
If you are using the Mesa graphics library, the call
|
|
to \p 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.
|
|
|
|
</TD>
|
|
|
|
</TR>
|
|
</TABLE></CENTER>
|
|
|
|
\subsection opengl_defining Defining the Subclass
|
|
|
|
To define the subclass you just subclass the Fl_Gl_Window class:
|
|
|
|
\code
|
|
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) {}
|
|
};
|
|
\endcode
|
|
|
|
The \p draw() and \p 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.)
|
|
|
|
\subsection opengl_draw The draw() Method
|
|
|
|
The \p draw() method is where you actually do your OpenGL drawing:
|
|
|
|
\code
|
|
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 ...
|
|
}
|
|
\endcode
|
|
|
|
\subsection opengl_handle The handle() Method
|
|
|
|
The \p handle() method handles mouse and keyboard
|
|
events for the window:
|
|
|
|
\code
|
|
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);
|
|
}
|
|
}
|
|
\endcode
|
|
|
|
When \p handle() is called, the OpenGL context is not
|
|
set up! If your display changes, you should call
|
|
\p redraw() and let \p draw() do the work. Don't
|
|
call any OpenGL drawing functions from inside \p handle()!
|
|
|
|
You can call \e some OpenGL stuff like hit detection and texture
|
|
loading functions by doing:
|
|
|
|
\code
|
|
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...
|
|
\endcode
|
|
|
|
Your main program can now create one of your windows by doing
|
|
<tt>new MyWindow(...)</tt>.
|
|
|
|
You can also use your new window class in
|
|
\ref fluid "FLUID"
|
|
by:
|
|
|
|
-# Putting your class definition in a \p MyWindow.H file.
|
|
-# Creating a Fl_Box widget in FLUID.
|
|
-# In the widget panel fill in the "class" field with \p MyWindow.
|
|
This will make FLUID produce constructors for your new class.
|
|
-# In the "Extra Code" field put <tt>\#include "MyWindow.H"</tt>,
|
|
so that the FLUID output file will compile.
|
|
|
|
You must put <tt>glwindow->show()</tt> in your main code
|
|
after calling \p show() on the window containing the
|
|
OpenGL window.
|
|
|
|
\section opengl_normal Using OpenGL in Normal FLTK Windows
|
|
|
|
You can put OpenGL code into the \p draw() method, as described in
|
|
\ref subclassing_drawing
|
|
in the previous chapter, or into the code for a
|
|
\ref common_boxtypes "boxtype"
|
|
or other places with some care.
|
|
|
|
Most importantly, before you show \e any windows,
|
|
including those that don't have OpenGL drawing, you <B>must</B>
|
|
initialize FLTK so that it knows it is going to use OpenGL. You
|
|
may use any of the symbols described for \p Fl_Gl_Window::mode()
|
|
to describe how you intend to use OpenGL:
|
|
|
|
\code
|
|
Fl::gl_visual(FL_RGB);
|
|
\endcode
|
|
|
|
\anchor opengl_gl_start
|
|
\anchor opengl_gl_finish
|
|
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 \p glScissor()
|
|
commands. These functions also synchronize the OpenGL graphics stream
|
|
with the drawing done by other X, WIN32, or FLTK functions.
|
|
|
|
\code
|
|
gl_start();
|
|
... put your OpenGL code here ...
|
|
gl_finish();
|
|
\endcode
|
|
|
|
The same context is reused each time. If your code changes
|
|
the projection transformation or anything else you should use
|
|
\p glPushMatrix() and \p glPopMatrix() functions to
|
|
put the state back before calling \p gl_finish().
|
|
|
|
You may want to use <tt>Fl_Window::current()-\>h()</tt> 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:
|
|
|
|
\li You must choose a default visual with Fl::gl_visual().
|
|
|
|
\li You cannot pass \p FL_DOUBLE to Fl::gl_visual().
|
|
|
|
\li You cannot use Fl_Double_Window or Fl_Overlay_Window.
|
|
|
|
Do \e not call \p gl_start() or
|
|
\p gl_finish() when drawing into an Fl_Gl_Window !
|
|
|
|
\section opengl_drawing OpenGL Drawing Functions
|
|
|
|
FLTK provides some useful OpenGL drawing functions. They can
|
|
be freely mixed with any OpenGL calls, and are defined by
|
|
including \p <FL/gl.h> which you should include
|
|
instead of the OpenGL header \p <GL/gl.h>.
|
|
|
|
void gl_color(Fl_Color)
|
|
|
|
\par
|
|
Sets the current OpenGL color to a FLTK color. <I>For
|
|
color-index modes it will use \p fl_xpixel(c), which is
|
|
only right if this window uses the default colormap!</I>
|
|
|
|
void gl_rect(int x, int y, int w, int h) <br>
|
|
void gl_rectf(int x, int y, int w, int h)
|
|
|
|
\par
|
|
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)
|
|
|
|
\par
|
|
Sets the current OpenGL font to the same font you get by calling
|
|
\ref ssect_Fonts "fl_font()".
|
|
|
|
int gl_height() <br>
|
|
int gl_descent() <br>
|
|
float gl_width(const char *s) <br>
|
|
float gl_width(const char *s, int n) <br>
|
|
float gl_width(uchar c)
|
|
|
|
\par
|
|
Returns information about the current OpenGL font.
|
|
|
|
void gl_draw(const char *s) <br>
|
|
void gl_draw(const char *s, int n)
|
|
|
|
\par
|
|
Draws a nul-terminated string or an array of \p n
|
|
characters in the current OpenGL font at the current raster
|
|
position.
|
|
|
|
void gl_draw(const char *s, int x, int y) <br>
|
|
void gl_draw(const char *s, int n, int x, int y) <br>
|
|
void gl_draw(const char *s, float x, float y) <br>
|
|
void gl_draw(const char *s, int n, float x, float y)
|
|
|
|
\par
|
|
Draws a nul-terminated string or an array of \p 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)
|
|
|
|
\par
|
|
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
|
|
\ref ssect_Text "fl_draw()".
|
|
|
|
\section opengl_speed 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 \p GL_SWAP_TYPE environment
|
|
variable. This variable declares what is in the backbuffer after
|
|
you do a swapbuffers.
|
|
|
|
\li <tt>setenv GL_SWAP_TYPE COPY</tt> <br>
|
|
<br>
|
|
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
|
|
\p damage() will not be cleared to -1.
|
|
|
|
\li <tt>setenv GL_SWAP_TYPE NODAMAGE</tt> <br>
|
|
<br>
|
|
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.
|
|
|
|
\li All other values for \p 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 \ref examples_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.
|
|
|
|
\section opengl_optimizer Using OpenGL Optimizer with FLTK
|
|
|
|
<A href="http://www.sgi.com/software/optimizer">OpenGL Optimizer</A>
|
|
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.
|
|
|
|
\par OptimizerWindow Class Definition
|
|
|
|
\par
|
|
To use
|
|
<A href="http://www.sgi.com/software/optimizer">OpenGL Optimizer</A>
|
|
with FLTK you'll need to create a
|
|
subclass of Fl_Gl_Widget that includes several state
|
|
variables:
|
|
|
|
\code
|
|
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();
|
|
}
|
|
}
|
|
};
|
|
\endcode
|
|
|
|
\par The camera() Method
|
|
|
|
\par
|
|
The \p camera() method sets the camera (projection and
|
|
viewpoint) to use when drawing the scene. The scene is redrawn after
|
|
this call.
|
|
|
|
\par The draw() Method
|
|
|
|
\par
|
|
The \p draw() method performs the needed initialization and does
|
|
the actual drawing:
|
|
|
|
\code
|
|
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_);
|
|
}
|
|
\endcode
|
|
|
|
\par The scene() Method
|
|
|
|
\par
|
|
The \p scene() method sets the scene to be drawn. The scene is
|
|
a collection of 3D objects in a \p csGroup. The scene is redrawn
|
|
after this call.
|
|
|
|
\section opengl3 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.
|
|
|
|
<b>On the MSWindows and Unix/Linux platforms</b>, FLTK creates contexts implementing
|
|
the highest OpenGL version supported by the hardware,
|
|
which are also compatible with lower OpenGL versions. Thus, FLTK allows
|
|
source code targeting any version of OpenGL. 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). <b>On the Mac OS X platform</b>,
|
|
FLTK creates by default contexts implementing OpenGL versions 1 or 2.
|
|
To access OpenGL 3.0 (or higher versions), use the <tt>FL_OPENGL3</tt> flag (see below).
|
|
Mac OS 10.7 or above is required; GLEW is possible but not necessary.
|
|
|
|
\par 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.
|
|
|
|
\par Source-level changes for OpenGL 3:
|
|
\li Put this in all OpenGL-using source files (instead of \#include <FL/gl.h>,
|
|
and before \#include <FL/glut.h> if you use GLUT):
|
|
\code
|
|
#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
|
|
\endcode
|
|
\li Add the <tt>FL_OPENGL3</tt> flag when calling Fl_Gl_Window::mode(int a)
|
|
or glutInitDisplayMode().
|
|
\li Put this in the <tt>handle(int event)</tt> member function of the first to be created
|
|
among your Fl_Gl_Window-derived classes:
|
|
\code
|
|
#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
|
|
\endcode
|
|
\li Alternatively, if you use GLUT, put
|
|
\code
|
|
#ifndef __APPLE__
|
|
glewInit(); // defines pters to functions of OpenGL V 1.2 and above
|
|
#endif
|
|
\endcode
|
|
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
|
|
\code
|
|
#ifdef __APPLE__
|
|
glewExperimental = TRUE;
|
|
#endif
|
|
\endcode
|
|
before the glewInit() call.
|
|
|
|
\par 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.
|
|
|
|
\htmlonly
|
|
<hr>
|
|
<table summary="navigation bar" width="100%" border="0">
|
|
<tr>
|
|
<td width="45%" align="LEFT">
|
|
<a class="el" href="subclassing.html">
|
|
[Prev]
|
|
Adding and Extending Widgets
|
|
</a>
|
|
</td>
|
|
<td width="10%" align="CENTER">
|
|
<a class="el" href="index.html">[Index]</a>
|
|
</td>
|
|
<td width="45%" align="RIGHT">
|
|
<a class="el" href="fluid.html">
|
|
Programming with FLUID
|
|
[Next]
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
\endhtmlonly
|
|
|
|
*/
|