fix animation-tests

This commit is contained in:
Andrew Meadows 2016-03-10 13:48:27 -08:00
parent ae596d79f1
commit df9ccf76ab
3 changed files with 114 additions and 78 deletions

View file

@ -22,7 +22,13 @@ AnimInverseKinematics::AnimInverseKinematics(const QString& id) : AnimNode(AnimN
} }
AnimInverseKinematics::~AnimInverseKinematics() { AnimInverseKinematics::~AnimInverseKinematics() {
std::cout << "adebug dtor" << std::endl; // adebug
clearConstraints(); clearConstraints();
std::cout << "adebug dtor 002" << std::endl; // adebug
_accumulators.clear();
std::cout << "adebug dtor 003 targetVarVec.size() = " << _targetVarVec.size() << std::endl; // adebug
_targetVarVec.clear();
std::cout << "adebug dtor 004 targetVarVec.size() = " << _targetVarVec.size() << std::endl; // adebug
} }
void AnimInverseKinematics::loadDefaultPoses(const AnimPoseVec& poses) { void AnimInverseKinematics::loadDefaultPoses(const AnimPoseVec& poses) {
@ -485,6 +491,7 @@ RotationConstraint* AnimInverseKinematics::getConstraint(int index) {
} }
void AnimInverseKinematics::clearConstraints() { void AnimInverseKinematics::clearConstraints() {
std::cout << "adebug clearConstraints size = " << _constraints.size() << std::endl; // adebug
std::map<int, RotationConstraint*>::iterator constraintItr = _constraints.begin(); std::map<int, RotationConstraint*>::iterator constraintItr = _constraints.begin();
while (constraintItr != _constraints.end()) { while (constraintItr != _constraints.end()) {
delete constraintItr->second; delete constraintItr->second;

View file

@ -29,7 +29,7 @@ const glm::quat identity = glm::quat();
const glm::quat quaterTurnAroundZ = glm::angleAxis(0.5f * PI, zAxis); const glm::quat quaterTurnAroundZ = glm::angleAxis(0.5f * PI, zAxis);
void makeTestFBXJoints(std::vector<FBXJoint>& fbxJoints) { void makeTestFBXJoints(FBXGeometry& geometry) {
FBXJoint joint; FBXJoint joint;
joint.isFree = false; joint.isFree = false;
joint.freeLineage.clear(); joint.freeLineage.clear();
@ -61,29 +61,29 @@ void makeTestFBXJoints(std::vector<FBXJoint>& fbxJoints) {
joint.name = "A"; joint.name = "A";
joint.parentIndex = -1; joint.parentIndex = -1;
joint.translation = origin; joint.translation = origin;
fbxJoints.push_back(joint); geometry.joints.push_back(joint);
joint.name = "B"; joint.name = "B";
joint.parentIndex = 0; joint.parentIndex = 0;
joint.translation = xAxis; joint.translation = xAxis;
fbxJoints.push_back(joint); geometry.joints.push_back(joint);
joint.name = "C"; joint.name = "C";
joint.parentIndex = 1; joint.parentIndex = 1;
joint.translation = xAxis; joint.translation = xAxis;
fbxJoints.push_back(joint); geometry.joints.push_back(joint);
joint.name = "D"; joint.name = "D";
joint.parentIndex = 2; joint.parentIndex = 2;
joint.translation = xAxis; joint.translation = xAxis;
fbxJoints.push_back(joint); geometry.joints.push_back(joint);
// compute each joint's transform // compute each joint's transform
for (int i = 1; i < (int)fbxJoints.size(); ++i) { for (int i = 1; i < (int)geometry.joints.size(); ++i) {
FBXJoint& j = fbxJoints[i]; FBXJoint& j = geometry.joints[i];
int parentIndex = j.parentIndex; int parentIndex = j.parentIndex;
// World = ParentWorld * T * (Roff * Rp) * Rpre * R * Rpost * (Rp-1 * Soff * Sp * S * Sp-1) // World = ParentWorld * T * (Roff * Rp) * Rpre * R * Rpost * (Rp-1 * Soff * Sp * S * Sp-1)
j.transform = fbxJoints[parentIndex].transform * j.transform = geometry.joints[parentIndex].transform *
glm::translate(j.translation) * glm::translate(j.translation) *
j.preTransform * j.preTransform *
glm::mat4_cast(j.preRotation * j.rotation * j.postRotation) * glm::mat4_cast(j.preRotation * j.rotation * j.postRotation) *
@ -94,14 +94,14 @@ void makeTestFBXJoints(std::vector<FBXJoint>& fbxJoints) {
} }
void AnimInverseKinematicsTests::testSingleChain() { void AnimInverseKinematicsTests::testSingleChain() {
std::vector<FBXJoint> fbxJoints; FBXGeometry geometry;
makeTestFBXJoints(fbxJoints); makeTestFBXJoints(geometry);
// create a skeleton and doll // create a skeleton and doll
AnimPose offset; AnimPose offset;
AnimSkeleton* skeleton = new AnimSkeleton(fbxJoints, offset); AnimSkeleton::Pointer skeletonPtr = std::make_shared<AnimSkeleton>(geometry);
AnimSkeleton::Pointer skeletonPtr(skeleton);
AnimInverseKinematics ikDoll("doll"); AnimInverseKinematics ikDoll("doll");
ikDoll.setSkeleton(skeletonPtr); ikDoll.setSkeleton(skeletonPtr);
{ // easy test IK of joint C { // easy test IK of joint C
@ -113,11 +113,11 @@ void AnimInverseKinematicsTests::testSingleChain() {
pose.rot = identity; pose.rot = identity;
pose.trans = origin; pose.trans = origin;
std::vector<AnimPose> poses; AnimPoseVec poses;
poses.push_back(pose); poses.push_back(pose);
pose.trans = xAxis; pose.trans = xAxis;
for (int i = 1; i < (int)fbxJoints.size(); ++i) { for (int i = 1; i < (int)geometry.joints.size(); ++i) {
poses.push_back(pose); poses.push_back(pose);
} }
ikDoll.loadPoses(poses); ikDoll.loadPoses(poses);
@ -134,7 +134,8 @@ void AnimInverseKinematicsTests::testSingleChain() {
AnimVariantMap varMap; AnimVariantMap varMap;
varMap.set("positionD", targetPosition); varMap.set("positionD", targetPosition);
varMap.set("rotationD", targetRotation); varMap.set("rotationD", targetRotation);
ikDoll.setTargetVars("D", "positionD", "rotationD"); varMap.set("targetType", (int)IKTarget::Type::RotationAndPosition);
ikDoll.setTargetVars(QString("D"), QString("positionD"), QString("rotationD"), QString("targetType"));
AnimNode::Triggers triggers; AnimNode::Triggers triggers;
// the IK solution should be: // the IK solution should be:
@ -144,38 +145,49 @@ void AnimInverseKinematicsTests::testSingleChain() {
// | // |
// A------>B------>C // A------>B------>C
// //
// iterate several times
float dt = 1.0f; float dt = 1.0f;
ikDoll.evaluate(varMap, dt, triggers); ikDoll.overlay(varMap, dt, triggers, poses);
ikDoll.overlay(varMap, dt, triggers, poses);
ikDoll.overlay(varMap, dt, triggers, poses);
ikDoll.overlay(varMap, dt, triggers, poses);
ikDoll.overlay(varMap, dt, triggers, poses);
ikDoll.overlay(varMap, dt, triggers, poses);
const AnimPoseVec& relativePoses = ikDoll.overlay(varMap, dt, triggers, poses);
// verify absolute results // verify absolute results
// NOTE: since we expect this solution to converge very quickly (one loop) // NOTE: since we expect this solution to converge very quickly (one loop)
// we can impose very tight error thresholds. // we can impose very tight error thresholds.
std::vector<AnimPose> absolutePoses; AnimPoseVec absolutePoses;
for (auto pose : poses) {
absolutePoses.push_back(pose);
}
ikDoll.computeAbsolutePoses(absolutePoses); ikDoll.computeAbsolutePoses(absolutePoses);
float acceptableAngle = 0.0001f; const float acceptableAngleError = 0.001f;
QCOMPARE_QUATS(absolutePoses[0].rot, identity, acceptableAngle); QCOMPARE_QUATS(absolutePoses[0].rot, identity, acceptableAngleError);
QCOMPARE_QUATS(absolutePoses[1].rot, identity, acceptableAngle); QCOMPARE_QUATS(absolutePoses[1].rot, identity, acceptableAngleError);
QCOMPARE_QUATS(absolutePoses[2].rot, quaterTurnAroundZ, acceptableAngle); QCOMPARE_QUATS(absolutePoses[2].rot, quaterTurnAroundZ, acceptableAngleError);
QCOMPARE_QUATS(absolutePoses[3].rot, quaterTurnAroundZ, acceptableAngle); QCOMPARE_QUATS(absolutePoses[3].rot, quaterTurnAroundZ, acceptableAngleError);
QCOMPARE_WITH_ABS_ERROR(absolutePoses[0].trans, origin, EPSILON); const float acceptableTranslationError = 0.025f;
QCOMPARE_WITH_ABS_ERROR(absolutePoses[1].trans, xAxis, EPSILON); QCOMPARE_WITH_ABS_ERROR(absolutePoses[0].trans, origin, acceptableTranslationError);
QCOMPARE_WITH_ABS_ERROR(absolutePoses[2].trans, 2.0f * xAxis, EPSILON); QCOMPARE_WITH_ABS_ERROR(absolutePoses[1].trans, xAxis, acceptableTranslationError);
QCOMPARE_WITH_ABS_ERROR(absolutePoses[3].trans, targetPosition, EPSILON); QCOMPARE_WITH_ABS_ERROR(absolutePoses[2].trans, 2.0f * xAxis, acceptableTranslationError);
QCOMPARE_WITH_ABS_ERROR(absolutePoses[3].trans, targetPosition, acceptableTranslationError);
// verify relative results // verify relative results
const std::vector<AnimPose>& relativePoses = ikDoll.getRelativePoses(); QCOMPARE_QUATS(relativePoses[0].rot, identity, acceptableAngleError);
QCOMPARE_QUATS(relativePoses[0].rot, identity, acceptableAngle); QCOMPARE_QUATS(relativePoses[1].rot, identity, acceptableAngleError);
QCOMPARE_QUATS(relativePoses[1].rot, identity, acceptableAngle); QCOMPARE_QUATS(relativePoses[2].rot, quaterTurnAroundZ, acceptableAngleError);
QCOMPARE_QUATS(relativePoses[2].rot, quaterTurnAroundZ, acceptableAngle); QCOMPARE_QUATS(relativePoses[3].rot, identity, acceptableAngleError);
QCOMPARE_QUATS(relativePoses[3].rot, identity, acceptableAngle);
QCOMPARE_WITH_ABS_ERROR(relativePoses[0].trans, origin, EPSILON); QCOMPARE_WITH_ABS_ERROR(relativePoses[0].trans, origin, acceptableTranslationError);
QCOMPARE_WITH_ABS_ERROR(relativePoses[1].trans, xAxis, EPSILON); QCOMPARE_WITH_ABS_ERROR(relativePoses[1].trans, xAxis, acceptableTranslationError);
QCOMPARE_WITH_ABS_ERROR(relativePoses[2].trans, xAxis, EPSILON); QCOMPARE_WITH_ABS_ERROR(relativePoses[2].trans, xAxis, acceptableTranslationError);
QCOMPARE_WITH_ABS_ERROR(relativePoses[3].trans, xAxis, EPSILON); QCOMPARE_WITH_ABS_ERROR(relativePoses[3].trans, xAxis, acceptableTranslationError);
} }
{ // hard test IK of joint C { // hard test IK of joint C
// load intial poses that look like this: // load intial poses that look like this:
// //
@ -188,8 +200,8 @@ void AnimInverseKinematicsTests::testSingleChain() {
pose.scale = glm::vec3(1.0f); pose.scale = glm::vec3(1.0f);
pose.rot = identity; pose.rot = identity;
pose.trans = origin; pose.trans = origin;
std::vector<AnimPose> poses; AnimPoseVec poses;
poses.push_back(pose); poses.push_back(pose);
pose.trans = xAxis; pose.trans = xAxis;
@ -211,15 +223,26 @@ void AnimInverseKinematicsTests::testSingleChain() {
AnimVariantMap varMap; AnimVariantMap varMap;
varMap.set("positionD", targetPosition); varMap.set("positionD", targetPosition);
varMap.set("rotationD", targetRotation); varMap.set("rotationD", targetRotation);
ikDoll.setTargetVars("D", "positionD", "rotationD"); varMap.set("targetType", (int)IKTarget::Type::RotationAndPosition);
ikDoll.setTargetVars(QString("D"), QString("positionD"), QString("rotationD"), QString("targetType"));
AnimNode::Triggers triggers; AnimNode::Triggers triggers;
// the IK solution should be: // the IK solution should be:
// //
// A------>B------>C------>D // A------>B------>C------>D
// //
// iterate several times
float dt = 1.0f; float dt = 1.0f;
ikDoll.evaluate(varMap, dt, triggers); ikDoll.overlay(varMap, dt, triggers, poses);
ikDoll.overlay(varMap, dt, triggers, poses);
ikDoll.overlay(varMap, dt, triggers, poses);
ikDoll.overlay(varMap, dt, triggers, poses);
ikDoll.overlay(varMap, dt, triggers, poses);
ikDoll.overlay(varMap, dt, triggers, poses);
ikDoll.overlay(varMap, dt, triggers, poses);
ikDoll.overlay(varMap, dt, triggers, poses);
const AnimPoseVec& relativePoses = ikDoll.overlay(varMap, dt, triggers, poses);
// verify absolute results // verify absolute results
// NOTE: the IK algorithm doesn't converge very fast for full-reach targets, // NOTE: the IK algorithm doesn't converge very fast for full-reach targets,
@ -228,31 +251,33 @@ void AnimInverseKinematicsTests::testSingleChain() {
// NOTE: constraints may help speed up convergence since some joints may get clamped // NOTE: constraints may help speed up convergence since some joints may get clamped
// to maximum extension. TODO: experiment with tightening the error thresholds when // to maximum extension. TODO: experiment with tightening the error thresholds when
// constraints are working. // constraints are working.
std::vector<AnimPose> absolutePoses; AnimPoseVec absolutePoses;
for (auto pose : poses) {
absolutePoses.push_back(pose);
}
ikDoll.computeAbsolutePoses(absolutePoses); ikDoll.computeAbsolutePoses(absolutePoses);
float acceptableAngle = 0.1f; // radians float acceptableAngle = 0.01f; // radians
QCOMPARE_QUATS(absolutePoses[0].rot, identity, acceptableAngle); QCOMPARE_QUATS(absolutePoses[0].rot, identity, acceptableAngle);
QCOMPARE_QUATS(absolutePoses[1].rot, identity, acceptableAngle); QCOMPARE_QUATS(absolutePoses[1].rot, identity, acceptableAngle);
QCOMPARE_QUATS(absolutePoses[2].rot, identity, acceptableAngle); QCOMPARE_QUATS(absolutePoses[2].rot, identity, acceptableAngle);
QCOMPARE_QUATS(absolutePoses[3].rot, identity, acceptableAngle); QCOMPARE_QUATS(absolutePoses[3].rot, identity, acceptableAngle);
float acceptableDistance = 0.4f; float acceptableDistance = 0.03f;
QCOMPARE_WITH_ABS_ERROR(absolutePoses[0].trans, origin, EPSILON); QCOMPARE_WITH_ABS_ERROR(absolutePoses[0].trans, origin, acceptableDistance);
QCOMPARE_WITH_ABS_ERROR(absolutePoses[1].trans, xAxis, acceptableDistance); QCOMPARE_WITH_ABS_ERROR(absolutePoses[1].trans, xAxis, acceptableDistance);
QCOMPARE_WITH_ABS_ERROR(absolutePoses[2].trans, 2.0f * xAxis, acceptableDistance); QCOMPARE_WITH_ABS_ERROR(absolutePoses[2].trans, 2.0f * xAxis, acceptableDistance);
QCOMPARE_WITH_ABS_ERROR(absolutePoses[3].trans, 3.0f * xAxis, acceptableDistance); QCOMPARE_WITH_ABS_ERROR(absolutePoses[3].trans, 3.0f * xAxis, acceptableDistance);
// verify relative results // verify relative results
const std::vector<AnimPose>& relativePoses = ikDoll.getRelativePoses();
QCOMPARE_QUATS(relativePoses[0].rot, identity, acceptableAngle); QCOMPARE_QUATS(relativePoses[0].rot, identity, acceptableAngle);
QCOMPARE_QUATS(relativePoses[1].rot, identity, acceptableAngle); QCOMPARE_QUATS(relativePoses[1].rot, identity, acceptableAngle);
QCOMPARE_QUATS(relativePoses[2].rot, identity, acceptableAngle); QCOMPARE_QUATS(relativePoses[2].rot, identity, acceptableAngle);
QCOMPARE_QUATS(relativePoses[3].rot, identity, acceptableAngle); QCOMPARE_QUATS(relativePoses[3].rot, identity, acceptableAngle);
QCOMPARE_WITH_ABS_ERROR(relativePoses[0].trans, origin, EPSILON); QCOMPARE_WITH_ABS_ERROR(relativePoses[0].trans, origin, acceptableDistance);
QCOMPARE_WITH_ABS_ERROR(relativePoses[1].trans, xAxis, EPSILON); QCOMPARE_WITH_ABS_ERROR(relativePoses[1].trans, xAxis, acceptableDistance);
QCOMPARE_WITH_ABS_ERROR(relativePoses[2].trans, xAxis, EPSILON); QCOMPARE_WITH_ABS_ERROR(relativePoses[2].trans, xAxis, acceptableDistance);
QCOMPARE_WITH_ABS_ERROR(relativePoses[3].trans, xAxis, EPSILON); QCOMPARE_WITH_ABS_ERROR(relativePoses[3].trans, xAxis, acceptableDistance);
} }
} }

View file

@ -38,8 +38,9 @@ void AnimTests::testClipInternalState() {
float endFrame = 20.0f; float endFrame = 20.0f;
float timeScale = 1.1f; float timeScale = 1.1f;
bool loopFlag = true; bool loopFlag = true;
bool mirrorFlag = false;
AnimClip clip(id, url, startFrame, endFrame, timeScale, loopFlag); AnimClip clip(id, url, startFrame, endFrame, timeScale, loopFlag, mirrorFlag);
QVERIFY(clip.getID() == id); QVERIFY(clip.getID() == id);
QVERIFY(clip.getType() == AnimNode::Type::Clip); QVERIFY(clip.getType() == AnimNode::Type::Clip);
@ -49,6 +50,7 @@ void AnimTests::testClipInternalState() {
QVERIFY(clip._endFrame == endFrame); QVERIFY(clip._endFrame == endFrame);
QVERIFY(clip._timeScale == timeScale); QVERIFY(clip._timeScale == timeScale);
QVERIFY(clip._loopFlag == loopFlag); QVERIFY(clip._loopFlag == loopFlag);
QVERIFY(clip._mirrorFlag == mirrorFlag);
} }
static float framesToSec(float secs) { static float framesToSec(float secs) {
@ -62,12 +64,13 @@ void AnimTests::testClipEvaulate() {
float startFrame = 2.0f; float startFrame = 2.0f;
float endFrame = 22.0f; float endFrame = 22.0f;
float timeScale = 1.0f; float timeScale = 1.0f;
float loopFlag = true; bool loopFlag = true;
bool mirrorFlag = false;
auto vars = AnimVariantMap(); auto vars = AnimVariantMap();
vars.set("FalseVar", false); vars.set("FalseVar", false);
AnimClip clip(id, url, startFrame, endFrame, timeScale, loopFlag); AnimClip clip(id, url, startFrame, endFrame, timeScale, loopFlag, mirrorFlag);
AnimNode::Triggers triggers; AnimNode::Triggers triggers;
clip.evaluate(vars, framesToSec(10.0f), triggers); clip.evaluate(vars, framesToSec(10.0f), triggers);
@ -97,7 +100,8 @@ void AnimTests::testClipEvaulateWithVars() {
float startFrame = 2.0f; float startFrame = 2.0f;
float endFrame = 22.0f; float endFrame = 22.0f;
float timeScale = 1.0f; float timeScale = 1.0f;
float loopFlag = true; bool loopFlag = true;
bool mirrorFlag = false;
float startFrame2 = 22.0f; float startFrame2 = 22.0f;
float endFrame2 = 100.0f; float endFrame2 = 100.0f;
@ -110,7 +114,7 @@ void AnimTests::testClipEvaulateWithVars() {
vars.set("timeScale2", timeScale2); vars.set("timeScale2", timeScale2);
vars.set("loopFlag2", loopFlag2); vars.set("loopFlag2", loopFlag2);
AnimClip clip(id, url, startFrame, endFrame, timeScale, loopFlag); AnimClip clip(id, url, startFrame, endFrame, timeScale, loopFlag, mirrorFlag);
clip.setStartFrameVar("startFrame2"); clip.setStartFrameVar("startFrame2");
clip.setEndFrameVar("endFrame2"); clip.setEndFrameVar("endFrame2");
clip.setTimeScaleVar("timeScale2"); clip.setTimeScaleVar("timeScale2");
@ -583,23 +587,23 @@ void AnimTests::testExpressionEvaluator() {
TEST_BOOL_EXPR(false && false); TEST_BOOL_EXPR(false && false);
TEST_BOOL_EXPR(false && true); TEST_BOOL_EXPR(false && true);
TEST_BOOL_EXPR(true || false && true); TEST_BOOL_EXPR(true || (false && true));
TEST_BOOL_EXPR(true || false && false); TEST_BOOL_EXPR(true || (false && false));
TEST_BOOL_EXPR(true || true && true); TEST_BOOL_EXPR(true || (true && true));
TEST_BOOL_EXPR(true || true && false); TEST_BOOL_EXPR(true || (true && false));
TEST_BOOL_EXPR(false || false && true); TEST_BOOL_EXPR(false || (false && true));
TEST_BOOL_EXPR(false || false && false); TEST_BOOL_EXPR(false || (false && false));
TEST_BOOL_EXPR(false || true && true); TEST_BOOL_EXPR(false || (true && true));
TEST_BOOL_EXPR(false || true && false); TEST_BOOL_EXPR(false || (true && false));
TEST_BOOL_EXPR(true && false || true); TEST_BOOL_EXPR((true && false) || true);
TEST_BOOL_EXPR(true && false || false); TEST_BOOL_EXPR((true && false) || false);
TEST_BOOL_EXPR(true && true || true); TEST_BOOL_EXPR((true && true) || true);
TEST_BOOL_EXPR(true && true || false); TEST_BOOL_EXPR((true && true) || false);
TEST_BOOL_EXPR(false && false || true); TEST_BOOL_EXPR((false && false) || true);
TEST_BOOL_EXPR(false && false || false); TEST_BOOL_EXPR((false && false) || false);
TEST_BOOL_EXPR(false && true || true); TEST_BOOL_EXPR((false && true) || true);
TEST_BOOL_EXPR(false && true || false); TEST_BOOL_EXPR((false && true) || false);
TEST_BOOL_EXPR(t || false); TEST_BOOL_EXPR(t || false);
TEST_BOOL_EXPR(t || true); TEST_BOOL_EXPR(t || true);
@ -610,14 +614,14 @@ void AnimTests::testExpressionEvaluator() {
TEST_BOOL_EXPR(!false); TEST_BOOL_EXPR(!false);
TEST_BOOL_EXPR(!true || true); TEST_BOOL_EXPR(!true || true);
TEST_BOOL_EXPR(!true && !false || !true); TEST_BOOL_EXPR((!true && !false) || !true);
TEST_BOOL_EXPR(!true && !false || true); TEST_BOOL_EXPR((!true && !false) || true);
TEST_BOOL_EXPR(!true && false || !true); TEST_BOOL_EXPR((!true && false) || !true);
TEST_BOOL_EXPR(!true && false || true); TEST_BOOL_EXPR((!true && false) || true);
TEST_BOOL_EXPR(true && !false || !true); TEST_BOOL_EXPR((true && !false) || !true);
TEST_BOOL_EXPR(true && !false || true); TEST_BOOL_EXPR((true && !false) || true);
TEST_BOOL_EXPR(true && false || !true); TEST_BOOL_EXPR((true && false) || !true);
TEST_BOOL_EXPR(true && false || true); TEST_BOOL_EXPR((true && false) || true);
TEST_BOOL_EXPR(!(true && f) || !t); TEST_BOOL_EXPR(!(true && f) || !t);
TEST_BOOL_EXPR(!!!(t) && (!!f || true)); TEST_BOOL_EXPR(!!!(t) && (!!f || true));