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