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
This commit is contained in:
parent
dcdda1044a
commit
db4525abf9
7 changed files with 406 additions and 0 deletions
|
@ -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)]' \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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)
|
||||
|
|
221
src/Viewer/VRManager.cxx
Normal file
221
src/Viewer/VRManager.cxx
Normal file
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
158
src/Viewer/VRManager.hxx
Normal file
158
src/Viewer/VRManager.hxx
Normal file
|
@ -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
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue