1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-02-04 16:45:32 +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/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

View file

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

View file

@ -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.
}
}

View file

@ -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()

View file

@ -2,7 +2,6 @@
#include "openxrswapchain.hpp"
#include "openxrswapchainimpl.hpp"
#include "vrtexture.hpp"
#include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlgraphicswindow.hpp>
@ -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:

View file

@ -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();
}

View file

@ -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<OpenXRSwapchainImpl> mPrivate;
};

View file

@ -1,5 +1,6 @@
#include "openxrswapchainimpl.hpp"
#include "vrenvironment.hpp"
#include "vrframebuffer.hpp"
#include <components/debug/debuglog.hpp>
@ -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<XrSwapchainImageBaseHeader*>(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()");
}
}

View file

@ -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<XrSwapchainImageOpenGLKHR> mSwapchainImageBuffers{};
@ -42,7 +49,7 @@ namespace MWVR
int64_t mSwapchainColorFormat = -1;
int64_t mSwapchainDepthFormat = -1;
uint32_t mFBO = 0;
std::vector<std::unique_ptr<VRTexture> > mRenderBuffers{};
std::vector<std::unique_ptr<VRFramebuffer> > mRenderBuffers{};
int mRenderBuffer{ 0 };
uint32_t mAcquiredImageIndex{ 0 };
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 "../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();
}

View file

@ -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 };
};
}

View file

@ -459,11 +459,10 @@ namespace MWVR
MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().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);
}

View file

@ -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:

View file

@ -1,4 +1,4 @@
#include "vrtexture.hpp"
#include "vrframebuffer.hpp"
#include <osg/Texture2D>
@ -11,7 +11,7 @@
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)
, 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);
}
}

View file

@ -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<osg::State> 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<osg::State> 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;
};

View file

@ -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;
}
}

View file

@ -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<osg::Texture2D> 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<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
{
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<osgViewer::Viewer> mOsgViewer{ nullptr };

View file

@ -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 <components/debug/debuglog.hpp>
@ -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> 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;

View file

@ -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 <vector>
@ -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 };

View file

@ -8,6 +8,7 @@
#include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlgraphicswindow.hpp>
#include <components/misc/stringops.hpp>
#include <Windows.h>
@ -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();
}

View file

@ -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 };

View file

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

View file

@ -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<osgViewer::Viewer> 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();
}

View file

@ -4,16 +4,20 @@
#include <memory>
#include <array>
#include <map>
#include <osg/Group>
#include <osg/Camera>
#include <osgViewer/Viewer>
#include "openxrmanager.hpp"
#include "vrgui.hpp"
#include <components/sceneutil/positionattitudetransform.hpp>
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<OpenXRManager::RealizeOperation> mRealizeOperation = nullptr;
osg::ref_ptr<osgViewer::Viewer> mViewer = nullptr;
std::map<std::string, osg::ref_ptr<VRView> > mViews{};
std::map<std::string, osg::ref_ptr<osg::Camera> > mCameras{};
osg::ref_ptr<PredrawCallback> mPreDraw{ nullptr };
osg::ref_ptr<PostdrawCallback> mPostDraw{ nullptr };
osg::GraphicsContext* mMainCameraGC{ nullptr };
std::unique_ptr<VRTexture> mMsaaResolveMirrorTexture[2]{ };
std::unique_ptr<VRTexture> mMirrorTexture{ nullptr };
std::unique_ptr<VRFramebuffer> mMsaaResolveMirrorTexture[2]{ };
std::unique_ptr<VRFramebuffer> mMirrorTexture{ nullptr };
std::mutex mMutex;