1
0
Fork 0

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.
This commit is contained in:
Julian Smith 2020-11-16 18:43:46 +00:00
parent 578f414c42
commit f62e5b9ce3
31 changed files with 1972 additions and 172 deletions

View file

@ -672,7 +672,7 @@ void GUIMgr::init()
_event_handler = new GUIEventHandler(desktop);
globals->get_renderer()
->getViewer()
->getView()
->getEventHandlers()
// GUI is on top of everything so lets install as first event handler
.push_front( _event_handler );
@ -706,7 +706,7 @@ void GUIMgr::shutdown()
if( _event_handler )
{
globals->get_renderer()
->getViewer()
->getView()
->removeEventHandler( _event_handler );
_event_handler = 0;
}

View file

@ -85,7 +85,7 @@ void CocoaFileDialog::exec()
// it window-modal.
NSWindow* cocoaWindow = nil;
std::vector<osgViewer::GraphicsWindow*> windows;
globals->get_renderer()->getViewer()->getWindows(windows);
globals->get_renderer()->getViewerBase()->getWindows(windows);
for (auto gw : windows) {
// OSG doesn't use RTTI, so no dynamic cast. Let's check the class type

View file

@ -26,11 +26,11 @@ namespace {
HWND getMainViewerHWND()
{
osgViewer::Viewer::Windows windows;
if (!globals->get_renderer() || !globals->get_renderer()->getViewer()) {
if (!globals->get_renderer() || !globals->get_renderer()->getViewerBase()) {
return 0;
}
globals->get_renderer()->getViewer()->getWindows(windows);
globals->get_renderer()->getViewerBase()->getWindows(windows);
osgViewer::Viewer::Windows::const_iterator it = windows.begin();
for(; it != windows.end(); ++it) {
if (strcmp((*it)->className(), "GraphicsWindowWin32")) {

View file

@ -61,11 +61,11 @@ bool isCanvasImplementationRegistered()
HWND getMainViewerHWND()
{
osgViewer::Viewer::Windows windows;
if (!globals || !globals->get_renderer() || !globals->get_renderer()->getViewer()) {
if (!globals || !globals->get_renderer() || !globals->get_renderer()->getViewerBase()) {
return 0;
}
globals->get_renderer()->getViewer()->getWindows(windows);
globals->get_renderer()->getViewerBase()->getWindows(windows);
osgViewer::Viewer::Windows::const_iterator it = windows.begin();
for(; it != windows.end(); ++it) {
if (strcmp((*it)->className(), "GraphicsWindowWin32")) {

View file

@ -62,7 +62,7 @@ public:
{
mActualCursor = mCursor;
globals->get_renderer()->getViewer()->getWindows(mWindows);
globals->get_renderer()->getViewerBase()->getWindows(mWindows);
}
virtual void setCursor(Cursor aCursor)
@ -160,7 +160,7 @@ FGMouseCursor* FGMouseCursor::instance()
#ifdef SG_WINDOWS
// set osgViewer cursor inherit, otherwise it will interefere
std::vector<osgViewer::GraphicsWindow*> gws;
globals->get_renderer()->getViewer()->getWindows(gws);
globals->get_renderer()->getViewerBase()->getWindows(gws);
for (auto gw : gws) {
gw->setCursor(osgViewer::GraphicsWindow::InheritCursor);
}

View file

@ -20,11 +20,11 @@ namespace {
HWND getMainViewerHWND()
{
osgViewer::Viewer::Windows windows;
if (!globals->get_renderer() || !globals->get_renderer()->getViewer()) {
if (!globals->get_renderer() || !globals->get_renderer()->getViewerBase()) {
return 0;
}
globals->get_renderer()->getViewer()->getWindows(windows);
globals->get_renderer()->getViewerBase()->getWindows(windows);
osgViewer::Viewer::Windows::const_iterator it = windows.begin();
for(; it != windows.end(); ++it) {
if (strcmp((*it)->className(), "GraphicsWindowWin32")) {

View file

@ -361,6 +361,54 @@ do_view_cycle (const SGPropertyNode * arg, SGPropertyNode * root)
return true;
}
/**
* Built-in command: view-push.
*/
static bool
do_view_push (const SGPropertyNode * arg, SGPropertyNode * root)
{
SG_LOG(SG_GENERAL, SG_ALERT, "do_view_push() called");
globals->get_viewmgr()->view_push();
return true;
}
/**
* Built-in command: clone view.
*/
static bool
do_view_clone (const SGPropertyNode * arg, SGPropertyNode * root)
{
SG_LOG(SG_GENERAL, SG_ALERT, "do_view_clone() called");
globals->get_viewmgr()->clone_current_view();
return true;
}
/**
* Built-in command: view last pair.
*/
static bool
do_view_last_pair (const SGPropertyNode * arg, SGPropertyNode * root)
{
SG_LOG(SG_GENERAL, SG_ALERT, "do_view_last_pair() called");
globals->get_viewmgr()->clone_last_pair();
return true;
}
/**
* Built-in command: double view last pair.
*/
static bool
do_view_last_pair_double (const SGPropertyNode * arg, SGPropertyNode * root)
{
SG_LOG(SG_GENERAL, SG_ALERT, "do_view_last_pair_double() called");
globals->get_viewmgr()->clone_last_pair_double();
return true;
}
/**
* Built-in command: toggle a bool property value.
*
@ -948,6 +996,10 @@ static struct {
{ "save-tape", do_save_tape },
{ "load-tape", do_load_tape },
{ "view-cycle", do_view_cycle },
{ "view-push", do_view_push },
{ "view-clone", do_view_clone },
{ "view-last-pair", do_view_last_pair },
{ "view-last-pair-double", do_view_last_pair_double },
/*
{ "set-sea-level-air-temp-degc", do_set_sea_level_degc },
{ "set-outside-air-temp-degc", do_set_oat_degc },

View file

@ -1283,7 +1283,7 @@ void fgStartNewReset()
FGRenderer* render = globals->get_renderer();
// needed or we crash in multi-threaded OSG mode
render->getViewer()->stopThreading();
render->getViewerBase()->stopThreading();
// order is important here since tile-manager shutdown needs to
// access the scenery object
@ -1294,8 +1294,8 @@ void fgStartNewReset()
// don't cancel the pager until after shutdown, since AIModels (and
// potentially others) can queue delete requests on the pager.
render->getViewer()->getDatabasePager()->cancel();
render->getViewer()->getDatabasePager()->clear();
render->getView()->getDatabasePager()->cancel();
render->getView()->getDatabasePager()->clear();
osgDB::Registry::instance()->clearObjectCache();
// Pager requests depend on this, so don't clear it until now
@ -1376,7 +1376,7 @@ void fgStartNewReset()
eventHandler->reset();
globals->set_renderer(render);
render->init();
render->setViewer(viewer.get());
render->setView(viewer.get());
sgUserDataInit( globals->get_props() );

View file

@ -195,15 +195,15 @@ FGGlobals::~FGGlobals()
// stop OSG threading first, to avoid thread races while we tear down
// scene-graph pieces
// there are some scenarios where renderer is already gone.
osg::ref_ptr<osgViewer::Viewer> vw;
osg::ref_ptr<osgViewer::ViewerBase> vb;
if (renderer) {
vw = renderer->getViewer();
if (vw) {
vb = renderer->getViewerBase();
if (vb) {
// 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)
vw->stopThreading();
vb->stopThreading();
}
}
@ -212,9 +212,10 @@ FGGlobals::~FGGlobals()
// don't cancel the pager until after shutdown, since AIModels (and
// potentially others) can queue delete requests on the pager.
if (vw && vw->getDatabasePager()) {
vw->getDatabasePager()->cancel();
vw->getDatabasePager()->clear();
osgViewer::View* v = renderer->getView();
if (v && v->getDatabasePager()) {
v->getDatabasePager()->cancel();
v->getDatabasePager()->clear();
}
osgDB::Registry::instance()->clearObjectCache();
@ -230,7 +231,7 @@ FGGlobals::~FGGlobals()
delete subsystem_mgr;
subsystem_mgr = nullptr; // important so ::get_subsystem returns NULL
vw = nullptr;
vb = nullptr;
set_matlib(NULL);
delete time_params;

View file

@ -743,10 +743,6 @@ int fgMainInit( int argc, char **argv )
fntInit();
globals->get_renderer()->preinit();
#if defined(ENABLE_COMPOSITOR)
flightgear::addSentryTag("compositor", "yes");
#endif
if (fgGetBool("/sim/ati-viewport-hack", true)) {
SG_LOG(SG_GENERAL, SG_WARN, "Enabling ATI/AMD viewport hack");
flightgear::addSentryTag("ati-viewport-hack", "enabled");

View file

@ -1617,6 +1617,7 @@ where:
OPTION_INT - property is an integer
OPTION_CHANNEL - name of option is the name of a channel
OPTION_FUNC - the option trigger a function
property :
b_param : if type==OPTION_BOOL,
value set to the property (has_param is false for boolean)
s_param : if type==OPTION_STRING,
@ -1870,6 +1871,7 @@ struct OptionDesc {
{"developer", true, OPTION_IGNORE | OPTION_BOOL, "", false, "", nullptr },
{"jsbsim-output-directive-file", true, OPTION_STRING, "/sim/jsbsim/output-directive-file", false, "", nullptr },
{"disable-gui", false, OPTION_FUNC, "", false, "", fgOptDisableGUI },
{"composite-viewer", true, OPTION_INT, "/sim/rendering/composite-viewer-enabled", "", "", nullptr},
{nullptr, false, 0, nullptr, false, nullptr, nullptr}
};

View file

@ -436,7 +436,7 @@ static InitPosResult setInitialPosFromCarrier( const string& carrier )
static InitPosResult checkCarrierSceneryLoaded(const SGSharedPtr<FGAICarrier> carrierRef)
{
SGVec3d cartPos = carrierRef->getCartPos();
auto framestamp = globals->get_renderer()->getViewer()->getFrameStamp();
auto framestamp = globals->get_renderer()->getFrameStamp();
simgear::CheckSceneryVisitor csnv(globals->get_scenery()->getPager(),
toOsg(cartPos),
100.0 /* range in metres */,

View file

@ -213,7 +213,7 @@ public:
if ( NULL == osgDB::Registry::instance()->getReaderWriterForExtension(_type))
throw sg_format_exception("Unsupported image type: " + type, type);
osg::Camera * camera = findLastCamera(globals->get_renderer()->getViewer(), window);
osg::Camera * camera = findLastCamera(globals->get_renderer()->getViewerBase(), window);
if ( NULL == camera)
throw sg_error("Can't find a camera for window '" + window + "'");

View file

@ -371,7 +371,7 @@ bool FGStgTerrain::scenery_available(const SGGeod& position, double range_m)
SGVec3f p = SGVec3f::fromGeod(SGGeod::fromGeodM(position, elev));
osg::FrameStamp* framestamp
= globals->get_renderer()->getViewer()->getFrameStamp();
= globals->get_renderer()->getFrameStamp();
FGScenery* pSceneryManager = globals->get_scenery();
simgear::CheckSceneryVisitor csnv(pSceneryManager->getPager(), toOsg(p), range_m, framestamp);

View file

@ -76,8 +76,8 @@ public:
if (_pagedLODMaximumProp->getType() == simgear::props::NONE) {
// not set, use OSG default / environment value variable
osg::ref_ptr<osgViewer::Viewer> viewer(globals->get_renderer()->getViewer());
int current = viewer->getDatabasePager()->getTargetMaximumNumberOfPageLOD();
osg::ref_ptr<osgViewer::View> view(globals->get_renderer()->getView());
int current = view->getDatabasePager()->getTargetMaximumNumberOfPageLOD();
_pagedLODMaximumProp->setIntValue(current);
}
_pagedLODMaximumProp->addChangeListener(this, true);
@ -106,9 +106,9 @@ public:
_manager->_enableCache = prop->getBoolValue();
} else if (prop == _pagedLODMaximumProp) {
int v = prop->getIntValue();
osg::ref_ptr<osgViewer::Viewer> viewer(globals->get_renderer()->getViewer());
if (viewer) {
osgDB::DatabasePager* pager = viewer->getDatabasePager();
osg::ref_ptr<osgViewer::View> view(globals->get_renderer()->getView());
if (view) {
osgDB::DatabasePager* pager = view->getDatabasePager();
if (pager) pager->setTargetMaximumNumberOfPageLOD(v);
}
} else if (prop == _lodDetailed || prop == _lodBareDelta || prop == _lodRoughDelta) {
@ -344,7 +344,7 @@ void FGTileMgr::schedule_needed(const SGBucket& curr_bucket, double vis)
// update timestamps, so all tiles scheduled now are *newer* than any tile previously loaded
osg::FrameStamp* framestamp
= globals->get_renderer()->getViewer()->getFrameStamp();
= globals->get_renderer()->getFrameStamp();
tile_cache.set_current_time(framestamp->getReferenceTime());
SGBucket b;
@ -379,8 +379,7 @@ void FGTileMgr::schedule_needed(const SGBucket& curr_bucket, double vis)
*/
void FGTileMgr::update_queues(bool& isDownloadingScenery)
{
osg::FrameStamp* framestamp
= globals->get_renderer()->getViewer()->getFrameStamp();
osg::FrameStamp* framestamp = globals->get_renderer()->getFrameStamp();
double current_time = framestamp->getReferenceTime();
double vis = _visibilityMeters->getDoubleValue();
TileEntry *e;

View file

@ -12,6 +12,7 @@ set(SOURCES
splash.cxx
view.cxx
viewmgr.cxx
sview.cxx
)
set(HEADERS
@ -24,6 +25,7 @@ set(HEADERS
splash.hxx
view.hxx
viewmgr.hxx
sview.hxx
)
if (Qt5Core_FOUND)

View file

@ -23,6 +23,7 @@
#include "FGEventHandler.hxx"
#include "WindowBuilder.hxx"
#include "WindowSystemAdapter.hxx"
#include "sview.hxx"
#include <simgear/math/SGRect.hxx>
#include <simgear/props/props.hxx>
@ -192,8 +193,8 @@ typedef std::vector<SGPropertyNode_ptr> SGPropertyNodeVec;
osg::ref_ptr<CameraGroup> CameraGroup::_defaultGroup;
CameraGroup::CameraGroup(osgViewer::Viewer* viewer) :
_viewer(viewer)
CameraGroup::CameraGroup(osgViewer::View* view) :
_viewer(view)
{
}
@ -688,6 +689,9 @@ void CameraGroup::buildCamera(SGPropertyNode* cameraNode)
osg::ref_ptr<SGReaderWriterOptions> options =
SGReaderWriterOptions::fromPath(globals->get_fg_root());
options->setPropertyNode(globals->get_props());
SViewSetCompositorParams(options, compositor_path);
Compositor *compositor = Compositor::create(_viewer,
window->gc,
viewport,
@ -783,10 +787,10 @@ void CameraGroup::buildGUICamera(SGPropertyNode* cameraNode,
camera->setStats(0);
}
CameraGroup* CameraGroup::buildCameraGroup(osgViewer::Viewer* viewer,
CameraGroup* CameraGroup::buildCameraGroup(osgViewer::View* view,
SGPropertyNode* gnode)
{
CameraGroup* cgroup = new CameraGroup(viewer);
CameraGroup* cgroup = new CameraGroup(view);
cgroup->_listener.reset(new CameraGroupListener(cgroup, gnode));
for (int i = 0; i < gnode->nChildren(); ++i) {
@ -862,7 +866,7 @@ computeCameraIntersection(const CameraGroup *cgroup,
osgUtil::IntersectionVisitor iv(picker);
iv.setTraversalMask(simgear::PICK_BIT);
const_cast<CameraGroup *>(cgroup)->getViewer()->getCamera()->accept(iv);
const_cast<CameraGroup *>(cgroup)->getView()->getCamera()->accept(iv);
if (picker->containsIntersections()) {
intersections = picker->getIntersections();
return true;
@ -921,7 +925,7 @@ void warpGUIPointer(CameraGroup* cgroup, int x, int y)
gw->getEventQueue()->mouseWarped(wx, wy);
gw->requestWarpPointer(wx, wy);
osgGA::GUIEventAdapter* eventState
= cgroup->getViewer()->getEventQueue()->getCurrentEventState();
= cgroup->getView()->getEventQueue()->getCurrentEventState();
double viewerX
= (eventState->getXmin()
+ ((wx / double(traits->width))
@ -930,10 +934,10 @@ void warpGUIPointer(CameraGroup* cgroup, int x, int y)
= (eventState->getYmin()
+ ((wyUp / double(traits->height))
* (eventState->getYmax() - eventState->getYmin())));
cgroup->getViewer()->getEventQueue()->mouseWarped(viewerX, viewerY);
cgroup->getView()->getEventQueue()->mouseWarped(viewerX, viewerY);
}
void CameraGroup::buildDefaultGroup(osgViewer::Viewer* viewer)
void CameraGroup::buildDefaultGroup(osgViewer::View* viewer)
{
// Look for windows, camera groups, and the old syntax of
// top-level cameras

View file

@ -30,6 +30,7 @@
#include <osg/Texture2D>
#include <osg/TexGen>
#include <osgUtil/RenderBin>
#include <osgViewer/View>
#include <simgear/scene/viewer/Compositor.hxx>
@ -122,14 +123,14 @@ public:
/** Create a camera group associated with an osgViewer::Viewer.
* @param viewer the viewer
*/
CameraGroup(osgViewer::Viewer* viewer);
CameraGroup(osgViewer::View* viewer);
virtual ~CameraGroup();
/** Set the default CameraGroup, which is the only one that
* matters at this time.
* @param group the group to set.
*/
static void buildDefaultGroup(osgViewer::Viewer* viewer);
static void buildDefaultGroup(osgViewer::View* view);
static void setDefault(CameraGroup* group) { _defaultGroup = group; }
/** Get the default CameraGroup.
* @return the default camera group.
@ -138,7 +139,7 @@ public:
/** Get the camera group's Viewer.
* @return the viewer
*/
osgViewer::Viewer* getViewer() { return _viewer.get(); }
osgViewer::View* getView() { return _viewer.get(); }
/** Create an osg::Camera from a property node and add it to the
* camera group.
* @param cameraNode the property node.
@ -192,7 +193,7 @@ protected:
typedef std::vector<osg::ref_ptr<CameraInfo>> CameraList;
CameraList _cameras;
osg::ref_ptr<osgViewer::Viewer> _viewer;
osg::ref_ptr<osgViewer::View> _viewer;
static osg::ref_ptr<CameraGroup> _defaultGroup;
std::unique_ptr<CameraGroupListener> _listener;
@ -206,7 +207,7 @@ protected:
* @param wbuilder the window builder to be used for this camera group.
* @param the camera group property node.
*/
static CameraGroup* buildCameraGroup(osgViewer::Viewer* viewer,
static CameraGroup* buildCameraGroup(osgViewer::View* viewer,
SGPropertyNode* node);
};

View file

@ -12,7 +12,9 @@
#include "CameraGroup.hxx"
#include "FGEventHandler.hxx"
#include "WindowSystemAdapter.hxx"
#include "WindowBuilder.hxx"
#include "renderer.hxx"
#include "sview.hxx"
#ifdef SG_MAC
// hack - during interactive resize on Mac, OSG queues and then flushes
@ -85,12 +87,18 @@ bool
eventToViewport(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us,
int& x, int& y)
{
flightgear::WindowBuilder* window_builder = flightgear::WindowBuilder::getWindowBuilder();
flightgear::GraphicsWindow* main_window = window_builder->getDefaultWindow();
x = -1;
y = -1;
const osg::GraphicsContext* eventGC = ea.getGraphicsContext();
if( !eventGC )
return false; // TODO how can this happen?
if (eventGC != main_window->gc.get()) {
return false;
}
const osg::GraphicsContext::Traits* traits = eventGC->getTraits();
osg::Camera* guiCamera = getGUICamera(CameraGroup::getDefault());
if (!guiCamera)
@ -115,6 +123,20 @@ eventToViewport(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us,
return false;
}
}
/* A hack for when we are linked with OSG-3.4 and CompositeViewer is
enabled. It seems that OSG-3.4 incorrectly calls our event handler for
extra view windows (e.g. resize/close events), so we try to detect
this. Unfortunately OSG also messes up <ea>'s graphics context pointer so this
does't alwys work. */
bool isMainWindow(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us)
{
int x;
int y;
bool ret = eventToViewport(ea, us, x, y);
return ret;
}
}
bool FGEventHandler::handle(const osgGA::GUIEventAdapter& ea,
@ -224,6 +246,9 @@ bool FGEventHandler::handle(const osgGA::GUIEventAdapter& ea,
return true;
case osgGA::GUIEventAdapter::RESIZE:
SG_LOG(SG_VIEW, SG_DEBUG, "FGEventHandler::handle: RESIZE event " << ea.getWindowHeight() << " x " << ea.getWindowWidth() << ", resizable: " << resizable);
if (!isMainWindow(ea, us)) {
return true;
}
CameraGroup::getDefault()->resized();
if (resizable)
globals->get_renderer()->resize(ea.getWindowWidth(), ea.getWindowHeight());
@ -236,6 +261,10 @@ bool FGEventHandler::handle(const osgGA::GUIEventAdapter& ea,
#endif
return true;
case osgGA::GUIEventAdapter::CLOSE_WINDOW:
if (!isMainWindow(ea, us)) {
return true;
}
// Fall through.
case osgGA::GUIEventAdapter::QUIT_APPLICATION:
fgOSExit(0);
return true;

View file

@ -275,13 +275,13 @@ PUICamera::~PUICamera()
// depending on if we're doing shutdown or reset, various things can be
// null here.
auto renderer = globals->get_renderer();
auto viewer = renderer ? renderer->getViewer() : nullptr;
if (viewer) {
viewer->removeEventHandler(_eventHandler);
auto view = renderer ? renderer->getView() : nullptr;
if (view) {
view->removeEventHandler(_eventHandler);
}
}
void PUICamera::init(osg::Group* parent, osgViewer::Viewer* viewer)
void PUICamera::init(osg::Group* parent, osgViewer::View* view)
{
setName("PUI FBO camera");
@ -351,7 +351,7 @@ void PUICamera::init(osg::Group* parent, osgViewer::Viewer* viewer)
// the rendering order (i.e top-most UI layer has the front-most event
// handler)
_eventHandler = new PUIEventHandler(this);
viewer->getEventHandlers().push_front(_eventHandler);
view->getEventHandlers().push_front(_eventHandler);
}
// remove once we require OSG 3.4

View file

@ -19,6 +19,7 @@
#include <osg/Camera>
#include <osg/Version>
#include <osgViewer/View>
namespace osg
{
@ -51,7 +52,7 @@ public:
// osg::Camera already defines a resize() so use this name
void resizeUi(int width, int height);
void init(osg::Group* parent, osgViewer::Viewer* viewer);
void init(osg::Group* parent, osgViewer::View* view);
private:
void manuallyResizeFBO(int width, int height);

View file

@ -189,7 +189,7 @@ class NotifyLevelListener : public SGPropertyChangeListener
public:
void valueChanged(SGPropertyNode* node)
{
osg::NotifySeverity severity = osg::WARN;
osg::NotifySeverity severity = osg::getNotifyLevel();
string val = simgear::strutils::lowercase(node->getStringValue());
if (val == "fatal") {
@ -216,6 +216,61 @@ void fgOSOpenWindow(bool stencil)
{
osg::setNotifyHandler(new NotifyLogger);
auto composite_viewer = dynamic_cast<osgViewer::CompositeViewer*>(
globals->get_renderer()->getViewerBase()
);
if (0) {}
else if (composite_viewer) {
/* We are using CompositeViewer. */
SG_LOG(SG_VIEW, SG_ALERT, "Using CompositeViewer");
osgViewer::ViewerBase* viewer = globals->get_renderer()->getViewerBase();
SG_LOG(SG_VIEW, SG_ALERT, "Creating osgViewer::View");
osgViewer::View* view = new osgViewer::View;
view->setFrameStamp(composite_viewer->getFrameStamp());
globals->get_renderer()->setView(view);
assert(globals->get_renderer()->getView() == view);
view->setDatabasePager(FGScenery::getPagerSingleton());
// https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg29820.html
view->getDatabasePager()->setUnrefImageDataAfterApplyPolicy(true, false);
osg::GraphicsContext::createNewContextID();
//viewer->setThreadingModel(osgViewer::Viewer::SingleThreaded);
std::string mode;
mode = fgGetString("/sim/rendering/multithreading-mode", "SingleThreaded");
SG_LOG( SG_VIEW, SG_INFO, "mode=" << mode);
if (mode == "AutomaticSelection")
viewer->setThreadingModel(osgViewer::Viewer::AutomaticSelection);
else if (mode == "CullDrawThreadPerContext")
viewer->setThreadingModel(osgViewer::Viewer::CullDrawThreadPerContext);
else if (mode == "DrawThreadPerContext")
viewer->setThreadingModel(osgViewer::Viewer::DrawThreadPerContext);
else if (mode == "CullThreadPerCameraDrawThreadPerContext")
viewer->setThreadingModel(osgViewer::Viewer::CullThreadPerCameraDrawThreadPerContext);
else
viewer->setThreadingModel(osgViewer::Viewer::SingleThreaded);
WindowBuilder::initWindowBuilder(stencil);
CameraGroup::buildDefaultGroup(view);
FGEventHandler* manipulator = globals->get_renderer()->getEventHandler();
WindowSystemAdapter* wsa = WindowSystemAdapter::getWSA();
if (wsa->windows.size() != 1) {
manipulator->setResizable(false);
}
view->getCamera()->setProjectionResizePolicy(osg::Camera::FIXED);
view->addEventHandler(manipulator);
// Let FG handle the escape key with a confirmation
viewer->setKeyEventSetsDone(0);
// The viewer won't start without some root.
view->setSceneData(new osg::Group);
globals->get_renderer()->setView(view);
}
else {
/* Not using CompositeViewer. */
SG_LOG(SG_VIEW, SG_DEBUG, "Not CompositeViewer.");
SG_LOG(SG_VIEW, SG_DEBUG, "Creating osgViewer::Viewer");
viewer = new osgViewer::Viewer;
viewer->setDatabasePager(FGScenery::getPagerSingleton());
@ -247,7 +302,8 @@ void fgOSOpenWindow(bool stencil)
viewer->setKeyEventSetsDone(0);
// The viewer won't start without some root.
viewer->setSceneData(new osg::Group);
globals->get_renderer()->setViewer(viewer.get());
globals->get_renderer()->setView(viewer.get());
}
}
SGPropertyNode* simHost = 0, *simFrameCount, *simTotalHostTime, *simFrameResetCount, *frameWait;
void fgOSResetProperties()
@ -286,8 +342,9 @@ static int status = 0;
void fgOSExit(int code)
{
viewer->setDone(true);
viewer->getDatabasePager()->cancel();
FGRenderer* renderer = globals->get_renderer();
renderer->getViewerBase()->setDone(true);
renderer->getView()->getDatabasePager()->cancel();
status = code;
// otherwise we crash if OSG does logging during static destruction, eg
@ -299,12 +356,13 @@ SGTimeStamp _lastUpdate;
int fgOSMainLoop()
{
viewer->setReleaseContextAtEndOfFrameHint(false);
if (!viewer->isRealized()) {
viewer->realize();
osgViewer::ViewerBase* viewer_base = globals->get_renderer()->getViewerBase();
viewer_base->setReleaseContextAtEndOfFrameHint(false);
if (!viewer_base->isRealized()) {
viewer_base->realize();
}
while (!viewer->done()) {
while (!viewer_base->done()) {
fgIdleHandler idleFunc = globals->get_renderer()->getEventHandler()->getIdleHandler();
if (idleFunc)
{
@ -338,7 +396,7 @@ int fgOSMainLoop()
}
}
globals->get_renderer()->update();
viewer->frame( globals->get_sim_time_sec() );
viewer_base->frame( globals->get_sim_time_sec() );
}
flightgear::addSentryBreadcrumb("main loop exited", "info");
@ -383,13 +441,16 @@ void fgOSInit(int* argc, char** argv)
void fgOSCloseWindow()
{
if (viewer) {
if (globals && globals->get_renderer()) {
osgViewer::ViewerBase* viewer_base = globals->get_renderer()->getViewerBase();
if (viewer_base) {
// https://code.google.com/p/flightgear-bugs/issues/detail?id=1291
// https://sourceforge.net/p/flightgear/codetickets/1830/
// explicitly stop threading before we delete the renderer or
// viewMgr (which ultimately holds refs to the CameraGroup, and
// GraphicsContext)
viewer->stopThreading();
viewer_base->stopThreading();
}
}
FGScenery::resetPagerSingleton();
flightgear::CameraGroup::setDefault(NULL);
@ -399,8 +460,9 @@ void fgOSCloseWindow()
void fgOSFullScreen()
{
osgViewer::ViewerBase* viewer_base = globals->get_renderer()->getViewerBase();
std::vector<osgViewer::GraphicsWindow*> windows;
viewer->getWindows(windows);
viewer_base->getWindows(windows);
if (windows.empty())
return; // Huh?!?
@ -544,11 +606,14 @@ static int _cursor = -1;
void fgSetMouseCursor(int cursor)
{
_cursor = cursor;
if (!viewer)
if (!globals || !globals->get_renderer())
return;
osgViewer::ViewerBase* viewer_base = globals->get_renderer()->getViewerBase();
if (!viewer_base)
return;
std::vector<osgViewer::GraphicsWindow*> windows;
viewer->getWindows(windows);
viewer_base->getWindows(windows);
for (osgViewer::GraphicsWindow* gw : windows) {
setMouseCursor(gw, cursor);
}

View file

@ -115,7 +115,7 @@ fgviewerMain(int argc, char** argv)
// construct the viewer.
FGRenderer* fgrenderer = new FGRenderer();
osgViewer::Viewer* viewer = new osgViewer::Viewer(arguments);
fgrenderer->setViewer(viewer);
fgrenderer->setView(viewer);
osg::Camera* camera = viewer->getCamera();
osgViewer::Renderer* renderer
= static_cast<osgViewer::Renderer*>(camera->getRenderer());

View file

@ -359,8 +359,8 @@ FGRenderer::~FGRenderer()
}
// replace the viewer's scene completely
if (getViewer()) {
getViewer()->setSceneData(new osg::Group);
if (getView()) {
getView()->setSceneData(new osg::Group);
}
delete _sky;
@ -398,28 +398,39 @@ FGRenderer::addChangeListener(SGPropertyChangeListener* l, const char* path)
}
// Initialize various GL/view parameters
//
// Note that this appears to be called *after* FGRenderer::init().
//
void
FGRenderer::preinit( void )
{
// important that we reset the viewer sceneData here, to ensure the reference
// time for everything is in sync; otherwise on reset the Viewer and
// GraphicsWindow clocks are out of sync.
osgViewer::Viewer* viewer = getViewer();
viewer->setName("osgViewer");
osgViewer::View* view = getView();
view->setName("osgViewer");
_viewerSceneRoot = new osg::Group;
_viewerSceneRoot->setName("viewerSceneRoot");
viewer->setSceneData(_viewerSceneRoot);
view->setSceneData(_viewerSceneRoot);
view->setDatabasePager(FGScenery::getPagerSingleton());
_quickDrawable = nullptr;
_splash = new SplashScreen;
_viewerSceneRoot->addChild(_splash);
if (composite_viewer) {
// Nothing to do - composite_viewer->addView() will tell view to use
// composite_viewer's FrameStamp.
}
else {
_frameStamp = new osg::FrameStamp;
viewer->setFrameStamp(_frameStamp.get());
view->setFrameStamp(_frameStamp.get());
}
// Scene doesn't seem to pass the frame stamp to the update
// visitor automatically.
_updateVisitor->setFrameStamp(_frameStamp.get());
viewer->setUpdateVisitor(_updateVisitor.get());
_updateVisitor->setFrameStamp(getFrameStamp());
getViewerBase()->setUpdateVisitor(_updateVisitor.get());
fgSetDouble("/sim/startup/splash-alpha", 1.0);
// hide the menubar if it overlaps the window, so the splash screen
@ -436,6 +447,31 @@ FGRenderer::init( void )
sgUserDataInit( globals->get_props() );
SGPropertyNode* composite_viewer_enabled_prop = fgGetNode("/sim/rendering/composite-viewer-enabled", true);
// After we've read composite_viewer_enabled_prop here, changing its value
// will have no affect, so mark it as read-only for clarity.
composite_viewer_enabled_prop->setAttributes(SGPropertyNode::READ);
if (composite_viewer_enabled_prop->getBoolValue()) {
const char* osg_version = osgGetVersion();
if (simgear::strutils::starts_with(osg_version, "3.4")) {
SG_LOG( SG_GENERAL, SG_POPUP,
"CompositeViewer is enabled and requires OpenSceneGraph-3.6, but\n"
" Flightgear has been built with OpenSceneGraph-" << osg_version << ".\n"
" There may be problems when opening/closing extra view windows.\n"
);
}
composite_viewer_enabled = 1;
SG_LOG(SG_VIEW, SG_ALERT, "Creating osgViewer::CompositeViewer");
composite_viewer = new osgViewer::CompositeViewer;
// https://stackoverflow.com/questions/15207076/openscenegraph-and-multiple-viewers
composite_viewer->setReleaseContextAtEndOfFrameHint(false);
composite_viewer->setThreadingModel(osgViewer::Viewer::SingleThreaded);
}
else {
composite_viewer_enabled = 0;
SG_LOG(SG_VIEW, SG_ALERT, "Not creating osgViewer::CompositeViewer");
}
_scenery_loaded = fgGetNode("/sim/sceneryloaded", true);
_position_finalized = fgGetNode("/sim/position-finalized", true);
@ -542,7 +578,7 @@ void FGRenderer::setupRoot()
void
FGRenderer::setupView( void )
{
osgViewer::Viewer* viewer = globals->get_renderer()->getViewer();
osgViewer::View* view = globals->get_renderer()->getView();
osg::initNotifyLevel();
// The number of polygon-offset "units" to place between layers. In
@ -571,7 +607,7 @@ FGRenderer::setupView( void )
fgGetNode("/environment", true),
opt.get());
viewer->getCamera()
view->getCamera()
->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
@ -647,10 +683,12 @@ FGRenderer::setupView( void )
#if defined(HAVE_PUI)
_puiCamera = new flightgear::PUICamera;
_puiCamera->init(guiCamera, viewer);
_puiCamera->init(guiCamera, view);
#endif
#if defined(ENABLE_QQ_UI)
osgViewer::Viewer* viewer = dynamic_cast<osgViewer::Viewer*>(view);
if (viewer) {
std::string rootQMLPath = fgGetString("/sim/gui/qml-root-path");
auto graphicsWindow = dynamic_cast<osgViewer::GraphicsWindow*>(guiCamera->getGraphicsContext());
@ -664,6 +702,7 @@ FGRenderer::setupView( void )
qqGeode->addDrawable(_quickDrawable);
guiCamera->addChild(qqGeode);
}
}
#endif
guiCamera->insertChild(0, FGPanelNode::create2DPanelNode());
}
@ -706,7 +745,6 @@ FGRenderer::update( ) {
}
return;
}
osgViewer::Viewer* viewer = globals->get_renderer()->getViewer();
if (_splash_alpha->getDoubleValue()>0.0)
{
@ -748,8 +786,21 @@ FGRenderer::update( ) {
// Force update of center dependent values ...
current__view->set_dirty();
osg::Camera *camera = viewer->getCamera();
assert(composite_viewer_enabled != -1);
std::vector<osg::Camera*> cameras;
if (composite_viewer) {
assert(!viewer);
unsigned n = composite_viewer->getNumViews();
for (unsigned i=0; i<n; ++i) {
osgViewer::View* view = composite_viewer->getView(i);
osg::Camera* camera = view->getCamera();
cameras.push_back(camera);
}
}
else {
cameras.push_back(viewer->getCamera());
}
for (osg::Camera* camera: cameras) {
osg::Vec4 clear_color = _altitude_ft->getDoubleValue() < 250000
? toOsg(l->adj_fog_color())
// skydome ends at ~262000ft (default rendering)
@ -762,9 +813,10 @@ FGRenderer::update( ) {
updateSky();
// need to call the update visitor once
_frameStamp->setCalendarTime(*globals->get_time_params()->getGmt());
getFrameStamp()->setCalendarTime(*globals->get_time_params()->getGmt());
_updateVisitor->setViewData(current__view->getViewPosition(),
current__view->getViewOrientation());
//_updateVisitor->setViewData(eye2, center3);
SGVec3f direction(l->sun_vec()[0], l->sun_vec()[1], l->sun_vec()[2]);
_updateVisitor->setLight(direction, l->scene_ambient(),
l->scene_diffuse(), l->scene_specular(),
@ -781,6 +833,7 @@ FGRenderer::update( ) {
camera->setCullMaskLeft(cullMask);
camera->setCullMaskRight(cullMask);
}
}
void
FGRenderer::updateSky()
@ -958,11 +1011,66 @@ PickList FGRenderer::pick(const osg::Vec2& windowPos)
return result;
}
void
FGRenderer::setViewer(osgViewer::Viewer* viewer_)
osgViewer::ViewerBase* FGRenderer::getViewerBase()
{
if (composite_viewer) {
return composite_viewer.get();
}
else {
return viewer.get();
}
}
osgViewer::View* FGRenderer::getView()
{
assert(composite_viewer_enabled != -1);
if (composite_viewer) {
assert(composite_viewer->getNumViews());
return composite_viewer->getView(0);
}
else {
return viewer.get();
}
}
const osgViewer::View* FGRenderer::getView() const
{
FGRenderer* this_ = const_cast<FGRenderer*>(this);
return this_->getView();
}
void
FGRenderer::setView(osgViewer::View* view)
{
assert(composite_viewer_enabled != -1);
if (composite_viewer) {
if (composite_viewer->getNumViews() == 0) {
SG_LOG(SG_VIEW, SG_ALERT, "adding view to composite_viewer.");
composite_viewer->stopThreading();
composite_viewer->addView(view);
composite_viewer->startThreading();
}
}
else {
osgViewer::Viewer* viewer_ = dynamic_cast<osgViewer::Viewer*>(view);
assert(viewer_);
viewer = viewer_;
}
}
osg::FrameStamp*
FGRenderer::getFrameStamp()
{
assert(composite_viewer_enabled != -1);
if (composite_viewer) {
assert(!viewer);
return composite_viewer->getFrameStamp();
}
else {
assert(viewer);
return viewer->getFrameStamp();
}
}
void
FGRenderer::setEventHandler(FGEventHandler* eventHandler_)
@ -991,8 +1099,8 @@ FGRenderer::setPlanes( double zNear, double zFar )
bool
fgDumpSceneGraphToFile(const char* filename)
{
osgViewer::Viewer* viewer = globals->get_renderer()->getViewer();
return osgDB::writeNodeFile(*viewer->getSceneData(), filename);
osgViewer::View* view = globals->get_renderer()->getView();
return osgDB::writeNodeFile(*view->getSceneData(), filename);
}
bool
@ -1199,14 +1307,14 @@ protected:
bool printVisibleSceneInfo(FGRenderer* renderer)
{
osgViewer::Viewer* viewer = renderer->getViewer();
osgViewer::View* view = renderer->getView();
VisibleSceneInfoVistor vsv;
Viewport* vp = 0;
if (!viewer->getCamera()->getViewport() && viewer->getNumSlaves() > 0) {
const osg::View::Slave& slave = viewer->getSlave(0);
if (!view->getCamera()->getViewport() && view->getNumSlaves() > 0) {
const osg::View::Slave& slave = view->getSlave(0);
vp = slave._camera->getViewport();
}
vsv.doTraversal(viewer->getCamera(), viewer->getSceneData(), vp);
vsv.doTraversal(view->getCamera(), view->getSceneData(), vp);
return true;
}

View file

@ -10,6 +10,9 @@
#include <osg/Vec2>
#include <osg/Vec3>
#include <osgViewer/CompositeViewer>
namespace osg
{
class Camera;
@ -62,11 +65,19 @@ public:
*/
PickList pick(const osg::Vec2& windowPos);
/* Returns either composite_viewer or viewer. */
osgViewer::ViewerBase* getViewerBase();
/** Get and set the OSG Viewer object, if any.
*/
osgViewer::Viewer* getViewer() { return viewer.get(); }
const osgViewer::Viewer* getViewer() const { return viewer.get(); }
void setViewer(osgViewer::Viewer* viewer);
osgViewer::View* getView();
const osgViewer::View* getView() const;
void setView(osgViewer::View* view);
/** Calls osgViewer::CompositeViewer::getFrameStamp() if we are using
composite viewer, otherwise osgViewer::Viewer::getFrameStamp(). */
osg::FrameStamp* getFrameStamp();
/** Get and set the manipulator object, if any.
*/
flightgear::FGEventHandler* getEventHandler() { return eventHandler.get(); }
@ -84,7 +95,9 @@ public:
void setPlanes( double zNear, double zFar );
protected:
int composite_viewer_enabled = -1;
osg::ref_ptr<osgViewer::Viewer> viewer;
osg::ref_ptr<osgViewer::CompositeViewer> composite_viewer;
osg::ref_ptr<flightgear::FGEventHandler> eventHandler;
osg::ref_ptr<osg::FrameStamp> _frameStamp;

View file

@ -204,6 +204,12 @@ void SplashScreen::createNodes()
nullptr, -1.0, osg::Vec4(1.0, 0.0, 0.0, 1.0));
}
if (fgGetBool("/sim/rendering/composite-viewer-enabled")) {
addText(geode, osg::Vec2(0.5f, 0.65f), 0.03,
"CompositeViewer",
osgText::Text::CENTER_CENTER,
nullptr, -1.0, osg::Vec4(1.0, 0.0, 0.0, 1.0));
}
///////////

1394
src/Viewer/sview.cxx Normal file

File diff suppressed because it is too large Load diff

80
src/Viewer/sview.hxx Normal file
View file

@ -0,0 +1,80 @@
#pragma once
/*
Support for extra view windows. Requires that composite-viewer is enabled at
startup with --composite-viewer=1.
*/
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <osgViewer/View>
/* Should be called before the first call to SviewCreate() so that
SviewCreate() can create new simgear::compositor::Compositor instances with the
same parameters as were used for the main window.
options
compositor_path
Suitable for passing to simgear::compositor::Compositor().
*/
void SViewSetCompositorParams(
osg::ref_ptr<simgear::SGReaderWriterOptions> options,
const std::string& compositor_path
);
/* Pushes current main window view to internal circular list of two items used
by SviewCreate() with 'last_pair' or 'last_pair_double'. */
void SviewPush();
/* Updates camera position/orientation/zoom of all sviews - should be called
each frame. Will also handle closing of Sview windows. */
void SviewUpdate(double dt);
/* Deletes all internal views; e.g. used when restarting. */
void SviewClear();
/* A view, typically an extra view window. The definition of this is not
public. */
struct SviewView;
/*
This is the main interface to the Sview system. We create a new SviewView in a
new top-level window. It will be updated as required by SviewUpdate().
As of 2020-11-18, the new window will be half width and height of the main
window, and will have top-left corner at (100, 100). It can be dragged, resized
and closed by the user.
type:
This controls what sort of view we create:
"current"
Clones the current view.
"last_pair"
Look from first pushed view's eye to second pushed view's eye. Returns
nullptr if SviewPush hasn't been called at least twice.
"last_pair_double"
Keep first pushed view's aircraft in foreground and second pushed
view's aircraft in background. Returns nullptr if SviewPush hasn't been
called at least twice.
Returns:
Shared ptr to SviewView instance. As of 2020-11-18 there is little that
the caller can do with this. We handle closing of the SviewView's window
internally.
As of 2020-11-17, extra views have various limitations including:
No event handling, so no panning, zooming etc.
Tower View AGL is like Tower View so no zooming to keep ground visible.
Cockpit view has a incorrect calculation giving slightly incorrect
translation when rolling.
No damping in chase views.
Hard-coded chase distances.
*/
std::shared_ptr<SviewView> SviewCreate(const std::string& type);

View file

@ -33,8 +33,12 @@
#include <Main/fg_props.hxx>
#include "view.hxx"
#include "sview.hxx"
#include "renderer.hxx"
#include "CameraGroup.hxx"
#include "Scenery/scenery.hxx"
// Constructor
FGViewMgr::FGViewMgr(void)
@ -134,6 +138,7 @@ FGViewMgr::unbind ()
_viewNumberProp.clear();
ViewPropertyEvaluator::clear();
SviewClear();
}
void
@ -155,6 +160,7 @@ FGViewMgr::update (double dt)
cameraGroup->update(toOsg(currentView->getViewPosition()),
toOsg(currentView->getViewOrientation()));
}
SviewUpdate(dt);
}
void FGViewMgr::clear()
@ -222,6 +228,33 @@ FGViewMgr::prev_view()
return get_current_view();
}
void FGViewMgr::view_push()
{
SviewPush();
}
void FGViewMgr::clone_current_view()
{
clone_internal("current");
}
void FGViewMgr::clone_last_pair()
{
clone_internal("last_pair");
}
void FGViewMgr::clone_last_pair_double()
{
clone_internal("last_pair_double");
}
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
void FGViewMgr::clone_internal(const std::string& type)
{
SviewCreate(type);
}
void
FGViewMgr::add_view( flightgear::View * v )
{

View file

@ -75,6 +75,19 @@ public:
flightgear::View* next_view();
flightgear::View* prev_view();
//
void view_push();
// Experimental. Only works if --compositer-viewer=1 was specified. Creates
// new window with clone of current view. As of 2020-09-03, the clone's
// scenery is not displayed correctly.
void clone_current_view();
//
void clone_last_pair();
void clone_last_pair_double();
// setters
void clear();
@ -84,6 +97,7 @@ private:
simgear::TiedPropertyList _tiedProperties;
void setCurrentViewIndex(int newview);
void clone_internal(const std::string& type);
bool _inited = false;
std::vector<SGPropertyNode_ptr> config_list;

View file

@ -45,7 +45,7 @@ void initScenery()
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
FGRenderer* render = globals->get_renderer();
render->init();
render->setViewer(viewer.get());
render->setView(viewer.get());
// Start up the scenery subsystem.
globals->add_new_subsystem<FGScenery>(SGSubsystemMgr::DISPLAY);