Skip to content

feat: add crate-stack and stone-archway showcase pieces - #199

Merged
TMHSDigital merged 4 commits into
mainfrom
feat/showcase-crate-stack-stone-archway
Sep 22, 2026
Merged

TMHSDigital merged 4 commits into
mainfrom
feat/showcase-crate-stack-stone-archway

Conversation

@TMHSDigital

Copy link
Copy Markdown
Owner

Two showcase pieces, one commit each, taking the tree from 26 to 28. Every number below was measured on a local binary before the budget band around it was written.

Binaries: .scratch/blender-4.5.11-windows-x64/blender.exe (Blender 4.5.11 LTS), .scratch/blender-5.1.2-windows-x64/blender.exe (Blender 5.1.2), .scratch/blender-5.2.1-windows-x64/blender.exe (Blender 5.2.1 LTS) — versions as the binaries report them.

45 of 45 cases green: 2 pieces × 15 cases (1 default + 6 or 7 falsifiers) × 3 versions, every exit code identical on all three.


crate-stack

Three shipping crates stacked, from one generator called three times with a per-instance seed. The seed drives yaw and plank widths, so the crates read as three of the same design rather than one model pasted three times.

The README says plainly what this can and cannot be: true instancing (three objects sharing one mesh datablock) and per-instance geometry variation are mutually exclusive. The piece takes the variation; the shipped asset is the flattened single mesh an engine receives.

Budgets — declared vs measured (5.2.1)

Budget Band Measured
Base triangles 3200–7200 4380
LOD1 / LOD2 ratio 0.32–0.62 / 0.10–0.35 0.5000 / 0.2005
Materials exactly 2 distinct 2
Iron / timber faces ≥ 120 / ≥ 600 216 / 2436
UV bounds / overlap in 0..1 / ≤ 1e-5 (0.0008,0.0008)–(0.9992,0.9992) / 0.000000
Outer AABB 0.658 × 0.524 × 0.704 m ± 0.020 0.6582 × 0.5235 × 0.7040
Crate footprint, own frame 0.585 × 0.425 m ± 0.015 0.5846 × 0.4246, all three
Collider tris ≤ 260 202
Hygiene all zero 0 across loose/non-manifold/zero-area/doubles/n-gons/coplanar-disjoint
Ground runners 3, each zmin ≤ 1e-3 3 at 0.00000
Crate-to-crate seat both, gap ≤ 0.0015 m 0.00000 (overlapping)
Per-instance variation yaw spread ≥ 0.020 rad, plank spread ≥ 0.0008 m 0.0262 rad, 0.00205 m

Falsifiers — same exit on 4.5.11 / 5.1.2 / 5.2.1

Flag Named budget it breaks Exit
--skip-decimate LOD1 ratio to 1.0000 9
--stray-vert mesh hygiene, loose_v 1 15
--lift-z grounded zmin to 0.050000 16
--short-skids one of three ground runners floats 12 mm 16
--float-stack crate-to-crate seat gap to 0.03424 18
--same-seed yaw spread and plank spread both to 0 20

stone-archway

Coursed piers, projecting imposts, nine voussoirs turning a semicircle, and a keystone standing proud at the crown.

The headline budget is the one an arch can fail invisibly. Nine wedge blocks in a row are still nine wedge blocks; what makes them an arch is that their intrados vertices sit on a circle. The piece recomputes that circle from vertex positions rather than from the angles the generator used.

--off-circle exists for exactly that. It keeps the angles, joints, materials, triangle count and bounding box identical and only wanders the intrados radius by 18 mm. Every other budget still passes. Only the circle fit sees it.

Budgets — declared vs measured (5.2.1)

Budget Band Measured
Base triangles 700–2600 924
LOD1 / LOD2 ratio 0.32–0.62 / 0.10–0.35 0.5000 / 0.2121
Dressed / ashlar faces ≥ 100 / ≥ 280 182 / 364
UV bounds / overlap in 0..1 / ≤ 1e-5 (0.0017,0.0017)–(0.9983,0.9983) / 0.000000
Outer AABB 1.644 × 0.510 × 2.057 m ± 0.020 1.6440 × 0.5100 × 2.0568
Clear opening 1.200 m ± 0.015 1.2033
Collider tris ≤ 260 168
Hygiene all zero 0 across all six
Pier supports 2, each base zmin ≤ 1e-3 2 at 0.00000
Keystone proud / springing joint ≥ 0.020 m / overlap ≤ 1e-4 0.03500 / 0.00000
Mortar joints every pair in 0.006–0.017 m all eight at 0.00970
Intrados circle fit every vertex within 0.004 m of R = 0.60 0.00097
Intrados arc span ≥ 168° 178.0°

Falsifiers — same exit on 4.5.11 / 5.1.2 / 5.2.1

Flag Named budget it breaks Exit
--skip-decimate LOD1 ratio to 1.0000 9
--stray-vert mesh hygiene, loose_v 1 15
--lift-z grounded zmin to 0.050000 16
--float-pier one pier base floats 12 mm, the other still grounds the AABB 16
--sink-keystone keystone projection to 0.00875 17
--wide-mortar every joint to 0.02909 18
--off-circle intrados deviation to 0.01896 19

What the budgets caught

These are defects the gates found in my own work, not hypotheticals.

  • bmesh.ops.bevel silently repainted 94% of the archway. Every face bevel creates gets material_index 0, so chamfering nineteen blocks left 30 faces on the dressed slot and moved 464 to ashlar. The slot count and the distinct-material check both still passed — exactly the wipe class showcase/README.md warns about, arriving by a different route. Only the per-material face floor caught it. Materials are now re-stamped per block after the bevel.
  • find_doubles caught two real joints in crate-stack: the corner straps sat flush with the post corner, and the lid rested exactly on the rail top. Both were welded vertices, not near-misses. Both now bite into their host.
  • The archway's imposts landed exactly on the top course — same footprint, same plane, coincident face centres. The z-fight budget caught it; the impost now has its own mortar bed.
  • BVHTree.find_nearest is unsigned. A voussoir seated inside the pier head reported a 5.4 mm gap where there was none. Joint gaps now test overlap first.
  • Joint gaps need measuring both ways. The keystone's radial faces are wider in Y than its neighbours', so every keystone vertex on the joint lies outside the neighbour and read 33 mm instead of the 9.7 mm joint.
  • Shape classification had to move to principal axes. Crate parts are axis-aligned in their crate's frame then yawed, so a world-AABB filter measures the rotated box: a 0.554 m board yawed 0.09 rad reports 0.061 m of depth instead of 0.013 m, and every thickness-keyed filter matched nothing.
  • Stack levels had to be clustered from the runners. Binning against the declared pitch put a lid on the wrong level as soon as a falsifier shifted a crate, and the seat budget then compared a crate against itself.

Falsifiers retuned so each fails on its own budget

Five falsifiers initially tripped an earlier check, which proves nothing about the budget they were built for. Fixed by changing the model or the falsifier, never by loosening a band:

Falsifier Was Now
--stray-vert (both) exit 8, vertex outside the silhouette inflated the AABB vertex placed inside; exit 15
--short-skids exit 8, lifting both runners shortened the stack lifts one of three, AABB still grounds; exit 16
--same-seed exit 8, identical seeds also collapsed the lateral offsets placement split into its own RNG stream, footprint unchanged; exit 20
--sink-keystone exit 8, removing the projection shrank Y by 70 mm imposts added to own the envelope; exit 17
--off-circle exit 8 as --flat-arch, a lintel is 0.62 m shorter wanders the radius inside the same envelope; exit 19

Contact sheets

Both pieces composited against the pinned calibration set (armature-bend, damped-track-aim, bmesh-gear), committed under docs/gallery/contact-sheets/.

Mean luminance — calibration band is 0.1722 to 0.3048 (damped-track-aim 0.1722, armature-bend 0.2787, bmesh-gear 0.3048):

Piece Mean luminance In band
crate-stack 0.2185 yes
stone-archway 0.2229 yes

Per-criterion verdicts:

Criterion crate-stack stone-archway
Stage darkness pass — near-black floor, wall falls off to the left pass, at the lighter end of the band
Wedge warmth pass after a fix — the first sheet showed a cool grey band, so the wedge moved to the backdrop at 560 W and (1.0, 0.72, 0.40) pass after the same fix, 640 W and (1.0, 0.70, 0.36)
Subject fill 0.728 vertical, measured 0.889 vertical, measured
Saturation pass — warm saturated timber against dark iron weakest criterion; stone is inherently desaturated, pushed toward sandstone and now reads warm rather than grey
Thumbnail legibility pass — stepped stack silhouette reads at cell size pass — arch silhouette reads instantly

Both sheets drove real changes: the wedge fix above was made because the first pair of sheets showed the pool reading cool against the calibration images.

Asset sheets were not produced. No showcase piece has ever shipped one — all four in docs/gallery/asset-sheets/ are examples, as are all 44 contact sheets — and the gate lives under CLAUDE.md § "Quality Gates for Example Runs". I followed that precedent for the asset sheet and broke it for the contact sheet, on the grounds that a visual claim needs evidence. Flagging the ambiguity rather than silently picking one: showcase/README.md does not say whether either gate applies to showcase pieces, and it should.

Framing gate

Measured on the render path, not eyeballed. The archway's first framing attempt was refused (exit 10, fill y=1.000, margins 0.000 / 0.000) and the camera was pulled back until it passed:

crate-stack    framing_fill x=0.359 y=0.728  margins l=0.325 r=0.316 b=0.128 t=0.144  ok
stone-archway  framing_fill x=0.319 y=0.889  margins l=0.353 r=0.328 b=0.028 t=0.083  ok

Cross-version determinism

Every measured value is byte-identical on 4.5.11, 5.1.2 and 5.2.1 except LOD2, the documented DECIMATE COLLAPSE divergence:

Piece LOD2 tris 4.5.11 / 5.1.2 LOD2 tris 5.2.1 Ratio band
crate-stack 934 (r2 0.2199) 878 (r2 0.2005) 0.10–0.35
stone-archway 202 (r2 0.2186) 196 (r2 0.2121) 0.10–0.35

That is why both gates are ratio bands, not exact counts.

Smoke cost

Piece 4.5.11 5.1.2 5.2.1
crate-stack ~0.99 s ~1.01 s ~1.20 s
stone-archway ~0.82 s ~0.85 s ~0.98 s

All 28 showcase pieces together: 20.9 s on 5.2.1, measured by running every showcase/ row in tests/smoke/catalog.json end to end, zero failures. Well inside the ~90 s per-version ceiling.

Integration

showcase/gallery.json (28 pieces), .cursor-plugin/plugin.json showcase (28), tests/smoke/catalog.json (88 rows), README counts and preview grid, CLAUDE.md inventory line, ROADMAP.md candidate pool (both marked shipped, four new subjects added), and docs/gallery/ regenerated.

Generated HTML read rather than the source JSON, per the CLAUDE.md gate:

<title>stone-archway — Showcase — Blender Developer Tools</title>
aria-label="stone-archway showcase piece detail page"
<span class="count" ...>51 examples, 28 showcase pieces</span>

The example/showcase noun split from #196 is carrying the new pieces correctly.

Local validators: tests/check_exit_code_readme.py, tests/check_import_export_rules.py, and the counts validator all pass.

One thing noticed, not touched

showcase/shipping-crate/shipping_crate.py carries an inverted eevee_engine_id():

return "BLENDER_EEVEE_NEXT" if bpy.app.version >= (4, 2, 0) else "BLENDER_EEVEE"

Blender 5.x offers only BLENDER_EEVEE, so its --output path raises TypeError: enum "BLENDER_EEVEE_NEXT" not found on 5.1 and 5.2. Every other piece in the tree has the correct form ("BLENDER_EEVEE" if version >= (5, 0, 0)). Render-path only — smoke never passes --output, so it is not red. Left alone under this loop's do-not-touch rule; worth a quality pass.

🤖 Generated with Claude Code

TMHSDigital and others added 4 commits September 21, 2026 22:12
Three shipping crates stacked, built by one generator called three times
with a per-instance seed. The subject is reuse with variation: the seed
drives yaw and plank widths so the crates read as three of the same design
rather than one model pasted three times, and the README says plainly that
true instancing and per-instance geometry variation are mutually exclusive.

Budgets recomputed from the finished mesh, measured before they were
written: 4380 tris, 2 materials with 216 iron and 2436 timber faces, UVs in
0..1 with zero overlap, outer AABB 0.658x0.524x0.704 m, each crate's own
footprint 0.585x0.425 m in its own frame, three ground runners at z=0, both
crate-to-crate seats overlapping, LOD ratios in band, 202-tri collider,
non-empty glTF, and hygiene zero across loose/non-manifold/zero-area/
doubles/n-gons/coplanar-disjoint.

Six falsifiers, each breaking one named budget, all proven on 4.5.11,
5.1.2 and 5.2.1 with identical exit codes: --skip-decimate 9, --stray-vert
15, --lift-z 16, --short-skids 16, --float-stack 18, --same-seed 20.

Three findings the budgets forced, all documented in the README:

- Shape filters had to move to principal axes. Every part is axis-aligned
  in its crate's frame then rotated about Z, so a world-AABB filter
  measures the rotated box: a 0.554 m board yawed 0.09 rad reports 0.061 m
  of depth instead of 0.013 m, and every thickness-keyed filter matched
  nothing.
- Stack levels had to be clustered from the runners the mesh actually has.
  Binning against the declared pitch put a lid on the wrong level as soon
  as a falsifier shifted a crate, and the seat budget then compared a crate
  against itself.
- find_doubles caught two real joints, not false positives: the corner
  straps sat flush with the post corner, and the lid rested exactly on the
  rail top. Both now bite into their host.

Falsifiers were also retuned so each fails on its own budget rather than on
an earlier check: the stray vertex sits inside the silhouette, --short-skids
floats one of three runners so the AABB still grounds, and --same-seed
collapses only the design stream while placement stays per-instance so the
footprint is unchanged.

LOD2 is the one value that differs across versions (878 tris on 5.2, 934 on
4.5 and 5.1) — DECIMATE COLLAPSE, which is why the gate is a ratio band.

Adds ~1.0 s to smoke on 4.5/5.1 and ~1.2 s on 5.2.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
Building the contact sheet against the pinned calibration set
(armature-bend, damped-track-aim, bmesh-gear) showed the wedge reading as a
cool grey band rather than the warm pool the house style calls for. Move it
closer to the backdrop, raise it to 560 W, and shift its colour to
(1.0, 0.72, 0.40).

Lighting only — no geometry, no budget, no assertion, no exit code changes.
Mean luminance 0.2185, inside the calibration band [0.1722, 0.3048]. Hero
and preview re-rendered; contact sheet committed under
docs/gallery/contact-sheets/.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
A freestanding masonry arch: two coursed piers, projecting imposts, nine
voussoirs turning a semicircle, and a keystone standing proud at the crown.

The headline budget is the one an arch can fail invisibly. Nine wedge
blocks laid in a row are still nine wedge blocks; what makes them an arch
is that their intrados vertices sit on a circle. The piece recomputes that
circle from vertex positions rather than from the angles the generator
used, and asserts every intrados vertex lands within 4 mm of the declared
0.60 m radius over an arc of at least 168 degrees. Measured: 0.97 mm over
178.0 degrees.

Other budgets, all recomputed: 924 tris, two materials with 182 dressed and
364 ashlar faces, UVs in 0..1 with zero overlap, outer AABB
1.644x0.510x2.057 m, clear opening 1.203 m, both piers grounded, the
springing joint an overlap, all eight mortar joints at 9.70 mm inside a
6-17 mm band, 168-tri collider, non-empty glTF, and hygiene zero across
loose/non-manifold/zero-area/doubles/n-gons/coplanar-disjoint.

Seven falsifiers, each breaking one named budget, all proven on 4.5.11,
5.1.2 and 5.2.1 with identical exit codes: --skip-decimate 9, --stray-vert
15, --lift-z 16, --float-pier 16, --sink-keystone 17, --wide-mortar 18,
--off-circle 19.

Four findings the budgets forced, all documented in the README:

- bmesh.ops.bevel gives every face it creates material_index 0. Chamfering
  nineteen blocks left 30 faces on the dressed slot and moved 464 to
  ashlar; the slot count and the distinct-material check both still
  passed. Only the per-material face floor caught it. Materials are now
  re-stamped per block after the bevel.
- The impost landed exactly on the top course — same footprint, same
  plane, coincident face centres. It now sits on its own mortar bed.
- BVHTree.find_nearest is unsigned, so a voussoir seated inside the pier
  head reported a 5.4 mm gap where there was none. Joint gaps now test
  overlap first.
- Joint gaps need measuring both ways: the keystone's radial faces are
  wider in Y than its neighbours', so every keystone vertex on the joint
  lies outside the neighbour and read 33 mm instead of 9.7 mm.

Three falsifiers needed the model changed rather than the budget loosened.
--sink-keystone originally shrank the Y bounding box and tripped the AABB
gate, so imposts were added to own the envelope — correct masonry the arch
was missing. --off-circle began as --flat-arch, which is 0.62 m shorter and
also fails on the bounding box, proving nothing about the circle fit.
--float-pier tripped the z-fight budget because the impost was pinned to an
absolute height while its courses rose into it.

LOD2 is the one value that differs across versions (196 tris on 5.2, 202 on
4.5 and 5.1) — DECIMATE COLLAPSE, which is why the gate is a ratio band.

Adds ~0.82 s to smoke on 4.5, ~0.85 s on 5.1, ~0.98 s on 5.2. All 28
showcase pieces together run in 20.9 s on 5.2.1.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
Mark crate-stack and stone-archway shipped, and restock the pool with four
subjects identified while building them: rope bridge, grain sacks, brazier,
apothecary shelf.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
@TMHSDigital TMHSDigital added the needs-5.1 Opt-in Blender 5.1 smoke on this PR. Default matrix stays 4.5 + 5.2. Auto-label will not apply this. label Sep 22, 2026
@github-actions github-actions Bot added showcase Budget-conformance props under showcase/ documentation Improvements or additions to documentation labels Sep 22, 2026
@TMHSDigital
TMHSDigital merged commit 0cc8197 into main Sep 22, 2026
13 checks passed
@TMHSDigital
TMHSDigital deleted the feat/showcase-crate-stack-stone-archway branch September 22, 2026 02:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation needs-5.1 Opt-in Blender 5.1 smoke on this PR. Default matrix stays 4.5 + 5.2. Auto-label will not apply this. showcase Budget-conformance props under showcase/

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant