From a40a51f65f9a9de3aac99fcea5a80fa01335c162 Mon Sep 17 00:00:00 2001 From: fOuttaMyPaint Date: Wed, 22 Jul 2026 09:00:35 -0400 Subject: [PATCH] feat: add collision-hull-proxy example (compound convex collision) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Game prop pipelines (engines generally, FiveM/GTA-style prop workflows specifically) ingest collision as a compound of convex pieces, each under a per-piece face budget. This example builds a fire hydrant street prop and its four-piece collision compound — each piece a bmesh.ops.convex_hull of a coarse sec(pi/n)-inflated cage — and proves per piece, from closed forms: render-vertex containment (5.9e-08), hull convexity, watertightness, positive signed volume, Euler characteristic 2, and the 255-face budget. A hull of the dense render mesh measures 368 faces — over budget — which is why pipelines hull cages, and why proud details cost cage rows while concave grooves are free. Falsified: post-hull render edit (exit 3, escape 0.730000), flipped winding (exit 5, volume -0.968858), deleted face (exit 4, 3 border edges). Check output byte-identical on Blender 4.5.11 LTS and 5.1.2. Signed-off-by: fOuttaMyPaint --- .cursor-plugin/plugin.json | 1 + .github/workflows/blender-smoke.yml | 12 + README.md | 25 +- ROADMAP.md | 4 + .../assets/collision-hull-proxy-hero.webp | Bin 0 -> 42018 bytes docs/gallery/collision-hull-proxy/index.html | 757 ++++++++++++++++++ .../collision-hull-proxy-contact-sheet.webp | Bin 0 -> 48588 bytes docs/gallery/index.html | 11 + examples/collision-hull-proxy/README.md | 72 ++ .../collision_hull_proxy.py | 509 ++++++++++++ examples/collision-hull-proxy/preview.webp | Bin 0 -> 34790 bytes examples/gallery.json | 12 + 12 files changed, 1400 insertions(+), 3 deletions(-) create mode 100644 docs/gallery/assets/collision-hull-proxy-hero.webp create mode 100644 docs/gallery/collision-hull-proxy/index.html create mode 100644 docs/gallery/contact-sheets/collision-hull-proxy-contact-sheet.webp create mode 100644 examples/collision-hull-proxy/README.md create mode 100644 examples/collision-hull-proxy/collision_hull_proxy.py create mode 100644 examples/collision-hull-proxy/preview.webp diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json index ef18564..bfb0dda 100644 --- a/.cursor-plugin/plugin.json +++ b/.cursor-plugin/plugin.json @@ -62,6 +62,7 @@ "examples/armature-bend", "examples/bmesh-gear", "examples/color-attribute-wheel", + "examples/collision-hull-proxy", "examples/compositor-glare", "examples/curve-bevel-arc", "examples/damped-track-aim", diff --git a/.github/workflows/blender-smoke.yml b/.github/workflows/blender-smoke.yml index e4b6a20..c3d41b5 100644 --- a/.github/workflows/blender-smoke.yml +++ b/.github/workflows/blender-smoke.yml @@ -448,3 +448,15 @@ jobs: # (ld.light_linking is an AttributeError). Exits non-zero on failure. xvfb-run -a "$BLENDER" --background \ --python examples/light-link-studio/light_link_studio.py -- + + - name: Shipped example - collision hull proxy (compound convex pieces) + run: | + set -euo pipefail + # Check only (no render): a lathed fire hydrant prop plus its + # four-piece compound collision, each piece a bmesh convex_hull of + # a coarse sec(pi/n)-inflated cage; asserts per-piece watertightness, + # positive signed volume, render-vertex containment and hull + # convexity by closed-form plane tests, Euler characteristic 2, and + # the 255-face per-piece engine budget. Exits non-zero on failure. + xvfb-run -a "$BLENDER" --background \ + --python examples/collision-hull-proxy/collision_hull_proxy.py -- diff --git a/README.md b/README.md index 3da8beb..713bf3b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@

- 12 skills  •  6 rules  •  2 templates  •  17 snippets  •  30 examples + 12 skills  •  6 rules  •  2 templates  •  17 snippets  •  31 examples

@@ -36,7 +36,7 @@ ## Overview -This repository ships **12 skills, 6 rules, 2 templates, 17 snippets, and 30 runnable examples** for Blender Python development targeting Blender 5.1 (current stable) with Blender 4.5 LTS fallback support. +This repository ships **12 skills, 6 rules, 2 templates, 17 snippets, and 31 runnable examples** for Blender Python development targeting Blender 5.1 (current stable) with Blender 4.5 LTS fallback support. The content is consumed by AI coding agents (Cursor, Claude Code, any MCP-capable client) when working on Blender add-ons, geometry nodes scripts, batch pipelines, or animation tooling. There is no build step. Edit the markdown and Python files directly. @@ -504,7 +504,7 @@ round-trips through the raw `POINT` buffer.

-Game asset pipeline — 5 examples +Game asset pipeline — 6 examples @@ -591,6 +591,25 @@ bit-exact weights, and identical deformation of the re-imported rig — plus the parenting hazard: unparented skinned meshes let the exporter bind an armature by name. + + + + +
+Collision hull proxy: a red fire hydrant street prop with a yellow bonnet on a dark studio floor, enclosed in four faceted translucent cyan collision shells - the compound convex hull set a game engine ingests + + +### [collision-hull-proxy](examples/collision-hull-proxy/) + +The compound-collision contract prop pipelines (engines generally, +FiveM/GTA-style prop workflows specifically) ingest: one convex hull piece +per part group, built with `bmesh.ops.convex_hull` from a coarse +`sec(π/n)`-inflated cage — never the dense render mesh, whose hull measures +368 faces, over the 255-face per-piece engine budget. Closed-form plane +tests prove containment (5.9e-08), convexity, watertightness, outward +winding, and Euler characteristic 2 per piece. Proud details cost cage rows; +concave grooves are free. +
diff --git a/ROADMAP.md b/ROADMAP.md index fe507be..fa5eda0 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -112,6 +112,10 @@ Not committed; target list for the next content version. (v0.3.0 shipped the smo - Degenerate-bevel weld hazard (snippet or rule): bevel width ≥ half a box dimension creates zero-area faces whose loops weld on glTF export (found authoring `gltf-export-roundtrip`, where the count check caught a 36-vertex weld) - ~~GAMMA_CROSS blend-curve witness~~ **SHIPPED** as `examples/vse-gamma-cross/` — the cross blends in a gamma-0.5 space: `((1-t)·√A + t·√B)²` with `t = (frame − start)/duration`, never 1 inside the effect; mid-cross dips 0.115 below the sRGB lerp from crimson/teal (closed form (0.341, 0.349, 0.463) confirmed per frame); AgX-default sampling poisons the fit (0.146 red-channel error, `view_transform='Standard'` mandatory); deleting a consumed input orphans-and-deletes the effect — follow-up to `vse-cut-list` - Falsy `bpy_prop_collection` trap snippet: an empty collection is falsy, so `editor.strips or editor.sequences` silently falls through to the legacy accessor on an empty timeline — always branch on `hasattr`; likely generalizes across the API (found authoring `vse-cut-list`) +- ~~Collision compound witness~~ **SHIPPED** as `examples/collision-hull-proxy/` — game-prop collision as a compound of convex pieces, each a `bmesh.ops.convex_hull` of a coarse `sec(π/n)`-inflated cage (containment 5.9e-08, watertight, positive signed volume, Euler 2, per-piece 255-face budget: body 70, caps 60×3, compound 250); a hull of the dense render mesh measures 374 faces — over budget — which is why pipelines hull cages; proud details cost cage rows, concave grooves are free; byte-identical on 4.5.11 and 5.1.2 +- Custom-normals / shade-by-angle witness (game prop shading) — `use_auto_smooth` removed in 4.1; assert what 4.5 vs 5.1 actually expose (`shade_smooth_by_angle` operator / Smooth-by-Angle node group), per-loop custom normals surviving depsgraph evaluation, unit-length basis, hard edges landing exactly where an independently recomputed dihedral angle crosses the threshold +- prop-origin-transform witness — origin to base center, `transform_apply` through the data API, delta transforms, `matrix_parent_inverse` so parented children do not teleport; closed forms: post-apply scale exactly (1,1,1), local bbox min Z == 0, world bbox unchanged (builds on `parent-inverse-orrery`, does not duplicate it) +- mesh-hygiene-audit witness — the engine-ingest checklist as executable contract: no ngons, no loose vertices, no non-manifold edges, no zero-area faces, consistent outward winding; closed forms via Euler characteristic and exact edge-face incidence counts ## Future (uncommitted) diff --git a/docs/gallery/assets/collision-hull-proxy-hero.webp b/docs/gallery/assets/collision-hull-proxy-hero.webp new file mode 100644 index 0000000000000000000000000000000000000000..ed5dcc1191d1816875d3b79e30fd9adba371b612 GIT binary patch literal 42018 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 z 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 W<++@ul>|Lt6m^!~H7Ht00001F(#+TZ literal 0 HcmV?d00001 diff --git a/docs/gallery/collision-hull-proxy/index.html b/docs/gallery/collision-hull-proxy/index.html new file mode 100644 index 0000000..8f468ec --- /dev/null +++ b/docs/gallery/collision-hull-proxy/index.html @@ -0,0 +1,757 @@ + + + + + + collision-hull-proxy — Examples — Blender Developer Tools + + + + + + + + + + + + + + + + + + +
+

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 368 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 — click to zoom.

+
witnesses A hull of the dense render mesh measures 368 faces, over the 255-face per-piece budget - the cage is a coarser lathe with rings inflated by sec(pi/n) so each cage ring circumscribes its render ring exactly (containment 5.9e-08). Proud details cost cage rows; concave grooves are free under the hull. Piece budgets: body 70, caps 60+60+60, compound 250.
+
+
blender --background --python examples/collision-hull-proxy/collision_hull_proxy.py --
+ +
+
+

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:

+
  • Containment. Every render-mesh vertex lies on the inner side of every face plane of its piece's hull — max excursion 5.9e-08 (tol 2e-4). This holds by construction, not luck: the cage is a coarse lathe whose rings are inflated by 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.
  • Convexity. The same plane test restricted to the hull's own vertices (5.0e-08) — what a convex solver assumes when it uploads the piece.
  • Watertight + manifold. Every hull edge borders exactly two faces, and V − E + F = 2: a convex hull is a topological sphere.
  • Outward winding. Positive signed volume by the divergence theorem (body piece 0.968858) — inverted collision is a physics no-op.
  • Budget. Every piece ≤ 255 faces: body 70, pumper 60, side caps 60+60; the compound totals 250. The naive approach fails this — a hull of the *dense render mesh* measures 368 faces, over budget, which is why real pipelines hull a cage, never the render mesh.
+

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 −0.968858, 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.

+

Run

+
# Cheap correctness check (no render) — the CI check:
+blender --background --python collision_hull_proxy.py --
+
+# 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
+

It exits non-zero on failure (render geometry escaping a hull, inverted winding, a non-watertight or non-convex piece, Euler drift, or a piece over the 255-face budget). The blender-smoke workflow runs the check on Blender 4.5 LTS and 5.1.

+
+
+

Source

+
+ examples/collision-hull-proxy/collision_hull_proxy.py + View on GitHub → +
+
"""A fire hydrant prop plus its convex collision proxies — a runnable example.
+
+Witnesses the collision contract every game prop pipeline (engines generally,
+FiveM/GTA-style prop workflows specifically) depends on: a prop's collision is
+a COMPOUND of convex pieces, each piece a watertight, outward-wound hull that
+fully encloses its render geometry and stays inside the engine's per-piece
+face budget. Each piece is built with `bmesh.ops.convex_hull` from a coarse
+collision cage — never from the dense render mesh, whose hull would blow the
+budget — and the check derives every property from closed forms:
+
+  containment  — every render-mesh vertex on the inner side of every face
+                 plane of its piece's hull (max excursion printed, tol 2e-4)
+  convexity    — the same plane test restricted to the hull's own vertices
+  watertight   — every hull edge borders exactly two faces
+  winding      — positive signed volume (divergence theorem)
+  manifold     — Euler characteristic V - E + F == 2 (a convex hull is a
+                 topological sphere)
+  budget       — every piece <= 255 faces (the common per-piece convex limit
+                 engines impose on collision meshes)
+
+The cage is a coarser lathe whose profile rings are inflated by sec(pi / n)
+so its n-gon rings circumscribe the render rings exactly — that is why
+containment holds with a measured margin of ~0 instead of by luck. Concave
+details (grooves) never touch the hull; proud details cost hull faces. That
+trade-off IS the collision-authoring lesson.
+
+By default it runs only the correctness check (no render) — the CI smoke
+check. Pass --output to also render a still:
+
+    blender --background --python collision_hull_proxy.py --                 # check only
+    blender --background --python collision_hull_proxy.py -- --output h.png  # + render
+"""
+import bpy, bmesh, sys, os, math, argparse
+from mathutils import Vector
+
+SEGMENTS = 48          # lathe resolution of the render mesh
+CAGE_SEG_BODY = 8      # collision cage resolutions — the hull face budget
+CAGE_SEG_CAP = 8       #   is spent through these, not through SEGMENTS
+CAGE_SEG_NUT = 5
+HULL_BUDGET = 255      # engine per-piece convex face limit
+TOL = 2e-4             # plane-test tolerance: cage circumscribes render exactly
+
+# ---------------------------------------------------------------------------
+# Prop construction. The hydrant is lathed around Z; caps are lathed around Z
+# too and tipped onto their sides by the object rotation. All dimensions are
+# invented for this prop — no real-world design is referenced.
+# ---------------------------------------------------------------------------
+
+# (radius, height) render profile of body + bonnet, bottom pole to top pole.
+# Ribs are GROOVES: concave details are free under a convex hull, proud ones
+# would have to be paid for in cage rows.
+BODY_PROFILE = [
+    (0.00, 0.000),
+    (0.60, 0.000),   # base flange edge
+    (0.60, 0.045),
+    (0.575, 0.055),  # flange groove (concave)
+    (0.575, 0.070),
+    (0.60, 0.080),
+    (0.60, 0.095),   # flange top edge
+    (0.48, 0.115),   # taper (convex curve, lies inside the cage chord)
+    (0.40, 0.155),
+    (0.37, 0.205),   # neck
+    (0.35, 0.320),   # barrel
+    (0.346, 0.545),
+    (0.334, 0.555),  # barrel groove (concave)
+    (0.334, 0.575),
+    (0.346, 0.590),
+    (0.345, 0.880),  # barrel top
+    (0.348, 0.930),
+    (0.336, 0.950),  # upper groove (concave)
+    (0.336, 1.000),
+    (0.348, 1.030),
+    (0.36, 1.090),   # shoulder under bonnet
+    (0.35, 1.150),   # bonnet dome (concave curve — cage keeps every row)
+    (0.30, 1.220),
+    (0.21, 1.280),
+    (0.10, 1.315),
+    (0.00, 1.325),
+]
+
+# Cage silhouette: every convex corner of the render profile, nothing else.
+# sec(pi/8) inflation makes each 8-gon cage ring circumscribe its render ring.
+CAGE_BODY_PROFILE = [
+    (0.00, 0.000),
+    (0.60, 0.000),
+    (0.60, 0.095),
+    (0.37, 0.205),
+    (0.345, 0.880),
+    (0.36, 1.090),
+    (0.35, 1.150),
+    (0.30, 1.220),
+    (0.21, 1.280),
+    (0.10, 1.315),
+    (0.00, 1.325),
+]
+
+NUT_PROFILE = [(0.09, 1.325), (0.072, 1.40), (0.0, 1.40)]
+
+# Side caps at z=0.76, pumper (bigger) at z=0.66. A cap's outlet direction
+# equals its azimuth (degrees from +X); the camera sits near azimuth 292, so
+# the pumper faces it and the side caps read on the flanks.
+SIDE_CAPS = [(0.66, 315.0), (0.76, 45.0), (0.76, 165.0)]
+PUMPER = 0  # index of the large front outlet in SIDE_CAPS
+
+
+def cap_profiles(r_cap, reach):
+    """Render and cage (r, z) profiles for one outlet cap. The rim flare is
+    1.10x the barrel radius so the dense rim rows lie inside the cage chord."""
+    render = [
+        (0.0, 0.0),
+        (r_cap, 0.0),
+        (r_cap, reach - 0.05),
+        (r_cap * 1.10, reach - 0.03),   # rim
+        (r_cap * 1.10, reach),
+        (r_cap * 0.72, reach + 0.045),  # cap dome
+        (0.0, reach + 0.06),
+    ]
+    cage = [
+        (0.0, 0.0),
+        (r_cap, 0.0),
+        (r_cap * 1.10, reach - 0.03),
+        (r_cap * 1.10, reach),
+        (r_cap * 0.72, reach + 0.045),
+        (0.0, reach + 0.06),
+    ]
+    return render, cage
+
+
+def signed_volume(me):
+    """Divergence-theorem volume; positive when face winding points outward."""
+    vol = 0.0
+    for p in me.polygons:
+        vs = [me.vertices[i].co for i in p.vertices]
+        v0 = vs[0]
+        for i in range(1, len(vs) - 1):
+            vol += v0.dot(vs[i].cross(vs[i + 1])) / 6.0
+    return vol
+
+
+def lathe_object(name, profile, segments, inflate=1.0):
+    """Spin an (r, z) profile around Z. r == 0 ends become pole vertices, so
+    the mesh closes with fans instead of degenerate rings. `inflate` scales
+    radii only — the collision cage uses sec(pi / segments) so its polygon
+    rings circumscribe the render rings exactly."""
+    me = bpy.data.meshes.new(name)
+    bm = bmesh.new()
+    try:
+        bot = top = None
+        if profile[0][0] == 0.0:
+            bot = bm.verts.new((0.0, 0.0, profile[0][1]))
+            profile = profile[1:]
+        if profile[-1][0] == 0.0:
+            top = bm.verts.new((0.0, 0.0, profile[-1][1]))
+            profile = profile[:-1]
+        rings = []
+        for i in range(segments):
+            a = 2.0 * math.pi * i / segments
+            ca, sa = math.cos(a), math.sin(a)
+            rings.append([bm.verts.new((r * inflate * ca, r * inflate * sa, z))
+                          for r, z in profile])
+        for i in range(segments):
+            j = (i + 1) % segments
+            for k in range(len(profile) - 1):
+                bm.faces.new((rings[i][k], rings[j][k], rings[j][k + 1], rings[i][k + 1]))
+            if bot is not None:
+                bm.faces.new((rings[j][0], rings[i][0], bot))
+            if top is not None:
+                bm.faces.new((rings[i][-1], rings[j][-1], top))
+        bmesh.ops.recalc_face_normals(bm, faces=bm.faces)
+        # recalc picks a consistent winding but not a direction; outward is
+        # pinned below by the same signed-volume form the check uses.
+        bm.to_mesh(me)
+    finally:
+        bm.free()
+    obj = bpy.data.objects.new(name, me)
+    bpy.context.collection.objects.link(obj)
+    if signed_volume(me) < 0.0:
+        for p in me.polygons:
+            p.flip()
+    return obj
+
+
+def make_material(name, color, metallic, roughness):
+    mat = bpy.data.materials.new(name)
+    mat.use_nodes = True
+    bsdf = mat.node_tree.nodes["Principled BSDF"]
+    bsdf.inputs["Base Color"].default_value = (*color, 1.0)
+    bsdf.inputs["Metallic"].default_value = metallic
+    bsdf.inputs["Roughness"].default_value = roughness
+    return mat
+
+
+def build_hydrant():
+    """Builds the prop and its collision cages. Returns a list of collision
+    groups: {"name", "render": [objects], "cage": [objects]} — one convex
+    hull piece per group, the compound-collision structure engines ingest."""
+    bpy.ops.wm.read_factory_settings(use_empty=True)
+    red = make_material("EnamelRed", (0.48, 0.035, 0.022), 0.15, 0.30)
+    yellow = make_material("EnamelYellow", (0.82, 0.53, 0.05), 0.15, 0.32)
+    iron = make_material("CastIron", (0.05, 0.052, 0.06), 0.9, 0.48)
+
+    inflate_body = 1.0 / math.cos(math.pi / CAGE_SEG_BODY)
+    inflate_cap = 1.0 / math.cos(math.pi / CAGE_SEG_CAP)
+    inflate_nut = 1.0 / math.cos(math.pi / CAGE_SEG_NUT)
+
+    # --- body group: body + bonnet + operating nut ---
+    body = lathe_object("Body", BODY_PROFILE, SEGMENTS)
+    body.data.materials.append(red)
+    body.data.materials.append(yellow)
+    for p in body.data.polygons:  # the bonnet dome is the yellow profile tail
+        if p.center.z > 1.09:
+            p.material_index = 1
+    nut = lathe_object("Nut", NUT_PROFILE, 5)
+    nut.data.materials.append(iron)
+    body_cage = lathe_object("BodyCage", CAGE_BODY_PROFILE, CAGE_SEG_BODY, inflate_body)
+    nut_cage = lathe_object("NutCage", NUT_PROFILE, CAGE_SEG_NUT, inflate_nut)
+    groups = [{"name": "body", "render": [body, nut], "cage": [body_cage, nut_cage]}]
+
+    # --- one group per outlet cap ---
+    for idx, (z, azim) in enumerate(SIDE_CAPS):
+        big = idx == PUMPER
+        r_cap = 0.19 if big else 0.145
+        reach = 0.62 if big else 0.56
+        prof_r, prof_c = cap_profiles(r_cap, reach)
+        cap = lathe_object("PumperCap" if big else f"SideCap{idx}", prof_r, 24)
+        cap.data.materials.append(red)
+        lug_prof = [(0.075, reach + 0.05), (0.07, reach + 0.11), (0.0, reach + 0.11)]
+        lug = lathe_object(f"Lug{idx}", lug_prof, 6)
+        lug.data.materials.append(iron)
+        cap_cage = lathe_object(f"CapCage{idx}", prof_c, CAGE_SEG_CAP, inflate_cap)
+        lug_cage = lathe_object(f"LugCage{idx}", lug_prof, CAGE_SEG_CAP, inflate_cap)
+        rot = (math.radians(90), 0.0, math.radians(azim + 90.0))
+        for o in (cap, lug, cap_cage, lug_cage):
+            o.rotation_euler = rot
+            o.location = (0.0, 0.0, z)
+        groups.append({"name": "pumper" if big else f"side{idx}",
+                       "render": [cap, lug], "cage": [cap_cage, lug_cage]})
+    return groups
+
+
+# ---------------------------------------------------------------------------
+# Hull construction and the contract checks.
+# ---------------------------------------------------------------------------
+
+def collect_points(objects):
+    """World-space vertex cloud, read via foreach_get."""
+    bpy.context.view_layer.update()  # matrix_world lags RNA writes until evaluated
+    pts = []
+    for o in objects:
+        me = o.data
+        buf = [0.0] * (len(me.vertices) * 3)
+        me.vertices.foreach_get("co", buf)
+        mw = o.matrix_world
+        for i in range(0, len(buf), 3):
+            pts.append(mw @ Vector((buf[i], buf[i + 1], buf[i + 2])))
+    return pts
+
+
+def build_hull(name, points):
+    """Convex hull of a point cloud via bmesh.ops.convex_hull."""
+    me = bpy.data.meshes.new(name)
+    bm = bmesh.new()
+    try:
+        verts = [bm.verts.new(p) for p in points]
+        bmesh.ops.convex_hull(bm, input=verts)
+        # the op leaves interior input verts behind; drop any vert not
+        # referenced by a face so V - E + F is meaningful
+        used = {v for f in bm.faces for v in f.verts}
+        for v in list(bm.verts):
+            if v not in used:
+                bm.verts.remove(v)
+        bmesh.ops.recalc_face_normals(bm, faces=bm.faces)
+        bm.to_mesh(me)
+    finally:
+        bm.free()
+    obj = bpy.data.objects.new(name, me)
+    bpy.context.collection.objects.link(obj)
+    return obj
+
+
+def max_plane_excursion(me, points):
+    """Largest signed distance of any point to the OUTER side of any face
+    plane. <= 0 means every point is inside or on the hull."""
+    worst = -math.inf
+    where = None
+    for p in me.polygons:
+        n = p.normal
+        c = p.center
+        for q in points:
+            d = (q - c).dot(n)
+            if d > worst:
+                worst = d
+                where = (p.index, tuple(round(v, 4) for v in q))
+    return worst, where
+
+
+def check(pieces):
+    """pieces: [(group_name, hull_obj, render_points)]. Returns exit code."""
+    total_faces = 0
+    for name, hull, points in pieces:
+        me = hull.data
+        nv, ne, nf = len(me.vertices), len(me.edges), len(me.polygons)
+        total_faces += nf
+
+        bm = bmesh.new()  # watertight: every edge borders exactly two faces
+        try:
+            bm.from_mesh(me)
+            bad_edges = sum(1 for e in bm.edges if len(e.link_faces) != 2)
+        finally:
+            bm.free()
+        if bad_edges:
+            print(f"ERROR: piece {name}: {bad_edges} hull edge(s) do not border "
+                  f"exactly two faces — proxy is not watertight", file=sys.stderr)
+            return 4
+
+        vol = signed_volume(me)  # winding: positive signed volume
+        if vol <= 0.0:
+            print(f"ERROR: piece {name}: signed volume {vol:.6f} <= 0 — hull "
+                  f"winding is inverted", file=sys.stderr)
+            return 5
+
+        contain_err, where = max_plane_excursion(me, points)
+        if contain_err > TOL:
+            print(f"ERROR: piece {name}: render vertex escapes its hull by "
+                  f"{contain_err:.6f} (tol {TOL}) at face {where[0]}, point "
+                  f"{where[1]} — proxy does not enclose the render mesh",
+                  file=sys.stderr)
+            return 3
+
+        hull_pts = [v.co.copy() for v in me.vertices]
+        hull_err, _ = max_plane_excursion(me, hull_pts)
+        if hull_err > TOL:
+            print(f"ERROR: piece {name}: hull vertex off its own face plane by "
+                  f"{hull_err:.6f} — proxy is not convex", file=sys.stderr)
+            return 6
+
+        euler = nv - ne + nf  # a convex hull is a topological sphere
+        if euler != 2:
+            print(f"ERROR: piece {name}: Euler characteristic {euler} != 2 — "
+                  f"hull is not a closed manifold sphere", file=sys.stderr)
+            return 7
+
+        if nf > HULL_BUDGET:
+            print(f"ERROR: piece {name}: {nf} faces, over the {HULL_BUDGET}-face "
+                  f"per-piece collision budget", file=sys.stderr)
+            return 8
+
+        print(f"piece {name}: verts={nv} edges={ne} faces={nf} watertight=True "
+              f"euler=2 volume={vol:.6f} containment={contain_err:.3e} "
+              f"convexity={hull_err:.3e} budget={nf}<={HULL_BUDGET}")
+
+    print(f"compound: pieces={len(pieces)} total_faces={total_faces} "
+          f"(per-piece budget {HULL_BUDGET}; engine ingests the compound)")
+    return 0
+
+
+# ---------------------------------------------------------------------------
+# Render: dark-studio staging per docs/VISUAL-STYLE.md. The hull pieces draw
+# as faceted translucent shells with a thin wire overlay; if a piece failed
+# to enclose its geometry, painted metal would poke through the shell.
+# ---------------------------------------------------------------------------
+
+def eevee_engine_id():
+    return 'BLENDER_EEVEE' if bpy.app.version >= (5, 0, 0) else 'BLENDER_EEVEE_NEXT'
+
+
+def render_still(groups, pieces, path, engine):
+    scene = bpy.context.scene
+    for g in groups:
+        for o in g["render"]:
+            for p in o.data.polygons:
+                p.use_smooth = True
+    for g in groups:  # cages exist only to build hulls; never rendered
+        for o in g["cage"]:
+            o.hide_render = True
+
+    # translucent shell: Transparent mixed over Principled works identically
+    # in EEVEE and Cycles without touching blend-method RNA (renamed in 4.2)
+    shell = bpy.data.materials.new("HullShell")
+    shell.use_nodes = True
+    nt = shell.node_tree
+    nt.nodes.clear()
+    out = nt.nodes.new("ShaderNodeOutputMaterial")
+    mix = nt.nodes.new("ShaderNodeMixShader")
+    mix.inputs[0].default_value = 0.14          # 86% transparent: output =
+                                                # (1-Fac)*Transparent + Fac*Principled
+    transp = nt.nodes.new("ShaderNodeBsdfTransparent")
+    bsdf = nt.nodes.new("ShaderNodeBsdfPrincipled")
+    bsdf.inputs["Base Color"].default_value = (0.16, 0.55, 0.75, 1.0)
+    bsdf.inputs["Roughness"].default_value = 0.12
+    nt.links.new(transp.outputs[0], mix.inputs[1])
+    nt.links.new(bsdf.outputs[0], mix.inputs[2])
+    nt.links.new(mix.outputs[0], out.inputs[0])
+
+    wire_mat = make_material("HullWire", (0.35, 0.85, 1.0), 0.0, 0.4)
+    wbsdf = wire_mat.node_tree.nodes["Principled BSDF"]
+    wbsdf.inputs["Emission Color"].default_value = (0.2, 0.65, 0.85, 1.0)
+    wbsdf.inputs["Emission Strength"].default_value = 0.6
+
+    for name, hull, _ in pieces:
+        hull.data.materials.append(shell)
+        for p in hull.data.polygons:
+            p.use_smooth = False                # facets must read as facets
+        hull.data.materials.append(wire_mat)
+        wire = bpy.data.objects.new(f"{name}Wire", hull.data)
+        mod = wire.modifiers.new("Wire", 'WIREFRAME')
+        mod.thickness = 0.010
+        mod.material_offset = 1
+        scene.collection.objects.link(wire)
+
+    floor_me = bpy.data.meshes.new("Floor")
+    bm = bmesh.new()
+    try:
+        bmesh.ops.create_grid(bm, x_segments=1, y_segments=1, size=30.0)
+        bm.to_mesh(floor_me)
+    finally:
+        bm.free()
+    fmat = make_material("Studio", (0.03, 0.032, 0.037), 0.0, 0.7)
+    floor_me.materials.append(fmat)
+    floor = bpy.data.objects.new("Floor", floor_me)
+    scene.collection.objects.link(floor)
+    wall = bpy.data.objects.new("Wall", floor_me.copy())
+    wall.location = (0.0, 11.0, 0.0)
+    wall.rotation_euler = (math.radians(90), 0.0, 0.0)
+    scene.collection.objects.link(wall)
+
+    world = bpy.data.worlds.new("World")
+    world.use_nodes = True
+    world.node_tree.nodes["Background"].inputs["Color"].default_value = (0.02, 0.021, 0.025, 1.0)
+    scene.world = world
+
+    def light(name, loc, energy, size, col, rot):
+        ld = bpy.data.lights.new(name, 'AREA')
+        ld.energy = energy; ld.size = size; ld.color = col
+        ob = bpy.data.objects.new(name, ld)
+        ob.location = loc
+        ob.rotation_euler = tuple(math.radians(a) for a in rot)
+        scene.collection.objects.link(ob)
+
+    # key/fill/rim/wedge per docs/VISUAL-STYLE.md
+    light("Key", (-3.5, -4.5, 5.5), 500.0, 4.5, (1.0, 0.96, 0.9), (48, 0, -35))
+    light("Fill", (5.0, -3.5, 2.5), 110.0, 9.0, (0.75, 0.85, 1.0), (65, 0, 50))
+    light("Rim", (1.5, 4.5, 3.5), 300.0, 3.0, (0.6, 0.78, 1.0), (-55, 0, 170))
+    light("Wedge", (2.5, 5.5, 4.0), 380.0, 6.0, (1.0, 0.76, 0.5), (-68, 0, 190))
+    # small warm glint off the camera-left shoulder: lifts the pumper cap face
+    light("Glint", (-1.5, -4.0, 3.5), 260.0, 2.0, (1.0, 0.9, 0.75), (55, 0, -15))
+
+    cam_data = bpy.data.cameras.new("Cam")
+    cam_data.lens = 52.0
+    cam = bpy.data.objects.new("Cam", cam_data)
+    cam.location = (2.05, -4.5, 1.85)
+    scene.collection.objects.link(cam)
+    target = bpy.data.objects.new("Aim", None)
+    target.location = (0.0, 0.0, 0.72)
+    scene.collection.objects.link(target)
+    con = cam.constraints.new('TRACK_TO')
+    con.target = target
+    scene.camera = cam
+
+    scene.render.engine = 'CYCLES' if engine == 'cycles' else eevee_engine_id()
+    if engine == 'cycles':
+        scene.cycles.samples = 48
+    else:
+        try:
+            scene.eevee.taa_render_samples = 64
+        except AttributeError:
+            pass
+    scene.render.resolution_x = 1280
+    scene.render.resolution_y = 720
+    scene.render.image_settings.file_format = 'PNG'
+    scene.render.filepath = path
+    # AgX would flatten the enamel toward pastel (docs/VISUAL-STYLE.md)
+    scene.view_settings.view_transform = 'Standard'
+    bpy.ops.render.render(write_still=True)
+    return os.path.exists(path) and os.path.getsize(path) > 0
+
+
+def main():
+    argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []
+    p = argparse.ArgumentParser()
+    p.add_argument("--output", default=None, help="optional: render a still PNG here")
+    p.add_argument("--engine", default="eevee", choices=("eevee", "cycles"),
+                   help="render engine for --output (cycles for GPU-less hosts)")
+    args = p.parse_args(argv)
+
+    groups = build_hydrant()
+    pieces = []
+    for g in groups:
+        hull = build_hull(f"{g['name']}Hull", collect_points(g["cage"]))
+        pieces.append((g["name"], hull, collect_points(g["render"])))
+    code = check(pieces)
+    if code:
+        return code
+
+    if args.output:
+        if not render_still(groups, pieces, os.path.abspath(args.output), args.engine):
+            print("ERROR: render produced no file", file=sys.stderr)
+            return 9
+        print(f"rendered still {args.output}")
+
+    print("collision-hull-proxy OK")
+    return 0
+
+
+if __name__ == "__main__":
+    try:
+        sys.exit(main())
+    except Exception as e:
+        import traceback; traceback.print_exc(); print(f"FATAL: {e}", file=sys.stderr); sys.exit(1)
+
+
+
+ +
+
+ generated from examples/gallery.json + CC-BY-NC-ND-4.0 + exit 0 +
+
+ + + diff --git a/docs/gallery/contact-sheets/collision-hull-proxy-contact-sheet.webp b/docs/gallery/contact-sheets/collision-hull-proxy-contact-sheet.webp new file mode 100644 index 0000000000000000000000000000000000000000..52fff979feb188b24f5d646c13304a649e30449a GIT binary patch literal 48588 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 zmQ0zWT&;Mlight-link-studio View example +
+ + 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. + +
+

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 368 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.

+

witnesses A hull of the dense render mesh measures 368 faces, over the 255-face per-piece budget - the cage is a coarser lathe with rings inflated by sec(pi/n) so each cage ring circumscribes its render ring exactly (containment 5.9e-08). Proud details cost cage rows; concave grooves are free under the hull. Piece budgets: body 70, caps 60+60+60, compound 250.

+ View example +
+