Fix for pole vector stability and knee pole vector computation

This commit is contained in:
Anthony J. Thibault 2017-06-23 10:38:21 -07:00
parent 79d9bbe6c5
commit 54af6af651
3 changed files with 71 additions and 38 deletions

View file

@ -530,22 +530,33 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
if (dLen > EPSILON) { if (dLen > EPSILON) {
glm::vec3 dUnit = d / dLen; glm::vec3 dUnit = d / dLen;
glm::vec3 e = midPose.xformVector(target.getPoleReferenceVector()); glm::vec3 e = midPose.xformVector(target.getPoleReferenceVector());
glm::vec3 p = target.getPoleVector();
glm::vec3 eProj = e - glm::dot(e, dUnit) * dUnit; glm::vec3 eProj = e - glm::dot(e, dUnit) * dUnit;
glm::vec3 pProj = p - glm::dot(p, dUnit) * dUnit;
float eProjLen = glm::length(eProj); float eProjLen = glm::length(eProj);
float pProjLen = glm::length(pProj);
if (eProjLen > EPSILON && pProjLen > EPSILON) {
const float MIN_EPROJ_LEN = 0.5f;
if (eProjLen < MIN_EPROJ_LEN) {
glm::vec3 midPoint = topPose.trans() + d * 0.5f;
e = midPose.trans() - midPoint;
eProj = e - glm::dot(e, dUnit) * dUnit;
eProjLen = glm::length(eProj);
}
glm::vec3 p = target.getPoleVector();
glm::vec3 pProj = p - glm::dot(p, dUnit) * dUnit;
float pProjLen = glm::length(pProj);
if (eProjLen > EPSILON && pProjLen > EPSILON) {
// as pProjLen become orthognal to d, reduce the amount of rotation. // as pProjLen become orthognal to d, reduce the amount of rotation.
float magnitude = easeOutExpo(pProjLen); float magnitude = easeOutExpo(pProjLen);
float dot = glm::clamp(glm::dot(eProj / eProjLen, pProj / pProjLen), 0.0f, 1.0f); float dot = glm::clamp(glm::dot(eProj / eProjLen, pProj / pProjLen), 0.0f, 1.0f);
float theta = acosf(dot); float theta = acosf(dot);
glm::vec3 cross = glm::cross(eProj, pProj); glm::vec3 cross = glm::cross(eProj, pProj);
float crossLen = glm::length(cross); const float MIN_ADJUSTMENT_ANGLE = 0.001745f; // 0.1 degree
if (crossLen > EPSILON) { if (theta > MIN_ADJUSTMENT_ANGLE) {
glm::vec3 axis = cross / crossLen; glm::vec3 axis = dUnit;
if (glm::dot(cross, dUnit) < 0) {
axis = -dUnit;
}
poleRot = glm::angleAxis(magnitude * theta, axis); poleRot = glm::angleAxis(magnitude * theta, axis);
} }
} }
@ -562,8 +573,17 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
glm::vec3 dUnit = d / dLen; glm::vec3 dUnit = d / dLen;
glm::vec3 e = midPose.xformVector(target.getPoleReferenceVector()); glm::vec3 e = midPose.xformVector(target.getPoleReferenceVector());
glm::vec3 p = target.getPoleVector();
glm::vec3 eProj = e - glm::dot(e, dUnit) * dUnit; glm::vec3 eProj = e - glm::dot(e, dUnit) * dUnit;
float eProjLen = glm::length(eProj);
const float MIN_EPROJ_LEN = 0.5f;
if (eProjLen < MIN_EPROJ_LEN) {
glm::vec3 midPoint = topPose.trans() + d * 0.5f;
e = midPose.trans() - midPoint;
eProj = e - glm::dot(e, dUnit) * dUnit;
eProjLen = glm::length(eProj);
}
glm::vec3 p = target.getPoleVector();
glm::vec3 pProj = p - glm::dot(p, dUnit) * dUnit; glm::vec3 pProj = p - glm::dot(p, dUnit) * dUnit;
const float PROJ_VECTOR_LEN = 10.0f; const float PROJ_VECTOR_LEN = 10.0f;
@ -573,11 +593,8 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
geomToWorldPose.xformPoint(topPose.trans()), geomToWorldPose.xformPoint(topPose.trans()),
YELLOW); YELLOW);
DebugDraw::getInstance().drawRay(geomToWorldPose.xformPoint(midPoint), DebugDraw::getInstance().drawRay(geomToWorldPose.xformPoint(midPoint),
geomToWorldPose.xformPoint(midPoint + PROJ_VECTOR_LEN * glm::normalize(eProj)), geomToWorldPose.xformPoint(midPoint + PROJ_VECTOR_LEN * glm::normalize(e)),
RED); RED);
DebugDraw::getInstance().drawRay(geomToWorldPose.xformPoint(midPoint),
geomToWorldPose.xformPoint(midPoint + PROJ_VECTOR_LEN * glm::normalize(pProj)),
GREEN);
DebugDraw::getInstance().drawRay(geomToWorldPose.xformPoint(midPoint), DebugDraw::getInstance().drawRay(geomToWorldPose.xformPoint(midPoint),
geomToWorldPose.xformPoint(midPoint + POLE_VECTOR_LEN * glm::normalize(p)), geomToWorldPose.xformPoint(midPoint + POLE_VECTOR_LEN * glm::normalize(p)),
BLUE); BLUE);
@ -1060,7 +1077,7 @@ void AnimInverseKinematics::initConstraints() {
// y | // y |
// | | // | |
// | O---O---O RightUpLeg // | O---O---O RightUpLeg
// z | | Hips2 | // z | | Hips |
// \ | | | // \ | | |
// \| | | // \| | |
// x -----+ O O RightLeg // x -----+ O O RightLeg

View file

@ -1265,14 +1265,14 @@ void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose
int hipsIndex = indexOfJoint("Hips"); int hipsIndex = indexOfJoint("Hips");
if (leftFootEnabled) { if (leftFootEnabled) {
glm::vec3 footPosition = leftFootPose.trans(); _animVars.set("leftFootPosition", leftFootPose.trans());
glm::quat footRotation = leftFootPose.rot(); _animVars.set("leftFootRotation", leftFootPose.rot());
_animVars.set("leftFootPosition", footPosition);
_animVars.set("leftFootRotation", footRotation);
_animVars.set("leftFootType", (int)IKTarget::Type::RotationAndPosition); _animVars.set("leftFootType", (int)IKTarget::Type::RotationAndPosition);
int footJointIndex = _animSkeleton->nameToJointIndex("LeftFoot"); int footJointIndex = _animSkeleton->nameToJointIndex("LeftFoot");
glm::vec3 poleVector = calculateKneePoleVector(footJointIndex, footRotation, hipsIndex); int kneeJointIndex = _animSkeleton->nameToJointIndex("LeftLeg");
int upLegJointIndex = _animSkeleton->nameToJointIndex("LeftUpLeg");
glm::vec3 poleVector = calculateKneePoleVector(footJointIndex, kneeJointIndex, upLegJointIndex, hipsIndex, leftFootPose);
// smooth toward desired pole vector from previous pole vector... to reduce jitter // smooth toward desired pole vector from previous pole vector... to reduce jitter
if (!_prevLeftFootPoleVectorValid) { if (!_prevLeftFootPoleVectorValid) {
@ -1295,14 +1295,14 @@ void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose
} }
if (rightFootEnabled) { if (rightFootEnabled) {
glm::vec3 footPosition = rightFootPose.trans(); _animVars.set("rightFootPosition", rightFootPose.trans());
glm::quat footRotation = rightFootPose.rot(); _animVars.set("rightFootRotation", rightFootPose.rot());
_animVars.set("rightFootPosition", footPosition);
_animVars.set("rightFootRotation", footRotation);
_animVars.set("rightFootType", (int)IKTarget::Type::RotationAndPosition); _animVars.set("rightFootType", (int)IKTarget::Type::RotationAndPosition);
int footJointIndex = _animSkeleton->nameToJointIndex("RightFoot"); int footJointIndex = _animSkeleton->nameToJointIndex("RightFoot");
glm::vec3 poleVector = calculateKneePoleVector(footJointIndex, footRotation, hipsIndex); int kneeJointIndex = _animSkeleton->nameToJointIndex("RightLeg");
int upLegJointIndex = _animSkeleton->nameToJointIndex("RightUpLeg");
glm::vec3 poleVector = calculateKneePoleVector(footJointIndex, kneeJointIndex, upLegJointIndex, hipsIndex, rightFootPose);
// smooth toward desired pole vector from previous pole vector... to reduce jitter // smooth toward desired pole vector from previous pole vector... to reduce jitter
if (!_prevRightFootPoleVectorValid) { if (!_prevRightFootPoleVectorValid) {
@ -1375,20 +1375,23 @@ glm::vec3 Rig::calculateElbowPoleVector(int handIndex, int elbowIndex, int armIn
AnimPose handPose = _externalPoseSet._absolutePoses[handIndex]; AnimPose handPose = _externalPoseSet._absolutePoses[handIndex];
AnimPose elbowPose = _externalPoseSet._absolutePoses[elbowIndex]; AnimPose elbowPose = _externalPoseSet._absolutePoses[elbowIndex];
AnimPose armPose = _externalPoseSet._absolutePoses[armIndex]; AnimPose armPose = _externalPoseSet._absolutePoses[armIndex];
// ray from hand to arm.
glm::vec3 d = glm::normalize(handPose.trans() - armPose.trans()); glm::vec3 d = glm::normalize(handPose.trans() - armPose.trans());
float sign = isLeft ? 1.0f : -1.0f; float sign = isLeft ? 1.0f : -1.0f;
// form a plane normal to the hips x-axis.
glm::vec3 n = hipsPose.rot() * Vectors::UNIT_X; glm::vec3 n = hipsPose.rot() * Vectors::UNIT_X;
glm::vec3 y = hipsPose.rot() * Vectors::UNIT_Y; glm::vec3 y = hipsPose.rot() * Vectors::UNIT_Y;
// project d onto n.
// project d onto this plane
glm::vec3 dProj = d - glm::dot(d, n) * n; glm::vec3 dProj = d - glm::dot(d, n) * n;
// give dProj a bit of offset away from the body.
float avatarScale = extractUniformScale(_modelOffset); float avatarScale = extractUniformScale(_modelOffset);
const float LATERAL_OFFSET = 0.333f * avatarScale; const float LATERAL_OFFSET = 0.333f * avatarScale;
const float VERTICAL_OFFSET = -0.333f * avatarScale; const float VERTICAL_OFFSET = -0.333f * avatarScale;
// give dProj a bit of offset away from the body.
glm::vec3 dProjWithOffset = dProj + sign * LATERAL_OFFSET * n + y * VERTICAL_OFFSET; glm::vec3 dProjWithOffset = dProj + sign * LATERAL_OFFSET * n + y * VERTICAL_OFFSET;
// rotate dProj by 90 degrees to get the poleVector. // rotate dProj by 90 degrees to get the poleVector.
@ -1402,19 +1405,32 @@ glm::vec3 Rig::calculateElbowPoleVector(int handIndex, int elbowIndex, int armIn
return glm::normalize(poleAdjust * poleVector); return glm::normalize(poleAdjust * poleVector);
} }
glm::vec3 Rig::calculateKneePoleVector(int footJointIndex, const glm::quat& footTargetOrientation, int hipsIndex) const { glm::vec3 Rig::calculateKneePoleVector(int footJointIndex, int kneeIndex, int upLegIndex, int hipsIndex, const AnimPose& targetFootPose) const {
AnimPose defaultPose = _animSkeleton->getAbsoluteDefaultPose(footJointIndex); AnimPose hipsPose = _externalPoseSet._absolutePoses[hipsIndex];
glm::vec3 localForward = glm::inverse(defaultPose.rot()) * Vectors::UNIT_Z; AnimPose footPose = targetFootPose;
glm::vec3 footForward = footTargetOrientation * localForward; AnimPose kneePose = _externalPoseSet._absolutePoses[kneeIndex];
AnimPose upLegPose = _externalPoseSet._absolutePoses[upLegIndex];
// compute the forward direction of the hips. // ray from foot to upLeg
glm::quat hipsRotation = _externalPoseSet._absolutePoses[hipsIndex].rot(); glm::vec3 d = glm::normalize(footPose.trans() - upLegPose.trans());
glm::vec3 hipsForward = hipsRotation * Vectors::UNIT_Z;
// blend between the hips and the foot. // form a plane normal to the hips x-axis
const float FOOT_TO_HIPS_BLEND_FACTOR = 0.5f; glm::vec3 n = hipsPose.rot() * Vectors::UNIT_X;
return glm::normalize(lerp(footForward, hipsForward, FOOT_TO_HIPS_BLEND_FACTOR)); glm::vec3 y = hipsPose.rot() * Vectors::UNIT_Y;
// project d onto this plane
glm::vec3 dProj = d - glm::dot(d, n) * n;
// rotate dProj by 90 degrees to get the poleVector.
glm::vec3 poleVector = glm::angleAxis(-PI / 2.0f, n) * dProj;
// blend the foot oreintation into the pole vector
glm::quat kneeToFootDelta = footPose.rot() * glm::inverse(kneePose.rot());
const float WRIST_POLE_ADJUST_FACTOR = 0.5f;
glm::quat poleAdjust = quatLerp(Quaternions::IDENTITY, kneeToFootDelta, WRIST_POLE_ADJUST_FACTOR);
return glm::normalize(poleAdjust * poleVector);
} }
void Rig::updateFromControllerParameters(const ControllerParameters& params, float dt) { void Rig::updateFromControllerParameters(const ControllerParameters& params, float dt) {

View file

@ -239,7 +239,7 @@ protected:
void calcAnimAlpha(float speed, const std::vector<float>& referenceSpeeds, float* alphaOut) const; 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; glm::vec3 calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex, int hipsIndex, bool isLeft) const;
glm::vec3 calculateKneePoleVector(int footJointIndex, const glm::quat& footTargetOrientation, int hipsIndex) const; glm::vec3 calculateKneePoleVector(int footJointIndex, int kneeJoint, int upLegIndex, int hipsIndex, const AnimPose& targetFootPose) const;
AnimPose _modelOffset; // model to rig space AnimPose _modelOffset; // model to rig space
AnimPose _geometryOffset; // geometry to model space (includes unit offset & fst offsets) AnimPose _geometryOffset; // geometry to model space (includes unit offset & fst offsets)