| … | … | @@ -166,9 +166,23 @@ class Session: |
| 166 | 166 | self._dirty_params = False |
| 167 | 167 | self._dirty_weights = False |
| 168 | 168 | |
| 169 | # A node graph, when one is bound, becomes the source of truth for the |
| 170 | # iterators. Re-read only when Blender says it changed - walking the tree |
| 171 | # every redraw would be wasted work and would fight the panel sliders. |
| 172 | self.tree_name = None |
| 173 | self._dirty_graph = False |
| 174 | |
| 169 | 175 | self._build_buffers() |
| 170 | 176 | self._resize(64, 64) |
| 171 | 177 | |
| 178 | def bind_tree(self, name: str | None): |
| 179 | """Makes a node tree drive this session's iterators.""" |
| 180 | self.tree_name = name |
| 181 | self._dirty_graph = name is not None |
| 182 | |
| 183 | def mark_graph_dirty(self): |
| 184 | self._dirty_graph = True |
| 185 | |
| 172 | 186 | def mark_dirty(self, kind: str): |
| 173 | 187 | """ |
| 174 | 188 | Flags what a UI edit invalidated. |
| … | … | @@ -214,6 +228,26 @@ class Session: |
| 214 | 228 | iterator.vec3_params = [tuple(item.vec3_values[i * 3:i * 3 + 3]) |
| 215 | 229 | for i in range(len(iterator.vec3_params))] |
| 216 | 230 | |
| 231 | def _apply_graph(self): |
| 232 | """Pulls the bound node graph onto the document.""" |
| 233 | if not self._dirty_graph or self.tree_name is None: |
| 234 | return |
| 235 | self._dirty_graph = False |
| 236 | tree = bpy.data.node_groups.get(self.tree_name) |
| 237 | if tree is None: |
| 238 | self.tree_name = None |
| 239 | return |
| 240 | import ifs_nodes |
| 241 | try: |
| 242 | ifs_nodes.apply_tree(tree, self.doc, self.library) |
| 243 | except ValueError: |
| 244 | # Iterator count no longer matches - the graph is for another |
| 245 | # document. Unbind rather than render a mismatch. |
| 246 | self.tree_name = None |
| 247 | return |
| 248 | # A graph edit can change topology and weights, so assume the most. |
| 249 | self.mark_dirty('weights') |
| 250 | |
| 217 | 251 | def _rebuild_dirty(self): |
| 218 | 252 | if not self._dirty_params: |
| 219 | 253 | return |
| … | … | @@ -368,6 +402,7 @@ class Session: |
| 368 | 402 | settings = getattr(context.scene, "ifs", None) |
| 369 | 403 | if settings is not None and settings.iterators: |
| 370 | 404 | self.sync_from_scene(settings) |
| 405 | self._apply_graph() |
| 371 | 406 | self._rebuild_dirty() |
| 372 | 407 | |
| 373 | 408 | space = context.space_data |
| … | … | @@ -456,6 +491,10 @@ def start(document_path, iters_per_frame=400): |
| 456 | 491 | global _session |
| 457 | 492 | stop() |
| 458 | 493 | session = Session(document_path, iters_per_frame) |
| 494 | for tree in bpy.data.node_groups: |
| 495 | if tree.bl_idname == 'IFSNodeTree': |
| 496 | session.bind_tree(tree.name) |
| 497 | break |
| 459 | 498 | session.handle = bpy.types.SpaceView3D.draw_handler_add( |
| 460 | 499 | _draw_callback, (session,), 'WINDOW', 'POST_PIXEL') |
| 461 | 500 | _session = session |
| … | … | @@ -481,6 +520,31 @@ def stop(): |
| 481 | 520 | _session = None |
| 482 | 521 | |
| 483 | 522 | |
| 523 | @bpy.app.handlers.persistent |
| 524 | def _on_depsgraph_update(scene, depsgraph): |
| 525 | """Flags the session when its bound node tree is edited.""" |
| 526 | if _session is None or _session.tree_name is None: |
| 527 | return |
| 528 | for update in depsgraph.updates: |
| 529 | if getattr(update.id, "name", None) == _session.tree_name: |
| 530 | _session.mark_graph_dirty() |
| 531 | for window in bpy.context.window_manager.windows: |
| 532 | for area in window.screen.areas: |
| 533 | if area.type == 'VIEW_3D': |
| 534 | area.tag_redraw() |
| 535 | return |
| 536 | |
| 537 | |
| 538 | def install_handlers(): |
| 539 | if _on_depsgraph_update not in bpy.app.handlers.depsgraph_update_post: |
| 540 | bpy.app.handlers.depsgraph_update_post.append(_on_depsgraph_update) |
| 541 | |
| 542 | |
| 543 | def remove_handlers(): |
| 544 | if _on_depsgraph_update in bpy.app.handlers.depsgraph_update_post: |
| 545 | bpy.app.handlers.depsgraph_update_post.remove(_on_depsgraph_update) |
| 546 | |
| 547 | |
| 484 | 548 | def is_running(): |
| 485 | 549 | return _session is not None |
| 486 | 550 | |