Merge pull request #13672 from luiscuenca/fixElbowPoleVector

Redesign and enable hand's Pole Vectors for elbow simulation
This commit is contained in:
Anthony Thibault 2018-07-26 11:51:33 -07:00 committed by GitHub
commit 6c92949aed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 119 additions and 60 deletions

View file

@ -613,6 +613,8 @@ public:
const MyHead* getMyHead() const;
Q_INVOKABLE void toggleSmoothPoleVectors() { _skeletonModel->getRig().toggleSmoothPoleVectors(); };
/**jsdoc
* Get the current position of the avatar's "Head" joint.
* @function MyAvatar.getHeadPosition

View file

@ -1247,11 +1247,9 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo,
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix) {
const bool ENABLE_POLE_VECTORS = false;
const bool ENABLE_POLE_VECTORS = true;
const float ELBOW_POLE_VECTOR_BLEND_FACTOR = 0.95f;
int hipsIndex = indexOfJoint("Hips");
if (leftHandEnabled) {
glm::vec3 handPosition = leftHandPose.trans();
@ -1270,22 +1268,33 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
int handJointIndex = _animSkeleton->nameToJointIndex("LeftHand");
int armJointIndex = _animSkeleton->nameToJointIndex("LeftArm");
int elbowJointIndex = _animSkeleton->nameToJointIndex("LeftForeArm");
if (ENABLE_POLE_VECTORS && !leftArmEnabled && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
glm::vec3 poleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, hipsIndex, true);
glm::vec3 sensorPoleVector = transformVectorFast(rigToSensorMatrix, poleVector);
int oppositeArmJointIndex = _animSkeleton->nameToJointIndex("RightArm");
if (ENABLE_POLE_VECTORS && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0 && oppositeArmJointIndex >= 0) {
glm::vec3 poleVector;
bool usePoleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, oppositeArmJointIndex, poleVector);
if (usePoleVector) {
glm::vec3 sensorPoleVector = transformVectorFast(rigToSensorMatrix, poleVector);
// smooth toward desired pole vector from previous pole vector... to reduce jitter
if (!_prevLeftHandPoleVectorValid) {
_prevLeftHandPoleVectorValid = true;
_prevLeftHandPoleVector = sensorPoleVector;
if (_smoothPoleVectors) {
// smooth toward desired pole vector from previous pole vector... to reduce jitter
if (!_prevLeftHandPoleVectorValid) {
_prevLeftHandPoleVectorValid = true;
_prevLeftHandPoleVector = sensorPoleVector;
}
glm::quat deltaRot = rotationBetween(_prevLeftHandPoleVector, sensorPoleVector);
glm::quat smoothDeltaRot = safeMix(deltaRot, Quaternions::IDENTITY, ELBOW_POLE_VECTOR_BLEND_FACTOR);
_prevLeftHandPoleVector = smoothDeltaRot * _prevLeftHandPoleVector;
} else {
_prevLeftHandPoleVector = sensorPoleVector;
}
_animVars.set("leftHandPoleVectorEnabled", true);
_animVars.set("leftHandPoleReferenceVector", Vectors::UNIT_X);
_animVars.set("leftHandPoleVector", transformVectorFast(sensorToRigMatrix, _prevLeftHandPoleVector));
} else {
_prevLeftHandPoleVectorValid = false;
_animVars.set("leftHandPoleVectorEnabled", false);
}
glm::quat deltaRot = rotationBetween(_prevLeftHandPoleVector, sensorPoleVector);
glm::quat smoothDeltaRot = safeMix(deltaRot, Quaternions::IDENTITY, ELBOW_POLE_VECTOR_BLEND_FACTOR);
_prevLeftHandPoleVector = smoothDeltaRot * _prevLeftHandPoleVector;
_animVars.set("leftHandPoleVectorEnabled", true);
_animVars.set("leftHandPoleReferenceVector", Vectors::UNIT_X);
_animVars.set("leftHandPoleVector", transformVectorFast(sensorToRigMatrix, _prevLeftHandPoleVector));
} else {
_prevLeftHandPoleVectorValid = false;
_animVars.set("leftHandPoleVectorEnabled", false);
@ -1297,7 +1306,6 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
_animVars.unset("leftHandPosition");
_animVars.unset("leftHandRotation");
_animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
}
if (rightHandEnabled) {
@ -1318,22 +1326,34 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
int handJointIndex = _animSkeleton->nameToJointIndex("RightHand");
int armJointIndex = _animSkeleton->nameToJointIndex("RightArm");
int elbowJointIndex = _animSkeleton->nameToJointIndex("RightForeArm");
if (ENABLE_POLE_VECTORS && !rightArmEnabled && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
glm::vec3 poleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, hipsIndex, false);
glm::vec3 sensorPoleVector = transformVectorFast(rigToSensorMatrix, poleVector);
int oppositeArmJointIndex = _animSkeleton->nameToJointIndex("LeftArm");
// smooth toward desired pole vector from previous pole vector... to reduce jitter
if (!_prevRightHandPoleVectorValid) {
_prevRightHandPoleVectorValid = true;
_prevRightHandPoleVector = sensorPoleVector;
if (ENABLE_POLE_VECTORS && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0 && oppositeArmJointIndex >= 0) {
glm::vec3 poleVector;
bool usePoleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, oppositeArmJointIndex, poleVector);
if (usePoleVector) {
glm::vec3 sensorPoleVector = transformVectorFast(rigToSensorMatrix, poleVector);
if (_smoothPoleVectors) {
// smooth toward desired pole vector from previous pole vector... to reduce jitter
if (!_prevRightHandPoleVectorValid) {
_prevRightHandPoleVectorValid = true;
_prevRightHandPoleVector = sensorPoleVector;
}
glm::quat deltaRot = rotationBetween(_prevRightHandPoleVector, sensorPoleVector);
glm::quat smoothDeltaRot = safeMix(deltaRot, Quaternions::IDENTITY, ELBOW_POLE_VECTOR_BLEND_FACTOR);
_prevRightHandPoleVector = smoothDeltaRot * _prevRightHandPoleVector;
} else {
_prevRightHandPoleVector = sensorPoleVector;
}
_animVars.set("rightHandPoleVectorEnabled", true);
_animVars.set("rightHandPoleReferenceVector", -Vectors::UNIT_X);
_animVars.set("rightHandPoleVector", transformVectorFast(sensorToRigMatrix, _prevRightHandPoleVector));
} else {
_prevRightHandPoleVectorValid = false;
_animVars.set("rightHandPoleVectorEnabled", false);
}
glm::quat deltaRot = rotationBetween(_prevRightHandPoleVector, sensorPoleVector);
glm::quat smoothDeltaRot = safeMix(deltaRot, Quaternions::IDENTITY, ELBOW_POLE_VECTOR_BLEND_FACTOR);
_prevRightHandPoleVector = smoothDeltaRot * _prevRightHandPoleVector;
_animVars.set("rightHandPoleVectorEnabled", true);
_animVars.set("rightHandPoleReferenceVector", -Vectors::UNIT_X);
_animVars.set("rightHandPoleVector", transformVectorFast(sensorToRigMatrix, _prevRightHandPoleVector));
} else {
_prevRightHandPoleVectorValid = false;
_animVars.set("rightHandPoleVectorEnabled", false);
@ -1472,39 +1492,73 @@ static glm::quat quatLerp(const glm::quat& q1, const glm::quat& q2, float alpha)
return glm::normalize(glm::lerp(q1, temp, alpha));
}
glm::vec3 Rig::calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex, int hipsIndex, bool isLeft) const {
AnimPose hipsPose = _externalPoseSet._absolutePoses[hipsIndex];
bool Rig::calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex, int oppositeArmIndex, glm::vec3& poleVector) const {
// The resulting Pole Vector is calculated as the sum of a three vectors.
// The first is the vector with direction shoulder-hand. The module of this vector is inversely proportional to the strength of the resulting Pole Vector.
// The second vector is always perpendicular to previous vector and is part of the plane that contains a point located on the horizontal line,
// pointing forward and with height aprox to the avatar head. The position of the horizontal point will be determined by the hands Y component.
// The third vector apply a weighted correction to the resulting pole vector to avoid interpenetration and force a more natural pose.
AnimPose oppositeArmPose = _externalPoseSet._absolutePoses[oppositeArmIndex];
AnimPose handPose = _externalPoseSet._absolutePoses[handIndex];
AnimPose elbowPose = _externalPoseSet._absolutePoses[elbowIndex];
AnimPose armPose = _externalPoseSet._absolutePoses[armIndex];
AnimPose elbowPose = _externalPoseSet._absolutePoses[elbowIndex];
// ray from hand to arm.
glm::vec3 d = glm::normalize(handPose.trans() - armPose.trans());
glm::vec3 armToHand = handPose.trans() - armPose.trans();
glm::vec3 armToElbow = elbowPose.trans() - armPose.trans();
glm::vec3 elbowToHand = handPose.trans() - elbowPose.trans();
float sign = isLeft ? 1.0f : -1.0f;
glm::vec3 backVector = oppositeArmPose.trans() - armPose.trans();
glm::vec3 backCenter = armPose.trans() + 0.5f * backVector;
const float OVER_BACK_HEAD_PERCENTAGE = 0.2f;
// form a plane normal to the hips x-axis.
glm::vec3 n = hipsPose.rot() * Vectors::UNIT_X;
glm::vec3 y = hipsPose.rot() * Vectors::UNIT_Y;
glm::vec3 headCenter = backCenter + glm::vec3(0, OVER_BACK_HEAD_PERCENTAGE * backVector.length(), 0);
glm::vec3 frontVector = glm::normalize(glm::cross(backVector, glm::vec3(0, 1, 0)));
// Make sure is pointing forward
frontVector = frontVector.z < 0 ? -frontVector : frontVector;
// project d onto this plane
glm::vec3 dProj = d - glm::dot(d, n) * n;
float horizontalModule = glm::dot(armToHand, glm::vec3(0, -1, 0));
glm::vec3 headForward = headCenter + horizontalModule * frontVector;
// give dProj a bit of offset away from the body.
float avatarScale = extractUniformScale(_modelOffset);
const float LATERAL_OFFSET = 1.0f * avatarScale;
const float VERTICAL_OFFSET = -0.333f * avatarScale;
glm::vec3 dProjWithOffset = dProj + sign * LATERAL_OFFSET * n + y * VERTICAL_OFFSET;
glm::vec3 armToHead = headForward - armPose.trans();
float armToHandDistance = glm::length(armToHand);
float armToElbowDistance = glm::length(armToElbow);
float elbowToHandDistance = glm::length(elbowToHand);
float armTotalDistance = armToElbowDistance + elbowToHandDistance;
// rotate dProj by 90 degrees to get the poleVector.
glm::vec3 poleVector = glm::angleAxis(PI / 2.0f, n) * dProjWithOffset;
glm::vec3 armToHandDir = armToHand / armToHandDistance;
glm::vec3 armToHeadPlaneNormal = glm::cross(armToHead, armToHandDir);
// blend the wrist oreintation into the pole vector to reduce the painfully bent wrist problem.
glm::quat elbowToHandDelta = handPose.rot() * glm::inverse(elbowPose.rot());
const float WRIST_POLE_ADJUST_FACTOR = 0.5f;
glm::quat poleAdjust = quatLerp(Quaternions::IDENTITY, elbowToHandDelta, WRIST_POLE_ADJUST_FACTOR);
// How much the hand is reaching for the opposite side
float oppositeProjection = glm::dot(armToHandDir, glm::normalize(backVector));
// Don't use pole vector when the hands are behind
if (glm::dot(frontVector, armToHand) < 0 && oppositeProjection < 0.5f * armTotalDistance) {
return false;
}
return glm::normalize(poleAdjust * poleVector);
// The strenght of the resulting pole determined by the arm flex.
float armFlexCoeficient = armToHandDistance / armTotalDistance;
glm::vec3 attenuationVector = armFlexCoeficient * armToHandDir;
// Pole vector is perpendicular to the shoulder-hand direction and located on the plane that contains the head-forward line
glm::vec3 fullPoleVector = glm::normalize(glm::cross(armToHeadPlaneNormal, armToHandDir));
// Push elbow forward when hand reaches opposite side
glm::vec3 correctionVector = glm::vec3(0, 0, 0);
const float FORWARD_TRIGGER_PERCENTAGE = 0.2f;
const float FORWARD_CORRECTOR_WEIGHT = 3.0f;
float elbowForwardTrigger = FORWARD_TRIGGER_PERCENTAGE * armToHandDistance;
if (oppositeProjection > -elbowForwardTrigger) {
float forwardAmount = FORWARD_CORRECTOR_WEIGHT * (elbowForwardTrigger + oppositeProjection);
correctionVector = forwardAmount * frontVector;
}
poleVector = glm::normalize(attenuationVector + fullPoleVector + correctionVector);
return true;
}
glm::vec3 Rig::calculateKneePoleVector(int footJointIndex, int kneeIndex, int upLegIndex, int hipsIndex, const AnimPose& targetFootPose) const {

View file

@ -217,7 +217,7 @@ public:
// input assumed to be in rig space
void computeHeadFromHMD(const AnimPose& hmdPose, glm::vec3& headPositionOut, glm::quat& headOrientationOut) const;
void toggleSmoothPoleVectors() { _smoothPoleVectors = !_smoothPoleVectors; };
signals:
void onLoadComplete();
@ -240,11 +240,12 @@ protected:
void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::vec3& lookAt, const glm::vec3& saccade);
void calcAnimAlpha(float speed, const std::vector<float>& referenceSpeeds, float* alphaOut) const;
glm::vec3 calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex, int hipsIndex, bool isLeft) const;
bool calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex, int oppositeArmIndex, glm::vec3& poleVector) const;
glm::vec3 calculateKneePoleVector(int footJointIndex, int kneeJoint, int upLegIndex, int hipsIndex, const AnimPose& targetFootPose) const;
glm::vec3 deflectHandFromTorso(const glm::vec3& handPosition, const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo) const;
AnimPose _modelOffset; // model to rig space
AnimPose _geometryOffset; // geometry to model space (includes unit offset & fst offsets)
AnimPose _invGeometryOffset;
@ -368,11 +369,13 @@ protected:
glm::vec3 _prevLeftFootPoleVector { Vectors::UNIT_Z }; // sensor space
bool _prevLeftFootPoleVectorValid { false };
glm::vec3 _prevRightHandPoleVector { -Vectors::UNIT_Z }; // sensor space
bool _prevRightHandPoleVectorValid { false };
glm::vec3 _prevRightHandPoleVector{ -Vectors::UNIT_Z }; // sensor space
bool _prevRightHandPoleVectorValid{ false };
glm::vec3 _prevLeftHandPoleVector { -Vectors::UNIT_Z }; // sensor space
bool _prevLeftHandPoleVectorValid { false };
glm::vec3 _prevLeftHandPoleVector{ -Vectors::UNIT_Z }; // sensor space
bool _prevLeftHandPoleVectorValid{ false };
bool _smoothPoleVectors { false };
int _rigId;
};