diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index dc0262e9f0..33523fc907 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1378,7 +1378,7 @@ void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) { hipsPosition.z *= -1.0f; maxHipsOffsetRadius = _characterController.measureMaxHipsOffsetRadius(hipsPosition, maxHipsOffsetRadius); } - _rig->setMaxHipsOffsetLength(maxHipsOffsetRadius); + _rig->updateMaxHipsOffsetLength(maxHipsOffsetRadius, deltaTime); glm::vec3 position = getPosition(); glm::quat orientation = getOrientation(); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 36f74b8784..b2bf5f64fc 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -149,6 +149,17 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->updateFromHeadParameters(headParams, deltaTime); + // OUTOFBODY_HACK: clamp horizontal component of head by maxHipsOffset. + // This is to prevent the hands from being incorrect relative to the head because + // the hips are being constrained by a small maxHipsOffset due to collision. + if (myAvatar->isOutOfBody()) { + float headOffsetLength2D = glm::length(glm::vec2(truncatedHMDPositionInRigSpace.x, truncatedHMDPositionInRigSpace.z)); + if (headOffsetLength2D > _rig->getMaxHipsOffsetLength()) { + truncatedHMDPositionInRigSpace.x *= _rig->getMaxHipsOffsetLength() / headOffsetLength2D; + truncatedHMDPositionInRigSpace.z *= _rig->getMaxHipsOffsetLength() / headOffsetLength2D; + } + } + Rig::HandParameters handParams; auto leftPose = myAvatar->getLeftHandControllerPoseInAvatarFrame(); diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 72483d21b1..4dca0aa6a8 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -392,10 +392,6 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars dt = MAX_OVERLAY_DT; } - // OUTOFBODY_HACK: smoothly update update _hipsOffsetLength, otherwise we risk introducing oscillation in the hips offset. - float tau = dt / 0.5f; - _maxHipsOffsetLength = (1.0f - tau) * _maxHipsOffsetLength + tau * _desiredMaxHipsOffsetLength; - if (_relativePoses.size() != underPoses.size()) { loadPoses(underPoses); } else { @@ -585,7 +581,7 @@ void AnimInverseKinematics::clearIKJointLimitHistory() { void AnimInverseKinematics::setMaxHipsOffsetLength(float maxLength) { // OUTOFBODY_HACK: manually adjust scale here const float METERS_TO_CENTIMETERS = 100.0f; - _desiredMaxHipsOffsetLength = METERS_TO_CENTIMETERS * maxLength; + _maxHipsOffsetLength = METERS_TO_CENTIMETERS * maxLength; } RotationConstraint* AnimInverseKinematics::getConstraint(int index) { diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index 4c8d29c7ed..b61343a97b 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -85,7 +85,6 @@ protected: // experimental data for moving hips during IK glm::vec3 _hipsOffset { Vectors::ZERO }; - float _desiredMaxHipsOffsetLength { 1.0f }; float _maxHipsOffsetLength { 1.0f }; int _headIndex { -1 }; int _hipsIndex { -1 }; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 9f77ce4772..05e589918a 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -307,18 +307,30 @@ void Rig::clearIKJointLimitHistory() { } } -void Rig::setMaxHipsOffsetLength(float maxLength) { +void Rig::updateMaxHipsOffsetLength(float maxLength, float deltaTime) { + + _desiredMaxHipsOffsetLength = maxLength; + + // OUTOFBODY_HACK: smoothly update update _hipsOffsetLength, otherwise we risk introducing oscillation in the hips offset. + const float MAX_HIPS_OFFSET_TIMESCALE = 0.33f; + float tau = deltaTime / MAX_HIPS_OFFSET_TIMESCALE; + _maxHipsOffsetLength = (1.0f - tau) * _maxHipsOffsetLength + tau * _desiredMaxHipsOffsetLength; + if (_animNode) { _animNode->traverse([&](AnimNode::Pointer node) { auto ikNode = std::dynamic_pointer_cast(node); if (ikNode) { - ikNode->setMaxHipsOffsetLength(maxLength); + ikNode->setMaxHipsOffsetLength(_maxHipsOffsetLength); } return true; }); } } +float Rig::getMaxHipsOffsetLength() const { + return _maxHipsOffsetLength; +} + void Rig::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) { if (isIndexValid(index)) { if (valid) { diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 4491397af0..aa1a2ad969 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -104,7 +104,8 @@ public: void clearJointAnimationPriority(int index); void clearIKJointLimitHistory(); - void setMaxHipsOffsetLength(float maxLength); + void updateMaxHipsOffsetLength(float maxLength, float deltaTime); + float getMaxHipsOffsetLength() const; // geometry space void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority); @@ -319,6 +320,9 @@ protected: bool _truncateIKTargets { false }; bool _enableDebugDrawIKTargets { false }; + float _maxHipsOffsetLength { 1.0f }; + float _desiredMaxHipsOffsetLength { 1.0f }; + private: QMap _stateHandlers; int _nextStateHandlerId { 0 };