85 lines
3.9 KiB
JavaScript
85 lines
3.9 KiB
JavaScript
import { readFile } from "node:fs/promises";
|
|
import path from "node:path";
|
|
import { NodeIO } from "@gltf-transform/core";
|
|
|
|
const projectRoot = path.resolve(import.meta.dirname, "../..");
|
|
const catalog = JSON.parse(await readFile(path.join(projectRoot, "shared/arena-stages.json"), "utf8"));
|
|
const io = new NodeIO();
|
|
const requested = new Set(process.argv.slice(2).filter((value) => !value.startsWith("--")));
|
|
const detail = process.argv.includes("--all");
|
|
|
|
class UnionFind {
|
|
constructor(size) { this.parent = Array.from({ length: size }, (_, index) => index); }
|
|
find(value) { if (this.parent[value] !== value) this.parent[value] = this.find(this.parent[value]); return this.parent[value]; }
|
|
union(left, right) { const a = this.find(left); const b = this.find(right); if (a !== b) this.parent[b] = a; }
|
|
}
|
|
|
|
function round(values) { return values.map((value) => Math.round(value * 100) / 100); }
|
|
|
|
for (const stage of catalog.stages) {
|
|
if (requested.size && !requested.has(stage.id)) continue;
|
|
const base = path.join(projectRoot, "public", ...stage.assetBaseUrl.split("/").filter(Boolean));
|
|
let document;
|
|
try { document = await io.read(path.join(base, "navigation.glb")); } catch { continue; }
|
|
const vertices = [];
|
|
const triangles = [];
|
|
const byPosition = new Map();
|
|
for (const mesh of document.getRoot().listMeshes()) {
|
|
for (const primitive of mesh.listPrimitives()) {
|
|
const positions = primitive.getAttribute("POSITION")?.getArray();
|
|
if (!positions) continue;
|
|
const indices = primitive.getIndices()?.getArray() ?? Uint32Array.from({ length: positions.length / 3 }, (_, index) => index);
|
|
for (let offset = 0; offset < indices.length; offset += 3) {
|
|
const triangle = [];
|
|
for (let corner = 0; corner < 3; corner += 1) {
|
|
const index = indices[offset + corner];
|
|
const vertex = [positions[index * 3], positions[index * 3 + 1], positions[index * 3 + 2]];
|
|
const key = vertex.map((value) => Math.round(value * 1000)).join(":");
|
|
if (!byPosition.has(key)) {
|
|
byPosition.set(key, vertices.length);
|
|
vertices.push(vertex);
|
|
}
|
|
triangle.push(byPosition.get(key));
|
|
}
|
|
triangles.push(triangle);
|
|
}
|
|
}
|
|
}
|
|
const union = new UnionFind(vertices.length);
|
|
for (const triangle of triangles) {
|
|
union.union(triangle[0], triangle[1]);
|
|
union.union(triangle[0], triangle[2]);
|
|
}
|
|
const grouped = new Map();
|
|
for (let index = 0; index < vertices.length; index += 1) {
|
|
const root = union.find(index);
|
|
const component = grouped.get(root) ?? [];
|
|
component.push(vertices[index]);
|
|
grouped.set(root, component);
|
|
}
|
|
const components = [...grouped.values()];
|
|
components.sort((left, right) => right.length - left.length);
|
|
const main = components[0] ?? [];
|
|
const min = [0, 1, 2].map((axis) => Math.min(...main.map((vertex) => vertex[axis])));
|
|
const max = [0, 1, 2].map((axis) => Math.max(...main.map((vertex) => vertex[axis])));
|
|
const center = min.map((value, axis) => (value + max[axis]) / 2);
|
|
const axis = max[0] - min[0] >= max[2] - min[2] ? 0 : 2;
|
|
const sorted = [...main].sort((left, right) => left[axis] - right[axis]);
|
|
const inset = Math.max(1, Math.floor(sorted.length * 0.08));
|
|
const teamSpawns = [sorted[inset] ?? sorted[0], sorted[sorted.length - inset - 1] ?? sorted.at(-1)];
|
|
console.log(JSON.stringify({
|
|
id: stage.id,
|
|
componentCount: components.length,
|
|
mainVertices: main.length,
|
|
nextVertices: components[1]?.length ?? 0,
|
|
mainBounds: { min: round(min), max: round(max) },
|
|
center: round(center),
|
|
suggestedTeamSpawns: teamSpawns.map(round),
|
|
topComponents: components.slice(0, detail ? components.length : 8).map((component) => ({
|
|
vertices: component.length,
|
|
min: round([0, 1, 2].map((componentAxis) => Math.min(...component.map((vertex) => vertex[componentAxis])))),
|
|
max: round([0, 1, 2].map((componentAxis) => Math.max(...component.map((vertex) => vertex[componentAxis])))),
|
|
})),
|
|
}));
|
|
}
|