]> git.mxchange.org Git - flightgear.git/blob - Cockpit/hud.cxx
Added a View Frustum Culling ratio display to the hud.
[flightgear.git] / Cockpit / hud.cxx
1 /**************************************************************************
2  * hud.cxx -- 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 <Math/fg_random.h>
47 #include <Math/mat3.h>
48 #include <Math/polar3d.h>
49 #include <Scenery/scenery.hxx>
50 #include <Time/fg_timer.hxx>
51 #include <Weather/weather.h>
52
53 #include "hud.hxx"
54
55 // The following routines obtain information concerntin the aircraft's
56 // current state and return it to calling instrument display routines.
57 // They should eventually be member functions of the aircraft.
58 //
59
60 //using namespace std;
61
62 /*
63
64 class instr_ptr {
65   private:
66     instr_item *pHI;
67
68   public:
69     instr_ptr ( instr_item * pHudInstr = 0) : pHI( pHudInstr){}
70     ~instr_ptr() { if( pHI ) delete pHI; }
71     instr_ptr & operator = (const instr_ptr & rhs);
72     instr_ptr( const instr_ptr & image );
73 }
74
75 instr_ptr &
76 instr_ptr ::
77 operator = ( const instr_ptr & rhs )
78 {
79   if( !(this == &rhs )) {
80     pHI = new instr_item( *(rhs.pHI));
81     }
82   return *this;
83 }
84
85 instr_ptr ::
86 instr_ptr ( const instr_ptr & image ) :
87 {
88   pHI = new instr_item( *(image.pHI));
89 }
90 */
91 deque< instr_item * > HUD_deque;
92
93 class locRECT {
94   public:
95     RECT rect;
96
97     locRECT( UINT left, UINT top, UINT right, UINT bottom);
98     RECT get_rect(void) { return rect;}
99 };
100
101 locRECT :: locRECT( UINT left, UINT top, UINT right, UINT bottom)
102 {
103   rect.left   =  left;
104   rect.top    =  top;
105   rect.right  =  right;
106   rect.bottom =  bottom;
107
108 }
109 // #define DEBUG
110
111 // #define drawOneLine(x1,y1,x2,y2)  glBegin(GL_LINES);
112 //   glVertex2f ((x1),(y1)); glVertex2f ((x2),(y2)); glEnd();
113 //
114
115 void drawOneLine( UINT x1, UINT y1, UINT x2, UINT y2)
116 {
117   glBegin(GL_LINES);
118   glVertex2f(x1, y1);
119   glVertex2f(x2, y2);
120   glEnd();
121 }
122
123 void drawOneLine( RECT &rect)
124 {
125   glBegin(GL_LINES);
126   glVertex2f(rect.left, rect.top);
127   glVertex2f(rect.right, rect.bottom);
128   glEnd();
129 }
130
131 //
132 // The following code deals with painting the "instrument" on the display
133 //
134    /* textString - Bitmap font string */
135
136 static void textString(int x, int y, char *msg, void *font)
137 {
138         glRasterPos2f(x, y);
139         while (*msg) {
140                 glutBitmapCharacter(font, *msg);
141                 msg++;
142     }
143 }
144
145 /* strokeString - Stroke font string */
146 /*
147 static void strokeString(int x, int y, char *msg, void *font)
148 {
149         glPushMatrix();
150         glTranslatef(x, y, 0);
151         glScalef(.04, .04, .04);
152         while (*msg) {
153                 glutStrokeCharacter(font, *msg);
154                 msg++;
155         }
156         glPopMatrix();
157 }
158 */
159
160
161
162 // Abstract Base Class instr_item
163 //
164 UINT instr_item :: instances;  // Initial value of zero
165
166 // constructor    ( No default provided )
167
168 instr_item  ::
169    instr_item( RECT             scr_pos,
170                DBLFNPTR         data_source,
171                ReadOriented     orientation,
172                bool             working) :
173                       handle         ( ++instances  ),
174                       scrn_pos       ( scr_pos      ),
175                       load_value_fn  ( data_source  ),
176                       oriented       ( orientation  ),
177                       is_enabled     ( working      ),
178                       broken         ( FALSE        ),
179                       brightness     ( BRT_DARK     )
180 {
181 int hitemp, widetemp;
182 int xtemp;
183
184          // Check for inverted rect coords.  We must have top/right > bottom
185          // left corners or derived classes will draw their boxes very oddly.
186          // This is the only place we can reliably validate this data. Note
187          // that the box may be entirely or partly located off the screen.
188          // This needs consideration and possibly a member function to move
189          // the box, a keen idea for supporting "editing" of the HUD.
190   if( scrn_pos.top < scrn_pos.bottom ) {
191     xtemp           = scrn_pos.bottom;
192     scrn_pos.bottom = scrn_pos.top;
193     scrn_pos.top    = xtemp;
194     }
195   if( scrn_pos.left > scrn_pos.right ) {
196     xtemp           = scrn_pos.left;
197     scrn_pos.left   = scrn_pos.right;
198     scrn_pos.right  = xtemp;
199     }
200
201          // Insure that the midpoint marker will fall exactly at the
202          // middle of the bar.
203   if( !((scr_pos.top-scr_pos.bottom) % 2)) {
204     scrn_pos.top++;
205     }
206   if( !((scr_pos.right - scr_pos.left ) % 2)) {
207     scrn_pos.right++;
208     }
209          // Set up convenience values for centroid of the box and
210          // the span values according to orientation
211
212   hitemp   = scr_pos.top - scr_pos.bottom;
213   widetemp = scr_pos.right - scr_pos.left;
214   if((orientation == ReadTOP) || (orientation == ReadBOTTOM)) {
215     scr_span = widetemp;
216     }
217   else {
218     scr_span = hitemp;
219     }
220          // Here we work out the centroid for the corrected box.
221   mid_span.x = scr_pos.left   + ((scr_pos.right - scr_pos.left) >> 1);
222   mid_span.y = scr_pos.bottom + ((scr_pos.top - scr_pos.bottom) >> 1);
223 }
224
225 // copy constructor
226 instr_item  ::
227      instr_item ( const instr_item & image ):
228                          handle       ( ++instances        ),
229                          scrn_pos     ( image.scrn_pos     ),
230                          load_value_fn( image.load_value_fn),
231                          oriented     ( image.oriented     ),
232                          is_enabled   ( image.is_enabled   ),
233                          broken       ( image.broken       ),
234                          brightness   ( image.brightness   ),
235                          scr_span     ( image.scr_span     ),
236                          mid_span     ( image.mid_span     )
237 {
238 }
239
240 // assignment operator
241
242 instr_item & instr_item :: operator = ( const instr_item & rhs )
243 {
244   if( !(this == &rhs )) { // Not an identity assignment
245     scrn_pos      = rhs.scrn_pos;
246     load_value_fn = rhs.load_value_fn;
247     oriented      = rhs.oriented;
248     is_enabled    = rhs.is_enabled;
249     broken        = rhs.broken;
250     brightness    = rhs.brightness;
251     }
252   return *this;
253 }
254
255 // destructor
256
257 instr_item :: ~instr_item ()
258 {
259   if( instances ) {
260     instances--;
261     }
262 }
263
264 void instr_item ::
265     update( void )
266 {
267 }
268
269 // break_display       This is emplaced to provide hooks for making
270 //                     instruments unreliable. The default behavior is
271 // to simply not display, but more sophisticated behavior is available
272 // by over riding the function which is virtual in this class.
273
274 void instr_item ::
275     break_display ( bool bad )
276 {
277   broken = !!bad;
278   is_enabled = FALSE;
279 }
280
281 void instr_item ::
282     SetBrightness  ( int level  )
283 {
284   brightness = level;   // This is all we will do for now. Later the
285                         // brightness levels will be sensitive both to
286                         // the control knob and the outside light levels
287                         // to emulated night vision effects.
288 }
289
290 UINT instr_item :: get_Handle( void )
291 {
292   return handle;
293 }
294
295 //======================= Top of instr_label class =========================
296 instr_label ::
297          instr_label( RECT          region,
298                       DBLFNPTR      data_source,
299                       const char   *label_format,
300                       const char   *pre_label_string,
301                       const char   *post_label_string,
302                       ReadOriented  orientation,
303                       fgLabelJust   justification,
304                       int           font_size,
305                       int           blinking,
306                       bool          working ):
307                            instr_item( region, data_source,
308                                             orientation, working ),
309                            pformat  ( label_format      ),
310                            pre_str  ( pre_label_string  ),
311                            post_str ( post_label_string ),
312                            justify  ( justification     ),
313                            fontSize ( font_size         ),
314                            blink    ( blinking          )
315 {
316 }
317
318 // I put this in to make it easy to construct a class member using the current
319 // C code.
320
321
322 instr_label :: ~instr_label()
323 {
324 }
325
326 // Copy constructor
327 instr_label :: instr_label( const instr_label & image) :
328                               instr_item((const instr_item &)image),
329                               pformat    ( image.pformat    ),
330                               pre_str  ( image.pre_str  ),
331                               post_str ( image.post_str ),
332                               blink    ( image.blink    )
333 {
334 }
335
336 instr_label & instr_label ::operator = (const instr_label & rhs )
337 {
338   if( !(this == &rhs)) {
339     instr_item::operator = (rhs);
340     pformat      = rhs.pformat;
341     fontSize   = rhs.fontSize;
342     blink      = rhs.blink;
343     justify    = rhs.justify;
344     pre_str    = rhs.pre_str;
345     post_str   = rhs.post_str;
346     }
347         return *this;
348 }
349
350
351 //
352 // draw                    Draws a label anywhere in the HUD
353 //
354 //
355 void instr_label ::
356 draw( void )       // Required method in base class
357 {
358   char format_buffer[80];
359   char label_buffer[80];
360   int posincr;
361   int lenstr;
362   RECT  scrn_rect = get_location();
363
364   if( pre_str != NULL) {
365     if( post_str != NULL ) {
366       sprintf( format_buffer, "%s%s%s", pre_str, pformat, post_str );
367       }
368     else {
369       sprintf( format_buffer, "%s%s",   pre_str, pformat );
370       }
371     }
372   else {
373     if( post_str != NULL ) {
374       sprintf( format_buffer, "%s%s",   pformat, post_str );
375       }
376     } // else do nothing if both pre and post strings are nulls. Interesting.
377
378   sprintf( label_buffer, format_buffer, get_value() );
379 #ifdef DEBUGHUD
380         fgPrintf( FG_COCKPIT, FG_DEBUG,  format_buffer );
381         fgPrintf( FG_COCKPIT, FG_DEBUG,  "\n" );
382         fgPrintf( FG_COCKPIT, FG_DEBUG, label_buffer );
383         fgPrintf( FG_COCKPIT, FG_DEBUG, "\n" );
384 #endif
385   lenstr = strlen( label_buffer );
386
387   posincr = 0;   //  default to RIGHT_JUST ... center located calc: -lenstr*8;
388
389   if( justify == CENTER_JUST ) {
390     posincr =  - (lenstr << 2); //  -lenstr*4;
391     }
392   else {
393     if( justify == LEFT_JUST ) {
394       posincr = - (lenstr << 8);  // 0;
395       }
396     }
397
398   if( fontSize == SMALL ) {
399     textString( scrn_rect.left + posincr, scrn_rect.bottom,
400                 label_buffer, GLUT_BITMAP_8_BY_13);
401     }
402   else  {
403     if( fontSize == LARGE ) {
404       textString( scrn_rect.left + posincr, scrn_rect.bottom,
405                   label_buffer, GLUT_BITMAP_9_BY_15);
406       }
407     }
408 }
409
410 //============== Top of instr_scale class memeber definitions ===============
411 //
412 // Notes:
413 // 1. instr_scales divide the specified location into half and then
414 //    the half opposite the read direction in half again. A bar is
415 //    then drawn along the second divider. Scale ticks are drawn
416 //    between the middle and quarter section lines (minor division
417 //    markers) or just over the middle line.
418 //
419 // 2.  This class was not intended to be instanciated. See moving_scale
420 //     and guage_instr classes.
421 //============================================================================
422 instr_scale ::
423 instr_scale ( RECT         the_box,
424               DBLFNPTR     load_fn,
425               ReadOriented orient,
426               int          show_range,
427               int          maxValue,
428               int          minValue,
429               UINT         major_divs,
430               UINT         minor_divs,
431               UINT         rollover,
432               bool         working ) :
433                 instr_item( the_box, load_fn, orient, working),
434                 range_shown  ( show_range ),
435                 Maximum_value( maxValue   ),
436                 Minimum_value( minValue   ),
437                 Maj_div      ( major_divs ),
438                 Min_div      ( minor_divs ),
439                 Modulo       ( rollover   )
440 {
441 int temp;
442
443   scale_factor   = (double)get_span() / range_shown;
444   if( show_range < 0 ) {
445     range_shown = -range_shown;
446     }
447   temp = (Maximum_value - Minimum_value) / 100;
448   if( range_shown < temp ) {
449     range_shown = temp;
450     }
451 }
452
453 instr_scale ::
454   instr_scale( const instr_scale & image ) :
455             instr_item( (const instr_item &) image),
456             range_shown  ( image.range_shown   ),
457             Maximum_value( image.Maximum_value ),
458             Minimum_value( image.Minimum_value ),
459             Maj_div      ( image.Maj_div       ),
460             Min_div      ( image.Min_div       ),
461             Modulo       ( image.Modulo        ),
462             scale_factor ( image.scale_factor  )
463 {
464 }
465
466 instr_scale & instr_scale :: operator = (const instr_scale & rhs )
467 {
468   if( !(this == &rhs)) {
469     instr_item::operator = (rhs);
470     Minimum_value = rhs.Minimum_value;
471     Maximum_value = rhs.Maximum_value;
472     Maj_div       = rhs.Maj_div;
473     Min_div       = rhs.Min_div;
474     Modulo        = rhs.Modulo;
475     scale_factor  = rhs.scale_factor;
476     range_shown   = rhs.range_shown;
477     }
478   return *this;
479 }
480
481 instr_scale :: ~ instr_scale () {}
482
483 //========== Top of moving_scale_instr class member definitions =============
484
485 moving_scale ::
486 moving_scale( RECT         the_box,
487               DBLFNPTR     data_source,
488               ReadOriented orientation,
489               int          max_value,
490               int          min_value,
491               UINT         major_divs,
492               UINT         minor_divs,
493               UINT         modulus,
494               double       value_span,
495               bool         working) :
496                 instr_scale( the_box,
497                              data_source, orientation,
498                              (int)value_span,
499                              max_value, min_value,
500                              major_divs, minor_divs, modulus,
501                              working),
502                 val_span   ( value_span)
503 {
504   half_width_units = range_to_show() / 2.0;
505 }
506
507 moving_scale ::
508 ~moving_scale() { }
509
510 moving_scale ::
511 moving_scale( const moving_scale & image):
512       instr_scale( (const instr_scale & ) image)
513 {
514 }
515
516 moving_scale & moving_scale ::
517 operator = (const moving_scale & rhs )
518 {
519   if( !( this == &rhs)){
520     instr_scale::operator = (rhs);
521     }
522   return *this;
523 }
524
525 void moving_scale ::
526 draw( void ) //  (HUD_scale * pscale )
527 {
528   double vmin, vmax;
529   int marker_x;
530   int marker_y;
531   register i;
532   char TextScale[80];
533   int condition;
534   int disp_val;
535   POINT mid_scr = get_centroid();
536   POINT bias_bar;                  // eliminates some orientation checks
537   double cur_value = get_value();
538   RECT   scrn_rect = get_location();
539   ReadOriented orientation = get_orientation();
540
541   vmin = cur_value - half_width_units; // width units == needle travel
542   vmax = cur_value + half_width_units; // or picture unit span.
543
544
545   if( (orientation == ReadLEFT) ||( orientation == ReadRIGHT))  // Vertical scale
546     {
547     if( orientation == ReadLEFT ) {    // Calculate x marker offset
548       marker_x = scrn_rect.left - 6;
549       bias_bar.x = ((scrn_rect.right - mid_scr.x)>>1) + mid_scr.x;
550       }
551     else {                             // We'll default this for now.
552       marker_x = mid_scr.x;            // scrn_rect.right;
553       bias_bar.x = ((mid_scr.x - scrn_rect.left)>>1) + scrn_rect.left;
554       }
555
556     // Draw the basic markings for the scale...
557
558     drawOneLine( bias_bar.x,    // Vertical scale bar
559                  scrn_rect.bottom,
560                  bias_bar.x,
561                  scrn_rect.top );
562
563
564     if( orientation == ReadLEFT )
565       {
566
567       drawOneLine( bias_bar.x,     // Bottom tick bar
568                    scrn_rect.bottom,
569                    mid_scr.x,
570                    scrn_rect.bottom );
571
572       drawOneLine( bias_bar.x,     // Top tick bar
573                    scrn_rect.top,
574                    mid_scr.x,
575                    scrn_rect.top );
576
577       drawOneLine( scrn_rect.right,       // Middle tick bar /Index
578                    mid_scr.y,
579                    bias_bar.x,
580                    mid_scr.y );
581
582       }
583     else       { // ReadRight
584       drawOneLine( bias_bar.x,
585                    scrn_rect.bottom,
586                    mid_scr.x,
587                    scrn_rect.bottom );
588
589       drawOneLine( bias_bar.x,
590                    scrn_rect.top,
591                    mid_scr.x,
592                    scrn_rect.top );
593
594       drawOneLine( scrn_rect.left,
595                    mid_scr.y,
596                    bias_bar.x,
597                    mid_scr.y );
598       }
599
600     // Work through from bottom to top of scale. Calculating where to put
601     // minor and major ticks.
602
603     for( i = (int)vmin; i <= (int)vmax; i++ )
604       {
605 //      if( sub_type == LIMIT ) {           // Don't show ticks
606         condition = (i >= min_val()); // below Minimum value.
607 //        }
608 //      else {
609 //        if( sub_type == NOLIMIT ) {
610 //          condition = 1;
611 //          }
612 //        }
613       if( condition )   // Show a tick if necessary
614         {
615         // Calculate the location of this tick
616         marker_y = scrn_rect.bottom + (int)((i - vmin) * factor());
617
618         // Block calculation artifact from drawing ticks below min coordinate.
619         // Calculation here accounts for text height.
620
621         if( marker_y < (scrn_rect.bottom + 4)) {  // Magic number!!!
622           continue;
623           }
624         if( div_min()) {
625           if( (i%div_min()) == 0) {
626 //            if( orientation == ReadLEFT )
627 //              {
628 //              drawOneLine( marker_x + 3, marker_y, marker_x + 6, marker_y );
629               drawOneLine( bias_bar.x, marker_y, mid_scr.x, marker_y );
630 //              }
631 //            else {
632 //              if( orientation == ReadRIGHT )
633 //                {
634 //                drawOneLine( marker_x, marker_y, marker_x + 3, marker_y );
635 //                drawOneLine( bias_bar.x, marker_y, mid_scr.x, marker_y );
636 //                }
637 //              }
638             }
639           }
640         if( div_max()) {
641           if( (i%div_max()) == 0 )            {
642 //            drawOneLine( marker_x,     marker_y,
643 //                         marker_x + 6, marker_y );
644             if(modulo()) {
645               disp_val = i % modulo();
646               if( disp_val < 0) {
647                 disp_val += modulo();
648                 }
649               }
650             else {
651               disp_val = i;
652               }
653             sprintf( TextScale, "%d", disp_val );
654             if( orientation == ReadLEFT )              {
655               drawOneLine( mid_scr.x - 3, marker_y, bias_bar.x, marker_y );
656
657               textString( marker_x -  8 * strlen(TextScale) - 2, marker_y - 4,
658                           TextScale, GLUT_BITMAP_8_BY_13 );
659               }
660             else  {
661               drawOneLine( mid_scr.x + 3, marker_y, bias_bar.x, marker_y );
662               textString( marker_x + 10,                         marker_y - 4,
663                           TextScale, GLUT_BITMAP_8_BY_13 );
664               } // Else read oriented right
665             } // End if modulo division by major interval is zero
666           }  // End if major interval divisor non-zero
667         } // End if condition
668       } // End for range of i from vmin to vmax
669     }  // End if VERTICAL SCALE TYPE
670   else {                                // Horizontal scale by default
671     {
672     if( orientation == ReadTOP ) {
673       bias_bar.y = ((mid_scr.y - scrn_rect.bottom)>>1 ) + scrn_rect.bottom;
674       marker_y = bias_bar.y;  // Don't use now
675       }
676     else {  // We will assume no other possibility at this time.
677       bias_bar.y = ((scrn_rect.top - mid_scr.y)>>1 ) + mid_scr.y;
678       marker_y = bias_bar.y;  // Don't use now
679       }
680
681     drawOneLine( scrn_rect.left,
682                  bias_bar.y,
683                  scrn_rect.right,
684                  bias_bar.y );
685
686     if( orientation == ReadTOP )
687       {
688       drawOneLine( scrn_rect.left,
689                    bias_bar.y,
690                    scrn_rect.left,
691                    mid_scr.y);
692
693       drawOneLine( scrn_rect.right,
694                    bias_bar.y,
695                    scrn_rect.right,
696                    mid_scr.y );
697
698       drawOneLine( mid_scr.x,
699                    scrn_rect.bottom,
700                    mid_scr.x,
701                    bias_bar.y );
702
703       }
704     else {
705       if( orientation == ReadBOTTOM )
706         {
707         drawOneLine( scrn_rect.left,
708                      bias_bar.y,
709                      scrn_rect.left,
710                      mid_scr.y );
711
712         drawOneLine( scrn_rect.right,
713                      bias_bar.y,
714                      scrn_rect.right,
715                      mid_scr.y );
716
717         drawOneLine( mid_scr.x,
718                      scrn_rect.top,
719                      mid_scr.x,
720                      bias_bar.y );
721         }
722       }
723
724     for( i = (int)vmin; i <= (int)vmax; i++ )  // increment is faster than addition
725       {
726 //      if( sub_type == LIMIT ) {
727         condition = (i >= min_val());
728 //        }
729 //      else {
730 //        if( sub_type == NOLIMIT ) {
731 //          condition = 1;
732 //          }
733 //        }
734       if( condition )        {
735         marker_x = scrn_rect.left + (int)((i - vmin) * factor());
736         if( div_min()){
737           if( (i%(int)div_min()) == 0 ) {
738 //            if( orientation == ReadTOP )
739 //              {
740 //              drawOneLine( marker_x, marker_y, marker_x, marker_y + 3 );
741               drawOneLine( marker_x, bias_bar.y, marker_x, mid_scr.y );
742 //              }
743 //            else {
744 //            drawOneLine( marker_x, marker_y + 3, marker_x, marker_y + 6 );
745 //              drawOneLine( marker_x, bias_bar.y, marker_x, mid_scr.y );
746 //              }
747             }
748            }
749         if( div_max()) {
750           if( (i%(int)div_max())==0 )
751             {
752             if(modulo()) {
753               disp_val = i % modulo();
754               if( disp_val < 0) {
755                 disp_val += modulo();
756                 }
757               }
758             else {
759               disp_val = i;
760               }
761               sprintf( TextScale, "%d", disp_val );
762             if( orientation == ReadTOP )
763               {
764 //              drawOneLine( marker_x, marker_y, marker_x, marker_y+6 );
765               drawOneLine( marker_x, mid_scr.y + 3,marker_x, bias_bar.y );
766               textString ( marker_x - 4 * strlen(TextScale), marker_y + 14,
767                            TextScale, GLUT_BITMAP_8_BY_13 );
768               }
769             else {
770 //            drawOneLine( marker_x, marker_y, marker_x, marker_y+6 );
771               drawOneLine( marker_x, mid_scr.y - 3,marker_x, bias_bar.y );
772               textString ( marker_x - 4 * strlen(TextScale), mid_scr.y - 14,
773                            TextScale, GLUT_BITMAP_8_BY_13 );
774               }
775             }
776           }  
777         }
778       }
779     }
780    }
781 }
782
783 //============== Top of guage_instr class member definitions ==============
784
785 guage_instr ::
786     guage_instr( RECT         the_box,
787                  DBLFNPTR     load_fn,
788                  ReadOriented readway,
789                  int          maxValue,
790                  int          minValue,
791                  UINT         major_divs,
792                  UINT         minor_divs,
793                  UINT         modulus,
794                  bool         working) :
795            instr_scale( the_box,
796                         load_fn, readway,
797                         maxValue, maxValue, minValue,
798                         major_divs, minor_divs,
799                         modulus,
800                         working)
801 {
802 }
803
804 guage_instr ::
805    ~guage_instr()
806 {
807 }
808
809 guage_instr ::
810     guage_instr( const guage_instr & image):
811        instr_scale( (instr_scale &) image)
812 {
813 }
814
815 guage_instr & guage_instr ::
816     operator = (const guage_instr & rhs )
817 {
818   if( !(this == &rhs)) {
819     instr_scale::operator = (rhs);
820     }
821   return *this;
822 }
823
824 // As implemented, draw only correctly draws a horizontal or vertical
825 // scale. It should contain a variation that permits clock type displays.
826 // Now is supports "tickless" displays such as control surface indicators.
827 // This routine should be worked over before using. Current value would be
828 // fetched and not used if not commented out. Clearly that is intollerable.
829
830 void guage_instr :: draw (void)
831 {
832   int marker_x;
833   int marker_y;
834   register i;
835   char TextScale[80];
836 //  int condition;
837   int disp_val;
838   double vmin              = min_val();
839   double vmax              = max_val();
840   POINT mid_scr            = get_centroid();
841 //  double cur_value         = get_value();
842   RECT   scrn_rect         = get_location();
843   ReadOriented orientation = get_orientation();
844
845   if( (orientation == ReadLEFT) ||( orientation == ReadRIGHT))  // Vertical scale
846     {
847     mid_scr = get_centroid();
848     if( orientation == ReadLEFT )     // Calculate x marker offset
849       marker_x = scrn_rect.left - 6;
850     else                              // We'll default this for now.
851       marker_x = scrn_rect.right;
852
853     // Draw the basic markings for the scale...
854
855     if( orientation == ReadLEFT )
856       {
857
858       drawOneLine( scrn_rect.right - 3,     // Bottom tick bar
859                    scrn_rect.bottom,
860                    scrn_rect.right,
861                    scrn_rect.bottom );
862
863       drawOneLine( scrn_rect.right - 3,     // Top tick bar
864                    scrn_rect.top,
865                    scrn_rect.right,
866                    scrn_rect.top );
867       }
868     else       {
869       drawOneLine( scrn_rect.right,
870                    scrn_rect.bottom,
871                    scrn_rect.right+3,
872                    scrn_rect.bottom );
873
874       drawOneLine( scrn_rect.right,
875                    scrn_rect.top,
876                    scrn_rect.right+3,
877                    scrn_rect.top );
878       }
879
880     // Work through from bottom to top of scale. Calculating where to put
881     // minor and major ticks.
882
883     for( i = (int)vmin; i <= (int)vmax; i++ )
884       {
885         // Calculate the location of this tick
886       marker_y = scrn_rect.bottom + (int)((i - vmin) * factor());
887
888       if( div_min()) {
889         if( (i%div_min()) == 0) {
890           if( orientation == ReadLEFT )
891             {
892             drawOneLine( marker_x + 3, marker_y, marker_x + 6, marker_y );
893             }
894           else {
895             drawOneLine( marker_x, marker_y, marker_x + 3, marker_y );
896             }
897           }
898         }
899       if( div_max()) {
900         if( (i%div_max()) == 0 )            {
901           drawOneLine( marker_x,     marker_y,
902                        marker_x + 6, marker_y );
903           if(modulo()) {
904             disp_val = i % modulo();
905             if( disp_val < 0) {
906               disp_val += modulo();
907               }
908             }
909           else {
910             disp_val = i;
911             }
912           sprintf( TextScale, "%d", disp_val );
913           if( orientation == ReadLEFT )              {
914             textString( marker_x -  8 * strlen(TextScale) - 2, marker_y - 4,
915                         TextScale, GLUT_BITMAP_8_BY_13 );
916             }
917           else  {
918             if( orientation == ReadRIGHT )              {
919             textString( marker_x + 10,                         marker_y - 4,
920                         TextScale, GLUT_BITMAP_8_BY_13 );
921               }
922             }
923           }
924         } // End if condition
925       } // End for range of i from vmin to vmax
926     }  // End if VERTICAL SCALE TYPE
927   else {                                // Horizontal scale by default
928     {
929     if( orientation == ReadTOP ) {
930       marker_y = scrn_rect.bottom;
931       }
932     else {
933       if( orientation == ReadBOTTOM ) {
934         marker_y = scrn_rect.bottom - 6;
935         }
936       }
937     drawOneLine( scrn_rect.left,
938                  scrn_rect.bottom,
939                  scrn_rect.right,
940                  scrn_rect.bottom );
941
942     if( orientation == ReadTOP )
943       {
944       drawOneLine( scrn_rect.left,
945                    scrn_rect.bottom,
946                    scrn_rect.left,
947                    scrn_rect.bottom + 3 );
948
949       drawOneLine( scrn_rect.right,
950                    scrn_rect.bottom,
951                    scrn_rect.right,
952                    scrn_rect.bottom + 6 );
953       }
954     else {
955       if( orientation == ReadBOTTOM )
956         {
957         drawOneLine( scrn_rect.left,
958                      scrn_rect.bottom,
959                      scrn_rect.left,
960                      scrn_rect.bottom - 6 );
961
962         drawOneLine( scrn_rect.right,
963                      scrn_rect.bottom,
964                      scrn_rect.right,
965                      scrn_rect.bottom - 6 );
966         }
967       }
968
969     for( i = (int)vmin; i <= (int)vmax; i++ )  // increment is faster than addition
970       {
971       marker_x = scrn_rect.left + (int)((i - vmin) * factor());
972       if( div_min()) {
973         if( (i%div_min()) == 0 ) {
974           if( orientation == ReadTOP )
975             {
976             drawOneLine( marker_x, marker_y, marker_x, marker_y + 3 );
977             }
978           else {
979             if( orientation == ReadBOTTOM )
980               {
981               drawOneLine( marker_x, marker_y + 3, marker_x, marker_y + 6 );
982               }
983             }
984           }  // End if minor tick called for.
985         }  // End if minor ticks are of interest
986       if( div_max()) {
987         if( (i%div_max())==0 )
988           {
989           // Modulo implies a "clock" style instrument. Needs work.
990           if(modulo()) {
991             disp_val = i % modulo();
992             if( disp_val < 0) {
993               disp_val += modulo();
994               }
995             }
996           else {   // Scale doesn't roll around.
997             disp_val = i;
998             }
999             sprintf( TextScale, "%d", disp_val );
1000           if( orientation == ReadTOP )
1001             {
1002             drawOneLine( marker_x, marker_y, marker_x, marker_y+6 );
1003             textString ( marker_x - 4 * strlen(TextScale), marker_y + 14,
1004                          TextScale, GLUT_BITMAP_8_BY_13 );
1005             }
1006           else {
1007             if( orientation == ReadBOTTOM )
1008               {
1009               drawOneLine( marker_x, marker_y, marker_x, marker_y+6 );
1010               textString ( marker_x - 4 * strlen(TextScale), marker_y - 14,
1011                            TextScale, GLUT_BITMAP_8_BY_13 );
1012               } // End if bottom
1013             } // End else if not ReadTOP
1014           } // End if major division point.
1015         } // End if major divisions of interest.
1016       }
1017     }
1018    }
1019 }
1020
1021 //============ Top of dual_instr_item class member definitions ============
1022
1023 dual_instr_item ::
1024   dual_instr_item ( RECT         the_box,
1025                     DBLFNPTR     chn1_source,
1026                     DBLFNPTR     chn2_source,
1027                     bool         working,
1028                     ReadOriented readway ):
1029                   instr_item( the_box, chn1_source, readway, working),
1030                   alt_data_source( chn2_source )
1031 {
1032 }
1033
1034 dual_instr_item ::
1035   dual_instr_item( const dual_instr_item & image) :
1036                  instr_item ((instr_item &) image ),
1037                  alt_data_source( image.alt_data_source)
1038 {
1039 }
1040
1041 dual_instr_item & dual_instr_item ::
1042   operator = (const dual_instr_item & rhs )
1043 {
1044   if( !(this == &rhs)) {
1045     instr_item::operator = (rhs);
1046     alt_data_source = rhs.alt_data_source;
1047     }
1048   return *this;
1049 }
1050
1051 //============ Top of fgTBI_instr class member definitions ==============
1052
1053 fgTBI_instr ::
1054 fgTBI_instr( RECT      the_box,
1055              DBLFNPTR  chn1_source,
1056              DBLFNPTR  chn2_source,
1057              UINT      maxBankAngle,
1058              UINT      maxSlipAngle,
1059              UINT      gap_width,
1060              bool      working ) :
1061                dual_instr_item( the_box,
1062                                 chn1_source,
1063                                 chn2_source,
1064                                 working,
1065                                 ReadTOP),
1066                BankLimit      (maxBankAngle),
1067                SlewLimit      (maxSlipAngle),
1068                scr_hole       (gap_width   )
1069 {
1070 }
1071
1072 fgTBI_instr :: ~fgTBI_instr() {}
1073
1074 fgTBI_instr :: fgTBI_instr( const fgTBI_instr & image):
1075                  dual_instr_item( (const dual_instr_item &) image),
1076                  BankLimit( image.BankLimit),
1077                  SlewLimit( image.SlewLimit),
1078                  scr_hole ( image.scr_hole )
1079 {
1080 }
1081
1082 fgTBI_instr & fgTBI_instr ::
1083 operator = (const fgTBI_instr & rhs )
1084 {
1085   if( !(this == &rhs)) {
1086     dual_instr_item::operator = (rhs);
1087     BankLimit = rhs.BankLimit;
1088     SlewLimit = rhs.SlewLimit;
1089     scr_hole  = rhs.scr_hole;
1090     }
1091    return *this;
1092 }
1093
1094 //
1095 //      Draws a Turn Bank Indicator on the screen
1096 //
1097
1098  void fgTBI_instr :: draw( void )
1099 {
1100   int x_inc1, y_inc1;
1101   int x_inc2, y_inc2;
1102   int x_t_inc1, y_t_inc1;
1103
1104   int d_bottom_x, d_bottom_y;
1105   int d_right_x, d_right_y;
1106   int d_top_x, d_top_y;
1107   int d_left_x, d_left_y;
1108
1109   int inc_b_x, inc_b_y;
1110   int inc_r_x, inc_r_y;
1111   int inc_t_x, inc_t_y;
1112   int inc_l_x, inc_l_y;
1113   RECT My_box = get_location();
1114   POINT centroid = get_centroid();
1115   int tee_height = My_box.top - My_box.bottom; // Hack, hack.
1116   
1117 //      struct fgFLIGHT *f = &current_aircraft.flight;
1118   double sin_bank, cos_bank;
1119   double bank_angle, sideslip_angle;
1120   double ss_const; // sideslip angle pixels per rad
1121
1122   bank_angle     = current_ch2();  // Roll limit +/- 30 degrees
1123   if( bank_angle < -FG_PI_2/3 ) {
1124     bank_angle = -FG_PI_2/3;
1125   }else if( bank_angle > FG_PI_2/3 ) {
1126     bank_angle = FG_PI_2/3;
1127   }
1128   sideslip_angle = current_ch1(); // Sideslip limit +/- 20 degrees
1129   if( sideslip_angle < -FG_PI/9 ) {
1130     sideslip_angle = -FG_PI/9;
1131   } else if( sideslip_angle > FG_PI/9 ) {
1132     sideslip_angle = FG_PI/9;
1133   }
1134
1135         // sin_bank = sin( FG_2PI-FG_Phi );
1136         // cos_bank = cos( FG_2PI-FG_Phi );
1137   sin_bank = sin(FG_2PI-bank_angle);
1138   cos_bank = cos(FG_2PI-bank_angle);
1139   
1140   x_inc1 = (int)(get_span() * cos_bank);
1141   y_inc1 = (int)(get_span() * sin_bank);
1142   x_inc2 = (int)(scr_hole  * cos_bank);
1143   y_inc2 = (int)(scr_hole  * sin_bank);
1144
1145   x_t_inc1 = (int)(tee_height * sin_bank);
1146   y_t_inc1 = (int)(tee_height * cos_bank);
1147   
1148   d_bottom_x = 0;
1149   d_bottom_y = (int)(-scr_hole);
1150   d_right_x  = (int)(scr_hole);
1151   d_right_y  = 0;
1152   d_top_x    = 0;
1153   d_top_y    = (int)(scr_hole);
1154   d_left_x   = (int)(-scr_hole);
1155   d_left_y   = 0;
1156
1157   ss_const = (get_span()*2)/(FG_2PI/9);  // width represents 40 degrees
1158
1159   d_bottom_x += (int)(sideslip_angle*ss_const);
1160   d_right_x  += (int)(sideslip_angle*ss_const);
1161   d_left_x   += (int)(sideslip_angle*ss_const);
1162   d_top_x    += (int)(sideslip_angle*ss_const);
1163   
1164   inc_b_x = (int)(d_bottom_x*cos_bank-d_bottom_y*sin_bank);
1165   inc_b_y = (int)(d_bottom_x*sin_bank+d_bottom_y*cos_bank);
1166   inc_r_x = (int)(d_right_x*cos_bank-d_right_y*sin_bank);
1167   inc_r_y = (int)(d_right_x*sin_bank+d_right_y*cos_bank);
1168   inc_t_x = (int)(d_top_x*cos_bank-d_top_y*sin_bank);
1169   inc_t_y = (int)(d_top_x*sin_bank+d_top_y*cos_bank);
1170   inc_l_x = (int)(d_left_x*cos_bank-d_left_y*sin_bank);
1171   inc_l_y = (int)(d_left_x*sin_bank+d_left_y*cos_bank);
1172
1173   if( scr_hole == 0 )
1174     {
1175     drawOneLine( centroid.x - x_inc1, centroid.y - y_inc1, \
1176                  centroid.x + x_inc1, centroid.y + y_inc1 );
1177     }
1178   else
1179     {
1180     drawOneLine( centroid.x - x_inc1, centroid.y - y_inc1, \
1181                  centroid.x - x_inc2, centroid.y - y_inc2 );
1182     drawOneLine( centroid.x + x_inc2, centroid.y + y_inc2, \
1183                  centroid.x + x_inc1, centroid.y + y_inc1 );
1184     }
1185
1186   // draw teemarks
1187   drawOneLine( centroid.x + x_inc2,            \
1188                  centroid.y + y_inc2,          \
1189                centroid.x + x_inc2 + x_t_inc1, \
1190                  centroid.y + y_inc2 - y_t_inc1 );
1191   drawOneLine( centroid.x - x_inc2,            \
1192                  centroid.y - y_inc2,          \
1193                centroid.x - x_inc2 + x_t_inc1, \
1194                  centroid.y - y_inc2 - y_t_inc1 );
1195                
1196   // draw sideslip diamond (it is not yet positioned correctly )
1197   drawOneLine( centroid.x + inc_b_x, \
1198                centroid.y + inc_b_y, \
1199                centroid.x + inc_r_x, \
1200                centroid.y + inc_r_y );
1201   drawOneLine( centroid.x + inc_r_x, \
1202                centroid.y + inc_r_y, \
1203                centroid.x + inc_t_x, \
1204                centroid.y + inc_t_y );
1205   drawOneLine( centroid.x + inc_t_x, \
1206                centroid.y + inc_t_y, \
1207                centroid.x + inc_l_x, \
1208                centroid.y + inc_l_y );
1209   drawOneLine( centroid.x + inc_l_x, \
1210                centroid.y + inc_l_y, \
1211                centroid.x + inc_b_x, \
1212                centroid.y + inc_b_y );
1213
1214 }
1215
1216 //====================== Top of HudLadder Class =======================
1217 HudLadder ::
1218   HudLadder(  RECT      the_box,
1219               DBLFNPTR  ptch_source,
1220               DBLFNPTR  roll_source,
1221               UINT      span_units,
1222               int       major_div,
1223               UINT      minor_div,
1224               UINT      screen_hole,
1225               UINT      lbl_pos,
1226               bool      working) :
1227                dual_instr_item( the_box,
1228                                 ptch_source,
1229                                 roll_source,
1230                                 working,
1231                                 ReadRIGHT ),
1232                width_units    ( span_units   ),
1233                div_units      ( major_div < 0? -major_div: major_div ),
1234                minor_div      ( minor_div    ),
1235                label_pos      ( lbl_pos      ),
1236                scr_hole       ( screen_hole  ),
1237                vmax           ( span_units/2 ),
1238                vmin           ( -vmax        )
1239 {
1240   if( !width_units ) {
1241     width_units = 45;
1242     }
1243   factor = (double)get_span() / (double) width_units;
1244 }
1245
1246 HudLadder ::
1247   ~HudLadder()
1248 {
1249 }
1250
1251 HudLadder ::
1252   HudLadder( const HudLadder & image ) :
1253         dual_instr_item( (dual_instr_item &) image),
1254         width_units    ( image.width_units   ),
1255         div_units      ( image.div_units     ),
1256         label_pos      ( image.label_pos     ),
1257         scr_hole       ( image.scr_hole      ),
1258         vmax           ( image.vmax ),
1259         vmin           ( image.vmin ),
1260         factor         ( image.factor        )
1261 {
1262 }
1263 HudLadder & HudLadder ::
1264   operator = ( const HudLadder & rhs )
1265 {
1266   if( !(this == &rhs)) {
1267     (dual_instr_item &)(*this) = (dual_instr_item &)rhs;
1268     width_units  = rhs.width_units;
1269     div_units    = rhs.div_units;
1270     label_pos    = rhs.label_pos;
1271     scr_hole     = rhs.scr_hole;
1272     vmax         = rhs.vmax;
1273     vmin         = rhs.vmin;
1274     factor       = rhs.factor;
1275     }
1276   return *this;
1277 }
1278
1279 //
1280 //      Draws a climb ladder in the center of the HUD
1281 //
1282
1283 void HudLadder :: draw( void )
1284 {
1285   double roll_value, pitch_value;
1286 //  int marker_x;
1287   int marker_y;
1288   int scr_min;
1289 //  int scr_max;
1290   int x_ini, x_end;
1291   int y_ini, y_end;
1292   int new_x_ini, new_x_end;
1293   int new_y_ini, new_y_end;
1294   register i;
1295   POINT centroid = get_centroid();
1296   RECT  box      = get_location();
1297   int half_span  = (box.right - box.left) >> 1;
1298   char TextLadder[80];
1299   int condition;
1300
1301   roll_value  = current_ch2();
1302   pitch_value = current_ch1() * RAD_TO_DEG;
1303
1304   vmin        = (int)(pitch_value - (double)width_units/2.0);
1305   vmax        = (int)(pitch_value + (double)width_units/2.0);
1306
1307   scr_min     = box.bottom; // centroid.y - ((box.top - box.bottom) >> 1);
1308 //  scr_max     = box.top;    // scr_min    + (box.top - box.bottom);
1309 //  marker_x    = centroid.x - half_span;
1310
1311 // Box the target.
1312   drawOneLine( centroid.x - 5, centroid.y,     centroid.x,     centroid.y + 5);
1313   drawOneLine( centroid.x,     centroid.y + 5, centroid.x + 5, centroid.y);
1314   drawOneLine( centroid.x + 5, centroid.y,     centroid.x,     centroid.y - 5);
1315   drawOneLine( centroid.x,     centroid.y - 5, centroid.x - 5, centroid.y);
1316
1317   for( i=(int)vmin; i<=(int)vmax; i+=1 )
1318     {
1319     condition = 1;
1320     if( condition )
1321       {
1322       marker_y = scr_min + (int)((i-vmin)*factor);
1323       if( div_units ) {
1324         if( i%div_units==0 )
1325           {
1326           sprintf( TextLadder, "%d", i );
1327           if( scr_hole == 0 )
1328             {
1329             if( i ) {
1330               x_ini = centroid.x - half_span;
1331               }
1332             else {
1333               x_ini = centroid.x - half_span - 10;
1334               }
1335             y_ini = marker_y;
1336             x_end = centroid.x + half_span;
1337             y_end = marker_y;
1338             new_x_ini = centroid.x + (int)(                     \
1339                        (x_ini - centroid.x) * cos(roll_value) - \
1340                        (y_ini - centroid.y) * sin(roll_value));
1341             new_y_ini = centroid.y + (int)(                     \
1342                        (x_ini - centroid.x) * sin(roll_value) + \
1343                        (y_ini - centroid.y) * cos(roll_value));
1344             new_x_end = centroid.x + (int)(                       \
1345                        (x_end - centroid.x) * cos(roll_value) - \
1346                        (y_end - centroid.y) * sin(roll_value));
1347             new_y_end = centroid.y + (int)(                            \
1348                        (x_end - centroid.x) * sin(roll_value) + \
1349                        (y_end - centroid.y) * cos(roll_value));
1350
1351             if( i >= 0 )
1352               {
1353               drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end );
1354               }
1355             else
1356               {
1357               glEnable(GL_LINE_STIPPLE);
1358               glLineStipple( 1, 0x00FF );
1359               drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end );
1360               glDisable(GL_LINE_STIPPLE);
1361               }
1362             textString( new_x_ini -  8 * strlen(TextLadder) - 8,
1363                         new_y_ini -  4,
1364                         TextLadder, GLUT_BITMAP_8_BY_13 );
1365             textString( new_x_end + 10,
1366                         new_y_end -  4,
1367                         TextLadder, GLUT_BITMAP_8_BY_13 );
1368             }
1369           else
1370             {
1371             if( i != 0 )  {
1372               x_ini = centroid.x - half_span;
1373               }
1374             else          {
1375               x_ini = centroid.x - half_span - 10;
1376               }
1377             y_ini = marker_y;
1378             x_end = centroid.x - half_span + scr_hole/2;
1379             y_end = marker_y;
1380             new_x_ini = centroid.x+ (int)(                      \
1381                         (x_ini - centroid.x) * cos(roll_value) -\
1382                         (y_ini - centroid.y) * sin(roll_value));
1383             new_y_ini = centroid.y+  (int)(                     \
1384                         (x_ini - centroid.x) * sin(roll_value) +\
1385                         (y_ini - centroid.y) * cos(roll_value));
1386             new_x_end = centroid.x+  (int)(                     \
1387                         (x_end - centroid.x) * cos(roll_value) -\
1388                         (y_end - centroid.y) * sin(roll_value));
1389             new_y_end = centroid.y+ (int)(                      \
1390                         (x_end - centroid.x) * sin(roll_value) +\
1391                         (y_end - centroid.y) * cos(roll_value));
1392
1393             if( i >= 0 )
1394               {
1395               drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end );
1396               }
1397             else
1398               {
1399               glEnable(GL_LINE_STIPPLE);
1400               glLineStipple( 1, 0x00FF );
1401               drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end );
1402               glDisable(GL_LINE_STIPPLE);
1403               }
1404             textString( new_x_ini - 8 * strlen(TextLadder) - 8,
1405                         new_y_ini - 4,
1406                         TextLadder, GLUT_BITMAP_8_BY_13 );
1407
1408             x_ini = centroid.x + half_span - scr_hole/2;
1409             y_ini = marker_y;
1410             if( i != 0 )  {
1411               x_end = centroid.x + half_span;
1412               }
1413             else          {
1414               x_end = centroid.x + half_span + 10;
1415               }
1416             y_end = marker_y;
1417             new_x_ini = centroid.x + (int)(                 \
1418                         (x_ini-centroid.x)*cos(roll_value) -\
1419                         (y_ini-centroid.y)*sin(roll_value));
1420             new_y_ini = centroid.y + (int)(                 \
1421                         (x_ini-centroid.x)*sin(roll_value) +\
1422                         (y_ini-centroid.y)*cos(roll_value));
1423             new_x_end = centroid.x + (int)(                 \
1424                         (x_end-centroid.x)*cos(roll_value) -\
1425                         (y_end-centroid.y)*sin(roll_value));
1426             new_y_end = centroid.y +  (int)(                  \
1427                         (x_end-centroid.x)*sin(roll_value) +\
1428                         (y_end-centroid.y)*cos(roll_value));
1429
1430             if( i >= 0 )
1431               {
1432               drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end );
1433               }
1434             else
1435               {
1436               glEnable(GL_LINE_STIPPLE);
1437               glLineStipple( 1, 0x00FF );
1438               drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end );
1439               glDisable(GL_LINE_STIPPLE);
1440               }
1441             textString( new_x_end+10, new_y_end-4,
1442                         TextLadder, GLUT_BITMAP_8_BY_13 );
1443             }
1444           }
1445          }
1446             /* if( i%div_max()==0 )
1447             {
1448                 drawOneLine( marker_x, marker_y, marker_x+6, marker_y );
1449                 sprintf( TextScale, "%d", i );
1450                 if( orientation == LEFT )
1451                 {
1452                         textString( marker_x-8*strlen(TextScale)-2, marker_y-4,
1453                               TextScale, GLUT_BITMAP_8_BY_13 );
1454                 }
1455                 else if( orientation == RIGHT )
1456                 {
1457                         textString( marker_x+10, marker_y-4,
1458                               TextScale, GLUT_BITMAP_8_BY_13 );
1459                 }
1460             } */
1461         }
1462     }
1463
1464 }
1465
1466 //========================= End of Class Implementations===================
1467 // fgHUDInit
1468 //
1469 // Constructs a HUD object and then adds in instruments. At the present
1470 // the instruments are hard coded into the routine. Ultimately these need
1471 // to be defined by the aircraft's instrumentation records so that the
1472 // display for a Piper Cub doesn't show the speed range of a North American
1473 // mustange and the engine readouts of a B36!
1474 //
1475
1476 #define INSTRDEFS 17
1477
1478 int fgHUDInit( fgAIRCRAFT * /* current_aircraft */ )
1479 {
1480   instr_item *HIptr;
1481   int index;
1482   RECT loc;
1483
1484   fgPrintf( FG_COCKPIT, FG_INFO, "Initializing current aircraft HUD\n" );
1485
1486   HUD_deque.erase( HUD_deque.begin(), HUD_deque.end());  // empty the HUD deque
1487
1488 //  hud->code = 1;
1489 //  hud->status = 0;
1490
1491   // For now lets just hardcode the hud here.
1492   // In the future, hud information has to come from the same place
1493   // aircraft information came from.
1494
1495 //  fgHUDSetTimeMode( hud, NIGHT );
1496 //  fgHUDSetBrightness( hud, BRT_LIGHT );
1497
1498   for( index = 0; index <= INSTRDEFS; index++) {
1499     switch ( index ) {
1500       case 0:     // TBI
1501       //  fgHUDAddHorizon( hud, 330, 100, 40, 5, 10, get_roll, get_sideslip );
1502         loc.left   =  330 - 40;
1503         loc.top    =  110;
1504         loc.right  =  330 + 40;
1505         loc.bottom =  100;
1506         HIptr = (instr_item *) new fgTBI_instr( loc );
1507         break;
1508
1509       case 1:     // Artificial Horizon
1510       //  fgHUDAddLadder ( hud, 330, 285, 120, 180, 70, 10,
1511       //                   NONE, 45, get_roll, get_pitch );
1512         loc.left   =  270; // 330 -  60
1513         loc.top    =  375; // 285 +  90
1514         loc.right  =  390; // 330 +  60
1515         loc.bottom =  195; // 285 -  90
1516         HIptr = (instr_item *) new HudLadder( loc );
1517         break;
1518
1519       case 2:    // KIAS
1520       //  fgHUDAddScale  ( hud, VERTICAL,     LIMIT, 200, 180, 380,  5,  10,
1521       //                      LEFT,     0,  100,   50,   0, get_speed );
1522         loc.left   =  160;
1523         loc.top    =  380;
1524         loc.right  =  200;
1525         loc.bottom =  180;
1526         HIptr = (instr_item *) new moving_scale( loc,
1527                                                  get_speed,
1528                                                  ReadLEFT,
1529                                                  200, 0,
1530                                                  10,  5,
1531                                                  0,
1532                                                  50.0,
1533                                                  TRUE);
1534         break;
1535
1536       case 3:    // Angle of Attack
1537       //  fgHUDAddScale  ( hud, HORIZONTAL, NOLIMIT, 180, 250, 410,  1,   5,
1538       //                      BOTTOM, -40,   50,   21,   0, get_aoa );
1539         loc.left   =  250;
1540         loc.top    =  190;
1541         loc.right  =  410;
1542         loc.bottom =  160;
1543         HIptr = (instr_item *) new moving_scale( loc,
1544                                                  get_aoa,
1545                                                  ReadBOTTOM,
1546                                                  50, -40,
1547                                                  5,    1,
1548                                                  0,
1549                                                  21.0,
1550                                                  TRUE);
1551         break;
1552
1553       case 4:    // GYRO COMPASS
1554       // fgHUDAddScale  ( hud, HORIZONTAL, NOLIMIT, 380, 200, 460,  5,  10,
1555       //                      TOP,      0,   50,   50, 360, get_heading );
1556         loc.left   =  200;
1557         loc.top    =  410;
1558         loc.right  =  460;
1559         loc.bottom =  380;
1560         HIptr = (instr_item *) new moving_scale( loc,
1561                                                  get_heading,
1562                                                  ReadTOP,
1563                                                  360, 0,
1564                                                  10,   5,
1565                                                  360,
1566                                                  50,
1567                                                  TRUE);
1568         break;
1569
1570       case 5:    // AMSL
1571       //  fgHUDAddScale  ( hud, VERTICAL,     LIMIT, 460, 180, 380, 25, 100,
1572       //                      RIGHT,    0, 15000, 250,   0, get_altitude);
1573         loc.left   =  460;
1574         loc.top    =  380;
1575         loc.right  =  490;
1576         loc.bottom =  180;
1577         HIptr = (instr_item *) new moving_scale( loc,
1578                                                  get_altitude,
1579                                                  ReadRIGHT,
1580                                                  15000, 0,
1581                                                  100,  25,
1582                                                  0,
1583                                                  250,
1584                                                  TRUE);
1585         break;
1586
1587       case 6:    // Digital KIAS
1588       //  fgHUDAddLabel  ( hud, 160, 150, SMALL, NOBLINK,
1589       //                 RIGHT_JUST, NULL, " Kts",      "%5.0f", get_speed );
1590         loc.left   =  160;
1591         loc.top    =  180; // Ignore
1592         loc.right  =  200; // Ignore
1593         loc.bottom =  150;
1594         HIptr = (instr_item *) new instr_label ( loc,
1595                                                  get_speed,
1596                                                  "%5.0f",
1597                                                  NULL,
1598                                                  " Kts",
1599                                                  ReadTOP,
1600                                                  RIGHT_JUST,
1601                                                  SMALL,
1602                                                  0,
1603                                                  TRUE );
1604         break;
1605
1606       case 7:    // Digital Altimeter
1607       //  fgHUDAddLabel  ( hud, 160, 135, SMALL, NOBLINK,
1608       //              RIGHT_JUST, NULL, " m",        "%5.0f", get_altitude );
1609         loc.left   =  160;
1610         loc.top    =  145; // Ignore
1611         loc.right  =  200; // Ignore
1612         loc.bottom =  135;
1613         HIptr = (instr_item *) new instr_label ( loc,
1614                                                  get_altitude,
1615                                                  "MSL  %5.0f",
1616                                                  NULL,
1617                                                  " m",
1618                                                  ReadTOP,
1619                                                  LEFT_JUST,
1620                                                  SMALL,
1621                                                  0,
1622                                                  TRUE );
1623         break;
1624
1625       case 8:    // Roll indication diagnostic
1626       // fgHUDAddLabel  ( hud, 160, 120, SMALL, NOBLINK,
1627       //                  RIGHT_JUST, NULL, " Roll",     "%5.2f", get_roll );
1628         loc.left   =  160;
1629         loc.top    =  130; // Ignore
1630         loc.right  =  200; // Ignore
1631         loc.bottom =  120;
1632         HIptr = (instr_item *) new instr_label ( loc,
1633                                                  get_roll,
1634                                                  "%5.2f",
1635                                                  " Roll",
1636                                                  " Deg",
1637                                                  ReadTOP,
1638                                                  RIGHT_JUST,
1639                                                  SMALL,
1640                                                  0,
1641                                                  TRUE );
1642         break;
1643
1644       case 9:    // Angle of attack diagnostic
1645       //  fgHUDAddLabel  ( hud, 440, 150, SMALL, NOBLINK,
1646       //                   RIGHT_JUST, NULL, " AOA",      "%5.2f", get_aoa );
1647         loc.left   =  440;
1648         loc.top    =  160; // Ignore
1649         loc.right  =  500; // Ignore
1650         loc.bottom =  150;
1651         HIptr = (instr_item *) new instr_label ( loc,
1652                                                  get_aoa,
1653                                                  "    %5.2f",
1654                                                  " AOA",
1655                                                  " Deg",
1656                                                  ReadTOP,
1657                                                  RIGHT_JUST,
1658                                                  SMALL,
1659                                                  0,
1660                                                  TRUE );
1661         break;
1662
1663       case 10:
1664       //  fgHUDAddLabel  ( hud, 440, 135, SMALL, NOBLINK,
1665       //               RIGHT_JUST, NULL, " Heading",  "%5.0f", get_heading );
1666         loc.left   =  440;
1667         loc.top    =  145; // Ignore
1668         loc.right  =  500; // Ignore
1669         loc.bottom =  135;
1670         HIptr = (instr_item *) new instr_label ( loc,
1671                                                  get_heading,
1672                                                  "%5.0f",
1673                                                  "Heading",
1674                                                  " Deg",
1675                                                  ReadTOP,
1676                                                  RIGHT_JUST,
1677                                                  SMALL,
1678                                                  0,
1679                                                  TRUE );
1680         break;
1681
1682       case 11:
1683       //  fgHUDAddLabel  ( hud, 440, 120, SMALL, NOBLINK,
1684       //              RIGHT_JUST, NULL, " Sideslip", "%5.2f", get_sideslip );
1685         loc.left   =  440;
1686         loc.top    =  130; // Ignore
1687         loc.right  =  500; // Ignore
1688         loc.bottom =  120;
1689         HIptr = (instr_item *) new instr_label ( loc,
1690                                                  get_sideslip,
1691                                                  "%5.2f",
1692                                                  "Sideslip ",
1693                                                  NULL,
1694                                                  ReadTOP,
1695                                                  RIGHT_JUST,
1696                                                  SMALL,
1697                                                  0,
1698                                                  TRUE );
1699         break;
1700
1701       case 12:
1702         loc.left   = 440;
1703         loc.top    =  90; // Ignore
1704         loc.right  = 440; // Ignore
1705         loc.bottom =  90;
1706         HIptr = (instr_item *) new instr_label( loc, get_throttleval,
1707                                                 "%.2f",
1708                                                 "Throttle ",
1709                                                 NULL,
1710                                                 ReadTOP,
1711                                                 RIGHT_JUST,
1712                                                 SMALL,
1713                                                 0,
1714                                                 TRUE );
1715         break;
1716
1717       case 13:
1718         loc.left   = 440;
1719         loc.top    =  70; // Ignore
1720         loc.right  = 500; // Ignore
1721         loc.bottom =  75;
1722         HIptr = (instr_item *) new instr_label( loc, get_elevatorval,
1723                                                 "%5.2f",
1724                                                 "Elevator",
1725                                                 NULL,
1726                                                 ReadTOP,
1727                                                 RIGHT_JUST,
1728                                                 SMALL,
1729                                                 0,
1730                                                 TRUE );
1731         break;
1732
1733       case 14:
1734         loc.left   = 440;
1735         loc.top    = 100; // Ignore
1736         loc.right  = 500; // Ignore
1737         loc.bottom =  60;
1738         HIptr = (instr_item *) new instr_label( loc, get_aileronval,
1739                                                 "%5.2f",
1740                                                 "Aileron",
1741                                                 NULL,
1742                                                 ReadTOP,
1743                                                 RIGHT_JUST,
1744                                                 SMALL,
1745                                                 0,
1746                                                 TRUE );
1747         break;
1748
1749       case 15:
1750         loc.left   = 10;
1751         loc.top    = 100; // Ignore
1752         loc.right  = 500; // Ignore
1753         loc.bottom =  10;
1754         HIptr = (instr_item *) new instr_label( loc, get_frame_rate,
1755                                                 "%.1f",
1756                                                 "Frame rate = ",
1757                                                 NULL,
1758                                                 ReadTOP,
1759                                                 RIGHT_JUST,
1760                                                 SMALL,
1761                                                 0,
1762                                                 TRUE );
1763         break;
1764
1765       case 16:
1766         loc.left   = 10;
1767         loc.top    = 100; // Ignore
1768         loc.right  = 500; // Ignore
1769         loc.bottom =  25;
1770         HIptr = (instr_item *) new instr_label( loc, get_vfc_ratio,
1771                                                 "%.2f",
1772                                                 "VFC Ratio = ",
1773                                                 NULL,
1774                                                 ReadTOP,
1775                                                 RIGHT_JUST,
1776                                                 SMALL,
1777                                                 0,
1778                                                 TRUE );
1779         break;
1780
1781       case 17:
1782         loc.left   = 10;
1783         loc.top    = 100; // Ignore
1784         loc.right  = 500; // Ignore
1785         loc.bottom =  40;
1786         HIptr = (instr_item *) new instr_label( loc, get_fov,
1787                                                 "%.1f",
1788                                                 "FOV = ",
1789                                                 NULL,
1790                                                 ReadTOP,
1791                                                 RIGHT_JUST,
1792                                                 SMALL,
1793                                                 0,
1794                                                 TRUE );
1795         break;
1796
1797       //  fgHUDAddControlSurfaces( hud, 10, 10, NULL );
1798 //        loc.left   =  250;
1799 //        loc.top    =  190;
1800 //        loc.right  =  410;
1801 //        loc.bottom =  180;
1802 //        HIptr = (instr_item *) new
1803 //        break;
1804
1805       default:;
1806       }
1807     if( HIptr ) {                   // Anything to install?
1808       HUD_deque.insert( HUD_deque.begin(), HIptr);
1809       }
1810     }
1811
1812 //  fgHUDAddControl( hud, HORIZONTAL, 50,  25, get_aileronval  ); // was 10, 10
1813 //  fgHUDAddControl( hud, VERTICAL,   150, 25, get_elevatorval ); // was 10, 10
1814 //  fgHUDAddControl( hud, HORIZONTAL, 250, 25, get_rudderval   ); // was 10, 10
1815   return 0;  // For now. Later we may use this for an error code.
1816 }
1817
1818
1819 // fgUpdateHUD
1820 //
1821 // Performs a once around the list of calls to instruments installed in
1822 // the HUD object with requests for redraw. Kinda. It will when this is
1823 // all C++.
1824 //
1825 int global_day_night_switch = DAY;
1826
1827 void fgUpdateHUD( void ) {
1828   int i;
1829   int brightness;
1830 //  int day_night_sw = current_aircraft.controls->day_night_switch;
1831   int day_night_sw = global_day_night_switch;
1832   int hud_displays = HUD_deque.size();
1833   instr_item *pHUDInstr;
1834
1835   if( !hud_displays ) {  // Trust everyone, but ALWAYS cut the cards!
1836     return;
1837     }
1838
1839   pHUDInstr = HUD_deque[0];
1840   brightness = pHUDInstr->get_brightness();
1841 //  brightness = HUD_deque.at(0)->get_brightness();
1842
1843   glMatrixMode(GL_PROJECTION);
1844   glPushMatrix();
1845
1846   glLoadIdentity();
1847   gluOrtho2D(0, 640, 0, 480);
1848   glMatrixMode(GL_MODELVIEW);
1849   glPushMatrix();
1850   glLoadIdentity();
1851
1852   glColor3f(1.0, 1.0, 1.0);
1853   glIndexi(7);
1854
1855   glDisable(GL_DEPTH_TEST);
1856   glDisable(GL_LIGHTING);
1857
1858   glLineWidth(1);
1859
1860   for( i = hud_displays; i; --i) { // Draw everything
1861 //    if( HUD_deque.at(i)->enabled()) {
1862     pHUDInstr = HUD_deque[i - 1];
1863     if( pHUDInstr->enabled()) {
1864                                    // We should to respond to a dial instead
1865                                    // or as well to the of time of day. Of
1866                                    // course, we have no dial!
1867       if( day_night_sw == DAY) {
1868         switch (brightness) {
1869           case BRT_LIGHT:
1870             glColor3f (0.1, 0.9, 0.1);
1871             break;
1872
1873           case BRT_MEDIUM:
1874             glColor3f (0.1, 0.7, 0.0);
1875             break;
1876
1877           case BRT_DARK:
1878             glColor3f (0.0, 0.5, 0.0);
1879             }
1880           }
1881         else {
1882           if( day_night_sw == NIGHT) {
1883             switch (brightness) {
1884               case BRT_LIGHT:
1885                 glColor3f (0.9, 0.1, 0.1);
1886                 break;
1887
1888               case BRT_MEDIUM:
1889                 glColor3f (0.7, 0.0, 0.1);
1890                 break;
1891
1892               case BRT_DARK:
1893               default:
1894                 glColor3f (0.5, 0.0, 0.0);
1895               }
1896             }
1897           else {     // Just in case default
1898             glColor3f (0.1, 0.9, 0.1);
1899             }
1900           }
1901     //  fgPrintf( FG_COCKPIT, FG_DEBUG, "HUD Code %d  Status %d\n",
1902     //            hud->code, hud->status );
1903       pHUDInstr->draw();
1904 //      HUD_deque.at(i)->draw(); // Responsible for broken or fixed variants.
1905                               // No broken displays honored just now.
1906       }
1907     }
1908
1909   glEnable(GL_DEPTH_TEST);
1910   glEnable(GL_LIGHTING);
1911   glMatrixMode(GL_PROJECTION);
1912   glPopMatrix();
1913   glMatrixMode(GL_MODELVIEW);
1914   glPopMatrix();
1915 }
1916
1917 /* $Log$
1918 /* Revision 1.10  1998/05/17 16:58:12  curt
1919 /* Added a View Frustum Culling ratio display to the hud.
1920 /*
1921  * Revision 1.9  1998/05/16 13:04:14  curt
1922  * New updates from Charlie Hotchkiss.
1923  *
1924  * Revision 1.8  1998/05/13 18:27:54  curt
1925  * Added an fov to hud display.
1926  *
1927  * Revision 1.7  1998/05/11 18:13:11  curt
1928  * Complete C++ rewrite of all cockpit code by Charlie Hotchkiss.
1929  *
1930  * Revision 1.22  1998/04/18 04:14:02  curt
1931  * Moved fg_debug.c to it's own library.
1932  *
1933  * Revision 1.21  1998/04/03 21:55:28  curt
1934  * Converting to Gnu autoconf system.
1935  * Tweaks to hud.c
1936  *
1937  * Revision 1.20  1998/03/09 22:48:40  curt
1938  * Minor "formatting" tweaks.
1939  *
1940  * Revision 1.19  1998/02/23 20:18:28  curt
1941  * Incorporated Michele America's hud changes.
1942  *
1943  * Revision 1.18  1998/02/21 14:53:10  curt
1944  * Added Charlie's HUD changes.
1945  *
1946  * Revision 1.17  1998/02/20 00:16:21  curt
1947  * Thursday's tweaks.
1948  *
1949  * Revision 1.16  1998/02/19 13:05:49  curt
1950  * Incorporated some HUD tweaks from Michelle America.
1951  * Tweaked the sky's sunset/rise colors.
1952  * Other misc. tweaks.
1953  *
1954  * Revision 1.15  1998/02/16 13:38:39  curt
1955  * Integrated changes from Charlie Hotchkiss.
1956  *
1957  * Revision 1.14  1998/02/12 21:59:41  curt
1958  * Incorporated code changes contributed by Charlie Hotchkiss
1959  * <chotchkiss@namg.us.anritsu.com>
1960  *
1961  * Revision 1.12  1998/02/09 15:07:48  curt
1962  * Minor tweaks.
1963  *
1964  * Revision 1.11  1998/02/07 15:29:34  curt
1965  * Incorporated HUD changes and struct/typedef changes from Charlie Hotchkiss
1966  * <chotchkiss@namg.us.anritsu.com>
1967  *
1968  * Revision 1.10  1998/02/03 23:20:14  curt
1969  * Lots of little tweaks to fix various consistency problems discovered by
1970  * Solaris' CC.  Fixed a bug in fg_debug.c with how the fgPrintf() wrapper
1971  * passed arguments along to the real printf().  Also incorporated HUD changes
1972  * by Michele America.
1973  *
1974  * Revision 1.9  1998/01/31 00:43:04  curt
1975  * Added MetroWorks patches from Carmen Volpe.
1976  *
1977  * Revision 1.8  1998/01/27 00:47:51  curt
1978  * Incorporated Paul Bleisch's <bleisch@chromatic.com> new debug message
1979  * system and commandline/config file processing code.
1980  *
1981  * Revision 1.7  1998/01/19 18:40:20  curt
1982  * Tons of little changes to clean up the code and to remove fatal errors
1983  * when building with the c++ compiler.
1984  *
1985  * Revision 1.6  1997/12/15 23:54:34  curt
1986  * Add xgl wrappers for debugging.
1987  * Generate terrain normals on the fly.
1988  *
1989  * Revision 1.5  1997/12/10 22:37:39  curt
1990  * Prepended "fg" on the name of all global structures that didn't have it yet.
1991  * i.e. "struct WEATHER {}" became "struct fgWEATHER {}"
1992  *
1993  * Revision 1.4  1997/09/23 00:29:32  curt
1994  * Tweaks to get things to compile with gcc-win32.
1995  *
1996  * Revision 1.3  1997/09/05 14:17:26  curt
1997  * More tweaking with stars.
1998  *
1999  * Revision 1.2  1997/09/04 02:17:30  curt
2000  * Shufflin' stuff.
2001  *
2002  * Revision 1.1  1997/08/29 18:03:22  curt
2003  * Initial revision.
2004  *
2005  */