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