Files
healer-man/scripts/dungeon-pipeline/creature-mesh-consolidation.test.mjs
2026-08-14 15:56:39 -04:00

103 lines
4.1 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import { Document } from "@gltf-transform/core";
import {
CREATURE_MESH_CONSOLIDATION_POLICY,
consolidateCreatureSkinnedMeshes,
} from "./creature-mesh-consolidation.mjs";
function createFixture({ animateRenderNode = false, distinctAttributes = false } = {}) {
const document = new Document();
const buffer = document.createBuffer();
const scene = document.createScene("Creature_Scene");
const root = document.createNode("CreatureRoot");
const skeleton = document.createNode("SkeletonRoot");
const joint = document.createNode("bone_root");
const skin = document.createSkin("CreatureSkin").setSkeleton(skeleton).addJoint(joint);
const material = document.createMaterial("BodyMaterial");
const position = document.createAccessor("positions", buffer)
.setType("VEC3")
.setArray(new Float32Array([
0, 0, 0,
1, 0, 0,
1, 1, 0,
0, 1, 0,
]));
scene.addChild(root);
root.addChild(skeleton);
skeleton.addChild(joint);
const renderNodes = [];
for (let index = 0; index < 2; index += 1) {
const attributes = distinctAttributes && index === 1
? document.createAccessor("positions-copy", buffer)
.setType("VEC3")
.setArray(new Float32Array(position.getArray()))
: position;
const indices = document.createAccessor(`indices-${index}`, buffer)
.setType("SCALAR")
.setArray(new Uint16Array(index === 0 ? [0, 1, 2] : [0, 2, 3]));
const primitive = document.createPrimitive()
.setMaterial(material)
.setAttribute("POSITION", attributes)
.setIndices(indices);
const mesh = document.createMesh(`GeosetMesh${index}`).addPrimitive(primitive);
const node = document.createNode(`Geoset${index}`).setMesh(mesh).setSkin(skin);
root.addChild(node);
renderNodes.push(node);
}
const animation = document.createAnimation("Stand");
const sampler = document.createAnimationSampler()
.setInput(document.createAccessor("time", buffer).setType("SCALAR").setArray(new Float32Array([0, 1])))
.setOutput(document.createAccessor("rotation", buffer).setType("VEC4").setArray(new Float32Array([
0, 0, 0, 1,
0, 0, 0, 1,
])));
animation.addSampler(sampler).addChannel(
document.createAnimationChannel()
.setSampler(sampler)
.setTargetNode(animateRenderNode ? renderNodes[0] : joint)
.setTargetPath("rotation"),
);
return { document, root, joint };
}
test("consolidates compatible skinned geosets without changing skeleton animation", async () => {
const { document, root, joint } = createFixture();
const result = await consolidateCreatureSkinnedMeshes(document);
assert.equal(result.policy, CREATURE_MESH_CONSOLIDATION_POLICY);
assert.equal(result.groups, 1);
assert.equal(result.sourceDrawCalls, 2);
assert.equal(result.runtimeDrawCalls, 1);
assert.equal(result.before.triangles, 2);
assert.equal(result.after.triangles, 2);
const renderNodes = document.getRoot().listNodes().filter((node) => node.getMesh());
assert.equal(renderNodes.length, 1);
assert.equal(renderNodes[0].getSkin(), document.getRoot().listSkins()[0]);
assert.deepEqual(
Array.from(renderNodes[0].getMesh().listPrimitives()[0].getIndices().getArray()),
[0, 1, 2, 0, 2, 3],
);
assert.equal(document.getRoot().listAnimations()[0].listChannels()[0].getTargetNode(), joint);
assert.equal(root.listChildren().some((node) => node.getName() === "CreatureRoot_MergedSkinned_01"), true);
});
test("skips render nodes targeted by animation channels", async () => {
const { document } = createFixture({ animateRenderNode: true });
const result = await consolidateCreatureSkinnedMeshes(document);
assert.equal(result.groups, 0);
assert.equal(result.before.drawCalls, 2);
assert.equal(result.after.drawCalls, 2);
});
test("does not join geosets backed by different vertex attributes", async () => {
const { document } = createFixture({ distinctAttributes: true });
const result = await consolidateCreatureSkinnedMeshes(document);
assert.equal(result.groups, 0);
assert.equal(result.before.drawCalls, 2);
assert.equal(result.after.drawCalls, 2);
});