diff options
Diffstat (limited to 'documentation/opengl.html')
| -rw-r--r-- | documentation/opengl.html | 486 |
1 files changed, 486 insertions, 0 deletions
diff --git a/documentation/opengl.html b/documentation/opengl.html new file mode 100644 index 000000000..e26895a2c --- /dev/null +++ b/documentation/opengl.html @@ -0,0 +1,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> |
