1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-10-05 01:56:30 +00:00

Fixed recentering adjusting the player's orientation. Cell changes should now retain the proper orientation. Added support for seated play.

This commit is contained in:
madsbuvi 2021-02-28 16:01:03 +01:00
parent 7986def3db
commit 3347cdfe30
17 changed files with 186 additions and 98 deletions

View file

@ -158,10 +158,10 @@ namespace MWPhysics
// But 2. is not so obvious. I guess it's doable if i compute the direction between current position and the player's // But 2. is not so obvious. I guess it's doable if i compute the direction between current position and the player's
// position in the VR stage, and just let it catch up at the character's own move speed, but it still needs to reach the position as exactly as possible. // position in the VR stage, and just let it catch up at the character's own move speed, but it still needs to reach the position as exactly as possible.
if (isPlayer) auto* session = MWVR::Environment::get().getSession();
if (session)
{ {
auto* session = MWVR::Environment::get().getSession(); if (isPlayer)
if (session)
{ {
float pitch = 0.f; float pitch = 0.f;
float yaw = 0.f; float yaw = 0.f;
@ -238,81 +238,90 @@ namespace MWPhysics
#ifdef USE_OPENXR #ifdef USE_OPENXR
// Catch the player character up to the real world position of the player. // Catch the player character up to the real world position of the player.
// But only if play is not seated.
// TODO: Hack. // TODO: Hack.
if (isPlayer && !world->getPlayer().isDisabled()) if (isPlayer)
{ {
bool shouldMove = true;
if (session && session->seatedPlay())
shouldMove = false;
if (world->getPlayer().isDisabled())
shouldMove = false;
auto* inputManager = reinterpret_cast<MWVR::VRCamera*>(MWBase::Environment::get().getWorld()->getRenderingManager().getCamera()); if (shouldMove)
osg::Vec3 headOffset = inputManager->headOffset();
osg::Vec3 trackingOffset = headOffset;
// Player's tracking height should not affect character position
trackingOffset.z() = 0;
float remainingTime = time;
bool seenGround = physicActor->getOnGround() && !physicActor->getOnSlope() && !actor.mFlying;
float remainder = 1.f;
for (int iterations = 0; iterations < sMaxIterations && remainingTime > 0.01f && remainder > 0.01; ++iterations)
{ {
osg::Vec3 toMove = trackingOffset * remainder; auto* inputManager = reinterpret_cast<MWVR::VRCamera*>(MWBase::Environment::get().getWorld()->getRenderingManager().getCamera());
osg::Vec3 nextpos = newPosition + toMove;
if ((newPosition - nextpos).length2() > 0.0001) osg::Vec3 headOffset = inputManager->headOffset();
osg::Vec3 trackingOffset = headOffset;
// Player's tracking height should not affect character position
trackingOffset.z() = 0;
float remainingTime = time;
bool seenGround = physicActor->getOnGround() && !physicActor->getOnSlope() && !actor.mFlying;
float remainder = 1.f;
for (int iterations = 0; iterations < sMaxIterations && remainingTime > 0.01f && remainder > 0.01; ++iterations)
{ {
// trace to where character would go if there were no obstructions osg::Vec3 toMove = trackingOffset * remainder;
tracer.doTrace(colobj, newPosition, nextpos, collisionWorld); osg::Vec3 nextpos = newPosition + toMove;
// check for obstructions if ((newPosition - nextpos).length2() > 0.0001)
if (tracer.mFraction >= 1.0f)
{ {
newPosition = tracer.mEndPos; // ok to move, so set newPosition // trace to where character would go if there were no obstructions
tracer.doTrace(colobj, newPosition, nextpos, collisionWorld);
// check for obstructions
if (tracer.mFraction >= 1.0f)
{
newPosition = tracer.mEndPos; // ok to move, so set newPosition
remainder = 0.f;
break;
}
}
else
{
// The current position and next position are nearly the same, so just exit.
// Note: Bullet can trigger an assert in debug modes if the positions
// are the same, since that causes it to attempt to normalize a zero
// length vector (which can also happen with nearly identical vectors, since
// precision can be lost due to any math Bullet does internally). Since we
// aren't performing any collision detection, we want to reject the next
// position, so that we don't slowly move inside another object.
remainder = 0.f; remainder = 0.f;
break; break;
} }
}
else if (isWalkableSlope(tracer.mPlaneNormal) && !actor.mFlying && newPosition.z() >= swimlevel)
{ seenGround = true;
// The current position and next position are nearly the same, so just exit.
// Note: Bullet can trigger an assert in debug modes if the positions // We are touching something.
// are the same, since that causes it to attempt to normalize a zero if (tracer.mFraction < 1E-9f)
// length vector (which can also happen with nearly identical vectors, since {
// precision can be lost due to any math Bullet does internally). Since we // Try to separate by backing off slighly to unstuck the solver
// aren't performing any collision detection, we want to reject the next osg::Vec3f backOff = (newPosition - tracer.mHitPoint) * 1E-2f;
// position, so that we don't slowly move inside another object. newPosition += backOff;
remainder = 0.f; }
break;
// We hit something. Check if we can step up.
float hitHeight = tracer.mHitPoint.z() - tracer.mEndPos.z() + halfExtents.z();
osg::Vec3f oldPosition = newPosition;
bool result = false;
if (hitHeight < sStepSizeUp && !isActor(tracer.mHitObject))
{
// Try to step up onto it.
// NOTE: stepMove does not allow stepping over, modifies newPosition if successful
result = stepper.step(newPosition, toMove, remainingTime, seenGround, iterations == 0);
remainder = remainingTime / time;
}
} }
if (isWalkableSlope(tracer.mPlaneNormal) && !actor.mFlying && newPosition.z() >= swimlevel) // Try not to lose any tracking
seenGround = true; osg::Vec3 moved = newPosition - actor.mPosition;
headOffset.x() -= moved.x();
// We are touching something. headOffset.y() -= moved.y();
if (tracer.mFraction < 1E-9f) inputManager->setHeadOffset(headOffset);
{
// Try to separate by backing off slighly to unstuck the solver
osg::Vec3f backOff = (newPosition - tracer.mHitPoint) * 1E-2f;
newPosition += backOff;
}
// We hit something. Check if we can step up.
float hitHeight = tracer.mHitPoint.z() - tracer.mEndPos.z() + halfExtents.z();
osg::Vec3f oldPosition = newPosition;
bool result = false;
if (hitHeight < sStepSizeUp && !isActor(tracer.mHitObject))
{
// Try to step up onto it.
// NOTE: stepMove does not allow stepping over, modifies newPosition if successful
result = stepper.step(newPosition, toMove, remainingTime, seenGround, iterations == 0);
remainder = remainingTime / time;
}
} }
// Try not to lose any tracking
osg::Vec3 moved = newPosition - actor.mPosition;
headOffset.x() -= moved.x();
headOffset.y() -= moved.y();
inputManager->setHeadOffset(headOffset);
} }
#endif #endif

View file

@ -90,8 +90,13 @@ namespace MWVR
CHECK_XRCMD(xrCreateReferenceSpace(mSession, &createInfo, &mReferenceSpaceView)); CHECK_XRCMD(xrCreateReferenceSpace(mSession, &createInfo, &mReferenceSpaceView));
createInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE; createInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE;
CHECK_XRCMD(xrCreateReferenceSpace(mSession, &createInfo, &mReferenceSpaceStage)); CHECK_XRCMD(xrCreateReferenceSpace(mSession, &createInfo, &mReferenceSpaceStage));
createInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
CHECK_XRCMD(xrCreateReferenceSpace(mSession, &createInfo, &mReferenceSpaceLocal));
} }
// Default to using the stage
mReferenceSpace = mReferenceSpaceStage;
{ // Read and log graphics properties for the swapchain { // Read and log graphics properties for the swapchain
xrGetSystemProperties(mInstance, mSystemId, &mSystemProperties); xrGetSystemProperties(mInstance, mSystemId, &mSystemProperties);
@ -302,7 +307,7 @@ namespace MWVR
compositionLayerProjectionViews[(int)Side::LEFT_SIDE] = toXR((*layerStack)[(int)Side::LEFT_SIDE]); compositionLayerProjectionViews[(int)Side::LEFT_SIDE] = toXR((*layerStack)[(int)Side::LEFT_SIDE]);
compositionLayerProjectionViews[(int)Side::RIGHT_SIDE] = toXR((*layerStack)[(int)Side::RIGHT_SIDE]); compositionLayerProjectionViews[(int)Side::RIGHT_SIDE] = toXR((*layerStack)[(int)Side::RIGHT_SIDE]);
layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION; layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION;
layer.space = getReferenceSpace(ReferenceSpace::STAGE); layer.space = getReferenceSpace();
layer.viewCount = 2; layer.viewCount = 2;
layer.views = compositionLayerProjectionViews.data(); layer.views = compositionLayerProjectionViews.data();
auto* xrLayerStack = reinterpret_cast<XrCompositionLayerBaseHeader*>(&layer); auto* xrLayerStack = reinterpret_cast<XrCompositionLayerBaseHeader*>(&layer);
@ -591,14 +596,9 @@ namespace MWVR
return XrFovf{ fov.angleLeft, fov.angleRight, fov.angleUp, fov.angleDown }; return XrFovf{ fov.angleLeft, fov.angleRight, fov.angleUp, fov.angleDown };
} }
XrSpace OpenXRManagerImpl::getReferenceSpace(ReferenceSpace space) XrSpace OpenXRManagerImpl::getReferenceSpace()
{ {
XrSpace referenceSpace = XR_NULL_HANDLE; return mReferenceSpace;
if (space == ReferenceSpace::STAGE)
referenceSpace = mReferenceSpaceStage;
if (space == ReferenceSpace::VIEW)
referenceSpace = mReferenceSpaceView;
return referenceSpace;
} }
bool OpenXRManagerImpl::xrExtensionIsEnabled(const char* extensionName) const bool OpenXRManagerImpl::xrExtensionIsEnabled(const char* extensionName) const

View file

@ -56,7 +56,7 @@ namespace MWVR
long long getLastPredictedDisplayTime(); long long getLastPredictedDisplayTime();
long long getLastPredictedDisplayPeriod(); long long getLastPredictedDisplayPeriod();
std::array<SwapchainConfig, 2> getRecommendedSwapchainConfig() const; std::array<SwapchainConfig, 2> getRecommendedSwapchainConfig() const;
XrSpace getReferenceSpace(ReferenceSpace space); XrSpace getReferenceSpace();
XrSession xrSession() const { return mSession; }; XrSession xrSession() const { return mSession; };
XrInstance xrInstance() const { return mInstance; }; XrInstance xrInstance() const { return mInstance; };
bool xrExtensionIsEnabled(const char* extensionName) const; bool xrExtensionIsEnabled(const char* extensionName) const;
@ -69,6 +69,7 @@ namespace MWVR
void eraseFormat(int64_t format); void eraseFormat(int64_t format);
OpenXRPlatform& platform() { return mPlatform; }; OpenXRPlatform& platform() { return mPlatform; };
protected: protected:
void setupExtensionsAndLayers(); void setupExtensionsAndLayers();
void setupDebugMessenger(void); void setupDebugMessenger(void);
@ -97,6 +98,8 @@ namespace MWVR
std::array<XrViewConfigurationView, 2> mConfigViews{ { {XR_TYPE_VIEW_CONFIGURATION_VIEW}, {XR_TYPE_VIEW_CONFIGURATION_VIEW} } }; std::array<XrViewConfigurationView, 2> mConfigViews{ { {XR_TYPE_VIEW_CONFIGURATION_VIEW}, {XR_TYPE_VIEW_CONFIGURATION_VIEW} } };
XrSpace mReferenceSpaceView = XR_NULL_HANDLE; XrSpace mReferenceSpaceView = XR_NULL_HANDLE;
XrSpace mReferenceSpaceStage = XR_NULL_HANDLE; XrSpace mReferenceSpaceStage = XR_NULL_HANDLE;
XrSpace mReferenceSpaceLocal = XR_NULL_HANDLE;
XrSpace mReferenceSpace = XR_NULL_HANDLE;
XrFrameState mFrameState{}; XrFrameState mFrameState{};
XrSessionState mSessionState = XR_SESSION_STATE_UNKNOWN; XrSessionState mSessionState = XR_SESSION_STATE_UNKNOWN;
XrDebugUtilsMessengerEXT mDebugMessenger{ nullptr }; XrDebugUtilsMessengerEXT mDebugMessenger{ nullptr };

View file

@ -469,6 +469,7 @@ namespace MWVR
float realHeight = Settings::Manager::getFloat("real height", "VR"); float realHeight = Settings::Manager::getFloat("real height", "VR");
float sizeFactor = charHeight / realHeight; float sizeFactor = charHeight / realHeight;
Environment::get().getSession()->setPlayerScale(sizeFactor); Environment::get().getSession()->setPlayerScale(sizeFactor);
Environment::get().getSession()->setEyeLevel(charHeightBase - 0.15f); // approximation
} }
void VRAnimation::setFingerPointingMode(bool enabled) void VRAnimation::setFingerPointingMode(bool enabled)

View file

@ -12,6 +12,9 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/class.hpp"
#include "../mwmechanics/movement.hpp"
#include <osg/Quat> #include <osg/Quat>
@ -45,16 +48,35 @@ namespace MWVR
// Move position of head to center of character // Move position of head to center of character
// Z should not be affected // Z should not be affected
mHeadOffset = osg::Vec3(0, 0, 0); mHeadOffset.x() = 0;
mHeadOffset.z() = mHeadPose.position.z(); mHeadOffset.y() = 0;
// Adjust orientation to zero yaw auto* session = Environment::get().getSession();
float yaw = 0.f; if (session->seatedPlay() && mShouldResetZ)
float pitch = 0.f; {
float roll = 0.f; // Adjust offset to place the current pose roughly at eye level
getEulerAngles(mHeadPose.orientation, yaw, pitch, roll); mHeadOffset.z() = session->eyeLevel() * Constants::UnitsPerMeter;
mYawOffset = -yaw; }
else
{
mHeadOffset.z() = mHeadPose.position.z();
}
mShouldResetZ = false;
// When the cell changes, the game rotates the character appropriately.
// To respect this, reset yaw offset to make our yaw match the character.
MWBase::World* world = MWBase::Environment::get().getWorld();
if (world)
{
auto& player = world->getPlayer();
auto playerPtr = player.getPlayer();
const auto& data = playerPtr.getRefData();
float yaw = 0.f;
float pitch = 0.f;
float roll = 0.f;
getEulerAngles(mHeadPose.orientation, yaw, pitch, roll);
mYawOffset = data.getPosition().rot[2] - yaw;
}
mShouldRecenter = false; mShouldRecenter = false;
Log(Debug::Verbose) << "Recentered"; Log(Debug::Verbose) << "Recentered";
} }
@ -194,4 +216,12 @@ namespace MWVR
{ {
return osg::Quat(mYawOffset, osg::Vec3(0, 0, -1)); return osg::Quat(mYawOffset, osg::Vec3(0, 0, -1));
} }
void VRCamera::requestRecenter(bool resetZ)
{
mShouldRecenter = true;
// Use OR so we don't a pending reset of Z.
mShouldResetZ |= resetZ;
}
} }

View file

@ -57,7 +57,7 @@ namespace MWVR
void rotateStage(float yaw) { mYawOffset += yaw; } void rotateStage(float yaw) { mYawOffset += yaw; }
void requestRecenter() { mShouldRecenter = true; } void requestRecenter(bool resetZ);
const osg::Vec3& headOffset() const { return mHeadOffset; } const osg::Vec3& headOffset() const { return mHeadOffset; }
@ -75,6 +75,7 @@ namespace MWVR
Pose mHeadPose{}; Pose mHeadPose{};
osg::Vec3 mHeadOffset{ 0,0,0 }; osg::Vec3 mHeadOffset{ 0,0,0 };
bool mShouldRecenter{ true }; bool mShouldRecenter{ true };
bool mShouldResetZ{ true };
bool mHasTrackingData{ false }; bool mHasTrackingData{ false };
float mYawOffset{ 0.f }; float mYawOffset{ 0.f };
bool mShouldTrackPlayerCharacter{ false }; bool mShouldTrackPlayerCharacter{ false };

View file

@ -42,7 +42,7 @@ namespace MWVR
mPrevious = mValue; mPrevious = mValue;
auto* xr = Environment::get().getManager(); auto* xr = Environment::get().getManager();
XrSpace referenceSpace = xr->impl().getReferenceSpace(ReferenceSpace::STAGE); XrSpace referenceSpace = xr->impl().getReferenceSpace();
XrSpaceLocation location{ XR_TYPE_SPACE_LOCATION }; XrSpaceLocation location{ XR_TYPE_SPACE_LOCATION };
XrSpaceVelocity velocity{ XR_TYPE_SPACE_VELOCITY }; XrSpaceVelocity velocity{ XR_TYPE_SPACE_VELOCITY };

View file

@ -280,10 +280,10 @@ namespace MWVR
mAxisDeadzone->setDeadzoneRadius(deadzoneRadius); mAxisDeadzone->setDeadzoneRadius(deadzoneRadius);
} }
void VRInputManager::requestRecenter() void VRInputManager::requestRecenter(bool resetZ)
{ {
// TODO: Hack, should have a cleaner way of accessing this // TODO: Hack, should have a cleaner way of accessing this
reinterpret_cast<VRCamera*>(MWBase::Environment::get().getWorld()->getRenderingManager().getCamera())->requestRecenter(); reinterpret_cast<VRCamera*>(MWBase::Environment::get().getWorld()->getRenderingManager().getCamera())->requestRecenter(resetZ);
} }
VRInputManager::VRInputManager( VRInputManager::VRInputManager(
@ -723,7 +723,7 @@ namespace MWVR
case A_Recenter: case A_Recenter:
vrGuiManager->updateTracking(); vrGuiManager->updateTracking();
if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
requestRecenter(); requestRecenter(true);
break; break;
case MWInput::A_Use: case MWInput::A_Use:
if (mActivationIndication || MWBase::Environment::get().getWindowManager()->isGuiMode()) if (mActivationIndication || MWBase::Environment::get().getWindowManager()->isGuiMode())

View file

@ -46,7 +46,7 @@ namespace MWVR
void update(float dt, bool disableControls = false, bool disableEvents = false) override; void update(float dt, bool disableControls = false, bool disableEvents = false) override;
/// Set current offset to 0 and re-align VR stage. /// Set current offset to 0 and re-align VR stage.
void requestRecenter(); void requestRecenter(bool resetZ);
/// Tracking pose of the given limb at the given predicted time /// Tracking pose of the given limb at the given predicted time
Pose getLimbPose(int64_t time, TrackedLimb limb); Pose getLimbPose(int64_t time, TrackedLimb limb);

View file

@ -6,7 +6,9 @@
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/statemanager.hpp" #include "../mwbase/statemanager.hpp"
#include "vrmetamenu.hpp"
#include "vrenvironment.hpp"
#include "vrinputmanager.hpp"
namespace MWVR namespace MWVR
{ {
@ -94,6 +96,11 @@ namespace MWVR
MWBase::Environment::get().getStateManager()->quickSave(); MWBase::Environment::get().getStateManager()->quickSave();
} }
void VrMetaMenu::onRecenter()
{
Environment::get().getInputManager()->requestRecenter(true);
}
void VrMetaMenu::close() void VrMetaMenu::close()
{ {
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_VrMetaMenu); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_VrMetaMenu);
@ -118,7 +125,9 @@ namespace MWVR
else if (name == "quickload") else if (name == "quickload")
onQuickLoad(); onQuickLoad();
else if (name == "quicksave") else if (name == "quicksave")
onQuickSave(); onQuickSave();
else if (name == "recenter")
onRecenter();
} }
bool VrMetaMenu::exit() bool VrMetaMenu::exit()
@ -128,7 +137,7 @@ namespace MWVR
void VrMetaMenu::updateMenu() void VrMetaMenu::updateMenu()
{ {
static std::vector<std::string> buttons{ "return", "quicksave", "quickload", "console", "inventory", "journal", "rest", "quickmenu", "gamemenu" }; static std::vector<std::string> buttons{ "return", "recenter", "quicksave", "quickload", "console", "inventory", "journal", "rest", "quickmenu", "gamemenu" };
if(mButtons.empty()) if(mButtons.empty())
for (std::string& buttonId : buttons) for (std::string& buttonId : buttons)

View file

@ -50,6 +50,7 @@ namespace MWVR
void onQuickMenu(); void onQuickMenu();
void onQuickLoad(); void onQuickLoad();
void onQuickSave(); void onQuickSave();
void onRecenter();
void close(); void close();
void updateMenu(); void updateMenu();

View file

@ -52,6 +52,7 @@ namespace MWVR
VRSession::VRSession() VRSession::VRSession()
{ {
mHandDirectedMovement = Settings::Manager::getBool("hand directed movement", "VR"); mHandDirectedMovement = Settings::Manager::getBool("hand directed movement", "VR");
mSeatedPlay = Settings::Manager::getBool("seated play", "VR");
} }
VRSession::~VRSession() VRSession::~VRSession()
@ -71,10 +72,8 @@ namespace MWVR
{ {
for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it) for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it)
{ {
if (it->first == "VR" && it->second == "hand directed movement") mHandDirectedMovement = Settings::Manager::getBool("hand directed movement", "VR");
{ setSeatedPlay(Settings::Manager::getBool("seated play", "VR"));
mHandDirectedMovement = Settings::Manager::getBool("hand directed movement", "VR");
}
} }
} }
@ -98,6 +97,15 @@ namespace MWVR
} }
} }
void VRSession::setSeatedPlay(bool seatedPlay)
{
std::swap(mSeatedPlay, seatedPlay);
if (mSeatedPlay != seatedPlay)
{
Environment::get().getInputManager()->requestRecenter(true);
}
}
osg::Matrix VRSession::viewMatrix(osg::Vec3 position, osg::Quat orientation) osg::Matrix VRSession::viewMatrix(osg::Vec3 position, osg::Quat orientation)
{ {
position = position * Constants::UnitsPerMeter; position = position * Constants::UnitsPerMeter;

View file

@ -66,9 +66,13 @@ namespace MWVR
void beginPhase(FramePhase phase); void beginPhase(FramePhase phase);
std::unique_ptr<VRFrameMeta>& getFrame(FramePhase phase); std::unique_ptr<VRFrameMeta>& getFrame(FramePhase phase);
bool seatedPlay() const { return mSeatedPlay; }
float playerScale() const { return mPlayerScale; } float playerScale() const { return mPlayerScale; }
float setPlayerScale(float scale) { return mPlayerScale = scale; } void setPlayerScale(float scale) { mPlayerScale = scale; }
float eyeLevel() const { return mEyeLevel; }
void setEyeLevel(float eyeLevel) { mEyeLevel = eyeLevel; }
osg::Matrix viewMatrix(osg::Vec3 position, osg::Quat orientation); osg::Matrix viewMatrix(osg::Vec3 position, osg::Quat orientation);
osg::Matrix viewMatrix(FramePhase phase, Side side, bool offset, bool glConvention); osg::Matrix viewMatrix(FramePhase phase, Side side, bool offset, bool glConvention);
@ -81,11 +85,15 @@ namespace MWVR
void beginFrame(); void beginFrame();
void endFrame(); void endFrame();
protected:
void setSeatedPlay(bool seatedPlay);
private: private:
std::mutex mMutex{}; std::mutex mMutex{};
std::condition_variable mCondition{}; std::condition_variable mCondition{};
bool mHandDirectedMovement{ false }; bool mHandDirectedMovement{ false };
bool mSeatedPlay{ false };
long long mFrames{ 0 }; long long mFrames{ 0 };
long long mLastRenderedFrame{ 0 }; long long mLastRenderedFrame{ 0 };
long long mLastPredictedDisplayTime{ 0 }; long long mLastPredictedDisplayTime{ 0 };
@ -95,6 +103,7 @@ namespace MWVR
std::chrono::steady_clock::time_point mLastRenderedFrameTimestamp{ std::chrono::steady_clock::now() }; std::chrono::steady_clock::time_point mLastRenderedFrameTimestamp{ std::chrono::steady_clock::now() };
float mPlayerScale{ 1.f }; float mPlayerScale{ 1.f };
float mEyeLevel{ 1.f };
}; };
} }

View file

@ -964,7 +964,7 @@ namespace MWWorld
#ifdef USE_OPENXR #ifdef USE_OPENXR
auto* xrInput = MWVR::Environment::get().getInputManager(); auto* xrInput = MWVR::Environment::get().getInputManager();
if (xrInput) if (xrInput)
xrInput->requestRecenter(); xrInput->requestRecenter(false);
#endif #endif
} }
@ -987,7 +987,7 @@ namespace MWWorld
#ifdef USE_OPENXR #ifdef USE_OPENXR
auto* xrInput = MWVR::Environment::get().getInputManager(); auto* xrInput = MWVR::Environment::get().getInputManager();
if (xrInput) if (xrInput)
xrInput->requestRecenter(); xrInput->requestRecenter(false);
#endif #endif
} }

View file

@ -595,7 +595,17 @@
<Property key="Caption" value="Hand directed movement"/> <Property key="Caption" value="Hand directed movement"/>
</Widget> </Widget>
</Widget> </Widget>
<Widget type="HBox" skin="" position="4 154 350 24"> <Widget type="HBox" skin="" position="4 154 260 24">
<Widget type="AutoSizedButton" skin="MW_Button" position="0 0 24 24" align="Left Top">
<UserString key="SettingCategory" value="VR"/>
<UserString key="SettingName" value="seated play"/>
<UserString key="SettingType" value="CheckButton"/>
</Widget>
<Widget type="AutoSizedTextBox" skin="SandText" position="28 4 71 16" align="Left Top">
<Property key="Caption" value="Seated play"/>
</Widget>
</Widget>
<Widget type="HBox" skin="" position="4 184 350 24">
<Widget type="ComboBox" skin="MW_ComboBox" position="0 0 85 24" align="Left Top" name="VRLeftHudPosition"> <Widget type="ComboBox" skin="MW_ComboBox" position="0 0 85 24" align="Left Top" name="VRLeftHudPosition">
<Property key="AddItem" value="Wrist"/> <Property key="AddItem" value="Wrist"/>
<Property key="AddItem" value="Top"/> <Property key="AddItem" value="Top"/>

View file

@ -12,6 +12,11 @@
<Property key="FontHeight" value="32"/> <Property key="FontHeight" value="32"/>
</Widget> </Widget>
<Widget type="AutoSizedButton" skin="MW_Button" name="recenter" align="HCenter">
<Property key="Caption" value="Recenter"/>
<Property key="FontHeight" value="32"/>
</Widget>
<Widget type="Spacer"/> <Widget type="Spacer"/>
<Widget type="Widget" skin="IB_T" align="HCenter" position="0 0 125 4"> <Widget type="Widget" skin="IB_T" align="HCenter" position="0 0 125 4">
<Widget type="Widget"/> <Widget type="Widget"/>

View file

@ -1023,6 +1023,8 @@ left hand hud position = wrist
# As the general quality of OpenXR DirectX runtimes is better than OpenGL runtimes, i default this to true. # As the general quality of OpenXR DirectX runtimes is better than OpenGL runtimes, i default this to true.
Prefer DirectX swapchains = true Prefer DirectX swapchains = true
seated play = false
[VR Debug] [VR Debug]
# If true, OpenMW-VR will enable gamma postprocessing # If true, OpenMW-VR will enable gamma postprocessing
gamma postprocessing = true gamma postprocessing = true