Files
EVEngine/test/animation_motion_normalization.cpp
T

82 lines
2.9 KiB
C++

#include "animation/AnimClip.h"
#include "animation/AnimSkeleton.h"
#include "animation/MotionDatabase.h"
#include "animation/MotionFeatureLayout.h"
#include "zeroerr/unittest.h"
#include <cmath>
#include <limits>
using namespace eve::animation;
namespace {
MotionFeatureLayout positionLayout() {
MotionFeatureLayout layout;
layout.sampleRate = 10;
MotionFeatureChannel channel;
channel.kind = MotionFeatureKind::Position;
channel.source = MotionFeatureSource::Pose;
channel.query = MotionFeatureQuery::Character;
channel.bone = 1;
channel.origin = 0;
channel.axes = 1;
channel.weight = 1.f;
layout.channels.push_back(channel);
return layout;
}
struct NormalizationFixture {
AnimSkeleton skeleton;
AnimClip low{"low"}, high{"high"};
NormalizationFixture() {
skeleton.addBone("root"); skeleton.addBone("sample", 0);
for (auto* clip : {&low, &high}) { clip->setDuration(1.f); clip->setLoop(false); }
low.addPositionKey(1, 0.f, 0.f, 0.f, 0.f);
high.addPositionKey(1, 0.f, 10.f, 0.f, 0.f);
}
void add(MotionDatabase& database) { database.addClip(&low); database.addClip(&high); }
};
}
TEST_CASE("animation.motionSchema.normalizationRangesPreserveMultiplicityAndSamplingIntervals") {
NormalizationFixture fixture;
MotionDatabase database(&fixture.skeleton);
fixture.add(database);
REQUIRE(database.setFeatureLayout(positionLayout()).ok());
std::vector<MotionNormalizationRange> ranges{{0, 0.f, 1.f}, {0, 0.f, 1.f}, {1, 0.f, 1.f}};
auto configured = database.setFeatureNormalizationRanges(ranges);
REQUIRE(configured.ok());
CHECK(configured.value() == 33);
ranges.clear();
database.bake();
float high = 0.f;
database.getFeature(11, &high, 1);
CHECK(std::abs(high - 1.5f) < 1e-5f);
MotionDatabase ordinary(&fixture.skeleton);
fixture.add(ordinary);
REQUIRE(ordinary.setFeatureLayout(positionLayout()).ok());
ordinary.bake();
ordinary.getFeature(11, &high, 1);
CHECK(std::abs(high - 1.f) < 1e-5f);
}
TEST_CASE("animation.motionSchema.normalizationRangeValidationIsAtomic") {
NormalizationFixture fixture;
MotionDatabase database(&fixture.skeleton);
fixture.add(database);
REQUIRE(database.setFeatureLayout(positionLayout()).ok());
std::vector<MotionNormalizationRange> valid{{0, 0.f, 0.f}};
REQUIRE(database.setFeatureNormalizationRanges(valid).ok());
for (const auto& invalid : std::vector<MotionNormalizationRange>{
{-1, 0.f, 1.f}, {2, 0.f, 1.f}, {0, -.1f, 1.f}, {0, .5f, .4f},
{0, 0.f, 2.f}, {0, 0.f, std::numeric_limits<float>::quiet_NaN()}}) {
REQUIRE(!database.setFeatureNormalizationRanges(std::span(&invalid, 1)).ok());
}
database.bake();
float high = 0.f;
database.getFeature(11, &high, 1);
// The retained one-sample low range has zero deviation and therefore unit scale.
CHECK(std::abs(high - 10.f) < 1e-5f);
}