1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-15 22:19:54 +00:00

In-place turning

This commit is contained in:
Mads Buvik Sandvei 2020-02-23 11:02:38 +01:00
parent 287886d545
commit bdade49129
16 changed files with 230 additions and 140 deletions

View file

@ -13,12 +13,13 @@ void OMW::Engine::initVr()
throw std::logic_error("mViewer must be initialized before calling initVr()");
mXR = new MWVR::OpenXRManager();
mEnvironment.setXRSession(new MWVR::OpenXRSession(mXR));
// Ref: https://wiki.openmw.org/index.php?title=Measurement_Units
float unitsPerYard = 64.f;
float yardsPerMeter = 0.9144f;
float unitsPerMeter = unitsPerYard / yardsPerMeter;
mXRViewer = new MWVR::OpenXRViewer(mXR, mViewer, unitsPerMeter);
mEnvironment.setXRSession(new MWVR::OpenXRSession(mXR, unitsPerMeter));
mXRViewer = new MWVR::OpenXRViewer(mXR, mViewer);
}

View file

@ -11,6 +11,10 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/refdata.hpp"
#ifdef USE_OPENXR
#include "../mwvr/openxrinputmanager.hpp"
#endif
#include "npcanimation.hpp"
#include <components/debug/debuglog.hpp>
@ -63,9 +67,11 @@ namespace MWRender
mVanity.enabled = false;
mVanity.allowed = true;
mPreviewCam.roll = 0.f;
mPreviewCam.pitch = 0.f;
mPreviewCam.yaw = 0.f;
mPreviewCam.offset = 400.f;
mMainCam.roll = 0.f;
mMainCam.pitch = 0.f;
mMainCam.yaw = 0.f;
mMainCam.offset = 400.f;
@ -112,7 +118,17 @@ namespace MWRender
osg::Vec3d position = getFocalPoint();
osg::Quat orient = osg::Quat(getPitch(), osg::Vec3d(1,0,0)) * osg::Quat(getYaw(), osg::Vec3d(0,0,1));
#ifdef USE_OPENXR
auto inputManager = MWBase::Environment::get().getXRInputManager();
if (inputManager)
{
position += inputManager->mHeadOffset;
}
#endif
osg::Quat orient = osg::Quat(getPitch(), osg::Vec3d(1,0,0)) * osg::Quat(getRoll(), osg::Vec3d(0, 1, 0)) * osg::Quat(getYaw(), osg::Vec3d(0,0,1));
osg::Vec3d offset = orient * osg::Vec3d(0, isFirstPerson() ? 0 : -mCameraDistance, 0);
position += offset;
@ -120,7 +136,10 @@ namespace MWRender
osg::Vec3d forward = orient * osg::Vec3d(0,1,0);
osg::Vec3d up = orient * osg::Vec3d(0,0,1);
cam->setViewMatrixAsLookAt(position, position + forward, up);
osg::Matrix lookAt = osg::Matrix::lookAt(position, position + forward, up);
//cam->setViewMatrixAsLookAt(position, position + forward, up);
cam->setViewMatrix(lookAt);
}
void Camera::reset()
@ -131,15 +150,17 @@ namespace MWRender
toggleViewMode();
}
void Camera::rotateCamera(float pitch, float yaw, bool adjust)
void Camera::rotateCamera(float pitch, float roll, float yaw, bool adjust)
{
if (adjust)
{
pitch += getPitch();
yaw += getYaw();
roll += getRoll();
}
setYaw(yaw);
setPitch(pitch);
setRoll(roll);
}
void Camera::attachTo(const MWWorld::Ptr &ptr)
@ -175,7 +196,7 @@ namespace MWRender
if(mVanity.enabled)
{
rotateCamera(0.f, osg::DegreesToRadians(3.f * duration), true);
rotateCamera(0.f, 0.f, osg::DegreesToRadians(3.f * duration), true);
}
}
@ -289,6 +310,29 @@ namespace MWRender
}
}
float Camera::getRoll()
{
if (mVanity.enabled || mPreviewMode)
return mPreviewCam.roll;
return mMainCam.roll;
}
void Camera::setRoll(float angle)
{
if (angle > osg::PI) {
angle -= osg::PI * 2;
}
else if (angle < -osg::PI) {
angle += osg::PI * 2;
}
if (mVanity.enabled || mPreviewMode) {
mPreviewCam.roll = angle;
}
else {
mMainCam.roll = angle;
}
}
float Camera::getPitch()
{
if (mVanity.enabled || mPreviewMode) {
@ -299,10 +343,10 @@ namespace MWRender
void Camera::setPitch(float angle)
{
#ifdef USE_OPENXR
// Pitch is defined purely by the HMD.
return (void)angle;
#endif
//#ifdef USE_OPENXR
// // Pitch is defined purely by the HMD.
// return (void)angle;
//#endif
const float epsilon = 0.000001f;
float limit = osg::PI_2 - epsilon;
if(mPreviewMode)

View file

@ -24,7 +24,7 @@ namespace MWRender
class Camera
{
struct CamData {
float pitch, yaw, offset;
float roll, pitch, yaw, offset;
};
MWWorld::Ptr mTrackingPtr;
@ -70,11 +70,14 @@ namespace MWRender
/// Set where the camera is looking at. Uses Morrowind (euler) angles
/// \param rot Rotation angles in radians
void rotateCamera(float pitch, float yaw, bool adjust);
void rotateCamera(float pitch, float roll, float yaw, bool adjust);
float getYaw();
void setYaw(float angle);
float getRoll();
void setRoll(float angle);
float getPitch();
void setPitch(float angle);

View file

@ -696,7 +696,7 @@ namespace MWRender
if(ptr == mCamera->getTrackingPtr() &&
!mCamera->isVanityOrPreviewModeEnabled())
{
mCamera->rotateCamera(-ptr.getRefData().getPosition().rot[0], -ptr.getRefData().getPosition().rot[2], false);
mCamera->rotateCamera(-ptr.getRefData().getPosition().rot[0], -ptr.getRefData().getPosition().rot[1], -ptr.getRefData().getPosition().rot[2], false);
}
ptr.getRefData().getBaseNode()->setAttitude(rot);
@ -1305,7 +1305,7 @@ namespace MWRender
if(!mCamera->isVanityOrPreviewModeEnabled())
return false;
mCamera->rotateCamera(rot[0], rot[2], true);
mCamera->rotateCamera(rot[0], 0.f, rot[2], true);
return true;
}

View file

@ -109,7 +109,7 @@ void ForearmController::operator()(osg::Node* node, osg::NodeVisitor* nv)
// Get current world transform of limb
osg::Matrix worldToLimb = osg::computeLocalToWorld(node->getParentalNodePaths()[0]);
// Get current world of the reference node
osg::Matrix worldReference = osg::computeLocalToWorld(mRelativeTo->getParentalNodePaths()[0]);
osg::Matrix worldReference = osg::Matrix::identity();
// New transform is reference node + tracker.
mTracker->computeLocalToWorldMatrix(worldReference, nullptr);
// Get hand

View file

@ -716,16 +716,16 @@ namespace MWVR
XrSpaceLocation location{ XR_TYPE_SPACE_LOCATION };
XrSpaceVelocity velocity{ XR_TYPE_SPACE_VELOCITY };
location.next = &velocity;
CHECK_XRCMD(xrLocateSpace(mHandSpace[(int)Chirality::LEFT_HAND], referenceSpace, time, &location));
CHECK_XRCMD(xrLocateSpace(mHandSpace[(int)Side::LEFT_HAND], referenceSpace, time, &location));
handPoses[(int)Chirality::LEFT_HAND] = MWVR::Pose{
handPoses[(int)Side::LEFT_HAND] = MWVR::Pose{
osg::fromXR(location.pose.position),
osg::fromXR(location.pose.orientation),
osg::fromXR(velocity.linearVelocity)
};
CHECK_XRCMD(xrLocateSpace(mHandSpace[(int)Chirality::RIGHT_HAND], referenceSpace, time, &location));
handPoses[(int)Chirality::RIGHT_HAND] = MWVR::Pose{
CHECK_XRCMD(xrLocateSpace(mHandSpace[(int)Side::RIGHT_HAND], referenceSpace, time, &location));
handPoses[(int)Side::RIGHT_HAND] = MWVR::Pose{
osg::fromXR(location.pose.position),
osg::fromXR(location.pose.orientation),
osg::fromXR(velocity.linearVelocity)
@ -795,6 +795,8 @@ namespace MWVR
auto* session = MWBase::Environment::get().getXRSession();
updateHead();
OpenXRActionEvent event{};
while (mXRInput->nextActionEvent(event))
{
@ -936,7 +938,7 @@ namespace MWVR
}
break;
case A_LookLeftRight:
mInputBinder->getChannel(A_LookLeftRight)->setValue(event.value / 2.f + 0.5f);
mYaw += osg::DegreesToRadians(event.value) * 2.f;
break;
case A_MoveLeftRight:
mInputBinder->getChannel(A_MoveLeftRight)->setValue(event.value / 2.f + 0.5f);
@ -957,4 +959,47 @@ namespace MWVR
Log(Debug::Warning) << "Unhandled XR action " << event.action;
}
}
void OpenXRInputManager::updateHead()
{
if (!mPlayer)
return;
MWBase::World* world = MWBase::Environment::get().getWorld();
auto player = mPlayer->getPlayer();
auto* session = MWBase::Environment::get().getXRSession();
auto currentHeadPose = session->predictedPoses().head[(int)TrackedSpace::STAGE];
currentHeadPose.position *= session->unitsPerMeter();
osg::Vec3 vrMovement = currentHeadPose.position - mPreviousHeadPose.position;
mPreviousHeadPose = currentHeadPose;
float yaw = 0.f;
float pitch = 0.f;
float roll = 0.f;
getEulerAngles(currentHeadPose.orientation, yaw, pitch, roll);
yaw += mYaw;
if (mRecenter)
{
mHeadOffset = osg::Vec3(0, 0, 0);
mRecenter = false;
}
else
{
osg::Quat gameworldYaw = osg::Quat(mYaw, osg::Vec3(0, 0, -1));
mHeadOffset += gameworldYaw * vrMovement;
float rot[3];
rot[0] = pitch;
rot[1] = roll;
rot[2] = yaw;
world->rotateObject(player, rot[0], rot[1], rot[2], MWBase::RotationFlag_none);
}
// Z will and should not be caught by the characyer
mHeadOffset.z() = currentHeadPose.position.z();
}
}

View file

@ -35,6 +35,8 @@ namespace MWVR
/// Overriden to update XR inputs
virtual void update(float dt, bool disableControls = false, bool disableEvents = false);
void updateHead();
void processEvent(const OpenXRActionEvent& event);
PoseSet getHandPoses(int64_t time, TrackedSpace space);
@ -43,6 +45,10 @@ namespace MWVR
osg::ref_ptr<OpenXRViewer> mXRViewer;
std::unique_ptr<OpenXRInput> mXRInput;
Pose mPreviousHeadPose{};
osg::Vec3 mHeadOffset{ 0,0,0 };
bool mRecenter{ true };
float mYaw{ 0.f };
};
}

View file

@ -70,7 +70,7 @@ namespace MWVR
VIEW=1 //!< Track limb in the VR view space. Meaning a space with the head as origin and orientation.
};
enum class Chirality
enum class Side
{
LEFT_HAND = 0,
RIGHT_HAND = 1

View file

@ -520,21 +520,21 @@ namespace osg
Vec3 fromXR(XrVector3f v)
{
return Vec3{ v.x, v.y, v.z };
return Vec3{ v.x, -v.z, v.y };
}
Quat fromXR(XrQuaternionf quat)
{
return Quat{ quat.x, quat.y, quat.z, quat.w };
return Quat{ quat.x, -quat.z, quat.y, quat.w };
}
XrVector3f toXR(Vec3 v)
{
return XrVector3f{ v.x(), v.y(), v.z() };
return XrVector3f{ v.x(), v.z(), -v.y() };
}
XrQuaternionf toXR(Quat quat)
{
return XrQuaternionf{ quat.x(), quat.y(), quat.z(), quat.w() };
return XrQuaternionf{ quat.x(), quat.z(), -quat.y(), quat.w() };
}
}

View file

@ -31,7 +31,7 @@ namespace MWVR
{
// I position menus half a meter in front of the player, facing the player.
mPose = predictedPose();
mPose.position += mPose.orientation * osg::Vec3(0, 0, -0.5);
mPose.position += mPose.orientation * osg::Vec3(0, 0.5, 0);
mPose.orientation = -mPose.orientation;
Log(Debug::Verbose) << "Menu pose updated to: " << mPose;

View file

@ -27,9 +27,10 @@
namespace MWVR
{
OpenXRSession::OpenXRSession(
osg::ref_ptr<OpenXRManager> XR)
osg::ref_ptr<OpenXRManager> XR,
float unitsPerMeter)
: mXR(XR)
// , mInputManager(new OpenXRInput(mXR))
, mUnitsPerMeter(unitsPerMeter)
{
}
@ -67,31 +68,11 @@ namespace MWVR
if (!mXR->sessionRunning())
return;
// For now it seems we must just accept crap performance from the rendering loop
// Since Oculus' implementation of waitFrame() does not even attempt to reflect real
// render time and just incurs a huge useless delay.
Timer timer("OpenXRSession::waitFrame");
mXR->waitFrame();
timer.checkpoint("waitFrame");
// mInputManager->updateControls();
predictNext(0);
//OpenXRActionEvent event{};
//while (mInputManager->nextActionEvent(event))
//{
// Log(Debug::Verbose) << "ActionEvent action=" << event.action << " onPress=" << event.onPress;
// if (event.action == MWInput::InputManager::A_GameMenu)
// {
// Log(Debug::Verbose) << "A_GameMenu";
// auto* menuLayer = dynamic_cast<OpenXRMenu*>(mLayerStack.layerObjects()[OpenXRLayerStack::MENU_VIEW_LAYER]);
// if (menuLayer)
// {
// menuLayer->setVisible(!menuLayer->isVisible());
// menuLayer->updatePosition();
// }
// }
//}
mPredictionsReady = true;
}
@ -121,10 +102,11 @@ namespace MWVR
}
// OSG doesn't provide API to extract yaw from a quat, but i need it.
// 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
static float getYaw(const osg::Quat& quat)
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;
@ -152,16 +134,19 @@ namespace MWVR
angle_z = atan2(tr_y, tr_x);
}
return angle_z;
yaw = angle_z;
pitch = angle_x;
roll = angle_y;
}
float OpenXRSession::movementYaw(void)
{
osg::Matrix lookAt;
lookAt.makeLookAt(osg::Vec3(0, 0, 0), osg::Vec3(0, 1, 0), osg::Vec3(0, 0, 1));
lookAt = osg::Matrix::inverse(lookAt);
auto lhandquat = predictedPoses().hands[(int)MWVR::TrackedSpace::STAGE][(int)MWVR::Chirality::LEFT_HAND].orientation * lookAt.getRotate();
return getYaw(lhandquat);
auto lhandquat = predictedPoses().hands[(int)TrackedSpace::VIEW][(int)MWVR::Side::LEFT_HAND].orientation;
float yaw = 0.f;
float pitch = 0.f;
float roll = 0.f;
getEulerAngles(lhandquat, yaw, pitch, roll);
return yaw;
}
void OpenXRSession::predictNext(int extraPeriods)
@ -170,6 +155,8 @@ namespace MWVR
auto input = MWBase::Environment::get().getXRInputManager();
auto previousHeadPose = mPredictedPoses.head[(int)TrackedSpace::STAGE];
// Update pose predictions
mPredictedPoses.head[(int)TrackedSpace::STAGE] = mXR->impl().getPredictedLimbPose(mPredictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::STAGE);
mPredictedPoses.head[(int)TrackedSpace::VIEW] = mXR->impl().getPredictedLimbPose(mPredictedDisplayTime, TrackedLimb::HEAD, TrackedSpace::VIEW);
@ -177,10 +164,10 @@ namespace MWVR
mPredictedPoses.hands[(int)TrackedSpace::VIEW] = input->getHandPoses(mPredictedDisplayTime, TrackedSpace::VIEW);
auto stageViews = mXR->impl().getPredictedViews(mPredictedDisplayTime, TrackedSpace::STAGE);
auto hmdViews = mXR->impl().getPredictedViews(mPredictedDisplayTime, TrackedSpace::VIEW);
mPredictedPoses.eye[(int)TrackedSpace::STAGE][(int)Chirality::LEFT_HAND] = fromXR(stageViews[(int)Chirality::LEFT_HAND].pose);
mPredictedPoses.eye[(int)TrackedSpace::VIEW][(int)Chirality::LEFT_HAND] = fromXR(hmdViews[(int)Chirality::LEFT_HAND].pose);
mPredictedPoses.eye[(int)TrackedSpace::STAGE][(int)Chirality::RIGHT_HAND] = fromXR(stageViews[(int)Chirality::RIGHT_HAND].pose);
mPredictedPoses.eye[(int)TrackedSpace::VIEW][(int)Chirality::RIGHT_HAND] = fromXR(hmdViews[(int)Chirality::RIGHT_HAND].pose);
mPredictedPoses.eye[(int)TrackedSpace::STAGE][(int)Side::LEFT_HAND] = fromXR(stageViews[(int)Side::LEFT_HAND].pose);
mPredictedPoses.eye[(int)TrackedSpace::VIEW][(int)Side::LEFT_HAND] = fromXR(hmdViews[(int)Side::LEFT_HAND].pose);
mPredictedPoses.eye[(int)TrackedSpace::STAGE][(int)Side::RIGHT_HAND] = fromXR(stageViews[(int)Side::RIGHT_HAND].pose);
mPredictedPoses.eye[(int)TrackedSpace::VIEW][(int)Side::RIGHT_HAND] = fromXR(hmdViews[(int)Side::RIGHT_HAND].pose);
}
}

View file

@ -15,6 +15,8 @@
namespace MWVR
{
extern void getEulerAngles(const osg::Quat& quat, float& yaw, float& pitch, float& roll);
class OpenXRSession
{
using seconds = std::chrono::duration<double>;
@ -23,13 +25,13 @@ class OpenXRSession
using time_point = clock::time_point;
public:
OpenXRSession(osg::ref_ptr<OpenXRManager> XR);
OpenXRSession(osg::ref_ptr<OpenXRManager> XR, float unitsPerMeter);
~OpenXRSession();
void setLayer(OpenXRLayerStack::Layer layerType, OpenXRLayer* layer);
void swapBuffers(osg::GraphicsContext* gc);
PoseSets& predictedPoses() { return mPredictedPoses; };
const PoseSets& predictedPoses() const { return mPredictedPoses; };
//! Call before updating poses and other inputs
void waitFrame();
@ -41,14 +43,16 @@ public:
void updateMenuPosition(void);
//! Yaw the movement if a tracking limb is configured
//! Yaw angle to be used for offsetting movement direction
float movementYaw(void);
float unitsPerMeter() const { return mUnitsPerMeter; };
osg::ref_ptr<OpenXRManager> mXR;
OpenXRLayerStack mLayerStack{};
float mUnitsPerMeter = 1.f;
PoseSets mPredictedPoses{};
bool mPredictionsReady{ false };
};

View file

@ -1,6 +1,7 @@
#include "openxrviewer.hpp"
#include "openxrsession.hpp"
#include "openxrmanagerimpl.hpp"
#include "openxrinputmanager.hpp"
#include "Windows.h"
#include "../mwrender/vismask.hpp"
#include "../mwmechanics/actorutil.hpp"
@ -17,8 +18,7 @@ namespace MWVR
OpenXRViewer::OpenXRViewer(
osg::ref_ptr<OpenXRManager> XR,
osg::ref_ptr<osgViewer::Viewer> viewer,
float metersPerUnit)
osg::ref_ptr<osgViewer::Viewer> viewer)
: osg::Group()
, mXR(XR)
, mCompositionLayerProjectionViews(2, {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW})
@ -26,7 +26,6 @@ namespace MWVR
, mViewer(viewer)
, mPreDraw(new PredrawCallback(this))
, mPostDraw(new PostdrawCallback(this))
, mMetersPerUnit(metersPerUnit)
, mConfigured(false)
{
mViewer->setRealizeOperation(mRealizeOperation);
@ -99,18 +98,19 @@ namespace MWVR
if (!mXR->realized())
mXR->realize(context);
auto* session = MWBase::Environment::get().getXRSession();
OpenXRSwapchain::Config leftConfig;
leftConfig.width = mXR->impl().mConfigViews[(int)Chirality::LEFT_HAND].recommendedImageRectWidth;
leftConfig.height = mXR->impl().mConfigViews[(int)Chirality::LEFT_HAND].recommendedImageRectHeight;
leftConfig.samples = mXR->impl().mConfigViews[(int)Chirality::LEFT_HAND].recommendedSwapchainSampleCount;
leftConfig.width = mXR->impl().mConfigViews[(int)Side::LEFT_HAND].recommendedImageRectWidth;
leftConfig.height = mXR->impl().mConfigViews[(int)Side::LEFT_HAND].recommendedImageRectHeight;
leftConfig.samples = mXR->impl().mConfigViews[(int)Side::LEFT_HAND].recommendedSwapchainSampleCount;
OpenXRSwapchain::Config rightConfig;
rightConfig.width = mXR->impl().mConfigViews[(int)Chirality::RIGHT_HAND].recommendedImageRectWidth;
rightConfig.height = mXR->impl().mConfigViews[(int)Chirality::RIGHT_HAND].recommendedImageRectHeight;
rightConfig.samples = mXR->impl().mConfigViews[(int)Chirality::RIGHT_HAND].recommendedSwapchainSampleCount;
rightConfig.width = mXR->impl().mConfigViews[(int)Side::RIGHT_HAND].recommendedImageRectWidth;
rightConfig.height = mXR->impl().mConfigViews[(int)Side::RIGHT_HAND].recommendedImageRectHeight;
rightConfig.samples = mXR->impl().mConfigViews[(int)Side::RIGHT_HAND].recommendedSwapchainSampleCount;
auto leftView = new OpenXRWorldView(mXR, "LeftEye", context->getState(), leftConfig, mMetersPerUnit);
auto rightView = new OpenXRWorldView(mXR, "RightEye", context->getState(), rightConfig, mMetersPerUnit);
auto leftView = new OpenXRWorldView(mXR, "LeftEye", context->getState(), leftConfig, session->unitsPerMeter());
auto rightView = new OpenXRWorldView(mXR, "RightEye", context->getState(), rightConfig, session->unitsPerMeter());
mViews["LeftEye"] = leftView;
mViews["RightEye"] = rightView;
@ -160,7 +160,6 @@ namespace MWVR
menuCamera->setPreDrawCallback(mPreDraw);
menuCamera->setPostDrawCallback(mPostDraw);
auto* session = MWBase::Environment::get().getXRSession();
mViewer->addSlave(menuCamera, true);
mViewer->getSlave(0)._updateSlaveCallback = new OpenXRWorldView::UpdateSlaveCallback(mXR, session, leftView, context);
@ -214,20 +213,21 @@ namespace MWVR
if (!mConfigured)
return;
////// NEW SYSTEM
Timer timer("OpenXRViewer::SwapBuffers");
mViews["LeftEye"]->swapBuffers(gc);
mViews["RightEye"]->swapBuffers(gc);
auto leftView = mViews["LeftEye"];
auto rightView = mViews["RightEye"];
leftView->swapBuffers(gc);
rightView->swapBuffers(gc);
timer.checkpoint("Views");
auto leftEyePose = toXR(mViews["LeftEye"]->predictedPose());
auto rightEyePose = toXR(mViews["RightEye"]->predictedPose());
mCompositionLayerProjectionViews[0].pose = leftEyePose;
mCompositionLayerProjectionViews[1].pose = rightEyePose;
mCompositionLayerProjectionViews[0].pose = toXR(leftView->predictedPose());
mCompositionLayerProjectionViews[1].pose = toXR(rightView->predictedPose());
timer.checkpoint("Poses");
// TODO: Keep track of these in the session too.
auto stageViews = mXR->impl().getPredictedViews(mXR->impl().frameState().predictedDisplayTime, TrackedSpace::STAGE);
auto stageViews = mXR->impl().getPredictedViews(mXR->impl().frameState().predictedDisplayTime, TrackedSpace::VIEW);
mCompositionLayerProjectionViews[0].fov = stageViews[0].fov;
mCompositionLayerProjectionViews[1].fov = stageViews[1].fov;
timer.checkpoint("Fovs");
@ -237,7 +237,7 @@ namespace MWVR
{
mLayer.reset(new XrCompositionLayerProjection);
mLayer->type = XR_TYPE_COMPOSITION_LAYER_PROJECTION;
mLayer->space = mXR->impl().mReferenceSpaceStage;
mLayer->space = mXR->impl().mReferenceSpaceView;
mLayer->viewCount = 2;
mLayer->views = mCompositionLayerProjectionViews.data();
}
@ -306,53 +306,64 @@ namespace MWVR
return;
}
auto& poses = MWBase::Environment::get().getXRSession()->predictedPoses();
auto handPosesView = poses.hands[(int)TrackedSpace::VIEW];
auto session = MWBase::Environment::get().getXRSession();
auto& poses = session->predictedPoses();
auto handPosesStage = poses.hands[(int)TrackedSpace::STAGE];
int chirality = (int)Chirality::LEFT_HAND;
int side = (int)Side::LEFT_HAND;
if (hand_transform->getName() == "tracker r hand")
chirality = (int)Chirality::RIGHT_HAND;
{
side = (int)Side::RIGHT_HAND;
}
MWVR::Pose hand = handPosesStage[chirality];
mXR->playerScale(hand);
auto orientation = hand.orientation;
auto position = hand.position;
position = position * mMetersPerUnit;
MWVR::Pose handStage = handPosesStage[side];
MWVR::Pose headStage = poses.head[(int)TrackedSpace::STAGE];
mXR->playerScale(handStage);
mXR->playerScale(headStage);
auto orientation = handStage.orientation;
auto position = handStage.position - headStage.position;
position = position * session->unitsPerMeter();
// Move OpenXR's poses into OpenMW's view by applying the inverse of the rotation of the view matrix.
// This works because OpenXR's conventions match opengl's clip space, thus the inverse of the view matrix converts an OpenXR pose to OpenMW's view space (including world rotation).
// For the hands we don't want the full camera view matrix, but the relative matrix from the player root. So i create a lookat matrix based on osg's conventions.
// TODO: The full camera view matrix could work if i change how animations are overriden.
osg::Matrix lookAt;
lookAt.makeLookAt(osg::Vec3(0, 0, 0), osg::Vec3(0, 1, 0), osg::Vec3(0, 0, 1));
lookAt = osg::Matrix::inverse(lookAt);
auto camera = mViewer->getCamera();
auto viewMatrix = camera->getViewMatrix();
orientation = orientation * lookAt.getRotate();
//position = invViewMatrix.preMult(position);
position = lookAt.getRotate() * position;
// Morrowind's meshes do not point forward by default.
// Static since they do not need to be recomputed.
// Align orientation with the game world
auto inputManager = MWBase::Environment::get().getXRInputManager();
if (inputManager)
{
auto playerYaw = osg::Quat(-inputManager->mYaw, osg::Vec3d(0, 0, 1));
position = playerYaw * position;
orientation = orientation * playerYaw;
}
// Add camera offset
osg::Vec3 viewPosition;
osg::Vec3 center;
osg::Vec3 up;
viewMatrix.getLookAt(viewPosition, center, up, 1.0);
position += viewPosition;
//// Morrowind's meshes do not point forward by default.
//// Static since they do not need to be recomputed.
static float VRbias = osg::DegreesToRadians(-90.f);
static osg::Quat yaw(VRbias, osg::Vec3f(0, 1, 0));
static osg::Quat pitch(2.f * VRbias, osg::Vec3f(0, 0, 1));
static osg::Quat roll (-VRbias, osg::Vec3f(1, 0, 0));
static osg::Quat yaw(VRbias, osg::Vec3f(0, 0, 1));
static osg::Quat pitch(2.f * VRbias, osg::Vec3f(0, 1, 0));
static osg::Quat roll (2.f * VRbias, osg::Vec3f(1, 0, 0));
orientation = pitch * yaw * orientation;
if (hand_transform->getName() == "tracker r hand")
orientation = roll * orientation;
else
orientation = roll.inverse() * orientation;
// Hand are by default not well-centered
// Note, these numbers are just a rough guess, but seem to work out well.
// These numbers are just a rough guess
osg::Vec3 offcenter = osg::Vec3(-0.175, 0., .033);
if (hand_transform->getName() == "tracker r hand")
offcenter.z() *= -1.;
osg::Vec3 recenter = orientation * offcenter;
position = position + recenter * mMetersPerUnit;
position = position + recenter * session->unitsPerMeter();
hand_transform->setAttitude(orientation);
hand_transform->setPosition(position);

View file

@ -85,8 +85,7 @@ namespace MWVR
public:
OpenXRViewer(
osg::ref_ptr<OpenXRManager> XR,
osg::ref_ptr<osgViewer::Viewer> viewer,
float metersPerUnit = 1.f);
osg::ref_ptr<osgViewer::Viewer> viewer);
~OpenXRViewer(void);
@ -126,7 +125,6 @@ namespace MWVR
std::mutex mMutex;
float mMetersPerUnit = 1.f;
bool mConfigured = false;
};
}

View file

@ -2,6 +2,7 @@
#include "openxrmanager.hpp"
#include "openxrmanagerimpl.hpp"
#include "../mwinput/inputmanagerimp.hpp"
#include "openxrinputmanager.hpp"
#include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlgraphicswindow.hpp>
@ -11,6 +12,7 @@
#include <openxr/openxr.h>
#include "../mwrender/vismask.hpp"
#include "../mwbase/environment.hpp"
#include <osg/Camera>
#include <osgViewer/Renderer>
@ -83,7 +85,7 @@ namespace MWVR
auto hmdViews = mXR->impl().getPredictedViews(mXR->impl().frameState().predictedDisplayTime, TrackedSpace::VIEW);
float near = Settings::Manager::getFloat("near clip", "Camera");
float far = Settings::Manager::getFloat("viewing distance", "Camera") * mMetersPerUnit;
float far = Settings::Manager::getFloat("viewing distance", "Camera");
//return perspectiveFovMatrix()
if(mName == "LeftEye")
return perspectiveFovMatrix(near, far, hmdViews[0].fov);
@ -94,15 +96,12 @@ namespace MWVR
{
MWVR::Pose pose = predictedPose();
mXR->playerScale(pose);
osg::Vec3 position = pose.position;
// invert orientation (co jugate of Quaternion) and position to apply to the view matrix as offset.
// This works, despite different conventions between OpenXR and OSG, because the OSG view matrix will
// have converted to OpenGL's clip space conventions before this matrix is applied, and OpenXR's conventions
// match OpenGL.
osg::Vec3 position = pose.position * mUnitsPerMeter;
osg::Quat orientation = pose.orientation;
osg::Matrix viewMatrix;
viewMatrix.setTrans(-position * mMetersPerUnit);
viewMatrix.postMultRotate(pose.orientation.conj());
viewMatrix.setTrans(-position);
viewMatrix.postMultRotate(orientation.conj());
return viewMatrix;
}
@ -112,9 +111,9 @@ namespace MWVR
std::string name,
osg::ref_ptr<osg::State> state,
OpenXRSwapchain::Config config,
float metersPerUnit )
float unitsPerMeter)
: OpenXRView(XR, name, config, state)
, mMetersPerUnit(metersPerUnit)
, mUnitsPerMeter(unitsPerMeter)
{
}
@ -140,9 +139,6 @@ namespace MWVR
auto* view = renderInfo.getView();
auto* camera = renderInfo.getCurrentCamera();
auto name = camera->getName();
//Log(Debug::Verbose) << "Updating camera " << name;
}
void
@ -160,23 +156,18 @@ namespace MWVR
{
mXR->handleEvents();
mSession->waitFrame();
auto leftEyePose = poses.eye[(int)TrackedSpace::STAGE][(int)Chirality::LEFT_HAND];
auto leftEyePose = poses.eye[(int)TrackedSpace::VIEW][(int)Side::LEFT_HAND];
mView->setPredictedPose(leftEyePose);
}
else
{
auto rightEyePose = poses.eye[(int)TrackedSpace::STAGE][(int)Chirality::RIGHT_HAND];
auto rightEyePose = poses.eye[(int)TrackedSpace::VIEW][(int)Side::RIGHT_HAND];
mView->setPredictedPose(rightEyePose);
}
if (!mXR->sessionRunning())
return;
// TODO: This is where controls should update
auto viewMatrix = view.getCamera()->getViewMatrix() * mView->viewMatrix();
//auto viewMatrix = mView->viewMatrix();
auto projectionMatrix = mView->projectionMatrix();
camera->setViewMatrix(viewMatrix);

View file

@ -26,7 +26,7 @@ namespace MWVR
};
public:
OpenXRWorldView(osg::ref_ptr<OpenXRManager> XR, std::string name, osg::ref_ptr<osg::State> state, OpenXRSwapchain::Config config, float metersPerUnit);
OpenXRWorldView(osg::ref_ptr<OpenXRManager> XR, std::string name, osg::ref_ptr<osg::State> state, OpenXRSwapchain::Config config, float unitsPerMeter);
~OpenXRWorldView();
//! Prepare for render (update matrices)
@ -36,7 +36,7 @@ namespace MWVR
//! View offset for this view
osg::Matrix viewMatrix();
float mMetersPerUnit = 1.f;
float mUnitsPerMeter = 1.f;
};
}