1 // hud.cxx -- hud defines and prototypes
3 // Written by Michele America, started September 1997.
5 // Copyright (C) 1997 Michele F. America - micheleamerica@geocities.com
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.
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.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include <simgear/compiler.h>
24 #include <simgear/structure/exception.hxx>
38 # define exception c_exception
44 #include <stdio.h> // char related functions
45 #include <string.h> // strcmp()
47 #include <simgear/constants.h>
48 #include <simgear/debug/logstream.hxx>
49 #include <simgear/props/props.hxx>
50 #include <simgear/misc/sg_path.hxx>
52 #include <Aircraft/aircraft.hxx>
53 #include <Autopilot/xmlauto.hxx>
55 #include <Main/globals.hxx>
56 #include <Main/fg_props.hxx>
57 #include <Scenery/scenery.hxx>
59 #if defined ( __sun__ ) || defined ( __sgi )
61 extern void *memmove(void *, const void *, size_t);
69 // The following routines obtain information concerning the aircraft's
70 // current state and return it to calling instrument display routines.
71 // They should eventually be member functions of the aircraft.
74 deque< instr_item * > HUD_deque;
76 fgTextList HUD_TextList;
77 fgLineList HUD_LineList;
78 fgLineList HUD_StippleLineList;
80 fntRenderer *HUDtext = 0;
81 float HUD_TextSize = 0;
85 static float hud_trans_alpha = 0.67f;
88 //$$$ begin - added, Neetha, 28 Nov 2k
96 static float span_units;
97 static float division_units;
98 static float minor_division = 0;
99 static UINT screen_hole;
102 static string loadfn;
104 static float maxValue;
105 static float minValue;
106 static float scaling;
107 static UINT major_divs;
108 static UINT minor_divs;
109 static UINT modulator;
110 static int dp_showing = 0;
111 static string label_format;
112 static string prelabel;
113 static string postlabel;
116 static float maxBankAngle;
117 static float maxSlipAngle;
118 static UINT gap_width;
119 static bool latitude;
120 static bool longitude;
121 static bool tick_bottom;
122 static bool tick_top;
123 static bool tick_right;
124 static bool tick_left;
125 static bool cap_bottom;
127 static bool cap_right;
128 static bool cap_left;
129 static float marker_off;
131 static bool enable_pointer;
132 static string type_pointer;
133 static bool frl_spot;
135 static bool vel_vector;
139 static bool climb_dive;
141 static float glide_slope_val;
142 static bool worm_energy;
143 static bool waypoint;
144 static string type_tick;//hud
145 static string length_tick;//hud
146 static bool label_box;//hud
147 static int digits; //suma
148 static float radius; //suma
149 static int divisions; //suma
150 static int zoom; //suma
151 static int zenith; //suma
152 static int nadir ; //suma
153 static int hat; //suma
154 static bool tsi; //suma
155 static float rad; //suma
158 static FLTFNPTR load_fn;
159 static fgLabelJust justification;
160 static const char *pre_label_string = 0;
161 static const char *post_label_string = 0;
163 int readHud( istream &input );
164 int readInstrument ( const SGPropertyNode * node);
165 static instr_item * readLadder ( const SGPropertyNode * node);
166 static instr_item * readCard ( const SGPropertyNode * node);
167 static instr_item * readLabel( const SGPropertyNode * node);
168 static instr_item * readTBI( const SGPropertyNode * node);
169 //$$$ end - added, Neetha, 28 Nov 2k
171 static void drawHUD();
172 static void fgUpdateHUDVirtual();
174 void fgHUDalphaInit( void );
180 locRECT( UINT left, UINT top, UINT right, UINT bottom);
181 RECT get_rect(void) { return rect;}
184 locRECT :: locRECT( UINT left, UINT top, UINT right, UINT bottom)
189 rect.bottom = bottom;
194 //========================= End of Class Implementations===================
197 // Constructs a HUD object and then adds in instruments. At the present
198 // the instruments are hard coded into the routine. Ultimately these need
199 // to be defined by the aircraft's instrumentation records so that the
200 // display for a Piper Cub doesn't show the speed range of a North American
201 // mustange and the engine readouts of a B36!
206 //$$$ begin - added, Neetha, 28 Nov 2k
208 readLadder(const SGPropertyNode * node)
213 name = node->getStringValue("name");
214 x = node->getIntValue("x");
215 y = node->getIntValue("y");
216 width = node->getIntValue("width");
217 height = node->getIntValue("height");
218 factor = node->getFloatValue("compression_factor");
219 span_units = node->getFloatValue("span_units");
220 division_units = node->getFloatValue("division_units");
221 screen_hole = node->getIntValue("screen_hole");
222 lbl_pos = node->getIntValue("lbl_pos");
223 frl_spot = node->getBoolValue("enable_frl",false);
224 target = node->getBoolValue("enable_target_spot",false);
225 vel_vector = node->getBoolValue("enable_velocity_vector",false);
226 drift = node->getBoolValue("enable_drift_marker",false);
227 alpha = node->getBoolValue("enable_alpha_bracket",false);
228 energy = node->getBoolValue("enable_energy_marker",false);
229 climb_dive = node->getBoolValue("enable_climb_dive_marker",false);
230 glide = node->getBoolValue("enable_glide_slope_marker",false);
231 glide_slope_val = node->getFloatValue("glide_slope",-4.0);
232 worm_energy = node->getBoolValue("enable_energy_marker",false);
233 waypoint = node->getBoolValue("enable_waypoint_marker",false);
234 working = node->getBoolValue("working");
235 zenith = node->getIntValue("zenith"); //suma
236 nadir = node->getIntValue("nadir"); //suma
237 hat = node->getIntValue("hat");
238 // The factor assumes a base of 55 degrees per 640 pixels.
239 // Invert to convert the "compression" factor to a
240 // pixels-per-degree number.
241 if(fgGetBool("/sim/hud/enable3d", true))
246 factor = (640./55.) / factor;
250 SG_LOG(SG_INPUT, SG_INFO, "Done reading instrument " << name);
252 p = (instr_item *) new HudLadder( name, x, y,
253 width, height, factor,
255 span_units, division_units, minor_division,
256 screen_hole, lbl_pos, frl_spot, target, vel_vector,
257 drift, alpha, energy, climb_dive,
258 glide, glide_slope_val, worm_energy,
259 waypoint, working, zenith, nadir, hat);
266 readCard(const SGPropertyNode * node)
271 name = node->getStringValue("name");
272 x = node->getIntValue("x");
273 y = node->getIntValue("y");
274 width = node->getIntValue("width");
275 height = node->getIntValue("height");
276 loadfn = node->getStringValue("loadfn");
277 options = node->getIntValue("options");
278 maxValue = node->getFloatValue("maxValue");
279 minValue = node->getFloatValue("minValue");
280 scaling = node->getFloatValue("disp_scaling");
281 major_divs = node->getIntValue("major_divs");
282 minor_divs = node->getIntValue("minor_divs");
283 modulator = node->getIntValue("modulator");
284 span_units = node->getFloatValue("value_span");
285 type = node->getStringValue("type");
286 tick_bottom = node->getBoolValue("tick_bottom",false);
287 tick_top = node->getBoolValue("tick_top",false);
288 tick_right = node->getBoolValue("tick_right",false);
289 tick_left = node->getBoolValue("tick_left",false);
290 cap_bottom = node->getBoolValue("cap_bottom",false);
291 cap_top = node->getBoolValue("cap_top",false);
292 cap_right = node->getBoolValue("cap_right",false);
293 cap_left = node->getBoolValue("cap_left",false);
294 marker_off = node->getFloatValue("marker_offset",0.0);
295 enable_pointer = node->getBoolValue("enable_pointer",true);
296 type_pointer = node->getStringValue("pointer_type");
297 type_tick = node->getStringValue("tick_type");//hud Can be 'circle' or 'line'
298 length_tick = node->getStringValue("tick_length");//hud For variable length
299 working = node->getBoolValue("working");
300 radius = node->getFloatValue("radius"); //suma
301 divisions = node->getIntValue("divisions"); //suma
302 zoom = node->getIntValue("zoom"); //suma
304 SG_LOG(SG_INPUT, SG_INFO, "Done reading instrument " << name);
308 span_units = maxValue - minValue;
311 if (loadfn=="anzg") {
313 } else if (loadfn=="heading") {
314 load_fn = get_heading;
315 } else if (loadfn=="aoa") {
317 } else if (loadfn=="climb") {
318 load_fn = get_climb_rate;
319 } else if (loadfn=="altitude") {
320 load_fn = get_altitude;
321 } else if (loadfn=="agl") {
323 } else if (loadfn=="speed") {
325 } else if (loadfn=="view_direction") {
326 load_fn = get_view_direction;
327 } else if (loadfn=="aileronval") {
328 load_fn = get_aileronval;
329 } else if (loadfn=="elevatorval") {
330 load_fn = get_elevatorval;
331 } else if (loadfn=="elevatortrimval") {
332 load_fn = get_elev_trimval;
333 } else if (loadfn=="rudderval") {
334 load_fn = get_rudderval;
335 } else if (loadfn=="throttleval") {
336 load_fn = get_throttleval;
340 if ( (type == "dial") | (type == "tape") ) {
341 p = (instr_item *) new hud_card( x,
349 major_divs, minor_divs,
373 p = (instr_item *) new gauge_instr( x, // x
377 load_fn, // data source
381 major_divs, minor_divs,
391 readLabel(const SGPropertyNode * node)
395 int font_size = (fgGetInt("/sim/startup/xsize") > 1000) ? HUD_FONT_LARGE : HUD_FONT_SMALL;
397 name = node->getStringValue("name");
398 x = node->getIntValue("x");
399 y = node->getIntValue("y");
400 width = node->getIntValue("width");
401 height = node->getIntValue("height");
402 loadfn = node->getStringValue("data_source");
403 label_format = node->getStringValue("label_format");
404 prelabel = node->getStringValue("pre_label_string");
405 postlabel = node->getStringValue("post_label_string");
406 scaling = node->getFloatValue("scale_data");
407 options = node->getIntValue("options");
408 justi = node->getIntValue("justification");
409 blinking = node->getIntValue("blinking");
410 latitude = node->getBoolValue("latitude",false);
411 longitude = node->getBoolValue("longitude",false);
412 label_box = node->getBoolValue("label_box",false);//hud
413 working = node->getBoolValue("working");
414 digits = node->getIntValue("digits"); //suma
417 SG_LOG(SG_INPUT, SG_INFO, "Done reading instrument " << name);
421 justification = LEFT_JUST;
424 justification = CENTER_JUST;
427 justification = RIGHT_JUST;
432 if ( prelabel == "NULL" ) {
433 pre_label_string = NULL;
435 if ( prelabel == "blank" ) {
436 pre_label_string = " ";
438 pre_label_string = prelabel.c_str();
442 if ( postlabel == "blank" ) {
443 post_label_string = " ";
445 if ( postlabel == "NULL" ) {
446 post_label_string = NULL;
448 if ( postlabel == "units" ) {
449 post_label_string = units;
451 post_label_string = postlabel.c_str();
456 if ( loadfn== "aux1" ) {
458 } else if ( loadfn == "aux2" ) {
460 } else if ( loadfn == "aux3" ) {
462 } else if ( loadfn == "aux4" ) {
464 } else if ( loadfn == "aux5" ) {
466 } else if ( loadfn == "aux6" ) {
468 } else if ( loadfn == "aux7" ) {
470 } else if ( loadfn == "aux8" ) {
472 } else if ( loadfn == "aux9" ) {
474 } else if ( loadfn == "aux10" ) {
476 } else if ( loadfn == "aux11" ) {
478 } else if ( loadfn == "aux12" ) {
480 } else if ( loadfn == "aux13" ) {
482 } else if ( loadfn == "aux14" ) {
484 } else if ( loadfn == "aux15" ) {
486 } else if ( loadfn == "aux16" ) {
488 } else if ( loadfn == "aux17" ) {
490 } else if ( loadfn == "aux18" ) {
492 } else if ( loadfn == "ax" ) {
494 } else if ( loadfn == "speed" ) {
496 } else if ( loadfn == "mach" ) {
498 } else if ( loadfn == "altitude" ) {
499 load_fn = get_altitude;
500 } else if ( loadfn == "agl" ) {
502 } else if ( loadfn == "framerate" ) {
503 load_fn = get_frame_rate;
504 } else if ( loadfn == "heading" ) {
505 load_fn = get_heading;
506 } else if ( loadfn == "fov" ) {
508 } else if ( loadfn == "vfc_tris_culled" ) {
509 load_fn = get_vfc_tris_culled;
510 } else if ( loadfn == "vfc_tris_drawn" ) {
511 load_fn = get_vfc_tris_drawn;
512 } else if ( loadfn == "aoa" ) {
514 } else if ( loadfn == "latitude" ) {
515 load_fn = get_latitude;
516 } else if ( loadfn == "anzg" ) {
518 } else if ( loadfn == "longitude" ) {
519 load_fn = get_longitude;
520 } else if (loadfn=="throttleval") {
521 load_fn = get_throttleval;
524 p = (instr_item *) new instr_label ( x,
529 label_format.c_str(),
547 readTBI(const SGPropertyNode * node)
552 name = node->getStringValue("name");
553 x = node->getIntValue("x");
554 y = node->getIntValue("y");
555 width = node->getIntValue("width");
556 height = node->getIntValue("height");
557 maxBankAngle = node->getFloatValue("maxBankAngle");
558 maxSlipAngle = node->getFloatValue("maxSlipAngle");
559 gap_width = node->getIntValue("gap_width");
560 working = node->getBoolValue("working");
561 tsi = node->getBoolValue("tsi"); //suma
562 rad = node->getFloatValue("rad"); //suma
564 SG_LOG(SG_INPUT, SG_INFO, "Done reading instrument " << name);
567 p = (instr_item *) new fgTBI_instr( x,
584 int readInstrument(const SGPropertyNode * node)
586 static const SGPropertyNode *startup_units_node
587 = fgGetNode("/sim/startup/units");
591 if ( !strcmp(startup_units_node->getStringValue(), "feet") ) {
592 strcpy(units, " ft");
597 const SGPropertyNode * ladder_group = node->getNode("ladders");
599 if (ladder_group != 0) {
600 int nLadders = ladder_group->nChildren();
601 for (int j = 0; j < nLadders; j++) {
603 HIptr = readLadder(ladder_group->getChild(j));
604 HUD_deque.insert( HUD_deque.begin(), HIptr);
609 const SGPropertyNode * card_group = node->getNode("cards");
610 if (card_group != 0) {
611 int nCards = card_group->nChildren();
612 for (int j = 0; j < nCards; j++) {
614 HIptr = readCard(card_group->getChild(j));
615 HUD_deque.insert( HUD_deque.begin(), HIptr);
620 const SGPropertyNode * label_group = node->getNode("labels");
621 if (label_group != 0) {
622 int nLabels = label_group->nChildren();
623 for (int j = 0; j < nLabels; j++) {
625 HIptr = readLabel(label_group->getChild(j));
626 HUD_deque.insert( HUD_deque.begin(), HIptr);
631 const SGPropertyNode * tbi_group = node->getNode("tbis");
632 if (tbi_group != 0) {
633 int nTbis = tbi_group->nChildren();
634 for (int j = 0; j < nTbis; j++) {
636 HIptr = readTBI(tbi_group->getChild(j));
637 HUD_deque.insert( HUD_deque.begin(), HIptr);
642 }//end readinstrument
645 int readHud( istream &input )
651 readProperties(input, &root);
652 } catch (const sg_exception &e) {
653 guiErrorMessage("Error reading HUD: ", e);
658 SG_LOG(SG_INPUT, SG_INFO, "Read properties for " <<
659 root.getStringValue("name"));
662 HUD_deque.erase( HUD_deque.begin(), HUD_deque.end()); // empty the HUD deque
665 SG_LOG(SG_INPUT, SG_INFO, "Reading Hud instruments");
667 const SGPropertyNode * instrument_group = root.getChild("instruments");
668 int nInstruments = instrument_group->nChildren();
670 for (int i = 0; i < nInstruments; i++) {
672 const SGPropertyNode * node = instrument_group->getChild(i);
674 SGPath path( globals->get_fg_root() );
675 path.append(node->getStringValue("path"));
677 SG_LOG(SG_INPUT, SG_INFO, "Reading Instrument "
682 SGPropertyNode root2;
684 readProperties(path.str(), &root2);
685 } catch (const sg_exception &e) {
686 guiErrorMessage("Error reading HUD instrument: ", e);
689 readInstrument(&root2);
696 int fgHUDInit( fgAIRCRAFT * /* current_aircraft */ )
701 SG_LOG( SG_COCKPIT, SG_INFO, "Initializing current aircraft HUD" );
704 fgGetString("/sim/hud/path", "Huds/Default/default.xml");
705 SGPath path(globals->get_fg_root());
706 path.append(hud_path);
708 ifstream input(path.c_str());
710 SG_LOG(SG_INPUT, SG_ALERT,
711 "Cannot read Hud configuration from " << path.str());
721 // this chunk of code is not necessarily thread safe if the
722 // compiler optimizer reorders these statements. Note that
723 // "delete ptr" does not set "ptr = NULL". We have to do that
725 fntRenderer *tmp = HUDtext;
730 // HUD_TextSize = fgGetInt("/sim/startup/xsize") / 60;
732 HUDtext = new fntRenderer();
733 HUDtext -> setFont ( guiFntHandle ) ;
734 HUDtext -> setPointSize ( HUD_TextSize ) ;
735 HUD_TextList.setFont( HUDtext );
737 return 0; // For now. Later we may use this for an error code.
741 int fgHUDInit2( fgAIRCRAFT * /* current_aircraft */ )
746 SG_LOG( SG_COCKPIT, SG_INFO, "Initializing current aircraft HUD" );
748 SGPath path(globals->get_fg_root());
749 path.append("Huds/Minimal/default.xml");
752 ifstream input(path.c_str());
754 SG_LOG(SG_INPUT, SG_ALERT,
755 "Cannot read Hud configuration from " << path.str());
761 return 0; // For now. Later we may use this for an error code.
764 //$$$ End - added, Neetha, 28 Nov 2k
766 static int global_day_night_switch = HUD_DAY;
768 void HUD_masterswitch( bool incr )
770 if ( fgGetBool("/sim/hud/visibility") ) {
771 if ( global_day_night_switch == HUD_DAY ) {
772 global_day_night_switch = HUD_NIGHT;
774 fgSetBool("/sim/hud/visibility", false);
777 fgSetBool("/sim/hud/visibility", true);
778 global_day_night_switch = HUD_DAY;
782 void HUD_brightkey( bool incr_bright )
784 instr_item *pHUDInstr = HUD_deque[0];
785 int brightness = pHUDInstr->get_brightness();
787 if( fgGetBool("/sim/hud/visibility") ) {
792 brightness = HUD_BRT_BLACK;
796 brightness = HUD_BRT_LIGHT;
800 brightness = HUD_BRT_MEDIUM;
804 brightness = HUD_BRT_DARK;
808 brightness = HUD_BRT_BLACK;
814 brightness = HUD_BRT_MEDIUM;
818 brightness = HUD_BRT_DARK;
822 brightness = HUD_BRT_BLACK;
826 brightness = HUD_BRT_LIGHT;
830 fgSetBool("/sim/hud/visibility", false);
834 fgSetBool("/sim/hud/visibility", true);
837 pHUDInstr->SetBrightness( brightness );
841 #define fgAP_CLAMP(val,min,max) ( (val) = (val) > (max) ? (max) : (val) < (min) ? (min) : (val) )
843 static puDialogBox *HUDalphaDialog;
844 static puText *HUDalphaText;
845 static puSlider *HUDalphaHS0;
846 //static puText *HUDtextText;
847 //static puSlider *HUDalphaHS1;
848 static char SliderText[2][ 8 ];
850 static void alpha_adj( puObject *hs ) {
853 hs-> getValue ( &val ) ;
854 fgAP_CLAMP ( val, 0.1, 1.0 ) ;
855 // printf ( "maxroll_adj( %p ) %f %f\n", hs, val, MaxRollAdjust * val ) ;
856 hud_trans_alpha = val;
857 sprintf( SliderText[ 0 ], "%05.2f", hud_trans_alpha );
858 HUDalphaText -> setLabel ( SliderText[ 0 ] ) ;
861 void fgHUDalphaAdjust( puObject * ) {
862 fgSetBool("/sim/hud/antialiased", true);
863 FG_PUSH_PUI_DIALOG( HUDalphaDialog );
866 static void goAwayHUDalphaAdjust (puObject *)
868 FG_POP_PUI_DIALOG( HUDalphaDialog );
871 static void cancelHUDalphaAdjust (puObject *)
873 fgSetBool("/sim/hud/antialiased", false);
874 FG_POP_PUI_DIALOG( HUDalphaDialog );
877 // Done once at system initialization
878 void fgHUDalphaInit( void ) {
880 // printf("fgHUDalphaInit\n");
881 #define HORIZONTAL FALSE
885 int DialogWidth = 240;
887 char Label[] = "HUD Adjuster";
890 int labelX = (DialogWidth / 2) -
891 ( puGetDefaultLabelFont().getStringWidth( Label ) / 2);
896 int slider_width = 220;
897 int slider_title_x = 15;
898 int slider_value_x = 160;
899 float slider_delta = 0.05f;
901 puFont HUDalphaLegendFont;
902 puFont HUDalphaLabelFont;
903 puGetDefaultFonts ( &HUDalphaLegendFont, &HUDalphaLabelFont );
905 HUDalphaDialog = new puDialogBox ( DialogX, DialogY ); {
906 int horiz_slider_height = HUDalphaLabelFont.getStringHeight() +
907 HUDalphaLabelFont.getStringDescender() +
908 PUSTR_TGAP + PUSTR_BGAP + 5;
911 HUDalphaFrame = new puFrame ( 0, 0, DialogWidth,
913 * horiz_slider_height ); */
916 HUDalphaDialogMessage = new puText ( labelX,
918 * horiz_slider_height );
919 HUDalphaDialogMessage -> setDefaultValue ( Label );
920 HUDalphaDialogMessage -> getDefaultValue ( &s );
921 HUDalphaDialogMessage -> setLabel ( s );
923 HUDalphaHS0 = new puSlider ( slider_x, slider_y,
924 slider_width, HORIZONTAL ) ;
925 HUDalphaHS0-> setDelta ( slider_delta ) ;
926 HUDalphaHS0-> setValue ( hud_trans_alpha ) ;
927 HUDalphaHS0-> setCBMode ( PUSLIDER_DELTA ) ;
928 HUDalphaHS0-> setCallback ( alpha_adj ) ;
931 HUDalphaTitle = new puText ( slider_title_x, slider_y ) ;
932 HUDalphaTitle-> setDefaultValue ( "Alpha" ) ;
933 HUDalphaTitle-> getDefaultValue ( &s ) ;
934 HUDalphaTitle-> setLabel ( s ) ;
936 HUDalphaText = new puText ( slider_value_x, slider_y ) ;
937 sprintf( SliderText[ 0 ], "%05.2f", hud_trans_alpha );
938 HUDalphaText-> setLabel ( SliderText[ 0 ] ) ;
941 HUDalphaOkButton = new puOneShot ( 10, 10, 60, 45 );
942 HUDalphaOkButton-> setLegend ( gui_msg_OK );
943 HUDalphaOkButton-> makeReturnDefault ( TRUE );
944 HUDalphaOkButton-> setCallback ( goAwayHUDalphaAdjust );
947 HUDalphaNoButton = new puOneShot ( 160, 10, 230, 45 );
948 HUDalphaNoButton-> setLegend ( gui_msg_CANCEL );
949 HUDalphaNoButton-> setCallback ( cancelHUDalphaAdjust );
951 FG_FINALIZE_PUI_DIALOG( HUDalphaDialog );
957 void fgHUDReshape(void) {
960 // this chunk of code is not necessarily thread safe if the
961 // compiler optimizer reorders these statements. Note that
962 // "delete ptr" does not set "ptr = NULL". We have to do that
964 fntRenderer *tmp = HUDtext;
969 HUD_TextSize = fgGetInt("/sim/startup/xsize") / 60;
971 HUDtext = new fntRenderer();
972 HUDtext -> setFont ( guiFntHandle ) ;
973 HUDtext -> setPointSize ( HUD_TextSize ) ;
974 HUD_TextList.setFont( HUDtext );
979 static void set_hud_color(float r, float g, float b) {
980 fgGetBool("/sim/hud/antialiased") ?
981 glColor4f(r,g,b,hud_trans_alpha) :
988 // Performs a once around the list of calls to instruments installed in
989 // the HUD object with requests for redraw. Kinda. It will when this is
992 void fgUpdateHUD( void ) {
994 static const SGPropertyNode *enable3d_node = fgGetNode("/sim/hud/enable3d");
995 if( HUD_style == 1 && enable3d_node->getBoolValue() )
997 fgUpdateHUDVirtual();
1001 static const float normal_aspect = float(640) / float(480);
1002 // note: aspect_ratio is Y/X
1003 float current_aspect = 1.0f/globals->get_current_view()->get_aspect_ratio();
1004 if( current_aspect > normal_aspect ) {
1005 float aspect_adjust = current_aspect / normal_aspect;
1006 float adjust = 320.0f*aspect_adjust - 320.0f;
1007 fgUpdateHUD( -adjust, 0.0f, 640.0f+adjust, 480.0f );
1009 float aspect_adjust = normal_aspect / current_aspect;
1010 float adjust = 240.0f*aspect_adjust - 240.0f;
1011 fgUpdateHUD( 0.0f, -adjust, 640.0f, 480.0f+adjust );
1015 void fgUpdateHUDVirtual()
1017 FGViewer* view = globals->get_current_view();
1019 // Standard fgfs projection, with essentially meaningless clip
1020 // planes (we'll map the whole HUD plane to z=-1)
1021 glMatrixMode(GL_PROJECTION);
1024 gluPerspective(view->get_v_fov(), 1/view->get_aspect_ratio(), 0.1, 10);
1026 glMatrixMode(GL_MODELVIEW);
1030 // Standard fgfs view direction computation
1032 lookat[0] = -sin(SG_DEGREES_TO_RADIANS * view->getHeadingOffset_deg());
1033 lookat[1] = tan(SG_DEGREES_TO_RADIANS * view->getPitchOffset_deg());
1034 lookat[2] = -cos(SG_DEGREES_TO_RADIANS * view->getHeadingOffset_deg());
1035 if(fabs(lookat[1]) > 9999) lookat[1] = 9999; // FPU sanity
1036 gluLookAt(0, 0, 0, lookat[0], lookat[1], lookat[2], 0, 1, 0);
1038 // Map the -1:1 square to a 55.0x41.25 degree wide patch at z=1.
1039 // This is the default fgfs field of view, which the HUD files are
1040 // written to assume.
1041 float dx = 0.52056705; // tan(55/2)
1042 float dy = dx * 0.75; // assumes 4:3 aspect ratio
1044 m[0] = dx; m[4] = 0; m[ 8] = 0; m[12] = 0;
1045 m[1] = 0; m[5] = dy; m[ 9] = 0; m[13] = 0;
1046 m[2] = 0; m[6] = 0; m[10] = 1; m[14] = 0;
1047 m[3] = 0; m[7] = 0; m[11] = 0; m[15] = 1;
1050 // Convert the 640x480 "HUD standard" coordinate space to a square
1051 // about the origin in the range [-1:1] at depth of -1
1052 glScalef(1./320, 1./240, 1);
1053 glTranslatef(-320, -240, -1);
1058 // Clean up our mess
1059 glMatrixMode(GL_PROJECTION);
1061 glMatrixMode(GL_MODELVIEW);
1065 void fgUpdateHUD( GLfloat x_start, GLfloat y_start,
1066 GLfloat x_end, GLfloat y_end )
1068 glMatrixMode(GL_PROJECTION);
1071 gluOrtho2D(x_start, x_end, y_start, y_end);
1073 glMatrixMode(GL_MODELVIEW);
1079 glMatrixMode(GL_PROJECTION);
1081 glMatrixMode(GL_MODELVIEW);
1087 if( !HUD_deque.size() ) { // Trust everyone, but ALWAYS cut the cards!
1091 HUD_TextList.erase();
1092 HUD_LineList.erase();
1093 // HUD_StippleLineList.erase();
1095 glDisable(GL_DEPTH_TEST);
1096 glDisable(GL_LIGHTING);
1098 static const SGPropertyNode *antialiased_node
1099 = fgGetNode("/sim/hud/antialiased", true);
1100 static const SGPropertyNode *heading_enabled
1101 = fgGetNode("/autopilot/locks/heading", true);
1102 static const SGPropertyNode *altitude_enabled
1103 = fgGetNode("/autopilot/locks/altitude", true);
1105 static char hud_hdg_text[256];
1106 static char hud_wp0_text[256];
1107 static char hud_wp1_text[256];
1108 static char hud_wp2_text[256];
1109 static char hud_alt_text[256];
1111 if( antialiased_node->getBoolValue() ) {
1112 glEnable(GL_LINE_SMOOTH);
1113 // glEnable(GL_BLEND);
1114 glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
1115 glHint(GL_LINE_SMOOTH_HINT,GL_DONT_CARE);
1121 if( global_day_night_switch == HUD_DAY) {
1122 switch (HUD_deque[0]->get_brightness())
1125 set_hud_color (0.1f, 0.9f, 0.1f);
1128 case HUD_BRT_MEDIUM:
1129 set_hud_color (0.1f, 0.7f, 0.0f);
1133 set_hud_color (0.0f, 0.6f, 0.0f);
1137 set_hud_color( 0.0f, 0.0f, 0.0f);
1141 set_hud_color (0.1f, 0.9f, 0.1f);
1144 if( global_day_night_switch == HUD_NIGHT) {
1145 switch (HUD_deque[0]->get_brightness())
1148 set_hud_color (0.9f, 0.1f, 0.1f);
1151 case HUD_BRT_MEDIUM:
1152 set_hud_color (0.7f, 0.0f, 0.1f);
1156 set_hud_color (0.6f, 0.0f, 0.0f);
1160 set_hud_color( 0.0f, 0.0f, 0.0f);
1164 set_hud_color (0.6f, 0.0f, 0.0f);
1166 } else { // Just in case default
1167 set_hud_color (0.1f, 0.9f, 0.1f);
1171 for_each(HUD_deque.begin(), HUD_deque.end(), HUDdraw());
1173 HUD_TextList.add( fgText(40, 10, get_formated_gmt_time(), 0) );
1179 if (strcmp( heading_enabled->getStringValue(), "dg-heading-hold") == 0 ) {
1180 snprintf( hud_hdg_text, 256, "hdg = %.1f\n",
1181 fgGetDouble("/autopilot/settings/heading-bug-deg") );
1182 HUD_TextList.add( fgText( 40, apY, hud_hdg_text ) );
1184 } else if ( strcmp(heading_enabled->getStringValue(), "true-heading-hold") == 0 ) {
1185 snprintf( hud_hdg_text, 256, "hdg = %.1f\n",
1186 fgGetDouble("/autopilot/settings/true-heading-deg") );
1187 HUD_TextList.add( fgText( 40, apY, hud_hdg_text ) );
1190 string wp0_id = fgGetString( "/autopilot/route-manager/wp[0]/id" );
1191 if ( wp0_id.length() > 0 ) {
1192 snprintf( hud_wp0_text, 256, "%5s %6.1fnm %s", wp0_id.c_str(),
1193 fgGetDouble( "/autopilot/route-manager/wp[0]/dist" ),
1194 fgGetString( "/autopilot/route-manager/wp[0]/eta" ) );
1195 HUD_TextList.add( fgText( 40, apY, hud_wp0_text ) );
1198 string wp1_id = fgGetString( "/autopilot/route-manager/wp[1]/id" );
1199 if ( wp1_id.length() > 0 ) {
1200 snprintf( hud_wp1_text, 256, "%5s %6.1fnm %s", wp1_id.c_str(),
1201 fgGetDouble( "/autopilot/route-manager/wp[1]/dist" ),
1202 fgGetString( "/autopilot/route-manager/wp[1]/eta" ) );
1203 HUD_TextList.add( fgText( 40, apY, hud_wp1_text ) );
1206 string wp2_id = fgGetString( "/autopilot/route-manager/wp-last/id" );
1207 if ( wp2_id.length() > 0 ) {
1208 snprintf( hud_wp2_text, 256, "%5s %6.1fnm %s", wp2_id.c_str(),
1209 fgGetDouble( "/autopilot/route-manager/wp-last/dist" ),
1210 fgGetString( "/autopilot/route-manager/wp-last/eta" ) );
1211 HUD_TextList.add( fgText( 40, apY, hud_wp2_text ) );
1216 if ( strcmp( altitude_enabled->getStringValue(), "altitude-hold" ) == 0 ) {
1217 snprintf( hud_alt_text, 256, "alt = %.0f\n",
1218 fgGetDouble("/autopilot/settings/target-altitude-ft") );
1219 HUD_TextList.add( fgText( 40, apY, hud_alt_text ) );
1221 } else if ( strcmp( altitude_enabled->getStringValue(), "agl-hold" ) == 0 ){
1222 snprintf( hud_alt_text, 256, "agl = %.0f\n",
1223 fgGetDouble("/autopilot/settings/target-agl-ft") );
1224 HUD_TextList.add( fgText( 40, apY, hud_alt_text ) );
1228 HUD_TextList.draw();
1230 HUD_LineList.draw();
1232 // glEnable(GL_LINE_STIPPLE);
1233 // glLineStipple( 1, 0x00FF );
1234 // HUD_StippleLineList.draw();
1235 // glDisable(GL_LINE_STIPPLE);
1237 if( antialiased_node->getBoolValue() ) {
1238 // glDisable(GL_BLEND);
1239 glDisable(GL_LINE_SMOOTH);
1243 glEnable(GL_DEPTH_TEST);
1244 glEnable(GL_LIGHTING);