]> git.mxchange.org Git - flightgear.git/blob - Cockpit/hud.cxx
Durk Talsma contributed a graphical frame rate counter which is displayed
[flightgear.git] / Cockpit / hud.cxx
1 /**************************************************************************
2  * hud.c -- hud defines and prototypes
3  *
4  * Written by Michele America, started September 1997.
5  *
6  * Copyright (C) 1997  Michele F. America  - micheleamerica@geocities.com
7  *
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.
12  *
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.
17  *
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.
21  *
22  * $Id$
23  * (Log is kept at end of this file)
24  **************************************************************************/
25  
26
27 #ifdef HAVE_CONFIG_H
28 #  include <config.h>
29 #endif
30
31 #ifdef HAVE_WINDOWS_H
32 #  include <windows.h>
33 #endif
34
35 #include <GL/glut.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #ifdef HAVE_VALUES_H
40 #  include <values.h>  // for MAXINT
41 #endif
42
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>
53
54 #include "hud.hxx"
55
56 // #define DEBUG
57
58 #define drawOneLine(x1,y1,x2,y2)  glBegin(GL_LINES);  \
59         glVertex2f ((x1),(y1)); glVertex2f ((x2),(y2)); glEnd();
60
61
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.
65 //
66
67 double get_frame_rate(void) {
68     fgGENERAL *g;
69
70     g = &general;
71
72     return g->frame_rate;
73 }
74
75 double get_throttleval( void )
76 {
77         fgCONTROLS *pcontrols;
78
79   pcontrols = current_aircraft.controls;
80   return pcontrols->throttle[0];     // Hack limiting to one engine
81 }
82
83 double get_aileronval( void )
84 {
85         fgCONTROLS *pcontrols;
86
87   pcontrols = current_aircraft.controls;
88   return pcontrols->aileron;
89 }
90
91 double get_elevatorval( void )
92 {
93         fgCONTROLS *pcontrols;
94
95   pcontrols = current_aircraft.controls;
96   return pcontrols->elevator;
97 }
98
99 double get_elev_trimval( void )
100 {
101         fgCONTROLS *pcontrols;
102
103   pcontrols = current_aircraft.controls;
104   return pcontrols->elevator_trim;
105 }
106
107 double get_rudderval( void )
108 {
109         fgCONTROLS *pcontrols;
110
111   pcontrols = current_aircraft.controls;
112   return pcontrols->rudder;
113 }
114
115 double get_speed( void )
116 {
117         fgFLIGHT *f;
118
119         f = current_aircraft.flight;
120         return( FG_V_equiv_kts );    // Make an explicit function call.
121 }
122
123 double get_aoa( void )
124 {
125         fgFLIGHT *f;
126               
127         f = current_aircraft.flight;
128         return( FG_Gamma_vert_rad * RAD_TO_DEG );
129 }
130
131 double get_roll( void )
132 {
133         fgFLIGHT *f;
134
135         f = current_aircraft.flight;
136         return( FG_Phi );
137 }
138
139 double get_pitch( void )
140 {
141         fgFLIGHT *f;
142               
143         f = current_aircraft.flight;
144         return( FG_Theta );
145 }
146
147 double get_heading( void )
148 {
149         fgFLIGHT *f;
150
151         f = current_aircraft.flight;
152         return( FG_Psi * RAD_TO_DEG );
153 }
154
155 double get_altitude( void )
156 {
157         fgFLIGHT *f;
158         // double rough_elev;
159
160         f = current_aircraft.flight;
161         // rough_elev = mesh_altitude(FG_Longitude * RAD_TO_ARCSEC,
162         //                                 FG_Latitude  * RAD_TO_ARCSEC);
163
164         return( FG_Altitude * FEET_TO_METER /* -rough_elev */ );
165 }
166
167 double get_sideslip( void )
168 {
169         fgFLIGHT *f;
170         
171         f = current_aircraft.flight;
172         
173         return( FG_Beta );
174 }
175
176 /****************************************************************************/
177 /* Convert degrees to dd mm.mmm' (DMM-Format)                               */
178 /****************************************************************************/
179 #if 0
180 char *toDM(double a)
181 {
182   short        neg = 0;
183   double       d, m;
184   static char  dm[13];
185   
186   if (a < 0.0) {
187     a = -a;
188     neg = 1;
189   }
190
191   d = (double) ( (int) a);
192   m = (a - d) * 60.0;
193   
194   if (m > 59.5) {
195     m  = 0.0;
196     d += 1.0;
197   }
198   if (neg) d = -d;
199   
200   sprintf(dm, "%.0f°%06.3f'", d, m);
201   return dm;
202 }
203 #endif // 0
204 double get_latitude( void )
205 {
206         fgFLIGHT *f;
207         f = current_aircraft.flight;
208
209 //      return( toDM(FG_Latitude * RAD_TO_DEG) );
210         return((double)((int)( FG_Latitude * RAD_TO_DEG)) );    
211 }
212 double get_lat_min( void )
213 {
214         fgFLIGHT *f;
215         double      a, d;
216
217         f = current_aircraft.flight;
218         
219         a = FG_Latitude * RAD_TO_DEG;   
220         if (a < 0.0) {
221                 a = -a;
222         }
223         d = (double) ( (int) a);
224         return( (a - d) * 60.0);
225 }
226
227
228 double get_longitude( void )
229 {
230         fgFLIGHT *f;
231         f = current_aircraft.flight;
232
233 //      return( toDM(FG_Longitude * RAD_TO_DEG) );
234         return((double)((int) (FG_Longitude * RAD_TO_DEG)) );
235 }
236 double get_long_min( void )
237 {
238         fgFLIGHT *f;
239         double  a, d;
240
241         f = current_aircraft.flight;
242         
243         a = FG_Longitude * RAD_TO_DEG;  
244         if (a < 0.0) {
245                 a = -a;
246         }
247         d = (double) ( (int) a);
248         return( (a - d) * 60.0);
249 }
250
251 //
252 // The following code deals with painting the "instrument" on the display
253 //
254    /* textString - Bitmap font string */
255
256 static void textString(int x, int y, char *msg, void *font)
257 {
258         glRasterPos2f(x, y);
259         while (*msg) {
260                 glutBitmapCharacter(font, *msg);
261                 msg++;
262     }
263 }
264
265 /* strokeString - Stroke font string */
266 /*
267 static void strokeString(int x, int y, char *msg, void *font)
268 {
269         glPushMatrix();
270         glTranslatef(x, y, 0);
271         glScalef(.04, .04, .04);
272         while (*msg) {
273                 glutStrokeCharacter(font, *msg);
274                 msg++;
275         }
276         glPopMatrix();
277 }
278 */
279
280 /*
281
282         Draws a measuring scale anywhere on the HUD
283
284
285         Needs: HUD_scale struct
286
287 */
288 static void drawscale( HUD_scale * pscale )
289 {
290   double vmin, vmax;
291   int marker_x;
292   int marker_y;
293   register i;
294   char TextScale[80];
295   int condition;
296   double cur_value = (*(pscale->load_value))();
297   int disp_val;
298
299   vmin = cur_value - pscale->half_width_units; // width units == needle travel
300   vmax = cur_value + pscale->half_width_units; // or picture unit span.
301
302
303   if( pscale->type == VERTICAL )     // Vertical scale
304     {
305       drawOneLine( pscale->scrn_pos.right,    // Vertical scale bar
306                    pscale->scrn_pos.bottom,
307                    pscale->scrn_pos.right,
308                    pscale->scrn_pos.top );
309
310     if( pscale->orientation == LEFT )     // Calculate x marker offset
311       marker_x = pscale->scrn_pos.left - 6;
312     else
313       if( pscale->orientation == RIGHT )
314         marker_x = pscale->scrn_pos.right;
315
316     // Draw the basic markings for the scale...
317
318     if( pscale->orientation == LEFT )
319       {
320
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 );
325
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 );
330
331       drawOneLine( pscale->scrn_pos.right,       // Middle tick bar /Index
332                    pscale->mid_scr,
333                    pscale->scrn_pos.right + 6,
334                    pscale->mid_scr );
335
336       }
337     else
338       if( pscale->orientation == RIGHT )
339         {
340         drawOneLine( pscale->scrn_pos.right,
341                      pscale->scrn_pos.bottom,
342                      pscale->scrn_pos.right+3,
343                      pscale->scrn_pos.bottom );
344
345         drawOneLine( pscale->scrn_pos.right,
346                      pscale->scrn_pos.top,
347                      pscale->scrn_pos.right+3,
348                      pscale->scrn_pos.top );
349
350         drawOneLine( pscale->scrn_pos.right,
351                      pscale->mid_scr,
352                      pscale->scrn_pos.right-6,
353                      pscale->mid_scr );
354         }
355
356     // Work through from bottom to top of scale. Calculating where to put
357     // minor and major ticks.
358
359     for( i = (int)(vmin); i <= (int)(vmax); i++ )
360       {
361       if( pscale->sub_type == LIMIT ) {           // Don't show ticks
362         condition = (i >= pscale->minimum_value); // below Minimum value.
363         }
364       else {
365         if( pscale->sub_type == NOLIMIT ) {
366           condition = 1;
367           }
368         }
369       if( condition )   // Show a tick if necessary
370         {
371         // Calculate the location of this tick
372         marker_y = (int)(pscale->scrn_pos.bottom + (i - vmin) * pscale->factor);
373
374         // Block calculation artifact from drawing ticks below min coordinate.
375         // Calculation here accounts for text height.
376
377         if( marker_y < (pscale->scrn_pos.bottom + 4)) {  // Magic number!!!
378           continue;
379           }
380         if( (i%pscale->div_min) == 0) {
381           if( pscale->orientation == LEFT )
382             {
383             drawOneLine( marker_x + 3, marker_y, marker_x + 6, marker_y );
384             }
385           else {
386             if( pscale->orientation == RIGHT )
387               {
388               drawOneLine( marker_x, marker_y, marker_x + 3, marker_y );
389               }
390             }
391           }
392         if( (i%pscale->div_max) == 0 )            {
393           drawOneLine( marker_x,     marker_y,
394                        marker_x + 6, marker_y );
395           if(pscale->modulo) {
396             disp_val = i % pscale->modulo;
397             if( !disp_val ) {
398               disp_val = pscale->modulo;
399               }
400             if( disp_val < 0) {
401               disp_val += pscale->modulo;
402               }
403             if( disp_val == pscale->modulo ) {
404               disp_val = 0;
405               }
406             }
407           else {
408             disp_val = i;
409             }
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 );
414             }
415           else  {
416             if( pscale->orientation == RIGHT )              {
417             textString( marker_x + 10,                         marker_y - 4,
418                         TextScale, GLUT_BITMAP_8_BY_13 );
419               }
420             }
421           }
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
426     {
427     if( pscale->orientation == TOP ) {
428       marker_y = pscale->scrn_pos.bottom;
429       }
430     else {
431       if( pscale->orientation == BOTTOM ) {
432         marker_y = pscale->scrn_pos.bottom - 6;
433         }
434       }
435     drawOneLine( pscale->scrn_pos.left,
436                  pscale->scrn_pos.bottom,
437                  pscale->scrn_pos.right,
438                  pscale->scrn_pos.bottom );
439
440     if( pscale->orientation == TOP )
441       {
442       drawOneLine( pscale->scrn_pos.left,
443                    pscale->scrn_pos.bottom,
444                    pscale->scrn_pos.left,
445                    pscale->scrn_pos.bottom + 3 );
446
447       drawOneLine( pscale->scrn_pos.right,
448                    pscale->scrn_pos.bottom,
449                    pscale->scrn_pos.right,
450                    pscale->scrn_pos.bottom + 6 );
451
452       drawOneLine( pscale->mid_scr,
453                    pscale->scrn_pos.bottom,
454                    pscale->mid_scr,
455                    pscale->scrn_pos.bottom - 6 );
456
457       }
458     else {
459       if( pscale->orientation == BOTTOM )
460         {
461         drawOneLine( pscale->scrn_pos.left,
462                      pscale->scrn_pos.bottom,
463                      pscale->scrn_pos.left,
464                      pscale->scrn_pos.bottom - 6 );
465
466         drawOneLine( pscale->scrn_pos.right,
467                      pscale->scrn_pos.bottom,
468                      pscale->scrn_pos.right,
469                      pscale->scrn_pos.bottom - 6 );
470
471         drawOneLine( pscale->mid_scr,
472                      pscale->scrn_pos.bottom,
473                      pscale->mid_scr,
474                      pscale->scrn_pos.bottom + 6 );
475         }
476       }
477
478     for( i = (int)(vmin); i <= (int)(vmax); i++ )  // increment is faster than addition
479       {
480       if( pscale->sub_type == LIMIT ) {
481         condition = (i >= pscale->minimum_value);
482         }
483       else {
484         if( pscale->sub_type == NOLIMIT ) {
485           condition = 1;
486           }
487         }
488       if( condition )        {
489         marker_x = (int)(pscale->scrn_pos.left + (i - vmin) * pscale->factor);
490         if( (i%pscale->div_min) == 0 ) {
491           if( pscale->orientation == TOP )
492             {
493             drawOneLine( marker_x, marker_y, marker_x, marker_y + 3 );
494             }
495           else {
496             if( pscale->orientation == BOTTOM )
497               {
498               drawOneLine( marker_x, marker_y + 3, marker_x, marker_y + 6 );
499               }
500             }
501           }
502         if( (i%pscale->div_max)==0 )
503           {
504           if(pscale->modulo) {
505             disp_val = i % pscale->modulo;
506             if( !disp_val ) {
507               disp_val = pscale->modulo;
508               }
509             if( disp_val < 0) {
510               disp_val += pscale->modulo;
511               }
512             if( disp_val == pscale->modulo ) {
513               disp_val = 0;
514               }
515             }
516           else {
517             disp_val = i;
518             }
519             sprintf( TextScale, "%d", disp_val );
520           if( pscale->orientation == TOP )
521             {
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 );
525             }
526           else {
527             if( pscale->orientation == BOTTOM )
528               {
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 );
532               }
533             }
534           }
535         }
536       }
537     }
538
539 }
540
541 //
542 //      Draws a climb ladder in the center of the HUD
543 //
544
545 static void drawladder( HUD_ladder *ladder )
546 {
547   double vmin, vmax;
548   double roll_value, pitch_value;
549   int marker_x, marker_y;
550 #ifdef DEBUGHUD
551   int mid_scr;
552 #endif
553   int scr_min, scr_max;
554   int x_ini, x_end;
555   int y_ini, y_end;
556   int new_x_ini, new_x_end;
557   int new_y_ini, new_y_end;
558   register i;
559   double factor;
560   char TextLadder[80];
561   int condition;
562
563   double cos_roll_value, sin_roll_value;
564 //  double cos_pitch_value, sin_pitch_value;
565
566   roll_value = (*ladder->load_roll)();
567   sin_roll_value = sin(roll_value);
568   cos_roll_value = cos(roll_value);
569   
570   pitch_value = (*ladder->load_pitch)()*RAD_TO_DEG;
571
572   vmin = pitch_value - ladder->width_units/2;
573   vmax = pitch_value + ladder->width_units/2;
574
575   scr_min = ladder->scrn_pos.y - (ladder->scr_height/2);
576   scr_max = scr_min       + ladder->scr_height;
577
578 #ifdef DEBUGHUD
579   mid_scr = scr_min       + (scr_max-scr_min)/2;
580 #endif
581
582   marker_x = ladder->scrn_pos.x - ladder->scr_width/2;
583
584   factor = (scr_max-scr_min)/ladder->width_units;
585
586   for( i=(int)(vmin); i<=(int)(vmax); i+=1 )
587     {
588     condition = 1;
589     if( condition )
590       {
591       marker_y = (int)(scr_min+(i-vmin)*factor);
592       if( i%ladder->div_units==0 )
593         {
594         sprintf( TextLadder, "%d", i );
595         if( ladder->scr_hole == 0 )
596           {
597           if( i ) {
598             x_ini = ladder->scrn_pos.x - ladder->scr_width/2;
599             }
600           else {
601             x_ini = ladder->scrn_pos.x - ladder->scr_width/2 - 10;
602             }
603           y_ini = marker_y;
604           x_end = ladder->scrn_pos.x + ladder->scr_width/2;
605           y_end = marker_y;
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);
618
619           if( i >= 0 )
620             {
621             drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end );
622             }
623           else
624             {
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);
629             }
630           textString( new_x_ini -  8 * strlen(TextLadder) - 8,
631                       new_y_ini -  4,
632                       TextLadder, GLUT_BITMAP_8_BY_13 );
633           textString( new_x_end + 10,
634                       new_y_end -  4,
635                       TextLadder, GLUT_BITMAP_8_BY_13 );
636           }
637         else
638           {
639           if( i != 0 )  {
640             x_ini = ladder->scrn_pos.x - ladder->scr_width/2;
641             }
642           else          {
643             x_ini = ladder->scrn_pos.x - ladder->scr_width/2 - 10;
644             }
645           y_ini = marker_y;
646           x_end = ladder->scrn_pos.x - ladder->scr_width/2 + ladder->scr_hole/2;
647           y_end = marker_y;
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);
660
661           if( i >= 0 )
662             {
663             drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end );
664             }
665           else
666             {
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);
671             }
672           textString( new_x_ini - 8 * strlen(TextLadder) - 8,
673                       new_y_ini - 4,
674                       TextLadder, GLUT_BITMAP_8_BY_13 );
675
676           x_ini = ladder->scrn_pos.x + ladder->scr_width/2 - ladder->scr_hole/2;
677           y_ini = marker_y;
678           if( i != 0 )  {
679             x_end = ladder->scrn_pos.x + ladder->scr_width/2;
680             }
681           else          {
682             x_end = ladder->scrn_pos.x + ladder->scr_width/2 + 10;
683             }
684           y_end = marker_y;
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);
697
698           if( i >= 0 )
699             {
700             drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end );
701             }
702           else
703             {
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);
708             }
709           textString( new_x_end+10, new_y_end-4,
710                       TextLadder, GLUT_BITMAP_8_BY_13 );
711           }
712         }
713             /* if( i%pscale->div_max==0 )
714             {
715                 drawOneLine( marker_x, marker_y, marker_x+6, marker_y );
716                 sprintf( TextScale, "%d", i );
717                 if( pscale->orientation == LEFT )
718                 {
719                         textString( marker_x-8*strlen(TextScale)-2, marker_y-4,
720                               TextScale, GLUT_BITMAP_8_BY_13 );
721                 }
722                 else if( pscale->orientation == RIGHT )
723                 {
724                         textString( marker_x+10, marker_y-4,
725                               TextScale, GLUT_BITMAP_8_BY_13 );
726                 }
727             } */
728         }
729     }
730
731 }
732
733 //
734 //      Draws an artificial horizon line in the center of the HUD
735 //      (with or without a center hole)
736 //
737 //      Needs: x_center, y_center, length, hole
738 //
739
740 static void drawhorizon( HUD_horizon *horizon )
741 {
742   int x_inc1, y_inc1;
743   int x_inc2, y_inc2;
744   int x_t_inc1, y_t_inc1;
745   
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;
750   
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;
755   
756 //      struct fgFLIGHT *f = &current_aircraft.flight;
757   double sin_bank, cos_bank;
758   double bank_angle, sideslip_angle;
759   double ss_const; // sideslip angle pixels per rad
760
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;
766   }
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;
772   }
773
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);
778   
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);
783
784   x_t_inc1 = (int)(horizon->tee_height * sin_bank);
785   y_t_inc1 = (int)(horizon->tee_height * cos_bank);
786   
787   d_bottom_x = 0;
788   d_bottom_y = (int)(-horizon->scr_hole);
789   d_right_x  = (int)(horizon->scr_hole);
790   d_right_y  = 0;
791   d_top_x    = 0;
792   d_top_y    = (int)(horizon->scr_hole);
793   d_left_x   = (int)(-horizon->scr_hole);
794   d_left_y   = 0;
795   
796   ss_const = (horizon->scr_width*2)/(FG_2PI/36.0);// width represents 10 degrees
797
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);
802   
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);
811   
812   if( horizon->scr_hole == 0 )
813     {
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 );
816     }
817   else
818     {
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 );
823     }
824
825   // draw teemarks (?)
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 );
830                
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 );
848   
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 ); */
865 }
866
867 //  drawControlSurfaces()
868 //      Draws a representation of the control surfaces in their current state
869 //      anywhere in the HUD
870 //
871
872 static void drawControlSurfaces( HUD_control_surfaces *ctrl_surf )
873 {
874         int x_ini, y_ini;
875         int x_end, y_end;
876         /* int x_1, y_1; */
877         /* int x_2, y_2; */
878         fgCONTROLS *pCtls;
879         int tmp;
880
881         x_ini = ctrl_surf->scrn_pos.x;
882         y_ini = ctrl_surf->scrn_pos.y;
883         x_end = x_ini + 150;
884         y_end = y_ini + 60;
885
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 );
894
895         pCtls = current_aircraft.controls;
896
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 )
903         {
904                 drawOneLine( x_ini + 10, tmp, x_ini + 20, tmp );
905         }
906         else
907         {
908                 drawOneLine( x_ini + 7, tmp, x_ini + 23, tmp);
909         }
910
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 )
917         {
918                 drawOneLine( tmp, y_end-20, tmp, y_end-10 );
919         }
920         else
921         {
922                 drawOneLine( tmp, y_end - 25, tmp, y_end -  5 );
923         }
924
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 );
929
930         tmp = x_ini + 35 + (int)(((pCtls->rudder + 1.0) / 2) * 50.0);
931         if( pCtls->rudder <= -0.01 || pCtls->rudder >= 0.01 )
932         {
933                 drawOneLine( tmp, y_ini + 20, tmp, y_ini + 10 );
934         }
935         else
936         {
937                 drawOneLine( tmp, y_ini + 25, tmp, y_ini +  5 );
938         }
939
940
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);
947
948
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 );
954
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 )
957         {
958                 drawOneLine( x_ini + 130, tmp, x_ini + 140, tmp);
959         }
960         else
961         {
962                 drawOneLine( x_ini + 127, tmp, x_ini + 143, tmp);
963         }
964
965 }
966
967 //
968 // Draws a label anywhere in the HUD
969 //
970 //
971
972 static void drawlabel( HUD_label *label )
973 {
974   char buffer[80];
975   char string[80];
976   int posincr;
977   int lenstr;
978
979   if( !label ) { // Eliminate the possible, but absurd case.
980     return;
981     }
982
983   if( label->pre_str != NULL) {
984     if( label->post_str != NULL ) {
985       sprintf( buffer, "%s%s%s", label->pre_str,  \
986                                  label->format,   \
987                                  label->post_str );
988       }
989     else {
990       sprintf( buffer, "%s%s",   label->pre_str, \
991                                  label->format );
992       }
993     }
994   else {
995     if( label->post_str != NULL ) {
996       sprintf( buffer, "%s%s",   label->format,  \
997                                  label->post_str );
998       }
999     } // else do nothing if both pre and post strings are nulls. Interesting.
1000
1001
1002   sprintf( string, buffer, (*label->load_value)() );
1003 #ifdef DEBUGHUD
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" );
1008 #endif
1009   lenstr = strlen( string );
1010   if( label->justify == LEFT_JUST ) {
1011    posincr = -lenstr*8;
1012    }
1013   else {
1014     if( label->justify == CENTER_JUST ) {
1015       posincr = -lenstr*4;
1016       }
1017     else {
1018       if( label->justify == RIGHT_JUST ) {
1019         posincr = 0;
1020         }
1021       }
1022     }
1023
1024   if( label->size == SMALL ) {
1025     textString( label->scrn_pos.x + posincr, label->scrn_pos.y,
1026                 string, GLUT_BITMAP_8_BY_13);
1027     }
1028   else  {
1029     if( label->size == LARGE ) {
1030       textString( label->scrn_pos.x + posincr, label->scrn_pos.y,
1031                   string, GLUT_BITMAP_9_BY_15);
1032       }
1033     }
1034 }
1035 // The following routines concern HUD object/component object construction
1036 //
1037
1038 // fgHUDInit
1039 //
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!
1045 //
1046 Hptr fgHUDInit( fgAIRCRAFT *current_aircraft )
1047 {
1048   Hptr hud;
1049
1050   fgPrintf( FG_COCKPIT, FG_INFO, "Initializing HUD\n" );
1051
1052   hud = (Hptr)calloc(sizeof( HUD),1);
1053   if( hud == NULL )
1054     return( NULL );
1055
1056   hud->code = 1;
1057   hud->status = 0;
1058
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.
1062
1063   fgHUDSetTimeMode( hud, NIGHT );
1064   fgHUDSetBrightness( hud, BRT_LIGHT );
1065
1066      // TBI
1067   fgHUDAddHorizon( hud, 330, 100, 40, 5, 10, get_roll, get_sideslip );
1068
1069   fgHUDAddLadder ( hud, 330, 285, 120, 180, 70, 10,
1070                    NONE, 45, get_roll, get_pitch );
1071      // KIAS
1072   fgHUDAddScale  ( hud, VERTICAL,     LIMIT, 200, 180, 380,  5,  10,
1073                       LEFT,     0,  100,   50,   0, get_speed );
1074      // Angle of Attack
1075   fgHUDAddScale  ( hud, HORIZONTAL, NOLIMIT, 180, 250, 410,  1,   5,
1076                       BOTTOM, -40,   50,   21,   0, get_aoa );
1077      // GYRO COMPASS
1078   fgHUDAddScale  ( hud, HORIZONTAL, NOLIMIT, 380, 200, 460,  5,  10,
1079                       TOP,      0,   50,   50, 360, get_heading );
1080      // AMSL
1081   fgHUDAddScale  ( hud, VERTICAL,     LIMIT, 460, 180, 380, 25, 100,
1082                       RIGHT,    0, 15000, 250,   0, get_altitude);
1083
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 );
1094   
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);
1107
1108   fgHUDAddControlSurfaces( hud, 10, 10, NULL );
1109
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
1113
1114   return( hud );
1115 }
1116
1117
1118 // add_instrument
1119 //
1120 // This is a stand in for linked list code that will get replaced later
1121 // by some more elegant list handling code.
1122
1123 void add_instrument( Hptr hud, HIptr pinstrument )
1124 {
1125     if( !hud || !pinstrument ) {
1126         return;
1127     }
1128
1129     pinstrument->next = hud->instruments;
1130     hud->instruments = pinstrument;
1131 }
1132
1133
1134 // fgHUDAddHorizon
1135 //
1136 // Constructs a HUD_horizon "object" and installs it into the hud instrument
1137 // list.
1138
1139 Hptr fgHUDAddHorizon( Hptr hud,     \
1140                       int x_pos,    \
1141                       int y_pos,    \
1142                       int length,   \
1143                       int hole_len, \
1144                       int tee_height,\
1145                       double (*load_roll)(),
1146                       double (*load_sideslip)() )
1147 {
1148     HUD_horizon *phorizon;
1149     HUD_instr   *pinstrument;
1150
1151     if( !hud ) {
1152         return NULL;
1153     }
1154                                        // construct the parent object
1155     pinstrument = (HIptr)calloc(sizeof(HUD_instr),1);
1156     if( pinstrument == NULL ) {
1157         return( NULL );
1158     }
1159     pinstrument->type    = HUDhorizon;  //  ARTIFICIAL_HORIZON;
1160
1161                                       // Construct the horizon
1162     phorizon = (HUD_horizon *) calloc( sizeof(HUD_horizon),1);
1163     if( phorizon == NULL )   {
1164         return( NULL );
1165     }
1166
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);
1178
1179     return( hud );
1180 }
1181
1182 // fgHUDAddScale
1183 //
1184 // Constructs a HUD_scale "object" and installs it into the hud instrument
1185 // list.
1186
1187 Hptr fgHUDAddScale( Hptr hud,        \
1188                     int type,        \
1189                     int sub_type,    \
1190                     int scr_pos,     \
1191                     int scr_min,     \
1192                     int scr_max,     \
1193                     int div_min,     \
1194                     int div_max,     \
1195                     int orientation, \
1196                     int min_value,   \
1197                     int max_value,   \
1198                     int width_units, \
1199                     int modulus,     \
1200                     double (*load_value)() )
1201 {
1202   HUD_scale *pscale;
1203   HUD_instr *pinstrument;
1204
1205   if( !hud ) {
1206     return NULL;
1207     }
1208
1209         pinstrument = (HIptr)calloc(sizeof(HUD_instr),1);
1210         if( pinstrument == NULL ) {
1211      return( NULL );
1212      }
1213
1214   pinstrument->type = HUDscale;
1215
1216   pscale = ( HUD_scale *)calloc(sizeof(HUD_scale),1);
1217   if( pscale == NULL )   {
1218      return( NULL );
1219     }
1220
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;
1230
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.
1237
1238   pscale->mid_scr  = (pscale->scr_span >> 1) + scr_min;
1239
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.
1243
1244   pscale->factor   = (double)pscale->scr_span / (double)width_units;
1245
1246   switch( type ) {
1247     case HORIZONTAL:
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;
1252       break;
1253
1254     case VERTICAL:
1255     default:
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;
1260     }
1261
1262                                      // Install the scale
1263   pinstrument->instr = pscale;
1264                                       //  Install the instrument into hud.
1265   add_instrument( hud, pinstrument);
1266
1267   return( hud );
1268 }
1269
1270 // fgHUDAddLabel
1271 //
1272 // Constructs a HUD_Label object and installs it into the hud instrument
1273 // list.
1274 Hptr fgHUDAddLabel( Hptr hud,       \
1275                     int x_pos,      \
1276                     int y_pos,      \
1277                     int size,       \
1278                     int blink,      \
1279                     int justify,    \
1280                                                   char *pre_str,  \
1281                     char *post_str, \
1282                     char *format,   \
1283                     double (*load_value)() )
1284 {
1285         HUD_label *plabel;
1286         HUD_instr *pinstrument;
1287
1288   if( !hud ) {
1289     return NULL;
1290     }
1291
1292         pinstrument = (HIptr)calloc(sizeof(HUD_instr),1);
1293         if( pinstrument == NULL ) {
1294     return NULL;
1295     }
1296         pinstrument->type = HUDlabel;
1297
1298         plabel = (HUD_label *)calloc(sizeof(HUD_label),1);
1299         if( plabel == NULL ){
1300     return NULL;
1301     }
1302
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);
1316
1317         return( hud );
1318 }
1319
1320 // fgHUDAddLadder
1321 //
1322 // Contains code that constructs a ladder "object" and installs it as
1323 // a hud instrument in the hud instrument list.
1324 //
1325 Hptr fgHUDAddLadder( Hptr hud,        \
1326                      int x_pos,       \
1327                      int y_pos,       \
1328                      int scr_width,   \
1329                      int scr_height,  \
1330                                                    int hole_len,    \
1331                      int div_units,   \
1332                      int label_pos,   \
1333                      int width_units, \
1334                                                    double (*load_roll)(),
1335                      double (*load_pitch)() )
1336 {
1337         HUD_ladder *pladder;
1338         HUD_instr  *pinstrument;
1339
1340   if( !hud ) {
1341     return NULL;
1342     }
1343
1344         pinstrument = (HIptr)calloc(sizeof(HUD_instr),1);
1345         if( pinstrument == NULL )
1346                 return( NULL );
1347
1348         pinstrument->type = HUDladder;
1349
1350         pladder = (HUD_ladder *)calloc(sizeof(HUD_ladder),1);
1351         if( pladder == NULL )
1352                 return( NULL );
1353
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;
1365
1366   pinstrument->instr      = pladder;
1367                                       //  Install the instrument into hud.
1368   add_instrument( hud, pinstrument);
1369         return hud;
1370 }
1371
1372 //   fgHUDAddControlSurfaces()
1373 //
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.
1379
1380 Hptr fgHUDAddControlSurfaces( Hptr hud,
1381                               int x_pos,
1382                               int y_pos,
1383                               double (*load_value)() )
1384 {
1385         HUD_control_surfaces *pcontrol_surfaces;
1386         HUD_instr *pinstrument;
1387
1388     if( !hud ) {
1389       return NULL;
1390     }
1391
1392     // Construct shell
1393     pinstrument = (HIptr)calloc(sizeof(HUD_instr),1);
1394     if( !pinstrument ) {
1395       return NULL;
1396       }
1397     pinstrument->type = HUDcontrol_surfaces;
1398
1399     // Construct core
1400     pcontrol_surfaces = (HUD_control_surfaces *)calloc(sizeof(HUD_control),1);
1401     if( !pcontrol_surfaces ) {
1402       return( NULL );
1403       }
1404
1405     pcontrol_surfaces->scrn_pos.x = x_pos;
1406     pcontrol_surfaces->scrn_pos.y = y_pos;
1407     pcontrol_surfaces->load_value = load_value;
1408
1409     pinstrument->instr     = pcontrol_surfaces;
1410                                                    // Install
1411     add_instrument( hud, pinstrument);
1412
1413     return hud;
1414 }
1415
1416 // fgHUDAddControl
1417 //
1418 //
1419
1420 Hptr fgHUDAddControl( Hptr hud,        \
1421                       int ctrl_x,      \
1422                       int ctrl_y,      \
1423                       int ctrl_length, \
1424                       int orientation, \
1425                       int alignment,   \
1426                       int min_value,   \
1427                       int max_value,   \
1428                       int width_units, \
1429                       double (*load_value)() )
1430 {
1431     HUD_control *pcontrol;
1432     HUD_instr *pinstrument;
1433
1434     if( !hud ) {
1435       return NULL;
1436     }
1437
1438     // Construct shell
1439     pinstrument = (HIptr)calloc(sizeof(HUD_instr),1);
1440     if( !pinstrument ) {
1441       return NULL;
1442       }
1443     pinstrument->type = HUDcontrol;
1444
1445     // Construct core
1446     pcontrol = (HUD_control *)calloc(sizeof(HUD_control),1);
1447     if( !(pcontrol == NULL) ) {
1448       return( NULL );
1449       }
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;
1459                                                    // Integrate
1460     pinstrument->instr     = pcontrol;
1461                                                    // Install
1462     add_instrument( hud, pinstrument);
1463
1464     return hud;
1465 }
1466
1467 /*
1468 Hptr fgHUDAddMovingHorizon(  Hptr hud,     \
1469                              int x_pos,    \
1470                              int y_pos,    \
1471                              int length,   \
1472                              int hole_len, \
1473                              int color )
1474 {
1475
1476 }
1477
1478 Hptr fgHUDAddCircularLadder( Hptr hud,    \
1479                              int scr_min, \
1480                              int scr_max, \
1481                              int div_min, \
1482                              int div_max, \
1483                              int max_value )
1484 {
1485
1486 }
1487
1488 Hptr fgHUDAddNumDisp( Hptr hud,           \
1489                       int x_pos,          \
1490                       int y_pos,          \
1491                       int size,           \
1492                       int color,          \
1493                       int blink,          \
1494                                                           char *pre_str,      \
1495                       char *post_str )
1496 {
1497
1498 }
1499 */
1500
1501 // fgUpdateHUD
1502 //
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
1505 // all C++.
1506 //
1507
1508 void fgUpdateHUD( Hptr hud ) {
1509     HIptr phud_instr;
1510
1511     glMatrixMode(GL_PROJECTION);
1512     glPushMatrix();
1513
1514     glLoadIdentity();
1515     gluOrtho2D(0, 640, 0, 480);
1516     glMatrixMode(GL_MODELVIEW);
1517     glPushMatrix();
1518     glLoadIdentity();
1519
1520     glColor3f(1.0, 1.0, 1.0);
1521     glIndexi(7);
1522
1523     glDisable(GL_DEPTH_TEST);
1524     glDisable(GL_LIGHTING);
1525
1526     glLineWidth(1);
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) {
1532          case BRT_LIGHT:
1533            glColor3f (0.1, 0.9, 0.1);
1534            break;
1535          case BRT_MEDIUM:
1536            glColor3f (0.1, 0.7, 0.0);
1537            break;
1538          case BRT_DARK:
1539            glColor3f (0.0, 0.5, 0.0);
1540          }
1541       }
1542     else if( hud->time_of_day==NIGHT) {
1543       switch (hud->brightness) {
1544          case BRT_LIGHT:
1545            glColor3f (0.9, 0.1, 0.1);
1546            break;
1547          case BRT_MEDIUM:
1548            glColor3f (0.7, 0.0, 0.1);
1549            break;
1550          case BRT_DARK:
1551            glColor3f (0.5, 0.0, 0.0);
1552          }
1553       }
1554     else {
1555       glColor3f (0.1, 0.9, 0.1);
1556       }
1557
1558     fgPrintf( FG_COCKPIT, FG_DEBUG, "HUD Code %d  Status %d\n",
1559               hud->code, hud->status );
1560
1561     phud_instr = hud->instruments;
1562     while( phud_instr ) {
1563         /* printf("Drawing Instrument %d\n", phud_instr->type); */
1564
1565         switch (phud_instr->type) {
1566     case HUDhorizon:   // ARTIFICIAL HORIZON
1567             drawhorizon( (pHUDhorizon)phud_instr->instr );
1568             break;
1569
1570     case HUDscale:     // Need to simplify this call.
1571             drawscale (  (pHUDscale)  phud_instr->instr  );
1572             break;
1573
1574     case HUDlabel:
1575             drawlabel (  (pHUDlabel)  phud_instr->instr  );
1576             break;
1577
1578     case HUDladder:
1579             drawladder(  (pHUDladder) phud_instr->instr  );
1580             break;
1581
1582 //    case HUDcontrol:
1583 //      drawControl( (pHUDcontrol) phud_instr->instr );
1584 //      break;
1585
1586     case HUDcontrol_surfaces:
1587             drawControlSurfaces( (pHUDControlSurfaces) phud_instr->instr );
1588             break;
1589
1590     default:; // Ignore anything you don't know about.
1591     }
1592
1593   phud_instr = phud_instr->next;
1594   }
1595
1596   glEnable(GL_DEPTH_TEST);
1597   glEnable(GL_LIGHTING);
1598   glMatrixMode(GL_PROJECTION);
1599   glPopMatrix();
1600   glMatrixMode(GL_MODELVIEW);
1601   glPopMatrix();
1602 }
1603
1604 void fgHUDSetTimeMode( Hptr hud, int time_of_day )
1605 {
1606
1607   hud->time_of_day = time_of_day;
1608
1609 }
1610
1611 void fgHUDSetBrightness( Hptr hud, int brightness )
1612 {
1613
1614   hud->brightness = brightness;
1615
1616 }
1617
1618 /* $Log$
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.
1622 /*
1623  * Revision 1.4  1998/05/03 00:46:46  curt
1624  * polar.h -> polar3d.h
1625  *
1626  * Revision 1.3  1998/04/30 12:36:02  curt
1627  * C++-ifying a couple source files.
1628  *
1629  * Revision 1.2  1998/04/25 22:06:27  curt
1630  * Edited cvs log messages in source files ... bad bad bad!
1631  *
1632  * Revision 1.1  1998/04/24 00:45:57  curt
1633  * C++-ifing the code a bit.
1634  *
1635  * Revision 1.22  1998/04/18 04:14:02  curt
1636  * Moved fg_debug.c to it's own library.
1637  *
1638  * Revision 1.21  1998/04/03 21:55:28  curt
1639  * Converting to Gnu autoconf system.
1640  * Tweaks to hud.c
1641  *
1642  * Revision 1.20  1998/03/09 22:48:40  curt
1643  * Minor "formatting" tweaks.
1644  *
1645  * Revision 1.19  1998/02/23 20:18:28  curt
1646  * Incorporated Michele America's hud changes.
1647  *
1648  * Revision 1.18  1998/02/21 14:53:10  curt
1649  * Added Charlie's HUD changes.
1650  *
1651  * Revision 1.17  1998/02/20 00:16:21  curt
1652  * Thursday's tweaks.
1653  *
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.
1658  *
1659  * Revision 1.15  1998/02/16 13:38:39  curt
1660  * Integrated changes from Charlie Hotchkiss.
1661  *
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>
1665  *
1666  * Revision 1.12  1998/02/09 15:07:48  curt
1667  * Minor tweaks.
1668  *
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>
1672  *
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.
1678  *
1679  * Revision 1.9  1998/01/31 00:43:04  curt
1680  * Added MetroWorks patches from Carmen Volpe.
1681  *
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.
1685  *
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.
1689  *
1690  * Revision 1.6  1997/12/15 23:54:34  curt
1691  * Add xgl wrappers for debugging.
1692  * Generate terrain normals on the fly.
1693  *
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 {}"
1697  *
1698  * Revision 1.4  1997/09/23 00:29:32  curt
1699  * Tweaks to get things to compile with gcc-win32.
1700  *
1701  * Revision 1.3  1997/09/05 14:17:26  curt
1702  * More tweaking with stars.
1703  *
1704  * Revision 1.2  1997/09/04 02:17:30  curt
1705  * Shufflin' stuff.
1706  *
1707  * Revision 1.1  1997/08/29 18:03:22  curt
1708  * Initial revision.
1709  *
1710  */