Some bugfixes, more experimenting with timing of frame sync calls.

pull/615/head
Mads Buvik Sandvei 5 years ago
parent f25c3af9cb
commit 9bd676f5be

@ -564,6 +564,12 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
// Create sound system
mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound));
#ifdef USE_OPENXR
mXrEnvironment.setGUIManager(new MWVR::VRGUIManager(mViewer));
//mViewer->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
#endif
if (!mSkipMenu)
{
const std::string& logo = Fallback::Map::getString("Movies_Company_Logo");
@ -729,11 +735,6 @@ void OMW::Engine::go()
osg::ref_ptr<Resource::StatsHandler> resourceshandler = new Resource::StatsHandler;
mViewer->addEventHandler(resourceshandler);
#ifdef USE_OPENXR
mXrEnvironment.setGUIManager(new MWVR::VRGUIManager(mViewer));
//mViewer->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
#endif
// Start the game
if (!mSaveGameFile.empty())
{

@ -288,13 +288,13 @@ namespace MWGui
MyGUI::PointerManager::getInstance().setVisible(false);
mVideoBackground = MyGUI::Gui::getInstance().createWidgetReal<MyGUI::ImageBox>("ImageBox", 0,0,1,1,
MyGUI::Align::Default, "InputBlocker");
MyGUI::Align::Default, "VideoPlayer");
mVideoBackground->setImageTexture("black");
mVideoBackground->setVisible(false);
mVideoBackground->setNeedMouseFocus(true);
mVideoBackground->setNeedKeyFocus(true);
mVideoWidget = mVideoBackground->createWidgetReal<VideoWidget>("ImageBox", 0,0,1,1, MyGUI::Align::Default);
mVideoWidget = mVideoBackground->createWidgetReal<VideoWidget>("ImageBox", 0,0,1,1, MyGUI::Align::Default, "VideoPlayer");
mVideoWidget->setNeedMouseFocus(true);
mVideoWidget->setNeedKeyFocus(true);
mVideoWidget->setVFS(resourceSystem->getVFS());
@ -1969,6 +1969,7 @@ namespace MWGui
void WindowManager::playVideo(const std::string &name, bool allowSkipping)
{
auto* vrGuiManager = MWVR::Environment::get().getGUIManager();
mVideoEnabled = true;
mVideoWidget->playVideo("video\\" + name);
@ -1990,6 +1991,9 @@ namespace MWGui
mVideoBackground->setVisible(true);
vrGuiManager->updateTracking(mViewer->getCamera());
vrGuiManager->insertLayer(mVideoBackground->getLayer()->getName());
bool cursorWasVisible = mCursorVisible;
setCursorVisible(false);
@ -2019,6 +2023,7 @@ namespace MWGui
mViewer->eventTraversal();
mViewer->updateTraversal();
//vrGuiManager->updateTracking(mViewer->getCamera());
mViewer->renderingTraversals();
}
// at the time this function is called we are in the middle of a frame,
@ -2039,6 +2044,7 @@ namespace MWGui
// Restore normal rendering
updateVisible();
vrGuiManager->removeLayer(mVideoBackground->getLayer()->getName());
mVideoBackground->setVisible(false);
mVideoEnabled = false;
}

@ -907,10 +907,10 @@ namespace MWRender
const bool isPlayer = (mPtr == MWMechanics::getPlayer());
//if (isPlayer)
//{
// Log(Debug::Verbose) << "groupname=" << groupname << ", start=" << start << ", stop=" << stop << ", accumRoot=" << mAccumRoot->getName();
//}
if (isPlayer)
{
Log(Debug::Debug) << "groupname=" << groupname << ", start=" << start << ", stop=" << stop << ", accumRoot=" << mAccumRoot->getName();
}
AnimStateMap::iterator stateiter = mStates.begin();
while(stateiter != mStates.end())

@ -57,9 +57,9 @@
namespace MWVR
{
// TODO: Make part of settings (is there already a setting like this?)
//! Delay before a long-press action is activated
static std::chrono::milliseconds gActionTime{ 1000 };
//! Delay before a long-press action is activated (and regular press is discarded)
//! TODO: Make this configurable?
static std::chrono::milliseconds gActionTime{ 666 };
//! Magnitude above which an axis action is considered active
static float gAxisEpsilon{ 0.01f };
@ -568,7 +568,7 @@ OpenXRInput::OpenXRInput()
, mMoveLeftRight(std::move(createMWAction<AxisAction>(MWInput::A_MoveLeftRight, "move_left_right", "Move Left Right", { })))
, mJournal(std::move(createMWAction<ButtonLongPressAction>(MWInput::A_Journal, "journal_book", "Journal Book", { })))
, mQuickSave(std::move(createMWAction<ButtonLongPressAction>(MWInput::A_QuickSave, "quick_save", "Quick Save", { })))
, mRest(std::move(createMWAction<ButtonLongPressAction>(MWInput::A_Rest, "rest", "Rest", { })))
, mRest(std::move(createMWAction<ButtonPressAction>(MWInput::A_Rest, "rest", "Rest", { })))
, mActivateTouch(std::move(createMWAction<AxisAction>(A_ActivateTouch, "activate_touched", "Activate Touch", { RIGHT_HAND })))
, mAlwaysRun(std::move(createMWAction<ButtonPressAction>(MWInput::A_AlwaysRun, "always_run", "Always Run", { })))
, mAutoMove(std::move(createMWAction<ButtonPressAction>(MWInput::A_AutoMove, "auto_move", "Auto Move", { })))
@ -1027,9 +1027,16 @@ private:
MWInput::InputManager::update(dt, disableControls, disableEvents);
// Start next frame phase
auto* session = Environment::get().getSession();
if (session)
session->beginPhase(VRSession::FramePhase::Update);
// The rest of this code assumes the game is running
if (MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running)
if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame)
{
return;
}
bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();

@ -274,10 +274,11 @@ void HandController::operator()(osg::Node* node, osg::NodeVisitor* nv)
osg::Quat rotate{ 0,0,0,1 };
auto* world = MWBase::Environment::get().getWorld();
auto windowManager = MWBase::Environment::get().getWindowManager();
auto animation = MWVR::Environment::get().getPlayerAnimation();
auto weaponType = world->getActiveWeaponType();
// Morrowind models do not hold most weapons at a natural angle, so i rotate the hand
// to more natural angles on weapons to allow more comfortable combat.
if (!windowManager->isGuiMode())
if (!windowManager->isGuiMode() && !animation->isPointingForward())
{
switch (weaponType)
@ -407,10 +408,8 @@ void WeaponPointerController::operator()(osg::Node* node, osg::NodeVisitor* nv)
}
else
{
// Hide the pointer
matrixTransform->setMatrix(
osg::Matrix::scale(1.f, 64.f, 1.f)
//osg::Matrix::scale(0.f, 0.f, 0.f)
);
}

@ -23,6 +23,7 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwgui/windowbase.hpp"
#include "../mwbase/statemanager.hpp"
#include <MyGUI_Widget.h>
#include <MyGUI_ILayer.h>
@ -408,20 +409,11 @@ VRGUIManager::VRGUIManager(
osg::ref_ptr<osgViewer::Viewer> viewer)
: mOsgViewer(viewer)
{
mGUIGeometriesRoot->setName("XR GUI Geometry Root");
mGUICamerasRoot->setName("XR GUI Cameras Root");
mGUIGeometriesRoot->setName("VR GUI Geometry Root");
mGUICamerasRoot->setName("VR GUI Cameras Root");
auto* root = viewer->getSceneData();
SceneUtil::FindByNameVisitor findSceneVisitor("Scene Root");
root->accept(findSceneVisitor);
if(!findSceneVisitor.mFoundNode)
{
Log(Debug::Error) << "Scene Root doesn't exist";
return;
}
findSceneVisitor.mFoundNode->addChild(mGUIGeometriesRoot);
root->asGroup()->addChild(mGUICamerasRoot);
root->asGroup()->addChild(mGUIGeometriesRoot);
}
@ -447,10 +439,12 @@ static const LayerConfig createDefaultConfig(int priority, bool background = tru
};
}
LayerConfig gDefaultConfig = createDefaultConfig(1);
LayerConfig gVideoPlayerConfig = createDefaultConfig(1, true, SizingMode::Fixed);
LayerConfig gLoadingScreenConfig = createDefaultConfig(1, true, SizingMode::Fixed);
LayerConfig gJournalBooksConfig = createDefaultConfig(2, false, SizingMode::Fixed);
LayerConfig gDefaultWindowsConfig = createDefaultConfig(3, true);
LayerConfig gMessageBoxConfig = createDefaultConfig(6, false, SizingMode::Auto);;
LayerConfig gNotificationConfig = createDefaultConfig(7, false, SizingMode::Fixed);;
LayerConfig gNotificationConfig = createDefaultConfig(7, false, SizingMode::Fixed);
static const float sSideBySideRadius = 1.f;
static const float sSideBySideAzimuthInterval = -osg::PI_4;
@ -527,6 +521,8 @@ static std::map<std::string, LayerConfig&> gLayerConfigs =
{"MessageBox", gMessageBoxConfig},
{"Windows", gDefaultWindowsConfig},
{"Notification", gNotificationConfig},
{"VideoPlayer", gVideoPlayerConfig},
{"LoadingScreen", gLoadingScreenConfig},
};
static std::set<std::string> layerBlacklist =
@ -673,21 +669,41 @@ void VRGUIManager::setVisible(MWGui::Layout* widget, bool visible)
void VRGUIManager::updateTracking(void)
{
// Get head pose by reading the camera view matrix to place the GUI in the world.
osg::Vec3 eye{};
osg::Vec3 center{};
osg::Vec3 up{};
Pose headPose{};
auto* world = MWBase::Environment::get().getWorld();
if (!world)
return;
auto* camera = world->getRenderingManager().getCamera()->getOsgCamera();
if (!camera)
return;
camera->getViewMatrixAsLookAt(eye, center, up);
headPose.position = eye;
headPose.orientation = camera->getViewMatrix().getRotate();
headPose.orientation = headPose.orientation.inverse();
updateTracking(camera);
}
void VRGUIManager::updateTracking(osg::Camera* camera)
{
// Get head pose by reading the camera view matrix to place the GUI in the world.
osg::Vec3 eye{};
osg::Vec3 center{};
osg::Vec3 up{};
Pose headPose{};
if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame)
{
// If App is not running, tracking will not be propagated to camera.
// So we adopt stage space.
auto pose = MWVR::Environment::get().getSession()->predictedPoses(MWVR::VRSession::FramePhase::Update).head;
osg::Vec3 position = pose.position * Environment::get().unitsPerMeter();
osg::Quat orientation = pose.orientation;
headPose.position = position;
headPose.orientation = orientation;
}
else
{
auto viewMatrix = camera->getViewMatrix();
viewMatrix.getLookAt(eye, center, up);
headPose.position = eye;
headPose.orientation = viewMatrix.getRotate();
headPose.orientation = headPose.orientation.inverse();
}
mHeadPose = headPose;

@ -137,6 +137,7 @@ namespace MWVR
void removeWidget(MWGui::Layout* widget);
void updateTracking(void);
void updateTracking(osg::Camera* camera);
bool updateFocus();

@ -5,6 +5,7 @@
#include "openxrswapchain.hpp"
#include "../mwinput/inputmanagerimp.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/statemanager.hpp"
#include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlgraphicswindow.hpp>
@ -26,6 +27,14 @@
#include <time.h>
#include <thread>
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
namespace MWVR
{
VRSession::VRSession()
@ -47,7 +56,23 @@ osg::Matrix VRSession::projectionMatrix(FramePhase phase, Side side)
osg::Matrix VRSession::viewMatrix(FramePhase phase, Side side)
{
MWVR::Pose pose = predictedPoses(phase).view[(int)side].pose;
MWVR::Pose pose{};
pose = predictedPoses(phase).view[(int)side].pose;
if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame)
{
pose = predictedPoses(phase).eye[(int)side];
osg::Vec3 position = pose.position * Environment::get().unitsPerMeter();
osg::Quat orientation = pose.orientation;
osg::Vec3d forward = orientation * osg::Vec3d(0, 1, 0);
osg::Vec3d up = orientation * osg::Vec3d(0, 0, 1);
osg::Matrix viewMatrix;
viewMatrix.makeLookAt(position, position + forward, up);
return viewMatrix;
}
osg::Vec3 position = pose.position * Environment::get().unitsPerMeter();
osg::Quat orientation = pose.orientation;
@ -106,72 +131,92 @@ void VRSession::swapBuffers(osg::GraphicsContext* gc, VRViewer& viewer)
layer.views = compositionLayerProjectionViews.data();
auto* layerStack = reinterpret_cast<XrCompositionLayerBaseHeader*>(&layer);
Log(Debug::Debug) << getFrame(FramePhase::Swap)->mFrameNo << ": EndFrame";
Log(Debug::Debug) << getFrame(FramePhase::Swap)->mFrameNo << ": EndFrame " <<std::this_thread::get_id();
xr->endFrame(getFrame(FramePhase::Swap)->mPredictedDisplayTime, 1, &layerStack);
}
std::unique_lock<std::mutex> lock(mMutex);
auto xrPredictionChange = xr->impl().frameState().predictedDisplayTime - mLastPredictedDisplayTime;
mLastPredictedDisplayTime = xr->impl().frameState().predictedDisplayTime;
mLastPredictedDisplayPeriod = xr->impl().frameState().predictedDisplayPeriod;
auto now = std::chrono::steady_clock::now();
mLastFrameInterval = std::chrono::duration_cast<std::chrono::nanoseconds>(now - mLastRenderedFrameTimestamp);
mLastRenderedFrameTimestamp = now;
mLastRenderedFrame = getFrame(FramePhase::Swap)->mFrameNo;
//Log(Debug::Verbose) << getFrame(FramePhase::Swap)->mFrameNo << ": xrPrediction=" << xr->impl().frameState().predictedDisplayTime << ", ourPrediction=" << getFrame(FramePhase::Swap)->mPredictedDisplayTime << ", miss=" << miss << "ms";
Log(Debug::Debug) << "xrPredictionChange=" << (xrPredictionChange / 1000000) << "ms";
auto seconds = std::chrono::duration_cast<std::chrono::duration<double>>(now - mStart).count();
static int sBaseFrames = 0;
if (seconds > 10.f)
{
Log(Debug::Verbose) << "Fps: " << (static_cast<double>(mLastRenderedFrame - sBaseFrames) / seconds);
mStart = now;
sBaseFrames = mLastRenderedFrame;
}
std::unique_lock<std::mutex> lock(mMutex);
// Some of these values are useless until the prediction time bug is resolved by oculus.
//auto xrPredictionChange = xr->impl().frameState().predictedDisplayTime - mLastPredictedDisplayTime;
mLastPredictedDisplayTime = xr->impl().frameState().predictedDisplayTime;
mLastPredictedDisplayPeriod = xr->impl().frameState().predictedDisplayPeriod;
auto now = std::chrono::steady_clock::now();
mLastFrameInterval = std::chrono::duration_cast<std::chrono::nanoseconds>(now - mLastRenderedFrameTimestamp);
mLastRenderedFrameTimestamp = now;
mLastRenderedFrame = getFrame(FramePhase::Swap)->mFrameNo;
//Log(Debug::Debug) << getFrame(FramePhase::Swap)->mFrameNo << ": xrPrediction=" << xr->impl().frameState().predictedDisplayTime << ", ourPrediction=" << getFrame(FramePhase::Swap)->mPredictedDisplayTime << ", miss=" << miss << "ms";
//Log(Debug::Debug) << "xrPredictionChange=" << (xrPredictionChange / 1000000) << "ms";
//Log(Debug::Debug) << "xrPredictionPeriod=" << (mLastPredictedDisplayPeriod / 1000000) << "ms";
// Just a quick averaging fps over some time rather than just the instantaneous.
auto seconds = std::chrono::duration_cast<std::chrono::duration<double>>(now - mStart).count();
static int sBaseFrames = 0;
if (seconds > 10.f)
{
Log(Debug::Debug) << "Fps: " << (static_cast<double>(mLastRenderedFrame - sBaseFrames) / seconds);
mStart = now;
sBaseFrames = mLastRenderedFrame;
}
getFrame(FramePhase::Swap) = nullptr;
getFrame(FramePhase::Swap) = nullptr;
mFramesInFlight--;
}
mCondition.notify_one();
}
void VRSession::beginPhase(FramePhase phase)
{
Timer timer("VRSession::advanceFrame");
Log(Debug::Debug) << "beginPhase(" << ((int)phase) << ")";
Log(Debug::Debug) << "beginPhase(" << ((int)phase) << ") " << std::this_thread::get_id();
if (phase != FramePhase::Update)
if (getFrame(phase))
{
Log(Debug::Warning) << "advanceFramePhase called with a frame alreay in the target phase";
return;
}
if (phase == FramePhase::Update)
{
prepareFrame();
}
else
{
std::unique_lock<std::mutex> lock(mMutex);
FramePhase previousPhase = static_cast<FramePhase>((int)phase - 1);
if (getFrame(phase))
throw std::logic_error("advanceFramePhase called with a frame alreay in the target phase");
if (!getFrame(previousPhase))
throw std::logic_error("advanceFramePhase called without a frame in the predraw phase");
throw std::logic_error("beginPhase called without a frame");
getFrame(phase) = std::move(getFrame(previousPhase));
}
else
prepareFrame();
if (phase == FramePhase::Cull && getFrame(phase)->mShouldRender)
// 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::Draw && getFrame(phase)->mShouldRender)
doFrameSync();
}
void VRSession::doFrameSync()
{
{
auto* xr = Environment::get().getManager();
// Since i am forced to do xr->beginFrame() before cull instead of draw
// i have to explicitly wait for xr->endFrame() to finish to avoid an
// out-of-order error from openxr.
// If i don't wait, we might hit beginFrame() before the previous frame
// reaches endFrame() in which case openxr will cancel the previous frame
// and we will get an out-of-order error due to two back-to-back calls to endFrame()
while (getFrame(phase)->mFrameNo != (mLastRenderedFrame + 1))
std::unique_lock<std::mutex> lock(mMutex);
while (mLastRenderedFrame != mFrames - 1)
{
std::unique_lock<std::mutex> lock(mMutex);
mCondition.wait(lock);
}
Log(Debug::Debug) << getFrame(phase)->mFrameNo << ": WaitFrame";
xr->waitFrame();
Log(Debug::Debug) << getFrame(phase)->mFrameNo << ": BeginFrame";
xr->beginFrame();
}
auto* xr = Environment::get().getManager();
Log(Debug::Debug) << mFrames << ": WaitFrame " << std::this_thread::get_id();
xr->waitFrame();
Log(Debug::Debug) << mFrames << ": BeginFrame " << std::this_thread::get_id();
xr->beginFrame();
}
std::unique_ptr<VRSession::VRFrame>& VRSession::getFrame(FramePhase phase)
@ -183,56 +228,65 @@ std::unique_ptr<VRSession::VRFrame>& VRSession::getFrame(FramePhase phase)
void VRSession::prepareFrame()
{
std::unique_lock<std::mutex> lock(mMutex);
mFrames++;
assert(!mPredrawFrame);
Timer timer("VRSession::startFrame");
auto* xr = Environment::get().getManager();
xr->handleEvents();
// Until OpenXR allows us to get a prediction without waiting
// we make our own (bad) prediction and call xrWaitFrame when it is more convenient
auto frameState = xr->impl().frameState();
long long predictedDisplayTime = 0;
mFrames++;
if (mLastPredictedDisplayTime == 0)
// auto predictedDisplayTime = frameState.predictedDisplayTime;
// if (predictedDisplayTime == 0)
// {
// // First time, need to invent a frame time since openxr won't help us without calling waitframe.
// predictedDisplayTime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
// }
// else
// {
// // Predict display time based on real framerate
// float intervalsf = static_cast<double>(mLastFrameInterval.count()) / static_cast<double>(mLastPredictedDisplayPeriod);
// int intervals = std::max((int)std::roundf(intervalsf), 1);
// predictedDisplayTime = mLastPredictedDisplayTime + intervals * (mFrames - mLastRenderedFrame) * mLastPredictedDisplayPeriod;
// }
// TODO:
//////////////////////// OCULUS BUG
//////////////////// Oculus will suddenly start monotonically increasing their predicted display time by precisely 1 second
//////////////////// regardless of real time passed, causing predictions to go crazy due to the time difference.
//////////////////// Therefore, for the time being, i ignore oculus' predicted display time altogether.
long long predictedDisplayTime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
if (mFrames > 1)
{
// First time, need to invent a frame time since openxr won't help us without calling waitframe.
predictedDisplayTime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
}
else if (mFrames > mLastRenderedFrame)
{
//predictedDisplayTime = mLastPredictedDisplayTime + mLastFrameInterval.count() * (mFrames - mLastRenderedFrame);
float intervalsf = static_cast<double>(mLastFrameInterval.count()) / static_cast<double>(mLastPredictedDisplayPeriod);
#ifdef max
#undef max
#endif
int intervals = std::max((int)std::roundf(intervalsf), 1);
predictedDisplayTime = mLastPredictedDisplayTime + intervals * (mFrames - mLastRenderedFrame) * mLastPredictedDisplayPeriod;
float intervalsf = static_cast<double>(mLastFrameInterval.count()) / static_cast<double>(frameState.predictedDisplayPeriod);
int intervals = std::max((int)std::roundf(intervalsf), 1);
predictedDisplayTime = predictedDisplayTime + intervals * (mFrames - mLastRenderedFrame) * frameState.predictedDisplayPeriod;
}
PoseSet predictedPoses{};
if (isRunning())
xr->impl().enablePredictions();
predictedPoses.head = xr->impl().getPredictedLimbPose(predictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::STAGE) * mPlayerScale;
auto hmdViews = xr->impl().getPredictedViews(predictedDisplayTime, TrackedSpace::VIEW);
predictedPoses.view[(int)Side::LEFT_HAND].pose = fromXR(hmdViews[(int)Side::LEFT_HAND].pose) * mPlayerScale;
predictedPoses.view[(int)Side::RIGHT_HAND].pose = fromXR(hmdViews[(int)Side::RIGHT_HAND].pose) * mPlayerScale;
predictedPoses.view[(int)Side::LEFT_HAND].fov = fromXR(hmdViews[(int)Side::LEFT_HAND].fov);
predictedPoses.view[(int)Side::RIGHT_HAND].fov = fromXR(hmdViews[(int)Side::RIGHT_HAND].fov);
auto stageViews = xr->impl().getPredictedViews(predictedDisplayTime, TrackedSpace::STAGE);
predictedPoses.eye[(int)Side::LEFT_HAND] = fromXR(stageViews[(int)Side::LEFT_HAND].pose) * mPlayerScale;
predictedPoses.eye[(int)Side::RIGHT_HAND] = fromXR(stageViews[(int)Side::RIGHT_HAND].pose) * mPlayerScale;
auto* input = Environment::get().getInputManager();
if (input)
{
xr->impl().enablePredictions();
predictedPoses.head = xr->impl().getPredictedLimbPose(predictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::STAGE) * mPlayerScale;
auto hmdViews = xr->impl().getPredictedViews(predictedDisplayTime, TrackedSpace::VIEW);
predictedPoses.view[(int)Side::LEFT_HAND].pose = fromXR(hmdViews[(int)Side::LEFT_HAND].pose) * mPlayerScale;
predictedPoses.view[(int)Side::RIGHT_HAND].pose = fromXR(hmdViews[(int)Side::RIGHT_HAND].pose) * mPlayerScale;
predictedPoses.view[(int)Side::LEFT_HAND].fov = fromXR(hmdViews[(int)Side::LEFT_HAND].fov);
predictedPoses.view[(int)Side::RIGHT_HAND].fov = fromXR(hmdViews[(int)Side::RIGHT_HAND].fov);
auto stageViews = xr->impl().getPredictedViews(predictedDisplayTime, TrackedSpace::STAGE);
predictedPoses.eye[(int)Side::LEFT_HAND] = fromXR(stageViews[(int)Side::LEFT_HAND].pose) * mPlayerScale;
predictedPoses.eye[(int)Side::RIGHT_HAND] = fromXR(stageViews[(int)Side::RIGHT_HAND].pose) * mPlayerScale;
auto* input = Environment::get().getInputManager();
if (input)
{
predictedPoses.hands[(int)Side::LEFT_HAND] = input->getHandPose(predictedDisplayTime, TrackedSpace::STAGE, Side::LEFT_HAND) * mPlayerScale;
predictedPoses.hands[(int)Side::RIGHT_HAND] = input->getHandPose(predictedDisplayTime, TrackedSpace::STAGE, Side::RIGHT_HAND) * mPlayerScale;
}
xr->impl().disablePredictions();
predictedPoses.hands[(int)Side::LEFT_HAND] = input->getHandPose(predictedDisplayTime, TrackedSpace::STAGE, Side::LEFT_HAND) * mPlayerScale;
predictedPoses.hands[(int)Side::RIGHT_HAND] = input->getHandPose(predictedDisplayTime, TrackedSpace::STAGE, Side::RIGHT_HAND) * mPlayerScale;
}
xr->impl().disablePredictions();
auto& frame = getFrame(FramePhase::Update);
frame.reset(new VRFrame);
@ -240,6 +294,7 @@ void VRSession::prepareFrame()
frame->mFrameNo = mFrames;
frame->mPredictedPoses = predictedPoses;
frame->mShouldRender = isRunning();
mFramesInFlight++;
}
const PoseSet& VRSession::predictedPoses(FramePhase phase)
@ -298,8 +353,9 @@ void VRSession::movementAngles(float& yaw, float& pitch)
// TODO: This strictly speaking violates the rule of not making predictions outside of prepareFrame()
// I should either add VIEW hands to the predicted pose set, or compute this using STAGE poses
// It would likely suffice to compute euler angles for STAGE head and hand and return the difference?
if (!getFrame(FramePhase::Update))
beginPhase(FramePhase::Update);
auto lhandquat = input->getHandPose(getFrame(FramePhase::Update)->mPredictedDisplayTime, TrackedSpace::VIEW, Side::LEFT_HAND).orientation;
//predictedPoses(FramePhase::Predraw).hands[(int)TrackedSpace::VIEW][(int)MWVR::Side::LEFT_HAND].orientation;
float roll = 0.f;
getEulerAngles(lhandquat, yaw, pitch, roll);

@ -54,6 +54,9 @@ public:
//! Starts a new frame
void prepareFrame();
//! Synchronize with openxr
void doFrameSync();
//! Angles to be used for overriding movement direction
void movementAngles(float& yaw, float& pitch);
@ -68,6 +71,7 @@ public:
osg::Matrix viewMatrix(FramePhase phase, Side side);
osg::Matrix projectionMatrix(FramePhase phase, Side side);
int mFramesInFlight{ 0 };
std::array<std::unique_ptr<VRFrame>, (int)FramePhase::NumPhases> mFrame{ nullptr };
std::mutex mMutex{};

@ -607,6 +607,7 @@ namespace MWWorld
void World::useDeathCamera()
{
#ifndef USE_OPENXR
if(mRendering->getCamera()->isVanityOrPreviewModeEnabled() )
{
mRendering->getCamera()->togglePreviewMode(false);
@ -614,6 +615,7 @@ namespace MWWorld
}
if(mRendering->getCamera()->isFirstPerson())
mRendering->getCamera()->toggleViewMode(true);
#endif
}
MWWorld::Player& World::getPlayer()

@ -25,5 +25,6 @@
<Layer name="LoadingScreen" overlapped="false" pick="true"/>
<Layer name="MessageBox" overlapped="false" pick="true"/>
<Layer name="InputBlocker" overlapped="false" pick="true"/>
<Layer name="VideoPlayer" overlapped="false" pick="true"/>
<Layer name="Pointer" overlapped="false" pick="false"/>
</MyGUI>
Loading…
Cancel
Save