Files
airdef/tools/airdef_assets/bounds.py
T
chexit 5620347ffe Add three art pilots and derive chassis envelopes from final models (#115 #114)
Keep authored JSON separate from generated output. Export rotated model bounds for SDK-free navigation and check grazing voxel corners. Include unretouched in-game before/after captures and explicit art/performance limits; pilots are an intermediate pass, not completion of the full asset set.

Co-Authored-By: Codex <noreply@openai.com>
2026-11-02 21:33:59 +03:00

43 lines
2.0 KiB
Python

"""Export conservative model bounds for SDK-free chassis navigation."""
import itertools
import json
import math
from pathlib import Path
def model_bounds(model):
points = []
for element in model.get("elements", []):
rotation = element.get("rotation")
for point in itertools.product(*zip(element["from"], element["to"])):
point = list(point)
if rotation:
axis = "xyz".index(rotation["axis"])
a, b = (axis + 1) % 3, (axis + 2) % 3
origin = rotation["origin"]
x, y = point[a] - origin[a], point[b] - origin[b]
angle = math.radians(rotation["angle"])
# Rescaled rotations expand in the plane perpendicular to the rotation axis.
scale = 1 / math.cos(angle) if rotation.get("rescale") else 1
point[a] = origin[a] + (x * math.cos(angle) - y * math.sin(angle)) * scale
point[b] = origin[b] + (x * math.sin(angle) + y * math.cos(angle)) * scale
points.append(point)
if not points:
return None
return tuple([min(p[a] for p in points) for a in range(3)]), tuple([max(p[a] for p in points) for a in range(3)])
def write_rust_bounds(stage):
stage = Path(stage)
rows = ["// Generated by tools/gen_models.py from the final staged models; do not edit.\n",
"pub fn bounds(id: &str) -> Option<([f64; 3], [f64; 3])> {\n Some(match id {\n"]
for path in sorted((stage / "assets/airdef/models/block").glob("*.json")):
bounds = model_bounds(json.loads(path.read_text(encoding="utf-8")))
if bounds:
lo, hi = ([round(float(x), 6) for x in corner] for corner in bounds)
rows.append(f' "airdef:{path.stem}" => ({lo}, {hi}),\n')
rows.append(" _ => return None,\n })\n}\n")
target = stage / "src/sim/generated_bounds.rs"
target.parent.mkdir(parents=True, exist_ok=True)
target.write_text("".join(rows), encoding="utf-8")