With >a >bit >of >care >you >can >also >use > OpenGL >to >draw >into >normal >FLTK > windows. > This > allows >you >to >use >Gouraud >shading >for > drawing > your >widgets. > To >do >this >you >use >the > gl_start()> and >gl_finish()> functions >around >your >OpenGL >code. >
You >must >include >FLTK's ><FL/gl.h>> header > file. > It >will > include >the >file ><GL/gl.h>> , >define >some >extra >drawing > functions >provided >by > FLTK, >and >include >the ><windows.h>> header > file >needed >by >WIN32 >applications. >
>#ifndef MESA >glDrawBuffer(GL_FRONT_AND_BACK); >#endif // !MESA >... draw stuff here ... >#ifndef MESA >glDrawBuffer(GL_BACK); >#endif // !MESA >Note:> If >you >are >using >the >Mesa >graphics > library, >the >call >to >glDrawBuffer()> is >not > required >and >will >slow >down >drawing >considerably. > The >preprocessor >instructions >shown >above >will > optimize >your >code >based >upon >the >graphics >library > used. >
>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) {}
>};
>
The >draw()> and >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.) >
>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_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);
> }
>}
>
When >handle()> is >called, >the >OpenGL >context >
is >not >set >up! > If >your >display >changes, >you >
should >call >redraw()> and >let > draw()>
do >the >work. >Don't >call >any >OpenGL >drawing >
functions >from > inside >handle()>! >
You >can >call >some> OpenGL >stuff >like >hit > detection >and >texture > loading >functions >by >doing: >
> 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...
>
Your >main >program >can >now >create >one >of >your >
windows >by >doing >new > MyWindow(...)>. > You >
can >also >use >FLUID> by: >
Most >importantly, >before >you >show >any> 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 > Fl_Gl_Window::mode()> to >describe >how >you > intend >to >use >OpenGL: >
>Fl::gl_visual(FL_RGB); >You >can >then >put >OpenGL >drawing >code >anywhere > you >can >draw >normally >by > surrounding >it >with: >
>gl_start(); >... put your OpenGL code here ... >gl_finish(); >gl_start()> and > gl_finish()> 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 > glScissor()> commands. > These >also >synchronize >the >OpenGL >graphics > stream > with >the >drawing >done >by >other >X, >WIN32, >or > FLTK >functions. >
The >same >context >is >reused >each >time. > If > your >code >changes >the > projection >transformation >or > anything >else >you >should >use > glPushMatrix()> and >glPopMatrix()> functions >to >put >the > state >back >before >calling >gl_finish()>. >
You >may >want >to >use >Fl_Window::current()->h()> 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: >
This >indicates >that >the >back >buffer >is >copied > to >the >front >buffer, >and >still >contains >it's > 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 > damage() >will >not >be >cleared >to >-1. >
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. >
This >is >easily >tested >by >running >the >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. >
>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();
> }
> }
>};
>
> >
>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_);
>}
>