1/** 2 3 \page opengl Using OpenGL 4 5This chapter discusses using FLTK for your OpenGL applications. 6 7\section opengl_using Using OpenGL in FLTK 8 9The easiest way to make an OpenGL display is to subclass 10Fl_Gl_Window. 11Your subclass must implement a \p draw() method which uses 12OpenGL calls to draw the display. Your main program should call 13\p redraw() when the display needs to change, and 14(somewhat later) FLTK will call \p draw(). 15 16With a bit of care you can also use OpenGL to draw into 17normal FLTK windows. This allows you to use Gouraud shading for 18drawing your widgets. To do this you use the 19\ref opengl_gl_start "gl_start()" and 20\ref opengl_gl_finish "gl_finish()" 21functions around your OpenGL code. 22 23You must include FLTK's \p <FL/gl.h> header 24file. It will include the file \p <GL/gl.h>, define 25some extra drawing functions provided by FLTK, and include the 26\p <windows.h> header file needed by WIN32 27applications. 28 29Some simple coding rules (see \ref osissues_retina) allow to write cross-platform code that will draw high resolution 30OpenGL graphics if run on 'retina' displays with Mac OS X. 31 32\section opengl_subclass Making a Subclass of Fl_Gl_Window 33 34To make a subclass of Fl_Gl_Window, you must provide: 35 36\li A class definition. 37\li A \p draw() method. 38\li A \p handle() method if you need to receive input from the user. 39 40If your subclass provides static controls in the window, they 41must be redrawn whenever the \p FL_DAMAGE_ALL bit is set 42in the value returned by \p damage(). For double-buffered 43windows you will need to surround the drawing code with the 44following code to make sure that both buffers are redrawn: 45 46\code 47#ifndef MESA 48glDrawBuffer(GL_FRONT_AND_BACK); 49#endif // !MESA 50... draw stuff here ... 51#ifndef MESA 52glDrawBuffer(GL_BACK); 53#endif // !MESA 54\endcode 55 56<CENTER><TABLE WIDTH="80%" BORDER="1" CELLPADDING="5" CELLSPACING="0" BGCOLOR="#cccccc"> 57<TR> 58 <TD><B>Note:</B> 59 60 If you are using the Mesa graphics library, the call 61 to \p glDrawBuffer() is not required and will slow 62 down drawing considerably. The preprocessor instructions 63 shown above will optimize your code based upon the 64 graphics library used. 65 66 </TD> 67 68</TR> 69</TABLE></CENTER> 70 71\subsection opengl_defining Defining the Subclass 72 73To define the subclass you just subclass the Fl_Gl_Window class: 74 75\code 76class MyWindow : public Fl_Gl_Window { 77 void draw(); 78 int handle(int); 79 80public: 81 MyWindow(int X, int Y, int W, int H, const char *L) 82 : Fl_Gl_Window(X, Y, W, H, L) {} 83}; 84\endcode 85 86The \p draw() and \p handle() methods are 87described below. Like any widget, you can include additional 88private and public data in your class (such as scene graph 89information, etc.) 90 91\subsection opengl_draw The draw() Method 92 93The \p draw() method is where you actually do your OpenGL drawing: 94 95\code 96void MyWindow::draw() { 97 if (!valid()) { 98 ... set up projection, viewport, etc ... 99 ... window size is in w() and h(). 100 ... valid() is turned on by FLTK after draw() returns 101 } 102 ... draw ... 103} 104\endcode 105 106\subsection opengl_handle The handle() Method 107 108The \p handle() method handles mouse and keyboard 109events for the window: 110 111\code 112int MyWindow::handle(int event) { 113 switch(event) { 114 case FL_PUSH: 115 ... mouse down event ... 116 ... position in Fl::event_x() and Fl::event_y() 117 return 1; 118 case FL_DRAG: 119 ... mouse moved while down event ... 120 return 1; 121 case FL_RELEASE: 122 ... mouse up event ... 123 return 1; 124 case FL_FOCUS : 125 case FL_UNFOCUS : 126 ... Return 1 if you want keyboard events, 0 otherwise 127 return 1; 128 case FL_KEYBOARD: 129 ... keypress, key is in Fl::event_key(), ascii in Fl::event_text() 130 ... Return 1 if you understand/use the keyboard event, 0 otherwise... 131 return 1; 132 case FL_SHORTCUT: 133 ... shortcut, key is in Fl::event_key(), ascii in Fl::event_text() 134 ... Return 1 if you understand/use the shortcut event, 0 otherwise... 135 return 1; 136 default: 137 // pass other events to the base class... 138 return Fl_Gl_Window::handle(event); 139 } 140} 141\endcode 142 143When \p handle() is called, the OpenGL context is not 144set up! If your display changes, you should call 145\p redraw() and let \p draw() do the work. Don't 146call any OpenGL drawing functions from inside \p handle()! 147 148You can call \e some OpenGL stuff like hit detection and texture 149loading functions by doing: 150 151\code 152 case FL_PUSH: 153 make_current(); // make OpenGL context current 154 if (!valid()) { 155 156 ... set up projection exactly the same as draw ... 157 158 valid(1); // stop it from doing this next time 159 } 160 ... ok to call NON-DRAWING OpenGL code here, such as hit 161 detection, loading textures, etc... 162\endcode 163 164Your main program can now create one of your windows by doing 165<tt>new MyWindow(...)</tt>. 166 167You can also use your new window class in 168\ref fluid "FLUID" 169by: 170 171-# Putting your class definition in a \p MyWindow.H file. 172-# Creating a Fl_Box widget in FLUID. 173-# In the widget panel fill in the "class" field with \p MyWindow. 174 This will make FLUID produce constructors for your new class. 175-# In the "Extra Code" field put <tt>\#include "MyWindow.H"</tt>, 176 so that the FLUID output file will compile. 177 178You must put <tt>glwindow->show()</tt> in your main code 179after calling \p show() on the window containing the 180OpenGL window. 181 182\section opengl_normal Using OpenGL in Normal FLTK Windows 183 184You can put OpenGL code into the \p draw() method, as described in 185\ref subclassing_drawing 186in the previous chapter, or into the code for a 187\ref common_boxtypes "boxtype" 188or other places with some care. 189 190Most importantly, before you show \e any windows, 191including those that don't have OpenGL drawing, you <B>must</B> 192initialize FLTK so that it knows it is going to use OpenGL. You 193may use any of the symbols described for \p Fl_Gl_Window::mode() 194to describe how you intend to use OpenGL: 195 196\code 197Fl::gl_visual(FL_RGB); 198\endcode 199 200\anchor opengl_gl_start 201\anchor opengl_gl_finish 202You can then put OpenGL drawing code anywhere you can draw 203normally by surrounding it with 204gl_start() and gl_finish() to set up, and later release, an OpenGL 205context with an orthographic projection so that 0,0 is the 206lower-left corner of the window and each pixel is one unit. The 207current clipping is reproduced with OpenGL \p glScissor() 208commands. These functions also synchronize the OpenGL graphics stream 209with the drawing done by other X, WIN32, or FLTK functions. 210 211\code 212gl_start(); 213... put your OpenGL code here ... 214gl_finish(); 215\endcode 216 217The same context is reused each time. If your code changes 218the projection transformation or anything else you should use 219\p glPushMatrix() and \p glPopMatrix() functions to 220put the state back before calling \p gl_finish(). 221 222You may want to use <tt>Fl_Window::current()-\>h()</tt> to 223get the drawable height so that you can flip the Y 224coordinates. 225 226Unfortunately, there are a bunch of limitations you must 227adhere to for maximum portability: 228 229\li You must choose a default visual with Fl::gl_visual(). 230 231\li You cannot pass \p FL_DOUBLE to Fl::gl_visual(). 232 233\li You cannot use Fl_Double_Window or Fl_Overlay_Window. 234 235Do \e not call \p gl_start() or 236\p gl_finish() when drawing into an Fl_Gl_Window ! 237 238\section opengl_drawing OpenGL Drawing Functions 239 240FLTK provides some useful OpenGL drawing functions. They can 241be freely mixed with any OpenGL calls, and are defined by 242including \p <FL/gl.h> which you should include 243instead of the OpenGL header \p <GL/gl.h>. 244 245void gl_color(Fl_Color) 246 247\par 248Sets the current OpenGL color to a FLTK color. <I>For 249color-index modes it will use \p fl_xpixel(c), which is 250only right if this window uses the default colormap!</I> 251 252void gl_rect(int x, int y, int w, int h) <br> 253void gl_rectf(int x, int y, int w, int h) 254 255\par 256Outlines or fills a rectangle with the current color. If 257Fl_Gl_Window::ortho() has been called, then the rectangle will exactly 258fill the pixel rectangle passed. 259 260void gl_font(Fl_Font fontid, int size) 261 262\par 263Sets the current OpenGL font to the same font you get by calling 264\ref ssect_Fonts "fl_font()". 265 266int gl_height() <br> 267int gl_descent() <br> 268float gl_width(const char *s) <br> 269float gl_width(const char *s, int n) <br> 270float gl_width(uchar c) 271 272\par 273Returns information about the current OpenGL font. 274 275void gl_draw(const char *s) <br> 276void gl_draw(const char *s, int n) 277 278\par 279Draws a nul-terminated string or an array of \p n 280characters in the current OpenGL font at the current raster 281position. 282 283void gl_draw(const char *s, int x, int y) <br> 284void gl_draw(const char *s, int n, int x, int y) <br> 285void gl_draw(const char *s, float x, float y) <br> 286void gl_draw(const char *s, int n, float x, float y) 287 288\par 289Draws a nul-terminated string or an array of \p n 290characters in the current OpenGL font at the given position. 291 292void gl_draw(const char *s, int x, int y, int w, int h, Fl_Align) 293 294\par 295Draws a string formatted into a box, with newlines and tabs 296expanded, other control characters changed to ^X, and aligned 297with the edges or center. Exactly the same output as 298\ref ssect_Text "fl_draw()". 299 300\section opengl_speed Speeding up OpenGL 301 302Performance of Fl_Gl_Window may be improved on some types of 303OpenGL implementations, in particular MESA and other software 304emulators, by setting the \p GL_SWAP_TYPE environment 305variable. This variable declares what is in the backbuffer after 306you do a swapbuffers. 307 308\li <tt>setenv GL_SWAP_TYPE COPY</tt> <br> 309 <br> 310 This indicates that the back buffer is copied to the 311 front buffer, and still contains its old data. This is 312 true of many hardware implementations. Setting this 313 will speed up emulation of overlays, and widgets that 314 can do partial update can take advantage of this as 315 \p damage() will not be cleared to -1. 316 317\li <tt>setenv GL_SWAP_TYPE NODAMAGE</tt> <br> 318 <br> 319 This indicates that nothing changes the back buffer 320 except drawing into it. This is true of MESA and Win32 321 software emulation and perhaps some hardware emulation 322 on systems with lots of memory. 323 324\li All other values for \p GL_SWAP_TYPE, and not 325 setting the variable, cause FLTK to assume that the 326 back buffer must be completely redrawn after a swap. 327 328This is easily tested by running the \ref examples_gl_overlay demo 329program and seeing if the display is correct when you drag 330another window over it or if you drag the window off the screen 331and back on. You have to exit and run the program again for it 332to see any changes to the environment variable. 333 334\section opengl_optimizer Using OpenGL Optimizer with FLTK 335 336<A href="http://www.sgi.com/software/optimizer">OpenGL Optimizer</A> 337is a scene graph toolkit for OpenGL available from 338Silicon Graphics for IRIX and Microsoft Windows. It allows you 339to view large scenes without writing a lot of OpenGL code. 340 341\par OptimizerWindow Class Definition 342 343\par 344To use 345<A href="http://www.sgi.com/software/optimizer">OpenGL Optimizer</A> 346with FLTK you'll need to create a 347subclass of Fl_Gl_Widget that includes several state 348variables: 349 350\code 351class OptimizerWindow : public Fl_Gl_Window { 352 csContext *context_; // Initialized to 0 and set by draw()... 353 csDrawAction *draw_action_; // Draw action... 354 csGroup *scene_; // Scene to draw... 355 csCamara *camera_; // Viewport for scene... 356 357 void draw(); 358 359public: 360 OptimizerWindow(int X, int Y, int W, int H, const char *L) 361 : Fl_Gl_Window(X, Y, W, H, L) { 362 context_ = (csContext *)0; 363 draw_action_ = (csDrawAction *)0; 364 scene_ = (csGroup *)0; 365 camera_ = (csCamera *)0; 366 } 367 368 void scene(csGroup *g) { scene_ = g; redraw(); } 369 370 void camera(csCamera *c) { 371 camera_ = c; 372 if (context_) { 373 draw_action_->setCamera(camera_); 374 camera_->draw(draw_action_); 375 redraw(); 376 } 377 } 378}; 379\endcode 380 381\par The camera() Method 382 383\par 384The \p camera() method sets the camera (projection and 385viewpoint) to use when drawing the scene. The scene is redrawn after 386this call. 387 388\par The draw() Method 389 390\par 391The \p draw() method performs the needed initialization and does 392the actual drawing: 393 394\code 395void OptimizerWindow::draw() { 396 if (!context_) { 397 // This is the first time we've been asked to draw; create the 398 // Optimizer context for the scene... 399 400#ifdef WIN32 401 context_ = new csContext((HDC)fl_getHDC()); 402 context_->ref(); 403 context_->makeCurrent((HDC)fl_getHDC()); 404#else 405 context_ = new csContext(fl_display, fl_visual); 406 context_->ref(); 407 context_->makeCurrent(fl_display, fl_window); 408#endif // WIN32 409 410 ... perform other context setup as desired ... 411 412 // Then create the draw action to handle drawing things... 413 414 draw_action_ = new csDrawAction; 415 if (camera_) { 416 draw_action_->setCamera(camera_); 417 camera_->draw(draw_action_); 418 } 419 } else { 420#ifdef WIN32 421 context_->makeCurrent((HDC)fl_getHDC()); 422#else 423 context_->makeCurrent(fl_display, fl_window); 424#endif // WIN32 425 } 426 427 if (!valid()) { 428 // Update the viewport for this context... 429 context_->setViewport(0, 0, w(), h()); 430 } 431 432 // Clear the window... 433 context_->clear(csContext::COLOR_CLEAR | csContext::DEPTH_CLEAR, 434 0.0f, // Red 435 0.0f, // Green 436 0.0f, // Blue 437 1.0f); // Alpha 438 439 // Then draw the scene (if any)... 440 if (scene_) 441 draw_action_->apply(scene_); 442} 443\endcode 444 445\par The scene() Method 446 447\par 448The \p scene() method sets the scene to be drawn. The scene is 449a collection of 3D objects in a \p csGroup. The scene is redrawn 450after this call. 451 452\section opengl3 Using OpenGL 3.0 (or higher versions) 453 454The examples subdirectory contains OpenGL3test.cxx, a toy program 455showing how to use OpenGL 3.0 (or higher versions) with FLTK in a cross-platform fashion. 456It contains also OpenGL3-glut-test.cxx which shows how to use FLTK's GLUT compatibility 457and OpenGL 3. 458 459To access OpenGL 3.0 (or higher versions), use the <tt>FL_OPENGL3</tt> flag 460when calling Fl_Gl_Window::mode(int a) or glutInitDisplayMode(). 461 462<b>On the Windows and Unix/Linux platforms</b>, FLTK creates contexts 463implementing the highest OpenGL version supported by the hardware. 464Such contexts may also be compatible with lower OpenGL versions. 465Access to functions from OpenGL 466versions above 1.1 requires to load function pointers at runtime on these platforms. 467FLTK recommends to use the GLEW library to perform this. It is therefore 468necessary to install the GLEW library (see below). 469 470<b>On the macOS platform</b>, MacOS 10.7 or above is required; 471GLEW is possible but not necessary. FLTK creates contexts for OpenGL 472versions 1 and 2 without the FL_OPENGL3 473flag and for OpenGL versions 3.2 and above with it. 474 475\par GLEW installation (Unix/Linux and MSWindows platforms) 476GLEW is available as a package for most Linux distributions and in source form at http://glew.sourceforge.net/. 477For 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. 478 479\par Source-level changes for OpenGL 3: 480\li Put this in all OpenGL-using source files (instead of \#include <FL/gl.h>, 481and before \#include <FL/glut.h> if you use GLUT): 482\code 483#if defined(__APPLE__) 484# include <OpenGL/gl3.h> // defines OpenGL 3.0+ functions 485#else 486# if defined(WIN32) 487# define GLEW_STATIC 1 488# endif 489# include <GL/glew.h> 490#endif 491\endcode 492\li Add the <tt>FL_OPENGL3</tt> flag when calling Fl_Gl_Window::mode(int a) 493or glutInitDisplayMode(). 494\li Put this in the <tt>handle(int event)</tt> member function of the first to be created 495among your Fl_Gl_Window-derived classes: 496\code 497#ifndef __APPLE__ 498 static int first = 1; 499 if (first && event == FL_SHOW && shown()) { 500 first = 0; 501 make_current(); 502 glewInit(); // defines pters to functions of OpenGL V 1.2 and above 503 } 504#endif 505\endcode 506\li Alternatively, if you use GLUT, put 507\code 508#ifndef __APPLE__ 509 glewInit(); // defines pters to functions of OpenGL V 1.2 and above 510#endif 511\endcode 512after the first glutCreateWindow() call. 513 514If GLEW is installed on the Mac OS development platform, it is possible 515to use the same code for all platforms, with one exception: put 516\code 517#ifdef __APPLE__ 518glewExperimental = GL_TRUE; 519#endif 520\endcode 521before the glewInit() call. 522 523\par Changes in the build process 524Link with libGLEW.so (on Unix/Linux), libglew32.a (with MinGW) or glew32.lib 525(with MS Visual Studio); no change is needed on the Mac OS platform. 526 527\htmlonly 528<hr> 529<table summary="navigation bar" width="100%" border="0"> 530<tr> 531 <td width="45%" align="LEFT"> 532 <a class="el" href="subclassing.html"> 533 [Prev] 534 Adding and Extending Widgets 535 </a> 536 </td> 537 <td width="10%" align="CENTER"> 538 <a class="el" href="index.html">[Index]</a> 539 </td> 540 <td width="45%" align="RIGHT"> 541 <a class="el" href="fluid.html"> 542 Programming with FLUID 543 [Next] 544 </a> 545 </td> 546</tr> 547</table> 548\endhtmlonly 549 550*/ 551