summaryrefslogtreecommitdiff
path: root/documentation/src/opengl.dox
blob: c1ce8a9fd4890bf3d21190f2d7a4d2371e21f2a8 (plain)
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
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
/**

 \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 (see \ref opengl_normal below). 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> (on macOS: \p <OpenGL/gl.h>), define
some extra drawing functions provided by FLTK, and include the
\p <windows.h> header file needed by Windows applications.

Some simple coding rules (see \ref opengl_highdpi) allow to write
cross-platform code that will support OpenGL run on HighDPI displays
(including the 'retina' displays of Apple hardware).

\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().

\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 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_highdpi  OpenGL and support of HighDPI displays

HighDPI displays (including the so-called 'retina' displays of Apple
hardware) are supported by FLTK in such a way that 1 unit of an FLTK
quantity (say, the value given by Fl_Gl_Window::w()) corresponds to more
than 1 pixel on the display. Conversely, when a program specifies the width and height of the
OpenGL viewport, it is necessary to use an API that returns quantities expressed in pixels.
That can be done as follows:
\code
  Fl_Gl_Window *glw = ...;
  glViewport(0, 0, glw->pixel_w(), glw->pixel_h());
\endcode
which makes use of the Fl_Gl_Window::pixel_w() and Fl_Gl_Window::pixel_h() methods giving
the size in pixels of an Fl_Gl_Window that is potentially mapped to a HighDPI display.
Method Fl_Gl_Window::pixels_per_unit() can also be useful in this context.
\note A further coding rule is necessary to properly support retina displays
and OpenGL under macOS (see \ref osissues_retina)

\section opengl_normal Using OpenGL in Normal FLTK Windows

\note Drawing both with OpenGL and Quartz in a normal FLTK window is not possible
with the macOS platform. This technique is therefore not useful under macOS because
it permits nothing more than what is possible with class Fl_Gl_Window.

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, Windows, 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_with_fltk_widgets Using FLTK widgets in OpenGL Windows

FLTK widgets can be added to `Fl_Gl_Window`s just as they would be added to
`Fl_Window`s. They are rendered as an overlay over the user defined
OpenGL graphics using 'fl_..' graphics calls that are implemented in GL.

`Fl_Gl_Window` does not add subsequent widgets as children by default as
`Fl_Window` does. Call `myGlWindow->begin()` after creating the GL window to
automatically add following widgets. Remember to call `myGlWindow->end()`.

\code
class My_Gl_Window : public Fl_Gl_Window {
...
  void draw();
...
};

...
myGlWindow = new My_Gl_Window(0, 0, 500, 500);
myGlWindow->begin();
myButton = new Fl_Button(10, 10, 120, 24, "Hello!");
myGlWindow->end();
...

void My_Gl_Window::draw() {
  // ... user GL drawing code
  Fl_Gl_Window::draw(); // Draw FLTK child widgets.
}
\endcode

Users can draw into the overlay by using GL graphics calls as well as all
`fl_...` graphics calls from the "Drawing Fast Shapes" section.

\code
void My_Gl_Window::draw() {
  // ... user GL drawing code
  Fl_Gl_Window::draw_begin(); // Set up 1:1 projection
  Fl_Window::draw();          // Draw FLTK children
  fl_color(FL_RED);
  fl_rect(10, 10, 100, 100);
  Fl_Gl_Window::draw_end();   // Restore GL state
}
\endcode

Widgets can be drawn with transparencies by assigning an alpha value to a
colormap entry and using that color in the widget.

\code
Fl::set_color(FL_FREE_COLOR, 255, 255, 0, 127); // 50% transparent yellow
myGlWindow = new My_Gl_Window(0, 0, 500, 500);
myGlWindow->begin();
myButton = new Fl_Button(10, 10, 120, 24, "Hello!");
myButton->box(FL_BORDER_BOX);
myButton->color(FL_FREE_COLOR);
myGlWindow->end();
\endcode

Transparencies can also be set directly when drawing. This can be used to
create custom box types and RGB overlay drawings with an alpha channel.

\code
fl_color(0, 255, 0, 127); // 50% transparent green
fl_rectf(10, 10, 100, 100);
fl_color(FL_RED); // back to opaque red
fl_rect(20, 20, 80, 80);
\endcode


\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 drawing_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 drawing_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 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 (<b>but not below</b>) with it.

\par GLEW installation (Linux and Windows platforms)
FLTK needs a header file, \c GL/glew.h, and a library, \c libGLEW.* or
equivalent, to support OpenGL 3 and above.
<br>These can be obtained for most Linux distributions
by installing package libglew-dev.
<br>For the Windows platform :
<ul>
<li> the header and a Visual Studio static library (glew32.lib) can
be downloaded from http://glew.sourceforge.net/ ;
<li> a MinGW-style static library (libglew32.a)
can be built from source (same web site) with the make command.
Alternatively, pre-built files are available for these architectures :
<ul><li>x86: download files
<a href=https://github.com/McNopper/OpenGL/blob/42fa2736e2b37db1df99c2b205279855ec6f2b1d/External/x86/Windows/MinGW/include/GL/glew.h>
glew.h</a> and
<a href=https://github.com/McNopper/OpenGL/blob/42fa2736e2b37db1df99c2b205279855ec6f2b1d/External/x86/Windows/MinGW/lib/libglew32.a>
libglew32.a</a>;
<li>x86_64: install GLEW as an MSYS2 package with command :
\code
pacman -S mingw-w64-x86_64-glew
\endcode
</ul>
</ul>

\par  Source-level changes for OpenGL 3:
\li Put this in all OpenGL-using source files (instead of, or before if needed, \#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 Testing for success of the glewInit() call
Testing whether the glewInit() call is successful is to be done as follows:
\code
#include <FL/platform.H> // defines FLTK_USE_WAYLAND under the Wayland platform
#include <FL/Fl.H> // for Fl::warning()
#ifndef __APPLE__
#  if defined(_WIN32)
#    define GLEW_STATIC 1
#  endif
#  include <GL/glew.h>

  GLenum err = glewInit(); // defines pters to functions of OpenGL V 1.2 and above
#  ifdef FLTK_USE_WAYLAND
      // glewInit returns GLEW_ERROR_NO_GLX_DISPLAY with Wayland
      if (fl_wl_display() && err == GLEW_ERROR_NO_GLX_DISPLAY) err = GLEW_OK;
#  endif
  if (err != GLEW_OK) Fl::warning("glewInit() failed returning %u", err);
#endif // ! __APPLE__
\endcode


\par Changes in the build process
Link with libGLEW.so (with X11 or Wayland), 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="fltk-options.html">
    FLTK Runtime Options
    [Next]
    </a>
  </td>
</tr>
</table>
\endhtmlonly

*/