Lattice Deform
A 2x2x2 Lattice modifier set to KEY_LINEAR on all three axes, with two top control points moved through LatticePoint.co_deform, read back through the depsgraph (evaluated_get, to_mesh, to_mesh_clear).
examples/lod-decimate-chain/
A retro rocket at LOD0/1/2 via the Decimate modifier evaluated through the depsgraph, with a wireframe of each evaluated mesh. The check proves the reduction is non-destructive, the triangle count hits ratio x base within bounds, and silhouette-critical dimensions survive.
Rendered headless by the example itself. Select it to enlarge.
runs on 4.5 LTS · 5.1 · 5.2 LTS
blender --background --python examples/lod-decimate-chain/lod_decimate_chain.py --
falsify it Breaks the contract on purpose and must exit 6
blender --background --python examples/lod-decimate-chain/lod_decimate_chain.py -- --no-decimate
A runnable example that builds a retro rocket — a lathed hull with a hollow nozzle bell, brass collar, red skirt, recessed panel seams, a raised brass shoulder band, a tangent-ogive nose and a brass antenna; four swept fins with chamfered brass-trimmed rims; a flanged porthole with a domed glass; two rows of rivets; six materials, every count a closed form — and evaluates it at LOD0/1/2 through the Decimate modifier via the depsgraph, following depsgraph-and-evaluated-data and mesh-editing-and-bmesh.
Pipeline arc: modeling/LOD here, weighting in vertex-weight-limit, export in gltf-export-roundtrip.
What it witnesses: the modifier-based LOD contract that game asset pipelines rely on.
obj.data keeps the closed-form counts (2,511 verts / 2,488 faces / 4,784 tris) — and every evaluated reference is released with to_mesh_clear(). An LOD chain that bakes the reduction into obj.data destroys the asset's LOD0 (applying the modifier instead: exit 7, caught).ratio x base_tris — measured 0.00% at ratio 0.5 (2,392 tris) and 0.13% at ratio 0.18 (860 tris). A stacked second Decimate halves the effective ratio (50.0% excursion, caught); a dropped modifier leaves evaluated == original (caught).The Decimate modifier API (decimate_type='COLLAPSE', ratio) is stable across Blender 4.5 LTS, 5.1 and 5.2 — the example runs identically on all three, which is itself the version witness (measured values match to the digit).
The render stages the three LODs left to right with a dark triangle wireframe laid over each: the same mesh through the same Decimate, then Triangulate and Wireframe, so the overlay draws exactly the triangles the check counts (measured 4,784 / 2,392 / 860 faces on the overlay's input). The density drop reads at thumbnail size — a fine mesh on LOD0, large facets on LOD2, where the porthole flange collapses to an octagon — while the silhouettes stay identical, which is contract 3 made visible. One label per LOD carries the evaluated triangle count from the check.
# Cheap correctness check (no render) — the CI check:
blender --background --python lod_decimate_chain.py --
# Falsifier: no Decimate on the LOD copies. Must exit non-zero (no reduction).
blender --background --python lod_decimate_chain.py -- --no-decimate
# Also render a still (EEVEE on a GPU host; use --engine cycles on GPU-less hosts):
blender --background --python lod_decimate_chain.py -- --output rocket.png
blender --background --python lod_decimate_chain.py -- --output rocket.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 | Base topology ≠ closed form |
| 4 | Base bbox ≠ closed form |
| 5 | LOD0 evaluated counts ≠ base |
| 6 | Evaluated tris ≥ base (--no-decimate lands here) |
| 7 | Original datablock mutated after evaluation |
| 8 | LOD tris outside ratio bounds |
| 9 | LOD bbox lost silhouette-critical dimensions |
| 10 | --output framing outside the Layer 1 band (gallery_framing) |
| 11 | --output asset below the quality floors (gallery_asset_quality) |
| 12 | --output produced no file |
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 --no-decimate (expects exit 6).