| … | … | @@ -11,7 +11,7 @@ Usage: |
| 11 | 11 | |
| 12 | 12 | import bpy |
| 13 | 13 | |
| 14 | ADDON_VERSION = "0.13.3" | |
| 14 | ADDON_VERSION = "0.13.4" | |
| 15 | 15 | |
| 16 | 16 | # Support hot-reload (F3 > Reload Scripts) |
| 17 | 17 | if "shm_protocol" in locals(): |
| … | … | @@ -172,6 +172,14 @@ def _poll_timer_impl(generation: int): |
| 172 | 172 | if _importer is not None: |
| 173 | 173 | _importer._id_to_name.update(_detector.get_id_map()) |
| 174 | 174 | |
| 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 | ||
| 175 | 183 | # Flush any pending tile_ready messages from the baker |
| 176 | 184 | if _baker is not None: |
| 177 | 185 | for tile_msg in _baker.get_pending_messages(): |
| … | … | @@ -2,7 +2,7 @@ schema_version = "1.0.0" |
| 2 | 2 | id = "blender_godot_bridge" |
| 3 | 3 | name = "Blender-Godot Bridge" |
| 4 | 4 | tagline = "Real-time bidirectional scene sync with Godot" |
| 5 | version = "0.13.3" | |
| 5 | version = "0.13.4" | |
| 6 | 6 | blender_version_min = "4.2.0" |
| 7 | 7 | type = "add-on" |
| 8 | 8 | maintainer = "blender-bridge <noreply@localhost>" |
| … | … | @@ -89,6 +89,11 @@ class ChangeDetector: |
| 89 | 89 | # Geometry hash cache: prevents re-flushing unchanged geometry |
| 90 | 90 | # (geometry nodes fire is_updated_geometry every frame even when output is identical) |
| 91 | 91 | 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() | |
| 92 | 97 | |
| 93 | 98 | def register(self): |
| 94 | 99 | """Register the depsgraph handler.""" |
| … | … | @@ -147,6 +152,16 @@ class ChangeDetector: |
| 147 | 152 | """Return the current ID map as {object_id: name} for syncing with SceneImporter.""" |
| 148 | 153 | return {obj_id: name for name, obj_id in self._name_to_id.items()} |
| 149 | 154 | |
| 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 | ||
| 150 | 165 | @staticmethod |
| 151 | 166 | def _hash_transform(mat) -> int: |
| 152 | 167 | """FNV-1a hash of 4×4 matrix floats for change detection.""" |
| … | … | @@ -167,6 +182,13 @@ class ChangeDetector: |
| 167 | 182 | |
| 168 | 183 | for update in depsgraph.updates: |
| 169 | 184 | 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 | ||
| 170 | 192 | if not isinstance(obj, bpy.types.Object): |
| 171 | 193 | continue |
| 172 | 194 |
| … | … | @@ -163,7 +163,7 @@ Error BlenderBridge::start_server(const String &socket_path) { |
| 163 | 163 | _known_instance_ids.clear(); |
| 164 | 164 | _next_object_id = 1; |
| 165 | 165 | |
| 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, ")"); | |
| 167 | 167 | return OK; |
| 168 | 168 | } |
| 169 | 169 |