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 <osgXR/Settings> + +#include <simgear/scene/viewer/CompositorPass.hxx> + +#include <Main/fg_props.hxx> +#include <Main/globals.hxx> + +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<VRManager> 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<CameraInfo> 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 <config.h> + +#ifdef ENABLE_OSGXR + +#include <osg/ref_ptr> +#include <osg/observer_ptr> + +#include <osgXR/Manager> + +#include <simgear/props/propertyObject.hxx> +#include <simgear/scene/viewer/CompositorPass.hxx> + +#include "CameraGroup.hxx" + +#include <map> + +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<VRManager> _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<osgXR::View *, osg::ref_ptr<CameraInfo>> XRViewToCamInfo; + XRViewToCamInfo _camInfos; + + typedef std::map<CameraInfo *, osg::ref_ptr<osgXR::View>> CamInfoToXRView; + CamInfoToXRView _xrViews; + + osg::ref_ptr<ReloadCompositorCallback> _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 <typename T> + 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<T>()); + } + + protected: + + VRManager *_manager; + SetterFn _setter; + }; + typedef Listener<bool> ListenerBool; + typedef Listener<const char *> 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 <Main/sentryIntegration.hxx> @@ -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);