/************************************************************************** * hud.c -- hud defines and prototypes * * Written by Michele America, started September 1997. * * Copyright (C) 1997 Michele F. America - micheleamerica@geocities.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id$ * (Log is kept at end of this file) **************************************************************************/ #ifdef WIN32 # include <windows.h> #endif #include <GL/glut.h> #include <stdlib.h> #include <string.h> #ifndef WIN32 # include <values.h> // for MAXINT #endif #include "hud.h" #include <Include/fg_constants.h> #include <Aircraft/aircraft.h> #include <Main/fg_debug.h> #include <Math/fg_random.h> #include <Math/mat3.h> #include <Math/polar.h> #include <Scenery/scenery.h> #include <Time/fg_timer.h> #include <Weather/weather.h> // #define DEBUG #define drawOneLine(x1,y1,x2,y2) glBegin(GL_LINES); \ glVertex2f ((x1),(y1)); glVertex2f ((x2),(y2)); glEnd(); // The following routines obtain information concerntin the aircraft's // current state and return it to calling instrument display routines. // They should eventually be member functions of the aircraft. // double get_throttleval( void ) { fgCONTROLS *pcontrols; pcontrols = current_aircraft.controls; return pcontrols->throttle[0]; // Hack limiting to one engine } double get_aileronval( void ) { fgCONTROLS *pcontrols; pcontrols = current_aircraft.controls; return pcontrols->aileron; } double get_elevatorval( void ) { fgCONTROLS *pcontrols; pcontrols = current_aircraft.controls; return pcontrols->elevator; } double get_elev_trimval( void ) { fgCONTROLS *pcontrols; pcontrols = current_aircraft.controls; return pcontrols->elevator_trim; } double get_rudderval( void ) { fgCONTROLS *pcontrols; pcontrols = current_aircraft.controls; return pcontrols->rudder; } double get_speed( void ) { fgFLIGHT *f; f = current_aircraft.flight; return( FG_V_equiv_kts ); // Make an explicit function call. } double get_aoa( void ) { fgFLIGHT *f; f = current_aircraft.flight; return( FG_Gamma_vert_rad * RAD_TO_DEG ); } double get_roll( void ) { fgFLIGHT *f; f = current_aircraft.flight; return( FG_Phi ); } double get_pitch( void ) { fgFLIGHT *f; f = current_aircraft.flight; return( FG_Theta ); } double get_heading( void ) { fgFLIGHT *f; f = current_aircraft.flight; return( FG_Psi * RAD_TO_DEG ); } double get_altitude( void ) { fgFLIGHT *f; // double rough_elev; f = current_aircraft.flight; // rough_elev = mesh_altitude(FG_Longitude * RAD_TO_ARCSEC, // FG_Latitude * RAD_TO_ARCSEC); return( FG_Altitude * FEET_TO_METER /* -rough_elev */ ); } double get_sideslip( void ) { fgFLIGHT *f; f = current_aircraft.flight; return( FG_Beta ); } // // The following code deals with painting the "instrument" on the display // /* textString - Bitmap font string */ static void textString(int x, int y, char *msg, void *font) { glRasterPos2f(x, y); while (*msg) { glutBitmapCharacter(font, *msg); msg++; } } /* strokeString - Stroke font string */ /* static void strokeString(int x, int y, char *msg, void *font) { glPushMatrix(); glTranslatef(x, y, 0); glScalef(.04, .04, .04); while (*msg) { glutStrokeCharacter(font, *msg); msg++; } glPopMatrix(); } */ /* Draws a measuring scale anywhere on the HUD Needs: HUD_scale struct */ static void drawscale( HUD_scale * pscale ) { double vmin, vmax; int marker_x; int marker_y; register i; char TextScale[80]; int condition; double cur_value = (*(pscale->load_value))(); int disp_val; vmin = cur_value - pscale->half_width_units; // width units == needle travel vmax = cur_value + pscale->half_width_units; // or picture unit span. if( pscale->type == VERTICAL ) // Vertical scale { drawOneLine( pscale->scrn_pos.right, // Vertical scale bar pscale->scrn_pos.bottom, pscale->scrn_pos.right, pscale->scrn_pos.top ); if( pscale->orientation == LEFT ) // Calculate x marker offset marker_x = pscale->scrn_pos.left - 6; else if( pscale->orientation == RIGHT ) marker_x = pscale->scrn_pos.right; // Draw the basic markings for the scale... if( pscale->orientation == LEFT ) { drawOneLine( pscale->scrn_pos.right - 3, // Bottom tick bar pscale->scrn_pos.bottom, pscale->scrn_pos.right, pscale->scrn_pos.bottom ); drawOneLine( pscale->scrn_pos.right - 3, // Top tick bar pscale->scrn_pos.top, pscale->scrn_pos.right, pscale->scrn_pos.top ); drawOneLine( pscale->scrn_pos.right, // Middle tick bar /Index pscale->mid_scr, pscale->scrn_pos.right + 6, pscale->mid_scr ); } else if( pscale->orientation == RIGHT ) { drawOneLine( pscale->scrn_pos.right, pscale->scrn_pos.bottom, pscale->scrn_pos.right+3, pscale->scrn_pos.bottom ); drawOneLine( pscale->scrn_pos.right, pscale->scrn_pos.top, pscale->scrn_pos.right+3, pscale->scrn_pos.top ); drawOneLine( pscale->scrn_pos.right, pscale->mid_scr, pscale->scrn_pos.right-6, pscale->mid_scr ); } // Work through from bottom to top of scale. Calculating where to put // minor and major ticks. for( i = (int)(vmin); i <= (int)(vmax); i++ ) { if( pscale->sub_type == LIMIT ) { // Don't show ticks condition = (i >= pscale->minimum_value); // below Minimum value. } else { if( pscale->sub_type == NOLIMIT ) { condition = 1; } } if( condition ) // Show a tick if necessary { // Calculate the location of this tick marker_y = (int)(pscale->scrn_pos.bottom + (i - vmin) * pscale->factor); // Block calculation artifact from drawing ticks below min coordinate. // Calculation here accounts for text height. if( marker_y < (pscale->scrn_pos.bottom + 4)) { // Magic number!!! continue; } if( (i%pscale->div_min) == 0) { if( pscale->orientation == LEFT ) { drawOneLine( marker_x + 3, marker_y, marker_x + 6, marker_y ); } else { if( pscale->orientation == RIGHT ) { drawOneLine( marker_x, marker_y, marker_x + 3, marker_y ); } } } if( (i%pscale->div_max) == 0 ) { drawOneLine( marker_x, marker_y, marker_x + 6, marker_y ); if(pscale->modulo) { disp_val = i % pscale->modulo; if( !disp_val ) { disp_val = pscale->modulo; } if( disp_val < 0) { disp_val += pscale->modulo; } if( disp_val == pscale->modulo ) { disp_val = 0; } } else { disp_val = i; } sprintf( TextScale, "%d", disp_val ); if( pscale->orientation == LEFT ) { textString( marker_x - 8 * strlen(TextScale) - 2, marker_y - 4, TextScale, GLUT_BITMAP_8_BY_13 ); } else { if( pscale->orientation == RIGHT ) { textString( marker_x + 10, marker_y - 4, TextScale, GLUT_BITMAP_8_BY_13 ); } } } } // End if condition } // End for range of i from vmin to vmax } // End if VERTICAL SCALE TYPE if( pscale->type == HORIZONTAL ) // Horizontal scale { if( pscale->orientation == TOP ) { marker_y = pscale->scrn_pos.bottom; } else { if( pscale->orientation == BOTTOM ) { marker_y = pscale->scrn_pos.bottom - 6; } } drawOneLine( pscale->scrn_pos.left, pscale->scrn_pos.bottom, pscale->scrn_pos.right, pscale->scrn_pos.bottom ); if( pscale->orientation == TOP ) { drawOneLine( pscale->scrn_pos.left, pscale->scrn_pos.bottom, pscale->scrn_pos.left, pscale->scrn_pos.bottom + 3 ); drawOneLine( pscale->scrn_pos.right, pscale->scrn_pos.bottom, pscale->scrn_pos.right, pscale->scrn_pos.bottom + 6 ); drawOneLine( pscale->mid_scr, pscale->scrn_pos.bottom, pscale->mid_scr, pscale->scrn_pos.bottom - 6 ); } else { if( pscale->orientation == BOTTOM ) { drawOneLine( pscale->scrn_pos.left, pscale->scrn_pos.bottom, pscale->scrn_pos.left, pscale->scrn_pos.bottom - 6 ); drawOneLine( pscale->scrn_pos.right, pscale->scrn_pos.bottom, pscale->scrn_pos.right, pscale->scrn_pos.bottom - 6 ); drawOneLine( pscale->mid_scr, pscale->scrn_pos.bottom, pscale->mid_scr, pscale->scrn_pos.bottom + 6 ); } } for( i = (int)(vmin); i <= (int)(vmax); i++ ) // increment is faster than addition { if( pscale->sub_type == LIMIT ) { condition = (i >= pscale->minimum_value); } else { if( pscale->sub_type == NOLIMIT ) { condition = 1; } } if( condition ) { marker_x = (int)(pscale->scrn_pos.left + (i - vmin) * pscale->factor); if( (i%pscale->div_min) == 0 ) { if( pscale->orientation == TOP ) { drawOneLine( marker_x, marker_y, marker_x, marker_y + 3 ); } else { if( pscale->orientation == BOTTOM ) { drawOneLine( marker_x, marker_y + 3, marker_x, marker_y + 6 ); } } } if( (i%pscale->div_max)==0 ) { if(pscale->modulo) { disp_val = i % pscale->modulo; if( !disp_val ) { disp_val = pscale->modulo; } if( disp_val < 0) { disp_val += pscale->modulo; } if( disp_val == pscale->modulo ) { disp_val = 0; } } else { disp_val = i; } sprintf( TextScale, "%d", disp_val ); if( pscale->orientation == TOP ) { drawOneLine( marker_x, marker_y, marker_x, marker_y+6 ); textString ( marker_x - 4 * strlen(TextScale), marker_y + 14, TextScale, GLUT_BITMAP_8_BY_13 ); } else { if( pscale->orientation == BOTTOM ) { drawOneLine( marker_x, marker_y, marker_x, marker_y+6 ); textString ( marker_x - 4 * strlen(TextScale), marker_y - 14, TextScale, GLUT_BITMAP_8_BY_13 ); } } } } } } } // // Draws a climb ladder in the center of the HUD // static void drawladder( HUD_ladder *ladder ) { double vmin, vmax; double roll_value, pitch_value; int marker_x, marker_y; #ifdef DEBUGHUD int mid_scr; #endif int scr_min, scr_max; int x_ini, x_end; int y_ini, y_end; int new_x_ini, new_x_end; int new_y_ini, new_y_end; register i; double factor; char TextLadder[80]; int condition; roll_value = (*ladder->load_roll)(); pitch_value = (*ladder->load_pitch)()*RAD_TO_DEG; vmin = pitch_value - ladder->width_units/2; vmax = pitch_value + ladder->width_units/2; scr_min = ladder->scrn_pos.y - (ladder->scr_height/2); scr_max = scr_min + ladder->scr_height; #ifdef DEBUGHUD mid_scr = scr_min + (scr_max-scr_min)/2; #endif marker_x = ladder->scrn_pos.x - ladder->scr_width/2; factor = (scr_max-scr_min)/ladder->width_units; for( i=(int)(vmin); i<=(int)(vmax); i+=1 ) { condition = 1; if( condition ) { marker_y = (int)(scr_min+(i-vmin)*factor); if( i%ladder->div_units==0 ) { sprintf( TextLadder, "%d", i ); if( ladder->scr_hole == 0 ) { if( i ) { x_ini = ladder->scrn_pos.x - ladder->scr_width/2; } else { x_ini = ladder->scrn_pos.x - ladder->scr_width/2 - 10; } y_ini = marker_y; x_end = ladder->scrn_pos.x + ladder->scr_width/2; y_end = marker_y; new_x_ini = (int)(ladder->scrn_pos.x + \ (x_ini - ladder->scrn_pos.x) * cos(roll_value) - \ (y_ini - ladder->scrn_pos.y) * sin(roll_value)); new_y_ini = (int)(ladder->scrn_pos.y + \ (x_ini - ladder->scrn_pos.x) * sin(roll_value) + \ (y_ini - ladder->scrn_pos.y) * cos(roll_value)); new_x_end = (int)(ladder->scrn_pos.x + \ (x_end - ladder->scrn_pos.x) * cos(roll_value) - \ (y_end - ladder->scrn_pos.y) * sin(roll_value)); new_y_end = (int)(ladder->scrn_pos.y + \ (x_end - ladder->scrn_pos.x) * sin(roll_value) + \ (y_end - ladder->scrn_pos.y) * cos(roll_value)); if( i >= 0 ) { drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end ); } else { glEnable(GL_LINE_STIPPLE); glLineStipple( 1, 0x00FF ); drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end ); glDisable(GL_LINE_STIPPLE); } textString( new_x_ini - 8 * strlen(TextLadder) - 8, new_y_ini - 4, TextLadder, GLUT_BITMAP_8_BY_13 ); textString( new_x_end + 10, new_y_end - 4, TextLadder, GLUT_BITMAP_8_BY_13 ); } else { if( i != 0 ) { x_ini = ladder->scrn_pos.x - ladder->scr_width/2; } else { x_ini = ladder->scrn_pos.x - ladder->scr_width/2 - 10; } y_ini = marker_y; x_end = ladder->scrn_pos.x - ladder->scr_width/2 + ladder->scr_hole/2; y_end = marker_y; new_x_ini = (int)(ladder->scrn_pos.x+ \ (x_ini - ladder->scrn_pos.x) * cos(roll_value) -\ (y_ini - ladder->scrn_pos.y) * sin(roll_value)); new_y_ini = (int)(ladder->scrn_pos.y+ \ (x_ini - ladder->scrn_pos.x) * sin(roll_value) +\ (y_ini - ladder->scrn_pos.y) * cos(roll_value)); new_x_end = (int)(ladder->scrn_pos.x+ \ (x_end - ladder->scrn_pos.x) * cos(roll_value) -\ (y_end - ladder->scrn_pos.y) * sin(roll_value)); new_y_end = (int)(ladder->scrn_pos.y+ \ (x_end - ladder->scrn_pos.x) * sin(roll_value) +\ (y_end - ladder->scrn_pos.y) * cos(roll_value)); if( i >= 0 ) { drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end ); } else { glEnable(GL_LINE_STIPPLE); glLineStipple( 1, 0x00FF ); drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end ); glDisable(GL_LINE_STIPPLE); } textString( new_x_ini - 8 * strlen(TextLadder) - 8, new_y_ini - 4, TextLadder, GLUT_BITMAP_8_BY_13 ); x_ini = ladder->scrn_pos.x + ladder->scr_width/2 - ladder->scr_hole/2; y_ini = marker_y; if( i != 0 ) { x_end = ladder->scrn_pos.x + ladder->scr_width/2; } else { x_end = ladder->scrn_pos.x + ladder->scr_width/2 + 10; } y_end = marker_y; new_x_ini = (int)(ladder->scrn_pos.x + \ (x_ini-ladder->scrn_pos.x)*cos(roll_value) -\ (y_ini-ladder->scrn_pos.y)*sin(roll_value)); new_y_ini = (int)(ladder->scrn_pos.y + \ (x_ini-ladder->scrn_pos.x)*sin(roll_value) +\ (y_ini-ladder->scrn_pos.y)*cos(roll_value)); new_x_end = (int)(ladder->scrn_pos.x + \ (x_end-ladder->scrn_pos.x)*cos(roll_value) -\ (y_end-ladder->scrn_pos.y)*sin(roll_value)); new_y_end = (int)(ladder->scrn_pos.y + \ (x_end-ladder->scrn_pos.x)*sin(roll_value) +\ (y_end-ladder->scrn_pos.y)*cos(roll_value)); if( i >= 0 ) { drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end ); } else { glEnable(GL_LINE_STIPPLE); glLineStipple( 1, 0x00FF ); drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end ); glDisable(GL_LINE_STIPPLE); } textString( new_x_end+10, new_y_end-4, TextLadder, GLUT_BITMAP_8_BY_13 ); } } /* if( i%pscale->div_max==0 ) { drawOneLine( marker_x, marker_y, marker_x+6, marker_y ); sprintf( TextScale, "%d", i ); if( pscale->orientation == LEFT ) { textString( marker_x-8*strlen(TextScale)-2, marker_y-4, TextScale, GLUT_BITMAP_8_BY_13 ); } else if( pscale->orientation == RIGHT ) { textString( marker_x+10, marker_y-4, TextScale, GLUT_BITMAP_8_BY_13 ); } } */ } } } // // Draws an artificial horizon line in the center of the HUD // (with or without a center hole) // // Needs: x_center, y_center, length, hole // static void drawhorizon( HUD_horizon *horizon ) { int x_inc1, y_inc1; int x_inc2, y_inc2; int x_t_inc1, y_t_inc1; int d_bottom_x, d_bottom_y; int d_right_x, d_right_y; int d_top_x, d_top_y; int d_left_x, d_left_y; int inc_b_x, inc_b_y; int inc_r_x, inc_r_y; int inc_t_x, inc_t_y; int inc_l_x, inc_l_y; // struct fgFLIGHT *f = ¤t_aircraft.flight; double sin_bank, cos_bank; double bank_angle, sideslip_angle; double ss_const; // sideslip angle pixels per rad bank_angle = (*horizon->load_roll)(); // Roll limit +/- 30 degrees if( bank_angle < -FG_PI_2/3 ) { bank_angle = -FG_PI_2/3; }else if( bank_angle > FG_PI_2/3 ) { bank_angle = FG_PI_2/3; } sideslip_angle = (*horizon->load_sideslip)(); // Sideslip limit +/- 5 degrees if( sideslip_angle < -FG_PI/36.0 ) { sideslip_angle = -FG_PI/36.0; } else if( sideslip_angle > FG_PI/36.0 ) { sideslip_angle = FG_PI/36.0; } // sin_bank = sin( FG_2PI-FG_Phi ); // cos_bank = cos( FG_2PI-FG_Phi ); sin_bank = sin(FG_2PI-bank_angle); cos_bank = cos(FG_2PI-bank_angle); x_inc1 = (int)(horizon->scr_width * cos_bank); y_inc1 = (int)(horizon->scr_width * sin_bank); x_inc2 = (int)(horizon->scr_hole * cos_bank); y_inc2 = (int)(horizon->scr_hole * sin_bank); x_t_inc1 = (int)(horizon->tee_height * sin_bank); y_t_inc1 = (int)(horizon->tee_height * cos_bank); d_bottom_x = 0; d_bottom_y = (int)(-horizon->scr_hole); d_right_x = (int)(horizon->scr_hole); d_right_y = 0; d_top_x = 0; d_top_y = (int)(horizon->scr_hole); d_left_x = (int)(-horizon->scr_hole); d_left_y = 0; ss_const = (horizon->scr_width*2)/(FG_2PI/36.0);// width represents 10 degrees d_bottom_x += (int)(sideslip_angle*ss_const); d_right_x += (int)(sideslip_angle*ss_const); d_left_x += (int)(sideslip_angle*ss_const); d_top_x += (int)(sideslip_angle*ss_const); inc_b_x = (int)(d_bottom_x*cos_bank-d_bottom_y*sin_bank); inc_b_y = (int)(d_bottom_x*sin_bank+d_bottom_y*cos_bank); inc_r_x = (int)(d_right_x*cos_bank-d_right_y*sin_bank); inc_r_y = (int)(d_right_x*sin_bank+d_right_y*cos_bank); inc_t_x = (int)(d_top_x*cos_bank-d_top_y*sin_bank); inc_t_y = (int)(d_top_x*sin_bank+d_top_y*cos_bank); inc_l_x = (int)(d_left_x*cos_bank-d_left_y*sin_bank); inc_l_y = (int)(d_left_x*sin_bank+d_left_y*cos_bank); if( horizon->scr_hole == 0 ) { drawOneLine( horizon->scrn_pos.x - x_inc1, horizon->scrn_pos.y - y_inc1, \ horizon->scrn_pos.x + x_inc1, horizon->scrn_pos.y + y_inc1 ); } else { drawOneLine( horizon->scrn_pos.x - x_inc1, horizon->scrn_pos.y - y_inc1, \ horizon->scrn_pos.x - x_inc2, horizon->scrn_pos.y - y_inc2 ); drawOneLine( horizon->scrn_pos.x + x_inc2, horizon->scrn_pos.y + y_inc2, \ horizon->scrn_pos.x + x_inc1, horizon->scrn_pos.y + y_inc1 ); } // draw teemarks (?) drawOneLine( horizon->scrn_pos.x + x_inc2, horizon->scrn_pos.y + y_inc2, \ horizon->scrn_pos.x + x_inc2 + x_t_inc1, horizon->scrn_pos.y + y_inc2 - y_t_inc1 ); drawOneLine( horizon->scrn_pos.x - x_inc2, horizon->scrn_pos.y - y_inc2, \ horizon->scrn_pos.x - x_inc2 + x_t_inc1, horizon->scrn_pos.y - y_inc2 - y_t_inc1 ); // draw sideslip diamond (it is not yet positioned correctly ) drawOneLine( horizon->scrn_pos.x + inc_b_x, \ horizon->scrn_pos.y + inc_b_y, \ horizon->scrn_pos.x + inc_r_x, \ horizon->scrn_pos.y + inc_r_y ) drawOneLine( horizon->scrn_pos.x + inc_r_x, \ horizon->scrn_pos.y + inc_r_y, \ horizon->scrn_pos.x + inc_t_x, \ horizon->scrn_pos.y + inc_t_y ); drawOneLine( horizon->scrn_pos.x + inc_t_x, \ horizon->scrn_pos.y + inc_t_y, \ horizon->scrn_pos.x + inc_l_x, \ horizon->scrn_pos.y + inc_l_y ); drawOneLine( horizon->scrn_pos.x + inc_l_x, \ horizon->scrn_pos.y + inc_l_y, \ horizon->scrn_pos.x + inc_b_x, \ horizon->scrn_pos.y + inc_b_y ); /* drawOneLine( horizon->scrn_pos.x + inc_b_x, \ horizon->scrn_pos.y + inc_b_y, \ horizon->scrn_pos.x + inc_r_x, \ horizon->scrn_pos.y + inc_r_y ) drawOneLine( horizon->scrn_pos.x + inc_r_x, \ horizon->scrn_pos.y + inc_r_y, \ horizon->scrn_pos.x + inc_t_x, \ horizon->scrn_pos.y + inc_t_y ); drawOneLine( horizon->scrn_pos.x + inc_t_x, \ horizon->scrn_pos.y + inc_t_y, \ horizon->scrn_pos.x + inc_l_x, \ horizon->scrn_pos.y + inc_l_y ); drawOneLine( horizon->scrn_pos.x + inc_l_x, \ horizon->scrn_pos.y + inc_l_y, \ horizon->scrn_pos.x + inc_b_x, \ horizon->scrn_pos.y + inc_b_y ); */ } // drawControlSurfaces() // Draws a representation of the control surfaces in their current state // anywhere in the HUD // static void drawControlSurfaces( HUD_control_surfaces *ctrl_surf ) { int x_ini, y_ini; int x_end, y_end; /* int x_1, y_1; */ /* int x_2, y_2; */ fgCONTROLS *pCtls; x_ini = ctrl_surf->scrn_pos.x; y_ini = ctrl_surf->scrn_pos.y; x_end = x_ini + 150; y_end = y_ini + 60; drawOneLine( x_ini, y_ini, x_end, y_ini ); drawOneLine( x_ini, y_ini, x_ini, y_end ); drawOneLine( x_ini, y_end, x_end, y_end ); drawOneLine( x_end, y_end, x_end, y_ini ); drawOneLine( x_ini + 30, y_ini, x_ini + 30, y_end ); drawOneLine( x_ini + 30, y_ini + 30, x_ini + 90, y_ini + 30 ); drawOneLine( x_ini + 90, y_ini, x_ini + 90, y_end ); drawOneLine( x_ini + 120, y_ini, x_ini + 120, y_end ); pCtls = current_aircraft.controls; /* Draw elevator diagram */ textString( x_ini + 1, y_end-11, "E", GLUT_BITMAP_8_BY_13 ); drawOneLine( x_ini + 15, y_ini + 5, x_ini + 15, y_ini + 55 ); drawOneLine( x_ini + 14, y_ini + 30, x_ini + 16, y_ini + 30 ); if( pCtls->elevator <= -0.01 || pCtls->elevator >= 0.01 ) { drawOneLine( x_ini + 10, y_ini + 5 + (int)(((pCtls->elevator + 1.0)/2)*50.0), \ x_ini + 20, y_ini + 5 + (int)(((pCtls->elevator + 1.0)/2)*50.0) ); } else { drawOneLine( x_ini + 7, y_ini + 5 + (int)(((pCtls->elevator + 1.0)/2)*50.0), \ x_ini + 23, y_ini + 5 + (int)(((pCtls->elevator + 1.0)/2)*50.0) ); } /* Draw aileron diagram */ textString( x_ini + 30 + 1, y_end-11, "A", GLUT_BITMAP_8_BY_13 ); drawOneLine( x_ini + 35, y_end-15, x_ini + 85, y_end-15 ); drawOneLine( x_ini + 60, y_end-14, x_ini + 60, y_end-16 ); if( pCtls->aileron <= -0.01 || pCtls->aileron >= 0.01 ) { drawOneLine( x_ini + 35 + (int)(((pCtls->aileron + 1.0)/2)*50.0), y_end-20, \ x_ini + 35 + (int)(((pCtls->aileron + 1.0)/2)*50.0), y_end-10 ); } else { drawOneLine( x_ini + 35 + (int)(((pCtls->aileron + 1.0) / 2) * 50.0), y_end - 25, x_ini + 35 + (int)(((pCtls->aileron + 1.0) / 2) * 50.0), y_end - 5 ); } /* Draw rudder diagram */ textString ( x_ini + 30 + 1, y_ini + 21, "R", GLUT_BITMAP_8_BY_13 ); drawOneLine( x_ini + 35, y_ini + 15, x_ini + 85, y_ini + 15 ); drawOneLine( x_ini + 60, y_ini + 14, x_ini + 60, y_ini + 16 ); if( pCtls->rudder <= -0.01 || pCtls->rudder >= 0.01 ) { drawOneLine( x_ini + 35 + (int)(((pCtls->rudder + 1.0) / 2) * 50.0), y_ini + 20, x_ini + 35 + (int)(((pCtls->rudder + 1.0) / 2) * 50.0), y_ini + 10 ); } else { drawOneLine( x_ini + 35 + (int)(((pCtls->rudder + 1.0) / 2) * 50.0), y_ini + 25, x_ini + 35 + (int)(((pCtls->rudder + 1.0) / 2) * 50.0), y_ini + 5 ); } /* Draw throttle diagram */ textString( x_ini + 90 + 1, y_end-11, "T", GLUT_BITMAP_8_BY_13 ); textString( x_ini + 90 + 1, y_end-21, "r", GLUT_BITMAP_8_BY_13 ); drawOneLine( x_ini + 105, y_ini + 5, x_ini + 105, y_ini + 55 ); drawOneLine( x_ini + 100, y_ini + 5 + (int)(pCtls->throttle[0]*50.0), \ x_ini + 110, y_ini + 5 + (int)(pCtls->throttle[0]*50.0) ); /* Draw elevator trim diagram */ textString( x_ini + 121, y_end-11, "T", GLUT_BITMAP_8_BY_13 ); textString( x_ini + 121, y_end-22, "m", GLUT_BITMAP_8_BY_13 ); drawOneLine( x_ini + 135, y_ini + 5, x_ini + 135, y_ini + 55 ); drawOneLine( x_ini + 134, y_ini + 30, x_ini + 136, y_ini + 30 ); if( pCtls->elevator_trim <= -0.01 || pCtls->elevator_trim >= 0.01 ) { drawOneLine( x_ini + 130, y_ini + 5 + (int)(((pCtls->elevator_trim + 1)/2)*50.0), \ x_ini + 140, y_ini + 5 + (int)(((pCtls->elevator_trim + 1.0)/2)*50.0) ); } else { drawOneLine( x_ini + 127, y_ini + 5 + (int)(((pCtls->elevator_trim + 1.0)/2)*50.0), \ x_ini + 143, y_ini + 5 + (int)(((pCtls->elevator_trim + 1.0)/2)*50.0) ); } } // // Draws a label anywhere in the HUD // // static void drawlabel( HUD_label *label ) { char buffer[80]; char string[80]; int posincr; int lenstr; if( !label ) { // Eliminate the possible, but absurd case. return; } if( label->pre_str != NULL) { if( label->post_str != NULL ) { sprintf( buffer, "%s%s%s", label->pre_str, \ label->format, \ label->post_str ); } else { sprintf( buffer, "%s%s", label->pre_str, \ label->format ); } } else { if( label->post_str != NULL ) { sprintf( buffer, "%s%s", label->format, \ label->post_str ); } } // else do nothing if both pre and post strings are nulls. Interesting. sprintf( string, buffer, (*label->load_value)() ); #ifdef DEBUGHUD fgPrintf( FG_COCKPIT, FG_DEBUG, buffer ); fgPrintf( FG_COCKPIT, FG_DEBUG, "\n" ); fgPrintf( FG_COCKPIT, FG_DEBUG, string ); fgPrintf( FG_COCKPIT, FG_DEBUG, "\n" ); #endif lenstr = strlen( string ); if( label->justify == LEFT_JUST ) { posincr = -lenstr*8; } else { if( label->justify == CENTER_JUST ) { posincr = -lenstr*4; } else { if( label->justify == RIGHT_JUST ) { posincr = 0; } } } if( label->size == SMALL ) { textString( label->scrn_pos.x + posincr, label->scrn_pos.y, string, GLUT_BITMAP_8_BY_13); } else { if( label->size == LARGE ) { textString( label->scrn_pos.x + posincr, label->scrn_pos.y, string, GLUT_BITMAP_9_BY_15); } } } // The following routines concern HUD object/component object construction // // fgHUDInit // // Constructs a HUD object and then adds in instruments. At the present // the instruments are hard coded into the routine. Ultimately these need // to be defined by the aircraft's instrumentation records so that the // display for a Piper Cub doesn't show the speed range of a North American // mustange and the engine readouts of a B36! // Hptr fgHUDInit( fgAIRCRAFT *current_aircraft ) { Hptr hud; fgPrintf( FG_COCKPIT, FG_INFO, "Initializing HUD\n" ); hud = (Hptr)calloc(sizeof( HUD),1); if( hud == NULL ) return( NULL ); hud->code = 1; hud->status = 0; // For now lets just hardcode the hud here. // In the future, hud information has to come from the same place // aircraft information came from. fgHUDSetTimeMode( hud, NIGHT ); fgHUDSetBrightness( hud, BRT_LIGHT ); // TBI fgHUDAddHorizon( hud, 330, 100, 40, 5, 10, get_roll, get_sideslip ); fgHUDAddLadder ( hud, 330, 285, 120, 180, 70, 10, NONE, 45, get_roll, get_pitch ); // KIAS fgHUDAddScale ( hud, VERTICAL, LIMIT, 200, 180, 380, 5, 10, LEFT, 0, 100, 50, 0, get_speed ); // Angle of Attack fgHUDAddScale ( hud, HORIZONTAL, NOLIMIT, 180, 250, 410, 1, 5, BOTTOM, -40, 50, 21, 0, get_aoa ); // GYRO COMPASS fgHUDAddScale ( hud, HORIZONTAL, NOLIMIT, 380, 200, 460, 5, 10, TOP, 0, 50, 50, 360, get_heading ); // AMSL fgHUDAddScale ( hud, VERTICAL, LIMIT, 460, 180, 380, 25, 100, RIGHT, 0, 15000, 250, 0, get_altitude); fgHUDAddLabel ( hud, 160, 150, SMALL, NOBLINK, RIGHT_JUST, NULL, " Kts", "%5.0f", get_speed ); fgHUDAddLabel ( hud, 160, 135, SMALL, NOBLINK, RIGHT_JUST, NULL, " m", "%5.0f", get_altitude ); fgHUDAddLabel ( hud, 160, 120, SMALL, NOBLINK, RIGHT_JUST, NULL, " Roll", "%5.2f", get_roll ); fgHUDAddLabel ( hud, 440, 150, SMALL, NOBLINK, RIGHT_JUST, NULL, " AOA", "%5.2f", get_aoa ); fgHUDAddLabel ( hud, 440, 135, SMALL, NOBLINK, RIGHT_JUST, NULL, " Heading", "%5.0f", get_heading ); fgHUDAddLabel ( hud, 440, 120, SMALL, NOBLINK, RIGHT_JUST, NULL, " Sideslip", "%5.2f", get_sideslip ); fgHUDAddControlSurfaces( hud, 10, 10, NULL ); // fgHUDAddControl( hud, HORIZONTAL, 50, 25, get_aileronval ); // was 10, 10 // fgHUDAddControl( hud, VERTICAL, 150, 25, get_elevatorval ); // was 10, 10 // fgHUDAddControl( hud, HORIZONTAL, 250, 25, get_rudderval ); // was 10, 10 return( hud ); } // add_instrument // // This is a stand in for linked list code that will get replaced later // by some more elegant list handling code. void add_instrument( Hptr hud, HIptr pinstrument ) { if( !hud || !pinstrument ) { return; } pinstrument->next = hud->instruments; hud->instruments = pinstrument; } // fgHUDAddHorizon // // Constructs a HUD_horizon "object" and installs it into the hud instrument // list. Hptr fgHUDAddHorizon( Hptr hud, \ int x_pos, \ int y_pos, \ int length, \ int hole_len, \ int tee_height,\ double (*load_roll)(), double (*load_sideslip)() ) { HUD_horizon *phorizon; HUD_instr *pinstrument; if( !hud ) { return NULL; } // construct the parent object pinstrument = (HIptr)calloc(sizeof(HUD_instr),1); if( pinstrument == NULL ) { return( NULL ); } pinstrument->type = HUDhorizon; // ARTIFICIAL_HORIZON; // Construct the horizon phorizon = (HUD_horizon *) calloc( sizeof(HUD_horizon),1); if( phorizon == NULL ) { return( NULL ); } phorizon->scrn_pos.x = x_pos; phorizon->scrn_pos.y = y_pos; phorizon->scr_width = length | 1; phorizon->scr_hole = hole_len; phorizon->tee_height = tee_height; phorizon->load_roll = load_roll; phorizon->load_sideslip = load_sideslip; // Install the horizon in the parent. pinstrument->instr = phorizon; // Install the instrument into hud. add_instrument( hud, pinstrument); return( hud ); } // fgHUDAddScale // // Constructs a HUD_scale "object" and installs it into the hud instrument // list. Hptr fgHUDAddScale( Hptr hud, \ int type, \ int sub_type, \ int scr_pos, \ int scr_min, \ int scr_max, \ int div_min, \ int div_max, \ int orientation, \ int min_value, \ int max_value, \ int width_units, \ int modulus, \ double (*load_value)() ) { HUD_scale *pscale; HUD_instr *pinstrument; if( !hud ) { return NULL; } pinstrument = (HIptr)calloc(sizeof(HUD_instr),1); if( pinstrument == NULL ) { return( NULL ); } pinstrument->type = HUDscale; pscale = ( HUD_scale *)calloc(sizeof(HUD_scale),1); if( pscale == NULL ) { return( NULL ); } pscale->type = type; pscale->sub_type = sub_type; pscale->div_min = div_min; pscale->div_max = div_max; pscale->orientation = orientation; pscale->minimum_value = min_value; pscale->maximum_value = max_value; pscale->modulo = modulus; pscale->load_value = load_value; pscale->half_width_units = width_units / 2.0; pscale->scr_span = scr_max - scr_min; // Run of scan in pix coord pscale->scr_span |= 1; // Force odd span of units. // If span is odd number of units, mid will be correct. // If not it will be high by one coordinate unit. This is // an artifact of integer division needed for screen loc's. pscale->mid_scr = (pscale->scr_span >> 1) + scr_min; // Calculate the number of screen units per indicator unit // We must force floating point calculation or the factor // will be low and miss locate tics by several units. pscale->factor = (double)pscale->scr_span / (double)width_units; switch( type ) { case HORIZONTAL: pscale->scrn_pos.left = scr_min; pscale->scrn_pos.top = scr_pos; pscale->scrn_pos.right = scr_max; pscale->scrn_pos.bottom = scr_pos; break; case VERTICAL: default: pscale->scrn_pos.left = scr_pos; pscale->scrn_pos.top = scr_max; pscale->scrn_pos.right = scr_pos; pscale->scrn_pos.bottom = scr_min; } // Install the scale pinstrument->instr = pscale; // Install the instrument into hud. add_instrument( hud, pinstrument); return( hud ); } // fgHUDAddLabel // // Constructs a HUD_Label object and installs it into the hud instrument // list. Hptr fgHUDAddLabel( Hptr hud, \ int x_pos, \ int y_pos, \ int size, \ int blink, \ int justify, \ char *pre_str, \ char *post_str, \ char *format, \ double (*load_value)() ) { HUD_label *plabel; HUD_instr *pinstrument; if( !hud ) { return NULL; } pinstrument = (HIptr)calloc(sizeof(HUD_instr),1); if( pinstrument == NULL ) { return NULL; } pinstrument->type = HUDlabel; plabel = (HUD_label *)calloc(sizeof(HUD_label),1); if( plabel == NULL ){ return NULL; } plabel->scrn_pos.x = x_pos; plabel->scrn_pos.y = y_pos; plabel->size = size; plabel->blink = blink; plabel->justify = justify; plabel->pre_str = pre_str; plabel->post_str = post_str; plabel->format = format; plabel->load_value = load_value; // Install the label pinstrument->instr = plabel; // Install the instrument into hud. add_instrument( hud, pinstrument); return( hud ); } // fgHUDAddLadder // // Contains code that constructs a ladder "object" and installs it as // a hud instrument in the hud instrument list. // Hptr fgHUDAddLadder( Hptr hud, \ int x_pos, \ int y_pos, \ int scr_width, \ int scr_height, \ int hole_len, \ int div_units, \ int label_pos, \ int width_units, \ double (*load_roll)(), double (*load_pitch)() ) { HUD_ladder *pladder; HUD_instr *pinstrument; if( !hud ) { return NULL; } pinstrument = (HIptr)calloc(sizeof(HUD_instr),1); if( pinstrument == NULL ) return( NULL ); pinstrument->type = HUDladder; pladder = (HUD_ladder *)calloc(sizeof(HUD_ladder),1); if( pladder == NULL ) return( NULL ); pladder->type = 0; // Not used. pladder->scrn_pos.x = x_pos; pladder->scrn_pos.y = y_pos; pladder->scr_width = scr_width; pladder->scr_height = scr_height; pladder->scr_hole = hole_len; pladder->div_units = div_units; pladder->label_position = label_pos; pladder->width_units = width_units; pladder->load_roll = load_roll; pladder->load_pitch = load_pitch; pinstrument->instr = pladder; // Install the instrument into hud. add_instrument( hud, pinstrument); return hud; } // fgHUDAddControlSurfaces() // // Adds the control surface indicators which make up for the lack of seat // of the pants feel. Should be unnecessary with joystick and pedals // enabled. But that is another improvement. Also, what of flaps? Spoilers? // This may need to be expanded or flattened into multiple indicators, // vertical and horizontal. Hptr fgHUDAddControlSurfaces( Hptr hud, int x_pos, int y_pos, double (*load_value)() ) { HUD_control_surfaces *pcontrol_surfaces; HUD_instr *pinstrument; if( !hud ) { return NULL; } // Construct shell pinstrument = (HIptr)calloc(sizeof(HUD_instr),1); if( !pinstrument ) { return NULL; } pinstrument->type = HUDcontrol_surfaces; // Construct core pcontrol_surfaces = (HUD_control_surfaces *)calloc(sizeof(HUD_control),1); if( !pcontrol_surfaces ) { return( NULL ); } pcontrol_surfaces->scrn_pos.x = x_pos; pcontrol_surfaces->scrn_pos.y = y_pos; pcontrol_surfaces->load_value = load_value; pinstrument->instr = pcontrol_surfaces; // Install add_instrument( hud, pinstrument); return hud; } // fgHUDAddControl // // Hptr fgHUDAddControl( Hptr hud, \ int ctrl_x, \ int ctrl_y, \ int ctrl_length, \ int orientation, \ int alignment, \ int min_value, \ int max_value, \ int width_units, \ double (*load_value)() ) { HUD_control *pcontrol; HUD_instr *pinstrument; if( !hud ) { return NULL; } // Construct shell pinstrument = (HIptr)calloc(sizeof(HUD_instr),1); if( !pinstrument ) { return NULL; } pinstrument->type = HUDcontrol; // Construct core pcontrol = (HUD_control *)calloc(sizeof(HUD_control),1); if( !(pcontrol == NULL) ) { return( NULL ); } pcontrol->scrn_pos.x = ctrl_x; pcontrol->scrn_pos.y = ctrl_y; pcontrol->ctrl_length = ctrl_length; pcontrol->orientation = orientation; pcontrol->alignment = alignment; pcontrol->min_value = min_value; pcontrol->max_value = max_value; pcontrol->width_units = width_units; pcontrol->load_value = load_value; // Integrate pinstrument->instr = pcontrol; // Install add_instrument( hud, pinstrument); return hud; } /* Hptr fgHUDAddMovingHorizon( Hptr hud, \ int x_pos, \ int y_pos, \ int length, \ int hole_len, \ int color ) { } Hptr fgHUDAddCircularLadder( Hptr hud, \ int scr_min, \ int scr_max, \ int div_min, \ int div_max, \ int max_value ) { } Hptr fgHUDAddNumDisp( Hptr hud, \ int x_pos, \ int y_pos, \ int size, \ int color, \ int blink, \ char *pre_str, \ char *post_str ) { } */ // fgUpdateHUD // // Performs a once around the list of calls to instruments installed in // the HUD object with requests for redraw. Kinda. It will when this is // all C++. // void fgUpdateHUD( Hptr hud ) { HIptr phud_instr; glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(0, 640, 0, 480); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glColor3f(1.0, 1.0, 1.0); glIndexi(7); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glLineWidth(1); // This is a good improvement, but needs // to respond to a dial instead of time // of day. Of course, we have no dial! if( hud->time_of_day==DAY) { switch (hud->brightness) { case BRT_LIGHT: glColor3f (0.1, 0.9, 0.1); break; case BRT_MEDIUM: glColor3f (0.1, 0.7, 0.0); break; case BRT_DARK: glColor3f (0.0, 0.5, 0.0); } } else if( hud->time_of_day==NIGHT) { switch (hud->brightness) { case BRT_LIGHT: glColor3f (0.9, 0.1, 0.1); break; case BRT_MEDIUM: glColor3f (0.7, 0.0, 0.1); break; case BRT_DARK: glColor3f (0.5, 0.0, 0.0); } } else { glColor3f (0.1, 0.9, 0.1); } fgPrintf( FG_COCKPIT, FG_DEBUG, "HUD Code %d Status %d\n", hud->code, hud->status ); phud_instr = hud->instruments; while( phud_instr ) { /* printf("Drawing Instrument %d\n", phud_instr->type); */ switch (phud_instr->type) { case HUDhorizon: // ARTIFICIAL HORIZON drawhorizon( (pHUDhorizon)phud_instr->instr ); break; case HUDscale: // Need to simplify this call. drawscale ( (pHUDscale) phud_instr->instr ); break; case HUDlabel: drawlabel ( (pHUDlabel) phud_instr->instr ); break; case HUDladder: drawladder( (pHUDladder) phud_instr->instr ); break; // case HUDcontrol: // drawControl( (pHUDcontrol) phud_instr->instr ); // break; case HUDcontrol_surfaces: drawControlSurfaces( (pHUDControlSurfaces) phud_instr->instr ); break; default:; // Ignore anything you don't know about. } phud_instr = phud_instr->next; } glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); } void fgHUDSetTimeMode( Hptr hud, int time_of_day ) { hud->time_of_day = time_of_day; } void fgHUDSetBrightness( Hptr hud, int brightness ) { hud->brightness = brightness; } /* $Log$ /* Revision 1.20 1998/03/09 22:48:40 curt /* Minor "formatting" tweaks. /* * Revision 1.19 1998/02/23 20:18:28 curt * Incorporated Michele America's hud changes. * * Revision 1.18 1998/02/21 14:53:10 curt * Added Charlie's HUD changes. * * Revision 1.17 1998/02/20 00:16:21 curt * Thursday's tweaks. * * Revision 1.16 1998/02/19 13:05:49 curt * Incorporated some HUD tweaks from Michelle America. * Tweaked the sky's sunset/rise colors. * Other misc. tweaks. * * Revision 1.15 1998/02/16 13:38:39 curt * Integrated changes from Charlie Hotchkiss. * * Revision 1.14 1998/02/12 21:59:41 curt * Incorporated code changes contributed by Charlie Hotchkiss * <chotchkiss@namg.us.anritsu.com> * * Revision 1.12 1998/02/09 15:07:48 curt * Minor tweaks. * * Revision 1.11 1998/02/07 15:29:34 curt * Incorporated HUD changes and struct/typedef changes from Charlie Hotchkiss * <chotchkiss@namg.us.anritsu.com> * * Revision 1.10 1998/02/03 23:20:14 curt * Lots of little tweaks to fix various consistency problems discovered by * Solaris' CC. Fixed a bug in fg_debug.c with how the fgPrintf() wrapper * passed arguments along to the real printf(). Also incorporated HUD changes * by Michele America. * * Revision 1.9 1998/01/31 00:43:04 curt * Added MetroWorks patches from Carmen Volpe. * * Revision 1.8 1998/01/27 00:47:51 curt * Incorporated Paul Bleisch's <bleisch@chromatic.com> new debug message * system and commandline/config file processing code. * * Revision 1.7 1998/01/19 18:40:20 curt * Tons of little changes to clean up the code and to remove fatal errors * when building with the c++ compiler. * * Revision 1.6 1997/12/15 23:54:34 curt * Add xgl wrappers for debugging. * Generate terrain normals on the fly. * * Revision 1.5 1997/12/10 22:37:39 curt * Prepended "fg" on the name of all global structures that didn't have it yet. * i.e. "struct WEATHER {}" became "struct fgWEATHER {}" * * Revision 1.4 1997/09/23 00:29:32 curt * Tweaks to get things to compile with gcc-win32. * * Revision 1.3 1997/09/05 14:17:26 curt * More tweaking with stars. * * Revision 1.2 1997/09/04 02:17:30 curt * Shufflin' stuff. * * Revision 1.1 1997/08/29 18:03:22 curt * Initial revision. * */