Fix for elbow glitches

The pole vector constraint calculation within the IK system would sometimes compute the incorrect rotations.
This would be visible as an instantaneous snap of the elbow joint as the bicep was curled.

When applying pole vector constraints, there needs to be two methods of determining the current orientation of the elbow joint.
One for when the arm/elbow joint is bent, and one for when the arm/elbow is straight.
Previously, the way we would switch between these two solutions could cause a large rotation delta to accur between very small angles.
Now we use the more accurate method (1) more often, and we smoothly blend between the solutions as the joint gets straighter.
This commit is contained in:
Anthony J. Thibault 2018-01-11 15:40:53 -08:00
parent 19106fcff9
commit 467a7eaf2d

View file

@ -591,19 +591,31 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
glm::vec3 d = basePose.trans() - topPose.trans();
float dLen = glm::length(d);
if (dLen > EPSILON) {
glm::vec3 dUnit = d / dLen;
glm::vec3 e = midPose.xformVector(target.getPoleReferenceVector());
// if mid joint is straight use the reference vector to compute eProj, otherwise use reference vector.
// however if mid joint angle is in between the two blend between both solutions.
vec3 u = normalize(basePose.trans() - midPose.trans());
vec3 v = normalize(topPose.trans() - midPose.trans());
const float LERP_THRESHOLD = 3.05433f; // 175 deg
const float BENT_THRESHOLD = 2.96706f; // 170 deg
float jointAngle = acos(dot(u, v));
if (jointAngle < BENT_THRESHOLD) {
glm::vec3 midPoint = topPose.trans() + d * 0.5f;
e = normalize(midPose.trans() - midPoint);
} else if (jointAngle < LERP_THRESHOLD) {
glm::vec3 midPoint = topPose.trans() + d * 0.5f;
float alpha = (jointAngle - LERP_THRESHOLD) / (BENT_THRESHOLD - LERP_THRESHOLD);
e = lerp(e, normalize(midPose.trans() - midPoint), alpha);
}
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;
float pProjLen = glm::length(pProj);
@ -636,15 +648,31 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
glm::vec3 dUnit = d / dLen;
glm::vec3 e = midPose.xformVector(target.getPoleReferenceVector());
// if mid joint is straight use the reference vector to compute eProj, otherwise use reference vector.
// however if mid joint angle is in between the two blend between both solutions.
vec3 u = normalize(basePose.trans() - midPose.trans());
vec3 v = normalize(topPose.trans() - midPose.trans());
const float LERP_THRESHOLD = 3.05433f; // 175 deg
const float BENT_THRESHOLD = 2.96706f; // 170 deg
float jointAngle = acos(dot(u, v));
glm::vec4 eColor = RED;
if (jointAngle < BENT_THRESHOLD) {
glm::vec3 midPoint = topPose.trans() + d * 0.5f;
e = normalize(midPose.trans() - midPoint);
eColor = GREEN;
} else if (jointAngle < LERP_THRESHOLD) {
glm::vec3 midPoint = topPose.trans() + d * 0.5f;
float alpha = (jointAngle - LERP_THRESHOLD) / (BENT_THRESHOLD - LERP_THRESHOLD);
e = lerp(e, normalize(midPose.trans() - midPoint), alpha);
eColor = YELLOW;
}
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();
const float PROJ_VECTOR_LEN = 10.0f;
@ -655,7 +683,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
YELLOW);
DebugDraw::getInstance().drawRay(geomToWorldPose.xformPoint(midPoint),
geomToWorldPose.xformPoint(midPoint + PROJ_VECTOR_LEN * glm::normalize(e)),
RED);
eColor);
DebugDraw::getInstance().drawRay(geomToWorldPose.xformPoint(midPoint),
geomToWorldPose.xformPoint(midPoint + POLE_VECTOR_LEN * glm::normalize(p)),
BLUE);