WIP: checkpoint of ik changes

This commit is contained in:
Anthony J. Thibault 2017-05-17 18:33:49 -07:00
parent c2b140cc03
commit abe19310da
3 changed files with 146 additions and 38 deletions

View file

@ -68,7 +68,7 @@
"typeVar": "rightHandType",
"weightVar": "rightHandWeight",
"weight": 1.0,
"flexCoefficients": [1, 0.5, 0.5, 0.25, 0.1, 0.05, 0.01, 0.0, 0.0]
"flexCoefficients": [1, 0.5, 0.5, 0.2, 0.00, 0.00, 0.00, 0.0, 0.0]
},
{
"jointName": "LeftHand",
@ -77,7 +77,7 @@
"typeVar": "leftHandType",
"weightVar": "leftHandWeight",
"weight": 1.0,
"flexCoefficients": [1, 0.5, 0.5, 0.25, 0.1, 0.05, 0.01, 0.0, 0.0]
"flexCoefficients": [1, 0.5, 0.5, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0]
},
{
"jointName": "RightFoot",
@ -113,7 +113,7 @@
"typeVar": "headType",
"weightVar": "headWeight",
"weight": 4.0,
"flexCoefficients": [1, 0.5, 0.5, 0.5, 0.5]
"flexCoefficients": [1, 0.35, 0.5, 0.35, 0.25]
}
]
},

View file

@ -175,7 +175,7 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::
}
}
void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<IKTarget>& targets) {
void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const AnimContext& context, const std::vector<IKTarget>& targets) {
// compute absolute poses that correspond to relative target poses
AnimPoseVec absolutePoses;
absolutePoses.resize(_relativePoses.size());
@ -194,24 +194,21 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
++numLoops;
// solve all targets
int lowestMovedIndex = (int)_relativePoses.size();
for (auto& target: targets) {
int lowIndex = solveTargetWithCCD(target, absolutePoses);
if (lowIndex < lowestMovedIndex) {
lowestMovedIndex = lowIndex;
}
bool debug = numLoops == MAX_IK_LOOPS;
solveTargetWithCCD(context, target, absolutePoses, debug);
}
// harvest accumulated rotations and apply the average
for (int i = lowestMovedIndex; i < _maxTargetIndex; ++i) {
for (int i = 0; i < (int)_relativePoses.size(); ++i) {
if (_accumulators[i].size() > 0) {
_relativePoses[i].rot() = _accumulators[i].getAverage();
_accumulators[i].clear();
}
}
// update the absolutePoses that need it (from lowestMovedIndex to _maxTargetIndex)
for (auto i = lowestMovedIndex; i <= _maxTargetIndex; ++i) {
// update the absolutePoses
for (int i = 0; i < (int)_relativePoses.size(); ++i) {
auto parentIndex = _skeleton->getParentIndex((int)i);
if (parentIndex != -1) {
absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i];
@ -236,7 +233,9 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
for (auto& target: targets) {
int tipIndex = target.getIndex();
int parentIndex = _skeleton->getParentIndex(tipIndex);
if (parentIndex != -1) {
// update rotationOnly targets that don't lie on the ik chain of other ik targets.
if (parentIndex != -1 && !_accumulators[tipIndex].isDirty() && target.getType() == IKTarget::Type::RotationOnly) {
const glm::quat& targetRotation = target.getRotation();
// compute tip's new parent-relative rotation
// Q = Qp * q --> q' = Qp^ * Q
@ -254,24 +253,23 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
}
}
int AnimInverseKinematics::solveTargetWithCCD(const IKTarget& target, AnimPoseVec& absolutePoses) {
int lowestMovedIndex = (int)_relativePoses.size();
void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const IKTarget& target, const AnimPoseVec& absolutePoses, bool debug) {
IKTarget::Type targetType = target.getType();
if (targetType == IKTarget::Type::RotationOnly) {
// the final rotation will be enforced after the iterations
// TODO: solve this correctly
return lowestMovedIndex;
return;
}
int tipIndex = target.getIndex();
int pivotIndex = _skeleton->getParentIndex(tipIndex);
if (pivotIndex == -1 || pivotIndex == _hipsIndex) {
return lowestMovedIndex;
return;
}
int pivotsParentIndex = _skeleton->getParentIndex(pivotIndex);
if (pivotsParentIndex == -1) {
// TODO?: handle case where tip's parent is root?
return lowestMovedIndex;
return;
}
// cache tip's absolute orientation
@ -281,9 +279,13 @@ int AnimInverseKinematics::solveTargetWithCCD(const IKTarget& target, AnimPoseVe
// the tip's parent-relative as we proceed up the chain
glm::quat tipParentOrientation = absolutePoses[pivotIndex].rot();
std::map<int, DebugJoint> debugJointMap;
// NOTE: if this code is removed, the head will remain rigid, causing the spine/hips to thrust forward backward
// as the head is nodded.
if (targetType == IKTarget::Type::HmdHead) {
if (targetType == IKTarget::Type::HmdHead ||
targetType == IKTarget::Type::RotationAndPosition ||
targetType == IKTarget::Type::HipsRelativeRotationAndPosition) {
// rotate tip directly to target orientation
tipOrientation = target.getRotation();
@ -291,8 +293,9 @@ int AnimInverseKinematics::solveTargetWithCCD(const IKTarget& target, AnimPoseVe
// then enforce tip's constraint
RotationConstraint* constraint = getConstraint(tipIndex);
bool constrained = false;
if (constraint) {
bool constrained = constraint->apply(tipRelativeRotation);
constrained = constraint->apply(tipRelativeRotation);
if (constrained) {
tipOrientation = tipParentOrientation * tipRelativeRotation;
tipRelativeRotation = tipRelativeRotation;
@ -300,6 +303,10 @@ int AnimInverseKinematics::solveTargetWithCCD(const IKTarget& target, AnimPoseVe
}
// store the relative rotation change in the accumulator
_accumulators[tipIndex].add(tipRelativeRotation, target.getWeight());
if (debug) {
debugJointMap[tipIndex] = DebugJoint(tipRelativeRotation, constrained);
}
}
// cache tip absolute position
@ -388,15 +395,15 @@ int AnimInverseKinematics::solveTargetWithCCD(const IKTarget& target, AnimPoseVe
// compute joint's new parent-relative rotation after swing
// Q' = dQ * Q and Q = Qp * q --> q' = Qp^ * dQ * Q
glm::quat newRot = glm::normalize(glm::inverse(
absolutePoses[pivotsParentIndex].rot()) *
deltaRotation *
absolutePoses[pivotIndex].rot());
glm::quat newRot = glm::normalize(glm::inverse(absolutePoses[pivotsParentIndex].rot()) *
deltaRotation *
absolutePoses[pivotIndex].rot());
// enforce pivot's constraint
RotationConstraint* constraint = getConstraint(pivotIndex);
bool constrained = false;
if (constraint) {
bool constrained = constraint->apply(newRot);
constrained = constraint->apply(newRot);
if (constrained) {
// the constraint will modify the local rotation of the tip so we must
// compute the corresponding model-frame deltaRotation
@ -408,9 +415,8 @@ int AnimInverseKinematics::solveTargetWithCCD(const IKTarget& target, AnimPoseVe
// store the relative rotation change in the accumulator
_accumulators[pivotIndex].add(newRot, target.getWeight());
// this joint has been changed so we check to see if it has the lowest index
if (pivotIndex < lowestMovedIndex) {
lowestMovedIndex = pivotIndex;
if (debug) {
debugJointMap[pivotIndex] = DebugJoint(newRot, constrained);
}
// keep track of tip's new transform as we descend towards root
@ -423,7 +429,10 @@ int AnimInverseKinematics::solveTargetWithCCD(const IKTarget& target, AnimPoseVe
chainDepth++;
}
return lowestMovedIndex;
if (debug) {
debugDrawIKChain(debugJointMap, context);
}
}
//virtual
@ -551,7 +560,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
{
PROFILE_RANGE_EX(simulation_animation, "ik/ccd", 0xffff00ff, 0);
solveWithCyclicCoordinateDescent(targets);
solveWithCyclicCoordinateDescent(context, targets);
}
if (_hipsTargetIndex < 0) {
@ -562,6 +571,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
}
}
}
return _relativePoses;
}
@ -843,7 +853,7 @@ void AnimInverseKinematics::initConstraints() {
stConstraint->setTwistLimits(-MAX_SHOULDER_TWIST, MAX_SHOULDER_TWIST);
std::vector<float> minDots;
const float MAX_SHOULDER_SWING = PI / 16.0f;
const float MAX_SHOULDER_SWING = PI / 12.0f;
minDots.push_back(cosf(MAX_SHOULDER_SWING));
stConstraint->setSwingLimits(minDots);
@ -855,8 +865,8 @@ void AnimInverseKinematics::initConstraints() {
stConstraint->setTwistLimits(-MAX_SPINE_TWIST, MAX_SPINE_TWIST);
// limit lateral swings more then forward-backward swings
const float MAX_SPINE_LATERAL_SWING = PI / 30.0f;
const float MAX_SPINE_ANTERIOR_SWING = PI / 20.0f;
const float MAX_SPINE_LATERAL_SWING = PI / 15.0f;
const float MAX_SPINE_ANTERIOR_SWING = PI / 10.0f;
setEllipticalSwingLimits(stConstraint, MAX_SPINE_LATERAL_SWING, MAX_SPINE_ANTERIOR_SWING);
if (0 == baseName.compare("Spine1", Qt::CaseSensitive)
@ -869,12 +879,12 @@ void AnimInverseKinematics::initConstraints() {
} else if (0 == baseName.compare("Neck", Qt::CaseSensitive)) {
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot());
const float MAX_NECK_TWIST = PI / 10.0f;
const float MAX_NECK_TWIST = PI / 8.0f;
stConstraint->setTwistLimits(-MAX_NECK_TWIST, MAX_NECK_TWIST);
// limit lateral swings more then forward-backward swings
const float MAX_NECK_LATERAL_SWING = PI / 10.0f;
const float MAX_NECK_ANTERIOR_SWING = PI / 8.0f;
const float MAX_NECK_LATERAL_SWING = PI / 8.0f;
const float MAX_NECK_ANTERIOR_SWING = PI / 6.0f;
setEllipticalSwingLimits(stConstraint, MAX_NECK_LATERAL_SWING, MAX_NECK_ANTERIOR_SWING);
constraint = static_cast<RotationConstraint*>(stConstraint);
@ -1047,6 +1057,95 @@ static glm::vec3 sphericalToCartesian(float phi, float theta) {
return glm::vec3(sin_phi * cosf(theta), cos_phi, -sin_phi * sinf(theta));
}
void AnimInverseKinematics::debugDrawRelativePoses(const AnimContext& context) const {
AnimPoseVec poses = _relativePoses;
// convert relative poses to absolute
_skeleton->convertRelativePosesToAbsolute(poses);
mat4 geomToWorldMatrix = context.getRigToWorldMatrix() * context.getGeometryToRigMatrix();
const vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
const vec4 GREEN(0.0f, 1.0f, 0.0f, 1.0f);
const vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f);
const vec4 GRAY(0.2f, 0.2f, 0.2f, 1.0f);
const float AXIS_LENGTH = 2.0f; // cm
// draw each pose
for (int i = 0; i < (int)poses.size(); i++) {
// transform local axes into world space.
auto pose = poses[i];
glm::vec3 xAxis = transformVectorFast(geomToWorldMatrix, pose.rot() * Vectors::UNIT_X);
glm::vec3 yAxis = transformVectorFast(geomToWorldMatrix, pose.rot() * Vectors::UNIT_Y);
glm::vec3 zAxis = transformVectorFast(geomToWorldMatrix, pose.rot() * Vectors::UNIT_Z);
glm::vec3 pos = transformPoint(geomToWorldMatrix, pose.trans());
DebugDraw::getInstance().drawRay(pos, pos + AXIS_LENGTH * xAxis, RED);
DebugDraw::getInstance().drawRay(pos, pos + AXIS_LENGTH * yAxis, GREEN);
DebugDraw::getInstance().drawRay(pos, pos + AXIS_LENGTH * zAxis, BLUE);
// draw line to parent
int parentIndex = _skeleton->getParentIndex(i);
if (parentIndex != -1) {
glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans());
DebugDraw::getInstance().drawRay(pos, parentPos, GRAY);
}
}
}
void AnimInverseKinematics::debugDrawIKChain(std::map<int, DebugJoint>& debugJointMap, const AnimContext& context) const {
AnimPoseVec poses = _relativePoses;
// copy debug joint rotations into the relative poses
for (auto& debugJoint : debugJointMap) {
poses[debugJoint.first].rot() = debugJoint.second.relRot;
}
// convert relative poses to absolute
_skeleton->convertRelativePosesToAbsolute(poses);
mat4 geomToWorldMatrix = context.getRigToWorldMatrix() * context.getGeometryToRigMatrix();
const vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
const vec4 GREEN(0.0f, 1.0f, 0.0f, 1.0f);
const vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f);
const vec4 GRAY(0.2f, 0.2f, 0.2f, 1.0f);
const float AXIS_LENGTH = 2.0f; // cm
// draw each pose
for (int i = 0; i < (int)poses.size(); i++) {
// only draw joints that are actually in debugJointMap, or their parents
auto iter = debugJointMap.find(i);
auto parentIter = debugJointMap.find(_skeleton->getParentIndex(i));
if (iter != debugJointMap.end() || parentIter != debugJointMap.end()) {
// transform local axes into world space.
auto pose = poses[i];
glm::vec3 xAxis = transformVectorFast(geomToWorldMatrix, pose.rot() * Vectors::UNIT_X);
glm::vec3 yAxis = transformVectorFast(geomToWorldMatrix, pose.rot() * Vectors::UNIT_Y);
glm::vec3 zAxis = transformVectorFast(geomToWorldMatrix, pose.rot() * Vectors::UNIT_Z);
glm::vec3 pos = transformPoint(geomToWorldMatrix, pose.trans());
DebugDraw::getInstance().drawRay(pos, pos + AXIS_LENGTH * xAxis, RED);
DebugDraw::getInstance().drawRay(pos, pos + AXIS_LENGTH * yAxis, GREEN);
DebugDraw::getInstance().drawRay(pos, pos + AXIS_LENGTH * zAxis, BLUE);
// draw line to parent
int parentIndex = _skeleton->getParentIndex(i);
if (parentIndex != -1) {
glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans());
glm::vec4 color = GRAY;
// draw constrained joints with a RED link to their parent.
if (parentIter != debugJointMap.end() && parentIter->second.constrained) {
color = RED;
}
DebugDraw::getInstance().drawRay(pos, parentPos, color);
}
}
}
}
void AnimInverseKinematics::debugDrawConstraints(const AnimContext& context) const {
if (_skeleton) {
const vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);

View file

@ -58,13 +58,22 @@ public:
protected:
void computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets, const AnimPoseVec& underPoses);
void solveWithCyclicCoordinateDescent(const std::vector<IKTarget>& targets);
int solveTargetWithCCD(const IKTarget& target, AnimPoseVec& absolutePoses);
void solveWithCyclicCoordinateDescent(const AnimContext& context, const std::vector<IKTarget>& targets);
void solveTargetWithCCD(const AnimContext& context, const IKTarget& target, const AnimPoseVec& absolutePoses, bool debug);
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override;
struct DebugJoint {
DebugJoint() : relRot(), constrained(false) {}
DebugJoint(const glm::quat& relRotIn, bool constrainedIn) : relRot(relRotIn), constrained(constrainedIn) {}
glm::quat relRot;
bool constrained;
};
void debugDrawIKChain(std::map<int, DebugJoint>& debugJointMap, const AnimContext& context) const;
void debugDrawRelativePoses(const AnimContext& context) const;
void debugDrawConstraints(const AnimContext& context) const;
void initRelativePosesFromSolutionSource(SolutionSource solutionSource, const AnimPoseVec& underPose);
void blendToPoses(const AnimPoseVec& targetPoses, const AnimPoseVec& underPose, float blendFactor);
// for AnimDebugDraw rendering
virtual const AnimPoseVec& getPosesInternal() const override { return _relativePoses; }