// Real packed bear assets: the auxiliary Hedra group precedes the moving Bip01 group. #include "granny.h" #include "../../src/platform/PackBackend.h" #include #include #include #include #include #include #include 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 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 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; }