BMesh Gear
A 14-tooth gear built entirely with bmesh — profile ring, face, extrude — with bm.free() in a try/finally, exactly as the ownership contract demands.
examples/collision-hull-proxy/
A fire hydrant street prop inside its compound collision shell: four convex pieces hulled by bmesh.ops.convex_hull from a coarse inflated cage. The dense render mesh is never hulled - its hull would measure 380 faces, over the 255-face per-piece engine budget. Closed-form plane tests prove containment, convexity, watertightness, outward winding, and Euler characteristic 2 per piece.
Rendered headless by the example itself. Select it to enlarge.
runs on 4.5 LTS · 5.1 · 5.2 LTS
blender --background --python examples/collision-hull-proxy/collision_hull_proxy.py --
falsify it Breaks the contract on purpose and must exit 3
blender --background --python examples/collision-hull-proxy/collision_hull_proxy.py -- --shrink-hull
A runnable example that builds a fire hydrant street prop — lathed barrel, bonnet dome, three outlet caps, operating nut — plus the collision a game prop pipeline ingests: a compound of four convex hull pieces, one per part group, each built with bmesh.ops.convex_hull from a coarse collision cage, following mesh-editing-and-bmesh.
Pipeline arc neighbor: LODs in lod-decimate-chain, tangent space in triangulate-tangents, export in gltf-export-roundtrip — collision bounds are the piece an engine's physics ingest checks first, and the piece whose failure rejects the whole prop.
Scope: this witnesses the bpy-level contract a prop pipeline relies on (watertight convex pieces that enclose the render mesh within budget). It is not an engine exporter and does not produce a shippable FiveM/engine asset — it proves the geometry properties such an asset must have.
What it witnesses: engines (GTA/FiveM-style prop workflows included) ingest collision as a compound of convex pieces, each piece under a per-piece face limit (255 is the common cap). The example builds that structure and derives every property from closed forms, per piece:
sec(π/n), so each n-gon cage ring circumscribes its render ring exactly. Proud details (a rim, a nut) must be paid for in cage rows; concave details (the grooves) are free — the hull bridges over them. That trade-off is the collision-authoring lesson.What each check catches on failure: an artist editing the render mesh after the hull was generated (probe: pumper lug stretched 2×, exit 3, measured escape 0.730000); inverted hull winding (probe: polygons flipped, exit 5, signed volume −1.022381, the exact negation); a non-watertight piece (probe: one face deleted, exit 4, three border edges).
Version witness: output is byte-identical on Blender 4.5.11 LTS and 5.1.2 — same piece counts, same volumes, same excursions. bmesh.ops.convex_hull and foreach_get are stable across the pair.
The render shows the prop inside its four faceted translucent hull pieces: if any piece failed to enclose its geometry, painted metal would poke through the shell in frame.
# Cheap correctness check (no render) — the CI check:
blender --background --python collision_hull_proxy.py --
# Falsifier: shrink hull vertices. Must exit non-zero (containment).
blender --background --python collision_hull_proxy.py -- --shrink-hull
# Also render a still (EEVEE on a GPU host; use --engine cycles on GPU-less hosts):
blender --background --python collision_hull_proxy.py -- --output hydrant.png
blender --background --python collision_hull_proxy.py -- --output hydrant.png --engine cycles
Per-script sequential checks. 9 is a valid check code; there is no rule against it.
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Uncaught exception (FATAL wrapper) |
| 2 | argparse / usage |
| 3 | Render vertex escapes its hull (--shrink-hull lands here) |
| 4 | Hull edge does not border exactly two faces |
| 5 | Signed volume ≤ 0 (inverted winding) |
| 6 | Hull vertex off its own face plane |
| 7 | Euler characteristic ≠ 2 |
| 8 | Piece over the 255-face collision budget |
| 9 | --output produced no file |
| 10 | Gallery framing violation (render path) |
| 11 | Gallery asset-quality violation (render path) |
The blender-smoke workflow runs the check on Blender 5.2 LTS and 4.5 LTS (5.1 on the weekly cron, the needs-5.1 PR label, or manual dispatch). Smoke does not pass --output. Its catalog falsifier is --shrink-hull (expects exit 3).