]> git.mxchange.org Git - flightgear.git/blob - src/Cockpit/panel.cxx
c2f6f997f51426a7d5807bddc4bb49879a79d446
[flightgear.git] / src / Cockpit / panel.cxx
1 //  panel.cxx - default, 2D single-engine prop instrument panel
2 //
3 //  Written by David Megginson, started January 2000.
4 //
5 //  This program is free software; you can redistribute it and/or
6 //  modify it under the terms of the GNU General Public License as
7 //  published by the Free Software Foundation; either version 2 of the
8 //  License, or (at your option) any later version.
9 // 
10 //  This program is distributed in the hope that it will be useful, but
11 //  WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 //  General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program; if not, write to the Free Software
17 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 //
19 //  $Id$
20
21 //JVK
22 // On 2D panels all instruments include light sources were in night displayed
23 // with a red mask (instrument light). It is not correct for light sources
24 // (bulbs). There is added new layer property "emissive" (boolean) (only for
25 // textured layers).
26 // If a layer has to shine set it in the "instrument_def_file.xml" inside the
27 // <layer> tag by adding <emissive>true</emissive> tag. When omitted the default
28 // value is for backward compatibility set to false.
29
30 #ifdef HAVE_CONFIG_H
31 #  include <config.h>
32 #endif
33
34 #ifdef HAVE_WINDOWS_H          
35 #  include <windows.h>
36 #endif
37
38 #include <stdio.h>      // sprintf
39 #include <string.h>
40
41 #include <osg/CullFace>
42 #include <osg/Depth>
43 #include <osg/Material>
44 #include <osg/TexEnv>
45 #include <osg/PolygonOffset>
46
47 #include <simgear/compiler.h>
48
49 #include SG_GLU_H
50
51 #include <plib/fnt.h>
52
53 #include <simgear/debug/logstream.hxx>
54 #include <simgear/misc/sg_path.hxx>
55 #include <simgear/scene/model/model.hxx>
56
57 #include <Main/globals.hxx>
58 #include <Main/fg_props.hxx>
59 #include <Main/viewmgr.hxx>
60 #include <Time/light.hxx>
61 #include <GUI/new_gui.hxx>      // FGFontCache
62
63 #include "hud.hxx"
64 #include "panel.hxx"
65
66 #define WIN_X 0
67 #define WIN_Y 0
68 #define WIN_W 1024
69 #define WIN_H 768
70
71 // The number of polygon-offset "units" to place between layers.  In
72 // principle, one is supposed to be enough.  In practice, I find that
73 // my hardware/driver requires many more.
74 #define POFF_UNITS 8
75
76 ////////////////////////////////////////////////////////////////////////
77 // Local functions.
78 ////////////////////////////////////////////////////////////////////////
79
80
81 /**
82  * Calculate the aspect adjustment for the panel.
83  */
84 static float
85 get_aspect_adjust (int xsize, int ysize)
86 {
87   float ideal_aspect = float(WIN_W) / float(WIN_H);
88   float real_aspect = float(xsize) / float(ysize);
89   return (real_aspect / ideal_aspect);
90 }
91
92
93 \f
94 ////////////////////////////////////////////////////////////////////////
95 // Global functions.
96 ////////////////////////////////////////////////////////////////////////
97
98 bool
99 fgPanelVisible ()
100 {
101      if (globals->get_current_panel() == 0)
102         return false;
103      if (globals->get_current_panel()->getVisibility() == 0)
104         return false;
105      if (globals->get_viewmgr()->get_current() != 0)
106         return false;
107      if (globals->get_current_view()->getHeadingOffset_deg() * SGD_DEGREES_TO_RADIANS != 0)
108         return false;
109      return true;
110 }
111
112
113 \f
114 ////////////////////////////////////////////////////////////////////////
115 // Implementation of FGTextureManager.
116 ////////////////////////////////////////////////////////////////////////
117
118 map<string,osg::ref_ptr<osg::Texture2D> > FGTextureManager::_textureMap;
119
120 osg::Texture2D*
121 FGTextureManager::createTexture (const string &relativePath)
122 {
123   osg::Texture2D* texture = _textureMap[relativePath].get();
124   if (texture == 0) {
125     SG_LOG( SG_COCKPIT, SG_DEBUG,
126             "Texture " << relativePath << " does not yet exist" );
127     SGPath tpath(globals->get_fg_root());
128     tpath.append(relativePath);
129
130     texture = SGLoadTexture2D(tpath);
131
132     _textureMap[relativePath] = texture;
133     if (!_textureMap[relativePath].valid()) 
134       SG_LOG( SG_COCKPIT, SG_ALERT, "Texture *still* doesn't exist" );
135     SG_LOG( SG_COCKPIT, SG_DEBUG, "Created texture " << relativePath );
136   }
137
138   return texture;
139 }
140
141
142
143 \f
144 ////////////////////////////////////////////////////////////////////////
145 // Implementation of FGCropped Texture.
146 ////////////////////////////////////////////////////////////////////////
147
148
149 FGCroppedTexture::FGCroppedTexture ()
150   : _path(""), _texture(0),
151     _minX(0.0), _minY(0.0), _maxX(1.0), _maxY(1.0)
152 {
153 }
154
155
156 FGCroppedTexture::FGCroppedTexture (const string &path,
157                                     float minX, float minY,
158                                     float maxX, float maxY)
159   : _path(path), _texture(0),
160     _minX(minX), _minY(minY), _maxX(maxX), _maxY(maxY)
161 {
162 }
163
164
165 FGCroppedTexture::~FGCroppedTexture ()
166 {
167 }
168
169
170 osg::StateSet*
171 FGCroppedTexture::getTexture ()
172 {
173   if (_texture == 0) {
174     _texture = new osg::StateSet;
175     _texture->setTextureAttribute(0, FGTextureManager::createTexture(_path));
176     _texture->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
177     _texture->setTextureAttribute(0, new osg::TexEnv(osg::TexEnv::MODULATE));
178   }
179   return _texture.get();
180 }
181
182
183 \f
184 ////////////////////////////////////////////////////////////////////////
185 // Implementation of FGPanel.
186 ////////////////////////////////////////////////////////////////////////
187
188 static fntRenderer text_renderer;
189 static sgVec4 panel_color;
190 static sgVec4 emissive_panel_color = {1,1,1,1};
191
192 /**
193  * Constructor.
194  */
195 FGPanel::FGPanel ()
196   : _mouseDown(false),
197     _mouseInstrument(0),
198     _width(WIN_W), _height(int(WIN_H * 0.5768 + 1)),
199     _view_height(int(WIN_H * 0.4232)),
200     _visibility(fgGetNode("/sim/panel/visibility", true)),
201     _x_offset(fgGetNode("/sim/panel/x-offset", true)),
202     _y_offset(fgGetNode("/sim/panel/y-offset", true)),
203     _jitter(fgGetNode("/sim/panel/jitter", true)),
204     _flipx(fgGetNode("/sim/panel/flip-x", true)),
205     _xsize_node(fgGetNode("/sim/startup/xsize", true)),
206     _ysize_node(fgGetNode("/sim/startup/ysize", true)),
207     _enable_depth_test(false)
208 {
209 }
210
211
212 /**
213  * Destructor.
214  */
215 FGPanel::~FGPanel ()
216 {
217   for (instrument_list_type::iterator it = _instruments.begin();
218        it != _instruments.end();
219        it++) {
220     delete *it;
221     *it = 0;
222   }
223 }
224
225
226 /**
227  * Add an instrument to the panel.
228  */
229 void
230 FGPanel::addInstrument (FGPanelInstrument * instrument)
231 {
232   _instruments.push_back(instrument);
233 }
234
235
236 /**
237  * Initialize the panel.
238  */
239 void
240 FGPanel::init ()
241 {
242 }
243
244
245 /**
246  * Bind panel properties.
247  */
248 void
249 FGPanel::bind ()
250 {
251   fgSetArchivable("/sim/panel/visibility");
252   fgSetArchivable("/sim/panel/x-offset");
253   fgSetArchivable("/sim/panel/y-offset");
254   fgSetArchivable("/sim/panel/jitter");
255 }
256
257
258 /**
259  * Unbind panel properties.
260  */
261 void
262 FGPanel::unbind ()
263 {
264 }
265
266
267 void
268 FGPanel::update (double dt)
269 {
270   std::cout << "OSGFIXME" << std::endl;
271 }
272
273 void
274 FGPanel::update (osg::State& state, GLfloat winx, GLfloat winw, GLfloat winy, GLfloat winh)
275 {
276                                // Calculate accelerations
277                                // and jiggle the panel accordingly
278                                // The factors and bounds are just
279                                // initial guesses; using sqrt smooths
280                                // out the spikes.
281   double x_offset = _x_offset->getIntValue();
282   double y_offset = _y_offset->getIntValue();
283
284
285   glMatrixMode(GL_PROJECTION);
286   glPushMatrix();
287   glLoadIdentity();
288   if ( _flipx->getBoolValue() ) {
289     gluOrtho2D(winx + winw, winx, winy + winh, winy); /* up side down */
290   } else {
291     gluOrtho2D(winx, winx + winw, winy, winy + winh); /* right side up */
292   }
293   
294   glMatrixMode(GL_MODELVIEW);
295   glPushMatrix();
296   glLoadIdentity();
297   
298   glTranslated(x_offset, y_offset, 0);
299   
300   draw(state);
301
302   glMatrixMode(GL_PROJECTION);
303   glPopMatrix();
304   glMatrixMode(GL_MODELVIEW);
305   glPopMatrix();
306 }
307
308 /**
309  * Update the panel.
310  */
311 void
312 FGPanel::update (osg::State& state)
313 {
314                                 // Do nothing if the panel isn't visible.
315     if ( !fgPanelVisible() ) {
316         return;
317     }
318
319     updateMouseDelay();
320
321                                 // Now, draw the panel
322     float aspect_adjust = get_aspect_adjust(_xsize_node->getIntValue(),
323                                             _ysize_node->getIntValue());
324     if (aspect_adjust <1.0)
325         update(state, WIN_X, int(WIN_W * aspect_adjust), WIN_Y, WIN_H);
326     else
327         update(state, WIN_X, WIN_W, WIN_Y, int(WIN_H / aspect_adjust));
328 }
329
330 /**
331  * Handle repeatable mouse events.  Called from update() and from
332  * fgUpdate3DPanels().  This functionality needs to move into the
333  * input subsystem.  Counting a tick every two frames is clumsy...
334  */
335 void FGPanel::updateMouseDelay()
336 {
337     if (_mouseDown) {
338         _mouseDelay--;
339         if (_mouseDelay < 0) {
340             _mouseInstrument->doMouseAction(_mouseButton, 0, _mouseX, _mouseY);
341             _mouseDelay = 2;
342         }
343     }
344 }
345
346
347 void
348 FGPanel::draw(osg::State& state)
349 {
350   // In 3D mode, it's possible that we are being drawn exactly on top
351   // of an existing polygon.  Use an offset to prevent z-fighting.  In
352   // 2D mode, this is a no-op.
353   static osg::ref_ptr<osg::StateSet> panelStateSet;
354   if (!panelStateSet.valid()) {
355     panelStateSet = new osg::StateSet;
356     panelStateSet->setAttributeAndModes(new osg::PolygonOffset(-1, -POFF_UNITS));
357     panelStateSet->setTextureAttribute(0, new osg::TexEnv);
358
359     // Draw the background
360     panelStateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
361     panelStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
362     panelStateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
363     panelStateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
364     osg::Material* material = new osg::Material;
365     material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
366     material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(1, 1, 1, 1));
367     material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4(1, 1, 1, 1));
368     material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4(0, 0, 0, 1));
369     material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4(0, 0, 0, 1));
370     panelStateSet->setAttribute(material);
371     panelStateSet->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
372     panelStateSet->setAttributeAndModes(new osg::CullFace(osg::CullFace::BACK));
373     if ( _enable_depth_test )
374       panelStateSet->setAttributeAndModes(new osg::Depth(osg::Depth::ALWAYS));
375     else
376       panelStateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
377   }
378   state.pushStateSet(panelStateSet.get());
379   state.apply();
380
381   FGLight *l = (FGLight *)(globals->get_subsystem("lighting"));
382   sgCopyVec4( panel_color, l->scene_diffuse().data());
383   if ( fgGetDouble("/systems/electrical/outputs/instrument-lights") > 1.0 ) {
384       if ( panel_color[0] < 0.7 ) panel_color[0] = 0.7;
385       if ( panel_color[1] < 0.2 ) panel_color[1] = 0.2;
386       if ( panel_color[2] < 0.2 ) panel_color[2] = 0.2;
387   }
388   glColor4fv( panel_color );
389   if (_bg != 0) {
390     state.pushStateSet(_bg.get());
391     state.apply();
392     glBegin(GL_POLYGON);
393     glTexCoord2f(0.0, 0.0); glVertex2f(WIN_X, WIN_Y);
394     glTexCoord2f(1.0, 0.0); glVertex2f(WIN_X + _width, WIN_Y);
395     glTexCoord2f(1.0, 1.0); glVertex2f(WIN_X + _width, WIN_Y + _height);
396     glTexCoord2f(0.0, 1.0); glVertex2f(WIN_X, WIN_Y + _height);
397     glEnd();
398     state.popStateSet();
399     state.apply();
400   } else {
401     for (int i = 0; i < 4; i ++) {
402       // top row of textures...(1,3,5,7)
403       state.pushStateSet(_mbg[i*2].get());
404       state.apply();
405       glBegin(GL_POLYGON);
406       glTexCoord2f(0.0, 0.0); glVertex2f(WIN_X + (_width/4) * i, WIN_Y + (_height/2));
407       glTexCoord2f(1.0, 0.0); glVertex2f(WIN_X + (_width/4) * (i+1), WIN_Y + (_height/2));
408       glTexCoord2f(1.0, 1.0); glVertex2f(WIN_X + (_width/4) * (i+1), WIN_Y + _height);
409       glTexCoord2f(0.0, 1.0); glVertex2f(WIN_X + (_width/4) * i, WIN_Y + _height);
410       glEnd();
411       state.popStateSet();
412       state.apply();
413       // bottom row of textures...(2,4,6,8)
414       state.pushStateSet(_mbg[i*2+1].get());
415       state.apply();
416       glBegin(GL_POLYGON);
417       glTexCoord2f(0.0, 0.0); glVertex2f(WIN_X + (_width/4) * i, WIN_Y);
418       glTexCoord2f(1.0, 0.0); glVertex2f(WIN_X + (_width/4) * (i+1), WIN_Y);
419       glTexCoord2f(1.0, 1.0); glVertex2f(WIN_X + (_width/4) * (i+1), WIN_Y + (_height/2));
420       glTexCoord2f(0.0, 1.0); glVertex2f(WIN_X + (_width/4) * i, WIN_Y + (_height/2));
421       glEnd();
422       state.popStateSet();
423       state.apply();
424     }
425   }
426
427   // Draw the instruments.
428   // Syd Adams: added instrument clipping
429   instrument_list_type::const_iterator current = _instruments.begin();
430   instrument_list_type::const_iterator end = _instruments.end();
431
432   GLdouble blx[4]={1.0,0.0,0.0,0.0};
433   GLdouble bly[4]={0.0,1.0,0.0,0.0};
434   GLdouble urx[4]={-1.0,0.0,0.0,0.0};
435   GLdouble ury[4]={0.0,-1.0,0.0,0.0};
436
437   for ( ; current != end; current++) {
438     FGPanelInstrument * instr = *current;
439     glPushMatrix();
440     glTranslated(instr->getXPos(), instr->getYPos(), 0);
441
442     int ix= instr->getWidth();
443     int iy= instr->getHeight();
444     glPushMatrix();
445     glTranslated(-ix/2,-iy/2,0);
446     glClipPlane(GL_CLIP_PLANE0,blx);
447     glClipPlane(GL_CLIP_PLANE1,bly);
448     glEnable(GL_CLIP_PLANE0);
449     glEnable(GL_CLIP_PLANE1);
450
451     glTranslated(ix,iy,0);
452     glClipPlane(GL_CLIP_PLANE2,urx);
453     glClipPlane(GL_CLIP_PLANE3,ury);
454     glEnable(GL_CLIP_PLANE2);
455     glEnable(GL_CLIP_PLANE3);
456     glPopMatrix();
457     instr->draw(state);
458
459     glPopMatrix();
460   }
461
462   glDisable(GL_CLIP_PLANE0);
463   glDisable(GL_CLIP_PLANE1);
464   glDisable(GL_CLIP_PLANE2);
465   glDisable(GL_CLIP_PLANE3);
466
467   state.popStateSet();
468   state.apply();
469
470   // Draw yellow "hotspots" if directed to.  This is a panel authoring
471   // feature; not intended to be high performance or to look good.
472   if ( fgGetBool("/sim/panel-hotspots") ) {
473     static osg::ref_ptr<osg::StateSet> hotspotStateSet = new osg::StateSet;
474     hotspotStateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF);
475     state.pushStateSet(hotspotStateSet.get());
476     state.apply();
477   
478     glColor3f(1, 1, 0);
479     
480     for ( unsigned int i = 0; i < _instruments.size(); i++ )
481       _instruments[i]->drawHotspots(state);
482     state.popStateSet();
483     state.apply();
484   }
485 }
486
487 /**
488  * Set the panel's visibility.
489  */
490 void
491 FGPanel::setVisibility (bool visibility)
492 {
493   _visibility->setBoolValue( visibility );
494 }
495
496
497 /**
498  * Return true if the panel is visible.
499  */
500 bool
501 FGPanel::getVisibility () const
502 {
503   return _visibility->getBoolValue();
504 }
505
506
507 /**
508  * Set the panel's background texture.
509  */
510 void
511 FGPanel::setBackground (osg::Texture2D* texture)
512 {
513   osg::StateSet* stateSet = new osg::StateSet;
514   stateSet->setTextureAttribute(0, texture);
515   stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
516   stateSet->setTextureAttribute(0, new osg::TexEnv(osg::TexEnv::MODULATE));
517   _bg = stateSet;
518 }
519
520 /**
521  * Set the panel's multiple background textures.
522  */
523 void
524 FGPanel::setMultiBackground (osg::Texture2D* texture, int idx)
525 {
526   _bg = 0;
527
528   osg::StateSet* stateSet = new osg::StateSet;
529   stateSet->setTextureAttribute(0, texture);
530   stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
531   stateSet->setTextureAttribute(0, new osg::TexEnv(osg::TexEnv::MODULATE));
532   _mbg[idx] = stateSet;
533 }
534
535 /**
536  * Set the panel's x-offset.
537  */
538 void
539 FGPanel::setXOffset (int offset)
540 {
541   if (offset <= 0 && offset >= -_width + WIN_W)
542     _x_offset->setIntValue( offset );
543 }
544
545
546 /**
547  * Set the panel's y-offset.
548  */
549 void
550 FGPanel::setYOffset (int offset)
551 {
552   if (offset <= 0 && offset >= -_height)
553     _y_offset->setIntValue( offset );
554 }
555
556 /**
557  * Handle a mouse action in panel-local (not screen) coordinates.
558  * Used by the 3D panel code in Model/panelnode.cxx, in situations
559  * where the panel doesn't control its own screen location.
560  */
561 bool
562 FGPanel::doLocalMouseAction(int button, int updown, int x, int y)
563 {
564   // Note a released button and return
565   if (updown == 1) {
566     if (_mouseInstrument != 0)
567         _mouseInstrument->doMouseAction(_mouseButton, 1, _mouseX, _mouseY);
568     _mouseDown = false;
569     _mouseInstrument = 0;
570     return false;
571   }
572
573   // Search for a matching instrument.
574   for (int i = 0; i < (int)_instruments.size(); i++) {
575     FGPanelInstrument *inst = _instruments[i];
576     int ix = inst->getXPos();
577     int iy = inst->getYPos();
578     int iw = inst->getWidth() / 2;
579     int ih = inst->getHeight() / 2;
580     if (x >= ix - iw && x < ix + iw && y >= iy - ih && y < iy + ih) {
581       _mouseDown = true;
582       _mouseDelay = 20;
583       _mouseInstrument = inst;
584       _mouseButton = button;
585       _mouseX = x - ix;
586       _mouseY = y - iy;
587       // Always do the action once.
588       return _mouseInstrument->doMouseAction(_mouseButton, 0,
589                                              _mouseX, _mouseY);
590     }
591   }
592   return false;
593 }
594
595 /**
596  * Perform a mouse action.
597  */
598 bool
599 FGPanel::doMouseAction (int button, int updown, int x, int y)
600 {
601                                 // FIXME: this same code appears in update()
602   int xsize = _xsize_node->getIntValue();
603   int ysize = _ysize_node->getIntValue();
604   float aspect_adjust = get_aspect_adjust(xsize, ysize);
605
606                                 // Scale for the real window size.
607   if (aspect_adjust < 1.0) {
608     x = int(((float)x / xsize) * WIN_W * aspect_adjust);
609     y = int(WIN_H - ((float(y) / ysize) * WIN_H));
610   } else {
611     x = int(((float)x / xsize) * WIN_W);
612     y = int((WIN_H - ((float(y) / ysize) * WIN_H)) / aspect_adjust);
613   }
614
615                                 // Adjust for offsets.
616   x -= _x_offset->getIntValue();
617   y -= _y_offset->getIntValue();
618
619   // Having fixed up the coordinates, fall through to the local
620   // coordinate handler.
621   return doLocalMouseAction(button, updown, x, y);
622
623
624 void FGPanel::setDepthTest (bool enable) {
625     _enable_depth_test = enable;
626 }
627
628
629 \f
630 ////////////////////////////////////////////////////////////////////////.
631 // Implementation of FGPanelAction.
632 ////////////////////////////////////////////////////////////////////////
633
634 FGPanelAction::FGPanelAction ()
635 {
636 }
637
638 FGPanelAction::FGPanelAction (int button, int x, int y, int w, int h,
639                               bool repeatable)
640     : _button(button), _x(x), _y(y), _w(w), _h(h), _repeatable(repeatable)
641 {
642 }
643
644 FGPanelAction::~FGPanelAction ()
645 {
646   for (unsigned int i = 0; i < 2; i++) {
647       for (unsigned int j = 0; j < _bindings[i].size(); j++)
648           delete _bindings[i][j];
649   }
650 }
651
652 void
653 FGPanelAction::addBinding (FGBinding * binding, int updown)
654 {
655   _bindings[updown].push_back(binding);
656 }
657
658 bool
659 FGPanelAction::doAction (int updown)
660 {
661   if (test()) {
662     if ((updown != _last_state) || (updown == 0 && _repeatable)) {
663         int nBindings = _bindings[updown].size();
664         for (int i = 0; i < nBindings; i++)
665             _bindings[updown][i]->fire();
666     }
667     _last_state = updown;
668     return true;
669   } else {
670     return false;
671   }
672 }
673
674
675 \f
676 ////////////////////////////////////////////////////////////////////////
677 // Implementation of FGPanelTransformation.
678 ////////////////////////////////////////////////////////////////////////
679
680 FGPanelTransformation::FGPanelTransformation ()
681   : table(0)
682 {
683 }
684
685 FGPanelTransformation::~FGPanelTransformation ()
686 {
687   delete table;
688 }
689
690
691 \f
692 ////////////////////////////////////////////////////////////////////////
693 // Implementation of FGPanelInstrument.
694 ////////////////////////////////////////////////////////////////////////
695
696
697 FGPanelInstrument::FGPanelInstrument ()
698 {
699   setPosition(0, 0);
700   setSize(0, 0);
701 }
702
703 FGPanelInstrument::FGPanelInstrument (int x, int y, int w, int h)
704 {
705   setPosition(x, y);
706   setSize(w, h);
707 }
708
709 FGPanelInstrument::~FGPanelInstrument ()
710 {
711   for (action_list_type::iterator it = _actions.begin();
712        it != _actions.end();
713        it++) {
714     delete *it;
715     *it = 0;
716   }
717 }
718
719 void
720 FGPanelInstrument::drawHotspots(osg::State& state)
721 {
722   for ( unsigned int i = 0; i < _actions.size(); i++ ) {
723     FGPanelAction* a = _actions[i];
724     float x1 = getXPos() + a->getX();
725     float x2 = x1 + a->getWidth();
726     float y1 = getYPos() + a->getY();
727     float y2 = y1 + a->getHeight();
728
729     glBegin(GL_LINE_LOOP);
730     glVertex2f(x1, y1);
731     glVertex2f(x1, y2);
732     glVertex2f(x2, y2);
733     glVertex2f(x2, y1);
734     glEnd();
735   }
736 }
737
738 void
739 FGPanelInstrument::setPosition (int x, int y)
740 {
741   _x = x;
742   _y = y;
743 }
744
745 void
746 FGPanelInstrument::setSize (int w, int h)
747 {
748   _w = w;
749   _h = h;
750 }
751
752 int
753 FGPanelInstrument::getXPos () const
754 {
755   return _x;
756 }
757
758 int
759 FGPanelInstrument::getYPos () const
760 {
761   return _y;
762 }
763
764 int
765 FGPanelInstrument::getWidth () const
766 {
767   return _w;
768 }
769
770 int
771 FGPanelInstrument::getHeight () const
772 {
773   return _h;
774 }
775
776 void
777 FGPanelInstrument::addAction (FGPanelAction * action)
778 {
779   _actions.push_back(action);
780 }
781
782                                 // Coordinates relative to centre.
783 bool
784 FGPanelInstrument::doMouseAction (int button, int updown, int x, int y)
785 {
786   if (test()) {
787     action_list_type::iterator it = _actions.begin();
788     action_list_type::iterator last = _actions.end();
789     for ( ; it != last; it++) {
790       if ((*it)->inArea(button, x, y) &&
791           (*it)->doAction(updown))
792         return true;
793     }
794   }
795   return false;
796 }
797
798
799 \f
800 ////////////////////////////////////////////////////////////////////////
801 // Implementation of FGLayeredInstrument.
802 ////////////////////////////////////////////////////////////////////////
803
804 FGLayeredInstrument::FGLayeredInstrument (int x, int y, int w, int h)
805   : FGPanelInstrument(x, y, w, h)
806 {
807 }
808
809 FGLayeredInstrument::~FGLayeredInstrument ()
810 {
811   for (layer_list::iterator it = _layers.begin(); it != _layers.end(); it++) {
812     delete *it;
813     *it = 0;
814   }
815 }
816
817 void
818 FGLayeredInstrument::draw (osg::State& state)
819 {
820   if (!test())
821     return;
822   
823   for (int i = 0; i < (int)_layers.size(); i++) {
824     glPushMatrix();
825     _layers[i]->draw(state);
826     glPopMatrix();
827   }
828 }
829
830 int
831 FGLayeredInstrument::addLayer (FGInstrumentLayer *layer)
832 {
833   int n = _layers.size();
834   if (layer->getWidth() == -1) {
835     layer->setWidth(getWidth());
836   }
837   if (layer->getHeight() == -1) {
838     layer->setHeight(getHeight());
839   }
840   _layers.push_back(layer);
841   return n;
842 }
843
844 int
845 FGLayeredInstrument::addLayer (const FGCroppedTexture &texture,
846                                int w, int h)
847 {
848   return addLayer(new FGTexturedLayer(texture, w, h));
849 }
850
851 void
852 FGLayeredInstrument::addTransformation (FGPanelTransformation * transformation)
853 {
854   int layer = _layers.size() - 1;
855   _layers[layer]->addTransformation(transformation);
856 }
857
858
859 \f
860 ////////////////////////////////////////////////////////////////////////
861 // Implementation of FGSpecialInstrument.
862 ////////////////////////////////////////////////////////////////////////
863
864 FGSpecialInstrument::FGSpecialInstrument (DCLGPS* sb)
865   : FGPanelInstrument()
866 {
867   complex = sb;
868 }
869
870 FGSpecialInstrument::~FGSpecialInstrument ()
871 {
872 }
873
874 void
875 FGSpecialInstrument::draw (osg::State& state)
876 {
877   complex->draw();
878 }
879
880
881 \f
882 ////////////////////////////////////////////////////////////////////////
883 // Implementation of FGInstrumentLayer.
884 ////////////////////////////////////////////////////////////////////////
885
886 FGInstrumentLayer::FGInstrumentLayer (int w, int h)
887   : _w(w),
888     _h(h)
889 {
890 }
891
892 FGInstrumentLayer::~FGInstrumentLayer ()
893 {
894   for (transformation_list::iterator it = _transformations.begin();
895        it != _transformations.end();
896        it++) {
897     delete *it;
898     *it = 0;
899   }
900 }
901
902 void
903 FGInstrumentLayer::transform () const
904 {
905   transformation_list::const_iterator it = _transformations.begin();
906   transformation_list::const_iterator last = _transformations.end();
907   while (it != last) {
908     FGPanelTransformation *t = *it;
909     if (t->test()) {
910       float val = (t->node == 0 ? 0.0 : t->node->getFloatValue());
911
912       if (t->has_mod)
913           val = fmod(val, t->mod);
914       if (val < t->min) {
915         val = t->min;
916       } else if (val > t->max) {
917         val = t->max;
918       }
919
920       if (t->table==0) {
921         val = val * t->factor + t->offset;
922       } else {
923         val = t->table->interpolate(val) * t->factor + t->offset;
924       }
925       
926       switch (t->type) {
927       case FGPanelTransformation::XSHIFT:
928         glTranslatef(val, 0.0, 0.0);
929         break;
930       case FGPanelTransformation::YSHIFT:
931         glTranslatef(0.0, val, 0.0);
932         break;
933       case FGPanelTransformation::ROTATION:
934         glRotatef(-val, 0.0, 0.0, 1.0);
935         break;
936       }
937     }
938     it++;
939   }
940 }
941
942 void
943 FGInstrumentLayer::addTransformation (FGPanelTransformation * transformation)
944 {
945   _transformations.push_back(transformation);
946 }
947
948
949 \f
950 ////////////////////////////////////////////////////////////////////////
951 // Implementation of FGGroupLayer.
952 ////////////////////////////////////////////////////////////////////////
953
954 FGGroupLayer::FGGroupLayer ()
955 {
956 }
957
958 FGGroupLayer::~FGGroupLayer ()
959 {
960   for (unsigned int i = 0; i < _layers.size(); i++)
961     delete _layers[i];
962 }
963
964 void
965 FGGroupLayer::draw (osg::State& state)
966 {
967   if (test()) {
968     transform();
969     int nLayers = _layers.size();
970     for (int i = 0; i < nLayers; i++)
971       _layers[i]->draw(state);
972   }
973 }
974
975 void
976 FGGroupLayer::addLayer (FGInstrumentLayer * layer)
977 {
978   _layers.push_back(layer);
979 }
980
981
982 \f
983 ////////////////////////////////////////////////////////////////////////
984 // Implementation of FGTexturedLayer.
985 ////////////////////////////////////////////////////////////////////////
986
987
988 FGTexturedLayer::FGTexturedLayer (const FGCroppedTexture &texture, int w, int h)
989   : FGInstrumentLayer(w, h),
990     _emissive(false)
991 {
992   setTexture(texture);
993 }
994
995
996 FGTexturedLayer::~FGTexturedLayer ()
997 {
998 }
999
1000
1001 void
1002 FGTexturedLayer::draw (osg::State& state)
1003 {
1004   if (test()) {
1005     int w2 = _w / 2;
1006     int h2 = _h / 2;
1007     
1008     transform();
1009     state.pushStateSet(_texture.getTexture());
1010     state.apply();
1011     glBegin(GL_POLYGON);
1012
1013     if (_emissive) {
1014       glColor4fv( emissive_panel_color );
1015     } else {
1016                                 // From Curt: turn on the panel
1017                                 // lights after sundown.
1018       glColor4fv( panel_color );
1019     }
1020
1021     glTexCoord2f(_texture.getMinX(), _texture.getMinY()); glVertex2f(-w2, -h2);
1022     glTexCoord2f(_texture.getMaxX(), _texture.getMinY()); glVertex2f(w2, -h2);
1023     glTexCoord2f(_texture.getMaxX(), _texture.getMaxY()); glVertex2f(w2, h2);
1024     glTexCoord2f(_texture.getMinX(), _texture.getMaxY()); glVertex2f(-w2, h2);
1025     glEnd();
1026     state.popStateSet();
1027     state.apply();
1028   }
1029 }
1030
1031
1032 \f
1033 ////////////////////////////////////////////////////////////////////////
1034 // Implementation of FGTextLayer.
1035 ////////////////////////////////////////////////////////////////////////
1036
1037 FGTextLayer::FGTextLayer (int w, int h)
1038   : FGInstrumentLayer(w, h), _pointSize(14.0), _font_name("Helvetica.txf")
1039 {
1040   _then.stamp();
1041   _color[0] = _color[1] = _color[2] = 0.0;
1042   _color[3] = 1.0;
1043 }
1044
1045 FGTextLayer::~FGTextLayer ()
1046 {
1047   chunk_list::iterator it = _chunks.begin();
1048   chunk_list::iterator last = _chunks.end();
1049   for ( ; it != last; it++) {
1050     delete *it;
1051   }
1052 }
1053
1054 void
1055 FGTextLayer::draw (osg::State& state)
1056 {
1057   if (test()) {
1058     glColor4fv(_color);
1059     transform();
1060
1061     FGFontCache *fc = globals->get_fontcache();
1062     text_renderer.setFont(fc->getTexFont(_font_name.c_str()));
1063
1064     text_renderer.setPointSize(_pointSize);
1065     text_renderer.begin();
1066     text_renderer.start3f(0, 0, 0);
1067
1068     _now.stamp();
1069     long diff = _now - _then;
1070
1071     if (diff > 100000 || diff < 0 ) {
1072       // ( diff < 0 ) is a sanity check and indicates our time stamp
1073       // difference math probably overflowed.  We can handle a max
1074       // difference of 35.8 minutes since the returned value is in
1075       // usec.  So if the panel is left off longer than that we can
1076       // over flow the math with it is turned back on.  This (diff <
1077       // 0) catches that situation, get's us out of trouble, and
1078       // back on track.
1079       recalc_value();
1080       _then = _now;
1081     }
1082
1083     // Something is goofy.  The code in this file renders only CCW
1084     // polygons, and I have verified that the font code in plib
1085     // renders only CCW trianbles.  Yet they come out backwards.
1086     // Something around here or in plib is either changing the winding
1087     // order or (more likely) pushing a left-handed matrix onto the
1088     // stack.  But I can't find it; get out the chainsaw...
1089     glFrontFace(GL_CW);
1090     text_renderer.puts((char *)(_value.c_str()));
1091     glFrontFace(GL_CCW);
1092
1093     text_renderer.end();
1094     glColor4f(1.0, 1.0, 1.0, 1.0);      // FIXME
1095   }
1096 }
1097
1098 void
1099 FGTextLayer::addChunk (FGTextLayer::Chunk * chunk)
1100 {
1101   _chunks.push_back(chunk);
1102 }
1103
1104 void
1105 FGTextLayer::setColor (float r, float g, float b)
1106 {
1107   _color[0] = r;
1108   _color[1] = g;
1109   _color[2] = b;
1110   _color[3] = 1.0;
1111 }
1112
1113 void
1114 FGTextLayer::setPointSize (float size)
1115 {
1116   _pointSize = size;
1117 }
1118
1119 void
1120 FGTextLayer::setFontName(const string &name)
1121 {
1122   _font_name = name + ".txf";
1123 }
1124
1125
1126 void
1127 FGTextLayer::setFont(fntFont * font)
1128 {
1129   text_renderer.setFont(font);
1130 }
1131
1132
1133 void
1134 FGTextLayer::recalc_value () const
1135 {
1136   _value = "";
1137   chunk_list::const_iterator it = _chunks.begin();
1138   chunk_list::const_iterator last = _chunks.end();
1139   for ( ; it != last; it++) {
1140     _value += (*it)->getValue();
1141   }
1142 }
1143
1144
1145 \f
1146 ////////////////////////////////////////////////////////////////////////
1147 // Implementation of FGTextLayer::Chunk.
1148 ////////////////////////////////////////////////////////////////////////
1149
1150 FGTextLayer::Chunk::Chunk (const string &text, const string &fmt)
1151   : _type(FGTextLayer::TEXT), _fmt(fmt)
1152 {
1153   _text = text;
1154   if (_fmt.empty()) 
1155     _fmt = "%s";
1156 }
1157
1158 FGTextLayer::Chunk::Chunk (ChunkType type, const SGPropertyNode * node,
1159                            const string &fmt, float mult, float offs,
1160                            bool truncation)
1161   : _type(type), _fmt(fmt), _mult(mult), _offs(offs), _trunc(truncation)
1162 {
1163   if (_fmt.empty()) {
1164     if (type == TEXT_VALUE)
1165       _fmt = "%s";
1166     else
1167       _fmt = "%.2f";
1168   }
1169   _node = node;
1170 }
1171
1172 const char *
1173 FGTextLayer::Chunk::getValue () const
1174 {
1175   if (test()) {
1176     _buf[0] = '\0';
1177     switch (_type) {
1178     case TEXT:
1179       sprintf(_buf, _fmt.c_str(), _text.c_str());
1180       return _buf;
1181     case TEXT_VALUE:
1182       sprintf(_buf, _fmt.c_str(), _node->getStringValue());
1183       break;
1184     case DOUBLE_VALUE:
1185       double d = _offs + _node->getFloatValue() * _mult;
1186       if (_trunc)  d = (d < 0) ? -floor(-d) : floor(d);
1187       sprintf(_buf, _fmt.c_str(), d);
1188       break;
1189     }
1190     return _buf;
1191   } else {
1192     return "";
1193   }
1194 }
1195
1196
1197 \f
1198 ////////////////////////////////////////////////////////////////////////
1199 // Implementation of FGSwitchLayer.
1200 ////////////////////////////////////////////////////////////////////////
1201
1202 FGSwitchLayer::FGSwitchLayer ()
1203   : FGGroupLayer()
1204 {
1205 }
1206
1207 void
1208 FGSwitchLayer::draw (osg::State& state)
1209 {
1210   if (test()) {
1211     transform();
1212     int nLayers = _layers.size();
1213     for (int i = 0; i < nLayers; i++) {
1214       if (_layers[i]->test()) {
1215           _layers[i]->draw(state);
1216           return;
1217       }
1218     }
1219   }
1220 }
1221
1222 \f
1223 // end of panel.cxx