Skip to content

Commit 0430939

Browse files
authored
feat: add custom-normals-shade example (post-4.1 shading contract) (#97)
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>
1 parent 872a725 commit 0430939

12 files changed

Lines changed: 1571 additions & 4 deletions

File tree

.cursor-plugin/plugin.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
"examples/collision-hull-proxy",
6666
"examples/compositor-glare",
6767
"examples/curve-bevel-arc",
68+
"examples/custom-normals-shade",
6869
"examples/damped-track-aim",
6970
"examples/depsgraph-export",
7071
"examples/driver-wave",

.github/workflows/blender-smoke.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,3 +460,17 @@ jobs:
460460
# the 255-face per-piece engine budget. Exits non-zero on failure.
461461
xvfb-run -a "$BLENDER" --background \
462462
--python examples/collision-hull-proxy/collision_hull_proxy.py --
463+
464+
- name: Shipped example - custom normals + shade by angle (post-4.1 shading contract)
465+
run: |
466+
set -euo pipefail
467+
# Check only (no render): a jerry can prop; asserts the legacy
468+
# shading API stays removed (use_auto_smooth/use_custom_normals/
469+
# calc_normals), set_sharp_from_angle sharp sets matching an
470+
# independent dihedral recompute exactly, evaluated normal welds/
471+
# splits, custom split normals surviving depsgraph evaluation
472+
# within int16 quantization, and the version-gated shade_auto_smooth
473+
# operator divergence (CANCELLED headless on 4.5, FINISHED on 5.1).
474+
# Exits non-zero on failure.
475+
xvfb-run -a "$BLENDER" --background \
476+
--python examples/custom-normals-shade/custom_normals_shade.py --

README.md

Lines changed: 24 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>31 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>32 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 31 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 32 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

@@ -504,7 +504,7 @@ round-trips through the raw `POINT` buffer.
504504
</details>
505505

506506
<details>
507-
<summary><strong>Game asset pipeline</strong> — 6 examples</summary>
507+
<summary><strong>Game asset pipeline</strong> — 7 examples</summary>
508508

509509
<table>
510510
<tr>
@@ -610,6 +610,27 @@ tests prove containment (5.9e-08), convexity, watertightness, outward
610610
winding, and Euler characteristic 2 per piece. Proud details cost cage rows;
611611
concave grooves are free.
612612

613+
</td>
614+
</tr>
615+
<tr>
616+
<td width="46%" valign="middle">
617+
<a href="examples/custom-normals-shade/"><img src="examples/custom-normals-shade/preview.webp" alt="Custom normals and shade by angle: three olive-drab jerry can props with pressed X ribs and red spout rings on a dark studio floor - one faceted flat, one smeared by smooth-everything, one crisp with correct hard edges - proving the post-4.1 shading contract" /></a>
618+
</td>
619+
<td valign="middle">
620+
621+
### [custom-normals-shade](examples/custom-normals-shade/)
622+
623+
The shading contract a prop's silhouette depends on: since Blender 4.1,
624+
hard edges are mesh data (face smooth flags + `sharp_edge` attribute), and
625+
`use_auto_smooth` / `use_custom_normals` / `calc_normals` are AttributeError
626+
on **both** 4.5 LTS and 5.1. `set_sharp_from_angle` marks sharp exactly the
627+
edges an independent dihedral recompute predicts; evaluated loop normals
628+
weld across smooth edges and split by the dihedral across sharp ones;
629+
custom split normals survive depsgraph evaluation within their int16
630+
quantization (1.407e-04, not float-exact). Documents the legacy
631+
`shade_auto_smooth` operator trap: CANCELLED headless on 4.5, FINISHED
632+
with the Smooth-by-Angle modifier on 5.1.
633+
613634
</td>
614635
</tr>
615636
</table>

ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ Not committed; target list for the next content version. (v0.3.0 shipped the smo
113113
- ~~GAMMA_CROSS blend-curve witness~~ **SHIPPED** as `examples/vse-gamma-cross/` — the cross blends in a gamma-0.5 space: `((1-t)·√A + t·√B)²` with `t = (frame − start)/duration`, never 1 inside the effect; mid-cross dips 0.115 below the sRGB lerp from crimson/teal (closed form (0.341, 0.349, 0.463) confirmed per frame); AgX-default sampling poisons the fit (0.146 red-channel error, `view_transform='Standard'` mandatory); deleting a consumed input orphans-and-deletes the effect — 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`)
115115
- ~~Collision compound witness~~ **SHIPPED** as `examples/collision-hull-proxy/` — game-prop collision as a compound of convex pieces, each a `bmesh.ops.convex_hull` of a coarse `sec(π/n)`-inflated cage (containment 5.9e-08, watertight, positive signed volume, Euler 2, per-piece 255-face budget: body 70, caps 60×3, compound 250); a hull of the dense render mesh measures 374 faces — over budget — which is why pipelines hull cages; proud details cost cage rows, concave grooves are free; byte-identical on 4.5.11 and 5.1.2
116-
- Custom-normals / shade-by-angle witness (game prop shading) — `use_auto_smooth` removed in 4.1; assert what 4.5 vs 5.1 actually expose (`shade_smooth_by_angle` operator / Smooth-by-Angle node group), per-loop custom normals surviving depsgraph evaluation, unit-length basis, hard edges landing exactly where an independently recomputed dihedral angle crosses the threshold
116+
- ~~Custom-normals / shade-by-angle witness~~ **SHIPPED** as `examples/custom-normals-shade/` — the post-4.1 shading contract: `use_auto_smooth`/`use_custom_normals`/`calc_normals` are AttributeError on BOTH 4.5.11 and 5.1.2; `set_sharp_from_angle` sharp sets match an independent dihedral recompute exactly (188/388 edges, 3 meshes); evaluated normal welds/splits exact (0.0 dev); custom split normals survive depsgraph evaluation within int16 quantization (1.407e-04, not float-exact); **divergence**: the legacy `shade_auto_smooth` operator CANCELS headless on 4.5 ("Asset loading is unfinished", mesh untouched, no exception) while 5.1 FINISHES with the Smooth-by-Angle NODES modifier — the data API is the portable path
117117
- prop-origin-transform witness — origin to base center, `transform_apply` through the data API, delta transforms, `matrix_parent_inverse` so parented children do not teleport; closed forms: post-apply scale exactly (1,1,1), local bbox min Z == 0, world bbox unchanged (builds on `parent-inverse-orrery`, does not duplicate it)
118118
- mesh-hygiene-audit witness — the engine-ingest checklist as executable contract: no ngons, no loose vertices, no non-manifold edges, no zero-area faces, consistent outward winding; closed forms via Euler characteristic and exact edge-face incidence counts
119119

23.5 KB
Loading
42.4 KB
Loading

0 commit comments

Comments
 (0)