Files
metin2-client/tests/port/port_granny_root_motion_test.cpp
T

91 lines
4.2 KiB
C++

// Real packed bear assets: the auxiliary Hedra group precedes the moving Bip01 group.
#include "granny.h"
#include "../../src/platform/PackBackend.h"
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <string>
#include <vector>
namespace {
int failures = 0;
void check(bool ok, const char* message) {
if (!ok) { std::fprintf(stderr, "FAIL %s\n", message); ++failures; }
}
granny_file* load(const std::string& path) {
std::vector<uint8_t> bytes;
if (!mtpack40250::read(path, bytes)) return nullptr;
return GrannyReadEntireFileFromMemory(bytes.size(), bytes.data());
}
void test_motion(granny_model* model, const char* name) {
const std::string base = "d:/ymir work/monster/bear/";
granny_file* file = load(base + name + ".gr2");
check(file != nullptr, "bear animation loads from the live pack path");
if (!file) return;
auto* info = GrannyGetFileInfo(file);
check(info && info->AnimationCount == 1, "one animation");
if (!info || info->AnimationCount != 1) { GrannyFreeFile(file); return; }
auto* animation = info->Animations[0];
check(animation->TrackGroupCount == 2, "both track groups exposed safely");
const bool auxiliaryFirst = animation->TrackGroupCount > 0 &&
!std::strcmp(animation->TrackGroups[0]->Name, "Hedra");
check(auxiliaryFirst, "auxiliary group first");
if (animation->TrackGroupCount == 2 && auxiliaryFirst) {
check(!std::strcmp(animation->TrackGroups[1]->Name, "Bip01"), "model group second");
}
std::vector<uint8_t> msa;
check(mtpack40250::read(base + name + ".msa", msa), "motion specification loads");
float accumulation[3] = {};
std::istringstream lines(std::string(msa.begin(), msa.end()));
for (std::string line; std::getline(lines, line);)
if (line.rfind("Accumulation", 0) == 0)
std::sscanf(line.c_str(), "Accumulation %f %f %f", accumulation, accumulation + 1, accumulation + 2);
check(accumulation[1] < 0, "moving resource has forward accumulation");
for (bool inverse : {false, true}) {
auto* instance = GrannyInstantiateModel(model);
auto* control = GrannyPlayControlledAnimation(0, animation, instance);
check(control != nullptr, "animation binds to named model");
if (control) {
GrannySetControlLoopCount(control, 0);
const float dt = 0.1f;
const float clock = animation->Duration + dt; // across the loop boundary
GrannySetModelClock(instance, clock);
float matrix[16] = {0,1,0,0, -1,0,0,0, 0,0,1,0, 100,200,0,1};
GrannyUpdateModelMatrix(instance, dt, matrix, matrix, inverse);
const float expectedX = 100 - (inverse ? -1.0f : 1.0f) * accumulation[1] * dt / animation->Duration;
check(std::abs(matrix[12] - expectedX) < 0.02f && std::abs(matrix[13] - 200) < 0.02f,
"model group root displacement, loop, rotation and inverse");
}
GrannyFreeModelInstance(instance);
}
// Hedra has no root displacement even though the other group moves.
granny_model decoration = *model;
decoration.Name = "Hedra";
auto* instance = GrannyInstantiateModel(&decoration);
auto* control = GrannyPlayControlledAnimation(0, animation, instance);
check(control != nullptr, "auxiliary object binds to its own group");
GrannySetModelClock(instance, 0.1f);
float matrix[16] = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};
GrannyUpdateModelMatrix(instance, 0.1f, matrix, matrix, false);
check(matrix[12] == 0 && matrix[13] == 0, "auxiliary group does not inherit bear movement");
GrannyFreeModelInstance(instance);
GrannyFreeFile(file);
}
}
int main(int argc, char** argv) {
if (argc < 2 || !mtpack40250::initialize(argv[1])) return std::getenv("MT_ASSETS_STRICT") ? 1 : 77;
auto* file = load("d:/ymir work/monster/bear/bear.gr2");
check(file != nullptr, "bear model loads");
if (!file) return 1;
auto* info = GrannyGetFileInfo(file);
check(info && info->ModelCount == 1, "one bear model");
if (info && info->ModelCount == 1) {
test_motion(info->Models[0], "02");
test_motion(info->Models[0], "03");
}
GrannyFreeFile(file);
return failures ? 1 : 0;
}