| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 |
- from .utilities.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]:
- if item.node_tree.nodes.active:
- sig_path.append(item.node_tree.nodes.active.name)
- else:
- return () # can't be determined, should not happen in normal use.
- if node:
- return tuple(sig_path+[node.name])
- else: # it's also necessary to do this if there is no path at all.
- return ()
- 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
- # - finally, I want to extend this selection sync to show active constraints and modifiers
- # and to link up with viewport gizmo tools.
- # - Serialize this so that it sticks around when saving/loading file
- # Eventually:
- # - sync scene/object selection to node graph. I have the structure in place
- # but I still have to write the selection tracking code.
- # Potential small BUG?
- # - I am not ensuring the base/root tree is the same for the space and the nodes
- # - because I straight up do not store that in the selection_sync dicts
- # - easy to fix, will do it when clearing TODO's here.
- def timer_update_selection_sync():
- try:
- 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()
- 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":
- if not space.path:
- return SELECTION_POLL_RATE # empty tree
- root_tree = space.path[0].node_tree
- if root_tree is None:
- return SELECTION_POLL_RATE
- if root_tree.bl_idname != 'MantisTree':
- return SELECTION_POLL_RATE
- tree_path = [path_item.node_tree for path_item in space.path]
- if len(tree_path[-1].nodes) == 0:
- return SELECTION_POLL_RATE # empty graph
- parsed_tree = root_tree.parsed_tree
- if tree_path[-1].nodes.active is not None:
- active_ui_signature = (get_signature_from_space(tree_path[-1].nodes.active, space))
- else:
- active_ui_signature = None
- for n in tree_path[-1].nodes:
- if n.bl_idname not in WATCH_FOR_SELECTION: continue
- selection_state[ui_signature := get_signature_from_space(n, space)] = 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 = not bool(current_tree_selected_total)
- try:
- update_scene_selection_from_node_graph_selection_sync(
- active_ui_signature, view_layer,
- selection_state, deselect)
- except Exception as e:
- prRed (e)
- return SELECTION_POLL_RATE
- except Exception as e:
- from .__init__ import DOERROR
- if DOERROR:
- print ("Error in Selection Sync")
- print (e)
- raise e
- return SELECTION_POLL_RATE
- from .utilities.utilities import set_bone_selection
- def update_scene_selection_from_node_graph_selection_sync(
- active_ui_signature, view_layer,
- selection_state, deselect ):
- from .base_definitions import selection_sync_node_to_object
- # prWhite("Updating Scene Selection from Node Graph Selection")
- deselect_do_once = []
- for ui_signature, selected in selection_state.items():
- this_one_is_active = False
- if ui_signature == active_ui_signature: this_one_is_active = True
- for object_key in selection_sync_node_to_object.get(ui_signature, []):
- id_data = bpy.data.objects.get(object_key[0])
- if object_key[1]: # we have a sub_value, it's a bone
- if deselect:
- if object_key[0] not in deselect_do_once:
- for pb in id_data.pose.bones:
- set_bone_selection(pb, False)
- deselect_do_once.append(object_key[0])
- else:
- pb = id_data.pose.bones[object_key[1]]
- set_bone_selection(pb, selected)
- if this_one_is_active: # set the active bone
- id_data.data.bones.active = pb.bone
- # now do its parent object...
- if selected:
- id_data.select_set(True, view_layer=view_layer)
- if this_one_is_active:
- view_layer.objects.active = id_data
- else: # or the sub_value is None, this is an object
- ob = id_data
- if deselect: selected = False
- ob.select_set(selected, view_layer=view_layer)
- if this_one_is_active and not deselect:
- view_layer.objects.active = ob
|