Skip to content

Commit be52857

Browse files
authored
fix: restage campfire and stall showcase stills
The shipped campfire clipped the floor under a 10-degree tilt; the stall was an empty frame with Lego goods. Sit the fire-ring flat and rebuild the stall as slatted timber so they read as props next to the well and crate.
1 parent f180078 commit be52857

14 files changed

Lines changed: 514 additions & 363 deletions

File tree

‎README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ Budget-conformance props. Not examples. Conventions: [`showcase/README.md`](show
7878
<a href="showcase/shipping-crate/"><img src="showcase/shipping-crate/preview.webp" alt="Shipping crate: a wooden slat crate with iron corner brackets on a dark studio floor, warm wedge on the back wall" /></a>
7979
<a href="showcase/stone-well/"><img src="showcase/stone-well/preview.webp" alt="Stone well: a round brick well with a shingled pyramid roof on posts, rope and bucket, on a dark studio floor" /></a>
8080
<a href="showcase/wooden-barrel/"><img src="showcase/wooden-barrel/preview.webp" alt="Wooden barrel: a staved barrel with iron hoops on a dark studio floor, warm wedge on the back wall" /></a>
81-
<a href="showcase/campfire/"><img src="showcase/campfire/preview.webp" alt="Campfire: a stone ring, crossed logs, and ash mound on a dark studio floor, warm wedge on the back wall" /></a>
82-
<a href="showcase/market-stall/"><img src="showcase/market-stall/preview.webp" alt="Market stall: a timber frame with a striped awning and counter on a dark studio floor, warm wedge on the back wall" /></a>
81+
<a href="showcase/campfire/"><img src="showcase/campfire/preview.webp" alt="Campfire: a two-course stone fire ring with a tripod of logs on a dark studio floor, warm wedge on the back wall" /></a>
82+
<a href="showcase/market-stall/"><img src="showcase/market-stall/preview.webp" alt="Market stall: a timber frame with post feet, slatted counter, rafters, and a striped awning on a dark studio floor, warm wedge on the back wall" /></a>
8383

8484
[`shipping-crate`](showcase/shipping-crate/) — procedural crate through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets. Falsifier `--skip-decimate` exits 9.
8585

‎ROADMAP.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ Not committed; target list for the next content version. (v0.3.0 shipped the smo
109109
- ~~Showcase sibling tree + shipping-crate pilot~~ **SHIPPED** as `showcase/shipping-crate/` — budget-conformance crate composing bake, LOD, collider, Unity glTF; `--skip-decimate` exits 9 on the LOD1 ratio band; DECIMATE COLLAPSE ratios diverge on 5.2 vs 4.5/5.1
110110
- ~~Round stone well showcase~~ **SHIPPED** as `showcase/stone-well/` — running-bond brick well, shingled pyramid roof, windlass/rope/bucket; `--skip-decimate` exits 9 on the LOD1 ratio band
111111
- ~~Wooden barrel showcase~~ **SHIPPED** as `showcase/wooden-barrel/` — bulged staves, four iron hoops, lid heads; `--skip-decimate` exits 9 on the LOD1 ratio band
112-
- ~~Campfire showcase~~ **SHIPPED** as `showcase/campfire/` — cobble ring, ash mound, crossed logs; `--skip-decimate` exits 9 on the LOD1 ratio band
113-
- ~~Market stall showcase~~ **SHIPPED** as `showcase/market-stall/` — timber frame, striped awning, counter; `--skip-decimate` exits 9 on the LOD1 ratio band
112+
- ~~Campfire showcase~~ **SHIPPED** as `showcase/campfire/` — two-course fire ring, log tripod; `--skip-decimate` exits 9 on the LOD1 ratio band
113+
- ~~Market stall showcase~~ **SHIPPED** as `showcase/market-stall/` — timber frame, slatted counter, striped awning; `--skip-decimate` exits 9 on the LOD1 ratio band
114114
- Procedural terrain or landscape showcase using Geometry Nodes scatter
115115
- Hero prop with a more complex silhouette (lantern or treasure chest) as a second showcase piece
116116
- Small modular kit showcase on recognizable geometry (`modular-kit-snap` contract)
-6.78 KB
Loading
4.21 KB
Loading

‎docs/gallery/campfire/index.html‎

Lines changed: 157 additions & 141 deletions
Large diffs are not rendered by default.

‎docs/gallery/index.html‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -925,7 +925,7 @@ <h2><a href="wooden-barrel/">wooden-barrel</a></h2>
925925
<div class="card-body">
926926
<h2><a href="campfire/">campfire</a></h2>
927927
<p class="teaches">A procedural campfire through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets rather than an API contract.</p>
928-
<p class="witnesses"><span class="tag">witnesses</span> Recomputed: 7528 tris, three materials with 116 wood and 18 ash faces, UVs in 0..1 with zero AABB overlap, outer AABB 0.964×0.950×0.252 m, LOD ratios in band, convex collider 105 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</p>
928+
<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>
929929
<a class="card-link" href="campfire/">View example <span aria-hidden="true">&rarr;</span></a>
930930
</div>
931931
</article>
@@ -936,7 +936,7 @@ <h2><a href="campfire/">campfire</a></h2>
936936
<div class="card-body">
937937
<h2><a href="market-stall/">market-stall</a></h2>
938938
<p class="teaches">A procedural timber market stall through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets rather than an API contract.</p>
939-
<p class="witnesses"><span class="tag">witnesses</span> Recomputed: 2244 tris, three materials with 48 faces per awning stripe slot, UVs in 0..1 with zero AABB overlap, outer AABB 1.360×0.964×1.743 m, LOD ratios in band, convex collider 28 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</p>
939+
<p class="witnesses"><span class="tag">witnesses</span> Recomputed: 4404 tris, three materials with 48 faces per awning stripe slot, UVs in 0..1 with zero AABB overlap, outer AABB 1.392×0.966×1.743 m, LOD ratios in band, convex collider 46 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</p>
940940
<a class="card-link" href="market-stall/">View example <span aria-hidden="true">&rarr;</span></a>
941941
</div>
942942
</article>

‎docs/gallery/market-stall/index.html‎

Lines changed: 89 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -253,18 +253,18 @@ <h1>market-stall</h1>
253253
<img src="../assets/market-stall-hero.webp" alt="market-stall render" width="1280" height="720" />
254254
</button>
255255
<p class="zoom-hint">Rendered headless by the example itself — click to zoom.</p>
256-
<div class="callout"><span class="tag">witnesses</span> Recomputed: 2244 tris, three materials with 48 faces per awning stripe slot, UVs in 0..1 with zero AABB overlap, outer AABB 1.360×0.964×1.743 m, LOD ratios in band, convex collider 28 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</div>
256+
<div class="callout"><span class="tag">witnesses</span> Recomputed: 4404 tris, three materials with 48 faces per awning stripe slot, UVs in 0..1 with zero AABB overlap, outer AABB 1.392×0.966×1.743 m, LOD ratios in band, convex collider 46 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</div>
257257
<div class="runline">
258258
<pre id="runCmd">blender --background --python showcase/market-stall/market_stall.py --</pre>
259259
<button class="copy-btn" id="copyRun" type="button">Copy</button>
260260
</div>
261261
<section class="detail-section md">
262-
<p>A showcase piece, not an example. Procedural timber stall (posts, slanted side plates, counter with apron and shelf, backboard, striped awning and valance) then the shipped pipeline: unique-cell UVs, Cycles high-to-low normal bake, LOD chain, convex collider, Unity glTF export.</p>
262+
<p>A showcase piece, not an example. Procedural timber stall (posts with feet, slanted rafters, slatted counter and shelf, back-wall planks, eave fascia, striped awning and valance) then the shipped pipeline: unique-cell UVs, Cycles high-to-low normal bake, LOD chain, convex collider, Unity glTF export.</p>
263263
<p>It asserts <strong>budget conformance</strong> of the generated result. It does not witness an API contract. &quot;It rendered without error&quot; is not a check.</p>
264264
<p><strong>Composes</strong> skills <code>mesh-editing-and-bmesh</code>, <code>bake-high-to-low</code>, <code>depsgraph-and-evaluated-data</code>, <code>engine-export-presets</code>, and snippets <code>bake_normal_high_to_low.py</code>, <code>setup_bake_target_image.py</code>, <code>lod_chain.py</code> / <code>decimate_to_budget.py</code>, <code>convex_hull_collider.py</code>, <code>export_preset_unity.py</code> (helpers copied, not imported as a package).</p>
265265
<h2>Budgets</h2>
266266
<p>Declared as named constants; every gate <strong>recomputes</strong> from the mesh, materials, UVs, evaluated LOD, collider, or export file.</p>
267-
<p>| Axis | Declared | Measured (4.5.11 / 5.1.2 / 5.2.1) | | --- | --- | --- | | Base triangles | 2140–2350 | 2244 / 2244 / 2244 | | LOD1 ratio | 0.32–0.62 of base | 0.5000 / 0.5000 / 0.5000 | | LOD2 ratio | 0.10–0.35 of base | 0.2193 / 0.2193 / 0.2193 | | Materials | exactly 3 distinct, ≥16 faces per stripe slot | 3 slots, 48 / 48 stripe faces | | UVs | in <code>0..1</code>, AABB overlap ≤ 1e-5 | in range, overlap 0 | | Outer AABB | (1.360, 0.964, 1.743) m ± 0.01 | (1.3600, 0.9639, 1.7430), zmin 0 | | Collider tris | ≤ 80 | 28 | | Export | written, size &gt; 0 | 163992 / 163992 / 163968 bytes |</p>
267+
<p>| Axis | Declared | Measured (4.5.11 / 5.1.2 / 5.2.1) | | --- | --- | --- | | Base triangles | 4280–4550 | 4404 / 4404 / 4404 | | 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 3 distinct, ≥16 faces per stripe slot | 3 slots, 48 / 48 stripe faces | | UVs | in <code>0..1</code>, AABB overlap ≤ 1e-5 | in range, overlap 0 | | Outer AABB | (1.392, 0.966, 1.743) m ± 0.01 | (1.3915, 0.9659, 1.7430), zmin 0 | | Collider tris | ≤ 80 | 46 | | Export | written, size &gt; 0 | 316748 / 316748 / 316724 bytes |</p>
268268
<p>DECIMATE COLLAPSE triangle counts are <strong>not</strong> guaranteed identical across series — the gate is a ratio band, not an exact count. This mesh happened to match on 4.5.11 / 5.1.2 / 5.2.1. Bake pixels are stochastic; the gate is <code>has_data</code> plus operator <code>FINISHED</code>, not byte-identity. Construction uses no RNG. glTF byte size differs by a few bytes across series.</p>
269269
<p><code>--skip-decimate</code> skips the LOD DECIMATE stage so LOD1 ratio is 1.0 and exit 9 fires. That is the named budget the falsifier violates.</p>
270270
<h2>Run</h2>
@@ -334,10 +334,10 @@ <h2>Source</h2>
334334
BRACE = <span class="n">0.036</span>
335335
BBOX_TOL = <span class="n">0.01</span>
336336
<span class="c"># Fitted to the generated AABB after locking geometry. Recomputed from bound_box.</span>
337-
OUTER_SIZE = (<span class="n">1.360</span>, <span class="n">0.964</span>, <span class="n">1.743</span>)
337+
OUTER_SIZE = (<span class="n">1.392</span>, <span class="n">0.966</span>, <span class="n">1.743</span>)
338338

339-
BASE_TRIS_MIN = <span class="n">2140</span>
340-
BASE_TRIS_MAX = <span class="n">2350</span>
339+
BASE_TRIS_MIN = <span class="n">4280</span>
340+
BASE_TRIS_MAX = <span class="n">4550</span>
341341
LOD1_RATIO_MIN = <span class="n">0.32</span>
342342
LOD1_RATIO_MAX = <span class="n">0.62</span>
343343
LOD2_RATIO_MIN = <span class="n">0.10</span>
@@ -396,6 +396,27 @@ <h2>Source</h2>
396396
<span class="k">return</span> verts
397397

398398

399+
<span class="k">def</span> add_cone(bm, loc, radius1, radius2, depth, segments, mat_idx, euler=(<span class="n">0.0</span>, <span class="n">0.0</span>, <span class="n">0.0</span>)):
400+
geo = bmesh.ops.create_cone(
401+
bm,
402+
cap_ends=<span class="k">True</span>,
403+
cap_tris=<span class="k">False</span>,
404+
segments=segments,
405+
radius1=radius1,
406+
radius2=radius2,
407+
depth=depth,
408+
)
409+
verts = geo[<span class="s">&quot;verts&quot;</span>]
410+
rot = Euler(euler).to_matrix()
411+
origin = Vector(loc)
412+
<span class="k">for</span> v <span class="k">in</span> verts:
413+
v.co = rot @ v.co + origin
414+
faces = {f <span class="k">for</span> v <span class="k">in</span> verts <span class="k">for</span> f <span class="k">in</span> v.link_faces}
415+
<span class="k">for</span> f <span class="k">in</span> faces:
416+
f.material_index = mat_idx
417+
<span class="k">return</span> verts
418+
419+
399420
<span class="k">def</span> pack_uvs(bm, margin=<span class="n">0.08</span>):
400421
uv = bm.loops.layers.uv.new(<span class="s">&quot;UVMap&quot;</span>)
401422
faces = list(bm.faces)
@@ -456,6 +477,9 @@ <h2>Source</h2>
456477
)
457478
<span class="k">for</span> x, y, z, h <span class="k">in</span> posts:
458479
wood_verts.extend(add_box(bm, (x, y, z), (POST, POST, h), WOOD_IDX))
480+
wood_verts.extend(
481+
add_box(bm, (x, y, <span class="n">0.018</span>), (POST * <span class="n">1.45</span>, POST * <span class="n">1.45</span>, <span class="n">0.036</span>), WOOD_IDX)
482+
)
459483

460484
<span class="c"># Front and back header beams (along X).</span>
461485
wood_verts.extend(
@@ -488,6 +512,18 @@ <h2>Source</h2>
488512
euler=(pitch, <span class="n">0.0</span>, <span class="n">0.0</span>),
489513
)
490514
)
515+
<span class="c"># Inner rafters under the awning — the empty underside was a slab of air.</span>
516+
<span class="k">for</span> i <span class="k">in</span> range(<span class="n">3</span>):
517+
rx = -hx + POST * <span class="n">2.0</span> + (i + <span class="n">1</span>) * (WIDTH - <span class="n">4.0</span> * POST) / <span class="n">4.0</span>
518+
wood_verts.extend(
519+
add_box(
520+
bm,
521+
(rx, <span class="n">0.0</span>, (FRONT_H + BACK_H) / <span class="n">2.0</span> - POST * <span class="n">0.42</span>),
522+
(POST * <span class="n">0.42</span>, slant_len * <span class="n">0.96</span>, POST * <span class="n">0.42</span>),
523+
WOOD_IDX,
524+
euler=(pitch, <span class="n">0.0</span>, <span class="n">0.0</span>),
525+
)
526+
)
491527

492528
<span class="c"># Mid rail on the back wall and two diagonal braces at the sides.</span>
493529
wood_verts.extend(
@@ -515,33 +551,44 @@ <h2>Source</h2>
515551
)
516552
)
517553

518-
<span class="c"># Counter top, apron, and four legs.</span>
554+
<span class="c"># Slatted counter — crate-lid language, not one fat slab.</span>
519555
cy = -hy + COUNTER_D / <span class="n">2.0</span> + <span class="n">0.04</span>
520-
wood_verts.extend(
521-
add_box(
522-
bm,
523-
(<span class="n">0.0</span>, cy, COUNTER_Z),
524-
(WIDTH - <span class="n">0.04</span>, COUNTER_D, COUNTER_T),
525-
WOOD_IDX,
556+
n_slats = <span class="n">6</span>
557+
slat_gap = <span class="n">0.010</span>
558+
slat_d = (COUNTER_D - <span class="n">0.02</span> - (n_slats - <span class="n">1</span>) * slat_gap) / n_slats
559+
y0 = cy - COUNTER_D / <span class="n">2.0</span> + <span class="n">0.01</span> + slat_d / <span class="n">2.0</span>
560+
<span class="k">for</span> i <span class="k">in</span> range(n_slats):
561+
wood_verts.extend(
562+
add_box(
563+
bm,
564+
(<span class="n">0.0</span>, y0 + i * (slat_d + slat_gap), COUNTER_Z),
565+
(WIDTH - <span class="n">0.06</span>, slat_d, COUNTER_T * <span class="n">0.72</span>),
566+
WOOD_IDX,
567+
)
526568
)
527-
)
528569
wood_verts.extend(
529570
add_box(
530571
bm,
531-
(<span class="n">0.0</span>, cy - COUNTER_D / <span class="n">2.0</span> + COUNTER_T / <span class="n">2.0</span>, COUNTER_Z - <span class="n">0.09</span>),
532-
(WIDTH - <span class="n">0.08</span>, COUNTER_T * <span class="n">0.7</span>, <span class="n">0.16</span>),
572+
(<span class="n">0.0</span>, cy - COUNTER_D / <span class="n">2.0</span> + <span class="n">0.018</span>, COUNTER_Z - <span class="n">0.09</span>),
573+
(WIDTH - <span class="n">0.08</span>, <span class="n">0.036</span>, <span class="n">0.16</span>),
533574
WOOD_IDX,
534575
)
535576
)
536577
under_shelf_z = <span class="n">0.38</span>
537-
wood_verts.extend(
538-
add_box(
539-
bm,
540-
(<span class="n">0.0</span>, cy, under_shelf_z),
541-
(WIDTH - <span class="n">0.16</span>, COUNTER_D * <span class="n">0.78</span>, <span class="n">0.028</span>),
542-
WOOD_IDX,
578+
n_shelf = <span class="n">4</span>
579+
shelf_d = COUNTER_D * <span class="n">0.78</span>
580+
shelf_gap = <span class="n">0.010</span>
581+
shelf_slat = (shelf_d - (n_shelf - <span class="n">1</span>) * shelf_gap) / n_shelf
582+
sy0 = cy - shelf_d / <span class="n">2.0</span> + shelf_slat / <span class="n">2.0</span>
583+
<span class="k">for</span> i <span class="k">in</span> range(n_shelf):
584+
wood_verts.extend(
585+
add_box(
586+
bm,
587+
(<span class="n">0.0</span>, sy0 + i * (shelf_slat + shelf_gap), under_shelf_z),
588+
(WIDTH - <span class="n">0.16</span>, shelf_slat, <span class="n">0.024</span>),
589+
WOOD_IDX,
590+
)
543591
)
544-
)
545592
<span class="k">for</span> lx <span class="k">in</span> (-hx + <span class="n">0.16</span>, hx - <span class="n">0.16</span>):
546593
<span class="k">for</span> ly <span class="k">in</span> (cy - COUNTER_D * <span class="n">0.32</span>, cy + COUNTER_D * <span class="n">0.32</span>):
547594
wood_verts.extend(
@@ -553,12 +600,24 @@ <h2>Source</h2>
553600
)
554601
)
555602

556-
<span class="c"># Backboard behind the counter.</span>
603+
<span class="c"># Back wall: horizontal planks with gaps, not a felt slab.</span>
604+
<span class="k">for</span> i <span class="k">in</span> range(<span class="n">5</span>):
605+
z = <span class="n">0.36</span> + i * <span class="n">0.155</span>
606+
wood_verts.extend(
607+
add_box(
608+
bm,
609+
(<span class="n">0.0</span>, hy - inset - <span class="n">0.014</span>, z),
610+
(WIDTH - <span class="n">2.35</span> * POST, <span class="n">0.028</span>, <span class="n">0.122</span>),
611+
WOOD_IDX,
612+
)
613+
)
614+
615+
<span class="c"># Front eave fascia under the valance.</span>
557616
wood_verts.extend(
558617
add_box(
559618
bm,
560-
(<span class="n">0.0</span>, hy - inset - <span class="n">0.012</span>, <span class="n">0.95</span>),
561-
(WIDTH - <span class="n">2.2</span> * POST, <span class="n">0.022</span>, <span class="n">0.55</span>),
619+
(<span class="n">0.0</span>, -hy - <span class="n">0.008</span>, FRONT_H - <span class="n">0.028</span>),
620+
(WIDTH - POST * <span class="n">0.4</span>, <span class="n">0.032</span>, <span class="n">0.048</span>),
562621
WOOD_IDX,
563622
)
564623
)
@@ -947,8 +1006,8 @@ <h2>Source</h2>
9471006
ob.hide_render = <span class="k">True</span>
9481007
ob.hide_viewport = <span class="k">True</span>
9491008

950-
low.rotation_euler.z = math.radians(-<span class="n">26.0</span>)
951-
low.rotation_euler.x = math.radians(<span class="n">3.0</span>)
1009+
low.rotation_euler.z = math.radians(-<span class="n">18.0</span>)
1010+
low.rotation_euler.x = math.radians(<span class="n">2.0</span>)
9521011

9531012
floor_me = bpy.data.meshes.new(<span class="s">&quot;Floor&quot;</span>)
9541013
bm = bmesh.new()
@@ -994,10 +1053,10 @@ <h2>Source</h2>
9941053
cam_data = bpy.data.cameras.new(<span class="s">&quot;Cam&quot;</span>)
9951054
cam_data.lens = <span class="n">50.0</span>
9961055
cam = bpy.data.objects.new(<span class="s">&quot;Cam&quot;</span>, cam_data)
997-
cam.location = (<span class="n">3.51</span>, -<span class="n">5.00</span>, <span class="n">2.42</span>)
1056+
cam.location = (<span class="n">3.11</span>, -<span class="n">5.10</span>, <span class="n">1.60</span>)
9981057
scene.collection.objects.link(cam)
9991058
aim = bpy.data.objects.new(<span class="s">&quot;Aim&quot;</span>, <span class="k">None</span>)
1000-
aim.location = (<span class="n">0.0</span>, <span class="n">0.0</span>, <span class="n">0.88</span>)
1059+
aim.location = (<span class="n">0.0</span>, -<span class="n">0.10</span>, <span class="n">0.92</span>)
10011060
scene.collection.objects.link(aim)
10021061
con = cam.constraints.new(<span class="s">&quot;TRACK_TO&quot;</span>)
10031062
con.target = aim

0 commit comments

Comments
 (0)