Files
healer-man/scripts/dungeon-pipeline/build-wow-uv-precision-candidates.mjs
T
2026-10-07 13:05:04 -04:00

96 lines
4.4 KiB
JavaScript

#!/usr/bin/env node
import { spawn } from "node:child_process";
import { mkdtemp, readFile, rm, stat } from "node:fs/promises";
import { createHash } from "node:crypto";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { projectRoot, workRoot } from "./lib.mjs";
const slug = "utgarde-pinnacle";
function runNodeScript(script, args) {
return new Promise((resolve, reject) => {
const child = spawn(process.execPath, [script, ...args], {
cwd: projectRoot,
stdio: "inherit",
windowsHide: true,
});
child.once("error", reject);
child.once("exit", (code) => code === 0 ? resolve() : reject(new Error(`${path.basename(script)} exited with code ${code}.`)));
});
}
async function buildCandidates() {
const source = path.join(workRoot, slug, "staging", "environment", `${slug}-visual.glb`);
const artifacts = path.join(projectRoot, "artifacts/dungeon-pipeline");
const temporaryDirectory = await mkdtemp(path.join(os.tmpdir(), `${slug}-uv-candidates-`));
const spatialOnly = process.argv.includes("--spatial-only");
const prepared = path.join(temporaryDirectory, `${slug}-prepared.glb`);
const cli = path.join(projectRoot, "node_modules/@gltf-transform/cli/bin/cli.js");
const encoder = path.join(projectRoot, "scripts/asset-pipeline/compress-ktx2.mjs");
const outputs = [
{ name: "p14-u12", command: "meshopt", args: ["--quantize-position", "14", "--quantize-texcoord", "12"] },
{ name: "p16-u12", command: "meshopt", args: ["--quantize-position", "16", "--quantize-texcoord", "12"] },
{ name: "p16-u16", command: "meshopt", args: ["--quantize-position", "16", "--quantize-texcoord", "16"] },
{ name: "repeat-optimize-p14-u12", command: "optimize", args: [
"--compress", "meshopt", "--meshopt-level", "high", "--instance", "true", "--instance-min", "3",
"--simplify", "false", "--texture-compress", "false",
] },
];
try {
const results = [];
if (!spatialOnly) {
await runNodeScript(cli, [
"optimize", source, prepared,
"--compress", "false", "--meshopt-level", "high", "--instance", "true", "--instance-min", "3",
"--simplify", "false", "--texture-compress", "false",
]);
await runNodeScript(encoder, [prepared, "--concurrency", "1", "--jobs", "2"]);
for (const output of outputs) {
const candidate = path.join(artifacts, `${slug}-uv-${output.name}.glb`);
const args = output.command === "meshopt"
? ["meshopt", prepared, candidate, "--level", "high", ...output.args]
: ["optimize", prepared, candidate, ...output.args];
await runNodeScript(cli, args);
const bytes = await stat(candidate);
const hash = createHash("sha256");
hash.update(await readFile(candidate));
results.push({ candidate: path.relative(projectRoot, candidate).replaceAll("\\", "/"), byteLength: bytes.size, sha256: hash.digest("hex") });
}
}
const spatialPrepared = path.join(temporaryDirectory, `${slug}-spatial-prepared.glb`);
await runNodeScript(cli, [
"optimize", source, spatialPrepared,
"--compress", "false", "--instance", "false", "--flatten", "false", "--join", "false", "--weld", "false",
"--simplify", "false", "--texture-compress", "false",
]);
await runNodeScript(encoder, [spatialPrepared, "--concurrency", "1", "--jobs", "2"]);
const spatialCandidate = path.join(artifacts, `${slug}-uv-spatial-p16-u16.glb`);
await runNodeScript(cli, [
"meshopt", spatialPrepared, spatialCandidate, "--level", "high",
"--quantize-position", "16", "--quantize-texcoord", "16",
]);
const spatialBytes = await stat(spatialCandidate);
const spatialHash = createHash("sha256").update(await readFile(spatialCandidate)).digest("hex");
results.push({
candidate: path.relative(projectRoot, spatialCandidate).replaceAll("\\", "/"),
byteLength: spatialBytes.size,
sha256: spatialHash,
geometryPacking: "no instance, flatten, join, or weld; one 16-bit position/UV meshopt pass",
});
console.log(JSON.stringify({ slug, source: path.relative(projectRoot, source).replaceAll("\\", "/"), candidates: results }, null, 2));
} finally {
await rm(temporaryDirectory, { recursive: true, force: true });
}
}
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
await buildCandidates();
}
export { buildCandidates };