]> git.mxchange.org Git - flightgear.git/blob - src/Main/main.cxx
Crash-fix: mat-lib is now reference-counted.
[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 #include <iostream>
31
32 #include <osg/Camera>
33 #include <osg/GraphicsContext>
34 #include <osgDB/Registry>
35
36 #if defined(HAVE_CRASHRPT)
37         #include <CrashRpt.h>
38 #endif
39
40 // Class references
41 #include <simgear/canvas/VGInitOperation.hxx>
42 #include <simgear/scene/model/modellib.hxx>
43 #include <simgear/scene/material/matlib.hxx>
44 #include <simgear/scene/material/Effect.hxx>
45 #include <simgear/props/AtomicChangeListener.hxx>
46 #include <simgear/props/props.hxx>
47 #include <simgear/timing/sg_time.hxx>
48 #include <simgear/io/raw_socket.hxx>
49 #include <simgear/scene/tsync/terrasync.hxx>
50 #include <simgear/math/SGMath.hxx>
51 #include <simgear/math/sg_random.h>
52
53 #include <Model/panelnode.hxx>
54 #include <Scenery/scenery.hxx>
55 #include <Scenery/tilemgr.hxx>
56 #include <Sound/soundmanager.hxx>
57 #include <Time/TimeManager.hxx>
58 #include <GUI/gui.h>
59 #include <Viewer/splash.hxx>
60 #include <Viewer/renderer.hxx>
61 #include <Viewer/WindowSystemAdapter.hxx>
62 #include <Navaids/NavDataCache.hxx>
63
64 #include "fg_commands.hxx"
65 #include "fg_io.hxx"
66 #include "main.hxx"
67 #include "util.hxx"
68 #include "fg_init.hxx"
69 #include "fg_os.hxx"
70 #include "fg_props.hxx"
71 #include "positioninit.hxx"
72 #include "screensaver_control.hxx"
73 #include "subsystemFactory.hxx"
74 #include "options.hxx"
75
76
77 using namespace flightgear;
78
79 using std::cerr;
80 using std::vector;
81
82 // The atexit() function handler should know when the graphical subsystem
83 // is initialized.
84 extern int _bootstrap_OSInit;
85
86 static SGPropertyNode_ptr frame_signal;
87 static TimeManager* timeMgr;
88
89 // What should we do when we have nothing else to do?  Let's get ready
90 // for the next move and update the display?
91 static void fgMainLoop( void )
92 {
93     frame_signal->fireValueChanged();
94
95     // compute simulated time (allowing for pause, warp, etc) and
96     // real elapsed time
97     double sim_dt, real_dt;
98     timeMgr->computeTimeDeltas(sim_dt, real_dt);
99
100     // update all subsystems
101     globals->get_subsystem_mgr()->update(sim_dt);
102
103     simgear::AtomicChangeListener::fireChangeListeners();
104 }
105
106 static void initTerrasync()
107 {
108     // add the terrasync root as a data path so data can be retrieved from it
109     // (even if we are in read-only mode)
110     std::string terraSyncDir(fgGetString("/sim/terrasync/scenery-dir"));
111     globals->append_data_path(terraSyncDir);
112     
113     if (fgGetBool("/sim/fghome-readonly", false)) {
114         return;
115     }
116     
117     // start TerraSync up now, so it can be synchronizing shared models
118     // and airports data in parallel with a nav-cache rebuild.
119     SGPath tsyncCache(globals->get_fg_home());
120     tsyncCache.append("terrasync-cache.xml");
121     
122     // wipe the cache file if requested
123     if (flightgear::Options::sharedInstance()->isOptionSet("restore-defaults")) {
124         SG_LOG(SG_GENERAL, SG_INFO, "restore-defaults requested, wiping terrasync update cache at " <<
125                tsyncCache);
126         if (tsyncCache.exists()) {
127             tsyncCache.remove();
128         }
129     }
130     
131     fgSetString("/sim/terrasync/cache-path", tsyncCache.c_str());
132     
133     simgear::SGTerraSync* terra_sync = new simgear::SGTerraSync();
134     terra_sync->setRoot(globals->get_props());
135     globals->add_subsystem("terrasync", terra_sync);
136     
137     terra_sync->bind();
138     terra_sync->init();
139 }
140
141 static void registerMainLoop()
142 {
143     // stash current frame signal property
144     frame_signal = fgGetNode("/sim/signals/frame", true);
145     timeMgr = (TimeManager*) globals->get_subsystem("time");
146     fgRegisterIdleHandler( fgMainLoop );
147 }
148
149 // This is the top level master main function that is registered as
150 // our idle function
151
152 // The first few passes take care of initialization things (a couple
153 // per pass) and once everything has been initialized fgMainLoop from
154 // then on.
155
156 static int idle_state = 0;
157
158 static void fgIdleFunction ( void ) {
159     // Specify our current idle function state.  This is used to run all
160     // our initializations out of the idle callback so that we can get a
161     // splash screen up and running right away.
162     
163     if ( idle_state == 0 ) {
164         if (guiInit())
165         {
166             idle_state+=2;
167             fgSplashProgress("loading-aircraft-list");
168         }
169
170     } else if ( idle_state == 2 ) {
171         initTerrasync();
172         idle_state++;
173         fgSplashProgress("loading-nav-dat");
174
175     } else if ( idle_state == 3 ) {
176         
177         bool done = fgInitNav();
178         if (done) {
179           ++idle_state;
180           fgSplashProgress("init-scenery");
181         } else {
182           fgSplashProgress("loading-nav-dat");
183         }
184       
185     } else if ( idle_state == 4 ) {
186         idle_state++;
187
188         TimeManager* t = new TimeManager;
189         globals->add_subsystem("time", t, SGSubsystemMgr::INIT);
190         
191         // Do some quick general initializations
192         if( !fgInitGeneral()) {
193             throw sg_exception("General initialization failed");
194         }
195
196         ////////////////////////////////////////////////////////////////////
197         // Initialize the property-based built-in commands
198         ////////////////////////////////////////////////////////////////////
199         fgInitCommands();
200
201         flightgear::registerSubsystemCommands(globals->get_commands());
202
203         ////////////////////////////////////////////////////////////////////
204         // Initialize the material manager
205         ////////////////////////////////////////////////////////////////////
206         globals->set_matlib( new SGMaterialLib );
207         simgear::SGModelLib::setPanelFunc(FGPanelNode::load);
208  
209     } else if (( idle_state == 5 ) || (idle_state == 2005)) {
210         idle_state+=2;
211         flightgear::initPosition();
212         flightgear::initTowerLocationListener();
213         
214         simgear::SGModelLib::init(globals->get_fg_root(), globals->get_props());
215         
216         TimeManager* timeManager = (TimeManager*) globals->get_subsystem("time");
217         timeManager->init();
218         
219         ////////////////////////////////////////////////////////////////////
220         // Initialize the TG scenery subsystem.
221         ////////////////////////////////////////////////////////////////////
222         
223         globals->set_scenery( new FGScenery );
224         globals->get_scenery()->init();
225         globals->get_scenery()->bind();
226         globals->set_tile_mgr( new FGTileMgr );
227         
228         fgSplashProgress("creating-subsystems");
229     } else if (( idle_state == 7 ) || (idle_state == 2007)) {
230         bool isReset = (idle_state == 2007);
231         idle_state = 8; // from the next state on, reset & startup are identical
232         SGTimeStamp st;
233         st.stamp();
234         fgCreateSubsystems(isReset);
235         SG_LOG(SG_GENERAL, SG_INFO, "Creating subsystems took:" << st.elapsedMSec());
236         fgSplashProgress("binding-subsystems");
237       
238     } else if ( idle_state == 8 ) {
239         idle_state++;
240         SGTimeStamp st;
241         st.stamp();
242         globals->get_subsystem_mgr()->bind();
243         SG_LOG(SG_GENERAL, SG_INFO, "Binding subsystems took:" << st.elapsedMSec());
244
245         fgSplashProgress("init-subsystems");
246     } else if ( idle_state == 9 ) {
247         SGSubsystem::InitStatus status = globals->get_subsystem_mgr()->incrementalInit();
248         if ( status == SGSubsystem::INIT_DONE) {
249           ++idle_state;
250           fgSplashProgress("finishing-subsystems");
251         } else {
252           fgSplashProgress("init-subsystems");
253         }
254       
255     } else if ( idle_state == 10 ) {
256         idle_state = 900;
257         fgPostInitSubsystems();
258         fgSplashProgress("finalize-position");
259     } else if ( idle_state == 900 ) {
260         idle_state = 1000;
261         
262         // setup OpenGL view parameters
263         globals->get_renderer()->setupView();
264
265         globals->get_renderer()->resize( fgGetInt("/sim/startup/xsize"),
266                                          fgGetInt("/sim/startup/ysize") );
267         WindowSystemAdapter::getWSA()->windows[0]->gc->add(
268           new simgear::canvas::VGInitOperation()
269         );
270
271         int session = fgGetInt("/sim/session",0);
272         session++;
273         fgSetInt("/sim/session",session);
274     }
275
276     if ( idle_state == 1000 ) {
277         // We've finished all our initialization steps, from now on we
278         // run the main loop.
279         fgSetBool("sim/sceneryloaded", false);
280         registerMainLoop();
281     }
282     
283     if ( idle_state == 2000 ) {
284         fgStartNewReset();
285         idle_state = 2005;
286     }
287 }
288
289 void fgResetIdleState()
290 {
291     idle_state = 2000;
292     fgRegisterIdleHandler( &fgIdleFunction );
293 }
294
295
296 static void upper_case_property(const char *name)
297 {
298     using namespace simgear;
299     SGPropertyNode *p = fgGetNode(name, false);
300     if (!p) {
301         p = fgGetNode(name, true);
302         p->setStringValue("");
303     } else {
304         props::Type t = p->getType();
305         if (t == props::NONE || t == props::UNSPECIFIED)
306             p->setStringValue("");
307         else
308             assert(t == props::STRING);
309     }
310     SGPropertyChangeListener* muc = new FGMakeUpperCase;
311     globals->addListenerToCleanup(muc);
312     p->addChangeListener(muc);
313 }
314
315 // see http://code.google.com/p/flightgear-bugs/issues/detail?id=385
316 // for the details of this.
317 static void ATIScreenSizeHack()
318 {
319     osg::ref_ptr<osg::Camera> hackCam = new osg::Camera;
320     hackCam->setRenderOrder(osg::Camera::PRE_RENDER);
321     int prettyMuchAnyInt = 1;
322     hackCam->setViewport(0, 0, prettyMuchAnyInt, prettyMuchAnyInt);
323     globals->get_renderer()->addCamera(hackCam, false);
324 }
325
326 // Propose NVIDIA Optimus to use high-end GPU
327 #if defined(SG_WINDOWS)
328 extern "C" {
329     _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
330 }
331 #endif
332
333 static void logToFile()
334 {
335     SGPath logPath = globals->get_fg_home();
336     logPath.append("fgfs.log");
337     if (logPath.exists()) {
338         SGPath prevLogPath = globals->get_fg_home();
339         prevLogPath.append("fgfs_0.log");
340         logPath.rename(prevLogPath);
341         // bit strange, we need to restore the correct value of logPath now
342         logPath = globals->get_fg_home();
343         logPath.append("fgfs.log");
344     }
345     sglog().logToFile(logPath, SG_ALL, SG_INFO);
346
347 #if defined(HAVE_CRASHRPT)
348         crAddFile2(logPath.c_str(), NULL, "FlightGear Log File", CR_AF_MAKE_FILE_COPY);
349 #endif
350 }
351
352 // Main top level initialization
353 int fgMainInit( int argc, char **argv ) {
354
355     // set default log levels
356     sglog().setLogLevels( SG_ALL, SG_ALERT );
357
358     globals = new FGGlobals;
359     if (!fgInitHome()) {
360         return EXIT_FAILURE;
361     }
362     
363     if (!fgGetBool("/sim/fghome-readonly")) {
364         // now home is initialised, we can log to a file inside it
365         logToFile();
366     }
367     
368     std::string version;
369 #ifdef FLIGHTGEAR_VERSION
370     version = FLIGHTGEAR_VERSION;
371 #else
372     version = "unknown version";
373 #endif
374     SG_LOG( SG_GENERAL, SG_INFO, "FlightGear:  Version "
375             << version );
376     SG_LOG( SG_GENERAL, SG_INFO, "Built with " << SG_COMPILER_STR << std::endl );
377
378     // Allocate global data structures.  This needs to happen before
379     // we parse command line options
380
381     
382     
383     // seed the random number generator
384     sg_srandom_time();
385
386     string_list *col = new string_list;
387     globals->set_channel_options_list( col );
388
389     fgValidatePath("", false);  // initialize static variables
390     upper_case_property("/sim/presets/airport-id");
391     upper_case_property("/sim/presets/runway");
392     upper_case_property("/sim/tower/airport-id");
393     upper_case_property("/autopilot/route-manager/input");
394
395     // Load the configuration parameters.  (Command line options
396     // override config file options.  Config file options override
397     // defaults.)
398     int configResult = fgInitConfig(argc, argv, false);
399     if (configResult == flightgear::FG_OPTIONS_ERROR) {
400         return EXIT_FAILURE;
401     } else if (configResult == flightgear::FG_OPTIONS_EXIT) {
402         return EXIT_SUCCESS;
403     }
404     
405     configResult = fgInitAircraft(false);
406     if (configResult == flightgear::FG_OPTIONS_ERROR) {
407         return EXIT_FAILURE;
408     } else if (configResult == flightgear::FG_OPTIONS_EXIT) {
409         return EXIT_SUCCESS;
410     }
411     
412     configResult = flightgear::Options::sharedInstance()->processOptions();
413     if (configResult == flightgear::FG_OPTIONS_ERROR) {
414         return EXIT_FAILURE;
415     } else if (configResult == flightgear::FG_OPTIONS_EXIT) {
416         return EXIT_SUCCESS;
417     }
418     
419     // Initialize the Window/Graphics environment.
420     fgOSInit(&argc, argv);
421     _bootstrap_OSInit++;
422
423     fgRegisterIdleHandler( &fgIdleFunction );
424
425     // Initialize sockets (WinSock needs this)
426     simgear::Socket::initSockets();
427
428     // Clouds3D requires an alpha channel
429     fgOSOpenWindow(true /* request stencil buffer */);
430     fgOSResetProperties();
431     
432     // Initialize the splash screen right away
433     fntInit();
434     fgSplashInit();
435
436     if (fgGetBool("/sim/ati-viewport-hack", true)) {
437         SG_LOG(SG_GENERAL, SG_ALERT, "Enabling ATI viewport hack");
438         ATIScreenSizeHack();
439     }
440     
441     fgOutputSettings();
442     
443     //try to disable the screensaver
444     fgOSDisableScreensaver();
445     
446     // pass control off to the master event handler
447     int result = fgOSMainLoop();
448     frame_signal.clear();
449     fgOSCloseWindow();
450     
451     simgear::clearEffectCache();
452     
453     // clean up here; ensure we null globals to avoid
454     // confusing the atexit() handler
455     delete globals;
456     globals = NULL;
457     
458     // delete the NavCache here. This will cause the destruction of many cached
459     // objects (eg, airports, navaids, runways).
460     delete flightgear::NavDataCache::instance();
461   
462     return result;
463 }