You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 1812640
Browse filesBrowse the repository at this point in the historyBrowse files
fix(examples): seat the triangulate-tangents buckler on the floor
The display stand hung 0.49 m above the floor with nothing under it, and
nothing held the buckler's 20-degree backward lean. The stand now rests on
the floor, the rim's lowest evaluated vertex bites 1 cm into its top, and a
kickstand strut runs from the buckler's back (found by a local-space ray) to
the floor. The camera and aim ride with the derived lift. Render path only;
exit codes are 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>
<metaproperty="og:image:alt" content="A round silver buckler on a stand, its face brushed in circular grooves with an anisotropic highlight." />
19
+
<metaproperty="og:image:alt" content="A round silver buckler leaning back on a low stand, its face brushed in circular grooves with an anisotropic highlight." />
<metaname="twitter:description" content="A machined buckler verifying the tangent-space contract a game engine's normal mapping depends on. Deterministic triangulation, unit orthogonal tangent frames, and the edge/UV-delta formula matching mikktspace within welding tolerance." />
<metaname="twitter:image:alt" content="A round silver buckler on a stand, its face brushed in circular grooves with an anisotropic highlight." />
25
+
<metaname="twitter:image:alt" content="A round silver buckler leaning back on a low stand, its face brushed in circular grooves with an anisotropic highlight." />
26
26
<style>
27
27
/* fonts are deployed by the landing build (docs/fonts/) */
<p>A machined buckler verifying the tangent-space contract a game engine's normal mapping depends on. Deterministic triangulation, unit orthogonal tangent frames, and the edge/UV-delta formula matching mikktspace within welding tolerance.</p>
269
269
</header>
270
270
<mainid="main">
271
-
<buttonclass="detail-hero" id="heroZoom" type="button" aria-label="View full size: A round silver buckler on a stand, its face brushed in circular grooves with an anisotropic highlight.">
272
-
<imgsrc="../assets/triangulate-tangents-hero.webp" alt="A round silver buckler on a stand, its face brushed in circular grooves with an anisotropic highlight." width="1280" height="720" />
271
+
<buttonclass="detail-hero" id="heroZoom" type="button" aria-label="View full size: A round silver buckler leaning back on a low stand, its face brushed in circular grooves with an anisotropic highlight.">
272
+
<imgsrc="../assets/triangulate-tangents-hero.webp" alt="A round silver buckler leaning back on a low stand, its face brushed in circular grooves with an anisotropic highlight." width="1280" height="720" />
273
273
</button>
274
274
<pclass="zoom-hint">Rendered headless by the example itself — click to zoom.</p>
275
275
<divclass="callout"><spanclass="tag">witnesses</span> calc_tangents aborts on any ngon; planar UVs on a cylindrical wall collapse tangents onto normals; a MeshUVLoopLayer handle held across calc_tangents dangles on 4.5 (471 phantom flips, silent exit 0) while the mikktspace math is byte-identical on both versions.</div>
@@ -284,7 +284,7 @@ <h1>triangulate-tangents</h1>
284
284
<ul><li><strong>Deterministic triangulation and the engine basis.</strong><code>calc_loop_triangles</code> yields the closed-form count (two tris per quad + the cap fan, 720), and every loop tangent is unit length (err 1.3e-07) and exactly orthogonal to its loop normal (err 6.0e-08), with the bitangent exactly <code>bitangent_sign * (normal x tangent)</code> (err 0.0). One ngon anywhere in the mesh and <code>calc_tangents</code><strong>aborts the whole call</strong> — the back cap is an explicit fan for exactly that reason.</li><li><strong>The tangent frame follows the UVs.</strong> On smooth-field triangles the per-loop tangents match the independently derived edge/UV-delta formula within mikktspace's vertex-welding tolerance (measured 2.3e-06, tol 0.15); a flipped frame inside a smooth field is never legal (0 measured). At UV seams the frame orientation is implementation-defined — 42 seam flips, 9 at chart seams, identical on both versions — and planar UVs on a cylindrical wall are degenerate: they collapse the tangent onto the normal (dot 0.998, measured and fixed by unwrapping strips).</li><li><strong>The reference-lifetime hazard (the real divergence).</strong> A <code>MeshUVLoopLayer</code> handle held across <code>calc_tangents()</code> dangles: on Blender 4.5 reads through it return tangent floats, not UVs — measured while authoring as a phantom <strong>471 flipped frames and 385 phantom seam positions, exit 0, a silent wrong answer</strong>. On 5.1 the same stale read survives by memory-layout luck. The mikktspace math itself is byte-identical on 4.5.11 and 5.1.2; the apparent version divergence was entirely the corrupt handle. Never hold layer handles across CustomData-reallocating calls — re-fetch by name.</li></ul>
285
285
<p><strong>What each check catches on failure:</strong> a remeshed asset breaking the triangulation count (probe: dropped profile ring, exit 3, 624 vs 720); a wrong tangent formula in the independent derivation (probe: swapped du/dv, exit 7, weld 1.459); and the stale-handle read (probe: no re-fetch — corrupt measurements on 4.5, correct values on 5.1 by luck).</p>
286
286
<p><strong>Version witness:</strong> mikktspace output is identical on Blender 4.5.11 LTS and 5.1.2 (same weld deviations, same seam flips). The <code>calc_tangents(uvmap=)</code> signature is stable. The lifetime hazard above is the only behavioral split, and it corrupts measurements rather than raising.</p>
287
-
<p>The render shows the buckler on its cradle: brushed grooves circulating the boss with the anisotropic sweep riding them — the circulating tangent field made visible.</p>
287
+
<p>The render shows the buckler on its cradle: brushed grooves circulating the boss with the anisotropic sweep riding them — the circulating tangent field made visible. The staging is derived from the posed mesh: the stand rests on the floor, the rim's lowest vertex sits 1 cm into its top, and a kickstand strut runs from the buckler's back (found by a local-space ray) to the floor behind it.</p>
288
288
<h2>Run</h2>
289
289
<pre><code># Cheap correctness check (no render) — the CI check:
<imgsrc="../assets/triangulate-tangents-hero.webp" alt="A round silver buckler on a stand, its face brushed in circular grooves with an anisotropic highlight." />
811
+
<imgsrc="../assets/triangulate-tangents-hero.webp" alt="A round silver buckler leaning back on a low stand, its face brushed in circular grooves with an anisotropic highlight." />
0 commit comments