1 /**************************************************************************
2 * hud.c -- hud defines and prototypes
4 * Written by Michele America, started September 1997.
6 * Copyright (C) 1997 Michele F. America - micheleamerica@geocities.com
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 * (Log is kept at end of this file)
24 **************************************************************************/
40 # include <values.h> // for MAXINT
43 #include <Aircraft/aircraft.h>
44 #include <Debug/fg_debug.h>
45 #include <Include/fg_constants.h>
46 #include <Include/general.h>
47 #include <Math/fg_random.h>
48 #include <Math/mat3.h>
49 #include <Math/polar3d.h>
50 #include <Scenery/scenery.hxx>
51 #include <Time/fg_timer.hxx>
52 #include <Weather/weather.h>
58 #define drawOneLine(x1,y1,x2,y2) glBegin(GL_LINES); \
59 glVertex2f ((x1),(y1)); glVertex2f ((x2),(y2)); glEnd();
62 // The following routines obtain information concerntin the aircraft's
63 // current state and return it to calling instrument display routines.
64 // They should eventually be member functions of the aircraft.
67 double get_frame_rate(void) {
75 double get_throttleval( void )
77 fgCONTROLS *pcontrols;
79 pcontrols = current_aircraft.controls;
80 return pcontrols->throttle[0]; // Hack limiting to one engine
83 double get_aileronval( void )
85 fgCONTROLS *pcontrols;
87 pcontrols = current_aircraft.controls;
88 return pcontrols->aileron;
91 double get_elevatorval( void )
93 fgCONTROLS *pcontrols;
95 pcontrols = current_aircraft.controls;
96 return pcontrols->elevator;
99 double get_elev_trimval( void )
101 fgCONTROLS *pcontrols;
103 pcontrols = current_aircraft.controls;
104 return pcontrols->elevator_trim;
107 double get_rudderval( void )
109 fgCONTROLS *pcontrols;
111 pcontrols = current_aircraft.controls;
112 return pcontrols->rudder;
115 double get_speed( void )
119 f = current_aircraft.flight;
120 return( FG_V_equiv_kts ); // Make an explicit function call.
123 double get_aoa( void )
127 f = current_aircraft.flight;
128 return( FG_Gamma_vert_rad * RAD_TO_DEG );
131 double get_roll( void )
135 f = current_aircraft.flight;
139 double get_pitch( void )
143 f = current_aircraft.flight;
147 double get_heading( void )
151 f = current_aircraft.flight;
152 return( FG_Psi * RAD_TO_DEG );
155 double get_altitude( void )
158 // double rough_elev;
160 f = current_aircraft.flight;
161 // rough_elev = mesh_altitude(FG_Longitude * RAD_TO_ARCSEC,
162 // FG_Latitude * RAD_TO_ARCSEC);
164 return( FG_Altitude * FEET_TO_METER /* -rough_elev */ );
167 double get_sideslip( void )
171 f = current_aircraft.flight;
176 /****************************************************************************/
177 /* Convert degrees to dd mm.mmm' (DMM-Format) */
178 /****************************************************************************/
191 d = (double) ( (int) a);
200 sprintf(dm, "%.0f°%06.3f'", d, m);
204 double get_latitude( void )
207 f = current_aircraft.flight;
209 // return( toDM(FG_Latitude * RAD_TO_DEG) );
210 return((double)((int)( FG_Latitude * RAD_TO_DEG)) );
212 double get_lat_min( void )
217 f = current_aircraft.flight;
219 a = FG_Latitude * RAD_TO_DEG;
223 d = (double) ( (int) a);
224 return( (a - d) * 60.0);
228 double get_longitude( void )
231 f = current_aircraft.flight;
233 // return( toDM(FG_Longitude * RAD_TO_DEG) );
234 return((double)((int) (FG_Longitude * RAD_TO_DEG)) );
236 double get_long_min( void )
241 f = current_aircraft.flight;
243 a = FG_Longitude * RAD_TO_DEG;
247 d = (double) ( (int) a);
248 return( (a - d) * 60.0);
252 // The following code deals with painting the "instrument" on the display
254 /* textString - Bitmap font string */
256 static void textString(int x, int y, char *msg, void *font)
260 glutBitmapCharacter(font, *msg);
265 /* strokeString - Stroke font string */
267 static void strokeString(int x, int y, char *msg, void *font)
270 glTranslatef(x, y, 0);
271 glScalef(.04, .04, .04);
273 glutStrokeCharacter(font, *msg);
282 Draws a measuring scale anywhere on the HUD
285 Needs: HUD_scale struct
288 static void drawscale( HUD_scale * pscale )
296 double cur_value = (*(pscale->load_value))();
299 vmin = cur_value - pscale->half_width_units; // width units == needle travel
300 vmax = cur_value + pscale->half_width_units; // or picture unit span.
303 if( pscale->type == VERTICAL ) // Vertical scale
305 drawOneLine( pscale->scrn_pos.right, // Vertical scale bar
306 pscale->scrn_pos.bottom,
307 pscale->scrn_pos.right,
308 pscale->scrn_pos.top );
310 if( pscale->orientation == LEFT ) // Calculate x marker offset
311 marker_x = pscale->scrn_pos.left - 6;
313 if( pscale->orientation == RIGHT )
314 marker_x = pscale->scrn_pos.right;
316 // Draw the basic markings for the scale...
318 if( pscale->orientation == LEFT )
321 drawOneLine( pscale->scrn_pos.right - 3, // Bottom tick bar
322 pscale->scrn_pos.bottom,
323 pscale->scrn_pos.right,
324 pscale->scrn_pos.bottom );
326 drawOneLine( pscale->scrn_pos.right - 3, // Top tick bar
327 pscale->scrn_pos.top,
328 pscale->scrn_pos.right,
329 pscale->scrn_pos.top );
331 drawOneLine( pscale->scrn_pos.right, // Middle tick bar /Index
333 pscale->scrn_pos.right + 6,
338 if( pscale->orientation == RIGHT )
340 drawOneLine( pscale->scrn_pos.right,
341 pscale->scrn_pos.bottom,
342 pscale->scrn_pos.right+3,
343 pscale->scrn_pos.bottom );
345 drawOneLine( pscale->scrn_pos.right,
346 pscale->scrn_pos.top,
347 pscale->scrn_pos.right+3,
348 pscale->scrn_pos.top );
350 drawOneLine( pscale->scrn_pos.right,
352 pscale->scrn_pos.right-6,
356 // Work through from bottom to top of scale. Calculating where to put
357 // minor and major ticks.
359 for( i = (int)(vmin); i <= (int)(vmax); i++ )
361 if( pscale->sub_type == LIMIT ) { // Don't show ticks
362 condition = (i >= pscale->minimum_value); // below Minimum value.
365 if( pscale->sub_type == NOLIMIT ) {
369 if( condition ) // Show a tick if necessary
371 // Calculate the location of this tick
372 marker_y = (int)(pscale->scrn_pos.bottom + (i - vmin) * pscale->factor);
374 // Block calculation artifact from drawing ticks below min coordinate.
375 // Calculation here accounts for text height.
377 if( marker_y < (pscale->scrn_pos.bottom + 4)) { // Magic number!!!
380 if( (i%pscale->div_min) == 0) {
381 if( pscale->orientation == LEFT )
383 drawOneLine( marker_x + 3, marker_y, marker_x + 6, marker_y );
386 if( pscale->orientation == RIGHT )
388 drawOneLine( marker_x, marker_y, marker_x + 3, marker_y );
392 if( (i%pscale->div_max) == 0 ) {
393 drawOneLine( marker_x, marker_y,
394 marker_x + 6, marker_y );
396 disp_val = i % pscale->modulo;
398 disp_val = pscale->modulo;
401 disp_val += pscale->modulo;
403 if( disp_val == pscale->modulo ) {
410 sprintf( TextScale, "%d", disp_val );
411 if( pscale->orientation == LEFT ) {
412 textString( marker_x - 8 * strlen(TextScale) - 2, marker_y - 4,
413 TextScale, GLUT_BITMAP_8_BY_13 );
416 if( pscale->orientation == RIGHT ) {
417 textString( marker_x + 10, marker_y - 4,
418 TextScale, GLUT_BITMAP_8_BY_13 );
422 } // End if condition
423 } // End for range of i from vmin to vmax
424 } // End if VERTICAL SCALE TYPE
425 if( pscale->type == HORIZONTAL ) // Horizontal scale
427 if( pscale->orientation == TOP ) {
428 marker_y = pscale->scrn_pos.bottom;
431 if( pscale->orientation == BOTTOM ) {
432 marker_y = pscale->scrn_pos.bottom - 6;
435 drawOneLine( pscale->scrn_pos.left,
436 pscale->scrn_pos.bottom,
437 pscale->scrn_pos.right,
438 pscale->scrn_pos.bottom );
440 if( pscale->orientation == TOP )
442 drawOneLine( pscale->scrn_pos.left,
443 pscale->scrn_pos.bottom,
444 pscale->scrn_pos.left,
445 pscale->scrn_pos.bottom + 3 );
447 drawOneLine( pscale->scrn_pos.right,
448 pscale->scrn_pos.bottom,
449 pscale->scrn_pos.right,
450 pscale->scrn_pos.bottom + 6 );
452 drawOneLine( pscale->mid_scr,
453 pscale->scrn_pos.bottom,
455 pscale->scrn_pos.bottom - 6 );
459 if( pscale->orientation == BOTTOM )
461 drawOneLine( pscale->scrn_pos.left,
462 pscale->scrn_pos.bottom,
463 pscale->scrn_pos.left,
464 pscale->scrn_pos.bottom - 6 );
466 drawOneLine( pscale->scrn_pos.right,
467 pscale->scrn_pos.bottom,
468 pscale->scrn_pos.right,
469 pscale->scrn_pos.bottom - 6 );
471 drawOneLine( pscale->mid_scr,
472 pscale->scrn_pos.bottom,
474 pscale->scrn_pos.bottom + 6 );
478 for( i = (int)(vmin); i <= (int)(vmax); i++ ) // increment is faster than addition
480 if( pscale->sub_type == LIMIT ) {
481 condition = (i >= pscale->minimum_value);
484 if( pscale->sub_type == NOLIMIT ) {
489 marker_x = (int)(pscale->scrn_pos.left + (i - vmin) * pscale->factor);
490 if( (i%pscale->div_min) == 0 ) {
491 if( pscale->orientation == TOP )
493 drawOneLine( marker_x, marker_y, marker_x, marker_y + 3 );
496 if( pscale->orientation == BOTTOM )
498 drawOneLine( marker_x, marker_y + 3, marker_x, marker_y + 6 );
502 if( (i%pscale->div_max)==0 )
505 disp_val = i % pscale->modulo;
507 disp_val = pscale->modulo;
510 disp_val += pscale->modulo;
512 if( disp_val == pscale->modulo ) {
519 sprintf( TextScale, "%d", disp_val );
520 if( pscale->orientation == TOP )
522 drawOneLine( marker_x, marker_y, marker_x, marker_y+6 );
523 textString ( marker_x - 4 * strlen(TextScale), marker_y + 14,
524 TextScale, GLUT_BITMAP_8_BY_13 );
527 if( pscale->orientation == BOTTOM )
529 drawOneLine( marker_x, marker_y, marker_x, marker_y+6 );
530 textString ( marker_x - 4 * strlen(TextScale), marker_y - 14,
531 TextScale, GLUT_BITMAP_8_BY_13 );
542 // Draws a climb ladder in the center of the HUD
545 static void drawladder( HUD_ladder *ladder )
548 double roll_value, pitch_value;
549 int marker_x, marker_y;
553 int scr_min, scr_max;
556 int new_x_ini, new_x_end;
557 int new_y_ini, new_y_end;
563 double cos_roll_value, sin_roll_value;
564 // double cos_pitch_value, sin_pitch_value;
566 roll_value = (*ladder->load_roll)();
567 sin_roll_value = sin(roll_value);
568 cos_roll_value = cos(roll_value);
570 pitch_value = (*ladder->load_pitch)()*RAD_TO_DEG;
572 vmin = pitch_value - ladder->width_units/2;
573 vmax = pitch_value + ladder->width_units/2;
575 scr_min = ladder->scrn_pos.y - (ladder->scr_height/2);
576 scr_max = scr_min + ladder->scr_height;
579 mid_scr = scr_min + (scr_max-scr_min)/2;
582 marker_x = ladder->scrn_pos.x - ladder->scr_width/2;
584 factor = (scr_max-scr_min)/ladder->width_units;
586 for( i=(int)(vmin); i<=(int)(vmax); i+=1 )
591 marker_y = (int)(scr_min+(i-vmin)*factor);
592 if( i%ladder->div_units==0 )
594 sprintf( TextLadder, "%d", i );
595 if( ladder->scr_hole == 0 )
598 x_ini = ladder->scrn_pos.x - ladder->scr_width/2;
601 x_ini = ladder->scrn_pos.x - ladder->scr_width/2 - 10;
604 x_end = ladder->scrn_pos.x + ladder->scr_width/2;
606 new_x_ini = (int)(ladder->scrn_pos.x + \
607 (x_ini - ladder->scrn_pos.x) * cos_roll_value - \
608 (y_ini - ladder->scrn_pos.y) * sin_roll_value);
609 new_y_ini = (int)(ladder->scrn_pos.y + \
610 (x_ini - ladder->scrn_pos.x) * sin_roll_value + \
611 (y_ini - ladder->scrn_pos.y) * cos_roll_value);
612 new_x_end = (int)(ladder->scrn_pos.x + \
613 (x_end - ladder->scrn_pos.x) * cos_roll_value - \
614 (y_end - ladder->scrn_pos.y) * sin_roll_value);
615 new_y_end = (int)(ladder->scrn_pos.y + \
616 (x_end - ladder->scrn_pos.x) * sin_roll_value + \
617 (y_end - ladder->scrn_pos.y) * cos_roll_value);
621 drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end );
625 glEnable(GL_LINE_STIPPLE);
626 glLineStipple( 1, 0x00FF );
627 drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end );
628 glDisable(GL_LINE_STIPPLE);
630 textString( new_x_ini - 8 * strlen(TextLadder) - 8,
632 TextLadder, GLUT_BITMAP_8_BY_13 );
633 textString( new_x_end + 10,
635 TextLadder, GLUT_BITMAP_8_BY_13 );
640 x_ini = ladder->scrn_pos.x - ladder->scr_width/2;
643 x_ini = ladder->scrn_pos.x - ladder->scr_width/2 - 10;
646 x_end = ladder->scrn_pos.x - ladder->scr_width/2 + ladder->scr_hole/2;
648 new_x_ini = (int)(ladder->scrn_pos.x+ \
649 (x_ini - ladder->scrn_pos.x) * cos_roll_value -\
650 (y_ini - ladder->scrn_pos.y) * sin_roll_value);
651 new_y_ini = (int)(ladder->scrn_pos.y+ \
652 (x_ini - ladder->scrn_pos.x) * sin_roll_value +\
653 (y_ini - ladder->scrn_pos.y) * cos_roll_value);
654 new_x_end = (int)(ladder->scrn_pos.x+ \
655 (x_end - ladder->scrn_pos.x) * cos_roll_value -\
656 (y_end - ladder->scrn_pos.y) * sin_roll_value);
657 new_y_end = (int)(ladder->scrn_pos.y+ \
658 (x_end - ladder->scrn_pos.x) * sin_roll_value +\
659 (y_end - ladder->scrn_pos.y) * cos_roll_value);
663 drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end );
667 glEnable(GL_LINE_STIPPLE);
668 glLineStipple( 1, 0x00FF );
669 drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end );
670 glDisable(GL_LINE_STIPPLE);
672 textString( new_x_ini - 8 * strlen(TextLadder) - 8,
674 TextLadder, GLUT_BITMAP_8_BY_13 );
676 x_ini = ladder->scrn_pos.x + ladder->scr_width/2 - ladder->scr_hole/2;
679 x_end = ladder->scrn_pos.x + ladder->scr_width/2;
682 x_end = ladder->scrn_pos.x + ladder->scr_width/2 + 10;
685 new_x_ini = (int)(ladder->scrn_pos.x + \
686 (x_ini-ladder->scrn_pos.x)*cos_roll_value -\
687 (y_ini-ladder->scrn_pos.y)*sin_roll_value);
688 new_y_ini = (int)(ladder->scrn_pos.y + \
689 (x_ini-ladder->scrn_pos.x)*sin_roll_value +\
690 (y_ini-ladder->scrn_pos.y)*cos_roll_value);
691 new_x_end = (int)(ladder->scrn_pos.x + \
692 (x_end-ladder->scrn_pos.x)*cos_roll_value -\
693 (y_end-ladder->scrn_pos.y)*sin_roll_value);
694 new_y_end = (int)(ladder->scrn_pos.y + \
695 (x_end-ladder->scrn_pos.x)*sin_roll_value +\
696 (y_end-ladder->scrn_pos.y)*cos_roll_value);
700 drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end );
704 glEnable(GL_LINE_STIPPLE);
705 glLineStipple( 1, 0x00FF );
706 drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end );
707 glDisable(GL_LINE_STIPPLE);
709 textString( new_x_end+10, new_y_end-4,
710 TextLadder, GLUT_BITMAP_8_BY_13 );
713 /* if( i%pscale->div_max==0 )
715 drawOneLine( marker_x, marker_y, marker_x+6, marker_y );
716 sprintf( TextScale, "%d", i );
717 if( pscale->orientation == LEFT )
719 textString( marker_x-8*strlen(TextScale)-2, marker_y-4,
720 TextScale, GLUT_BITMAP_8_BY_13 );
722 else if( pscale->orientation == RIGHT )
724 textString( marker_x+10, marker_y-4,
725 TextScale, GLUT_BITMAP_8_BY_13 );
734 // Draws an artificial horizon line in the center of the HUD
735 // (with or without a center hole)
737 // Needs: x_center, y_center, length, hole
740 static void drawhorizon( HUD_horizon *horizon )
744 int x_t_inc1, y_t_inc1;
746 int d_bottom_x, d_bottom_y;
747 int d_right_x, d_right_y;
748 int d_top_x, d_top_y;
749 int d_left_x, d_left_y;
751 int inc_b_x, inc_b_y;
752 int inc_r_x, inc_r_y;
753 int inc_t_x, inc_t_y;
754 int inc_l_x, inc_l_y;
756 // struct fgFLIGHT *f = ¤t_aircraft.flight;
757 double sin_bank, cos_bank;
758 double bank_angle, sideslip_angle;
759 double ss_const; // sideslip angle pixels per rad
761 bank_angle = (*horizon->load_roll)(); // Roll limit +/- 30 degrees
762 if( bank_angle < -FG_PI_2/3 ) {
763 bank_angle = -FG_PI_2/3;
764 }else if( bank_angle > FG_PI_2/3 ) {
765 bank_angle = FG_PI_2/3;
767 sideslip_angle = (*horizon->load_sideslip)(); // Sideslip limit +/- 5 degrees
768 if( sideslip_angle < -FG_PI/36.0 ) {
769 sideslip_angle = -FG_PI/36.0;
770 } else if( sideslip_angle > FG_PI/36.0 ) {
771 sideslip_angle = FG_PI/36.0;
774 // sin_bank = sin( FG_2PI-FG_Phi );
775 // cos_bank = cos( FG_2PI-FG_Phi );
776 sin_bank = sin(FG_2PI-bank_angle);
777 cos_bank = cos(FG_2PI-bank_angle);
779 x_inc1 = (int)(horizon->scr_width * cos_bank);
780 y_inc1 = (int)(horizon->scr_width * sin_bank);
781 x_inc2 = (int)(horizon->scr_hole * cos_bank);
782 y_inc2 = (int)(horizon->scr_hole * sin_bank);
784 x_t_inc1 = (int)(horizon->tee_height * sin_bank);
785 y_t_inc1 = (int)(horizon->tee_height * cos_bank);
788 d_bottom_y = (int)(-horizon->scr_hole);
789 d_right_x = (int)(horizon->scr_hole);
792 d_top_y = (int)(horizon->scr_hole);
793 d_left_x = (int)(-horizon->scr_hole);
796 ss_const = (horizon->scr_width*2)/(FG_2PI/36.0);// width represents 10 degrees
798 d_bottom_x += (int)(sideslip_angle*ss_const);
799 d_right_x += (int)(sideslip_angle*ss_const);
800 d_left_x += (int)(sideslip_angle*ss_const);
801 d_top_x += (int)(sideslip_angle*ss_const);
803 inc_b_x = (int)(d_bottom_x*cos_bank-d_bottom_y*sin_bank);
804 inc_b_y = (int)(d_bottom_x*sin_bank+d_bottom_y*cos_bank);
805 inc_r_x = (int)(d_right_x*cos_bank-d_right_y*sin_bank);
806 inc_r_y = (int)(d_right_x*sin_bank+d_right_y*cos_bank);
807 inc_t_x = (int)(d_top_x*cos_bank-d_top_y*sin_bank);
808 inc_t_y = (int)(d_top_x*sin_bank+d_top_y*cos_bank);
809 inc_l_x = (int)(d_left_x*cos_bank-d_left_y*sin_bank);
810 inc_l_y = (int)(d_left_x*sin_bank+d_left_y*cos_bank);
812 if( horizon->scr_hole == 0 )
814 drawOneLine( horizon->scrn_pos.x - x_inc1, horizon->scrn_pos.y - y_inc1, \
815 horizon->scrn_pos.x + x_inc1, horizon->scrn_pos.y + y_inc1 );
819 drawOneLine( horizon->scrn_pos.x - x_inc1, horizon->scrn_pos.y - y_inc1, \
820 horizon->scrn_pos.x - x_inc2, horizon->scrn_pos.y - y_inc2 );
821 drawOneLine( horizon->scrn_pos.x + x_inc2, horizon->scrn_pos.y + y_inc2, \
822 horizon->scrn_pos.x + x_inc1, horizon->scrn_pos.y + y_inc1 );
826 drawOneLine( horizon->scrn_pos.x + x_inc2, horizon->scrn_pos.y + y_inc2, \
827 horizon->scrn_pos.x + x_inc2 + x_t_inc1, horizon->scrn_pos.y + y_inc2 - y_t_inc1 );
828 drawOneLine( horizon->scrn_pos.x - x_inc2, horizon->scrn_pos.y - y_inc2, \
829 horizon->scrn_pos.x - x_inc2 + x_t_inc1, horizon->scrn_pos.y - y_inc2 - y_t_inc1 );
831 // draw sideslip diamond (it is not yet positioned correctly )
832 drawOneLine( horizon->scrn_pos.x + inc_b_x, \
833 horizon->scrn_pos.y + inc_b_y, \
834 horizon->scrn_pos.x + inc_r_x, \
835 horizon->scrn_pos.y + inc_r_y )
836 drawOneLine( horizon->scrn_pos.x + inc_r_x, \
837 horizon->scrn_pos.y + inc_r_y, \
838 horizon->scrn_pos.x + inc_t_x, \
839 horizon->scrn_pos.y + inc_t_y );
840 drawOneLine( horizon->scrn_pos.x + inc_t_x, \
841 horizon->scrn_pos.y + inc_t_y, \
842 horizon->scrn_pos.x + inc_l_x, \
843 horizon->scrn_pos.y + inc_l_y );
844 drawOneLine( horizon->scrn_pos.x + inc_l_x, \
845 horizon->scrn_pos.y + inc_l_y, \
846 horizon->scrn_pos.x + inc_b_x, \
847 horizon->scrn_pos.y + inc_b_y );
849 /* drawOneLine( horizon->scrn_pos.x + inc_b_x, \
850 horizon->scrn_pos.y + inc_b_y, \
851 horizon->scrn_pos.x + inc_r_x, \
852 horizon->scrn_pos.y + inc_r_y )
853 drawOneLine( horizon->scrn_pos.x + inc_r_x, \
854 horizon->scrn_pos.y + inc_r_y, \
855 horizon->scrn_pos.x + inc_t_x, \
856 horizon->scrn_pos.y + inc_t_y );
857 drawOneLine( horizon->scrn_pos.x + inc_t_x, \
858 horizon->scrn_pos.y + inc_t_y, \
859 horizon->scrn_pos.x + inc_l_x, \
860 horizon->scrn_pos.y + inc_l_y );
861 drawOneLine( horizon->scrn_pos.x + inc_l_x, \
862 horizon->scrn_pos.y + inc_l_y, \
863 horizon->scrn_pos.x + inc_b_x, \
864 horizon->scrn_pos.y + inc_b_y ); */
867 // drawControlSurfaces()
868 // Draws a representation of the control surfaces in their current state
869 // anywhere in the HUD
872 static void drawControlSurfaces( HUD_control_surfaces *ctrl_surf )
881 x_ini = ctrl_surf->scrn_pos.x;
882 y_ini = ctrl_surf->scrn_pos.y;
886 drawOneLine( x_ini, y_ini, x_end, y_ini );
887 drawOneLine( x_ini, y_ini, x_ini, y_end );
888 drawOneLine( x_ini, y_end, x_end, y_end );
889 drawOneLine( x_end, y_end, x_end, y_ini );
890 drawOneLine( x_ini + 30, y_ini, x_ini + 30, y_end );
891 drawOneLine( x_ini + 30, y_ini + 30, x_ini + 90, y_ini + 30 );
892 drawOneLine( x_ini + 90, y_ini, x_ini + 90, y_end );
893 drawOneLine( x_ini + 120, y_ini, x_ini + 120, y_end );
895 pCtls = current_aircraft.controls;
897 /* Draw elevator diagram */
898 textString( x_ini + 1, y_end-11, "E", GLUT_BITMAP_8_BY_13 );
899 drawOneLine( x_ini + 15, y_ini + 5, x_ini + 15, y_ini + 55 );
900 drawOneLine( x_ini + 14, y_ini + 30, x_ini + 16, y_ini + 30 );
901 tmp = y_ini + 5 + (int)(((pCtls->elevator + 1.0)/2)*50.0);
902 if( pCtls->elevator <= -0.01 || pCtls->elevator >= 0.01 )
904 drawOneLine( x_ini + 10, tmp, x_ini + 20, tmp );
908 drawOneLine( x_ini + 7, tmp, x_ini + 23, tmp);
911 /* Draw aileron diagram */
912 textString( x_ini + 30 + 1, y_end-11, "A", GLUT_BITMAP_8_BY_13 );
913 drawOneLine( x_ini + 35, y_end-15, x_ini + 85, y_end-15 );
914 drawOneLine( x_ini + 60, y_end-14, x_ini + 60, y_end-16 );
915 tmp = x_ini + 35 + (int)(((pCtls->aileron + 1.0)/2)*50.0);
916 if( pCtls->aileron <= -0.01 || pCtls->aileron >= 0.01 )
918 drawOneLine( tmp, y_end-20, tmp, y_end-10 );
922 drawOneLine( tmp, y_end - 25, tmp, y_end - 5 );
925 /* Draw rudder diagram */
926 textString ( x_ini + 30 + 1, y_ini + 21, "R", GLUT_BITMAP_8_BY_13 );
927 drawOneLine( x_ini + 35, y_ini + 15, x_ini + 85, y_ini + 15 );
928 drawOneLine( x_ini + 60, y_ini + 14, x_ini + 60, y_ini + 16 );
930 tmp = x_ini + 35 + (int)(((pCtls->rudder + 1.0) / 2) * 50.0);
931 if( pCtls->rudder <= -0.01 || pCtls->rudder >= 0.01 )
933 drawOneLine( tmp, y_ini + 20, tmp, y_ini + 10 );
937 drawOneLine( tmp, y_ini + 25, tmp, y_ini + 5 );
941 /* Draw throttle diagram */
942 textString( x_ini + 90 + 1, y_end-11, "T", GLUT_BITMAP_8_BY_13 );
943 textString( x_ini + 90 + 1, y_end-21, "r", GLUT_BITMAP_8_BY_13 );
944 drawOneLine( x_ini + 105, y_ini + 5, x_ini + 105, y_ini + 55 );
945 tmp = y_ini + 5 + (int)(pCtls->throttle[0]*50.0);
946 drawOneLine( x_ini + 100, tmp, x_ini + 110, tmp);
949 /* Draw elevator trim diagram */
950 textString( x_ini + 121, y_end-11, "T", GLUT_BITMAP_8_BY_13 );
951 textString( x_ini + 121, y_end-22, "m", GLUT_BITMAP_8_BY_13 );
952 drawOneLine( x_ini + 135, y_ini + 5, x_ini + 135, y_ini + 55 );
953 drawOneLine( x_ini + 134, y_ini + 30, x_ini + 136, y_ini + 30 );
955 tmp = y_ini + 5 + (int)(((pCtls->elevator_trim + 1)/2)*50.0);
956 if( pCtls->elevator_trim <= -0.01 || pCtls->elevator_trim >= 0.01 )
958 drawOneLine( x_ini + 130, tmp, x_ini + 140, tmp);
962 drawOneLine( x_ini + 127, tmp, x_ini + 143, tmp);
968 // Draws a label anywhere in the HUD
972 static void drawlabel( HUD_label *label )
979 if( !label ) { // Eliminate the possible, but absurd case.
983 if( label->pre_str != NULL) {
984 if( label->post_str != NULL ) {
985 sprintf( buffer, "%s%s%s", label->pre_str, \
990 sprintf( buffer, "%s%s", label->pre_str, \
995 if( label->post_str != NULL ) {
996 sprintf( buffer, "%s%s", label->format, \
999 } // else do nothing if both pre and post strings are nulls. Interesting.
1002 sprintf( string, buffer, (*label->load_value)() );
1004 fgPrintf( FG_COCKPIT, FG_DEBUG, buffer );
1005 fgPrintf( FG_COCKPIT, FG_DEBUG, "\n" );
1006 fgPrintf( FG_COCKPIT, FG_DEBUG, string );
1007 fgPrintf( FG_COCKPIT, FG_DEBUG, "\n" );
1009 lenstr = strlen( string );
1010 if( label->justify == LEFT_JUST ) {
1011 posincr = -lenstr*8;
1014 if( label->justify == CENTER_JUST ) {
1015 posincr = -lenstr*4;
1018 if( label->justify == RIGHT_JUST ) {
1024 if( label->size == SMALL ) {
1025 textString( label->scrn_pos.x + posincr, label->scrn_pos.y,
1026 string, GLUT_BITMAP_8_BY_13);
1029 if( label->size == LARGE ) {
1030 textString( label->scrn_pos.x + posincr, label->scrn_pos.y,
1031 string, GLUT_BITMAP_9_BY_15);
1035 // The following routines concern HUD object/component object construction
1040 // Constructs a HUD object and then adds in instruments. At the present
1041 // the instruments are hard coded into the routine. Ultimately these need
1042 // to be defined by the aircraft's instrumentation records so that the
1043 // display for a Piper Cub doesn't show the speed range of a North American
1044 // mustange and the engine readouts of a B36!
1046 Hptr fgHUDInit( fgAIRCRAFT *current_aircraft )
1050 fgPrintf( FG_COCKPIT, FG_INFO, "Initializing HUD\n" );
1052 hud = (Hptr)calloc(sizeof( HUD),1);
1059 // For now lets just hardcode the hud here.
1060 // In the future, hud information has to come from the same place
1061 // aircraft information came from.
1063 fgHUDSetTimeMode( hud, NIGHT );
1064 fgHUDSetBrightness( hud, BRT_LIGHT );
1067 fgHUDAddHorizon( hud, 330, 100, 40, 5, 10, get_roll, get_sideslip );
1069 fgHUDAddLadder ( hud, 330, 285, 120, 180, 70, 10,
1070 NONE, 45, get_roll, get_pitch );
1072 fgHUDAddScale ( hud, VERTICAL, LIMIT, 200, 180, 380, 5, 10,
1073 LEFT, 0, 100, 50, 0, get_speed );
1075 fgHUDAddScale ( hud, HORIZONTAL, NOLIMIT, 180, 250, 410, 1, 5,
1076 BOTTOM, -40, 50, 21, 0, get_aoa );
1078 fgHUDAddScale ( hud, HORIZONTAL, NOLIMIT, 380, 200, 460, 5, 10,
1079 TOP, 0, 50, 50, 360, get_heading );
1081 fgHUDAddScale ( hud, VERTICAL, LIMIT, 460, 180, 380, 25, 100,
1082 RIGHT, 0, 15000, 250, 0, get_altitude);
1084 fgHUDAddLabel ( hud, 160, 150, SMALL, NOBLINK,
1085 RIGHT_JUST, NULL, " Kts", "%5.0f", get_speed );
1086 fgHUDAddLabel ( hud, 160, 135, SMALL, NOBLINK,
1087 RIGHT_JUST, NULL, " m", "%5.0f", get_altitude );
1088 fgHUDAddLabel ( hud, 160, 120, SMALL, NOBLINK,
1089 RIGHT_JUST, NULL, " Roll", "%5.2f", get_roll );
1090 fgHUDAddLabel ( hud, 160, 105, SMALL, NOBLINK,
1091 RIGHT_JUST, "Lat ", "d", "%03.0f", get_latitude );
1092 fgHUDAddLabel ( hud, 160, 90, SMALL, NOBLINK,
1093 RIGHT_JUST, NULL, " m", "%05.2f", get_lat_min );
1095 fgHUDAddLabel ( hud, 440, 150, SMALL, NOBLINK,
1096 RIGHT_JUST, NULL, " AOA", "%5.2f", get_aoa );
1097 fgHUDAddLabel ( hud, 440, 135, SMALL, NOBLINK,
1098 RIGHT_JUST, NULL, " Heading", "%5.0f", get_heading );
1099 fgHUDAddLabel ( hud, 440, 120, SMALL, NOBLINK,
1100 RIGHT_JUST, NULL, " Sideslip", "%5.2f", get_sideslip );
1101 fgHUDAddLabel ( hud, 440, 105, SMALL, NOBLINK,
1102 RIGHT_JUST, "Lon ", "d", "%04.0f", get_longitude );
1103 fgHUDAddLabel ( hud, 440, 90, SMALL, NOBLINK,
1104 RIGHT_JUST, NULL, " m", "%05.2f", get_long_min );
1105 fgHUDAddLabel ( hud, 10,470, SMALL, NOBLINK,
1106 RIGHT_JUST, "Frame rate =", NULL, "%2.2f ", get_frame_rate);
1108 fgHUDAddControlSurfaces( hud, 10, 10, NULL );
1110 // fgHUDAddControl( hud, HORIZONTAL, 50, 25, get_aileronval ); // was 10, 10
1111 // fgHUDAddControl( hud, VERTICAL, 150, 25, get_elevatorval ); // was 10, 10
1112 // fgHUDAddControl( hud, HORIZONTAL, 250, 25, get_rudderval ); // was 10, 10
1120 // This is a stand in for linked list code that will get replaced later
1121 // by some more elegant list handling code.
1123 void add_instrument( Hptr hud, HIptr pinstrument )
1125 if( !hud || !pinstrument ) {
1129 pinstrument->next = hud->instruments;
1130 hud->instruments = pinstrument;
1136 // Constructs a HUD_horizon "object" and installs it into the hud instrument
1139 Hptr fgHUDAddHorizon( Hptr hud, \
1145 double (*load_roll)(),
1146 double (*load_sideslip)() )
1148 HUD_horizon *phorizon;
1149 HUD_instr *pinstrument;
1154 // construct the parent object
1155 pinstrument = (HIptr)calloc(sizeof(HUD_instr),1);
1156 if( pinstrument == NULL ) {
1159 pinstrument->type = HUDhorizon; // ARTIFICIAL_HORIZON;
1161 // Construct the horizon
1162 phorizon = (HUD_horizon *) calloc( sizeof(HUD_horizon),1);
1163 if( phorizon == NULL ) {
1167 phorizon->scrn_pos.x = x_pos;
1168 phorizon->scrn_pos.y = y_pos;
1169 phorizon->scr_width = length | 1;
1170 phorizon->scr_hole = hole_len;
1171 phorizon->tee_height = tee_height;
1172 phorizon->load_roll = load_roll;
1173 phorizon->load_sideslip = load_sideslip;
1174 // Install the horizon in the parent.
1175 pinstrument->instr = phorizon;
1176 // Install the instrument into hud.
1177 add_instrument( hud, pinstrument);
1184 // Constructs a HUD_scale "object" and installs it into the hud instrument
1187 Hptr fgHUDAddScale( Hptr hud, \
1200 double (*load_value)() )
1203 HUD_instr *pinstrument;
1209 pinstrument = (HIptr)calloc(sizeof(HUD_instr),1);
1210 if( pinstrument == NULL ) {
1214 pinstrument->type = HUDscale;
1216 pscale = ( HUD_scale *)calloc(sizeof(HUD_scale),1);
1217 if( pscale == NULL ) {
1221 pscale->type = type;
1222 pscale->sub_type = sub_type;
1223 pscale->div_min = div_min;
1224 pscale->div_max = div_max;
1225 pscale->orientation = orientation;
1226 pscale->minimum_value = min_value;
1227 pscale->maximum_value = max_value;
1228 pscale->modulo = modulus;
1229 pscale->load_value = load_value;
1231 pscale->half_width_units = width_units / 2.0;
1232 pscale->scr_span = scr_max - scr_min; // Run of scan in pix coord
1233 pscale->scr_span |= 1; // Force odd span of units.
1234 // If span is odd number of units, mid will be correct.
1235 // If not it will be high by one coordinate unit. This is
1236 // an artifact of integer division needed for screen loc's.
1238 pscale->mid_scr = (pscale->scr_span >> 1) + scr_min;
1240 // Calculate the number of screen units per indicator unit
1241 // We must force floating point calculation or the factor
1242 // will be low and miss locate tics by several units.
1244 pscale->factor = (double)pscale->scr_span / (double)width_units;
1248 pscale->scrn_pos.left = scr_min;
1249 pscale->scrn_pos.top = scr_pos;
1250 pscale->scrn_pos.right = scr_max;
1251 pscale->scrn_pos.bottom = scr_pos;
1256 pscale->scrn_pos.left = scr_pos;
1257 pscale->scrn_pos.top = scr_max;
1258 pscale->scrn_pos.right = scr_pos;
1259 pscale->scrn_pos.bottom = scr_min;
1262 // Install the scale
1263 pinstrument->instr = pscale;
1264 // Install the instrument into hud.
1265 add_instrument( hud, pinstrument);
1272 // Constructs a HUD_Label object and installs it into the hud instrument
1274 Hptr fgHUDAddLabel( Hptr hud, \
1283 double (*load_value)() )
1286 HUD_instr *pinstrument;
1292 pinstrument = (HIptr)calloc(sizeof(HUD_instr),1);
1293 if( pinstrument == NULL ) {
1296 pinstrument->type = HUDlabel;
1298 plabel = (HUD_label *)calloc(sizeof(HUD_label),1);
1299 if( plabel == NULL ){
1303 plabel->scrn_pos.x = x_pos;
1304 plabel->scrn_pos.y = y_pos;
1305 plabel->size = size;
1306 plabel->blink = blink;
1307 plabel->justify = justify;
1308 plabel->pre_str = pre_str;
1309 plabel->post_str = post_str;
1310 plabel->format = format;
1311 plabel->load_value = load_value;
1312 // Install the label
1313 pinstrument->instr = plabel;
1314 // Install the instrument into hud.
1315 add_instrument( hud, pinstrument);
1322 // Contains code that constructs a ladder "object" and installs it as
1323 // a hud instrument in the hud instrument list.
1325 Hptr fgHUDAddLadder( Hptr hud, \
1334 double (*load_roll)(),
1335 double (*load_pitch)() )
1337 HUD_ladder *pladder;
1338 HUD_instr *pinstrument;
1344 pinstrument = (HIptr)calloc(sizeof(HUD_instr),1);
1345 if( pinstrument == NULL )
1348 pinstrument->type = HUDladder;
1350 pladder = (HUD_ladder *)calloc(sizeof(HUD_ladder),1);
1351 if( pladder == NULL )
1354 pladder->type = 0; // Not used.
1355 pladder->scrn_pos.x = x_pos;
1356 pladder->scrn_pos.y = y_pos;
1357 pladder->scr_width = scr_width;
1358 pladder->scr_height = scr_height;
1359 pladder->scr_hole = hole_len;
1360 pladder->div_units = div_units;
1361 pladder->label_position = label_pos;
1362 pladder->width_units = width_units;
1363 pladder->load_roll = load_roll;
1364 pladder->load_pitch = load_pitch;
1366 pinstrument->instr = pladder;
1367 // Install the instrument into hud.
1368 add_instrument( hud, pinstrument);
1372 // fgHUDAddControlSurfaces()
1374 // Adds the control surface indicators which make up for the lack of seat
1375 // of the pants feel. Should be unnecessary with joystick and pedals
1376 // enabled. But that is another improvement. Also, what of flaps? Spoilers?
1377 // This may need to be expanded or flattened into multiple indicators,
1378 // vertical and horizontal.
1380 Hptr fgHUDAddControlSurfaces( Hptr hud,
1383 double (*load_value)() )
1385 HUD_control_surfaces *pcontrol_surfaces;
1386 HUD_instr *pinstrument;
1393 pinstrument = (HIptr)calloc(sizeof(HUD_instr),1);
1394 if( !pinstrument ) {
1397 pinstrument->type = HUDcontrol_surfaces;
1400 pcontrol_surfaces = (HUD_control_surfaces *)calloc(sizeof(HUD_control),1);
1401 if( !pcontrol_surfaces ) {
1405 pcontrol_surfaces->scrn_pos.x = x_pos;
1406 pcontrol_surfaces->scrn_pos.y = y_pos;
1407 pcontrol_surfaces->load_value = load_value;
1409 pinstrument->instr = pcontrol_surfaces;
1411 add_instrument( hud, pinstrument);
1420 Hptr fgHUDAddControl( Hptr hud, \
1429 double (*load_value)() )
1431 HUD_control *pcontrol;
1432 HUD_instr *pinstrument;
1439 pinstrument = (HIptr)calloc(sizeof(HUD_instr),1);
1440 if( !pinstrument ) {
1443 pinstrument->type = HUDcontrol;
1446 pcontrol = (HUD_control *)calloc(sizeof(HUD_control),1);
1447 if( !(pcontrol == NULL) ) {
1450 pcontrol->scrn_pos.x = ctrl_x;
1451 pcontrol->scrn_pos.y = ctrl_y;
1452 pcontrol->ctrl_length = ctrl_length;
1453 pcontrol->orientation = orientation;
1454 pcontrol->alignment = alignment;
1455 pcontrol->min_value = min_value;
1456 pcontrol->max_value = max_value;
1457 pcontrol->width_units = width_units;
1458 pcontrol->load_value = load_value;
1460 pinstrument->instr = pcontrol;
1462 add_instrument( hud, pinstrument);
1468 Hptr fgHUDAddMovingHorizon( Hptr hud, \
1478 Hptr fgHUDAddCircularLadder( Hptr hud, \
1488 Hptr fgHUDAddNumDisp( Hptr hud, \
1503 // Performs a once around the list of calls to instruments installed in
1504 // the HUD object with requests for redraw. Kinda. It will when this is
1508 void fgUpdateHUD( Hptr hud ) {
1511 glMatrixMode(GL_PROJECTION);
1515 gluOrtho2D(0, 640, 0, 480);
1516 glMatrixMode(GL_MODELVIEW);
1520 glColor3f(1.0, 1.0, 1.0);
1523 glDisable(GL_DEPTH_TEST);
1524 glDisable(GL_LIGHTING);
1527 // This is a good improvement, but needs
1528 // to respond to a dial instead of time
1529 // of day. Of course, we have no dial!
1530 if( hud->time_of_day==DAY) {
1531 switch (hud->brightness) {
1533 glColor3f (0.1, 0.9, 0.1);
1536 glColor3f (0.1, 0.7, 0.0);
1539 glColor3f (0.0, 0.5, 0.0);
1542 else if( hud->time_of_day==NIGHT) {
1543 switch (hud->brightness) {
1545 glColor3f (0.9, 0.1, 0.1);
1548 glColor3f (0.7, 0.0, 0.1);
1551 glColor3f (0.5, 0.0, 0.0);
1555 glColor3f (0.1, 0.9, 0.1);
1558 fgPrintf( FG_COCKPIT, FG_DEBUG, "HUD Code %d Status %d\n",
1559 hud->code, hud->status );
1561 phud_instr = hud->instruments;
1562 while( phud_instr ) {
1563 /* printf("Drawing Instrument %d\n", phud_instr->type); */
1565 switch (phud_instr->type) {
1566 case HUDhorizon: // ARTIFICIAL HORIZON
1567 drawhorizon( (pHUDhorizon)phud_instr->instr );
1570 case HUDscale: // Need to simplify this call.
1571 drawscale ( (pHUDscale) phud_instr->instr );
1575 drawlabel ( (pHUDlabel) phud_instr->instr );
1579 drawladder( (pHUDladder) phud_instr->instr );
1583 // drawControl( (pHUDcontrol) phud_instr->instr );
1586 case HUDcontrol_surfaces:
1587 drawControlSurfaces( (pHUDControlSurfaces) phud_instr->instr );
1590 default:; // Ignore anything you don't know about.
1593 phud_instr = phud_instr->next;
1596 glEnable(GL_DEPTH_TEST);
1597 glEnable(GL_LIGHTING);
1598 glMatrixMode(GL_PROJECTION);
1600 glMatrixMode(GL_MODELVIEW);
1604 void fgHUDSetTimeMode( Hptr hud, int time_of_day )
1607 hud->time_of_day = time_of_day;
1611 void fgHUDSetBrightness( Hptr hud, int brightness )
1614 hud->brightness = brightness;
1619 /* Revision 1.5 1998/05/06 03:15:08 curt
1620 /* Durk Talsma contributed a graphical frame rate counter which is displayed
1621 /* as part of the HUD.
1623 * Revision 1.4 1998/05/03 00:46:46 curt
1624 * polar.h -> polar3d.h
1626 * Revision 1.3 1998/04/30 12:36:02 curt
1627 * C++-ifying a couple source files.
1629 * Revision 1.2 1998/04/25 22:06:27 curt
1630 * Edited cvs log messages in source files ... bad bad bad!
1632 * Revision 1.1 1998/04/24 00:45:57 curt
1633 * C++-ifing the code a bit.
1635 * Revision 1.22 1998/04/18 04:14:02 curt
1636 * Moved fg_debug.c to it's own library.
1638 * Revision 1.21 1998/04/03 21:55:28 curt
1639 * Converting to Gnu autoconf system.
1642 * Revision 1.20 1998/03/09 22:48:40 curt
1643 * Minor "formatting" tweaks.
1645 * Revision 1.19 1998/02/23 20:18:28 curt
1646 * Incorporated Michele America's hud changes.
1648 * Revision 1.18 1998/02/21 14:53:10 curt
1649 * Added Charlie's HUD changes.
1651 * Revision 1.17 1998/02/20 00:16:21 curt
1652 * Thursday's tweaks.
1654 * Revision 1.16 1998/02/19 13:05:49 curt
1655 * Incorporated some HUD tweaks from Michelle America.
1656 * Tweaked the sky's sunset/rise colors.
1657 * Other misc. tweaks.
1659 * Revision 1.15 1998/02/16 13:38:39 curt
1660 * Integrated changes from Charlie Hotchkiss.
1662 * Revision 1.14 1998/02/12 21:59:41 curt
1663 * Incorporated code changes contributed by Charlie Hotchkiss
1664 * <chotchkiss@namg.us.anritsu.com>
1666 * Revision 1.12 1998/02/09 15:07:48 curt
1669 * Revision 1.11 1998/02/07 15:29:34 curt
1670 * Incorporated HUD changes and struct/typedef changes from Charlie Hotchkiss
1671 * <chotchkiss@namg.us.anritsu.com>
1673 * Revision 1.10 1998/02/03 23:20:14 curt
1674 * Lots of little tweaks to fix various consistency problems discovered by
1675 * Solaris' CC. Fixed a bug in fg_debug.c with how the fgPrintf() wrapper
1676 * passed arguments along to the real printf(). Also incorporated HUD changes
1677 * by Michele America.
1679 * Revision 1.9 1998/01/31 00:43:04 curt
1680 * Added MetroWorks patches from Carmen Volpe.
1682 * Revision 1.8 1998/01/27 00:47:51 curt
1683 * Incorporated Paul Bleisch's <pbleisch@acm.org> new debug message
1684 * system and commandline/config file processing code.
1686 * Revision 1.7 1998/01/19 18:40:20 curt
1687 * Tons of little changes to clean up the code and to remove fatal errors
1688 * when building with the c++ compiler.
1690 * Revision 1.6 1997/12/15 23:54:34 curt
1691 * Add xgl wrappers for debugging.
1692 * Generate terrain normals on the fly.
1694 * Revision 1.5 1997/12/10 22:37:39 curt
1695 * Prepended "fg" on the name of all global structures that didn't have it yet.
1696 * i.e. "struct WEATHER {}" became "struct fgWEATHER {}"
1698 * Revision 1.4 1997/09/23 00:29:32 curt
1699 * Tweaks to get things to compile with gcc-win32.
1701 * Revision 1.3 1997/09/05 14:17:26 curt
1702 * More tweaking with stars.
1704 * Revision 1.2 1997/09/04 02:17:30 curt
1707 * Revision 1.1 1997/08/29 18:03:22 curt