From bb0d7fc0a71c12d49d62f67ae567a467d3db14c4 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Wed, 9 Feb 2022 23:11:17 +0000 Subject: [PATCH] src/Viewer: Move splash to cam group camera Move the splash screen from the scene root to a camera group camera, just below the GUI camera. This has a number of advantages, mainly VR related: - The splash FBO will only be rendered once per frame. Currently it appears to be rendered for both near & far cameras, and with stereoscopic rendering for both left & right (even though it splats right across both views at the moment), so 2 or 4 times. - It makes it possible to render the splash fullscreen on the desktop window, while presenting it on a 3d quad in VR (possibly via an OpenXR composition layer so the runtime can keep rendering it smoothly while FlightGear framerate drops during loading. --- src/Viewer/CameraGroup.cxx | 91 +++++++++++++++++++++++++++++++++++--- src/Viewer/CameraGroup.hxx | 13 +++++- src/Viewer/renderer.cxx | 14 ++++-- src/Viewer/renderer.hxx | 4 +- 4 files changed, 111 insertions(+), 11 deletions(-) diff --git a/src/Viewer/CameraGroup.cxx b/src/Viewer/CameraGroup.cxx index e7de0602b..92bd7078c 100644 --- a/src/Viewer/CameraGroup.cxx +++ b/src/Viewer/CameraGroup.cxx @@ -23,6 +23,7 @@ #include "FGEventHandler.hxx" #include "WindowBuilder.hxx" #include "WindowSystemAdapter.hxx" +#include "splash.hxx" #include "sview.hxx" #include "VRManager.hxx" @@ -211,7 +212,7 @@ void CameraGroup::update(const osg::Vec3d& position, for (const auto &info : _cameras) { osg::Matrix view_matrix; - if (info->flags & CameraInfo::GUI) + if (info->flags & (CameraInfo::SPLASH | CameraInfo::GUI)) view_matrix = osg::Matrix::identity(); else if ((info->flags & CameraInfo::VIEW_ABSOLUTE) != 0) view_matrix = info->viewOffset; @@ -219,7 +220,7 @@ void CameraGroup::update(const osg::Vec3d& position, view_matrix = masterView * info->viewOffset; osg::Matrix proj_matrix; - if (info->flags & CameraInfo::GUI) { + if (info->flags & (CameraInfo::SPLASH | CameraInfo::GUI)) { const osg::GraphicsContext::Traits *traits = info->compositor->getGraphicsContext()->getTraits(); proj_matrix = osg::Matrix::ortho2D(0, traits->width, 0, traits->height); @@ -254,7 +255,8 @@ void CameraGroup::update(const osg::Vec3d& position, } osg::Matrix new_proj_matrix = proj_matrix; - if ((info->flags & CameraInfo::GUI) == 0 && + if ((info->flags & CameraInfo::SPLASH) == 0 && + (info->flags & CameraInfo::GUI) == 0 && (info->flags & CameraInfo::FIXED_NEAR_FAR) == 0) { makeNewProjMat(proj_matrix, _zNear, _zFar, new_proj_matrix); } @@ -741,6 +743,78 @@ void CameraGroup::removeCamera(CameraInfo *info) } } +void CameraGroup::buildSplashCamera(SGPropertyNode* cameraNode, + GraphicsWindow* window) +{ + WindowBuilder* wBuild = WindowBuilder::getWindowBuilder(); + const SGPropertyNode* windowNode = (cameraNode + ? cameraNode->getNode("window") + : 0); + if (!window && windowNode) { + // New style window declaration / definition + window = wBuild->buildWindow(windowNode, true /*isMainWindow*/); + } + + if (!window) { // buildWindow can fail + SG_LOG(SG_VIEW, SG_WARN, "CameraGroup::buildSplashCamera: failed to build a window"); + return; + } + + Camera* camera = new Camera; + camera->setName("SplashCamera"); + camera->setAllowEventFocus(false); + camera->setGraphicsContext(window->gc.get()); + // If a viewport isn't set on the camera, then it's hard to dig it + // out of the SceneView objects in the viewer, and the coordinates + // of mouse events are somewhat bizzare. + osg::Viewport* viewport = new osg::Viewport( + 0, 0, window->gc->getTraits()->width, window->gc->getTraits()->height); + camera->setViewport(viewport); + camera->setClearMask(0); + camera->setInheritanceMask(CullSettings::ALL_VARIABLES + & ~(CullSettings::COMPUTE_NEAR_FAR_MODE + | CullSettings::CULLING_MODE + | CullSettings::CLEAR_MASK + )); + camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR); + camera->setCullingMode(osg::CullSettings::NO_CULLING); + camera->setProjectionResizePolicy(osg::Camera::FIXED); + + // The camera group will always update the camera + camera->setReferenceFrame(Transform::ABSOLUTE_RF); + + // XXX Camera needs to be drawn just before GUI; eventually the render order + // should be assigned by a camera manager. + camera->setRenderOrder(osg::Camera::POST_RENDER, 9999); + + // Add splash screen! + camera->addChild(globals->get_renderer()->getSplash()); + + Pass* pass = new Pass; + pass->camera = camera; + pass->useMastersSceneData = false; + + // For now we just build a simple Compositor directly from C++ space that + // encapsulates a single osg::Camera. This could be improved by letting + // users change the Compositor config in XML space, for example to be able + // to add post-processing to a HUD. + // However, since many other parts of FG require direct access to the GUI + // osg::Camera object, this is fine for now. + Compositor* compositor = new Compositor(_viewer, window->gc, viewport); + compositor->addPass(pass); + + const int cameraFlags = CameraInfo::SPLASH; + CameraInfo* info = new CameraInfo(cameraFlags); + info->name = "Splash camera"; + info->viewOffset = osg::Matrix::identity(); + info->projOffset = osg::Matrix::identity(); + info->compositor.reset(compositor); + _cameras.push_back(info); + + // Disable statistics for the splash camera. + camera->setStats(0); +} + void CameraGroup::buildGUICamera(SGPropertyNode* cameraNode, GraphicsWindow* window) { @@ -874,6 +948,8 @@ CameraGroup* CameraGroup::buildCameraGroup(osgViewer::View* view, cgroup->buildCamera(pNode); } else if (name == "window") { WindowBuilder::getWindowBuilder()->buildWindow(pNode); + } else if (name == "splash") { + cgroup->buildSplashCamera(pNode); } else if (name == "gui") { cgroup->buildGUICamera(pNode); } @@ -1033,8 +1109,8 @@ void reloadCompositors(CameraGroup *cgroup) Compositor::resetOrderOffset(); for (auto &info : cgroup->_cameras) { - // Ignore the GUI camera - if (info->flags & CameraInfo::GUI) + // Ignore the splash & GUI camera + if (info->flags & (CameraInfo::SPLASH | CameraInfo::GUI)) continue; // Get the viewport and the graphics context from the old Compositor osg::ref_ptr viewport = info->compositor->getViewport(); @@ -1109,9 +1185,12 @@ void CameraGroup::buildDefaultGroup(osgViewer::View* viewer) setValue(masterCamera->getNode("vr-mirror", true), true); } SGPropertyNode* nameNode = masterCamera->getNode("window/name"); - if (nameNode) + if (nameNode) { setValue(cgroupNode->getNode("gui/window/name", true), nameNode->getStringValue()); + setValue(cgroupNode->getNode("splash/window/name", true), + nameNode->getStringValue()); + } } CameraGroup* cgroup = buildCameraGroup(viewer, cgroupNode); diff --git a/src/Viewer/CameraGroup.hxx b/src/Viewer/CameraGroup.hxx index 076c6f660..fb0cc0840 100644 --- a/src/Viewer/CameraGroup.hxx +++ b/src/Viewer/CameraGroup.hxx @@ -71,7 +71,8 @@ struct CameraInfo : public osg::Referenced FIXED_NEAR_FAR = 0x20, /**< take the near far values in the projection for real. */ ENABLE_MASTER_ZOOM = 0x40, /**< Can apply the zoom algorithm. */ - VR_MIRROR = 0x80 /**< Switch to a mirror of VR. */ + VR_MIRROR = 0x80, /**< Switch to a mirror of VR. */ + SPLASH = 0x100 /**< For splash screen. */ }; CameraInfo(unsigned flags_) : @@ -165,6 +166,16 @@ public: * @param info the camera info to remove. */ void removeCamera(CameraInfo *info); + /** Create a camera from properties that will draw the splash screen and add + * it to the camera group. + * @param cameraNode the property node. This can be 0, in which + * case a default GUI camera is created. + * @param window the GraphicsWindow to use for the splash camera. If + * this is 0, the window is determined from the property node. + * @return a CameraInfo object for the GUI camera. + */ + void buildSplashCamera(SGPropertyNode* cameraNode, + GraphicsWindow* window = 0); /** 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 diff --git a/src/Viewer/renderer.cxx b/src/Viewer/renderer.cxx index 71c5a8e84..566b60b18 100644 --- a/src/Viewer/renderer.cxx +++ b/src/Viewer/renderer.cxx @@ -345,7 +345,8 @@ bool FGScenerySwitchCallback::scenery_enabled = false; FGRenderer::FGRenderer() : _sky(NULL), - MaximumTextureSize(0) + MaximumTextureSize(0), + _splash(nullptr) { _root = new osg::Group; _root->setName("fakeRoot"); @@ -427,8 +428,7 @@ FGRenderer::preinit( void ) view->setDatabasePager(FGScenery::getPagerSingleton()); _quickDrawable = nullptr; - _splash = new SplashScreen; - _viewerSceneRoot->addChild(_splash); + getSplash(); if (composite_viewer) { // Nothing to do - composite_viewer->addView() will tell view to use @@ -1166,6 +1166,14 @@ FGRenderer::setPlanes( double zNear, double zFar ) // _planes->set( osg::Vec3f( - zFar, - zFar * zNear, zFar - zNear ) ); } +SplashScreen* +FGRenderer::getSplash() +{ + if (!_splash) + _splash = new SplashScreen; + return _splash; +} + bool fgDumpSceneGraphToFile(const char* filename) { diff --git a/src/Viewer/renderer.hxx b/src/Viewer/renderer.hxx index e87afedae..d84a88ffb 100644 --- a/src/Viewer/renderer.hxx +++ b/src/Viewer/renderer.hxx @@ -99,6 +99,8 @@ public: void setPlanes( double zNear, double zFar ); + SplashScreen* getSplash(); + protected: int composite_viewer_enabled = -1; osg::ref_ptr viewer; @@ -133,7 +135,7 @@ protected: void setupRoot(); - SplashScreen* _splash; + osg::ref_ptr _splash; QQuickDrawable* _quickDrawable = nullptr; flightgear::PUICamera* _puiCamera = nullptr; };