]> git.mxchange.org Git - flightgear.git/blob - src/Cockpit/panel.cxx
Panel updates from David Megginson. Radials can now be adjusted on the fly
[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 #include <GL/glut.h>
34
35 #include <simgear/debug/logstream.hxx>
36 #include <simgear/misc/fgpath.hxx>
37 #include <Main/options.hxx>
38 #include <Main/views.hxx>
39 #include <Main/bfi.hxx>
40 #include <Objects/texload.h>
41 #include <Autopilot/autopilot.hxx>
42 #include <Time/fg_time.hxx>
43
44 #include "cockpit.hxx"
45 #include "panel.hxx"
46 #include "hud.hxx"
47 #include "steam.hxx"
48
49 extern fgAPDataPtr APDataGlobal;
50
51 #define SIX_X 200
52 #define SIX_Y 345
53 #define SIX_W 128
54 #define SIX_SPACING (SIX_W + 5)
55 #define SMALL_W 112
56
57
58 \f
59 ////////////////////////////////////////////////////////////////////////
60 // Static functions for obtaining settings.
61 //
62 // These should be replaced with functions from a global facade,
63 // or BFI (Big Friendly Interface).
64 ////////////////////////////////////////////////////////////////////////
65
66 static char * panelGetTime (char * buf)
67 {
68   struct tm * t = FGTime::cur_time_params->getGmt();
69   sprintf(buf, " %.2d:%.2d:%.2d",
70           t->tm_hour, t->tm_min, t->tm_sec);
71   return buf;
72 }
73
74
75 \f
76 ////////////////////////////////////////////////////////////////////////
77 // Static factory functions to create textured gauges.
78 //
79 // These will be replaced first with a giant table, and then with
80 // configuration files read from an external source, but for now
81 // they're hard-coded.
82 ////////////////////////////////////////////////////////////////////////
83
84
85 #define createTexture(a) FGTextureManager::createTexture(a)
86
87 /**
88  * Construct an airspeed indicator for a single-engine prop.
89  */
90 static FGPanelInstrument *
91 createAirspeedIndicator (int x, int y)
92 {
93   FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W, SIX_W);
94
95                                 // Layer 0: gauge background.
96   inst->addLayer(0, createTexture("Textures/Panel/airspeed.rgb"));
97
98                                 // Layer 1: needle.
99                                 // Rotates with airspeed.
100   inst->addLayer(1, createTexture("Textures/Panel/long-needle.rgb"));
101   inst->addTransformation(1, FGInstrumentLayer::ROTATION,
102                           FGSteam::get_ASI_kias,
103                           30.0, 220.0, 36.0 / 20.0, -54.0);
104   return inst;
105 }
106
107
108 /**
109  * Construct an artificial horizon.
110  */
111 static FGPanelInstrument *
112 createHorizon (int x, int y)
113 {
114   FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W, SIX_W);
115
116                                 // Layer 0: coloured background
117                                 // moves with roll only
118   inst->addLayer(0, createTexture("Textures/Panel/horizon-bg.rgb"));
119   inst->addTransformation(0, FGInstrumentLayer::ROTATION,
120                           FGBFI::getRoll,
121                           -360.0, 360.0, -1.0, 0.0);
122
123                                 // Layer 1: floating horizon
124                                 // moves with roll and pitch
125   inst->addLayer(1, createTexture("Textures/Panel/horizon-float.rgb"));
126   inst->addTransformation(1, FGInstrumentLayer::ROTATION,
127                           FGBFI::getRoll,
128                           -360.0, 360.0, -1.0, 0.0);
129   inst->addTransformation(1, FGInstrumentLayer::YSHIFT,
130                           FGBFI::getPitch,
131                           -20.0, 20.0, -(1.5 / 160.0) * SIX_W, 0.0);
132
133                                 // Layer 2: rim
134                                 // moves with roll only
135   inst->addLayer(2, createTexture("Textures/Panel/horizon-rim.rgb"));
136   inst->addTransformation(2, FGInstrumentLayer::ROTATION,
137                           FGBFI::getRoll,
138                           -360.0, 360.0, -1.0, 0.0);
139
140                                 // Layer 3: glass front of gauge
141                                 // fixed, with markings
142   inst->addLayer(3, createTexture("Textures/Panel/horizon-fg.rgb"));
143
144   return inst;
145 }
146
147
148 /**
149  * Construct an altimeter.
150  */
151 static FGPanelInstrument *
152 createAltimeter (int x, int y)
153 {
154   FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W, SIX_W);
155
156                                 // Layer 0: gauge background
157   inst->addLayer(0, createTexture("Textures/Panel/altimeter.rgb"));
158
159                                 // Layer 1: hundreds needle (long)
160                                 // moves with altitude
161   inst->addLayer(1, createTexture("Textures/Panel/long-needle.rgb"));
162   inst->addTransformation(1, FGInstrumentLayer::ROTATION,
163                           FGSteam::get_ALT_ft,
164                           0.0, 100000.0, 360.0 / 1000.0, 0.0);
165
166                                 // Layer 2: thousands needle (short)
167                                 // moves with altitude
168   inst->addLayer(2, createTexture("Textures/Panel/short-needle.rgb"));
169   inst->addTransformation(2, FGInstrumentLayer::ROTATION,
170                           FGSteam::get_ALT_ft,
171                           0.0, 100000.0, 360.0 / 10000.0, 0.0);
172
173                                 // Layer 3: ten thousands bug (outside)
174                                 // moves with altitude
175   inst->addLayer(3, createTexture("Textures/Panel/bug.rgb"));
176   inst->addTransformation(3, FGInstrumentLayer::ROTATION,
177                           FGSteam::get_ALT_ft,
178                           0.0, 100000.0, 360.0 / 100000.0, 0.0);
179
180   return inst;
181 }
182
183
184 /**
185  * Construct a turn coordinator.
186  */
187 static FGPanelInstrument *
188 createTurnCoordinator (int x, int y)
189 {
190   FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W, SIX_W);
191
192                                 // Layer 0: background
193   inst->addLayer(0, createTexture("Textures/Panel/turn-bg.rgb"));
194
195                                 // Layer 1: little plane
196                                 // moves with roll
197   inst->addLayer(1, createTexture("Textures/Panel/turn.rgb"));
198   inst->addTransformation(1, FGInstrumentLayer::ROTATION,
199                           FGSteam::get_TC_radps,
200                           -30.0, 30.0, 1.0, 0.0);
201
202                                 // Layer 2: little ball
203                                 // moves with slip/skid
204   inst->addLayer(2, createTexture("Textures/Panel/ball.rgb"));
205   inst->addTransformation(2, FGInstrumentLayer::ROTATION,
206                           FGSteam::get_TC_rad,
207                           -0.1, 0.1, 450.0, 0.0);
208
209   return inst;
210 }
211
212
213 /**
214  * Construct a gyro compass.
215  */
216 static FGPanelInstrument *
217 createGyroCompass (int x, int y)
218 {
219   FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W, SIX_W);
220
221                                 // Action: move bug counter-clockwise
222   inst->addAction(SIX_W/2 - SIX_W/5, -SIX_W/2, SIX_W/10, SIX_W/5,
223                   new FGAdjustAction(FGBFI::getAPHeading,
224                                      FGBFI::setAPHeading,
225                                      -1.0, 0.0, 360.0, true));
226
227                                 // Action: move bug clockwise
228   inst->addAction(SIX_W/2 - SIX_W/10, -SIX_W/2, SIX_W/10, SIX_W/5,
229                   new FGAdjustAction(FGBFI::getAPHeading,
230                                      FGBFI::setAPHeading,
231                                      1.0, 0.0, 360.0, true));
232
233                                 // Layer 0: compass background
234                                 // rotates with heading
235   inst->addLayer(0, createTexture("Textures/Panel/gyro-bg.rgb"));
236   inst->addTransformation(0, FGInstrumentLayer::ROTATION,
237                           FGSteam::get_DG_deg,
238                           -360.0, 360.0, -1.0, 0.0);
239
240                                 // Layer 1: heading bug
241                                 // rotates with heading and AP heading
242   inst->addLayer(1, createTexture("Textures/Panel/bug.rgb"));
243   inst->addTransformation(1, FGInstrumentLayer::ROTATION,
244                           FGSteam::get_DG_deg,
245                           -360.0, 360.0, -1.0, 0.0);
246   inst->addTransformation(1, FGInstrumentLayer::ROTATION,
247                           FGBFI::getAPHeading,
248                           -360.0, 360.0, 1.0, 0.0);
249
250                                 // Layer 2: fixed center
251   inst->addLayer(2, createTexture("Textures/Panel/gyro-fg.rgb"));
252
253                                 // Layer 3: heading knob
254                                 // rotates with AP heading
255   inst->addLayer(3, createTexture("Textures/Panel/heading-knob.rgb"));
256   inst->addTransformation(3, FGInstrumentLayer::XSHIFT, SIX_W/2 - 10); 
257   inst->addTransformation(3, FGInstrumentLayer::YSHIFT, -SIX_W/2 + 10); 
258   inst->addTransformation(3, FGInstrumentLayer::ROTATION,
259                           FGBFI::getAPHeading,
260                           -360.0, 360.0, 1.0, 0.0);
261
262   return inst;
263 }
264
265
266 /**
267  * Construct a vertical velocity indicator.
268  */
269 static FGPanelInstrument *
270 createVerticalVelocity (int x, int y)
271 {
272   FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W, SIX_W);
273
274                                 // Layer 0: gauge background
275   inst->addLayer(0, createTexture("Textures/Panel/vertical.rgb"));
276
277                                 // Layer 1: needle
278                                 // moves with vertical velocity
279   inst->addLayer(1, createTexture("Textures/Panel/long-needle.rgb"));
280   inst->addTransformation(1, FGInstrumentLayer::ROTATION,
281                           FGSteam::get_VSI_fps,
282                           -2000.0, 2000.0, 42.0/500.0, 270.0);
283
284   return inst;
285 }
286
287
288 /**
289  * Construct an RPM gauge.
290  */
291 static FGPanelInstrument *
292 createRPMGauge (int x, int y)
293 {
294   FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SMALL_W, SMALL_W);
295
296                                 // Layer 0: gauge background
297   inst->addLayer(0, createTexture("Textures/Panel/rpm.rgb"));
298
299                                 // Layer 1: long needle
300                                 // FIXME: moves with throttle (for now)
301   inst->addLayer(1, createTexture("Textures/Panel/long-needle.rgb"));
302   inst->addTransformation(1, FGInstrumentLayer::ROTATION,
303                           FGBFI::getThrottle,
304                           0.0, 100.0, 300.0, -150.0);
305
306   return inst;
307 }
308
309
310 /**
311  * Construct a flap position indicator.
312  */
313 static FGPanelInstrument *
314 createFlapIndicator (int x, int y)
315 {
316   FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SMALL_W, SMALL_W);
317
318                                 // Layer 0: gauge background
319   inst->addLayer(0, createTexture("Textures/Panel/flaps.rgb"));
320
321                                 // Layer 1: long needle
322                                 // shifted over, rotates with flap position
323   inst->addLayer(1, createTexture("Textures/Panel/long-needle.rgb"));
324   inst->addTransformation(1, FGInstrumentLayer::XSHIFT,
325                           -(SMALL_W / 4) + (SMALL_W / 16));
326   inst->addTransformation(1, FGInstrumentLayer::ROTATION,
327                           FGBFI::getFlaps,
328                           0.0, 1.0, 120.0, 30.0);
329
330   return inst;
331 }
332
333 static FGPanelInstrument *
334 createChronometer (int x, int y)
335 {
336   FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SMALL_W, SMALL_W);
337
338                                 // Layer 0: gauge background
339   inst->addLayer(0, createTexture("Textures/Panel/clock.rgb"));
340
341                                 // Layer 1: text
342                                 // displays current GMT
343   FGCharInstrumentLayer * text =
344     new FGCharInstrumentLayer(panelGetTime,
345                               SMALL_W, SMALL_W, 1);
346   text->setPointSize(14);
347   text->setColor(0.2, 0.2, 0.2);
348   inst->addLayer(text);
349   inst->addTransformation(1, FGInstrumentLayer::XSHIFT, SMALL_W * -0.38);
350   inst->addTransformation(1, FGInstrumentLayer::YSHIFT, SMALL_W * -0.06);
351
352   return inst;
353 }
354
355
356 /**
357  * Construct control-position indicators.
358  */
359 static FGPanelInstrument *
360 createControls (int x, int y)
361 {
362   FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SMALL_W, SMALL_W);
363
364                                 // Layer 0: gauge background
365   inst->addLayer(0, createTexture("Textures/Panel/controls.rgb"));
366
367                                 // Layer 1: bug
368                                 // moves left-right with aileron
369   inst->addLayer(1, createTexture("Textures/Panel/bug.rgb"));
370   inst->addTransformation(1, FGInstrumentLayer::XSHIFT, FGBFI::getAileron,
371                           -1.0, 1.0, SMALL_W * .75 / 2.0, 0.0);
372
373                                 // Layer 2: bug
374                                 // moves left-right with rudder
375   inst->addLayer(2, createTexture("Textures/Panel/bug.rgb"));
376   inst->addTransformation(2, FGInstrumentLayer::ROTATION, 180.0);
377   inst->addTransformation(2, FGInstrumentLayer::XSHIFT, FGBFI::getRudder,
378                           -1.0, 1.0, -SMALL_W * .75 / 2.0, 0.0);
379
380                                 // Layer 3: bug
381                                 // moves up-down with elevator trim
382   inst->addLayer(3, createTexture("Textures/Panel/bug.rgb"));
383   inst->addTransformation(3, FGInstrumentLayer::ROTATION, 270.0);
384   inst->addTransformation(3, FGInstrumentLayer::YSHIFT,
385                           -SMALL_W * (3.0 / 8.0));
386   inst->addTransformation(3, FGInstrumentLayer::XSHIFT, FGBFI::getElevatorTrim,
387                           -1.0, 1.0, SMALL_W * .75 / 2.0, 0.0);
388
389                                 // Layer 4: bug
390                                 // moves up-down with elevator
391   inst->addLayer(4, createTexture("Textures/Panel/bug.rgb"));
392   inst->addTransformation(4, FGInstrumentLayer::ROTATION, 90.0);
393   inst->addTransformation(4, FGInstrumentLayer::YSHIFT,
394                           -SMALL_W * (3.0 / 8.0));
395   inst->addTransformation(4, FGInstrumentLayer::XSHIFT, FGBFI::getElevator,
396                           -1.0, 1.0, -SMALL_W * .75 / 2.0, 0.0);
397
398   return inst;
399 }
400
401
402 /**
403  * Construct a NAV1 gauge (hardwired).
404  */
405 static FGPanelInstrument *
406 createNAV1 (int x, int y)
407 {
408   FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W, SIX_W);
409
410                                 // Action: increase selected radial
411   inst->addAction(SIX_W/2 - SIX_W/5, -SIX_W/2, SIX_W/10, SIX_W/5,
412                   new FGAdjustAction(FGBFI::getNAV1SelRadial,
413                                      FGBFI::setNAV1SelRadial,
414                                      1.0, 0.0, 360.0, true));
415
416                                 // Action: decrease selected radial
417   inst->addAction(SIX_W/2 - SIX_W/10, -SIX_W/2, SIX_W/10, SIX_W/5,
418                   new FGAdjustAction(FGBFI::getNAV1SelRadial,
419                                      FGBFI::setNAV1SelRadial,
420                                      -1.0, 0.0, 360.0, true));
421
422                                 // Layer 0: background
423   inst->addLayer(0, createTexture("Textures/Panel/gyro-bg.rgb"));
424   inst->addTransformation(0, FGInstrumentLayer::ROTATION,
425                           FGBFI::getNAV1SelRadial,
426                           -360.0, 360.0, -1.0, 0.0);
427
428                                 // Layer 1: left-right needle.
429   inst->addLayer(1, createTexture("Textures/Panel/nav-needle.rgb"));
430   inst->addTransformation(1, FGInstrumentLayer::XSHIFT,
431                           FGSteam::get_HackVOR1_deg,
432                           -10.0, 10.0, SIX_W / 40.0, 0.0);
433
434                                 // Layer 2: glidescope needle
435   inst->addLayer(2, createTexture("Textures/Panel/nav-needle.rgb"));
436   inst->addTransformation(2, FGInstrumentLayer::YSHIFT,
437                           FGSteam::get_HackGS_deg,
438                           -1.0, 1.0, SIX_W / 5.0, 0.0);
439   inst->addTransformation(2, FGInstrumentLayer::ROTATION,
440                           90 );
441
442                                 // Layer 3: face with markings
443   inst->addLayer(3, createTexture("Textures/Panel/nav-face.rgb"));
444
445                                 // Layer 4: heading knob
446                                 // rotates with selected radial
447   inst->addLayer(4, createTexture("Textures/Panel/heading-knob.rgb"));
448   inst->addTransformation(4, FGInstrumentLayer::XSHIFT, SIX_W/2 - 10); 
449   inst->addTransformation(4, FGInstrumentLayer::YSHIFT, -SIX_W/2 + 10); 
450   inst->addTransformation(4, FGInstrumentLayer::ROTATION,
451                           FGBFI::getNAV1SelRadial,
452                           -360.0, 360.0, -1.0, 0.0);
453
454   return inst;
455 }
456
457
458 /**
459  * Construct a NAV2 gauge.
460  */
461 static FGPanelInstrument *
462 createNAV2 (int x, int y)
463 {
464   FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W, SIX_W);
465
466                                 // Action: increase selected radial
467   inst->addAction(SIX_W/2 - SIX_W/5, -SIX_W/2, SIX_W/10, SIX_W/5,
468                   new FGAdjustAction(FGBFI::getNAV2SelRadial,
469                                      FGBFI::setNAV2SelRadial,
470                                      1.0, 0.0, 360.0, true));
471
472                                 // Action: decrease selected radial
473   inst->addAction(SIX_W/2 - SIX_W/10, -SIX_W/2, SIX_W/10, SIX_W/5,
474                   new FGAdjustAction(FGBFI::getNAV2SelRadial,
475                                      FGBFI::setNAV2SelRadial,
476                                      -1.0, 0.0, 360.0, true));
477
478                                 // Layer 0: background
479   inst->addLayer(0, createTexture("Textures/Panel/gyro-bg.rgb"));
480   inst->addTransformation(0, FGInstrumentLayer::ROTATION,
481                           FGBFI::getNAV2SelRadial,
482                           -360.0, 360.0, -1.0, 0.0);
483
484                                 // Layer 1: left-right needle.
485   inst->addLayer(1, createTexture("Textures/Panel/nav-needle.rgb"));
486   inst->addTransformation(1, FGInstrumentLayer::XSHIFT,
487                           FGSteam::get_HackVOR2_deg,
488                           -10.0, 10.0, SIX_W / 40.0, 0.0);
489 //   inst->addTransformation(1, FGInstrumentLayer::YSHIFT,
490 //                        -SIX_W / 4.4 );
491
492                                 // Layer 2: face with markings.
493   inst->addLayer(2, createTexture("Textures/Panel/nav-face.rgb"));
494
495                                 // Layer 3: heading knob
496                                 // rotates with selected radial
497   inst->addLayer(3, createTexture("Textures/Panel/heading-knob.rgb"));
498   inst->addTransformation(3, FGInstrumentLayer::XSHIFT, SIX_W/2 - 10); 
499   inst->addTransformation(3, FGInstrumentLayer::YSHIFT, -SIX_W/2 + 10); 
500   inst->addTransformation(3, FGInstrumentLayer::ROTATION,
501                           FGBFI::getNAV2SelRadial,
502                           -360.0, 360.0, -1.0, 0.0);
503
504   return inst;
505 }
506
507
508 /**
509  * Construct an ADF gauge (hardwired).
510  */
511 static FGPanelInstrument *
512 createADF (int x, int y)
513 {
514   FGLayeredInstrument * inst = new FGLayeredInstrument(x, y, SIX_W, SIX_W);
515
516                                 // Action: increase selected rotation
517   inst->addAction(SIX_W/2 - SIX_W/5, -SIX_W/2, SIX_W/10, SIX_W/5,
518                   new FGAdjustAction(FGBFI::getADFRotation,
519                                      FGBFI::setADFRotation,
520                                      1.0, 0.0, 360.0, true));
521
522                                 // Action: decrease selected rotation
523   inst->addAction(SIX_W/2 - SIX_W/10, -SIX_W/2, SIX_W/10, SIX_W/5,
524                   new FGAdjustAction(FGBFI::getADFRotation,
525                                      FGBFI::setADFRotation,
526                                      -1.0, 0.0, 360.0, true));
527
528                                 // Layer 0: background
529   inst->addLayer(0, createTexture("Textures/Panel/gyro-bg.rgb"));
530   inst->addTransformation(0, FGInstrumentLayer::ROTATION,
531                           FGBFI::getADFRotation,
532                           0.0, 360.0, 1.0, 0.0);
533
534                                 // Layer 1: Direction needle.
535   inst->addLayer(1, createTexture("Textures/Panel/long-needle.rgb"));
536   inst->addTransformation(1, FGInstrumentLayer::ROTATION,
537                           FGSteam::get_HackADF_deg,
538                           -720.0, 720.0, 1.0, 0.0);
539
540                                 // Layer 2: heading knob
541                                 // rotates with selected radial
542   inst->addLayer(2, createTexture("Textures/Panel/heading-knob.rgb"));
543   inst->addTransformation(2, FGInstrumentLayer::XSHIFT, SIX_W/2 - 10); 
544   inst->addTransformation(2, FGInstrumentLayer::YSHIFT, -SIX_W/2 + 10); 
545   inst->addTransformation(2, FGInstrumentLayer::ROTATION,
546                           FGBFI::getADFRotation,
547                           -360.0, 360.0, -1.0, 0.0);
548   return inst;
549 }
550
551
552 \f
553 ////////////////////////////////////////////////////////////////////////
554 // Implementation of FGTextureManager.
555 ////////////////////////////////////////////////////////////////////////
556
557 map<const char *,ssgTexture *> FGTextureManager::_textureMap;
558
559 ssgTexture *
560 FGTextureManager::createTexture (const char * relativePath)
561 {
562   ssgTexture *texture;
563
564   texture = _textureMap[relativePath];
565   if (texture == 0) {
566     FGPath tpath(current_options.get_fg_root());
567     tpath.append(relativePath);
568     texture = new ssgTexture((char *)tpath.c_str(), false, false);
569     _textureMap[relativePath] = texture;
570     cerr << "Created texture " << relativePath
571          << " handle=" << texture->getHandle() << endl;
572   }
573
574   return texture;
575 }
576
577
578 \f
579 ////////////////////////////////////////////////////////////////////////
580 // Implementation of FGPanel.
581 ////////////////////////////////////////////////////////////////////////
582
583 FGPanel current_panel;
584
585 FGPanel::FGPanel ()
586   : _initialized(false),
587     _visibility(false)
588 {
589 }
590
591 FGPanel::~FGPanel ()
592 {
593   instrument_list_type::iterator current = _instruments.begin();
594   instrument_list_type::iterator last = _instruments.end();
595   
596   for ( ; current != last; ++current) {
597     delete *current;
598     *current = 0;
599   }
600 }
601
602 void
603 FGPanel::addInstrument (FGPanelInstrument * instrument)
604 {
605   _instruments.push_back(instrument);
606 }
607
608 void
609 FGPanel::init (int x, int y, int finx, int finy)
610 {
611   _x = x;
612   _y = y;
613   _w = finx - x;
614   _h = finy - y;
615   _panel_h = (int)((finy - y) * 0.5768 + 1);
616
617                                 // Don't reconstruct all of the
618                                 // instruments.
619   if (_initialized)
620     return;
621
622   x = SIX_X;
623   y = SIX_Y;
624
625   _bg = createTexture("Textures/Panel/panel-bg.rgb");
626
627                                 // Chronometer alone at side
628   x = SIX_X - SIX_SPACING - 8;
629   addInstrument(createChronometer(x, y));
630
631                                 // Top row
632   x = SIX_X;
633   addInstrument(createAirspeedIndicator(x, y));
634   x += SIX_SPACING;
635   addInstrument(createHorizon(x, y));
636   x += SIX_SPACING;
637   addInstrument(createAltimeter(x, y));
638   x += SIX_SPACING + 20;
639   addInstrument(createNAV1(x, y));
640
641                                 // Middle row
642   x = SIX_X;
643   y -= SIX_SPACING;
644   addInstrument(createTurnCoordinator(x, y));
645   x += SIX_SPACING;
646   addInstrument(createGyroCompass(x, y));
647   x += SIX_SPACING;
648   addInstrument(createVerticalVelocity(x, y));
649   x += SIX_SPACING + 20;
650   addInstrument(createNAV2(x, y));
651
652                                 // Bottom row
653   x = SIX_X;
654   y -= SIX_SPACING + 10;
655   addInstrument(createControls(x, y));
656   x += SIX_SPACING;
657   addInstrument(createFlapIndicator(x, y));
658   x += SIX_SPACING;
659   addInstrument(createRPMGauge(x, y));
660   x += SIX_SPACING + 20;
661   y += 10;
662   addInstrument(createADF(x, y));
663 }
664
665 void
666 FGPanel::update () const
667 {
668                                 // Do nothing if the panel isn't visible.
669   if (!_visibility)
670     return;
671
672   glMatrixMode(GL_PROJECTION);
673   glPushMatrix();
674   glLoadIdentity();
675   gluOrtho2D(_x, _x + _w, _y, _y + _h);
676
677   glMatrixMode(GL_MODELVIEW);
678   glPushMatrix();
679   glLoadIdentity();
680
681                                 // Draw the background
682   glEnable(GL_TEXTURE_2D);
683   glDisable(GL_LIGHTING);
684   glEnable(GL_BLEND);
685   glEnable(GL_ALPHA_TEST);
686   glEnable(GL_COLOR_MATERIAL);
687   glColor4f(1.0, 1.0, 1.0, 1.0);
688   glBindTexture(GL_TEXTURE_2D, _bg->getHandle());
689   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
690   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
691   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
692   glBegin(GL_POLYGON);
693   glTexCoord2f(0.0, 0.0); glVertex3f(_x, _y, 0);
694   glTexCoord2f(10.0, 0.0); glVertex3f(_x + _w, _y, 0);
695   glTexCoord2f(10.0, 5.0); glVertex3f(_x + _w, _y + _panel_h, 0);
696   glTexCoord2f(0.0, 5.0); glVertex3f(_x, _y + _panel_h, 0);
697   glEnd();
698
699                                 // Draw the instruments.
700   instrument_list_type::const_iterator current = _instruments.begin();
701   instrument_list_type::const_iterator end = _instruments.end();
702
703   for ( ; current != end; current++) {
704     FGPanelInstrument * instr = *current;
705     glLoadIdentity();
706     glTranslated(instr->getXPos(), instr->getYPos(), 0);
707     instr->draw();
708   }
709
710   glMatrixMode(GL_PROJECTION);
711   glPopMatrix();
712   glMatrixMode(GL_MODELVIEW);
713   glPopMatrix();
714   ssgForceBasicState();
715   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
716 }
717
718 void
719 FGPanel::setVisibility (bool visibility)
720 {
721   _visibility = visibility;
722 }
723
724 bool
725 FGPanel::getVisibility () const
726 {
727   return _visibility;
728 }
729
730 bool
731 FGPanel::doMouseAction (int button, int updown, int x, int y)
732 {
733                                 // For now, ignore the release
734   if (updown == 1) 
735     return true;
736
737   x = (int)(((float)x / current_view.get_winWidth()) * _w);
738   y = (int)(_h - (((float)y / current_view.get_winHeight()) * _h));
739
740   for (int i = 0; i < _instruments.size(); i++) {
741     FGPanelInstrument *inst = _instruments[i];
742     int ix = inst->getXPos();
743     int iy = inst->getYPos();
744     int iw = inst->getWidth() / 2;
745     int ih = inst->getHeight() / 2;
746     if (x >= ix - iw && x < ix + iw && y >= iy - ih && y < iy + ih) {
747       cout << "Do mouse action for component " << i << '\n';
748       return inst->doMouseAction(button, updown, x - ix, y - iy);
749     }
750   }
751   cout << "Did not click on an instrument\n";
752   return false;
753 }
754
755
756 \f
757 ////////////////////////////////////////////////////////////////////////
758 // Implementation of FGAdjustAction.
759 ////////////////////////////////////////////////////////////////////////
760
761 FGAdjustAction::FGAdjustAction (getter_type getter, setter_type setter,
762                                 double increment, double min, double max,
763                                 bool wrap=false)
764   : _getter(getter), _setter(setter), _increment(increment),
765     _min(min), _max(max), _wrap(wrap)
766 {
767 }
768
769 FGAdjustAction::~FGAdjustAction ()
770 {
771 }
772
773 void
774 FGAdjustAction::doAction ()
775 {
776   double value = (*_getter)();
777   cout << "Do action; value=" << value << '\n';
778   value += _increment;
779   if (value < _min) {
780     value = (_wrap ? _max : _min);
781   } else if (value > _max) {
782     value = (_wrap ? _min : _max);
783   }
784   cout << "New value is " << value << '\n';
785   (*_setter)(value);
786 }
787
788
789 \f
790 ////////////////////////////////////////////////////////////////////////
791 // Implementation of FGPanelInstrument.
792 ////////////////////////////////////////////////////////////////////////
793
794
795 FGPanelInstrument::FGPanelInstrument ()
796 {
797   setPosition(0, 0);
798   setSize(0, 0);
799 }
800
801 FGPanelInstrument::FGPanelInstrument (int x, int y, int w, int h)
802 {
803   setPosition(x, y);
804   setSize(w, h);
805 }
806
807 FGPanelInstrument::~FGPanelInstrument ()
808 {
809   action_list_type::iterator it = _actions.begin();
810   action_list_type::iterator last = _actions.end();
811   for ( ; it != last; it++) {
812     delete it->action;
813   }
814 }
815
816 void
817 FGPanelInstrument::setPosition (int x, int y)
818 {
819   _x = x;
820   _y = y;
821 }
822
823 void
824 FGPanelInstrument::setSize (int w, int h)
825 {
826   _w = w;
827   _h = h;
828 }
829
830 int
831 FGPanelInstrument::getXPos () const
832 {
833   return _x;
834 }
835
836 int
837 FGPanelInstrument::getYPos () const
838 {
839   return _y;
840 }
841
842 int
843 FGPanelInstrument::getWidth () const
844 {
845   return _w;
846 }
847
848 int
849 FGPanelInstrument::getHeight () const
850 {
851   return _h;
852 }
853
854 void
855 FGPanelInstrument::addAction (int x, int y, int w, int h,
856                               FGPanelAction * action)
857 {
858   FGPanelInstrument::inst_action act;
859   act.x = x;
860   act.y = y;
861   act.w = w;
862   act.h = h;
863   act.action = action;
864   _actions.push_back(act);
865 }
866
867                                 // Coordinates relative to centre.
868 bool
869 FGPanelInstrument::doMouseAction (int button, int updown, int x, int y)
870 {
871   action_list_type::iterator it = _actions.begin();
872   action_list_type::iterator last = _actions.end();
873   cout << "Mouse action at " << x << ',' << y << '\n';
874   for ( ; it != last; it++) {
875     cout << "Trying action at " << it->x << ',' << it->y << ','
876          << it->w <<',' << it->h << '\n';
877     if (x >= it->x && x < it->x + it->w && y >= it->y && y < it->y + it->h) {
878       it->action->doAction();
879       return true;
880     }
881   }
882   return false;
883 }
884
885
886 \f
887 ////////////////////////////////////////////////////////////////////////
888 // Implementation of FGLayeredInstrument.
889 ////////////////////////////////////////////////////////////////////////
890
891 FGLayeredInstrument::FGLayeredInstrument (int x, int y, int w, int h)
892   : FGPanelInstrument(x, y, w, h)
893 {
894 }
895
896 FGLayeredInstrument::~FGLayeredInstrument ()
897 {
898   // FIXME: free layers
899 }
900
901 void
902 FGLayeredInstrument::draw () const
903 {
904   layer_list::const_iterator it = _layers.begin();
905   layer_list::const_iterator last = _layers.end();
906   while (it != last) {
907     (*it)->draw();
908     it++;
909   }
910 }
911
912 void
913 FGLayeredInstrument::addLayer (FGInstrumentLayer *layer)
914 {
915   _layers.push_back(layer);
916 }
917
918 void
919 FGLayeredInstrument::addLayer (int layer, ssgTexture * texture)
920 {
921   addLayer(new FGTexturedInstrumentLayer(texture, _w, _h, layer));
922 }
923
924 void
925 FGLayeredInstrument::addTransformation (int layer,
926                                         FGInstrumentLayer::transform_type type,
927                                         FGInstrumentLayer::transform_func func,
928                                         double min, double max,
929                                         double factor, double offset)
930 {
931   _layers[layer]->addTransformation(type, func, min, max, factor, offset);
932 }
933
934
935 \f
936 ////////////////////////////////////////////////////////////////////////
937 // Implementation of FGInstrumentLayer.
938 ////////////////////////////////////////////////////////////////////////
939
940 FGInstrumentLayer::FGInstrumentLayer (int w, int h, int z)
941   : _w(w),
942     _h(h),
943     _z(z)
944 {
945 }
946
947 FGInstrumentLayer::~FGInstrumentLayer ()
948 {
949   transformation_list::iterator it = _transformations.begin();
950   transformation_list::iterator end = _transformations.end();
951   while (it != end) {
952     delete *it;
953     it++;
954   }
955 }
956
957 void
958 FGInstrumentLayer::transform () const
959 {
960   glTranslatef(0.0, 0.0, (_z / 100.0) + 0.1);
961
962   transformation_list::const_iterator it = _transformations.begin();
963   transformation_list::const_iterator last = _transformations.end();
964   while (it != last) {
965     transformation *t = *it;
966     double value = (t->func == 0 ? 0.0 : (*(t->func))());
967     if (value < t->min) {
968       value = t->min;
969     } else if (value > t->max) {
970       value = t->max;
971     }
972     value = value * t->factor + t->offset;
973
974     switch (t->type) {
975     case XSHIFT:
976       glTranslatef(value, 0.0, 0.0);
977       break;
978     case YSHIFT:
979       glTranslatef(0.0, value, 0.0);
980       break;
981     case ROTATION:
982       glRotatef(-value, 0.0, 0.0, 1.0);
983       break;
984     }
985     it++;
986   }
987 }
988
989 void
990 FGInstrumentLayer::addTransformation (transform_type type,
991                                       transform_func func,
992                                       double min, double max,
993                                       double factor, double offset)
994 {
995   transformation *t = new transformation;
996   t->type = type;
997   t->func = func;
998   t->min = min;
999   t->max = max;
1000   t->factor = factor;
1001   t->offset = offset;
1002   _transformations.push_back(t);
1003 }
1004
1005
1006 \f
1007 ////////////////////////////////////////////////////////////////////////
1008 // Implementation of FGTexturedInstrumentLayer.
1009 ////////////////////////////////////////////////////////////////////////
1010
1011 // FGTexturedInstrumentLayer::FGTexturedInstrumentLayer (const char *tname,
1012 //                                                    int w, int h, int z)
1013 //   : FGInstrumentLayer(w, h, z)
1014 // {
1015 //   setTexture(tname);
1016 // }
1017
1018 FGTexturedInstrumentLayer::FGTexturedInstrumentLayer (ssgTexture * texture,
1019                                                       int w, int h, int z)
1020   : FGInstrumentLayer(w, h, z)
1021 {
1022   setTexture(texture);
1023 }
1024
1025 FGTexturedInstrumentLayer::~FGTexturedInstrumentLayer ()
1026 {
1027 }
1028
1029 void
1030 FGTexturedInstrumentLayer::draw () const
1031 {
1032   int w2 = _w / 2;
1033   int h2 = _h / 2;
1034
1035   glPushMatrix();
1036   transform();
1037   glBindTexture(GL_TEXTURE_2D, _texture->getHandle());
1038   glBegin(GL_POLYGON);
1039                                 // FIXME: is this really correct
1040                                 // for layering?
1041   glTexCoord2f(0.0, 0.0); glVertex2f(-w2, -h2);
1042   glTexCoord2f(1.0, 0.0); glVertex2f(w2, -h2);
1043   glTexCoord2f(1.0, 1.0); glVertex2f(w2, h2);
1044   glTexCoord2f(0.0, 1.0); glVertex2f(-w2, h2);
1045   glEnd();
1046   glPopMatrix();
1047 }
1048
1049 // void
1050 // FGTexturedInstrumentLayer::setTexture (const char *textureName)
1051 // {
1052 //   FGPath tpath(current_options.get_fg_root());
1053 //   tpath.append(textureName);
1054 //   ssgTexture * texture = new ssgTexture((char *)tpath.c_str(), false, false);
1055 //   setTexture(texture);
1056 // }
1057
1058
1059 \f
1060 ////////////////////////////////////////////////////////////////////////
1061 // Implementation of FGCharInstrumentLayer.
1062 ////////////////////////////////////////////////////////////////////////
1063
1064 FGCharInstrumentLayer::FGCharInstrumentLayer (text_func func,
1065                                               int w, int h, int z)
1066   : FGInstrumentLayer(w, h, z),
1067     _func(func)
1068 {
1069   _renderer.setFont(guiFntHandle);
1070   _renderer.setPointSize(14);
1071   _color[0] = _color[1] = _color[2] = 0.0;
1072   _color[3] = 1.0;
1073 }
1074
1075 FGCharInstrumentLayer::~FGCharInstrumentLayer ()
1076 {
1077 }
1078
1079 void
1080 FGCharInstrumentLayer::draw () const
1081 {
1082   glPushMatrix();
1083   glColor4fv(_color);
1084   transform();
1085   _renderer.begin();
1086   _renderer.start3f(0, 0, 0);
1087   _renderer.puts((*_func)(_buf));
1088   _renderer.end();
1089   glColor4f(1.0, 1.0, 1.0, 1.0);        // FIXME
1090   glPopMatrix();
1091 }
1092
1093 void
1094 FGCharInstrumentLayer::setColor (float r, float g, float b)
1095 {
1096   _color[0] = r;
1097   _color[1] = g;
1098   _color[2] = b;
1099   _color[3] = 1.0;
1100 }
1101
1102 void
1103 FGCharInstrumentLayer::setPointSize (const float size)
1104 {
1105   _renderer.setPointSize(size);
1106 }
1107
1108 void
1109 FGCharInstrumentLayer::setFont(fntFont * font)
1110 {
1111   _renderer.setFont(font);
1112 }
1113
1114
1115 \f
1116 // end of panel.cxx