d658ae6039
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@6406 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
461 lines
13 KiB
Plaintext
461 lines
13 KiB
Plaintext
/**
|
|
|
|
\page opengl 8 - 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
|
|
<A href="Fl_Gl_Window.html#Fl_Gl_Window"><tt>Fl_Gl_Window</tt></A>.
|
|
Your subclass must implement a <tt>draw()</tt> method which uses
|
|
OpenGL calls to draw the display. Your main program should call
|
|
<tt>redraw()</tt> when the display needs to change, and
|
|
(somewhat later) FLTK will call <tt>draw()</tt>.
|
|
|
|
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
|
|
<A href="#gl_start"><tt>gl_start()</tt></A>
|
|
and
|
|
<A href=#gl_finish><tt>gl_finish()</tt></A>
|
|
functions around your OpenGL code.
|
|
|
|
You must include FLTK's <tt><FL/gl.h></tt> header
|
|
file. It will include the file <tt><GL/gl.h></tt>, define
|
|
some extra drawing functions provided by FLTK, and include the
|
|
<tt><windows.h></tt> header file needed by WIN32
|
|
applications.
|
|
|
|
\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 <tt>draw()</tt> method.
|
|
|
|
\li A <tt>handle()</tt> 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 <tt>FL_DAMAGE_ALL</tt> bit is set
|
|
in the value returned by <tt>damage()</tt>. 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>
|
|
|
|
<P>If you are using the Mesa graphics library, the call
|
|
to <tt>glDrawBuffer()</tt> 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 <tt>Fl_Gl_Window</tt> 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 <tt>draw()</tt> and <tt>handle()</tt> 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 <tt>draw()</tt> 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 <tt>handle()</tt> 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 <tt>handle()</tt> is called, the OpenGL context is not
|
|
set up! If your display changes, you should call
|
|
<tt>redraw()</tt> and let <tt>draw()</tt> do the work. Don't
|
|
call any OpenGL drawing functions from inside <tt>handle()</tt>!
|
|
|
|
You can call <I>some</I> 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
|
|
<A href="fluid.html#FLUID">FLUID</A>
|
|
by:
|
|
|
|
-# Putting your class definition in a <tt>MyWindow.H</tt> file.
|
|
<br>
|
|
-# Creating a <tt>Fl_Box</tt> widget in FLUID.
|
|
<br>
|
|
-# In the widget panel fill in the "class" field with <tt>MyWindow</tt>.
|
|
This will make FLUID produce constructors for your new class.
|
|
<br>
|
|
-# 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 <tt>show()</tt> on the window containing the
|
|
OpenGL window.
|
|
|
|
\section opengl_normal Using OpenGL in Normal FLTK Windows
|
|
|
|
You can put OpenGL code into an
|
|
<A href="subclassing.html#draw"><tt>Fl_Widget::draw()</tt></A>
|
|
method or into the code for a
|
|
<A href="common.html#boxtypes">boxtype</A>
|
|
or other places with some care.
|
|
|
|
Most importantly, before you show <I>any</I> 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 Fl_Gl_Window::mode()
|
|
to describe how you intend to use OpenGL:
|
|
|
|
\code
|
|
Fl::gl_visual(FL_RGB);
|
|
\endcode
|
|
|
|
You can then put OpenGL drawing code anywhere you can draw
|
|
normally by surrounding it with:
|
|
|
|
\code
|
|
gl_start();
|
|
... put your OpenGL code here ...
|
|
gl_finish();
|
|
\endcode
|
|
|
|
<A name="gl_start"><tt>gl_start()</tt></A>
|
|
and
|
|
<A name="gl_finish"><tt>gl_finish()</tt></A>
|
|
set up 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 <tt>glScissor()</tt>
|
|
commands. These functions also synchronize the OpenGL graphics stream
|
|
with the drawing done by other X, WIN32, or FLTK functions.
|
|
|
|
The same context is reused each time. If your code changes
|
|
the projection transformation or anything else you should use
|
|
<tt>glPushMatrix()</tt> and <tt>glPopMatrix()</tt> functions to
|
|
put the state back before calling <tt>gl_finish()</tt>.
|
|
|
|
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:
|
|
|
|
\li You must choose a default visual with Fl::gl_visual().
|
|
|
|
\li You cannot pass <tt>FL_DOUBLE</tt> to Fl::gl_visual().
|
|
|
|
\li You cannot use Fl_Double_Window or Fl_Overlay_Window.
|
|
|
|
Do <I>not</I> call <tt>gl_start()</tt> or
|
|
<tt>gl_finish()</tt> 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 <FL/gl.H> which you should include
|
|
instead of the OpenGL header <tt><GL/gl.h></tt>.
|
|
|
|
void gl_color(Fl_Color)
|
|
|
|
\par
|
|
Sets the current OpenGL color to a FLTK color. <I>For
|
|
color-index modes it will use <tt>fl_xpixel(c)</tt>, 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
|
|
<A href="drawing.html#fl_font"><tt>fl_font()</tt></A>.
|
|
|
|
int gl_height() <br>
|
|
int gl_descent() <br>
|
|
float gl_width(const char *) <br>
|
|
float gl_width(const char *, int n) <br>
|
|
float gl_width(uchar)
|
|
|
|
\par
|
|
Returns information about the current OpenGL font.
|
|
|
|
void gl_draw(const char *) <br>
|
|
void gl_draw(const char *, int n)
|
|
|
|
\par
|
|
Draws a nul-terminated string or an array of <tt>n</tt>
|
|
characters in the current OpenGL font at the current raster
|
|
position.
|
|
|
|
void gl_draw(const char *, int x, int y) <br>
|
|
void gl_draw(const char *, int n, int x, int y) <br>
|
|
void gl_draw(const char *, float x, float y) <br>
|
|
void gl_draw(const char *, int n, float x, float y)
|
|
|
|
\par
|
|
Draws a nul-terminated string or an array of <tt>n</tt>
|
|
characters in the current OpenGL font at the given position.
|
|
|
|
void gl_draw(const char *, 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
|
|
<A href="drawing.html#text"><tt>fl_draw()</tt></A>.
|
|
|
|
\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 <tt>GL_SWAP_TYPE</tt> 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 it's 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. <p>
|
|
|
|
\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. <p>
|
|
|
|
\li All other values for <tt>GL_SWAP_TYPE</tt>, 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 <tt>gl_overlay</tt> 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 OpenGL Optimizer with FLTK you'll need to create a
|
|
subclass of <tt>Fl_Gl_Widget</tt> 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 <tt>camera()</tt> 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 <tt>draw()</tt> 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 <tt>scene()</tt> method sets the scene to be drawn. The scene is
|
|
a collection of 3D objects in a <tt>csGroup</tt>. The scene is redrawn
|
|
after this call.
|
|
|
|
<hr>
|
|
<a class="el" href="index.html">[Index]</a>
|
|
<a class="el" href="subclassing.html">[Previous]</a>
|
|
\ref subclassing
|
|
<a class="el" href="fluid.html">[Next]</a>
|
|
\ref fluid
|
|
|
|
*/
|