550 lines
16 KiB
Text
550 lines
16 KiB
Text
/**
|
|
|
|
\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.
|
|
|
|
To access OpenGL 3.0 (or higher versions), use the <tt>FL_OPENGL3</tt> flag
|
|
when calling Fl_Gl_Window::mode(int a) or glutInitDisplayMode().
|
|
|
|
<b>On the Windows and Unix/Linux platforms</b>, 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).
|
|
|
|
<b>On the macOS platform</b>, 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.
|
|
|
|
\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 = GL_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
|
|
|
|
*/
|