# pythonParser
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this module provides and who
consumes it, then delete both comment lines. -->
TODO: describe this module.
# blender
Blender MCP-compatible modeling surface for Zero. `execute_blender_code` runs Python/bpy as Zero world content; no external Blender is used. Tool names and argument names mirror Blender MCP.
The Python is interpreted in Luau (`modeling/pythonParser`, `modeling/pythonRuntime`) — a subset, not CPython. The `bpy`/`bmesh`/`mathutils` surface is `modeling/blenderSession`.
## Tools
| Tool | Notes |
|---|---|
| `get_addon_status(user_prompt?)` | Implementation and compatibility version. |
| `bpy_api_lookup(query, user_prompt?)` | Returns the schema from `api.json`. Unknown names return `NotImplementedError` plus related supported names. |
| `execute_blender_code(code, user_prompt?, session?)` | Runs Python, then evaluates modifiers and writes meshes/materials/entities under `modeling/sessions/<session>`. Sessions are isolated; default `default`. |
| `get_scene_info(session?)` | Objects, locations, material count. |
| `get_object_info(object_name, session?)` | Dimensions, mesh counts, evaluated triangles, modifier stack, topology audit. |
| `get_viewport_screenshot(max_size?, session?, view?, perspective?)` | Renders the session's viewport state, which scripts drive through bpy (`bpy.ops.view3d.view_axis/view_all/view_selected/view_orbit/view_persportho`, or `region_3d.view_rotation/view_location/view_distance/view_perspective`). `view` (FRONT…BOTTOM, ISO) and `perspective` (PERSP/ORTHO) override it for one capture. Rendered by Zero, not a Blender viewport. |
## Coordinates
Scripts use Blender coordinates (Z-up, right-handed). Zero entities receive `(x, y, z) -> (x, z, -y)`.
## Supported API
Authoritative list: `api.json` (or `bpy_api_lookup`). Summary:
- **Primitives:** cube, plane, cylinder, cone, uv_sphere, ico_sphere, torus, circle, grid — with `location`, `rotation`, `scale`, `enter_editmode`. New primitives are fully selected, as in Blender.
- **Object ops:** `mode_set` (OBJECT/EDIT), `select_all`, `delete`, `transform_apply`, `shade_smooth/flat`, `modifier_apply`, `modifier_move_up`.
- **Edit ops:** `select_mode`, `select_all`, `extrude_region_move`, `inset` (region and individual, `depth`), `subdivide` (`number_cuts` 1–100), `loopcut_slide`, `bevel` (edges and vertices, segments), `bridge_edge_loops`, `spin`, `remove_doubles`, `object.material_slot_assign`.
- **Transform:** `translate`, `resize`, `rotate`. In EDIT mode the pivot defaults to the selection median; `center_override` sets it explicitly.
- **Data:** `bpy.data.meshes/objects/materials.new`, `objects.remove`, `Mesh.from_pydata`, `mesh.vertices/edges/polygons/materials`, `collection.objects.link`, `view_layer.objects.active`.
- **bmesh:** `new`, `from_edit_mesh`, `update_edit_mesh`, `from_mesh`, `to_mesh`, `verts.new`, `faces.new`, `edges` (`verts`, `link_faces`, `is_boundary`, `is_manifold`, `calc_length`, `select`), `calc_volume`, `ops.recalc_face_normals`.
- **Modifiers:** BEVEL (ANGLE/NONE limit), BOOLEAN (DIFFERENCE/UNION/INTERSECT), ARRAY (with merge), SUBSURF, MIRROR (with merge), SOLIDIFY (offset −1/0/1), SCREW, WELD, DISPLACE (no texture).
- **Curves and text:** `bpy.data.curves.new(name, 'CURVE'|'FONT')`, POLY, BEZIER and NURBS splines, `bevel_depth`, `extrude`, 2D fills with holes, `object.text_add`, `curve.primitive_bezier_curve_add/circle_add`, `object.convert(target='MESH')`. Text uses Zero fonts via `curve.zero_font` (default Arimo).
- **UVs:** `bpy.ops.uv.smart_project`, `mesh.uv_layers` (one layer). UVs are carried into rendered meshes and bundles.
- **Images and shader nodes:** `bpy.data.images.new/load/remove`, `Image.pixels` (incl. `[:]` assignment), `colorspace_settings.name`; node tree with Principled BSDF, Image Texture and Normal Map nodes; texture links to Base Color, Roughness, Metallic, Emission Color and Normal become Zero PBR texture slots (roughness/metallic packed).
- **Zero shaders as nodes:** `nt.nodes.new('ShaderNodeZeroShader')`, set `node.shader` to any Zero shader; its properties become inputs.
- **Knife project, texture DISPLACE, Closest interpolation, float images, multiple UV layers** are implemented (see api.json).
- **Hierarchy:** `Object.parent`, `parent_set`, `parent_clear`, `matrix_world` and friends, `object.join`, `mathutils.Matrix`.
- **Viewport:** `bpy.ops.view3d.*` above, `RegionView3D`, `SpaceView3D.lens`, `context.screen.areas`, `temp_override`.
- **Compare to reference:** `blender.compare_to_reference(reference, session)` scores the session's FRONT elevation against a reference sheet (silhouette IoU, per-band scores, overlay image).
- **Output (preferred):** `bpy.ops.export_scene.zero_bundle(filepath='/zero/source/<folder>/<Name>.bundle')` writes a native Zero bundle — meshes and materials inside it, one Model per material slot, spawnable with the `Asset` component. A material bound with `mat.zero_material = '<material identity>'` keeps its custom Zero shader. Exports are frozen snapshots: an existing path is refused unless `overwrite=True`, and each bundle carries `provenance.json`.
- **Interchange (optional):** `bpy.ops.export_scene.gltf(..., export_format='GLB')` exists for leaving Zero; nothing inside Zero needs it.
- **Materials:** Principled BSDF `Base Color`, `Metallic`, `Roughness`, `Alpha`, `IOR`; multiple slots with per-face `material_index`; `material_slots`, `active_material(_index)`; `zero_material` binds an existing Zero material asset.
## Rigging and animation
Armatures (`bpy.data.armatures`, `object.armature_add`, `edit_bones`, `pose.bones`), bone parenting (`parent_set(type='BONE')`), keyframes (`keyframe_insert` on objects and pose bones), Blender 5 slotted actions and F-Curves, and `scene.frame_set`. Bone matrices and F-Curve evaluation match Blender 5.2.1.
Actions are written into the document's `output.bundle` as animation clips; a placed `.zblend` plays them (SkinnedModel + ClipPlayer). Vertex groups, the ARMATURE modifier and automatic weights deform meshes; constraints (incl. IK), drivers and NLA are implemented and baked into the clips.
## Documents: `.zblend` (live)
`session` names a `.zblend` document, Zero's `.blend`: a bare name maps to `modeling/models/<name>.zblend`, or pass `"<folder>/<Name>"`. The document holds `scene.json` (objects, meshes, curves, modifier stacks, node graphs, images, UV layers, view), `history.json`, `images/`, and `output.bundle/`.
Every `execute_blender_code` updates the document and its `output.bundle` in place (only changed objects are rebuilt). A `.zblend` is scene-instantiable: drag it into a scene or call `asset.resolve(id, "zblend"):instantiate(...)`. Placed copies follow later edits. It has a preview and is indexed for search. `export_scene.zero_bundle` is only for a frozen snapshot; nothing needs an export step to use the model inside Zero.
Session previews live in the `ModelingWorkspace` additive scene layer (`ZeroBlender.package/workspace/ModelingWorkspace.scene`, loaded persistent on first use), so modeling never touches or saves into the scene open in the editor.
Old `modeling/sessions/<name>/` folders migrate automatically on first open.
## Known differences and limits
- Unsupported calls raise `NotImplementedError: …` or `AttributeError: …` naming the missing API. Nothing is silently ignored except `calc_uvs`.
- `subdivide` requires all faces selected; `number_cuts > 1` requires quads.
- `inset` expects planar selections; `extrude_region_move` requires FACE selection.
- BOOLEAN operands must be closed manifold solids.
- ARRAY merge, bevel `limit_method` WEIGHT/VGROUP, and adding primitives in EDIT mode are not implemented.
- SUBSURF: `get_object_info` reports the dimensions of the smoothed surface; Blender with GPU subdivision may report the cage size.
- Python: one number type (so `24.0` prints as `24`); no classes, generators, decorators or `*args`.
## Parity suite
`modeling/references/parity/cases.json` holds scripts with numbers captured from native Blender 5.2.1. Run `execute { file = "/zero/source/modeling/references/parity/run.luau" }`; currently 76/76 pass (subsurf counted as a documented difference).
## Not implemented yet
Scene arrangement is implemented: empties, duplicates and linked duplicates, collections, collection instances, `Object.zero_instance` (place another `.zblend`), `origin_set`, the 3D cursor and snapping.
# curves
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this module provides and who
consumes it, then delete both comment lines. -->
TODO: describe this module.
# meshOps
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this module provides and who
consumes it, then delete both comment lines. -->
TODO: describe this module.
# xform
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this module provides and who
consumes it, then delete both comment lines. -->
TODO: describe this module.
# polygonTopology
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this module provides and who
consumes it, then delete both comment lines. -->
TODO: describe this module.
# ZeroBlender
Blender-style modeling inside Zero. Install this package and you get the whole surface: the `.zblend` document type, the tools that edit it, the Python/bpy runtime behind them, the layer previews live in, and the workflow that drives a modeling job to the end.
## Contents
| Path | What it is |
|---|---|
| `zblend.assetType/` | The `.zblend` document (Zero's `.blend`): editable `scene.json`, history, images, and a live `output.bundle`. Scene-instantiable, previewed, searchable. Its ref methods open and edit documents (below); `M.modules` exposes every module here to instances. |
| `blender.toolbox/` | Blender MCP-compatible tools: `execute_blender_code`, `get_scene_info`, `get_object_info`, `get_viewport_screenshot`, `bpy_api_lookup`, `get_addon_status`, `compare_to_reference`. `api.json` is the implemented bpy surface. |
| `modules/blenderSession` | The bpy/bmesh/mathutils session: objects, meshes, modifiers, materials, viewport, flush to the document and bundle. |
| `modules/pythonParser`, `pythonRuntime` | The Python subset the scripts run in (Luau-hosted, not CPython). |
| `modules/animation`, `rigging` | Armatures, pose, vertex groups, ARMATURE deform, auto weights, keyframes/actions/F-Curves, constraints, IK, drivers, NLA; clip baking. |
| `modules/sceneOps` | Empties, duplicates, collections, collection instances, `zero_instance`, `origin_set`, cursor, snapping. |
| `modules/curves`, `meshOps`, `polygonTopology`, `xform` | Curve/text meshing, edit-mode operators, topology and evaluation, matrix math. |
| `modules/referenceCompare` | Silhouette scoring against a reference image. |
| `modules/bpy`, `hardSurface`, `kestrel` | Older core geometry layer and hard-surface helpers used by the session. |
| `workspace/ModelingWorkspace.scene` | Additive layer holding document previews, on render layer `modelingWorkspace` (hidden from the editor camera, drawn by screenshots). |
| `model.workflow/` | Multi-pass modeling job: reference → model → critic/builder rounds → finish. |
| `modeling.agentSkill/` | The `modeling` skill that starts the workflow. |
## Usage
```luau
-- Make or open a document and edit it
local doc = asset.create("zblend", "Lantern", { folder = "models" })
doc:run([[import bpy
bpy.ops.mesh.primitive_cylinder_add(radius=0.1, depth=0.3)]])
doc:screenshot("ISO")
doc:sceneInfo()
doc:open() -- load as a live session, returns its name
-- Place it (live: placed copies follow edits and play its clips)
doc:instantiate(entity.find("Shelf"))
-- Frozen copy
doc:exportBundle("/zero/source/models/Lantern_v1.bundle")
```
Agents use the `blender` toolbox directly with `session="<folder>/<Name>"`, or start the `model` workflow for a full modeling job (`zero workflow start --name model --args '{"subject":"..."}'`).
Documents default to `modeling/models/<name>.zblend`; user data (models, references, reports) is not part of the package.
# referenceCompare
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this module provides and who
consumes it, then delete both comment lines. -->
TODO: describe this module.
# rigging
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this module provides and who
consumes it, then delete both comment lines. -->
TODO: describe this module.
# pythonRuntime
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this module provides and who
consumes it, then delete both comment lines. -->
TODO: describe this module.
# bpy_api_lookup
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this tool provides and who
consumes it, then delete both comment lines. -->
TODO: describe this tool.
# compare_to_reference
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this tool provides and who
consumes it, then delete both comment lines. -->
TODO: describe this tool.
# execute_blender_code
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this tool provides and who
consumes it, then delete both comment lines. -->
TODO: describe this tool.
# get_addon_status
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this tool provides and who
consumes it, then delete both comment lines. -->
TODO: describe this tool.
# get_object_info
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this tool provides and who
consumes it, then delete both comment lines. -->
TODO: describe this tool.
# get_scene_info
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this tool provides and who
consumes it, then delete both comment lines. -->
TODO: describe this tool.
# get_viewport_screenshot
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this tool provides and who
consumes it, then delete both comment lines. -->
TODO: describe this tool.
# replay_capture
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this tool provides and who
consumes it, then delete both comment lines. -->
TODO: describe this tool.
# shared
Cross-tool helpers for the blender toolbox. Every tool inside the
toolbox can pull these in with `require(".shared")`. Holds the small
set of helpers that more than one tool needs — typically the
`ok(value, formatted?)` / `fail(message, exitCode?)` constructors that
produce the canonical `blenderToolResult` shape, plus any toolbox-wide
parsers, validators, or shared state. Per-tool implementation stays
inside each `<x>.tool/init.luau`; this module is not a place to
forward logic from.
## Exports
- `M.ok(value: any?, formatted: string?) -> blenderToolResult` — success envelope (`exitCode = 0`, `ok = true`).
- `M.fail(message: string, exitCode: number?) -> blenderToolResult` — failure envelope (`exitCode` defaults to 1, `ok = false`).
Types:
- `blenderToolResult = { stdout: string, stderr: string, exitCode: number, ok: boolean, value: any? }`
- `stdout` — human-readable formatted output; empty when the op produces no text.
- `stderr` — error message on failure; empty on success.
- `exitCode` — POSIX-style exit code: `0` success, `1` generic failure, `2` bad arguments.
- `ok` — `true` iff `exitCode == 0`.
- `value` — structured return from the underlying op; `nil` for void ops.
## Usage
```luau
local shared = require(".shared")
function M.someTool(args)
if not args.id then
return shared.fail("missing id", 2)
end
return shared.ok({ id = args.id }, "created " .. args.id)
end
```
## Notes
- Pure constructors — no engine calls, no state. Safe to call from any
context.
- Tools that opt into this shape get a stable envelope every other tool
in the toolbox can rely on. Tools that do something custom can return
whatever shape they want — `blenderToolResult` is opt-in.
- Per-tool implementations still own their own `--!desc` / `--!arg` /
`--!return` / `--!example` annotations even when their result is
constructed through these helpers.
# sceneOps
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this module provides and who
consumes it, then delete both comment lines. -->
TODO: describe this module.
# animation
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this module provides and who
consumes it, then delete both comment lines. -->
TODO: describe this module.
# bpy
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this module provides and who
consumes it, then delete both comment lines. -->
TODO: describe this module.
# blenderSession
<!-- zero:scaffolded-readme -->
<!-- Replace the line below with what this module provides and who
consumes it, then delete both comment lines. -->
TODO: describe this module.