]> git.mxchange.org Git - flightgear.git/blob - src/Cockpit/panel.cxx
David Megginson writes:
[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., 675 Mass Ave, Cambridge, MA 02139, USA.
18 //
19 //  $Id$
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 <string.h>
30
31 #include <plib/ssg.h>
32 #include <plib/fnt.h>
33
34 #include <simgear/debug/logstream.hxx>
35 #include <simgear/misc/fgpath.hxx>
36 #include <Main/options.hxx>
37 #include <Main/views.hxx>
38 #include <Objects/texload.h>
39
40 #include "hud.hxx"
41 #include "panel.hxx"
42
43
44 \f
45 ////////////////////////////////////////////////////////////////////////
46 // Implementation of FGTextureManager.
47 ////////////////////////////////////////////////////////////////////////
48
49 map<string,ssgTexture *> FGTextureManager::_textureMap;
50
51 ssgTexture *
52 FGTextureManager::createTexture (const string &relativePath)
53 {
54   ssgTexture * texture = _textureMap[relativePath];
55   if (texture == 0) {
56     cerr << "Texture " << relativePath << " does not yet exist" << endl;
57     FGPath tpath(current_options.get_fg_root());
58     tpath.append(relativePath);
59     texture = new ssgTexture((char *)tpath.c_str(), false, false);
60     _textureMap[relativePath] = texture;
61     if (_textureMap[relativePath] == 0) 
62       cerr << "Texture *still* doesn't exist" << endl;
63     cerr << "Created texture " << relativePath
64          << " handle=" << texture->getHandle() << endl;
65   }
66
67   return texture;
68 }
69
70
71
72 \f
73 ////////////////////////////////////////////////////////////////////////
74 // Implementation of FGCropped Texture.
75 ////////////////////////////////////////////////////////////////////////
76
77
78 FGCroppedTexture::FGCroppedTexture ()
79   : _path(""), _texture(0),
80     _minX(0.0), _minY(0.0), _maxX(1.0), _maxY(1.0)
81 {
82 }
83
84
85 FGCroppedTexture::FGCroppedTexture (const string &path,
86                                     float minX, float minY,
87                                     float maxX, float maxY)
88   : _path(path), _texture(0),
89     _minX(minX), _minY(minY), _maxX(maxX), _maxY(maxY)
90 {
91 }
92
93
94 FGCroppedTexture::~FGCroppedTexture ()
95 {
96 }
97
98
99 ssgTexture *
100 FGCroppedTexture::getTexture ()
101 {
102   if (_texture == 0) {
103     _texture = FGTextureManager::createTexture(_path);
104   }
105   return _texture;
106 }
107
108
109 \f
110 ////////////////////////////////////////////////////////////////////////
111 // Implementation of FGPanel.
112 ////////////////////////////////////////////////////////////////////////
113
114 FGPanel * current_panel = NULL;
115 static fntRenderer text_renderer;
116
117
118 /**
119  * Constructor.
120  */
121 FGPanel::FGPanel (int window_x, int window_y, int window_w, int window_h)
122   : _mouseDown(false),
123     _mouseInstrument(0),
124     _winx(window_x), _winy(window_y), _winw(window_w), _winh(window_h),
125     _width(_winw), _height(int(_winh * 0.5768 + 1)),
126     _x_offset(0), _y_offset(0), _view_height(int(_winh * 0.4232))
127 {
128   setVisibility(current_options.get_panel_status());
129 }
130
131
132 /**
133  * Destructor.
134  */
135 FGPanel::~FGPanel ()
136 {
137   for (instrument_list_type::iterator it = _instruments.begin();
138        it != _instruments.end();
139        it++) {
140     delete *it;
141     *it = 0;
142   }
143 }
144
145
146 /**
147  * Add an instrument to the panel.
148  */
149 void
150 FGPanel::addInstrument (FGPanelInstrument * instrument)
151 {
152   _instruments.push_back(instrument);
153 }
154
155
156 /**
157  * Update the panel.
158  */
159 void
160 FGPanel::update () const
161 {
162                                 // Do nothing if the panel isn't visible.
163   if (!_visibility)
164     return;
165
166                                 // If the mouse is down, do something
167   if (_mouseDown) {
168     _mouseDelay--;
169     if (_mouseDelay < 0) {
170       _mouseInstrument->doMouseAction(_mouseButton, _mouseX, _mouseY);
171       _mouseDelay = 2;
172     }
173   }
174
175                                 // Now, draw the panel
176   glMatrixMode(GL_PROJECTION);
177   glPushMatrix();
178   glLoadIdentity();
179   gluOrtho2D(_winx, _winx + _winw, _winy, _winy + _winh);
180
181   glMatrixMode(GL_MODELVIEW);
182   glPushMatrix();
183   glLoadIdentity();
184
185   glTranslated(_x_offset, _y_offset, 0);
186
187                                 // Draw the background
188   glEnable(GL_TEXTURE_2D);
189   glDisable(GL_LIGHTING);
190   glEnable(GL_BLEND);
191   glEnable(GL_ALPHA_TEST);
192   glEnable(GL_COLOR_MATERIAL);
193   // glColor4f(1.0, 1.0, 1.0, 1.0);
194   if ( cur_light_params.sun_angle * RAD_TO_DEG < 95.0 ) {
195       glColor4fv( cur_light_params.scene_diffuse );
196   } else {
197       glColor4f(0.7, 0.2, 0.2, 1.0);
198   }
199   glBindTexture(GL_TEXTURE_2D, _bg->getHandle());
200   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
201   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
202   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
203   glBegin(GL_POLYGON);
204   glTexCoord2f(0.0, 0.0); glVertex3f(_winx, _winy, 0);
205   glTexCoord2f(10.0, 0.0); glVertex3f(_winx + _width, _winy, 0);
206   glTexCoord2f(10.0, 5.0); glVertex3f(_winx + _width, _winy + _height, 0);
207   glTexCoord2f(0.0, 5.0); glVertex3f(_winx, _winy + _height, 0);
208   glEnd();
209
210                                 // Draw the instruments.
211   instrument_list_type::const_iterator current = _instruments.begin();
212   instrument_list_type::const_iterator end = _instruments.end();
213
214   for ( ; current != end; current++) {
215     FGPanelInstrument * instr = *current;
216     glLoadIdentity();
217     glTranslated(_x_offset, _y_offset, 0);
218     glTranslated(instr->getXPos(), instr->getYPos(), 0);
219     instr->draw();
220   }
221
222   glMatrixMode(GL_PROJECTION);
223   glPopMatrix();
224   glMatrixMode(GL_MODELVIEW);
225   glPopMatrix();
226   ssgForceBasicState();
227   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
228 }
229
230
231 /**
232  * Set the panel's visibility.
233  */
234 void
235 FGPanel::setVisibility (bool visibility)
236 {
237   _visibility = visibility;
238 }
239
240
241 /**
242  * Return true if the panel is visible.
243  */
244 bool
245 FGPanel::getVisibility () const
246 {
247   return _visibility;
248 }
249
250
251 /**
252  * Set the panel's background texture.
253  */
254 void
255 FGPanel::setBackground (ssgTexture * texture)
256 {
257   _bg = texture;
258 }
259
260
261 /**
262  * Set the panel's x-offset.
263  */
264 void
265 FGPanel::setXOffset (int offset)
266 {
267   if (offset <= 0 && offset >= -_width + _winw)
268     _x_offset = offset;
269 }
270
271
272 /**
273  * Set the panel's y-offset.
274  */
275 void
276 FGPanel::setYOffset (int offset)
277 {
278   if (offset <= 0 && offset >= -_height)
279     _y_offset = offset;
280 }
281
282
283 /**
284  * Perform a mouse action.
285  */
286 bool
287 FGPanel::doMouseAction (int button, int updown, int x, int y)
288 {
289
290                                 // Note a released button and return
291   // cerr << "Doing mouse action\n";
292   if (updown == 1) {
293     _mouseDown = false;
294     _mouseInstrument = 0;
295     return true;
296   }
297
298                                 // Scale for the real window size.
299   x = int(((float)x / current_view.get_winWidth()) * _winw);
300   y = int(_winh - (((float)y / current_view.get_winHeight()) * _winh));
301
302                                 // Adjust for offsets.
303   x -= _x_offset;
304   y -= _y_offset;
305
306                                 // Search for a matching instrument.
307   for (int i = 0; i < (int)_instruments.size(); i++) {
308     FGPanelInstrument *inst = _instruments[i];
309     int ix = inst->getXPos();
310     int iy = inst->getYPos();
311     int iw = inst->getWidth() / 2;
312     int ih = inst->getHeight() / 2;
313     if (x >= ix - iw && x < ix + iw && y >= iy - ih && y < iy + ih) {
314       _mouseDown = true;
315       _mouseDelay = 20;
316       _mouseInstrument = inst;
317       _mouseButton = button;
318       _mouseX = x - ix;
319       _mouseY = y - iy;
320                                 // Always do the action once.
321       _mouseInstrument->doMouseAction(_mouseButton, _mouseX, _mouseY);
322       return true;
323     }
324   }
325   return false;
326 }
327
328
329 \f
330 ////////////////////////////////////////////////////////////////////////.
331 // Implementation of FGPanelAction.
332 ////////////////////////////////////////////////////////////////////////
333
334 FGPanelAction::FGPanelAction ()
335 {
336 }
337
338 FGPanelAction::FGPanelAction (int button, int x, int y, int w, int h)
339   : _button(button), _x(x), _y(y), _w(w), _h(h)
340 {
341 }
342
343 FGPanelAction::~FGPanelAction ()
344 {
345 }
346
347
348 \f
349 ////////////////////////////////////////////////////////////////////////
350 // Implementation of FGAdjustAction.
351 ////////////////////////////////////////////////////////////////////////
352
353 FGAdjustAction::FGAdjustAction (int button, int x, int y, int w, int h,
354                                 SGValue * value, float increment, 
355                                 float min, float max, bool wrap)
356   : FGPanelAction(button, x, y, w, h),
357     _value(value), _increment(increment), _min(min), _max(max), _wrap(wrap)
358 {
359 }
360
361 FGAdjustAction::~FGAdjustAction ()
362 {
363 }
364
365 void
366 FGAdjustAction::doAction ()
367 {
368   float val = _value->getFloatValue();
369   val += _increment;
370   if (val < _min) {
371     val = (_wrap ? _max : _min);
372   } else if (val > _max) {
373     val = (_wrap ? _min : _max);
374   }
375   _value->setDoubleValue(val);
376 }
377
378
379 \f
380 ////////////////////////////////////////////////////////////////////////
381 // Implementation of FGSwapAction.
382 ////////////////////////////////////////////////////////////////////////
383
384 FGSwapAction::FGSwapAction (int button, int x, int y, int w, int h,
385                             SGValue * value1, SGValue * value2)
386   : FGPanelAction(button, x, y, w, h), _value1(value1), _value2(value2)
387 {
388 }
389
390 FGSwapAction::~FGSwapAction ()
391 {
392 }
393
394 void
395 FGSwapAction::doAction ()
396 {
397   float val = _value1->getFloatValue();
398   _value1->setDoubleValue(_value2->getFloatValue());
399   _value2->setDoubleValue(val);
400 }
401
402
403 \f
404 ////////////////////////////////////////////////////////////////////////
405 // Implementation of FGToggleAction.
406 ////////////////////////////////////////////////////////////////////////
407
408 FGToggleAction::FGToggleAction (int button, int x, int y, int w, int h,
409                                 SGValue * value)
410   : FGPanelAction(button, x, y, w, h), _value(value)
411 {
412 }
413
414 FGToggleAction::~FGToggleAction ()
415 {
416 }
417
418 void
419 FGToggleAction::doAction ()
420 {
421   _value->setBoolValue(!(_value->getBoolValue()));
422 }
423
424
425 \f
426 ////////////////////////////////////////////////////////////////////////
427 // Implementation of FGPanelTransformation.
428 ////////////////////////////////////////////////////////////////////////
429
430 FGPanelTransformation::FGPanelTransformation ()
431 {
432 }
433
434 FGPanelTransformation::~FGPanelTransformation ()
435 {
436 }
437
438
439 \f
440 ////////////////////////////////////////////////////////////////////////
441 // Implementation of FGPanelInstrument.
442 ////////////////////////////////////////////////////////////////////////
443
444
445 FGPanelInstrument::FGPanelInstrument ()
446 {
447   setPosition(0, 0);
448   setSize(0, 0);
449 }
450
451 FGPanelInstrument::FGPanelInstrument (int x, int y, int w, int h)
452 {
453   setPosition(x, y);
454   setSize(w, h);
455 }
456
457 FGPanelInstrument::~FGPanelInstrument ()
458 {
459   for (action_list_type::iterator it = _actions.begin();
460        it != _actions.end();
461        it++) {
462     delete *it;
463     *it = 0;
464   }
465 }
466
467 void
468 FGPanelInstrument::setPosition (int x, int y)
469 {
470   _x = x;
471   _y = y;
472 }
473
474 void
475 FGPanelInstrument::setSize (int w, int h)
476 {
477   _w = w;
478   _h = h;
479 }
480
481 int
482 FGPanelInstrument::getXPos () const
483 {
484   return _x;
485 }
486
487 int
488 FGPanelInstrument::getYPos () const
489 {
490   return _y;
491 }
492
493 int
494 FGPanelInstrument::getWidth () const
495 {
496   return _w;
497 }
498
499 int
500 FGPanelInstrument::getHeight () const
501 {
502   return _h;
503 }
504
505 void
506 FGPanelInstrument::addAction (FGPanelAction * action)
507 {
508   _actions.push_back(action);
509 }
510
511                                 // Coordinates relative to centre.
512 bool
513 FGPanelInstrument::doMouseAction (int button, int x, int y)
514 {
515   action_list_type::iterator it = _actions.begin();
516   action_list_type::iterator last = _actions.end();
517   for ( ; it != last; it++) {
518     if ((*it)->inArea(button, x, y)) {
519       (*it)->doAction();
520       return true;
521     }
522   }
523   return false;
524 }
525
526
527 \f
528 ////////////////////////////////////////////////////////////////////////
529 // Implementation of FGLayeredInstrument.
530 ////////////////////////////////////////////////////////////////////////
531
532 FGLayeredInstrument::FGLayeredInstrument (int x, int y, int w, int h)
533   : FGPanelInstrument(x, y, w, h)
534 {
535 }
536
537 FGLayeredInstrument::~FGLayeredInstrument ()
538 {
539   for (layer_list::iterator it = _layers.begin(); it != _layers.end(); it++) {
540     delete *it;
541     *it = 0;
542   }
543 }
544
545 void
546 FGLayeredInstrument::draw ()
547 {
548   for (int i = 0; i < (int)_layers.size(); i++) {
549     glPushMatrix();
550     glTranslatef(0.0, 0.0, (i / 100.0) + 0.1);
551     _layers[i]->draw();
552     glPopMatrix();
553   }
554 }
555
556 int
557 FGLayeredInstrument::addLayer (FGInstrumentLayer *layer)
558 {
559   int n = _layers.size();
560   if (layer->getWidth() == -1) {
561     layer->setWidth(getWidth());
562   }
563   if (layer->getHeight() == -1) {
564     layer->setHeight(getHeight());
565   }
566   _layers.push_back(layer);
567   return n;
568 }
569
570 int
571 FGLayeredInstrument::addLayer (FGCroppedTexture &texture,
572                                int w, int h)
573 {
574   return addLayer(new FGTexturedLayer(texture, w, h));
575 }
576
577 void
578 FGLayeredInstrument::addTransformation (FGPanelTransformation * transformation)
579 {
580   int layer = _layers.size() - 1;
581   _layers[layer]->addTransformation(transformation);
582 }
583
584
585 \f
586 ////////////////////////////////////////////////////////////////////////
587 // Implementation of FGInstrumentLayer.
588 ////////////////////////////////////////////////////////////////////////
589
590 FGInstrumentLayer::FGInstrumentLayer (int w, int h)
591   : _w(w),
592     _h(h)
593 {
594 }
595
596 FGInstrumentLayer::~FGInstrumentLayer ()
597 {
598   for (transformation_list::iterator it = _transformations.begin();
599        it != _transformations.end();
600        it++) {
601     delete *it;
602     *it = 0;
603   }
604 }
605
606 void
607 FGInstrumentLayer::transform () const
608 {
609   transformation_list::const_iterator it = _transformations.begin();
610   transformation_list::const_iterator last = _transformations.end();
611   while (it != last) {
612     FGPanelTransformation *t = *it;
613     float val = (t->value == 0 ? 0.0 : t->value->getFloatValue());
614     if (val < t->min) {
615       val = t->min;
616     } else if (val > t->max) {
617       val = t->max;
618     }
619     val = val * t->factor + t->offset;
620
621     switch (t->type) {
622     case FGPanelTransformation::XSHIFT:
623       glTranslatef(val, 0.0, 0.0);
624       break;
625     case FGPanelTransformation::YSHIFT:
626       glTranslatef(0.0, val, 0.0);
627       break;
628     case FGPanelTransformation::ROTATION:
629       glRotatef(-val, 0.0, 0.0, 1.0);
630       break;
631     }
632     it++;
633   }
634 }
635
636 void
637 FGInstrumentLayer::addTransformation (FGPanelTransformation * transformation)
638 {
639   _transformations.push_back(transformation);
640 }
641
642
643 \f
644 ////////////////////////////////////////////////////////////////////////
645 // Implementation of FGTexturedLayer.
646 ////////////////////////////////////////////////////////////////////////
647
648
649 FGTexturedLayer::FGTexturedLayer (const FGCroppedTexture &texture, int w, int h)
650   : FGInstrumentLayer(w, h)
651 {
652   setTexture(texture);
653 }
654
655
656 FGTexturedLayer::~FGTexturedLayer ()
657 {
658 }
659
660
661 void
662 FGTexturedLayer::draw ()
663 {
664   int w2 = _w / 2;
665   int h2 = _h / 2;
666
667   transform();
668   glBindTexture(GL_TEXTURE_2D, _texture.getTexture()->getHandle());
669   glBegin(GL_POLYGON);
670
671                                 // From Curt: turn on the panel
672                                 // lights after sundown.
673   if ( cur_light_params.sun_angle * RAD_TO_DEG < 95.0 ) {
674       glColor4fv( cur_light_params.scene_diffuse );
675   } else {
676       glColor4f(0.7, 0.2, 0.2, 1.0);
677   }
678
679
680   glTexCoord2f(_texture.getMinX(), _texture.getMinY()); glVertex2f(-w2, -h2);
681   glTexCoord2f(_texture.getMaxX(), _texture.getMinY()); glVertex2f(w2, -h2);
682   glTexCoord2f(_texture.getMaxX(), _texture.getMaxY()); glVertex2f(w2, h2);
683   glTexCoord2f(_texture.getMinX(), _texture.getMaxY()); glVertex2f(-w2, h2);
684   glEnd();
685 }
686
687
688 \f
689 ////////////////////////////////////////////////////////////////////////
690 // Implementation of FGTextLayer.
691 ////////////////////////////////////////////////////////////////////////
692
693 FGTextLayer::FGTextLayer (int w, int h)
694   : FGInstrumentLayer(w, h), _pointSize(14.0)
695 {
696   _then.stamp();
697   _color[0] = _color[1] = _color[2] = 0.0;
698   _color[3] = 1.0;
699 }
700
701 FGTextLayer::~FGTextLayer ()
702 {
703   chunk_list::iterator it = _chunks.begin();
704   chunk_list::iterator last = _chunks.end();
705   for ( ; it != last; it++) {
706     delete *it;
707   }
708 }
709
710 void
711 FGTextLayer::draw ()
712 {
713   glPushMatrix();
714   glColor4fv(_color);
715   transform();
716   text_renderer.setFont(guiFntHandle);
717   text_renderer.setPointSize(_pointSize);
718   text_renderer.begin();
719   text_renderer.start3f(0, 0, 0);
720
721   _now.stamp();
722   if (_now - _then > 100000) {
723     recalc_value();
724     _then = _now;
725   }
726   text_renderer.puts((char *)(_value.c_str()));
727
728   text_renderer.end();
729   glColor4f(1.0, 1.0, 1.0, 1.0);        // FIXME
730   glPopMatrix();
731 }
732
733 void
734 FGTextLayer::addChunk (FGTextLayer::Chunk * chunk)
735 {
736   _chunks.push_back(chunk);
737 }
738
739 void
740 FGTextLayer::setColor (float r, float g, float b)
741 {
742   _color[0] = r;
743   _color[1] = g;
744   _color[2] = b;
745   _color[3] = 1.0;
746 }
747
748 void
749 FGTextLayer::setPointSize (float size)
750 {
751   _pointSize = size;
752 }
753
754 void
755 FGTextLayer::setFont(fntFont * font)
756 {
757   text_renderer.setFont(font);
758 }
759
760
761 void
762 FGTextLayer::recalc_value () const
763 {
764   _value = "";
765   chunk_list::const_iterator it = _chunks.begin();
766   chunk_list::const_iterator last = _chunks.end();
767   for ( ; it != last; it++) {
768     _value += (*it)->getValue();
769   }
770 }
771
772
773 \f
774 ////////////////////////////////////////////////////////////////////////
775 // Implementation of FGTextLayer::Chunk.
776 ////////////////////////////////////////////////////////////////////////
777
778 FGTextLayer::Chunk::Chunk (const string &text, const string &fmt)
779   : _type(FGTextLayer::TEXT), _fmt(fmt)
780 {
781   _text = text;
782   if (_fmt == "") 
783     _fmt = "%s";
784 }
785
786 FGTextLayer::Chunk::Chunk (ChunkType type, const SGValue * value,
787                            const string &fmt, float mult)
788   : _type(type), _fmt(fmt), _mult(mult)
789 {
790   if (_fmt == "") {
791     if (type == TEXT_VALUE)
792       _fmt = "%s";
793     else
794       _fmt = "%.2f";
795   }
796   _value = value;
797 }
798
799 const char *
800 FGTextLayer::Chunk::getValue () const
801 {
802   switch (_type) {
803   case TEXT:
804     sprintf(_buf, _fmt.c_str(), _text.c_str());
805     return _buf;
806   case TEXT_VALUE:
807     sprintf(_buf, _fmt.c_str(), _value->getStringValue().c_str());
808     break;
809   case DOUBLE_VALUE:
810     sprintf(_buf, _fmt.c_str(), _value->getFloatValue() * _mult);
811     break;
812   }
813   return _buf;
814 }
815
816
817 \f
818 ////////////////////////////////////////////////////////////////////////
819 // Implementation of FGSwitchLayer.
820 ////////////////////////////////////////////////////////////////////////
821
822 FGSwitchLayer::FGSwitchLayer (int w, int h, const SGValue * value,
823                               FGInstrumentLayer * layer1,
824                               FGInstrumentLayer * layer2)
825   : FGInstrumentLayer(w, h), _value(value), _layer1(layer1), _layer2(layer2)
826 {
827 }
828
829 FGSwitchLayer::~FGSwitchLayer ()
830 {
831   delete _layer1;
832   delete _layer2;
833 }
834
835 void
836 FGSwitchLayer::draw ()
837 {
838   transform();
839   if (_value->getBoolValue()) {
840     _layer1->draw();
841   } else {
842     _layer2->draw();
843   }
844 }
845
846 \f
847 // end of panel.cxx