From 7a096d983568ec04c9fad4a33fdb64543c6aa9f2 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 2 Aug 2021 23:30:19 +0100 Subject: [PATCH 1/5] CameraGroup::buildCamera(): Return CameraInfo* Make CameraGroup::buildCamera() return the new CameraInfo* so the caller can make adjustments. --- src/Viewer/CameraGroup.cxx | 8 +++++--- src/Viewer/CameraGroup.hxx | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Viewer/CameraGroup.cxx b/src/Viewer/CameraGroup.cxx index 8b326e019..15b6f0029 100644 --- a/src/Viewer/CameraGroup.cxx +++ b/src/Viewer/CameraGroup.cxx @@ -477,7 +477,7 @@ void CameraGroup::buildDistortionCamera(const SGPropertyNode* psNode, #endif } -void CameraGroup::buildCamera(SGPropertyNode* cameraNode) +CameraInfo* CameraGroup::buildCamera(SGPropertyNode* cameraNode) { WindowBuilder *wBuild = WindowBuilder::getWindowBuilder(); const SGPropertyNode* windowNode = cameraNode->getNode("window"); @@ -491,7 +491,7 @@ void CameraGroup::buildCamera(SGPropertyNode* cameraNode) window = wBuild->buildWindow(cameraNode); } if (!window) { - return; + return nullptr; } osg::Matrix vOff; @@ -599,7 +599,7 @@ void CameraGroup::buildCamera(SGPropertyNode* cameraNode) if (it == _cameras.end()) { SG_LOG(SG_VIEW, SG_ALERT, "CameraGroup::buildCamera: " "failed to find parent camera for relative camera!"); - return; + return nullptr; } parentInfo = (*it); if (projectionNode->getNameString() == "right-of-perspective") { @@ -717,6 +717,8 @@ void CameraGroup::buildCamera(SGPropertyNode* cameraNode) info->flags = info->flags | CameraInfo::VIEW_ABSOLUTE; //buildDistortionCamera(psNode, camera); } + + return info; } void CameraGroup::buildGUICamera(SGPropertyNode* cameraNode, diff --git a/src/Viewer/CameraGroup.hxx b/src/Viewer/CameraGroup.hxx index 60d4faeb1..0f24adf65 100644 --- a/src/Viewer/CameraGroup.hxx +++ b/src/Viewer/CameraGroup.hxx @@ -150,7 +150,7 @@ public: * @param cameraNode the property node. * @return a CameraInfo object for the camera. */ - void buildCamera(SGPropertyNode* cameraNode); + CameraInfo* buildCamera(SGPropertyNode* cameraNode); /** Create a camera from properties that will draw the GUI and add * it to the camera group. * @param cameraNode the property node. This can be 0, in which From 9e430b3fc21bb9eceb92b04dda65e5786d4cd273 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 2 Aug 2021 23:36:04 +0100 Subject: [PATCH 2/5] CameraGroup: Add removeCamera() Add removeCamera() method to CameraGroup to find and remove a single CameraInfo. This will allow cameras created for VR to be dynamically reconfigured. --- src/Viewer/CameraGroup.cxx | 10 ++++++++++ src/Viewer/CameraGroup.hxx | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/src/Viewer/CameraGroup.cxx b/src/Viewer/CameraGroup.cxx index 15b6f0029..a32ad3f15 100644 --- a/src/Viewer/CameraGroup.cxx +++ b/src/Viewer/CameraGroup.cxx @@ -721,6 +721,16 @@ CameraInfo* CameraGroup::buildCamera(SGPropertyNode* cameraNode) return info; } +void CameraGroup::removeCamera(CameraInfo *info) +{ + for (auto it = _cameras.begin(); it != _cameras.end(); ++it) { + if (*it == info) { + _cameras.erase(it); + return; + } + } +} + void CameraGroup::buildGUICamera(SGPropertyNode* cameraNode, GraphicsWindow* window) { diff --git a/src/Viewer/CameraGroup.hxx b/src/Viewer/CameraGroup.hxx index 0f24adf65..9ebcc570e 100644 --- a/src/Viewer/CameraGroup.hxx +++ b/src/Viewer/CameraGroup.hxx @@ -151,6 +151,10 @@ public: * @return a CameraInfo object for the camera. */ CameraInfo* buildCamera(SGPropertyNode* cameraNode); + /** Remove a camera from the camera group. + * @param info the camera info to remove. + */ + void removeCamera(CameraInfo *info); /** Create a camera from properties that will draw the GUI and add * it to the camera group. * @param cameraNode the property node. This can be 0, in which From e25a5605910ee6c02e8c1f3c4ebc32aa8c681781 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 2 Aug 2021 23:38:56 +0100 Subject: [PATCH 3/5] CameraGroup: Add compositor reload callbacks Add a compositor reload callback object to CameraInfo, with callbacks for just prior and after the CameraInfo's compositor is reloaded by reloadCompositors(). This will allow VR cameras to be reconfigured after a reload. --- src/Viewer/CameraGroup.cxx | 6 ++++++ src/Viewer/CameraGroup.hxx | 11 ++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Viewer/CameraGroup.cxx b/src/Viewer/CameraGroup.cxx index a32ad3f15..e122b9c0e 100644 --- a/src/Viewer/CameraGroup.cxx +++ b/src/Viewer/CameraGroup.cxx @@ -982,6 +982,9 @@ void reloadCompositors(CameraGroup *cgroup) SGReaderWriterOptions::fromPath(globals->get_fg_root()); options->setPropertyNode(globals->get_props()); + if (info->reloadCompositorCallback.valid()) + info->reloadCompositorCallback->preReloadCompositor(cgroup, info); + // Force deletion info->compositor.reset(nullptr); // Then replace it with a new instance @@ -993,6 +996,9 @@ void reloadCompositors(CameraGroup *cgroup) viewport, compositor_path, options)); + + if (info->reloadCompositorCallback.valid()) + info->reloadCompositorCallback->postReloadCompositor(cgroup, info); } cgroup->_viewer->getViewerBase()->startThreading(); diff --git a/src/Viewer/CameraGroup.hxx b/src/Viewer/CameraGroup.hxx index 9ebcc570e..6e0bc58aa 100644 --- a/src/Viewer/CameraGroup.hxx +++ b/src/Viewer/CameraGroup.hxx @@ -51,6 +51,7 @@ namespace flightgear class CameraGroupListener; class GraphicsWindow; +class CameraGroup; /** A wrapper around osg::Camera that contains some extra information. */ @@ -76,7 +77,7 @@ struct CameraInfo : public osg::Referenced flags(flags_), physicalWidth(0), physicalHeight(0), bezelHeightTop(0), bezelHeightBottom(0), bezelWidthLeft(0), bezelWidthRight(0), - relativeCameraParent(0) { } + relativeCameraParent(0), reloadCompositorCallback(nullptr) { } /** The name as given in the config file. */ std::string name; @@ -120,6 +121,14 @@ struct CameraInfo : public osg::Referenced * Compositor path and should use the default one. */ std::string compositor_path; + + struct ReloadCompositorCallback : public virtual osg::Referenced + { + virtual void preReloadCompositor(CameraGroup *, CameraInfo *) = 0; + virtual void postReloadCompositor(CameraGroup *, CameraInfo *) = 0; + }; + osg::ref_ptr reloadCompositorCallback; + }; class CameraGroup : public osg::Referenced From dcdda1044a9f4e71625a963dce827e765bb05142 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Sat, 17 Jul 2021 23:19:14 +0100 Subject: [PATCH 4/5] VR: Find the osgXR library Allow the osgXR[1] library to be found and linked against. We'll use this to implement VR in FlightGear. [1] https://github.com/amalon/osgXR --- CMakeLists.txt | 6 ++++++ CMakeModules/SetupFGFSIncludes.cmake | 4 ++++ CMakeModules/SetupFGFSLibraries.cmake | 4 ++++ src/Include/config_cmake.h.in | 2 ++ 4 files changed, 16 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 220c3e557..671f83980 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,6 +151,11 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR if(HTS_ENGINE_FOUND) set(SYSTEM_HTS_ENGINE_DEFAULT 1) endif() + + find_package(osgXR 0.3) + if (osgXR_FOUND) + set(ENABLE_OSGXR_DEFAULT 1) + endif() endif() # FlightGear build options @@ -171,6 +176,7 @@ option(SYSTEM_GSM "Set to ON to build IAXClient with the system's GSM lib option(SYSTEM_FLITE "Set to ON to build Flightgear with the system's Flite library" ${SYSTEM_FLITE_DEFAULT}) option(SYSTEM_HTS_ENGINE "Set to ON to build Flightgear with the system's HTS Engine library" ${SYSTEM_HTS_ENGINE_DEFAULT}) option(SYSTEM_CPPUNIT "Set to ON to build Flightgear with the system's CppUnit library") +option(ENABLE_OSGXR "Set to ON to build Flightgear with OpenXR support via the osgXR library" ${ENABLE_OSGXR_DEFAULT}) # additional utilities option(ENABLE_FGELEV "Set to ON to build the fgelev application (default)" ON) diff --git a/CMakeModules/SetupFGFSIncludes.cmake b/CMakeModules/SetupFGFSIncludes.cmake index d0d625352..d47db41a3 100644 --- a/CMakeModules/SetupFGFSIncludes.cmake +++ b/CMakeModules/SetupFGFSIncludes.cmake @@ -4,6 +4,10 @@ function(setup_fgfs_includes target) target_include_directories(${target} PRIVATE ${PROJECT_SOURCE_DIR}/src/FDM/JSBSim) endif() + if(ENABLE_OSGXR) + target_include_directories(${target} PRIVATE ${osgXR_INCLUDE_DIR}) + endif() + target_include_directories(${target} PRIVATE ${PLIB_INCLUDE_DIR}) target_include_directories(${target} PRIVATE ${PROJECT_SOURCE_DIR}/3rdparty/cjson) # only actually needed for httpd.cxx diff --git a/CMakeModules/SetupFGFSLibraries.cmake b/CMakeModules/SetupFGFSLibraries.cmake index ae6396946..ad5a23332 100644 --- a/CMakeModules/SetupFGFSLibraries.cmake +++ b/CMakeModules/SetupFGFSLibraries.cmake @@ -23,6 +23,10 @@ function(setup_fgfs_libraries target) target_link_libraries(${target} ${X11_LIBRARIES}) endif() + if(ENABLE_OSGXR) + target_link_libraries(${target} osgXR::osgXR) + endif() + target_link_libraries(${target} fgsqlite3 fgvoicesynth fgembeddedresources) target_link_libraries(${target} diff --git a/src/Include/config_cmake.h.in b/src/Include/config_cmake.h.in index bd69ade93..db44de3b1 100644 --- a/src/Include/config_cmake.h.in +++ b/src/Include/config_cmake.h.in @@ -75,3 +75,5 @@ #cmakedefine HAVE_SENTRY #define SENTRY_API_KEY "@sentry_api_key@" + +#cmakedefine ENABLE_OSGXR From db4525abf9dd86f14e94546d3f7d91ed57772053 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Sat, 17 Jul 2021 23:19:14 +0100 Subject: [PATCH 5/5] VR: Implement minimal VR support Implement support for virtual reality headsets via version 0.3 of the osgXR[1] library I've been working on which uses OpenXR. Add a new VRManager class based on osgXR::Manager to implement its callbacks. When osgXR needs a new view created, we build a new camera in the default camera group, and notify osgXR of each new "scene" typed render pass. It also hooks into the CameraInfo's new compositor reload callback to ensure osgXR is updated when the compositors are reloaded. VR settings are controlled by properties, and new --enable-vr / --disable-vr options are implemented to enable/disable VR at start. This is enough to get basic VR for looking around the cockpit, but more work will be required to support a desktop mirror of VR view, VR splash screen, VR GUI, controller interaction, and correct positional sound. [1] https://github.com/amalon/osgXR --- scripts/completion/fg-completion.zsh | 2 + src/Include/config_cmake.h.in | 1 + src/Main/options.cxx | 4 + src/Viewer/CMakeLists.txt | 7 + src/Viewer/VRManager.cxx | 221 +++++++++++++++++++++++++++ src/Viewer/VRManager.hxx | 158 +++++++++++++++++++ src/Viewer/fg_os_osgviewer.cxx | 13 ++ 7 files changed, 406 insertions(+) create mode 100644 src/Viewer/VRManager.cxx create mode 100644 src/Viewer/VRManager.hxx diff --git a/scripts/completion/fg-completion.zsh b/scripts/completion/fg-completion.zsh index 3586fedf4..7fd0dbea7 100755 --- a/scripts/completion/fg-completion.zsh +++ b/scripts/completion/fg-completion.zsh @@ -60,6 +60,8 @@ _fgfs_options=( '--enable-skyblend[Enable sky blending]' \ '--disable-textures[Disable textures]' \ '--enable-textures[Enable textures]' \ + '--disable-vr[Disable VR]' \ + '--enable-vr[Enable VR]' \ '--disable-wireframe[Disable wireframe drawing mode]' \ '--enable-wireframe[Enable wireframe drawing mode]' \ '--notrim[Do NOT attempt to trim the model (only with fdm=jsbsim)]' \ diff --git a/src/Include/config_cmake.h.in b/src/Include/config_cmake.h.in index db44de3b1..b7c67d055 100644 --- a/src/Include/config_cmake.h.in +++ b/src/Include/config_cmake.h.in @@ -27,6 +27,7 @@ #define FLIGHTGEAR_VERSION "@FLIGHTGEAR_VERSION@" #define FLIGHTGEAR_MAJOR_VERSION @FG_VERSION_MAJOR@ #define FLIGHTGEAR_MINOR_VERSION @FG_VERSION_MINOR@ +#define FLIGHTGEAR_PATCH_VERSION @FG_VERSION_PATCH@ #cmakedefine ENABLE_UIUC_MODEL #cmakedefine ENABLE_LARCSIM diff --git a/src/Main/options.cxx b/src/Main/options.cxx index f0f9c43e3..7bb02f76b 100644 --- a/src/Main/options.cxx +++ b/src/Main/options.cxx @@ -1900,6 +1900,10 @@ struct OptionDesc { {"disable-wireframe", false, OPTION_BOOL, "/sim/rendering/wireframe", false, "", 0 }, {"enable-wireframe", false, OPTION_BOOL, "/sim/rendering/wireframe", true, "", 0 }, {"materials-file", true, OPTION_STRING, "/sim/rendering/materials-file", false, "", 0 }, +#ifdef ENABLE_OSGXR + {"disable-vr", false, OPTION_BOOL, "/sim/vr/enabled", false, "", 0 }, + {"enable-vr", false, OPTION_BOOL, "/sim/vr/enabled", true, "", 0 }, +#endif {"disable-terrasync", false, OPTION_BOOL, "/sim/terrasync/enabled", false, "", 0 }, {"enable-terrasync", false, OPTION_BOOL, "/sim/terrasync/enabled", true, "", 0 }, {"terrasync-dir", true, OPTION_IGNORE, "", false, "", 0 }, diff --git a/src/Viewer/CMakeLists.txt b/src/Viewer/CMakeLists.txt index f65fee469..6646fde85 100644 --- a/src/Viewer/CMakeLists.txt +++ b/src/Viewer/CMakeLists.txt @@ -28,8 +28,15 @@ set(HEADERS viewmgr.hxx sview.hxx GraphicsPresets.hxx + VRManager.hxx ) +if (ENABLE_OSGXR) + list(APPEND SOURCES + VRManager.cxx + ) +endif() + if (YES) list(APPEND HEADERS PUICamera.hxx) list(APPEND SOURCES PUICamera.cxx) diff --git a/src/Viewer/VRManager.cxx b/src/Viewer/VRManager.cxx new file mode 100644 index 000000000..8417df65c --- /dev/null +++ b/src/Viewer/VRManager.cxx @@ -0,0 +1,221 @@ +// Copyright (C) 2021 James Hogan +// +// 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 +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "VRManager.hxx" +#include "WindowBuilder.hxx" +#include "renderer.hxx" + +#include + +#include + +#include
+#include
+ +namespace flightgear +{ + +VRManager::VRManager() : + _reloadCompositorCallback(new ReloadCompositorCallback(this)), + _propXrLayersValidation("/sim/vr/openxr/layers/validation"), + _propXrExtensionsDepthInfo("/sim/vr/openxr/extensions/depth-info"), + _propXrRuntimeName("/sim/vr/openxr/runtime/name"), + _propXrSystemName("/sim/vr/openxr/system/name"), + _propStateString("/sim/vr/state-string"), + _propPresent("/sim/vr/present"), + _propRunning("/sim/vr/running"), + _propEnabled("/sim/vr/enabled"), + _propDepthInfo("/sim/vr/depth-info"), + _propValidationLayer("/sim/vr/validation-layer"), + _propMode("/sim/vr/mode"), + _propSwapchainMode("/sim/vr/swapchain-mode"), + _listenerEnabled(this, &osgXR::Manager::setEnabled), + _listenerDepthInfo(this, &VRManager::setDepthInfo), + _listenerValidationLayer(this, &VRManager::setValidationLayer), + _listenerMode(this, &VRManager::setVRMode), + _listenerSwapchainMode(this, &VRManager::setSwapchainMode) +{ + uint32_t fgVersion = (FLIGHTGEAR_MAJOR_VERSION << 16 | + FLIGHTGEAR_MINOR_VERSION << 8 | + FLIGHTGEAR_PATCH_VERSION); + _settings->setApp("FlightGear", fgVersion); + _settings->preferEnvBlendMode(osgXR::Settings::OPAQUE); + + // Hook into viewer, but don't enable VR just yet + osgViewer::View *view = globals->get_renderer()->getView(); + if (view) { + setViewer(globals->get_renderer()->getViewerBase()); + view->apply(this); + } + + syncReadOnlyProperties(); + + _propEnabled.node(true)->addChangeListener(&_listenerEnabled, true); + _propDepthInfo.node(true)->addChangeListener(&_listenerDepthInfo, true); + _propValidationLayer.node(true)->addChangeListener(&_listenerValidationLayer, true); + _propMode.node(true)->addChangeListener(&_listenerMode, true); + _propSwapchainMode.node(true)->addChangeListener(&_listenerSwapchainMode, true); +} + +VRManager *VRManager::instance() +{ + static osg::ref_ptr single = new VRManager; + return single; +} + +void VRManager::syncProperties() +{ + // If the state has changed, properties may need synchronising + if (checkAndResetStateChanged()) { + syncReadOnlyProperties(); + syncSettingProperties(); + } +} + +void VRManager::syncReadOnlyProperties() +{ + _propXrLayersValidation = hasValidationLayer(); + _propXrExtensionsDepthInfo = hasDepthInfoExtension(); + _propXrRuntimeName = getRuntimeName(); + _propXrSystemName = getSystemName(); + + _propStateString = getStateString(); + _propPresent = getPresent(); + _propRunning = isRunning(); +} + +void VRManager::syncSettingProperties() +{ + bool enabled = getEnabled(); + if (_propEnabled != enabled) + _propEnabled = enabled; +} + +void VRManager::setValidationLayer(bool validationLayer) +{ + _settings->setValidationLayer(validationLayer); + syncSettings(); +} + +void VRManager::setDepthInfo(bool depthInfo) +{ + _settings->setDepthInfo(depthInfo); + syncSettings(); +} + +void VRManager::setVRMode(const char * mode) +{ + osgXR::Settings::VRMode vrMode = osgXR::Settings::VRMODE_AUTOMATIC; + + if (strcmp(mode, "AUTOMATIC") == 0) { + vrMode = osgXR::Settings::VRMODE_AUTOMATIC; + } else if (strcmp(mode, "SLAVE_CAMERAS") == 0) { + vrMode = osgXR::Settings::VRMODE_SLAVE_CAMERAS; + } else if (strcmp(mode, "SCENE_VIEW") == 0) { + vrMode = osgXR::Settings::VRMODE_SCENE_VIEW; + } + + _settings->setVRMode(vrMode); + syncSettings(); +} + +void VRManager::setSwapchainMode(const char * mode) +{ + osgXR::Settings::SwapchainMode swapchainMode = osgXR::Settings::SWAPCHAIN_AUTOMATIC; + + if (strcmp(mode, "AUTOMATIC") == 0) { + swapchainMode = osgXR::Settings::SWAPCHAIN_AUTOMATIC; + } else if (strcmp(mode,"MULTIPLE") == 0) { + swapchainMode = osgXR::Settings::SWAPCHAIN_MULTIPLE; + } else if (strcmp(mode,"SINGLE") == 0) { + swapchainMode = osgXR::Settings::SWAPCHAIN_SINGLE; + } + + _settings->setSwapchainMode(swapchainMode); + syncSettings(); +} + +void VRManager::update() +{ + osgXR::Manager::update(); + syncProperties(); +} + +void VRManager::doCreateView(osgXR::View *xrView) +{ + // Restarted in osgXR::Manager::update() + _viewer->stopThreading(); + + // Construct a property tree for the camera + SGPropertyNode_ptr camNode = new SGPropertyNode; + WindowBuilder *windowBuilder = WindowBuilder::getWindowBuilder(); + setValue(camNode->getNode("window/name", true), + windowBuilder->getDefaultWindowName()); + + // Build a camera + CameraGroup *cgroup = CameraGroup::getDefault(); + CameraInfo *info = cgroup->buildCamera(camNode); + + // Notify osgXR about the new compositor's scene slave cameras + if (info) { + _camInfos[xrView] = info; + _xrViews[info] = xrView; + info->reloadCompositorCallback = _reloadCompositorCallback; + + postReloadCompositor(cgroup, info); + } +} + +void VRManager::doDestroyView(osgXR::View *xrView) +{ + // Restarted in osgXR::Manager::update() + _viewer->stopThreading(); + + CameraGroup *cgroup = CameraGroup::getDefault(); + auto it = _camInfos.find(xrView); + if (it != _camInfos.end()) { + osg::ref_ptr info = (*it).second; + _camInfos.erase(it); + + auto it2 = _xrViews.find(info.get()); + if (it2 != _xrViews.end()) + _xrViews.erase(it2); + + cgroup->removeCamera(info.get()); + } +} + +void VRManager::preReloadCompositor(CameraGroup *cgroup, CameraInfo *info) +{ + osgXR::View *xrView = _xrViews[info]; + + auto& passes = info->compositor->getPassList(); + for (auto& pass: passes) + if (pass->type == "scene") + xrView->removeSlave(pass->camera); +} + +void VRManager::postReloadCompositor(CameraGroup *cgroup, CameraInfo *info) +{ + osgXR::View *xrView = _xrViews[info]; + + auto& passes = info->compositor->getPassList(); + for (auto& pass: passes) + if (pass->type == "scene") + xrView->addSlave(pass->camera); +} + +} diff --git a/src/Viewer/VRManager.hxx b/src/Viewer/VRManager.hxx new file mode 100644 index 000000000..db97f682d --- /dev/null +++ b/src/Viewer/VRManager.hxx @@ -0,0 +1,158 @@ +// Copyright (C) 2021 James Hogan +// +// 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 +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef VRMANAGER_HXX +#define VRMANAGER_HXX 1 + +#include + +#ifdef ENABLE_OSGXR + +#include +#include + +#include + +#include +#include + +#include "CameraGroup.hxx" + +#include + +namespace flightgear +{ + +class VRManager : public osgXR::Manager +{ + public: + + class ReloadCompositorCallback : public CameraInfo::ReloadCompositorCallback + { + public: + + ReloadCompositorCallback(VRManager *manager) : + _manager(manager) + { + }; + + virtual void preReloadCompositor(CameraGroup *cgroup, CameraInfo *info) + { + _manager->preReloadCompositor(cgroup, info); + } + + virtual void postReloadCompositor(CameraGroup *cgroup, CameraInfo *info) + { + _manager->postReloadCompositor(cgroup, info); + } + + protected: + + osg::observer_ptr _manager; + }; + + VRManager(); + + static VRManager *instance(); + + void syncProperties(); + void syncReadOnlyProperties(); + void syncSettingProperties(); + + // Settings + + void setValidationLayer(bool validationLayer); + void setDepthInfo(bool depthInfo); + + void setVRMode(const char * mode); + void setSwapchainMode(const char * mode); + + // osgXR::Manager overrides + + void update() override; + + void doCreateView(osgXR::View *xrView) override; + void doDestroyView(osgXR::View *xrView) override; + + void preReloadCompositor(CameraGroup *cgroup, CameraInfo *info); + void postReloadCompositor(CameraGroup *cgroup, CameraInfo *info); + + protected: + + typedef std::map> XRViewToCamInfo; + XRViewToCamInfo _camInfos; + + typedef std::map> CamInfoToXRView; + CamInfoToXRView _xrViews; + + osg::ref_ptr _reloadCompositorCallback; + + // Properties + + SGPropObjBool _propXrLayersValidation; + SGPropObjBool _propXrExtensionsDepthInfo; + SGPropObjString _propXrRuntimeName; + SGPropObjString _propXrSystemName; + + SGPropObjString _propStateString; + SGPropObjBool _propPresent; + SGPropObjBool _propRunning; + + SGPropObjBool _propEnabled; + SGPropObjBool _propDepthInfo; + SGPropObjBool _propValidationLayer; + SGPropObjString _propMode; + SGPropObjString _propSwapchainMode; + + // Property listeners + + template + class Listener : public SGPropertyChangeListener + { + public: + typedef void (VRManager::*SetterFn)(T v); + + Listener(VRManager *manager, SetterFn setter) : + _manager(manager), + _setter(setter) + { + } + + void valueChanged(SGPropertyNode *node) override + { + (_manager->*_setter)(node->template getValue()); + } + + protected: + + VRManager *_manager; + SetterFn _setter; + }; + typedef Listener ListenerBool; + typedef Listener ListenerString; + + ListenerBool _listenerEnabled; + ListenerBool _listenerDepthInfo; + ListenerBool _listenerValidationLayer; + ListenerString _listenerMode; + ListenerString _listenerSwapchainMode; +}; + +} + +#endif // ENABLE_OSGXR + +#endif diff --git a/src/Viewer/fg_os_osgviewer.cxx b/src/Viewer/fg_os_osgviewer.cxx index 600fbcf73..dae44a64d 100644 --- a/src/Viewer/fg_os_osgviewer.cxx +++ b/src/Viewer/fg_os_osgviewer.cxx @@ -52,6 +52,7 @@ #include "renderer.hxx" #include "CameraGroup.hxx" #include "FGEventHandler.hxx" +#include "VRManager.hxx" #include "WindowBuilder.hxx" #include "WindowSystemAdapter.hxx" #include
@@ -328,6 +329,12 @@ void fgOSResetProperties() fgTie("/sim/rendering/osg-displaysettings/double-buffer", displaySettings, &DisplaySettings::getDoubleBuffer, &DisplaySettings::setDoubleBuffer ); fgTie("/sim/rendering/osg-displaysettings/depth-buffer", displaySettings, &DisplaySettings::getDepthBuffer, &DisplaySettings::setDepthBuffer ); fgTie("/sim/rendering/osg-displaysettings/rgb", displaySettings, &DisplaySettings::getRGB, &DisplaySettings::setRGB ); + +#ifdef ENABLE_OSGXR + fgSetBool("/sim/vr/built", true); +#else + fgSetBool("/sim/vr/built", false); +#endif } @@ -389,6 +396,9 @@ int fgOSMainLoop() } } globals->get_renderer()->update(); +#ifdef ENABLE_OSGXR + VRManager::instance()->update(); +#endif viewer_base->frame( globals->get_sim_time_sec() ); } @@ -437,6 +447,9 @@ void fgOSCloseWindow() viewer_base->stopThreading(); } } +#ifdef ENABLE_OSGXR + VRManager::instance()->destroyAndWait(); +#endif FGScenery::resetPagerSingleton(); flightgear::addSentryBreadcrumb("fgOSCloseWindow, clearing camera group", "info"); flightgear::CameraGroup::setDefault(NULL);