Files
healer-man/scripts/runewaker-pipeline/lib/rom-derived-support-styling.test.mjs
2026-10-06 22:25:34 -04:00

113 lines
6.4 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { existsSync } from "node:fs";
import { readFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { Document, NodeIO } from "@gltf-transform/core";
import { ALL_EXTENSIONS } from "@gltf-transform/extensions";
import { MeshoptDecoder } from "meshoptimizer";
import { applyRunewakerDerivedSupportStyling, listRunewakerDerivedSupportStylingPolicies } from "./rom-derived-support-styling.mjs";
import { applyRunewakerDerivedSupportStylingToFile } from "./rom-derived-support-styling-file.mjs";
const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
const mysticVisual = path.join(repositoryRoot, "runewaker-export-work/mystic-altar/converted/mystic-altar-visual.glb");
const mysticReport = path.join(repositoryRoot, "runewaker-export-work/mystic-altar/prepared/export-report.json");
const mysticMetadata = path.join(repositoryRoot, "src/assets/game/dungeons/mystic-altar/import-metadata.json");
const mysticDds = path.join(repositoryRoot, "runewaker-export-work/mystic-altar/raw/textures/caa8475c_mystic_altar_floor.dds");
const mysticRos = path.resolve(repositoryRoot, "../rom-pvt-server-backups/Runewaker/Resource/model/dungeon/mystic_altar/act_mystic_altar_instance.ros");
const sourceSha256 = (bytes) => createHash("sha256").update(bytes).digest("hex");
function accessorSnapshot(document) {
return document.getRoot().listAccessors().map((accessor) => {
const array = accessor.getArray();
return JSON.stringify({
name: accessor.getName(),
type: accessor.getType(),
count: accessor.getCount(),
hash: array ? sourceSha256(Buffer.from(array.buffer, array.byteOffset, array.byteLength)) : null,
});
}).sort();
}
function imageSnapshot(document) {
return document.getRoot().listTextures().map((texture) => JSON.stringify({
name: texture.getName(),
hash: sourceSha256(Buffer.from(texture.getImage() ?? [])),
})).sort();
}
test("unlisted packages do not read or mutate a converted source", async () => {
assert.deepEqual(await applyRunewakerDerivedSupportStylingToFile("missing-source.glb", {}), {
status: "not-requested", geometryChanged: false,
});
});
test("the base Demon support policy refuses the level125 variant before mutation", () => {
const document = new Document();
assert.throws(() => applyRunewakerDerivedSupportStyling(document, {
dungeonId: "demon-stronghold-125", policyId: listRunewakerDerivedSupportStylingPolicies()[0].id,
}), /cannot run/);
assert.equal(document.getRoot().listMaterials().length, 0);
});
test("the reviewed floor cannot be substituted with mismatched source DDS content", () => {
const policy = listRunewakerDerivedSupportStylingPolicies()[0];
const document = new Document();
assert.throws(() => applyRunewakerDerivedSupportStyling(document, {
dungeonId: policy.dungeonId, policyId: policy.id,
sourceReport: { sourceModel: policy.sourceModel, sourceResourceSha256: policy.sourceModelSha256,
materials: [{ name: policy.sourceMaterial, sourceTexture: policy.sourceTexture, stagedTexture: policy.stagedTexture }] },
sourceDdsBytes: Buffer.from("unreviewed floor"),
}), /source DDS hash/);
assert.equal(document.getRoot().listMaterials().length, 0);
});
test("Mystic derived pads use the source floor UV mapping without changing native accessors or images", {
skip: ![mysticVisual, mysticReport, mysticMetadata, mysticDds, mysticRos].every(existsSync),
}, async () => {
const io = new NodeIO().registerExtensions(ALL_EXTENSIONS).registerDependencies({ "meshopt.decoder": MeshoptDecoder });
await MeshoptDecoder.ready;
const document = await io.read(mysticVisual);
const policy = listRunewakerDerivedSupportStylingPolicies().find(({ dungeonId }) => dungeonId === "mystic-altar");
assert.ok(policy);
const sourceReport = JSON.parse(await readFile(mysticReport, "utf8"));
const metadata = JSON.parse(await readFile(mysticMetadata, "utf8"));
sourceReport.sourceResourceSha256 = metadata.source.modelSha256;
const sourceDdsBytes = await readFile(mysticDds);
assert.equal(sourceSha256(await readFile(mysticRos)), policy.sourceModelSha256);
const originalAccessors = accessorSnapshot(document);
const originalImages = imageSnapshot(document);
const floorMaterial = document.getRoot().listMaterials().find((material) => material.getName() === policy.sourceMaterial);
const floorUv = floorMaterial?.getBaseColorTextureInfo()?.getTexCoord();
const floorImageHash = sourceSha256(Buffer.from(floorMaterial?.getBaseColorTexture()?.getImage() ?? []));
const result = applyRunewakerDerivedSupportStyling(document, {
dungeonId: "mystic-altar",
policyId: policy.id,
sourceReport,
sourceDdsBytes,
});
assert.equal(result.status, "applied");
assert.equal(result.padCount, 250);
assert.equal(result.triangleCount, 3000);
assert.deepEqual(result.sourceFloorReference.anchorWorld, policy.anchorWorld);
assert.deepEqual(result.sourceFloorReference.anchorUv, policy.anchorUv);
assert.equal(result.sourceFloorReference.densityPixelsPerMeter, 22.176030799610068);
assert.ok(Object.values(result.sourceFloorReference.fullRankFaceDeterminants).every((value) => Math.abs(value) > 1e-8));
const support = document.getRoot().listNodes().find((node) => node.getName() === policy.supportNode);
const supportPrimitive = support?.getMesh()?.listPrimitives()[0];
assert.ok(supportPrimitive?.getAttribute("TEXCOORD_1"));
assert.equal(supportPrimitive.getAttribute("TEXCOORD_0"), null);
assert.equal(supportPrimitive.getExtras().romDerivedSupportStyling.sourcePositionHash, result.policy.sourcePositionHash);
assert.equal(supportPrimitive.getExtras().romDerivedSupportStyling.sourceIndexHash, result.policy.sourceIndexHash);
const styledAccessors = accessorSnapshot(document);
for (const original of originalAccessors) assert.ok(styledAccessors.includes(original), `missing original accessor ${original}`);
assert.deepEqual(imageSnapshot(document), originalImages);
const unchangedFloorMaterial = document.getRoot().listMaterials().find((material) => material.getName() === policy.sourceMaterial);
assert.equal(unchangedFloorMaterial?.getBaseColorTextureInfo()?.getTexCoord(), floorUv);
assert.equal(sourceSha256(Buffer.from(unchangedFloorMaterial?.getBaseColorTexture()?.getImage() ?? [])), floorImageHash);
});