Prechádzať zdrojové kódy

add node: get existing bone

this new node lets you get a bone from the tree
it is needed for referencing existing rigs in the scene
for example, for retargeting from the deform bones to the controls.

make sure to read the special thank-you note!
Joseph Brandenburg 7 mesiacov pred
rodič
commit
497b7a9bd5
5 zmenil súbory, kde vykonal 91 pridanie a 18 odobranie
  1. 1 0
      __init__.py
  2. 20 18
      socket_definitions.py
  3. 38 0
      xForm_nodes.py
  4. 23 0
      xForm_nodes_ui.py
  5. 9 0
      xForm_socket_templates.py

+ 1 - 0
__init__.py

@@ -80,6 +80,7 @@ input_category=[
             NodeItem("InputMatrixNode"),
             NodeItem("InputWidget"),
             NodeItem("InputExistingGeometryObject"),
+            NodeItem("xFormGetBone"),
             NodeItem("InputExistingGeometryData"),
             NodeItem("UtilityDeclareCollections"),
             NodeItem("InputThemeBoneColorSets"),

+ 20 - 18
socket_definitions.py

@@ -215,6 +215,8 @@ def TellClasses() -> List[MantisSocket]:
 
              EnumMetaRigSocket,
              EnumMetaBoneSocket,
+             EnumArmature,
+             EnumExistingBoneSocket,
              EnumCurveSocket,
              EnumWidgetLibrarySocket,
              BoolUpdateParentNode,
@@ -547,7 +549,7 @@ def update_parent_node(self, context):
 def update_metarig_armature(self, context,):
     if self.search_prop:
         self.node.armature = self.search_prop.name
-        self.node.inputs["Meta-Bone"].search_prop = self.search_prop
+        self.node.inputs[1].armature = self.search_prop
     default_update(self,context)
 
 def update_metarig_posebone(self, context,):
@@ -1524,6 +1526,10 @@ class EnumMetaRigSocket(MantisSocket):
     def draw_color_simple(self):
         return self.color_simple
 
+class EnumArmature(EnumMetaRigSocket):
+    bl_idname = "EnumArmature"
+    bl_label = "Armature"
+
 def poll_is_curve(self, obj):
     return obj.type == "CURVE"
 
@@ -1567,43 +1573,34 @@ class EnumCurveSocket(MantisSocket):
         return self.color_simple
 
 def SearchPBDraw(self, context, layout, node, text, icon = "NONE", use_enum=True, nice_bool=True, icon_only=False):
-    layout.prop_search(data=self, property="default_value", search_data=self.search_prop.data, search_property="bones", text=text, icon=icon, results_are_suggestions=True)
+    layout.prop_search(data=self, property="default_value", search_data=self.armature.data, search_property="bones", text=text, icon=icon, results_are_suggestions=True)
 
 class EnumMetaBoneSocket(MantisSocket):
-    '''Custom node socket type'''
+    '''Socket to Get a Bone from an object'''
     bl_idname = 'EnumMetaBoneSocket'
-    bl_label = "Meta Bone"
-
+    bl_label = "Bone"
 
-    search_prop:PointerProperty(type=bpy.types.Object)
+    # this armature property is set by the parent node.
+    armature:PointerProperty(type=bpy.types.Object)
     bone:StringProperty()
 
     def populate_bones_list(self, context):
         # just gonna hardcode the value
-        if (meta_rig := self.search_prop):
-            retList = []
-            armatures = []
-            i = -1
+        if (meta_rig := self.armature):
+            retList = []; i = -1
             retList.append( ('NONE', '', '', 'NONE', i:=i+1 ) )
             for b in meta_rig.data.bones:
                 retList.append( (b.name, b.name, "Bone to copy matrix from", "BONE_DATA", i:=i+1 ) )
             return(retList)
         return None
 
-    # default_value : bpy.props.EnumProperty(
-                 # items = populate_bones_list,
-                 # name = "Meta Rig")
-
-    # def get_default_value(self):
-    #     return self.search_prop.name
-
     default_value  : StringProperty(name = "", update=update_metarig_posebone)
 
     color_simple = cString
     color : bpy.props.FloatVectorProperty(default=cString, size=4)
     def draw(self, context, layout, node, text):
         if not (self.is_linked):
-            if self.search_prop is None:
+            if self.armature is None:
                 layout.prop(self, "default_value", text="", icon="BONE_DATA",)
             else:
                 SearchPBDraw(self, context, layout, node, text="")
@@ -1616,6 +1613,11 @@ class EnumMetaBoneSocket(MantisSocket):
     def draw_color_simple(self):
         return self.color_simple
 
+class EnumExistingBoneSocket(EnumMetaBoneSocket):
+    '''Socket to Get a Bone from an object'''
+    bl_idname = 'EnumExistingBoneSocket'
+    bl_label = "Bone"
+
 # TODO: make it so that this makes an item for "missing" widgets
 # for when a widget is moved or deleted
 # Make it read .blend

+ 38 - 0
xForm_nodes.py

@@ -12,6 +12,7 @@ def TellClasses():
              xFormGeometryObject,
              xFormObjectInstance,
              xFormCurvePin,
+             xFormGetBone,
            ]
 
 #*#-------------------------------#++#-------------------------------#*#
@@ -996,3 +997,40 @@ class xFormCurvePin(xFormNode):
 
     def bGetObject(self, mode = 'POSE'):
         return self.bObject
+
+
+# special thank-you to Natalie Cuthbert, who submitted a patch for this
+# it turned out, I already had this patch laying around
+# because I was too busy with grad school I didn't succeed in collaborating
+# so while I apologize for failing to get your name in the commit history
+# now you get a special thank-you in Mantis instead!
+class xFormGetBone(xFormNode):
+    """Represents an instance of an existing geometry object."""
+    def __init__(self, signature, base_tree):
+        super().__init__(signature, base_tree, xFormGetBoneSockets)
+        self.init_parameters()
+        # this is just a getter
+        self.prepared=True; self.executed=True; self.execution_prepared=True
+    
+    def bGetParentArmature(self):
+        from bpy import data
+        return data.objects.get(self.evaluate_input("Parent Armature"))
+
+    def bGetObject(self, mode = 'POSE'):
+        bone = None
+        self.bObject = self.evaluate_input("Bone")
+        armature = self.bGetParentArmature()
+        from bpy.types import Object
+        assert armature is not None, f"{self} requires a parent armature input to operate."
+        assert isinstance(armature, Object), f"{self}: The parent armature must be an armature object."
+        if armature:
+            assert armature.type == 'ARMATURE', f"{self}: The parent armature must be an armature object."
+            match mode:
+                case 'EDIT':
+                    bone = armature.data.edit_bones.get(self.evaluate_input("Bone"))
+                case 'OBJECT':
+                    bone = armature.bones.get(self.evaluate_input("Bone"))
+                case 'POSE':
+                    bone = armature.pose.bones.get(self.evaluate_input("Bone"))
+        assert self.bObject is not None, f"{self} failed to get the desired bone. Check if the bone name exists."
+        return bone

+ 23 - 0
xForm_nodes_ui.py

@@ -16,6 +16,7 @@ def TellClasses():
         xFormGeometryObjectNode,
         xFormObjectInstance,
         xFormCurvePin,
+        xFormGetBone,
         ]
 
 def default_traverse(self, socket):
@@ -417,5 +418,27 @@ class xFormCurvePin(Node, xFormNode):
         self.color = xFormColor
         self.initialized = True
 
+class xFormGetBone(Node, xFormNode):
+    """"A node representing an existing bone"""
+    bl_idname = "xFormGetBone"
+    bl_label = "Existing Bone"
+    bl_icon = "BONE_DATA"
+    initialized : bpy.props.BoolProperty(default = False)
+    armature : bpy.props.StringProperty(default="")
+    mantis_node_class_name=bl_idname
+    
+    def init(self, context):
+        self.init_sockets(xFormGetBoneSockets)
+        self.use_custom_color = True
+        self.color = xFormColor
+        self.initialized = True
+
+    def display_update(self, parsed_tree, context):
+        if context.space_data:
+            mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
+            if mantis_node:
+                # need this parent armature to use the bone picker.
+                self.inputs["Bone"].armature = mantis_node.bGetParentArmature()
+
 for cls in TellClasses():
     cls.set_mantis_class()

+ 9 - 0
xForm_socket_templates.py

@@ -75,6 +75,15 @@ xFormCurvePinSockets = [
     xFormOutTemplate,
 ]
 
+xFormGetBoneSockets = [
+    ParentArmatureTemplate := SockTemplate(
+        name="Parent Armature", is_input=True,  bl_idname='EnumArmature', ),
+    ExistingBoneNameTemplate := replace(NameTemplate, name='Bone',
+        default_value='', bl_idname="EnumExistingBoneSocket"),
+    xFormOutTemplate,
+]
+
+
 # and bones! this one is a bit much...
 from math import pi
 xFormBoneSockets = [