Skip to content

Commit beb7a98

Browse files
docs: python bindings guide
1 parent 4a0e2ce commit beb7a98

1 file changed

Lines changed: 97 additions & 0 deletions

File tree

docs/PYTHON_BINDINGS.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Python Bindings
2+
3+
Python drives the engine through the command stream. You build a command as plain
4+
data, submit it, and read the reply. The bindings live at
5+
`crates/nightshade-api/bindings/python` and wrap that stream in a small ergonomic
6+
layer, so the spinning cube is the whole pitch:
7+
8+
```python
9+
import nightshade as ns
10+
11+
app = ns.open()
12+
cube = app.spawn_cube([0.0, 0.5, 0.0])
13+
app.set_color(cube, [0.2, 0.6, 1.0, 1.0])
14+
while app.frame():
15+
app.rotate(cube, [0.0, 1.0, 0.0], app.delta_time())
16+
```
17+
18+
## Building
19+
20+
Needs a Rust toolchain and `maturin`. From the binding directory:
21+
22+
```
23+
just develop # build the extension into the active environment
24+
just test # headless tests (the GPU render test is gated off)
25+
just build # produce an abi3 wheel in ./dist
26+
```
27+
28+
The wheel is `abi3`, so one build serves CPython 3.8 and up. A window needs a
29+
display and a GPU. For headless work, `ns.render_to_image` draws one offscreen
30+
frame after a setup batch.
31+
32+
## The model
33+
34+
The extension is a thin forwarder. It opens a window, advances a frame, and
35+
submits commands through the same `submit_commands` the rest of the api uses. No
36+
`World`, no engine internals cross the boundary, only commands in and replies
37+
out. Commands and replies travel as json, so the bridge has no per-object
38+
conversion layer.
39+
40+
`App` is the open window. Its ergonomic methods build a command and submit it:
41+
`spawn_cube`, `spawn_sphere`, `spawn_floor`, `set_color`, `set_metallic_roughness`,
42+
`set_emissive`, `set_position`, `rotate`, `set_scale`, `set_parent`, `despawn`,
43+
`point_light`, `set_sun`, `set_background`, `orbit_camera`, `fly_camera`,
44+
`look_at`, `play_animation`, `delta_time`, `elapsed_seconds`, `key_down`,
45+
`key_pressed`, `mouse_clicked`, `wasd`, `clicked_entity`, `entity_under_cursor`,
46+
`cursor_on_ground`, and more.
47+
48+
Spawns return an `Entity` handle. Pass it straight back into any command that
49+
takes one.
50+
51+
```python
52+
ball = app.spawn_sphere([0.0, 4.0, 0.0])
53+
app.set_metallic_roughness(ball, 0.9, 0.2)
54+
hit = app.clicked_entity()
55+
if hit is not None:
56+
app.set_color(hit, [1.0, 0.3, 0.2, 1.0])
57+
```
58+
59+
## The full surface
60+
61+
The ergonomic methods cover the common calls. For anything else, `submit` and
62+
`submit_batch` take the whole command surface as dicts in the
63+
`{"Variant": {fields}}` form. A batch can reference an entity an earlier command
64+
produced, so it builds and wires a scene in one call.
65+
66+
```python
67+
app.submit({"SetExposure": {"exposure": 1.2}})
68+
app.submit_batch([
69+
{"SpawnCube": {"position": [-1.5, 0.5, 0.0]}},
70+
{"SpawnSphere": {"position": [1.5, 0.5, 0.0]}},
71+
])
72+
```
73+
74+
`ns.command_schema()` returns the json schema for the command surface, and
75+
`ns.command_reply_schema()` the reply schema, both as dicts. They are the source
76+
a binding language generates its encoder and decoder from.
77+
78+
## Input
79+
80+
Key names are plain strings: letters, digits, `"space"`, `"enter"`, `"escape"`,
81+
`"tab"`, the arrows `"left"`/`"right"`/`"up"`/`"down"`, and `"shift"`/`"ctrl"`/
82+
`"alt"`. The `ns.keys` namespace holds the common ones. Mouse buttons are
83+
`0` left, `1` middle, `2` right.
84+
85+
```python
86+
while app.frame():
87+
if app.key_down("space"):
88+
...
89+
move = app.wasd()
90+
```
91+
92+
## Not bound yet
93+
94+
The participant event stream (`push_command` and `drain_events`) is not exposed.
95+
Its `Event` and `Command` types need a serde wire form that matches their
96+
packed-integer entity schema before the binding can carry them. The command
97+
stream above is the whole surface for now.

0 commit comments

Comments
 (0)