]> git.mxchange.org Git - flightgear.git/blob - utils/fgpanel/panel.cxx
Merge branch 'next' of http://git.gitorious.org/fg/flightgear into next
[flightgear.git] / utils / fgpanel / 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: panel.cxx,v 1.44 2006/09/05 20:28:48 curt Exp $
20
21 #ifdef HAVE_CONFIG_H
22 #  include <config.h>
23 #endif
24
25 #ifdef HAVE_WINDOWS_H          
26 #  include <windows.h>
27 #endif
28
29 #include <stdio.h>      // sprintf
30 #include <string.h>
31
32 #include <simgear/compiler.h>
33
34 #include <GL/glut.h>
35
36 #include <plib/fnt.h>
37
38 #include <simgear/debug/logstream.hxx>
39 #include <simgear/misc/sg_path.hxx>
40
41 #include "panel.hxx"
42 #include "ApplicationProperties.hxx"
43 ////////////////////////////////////////////////////////////////////////
44 // Local functions.
45 ////////////////////////////////////////////////////////////////////////
46
47 class FGDummyTextureLoader : public FGTextureLoaderInterface {
48 public:
49   virtual GLuint loadTexture( const string & filename );
50 };
51
52 GLuint FGDummyTextureLoader::loadTexture( const string & filename )
53 {
54   GLuint _texture = 0;
55   glGenTextures( 1, &_texture );
56   glBindTexture( GL_TEXTURE_2D, _texture );
57
58 //  glTexEnvi       ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ) ;
59 //  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ) ;
60 //  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ) ;
61 //  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ) ;
62 //  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ) ;
63
64   GLubyte image[ 2 * 2 * 3 ] ;
65
66   /* Red and white chequerboard */
67   image [ 0 ] = 255 ; image [ 1 ] =  0  ; image [ 2 ] =  0  ;
68   image [ 3 ] = 255 ; image [ 4 ] = 255 ; image [ 5 ] = 255 ;
69   image [ 6 ] = 255 ; image [ 7 ] = 255 ; image [ 8 ] = 255 ;
70   image [ 9 ] = 255 ; image [ 10] =  0  ; image [ 11] =  0  ;
71
72   glTexImage2D(GL_TEXTURE_2D,0, GL_RGB, 2, 2, 0,
73         GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*) image);
74
75   return _texture;
76 }
77
78 ////////////////////////////////////////////////////////////////////////
79 // Implementation of FGCropped Texture.
80 ////////////////////////////////////////////////////////////////////////
81
82 GLuint FGCroppedTexture::current_bound_texture = 0;
83 map<string,GLuint> FGCroppedTexture::cache;
84 map<string,FGTextureLoaderInterface*> FGCroppedTexture::textureLoader;
85 static FGDummyTextureLoader dummyTextureLoader;
86
87 FGCroppedTexture::FGCroppedTexture (const string &path,
88                                     float minX, float minY,
89                                     float maxX, float maxY)
90   : _path(path),
91     _minX(minX), _minY(minY), _maxX(maxX), _maxY(maxY), _texture(0)
92 {
93 }
94
95 FGCroppedTexture::~FGCroppedTexture ()
96 {
97 }
98
99 void FGCroppedTexture::bind( bool doGLBind )
100 {
101   if( _texture == 0 ) {
102     SG_LOG( SG_COCKPIT, SG_DEBUG, "First bind of texture " << _path );
103     if( cache.count(_path) > 0 ) {
104       _texture = cache[_path];
105       SG_LOG( SG_COCKPIT, SG_DEBUG, "Using texture " << _path << " from cache (#" << _texture << ")" );
106     } else {
107       SGPath tpath = ApplicationProperties::GetRootPath(_path.c_str());
108       string extension = tpath.extension();
109       FGTextureLoaderInterface * loader = &dummyTextureLoader;
110       if( textureLoader.count( extension ) == 0 ) {
111         SG_LOG( SG_COCKPIT, SG_ALERT, "Can't handle textures of type " << extension );
112       } else {
113         loader = textureLoader[extension];
114       }
115
116       _texture = loader->loadTexture( tpath.c_str() );
117       SG_LOG( SG_COCKPIT, SG_DEBUG, "Texture " << tpath.c_str() << " loaded from file as #" << _texture );
118       
119       cache[_path] = _texture;
120     }
121   }
122
123   if( !doGLBind || current_bound_texture == _texture )
124     return;
125
126   glBindTexture( GL_TEXTURE_2D, _texture );
127   current_bound_texture = _texture;
128 }
129
130 \f
131 ////////////////////////////////////////////////////////////////////////
132 // Implementation of FGPanel.
133 ////////////////////////////////////////////////////////////////////////
134
135 /**
136  * Constructor.
137  */
138 FGPanel::FGPanel ( SGPropertyNode_ptr root)
139   : _root(root),
140     _flipx(root->getNode("/sim/panel/flip-x", true)),
141     _rotate(root->getNode("/sim/panel/rotate-deg", true)),
142     _bg_width(1.0), _bg_height(1.0),
143     initDisplayList(0)
144 {
145 }
146
147
148 /**
149  * Destructor.
150  */
151 FGPanel::~FGPanel ()
152 {
153   for (instrument_list_type::iterator it = _instruments.begin();
154        it != _instruments.end();
155        it++) {
156     delete *it;
157     *it = 0;
158   }
159 }
160
161
162 /**
163  * Add an instrument to the panel.
164  */
165 void
166 FGPanel::addInstrument (FGPanelInstrument * instrument)
167 {
168   _instruments.push_back(instrument);
169 }
170
171
172 /**
173  * Initialize the panel.
174  */
175 void
176 FGPanel::init ()
177 {
178 }
179
180
181 /**
182  * Bind panel properties.
183  */
184 void
185 FGPanel::bind ()
186 {
187 }
188
189
190 /**
191  * Unbind panel properties.
192  */
193 void
194 FGPanel::unbind ()
195 {
196 }
197
198 GLuint FGPanel::getInitDisplayList()
199 {
200   if( initDisplayList != 0 ) return initDisplayList;
201   glMatrixMode(GL_PROJECTION);
202   glLoadIdentity();
203   if ( _flipx->getBoolValue() ) {
204     gluOrtho2D( _width, 0, _height, 0 ); /* up side down */
205   } else {
206     gluOrtho2D( 0, _width, 0, _height ); /* right side up */
207   }
208
209   glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
210   
211   glMatrixMode(GL_MODELVIEW);
212   glLoadIdentity();
213
214   glClear( GL_COLOR_BUFFER_BIT);
215
216   // save some state
217   glPushAttrib( GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT | GL_LIGHTING_BIT
218                 | GL_TEXTURE_BIT | GL_PIXEL_MODE_BIT | GL_CULL_FACE 
219                 | GL_DEPTH_BUFFER_BIT );
220
221   // Draw the background
222   glEnable(GL_TEXTURE_2D);
223
224   glDisable(GL_LIGHTING);
225   glEnable(GL_BLEND);
226   glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
227   glEnable(GL_ALPHA_TEST);
228   glEnable(GL_COLOR_MATERIAL);
229   glEnable(GL_CULL_FACE);
230   glCullFace(GL_BACK);
231   glDisable(GL_DEPTH_TEST);
232
233   if (_bg != NULL) {
234     _bg->bind();
235 //    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
236     glBegin(GL_QUADS);
237     glTexCoord2f(0.0, 0.0); glVertex2f(0, 0);
238     glTexCoord2f(_bg_width, 0.0); glVertex2f(_width, 0);
239     glTexCoord2f(_bg_width, _bg_height); glVertex2f(_width, _height);
240     glTexCoord2f(0.0, _bg_height); glVertex2f(0, _height);
241     glEnd();
242   } else if( _mbg[0] != NULL ) {
243     for (int i = 0; i < 4; i ++) {
244       // top row of textures...(1,3,5,7)
245       _mbg[i*2]->bind();
246 //      glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
247       glBegin(GL_QUADS);
248       glTexCoord2f(0.0, 0.0); glVertex2f(i*_width/4,     _height/2);
249       glTexCoord2f(1.0, 0.0); glVertex2f((i+1)*_width/4, _height/2);
250       glTexCoord2f(1.0, 1.0); glVertex2f((i+1)*_width/4, _height);
251       glTexCoord2f(0.0, 1.0); glVertex2f(i*_width/4,     _height);
252       glEnd();
253       // bottom row of textures...(2,4,6,8)
254       _mbg[i*2+1]->bind();
255 //      glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
256       glBegin(GL_QUADS);
257       glTexCoord2f(0.0, 0.0); glVertex2f( i*_width/4,     0);
258       glTexCoord2f(1.0, 0.0); glVertex2f( (i+1)*_width/4, 0);
259       glTexCoord2f(1.0, 1.0); glVertex2f( (i+1)*_width/4, _height/2);
260       glTexCoord2f(0.0, 1.0); glVertex2f( i*_width/4,     _height/2);
261       glEnd();
262     }
263   } else {
264     float c[4];
265     glGetFloatv( GL_CURRENT_COLOR, c );
266     glColor4f( 0.0, 0.0, 0.0, 1.0 );
267     glBegin(GL_QUADS);
268     glVertex2f(0, 0);
269     glVertex2f(_width, 0);
270     glVertex2f(_width, _height);
271     glVertex2f(0, _height);
272     glEnd();
273     glColor4fv( c );
274   }
275
276
277   return initDisplayList;  
278 }
279
280 void
281 FGPanel::update (double dt)
282 {
283   glCallList(getInitDisplayList());
284
285   // Draw the instruments.
286   // Syd Adams: added instrument clipping
287   instrument_list_type::const_iterator current = _instruments.begin();
288   instrument_list_type::const_iterator end = _instruments.end();
289
290   GLdouble blx[4]={1.0,0.0,0.0,0.0};
291   GLdouble bly[4]={0.0,1.0,0.0,0.0};
292   GLdouble urx[4]={-1.0,0.0,0.0,0.0};
293   GLdouble ury[4]={0.0,-1.0,0.0,0.0};
294
295   for ( ; current != end; current++) {
296     FGPanelInstrument * instr = *current;
297     glPushMatrix();
298     glTranslated(instr->getXPos(), instr->getYPos(), 0);
299
300     int ix= instr->getWidth();
301     int iy= instr->getHeight();
302     glPushMatrix();
303     glTranslated(-ix/2,-iy/2,0);
304     glClipPlane(GL_CLIP_PLANE0,blx);
305     glClipPlane(GL_CLIP_PLANE1,bly);
306     glEnable(GL_CLIP_PLANE0);
307     glEnable(GL_CLIP_PLANE1);
308
309     glTranslated(ix,iy,0);
310     glClipPlane(GL_CLIP_PLANE2,urx);
311     glClipPlane(GL_CLIP_PLANE3,ury);
312     glEnable(GL_CLIP_PLANE2);
313     glEnable(GL_CLIP_PLANE3);
314     glPopMatrix();
315     instr->draw();
316
317     glPopMatrix();
318   }
319
320   glDisable(GL_CLIP_PLANE0);
321   glDisable(GL_CLIP_PLANE1);
322   glDisable(GL_CLIP_PLANE2);
323   glDisable(GL_CLIP_PLANE3);
324
325   // restore some original state
326   glPopAttrib();
327 }
328
329 #if 0
330 /**
331  * Update the panel.
332  */
333 void
334 FGPanel::update (double dt)
335 {
336   glMatrixMode(GL_PROJECTION);
337   glLoadIdentity();
338   if ( _flipx->getBoolValue() ) {
339     gluOrtho2D( _width, 0, _height, 0 ); /* up side down */
340   } else {
341     gluOrtho2D( 0, _width, 0, _height ); /* right side up */
342   }
343
344   glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
345   
346   glMatrixMode(GL_MODELVIEW);
347   glLoadIdentity();
348   
349   draw();
350 }
351
352 void FGPanel::draw()
353 {
354   glClear( GL_COLOR_BUFFER_BIT);
355
356   // save some state
357   glPushAttrib( GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT | GL_LIGHTING_BIT
358                 | GL_TEXTURE_BIT | GL_PIXEL_MODE_BIT | GL_CULL_FACE 
359                 | GL_DEPTH_BUFFER_BIT );
360
361   // Draw the background
362   glEnable(GL_TEXTURE_2D);
363
364   glDisable(GL_LIGHTING);
365   glEnable(GL_BLEND);
366   glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
367   glEnable(GL_ALPHA_TEST);
368   glEnable(GL_COLOR_MATERIAL);
369   glEnable(GL_CULL_FACE);
370   glCullFace(GL_BACK);
371   glDisable(GL_DEPTH_TEST);
372
373   if (_bg != NULL) {
374     _bg->bind();
375 //    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
376     glBegin(GL_QUADS);
377     glTexCoord2f(0.0, 0.0); glVertex2f(0, 0);
378     glTexCoord2f(_bg_width, 0.0); glVertex2f(_width, 0);
379     glTexCoord2f(_bg_width, _bg_height); glVertex2f(_width, _height);
380     glTexCoord2f(0.0, _bg_height); glVertex2f(0, _height);
381     glEnd();
382   } else if( _mbg[0] != NULL ) {
383     for (int i = 0; i < 4; i ++) {
384       // top row of textures...(1,3,5,7)
385       _mbg[i*2]->bind();
386 //      glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
387       glBegin(GL_QUADS);
388       glTexCoord2f(0.0, 0.0); glVertex2f(i*_width/4,     _height/2);
389       glTexCoord2f(1.0, 0.0); glVertex2f((i+1)*_width/4, _height/2);
390       glTexCoord2f(1.0, 1.0); glVertex2f((i+1)*_width/4, _height);
391       glTexCoord2f(0.0, 1.0); glVertex2f(i*_width/4,     _height);
392       glEnd();
393       // bottom row of textures...(2,4,6,8)
394       _mbg[i*2+1]->bind();
395 //      glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
396       glBegin(GL_QUADS);
397       glTexCoord2f(0.0, 0.0); glVertex2f( i*_width/4,     0);
398       glTexCoord2f(1.0, 0.0); glVertex2f( (i+1)*_width/4, 0);
399       glTexCoord2f(1.0, 1.0); glVertex2f( (i+1)*_width/4, _height/2);
400       glTexCoord2f(0.0, 1.0); glVertex2f( i*_width/4,     _height/2);
401       glEnd();
402     }
403   } else {
404     float c[4];
405     glGetFloatv( GL_CURRENT_COLOR, c );
406     glColor4f( 0.0, 0.0, 0.0, 1.0 );
407     glBegin(GL_QUADS);
408     glVertex2f(0, 0);
409     glVertex2f(_width, 0);
410     glVertex2f(_width, _height);
411     glVertex2f(0, _height);
412     glEnd();
413     glColor4fv( c );
414   }
415
416   // Draw the instruments.
417   // Syd Adams: added instrument clipping
418   instrument_list_type::const_iterator current = _instruments.begin();
419   instrument_list_type::const_iterator end = _instruments.end();
420
421   GLdouble blx[4]={1.0,0.0,0.0,0.0};
422   GLdouble bly[4]={0.0,1.0,0.0,0.0};
423   GLdouble urx[4]={-1.0,0.0,0.0,0.0};
424   GLdouble ury[4]={0.0,-1.0,0.0,0.0};
425
426   for ( ; current != end; current++) {
427     FGPanelInstrument * instr = *current;
428     glPushMatrix();
429     glTranslated(instr->getXPos(), instr->getYPos(), 0);
430
431     int ix= instr->getWidth();
432     int iy= instr->getHeight();
433     glPushMatrix();
434     glTranslated(-ix/2,-iy/2,0);
435     glClipPlane(GL_CLIP_PLANE0,blx);
436     glClipPlane(GL_CLIP_PLANE1,bly);
437     glEnable(GL_CLIP_PLANE0);
438     glEnable(GL_CLIP_PLANE1);
439
440     glTranslated(ix,iy,0);
441     glClipPlane(GL_CLIP_PLANE2,urx);
442     glClipPlane(GL_CLIP_PLANE3,ury);
443     glEnable(GL_CLIP_PLANE2);
444     glEnable(GL_CLIP_PLANE3);
445     glPopMatrix();
446     instr->draw();
447
448     glPopMatrix();
449   }
450
451   glDisable(GL_CLIP_PLANE0);
452   glDisable(GL_CLIP_PLANE1);
453   glDisable(GL_CLIP_PLANE2);
454   glDisable(GL_CLIP_PLANE3);
455
456   // restore some original state
457   glPopAttrib();
458 }
459 #endif
460
461 /**
462  * Set the panel's background texture.
463  */
464 void
465 FGPanel::setBackground (FGCroppedTexture_ptr texture)
466 {
467   _bg = texture;
468 }
469
470 /**
471  * Set the panel's multiple background textures.
472  */
473 void
474 FGPanel::setMultiBackground (FGCroppedTexture_ptr texture, int idx)
475 {
476   _bg = 0;
477   _mbg[idx] = texture;
478 }
479
480 ////////////////////////////////////////////////////////////////////////
481 // Implementation of FGPanelTransformation.
482 ////////////////////////////////////////////////////////////////////////
483
484 FGPanelTransformation::FGPanelTransformation ()
485   : table(0)
486 {
487 }
488
489 FGPanelTransformation::~FGPanelTransformation ()
490 {
491   delete table;
492 }
493
494
495 \f
496 ////////////////////////////////////////////////////////////////////////
497 // Implementation of FGPanelInstrument.
498 ////////////////////////////////////////////////////////////////////////
499
500
501 FGPanelInstrument::FGPanelInstrument ()
502 {
503   setPosition(0, 0);
504   setSize(0, 0);
505 }
506
507 FGPanelInstrument::FGPanelInstrument (int x, int y, int w, int h)
508 {
509   setPosition(x, y);
510   setSize(w, h);
511 }
512
513 FGPanelInstrument::~FGPanelInstrument ()
514 {
515 }
516
517 void
518 FGPanelInstrument::setPosition (int x, int y)
519 {
520   _x = x;
521   _y = y;
522 }
523
524 void
525 FGPanelInstrument::setSize (int w, int h)
526 {
527   _w = w;
528   _h = h;
529 }
530
531 int
532 FGPanelInstrument::getXPos () const
533 {
534   return _x;
535 }
536
537 int
538 FGPanelInstrument::getYPos () const
539 {
540   return _y;
541 }
542
543 int
544 FGPanelInstrument::getWidth () const
545 {
546   return _w;
547 }
548
549 int
550 FGPanelInstrument::getHeight () const
551 {
552   return _h;
553 }
554
555 \f
556 ////////////////////////////////////////////////////////////////////////
557 // Implementation of FGLayeredInstrument.
558 ////////////////////////////////////////////////////////////////////////
559
560 FGLayeredInstrument::FGLayeredInstrument (int x, int y, int w, int h)
561   : FGPanelInstrument(x, y, w, h)
562 {
563 }
564
565 FGLayeredInstrument::~FGLayeredInstrument ()
566 {
567   for (layer_list::iterator it = _layers.begin(); it != _layers.end(); it++) {
568     delete *it;
569     *it = 0;
570   }
571 }
572
573 void
574 FGLayeredInstrument::draw ()
575 {
576   if (!test())
577     return;
578   
579   for (int i = 0; i < (int)_layers.size(); i++) {
580     glPushMatrix();
581     _layers[i]->draw();
582     glPopMatrix();
583   }
584 }
585
586 int
587 FGLayeredInstrument::addLayer (FGInstrumentLayer *layer)
588 {
589   int n = _layers.size();
590   if (layer->getWidth() == -1) {
591     layer->setWidth(getWidth());
592   }
593   if (layer->getHeight() == -1) {
594     layer->setHeight(getHeight());
595   }
596   _layers.push_back(layer);
597   return n;
598 }
599
600 int
601 FGLayeredInstrument::addLayer (FGCroppedTexture_ptr texture, int w, int h)
602 {
603   return addLayer(new FGTexturedLayer(texture, w, h));
604 }
605
606 void
607 FGLayeredInstrument::addTransformation (FGPanelTransformation * transformation)
608 {
609   int layer = _layers.size() - 1;
610   _layers[layer]->addTransformation(transformation);
611 }
612
613
614 \f
615 ////////////////////////////////////////////////////////////////////////
616 // Implementation of FGInstrumentLayer.
617 ////////////////////////////////////////////////////////////////////////
618
619 FGInstrumentLayer::FGInstrumentLayer (int w, int h)
620   : _w(w),
621     _h(h)
622 {
623 }
624
625 FGInstrumentLayer::~FGInstrumentLayer ()
626 {
627   for (transformation_list::iterator it = _transformations.begin();
628        it != _transformations.end();
629        it++) {
630     delete *it;
631     *it = 0;
632   }
633 }
634
635 void
636 FGInstrumentLayer::transform () const
637 {
638   transformation_list::const_iterator it = _transformations.begin();
639   transformation_list::const_iterator last = _transformations.end();
640   while (it != last) {
641     FGPanelTransformation *t = *it;
642     if (t->test()) {
643       float val = (t->node == 0 ? 0.0 : t->node->getFloatValue());
644
645       if (t->has_mod)
646           val = fmod(val, t->mod);
647       if (val < t->min) {
648         val = t->min;
649       } else if (val > t->max) {
650         val = t->max;
651       }
652
653       if (t->table==0) {
654         val = val * t->factor + t->offset;
655       } else {
656         val = t->table->interpolate(val) * t->factor + t->offset;
657      }
658       
659       switch (t->type) {
660       case FGPanelTransformation::XSHIFT:
661         glTranslatef(val, 0.0, 0.0);
662         break;
663       case FGPanelTransformation::YSHIFT:
664         glTranslatef(0.0, val, 0.0);
665         break;
666       case FGPanelTransformation::ROTATION:
667         glRotatef(-val, 0.0, 0.0, 1.0);
668         break;
669       }
670     }
671     it++;
672   }
673 }
674
675 void
676 FGInstrumentLayer::addTransformation (FGPanelTransformation * transformation)
677 {
678   _transformations.push_back(transformation);
679 }
680
681
682 \f
683 ////////////////////////////////////////////////////////////////////////
684 // Implementation of FGGroupLayer.
685 ////////////////////////////////////////////////////////////////////////
686
687 FGGroupLayer::FGGroupLayer ()
688 {
689 }
690
691 FGGroupLayer::~FGGroupLayer ()
692 {
693   for (unsigned int i = 0; i < _layers.size(); i++)
694     delete _layers[i];
695 }
696
697 void
698 FGGroupLayer::draw ()
699 {
700   if (test()) {
701     transform();
702     int nLayers = _layers.size();
703     for (int i = 0; i < nLayers; i++)
704       _layers[i]->draw( );
705   }
706 }
707
708 void
709 FGGroupLayer::addLayer (FGInstrumentLayer * layer)
710 {
711   _layers.push_back(layer);
712 }
713
714
715 \f
716 ////////////////////////////////////////////////////////////////////////
717 // Implementation of FGTexturedLayer.
718 ////////////////////////////////////////////////////////////////////////
719
720
721 FGTexturedLayer::FGTexturedLayer (FGCroppedTexture_ptr texture, int w, int h)
722   : FGInstrumentLayer(w, h),
723     _emissive(false),
724     displayList(0)
725 {
726   setTexture(texture);
727 }
728
729
730 FGTexturedLayer::~FGTexturedLayer ()
731 {
732 }
733
734 GLuint
735 FGTexturedLayer::getDisplayList()
736 {
737   if( displayList != 0 )
738     return displayList;
739
740   int w2 = _w / 2;
741   int h2 = _h / 2;
742
743   _texture->bind( false );
744   displayList = glGenLists(1);
745   glNewList(displayList,GL_COMPILE_AND_EXECUTE);
746     glBindTexture( GL_TEXTURE_2D, _texture->getTexture() );
747     glBegin(GL_QUADS);
748       glTexCoord2f(_texture->getMinX(), _texture->getMinY()); glVertex2f(-w2, -h2);
749       glTexCoord2f(_texture->getMaxX(), _texture->getMinY()); glVertex2f(w2, -h2);
750       glTexCoord2f(_texture->getMaxX(), _texture->getMaxY()); glVertex2f(w2, h2);
751       glTexCoord2f(_texture->getMinX(), _texture->getMaxY()); glVertex2f(-w2, h2);
752     glEnd();
753   glEndList();
754
755   return displayList;
756 }
757
758 void
759 FGTexturedLayer::draw ( )
760 {
761   if (test()) {
762     transform();
763     glCallList(getDisplayList());
764   }
765 }
766
767
768 \f
769 ////////////////////////////////////////////////////////////////////////
770 // Implementation of FGTextLayer.
771 ////////////////////////////////////////////////////////////////////////
772
773 fntRenderer FGTextLayer::text_renderer;
774
775 FGTextLayer::FGTextLayer (int w, int h)
776   : FGInstrumentLayer(w, h), _pointSize(14.0), _font_name("Helvetica.txf")
777 {
778   _then.stamp();
779   _color[0] = _color[1] = _color[2] = 0.0;
780   _color[3] = 1.0;
781 }
782
783 FGTextLayer::~FGTextLayer ()
784 {
785   chunk_list::iterator it = _chunks.begin();
786   chunk_list::iterator last = _chunks.end();
787   for ( ; it != last; it++) {
788     delete *it;
789   }
790 }
791
792 void
793 FGTextLayer::draw ()
794 {
795   if (test()) {
796     float c[4];
797     glGetFloatv( GL_CURRENT_COLOR, c );
798     glColor4fv(_color);
799     transform();
800
801     text_renderer.setFont(ApplicationProperties::fontCache.getTexFont(_font_name.c_str()));
802
803     text_renderer.setPointSize(_pointSize);
804     text_renderer.begin();
805     text_renderer.start3f(0, 0, 0);
806
807     _now.stamp();
808     long diff = (_now - _then).toUSecs();
809
810     if (diff > 100000 || diff < 0 ) {
811       // ( diff < 0 ) is a sanity check and indicates our time stamp
812       // difference math probably overflowed.  We can handle a max
813       // difference of 35.8 minutes since the returned value is in
814       // usec.  So if the panel is left off longer than that we can
815       // over flow the math with it is turned back on.  This (diff <
816       // 0) catches that situation, get's us out of trouble, and
817       // back on track.
818       recalc_value();
819       _then = _now;
820     }
821
822     // Something is goofy.  The code in this file renders only CCW
823     // polygons, and I have verified that the font code in plib
824     // renders only CCW trianbles.  Yet they come out backwards.
825     // Something around here or in plib is either changing the winding
826     // order or (more likely) pushing a left-handed matrix onto the
827     // stack.  But I can't find it; get out the chainsaw...
828     glFrontFace(GL_CW);
829     text_renderer.puts((char *)(_value.c_str()));
830     glFrontFace(GL_CCW);
831
832     text_renderer.end();
833     glColor4fv( c );
834   }
835 }
836
837 void
838 FGTextLayer::addChunk (FGTextLayer::Chunk * chunk)
839 {
840   _chunks.push_back(chunk);
841 }
842
843 void
844 FGTextLayer::setColor (float r, float g, float b)
845 {
846   _color[0] = r;
847   _color[1] = g;
848   _color[2] = b;
849   _color[3] = 1.0;
850 }
851
852 void
853 FGTextLayer::setPointSize (float size)
854 {
855   _pointSize = size;
856 }
857
858 void
859 FGTextLayer::setFontName(const string &name)
860 {
861   _font_name = name + ".txf";
862 }
863
864
865 void
866 FGTextLayer::setFont(fntFont * font)
867 {
868   FGTextLayer::text_renderer.setFont(font);
869 }
870
871
872 void
873 FGTextLayer::recalc_value () const
874 {
875   _value = "";
876   chunk_list::const_iterator it = _chunks.begin();
877   chunk_list::const_iterator last = _chunks.end();
878   for ( ; it != last; it++) {
879     _value += (*it)->getValue();
880   }
881 }
882
883
884 \f
885 ////////////////////////////////////////////////////////////////////////
886 // Implementation of FGTextLayer::Chunk.
887 ////////////////////////////////////////////////////////////////////////
888
889 FGTextLayer::Chunk::Chunk (const string &text, const string &fmt)
890   : _type(FGTextLayer::TEXT), _fmt(fmt)
891 {
892   _text = text;
893   if (_fmt.empty()) 
894     _fmt = "%s";
895 }
896
897 FGTextLayer::Chunk::Chunk (ChunkType type, const SGPropertyNode * node,
898                            const string &fmt, float mult, float offs,
899                            bool truncation)
900   : _type(type), _fmt(fmt), _mult(mult), _offs(offs), _trunc(truncation)
901 {
902   if (_fmt.empty()) {
903     if (type == TEXT_VALUE)
904       _fmt = "%s";
905     else
906       _fmt = "%.2f";
907   }
908   _node = node;
909 }
910
911 const char *
912 FGTextLayer::Chunk::getValue () const
913 {
914   if (test()) {
915     _buf[0] = '\0';
916     switch (_type) {
917     case TEXT:
918       sprintf(_buf, _fmt.c_str(), _text.c_str());
919       return _buf;
920     case TEXT_VALUE:
921       sprintf(_buf, _fmt.c_str(), _node->getStringValue());
922       break;
923     case DOUBLE_VALUE:
924       double d = _offs + _node->getFloatValue() * _mult;
925       if (_trunc)  d = (d < 0) ? -floor(-d) : floor(d);
926       sprintf(_buf, _fmt.c_str(), d);
927       break;
928     }
929     return _buf;
930   } else {
931     return "";
932   }
933 }
934
935
936 \f
937 ////////////////////////////////////////////////////////////////////////
938 // Implementation of FGSwitchLayer.
939 ////////////////////////////////////////////////////////////////////////
940
941 FGSwitchLayer::FGSwitchLayer ()
942   : FGGroupLayer()
943 {
944 }
945
946 void
947 FGSwitchLayer::draw ()
948 {
949   if (test()) {
950     transform();
951     int nLayers = _layers.size();
952     for (int i = 0; i < nLayers; i++) {
953       if (_layers[i]->test()) {
954           _layers[i]->draw();
955           return;
956       }
957     }
958   }
959 }
960
961 \f
962 // end of panel.cxx