feat(editor): add --audit-watertight project-level QA tool

Walk every .wom under <zoneDir|projectDir>, run the welded
watertight check from cli_weld + the same edge-analysis as
--info-mesh-stats, and report PASS/FAIL with the per-mesh
failure detail (boundary / non-manifold edge counts).

Exit code is the number of failures (capped at 255), so
CI pipelines can gate on `--audit-watertight $project` and
fail the build if any mesh isn't a closed solid.

Smoke-tested over 61 procedurally-generated WOMs:
  • 49 PASS — most stand-alone primitives are watertight
  • tent_fixed FAIL with 5 boundary edges = the intentional
    door cutout (correct surface count)
  • woodpile / bed / well variants FAIL with non-manifold
    edges = adjacent stacked cylinders/legs sharing corners
    (correct geometry callout)

Defaults to weld eps 1e-4 — a good balance for procedural
output where positions are exact rationals at typical scales.
This commit is contained in:
Kelsi 2026-05-09 11:11:09 -07:00
parent e732894b4c
commit 89b7e2f505
3 changed files with 145 additions and 0 deletions

View file

@ -497,6 +497,8 @@ void printUsage(const char* argv0) {
std::printf(" Bake every zone in a project into one glTF 2.0 (one mesh per zone)\n");
std::printf(" --bake-wom-collision <wom-base> [out.woc] [--weld <eps>] [--steep <deg>]\n");
std::printf(" Convert a WOM into a WOC collision file (raycast / walkability mesh) with optional vertex weld\n");
std::printf(" --audit-watertight <zoneDir|projectDir> [--weld <eps>] [--json]\n");
std::printf(" Walk every .wom under root, run welded watertight check; exit code = failure count (CI-friendly)\n");
std::printf(" --import-obj <obj-path> [wom-base]\n");
std::printf(" Convert a Wavefront OBJ back into WOM (round-trips with --export-obj)\n");
std::printf(" --export-wob-obj <wob-base> [out.obj]\n");