86 lines
4.2 KiB
JavaScript
86 lines
4.2 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import sharp from "sharp";
|
|
import test from "node:test";
|
|
import { Document } from "@gltf-transform/core";
|
|
import { MeshoptSimplifier } from "meshoptimizer";
|
|
import { eligibleFallbackMaterial, simplifyEligibleFallbackPrimitive } from "./shadowfang-fallback-preprocess.mjs";
|
|
|
|
function gridPrimitive(document, buffer, material, name, segments = 20) {
|
|
const positions = new Float32Array((segments + 1) * (segments + 1) * 3);
|
|
const indices = new Uint16Array(segments * segments * 6);
|
|
let indexOffset = 0;
|
|
for (let row = 0; row <= segments; row += 1) {
|
|
for (let column = 0; column <= segments; column += 1) {
|
|
const vertex = row * (segments + 1) + column;
|
|
positions.set([column, row, Math.sin(column * 0.2) * Math.cos(row * 0.2) * 0.1], vertex * 3);
|
|
if (row < segments && column < segments) {
|
|
const topLeft = vertex;
|
|
const topRight = vertex + 1;
|
|
const bottomLeft = vertex + segments + 1;
|
|
const bottomRight = bottomLeft + 1;
|
|
indices.set([topLeft, bottomLeft, topRight, topRight, bottomLeft, bottomRight], indexOffset);
|
|
indexOffset += 6;
|
|
}
|
|
}
|
|
}
|
|
const primitive = document.createPrimitive()
|
|
.setAttribute("POSITION", document.createAccessor(`${name}-position`).setType("VEC3").setArray(positions).setBuffer(buffer))
|
|
.setIndices(document.createAccessor(`${name}-indices`).setType("SCALAR").setArray(indices).setBuffer(buffer))
|
|
.setMaterial(material);
|
|
return primitive;
|
|
}
|
|
|
|
function triangleCount(primitive) {
|
|
return (primitive.getIndices()?.getCount() ?? primitive.getAttribute("POSITION").getCount()) / 3;
|
|
}
|
|
|
|
function accessorHash(accessor) {
|
|
const array = accessor.getArray();
|
|
return Buffer.from(array.buffer, array.byteOffset, array.byteLength).toString("base64");
|
|
}
|
|
|
|
test("selective Shadowfang simplification skips textured lookalikes and unrelated materials", async () => {
|
|
await MeshoptSimplifier.ready;
|
|
const document = new Document();
|
|
const buffer = document.createBuffer();
|
|
const image = await sharp({
|
|
create: { width: 1, height: 1, channels: 4, background: { r: 160, g: 120, b: 80, alpha: 1 } },
|
|
}).png().toBuffer();
|
|
const texture = document.createTexture("fallback-test").setMimeType("image/png").setImage(image);
|
|
const texturedTerrain = document.createMaterial("ADT_Terrain").setBaseColorTexture(texture);
|
|
const untexturedTerrain = document.createMaterial("ADT_Terrain.001");
|
|
const unrelated = document.createMaterial("WMO_Wall");
|
|
const profile = {
|
|
materialNamePattern: "^ADT_(Terrain|Liquid_B2)(?:\\.\\d+)?$",
|
|
sourceExpandedTriangles: { ADT_Terrain: 800 },
|
|
targetExpandedTriangles: { ADT_Terrain: 160 },
|
|
simplifyError: 1,
|
|
lockBorder: false,
|
|
};
|
|
const textured = gridPrimitive(document, buffer, texturedTerrain, "textured-terrain");
|
|
const fallback = gridPrimitive(document, buffer, untexturedTerrain, "fallback-terrain");
|
|
const wall = gridPrimitive(document, buffer, unrelated, "unrelated-wall");
|
|
const texturedBefore = {
|
|
positions: accessorHash(textured.getAttribute("POSITION")),
|
|
indices: accessorHash(textured.getIndices()),
|
|
};
|
|
const texturedTrianglesBefore = triangleCount(textured);
|
|
const fallbackTrianglesBefore = triangleCount(fallback);
|
|
const wallTrianglesBefore = triangleCount(wall);
|
|
|
|
assert.equal(eligibleFallbackMaterial(texturedTerrain, profile), null);
|
|
assert.equal(eligibleFallbackMaterial(untexturedTerrain, profile), "ADT_Terrain");
|
|
assert.equal(eligibleFallbackMaterial(unrelated, profile), null);
|
|
assert.equal(simplifyEligibleFallbackPrimitive(textured, profile), false);
|
|
assert.equal(simplifyEligibleFallbackPrimitive(wall, profile), false);
|
|
assert.equal(simplifyEligibleFallbackPrimitive(fallback, profile), true);
|
|
|
|
assert.equal(triangleCount(textured), texturedTrianglesBefore);
|
|
assert.deepEqual({
|
|
positions: accessorHash(textured.getAttribute("POSITION")),
|
|
indices: accessorHash(textured.getIndices()),
|
|
}, texturedBefore, "the textured same-name primitive must keep every accessor byte");
|
|
assert.ok(triangleCount(fallback) < fallbackTrianglesBefore, "the allowed untextured terrain must simplify");
|
|
assert.equal(triangleCount(wall), wallTrianglesBefore, "the allowlist must leave unrelated geometry alone");
|
|
});
|