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