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