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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ shader `Attribute` node is actually linked to Base Color.
</tr>
<tr>
<td width="46%" valign="middle">
<a href="examples/image-pixels-testcard/"><img src="examples/image-pixels-testcard/preview.webp" alt="Image pixels testcard: a studio monitor showing a procedural broadcast test card — seven neon color bars behind the classic dark circle, a luminance ramp, and a PLUGE row with a white bottom-left origin marker — over a teal underglow on a dark studio floor" /></a>
<a href="examples/image-pixels-testcard/"><img src="examples/image-pixels-testcard/preview.webp" alt="Image pixels testcard: a studio monitor on a machined metal stand showing a procedural broadcast test card — seven neon color bars behind the classic dark circle, a luminance ramp, and a PLUGE row with a white bottom-left origin marker — in a dark studio with a warm pool raking the back wall" /></a>
</td>
<td valign="middle">

Expand All @@ -168,7 +168,7 @@ non-destructive path.
</tr>
<tr>
<td width="46%" valign="middle">
<a href="examples/png-exr-alpha/"><img src="examples/png-exr-alpha/preview.webp" alt="PNG vs EXR alpha: two easel panels on a dark studio floor — left float→PNG false-unpremul clamps dark mid-tones to white at low alpha, right the EXR-clean authored straight buffer with surviving red and green primaries" /></a>
<a href="examples/png-exr-alpha/"><img src="examples/png-exr-alpha/preview.webp" alt="PNG vs EXR alpha: two framed verification displays on a shared plinth in a dark studio — left the float→PNG false-unpremul mangling clamps dark rows to white, right the EXR-clean straight buffer, nameplates reading FLOAT → PNG and FLOAT → EXR under a warm wall pool" /></a>
</td>
<td valign="middle">

Expand Down Expand Up @@ -205,7 +205,7 @@ result is watertight (every edge borders exactly two faces).
</tr>
<tr>
<td width="46%" valign="middle">
<a href="examples/uv-layer-grid/"><img src="examples/uv-layer-grid/preview.webp" alt="UV layer grid: two easel panels on a dark studio floor — left a flat teal square from a missing UV layer sampling texel (0,0), right a magenta/cyan neon checker proving pre-create + calc_uvs" /></a>
<a href="examples/uv-layer-grid/"><img src="examples/uv-layer-grid/preview.webp" alt="UV layer grid: two framed lightbox displays on floor trays against a dark studio wall — left one flat teal face (the calc_uvs silent no-op hazard), right a saturated magenta-cyan checker (the pre-create repair), under a warm wall pool" /></a>
</td>
<td valign="middle">

Expand Down
Binary file modified docs/gallery/assets/image-pixels-testcard-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/png-exr-alpha-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/uv-layer-grid-hero.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/gallery/gltf-export-roundtrip/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ <h1>gltf-export-roundtrip</h1>
<p>A runnable example that builds a sci-fi supply crate — 35 beveled box shells with three material slots and box-mapped UVs — exports it with <code>bpy.ops.export_scene.gltf</code>, parses the file on disk, re-imports it, and verifies the whole round-trip against the depsgraph-evaluated mesh, following <a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/skills/depsgraph-and-evaluated-data/SKILL.md"><code>depsgraph-and-evaluated-data</code></a> and <a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/skills/headless-batch-scripting/SKILL.md"><code>headless-batch-scripting</code></a>.</p>
<p><strong>Pipeline arc:</strong> modeling/LOD in <a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/examples/lod-decimate-chain"><code>lod-decimate-chain</code></a>, weighting in <a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/examples/vertex-weight-limit"><code>vertex-weight-limit</code></a>, export here.</p>
<p><strong>What it witnesses:</strong> the interchange contracts AI-generated export code most often gets silently wrong.</p>
<p>1. <strong>The +Y-up convention is baked into vertex data.</strong> glTF is +Y-up, Blender is +Z-up, and <code>export_yup=True</code> (the default) writes <code>(x, y, z) -&gt; (x, z, -y)</code> directly into the POSITION buffer — the node carries <strong>no</strong> rotation or scale. The check parses the <code>.gltf</code> JSON and asserts the accessor bounds equal the axis-converted evaluated bounding box, and that the node transform is absent. Exporting with <code>export_yup=False</code> ships raw Z-up data every engine displays lying on its back. 2. <strong><code>export_apply=True</code> ships the evaluated mesh, not the base cage.</strong> The crate&#x27;s bevel modifier lives only in the depsgraph; with flat shading and UV seams the exporter splits exactly one vertex per evaluated loop (7,560), so the on-disk POSITION count is an exact witness. <code>export_apply=False</code> silently writes the 624-vertex cage. 3. <strong>The round-trip is faithful.</strong> Re-imported positions (bit-exact here), loop normals (≤2e-4), box-mapped UVs (≤3e-5), and per-triangle material bindings all match the evaluated mesh. UVs are V-flipped on disk (glTF texture origin is top-left) and flipped back on import — both flips are proven by reading the <code>.bin</code> buffer directly.</p>
<ul><li><strong>The +Y-up convention is baked into vertex data.</strong> glTF is +Y-up, Blender is +Z-up, and <code>export_yup=True</code> (the default) writes <code>(x, y, z) -&gt; (x, z, -y)</code> directly into the POSITION buffer — the node carries <strong>no</strong> rotation or scale. The check parses the <code>.gltf</code> JSON and asserts the accessor bounds equal the axis-converted evaluated bounding box, and that the node transform is absent. Exporting with <code>export_yup=False</code> ships raw Z-up data every engine displays lying on its back.</li><li><strong><code>export_apply=True</code> ships the evaluated mesh, not the base cage.</strong> The crate&#x27;s bevel modifier lives only in the depsgraph; with flat shading and UV seams the exporter splits exactly one vertex per evaluated loop (7,560), so the on-disk POSITION count is an exact witness. <code>export_apply=False</code> silently writes the 624-vertex cage.</li><li><strong>The round-trip is faithful.</strong> Re-imported positions (bit-exact here), loop normals (≤2e-4), box-mapped UVs (≤3e-5), and per-triangle material bindings all match the evaluated mesh. UVs are V-flipped on disk (glTF texture origin is top-left) and flipped back on import — both flips are proven by reading the <code>.bin</code> buffer directly.</li></ul>
<p><strong>Version witness (probed on Blender 4.5.11 LTS and 5.1.2):</strong> the operator signatures are byte-identical — 109 exporter properties, 20 importer properties, same defaults — and the exported JSON differs only in <code>asset.generator</code> (&quot;Khronos glTF Blender I/O v4.5.51&quot; vs &quot;v5.1.20&quot;). The example therefore runs identical kwargs on both versions and guards forward drift explicitly: every kwarg it passes must still exist in the operator&#x27;s RNA, so a future rename fails loudly instead of drifting silently. One genuine 5.x removal surfaced during authoring: <code>Mesh.calc_normals()</code> is gone (loop normals auto-compute on read) — calling it is itself a cross-version hazard, noted in the code.</p>
<p>Two more authoring hazards are pinned in comments: exact face-plane coincidences between kit-bashed shells weld loops on export (the count check catches it), and <code>read_factory_settings</code> mid-check frees the original mesh — touching a freed RNA raises <code>ReferenceError</code>, so counts are captured before the wipe.</p>
<p>The render stages the authored crate beside the actual re-imported one — same bevels, same materials carried through the file itself. If the axis conversion broke, the right twin would lie on its side; if the modifier contract broke, its silhouette would lose the rounded edges.</p>
Expand Down
2 changes: 1 addition & 1 deletion docs/gallery/gltf-skin-roundtrip/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ <h1>gltf-skin-roundtrip</h1>
<p>A runnable example that rigs a mech scorpion — seven-bone chain from pedestal root to stinger, blend rings at every joint seam — exports it with <code>bpy.ops.export_scene.gltf</code> (<code>export_skins=True</code>), parses the file, re-imports it, and verifies the whole skinning contract against the authored rig, following <a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/skills/depsgraph-and-evaluated-data/SKILL.md"><code>depsgraph-and-evaluated-data</code></a>.</p>
<p><strong>Pipeline arc:</strong> modeling/LOD in <a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/examples/lod-decimate-chain"><code>lod-decimate-chain</code></a>, weighting in <a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/examples/vertex-weight-limit"><code>vertex-weight-limit</code></a>, export in <a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/examples/gltf-export-roundtrip"><code>gltf-export-roundtrip</code></a> — this is the skinning counterpart to the crate&#x27;s geometry round-trip. Tangent frames for the normal maps are in <a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/examples/triangulate-tangents"><code>triangulate-tangents</code></a>.</p>
<p><strong>What it witnesses:</strong> the skinned-mesh export contract the geometry round-trip left uncovered.</p>
<p>1. <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). 2. <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. 3. <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). 4. <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.</p>
<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>
<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>
<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>
<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>
Expand Down
Loading
Loading