1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-03-30 03:06:43 +00:00

Fixed performance issues related to xr synchronization as well as osg pipelining. Some code refactoring / cleanup.

This commit is contained in:
Mads Buvik Sandvei 2020-05-24 18:00:42 +02:00
parent 3e3ed7ecee
commit 480ce82217
36 changed files with 794 additions and 1014 deletions

View file

@ -124,32 +124,28 @@ if(BUILD_VR_OPENXR)
vrengine.cpp
mwvr/openxrinputmanager.hpp
mwvr/openxrinputmanager.cpp
mwvr/openxrlayer.hpp
mwvr/openxrlayer.cpp
mwvr/openxrmanager.hpp
mwvr/openxrmanager.cpp
mwvr/openxrmanagerimpl.hpp
mwvr/openxrmanagerimpl.cpp
mwvr/vrgui.hpp
mwvr/vrgui.cpp
mwvr/openxrsession.hpp
mwvr/openxrsession.cpp
mwvr/openxrswapchain.hpp
mwvr/openxrswapchain.cpp
mwvr/vrtexture.hpp
mwvr/vrtexture.cpp
mwvr/openxrview.hpp
mwvr/openxrview.cpp
mwvr/openxrviewer.hpp
mwvr/openxrviewer.cpp
mwvr/openxrworldview.hpp
mwvr/openxrworldview.cpp
mwvr/realisticcombat.hpp
mwvr/realisticcombat.cpp
mwvr/vranimation.hpp
mwvr/vranimation.cpp
mwvr/vrenvironment.hpp
mwvr/vrenvironment.cpp
mwvr/vrgui.hpp
mwvr/vrgui.cpp
mwvr/vrsession.hpp
mwvr/vrsession.cpp
mwvr/vrtexture.hpp
mwvr/vrtexture.cpp
mwvr/vrview.hpp
mwvr/vrview.cpp
mwvr/vrviewer.hpp
mwvr/vrviewer.cpp
)
openmw_add_executable(openmw_vr

View file

@ -61,7 +61,7 @@
#ifdef USE_OPENXR
#include "mwvr/openxrinputmanager.hpp"
#include "mwvr/openxrviewer.hpp"
#include "mwvr/vrviewer.hpp"
#endif
namespace
@ -502,6 +502,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
);
int numThreads = Settings::Manager::getInt("preload num threads", "Cells");
Log(Debug::Verbose) << "Num threads: " << numThreads;
if (numThreads <= 0)
throw std::runtime_error("Invalid setting: 'preload num threads' must be >0");
mWorkQueue = new SceneUtil::WorkQueue(numThreads);
@ -733,10 +734,6 @@ void OMW::Engine::go()
mViewer->addEventHandler(resourceshandler);
#ifdef USE_OPENXR
auto* root = mViewer->getSceneData();
auto* xrViewer = MWVR::Environment::get().getViewer();
xrViewer->addChild(root);
mViewer->setSceneData(xrViewer);
mXrEnvironment.setGUIManager(new MWVR::VRGUIManager(mViewer));
#endif
@ -764,8 +761,6 @@ void OMW::Engine::go()
mEnvironment.getWindowManager()->executeInConsole(mStartupScript);
}
// Start the main rendering loop
osg::Timer frameTimer;
double simulationTime = 0.0;
@ -789,13 +784,8 @@ void OMW::Engine::go()
mEnvironment.getWorld()->updateWindowManager();
#ifdef USE_OPENXR
xrViewer->traversals();
#else
mViewer->updateTraversal();
mViewer->renderingTraversals();
#endif
bool guiActive = mEnvironment.getWindowManager()->isGuiMode();
if (!guiActive)

View file

@ -20,7 +20,7 @@
#include "../mwworld/refdata.hpp"
#ifdef USE_OPENXR
#include "../mwvr/openxrsession.hpp"
#include "../mwvr/vrsession.hpp"
#include "../mwvr/openxrinputmanager.hpp"
#include "../mwvr/vrenvironment.hpp"
#endif

View file

@ -73,7 +73,7 @@
#ifdef USE_OPENXR
#include "../mwvr/vranimation.hpp"
#include "../mwvr/openxrviewer.hpp"
#include "../mwvr/vrviewer.hpp"
#include "../mwvr/vrenvironment.hpp"
#endif
@ -1015,8 +1015,11 @@ namespace MWRender
osg::Camera* camera = mViewer->getCamera();
#ifdef USE_OPENXR
// In VR mode, the main camera is disabled.
auto& slave = mViewer->getSlave(1);
camera = slave._camera;
if (mViewer->getNumSlaves() > 0)
{
auto& slave = mViewer->getSlave(mViewer->getNumSlaves()-1);
camera = slave._camera;
}
#endif
osg::ref_ptr<osg::Drawable> tempDrw = new osg::Drawable;
tempDrw->setDrawCallback(new ReadImageFromFramebufferCallback(image, w, h));

View file

@ -443,6 +443,8 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem
mWaterGeom = SceneUtil::createWaterGeometry(Constants::CellSizeInUnits*150, 40, 900);
mWaterGeom->setDrawCallback(new DepthClampCallback);
mWaterGeom->setNodeMask(Mask_Water);
mWaterGeom->setName("Water Geometry");
//mWaterGeom->setDataVariance(osg::Object::STATIC);
mWaterNode = new osg::PositionAttitudeTransform;
mWaterNode->setName("Water Root");
@ -453,6 +455,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem
osg::ref_ptr<osg::Geometry> geom2 (osg::clone(mWaterGeom.get(), osg::CopyOp::DEEP_COPY_NODES));
createSimpleWaterStateSet(geom2, Fallback::Map::getFloat("Water_Map_Alpha"));
geom2->setNodeMask(Mask_SimpleWater);
geom2->setName("Water Geometry Fallback");
mWaterNode->addChild(geom2);
mSceneRoot->addChild(mWaterNode);

View file

@ -330,7 +330,7 @@ struct OpenXRInput
void updateControls();
const Action* nextAction();
PoseSet getHandPoses(int64_t time, TrackedSpace space);
Pose getHandPose(int64_t time, TrackedSpace space, Side side);
void applyHaptics(SubAction subAction, float intensity)
{
@ -798,15 +798,6 @@ OpenXRInput::updateControls()
mAutoMove->updateAndQueue(mActionQueue);
mToggleHUD->updateAndQueue(mActionQueue);
mToggleDebug->updateAndQueue(mActionQueue);
//if (mActivateTouch->isActive())
//{
// mHapticsAction.applyHaptics(mSubactionPath[RIGHT_HAND], 0.5);
//}
//if (mSneak->isActive())
//{
// mHapticsAction.applyHaptics(mSubactionPath[LEFT_HAND], 0.5);
//}
}
XrPath OpenXRInput::generateXrPath(const std::string& path)
@ -828,53 +819,35 @@ const Action* OpenXRInput::nextAction()
}
PoseSet
OpenXRInput::getHandPoses(
Pose
OpenXRInput::getHandPose(
int64_t time,
TrackedSpace space)
TrackedSpace space,
Side side)
{
auto* xr = Environment::get().getManager();
PoseSet handPoses{};
XrSpace referenceSpace = XR_NULL_HANDLE;
if (space == TrackedSpace::STAGE)
referenceSpace = xr->impl().mReferenceSpaceStage;
if (space == TrackedSpace::VIEW)
referenceSpace = xr->impl().mReferenceSpaceView;
XrSpace referenceSpace = xr->impl().getReferenceSpace(space);
XrSpaceLocation location{ XR_TYPE_SPACE_LOCATION };
XrSpaceVelocity velocity{ XR_TYPE_SPACE_VELOCITY };
location.next = &velocity;
CHECK_XRCMD(xrLocateSpace(mHandSpace[(int)Side::LEFT_HAND], referenceSpace, time, &location));
CHECK_XRCMD(xrLocateSpace(mHandSpace[(int)side], referenceSpace, time, &location));
if (!location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT)
// Quat must have a magnitude of 1 but openxr sets it to 0 when tracking is unavailable.
// I want a no-track pose to still be a valid quat so osg won't throw errors
location.pose.orientation.w = 1;
handPoses[(int)Side::LEFT_HAND] = MWVR::Pose{
return MWVR::Pose{
osg::fromXR(location.pose.position),
osg::fromXR(location.pose.orientation),
osg::fromXR(velocity.linearVelocity)
};
CHECK_XRCMD(xrLocateSpace(mHandSpace[(int)Side::RIGHT_HAND], referenceSpace, time, &location));
if (!location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT)
// Quat must have a magnitude of 1 but openxr sets it to 0 when tracking is unavailable.
// I want a no-track pose to still be a valid quat so osg won't throw errors
location.pose.orientation.w = 1;
handPoses[(int)Side::RIGHT_HAND] = MWVR::Pose{
osg::fromXR(location.pose.position),
osg::fromXR(location.pose.orientation),
osg::fromXR(velocity.linearVelocity)
};
return handPoses;
}
PoseSet OpenXRInputManager::getHandPoses(int64_t time, TrackedSpace space)
Pose OpenXRInputManager::getHandPose(int64_t time, TrackedSpace space, Side side)
{
return mXRInput->getHandPoses(time, space);
return mXRInput->getHandPose(time, space, side);
}
void OpenXRInputManager::updateActivationIndication(void)
@ -1030,12 +1003,9 @@ private:
bool disableControls,
bool disableEvents)
{
auto begin = std::chrono::steady_clock::now();
mXRInput->updateControls();
// Annoyingly, this is called at least once before the World object has been instantiated.
auto* world = MWBase::Environment::get().getWorld();
if (!world)
return;
auto* vrGuiManager = Environment::get().getGUIManager();
bool vrHasFocus = vrGuiManager->updateFocus();
@ -1054,27 +1024,42 @@ private:
MWInput::InputManager::update(dt, disableControls, disableEvents);
// The rest of this code assumes the game is running
if (MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running)
return;
bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();
// OpenMW assumes all input will come via SDL which i often violate.
// This keeps player controls correctly enabled for my purposes.
mBindingsManager->setPlayerControlsEnabled(!guiMode);
auto& player = world->getPlayer();
auto playerPtr = player.getPlayer();
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);
updateHead();
if (!guiMode)
{
auto* world = MWBase::Environment::get().getWorld();
auto& player = world->getPlayer();
auto playerPtr = world->getPlayerPtr();
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);
}
else if(mRealisticCombat)
mRealisticCombat->update(dt, false);
// Update tracking every frame if player is not currently in GUI mode.
// This ensures certain widgets like Notifications will be visible.
if (!guiMode)
{
vrGuiManager->updateTracking();
}
auto end = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - begin);
}
void OpenXRInputManager::processAction(const Action* action)
@ -1249,34 +1234,31 @@ private:
void OpenXRInputManager::updateHead()
{
MWBase::World* world = MWBase::Environment::get().getWorld();
auto& player = world->getPlayer();
auto playerPtr = player.getPlayer();
auto* xr = Environment::get().getManager();
auto* session = Environment::get().getSession();
auto currentHeadPose = session->predictedPoses(OpenXRSession::PredictionSlice::Predraw).head[(int)TrackedSpace::STAGE];
xr->playerScale(currentHeadPose);
auto currentHeadPose = session->predictedPoses(VRSession::FramePhase::Predraw).head;
currentHeadPose.position *= Environment::get().unitsPerMeter();
osg::Vec3 vrMovement = currentHeadPose.position - mPreviousHeadPose.position;
mPreviousHeadPose = currentHeadPose;
osg::Vec3 vrMovement = currentHeadPose.position - mHeadPose.position;
mHeadPose = currentHeadPose;
osg::Quat gameworldYaw = osg::Quat(mYaw, osg::Vec3(0, 0, -1));
mHeadOffset += gameworldYaw * vrMovement;
if (mRecenter)
{
mHeadOffset = osg::Vec3(0, 0, 0);
// Z should not be affected
mHeadOffset.z() = currentHeadPose.position.z();
mHeadOffset.z() = mHeadPose.position.z();
mRecenter = false;
}
else
{
osg::Quat gameworldYaw = osg::Quat(mYaw, osg::Vec3(0, 0, -1));
mHeadOffset += gameworldYaw * vrMovement;
MWBase::World* world = MWBase::Environment::get().getWorld();
auto& player = world->getPlayer();
auto playerPtr = player.getPlayer();
float yaw = 0.f;
float pitch = 0.f;
float roll = 0.f;
getEulerAngles(currentHeadPose.orientation, yaw, pitch, roll);
getEulerAngles(mHeadPose.orientation, yaw, pitch, roll);
yaw += mYaw;

View file

@ -1,7 +1,7 @@
#ifndef OPENXR_INPUT_MANAGER_HPP
#define OPENXR_INPUT_MANAGER_HPP
#include "openxrviewer.hpp"
#include "vrviewer.hpp"
#include "realisticcombat.hpp"
#include "../mwinput/inputmanagerimp.hpp"
@ -42,7 +42,7 @@ namespace MWVR
void processAction(const class Action* action);
PoseSet getHandPoses(int64_t time, TrackedSpace space);
Pose getHandPose(int64_t time, TrackedSpace space, Side side);
void updateActivationIndication(void);
void pointActivation(bool onPress);
@ -55,7 +55,7 @@ namespace MWVR
std::unique_ptr<OpenXRInput> mXRInput;
std::unique_ptr<RealisticCombat::StateMachine> mRealisticCombat;
Pose mPreviousHeadPose{};
Pose mHeadPose{};
osg::Vec3 mHeadOffset{ 0,0,0 };
bool mRecenter{ true };
bool mActivationIndication{ false };

View file

@ -1,26 +0,0 @@
#include "openxrlayer.hpp"
namespace MWVR {
void OpenXRLayerStack::setLayer(Layer layer, OpenXRLayer* layerObj)
{
mLayerObjects[layer] = layerObj;
}
OpenXRLayerStack::LayerHeaders OpenXRLayerStack::layerHeaders()
{
LayerHeaders headers{};
for (auto* layerObject: mLayerObjects)
{
if (layerObject)
{
auto* header = layerObject->layer();
if (header)
headers.push_back(header);
}
}
return headers;
}
}

View file

@ -1,53 +0,0 @@
#ifndef OPENXR_LAYER_HPP
#define OPENXR_LAYER_HPP
#include <array>
#include "openxrmanager.hpp"
#include "vrtexture.hpp"
struct XrCompositionLayerBaseHeader;
namespace MWVR
{
class OpenXRLayer
{
public:
OpenXRLayer(void) = default;
virtual ~OpenXRLayer(void) = default;
virtual const XrCompositionLayerBaseHeader* layer() = 0;
virtual void swapBuffers(osg::GraphicsContext* gc) = 0;
bool isVisible() const { return mVisible; };
void setVisible(bool visible) { mVisible = visible; };
protected:
bool mVisible{ false };
};
class OpenXRLayerStack
{
public:
enum Layer {
WORLD_VIEW_LAYER = 0,
MENU_VIEW_LAYER = 1,
LAYER_MAX = MENU_VIEW_LAYER //!< Used to size layer arrays. Not a valid input.
};
using LayerObjectStack = std::array< OpenXRLayer*, LAYER_MAX + 1>;
using LayerHeaders = std::vector<const XrCompositionLayerBaseHeader*>;
OpenXRLayerStack() = default;
~OpenXRLayerStack() = default;
void setLayer(Layer layer, OpenXRLayer* layerObj);
LayerHeaders layerHeaders();
LayerObjectStack& layerObjects() { return mLayerObjects; };
private:
LayerObjectStack mLayerObjects;
};
}
#endif

View file

@ -31,7 +31,6 @@ namespace MWVR
static void statsThreadRun()
{
return;
while (statsThreadRunning)
{
std::stringstream ss;
@ -156,10 +155,10 @@ namespace MWVR
return impl().beginFrame();
}
void OpenXRManager::endFrame(int64_t displayTime, class OpenXRLayerStack* layerStack)
void OpenXRManager::endFrame(int64_t displayTime, int layerCount, XrCompositionLayerBaseHeader** layerStack)
{
if (realized())
return impl().endFrame(displayTime, layerStack);
return impl().endFrame(displayTime, layerCount, layerStack);
}
void OpenXRManager::updateControls()
@ -217,11 +216,6 @@ namespace MWVR
}
void OpenXRManager::playerScale(MWVR::Pose& stagePose)
{
if (mPrivate)
mPrivate->playerScale(stagePose);
}
Pose Pose::operator+(const Pose& rhs)
{
Pose pose = *this;
@ -229,11 +223,72 @@ namespace MWVR
pose.orientation = rhs.orientation * this->orientation;
return pose;
}
const Pose& Pose::operator+=(const Pose& rhs)
{
*this = *this + rhs;
return *this;
}
Pose Pose::operator*(float scalar)
{
Pose pose = *this;
pose.position *= scalar;
pose.velocity *= scalar;
return pose;
}
const Pose& Pose::operator*=(float scalar)
{
*this = *this * scalar;
return *this;
}
// near and far named with an underscore because of galaxy brain defines in included windows headers.
osg::Matrix FieldOfView::perspectiveMatrix(float near_, float far_)
{
const float tanLeft = tanf(angleLeft);
const float tanRight = tanf(angleRight);
const float tanDown = tanf(angleDown);
const float tanUp = tanf(angleUp);
const float tanWidth = tanRight - tanLeft;
const float tanHeight = tanUp - tanDown;
const float offset = near_;
float matrix[16] = {};
matrix[0] = 2 / tanWidth;
matrix[4] = 0;
matrix[8] = (tanRight + tanLeft) / tanWidth;
matrix[12] = 0;
matrix[1] = 0;
matrix[5] = 2 / tanHeight;
matrix[9] = (tanUp + tanDown) / tanHeight;
matrix[13] = 0;
if (far_ <= near_) {
matrix[2] = 0;
matrix[6] = 0;
matrix[10] = -1;
matrix[14] = -(near_ + offset);
}
else {
matrix[2] = 0;
matrix[6] = 0;
matrix[10] = -(far_ + offset) / (far_ - near_);
matrix[14] = -(far_ * (near_ + offset)) / (far_ - near_);
}
matrix[3] = 0;
matrix[7] = 0;
matrix[11] = -1;
matrix[15] = 0;
return osg::Matrix(matrix);
}
}
std::ostream& operator <<(

View file

@ -15,7 +15,7 @@
struct XrSwapchainSubImage;
struct XrCompositionLayerBaseHeader;
namespace MWVR
{
@ -45,17 +45,36 @@ namespace MWVR
//! Speed of movement in VR space, expressed in meters per second
osg::Vec3 velocity{ 0,0,0 };
//! Add one pose to another
Pose operator+(const Pose& rhs);
const Pose& operator+=(const Pose& rhs);
//! Scale a pose (does not affect orientation)
Pose operator*(float scalar);
const Pose& operator*=(float scalar);
};
using PoseSet = std::array<Pose, 2>;
struct FieldOfView {
float angleLeft;
float angleRight;
float angleUp;
float angleDown;
struct PoseSets
osg::Matrix perspectiveMatrix(float near, float far);
};
struct View
{
PoseSet eye[2]{};
PoseSet hands[2]{};
PoseSet head{};
Pose pose;
FieldOfView fov;
};
struct PoseSet
{
View view[2]{};
Pose eye[2]{};
Pose hands[2]{};
Pose head{};
};
//! Describes what limb to track.
@ -117,9 +136,8 @@ namespace MWVR
void handleEvents();
void waitFrame();
void beginFrame();
void endFrame(int64_t displayTime, class OpenXRLayerStack* layerStack);
void endFrame(int64_t displayTime, int layerCount, XrCompositionLayerBaseHeader** layerStack);
void updateControls();
void playerScale(MWVR::Pose& stagePose);
void realize(osg::GraphicsContext* gc);

View file

@ -295,34 +295,17 @@ namespace MWVR
}
void
OpenXRManagerImpl::endFrame(int64_t displayTime, OpenXRLayerStack* layerStack)
OpenXRManagerImpl::endFrame(int64_t displayTime, int layerCount, XrCompositionLayerBaseHeader** layerStack)
{
Timer timer("endFrame()");
if (!mSessionRunning)
return;
OpenXRLayerStack::LayerHeaders headers;
XrFrameEndInfo frameEndInfo{ XR_TYPE_FRAME_END_INFO };
frameEndInfo.displayTime = displayTime;
frameEndInfo.environmentBlendMode = mEnvironmentBlendMode;
if (layerStack)
{
headers = layerStack->layerHeaders();
frameEndInfo.layerCount = headers.size();
frameEndInfo.layers = headers.data();
}
else {
frameEndInfo.layerCount = 0;
frameEndInfo.layers = nullptr;
}
//Log(Debug::Verbose) << "LayerStack<" << frameEndInfo.layerCount << ", " << frameEndInfo.layers << ">";
static std::chrono::steady_clock::time_point last = std::chrono::steady_clock::now();
auto now = std::chrono::steady_clock::now();
auto elapsed = now -last;
last = now;
frameEndInfo.layerCount = layerCount;
frameEndInfo.layers = layerStack;
CHECK_XRCMD(xrEndFrame(mSession, &frameEndInfo));
}
@ -498,26 +481,24 @@ namespace MWVR
return XrPosef{ osg::toXR(pose.orientation), osg::toXR(pose.position) };
}
void OpenXRManagerImpl::playerScale(MWVR::Pose& stagePose)
MWVR::FieldOfView fromXR(XrFovf fov)
{
auto worldPtr = MWBase::Environment::get().getWorld();
if (worldPtr)
{
auto playerPtr = MWMechanics::getPlayer();
const MWWorld::LiveCellRef<ESM::NPC>* ref = playerPtr.get<ESM::NPC>();
const ESM::Race* race =
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 charHeight = charHeightBase * charHeightFactor;
// TODO: Player height should be configurable
// For now i'm just using my own
float sizeFactor = 1.85f / charHeight;
stagePose.position /= sizeFactor;
stagePose.velocity /= sizeFactor;
}
return MWVR::FieldOfView{ fov.angleLeft, fov.angleRight, fov.angleUp, fov.angleDown };
}
XrFovf toXR(MWVR::FieldOfView fov)
{
return XrFovf{ fov.angleLeft, fov.angleRight, fov.angleUp, fov.angleDown };
}
XrSpace OpenXRManagerImpl::getReferenceSpace(TrackedSpace space)
{
XrSpace referenceSpace = XR_NULL_HANDLE;
if (space == TrackedSpace::STAGE)
referenceSpace = mReferenceSpaceStage;
if (space == TrackedSpace::VIEW)
referenceSpace = mReferenceSpaceView;
return referenceSpace;
}
}

View file

@ -2,7 +2,6 @@
#define OPENXR_MANAGER_IMPL_HPP
#include "openxrmanager.hpp"
#include "openxrlayer.hpp"
#include "../mwinput/inputmanagerimp.hpp"
#include <components/debug/debuglog.hpp>
@ -42,6 +41,8 @@ namespace MWVR
XrResult CheckXrResult(XrResult res, const char* originator = nullptr, const char* sourceLocation = nullptr);
MWVR::Pose fromXR(XrPosef pose);
XrPosef toXR(MWVR::Pose pose);
MWVR::FieldOfView fromXR(XrFovf fov);
XrFovf toXR(MWVR::FieldOfView fov);
struct OpenXRManagerImpl
{
@ -55,7 +56,7 @@ namespace MWVR
const XrEventDataBaseHeader* nextEvent();
void waitFrame();
void beginFrame();
void endFrame(int64_t displayTime, class OpenXRLayerStack* layerStack);
void endFrame(int64_t displayTime, int layerCount, XrCompositionLayerBaseHeader** layerStack);
std::array<XrView, 2> getPredictedViews(int64_t predictedDisplayTime, TrackedSpace mSpace);
MWVR::Pose getPredictedLimbPose(int64_t predictedDisplayTime, TrackedLimb limb, TrackedSpace space);
int eyes();
@ -63,7 +64,7 @@ namespace MWVR
void updateControls();
void HandleSessionStateChanged(const XrEventDataSessionStateChanged& stateChangedEvent);
XrFrameState frameState();
void playerScale(MWVR::Pose& stagePose);
XrSpace getReferenceSpace(TrackedSpace space);
bool initialized = false;
long long mFrameIndex = 0;

View file

@ -1,212 +0,0 @@
#include "vrenvironment.hpp"
#include "openxrmanager.hpp"
#include "openxrmanagerimpl.hpp"
#include "openxrinputmanager.hpp"
#include "openxrswapchain.hpp"
#include "../mwinput/inputmanagerimp.hpp"
#include "../mwbase/environment.hpp"
#include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlgraphicswindow.hpp>
#include <Windows.h>
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
#include <openxr/openxr_platform_defines.h>
#include <openxr/openxr_reflection.h>
#include <osg/Camera>
#include <vector>
#include <array>
#include <iostream>
#include "openxrsession.hpp"
#include "vrgui.hpp"
#include <time.h>
namespace MWVR
{
OpenXRSession::OpenXRSession()
{
}
OpenXRSession::~OpenXRSession()
{
}
void OpenXRSession::setLayer(
OpenXRLayerStack::Layer layerType,
OpenXRLayer* layer)
{
mLayerStack.setLayer(layerType, layer);
}
void OpenXRSession::swapBuffers(osg::GraphicsContext* gc)
{
Timer timer("OpenXRSession::SwapBuffers");
auto* xr = Environment::get().getManager();
if (!mShouldRender)
return;
if (mRenderedFrames != mRenderFrame - 1)
{
Log(Debug::Warning) << "swapBuffers called out of order";
waitFrame();
return;
}
for (auto layer : mLayerStack.layerObjects())
if (layer)
layer->swapBuffers(gc);
timer.checkpoint("Rendered");
xr->endFrame(mDrawPredictedDisplayTime, &mLayerStack);
mRenderedFrames++;
}
const PoseSets& OpenXRSession::predictedPoses(PredictionSlice slice)
{
if (slice == PredictionSlice::Predraw)
waitFrame();
return mPredictedPoses[(int)slice];
}
static std::chrono::steady_clock::time_point gLast{};
void OpenXRSession::waitFrame()
{
if (mPredictedFrames < mPredictionFrame)
{
Timer timer("OpenXRSession::waitFrame");
auto* xr = Environment::get().getManager();
xr->handleEvents();
mIsRunning = xr->sessionRunning();
if (!mIsRunning)
return;
// Until OpenXR allows us to get a prediction without waiting
// we make our own (bad) prediction and let openxr wait asynchronously.
auto frameState = xr->impl().frameState();
std::thread waitThread([]{
Environment::get().getManager()->waitFrame();
});
if (mPredrawPredictedDisplayTime == 0)
{
// First time
waitThread.join();
mPredrawPredictedDisplayTime = xr->impl().frameState().predictedDisplayTime;
}
else
{
waitThread.detach();
mPredrawPredictedDisplayTime = frameState.predictedDisplayTime + frameState.predictedDisplayPeriod;
}
timer.checkpoint("waitFrame");
predictNext(0);
mPredictedFrames++;
}
}
// OSG doesn't provide API to extract euler angles from a quat, but i need it.
// Credits goes to Dennis Bunfield, i just copied his formula https://narkive.com/v0re6547.4
void getEulerAngles(const osg::Quat& quat, float& yaw, float& pitch, float& roll)
{
// Now do the computation
osg::Matrixd m2(osg::Matrixd::rotate(quat));
double* mat = (double*)m2.ptr();
double angle_x = 0.0;
double angle_y = 0.0;
double angle_z = 0.0;
double D, C, tr_x, tr_y;
angle_y = D = asin(mat[2]); /* Calculate Y-axis angle */
C = cos(angle_y);
if (fabs(C) > 0.005) /* Test for Gimball lock? */
{
tr_x = mat[10] / C; /* No, so get X-axis angle */
tr_y = -mat[6] / C;
angle_x = atan2(tr_y, tr_x);
tr_x = mat[0] / C; /* Get Z-axis angle */
tr_y = -mat[1] / C;
angle_z = atan2(tr_y, tr_x);
}
else /* Gimball lock has occurred */
{
angle_x = 0; /* Set X-axis angle to zero
*/
tr_x = mat[5]; /* And calculate Z-axis angle
*/
tr_y = mat[4];
angle_z = atan2(tr_y, tr_x);
}
yaw = angle_z;
pitch = angle_x;
roll = angle_y;
}
void OpenXRSession::movementAngles(float& yaw, float& pitch)
{
auto lhandquat = predictedPoses(PredictionSlice::Predraw).hands[(int)TrackedSpace::VIEW][(int)MWVR::Side::LEFT_HAND].orientation;
float roll = 0.f;
getEulerAngles(lhandquat, yaw, pitch, roll);
}
void OpenXRSession::advanceFrame(void)
{
auto* xr = Environment::get().getManager();
mShouldRender = mIsRunning;
if (!mIsRunning)
return;
if (mPredictedFrames != mPredictionFrame)
{
Log(Debug::Warning) << "advanceFrame() called out of order";
return;
}
// For the same reason we do waitFrame() asynchronously,
// we do beginFrame async and just blit to the swapchains later.
//std::thread([=] {
xr->beginFrame();
//}).detach();
mPredictionFrame++;
mRenderFrame++;
mPredictedPoses[(int)PredictionSlice::Draw] = mPredictedPoses[(int)PredictionSlice::Predraw];
mDrawPredictedDisplayTime = mPredrawPredictedDisplayTime;
}
void OpenXRSession::predictNext(int extraPeriods)
{
auto* xr = Environment::get().getManager();
auto* input = Environment::get().getInputManager();
PoseSets newPoses{};
newPoses.head[(int)TrackedSpace::STAGE] = xr->impl().getPredictedLimbPose(mPredrawPredictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::STAGE);
newPoses.head[(int)TrackedSpace::VIEW] = xr->impl().getPredictedLimbPose(mPredrawPredictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::VIEW);
newPoses.hands[(int)TrackedSpace::STAGE] = input->getHandPoses(mPredrawPredictedDisplayTime, TrackedSpace::STAGE);
newPoses.hands[(int)TrackedSpace::VIEW] = input->getHandPoses(mPredrawPredictedDisplayTime, TrackedSpace::VIEW);
auto stageViews = xr->impl().getPredictedViews(mPredrawPredictedDisplayTime, TrackedSpace::STAGE);
auto hmdViews = xr->impl().getPredictedViews(mPredrawPredictedDisplayTime, TrackedSpace::VIEW);
newPoses.eye[(int)TrackedSpace::STAGE][(int)Side::LEFT_HAND] = fromXR(stageViews[(int)Side::LEFT_HAND].pose);
newPoses.eye[(int)TrackedSpace::VIEW][(int)Side::LEFT_HAND] = fromXR(hmdViews[(int)Side::LEFT_HAND].pose);
newPoses.eye[(int)TrackedSpace::STAGE][(int)Side::RIGHT_HAND] = fromXR(stageViews[(int)Side::RIGHT_HAND].pose);
newPoses.eye[(int)TrackedSpace::VIEW][(int)Side::RIGHT_HAND] = fromXR(hmdViews[(int)Side::RIGHT_HAND].pose);
mPredictedPoses[(int)PredictionSlice::Predraw] = newPoses;
}
}
std::ostream& operator <<(
std::ostream& os,
const MWVR::Pose& pose)
{
os << "position=" << pose.position << " orientation=" << pose.orientation << " velocity=" << pose.velocity;
return os;
}

View file

@ -1,76 +0,0 @@
#ifndef MWVR_OPENRXSESSION_H
#define MWVR_OPENRXSESSION_H
#include <memory>
#include <mutex>
#include <array>
#include <chrono>
#include <deque>
#include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlgraphicswindow.hpp>
#include <components/settings/settings.hpp>
#include "openxrmanager.hpp"
#include "openxrlayer.hpp"
namespace MWVR
{
extern void getEulerAngles(const osg::Quat& quat, float& yaw, float& pitch, float& roll);
class OpenXRSession
{
public:
using seconds = std::chrono::duration<double>;
using nanoseconds = std::chrono::nanoseconds;
using clock = std::chrono::steady_clock;
using time_point = clock::time_point;
enum class PredictionSlice
{
Predraw = 0, //!< Get poses predicted for the next frame to be drawn
Draw = 1, //!< Get poses predicted for the current rendering
NumSlices
};
public:
OpenXRSession();
~OpenXRSession();
void setLayer(OpenXRLayerStack::Layer layerType, OpenXRLayer* layer);
void swapBuffers(osg::GraphicsContext* gc);
const PoseSets& predictedPoses(PredictionSlice slice);
//! Call before updating poses and other inputs
void waitFrame();
//! Update predictions
void predictNext(int extraPeriods);
//! Angles to be used for overriding movement direction
void movementAngles(float& yaw, float& pitch);
void advanceFrame(void);
bool isRunning() { return mIsRunning; }
bool shouldRender() { return mShouldRender; }
OpenXRLayerStack mLayerStack{};
PoseSets mPredictedPoses[(int)PredictionSlice::NumSlices]{};
int mRenderFrame{ 0 };
int mRenderedFrames{ 0 };
int mPredictionFrame{ 1 };
int mPredictedFrames{ 0 };
long long mPredrawPredictedDisplayTime{ 0 };
long long mDrawPredictedDisplayTime{ 0 };
bool mIsRunning{ false };
bool mShouldRender{ false };
};
}
#endif

View file

@ -1,6 +1,7 @@
#include "openxrswapchain.hpp"
#include "openxrmanager.hpp"
#include "openxrmanagerimpl.hpp"
#include "vrenvironment.hpp"
#include <components/debug/debuglog.hpp>
@ -23,13 +24,12 @@ namespace MWVR {
SUBVIEW_MAX = RIGHT_VIEW, //!< Used to size subview arrays. Not a valid input.
};
OpenXRSwapchainImpl(osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, OpenXRSwapchain::Config config);
OpenXRSwapchainImpl(osg::ref_ptr<osg::State> state, OpenXRSwapchain::Config config);
~OpenXRSwapchainImpl();
void beginFrame(osg::GraphicsContext* gc);
int endFrame(osg::GraphicsContext* gc);
osg::ref_ptr<OpenXRManager> mXR;
XrSwapchain mSwapchain = XR_NULL_HANDLE;
std::vector<XrSwapchainImageOpenGLKHR> mSwapchainImageBuffers{};
//std::vector<osg::ref_ptr<VRTexture> > mTextureBuffers{};
@ -42,10 +42,8 @@ namespace MWVR {
VRTexture* mRenderBuffer = nullptr;
};
OpenXRSwapchainImpl::OpenXRSwapchainImpl(
osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, OpenXRSwapchain::Config config)
: mXR(XR)
, mWidth(config.width)
OpenXRSwapchainImpl::OpenXRSwapchainImpl(osg::ref_ptr<osg::State> state, OpenXRSwapchain::Config config)
: mWidth(config.width)
, mHeight(config.height)
, mSamples(config.samples)
{
@ -56,13 +54,13 @@ namespace MWVR {
if (mSamples <= 0)
throw std::invalid_argument("Samples must be a positive integer");
auto& pXR = mXR->impl();
auto* xr = Environment::get().getManager();
// Select a swapchain format.
uint32_t swapchainFormatCount;
CHECK_XRCMD(xrEnumerateSwapchainFormats(pXR.mSession, 0, &swapchainFormatCount, nullptr));
CHECK_XRCMD(xrEnumerateSwapchainFormats(xr->impl().mSession, 0, &swapchainFormatCount, nullptr));
std::vector<int64_t> swapchainFormats(swapchainFormatCount);
CHECK_XRCMD(xrEnumerateSwapchainFormats(pXR.mSession, (uint32_t)swapchainFormats.size(), &swapchainFormatCount, swapchainFormats.data()));
CHECK_XRCMD(xrEnumerateSwapchainFormats(xr->impl().mSession, (uint32_t)swapchainFormats.size(), &swapchainFormatCount, swapchainFormats.data()));
// List of supported color swapchain formats.
constexpr int64_t SupportedColorSwapchainFormats[] = {
@ -91,7 +89,7 @@ namespace MWVR {
swapchainCreateInfo.faceCount = 1;
swapchainCreateInfo.sampleCount = mSamples;
swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
CHECK_XRCMD(xrCreateSwapchain(pXR.mSession, &swapchainCreateInfo, &mSwapchain));
CHECK_XRCMD(xrCreateSwapchain(xr->impl().mSession, &swapchainCreateInfo, &mSwapchain));
uint32_t imageCount = 0;
CHECK_XRCMD(xrEnumerateSwapchainImages(mSwapchain, 0, &imageCount, nullptr));
@ -140,9 +138,8 @@ namespace MWVR {
return swapchainImageIndex;
}
OpenXRSwapchain::OpenXRSwapchain(
osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, OpenXRSwapchain::Config config)
: mPrivate(new OpenXRSwapchainImpl(XR, state, config))
OpenXRSwapchain::OpenXRSwapchain(osg::ref_ptr<osg::State> state, OpenXRSwapchain::Config config)
: mPrivate(new OpenXRSwapchainImpl(state, config))
{
}

View file

@ -21,7 +21,7 @@ namespace MWVR
};
public:
OpenXRSwapchain(osg::ref_ptr<OpenXRManager> XR, osg::ref_ptr<osg::State> state, Config config);
OpenXRSwapchain(osg::ref_ptr<osg::State> state, Config config);
~OpenXRSwapchain();
public:

View file

@ -1,84 +0,0 @@
#include "openxrview.hpp"
#include "openxrmanager.hpp"
#include "openxrmanagerimpl.hpp"
#include "../mwinput/inputmanagerimp.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwrender/renderingmanager.hpp"
#include "../mwrender/water.hpp"
#include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlgraphicswindow.hpp>
#include <Windows.h>
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
#include <openxr/openxr_platform_defines.h>
#include <openxr/openxr_reflection.h>
namespace MWVR {
OpenXRView::OpenXRView(
osg::ref_ptr<OpenXRManager> XR,
std::string name,
OpenXRSwapchain::Config config,
osg::ref_ptr<osg::State> state)
: mXR(XR)
, mSwapchainConfig{ config }
, mSwapchain(new OpenXRSwapchain(mXR, state, mSwapchainConfig))
, mName(name)
, mTimer(mName.c_str())
{
}
OpenXRView::~OpenXRView()
{
}
osg::Camera* OpenXRView::createCamera(int eye, const osg::Vec4& clearColor, osg::GraphicsContext* gc)
{
osg::ref_ptr<osg::Camera> camera = new osg::Camera();
camera->setClearColor(clearColor);
camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
camera->setRenderOrder(osg::Camera::PRE_RENDER, eye + 2);
camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
camera->setAllowEventFocus(false);
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
camera->setViewport(0, 0, mSwapchain->width(), mSwapchain->height());
camera->setGraphicsContext(gc);
camera->setInitialDrawCallback(new OpenXRView::InitialDrawCallback());
camera->setFinalDrawCallback(new OpenXRView::FinalDrawCallback());
return camera.release();
}
void OpenXRView::prerenderCallback(osg::RenderInfo& renderInfo)
{
if (mSwapchain)
{
mSwapchain->beginFrame(renderInfo.getState()->getGraphicsContext());
}
mTimer.checkpoint("Prerender");
}
void OpenXRView::postrenderCallback(osg::RenderInfo& renderInfo)
{
auto name = renderInfo.getCurrentCamera()->getName();
mTimer.checkpoint("Postrender");
}
void OpenXRView::swapBuffers(osg::GraphicsContext* gc)
{
swapchain().endFrame(gc);
}
void OpenXRView::setPredictedPose(const Pose& pose)
{
mPredictedPose = pose;
};
}

View file

@ -1,188 +0,0 @@
#include "openxrworldview.hpp"
#include "vrenvironment.hpp"
#include "openxrmanager.hpp"
#include "openxrmanagerimpl.hpp"
#include "../mwinput/inputmanagerimp.hpp"
#include "openxrinputmanager.hpp"
#include <components/debug/debuglog.hpp>
#include <Windows.h>
#include <openxr/openxr.h>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwrender/renderingmanager.hpp"
#include "../mwrender/camera.hpp"
#include <osg/Camera>
#include <osgViewer/Renderer>
#include <vector>
#include <array>
#include <iostream>
namespace MWVR
{
// Some headers like to define these.
#ifdef near
#undef near
#endif
#ifdef far
#undef far
#endif
static osg::Matrix
perspectiveFovMatrix(float near, float far, XrFovf fov)
{
const float tanLeft = tanf(fov.angleLeft);
const float tanRight = tanf(fov.angleRight);
const float tanDown = tanf(fov.angleDown);
const float tanUp = tanf(fov.angleUp);
const float tanWidth = tanRight - tanLeft;
const float tanHeight = tanUp - tanDown;
const float offset = near;
float matrix[16] = {};
matrix[0] = 2 / tanWidth;
matrix[4] = 0;
matrix[8] = (tanRight + tanLeft) / tanWidth;
matrix[12] = 0;
matrix[1] = 0;
matrix[5] = 2 / tanHeight;
matrix[9] = (tanUp + tanDown) / tanHeight;
matrix[13] = 0;
if (far <= near) {
matrix[2] = 0;
matrix[6] = 0;
matrix[10] = -1;
matrix[14] = -(near + offset);
}
else {
matrix[2] = 0;
matrix[6] = 0;
matrix[10] = -(far + offset) / (far - near);
matrix[14] = -(far * (near + offset)) / (far - near);
}
matrix[3] = 0;
matrix[7] = 0;
matrix[11] = -1;
matrix[15] = 0;
return osg::Matrix(matrix);
}
osg::Matrix OpenXRWorldView::projectionMatrix()
{
// TODO: Get this from the session predictions instead
auto hmdViews = mXR->impl().getPredictedViews(mXR->impl().frameState().predictedDisplayTime, TrackedSpace::STAGE);
float near = Settings::Manager::getFloat("near clip", "Camera");
float far = Settings::Manager::getFloat("viewing distance", "Camera");
//return perspectiveFovMatrix()
if(mName == "LeftEye")
return perspectiveFovMatrix(near, far, hmdViews[0].fov);
return perspectiveFovMatrix(near, far, hmdViews[1].fov);
}
osg::Matrix OpenXRWorldView::viewMatrix()
{
MWVR::Pose pose = predictedPose();
mXR->playerScale(pose);
osg::Vec3 position = pose.position * Environment::get().unitsPerMeter();
osg::Quat orientation = pose.orientation;
float y = position.y();
float z = position.z();
position.y() = z;
position.z() = -y;
y = orientation.y();
z = orientation.z();
orientation.y() = z;
orientation.z() = -y;
osg::Matrix viewMatrix;
viewMatrix.setTrans(-position);
viewMatrix.postMultRotate(orientation.conj());
return viewMatrix;
}
OpenXRWorldView::OpenXRWorldView(
osg::ref_ptr<OpenXRManager> XR,
std::string name,
osg::ref_ptr<osg::State> state,
OpenXRSwapchain::Config config)
: OpenXRView(XR, name, config, state)
{
}
OpenXRWorldView::~OpenXRWorldView()
{
}
void OpenXRWorldView::InitialDrawCallback::operator()(osg::RenderInfo& renderInfo) const
{
osg::GraphicsOperation* graphicsOperation = renderInfo.getCurrentCamera()->getRenderer();
osgViewer::Renderer* renderer = dynamic_cast<osgViewer::Renderer*>(graphicsOperation);
if (renderer != nullptr)
{
// Disable normal OSG FBO camera setup because it will undo the MSAA FBO configuration.
renderer->setCameraRequiresSetUp(false);
}
auto name = renderInfo.getCurrentCamera()->getName();
}
// TODO: This would have been useful but OSG never calls it.
void OpenXRWorldView::FinalDrawCallback::operator()(osg::RenderInfo& renderInfo) const
{
}
void
OpenXRWorldView::UpdateSlaveCallback::updateSlave(
osg::View& view,
osg::View::Slave& slave)
{
mView->mTimer.checkpoint("UpdateSlave");
auto* camera = slave._camera.get();
auto name = camera->getName();
int side = 0;
if (name == "LeftEye")
{
side = (int)Side::LEFT_HAND;
}
else
{
side = (int)Side::RIGHT_HAND;
}
auto& poses = mSession->predictedPoses(OpenXRSession::PredictionSlice::Predraw);
Pose viewPose{};
Pose stagePose{};
stagePose = poses.eye[(int)TrackedSpace::STAGE][side];
viewPose = poses.eye[(int)TrackedSpace::VIEW][side];
mView->setPredictedPose(viewPose);
auto viewMatrix = view.getCamera()->getViewMatrix();
auto modifiedViewMatrix = viewMatrix * mView->viewMatrix();
auto projectionMatrix = mView->projectionMatrix();
camera->setViewMatrix(modifiedViewMatrix);
camera->setProjectionMatrix(projectionMatrix);
slave.updateSlaveImplementation(view);
}
}

View file

@ -1,39 +0,0 @@
#ifndef OPENXRWORLDVIEW_HPP
#define OPENXRWORLDVIEW_HPP
#include "openxrview.hpp"
#include "openxrsession.hpp"
namespace MWVR
{
class OpenXRWorldView : public OpenXRView
{
public:
class UpdateSlaveCallback : public osg::View::Slave::UpdateSlaveCallback
{
public:
UpdateSlaveCallback(osg::ref_ptr<OpenXRManager> XR, OpenXRSession* session, osg::ref_ptr<OpenXRWorldView> view, osg::GraphicsContext* gc)
: mXR(XR), mSession(session), mView(view), mGC(gc)
{}
void updateSlave(osg::View& view, osg::View::Slave& slave) override;
private:
osg::ref_ptr<OpenXRManager> mXR;
OpenXRSession* mSession;
osg::ref_ptr<OpenXRWorldView> mView;
osg::ref_ptr<osg::GraphicsContext> mGC;
};
public:
OpenXRWorldView(osg::ref_ptr<OpenXRManager> XR, std::string name, osg::ref_ptr<osg::State> state, OpenXRSwapchain::Config config);
~OpenXRWorldView();
//! Projection offset for this view
osg::Matrix projectionMatrix();
//! View offset for this view
osg::Matrix viewMatrix();
};
}
#endif

View file

@ -103,8 +103,8 @@ void StateMachine::update(float dt, bool enabled)
{
auto* session = Environment::get().getSession();
auto* world = MWBase::Environment::get().getWorld();
auto& predictedPoses = session->predictedPoses(OpenXRSession::PredictionSlice::Predraw);
auto& handPose = predictedPoses.hands[(int)MWVR::TrackedSpace::STAGE][(int)MWVR::Side::RIGHT_HAND];
auto& predictedPoses = session->predictedPoses(VRSession::FramePhase::Predraw);
auto& handPose = predictedPoses.hands[(int)MWVR::Side::RIGHT_HAND];
auto weaponType = world->getActiveWeaponType();
enabled = enabled && isMeleeWeapon(weaponType);

View file

@ -8,7 +8,7 @@
#include "../mwworld/class.hpp"
#include "vrenvironment.hpp"
#include "openxrsession.hpp"
#include "vrsession.hpp"
namespace MWVR { namespace RealisticCombat {
enum SwingState

View file

@ -59,7 +59,7 @@
#include "../mwphysics/physicssystem.hpp"
#include "vrenvironment.hpp"
#include "openxrviewer.hpp"
#include "vrviewer.hpp"
#include "openxrinputmanager.hpp"
namespace MWVR
@ -102,11 +102,8 @@ void ForearmController::operator()(osg::Node* node, osg::NodeVisitor* nv)
osg::MatrixTransform* transform = static_cast<osg::MatrixTransform*>(node);
auto* xr = Environment::get().getManager();
auto* session = Environment::get().getSession();
auto* xrViewer = Environment::get().getViewer();
auto& poses = session->predictedPoses(OpenXRSession::PredictionSlice::Predraw);
auto handPosesStage = poses.hands[(int)TrackedSpace::STAGE];
int side = (int)Side::RIGHT_HAND;
if (node->getName().find_first_of("L") != std::string::npos)
{
@ -117,10 +114,8 @@ void ForearmController::operator()(osg::Node* node, osg::NodeVisitor* nv)
MWBase::Environment::get().getWorld()->getRenderingManager().getCamera()->updateCamera();
}
MWVR::Pose handStage = handPosesStage[side];
MWVR::Pose headStage = poses.head[(int)TrackedSpace::STAGE];
xr->playerScale(handStage);
xr->playerScale(headStage);
MWVR::Pose handStage = session->predictedPoses(VRSession::FramePhase::Predraw).hands[side];
MWVR::Pose headStage = session->predictedPoses(VRSession::FramePhase::Predraw).head;
auto orientation = handStage.orientation;
@ -426,7 +421,7 @@ void WeaponPointerController::operator()(osg::Node* node, osg::NodeVisitor* nv)
VRAnimation::VRAnimation(
const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem,
bool disableSounds, std::shared_ptr<OpenXRSession> xrSession)
bool disableSounds, std::shared_ptr<VRSession> xrSession)
// Note that i let it construct as 3rd person and then later update it to VM_VRHeadless
// when the character controller updates
: MWRender::NpcAnimation(ptr, parentNode, resourceSystem, disableSounds, VM_Normal, 55.f)
@ -516,6 +511,20 @@ void VRAnimation::updateParts()
removeIndividualPart(ESM::PartReferenceType::PRT_RForearm);
removeIndividualPart(ESM::PartReferenceType::PRT_RWrist);
}
auto playerPtr = MWMechanics::getPlayer();
const MWWorld::LiveCellRef<ESM::NPC>* ref = playerPtr.get<ESM::NPC>();
const ESM::Race* race =
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 charHeight = charHeightBase * charHeightFactor;
// TODO: Player height should be configurable
// For now i'm just using my own
float sizeFactor = 1.85f / charHeight;
Environment::get().getSession()->setPlayerScale(sizeFactor);
}
void VRAnimation::setPointForward(bool enabled)

View file

@ -4,7 +4,7 @@
#include "../mwrender/npcanimation.hpp"
#include "../mwrender/renderingmanager.hpp"
#include "openxrmanager.hpp"
#include "openxrsession.hpp"
#include "vrsession.hpp"
namespace MWVR
{
@ -30,7 +30,7 @@ public:
* @param xrSession The XR session that shall be used to track limbs
*/
VRAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem,
bool disableSounds, std::shared_ptr<OpenXRSession> xrSession );
bool disableSounds, std::shared_ptr<VRSession> xrSession );
virtual ~VRAnimation();
/// Overridden to always be false
@ -74,7 +74,7 @@ protected:
float getVelocity(const std::string& groupname) const override;
public:
std::shared_ptr<OpenXRSession> mSession;
std::shared_ptr<VRSession> mSession;
osg::ref_ptr<ForearmController> mForearmControllers[2];
osg::ref_ptr<HandController> mHandControllers[2];
osg::ref_ptr<FingerController> mIndexFingerControllers[2];

View file

@ -4,7 +4,7 @@
#include "vranimation.hpp"
#include "openxrinputmanager.hpp"
#include "openxrsession.hpp"
#include "vrsession.hpp"
#include "vrgui.hpp"
#include "../mwbase/environment.hpp"
@ -55,12 +55,12 @@ MWVR::OpenXRInputManager* MWVR::Environment::getInputManager() const
return xrInputManager;
}
MWVR::OpenXRSession* MWVR::Environment::getSession() const
MWVR::VRSession* MWVR::Environment::getSession() const
{
return mSession;
}
void MWVR::Environment::setSession(MWVR::OpenXRSession* xrSession)
void MWVR::Environment::setSession(MWVR::VRSession* xrSession)
{
mSession = xrSession;
}
@ -86,12 +86,12 @@ void MWVR::Environment::setPlayerAnimation(MWVR::VRAnimation* xrAnimation)
}
MWVR::OpenXRViewer* MWVR::Environment::getViewer() const
MWVR::VRViewer* MWVR::Environment::getViewer() const
{
return mViewer;
}
void MWVR::Environment::setViewer(MWVR::OpenXRViewer* xrViewer)
void MWVR::Environment::setViewer(MWVR::VRViewer* xrViewer)
{
mViewer = xrViewer;
}

View file

@ -5,9 +5,9 @@ namespace MWVR
{
class VRAnimation;
class OpenXRInputManager;
class OpenXRSession;
class VRSession;
class VRGUIManager;
class OpenXRViewer;
class VRViewer;
class OpenXRManager;
/// \brief Central hub for mw openxr subsystems
@ -51,11 +51,11 @@ namespace MWVR
MWVR::VRAnimation* getPlayerAnimation() const;
void setPlayerAnimation(MWVR::VRAnimation* xrAnimation);
MWVR::OpenXRSession* getSession() const;
void setSession(MWVR::OpenXRSession* xrSession);
MWVR::VRSession* getSession() const;
void setSession(MWVR::VRSession* xrSession);
MWVR::OpenXRViewer* getViewer() const;
void setViewer(MWVR::OpenXRViewer* xrViewer);
MWVR::VRViewer* getViewer() const;
void setViewer(MWVR::VRViewer* xrViewer);
MWVR::OpenXRManager* getManager() const;
void setManager(MWVR::OpenXRManager* xrManager);
@ -64,10 +64,10 @@ namespace MWVR
void setUnitsPerMeter(float unitsPerMeter);
private:
MWVR::OpenXRSession* mSession{ nullptr };
MWVR::VRSession* mSession{ nullptr };
MWVR::VRGUIManager* mGUIManager{ nullptr };
MWVR::VRAnimation* mPlayerAnimation{ nullptr };
MWVR::OpenXRViewer* mViewer{ nullptr };
MWVR::VRViewer* mViewer{ nullptr };
MWVR::OpenXRManager* mOpenXRManager{ nullptr };
float mUnitsPerMeter{ 1.f };
};

View file

@ -1,6 +1,6 @@
#include "vrgui.hpp"
#include "vrenvironment.hpp"
#include "openxrsession.hpp"
#include "vrsession.hpp"
#include "openxrmanagerimpl.hpp"
#include "openxrinputmanager.hpp"
#include "vranimation.hpp"
@ -182,6 +182,8 @@ VRGUILayer::VRGUILayer(
mGUICamera = new GUICamera(config.pixelResolution.x(), config.pixelResolution.y(), config.backgroundColor);
osgMyGUI::RenderManager& renderManager = static_cast<osgMyGUI::RenderManager&>(MyGUI::RenderManager::getInstance());
mMyGUICamera = renderManager.createGUICamera(osg::Camera::NESTED_RENDER, filter);
//myGUICamera->setViewport(0, 0, 256, 256);
//mMyGUICamera->setProjectionMatrixAsOrtho2D(-1, 1, -1, 1);
mGUICamera->setScene(mMyGUICamera);
// Define state set that allows rendering with transparency
@ -543,7 +545,7 @@ void VRGUIManager::updateSideBySideLayers()
float span = sSideBySideAzimuthInterval * (n - 1); // zero index, places lone layers straight ahead
float low = -span / 2;
for (int i = 0; i < mSideBySideLayers.size(); i++)
for (unsigned i = 0; i < mSideBySideLayers.size(); i++)
mSideBySideLayers[i]->setAngle(low + static_cast<float>(i) * sSideBySideAzimuthInterval);
}

View file

@ -12,8 +12,8 @@
#include <osg/Camera>
#include <osg/PositionAttitudeTransform>
#include "openxrview.hpp"
#include "openxrlayer.hpp"
#include "vrview.hpp"
#include "openxrmanager.hpp"
namespace MyGUI
{

View file

@ -0,0 +1,271 @@
#include "vrenvironment.hpp"
#include "openxrmanager.hpp"
#include "openxrmanagerimpl.hpp"
#include "openxrinputmanager.hpp"
#include "openxrswapchain.hpp"
#include "../mwinput/inputmanagerimp.hpp"
#include "../mwbase/environment.hpp"
#include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlgraphicswindow.hpp>
#include <Windows.h>
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
#include <openxr/openxr_platform_defines.h>
#include <openxr/openxr_reflection.h>
#include <osg/Camera>
#include <vector>
#include <array>
#include <iostream>
#include "vrsession.hpp"
#include "vrgui.hpp"
#include <time.h>
namespace MWVR
{
VRSession::VRSession()
{
}
VRSession::~VRSession()
{
}
osg::Matrix VRSession::projectionMatrix(FramePhase phase, Side side)
{
assert(((int)side) < 2);
auto fov = predictedPoses(VRSession::FramePhase::Predraw).view[(int)side].fov;
float near_ = Settings::Manager::getFloat("near clip", "Camera");
float far_ = Settings::Manager::getFloat("viewing distance", "Camera");
return fov.perspectiveMatrix(near_, far_);
}
osg::Matrix VRSession::viewMatrix(FramePhase phase, Side side)
{
MWVR::Pose pose = predictedPoses(VRSession::FramePhase::Predraw).view[(int)side].pose;
osg::Vec3 position = pose.position * Environment::get().unitsPerMeter();
osg::Quat orientation = pose.orientation;
float y = position.y();
float z = position.z();
position.y() = z;
position.z() = -y;
y = orientation.y();
z = orientation.z();
orientation.y() = z;
orientation.z() = -y;
osg::Matrix viewMatrix;
viewMatrix.setTrans(-position);
viewMatrix.postMultRotate(orientation.conj());
return viewMatrix;
}
bool VRSession::isRunning() const {
auto* xr = Environment::get().getManager();
return xr->sessionRunning();
}
void VRSession::swapBuffers(osg::GraphicsContext* gc, VRViewer& viewer)
{
Timer timer("VRSession::SwapBuffers");
auto* xr = Environment::get().getManager();
{
std::unique_lock<std::mutex> lock(mMutex);
xr->handleEvents();
mPostdrawFrame = std::move(mDrawFrame);
assert(mPostdrawFrame);
}
// TODO: Should blit mirror texture regardless
if (!isRunning())
return;
xr->waitFrame();
xr->beginFrame();
viewer.swapBuffers(gc);
auto leftView = viewer.mViews["LeftEye"];
auto rightView = viewer.mViews["RightEye"];
leftView->swapBuffers(gc);
rightView->swapBuffers(gc);
std::array<XrCompositionLayerProjectionView, 2> compositionLayerProjectionViews{};
compositionLayerProjectionViews[0].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
compositionLayerProjectionViews[1].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
compositionLayerProjectionViews[0].subImage = leftView->swapchain().subImage();
compositionLayerProjectionViews[1].subImage = rightView->swapchain().subImage();
compositionLayerProjectionViews[0].pose = toXR(mPostdrawFrame->mPredictedPoses.eye[(int)Side::LEFT_HAND]);
compositionLayerProjectionViews[1].pose = toXR(mPostdrawFrame->mPredictedPoses.eye[(int)Side::RIGHT_HAND]);
compositionLayerProjectionViews[0].fov = toXR(mPostdrawFrame->mPredictedPoses.view[(int)Side::LEFT_HAND].fov);
compositionLayerProjectionViews[1].fov = toXR(mPostdrawFrame->mPredictedPoses.view[(int)Side::RIGHT_HAND].fov);
XrCompositionLayerProjection layer{};
layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION;
layer.space = xr->impl().getReferenceSpace(TrackedSpace::STAGE);
layer.viewCount = 2;
layer.views = compositionLayerProjectionViews.data();
auto* layerStack = reinterpret_cast<XrCompositionLayerBaseHeader*>(&layer);
xr->endFrame(mPostdrawFrame->mPredictedDisplayTime, 1, &layerStack);
mLastRenderedFrame = mPostdrawFrame->mFrameNo;
}
void VRSession::advanceFramePhase(void)
{
std::unique_lock<std::mutex> lock(mMutex);
Timer timer("VRSession::advanceFrame");
assert(!mDrawFrame);
mDrawFrame = std::move(mPredrawFrame);
}
void VRSession::startFrame()
{
std::unique_lock<std::mutex> lock(mMutex);
assert(!mPredrawFrame);
Timer timer("VRSession::startFrame");
auto* xr = Environment::get().getManager();
auto* input = Environment::get().getInputManager();
// Until OpenXR allows us to get a prediction without waiting
// we make our own (bad) prediction and let openxr wait
auto frameState = xr->impl().frameState();
long long predictedDisplayTime = 0;
if (mFrames == mLastRenderedFrame)
{
predictedDisplayTime = frameState.predictedDisplayTime;
}
else if (mFrames > mLastRenderedFrame)
{
predictedDisplayTime = frameState.predictedDisplayTime + frameState.predictedDisplayPeriod;
}
if (mFrames == 0 || predictedDisplayTime == 0)
{
// First time, need to populate the frame state struct
predictedDisplayTime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
}
PoseSet predictedPoses{};
if (isRunning())
{
predictedPoses.head = xr->impl().getPredictedLimbPose(predictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::STAGE) * mPlayerScale;
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;
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;
}
else
{
// If session is not running, copy poses from the last frame
if (mPostdrawFrame)
predictedPoses = mPostdrawFrame->mPredictedPoses;
}
mFrames++;
mPredrawFrame.reset(new VRFrame);
mPredrawFrame->mPredictedDisplayTime = predictedDisplayTime;
mPredrawFrame->mFrameNo = mFrames;
mPredrawFrame->mPredictedPoses = predictedPoses;
}
const PoseSet& VRSession::predictedPoses(FramePhase phase)
{
switch (phase)
{
case FramePhase::Predraw:
if (!mPredrawFrame)
startFrame();
assert(mPredrawFrame);
return mPredrawFrame->mPredictedPoses;
case FramePhase::Draw:
assert(mDrawFrame);
return mDrawFrame->mPredictedPoses;
case FramePhase::Postdraw:
assert(mPostdrawFrame);
return mPostdrawFrame->mPredictedPoses;
}
assert(0);
return mPredrawFrame->mPredictedPoses;
}
// OSG doesn't provide API to extract euler angles from a quat, but i need it.
// Credits goes to Dennis Bunfield, i just copied his formula https://narkive.com/v0re6547.4
void getEulerAngles(const osg::Quat& quat, float& yaw, float& pitch, float& roll)
{
// Now do the computation
osg::Matrixd m2(osg::Matrixd::rotate(quat));
double* mat = (double*)m2.ptr();
double angle_x = 0.0;
double angle_y = 0.0;
double angle_z = 0.0;
double D, C, tr_x, tr_y;
angle_y = D = asin(mat[2]); /* Calculate Y-axis angle */
C = cos(angle_y);
if (fabs(C) > 0.005) /* Test for Gimball lock? */
{
tr_x = mat[10] / C; /* No, so get X-axis angle */
tr_y = -mat[6] / C;
angle_x = atan2(tr_y, tr_x);
tr_x = mat[0] / C; /* Get Z-axis angle */
tr_y = -mat[1] / C;
angle_z = atan2(tr_y, tr_x);
}
else /* Gimball lock has occurred */
{
angle_x = 0; /* Set X-axis angle to zero
*/
tr_x = mat[5]; /* And calculate Z-axis angle
*/
tr_y = mat[4];
angle_z = atan2(tr_y, tr_x);
}
yaw = angle_z;
pitch = angle_x;
roll = angle_y;
}
void VRSession::movementAngles(float& yaw, float& pitch)
{
assert(mPredrawFrame);
auto* input = Environment::get().getInputManager();
auto lhandquat = input->getHandPose(mPredrawFrame->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);
}
}
std::ostream& operator <<(
std::ostream& os,
const MWVR::Pose& pose)
{
os << "position=" << pose.position << " orientation=" << pose.orientation << " velocity=" << pose.velocity;
return os;
}

View file

@ -0,0 +1,81 @@
#ifndef MWVR_OPENRXSESSION_H
#define MWVR_OPENRXSESSION_H
#include <memory>
#include <mutex>
#include <array>
#include <chrono>
#include <queue>
#include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlgraphicswindow.hpp>
#include <components/settings/settings.hpp>
#include "openxrmanager.hpp"
#include "vrviewer.hpp"
namespace MWVR
{
extern void getEulerAngles(const osg::Quat& quat, float& yaw, float& pitch, float& roll);
class VRSession
{
public:
using seconds = std::chrono::duration<double>;
using nanoseconds = std::chrono::nanoseconds;
using clock = std::chrono::steady_clock;
using time_point = clock::time_point;
enum class FramePhase
{
Predraw = 0, //!< Get poses predicted for the next frame to be drawn
Draw = 1, //!< Get poses predicted for the current rendering frame
Postdraw = 2, //!< Get poses predicted for the last rendered frame
};
struct VRFrame
{
long long mFrameNo{ 0 };
long long mPredictedDisplayTime{ 0 };
PoseSet mPredictedPoses{};
};
public:
VRSession();
~VRSession();
void swapBuffers(osg::GraphicsContext* gc, VRViewer& viewer);
const PoseSet& predictedPoses(FramePhase phase);
//! Starts a new frame
void startFrame();
//! Angles to be used for overriding movement direction
void movementAngles(float& yaw, float& pitch);
void advanceFramePhase(void);
bool isRunning() const;
float playerScale() const { return mPlayerScale; }
float setPlayerScale(float scale) { return mPlayerScale = scale; }
osg::Matrix viewMatrix(FramePhase phase, Side side);
osg::Matrix projectionMatrix(FramePhase phase, Side side);
std::unique_ptr<VRFrame> mPredrawFrame{ nullptr };
std::unique_ptr<VRFrame> mDrawFrame{ nullptr };
std::unique_ptr<VRFrame> mPostdrawFrame{ nullptr };
std::mutex mMutex{};
std::condition_variable mCondition{};
long long mFrames{ 0 };
long long mLastRenderedFrame{ 0 };
float mPlayerScale{ 1.f };
};
}
#endif

View file

@ -1,4 +1,4 @@
#include "openxrviewer.hpp"
#include "vrviewer.hpp"
#include "vrtexture.hpp"
#include <osg/Texture2D>
#include <osgViewer/Renderer>

114
apps/openmw/mwvr/vrview.cpp Normal file
View file

@ -0,0 +1,114 @@
#include "vrview.hpp"
#include "vrsession.hpp"
#include "openxrmanager.hpp"
#include "openxrmanagerimpl.hpp"
#include "../mwinput/inputmanagerimp.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwrender/renderingmanager.hpp"
#include "../mwrender/water.hpp"
#include "vrenvironment.hpp"
#include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlgraphicswindow.hpp>
#include <Windows.h>
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
#include <openxr/openxr_platform_defines.h>
#include <openxr/openxr_reflection.h>
#include <osgViewer/Renderer>
namespace MWVR {
VRView::VRView(
std::string name,
OpenXRSwapchain::Config config,
osg::ref_ptr<osg::State> state)
: mSwapchainConfig{ config }
, mSwapchain(new OpenXRSwapchain(state, mSwapchainConfig))
, mName(name)
, mTimer(mName.c_str())
{
}
VRView::~VRView()
{
}
osg::Camera* VRView::createCamera(int eye, const osg::Vec4& clearColor, osg::GraphicsContext* gc)
{
osg::ref_ptr<osg::Camera> camera = new osg::Camera();
camera->setClearColor(clearColor);
camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
camera->setRenderOrder(osg::Camera::PRE_RENDER, eye + 2);
camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
camera->setAllowEventFocus(false);
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
camera->setViewport(0, 0, mSwapchain->width(), mSwapchain->height());
camera->setGraphicsContext(gc);
camera->setInitialDrawCallback(new VRView::InitialDrawCallback());
return camera.release();
}
void VRView::prerenderCallback(osg::RenderInfo& renderInfo)
{
if (mSwapchain)
{
mSwapchain->beginFrame(renderInfo.getState()->getGraphicsContext());
}
mTimer.checkpoint("Prerender");
}
void VRView::InitialDrawCallback::operator()(osg::RenderInfo& renderInfo) const
{
osg::GraphicsOperation* graphicsOperation = renderInfo.getCurrentCamera()->getRenderer();
osgViewer::Renderer* renderer = dynamic_cast<osgViewer::Renderer*>(graphicsOperation);
if (renderer != nullptr)
{
// Disable normal OSG FBO camera setup
renderer->setCameraRequiresSetUp(false);
}
auto name = renderInfo.getCurrentCamera()->getName();
}
void VRView::UpdateSlaveCallback::updateSlave(
osg::View& view,
osg::View::Slave& slave)
{
mView->mTimer.checkpoint("UpdateSlave");
auto* camera = slave._camera.get();
auto name = camera->getName();
Side side = Side::RIGHT_HAND;
if (name == "LeftEye")
side = Side::LEFT_HAND;
auto* session = Environment::get().getSession();
auto viewMatrix = view.getCamera()->getViewMatrix();
auto modifiedViewMatrix = viewMatrix * session->viewMatrix(VRSession::FramePhase::Predraw, side);
auto projectionMatrix = session->projectionMatrix(VRSession::FramePhase::Predraw, side);
camera->setViewMatrix(modifiedViewMatrix);
camera->setProjectionMatrix(projectionMatrix);
slave.updateSlaveImplementation(view);
}
void VRView::postrenderCallback(osg::RenderInfo& renderInfo)
{
auto name = renderInfo.getCurrentCamera()->getName();
mTimer.checkpoint("Postrender");
}
void VRView::swapBuffers(osg::GraphicsContext* gc)
{
swapchain().endFrame(gc);
}
}

View file

@ -10,7 +10,7 @@ struct XrSwapchainSubImage;
namespace MWVR
{
class OpenXRView : public osg::Referenced
class VRView : public osg::Referenced
{
public:
@ -19,15 +19,25 @@ namespace MWVR
public:
virtual void operator()(osg::RenderInfo& renderInfo) const;
};
class FinalDrawCallback : public osg::Camera::DrawCallback
class UpdateSlaveCallback : public osg::View::Slave::UpdateSlaveCallback
{
public:
virtual void operator()(osg::RenderInfo& renderInfo) const;
UpdateSlaveCallback(osg::ref_ptr<VRView> view, osg::GraphicsContext* gc)
: mView(view), mGC(gc)
{}
void updateSlave(osg::View& view, osg::View::Slave& slave) override;
private:
osg::ref_ptr<OpenXRManager> mXR;
osg::ref_ptr<VRView> mView;
osg::ref_ptr<osg::GraphicsContext> mGC;
};
protected:
OpenXRView(osg::ref_ptr<OpenXRManager> XR, std::string name, OpenXRSwapchain::Config config, osg::ref_ptr<osg::State> state);
virtual ~OpenXRView();
public:
VRView(std::string name, OpenXRSwapchain::Config config, osg::ref_ptr<osg::State> state);
virtual ~VRView();
public:
//! Prepare for render (set FBO)
@ -41,18 +51,12 @@ namespace MWVR
void swapBuffers(osg::GraphicsContext* gc);
void setPredictedPose(const Pose& pose);
Pose& predictedPose() { return mPredictedPose; };
public:
osg::ref_ptr<OpenXRManager> mXR;
OpenXRSwapchain::Config mSwapchainConfig;
std::unique_ptr<OpenXRSwapchain> mSwapchain;
std::string mName{};
bool mRendering{ false };
Timer mTimer;
Pose mPredictedPose{ {}, {0,0,0,1}, {} };
};
}

View file

@ -1,5 +1,5 @@
#include "openxrviewer.hpp"
#include "openxrsession.hpp"
#include "vrviewer.hpp"
#include "vrsession.hpp"
#include "openxrmanagerimpl.hpp"
#include "openxrinputmanager.hpp"
#include "vrenvironment.hpp"
@ -17,70 +17,47 @@
namespace MWVR
{
OpenXRViewer::OpenXRViewer(
VRViewer::VRViewer(
osg::ref_ptr<osgViewer::Viewer> viewer)
: osg::Group()
, mCompositionLayerProjectionViews(2, {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW})
, mRealizeOperation(new RealizeOperation())
: mRealizeOperation(new RealizeOperation())
, mViewer(viewer)
, mPreDraw(new PredrawCallback(this))
, mPostDraw(new PostdrawCallback(this))
, mConfigured(false)
{
mViewer->setRealizeOperation(mRealizeOperation);
mCompositionLayerProjectionViews[0].pose.orientation.w = 1;
mCompositionLayerProjectionViews[1].pose.orientation.w = 1;
this->setName("OpenXRRoot");
//this->setName("OpenXRRoot");
}
OpenXRViewer::~OpenXRViewer(void)
VRViewer::~VRViewer(void)
{
}
void
OpenXRViewer::traverse(
osg::NodeVisitor& visitor)
{
if (mRealizeOperation->realized())
osg::Group::traverse(visitor);
}
const XrCompositionLayerBaseHeader*
OpenXRViewer::layer()
{
// Does not check for visible, since this should always render
return reinterpret_cast<XrCompositionLayerBaseHeader*>(mLayer.get());
}
void OpenXRViewer::traversals()
void VRViewer::traversals()
{
mViewer->updateTraversal();
mViewer->renderingTraversals();
}
void OpenXRViewer::realize(osg::GraphicsContext* context)
void VRViewer::realize(osg::GraphicsContext* context)
{
std::unique_lock<std::mutex> lock(mMutex);
if (mConfigured)
{
return;
}
if (!context->isCurrent())
if (!context->makeCurrent())
{
throw std::logic_error("OpenXRViewer::configure() failed to make graphics context current.");
throw std::logic_error("VRViewer::configure() failed to make graphics context current.");
return;
}
auto mainCamera = mCameras["MainCamera"] = mViewer->getCamera();
mainCamera->setName("Main");
mainCamera->setInitialDrawCallback(new OpenXRView::InitialDrawCallback());
// Use the main camera to render any GUI to the OpenXR GUI quad's swapchain.
// (When swapping the window buffer we'll blit the mirror texture to it instead.)
mainCamera->setCullMask(MWRender::Mask_GUI);
mainCamera->setInitialDrawCallback(new VRView::InitialDrawCallback());
osg::Vec4 clearColor = mainCamera->getClearColor();
@ -88,7 +65,7 @@ namespace MWVR
if (!xr->realized())
xr->realize(context);
auto* session = Environment::get().getSession();
xr->handleEvents();
OpenXRSwapchain::Config leftConfig;
leftConfig.width = xr->impl().mConfigViews[(int)Side::LEFT_HAND].recommendedImageRectWidth;
@ -99,8 +76,8 @@ namespace MWVR
rightConfig.height = xr->impl().mConfigViews[(int)Side::RIGHT_HAND].recommendedImageRectHeight;
rightConfig.samples = xr->impl().mConfigViews[(int)Side::RIGHT_HAND].recommendedSwapchainSampleCount;
auto leftView = new OpenXRWorldView(xr, "LeftEye", context->getState(), leftConfig);
auto rightView = new OpenXRWorldView(xr, "RightEye", context->getState(), rightConfig);
auto leftView = new VRView("LeftEye", leftConfig, context->getState());
auto rightView = new VRView("RightEye", rightConfig, context->getState());
mViews["LeftEye"] = leftView;
mViews["RightEye"] = rightView;
@ -111,28 +88,36 @@ namespace MWVR
leftCamera->setPreDrawCallback(mPreDraw);
rightCamera->setPreDrawCallback(mPreDraw);
//leftCamera->setPostDrawCallback(mPostDraw);
//rightCamera->setPostDrawCallback(mPostDraw);
leftCamera->setFinalDrawCallback(mPostDraw);
rightCamera->setFinalDrawCallback(mPostDraw);
// Stereo cameras should only draw the scene (AR layers should later add minimap, health, etc.)
leftCamera->setCullMask(~MWRender::Mask_GUI);
rightCamera->setCullMask(~MWRender::Mask_GUI);
// Stereo cameras should only draw the scene
leftCamera->setCullMask(~MWRender::Mask_GUI & ~MWRender::Mask_SimpleWater & ~MWRender::Mask_UpdateVisitor);
rightCamera->setCullMask(~MWRender::Mask_GUI & ~MWRender::Mask_SimpleWater & ~MWRender::Mask_UpdateVisitor);
leftCamera->setName("LeftEye");
rightCamera->setName("RightEye");
mViewer->addSlave(leftCamera, leftView->projectionMatrix(), leftView->viewMatrix(), true);
mViewer->addSlave(rightCamera, rightView->projectionMatrix(), rightView->viewMatrix(), true);
osg::Camera::CullingMode cullingMode = osg::Camera::DEFAULT_CULLING|osg::Camera::FAR_PLANE_CULLING;
if (!Settings::Manager::getBool("small feature culling", "Camera"))
cullingMode &= ~(osg::CullStack::SMALL_FEATURE_CULLING);
else
{
auto smallFeatureCullingPixelSize = Settings::Manager::getFloat("small feature culling pixel size", "Camera");
leftCamera->setSmallFeatureCullingPixelSize(smallFeatureCullingPixelSize);
rightCamera->setSmallFeatureCullingPixelSize(smallFeatureCullingPixelSize);
cullingMode |= osg::CullStack::SMALL_FEATURE_CULLING;
}
leftCamera->setCullingMode(cullingMode);
rightCamera->setCullingMode(cullingMode);
mViewer->addSlave(leftCamera, true);
mViewer->addSlave(rightCamera, true);
mViewer->setLightingMode(osg::View::SKY_LIGHT);
mViewer->setReleaseContextAtEndOfFrameHint(false);
mCompositionLayerProjectionViews[0].subImage = leftView->swapchain().subImage();
mCompositionLayerProjectionViews[1].subImage = rightView->swapchain().subImage();
OpenXRSwapchain::Config config;
config.width = mainCamera->getViewport()->width();
@ -140,32 +125,31 @@ namespace MWVR
config.samples = 1;
// Mirror texture doesn't have to be an OpenXR swapchain.
// It's just convenient.
mMirrorTextureSwapchain.reset(new OpenXRSwapchain(xr, context->getState(), config));
// It was just convenient at the time.
mMirrorTextureSwapchain.reset(new OpenXRSwapchain(context->getState(), config));
//mViewer->addSlave(menuCamera, true);
mViewer->getSlave(0)._updateSlaveCallback = new OpenXRWorldView::UpdateSlaveCallback(xr, session, leftView, context);
mViewer->getSlave(1)._updateSlaveCallback = new OpenXRWorldView::UpdateSlaveCallback(xr, session, rightView, context);
mViewer->getSlave(0)._updateSlaveCallback = new VRView::UpdateSlaveCallback(leftView, context);
mViewer->getSlave(1)._updateSlaveCallback = new VRView::UpdateSlaveCallback(rightView, context);
mMainCameraGC = mainCamera->getGraphicsContext();
mMainCameraGC->setSwapCallback(new OpenXRViewer::SwapBuffersCallback(this));
mMainCameraGC->setSwapCallback(new VRViewer::SwapBuffersCallback(this));
mainCamera->setGraphicsContext(nullptr);
session->setLayer(OpenXRLayerStack::WORLD_VIEW_LAYER, this);
mConfigured = true;
Log(Debug::Verbose) << "Realized";
}
void OpenXRViewer::enableMainCamera(void)
void VRViewer::enableMainCamera(void)
{
mCameras["MainCamera"]->setGraphicsContext(mMainCameraGC);
}
void OpenXRViewer::disableMainCamera(void)
void VRViewer::disableMainCamera(void)
{
mCameras["MainCamera"]->setGraphicsContext(nullptr);
}
void OpenXRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc)
void VRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc)
{
//includeMenu = false;
mMirrorTextureSwapchain->beginFrame(gc);
@ -189,60 +173,33 @@ namespace MWVR
}
void
OpenXRViewer::SwapBuffersCallback::swapBuffersImplementation(
VRViewer::SwapBuffersCallback::swapBuffersImplementation(
osg::GraphicsContext* gc)
{
auto* session = Environment::get().getSession();
session->swapBuffers(gc);
session->swapBuffers(gc, *mViewer);
gc->swapBuffersImplementation();
}
void OpenXRViewer::swapBuffers(osg::GraphicsContext* gc)
void VRViewer::swapBuffers(osg::GraphicsContext* gc)
{
if (!mConfigured)
return;
auto* session = Environment::get().getSession();
auto* xr = Environment::get().getManager();
Timer timer("VRViewer::SwapBuffers");
Timer timer("OpenXRViewer::SwapBuffers");
for (auto& view : mViews)
view.second->swapBuffers(gc);
auto leftView = mViews["LeftEye"];
auto rightView = mViews["RightEye"];
leftView->swapBuffers(gc);
rightView->swapBuffers(gc);
timer.checkpoint("Views");
auto& drawPoses = session->predictedPoses(OpenXRSession::PredictionSlice::Draw);
mCompositionLayerProjectionViews[0].pose = toXR(drawPoses.eye[(int)TrackedSpace::STAGE][(int)Side::LEFT_HAND]);
mCompositionLayerProjectionViews[1].pose = toXR(drawPoses.eye[(int)TrackedSpace::STAGE][(int)Side::RIGHT_HAND]);
timer.checkpoint("Poses");
// TODO: Keep track of these in the session too.
auto stageViews = xr->impl().getPredictedViews(xr->impl().frameState().predictedDisplayTime, TrackedSpace::STAGE);
mCompositionLayerProjectionViews[0].fov = stageViews[0].fov;
mCompositionLayerProjectionViews[1].fov = stageViews[1].fov;
timer.checkpoint("Fovs");
if (!mLayer)
{
mLayer.reset(new XrCompositionLayerProjection);
mLayer->type = XR_TYPE_COMPOSITION_LAYER_PROJECTION;
mLayer->space = xr->impl().mReferenceSpaceStage;
mLayer->viewCount = 2;
mLayer->views = mCompositionLayerProjectionViews.data();
}
blitEyesToMirrorTexture(gc);
gc->swapBuffersImplementation();
}
void
OpenXRViewer::RealizeOperation::operator()(
VRViewer::RealizeOperation::operator()(
osg::GraphicsContext* gc)
{
OpenXRManager::RealizeOperation::operator()(gc);
@ -251,24 +208,24 @@ namespace MWVR
}
bool
OpenXRViewer::RealizeOperation::realized()
VRViewer::RealizeOperation::realized()
{
return Environment::get().getViewer()->realized();
}
void OpenXRViewer::preDrawCallback(osg::RenderInfo& info)
void VRViewer::preDrawCallback(osg::RenderInfo& info)
{
auto* camera = info.getCurrentCamera();
auto name = camera->getName();
auto& view = mViews[name];
if (name == "LeftEye")
Environment::get().getSession()->advanceFrame();
Environment::get().getSession()->advanceFramePhase();
view->prerenderCallback(info);
}
void OpenXRViewer::postDrawCallback(osg::RenderInfo& info)
void VRViewer::postDrawCallback(osg::RenderInfo& info)
{
auto* camera = info.getCurrentCamera();
auto name = camera->getName();
@ -282,5 +239,8 @@ namespace MWVR
camera->setPreDrawCallback(mPreDraw);
Log(Debug::Warning) << ("osg overwrote predraw");
}
//if(mDelay && name == "RightEye")
// std::this_thread::sleep_for(std::chrono::milliseconds(25));
}
}

View file

@ -9,17 +9,12 @@
#include <osgViewer/Viewer>
#include "openxrmanager.hpp"
#include "openxrsession.hpp"
#include "openxrlayer.hpp"
#include "openxrworldview.hpp"
#include "vrgui.hpp"
#include <components/sceneutil/positionattitudetransform.hpp>
struct XrCompositionLayerProjection;
struct XrCompositionLayerProjectionView;
namespace MWVR
{
class OpenXRViewer : public osg::Group, public OpenXRLayer
class VRViewer
{
public:
class RealizeOperation : public OpenXRManager::RealizeOperation
@ -35,17 +30,17 @@ namespace MWVR
class SwapBuffersCallback : public osg::GraphicsContext::SwapCallback
{
public:
SwapBuffersCallback(OpenXRViewer* viewer) : mViewer(viewer) {};
SwapBuffersCallback(VRViewer* viewer) : mViewer(viewer) {};
void swapBuffersImplementation(osg::GraphicsContext* gc) override;
private:
OpenXRViewer* mViewer;
VRViewer* mViewer;
};
class PredrawCallback : public osg::Camera::DrawCallback
{
public:
PredrawCallback(OpenXRViewer* viewer)
PredrawCallback(VRViewer* viewer)
: mViewer(viewer)
{}
@ -53,13 +48,13 @@ namespace MWVR
private:
OpenXRViewer* mViewer;
VRViewer* mViewer;
};
class PostdrawCallback : public osg::Camera::DrawCallback
{
public:
PostdrawCallback(OpenXRViewer* viewer)
PostdrawCallback(VRViewer* viewer)
: mViewer(viewer)
{}
@ -67,38 +62,34 @@ namespace MWVR
private:
OpenXRViewer* mViewer;
VRViewer* mViewer;
};
public:
OpenXRViewer(
VRViewer(
osg::ref_ptr<osgViewer::Viewer> viewer);
~OpenXRViewer(void);
~VRViewer(void);
virtual void traverse(osg::NodeVisitor& visitor) override;
//virtual void traverse(osg::NodeVisitor& visitor) override;
const XrCompositionLayerBaseHeader* layer() override;
const XrCompositionLayerBaseHeader* layer();
void traversals();
void preDrawCallback(osg::RenderInfo& info);
void postDrawCallback(osg::RenderInfo& info);
void blitEyesToMirrorTexture(osg::GraphicsContext* gc);
void swapBuffers(osg::GraphicsContext* gc) override;
void swapBuffers(osg::GraphicsContext* gc);
void realize(osg::GraphicsContext* gc);
bool realized() { return mConfigured; }
void updateTransformNode(osg::Object* object, osg::Object* data);
void enableMainCamera(void);
void disableMainCamera(void);
public:
std::unique_ptr<XrCompositionLayerProjection> mLayer = nullptr;
std::vector<XrCompositionLayerProjectionView> mCompositionLayerProjectionViews;
osg::ref_ptr<OpenXRManager::RealizeOperation> mRealizeOperation = nullptr;
osg::ref_ptr<osgViewer::Viewer> mViewer = nullptr;
std::map<std::string, osg::ref_ptr<OpenXRView> > mViews{};
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 };

View file

@ -1,7 +1,7 @@
#include "engine.hpp"
#include "mwvr/openxrmanager.hpp"
#include "mwvr/openxrsession.hpp"
#include "mwvr/openxrviewer.hpp"
#include "mwvr/vrsession.hpp"
#include "mwvr/vrviewer.hpp"
#include "mwvr/vrgui.hpp"
#ifndef USE_OPENXR
@ -20,6 +20,6 @@ void OMW::Engine::initVr()
float yardsPerMeter = 0.9144f;
float unitsPerMeter = unitsPerYard / yardsPerMeter;
mXrEnvironment.setUnitsPerMeter(unitsPerMeter);
mXrEnvironment.setSession(new MWVR::OpenXRSession());
mXrEnvironment.setViewer(new MWVR::OpenXRViewer(mViewer));
mXrEnvironment.setSession(new MWVR::VRSession());
mXrEnvironment.setViewer(new MWVR::VRViewer(mViewer));
}