Skip to content

Commit 02ec708

Browse files
TMHSDigitalclaude
andcommitted
fix: usd-export-evaluation-mode presentation, ingots rest on the floor
Render path only: both subdivided balls hovered 0.16 m above the floor; each now rests on z = 0 from its evaluated vertices. Brighter key. Contract and exit codes unchanged on 4.5.11, 5.1.2 and 5.2.1. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
1 parent d18608a commit 02ec708

8 files changed

Lines changed: 45 additions & 14 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ three. `--assume-distinct-names` is red only on 5.2.
450450
</tr>
451451
<tr>
452452
<td width="46%" valign="middle">
453-
<a href="examples/usd-export-evaluation-mode/"><img src="examples/usd-export-evaluation-mode/preview.webp" alt="USD export evaluation_mode: two steel SUBSURF cubes on a dark studio floor — left faceted viewport tessellation, right smoother render tessellation, warm wedge on the back wall" /></a>
453+
<a href="examples/usd-export-evaluation-mode/"><img src="examples/usd-export-evaluation-mode/preview.webp" alt="USD export evaluation_mode: two steel SUBSURF cubes resting on a dark studio floor — left faceted viewport tessellation, right smoother render tessellation, warm wedge on the back wall" /></a>
454454
</td>
455455
<td valign="middle">
456456

4.28 KB
Loading
-26.1 KB
Loading

‎docs/gallery/usd-export-evaluation-mode/index.html‎

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616
<meta property="og:image" content="https://tmhsdigital.github.io/Blender-Developer-Tools/gallery/assets/usd-export-evaluation-mode-hero.webp" />
1717
<meta property="og:image:width" content="1280" />
1818
<meta property="og:image:height" content="720" />
19-
<meta property="og:image:alt" content="Two grey spheres: a coarse, flat-shaded one on the left and a smooth, subdivided one on the right." />
19+
<meta property="og:image:alt" content="Two steel balls resting on the floor: a coarse, flat-shaded one on the left and a smooth, subdivided one on the right." />
2020
<meta property="og:site_name" content="Blender Developer Tools" />
2121
<meta name="twitter:card" content="summary_large_image" />
2222
<meta name="twitter:title" content="usd-export-evaluation-mode — Examples — Blender Developer Tools" />
2323
<meta name="twitter:description" content="The USD exporter evaluation_mode chooses viewport versus render modifier quality. TESSELLATE makes the split observable; BEST_MATCH writes the cage and the mode is silent." />
2424
<meta name="twitter:image" content="https://tmhsdigital.github.io/Blender-Developer-Tools/gallery/assets/usd-export-evaluation-mode-hero.webp" />
25-
<meta name="twitter:image:alt" content="Two grey spheres: a coarse, flat-shaded one on the left and a smooth, subdivided one on the right." />
25+
<meta name="twitter:image:alt" content="Two steel balls resting on the floor: a coarse, flat-shaded one on the left and a smooth, subdivided one on the right." />
2626
<style>
2727
/* fonts are deployed by the landing build (docs/fonts/) */
2828
@font-face { font-family: 'Barlow Condensed'; font-weight: 600; font-display: swap;
@@ -268,8 +268,8 @@ <h1>usd-export-evaluation-mode</h1>
268268
<p>The USD exporter evaluation_mode chooses viewport versus render modifier quality. TESSELLATE makes the split observable; BEST_MATCH writes the cage and the mode is silent.</p>
269269
</header>
270270
<main id="main">
271-
<button class="detail-hero" id="heroZoom" type="button" aria-label="View full size: Two grey spheres: a coarse, flat-shaded one on the left and a smooth, subdivided one on the right.">
272-
<img src="../assets/usd-export-evaluation-mode-hero.webp" alt="Two grey spheres: a coarse, flat-shaded one on the left and a smooth, subdivided one on the right." width="1280" height="720" />
271+
<button class="detail-hero" id="heroZoom" type="button" aria-label="View full size: Two steel balls resting on the floor: a coarse, flat-shaded one on the left and a smooth, subdivided one on the right.">
272+
<img src="../assets/usd-export-evaluation-mode-hero.webp" alt="Two steel balls resting on the floor: a coarse, flat-shaded one on the left and a smooth, subdivided one on the right." width="1280" height="720" />
273273
</button>
274274
<p class="zoom-hint">Rendered headless by the example itself — click to zoom.</p>
275275
<div class="callout"><span class="tag">witnesses</span> TESSELLATE+VIEWPORT writes 26/24; TESSELLATE+RENDER writes 98/96 subdivisionScheme=none; BEST_MATCH writes the 8-vert catmullClark cage. --evaluation-mode VIEWPORT and --subdivision BEST_MATCH exit 4.</div>
@@ -283,7 +283,7 @@ <h1>usd-export-evaluation-mode</h1>
283283
<p>The still is numeric-adjacent: left is viewport tessellation (flat-shaded L1), right is render tessellation (smoothed L2). The USDA point counts are the evidence; the two balls depict the two qualities the exporter chooses between.</p>
284284
<p><strong>What failure each check would catch:</strong></p>
285285
<ul><li>exit 3 — VIEWPORT TESSELLATE did not write the L1 closed form</li><li>exit 4 — RENDER TESSELLATE wrote viewport quality or the BEST_MATCH cage (<code>--evaluation-mode VIEWPORT</code> measured 26/24; <code>--subdivision BEST_MATCH</code> measured 8/6 <code>catmullClark</code>)</li><li>exit 5 — BEST_MATCH stopped writing the cage</li><li>exit 6 — RENDER and VIEWPORT files are identical</li></ul>
286-
<p>The still depicts viewport vs render tessellation of the same SUBSURF cube (flat-shaded L1 vs smoothed L2). The USDA point counts are the evidence.</p>
286+
<p>Staging: Catmull-Clark pulls the surface inside the 2 m cage, and level 1 and level 2 shrink by different amounts, so both balls used to hover about 0.16 m over the floor at the cage&#x27;s z = 1.0. The render path now rests each one on the floor from its own evaluated vertices (measured zmin 0.000000 for both), after the check has run.</p>
287287
<p>Probed on the CI Linux portables <strong>Blender 5.2.1 LTS</strong> (<code>9e2066aef7ef</code>) and <strong>Blender 4.5.13 LTS</strong> (<code>daeeeca98fb0</code>): <code>wm.usd_export</code> exists, <code>poll()</code> is true in <code>--background</code>, <code>evaluation_mode</code> is <code>{RENDER, VIEWPORT}</code>. The <code>--output</code> render path measures framing via <code>examples/gallery_framing.py</code> (exit 10 on violation).</p>
288288
<h2>Run</h2>
289289
<pre><code>blender --background --python usd_export_evaluation_mode.py --
@@ -319,6 +319,7 @@ <h2>Source</h2>
319319
blender --background --python usd_export_evaluation_mode.py -- --output u.png
320320
&quot;&quot;&quot;</span>
321321
<span class="k">import</span> bpy, bmesh, sys, os, math, argparse, tempfile
322+
<span class="k">from</span> mathutils <span class="k">import</span> Vector
322323

323324
sys.path.insert(<span class="n">0</span>, os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir))
324325
sys.dont_write_bytecode = <span class="k">True</span>
@@ -523,6 +524,19 @@ <h2>Source</h2>
523524
rm.render_levels = LEVEL_RD
524525
<span class="k">for</span> poly <span class="k">in</span> right.data.polygons:
525526
poly.use_smooth = <span class="k">True</span>
527+
<span class="c"># Subdivision pulls the surface inside the 2 m cage, so at z = 1.0 both</span>
528+
<span class="c"># ingots hovered above the floor. Rest each on z = 0 from its own</span>
529+
<span class="c"># evaluated bounds (level 1 and level 2 shrink by different amounts).</span>
530+
bpy.context.view_layer.update()
531+
dg = bpy.context.evaluated_depsgraph_get()
532+
<span class="k">for</span> ob <span class="k">in</span> (left, right):
533+
ev = ob.evaluated_get(dg)
534+
em = ev.to_mesh()
535+
<span class="k">try</span>:
536+
zmin = min((ev.matrix_world @ v.co).z <span class="k">for</span> v <span class="k">in</span> em.vertices)
537+
<span class="k">finally</span>:
538+
ev.to_mesh_clear()
539+
ob.location.z -= zmin
526540

527541
floor_me = bpy.data.meshes.new(<span class="s">&quot;Floor&quot;</span>)
528542
bm = bmesh.new()
@@ -556,12 +570,12 @@ <h2>Source</h2>
556570
ob.rotation_euler = tuple(math.radians(a) <span class="k">for</span> a <span class="k">in</span> rot)
557571
scene.collection.objects.link(ob)
558572

559-
light(<span class="s">&quot;Key&quot;</span>, (-<span class="n">4.0</span>, -<span class="n">5.0</span>, <span class="n">6.0</span>), <span class="n">200.0</span>, <span class="n">7.0</span>, (<span class="n">1.0</span>, <span class="n">0.96</span>, <span class="n">0.9</span>), (<span class="n">46</span>, <span class="n">0</span>, -<span class="n">35</span>))
573+
light(<span class="s">&quot;Key&quot;</span>, (-<span class="n">4.0</span>, -<span class="n">5.0</span>, <span class="n">6.0</span>), <span class="n">420.0</span>, <span class="n">5.0</span>, (<span class="n">1.0</span>, <span class="n">0.96</span>, <span class="n">0.9</span>), (<span class="n">46</span>, <span class="n">0</span>, -<span class="n">35</span>))
560574
light(<span class="s">&quot;Fill&quot;</span>, (<span class="n">5.0</span>, -<span class="n">3.5</span>, <span class="n">3.0</span>), <span class="n">180.0</span>, <span class="n">9.0</span>, (<span class="n">0.75</span>, <span class="n">0.85</span>, <span class="n">1.0</span>), (<span class="n">62</span>, <span class="n">0</span>, <span class="n">50</span>))
561575
light(<span class="s">&quot;Wedge&quot;</span>, (<span class="n">2.5</span>, <span class="n">5.5</span>, <span class="n">4.0</span>), <span class="n">360.0</span>, <span class="n">6.0</span>, (<span class="n">1.0</span>, <span class="n">0.76</span>, <span class="n">0.5</span>), (-<span class="n">68</span>, <span class="n">0</span>, <span class="n">190</span>))
562576

563577
aim = bpy.data.objects.new(<span class="s">&quot;Aim&quot;</span>, <span class="k">None</span>)
564-
aim.location = (<span class="n">0.0</span>, <span class="n">0.0</span>, <span class="n">1.0</span>)
578+
aim.location = (<span class="n">0.0</span>, <span class="n">0.0</span>, <span class="n">0.78</span>)
565579
aim.hide_render = <span class="k">True</span>
566580
scene.collection.objects.link(aim)
567581
cam_data = bpy.data.cameras.new(<span class="s">&quot;Cam&quot;</span>)
@@ -649,7 +663,7 @@ <h2>Source</h2>
649663
</main>
650664
<dialog class="lightbox" id="lightbox" aria-label="Full-size render">
651665
<button class="lightbox-close" type="button" autofocus>Close</button>
652-
<img src="../assets/usd-export-evaluation-mode-hero.webp" alt="Two grey spheres: a coarse, flat-shaded one on the left and a smooth, subdivided one on the right." />
666+
<img src="../assets/usd-export-evaluation-mode-hero.webp" alt="Two steel balls resting on the floor: a coarse, flat-shaded one on the left and a smooth, subdivided one on the right." />
653667
</dialog>
654668
<footer>
655669
<div class="statusbar">

‎examples/gallery.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,7 @@
645645
"name": "usd-export-evaluation-mode",
646646
"dir": "examples/usd-export-evaluation-mode",
647647
"teaches": "The USD exporter evaluation_mode chooses viewport versus render modifier quality. TESSELLATE makes the split observable; BEST_MATCH writes the cage and the mode is silent.",
648-
"alt": "Two grey spheres: a coarse, flat-shaded one on the left and a smooth, subdivided one on the right.",
648+
"alt": "Two steel balls resting on the floor: a coarse, flat-shaded one on the left and a smooth, subdivided one on the right.",
649649
"witnessesFix": "TESSELLATE+VIEWPORT writes 26/24; TESSELLATE+RENDER writes 98/96 subdivisionScheme=none; BEST_MATCH writes the 8-vert catmullClark cage. --evaluation-mode VIEWPORT and --subdivision BEST_MATCH exit 4.",
650650
"hero": "docs/gallery/assets/usd-export-evaluation-mode-hero.webp",
651651
"preview": "examples/usd-export-evaluation-mode/preview.webp",

‎examples/usd-export-evaluation-mode/README.md‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@ between.
2626
- exit 5 — BEST_MATCH stopped writing the cage
2727
- exit 6 — RENDER and VIEWPORT files are identical
2828

29-
The still depicts viewport vs render tessellation of the same SUBSURF cube
30-
(flat-shaded L1 vs smoothed L2). The USDA point counts are the evidence.
29+
Staging: Catmull-Clark pulls the surface inside the 2 m cage, and level 1
30+
and level 2 shrink by different amounts, so both balls used to hover about
31+
0.16 m over the floor at the cage's z = 1.0. The render path now rests each
32+
one on the floor from its own evaluated vertices (measured zmin 0.000000
33+
for both), after the check has run.
3134

3235
Probed on the CI Linux portables **Blender 5.2.1 LTS** (`9e2066aef7ef`) and
3336
**Blender 4.5.13 LTS** (`daeeeca98fb0`): `wm.usd_export` exists, `poll()` is
3.8 KB
Loading

‎examples/usd-export-evaluation-mode/usd_export_evaluation_mode.py‎

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
blender --background --python usd_export_evaluation_mode.py -- --output u.png
1919
"""
2020
import bpy, bmesh, sys, os, math, argparse, tempfile
21+
from mathutils import Vector
2122

2223
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir))
2324
sys.dont_write_bytecode = True
@@ -222,6 +223,19 @@ def render_still(obj, path, engine):
222223
rm.render_levels = LEVEL_RD
223224
for poly in right.data.polygons:
224225
poly.use_smooth = True
226+
# Subdivision pulls the surface inside the 2 m cage, so at z = 1.0 both
227+
# ingots hovered above the floor. Rest each on z = 0 from its own
228+
# evaluated bounds (level 1 and level 2 shrink by different amounts).
229+
bpy.context.view_layer.update()
230+
dg = bpy.context.evaluated_depsgraph_get()
231+
for ob in (left, right):
232+
ev = ob.evaluated_get(dg)
233+
em = ev.to_mesh()
234+
try:
235+
zmin = min((ev.matrix_world @ v.co).z for v in em.vertices)
236+
finally:
237+
ev.to_mesh_clear()
238+
ob.location.z -= zmin
225239

226240
floor_me = bpy.data.meshes.new("Floor")
227241
bm = bmesh.new()
@@ -255,12 +269,12 @@ def light(name, loc, energy, size, col, rot):
255269
ob.rotation_euler = tuple(math.radians(a) for a in rot)
256270
scene.collection.objects.link(ob)
257271

258-
light("Key", (-4.0, -5.0, 6.0), 200.0, 7.0, (1.0, 0.96, 0.9), (46, 0, -35))
272+
light("Key", (-4.0, -5.0, 6.0), 420.0, 5.0, (1.0, 0.96, 0.9), (46, 0, -35))
259273
light("Fill", (5.0, -3.5, 3.0), 180.0, 9.0, (0.75, 0.85, 1.0), (62, 0, 50))
260274
light("Wedge", (2.5, 5.5, 4.0), 360.0, 6.0, (1.0, 0.76, 0.5), (-68, 0, 190))
261275

262276
aim = bpy.data.objects.new("Aim", None)
263-
aim.location = (0.0, 0.0, 1.0)
277+
aim.location = (0.0, 0.0, 0.78)
264278
aim.hide_render = True
265279
scene.collection.objects.link(aim)
266280
cam_data = bpy.data.cameras.new("Cam")

0 commit comments

Comments
 (0)