From 7db47beb0e4aca554277173cab6524bc51d92a84 Mon Sep 17 00:00:00 2001 From: James Hogan <james@albanarts.com> Date: Thu, 5 Aug 2021 09:29:28 +0100 Subject: [PATCH] VR: Implement mirroring of VR to desktop Set up the default camera as a mirror of the VR view when needed, using a new CameraInfo flag named VR_MIRROR based on a camera property "vr-mirror" which is set on the default camera group. When set this flag causes the compositor to be constructed (and reloaded) using buildVRMirrorCompositor() and osgXR's mirror camera setup code, rather than the usual Compositor::create(). --- src/Viewer/CameraGroup.cxx | 81 +++++++++++++++++++++++++++++++++----- src/Viewer/CameraGroup.hxx | 10 ++++- src/Viewer/VRManager.cxx | 18 +++++++++ src/Viewer/VRManager.hxx | 3 ++ 4 files changed, 101 insertions(+), 11 deletions(-) diff --git a/src/Viewer/CameraGroup.cxx b/src/Viewer/CameraGroup.cxx index 3ec5f6853..586e5bbad 100644 --- a/src/Viewer/CameraGroup.cxx +++ b/src/Viewer/CameraGroup.cxx @@ -24,6 +24,7 @@ #include "WindowBuilder.hxx" #include "WindowSystemAdapter.hxx" #include "sview.hxx" +#include "VRManager.hxx" #include <simgear/math/SGRect.hxx> #include <simgear/props/props.hxx> @@ -494,6 +495,10 @@ CameraInfo* CameraGroup::buildCamera(SGPropertyNode* cameraNode) return nullptr; } + // Set vr-mirror flag so camera switches to VR mirror when appropriate. + if (cameraNode->getBoolValue("vr-mirror", false)) + cameraFlags |= CameraInfo::VR_MIRROR; + osg::Matrix vOff; const SGPropertyNode* viewNode = cameraNode->getNode("view"); if (viewNode) { @@ -700,11 +705,16 @@ CameraInfo* CameraGroup::buildCamera(SGPropertyNode* cameraNode) SViewSetCompositorParams(options, compositor_path); - Compositor *compositor = Compositor::create(_viewer, - window->gc, - viewport, - compositor_path, - options); + Compositor *compositor = nullptr; + if (info->flags & CameraInfo::VR_MIRROR) + compositor = buildVRMirrorCompositor(window->gc, viewport); + if (!compositor) + compositor = Compositor::create(_viewer, + window->gc, + viewport, + compositor_path, + options); + if (compositor) { info->compositor.reset(compositor); } else { @@ -807,6 +817,50 @@ void CameraGroup::buildGUICamera(SGPropertyNode* cameraNode, camera->setStats(0); } +Compositor *CameraGroup::buildVRMirrorCompositor(osg::GraphicsContext* gc, + osg::Viewport *viewport) +{ +#ifdef ENABLE_OSGXR + if (VRManager::instance()->getUseMirror()) { + Camera* camera = new Camera; + camera->setName("VRMirror"); + camera->setAllowEventFocus(false); + camera->setGraphicsContext(gc); + camera->setViewport(viewport); + camera->setClearMask(0); + camera->setInheritanceMask(CullSettings::ALL_VARIABLES + & ~(CullSettings::COMPUTE_NEAR_FAR_MODE + | CullSettings::CULLING_MODE + | CullSettings::CLEAR_MASK + )); + camera->setComputeNearFarMode(CullSettings::DO_NOT_COMPUTE_NEAR_FAR); + camera->setCullingMode(CullSettings::NO_CULLING); + camera->setProjectionResizePolicy(Camera::FIXED); + + // The camera group will always update the camera + camera->setReferenceFrame(Transform::ABSOLUTE_RF); + + // Mirror camera needs to be drawn after VR cameras and before GUI + camera->setRenderOrder(Camera::POST_RENDER, 9000); + + // Let osgXR do the mirror camera setup + VRManager::instance()->setupMirrorCamera(camera); + + Pass *pass = new Pass; + pass->camera = camera; + pass->useMastersSceneData = false; + + // We just build a simple Compositor directly from C++ space that + // encapsulates a single osg::Camera. + Compositor *compositor = new Compositor(_viewer, gc, viewport); + compositor->addPass(pass); + + return compositor; + } +#endif + return nullptr; +} + CameraGroup* CameraGroup::buildCameraGroup(osgViewer::View* view, SGPropertyNode* gnode) { @@ -994,11 +1048,16 @@ void reloadCompositors(CameraGroup *cgroup) std::string compositor_path = info->compositor_path.empty() ? fgGetString("/sim/rendering/default-compositor", "Compositor/default") : info->compositor_path; - info->compositor.reset(Compositor::create(cgroup->_viewer, - gc, - viewport, - compositor_path, - options)); + Compositor *compositor = nullptr; + if (info->flags & CameraInfo::VR_MIRROR) + compositor = cgroup->buildVRMirrorCompositor(gc, viewport); + if (!compositor) + compositor = Compositor::create(cgroup->_viewer, + gc, + viewport, + compositor_path, + options); + info->compositor.reset(compositor); if (info->reloadCompositorCallback.valid()) info->reloadCompositorCallback->postReloadCompositor(cgroup, info); @@ -1041,6 +1100,8 @@ void CameraGroup::buildDefaultGroup(osgViewer::View* viewer) masterCamera = cgroupNode->getChild("camera", cameras.size(), true); setValue(masterCamera->getNode("window/name", true), windowBuilder->getDefaultWindowName()); + // Use VR mirror compositor when VR is enabled. + setValue(masterCamera->getNode("vr-mirror", true), true); } SGPropertyNode* nameNode = masterCamera->getNode("window/name"); if (nameNode) diff --git a/src/Viewer/CameraGroup.hxx b/src/Viewer/CameraGroup.hxx index 6e0bc58aa..ce5ccf363 100644 --- a/src/Viewer/CameraGroup.hxx +++ b/src/Viewer/CameraGroup.hxx @@ -70,7 +70,8 @@ struct CameraInfo : public osg::Referenced camera. */ FIXED_NEAR_FAR = 0x20, /**< take the near far values in the projection for real. */ - ENABLE_MASTER_ZOOM = 0x40 /**< Can apply the zoom algorithm. */ + ENABLE_MASTER_ZOOM = 0x40, /**< Can apply the zoom algorithm. */ + VR_MIRROR = 0x80 /**< Switch to a mirror of VR. */ }; CameraInfo(unsigned flags_) : @@ -224,6 +225,13 @@ protected: float _zFar; float _nearField; + /** Create a compositor for a VR mirror. + * @param gc the graphics context to use. + * @param viewport the viewport to render to. + * @return a new compositor or nullptr if no VR mirror is needed. + */ + simgear::compositor::Compositor *buildVRMirrorCompositor(osg::GraphicsContext *gc, + osg::Viewport *viewport); /** Build a complete CameraGroup from a property node. * @param viewer the viewer associated with this camera group. * @param wbuilder the window builder to be used for this camera group. diff --git a/src/Viewer/VRManager.cxx b/src/Viewer/VRManager.cxx index 6da3b72d8..5159614b3 100644 --- a/src/Viewer/VRManager.cxx +++ b/src/Viewer/VRManager.cxx @@ -232,6 +232,24 @@ void VRManager::doDestroyView(osgXR::View *xrView) } } +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]; diff --git a/src/Viewer/VRManager.hxx b/src/Viewer/VRManager.hxx index 6c1c61020..71694a1bf 100644 --- a/src/Viewer/VRManager.hxx +++ b/src/Viewer/VRManager.hxx @@ -90,6 +90,9 @@ class VRManager : public osgXR::Manager void doCreateView(osgXR::View *xrView) override; void doDestroyView(osgXR::View *xrView) override; + void onRunning() override; + void onStopped() override; + void preReloadCompositor(CameraGroup *cgroup, CameraInfo *info); void postReloadCompositor(CameraGroup *cgroup, CameraInfo *info);