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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ Budget-conformance props. Not examples. Conventions: [`showcase/README.md`](show
<a href="showcase/grindstone/"><img src="showcase/grindstone/preview.webp" width="24%" alt="Grindstone: a stone wheel on a timber A-frame with an iron axle, hubs, crank, and tray on a dark studio floor" /></a>
<a href="showcase/hand-pump/"><img src="showcase/hand-pump/preview.webp" width="24%" alt="Hand pump: a cast-iron village pump on a wooden plinth with a spout and handle on a dark studio floor" /></a>
<a href="showcase/signpost/"><img src="showcase/signpost/preview.webp" width="24%" alt="Signpost: a timber post with two painted fingerboards, iron straps, and a pyramidal cap on a dark studio floor" /></a>
<a href="showcase/chopping-block/"><img src="showcase/chopping-block/preview.webp" width="24%" alt="Chopping block: a hooped timber stump with an embedded axe on a dark studio floor" /></a>
<a href="showcase/chopping-block/"><img src="showcase/chopping-block/preview.webp" width="24%" alt="Chopping block: a bark-sided log round with growth rings and drying checks on its sawn top, a riveted iron hoop, and a felling axe buried in the top, on a dark studio floor" /></a>
<a href="showcase/wooden-bucket/"><img src="showcase/wooden-bucket/preview.webp" width="24%" alt="Wooden bucket: a coopered pail with a round rope bail through ear rings and chord-lofted iron hoops on a dark studio floor" /></a>
<a href="showcase/wall-torch/"><img src="showcase/wall-torch/preview.webp" width="24%" alt="Wall torch: a dressed stone plaque with an iron sconce, wooden haft, and flame on a dark studio floor" /></a>
<a href="showcase/tavern-stool/"><img src="showcase/tavern-stool/preview.webp" width="24%" alt="Tavern stool: a round wooden seat on turned splayed legs with stretchers and iron ferrules on a dark studio floor" /></a>
Expand Down Expand Up @@ -592,7 +592,7 @@ passthrough.
</tr>
<tr>
<td width="46%" valign="middle">
<a href="examples/gn-socket-rename/"><img src="examples/gn-socket-rename/preview.webp" alt="GN socket rename: a copper jo-block column on a steel plinth in a dark studio, the column switched in by Compare and tagged by Random Value, warm wedge on the back wall" /></a>
<a href="examples/gn-socket-rename/"><img src="examples/gn-socket-rename/preview.webp" alt="GN socket rename: a brushed copper gauge column on a steel plinth in a dark studio, graduated every 0.1 m from its stored gauge_h attribute, warm wedge on the back wall" /></a>
</td>
<td valign="middle">

Expand Down
Binary file modified docs/gallery/assets/chopping-block-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/gn-socket-rename-hero.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 19 additions & 6 deletions docs/gallery/cart/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,8 @@ <h1>cart</h1>
<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 | 2760–2960 | 2856 / 2856 / 2856 | | LOD1 ratio | 0.32–0.62 of base | 0.5000 / 0.5000 / 0.5000 | | LOD2 ratio | 0.10–0.35 of base | 0.2200 / 0.2200 / 0.2154 | | Materials | exactly 2 distinct, metal ≥ 24, wood ≥ 800 faces | 2 slots, floors met | | UVs | in <code>0..1</code>, AABB overlap ≤ 1e-5 | in range, overlap 0 | | Outer AABB | (1.539, 0.749, 0.640) m ± 0.01 | (1.5393, 0.7486, 0.6400) | | Grounded | bbox min Z within 1e-4 of 0 | 0.0000 / 0.0000 / 0.0000 | | Named supports | each tyre&#x27;s own zmin within 1e-4 of the floor | +Y 0.000000, −Y 0.000000 | | Hygiene | loose V/E, non-manifold, zero-area, doubles @1e-5, n-gons: all 0 | 0 / 0 / 0 on every axis | | Z-fighting | coplanar cross-shell face pairs = 0 | 0 / 0 / 0 | | Tyre seat | 0.0030–0.0055 m interference, per angular station | [0.00400, 0.00400] over 48 stations | | Wheel mirror | the two wheels match in X, Z and extent within 5e-5 m | 0.00000 mm | | Material-island gap | metal↔wood min distance ≤ 0.008 m | 0.00000 / 0.00000 / 0.00000 | | Collider tris | ≤ 360 | 122 | | Export | written, size &gt; 0 | 201048 / 201048 / 201032 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 8 B on 5.2.1 (glTF serializer), not a gated axis.</p>
<p>| Axis | Declared | Measured (4.5.11 / 5.1.2 / 5.2.1) | | --- | --- | --- | | Base triangles | 2760–2960 | 2856 / 2856 / 2856 | | LOD1 ratio | 0.32–0.62 of base | 0.5000 / 0.5000 / 0.5000 | | LOD2 ratio | 0.10–0.35 of base | 0.2199 / 0.2199 / 0.2157 | | Materials | exactly 2 distinct, metal ≥ 24, wood ≥ 800 faces | 2 slots, floors met | | UVs | in <code>0..1</code>, AABB overlap ≤ 1e-5 | in range, overlap 0 | | Outer AABB | (1.539, 0.749, 0.640) m ± 0.01 | (1.5393, 0.7486, 0.6400) | | Grounded | bbox min Z within 1e-4 of 0 | 0.0000 / 0.0000 / 0.0000 | | Named supports | each tyre&#x27;s own zmin within 1e-4 of the floor | +Y 0.000000, −Y 0.000000 | | Hygiene | loose V/E, non-manifold, zero-area, doubles @1e-5, n-gons: all 0 | 0 / 0 / 0 on every axis | | Z-fighting | coplanar cross-shell face pairs = 0 | 0 / 0 / 0 | | Tyre seat | 0.0030–0.0055 m interference, per angular station | [0.00400, 0.00400] over 48 stations | | Wheel mirror | the two wheels match in X, Z and extent within 5e-5 m | 0.00000 mm | | Material-island gap | metal↔wood min distance ≤ 0.008 m | 0.00000 / 0.00000 / 0.00000 | | Collider tris | ≤ 360 | 154 | | Export | written, size &gt; 0, removed after measuring | 221960 bytes on 5.2.1 |</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 a few bytes across series (glTF serializer); the gate is &quot;written and non-empty&quot;, not a byte count, and the file is removed once measured.</p>
<h2>Falsifiers</h2>
<p>Each flag breaks one stage so a <strong>named</strong> budget fails and the piece exits *that* code. Magnitudes are sized so no earlier gate can steal the failure: the two wheel moves stay inside <code>BBOX_TOL</code>, so the AABB gate (exit 8) still passes.</p>
<p>| Flag | Target budget | Exit | | --- | --- | --- | | <code>--skip-decimate</code> | LOD1 ratio band (ratio becomes 1.0) | 9 | | <code>--lift-z</code> | AABB grounded zmin (whole mesh up 0.05 m) | 16 | | <code>--flush-tyre</code> | Coplanar cross-shell pairs — sets the tyre&#x27;s inner radius equal to the felloe&#x27;s outer radius, which is the construction bug this piece was rebuilt to remove (0 → 48 pairs) | 15 | | <code>--sink-tyre</code> | Tyre seat band — buries the hoop 9 mm into the felloe so it reads as one body | 18 | | <code>--float-wheel</code> | Named supports — lifts one wheel 3 mm while the other still grounds the AABB | 16 | | <code>--skew-wheel</code> | Wheel mirror — pushes one wheel 6 mm out along the track | 19 |</p>
Expand Down Expand Up @@ -299,17 +299,21 @@ <h2>Source</h2>
export.

Budgets are declared below and recomputed from the generated result.
They are not API-contract witnesses. ``--skip-decimate`` skips the LOD
DECIMATE stage so the LOD-ratio budget fails. ``--lift-z`` raises the
mesh so the grounded-zmin hygiene budget fails.
They are not API-contract witnesses. Each falsifier flag breaks one
stage so a named budget fails and the piece exits that budget&#x27;s code:
``--skip-decimate`` (LOD ratio, 9), ``--lift-z`` (AABB grounded, 16),
``--float-wheel`` (named supports, 16), ``--flush-tyre`` (coplanar
cross-shell pairs, 15), ``--sink-tyre`` (tyre seat band, 18),
``--skew-wheel`` (wheel mirror, 19). The two wheel moves stay inside
``BBOX_TOL`` so the AABB gate cannot steal the failure.

No RNG. Construction is closed-form. DECIMATE COLLAPSE triangle counts
are not byte-identical across Blender versions — the LOD gate is a
ratio band, not an exact count.

blender --background --python cart.py --
blender --background --python cart.py -- --skip-decimate
blender --background --python cart.py -- --lift-z
blender --background --python cart.py -- --flush-tyre
blender --background --python cart.py -- --output cart.png
&quot;&quot;&quot;</span>
<span class="k">import</span> argparse
Expand Down Expand Up @@ -1295,6 +1299,15 @@ <h2>Source</h2>
os.remove(export_path)
export_unity(export_path, [low, collider])
export_size = os.path.getsize(export_path) <span class="k">if</span> os.path.isfile(export_path) <span class="k">else</span> <span class="n">0</span>
<span class="c"># Blender sets TMPDIR from its own preference, which resolves to the</span>
<span class="c"># working directory on a stock portable build — so gettempdir() is the</span>
<span class="c"># repo root under CI and every run left a .glb behind. The budget only</span>
<span class="c"># needs the byte count, so drop the file once it is measured.</span>
<span class="k">if</span> os.path.isfile(export_path):
<span class="k">try</span>:
os.remove(export_path)
<span class="k">except</span> OSError:
<span class="k">pass</span>

print(<span class="s">f&quot;</span><span class="s">blender=</span>{tuple(bpy.app.version)}<span class="s"> skip_decimate=</span>{skip_decimate}<span class="s">&quot;</span>)
print(
Expand Down
Loading
Loading