Skip to content

Commit a12a6bf

Browse files
authored
Merge pull request #67 from TMHSDigital/feat/uv-layer-grid-example
feat: add uv-layer-grid example (create_grid calc_uvs silent no-op)
2 parents e5e84f7 + 68bf1f2 commit a12a6bf

13 files changed

Lines changed: 1097 additions & 13 deletions

File tree

‎.cursor-plugin/plugin.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
"examples/temp-override-join",
7979
"examples/text-version-stamp",
8080
"examples/turntable",
81+
"examples/uv-layer-grid",
8182
"examples/wave-displace"
8283
]
8384
}

‎.github/workflows/blender-smoke.yml‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,3 +326,15 @@ jobs:
326326
# it. Exits non-zero on failure.
327327
xvfb-run -a "$BLENDER" --background \
328328
--python examples/image-pixels-testcard/image_pixels_testcard.py --
329+
330+
- name: Shipped example - uv layer grid (calc_uvs silent no-op)
331+
run: |
332+
set -euo pipefail
333+
# Check only (no render): create_grid(..., calc_uvs=True) without a
334+
# pre-existing UV layer leaves 0 layers (silent no-op); pre-create +
335+
# calc_uvs fills loops to the closed-form grid UVs
336+
# ((x/size+1)/2, (y/size+1)/2) within 1e-6 including mesh persistence;
337+
# explicit loop assignment is the calc_uvs-free fallback. Exits
338+
# non-zero on failure.
339+
xvfb-run -a "$BLENDER" --background \
340+
--python examples/uv-layer-grid/uv_layer_grid.py --

‎AGENTS.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ The content base (counts are CI-enforced against README.md and the manifest):
2727
- 2 templates: `extension-addon-template` for Extensions Platform add-ons,
2828
and `headless-batch-script-template` for unattended batch jobs.
2929
- 17 snippets covering canonical patterns.
30-
- 20 examples under `examples/<name>/`: runnable scripts that assert a real
30+
- 21 examples under `examples/<name>/`: runnable scripts that assert a real
3131
API contract with deterministic checks, exit non-zero on failure, and
3232
optionally render a still via `--output`. Each is executed headless on
3333
Blender 4.5 LTS and 5.1 by `blender-smoke.yml`; its render ships in the
@@ -42,7 +42,7 @@ Blender-Developer-Tools/
4242
rules/<rule-name>.mdc # 6 rule files
4343
templates/<template-name>/ # 2 starter templates
4444
snippets/<snippet-name>.py # 17 standalone Python snippets
45-
examples/<name>/ # 20 runnable smoke-gated examples (+ gallery.json)
45+
examples/<name>/ # 21 runnable smoke-gated examples (+ gallery.json)
4646
scripts/build_gallery.py # generates docs/gallery/ (stdlib only)
4747
scripts/site/ # vendored landing-page build (build_site.py + template)
4848
docs/gallery/ # committed generated gallery pages + hero assets

‎CLAUDE.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ skills/<skill-name>/SKILL.md - AI workflow definitions, 12 total
1919
rules/<rule-name>.mdc - Anti-pattern rules, 6 total
2020
templates/<template-name>/ - Starter projects, 2 total
2121
snippets/<snippet-name>.py - Standalone code patterns, 17 total
22-
examples/<name>/ - Runnable smoke-gated examples, 20 total (+ gallery.json)
22+
examples/<name>/ - Runnable smoke-gated examples, 21 total (+ gallery.json)
2323
scripts/build_gallery.py - Regenerates docs/gallery/ from gallery.json (stdlib only)
2424
scripts/site/ - Vendored landing-page build (Jinja2)
2525
docs/gallery/ - Committed generated gallery pages + hero renders
@@ -80,11 +80,11 @@ v0.1.0: canonical object creation and deletion, depsgraph evaluated mesh, bmesh
8080

8181
v0.2.0: Principled BSDF material, driver-with-custom-function via `driver_namespace`, application handler registration, shader node group with cross-version `interface` API, `foreach_get` bulk vertex read, version-branch skeleton, and USD export with `evaluation_mode='RENDER'`.
8282

83-
## Examples (20)
83+
## Examples (21)
8484

8585
Runnable scripts at `examples/<name>/`, each asserting a real API contract with
8686
deterministic checks (exit non-zero on failure) and optionally rendering a still via
87-
`--output`. All twenty run headless on Blender 4.5 LTS and 5.1 in `blender-smoke.yml`;
87+
`--output`. All twenty-one run headless on Blender 4.5 LTS and 5.1 in `blender-smoke.yml`;
8888
their renders ship in the site gallery at `docs/gallery/`. `examples/gallery.json` is the
8989
gallery's source of truth. When authoring a new one, copy the anatomy of
9090
`examples/bmesh-gear/` (script structure, README shape, dark-studio render recipe) and

‎README.md‎

Lines changed: 17 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>20 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>21 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 20 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 21 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

@@ -171,7 +171,7 @@ non-destructive path.
171171
</details>
172172

173173
<details>
174-
<summary><strong>Mesh, curves &amp; text</strong> — 6 examples</summary>
174+
<summary><strong>Mesh, curves &amp; text</strong> — 7 examples</summary>
175175

176176
<table>
177177
<tr>
@@ -186,6 +186,20 @@ A 14-tooth gear built entirely with bmesh — with `bm.free()` in a `try`/`final
186186
ownership contract demands. Asserts the closed-form vert/edge/face counts and that the
187187
result is watertight (every edge borders exactly two faces).
188188

189+
</td>
190+
</tr>
191+
<tr>
192+
<td width="46%" valign="middle">
193+
<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>
194+
</td>
195+
<td valign="middle">
196+
197+
### [uv-layer-grid](examples/uv-layer-grid/)
198+
199+
`bmesh.ops.create_grid(..., calc_uvs=True)` is a silent no-op unless a UV layer already
200+
exists — without one an Image Texture samples texel (0, 0) everywhere. Asserts the hazard,
201+
the pre-create repair against closed-form grid UVs, and an explicit assignment fallback.
202+
189203
</td>
190204
</tr>
191205
<tr>

‎ROADMAP.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,9 @@ Not committed; target list for the next content version. (v0.3.0 shipped the smo
9898
- Bump `blender_version_min` in the templates if 5.2 APIs are used
9999
- Additional snippets for asset library scripting, EXR baking, multi-file extensions
100100
- Gallery coverage follow-ups from the GPv3 review: light-linking, VSE sequences-to-strips witness (`grease-pencil-rosette`, `armature-bend`, `text-version-stamp`, and `image-pixels-testcard` shipped first)
101-
- UV-layer authoring witness: `bmesh.ops.create_grid(calc_uvs=True)` silently creates no UV layer unless one already exists, and an Image Texture without UVs samples texel (0,0) everywhere (found while authoring `image-pixels-testcard`)
101+
- ~~UV-layer authoring witness~~ **SHIPPED** as `examples/uv-layer-grid/` — `create_grid(..., calc_uvs=True)` silent no-op without a pre-existing UV layer; closed-form UV fill + explicit assignment fallback; dual-panel render (flat texel (0,0) vs neon checker)
102102
- Image save-format witness: float images saved to PNG with varying alpha suffer premultiply quantization (~0.98 worst-case round-trip error at 8-bit alpha extremes); EXR vs PNG storage contract
103+
- Attribute domain witness: writing a POINT-domain color attribute and reading it as if it were CORNER (or vice versa) silently shears colors across shared verts — companion to `color-attribute-wheel`
103104

104105
## Future (uncommitted)
105106

17.9 KB
Loading

‎docs/gallery/index.html‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ <h1>Examples Gallery</h1>
199199
<button class="chip" data-tag="rendering" type="button">rendering</button>
200200
<button class="chip" data-tag="shape-keys" type="button">shape-keys</button>
201201
<button class="chip" data-tag="transforms" type="button">transforms</button>
202+
<button class="chip" data-tag="uv" type="button">uv</button>
202203
</div>
203204
<main id="main">
204205
<div class="grid">
@@ -422,6 +423,17 @@ <h2><a href="image-pixels-testcard/">image-pixels-testcard</a></h2>
422423
<a class="card-link" href="image-pixels-testcard/">View example <span aria-hidden="true">&rarr;</span></a>
423424
</div>
424425
</article>
426+
<article class="card" data-tags="mesh bmesh uv materials">
427+
<a class="card-media" href="uv-layer-grid/" aria-label="uv-layer-grid example detail page">
428+
<img src="assets/uv-layer-grid-hero.webp" alt="uv-layer-grid — The UV-layer authoring hazard — bmesh" loading="lazy" decoding="async" />
429+
</a>
430+
<div class="card-body">
431+
<h2><a href="uv-layer-grid/">uv-layer-grid</a></h2>
432+
<p class="teaches">The UV-layer authoring hazard — bmesh.ops.create_grid(..., calc_uvs=True) is a silent no-op unless a UV layer already exists; without one an Image Texture samples texel (0,0) everywhere.</p>
433+
<p class="witnesses"><span class="tag">witnesses</span> calc_uvs=True alone leaves 0 UV layers; pre-create + calc_uvs fills loops to the closed-form grid UVs ((x/size+1)/2, (y/size+1)/2) within 1e-6; explicit loop assignment is the calc_uvs-free fallback.</p>
434+
<a class="card-link" href="uv-layer-grid/">View example <span aria-hidden="true">&rarr;</span></a>
435+
</div>
436+
</article>
425437
</div>
426438
</main>
427439
<footer>

0 commit comments

Comments
 (0)