forked from phenom/healer-man
96 lines
4.4 KiB
JavaScript
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 };
|