Skip to content

Commit c1a2f8d

Browse files
authored
feat: add gltf-skin-roundtrip example witnessing the skinned export contract (#88)
The geometry round-trip left the skinning side of the format uncovered: skeleton, weights, and deformation must all survive it. A seven-bone scorpion rig exported with export_skins and re-imported pins all three — joint list and unit weight sums on disk, bit-exact weights and rest matrices on re-import, identical deformation — plus the parenting hazard (unparented skinned meshes let the exporter bind an armature by name) and the sorted-multiset trap (the exporter welds duplicate loops; compare by rest-key). The skins pipeline is byte-identical on 4.5.11 and 5.1.2. Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>
1 parent 1fb7dc3 commit c1a2f8d

12 files changed

Lines changed: 1552 additions & 4 deletions

File tree

.cursor-plugin/plugin.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
"examples/depsgraph-export",
6969
"examples/driver-wave",
7070
"examples/gltf-export-roundtrip",
71+
"examples/gltf-skin-roundtrip",
7172
"examples/gn-instance-grid",
7273
"examples/gn-sdf-remesh",
7374
"examples/grease-pencil-rosette",

.github/workflows/blender-smoke.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,3 +416,14 @@ jobs:
416416
# surviving calc_tangents. Exits non-zero on failure.
417417
xvfb-run -a "$BLENDER" --background \
418418
--python examples/triangulate-tangents/triangulate_tangents.py --
419+
420+
- name: Shipped example - glTF skin round-trip (skins, weights, deform)
421+
run: |
422+
set -euo pipefail
423+
# Check only (no render): a 7-bone mech scorpion exported with
424+
# export_skins and re-imported; asserts skins[0] joints, JOINTS_0/
425+
# WEIGHTS_0 unit sums, bone/parent/rest-matrix round-trip, bit-exact
426+
# weights via position-key lookup, and identical deformation of the
427+
# re-imported rig. Exits non-zero on failure.
428+
xvfb-run -a "$BLENDER" --background \
429+
--python examples/gltf-skin-roundtrip/gltf_skin_roundtrip.py --

README.md

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
</p>
1919

2020
<p align="center">
21-
<strong>12 skills</strong> &nbsp;&bull;&nbsp; <strong>6 rules</strong> &nbsp;&bull;&nbsp; <strong>2 templates</strong> &nbsp;&bull;&nbsp; <strong>17 snippets</strong> &nbsp;&bull;&nbsp; <strong>27 examples</strong>
21+
<strong>12 skills</strong> &nbsp;&bull;&nbsp; <strong>6 rules</strong> &nbsp;&bull;&nbsp; <strong>2 templates</strong> &nbsp;&bull;&nbsp; <strong>17 snippets</strong> &nbsp;&bull;&nbsp; <strong>28 examples</strong>
2222
</p>
2323

2424
<p align="center">
@@ -36,7 +36,7 @@
3636

3737
## Overview
3838

39-
This repository ships **12 skills, 6 rules, 2 templates, 17 snippets, and 27 runnable examples** for Blender Python development targeting Blender 5.1 (current stable) with Blender 4.5 LTS fallback support.
39+
This repository ships **12 skills, 6 rules, 2 templates, 17 snippets, and 28 runnable examples** for Blender Python development targeting Blender 5.1 (current stable) with Blender 4.5 LTS fallback support.
4040

4141
The content is consumed by AI coding agents (Cursor, Claude Code, any MCP-capable client) when working on Blender add-ons, geometry nodes scripts, batch pipelines, or animation tooling. There is no build step. Edit the markdown and Python files directly.
4242

@@ -472,7 +472,7 @@ round-trips through the raw `POINT` buffer.
472472
</details>
473473

474474
<details>
475-
<summary><strong>Game asset pipeline</strong> — 4 examples</summary>
475+
<summary><strong>Game asset pipeline</strong> — 5 examples</summary>
476476

477477
<table>
478478
<tr>
@@ -542,6 +542,23 @@ edge/UV-delta formula on smooth fields. Documents the planar-on-cylinder UV
542542
degeneracy (tangent collapses onto the normal) and the stale layer-handle
543543
hazard that silently corrupts measurements on 4.5.
544544

545+
</td>
546+
</tr>
547+
<tr>
548+
<td width="46%" valign="middle">
549+
<a href="examples/gltf-skin-roundtrip/"><img src="examples/gltf-skin-roundtrip/preview.webp" alt="glTF skin round-trip: two identical mech scorpions on a dark studio floor - orange armor, black segmented tails curled over their backs with glowing stinger tips, teal eyes - proving skeleton, weights, and deformation survive the export" /></a>
550+
</td>
551+
<td valign="middle">
552+
553+
### [gltf-skin-roundtrip](examples/gltf-skin-roundtrip/)
554+
555+
The skinning counterpart to `gltf-export-roundtrip`: a seven-bone rigged
556+
scorpion exported with `export_skins` and re-imported, asserting the joint
557+
list, JOINTS_0/WEIGHTS_0 unit sums, bone/parent/rest-matrix round-trip,
558+
bit-exact weights, and identical deformation of the re-imported rig — plus
559+
the parenting hazard: unparented skinned meshes let the exporter bind an
560+
armature by name.
561+
545562
</td>
546563
</tr>
547564
</table>

ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ Not committed; target list for the next content version. (v0.3.0 shipped the smo
108108
- ~~Vertex weight limit witness~~ **SHIPPED** as `examples/vertex-weight-limit/` — the 4-influence engine cap enforced via the data API (`v.groups` + `VertexGroup.remove` + renormalize); unit sums (measured 3e-8), pose preserved (4.9e-3), exact LBS from the mesh's own deform layer (2.7e-7), Root mount pinned
109109
- ~~Triangulate + tangent-space witness~~ **SHIPPED** as `examples/triangulate-tangents/``calc_tangents` aborts on any ngon (back cap must be an explicit fan); mikktspace matches the edge/UV-delta formula within welding tolerance on smooth fields (2.3e-6 measured); planar UVs on a cylindrical wall collapse tangents onto normals (dot 0.998); `MeshUVLoopLayer` handle dangles across `calc_tangents()` on 4.5 (471 phantom flips, silent exit 0) while the mikktspace math is byte-identical on 4.5.11 and 5.1.2
110110
- UV-handle lifetime snippet: re-fetch attribute/UV layers by name after any CustomData-reallocating call (`calc_tangents`, `VertexGroup.add`, modifier edits) — held handles dangle silently on 4.5, survive by luck on 5.1 (found authoring `triangulate-tangents`)
111-
- glTF skinned-mesh export witness: follow-up to `gltf-export-roundtrip` + `vertex-weight-limit`export a rigged mesh (weights + joints) to glTF and round-trip the skin, asserting influence counts and joint bindings survive the format
111+
- glTF skinned-mesh export witness: follow-up to `gltf-export-roundtrip` + `vertex-weight-limit`**SHIPPED** as `examples/gltf-skin-roundtrip/``skins[0].joints` names every bone, JOINTS_0/WEIGHTS_0 with unit sums (3e-8), weights bit-exact, rest matrices to 2.4e-07, deformation to 4.8e-07; exporter welds duplicate loops (sorted-multiset comparisons mispair — compare by rest-key); unparented skinned meshes make the exporter bind an armature by name
112112
- Degenerate-bevel weld hazard (snippet or rule): bevel width ≥ half a box dimension creates zero-area faces whose loops weld on glTF export (found authoring `gltf-export-roundtrip`, where the count check caught a 36-vertex weld)
113113
- GAMMA_CROSS blend-curve witness: the cross blend is not the naive linear mix (mid-cross measured (0.341, 0.349, 0.463) from crimson (0.85, 0.10, 0.22) and teal (0.06, 0.75, 0.80)) — derive and assert the gamma-space closed form per frame; follow-up to `vse-cut-list`
114114
- Falsy `bpy_prop_collection` trap snippet: an empty collection is falsy, so `editor.strips or editor.sequences` silently falls through to the legacy accessor on an empty timeline — always branch on `hasattr`; likely generalizes across the API (found authoring `vse-cut-list`)
14.6 KB
Loading
38.1 KB
Loading

0 commit comments

Comments
 (0)