OverviewFilesCommitsIssuesDocsReleasesPeers

blender-bridge commits / 65b53b1

Update SPEC.md: VT→SVT rename, mark B3/B4/B5 as fixed
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
65b53b14de06a923ecb3f388b623ecef6e6ad8a3
by plankatron <fleeky@gmail.com> · 2026-03-04T18:35:30.000Z

Changed files (1) +401 -0

docs/SPEC.md +401 -0
……@@ -0,0 +1,401 @@
1# Blender-Bridge Protocol Specification
2
3**Version**: 1.0 (protocol v1)
4**Last Updated**: 2026-03-04
5
6This document is the authoritative reference for the Blender-Bridge shared memory
7protocol, UDS message protocol, data model mappings, and architectural constraints.
8All implementation changes MUST be validated against this spec.
9
10---
11
12## 1. Architecture Overview
13
14```
15Blender (Python addon) Godot (GDExtension, C++)
16─────────────────────── ────────────────────────
17ChangeDetector BlenderBridge
18 ├─ depsgraph handler ├─ export_scene() / _detect_and_push_changes()
19 ├─ flush() → write Region 1 ├─ import_changes() ← read Region 1
20 └─ read Region 0 └─ write Region 0
21
22SceneImporter VirtualTexture
23 └─ read Region 0 → create ├─ PageTable (mip pyramid)
24 Blender objects ├─ TileCache (LRU atlas)
25 └─ svt_lookup.gdshader
26TileBaker
27 └─ bake tiles → SHM ring buffer
28
29 ┌──────── POSIX SHM (/dev/shm/blender_bridge) ────────┐
30 │ Region 0 (G→B)│ Region 1 (B→G)│ Tile Ring (B→G) │
31 │ 8 MB │ 8 MB │ 8 MB │
32 └────────────────┴────────────────┴─────────────────────┘
33
34 ┌──────── UDS (/tmp/blender_bridge.sock) ──────────────┐
35 │ Control messages, texture registration, tile signals │
36 └───────────────────────────────────────────────────────┘
37```
38
39### Dual-Region Design
40
41- **Region 0** (Godot → Blender): Godot writes its scene state; Blender reads.
42- **Region 1** (Blender → Godot): Blender writes its scene state; Godot reads.
43- **Tile Ring** (Blender → Godot): Raw RGBA8 tile data for texture streaming.
44
45Each side writes ONLY to its own region and reads ONLY from the other's region.
46The UDS channel carries control/notification messages only (no bulk data).
47
48---
49
50## 2. SHM Binary Format
51
52### 2.1 Memory Layout
53
54```
55Offset 0 REGION_SIZE 2*REGION_SIZE SHM_SIZE
56├── Region 0 ──────────────├── Region 1 ────────────├── Tile Ring ──────────┤
57│ Godot → Blender │ Blender → Godot │ Texture tiles (RGBA8) │
58```
59
60**Constants:**
61- `REGION_SIZE` = 8 MB (default, grows dynamically via `ftruncate`)
62- `TILE_RING_SIZE` = 8 MB
63- `SHM_SIZE` = `2 * REGION_SIZE + TILE_RING_SIZE`
64- `MAX_OBJECTS` = 256
65
66### 2.2 Region Internal Layout
67
68```
69Offset (within region):
70+0x0000 BridgeHeader 64 bytes
71+0x0040 ObjectEntry[0] 32 bytes
72+0x0060 ObjectEntry[1] 32 bytes
73 ...
74+0x0040 + N*32 ObjectEntry[N-1]
75+0x2040 Data blob area (remaining space)
76```
77
78Blob area starts at: `64 + MAX_OBJECTS * 32 = 8256 bytes`
79
80### 2.3 BridgeHeader (64 bytes)
81
82| Offset | Size | Type | Field | Description |
83|--------|------|---------|--------------|----------------------------------------|
84| 0 | 4 | char[4] | magic | `"BGOD"` |
85| 4 | 2 | uint16 | version | Protocol version = 1 |
86| 6 | 1 | uint8 | direction | 0 = G→B, 1 = B→G |
87| 7 | 1 | uint8 | _pad | |
88| 8 | 8 | uint64 | sequence | Monotonically increasing per write |
89| 16 | 4 | uint32 | object_count | Valid entries this frame |
90| 20 | 4 | uint32 | _pad2 | |
91| 24 | 8 | uint64 | frame_number | Frame counter |
92| 32 | 32 | bytes | _reserved | Zeros |
93
94### 2.4 ObjectEntry (32 bytes)
95
96| Offset | Size | Type | Field | Description |
97|--------|------|--------|-------------|-----------------------------------------|
98| 0 | 4 | uint32 | object_id | Unique ID (monotonic from 1) |
99| 4 | 1 | uint8 | object_type | See ObjectType enum |
100| 5 | 1 | uint8 | update_type | See UpdateType enum |
101| 6 | 1 | uint8 | sub_type | Light subtype (0=POINT, 1=SUN, 2=SPOT) |
102| 7 | 1 | uint8 | _pad | |
103| 8 | 4 | uint32 | data_offset | Byte offset to blob (region-relative) |
104| 12 | 4 | uint32 | data_size | Total blob size in bytes |
105| 16 | 4 | uint32 | sequence | Export sequence when written |
106| 20 | 12 | bytes | _reserved | Zeros |
107
108### 2.5 Enums
109
110**ObjectType:**
111| Value | Name | Blender Type | Godot Node Type |
112|-------|------------|-------------------------|-----------------------|
113| 0 | OBJ_MESH | MESH | MeshInstance3D |
114| 1 | OBJ_LIGHT | LIGHT | OmniLight3D / DirectionalLight3D / SpotLight3D |
115| 2 | OBJ_CAMERA | CAMERA | Camera3D |
116| 3 | OBJ_EMPTY | Everything else | Node3D |
117
118**UpdateType:**
119| Value | Name | Blob contents |
120|-------|------------------|------------------------------------------|
121| 0 | UPDATE_TRANSFORM | name + transform |
122| 1 | UPDATE_VERTEX | name + transform + geometry |
123| 3 | UPDATE_FULL | name + transform + geometry + material |
124| 4 | UPDATE_DELETE | entry only (data_offset=0, data_size=0) |
125
126Note: UPDATE_MATERIAL (2) is defined but not used in current implementation.
127
128---
129
130## 3. Object Blob Format
131
132### 3.1 Name Prefix (all objects)
133
134```
135[uint8 name_len] 1 byte
136[char[] name_bytes] name_len bytes (UTF-8)
137[uint8[] padding] 0-3 bytes to 4-byte align
138```
139
140### 3.2 Transform (all objects, 64 bytes)
141
1424x4 float32 matrix in **row-major** order, in **Blender coordinate space** (Z-up, right-handed).
143
144### 3.3 Mesh Geometry (OBJ_MESH, UPDATE_VERTEX or UPDATE_FULL)
145
146```
147[uint32 vert_count] 4 bytes
148[float32 positions[vert_count * 3]] vert_count * 12 bytes (x, y, z per vertex)
149[uint32 has_normals] 4 bytes (0 or 1)
150 if has_normals:
151 [float32 normals[vert_count * 3]] vert_count * 12 bytes
152[uint32 idx_count] 4 bytes
153 if idx_count > 0:
154 [int32 indices[idx_count]] idx_count * 4 bytes
155[uint32 smooth_count] 4 bytes
156 if smooth_count > 0:
157 [uint8 smooth_flags[smooth_count]] 1 byte per triangle (0=flat, 1=smooth)
158[uint32 uv_count] 4 bytes
159 if uv_count > 0:
160 [float32 uvs[uv_count * 2]] uv_count * 8 bytes (u, v per vertex)
161```
162
163### 3.4 Material (OBJ_MESH, UPDATE_FULL only, after geometry)
164
165```
166[uint32 has_material] 4 bytes (0 or 1)
167 if has_material:
168 [float32 base_color[4]] 16 bytes (R, G, B, A)
169 [float32 roughness] 4 bytes
170 [float32 metallic] 4 bytes
171 [float32 emission_color[4]] 16 bytes (R, G, B, A)
172 [float32 emission_strength] 4 bytes
173 ────────
174 44 bytes total material payload
175```
176
177---
178
179## 4. Coordinate Conversion
180
181### 4.1 Axis Mapping
182
183```
184Blender (Z-up, right-handed) Godot (Y-up, right-handed)
185 X → X X → X
186 Y → -Z Y → Z (Blender)
187 Z → Y Z → -Y (Blender)
188```
189
190### 4.2 Position Conversion
191
192**Export (Godot → Blender):**
193```
194blender.x = godot.x
195blender.y = -godot.z
196blender.z = godot.y
197```
198
199**Import (Blender → Godot):**
200```
201godot.x = blender.x
202godot.y = blender.z
203godot.z = -blender.y
204```
205
206### 4.3 Basis/Transform Conversion
207
208Similarity transform `M * B * M^{-1}` where M swaps Y↔Z:
209```
210M = [1, 0, 0]
211 [0, 0, -1]
212 [0, 1, 0]
213```
214
215### 4.4 Camera/Light Look Compensation
216
217Cameras and directional/spot lights use -Z as forward in Godot but +Y in Blender.
218A 90-degree X rotation is applied/removed during export/import.
219
220---
221
222## 5. UDS Message Protocol
223
224### 5.1 Wire Format
225
226```
227[uint32 LE length][payload bytes]
228```
229
230Maximum message size: 16 MB. All payloads are UTF-8 text.
231
232### 5.2 Messages: Blender → Godot
233
234| Message | Description |
235|--------------------------------------|------------------------------------------|
236| `hello` | Handshake request |
237| `updated` | Changes written to Region 1 |
238| `shm_resized:{total},{region}` | SHM grown, remap needed |
239| `request_texture:{name},{w},{h}` | Register SVT texture |
240| `tile_ready:{id},{tx},{ty},{mip},{offset},{size}` | Tile data in ring buffer |
241| `finalize` | Save scene to disk |
242
243### 5.3 Messages: Godot → Blender
244
245| Message | Description |
246|--------------------------------------|------------------------------------------|
247| `connected` | Handshake reply |
248| `scene_exported` | Full scene written to Region 0 |
249| `updated` | Incremental changes written to Region 0 |
250| `texture_registered:{name},{id}` | SVT texture registered with ID |
251| `finalized` | Scene save succeeded |
252| `finalize_error` | Scene save failed |
253
254### 5.4 Connection Lifecycle
255
2561. Godot creates SHM + listens on UDS socket
2572. Blender connects, sends `hello`
2583. Godot resets import sequence, cleans stale objects, replies `connected`
2594. If Godot has existing scene, sends `scene_exported`
2605. Bidirectional sync via `updated` notifications
2616. Texture streaming via `request_texture` → `texture_registered` → `tile_ready`
262
263---
264
265## 6. Material Property Mapping
266
267| Property | Blender Source | Godot Source |
268|--------------------|------------------------------------------|-----------------------------------------|
269| Base Color (RGBA) | Principled BSDF → Base Color default | StandardMaterial3D → albedo |
270| Roughness | Principled BSDF → Roughness default | StandardMaterial3D → roughness |
271| Metallic | Principled BSDF → Metallic default | StandardMaterial3D → metallic |
272| Emission (RGBA) | Principled BSDF → Emission Color default | StandardMaterial3D → emission |
273| Emission Strength | Principled BSDF → Emission Strength | StandardMaterial3D → emission_energy |
274
275**Not synced** (future work): Specular, IOR, Transmission, Subsurface, Clearcoat,
276Anisotropic, Alpha/transparency, Normal maps, Roughness maps, Metallic maps.
277
278**Constraint**: Only `active_material` (slot 0) is synced. Multi-material meshes
279lose materials beyond slot 0. Only surface 0 of Godot meshes is exported.
280
281---
282
283## 7. Virtual Texture System
284
285### 7.1 Architecture
286
287The SVT system implements Sparse Virtual Texturing (SVT):
288
289```
290Blender Image → TileBaker → SHM Ring → Godot SVT Node
291 ├─ TileCache (LRU, 32x32 grid)
292 ├─ PageTable (128x128 mip pyramid)
293 ├─ Atlas (4096x4096 RGBA8 GPU texture)
294 └─ svt_lookup.gdshader
295```
296
297### 7.2 Known Limitations (CRITICAL)
298
299**L1: Single Virtual Address Space**
300The PageTable maps `(mip, tile_x, tile_y)` → `PhysicalLocation` with NO texture_id
301dimension. All registered textures share one page table. Multiple textures with
302overlapping UV-space tile coordinates will overwrite each other. **The SVT system
303currently supports at most ONE texture correctly.**
304
305**L2: No LOD in Shader**
306The shader always samples at mip 0. The fallback mechanism (coarse mip filling fine
307entries) provides degradation but is not true LOD selection.
308
309**L3: Ring Buffer Overflow**
310The 8 MB ring holds ~128 tiles (64 KB each). Textures larger than ~1400x1400 pixels
311produce more tiles than the ring can hold, causing overwrites before Godot reads them.
312
313**L4: No Feedback Loop**
314The feedback buffer and request queue exist but no GPU feedback pass is implemented.
315Tile loading is entirely push-based from Blender.
316
317**L5: Finalization is Broken for Multi-Texture**
318`finalize_scene()` applies the first available texture to ALL SVT meshes regardless
319of which texture each mesh actually uses.
320
321### 7.3 Tile Ring Format
322
323```
324[SHM offset 2*REGION_SIZE ... SHM_SIZE]
325Raw RGBA8 tile data, 128x128x4 = 65536 bytes per tile.
326Circular write pointer, no read acknowledgment.
327```
328
329---
330
331## 8. Data Not Currently Synced
332
333| Category | Blender Data | Godot Equivalent |
334|-----------------------|---------------------------|---------------------------|
335| Hierarchy | Parent-child, Collections | Node tree |
336| Armatures | Bones, pose, constraints | Skeleton3D |
337| Shape Keys | Blend shapes | Blend shapes |
338| Multi-Material | Material slots 1+ | Surfaces 1+ |
339| UV Layers 2+ | uv_layers[1:] | ARRAY_TEX_UV2 |
340| Vertex Colors | color_attributes | ARRAY_COLOR |
341| Light Properties | color, energy, range | OmniLight3D params |
342| Camera Properties | FOV, clip near/far | Camera3D params |
343| Animations | Actions, keyframes, NLA | AnimationPlayer |
344| Texture Maps | Normal, roughness maps | StandardMaterial3D maps |
345| Visibility | hide_render, hide_viewport| Node3D.visible |
346| Curves/Text/Volumes | Spline data, text content | Path3D, etc. |
347| Physics | Rigid body, collision | PhysicsBody3D |
348
349---
350
351## 9. What Currently Works (Verified Features)
352
3531. **Transform sync (bidirectional)** — position, rotation, scale via world matrix
3542. **Mesh geometry sync (bidirectional)** — triangulated vertices, normals, UVs
3553. **Basic PBR material sync (bidirectional)** — 5 scalar/color properties
3564. **Object create/delete tracking** — unique IDs, automatic cleanup
3575. **Multi-object type support** — meshes, cameras, lights, empties
3586. **Winding order correction** — handedness change handled correctly
3597. **Smooth/flat shading** — via per-corner normals (B→G) and smooth flags (G→B)
3608. **Geometry Nodes support** — instance realization + combined mesh export
3619. **SHM auto-resize** — dynamic growth for large meshes
36210. **Auto-reconnect** — Blender reconnects when Godot restarts
363
364---
365
366## 10. Known Bugs
367
368### B1: Reconnect Sequence Mismatch (Fixed in v0.16.1)
369After SHM resize triggers reconnect, new ChangeDetector started at seq=0 while
370Godot retained old `_last_import_sequence`. First flush silently skipped.
371**Fix**: Reset `_last_import_sequence` on hello + preserve detector across reconnects.
372
373### B2: Reconnect ID Loss (Fixed in v0.16.1)
374New ChangeDetector lost `_name_to_id` for Blender-origin objects.
375**Fix**: Reuse detector instance + merge `set_id_map` additively.
376
377### B3: SVT Applied to All Meshes (Fixed in Phase 0)
378When SVT has registered textures, SVT ShaderMaterial is applied to ALL meshes,
379blocking StandardMaterial3D. Meshes without image textures appear with fallback
380color instead of their actual Blender material colors.
381**Root cause**: No per-mesh texture discrimination at the shader level.
382**Fix**: Disabled SVT material application in `import_changes()`. Always use
383StandardMaterial3D. SVT materials will return in Phase 2 with per-texture page tables.
384
385### B4: Reconnect Texture Loss (Fixed in Phase 0)
386After Godot restart, `_baker._texture_ids` is stale. `_scan_material_textures()`
387skips re-registration. Textures never flow.
388**Fix**: Clear `_baker._texture_ids` on reconnect to force re-registration
389with fresh Godot VT instance.
390
391### B5: texture_id Protocol Mismatch (Fixed in Phase 0)
392The v0.16.1 commit added a 4-byte `texture_id` field to the material blob,
393changing the format from 44 to 48 bytes. But this field is never correctly
394consumed because:
395- The `vert_count==0` hack for material-only re-flush is unreachable (C++ skips
396 the entire material block when vert_count==0)
397- The SVT shader has no texture_id concept
398This is dead code that adds protocol complexity for no benefit.
399**Fix**: Reverted to 44-byte material format. Removed all texture_id
400read/write code from C++ (3 write sites, 1 read site), Python detector,
401and Python importer.