Anvil

showcase/anvil/

A procedural blacksmith anvil on a timber stump through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets rather than an API contract.

Rendered headless by the showcase piece itself. Select it to enlarge.

witnesses Recomputed: 3168 tris, two materials with 1040 wood and 912 metal faces, UVs in 0..1 with zero AABB overlap, outer AABB 0.621×0.414×0.569 m, LOD ratios in band, convex collider 130 tris, hygiene 0, 16 grounded staves, hoop bite 0.003 m, foot-on-head gap 0.003 m, waist corner reach 0.8572, non-empty glTF. --skip-decimate exits 9; --stray-vert 15; --lift-z/--short-staves 16; --float-anvil 17; --round-band 18; --round-waist 19.

category Village

tags mesh export showcase

blender --background --python showcase/anvil/anvil.py --

A showcase piece, not an example. Procedural London-pattern anvil on a coopered timber stump (16 jittered staves, chord-lofted iron hoops, a sawn head, spreading foot, pinched waist, one slotted face with hardy and pritchel through-holes, oval-lofted horn) then the shipped pipeline: unique-cell UVs, Cycles high-to-low normal bake, LOD chain, convex collider, Unity glTF export.

The face is one add_slotted_slab so hardy/pritchel are real holes in a single shell, not nubs glued onto stacked boxes. Hoops evaluate the same host_outer_r chord as the stave flats. --round-band is the falsifier: a circle of radius_at(z) instead of the chord.

It asserts budget conformance of the generated result. It does not witness an API contract. "It rendered without error" is not a check.

Composes skills mesh-editing-and-bmesh, bake-high-to-low, depsgraph-and-evaluated-data, engine-export-presets, and snippets bake_normal_high_to_low.py, setup_bake_target_image.py, lod_chain.py / decimate_to_budget.py, convex_hull_collider.py, export_preset_unity.py (helpers copied, not imported as a package).

Budgets#

Declared as named constants; every gate recomputes from the mesh, materials, UVs, evaluated LOD, collider, or export file.

AxisDeclaredMeasured (4.5.11 / 5.1.2 / 5.2.1)
Base triangles2500–45003168 / 3168 / 3168
LOD1 ratio0.32–0.62 of base0.5000 / 0.5000 / 0.5000
LOD2 ratio0.10–0.35 of base0.2197 / 0.2197 / 0.2197
Materialsexactly 2 distinct; ≥400 wood, ≥400 metal faces2 slots; 1040 / 912
UVsin 0..1, AABB overlap ≤ 1e-5in range, overlap 0
Outer AABB(0.621, 0.414, 0.569) m ± 0.015(0.6210, 0.4137, 0.5690), zmin 0
Collider tris≤ 280130
Exportwritten, size > 0258408 / 258408 / 258400 bytes (4.5.11 / 5.1.2 / 5.2.1)

Base triangles dropped from 6616 to 2988 in the quality pass: overlapping face boxes and a beveled superellipse horn became one slotted slab plus a circular oval loft. Hidden coincident faces were the waste.

DECIMATE COLLAPSE triangle counts are not identical across series — the gate is a ratio band, not an exact count. Bake pixels are stochastic; the gate is has_data plus operator FINISHED, not byte-identity. Stave-width jitter uses fixed seed 17, and the per-piece wood tone its own seed. Export byte counts differ on 5.2.1 (glTF serializer), not a gated axis.

Hygiene#

Recomputed from the generated mesh, not asserted about the script.

AxisDeclaredMeasured (all three)
Non-manifold edges00
Loose verts / edges0 / 00 / 0
Doubles merged at 1e-500
Zero-area faces00
N-gons00
Coplanar disjoint face pairs00
Grounded: zminwithin 1e-4 of 00.0000
Named supports: 16 staveseach zmin ≤ 0.00116, stave_z 0.00000
Body plan0.621 m long × 0.260 m high ± 0.050.6210 × 0.2600
Waist formcorner reach at the pinch ≥ 0.80 (an ellipse is 0.707)0.8572

Joint fit and seat#

AxisDeclaredMeasured (all three)
Foot-on-head BVH gap≤ 0.006 m0.00300
Hoop bite (host r − inner hoop r)0.0012–0.008 m0.00300

Waist, steel and stump#

The waist was lofted from ellipses, so under the rectangular face it read as a turned funnel, not a forged anvil. It is now a superellipse section (exponent 4.5) through eight stations: a broad foot, in to a pinch, and out into the underside of the face. Waist form measures corner reach at the narrowest ring: how far a section vertex fills its bounding rectangle's corner. --round-waist restores the ellipse and exits 19 (0.7071).

The steel was satin (metallic 0.92, roughness 0.32) and read as chrome; it is now dark forged steel with rust. Staves and head carry their own wood tone and grain. The stage grid grew from 14 to 60 m. The temp .glb is removed after its size is measured.

The hero dresses the scene without touching the checked mesh: up-facing steel (the working face and the top of the horn) is mixed toward bright, low-roughness metal by the geometry normal, as a hammer polishes it; a render-only cross-peen hammer lies on the floor beside the stump; and a low warm point light off frame stands in for the forge. The hammer is in the framing gate's element set, so it has to clear the frame edges too.

A later pass rebuilt the forging to London-pattern proportions. The reference is a 165 lb Peddinghaus: 25 in long, 10.5 in tall, a 5 in face and a 9.25 × 11 in base, so height is about 0.42 of length. The first build stood 0.18 m on 0.62 m: a 50 mm face board on a narrow stem over a small plate, with a needle horn.

Falsifiers#

Each violates one named budget. All seven were run on 4.5.11, 5.1.2 and 5.2.1 and returned the same code on each.

FlagBudget violatedExit
--skip-decimateLOD1 ratio band9
--stray-vertloose vertex count is 015
--lift-zbounding box zmin is 016
--short-stavesnamed stave supports at Z=016
--float-anvilfoot-on-head gap17
--round-bandhoop bite band18
--round-waistwaist form19

Run#

blender --background --python anvil.py --
blender --background --python anvil.py -- --skip-decimate
blender --background --python anvil.py -- --stray-vert
blender --background --python anvil.py -- --lift-z
blender --background --python anvil.py -- --short-staves
blender --background --python anvil.py -- --float-anvil
blender --background --python anvil.py -- --round-band
blender --background --python anvil.py -- --round-waist
blender --background --python anvil.py -- --output anvil.png

Smoke passes no flags.

Exit codes#

File-local. 9 is a valid check code. 10 is reserved for gallery_framing.check_framing on the --output path. 15–19 are the hygiene and joint-fit family.

CodeMeaning
0Success
1Uncaught exception (FATAL wrapper)
2argparse / usage
3Mesh did not build / no UV layer
4Base triangle count outside range
5Material count ≠ 2 distinct slots, or a face-count floor missed
6UVs outside 0..1
7UV AABB overlap above tolerance
8World AABB off declared outer size
9LOD ratio band (--skip-decimate lands here)
10Framing gate (render path only)
11Collider triangle count above ceiling
12Bake did not finish or image has no data
13Export file missing or empty
14--output produced no file
15Mesh hygiene: loose, non-manifold, zero-area, doubles, n-gons, z-fight
16Not grounded: bounding box zmin off 0, or a named stave floats
17Joint fit: foot-on-head gap (--float-anvil)
18Seat: hoop bite band (--round-band)
19Body length or height off the stated real-world size, or waist form (--round-waist)
20Gallery asset-quality violation (render path; the floors' 11 is remapped here)

Source

showcase/anvil/anvil.py View the script on GitHub →
A London-pattern forged anvil with a thick face block, tapered horn and broad flared foot on a hooped oak stand, a cross-peen hammer on the floor beside it.