From bf8c26c2a38330a64afbcacd82ad266c6e91607e Mon Sep 17 00:00:00 2001
From: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
Date: Fri, 11 Sep 2026 19:45:18 -0400
Subject: [PATCH 1/3] feat: add engine-export-presets skill and
Unity/Godot/Unreal snippets
glTF axis is export_yup; FBX is axis_forward/axis_up plus centimeter scale.
The presets compose ai-mesh-cleanup and depsgraph-and-evaluated-data instead of restating them.
Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
Co-authored-by: Cursor
---
skills/engine-export-presets/SKILL.md | 139 ++++++++++++++++++++++++++
snippets/export_preset_godot.py | 47 +++++++++
snippets/export_preset_unity.py | 47 +++++++++
snippets/export_preset_unreal.py | 67 +++++++++++++
4 files changed, 300 insertions(+)
create mode 100644 skills/engine-export-presets/SKILL.md
create mode 100644 snippets/export_preset_godot.py
create mode 100644 snippets/export_preset_unity.py
create mode 100644 snippets/export_preset_unreal.py
diff --git a/skills/engine-export-presets/SKILL.md b/skills/engine-export-presets/SKILL.md
new file mode 100644
index 0000000..f900fb3
--- /dev/null
+++ b/skills/engine-export-presets/SKILL.md
@@ -0,0 +1,139 @@
+---
+name: engine-export-presets
+description: Unity, Godot, and Unreal glTF/FBX export presets. glTF uses export_yup; FBX uses axis_forward/axis_up plus centimeter scale. Targets 5.2 LTS with 4.5 LTS fallback.
+standards-version: 1.10.0
+---
+
+# Engine Export Presets
+
+## Trigger
+
+Use this skill when the user:
+
+- Needs a Unity, Godot, or Unreal export from Blender Python
+- Mentions Y-up, Z-up, centimeter scale, `export_yup`, `axis_forward`, or `axis_up`
+- Is about to pass FBX axis kwargs to `bpy.ops.export_scene.gltf`
+- Wants a headless preset the later `ai-asset-pipeline-template` can call
+
+This skill is the export layer. It composes `ai-mesh-cleanup` (apply transforms, units), `depsgraph-and-evaluated-data` (`export_apply` ships evaluated mesh), and the `unapplied-scale-gltf` witness (`export_apply` does not bake object scale). It does not generate meshes.
+
+## The core misunderstanding
+
+glTF and FBX do not share axis RNA. `bpy.ops.export_scene.gltf` has `export_yup` (boolean, "+Y Up"). It does not have `axis_forward` or `axis_up`. `bpy.ops.export_scene.fbx` has `axis_forward` and `axis_up` (axis enums) and `global_scale`. It does not have `export_yup`. Passing the other exporter's kwargs is a TypeError or a silent no-op depending on how the call is built.
+
+That split is the contract. Verified on current RNA:
+
+- glTF: https://docs.blender.org/api/current/bpy.ops.export_scene.html#bpy.ops.export_scene.gltf (`export_yup`, no axis enums)
+- FBX: https://docs.blender.org/api/current/bpy.ops.export_scene.html#bpy.ops.export_scene.fbx (`axis_forward`, `axis_up`, `global_scale`)
+
+## Shared prelude
+
+Before any preset: meters in the scene (`scale_length == 1.0`), identity object scale via `transform_apply` through `temp_override`, `export_apply=True` / `use_mesh_modifiers=True` so modifiers ship. See `ai-mesh-cleanup` and `unapplied-scale-gltf`.
+
+```python
+def apply_selected_mesh_transforms():
+ for obj in list(bpy.context.selected_objects):
+ if obj.type != "MESH":
+ continue
+ with bpy.context.temp_override(
+ object=obj, active_object=obj, selected_objects=[obj]
+ ):
+ bpy.ops.object.transform_apply(
+ location=False, rotation=True, scale=True
+ )
+```
+
+`use_selection=True` on every preset. Draco is opt-in on glTF; do not copy `gltf_draco_export.py` wholesale.
+
+## Unity (Y-up, meters, glTF)
+
+```python
+bpy.ops.export_scene.gltf(
+ filepath=path,
+ use_selection=True,
+ export_yup=True,
+ export_apply=True,
+ export_draco_mesh_compression_enable=draco,
+ export_animations=False,
+)
+```
+
+`export_yup=True` bakes `(x, y, z) -> (x, z, -y)` into POSITION with no node rotation. Witness: `examples/gltf-export-roundtrip/` and `examples/export-preset-axis/`.
+
+Snippet: `snippets/export_preset_unity.py`.
+
+## Godot (Z-up glTF, meters)
+
+```python
+bpy.ops.export_scene.gltf(
+ filepath=path,
+ use_selection=True,
+ export_yup=False,
+ export_apply=True,
+ export_draco_mesh_compression_enable=draco,
+ export_animations=False,
+)
+```
+
+`export_yup=False` writes Blender Z-up POSITION. This preset is the Z-up interop path. It is not the Unity kwargs; if both used `export_yup=True` the files would match and the axis contract would be untestable.
+
+Snippet: `snippets/export_preset_godot.py`.
+
+## Unreal (centimeters)
+
+glTF has no `global_scale`. Bake 100x (1 m -> 100 cm) onto the selected meshes, apply, then `export_yup=True`. That mutates the objects; copy first if the source must stay in meters.
+
+FBX keeps the meter mesh and scales on the way out:
+
+```python
+bpy.ops.export_scene.fbx(
+ filepath=path,
+ use_selection=True,
+ axis_forward="-Z",
+ axis_up="Y",
+ global_scale=100.0,
+ apply_unit_scale=False,
+ use_mesh_modifiers=True,
+ bake_anim=False,
+)
+```
+
+Do not pass `export_yup` to FBX. Do not pass `axis_forward` to glTF.
+
+Snippet: `snippets/export_preset_unreal.py`.
+
+## Common AI mistakes
+
+1. **`export_scene.gltf(..., axis_forward="-Z", axis_up="Y")`.** Those names are FBX. Rule `use-correct-axis-rna-per-exporter`.
+2. **`export_scene.fbx(..., export_yup=True)`.** Same rule, other direction.
+3. **Skipping `transform_apply`.** `export_apply` is modifiers, not object scale. `examples/unapplied-scale-gltf/`.
+4. **Unity and Godot as the same kwargs.** They differ on `export_yup`. `examples/export-preset-axis/` asserts the re-imported orientations diverge.
+5. **Unreal glTF without the 100x bake.** glTF has no `global_scale`.
+
+## Version correctness
+
+Probed on 4.5 LTS, 5.1, and 5.2: `export_yup` on glTF and `axis_forward` / `axis_up` / `global_scale` on FBX are present on all three. No version branch for the axis kwargs. Guard by requiring those names in operator RNA so a future rename fails loudly, as `examples/gltf-export-roundtrip/` does.
+
+`export_format` defaults differ by call site; pass the filepath suffix (`.glb` / `.gltf` / `.fbx`) and let the operator infer, or set `export_format` explicitly on glTF.
+
+## Related
+
+- Skill `ai-mesh-cleanup`
+- Skill `depsgraph-and-evaluated-data`
+- Rule `use-correct-axis-rna-per-exporter`
+- Rule `no-unapplied-modifiers-on-export`
+- Rule `validate-imported-mesh-scale`
+- Snippet `snippets/export_preset_unity.py`
+- Snippet `snippets/export_preset_godot.py`
+- Snippet `snippets/export_preset_unreal.py`
+- Snippet `snippets/gltf_draco_export.py`
+- Example `export-preset-axis`
+- Example `gltf-export-roundtrip`
+- Example `unapplied-scale-gltf`
+
+## References
+
+- `bpy.ops.export_scene.gltf`: https://docs.blender.org/api/current/bpy.ops.export_scene.html#bpy.ops.export_scene.gltf
+- `bpy.ops.export_scene.fbx`: https://docs.blender.org/api/current/bpy.ops.export_scene.html#bpy.ops.export_scene.fbx
+- glTF 5.1: https://docs.blender.org/api/5.1/bpy.ops.export_scene.html#bpy.ops.export_scene.gltf
+- FBX 5.1: https://docs.blender.org/api/5.1/bpy.ops.export_scene.html#bpy.ops.export_scene.fbx
diff --git a/snippets/export_preset_godot.py b/snippets/export_preset_godot.py
new file mode 100644
index 0000000..ae13f4c
--- /dev/null
+++ b/snippets/export_preset_godot.py
@@ -0,0 +1,47 @@
+# Godot glTF preset: Z-up (Blender-native), meter scale, selected objects only.
+# export_yup=False writes raw Z-up POSITION. glTF RNA has no axis_forward /
+# axis_up. Draco is opt-in; see snippets/gltf_draco_export.py for the
+# compression-only helper this does not duplicate.
+#
+# Assumption: scene units are meters (scale_length == 1.0). This preset is
+# the Z-up interop path; it is intentionally not the Unity Y-up kwargs.
+#
+# Reference:
+# https://docs.blender.org/api/current/bpy.ops.export_scene.html#bpy.ops.export_scene.gltf
+
+import tempfile
+
+import bpy
+
+
+def apply_selected_mesh_transforms():
+ for obj in list(bpy.context.selected_objects):
+ if obj.type != "MESH":
+ continue
+ with bpy.context.temp_override(
+ object=obj, active_object=obj, selected_objects=[obj]
+ ):
+ bpy.ops.object.transform_apply(
+ location=False, rotation=True, scale=True
+ )
+
+
+def export_preset_godot(filepath, selected_only=True, draco=False):
+ apply_selected_mesh_transforms()
+ bpy.ops.export_scene.gltf(
+ filepath=filepath,
+ use_selection=selected_only,
+ export_yup=False,
+ export_apply=True,
+ export_draco_mesh_compression_enable=draco,
+ export_animations=False,
+ )
+
+
+if __name__ == "__main__":
+ obj = bpy.context.active_object
+ if obj is not None and obj.type == "MESH":
+ obj.select_set(True)
+ path = tempfile.NamedTemporaryFile(suffix=".glb", delete=False).name
+ export_preset_godot(path)
+ print(f"wrote {path}")
diff --git a/snippets/export_preset_unity.py b/snippets/export_preset_unity.py
new file mode 100644
index 0000000..bc7756c
--- /dev/null
+++ b/snippets/export_preset_unity.py
@@ -0,0 +1,47 @@
+# Unity glTF preset: Y-up, meter scale, selected objects only.
+# Apply object rotation and scale before export so they do not land on the
+# glTF node. Axis is export_yup=True. glTF RNA has no axis_forward / axis_up.
+# Draco is opt-in; see snippets/gltf_draco_export.py for the compression-only
+# helper this does not duplicate.
+#
+# Assumption: scene units are meters (scale_length == 1.0).
+#
+# Reference:
+# https://docs.blender.org/api/current/bpy.ops.export_scene.html#bpy.ops.export_scene.gltf
+
+import tempfile
+
+import bpy
+
+
+def apply_selected_mesh_transforms():
+ for obj in list(bpy.context.selected_objects):
+ if obj.type != "MESH":
+ continue
+ with bpy.context.temp_override(
+ object=obj, active_object=obj, selected_objects=[obj]
+ ):
+ bpy.ops.object.transform_apply(
+ location=False, rotation=True, scale=True
+ )
+
+
+def export_preset_unity(filepath, selected_only=True, draco=False):
+ apply_selected_mesh_transforms()
+ bpy.ops.export_scene.gltf(
+ filepath=filepath,
+ use_selection=selected_only,
+ export_yup=True,
+ export_apply=True,
+ export_draco_mesh_compression_enable=draco,
+ export_animations=False,
+ )
+
+
+if __name__ == "__main__":
+ obj = bpy.context.active_object
+ if obj is not None and obj.type == "MESH":
+ obj.select_set(True)
+ path = tempfile.NamedTemporaryFile(suffix=".glb", delete=False).name
+ export_preset_unity(path)
+ print(f"wrote {path}")
diff --git a/snippets/export_preset_unreal.py b/snippets/export_preset_unreal.py
new file mode 100644
index 0000000..be0fc88
--- /dev/null
+++ b/snippets/export_preset_unreal.py
@@ -0,0 +1,67 @@
+# Unreal presets: centimeter scale. glTF has no global_scale and no
+# axis_forward / axis_up; bake 100x then export_yup=True. FBX uses
+# global_scale=100.0 plus axis_forward='-Z' and axis_up='Y'. That RNA
+# split is the contract. glTF bake mutates selected mesh objects.
+# Draco is glTF-only and opt-in; see snippets/gltf_draco_export.py.
+#
+# Assumption: scene units are meters before the 100x bake / FBX scale.
+#
+# Reference:
+# https://docs.blender.org/api/current/bpy.ops.export_scene.html#bpy.ops.export_scene.gltf
+# https://docs.blender.org/api/current/bpy.ops.export_scene.html#bpy.ops.export_scene.fbx
+
+import tempfile
+
+import bpy
+
+
+def apply_selected_mesh_transforms():
+ for obj in list(bpy.context.selected_objects):
+ if obj.type != "MESH":
+ continue
+ with bpy.context.temp_override(
+ object=obj, active_object=obj, selected_objects=[obj]
+ ):
+ bpy.ops.object.transform_apply(
+ location=False, rotation=True, scale=True
+ )
+
+
+def export_preset_unreal_gltf(filepath, selected_only=True, draco=False):
+ apply_selected_mesh_transforms()
+ for obj in list(bpy.context.selected_objects):
+ if obj.type != "MESH":
+ continue
+ obj.scale = (obj.scale[0] * 100.0, obj.scale[1] * 100.0, obj.scale[2] * 100.0)
+ apply_selected_mesh_transforms()
+ bpy.ops.export_scene.gltf(
+ filepath=filepath,
+ use_selection=selected_only,
+ export_yup=True,
+ export_apply=True,
+ export_draco_mesh_compression_enable=draco,
+ export_animations=False,
+ )
+
+
+def export_preset_unreal_fbx(filepath, selected_only=True):
+ apply_selected_mesh_transforms()
+ bpy.ops.export_scene.fbx(
+ filepath=filepath,
+ use_selection=selected_only,
+ axis_forward="-Z",
+ axis_up="Y",
+ global_scale=100.0,
+ apply_unit_scale=False,
+ use_mesh_modifiers=True,
+ bake_anim=False,
+ )
+
+
+if __name__ == "__main__":
+ obj = bpy.context.active_object
+ if obj is not None and obj.type == "MESH":
+ obj.select_set(True)
+ path = tempfile.NamedTemporaryFile(suffix=".fbx", delete=False).name
+ export_preset_unreal_fbx(path)
+ print(f"wrote {path}")
From 4cdaa6910c8716372872bf2d4ec60dc60fcb973d Mon Sep 17 00:00:00 2001
From: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
Date: Fri, 11 Sep 2026 19:45:32 -0400
Subject: [PATCH 2/3] feat: flag mixed glTF/FBX axis RNA in the import/export
harness
export_scene.gltf does not take axis_forward/axis_up; export_scene.fbx does not take export_yup.
The check is per-call so a file that correctly uses both exporters still passes.
Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
Co-authored-by: Cursor
---
rules/use-correct-axis-rna-per-exporter.mdc | 83 +++++++++++++++++++++
tests/check_import_export_rules.py | 47 +++++++++++-
2 files changed, 128 insertions(+), 2 deletions(-)
create mode 100644 rules/use-correct-axis-rna-per-exporter.mdc
diff --git a/rules/use-correct-axis-rna-per-exporter.mdc b/rules/use-correct-axis-rna-per-exporter.mdc
new file mode 100644
index 0000000..3082e3d
--- /dev/null
+++ b/rules/use-correct-axis-rna-per-exporter.mdc
@@ -0,0 +1,83 @@
+---
+description: Flag export_scene.gltf calls that pass FBX axis_forward or axis_up, and export_scene.fbx calls that pass glTF export_yup. The two exporters do not share axis RNA.
+alwaysApply: true
+globs:
+ - "**/*.py"
+standards-version: 1.10.0
+---
+
+# Use correct axis RNA per exporter
+
+`bpy.ops.export_scene.gltf` exposes axis as `export_yup` (bool).
+`bpy.ops.export_scene.fbx` exposes `axis_forward` and `axis_up` (axis
+enums) plus `global_scale`. Mixing them is the usual engine-preset bug:
+the glTF call raises or ignores FBX names, and the FBX call never sees
+`export_yup`.
+
+Verified: https://docs.blender.org/api/current/bpy.ops.export_scene.html#bpy.ops.export_scene.gltf
+and https://docs.blender.org/api/current/bpy.ops.export_scene.html#bpy.ops.export_scene.fbx
+
+## What this rule flags
+
+A `bpy.ops.export_scene.gltf(...)` call whose arguments include
+`axis_forward` or `axis_up`. A `bpy.ops.export_scene.fbx(...)` call whose
+arguments include `export_yup`. Whole-file scans are not enough: a file
+that correctly calls both exporters (Unreal glTF plus Unreal FBX) must
+still pass.
+
+```python
+# WRONG: FBX axis names on the glTF operator
+bpy.ops.export_scene.gltf(
+ filepath=path,
+ use_selection=True,
+ axis_forward="-Z",
+ axis_up="Y",
+)
+```
+
+```python
+# WRONG: glTF Y-up flag on the FBX operator
+bpy.ops.export_scene.fbx(
+ filepath=path,
+ use_selection=True,
+ export_yup=True,
+)
+```
+
+## The required pattern
+
+```python
+# glTF: Unity Y-up
+bpy.ops.export_scene.gltf(
+ filepath=path,
+ use_selection=True,
+ export_yup=True,
+ export_apply=True,
+)
+
+# FBX: Unreal centimeters
+bpy.ops.export_scene.fbx(
+ filepath=path,
+ use_selection=True,
+ axis_forward="-Z",
+ axis_up="Y",
+ global_scale=100.0,
+ use_mesh_modifiers=True,
+)
+```
+
+## Why it matters
+
+Unity vs Godot vs Unreal is not one export with different comments. glTF
+Y-up bakes `(x, y, z) -> (x, z, -y)` into POSITION. Z-up glTF writes the
+Blender coords. FBX uses a different axis pair and can scale to
+centimeters without mutating the mesh. Getting the RNA names wrong ships
+the default axis and looks like "the engine importer is broken".
+
+## Related
+
+- Skill `engine-export-presets`
+- Example `export-preset-axis`
+- Example `gltf-export-roundtrip`
+- Snippet `export_preset_unity.py`
+- Snippet `export_preset_unreal.py`
diff --git a/tests/check_import_export_rules.py b/tests/check_import_export_rules.py
index 0f8382c..8beea6b 100644
--- a/tests/check_import_export_rules.py
+++ b/tests/check_import_export_rules.py
@@ -1,5 +1,5 @@
-"""Static checks for validate-imported-mesh-scale and
-no-unapplied-modifiers-on-export.
+"""Static checks for validate-imported-mesh-scale,
+no-unapplied-modifiers-on-export, and use-correct-axis-rna-per-exporter.
Scans snippets/ and templates/**/*.py. examples/ is excluded because several
examples are intentional pathology witnesses (unapplied-scale-gltf).
@@ -14,6 +14,7 @@
REQUIRED_RULES = (
"rules/validate-imported-mesh-scale.mdc",
"rules/no-unapplied-modifiers-on-export.mdc",
+ "rules/use-correct-axis-rna-per-exporter.mdc",
)
IMPORT_RE = re.compile(r"bpy\.ops\.import_scene\.(gltf|fbx)\s*\(")
@@ -27,6 +28,11 @@
)
MODIFIER_NEW_RE = re.compile(r"modifiers\.new")
MODIFIER_APPLY_RE = re.compile(r"modifier_apply")
+GLTF_CALL_RE = re.compile(r"bpy\.ops\.export_scene\.gltf\s*\(")
+FBX_CALL_RE = re.compile(r"bpy\.ops\.export_scene\.fbx\s*\(")
+AXIS_FORWARD_RE = re.compile(r"\baxis_forward\b")
+AXIS_UP_RE = re.compile(r"\baxis_up\b")
+EXPORT_YUP_RE = re.compile(r"\bexport_yup\b")
def scan_paths(extra):
@@ -40,6 +46,31 @@ def scan_paths(extra):
return paths
+def _call_bodies(text, opener_re):
+ """Extract argument text of each matching call, paren-matched.
+
+ Whole-file scans false-positive a file that correctly calls both
+ exporters (Unreal glTF plus Unreal FBX). Per-call bodies keep those
+ legal.
+ """
+ bodies = []
+ for match in opener_re.finditer(text):
+ i = match.end()
+ depth = 1
+ start = i
+ while i < len(text) and depth:
+ char = text[i]
+ if char == "(":
+ depth += 1
+ elif char == ")":
+ depth -= 1
+ i += 1
+ if depth != 0:
+ continue
+ bodies.append(text[start : i - 1])
+ return bodies
+
+
def check_text(rel, text):
errors = []
if IMPORT_RE.search(text) and MESH_WORK_RE.search(text):
@@ -55,6 +86,18 @@ def check_text(rel, text):
f"{rel}: export with modifiers.new but no export_apply=True, "
"evaluation_mode, or modifier_apply"
)
+ for body in _call_bodies(text, GLTF_CALL_RE):
+ if AXIS_FORWARD_RE.search(body) or AXIS_UP_RE.search(body):
+ errors.append(
+ f"{rel}: export_scene.gltf call passes axis_forward or "
+ "axis_up (FBX RNA; glTF uses export_yup)"
+ )
+ for body in _call_bodies(text, FBX_CALL_RE):
+ if EXPORT_YUP_RE.search(body):
+ errors.append(
+ f"{rel}: export_scene.fbx call passes export_yup "
+ "(glTF RNA; FBX uses axis_forward / axis_up)"
+ )
return errors
From 31a09b5ed23a9ebc86da20adc645eae0e7b89bc7 Mon Sep 17 00:00:00 2001
From: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
Date: Fri, 11 Sep 2026 19:45:42 -0400
Subject: [PATCH 3/3] feat: witness Unity vs Godot glTF axis conversion on
reimport
The same beacon under export_yup True vs False produces different disk POSITION and different reimported orientation.
Inventory, gallery, catalog, and the engine-export-presets roadmap row ship with the example.
Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
Co-authored-by: Cursor
---
.cursor-plugin/plugin.json | 8 +-
AGENTS.md | 10 +-
CLAUDE.md | 20 +-
README.md | 37 +-
ROADMAP.md | 12 +-
.../assets/export-preset-axis-hero.webp | Bin 0 -> 8272 bytes
.../export-preset-axis-contact-sheet.webp | Bin 0 -> 35954 bytes
docs/gallery/export-preset-axis/index.html | 838 ++++++++++++++++++
docs/gallery/index.html | 13 +-
examples/export-preset-axis/README.md | 49 +
.../export-preset-axis/export_preset_axis.py | 514 +++++++++++
examples/export-preset-axis/preview.webp | Bin 0 -> 7780 bytes
examples/gallery.json | 11 +
tests/smoke/catalog.json | 1 +
14 files changed, 1481 insertions(+), 32 deletions(-)
create mode 100644 docs/gallery/assets/export-preset-axis-hero.webp
create mode 100644 docs/gallery/contact-sheets/export-preset-axis-contact-sheet.webp
create mode 100644 docs/gallery/export-preset-axis/index.html
create mode 100644 examples/export-preset-axis/README.md
create mode 100644 examples/export-preset-axis/export_preset_axis.py
create mode 100644 examples/export-preset-axis/preview.webp
diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json
index 7e53567..dc04f9d 100644
--- a/.cursor-plugin/plugin.json
+++ b/.cursor-plugin/plugin.json
@@ -16,6 +16,7 @@
"skills": [
"skills/addon-scaffolding/SKILL.md",
"skills/ai-mesh-cleanup/SKILL.md",
+ "skills/engine-export-presets/SKILL.md",
"skills/operators/SKILL.md",
"skills/ui-panels/SKILL.md",
"skills/custom-properties/SKILL.md",
@@ -37,7 +38,8 @@
"rules/prefer-temp-override-over-context-copy.mdc",
"rules/use-foreach-set-for-bulk-data.mdc",
"rules/validate-imported-mesh-scale.mdc",
- "rules/no-unapplied-modifiers-on-export.mdc"
+ "rules/no-unapplied-modifiers-on-export.mdc",
+ "rules/use-correct-axis-rna-per-exporter.mdc"
],
"snippets": [
"snippets/action-ensure-channelbag-for-slot.py",
@@ -50,6 +52,9 @@
"snippets/decimate_to_budget.py",
"snippets/depsgraph-evaluated-mesh.py",
"snippets/driver-with-custom-function.py",
+ "snippets/export_preset_godot.py",
+ "snippets/export_preset_unity.py",
+ "snippets/export_preset_unreal.py",
"snippets/foreach-get-vertices.py",
"snippets/foreach-set-vertices.py",
"snippets/gltf_draco_export.py",
@@ -83,6 +88,7 @@
"examples/depsgraph-export",
"examples/driver-wave",
"examples/exit-pre-sidecar",
+ "examples/export-preset-axis",
"examples/gltf-export-roundtrip",
"examples/gltf-skin-roundtrip",
"examples/gn-bundle-roundtrip",
diff --git a/AGENTS.md b/AGENTS.md
index 6394fbd..61e2776 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -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 14 skills, 8 rules, 2 templates, 21 snippets, and 53
+The content base is 15 skills, 9 rules, 2 templates, 24 snippets, and 54
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
@@ -31,11 +31,11 @@ in `docs/VISUAL-STYLE.md`; the canonical run prompt is
```
Blender-Developer-Tools/
- skills//SKILL.md # 14 skill files
- rules/.mdc # 8 rule files
+ skills//SKILL.md # 15 skill files
+ rules/.mdc # 9 rule files
templates// # 2 starter templates
- snippets/.py # 21 standalone Python snippets
- examples// # 53 runnable smoke-gated examples (+ gallery.json)
+ snippets/.py # 24 standalone Python snippets
+ examples// # 54 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)
scripts/site/ # vendored landing-page build (build_site.py + template)
diff --git a/CLAUDE.md b/CLAUDE.md
index f7b1ff8..f4f4b24 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -17,23 +17,24 @@ The **Blender Developer Tools** repository is at **v0.54.0**. It packages skills
## Repository Architecture
```
-skills//SKILL.md - AI workflow definitions, 14 total
-rules/.mdc - Anti-pattern rules, 8 total
+skills//SKILL.md - AI workflow definitions, 15 total
+rules/.mdc - Anti-pattern rules, 9 total
templates// - Starter projects, 2 total
-snippets/.py - Standalone code patterns, 21 total
-examples// - Runnable smoke-gated examples, 53 total (+ gallery.json)
+snippets/.py - Standalone code patterns, 24 total
+examples// - Runnable smoke-gated examples, 54 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 (14)
+## Skills (15)
| 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 |
+| engine-export-presets | Unity Y-up, Godot Z-up, and Unreal centimeter glTF/FBX presets; glTF uses export_yup, FBX uses axis_forward/axis_up |
| 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 |
@@ -47,7 +48,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 (8)
+## Rules (9)
| Rule | Scope | What it flags |
| --- | --- | --- |
@@ -59,6 +60,7 @@ VERSION - Source of truth for the repo version
| 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 |
+| use-correct-axis-rna-per-exporter | `*.py` | `export_scene.gltf` with FBX `axis_forward`/`axis_up`, or `export_scene.fbx` with glTF `export_yup` |
## Templates (2)
@@ -78,7 +80,7 @@ VERSION - Source of truth for the repo version
- glTF export via `bpy.ops.export_scene.gltf`
- Explicit exit codes for CI integration
-## Snippets (21)
+## Snippets (24)
Small standalone `.py` files at `snippets/.py`, each 5 to 50 lines.
@@ -86,9 +88,9 @@ v0.1.0: canonical object creation and deletion, depsgraph evaluated mesh, bmesh
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`.
+AI asset pipeline track: `decimate_to_budget.py`, `convex_hull_collider.py`, `lod_chain.py` (helper duplicated, not imported), `gltf_draco_export.py`, `export_preset_unity.py`, `export_preset_godot.py`, `export_preset_unreal.py`.
-## Examples (53)
+## Examples (54)
Runnable scripts at `examples//`, each asserting a real API contract with
deterministic checks (exit non-zero on failure) and optionally rendering a still via
diff --git a/README.md b/README.md
index c4126c0..3ed577c 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@
- 14 skills • 8 rules • 2 templates • 21 snippets • 53 examples
+ 15 skills • 9 rules • 2 templates • 24 snippets • 54 examples
@@ -36,16 +36,16 @@
## Overview
-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.
+This repository ships **15 skills, 9 rules, 2 templates, 24 snippets, and 54 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, 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 |
+| **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, engine export presets |
+| **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, mixed glTF/FBX axis RNA |
| **Templates** | A working Extensions Platform add-on starter and a headless batch script starter |
-| **Snippets** | 21 small standalone Python files demonstrating canonical patterns |
+| **Snippets** | 24 small standalone Python files demonstrating canonical patterns |
## Quick start
@@ -627,7 +627,7 @@ portable path is `radius`.
-Game asset pipeline — 20 examples
+Game asset pipeline — 21 examples
@@ -646,6 +646,22 @@ loop), V-flipped UVs, and per-triangle material bindings — all against the
depsgraph-evaluated mesh. The exporter/importer RNA signatures are probed
byte-identical on 4.5.11 and 5.1.2 and guarded against future renames.
+
+
+
+
+
+
+
+
+### [export-preset-axis](examples/export-preset-axis/)
+
+The same beacon mesh under the Unity (`export_yup=True`) and Godot
+(`export_yup=False`) glTF presets. Re-importing each file proves the axis
+conversion: Unity stands, Godot lies along `-Y`. `--same-axis` exports both
+Y-up and the differ check exits 9. Neighbor of
+[`gltf-export-roundtrip`](examples/gltf-export-roundtrip/).
+
@@ -1008,15 +1024,15 @@ the duplicates, then glTF ships 24 tris / 48 positions / 8 unique.
## How content is organized
```
-skills//SKILL.md - 14 skill files, YAML frontmatter, one canonical pattern each
-rules/.mdc - 8 rule files, anti-pattern + correction
+skills//SKILL.md - 15 skill files, YAML frontmatter, one canonical pattern each
+rules/.mdc - 9 rule files, anti-pattern + correction
templates// - 2 template directories (extension-addon-template, headless-batch-script-template)
-snippets/.py - 21 standalone Python snippets, 5 to 50 lines each
+snippets/.py - 24 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 eight 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 nine 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`
@@ -1026,6 +1042,7 @@ The `.mdc` files in `rules/` apply automatically when Cursor opens a Blender Pyt
- `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
+- `use-correct-axis-rna-per-exporter`: flags `export_scene.gltf` calls that pass FBX `axis_forward` / `axis_up`, and `export_scene.fbx` calls that pass glTF `export_yup`
Symlink or clone this repo, then point Cursor at it as a skills/rules source.
diff --git a/ROADMAP.md b/ROADMAP.md
index 03fb9bd..d59d2de 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -18,8 +18,8 @@ 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: post-generation cleanup | 14 | 8 | 2 | 21 | Shipped (v0.54.0) |
+| AI asset pipeline: engine export presets | 15 | 9 | 2 | 24 | Shipped |
| AI asset pipeline: headless template | - | - | - | - | Upcoming |
| AI asset pipeline: live-session bridge (spike) | - | - | - | - | Upcoming |
| Stable | — | — | — | — | Upcoming |
@@ -95,10 +95,10 @@ Audit pass on v0.1.0 content: standards-version markers bumped from `1.9.1` to `
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.
+- **Post-generation cleanup skills.** Import and unit-scale normalization, transform apply and origin, poly-budget decimate, LOD chain, collision mesh. Phase 1 shipped in v0.54.0 as `ai-mesh-cleanup`, four snippets, two rules. Bake/UV/atlas follow on.
+- **Engine export presets.** **Delivered.** Unity (Y-up glTF), Godot (Z-up glTF, meters), Unreal (centimeter glTF bake and FBX `global_scale`). Skill `engine-export-presets`, three snippets, rule `use-correct-axis-rna-per-exporter`, witness `examples/export-preset-axis/`. Draco remains opt-in via `gltf_draco_export.py`.
+- **`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 3. Unpinned.
+- **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. Unpinned.
## Candidate pool (next content)
diff --git a/docs/gallery/assets/export-preset-axis-hero.webp b/docs/gallery/assets/export-preset-axis-hero.webp
new file mode 100644
index 0000000000000000000000000000000000000000..842f6f556e74a4e4aedc3a4d8055388d66c8f879
GIT binary patch
literal 8272
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 ^l1yFLH~BYjS~xXy|a
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
literal 0
HcmV?d00001
diff --git a/docs/gallery/export-preset-axis/index.html b/docs/gallery/export-preset-axis/index.html
new file mode 100644
index 0000000..ac5bd06
--- /dev/null
+++ b/docs/gallery/export-preset-axis/index.html
@@ -0,0 +1,838 @@
+
+
+
+
+
+ export-preset-axis — Examples — Blender Developer Tools
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Skip to content
+
+
+
+
+