Skip to content

Commit 58e2aef

Browse files
TMHSDigitalclaude
andcommitted
fix(examples): turn the gltf-skin-roundtrip scorpions three-quarters
The tail curls in each rig's Y-Z plane, and the camera looked at both scorpions head-on, so the curl was seen end-on and the tail stood straight up like a chimney: a boxy robot, not a scorpion. Both armatures are now yawed -50 deg about their own origins, composed into the world matrix because the glTF importer leaves its armature in quaternion mode, and set 1.0 m either side of centre. Render path only. Exit codes unchanged on 4.5.11, 5.1.2 and 5.2.1 (default 0, --no-skins 5, --output 0) and every measured line matches the baseline. Shared gallery index rides here. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
1 parent 1da59e4 commit 58e2aef

7 files changed

Lines changed: 44 additions & 9 deletions

File tree

7.23 KB
Loading
15.6 KB
Loading

‎docs/gallery/gltf-skin-roundtrip/index.html‎

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ <h1>gltf-skin-roundtrip</h1>
284284
<ul><li><strong>The skeleton survives.</strong> <code>skins[0].joints</code> names every bone; the re-imported armature carries the same 7 bones, the same parent chain, and rest matrices within 2.4e-07 — the +Y-up conversion applies to bone nodes exactly as it does to meshes (their translations convert, no rotation is written).</li><li><strong>The weights survive.</strong> Every primitive carries JOINTS_0/WEIGHTS_0; per-vertex weights on disk sum to 1 (err 3.0e-08); the re-imported vertex groups match the authored groups <strong>bit-exactly</strong> (w_err 0.0), compared as straddle-safe position keys, the same protocol as the crate example.</li><li><strong>The deformation survives.</strong> Posed identically, the re-imported rig&#x27;s evaluated mesh matches the original&#x27;s within 4.8e-07 — linear blend skinning through the file format. The comparison is by rest-position key, never sorted multisets: the exporter welds duplicate loops (32 here), so cardinalities differ and a naive sorted zip mispairs vertices (a phantom 2.29 &quot;deviation&quot; measured and fixed during authoring).</li><li><strong>The mesh must be parented to the armature.</strong> The exporter warns &quot;Armature must be the parent of skinned mesh&quot; and picks an armature by name otherwise — with two rigs in the file it can bind the wrong one.</li></ul>
285285
<p><strong>What each check catches on failure:</strong> exporting with <code>export_skins=False</code> (exit 5 — no skin on disk), stripping the weights (exit 4 — no vertex groups), and posing the re-imported rig differently (exit 19 — deformation deviates 0.90).</p>
286286
<p><strong>Version witness:</strong> the skins pipeline is stable between Blender 4.5 LTS and 5.1 — the exporter/importer RNA is byte-identical (probed with <code>gltf-export-roundtrip</code>), and every measured value matches to the digit on 4.5.11 and 5.1.2.</p>
287-
<p>The render stages the authored scorpion beside the actual re-imported one — same curl, same glowing stinger — proof the skin rode the format through.</p>
287+
<p>The render stages the authored scorpion beside the actual re-imported one — same curl, same glowing stinger — proof the skin rode the format through. Both rigs are turned three-quarters to the camera. The tail curls in the rig&#x27;s own Y-Z plane, so a head-on view saw the curl end-on: the tail stood straight up behind the body like a chimney. The yaw is composed into each armature&#x27;s world matrix, because the glTF importer leaves its armature in quaternion mode, where <code>rotation_euler</code> does nothing.</p>
288288
<h2>Run</h2>
289289
<pre><code># Cheap correctness check (no render) — the CI check:
290290
blender --background --python gltf_skin_roundtrip.py --
@@ -360,6 +360,10 @@ <h2>Source</h2>
360360
REST_TOL = <span class="n">1e-5</span> <span class="c"># rest-matrix round-trip tolerance</span>
361361
LBS_TOL = <span class="n">5e-4</span>
362362
KEY_DIGITS = <span class="n">4</span>
363+
<span class="c"># render staging only (not part of the check): each rig&#x27;s X offset and its</span>
364+
<span class="c"># yaw, so the tail&#x27;s curl (in the rig&#x27;s Y-Z plane) is seen in profile</span>
365+
STILL_X = <span class="n">1.0</span>
366+
STILL_YAW = -<span class="n">50.0</span>
363367

364368
EXPORT_KWARGS = dict(
365369
export_format=<span class="s">&#x27;GLTF_SEPARATE&#x27;</span>,
@@ -897,9 +901,20 @@ <h2>Source</h2>
897901
authored.data.materials.append(m)
898902
assign_weights(authored, part_of2)
899903
authored_arm = build_rig(authored)
900-
<span class="c"># move the ARMATURES; the skinned meshes follow as children</span>
901-
authored_arm.location.x = -<span class="n">1.1</span>
902-
roundtrip_arm.location.x = <span class="n">1.1</span>
904+
<span class="c"># move the ARMATURES; the skinned meshes follow as children. Each is</span>
905+
<span class="c"># turned three-quarters to the camera: seen head-on, the tail stood</span>
906+
<span class="c"># straight up behind the body like a chimney and the scorpion read</span>
907+
<span class="c"># as a boxy robot; side-on, the arch of the tail over the back and</span>
908+
<span class="c"># the legs down its flanks carry the read.</span>
909+
<span class="c"># The yaw is composed into the world matrix about the rig&#x27;s own</span>
910+
<span class="c"># origin: the glTF importer leaves its armature in quaternion mode,</span>
911+
<span class="c"># where rotation_euler does nothing.</span>
912+
<span class="k">for</span> arm, x <span class="k">in</span> ((authored_arm, -STILL_X), (roundtrip_arm, STILL_X)):
913+
arm.location.x = x
914+
bpy.context.view_layer.update()
915+
at = mathutils.Matrix.Translation(arm.matrix_world.translation)
916+
arm.matrix_world = (at @ mathutils.Matrix.Rotation(
917+
math.radians(STILL_YAW), <span class="n">4</span>, <span class="s">&#x27;Z&#x27;</span>) @ at.inverted() @ arm.matrix_world)
903918
<span class="k">if</span> <span class="k">not</span> render_still(authored, os.path.abspath(args.output), args.engine):
904919
print(<span class="s">&quot;ERROR: render produced no file&quot;</span>, file=sys.stderr)
905920
<span class="k">return</span> <span class="n">20</span>

‎docs/gallery/index.html‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,12 +1036,12 @@ <h2><a href="park-bench/">park-bench</a></h2>
10361036
</article>
10371037
<article class="card" data-tags="mesh export showcase">
10381038
<a class="card-media" href="wheelbarrow/" tabindex="-1" aria-hidden="true">
1039-
<img src="assets/wheelbarrow-hero.webp" alt="A wooden wheelbarrow whose tray walls are stacked boards held by iron straps, with a spoked front wheel and two long handles." loading="lazy" decoding="async" />
1039+
<img src="assets/wheelbarrow-hero.webp" alt="A wooden wheelbarrow with a flared tray of stacked boards held by iron straps, its front raked out over a spoked wheel, and two long shafts running from the handles under the tray to the axle." loading="lazy" decoding="async" />
10401040
</a>
10411041
<div class="card-body">
10421042
<h2><a href="wheelbarrow/">wheelbarrow</a></h2>
10431043
<p class="teaches">A procedural wooden wheelbarrow through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets rather than an API contract.</p>
1044-
<p class="witnesses"><span class="tag">witnesses</span> Recomputed: 2536 tris, two materials with 1157 wood and 190 metal faces, UVs in 0..1 with zero AABB overlap, outer AABB 1.556×0.630×0.572 m, zmin 0, hygiene 0, wall-floor seat 12 mm, spoke clearance 40 mm, two boards per side and front wall with 2 mm seams, LOD ratios in band (5.2 COLLAPSE leaner on LOD2), convex collider 90 tris, non-empty glTF. --skip-decimate exits 9 on LOD1; --stray-vert exits 15 on hygiene; --lift-z / --short-legs exit 16 on grounded; --fat-spokes / --float-walls / --wide-seams exit 17 on joint fit; --pipe-rim exits 18 on tread aspect.</p>
1044+
<p class="witnesses"><span class="tag">witnesses</span> Recomputed: 2440 tris, two materials with 1085 wood and 214 metal faces, UVs in 0..1 with zero AABB overlap, outer AABB 1.563x0.594x0.577 m, zmin 0, hygiene 0, both shafts one timber from grip to axle, tray flared 15 deg and raked 30 deg, wall-floor seat 8 mm, spoke clearance 52 mm, two boards per side and front wall with 2 mm seams measured in the wall&#x27;s plane, LOD ratios in band (5.2 COLLAPSE leaner on LOD2), convex collider 80 tris, non-empty glTF. --skip-decimate exits 9 on LOD1; --stray-vert exits 15 on hygiene; --lift-z / --short-legs exit 16 on grounded; --fat-spokes / --float-walls / --wide-seams exit 17 on joint fit; --pipe-rim exits 18 on tread aspect; --split-shafts exits 20 on shaft runs; --box-tray exits 21 on tray flare.</p>
10451045
<a class="card-link" href="wheelbarrow/">View showcase piece<span class="sr-only"> wheelbarrow</span> <span aria-hidden="true">&rarr;</span></a>
10461046
</div>
10471047
</article>

‎examples/gltf-skin-roundtrip/README.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ deviates 0.90).
4646

4747
The render stages the authored scorpion beside the actual re-imported one —
4848
same curl, same glowing stinger — proof the skin rode the format through.
49+
Both rigs are turned three-quarters to the camera. The tail curls in the
50+
rig's own Y-Z plane, so a head-on view saw the curl end-on: the tail stood
51+
straight up behind the body like a chimney. The yaw is composed into each
52+
armature's world matrix, because the glTF importer leaves its armature in
53+
quaternion mode, where `rotation_euler` does nothing.
4954

5055
## Run
5156

‎examples/gltf-skin-roundtrip/gltf_skin_roundtrip.py‎

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@
5252
REST_TOL = 1e-5 # rest-matrix round-trip tolerance
5353
LBS_TOL = 5e-4
5454
KEY_DIGITS = 4
55+
# render staging only (not part of the check): each rig's X offset and its
56+
# yaw, so the tail's curl (in the rig's Y-Z plane) is seen in profile
57+
STILL_X = 1.0
58+
STILL_YAW = -50.0
5559

5660
EXPORT_KWARGS = dict(
5761
export_format='GLTF_SEPARATE',
@@ -589,9 +593,20 @@ def main():
589593
authored.data.materials.append(m)
590594
assign_weights(authored, part_of2)
591595
authored_arm = build_rig(authored)
592-
# move the ARMATURES; the skinned meshes follow as children
593-
authored_arm.location.x = -1.1
594-
roundtrip_arm.location.x = 1.1
596+
# move the ARMATURES; the skinned meshes follow as children. Each is
597+
# turned three-quarters to the camera: seen head-on, the tail stood
598+
# straight up behind the body like a chimney and the scorpion read
599+
# as a boxy robot; side-on, the arch of the tail over the back and
600+
# the legs down its flanks carry the read.
601+
# The yaw is composed into the world matrix about the rig's own
602+
# origin: the glTF importer leaves its armature in quaternion mode,
603+
# where rotation_euler does nothing.
604+
for arm, x in ((authored_arm, -STILL_X), (roundtrip_arm, STILL_X)):
605+
arm.location.x = x
606+
bpy.context.view_layer.update()
607+
at = mathutils.Matrix.Translation(arm.matrix_world.translation)
608+
arm.matrix_world = (at @ mathutils.Matrix.Rotation(
609+
math.radians(STILL_YAW), 4, 'Z') @ at.inverted() @ arm.matrix_world)
595610
if not render_still(authored, os.path.abspath(args.output), args.engine):
596611
print("ERROR: render produced no file", file=sys.stderr)
597612
return 20
7.85 KB
Loading

0 commit comments

Comments
 (0)