1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
|
<HTML>
<BODY>
<H1 ALIGN=RIGHT><A NAME="opengl">7 - Using OpenGL</A></H1>
This chapter discusses using FLTK for your OpenGL applications.
<H2>The OpenGL Widget</H2>
<H2>Making a Simple OpenGL Wrapper Widget</H2>
<H2>A Simple Flight Simulator</H2>
<H2>Using FLTK with OpenGL Optimizer</H2>
<H2>Using OpenGL Optimizer for the Flight Simulator</H2>
</BODY>
</HTML>
<title>Using OpenGL in Fltk</title>
<h2>Using OpenGL in Fltk<br>#include <FL/gl.h></h2>
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().
<p>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 <a
href=#gl_start>gl_start() and gl_finish()</a> functions around your
OpenGL code.
<p>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.
<h2>Sample code for subclassing Fl_Gl_Window</h2>
<p><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) {}
};
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;
}
}
</pre>
<p>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()!
<p>This may mean you cannot call some OpenGl stuff like hit detection.
You can fix this by doing:
<p><pre>
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 ...
</pre>
<p>Your main program can now create one of your windows by doing "new
MyWindow(...)". You can also use <a href=fluid.html>fluid</a>:
<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 "#include "MyWindow.H"", so that the fluid
output file will compile.
</ol>
<p>You must put glwindow->show() in your main code after calling
show() on the window containing the gl window.
<p><hr>
<h2>class Fl_Gl_Window : public <a href=Fl_Window.html>Fl_Window</a></h2>
<p>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.
<p>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.
<p>The draw() method can <i>only</i> 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().
<h2>Methods:</h2>
<h4><code>Fl_Gl_Window::Fl_Gl_Window(int W, int H, const char *l=0);
<br>Fl_Gl_Window::Fl_Gl_Window(int X, int Y, int W, int H, const char
*l=0)</code></h4><ul>
The constructors. Fl_Gl_Window::mode() defaults to
<code>FL_RGB|FL_DOUBLE|FL_DEPTH</code>.
<a name=mode>
</ul><h4><code>const int Fl_Gl_Window::mode() const;
<br>int Fl_Gl_Window::mode(int);</code></h4><ul>
Set or change the OpenGL capabilites of the window. The value can be
any of the following or'd together:
<p><ul>
<li><code>FL_RGB</code> - Color (not indexed)
<li><code>FL_RGB8</code> - Color with at least 8 bits of each color
<li><code>FL_INDEX</code> - Indexed mode
<li><code>FL_SINGLE</code> - not double buffered
<li><code>FL_DOUBLE</code> - double buffered
<li><code>FL_ACCUM</code> - accumulation buffer
<li><code>FL_ALPHA</code> - alpha channel in color
<li><code>FL_DEPTH</code> - depth buffer
<li><code>FL_STENCIL</code> - stencil buffer
<li><code>FL_MULTISAMPLE</code> - multisample antialiasing
</ul>
<p><code>FL_RGB</code> and <code>FL_SINGLE</code> have a
value of zero, they are "on" <i>unless</i> you give
<code>FL_INDEX</code> or <code>FL_DOUBLE</code>.
<p>If the desired combination cannot be done, fltk will try turning off
the <code>FL_MULTISAMPLE</code>. If this also fails show() will call
Fl::error() and not show the window.
<p>You can change the mode while the window is displayed. This
is most useful for turning double-buffering on and off. <i>Under
X this will cause the old X window to be destroyed and a new one
created. If this is a top-level window this will unfortunately also
cause the window to blink, raise to the top, and be de-iconized, and
the xid() will change, possibly breaking other code. It is best to
make the GL window a child of another window if you wish to do this!</i>
</ul><h4><code>int Fl_Gl_Window::mode(const int *);</code></h4><ul>
<p><i>This call only works on systems using glX.</i> This value is
passed unchanged to glXChooseVisual(), superceeding the value
calculated from mode(int). See "man glXChooseVisual" if you wish to
construct your own mode. Fltk assummes that the pointer is to static
const data, and caches the pointer with the found visual.
glXChooseVisual is not called until show() or can_do()
is called. To restore the use of mode(int), call
<code>mode((int*)0)</code>.
</ul><h4><code>static int Fl_Gl_Window::can_do(int);
<br>static int Fl_Gl_Window::can_do(const int *mode);
<br>int Fl_Gl_Window::can_do() const;</code></h4><ul>
Returns non-zero if show() will not call Fl::error() if called with
the given or current mode.
</ul><h4><code>char Fl_Gl_Window::valid() const;
<br>void Fl_Gl_Window::invalidate();
<br>void Fl_Gl_Window::valid(char i);</code></h4><ul>
<code>Fl_Gl_Window::valid()</code> is turned off when fltk creates a
new context for this window and by the window resizing, and is turned
on <i>after</i> draw() is called. You can use this inside your draw()
method to avoid unneccessarily initializing the OpenGL context. Just
do this:
<ul><pre><code>void mywindow::draw() {
if (!valid()) {
glViewport(0,0,w(),h());
glFrustum(...);
glLight(...);
...other initilization...
}
... draw your geometry here ...
}
</code></pre></ul>
<p>You can also turn valid() off yourself (for instance if you know
the current projection has changed). To do this call
<code>invalidate()</code>.
<p>You can turn valid() on by calling valid(1). You should only do
this after fixing the transformation inside a draw() or after
make_current(). This is done automatically after draw() returns.
</ul><h4><code>void Fl_Gl_Window::ortho();</code></h4><ul>
Set the projection so 0,0 is in the lower left of the window and each
pixel is 1 unit wide/tall. If you are drawing 2D images, your draw()
method may want to call this if valid() is false.
</ul><h4><code>void Fl_Gl_Window::make_current();
<br>void Fl_Gl_Window::make_overlay_current();
<br>void Fl_Gl_Window::swap_buffers();</code></h4><ul>
These functions can be used to set the current GL context to a window
and draw into it incrementally, rather than using the draw() method.
You will also need to call make_current() to do OpenGL feedback or hit
detection in response to events. After calling make_current(), be
sure to test valid(), and if false, initialize the transformation and
call valid(1).
</ul><h4><code>void Fl_Gl_Window::hide();
<br>Fl_Gl_Window::~Fl_Gl_Window();</code></h4><ul>
Hiding the window or destroying it also destroys the OpenGL context it
uses.
</ul><h2>Fl_Gl_Window overlay</h2>
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.
</ul><h4><code>int Fl_Gl_Window::can_do_overlay();</code></h4><ul>
Returns true if the hardware overlay is possible. If this is false,
fltk will try to simulate the overlay, with significant loss of update
speed. Calling this will cause fltk to open the display.
</ul><h4><code>void Fl_Gl_Window::redraw_overlay();</code></h4><ul>
Call this if what is drawn in the overlay needs to change, this will
cause draw_overlay to be called at a later time. Initially the
overlay is clear, if you want the window to display something in the
overlay when it first appears, you must call this immediately after
you show() your window.
</ul><h4><code>virtual void Fl_Gl_Window::draw_overlay();</code></h4><ul>
You must implement this virtual function if you want to draw into the
overlay. The overlay is cleared before this is called. You should
draw anything that is not clear, using OpenGl. You must use
gl_color(i) to choose colors (it allocates them from the colormap
using system-specific calls), and remember that you are in an indexed
OpenGL mode and drawing anything other than flat-shaded will probably
not work.
<p>Both this function and Fl_Gl_Window::draw() must check
Fl_Gl_Window::valid(), and set the same transformation. If you don't
your code may not work on other systems. Depending on the OS, and on
whether overlays are real or simulated, the OpenGL context may be the
same or different between the overlay and main window.
</ul><p><hr>
<a name=gl_start>
<h2>Using OpenGL in normal Fltk windows</h2>
<p>You can put OpenGL code into an <a
href=subclass.html#draw>Fl_Widget::draw()</a> method or into the code
for a <a href=Boxtypes.html>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/X so that it
knows it is going to use OpenGL. You may use any of the symbols
described for <a href=#mode>Fl_Gl_Window::mode()</a> to describe how
you intend to use OpenGL:
<ul><p><code> Fl::gl_visual(FL_RGB);</code></ul>
<p>You can then put OpenGL drawing code anywhere you can draw normally
by surrounding it with:
<ul><p><code>gl_start();</code><br>
<i>... put your OpenGL code here ...</i><br>
<code>gl_finish();</code></ul>
<p>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.
<p>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().
<p>You may want to use <code>Fl_Window::current()->h()</code> to get
the drawable height so you can flip the coordinate system.
<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=Fl.html#gl_visual>Fl::gl_visual()</a>.
<li>You cannot pass FL_DOUBLE to Fl::gl_visual().
<li>You cannot use Fl_Double_Window (or Fl_Overlay_Window).
</ul>
<p>Do <i>not</i> call gl_start()/gl_finish() when drawing an
Fl_Gl_Window!
</ul><p><hr>
<a name=drawing>
<h2>OpenGL drawing functions
<br>#include <FL/gl_draw.H></h2>
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>).
</ul><h4><code>void gl_color(Fl_Color);</code></h4><ul>
Set the current color to a fltk color index. <i>For color-index modes
it will use fl_xpixel(c), which is only right if this window uses the
default X colormap</i>.
</ul><h4><code>void gl_rect(int x,int y,int w,int h);
<br>void gl_rectf(int x,int y,int w,int h);</code></h4><ul>
Outline or fill a rectangle with the current color. If ortho() has
been called, then the rectangle will exactly fill the pixel rectangle
passed.
</ul><h4><code>void gl_font(Fl_Font fontid, int size);</code></h4><ul>
Set the "current GL font" to the same font you get by calling
<a href=Draw.html#fl_font>fl_font()</a>.
</ul><h4><code>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);</code></h4><ul>
Return information about the current GL font.
</ul><h4><code>void gl_draw(const char *);
<br>void gl_draw(const char *, int n);</code></h4><ul>
Draw a null-terminated string or an array of <i>n</i> characters in
the current GL font at the current glRasterPos.
</ul><h4><code>void gl_draw(const char *, int x, int y);
<br>void gl_draw(const char *, int n, int x, int y);</code></h4><ul>
Draw a null-terminated string or an array of <i>n</i> characters in
the current GL font at the given position.
</ul><h4><code>void gl_draw(const char *, int x, int y, int w, int h, Fl_Align);</code></h4><ul>
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=Draw.html#fl_draw>fl_draw()</a>.
</ul>
<p><a href = index.html>(back to contents)</a>
<title>Fltk example: shape.C</title>
<h2>shape.C</h2>
<p>Of course GL is no fun unless you can draw your own graphics. This
is done with a subclass that you create:
<p><img src = shape.C.gif align=top>
<pre>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Hor_Slider.H>
#include <FL/math.h>
#include <FL/gl.h>
#include <FL/Fl_Gl_Window.H>
class shape_window : public Fl_Gl_Window {
void draw();
public:
int sides;
shape_window(int x,int y,int w,int h,const char *l=0);
};
shape_window::shape_window(int x,int y,int w,int h,const char *l) :
Fl_Gl_Window(x,y,w,h,l) {
sides = 3;
}
void shape_window::draw() {
// the valid() property may be used to avoid reinitializing your
// GL transformation for each redraw:
if (!valid()) {
valid(1);
glLoadIdentity();
glViewport(0,0,w(),h());
}
// draw an amazing graphic:
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(.5,.6,.7);
glBegin(GL_POLYGON);
for (int i=0; i<sides; i++) {
double ang = i*2*M_PI/sides;
glVertex3f(cos(ang),sin(ang),0);
}
glEnd();
}
// when you change the data, as in this callback, you must call redraw():
void sides_cb(Fl_Widget *o, void *p) {
shape_window *sw = (shape_window *)p;
sw->sides = int(((Fl_Slider *)o)->value());
sw->redraw();
}
int main(int argc, char **argv) {
Fl_Window window(300, 330);
shape_window sw(10, 10, 280, 280);
window.resizable(&sw);
Fl_Hor_Slider slider(50, 295, window.w()-60, 30, "Sides:");
slider.align(FL_ALIGN_LEFT);
slider.callback(sides_cb,&sw);
slider.value(sw.sides);
slider.step(1);
slider.bounds(3,40);
window.show(argc,argv);
return Fl::run();
}
</pre>
<p>To do your own drawing, you must subclass <a
href=Fl_Gl_Window.html>Fl_Gl_Window</a>. The virtual method <a
href=subclass.html#draw>draw()</a> is called when the window should
update. You can only draw into the window inside a draw() method.
You call the method <a href=Fl_Widget.html#redraw>redraw()</a> on the
window to indicate that draw() needs to be called. It won't actually
be called until <a href=Fl.html#wait>Fl::wait()</a> is called.
<P>The window may be made a child of another window, as it is here.
This is done by add()ing it to a parent before you show() it. <i>If
you don't want to make a child window, be sure to end() the previous
window!</i> The Fl_Gl_Window constructor automatically does end() so
you don't accidentally add children to it.
<p>The files <FL/math.h> and <FL/gl.h> are wrappers for the
normal header files. You should use them to port to MSWindows because
the MicroSoft header files have errors or ommisions in them.
<p><a href = index.html>[back to contents]</a>
|