glazed 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.
- glazed-0.1.0/LICENSE +21 -0
- glazed-0.1.0/PKG-INFO +311 -0
- glazed-0.1.0/README.md +288 -0
- glazed-0.1.0/pyproject.toml +43 -0
- glazed-0.1.0/setup.cfg +4 -0
- glazed-0.1.0/src/glazed/__init__.py +1 -0
- glazed-0.1.0/src/glazed/__main__.py +4 -0
- glazed-0.1.0/src/glazed/cli.py +155 -0
- glazed-0.1.0/src/glazed/io.py +288 -0
- glazed-0.1.0/src/glazed/preview.html +613 -0
- glazed-0.1.0/src/glazed/preview.py +176 -0
- glazed-0.1.0/src/glazed/render/__init__.py +5 -0
- glazed-0.1.0/src/glazed/render/assets.py +148 -0
- glazed-0.1.0/src/glazed/render/blender.py +42 -0
- glazed-0.1.0/src/glazed/render/camera.py +61 -0
- glazed-0.1.0/src/glazed/render/color.py +49 -0
- glazed-0.1.0/src/glazed/render/geometry.py +55 -0
- glazed-0.1.0/src/glazed/render/output.py +166 -0
- glazed-0.1.0/src/glazed/render/pipeline.py +79 -0
- glazed-0.1.0/src/glazed/render/scene.py +58 -0
- glazed-0.1.0/src/glazed/render/studio.py +44 -0
- glazed-0.1.0/src/glazed/settings.py +55 -0
- glazed-0.1.0/src/glazed.egg-info/PKG-INFO +311 -0
- glazed-0.1.0/src/glazed.egg-info/SOURCES.txt +26 -0
- glazed-0.1.0/src/glazed.egg-info/dependency_links.txt +1 -0
- glazed-0.1.0/src/glazed.egg-info/entry_points.txt +2 -0
- glazed-0.1.0/src/glazed.egg-info/requires.txt +8 -0
- glazed-0.1.0/src/glazed.egg-info/top_level.txt +1 -0
glazed-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Junha Lee
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
glazed-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: glazed
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Studio-lit Blender Cycles renders of point clouds, meshes, and scenes mixing both.
|
|
5
|
+
Author-email: Junha Lee <junha.lee@postech.ac.kr>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/junha-l/glazed
|
|
8
|
+
Project-URL: Issues, https://github.com/junha-l/glazed/issues
|
|
9
|
+
Keywords: blender,cycles,rendering,point-cloud,mesh,visualization
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Topic :: Multimedia :: Graphics :: 3D Rendering
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
14
|
+
Requires-Python: >=3.9
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: numpy>=1.21
|
|
18
|
+
Requires-Dist: tomli>=1.1; python_version < "3.11"
|
|
19
|
+
Provides-Extra: render
|
|
20
|
+
Requires-Dist: bpy>=5.0; extra == "render"
|
|
21
|
+
Requires-Dist: matplotlib>=3.8; extra == "render"
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
<div align="center">
|
|
25
|
+
|
|
26
|
+
# 🍩 glazed
|
|
27
|
+
|
|
28
|
+
**Studio-lit Blender Cycles renders of point clouds, meshes, and scenes mixing both.**
|
|
29
|
+
|
|
30
|
+
[](#installation)
|
|
31
|
+
[](#rendering)
|
|
32
|
+
[](#preview-in-the-browser)
|
|
33
|
+
[](#claude-code-plugin)
|
|
34
|
+
|
|
35
|
+
[Installation](#installation) ·
|
|
36
|
+
[Quickstart](#quickstart) ·
|
|
37
|
+
[Preview](#preview-in-the-browser) ·
|
|
38
|
+
[Rendering](#rendering) ·
|
|
39
|
+
[Scene files](#scene-files) ·
|
|
40
|
+
[Claude Code plugin](#claude-code-plugin)
|
|
41
|
+
|
|
42
|
+
|  |  |  |
|
|
43
|
+
|:---:|:---:|:---:|
|
|
44
|
+
| Meshes and an RGB point cloud | A point cloud through a colormap | Any backdrop |
|
|
45
|
+
|
|
46
|
+
[](#preview-in-the-browser)
|
|
47
|
+
|
|
48
|
+
<sub>Pick the view in a live browser preview, then render it with the command it hands back.</sub>
|
|
49
|
+
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
Raw 3D assets in, finished pieces out. Like a potter's glaze and firing, glazed
|
|
53
|
+
colours point clouds, meshes, and scenes mixing both, then fires them in Blender
|
|
54
|
+
Cycles: seamless backdrop, soft side light, contact shadows, and colormaps that
|
|
55
|
+
stay true to their colorbars. Renders stills, turntable videos, and GIFs.
|
|
56
|
+
|
|
57
|
+
## Highlights
|
|
58
|
+
|
|
59
|
+
- **Point clouds, meshes, or both** in one scene: `.npy`, `.ply`, `.stl`, `.obj`,
|
|
60
|
+
`.glb`, `.gltf`, and `.fbx`, laid out by a small TOML scene file.
|
|
61
|
+
- **A studio look out of the box**: a seamless backdrop, a soft side light, contact
|
|
62
|
+
shadows, and Blender's Standard view, so colormaps match their colorbars.
|
|
63
|
+
- **Stills, turntable videos, and GIFs** from one command, with camera paths for
|
|
64
|
+
custom shots.
|
|
65
|
+
- **A live browser preview** that frames and lights the scene like the render and
|
|
66
|
+
hands back the matching command.
|
|
67
|
+
- **Exports from any research codebase**: `glazed.io` needs only numpy and takes
|
|
68
|
+
torch tensors on any device.
|
|
69
|
+
- **A Claude Code plugin** that teaches Claude to export and render your project's
|
|
70
|
+
outputs.
|
|
71
|
+
|
|
72
|
+
## Installation
|
|
73
|
+
|
|
74
|
+
glazed has two parts, usually installed in two environments:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip install glazed # glazed.io and the preview: numpy only, Python >= 3.9
|
|
78
|
+
pip install "glazed[render]" # the renderer: Blender's bpy, Python 3.13 only
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
| Part | Install into | Needs |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| `glazed.io` and `--preview` | your research project's environment | Python ≥ 3.9, numpy |
|
|
84
|
+
| The renderer | a separate environment | Python 3.13, for Blender's `bpy` 5.x |
|
|
85
|
+
| Videos and GIFs | | `ffmpeg` on the PATH |
|
|
86
|
+
|
|
87
|
+
To work on glazed itself, install a checkout: `pip install /path/to/glazed`,
|
|
88
|
+
adding `-e` while developing.
|
|
89
|
+
|
|
90
|
+
### Without installing
|
|
91
|
+
|
|
92
|
+
Blender can run glazed from a source checkout with its own Python:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
blender -b -P render.py -- examples/donut.npy
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
This needs Blender 5.x, with `matplotlib` installed into Blender's bundled Python.
|
|
99
|
+
|
|
100
|
+
## Quickstart
|
|
101
|
+
|
|
102
|
+
The repository ships a glazed donut to try everything on. In the render
|
|
103
|
+
environment:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
glazed examples/glazed_donut.toml --preview # pick a view in the browser
|
|
107
|
+
glazed examples/glazed_donut.toml --elev 35 --gpu # outs/glazed_donut.png
|
|
108
|
+
glazed examples/glazed_donut.toml --mode mp4 gif --gpu # a turntable video and GIF
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The example files are baked by `examples/make_donut.py` using only `glazed.io`,
|
|
112
|
+
so it doubles as a worked example of exporting scenes.
|
|
113
|
+
|
|
114
|
+
## Exporting from your project
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from glazed.io import save_mesh, save_points, write_scene
|
|
118
|
+
|
|
119
|
+
save_mesh("out/object.ply", vertices, faces) # numpy arrays or torch tensors
|
|
120
|
+
save_points("out/pred.npy", points, values=per_point_error)
|
|
121
|
+
write_scene("out/scene.toml", [
|
|
122
|
+
{"path": "out/object.ply", "color": "#c9ccd1"},
|
|
123
|
+
{"path": "out/pred.npy"},
|
|
124
|
+
])
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Point clouds are `.npy` arrays of shape (N, 3) xyz, (N, 4) xyz + value, or
|
|
128
|
+
(N, 6) xyz + RGB. Meshes can be `.ply`, `.stl`, `.obj`, `.glb`, `.gltf`, or `.fbx`;
|
|
129
|
+
`save_mesh` writes PLY, which Blender reads with coordinates as stored, while OBJ
|
|
130
|
+
files are treated as Y-up. `glazed.io` also has `sample_surface`, `to_z_up`,
|
|
131
|
+
`check_alignment`, `grid_locations`, `read_scene`, and `check_scene`.
|
|
132
|
+
|
|
133
|
+
## Preview in the browser
|
|
134
|
+
|
|
135
|
+
A Cycles render takes minutes, so pick the view and lighting in a live preview
|
|
136
|
+
first:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
glazed examples/glazed_donut.toml --preview
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
This opens the scene in the browser, framed and lit like the render. Drag to
|
|
143
|
+
orbit, scroll to zoom, and adjust the lighting, backdrop, colormap, and image size
|
|
144
|
+
in the panel, which shows the matching `glazed` command to copy, including the
|
|
145
|
+
other options you passed. **Render with Cycles** renders the current view at
|
|
146
|
+
preview quality to check the real look, and **Play turntable** plays a video's
|
|
147
|
+
orbit at its real speed.
|
|
148
|
+
|
|
149
|
+
| Live preview | Cycles render of the copied command |
|
|
150
|
+
|:---:|:---:|
|
|
151
|
+
|  |  |
|
|
152
|
+
|
|
153
|
+
The same view, `glazed examples/glazed_donut.toml --azim 30 --elev 40`, in the
|
|
154
|
+
preview and rendered by Cycles.
|
|
155
|
+
|
|
156
|
+
The preview needs only numpy, so it runs in a research environment too. There,
|
|
157
|
+
the Cycles button is off, since it needs bpy, and colormaps other than viridis
|
|
158
|
+
need matplotlib. On a remote machine, pick a port and forward it:
|
|
159
|
+
`glazed scene.toml --preview --port 8765`, then `ssh -L 8765:localhost:8765 <host>`
|
|
160
|
+
and open `http://localhost:8765`. The page loads three.js from a CDN, so the
|
|
161
|
+
browser needs internet access. Soft floor shadows are approximate, mostly where
|
|
162
|
+
several objects' shadows overlap; the Cycles button shows the real ones.
|
|
163
|
+
|
|
164
|
+
## Rendering
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
glazed examples/glazed_donut.toml --elev 35 --gpu # dough and glaze meshes, RGB sprinkles
|
|
168
|
+
glazed examples/donut.npy --gpu # a point cloud, coloured by height
|
|
169
|
+
glazed examples/glazed_donut.toml --mode mp4 gif --gpu # a turntable video and GIF
|
|
170
|
+
glazed mesh.ply cloud.npy --bg "#2b2f36" --gpu # any files, dark backdrop
|
|
171
|
+
glazed --help # every option
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Outputs
|
|
175
|
+
|
|
176
|
+
Output goes to `outs/<first input's name>` plus an extension per mode. Set the
|
|
177
|
+
path with `--out figs/teaser`; `--out figs/teaser.png` works too, without doubling
|
|
178
|
+
the extension.
|
|
179
|
+
|
|
180
|
+
| Output | Path |
|
|
181
|
+
|---|---|
|
|
182
|
+
| Still (`--mode still`, the default) | `outs/glazed_donut.png` |
|
|
183
|
+
| Video and GIF (`--mode mp4 gif`) | `outs/glazed_donut.mp4`, `.gif`, with frames kept in `outs/glazed_donut_frames/` |
|
|
184
|
+
| Frames only (`--mode png`) | `outs/glazed_donut_frames/frame_00000.png`, ... |
|
|
185
|
+
| Several colormaps (`--cmap viridis magma`) | `outs/donut_viridis.png`, `outs/donut_magma.png` |
|
|
186
|
+
| `--colorbar`, `--save-blend` | `outs/donut_colorbar.png`, `outs/donut.blend`, suffixed per colormap like the renders |
|
|
187
|
+
|
|
188
|
+
`--no-keep-frames` deletes the frames once the video is encoded.
|
|
189
|
+
|
|
190
|
+
### Lighting and camera
|
|
191
|
+
|
|
192
|
+
The studio is one soft key light from the side, white ambient fill, and a floor
|
|
193
|
+
that only catches shadows. The lights turn with the camera, like a turntable under
|
|
194
|
+
fixed lights, so an orbit looks the same from every angle.
|
|
195
|
+
|
|
196
|
+
| Option | Default | |
|
|
197
|
+
|---|---|---|
|
|
198
|
+
| `--exposure` | `0` | stops; `-1` halves the brightness |
|
|
199
|
+
| `--key` | `1` | key light strength |
|
|
200
|
+
| `--ambient` | `1` | fill light; lower gives darker shadows and more contrast |
|
|
201
|
+
| `--key-azim` | `-90` | key angle from the camera, degrees, negative to its left; near 0 looks flat, near ±90 shows the most depth |
|
|
202
|
+
| `--key-elev` | `35` | key height, degrees; lower gives stronger shading on curved surfaces |
|
|
203
|
+
| `--bg` | `white` | backdrop, any matplotlib colour; `--transparent` leaves it transparent for compositing |
|
|
204
|
+
| `--azim`, `--elev` | `45`, `22` | camera angle, degrees; an orbit starts here |
|
|
205
|
+
| `--distance` | fits the scene | multiple of the scene's half-size |
|
|
206
|
+
| `--lens` | `50` | mm; shorter gives stronger perspective |
|
|
207
|
+
| `--cmap`, `--vmin`, `--vmax` | `viridis`, data range | colours per-point values; the range is shared across the scene |
|
|
208
|
+
|
|
209
|
+
### Turntables and camera paths
|
|
210
|
+
|
|
211
|
+
Video modes orbit the scene, by default `--frames 120 --fps 30 --turns 1`, which
|
|
212
|
+
loops seamlessly. `--elev-amp 10` adds an up-and-down sway, `--dolly 0.15` pushes
|
|
213
|
+
in by 15% over the shot, and `--ease` slows the start and end, which breaks the
|
|
214
|
+
loop. `--gif-width` sets the GIF's width (720).
|
|
215
|
+
|
|
216
|
+
`--waypoints path.json` replaces the orbit with a smooth spline through camera
|
|
217
|
+
positions. Coordinates are in normalised scene units: the scene is centred on the
|
|
218
|
+
origin with a largest half-side of 1. `target` is optional and defaults to the
|
|
219
|
+
orbit's centre.
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
{"eye": [[3, -3, 1], [3, 3, 1.5], [-3, 3, 1]], "target": [[0, 0, 0]], "loop": true}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Quality
|
|
226
|
+
|
|
227
|
+
Renders default to `--res 1400 1050 --samples 256 --supersample 2`, rendering at
|
|
228
|
+
twice the resolution and filtering down for clean edges. `--gpu` uses the first of
|
|
229
|
+
OptiX, CUDA, HIP, Metal, or oneAPI it finds, and falls back to the CPU. Preview
|
|
230
|
+
views with `--res 480 360 --samples 32 --supersample 1`; for videos,
|
|
231
|
+
`--res 1280 720 --samples 128 --supersample 1` keeps frame times down.
|
|
232
|
+
`--save-blend` also saves the scene as a `.blend` to adjust by hand in Blender.
|
|
233
|
+
|
|
234
|
+
### Python API
|
|
235
|
+
|
|
236
|
+
In the render environment:
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
from glazed.io import AssetSpec
|
|
240
|
+
from glazed.render import render
|
|
241
|
+
from glazed.settings import RenderSettings, View
|
|
242
|
+
|
|
243
|
+
render([AssetSpec("examples/donut.ply", color="#d9a066"),
|
|
244
|
+
AssetSpec("examples/glaze.ply", color="#f4a6c6"),
|
|
245
|
+
AssetSpec("examples/sprinkles.npy", radius=0.0009)],
|
|
246
|
+
out="outs/glazed_donut", settings=RenderSettings(gpu=True), view=View(elev=35))
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
`out` works like `--out` and defaults to `outs/render`. `modes` takes the
|
|
250
|
+
`--mode` values, e.g. `modes=("mp4", "gif")`, and the other options live in
|
|
251
|
+
`glazed.settings` as dataclasses: `RenderSettings`, `Lighting`, `View`,
|
|
252
|
+
`Animation`, and `Colormap`.
|
|
253
|
+
|
|
254
|
+
## Scene files
|
|
255
|
+
|
|
256
|
+
```toml
|
|
257
|
+
[[objects]]
|
|
258
|
+
path = "cloud.npy"
|
|
259
|
+
radius_scale = 1.25
|
|
260
|
+
|
|
261
|
+
[[objects]]
|
|
262
|
+
path = "mesh.ply"
|
|
263
|
+
color = "#c9ccd1"
|
|
264
|
+
location = [7.5, 0, 0] # in the file's own units
|
|
265
|
+
rotation = [0, 0, 90] # degrees about x, y, z
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
| Key | Default | |
|
|
269
|
+
|---|---|---|
|
|
270
|
+
| `path` | required | relative to the scene file |
|
|
271
|
+
| `color` | `"#cccccc"` | any matplotlib colour; `"original"` keeps a mesh's own materials. Ignored for (N, 4) and (N, 6) point clouds |
|
|
272
|
+
| `location` | `[0, 0, 0]` | file units |
|
|
273
|
+
| `rotation` | `[0, 0, 0]` | degrees about x, y, z, pivoting on the file's origin |
|
|
274
|
+
| `scale` | `1.0` | uniform |
|
|
275
|
+
| `radius` | automatic | point clouds, file units |
|
|
276
|
+
| `radius_scale` | `1.0` | point clouds, multiplier on the automatic radius |
|
|
277
|
+
| `smooth` | `true` | meshes, smooth shading |
|
|
278
|
+
|
|
279
|
+
The world is Z-up. `--color`, `--radius` and `--radius-scale` set defaults that
|
|
280
|
+
entries override. The same structure in JSON (`{"objects": [{...}, ...]}`) also
|
|
281
|
+
works.
|
|
282
|
+
|
|
283
|
+
## Claude Code plugin
|
|
284
|
+
|
|
285
|
+
This repository is also a Claude Code plugin. Its `glazed-render` skill teaches
|
|
286
|
+
Claude, working in your research project, to export the project's outputs with
|
|
287
|
+
`glazed.io`, keep the parts of a scene aligned, and render them. Domain guides in
|
|
288
|
+
`skills/glazed-render/references/` cover specific kinds of projects, starting with
|
|
289
|
+
hand-object grasps. Install it once, for every project:
|
|
290
|
+
|
|
291
|
+
```
|
|
292
|
+
/plugin marketplace add /path/to/glazed # or <owner>/<repo> once it's on GitHub
|
|
293
|
+
/plugin install glazed@glazed
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Extending
|
|
297
|
+
|
|
298
|
+
- **New file type:** list its suffix in `glazed/io.py`, then map it to a Blender
|
|
299
|
+
importer in `IMPORTERS`, or to an `Asset` subclass in `LOADERS`, in
|
|
300
|
+
`glazed/render/assets.py`. For the preview, map it to a three.js loader in
|
|
301
|
+
`LOADERS` in `glazed/preview.html`.
|
|
302
|
+
- **New asset type:** subclass `Asset`, build its objects in `__init__`, and
|
|
303
|
+
override `paint()`.
|
|
304
|
+
- **New domain guide for the skill:** add a Markdown file to
|
|
305
|
+
`skills/glazed-render/references/` and list it in `SKILL.md`.
|
|
306
|
+
|
|
307
|
+
## Acknowledgements
|
|
308
|
+
|
|
309
|
+
glazed renders with [Blender](https://www.blender.org/)'s Cycles through its
|
|
310
|
+
`bpy` module, and its preview runs on [three.js](https://threejs.org/), whose
|
|
311
|
+
percentage-closer soft shadows example the preview's floor shadows adapt.
|
glazed-0.1.0/README.md
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 🍩 glazed
|
|
4
|
+
|
|
5
|
+
**Studio-lit Blender Cycles renders of point clouds, meshes, and scenes mixing both.**
|
|
6
|
+
|
|
7
|
+
[](#installation)
|
|
8
|
+
[](#rendering)
|
|
9
|
+
[](#preview-in-the-browser)
|
|
10
|
+
[](#claude-code-plugin)
|
|
11
|
+
|
|
12
|
+
[Installation](#installation) ·
|
|
13
|
+
[Quickstart](#quickstart) ·
|
|
14
|
+
[Preview](#preview-in-the-browser) ·
|
|
15
|
+
[Rendering](#rendering) ·
|
|
16
|
+
[Scene files](#scene-files) ·
|
|
17
|
+
[Claude Code plugin](#claude-code-plugin)
|
|
18
|
+
|
|
19
|
+
|  |  |  |
|
|
20
|
+
|:---:|:---:|:---:|
|
|
21
|
+
| Meshes and an RGB point cloud | A point cloud through a colormap | Any backdrop |
|
|
22
|
+
|
|
23
|
+
[](#preview-in-the-browser)
|
|
24
|
+
|
|
25
|
+
<sub>Pick the view in a live browser preview, then render it with the command it hands back.</sub>
|
|
26
|
+
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
Raw 3D assets in, finished pieces out. Like a potter's glaze and firing, glazed
|
|
30
|
+
colours point clouds, meshes, and scenes mixing both, then fires them in Blender
|
|
31
|
+
Cycles: seamless backdrop, soft side light, contact shadows, and colormaps that
|
|
32
|
+
stay true to their colorbars. Renders stills, turntable videos, and GIFs.
|
|
33
|
+
|
|
34
|
+
## Highlights
|
|
35
|
+
|
|
36
|
+
- **Point clouds, meshes, or both** in one scene: `.npy`, `.ply`, `.stl`, `.obj`,
|
|
37
|
+
`.glb`, `.gltf`, and `.fbx`, laid out by a small TOML scene file.
|
|
38
|
+
- **A studio look out of the box**: a seamless backdrop, a soft side light, contact
|
|
39
|
+
shadows, and Blender's Standard view, so colormaps match their colorbars.
|
|
40
|
+
- **Stills, turntable videos, and GIFs** from one command, with camera paths for
|
|
41
|
+
custom shots.
|
|
42
|
+
- **A live browser preview** that frames and lights the scene like the render and
|
|
43
|
+
hands back the matching command.
|
|
44
|
+
- **Exports from any research codebase**: `glazed.io` needs only numpy and takes
|
|
45
|
+
torch tensors on any device.
|
|
46
|
+
- **A Claude Code plugin** that teaches Claude to export and render your project's
|
|
47
|
+
outputs.
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
glazed has two parts, usually installed in two environments:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install glazed # glazed.io and the preview: numpy only, Python >= 3.9
|
|
55
|
+
pip install "glazed[render]" # the renderer: Blender's bpy, Python 3.13 only
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
| Part | Install into | Needs |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| `glazed.io` and `--preview` | your research project's environment | Python ≥ 3.9, numpy |
|
|
61
|
+
| The renderer | a separate environment | Python 3.13, for Blender's `bpy` 5.x |
|
|
62
|
+
| Videos and GIFs | | `ffmpeg` on the PATH |
|
|
63
|
+
|
|
64
|
+
To work on glazed itself, install a checkout: `pip install /path/to/glazed`,
|
|
65
|
+
adding `-e` while developing.
|
|
66
|
+
|
|
67
|
+
### Without installing
|
|
68
|
+
|
|
69
|
+
Blender can run glazed from a source checkout with its own Python:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
blender -b -P render.py -- examples/donut.npy
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This needs Blender 5.x, with `matplotlib` installed into Blender's bundled Python.
|
|
76
|
+
|
|
77
|
+
## Quickstart
|
|
78
|
+
|
|
79
|
+
The repository ships a glazed donut to try everything on. In the render
|
|
80
|
+
environment:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
glazed examples/glazed_donut.toml --preview # pick a view in the browser
|
|
84
|
+
glazed examples/glazed_donut.toml --elev 35 --gpu # outs/glazed_donut.png
|
|
85
|
+
glazed examples/glazed_donut.toml --mode mp4 gif --gpu # a turntable video and GIF
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The example files are baked by `examples/make_donut.py` using only `glazed.io`,
|
|
89
|
+
so it doubles as a worked example of exporting scenes.
|
|
90
|
+
|
|
91
|
+
## Exporting from your project
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from glazed.io import save_mesh, save_points, write_scene
|
|
95
|
+
|
|
96
|
+
save_mesh("out/object.ply", vertices, faces) # numpy arrays or torch tensors
|
|
97
|
+
save_points("out/pred.npy", points, values=per_point_error)
|
|
98
|
+
write_scene("out/scene.toml", [
|
|
99
|
+
{"path": "out/object.ply", "color": "#c9ccd1"},
|
|
100
|
+
{"path": "out/pred.npy"},
|
|
101
|
+
])
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Point clouds are `.npy` arrays of shape (N, 3) xyz, (N, 4) xyz + value, or
|
|
105
|
+
(N, 6) xyz + RGB. Meshes can be `.ply`, `.stl`, `.obj`, `.glb`, `.gltf`, or `.fbx`;
|
|
106
|
+
`save_mesh` writes PLY, which Blender reads with coordinates as stored, while OBJ
|
|
107
|
+
files are treated as Y-up. `glazed.io` also has `sample_surface`, `to_z_up`,
|
|
108
|
+
`check_alignment`, `grid_locations`, `read_scene`, and `check_scene`.
|
|
109
|
+
|
|
110
|
+
## Preview in the browser
|
|
111
|
+
|
|
112
|
+
A Cycles render takes minutes, so pick the view and lighting in a live preview
|
|
113
|
+
first:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
glazed examples/glazed_donut.toml --preview
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
This opens the scene in the browser, framed and lit like the render. Drag to
|
|
120
|
+
orbit, scroll to zoom, and adjust the lighting, backdrop, colormap, and image size
|
|
121
|
+
in the panel, which shows the matching `glazed` command to copy, including the
|
|
122
|
+
other options you passed. **Render with Cycles** renders the current view at
|
|
123
|
+
preview quality to check the real look, and **Play turntable** plays a video's
|
|
124
|
+
orbit at its real speed.
|
|
125
|
+
|
|
126
|
+
| Live preview | Cycles render of the copied command |
|
|
127
|
+
|:---:|:---:|
|
|
128
|
+
|  |  |
|
|
129
|
+
|
|
130
|
+
The same view, `glazed examples/glazed_donut.toml --azim 30 --elev 40`, in the
|
|
131
|
+
preview and rendered by Cycles.
|
|
132
|
+
|
|
133
|
+
The preview needs only numpy, so it runs in a research environment too. There,
|
|
134
|
+
the Cycles button is off, since it needs bpy, and colormaps other than viridis
|
|
135
|
+
need matplotlib. On a remote machine, pick a port and forward it:
|
|
136
|
+
`glazed scene.toml --preview --port 8765`, then `ssh -L 8765:localhost:8765 <host>`
|
|
137
|
+
and open `http://localhost:8765`. The page loads three.js from a CDN, so the
|
|
138
|
+
browser needs internet access. Soft floor shadows are approximate, mostly where
|
|
139
|
+
several objects' shadows overlap; the Cycles button shows the real ones.
|
|
140
|
+
|
|
141
|
+
## Rendering
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
glazed examples/glazed_donut.toml --elev 35 --gpu # dough and glaze meshes, RGB sprinkles
|
|
145
|
+
glazed examples/donut.npy --gpu # a point cloud, coloured by height
|
|
146
|
+
glazed examples/glazed_donut.toml --mode mp4 gif --gpu # a turntable video and GIF
|
|
147
|
+
glazed mesh.ply cloud.npy --bg "#2b2f36" --gpu # any files, dark backdrop
|
|
148
|
+
glazed --help # every option
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Outputs
|
|
152
|
+
|
|
153
|
+
Output goes to `outs/<first input's name>` plus an extension per mode. Set the
|
|
154
|
+
path with `--out figs/teaser`; `--out figs/teaser.png` works too, without doubling
|
|
155
|
+
the extension.
|
|
156
|
+
|
|
157
|
+
| Output | Path |
|
|
158
|
+
|---|---|
|
|
159
|
+
| Still (`--mode still`, the default) | `outs/glazed_donut.png` |
|
|
160
|
+
| Video and GIF (`--mode mp4 gif`) | `outs/glazed_donut.mp4`, `.gif`, with frames kept in `outs/glazed_donut_frames/` |
|
|
161
|
+
| Frames only (`--mode png`) | `outs/glazed_donut_frames/frame_00000.png`, ... |
|
|
162
|
+
| Several colormaps (`--cmap viridis magma`) | `outs/donut_viridis.png`, `outs/donut_magma.png` |
|
|
163
|
+
| `--colorbar`, `--save-blend` | `outs/donut_colorbar.png`, `outs/donut.blend`, suffixed per colormap like the renders |
|
|
164
|
+
|
|
165
|
+
`--no-keep-frames` deletes the frames once the video is encoded.
|
|
166
|
+
|
|
167
|
+
### Lighting and camera
|
|
168
|
+
|
|
169
|
+
The studio is one soft key light from the side, white ambient fill, and a floor
|
|
170
|
+
that only catches shadows. The lights turn with the camera, like a turntable under
|
|
171
|
+
fixed lights, so an orbit looks the same from every angle.
|
|
172
|
+
|
|
173
|
+
| Option | Default | |
|
|
174
|
+
|---|---|---|
|
|
175
|
+
| `--exposure` | `0` | stops; `-1` halves the brightness |
|
|
176
|
+
| `--key` | `1` | key light strength |
|
|
177
|
+
| `--ambient` | `1` | fill light; lower gives darker shadows and more contrast |
|
|
178
|
+
| `--key-azim` | `-90` | key angle from the camera, degrees, negative to its left; near 0 looks flat, near ±90 shows the most depth |
|
|
179
|
+
| `--key-elev` | `35` | key height, degrees; lower gives stronger shading on curved surfaces |
|
|
180
|
+
| `--bg` | `white` | backdrop, any matplotlib colour; `--transparent` leaves it transparent for compositing |
|
|
181
|
+
| `--azim`, `--elev` | `45`, `22` | camera angle, degrees; an orbit starts here |
|
|
182
|
+
| `--distance` | fits the scene | multiple of the scene's half-size |
|
|
183
|
+
| `--lens` | `50` | mm; shorter gives stronger perspective |
|
|
184
|
+
| `--cmap`, `--vmin`, `--vmax` | `viridis`, data range | colours per-point values; the range is shared across the scene |
|
|
185
|
+
|
|
186
|
+
### Turntables and camera paths
|
|
187
|
+
|
|
188
|
+
Video modes orbit the scene, by default `--frames 120 --fps 30 --turns 1`, which
|
|
189
|
+
loops seamlessly. `--elev-amp 10` adds an up-and-down sway, `--dolly 0.15` pushes
|
|
190
|
+
in by 15% over the shot, and `--ease` slows the start and end, which breaks the
|
|
191
|
+
loop. `--gif-width` sets the GIF's width (720).
|
|
192
|
+
|
|
193
|
+
`--waypoints path.json` replaces the orbit with a smooth spline through camera
|
|
194
|
+
positions. Coordinates are in normalised scene units: the scene is centred on the
|
|
195
|
+
origin with a largest half-side of 1. `target` is optional and defaults to the
|
|
196
|
+
orbit's centre.
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
{"eye": [[3, -3, 1], [3, 3, 1.5], [-3, 3, 1]], "target": [[0, 0, 0]], "loop": true}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Quality
|
|
203
|
+
|
|
204
|
+
Renders default to `--res 1400 1050 --samples 256 --supersample 2`, rendering at
|
|
205
|
+
twice the resolution and filtering down for clean edges. `--gpu` uses the first of
|
|
206
|
+
OptiX, CUDA, HIP, Metal, or oneAPI it finds, and falls back to the CPU. Preview
|
|
207
|
+
views with `--res 480 360 --samples 32 --supersample 1`; for videos,
|
|
208
|
+
`--res 1280 720 --samples 128 --supersample 1` keeps frame times down.
|
|
209
|
+
`--save-blend` also saves the scene as a `.blend` to adjust by hand in Blender.
|
|
210
|
+
|
|
211
|
+
### Python API
|
|
212
|
+
|
|
213
|
+
In the render environment:
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
from glazed.io import AssetSpec
|
|
217
|
+
from glazed.render import render
|
|
218
|
+
from glazed.settings import RenderSettings, View
|
|
219
|
+
|
|
220
|
+
render([AssetSpec("examples/donut.ply", color="#d9a066"),
|
|
221
|
+
AssetSpec("examples/glaze.ply", color="#f4a6c6"),
|
|
222
|
+
AssetSpec("examples/sprinkles.npy", radius=0.0009)],
|
|
223
|
+
out="outs/glazed_donut", settings=RenderSettings(gpu=True), view=View(elev=35))
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
`out` works like `--out` and defaults to `outs/render`. `modes` takes the
|
|
227
|
+
`--mode` values, e.g. `modes=("mp4", "gif")`, and the other options live in
|
|
228
|
+
`glazed.settings` as dataclasses: `RenderSettings`, `Lighting`, `View`,
|
|
229
|
+
`Animation`, and `Colormap`.
|
|
230
|
+
|
|
231
|
+
## Scene files
|
|
232
|
+
|
|
233
|
+
```toml
|
|
234
|
+
[[objects]]
|
|
235
|
+
path = "cloud.npy"
|
|
236
|
+
radius_scale = 1.25
|
|
237
|
+
|
|
238
|
+
[[objects]]
|
|
239
|
+
path = "mesh.ply"
|
|
240
|
+
color = "#c9ccd1"
|
|
241
|
+
location = [7.5, 0, 0] # in the file's own units
|
|
242
|
+
rotation = [0, 0, 90] # degrees about x, y, z
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
| Key | Default | |
|
|
246
|
+
|---|---|---|
|
|
247
|
+
| `path` | required | relative to the scene file |
|
|
248
|
+
| `color` | `"#cccccc"` | any matplotlib colour; `"original"` keeps a mesh's own materials. Ignored for (N, 4) and (N, 6) point clouds |
|
|
249
|
+
| `location` | `[0, 0, 0]` | file units |
|
|
250
|
+
| `rotation` | `[0, 0, 0]` | degrees about x, y, z, pivoting on the file's origin |
|
|
251
|
+
| `scale` | `1.0` | uniform |
|
|
252
|
+
| `radius` | automatic | point clouds, file units |
|
|
253
|
+
| `radius_scale` | `1.0` | point clouds, multiplier on the automatic radius |
|
|
254
|
+
| `smooth` | `true` | meshes, smooth shading |
|
|
255
|
+
|
|
256
|
+
The world is Z-up. `--color`, `--radius` and `--radius-scale` set defaults that
|
|
257
|
+
entries override. The same structure in JSON (`{"objects": [{...}, ...]}`) also
|
|
258
|
+
works.
|
|
259
|
+
|
|
260
|
+
## Claude Code plugin
|
|
261
|
+
|
|
262
|
+
This repository is also a Claude Code plugin. Its `glazed-render` skill teaches
|
|
263
|
+
Claude, working in your research project, to export the project's outputs with
|
|
264
|
+
`glazed.io`, keep the parts of a scene aligned, and render them. Domain guides in
|
|
265
|
+
`skills/glazed-render/references/` cover specific kinds of projects, starting with
|
|
266
|
+
hand-object grasps. Install it once, for every project:
|
|
267
|
+
|
|
268
|
+
```
|
|
269
|
+
/plugin marketplace add /path/to/glazed # or <owner>/<repo> once it's on GitHub
|
|
270
|
+
/plugin install glazed@glazed
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Extending
|
|
274
|
+
|
|
275
|
+
- **New file type:** list its suffix in `glazed/io.py`, then map it to a Blender
|
|
276
|
+
importer in `IMPORTERS`, or to an `Asset` subclass in `LOADERS`, in
|
|
277
|
+
`glazed/render/assets.py`. For the preview, map it to a three.js loader in
|
|
278
|
+
`LOADERS` in `glazed/preview.html`.
|
|
279
|
+
- **New asset type:** subclass `Asset`, build its objects in `__init__`, and
|
|
280
|
+
override `paint()`.
|
|
281
|
+
- **New domain guide for the skill:** add a Markdown file to
|
|
282
|
+
`skills/glazed-render/references/` and list it in `SKILL.md`.
|
|
283
|
+
|
|
284
|
+
## Acknowledgements
|
|
285
|
+
|
|
286
|
+
glazed renders with [Blender](https://www.blender.org/)'s Cycles through its
|
|
287
|
+
`bpy` module, and its preview runs on [three.js](https://threejs.org/), whose
|
|
288
|
+
percentage-closer soft shadows example the preview's floor shadows adapt.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"] # for the SPDX license field
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "glazed"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Studio-lit Blender Cycles renders of point clouds, meshes, and scenes mixing both."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
authors = [{ name = "Junha Lee", email = "junha.lee@postech.ac.kr" }]
|
|
13
|
+
keywords = ["blender", "cycles", "rendering", "point-cloud", "mesh", "visualization"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Intended Audience :: Science/Research",
|
|
17
|
+
"Topic :: Multimedia :: Graphics :: 3D Rendering",
|
|
18
|
+
"Topic :: Scientific/Engineering :: Visualization",
|
|
19
|
+
]
|
|
20
|
+
requires-python = ">=3.9"
|
|
21
|
+
dependencies = [ # enough to write scenes (glazed.io) in any research environment
|
|
22
|
+
"numpy>=1.21",
|
|
23
|
+
"tomli>=1.1; python_version < '3.11'",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.optional-dependencies]
|
|
27
|
+
render = [ # the bpy wheels for Blender 5.x need Python 3.13
|
|
28
|
+
"bpy>=5.0",
|
|
29
|
+
"matplotlib>=3.8",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Homepage = "https://github.com/junha-l/glazed"
|
|
34
|
+
Issues = "https://github.com/junha-l/glazed/issues"
|
|
35
|
+
|
|
36
|
+
[project.scripts]
|
|
37
|
+
glazed = "glazed.cli:main"
|
|
38
|
+
|
|
39
|
+
[tool.setuptools.packages.find]
|
|
40
|
+
where = ["src"]
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.package-data]
|
|
43
|
+
glazed = ["preview.html"] # the --preview page
|
glazed-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""glazed: studio-lit Cycles renders of point clouds, meshes, and scenes mixing both."""
|