OverviewFilesCommitsIssuesDocsReleasesPeers

blender-bridge commits / 24f482c

Live texture editing via depsgraph Image detection (v0.13.4)
ChangeDetector now monitors depsgraph for bpy.types.Image updates (texture paint, image editor modifications). Dirty image names are accumulated in _dirty_images set, then drained by the timer and re-baked via TileBaker on each tick. Tracked images are kept in sync with baker's registered textures. Full image rebake on each edit (~64 tiles for 1024x1024 at mip 0) is manageable at 20Hz polling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
24f482c59cc41b0690971351435511bc5e425668
by plankatron <fleeky@gmail.com> · 2026-03-03T21:06:20.000Z

Changed files (4) +33 -3

blender_addon/__init__.py +9 -1
……@@ -11,7 +11,7 @@ Usage:
1111
1212import bpy
1313
14ADDON_VERSION = "0.13.3"
14ADDON_VERSION = "0.13.4"
1515
1616# Support hot-reload (F3 > Reload Scripts)
1717if "shm_protocol" in locals():
……@@ -172,6 +172,14 @@ def _poll_timer_impl(generation: int):
172172 if _importer is not None:
173173 _importer._id_to_name.update(_detector.get_id_map())
174174
175 # Re-bake any images that were edited (texture paint, image editor)
176 if _detector is not None and _baker is not None:
177 # Keep detector's tracked images in sync with baker's registrations
178 _detector.set_tracked_images(set(_baker._texture_ids.keys()))
179 dirty_images = _detector.drain_dirty_images()
180 for img_name in dirty_images:
181 _baker.bake_full_image(img_name)
182
175183 # Flush any pending tile_ready messages from the baker
176184 if _baker is not None:
177185 for tile_msg in _baker.get_pending_messages():
blender_addon/blender_manifest.toml +1 -1
……@@ -2,7 +2,7 @@ schema_version = "1.0.0"
22id = "blender_godot_bridge"
33name = "Blender-Godot Bridge"
44tagline = "Real-time bidirectional scene sync with Godot"
5version = "0.13.3"
5version = "0.13.4"
66blender_version_min = "4.2.0"
77type = "add-on"
88maintainer = "blender-bridge <noreply@localhost>"
blender_addon/change_detector.py +22 -0
……@@ -89,6 +89,11 @@ class ChangeDetector:
8989 # Geometry hash cache: prevents re-flushing unchanged geometry
9090 # (geometry nodes fire is_updated_geometry every frame even when output is identical)
9191 self._geometry_hashes: dict[str, int] = {}
92 # Dirty images: image names that were edited (texture paint, image editor)
93 # Drained by the timer for re-baking via TileBaker
94 self._dirty_images: set[str] = {}
95 # Known texture image names (set by timer from baker._texture_ids)
96 self._tracked_images: set[str] = set()
9297
9398 def register(self):
9499 """Register the depsgraph handler."""
……@@ -147,6 +152,16 @@ class ChangeDetector:
147152 """Return the current ID map as {object_id: name} for syncing with SceneImporter."""
148153 return {obj_id: name for name, obj_id in self._name_to_id.items()}
149154
155 def set_tracked_images(self, image_names: set[str]):
156 """Set which image names should trigger re-bake on edit."""
157 self._tracked_images = image_names
158
159 def drain_dirty_images(self) -> set[str]:
160 """Return and clear the set of dirty image names."""
161 dirty = self._dirty_images
162 self._dirty_images = set()
163 return dirty
164
150165 @staticmethod
151166 def _hash_transform(mat) -> int:
152167 """FNV-1a hash of 4×4 matrix floats for change detection."""
……@@ -167,6 +182,13 @@ class ChangeDetector:
167182
168183 for update in depsgraph.updates:
169184 obj = update.id
185
186 # Detect image data changes (texture paint, image editor)
187 if isinstance(obj, bpy.types.Image):
188 if obj.name in self._tracked_images:
189 self._dirty_images.add(obj.name)
190 continue
191
170192 if not isinstance(obj, bpy.types.Object):
171193 continue
172194
src/blender_bridge.cpp +1 -1
……@@ -163,7 +163,7 @@ Error BlenderBridge::start_server(const String &socket_path) {
163163 _known_instance_ids.clear();
164164 _next_object_id = 1;
165165
166 UtilityFunctions::print("BlenderBridge v0.13.3: Server started (shm=", _shm_name, ", sock=", socket_path, ", regions=2x", (int)_region_size, ")");
166 UtilityFunctions::print("BlenderBridge v0.13.4: Server started (shm=", _shm_name, ", sock=", socket_path, ", regions=2x", (int)_region_size, ")");
167167 return OK;
168168}
169169