From 1d47807419f1a9149ff5d8abb1549f57c5086035 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sun, 23 Feb 2020 12:56:48 +0100 Subject: [PATCH] Character movement following VR movement --- apps/openmw/mwphysics/physicssystem.cpp | 78 ++++++++++++++++++++++++- apps/openmw/mwrender/camera.cpp | 18 +++--- apps/openmw/mwvr/openxranimation.cpp | 9 +++ apps/openmw/mwvr/openxrinputmanager.cpp | 3 +- 4 files changed, 96 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index ac8117acf..eb4ef8389 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -42,6 +42,7 @@ #ifdef USE_OPENXR #include "../mwvr/openxrsession.hpp" +#include "../mwvr/openxrinputmanager.hpp" #endif #include "collisiontype.hpp" @@ -383,6 +384,81 @@ namespace MWPhysics Stepper stepper(collisionWorld, colobj); osg::Vec3f origVelocity = velocity; osg::Vec3f newPosition = position; + +#ifdef USE_OPENXR + if (isPlayer) + { + auto inputManager = MWBase::Environment::get().getXRInputManager(); + + osg::Vec3 trackingOffset = inputManager->mHeadOffset; + // Player's tracking height should not affect character position + trackingOffset.z() = 0; + + float remainingTime = time; + float remainder = 1.f; + + for (int iterations = 0; iterations < sMaxIterations && remainingTime > 0.01f && remainder > 0.01; ++iterations) + { + osg::Vec3 toMove = trackingOffset * remainder; + osg::Vec3 nextpos = newPosition + toMove; + + if ((newPosition - nextpos).length2() > 0.0001) + { + // trace to where character would go if there were no obstructions + tracer.doTrace(colobj, newPosition, nextpos, collisionWorld); + + // check for obstructions + if (tracer.mFraction >= 1.0f) + { + newPosition = tracer.mEndPos; // ok to move, so set newPosition + remainder = 0.f; + break; + } + } + else + { + // The current position and next position are nearly the same, so just exit. + // Note: Bullet can trigger an assert in debug modes if the positions + // are the same, since that causes it to attempt to normalize a zero + // length vector (which can also happen with nearly identical vectors, since + // precision can be lost due to any math Bullet does internally). Since we + // aren't performing any collision detection, we want to reject the next + // position, so that we don't slowly move inside another object. + remainder = 0.f; + break; + } + // We are touching something. + if (tracer.mFraction < 1E-9f) + { + // Try to separate by backing off slighly to unstuck the solver + osg::Vec3f backOff = (newPosition - tracer.mHitPoint) * 1E-2f; + newPosition += backOff; + } + + // We hit something. Check if we can step up. + float hitHeight = tracer.mHitPoint.z() - tracer.mEndPos.z() + halfExtents.z(); + osg::Vec3f oldPosition = newPosition; + bool result = false; + if (hitHeight < sStepSizeUp && !isActor(tracer.mHitObject)) + { + // Try to step up onto it. + // NOTE: stepMove does not allow stepping over, modifies newPosition if successful + result = stepper.step(newPosition, toMove, remainingTime); + remainder = remainingTime / time; + } + } + + // Best effort attempt and not losing any tracking + osg::Vec3 moved = newPosition - position; + inputManager->mHeadOffset.x() -= moved.x(); + inputManager->mHeadOffset.y() -= moved.y(); + + Log(Debug::Verbose) << "trackingOffset: " << trackingOffset << ", remainder=" << remainder << ", moved=" << moved; + + } +#endif + + /* * A loop to find newPosition using tracer, if successful different from the starting position. * nextpos is the local variable used to find potential newPosition, using velocity and remainingTime @@ -391,7 +467,7 @@ namespace MWPhysics float remainingTime = time; for(int iterations = 0; iterations < sMaxIterations && remainingTime > 0.01f; ++iterations) { - osg::Vec3f nextpos = newPosition + velocity * remainingTime; + osg::Vec3 nextpos = newPosition + velocity * remainingTime; // If not able to fly, don't allow to swim up into the air if(!isFlying && // can't fly diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 592b1d997..fc341a1a8 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -116,22 +116,24 @@ namespace MWRender if (mTrackingPtr.isEmpty()) return; - osg::Vec3d position = getFocalPoint(); + 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 position = getFocalPoint(); #ifdef USE_OPENXR auto inputManager = MWBase::Environment::get().getXRInputManager(); if (inputManager) { position += inputManager->mHeadOffset; + // To show the body, we'll need a neck offset for comfort + // This won't do as it will mess with tracking when the player turns his head + //position += orient * osg::Vec3(0, 15, 0); } -#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)); - - +#else osg::Vec3d offset = orient * osg::Vec3d(0, isFirstPerson() ? 0 : -mCameraDistance, 0); position += offset; +#endif + osg::Vec3d forward = orient * osg::Vec3d(0,1,0); osg::Vec3d up = orient * osg::Vec3d(0,0,1); @@ -343,10 +345,6 @@ namespace MWRender void Camera::setPitch(float angle) { -//#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) diff --git a/apps/openmw/mwvr/openxranimation.cpp b/apps/openmw/mwvr/openxranimation.cpp index b007558bd..fec2ffbdf 100644 --- a/apps/openmw/mwvr/openxranimation.cpp +++ b/apps/openmw/mwvr/openxranimation.cpp @@ -194,6 +194,15 @@ void OpenXRAnimation::updateParts() removeIndividualPart(ESM::PartReferenceType::PRT_RForearm); removeIndividualPart(ESM::PartReferenceType::PRT_RUpperarm); removeIndividualPart(ESM::PartReferenceType::PRT_RWrist); + removeIndividualPart(ESM::PartReferenceType::PRT_Cuirass); + removeIndividualPart(ESM::PartReferenceType::PRT_Groin); + removeIndividualPart(ESM::PartReferenceType::PRT_Neck); + removeIndividualPart(ESM::PartReferenceType::PRT_Skirt); + removeIndividualPart(ESM::PartReferenceType::PRT_Tail); + removeIndividualPart(ESM::PartReferenceType::PRT_LLeg); + removeIndividualPart(ESM::PartReferenceType::PRT_RLeg); + removeIndividualPart(ESM::PartReferenceType::PRT_LAnkle); + removeIndividualPart(ESM::PartReferenceType::PRT_RAnkle); } void OpenXRAnimation::setPointForward(bool enabled) { diff --git a/apps/openmw/mwvr/openxrinputmanager.cpp b/apps/openmw/mwvr/openxrinputmanager.cpp index b0e87b9e8..69492c8d4 100644 --- a/apps/openmw/mwvr/openxrinputmanager.cpp +++ b/apps/openmw/mwvr/openxrinputmanager.cpp @@ -971,6 +971,7 @@ namespace MWVR auto* session = MWBase::Environment::get().getXRSession(); auto currentHeadPose = session->predictedPoses().head[(int)TrackedSpace::STAGE]; + session->mXR->playerScale(currentHeadPose); currentHeadPose.position *= session->unitsPerMeter(); osg::Vec3 vrMovement = currentHeadPose.position - mPreviousHeadPose.position; mPreviousHeadPose = currentHeadPose; @@ -999,7 +1000,7 @@ namespace MWVR world->rotateObject(player, rot[0], rot[1], rot[2], MWBase::RotationFlag_none); } - // Z will and should not be caught by the characyer + // Z should not be affected mHeadOffset.z() = currentHeadPose.position.z(); } }