fltk/documentation/opengl.html

361 lines
11 KiB
HTML
Raw Normal View History

<HTML>
<BODY>
<H1 ALIGN=RIGHT><A NAME="opengl">9 - Using OpenGL</A></H1>
This chapter discusses using FLTK for your OpenGL applications.
<h2>Using OpenGL in FLTK</h2>
The easiest way to make an OpenGL display is to subclass <a
href="#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>.
<p>With a bit of care you can also use OpenGL to draw into normal FLTK
windows. This is mostly useful because you can use Gourand 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.
<p>You must include FLTK's <tt>&lt;FL/gl.h></tt> header file. It will include
the file <tt>&lt;GL/gl.h></tt>, define some extra drawing functions
provided by FLTK, and include the <tt>&lt;windows.h></tt> header file needed
by WIN32 applications.
<h2>Making a Subclass of Fl_Gl_Window</h2>
To make a subclass of Fl_Gl_Window, you must provide:
<ul>
<li>A class definition.
<li>A <tt>draw()</tt> method.
<li>A <tt>handle()</tt> method (if you need to recieve input from
the user).
</ul>
<h3>Defining the Subclass</h3>
To define the subclass you just subclass <tt>Fl_Gl_Window</tt> class:
<ul><pre>
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) {}
};
</pre></ul>
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.)
<h3>The draw() Method</H3>
The <tt>draw()</tt> method is where you actually do your OpenGL drawing:
<ul><pre>
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 ...
}
</pre></ul>
<h3>The handle() Method</h3>
The <tt>handle()</tt> method handles mouse and keyboard events for the
window:
<ul><pre>
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;
default:
// tell FLTK that I don't understand other events
return 0;
}
}
</pre></ul>
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>!
<p>You can call some OpenGL stuff like hit detection and texture loading
functions by doing:
<ul><pre>
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...
</pre></ul>
Your main program can now create one of your windows by doing <tt>new
MyWindow(...)</tt>. You can also use <a href="#fluid">fluid</a> by:
<ol>
<li>Put your class definition in a MyWindow.H file.
<li>In fluid create a box object, resize & place where you want.
<li>In the control panel, fill in the "class" field with MyWindow.H.
This will make fluid produce constructors for your new class.
<li>In the "extra code" put <tt>#include "MyWindow.H"</tt>, so
that the fluid output file will compile.
</ol>
You must put <tt>glwindow->show()</tt> in your main code after calling
<tt>show()</tt> on the window containing the OpenGL window.
<h2>Using OpenGL in Normal FLTK Windows</h2>
You can put OpenGL code into an <a
href="#draw"><tt>Fl_Widget::draw()</tt></a> method or into the code for
a <a href="#boxtypes">boxtype</a> or other places with some care.
<p>Most important, before you show <i>any</i> 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 <a
href="#Fl_Gl_Window.mode"><tt>Fl_Gl_Window::mode()</tt></a> to describe
how you intend to use OpenGL:
<ul><pre>
Fl::gl_visual(FL_RGB);
</pre></ul>
You can then put OpenGL drawing code anywhere you can draw normally
by surrounding it with:
<ul><pre>
gl_start();
... put your OpenGL code here ...
gl_finish();
</pre></ul>
<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 also synchronize the
OpenGL graphics stream with the drawing done by other X, WIN32, or FLTK
functions.
<p>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>.
<p>You may want to use <tt>Fl_Window::current()->h()</tt> to get
the drawable height so you can flip the Y coordinates.
<p>Unfortunately, there are a bunch of limitations you must adhere to for
maximum portability:
<ul>
<li>You must choose a default visual with <a
href="#gl_visual"><tt>Fl::gl_visual()</tt></a>.
<li>You cannot pass <tt>FL_DOUBLE</tt> to <tt>Fl::gl_visual()</tt>.
<li>You cannot use <tt>Fl_Double_Window</tt> or
<tt>Fl_Overlay_Window</tt>.
</ul>
Do <i>not</i> call <tt>gl_start()</tt> or <tt>gl_finish()</tt> when drawing
into an <tt>Fl_Gl_Window</tt>!
<h2>OpenGL drawing functions</h2>
FLTK provides some useful OpenGL drawing functions. They can be freely
mixed with any OpenGL calls, and are defined by including
<tt>&lt;FL/gl.H></tt> (which you should include instead of the OpenGL
header <tt>&lt;GL/gl.h></tt>).
<h3>void gl_color(Fl_Color)</h3>
Set the current color to a FLTK color index. <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>
<h3>void gl_rect(int x, int y, int w, int h)<br>
void gl_rectf(int x, int y, int w, int h)</h3>
Outline or fill a rectangle with the current color. If
<tt>ortho()</tt> has been called, then the rectangle will exactly fill
the pixel rectangle passed.
<h3>void gl_font(Fl_Font fontid, int size)</h3>
Set the "current OpenGL font" to the same font you get by calling
<a href="#fl_font"><tt>fl_font()</tt></a>.
<h3>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)</h3>
Return information about the current OpenGL font.
<h3>void gl_draw(const char *)<br>
void gl_draw(const char *, int n)</h3>
Draw a nul-terminated string or an array of <tt>n</tt> characters in
the current OpenGL font at the current <tt>glRasterPos</tt>.
<h3>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)</h3>
Draw a nul-terminated string or an array of <tt>n</tt> characters in
the current OpenGL font at the given position.
<h3>void gl_draw(const char *, int x, int y, int w, int h, Fl_Align)</h3>
Draw 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="#fl_draw"><tt>fl_draw()</tt></a>.
<h2>Using OpenGL Optimizer with FLTK</h2>
<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. Versions are in the works for Solaris and
HP-UX. It allows you to view large scenes without writing a lot of
OpenGL code.
<h3>OptimizerWindow Class Definition</h3>
To use OpenGL Optimizer with FLTK you'll need to create a subclass of
<tt>Fl_Gl_Widget</tt> that includes several state variables:
<ul><pre>
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();
}
}
};
</ul><pre>
<H3>The camera() Method</H3>
The <tt>camera()</tt> method sets the camera (projection and viewpoint)
to use when drawing the scene. The scene is redrawn after this call.
<h3>The draw() Method</h3>
The <tt>draw()</tt> method performs the needed initialization
and does the actual drawing:
<ul><pre>
void OptimizerWindow::draw() {
if (!context_) {
// This is the first time we've been asked to draw; create the
// Optimizer context for the scene...
context_ = new csContext(fl_display, fl_visual);
context_->ref();
context_->makeCurrent(fl_display, fl_window);
... 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_);
}
}
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_);
}
</pre></ul>
<H3>The scene() Method</H3>
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.
</BODY>
</HTML>