Files
healer-man/scripts/audit-visual-materials.mjs
2026-08-14 15:56:39 -04:00

79 lines
2.3 KiB
JavaScript

import { readFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";
import { dirname, resolve } from "node:path";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
globalThis.self = globalThis;
globalThis.createImageBitmap = async () => ({
width: 1,
height: 1,
close() {},
});
globalThis.ProgressEvent ??= class ProgressEvent {
constructor(type, init = {}) {
this.type = type;
Object.assign(this, init);
}
};
const here = dirname(fileURLToPath(import.meta.url));
const visualPath = resolve(
here,
"../src/assets/game/dungeons/wailing-caverns/wailing-caverns-visual.glb",
);
const bytes = await readFile(visualPath);
const arrayBuffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
const gltf = await new Promise((resolveLoad, rejectLoad) => {
new GLTFLoader().parse(arrayBuffer, "", resolveLoad, rejectLoad);
});
const summarize = (root) => {
const uniqueMaterials = new Map();
let meshes = 0;
let instancedMeshes = 0;
let representedInstances = 0;
root.traverse((object) => {
if (!object.isMesh) return;
meshes += 1;
if (object.isInstancedMesh) {
instancedMeshes += 1;
representedInstances += object.count;
}
const materials = Array.isArray(object.material) ? object.material : [object.material];
for (const material of materials) {
const existing = uniqueMaterials.get(material.uuid) ?? {
name: material.name || "(unnamed)",
uses: 0,
transparent: material.transparent,
opacity: material.opacity,
alphaTest: material.alphaTest,
depthWrite: material.depthWrite,
side: material.side,
map: material.map?.name ?? null,
};
existing.uses += 1;
uniqueMaterials.set(material.uuid, existing);
}
});
return {
name: root.name,
meshes,
instancedMeshes,
representedInstances,
materials: [...uniqueMaterials.values()].sort((a, b) => a.name.localeCompare(b.name)),
};
};
const shell = gltf.scene.getObjectByName("WailingCaverns_Shell");
console.log(JSON.stringify({
topLevel: gltf.scene.children.map((child) => ({
name: child.name,
type: child.type,
children: child.children.length,
})),
fullScene: summarize(gltf.scene),
shell: shell ? summarize(shell) : null,
}, null, 2));