mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-03-29 17:06:43 +00:00
Some bugfixes, more experimenting with timing of frame sync calls.
This commit is contained in:
parent
f25c3af9cb
commit
9bd676f5be
11 changed files with 214 additions and 121 deletions
|
@ -564,6 +564,12 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||||
// Create sound system
|
// Create sound system
|
||||||
mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound));
|
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)
|
if (!mSkipMenu)
|
||||||
{
|
{
|
||||||
const std::string& logo = Fallback::Map::getString("Movies_Company_Logo");
|
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;
|
osg::ref_ptr<Resource::StatsHandler> resourceshandler = new Resource::StatsHandler;
|
||||||
mViewer->addEventHandler(resourceshandler);
|
mViewer->addEventHandler(resourceshandler);
|
||||||
|
|
||||||
#ifdef USE_OPENXR
|
|
||||||
mXrEnvironment.setGUIManager(new MWVR::VRGUIManager(mViewer));
|
|
||||||
//mViewer->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Start the game
|
// Start the game
|
||||||
if (!mSaveGameFile.empty())
|
if (!mSaveGameFile.empty())
|
||||||
{
|
{
|
||||||
|
|
|
@ -288,13 +288,13 @@ namespace MWGui
|
||||||
MyGUI::PointerManager::getInstance().setVisible(false);
|
MyGUI::PointerManager::getInstance().setVisible(false);
|
||||||
|
|
||||||
mVideoBackground = MyGUI::Gui::getInstance().createWidgetReal<MyGUI::ImageBox>("ImageBox", 0,0,1,1,
|
mVideoBackground = MyGUI::Gui::getInstance().createWidgetReal<MyGUI::ImageBox>("ImageBox", 0,0,1,1,
|
||||||
MyGUI::Align::Default, "InputBlocker");
|
MyGUI::Align::Default, "VideoPlayer");
|
||||||
mVideoBackground->setImageTexture("black");
|
mVideoBackground->setImageTexture("black");
|
||||||
mVideoBackground->setVisible(false);
|
mVideoBackground->setVisible(false);
|
||||||
mVideoBackground->setNeedMouseFocus(true);
|
mVideoBackground->setNeedMouseFocus(true);
|
||||||
mVideoBackground->setNeedKeyFocus(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->setNeedMouseFocus(true);
|
||||||
mVideoWidget->setNeedKeyFocus(true);
|
mVideoWidget->setNeedKeyFocus(true);
|
||||||
mVideoWidget->setVFS(resourceSystem->getVFS());
|
mVideoWidget->setVFS(resourceSystem->getVFS());
|
||||||
|
@ -1969,6 +1969,7 @@ namespace MWGui
|
||||||
|
|
||||||
void WindowManager::playVideo(const std::string &name, bool allowSkipping)
|
void WindowManager::playVideo(const std::string &name, bool allowSkipping)
|
||||||
{
|
{
|
||||||
|
auto* vrGuiManager = MWVR::Environment::get().getGUIManager();
|
||||||
mVideoEnabled = true;
|
mVideoEnabled = true;
|
||||||
mVideoWidget->playVideo("video\\" + name);
|
mVideoWidget->playVideo("video\\" + name);
|
||||||
|
|
||||||
|
@ -1990,6 +1991,9 @@ namespace MWGui
|
||||||
|
|
||||||
mVideoBackground->setVisible(true);
|
mVideoBackground->setVisible(true);
|
||||||
|
|
||||||
|
vrGuiManager->updateTracking(mViewer->getCamera());
|
||||||
|
vrGuiManager->insertLayer(mVideoBackground->getLayer()->getName());
|
||||||
|
|
||||||
bool cursorWasVisible = mCursorVisible;
|
bool cursorWasVisible = mCursorVisible;
|
||||||
setCursorVisible(false);
|
setCursorVisible(false);
|
||||||
|
|
||||||
|
@ -2019,6 +2023,7 @@ namespace MWGui
|
||||||
|
|
||||||
mViewer->eventTraversal();
|
mViewer->eventTraversal();
|
||||||
mViewer->updateTraversal();
|
mViewer->updateTraversal();
|
||||||
|
//vrGuiManager->updateTracking(mViewer->getCamera());
|
||||||
mViewer->renderingTraversals();
|
mViewer->renderingTraversals();
|
||||||
}
|
}
|
||||||
// at the time this function is called we are in the middle of a frame,
|
// at the time this function is called we are in the middle of a frame,
|
||||||
|
@ -2039,6 +2044,7 @@ namespace MWGui
|
||||||
// Restore normal rendering
|
// Restore normal rendering
|
||||||
updateVisible();
|
updateVisible();
|
||||||
|
|
||||||
|
vrGuiManager->removeLayer(mVideoBackground->getLayer()->getName());
|
||||||
mVideoBackground->setVisible(false);
|
mVideoBackground->setVisible(false);
|
||||||
mVideoEnabled = false;
|
mVideoEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -907,10 +907,10 @@ namespace MWRender
|
||||||
|
|
||||||
const bool isPlayer = (mPtr == MWMechanics::getPlayer());
|
const bool isPlayer = (mPtr == MWMechanics::getPlayer());
|
||||||
|
|
||||||
//if (isPlayer)
|
if (isPlayer)
|
||||||
//{
|
{
|
||||||
// Log(Debug::Verbose) << "groupname=" << groupname << ", start=" << start << ", stop=" << stop << ", accumRoot=" << mAccumRoot->getName();
|
Log(Debug::Debug) << "groupname=" << groupname << ", start=" << start << ", stop=" << stop << ", accumRoot=" << mAccumRoot->getName();
|
||||||
//}
|
}
|
||||||
|
|
||||||
AnimStateMap::iterator stateiter = mStates.begin();
|
AnimStateMap::iterator stateiter = mStates.begin();
|
||||||
while(stateiter != mStates.end())
|
while(stateiter != mStates.end())
|
||||||
|
|
|
@ -57,9 +57,9 @@
|
||||||
namespace MWVR
|
namespace MWVR
|
||||||
{
|
{
|
||||||
|
|
||||||
// TODO: Make part of settings (is there already a setting like this?)
|
//! Delay before a long-press action is activated (and regular press is discarded)
|
||||||
//! Delay before a long-press action is activated
|
//! TODO: Make this configurable?
|
||||||
static std::chrono::milliseconds gActionTime{ 1000 };
|
static std::chrono::milliseconds gActionTime{ 666 };
|
||||||
//! Magnitude above which an axis action is considered active
|
//! Magnitude above which an axis action is considered active
|
||||||
static float gAxisEpsilon{ 0.01f };
|
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", { })))
|
, 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", { })))
|
, mJournal(std::move(createMWAction<ButtonLongPressAction>(MWInput::A_Journal, "journal_book", "Journal Book", { })))
|
||||||
, mQuickSave(std::move(createMWAction<ButtonLongPressAction>(MWInput::A_QuickSave, "quick_save", "Quick Save", { })))
|
, 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 })))
|
, 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", { })))
|
, mAlwaysRun(std::move(createMWAction<ButtonPressAction>(MWInput::A_AlwaysRun, "always_run", "Always Run", { })))
|
||||||
, mAutoMove(std::move(createMWAction<ButtonPressAction>(MWInput::A_AutoMove, "auto_move", "Auto Move", { })))
|
, mAutoMove(std::move(createMWAction<ButtonPressAction>(MWInput::A_AutoMove, "auto_move", "Auto Move", { })))
|
||||||
|
@ -1027,9 +1027,16 @@ private:
|
||||||
|
|
||||||
MWInput::InputManager::update(dt, disableControls, disableEvents);
|
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
|
// 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;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();
|
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 };
|
osg::Quat rotate{ 0,0,0,1 };
|
||||||
auto* world = MWBase::Environment::get().getWorld();
|
auto* world = MWBase::Environment::get().getWorld();
|
||||||
auto windowManager = MWBase::Environment::get().getWindowManager();
|
auto windowManager = MWBase::Environment::get().getWindowManager();
|
||||||
|
auto animation = MWVR::Environment::get().getPlayerAnimation();
|
||||||
auto weaponType = world->getActiveWeaponType();
|
auto weaponType = world->getActiveWeaponType();
|
||||||
// Morrowind models do not hold most weapons at a natural angle, so i rotate the hand
|
// 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.
|
// to more natural angles on weapons to allow more comfortable combat.
|
||||||
if (!windowManager->isGuiMode())
|
if (!windowManager->isGuiMode() && !animation->isPointingForward())
|
||||||
{
|
{
|
||||||
|
|
||||||
switch (weaponType)
|
switch (weaponType)
|
||||||
|
@ -407,10 +408,8 @@ void WeaponPointerController::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Hide the pointer
|
|
||||||
matrixTransform->setMatrix(
|
matrixTransform->setMatrix(
|
||||||
osg::Matrix::scale(1.f, 64.f, 1.f)
|
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/environment.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
#include "../mwgui/windowbase.hpp"
|
#include "../mwgui/windowbase.hpp"
|
||||||
|
#include "../mwbase/statemanager.hpp"
|
||||||
|
|
||||||
#include <MyGUI_Widget.h>
|
#include <MyGUI_Widget.h>
|
||||||
#include <MyGUI_ILayer.h>
|
#include <MyGUI_ILayer.h>
|
||||||
|
@ -408,20 +409,11 @@ VRGUIManager::VRGUIManager(
|
||||||
osg::ref_ptr<osgViewer::Viewer> viewer)
|
osg::ref_ptr<osgViewer::Viewer> viewer)
|
||||||
: mOsgViewer(viewer)
|
: mOsgViewer(viewer)
|
||||||
{
|
{
|
||||||
mGUIGeometriesRoot->setName("XR GUI Geometry Root");
|
mGUIGeometriesRoot->setName("VR GUI Geometry Root");
|
||||||
mGUICamerasRoot->setName("XR GUI Cameras Root");
|
mGUICamerasRoot->setName("VR GUI Cameras Root");
|
||||||
auto* root = viewer->getSceneData();
|
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(mGUICamerasRoot);
|
||||||
|
root->asGroup()->addChild(mGUIGeometriesRoot);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,10 +439,12 @@ static const LayerConfig createDefaultConfig(int priority, bool background = tru
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
LayerConfig gDefaultConfig = createDefaultConfig(1);
|
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 gJournalBooksConfig = createDefaultConfig(2, false, SizingMode::Fixed);
|
||||||
LayerConfig gDefaultWindowsConfig = createDefaultConfig(3, true);
|
LayerConfig gDefaultWindowsConfig = createDefaultConfig(3, true);
|
||||||
LayerConfig gMessageBoxConfig = createDefaultConfig(6, false, SizingMode::Auto);;
|
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 sSideBySideRadius = 1.f;
|
||||||
static const float sSideBySideAzimuthInterval = -osg::PI_4;
|
static const float sSideBySideAzimuthInterval = -osg::PI_4;
|
||||||
|
@ -527,6 +521,8 @@ static std::map<std::string, LayerConfig&> gLayerConfigs =
|
||||||
{"MessageBox", gMessageBoxConfig},
|
{"MessageBox", gMessageBoxConfig},
|
||||||
{"Windows", gDefaultWindowsConfig},
|
{"Windows", gDefaultWindowsConfig},
|
||||||
{"Notification", gNotificationConfig},
|
{"Notification", gNotificationConfig},
|
||||||
|
{"VideoPlayer", gVideoPlayerConfig},
|
||||||
|
{"LoadingScreen", gLoadingScreenConfig},
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::set<std::string> layerBlacklist =
|
static std::set<std::string> layerBlacklist =
|
||||||
|
@ -673,21 +669,41 @@ void VRGUIManager::setVisible(MWGui::Layout* widget, bool visible)
|
||||||
|
|
||||||
void VRGUIManager::updateTracking(void)
|
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();
|
auto* world = MWBase::Environment::get().getWorld();
|
||||||
if (!world)
|
if (!world)
|
||||||
return;
|
return;
|
||||||
auto* camera = world->getRenderingManager().getCamera()->getOsgCamera();
|
auto* camera = world->getRenderingManager().getCamera()->getOsgCamera();
|
||||||
if (!camera)
|
if (!camera)
|
||||||
return;
|
return;
|
||||||
camera->getViewMatrixAsLookAt(eye, center, up);
|
updateTracking(camera);
|
||||||
headPose.position = eye;
|
}
|
||||||
headPose.orientation = camera->getViewMatrix().getRotate();
|
|
||||||
headPose.orientation = headPose.orientation.inverse();
|
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;
|
mHeadPose = headPose;
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,7 @@ namespace MWVR
|
||||||
void removeWidget(MWGui::Layout* widget);
|
void removeWidget(MWGui::Layout* widget);
|
||||||
|
|
||||||
void updateTracking(void);
|
void updateTracking(void);
|
||||||
|
void updateTracking(osg::Camera* camera);
|
||||||
|
|
||||||
bool updateFocus();
|
bool updateFocus();
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "openxrswapchain.hpp"
|
#include "openxrswapchain.hpp"
|
||||||
#include "../mwinput/inputmanagerimp.hpp"
|
#include "../mwinput/inputmanagerimp.hpp"
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/statemanager.hpp"
|
||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||||
|
@ -26,6 +27,14 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
#ifdef max
|
||||||
|
#undef max
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef min
|
||||||
|
#undef min
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace MWVR
|
namespace MWVR
|
||||||
{
|
{
|
||||||
VRSession::VRSession()
|
VRSession::VRSession()
|
||||||
|
@ -47,7 +56,23 @@ osg::Matrix VRSession::projectionMatrix(FramePhase phase, Side side)
|
||||||
|
|
||||||
osg::Matrix VRSession::viewMatrix(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::Vec3 position = pose.position * Environment::get().unitsPerMeter();
|
||||||
osg::Quat orientation = pose.orientation;
|
osg::Quat orientation = pose.orientation;
|
||||||
|
|
||||||
|
@ -106,72 +131,92 @@ void VRSession::swapBuffers(osg::GraphicsContext* gc, VRViewer& viewer)
|
||||||
layer.views = compositionLayerProjectionViews.data();
|
layer.views = compositionLayerProjectionViews.data();
|
||||||
auto* layerStack = reinterpret_cast<XrCompositionLayerBaseHeader*>(&layer);
|
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);
|
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);
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
mStart = now;
|
|
||||||
sBaseFrames = mLastRenderedFrame;
|
|
||||||
}
|
|
||||||
|
|
||||||
getFrame(FramePhase::Swap) = nullptr;
|
// 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;
|
||||||
|
mFramesInFlight--;
|
||||||
|
}
|
||||||
mCondition.notify_one();
|
mCondition.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VRSession::beginPhase(FramePhase phase)
|
void VRSession::beginPhase(FramePhase phase)
|
||||||
{
|
{
|
||||||
Timer timer("VRSession::advanceFrame");
|
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);
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
FramePhase previousPhase = static_cast<FramePhase>((int)phase - 1);
|
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))
|
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));
|
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();
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
// Since i am forced to do xr->beginFrame() before cull instead of draw
|
while (mLastRenderedFrame != mFrames - 1)
|
||||||
// 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);
|
|
||||||
mCondition.wait(lock);
|
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)
|
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()
|
void VRSession::prepareFrame()
|
||||||
{
|
{
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
mFrames++;
|
||||||
assert(!mPredrawFrame);
|
assert(!mPredrawFrame);
|
||||||
|
|
||||||
Timer timer("VRSession::startFrame");
|
Timer timer("VRSession::startFrame");
|
||||||
auto* xr = Environment::get().getManager();
|
auto* xr = Environment::get().getManager();
|
||||||
xr->handleEvents();
|
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();
|
auto frameState = xr->impl().frameState();
|
||||||
long long predictedDisplayTime = 0;
|
// auto predictedDisplayTime = frameState.predictedDisplayTime;
|
||||||
mFrames++;
|
// if (predictedDisplayTime == 0)
|
||||||
if (mLastPredictedDisplayTime == 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.
|
float intervalsf = static_cast<double>(mLastFrameInterval.count()) / static_cast<double>(frameState.predictedDisplayPeriod);
|
||||||
predictedDisplayTime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
int intervals = std::max((int)std::roundf(intervalsf), 1);
|
||||||
}
|
predictedDisplayTime = predictedDisplayTime + intervals * (mFrames - mLastRenderedFrame) * frameState.predictedDisplayPeriod;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
PoseSet predictedPoses{};
|
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();
|
xr->impl().enablePredictions();
|
||||||
if (input)
|
predictedPoses.head = xr->impl().getPredictedLimbPose(predictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::STAGE) * mPlayerScale;
|
||||||
{
|
auto hmdViews = xr->impl().getPredictedViews(predictedDisplayTime, TrackedSpace::VIEW);
|
||||||
predictedPoses.hands[(int)Side::LEFT_HAND] = input->getHandPose(predictedDisplayTime, TrackedSpace::STAGE, Side::LEFT_HAND) * mPlayerScale;
|
predictedPoses.view[(int)Side::LEFT_HAND].pose = fromXR(hmdViews[(int)Side::LEFT_HAND].pose) * mPlayerScale;
|
||||||
predictedPoses.hands[(int)Side::RIGHT_HAND] = input->getHandPose(predictedDisplayTime, TrackedSpace::STAGE, Side::RIGHT_HAND) * 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);
|
||||||
xr->impl().disablePredictions();
|
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();
|
||||||
|
|
||||||
auto& frame = getFrame(FramePhase::Update);
|
auto& frame = getFrame(FramePhase::Update);
|
||||||
frame.reset(new VRFrame);
|
frame.reset(new VRFrame);
|
||||||
|
@ -240,6 +294,7 @@ void VRSession::prepareFrame()
|
||||||
frame->mFrameNo = mFrames;
|
frame->mFrameNo = mFrames;
|
||||||
frame->mPredictedPoses = predictedPoses;
|
frame->mPredictedPoses = predictedPoses;
|
||||||
frame->mShouldRender = isRunning();
|
frame->mShouldRender = isRunning();
|
||||||
|
mFramesInFlight++;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PoseSet& VRSession::predictedPoses(FramePhase phase)
|
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()
|
// 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
|
// 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?
|
// 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;
|
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;
|
float roll = 0.f;
|
||||||
getEulerAngles(lhandquat, yaw, pitch, roll);
|
getEulerAngles(lhandquat, yaw, pitch, roll);
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,9 @@ public:
|
||||||
//! Starts a new frame
|
//! Starts a new frame
|
||||||
void prepareFrame();
|
void prepareFrame();
|
||||||
|
|
||||||
|
//! Synchronize with openxr
|
||||||
|
void doFrameSync();
|
||||||
|
|
||||||
//! Angles to be used for overriding movement direction
|
//! Angles to be used for overriding movement direction
|
||||||
void movementAngles(float& yaw, float& pitch);
|
void movementAngles(float& yaw, float& pitch);
|
||||||
|
|
||||||
|
@ -68,6 +71,7 @@ public:
|
||||||
osg::Matrix viewMatrix(FramePhase phase, Side side);
|
osg::Matrix viewMatrix(FramePhase phase, Side side);
|
||||||
osg::Matrix projectionMatrix(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::array<std::unique_ptr<VRFrame>, (int)FramePhase::NumPhases> mFrame{ nullptr };
|
||||||
|
|
||||||
std::mutex mMutex{};
|
std::mutex mMutex{};
|
||||||
|
|
|
@ -607,6 +607,7 @@ namespace MWWorld
|
||||||
|
|
||||||
void World::useDeathCamera()
|
void World::useDeathCamera()
|
||||||
{
|
{
|
||||||
|
#ifndef USE_OPENXR
|
||||||
if(mRendering->getCamera()->isVanityOrPreviewModeEnabled() )
|
if(mRendering->getCamera()->isVanityOrPreviewModeEnabled() )
|
||||||
{
|
{
|
||||||
mRendering->getCamera()->togglePreviewMode(false);
|
mRendering->getCamera()->togglePreviewMode(false);
|
||||||
|
@ -614,6 +615,7 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
if(mRendering->getCamera()->isFirstPerson())
|
if(mRendering->getCamera()->isFirstPerson())
|
||||||
mRendering->getCamera()->toggleViewMode(true);
|
mRendering->getCamera()->toggleViewMode(true);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Player& World::getPlayer()
|
MWWorld::Player& World::getPlayer()
|
||||||
|
|
|
@ -25,5 +25,6 @@
|
||||||
<Layer name="LoadingScreen" overlapped="false" pick="true"/>
|
<Layer name="LoadingScreen" overlapped="false" pick="true"/>
|
||||||
<Layer name="MessageBox" overlapped="false" pick="true"/>
|
<Layer name="MessageBox" overlapped="false" pick="true"/>
|
||||||
<Layer name="InputBlocker" 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"/>
|
<Layer name="Pointer" overlapped="false" pick="false"/>
|
||||||
</MyGUI>
|
</MyGUI>
|
Loading…
Reference in a new issue