Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,182 changes: 853 additions & 329 deletions docs/gallery/anvil/index.html

Large diffs are not rendered by default.

Binary file modified docs/gallery/assets/anvil-hero.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/gallery/assets/campfire-hero.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
883 changes: 640 additions & 243 deletions docs/gallery/campfire/index.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/gallery/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,7 @@ <h2><a href="wooden-barrel/">wooden-barrel</a></h2>
<div class="card-body">
<h2><a href="campfire/">campfire</a></h2>
<p class="teaches">A procedural campfire through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets rather than an API contract.</p>
<p class="witnesses"><span class="tag">witnesses</span> Recomputed: 3616 tris, three materials with 100 wood and 48 ash faces, UVs in 0..1 with zero AABB overlap, outer AABB 0.789×0.789×0.410 m, LOD ratios in band, convex collider 254 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</p>
<p class="witnesses"><span class="tag">witnesses</span> Recomputed: 2200 tris, three materials with 216 wood, 48 ash and 1304 stone faces, UVs in 0..1 with zero AABB overlap, outer AABB 0.807×0.808×0.404 m, LOD ratios in band, convex collider 198 tris, hygiene 0, 14 grounded bottom stones, teepee kiss 0.00001 m, course seat 0, non-empty glTF. --skip-decimate exits 9; --stray-vert 15; --lift-z/--short-stones 16; --float-logs 17; --gap-courses 18.</p>
<a class="card-link" href="campfire/">View example <span aria-hidden="true">&rarr;</span></a>
</div>
</article>
Expand Down Expand Up @@ -1035,7 +1035,7 @@ <h2><a href="wheelbarrow/">wheelbarrow</a></h2>
<div class="card-body">
<h2><a href="anvil/">anvil</a></h2>
<p class="teaches">A procedural blacksmith anvil on a timber stump through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets rather than an API contract.</p>
<p class="witnesses"><span class="tag">witnesses</span> Recomputed: 6616 tris, two materials with 2616 metal faces, UVs in 0..1 with zero AABB overlap, outer AABB 0.772×0.419×0.662 m, LOD ratios in band, convex collider 190 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</p>
<p class="witnesses"><span class="tag">witnesses</span> Recomputed: 2988 tris, two materials with 1040 wood and 822 metal faces, UVs in 0..1 with zero AABB overlap, outer AABB 0.621×0.414×0.489 m, LOD ratios in band, convex collider 134 tris, hygiene 0, 16 grounded staves, hoop bite 0.003 m, foot-on-head gap 0.003 m, non-empty glTF. --skip-decimate exits 9; --stray-vert 15; --lift-z/--short-staves 16; --float-anvil 17; --round-band 18.</p>
<a class="card-link" href="anvil/">View example <span aria-hidden="true">&rarr;</span></a>
</div>
</article>
Expand Down
92 changes: 74 additions & 18 deletions showcase/anvil/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
# Anvil

A showcase piece, not an example. Procedural London-pattern anvil on a
timber stump (horn, face, heel, waist, iron hoops) then the shipped
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.

Expand All @@ -21,39 +28,83 @@ materials, UVs, evaluated LOD, collider, or export file.

| Axis | Declared | Measured (4.5.11 / 5.1.2 / 5.2.1) |
| --- | --- | --- |
| Base triangles | 6450–6800 | 6616 / 6616 / 6616 |
| Base triangles | 2500–4500 | 2988 / 2988 / 2988 |
| LOD1 ratio | 0.32–0.62 of base | 0.5000 / 0.5000 / 0.5000 |
| LOD2 ratio | 0.10–0.35 of base | 0.2198 / 0.2198 / 0.2198 |
| Materials | exactly 2 distinct, ≥24 metal faces | 2 slots, 628 wood, 2616 metal |
| LOD2 ratio | 0.10–0.35 of base | 0.2195 / 0.2195 / 0.2195 |
| Materials | exactly 2 distinct; ≥400 wood, ≥400 metal faces | 2 slots; 1040 / 822 |
| UVs | in `0..1`, AABB overlap ≤ 1e-5 | in range, overlap 0 |
| Outer AABB | (0.772, 0.419, 0.662) m ± 0.01 | (0.7720, 0.4183, 0.6618), zmin 0 |
| Collider tris | ≤ 220 | 190 |
| Export | written, size > 0 | 474984 / 474984 / 474972 bytes |
| Outer AABB | (0.621, 0.414, 0.489) m ± 0.015 | (0.6210, 0.4137, 0.4890), zmin 0 |
| Collider tris | ≤ 280 | 134 |
| Export | written, size > 0 | 244052 / 244052 / 244036 bytes |

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. Export byte counts
differ by 16 B on 5.2.1 (glTF serializer), not a gated axis.

### Hygiene

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

| Axis | Declared | Measured (all three) |
| --- | --- | --- |
| Non-manifold edges | 0 | 0 |
| Loose verts / edges | 0 / 0 | 0 / 0 |
| Doubles merged at 1e-5 | 0 | 0 |
| Zero-area faces | 0 | 0 |
| N-gons | 0 | 0 |
| Coplanar disjoint face pairs | 0 | 0 |
| Grounded: `zmin` | within 1e-4 of 0 | 0.0000 |
| Named supports: 16 staves | each `zmin` ≤ 0.001 | 16, stave_z 0.00000 |
| Body plan | 0.621 m long × 0.180 m high ± 0.05 | 0.6210 × 0.1800 |

### Joint fit and seat

| Axis | Declared | Measured (all three) |
| --- | --- | --- |
| Foot-on-head BVH gap | ≤ 0.006 m | 0.00300 |
| Hoop bite (host r − inner hoop r) | 0.0012–0.008 m | 0.00300 |

### Falsifiers

DECIMATE COLLAPSE triangle counts are **not** identical across series in
general — the gate is a ratio band, not an exact count. On this mesh LOD
ratios matched across 4.5.11 / 5.1.2 / 5.2.1. Bake pixels are stochastic;
the gate is `has_data` plus operator `FINISHED`, not byte-identity.
Construction uses no RNG. Export byte counts differ by 12 B on 5.2.1
(glTF serializer), not a gated axis.
Each violates one named budget. All six were run on 4.5.11, 5.1.2 and
5.2.1 and returned the same code on each.

`--skip-decimate` skips the LOD DECIMATE stage so LOD1 ratio is 1.0 and
exit 9 fires. That is the named budget the falsifier violates.
| Flag | Budget violated | Exit |
| --- | --- | --- |
| `--skip-decimate` | LOD1 ratio band | 9 |
| `--stray-vert` | loose vertex count is 0 | 15 |
| `--lift-z` | bounding box `zmin` is 0 | 16 |
| `--short-staves` | named stave supports at Z=0 | 16 |
| `--float-anvil` | foot-on-head gap | 17 |
| `--round-band` | hoop bite band | 18 |

## Run

```bash
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 -- --output anvil.png
```

Smoke does not pass `--output` or `--skip-decimate`.
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.
`gallery_framing.check_framing` on the `--output` path. `15`–`19` are the
hygiene and joint-fit family.

| Code | Meaning |
| --- | --- |
Expand All @@ -62,7 +113,7 @@ File-local. `9` is a valid check code. `10` is reserved for
| 2 | argparse / usage |
| 3 | Mesh did not build / no UV layer |
| 4 | Base triangle count outside range |
| 5 | Material count ≠ 2 distinct slots, or wood/metal faces missing |
| 5 | Material count ≠ 2 distinct slots, or a face-count floor missed |
| 6 | UVs outside 0..1 |
| 7 | UV AABB overlap above tolerance |
| 8 | World AABB off declared outer size |
Expand All @@ -72,3 +123,8 @@ File-local. `9` is a valid check code. `10` is reserved for
| 12 | Bake did not finish or image has no data |
| 13 | Export file missing or empty |
| 14 | `--output` produced no file |
| 15 | Mesh hygiene: loose, non-manifold, zero-area, doubles, n-gons, z-fight |
| 16 | Not grounded: bounding box `zmin` off 0, or a named stave floats |
| 17 | Joint fit: foot-on-head gap (`--float-anvil`) |
| 18 | Seat: hoop bite band (`--round-band`) |
| 19 | Body length or height off the stated real-world size |
Loading
Loading