feat: add custom-normals-shade example (post-4.1 shading contract)#97
Merged
Conversation
A prop's silhouette lives or dies on which edges shade hard and which
shade smooth, and since Blender 4.1 that is mesh data: face smooth flags
plus a sharp_edge attribute. AI-generated code still emits the removed API
(mesh.use_auto_smooth, shade_auto_smooth), so this example pins the truth
on both supported versions: the legacy API is AttributeError on 4.5 LTS
and 5.1; set_sharp_from_angle marks sharp exactly the edges an
independent dihedral recompute predicts (188/388 across 3 meshes);
evaluated loop normals weld across smooth edges and split by the dihedral
across sharp ones (0.0 deviation); and per-loop custom normals survive
depsgraph evaluation within their int16 storage quantization (1.407e-04 —
asserting float-exactness is a real, caught bug).
Version-gated divergence, probed on both binaries: the legacy
shade_auto_smooth operator needs the bundled Smooth-by-Angle node-group
asset — headless on 4.5 it returns {'CANCELLED'} with the mesh untouched
(silent flat shading for scripts that ignore the return set), while 5.1
FINISHES with the NODES modifier. The data API is the portable path.
Falsified: threshold drift (exit 5, 4 extra edges), smooth flags lost
after marking (exit 6, 3.83e-01), float-exact custom normals (exit 7,
1.407e-04 vs 1e-6). Check output identical on Blender 4.5.11 LTS and
5.1.2 except the asserted operator divergence.
Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ships
examples/custom-normals-shade/— the second of the "game prop validation" pair (pairs withcollision-hull-proxy, #96). A jerry can prop (rounded-slab shell, pressed X ribs, spout + cap, three-post handle) verifying the shading contract a prop's silhouette depends on: since Blender 4.1, hard/smooth edges are mesh data — face smooth flags plus asharp_edgeattribute.Scope guard: witnesses the bpy-level contract a prop pipeline relies on; not an engine exporter, no FiveM/engine compatibility claimed — the README says so explicitly.
Contract witnessed
use_auto_smooth,use_custom_normals,calc_normalsare AttributeError on 4.5 LTS and 5.1. AI code still emitsmesh.use_auto_smooth = True; the check keeps it dead.mesh.set_sharp_from_angle(30°)(probed: it also sets the face smooth flags, both versions) marks sharp exactly the edges an independently recomputed dihedral test predicts: Shell 48/72, rib 12/12, neck 128/304 — exact set match, 188/388 edges.shade_auto_smoothoperator needs the bundled Smooth-by-Angle node-group asset. Headless on 4.5:"Warning: Asset loading is unfinished"→{'CANCELLED'}, mesh untouched, no exception — silent flat shading for any script that ignores the return set. On 5.1:{'FINISHED'}+ the NODES modifier. The data API is the portable path.Falsification evidence (probes run, checks fail as claimed)
sharp_edgeset, face smooth flags then lostThe legacy-operator divergence is asserted live on every run (exit 8 on drift) — it is the version gate, not a probe.
Visual notes
Hero render (Cycles, 48 samples): the same can three ways under a strip light — flat (faceted corners), smooth-everywhere (the smeared AI bug: ribs melt, highlight warps at the rim), by-angle (crisp). A broken path is visible in the image. Contact sheet (candidate vs pinned calibration set): custom-normals-shade-contact-sheet.webp
Per-criterion verdicts: stage darkness matches (same recipe); wedge warmth present (pool right); subject fill ~75% frame height; saturation — olive-drab is deliberately muted vs the vivid set, carried by the glossy steel + red marker rings; thumbnail legibility — the melted-vs-crisp rib tell survives 320px. Mean luminance 146.3/255 vs calibration 139.4 / 100.1 / 143.4 — top of band but within 2% of bmesh-gear; the set's own spread is 43 points, so not sortable as the odd one out. First draft measured 151.7 and was re-lit (key 600→520 W, strip 650→460 W, richer olive) before shipping. No calibration-set change.
Test plan
.scratch/blender-5.1.2-windows-x64/blender.exe(reports Blender 5.1.2, build 2026-05-19) — check-only exit 0; all three falsification probes confirmed; EEVEE drafts + final Cycles hero rendered locally..scratch/blender-4.5.11-windows-x64/blender.exe(reports Blender 4.5.11 LTS, build 2026-06-23) — check-only exit 0 with the CANCELLED legacy-op path asserted; all other values identical to 5.1.2.blender-smokeruns the check-only step under both matrix series. Note: CI currently resolves 4.5.12 (local is 4.5.11); the 4.5-series legacy-op assertion (CANCELLED headless) is expected to hold — if a 4.5.x patch changed asset loading, this step goes red and that drift is the witness working.Live-run vs inspection
shade_auto_smoothdivergence (isolated fresh-mesh probes plus the in-script assertion), probe exits, hero/preview/contact-sheet pixels, gallery HTML audited post-generation.eevee_engine_id()branch exercised on 5.1; Cycles is the shipped hero engine).Integration
Example dir + README + preview, hero webp, committed contact sheet,
gallery.jsonentry, plugin manifest (alphabetical),blender-smoke.ymlstep (both matrix series), top-level README counts 31→32 + Game asset pipeline row (6→7), ROADMAP pool entry marked shipped, gallery regenerated viascripts/build_gallery.py.