From f25be2a44a5fd58b6fe3bd630a8189a40ee36dcc Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sun, 1 Mar 2020 23:05:38 +0100 Subject: [PATCH] RTT camera --- apps/openmw/mwvr/openxrmenu.cpp | 139 ++++++++++++++++++----------- apps/openmw/mwvr/openxrmenu.hpp | 19 ++-- apps/openmw/mwvr/openxrtexture.cpp | 15 ++++ apps/openmw/mwvr/openxrtexture.hpp | 2 + apps/openmw/mwvr/openxrview.cpp | 10 ++- apps/openmw/mwvr/openxrviewer.cpp | 70 ++++++++++----- 6 files changed, 173 insertions(+), 82 deletions(-) diff --git a/apps/openmw/mwvr/openxrmenu.cpp b/apps/openmw/mwvr/openxrmenu.cpp index 0960e5422..ba181930c 100644 --- a/apps/openmw/mwvr/openxrmenu.cpp +++ b/apps/openmw/mwvr/openxrmenu.cpp @@ -4,10 +4,82 @@ #include "openxrmanagerimpl.hpp" #include #include +#include +#include #include +#include +#include +#include "../mwrender/util.hpp" namespace MWVR { + + +class Menus : public osg::Camera +{ +public: + Menus() + { + setRenderOrder(osg::Camera::PRE_RENDER); + setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); + setReferenceFrame(osg::Camera::ABSOLUTE_RF); + setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water")); + setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR); + setName("ReflectionCamera"); + + setCullMask(MWRender::Mask_GUI); + setNodeMask(MWRender::Mask_RenderToTexture); + + unsigned int rttSize = 1000; + setViewport(0, 0, rttSize, rttSize); + + // No need for Update traversal since the mSceneRoot is already updated as part of the main scene graph + // A double update would mess with the light collection (in addition to being plain redundant) + setUpdateCallback(new MWRender::NoTraverseCallback); + + mMenuTexture = new osg::Texture2D; + mMenuTexture->setTextureSize(rttSize, rttSize); + mMenuTexture->setInternalFormat(GL_RGB); + mMenuTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); + mMenuTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); + mMenuTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + mMenuTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); + + attach(osg::Camera::COLOR_BUFFER, mMenuTexture); + + // XXX: should really flip the FrontFace on each renderable instead of forcing clockwise. + //osg::ref_ptr frontFace(new osg::FrontFace); + //frontFace->setMode(osg::FrontFace::CLOCKWISE); + //getOrCreateStateSet()->setAttributeAndModes(frontFace, osg::StateAttribute::ON); + + //mClipCullNode = new ClipCullNode; + //addChild(mClipCullNode); + + SceneUtil::ShadowManager::disableShadowsForStateSet(getOrCreateStateSet()); + } + + void setScene(osg::Node* scene) + { + if (mScene) + removeChild(mScene); + mScene = scene; + addChild(scene); + } + + osg::Texture2D* getMenuTexture() const + { + return mMenuTexture.get(); + } + +private: + osg::ref_ptr mMenuTexture; + osg::ref_ptr mScene; +}; + + + + class PredrawCallback : public osg::Camera::DrawCallback { public: @@ -38,7 +110,7 @@ namespace MWVR OpenXRMenu::OpenXRMenu( osg::ref_ptr parent, - osg::ref_ptr menuSubgraph, + osg::ref_ptr menuSubgraph, const std::string& title, osg::Vec2 extent_meters, Pose pose, @@ -50,11 +122,6 @@ namespace MWVR , mParent(parent) , mMenuSubgraph(menuSubgraph) { - mMenuTexture->setTextureSize(width, height); - mMenuTexture->setInternalFormat(GL_RGBA); - mMenuTexture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR); - mMenuTexture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR); - osg::ref_ptr vertices{ new osg::Vec3Array(4) }; osg::ref_ptr texCoords{ new osg::Vec2Array(4) }; osg::ref_ptr normals{ new osg::Vec3Array(1) }; @@ -87,7 +154,10 @@ namespace MWVR mGeometry->setDataVariance(osg::Object::DYNAMIC); mGeometry->setSupportsDisplayList(false); - mStateSet->setTextureAttributeAndModes(0, mMenuTexture, osg::StateAttribute::ON); + mMenuCamera = new Menus(); + mMenuCamera->setScene(menuSubgraph); + + mStateSet->setTextureAttributeAndModes(0, menuTexture(), osg::StateAttribute::ON); mStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); mGeometry->setStateSet(mStateSet); @@ -104,58 +174,29 @@ namespace MWVR - mMenuCamera->setClearColor(clearColor); - mMenuCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - - // This is based on the osgprerender example. - // I'm not sure this part is meaningful when all i'm rendering is the GUI. - - //auto& bs = mMenuSubgraph->getBound(); - //if (!bs.valid()) - // Log(Debug::Verbose) << "OpenXRMenu: Invalid bound"; - //float znear = 1.0f * bs.radius(); - //float zfar = 3.0f * bs.radius(); - //float proj_top = 0.25f * znear; - //float proj_right = 0.5f * znear; - //znear *= 0.9f; - //zfar *= 1.1f; - //mMenuCamera->setProjectionMatrixAsFrustum(-proj_right, proj_right, -proj_top, proj_top, znear, zfar); - //mMenuCamera->setViewMatrixAsLookAt(bs.center() - osg::Vec3(0.0f, 2.0f, 0.0f) * bs.radius(), bs.center(), osg::Vec3(0.0f, 0.0f, 1.0f)); - - - mMenuCamera->setViewMatrix(viewer->getCamera()->getViewMatrix()); - mMenuCamera->setProjectionMatrix(viewer->getCamera()->getProjectionMatrix()); - - // Camera details - mMenuCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); - mMenuCamera->setViewport(0, 0, width, height); - mMenuCamera->setRenderOrder(osg::Camera::PRE_RENDER); - mMenuCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); - mMenuCamera->attach(osg::Camera::COLOR_BUFFER, mMenuTexture); - mMenuCamera->addChild(mMenuSubgraph); - mMenuCamera->setCullMask(MWRender::Mask_GUI); - mMenuCamera->setPreDrawCallback(new PredrawCallback(this)); - mMenuCamera->setPostDrawCallback(new PostdrawCallback(this)); - mMenuCamera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR); - mMenuCamera->setAllowEventFocus(false); - - mParent->addChild(mMenuCamera); + mParent->addChild(mMenuCamera.get()); } OpenXRMenu::~OpenXRMenu() { mParent->removeChild(mTransform); - mParent->removeChild(mMenuCamera); + mParent->removeChild(mMenuCamera.get()); } void OpenXRMenu::updateCallback() { } - void OpenXRMenu::postRenderCallback(osg::RenderInfo& renderInfo) + osg::Camera* OpenXRMenu::camera() { - Log(Debug::Verbose) << "Menu: PostRender"; + return mMenuCamera.get(); + } + + osg::ref_ptr OpenXRMenu::menuTexture() + { + if (mMenuCamera) + return mMenuCamera->getMenuTexture(); + return nullptr; } void OpenXRMenu::updatePose(Pose pose) @@ -164,10 +205,6 @@ namespace MWVR mTransform->setPosition(pose.position); } - void OpenXRMenu::preRenderCallback(osg::RenderInfo& renderInfo) - { - Log(Debug::Verbose) << "Menu: PreRender"; - } OpenXRMenuManager::OpenXRMenuManager( osg::ref_ptr viewer) diff --git a/apps/openmw/mwvr/openxrmenu.hpp b/apps/openmw/mwvr/openxrmenu.hpp index 3eb2cd1da..e7f303db1 100644 --- a/apps/openmw/mwvr/openxrmenu.hpp +++ b/apps/openmw/mwvr/openxrmenu.hpp @@ -13,12 +13,14 @@ struct XrCompositionLayerQuad; namespace MWVR { + class Menus; + class OpenXRMenu { public: OpenXRMenu( osg::ref_ptr parent, - osg::ref_ptr menuSubgraph, + osg::ref_ptr menuSubgraph, const std::string& title, osg::Vec2 extent_meters, Pose pose, @@ -32,21 +34,22 @@ namespace MWVR void preRenderCallback(osg::RenderInfo& renderInfo); void postRenderCallback(osg::RenderInfo& renderInfo); - osg::Camera* camera() { return mMenuCamera; } + osg::Camera* camera(); + + osg::ref_ptr menuTexture(); void updatePose(Pose pose); - protected: + public: std::string mTitle; osg::ref_ptr mParent; osg::ref_ptr mGeometry{ new osg::Geometry }; osg::ref_ptr mGeode{ new osg::Geode }; osg::ref_ptr mTransform{ new osg::PositionAttitudeTransform }; - osg::ref_ptr mMenuSubgraph; - osg::ref_ptr mMenuCamera{ new osg::Camera }; - osg::ref_ptr mMenuTexture{ new osg::Texture2D }; + osg::ref_ptr mMenuSubgraph; osg::ref_ptr mStateSet{ new osg::StateSet }; + osg::ref_ptr mMenuCamera; }; class OpenXRMenuManager @@ -61,11 +64,13 @@ namespace MWVR void updatePose(void); + OpenXRMenu* getMenu(void) const { return mMenu.get(); } + private: Pose mPose{}; osg::ref_ptr mOsgViewer{ nullptr }; osg::ref_ptr mMenusRoot{ new osg::Group }; - osg::ref_ptr mGuiRoot{ new osg::Group }; + osg::ref_ptr mGuiRoot{ nullptr }; std::unique_ptr mMenu{ nullptr }; }; } diff --git a/apps/openmw/mwvr/openxrtexture.cpp b/apps/openmw/mwvr/openxrtexture.cpp index deb09b0a9..de723d0f1 100644 --- a/apps/openmw/mwvr/openxrtexture.cpp +++ b/apps/openmw/mwvr/openxrtexture.cpp @@ -107,6 +107,8 @@ namespace MWVR auto state = gc->getState(); auto* gl = osg::GLExtensions::Get(state->getContextID(), false); gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mFBO); + + Log(Debug::Verbose) << "Bound FBO: " << mFBO; } void OpenXRTextureBuffer::endFrame(osg::GraphicsContext* gc, uint32_t blitTarget) @@ -130,4 +132,17 @@ namespace MWVR gl->glBlitFramebuffer(0, 0, mWidth, mHeight, x, y, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0); } + + void OpenXRTextureBuffer::blit(osg::GraphicsContext* gc, int x, int y, int w, int h, int blitTarget) + { + auto* state = gc->getState(); + auto* gl = osg::GLExtensions::Get(state->getContextID(), false); + gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, mBlitFBO); + gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, mFBO); + gl->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, blitTarget, 0); + gl->glBlitFramebuffer(0, 0, mWidth, mHeight, x, y, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); + gl->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0); + gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0); + gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0); + } } diff --git a/apps/openmw/mwvr/openxrtexture.hpp b/apps/openmw/mwvr/openxrtexture.hpp index 27db42866..46258469d 100644 --- a/apps/openmw/mwvr/openxrtexture.hpp +++ b/apps/openmw/mwvr/openxrtexture.hpp @@ -26,9 +26,11 @@ namespace MWVR void endFrame(osg::GraphicsContext* gc, uint32_t blitTarget); uint32_t fbo(void) const { return mFBO; } + uint32_t colorBuffer(void) const { return mColorBuffer; } //! Blit to region in currently bound draw fbo void blit(osg::GraphicsContext* gc, int x, int y, int w, int h); + void blit(osg::GraphicsContext* gc, int x, int y, int w, int h, int target); private: // Set aside a weak pointer to the constructor state to use when freeing FBOs, if no state is given to destroy() diff --git a/apps/openmw/mwvr/openxrview.cpp b/apps/openmw/mwvr/openxrview.cpp index 6fdd28941..bc036f161 100644 --- a/apps/openmw/mwvr/openxrview.cpp +++ b/apps/openmw/mwvr/openxrview.cpp @@ -38,7 +38,7 @@ namespace MWVR { camera->setClearColor(clearColor); camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); - camera->setRenderOrder(osg::Camera::PRE_RENDER, eye); + camera->setRenderOrder(osg::Camera::PRE_RENDER, eye + 2); camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR); camera->setAllowEventFocus(false); camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); @@ -50,8 +50,12 @@ namespace MWVR { return camera.release(); } + static GLint wfbo = 0; + static GLint rfbo = 0; void OpenXRView::prerenderCallback(osg::RenderInfo& renderInfo) { + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &wfbo); + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING_EXT, &rfbo); if (mSwapchain) { mSwapchain->beginFrame(renderInfo.getState()->getGraphicsContext()); @@ -66,6 +70,10 @@ namespace MWVR { mTimer.checkpoint("Postrender"); Log(Debug::Verbose) << "XRView: PostRender"; + auto state = renderInfo.getState(); + auto gl = osg::GLExtensions::Get(state->getContextID(), false); + gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, wfbo); + gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, rfbo); } void OpenXRView::swapBuffers(osg::GraphicsContext* gc) diff --git a/apps/openmw/mwvr/openxrviewer.cpp b/apps/openmw/mwvr/openxrviewer.cpp index 19572b175..a72e56d10 100644 --- a/apps/openmw/mwvr/openxrviewer.cpp +++ b/apps/openmw/mwvr/openxrviewer.cpp @@ -123,12 +123,14 @@ namespace MWVR leftCamera->setPreDrawCallback(mPreDraw); rightCamera->setPreDrawCallback(mPreDraw); - leftCamera->setPostDrawCallback(mPostDraw); - rightCamera->setPostDrawCallback(mPostDraw); + //leftCamera->setPostDrawCallback(mPostDraw); + //rightCamera->setPostDrawCallback(mPostDraw); + leftCamera->setFinalDrawCallback(mPostDraw); + rightCamera->setFinalDrawCallback(mPostDraw); // Stereo cameras should only draw the scene (AR layers should later add minimap, health, etc.) - //leftCamera->setCullMask(~MWRender::Mask_GUI); - //rightCamera->setCullMask(~MWRender::Mask_GUI); + leftCamera->setCullMask(~MWRender::Mask_GUI); + rightCamera->setCullMask(~MWRender::Mask_GUI); leftCamera->setName("LeftEye"); rightCamera->setName("RightEye"); @@ -153,19 +155,6 @@ namespace MWVR // It's just convenient. mMirrorTextureSwapchain.reset(new OpenXRSwapchain(xr, context->getState(), config)); - //auto menuView = new OpenXRMenu(xr, config, context->getState(), "MainMenu", osg::Vec2(1.f, 1.f)); - //mViews["MenuView"] = menuView; - //auto menuCamera = mCameras["MenuView"] = menuView->createCamera(2, clearColor, context); - - //mMenus.reset(new OpenXRMenu(mMenusRoot, "MainMenu", osg::Vec2(1.f, 1.f), MWVR::Pose(), config.width, config.height, osg::Vec4(0.f, 0.f, 0.f, 0.f), context)); - //auto menuCamera = mMenus->camera(); - //menuCamera->setCullMask(MWRender::Mask_GUI); - //menuCamera->setName("MenuView"); - //menuCamera->setPreDrawCallback(mPreDraw); - //menuCamera->setPostDrawCallback(mPostDraw); - - - //mViewer->addSlave(menuCamera, true); mViewer->getSlave(0)._updateSlaveCallback = new OpenXRWorldView::UpdateSlaveCallback(xr, session, leftView, context); mViewer->getSlave(1)._updateSlaveCallback = new OpenXRWorldView::UpdateSlaveCallback(xr, session, rightView, context); @@ -173,14 +162,13 @@ namespace MWVR mainCamera->getGraphicsContext()->setSwapCallback(new OpenXRViewer::SwapBuffersCallback(this)); mainCamera->setGraphicsContext(nullptr); session->setLayer(OpenXRLayerStack::WORLD_VIEW_LAYER, this); - //session->setLayer(OpenXRLayerStack::MENU_VIEW_LAYER, dynamic_cast(mViews["MenuView"].get())); mConfigured = true; } void OpenXRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc, bool includeMenu) { - includeMenu = false; + //includeMenu = false; mMirrorTextureSwapchain->beginFrame(gc); int mirror_width = 0; @@ -193,15 +181,51 @@ namespace MWVR mViews["RightEye"]->swapchain().renderBuffer()->blit(gc, 0, 0, mirror_width, mMirrorTextureSwapchain->height()); mViews["LeftEye"]->swapchain().renderBuffer()->blit(gc, mirror_width, 0, 2 * mirror_width, mMirrorTextureSwapchain->height()); - //if(includeMenu) - // mViews["MenuView"]->swapchain().renderBuffer()->blit(gc, 2 * mirror_width, 0, 3 * mirror_width, mMirrorTextureSwapchain->height()); + auto* state = gc->getState(); + auto* gl = osg::GLExtensions::Get(state->getContextID(), false); + + if (includeMenu) + { + auto menuManager = OpenXREnvironment::get().getMenuManager(); + if (menuManager) + { + auto menu = menuManager->getMenu(); + if (menu) + { + auto texture = menu->menuTexture(); + if (texture) + { + auto textureObject = texture->getTextureObject(state->getContextID()); + if (textureObject) + { + auto textureId = textureObject->id(); + + Log(Debug::Verbose) << "texture id: " << textureId; + + GLuint fbo = 0; + gl->glGenFramebuffers(1, &fbo); + + gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, fbo); + gl->glFramebufferTexture2D(GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, textureId, 0); + gl->glBlitFramebuffer(0, 0, texture->getTextureWidth(), texture->getTextureHeight(), 2 * mirror_width, 0, 3 * mirror_width, mMirrorTextureSwapchain->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST); + gl->glFramebufferTexture2D(GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0); + gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0); + + gl->glDeleteFramebuffers(1, &fbo); + } + else + { + Log(Debug::Warning) << "Texture object was null"; + } + } + } + } + } mMirrorTextureSwapchain->endFrame(gc); - auto* state = gc->getState(); - auto* gl = osg::GLExtensions::Get(state->getContextID(), false); gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); mMirrorTextureSwapchain->renderBuffer()->blit(gc, 0, 0, mMirrorTextureSwapchain->width(), mMirrorTextureSwapchain->height()); }