]> git.mxchange.org Git - flightgear.git/blob - Simulator/Main/GLUTmain.cxx
Removed in-src cvs logs.
[flightgear.git] / Simulator / Main / GLUTmain.cxx
1 // GLUTmain.cxx -- top level sim routines
2 //
3 // Written by Curtis Olson for OpenGL, started May 1997.
4 //
5 // Copyright (C) 1997  Curtis L. Olson  - curt@me.umn.edu
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23 #define MICHAEL_JOHNSON_EXPERIMENTAL_ENGINE_AUDIO
24
25 #ifdef HAVE_CONFIG_H
26 #  include <config.h>
27 #endif
28
29 #ifdef HAVE_WINDOWS_H
30 #  include <windows.h>                     
31 #  include <float.h>                    
32 #endif
33
34 #include <GL/glut.h>
35 #include <XGL/xgl.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <string>
39
40 #ifdef HAVE_STDLIB_H
41 #   include <stdlib.h>
42 #endif
43
44 #ifdef HAVE_SYS_STAT_H
45 #  include <sys/stat.h> /* for stat() */
46 #endif
47
48 #ifdef HAVE_UNISTD_H
49 #  include <unistd.h>    /* for stat() */
50 #endif
51
52 #include <Include/fg_constants.h>  // for VERSION
53 #include <Include/general.hxx>
54
55 #include <Debug/logstream.hxx>
56 #include <Aircraft/aircraft.hxx>
57 #include <Astro/sky.hxx>
58 #include <Astro/stars.hxx>
59 #include <Astro/solarsystem.hxx>
60
61 #ifdef ENABLE_AUDIO_SUPPORT
62 #  include <sl.h>
63 #  include <sm.h>
64 #endif
65
66 #include <Autopilot/autopilot.hxx>
67 #include <Cockpit/cockpit.hxx>
68 #include <GUI/gui.h>
69 #include <Joystick/joystick.hxx>
70 #include <Math/fg_geodesy.hxx>
71 #include <Math/mat3.h>
72 #include <Math/polar3d.hxx>
73 #include <Math/fg_random.h>
74 #include <pu.h>
75 #include <Scenery/scenery.hxx>
76 #include <Scenery/tilemgr.hxx>
77 #include <Time/event.hxx>
78 #include <Time/fg_time.hxx>
79 #include <Time/fg_timer.hxx>
80 #include <Time/sunpos.hxx>
81 #include <Weather/weather.hxx>
82
83 #include "GLUTkey.hxx"
84 #include "fg_init.hxx"
85 #include "options.hxx"
86 #include "splash.hxx"
87 #include "views.hxx"
88 #include "fg_serial.hxx"
89
90
91 // This is a record containing a bit of global housekeeping information
92 FGGeneral general;
93
94 // Specify our current idle function state.  This is used to run all
95 // our initializations out of the glutIdleLoop() so that we can get a
96 // splash screen up and running right away.
97 static int idle_state = 0;
98
99 // Another hack
100 int use_signals = 0;
101
102 // Global structures for the Audio library
103 #ifdef ENABLE_AUDIO_SUPPORT
104 slEnvelope pitch_envelope ( 1, SL_SAMPLE_ONE_SHOT ) ;
105 slEnvelope volume_envelope ( 1, SL_SAMPLE_ONE_SHOT ) ;
106 slScheduler *audio_sched;
107 smMixer *audio_mixer;
108 slSample *s1;
109 slSample *s2;
110 #endif
111
112
113 // The following defines flight gear options. Because glutlib will also
114 // want to parse its own options, those options must not be included here
115 // or they will get parsed by the main program option parser. Hence case
116 // is significant for any option added that might be in conflict with
117 // glutlib's parser.
118 //
119 // glutlib parses for:
120 //    -display
121 //    -direct   (invalid in Win32)
122 //    -geometry
123 //    -gldebug
124 //    -iconized
125 //    -indirect (invalid in Win32)
126 //    -synce
127 //
128 // Note that glutlib depends upon strings while this program's
129 // option parser wants only initial characters followed by numbers
130 // or pathnames.
131 //
132
133
134 // fgInitVisuals() -- Initialize various GL/view parameters
135 static void fgInitVisuals( void ) {
136     fgLIGHT *l;
137
138     l = &cur_light_params;
139
140     // Go full screen if requested ...
141     if ( current_options.get_fullscreen() ) {
142         glutFullScreen();
143     }
144
145     // If enabled, normal vectors specified with glNormal are scaled
146     // to unit length after transformation.  See glNormal.
147     // xglEnable( GL_NORMALIZE );
148
149     xglEnable( GL_LIGHTING );
150     xglEnable( GL_LIGHT0 );
151     xglLightfv( GL_LIGHT0, GL_POSITION, l->sun_vec );
152
153     // xglFogi (GL_FOG_MODE, GL_LINEAR);
154     xglFogi (GL_FOG_MODE, GL_EXP2);
155     // Fog density is now set when the weather system is initialized
156     // xglFogf (GL_FOG_DENSITY, w->fog_density);
157     if ( (current_options.get_fog() == 1) || 
158          (current_options.get_shading() == 0) ) {
159         // if fastest fog requested, or if flat shading force fastest
160         xglHint ( GL_FOG_HINT, GL_FASTEST );
161     } else if ( current_options.get_fog() == 2 ) {
162         xglHint ( GL_FOG_HINT, GL_NICEST );
163     }
164     if ( current_options.get_wireframe() ) {
165         // draw wire frame
166         xglPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
167     }
168
169     // This is the default anyways, but it can't hurt
170     xglFrontFace ( GL_CCW );
171
172     // Just testing ...
173     // xglEnable(GL_POINT_SMOOTH);
174     // xglEnable(GL_LINE_SMOOTH);
175     // xglEnable(GL_POLYGON_SMOOTH);      
176 }
177
178
179 #if 0
180 // Draw a basic instrument panel
181 static void fgUpdateInstrViewParams( void ) {
182
183     exit(0);
184
185     fgVIEW *v = &current_view;
186
187     xglViewport(0, 0 , (GLint)(v->winWidth), (GLint)(v->winHeight) / 2);
188   
189     xglMatrixMode(GL_PROJECTION);
190     xglPushMatrix();
191   
192     xglLoadIdentity();
193     gluOrtho2D(0, 640, 0, 480);
194     xglMatrixMode(GL_MODELVIEW);
195     xglPushMatrix();
196     xglLoadIdentity();
197
198     xglColor3f(1.0, 1.0, 1.0);
199     xglIndexi(7);
200   
201     xglDisable(GL_DEPTH_TEST);
202     xglDisable(GL_LIGHTING);
203   
204     xglLineWidth(1);
205     xglColor3f (0.5, 0.5, 0.5);
206
207     xglBegin(GL_QUADS);
208     xglVertex2f(0.0, 0.00);
209     xglVertex2f(0.0, 480.0);
210     xglVertex2f(640.0,480.0);
211     xglVertex2f(640.0, 0.0);
212     xglEnd();
213
214     xglRectf(0.0,0.0, 640, 480);
215     xglEnable(GL_DEPTH_TEST);
216     xglEnable(GL_LIGHTING);
217     xglMatrixMode(GL_PROJECTION);
218     xglPopMatrix();
219     xglMatrixMode(GL_MODELVIEW);
220     xglPopMatrix();
221 }
222 #endif
223
224
225 // Update all Visuals (redraws anything graphics related)
226 static void fgRenderFrame( void ) {
227     fgLIGHT *l = &cur_light_params;
228     fgTIME *t = &cur_time_params;
229     FGView *v = &current_view;
230
231     double angle;
232     // GLfloat black[4] = { 0.0, 0.0, 0.0, 1.0 };
233     GLfloat white[4] = { 1.0, 1.0, 1.0, 1.0 };
234     GLfloat terrain_color[4] = { 0.54, 0.44, 0.29, 1.0 };
235     GLbitfield clear_mask;
236
237     if ( idle_state != 1000 ) {
238         // still initializing, draw the splash screen
239         if ( current_options.get_splash_screen() == 1 ) {
240             fgSplashUpdate(0.0);
241         }
242     } else {
243         // idle_state is now 1000 meaning we've finished all our
244         // initializations and are running the main loop, so this will
245         // now work without seg faulting the system.
246
247         // printf("Ground = %.2f  Altitude = %.2f\n", scenery.cur_elev, 
248         //        FG_Altitude * FEET_TO_METER);
249     
250         // this is just a temporary hack, to make me understand Pui
251         // timerText -> setLabel (ctime (&t->cur_time));
252         // end of hack
253
254         // update view volume parameters
255         v->UpdateViewParams();
256
257         // set the sun position
258         xglLightfv( GL_LIGHT0, GL_POSITION, l->sun_vec );
259
260         clear_mask = GL_DEPTH_BUFFER_BIT;
261         if ( current_options.get_wireframe() ) {
262             clear_mask |= GL_COLOR_BUFFER_BIT;
263         }
264         if ( current_options.get_panel_status() ) {
265             // we can't clear the screen when the panel is active
266         } else if ( current_options.get_skyblend() ) {
267             if ( current_options.get_textures() ) {
268                 // glClearColor(black[0], black[1], black[2], black[3]);
269                 glClearColor(l->adj_fog_color[0], l->adj_fog_color[1], 
270                              l->adj_fog_color[2], l->adj_fog_color[3]);
271                 clear_mask |= GL_COLOR_BUFFER_BIT;              
272             }
273         } else {
274             glClearColor(l->sky_color[0], l->sky_color[1], 
275                          l->sky_color[2], l->sky_color[3]);
276             clear_mask |= GL_COLOR_BUFFER_BIT;
277         }
278         xglClear( clear_mask );
279
280         // Tell GL we are switching to model view parameters
281         xglMatrixMode(GL_MODELVIEW);
282         // xglLoadIdentity();
283
284         // draw sky
285         xglDisable( GL_DEPTH_TEST );
286         xglDisable( GL_LIGHTING );
287         xglDisable( GL_CULL_FACE );
288         xglDisable( GL_FOG );
289         xglShadeModel( GL_SMOOTH );
290         if ( current_options.get_skyblend() ) {
291             fgSkyRender();
292         }
293
294         // setup transformation for drawing astronomical objects
295         xglPushMatrix();
296         // Translate to view position
297         Point3D view_pos = v->get_view_pos();
298         xglTranslatef( view_pos.x(), view_pos.y(), view_pos.z() );
299         // Rotate based on gst (sidereal time)
300         // note: constant should be 15.041085, Curt thought it was 15
301         angle = t->gst * 15.041085;
302         // printf("Rotating astro objects by %.2f degrees\n",angle);
303         xglRotatef( angle, 0.0, 0.0, -1.0 );
304
305         // draw stars and planets
306         fgStarsRender();
307         SolarSystem::theSolarSystem->draw();
308
309         xglPopMatrix();
310
311         // draw scenery
312         if ( current_options.get_shading() ) {
313             xglShadeModel( GL_SMOOTH ); 
314         } else {
315             xglShadeModel( GL_FLAT ); 
316         }
317         xglEnable( GL_DEPTH_TEST );
318         if ( current_options.get_fog() > 0 ) {
319             xglEnable( GL_FOG );
320             xglFogfv (GL_FOG_COLOR, l->adj_fog_color);
321         }
322         // set lighting parameters
323         xglLightfv(GL_LIGHT0, GL_AMBIENT, l->scene_ambient );
324         xglLightfv(GL_LIGHT0, GL_DIFFUSE, l->scene_diffuse );
325         
326         if ( current_options.get_textures() ) {
327             // texture parameters
328             xglEnable( GL_TEXTURE_2D );
329             xglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ) ;
330             xglHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ) ;
331             // set base color (I don't think this is doing anything here)
332             xglMaterialfv (GL_FRONT, GL_AMBIENT, white);
333             xglMaterialfv (GL_FRONT, GL_DIFFUSE, white);
334         } else {
335             xglDisable( GL_TEXTURE_2D );
336             xglMaterialfv (GL_FRONT, GL_AMBIENT, terrain_color);
337             xglMaterialfv (GL_FRONT, GL_DIFFUSE, terrain_color);
338             // xglMaterialfv (GL_FRONT, GL_AMBIENT, white);
339             // xglMaterialfv (GL_FRONT, GL_DIFFUSE, white);
340         }
341
342         fgTileMgrRender();
343
344         xglDisable( GL_TEXTURE_2D );
345         xglDisable( GL_FOG );
346
347         // display HUD && Panel
348         fgCockpitUpdate();
349
350         // We can do translucent menus, so why not. :-)
351         xglEnable    ( GL_BLEND ) ;
352         xglBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
353         puDisplay();
354         xglDisable   ( GL_BLEND ) ;
355         xglEnable( GL_FOG );
356     }
357
358     xglutSwapBuffers();
359 }
360
361
362 // Update internal time dependent calculations (i.e. flight model)
363 void fgUpdateTimeDepCalcs(int multi_loop, int remainder) {
364     FGInterface *f = current_aircraft.fdm_state;
365     fgLIGHT *l = &cur_light_params;
366     fgTIME *t = &cur_time_params;
367     FGView *v = &current_view;
368     int i;
369
370     // update the flight model
371     if ( multi_loop < 0 ) {
372         multi_loop = DEFAULT_MULTILOOP;
373     }
374
375     if ( !t->pause ) {
376         // run Autopilot system
377         fgAPRun();
378
379         // printf("updating flight model x %d\n", multi_loop);
380         fgFDMUpdate( current_options.get_flight_model(), 
381                              cur_fdm_state, multi_loop, remainder );
382     } else {
383         fgFDMUpdate( current_options.get_flight_model(), 
384                              cur_fdm_state, 0, remainder );
385     }
386
387     // update the view angle
388     for ( i = 0; i < multi_loop; i++ ) {
389         if ( fabs(v->get_goal_view_offset() - v->get_view_offset()) < 0.05 ) {
390             v->set_view_offset( v->get_goal_view_offset() );
391             break;
392         } else {
393             // move v->view_offset towards v->goal_view_offset
394             if ( v->get_goal_view_offset() > v->get_view_offset() ) {
395                 if ( v->get_goal_view_offset() - v->get_view_offset() < FG_PI ){
396                     v->inc_view_offset( 0.01 );
397                 } else {
398                     v->inc_view_offset( -0.01 );
399                 }
400             } else {
401                 if ( v->get_view_offset() - v->get_goal_view_offset() < FG_PI ){
402                     v->inc_view_offset( -0.01 );
403                 } else {
404                     v->inc_view_offset( 0.01 );
405                 }
406             }
407             if ( v->get_view_offset() > FG_2PI ) {
408                 v->inc_view_offset( -FG_2PI );
409             } else if ( v->get_view_offset() < 0 ) {
410                 v->inc_view_offset( FG_2PI );
411             }
412         }
413     }
414
415     double tmp = -(l->sun_rotation + FG_PI) 
416         - (f->get_Psi() - v->get_view_offset() );
417     while ( tmp < 0.0 ) {
418         tmp += FG_2PI;
419     }
420     while ( tmp > FG_2PI ) {
421         tmp -= FG_2PI;
422     }
423     /* printf("Psi = %.2f, viewoffset = %.2f sunrot = %.2f rottosun = %.2f\n",
424            FG_Psi * RAD_TO_DEG, v->view_offset * RAD_TO_DEG, 
425            -(l->sun_rotation+FG_PI) * RAD_TO_DEG, tmp * RAD_TO_DEG); */
426     l->UpdateAdjFog();
427 }
428
429
430 void fgInitTimeDepCalcs( void ) {
431     // initialize timer
432
433     // #ifdef HAVE_SETITIMER
434     //   fgTimerInit( 1.0 / DEFAULT_TIMER_HZ, fgUpdateTimeDepCalcs );
435     // #endif HAVE_SETITIMER
436 }
437
438 static const double alt_adjust_ft = 3.758099;
439 static const double alt_adjust_m = alt_adjust_ft * FEET_TO_METER;
440
441 // What should we do when we have nothing else to do?  Let's get ready
442 // for the next move and update the display?
443 static void fgMainLoop( void ) {
444     FGInterface *f;
445     fgTIME *t;
446     static long remainder = 0;
447     long elapsed, multi_loop;
448     // int i;
449     // double accum;
450     static time_t last_time = 0;
451     static int frames = 0;
452
453     f = current_aircraft.fdm_state;
454     t = &cur_time_params;
455
456     FG_LOG( FG_ALL, FG_DEBUG, "Running Main Loop");
457     FG_LOG( FG_ALL, FG_DEBUG, "======= ==== ====");
458
459 #if defined( ENABLE_LINUX_JOYSTICK )
460     // Read joystick and update control settings
461     fgJoystickRead();
462 #elif defined( ENABLE_GLUT_JOYSTICK )
463     // Glut joystick support works by feeding a joystick handler
464     // function to glut.  This is taken care of once in the joystick
465     // init routine and we don't have to worry about it again.
466 #endif
467
468     current_weather.Update();
469
470     // Fix elevation.  I'm just sticking this here for now, it should
471     // probably move eventually
472
473     /* printf("Before - ground = %.2f  runway = %.2f  alt = %.2f\n",
474            scenery.cur_elev,
475            f->get_Runway_altitude() * FEET_TO_METER,
476            f->get_Altitude() * FEET_TO_METER); */
477
478     if ( scenery.cur_elev > -9990 ) {
479         if ( f->get_Altitude() * FEET_TO_METER < 
480              (scenery.cur_elev + alt_adjust_m - 3.0) ) {
481             // now set aircraft altitude above ground
482             printf("Current Altitude = %.2f < %.2f forcing to %.2f\n", 
483                    f->get_Altitude() * FEET_TO_METER,
484                    scenery.cur_elev + alt_adjust_m - 3.0,
485                    scenery.cur_elev + alt_adjust_m );
486             fgFDMForceAltitude( current_options.get_flight_model(), 
487                                 scenery.cur_elev + alt_adjust_m );
488
489             FG_LOG( FG_ALL, FG_DEBUG, 
490                     "<*> resetting altitude to " 
491                     << f->get_Altitude() * FEET_TO_METER << " meters" );
492         }
493         fgFDMSetGroundElevation( current_options.get_flight_model(),
494                                  scenery.cur_elev );  // meters
495     }
496
497     /* printf("Adjustment - ground = %.2f  runway = %.2f  alt = %.2f\n",
498            scenery.cur_elev,
499            f->get_Runway_altitude() * FEET_TO_METER,
500            f->get_Altitude() * FEET_TO_METER); */
501
502     // update "time"
503     fgTimeUpdate(f, t);
504
505     // Get elapsed time (in usec) for this past frame
506     elapsed = fgGetTimeInterval();
507     FG_LOG( FG_ALL, FG_DEBUG, 
508             "Elapsed time interval is = " << elapsed 
509             << ", previous remainder is = " << remainder );
510
511     // Calculate frame rate average
512     if ( (t->cur_time != last_time) && (last_time > 0) ) {
513         general.set_frame_rate( frames );
514         FG_LOG( FG_ALL, FG_DEBUG, 
515                 "--> Frame rate is = " << general.get_frame_rate() );
516         frames = 0;
517     }
518     last_time = t->cur_time;
519     ++frames;
520
521     /* old fps calculation
522     if ( elapsed > 0 ) {
523         accum = 0.0;
524         for ( i = FG_FRAME_RATE_HISTORY - 2; i >= 0; i-- ) {
525             accum += g->frames[i];
526             // printf("frame[%d] = %.2f\n", i, g->frames[i]);
527             g->frames[i+1] = g->frames[i];
528         }
529         g->frames[0] = 1000.0 / (float)elapsed;
530         // printf("frame[0] = %.2f\n", g->frames[0]);
531         accum += g->frames[0];
532         g->frame_rate = accum / (float)FG_FRAME_RATE_HISTORY;
533         // printf("ave = %.2f\n", g->frame_rate);
534     }
535     */
536
537     // Run flight model
538     if ( ! use_signals ) {
539         // Calculate model iterations needed for next frame
540         elapsed += remainder;
541
542         multi_loop = (int)(((double)elapsed * 0.000001) * DEFAULT_MODEL_HZ);
543         remainder = elapsed - ((multi_loop*1000000) / DEFAULT_MODEL_HZ);
544         FG_LOG( FG_ALL, FG_DEBUG, 
545                 "Model iterations needed = " << multi_loop
546                 << ", new remainder = " << remainder );
547         
548         // flight model
549         if ( multi_loop > 0 ) {
550             fgUpdateTimeDepCalcs(multi_loop, remainder);
551         } else {
552             FG_LOG( FG_ALL, FG_INFO, "Elapsed time is zero ... we're zinging" );
553         }
554     }
555
556     // Do any serial port work that might need to be done
557     fgSerialProcess();
558
559     // see if we need to load any new scenery tiles
560     fgTileMgrUpdate();
561
562     // Process/manage pending events
563     global_events.Process();
564
565     // Run audio scheduler
566 #ifdef ENABLE_AUDIO_SUPPORT
567     if ( current_options.get_sound() && !audio_sched->not_working() ) {
568
569 #   ifdef MICHAEL_JOHNSON_EXPERIMENTAL_ENGINE_AUDIO
570
571         // note: all these factors are relative to the sample.  our
572         // sample format should really contain a conversion factor so
573         // that we can get prop speed right for arbitrary samples.
574         // Note that for normal-size props, there is a point at which
575         // the prop tips approach the speed of sound; that is a pretty
576         // strong limit to how fast the prop can go.
577
578         // multiplication factor is prime pitch control; add some log
579         // component for verisimilitude
580
581         double pitch = log((controls.get_throttle(0) * 14.0) + 1.0);
582         //fprintf(stderr, "pitch1: %f ", pitch);
583         if (controls.get_throttle(0) > 0.0 || f->v_rel_wind > 40.0) {
584             //fprintf(stderr, "rel_wind: %f ", f->v_rel_wind);
585             // only add relative wind and AoA if prop is moving
586             // or we're really flying at idle throttle
587             if (pitch < 5.4) {  // this needs tuning
588                 // prop tips not breaking sound barrier
589                 pitch += log(f->v_rel_wind + 0.8)/2;
590             } else {
591                 // prop tips breaking sound barrier
592                 pitch += log(f->v_rel_wind + 0.8)/10;
593             }
594             //fprintf(stderr, "pitch2: %f ", pitch);
595             //fprintf(stderr, "AoA: %f ", FG_Gamma_vert_rad);
596
597             // Angle of Attack next... -x^3(e^x) is my best guess Just
598             // need to calculate some reasonable scaling factor and
599             // then clamp it on the positive aoa (neg adj) side
600             double aoa = f->get_Gamma_vert_rad() * 2.2;
601             double tmp = 3.0;
602             double aoa_adj = pow(-aoa, tmp) * pow(M_E, aoa);
603             if (aoa_adj < -0.8) aoa_adj = -0.8;
604             pitch += aoa_adj;
605             //fprintf(stderr, "pitch3: %f ", pitch);
606
607             // don't run at absurdly slow rates -- not realistic
608             // and sounds bad to boot.  :-)
609             if (pitch < 0.8) pitch = 0.8;
610         }
611         //fprintf(stderr, "pitch4: %f\n", pitch);
612
613         double volume = controls.get_throttle(0) * 1.15 + 0.3 +
614             log(f->v_rel_wind + 1.0)/14.0;
615         // fprintf(stderr, "volume: %f\n", volume);
616
617         pitch_envelope.setStep  ( 0, 0.01, pitch );
618         volume_envelope.setStep ( 0, 0.01, volume );
619
620 #   else
621
622        double param = controls.get_throttle( 0 ) * 2.0 + 1.0;
623        pitch_envelope.setStep  ( 0, 0.01, param );
624        volume_envelope.setStep ( 0, 0.01, param );
625
626 #   endif // experimental throttle patch
627
628         audio_sched -> update();
629     }
630 #endif
631
632     // redraw display
633     fgRenderFrame();
634
635     FG_LOG( FG_ALL, FG_DEBUG, "" );
636 }
637
638
639 // This is the top level master main function that is registered as
640 // our idle funciton
641 //
642
643 // The first few passes take care of initialization things (a couple
644 // per pass) and once everything has been initialized fgMainLoop from
645 // then on.
646
647 static void fgIdleFunction ( void ) {
648     // printf("idle state == %d\n", idle_state);
649
650     if ( idle_state == 0 ) {
651         // Initialize the splash screen right away
652         if ( current_options.get_splash_screen() ) {
653             fgSplashInit();
654         }
655         
656         idle_state++;
657     } else if ( idle_state == 1 ) {
658         // Start the intro music
659 #if !defined(WIN32)
660         if ( current_options.get_intro_music() ) {
661             string lockfile = "/tmp/mpg123.running";
662             string mp3file = current_options.get_fg_root() +
663                 "/Sounds/intro.mp3";
664             string command = "(touch " + lockfile + "; mpg123 " + mp3file +
665                  "> /dev/null 2>&1; /bin/rm " + lockfile + ") &";
666             FG_LOG( FG_GENERAL, FG_INFO, 
667                     "Starting intro music: " << mp3file );
668             system ( command.c_str() );
669         }
670 #endif
671
672         idle_state++;
673     } else if ( idle_state == 2 ) {
674         // These are a few miscellaneous things that aren't really
675         // "subsystems" but still need to be initialized.
676
677 #ifdef USE_GLIDE
678         if ( strstr ( general.get_glRenderer(), "Glide" ) ) {
679             grTexLodBiasValue ( GR_TMU0, 1.0 ) ;
680         }
681 #endif
682
683         idle_state++;
684     } else if ( idle_state == 3 ) {
685         // This is the top level init routine which calls all the
686         // other subsystem initialization routines.  If you are adding
687         // a subsystem to flight gear, its initialization call should
688         // located in this routine.
689         if( !fgInitSubsystems()) {
690             FG_LOG( FG_GENERAL, FG_ALERT,
691                     "Subsystem initializations failed ..." );
692             exit(-1);
693         }
694
695         idle_state++;
696     } else if ( idle_state == 4 ) {
697         // setup OpenGL view parameters
698         fgInitVisuals();
699
700         if ( use_signals ) {
701             // init timer routines, signals, etc.  Arrange for an alarm
702             // signal to be generated, etc.
703             fgInitTimeDepCalcs();
704         }
705
706         idle_state++;
707     } else if ( idle_state == 5 ) {
708
709         idle_state++;
710     } else if ( idle_state == 6 ) {
711         // Initialize audio support
712 #ifdef ENABLE_AUDIO_SUPPORT
713
714 #if !defined(WIN32)
715         if ( current_options.get_intro_music() ) {
716             // Let's wait for mpg123 to finish
717             string lockfile = "/tmp/mpg123.running";
718             struct stat stat_buf;
719
720             FG_LOG( FG_GENERAL, FG_INFO, 
721                     "Waiting for mpg123 player to finish ..." );
722             while ( stat(lockfile.c_str(), &stat_buf) == 0 ) {
723                 // file exist, wait ...
724                 sleep(1);
725                 FG_LOG( FG_GENERAL, FG_INFO, ".");
726             }
727             FG_LOG( FG_GENERAL, FG_INFO, "");
728         }
729 #endif // WIN32
730
731         audio_sched = new slScheduler ( 8000 );
732         audio_mixer = new smMixer;
733         audio_mixer -> setMasterVolume ( 80 ) ;  /* 80% of max volume. */
734         audio_sched -> setSafetyMargin ( 1.0 ) ;
735         string slfile = current_options.get_fg_root() + "/Sounds/wasp.wav";
736
737         s1 = new slSample ( (char *)slfile.c_str() );
738         FG_LOG( FG_GENERAL, FG_INFO,
739                 "Rate = " << s1 -> getRate()
740                 << "  Bps = " << s1 -> getBps()
741                 << "  Stereo = " << s1 -> getStereo() );
742         audio_sched -> loopSample ( s1 );
743
744         if ( audio_sched->not_working() ) {
745             // skip
746         } else {
747             pitch_envelope.setStep  ( 0, 0.01, 0.6 );
748             volume_envelope.setStep ( 0, 0.01, 0.6 );
749
750             audio_sched -> addSampleEnvelope( s1, 0, 0, &
751                                               pitch_envelope,
752                                               SL_PITCH_ENVELOPE );
753             audio_sched -> addSampleEnvelope( s1, 0, 1, 
754                                               &volume_envelope,
755                                               SL_VOLUME_ENVELOPE );
756         }
757
758         // strcpy(slfile, path);
759         // strcat(slfile, "thunder.wav");
760         // s2 -> loadFile ( slfile );
761         // s2 -> adjustVolume(0.5);
762         // audio_sched -> playSample ( s2 );
763 #endif
764
765         // sleep(1);
766         idle_state = 1000;
767     } 
768
769     if ( idle_state == 1000 ) {
770         // We've finished all our initialization steps, from now on we
771         // run the main loop.
772
773         fgMainLoop();
774     } else {
775         if ( current_options.get_splash_screen() == 1 ) {
776             fgSplashUpdate(0.0);
777         }
778     }
779 }
780
781
782 // Handle new window size or exposure
783 static void fgReshape( int width, int height ) {
784     // Do this so we can call fgReshape(0,0) ourselves without having
785     // to know what the values of width & height are.
786     if ( (height > 0) && (width > 0) ) {
787         if ( ! current_options.get_panel_status() ) {
788             current_view.set_win_ratio( (GLfloat) width / (GLfloat) height );
789         } else {
790             current_view.set_win_ratio( (GLfloat) width / 
791                                         ((GLfloat) (height)*0.4232) );
792         }
793     }
794
795     current_view.set_winWidth( width );
796     current_view.set_winHeight( height );
797     current_view.force_update_fov_math();
798
799     // Inform gl of our view window size (now handled elsewhere)
800     // xglViewport(0, 0, (GLint)width, (GLint)height);
801     if ( idle_state == 1000 ) {
802         // yes we've finished all our initializations and are running
803         // the main loop, so this will now work without seg faulting
804         // the system.
805         current_view.UpdateViewParams();
806         if ( current_options.get_panel_status() ) {
807             FGPanel::OurPanel->ReInit(0, 0, 1024, 768);
808         }
809     }
810 }
811
812
813 // Initialize GLUT and define a main window
814 int fgGlutInit( int *argc, char **argv ) {
815     // GLUT will extract all glut specific options so later on we only
816     // need wory about our own.
817     xglutInit(argc, argv);
818
819     // Define Display Parameters
820     xglutInitDisplayMode( GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE );
821
822     FG_LOG( FG_GENERAL, FG_INFO, "Opening a window: " <<
823             current_options.get_xsize() << "x" << current_options.get_ysize() );
824
825     // Define initial window size
826     xglutInitWindowSize( current_options.get_xsize(),
827                          current_options.get_ysize() );
828
829     // Initialize windows
830     if ( current_options.get_game_mode() == 0 ) {
831         // Open the regular window
832         xglutCreateWindow("Flight Gear");
833     } else {
834         // Open the cool new 'game mode' window
835         char game_mode_str[256];
836         sprintf( game_mode_str, "width=%d height=%d bpp=16",
837                  current_options.get_xsize(),
838                  current_options.get_ysize() );
839
840         FG_LOG( FG_GENERAL, FG_INFO, 
841                 "game mode params = " << game_mode_str );
842         glutGameModeString( game_mode_str );
843         glutEnterGameMode();
844     }
845
846     // This seems to be the absolute earliest in the init sequence
847     // that these calls will return valid info.  Too bad it's after
848     // we've already created and sized out window. :-(
849     general.set_glVendor( (char *)glGetString ( GL_VENDOR ) );
850     general.set_glRenderer( (char *)glGetString ( GL_RENDERER ) );
851     general.set_glVersion( (char *)glGetString ( GL_VERSION ) );
852
853     FG_LOG ( FG_GENERAL, FG_INFO, general.get_glRenderer() );
854
855 #if 0
856     // try to determine if we should adjust the initial default
857     // display resolution.  The options class defaults (is
858     // initialized) to 640x480.
859     string renderer = general.glRenderer;
860
861     // currently we only know how to deal with Mesa/Glide/Voodoo cards
862     if ( renderer.find( "Glide" ) != string::npos ) {
863         FG_LOG( FG_GENERAL, FG_INFO, "Detected a Glide driver" );
864         if ( renderer.find( "FB/8" ) != string::npos ) {
865             // probably a voodoo-2
866             if ( renderer.find( "TMU/SLI" ) != string::npos ) {
867                 // probably two SLI'd Voodoo-2's
868                 current_options.set_xsize( 1024 );
869                 current_options.set_ysize( 768 );
870                 FG_LOG( FG_GENERAL, FG_INFO,
871                         "It looks like you have two sli'd voodoo-2's." << endl
872                         << "upgrading your win resolution to 1024 x 768" );
873                 glutReshapeWindow(1024, 768);
874             } else {
875                 // probably a single non-SLI'd Voodoo-2
876                 current_options.set_xsize( 800 );
877                 current_options.set_ysize( 600 );
878                 FG_LOG( FG_GENERAL, FG_INFO,
879                         "It looks like you have a voodoo-2." << endl
880                         << "upgrading your win resolution to 800 x 600" );
881                 glutReshapeWindow(800, 600);
882             }
883         } else if ( renderer.find( "FB/2" ) != string::npos ) {
884             // probably a voodoo-1, stick with the default
885         }
886     } else {
887         // we have no special knowledge of this card, stick with the default
888     }
889 #endif
890
891     return(1);
892 }
893
894
895 // Initialize GLUT event handlers
896 int fgGlutInitEvents( void ) {
897     // call fgReshape() on window resizes
898     xglutReshapeFunc( fgReshape );
899
900     // call GLUTkey() on keyboard event
901     xglutKeyboardFunc( GLUTkey );
902     glutSpecialFunc( GLUTspecialkey );
903
904     // call guiMouseFunc() whenever our little rodent is used
905     glutMouseFunc ( guiMouseFunc );
906     glutMotionFunc (guiMotionFunc );
907     glutPassiveMotionFunc (guiMotionFunc );
908
909     // call fgMainLoop() whenever there is
910     // nothing else to do
911     xglutIdleFunc( fgIdleFunction );
912
913     // draw the scene
914     xglutDisplayFunc( fgRenderFrame );
915
916     return(1);
917 }
918
919
920 // Main ...
921 int main( int argc, char **argv ) {
922     FGInterface *f;
923
924     f = current_aircraft.fdm_state;
925
926 #ifdef HAVE_BC5PLUS
927     _control87(MCW_EM, MCW_EM);  /* defined in float.h */
928 #endif
929
930     // set default log levels
931     fglog().setLogLevels( FG_ALL, FG_INFO );
932
933     FG_LOG( FG_GENERAL, FG_INFO, "Flight Gear:  Version " << VERSION << endl );
934
935     string root;
936
937     FG_LOG( FG_GENERAL, FG_INFO, "General Initialization" );
938     FG_LOG( FG_GENERAL, FG_INFO, "======= ==============" );
939
940     // seed the random number generater
941     fg_srandom();
942
943     // Attempt to locate and parse a config file
944     // First check fg_root
945     string config = current_options.get_fg_root() + "/system.fgfsrc";
946     current_options.parse_config_file( config );
947
948     // Next check home directory
949     char* envp = ::getenv( "HOME" );
950     if ( envp != NULL ) {
951         config = envp;
952         config += "/.fgfsrc";
953         current_options.parse_config_file( config );
954     }
955
956     // Parse remaining command line options
957     // These will override anything specified in a config file
958     if ( current_options.parse_command_line(argc, argv) !=
959                                       fgOPTIONS::FG_OPTIONS_OK )
960     {
961         // Something must have gone horribly wrong with the command
962         // line parsing or maybe the user just requested help ... :-)
963         current_options.usage();
964         FG_LOG( FG_GENERAL, FG_ALERT, "\nExiting ...");
965         exit(-1);
966     }
967     
968     // Initialize the Window/Graphics environment.
969     if( !fgGlutInit(&argc, argv) ) {
970         FG_LOG( FG_GENERAL, FG_ALERT, "GLUT initialization failed ..." );
971         exit(-1);
972     }
973
974     // Initialize the various GLUT Event Handlers.
975     if( !fgGlutInitEvents() ) {
976         FG_LOG( FG_GENERAL, FG_ALERT, 
977                 "GLUT event handler initialization failed ..." );
978         exit(-1);
979     }
980
981     // First do some quick general initializations
982     if( !fgInitGeneral()) {
983         FG_LOG( FG_GENERAL, FG_ALERT, 
984                 "General initializations failed ..." );
985         exit(-1);
986     }
987
988     // Init the user interface (we need to do this before passing off
989     // control to glut
990     guiInit();
991
992     // pass control off to the master GLUT event handler
993     glutMainLoop();
994
995     // we never actually get here ... but just in case ... :-)
996     return(0);
997 }
998
999