]> git.mxchange.org Git - flightgear.git/blob - src/Cockpit/hud.cxx
remove readLadder(const SGPropertyNode *) and make the HudLadder
[flightgear.git] / src / Cockpit / hud.cxx
1 // hud.cxx -- hud defines and prototypes
2 //
3 // Written by Michele America, started September 1997.
4 //
5 // Copyright (C) 1997  Michele F. America  - micheleamerica@geocities.com
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23 #include <simgear/compiler.h>
24 #include <simgear/structure/exception.hxx>
25
26 #include STL_STRING
27 #include STL_FSTREAM
28
29 #ifdef HAVE_CONFIG_H
30 #  include <config.h>
31 #endif
32
33 #ifdef HAVE_WINDOWS_H
34 #  include <windows.h>
35 #endif
36
37 #ifdef __BORLANDC__
38 #  define exception c_exception
39 #endif
40
41 #include <math.h>
42
43 #include <stdlib.h>
44 #include <stdio.h>              // char related functions
45 #include <string.h>             // strcmp()
46
47 #include SG_GLU_H
48
49 #include <simgear/constants.h>
50 #include <simgear/debug/logstream.hxx>
51 #include <simgear/props/props.hxx>
52 #include <simgear/misc/sg_path.hxx>
53
54 #include <Aircraft/aircraft.hxx>
55 #include <Autopilot/xmlauto.hxx>
56 #include <GUI/new_gui.hxx>
57 #include <Main/globals.hxx>
58 #include <Main/fg_props.hxx>
59 #include <Scenery/scenery.hxx>
60
61 #include "hud.hxx"
62
63
64 static HUD_Properties *HUD = 0;
65
66 static char units[5];
67
68 // The following routines obtain information concerning the aircraft's
69 // current state and return it to calling instrument display routines.
70 // They should eventually be member functions of the aircraft.
71 //
72
73 deque< instr_item * > HUD_deque;
74
75 fgTextList         HUD_TextList;
76 fgLineList         HUD_LineList;
77 fgLineList         HUD_StippleLineList;
78
79 fntRenderer *HUDtext = 0;
80 fntTexFont *HUD_Font = 0;
81 float HUD_TextSize = 0;
82 int HUD_style = 0;
83
84 float HUD_matrix[16];
85
86
87 //$$$ begin - added, Neetha, 28 Nov 2k
88
89 static string  name;
90 static int     x;
91 static int     y;
92 static UINT    width;
93 static UINT    height;
94 static float   span_units;
95 static bool    working;
96 static string  loadfn;
97 static UINT    options;
98 static float   maxValue;
99 static float   minValue;
100 static float   scaling;
101 static UINT    major_divs;
102 static UINT    minor_divs;
103 static UINT    modulator;
104 static int     dp_showing = 0;
105 static string  label_format;
106 static string  prelabel;
107 static string  postlabel;
108 static int     justi;
109 static int     blinking;
110 static float   maxBankAngle;
111 static float   maxSlipAngle;
112 static UINT    gap_width;
113 static bool    latitude;
114 static bool    longitude;
115 static bool    tick_bottom;
116 static bool    tick_top;
117 static bool    tick_right;
118 static bool    tick_left;
119 static bool    cap_bottom;
120 static bool    cap_top;
121 static bool    cap_right;
122 static bool    cap_left;
123 static float   marker_off;
124 static string  type;
125 static bool    enable_pointer;
126 static string  type_pointer;
127 static string  type_tick;
128 static string  length_tick;
129 static bool    label_box;
130 static int     digits;
131 static float   radius;
132 static int     divisions;
133 static int     zoom;
134 static bool    tsi;
135 static float   rad;
136
137
138 static FLTFNPTR load_fn;
139 static fgLabelJust justification;
140 static const char *pre_label_string  = 0;
141 static const char *post_label_string = 0;
142
143 int readHud( istream &input );
144 int readInstrument ( const SGPropertyNode * node);
145 static instr_item * readCard ( const SGPropertyNode * node);
146 static instr_item * readLabel( const SGPropertyNode * node);
147 static instr_item * readTBI( const SGPropertyNode * node);
148 //$$$ end   - added, Neetha, 28 Nov 2k
149
150 static void drawHUD();
151 static void fgUpdateHUDVirtual();
152
153 class locRECT {
154 public:
155     RECT rect;
156
157     locRECT( UINT left, UINT top, UINT right, UINT bottom);
158     RECT get_rect(void) { return rect; }
159 };
160
161 locRECT :: locRECT( UINT left, UINT top, UINT right, UINT bottom)
162 {
163     rect.left   =  left;
164     rect.top    =  top;
165     rect.right  =  right;
166     rect.bottom =  bottom;
167
168 }
169 // #define DEBUG
170
171 //========================= End of Class Implementations===================
172 // fgHUDInit
173 //
174 // Constructs a HUD object and then adds in instruments. At the present
175 // the instruments are hard coded into the routine. Ultimately these need
176 // to be defined by the aircraft's instrumentation records so that the
177 // display for a Piper Cub doesn't show the speed range of a North American
178 // mustange and the engine readouts of a B36!
179 //
180
181 static instr_item *
182 readCard(const SGPropertyNode * node)
183 {
184
185     instr_item *p;
186
187     name               = node->getStringValue("name");
188     x                  = node->getIntValue("x");
189     y                  = node->getIntValue("y");
190     width              = node->getIntValue("width");
191     height             = node->getIntValue("height");
192     loadfn             = node->getStringValue("loadfn");
193     options            = node->getIntValue("options");
194     maxValue           = node->getFloatValue("maxValue");
195     minValue           = node->getFloatValue("minValue");
196     scaling            = node->getFloatValue("disp_scaling");
197     major_divs         = node->getIntValue("major_divs");
198     minor_divs         = node->getIntValue("minor_divs");
199     modulator          = node->getIntValue("modulator");
200     span_units         = node->getFloatValue("value_span");
201     type               = node->getStringValue("type");
202     tick_bottom        = node->getBoolValue("tick_bottom",false);
203     tick_top           = node->getBoolValue("tick_top",false);
204     tick_right         = node->getBoolValue("tick_right",false);
205     tick_left          = node->getBoolValue("tick_left",false);
206     cap_bottom         = node->getBoolValue("cap_bottom",false);
207     cap_top            = node->getBoolValue("cap_top",false);
208     cap_right          = node->getBoolValue("cap_right",false);
209     cap_left           = node->getBoolValue("cap_left",false);
210     marker_off         = node->getFloatValue("marker_offset",0.0);
211     enable_pointer     = node->getBoolValue("enable_pointer",true);
212     type_pointer       = node->getStringValue("pointer_type");
213     type_tick          = node->getStringValue("tick_type"); // 'circle' or 'line'
214     length_tick        = node->getStringValue("tick_length"); // for variable length
215     working            = node->getBoolValue("working");
216     radius             = node->getFloatValue("radius");
217     divisions          = node->getIntValue("divisions");
218     zoom               = node->getIntValue("zoom");
219
220     SG_LOG(SG_INPUT, SG_INFO, "Done reading instrument " << name);
221
222
223     if (type=="gauge") {
224         span_units = maxValue - minValue;
225     }
226
227     if (loadfn=="anzg") {
228         load_fn = get_anzg;
229     } else if (loadfn=="heading") {
230         load_fn = get_heading;
231     } else if (loadfn=="aoa") {
232         load_fn = get_aoa;
233     } else if (loadfn=="climb") {
234         load_fn = get_climb_rate;
235     } else if (loadfn=="altitude") {
236         load_fn = get_altitude;
237     } else if (loadfn=="agl") {
238         load_fn = get_agl;
239     } else if (loadfn=="speed") {
240         load_fn = get_speed;
241     } else if (loadfn=="view_direction") {
242         load_fn = get_view_direction;
243     } else if (loadfn=="aileronval") {
244         load_fn = get_aileronval;
245     } else if (loadfn=="elevatorval") {
246         load_fn = get_elevatorval;
247     } else if (loadfn=="elevatortrimval") {
248         load_fn = get_elev_trimval;
249     } else if (loadfn=="rudderval") {
250         load_fn = get_rudderval;
251     } else if (loadfn=="throttleval") {
252         load_fn = get_throttleval;
253     }
254
255
256     if ( (type == "dial") | (type == "tape") ) {
257         p = (instr_item *) new hud_card( x,
258                                          y,
259                                          width,
260                                          height,
261                                          load_fn,
262                                          options,
263                                          maxValue, minValue,
264                                          scaling,
265                                          major_divs, minor_divs,
266                                          modulator,
267                                          dp_showing,
268                                          span_units,
269                                          type,
270                                          tick_bottom,
271                                          tick_top,
272                                          tick_right,
273                                          tick_left,
274                                          cap_bottom,
275                                          cap_top,
276                                          cap_right,
277                                          cap_left,
278                                          marker_off,
279                                          enable_pointer,
280                                          type_pointer,
281                                          type_tick,
282                                          length_tick,
283                                          working,
284                                          radius,
285                                          divisions,
286                                          zoom
287                                          );
288     } else {
289         p = (instr_item *) new  gauge_instr( x,       // x
290                                              y,       // y
291                                              width,   // width
292                                              height,  // height
293                                              load_fn, // data source
294                                              options,
295                                              scaling,
296                                              maxValue,minValue,
297                                              major_divs, minor_divs,
298                                              dp_showing,
299                                              modulator,
300                                              working);
301     }
302
303     return p;
304 }// end readCard
305
306 static instr_item *
307 readLabel(const SGPropertyNode * node)
308 {
309     instr_item *p;
310
311     int font_size = (fgGetInt("/sim/startup/xsize") > 1000) ? HUD_FONT_LARGE : HUD_FONT_SMALL;
312
313     name               = node->getStringValue("name");
314     x                  = node->getIntValue("x");
315     y                  = node->getIntValue("y");
316     width              = node->getIntValue("width");
317     height             = node->getIntValue("height");
318     loadfn             = node->getStringValue("data_source");
319     label_format       = node->getStringValue("label_format");
320     prelabel           = node->getStringValue("pre_label_string");
321     postlabel          = node->getStringValue("post_label_string");
322     scaling            = node->getFloatValue("scale_data");
323     options            = node->getIntValue("options");
324     justi              = node->getIntValue("justification");
325     blinking           = node->getIntValue("blinking");
326     latitude           = node->getBoolValue("latitude",false);
327     longitude          = node->getBoolValue("longitude",false);
328     label_box          = node->getBoolValue("label_box",false);
329     working            = node->getBoolValue("working");
330     digits             = node->getIntValue("digits");
331
332
333     SG_LOG(SG_INPUT, SG_INFO, "Done reading instrument " << name);
334
335
336     if ( justi == 0 ) {
337         justification = LEFT_JUST;
338     } else {
339         if ( justi == 1 ) {
340             justification = CENTER_JUST;
341         } else {
342             if ( justi == 2 ) {
343                 justification = RIGHT_JUST;
344             }
345         }
346     }
347
348     if ( prelabel == "NULL" ) {
349         pre_label_string = NULL;
350     } else {
351         if ( prelabel == "blank" ) {
352             pre_label_string = " ";
353         } else {
354             pre_label_string = prelabel.c_str();
355         }
356     }
357
358     if ( postlabel == "blank" ) {
359         post_label_string = " ";
360     } else {
361         if ( postlabel == "NULL" ) {
362             post_label_string = NULL;
363         } else {
364             if ( postlabel == "units" ) {
365                 post_label_string = units;
366             } else {
367                 post_label_string = postlabel.c_str();
368             }
369         }
370     }
371
372 #ifdef ENABLE_SP_FMDS
373     if ( loadfn== "aux1" ) {
374         load_fn = get_aux1;
375     } else if ( loadfn == "aux2" ) {
376         load_fn = get_aux2;
377     } else if ( loadfn == "aux3" ) {
378         load_fn = get_aux3;
379     } else if ( loadfn == "aux4" ) {
380         load_fn = get_aux4;
381     } else if ( loadfn == "aux5" ) {
382         load_fn = get_aux5;
383     } else if ( loadfn == "aux6" ) {
384         load_fn = get_aux6;
385     } else if ( loadfn == "aux7" ) {
386         load_fn = get_aux7;
387     } else if ( loadfn == "aux8" ) {
388         load_fn = get_aux8;
389     } else if ( loadfn == "aux9" ) {
390         load_fn = get_aux9;
391     } else if ( loadfn == "aux10" ) {
392         load_fn = get_aux10;
393     } else if ( loadfn == "aux11" ) {
394         load_fn = get_aux11;
395     } else if ( loadfn == "aux12" ) {
396         load_fn = get_aux12;
397     } else if ( loadfn == "aux13" ) {
398         load_fn = get_aux13;
399     } else if ( loadfn == "aux14" ) {
400         load_fn = get_aux14;
401     } else if ( loadfn == "aux15" ) {
402         load_fn = get_aux15;
403     } else if ( loadfn == "aux16" ) {
404         load_fn = get_aux16;
405     } else if ( loadfn == "aux17" ) {
406         load_fn = get_aux17;
407     } else if ( loadfn == "aux18" ) {
408         load_fn = get_aux18;
409     } else
410 #endif
411     if ( loadfn == "ax" ) {
412         load_fn = get_Ax;
413     } else if ( loadfn == "speed" ) {
414         load_fn = get_speed;
415     } else if ( loadfn == "mach" ) {
416         load_fn = get_mach;
417     } else if ( loadfn == "altitude" ) {
418         load_fn = get_altitude;
419     } else if ( loadfn == "agl" ) {
420         load_fn = get_agl;
421     } else if ( loadfn == "framerate" ) {
422         load_fn = get_frame_rate;
423     } else if ( loadfn == "heading" ) {
424         load_fn = get_heading;
425     } else if ( loadfn == "fov" ) {
426         load_fn = get_fov;
427     } else if ( loadfn == "vfc_tris_culled" ) {
428         load_fn = get_vfc_tris_culled;
429     } else if ( loadfn == "vfc_tris_drawn" ) {
430         load_fn = get_vfc_tris_drawn;
431     } else if ( loadfn == "aoa" ) {
432         load_fn = get_aoa;
433     } else if ( loadfn == "latitude" ) {
434         load_fn  = get_latitude;
435     } else if ( loadfn == "anzg" ) {
436         load_fn = get_anzg;
437     } else if ( loadfn == "longitude" ) {
438         load_fn   = get_longitude;
439     } else if (loadfn=="throttleval") {
440         load_fn = get_throttleval;
441     }
442
443     p = (instr_item *) new instr_label ( x,
444                                          y,
445                                          width,
446                                          height,
447                                          load_fn,
448                                          label_format.c_str(),
449                                          pre_label_string,
450                                          post_label_string,
451                                          scaling,
452                                          options,
453                                          justification,
454                                          font_size,
455                                          blinking,
456                                          latitude,
457                                          longitude,
458                                          label_box,
459                                          working,
460                                          digits);
461
462     return p;
463 } // end readLabel
464
465 static instr_item *
466 readTBI(const SGPropertyNode * node)
467 {
468
469     instr_item *p;
470
471     name           = node->getStringValue("name");
472     x              = node->getIntValue("x");
473     y              = node->getIntValue("y");
474     width          = node->getIntValue("width");
475     height         = node->getIntValue("height");
476     maxBankAngle   = node->getFloatValue("maxBankAngle");
477     maxSlipAngle   = node->getFloatValue("maxSlipAngle");
478     gap_width      = node->getIntValue("gap_width");
479     working        = node->getBoolValue("working");
480     tsi            = node->getBoolValue("tsi");
481     rad            = node->getFloatValue("rad");
482
483     SG_LOG(SG_INPUT, SG_INFO, "Done reading instrument " << name);
484
485
486     p = (instr_item *) new fgTBI_instr( x,
487                                         y,
488                                         width,
489                                         height,
490                                         get_roll,
491                                         get_sideslip,
492                                         maxBankAngle,
493                                         maxSlipAngle,
494                                         gap_width,
495                                         working,
496                                         tsi,
497                                         rad);
498
499     return p;
500 } //end readTBI
501
502 static instr_item *
503 readRunway(const SGPropertyNode * node) {
504         name     = node->getStringValue("name");
505         x        = node->getIntValue("x");
506         y        = node->getIntValue("y");
507         width    = node->getIntValue("width");
508         height   = node->getIntValue("height");
509         scaling  = node->getDoubleValue("scale");
510         working  = node->getBoolValue("working",true);
511         runway_instr *ri = new runway_instr(x,y,width,height,scaling,working);
512         double scale = node->getDoubleValue("arrow_scale",1.0);
513         ri->setDrawArrow((scale>0)?true:false);
514         ri->setDrawArrowAlways((scale>0)?node->getBoolValue("arrow_always"):false);
515         ri->setStippleOutline(node->getIntValue("outer_stipple",0xFFFF));
516         ri->setStippleCenterline(node->getIntValue("center_stipple",0xFFFF));
517         ri->setArrowRotationRadius(node->getDoubleValue("arrow_radius"));
518         ri->setArrowScale(scale);
519         ri->setLineScale(node->getDoubleValue("line_scale",1.0));
520         ri->setScaleDist(node->getDoubleValue("scale_dist_nm"));
521         SG_LOG(SG_INPUT, SG_INFO, "Done reading instrument " << name);
522         return (instr_item *) ri;
523 }
524
525
526 int readInstrument(const SGPropertyNode * node)
527 {
528     static const SGPropertyNode *startup_units_node
529         = fgGetNode("/sim/startup/units");
530
531     instr_item *HIptr;
532
533     if ( !strcmp(startup_units_node->getStringValue(), "feet") ) {
534         strcpy(units, " ft");
535     } else {
536         strcpy(units, " m");
537     }
538
539     const SGPropertyNode * ladder_group = node->getNode("ladders");
540
541     if (ladder_group != 0) {
542         int nLadders = ladder_group->nChildren();
543         for (int j = 0; j < nLadders; j++) {
544
545             HIptr = static_cast<instr_item *>(new HudLadder(ladder_group->getChild(j)));
546             HUD_deque.insert( HUD_deque.begin(), HIptr);
547
548         }// for - ladders
549     }
550
551     const SGPropertyNode * card_group = node->getNode("cards");
552     if (card_group != 0) {
553         int nCards = card_group->nChildren();
554         for (int j = 0; j < nCards; j++) {
555
556             HIptr = readCard(card_group->getChild(j));
557             HUD_deque.insert( HUD_deque.begin(), HIptr);
558
559         }//for - cards
560     }
561
562     const SGPropertyNode * label_group = node->getNode("labels");
563     if (label_group != 0) {
564         int nLabels = label_group->nChildren();
565         for (int j = 0; j < nLabels; j++) {
566
567             HIptr = readLabel(label_group->getChild(j));
568             HUD_deque.insert( HUD_deque.begin(), HIptr);
569
570         }//for - labels
571     }
572
573     const SGPropertyNode * tbi_group = node->getNode("tbis");
574     if (tbi_group != 0) {
575         int nTbis = tbi_group->nChildren();
576         for (int j = 0; j < nTbis; j++) {
577
578             HIptr = readTBI(tbi_group->getChild(j));
579             HUD_deque.insert( HUD_deque.begin(), HIptr);
580
581         }//for - tbis
582     }
583
584     const SGPropertyNode * rwy_group = node->getNode("runways");
585     if (rwy_group != 0) {
586         int nRwy = rwy_group->nChildren();
587         for (int j = 0; j < nRwy; j++) {
588             SG_LOG( SG_COCKPIT, SG_DEBUG,
589                     "**************  Reading runway properties" );
590             HIptr = readRunway(rwy_group->getChild(j));
591             HUD_deque.insert( HUD_deque.begin(), HIptr);
592
593         }//for - runways
594     }
595     return 0;
596 }//end readinstrument
597
598
599 int readHud( istream &input )
600 {
601
602     SGPropertyNode root;
603
604     try {
605         readProperties(input, &root);
606     } catch (const sg_exception &e) {
607         guiErrorMessage("Error reading HUD: ", e);
608         return 0;
609     }
610
611
612     SG_LOG(SG_INPUT, SG_INFO, "Read properties for  " <<
613            root.getStringValue("name"));
614
615
616     HUD_deque.erase( HUD_deque.begin(), HUD_deque.end());
617
618
619     SG_LOG(SG_INPUT, SG_INFO, "Reading Hud instruments");
620
621     const SGPropertyNode * instrument_group = root.getChild("instruments");
622     int nInstruments = instrument_group->nChildren();
623
624     for (int i = 0; i < nInstruments; i++) {
625
626         const SGPropertyNode * node = instrument_group->getChild(i);
627
628         SGPath path( globals->get_fg_root() );
629         path.append(node->getStringValue("path"));
630
631         SG_LOG(SG_INPUT, SG_INFO, "Reading Instrument "
632                << node->getName()
633                << " from "
634                << path.str());
635
636         SGPropertyNode root2;
637         try {
638             readProperties(path.str(), &root2);
639         } catch (const sg_exception &e) {
640             guiErrorMessage("Error reading HUD instrument: ", e);
641             continue;
642         }
643         readInstrument(&root2);
644     }//for loop(i)
645
646     return 0;
647 }
648
649
650 int fgHUDInit( fgAIRCRAFT * /* current_aircraft */ )
651 {
652
653     HUD_style = 1;
654
655     SG_LOG( SG_COCKPIT, SG_INFO, "Initializing current aircraft HUD" );
656
657     string hud_path =
658         fgGetString("/sim/hud/path", "Huds/Default/default.xml");
659     SGPath path(globals->get_fg_root());
660     path.append(hud_path);
661
662     ifstream input(path.c_str());
663     if (!input.good()) {
664         SG_LOG(SG_INPUT, SG_ALERT,
665                "Cannot read Hud configuration from " << path.str());
666     } else {
667         readHud(input);
668         input.close();
669     }
670
671     fgHUDReshape();
672
673     if ( HUDtext ) {
674         // this chunk of code is not necessarily thread safe if the
675         // compiler optimizer reorders these statements.  Note that
676         // "delete ptr" does not set "ptr = NULL".  We have to do that
677         // ourselves.
678         fntRenderer *tmp = HUDtext;
679         HUDtext = NULL;
680         delete tmp;
681     }
682
683     FGFontCache *fc = globals->get_fontcache();
684     HUD_Font = fc->getTexFont(fgGetString("/sim/hud/font/name", "Helvetica.txf"));
685     if (!HUD_Font)
686         throw sg_throwable(string("/sim/hud/font/name is not a texture font"));
687
688     HUD_TextSize = fgGetFloat("/sim/hud/font/size", 10);
689
690     HUDtext = new fntRenderer();
691     HUDtext->setFont(HUD_Font);
692     HUDtext->setPointSize(HUD_TextSize);
693     HUD_TextList.setFont( HUDtext );
694
695     if (!HUD)
696         HUD = new HUD_Properties;
697     return 0;  // For now. Later we may use this for an error code.
698
699 }
700
701 int fgHUDInit2( fgAIRCRAFT * /* current_aircraft */ )
702 {
703
704     HUD_style = 2;
705
706     SG_LOG( SG_COCKPIT, SG_INFO, "Initializing current aircraft HUD" );
707
708     SGPath path(globals->get_fg_root());
709     path.append("Huds/Minimal/default.xml");
710
711
712     ifstream input(path.c_str());
713     if (!input.good()) {
714         SG_LOG(SG_INPUT, SG_ALERT,
715                "Cannot read Hud configuration from " << path.str());
716     } else {
717         readHud(input);
718         input.close();
719     }
720
721     if (!HUD)
722         HUD = new HUD_Properties;
723     return 0;  // For now. Later we may use this for an error code.
724
725 }
726 //$$$ End - added, Neetha, 28 Nov 2k
727
728
729 void fgHUDReshape(void) {
730 #if 0
731     if ( HUDtext ) {
732         // this chunk of code is not necessarily thread safe if the
733         // compiler optimizer reorders these statements.  Note that
734         // "delete ptr" does not set "ptr = NULL".  We have to do that
735         // ourselves.
736         fntRenderer *tmp = HUDtext;
737         HUDtext = NULL;
738         delete tmp;
739     }
740
741     HUD_TextSize = fgGetInt("/sim/startup/xsize") / 60;
742     HUD_TextSize = 10;
743     HUDtext = new fntRenderer();
744     HUDtext -> setFont      ( guiFntHandle ) ;
745     HUDtext -> setPointSize ( HUD_TextSize ) ;
746     HUD_TextList.setFont( HUDtext );
747 #endif
748 }
749
750
751 // fgUpdateHUD
752 //
753 // Performs a once around the list of calls to instruments installed in
754 // the HUD object with requests for redraw. Kinda. It will when this is
755 // all C++.
756 //
757 void fgUpdateHUD( void ) {
758
759     static const SGPropertyNode *enable3d_node = fgGetNode("/sim/hud/enable3d");
760     if ( HUD_style == 1 && enable3d_node->getBoolValue() ) {
761         fgUpdateHUDVirtual();
762         return;
763     }
764
765     static const float normal_aspect = float(640) / float(480);
766     // note: aspect_ratio is Y/X
767     float current_aspect = 1.0f/globals->get_current_view()->get_aspect_ratio();
768     if ( current_aspect > normal_aspect ) {
769         float aspect_adjust = current_aspect / normal_aspect;
770         float adjust = 320.0f*aspect_adjust - 320.0f;
771         fgUpdateHUD( -adjust, 0.0f, 640.0f+adjust, 480.0f );
772     } else {
773         float aspect_adjust = normal_aspect / current_aspect;
774         float adjust = 240.0f*aspect_adjust - 240.0f;
775         fgUpdateHUD( 0.0f, -adjust, 640.0f, 480.0f+adjust );
776     }
777 }
778
779 void fgUpdateHUDVirtual()
780 {
781     FGViewer* view = globals->get_current_view();
782
783     // Standard fgfs projection, with essentially meaningless clip
784     // planes (we'll map the whole HUD plane to z=-1)
785     glMatrixMode(GL_PROJECTION);
786     glPushMatrix();
787     glLoadIdentity();
788     gluPerspective(view->get_v_fov(), 1/view->get_aspect_ratio(), 0.1, 10);
789
790     glMatrixMode(GL_MODELVIEW);
791     glPushMatrix();
792     glLoadIdentity();
793
794     // Standard fgfs view direction computation
795     float lookat[3];
796     lookat[0] = -sin(SG_DEGREES_TO_RADIANS * view->getHeadingOffset_deg());
797     lookat[1] = tan(SG_DEGREES_TO_RADIANS * view->getPitchOffset_deg());
798     lookat[2] = -cos(SG_DEGREES_TO_RADIANS * view->getHeadingOffset_deg());
799     if (fabs(lookat[1]) > 9999)
800         lookat[1] = 9999; // FPU sanity
801     gluLookAt(0, 0, 0, lookat[0], lookat[1], lookat[2], 0, 1, 0);
802
803     // Map the -1:1 square to a 55.0x41.25 degree wide patch at z=1.
804     // This is the default fgfs field of view, which the HUD files are
805     // written to assume.
806     float dx = 0.52056705; // tan(55/2)
807     float dy = dx * 0.75;  // assumes 4:3 aspect ratio
808     float m[16];
809     m[0] = dx; m[4] =  0; m[ 8] = 0; m[12] = 0;
810     m[1] =  0; m[5] = dy; m[ 9] = 0; m[13] = 0;
811     m[2] =  0; m[6] =  0; m[10] = 1; m[14] = 0;
812     m[3] =  0; m[7] =  0; m[11] = 0; m[15] = 1;
813     glMultMatrixf(m);
814
815     // Convert the 640x480 "HUD standard" coordinate space to a square
816     // about the origin in the range [-1:1] at depth of -1
817     glScalef(1./320, 1./240, 1);
818     glTranslatef(-320, -240, -1);
819
820     // Do the deed
821     drawHUD();
822
823     // Clean up our mess
824     glMatrixMode(GL_PROJECTION);
825     glPopMatrix();
826     glMatrixMode(GL_MODELVIEW);
827     glPopMatrix();
828 }
829
830 void fgUpdateHUD( GLfloat x_start, GLfloat y_start,
831                   GLfloat x_end, GLfloat y_end )
832 {
833     glMatrixMode(GL_PROJECTION);
834     glPushMatrix();
835     glLoadIdentity();
836     gluOrtho2D(x_start, x_end, y_start, y_end);
837
838     glMatrixMode(GL_MODELVIEW);
839     glPushMatrix();
840     glLoadIdentity();
841
842     drawHUD();
843
844     glMatrixMode(GL_PROJECTION);
845     glPopMatrix();
846     glMatrixMode(GL_MODELVIEW);
847     glPopMatrix();
848 }
849
850 void drawHUD()
851 {
852     if ( !HUD_deque.size() ) // Trust everyone, but ALWAYS cut the cards!
853         return;
854
855     HUD_TextList.erase();
856     HUD_LineList.erase();
857     // HUD_StippleLineList.erase();
858
859     glDisable(GL_DEPTH_TEST);
860     glDisable(GL_LIGHTING);
861
862     static const SGPropertyNode *heading_enabled
863         = fgGetNode("/autopilot/locks/heading", true);
864     static const SGPropertyNode *altitude_enabled
865         = fgGetNode("/autopilot/locks/altitude", true);
866
867     static char hud_hdg_text[256];
868     static char hud_wp0_text[256];
869     static char hud_wp1_text[256];
870     static char hud_wp2_text[256];
871     static char hud_alt_text[256];
872
873     glEnable(GL_BLEND);
874     if (HUD->isTransparent())
875         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
876     else
877         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
878
879     if (HUD->isAntialiased()) {
880         glEnable(GL_LINE_SMOOTH);
881         glAlphaFunc(GL_GREATER, HUD->alphaClamp());
882         glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
883         //glLineWidth(1.5);
884     } else {
885         //glLineWidth(1.0);
886     }
887
888     HUD->setColor();
889     for_each(HUD_deque.begin(), HUD_deque.end(), HUDdraw());
890
891     //HUD_TextList.add( fgText(40, 10, get_formated_gmt_time(), 0) );
892
893
894     int apY = 480 - 80;
895
896
897     if (strcmp( heading_enabled->getStringValue(), "dg-heading-hold") == 0 ) {
898         snprintf( hud_hdg_text, 256, "hdg = %.1f\n",
899                   fgGetDouble("/autopilot/settings/heading-bug-deg") );
900         HUD_TextList.add( fgText( 40, apY, hud_hdg_text ) );
901         apY -= 15;
902     } else if ( strcmp(heading_enabled->getStringValue(), "true-heading-hold") == 0 ) {
903         snprintf( hud_hdg_text, 256, "hdg = %.1f\n",
904                   fgGetDouble("/autopilot/settings/true-heading-deg") );
905         HUD_TextList.add( fgText( 40, apY, hud_hdg_text ) );
906         apY -= 15;
907
908         string wp0_id = fgGetString( "/autopilot/route-manager/wp[0]/id" );
909         if ( wp0_id.length() > 0 ) {
910             snprintf( hud_wp0_text, 256, "%5s %6.1fnm %s", wp0_id.c_str(),
911                       fgGetDouble( "/autopilot/route-manager/wp[0]/dist" ),
912                       fgGetString( "/autopilot/route-manager/wp[0]/eta" ) );
913             HUD_TextList.add( fgText( 40, apY, hud_wp0_text ) );
914             apY -= 15;
915         }
916         string wp1_id = fgGetString( "/autopilot/route-manager/wp[1]/id" );
917         if ( wp1_id.length() > 0 ) {
918             snprintf( hud_wp1_text, 256, "%5s %6.1fnm %s", wp1_id.c_str(),
919                       fgGetDouble( "/autopilot/route-manager/wp[1]/dist" ),
920                       fgGetString( "/autopilot/route-manager/wp[1]/eta" ) );
921             HUD_TextList.add( fgText( 40, apY, hud_wp1_text ) );
922             apY -= 15;
923         }
924         string wp2_id = fgGetString( "/autopilot/route-manager/wp-last/id" );
925         if ( wp2_id.length() > 0 ) {
926             snprintf( hud_wp2_text, 256, "%5s %6.1fnm %s", wp2_id.c_str(),
927                       fgGetDouble( "/autopilot/route-manager/wp-last/dist" ),
928                       fgGetString( "/autopilot/route-manager/wp-last/eta" ) );
929             HUD_TextList.add( fgText( 40, apY, hud_wp2_text ) );
930             apY -= 15;
931         }
932     }
933
934     if ( strcmp( altitude_enabled->getStringValue(), "altitude-hold" ) == 0 ) {
935         snprintf( hud_alt_text, 256, "alt = %.0f\n",
936                   fgGetDouble("/autopilot/settings/target-altitude-ft") );
937         HUD_TextList.add( fgText( 40, apY, hud_alt_text ) );
938         apY -= 15;
939     } else if ( strcmp( altitude_enabled->getStringValue(), "agl-hold" ) == 0 ){
940         snprintf( hud_alt_text, 256, "agl = %.0f\n",
941                   fgGetDouble("/autopilot/settings/target-agl-ft") );
942         HUD_TextList.add( fgText( 40, apY, hud_alt_text ) );
943         apY -= 15;
944     }
945
946     HUD_TextList.draw();
947     HUD_LineList.draw();
948
949     // glEnable(GL_LINE_STIPPLE);
950     // glLineStipple( 1, 0x00FF );
951     // HUD_StippleLineList.draw();
952     // glDisable(GL_LINE_STIPPLE);
953
954     if (HUD->isAntialiased()) {
955         glDisable(GL_ALPHA_TEST);
956         glDisable(GL_LINE_SMOOTH);
957         //glLineWidth(1.0);
958     }
959
960     glEnable(GL_DEPTH_TEST);
961     glEnable(GL_LIGHTING);
962 }
963
964
965
966 void fgTextList::draw()
967 {
968     if (!Font)
969         return;
970
971     vector<fgText>::iterator curString = List.begin();
972     vector<fgText>::iterator lastString = List.end();
973
974     glPushAttrib(GL_COLOR_BUFFER_BIT);
975     glEnable(GL_BLEND);
976     if (HUD->isTransparent())
977         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
978     else
979         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
980
981     if (HUD->isAntialiased()) {
982         glEnable(GL_ALPHA_TEST);
983         glAlphaFunc(GL_GREATER, HUD->alphaClamp());
984     }
985
986     Font->begin();
987     for (; curString != lastString; curString++)
988         curString->Draw(Font,curString->digit);
989     Font->end();
990
991     glDisable(GL_TEXTURE_2D);
992     glPopAttrib();
993 }
994
995
996
997 // HUD property listener class
998 //
999 HUD_Properties::HUD_Properties() :
1000     _current(fgGetNode("/sim/hud/current-color", true)),
1001     _visibility(fgGetNode("/sim/hud/visibility", true)),
1002     _antialiasing(fgGetNode("/sim/hud/color/antialiased", true)),
1003     _transparency(fgGetNode("/sim/hud/color/transparent", true)),
1004     _red(fgGetNode("/sim/hud/color/red", true)),
1005     _green(fgGetNode("/sim/hud/color/green", true)),
1006     _blue(fgGetNode("/sim/hud/color/blue", true)),
1007     _alpha(fgGetNode("/sim/hud/color/alpha", true)),
1008     _alpha_clamp(fgGetNode("/sim/hud/color/alpha-clamp", true)),
1009     _brightness(fgGetNode("/sim/hud/color/brightness", true)),
1010     _visible(false),
1011     _antialiased(false),
1012     _transparent(false),
1013     _a(0.67),
1014     _cl(0.01)
1015 {
1016     _visibility->addChangeListener(this);
1017     _antialiasing->addChangeListener(this);
1018     _transparency->addChangeListener(this);
1019     _red->addChangeListener(this);
1020     _green->addChangeListener(this);
1021     _blue->addChangeListener(this);
1022     _alpha->addChangeListener(this);
1023     _alpha_clamp->addChangeListener(this);
1024     _brightness->addChangeListener(this);
1025     _current->addChangeListener(this, true);
1026 }
1027
1028
1029 void HUD_Properties::valueChanged(SGPropertyNode *node)
1030 {
1031     if (!strcmp(node->getName(), "current-color")) {
1032         int i = node->getIntValue();
1033         if (i < 0)
1034             i = 0;
1035         SGPropertyNode *n = fgGetNode("/sim/hud/palette", true);
1036         if ((n = n->getChild("color", i, false))) {
1037             if (n->hasValue("red"))
1038                 _red->setFloatValue(n->getFloatValue("red", 1.0));
1039             if (n->hasValue("green"))
1040                 _green->setFloatValue(n->getFloatValue("green", 1.0));
1041             if (n->hasValue("blue"))
1042                 _blue->setFloatValue(n->getFloatValue("blue", 1.0));
1043             if (n->hasValue("alpha"))
1044                 _alpha->setFloatValue(n->getFloatValue("alpha", 0.67));
1045             if (n->hasValue("alpha-clamp"))
1046                 _alpha_clamp->setFloatValue(n->getFloatValue("alpha-clamp", 0.01));
1047             if (n->hasValue("brightness"))
1048                 _brightness->setFloatValue(n->getFloatValue("brightness", 0.75));
1049             if (n->hasValue("antialiased"))
1050                 _antialiasing->setBoolValue(n->getBoolValue("antialiased", false));
1051             if (n->hasValue("transparent"))
1052                 _transparency->setBoolValue(n->getBoolValue("transparent", false));
1053         }
1054     }
1055     _visible = _visibility->getBoolValue();
1056     _transparent = _transparency->getBoolValue();
1057     _antialiased = _antialiasing->getBoolValue();
1058     float brt = _brightness->getFloatValue();
1059     _r = clamp(brt * _red->getFloatValue());
1060     _g = clamp(brt * _green->getFloatValue());
1061     _b = clamp(brt * _blue->getFloatValue());
1062     _a = clamp(_alpha->getFloatValue());
1063     _cl = clamp(_alpha_clamp->getFloatValue());
1064 }
1065
1066
1067 void HUD_Properties::setColor() const
1068 {
1069     if (_antialiased)
1070         glColor4f(_r, _g, _b, _a);
1071     else
1072         glColor3f(_r, _g, _b);
1073 }
1074