Skip to content

fix(examples): rebuild triangulate-tangents buckler as a machined shield - #261

Merged
TMHSDigital merged 4 commits into
mainfrom
fix/hero-triangulate-tangents
Sep 26, 2026
Merged

TMHSDigital merged 4 commits into
mainfrom
fix/hero-triangulate-tangents

Conversation

@TMHSDigital

@TMHSDigital TMHSDigital commented Sep 26, 2026 •

Copy link
Copy Markdown
Owner

What and why

A gallery review judged the triangulate-tangents hero weak:

  • Wobbly face. The face was covered in wavy ring ripples that read as a shading bug. That is the worst look for an example about tangent-space correctness. The root cause was the old material: a ShaderNodeTexWave on Generated coordinates with Distortion 3.0. The ripples were never UV-driven.
  • Wrong read. The object looked like a dish or speaker cone with a murky grey interior.
  • Hole at the apex. The profile's innermost ring was left open and rendered as a black hole.

Changes

  • Geometry (checked mesh). The buckler is one closed lathe mesh, 24 rings × 96 sides:
    • a pointed turned boss, a flat flange, a cosine dome, a cut groove and a rolled rim band;
    • a new apex fan closes the top, and the closed-form triangle count includes it (2*((RINGS-1)*SIDES + 2*SIDES) + 2*SIDES = 4992);
    • the back plate is thinner;
    • the mesh is smooth-shaded, with sharp edges where faces turn more than 30°. As a result mikktspace now really welds frames across shared vertices.
  • Materials (render only). Assigned per lathe ring: turned brass boss and rim, blued-steel flange, turned-steel dome, leather back, and a separate Buckler.Rivets part with 16 rim rivets and 8 flange rivets.
  • Grooves. Lathe grooves are |sin(pi*40*v)| bumps read straight off UV v, which is the radius. They are exact circles with no distortion, masked by a constant ramp into two decorative bands.
  • Highlight. The anisotropic highlight follows a UV-map ShaderNodeTangent. The old code looked up a nonexistent "Anisotropic IOR Level" input first; it now uses Anisotropic directly.
  • Staging. The shield is turned three-quarter (yaw +48°), so the boss projects and the dome reads convex. The stand is walnut with beveled edges. The kickstand foot now angles away from the camera, so it no longer peeks out under the rim. A warmer key replaces the old Glint light, a Top softbox is added, and the Fill is trimmed.
  • Asset-quality gate. The render path now runs gallery_asset_quality.check_asset_quality (exit 11) after check_framing.
  • Docs.
    • The docs/VISUAL-STYLE.md numeric-only precedent sentence now states the actual mechanism: grooves are a bump off UV v, and the streak follows the renderer's own UV-map tangent. So a break in the calc_tangents() loop data the check reads does not move pixels.
    • The README numbers, render description and exit-code table (11) are updated.
    • The gallery.json alt and the README gallery-row alt are updated.

Evidence

  • Contact sheet: docs/gallery/contact-sheets/triangulate-tangents-contact-sheet.webp
  • Asset sheet: docs/gallery/asset-sheets/triangulate-tangents.webp. It was regenerated with the committed scripts/asset_sheet.py (from chore(gates): raise the asset-sheet reference bar and commit its renderer #257) against the new reference set on 5.2.1.
    • Candidate first, then collision-hull-proxy, socket-attach-points, vertex-color-ao, wheelbarrow and apothecary-shelf.
    • The new SELECT row is ^Buckler(\.Rivets)?$: the lathe mesh plus the rivets. The walnut stand and strut are display staging, not part of the asset, so they are excluded.
    • Panel floors: parts=2, materials=4, edge90=0.012.
  • Before: the committed hero on main (docs/gallery/assets/triangulate-tangents-hero.webp at 15ba0a3).

Gate numbers (5.2.1)

  • Framing: fill x=0.381, y=0.828 (band 0.70–0.90) ok. Margins: left 0.300, right 0.319, bottom 0.028, top 0.039 (min 0.02). No touch or cross.
  • Asset quality: no default names; materials n=4; edge90 0.012 (96/8224), no degenerate faces; compactness 10.2 (informational only). aq_ok.
    • The line prints [email protected] ... FAIL, but that metric is informational only and is not gated (see VISUAL-STYLE § Dropped floors).
  • Clipping: 0.09 % of pixels at full white. The old hero had a blown dome.
  • Drift: measure_hero_drift.py --only triangulate-tangents gives mean_abs 0.00307, gt2pct 0.33 %, verdict matches.

Contact-sheet metrics

tile luma stage warmth sat
candidate (new) 0.2147 0.1891 +0.0186 0.1018
old hero 0.2454 0.1996 −0.0074 0.0553
armature-bend 0.2787 0.2132 −0.4279 0.1436
damped-track-aim 0.1511 0.1448 +0.2772 0.2305
bmesh-gear 0.3024 0.2357 +0.3743 0.1953

Per-criterion verdicts

  • Stage darkness: pass. Stage 0.189 sits inside the calibration range 0.145–0.236.
  • Wedge warmth: marginal. It is +0.019, up from −0.007. The brightest tenth of the lower half is the steel dome, which is neutral by material. The warm wedge is visible on the back wall at upper right, and the key is warm.
  • Subject fill: pass. 0.828 y.
  • Saturation: improved but below calibration. 0.102, up from 0.055. A steel subject is inherently low-saturation; the brass boss and rim and the walnut stand carry the colour.
  • Mean luminance: pass. 0.215 falls within the calibration spread of 0.151–0.302.
  • Thumbnail legibility: pass. At 320×180 it reads as a round shield with a pointed boss, riveted rim and concentric grooves. There is no wobble or moiré.

Asset-sheet verdict (new reference set)

Pass, but close. The buckler is on par with the hydrant (collision-hull-proxy). Both are lathe-turned bodies with distinct material zones and designed profile breaks. The buckler adds a pointed boss, a groove-banded dome and 24 rivets, so it is not identifiable as the least-designed object.

It is the simplest silhouette in the lineup, though. The drone, well, wheelbarrow and shelf are assemblies of many parts with more structure. A round shield is inherently a single turned plate, so it will not match their part count.

Checks per binary (check-only path)

binary (reported version) exit tris unit / dot / btn / uv err weld_err seam / chart flips
E:\Blender-Developer-Tools\.scratch\blender-5.2.1-windows-x64\blender.exe (Blender 5.2.1 LTS) 0 4992 1.34e-07 / 9.89e-08 / 0 / 0 3.27e-02 150 / 12
...\blender-5.1.2-windows-x64\blender.exe (Blender 5.1.2) 0 4992 identical 3.27e-02 150 / 12
...\blender-4.5.11-windows-x64\blender.exe (Blender 4.5.11 LTS) 0 4992 identical 3.27e-02 150 / 12

Falsifiers (re-proven on the new geometry, all three binaries unless noted)

probe how 5.2.1 5.1.2 4.5.11 measured
--zero-uv README flag exit 4 exit 4 exit 4 UV deviation 1.405
dropped profile ring delete one rim ring from PROFILE exit 3 exit 3 exit 3 4800 vs 4992 triangles
swapped du/dv break the independent tangent formula exit 7 exit 7 exit 7 weld 1.460
no re-fetch after calc_tangents stale MeshUVLoopLayer handle exit 0, correct exit 0, correct exit 0, corrupt 4.5 shows 2829 phantom seam flips over 2497 phantom seam positions
asset-quality gate (new call site) rename Buckler.Rivets to Sphere (5.2.1, --output) exit 11, no still written — — —

The stale-handle row matches the documented hazard: the handle is silent on 4.5 and survives by luck on 5.x.

Proven by live run vs established by inspection only

Proven by live run (headless binaries):

  • all check-path numbers above;
  • all falsifier exits;
  • framing and asset-quality gate output;
  • the hero and preview regenerated via scripts/render_hero.py;
  • contact-sheet metrics;
  • asset-sheet panels, via scripts/asset_sheet.py against the chore(gates): raise the asset-sheet reference bar and commit its renderer #257 reference set;
  • the drift measurement;
  • build_gallery.py output, with the generated <img alt> read back in docs/gallery/index.html and docs/gallery/triangulate-tangents/index.html.

Established by inspection only:

  • the thumbnail-legibility and asset-sheet verdicts are eye judgments of the committed images;
  • the VISUAL-STYLE claim that the render does not depend on calc_tangents() loop data. It rests on the material using only the UV-map Tangent node and UV coordinates; no render was made with corrupted loop tangents.

🤖 Generated with Claude Code

The gallery review judged the hero weak. The face was covered in wobbly
ring ripples that read as a shading bug, which is the worst look for an
example about tangent-space correctness. The object also read as a dish or
speaker cone with a murky grey interior. The ripples came from a Generated-
coordinate wave texture with distortion 3.0, so they were never UV-driven.
The open ring at the apex also rendered as a black hole.

The buckler is now one closed lathe mesh: a pointed turned boss, a flange,
a cosine dome, a cut groove and a rolled rim. An apex fan closes the top and
is counted in the closed-form triangle count (4992). The mesh is smooth-shaded
with sharp edges at the machined breaks, so mikktspace now really welds
frames. The measured weld is 3.27e-02, inside the unchanged 0.15 tolerance.
Materials are assigned per ring: brass boss and rim, blued-steel flange,
turned-steel dome, leather back, plus steel rivets. The lathe grooves are a
bump read straight off UV v, so they are exact circles. The anisotropic
streak follows the UV-map Tangent node. The shield is turned three-quarter so
the boss projects and the dome reads convex. The render path now also runs
the asset-quality floors (exit 11).

Every check is unchanged and passes, with identical numbers on 4.5.11,
5.1.2 and 5.2.1. The falsifiers were re-proven on the new geometry. The
VISUAL-STYLE numeric-only precedent sentence now states the actual
mechanism.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Signed-off-by: TMHSDigital <[email protected]>
@github-actions github-actions Bot added examples Runnable smoke-gated examples under examples/ documentation Improvements or additions to documentation labels Sep 26, 2026
TMHSDigital and others added 3 commits September 25, 2026 20:17
…e new reference set

Adds a SELECT row for the buckler to scripts/asset_sheet.py. It matches the
Buckler lathe mesh and Buckler.Rivets only. The walnut stand and kickstand
strut are display staging, not part of the asset, so they are excluded.
The asset sheet is re-rendered with the committed tooling against the
reference set from #257 (collision-hull-proxy, socket-attach-points,
vertex-color-ao, wheelbarrow, apothecary-shelf).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Signed-off-by: TMHSDigital <[email protected]>
Keeps all asset-sheet SELECT rows; generated gallery pages regenerate
unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Signed-off-by: TMHSDigital <[email protected]>
@TMHSDigital
TMHSDigital merged commit 2f4f4e0 into main Sep 26, 2026
11 checks passed
@TMHSDigital
TMHSDigital deleted the fix/hero-triangulate-tangents branch September 26, 2026 23:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation examples Runnable smoke-gated examples under examples/

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant