Files
flightgear/src/Viewer/VRManager.cxx
2022-10-20 20:29:11 +08:00

299 lines
9.4 KiB
C++

// 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/util/RenderConstants.hxx>
#include <simgear/scene/viewer/CompositorPass.hxx>
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
namespace flightgear
{
// Unfortunately, this can't be scoped inside VRManager::instance().
// If its initialisation completes after main() calls atexit(fgExitCleanup),
// then its destruction should take place before fgExitCleanup() is called.
static osg::ref_ptr<VRManager> managerInstance;
VRManager::VRManager() :
_reloadCompositorCallback(new ReloadCompositorCallback(this)),
_propXrLayersValidation("/sim/vr/openxr/layers/validation"),
_propXrExtensionsDepthInfo("/sim/vr/openxr/extensions/depth-info"),
_propXrExtensionsVisibilityMask("/sim/vr/openxr/extensions/visibility-mask"),
_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"),
_propVisibilityMask("/sim/vr/visibility-mask"),
_propValidationLayer("/sim/vr/validation-layer"),
_propMode("/sim/vr/mode"),
_propSwapchainMode("/sim/vr/swapchain-mode"),
_propMirrorEnabled("/sim/vr/mirror-enabled"),
_propMirrorMode("/sim/vr/mirror-mode"),
_listenerEnabled(this, &osgXR::Manager::setEnabled),
_listenerDepthInfo(this, &VRManager::setDepthInfo),
_listenerVisibilityMask(this, &VRManager::setVisibilityMask),
_listenerValidationLayer(this, &VRManager::setValidationLayer),
_listenerMode(this, &VRManager::setVRMode),
_listenerSwapchainMode(this, &VRManager::setSwapchainMode),
_listenerMirrorMode(this, &VRManager::setMirrorMode)
{
uint32_t fgVersion = (FLIGHTGEAR_MAJOR_VERSION << 16 |
FLIGHTGEAR_MINOR_VERSION << 8 |
FLIGHTGEAR_PATCH_VERSION);
_settings->setApp("FlightGear", fgVersion);
_settings->preferEnvBlendMode(osgXR::Settings::BLEND_MODE_OPAQUE);
// Inform osgXR what node masks to use
setVisibilityMaskNodeMasks(simgear::NodeMask::LEFT_BIT,
simgear::NodeMask::RIGHT_BIT);
// 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);
_propVisibilityMask.node(true)->addChangeListener(&_listenerVisibilityMask, true);
_propValidationLayer.node(true)->addChangeListener(&_listenerValidationLayer, true);
_propMode.node(true)->addChangeListener(&_listenerMode, true);
_propSwapchainMode.node(true)->addChangeListener(&_listenerSwapchainMode, true);
_propMirrorMode.node(true)->addChangeListener(&_listenerMirrorMode, true);
// No need for a change listener, but it should still be resolvable
_propMirrorEnabled.node(true);
}
VRManager *VRManager::instance()
{
static bool initialised = false;
if (!initialised) {
managerInstance = new VRManager;
initialised = true;
}
return managerInstance;
}
void VRManager::syncProperties()
{
// If the state has changed, properties may need synchronising
if (checkAndResetStateChanged()) {
syncReadOnlyProperties();
syncSettingProperties();
}
}
void VRManager::syncReadOnlyProperties()
{
_propXrLayersValidation = hasValidationLayer();
_propXrExtensionsDepthInfo = hasDepthInfoExtension();
_propXrExtensionsVisibilityMask = hasVisibilityMaskExtension();
_propXrRuntimeName = getRuntimeName();
_propXrSystemName = getSystemName();
_propStateString = getStateString();
_propPresent = getPresent();
_propRunning = isRunning();
}
void VRManager::syncSettingProperties()
{
bool enabled = getEnabled();
if (_propEnabled != enabled)
_propEnabled = enabled;
}
bool VRManager::getUseMirror() const
{
return _propMirrorEnabled && isRunning();
}
void VRManager::setValidationLayer(bool validationLayer)
{
_settings->setValidationLayer(validationLayer);
syncSettings();
}
void VRManager::setDepthInfo(bool depthInfo)
{
_settings->setDepthInfo(depthInfo);
syncSettings();
}
void VRManager::setVisibilityMask(bool visibilityMask)
{
_settings->setVisibilityMask(visibilityMask);
syncSettings();
}
void VRManager::setVRMode(const std::string& mode)
{
osgXR::Settings::VRMode vrMode = osgXR::Settings::VRMODE_AUTOMATIC;
if (mode == "AUTOMATIC") {
vrMode = osgXR::Settings::VRMODE_AUTOMATIC;
} else if (mode == "SLAVE_CAMERAS") {
vrMode = osgXR::Settings::VRMODE_SLAVE_CAMERAS;
} else if (mode == "SCENE_VIEW") {
vrMode = osgXR::Settings::VRMODE_SCENE_VIEW;
}
_settings->setVRMode(vrMode);
syncSettings();
}
void VRManager::setSwapchainMode(const std::string& mode)
{
osgXR::Settings::SwapchainMode swapchainMode = osgXR::Settings::SWAPCHAIN_AUTOMATIC;
if (mode == "AUTOMATIC") {
swapchainMode = osgXR::Settings::SWAPCHAIN_AUTOMATIC;
} else if (mode == "MULTIPLE") {
swapchainMode = osgXR::Settings::SWAPCHAIN_MULTIPLE;
} else if (mode == "SINGLE") {
swapchainMode = osgXR::Settings::SWAPCHAIN_SINGLE;
}
_settings->setSwapchainMode(swapchainMode);
syncSettings();
}
void VRManager::setMirrorMode(const std::string& mode)
{
osgXR::MirrorSettings::MirrorMode mirrorMode = osgXR::MirrorSettings::MIRROR_AUTOMATIC;
int viewIndex = -1;
if (mode == "AUTOMATIC") {
mirrorMode = osgXR::MirrorSettings::MIRROR_AUTOMATIC;
} else if (mode == "NONE") {
mirrorMode = osgXR::MirrorSettings::MIRROR_NONE;
} else if (mode == "LEFT") {
mirrorMode = osgXR::MirrorSettings::MIRROR_SINGLE;
viewIndex = 0;
} else if (mode == "RIGHT") {
mirrorMode = osgXR::MirrorSettings::MIRROR_SINGLE;
viewIndex = 1;
} else if (mode == "LEFT_RIGHT") {
mirrorMode = osgXR::MirrorSettings::MIRROR_LEFT_RIGHT;
}
_settings->getMirrorSettings().setMirror(mirrorMode, viewIndex);
}
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::onRunning()
{
// Reload compositors to trigger switch to mirror of VR
CameraGroup *cgroup = CameraGroup::getDefault();
reloadCompositors(cgroup);
}
void VRManager::onStopped()
{
// As long as we're not in the process of destroying FlightGear, reload
// compositors to trigger switch away from mirror of VR
if (!isDestroying())
{
CameraGroup *cgroup = CameraGroup::getDefault();
reloadCompositors(cgroup);
}
}
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);
}
}