Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
],
"skills": [
"skills/addon-scaffolding/SKILL.md",
"skills/ai-mesh-cleanup/SKILL.md",
"skills/operators/SKILL.md",
"skills/ui-panels/SKILL.md",
"skills/custom-properties/SKILL.md",
Expand All @@ -34,19 +35,25 @@
"rules/target-extensions-platform-format.mdc",
"rules/type-annotate-props-and-defend-context.mdc",
"rules/prefer-temp-override-over-context-copy.mdc",
"rules/use-foreach-set-for-bulk-data.mdc"
"rules/use-foreach-set-for-bulk-data.mdc",
"rules/validate-imported-mesh-scale.mdc",
"rules/no-unapplied-modifiers-on-export.mdc"
],
"snippets": [
"snippets/action-ensure-channelbag-for-slot.py",
"snippets/app-handler-registration.py",
"snippets/bmesh-load-edit-free.py",
"snippets/canonical-object-creation.py",
"snippets/canonical-object-deletion.py",
"snippets/convex_hull_collider.py",
"snippets/cross-version-property-delete.py",
"snippets/decimate_to_budget.py",
"snippets/depsgraph-evaluated-mesh.py",
"snippets/driver-with-custom-function.py",
"snippets/foreach-get-vertices.py",
"snippets/foreach-set-vertices.py",
"snippets/gltf_draco_export.py",
"snippets/lod_chain.py",
"snippets/pointerproperty-binding.py",
"snippets/principled-bsdf-material.py",
"snippets/register-classes-factory.py",
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ jobs:
fi
echo "All snippets have valid Python syntax."

- name: Check import-scale and unevaluated-export anti-patterns
run: python3 tests/check_import_export_rules.py

- name: Validate template Python syntax
run: |
echo "Checking template Python syntax..."
Expand Down
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ a `.cursor-plugin/plugin.json` manifest so the ecosystem drift checker
classifies it as a `cursor-plugin`. This is content the AI loads when the user
asks Blender questions or works on Blender add-ons in Cursor or Claude Code.

The content base is 13 skills, 6 rules, 2 templates, 17 snippets, and 53
The content base is 14 skills, 8 rules, 2 templates, 21 snippets, and 53
examples (counts are CI-enforced against README.md and the manifest). The full
inventory tables and per-item purposes live in `CLAUDE.md`. Example anatomy
and authoring rules: copy `examples/bmesh-gear/`; the render look is specified
Expand All @@ -31,10 +31,10 @@ in `docs/VISUAL-STYLE.md`; the canonical run prompt is

```
Blender-Developer-Tools/
skills/<skill-name>/SKILL.md # 13 skill files
rules/<rule-name>.mdc # 6 rule files
skills/<skill-name>/SKILL.md # 14 skill files
rules/<rule-name>.mdc # 8 rule files
templates/<template-name>/ # 2 starter templates
snippets/<snippet-name>.py # 17 standalone Python snippets
snippets/<snippet-name>.py # 21 standalone Python snippets
examples/<name>/ # 53 runnable smoke-gated examples (+ gallery.json)
examples/gallery_framing.py # shared Layer 1 framing measurement (render path only)
scripts/build_gallery.py # generates docs/gallery/ (stdlib only)
Expand Down
17 changes: 11 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,23 @@ The **Blender Developer Tools** repository is at **v0.53.0**. It packages skills
## Repository Architecture

```
skills/<skill-name>/SKILL.md - AI workflow definitions, 13 total
rules/<rule-name>.mdc - Anti-pattern rules, 6 total
skills/<skill-name>/SKILL.md - AI workflow definitions, 14 total
rules/<rule-name>.mdc - Anti-pattern rules, 8 total
templates/<template-name>/ - Starter projects, 2 total
snippets/<snippet-name>.py - Standalone code patterns, 17 total
snippets/<snippet-name>.py - Standalone code patterns, 21 total
examples/<name>/ - Runnable smoke-gated examples, 53 total (+ gallery.json)
scripts/build_gallery.py - Regenerates docs/gallery/ from gallery.json (stdlib only)
scripts/site/ - Vendored landing-page build (Jinja2)
docs/gallery/ - Committed generated gallery pages + hero renders
VERSION - Source of truth for the repo version
```

## Skills (13)
## Skills (14)

| Skill | Purpose |
| --- | --- |
| addon-scaffolding | Extensions Platform manifest, file layout, register/unregister symmetry |
| ai-mesh-cleanup | Ordered cleanup for imported generated meshes: units, transform apply, origin, normals, budget, collider |
| operators | `bpy.types.Operator` lifecycle, `bl_idname`, redo, defensive context handling |
| ui-panels | `bpy.types.Panel` declarative `draw()`, layout primitives, conditional UI |
| custom-properties | `bpy.props` annotations, PropertyGroup, PointerProperty, storage tradeoffs |
Expand All @@ -46,7 +47,7 @@ VERSION - Source of truth for the repo version
| bl-info-migration | Three-step migration from legacy `bl_info` to Extensions Platform, dual-format pattern |
| vse-python | VSE timeline from Python: `.strips` vs `.sequences`, `new_effect` kwargs, 5.2 COLOR `width`/`height` bake |

## Rules (6)
## Rules (8)

| Rule | Scope | What it flags |
| --- | --- | --- |
Expand All @@ -56,6 +57,8 @@ VERSION - Source of truth for the repo version
| type-annotate-props-and-defend-context | `*.py` | `bpy.props` defined as assignments, unguarded `context.active_object` |
| prefer-temp-override-over-context-copy | `*.py` | `bpy.context.copy()` passed to operators (deprecated 4.x, removed 5.x) |
| use-foreach-set-for-bulk-data | `*.py` | Python loops over `mesh.vertices` setting bulk attributes one at a time |
| validate-imported-mesh-scale | `*.py` | glTF/FBX import then mesh work with no `transform_apply` and no unit-scale check |
| no-unapplied-modifiers-on-export | `*.py` | Export with live modifiers when the export does not request evaluated geometry |

## Templates (2)

Expand All @@ -75,14 +78,16 @@ VERSION - Source of truth for the repo version
- glTF export via `bpy.ops.export_scene.gltf`
- Explicit exit codes for CI integration

## Snippets (17)
## Snippets (21)

Small standalone `.py` files at `snippets/<name>.py`, each 5 to 50 lines.

v0.1.0: canonical object creation and deletion, depsgraph evaluated mesh, bmesh load-edit-free, temp_override context, foreach_set vertex bulk write, register_classes_factory, PointerProperty binding, cross-version property delete, and the `action_ensure_channelbag_for_slot` slotted-actions bridge.

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'`.

AI asset pipeline track: `decimate_to_budget.py`, `convex_hull_collider.py`, `lod_chain.py` (helper duplicated, not imported), `gltf_draco_export.py`.

## Examples (53)

Runnable scripts at `examples/<name>/`, each asserting a real API contract with
Expand Down
20 changes: 11 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
</p>

<p align="center">
<strong>13 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>53 examples</strong>
<strong>14 skills</strong> &nbsp;&bull;&nbsp; <strong>8 rules</strong> &nbsp;&bull;&nbsp; <strong>2 templates</strong> &nbsp;&bull;&nbsp; <strong>21 snippets</strong> &nbsp;&bull;&nbsp; <strong>53 examples</strong>
</p>

<p align="center">
Expand All @@ -36,16 +36,16 @@

## Overview

This repository ships **13 skills, 6 rules, 2 templates, 17 snippets, and 48 runnable examples** for Blender Python development targeting Blender 5.2 LTS (current stable) with Blender 4.5 LTS fallback support. Blender 5.1 is prior stable.
This repository ships **14 skills, 8 rules, 2 templates, 21 snippets, and 53 examples** for Blender Python development targeting Blender 5.2 LTS (current stable) with Blender 4.5 LTS fallback support. Blender 5.1 is prior stable.

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.

| Layer | Role |
| --- | --- |
| **Skills** | Guided workflows: scaffolding, operators, panels, properties, mesh and bmesh, headless batch, slotted actions, geometry nodes, procedural materials, depsgraph queries, drivers and handlers, `bl_info` migration, video sequencer |
| **Rules** | Guardrails for the most common AI mistakes: ops-in-loops, bmesh leaks, legacy `bl_info` only, prop assignments, deprecated context-copy override, per-element loops over bulk mesh data |
| **Skills** | Guided workflows: scaffolding, operators, panels, properties, mesh and bmesh, headless batch, slotted actions, geometry nodes, procedural materials, depsgraph queries, drivers and handlers, `bl_info` migration, video sequencer, imported-mesh cleanup |
| **Rules** | Guardrails for the most common AI mistakes: ops-in-loops, bmesh leaks, legacy `bl_info` only, prop assignments, deprecated context-copy override, per-element loops over bulk mesh data, import without scale check, export without evaluated geometry |
| **Templates** | A working Extensions Platform add-on starter and a headless batch script starter |
| **Snippets** | 17 small standalone Python files demonstrating canonical patterns |
| **Snippets** | 21 small standalone Python files demonstrating canonical patterns |

## Quick start

Expand Down Expand Up @@ -1008,22 +1008,24 @@ the duplicates, then glTF ships 24 tris / 48 positions / 8 unique.
## How content is organized

```
skills/<name>/SKILL.md - 13 skill files, YAML frontmatter, one canonical pattern each
rules/<name>.mdc - 6 rule files, anti-pattern + correction
skills/<name>/SKILL.md - 14 skill files, YAML frontmatter, one canonical pattern each
rules/<name>.mdc - 8 rule files, anti-pattern + correction
templates/<name>/ - 2 template directories (extension-addon-template, headless-batch-script-template)
snippets/<name>.py - 17 standalone Python snippets, 5 to 50 lines each
snippets/<name>.py - 21 standalone Python snippets, 5 to 50 lines each
```

## Using rules in Cursor

The `.mdc` files in `rules/` apply automatically when Cursor opens a Blender Python project, scoped by the `globs` in each rule's frontmatter. The six rules are:
The `.mdc` files in `rules/` apply automatically when Cursor opens a Blender Python project, scoped by the `globs` in each rule's frontmatter. The eight rules are:

- `prefer-data-over-ops-in-loops`: flags `bpy.ops.*` calls inside object iteration
- `always-free-bmesh`: flags `bmesh.new()` without paired `bm.free()` in `try`/`finally`
- `target-extensions-platform-format`: flags add-ons missing `blender_manifest.toml`
- `type-annotate-props-and-defend-context`: flags `bpy.props` assignment form and unguarded `context.active_object`
- `prefer-temp-override-over-context-copy`: flags `bpy.context.copy()` passed to operators (deprecated 4.x, removed 5.x)
- `use-foreach-set-for-bulk-data`: flags Python loops over `mesh.vertices` setting `co`, normals, or other per-element bulk data
- `validate-imported-mesh-scale`: flags glTF/FBX import then mesh work with no `transform_apply` and no unit-scale check
- `no-unapplied-modifiers-on-export`: flags export of objects with live modifiers when the export does not request evaluated geometry

Symlink or clone this repo, then point Cursor at it as a skills/rules source.

Expand Down
13 changes: 13 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ derives the actual version from conventional-commit types.
| 5.2 LTS targeting, GN modifier inputs | 12 | 6 | 2 | 17 | Shipped |
| VSE COLOR strip intrinsic size (undocumented 5.2) | 13 | 6 | 2 | 17 | Shipped |
| Modal operators, USD, mathutils | — | — | — | — | Upcoming |
| AI asset pipeline: post-generation cleanup | - | - | - | - | Upcoming |
| AI asset pipeline: engine export presets | - | - | - | - | Upcoming |
| AI asset pipeline: headless template | - | - | - | - | Upcoming |
| AI asset pipeline: live-session bridge (spike) | - | - | - | - | Upcoming |
| Stable | — | — | — | — | Upcoming |

## v0.1.0 - Foundation
Expand Down Expand Up @@ -87,6 +91,15 @@ The 7 new snippets:

Audit pass on v0.1.0 content: standards-version markers bumped from `1.9.1` to `1.9.4` across all skills, rules, AGENTS.md, CLAUDE.md, and ROADMAP.md. Verified the `bpy_extras.anim_utils.action_ensure_channelbag_for_slot` import path against the current Blender 5.1 API reference and removed the stale "verify before production" caveat in `slotted-actions-animation/SKILL.md`.

## AI asset pipeline track

Provider-agnostic GLB-in / engine-ready-out. This repo does not generate meshes.

- **Post-generation cleanup skills** (this phase starts the family; bake/UV/atlas follow on): import and unit-scale normalization, transform apply and origin, poly-budget decimate, LOD chain, collision mesh, high-to-low bake, UV transfer and atlas packing. Phase 1: `ai-mesh-cleanup`, four snippets, two rules.
- **Engine export presets.** Unity (Y-up), Godot, and Unreal (centimeter scale) glTF and FBX paths with Draco. One skill, one snippet set.
- **`ai-asset-pipeline-template/`.** Third template. Headless: GLB path in; LOD set, convex collider, engine-preset export; explicit CI exit codes. Pattern: `templates/headless-batch-script-template/`. Phase 2.
- **Live-session agent bridge.** Research spike, not a committed deliverable. MCP server or socket listener so an agent can execute against a running Blender instance instead of blind `--background` scripts. Built on `templates/extension-addon-template/`. Needs its own design pass.

## Candidate pool (next content)

Not committed; target list for the next content version. (v0.3.0 shipped the smoke-gated `examples/` track.)
Expand Down
89 changes: 89 additions & 0 deletions rules/no-unapplied-modifiers-on-export.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
description: Flag an export call on objects that still carry unapplied modifiers when the export arguments do not request evaluated geometry. The engine then receives the authored cage, not the modifier result.
alwaysApply: true
globs:
- "**/*.py"
standards-version: 1.10.0
---

# No unapplied modifiers on export

`obj.data` is the authored mesh. DECIMATE, subdivision, and geometry nodes
live in the depsgraph. An export that does not ask for evaluated geometry
writes the cage: the LOD modifier is dropped, the engine gets the dense
mesh, and the script still exits 0.

glTF: `export_apply=True` applies modifiers excluding armatures.
USD: `evaluation_mode='RENDER'` or `'VIEWPORT'`.
FBX: `use_mesh_modifiers=True`.

`export_apply` is not "apply object transforms". Apply object scale first.
See rule `validate-imported-mesh-scale`.

## What this rule flags

A `bpy.ops.export_scene.gltf`, `bpy.ops.export_scene.fbx`, or
`bpy.ops.wm.usd_export` call in a file that adds modifiers (`modifiers.new`)
and never requests evaluated geometry (`export_apply=True` or
`evaluation_mode=`), and never applies those modifiers before export.

```python
# WRONG: DECIMATE on the object, glTF without export_apply
mod = obj.modifiers.new("Lod", "DECIMATE")
mod.decimate_type = "COLLAPSE"
mod.ratio = 0.25
bpy.ops.export_scene.gltf(filepath=path, use_selection=True)
```

```python
# WRONG: USD viewport/render mode omitted; default BEST_MATCH writes the cage
obj.modifiers.new("ss", "SUBSURF").levels = 2
bpy.ops.wm.usd_export(filepath=path)
```

## The required pattern

```python
import bpy

mod = obj.modifiers.new("DecimateBudget", "DECIMATE")
mod.decimate_type = "COLLAPSE"
mod.ratio = 0.25

bpy.ops.export_scene.gltf(
filepath=path,
use_selection=True,
export_apply=True,
export_yup=True,
)
```

USD:

```python
bpy.ops.wm.usd_export(
filepath=path,
evaluation_mode="RENDER",
export_subdivision="TESSELLATE",
)
```

Alternatively apply the modifier before export with `temp_override` and
`bpy.ops.object.modifier_apply`. Either path is valid; omitting both is not.

## Why it matters

A live DECIMATE that never ships is the usual LOD bug: Blender's viewport
shows the reduced mesh, the glTF still has the source triangle count, and
the engine budget check fails in production. `examples/lod-decimate-chain/`
shows the modifier is non-destructive on `obj.data`; export must opt in to
the evaluated result.

## Related

- Skill `ai-mesh-cleanup`
- Skill `depsgraph-and-evaluated-data`
- Snippet `gltf_draco_export.py`
- Snippet `usd-export-evaluation-mode.py`
- Example `lod-decimate-chain`
- `bpy.ops.export_scene.gltf`: https://docs.blender.org/api/current/bpy.ops.export_scene.html#bpy.ops.export_scene.gltf
Loading
Loading