2000-07-07 17:27:53 +00:00
// globals.cxx -- Global state that needs to be shared among the sim modules
//
// Written by Curtis Olson, started July 2000.
//
2004-11-19 22:10:41 +00:00
// Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt
2000-07-07 17:27:53 +00:00
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
2009-10-24 08:31:37 +00:00
// along with this program; if not, write to the Free Software Foundation,
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2000-07-07 17:27:53 +00:00
//
// $Id$
2017-03-21 20:43:42 +00:00
# include <config.h>
2000-07-07 17:27:53 +00:00
2011-10-16 17:35:40 +00:00
# include <algorithm>
2013-12-30 15:23:01 +00:00
# include <osgViewer/Viewer>
# include <osgDB/Registry>
2003-09-24 17:20:55 +00:00
# include <simgear/structure/commands.hxx>
2017-03-21 20:43:42 +00:00
# include <simgear/structure/exception.hxx>
2003-04-24 02:18:12 +00:00
# include <simgear/misc/sg_path.hxx>
2010-07-05 07:31:09 +00:00
# include <simgear/misc/sg_dir.hxx>
2007-10-20 08:36:21 +00:00
# include <simgear/timing/sg_time.hxx>
# include <simgear/ephemeris/ephemeris.hxx>
2008-07-31 12:04:32 +00:00
# include <simgear/structure/subsystem_mgr.hxx>
# include <simgear/structure/event_mgr.hxx>
2017-03-21 20:43:42 +00:00
2010-09-06 08:13:10 +00:00
# include <simgear/misc/ResourceManager.hxx>
2010-12-02 20:29:28 +00:00
# include <simgear/props/propertyObject.hxx>
2011-07-30 09:47:28 +00:00
# include <simgear/props/props_io.hxx>
2020-10-29 17:28:05 +00:00
# include <simgear/props/AtomicChangeListener.hxx>
2013-11-16 11:56:42 +00:00
# include <simgear/scene/model/modellib.hxx>
2014-05-31 17:04:56 +00:00
# include <simgear/package/Root.hxx>
2002-03-16 00:18:38 +00:00
2018-01-09 23:27:53 +00:00
# include <Add-ons/AddonResourceProvider.hxx>
2007-10-20 08:36:21 +00:00
# include <Aircraft/controls.hxx>
# include <Airports/runways.hxx>
# include <Autopilot/route_mgr.hxx>
2017-03-21 20:43:42 +00:00
# include <Navaids/navlist.hxx>
2011-12-29 23:39:08 +00:00
# include <GUI/gui.h>
2021-04-21 20:57:32 +00:00
# include <Main/sentryIntegration.hxx>
2017-03-21 20:43:42 +00:00
# include <Viewer/viewmgr.hxx>
2007-10-20 08:36:21 +00:00
# include <Scenery/scenery.hxx>
# include <Scenery/tilemgr.hxx>
2012-04-25 21:28:00 +00:00
# include <Viewer/renderer.hxx>
2016-12-10 23:24:09 +00:00
# include <GUI/FGFontCache.hxx>
2017-09-20 16:57:25 +00:00
# include <GUI/MessageBox.hxx>
2006-06-05 20:21:45 +00:00
2017-03-21 20:43:42 +00:00
# include <simgear/sound/soundmgr.hxx>
# include <simgear/scene/material/matlib.hxx>
2000-07-07 17:27:53 +00:00
# include "globals.hxx"
2012-04-21 18:17:42 +00:00
# include "locale.hxx"
2002-04-05 18:46:47 +00:00
2001-01-12 15:37:40 +00:00
# include "fg_props.hxx"
2002-08-25 20:56:16 +00:00
# include "fg_io.hxx"
2000-07-07 17:27:53 +00:00
2010-11-14 20:06:19 +00:00
class AircraftResourceProvider : public simgear : : ResourceProvider
2010-09-06 08:13:10 +00:00
{
public :
AircraftResourceProvider ( ) :
simgear : : ResourceProvider ( simgear : : ResourceManager : : PRIORITY_HIGH )
{
}
2015-09-28 20:12:46 +00:00
2010-09-06 08:13:10 +00:00
virtual SGPath resolve ( const std : : string & aResource , SGPath & ) const
{
string_list pieces ( sgPathBranchSplit ( aResource ) ) ;
if ( ( pieces . size ( ) < 3 ) | | ( pieces . front ( ) ! = " Aircraft " ) ) {
return SGPath ( ) ; // not an Aircraft path
}
2015-09-28 20:12:46 +00:00
2010-09-08 10:01:26 +00:00
// test against the aircraft-dir property
2010-09-06 08:13:10 +00:00
const char * aircraftDir = fgGetString ( " /sim/aircraft-dir " ) ;
string_list aircraftDirPieces ( sgPathBranchSplit ( aircraftDir ) ) ;
2010-11-14 20:06:19 +00:00
if ( ! aircraftDirPieces . empty ( ) & & ( aircraftDirPieces . back ( ) = = pieces [ 1 ] ) ) {
// current aircraft-dir matches resource aircraft
SGPath r ( aircraftDir ) ;
for ( unsigned int i = 2 ; i < pieces . size ( ) ; + + i ) {
r . append ( pieces [ i ] ) ;
}
2015-09-28 20:12:46 +00:00
2010-11-14 20:06:19 +00:00
if ( r . exists ( ) ) {
return r ;
Don't load resources for the current aircraft from several aircraft dirs
* If one has the same aircraft in several aircraft directories,
FlightGear should not mix resources from the various aircraft
directories. For instance, if one starts FG with:
--fg-aircraft=/my/personal/dir:/path/to/fgaddon/Aircraft
and one has in /my/personal/dir/ec130 a clone of the upstream
developer repo, FlightGear should use either the upstream version from
/my/personal/dir/ec130 or the FGAddon version from
/path/to/fgaddon/Aircraft/ec130, but not some strange, untested hybrid
of both.
* This commit makes sure that when the looked-up resource starts with
Aircraft/<ac>, where <ac> is the current aircraft name [last component
of aircraftDir = fgGetString("/sim/aircraft-dir")], then
AircraftResourceProvider::resolve() doesn't search other aircraft
directories if the resource isn't found under 'aircraftDir'.
* To reproduce the bug before this commit, you may add the following
code (there is nothing specific about the SenecaII here, it's just the
aircraft I used for testing):
var file_path = resolvepath("Aircraft/SenecaII/flo-test");
if (file_path != "")
gui.popupTip("flo-test found", 2);
else
gui.popupTip("flo-test not found", 2);
in a keyboard binding for the SenecaII (for instance; you may use the
F11 binding that otherwise only prints a short message). You should
add this to the SenecaII/SenecaII-base.xml file *that will be loaded
by FlightGear*, let's say the one under /my/personal/dir in the
example above (beware of the <path-cache> in autosave_X_Y.xml). Then,
by creating or removing a file named "flo-test" in the SenecaII
subdirectory of other aircraft dirs (for instance,
/path/to/fgaddon/Aircraft in the example above), you can see that the
behavior of the loaded aircraft is influenced by the contents of
unrelated versions of the same aircraft that might be present in other
aircraft dirs (e.g., loaded /my/personal/dir/SenecaII influenced by
/path/to/fgaddon/Aircraft/SenecaII).
* Aircrafts loading resources using paths relative to the current
aircraft directory (e.g., with 'resolvepath("flo-test")') are not
affected by this kind of problem, because this scheme is handled by
CurrentAircraftDirProvider, which does not exhibit this bug.
2015-09-25 22:04:28 +00:00
} else {
// Stop here, otherwise we could end up returning a resource that
// belongs to an unrelated version of the same aircraft (from a
// different aircraft directory).
return SGPath ( ) ;
2010-11-14 20:06:19 +00:00
}
2010-09-08 10:01:26 +00:00
}
2015-09-28 20:12:46 +00:00
2010-11-14 20:06:19 +00:00
// try each aircraft dir in turn
2010-09-08 10:01:26 +00:00
std : : string res ( aResource , 9 ) ; // resource path with 'Aircraft/' removed
2016-06-21 11:29:04 +00:00
const PathList & dirs ( globals - > get_aircraft_paths ( ) ) ;
PathList : : const_iterator it = dirs . begin ( ) ;
2010-09-08 10:01:26 +00:00
for ( ; it ! = dirs . end ( ) ; + + it ) {
2016-06-21 11:29:04 +00:00
SGPath p ( * it ) ;
p . append ( res ) ;
2010-09-08 10:01:26 +00:00
if ( p . exists ( ) ) {
return p ;
}
} // of aircraft path iteration
2015-09-28 20:12:46 +00:00
2010-09-08 10:01:26 +00:00
return SGPath ( ) ; // not found
2010-09-06 08:13:10 +00:00
}
} ;
2012-12-13 09:10:39 +00:00
class CurrentAircraftDirProvider : public simgear : : ResourceProvider
{
public :
CurrentAircraftDirProvider ( ) :
simgear : : ResourceProvider ( simgear : : ResourceManager : : PRIORITY_HIGH )
{
}
2015-09-28 20:12:46 +00:00
2012-12-13 09:10:39 +00:00
virtual SGPath resolve ( const std : : string & aResource , SGPath & ) const
{
2016-06-21 11:29:04 +00:00
SGPath p = SGPath : : fromUtf8 ( fgGetString ( " /sim/aircraft-dir " ) ) ;
2012-12-13 09:10:39 +00:00
p . append ( aResource ) ;
return p . exists ( ) ? p : SGPath ( ) ;
}
} ;
2001-01-19 22:57:24 +00:00
////////////////////////////////////////////////////////////////////////
// Implementation of FGGlobals.
////////////////////////////////////////////////////////////////////////
2000-07-07 17:27:53 +00:00
// global global :-)
2013-11-16 12:10:32 +00:00
FGGlobals * globals = NULL ;
2000-07-07 17:27:53 +00:00
// Constructor
FGGlobals : : FGGlobals ( ) :
2004-09-20 13:21:51 +00:00
renderer ( new FGRenderer ) ,
2019-06-13 14:14:06 +00:00
subsystem_mgr ( new SGSubsystemMgr ) ,
2003-09-24 17:20:55 +00:00
event_mgr ( new SGEventMgr ) ,
2002-12-11 21:07:30 +00:00
sim_time_sec ( 0.0 ) ,
fg_root ( " " ) ,
2012-08-23 20:52:30 +00:00
fg_home ( " " ) ,
2002-12-11 21:07:30 +00:00
time_params ( NULL ) ,
2013-11-24 19:06:28 +00:00
commands ( SGCommandMgr : : instance ( ) ) ,
2002-12-11 21:07:30 +00:00
channel_options_list ( NULL ) ,
2004-06-14 18:47:21 +00:00
initial_waypoints ( NULL ) ,
2011-12-29 23:39:08 +00:00
channellist ( NULL ) ,
2020-07-14 11:05:53 +00:00
haveUserSettings ( false )
2000-07-07 17:27:53 +00:00
{
2013-11-16 12:10:32 +00:00
SGPropertyNode * root = new SGPropertyNode ;
props = SGPropertyNode_ptr ( root ) ;
locale = new FGLocale ( props ) ;
2015-09-28 20:12:46 +00:00
2018-01-09 23:27:53 +00:00
auto resMgr = simgear : : ResourceManager : : instance ( ) ;
resMgr - > addProvider ( new AircraftResourceProvider ( ) ) ;
resMgr - > addProvider ( new CurrentAircraftDirProvider ( ) ) ;
resMgr - > addProvider ( new flightgear : : addons : : ResourceProvider ( ) ) ;
2013-11-16 12:10:32 +00:00
initProperties ( ) ;
}
void FGGlobals : : initProperties ( )
{
simgear : : PropertyObjectBase : : setDefaultRoot ( props ) ;
2015-09-28 20:12:46 +00:00
2013-11-16 12:10:32 +00:00
positionLon = props - > getNode ( " position/longitude-deg " , true ) ;
positionLat = props - > getNode ( " position/latitude-deg " , true ) ;
positionAlt = props - > getNode ( " position/altitude-ft " , true ) ;
2015-09-28 20:12:46 +00:00
2013-11-16 12:10:32 +00:00
viewLon = props - > getNode ( " sim/current-view/viewer-lon-deg " , true ) ;
viewLat = props - > getNode ( " sim/current-view/viewer-lat-deg " , true ) ;
viewAlt = props - > getNode ( " sim/current-view/viewer-elev-ft " , true ) ;
2015-09-28 20:12:46 +00:00
2021-04-16 20:02:11 +00:00
referenceOffsetX = props - > getNode ( " sim/model/reference-offset-x " , true ) ;
referenceOffsetY = props - > getNode ( " sim/model/reference-offset-y " , true ) ;
referenceOffsetZ = props - > getNode ( " sim/model/reference-offset-z " , true ) ;
2013-11-16 12:10:32 +00:00
orientPitch = props - > getNode ( " orientation/pitch-deg " , true ) ;
orientHeading = props - > getNode ( " orientation/heading-deg " , true ) ;
orientRoll = props - > getNode ( " orientation/roll-deg " , true ) ;
2000-07-07 17:27:53 +00:00
}
// Destructor
2015-09-28 20:12:46 +00:00
FGGlobals : : ~ FGGlobals ( )
2011-12-29 23:39:08 +00:00
{
// save user settings (unless already saved)
saveUserSettings ( ) ;
2015-12-19 06:00:37 +00:00
// stop OSG threading first, to avoid thread races while we tear down
// scene-graph pieces
2020-08-23 16:41:09 +00:00
// there are some scenarios where renderer is already gone.
CompositeViewer: Support for multiple view windows using osgViewer::CompositeViewer.
Overview:
Previously Flightgear always used a single osgViewer::Viewer(), which
inherits from both osgViewer::ViewerBase and osgViewer::View, giving a
single view window.
If CompositeViewer is enabled, we instead use a osgViewer::CompositeViewer
which contains a list of osgViewer::View's. Each of these View's can have
its own eye position, so we can have multiple different views of the same
scene.
Enable at runtime with: --composite-viewer=1
Changes to allow use of osgViewer::CompositeViewer:
Previously FGRenderer had this method:
osgViewer::Viewer* getViewer();
This has been replaced by these two new methods:
osgViewer::ViewerBase* getViewerBase();
osgViewer::View* getView();
If CompositeViewer is not enabled (the default), the actual runtime state
is unchanged, and getViewerBase() and getView() both return a pointer to
the singleton osgViewer::Viewer() object.
If CompositeViewer is enabled, getViewerBase() returns a pointer to a
singleton osgViewer::CompositeViewer object, and getView() returns a
pointer to the first osgViewer::View in the osgViewer::CompositeViewer's
list.
The other significant change to FGRenderer() is the new method:
osg::FrameStamp* getFrameStamp()
If CompositeViewer is not enabled, this simply returns
getView()->getFrameStamp(). If CompositeViewer is enabled it returns
getViewerBase()->getFrameStamp(). It is important that code that previously
called getView()->getFrameStamp() is changed to use the new method, because
when CompositeViewer is enabled individual osgViewer::View's appear to
return an osg::FrameStamp with zero frame number).
All code that uses FGRenderer has been patched up to use the new methods so
that things work as before regardless of whether CompositeViewer is enabled
or not.
We make FGRenderer::update() call SviewUpdate() which updates any extra
views.
Extra view windows:
If CompositeViewer is enabled, one can create top-level extra view windows
by calling SviewCreate(). See src/Viewer/sview.hxx for details.
Each extra view window has its own simgear::compositor::Compositor
instance.
Currently SviewCreate() can create extra view windows that clone the
current view, or view from one point to another (e.g. from one multiplayer
aircraft to the user's aircradt) or keep two aircraft in view, one at a
fixed distance in the foreground.
SviewCreate() can be called from nasal via new nasal commands "view-clone",
"view-last-pair", "view-last-pair-double" and "view-push". Associated
changes to fgdata gives access to these via the View menu. The "view-push"
command tags the current view for later use by "view-last-pair" and
"view-last-pair-double".
Extra view windows created by SviewCreate() use a new view system called
Sview, which allows views to be constructed at runtime instead of being
hard-coded in *-set.xml files. This is work in progress and views aren't
all fully implemented. For example Pilot view gets things slightly wrong
with large roll values, Tower View AGL is not implemented, and we don't
implement damping. See top of src/Viewer/sview.cxx for an overview.
OpenSceneGraph-3.4 issues:
OSG-3.4's event handling seems to be incorrect with CompositeViewer -
events get sent for the wrong window which causes issues with resize and
closing. It doesn't seem to be possible to work around this, so closing
extra view windows can end up closing the main window for example.
OSG-3.6 seems to fix the problems.
We warn if CompositeViewer is enabled and OpenSceneGraph is 3.4.
2020-11-16 18:43:46 +00:00
osg : : ref_ptr < osgViewer : : ViewerBase > vb ;
2020-08-23 16:41:09 +00:00
if ( renderer ) {
CompositeViewer: Support for multiple view windows using osgViewer::CompositeViewer.
Overview:
Previously Flightgear always used a single osgViewer::Viewer(), which
inherits from both osgViewer::ViewerBase and osgViewer::View, giving a
single view window.
If CompositeViewer is enabled, we instead use a osgViewer::CompositeViewer
which contains a list of osgViewer::View's. Each of these View's can have
its own eye position, so we can have multiple different views of the same
scene.
Enable at runtime with: --composite-viewer=1
Changes to allow use of osgViewer::CompositeViewer:
Previously FGRenderer had this method:
osgViewer::Viewer* getViewer();
This has been replaced by these two new methods:
osgViewer::ViewerBase* getViewerBase();
osgViewer::View* getView();
If CompositeViewer is not enabled (the default), the actual runtime state
is unchanged, and getViewerBase() and getView() both return a pointer to
the singleton osgViewer::Viewer() object.
If CompositeViewer is enabled, getViewerBase() returns a pointer to a
singleton osgViewer::CompositeViewer object, and getView() returns a
pointer to the first osgViewer::View in the osgViewer::CompositeViewer's
list.
The other significant change to FGRenderer() is the new method:
osg::FrameStamp* getFrameStamp()
If CompositeViewer is not enabled, this simply returns
getView()->getFrameStamp(). If CompositeViewer is enabled it returns
getViewerBase()->getFrameStamp(). It is important that code that previously
called getView()->getFrameStamp() is changed to use the new method, because
when CompositeViewer is enabled individual osgViewer::View's appear to
return an osg::FrameStamp with zero frame number).
All code that uses FGRenderer has been patched up to use the new methods so
that things work as before regardless of whether CompositeViewer is enabled
or not.
We make FGRenderer::update() call SviewUpdate() which updates any extra
views.
Extra view windows:
If CompositeViewer is enabled, one can create top-level extra view windows
by calling SviewCreate(). See src/Viewer/sview.hxx for details.
Each extra view window has its own simgear::compositor::Compositor
instance.
Currently SviewCreate() can create extra view windows that clone the
current view, or view from one point to another (e.g. from one multiplayer
aircraft to the user's aircradt) or keep two aircraft in view, one at a
fixed distance in the foreground.
SviewCreate() can be called from nasal via new nasal commands "view-clone",
"view-last-pair", "view-last-pair-double" and "view-push". Associated
changes to fgdata gives access to these via the View menu. The "view-push"
command tags the current view for later use by "view-last-pair" and
"view-last-pair-double".
Extra view windows created by SviewCreate() use a new view system called
Sview, which allows views to be constructed at runtime instead of being
hard-coded in *-set.xml files. This is work in progress and views aren't
all fully implemented. For example Pilot view gets things slightly wrong
with large roll values, Tower View AGL is not implemented, and we don't
implement damping. See top of src/Viewer/sview.cxx for an overview.
OpenSceneGraph-3.4 issues:
OSG-3.4's event handling seems to be incorrect with CompositeViewer -
events get sent for the wrong window which causes issues with resize and
closing. It doesn't seem to be possible to work around this, so closing
extra view windows can end up closing the main window for example.
OSG-3.6 seems to fix the problems.
We warn if CompositeViewer is enabled and OpenSceneGraph is 3.4.
2020-11-16 18:43:46 +00:00
vb = renderer - > getViewerBase ( ) ;
if ( vb ) {
2020-08-23 16:41:09 +00:00
// https://code.google.com/p/flightgear-bugs/issues/detail?id=1291
// explicitly stop trheading before we delete the renderer or
// viewMgr (which ultimately holds refs to the CameraGroup, and
// GraphicsContext)
CompositeViewer: Support for multiple view windows using osgViewer::CompositeViewer.
Overview:
Previously Flightgear always used a single osgViewer::Viewer(), which
inherits from both osgViewer::ViewerBase and osgViewer::View, giving a
single view window.
If CompositeViewer is enabled, we instead use a osgViewer::CompositeViewer
which contains a list of osgViewer::View's. Each of these View's can have
its own eye position, so we can have multiple different views of the same
scene.
Enable at runtime with: --composite-viewer=1
Changes to allow use of osgViewer::CompositeViewer:
Previously FGRenderer had this method:
osgViewer::Viewer* getViewer();
This has been replaced by these two new methods:
osgViewer::ViewerBase* getViewerBase();
osgViewer::View* getView();
If CompositeViewer is not enabled (the default), the actual runtime state
is unchanged, and getViewerBase() and getView() both return a pointer to
the singleton osgViewer::Viewer() object.
If CompositeViewer is enabled, getViewerBase() returns a pointer to a
singleton osgViewer::CompositeViewer object, and getView() returns a
pointer to the first osgViewer::View in the osgViewer::CompositeViewer's
list.
The other significant change to FGRenderer() is the new method:
osg::FrameStamp* getFrameStamp()
If CompositeViewer is not enabled, this simply returns
getView()->getFrameStamp(). If CompositeViewer is enabled it returns
getViewerBase()->getFrameStamp(). It is important that code that previously
called getView()->getFrameStamp() is changed to use the new method, because
when CompositeViewer is enabled individual osgViewer::View's appear to
return an osg::FrameStamp with zero frame number).
All code that uses FGRenderer has been patched up to use the new methods so
that things work as before regardless of whether CompositeViewer is enabled
or not.
We make FGRenderer::update() call SviewUpdate() which updates any extra
views.
Extra view windows:
If CompositeViewer is enabled, one can create top-level extra view windows
by calling SviewCreate(). See src/Viewer/sview.hxx for details.
Each extra view window has its own simgear::compositor::Compositor
instance.
Currently SviewCreate() can create extra view windows that clone the
current view, or view from one point to another (e.g. from one multiplayer
aircraft to the user's aircradt) or keep two aircraft in view, one at a
fixed distance in the foreground.
SviewCreate() can be called from nasal via new nasal commands "view-clone",
"view-last-pair", "view-last-pair-double" and "view-push". Associated
changes to fgdata gives access to these via the View menu. The "view-push"
command tags the current view for later use by "view-last-pair" and
"view-last-pair-double".
Extra view windows created by SviewCreate() use a new view system called
Sview, which allows views to be constructed at runtime instead of being
hard-coded in *-set.xml files. This is work in progress and views aren't
all fully implemented. For example Pilot view gets things slightly wrong
with large roll values, Tower View AGL is not implemented, and we don't
implement damping. See top of src/Viewer/sview.cxx for an overview.
OpenSceneGraph-3.4 issues:
OSG-3.4's event handling seems to be incorrect with CompositeViewer -
events get sent for the wrong window which causes issues with resize and
closing. It doesn't seem to be possible to work around this, so closing
extra view windows can end up closing the main window for example.
OSG-3.6 seems to fix the problems.
We warn if CompositeViewer is enabled and OpenSceneGraph is 3.4.
2020-11-16 18:43:46 +00:00
vb - > stopThreading ( ) ;
2020-08-23 16:41:09 +00:00
}
2015-12-19 06:00:37 +00:00
}
2020-08-23 16:41:09 +00:00
2013-11-16 12:10:32 +00:00
subsystem_mgr - > shutdown ( ) ;
2015-09-28 20:12:46 +00:00
subsystem_mgr - > unbind ( ) ;
2013-11-16 14:25:12 +00:00
2013-12-30 15:23:01 +00:00
// don't cancel the pager until after shutdown, since AIModels (and
// potentially others) can queue delete requests on the pager.
CompositeViewer: Support for multiple view windows using osgViewer::CompositeViewer.
Overview:
Previously Flightgear always used a single osgViewer::Viewer(), which
inherits from both osgViewer::ViewerBase and osgViewer::View, giving a
single view window.
If CompositeViewer is enabled, we instead use a osgViewer::CompositeViewer
which contains a list of osgViewer::View's. Each of these View's can have
its own eye position, so we can have multiple different views of the same
scene.
Enable at runtime with: --composite-viewer=1
Changes to allow use of osgViewer::CompositeViewer:
Previously FGRenderer had this method:
osgViewer::Viewer* getViewer();
This has been replaced by these two new methods:
osgViewer::ViewerBase* getViewerBase();
osgViewer::View* getView();
If CompositeViewer is not enabled (the default), the actual runtime state
is unchanged, and getViewerBase() and getView() both return a pointer to
the singleton osgViewer::Viewer() object.
If CompositeViewer is enabled, getViewerBase() returns a pointer to a
singleton osgViewer::CompositeViewer object, and getView() returns a
pointer to the first osgViewer::View in the osgViewer::CompositeViewer's
list.
The other significant change to FGRenderer() is the new method:
osg::FrameStamp* getFrameStamp()
If CompositeViewer is not enabled, this simply returns
getView()->getFrameStamp(). If CompositeViewer is enabled it returns
getViewerBase()->getFrameStamp(). It is important that code that previously
called getView()->getFrameStamp() is changed to use the new method, because
when CompositeViewer is enabled individual osgViewer::View's appear to
return an osg::FrameStamp with zero frame number).
All code that uses FGRenderer has been patched up to use the new methods so
that things work as before regardless of whether CompositeViewer is enabled
or not.
We make FGRenderer::update() call SviewUpdate() which updates any extra
views.
Extra view windows:
If CompositeViewer is enabled, one can create top-level extra view windows
by calling SviewCreate(). See src/Viewer/sview.hxx for details.
Each extra view window has its own simgear::compositor::Compositor
instance.
Currently SviewCreate() can create extra view windows that clone the
current view, or view from one point to another (e.g. from one multiplayer
aircraft to the user's aircradt) or keep two aircraft in view, one at a
fixed distance in the foreground.
SviewCreate() can be called from nasal via new nasal commands "view-clone",
"view-last-pair", "view-last-pair-double" and "view-push". Associated
changes to fgdata gives access to these via the View menu. The "view-push"
command tags the current view for later use by "view-last-pair" and
"view-last-pair-double".
Extra view windows created by SviewCreate() use a new view system called
Sview, which allows views to be constructed at runtime instead of being
hard-coded in *-set.xml files. This is work in progress and views aren't
all fully implemented. For example Pilot view gets things slightly wrong
with large roll values, Tower View AGL is not implemented, and we don't
implement damping. See top of src/Viewer/sview.cxx for an overview.
OpenSceneGraph-3.4 issues:
OSG-3.4's event handling seems to be incorrect with CompositeViewer -
events get sent for the wrong window which causes issues with resize and
closing. It doesn't seem to be possible to work around this, so closing
extra view windows can end up closing the main window for example.
OSG-3.6 seems to fix the problems.
We warn if CompositeViewer is enabled and OpenSceneGraph is 3.4.
2020-11-16 18:43:46 +00:00
osgViewer : : View * v = renderer - > getView ( ) ;
if ( v & & v - > getDatabasePager ( ) ) {
v - > getDatabasePager ( ) - > cancel ( ) ;
v - > getDatabasePager ( ) - > clear ( ) ;
2013-12-30 17:16:53 +00:00
}
2015-09-28 20:12:46 +00:00
2013-12-30 15:23:01 +00:00
osgDB : : Registry : : instance ( ) - > clearObjectCache ( ) ;
2020-09-30 13:39:41 +00:00
if ( subsystem_mgr - > get_subsystem ( FGScenery : : staticSubsystemClassId ( ) ) ) {
subsystem_mgr - > remove ( FGScenery : : staticSubsystemClassId ( ) ) ;
}
2015-09-28 20:12:46 +00:00
2013-11-16 12:10:32 +00:00
// renderer touches subsystems during its destruction
2020-08-23 16:41:09 +00:00
set_renderer ( nullptr ) ;
2015-09-28 20:12:46 +00:00
2016-12-10 23:24:09 +00:00
FGFontCache : : shutdown ( ) ;
fgCancelSnapShot ( ) ;
2008-03-22 09:31:06 +00:00
delete subsystem_mgr ;
2020-08-23 16:41:09 +00:00
subsystem_mgr = nullptr ; // important so ::get_subsystem returns NULL
CompositeViewer: Support for multiple view windows using osgViewer::CompositeViewer.
Overview:
Previously Flightgear always used a single osgViewer::Viewer(), which
inherits from both osgViewer::ViewerBase and osgViewer::View, giving a
single view window.
If CompositeViewer is enabled, we instead use a osgViewer::CompositeViewer
which contains a list of osgViewer::View's. Each of these View's can have
its own eye position, so we can have multiple different views of the same
scene.
Enable at runtime with: --composite-viewer=1
Changes to allow use of osgViewer::CompositeViewer:
Previously FGRenderer had this method:
osgViewer::Viewer* getViewer();
This has been replaced by these two new methods:
osgViewer::ViewerBase* getViewerBase();
osgViewer::View* getView();
If CompositeViewer is not enabled (the default), the actual runtime state
is unchanged, and getViewerBase() and getView() both return a pointer to
the singleton osgViewer::Viewer() object.
If CompositeViewer is enabled, getViewerBase() returns a pointer to a
singleton osgViewer::CompositeViewer object, and getView() returns a
pointer to the first osgViewer::View in the osgViewer::CompositeViewer's
list.
The other significant change to FGRenderer() is the new method:
osg::FrameStamp* getFrameStamp()
If CompositeViewer is not enabled, this simply returns
getView()->getFrameStamp(). If CompositeViewer is enabled it returns
getViewerBase()->getFrameStamp(). It is important that code that previously
called getView()->getFrameStamp() is changed to use the new method, because
when CompositeViewer is enabled individual osgViewer::View's appear to
return an osg::FrameStamp with zero frame number).
All code that uses FGRenderer has been patched up to use the new methods so
that things work as before regardless of whether CompositeViewer is enabled
or not.
We make FGRenderer::update() call SviewUpdate() which updates any extra
views.
Extra view windows:
If CompositeViewer is enabled, one can create top-level extra view windows
by calling SviewCreate(). See src/Viewer/sview.hxx for details.
Each extra view window has its own simgear::compositor::Compositor
instance.
Currently SviewCreate() can create extra view windows that clone the
current view, or view from one point to another (e.g. from one multiplayer
aircraft to the user's aircradt) or keep two aircraft in view, one at a
fixed distance in the foreground.
SviewCreate() can be called from nasal via new nasal commands "view-clone",
"view-last-pair", "view-last-pair-double" and "view-push". Associated
changes to fgdata gives access to these via the View menu. The "view-push"
command tags the current view for later use by "view-last-pair" and
"view-last-pair-double".
Extra view windows created by SviewCreate() use a new view system called
Sview, which allows views to be constructed at runtime instead of being
hard-coded in *-set.xml files. This is work in progress and views aren't
all fully implemented. For example Pilot view gets things slightly wrong
with large roll values, Tower View AGL is not implemented, and we don't
implement damping. See top of src/Viewer/sview.cxx for an overview.
OpenSceneGraph-3.4 issues:
OSG-3.4's event handling seems to be incorrect with CompositeViewer -
events get sent for the wrong window which causes issues with resize and
closing. It doesn't seem to be possible to work around this, so closing
extra view windows can end up closing the main window for example.
OSG-3.6 seems to fix the problems.
We warn if CompositeViewer is enabled and OpenSceneGraph is 3.4.
2020-11-16 18:43:46 +00:00
vb = nullptr ;
2013-11-21 17:38:22 +00:00
set_matlib ( NULL ) ;
2015-12-10 22:30:26 +00:00
2017-03-21 20:43:42 +00:00
delete time_params ;
2008-03-22 09:31:06 +00:00
delete channel_options_list ;
delete initial_waypoints ;
2007-10-20 08:36:21 +00:00
delete channellist ;
2012-04-21 18:17:42 +00:00
2020-09-14 10:04:53 +00:00
// delete commands before we release the property root
// this avoids crash where commands might be storing a propery
// ref/pointer.
// see https://sentry.io/organizations/flightgear/issues/1890563449
delete commands ;
commands = nullptr ;
2013-11-16 11:56:42 +00:00
simgear : : PropertyObjectBase : : setDefaultRoot ( NULL ) ;
simgear : : SGModelLib : : resetPropertyRoot ( ) ;
2012-04-21 18:17:42 +00:00
delete locale ;
locale = NULL ;
2015-09-28 20:12:46 +00:00
2013-12-04 09:11:26 +00:00
cleanupListeners ( ) ;
2015-09-28 20:12:46 +00:00
2013-11-16 12:10:32 +00:00
props . clear ( ) ;
2015-09-28 20:12:46 +00:00
2018-05-28 20:42:56 +00:00
delete simgear : : ResourceManager : : instance ( ) ;
2001-01-19 22:57:24 +00:00
}
2003-04-24 02:18:12 +00:00
// set the fg_root path
2016-06-21 11:29:04 +00:00
void FGGlobals : : set_fg_root ( const SGPath & root ) {
2012-01-12 21:06:40 +00:00
SGPath tmp ( root ) ;
fg_root = tmp . realpath ( ) ;
2008-06-16 07:46:55 +00:00
2003-04-24 02:18:12 +00:00
// append /data to root if it exists
tmp . append ( " data " ) ;
tmp . append ( " version " ) ;
2011-11-19 20:25:51 +00:00
if ( tmp . exists ( ) ) {
2016-06-21 11:29:04 +00:00
fgGetNode ( " BAD_FG_ROOT " , true ) - > setStringValue ( fg_root . utf8Str ( ) ) ;
fg_root . append ( " data " ) ;
fgGetNode ( " GOOD_FG_ROOT " , true ) - > setStringValue ( fg_root . utf8Str ( ) ) ;
2009-01-30 17:55:14 +00:00
SG_LOG ( SG_GENERAL , SG_ALERT , " *** \n *** \n *** Warning: changing bad FG_ROOT/--fg-root to ' "
< < fg_root < < " ' \n *** \n *** " ) ;
2006-01-09 03:47:33 +00:00
}
2015-03-13 18:11:29 +00:00
// deliberately not a tied property, for fgValidatePath security
// write-protect to avoid accidents
2008-06-16 07:46:55 +00:00
SGPropertyNode * n = fgGetNode ( " /sim " , true ) ;
2014-03-05 23:41:41 +00:00
n - > removeChild ( " fg-root " , 0 ) ;
2008-06-16 07:46:55 +00:00
n = n - > getChild ( " fg-root " , 0 , true ) ;
2016-06-21 11:29:04 +00:00
n - > setStringValue ( fg_root . utf8Str ( ) ) ;
2008-06-16 07:46:55 +00:00
n - > setAttribute ( SGPropertyNode : : WRITE , false ) ;
2015-09-28 20:12:46 +00:00
simgear : : ResourceManager : : instance ( ) - > addBasePath ( fg_root ,
2010-09-06 08:13:10 +00:00
simgear : : ResourceManager : : PRIORITY_DEFAULT ) ;
2003-04-24 02:18:12 +00:00
}
2012-08-23 20:52:30 +00:00
// set the fg_home path
2016-06-21 11:29:04 +00:00
void FGGlobals : : set_fg_home ( const SGPath & home )
{
fg_home = home . realpath ( ) ;
2013-06-27 08:37:53 +00:00
}
2018-11-24 19:19:02 +00:00
void FGGlobals : : set_texture_cache_dir ( const SGPath & textureCache )
{
texture_cache_dir = textureCache . realpath ( ) ;
}
2013-06-27 08:37:53 +00:00
PathList FGGlobals : : get_data_paths ( ) const
{
PathList r ( additional_data_paths ) ;
2016-06-21 11:29:04 +00:00
r . push_back ( fg_root ) ;
2020-06-30 16:08:29 +00:00
r . insert ( r . end ( ) , _dataPathsAfterFGRoot . begin ( ) , _dataPathsAfterFGRoot . end ( ) ) ;
2013-06-27 08:37:53 +00:00
return r ;
}
PathList FGGlobals : : get_data_paths ( const std : : string & suffix ) const
{
PathList r ;
2019-02-06 13:08:49 +00:00
for ( SGPath p : get_data_paths ( ) ) {
2013-06-27 08:37:53 +00:00
p . append ( suffix ) ;
if ( p . exists ( ) ) {
r . push_back ( p ) ;
}
}
return r ;
}
2020-06-30 16:08:29 +00:00
void FGGlobals : : append_data_path ( const SGPath & path , bool afterFGRoot )
2013-06-27 08:37:53 +00:00
{
if ( ! path . exists ( ) ) {
SG_LOG ( SG_GENERAL , SG_WARN , " adding non-existant data path: " < < path ) ;
}
2015-09-28 20:12:46 +00:00
2020-06-30 16:08:29 +00:00
using RM = simgear : : ResourceManager ;
auto resManager = RM : : instance ( ) ;
if ( afterFGRoot ) {
_dataPathsAfterFGRoot . push_back ( path ) ;
// after FG_ROOT
resManager - > addBasePath ( path , static_cast < RM : : Priority > ( RM : : PRIORITY_DEFAULT - 10 ) ) ;
} else {
additional_data_paths . push_back ( path ) ;
// after NORMAL prioirty, but ahead of FG_ROOT
resManager - > addBasePath ( path , static_cast < RM : : Priority > ( RM : : PRIORITY_DEFAULT + 10 ) ) ;
}
2013-06-27 08:37:53 +00:00
}
2020-07-03 11:34:37 +00:00
SGPath FGGlobals : : findDataPath ( const std : : string & pathSuffix ) const
2013-06-27 08:37:53 +00:00
{
2019-02-06 13:08:49 +00:00
for ( SGPath p : additional_data_paths ) {
2013-06-27 08:37:53 +00:00
p . append ( pathSuffix ) ;
if ( p . exists ( ) ) {
return p ;
}
}
2015-09-28 20:12:46 +00:00
2013-06-27 08:37:53 +00:00
SGPath rootPath ( fg_root ) ;
rootPath . append ( pathSuffix ) ;
if ( rootPath . exists ( ) ) {
return rootPath ;
}
2015-09-28 20:12:46 +00:00
2020-06-30 16:08:29 +00:00
for ( SGPath p : _dataPathsAfterFGRoot ) {
p . append ( pathSuffix ) ;
if ( p . exists ( ) ) {
return p ;
}
}
return SGPath { } ;
2012-08-23 20:52:30 +00:00
}
2016-10-18 21:29:44 +00:00
void FGGlobals : : append_fg_scenery ( const PathList & paths )
2016-06-21 11:29:04 +00:00
{
2016-11-19 13:19:19 +00:00
for ( const SGPath & path : paths ) {
2016-06-21 11:29:04 +00:00
append_fg_scenery ( path ) ;
}
}
2016-10-18 21:29:44 +00:00
void FGGlobals : : append_fg_scenery ( const SGPath & path )
2010-07-05 07:31:09 +00:00
{
2011-05-24 23:03:51 +00:00
SGPropertyNode * sim = fgGetNode ( " /sim " , true ) ;
2011-10-16 17:35:40 +00:00
2016-06-21 11:29:04 +00:00
// find first unused fg-scenery property in /sim
2011-10-16 17:35:40 +00:00
int propIndex = 0 ;
while ( sim - > getChild ( " fg-scenery " , propIndex ) ! = NULL ) {
2016-06-21 11:29:04 +00:00
+ + propIndex ;
2011-10-16 17:35:40 +00:00
}
2015-09-28 20:12:46 +00:00
2016-06-21 11:29:04 +00:00
SGPath abspath ( path . realpath ( ) ) ;
if ( ! abspath . exists ( ) ) {
SG_LOG ( SG_GENERAL , SG_WARN , " scenery path not found: " < < abspath ) ;
return ;
}
2015-09-28 20:12:46 +00:00
2016-06-21 11:29:04 +00:00
// check for duplicates
PathList : : const_iterator ex = std : : find ( fg_scenery . begin ( ) , fg_scenery . end ( ) , abspath ) ;
if ( ex ! = fg_scenery . end ( ) ) {
SG_LOG ( SG_GENERAL , SG_INFO , " skipping duplicate add of scenery path: " < < abspath ) ;
return ;
}
2015-09-28 20:12:46 +00:00
2016-06-21 11:29:04 +00:00
// tell the ResouceManager about the scenery path
// needed to load Models from this scenery path
2020-03-12 14:23:44 +00:00
simgear : : ResourceManager : : instance ( ) - > addBasePath ( abspath , simgear : : ResourceManager : : PRIORITY_DEFAULT ) ;
2016-06-21 11:29:04 +00:00
fg_scenery . push_back ( abspath ) ;
2016-10-18 21:29:44 +00:00
extra_read_allowed_paths . push_back ( abspath ) ;
2015-09-28 20:12:46 +00:00
2016-06-21 11:29:04 +00:00
// make scenery dirs available to Nasal
SGPropertyNode * n = sim - > getChild ( " fg-scenery " , propIndex + + , true ) ;
n - > setStringValue ( abspath . utf8Str ( ) ) ;
n - > setAttribute ( SGPropertyNode : : WRITE , false ) ;
2017-02-27 23:15:07 +00:00
2016-06-21 11:29:04 +00:00
// temporary fix so these values survive reset
n - > setAttribute ( SGPropertyNode : : PRESERVE , true ) ;
2004-06-07 09:52:11 +00:00
}
2016-10-09 14:21:09 +00:00
void FGGlobals : : append_read_allowed_paths ( const SGPath & path )
{
SGPath abspath ( path . realpath ( ) ) ;
if ( ! abspath . exists ( ) ) {
2020-04-10 18:12:10 +00:00
SG_LOG ( SG_IO , SG_DEBUG , " read-allowed path not found: " < < abspath ) ;
2016-10-09 14:21:09 +00:00
return ;
}
extra_read_allowed_paths . push_back ( abspath ) ;
}
2014-02-22 16:13:36 +00:00
void FGGlobals : : clear_fg_scenery ( )
{
fg_scenery . clear ( ) ;
2015-11-27 23:25:53 +00:00
fgGetNode ( " /sim " , true ) - > removeChildren ( " fg-scenery " ) ;
2014-02-22 16:13:36 +00:00
}
Add FGGlobals::get/set_download_dir(), unify TerraSync and download dirs setup
Similar to the existing FGGlobals::get/set_terrasync_dir(), add
FGGlobals::get_download_dir() and FGGlobals::set_download_dir() methods,
and of course the corresponding FGGlobals::download_dir public member
variable. FGGlobals::set_download_dir() stores the realpath() of the
given directory, including into the /sim/paths/download-dir property,
which is marked as read-only just as /sim/terrasync/scenery-dir already
is.
Handle the setup of the TerraSync and download dirs all in the same
place (Options::processOptions()), since most of the work is already
done there. This allows one to get rid of fgOptTerrasyncDir() and
fgOptDownloadDir(), and to make it easier to see that
globals->set_terrasync_dir() (resp. globals->set_download_dir()) is
called on the correct SGPath, regardless of whether --terrasync-dir
(resp. --download-dir) was passed.
Always create the TerraSync and download dirs when they don't already
exist, regardless of whether --terrasync-dir or --download-dir has been
given on the command line.
Add comments explaining how to avoid security pitfalls with download and
TerraSync dirs (cf. discussion around
<https://sourceforge.net/p/flightgear/mailman/message/35461636/>).
Adjust indentation where it was too broken, hampering readbility.
2016-10-31 20:48:32 +00:00
// The 'path' argument to this method must come from trustworthy code, because
// the method grants read permissions to Nasal code for many files beneath
// 'path'. In particular, don't call this method with a 'path' value taken
// from the property tree or any other Nasal-writable place.
void FGGlobals : : set_download_dir ( const SGPath & path )
{
SGPath abspath ( path . realpath ( ) ) ;
download_dir = abspath ;
append_read_allowed_paths ( abspath / " Aircraft " ) ;
append_read_allowed_paths ( abspath / " AI " ) ;
append_read_allowed_paths ( abspath / " Liveries " ) ;
// If in use, abspath / TerraSync will be added to 'extra_read_allowed_paths'
// by FGGlobals::append_fg_scenery(), as any scenery path.
SGPropertyNode * n = fgGetNode ( " /sim/paths/download-dir " , true ) ;
n - > setAttribute ( SGPropertyNode : : WRITE , true ) ;
n - > setStringValue ( abspath . utf8Str ( ) ) ;
n - > setAttribute ( SGPropertyNode : : WRITE , false ) ;
}
// The 'path' argument to this method must come from trustworthy code, because
// the method grants read permissions to Nasal code for all files beneath
// 'path'. In particular, don't call this method with a 'path' value taken
// from the property tree or any other Nasal-writable place.
2016-10-18 21:10:09 +00:00
void FGGlobals : : set_terrasync_dir ( const SGPath & path )
{
if ( terrasync_dir . realpath ( ) ! = SGPath ( fgGetString ( " /sim/terrasync/scenery-dir " ) ) . realpath ( ) ) {
// if they don't match, /sim/terrasync/scenery-dir has been set by something else
2020-09-06 13:57:58 +00:00
SG_LOG ( SG_GENERAL , SG_WARN , " /sim/terrasync/scenery-dir is no longer stored across runs: if you wish to keep using a non-standard Terrasync directory, use --terrasync-dir or the launcher's settings " ) ;
2016-10-18 21:10:09 +00:00
}
SGPath abspath ( path . realpath ( ) ) ;
terrasync_dir = abspath ;
// deliberately not a tied property, for fgValidatePath security
// write-protect to avoid accidents
SGPropertyNode * n = fgGetNode ( " /sim/terrasync/scenery-dir " , true ) ;
n - > setAttribute ( SGPropertyNode : : WRITE , true ) ;
n - > setStringValue ( abspath . utf8Str ( ) ) ;
n - > setAttribute ( SGPropertyNode : : WRITE , false ) ;
// don't add it to fg_scenery yet, as we want it ordered after explicit --fg-scenery
}
2014-06-11 17:19:41 +00:00
void FGGlobals : : set_catalog_aircraft_path ( const SGPath & path )
{
catalog_aircraft_dir = path ;
}
2016-06-21 11:29:04 +00:00
PathList FGGlobals : : get_aircraft_paths ( ) const
2014-06-11 17:19:41 +00:00
{
2016-06-21 11:29:04 +00:00
PathList r ;
2014-06-11 17:19:41 +00:00
if ( ! catalog_aircraft_dir . isNull ( ) ) {
2016-06-21 11:29:04 +00:00
r . push_back ( catalog_aircraft_dir ) ;
2014-06-11 17:19:41 +00:00
}
r . insert ( r . end ( ) , fg_aircraft_dirs . begin ( ) , fg_aircraft_dirs . end ( ) ) ;
return r ;
}
2016-06-21 11:29:04 +00:00
void FGGlobals : : append_aircraft_path ( const SGPath & path )
2010-07-18 12:21:30 +00:00
{
SGPath dirPath ( path ) ;
if ( ! dirPath . exists ( ) ) {
2020-09-06 13:57:58 +00:00
SG_LOG ( SG_GENERAL , SG_WARN , " aircraft path not found: " < < path ) ;
2010-07-18 12:21:30 +00:00
return ;
}
2014-06-22 13:36:25 +00:00
2014-01-15 17:10:12 +00:00
SGPath acSubdir ( dirPath ) ;
acSubdir . append ( " Aircraft " ) ;
2014-01-22 10:56:04 +00:00
if ( acSubdir . exists ( ) ) {
2014-06-22 13:36:25 +00:00
SG_LOG (
SG_GENERAL ,
SG_WARN ,
" Specified an aircraft-dir with an 'Aircraft' subdirectory: " < < dirPath
< < " , will instead use child directory: " < < acSubdir
) ;
2014-01-22 10:56:04 +00:00
dirPath = acSubdir ;
2014-01-15 17:10:12 +00:00
}
2014-06-22 13:36:25 +00:00
2016-06-21 11:29:04 +00:00
fg_aircraft_dirs . push_back ( dirPath . realpath ( ) ) ;
2016-10-18 21:35:10 +00:00
extra_read_allowed_paths . push_back ( dirPath . realpath ( ) ) ;
2010-07-18 12:21:30 +00:00
}
2016-06-21 11:29:04 +00:00
void FGGlobals : : append_aircraft_paths ( const PathList & paths )
2010-07-18 12:21:30 +00:00
{
for ( unsigned int p = 0 ; p < paths . size ( ) ; + + p ) {
append_aircraft_path ( paths [ p ] ) ;
}
}
SGPath FGGlobals : : resolve_aircraft_path ( const std : : string & branch ) const
{
2010-09-06 08:13:10 +00:00
return simgear : : ResourceManager : : instance ( ) - > findPath ( branch ) ;
2010-07-18 12:21:30 +00:00
}
SGPath FGGlobals : : resolve_maybe_aircraft_path ( const std : : string & branch ) const
{
2010-09-06 08:13:10 +00:00
return simgear : : ResourceManager : : instance ( ) - > findPath ( branch ) ;
2010-07-18 12:21:30 +00:00
}
2003-04-24 02:18:12 +00:00
2012-09-25 09:07:11 +00:00
SGPath FGGlobals : : resolve_resource_path ( const std : : string & branch ) const
2012-04-21 13:31:20 +00:00
{
return simgear : : ResourceManager : : instance ( )
- > findPath ( branch , SGPath ( fgGetString ( " /sim/aircraft-dir " ) ) ) ;
}
2004-09-20 13:21:51 +00:00
FGRenderer *
FGGlobals : : get_renderer ( ) const
{
return renderer ;
}
2013-11-16 12:10:32 +00:00
void FGGlobals : : set_renderer ( FGRenderer * render )
{
if ( render = = renderer ) {
return ;
}
2015-09-28 20:12:46 +00:00
2013-11-16 12:10:32 +00:00
delete renderer ;
renderer = render ;
}
2003-09-24 17:20:55 +00:00
SGSubsystemMgr *
2003-01-16 16:01:26 +00:00
FGGlobals : : get_subsystem_mgr ( ) const
{
return subsystem_mgr ;
}
2003-09-24 17:20:55 +00:00
SGSubsystem *
2015-12-10 21:05:54 +00:00
FGGlobals : : get_subsystem ( const char * name ) const
2003-01-16 16:01:26 +00:00
{
2013-11-16 14:25:12 +00:00
if ( ! subsystem_mgr ) {
return NULL ;
}
2015-09-28 20:12:46 +00:00
2003-01-16 16:01:26 +00:00
return subsystem_mgr - > get_subsystem ( name ) ;
}
void
FGGlobals : : add_subsystem ( const char * name ,
2003-09-24 17:20:55 +00:00
SGSubsystem * subsystem ,
SGSubsystemMgr : : GroupType type ,
2003-01-16 16:01:26 +00:00
double min_time_sec )
{
subsystem_mgr - > add ( name , subsystem , type , min_time_sec ) ;
}
2003-09-24 17:20:55 +00:00
SGEventMgr *
FGGlobals : : get_event_mgr ( ) const
{
return event_mgr ;
}
2012-09-17 23:58:27 +00:00
SGGeod
2011-08-20 06:54:47 +00:00
FGGlobals : : get_aircraft_position ( ) const
{
2012-09-19 17:17:44 +00:00
return SGGeod : : fromDegFt ( positionLon - > getDoubleValue ( ) ,
positionLat - > getDoubleValue ( ) ,
positionAlt - > getDoubleValue ( ) ) ;
2011-08-20 06:54:47 +00:00
}
2011-10-30 09:31:41 +00:00
SGVec3d
2012-09-25 09:06:30 +00:00
FGGlobals : : get_aircraft_position_cart ( ) const
2011-10-26 16:26:11 +00:00
{
return SGVec3d : : fromGeod ( get_aircraft_position ( ) ) ;
}
2011-08-20 06:54:47 +00:00
2012-09-19 17:17:44 +00:00
void FGGlobals : : get_aircraft_orientation ( double & heading , double & pitch , double & roll )
{
heading = orientHeading - > getDoubleValue ( ) ;
pitch = orientPitch - > getDoubleValue ( ) ;
roll = orientRoll - > getDoubleValue ( ) ;
}
2012-09-25 09:06:30 +00:00
SGGeod
FGGlobals : : get_view_position ( ) const
{
return SGGeod : : fromDegFt ( viewLon - > getDoubleValue ( ) ,
viewLat - > getDoubleValue ( ) ,
viewAlt - > getDoubleValue ( ) ) ;
}
SGVec3d
FGGlobals : : get_view_position_cart ( ) const
{
return SGVec3d : : fromGeod ( get_view_position ( ) ) ;
}
2021-04-16 20:02:11 +00:00
SGVec3d FGGlobals : : get_ownship_reference_position_cart ( ) const
{
SGVec3d pos = get_aircraft_position_cart ( ) ;
if ( referenceOffsetX )
pos [ 0 ] + = referenceOffsetX - > getDoubleValue ( ) ;
if ( referenceOffsetY )
pos [ 1 ] + = referenceOffsetY - > getDoubleValue ( ) ;
if ( referenceOffsetZ )
pos [ 2 ] + = referenceOffsetZ - > getDoubleValue ( ) ;
return pos ;
}
2003-09-24 17:20:55 +00:00
2013-11-16 12:10:32 +00:00
static void treeDumpRefCounts ( int depth , SGPropertyNode * nd )
{
for ( int i = 0 ; i < nd - > nChildren ( ) ; + + i ) {
SGPropertyNode * cp = nd - > getChild ( i ) ;
if ( SGReferenced : : count ( cp ) > 1 ) {
SG_LOG ( SG_GENERAL , SG_INFO , " \t " < < cp - > getPath ( ) < < " refcount: " < < SGReferenced : : count ( cp ) ) ;
}
2015-09-28 20:12:46 +00:00
2013-11-16 12:10:32 +00:00
treeDumpRefCounts ( depth + 1 , cp ) ;
}
}
2014-01-13 16:41:31 +00:00
static void treeClearAliases ( SGPropertyNode * nd )
{
if ( nd - > isAlias ( ) ) {
nd - > unalias ( ) ;
}
2015-09-28 20:12:46 +00:00
2014-01-13 16:41:31 +00:00
for ( int i = 0 ; i < nd - > nChildren ( ) ; + + i ) {
SGPropertyNode * cp = nd - > getChild ( i ) ;
treeClearAliases ( cp ) ;
}
}
2013-11-16 12:10:32 +00:00
void
FGGlobals : : resetPropertyRoot ( )
{
delete locale ;
2015-09-28 20:12:46 +00:00
2021-01-07 09:57:30 +00:00
// avoid a warning when we processOptions after reset
terrasync_dir = SGPath { } ;
2013-12-04 09:11:26 +00:00
cleanupListeners ( ) ;
2015-09-28 20:12:46 +00:00
2013-12-04 09:11:26 +00:00
// we don't strictly need to clear these (they will be reset when we
// initProperties again), but trying to reduce false-positives when dumping
// ref-counts.
positionLon . clear ( ) ;
positionLat . clear ( ) ;
positionAlt . clear ( ) ;
viewLon . clear ( ) ;
viewLat . clear ( ) ;
viewAlt . clear ( ) ;
orientPitch . clear ( ) ;
orientHeading . clear ( ) ;
orientRoll . clear ( ) ;
2015-09-28 20:12:46 +00:00
2014-01-13 16:41:31 +00:00
// clear aliases so ref-counts are accurate when dumped
treeClearAliases ( props ) ;
2015-09-28 20:12:46 +00:00
2013-11-16 12:10:32 +00:00
SG_LOG ( SG_GENERAL , SG_INFO , " root props refcount: " < < props . getNumRefs ( ) ) ;
treeDumpRefCounts ( 0 , props ) ;
2013-11-22 22:42:04 +00:00
2013-12-04 09:11:26 +00:00
//BaseStackSnapshot::dumpAll(std::cout);
2015-09-28 20:12:46 +00:00
2013-11-16 12:10:32 +00:00
props = new SGPropertyNode ;
initProperties ( ) ;
locale = new FGLocale ( props ) ;
2015-09-28 20:12:46 +00:00
2013-11-16 12:10:32 +00:00
// remove /sim/fg-root before writing to prevent hijacking
SGPropertyNode * n = props - > getNode ( " /sim " , true ) ;
2014-03-05 23:41:41 +00:00
n - > removeChild ( " fg-root " , 0 ) ;
2013-11-16 12:10:32 +00:00
n = n - > getChild ( " fg-root " , 0 , true ) ;
2016-06-23 13:26:34 +00:00
n - > setStringValue ( fg_root . utf8Str ( ) ) ;
2013-11-16 12:10:32 +00:00
n - > setAttribute ( SGPropertyNode : : WRITE , false ) ;
}
2013-02-03 16:57:31 +00:00
static std : : string autosaveName ( )
{
std : : ostringstream os ;
string_list versionParts = simgear : : strutils : : split ( VERSION , " . " ) ;
if ( versionParts . size ( ) < 2 ) {
return " autosave.xml " ;
}
2015-09-28 20:12:46 +00:00
2013-02-03 16:57:31 +00:00
os < < " autosave_ " < < versionParts [ 0 ] < < " _ " < < versionParts [ 1 ] < < " .xml " ;
return os . str ( ) ;
}
2001-01-19 22:57:24 +00:00
2016-11-01 13:04:36 +00:00
SGPath FGGlobals : : autosaveFilePath ( SGPath userDataPath ) const
{
if ( userDataPath . isNull ( ) ) {
userDataPath = get_fg_home ( ) ;
}
return simgear : : Dir ( userDataPath ) . file ( autosaveName ( ) ) ;
}
2017-04-10 13:38:35 +00:00
static void deleteProperties ( SGPropertyNode * props , const string_list & blacklist )
{
const std : : string path ( props - > getPath ( ) ) ;
auto it = std : : find_if ( blacklist . begin ( ) , blacklist . end ( ) , [ path ] ( const std : : string & black )
{ return simgear : : strutils : : matchPropPathToTemplate ( path , black ) ; } ) ;
if ( it ! = blacklist . end ( ) ) {
SGPropertyNode * pr = props - > getParent ( ) ;
pr - > removeChild ( props ) ;
return ;
}
// recurse
for ( int c = 0 ; c < props - > nChildren ( ) ; + + c ) {
deleteProperties ( props - > getChild ( c ) , blacklist ) ;
}
}
using VersionPair = std : : pair < int , int > ;
static bool operator < ( const VersionPair & a , const VersionPair & b )
{
if ( a . first = = b . first ) {
return a . second < b . second ;
}
return a . first < b . first ;
}
static void tryAutosaveMigration ( const SGPath & userDataPath , SGPropertyNode * props )
{
const string_list versionParts = simgear : : strutils : : split ( VERSION , " . " ) ;
if ( versionParts . size ( ) < 2 ) {
return ;
}
simgear : : Dir userDataDir ( userDataPath ) ;
SGPath migratePath ;
VersionPair foundVersion ( 0 , 0 ) ;
const VersionPair currentVersion ( simgear : : strutils : : to_int ( versionParts [ 0 ] ) ,
simgear : : strutils : : to_int ( versionParts [ 1 ] ) ) ;
for ( auto previousSave : userDataDir . children ( simgear : : Dir : : TYPE_FILE , " .xml " ) ) {
const std : : string base = previousSave . file_base ( ) ;
VersionPair v ;
// extract version from name
const int matches = : : sscanf ( base . c_str ( ) , " autosave_%d_%d " , & v . first , & v . second ) ;
if ( matches ! = 2 ) {
continue ;
}
2017-10-13 16:36:20 +00:00
2017-04-10 13:38:35 +00:00
if ( currentVersion < v ) {
// ignore autosaves from more recent versions; this happens when
// running unsable and stable at the same time
continue ;
}
if ( v . first < 2000 ) {
// ignore autosaves from older versions, too much change to deal
// with.
continue ;
}
if ( foundVersion < v ) {
foundVersion = v ;
migratePath = previousSave ;
}
}
if ( ! migratePath . exists ( ) ) {
return ;
}
SG_LOG ( SG_GENERAL , SG_INFO , " Migrating old autosave: " < < migratePath ) ;
SGPropertyNode oldProps ;
try {
readProperties ( migratePath , & oldProps , SGPropertyNode : : USERARCHIVE ) ;
} catch ( sg_exception & e ) {
SG_LOG ( SG_GENERAL , SG_WARN , " failed to read previous user settings: " < < e . getMessage ( )
< < " (from " < < e . getOrigin ( ) < < " ) " ) ;
return ;
}
// read migration blacklist
string_list blacklist ;
2017-09-20 16:57:25 +00:00
SGPropertyNode_ptr blacklistNode = fgGetNode ( " /sim/autosave-migration/blacklist " , true ) ;
for ( auto node : blacklistNode - > getChildren ( " path " ) ) {
2017-04-10 13:38:35 +00:00
blacklist . push_back ( node - > getStringValue ( ) ) ;
}
// apply migration filters for each property in turn
deleteProperties ( & oldProps , blacklist ) ;
2019-04-11 19:40:17 +00:00
// manual migrations
// migrate pre-2019.1 sense of /sim/mouse/invert-mouse-wheel
if ( foundVersion . first < 2019 ) {
SGPropertyNode_ptr wheelNode = oldProps . getNode ( " /sim/mouse/invert-mouse-wheel " ) ;
if ( wheelNode ) {
wheelNode - > setBoolValue ( ! wheelNode - > getBoolValue ( ) ) ;
}
}
2017-04-10 13:38:35 +00:00
// copy remaining props out
copyProperties ( & oldProps , props ) ;
2017-10-13 16:36:20 +00:00
2020-06-17 09:57:34 +00:00
// we can't inform the user yet, becuase embedded resources and the locale
// are not done. So we set a flag and check it once those things are done.
fgSetBool ( " /sim/autosave-migration/did-migrate " , true ) ;
2017-04-10 13:38:35 +00:00
}
2016-11-01 13:04:36 +00:00
// Load user settings from the autosave file (normally in $FG_HOME)
2011-12-29 23:39:08 +00:00
void
2017-09-20 16:57:25 +00:00
FGGlobals : : loadUserSettings ( SGPath userDataPath )
2011-12-29 23:39:08 +00:00
{
2016-11-01 13:04:36 +00:00
if ( userDataPath . isNull ( ) ) {
userDataPath = get_fg_home ( ) ;
}
// Remember that we have (tried) to load any existing autosave file
2011-12-29 23:39:08 +00:00
haveUserSettings = true ;
2011-12-30 00:01:15 +00:00
2016-11-01 13:04:36 +00:00
SGPath autosaveFile = autosaveFilePath ( userDataPath ) ;
2011-12-30 00:01:15 +00:00
SGPropertyNode autosave ;
if ( autosaveFile . exists ( ) ) {
2016-11-01 13:04:36 +00:00
SG_LOG ( SG_INPUT , SG_INFO ,
" Reading user settings from " < < autosaveFile ) ;
2011-12-30 00:01:15 +00:00
try {
2021-04-21 20:57:32 +00:00
flightgear : : SentryXMLErrorSupression xs ;
2016-06-23 13:26:34 +00:00
readProperties ( autosaveFile , & autosave , SGPropertyNode : : USERARCHIVE ) ;
2011-12-30 00:01:15 +00:00
} catch ( sg_exception & e ) {
SG_LOG ( SG_INPUT , SG_WARN , " failed to read user settings: " < < e . getMessage ( )
< < " (from " < < e . getOrigin ( ) < < " ) " ) ;
}
2017-09-20 16:57:25 +00:00
} else {
2017-04-10 13:38:35 +00:00
tryAutosaveMigration ( userDataPath , & autosave ) ;
2011-12-30 00:01:15 +00:00
}
2020-03-11 00:07:56 +00:00
/* Before 2020-03-10, we could save portions of the /ai/models/ tree, which
confuses things when loaded again . So delete any such items if they have
been loaded . */
SGPropertyNode * ai = autosave . getNode ( " ai " ) ;
if ( ai ) {
ai - > removeChildren ( " models " ) ;
}
2011-12-30 00:01:15 +00:00
copyProperties ( & autosave , globals - > get_props ( ) ) ;
2011-12-29 23:39:08 +00:00
}
2016-11-01 13:04:36 +00:00
// Save user settings to the autosave file.
//
// When calling this method, make sure the value of 'userDataPath' is
// trustworthy. In particular, make sure it can't be influenced by Nasal code,
// not even indirectly via a Nasal-writable place such as the property tree.
//
// Note: the default value, which causes the autosave file to be written to
// $FG_HOME, is safe---if not, it would be a bug.
2011-12-29 23:39:08 +00:00
void
2016-11-01 13:04:36 +00:00
FGGlobals : : saveUserSettings ( SGPath userDataPath )
2011-12-29 23:39:08 +00:00
{
2016-11-01 13:04:36 +00:00
if ( userDataPath . isNull ( ) ) userDataPath = get_fg_home ( ) ;
2011-12-29 23:39:08 +00:00
// only save settings when we have (tried) to load the previous
// settings (otherwise user data was lost)
if ( ! haveUserSettings )
return ;
if ( fgGetBool ( " /sim/startup/save-on-exit " ) ) {
// don't save settings more than once on shutdown
haveUserSettings = false ;
2016-11-01 13:04:36 +00:00
SGPath autosaveFile = autosaveFilePath ( userDataPath ) ;
2011-12-29 23:39:08 +00:00
autosaveFile . create_dir ( 0700 ) ;
2016-06-23 13:26:34 +00:00
SG_LOG ( SG_IO , SG_INFO , " Saving user settings to " < < autosaveFile ) ;
2011-12-29 23:39:08 +00:00
try {
2016-06-23 13:26:34 +00:00
writeProperties ( autosaveFile , globals - > get_props ( ) , false , SGPropertyNode : : USERARCHIVE ) ;
2011-12-29 23:39:08 +00:00
} catch ( const sg_exception & e ) {
2013-02-03 16:57:31 +00:00
guiErrorMessage ( " Error writing autosave: " , e ) ;
2011-12-29 23:39:08 +00:00
}
SG_LOG ( SG_INPUT , SG_DEBUG , " Finished Saving user settings " ) ;
}
}
2010-11-21 23:43:41 +00:00
long int FGGlobals : : get_warp ( ) const
{
return fgGetInt ( " /sim/time/warp " ) ;
}
void FGGlobals : : set_warp ( long int w )
{
fgSetInt ( " /sim/time/warp " , w ) ;
}
long int FGGlobals : : get_warp_delta ( ) const
{
return fgGetInt ( " /sim/time/warp-delta " ) ;
}
void FGGlobals : : set_warp_delta ( long int d )
{
fgSetInt ( " /sim/time/warp-delta " , d ) ;
}
2012-01-02 23:16:18 +00:00
2013-11-16 12:10:32 +00:00
FGScenery * FGGlobals : : get_scenery ( ) const
{
2015-12-10 22:48:02 +00:00
return get_subsystem < FGScenery > ( ) ;
2013-11-16 12:10:32 +00:00
}
2015-12-10 22:48:02 +00:00
FGViewMgr * FGGlobals : : get_viewmgr ( ) const
2013-11-16 12:10:32 +00:00
{
2015-12-10 22:48:02 +00:00
return get_subsystem < FGViewMgr > ( ) ;
2013-11-16 12:10:32 +00:00
}
2016-01-17 21:06:45 +00:00
flightgear : : View * FGGlobals : : get_current_view ( ) const
2013-11-16 12:10:32 +00:00
{
2015-12-10 22:48:02 +00:00
FGViewMgr * vm = get_viewmgr ( ) ;
return vm ? vm - > get_current_view ( ) : 0 ;
2013-11-16 12:10:32 +00:00
}
2013-11-21 17:38:22 +00:00
void FGGlobals : : set_matlib ( SGMaterialLib * m )
{
matlib = m ;
}
2015-12-10 21:05:54 +00:00
FGControls * FGGlobals : : get_controls ( ) const
{
return get_subsystem < FGControls > ( ) ;
}
2013-12-04 09:11:26 +00:00
void FGGlobals : : addListenerToCleanup ( SGPropertyChangeListener * l )
{
_listeners_to_cleanup . push_back ( l ) ;
}
void FGGlobals : : cleanupListeners ( )
{
SGPropertyChangeListenerVec : : iterator i = _listeners_to_cleanup . begin ( ) ;
for ( ; i ! = _listeners_to_cleanup . end ( ) ; + + i ) {
delete * i ;
}
_listeners_to_cleanup . clear ( ) ;
2020-10-29 17:28:05 +00:00
simgear : : AtomicChangeListener : : clearPendingChanges ( ) ;
2013-12-04 09:11:26 +00:00
}
2014-05-31 17:04:56 +00:00
simgear : : pkg : : Root * FGGlobals : : packageRoot ( )
{
return _packageRoot . get ( ) ;
}
void FGGlobals : : setPackageRoot ( const SGSharedPtr < simgear : : pkg : : Root > & p )
{
_packageRoot = p ;
}
2018-06-11 06:50:07 +00:00
bool FGGlobals : : is_headless ( )
{
2020-07-14 11:05:53 +00:00
return flightgear : : isHeadlessMode ( ) ;
2018-06-11 06:50:07 +00:00
}
void FGGlobals : : set_headless ( bool mode )
{
2020-07-14 11:05:53 +00:00
flightgear : : setHeadlessMode ( mode ) ;
2018-06-11 06:50:07 +00:00
}
2001-01-19 22:57:24 +00:00
// end of globals.cxx