blender-cli 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- blender_cli-0.1.0/PKG-INFO +281 -0
- blender_cli-0.1.0/README.md +251 -0
- blender_cli-0.1.0/pyproject.toml +57 -0
- blender_cli-0.1.0/src/blender_cli/__init__.py +151 -0
- blender_cli-0.1.0/src/blender_cli/__init__.pyi +55 -0
- blender_cli-0.1.0/src/blender_cli/alignment/__init__.py +37 -0
- blender_cli-0.1.0/src/blender_cli/alignment/_fal.py +74 -0
- blender_cli-0.1.0/src/blender_cli/alignment/depth.py +250 -0
- blender_cli-0.1.0/src/blender_cli/alignment/generation.py +248 -0
- blender_cli-0.1.0/src/blender_cli/alignment/integration.py +80 -0
- blender_cli-0.1.0/src/blender_cli/alignment/pipeline.py +200 -0
- blender_cli-0.1.0/src/blender_cli/alignment/pose.py +1453 -0
- blender_cli-0.1.0/src/blender_cli/alignment/types.py +165 -0
- blender_cli-0.1.0/src/blender_cli/alignment/viz.py +62 -0
- blender_cli-0.1.0/src/blender_cli/animation/__init__.py +15 -0
- blender_cli-0.1.0/src/blender_cli/animation/codegen.py +134 -0
- blender_cli-0.1.0/src/blender_cli/animation/keyframes.py +211 -0
- blender_cli-0.1.0/src/blender_cli/assets/__init__.py +14 -0
- blender_cli-0.1.0/src/blender_cli/assets/image.py +36 -0
- blender_cli-0.1.0/src/blender_cli/assets/material.py +578 -0
- blender_cli-0.1.0/src/blender_cli/assets/prefab.py +72 -0
- blender_cli-0.1.0/src/blender_cli/assets/registry.py +189 -0
- blender_cli-0.1.0/src/blender_cli/blenvy.py +214 -0
- blender_cli-0.1.0/src/blender_cli/blenvy_registry.py +237 -0
- blender_cli-0.1.0/src/blender_cli/build/__init__.py +18 -0
- blender_cli-0.1.0/src/blender_cli/build/build_types.py +9 -0
- blender_cli-0.1.0/src/blender_cli/build/context.py +179 -0
- blender_cli-0.1.0/src/blender_cli/build/generation_step.py +97 -0
- blender_cli-0.1.0/src/blender_cli/build/runner.py +74 -0
- blender_cli-0.1.0/src/blender_cli/cli/__init__.py +5 -0
- blender_cli-0.1.0/src/blender_cli/cli/__main__.py +5 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/__init__.py +65 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/align.py +524 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/anchor_cmd.py +47 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/animation_cmd.py +187 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/assets.py +94 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/blenvy_cmd.py +228 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/camera_cmd.py +120 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/candidates.py +289 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/inspect.py +124 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/instance_cmd.py +72 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/manifest.py +90 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/material_cmd.py +65 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/measure.py +58 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/modifier_cmd.py +179 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/object_cmd.py +80 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/op.py +2353 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/project.py +343 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/raycast.py +85 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/render.py +503 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/render_settings_cmd.py +114 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/repl_cmd.py +25 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/run.py +34 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/select.py +22 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/session_cmd.py +90 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/stats.py +20 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/terrain_cmd.py +116 -0
- blender_cli-0.1.0/src/blender_cli/cli/commands/world_cmd.py +91 -0
- blender_cli-0.1.0/src/blender_cli/cli/common.py +339 -0
- blender_cli-0.1.0/src/blender_cli/cli/main.py +74 -0
- blender_cli-0.1.0/src/blender_cli/cli/repl.py +121 -0
- blender_cli-0.1.0/src/blender_cli/constants.py +6 -0
- blender_cli-0.1.0/src/blender_cli/core/__init__.py +39 -0
- blender_cli-0.1.0/src/blender_cli/core/diagnostics.py +9 -0
- blender_cli-0.1.0/src/blender_cli/core/metadata.py +70 -0
- blender_cli-0.1.0/src/blender_cli/geometry/__init__.py +34 -0
- blender_cli-0.1.0/src/blender_cli/geometry/_erosion.py +114 -0
- blender_cli-0.1.0/src/blender_cli/geometry/field2d.py +306 -0
- blender_cli-0.1.0/src/blender_cli/geometry/heightfield.py +586 -0
- blender_cli-0.1.0/src/blender_cli/geometry/mask.py +232 -0
- blender_cli-0.1.0/src/blender_cli/geometry/pointset.py +603 -0
- blender_cli-0.1.0/src/blender_cli/geometry/spline.py +279 -0
- blender_cli-0.1.0/src/blender_cli/geometry/spline_ops.py +320 -0
- blender_cli-0.1.0/src/blender_cli/modifiers/__init__.py +15 -0
- blender_cli-0.1.0/src/blender_cli/modifiers/codegen.py +94 -0
- blender_cli-0.1.0/src/blender_cli/modifiers/modifier.py +185 -0
- blender_cli-0.1.0/src/blender_cli/modifiers/registry.py +346 -0
- blender_cli-0.1.0/src/blender_cli/project/__init__.py +6 -0
- blender_cli-0.1.0/src/blender_cli/project/project_file.py +2207 -0
- blender_cli-0.1.0/src/blender_cli/project/session.py +124 -0
- blender_cli-0.1.0/src/blender_cli/py.typed +0 -0
- blender_cli-0.1.0/src/blender_cli/render/__init__.py +46 -0
- blender_cli-0.1.0/src/blender_cli/render/camera.py +397 -0
- blender_cli-0.1.0/src/blender_cli/render/camera_path.py +321 -0
- blender_cli-0.1.0/src/blender_cli/render/context.py +1897 -0
- blender_cli-0.1.0/src/blender_cli/render/settings.py +286 -0
- blender_cli-0.1.0/src/blender_cli/render/world.py +170 -0
- blender_cli-0.1.0/src/blender_cli/scene/__init__.py +27 -0
- blender_cli-0.1.0/src/blender_cli/scene/anchor.py +65 -0
- blender_cli-0.1.0/src/blender_cli/scene/entity.py +542 -0
- blender_cli-0.1.0/src/blender_cli/scene/instances.py +524 -0
- blender_cli-0.1.0/src/blender_cli/scene/primitives.py +574 -0
- blender_cli-0.1.0/src/blender_cli/scene/scene.py +1613 -0
- blender_cli-0.1.0/src/blender_cli/scene/selection.py +273 -0
- blender_cli-0.1.0/src/blender_cli/snap/__init__.py +26 -0
- blender_cli-0.1.0/src/blender_cli/snap/axis.py +80 -0
- blender_cli-0.1.0/src/blender_cli/snap/objects.py +628 -0
- blender_cli-0.1.0/src/blender_cli/snap/results.py +190 -0
- blender_cli-0.1.0/src/blender_cli/types.py +292 -0
- blender_cli-0.1.0/src/blender_cli/utils/__init__.py +29 -0
- blender_cli-0.1.0/src/blender_cli/utils/placement.py +511 -0
- blender_cli-0.1.0/src/blender_cli/utils/spline_strip.py +233 -0
- blender_cli-0.1.0/src/blender_cli/utils/strings.py +30 -0
- blender_cli-0.1.0/src/blender_cli/utils/sweep.py +154 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: blender-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Agent-friendly procedural map generation SDK built on Blender's Python API
|
|
5
|
+
Keywords: blender,procedural,map,3d,terrain,generation
|
|
6
|
+
Author: Julien Blanchon
|
|
7
|
+
Author-email: Julien Blanchon <julien@blanchon.cc>
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Topic :: Multimedia :: Graphics :: 3D Modeling
|
|
14
|
+
Classifier: Topic :: Games/Entertainment
|
|
15
|
+
Requires-Dist: bpy>=5.1.0
|
|
16
|
+
Requires-Dist: cma>=3.3
|
|
17
|
+
Requires-Dist: click>=8.0
|
|
18
|
+
Requires-Dist: fal-client>=0.7
|
|
19
|
+
Requires-Dist: matplotlib>=3.8
|
|
20
|
+
Requires-Dist: numpy>=1.24
|
|
21
|
+
Requires-Dist: opencv-python-headless>=4.8
|
|
22
|
+
Requires-Dist: pillow>=10.0
|
|
23
|
+
Requires-Dist: prompt-toolkit>=3.0
|
|
24
|
+
Requires-Dist: pyrender>=0.1.45
|
|
25
|
+
Requires-Dist: rtree>=1.2
|
|
26
|
+
Requires-Dist: scipy>=1.11
|
|
27
|
+
Requires-Dist: trimesh>=4.0
|
|
28
|
+
Requires-Python: >=3.13
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# blender-cli
|
|
32
|
+
|
|
33
|
+
Agent-friendly procedural map generation SDK built on Blender's Python API.
|
|
34
|
+
|
|
35
|
+
`blender-cli` provides a **Python SDK** and a **CLI** for building 3D scenes declaratively through a `project.json` file — procedural terrain, PBR materials, object placement with snap/raycast, GPU-instanced scatter, cameras, lights, and Bevy/Blenvy ECS component embedding — then exports to GLB.
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
Requires Python 3.13+ and [uv](https://docs.astral.sh/uv/).
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Install from source
|
|
43
|
+
uv pip install .
|
|
44
|
+
|
|
45
|
+
# Or run directly with uvx (no install needed)
|
|
46
|
+
uvx --from . blender-cli
|
|
47
|
+
|
|
48
|
+
# From a git repo
|
|
49
|
+
uvx --from git+https://github.com/julien-blanchon/blender_cli blender-cli
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Quick start
|
|
53
|
+
|
|
54
|
+
### CLI
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Create a project
|
|
58
|
+
blender-cli project new --name "Alpine" --profile hd1080p -o project.json
|
|
59
|
+
|
|
60
|
+
# Add materials
|
|
61
|
+
blender-cli material add --project project.json grass --pbr-folder ./textures/grass/ --tiling 4 4
|
|
62
|
+
|
|
63
|
+
# Build terrain
|
|
64
|
+
blender-cli terrain init --project project.json --width 512 --height 512 --seed 42
|
|
65
|
+
blender-cli terrain op --project project.json noise type=fbm amp=15 freq=0.008 octaves=6
|
|
66
|
+
blender-cli terrain op --project project.json smooth radius=3 iters=1
|
|
67
|
+
blender-cli terrain material --project project.json grass
|
|
68
|
+
|
|
69
|
+
# Place objects (snapped to terrain)
|
|
70
|
+
blender-cli project add-object --project project.json Wall \
|
|
71
|
+
--primitive box --primitive-params '{"size":[10,0.3,3]}' \
|
|
72
|
+
--at 50 50 0 --material rock --snap -Z --tags building
|
|
73
|
+
|
|
74
|
+
# Add camera and light
|
|
75
|
+
blender-cli project add-camera --project project.json Main --at 0 -50 30 --lens 35
|
|
76
|
+
blender-cli project add-light --project project.json Sun --type SUN --energy 5.0
|
|
77
|
+
|
|
78
|
+
# Export to GLB
|
|
79
|
+
blender-cli project export --project project.json --out scene.glb
|
|
80
|
+
|
|
81
|
+
# Render a preview
|
|
82
|
+
blender-cli render still --glb scene.glb --project project.json --preset iso --out preview.png
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Python SDK
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from blender_cli import ProjectFile
|
|
89
|
+
|
|
90
|
+
pf = ProjectFile.new("Alpine", profile="hd1080p")
|
|
91
|
+
|
|
92
|
+
# Materials
|
|
93
|
+
pf.add_material("grass", pbr_folder="./textures/grass/", tiling=[4, 4])
|
|
94
|
+
pf.add_material("rock", pbr_folder="./textures/rock/", tiling=[2, 2])
|
|
95
|
+
|
|
96
|
+
# Terrain
|
|
97
|
+
pf.set_terrain(512, 512, seed=42)
|
|
98
|
+
pf.terrain_op("noise", type="fbm", amp=12.0, freq=0.008, octaves=6)
|
|
99
|
+
pf.terrain_op("smooth", radius=3, iters=1)
|
|
100
|
+
pf.terrain_op("erode", type="hydraulic", iterations=40)
|
|
101
|
+
pf.set_terrain_material("grass")
|
|
102
|
+
|
|
103
|
+
# Anchors (named spatial references)
|
|
104
|
+
pf.add_anchor("village", [256, 256, 0], annotation="center")
|
|
105
|
+
|
|
106
|
+
# Objects (snapped to terrain at export)
|
|
107
|
+
pf.add_object("Cottage", asset_path="./assets/cottage.glb",
|
|
108
|
+
location=pf.anchor_pos("village"), tags=["building"], snap=True)
|
|
109
|
+
|
|
110
|
+
# Camera, light
|
|
111
|
+
pf.add_camera("Main", location=[0, -50, 30], look_at=[256, 256, 0], lens=35)
|
|
112
|
+
pf.add_light("Sun", "SUN", energy=5.0, rotation=[0.5, 0.0, 0.0])
|
|
113
|
+
|
|
114
|
+
# Export
|
|
115
|
+
pf.save("project.json")
|
|
116
|
+
pf.export_glb("scene.glb")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Architecture
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
project.json ──[export_glb]──► scene.glb
|
|
123
|
+
▲ │
|
|
124
|
+
│ │
|
|
125
|
+
└────────[import_glb]──────────┘
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Everything goes through **`project.json`** as the single source of truth. Edit it via the Python SDK (`ProjectFile`) or the CLI (`blender-cli`), then export to GLB. You can also import an existing GLB back into a project file.
|
|
129
|
+
|
|
130
|
+
## Features
|
|
131
|
+
|
|
132
|
+
### Terrain
|
|
133
|
+
|
|
134
|
+
Procedural heightfield terrain built from a chain of operations:
|
|
135
|
+
|
|
136
|
+
| Operation | Effect |
|
|
137
|
+
| ---------------- | ------------------------- |
|
|
138
|
+
| `flat` | Start flat at elevation z |
|
|
139
|
+
| `noise` | Add fbm/ridged noise |
|
|
140
|
+
| `smooth` | Gaussian blur |
|
|
141
|
+
| `terrace` | Plateau effect |
|
|
142
|
+
| `clamp` | Clamp height range |
|
|
143
|
+
| `stamp` | Local circular/ring edit |
|
|
144
|
+
| `erode` | Hydraulic/thermal erosion |
|
|
145
|
+
| `radial_falloff` | Island/crater falloff |
|
|
146
|
+
| `remap_curve` | Height remapping curve |
|
|
147
|
+
|
|
148
|
+
### Materials
|
|
149
|
+
|
|
150
|
+
PBR texture folders (auto-detects diffuse, normal, roughness, AO, displacement) or flat colors with metallic/roughness control.
|
|
151
|
+
|
|
152
|
+
### Objects & Primitives
|
|
153
|
+
|
|
154
|
+
Primitives: `box`, `plane`, `cylinder`, `sphere`, `cone`, `torus`. Also supports prefab GLB assets and empties.
|
|
155
|
+
|
|
156
|
+
**Snap system** — raycast objects onto geometry at export time:
|
|
157
|
+
|
|
158
|
+
| Axis | Use case | Policy | Effect |
|
|
159
|
+
| ---- | ------------- | --------- | ------------------------ |
|
|
160
|
+
| `-Z` | Floor/terrain | `FIRST` | Sit on first surface hit |
|
|
161
|
+
| `+Z` | Ceiling mount | `ORIENT` | Align to surface normal |
|
|
162
|
+
| `+X` | Wall mount | `HIGHEST` | Highest hit Z |
|
|
163
|
+
|
|
164
|
+
Filter snapping with `target_tags` and `exclude_tags`.
|
|
165
|
+
|
|
166
|
+
### Anchors
|
|
167
|
+
|
|
168
|
+
Named spatial reference points. Place objects, cameras, and lights relative to anchors instead of hardcoded coordinates:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
pf.add_anchor("gate", [100, 100, 0])
|
|
172
|
+
pf.add_object("Tower", location=pf.anchor_pos("gate", [5, 0, 0]), snap=True)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Instances
|
|
176
|
+
|
|
177
|
+
GPU-instanced scatter groups for vegetation, rocks, and props with per-point rotation and scale.
|
|
178
|
+
|
|
179
|
+
### Cameras
|
|
180
|
+
|
|
181
|
+
Static cameras or animated paths (orbit, dolly, flyover). Ghost cameras are excluded from GLB export and used only for debug renders.
|
|
182
|
+
|
|
183
|
+
### Lights
|
|
184
|
+
|
|
185
|
+
SUN, POINT, SPOT, AREA light types. Ghost lights for debug previews.
|
|
186
|
+
|
|
187
|
+
### Bevy / Blenvy Integration
|
|
188
|
+
|
|
189
|
+
Embed Bevy ECS components directly in the GLB. The SDK validates component names against a Bevy `registry.json`:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
blender-cli blenvy set-registry --project project.json assets/registry.json
|
|
193
|
+
blender-cli blenvy add-component --project project.json Ground RigidBody Static
|
|
194
|
+
blender-cli blenvy validate --registry assets/registry.json --project project.json
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Render & Debug
|
|
198
|
+
|
|
199
|
+
Render stills from named cameras or presets (`top`, `iso`). Supports:
|
|
200
|
+
|
|
201
|
+
- Tag-based show/hide (`--hide-tag ceiling` for dollhouse views)
|
|
202
|
+
- Wireframe mode
|
|
203
|
+
- Highlight + ghost mode
|
|
204
|
+
- Decomposition render (flat colors per object/tag)
|
|
205
|
+
- Multi-pass (beauty, depth, normal)
|
|
206
|
+
|
|
207
|
+
### Session
|
|
208
|
+
|
|
209
|
+
Undo/redo with named snapshots.
|
|
210
|
+
|
|
211
|
+
### REPL
|
|
212
|
+
|
|
213
|
+
Interactive prompt for exploring scenes:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
blender-cli repl --project project.json
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Render profiles
|
|
220
|
+
|
|
221
|
+
| Profile | Engine | Resolution | Samples | Use case |
|
|
222
|
+
| ------------------- | ------ | ---------- | ------- | ------------------- |
|
|
223
|
+
| `default` | EEVEE | 1920x1080 | 64 | General development |
|
|
224
|
+
| `preview` | EEVEE | 960x540 | 16 | Quick iteration |
|
|
225
|
+
| `hd1080p` | CYCLES | 1920x1080 | 128 | High quality |
|
|
226
|
+
| `4k` | CYCLES | 3840x2160 | 256 | Ultra quality |
|
|
227
|
+
| `product_render` | CYCLES | 2048x2048 | 512 | Transparent bg |
|
|
228
|
+
| `animation_preview` | EEVEE | 1280x720 | 16 | Animation testing |
|
|
229
|
+
|
|
230
|
+
## CLI reference
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
blender-cli [--project FILE] [--json] COMMAND
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
| Command | Description |
|
|
237
|
+
| ------------ | ----------------------------------------- |
|
|
238
|
+
| `project` | Create, info, export, import projects |
|
|
239
|
+
| `terrain` | Init, add ops, set mesh/material |
|
|
240
|
+
| `material` | Add/list PBR or flat-color materials |
|
|
241
|
+
| `anchor` | Add/list/remove spatial anchors |
|
|
242
|
+
| `object` | Add/remove/manage objects |
|
|
243
|
+
| `instance` | Add/list/remove GPU-instanced groups |
|
|
244
|
+
| `camera` | Add cameras and animated paths |
|
|
245
|
+
| `render` | Render stills, focus shots, decomposition |
|
|
246
|
+
| `world` | Set background color or HDRI |
|
|
247
|
+
| `blenvy` | Bevy component validation and embedding |
|
|
248
|
+
| `session` | Undo/redo/history |
|
|
249
|
+
| `select` | Query objects with DSL |
|
|
250
|
+
| `inspect` | Inspect GLB scene contents |
|
|
251
|
+
| `stats` | Scene statistics |
|
|
252
|
+
| `repl` | Interactive REPL |
|
|
253
|
+
| `align` | Image-to-3D alignment pipeline |
|
|
254
|
+
| `animation` | Keyframe animation |
|
|
255
|
+
| `modifier` | Geometry modifiers |
|
|
256
|
+
| `op` | Low-level Blender operations |
|
|
257
|
+
| `run` | Execute build scripts |
|
|
258
|
+
| `measure` | Measure distances and dimensions |
|
|
259
|
+
| `raycast` | Raycast queries |
|
|
260
|
+
| `candidates` | Placement candidate generation |
|
|
261
|
+
| `assets` | Asset management |
|
|
262
|
+
| `manifest` | Scene manifest operations |
|
|
263
|
+
|
|
264
|
+
Pass `--json` for machine-readable JSON output on all commands.
|
|
265
|
+
|
|
266
|
+
## Development
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
# Install dev dependencies
|
|
270
|
+
uv sync --group dev
|
|
271
|
+
|
|
272
|
+
# Run tests
|
|
273
|
+
uv run pytest
|
|
274
|
+
|
|
275
|
+
# Type check
|
|
276
|
+
uv run pyright
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## License
|
|
280
|
+
|
|
281
|
+
MIT
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# blender-cli
|
|
2
|
+
|
|
3
|
+
Agent-friendly procedural map generation SDK built on Blender's Python API.
|
|
4
|
+
|
|
5
|
+
`blender-cli` provides a **Python SDK** and a **CLI** for building 3D scenes declaratively through a `project.json` file — procedural terrain, PBR materials, object placement with snap/raycast, GPU-instanced scatter, cameras, lights, and Bevy/Blenvy ECS component embedding — then exports to GLB.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Requires Python 3.13+ and [uv](https://docs.astral.sh/uv/).
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Install from source
|
|
13
|
+
uv pip install .
|
|
14
|
+
|
|
15
|
+
# Or run directly with uvx (no install needed)
|
|
16
|
+
uvx --from . blender-cli
|
|
17
|
+
|
|
18
|
+
# From a git repo
|
|
19
|
+
uvx --from git+https://github.com/julien-blanchon/blender_cli blender-cli
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick start
|
|
23
|
+
|
|
24
|
+
### CLI
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Create a project
|
|
28
|
+
blender-cli project new --name "Alpine" --profile hd1080p -o project.json
|
|
29
|
+
|
|
30
|
+
# Add materials
|
|
31
|
+
blender-cli material add --project project.json grass --pbr-folder ./textures/grass/ --tiling 4 4
|
|
32
|
+
|
|
33
|
+
# Build terrain
|
|
34
|
+
blender-cli terrain init --project project.json --width 512 --height 512 --seed 42
|
|
35
|
+
blender-cli terrain op --project project.json noise type=fbm amp=15 freq=0.008 octaves=6
|
|
36
|
+
blender-cli terrain op --project project.json smooth radius=3 iters=1
|
|
37
|
+
blender-cli terrain material --project project.json grass
|
|
38
|
+
|
|
39
|
+
# Place objects (snapped to terrain)
|
|
40
|
+
blender-cli project add-object --project project.json Wall \
|
|
41
|
+
--primitive box --primitive-params '{"size":[10,0.3,3]}' \
|
|
42
|
+
--at 50 50 0 --material rock --snap -Z --tags building
|
|
43
|
+
|
|
44
|
+
# Add camera and light
|
|
45
|
+
blender-cli project add-camera --project project.json Main --at 0 -50 30 --lens 35
|
|
46
|
+
blender-cli project add-light --project project.json Sun --type SUN --energy 5.0
|
|
47
|
+
|
|
48
|
+
# Export to GLB
|
|
49
|
+
blender-cli project export --project project.json --out scene.glb
|
|
50
|
+
|
|
51
|
+
# Render a preview
|
|
52
|
+
blender-cli render still --glb scene.glb --project project.json --preset iso --out preview.png
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Python SDK
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from blender_cli import ProjectFile
|
|
59
|
+
|
|
60
|
+
pf = ProjectFile.new("Alpine", profile="hd1080p")
|
|
61
|
+
|
|
62
|
+
# Materials
|
|
63
|
+
pf.add_material("grass", pbr_folder="./textures/grass/", tiling=[4, 4])
|
|
64
|
+
pf.add_material("rock", pbr_folder="./textures/rock/", tiling=[2, 2])
|
|
65
|
+
|
|
66
|
+
# Terrain
|
|
67
|
+
pf.set_terrain(512, 512, seed=42)
|
|
68
|
+
pf.terrain_op("noise", type="fbm", amp=12.0, freq=0.008, octaves=6)
|
|
69
|
+
pf.terrain_op("smooth", radius=3, iters=1)
|
|
70
|
+
pf.terrain_op("erode", type="hydraulic", iterations=40)
|
|
71
|
+
pf.set_terrain_material("grass")
|
|
72
|
+
|
|
73
|
+
# Anchors (named spatial references)
|
|
74
|
+
pf.add_anchor("village", [256, 256, 0], annotation="center")
|
|
75
|
+
|
|
76
|
+
# Objects (snapped to terrain at export)
|
|
77
|
+
pf.add_object("Cottage", asset_path="./assets/cottage.glb",
|
|
78
|
+
location=pf.anchor_pos("village"), tags=["building"], snap=True)
|
|
79
|
+
|
|
80
|
+
# Camera, light
|
|
81
|
+
pf.add_camera("Main", location=[0, -50, 30], look_at=[256, 256, 0], lens=35)
|
|
82
|
+
pf.add_light("Sun", "SUN", energy=5.0, rotation=[0.5, 0.0, 0.0])
|
|
83
|
+
|
|
84
|
+
# Export
|
|
85
|
+
pf.save("project.json")
|
|
86
|
+
pf.export_glb("scene.glb")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Architecture
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
project.json ──[export_glb]──► scene.glb
|
|
93
|
+
▲ │
|
|
94
|
+
│ │
|
|
95
|
+
└────────[import_glb]──────────┘
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Everything goes through **`project.json`** as the single source of truth. Edit it via the Python SDK (`ProjectFile`) or the CLI (`blender-cli`), then export to GLB. You can also import an existing GLB back into a project file.
|
|
99
|
+
|
|
100
|
+
## Features
|
|
101
|
+
|
|
102
|
+
### Terrain
|
|
103
|
+
|
|
104
|
+
Procedural heightfield terrain built from a chain of operations:
|
|
105
|
+
|
|
106
|
+
| Operation | Effect |
|
|
107
|
+
| ---------------- | ------------------------- |
|
|
108
|
+
| `flat` | Start flat at elevation z |
|
|
109
|
+
| `noise` | Add fbm/ridged noise |
|
|
110
|
+
| `smooth` | Gaussian blur |
|
|
111
|
+
| `terrace` | Plateau effect |
|
|
112
|
+
| `clamp` | Clamp height range |
|
|
113
|
+
| `stamp` | Local circular/ring edit |
|
|
114
|
+
| `erode` | Hydraulic/thermal erosion |
|
|
115
|
+
| `radial_falloff` | Island/crater falloff |
|
|
116
|
+
| `remap_curve` | Height remapping curve |
|
|
117
|
+
|
|
118
|
+
### Materials
|
|
119
|
+
|
|
120
|
+
PBR texture folders (auto-detects diffuse, normal, roughness, AO, displacement) or flat colors with metallic/roughness control.
|
|
121
|
+
|
|
122
|
+
### Objects & Primitives
|
|
123
|
+
|
|
124
|
+
Primitives: `box`, `plane`, `cylinder`, `sphere`, `cone`, `torus`. Also supports prefab GLB assets and empties.
|
|
125
|
+
|
|
126
|
+
**Snap system** — raycast objects onto geometry at export time:
|
|
127
|
+
|
|
128
|
+
| Axis | Use case | Policy | Effect |
|
|
129
|
+
| ---- | ------------- | --------- | ------------------------ |
|
|
130
|
+
| `-Z` | Floor/terrain | `FIRST` | Sit on first surface hit |
|
|
131
|
+
| `+Z` | Ceiling mount | `ORIENT` | Align to surface normal |
|
|
132
|
+
| `+X` | Wall mount | `HIGHEST` | Highest hit Z |
|
|
133
|
+
|
|
134
|
+
Filter snapping with `target_tags` and `exclude_tags`.
|
|
135
|
+
|
|
136
|
+
### Anchors
|
|
137
|
+
|
|
138
|
+
Named spatial reference points. Place objects, cameras, and lights relative to anchors instead of hardcoded coordinates:
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
pf.add_anchor("gate", [100, 100, 0])
|
|
142
|
+
pf.add_object("Tower", location=pf.anchor_pos("gate", [5, 0, 0]), snap=True)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Instances
|
|
146
|
+
|
|
147
|
+
GPU-instanced scatter groups for vegetation, rocks, and props with per-point rotation and scale.
|
|
148
|
+
|
|
149
|
+
### Cameras
|
|
150
|
+
|
|
151
|
+
Static cameras or animated paths (orbit, dolly, flyover). Ghost cameras are excluded from GLB export and used only for debug renders.
|
|
152
|
+
|
|
153
|
+
### Lights
|
|
154
|
+
|
|
155
|
+
SUN, POINT, SPOT, AREA light types. Ghost lights for debug previews.
|
|
156
|
+
|
|
157
|
+
### Bevy / Blenvy Integration
|
|
158
|
+
|
|
159
|
+
Embed Bevy ECS components directly in the GLB. The SDK validates component names against a Bevy `registry.json`:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
blender-cli blenvy set-registry --project project.json assets/registry.json
|
|
163
|
+
blender-cli blenvy add-component --project project.json Ground RigidBody Static
|
|
164
|
+
blender-cli blenvy validate --registry assets/registry.json --project project.json
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Render & Debug
|
|
168
|
+
|
|
169
|
+
Render stills from named cameras or presets (`top`, `iso`). Supports:
|
|
170
|
+
|
|
171
|
+
- Tag-based show/hide (`--hide-tag ceiling` for dollhouse views)
|
|
172
|
+
- Wireframe mode
|
|
173
|
+
- Highlight + ghost mode
|
|
174
|
+
- Decomposition render (flat colors per object/tag)
|
|
175
|
+
- Multi-pass (beauty, depth, normal)
|
|
176
|
+
|
|
177
|
+
### Session
|
|
178
|
+
|
|
179
|
+
Undo/redo with named snapshots.
|
|
180
|
+
|
|
181
|
+
### REPL
|
|
182
|
+
|
|
183
|
+
Interactive prompt for exploring scenes:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
blender-cli repl --project project.json
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Render profiles
|
|
190
|
+
|
|
191
|
+
| Profile | Engine | Resolution | Samples | Use case |
|
|
192
|
+
| ------------------- | ------ | ---------- | ------- | ------------------- |
|
|
193
|
+
| `default` | EEVEE | 1920x1080 | 64 | General development |
|
|
194
|
+
| `preview` | EEVEE | 960x540 | 16 | Quick iteration |
|
|
195
|
+
| `hd1080p` | CYCLES | 1920x1080 | 128 | High quality |
|
|
196
|
+
| `4k` | CYCLES | 3840x2160 | 256 | Ultra quality |
|
|
197
|
+
| `product_render` | CYCLES | 2048x2048 | 512 | Transparent bg |
|
|
198
|
+
| `animation_preview` | EEVEE | 1280x720 | 16 | Animation testing |
|
|
199
|
+
|
|
200
|
+
## CLI reference
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
blender-cli [--project FILE] [--json] COMMAND
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
| Command | Description |
|
|
207
|
+
| ------------ | ----------------------------------------- |
|
|
208
|
+
| `project` | Create, info, export, import projects |
|
|
209
|
+
| `terrain` | Init, add ops, set mesh/material |
|
|
210
|
+
| `material` | Add/list PBR or flat-color materials |
|
|
211
|
+
| `anchor` | Add/list/remove spatial anchors |
|
|
212
|
+
| `object` | Add/remove/manage objects |
|
|
213
|
+
| `instance` | Add/list/remove GPU-instanced groups |
|
|
214
|
+
| `camera` | Add cameras and animated paths |
|
|
215
|
+
| `render` | Render stills, focus shots, decomposition |
|
|
216
|
+
| `world` | Set background color or HDRI |
|
|
217
|
+
| `blenvy` | Bevy component validation and embedding |
|
|
218
|
+
| `session` | Undo/redo/history |
|
|
219
|
+
| `select` | Query objects with DSL |
|
|
220
|
+
| `inspect` | Inspect GLB scene contents |
|
|
221
|
+
| `stats` | Scene statistics |
|
|
222
|
+
| `repl` | Interactive REPL |
|
|
223
|
+
| `align` | Image-to-3D alignment pipeline |
|
|
224
|
+
| `animation` | Keyframe animation |
|
|
225
|
+
| `modifier` | Geometry modifiers |
|
|
226
|
+
| `op` | Low-level Blender operations |
|
|
227
|
+
| `run` | Execute build scripts |
|
|
228
|
+
| `measure` | Measure distances and dimensions |
|
|
229
|
+
| `raycast` | Raycast queries |
|
|
230
|
+
| `candidates` | Placement candidate generation |
|
|
231
|
+
| `assets` | Asset management |
|
|
232
|
+
| `manifest` | Scene manifest operations |
|
|
233
|
+
|
|
234
|
+
Pass `--json` for machine-readable JSON output on all commands.
|
|
235
|
+
|
|
236
|
+
## Development
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# Install dev dependencies
|
|
240
|
+
uv sync --group dev
|
|
241
|
+
|
|
242
|
+
# Run tests
|
|
243
|
+
uv run pytest
|
|
244
|
+
|
|
245
|
+
# Type check
|
|
246
|
+
uv run pyright
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## License
|
|
250
|
+
|
|
251
|
+
MIT
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "blender-cli"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Agent-friendly procedural map generation SDK built on Blender's Python API"
|
|
5
|
+
keywords = ["blender", "procedural", "map", "3d", "terrain", "generation"]
|
|
6
|
+
classifiers = [
|
|
7
|
+
"Development Status :: 3 - Alpha",
|
|
8
|
+
"Intended Audience :: Developers",
|
|
9
|
+
"Programming Language :: Python :: 3",
|
|
10
|
+
"Programming Language :: Python :: 3.11",
|
|
11
|
+
"Programming Language :: Python :: 3.12",
|
|
12
|
+
"Topic :: Multimedia :: Graphics :: 3D Modeling",
|
|
13
|
+
"Topic :: Games/Entertainment",
|
|
14
|
+
]
|
|
15
|
+
readme = "README.md"
|
|
16
|
+
authors = [{ name = "Julien Blanchon", email = "julien@blanchon.cc" }]
|
|
17
|
+
requires-python = ">=3.13"
|
|
18
|
+
dependencies = [
|
|
19
|
+
"bpy>=5.1.0",
|
|
20
|
+
"cma>=3.3",
|
|
21
|
+
"click>=8.0",
|
|
22
|
+
"fal-client>=0.7",
|
|
23
|
+
"matplotlib>=3.8",
|
|
24
|
+
"numpy>=1.24",
|
|
25
|
+
"opencv-python-headless>=4.8",
|
|
26
|
+
"Pillow>=10.0",
|
|
27
|
+
"prompt-toolkit>=3.0",
|
|
28
|
+
"pyrender>=0.1.45",
|
|
29
|
+
"rtree>=1.2",
|
|
30
|
+
"scipy>=1.11",
|
|
31
|
+
"trimesh>=4.0",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
blender_cli = "blender_cli.cli:main"
|
|
36
|
+
|
|
37
|
+
[dependency-groups]
|
|
38
|
+
dev = [
|
|
39
|
+
"pytest>=8.0",
|
|
40
|
+
"pyright>=1.1",
|
|
41
|
+
"trimesh>=4.0",
|
|
42
|
+
"numpy>=1.24",
|
|
43
|
+
"rtree>=1.4.1",
|
|
44
|
+
"fake-bpy-module>=20260220",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
[tool.pytest.ini_options]
|
|
48
|
+
markers = ["slow: tests taking >30s (deselect with -m 'not slow')"]
|
|
49
|
+
testpaths = ["tests"]
|
|
50
|
+
|
|
51
|
+
[tool.pyright]
|
|
52
|
+
# bpy stubs are incomplete — data/types/ops/context are not recognized.
|
|
53
|
+
reportAttributeAccessIssue = "warning"
|
|
54
|
+
|
|
55
|
+
[build-system]
|
|
56
|
+
requires = ["uv_build>=0.10.5,<0.11.0"]
|
|
57
|
+
build-backend = "uv_build"
|