|
|
@@ -0,0 +1,157 @@
|
|
|
+from .utilities import (prRed, prGreen, prPurple, prWhite,
|
|
|
+ prOrange,
|
|
|
+ wrapRed, wrapGreen, wrapPurple, wrapWhite,
|
|
|
+ wrapOrange,)
|
|
|
+
|
|
|
+# the first step is to get selection syncing working
|
|
|
+import bpy
|
|
|
+from bpy import context
|
|
|
+from .base_definitions import get_signature_from_edited_tree
|
|
|
+
|
|
|
+from bpy.types import Bone, PoseBone
|
|
|
+# just to prevent confusing with Python object:
|
|
|
+from bpy.types import Object as bpy_object
|
|
|
+
|
|
|
+SELECTION_POLL_RATE = 0.01
|
|
|
+ENABLED = True
|
|
|
+cached_selection_state = {}
|
|
|
+
|
|
|
+import bpy
|
|
|
+
|
|
|
+# The following node types represent objects that can be selected.
|
|
|
+WATCH_FOR_SELECTION = [ 'xFormBoneNode', 'xFormArmatureNode',
|
|
|
+ 'xFormGeometryObject', 'xFormObjectInstance', 'xFormCurvePin',
|
|
|
+ 'xFormGetBone', 'InputExistingGeometryObject']
|
|
|
+
|
|
|
+
|
|
|
+def get_signature_from_space(node, space):
|
|
|
+ sig_path=[None,]
|
|
|
+ for item in space.path[:-1]:
|
|
|
+ sig_path.append(item.node_tree.nodes.active.name)
|
|
|
+ return tuple(sig_path+[node.name])
|
|
|
+
|
|
|
+def get_visible_space_data(space_types={'NODE_EDITOR'}):
|
|
|
+ visible_spaces = []
|
|
|
+ for wm in bpy.data.window_managers:
|
|
|
+ for window in wm.windows:
|
|
|
+ for area in window.screen.areas:
|
|
|
+ if area.type in space_types:
|
|
|
+ if area.spaces.active:
|
|
|
+ visible_spaces.append(area.spaces.active)
|
|
|
+
|
|
|
+ return visible_spaces
|
|
|
+
|
|
|
+# Example usage: Get all active 3D Viewport space_data objects
|
|
|
+spaces = get_visible_space_data('VIEW_3D')
|
|
|
+
|
|
|
+for space in spaces:
|
|
|
+ print(f"Found 3D Viewport: shading mode = {space.shading.type}")
|
|
|
+
|
|
|
+
|
|
|
+# TODO: now that I have this working...
|
|
|
+# - Selection State should be tracked and handled on a per-object basis
|
|
|
+# so Mantis should update selection state one item at a time
|
|
|
+# - every object should be kept in the cache at all times.
|
|
|
+# - a deselect event should check only the current tree for empty selection
|
|
|
+# - but deselect should deselect anything that is tracked by Mantis
|
|
|
+# - Mantis node <==> UI node relationships should be cached instead of using the
|
|
|
+# tree cache, and ideally, saved in the Scene object or something
|
|
|
+# - I'll probably do like a JSON or something for that, maybe a pickle
|
|
|
+# - anyhow the important thing is I need a bi-directional dictionary so I can
|
|
|
+# make the selection sync happen both ways
|
|
|
+# - finally, I want to extend this selection sync to show active constraints and modifiers
|
|
|
+# and to link up with viewport gizmo tools.
|
|
|
+
|
|
|
+def timer_update_selection_sync():
|
|
|
+ if not ENABLED: return None
|
|
|
+ root_tree = None
|
|
|
+ tree_path = None
|
|
|
+ parsed_tree = None
|
|
|
+ spaces = get_visible_space_data()
|
|
|
+ if not spaces:
|
|
|
+ return SELECTION_POLL_RATE
|
|
|
+
|
|
|
+ selection_state_start = cached_selection_state.copy()
|
|
|
+ selection_state = cached_selection_state.copy()
|
|
|
+ ui_signatures = list(cached_selection_state.keys())
|
|
|
+ current_tree_selected_total = 0
|
|
|
+
|
|
|
+ # we can't get UI context in this function so we have to checc all of them
|
|
|
+ #maybe the tree itself can load the timer when it is opened in the node editor?
|
|
|
+ for space in spaces:
|
|
|
+ # if we are in the node editor, manage node selection
|
|
|
+ if space.type == "NODE_EDITOR":
|
|
|
+ root_tree = space.path[0].node_tree
|
|
|
+ parsed_tree = root_tree.parsed_tree
|
|
|
+ tree_path = [path_item.node_tree for path_item in space.path]
|
|
|
+ if root_tree is None:
|
|
|
+ return SELECTION_POLL_RATE
|
|
|
+ if root_tree.bl_idname != 'MantisTree':
|
|
|
+ return SELECTION_POLL_RATE
|
|
|
+ active_ui_signature = (get_signature_from_space(tree_path[-1].nodes.active, space))
|
|
|
+ for n in tree_path[-1].nodes:
|
|
|
+ if n.bl_idname not in WATCH_FOR_SELECTION: continue
|
|
|
+ ui_signatures.append( ui_signature := get_signature_from_space(n, space))
|
|
|
+ selection_state[ui_signature] = n.select
|
|
|
+ current_tree_selected_total += int(n.select)
|
|
|
+
|
|
|
+ if parsed_tree is None:
|
|
|
+ return SELECTION_POLL_RATE
|
|
|
+ cached_selection_state.update(selection_state)
|
|
|
+ # We only fire the update if things have changed.
|
|
|
+ if selection_state != selection_state_start:
|
|
|
+
|
|
|
+ from bpy import context
|
|
|
+ view_layer = context.view_layer
|
|
|
+ deselect = bool(current_tree_selected_total)
|
|
|
+ try:
|
|
|
+ update_scene_selection_from_selection_sync(
|
|
|
+ parsed_tree, ui_signatures, active_ui_signature, view_layer,
|
|
|
+ selection_state, deselect)
|
|
|
+ except Exception as e:
|
|
|
+ prRed (e)
|
|
|
+ return SELECTION_POLL_RATE
|
|
|
+ return SELECTION_POLL_RATE
|
|
|
+
|
|
|
+
|
|
|
+# 1 get the selected nodes
|
|
|
+# 2 find mantis nodes that have the ui_signature of the node
|
|
|
+# 3 get their b-objects and select them
|
|
|
+def update_scene_selection_from_selection_sync(
|
|
|
+ parsed_tree, ui_signatures, active_ui_signature, view_layer,
|
|
|
+ selection_state, deselect ):
|
|
|
+ # first check if this is a deselect event.
|
|
|
+ # if it is, do not change the active object!
|
|
|
+ # that makes the armature switch modes, it's annoying
|
|
|
+ deselect=False
|
|
|
+ if sum(selection_state.values()) == 0:
|
|
|
+ deselect=True
|
|
|
+ prWhite("Updating Scene Selection from Node Graph Selection")
|
|
|
+ for mantis_node in parsed_tree.values():
|
|
|
+ this_one_is_active = False
|
|
|
+ if not hasattr(mantis_node, 'bGetObject'): continue
|
|
|
+ if mantis_node.node_type != 'XFORM': continue
|
|
|
+ if mantis_node.ui_signature in ui_signatures:
|
|
|
+ selected = selection_state[mantis_node.ui_signature]
|
|
|
+ if mantis_node.ui_signature == active_ui_signature:
|
|
|
+ this_one_is_active = True
|
|
|
+ ob = mantis_node.bGetObject()
|
|
|
+ if isinstance(ob, PoseBone):
|
|
|
+ if deselect:
|
|
|
+ ob.select = False
|
|
|
+ continue
|
|
|
+ ob.select = selected
|
|
|
+ if this_one_is_active: # set the active bone
|
|
|
+ ob.id_data.data.bones.active = ob.bone
|
|
|
+ # now do its parent object...
|
|
|
+ if selected:
|
|
|
+ if ob.id_data.select_get(view_layer=view_layer) == False:
|
|
|
+ ob.id_data.select_set(True, view_layer=view_layer)
|
|
|
+ if this_one_is_active:
|
|
|
+ view_layer.objects.active = ob.id_data
|
|
|
+ elif isinstance(ob, bpy_object):
|
|
|
+ if ob.select_get(view_layer=view_layer) != selected:
|
|
|
+ ob.select_set(selected, view_layer=view_layer)
|
|
|
+ if this_one_is_active and not deselect:
|
|
|
+ view_layer.objects.active = ob
|
|
|
+
|