]> git.mxchange.org Git - flightgear.git/blob - src/Main/globals.cxx
Clean-up some SGMath dependencies.
[flightgear.git] / src / Main / globals.cxx
1 // globals.cxx -- Global state that needs to be shared among the sim modules
2 //
3 // Written by Curtis Olson, started July 2000.
4 //
5 // Copyright (C) 2000  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 Foundation,
19 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include <config.h>
25 #endif
26
27 #include <boost/foreach.hpp>
28 #include <algorithm>
29
30 #include <simgear/structure/commands.hxx>
31 #include <simgear/misc/sg_path.hxx>
32 #include <simgear/misc/sg_dir.hxx>
33 #include <simgear/timing/sg_time.hxx>
34 #include <simgear/ephemeris/ephemeris.hxx>
35 #include <simgear/magvar/magvar.hxx>
36 #include <simgear/scene/material/matlib.hxx>
37 #include <simgear/structure/subsystem_mgr.hxx>
38 #include <simgear/structure/event_mgr.hxx>
39 #include <simgear/sound/soundmgr_openal.hxx>
40 #include <simgear/misc/ResourceManager.hxx>
41 #include <simgear/props/propertyObject.hxx>
42 #include <simgear/props/props_io.hxx>
43 #include <simgear/scene/model/placement.hxx>
44
45 #include <Aircraft/controls.hxx>
46 #include <Airports/runways.hxx>
47 #include <ATCDCL/ATCmgr.hxx>
48 #include <Autopilot/route_mgr.hxx>
49 #include <Cockpit/panel.hxx>
50 #include <GUI/FGFontCache.hxx>
51 #include <GUI/gui.h>
52 #include <Model/acmodel.hxx>
53 #include <Model/modelmgr.hxx>
54 #include <MultiPlayer/multiplaymgr.hxx>
55 #include <Scenery/scenery.hxx>
56 #include <Scenery/tilemgr.hxx>
57 #include <Navaids/navlist.hxx>
58 #include <Viewer/renderer.hxx>
59 #include <Viewer/viewmgr.hxx>
60
61 #include "globals.hxx"
62 #include "locale.hxx"
63
64 #include "fg_props.hxx"
65 #include "fg_io.hxx"
66
67 class AircraftResourceProvider : public simgear::ResourceProvider
68 {
69 public:
70   AircraftResourceProvider() :
71     simgear::ResourceProvider(simgear::ResourceManager::PRIORITY_HIGH)
72   {
73   }
74   
75   virtual SGPath resolve(const std::string& aResource, SGPath&) const
76   {
77     string_list pieces(sgPathBranchSplit(aResource));
78     if ((pieces.size() < 3) || (pieces.front() != "Aircraft")) {
79       return SGPath(); // not an Aircraft path
80     }
81     
82   // test against the aircraft-dir property
83     const char* aircraftDir = fgGetString("/sim/aircraft-dir");
84     string_list aircraftDirPieces(sgPathBranchSplit(aircraftDir));
85     if (!aircraftDirPieces.empty() && (aircraftDirPieces.back() == pieces[1])) {
86         // current aircraft-dir matches resource aircraft
87         SGPath r(aircraftDir);
88         for (unsigned int i=2; i<pieces.size(); ++i) {
89           r.append(pieces[i]);
90         }
91         
92         if (r.exists()) {
93           SG_LOG(SG_IO, SG_DEBUG, "found path:" << aResource << " via /sim/aircraft-dir: " << r.str());
94           return r;
95         }
96     }
97   
98   // try each aircraft dir in turn
99     std::string res(aResource, 9); // resource path with 'Aircraft/' removed
100     const string_list& dirs(globals->get_aircraft_paths());
101     string_list::const_iterator it = dirs.begin();
102     for (; it != dirs.end(); ++it) {
103       SGPath p(*it, res);
104       if (p.exists()) {
105         SG_LOG(SG_IO, SG_DEBUG, "found path:" << aResource << " in aircraft dir: " << *it);
106         return p;
107       }
108     } // of aircraft path iteration
109     
110     return SGPath(); // not found
111   }
112 };
113
114 ////////////////////////////////////////////////////////////////////////
115 // Implementation of FGGlobals.
116 ////////////////////////////////////////////////////////////////////////
117
118 // global global :-)
119 FGGlobals *globals;
120
121
122 // Constructor
123 FGGlobals::FGGlobals() :
124     props( new SGPropertyNode ),
125     initial_state( NULL ),
126     locale( new FGLocale(props) ),
127     renderer( new FGRenderer ),
128     subsystem_mgr( new SGSubsystemMgr ),
129     event_mgr( new SGEventMgr ),
130     sim_time_sec( 0.0 ),
131     fg_root( "" ),
132     time_params( NULL ),
133     ephem( NULL ),
134     mag( NULL ),
135     matlib( NULL ),
136     route_mgr( NULL ),
137     current_panel( NULL ),
138     ATC_mgr( NULL ),
139     controls( NULL ),
140     viewmgr( NULL ),
141     commands( SGCommandMgr::instance() ),
142     acmodel( NULL ),
143     model_mgr( NULL ),
144     channel_options_list( NULL ),
145     initial_waypoints( NULL ),
146     scenery( NULL ),
147     tile_mgr( NULL ),
148     fontcache ( new FGFontCache ),
149     navlist( NULL ),
150     loclist( NULL ),
151     gslist( NULL ),
152     dmelist( NULL ),
153     tacanlist( NULL ),
154     carrierlist( NULL ),
155     channellist( NULL ),
156     haveUserSettings(false)
157 {
158   simgear::ResourceManager::instance()->addProvider(new AircraftResourceProvider());
159   simgear::PropertyObjectBase::setDefaultRoot(props);
160 }
161
162 // Destructor
163 FGGlobals::~FGGlobals() 
164 {
165     // save user settings (unless already saved)
166     saveUserSettings();
167
168     // The AIModels manager performs a number of actions upon
169     // Shutdown that implicitly assume that other subsystems
170     // are still operational (Due to the dynamic allocation and
171     // deallocation of AIModel objects. To ensure we can safely
172     // shut down all subsystems, make sure we take down the 
173     // AIModels system first.
174     SGSubsystem* ai = subsystem_mgr->remove("ai-model");
175     if (ai) {
176         ai->unbind();
177         delete ai;
178     }
179     SGSubsystem* sound = subsystem_mgr->remove("sound");
180
181     subsystem_mgr->shutdown();
182     subsystem_mgr->unbind();
183     delete subsystem_mgr;
184     
185     delete renderer;
186     renderer = NULL;
187     
188     delete time_params;
189     delete mag;
190     delete matlib;
191     delete route_mgr;
192     current_panel = NULL;
193
194     delete ATC_mgr;
195
196     if (controls)
197     {
198         controls->unbind();
199         delete controls;
200     }
201
202     delete channel_options_list;
203     delete initial_waypoints;
204     delete scenery;
205     delete fontcache;
206
207     delete navlist;
208     delete loclist;
209     delete gslist;
210     delete dmelist;
211     delete tacanlist;
212     delete carrierlist;
213     delete channellist;
214     delete sound;
215
216     delete locale;
217     locale = NULL;
218 }
219
220
221 // set the fg_root path
222 void FGGlobals::set_fg_root (const string &root) {
223     SGPath tmp(root);
224     fg_root = tmp.realpath();
225
226     // append /data to root if it exists
227     tmp.append( "data" );
228     tmp.append( "version" );
229     if ( tmp.exists() ) {
230         fgGetNode("BAD_FG_ROOT", true)->setStringValue(fg_root);
231         fg_root += "/data";
232         fgGetNode("GOOD_FG_ROOT", true)->setStringValue(fg_root);
233         SG_LOG(SG_GENERAL, SG_ALERT, "***\n***\n*** Warning: changing bad FG_ROOT/--fg-root to '"
234                 << fg_root << "'\n***\n***");
235     }
236
237     // remove /sim/fg-root before writing to prevent hijacking
238     SGPropertyNode *n = fgGetNode("/sim", true);
239     n->removeChild("fg-root", 0, false);
240     n = n->getChild("fg-root", 0, true);
241     n->setStringValue(fg_root.c_str());
242     n->setAttribute(SGPropertyNode::WRITE, false);
243     
244     simgear::ResourceManager::instance()->addBasePath(fg_root, 
245       simgear::ResourceManager::PRIORITY_DEFAULT);
246 }
247
248 void FGGlobals::append_fg_scenery (const string &paths)
249 {
250 //    fg_scenery.clear();
251     SGPropertyNode* sim = fgGetNode("/sim", true);
252
253   // find first unused fg-scenery property in /sim
254     int propIndex = 0;
255     while (sim->getChild("fg-scenery", propIndex) != NULL) {
256       ++propIndex; 
257     }
258   
259     BOOST_FOREACH(const SGPath& path, sgPathSplit( paths )) {
260         SGPath abspath(path.realpath());
261         if (!abspath.exists()) {
262           SG_LOG(SG_GENERAL, SG_WARN, "scenery path not found:" << abspath.str());
263           continue;
264         }
265
266       // check for duplicates
267       string_list::const_iterator ex = std::find(fg_scenery.begin(), fg_scenery.end(), abspath.str());
268       if (ex != fg_scenery.end()) {
269         SG_LOG(SG_GENERAL, SG_INFO, "skipping duplicate add of scenery path:" << abspath.str());
270         continue;
271       }
272       
273         simgear::Dir dir(abspath);
274         SGPath terrainDir(dir.file("Terrain"));
275         SGPath objectsDir(dir.file("Objects"));
276         
277       // this code used to add *either* the base dir, OR add the 
278       // Terrain and Objects subdirs, but the conditional logic was commented
279       // out, such that all three dirs are added. Unfortunately there's
280       // no information as to why the change was made.
281         fg_scenery.push_back(abspath.str());
282         
283         if (terrainDir.exists()) {
284           fg_scenery.push_back(terrainDir.str());
285         }
286         
287         if (objectsDir.exists()) {
288           fg_scenery.push_back(objectsDir.str());
289         }
290         
291         // insert a marker for FGTileEntry::load(), so that
292         // FG_SCENERY=A:B becomes list ["A/Terrain", "A/Objects", "",
293         // "B/Terrain", "B/Objects", ""]
294         fg_scenery.push_back("");
295         
296       // make scenery dirs available to Nasal
297         SGPropertyNode* n = sim->getChild("fg-scenery", propIndex++, true);
298         n->setStringValue(abspath.str());
299         n->setAttribute(SGPropertyNode::WRITE, false);
300     } // of path list iteration
301 }
302
303 void FGGlobals::append_aircraft_path(const std::string& path)
304 {
305   SGPath dirPath(path);
306   if (!dirPath.exists()) {
307     SG_LOG(SG_GENERAL, SG_WARN, "aircraft path not found:" << path);
308     return;
309   }
310   std::string abspath = dirPath.realpath();
311   
312   unsigned int index = fg_aircraft_dirs.size();  
313   fg_aircraft_dirs.push_back(abspath);
314   
315 // make aircraft dirs available to Nasal
316   SGPropertyNode* sim = fgGetNode("/sim", true);
317   sim->removeChild("fg-aircraft", index, false);
318   SGPropertyNode* n = sim->getChild("fg-aircraft", index, true);
319   n->setStringValue(abspath);
320   n->setAttribute(SGPropertyNode::WRITE, false);
321 }
322
323 void FGGlobals::append_aircraft_paths(const std::string& path)
324 {
325   string_list paths = sgPathSplit(path);
326   for (unsigned int p = 0; p<paths.size(); ++p) {
327     append_aircraft_path(paths[p]);
328   }
329 }
330
331 SGPath FGGlobals::resolve_aircraft_path(const std::string& branch) const
332 {
333   return simgear::ResourceManager::instance()->findPath(branch);
334 }
335
336 SGPath FGGlobals::resolve_maybe_aircraft_path(const std::string& branch) const
337 {
338   return simgear::ResourceManager::instance()->findPath(branch);
339 }
340
341 FGRenderer *
342 FGGlobals::get_renderer () const
343 {
344    return renderer;
345 }
346
347 SGSubsystemMgr *
348 FGGlobals::get_subsystem_mgr () const
349 {
350     return subsystem_mgr;
351 }
352
353 SGSubsystem *
354 FGGlobals::get_subsystem (const char * name)
355 {
356     return subsystem_mgr->get_subsystem(name);
357 }
358
359 void
360 FGGlobals::add_subsystem (const char * name,
361                           SGSubsystem * subsystem,
362                           SGSubsystemMgr::GroupType type,
363                           double min_time_sec)
364 {
365     subsystem_mgr->add(name, subsystem, type, min_time_sec);
366 }
367
368 SGSoundMgr *
369 FGGlobals::get_soundmgr () const
370 {
371     if (subsystem_mgr)
372         return (SGSoundMgr*) subsystem_mgr->get_subsystem("sound");
373
374     return NULL;
375 }
376
377 SGEventMgr *
378 FGGlobals::get_event_mgr () const
379 {
380     return event_mgr;
381 }
382
383 const SGGeod &
384 FGGlobals::get_aircraft_position() const
385 {
386     if( acmodel != NULL ) {
387         SGModelPlacement * mp = acmodel->get3DModel();
388         if( mp != NULL )
389             return mp->getPosition();
390     }
391     throw sg_exception("Can't get aircraft position", "FGGlobals::get_aircraft_position()" );
392 }
393
394 SGVec3d
395 FGGlobals::get_aircraft_positon_cart() const
396 {
397     return SGVec3d::fromGeod(get_aircraft_position());
398 }
399
400
401 // Save the current state as the initial state.
402 void
403 FGGlobals::saveInitialState ()
404 {
405   initial_state = new SGPropertyNode();
406
407   // copy properties which are READ/WRITEable - but not USERARCHIVEd or PRESERVEd
408   int checked  = SGPropertyNode::READ+SGPropertyNode::WRITE+
409                  SGPropertyNode::USERARCHIVE+SGPropertyNode::PRESERVE;
410   int expected = SGPropertyNode::READ+SGPropertyNode::WRITE;
411   if (!copyProperties(props, initial_state, expected, checked))
412     SG_LOG(SG_GENERAL, SG_ALERT, "Error saving initial state");
413     
414   // delete various properties from the initial state, since we want to
415   // preserve their values even if doing a restore
416   // => Properties should now use the PRESERVE flag to protect their values
417   // on sim-reset. Remove some specific properties for backward compatibility.
418   SGPropertyNode* sim = initial_state->getChild("sim");
419   SGPropertyNode* cameraGroupNode = sim->getNode("rendering/camera-group");
420   if (cameraGroupNode) {
421     cameraGroupNode->removeChild("camera");
422     cameraGroupNode->removeChild("gui");
423   }
424 }
425
426
427 // Restore the saved initial state, if any
428 void
429 FGGlobals::restoreInitialState ()
430 {
431     if ( initial_state == 0 ) {
432         SG_LOG(SG_GENERAL, SG_ALERT,
433                "No initial state available to restore!!!");
434         return;
435     }
436     // copy properties which are READ/WRITEable - but not USERARCHIVEd or PRESERVEd
437     int checked  = SGPropertyNode::READ+SGPropertyNode::WRITE+
438                    SGPropertyNode::USERARCHIVE+SGPropertyNode::PRESERVE;
439     int expected = SGPropertyNode::READ+SGPropertyNode::WRITE;
440     if ( copyProperties(initial_state, props, expected, checked)) {
441         SG_LOG( SG_GENERAL, SG_INFO, "Initial state restored successfully" );
442     } else {
443         SG_LOG( SG_GENERAL, SG_INFO,
444                 "Some errors restoring initial state (read-only props?)" );
445     }
446
447 }
448
449 // Load user settings from autosave.xml
450 void
451 FGGlobals::loadUserSettings(const SGPath& dataPath)
452 {
453     // remember that we have (tried) to load any existing autsave.xml
454     haveUserSettings = true;
455
456     SGPath autosaveFile = simgear::Dir(dataPath).file("autosave.xml");
457     SGPropertyNode autosave;
458     if (autosaveFile.exists()) {
459       SG_LOG(SG_INPUT, SG_INFO, "Reading user settings from " << autosaveFile.str());
460       try {
461           readProperties(autosaveFile.str(), &autosave, SGPropertyNode::USERARCHIVE);
462       } catch (sg_exception& e) {
463           SG_LOG(SG_INPUT, SG_WARN, "failed to read user settings:" << e.getMessage()
464             << "(from " << e.getOrigin() << ")");
465       }
466     }
467     copyProperties(&autosave, globals->get_props());
468 }
469
470 // Save user settings in autosave.xml
471 void
472 FGGlobals::saveUserSettings()
473 {
474     // only save settings when we have (tried) to load the previous
475     // settings (otherwise user data was lost)
476     if (!haveUserSettings)
477         return;
478
479     if (fgGetBool("/sim/startup/save-on-exit")) {
480       // don't save settings more than once on shutdown
481       haveUserSettings = false;
482
483       SGPath autosaveFile(fgGetString("/sim/fg-home"));
484       autosaveFile.append( "autosave.xml" );
485       autosaveFile.create_dir( 0700 );
486       SG_LOG(SG_IO, SG_INFO, "Saving user settings to " << autosaveFile.str());
487       try {
488         writeProperties(autosaveFile.str(), globals->get_props(), false, SGPropertyNode::USERARCHIVE);
489       } catch (const sg_exception &e) {
490         guiErrorMessage("Error writing autosave.xml: ", e);
491       }
492       SG_LOG(SG_INPUT, SG_DEBUG, "Finished Saving user settings");
493     }
494 }
495
496 FGViewer *
497 FGGlobals::get_current_view () const
498 {
499   return viewmgr->get_current_view();
500 }
501
502 long int FGGlobals::get_warp() const
503 {
504   return fgGetInt("/sim/time/warp");
505 }
506
507 void FGGlobals::set_warp( long int w )
508 {
509   fgSetInt("/sim/time/warp", w);
510 }
511
512 long int FGGlobals::get_warp_delta() const
513 {
514   return fgGetInt("/sim/time/warp-delta");
515 }
516
517 void FGGlobals::set_warp_delta( long int d )
518 {
519   fgSetInt("/sim/time/warp-delta", d);
520 }
521
522 void FGGlobals::set_current_panel( FGPanel *cp )
523 {
524   current_panel = cp;
525 // poke the renderer to rebuild the scene node as necessary
526   get_renderer()->panelChanged();
527 }
528     
529 // end of globals.cxx