1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-02-04 22:45:35 +00:00

Comments etc

This commit is contained in:
Mads Buvik Sandvei 2020-06-28 11:33:01 +02:00
parent 91de6392ca
commit 45656f1d06
24 changed files with 319 additions and 282 deletions

View file

@ -152,8 +152,8 @@ if(BUILD_VR_OPENXR)
mwvr/vrinput.cpp mwvr/vrinput.cpp
mwvr/vrsession.hpp mwvr/vrsession.hpp
mwvr/vrsession.cpp mwvr/vrsession.cpp
mwvr/vrtexture.hpp mwvr/vrframebuffer.hpp
mwvr/vrtexture.cpp mwvr/vrframebuffer.cpp
mwvr/vrtypes.hpp mwvr/vrtypes.hpp
mwvr/vrtypes.cpp mwvr/vrtypes.cpp
mwvr/vrview.hpp mwvr/vrview.hpp

View file

@ -62,6 +62,7 @@
#ifdef USE_OPENXR #ifdef USE_OPENXR
#include "mwvr/vrinputmanager.hpp" #include "mwvr/vrinputmanager.hpp"
#include "mwvr/vrviewer.hpp" #include "mwvr/vrviewer.hpp"
#include "mwvr/vrgui.hpp"
#endif #endif
namespace namespace

View file

@ -111,26 +111,11 @@ namespace MWVR
return impl().getRecommendedSwapchainConfig(); 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 void
OpenXRManager::CleanupOperation::operator()( OpenXRManager::CleanupOperation::operator()(
osg::GraphicsContext* gc) osg::GraphicsContext* gc)
{ {
// TODO: Use this to make proper cleanup such as cleaning up VRFramebuffers.
} }
} }

View file

@ -26,16 +26,6 @@ namespace MWVR
class OpenXRManager : public osg::Referenced class OpenXRManager : public osg::Referenced
{ {
public: 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 class CleanupOperation : public osg::GraphicsOperation
{ {
public: public:
@ -51,6 +41,7 @@ namespace MWVR
~OpenXRManager(); ~OpenXRManager();
/// Manager has been initialized.
bool realized(); bool realized();
//! Forward call to xrWaitFrame() //! Forward call to xrWaitFrame()

View file

@ -2,7 +2,6 @@
#include "openxrswapchain.hpp" #include "openxrswapchain.hpp"
#include "openxrswapchainimpl.hpp" #include "openxrswapchainimpl.hpp"
#include "vrtexture.hpp"
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlgraphicswindow.hpp> #include <components/sdlutil/sdlgraphicswindow.hpp>
@ -445,13 +444,11 @@ namespace MWVR
switch (newState) switch (newState)
{ {
case XR_SESSION_STATE_READY: case XR_SESSION_STATE_READY:
//case XR_SESSION_STATE_IDLE:
{ {
XrSessionBeginInfo beginInfo{ XR_TYPE_SESSION_BEGIN_INFO }; XrSessionBeginInfo beginInfo{ XR_TYPE_SESSION_BEGIN_INFO };
beginInfo.primaryViewConfigurationType = mViewConfigType; beginInfo.primaryViewConfigurationType = mViewConfigType;
CHECK_XRCMD(xrBeginSession(mSession, &beginInfo)); CHECK_XRCMD(xrBeginSession(mSession, &beginInfo));
mSessionRunning = true; mSessionRunning = true;
//waitFrame();
break; break;
} }
case XR_SESSION_STATE_STOPPING: case XR_SESSION_STATE_STOPPING:

View file

@ -33,19 +33,14 @@ namespace MWVR {
return impl().endFrame(gc); 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); return impl().acquiredDepthTexture();
}
uint32_t OpenXRSwapchain::acquiredImage() const
{
return impl().acquiredImage();
} }
int OpenXRSwapchain::width() const int OpenXRSwapchain::width() const
@ -68,7 +63,7 @@ namespace MWVR {
return impl().isAcquired(); return impl().isAcquired();
} }
VRTexture* OpenXRSwapchain::renderBuffer() const VRFramebuffer* OpenXRSwapchain::renderBuffer() const
{ {
return impl().renderBuffer(); return impl().renderBuffer();
} }

View file

@ -2,13 +2,13 @@
#define OPENXR_SWAPCHAIN_HPP #define OPENXR_SWAPCHAIN_HPP
#include "openxrmanager.hpp" #include "openxrmanager.hpp"
#include "vrtexture.hpp"
struct XrSwapchainSubImage; struct XrSwapchainSubImage;
namespace MWVR namespace MWVR
{ {
class OpenXRSwapchainImpl; class OpenXRSwapchainImpl;
class VRFramebuffer;
/// \brief Creation and management of openxr swapchains /// \brief Creation and management of openxr swapchains
class OpenXRSwapchain class OpenXRSwapchain
@ -20,29 +20,40 @@ namespace MWVR
public: public:
//! Prepare for render (set FBO) //! Prepare for render (set FBO)
void beginFrame(osg::GraphicsContext* gc); void beginFrame(osg::GraphicsContext* gc);
//! Finalize render //! Finalize render
void endFrame(osg::GraphicsContext* gc); void endFrame(osg::GraphicsContext* gc);
//! Prepare for render
void acquire(osg::GraphicsContext* gc); //! Currently acquired color texture
//! Finalize render uint32_t acquiredColorTexture() const;
void release(osg::GraphicsContext* gc);
//! Currently acquired image //! Currently acquired depth texture
uint32_t acquiredImage() const; uint32_t acquiredDepthTexture() const;
//! Whether subchain is currently acquired (true) or released (false) //! Whether subchain is currently acquired (true) or released (false)
bool isAcquired() const; bool isAcquired() const;
//! Width of the view surface //! Width of the view surface
int width() const; int width() const;
//! Height of the view surface //! Height of the view surface
int height() const; int height() const;
//! Samples of the view surface //! Samples of the view surface
int samples() const; int samples() const;
//! Get the current texture //! Get the current texture
VRTexture* renderBuffer() const; VRFramebuffer* renderBuffer() const;
//! Get the private implementation //! Get the private implementation
OpenXRSwapchainImpl& impl() { return *mPrivate; } OpenXRSwapchainImpl& impl() { return *mPrivate; }
//! Get the private implementation //! Get the private implementation
const OpenXRSwapchainImpl& impl() const { return *mPrivate; } const OpenXRSwapchainImpl& impl() const { return *mPrivate; }
protected:
OpenXRSwapchain(const OpenXRSwapchain&) = delete;
void operator=(const OpenXRSwapchain&) = delete;
private: private:
std::unique_ptr<OpenXRSwapchainImpl> mPrivate; std::unique_ptr<OpenXRSwapchainImpl> mPrivate;
}; };

View file

@ -1,5 +1,6 @@
#include "openxrswapchainimpl.hpp" #include "openxrswapchainimpl.hpp"
#include "vrenvironment.hpp" #include "vrenvironment.hpp"
#include "vrframebuffer.hpp"
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
@ -47,9 +48,8 @@ namespace MWVR {
// Find supported depth swapchain format. // Find supported depth swapchain format.
constexpr int64_t RequestedDepthSwapchainFormats[] = { constexpr int64_t RequestedDepthSwapchainFormats[] = {
GL_DEPTH_COMPONENT32F,
GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT24,
GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT32F,
}; };
swapchainFormatIt = swapchainFormatIt =
@ -108,7 +108,7 @@ namespace MWVR {
CHECK_XRCMD(xrEnumerateSwapchainImages(mDepthSwapchain, imageCount, &imageCount, reinterpret_cast<XrSwapchainImageBaseHeader*>(mDepthSwapchainImageBuffers.data()))); CHECK_XRCMD(xrEnumerateSwapchainImages(mDepthSwapchain, imageCount, &imageCount, reinterpret_cast<XrSwapchainImageBaseHeader*>(mDepthSwapchainImageBuffers.data())));
for (unsigned i = 0; i < imageCount; i++) 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.swapchain = mSwapchain;
mSubImage.imageRect.offset = { 0, 0 }; mSubImage.imageRect.offset = { 0, 0 };
@ -121,18 +121,22 @@ namespace MWVR {
CHECK_XRCMD(xrDestroySwapchain(mSwapchain)); CHECK_XRCMD(xrDestroySwapchain(mSwapchain));
} }
VRTexture* OpenXRSwapchainImpl::renderBuffer() const VRFramebuffer* OpenXRSwapchainImpl::renderBuffer() const
{ {
if (isAcquired()) checkAcquired();
return mRenderBuffers[mAcquiredImageIndex].get(); return mRenderBuffers[mAcquiredImageIndex].get();
throw std::logic_error("Swapbuffer not acquired before use");
} }
uint32_t OpenXRSwapchainImpl::acquiredImage() const uint32_t OpenXRSwapchainImpl::acquiredColorTexture() const
{ {
if (isAcquired()) checkAcquired();
return mSwapchainImageBuffers[mAcquiredImageIndex].image;
}
uint32_t OpenXRSwapchainImpl::acquiredDepthTexture() const
{
checkAcquired();
return mSwapchainImageBuffers[mAcquiredImageIndex].image; return mSwapchainImageBuffers[mAcquiredImageIndex].image;
throw std::logic_error("Swapbuffer not acquired before use");
} }
bool OpenXRSwapchainImpl::isAcquired() const bool OpenXRSwapchainImpl::isAcquired() const
@ -142,14 +146,17 @@ namespace MWVR {
void OpenXRSwapchainImpl::beginFrame(osg::GraphicsContext* gc) void OpenXRSwapchainImpl::beginFrame(osg::GraphicsContext* gc)
{ {
if (isAcquired())
throw std::logic_error("Trying to acquire already acquired swapchain");
acquire(gc); acquire(gc);
renderBuffer()->beginFrame(gc); renderBuffer()->bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
} }
int swapCount = 0; int swapCount = 0;
void OpenXRSwapchainImpl::endFrame(osg::GraphicsContext* gc) void OpenXRSwapchainImpl::endFrame(osg::GraphicsContext* gc)
{ {
checkAcquired();
release(gc); release(gc);
} }
@ -180,4 +187,9 @@ namespace MWVR {
CHECK_XRCMD(xrReleaseSwapchainImage(mSwapchain, &releaseInfo)); CHECK_XRCMD(xrReleaseSwapchainImage(mSwapchain, &releaseInfo));
CHECK_XRCMD(xrReleaseSwapchainImage(mDepthSwapchain, &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()");
}
} }

View file

@ -17,12 +17,10 @@ namespace MWVR
void beginFrame(osg::GraphicsContext* gc); void beginFrame(osg::GraphicsContext* gc);
void endFrame(osg::GraphicsContext* gc); void endFrame(osg::GraphicsContext* gc);
void acquire(osg::GraphicsContext* gc);
void release(osg::GraphicsContext* gc);
VRTexture* renderBuffer() const; VRFramebuffer* renderBuffer() const;
uint32_t acquiredColorTexture() const;
uint32_t acquiredImage() const; uint32_t acquiredDepthTexture() const;
bool isAcquired() const; bool isAcquired() const;
XrSwapchain xrSwapchain(void) const { return mSwapchain; }; XrSwapchain xrSwapchain(void) const { return mSwapchain; };
@ -31,6 +29,15 @@ namespace MWVR
int height() const { return mHeight; }; int height() const { return mHeight; };
int samples() const { return mSamples; }; 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 mSwapchain = XR_NULL_HANDLE;
XrSwapchain mDepthSwapchain = XR_NULL_HANDLE; XrSwapchain mDepthSwapchain = XR_NULL_HANDLE;
std::vector<XrSwapchainImageOpenGLKHR> mSwapchainImageBuffers{}; std::vector<XrSwapchainImageOpenGLKHR> mSwapchainImageBuffers{};
@ -42,7 +49,7 @@ namespace MWVR
int64_t mSwapchainColorFormat = -1; int64_t mSwapchainColorFormat = -1;
int64_t mSwapchainDepthFormat = -1; int64_t mSwapchainDepthFormat = -1;
uint32_t mFBO = 0; uint32_t mFBO = 0;
std::vector<std::unique_ptr<VRTexture> > mRenderBuffers{}; std::vector<std::unique_ptr<VRFramebuffer> > mRenderBuffers{};
int mRenderBuffer{ 0 }; int mRenderBuffer{ 0 };
uint32_t mAcquiredImageIndex{ 0 }; uint32_t mAcquiredImageIndex{ 0 };
bool mIsAcquired{ false }; bool mIsAcquired{ false };

View file

@ -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 "realisticcombat.hpp"
#include "../mwbase/environment.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() bool StateMachine::canSwing()
{ {
if (swingType >= 0) if (mSwingType >= 0)
if (velocity >= minVelocity) if (mVelocity >= mMinVelocity)
if (swingType != ESM::Weapon::AT_Thrust || thrustVelocity >= 0.f) if (mSwingType != ESM::Weapon::AT_Thrust || mThrustVelocity >= 0.f)
return true; return true;
return false; return false;
} }
@ -85,20 +68,20 @@ namespace MWVR {
void StateMachine::transition( void StateMachine::transition(
SwingState newState) SwingState newState)
{ {
Log(Debug::Verbose) << "Transition:" << stateToString(state) << "->" << stateToString(newState); Log(Debug::Verbose) << "Transition:" << stateToString(mState) << "->" << stateToString(newState);
maxSwingVelocity = 0.f; mMaxSwingVelocity = 0.f;
timeSinceEnteredState = 0.f; mTimeSinceEnteredState = 0.f;
movementSinceEnteredState = 0.f; mMovementSinceEnteredState = 0.f;
state = newState; mState = newState;
} }
void StateMachine::reset() void StateMachine::reset()
{ {
maxSwingVelocity = 0.f; mMaxSwingVelocity = 0.f;
timeSinceEnteredState = 0.f; mTimeSinceEnteredState = 0.f;
velocity = 0.f; mVelocity = 0.f;
previousPosition = osg::Vec3(0.f, 0.f, 0.f); mPreviousPosition = osg::Vec3(0.f, 0.f, 0.f);
state = SwingState_Ready; mState = SwingState_Ready;
} }
static bool isMeleeWeapon(int type) static bool isMeleeWeapon(int type)
@ -151,7 +134,7 @@ namespace MWVR {
if (!enabled) if (!enabled)
return; return;
timeSinceEnteredState += dt; mTimeSinceEnteredState += dt;
// First determine direction of different swing types // 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. // 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 // Theoretically, the player's hand really could be at 0,0,0
// but that's a super rare case so whatever. // but that's a super rare case so whatever.
if (previousPosition == osg::Vec3(0.f, 0.f, 0.f)) if (mPreviousPosition == osg::Vec3(0.f, 0.f, 0.f))
previousPosition = handPose.position; mPreviousPosition = handPose.position;
osg::Vec3 movement = handPose.position - previousPosition; osg::Vec3 movement = handPose.position - mPreviousPosition;
movementSinceEnteredState += movement.length(); mMovementSinceEnteredState += movement.length();
previousPosition = handPose.position; mPreviousPosition = handPose.position;
osg::Vec3 swingVector = movement / dt; osg::Vec3 swingVector = movement / dt;
osg::Vec3 swingDirection = swingVector; osg::Vec3 swingDirection = swingVector;
swingDirection.normalize(); swingDirection.normalize();
@ -193,24 +176,24 @@ namespace MWVR {
// Compute swing velocities // Compute swing velocities
// Thrust follows the orientation of the weapon. Negative thrust = no attack. // Thrust follows the orientation of the weapon. Negative thrust = no attack.
thrustVelocity = swingVector * thrustDirection; mThrustVelocity = swingVector * thrustDirection;
velocity = swingVector.length(); mVelocity = swingVector.length();
if (isSideSwingValidForWeapon(weaponType)) if (isSideSwingValidForWeapon(weaponType))
{ {
// Compute velocity in the plane normal to the thrust direction. // 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); float planeComponent = std::sqrt(1 - thrustComponent * thrustComponent);
slashChopVelocity = velocity * planeComponent; mSlashChopVelocity = mVelocity * planeComponent;
sideVelocity = -1000.f; mSideVelocity = -1000.f;
} }
else else
{ {
// If side swing is not valid for the weapon, count slash/chop only along in // If side swing is not valid for the weapon, count slash/chop only along in
// the direction of the weapon's edge. // the direction of the weapon's edge.
slashChopVelocity = std::abs(swingVector * slashChopDirection); mSlashChopVelocity = std::abs(swingVector * slashChopDirection);
sideVelocity = std::abs(swingVector * sideDirection); mSideVelocity = std::abs(swingVector * sideDirection);
} }
@ -220,34 +203,34 @@ namespace MWVR {
// Pick swing type based on greatest current velocity // Pick swing type based on greatest current velocity
// Note i use abs() of thrust velocity to prevent accidentally triggering // Note i use abs() of thrust velocity to prevent accidentally triggering
// chop/slash when player is withdrawing the weapon. // 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 // Player is swinging with the "blunt" side of a weapon that
// cannot be used that way. // 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 else
{ {
// First check if the weapon is pointing upwards. In which case slash is not // First check if the weapon is pointing upwards. In which case slash is not
// applicable, and the attack must be a chop. // applicable, and the attack must be a chop.
if (orientationVerticality > 0.707) if (orientationVerticality > 0.707)
swingType = ESM::Weapon::AT_Chop; mSwingType = ESM::Weapon::AT_Chop;
else else
{ {
// Next check if the swing is more horizontal or vertical. A slash // Next check if the swing is more horizontal or vertical. A slash
// would be more horizontal. // would be more horizontal.
if (swingVerticality > 0.707) if (swingVerticality > 0.707)
swingType = ESM::Weapon::AT_Chop; mSwingType = ESM::Weapon::AT_Chop;
else else
swingType = ESM::Weapon::AT_Slash; mSwingType = ESM::Weapon::AT_Slash;
} }
} }
switch (state) switch (mState)
{ {
case SwingState_Cooldown: case SwingState_Cooldown:
return update_cooldownState(); return update_cooldownState();
@ -260,13 +243,13 @@ namespace MWVR {
case SwingState_Launch: case SwingState_Launch:
return update_launchState(); return update_launchState();
default: 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() void StateMachine::update_cooldownState()
{ {
if (timeSinceEnteredState >= minimumPeriod) if (mTimeSinceEnteredState >= mMinimumPeriod)
transition_cooldownToReady(); transition_cooldownToReady();
} }
@ -291,19 +274,19 @@ namespace MWVR {
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
std::string sound = "Weapon Swish"; std::string sound = "Weapon Swish";
if (strength < 0.5f) if (mStrength < 0.5f)
sndMgr->playSound3D(ptr, sound, 1.0f, 0.8f); //Weak attack sndMgr->playSound3D(mPtr, sound, 1.0f, 0.8f); //Weak attack
if (strength < 1.0f) if (mStrength < 1.0f)
sndMgr->playSound3D(ptr, sound, 1.0f, 1.0f); //Medium attack sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f); //Medium attack
else 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() void StateMachine::update_launchState()
{ {
if (movementSinceEnteredState > minimumPeriod) if (mMovementSinceEnteredState > mMinimumPeriod)
transition_launchToSwing(); transition_launchToSwing();
if (!canSwing()) if (!canSwing())
return transition_launchToReady(); return transition_launchToReady();
@ -326,26 +309,26 @@ namespace MWVR {
void StateMachine::update_swingState() void StateMachine::update_swingState()
{ {
maxSwingVelocity = std::max(velocity, maxSwingVelocity); mMaxSwingVelocity = std::max(mVelocity, mMaxSwingVelocity);
strength = std::min(1.f, (maxSwingVelocity - minVelocity) / maxVelocity); mStrength = std::min(1.f, (mMaxSwingVelocity - mMinVelocity) / mMaxVelocity);
// When velocity falls below minimum, transition to register the miss // When velocity falls below minimum, transition to register the miss
if (!canSwing()) if (!canSwing())
return transition_swingingToImpact(); return transition_swingingToImpact();
// Call hit with simulated=true to check for hit without actually causing an impact // 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(); return transition_swingingToImpact();
} }
void StateMachine::transition_swingingToImpact() void StateMachine::transition_swingingToImpact()
{ {
ptr.getClass().hit(ptr, strength, swingType, false); mPtr.getClass().hit(mPtr, mStrength, mSwingType, false);
transition(SwingState_Impact); transition(SwingState_Impact);
} }
void StateMachine::update_impactState() void StateMachine::update_impactState()
{ {
if (velocity < minVelocity) if (mVelocity < mMinVelocity)
return transition_impactToCooldown(); return transition_impactToCooldown();
} }

View file

@ -12,6 +12,8 @@
namespace MWVR { namespace MWVR {
namespace RealisticCombat { namespace RealisticCombat {
/// Enum describing the current state of the MWVR::RealisticCombat::StateMachine
enum SwingState enum SwingState
{ {
SwingState_Ready, SwingState_Ready,
@ -21,35 +23,38 @@ namespace MWVR {
SwingState_Cooldown, 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 struct StateMachine
{ {
// TODO: These should be configurable public:
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 };
StateMachine(MWWorld::Ptr ptr); StateMachine(MWWorld::Ptr ptr);
void update(float dt, bool enabled);
MWWorld::Ptr ptr() { return mPtr; }
protected:
bool canSwing(); bool canSwing();
void playSwish(); void playSwish();
@ -57,7 +62,6 @@ namespace MWVR {
void transition(SwingState newState); void transition(SwingState newState);
void update(float dt, bool enabled);
void update_cooldownState(); void update_cooldownState();
void transition_cooldownToReady(); void transition_cooldownToReady();
@ -74,6 +78,31 @@ namespace MWVR {
void update_impactState(); void update_impactState();
void transition_impactToCooldown(); 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 };
}; };
} }

View file

@ -459,11 +459,10 @@ namespace MWVR
MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace); MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
bool isMale = ref->mBase->isMale(); bool isMale = ref->mBase->isMale();
float charHeightFactor = isMale ? race->mData.mHeight.mMale : race->mData.mHeight.mFemale; 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; float charHeight = charHeightBase * charHeightFactor;
// TODO: Player height should be configurable float realHeight = Settings::Manager::getFloat("real height", "VR");
// For now i'm just using my own float sizeFactor = charHeight / realHeight;
float sizeFactor = 1.85f / charHeight;
Environment::get().getSession()->setPlayerScale(sizeFactor); Environment::get().getSession()->setPlayerScale(sizeFactor);
} }

View file

@ -13,7 +13,7 @@ namespace MWVR
class FingerController; class FingerController;
class ForearmController; class ForearmController;
/// Subclassing NpcAnimation to override behaviours not compatible with VR /// Subclassing NpcAnimation to implement VR related behaviour
class VRAnimation : public MWRender::NpcAnimation class VRAnimation : public MWRender::NpcAnimation
{ {
protected: protected:

View file

@ -1,4 +1,4 @@
#include "vrtexture.hpp" #include "vrframebuffer.hpp"
#include <osg/Texture2D> #include <osg/Texture2D>
@ -11,7 +11,7 @@
namespace MWVR namespace MWVR
{ {
VRTexture::VRTexture(osg::ref_ptr<osg::State> state, std::size_t width, std::size_t height, uint32_t msaaSamples, uint32_t colorBuffer, uint32_t depthBuffer) VRFramebuffer::VRFramebuffer(osg::ref_ptr<osg::State> state, std::size_t width, std::size_t height, uint32_t msaaSamples, uint32_t colorBuffer, uint32_t depthBuffer)
: mState(state) : mState(state)
, mWidth(width) , mWidth(width)
, mHeight(height) , mHeight(height)
@ -72,12 +72,12 @@ namespace MWVR
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
} }
VRTexture::~VRTexture() VRFramebuffer::~VRFramebuffer()
{ {
destroy(nullptr); destroy(nullptr);
} }
void VRTexture::destroy(osg::State* state) void VRFramebuffer::destroy(osg::State* state)
{ {
if (!state) if (!state)
{ {
@ -92,7 +92,7 @@ namespace MWVR
gl->glDeleteFramebuffers(1, &mFBO); gl->glDeleteFramebuffers(1, &mFBO);
} }
else if (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"; Log(Debug::Warning) << "destroy() called without a State. Leaking FBO";
if (mDepthBuffer) if (mDepthBuffer)
@ -100,30 +100,17 @@ namespace MWVR
if (mColorBuffer) if (mColorBuffer)
glDeleteTextures(1, &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 state = gc->getState();
auto* gl = osg::GLExtensions::Get(state->getContextID(), false); 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) 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);
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)
{ {
auto* state = gc->getState(); auto* state = gc->getState();
auto* gl = osg::GLExtensions::Get(state->getContextID(), false); 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->glBlitFramebuffer(0, 0, mWidth, mHeight, x, y, w, h, GL_COLOR_BUFFER_BIT, GL_LINEAR);
gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0); 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);
}
} }

View file

@ -10,11 +10,14 @@
namespace MWVR 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: public:
VRTexture(osg::ref_ptr<osg::State> state, std::size_t width, std::size_t height, uint32_t msaaSamples, uint32_t colorBuffer = 0, uint32_t depthBuffer = 0); VRFramebuffer(osg::ref_ptr<osg::State> state, std::size_t width, std::size_t height, uint32_t msaaSamples, uint32_t colorBuffer = 0, uint32_t depthBuffer = 0);
~VRTexture(); ~VRFramebuffer();
void destroy(osg::State* state); void destroy(osg::State* state);
@ -22,15 +25,13 @@ namespace MWVR
auto height() const { return mHeight; } auto height() const { return mHeight; }
auto msaaSamples() const { return mSamples; } auto msaaSamples() const { return mSamples; }
void beginFrame(osg::GraphicsContext* gc); void bindFramebuffer(osg::GraphicsContext* gc, uint32_t target);
void endFrame(osg::GraphicsContext* gc, uint32_t blitTarget);
uint32_t fbo(void) const { return mFBO; } uint32_t fbo(void) const { return mFBO; }
uint32_t colorBuffer(void) const { return mColorBuffer; } uint32_t colorBuffer(void) const { return mColorBuffer; }
//! Blit to region in currently bound draw fbo //! 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);
void blit(osg::GraphicsContext* gc, int x, int y, int w, int h, int target);
private: private:
// Set aside a weak pointer to the constructor state to use when freeing FBOs, if no state is given to destroy() // 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 mFBO = 0;
uint32_t mBlitFBO = 0; uint32_t mBlitFBO = 0;
uint32_t mDepthBuffer = 0; uint32_t mDepthBuffer = 0;
bool mOwnDepthBuffer = false;
uint32_t mColorBuffer = 0; uint32_t mColorBuffer = 0;
bool mOwnColorBuffer = false;
uint32_t mSamples = 0; uint32_t mSamples = 0;
uint32_t mTextureTarget = 0; uint32_t mTextureTarget = 0;
}; };

View file

@ -327,8 +327,7 @@ namespace MWVR
if (mLayerName == "Notification") if (mLayerName == "Notification")
{ {
// The latest widget for notification is always the top one // The latest widget for notification is always the top one
// So we just have to stretch the rectangle to the bottom // So i just stretch the rectangle to the bottom.
// TODO: This might get deprecated with this new system?
mRealRect.bottom = 1.f; mRealRect.bottom = 1.f;
} }
} }

View file

@ -48,10 +48,11 @@ namespace MWVR
Fixed Fixed
}; };
/// Configuration of a VRGUILayer
struct LayerConfig struct LayerConfig
{ {
int priority; //!< Higher priority shows over lower priority windows. int priority; //!< Higher priority shows over lower priority windows by moving higher priority layers slightly closer to the player.
bool sideBySide; //!< Resize layer window to occupy full quad 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::Vec4 backgroundColor; //!< Background color of layer
osg::Vec3 offset; //!< Offset from tracked node in meters 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. 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; } 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 class VRGUILayer
{ {
public: public:
@ -77,20 +83,19 @@ namespace MWVR
VRGUIManager* parent); VRGUIManager* parent);
~VRGUILayer(); ~VRGUILayer();
void update();
protected:
friend class VRGUIManager;
osg::Camera* camera(); osg::Camera* camera();
osg::ref_ptr<osg::Texture2D> menuTexture(); osg::ref_ptr<osg::Texture2D> menuTexture();
void setAngle(float angle); void setAngle(float angle);
void updateTracking(const Pose& headPose = {}); void updateTracking(const Pose& headPose = {});
void updatePose(); void updatePose();
void updateRect(); void updateRect();
void update();
void insertWidget(MWGui::Layout* widget); void insertWidget(MWGui::Layout* widget);
void removeWidget(MWGui::Layout* widget); void removeWidget(MWGui::Layout* widget);
int widgetCount() { return mWidgets.size(); } int widgetCount() { return mWidgets.size(); }
bool operator<(const VRGUILayer& rhs) const { return mConfig.priority < rhs.mConfig.priority; } bool operator<(const VRGUILayer& rhs) const { return mConfig.priority < rhs.mConfig.priority; }
public: public:
@ -108,6 +113,7 @@ namespace MWVR
osg::Quat mRotation{ 0,0,0,1 }; 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 class VRGUILayerUserData : public osg::Referenced
{ {
public: public:
@ -116,6 +122,11 @@ namespace MWVR
std::weak_ptr<VRGUILayer> mLayer; std::weak_ptr<VRGUILayer> 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 class VRGUIManager
{ {
public: public:
@ -124,29 +135,33 @@ namespace MWVR
~VRGUIManager(void); ~VRGUIManager(void);
/// Set visibility of the layer this layout is on
void setVisible(MWGui::Layout*, bool visible); 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 insertLayer(const std::string& name);
void insertWidget(MWGui::Layout* widget); /// Remove the given layer quad
void removeLayer(const std::string& name); void removeLayer(const std::string& name);
void removeWidget(MWGui::Layout* widget); /// Update layer quads based on player camera
void updateTracking(void); void updateTracking(void);
/// Update layer quads based on the given camera
void updateTracking(osg::Camera* camera); void updateTracking(osg::Camera* camera);
/// Check current pointer target and update focus layer
bool updateFocus(); 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; }; osg::Vec2i guiCursor() { return mGuiCursor; };
private: private:
void computeGuiCursor(osg::Vec3 hitPoint); void computeGuiCursor(osg::Vec3 hitPoint);
void updateSideBySideLayers();
void insertWidget(MWGui::Layout* widget);
void removeWidget(MWGui::Layout* widget);
void setFocusLayer(VRGUILayer* layer);
osg::ref_ptr<osgViewer::Viewer> mOsgViewer{ nullptr }; osg::ref_ptr<osgViewer::Viewer> mOsgViewer{ nullptr };

View file

@ -1,11 +1,14 @@
#include "vrinputmanager.hpp" #include "vrinputmanager.hpp"
#include "vrviewer.hpp"
#include "vrgui.hpp"
#include "vranimation.hpp" #include "vranimation.hpp"
#include "openxrinput.hpp" #include "openxrinput.hpp"
#include "vrenvironment.hpp" #include "vrenvironment.hpp"
#include "openxrmanager.hpp" #include "openxrmanager.hpp"
#include "openxrmanagerimpl.hpp" #include "openxrmanagerimpl.hpp"
#include "openxraction.hpp" #include "openxraction.hpp"
#include "realisticcombat.hpp"
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
@ -139,14 +142,15 @@ namespace MWVR
channel->setEnabled(true); channel->setEnabled(true);
} }
// TODO: Configurable haptics: on/off + max intensity
void VRInputManager::applyHapticsLeftHand(float intensity) void VRInputManager::applyHapticsLeftHand(float intensity)
{ {
if (mHapticsEnabled)
mXRInput->applyHaptics(TrackedLimb::LEFT_HAND, intensity); mXRInput->applyHaptics(TrackedLimb::LEFT_HAND, intensity);
} }
void VRInputManager::applyHapticsRightHand(float intensity) void VRInputManager::applyHapticsRightHand(float intensity)
{ {
if (mHapticsEnabled)
mXRInput->applyHaptics(TrackedLimb::RIGHT_HAND, intensity); mXRInput->applyHaptics(TrackedLimb::RIGHT_HAND, intensity);
} }
@ -176,6 +180,7 @@ namespace MWVR
controllerBindingsFile, controllerBindingsFile,
grab) grab)
, mXRInput(nullptr) , mXRInput(nullptr)
, mHapticsEnabled{Settings::Manager::getBool("haptics enabled", "VR")}
{ {
std::vector<SuggestedBindings> suggestedBindings; std::vector<SuggestedBindings> suggestedBindings;
// Set up default bindings for the oculus // Set up default bindings for the oculus
@ -337,7 +342,7 @@ namespace MWVR
auto& player = world->getPlayer(); auto& player = world->getPlayer();
auto playerPtr = world->getPlayerPtr(); auto playerPtr = world->getPlayerPtr();
if (!mRealisticCombat || mRealisticCombat->ptr != playerPtr) if (!mRealisticCombat || mRealisticCombat->ptr() != playerPtr)
mRealisticCombat.reset(new RealisticCombat::StateMachine(playerPtr)); mRealisticCombat.reset(new RealisticCombat::StateMachine(playerPtr));
bool enabled = !guiMode && player.getDrawState() == MWMechanics::DrawState_Weapon && !player.isDisabled(); bool enabled = !guiMode && player.getDrawState() == MWMechanics::DrawState_Weapon && !player.isDisabled();
mRealisticCombat->update(dt, enabled); mRealisticCombat->update(dt, enabled);
@ -360,7 +365,7 @@ namespace MWVR
void VRInputManager::processAction(const Action* action, float dt, bool disableControls) void VRInputManager::processAction(const Action* action, float dt, bool disableControls)
{ {
static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input"); 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. // OpenMW does not currently provide any way to directly request skipping a video.
@ -425,7 +430,7 @@ namespace MWVR
mActionManager->screenshot(); mActionManager->screenshot();
break; break;
case A_Recenter: case A_Recenter:
xrGUIManager->updateTracking(); vrGuiManager->updateTracking();
requestRecenter(); requestRecenter();
break; break;
case A_MenuSelect: case A_MenuSelect:
@ -601,7 +606,7 @@ namespace MWVR
mActionManager->setAttemptJump(true); mActionManager->setAttemptJump(true);
break; break;
case A_Recenter: case A_Recenter:
xrGUIManager->updateTracking(); vrGuiManager->updateTracking();
if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
requestRecenter(); requestRecenter();
break; break;

View file

@ -1,8 +1,8 @@
#ifndef VR_INPUT_MANAGER_HPP #ifndef VR_INPUT_MANAGER_HPP
#define VR_INPUT_MANAGER_HPP #define VR_INPUT_MANAGER_HPP
#include "vrviewer.hpp" #include "vrtypes.hpp"
#include "realisticcombat.hpp"
#include "../mwinput/inputmanagerimp.hpp" #include "../mwinput/inputmanagerimp.hpp"
#include <vector> #include <vector>
@ -15,8 +15,11 @@ namespace MWVR
{ {
struct OpenXRInput; struct OpenXRInput;
/// As far as I can tell, SDL does not support VR controllers. namespace RealisticCombat {
/// So I subclass the input manager and insert VR controls. class StateMachine;
}
/// Extension of the input manager to include VR inputs
class VRInputManager : public MWInput::InputManager class VRInputManager : public MWInput::InputManager
{ {
public: public:
@ -72,6 +75,7 @@ namespace MWVR
osg::Vec3 mHeadOffset{ 0,0,0 }; osg::Vec3 mHeadOffset{ 0,0,0 };
bool mShouldRecenter{ true }; bool mShouldRecenter{ true };
bool mActivationIndication{ false }; bool mActivationIndication{ false };
bool mHapticsEnabled{ true };
float mYaw{ 0.f }; float mYaw{ 0.f };
float mVrAngles[3]{ 0.f,0.f,0.f }; float mVrAngles[3]{ 0.f,0.f,0.f };

View file

@ -8,6 +8,7 @@
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlgraphicswindow.hpp> #include <components/sdlutil/sdlgraphicswindow.hpp>
#include <components/misc/stringops.hpp>
#include <Windows.h> #include <Windows.h>
@ -37,7 +38,24 @@
namespace MWVR namespace MWVR
{ {
VRSession::VRSession() 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() VRSession::~VRSession()
@ -177,13 +195,10 @@ namespace MWVR
} }
// TODO: Invokation should depend on earliest render rather than necessarily phase. // TODO: Invokation could depend on earliest actual render rather than necessarily any specific phase.
// Specifically. Without shadows this is fine because nothing is being rendered // For example, shadows do some draw calls during cull an as such phase should be "Cull" or earlier with shadows enabled.
// during cull or earlier. // But may be "Draw" without shadows.
// Thought: Add an Shadowmapping phase and invoke it from the shadow code if (phase == mXrSyncPhase && getFrame(phase)->mShouldRender)
// 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)
doFrameSync(); doFrameSync();
} }

View file

@ -82,6 +82,7 @@ namespace MWVR
private: private:
std::mutex mMutex{}; std::mutex mMutex{};
std::condition_variable mCondition{}; std::condition_variable mCondition{};
FramePhase mXrSyncPhase{ FramePhase::Cull };
long long mFrames{ 0 }; long long mFrames{ 0 };
long long mLastRenderedFrame{ 0 }; long long mLastRenderedFrame{ 0 };

View file

@ -57,7 +57,8 @@ namespace MWVR {
void VRView::prerenderCallback(osg::RenderInfo& renderInfo) void VRView::prerenderCallback(osg::RenderInfo& renderInfo)
{ {
if (mSwapchain)
if (Environment::get().getManager()->xrSessionRunning())
{ {
mSwapchain->beginFrame(renderInfo.getState()->getGraphicsContext()); mSwapchain->beginFrame(renderInfo.getState()->getGraphicsContext());
} }

View file

@ -3,6 +3,8 @@
#include "openxrmanagerimpl.hpp" #include "openxrmanagerimpl.hpp"
#include "vrenvironment.hpp" #include "vrenvironment.hpp"
#include "vrsession.hpp" #include "vrsession.hpp"
#include "vrframebuffer.hpp"
#include "vrview.hpp"
#include "../mwrender/vismask.hpp" #include "../mwrender/vismask.hpp"
@ -14,16 +16,25 @@ namespace MWVR
"RightEye" "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( VRViewer::VRViewer(
osg::ref_ptr<osgViewer::Viewer> viewer) osg::ref_ptr<osgViewer::Viewer> viewer)
: mRealizeOperation(new RealizeOperation()) : mViewer(viewer)
, mViewer(viewer)
, mPreDraw(new PredrawCallback(this)) , mPreDraw(new PredrawCallback(this))
, mPostDraw(new PostdrawCallback(this)) , mPostDraw(new PostdrawCallback(this))
, mConfigured(false) , mConfigured(false)
{ {
mViewer->setRealizeOperation(mRealizeOperation); mViewer->setRealizeOperation(new RealizeOperation());
//this->setName("OpenXRRoot");
} }
VRViewer::~VRViewer(void) VRViewer::~VRViewer(void)
@ -51,7 +62,6 @@ namespace MWVR
mainCamera->setInitialDrawCallback(new VRView::InitialDrawCallback()); mainCamera->setInitialDrawCallback(new VRView::InitialDrawCallback());
auto* xr = Environment::get().getManager(); auto* xr = Environment::get().getManager();
if (!xr->realized())
xr->realize(context); xr->realize(context);
// Run through initial events to start session // Run through initial events to start session
@ -70,6 +80,9 @@ namespace MWVR
// Configure eyes, their cameras, and their enslavement. // Configure eyes, their cameras, and their enslavement.
osg::Vec4 clearColor = mainCamera->getClearColor(); osg::Vec4 clearColor = mainCamera->getClearColor();
auto config = xr->getRecommendedSwapchainConfig(); 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++) for (unsigned i = 0; i < sViewNames.size(); i++)
{ {
@ -86,14 +99,15 @@ namespace MWVR
mViewer->addSlave(camera, true); mViewer->addSlave(camera, true);
mViewer->getSlave(i)._updateSlaveCallback = new VRView::UpdateSlaveCallback(view, context); mViewer->getSlave(i)._updateSlaveCallback = new VRView::UpdateSlaveCallback(view, context);
mMsaaResolveMirrorTexture[i].reset(new VRTexture(context->getState(), if (mirror)
mMsaaResolveMirrorTexture[i].reset(new VRFramebuffer(context->getState(),
view->swapchain().width(), view->swapchain().width(),
view->swapchain().height(), view->swapchain().height(),
0)); 0));
} }
if (mirror)
mMirrorTexture.reset(new VRTexture(context->getState(), mainCamera->getViewport()->width(), mainCamera->getViewport()->height(), 0)); mMirrorTexture.reset(new VRFramebuffer(context->getState(), mainCamera->getViewport()->width(), mainCamera->getViewport()->height(), 0));
mViewer->setReleaseContextAtEndOfFrameHint(false); mViewer->setReleaseContextAtEndOfFrameHint(false);
mMainCameraGC = mainCamera->getGraphicsContext(); mMainCameraGC = mainCamera->getGraphicsContext();
@ -124,6 +138,9 @@ namespace MWVR
void VRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc) void VRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc)
{ {
if (!mMirrorTexture)
return;
auto* state = gc->getState(); auto* state = gc->getState();
auto* gl = osg::GLExtensions::Get(state->getContextID(), false); auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
@ -131,12 +148,14 @@ namespace MWVR
int mirrorWidth = screenWidth / 2; int mirrorWidth = screenWidth / 2;
int screenHeight = mCameras["MainCamera"]->getViewport()->height(); 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++) for (unsigned i = 0; i < sViewNames.size(); i++)
{ {
auto& resolveTexture = *mMsaaResolveMirrorTexture[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()); 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. // Mirror the index when rendering to the mirror texture to allow cross eye mirror textures.
unsigned mirrorIndex = sViewNames.size() - 1 - i; unsigned mirrorIndex = sViewNames.size() - 1 - i;
resolveTexture.blit(gc, mirrorIndex * mirrorWidth, 0, (mirrorIndex + 1) * mirrorWidth, screenHeight); resolveTexture.blit(gc, mirrorIndex * mirrorWidth, 0, (mirrorIndex + 1) * mirrorWidth, screenHeight);
@ -155,16 +174,14 @@ namespace MWVR
} }
void void
VRViewer::RealizeOperation::operator()( RealizeOperation::operator()(
osg::GraphicsContext* gc) osg::GraphicsContext* gc)
{ {
OpenXRManager::RealizeOperation::operator()(gc); return Environment::get().getViewer()->realize(gc);
Environment::get().getViewer()->realize(gc);
} }
bool bool
VRViewer::RealizeOperation::realized() RealizeOperation::realized()
{ {
return Environment::get().getViewer()->realized(); return Environment::get().getViewer()->realized();
} }

View file

@ -4,16 +4,20 @@
#include <memory> #include <memory>
#include <array> #include <array>
#include <map> #include <map>
#include <osg/Group> #include <osg/Group>
#include <osg/Camera> #include <osg/Camera>
#include <osgViewer/Viewer> #include <osgViewer/Viewer>
#include "openxrmanager.hpp" #include "openxrmanager.hpp"
#include "vrgui.hpp"
#include <components/sceneutil/positionattitudetransform.hpp> #include <components/sceneutil/positionattitudetransform.hpp>
namespace MWVR namespace MWVR
{ {
class VRFramebuffer;
class VRView;
/// \brief Manages stereo rendering and mirror texturing. /// \brief Manages stereo rendering and mirror texturing.
/// ///
/// Manipulates the osgViewer by disabling main camera rendering, and instead rendering to /// Manipulates the osgViewer by disabling main camera rendering, and instead rendering to
@ -21,16 +25,6 @@ namespace MWVR
class VRViewer class VRViewer
{ {
public: public:
class RealizeOperation : public OpenXRManager::RealizeOperation
{
public:
RealizeOperation() {};
void operator()(osg::GraphicsContext* gc) override;
bool realized() override;
private:
};
class SwapBuffersCallback : public osg::GraphicsContext::SwapCallback class SwapBuffersCallback : public osg::GraphicsContext::SwapCallback
{ {
public: public:
@ -90,15 +84,14 @@ namespace MWVR
void disableMainCamera(void); void disableMainCamera(void);
private: private:
osg::ref_ptr<OpenXRManager::RealizeOperation> mRealizeOperation = nullptr;
osg::ref_ptr<osgViewer::Viewer> mViewer = nullptr; osg::ref_ptr<osgViewer::Viewer> mViewer = nullptr;
std::map<std::string, osg::ref_ptr<VRView> > mViews{}; std::map<std::string, osg::ref_ptr<VRView> > mViews{};
std::map<std::string, osg::ref_ptr<osg::Camera> > mCameras{}; std::map<std::string, osg::ref_ptr<osg::Camera> > mCameras{};
osg::ref_ptr<PredrawCallback> mPreDraw{ nullptr }; osg::ref_ptr<PredrawCallback> mPreDraw{ nullptr };
osg::ref_ptr<PostdrawCallback> mPostDraw{ nullptr }; osg::ref_ptr<PostdrawCallback> mPostDraw{ nullptr };
osg::GraphicsContext* mMainCameraGC{ nullptr }; osg::GraphicsContext* mMainCameraGC{ nullptr };
std::unique_ptr<VRTexture> mMsaaResolveMirrorTexture[2]{ }; std::unique_ptr<VRFramebuffer> mMsaaResolveMirrorTexture[2]{ };
std::unique_ptr<VRTexture> mMirrorTexture{ nullptr }; std::unique_ptr<VRFramebuffer> mMirrorTexture{ nullptr };
std::mutex mMutex; std::mutex mMutex;