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:
parent
287886d545
commit
bdade49129
16 changed files with 230 additions and 140 deletions
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue