1 /*
2  * CRRCsim - the Charles River Radio Control Club Flight Simulator Project
3  *
4  * Copyright (C) 2005, 2008 Olivier Bordes (original author)
5  * Copyright (C) 2005 Lionel Cailler
6  * Copyright (C) 2005, 2009-2010 Jens Wilhelm Wulf
7  * Copyright (C) 2005-2009 Jan Reucker
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2
11  * as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU 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., 59 Temple Place, Suite 330,
21  * Boston, MA 02111-1307, USA.
22  *
23  */
24 
25 /**
26  * \file handlerF3A.cpp
27  *
28  * Purpose: Add  F3A functions to crrcsim.
29  *          F3A is a FAI category which define aerobatics contest
30  */
31 #include "../../i18n.h"
32 #include "../../include_gl.h"
33 
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <math.h>
37 #include <iostream>
38 #include "../../global.h"
39 #include "../../aircraft.h"
40 #include "../../crrc_soundserver.h"
41 #include "../../global_video.h"
42 #include "../../crrc_system.h"
43 #include "../../mod_misc/ls_constants.h"
44 #include "../../mod_misc/SimpleXMLTransfer.h"
45 #include "../../mod_misc/lib_conversions.h"
46 #include "../../mod_misc/filesystools.h"
47 #include "../../mod_landscape/crrc_scenery.h"
48 #include "../../record.h"
49 #include "../../robots.h"
50 #include "../../mod_robots/marker.h"
51 #include "../../mod_video/fonts.h"
52 #include "../../mod_video/gloverlay.h"
53 #include "../../SimStateHandler.h"
54 #include "handlerF3A.h"
55 using namespace std;
56 
57 #ifndef TRUE
58 #define TRUE 1
59 #endif
60 
61 #ifndef FALSE
62 #define FALSE 0
63 #endif
64 
65 /** \brief The default constructor
66  *
67  *  Creates an F3A game handler
68  */
HandlerF3A()69 HandlerF3A::HandlerF3A()
70 {
71   prepareConfigFile(cfgfile);
72   //retrieve information from config file
73   draw_grid = cfgfile->getInt("game.f3a.draw_grid");
74   draw_trajectory = cfgfile->getInt("game.f3a.draw_trajectory");
75   draw_indicators = cfgfile->getInt("game.f3a.draw_indicators");
76   persistence = cfgfile->getInt("game.f3a.trajectory_persistence");
77   tolerance = cfgfile->getInt("game.f3a.attitude_tolerance");
78 
79   SimpleXMLTransfer* f3acfg = cfg->getCurLocCfgPtr(cfgfile)->getChild("game.f3a", true);
80   grid_size = f3acfg->getInt ("grid_size");
81   flight_height = f3acfg->getInt ("flight_height");
82   flight_distance = f3acfg->getInt ("flight_distance");
83   set_orientation(f3acfg->getInt("orientation") );
84   center_grid_position_north = f3acfg->getInt("position_north");
85   center_grid_position_east  = f3acfg->getInt("position_east");
86   center_grid_position_elev  = Global::scenery->getHeight(
87                                  center_grid_position_north,
88                                  center_grid_position_east);
89 
90   window_xsize = 0;
91   window_ysize = 0;
92 
93   // states for OpenGL rendering
94   trajectory_rendering_state = new ssgSimpleState();
95   trajectory_rendering_state->disable(GL_CULL_FACE);
96   trajectory_rendering_state->disable(GL_COLOR_MATERIAL);
97   trajectory_rendering_state->disable(GL_TEXTURE_2D);
98   trajectory_rendering_state->disable(GL_LIGHTING);
99   trajectory_rendering_state->enable(GL_BLEND);
100   trajectory_rendering_state->enable(GL_LINE_SMOOTH);
101 
102   grid_rendering_state = new ssgSimpleState();
103   grid_rendering_state->disable(GL_CULL_FACE);
104   grid_rendering_state->disable(GL_COLOR_MATERIAL);
105   grid_rendering_state->disable(GL_TEXTURE_2D);
106   grid_rendering_state->disable(GL_LIGHTING);
107   grid_rendering_state->enable(GL_BLEND);
108   grid_rendering_state->enable(GL_LINE_SMOOTH);
109 
110   text_rendering_state = new ssgSimpleState();
111   text_rendering_state->disable(GL_CULL_FACE);
112   text_rendering_state->disable(GL_COLOR_MATERIAL);
113   text_rendering_state->disable(GL_TEXTURE_2D);
114   text_rendering_state->disable(GL_LIGHTING);
115   text_rendering_state->enable(GL_BLEND);
116   text_rendering_state->setMaterial(GL_EMISSION, 0.0, 0.0, 0.0, 0.0);
117   text_rendering_state->setMaterial(GL_AMBIENT, 1.0, 1.0, 1.0, 1.0);
118   text_rendering_state->setMaterial(GL_DIFFUSE, 1.0, 1.0, 1.0, 1.0);
119   text_rendering_state->setMaterial(GL_SPECULAR, 0.0, 0.0, 0.0, 0.1);
120 
121   reset();
122 }
123 
124 
125 /** \brief The destructor.
126  *
127  *  Deletes an F3A game handler
128  */
~HandlerF3A()129 HandlerF3A::~HandlerF3A()
130 {
131   delete grid_rendering_state;
132   delete trajectory_rendering_state;
133   delete text_rendering_state;
134 }
135 
136 
137 /** \brief Render game-mode-specific details
138  *
139  *  This method renders graphics objects that are specific to the
140  *  F3F game mode, namely the base pylons.
141  */
draw()142 void HandlerF3A::draw()
143 {
144   if (Global::testmode)
145     return;
146 
147   if (draw_grid)
148   {
149     grid_rendering_state->apply();
150     draw_f3a_grid();
151   }
152   if (draw_trajectory)
153   {
154     trajectory_rendering_state->apply();
155     draw_f3a_trajectory();
156   }
157 }
158 
159 
160 /** \brief Print the game-mode-specific text overlay
161  *
162  *  This method renders the text overlay for the F3A mode.
163  *
164  *  \todo This method should use PLIB instead of GLUT for font rendering.
165  *
166  *  \param  ww  current OpenGL window width
167  *  \param  hh  current OpenGL window height
168  */
display_infos(GLfloat ww,GLfloat hh)169 void HandlerF3A::display_infos(GLfloat ww, GLfloat hh)
170 {
171   int y_offset;
172   int lineheight = 35;
173   char astring[256];
174 
175   window_xsize = ww;
176   window_ysize = hh;
177 
178   if (draw_indicators)
179   {
180     // draw indicators overlay
181     GlOverlay::setupRenderingState(window_xsize, window_ysize);
182     draw_f3a_indicators(window_xsize, window_ysize);
183     GlOverlay::restoreRenderingState();
184   }
185 
186   startTextRendering();
187   text_rendering_state->apply();
188 
189   if (ww <= 800)
190   {
191     if (Video::textureFont)
192     {
193       fontRenderer.setFont(Video::textureFont);
194       fontRenderer.setPointSize(16);
195     }
196     else fontRenderer.setFont(fntGetBitmapFont(FNT_BITMAP_HELVETICA_18));
197     y_offset = 45;
198     lineheight = 27;
199   }
200   else
201   {
202     if (Video::textureFont)
203     {
204       fontRenderer.setFont(Video::textureFont);
205       fontRenderer.setPointSize(22);
206     }
207     else fontRenderer.setFont(fntGetBitmapFont(FNT_BITMAP_TIMES_ROMAN_24));
208     y_offset = 55;
209     lineheight = 36;
210   }
211 
212   // debug string
213   sprintf(astring, "- F3A -");
214   output(window_xsize/2 - textLength(astring)/2,
215          window_ysize - y_offset - 0*lineheight,
216          astring);
217 
218   finishTextRendering();
219 }
220 
221 
222 /** \brief Switch to a text rendering state/projection
223  *
224  *  Sets up the OpenGL projection matrix for 2D text rendering.
225  */
startTextRendering() const226 void HandlerF3A::startTextRendering() const
227 {
228   glMatrixMode(GL_PROJECTION);
229   glPushMatrix();
230 
231   glLoadIdentity();
232   gluOrtho2D(0, window_xsize, 0, window_ysize);
233 }
234 
235 
236 /** \brief Switch back to 3D rendering
237  *
238  *  Revert the changes done in startTextRendering()
239  */
finishTextRendering() const240 void HandlerF3A::finishTextRendering() const
241 {
242   glPopMatrix();
243   glMatrixMode(GL_MODELVIEW);
244 }
245 
246 
247 /** \brief Render a text string
248  *
249  *  This method renders a string at a given position using the
250  *  specified font.
251  *
252  *  \todo This method should use PLIB instead of GLUT for font rendering.
253  *
254  *  \param  x     Horizontal start of text
255  *  \param  y     Vertical start of text
256  *  \param  text  the string to be displayed
257  */
output(GLfloat x,GLfloat y,const char * text)258 void HandlerF3A::output(GLfloat x, GLfloat y, const char *text)
259 {
260   fontRenderer.begin();
261   glColor4f(1, 0.3, 0.3, 0.7);
262   fontRenderer.start2f(x, y);
263   fontRenderer.puts(text);
264   fontRenderer.end();
265 }
266 
267 
268 /** \brief Calculate the length of a rendered text string
269  *
270  *  This method calculates the length of a text string rendered
271  *  in the current font.
272  *
273  *  \param    text    pointer to the text to be rendered
274  *  \return   width of the rendered text string
275  */
textLength(const char * text) const276 GLfloat HandlerF3A::textLength(const char *text) const
277 {
278   float left, right;
279 
280   fontRenderer.getFont()->getBBox(text,
281                                   fontRenderer.getPointSize(),
282                                   fontRenderer.getSlant(),
283                                   &left, &right,
284                                   NULL, NULL);
285   return (right - left);
286 }
287 
288 
289 /** \brief Reset the game handler.
290  *
291  *  Everything will be reset to be ready for a new run.
292  */
reset()293 void HandlerF3A::reset()
294 {
295   // reset trajectory storage
296   traj_t0 = 0;
297   traj_first = MAX_TRAJ - 1;
298   traj_last = 0;
299 
300   // reset trajectory slope angle
301   THETA_cg = 0.;
302 }
303 
304 
305 /** \brief Cyclic game-handler function for F3A
306  *
307  *  Check if the model cross pylons A or pylons B.
308  *  Increase BASE counter.
309  */
update(float a,float b,float c,FlightRecorder * recorder,Robots * robots)310 void HandlerF3A::update(float a, float b, float c, FlightRecorder* recorder, Robots* robots)
311 {
312   if (Global::testmode)
313     return;
314 
315   // save previous cg coords
316   float xx_cg_old = XX_cg_grd;
317   float yy_cg_old = YY_cg_grd;
318   float zz_cg_old = ZZ_cg_grd;
319 
320   //convert coords on F3A coords
321   float a0 = a - center_grid_position_north;
322   float b0 = b - center_grid_position_east;
323   float c0 = c - center_grid_position_elev;
324   XX_cg_grd =  - b0 * cos_dir + a0 * sin_dir;
325   YY_cg_grd =  + a0 * cos_dir + b0 * sin_dir;
326   ZZ_cg_grd =  c0;
327 
328   // compute cg trajectory slope angle
329   THETA_cg = 0.0;
330   if (traj_t0)
331   {
332     float dx = XX_cg_grd - xx_cg_old;
333     float dy = YY_cg_grd - yy_cg_old;
334     float dz = ZZ_cg_grd - zz_cg_old;
335     float dl = sqrt(dx*dx + dy*dy + dz*dz);
336     if (dl > 0.1)
337     {
338       float dd = sqrt(dx*dx + dy*dy);
339       THETA_cg = atan2(dz,dd);
340     }
341   }
342 
343   // advance buffer index, avoid overwriting
344   if (++traj_first == MAX_TRAJ)
345     traj_first = 0;
346   if (traj_t0 && (traj_first == traj_last))
347   {
348     if (++traj_last == MAX_TRAJ)
349       traj_last = 0;
350   }
351 
352   // save trajectory in circular buffer
353   traj_x[traj_first] = a;
354   traj_y[traj_first] = b;
355   traj_z[traj_first] = c;
356   traj_t[traj_first] = Global::Simulation->getSimulationTimeSinceReset();
357   traj_CL[traj_first] = Global::aircraft->getFDM()->getFlightCL();
358   if (!traj_t0)
359     traj_t0 = traj_t[traj_first];
360 
361   // identify last time sample
362   int t_last = traj_t[traj_first] - persistence*1000;
363   if (t_last < traj_t0)
364     t_last = traj_t0;
365   while (traj_t[traj_last] < t_last)
366   {
367     if (++traj_last == MAX_TRAJ)
368       traj_last = 0;
369   }
370 }
371 
372 /** \brief Prepare the config file
373  *
374  * This function checks if the config file contains all tags needed
375  * to store the F3A configuration. If a tag is missing, it is
376  * created and filled with a sensible default value.
377  *
378  * \param cfgfile   Pointer to the config file
379  */
prepareConfigFile(SimpleXMLTransfer * cfgfile)380 void HandlerF3A::prepareConfigFile(SimpleXMLTransfer *cfgfile)
381 {
382   int grid_size, flight_height, flight_distance, orientation, position_north, position_east;
383 
384   //general F3A-options
385   cfgfile->makeSureAttributeExists("game.f3a.enabled", "0");
386   cfgfile->makeSureAttributeExists("game.f3a.draw_grid", "0");
387   cfgfile->makeSureAttributeExists("game.f3a.draw_trajectory", "0");
388   cfgfile->makeSureAttributeExists("game.f3a.draw_indicators", "0");
389   cfgfile->makeSureAttributeExists("game.f3a.trajectory_persistence", "30");
390   cfgfile->makeSureAttributeExists("game.f3a.attitude_tolerance", "2");
391 
392   //location specifics parameters
393   SimpleXMLTransfer *xml_scenery = Global::scenery->getXMLsection("F3A");
394   if (!xml_scenery) xml_scenery = Global::scenery->getXMLsection("f3a");
395   if (xml_scenery)
396   {
397     //retrieve information from scenery file description
398     grid_size = xml_scenery->attributeAsInt("grid_size", 100);
399     flight_height = xml_scenery->attributeAsInt("flight_height", 100);
400     flight_distance = xml_scenery->attributeAsInt("flight_distance", 500);
401     orientation = xml_scenery->attributeAsInt("orientation", 0);
402     position_north = xml_scenery->attributeAsInt("position_north", 0);
403     position_east = xml_scenery->attributeAsInt("position_east", 0);
404   }
405   else
406   {
407     //default value
408     grid_size = 100;
409     flight_height = 100;
410     flight_distance = 500;
411     orientation = 0;
412     position_north = 0;
413     position_east = 0;
414   }
415   //put on location section on configfile
416   SimpleXMLTransfer* f3acfg = cfg->getCurLocCfgPtr(cfgfile)->getChild("game.f3a", true);
417   char buf[100];
418   sprintf(buf,"%d",grid_size);
419   f3acfg->makeSureAttributeExists("grid_size", buf);
420   sprintf(buf,"%d",flight_height);
421   f3acfg->makeSureAttributeExists("flight_height", buf);
422   sprintf(buf,"%d",flight_distance);
423   f3acfg->makeSureAttributeExists("flight_distance", buf);
424   sprintf(buf,"%d",orientation);
425   f3acfg->makeSureAttributeExists("orientation", buf);
426   sprintf(buf,"%d",position_north);
427   f3acfg->makeSureAttributeExists("position_north",buf);
428   sprintf(buf,"%d",position_east);
429   f3acfg->makeSureAttributeExists("position_east",buf);
430 }
431 
432 
GetRecordHeader()433 SimpleXMLTransfer* HandlerF3A::GetRecordHeader()
434 {
435   SimpleXMLTransfer* header = new SimpleXMLTransfer();
436   header->setAttribute("mode", "F3A");
437   return(header);
438 }
439 
440 
draw_f3a_grid() const441 void HandlerF3A::draw_f3a_grid() const
442 {
443   GLfloat htan = tan(H_ANGLE*M_PI/180.);
444   GLfloat vtan = tan(V_ANGLE*M_PI/180.);
445   GLfloat xl, xr, y, zb, zt;
446 
447   // grid plane dimensions, relative to base center
448   // y positive to the front, x positive to the left
449   y  = flight_distance;
450   xr = y*htan;
451   xl = - xr;
452   zb = flight_height;
453   zt = y*vtan;
454 
455   draw_f3a_grid_plane(xl, xr, y, zb, zt);
456 }
457 
458 
draw_f3a_grid_plane(GLfloat xl,GLfloat xr,GLfloat y,GLfloat zb,GLfloat zt) const459 void HandlerF3A::draw_f3a_grid_plane(GLfloat xl, GLfloat xr, GLfloat y, GLfloat zb, GLfloat zt) const
460 {
461   GLfloat ln, le, cn, ce, rn, re, z1, z2;
462   GLfloat x, z;
463   GLfloat alpha0 = 0.5;
464   GLfloat color_alpha[] = {1,0,0, alpha0};
465 
466   // grid plane world coords
467   ln = center_grid_position_north + y * cos_dir - xl * sin_dir;
468   cn = center_grid_position_north + y * cos_dir;
469   rn = center_grid_position_north + y * cos_dir - xr * sin_dir;
470   le = center_grid_position_east  + y * sin_dir + xl * cos_dir;
471   ce = center_grid_position_east  + y * sin_dir;
472   re = center_grid_position_east  + y * sin_dir + xr * cos_dir;
473   z1 = center_grid_position_elev  + zb;
474   z2 = center_grid_position_elev  + zt;
475 
476   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
477   glColor4fv(color_alpha);
478 
479   // draw grid bottom and center line
480   glLineWidth(4.0);
481   glBegin(GL_LINES);
482   glVertex3f(le, z1, -ln);
483   glVertex3f(re, z1, -rn);
484   glEnd();
485 
486   glBegin(GL_LINES);
487   glVertex3f(ce, z1, -cn);
488   glVertex3f(ce, z2, -cn);
489   glEnd();
490 
491   // draw grid perimeter
492   color_alpha[3] = alpha0*0.2;
493   glColor4fv(color_alpha);
494   glLineWidth(4.0);
495   glBegin(GL_LINE_STRIP);
496   glVertex3f(le, z1, -ln);
497   glVertex3f(le, z2, -ln);
498   glVertex3f(re, z2, -rn);
499   glVertex3f(re, z1, -rn);
500   glEnd();
501 
502   // draw vertical grid lines
503   glLineWidth(1.0);
504   x = 0.0 + grid_size;
505   while (x < xr)
506   {
507     color_alpha[3] = alpha0*(0.8*(1. - x/xr) + 0.2);
508     glColor4fv(color_alpha);
509     glBegin(GL_LINES);
510     glVertex3f(ce - x*cos_dir, z1, -(cn + x*sin_dir));
511     glVertex3f(ce - x*cos_dir, z2, -(cn + x*sin_dir));
512     glVertex3f(ce + x*cos_dir, z1, -(cn - x*sin_dir));
513     glVertex3f(ce + x*cos_dir, z2, -(cn - x*sin_dir));
514     glEnd();
515 
516     x += grid_size;
517   }
518 
519   // draw horizontal grid lines
520   glLineWidth(1.0);
521   z = z1 + grid_size;
522   while (z < z2)
523   {
524     color_alpha[3] = alpha0*(0.8*(1. - (z - z1)/(z2 - z1)) + 0.2);
525     glColor4fv(color_alpha);
526     glBegin(GL_LINES);
527     glVertex3f(le, z, -ln);
528     glVertex3f(re, z, -rn);
529     glEnd();
530 
531     z += grid_size;
532   }
533 
534   glLineWidth(1.0);
535 }
536 
537 
draw_f3a_trajectory() const538 void HandlerF3A::draw_f3a_trajectory() const
539 {
540   GLfloat alpha0 = 0.4;
541   GLfloat a, b, x, y, north, east;
542   GLfloat color1_alpha[] = {0,0,0, alpha0};
543   GLfloat color2_alpha[] = {1,0,0, alpha0};
544 
545   if (traj_t0)
546   {
547     int t_blur = traj_t[traj_last] + 1000;
548 
549     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
550     glLineStipple(2, 0x0F0F); // dashed line pattern
551 
552     // draw 3D trajectory line
553     int segment = 0;
554     int i = traj_last;
555     int n = traj_first;
556     if (++n == MAX_TRAJ)
557       n = 0;
558 
559     glLineWidth(3.0);
560     while (i != n)
561     {
562       if (traj_t[i] < t_blur)
563         color1_alpha[3] = alpha0*(1. - (t_blur - traj_t[i])/1000.);
564       else
565         color1_alpha[3] = alpha0;
566 
567       if (!segment)
568       {
569         if (traj_CL[i] >= 0.0)
570         {
571           segment = 1;
572           glDisable(GL_LINE_STIPPLE);
573           glBegin(GL_LINE_STRIP);
574         }
575         else
576         {
577           segment = -1;
578           glEnable(GL_LINE_STIPPLE);
579           glBegin(GL_LINE_STRIP);
580         }
581       }
582       glColor4fv(color1_alpha);
583       glVertex3f(traj_y[i], traj_z[i], -traj_x[i]);
584 
585       if (segment*traj_CL[i] > 0.0)
586       {
587         if (++i == MAX_TRAJ)
588           i = 0;
589       }
590       else
591       {
592         glEnd();
593         segment = 0;
594       }
595     }
596     glEnd();
597     glDisable(GL_LINE_STIPPLE);
598 
599     // draw 2D trajectory line on grid plane
600     if (draw_grid)
601     {
602       int segment = 0;
603       int i = traj_last;
604       int n = traj_first;
605       if (++n == MAX_TRAJ)
606         n = 0;
607 
608       glLineWidth(3.0);
609       while (i != n)
610       {
611         //convert world coords to F3A grid coords
612         a = traj_x[i] - center_grid_position_north;
613         b = traj_y[i] - center_grid_position_east;
614         x = b * cos_dir - a * sin_dir;
615         y = a * cos_dir + b * sin_dir;
616         // project to grid plane
617         y = flight_distance;
618         // convert back to world coords
619         north = center_grid_position_north + y * cos_dir - x * sin_dir;
620         east  = center_grid_position_east  + y * sin_dir + x * cos_dir;
621 
622         if (traj_t[i] < t_blur)
623           color2_alpha[3] = alpha0*(1. - (t_blur - traj_t[i])/1000.);
624         else
625           color2_alpha[3] = alpha0;
626 
627         if (!segment)
628         {
629           if (traj_CL[i] >= 0.0)
630           {
631             segment = 1;
632             glDisable(GL_LINE_STIPPLE);
633             glBegin(GL_LINE_STRIP);
634           }
635           else
636           {
637             segment = -1;
638             glEnable(GL_LINE_STIPPLE);
639             glBegin(GL_LINE_STRIP);
640           }
641         }
642         glColor4fv(color2_alpha);
643         glVertex3f(east, traj_z[i], -north);
644 
645         if (segment*traj_CL[i] > 0.0)
646         {
647           if (++i == MAX_TRAJ)
648             i = 0;
649         }
650         else
651         {
652           glEnd();
653           segment = 0;
654         }
655       }
656       glEnd();
657       glDisable(GL_LINE_STIPPLE);
658     }
659 
660     glLineWidth(1.0);
661   }
662 }
663 
664 
draw_f3a_indicators(int window_xsize,int window_ysize) const665 void HandlerF3A::draw_f3a_indicators(int window_xsize, int window_ysize) const
666 {
667   // pitch from aircraft attitude
668   //GLfloat pitch = Global::aircraft->getFDM()->getTheta() * SG_RADIANS_TO_DEGREES;
669   //draw_f3a_horizon(window_xsize, window_ysize, 0, pitch);
670 
671   // pitch from cg trajectory slope
672   GLfloat pitch = THETA_cg * SG_RADIANS_TO_DEGREES;
673   draw_f3a_horizon(window_xsize, window_ysize, 0, pitch);
674 
675   GLfloat roll = Global::aircraft->getFDM()->getPhi() * SG_RADIANS_TO_DEGREES;
676   draw_f3a_horizon(window_xsize, window_ysize, 1, -roll);
677 }
678 
679 
draw_f3a_horizon(int window_xsize,int window_ysize,int position,GLfloat angle) const680 void HandlerF3A::draw_f3a_horizon(int window_xsize, int window_ysize, int position, GLfloat angle) const
681 {
682   int basex = 1.5*(window_ysize >> 5);
683   int basey = 2.0*(window_ysize >> 5);
684   int r = window_ysize >> 4;
685   int gap = r >> 3;
686   int tic = 0.5*gap;
687   int w = 2*(r + gap);
688   int h = 2*gap;
689   int i;
690 
691   GLfloat alpha = 0.6;
692   GLfloat white[] = {1,1,1};
693   GLfloat black[] = {0,0,0};
694   GLfloat red[]   = {1,0,0};
695   GLfloat white_alpha[] = {1,1,1, alpha};
696   GLfloat black_alpha[] = {0,0,0, alpha};
697   GLfloat ground_alpha[] = {.375,.275,0, alpha};
698   GLfloat sky_alpha[] = {0,.75,1, alpha};
699 
700   GLUquadricObj *quadric;
701   quadric = gluNewQuadric();
702 
703   // draw pitch or roll indicator
704 
705   basey += position*w; // stack indicators on top of previous
706 
707   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
708   glPushMatrix();
709   glTranslatef(basex + w/2, basey + w/2, 0);
710 
711   // horizon
712   glColor4fv(black);
713   gluDisk(quadric, r, r+2, 32, 1);
714   //glRecti(-w/2, -w/2, w/2, w/2);
715   glColor4fv(ground_alpha);
716   gluPartialDisk(quadric, 0, r, 32, 1, 90 + angle, 180);
717   glColor4fv(sky_alpha);
718   gluPartialDisk(quadric, 0, r, 32, 1, 270 + angle, 180);
719 
720   // marks
721   glColor3fv(white);
722   for (i = 0; i < 24; i++)
723   {
724     if (i % 3)
725     {
726       glLineWidth(1.0);
727       glBegin(GL_LINES);
728       glVertex2i(      r, 0);
729       glVertex2i(r - tic, 0);
730       glEnd();
731     }
732     else
733     {
734       glLineWidth(2.0);
735       glBegin(GL_LINES);
736       glVertex2i(      r, 0);
737       glVertex2i(r - gap, 0);
738       glEnd();
739     }
740     glRotatef(15, 0, 0, 1);
741   }
742 
743   // airplane silouette
744   if (fabs(angle - 45.0*floor((angle + 0.5)/45.0)) <= tolerance)
745     glColor3fv(red);
746   else
747     glColor3fv(black);
748   glLineWidth(2.0);
749   glBegin(GL_LINES);
750   glVertex2i(-(r - gap), 0);
751   glVertex2i(   r - gap, 0);
752   glEnd();
753   if (position == 0)
754   {
755     // pitch indicator
756     glBegin(GL_TRIANGLES);
757     glVertex2i(    r - gap,  0);
758     glVertex2i(r - gap - h,  h/2);
759     glVertex2i(r - gap - h, -h/2);
760     glEnd();
761 
762     glColor4fv(black_alpha);
763     gluPartialDisk(quadric, 0, h, 16, 1,   0, 90);
764     gluPartialDisk(quadric, 0, h, 16, 1, 180, 90);
765     glColor4fv(white_alpha);
766     gluPartialDisk(quadric, 0, h, 16, 1,  90, 90);
767     gluPartialDisk(quadric, 0, h, 16, 1, 270, 90);
768   }
769   else
770   {
771     // roll indicator
772     glBegin(GL_TRIANGLES);
773     glVertex2i(-h/2, 0);
774     glVertex2i( 0, h);
775     glVertex2i( h/2, 0);
776     glEnd();
777   }
778   glLineWidth(1.0);
779   glPopMatrix();
780 
781   gluDeleteQuadric(quadric);
782 }
783