From 2be87fe7dcf47ff33560349fd36d2be6012cf30b Mon Sep 17 00:00:00 2001 From: Phil Palmer Date: Sun, 27 Dec 2020 22:13:09 -0500 Subject: [PATCH] Fix VR recentering cases that gave incorrect vertical position. Fix for MyAvatar sometimes hovering off the ground after foot tracking was enabled. MyAvatar's body is now recentered when foot-tracking starts or ends (in MyAvatar::update). Repro: - Start without foot tracking, 'Allow my avatar to stand: Always'. - Crouch, and wait a few seconds for the avatar to pop back to the standing position. - Slowly stand straight so that the avatar raises off the ground without recentering (PS: is this a bug?). If it recenters, retry from previous step. - Calibrate foot tracking. Previously the avatar would remain hovering off the ground until MyAvatar::centerBody was called. Fix for MyAvatar popping to a standing position if the left foot lost tracking. The recentering now takes into account if either of the feet are tracked. Repro: - Enable foot tracking (and ideally hips tracking). - Sit on the floor. - Cover the left foot sensor so it loses tracking. Previously the avatar and viewpoint would pop to a standing position. Optimisation: MyAvatar::update now stores bools indicating which body parts are tracked (_isBodyPartTracked). This avoid unnecessary computations that came from using getControllerPoseInAvatarFrame to convert controller::Pose from sensor space to world space and then to avatar space, only to check if the pose was valid. --- interface/src/avatar/MyAvatar.cpp | 53 +++++++++++++++++++------------ interface/src/avatar/MyAvatar.h | 13 ++++++-- 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4584f25e63..86c6bcd0d8 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -595,7 +595,7 @@ void MyAvatar::updateSitStandState(float newHeightReading, float dt) { const float SITTING_TIMEOUT = 4.0f; // 4 seconds const float STANDING_TIMEOUT = 0.3333f; // 1/3 second const float SITTING_UPPER_BOUND = 1.52f; - if (!getIsAway() && getControllerPoseInAvatarFrame(controller::Action::HEAD).isValid()) { + if (!getIsAway() && _isBodyPartTracked._head) { if (getIsInSittingState()) { if (newHeightReading > (STANDING_HEIGHT_MULTIPLE * _tippingPoint)) { // if we recenter upwards then no longer in sitting state @@ -659,10 +659,35 @@ void MyAvatar::update(float deltaTime) { float tau = deltaTime / HMD_FACING_TIMESCALE; setHipToHandController(computeHandAzimuth()); + // Determine which body parts are under direct control (tracked). + { + _isBodyPartTracked._leftHand = getControllerPoseInSensorFrame(controller::Action::LEFT_HAND).isValid(); + _isBodyPartTracked._rightHand = getControllerPoseInSensorFrame(controller::Action::RIGHT_HAND).isValid(); + _isBodyPartTracked._head = getControllerPoseInSensorFrame(controller::Action::HEAD).isValid(); + + // Check for either foot so that if one foot loses tracking, we don't break out of foot-tracking behaviour + // (in terms of avatar recentering for example). + _isBodyPartTracked._feet = _isBodyPartTracked._head && // Feet can't be tracked unless head is tracked. + (getControllerPoseInSensorFrame(controller::Action::LEFT_FOOT).isValid() || + getControllerPoseInSensorFrame(controller::Action::RIGHT_FOOT).isValid()); + + _isBodyPartTracked._hips = _isBodyPartTracked._feet && // Hips can't be tracked unless feet are tracked. + getControllerPoseInSensorFrame(controller::Action::HIPS).isValid(); + } + + // Recenter the body when foot tracking starts or ends. + { + static bool prevFeetWereTracked = _isBodyPartTracked._feet; + if (_isBodyPartTracked._feet != prevFeetWereTracked) { + centerBodyInternal(false); + prevFeetWereTracked = _isBodyPartTracked._feet; + } + } + // put the average hand azimuth into sensor space. // then mix it with head facing direction to determine rotation recenter int spine2Index = _skeletonModel->getRig().indexOfJoint("Spine2"); - if (getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && !(spine2Index < 0)) { + if (_isBodyPartTracked._leftHand && _isBodyPartTracked._rightHand && !(spine2Index < 0)) { // use the spine for the azimuth origin. glm::quat spine2Rot = getAbsoluteJointRotationInObjectFrame(spine2Index); @@ -2760,8 +2785,7 @@ void MyAvatar::prepareForPhysicsSimulation() { _characterController.setScaleFactor(getSensorToWorldScale()); _characterController.setPositionAndOrientation(getWorldPosition(), getWorldOrientation()); - auto headPose = getControllerPoseInAvatarFrame(controller::Action::HEAD); - if (headPose.isValid()) { + if (_isBodyPartTracked._head) { _follow.prePhysicsUpdate(*this, deriveBodyFromHMDSensor(), _bodySensorMatrix, hasDriveInput()); } else { _follow.deactivate(); @@ -5146,7 +5170,7 @@ float MyAvatar::computeStandingHeightMode(const controller::Pose& head) { modeInMeters = ((float)mode) / CENTIMETERS_PER_METER; if (!(modeInMeters > getCurrentStandingHeight())) { // if not greater check for a reset - if (getResetMode() && getControllerPoseInAvatarFrame(controller::Action::HEAD).isValid()) { + if (getResetMode() && _isBodyPartTracked._head) { setResetMode(false); float resetModeInCentimeters = glm::floor((head.getTranslation().y - MODE_CORRECTION_FACTOR)*CENTIMETERS_PER_METER); modeInMeters = (resetModeInCentimeters / CENTIMETERS_PER_METER); @@ -5746,8 +5770,6 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput) { - const bool feetAreTracked = myAvatar.areFeetTracked(); - if (myAvatar.getHMDLeanRecenterEnabled()) { // Rotation recenter @@ -5763,8 +5785,8 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, // Horizontal and rotation recenter - if ((feetAreTracked || getForceActivateHorizontal()) && !isActive(CharacterController::FollowType::Horizontal)) { - activate(CharacterController::FollowType::Horizontal, feetAreTracked); + if ((myAvatar.areFeetTracked() || getForceActivateHorizontal()) && !isActive(CharacterController::FollowType::Horizontal)) { + activate(CharacterController::FollowType::Horizontal, myAvatar.areFeetTracked()); setForceActivateHorizontal(false); } else { if ((myAvatar.getAllowAvatarLeaningPreference() != MyAvatar::AllowAvatarLeaningPreference::AlwaysNoRecenter) && @@ -7030,21 +7052,10 @@ bool MyAvatar::isAllowedToLean() const { !getIsInSittingState()); } -// Determine if the feet are under direct control (tracked). -bool MyAvatar::areFeetTracked() const { - // Foot tracking only activates when both feet are tracked, so we only need to test one. - return getControllerPoseInSensorFrame(controller::Action::LEFT_FOOT).isValid(); -} - -// Determine if the hips are under direct control (tracked). -bool MyAvatar::areHipsTracked() const { - return getControllerPoseInSensorFrame(controller::Action::HIPS).isValid(); -} - // Determine if crouch recentering is enabled (making the avatar stand when the user is sitting in the real world). bool MyAvatar::getHMDCrouchRecenterEnabled() const { return (!_characterController.getSeated() && - (_allowAvatarStandingPreference.get() == AllowAvatarStandingPreference::Always) && !areFeetTracked()); + (_allowAvatarStandingPreference.get() == AllowAvatarStandingPreference::Always) && !_isBodyPartTracked._feet); } bool MyAvatar::setPointAt(const glm::vec3& pointAtTarget) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index c06a1a88ab..99d5759011 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -2008,8 +2008,8 @@ public: bool isJumping(); bool getHMDCrouchRecenterEnabled() const; bool isAllowedToLean() const; - bool areFeetTracked() const; - bool areHipsTracked() const; + bool areFeetTracked() const { return _isBodyPartTracked._feet; }; // Determine if the feet are under direct control. + bool areHipsTracked() const { return _isBodyPartTracked._hips; }; // Determine if the hips are under direct control. public slots: @@ -2730,6 +2730,15 @@ private: bool _isBraking { false }; bool _isAway { false }; + // Indicates which parts of the body are under direct control (tracked). + struct { + bool _feet{ false }; // Left or right foot. + bool _hips{ false }; + bool _leftHand{ false }; + bool _rightHand{ false }; + bool _head{ false }; + } _isBodyPartTracked; + float _boomLength { ZOOM_DEFAULT }; float _yawSpeed; // degrees/sec float _pitchSpeed; // degrees/sec