Pārlūkot izejas kodu

meta-rig input use overlay

Joseph Brandenburg 1 nedēļu atpakaļ
vecāks
revīzija
6f802c1e4e
6 mainītis faili ar 98 papildinājumiem un 42 dzēšanām
  1. 21 8
      base_definitions.py
  2. 28 20
      execution/overlays.py
  3. 4 2
      misc_nodes.py
  4. 2 1
      misc_nodes_ui.py
  5. 34 11
      socket_definitions.py
  6. 9 0
      utilities/utilities.py

+ 21 - 8
base_definitions.py

@@ -10,7 +10,7 @@ from .utilities.utilities import (prRed, prGreen, prPurple, prWhite,
                               wrapRed, wrapGreen, wrapPurple, wrapWhite,
                               wrapOrange,)
 
-from .utilities.utilities import get_socket_maps, relink_socket_map, do_relink
+from .utilities.utilities import get_socket_maps, get_socket_overlay_info, do_relink
 from .mantis_dataclasses import MantisSocketTemplate
 
 MANTIS_VERSION_MAJOR=0
@@ -557,6 +557,7 @@ def node_group_update(node, force = False):
            (node.id_data.is_executing == True) or \
            (node.id_data.is_exporting == True):
             return
+
     # note: if (node.id_data.is_exporting == True) I need to be able to update so I can make links.
     if node.node_tree is None:
         node.inputs.clear(); node.outputs.clear()
@@ -578,10 +579,6 @@ def node_group_update(node, force = False):
         for i, socket in enumerate(collection):
             map[socket.identifier]=i
 
-    if node.node_tree is None:
-        node.inputs.clear(); node.outputs.clear()
-        node.id_data.do_live_update = toggle_update
-        return
     found_in, found_out = [], []
     update_input, update_output = False, False
     for item in node.node_tree.interface.items_tree:
@@ -636,6 +633,10 @@ def node_group_update(node, force = False):
     if not (update_input or update_output):
         node.id_data.do_live_update = toggle_update
         return
+    
+    # At this point, we know we're gonna change things so let's do this
+    # remember what has Overlay turned on and what the value is
+    overlay_state = get_socket_overlay_info(node) 
 
     if update_input or update_output and (socket_maps is not None):
         if update_input :
@@ -677,6 +678,7 @@ def node_group_update(node, force = False):
             socket_collection = node.inputs if is_input else node.outputs
             counter = input_index if is_input else output_index
             reorder_collection = reorder_me_input if is_input else reorder_me_output
+            socket = None
             if socket_map:
                 if item.identifier in socket_map.keys():
                     socket = relink_socket_map_add_socket(node, socket_collection, item, item.in_out)
@@ -733,6 +735,11 @@ def node_group_update(node, force = False):
                     to_index+=1
                 collection.move(i, to_index)
 
+        # restore the overlay state, whether it is using Overlay or not and the overlay text
+        for socket in node.inputs:
+            if socket and (overlay_text := overlay_state.get(socket.identifier)):
+                socket.use_overlay=True; socket.overlay_text = overlay_text
+        
         # at this point there is no wildcard socket
         if socket_map_in and '__extend__' in socket_map_in.keys():
             do_relink(node, None, socket_map_in, in_out='INPUT', parent_name='Constant' )
@@ -1127,15 +1134,19 @@ class MantisNode:
         return change_handled
 
     def ui_modify_socket(self, ui_socket, socket_name=None) -> bool:
+        from .execution.overlays import node_scan_overlays, get_socket_idname_data_type, parser_handle_text
         """ Handle changes in the node's UI. Updates the rig if possible."""
         # Always update the node's data
         change_handled=False
         if socket_name is None: socket_name = ui_socket.name
         value = ui_socket.default_value
+        if hasattr(ui_socket, 'use_overlay') and ui_socket.use_overlay:
+            value = ui_socket.overlay_text
+            if get_socket_idname_data_type(ui_socket.bl_idname) != str:
+                value = parser_handle_text(value)
         # apply overlay variables here so this updates predictably
         if (isinstance(value, str) and
                 (self.signature, ui_socket.identifier) in self.mContext.tagged_for_overlay):
-            from .execution.overlays import node_scan_overlays
             value = node_scan_overlays(self, ui_socket.identifier)
         # TODO: I need to make Node Groups apply overlays downstream, but since
         #       they don't update live anyway, no need just yet
@@ -1184,7 +1195,7 @@ class MantisNode:
 
     def fill_parameters(self, ui_node=None)  -> None:
         from mathutils import Matrix
-        from .execution.overlays import handle_overlay_detect
+        from .execution.overlays import handle_overlay_detect, get_socket_idname_data_type, parser_handle_text
         if not ui_node:
             if ( (self.signature[0] in FILL_PARAMETERS_SKIP_ROOTS) or
                 (self.signature[-1] in FILL_PARAMETERS_SKIP_NAMES) ): # I think this is harmless
@@ -1209,7 +1220,9 @@ class MantisNode:
                                                     tuple(v[12:16]) ))
                 elif (value := node_socket.default_value) is not None:
                     if hasattr(node_socket, 'use_overlay') and node_socket.use_overlay:
-                        value = "$"+node_socket.overlay_text
+                        value = node_socket.overlay_text
+                        if get_socket_idname_data_type(node_socket.bl_idname) != str:
+                            value = parser_handle_text(value)
                         # TODO: the overlay system should use a math parser for numerical types
                         #       that treats strings as variable names
                         # but for now I am just gonna hamfist it like this lol

+ 28 - 20
execution/overlays.py

@@ -1,4 +1,17 @@
 
+# For now, this is not yet implemented. lol
+def parser_handle_text(text):
+    return '$' + text
+
+def get_socket_idname_data_type(socket_idname):
+    data_type = str
+    match socket_idname:
+        case "IntSocket" | "UnsignedIntSocket":
+            data_type = int
+        case "FloatSocket" | "FloatPositiveSocket" | "FloatFactorSocket" | "FloatAngleSocket":
+            data_type = float
+    return (data_type)
+
 # If a $ character is in a value, it is an overlay request
 # in the root tree, supply it immediately, otherwise tag this node
 # once we finish pulling data from the tree we handle the overlay.
@@ -9,30 +22,27 @@ def handle_overlay_detect(node, socket_id, socket_idname, value) -> str:
     # detect if it is in the root tree, if so, handle immediately
     out_value = value
     # what this recieves is determined by its idname
-    socket_type = str
-    match socket_idname:
-        case "IntSocket" | "UnsignedIntSocket":
-            socket_type = int
-        case "FloatSocket" | "FloatPositiveSocket" | "FloatFactorSocket" | "FloatAngleSocket":
-            socket_type = float
+
+    socket_data_type = get_socket_idname_data_type(socket_idname)
+
     # ONLY for root node trees, we can just directly fill this.
     if node.signature[0] is None and len(node.signature) == 2:
         for key, interface_item in root_items_tree.items():
             if interface_item.item_type != "SOCKET": continue
             if interface_item.in_out != "INPUT": continue
             # then what the interface provides is determined by its idname in turn
-            # NOTE I'm not 100% sure checking the socket_type is the right thing to do here
+            # NOTE I'm not 100% sure checking the socket_data_type is the right thing to do here
             match interface_item.bl_socket_idname:
                 case "IntSocket" | "BooleanSocket":
-                    if socket_type in {int, float} and '$'+key == out_value:
+                    if socket_data_type in {int, float} and '$'+key == out_value:
                         # TODO this is the wrong way to do this, replace this with parser later
-                        out_value = socket_type(interface_item.default_int)
+                        out_value = socket_data_type(interface_item.default_int)
                 case "FloatSocket":
-                    if socket_type in {int, float} and '$'+key == out_value:
-                        out_value = socket_type(interface_item.default_float)
+                    if socket_data_type in {int, float} and '$'+key == out_value:
+                        out_value = socket_data_type(interface_item.default_float)
                 case "StringSocket" | 'BoneCollectionSocket':
-                    if socket_type == str:
-                        out_value = socket_type(out_value.replace("$"+key, interface_item.default_string))
+                    if socket_data_type == str:
+                        out_value = socket_data_type(out_value.replace("$"+key, interface_item.default_string))
 
     else: # since it is not root, we have to tag it.
         mContext.tagged_for_overlay.append((node.signature, socket_id))
@@ -69,8 +79,10 @@ def node_scan_overlays(node, socket_id,):
                 raise KeyError(f"Socket not found by ID {socket_id} in node {node.ui_signature}\n"
                             f"Candidates: {[socket.identifier for socket in ui_node.inputs]}")
         value = socket.default_value # get the ACTUAL UI value
-        if hasattr(socket, 'overlay_text'): # TODO this is all wrong but for now
-            value = "$"+socket.overlay_text # I am not implementing the Parser feature
+        if hasattr(socket, 'overlay_text'):
+            value = socket.overlay_text
+            if get_socket_idname_data_type(socket.bl_idname) != str:
+                value = parser_handle_text(value)
     else: # since it doesn't have a UI node
         value = node.parameters[socket_id]
     
@@ -78,11 +90,7 @@ def node_scan_overlays(node, socket_id,):
     if isinstance(node, (AutoGenNode) ):
         overlay_data_type = type(node.parameters[list(node.outputs.values())[0].name])
     else: # we have the UI socket
-        match socket.bl_idname:
-            case "IntSocket" | "UnsignedIntSocket":
-                overlay_data_type = int
-            case "FloatSocket" | "FloatPositiveSocket" | "FloatFactorSocket" | "FloatAngleSocket":
-                overlay_data_type = float
+        overlay_data_type = get_socket_idname_data_type(socket.bl_idname)
     
     if not isinstance(value, str): return
     # First step: get the interface nodes and the overlay keys

+ 4 - 2
misc_nodes.py

@@ -370,12 +370,14 @@ class SimpleInputNode(MantisMiscNode):
         self.prepared, self.executed = True, True
 
     def fill_parameters(self, ui_node=None):
-        from .execution.overlays import handle_overlay_detect
+        from .execution.overlays import handle_overlay_detect, get_socket_idname_data_type, parser_handle_text
         ui_outputs = ui_node.outputs
         node_socket = ui_outputs[0] # this is a simple input node.
         if (value := node_socket.default_value) is not None:
             if hasattr(node_socket, 'use_overlay') and node_socket.use_overlay:
-                value = '$' + node_socket.overlay_text
+                value = node_socket.overlay_text
+                if get_socket_idname_data_type(node_socket.bl_idname) != str:
+                    value = parser_handle_text(value)
             if isinstance(value, str) and "$" in value:
                 value = handle_overlay_detect(self, node_socket.identifier, node_socket.bl_idname, value)
             self.parameters[node_socket.name] = value

+ 2 - 1
misc_nodes_ui.py

@@ -368,10 +368,11 @@ class UtilityMetaRigNode(Node, MantisMiscNode):
     
     def display_update(self, parsed_tree, context):
         mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
+        using_overlay = self.inputs['Meta-Armature'].use_overlay
         if mantis_node:
             self.armature= mantis_node.evaluate_input("Meta-Armature")
             self.pose_bone= mantis_node.evaluate_input("Meta-Bone")
-        if not self.armature:
+        if not self.armature and not using_overlay:
             self.inputs["Meta-Bone"].hide=True
         else:
             self.inputs["Meta-Bone"].hide=False

+ 34 - 11
socket_definitions.py

@@ -44,29 +44,43 @@ class MantisSocket(parent_classes):
         return self.color
     @classmethod
     def draw_color_simple(self):
-        return self.color_simple    
+        return self.color_simple
+
+def overlay_toggle_update(self, context):
+    if self.node.id_data.bl_idname in {'MantisTree', 'SchemaTree'}:
+        parsed_tree = self.node.id_data.parsed_tree
+        self.node.display_update(parsed_tree=parsed_tree, context=context)
+        # and we need to mark the node as un-prepared.
+        # For now I am not gonna try to resolve the overlay value from the UI.
+        from .base_definitions import get_signature_from_edited_tree
+        mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
+        mantis_node.reset_execution_recursive()
+    
+def overlay_text_update(self, context):
+    if self.node.id_data.bl_idname in {'MantisTree', 'SchemaTree'}:
+        parsed_tree = self.node.id_data.parsed_tree
+        from .base_definitions import get_signature_from_edited_tree
+        mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
+        mantis_node.ui_modify_socket(self, self.name)
 
 class MantisSocketOverlayEnabled(MantisSocket):
     # this class just needs a couple properties
-    use_overlay: bpy.props.BoolProperty( default=False )
-    overlay_text: bpy.props.StringProperty( default="", )
+    use_overlay: bpy.props.BoolProperty( default=False, update=overlay_toggle_update)
+    overlay_text: bpy.props.StringProperty( default="", update=overlay_text_update)
     def draw(self, context, layout, node, text):
         is_simple_out = ((hasattr(self, "default_value")) and hasattr(self, "input") and getattr(self, "input"))
         if self.is_linked and not is_simple_out:
             layout.label(text=getattr(self, "display_text", "") )
         elif hasattr(self, "default_value"):
-            has_overlay = hasattr(self, "overlay_text") and hasattr(self, "use_overlay")
-            row = layout.row(align=True) if has_overlay else layout
+            row = layout.row(align=True)
             # We'll show the property or the overlay field here
-            prop_field = "overlay_text" if (has_overlay and self.use_overlay) else "default_value"
+            prop_field = "overlay_text" if self.use_overlay else "default_value"
             row.prop(self, prop_field,text=self.name)
             # and the button to choose whether or not to use the overlay field
-            if has_overlay: row.prop(self, "use_overlay", text="", icon='CONSOLE', toggle=True)
+            row.prop(self, "use_overlay", text="", icon='CONSOLE', toggle=True)
         else:
             layout.label(text=self.name)
 
-
-
 from .utilities.utilities import (prRed, prGreen, prPurple, prWhite,
                               prOrange,
                               wrapRed, wrapGreen, wrapPurple, wrapWhite,
@@ -1297,7 +1311,7 @@ def poll_is_armature(self, obj):
 # def poll_is_armature(self, obj):
     # return obj.type == "ARMATURE"
 
-class EnumMetaRigSocket(MantisSocket):
+class EnumMetaRigSocket(MantisSocketOverlayEnabled):
     '''Custom node socket type'''
     bl_idname = 'EnumMetaRigSocket'
     bl_label = "Meta Rig"
@@ -1322,7 +1336,16 @@ class EnumMetaRigSocket(MantisSocket):
         if self.is_output:
             layout.label(text=self.name)
         elif not (self.is_linked):
-            layout.prop_search(data=self, property="search_prop", search_data=bpy.data, search_property="objects", text="", icon="OUTLINER_OB_ARMATURE", results_are_suggestions=True)
+            row = layout.row(align=True)
+            # We'll show the property or the overlay field here
+            if self.use_overlay:
+                row.prop(self, "overlay_text",text=self.name)
+            else:
+                row.prop_search(data=self, property="search_prop", search_data=bpy.data,
+                                   search_property="objects", text="", icon="OUTLINER_OB_ARMATURE",
+                                   results_are_suggestions=True)
+            # and the button to choose whether or not to use the overlay field
+            row.prop(self, "use_overlay", text="", icon='CONSOLE', toggle=True)
         elif hasattr(self.node, "armature"):
             layout.label(text=self.node.armature)
             # TODO: we should actually use the parsed tree to query this info directly, since this socket may belong to a node group in/out

+ 9 - 0
utilities/utilities.py

@@ -131,6 +131,15 @@ def set_bone_selection(pose_bone, selection_state):
 # groups and changing sockets -- this is used extensively by Schema.
 ##################################################################################################
 
+# store the overlay state of the node's sockets to ensure they don't reset.
+# The socket will only be in the overlay map if it is set to use overlay
+def get_socket_overlay_info(node):
+    overlay_state = {} # we only need the inputs for this
+    for socket in node.inputs:
+        if hasattr(socket, 'use_overlay') and socket.use_overlay:
+            overlay_state[socket.identifier] = socket.overlay_text
+    return overlay_state
+
 # this one returns None if there is an error.
 def get_socket_maps(node, force=False):
     maps = [{}, {}]