]> git.mxchange.org Git - flightgear.git/blob - src/Main/main.cxx
Merge commit 'refs/merge-requests/14' of git://gitorious.org/fg/flightgear into next
[flightgear.git] / src / Main / main.cxx
1 // main.cxx -- top level sim routines
2 //
3 // Written by Curtis Olson, started May 1997.
4 //
5 // Copyright (C) 1997 - 2002  Curtis L. Olson  - http://www.flightgear.org/~curt
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #include <simgear/compiler.h>
29
30 #if defined(__linux__) && defined(__i386__)
31 #  include <fpu_control.h>
32 #  include <signal.h>
33 #endif
34
35 #include <iostream>
36
37 #include <osg/Camera>
38 #include <osg/GraphicsContext>
39 #include <osgDB/Registry>
40
41 // Class references
42 #include <simgear/scene/model/modellib.hxx>
43 #include <simgear/scene/material/matlib.hxx>
44 #include <simgear/scene/model/animation.hxx>
45 #include <simgear/scene/sky/sky.hxx>
46 #include <simgear/structure/event_mgr.hxx>
47 #include <simgear/props/AtomicChangeListener.hxx>
48 #include <simgear/props/props.hxx>
49 //#include <simgear/timing/sg_time.hxx>
50 #include <simgear/math/sg_random.h>
51 #include <simgear/io/raw_socket.hxx>
52 #include <simgear/misc/sg_sleep.hxx>
53
54 #include <Time/light.hxx>
55 #include <Aircraft/replay.hxx>
56 #include <Aircraft/controls.hxx>
57 #include <Model/panelnode.hxx>
58 #include <Model/acmodel.hxx>
59 #include <Scenery/scenery.hxx>
60 #include <Scenery/tilemgr.hxx>
61 #include <Sound/fg_fx.hxx>
62 #include <Sound/beacon.hxx>
63 #include <Sound/morse.hxx>
64 #include <Sound/fg_fx.hxx>
65 #include <ATCDCL/ATCmgr.hxx>
66 #include <Time/TimeManager.hxx>
67 #include <Environment/environment_mgr.hxx>
68 #include <Environment/ephemeris.hxx>
69 #include <GUI/new_gui.hxx>
70 #include <MultiPlayer/multiplaymgr.hxx>
71
72 #include "CameraGroup.hxx"
73 #include "fg_commands.hxx"
74 #include "fg_io.hxx"
75 #include "renderer.hxx"
76 #include "splash.hxx"
77 #include "main.hxx"
78 #include "util.hxx"
79 #include "fg_init.hxx"
80 #include "fg_os.hxx"
81 #include "WindowSystemAdapter.hxx"
82 #include <Main/viewer.hxx>
83
84
85 using namespace flightgear;
86
87 using std::cerr;
88
89 // Specify our current idle function state.  This is used to run all
90 // our initializations out of the idle callback so that we can get a
91 // splash screen up and running right away.
92 int idle_state = 0;
93
94
95 void fgInitSoundManager();
96 void fgSetNewSoundDevice(const char *);
97
98 // The atexit() function handler should know when the graphical subsystem
99 // is initialized.
100 extern int _bootstrap_OSInit;
101
102 // What should we do when we have nothing else to do?  Let's get ready
103 // for the next move and update the display?
104 static void fgMainLoop( void ) {
105     
106     static SGConstPropertyNode_ptr longitude
107         = fgGetNode("/position/longitude-deg");
108     static SGConstPropertyNode_ptr latitude
109         = fgGetNode("/position/latitude-deg");
110     static SGConstPropertyNode_ptr altitude
111         = fgGetNode("/position/altitude-ft");
112     static SGConstPropertyNode_ptr vn_fps
113         = fgGetNode("/velocities/speed-north-fps");
114     static SGConstPropertyNode_ptr ve_fps
115         = fgGetNode("/velocities/speed-east-fps");
116     static SGConstPropertyNode_ptr vd_fps
117         = fgGetNode("/velocities/speed-down-fps");
118       
119     static SGPropertyNode_ptr frame_signal
120         = fgGetNode("/sim/signals/frame", true);
121
122     frame_signal->fireValueChanged();
123     SGCloudLayer::enable_bump_mapping = fgGetBool("/sim/rendering/bump-mapping");
124     
125     SG_LOG( SG_ALL, SG_DEBUG, "Running Main Loop");
126     SG_LOG( SG_ALL, SG_DEBUG, "======= ==== ====");
127     
128     
129   // update "time"
130     double sim_dt, real_dt;
131     TimeManager* timeMgr = (TimeManager*) globals->get_subsystem("time");
132     // compute simulated time (allowing for pause, warp, etc) and
133     // real elapsed time
134     timeMgr->computeTimeDeltas(sim_dt, real_dt);
135
136     // update magvar model
137     globals->get_mag()->update( longitude->getDoubleValue()
138                                 * SGD_DEGREES_TO_RADIANS,
139                                 latitude->getDoubleValue()
140                                 * SGD_DEGREES_TO_RADIANS,
141                                 altitude->getDoubleValue() * SG_FEET_TO_METER,
142                                 globals->get_time_params()->getJD() );
143
144 #if ENABLE_ATCDCL  
145     // Run ATC subsystem
146     if (fgGetBool("/sim/atc/enabled"))
147         globals->get_ATC_mgr()->update(sim_dt);
148 #endif  
149     
150     globals->get_subsystem_mgr()->update(sim_dt);
151
152     // Update the sound manager last so it can use the CPU while the GPU
153     // is processing the scenery (doubled the frame-rate for me) -EMH-
154 #ifdef ENABLE_AUDIO_SUPPORT
155     static bool smgr_init = true;
156     static SGPropertyNode *sound_working = fgGetNode("/sim/sound/working");
157     if (smgr_init == true) {
158         if (sound_working->getBoolValue() == true) {
159             fgInitSoundManager();
160             smgr_init = false;
161         }
162     } else {
163         static SGPropertyNode *sound_enabled = fgGetNode("/sim/sound/enabled");
164         static SGSoundMgr *smgr = globals->get_soundmgr();
165         static bool smgr_enabled = true;
166
167         if (sound_working->getBoolValue() == false) {   // request to reinit
168            smgr->reinit();
169            smgr->resume();
170            sound_working->setBoolValue(true);
171         }
172
173         if (smgr_enabled != sound_enabled->getBoolValue()) {
174             if (smgr_enabled == true) { // request to suspend
175                 smgr->suspend();
176                 smgr_enabled = false;
177             } else {
178                 smgr->resume();
179                 smgr_enabled = true;
180             }
181         }
182
183         if (smgr_enabled == true) {
184             static SGPropertyNode *volume = fgGetNode("/sim/sound/volume");
185             smgr->set_volume(volume->getFloatValue());
186             smgr->update(sim_dt);
187         }
188     }
189 #endif
190
191     // END Tile Manager updates
192     bool scenery_loaded = fgGetBool("sim/sceneryloaded");
193     if (!scenery_loaded)
194     {
195         if (globals->get_tile_mgr()->isSceneryLoaded()
196              && fgGetBool("sim/fdm-initialized")) {
197             fgSetBool("sim/sceneryloaded",true);
198             if (fgGetBool("/sim/sound/working")) {
199                 globals->get_soundmgr()->activate();
200             }
201             globals->get_props()->tie("/sim/sound/devices/name",
202                   SGRawValueFunctions<const char *>(0, fgSetNewSoundDevice), false);
203         }
204         else
205         {
206             // be nice to loader threads while waiting for initial scenery, reduce to 2fps
207             simgear::sleepForMSec(500);
208         }
209     }
210     simgear::AtomicChangeListener::fireChangeListeners();
211
212     SG_LOG( SG_ALL, SG_DEBUG, "" );
213 }
214
215 void fgInitSoundManager()
216 {
217     SGSoundMgr *smgr = globals->get_soundmgr();
218
219     smgr->bind();
220     smgr->init(fgGetString("/sim/sound/device-name", NULL));
221
222     vector <const char*>devices = smgr->get_available_devices();
223     for (unsigned int i=0; i<devices.size(); i++) {
224         SGPropertyNode *p = fgGetNode("/sim/sound/devices/device", i, true);
225         p->setStringValue(devices[i]);
226     }
227     devices.clear();
228 }
229
230 void fgSetNewSoundDevice(const char *device)
231 {
232     globals->get_soundmgr()->suspend();
233     globals->get_soundmgr()->stop();
234     globals->get_soundmgr()->init(device);
235     globals->get_soundmgr()->resume();
236 }
237
238 // Operation for querying OpenGL parameters. This must be done in a
239 // valid OpenGL context, potentially in another thread.
240 namespace
241 {
242 struct GeneralInitOperation : public GraphicsContextOperation
243 {
244     GeneralInitOperation()
245         : GraphicsContextOperation(std::string("General init"))
246     {
247     }
248     void run(osg::GraphicsContext* gc)
249     {
250         SGPropertyNode* simRendering = fgGetNode("/sim/rendering");
251         
252         simRendering->setStringValue("gl-vendor", (char*) glGetString(GL_VENDOR));
253         SG_LOG( SG_GENERAL, SG_INFO, glGetString(GL_VENDOR));
254         
255         simRendering->setStringValue("gl-renderer", (char*) glGetString(GL_RENDERER));
256         SG_LOG( SG_GENERAL, SG_INFO, glGetString(GL_RENDERER));
257         
258         simRendering->setStringValue("gl-version", (char*) glGetString(GL_VERSION));
259         SG_LOG( SG_GENERAL, SG_INFO, glGetString(GL_VERSION));
260
261         GLint tmp;
262         glGetIntegerv( GL_MAX_TEXTURE_SIZE, &tmp );
263         simRendering->setIntValue("max-texture-size", tmp);
264
265         glGetIntegerv( GL_DEPTH_BITS, &tmp );
266         simRendering->setIntValue("depth-buffer-bits", tmp);
267     }
268 };
269
270
271 osg::Node* load_panel(SGPropertyNode *n)
272 {
273     osg::Geode* geode = new osg::Geode;
274     geode->addDrawable(new FGPanelNode(n));
275     return geode;
276 }
277
278 SGPath resolve_path(const std::string& s)
279 {
280   return globals->resolve_maybe_aircraft_path(s);
281 }
282
283 }
284
285 // This is the top level master main function that is registered as
286 // our idle funciton
287
288 // The first few passes take care of initialization things (a couple
289 // per pass) and once everything has been initialized fgMainLoop from
290 // then on.
291
292 static void fgIdleFunction ( void ) {
293     static osg::ref_ptr<GeneralInitOperation> genOp;
294     if ( idle_state == 0 ) {
295         idle_state++;
296         // Pick some window on which to do queries.
297         // XXX Perhaps all this graphics initialization code should be
298         // moved to renderer.cxx?
299         genOp = new GeneralInitOperation;
300         osg::Camera* guiCamera = getGUICamera(CameraGroup::getDefault());
301         WindowSystemAdapter* wsa = WindowSystemAdapter::getWSA();
302         osg::GraphicsContext* gc = 0;
303         if (guiCamera)
304             gc = guiCamera->getGraphicsContext();
305         if (gc) {
306             gc->add(genOp.get());
307         } else {
308             wsa->windows[0]->gc->add(genOp.get());
309         }
310         guiStartInit(gc);
311     } else if ( idle_state == 1 ) {
312         if (genOp.valid()) {
313             if (!genOp->isFinished())
314                 return;
315             genOp = 0;
316         }
317         if (!guiFinishInit())
318             return;
319         idle_state++;
320         fgSplashProgress("reading aircraft list");
321
322
323     } else if ( idle_state == 2 ) {
324         idle_state++;
325                 
326         fgSplashProgress("reading airport & navigation data");
327
328
329     } else if ( idle_state == 3 ) {
330         idle_state++;
331         fgInitNav();
332         fgSplashProgress("setting up scenery");
333
334
335     } else if ( idle_state == 4 ) {
336         idle_state++;
337         // based on the requested presets, calculate the true starting
338         // lon, lat
339         fgInitPosition();
340         fgInitTowerLocationListener();
341
342         TimeManager* t = new TimeManager;
343         globals->add_subsystem("time", t, SGSubsystemMgr::INIT);
344         t->init(); // need to init now, not during initSubsystems
345         
346         // Do some quick general initializations
347         if( !fgInitGeneral()) {
348             SG_LOG( SG_GENERAL, SG_ALERT,
349                 "General initialization failed ..." );
350             exit(-1);
351         }
352
353         ////////////////////////////////////////////////////////////////////
354         // Initialize the property-based built-in commands
355         ////////////////////////////////////////////////////////////////////
356         fgInitCommands();
357
358
359         ////////////////////////////////////////////////////////////////////
360         // Initialize the material manager
361         ////////////////////////////////////////////////////////////////////
362         globals->set_matlib( new SGMaterialLib );
363         simgear::SGModelLib::init(globals->get_fg_root());
364         simgear::SGModelLib::setPropRoot(globals->get_props());
365         simgear::SGModelLib::setPanelFunc(load_panel);
366         
367         ////////////////////////////////////////////////////////////////////
368         // Initialize the TG scenery subsystem.
369         ////////////////////////////////////////////////////////////////////
370         globals->set_scenery( new FGScenery );
371         globals->get_scenery()->init();
372         globals->get_scenery()->bind();
373         globals->set_tile_mgr( new FGTileMgr );
374
375
376         fgSplashProgress("loading aircraft");
377
378
379     } else if ( idle_state == 5 ) {
380         idle_state++;
381
382         fgSplashProgress("generating sky elements");
383
384
385     } else if ( idle_state == 6 ) {
386         idle_state++;
387         // Initialize the sky
388
389         Ephemeris* eph = new Ephemeris;
390         globals->add_subsystem("ephemeris", eph);
391         eph->init(); // FIXME - remove this once SGSky code below is also a subsystem
392         eph->bind();
393
394         // TODO: move to environment mgr
395         thesky = new SGSky;
396         SGPath texture_path(globals->get_fg_root());
397         texture_path.append("Textures");
398         texture_path.append("Sky");
399         for (int i = 0; i < FGEnvironmentMgr::MAX_CLOUD_LAYERS; i++) {
400             SGCloudLayer * layer = new SGCloudLayer(texture_path.str());
401             thesky->add_cloud_layer(layer);
402         }
403
404         SGPath sky_tex_path( globals->get_fg_root() );
405         sky_tex_path.append( "Textures" );
406         sky_tex_path.append( "Sky" );
407         thesky->texture_path( sky_tex_path.str() );
408
409         // The sun and moon diameters are scaled down numbers of the
410         // actual diameters. This was needed to fit both the sun and the
411         // moon within the distance to the far clip plane.
412         // Moon diameter:    3,476 kilometers
413         // Sun diameter: 1,390,000 kilometers
414         thesky->build( 80000.0, 80000.0,
415                        463.3, 361.8,
416                        *globals->get_ephem(),
417                        fgGetNode("/environment", true));
418
419         // Initialize MagVar model
420         SGMagVar *magvar = new SGMagVar();
421         globals->set_mag( magvar );
422
423
424                                     // kludge to initialize mag compass
425                                     // (should only be done for in-flight
426                                     // startup)
427         // update magvar model
428         globals->get_mag()->update( fgGetDouble("/position/longitude-deg")
429                                     * SGD_DEGREES_TO_RADIANS,
430                                     fgGetDouble("/position/latitude-deg")
431                                     * SGD_DEGREES_TO_RADIANS,
432                                     fgGetDouble("/position/altitude-ft")
433                                     * SG_FEET_TO_METER,
434                                     globals->get_time_params()->getJD() );
435         double var = globals->get_mag()->get_magvar() * SGD_RADIANS_TO_DEGREES;
436         fgSetDouble("/instrumentation/heading-indicator/offset-deg", -var);
437         fgSetDouble("/instrumentation/heading-indicator-fg/offset-deg", -var);
438
439
440         // airport = new ssgBranch;
441         // airport->setName( "Airport Lighting" );
442         // lighting->addKid( airport );
443
444         // build our custom render states
445         fgSplashProgress("initializing subsystems");
446
447
448     } else if ( idle_state == 7 ) {
449         idle_state++;
450         // Initialize audio support
451 #ifdef ENABLE_AUDIO_SUPPORT
452
453         // Start the intro music
454         if ( fgGetBool("/sim/startup/intro-music") ) {
455             SGPath mp3file( globals->get_fg_root() );
456             mp3file.append( "Sounds/intro.mp3" );
457
458             SG_LOG( SG_GENERAL, SG_INFO,
459                 "Starting intro music: " << mp3file.str() );
460
461 # if defined( __CYGWIN__ )
462             string command = "start /m `cygpath -w " + mp3file.str() + "`";
463 # elif defined( _WIN32 )
464             string command = "start /m " + mp3file.str();
465 # else
466             string command = "mpg123 " + mp3file.str() + "> /dev/null 2>&1";
467 # endif
468
469             system ( command.c_str() );
470         }
471 #endif
472         // This is the top level init routine which calls all the
473         // other subsystem initialization routines.  If you are adding
474         // a subsystem to flightgear, its initialization call should be
475         // located in this routine.
476         if( !fgInitSubsystems()) {
477             SG_LOG( SG_GENERAL, SG_ALERT,
478                 "Subsystem initialization failed ..." );
479             exit(-1);
480         }
481
482         // Torsten Dreyer:
483         // ugly hack for automatic runway selection on startup based on
484         // metar data. Makes startup.nas obsolete and guarantees the same
485         // runway selection as for AI traffic. However, this code belongs to
486         // somewhere(?) else - if I only new where...
487         if( true == fgGetBool( "/environment/metar/valid" ) ) {
488             // the realwx_ctrl fetches metar in the foreground on init,
489             // If it was able to fetch a metar or one was given on the commandline,
490             // the valid flag is set here, otherwise it is false
491             double hdg = fgGetDouble( "/environment/metar/base-wind-dir-deg", 9999.0 );
492             string apt = fgGetString( "/sim/startup/options/airport" );
493             string rwy = fgGetString( "/sim/startup/options/runway" );
494             double strthdg = fgGetDouble( "/sim/startup/options/heading-deg", 9999.0 );
495             string parkpos = fgGetString( "/sim/presets/parkpos" );
496             bool onground = fgGetBool( "/sim/presets/onground", false );
497             // don't check for wind-speed < 1kt, this belongs to the runway-selection code
498             // the other logic is taken from former startup.nas
499             if( hdg < 360.0 && apt.length() > 0 && strthdg > 360.0 && rwy.length() == 0 && onground && parkpos.length() == 0 ) {
500                 extern bool fgSetPosFromAirportIDandHdg( const string& id, double tgt_hdg );
501                 fgSetPosFromAirportIDandHdg( apt, hdg );
502             }
503         }
504         fgSplashProgress("setting up time & renderer");
505
506     } else if ( idle_state == 8 ) {
507         idle_state = 1000;
508         
509         // setup OpenGL view parameters
510         globals->get_renderer()->init();
511
512         globals->get_renderer()->resize( fgGetInt("/sim/startup/xsize"),
513                                          fgGetInt("/sim/startup/ysize") );
514
515         fgSplashProgress("loading scenery objects");
516         int session = fgGetInt("/sim/session",0);
517         session++;
518         fgSetInt("/sim/session",session);
519     }
520
521     if ( idle_state == 1000 ) {
522         // We've finished all our initialization steps, from now on we
523         // run the main loop.
524         fgSetBool("sim/sceneryloaded", false);
525         fgRegisterIdleHandler( fgMainLoop );
526     }
527 }
528
529
530 static void upper_case_property(const char *name)
531 {
532     using namespace simgear;
533     SGPropertyNode *p = fgGetNode(name, false);
534     if (!p) {
535         p = fgGetNode(name, true);
536         p->setStringValue("");
537     } else {
538         props::Type t = p->getType();
539         if (t == props::NONE || t == props::UNSPECIFIED)
540             p->setStringValue("");
541         else
542             assert(t == props::STRING);
543     }
544     p->addChangeListener(new FGMakeUpperCase);
545 }
546
547
548 // Main top level initialization
549 int fgMainInit( int argc, char **argv ) {
550
551     // set default log levels
552     sglog().setLogLevels( SG_ALL, SG_ALERT );
553
554     string version;
555 #ifdef FLIGHTGEAR_VERSION
556     version = FLIGHTGEAR_VERSION;
557 #else
558     version = "unknown version";
559 #endif
560     SG_LOG( SG_GENERAL, SG_INFO, "FlightGear:  Version "
561             << version );
562     SG_LOG( SG_GENERAL, SG_INFO, "Built with " << SG_COMPILER_STR << endl );
563
564     // Allocate global data structures.  This needs to happen before
565     // we parse command line options
566
567     globals = new FGGlobals;
568
569     // seed the random number generator
570     sg_srandom_time();
571
572     FGControls *controls = new FGControls;
573     globals->set_controls( controls );
574
575     string_list *col = new string_list;
576     globals->set_channel_options_list( col );
577
578     fgValidatePath("", false);  // initialize static variables
579     upper_case_property("/sim/presets/airport-id");
580     upper_case_property("/sim/presets/runway");
581     upper_case_property("/sim/tower/airport-id");
582     upper_case_property("/autopilot/route-manager/input");
583
584     // Scan the config file(s) and command line options to see if
585     // fg_root was specified (ignore all other options for now)
586     fgInitFGRoot(argc, argv);
587
588     // Check for the correct base package version
589     static char required_version[] = "2.0.0";
590     string base_version = fgBasePackageVersion();
591     if ( !(base_version == required_version) ) {
592         // tell the operator how to use this application
593
594         SG_LOG( SG_GENERAL, SG_ALERT, "" ); // To popup the console on windows
595         cerr << endl << "Base package check failed ... " \
596              << "Found version " << base_version << " at: " \
597              << globals->get_fg_root() << endl;
598         cerr << "Please upgrade to version: " << required_version << endl;
599 #ifdef _MSC_VER
600         cerr << "Hit a key to continue..." << endl;
601         cin.get();
602 #endif
603         exit(-1);
604     }
605
606     // Load the configuration parameters.  (Command line options
607     // override config file options.  Config file options override
608     // defaults.)
609     if ( !fgInitConfig(argc, argv) ) {
610         SG_LOG( SG_GENERAL, SG_ALERT, "Config option parsing failed ..." );
611         exit(-1);
612     }
613
614     // Initialize the Window/Graphics environment.
615     fgOSInit(&argc, argv);
616     _bootstrap_OSInit++;
617
618     fgRegisterWindowResizeHandler( &FGRenderer::resize );
619     fgRegisterIdleHandler( &fgIdleFunction );
620     fgRegisterDrawHandler( &FGRenderer::update );
621
622     // Initialize sockets (WinSock needs this)
623     simgear::Socket::initSockets();
624
625     // Clouds3D requires an alpha channel
626     fgOSOpenWindow(true /* request stencil buffer */);
627
628     // Initialize the splash screen right away
629     fntInit();
630     fgSplashInit();
631
632     // pass control off to the master event handler
633     int result = fgOSMainLoop();
634     
635     // clean up here; ensure we null globals to avoid
636     // confusing the atexit() handler
637     delete globals;
638     globals = NULL;
639     
640     return result;
641 }
642
643