1 // panel.cxx - default, 2D single-engine prop instrument panel
3 // Written by David Megginson, started January 2000.
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.
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.
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.
35 #include <simgear/debug/logstream.hxx>
36 #include <simgear/misc/fgpath.hxx>
37 #include <Main/options.hxx>
38 #include <Main/bfi.hxx>
39 #include <Objects/texload.h>
40 #include <Autopilot/autopilot.hxx>
41 #include <Time/fg_time.hxx>
43 #include "cockpit.hxx"
48 extern fgAPDataPtr APDataGlobal;
53 #define SIX_SPACING (SIX_W + 5)
58 ////////////////////////////////////////////////////////////////////////
59 // Static functions for obtaining settings.
61 // These should be replaced with functions from a global facade,
62 // or BFI (Big Friendly Interface).
63 ////////////////////////////////////////////////////////////////////////
65 static char * panelGetTime (char * buf)
67 struct tm * t = FGTime::cur_time_params->getGmt();
68 sprintf(buf, " %.2d:%.2d:%.2d",
69 t->tm_hour, t->tm_min, t->tm_sec);
75 ////////////////////////////////////////////////////////////////////////
76 // Static factory functions to create textured gauges.
78 // These will be replaced first with a giant table, and then with
79 // configuration files read from an external source, but for now
80 // they're hard-coded.
81 ////////////////////////////////////////////////////////////////////////
84 createTexture (const char * relativePath)
86 return FGPanel::OurPanel->createTexture(relativePath);
91 * Construct an airspeed indicator for a single-engine prop.
93 static FGPanelInstrument *
94 createAirspeedIndicator (int x, int y)
96 FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W, SIX_W);
98 // Layer 0: gauge background.
99 inst->addLayer(0, createTexture("Textures/Panel/airspeed.rgb"));
102 // Rotates with airspeed.
103 inst->addLayer(1, createTexture("Textures/Panel/long-needle.rgb"));
104 inst->addTransformation(1, FGInstrumentLayer::ROTATION,
105 FGSteam::get_ASI_kias,
106 30.0, 220.0, 36.0 / 20.0, -54.0);
112 * Construct an artificial horizon.
114 static FGPanelInstrument *
115 createHorizon (int x, int y)
117 FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W, SIX_W);
119 // Layer 0: coloured background
120 // moves with roll only
121 inst->addLayer(0, createTexture("Textures/Panel/horizon-bg.rgb"));
122 inst->addTransformation(0, FGInstrumentLayer::ROTATION,
124 -360.0, 360.0, -1.0, 0.0);
126 // Layer 1: floating horizon
127 // moves with roll and pitch
128 inst->addLayer(1, createTexture("Textures/Panel/horizon-float.rgb"));
129 inst->addTransformation(1, FGInstrumentLayer::ROTATION,
131 -360.0, 360.0, -1.0, 0.0);
132 inst->addTransformation(1, FGInstrumentLayer::YSHIFT,
134 -20.0, 20.0, -(1.5 / 160.0) * SIX_W, 0.0);
137 // moves with roll only
138 inst->addLayer(2, createTexture("Textures/Panel/horizon-rim.rgb"));
139 inst->addTransformation(2, FGInstrumentLayer::ROTATION,
141 -360.0, 360.0, -1.0, 0.0);
143 // Layer 3: glass front of gauge
144 // fixed, with markings
145 inst->addLayer(3, createTexture("Textures/Panel/horizon-fg.rgb"));
152 * Construct an altimeter.
154 static FGPanelInstrument *
155 createAltimeter (int x, int y)
157 FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W, SIX_W);
159 // Layer 0: gauge background
160 inst->addLayer(0, createTexture("Textures/Panel/altimeter.rgb"));
162 // Layer 1: hundreds needle (long)
163 // moves with altitude
164 inst->addLayer(1, createTexture("Textures/Panel/long-needle.rgb"));
165 inst->addTransformation(1, FGInstrumentLayer::ROTATION,
167 0.0, 100000.0, 360.0 / 1000.0, 0.0);
169 // Layer 2: thousands needle (short)
170 // moves with altitude
171 inst->addLayer(2, createTexture("Textures/Panel/short-needle.rgb"));
172 inst->addTransformation(2, FGInstrumentLayer::ROTATION,
174 0.0, 100000.0, 360.0 / 10000.0, 0.0);
176 // Layer 3: ten thousands bug (outside)
177 // moves with altitude
178 inst->addLayer(3, createTexture("Textures/Panel/bug.rgb"));
179 inst->addTransformation(3, FGInstrumentLayer::ROTATION,
181 0.0, 100000.0, 360.0 / 100000.0, 0.0);
188 * Construct a turn coordinator.
190 static FGPanelInstrument *
191 createTurnCoordinator (int x, int y)
193 FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W, SIX_W);
195 // Layer 0: background
196 inst->addLayer(0, createTexture("Textures/Panel/turn-bg.rgb"));
198 // Layer 1: little plane
200 inst->addLayer(1, createTexture("Textures/Panel/turn.rgb"));
201 inst->addTransformation(1, FGInstrumentLayer::ROTATION,
202 FGSteam::get_TC_radps,
203 -30.0, 30.0, 1.0, 0.0);
205 // Layer 2: little ball
206 // moves with slip/skid
207 inst->addLayer(2, createTexture("Textures/Panel/ball.rgb"));
208 inst->addTransformation(2, FGInstrumentLayer::ROTATION,
210 -0.1, 0.1, 450.0, 0.0);
217 * Construct a gyro compass.
219 static FGPanelInstrument *
220 createGyroCompass (int x, int y)
222 FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W, SIX_W);
224 // Layer 0: compass background
225 // rotates with heading
226 inst->addLayer(0, createTexture("Textures/Panel/gyro-bg.rgb"));
227 inst->addTransformation(0, FGInstrumentLayer::ROTATION,
229 -360.0, 360.0, -1.0, 0.0);
231 // Layer 1: heading bug
232 // rotates with heading and AP heading
233 inst->addLayer(1, createTexture("Textures/Panel/bug.rgb"));
234 inst->addTransformation(1, FGInstrumentLayer::ROTATION,
236 -360.0, 360.0, -1.0, 0.0);
237 inst->addTransformation(1, FGInstrumentLayer::ROTATION,
239 -360.0, 360.0, 1.0, 0.0);
241 // Layer 2: fixed center
242 inst->addLayer(2, createTexture("Textures/Panel/gyro-fg.rgb"));
249 * Construct a vertical velocity indicator.
251 static FGPanelInstrument *
252 createVerticalVelocity (int x, int y)
254 FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W, SIX_W);
256 // Layer 0: gauge background
257 inst->addLayer(0, createTexture("Textures/Panel/vertical.rgb"));
260 // moves with vertical velocity
261 inst->addLayer(1, createTexture("Textures/Panel/long-needle.rgb"));
262 inst->addTransformation(1, FGInstrumentLayer::ROTATION,
263 FGSteam::get_VSI_fps,
264 -2000.0, 2000.0, 42.0/500.0, 270.0);
271 * Construct an RPM gauge.
273 static FGPanelInstrument *
274 createRPMGauge (int x, int y)
276 FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SMALL_W, SMALL_W);
278 // Layer 0: gauge background
279 inst->addLayer(0, createTexture("Textures/Panel/rpm.rgb"));
281 // Layer 1: long needle
282 // FIXME: moves with throttle (for now)
283 inst->addLayer(1, createTexture("Textures/Panel/long-needle.rgb"));
284 inst->addTransformation(1, FGInstrumentLayer::ROTATION,
286 0.0, 100.0, 300.0, -150.0);
293 * Construct a flap position indicator.
295 static FGPanelInstrument *
296 createFlapIndicator (int x, int y)
298 FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SMALL_W, SMALL_W);
300 // Layer 0: gauge background
301 inst->addLayer(0, createTexture("Textures/Panel/flaps.rgb"));
303 // Layer 1: long needle
304 // shifted over, rotates with flap position
305 inst->addLayer(1, createTexture("Textures/Panel/long-needle.rgb"));
306 inst->addTransformation(1, FGInstrumentLayer::XSHIFT,
307 -(SMALL_W / 4) + (SMALL_W / 16));
308 inst->addTransformation(1, FGInstrumentLayer::ROTATION,
310 0.0, 1.0, 120.0, 30.0);
315 static FGPanelInstrument *
316 createChronometer (int x, int y)
318 FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SMALL_W, SMALL_W);
320 // Layer 0: gauge background
321 inst->addLayer(0, createTexture("Textures/Panel/clock.rgb"));
324 // displays current GMT
325 FGCharInstrumentLayer * text =
326 new FGCharInstrumentLayer(panelGetTime,
327 SMALL_W, SMALL_W, 1);
328 text->setPointSize(14);
329 text->setColor(0.2, 0.2, 0.2);
330 inst->addLayer(text);
331 inst->addTransformation(1, FGInstrumentLayer::XSHIFT, SMALL_W * -0.38);
332 inst->addTransformation(1, FGInstrumentLayer::YSHIFT, SMALL_W * -0.06);
339 * Construct control-position indicators.
341 static FGPanelInstrument *
342 createControls (int x, int y)
344 FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SMALL_W, SMALL_W);
346 // Layer 0: gauge background
347 inst->addLayer(0, createTexture("Textures/Panel/controls.rgb"));
350 // moves left-right with aileron
351 inst->addLayer(1, createTexture("Textures/Panel/bug.rgb"));
352 inst->addTransformation(1, FGInstrumentLayer::XSHIFT, FGBFI::getAileron,
353 -1.0, 1.0, SMALL_W * .75 / 2.0, 0.0);
356 // moves left-right with rudder
357 inst->addLayer(2, createTexture("Textures/Panel/bug.rgb"));
358 inst->addTransformation(2, FGInstrumentLayer::ROTATION, 180.0);
359 inst->addTransformation(2, FGInstrumentLayer::XSHIFT, FGBFI::getRudder,
360 -1.0, 1.0, -SMALL_W * .75 / 2.0, 0.0);
363 // moves up-down with elevator trim
364 inst->addLayer(3, createTexture("Textures/Panel/bug.rgb"));
365 inst->addTransformation(3, FGInstrumentLayer::ROTATION, 270.0);
366 inst->addTransformation(3, FGInstrumentLayer::YSHIFT,
367 -SMALL_W * (3.0 / 8.0));
368 inst->addTransformation(3, FGInstrumentLayer::XSHIFT, FGBFI::getElevatorTrim,
369 -1.0, 1.0, SMALL_W * .75 / 2.0, 0.0);
372 // moves up-down with elevator
373 inst->addLayer(4, createTexture("Textures/Panel/bug.rgb"));
374 inst->addTransformation(4, FGInstrumentLayer::ROTATION, 90.0);
375 inst->addTransformation(4, FGInstrumentLayer::YSHIFT,
376 -SMALL_W * (3.0 / 8.0));
377 inst->addTransformation(4, FGInstrumentLayer::XSHIFT, FGBFI::getElevator,
378 -1.0, 1.0, -SMALL_W * .75 / 2.0, 0.0);
385 * Construct a NAV1 gauge (hardwired).
387 static FGPanelInstrument *
388 createNAV1 (int x, int y)
390 FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W, SIX_W);
392 // Layer 0: background
393 inst->addLayer(0, createTexture("Textures/Panel/gyro-bg.rgb"));
394 inst->addTransformation(0, FGInstrumentLayer::ROTATION,
395 FGSteam::get_HackOBS1_deg,
396 -360.0, 360.0, -1.0, 0.0);
397 // Layer 1: long needle
398 inst->addLayer(1, createTexture("Textures/Panel/nav-needle.rgb"));
399 inst->addTransformation(1, FGInstrumentLayer::XSHIFT,
400 FGSteam::get_HackVOR1_deg,
401 -10.0, 10.0, SIX_W / 40.0, 0.0);
402 inst->addLayer(2, createTexture("Textures/Panel/nav-needle.rgb"));
403 inst->addTransformation(2, FGInstrumentLayer::YSHIFT,
404 FGSteam::get_HackGS_deg,
405 -1.0, 1.0, SIX_W / 5.0, 0.0);
406 inst->addTransformation(2, FGInstrumentLayer::ROTATION,
408 inst->addLayer(3, createTexture("Textures/Panel/nav-face.rgb"));
415 * Construct a NAV2 gauge (hardwired).
417 static FGPanelInstrument *
418 createNAV2 (int x, int y)
420 FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W, SIX_W);
422 // Layer 0: background
423 inst->addLayer(0, createTexture("Textures/Panel/gyro-bg.rgb"));
424 inst->addTransformation(0, FGInstrumentLayer::ROTATION,
425 FGSteam::get_HackOBS2_deg,
426 -360.0, 360.0, -1.0, 0.0);
427 inst->addLayer(1, createTexture("Textures/Panel/nav-needle.rgb"));
428 inst->addTransformation(1, FGInstrumentLayer::XSHIFT,
429 FGSteam::get_HackVOR2_deg,
430 -10.0, 10.0, SIX_W / 40.0, 0.0);
431 // inst->addTransformation(1, FGInstrumentLayer::YSHIFT,
433 inst->addLayer(2, createTexture("Textures/Panel/nav-face.rgb"));
440 * Construct an ADF gauge (hardwired).
442 static FGPanelInstrument *
443 createADF (int x, int y)
445 FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W, SIX_W);
447 // Layer 0: background
448 inst->addLayer(0, createTexture("Textures/Panel/gyro-bg.rgb"));
449 inst->addLayer(1, createTexture("Textures/Panel/long-needle.rgb"));
450 inst->addTransformation(1, FGInstrumentLayer::ROTATION,
451 FGSteam::get_HackADF_deg,
452 -720.0, 720.0, 1.0, 0.0);
459 ////////////////////////////////////////////////////////////////////////
460 // Implementation of FGPanel.
461 ////////////////////////////////////////////////////////////////////////
463 FGPanel * FGPanel::OurPanel = 0;
470 FG_LOG(FG_GENERAL, FG_ALERT, "Multiple panels");
477 _bg = createTexture("Textures/Panel/panel-bg.rgb");
479 // Chronometer alone at side
480 x = SIX_X - SIX_SPACING - 8;
481 _instruments.push_back(createChronometer(x, y));
485 _instruments.push_back(createAirspeedIndicator(x, y));
487 _instruments.push_back(createHorizon(x, y));
489 _instruments.push_back(createAltimeter(x, y));
490 x += SIX_SPACING + 20;
491 _instruments.push_back(createNAV1(x, y));
496 _instruments.push_back(createTurnCoordinator(x, y));
498 _instruments.push_back(createGyroCompass(x, y));
500 _instruments.push_back(createVerticalVelocity(x, y));
501 x += SIX_SPACING + 20;
502 _instruments.push_back(createNAV2(x, y));
506 y -= SIX_SPACING + 10;
507 _instruments.push_back(createControls(x, y));
509 _instruments.push_back(createFlapIndicator(x, y));
511 _instruments.push_back(createRPMGauge(x, y));
512 x += SIX_SPACING + 20;
514 _instruments.push_back(createADF(x, y));
521 instrument_list_type::iterator current = _instruments.begin();
522 instrument_list_type::iterator last = _instruments.end();
524 for ( ; current != last; ++current) {
531 FGPanel::get_height () const
537 FGPanel::ReInit (int x, int y, int finx, int finy)
543 _panel_h = (int)((finy - y) * 0.5768 + 1);
547 FGPanel::Update () const
549 glMatrixMode(GL_PROJECTION);
552 gluOrtho2D(_x, _x + _w, _y, _y + _h);
554 glMatrixMode(GL_MODELVIEW);
558 // Draw the background
559 glEnable(GL_TEXTURE_2D);
560 glDisable(GL_LIGHTING);
562 glEnable(GL_ALPHA_TEST);
563 glEnable(GL_COLOR_MATERIAL);
564 glColor4f(1.0, 1.0, 1.0, 1.0);
565 glBindTexture(GL_TEXTURE_2D, _bg->getHandle());
566 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
567 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
568 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
570 glTexCoord2f(0.0, 0.0); glVertex3f(_x, _y, 0);
571 glTexCoord2f(10.0, 0.0); glVertex3f(_x + _w, _y, 0);
572 glTexCoord2f(10.0, 5.0); glVertex3f(_x + _w, _y + _panel_h, 0);
573 glTexCoord2f(0.0, 5.0); glVertex3f(_x, _y + _panel_h, 0);
576 // Draw the instruments.
577 instrument_list_type::const_iterator current = _instruments.begin();
578 instrument_list_type::const_iterator end = _instruments.end();
580 for ( ; current != end; current++) {
581 FGPanelInstrument * instr = *current;
583 glTranslated(instr->getXPos(), instr->getYPos(), 0);
587 glMatrixMode(GL_PROJECTION);
589 glMatrixMode(GL_MODELVIEW);
591 ssgForceBasicState();
592 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
596 FGPanel::createTexture (const char * relativePath)
600 texture = _textureMap[relativePath];
602 FGPath tpath(current_options.get_fg_root());
603 tpath.append(relativePath);
604 texture = new ssgTexture((char *)tpath.c_str(), false, false);
605 _textureMap[relativePath] = texture;
606 cerr << "Created texture " << relativePath
607 << " handle=" << texture->getHandle() << endl;
615 ////////////////////////////////////////////////////////////////////////
616 // Implementation of FGPanelInstrument.
617 ////////////////////////////////////////////////////////////////////////
620 FGPanelInstrument::FGPanelInstrument ()
626 FGPanelInstrument::FGPanelInstrument (int x, int y, int w, int h)
632 FGPanelInstrument::~FGPanelInstrument ()
637 FGPanelInstrument::setPosition (int x, int y)
644 FGPanelInstrument::setSize (int w, int h)
651 FGPanelInstrument::getXPos () const
657 FGPanelInstrument::getYPos () const
664 ////////////////////////////////////////////////////////////////////////
665 // Implementation of FGLayeredInstrument.
666 ////////////////////////////////////////////////////////////////////////
668 FGLayeredInstrument::FGLayeredInstrument (int x, int y, int w, int h)
669 : FGPanelInstrument(x, y, w, h)
673 FGLayeredInstrument::~FGLayeredInstrument ()
675 // FIXME: free layers
679 FGLayeredInstrument::draw () const
681 layer_list::const_iterator it = _layers.begin();
682 layer_list::const_iterator last = _layers.end();
690 FGLayeredInstrument::addLayer (FGInstrumentLayer *layer)
692 _layers.push_back(layer);
696 FGLayeredInstrument::addLayer (int layer, ssgTexture * texture)
698 addLayer(new FGTexturedInstrumentLayer(texture, _w, _h, layer));
702 FGLayeredInstrument::addTransformation (int layer,
703 FGInstrumentLayer::transform_type type,
704 FGInstrumentLayer::transform_func func,
705 double min, double max,
706 double factor, double offset)
708 _layers[layer]->addTransformation(type, func, min, max, factor, offset);
713 ////////////////////////////////////////////////////////////////////////
714 // Implementation of FGInstrumentLayer.
715 ////////////////////////////////////////////////////////////////////////
717 FGInstrumentLayer::FGInstrumentLayer (int w, int h, int z)
724 FGInstrumentLayer::~FGInstrumentLayer ()
726 transformation_list::iterator it = _transformations.begin();
727 transformation_list::iterator end = _transformations.end();
735 FGInstrumentLayer::transform () const
737 glTranslatef(0.0, 0.0, (_z / 100.0) + 0.1);
739 transformation_list::const_iterator it = _transformations.begin();
740 transformation_list::const_iterator last = _transformations.end();
742 transformation *t = *it;
743 double value = (t->func == 0 ? 0.0 : (*(t->func))());
744 if (value < t->min) {
746 } else if (value > t->max) {
749 value = value * t->factor + t->offset;
753 glTranslatef(value, 0.0, 0.0);
756 glTranslatef(0.0, value, 0.0);
759 glRotatef(-value, 0.0, 0.0, 1.0);
767 FGInstrumentLayer::addTransformation (transform_type type,
769 double min, double max,
770 double factor, double offset)
772 transformation *t = new transformation;
779 _transformations.push_back(t);
784 ////////////////////////////////////////////////////////////////////////
785 // Implementation of FGTexturedInstrumentLayer.
786 ////////////////////////////////////////////////////////////////////////
788 // FGTexturedInstrumentLayer::FGTexturedInstrumentLayer (const char *tname,
789 // int w, int h, int z)
790 // : FGInstrumentLayer(w, h, z)
792 // setTexture(tname);
795 FGTexturedInstrumentLayer::FGTexturedInstrumentLayer (ssgTexture * texture,
797 : FGInstrumentLayer(w, h, z)
802 FGTexturedInstrumentLayer::~FGTexturedInstrumentLayer ()
807 FGTexturedInstrumentLayer::draw () const
814 glBindTexture(GL_TEXTURE_2D, _texture->getHandle());
816 // FIXME: is this really correct
818 glTexCoord2f(0.0, 0.0); glVertex2f(-w2, -h2);
819 glTexCoord2f(1.0, 0.0); glVertex2f(w2, -h2);
820 glTexCoord2f(1.0, 1.0); glVertex2f(w2, h2);
821 glTexCoord2f(0.0, 1.0); glVertex2f(-w2, h2);
827 // FGTexturedInstrumentLayer::setTexture (const char *textureName)
829 // FGPath tpath(current_options.get_fg_root());
830 // tpath.append(textureName);
831 // ssgTexture * texture = new ssgTexture((char *)tpath.c_str(), false, false);
832 // setTexture(texture);
837 ////////////////////////////////////////////////////////////////////////
838 // Implementation of FGCharInstrumentLayer.
839 ////////////////////////////////////////////////////////////////////////
841 FGCharInstrumentLayer::FGCharInstrumentLayer (text_func func,
843 : FGInstrumentLayer(w, h, z),
846 _renderer.setFont(guiFntHandle);
847 _renderer.setPointSize(14);
848 _color[0] = _color[1] = _color[2] = 0.0;
852 FGCharInstrumentLayer::~FGCharInstrumentLayer ()
857 FGCharInstrumentLayer::draw () const
863 _renderer.start3f(0, 0, 0);
864 _renderer.puts((*_func)(_buf));
866 glColor4f(1.0, 1.0, 1.0, 1.0); // FIXME
871 FGCharInstrumentLayer::setColor (float r, float g, float b)
880 FGCharInstrumentLayer::setPointSize (const float size)
882 _renderer.setPointSize(size);
886 FGCharInstrumentLayer::setFont(fntFont * font)
888 _renderer.setFont(font);