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
126 changes: 82 additions & 44 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/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.
Binary file modified docs/gallery/assets/signpost-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/water-trough-hero.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
195 changes: 135 additions & 60 deletions docs/gallery/hand-pump/index.html

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions docs/gallery/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1035,18 +1035,18 @@ <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: 1052 tris, two materials with 102 metal faces, UVs in 0..1 with zero AABB overlap, outer AABB 0.750×0.406×0.690 m, LOD ratios in band (5.2 COLLAPSE more aggressive on LOD2), convex collider 134 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</p>
<p class="witnesses"><span class="tag">witnesses</span> Recomputed: 1696 tris, two materials with 274 metal faces, UVs in 0..1 with zero AABB overlap, outer AABB 0.746×0.410×0.682 m, LOD ratios in band (5.2 COLLAPSE more aggressive on LOD2), convex collider 162 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</p>
<a class="card-link" href="anvil/">View example <span aria-hidden="true">&rarr;</span></a>
</div>
</article>
<article class="card" data-tags="mesh export showcase">
<a class="card-media" href="water-trough/" aria-label="water-trough example detail page">
<img src="assets/water-trough-hero.webp" alt="water-trough — A procedural staved water trough through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets rather than an API contract." loading="lazy" decoding="async" />
<img src="assets/water-trough-hero.webp" alt="water-trough — A procedural staved water trough with water through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets rather than an API contract." loading="lazy" decoding="async" />
</a>
<div class="card-body">
<h2><a href="water-trough/">water-trough</a></h2>
<p class="teaches">A procedural staved water trough 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: 4008 tris, two materials with 168 metal faces, UVs in 0..1 with zero AABB overlap, outer AABB 1.136×0.482×0.470 m, LOD ratios in band, convex collider 108 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</p>
<p class="teaches">A procedural staved water trough with water 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: 4776 tris, three materials with 168 metal and 60 water faces, UVs in 0..1 with zero AABB overlap, outer AABB 1.162×0.528×0.488 m, LOD ratios in band, convex collider 128 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</p>
<a class="card-link" href="water-trough/">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 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 with a gooseneck through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets rather than an API contract." 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 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: 616 tris, two materials with 96 metal faces, UVs in 0..1 with zero AABB overlap, outer AABB 0.567×0.366×1.027 m, LOD ratios in band (5.2 COLLAPSE more aggressive on LOD2), convex collider 118 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</p>
<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>
<a class="card-link" href="hand-pump/">View example <span aria-hidden="true">&rarr;</span></a>
</div>
</article>
Expand All @@ -1090,7 +1090,7 @@ <h2><a href="hand-pump/">hand-pump</a></h2>
<div class="card-body">
<h2><a href="signpost/">signpost</a></h2>
<p class="teaches">A procedural timber fingerboard signpost 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: 388 tris, three materials with 66 metal and 22 board faces, UVs in 0..1 with zero AABB overlap, outer AABB 1.162×0.147×1.575 m, LOD ratios in band (5.2 COLLAPSE more aggressive on LOD2), convex collider 92 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</p>
<p class="witnesses"><span class="tag">witnesses</span> Recomputed: 504 tris, three materials with 66 metal and 24 board faces, UVs in 0..1 with zero AABB overlap, outer AABB 1.368×0.201×1.566 m, LOD ratios in band (5.2 COLLAPSE more aggressive on LOD2), convex collider 56 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</p>
<a class="card-link" href="signpost/">View example <span aria-hidden="true">&rarr;</span></a>
</div>
</article>
Expand Down
85 changes: 48 additions & 37 deletions docs/gallery/signpost/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -253,18 +253,18 @@ <h1>signpost</h1>
<img src="../assets/signpost-hero.webp" alt="signpost render" width="1280" height="720" />
</button>
<p class="zoom-hint">Rendered headless by the example itself — click to zoom.</p>
<div class="callout"><span class="tag">witnesses</span> Recomputed: 388 tris, three materials with 66 metal and 22 board faces, UVs in 0..1 with zero AABB overlap, outer AABB 1.162×0.147×1.575 m, LOD ratios in band (5.2 COLLAPSE more aggressive on LOD2), convex collider 92 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</div>
<div class="callout"><span class="tag">witnesses</span> Recomputed: 504 tris, three materials with 66 metal and 24 board faces, UVs in 0..1 with zero AABB overlap, outer AABB 1.368×0.201×1.566 m, LOD ratios in band (5.2 COLLAPSE more aggressive on LOD2), convex collider 56 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</div>
<div class="runline">
<pre id="runCmd">blender --background --python showcase/signpost/signpost.py --</pre>
<button class="copy-btn" id="copyRun" type="button">Copy</button>
</div>
<section class="detail-section md">
<p>A showcase piece, not an example. Procedural timber fingerboard signpost (square post, pyramidal cap, two painted boards, iron shoe and straps) then the shipped pipeline: unique-cell UVs, Cycles high-to-low normal bake, LOD chain, convex collider, Unity glTF export.</p>
<p>A showcase piece, not an example. Procedural timber fingerboard signpost (square post, collar and pyramidal cap, two painted planks with diamond tips, iron shoe and straps) then the shipped pipeline: unique-cell UVs, Cycles high-to-low normal bake, LOD chain, convex collider, Unity glTF export.</p>
<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>
<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>
<h2>Budgets</h2>
<p>Declared as named constants; every gate <strong>recomputes</strong> from the mesh, materials, UVs, evaluated LOD, collider, or export file.</p>
<p>| Axis | Declared | Measured (4.5.11 / 5.1.2 / 5.2.1) | | --- | --- | --- | | Base triangles | 320–460 | 388 / 388 / 388 | | LOD1 ratio | 0.32–0.62 of base | 0.5000 / 0.5000 / 0.5000 | | LOD2 ratio | 0.10–0.35 of base | 0.2165 / 0.2165 / 0.1804 | | Materials | exactly 3 distinct, ≥24 metal and ≥12 board faces | 3 slots, 66 metal, 22 board | | UVs | in <code>0..1</code>, AABB overlap ≤ 1e-5 | in range, overlap 0 | | Outer AABB | (1.162, 0.147, 1.575) m ± 0.01 | (1.1620, 0.1474, 1.5750), zmin 0 | | Collider tris | ≤ 140 | 92 | | Export | written, size &gt; 0 | 40124 / 40124 / 40112 bytes |</p>
<p>| Axis | Declared | Measured (4.5.11 / 5.1.2 / 5.2.1) | | --- | --- | --- | | Base triangles | 430–580 | 504 / 504 / 504 | | LOD1 ratio | 0.32–0.62 of base | 0.5000 / 0.5000 / 0.5000 | | LOD2 ratio | 0.10–0.35 of base | 0.2183 / 0.2183 / 0.1746 | | Materials | exactly 3 distinct, ≥24 metal and ≥12 board faces | 3 slots, 66 metal, 24 board | | UVs | in <code>0..1</code>, AABB overlap ≤ 1e-5 | in range, overlap 0 | | Outer AABB | (1.368, 0.201, 1.566) m ± 0.01 | (1.3679, 0.2014, 1.5660), zmin 0 | | Collider tris | ≤ 140 | 56 | | Export | written, size &gt; 0 | 45164 / 45164 / 45152 bytes |</p>
<p>DECIMATE COLLAPSE triangle counts are <strong>not</strong> 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 <code>has_data</code> plus operator <code>FINISHED</code>, not byte-identity. Construction uses no RNG. Export byte counts differ by 12 B on 5.2.1 (glTF serializer), not a gated axis.</p>
<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>
<h2>Run</h2>
Expand Down Expand Up @@ -319,20 +319,20 @@ <h2>Source</h2>
sys.dont_write_bytecode = <span class="k">True</span>
<span class="k">import</span> gallery_framing <span class="c"># noqa: E402</span>

POST_W = <span class="n">0.110</span>
POST_W = <span class="n">0.118</span>
POST_H = <span class="n">1.48</span>
CAP_H = <span class="n">0.095</span>
CAP_H = <span class="n">0.058</span>
SHOE_H = <span class="n">0.048</span>
SHOE_SCALE = <span class="n">1.34</span>
BOARD_Y = <span class="n">0.072</span>
BOARD_ZTH = <span class="n">0.046</span>
BOARD_A = (<span class="n">1.24</span>, <span class="n">0.52</span>, <span class="n">1.0</span>)
BOARD_B = (<span class="n">1.02</span>, <span class="n">0.44</span>, -<span class="n">1.0</span>)
BOARD_T = <span class="n">0.028</span>
BOARD_H = <span class="n">0.112</span>
BOARD_A = (<span class="n">1.24</span>, <span class="n">0.62</span>, <span class="n">1.0</span>, <span class="n">0.12</span>)
BOARD_B = (<span class="n">1.00</span>, <span class="n">0.54</span>, -<span class="n">1.0</span>, -<span class="n">0.18</span>)

BBOX_TOL = <span class="n">0.01</span>
OUTER_SIZE = (<span class="n">1.162</span>, <span class="n">0.147</span>, <span class="n">1.575</span>)
BASE_TRIS_MIN = <span class="n">320</span>
BASE_TRIS_MAX = <span class="n">460</span>
OUTER_SIZE = (<span class="n">1.368</span>, <span class="n">0.201</span>, <span class="n">1.566</span>)
BASE_TRIS_MIN = <span class="n">430</span>
BASE_TRIS_MAX = <span class="n">580</span>
LOD1_RATIO_MIN = <span class="n">0.32</span>
LOD1_RATIO_MAX = <span class="n">0.62</span>
LOD2_RATIO_MIN = <span class="n">0.10</span>
Expand Down Expand Up @@ -559,12 +559,20 @@ <h2>Source</h2>
WOOD_IDX,
)
)
wood.extend(
add_box(
bm,
(<span class="n">0.0</span>, <span class="n">0.0</span>, POST_H + <span class="n">0.014</span>),
(POST_W * <span class="n">1.16</span>, POST_W * <span class="n">1.16</span>, <span class="n">0.028</span>),
WOOD_IDX,
)
)
wood.extend(
add_cone(
bm,
(<span class="n">0.0</span>, <span class="n">0.0</span>, POST_H + CAP_H / <span class="n">2.0</span>),
POST_W * <span class="n">0.78</span>,
<span class="n">0.012</span>,
(<span class="n">0.0</span>, <span class="n">0.0</span>, POST_H + <span class="n">0.028</span> + CAP_H / <span class="n">2.0</span>),
POST_W * <span class="n">0.72</span>,
<span class="n">0.008</span>,
CAP_H,
<span class="n">4</span>,
WOOD_IDX,
Expand All @@ -586,50 +594,53 @@ <h2>Source</h2>
<span class="k">for</span> f <span class="k">in</span> ret.get(<span class="s">&quot;faces&quot;</span>) <span class="k">or</span> []:
f.material_index = WOOD_IDX

<span class="k">for</span> z, length, sign <span class="k">in</span> (BOARD_A, BOARD_B):
cx = sign * (half + length / <span class="n">2.0</span> - <span class="n">0.018</span>)
<span class="k">for</span> z, length, sign, yaw <span class="k">in</span> (BOARD_A, BOARD_B):
eul = Euler((<span class="n">0.0</span>, <span class="n">0.0</span>, yaw))
rot = eul.to_matrix()
inner = Vector((sign * (half - <span class="n">0.016</span>), <span class="n">0.0</span>, z))
center = inner + rot @ Vector((sign * length / <span class="n">2.0</span>, <span class="n">0.0</span>, <span class="n">0.0</span>))
boards.extend(
add_box(
bm,
(cx, <span class="n">0.0</span>, z),
(length, BOARD_Y, BOARD_ZTH),
(center.x, center.y, center.z),
(length, BOARD_T, BOARD_H),
BOARD_IDX,
euler=(<span class="n">0.0</span>, <span class="n">0.0</span>, yaw),
)
)
tip_x = cx + sign * (length / <span class="n">2.0</span> + <span class="n">0.028</span>)
tip = inner + rot @ Vector((sign * (length + <span class="n">0.004</span>), <span class="n">0.0</span>, <span class="n">0.0</span>))
boards.extend(
add_cone(
add_box(
bm,
(tip_x, <span class="n">0.0</span>, z),
<span class="n">0.052</span>,
<span class="n">0.004</span>,
<span class="n">0.072</span>,
<span class="n">3</span>,
(tip.x, tip.y, tip.z),
(<span class="n">0.088</span>, BOARD_T, <span class="n">0.088</span>),
BOARD_IDX,
euler=(<span class="n">0.0</span>, sign * math.pi / <span class="n">2.0</span>, <span class="n">0.0</span>),
euler=(<span class="n">0.0</span>, math.pi / <span class="n">4.0</span>, yaw),
)
)
t = <span class="n">0.012</span>
h = BOARD_H + <span class="n">0.024</span>
metal.extend(
add_box(
bm,
(sign * (half + <span class="n">0.010</span>), <span class="n">0.0</span>, z),
(<span class="n">0.020</span>, BOARD_Y + <span class="n">0.016</span>, BOARD_ZTH + <span class="n">0.018</span>),
(sign * (half + t / <span class="n">2.0</span>), <span class="n">0.0</span>, z),
(t, POST_W * <span class="n">0.94</span>, h),
METAL_IDX,
)
)
metal.extend(
add_box(
bm,
(<span class="n">0.0</span>, half + <span class="n">0.010</span>, z),
(POST_W * <span class="n">0.92</span>, <span class="n">0.014</span>, BOARD_ZTH * <span class="n">1.15</span>),
(<span class="n">0.0</span>, half + t / <span class="n">2.0</span>, z),
(POST_W * <span class="n">0.94</span>, t, h),
METAL_IDX,
)
)
metal.extend(
add_box(
bm,
(<span class="n">0.0</span>, -(half + <span class="n">0.010</span>), z),
(POST_W * <span class="n">0.92</span>, <span class="n">0.014</span>, BOARD_ZTH * <span class="n">1.15</span>),
(<span class="n">0.0</span>, -(half + t / <span class="n">2.0</span>), z),
(POST_W * <span class="n">0.94</span>, t, h),
METAL_IDX,
)
)
Expand Down Expand Up @@ -817,7 +828,7 @@ <h2>Source</h2>
low = build_signpost_mesh(<span class="s">&quot;SignpostLow&quot;</span>, bevel_offset=<span class="n">0.004</span>, bevel_segments=<span class="n">2</span>)
high = build_signpost_mesh(<span class="s">&quot;SignpostHigh&quot;</span>, bevel_offset=<span class="n">0.004</span>, bevel_segments=<span class="n">4</span>)
wood = principled(<span class="s">&quot;SignpostWood&quot;</span>, (<span class="n">0.34</span>, <span class="n">0.18</span>, <span class="n">0.07</span>, <span class="n">1.0</span>), <span class="n">0.0</span>, <span class="n">0.62</span>)
board = principled(<span class="s">&quot;SignpostBoard&quot;</span>, (<span class="n">0.72</span>, <span class="n">0.58</span>, <span class="n">0.28</span>, <span class="n">1.0</span>), <span class="n">0.0</span>, <span class="n">0.55</span>)
board = principled(<span class="s">&quot;SignpostBoard&quot;</span>, (<span class="n">0.76</span>, <span class="n">0.58</span>, <span class="n">0.22</span>, <span class="n">1.0</span>), <span class="n">0.0</span>, <span class="n">0.52</span>)
metal = principled(<span class="s">&quot;SignpostIron&quot;</span>, (<span class="n">0.14</span>, <span class="n">0.145</span>, <span class="n">0.155</span>, <span class="n">1.0</span>), <span class="n">1.0</span>, <span class="n">0.38</span>)
assign_slots(low, wood, board, metal)
assign_slots(high, wood, board, metal)
Expand Down Expand Up @@ -974,7 +985,7 @@ <h2>Source</h2>
ob.hide_render = <span class="k">True</span>
ob.hide_viewport = <span class="k">True</span>

low.rotation_euler.z = math.radians(-<span class="n">18.0</span>)
low.rotation_euler.z = math.radians(-<span class="n">12.0</span>)
low.rotation_euler.x = math.radians(<span class="n">0.0</span>)

floor_me = bpy.data.meshes.new(<span class="s">&quot;Floor&quot;</span>)
Expand Down Expand Up @@ -1021,10 +1032,10 @@ <h2>Source</h2>
cam_data = bpy.data.cameras.new(<span class="s">&quot;Cam&quot;</span>)
cam_data.lens = <span class="n">50.0</span>
cam = bpy.data.objects.new(<span class="s">&quot;Cam&quot;</span>, cam_data)
cam.location = (<span class="n">3.06</span>, -<span class="n">4.02</span>, <span class="n">1.38</span>)
cam.location = (<span class="n">2.80</span>, -<span class="n">3.40</span>, <span class="n">1.35</span>)
scene.collection.objects.link(cam)
aim = bpy.data.objects.new(<span class="s">&quot;Aim&quot;</span>, <span class="k">None</span>)
aim.location = (<span class="n">0.0</span>, <span class="n">0.0</span>, <span class="n">0.78</span>)
aim.location = (<span class="n">0.0</span>, <span class="n">0.0</span>, <span class="n">0.82</span>)
scene.collection.objects.link(aim)
con = cam.constraints.new(<span class="s">&quot;TRACK_TO&quot;</span>)
con.target = aim
Expand Down
Loading
Loading