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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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()
49 #include <simgear/constants.h>
50 #include <simgear/debug/logstream.hxx>
51 #include <simgear/props/props.hxx>
52 #include <simgear/misc/sg_path.hxx>
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>
64 static HUD_Properties *HUD = 0;
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.
73 deque< instr_item * > HUD_deque;
75 fgTextList HUD_TextList;
76 fgLineList HUD_LineList;
77 fgLineList HUD_StippleLineList;
79 fntRenderer *HUDtext = 0;
80 fntTexFont *HUD_Font = 0;
81 float HUD_TextSize = 0;
87 //$$$ begin - added, Neetha, 28 Nov 2k
95 static float span_units;
96 static float division_units;
97 static float minor_division = 0;
98 static UINT screen_hole;
101 static string loadfn;
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;
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;
126 static bool cap_right;
127 static bool cap_left;
128 static float marker_off;
130 static bool enable_pointer;
131 static string type_pointer;
132 static bool frl_spot;
134 static bool vel_vector;
138 static bool climb_dive;
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;
148 static int divisions;
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;
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
170 static void drawHUD();
171 static void fgUpdateHUDVirtual();
177 locRECT( UINT left, UINT top, UINT right, UINT bottom);
178 RECT get_rect(void) { return rect;}
181 locRECT :: locRECT( UINT left, UINT top, UINT right, UINT bottom)
186 rect.bottom = bottom;
191 //========================= End of Class Implementations===================
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!
203 //$$$ begin - added, Neetha, 28 Nov 2k
205 readLadder(const SGPropertyNode * node)
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) {
241 factor = (640./55.) / factor;
245 SG_LOG(SG_INPUT, SG_INFO, "Done reading instrument " << name);
247 p = (instr_item *) new HudLadder( name, x, y,
248 width, height, factor,
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);
261 readCard(const SGPropertyNode * node)
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");
299 SG_LOG(SG_INPUT, SG_INFO, "Done reading instrument " << name);
303 span_units = maxValue - minValue;
306 if (loadfn=="anzg") {
308 } else if (loadfn=="heading") {
309 load_fn = get_heading;
310 } else if (loadfn=="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") {
318 } else if (loadfn=="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;
335 if ( (type == "dial") | (type == "tape") ) {
336 p = (instr_item *) new hud_card( x,
344 major_divs, minor_divs,
368 p = (instr_item *) new gauge_instr( x, // x
372 load_fn, // data source
376 major_divs, minor_divs,
386 readLabel(const SGPropertyNode * node)
390 int font_size = (fgGetInt("/sim/startup/xsize") > 1000) ? HUD_FONT_LARGE : HUD_FONT_SMALL;
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");
412 SG_LOG(SG_INPUT, SG_INFO, "Done reading instrument " << name);
416 justification = LEFT_JUST;
419 justification = CENTER_JUST;
422 justification = RIGHT_JUST;
427 if ( prelabel == "NULL" ) {
428 pre_label_string = NULL;
430 if ( prelabel == "blank" ) {
431 pre_label_string = " ";
433 pre_label_string = prelabel.c_str();
437 if ( postlabel == "blank" ) {
438 post_label_string = " ";
440 if ( postlabel == "NULL" ) {
441 post_label_string = NULL;
443 if ( postlabel == "units" ) {
444 post_label_string = units;
446 post_label_string = postlabel.c_str();
451 #ifdef ENABLE_SP_FMDS
452 if ( loadfn== "aux1" ) {
454 } else if ( loadfn == "aux2" ) {
456 } else if ( loadfn == "aux3" ) {
458 } else if ( loadfn == "aux4" ) {
460 } else if ( loadfn == "aux5" ) {
462 } else if ( loadfn == "aux6" ) {
464 } else if ( loadfn == "aux7" ) {
466 } else if ( loadfn == "aux8" ) {
468 } else if ( loadfn == "aux9" ) {
470 } else if ( loadfn == "aux10" ) {
472 } else if ( loadfn == "aux11" ) {
474 } else if ( loadfn == "aux12" ) {
476 } else if ( loadfn == "aux13" ) {
478 } else if ( loadfn == "aux14" ) {
480 } else if ( loadfn == "aux15" ) {
482 } else if ( loadfn == "aux16" ) {
484 } else if ( loadfn == "aux17" ) {
486 } else if ( loadfn == "aux18" ) {
490 if ( loadfn == "ax" ) {
492 } else if ( loadfn == "speed" ) {
494 } else if ( loadfn == "mach" ) {
496 } else if ( loadfn == "altitude" ) {
497 load_fn = get_altitude;
498 } else if ( loadfn == "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" ) {
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" ) {
512 } else if ( loadfn == "latitude" ) {
513 load_fn = get_latitude;
514 } else if ( loadfn == "anzg" ) {
516 } else if ( loadfn == "longitude" ) {
517 load_fn = get_longitude;
518 } else if (loadfn=="throttleval") {
519 load_fn = get_throttleval;
522 p = (instr_item *) new instr_label ( x,
527 label_format.c_str(),
545 readTBI(const SGPropertyNode * node)
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");
562 SG_LOG(SG_INPUT, SG_INFO, "Done reading instrument " << name);
565 p = (instr_item *) new fgTBI_instr( x,
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;
605 int readInstrument(const SGPropertyNode * node)
607 static const SGPropertyNode *startup_units_node
608 = fgGetNode("/sim/startup/units");
612 if ( !strcmp(startup_units_node->getStringValue(), "feet") ) {
613 strcpy(units, " ft");
618 const SGPropertyNode * ladder_group = node->getNode("ladders");
620 if (ladder_group != 0) {
621 int nLadders = ladder_group->nChildren();
622 for (int j = 0; j < nLadders; j++) {
624 HIptr = readLadder(ladder_group->getChild(j));
625 HUD_deque.insert( HUD_deque.begin(), HIptr);
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++) {
635 HIptr = readCard(card_group->getChild(j));
636 HUD_deque.insert( HUD_deque.begin(), HIptr);
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++) {
646 HIptr = readLabel(label_group->getChild(j));
647 HUD_deque.insert( HUD_deque.begin(), HIptr);
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++) {
657 HIptr = readTBI(tbi_group->getChild(j));
658 HUD_deque.insert( HUD_deque.begin(), HIptr);
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);
675 }//end readinstrument
678 int readHud( istream &input )
684 readProperties(input, &root);
685 } catch (const sg_exception &e) {
686 guiErrorMessage("Error reading HUD: ", e);
691 SG_LOG(SG_INPUT, SG_INFO, "Read properties for " <<
692 root.getStringValue("name"));
695 HUD_deque.erase( HUD_deque.begin(), HUD_deque.end());
698 SG_LOG(SG_INPUT, SG_INFO, "Reading Hud instruments");
700 const SGPropertyNode * instrument_group = root.getChild("instruments");
701 int nInstruments = instrument_group->nChildren();
703 for (int i = 0; i < nInstruments; i++) {
705 const SGPropertyNode * node = instrument_group->getChild(i);
707 SGPath path( globals->get_fg_root() );
708 path.append(node->getStringValue("path"));
710 SG_LOG(SG_INPUT, SG_INFO, "Reading Instrument "
715 SGPropertyNode root2;
717 readProperties(path.str(), &root2);
718 } catch (const sg_exception &e) {
719 guiErrorMessage("Error reading HUD instrument: ", e);
722 readInstrument(&root2);
729 int fgHUDInit( fgAIRCRAFT * /* current_aircraft */ )
734 SG_LOG( SG_COCKPIT, SG_INFO, "Initializing current aircraft HUD" );
737 fgGetString("/sim/hud/path", "Huds/Default/default.xml");
738 SGPath path(globals->get_fg_root());
739 path.append(hud_path);
741 ifstream input(path.c_str());
743 SG_LOG(SG_INPUT, SG_ALERT,
744 "Cannot read Hud configuration from " << path.str());
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
757 fntRenderer *tmp = HUDtext;
762 FGFontCache *fc = globals->get_fontcache();
763 HUD_Font = fc->getTexFont(fgGetString("/sim/hud/font/name", "Helvetica.txf"));
765 throw sg_throwable(string("/sim/hud/font/name is not a texture font"));
767 HUD_TextSize = fgGetFloat("/sim/hud/font/size", 10);
769 HUDtext = new fntRenderer();
770 HUDtext->setFont(HUD_Font);
771 HUDtext->setPointSize(HUD_TextSize);
772 HUD_TextList.setFont( HUDtext );
775 HUD = new HUD_Properties;
776 return 0; // For now. Later we may use this for an error code.
780 int fgHUDInit2( fgAIRCRAFT * /* current_aircraft */ )
785 SG_LOG( SG_COCKPIT, SG_INFO, "Initializing current aircraft HUD" );
787 SGPath path(globals->get_fg_root());
788 path.append("Huds/Minimal/default.xml");
791 ifstream input(path.c_str());
793 SG_LOG(SG_INPUT, SG_ALERT,
794 "Cannot read Hud configuration from " << path.str());
801 HUD = new HUD_Properties;
802 return 0; // For now. Later we may use this for an error code.
805 //$$$ End - added, Neetha, 28 Nov 2k
808 void fgHUDReshape(void) {
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
815 fntRenderer *tmp = HUDtext;
820 HUD_TextSize = fgGetInt("/sim/startup/xsize") / 60;
822 HUDtext = new fntRenderer();
823 HUDtext -> setFont ( guiFntHandle ) ;
824 HUDtext -> setPointSize ( HUD_TextSize ) ;
825 HUD_TextList.setFont( HUDtext );
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
836 void fgUpdateHUD( void ) {
838 static const SGPropertyNode *enable3d_node = fgGetNode("/sim/hud/enable3d");
839 if ( HUD_style == 1 && enable3d_node->getBoolValue() ) {
840 fgUpdateHUDVirtual();
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 );
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 );
858 void fgUpdateHUDVirtual()
860 FGViewer* view = globals->get_current_view();
862 // Standard fgfs projection, with essentially meaningless clip
863 // planes (we'll map the whole HUD plane to z=-1)
864 glMatrixMode(GL_PROJECTION);
867 gluPerspective(view->get_v_fov(), 1/view->get_aspect_ratio(), 0.1, 10);
869 glMatrixMode(GL_MODELVIEW);
873 // Standard fgfs view direction computation
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);
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
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;
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);
903 glMatrixMode(GL_PROJECTION);
905 glMatrixMode(GL_MODELVIEW);
909 void fgUpdateHUD( GLfloat x_start, GLfloat y_start,
910 GLfloat x_end, GLfloat y_end )
912 glMatrixMode(GL_PROJECTION);
915 gluOrtho2D(x_start, x_end, y_start, y_end);
917 glMatrixMode(GL_MODELVIEW);
923 glMatrixMode(GL_PROJECTION);
925 glMatrixMode(GL_MODELVIEW);
931 if ( !HUD_deque.size() ) // Trust everyone, but ALWAYS cut the cards!
934 HUD_TextList.erase();
935 HUD_LineList.erase();
936 // HUD_StippleLineList.erase();
938 glDisable(GL_DEPTH_TEST);
939 glDisable(GL_LIGHTING);
941 static const SGPropertyNode *heading_enabled
942 = fgGetNode("/autopilot/locks/heading", true);
943 static const SGPropertyNode *altitude_enabled
944 = fgGetNode("/autopilot/locks/altitude", true);
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];
953 if (HUD->isTransparent())
954 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
956 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
958 if (HUD->isAntialiased()) {
959 glEnable(GL_LINE_SMOOTH);
960 glAlphaFunc(GL_GREATER, HUD->alphaClamp());
961 glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
968 for_each(HUD_deque.begin(), HUD_deque.end(), HUDdraw());
970 //HUD_TextList.add( fgText(40, 10, get_formated_gmt_time(), 0) );
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 ) );
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 ) );
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 ) );
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 ) );
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 ) );
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 ) );
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 ) );
1025 HUD_TextList.draw();
1026 HUD_LineList.draw();
1028 // glEnable(GL_LINE_STIPPLE);
1029 // glLineStipple( 1, 0x00FF );
1030 // HUD_StippleLineList.draw();
1031 // glDisable(GL_LINE_STIPPLE);
1033 if (HUD->isAntialiased()) {
1034 glDisable(GL_ALPHA_TEST);
1035 glDisable(GL_LINE_SMOOTH);
1039 glEnable(GL_DEPTH_TEST);
1040 glEnable(GL_LIGHTING);
1045 void fgTextList::draw()
1050 vector<fgText>::iterator curString = List.begin();
1051 vector<fgText>::iterator lastString = List.end();
1053 glPushAttrib(GL_COLOR_BUFFER_BIT);
1055 if (HUD->isTransparent())
1056 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1058 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1060 if (HUD->isAntialiased()) {
1061 glEnable(GL_ALPHA_TEST);
1062 glAlphaFunc(GL_GREATER, HUD->alphaClamp());
1066 for (; curString != lastString; curString++)
1067 curString->Draw(Font,curString->digit);
1070 glDisable(GL_TEXTURE_2D);
1076 // HUD property listener class
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)),
1090 _antialiased(false),
1091 _transparent(false),
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);
1108 void HUD_Properties::valueChanged(SGPropertyNode *node)
1110 if (!strcmp(node->getName(), "current-color")) {
1111 int i = node->getIntValue();
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));
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());
1146 void HUD_Properties::setColor() const
1149 glColor4f(_r, _g, _b, _a);
1151 glColor3f(_r, _g, _b);