From 45656f1d065f4779f3786fbc6fe5a9e2c6bfe67c Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sun, 28 Jun 2020 11:33:01 +0200 Subject: [PATCH] Comments etc --- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/engine.cpp | 1 + apps/openmw/mwvr/openxrmanager.cpp | 17 +-- apps/openmw/mwvr/openxrmanager.hpp | 11 +- apps/openmw/mwvr/openxrmanagerimpl.cpp | 3 - apps/openmw/mwvr/openxrswapchain.cpp | 15 +- apps/openmw/mwvr/openxrswapchain.hpp | 27 ++-- apps/openmw/mwvr/openxrswapchainimpl.cpp | 36 +++-- apps/openmw/mwvr/openxrswapchainimpl.hpp | 19 ++- apps/openmw/mwvr/realisticcombat.cpp | 129 ++++++++---------- apps/openmw/mwvr/realisticcombat.hpp | 81 +++++++---- apps/openmw/mwvr/vranimation.cpp | 7 +- apps/openmw/mwvr/vranimation.hpp | 2 +- .../mwvr/{vrtexture.cpp => vrframebuffer.cpp} | 44 ++---- .../mwvr/{vrtexture.hpp => vrframebuffer.hpp} | 15 +- apps/openmw/mwvr/vrgui.cpp | 3 +- apps/openmw/mwvr/vrgui.hpp | 47 ++++--- apps/openmw/mwvr/vrinputmanager.cpp | 19 ++- apps/openmw/mwvr/vrinputmanager.hpp | 12 +- apps/openmw/mwvr/vrsession.cpp | 29 +++- apps/openmw/mwvr/vrsession.hpp | 1 + apps/openmw/mwvr/vrview.cpp | 3 +- apps/openmw/mwvr/vrviewer.cpp | 55 +++++--- apps/openmw/mwvr/vrviewer.hpp | 21 +-- 24 files changed, 319 insertions(+), 282 deletions(-) rename apps/openmw/mwvr/{vrtexture.cpp => vrframebuffer.cpp} (65%) rename apps/openmw/mwvr/{vrtexture.hpp => vrframebuffer.hpp} (67%) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 75e0ec201..be601d3b5 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -152,8 +152,8 @@ if(BUILD_VR_OPENXR) mwvr/vrinput.cpp mwvr/vrsession.hpp mwvr/vrsession.cpp - mwvr/vrtexture.hpp - mwvr/vrtexture.cpp + mwvr/vrframebuffer.hpp + mwvr/vrframebuffer.cpp mwvr/vrtypes.hpp mwvr/vrtypes.cpp mwvr/vrview.hpp diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index b041ecebc..456b23391 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -62,6 +62,7 @@ #ifdef USE_OPENXR #include "mwvr/vrinputmanager.hpp" #include "mwvr/vrviewer.hpp" +#include "mwvr/vrgui.hpp" #endif namespace diff --git a/apps/openmw/mwvr/openxrmanager.cpp b/apps/openmw/mwvr/openxrmanager.cpp index d4a49b78a..0061049b0 100644 --- a/apps/openmw/mwvr/openxrmanager.cpp +++ b/apps/openmw/mwvr/openxrmanager.cpp @@ -111,26 +111,11 @@ namespace MWVR return impl().getRecommendedSwapchainConfig(); } - void - OpenXRManager::RealizeOperation::operator()( - osg::GraphicsContext* gc) - { - auto* xr = Environment::get().getManager(); - xr->realize(gc); - } - - bool - OpenXRManager::RealizeOperation::realized() - { - auto* xr = Environment::get().getManager(); - return xr->realized(); - } - void OpenXRManager::CleanupOperation::operator()( osg::GraphicsContext* gc) { - + // TODO: Use this to make proper cleanup such as cleaning up VRFramebuffers. } } diff --git a/apps/openmw/mwvr/openxrmanager.hpp b/apps/openmw/mwvr/openxrmanager.hpp index 2e2366902..3dc248f73 100644 --- a/apps/openmw/mwvr/openxrmanager.hpp +++ b/apps/openmw/mwvr/openxrmanager.hpp @@ -26,16 +26,6 @@ namespace MWVR class OpenXRManager : public osg::Referenced { public: - class RealizeOperation : public osg::GraphicsOperation - { - public: - RealizeOperation() : osg::GraphicsOperation("OpenXRRealizeOperation", false) {}; - void operator()(osg::GraphicsContext* gc) override; - virtual bool realized(); - - private: - }; - class CleanupOperation : public osg::GraphicsOperation { public: @@ -51,6 +41,7 @@ namespace MWVR ~OpenXRManager(); + /// Manager has been initialized. bool realized(); //! Forward call to xrWaitFrame() diff --git a/apps/openmw/mwvr/openxrmanagerimpl.cpp b/apps/openmw/mwvr/openxrmanagerimpl.cpp index 5c6df795e..975ea76d6 100644 --- a/apps/openmw/mwvr/openxrmanagerimpl.cpp +++ b/apps/openmw/mwvr/openxrmanagerimpl.cpp @@ -2,7 +2,6 @@ #include "openxrswapchain.hpp" #include "openxrswapchainimpl.hpp" -#include "vrtexture.hpp" #include #include @@ -445,13 +444,11 @@ namespace MWVR switch (newState) { case XR_SESSION_STATE_READY: - //case XR_SESSION_STATE_IDLE: { XrSessionBeginInfo beginInfo{ XR_TYPE_SESSION_BEGIN_INFO }; beginInfo.primaryViewConfigurationType = mViewConfigType; CHECK_XRCMD(xrBeginSession(mSession, &beginInfo)); mSessionRunning = true; - //waitFrame(); break; } case XR_SESSION_STATE_STOPPING: diff --git a/apps/openmw/mwvr/openxrswapchain.cpp b/apps/openmw/mwvr/openxrswapchain.cpp index 94a23ecd6..4b2c509e7 100644 --- a/apps/openmw/mwvr/openxrswapchain.cpp +++ b/apps/openmw/mwvr/openxrswapchain.cpp @@ -33,19 +33,14 @@ namespace MWVR { return impl().endFrame(gc); } - void OpenXRSwapchain::acquire(osg::GraphicsContext* gc) + uint32_t OpenXRSwapchain::acquiredColorTexture() const { - return impl().acquire(gc); + return impl().acquiredColorTexture(); } - void OpenXRSwapchain::release(osg::GraphicsContext* gc) + uint32_t OpenXRSwapchain::acquiredDepthTexture() const { - return impl().release(gc); - } - - uint32_t OpenXRSwapchain::acquiredImage() const - { - return impl().acquiredImage(); + return impl().acquiredDepthTexture(); } int OpenXRSwapchain::width() const @@ -68,7 +63,7 @@ namespace MWVR { return impl().isAcquired(); } - VRTexture* OpenXRSwapchain::renderBuffer() const + VRFramebuffer* OpenXRSwapchain::renderBuffer() const { return impl().renderBuffer(); } diff --git a/apps/openmw/mwvr/openxrswapchain.hpp b/apps/openmw/mwvr/openxrswapchain.hpp index a15adb395..e819bb485 100644 --- a/apps/openmw/mwvr/openxrswapchain.hpp +++ b/apps/openmw/mwvr/openxrswapchain.hpp @@ -2,13 +2,13 @@ #define OPENXR_SWAPCHAIN_HPP #include "openxrmanager.hpp" -#include "vrtexture.hpp" struct XrSwapchainSubImage; namespace MWVR { class OpenXRSwapchainImpl; + class VRFramebuffer; /// \brief Creation and management of openxr swapchains class OpenXRSwapchain @@ -20,29 +20,40 @@ namespace MWVR public: //! Prepare for render (set FBO) void beginFrame(osg::GraphicsContext* gc); + //! Finalize render void endFrame(osg::GraphicsContext* gc); - //! Prepare for render - void acquire(osg::GraphicsContext* gc); - //! Finalize render - void release(osg::GraphicsContext* gc); - //! Currently acquired image - uint32_t acquiredImage() const; + + //! Currently acquired color texture + uint32_t acquiredColorTexture() const; + + //! Currently acquired depth texture + uint32_t acquiredDepthTexture() const; + //! Whether subchain is currently acquired (true) or released (false) bool isAcquired() const; + //! Width of the view surface int width() const; + //! Height of the view surface int height() const; + //! Samples of the view surface int samples() const; + //! Get the current texture - VRTexture* renderBuffer() const; + VRFramebuffer* renderBuffer() const; + //! Get the private implementation OpenXRSwapchainImpl& impl() { return *mPrivate; } + //! Get the private implementation const OpenXRSwapchainImpl& impl() const { return *mPrivate; } + protected: + OpenXRSwapchain(const OpenXRSwapchain&) = delete; + void operator=(const OpenXRSwapchain&) = delete; private: std::unique_ptr mPrivate; }; diff --git a/apps/openmw/mwvr/openxrswapchainimpl.cpp b/apps/openmw/mwvr/openxrswapchainimpl.cpp index d73c3019f..ce1d0d638 100644 --- a/apps/openmw/mwvr/openxrswapchainimpl.cpp +++ b/apps/openmw/mwvr/openxrswapchainimpl.cpp @@ -1,5 +1,6 @@ #include "openxrswapchainimpl.hpp" #include "vrenvironment.hpp" +#include "vrframebuffer.hpp" #include @@ -47,9 +48,8 @@ namespace MWVR { // Find supported depth swapchain format. constexpr int64_t RequestedDepthSwapchainFormats[] = { - GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT24, - GL_DEPTH_COMPONENT16, + GL_DEPTH_COMPONENT32F, }; swapchainFormatIt = @@ -108,7 +108,7 @@ namespace MWVR { CHECK_XRCMD(xrEnumerateSwapchainImages(mDepthSwapchain, imageCount, &imageCount, reinterpret_cast(mDepthSwapchainImageBuffers.data()))); for (unsigned i = 0; i < imageCount; i++) - mRenderBuffers.emplace_back(new VRTexture(state, mWidth, mHeight, mSamples, mSwapchainImageBuffers[i].image, mDepthSwapchainImageBuffers[i].image)); + mRenderBuffers.emplace_back(new VRFramebuffer(state, mWidth, mHeight, mSamples, mSwapchainImageBuffers[i].image, mDepthSwapchainImageBuffers[i].image)); mSubImage.swapchain = mSwapchain; mSubImage.imageRect.offset = { 0, 0 }; @@ -121,18 +121,22 @@ namespace MWVR { CHECK_XRCMD(xrDestroySwapchain(mSwapchain)); } - VRTexture* OpenXRSwapchainImpl::renderBuffer() const + VRFramebuffer* OpenXRSwapchainImpl::renderBuffer() const { - if (isAcquired()) - return mRenderBuffers[mAcquiredImageIndex].get(); - throw std::logic_error("Swapbuffer not acquired before use"); + checkAcquired(); + return mRenderBuffers[mAcquiredImageIndex].get(); } - uint32_t OpenXRSwapchainImpl::acquiredImage() const + uint32_t OpenXRSwapchainImpl::acquiredColorTexture() const { - if (isAcquired()) - return mSwapchainImageBuffers[mAcquiredImageIndex].image; - throw std::logic_error("Swapbuffer not acquired before use"); + checkAcquired(); + return mSwapchainImageBuffers[mAcquiredImageIndex].image; + } + + uint32_t OpenXRSwapchainImpl::acquiredDepthTexture() const + { + checkAcquired(); + return mSwapchainImageBuffers[mAcquiredImageIndex].image; } bool OpenXRSwapchainImpl::isAcquired() const @@ -142,14 +146,17 @@ namespace MWVR { void OpenXRSwapchainImpl::beginFrame(osg::GraphicsContext* gc) { + if (isAcquired()) + throw std::logic_error("Trying to acquire already acquired swapchain"); acquire(gc); - renderBuffer()->beginFrame(gc); + renderBuffer()->bindFramebuffer(gc, GL_FRAMEBUFFER_EXT); } int swapCount = 0; void OpenXRSwapchainImpl::endFrame(osg::GraphicsContext* gc) { + checkAcquired(); release(gc); } @@ -180,4 +187,9 @@ namespace MWVR { CHECK_XRCMD(xrReleaseSwapchainImage(mSwapchain, &releaseInfo)); CHECK_XRCMD(xrReleaseSwapchainImage(mDepthSwapchain, &releaseInfo)); } + void OpenXRSwapchainImpl::checkAcquired() const + { + if (!isAcquired()) + throw std::logic_error("Swapchain must be acquired before use. Call between OpenXRSwapchain::beginFrame() and OpenXRSwapchain::endFrame()"); + } } diff --git a/apps/openmw/mwvr/openxrswapchainimpl.hpp b/apps/openmw/mwvr/openxrswapchainimpl.hpp index 14acac991..9f7e98ab1 100644 --- a/apps/openmw/mwvr/openxrswapchainimpl.hpp +++ b/apps/openmw/mwvr/openxrswapchainimpl.hpp @@ -17,12 +17,10 @@ namespace MWVR void beginFrame(osg::GraphicsContext* gc); void endFrame(osg::GraphicsContext* gc); - void acquire(osg::GraphicsContext* gc); - void release(osg::GraphicsContext* gc); - VRTexture* renderBuffer() const; - - uint32_t acquiredImage() const; + VRFramebuffer* renderBuffer() const; + uint32_t acquiredColorTexture() const; + uint32_t acquiredDepthTexture() const; bool isAcquired() const; XrSwapchain xrSwapchain(void) const { return mSwapchain; }; @@ -31,6 +29,15 @@ namespace MWVR int height() const { return mHeight; }; int samples() const { return mSamples; }; + protected: + OpenXRSwapchainImpl(const OpenXRSwapchainImpl&) = delete; + void operator=(const OpenXRSwapchainImpl&) = delete; + + void acquire(osg::GraphicsContext* gc); + void release(osg::GraphicsContext* gc); + void checkAcquired() const; + + private: XrSwapchain mSwapchain = XR_NULL_HANDLE; XrSwapchain mDepthSwapchain = XR_NULL_HANDLE; std::vector mSwapchainImageBuffers{}; @@ -42,7 +49,7 @@ namespace MWVR int64_t mSwapchainColorFormat = -1; int64_t mSwapchainDepthFormat = -1; uint32_t mFBO = 0; - std::vector > mRenderBuffers{}; + std::vector > mRenderBuffers{}; int mRenderBuffer{ 0 }; uint32_t mAcquiredImageIndex{ 0 }; bool mIsAcquired{ false }; diff --git a/apps/openmw/mwvr/realisticcombat.cpp b/apps/openmw/mwvr/realisticcombat.cpp index 54d4b2c11..780ca6a18 100644 --- a/apps/openmw/mwvr/realisticcombat.cpp +++ b/apps/openmw/mwvr/realisticcombat.cpp @@ -1,27 +1,3 @@ -///////////////////////////////////////////////// -// -// State machine for "realistic" combat in openmw vr -// -// Initial state: Ready. -// -// State Ready: Ready to initiate a new attack. -// State Launch: Player has begun swinging his weapon. -// State Swing: Currently swinging weapon. -// State Impact: Contact made, weapon still swinging. -// State Cooldown: Swing completed, wait a minimum period before next. -// -// Transition rules: -// Ready -> Launch: When the minimum velocity of swing is achieved. -// Launch -> Ready: When the minimum velocity of swing is lost before minimum distance was swung. -// Launch -> Swing: When minimum distance is swung. -// - Play Swish sound -// Swing -> Impact: When minimum velocity is lost, or when a hit is detected. -// - Register hit based on max velocity observed in swing state -// Impact -> Cooldown: When velocity returns below minimum. -// Cooldown -> Ready: When the minimum period has passed since entering Cooldown state -// - - #include "realisticcombat.hpp" #include "../mwbase/environment.hpp" @@ -70,13 +46,20 @@ namespace MWVR { } } - StateMachine::StateMachine(MWWorld::Ptr ptr) : ptr(ptr) {} + StateMachine::StateMachine(MWWorld::Ptr ptr) + : mPtr(ptr) + , mMinVelocity(Settings::Manager::getFloat("realistic combat minimum swing velocity", "VR")) + , mMaxVelocity(Settings::Manager::getFloat("realistic combat maximum swing velocity", "VR")) + { + Log(Debug::Verbose) << "realistic combat minimum swing velocity: " << mMinVelocity; + Log(Debug::Verbose) << "realistic combat maximum swing velocity: " << mMaxVelocity; + } bool StateMachine::canSwing() { - if (swingType >= 0) - if (velocity >= minVelocity) - if (swingType != ESM::Weapon::AT_Thrust || thrustVelocity >= 0.f) + if (mSwingType >= 0) + if (mVelocity >= mMinVelocity) + if (mSwingType != ESM::Weapon::AT_Thrust || mThrustVelocity >= 0.f) return true; return false; } @@ -85,20 +68,20 @@ namespace MWVR { void StateMachine::transition( SwingState newState) { - Log(Debug::Verbose) << "Transition:" << stateToString(state) << "->" << stateToString(newState); - maxSwingVelocity = 0.f; - timeSinceEnteredState = 0.f; - movementSinceEnteredState = 0.f; - state = newState; + Log(Debug::Verbose) << "Transition:" << stateToString(mState) << "->" << stateToString(newState); + mMaxSwingVelocity = 0.f; + mTimeSinceEnteredState = 0.f; + mMovementSinceEnteredState = 0.f; + mState = newState; } void StateMachine::reset() { - maxSwingVelocity = 0.f; - timeSinceEnteredState = 0.f; - velocity = 0.f; - previousPosition = osg::Vec3(0.f, 0.f, 0.f); - state = SwingState_Ready; + mMaxSwingVelocity = 0.f; + mTimeSinceEnteredState = 0.f; + mVelocity = 0.f; + mPreviousPosition = osg::Vec3(0.f, 0.f, 0.f); + mState = SwingState_Ready; } static bool isMeleeWeapon(int type) @@ -151,7 +134,7 @@ namespace MWVR { if (!enabled) return; - timeSinceEnteredState += dt; + mTimeSinceEnteredState += dt; // First determine direction of different swing types @@ -180,12 +163,12 @@ namespace MWVR { // So i reset position when tracking is re-acquired to avoid a superspeed strike. // Theoretically, the player's hand really could be at 0,0,0 // but that's a super rare case so whatever. - if (previousPosition == osg::Vec3(0.f, 0.f, 0.f)) - previousPosition = handPose.position; + if (mPreviousPosition == osg::Vec3(0.f, 0.f, 0.f)) + mPreviousPosition = handPose.position; - osg::Vec3 movement = handPose.position - previousPosition; - movementSinceEnteredState += movement.length(); - previousPosition = handPose.position; + osg::Vec3 movement = handPose.position - mPreviousPosition; + mMovementSinceEnteredState += movement.length(); + mPreviousPosition = handPose.position; osg::Vec3 swingVector = movement / dt; osg::Vec3 swingDirection = swingVector; swingDirection.normalize(); @@ -193,24 +176,24 @@ namespace MWVR { // Compute swing velocities // Thrust follows the orientation of the weapon. Negative thrust = no attack. - thrustVelocity = swingVector * thrustDirection; - velocity = swingVector.length(); + mThrustVelocity = swingVector * thrustDirection; + mVelocity = swingVector.length(); if (isSideSwingValidForWeapon(weaponType)) { // Compute velocity in the plane normal to the thrust direction. - float thrustComponent = std::abs(thrustVelocity / velocity); + float thrustComponent = std::abs(mThrustVelocity / mVelocity); float planeComponent = std::sqrt(1 - thrustComponent * thrustComponent); - slashChopVelocity = velocity * planeComponent; - sideVelocity = -1000.f; + mSlashChopVelocity = mVelocity * planeComponent; + mSideVelocity = -1000.f; } else { // If side swing is not valid for the weapon, count slash/chop only along in // the direction of the weapon's edge. - slashChopVelocity = std::abs(swingVector * slashChopDirection); - sideVelocity = std::abs(swingVector * sideDirection); + mSlashChopVelocity = std::abs(swingVector * slashChopDirection); + mSideVelocity = std::abs(swingVector * sideDirection); } @@ -220,34 +203,34 @@ namespace MWVR { // Pick swing type based on greatest current velocity // Note i use abs() of thrust velocity to prevent accidentally triggering // chop/slash when player is withdrawing the weapon. - if (sideVelocity > std::abs(thrustVelocity) && sideVelocity > slashChopVelocity) + if (mSideVelocity > std::abs(mThrustVelocity) && mSideVelocity > mSlashChopVelocity) { // Player is swinging with the "blunt" side of a weapon that // cannot be used that way. - swingType = -1; + mSwingType = -1; } - else if (std::abs(thrustVelocity) > slashChopVelocity) + else if (std::abs(mThrustVelocity) > mSlashChopVelocity) { - swingType = ESM::Weapon::AT_Thrust; + mSwingType = ESM::Weapon::AT_Thrust; } else { // First check if the weapon is pointing upwards. In which case slash is not // applicable, and the attack must be a chop. if (orientationVerticality > 0.707) - swingType = ESM::Weapon::AT_Chop; + mSwingType = ESM::Weapon::AT_Chop; else { // Next check if the swing is more horizontal or vertical. A slash // would be more horizontal. if (swingVerticality > 0.707) - swingType = ESM::Weapon::AT_Chop; + mSwingType = ESM::Weapon::AT_Chop; else - swingType = ESM::Weapon::AT_Slash; + mSwingType = ESM::Weapon::AT_Slash; } } - switch (state) + switch (mState) { case SwingState_Cooldown: return update_cooldownState(); @@ -260,13 +243,13 @@ namespace MWVR { case SwingState_Launch: return update_launchState(); default: - throw std::logic_error(std::string("You forgot to implement state ") + stateToString(state) + " ya dingus"); + throw std::logic_error(std::string("You forgot to implement state ") + stateToString(mState) + " ya dingus"); } } void StateMachine::update_cooldownState() { - if (timeSinceEnteredState >= minimumPeriod) + if (mTimeSinceEnteredState >= mMinimumPeriod) transition_cooldownToReady(); } @@ -291,19 +274,19 @@ namespace MWVR { MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager(); std::string sound = "Weapon Swish"; - if (strength < 0.5f) - sndMgr->playSound3D(ptr, sound, 1.0f, 0.8f); //Weak attack - if (strength < 1.0f) - sndMgr->playSound3D(ptr, sound, 1.0f, 1.0f); //Medium attack + if (mStrength < 0.5f) + sndMgr->playSound3D(mPtr, sound, 1.0f, 0.8f); //Weak attack + if (mStrength < 1.0f) + sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f); //Medium attack else - sndMgr->playSound3D(ptr, sound, 1.0f, 1.2f); //Strong attack + sndMgr->playSound3D(mPtr, sound, 1.0f, 1.2f); //Strong attack - Log(Debug::Verbose) << "Swing: " << swingTypeToString(swingType); + Log(Debug::Verbose) << "Swing: " << swingTypeToString(mSwingType); } void StateMachine::update_launchState() { - if (movementSinceEnteredState > minimumPeriod) + if (mMovementSinceEnteredState > mMinimumPeriod) transition_launchToSwing(); if (!canSwing()) return transition_launchToReady(); @@ -326,26 +309,26 @@ namespace MWVR { void StateMachine::update_swingState() { - maxSwingVelocity = std::max(velocity, maxSwingVelocity); - strength = std::min(1.f, (maxSwingVelocity - minVelocity) / maxVelocity); + mMaxSwingVelocity = std::max(mVelocity, mMaxSwingVelocity); + mStrength = std::min(1.f, (mMaxSwingVelocity - mMinVelocity) / mMaxVelocity); // When velocity falls below minimum, transition to register the miss if (!canSwing()) return transition_swingingToImpact(); // Call hit with simulated=true to check for hit without actually causing an impact - if (ptr.getClass().hit(ptr, strength, swingType, true)) + if (mPtr.getClass().hit(mPtr, mStrength, mSwingType, true)) return transition_swingingToImpact(); } void StateMachine::transition_swingingToImpact() { - ptr.getClass().hit(ptr, strength, swingType, false); + mPtr.getClass().hit(mPtr, mStrength, mSwingType, false); transition(SwingState_Impact); } void StateMachine::update_impactState() { - if (velocity < minVelocity) + if (mVelocity < mMinVelocity) return transition_impactToCooldown(); } diff --git a/apps/openmw/mwvr/realisticcombat.hpp b/apps/openmw/mwvr/realisticcombat.hpp index 853110498..3d981e65a 100644 --- a/apps/openmw/mwvr/realisticcombat.hpp +++ b/apps/openmw/mwvr/realisticcombat.hpp @@ -12,6 +12,8 @@ namespace MWVR { namespace RealisticCombat { + + /// Enum describing the current state of the MWVR::RealisticCombat::StateMachine enum SwingState { SwingState_Ready, @@ -21,35 +23,38 @@ namespace MWVR { SwingState_Cooldown, }; + ///////////////////////////////////////////////////////////////////// + /// \brief State machine for "realistic" combat in openmw vr + /// + /// \sa SwingState + /// + /// Initial state: Ready. + /// + /// State Ready: Ready to initiate a new attack. + /// State Launch: Player has begun swinging his weapon. + /// State Swing: Currently swinging weapon. + /// State Impact: Contact made, weapon still swinging. + /// State Cooldown: Swing completed, wait a minimum period before next. + /// + /// Transition rules: + /// Ready -> Launch: When the minimum velocity of swing is achieved. + /// Launch -> Ready: When the minimum velocity of swing is lost before minimum distance was swung. + /// Launch -> Swing: When minimum distance is swung. + /// - Play Swish sound + /// Swing -> Impact: When minimum velocity is lost, or when a hit is detected. + /// - Register hit based on max velocity observed in swing state + /// Impact -> Cooldown: When velocity returns below minimum. + /// Cooldown -> Ready: When the minimum period has passed since entering Cooldown state + /// + /// struct StateMachine { - // TODO: These should be configurable - const float minVelocity = 1.f; - const float maxVelocity = 4.f; - - float velocity = 0.f; - float maxSwingVelocity = 0.f; - - SwingState state = SwingState_Ready; - MWWorld::Ptr ptr = MWWorld::Ptr(); - int swingType = -1; - float strength = 0.f; - - float thrustVelocity{ 0.f }; - float slashChopVelocity{ 0.f }; - float sideVelocity{ 0.f }; - - float minimumPeriod{ .25f }; - - float timeSinceEnteredState = { 0.f }; - float movementSinceEnteredState = { 0.f }; - - bool mEnabled = false; - - osg::Vec3 previousPosition{ 0.f,0.f,0.f }; - + public: StateMachine(MWWorld::Ptr ptr); + void update(float dt, bool enabled); + MWWorld::Ptr ptr() { return mPtr; } + protected: bool canSwing(); void playSwish(); @@ -57,7 +62,6 @@ namespace MWVR { void transition(SwingState newState); - void update(float dt, bool enabled); void update_cooldownState(); void transition_cooldownToReady(); @@ -74,6 +78,31 @@ namespace MWVR { void update_impactState(); void transition_impactToCooldown(); + + private: + MWWorld::Ptr mPtr; + const float mMinVelocity; + const float mMaxVelocity; + + float mVelocity = 0.f; + float mMaxSwingVelocity = 0.f; + + SwingState mState = SwingState_Ready; + int mSwingType = -1; + float mStrength = 0.f; + + float mThrustVelocity{ 0.f }; + float mSlashChopVelocity{ 0.f }; + float mSideVelocity{ 0.f }; + + float mMinimumPeriod{ .25f }; + + float mTimeSinceEnteredState = { 0.f }; + float mMovementSinceEnteredState = { 0.f }; + + bool mEnabled = false; + + osg::Vec3 mPreviousPosition{ 0.f,0.f,0.f }; }; } diff --git a/apps/openmw/mwvr/vranimation.cpp b/apps/openmw/mwvr/vranimation.cpp index c294589ba..fe21af7c0 100644 --- a/apps/openmw/mwvr/vranimation.cpp +++ b/apps/openmw/mwvr/vranimation.cpp @@ -459,11 +459,10 @@ namespace MWVR MWBase::Environment::get().getWorld()->getStore().get().find(ref->mBase->mRace); bool isMale = ref->mBase->isMale(); float charHeightFactor = isMale ? race->mData.mHeight.mMale : race->mData.mHeight.mFemale; - float charHeightBase = 1.8f; + float charHeightBase = 1.8f; // Is this ~ the right value? float charHeight = charHeightBase * charHeightFactor; - // TODO: Player height should be configurable - // For now i'm just using my own - float sizeFactor = 1.85f / charHeight; + float realHeight = Settings::Manager::getFloat("real height", "VR"); + float sizeFactor = charHeight / realHeight; Environment::get().getSession()->setPlayerScale(sizeFactor); } diff --git a/apps/openmw/mwvr/vranimation.hpp b/apps/openmw/mwvr/vranimation.hpp index fb595e62a..f232a8b85 100644 --- a/apps/openmw/mwvr/vranimation.hpp +++ b/apps/openmw/mwvr/vranimation.hpp @@ -13,7 +13,7 @@ namespace MWVR class FingerController; class ForearmController; - /// Subclassing NpcAnimation to override behaviours not compatible with VR + /// Subclassing NpcAnimation to implement VR related behaviour class VRAnimation : public MWRender::NpcAnimation { protected: diff --git a/apps/openmw/mwvr/vrtexture.cpp b/apps/openmw/mwvr/vrframebuffer.cpp similarity index 65% rename from apps/openmw/mwvr/vrtexture.cpp rename to apps/openmw/mwvr/vrframebuffer.cpp index 24d834b6a..0598d17e3 100644 --- a/apps/openmw/mwvr/vrtexture.cpp +++ b/apps/openmw/mwvr/vrframebuffer.cpp @@ -1,4 +1,4 @@ -#include "vrtexture.hpp" +#include "vrframebuffer.hpp" #include @@ -11,7 +11,7 @@ namespace MWVR { - VRTexture::VRTexture(osg::ref_ptr state, std::size_t width, std::size_t height, uint32_t msaaSamples, uint32_t colorBuffer, uint32_t depthBuffer) + VRFramebuffer::VRFramebuffer(osg::ref_ptr state, std::size_t width, std::size_t height, uint32_t msaaSamples, uint32_t colorBuffer, uint32_t depthBuffer) : mState(state) , mWidth(width) , mHeight(height) @@ -72,12 +72,12 @@ namespace MWVR gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); } - VRTexture::~VRTexture() + VRFramebuffer::~VRFramebuffer() { destroy(nullptr); } - void VRTexture::destroy(osg::State* state) + void VRFramebuffer::destroy(osg::State* state) { if (!state) { @@ -92,7 +92,7 @@ namespace MWVR gl->glDeleteFramebuffers(1, &mFBO); } else if (mFBO) - // Without access to opengl methods, i'll just let the FBOs leak. + // Without access to glDeleteFramebuffers, i'll have to leak FBOs. Log(Debug::Warning) << "destroy() called without a State. Leaking FBO"; if (mDepthBuffer) @@ -100,30 +100,17 @@ namespace MWVR if (mColorBuffer) glDeleteTextures(1, &mColorBuffer); - mFBO = mDepthBuffer = mColorBuffer; + mFBO = mDepthBuffer = mColorBuffer = 0; } - void VRTexture::beginFrame(osg::GraphicsContext* gc) + void VRFramebuffer::bindFramebuffer(osg::GraphicsContext* gc, uint32_t target) { auto state = gc->getState(); auto* gl = osg::GLExtensions::Get(state->getContextID(), false); - gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mFBO); + gl->glBindFramebuffer(target, mFBO); } - void VRTexture::endFrame(osg::GraphicsContext* gc, uint32_t 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, 0, 0, mWidth, mHeight, 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); - } - - void VRTexture::blit(osg::GraphicsContext* gc, int x, int y, int w, int h) + void VRFramebuffer::blit(osg::GraphicsContext* gc, int x, int y, int w, int h) { auto* state = gc->getState(); auto* gl = osg::GLExtensions::Get(state->getContextID(), false); @@ -131,17 +118,4 @@ namespace MWVR gl->glBlitFramebuffer(0, 0, mWidth, mHeight, x, y, w, h, GL_COLOR_BUFFER_BIT, GL_LINEAR); gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0); } - - void VRTexture::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_LINEAR); - 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/vrtexture.hpp b/apps/openmw/mwvr/vrframebuffer.hpp similarity index 67% rename from apps/openmw/mwvr/vrtexture.hpp rename to apps/openmw/mwvr/vrframebuffer.hpp index 7aff59429..8bed8c731 100644 --- a/apps/openmw/mwvr/vrtexture.hpp +++ b/apps/openmw/mwvr/vrframebuffer.hpp @@ -10,11 +10,14 @@ namespace MWVR { - class VRTexture : public osg::Referenced + /// \brief Manages an opengl framebuffer + /// + /// Intended for managing the vr swapchain, but is also use to manage the mirror texture as a convenience. + class VRFramebuffer : public osg::Referenced { public: - VRTexture(osg::ref_ptr state, std::size_t width, std::size_t height, uint32_t msaaSamples, uint32_t colorBuffer = 0, uint32_t depthBuffer = 0); - ~VRTexture(); + VRFramebuffer(osg::ref_ptr state, std::size_t width, std::size_t height, uint32_t msaaSamples, uint32_t colorBuffer = 0, uint32_t depthBuffer = 0); + ~VRFramebuffer(); void destroy(osg::State* state); @@ -22,15 +25,13 @@ namespace MWVR auto height() const { return mHeight; } auto msaaSamples() const { return mSamples; } - void beginFrame(osg::GraphicsContext* gc); - void endFrame(osg::GraphicsContext* gc, uint32_t blitTarget); + void bindFramebuffer(osg::GraphicsContext* gc, uint32_t target); 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() @@ -44,7 +45,9 @@ namespace MWVR uint32_t mFBO = 0; uint32_t mBlitFBO = 0; uint32_t mDepthBuffer = 0; + bool mOwnDepthBuffer = false; uint32_t mColorBuffer = 0; + bool mOwnColorBuffer = false; uint32_t mSamples = 0; uint32_t mTextureTarget = 0; }; diff --git a/apps/openmw/mwvr/vrgui.cpp b/apps/openmw/mwvr/vrgui.cpp index 800b0333a..8182191ba 100644 --- a/apps/openmw/mwvr/vrgui.cpp +++ b/apps/openmw/mwvr/vrgui.cpp @@ -327,8 +327,7 @@ namespace MWVR if (mLayerName == "Notification") { // The latest widget for notification is always the top one - // So we just have to stretch the rectangle to the bottom - // TODO: This might get deprecated with this new system? + // So i just stretch the rectangle to the bottom. mRealRect.bottom = 1.f; } } diff --git a/apps/openmw/mwvr/vrgui.hpp b/apps/openmw/mwvr/vrgui.hpp index 92a89d678..0351825ce 100644 --- a/apps/openmw/mwvr/vrgui.hpp +++ b/apps/openmw/mwvr/vrgui.hpp @@ -48,10 +48,11 @@ namespace MWVR Fixed }; + /// Configuration of a VRGUILayer struct LayerConfig { - int priority; //!< Higher priority shows over lower priority windows. - bool sideBySide; //!< Resize layer window to occupy full quad + int priority; //!< Higher priority shows over lower priority windows by moving higher priority layers slightly closer to the player. + bool sideBySide; //!< All layers with this config will show up side by side in a partial circle around the player, and will all be resized to a predefined size. osg::Vec4 backgroundColor; //!< Background color of layer osg::Vec3 offset; //!< Offset from tracked node in meters osg::Vec2 center; //!< Model space centerpoint of menu geometry. All menu geometries have model space lengths of 1 in each dimension. Use this to affect how geometries grow with changing size. @@ -66,6 +67,11 @@ namespace MWVR bool operator<(const LayerConfig& rhs) const { return priority < rhs.priority; } }; + /// \brief A single VR GUI Quad. + /// + /// In VR menus are shown as quads within the game world. + /// The behaviour of that quad is defined by the MWVR::LayerConfig struct + /// Each instance of VRGUILayer is used to show one MYGUI layer. class VRGUILayer { public: @@ -77,20 +83,19 @@ namespace MWVR VRGUIManager* parent); ~VRGUILayer(); + void update(); + + protected: + friend class VRGUIManager; osg::Camera* camera(); - osg::ref_ptr menuTexture(); - void setAngle(float angle); void updateTracking(const Pose& headPose = {}); void updatePose(); void updateRect(); - void update(); - void insertWidget(MWGui::Layout* widget); void removeWidget(MWGui::Layout* widget); int widgetCount() { return mWidgets.size(); } - bool operator<(const VRGUILayer& rhs) const { return mConfig.priority < rhs.mConfig.priority; } public: @@ -108,6 +113,7 @@ namespace MWVR osg::Quat mRotation{ 0,0,0,1 }; }; + /// \brief osg user data used to refer back to VRGUILayer objects when intersecting with the scene graph. class VRGUILayerUserData : public osg::Referenced { public: @@ -116,6 +122,11 @@ namespace MWVR std::weak_ptr mLayer; }; + /// \brief Manager of VRGUILayer objects. + /// + /// Constructs and destructs VRGUILayer objects in response to MWGui::Layout::setVisible calls. + /// Layers can also be made visible directly by calling insertLayer() directly, e.g. to show + /// the video player. class VRGUIManager { public: @@ -124,29 +135,33 @@ namespace MWVR ~VRGUIManager(void); + /// Set visibility of the layer this layout is on void setVisible(MWGui::Layout*, bool visible); - - void updateSideBySideLayers(); - + + /// Insert the given layer quad if it isn't already void insertLayer(const std::string& name); - void insertWidget(MWGui::Layout* widget); - + /// Remove the given layer quad void removeLayer(const std::string& name); - void removeWidget(MWGui::Layout* widget); - + /// Update layer quads based on player camera void updateTracking(void); + + /// Update layer quads based on the given camera void updateTracking(osg::Camera* camera); + /// Check current pointer target and update focus layer bool updateFocus(); - void setFocusLayer(VRGUILayer* layer); - + /// Gui cursor coordinates to use to simulate a mouse press/move if the player is currently pointing at a vr gui layer osg::Vec2i guiCursor() { return mGuiCursor; }; private: void computeGuiCursor(osg::Vec3 hitPoint); + void updateSideBySideLayers(); + void insertWidget(MWGui::Layout* widget); + void removeWidget(MWGui::Layout* widget); + void setFocusLayer(VRGUILayer* layer); osg::ref_ptr mOsgViewer{ nullptr }; diff --git a/apps/openmw/mwvr/vrinputmanager.cpp b/apps/openmw/mwvr/vrinputmanager.cpp index 047857334..3f9a673e1 100644 --- a/apps/openmw/mwvr/vrinputmanager.cpp +++ b/apps/openmw/mwvr/vrinputmanager.cpp @@ -1,11 +1,14 @@ #include "vrinputmanager.hpp" +#include "vrviewer.hpp" +#include "vrgui.hpp" #include "vranimation.hpp" #include "openxrinput.hpp" #include "vrenvironment.hpp" #include "openxrmanager.hpp" #include "openxrmanagerimpl.hpp" #include "openxraction.hpp" +#include "realisticcombat.hpp" #include @@ -139,15 +142,16 @@ namespace MWVR channel->setEnabled(true); } - // TODO: Configurable haptics: on/off + max intensity void VRInputManager::applyHapticsLeftHand(float intensity) { - mXRInput->applyHaptics(TrackedLimb::LEFT_HAND, intensity); + if (mHapticsEnabled) + mXRInput->applyHaptics(TrackedLimb::LEFT_HAND, intensity); } void VRInputManager::applyHapticsRightHand(float intensity) { - mXRInput->applyHaptics(TrackedLimb::RIGHT_HAND, intensity); + if (mHapticsEnabled) + mXRInput->applyHaptics(TrackedLimb::RIGHT_HAND, intensity); } void VRInputManager::requestRecenter() @@ -176,6 +180,7 @@ namespace MWVR controllerBindingsFile, grab) , mXRInput(nullptr) + , mHapticsEnabled{Settings::Manager::getBool("haptics enabled", "VR")} { std::vector suggestedBindings; // Set up default bindings for the oculus @@ -337,7 +342,7 @@ namespace MWVR auto& player = world->getPlayer(); auto playerPtr = world->getPlayerPtr(); - if (!mRealisticCombat || mRealisticCombat->ptr != playerPtr) + if (!mRealisticCombat || mRealisticCombat->ptr() != playerPtr) mRealisticCombat.reset(new RealisticCombat::StateMachine(playerPtr)); bool enabled = !guiMode && player.getDrawState() == MWMechanics::DrawState_Weapon && !player.isDisabled(); mRealisticCombat->update(dt, enabled); @@ -360,7 +365,7 @@ namespace MWVR void VRInputManager::processAction(const Action* action, float dt, bool disableControls) { static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input"); - auto* xrGUIManager = Environment::get().getGUIManager(); + auto* vrGuiManager = Environment::get().getGUIManager(); // OpenMW does not currently provide any way to directly request skipping a video. @@ -425,7 +430,7 @@ namespace MWVR mActionManager->screenshot(); break; case A_Recenter: - xrGUIManager->updateTracking(); + vrGuiManager->updateTracking(); requestRecenter(); break; case A_MenuSelect: @@ -601,7 +606,7 @@ namespace MWVR mActionManager->setAttemptJump(true); break; case A_Recenter: - xrGUIManager->updateTracking(); + vrGuiManager->updateTracking(); if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) requestRecenter(); break; diff --git a/apps/openmw/mwvr/vrinputmanager.hpp b/apps/openmw/mwvr/vrinputmanager.hpp index fc1d9baad..944e36046 100644 --- a/apps/openmw/mwvr/vrinputmanager.hpp +++ b/apps/openmw/mwvr/vrinputmanager.hpp @@ -1,8 +1,8 @@ #ifndef VR_INPUT_MANAGER_HPP #define VR_INPUT_MANAGER_HPP -#include "vrviewer.hpp" -#include "realisticcombat.hpp" +#include "vrtypes.hpp" + #include "../mwinput/inputmanagerimp.hpp" #include @@ -15,8 +15,11 @@ namespace MWVR { struct OpenXRInput; - /// As far as I can tell, SDL does not support VR controllers. - /// So I subclass the input manager and insert VR controls. + namespace RealisticCombat { + class StateMachine; + } + + /// Extension of the input manager to include VR inputs class VRInputManager : public MWInput::InputManager { public: @@ -72,6 +75,7 @@ namespace MWVR osg::Vec3 mHeadOffset{ 0,0,0 }; bool mShouldRecenter{ true }; bool mActivationIndication{ false }; + bool mHapticsEnabled{ true }; float mYaw{ 0.f }; float mVrAngles[3]{ 0.f,0.f,0.f }; diff --git a/apps/openmw/mwvr/vrsession.cpp b/apps/openmw/mwvr/vrsession.cpp index 2b1035485..f2fbf14f1 100644 --- a/apps/openmw/mwvr/vrsession.cpp +++ b/apps/openmw/mwvr/vrsession.cpp @@ -8,6 +8,7 @@ #include #include +#include #include @@ -37,7 +38,24 @@ namespace MWVR { VRSession::VRSession() + : mXrSyncPhase{FramePhase::Cull} { + auto syncPhase = Settings::Manager::getString("openxr sync phase", "VR"); + syncPhase = Misc::StringUtils::lowerCase(syncPhase); + if (syncPhase == "update") + mXrSyncPhase = FramePhase::Update; + else if (syncPhase == "cull") + mXrSyncPhase = FramePhase::Cull; + else if (syncPhase == "draw") + mXrSyncPhase = FramePhase::Draw; + else if (syncPhase == "swap") + mXrSyncPhase = FramePhase::Swap; + else + { + Log(Debug::Verbose) << "Invalid openxr sync phase " << syncPhase << ", defaulting to cull"; + return; + } + Log(Debug::Verbose) << "Using openxr sync phase " << syncPhase; } VRSession::~VRSession() @@ -177,13 +195,10 @@ namespace MWVR } - // TODO: Invokation should depend on earliest render rather than necessarily phase. - // Specifically. Without shadows this is fine because nothing is being rendered - // during cull or earlier. - // Thought: Add an Shadowmapping phase and invoke it from the shadow code - // But with shadows rendering occurs during cull and we must do frame sync before those calls. - // If you want to pay the FPS toll and play with shadows, change FramePhase::Draw to FramePhase::Cull or enjoy your eyes getting torn apart by jitters. - if (phase == FramePhase::Cull && getFrame(phase)->mShouldRender) + // TODO: Invokation could depend on earliest actual render rather than necessarily any specific phase. + // For example, shadows do some draw calls during cull an as such phase should be "Cull" or earlier with shadows enabled. + // But may be "Draw" without shadows. + if (phase == mXrSyncPhase && getFrame(phase)->mShouldRender) doFrameSync(); } diff --git a/apps/openmw/mwvr/vrsession.hpp b/apps/openmw/mwvr/vrsession.hpp index 8ad3d66dc..5b60c67b2 100644 --- a/apps/openmw/mwvr/vrsession.hpp +++ b/apps/openmw/mwvr/vrsession.hpp @@ -82,6 +82,7 @@ namespace MWVR private: std::mutex mMutex{}; std::condition_variable mCondition{}; + FramePhase mXrSyncPhase{ FramePhase::Cull }; long long mFrames{ 0 }; long long mLastRenderedFrame{ 0 }; diff --git a/apps/openmw/mwvr/vrview.cpp b/apps/openmw/mwvr/vrview.cpp index 6dc54743f..9d9a7938d 100644 --- a/apps/openmw/mwvr/vrview.cpp +++ b/apps/openmw/mwvr/vrview.cpp @@ -57,7 +57,8 @@ namespace MWVR { void VRView::prerenderCallback(osg::RenderInfo& renderInfo) { - if (mSwapchain) + + if (Environment::get().getManager()->xrSessionRunning()) { mSwapchain->beginFrame(renderInfo.getState()->getGraphicsContext()); } diff --git a/apps/openmw/mwvr/vrviewer.cpp b/apps/openmw/mwvr/vrviewer.cpp index 91c7cbde4..38b1d3d6f 100644 --- a/apps/openmw/mwvr/vrviewer.cpp +++ b/apps/openmw/mwvr/vrviewer.cpp @@ -3,6 +3,8 @@ #include "openxrmanagerimpl.hpp" #include "vrenvironment.hpp" #include "vrsession.hpp" +#include "vrframebuffer.hpp" +#include "vrview.hpp" #include "../mwrender/vismask.hpp" @@ -14,16 +16,25 @@ namespace MWVR "RightEye" }; + // Callback to do construction with a graphics context + class RealizeOperation : public osg::GraphicsOperation + { + public: + RealizeOperation() : osg::GraphicsOperation("VRRealizeOperation", false) {}; + void operator()(osg::GraphicsContext* gc) override; + bool realized(); + + private: + }; + VRViewer::VRViewer( osg::ref_ptr viewer) - : mRealizeOperation(new RealizeOperation()) - , mViewer(viewer) + : mViewer(viewer) , mPreDraw(new PredrawCallback(this)) , mPostDraw(new PostdrawCallback(this)) , mConfigured(false) { - mViewer->setRealizeOperation(mRealizeOperation); - //this->setName("OpenXRRoot"); + mViewer->setRealizeOperation(new RealizeOperation()); } VRViewer::~VRViewer(void) @@ -51,8 +62,7 @@ namespace MWVR mainCamera->setInitialDrawCallback(new VRView::InitialDrawCallback()); auto* xr = Environment::get().getManager(); - if (!xr->realized()) - xr->realize(context); + xr->realize(context); // Run through initial events to start session // For the rest of runtime this is handled by vrsession @@ -70,6 +80,9 @@ namespace MWVR // Configure eyes, their cameras, and their enslavement. osg::Vec4 clearColor = mainCamera->getClearColor(); auto config = xr->getRecommendedSwapchainConfig(); + bool mirror = Settings::Manager::getBool("mirror texture", "VR"); + // TODO: If mirror is false either hide the window or paste something meaningful into it. + // E.g. Fanart of Dagoth UR wearing a VR headset for (unsigned i = 0; i < sViewNames.size(); i++) { @@ -86,14 +99,15 @@ namespace MWVR mViewer->addSlave(camera, true); mViewer->getSlave(i)._updateSlaveCallback = new VRView::UpdateSlaveCallback(view, context); - mMsaaResolveMirrorTexture[i].reset(new VRTexture(context->getState(), - view->swapchain().width(), - view->swapchain().height(), - 0)); + if (mirror) + mMsaaResolveMirrorTexture[i].reset(new VRFramebuffer(context->getState(), + view->swapchain().width(), + view->swapchain().height(), + 0)); } - - mMirrorTexture.reset(new VRTexture(context->getState(), mainCamera->getViewport()->width(), mainCamera->getViewport()->height(), 0)); + if (mirror) + mMirrorTexture.reset(new VRFramebuffer(context->getState(), mainCamera->getViewport()->width(), mainCamera->getViewport()->height(), 0)); mViewer->setReleaseContextAtEndOfFrameHint(false); mMainCameraGC = mainCamera->getGraphicsContext(); @@ -124,6 +138,9 @@ namespace MWVR void VRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc) { + if (!mMirrorTexture) + return; + auto* state = gc->getState(); auto* gl = osg::GLExtensions::Get(state->getContextID(), false); @@ -131,12 +148,14 @@ namespace MWVR int mirrorWidth = screenWidth / 2; int screenHeight = mCameras["MainCamera"]->getViewport()->height(); + // Since OpenXR does not include native support for mirror textures, we have to generate them ourselves + // which means resolving msaa twice. If this is a performance concern, add an option to disable the mirror texture. for (unsigned i = 0; i < sViewNames.size(); i++) { auto& resolveTexture = *mMsaaResolveMirrorTexture[i]; - resolveTexture.beginFrame(gc); + resolveTexture.bindFramebuffer(gc, GL_FRAMEBUFFER_EXT); mViews[sViewNames[i]]->swapchain().renderBuffer()->blit(gc, 0, 0, resolveTexture.width(), resolveTexture.height()); - mMirrorTexture->beginFrame(gc); + mMirrorTexture->bindFramebuffer(gc, GL_FRAMEBUFFER_EXT); // Mirror the index when rendering to the mirror texture to allow cross eye mirror textures. unsigned mirrorIndex = sViewNames.size() - 1 - i; resolveTexture.blit(gc, mirrorIndex * mirrorWidth, 0, (mirrorIndex + 1) * mirrorWidth, screenHeight); @@ -155,16 +174,14 @@ namespace MWVR } void - VRViewer::RealizeOperation::operator()( + RealizeOperation::operator()( osg::GraphicsContext* gc) { - OpenXRManager::RealizeOperation::operator()(gc); - - Environment::get().getViewer()->realize(gc); + return Environment::get().getViewer()->realize(gc); } bool - VRViewer::RealizeOperation::realized() + RealizeOperation::realized() { return Environment::get().getViewer()->realized(); } diff --git a/apps/openmw/mwvr/vrviewer.hpp b/apps/openmw/mwvr/vrviewer.hpp index 627b8aad2..83c203ccd 100644 --- a/apps/openmw/mwvr/vrviewer.hpp +++ b/apps/openmw/mwvr/vrviewer.hpp @@ -4,16 +4,20 @@ #include #include #include + #include #include #include #include "openxrmanager.hpp" -#include "vrgui.hpp" + #include namespace MWVR { + class VRFramebuffer; + class VRView; + /// \brief Manages stereo rendering and mirror texturing. /// /// Manipulates the osgViewer by disabling main camera rendering, and instead rendering to @@ -21,16 +25,6 @@ namespace MWVR class VRViewer { public: - class RealizeOperation : public OpenXRManager::RealizeOperation - { - public: - RealizeOperation() {}; - void operator()(osg::GraphicsContext* gc) override; - bool realized() override; - - private: - }; - class SwapBuffersCallback : public osg::GraphicsContext::SwapCallback { public: @@ -90,15 +84,14 @@ namespace MWVR void disableMainCamera(void); private: - osg::ref_ptr mRealizeOperation = nullptr; osg::ref_ptr mViewer = nullptr; std::map > mViews{}; std::map > mCameras{}; osg::ref_ptr mPreDraw{ nullptr }; osg::ref_ptr mPostDraw{ nullptr }; osg::GraphicsContext* mMainCameraGC{ nullptr }; - std::unique_ptr mMsaaResolveMirrorTexture[2]{ }; - std::unique_ptr mMirrorTexture{ nullptr }; + std::unique_ptr mMsaaResolveMirrorTexture[2]{ }; + std::unique_ptr mMirrorTexture{ nullptr }; std::mutex mMutex;