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"); });