import { NodeIO } from "@gltf-transform/core"; import { ALL_EXTENSIONS } from "@gltf-transform/extensions"; import { MeshoptDecoder } from "meshoptimizer"; import { Matrix4, Vector3 } from "three"; import { writeFileSync, readdirSync } from "node:fs"; import catalog from "../../shared/arena-stages.json" with { type: "json" }; const io = new NodeIO().registerExtensions(ALL_EXTENSIONS).registerDependencies({ "meshopt.decoder": MeshoptDecoder }); const stages = {}; for (const stage of catalog.stages) { stages[stage.id] = {}; for (const kind of ["navigation", "collision"]) { const triangles = []; const files = kind === "collision" ? readdirSync(`public${stage.assetBaseUrl}`).filter((name) => /^collision(?:-\d+)?\.glb$/.test(name)) : ["navigation.glb"]; for (const file of files) { const document = await io.read(`public${stage.assetBaseUrl}/${file}`); for (const node of document.getRoot().listNodes()) { const mesh = node.getMesh(); if (!mesh) continue; const matrix = new Matrix4().fromArray(node.getWorldMatrix()); for (const primitive of mesh.listPrimitives()) { const positions = primitive.getAttribute("POSITION"); if (!positions) continue; const indices = primitive.getIndices()?.getArray() ?? Array.from({ length: positions.getCount() }, (_, index) => index); for (let offset = 0; offset < indices.length; offset += 3) { const triangle = Array.from({ length: 3 }, (_, corner) => new Vector3().fromArray(positions.getElement(indices[offset + corner], [])).applyMatrix4(matrix).toArray()); if ([0, 2].some((axis) => triangle.every((point) => point[axis] < stage.bounds.min[axis] - 3) || triangle.every((point) => point[axis] > stage.bounds.max[axis] + 3))) continue; triangles.push(...triangle.flat().map((value) => Math.round(value * 1000) / 1000)); } } } } stages[stage.id][kind] = triangles; } console.log(`${stage.id}: ${stages[stage.id].navigation.length / 9} navigation, ${stages[stage.id].collision.length / 9} collision triangles`); } writeFileSync("server/generated/arena-geometry.json", `${JSON.stringify(stages)}\n`); writeFileSync("server/generated/arena-navigation.json", `${JSON.stringify(Object.fromEntries(Object.entries(stages).map(([id, stage]) => [id, stage.navigation])))}\n`);