Fix for incorrect hand offset when backing into collision.

We know properly account for the offset of the head due to clamping from a small maxHipsOffset.
This means the hands should look more natural when you are out-of-body and are moving your hand controllers.
This commit is contained in:
Anthony J. Thibault 2016-09-23 11:44:36 -07:00
parent 0f60abdf4e
commit b68dbab994
6 changed files with 32 additions and 10 deletions

View file

@ -1378,7 +1378,7 @@ void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) {
hipsPosition.z *= -1.0f; hipsPosition.z *= -1.0f;
maxHipsOffsetRadius = _characterController.measureMaxHipsOffsetRadius(hipsPosition, maxHipsOffsetRadius); maxHipsOffsetRadius = _characterController.measureMaxHipsOffsetRadius(hipsPosition, maxHipsOffsetRadius);
} }
_rig->setMaxHipsOffsetLength(maxHipsOffsetRadius); _rig->updateMaxHipsOffsetLength(maxHipsOffsetRadius, deltaTime);
glm::vec3 position = getPosition(); glm::vec3 position = getPosition();
glm::quat orientation = getOrientation(); glm::quat orientation = getOrientation();

View file

@ -149,6 +149,17 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
_rig->updateFromHeadParameters(headParams, deltaTime); _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; Rig::HandParameters handParams;
auto leftPose = myAvatar->getLeftHandControllerPoseInAvatarFrame(); auto leftPose = myAvatar->getLeftHandControllerPoseInAvatarFrame();

View file

@ -392,10 +392,6 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
dt = MAX_OVERLAY_DT; 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()) { if (_relativePoses.size() != underPoses.size()) {
loadPoses(underPoses); loadPoses(underPoses);
} else { } else {
@ -585,7 +581,7 @@ void AnimInverseKinematics::clearIKJointLimitHistory() {
void AnimInverseKinematics::setMaxHipsOffsetLength(float maxLength) { void AnimInverseKinematics::setMaxHipsOffsetLength(float maxLength) {
// OUTOFBODY_HACK: manually adjust scale here // OUTOFBODY_HACK: manually adjust scale here
const float METERS_TO_CENTIMETERS = 100.0f; const float METERS_TO_CENTIMETERS = 100.0f;
_desiredMaxHipsOffsetLength = METERS_TO_CENTIMETERS * maxLength; _maxHipsOffsetLength = METERS_TO_CENTIMETERS * maxLength;
} }
RotationConstraint* AnimInverseKinematics::getConstraint(int index) { RotationConstraint* AnimInverseKinematics::getConstraint(int index) {

View file

@ -85,7 +85,6 @@ protected:
// experimental data for moving hips during IK // experimental data for moving hips during IK
glm::vec3 _hipsOffset { Vectors::ZERO }; glm::vec3 _hipsOffset { Vectors::ZERO };
float _desiredMaxHipsOffsetLength { 1.0f };
float _maxHipsOffsetLength { 1.0f }; float _maxHipsOffsetLength { 1.0f };
int _headIndex { -1 }; int _headIndex { -1 };
int _hipsIndex { -1 }; int _hipsIndex { -1 };

View file

@ -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) { if (_animNode) {
_animNode->traverse([&](AnimNode::Pointer node) { _animNode->traverse([&](AnimNode::Pointer node) {
auto ikNode = std::dynamic_pointer_cast<AnimInverseKinematics>(node); auto ikNode = std::dynamic_pointer_cast<AnimInverseKinematics>(node);
if (ikNode) { if (ikNode) {
ikNode->setMaxHipsOffsetLength(maxLength); ikNode->setMaxHipsOffsetLength(_maxHipsOffsetLength);
} }
return true; return true;
}); });
} }
} }
float Rig::getMaxHipsOffsetLength() const {
return _maxHipsOffsetLength;
}
void Rig::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) { void Rig::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) {
if (isIndexValid(index)) { if (isIndexValid(index)) {
if (valid) { if (valid) {

View file

@ -104,7 +104,8 @@ public:
void clearJointAnimationPriority(int index); void clearJointAnimationPriority(int index);
void clearIKJointLimitHistory(); void clearIKJointLimitHistory();
void setMaxHipsOffsetLength(float maxLength); void updateMaxHipsOffsetLength(float maxLength, float deltaTime);
float getMaxHipsOffsetLength() const;
// geometry space // geometry space
void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority); 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 _truncateIKTargets { false };
bool _enableDebugDrawIKTargets { false }; bool _enableDebugDrawIKTargets { false };
float _maxHipsOffsetLength { 1.0f };
float _desiredMaxHipsOffsetLength { 1.0f };
private: private:
QMap<int, StateHandler> _stateHandlers; QMap<int, StateHandler> _stateHandlers;
int _nextStateHandlerId { 0 }; int _nextStateHandlerId { 0 };