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
Binary file modified docs/gallery/assets/fence-kit-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/hand-pump-hero.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
578 changes: 482 additions & 96 deletions docs/gallery/fence-kit/index.html

Large diffs are not rendered by default.

740 changes: 547 additions & 193 deletions docs/gallery/hand-pump/index.html

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions docs/gallery/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -975,12 +975,12 @@ <h2><a href="terrain-scatter/">terrain-scatter</a></h2>
</article>
<article class="card" data-tags="mesh export showcase">
<a class="card-media" href="fence-kit/" aria-label="fence-kit example detail page">
<img src="assets/fence-kit-hero.webp" alt="fence-kit — A procedural post-and-rail fence section through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets rather than an API contract." loading="lazy" decoding="async" />
<img src="assets/fence-kit-hero.webp" alt="fence-kit — A procedural post-and-rail fence section whose brace is an oriented box between named post stations, through UVs, bake, LOD, collider, and Unity glTF" loading="lazy" decoding="async" />
</a>
<div class="card-body">
<h2><a href="fence-kit/">fence-kit</a></h2>
<p class="teaches">A procedural post-and-rail fence section 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: 1212 tris, two materials with 120 metal faces, UVs in 0..1 with zero AABB overlap, outer AABB 1.642×0.157×1.235 m, LOD ratios in band, convex collider 62 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</p>
<p class="teaches">A procedural post-and-rail fence section whose brace is an oriented box between named post stations, 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: 1092 tris, two materials with 156 metal and 390 wood faces, UVs in 0..1 with zero AABB overlap, outer AABB 1.624×0.134×1.232 m, grounded zmin, 2 iron shoes, brace-to-post gap 2.4 mm on both posts, rail-to-post gap 0.93 mm, rail span 1.390 m, LOD ratios in band (5.2 COLLAPSE leaner on LOD2), convex collider 32 tris, hygiene all zero, non-empty glTF. --skip-decimate exits 9; --stray-vert 15; --lift-z/--short-shoes 16; --short-brace 17; --gap-rails 18; --long-rails 19.</p>
<a class="card-link" href="fence-kit/">View example <span aria-hidden="true">&rarr;</span></a>
</div>
</article>
Expand Down Expand Up @@ -1074,12 +1074,12 @@ <h2><a href="grindstone/">grindstone</a></h2>
</article>
<article class="card" data-tags="mesh export showcase">
<a class="card-media" href="hand-pump/" aria-label="hand-pump example detail page">
<img src="assets/hand-pump-hero.webp" alt="hand-pump — A procedural village hand pump with a gooseneck through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets rather than an API contract." loading="lazy" decoding="async" />
<img src="assets/hand-pump-hero.webp" alt="hand-pump — A procedural village hand pump whose gooseneck is a 6-gon tube from the lower-column radius, through UVs, bake, LOD, collider, and Unity glTF, asserting" loading="lazy" decoding="async" />
</a>
<div class="card-body">
<h2><a href="hand-pump/">hand-pump</a></h2>
<p class="teaches">A procedural village hand pump with a gooseneck 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: 1164 tris, two materials with 222 metal faces, UVs in 0..1 with zero AABB overlap, outer AABB 0.610×0.364×1.049 m, LOD ratios in band (5.2 COLLAPSE more aggressive on LOD2), convex collider 110 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</p>
<p class="teaches">A procedural village hand pump whose gooseneck is a 6-gon tube from the lower-column radius, 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: 1064 tris, two materials with 564 metal and 144 wood faces, UVs in 0..1 with zero AABB overlap, outer AABB 0.610×0.382×1.052 m, grounded zmin, spout-to-column gap 2.7 mm, flange-to-plinth gap 6.0 mm, column radius 62 mm, LOD ratios in band, convex collider 114 tris, hygiene all zero, non-empty glTF. --skip-decimate exits 9; --stray-vert 15; --lift-z 16; --float-spout 17; --float-flange 18; --skinny-col 19.</p>
<a class="card-link" href="hand-pump/">View example <span aria-hidden="true">&rarr;</span></a>
</div>
</article>
Expand Down
5 changes: 5 additions & 0 deletions showcase/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ entry in `showcase/gallery.json`, and a rendered still.
the host's chamfer as a spike — that is the class this catches.
Measure in the construction frame (un-rotate by any rake) so a tilted
host does not inflate its own AABB.
- **Diagonal from stations (exit 17).** A brace, gooseneck or scroll is
an oriented primitive or a tube between named host endpoints, not a
hypot-length box rotated about its centroid. The centroid form is
short of one host and punches through the other. `--short-brace` /
`--float-spout` (or the piece's equivalent) is the falsifier.
- **Seat conformance (exit 18).** A band, hoop, strap or collar wrapped
around a host asserts a **banded** seat depth — a minimum so it cannot
float and a maximum so it cannot sink — sampled per angular segment
Expand Down
102 changes: 87 additions & 15 deletions showcase/fence-kit/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
# Fence kit

A showcase piece, not an example. Procedural post-and-rail fence
section (two posts, pyramidal caps, three rails, kickboard, brace,
iron shoes and straps) then the shipped pipeline: unique-cell UVs,
Cycles high-to-low normal bake, LOD chain, convex collider, Unity
glTF export.
section (two posts, pyramidal caps, three rails, kickboard, a brace
from named post stations, iron shoes and U-wrap straps) then the
shipped pipeline: unique-cell UVs, Cycles high-to-low normal bake, LOD
chain, convex collider, Unity glTF export.

Posts, rails, kick, brace and collars share named stations
(`POST_XS`, `RAIL_ZS`, `Y_BRACE`, `SHOE_H`). The brace is an oriented
box from the left-post lower-rail station to the right-post upper-rail
station on the back face, not a hypot-length box rotated about its
centroid. Rails and kick tenon into the post volume; rail collars are
U-wraps so the span-facing plate is not coplanar with the rail end.

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 @@ -16,6 +23,11 @@ re-witness the `modular-kit-snap` snap contract.
`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).
Hygiene combinatorics match `examples/mesh-hygiene-audit` (copied, not
imported).

Intended size: 1.60 m bay, 1.14 m posts, 0.11 m post stock; outer AABB
1.624 × 0.134 × 1.232 m.

## Budgets

Expand All @@ -24,37 +36,92 @@ materials, UVs, evaluated LOD, collider, or export file.

| Axis | Declared | Measured (4.5.11 / 5.1.2 / 5.2.1) |
| --- | --- | --- |
| Base triangles | 1160–1280 | 1212 / 1212 / 1212 |
| Base triangles | 1000–1400 | 1092 / 1092 / 1092 |
| LOD1 ratio | 0.32–0.62 of base | 0.5000 / 0.5000 / 0.5000 |
| LOD2 ratio | 0.10–0.35 of base | 0.2195 / 0.2195 / 0.2079 |
| Materials | exactly 2 distinct, ≥24 metal faces | 2 slots, 120 metal |
| LOD2 ratio | 0.10–0.35 of base | 0.2198 / 0.2198 / 0.1832 |
| Materials | exactly 2 distinct, ≥48 metal, ≥24 wood | 2 slots, 156 metal / 390 wood |
| UVs | in `0..1`, AABB overlap ≤ 1e-5 | in range, overlap 0 |
| Outer AABB | (1.642, 0.157, 1.235) m ± 0.01 | (1.6418, 0.1574, 1.2350), zmin 0 |
| Collider tris | ≤ 96 | 62 |
| Export | written, size > 0 | 93232 / 93232 / 93224 bytes |
| Outer AABB | (1.624, 0.134, 1.232) m ± 0.015 | (1.6240, 0.1340, 1.2320), zmin 0 |
| Collider tris | ≤ 180 | 32 |
| Export | written, size > 0 | 82216 / 82216 / 82208 bytes |

Base triangles dropped from **1212 to 1092** in the quality pass: rail
collars became U-wraps (no span-facing plate). Outer AABB tightened from
1.642 × 0.157 × 1.235 m.

DECIMATE COLLAPSE triangle counts are **not** identical across series —
5.2.1 is more aggressive on LOD2. 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. Construction uses no RNG.
Export byte counts differ by 8 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 shoes | 2, each `zmin` ≤ 0.002 | 2, 0.00000 |

### Joint fit and span

| Axis | Declared | Measured (all three) |
| --- | --- | --- |
| Brace-to-post BVH gap (both posts) | ≤ 0.008 m | 0.00240 |
| Rail-to-post BVH gap | ≤ 0.008 m | 0.00093 |
| Rail span vs `2·RAIL_HALF` | ± 0.04 m | 1.3900 vs 1.390 |

### Falsifiers

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

| 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-shoes` | named shoes plant at z=0 | 16 |
| `--short-brace` | brace-to-post BVH gap (both posts) | 17 |
| `--gap-rails` | rail-to-post BVH gap | 18 |
| `--long-rails` | rail span vs `2·RAIL_HALF` | 19 |

`--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.
`--short-shoes` lifts only the iron shoes. After recenter a post or kick
is the AABB `zmin`, so the AABB gate would still pass; the named-shoe
stations fail. `--short-brace` shortens the left station; `brace_gap` is
the max of the per-post minima so a brace that still hits the right post
cannot sneak through. `--long-rails` adds an extra spanning board; the
tenoned rails stay put so the gap budget still passes.

## Run

```bash
blender --background --python fence_kit.py --
blender --background --python fence_kit.py -- --skip-decimate
blender --background --python fence_kit.py -- --stray-vert
blender --background --python fence_kit.py -- --lift-z
blender --background --python fence_kit.py -- --short-shoes
blender --background --python fence_kit.py -- --short-brace
blender --background --python fence_kit.py -- --gap-rails
blender --background --python fence_kit.py -- --long-rails
blender --background --python fence_kit.py -- --output fence.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 @@ -63,7 +130,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 metal faces missing |
| 5 | Material count ≠ 2 distinct slots, or wood/metal faces missing |
| 6 | UVs outside 0..1 |
| 7 | UV AABB overlap above tolerance |
| 8 | World AABB off declared outer size |
Expand All @@ -73,3 +140,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 | Hygiene (`--stray-vert` lands here) |
| 16 | Grounded zmin / named shoes (`--lift-z`, `--short-shoes`) |
| 17 | Brace-to-post gap (`--short-brace`) |
| 18 | Rail-to-post gap (`--gap-rails`) |
| 19 | Rail span (`--long-rails`) |
Loading
Loading