fix IK looping error after minor refactor

This commit is contained in:
Andrew Meadows 2016-01-29 10:19:12 -08:00
parent 7b3f688a17
commit 48f6a9c05f

View file

@ -142,18 +142,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
absolutePoses.resize(_relativePoses.size()); absolutePoses.resize(_relativePoses.size());
computeAbsolutePoses(absolutePoses); computeAbsolutePoses(absolutePoses);
#if 0 //if (targets[0].getType() == IKTarget::Type::HmdHead) {
// TODO: implement this
if (targets[0].getType() == IKTarget::Type::HmdHead) {
// first solve HmdHead rotation
IKTarget& target = targets[0];
// measure the head's position error
// update hipsOffset to compensate for error
}
#endif
// clear the accumulators before we start the IK solver // clear the accumulators before we start the IK solver
for (auto& accumulator: _accumulators) { for (auto& accumulator: _accumulators) {
accumulator.clearAndClean(); accumulator.clearAndClean();
@ -162,30 +151,28 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
int numLoops = 0; int numLoops = 0;
const int MAX_IK_LOOPS = 4; const int MAX_IK_LOOPS = 4;
while (numLoops < MAX_IK_LOOPS) { while (numLoops < MAX_IK_LOOPS) {
++numLoops;
// solve all targets
int lowestMovedIndex = (int)_relativePoses.size(); int lowestMovedIndex = (int)_relativePoses.size();
for (auto& target: targets) { for (auto& target: targets) {
lowestMovedIndex = solveTargetWithCCD(target, absolutePoses); lowestMovedIndex = solveTargetWithCCD(target, absolutePoses);
++numLoops; }
if (lowestMovedIndex >= (int)_relativePoses.size()) { // harvest accumulated rotations and apply the average
continue; const int numJoints = (int)_accumulators.size();
for (int i = 0; i < numJoints; ++i) {
if (_accumulators[i].size() > 0) {
_relativePoses[i].rot = _accumulators[i].getAverage();
_accumulators[i].clear();
} }
}
// harvest accumulated rotations and apply the average // only update the absolutePoses that need it: those between lowestMovedIndex and _maxTargetIndex
const int numJoints = (int)_accumulators.size(); for (auto i = lowestMovedIndex; i <= _maxTargetIndex; ++i) {
for (int i = 0; i < numJoints; ++i) { auto parentIndex = _skeleton->getParentIndex((int)i);
if (_accumulators[i].size() > 0) { if (parentIndex != -1) {
_relativePoses[i].rot = _accumulators[i].getAverage(); absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i];
_accumulators[i].clear();
}
}
// only update the absolutePoses that need it: those between lowestMovedIndex and _maxTargetIndex
for (auto i = lowestMovedIndex; i <= _maxTargetIndex; ++i) {
auto parentIndex = _skeleton->getParentIndex((int)i);
if (parentIndex != -1) {
absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i];
}
} }
} }
} }
@ -371,7 +358,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
if (_relativePoses.size() != underPoses.size()) { if (_relativePoses.size() != underPoses.size()) {
loadPoses(underPoses); loadPoses(underPoses);
} else { } else {
// relax toward underpose // relax toward underPoses
// HACK: this relaxation needs to be constant per-frame rather than per-realtime // HACK: this relaxation needs to be constant per-frame rather than per-realtime
// in order to prevent IK "flutter" for bad FPS. The bad news is that the good parts // in order to prevent IK "flutter" for bad FPS. The bad news is that the good parts
// of this relaxation will be FPS dependent (low FPS will make the limbs align slower // of this relaxation will be FPS dependent (low FPS will make the limbs align slower
@ -382,8 +369,10 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
for (int i = 0; i < numJoints; ++i) { for (int i = 0; i < numJoints; ++i) {
float dotSign = copysignf(1.0f, glm::dot(_relativePoses[i].rot, underPoses[i].rot)); float dotSign = copysignf(1.0f, glm::dot(_relativePoses[i].rot, underPoses[i].rot));
if (_accumulators[i].isDirty()) { if (_accumulators[i].isDirty()) {
// this joint is affected by IK --> blend toward underPose rotation
_relativePoses[i].rot = glm::normalize(glm::lerp(_relativePoses[i].rot, dotSign * underPoses[i].rot, blend)); _relativePoses[i].rot = glm::normalize(glm::lerp(_relativePoses[i].rot, dotSign * underPoses[i].rot, blend));
} else { } else {
// this joint is NOT affected by IK --> slam to underPose rotation
_relativePoses[i].rot = underPoses[i].rot; _relativePoses[i].rot = underPoses[i].rot;
} }
_relativePoses[i].trans = underPoses[i].trans; _relativePoses[i].trans = underPoses[i].trans;
@ -406,7 +395,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
++constraintItr; ++constraintItr;
} }
} else { } else {
// shift the everything according to the _hipsOffset from the previous frame // shift hips according to the _hipsOffset from the previous frame
float offsetLength = glm::length(_hipsOffset); float offsetLength = glm::length(_hipsOffset);
const float MIN_HIPS_OFFSET_LENGTH = 0.03f; const float MIN_HIPS_OFFSET_LENGTH = 0.03f;
if (offsetLength > MIN_HIPS_OFFSET_LENGTH) { if (offsetLength > MIN_HIPS_OFFSET_LENGTH) {
@ -423,14 +412,14 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
hipsFrameRotation *= _relativePoses[index].rot; hipsFrameRotation *= _relativePoses[index].rot;
index = _skeleton->getParentIndex(index); index = _skeleton->getParentIndex(index);
} }
_relativePoses[_hipsIndex].trans = underPoses[_hipsIndex].trans _relativePoses[_hipsIndex].trans = underPoses[_hipsIndex].trans
+ glm::inverse(glm::normalize(hipsFrameRotation)) * (scaleFactor * _hipsOffset); + glm::inverse(glm::normalize(hipsFrameRotation)) * (scaleFactor * _hipsOffset);
} }
} }
solveWithCyclicCoordinateDescent(targets); solveWithCyclicCoordinateDescent(targets);
// compute the new target hips offset (for next frame) // measure new _hipsOffset for next frame
// by looking for discrepancies between where a targeted endEffector is // by looking for discrepancies between where a targeted endEffector is
// and where it wants to be (after IK solutions are done) // and where it wants to be (after IK solutions are done)
glm::vec3 newHipsOffset = Vectors::ZERO; glm::vec3 newHipsOffset = Vectors::ZERO;
@ -439,7 +428,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
if (targetIndex == _headIndex && _headIndex != -1) { if (targetIndex == _headIndex && _headIndex != -1) {
// special handling for headTarget // special handling for headTarget
if (target.getType() == IKTarget::Type::RotationOnly) { if (target.getType() == IKTarget::Type::RotationOnly) {
// we want to shift the hips to bring the underpose closer // we want to shift the hips to bring the underPose closer
// to where the head happens to be (overpose) // to where the head happens to be (overpose)
glm::vec3 under = _skeleton->getAbsolutePose(_headIndex, underPoses).trans; glm::vec3 under = _skeleton->getAbsolutePose(_headIndex, underPoses).trans;
glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans; glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans;