Просмотр исходного кода

Big Fix Cleanup: Mantis Socket Fillers

This very BIG commit adds a new system to simplify the way socket templates are
used to generate property-socket maps. Originally, this system was just a dict
then it became a dict with nested tuples and it was unbelievably complicated

this commit makes a new data class to simplify things and make the code easier
to understand

as a consequence of this work, I've finally fixed the bug where I could only assign a
driver to the last weight in the Armature node...
I've finished implementing the basic features of the BBone and made it so armatures
can always have drivers for the drivable properties, hooray!
Joseph Brandenburg 1 месяц назад
Родитель
Сommit
9fba5be67f
9 измененных файлов с 429 добавлено и 280 удалено
  1. 17 3
      base_definitions.py
  2. 14 5
      deformer_nodes.py
  3. 24 7
      link_nodes.py
  4. 21 2
      mantis_dataclasses.py
  5. 7 1
      utilities/geometry_node_graphgen.py
  6. 89 49
      utilities/node_common.py
  7. 31 0
      versioning.py
  8. 66 136
      xForm_nodes.py
  9. 160 77
      xForm_socket_templates.py

+ 17 - 3
base_definitions.py

@@ -782,16 +782,30 @@ class MantisNode:
         for key, value in additional_parameters.items():
         for key, value in additional_parameters.items():
             self.parameters[key]=value
             self.parameters[key]=value
 
 
-    def gen_property_socket_map(self) -> dict:
+    def gen_property_socket_map(self, prop_owner_group=None) -> dict:
+        from .mantis_dataclasses import MantisSocketFiller
+        from dataclasses import replace
         props_sockets = {}
         props_sockets = {}
         for s_template in self.socket_templates:
         for s_template in self.socket_templates:
+            if prop_owner_group is not None and \
+                    s_template.prop_owner_group != prop_owner_group:
+                continue # for composite objects like bones
             if not s_template.blender_property:
             if not s_template.blender_property:
                 continue
                 continue
+            filler = MantisSocketFiller(socket_name=s_template.name)
             if isinstance(s_template.blender_property, str):
             if isinstance(s_template.blender_property, str):
-                props_sockets[s_template.blender_property]=(s_template.name, s_template.default_value)
+                filler = replace(filler)
+                filler.property_name = s_template.blender_property
+                filler.default_value=s_template.default_value
+                props_sockets[s_template.blender_property]=filler
             elif isinstance(s_template.blender_property, (tuple, list)):
             elif isinstance(s_template.blender_property, (tuple, list)):
                 for index, sub_prop in enumerate(s_template.blender_property):
                 for index, sub_prop in enumerate(s_template.blender_property):
-                    props_sockets[sub_prop]=( (s_template.name, index),s_template.default_value[index] )
+                    filler = replace(filler)
+                    filler.property_name = sub_prop
+                    filler.value_index=index
+                    filler.default_value = s_template.default_value[index]
+                    filler.combo_property=True
+                    props_sockets[sub_prop]= filler
         return props_sockets
         return props_sockets
 
 
     def set_traverse(self, traversal_pairs = [(str, str)]) -> None:
     def set_traverse(self, traversal_pairs = [(str, str)]) -> None:

+ 14 - 5
deformer_nodes.py

@@ -561,7 +561,13 @@ class DeformerMorphTargetDeform(MantisDeformerNode):
             sk.slider_min = -10
             sk.slider_min = -10
             sk.slider_max = 10
             sk.slider_max = 10
             keys[mt_name]=sk
             keys[mt_name]=sk
-            props_sockets[mt_name]= ("Value."+str(i).zfill(3), 1.0)
+            from .mantis_dataclasses import MantisSocketFiller
+            socket_value = MantisSocketFiller(
+                property_name=mt_name,
+                socket_name = "Value."+str(i).zfill(3),
+                default_value= 1.0,
+            )
+            props_sockets[mt_name]= socket_value
         for i, t in enumerate(targets):
         for i, t in enumerate(targets):
             mt_node = t.links[0].from_node; sk_ob = mt_node.GetxForm().bGetObject()
             mt_node = t.links[0].from_node; sk_ob = mt_node.GetxForm().bGetObject()
             if sk_ob is None: continue
             if sk_ob is None: continue
@@ -574,9 +580,6 @@ class DeformerMorphTargetDeform(MantisDeformerNode):
         finish_drivers(self)
         finish_drivers(self)
         prWhite(f"Initializing morph target took {time() -start_time} seconds")
         prWhite(f"Initializing morph target took {time() -start_time} seconds")
 
 
-
-        
-
     def gen_shape_key(self, xf, context):
     def gen_shape_key(self, xf, context):
         # TODO: make this a feature of the node definition that appears only when there are no prior deformers - and shows a warning!
         # TODO: make this a feature of the node definition that appears only when there are no prior deformers - and shows a warning!
         # TODO: the below works well, but it is quite slow. It does not seem to have better performence. Its only advantage is export to FBX.
         # TODO: the below works well, but it is quite slow. It does not seem to have better performence. Its only advantage is export to FBX.
@@ -652,7 +655,13 @@ class DeformerMorphTargetDeform(MantisDeformerNode):
             sk.slider_min = -10
             sk.slider_min = -10
             sk.slider_max = 10
             sk.slider_max = 10
             keys[mt_name]=sk
             keys[mt_name]=sk
-            props_sockets[mt_name]= ("Value."+str(i).zfill(3), 1.0)
+            from .mantis_dataclasses import MantisSocketFiller
+            socket_value = MantisSocketFiller(
+                property_name=mt_name,
+                socket_name = "Value."+str(i).zfill(3),
+                default_value= 1.0,
+            )
+            props_sockets[mt_name]= socket_value
         for i, t in enumerate(targets):
         for i, t in enumerate(targets):
             mt_node = t.links[0].from_node; sk_ob = mt_node.GetxForm().bGetObject()
             mt_node = t.links[0].from_node; sk_ob = mt_node.GetxForm().bGetObject()
             if sk_ob is None: continue
             if sk_ob is None: continue

+ 24 - 7
link_nodes.py

@@ -65,7 +65,7 @@ class MantisLinkNode(MantisNode):
             return None
             return None
 
 
         else:
         else:
-            return super().evaluate_input(input_name)
+            return super().evaluate_input(input_name, index)
 
 
     def gen_property_socket_map(self) -> dict:
     def gen_property_socket_map(self) -> dict:
         props_sockets = super().gen_property_socket_map()
         props_sockets = super().gen_property_socket_map()
@@ -319,17 +319,21 @@ class LinkTransformation(MantisLinkNode):
                 for axis in ['x', 'y', 'z']:
                 for axis in ['x', 'y', 'z']:
                     stub='from_min_'+axis
                     stub='from_min_'+axis
                     props_sockets[stub+from_suffix]=props_sockets[stub]
                     props_sockets[stub+from_suffix]=props_sockets[stub]
+                    props_sockets[stub+from_suffix].property_name = stub+from_suffix
                     del props_sockets[stub]
                     del props_sockets[stub]
                     stub='from_max_'+axis
                     stub='from_max_'+axis
                     props_sockets[stub+from_suffix]=props_sockets[stub]
                     props_sockets[stub+from_suffix]=props_sockets[stub]
+                    props_sockets[stub+from_suffix].property_name = stub+from_suffix
                     del props_sockets[stub]
                     del props_sockets[stub]
             if to_replace:
             if to_replace:
                 for axis in ['x', 'y', 'z']:
                 for axis in ['x', 'y', 'z']:
                     stub='to_min_'+axis
                     stub='to_min_'+axis
                     props_sockets[stub+to_replace]=props_sockets[stub]
                     props_sockets[stub+to_replace]=props_sockets[stub]
+                    props_sockets[stub+from_suffix].property_name = stub+to_replace
                     del props_sockets[stub]
                     del props_sockets[stub]
                     stub='to_max_'+axis
                     stub='to_max_'+axis
                     props_sockets[stub+to_replace]=props_sockets[stub]
                     props_sockets[stub+to_replace]=props_sockets[stub]
+                    props_sockets[stub+from_suffix].property_name = stub+to_replace
                     del props_sockets[stub]
                     del props_sockets[stub]
             evaluate_sockets(self, c, props_sockets)
             evaluate_sockets(self, c, props_sockets)
         self.executed = True
         self.executed = True
@@ -766,8 +770,11 @@ class LinkDrivenParameter(MantisLinkNode):
                 def_value=[0.0]*length
                 def_value=[0.0]*length
                 self.parameters["Value"] = tuple( 0.0 if i != index else value for i in range(length))
                 self.parameters["Value"] = tuple( 0.0 if i != index else value for i in range(length))
 
 
+            from .mantis_dataclasses import MantisSocketFiller
             props_sockets = {
             props_sockets = {
-                prop: ("Value", def_value)
+                prop: MantisSocketFiller( property_name=prop,
+                                          socket_name="Value",
+                                          default_value=def_value)
             }
             }
             evaluate_sockets(self, ob, props_sockets)
             evaluate_sockets(self, ob, props_sockets)
 
 
@@ -814,6 +821,7 @@ class LinkArmature(MantisLinkNode):
             #       are all in the same armature
             #       are all in the same armature
             # Mantis should intelligently provide a working armature constraint
             # Mantis should intelligently provide a working armature constraint
             # even if the user supplies targets that would not work in vanilla Blender
             # even if the user supplies targets that would not work in vanilla Blender
+            # TODO find out this is reading the arrays in reverse order? is that bad?
             targets_weights = {}
             targets_weights = {}
             for i in range(num_targets):
             for i in range(num_targets):
                 target = c.targets.new()
                 target = c.targets.new()
@@ -822,7 +830,17 @@ class LinkArmature(MantisLinkNode):
                 if not isinstance(weight_value, float):
                 if not isinstance(weight_value, float):
                     weight_value=0
                     weight_value=0
                 targets_weights[i]=weight_value
                 targets_weights[i]=weight_value
-                props_sockets["targets[%d].weight" % i] = ("Weights", i)
+                from .mantis_dataclasses import MantisSocketFiller
+                default_value = 1.0
+                if self.inputs['Weights'].links:
+                    default_value = 1/len(self.inputs['Weights'].links)
+                filler = MantisSocketFiller(
+                    property_name= (prop_name := "targets[%d].weight" % i),
+                    socket_name="Weights",
+                    default_value=default_value,
+                    array_index=i,
+                )
+                props_sockets[prop_name] = filler
             evaluate_sockets(self, c, props_sockets)
             evaluate_sockets(self, c, props_sockets)
             for target, value in targets_weights.items():
             for target, value in targets_weights.items():
                 c.targets[target].weight=value
                 c.targets[target].weight=value
@@ -904,14 +922,13 @@ class LinkShrinkWrap(MantisLinkNode):
                 wrapGreen(" Constraint for bone: ") +
                 wrapGreen(" Constraint for bone: ") +
                 wrapOrange(xf.bGetObject().name))
                 wrapOrange(xf.bGetObject().name))
             c = xf.bGetObject().constraints.new('SHRINKWRAP')
             c = xf.bGetObject().constraints.new('SHRINKWRAP')
+            self.bObject.append(c)
             self.get_target_and_subtarget(c)
             self.get_target_and_subtarget(c)
             if constraint_name := self.evaluate_input("Name"):
             if constraint_name := self.evaluate_input("Name"):
                 c.name = constraint_name
                 c.name = constraint_name
             props_sockets = self.gen_property_socket_map()
             props_sockets = self.gen_property_socket_map()
-            print (props_sockets.keys())
-            self.bObject.append(c)
-            # self.set_custom_space() # this needs to be overridden for me TODO
             evaluate_sockets(self, c, props_sockets)
             evaluate_sockets(self, c, props_sockets)
+            # self.set_custom_space() # this needs to be overridden for me TODO
         self.executed = True
         self.executed = True
 
 
 
 
@@ -931,10 +948,10 @@ class LinkGeometryAttribute(MantisLinkNode):
         for xf in self.GetxForm():
         for xf in self.GetxForm():
             print
             print
             c = xf.bGetObject().constraints.new('GEOMETRY_ATTRIBUTE')
             c = xf.bGetObject().constraints.new('GEOMETRY_ATTRIBUTE')
+            self.bObject.append(c)
             self.get_target_and_subtarget(c)
             self.get_target_and_subtarget(c)
             if constraint_name := self.evaluate_input("Name"):
             if constraint_name := self.evaluate_input("Name"):
                 c.name = constraint_name
                 c.name = constraint_name
             props_sockets = self.gen_property_socket_map()
             props_sockets = self.gen_property_socket_map()
-            self.bObject.append(c)
             evaluate_sockets(self, c, props_sockets)
             evaluate_sockets(self, c, props_sockets)
         self.executed = True
         self.executed = True

+ 21 - 2
mantis_dataclasses.py

@@ -1,5 +1,5 @@
 from dataclasses import dataclass, field
 from dataclasses import dataclass, field
-from typing import List, Any, Tuple
+from typing import List, Any, Tuple, Optional
 
 
 @dataclass
 @dataclass
 class MantisSocketTemplate():
 class MantisSocketTemplate():
@@ -19,6 +19,7 @@ class MantisSocketTemplate():
     #  where multiple blender objects own the properties, like e.g. bone/posebone
     #  where multiple blender objects own the properties, like e.g. bone/posebone
     prop_owner_group : str = field(default="")
     prop_owner_group : str = field(default="")
     # do? add versioning fields for Mantis, Blender version min/max
     # do? add versioning fields for Mantis, Blender version min/max
+    is_animatable    : bool = field(default=True) # controls whether a driver is legal or not
 
 
 @dataclass
 @dataclass
 class xForm_info():
 class xForm_info():
@@ -78,4 +79,22 @@ class MantisBBoneTransform():
     # Custom Handles
     # Custom Handles
     use_bbone_custom_handles: bool = field(default=False)
     use_bbone_custom_handles: bool = field(default=False)
     bbone_custom_handle_start: str = field(default=None)
     bbone_custom_handle_start: str = field(default=None)
-    bbone_custom_handle_end: str = field(default=None)
+    bbone_custom_handle_end: str = field(default=None)
+
+# used to apply values or drivers to blender properties in a generic way
+@dataclass
+class MantisSocketFiller():
+    socket_name: str = field(default="")
+    property_name : str = field(default="")
+    default_value: Any = field(default=None)
+    array_index: int = field(default=0) # for multi-input array sockets
+    value_index: Optional[int] = field(default=None) # for vector properties
+    # A Combo Property is a property that jams a bunch of blender properties into one socket
+    combo_property: bool = field(default=False)
+
+    def __repr__(self):
+        return f"socket_name: {self.socket_name}, " \
+               f"property_name: {self.property_name}, " \
+               f"default_value: {self.default_value}, " \
+               f"array_index: {self.array_index or 0}, " \
+               f"value_index: {self.value_index or None}" 

+ 7 - 1
utilities/geometry_node_graphgen.py

@@ -79,7 +79,13 @@ def gen_morph_target_nodes(mod_name, mod_ob, targets, context, use_offset=True):
             ng.links.new(input=inp.outputs[mt_name+" Value"], output=scale1.inputs["Scale"])
             ng.links.new(input=inp.outputs[mt_name+" Value"], output=scale1.inputs["Scale"])
         add_these.append(scale1)
         add_these.append(scale1)
         object_map["Socket_"+str((i+1)*2)]=mt_node.GetxForm().bGetObject()
         object_map["Socket_"+str((i+1)*2)]=mt_node.GetxForm().bGetObject()
-        props_sockets["Socket_"+str((i+1)*2+1)]= ("Value."+str(i).zfill(3), 1.0)
+        from ..mantis_dataclasses import MantisSocketFiller
+        socket_value = MantisSocketFiller(
+            property_name = (prop := "Socket_"+str((i+1)*2+1)),
+            socket_name = (socket_name := "Value."+str(i).zfill(3)),
+            default_value = 1.0
+        )
+        props_sockets[prop] = socket_value
     
     
     set_position = ng.nodes.new("GeometryNodeSetPosition")
     set_position = ng.nodes.new("GeometryNodeSetPosition")
     bake = ng.nodes.new("GeometryNodeBake")
     bake = ng.nodes.new("GeometryNodeBake")

+ 89 - 49
utilities/node_common.py

@@ -124,15 +124,14 @@ def setup_custom_property_inputs_outputs(mantis_node):
     if mantis_node.signature[0] == 'SCHEMA_AUTOGENERATED':
     if mantis_node.signature[0] == 'SCHEMA_AUTOGENERATED':
         from ..base_definitions import custom_props_types
         from ..base_definitions import custom_props_types
         if mantis_node.__class__.__name__ not in custom_props_types:
         if mantis_node.__class__.__name__ not in custom_props_types:
-            # prRed(f"Reminder: figure out how to deal with custom property setting for Schema Node {mantis_node}")
             raise RuntimeError(wrapRed(f"Custom Properties not allowed for class {mantis_node.__class__.__name__}"))
             raise RuntimeError(wrapRed(f"Custom Properties not allowed for class {mantis_node.__class__.__name__}"))
         return
         return
     else:
     else:
-        ui_node = get_ui_node(mantis_node.signature, mantis_node.base_tree)
+        ui_node = get_ui_node(mantis_node.ui_signature, mantis_node.base_tree)
     if ui_node:
     if ui_node:
         setup_custom_props_from_np(mantis_node, ui_node)
         setup_custom_props_from_np(mantis_node, ui_node)
     else:
     else:
-        prRed("Failed to setup custom properties for: mantis_node")
+        prRed(f"Failed to setup custom properties for: {mantis_node.ui_signature}")
 
 
 def setup_custom_props_from_np(mantis_node, ui_node):
 def setup_custom_props_from_np(mantis_node, ui_node):
     for inp in ui_node.inputs:
     for inp in ui_node.inputs:
@@ -162,13 +161,27 @@ def prepare_parameters(mantis_node):
             mantis_node.parameters[s_name] = sock.links[0].from_node.parameters[sock.name]
             mantis_node.parameters[s_name] = sock.links[0].from_node.parameters[sock.name]
     # should work, this is ugly.
     # should work, this is ugly.
 
 
-def check_for_driver(mantis_node, input_name, index = None):
-    prop = mantis_node.evaluate_input(input_name)
-    if (index is not None):
-        prop = prop[index]
+def check_for_driver(mantis_node, input_name, value_index = None, array_index = 0, combo_property=False):
+    prop = mantis_node.evaluate_input(input_name, array_index)
+    if (value_index is not None):
+        try:
+            prop = prop[value_index]
+        except KeyError: # We'll try to use the driver for all three channels.
+            pass
+        # TODO an Index or Key error here should have a reasonable error message
     return (prop.__class__.__name__ == 'MantisDriver')
     return (prop.__class__.__name__ == 'MantisDriver')
 
 
-# TODO: this should handle sub-properties better
+def print_driver_item(mantis_node, b_object, original_prop):
+    if mantis_node.node_type in ['LINK',]:
+        printname  = wrapOrange(b_object.id_data.name)
+    elif mantis_node.node_type in ['XFORM',]:
+        printname  = wrapOrange(mantis_node.bGetObject().name)
+    else:
+        printname = wrapOrange(mantis_node)
+    print("Adding driver %s to %s in %s" % (wrapPurple(original_prop), wrapWhite(mantis_node.signature[-1]), printname))
+
+# HACK This is the most hideous voodoo black magic fucntion in the entire program
+#      but everything depends on it and it works 
 def evaluate_sockets(mantis_node, b_object, props_sockets,):
 def evaluate_sockets(mantis_node, b_object, props_sockets,):
     # this is neccesary because some things use dict properties for dynamic properties and setattr doesn't work
     # this is neccesary because some things use dict properties for dynamic properties and setattr doesn't work
     def safe_setattr(ob, att_name, val):
     def safe_setattr(ob, att_name, val):
@@ -181,20 +194,25 @@ def evaluate_sockets(mantis_node, b_object, props_sockets,):
             # it is of the form prop[int].prop2
             # it is of the form prop[int].prop2
             prop=att_name.split('[')[0]
             prop=att_name.split('[')[0]
             prop1=att_name.split('.')[1]
             prop1=att_name.split('.')[1]
-            index = int(att_name.split('[')[1][0])
-            setattr(getattr(b_object, prop)[index], prop1, val)
+            get_index = int(att_name.split('[')[1][0])
+            setattr(getattr(b_object, prop)[get_index], prop1, val)
         else:
         else:
             try:
             try:
                 setattr(ob, att_name, val)
                 setattr(ob, att_name, val)
             except Exception as e:
             except Exception as e:
-                prRed(ob, att_name, val); raise e
-    for prop, (sock, default) in props_sockets.items():
-        # annoyingly, sometimes the socket is an array
-        index = None
-        if isinstance(sock, tuple):
-            index = sock[1]; sock = sock[0]
-        if (check_for_driver(mantis_node, sock, index)):
-            sock = (sock, index)
+                print ("ob, att_name, val")
+                prRed(ob, att_name, val)
+                raise e
+    for prop, filler_item in props_sockets.items():
+        if isinstance(filler_item, tuple):
+            raise NotImplementedError(mantis_node)
+        sock = filler_item.socket_name
+        default = filler_item.default_value
+        value_index = filler_item.value_index
+        array_index = filler_item.array_index
+        combo_property = filler_item.combo_property
+        driver_key = (sock, value_index, array_index)
+        if (check_for_driver(mantis_node, sock, value_index, array_index, combo_property)):
             original_prop = prop
             original_prop = prop
             # TODO: deduplicate this terrible hack
             # TODO: deduplicate this terrible hack
             if ("." in prop) and not b_object.__class__.__name__ in ["Key"]: # this is a property of a property...
             if ("." in prop) and not b_object.__class__.__name__ in ["Key"]: # this is a property of a property...
@@ -204,31 +222,25 @@ def evaluate_sockets(mantis_node, b_object, props_sockets,):
                     prop = split_prop[1]
                     prop = split_prop[1]
                     sub_prop = (split_prop[0])
                     sub_prop = (split_prop[0])
                     if ("[" in sub_prop):
                     if ("[" in sub_prop):
-                        sub_prop, index = sub_prop.split("[")
-                        index = int(index[0])
-                        sub_props.append(getattr(sub_props[-1], sub_prop)[index])
+                        sub_prop, sub_index = sub_prop.split("[")
+                        sub_prop_index = int(sub_index[0])
+                        sub_props.append(getattr(sub_props[-1], sub_prop)[sub_prop_index])
                     else:
                     else:
                         sub_props.append(getattr(sub_props[-1], sub_prop))
                         sub_props.append(getattr(sub_props[-1], sub_prop))
                 safe_setattr(sub_props[-1], prop, default)
                 safe_setattr(sub_props[-1], prop, default)
             # this is really stupid
             # this is really stupid
             else:
             else:
                 safe_setattr(b_object, prop, default)
                 safe_setattr(b_object, prop, default)
-            if mantis_node.node_type in ['LINK',]:
-                printname  = wrapOrange(b_object.id_data.name)
-            elif mantis_node.node_type in ['XFORM',]:
-                printname  = wrapOrange(mantis_node.bGetObject().name)
-            else:
-                printname = wrapOrange(mantis_node)
-            print("Adding driver %s to %s in %s" % (wrapPurple(original_prop), wrapWhite(mantis_node.signature[-1]), printname))
+            print_driver_item( mantis_node, b_object, original_prop)
             if b_object.__class__.__name__ in ["NodesModifier"]:
             if b_object.__class__.__name__ in ["NodesModifier"]:
-                mantis_node.drivers[sock] = "[\""+original_prop+"\"]" # lol. It is a dict element not a "true" property
+                mantis_node.drivers[driver_key] = "[\""+original_prop+"\"]" # lol. It is a dict element not a "true" property
             elif b_object.__class__.__name__ in ["Key"]:
             elif b_object.__class__.__name__ in ["Key"]:
-                mantis_node.drivers[sock] = "key_blocks[\""+original_prop+"\"].value"
+                mantis_node.drivers[driver_key] = "key_blocks[\""+original_prop+"\"].value"
             else:
             else:
-                mantis_node.drivers[sock] = original_prop
+                mantis_node.drivers[driver_key] = original_prop
         else: # here we can do error checking for the socket if needed
         else: # here we can do error checking for the socket if needed
-            if (index is not None):
-                safe_setattr(b_object, prop, mantis_node.evaluate_input(sock)[index])
+            if (value_index is not None):
+                safe_setattr(b_object, prop, mantis_node.evaluate_input(sock)[value_index])
             else:                    # 'mute' is better than 'enabled'
             else:                    # 'mute' is better than 'enabled'
                 # UGLY HACK          # because it is available in older
                 # UGLY HACK          # because it is available in older
                 if (prop == 'mute'): # Blenders.
                 if (prop == 'mute'): # Blenders.
@@ -237,9 +249,6 @@ def evaluate_sockets(mantis_node, b_object, props_sockets,):
                     safe_setattr(b_object, prop, bool(mantis_node.evaluate_input(sock)))
                     safe_setattr(b_object, prop, bool(mantis_node.evaluate_input(sock)))
                 else:
                 else:
                     try:
                     try:
-                        # prRed(b_object.name, mantis_node, prop, mantis_node.evaluate_input(sock) )
-                        # print( mantis_node.evaluate_input(sock))
-                    # value_eval = mantis_node.evaluate_input(sock)
                     # just wanna see if we are dealing with some collection
                     # just wanna see if we are dealing with some collection
                     # check hasattr in case it is one of those ["such-and-such"] props, and ignore those
                     # check hasattr in case it is one of those ["such-and-such"] props, and ignore those
                         if hasattr(b_object, prop) and (not isinstance(getattr(b_object, prop), str)) and hasattr(getattr(b_object, prop), "__getitem__"):
                         if hasattr(b_object, prop) and (not isinstance(getattr(b_object, prop), str)) and hasattr(getattr(b_object, prop), "__getitem__"):
@@ -248,7 +257,7 @@ def evaluate_sockets(mantis_node, b_object, props_sockets,):
                                 # assume this will work, both because val should have the right number of elements, and because this should be the right data type.
                                 # assume this will work, both because val should have the right number of elements, and because this should be the right data type.
                                 from .drivers import MantisDriver
                                 from .drivers import MantisDriver
                                 if isinstance(value, MantisDriver):
                                 if isinstance(value, MantisDriver):
-                                    getattr(b_object,prop)[val_index] =  default[val_index]
+                                    getattr(b_object,prop)[val_index] = default[val_index]
                                     print("Adding driver %s to %s in %s" % (wrapPurple(prop), wrapWhite(mantis_node.signature[-1]), mantis_node))
                                     print("Adding driver %s to %s in %s" % (wrapPurple(prop), wrapWhite(mantis_node.signature[-1]), mantis_node))
                                     try:
                                     try:
                                         mantis_node.drivers[sock].append((prop, val_index))
                                         mantis_node.drivers[sock].append((prop, val_index))
@@ -257,24 +266,50 @@ def evaluate_sockets(mantis_node, b_object, props_sockets,):
                                 else:
                                 else:
                                     getattr(b_object,prop)[val_index] =  value
                                     getattr(b_object,prop)[val_index] =  value
                         else:
                         else:
-                            # prOrange("Skipping the Thing", getattr(b_object, prop))
                             safe_setattr(b_object, prop, mantis_node.evaluate_input(sock))
                             safe_setattr(b_object, prop, mantis_node.evaluate_input(sock))
                     except Exception as e:
                     except Exception as e:
                         prRed(b_object, mantis_node, prop, sock, mantis_node.evaluate_input(sock))
                         prRed(b_object, mantis_node, prop, sock, mantis_node.evaluate_input(sock))
                         raise e
                         raise e
 
 
 def finish_driver(mantis_node, b_object, driver_item, prop):
 def finish_driver(mantis_node, b_object, driver_item, prop):
-    # prWhite(mantis_node, prop)
-    index = driver_item[1]; driver_sock = driver_item[0]
-    driver_trace = trace_single_line(mantis_node, driver_sock)
+    try:
+        driver_sock = driver_item[0]; index = driver_item[1]; array_index =driver_item[2]
+    except Exception as e:
+        print (wrapGreen(mantis_node), wrapOrange(b_object), wrapRed(driver_item), wrapWhite(prop),)
+        raise e
+    driver_trace = trace_single_line(mantis_node, driver_sock, array_index)
     driver_provider, driver_socket = driver_trace[0][-1], driver_trace[1]
     driver_provider, driver_socket = driver_trace[0][-1], driver_trace[1]
+    
+    combo_property = False
+    # try to detect combo properties
+    if mantis_node.socket_templates:
+        for st in mantis_node.socket_templates:
+            if st.name == driver_sock:
+                if isinstance(st.blender_property, tuple):
+                    combo_property = True
+                break
+
     if index is not None:
     if index is not None:
-        driver = driver_provider.parameters[driver_socket.name][index].copy()
+        try:
+            driver = driver_provider.parameters[driver_socket.name][index].copy()
+        except KeyError as e: # try single driver, it should still work fine.
+            if combo_property:
+                driver = driver_provider.parameters[driver_socket.name].copy()
+            else:
+                raise e
         # this is harmless and necessary for the weird ones where the property is a vector too
         # this is harmless and necessary for the weird ones where the property is a vector too
-        driver["ind"] = index
+        if not combo_property:
+            driver["ind"] = index
     else:
     else:
         driver = driver_provider.parameters[driver_socket.name].copy()
         driver = driver_provider.parameters[driver_socket.name].copy()
     if driver:
     if driver:
+        socket_template = None
+        if mantis_node.socket_templates:
+            for socket_template in mantis_node.socket_templates:
+                if socket_template.name == driver_sock: break
+        if socket_template:
+            if socket_template.is_animatable == False:
+                raise RuntimeError(f"Driver is not allowed for {driver_sock} in node {mantis_node}")
         if ("." in prop) and mantis_node.__class__.__name__ != "DeformerMorphTargetDeform": # this is a property of a property...
         if ("." in prop) and mantis_node.__class__.__name__ != "DeformerMorphTargetDeform": # this is a property of a property...
             sub_props = [b_object]
             sub_props = [b_object]
             while ("." in prop):
             while ("." in prop):
@@ -291,11 +326,14 @@ def finish_driver(mantis_node, b_object, driver_item, prop):
         elif mantis_node.node_type in ['XFORM',] and mantis_node.__class__.__name__ in ['xFormBone']:
         elif mantis_node.node_type in ['XFORM',] and mantis_node.__class__.__name__ in ['xFormBone']:
             # TODO: I really shouldn't have to hardcode this. Look into better solutions.
             # TODO: I really shouldn't have to hardcode this. Look into better solutions.
             # NOTE: VERSIONING in Blender 5.0 we have to use pose bone for this, too.
             # NOTE: VERSIONING in Blender 5.0 we have to use pose bone for this, too.
-            if bpy_version <= (5,0,0) and prop  == 'hide': # older blendders, we need the bone
+            # default to pose bone, it's usually what we want
+            bone_col = mantis_node.bGetParentArmature().pose.bones
+            from ..xForm_socket_templates import attributes_shared_by_bone_and_pose_bone
+            if socket_template.prop_owner_group == 'bone':
                 bone_col = mantis_node.bGetParentArmature().data.bones
                 bone_col = mantis_node.bGetParentArmature().data.bones
-            elif prop  == 'show_wire': # TODO check if the bug that forced the above affects this
-                bone_col = mantis_node.bGetParentArmature().data.bones
-            else:
+            # this is a little bit clumsy. TODO clean up here
+            if prop in attributes_shared_by_bone_and_pose_bone:
+                # we want to use the pose bone for this...only if it has a driver
                 bone_col = mantis_node.bGetParentArmature().pose.bones
                 bone_col = mantis_node.bGetParentArmature().pose.bones
             driver["owner"] = bone_col[b_object] # we use "unsafe" brackets instead of get() because we want to see any errors that occur
             driver["owner"] = bone_col[b_object] # we use "unsafe" brackets instead of get() because we want to see any errors that occur
         # HACK having special cases here is indicitave of a deeper problem that should be refactored
         # HACK having special cases here is indicitave of a deeper problem that should be refactored
@@ -323,11 +361,13 @@ def finish_drivers(mantis_node):
         if mantis_node.node_type == 'LINK':
         if mantis_node.node_type == 'LINK':
             b_objects = mantis_node.bObject # this is already a list
             b_objects = mantis_node.bObject # this is already a list
         for b_object in b_objects:
         for b_object in b_objects:
+            # Mutating the driver_item here is the WORST THING I HAVE EVER SEEN
+            # HACK UGLY BUG TERRIBLE BAD CODE
             if isinstance(prop, list):
             if isinstance(prop, list):
                 for sub_item in prop:
                 for sub_item in prop:
-                        drivers.append(finish_driver(mantis_node, b_object, (driver_item, sub_item[1]), sub_item[0]))
+                        drivers.append(finish_driver(mantis_node, b_object, (driver_item, sub_item[1], 0), sub_item[0]))
                 else:
                 else:
-                    drivers.append(finish_driver(mantis_node, b_object, (driver_item, sub_item[1]), sub_item[0]))
+                    drivers.append(finish_driver(mantis_node, b_object, (driver_item, sub_item[1], 0), sub_item[0]))
             else:
             else:
                 drivers.append(finish_driver(mantis_node, b_object, driver_item, prop))
                 drivers.append(finish_driver(mantis_node, b_object, driver_item, prop))
     from .drivers import CreateDrivers
     from .drivers import CreateDrivers

+ 31 - 0
versioning.py

@@ -423,6 +423,36 @@ def update_fcurve_and_armature_node_to_multi_socket(*args, **kwargs):
             other_node.is_updating = was_updating[other_node.name]
             other_node.is_updating = was_updating[other_node.name]
         node.id_data.do_live_update = live_updates
         node.id_data.do_live_update = live_updates
 
 
+# These BBone Scale-In, Scale-Out properties had the wrong defaults
+# now that they are implemented properly, 
+# since they were not implemented before, we'll do all of them at once.
+# Normally it is wrong to set things directly like this, but they didn't
+# do anything before. Oh well.
+def fix_bbone_default_scale_in_and_out(*args, **kwargs):
+    tree = kwargs['tree']
+    current_major_version = tree.mantis_version[0]
+    current_minor_version = tree.mantis_version[1]
+    current_sub_version = tree.mantis_version[2]
+    if  current_major_version > 0: return# major version must be 0
+    if current_minor_version >= 13: return# minor version must be 12 or less
+    if current_minor_version == 13 and current_sub_version >= 5: return# sub version must be 4 or less
+    prPurple(f"Fixing BBone Settings for {tree.name}")
+    live_updates = tree.do_live_update
+    bones = []
+    for node in tree.nodes:
+        if hasattr(node, 'is_updating'):
+            node.is_updating = True
+        if node.bl_idname == 'xFormBoneNode':
+            bones.append(node)
+    try:
+        for bone_node in bones:
+            bone_node.inputs['BBone Scale-In'].default_value = (1,1,1)
+            bone_node.inputs['BBone Scale-Out'].default_value = (1,1,1)
+    finally:
+        for node in tree.nodes:
+            if hasattr(node, 'is_updating'):
+                node.is_updating = False
+
 # versioning tasks that involve Blender versions rather than Mantis versions:
 # versioning tasks that involve Blender versions rather than Mantis versions:
 error_description_4_5_0_LTS = 'There is a bug in Blender 4.5.0 LTS, that is why these sockets are blue.'\
 error_description_4_5_0_LTS = 'There is a bug in Blender 4.5.0 LTS, that is why these sockets are blue.'\
                              ' This will be fixed in future Blender versions.'
                              ' This will be fixed in future Blender versions.'
@@ -459,6 +489,7 @@ versioning_tasks = [
     (['SchemaTree'], schema_enable_custom_interface_types, ['tree']),
     (['SchemaTree'], schema_enable_custom_interface_types, ['tree']),
     (['UtilityFCurve', 'LinkArmature'],
     (['UtilityFCurve', 'LinkArmature'],
            update_fcurve_and_armature_node_to_multi_socket, ['node']),
            update_fcurve_and_armature_node_to_multi_socket, ['node']),
+    (['MantisTree', 'SchemaTree'], fix_bbone_default_scale_in_and_out, ['tree']),
 ]
 ]
 
 
 
 

+ 66 - 136
xForm_nodes.py

@@ -76,6 +76,26 @@ class xFormNode(MantisNode):
     def reset_execution(self):
     def reset_execution(self):
         super().reset_execution()
         super().reset_execution()
         self.prepared=False
         self.prepared=False
+
+    def check_drivers(self):
+        for st in self.socket_templates:
+            if st.is_animatable == False:
+                socket = st.name
+                multi = st.use_multi_input
+                vector = isinstance(st.default_value, tuple)
+                # if single property
+                if not multi and not vector:
+                    has_driver = check_for_driver(self, socket)
+                elif not multi and vector:
+                    for i in range(len(st.default_value)):
+                        has_driver = check_for_driver(self, socket, i)
+                        if has_driver: break
+                elif multi:
+                    for i in range(len(self.inputs[socket].links)):
+                        has_driver = check_for_driver(self, socket, 0, i)
+                        if has_driver: break
+                if has_driver:
+                    raise GraphError(f"Error: Socket \"{socket}\" is not animatable and cannot be driven.")
     
     
     def setup_custom_props(self, ob, input):
     def setup_custom_props(self, ob, input):
         from .mantis_dataclasses import custom_prop_template
         from .mantis_dataclasses import custom_prop_template
@@ -149,9 +169,13 @@ class xFormArmature(xFormNode):
 
 
         #check if an object by the name exists
         #check if an object by the name exists
         if (name) and (ob := bpy.data.objects.get(name)):
         if (name) and (ob := bpy.data.objects.get(name)):
+            # Clear the drivers from BOTH the object/pose and the skeleton
             if (ob.animation_data):
             if (ob.animation_data):
                 while (ob.animation_data.drivers):
                 while (ob.animation_data.drivers):
                         ob.animation_data.drivers.remove(ob.animation_data.drivers[-1])
                         ob.animation_data.drivers.remove(ob.animation_data.drivers[-1])
+            if (ob.data.animation_data):
+                while (ob.data.animation_data.drivers):
+                        ob.data.animation_data.drivers.remove(ob.data.animation_data.drivers[-1])
             for pb in ob.pose.bones:
             for pb in ob.pose.bones:
                 # clear it, even after deleting the edit bones,
                 # clear it, even after deleting the edit bones,
                 #  if we create them again the pose bones will be reused
                 #  if we create them again the pose bones will be reused
@@ -269,6 +293,7 @@ class xFormBone(xFormNode):
             eb.inherit_scale = parent_mantis_node.evaluate_input("Inherit Scale")
             eb.inherit_scale = parent_mantis_node.evaluate_input("Inherit Scale")
 
 
     def bPrepare(self, bContext=None):
     def bPrepare(self, bContext=None):
+        self.check_drivers()
         self.parameters['Matrix'] = get_matrix(self)
         self.parameters['Matrix'] = get_matrix(self)
         self.prepared = True
         self.prepared = True
 
 
@@ -341,13 +366,9 @@ class xFormBone(xFormNode):
             raise RuntimeError(wrapRed(f"Could not create edit bone: {name} because bone head was located in the same place as bone tail."))
             raise RuntimeError(wrapRed(f"Could not create edit bone: {name} because bone head was located in the same place as bone tail."))
 
 
         # Setup Deform attributes...
         # Setup Deform attributes...
-        eb.use_deform            = self.evaluate_input("Deform")
-        eb.envelope_distance     = self.evaluate_input("Envelope Distance")
-        eb.envelope_weight       = self.evaluate_input("Envelope Weight")
-        eb.use_envelope_multiply = self.evaluate_input("Envelope Multiply")
-        eb.head_radius           = self.evaluate_input("Envelope Head Radius")
-        eb.tail_radius           = self.evaluate_input("Envelope Tail Radius")
-
+        eb_props_sockets = self.gen_property_socket_map(prop_owner_group="edit_bone")
+        evaluate_sockets(self, eb, eb_props_sockets)
+        
         print( wrapGreen("Created Bone: ") + wrapOrange(eb.name) + wrapGreen(" in ") + wrapWhite(self.bGetParentArmature().name))
         print( wrapGreen("Created Bone: ") + wrapOrange(eb.name) + wrapGreen(" in ") + wrapWhite(self.bGetParentArmature().name))
 
 
         parent_xForm_info = get_parent_xForm_info(self)
         parent_xForm_info = get_parent_xForm_info(self)
@@ -360,7 +381,6 @@ class xFormBone(xFormNode):
             self_edit_name=eb.name,
             self_edit_name=eb.name,
         )
         )
         self.parameters['xForm Out'] = my_info
         self.parameters['xForm Out'] = my_info
-
         self.executed = True
         self.executed = True
 
 
     def set_bone_color(self, b, inherit_color, bContext):
     def set_bone_color(self, b, inherit_color, bContext):
@@ -401,155 +421,68 @@ class xFormBone(xFormNode):
 
 
 
 
     def bFinalize(self, bContext = None):
     def bFinalize(self, bContext = None):
+
         b = self.bGetParentArmature().data.bones[self.bObject]
         b = self.bGetParentArmature().data.bones[self.bObject]
         # let's do bone colors first
         # let's do bone colors first
         inherit_color = self.evaluate_input("Inherit Color")
         inherit_color = self.evaluate_input("Inherit Color")
         if len(self.inputs['Color'].links) > 0:
         if len(self.inputs['Color'].links) > 0:
             inherit_color = False # use the link instead
             inherit_color = False # use the link instead
-        # try: # just in case, this shouldn't cause a failure
         self.set_bone_color(b, inherit_color, bContext)
         self.set_bone_color(b, inherit_color, bContext)
-        # except Exception as e:
-        #     prRed("WARNING: failed to set color because of error, see report below:")
-        #     prOrange(e)
-        #
-        do_bb=False
-        b.bbone_x = self.evaluate_input("BBone X Size"); b.bbone_x = max(b.bbone_x, 0.0002)
-        b.bbone_z = self.evaluate_input("BBone Z Size"); b.bbone_z = max(b.bbone_z, 0.0002)
-        if (segs := self.evaluate_input("BBone Segments")) > 1:
-            do_bb=True
-            b.bbone_segments = segs
-            b.bbone_x = self.evaluate_input("BBone X Size")
-            b.bbone_z = self.evaluate_input("BBone Z Size")
-            if self.evaluate_input("BBone HQ Deformation"):
-                b.bbone_mapping_mode = "CURVED"
-            # 'bbone_handle_type_start'    : ("BBone Start Handle Type", "AUTO"),
-            # 'bbone_handle_type_end'      : ("BBone End Handle Type", "AUTO"),
-            # 'bbone_custom_handle_start'  : ("BBone Custom Start Handle", "AUTO"),
-            # 'bbone_custom_handle_end'    : ("BBone Custom End Handle", "AUTO"),
-            if handle_type := self.evaluate_input("BBone Start Handle Type"):
-                b.bbone_handle_type_start = handle_type
-            if handle_type := self.evaluate_input("BBone End Handle Type"):
-                b.bbone_handle_type_end = handle_type
-
-            try:
-                if (custom_handle := self.evaluate_input("BBone Custom Start Handle")):
-                    b.bbone_custom_handle_start = self.bGetParentArmature().data.bones[custom_handle]
-                if (custom_handle := self.evaluate_input("BBone Custom End Handle")):
-                    b.bbone_custom_handle_end = self.bGetParentArmature().data.bones[custom_handle]
-            except KeyError:
-                prRed("Warning: BBone start or end handle not set because of missing bone in armature.")
-
-            bone_props_socket= {
-                'bbone_curveinx'     : ("BBone X Curve-In", 0.0),
-                'bbone_curveinz'     : ("BBone Z Curve-In", 0.0),
-                'bbone_curveoutx'    : ("BBone X Curve-Out", 0.0),
-                'bbone_curveoutz'    : ("BBone Z Curve-Out", 0.0),
-            }
-            evaluate_sockets(self, b, bone_props_socket)
-            # TODO this section should be done with props-socket thing
-            b.bbone_handle_use_scale_start = self.evaluate_input("BBone Start Handle Scale")
-            b.bbone_handle_use_scale_end = self.evaluate_input("BBone End Handle Scale")
-
+        if self.evaluate_input("BBone HQ Deformation"):
+            b.bbone_mapping_mode = "CURVED"
+        if handle_type := self.evaluate_input("BBone Start Handle Type"):
+            b.bbone_handle_type_start = handle_type
+        if handle_type := self.evaluate_input("BBone End Handle Type"):
+            b.bbone_handle_type_end = handle_type
+        try:
+            if (custom_handle := self.evaluate_input("BBone Custom Start Handle")):
+                b.bbone_custom_handle_start = self.bGetParentArmature().data.bones[custom_handle]
+            if (custom_handle := self.evaluate_input("BBone Custom End Handle")):
+                b.bbone_custom_handle_end = self.bGetParentArmature().data.bones[custom_handle]
+        except KeyError:
+            prRed("Warning: BBone start or end handle not set because of missing bone in armature.")
 
 
         import bpy
         import bpy
-
         pb = self.bGetParentArmature().pose.bones[self.bObject]
         pb = self.bGetParentArmature().pose.bones[self.bObject]
-        rotation_mode = self.evaluate_input("Rotation Order")
-        if rotation_mode == "AUTO": rotation_mode = "XYZ"
-        pb.rotation_mode = rotation_mode
-        pb.id_properties_clear()
-        self.setup_custom_props(pb, "Custom Properties")
 
 
-        if (pb.is_in_ik_chain):
-            # this  props_socket thing wasn't really meant to work here but it does, neat
-            props_sockets = {
-            'ik_stretch'          : ("IK Stretch", 0),
-            'lock_ik_x'           : (("Lock IK", 0), False),
-            'lock_ik_y'           : (("Lock IK", 1), False),
-            'lock_ik_z'           : (("Lock IK", 2), False),
-            'ik_stiffness_x'      : (("IK Stiffness", 0), 0.0),
-            'ik_stiffness_y'      : (("IK Stiffness", 1), 0.0),
-            'ik_stiffness_z'      : (("IK Stiffness", 2), 0.0),
-            'use_ik_limit_x'      : (("Limit IK", 0), False),
-            'use_ik_limit_y'      : (("Limit IK", 1), False),
-            'use_ik_limit_z'      : (("Limit IK", 2), False),
-            'ik_min_x'            : ("X Min", 0),
-            'ik_max_x'            : ("X Max", 0),
-            'ik_min_y'            : ("Y Min", 0),
-            'ik_max_y'            : ("Y Max", 0),
-            'ik_min_z'            : ("Z Min", 0),
-            'ik_max_z'            : ("Z Max", 0),
-            }
+        if check_for_driver(self, 'Rotation Order'):
+            # this is stupid and a HACK
+            from .mantis_dataclasses import MantisSocketFiller
+            props_sockets = {"rotation_mode": MantisSocketFiller( socket_name="Rotation Order",
+                property_name="rotation_mode", default_value='XYZ',)}
             evaluate_sockets(self, pb, props_sockets)
             evaluate_sockets(self, pb, props_sockets)
-        if do_bb:
-            props_sockets = {
-            'bbone_curveinx'             : ("BBone X Curve-In", pb.bone.bbone_curveinx),
-            'bbone_curveinz'             : ("BBone Z Curve-In", pb.bone.bbone_curveinz),
-            'bbone_curveoutx'            : ("BBone X Curve-Out", pb.bone.bbone_curveoutx),
-            'bbone_curveoutz'            : ("BBone Z Curve-Out", pb.bone.bbone_curveoutz),
-            'bbone_easein'               : ("BBone Ease-In", 0),
-            'bbone_easeout'              : ("BBone Ease-Out", 0),
-            'bbone_rollin'               : ("BBone Roll-In", 0),
-            'bbone_rollout'              : ("BBone Roll-Out", 0),
-            'bbone_scalein'              : ("BBone Scale-In", (1,1,1)),
-            'bbone_scaleout'             : ("BBone Scale-Out", (1,1,1)),
-            }
-            evaluate_sockets(self, pb, props_sockets)
-            # we need to clear this stuff since our only real goal was to get some drivers from the above
-            for attr_name in props_sockets.keys():
-                try:
-                    setattr(pb, attr_name, 0) # just clear it
-                except (ValueError, TypeError): # Blender 5.2 raises TypeError for scalar -> vector
-                    setattr(pb, attr_name, (1.0,1.0,1.0)) # scale needs to be set to 1
-
+        else: # This is stupid, but I did it to myself and it will be annoying to fix
+            rotation_mode = self.evaluate_input("Rotation Order")
+            if rotation_mode == "AUTO": rotation_mode = "XYZ"
+            pb.rotation_mode = rotation_mode
+            
+            
 
 
-            # important TODO... all of the drivers and stuff should be handled this way, right?
-        # time to set up drivers!
-
-        # just gonna add this to the end and build off it I guess
-        props_sockets = {
-            "lock_location"               : ("Lock Location", [False, False, False]),
-            "lock_rotation"               : ("Lock Rotation", [False, False, False]),
-            "lock_scale"                  : ("Lock Scale", [False, False, False]),
-            'custom_shape_scale_xyz'      : ("Custom Object Scale",  (0.0,0.0,0.0) ),
-            'custom_shape_translation'    : ("Custom Object Translation",  (0.0,0.0,0.0) ),
-            'custom_shape_rotation_euler' : ("Custom Object Rotation",  (0.0,0.0,0.0) ),
-            'use_custom_shape_bone_size'  : ("Custom Object Scale to Bone Length",  True,),
-        }
-        # in 5.0.0 and newer I have to do pose bone, this is an error in < 5.0
-        if bpy_version >=(5,0,0):
-            props_sockets['hide'] = ("Hide", False)
+        pb.id_properties_clear()
+        self.setup_custom_props(pb, "Custom Properties")
+        pb_props_sockets = self.gen_property_socket_map(prop_owner_group='pose_bone')
+        evaluate_sockets(self, pb, pb_props_sockets)
 
 
-        evaluate_sockets(self, pb, props_sockets)
+        bone_props_socket = self.gen_property_socket_map(prop_owner_group='bone')
+        evaluate_sockets(self, pb.bone, bone_props_socket)
 
 
-        # this could probably be moved to bTransformPass
-        # TODO VERSIONING: newer (5.0+ ?) Blenders need to use PoseBone for this
-        props_sockets = {
-            'hide'      : ("Hide", False),
-            'show_wire' : ("Custom Object Wireframe", False),
-        }
-        evaluate_sockets(self, pb.bone, props_sockets)
+        # and in edit mode we need to explicitly set the bbone properties
+        # I don't like this HACK but there seems to be no other way...
+        # Better to ask forgiveness than permission. If anyone wants control over this, I'll solve the problem then
+        # IN the near future, in fact, I think I'll make a BBone Rest Pose matrix thing
+        # and allow users to set it from bezier curves
+        pb.bone.bbone_easein = 1; pb.bone.bbone_easeout = 1
 
 
-        # whatever I was doing there.... was stupid. CLEAN UP TODO
-        # this is the right thing to do.
         finish_drivers(self)
         finish_drivers(self)
-        #
-        # OK, visual settings
-        #
-        # Get the override xform's bone:
-
-
 
 
         pb.custom_shape_transform = None
         pb.custom_shape_transform = None
         pb.custom_shape = None
         pb.custom_shape = None
-
         if len(self.inputs["Custom Object xForm Override"].links) > 0:
         if len(self.inputs["Custom Object xForm Override"].links) > 0:
             trace = trace_single_line(self, "Custom Object xForm Override")
             trace = trace_single_line(self, "Custom Object xForm Override")
             try:
             try:
                 pb.custom_shape_transform = trace[0][1].bGetObject()
                 pb.custom_shape_transform = trace[0][1].bGetObject()
             except AttributeError:
             except AttributeError:
                 pass
                 pass
-
         if len(self.inputs["Custom Object"].links) > 0:
         if len(self.inputs["Custom Object"].links) > 0:
             trace = trace_single_line(self, "Custom Object")
             trace = trace_single_line(self, "Custom Object")
             try:
             try:
@@ -668,10 +601,7 @@ class xFormGeometryObject(xFormNode):
         self.has_shape_keys = False
         self.has_shape_keys = False
         # putting this in bTransformPass simply prevents it from being run more than once.
         # putting this in bTransformPass simply prevents it from being run more than once.
         # maybe I should do that with the rest of bPrepare, too.
         # maybe I should do that with the rest of bPrepare, too.
-        props_sockets = {
-            'hide_viewport'    : ("Hide in Viewport", False),
-            'hide_render'      : ("Hide in Render", False),
-        }
+        props_sockets = self.gen_property_socket_map()
         evaluate_sockets(self, self.bObject, props_sockets)
         evaluate_sockets(self, self.bObject, props_sockets)
         self.executed = True
         self.executed = True
 
 

+ 160 - 77
xForm_socket_templates.py

@@ -1,6 +1,7 @@
 from .mantis_dataclasses import MantisSocketTemplate as SockTemplate
 from .mantis_dataclasses import MantisSocketTemplate as SockTemplate
 from .misc_nodes_socket_templates import SplineIndexTemplate
 from .misc_nodes_socket_templates import SplineIndexTemplate
 from dataclasses import replace
 from dataclasses import replace
+from bpy.app import version as bpy_version
 
 
 xFormArmatureSockets=[
 xFormArmatureSockets=[
     NameTemplate := SockTemplate(
     NameTemplate := SockTemplate(
@@ -16,7 +17,7 @@ xFormArmatureSockets=[
         name="Relationship", is_input=True,  bl_idname='RelationshipSocket', ),
         name="Relationship", is_input=True,  bl_idname='RelationshipSocket', ),
     CustomPropsTemplate := SockTemplate(
     CustomPropsTemplate := SockTemplate(
         name="Custom Properties", is_input=True,  bl_idname='CustomPropSocket',
         name="Custom Properties", is_input=True,  bl_idname='CustomPropSocket',
-            use_multi_input=True, ),
+            use_multi_input=True, is_animatable=False, ),
     xFormOutTemplate := SockTemplate(
     xFormOutTemplate := SockTemplate(
         name="xForm Out", is_input=False,  bl_idname='xFormSocket', ),
         name="xForm Out", is_input=False,  bl_idname='xFormSocket', ),
 ]
 ]
@@ -84,151 +85,233 @@ xFormGetBoneSockets = [
 ]
 ]
 
 
 
 
+# Some (especially BBone) properties are good to treat differently
+#  based on bone/posebone or value/driver
+# I'll use these lists to make decisions about that sort of thing
+attributes_shared_by_bone_and_pose_bone = [
+    "bbone_curveinx",
+    "bbone_curveinz",
+    "bbone_curveoutx",
+    "bbone_curveoutz",
+    "bbone_custom_handle_end",
+    "bbone_custom_handle_start",
+    "bbone_easein",
+    "bbone_easeout",
+    "bbone_rollin",
+    "bbone_rollout",
+    "bbone_scalein",
+    "bbone_scaleout",
+]
+
+# these need to be 1 in edit mode and 0 in pose mode, by default. Pain.
+need_different_defaults = [
+    ("bbone_easein", 1, 0 ),
+    ("bbone_easeout", 1, 0),
+]
+
+
+# VERSIONING for Bone
+bone_hide_prop_group='bone'
+if bpy_version >= (5,0,0):
+    bone_hide_prop_group='pose_bone'
+bone_hide_template = replace(HideTemplate, name='Hide', category='always_show',
+                            blender_property='hide', default_value=False,
+                            prop_owner_group = bone_hide_prop_group)
+
+bbone_template = SockTemplate( name="<BBone Template>",
+        default_value = 0.0, blender_property=None, is_input=True,
+        bl_idname='FloatSocket', prop_owner_group='bone',)
+
+# And there is some serious confusing stuff regarding BBones so I'll explain...
+# Blender has duplicated properties for Bone and Pose Bone
+# When there is a driver, you usually want the Pose Bone to hold it
+# but when there isn't you probably want it on the Bone since it isn't animated
+
 # and bones! this one is a bit much...
 # and bones! this one is a bit much...
 from math import pi
 from math import pi
 xFormBoneSockets = [
 xFormBoneSockets = [
-    replace(NameTemplate, default_value='Bone', category='always_show'),
-    replace(RotationOrderTemplate, category='always_show'),
-    BoneMatrixTemplate := replace(ObjectMatrixTemplate,
-            blender_property='matrix', category='always_show'),
-    replace(RelationshipInTemplate, category='always_show'),
+    replace(NameTemplate, default_value='Bone', category='always_show',
+            is_animatable=False,),
+    replace(RotationOrderTemplate, category='always_show',),
+    BoneMatrixTemplate := replace(ObjectMatrixTemplate, is_animatable=False,
+            blender_property='matrix', category='always_show',),
+    replace(RelationshipInTemplate, category='always_show', is_animatable=False,),
     # IK
     # IK
     IKStretch := SockTemplate(
     IKStretch := SockTemplate(
         name="IK Stretch", is_input=True,  bl_idname='FloatFactorSocket',
         name="IK Stretch", is_input=True,  bl_idname='FloatFactorSocket',
-        default_value=0, blender_property='ik_stretch', category='IK'),
+        default_value=0, blender_property='ik_stretch', category='IK',
+        prop_owner_group='pose_bone',),
     IKLock := SockTemplate(
     IKLock := SockTemplate(
         name="Lock IK", is_input=True,  bl_idname='BooleanThreeTupleSocket',
         name="Lock IK", is_input=True,  bl_idname='BooleanThreeTupleSocket',
         default_value=(False, False, False), category='IK',
         default_value=(False, False, False), category='IK',
-        blender_property=('lock_ik_x', 'lock_ik_y', 'lock_ik_z'),),
+        blender_property=('lock_ik_x', 'lock_ik_y', 'lock_ik_z'),
+        prop_owner_group='pose_bone', ),
     IKStiffness := SockTemplate(
     IKStiffness := SockTemplate(
         name="IK Stiffness", is_input=True,  bl_idname='BooleanThreeTupleSocket',
         name="IK Stiffness", is_input=True,  bl_idname='BooleanThreeTupleSocket',
         default_value=(False, False, False), category='IK',
         default_value=(False, False, False), category='IK',
-        blender_property=('ik_stiffness_x', 'ik_stiffness_y', 'ik_stiffness_z')),
+        blender_property=('ik_stiffness_x', 'ik_stiffness_y', 'ik_stiffness_z'),
+        prop_owner_group='pose_bone', ),
     IKLimit := SockTemplate(
     IKLimit := SockTemplate(
         name="Limit IK", is_input=True,  bl_idname='BooleanThreeTupleSocket',
         name="Limit IK", is_input=True,  bl_idname='BooleanThreeTupleSocket',
         default_value=(False, False, False), category='IK',
         default_value=(False, False, False), category='IK',
-        blender_property=('use_ik_limit_x', 'use_ik_limit_y', 'use_ik_limit_z')),
+        blender_property=('use_ik_limit_x', 'use_ik_limit_y', 'use_ik_limit_z'),
+        prop_owner_group='pose_bone', ),
     IKXMin := SockTemplate(
     IKXMin := SockTemplate(
         name="X Min", is_input=True,  bl_idname='NodeSocketFloatAngle',
         name="X Min", is_input=True,  bl_idname='NodeSocketFloatAngle',
-        default_value=-pi, blender_property='ik_min_x', category='IK'),
+        default_value=-pi, blender_property='ik_min_x', category='IK',
+        prop_owner_group='pose_bone', ),
     IKXMax := replace(IKXMin, name="X Max", default_value=pi,
     IKXMax := replace(IKXMin, name="X Max", default_value=pi,
-                      blender_property='ik_max_x'),
+                      blender_property='ik_max_x',),
     IKYMax := replace(IKXMin, name="Y Min", blender_property='ik_min_y'),
     IKYMax := replace(IKXMin, name="Y Min", blender_property='ik_min_y'),
     IKYMax := replace(IKXMax, name="Y Max", blender_property='ik_max_y'),
     IKYMax := replace(IKXMax, name="Y Max", blender_property='ik_max_y'),
     IKZMax := replace(IKXMin, name="Z Min", blender_property='ik_min_z'),
     IKZMax := replace(IKXMin, name="Z Min", blender_property='ik_min_z'),
     IKZMax := replace(IKXMax, name="Z Max", blender_property='ik_max_z'),
     IKZMax := replace(IKXMax, name="Z Max", blender_property='ik_max_z'),
     # Visual Stuff
     # Visual Stuff
     BoneCollectionTemplate := SockTemplate(name="Bone Collection", is_input=True,
     BoneCollectionTemplate := SockTemplate(name="Bone Collection", is_input=True,
-            bl_idname='BoneCollectionSocket', category='Display', use_multi_input=True),
+            bl_idname='BoneCollectionSocket', category='Display', use_multi_input=True,
+            is_animatable=False,),
     CustomObjectTemplate := replace(SourcexFormTemplate, name='Custom Object',
     CustomObjectTemplate := replace(SourcexFormTemplate, name='Custom Object',
-                                    category='Display'),
-    OverrideXFormTemplate := replace(SourcexFormTemplate,
-                    name='Custom Object xForm Override', category='Display'),
+        category='Display', is_animatable=False,),
+    OverrideXFormTemplate := replace(SourcexFormTemplate, is_animatable=False,
+        name='Custom Object xForm Override', category='Display',),
     CustomObjectScaleToBoneTemplate := replace(HideRenderTemplate,
     CustomObjectScaleToBoneTemplate := replace(HideRenderTemplate,
-            name='Custom Object Scale to Bone Length', category='Display',
-            blender_property='use_custom_shape_bone_size',
-            default_value=True,),
+        name='Custom Object Scale to Bone Length', category='Display',
+        blender_property='use_custom_shape_bone_size',
+        default_value=True, prop_owner_group='pose_bone', ),
     CustomObjectWireframeTemplate := replace(HideRenderTemplate,
     CustomObjectWireframeTemplate := replace(HideRenderTemplate,
             name='Custom Object Wireframe', category='Display',
             name='Custom Object Wireframe', category='Display',
-            blender_property='show_wire', default_value=True,),
+            blender_property='show_wire', default_value=True,
+            prop_owner_group='bone', ),
     CustomObjectScaleTemplate := SockTemplate(name="Custom Object Scale",
     CustomObjectScaleTemplate := SockTemplate(name="Custom Object Scale",
             is_input=True, bl_idname='VectorScaleSocket',
             is_input=True, bl_idname='VectorScaleSocket',
-            category='Display', default_value=(1.0,1.0,1.0),),
+            category='Display', default_value=(1.0,1.0,1.0),
+            blender_property = 'custom_shape_scale_xyz',
+            prop_owner_group='pose_bone', ),
     CustomObjectTranslationTemplate := SockTemplate(name="Custom Object Translation",
     CustomObjectTranslationTemplate := SockTemplate(name="Custom Object Translation",
             is_input=True, bl_idname='VectorSocket',
             is_input=True, bl_idname='VectorSocket',
-            category='Display', default_value=(0.0,0.0,0.0),),
+            category='Display', default_value=(0.0,0.0,0.0),
+            blender_property = 'custom_shape_translation',
+            prop_owner_group='pose_bone', ),
     CustomObjectRotationTemplate := SockTemplate(name="Custom Object Rotation",
     CustomObjectRotationTemplate := SockTemplate(name="Custom Object Rotation",
             is_input=True, bl_idname='VectorEulerSocket',
             is_input=True, bl_idname='VectorEulerSocket',
-            category='Display', default_value=(0.0,0.0,0.0),),
+            category='Display', default_value=(0.0,0.0,0.0),
+            blender_property = 'custom_shape_rotation_euler',
+            prop_owner_group='pose_bone', ),
     CustomColor := SockTemplate(name="Color",
     CustomColor := SockTemplate(name="Color",
             is_input=True, bl_idname='ColorSetSocket',
             is_input=True, bl_idname='ColorSetSocket',
-            category='Display',),
+            category='Display', is_animatable=False,),
     InheritColor := SockTemplate(name="Inherit Color",
     InheritColor := SockTemplate(name="Inherit Color",
-            is_input=True, bl_idname='BooleanSocket',
-            category='Display', default_value=True),
+            is_input=True, bl_idname='BooleanSocket', is_animatable=False,
+            category='Display', default_value=True, prop_owner_group='pose_bone', ),
     # Deform Stuff
     # Deform Stuff
     BoneDeformTemplate := replace(HideRenderTemplate, name='Deform',
     BoneDeformTemplate := replace(HideRenderTemplate, name='Deform',
-        category='Deform', blender_property='use_deform', default_value=False,),
+        category='Deform', blender_property='use_deform', default_value=False,
+        prop_owner_group='edit_bone', is_animatable=False,),
     EnvelopeDistanceTemplate := SockTemplate( name='Envelope Distance',
     EnvelopeDistanceTemplate := SockTemplate( name='Envelope Distance',
-            bl_idname='FloatPositiveSocket', category='Deform',
-            blender_property='envelope_distance', default_value=0,
-            is_input=True,),
-    EnvelopeWeightTemplate := replace(EnvelopeDistanceTemplate,
-            name='Envelope Weight', bl_idname = 'FloatFactorSocket',
-            blender_property='envelope_weight', default_value=1.0,),
-    EnvelopeWeightTemplate := replace(EnvelopeDistanceTemplate,
-            name='Envelope Multiply', bl_idname = 'BooleanSocket',
-            blender_property='use_envelope_multiply', default_value=False,),
+        bl_idname='FloatPositiveSocket', category='Deform',
+        blender_property='envelope_distance', default_value=0,
+        is_input=True, prop_owner_group='edit_bone', is_animatable=False,),
     EnvelopeWeightTemplate := replace(EnvelopeDistanceTemplate,
     EnvelopeWeightTemplate := replace(EnvelopeDistanceTemplate,
+        name='Envelope Weight', bl_idname = 'FloatFactorSocket',
+        blender_property='envelope_weight', default_value=1.0,
+        prop_owner_group='edit_bone', is_animatable=False,),
+    EnvelopeMultiplyTemplate := replace(EnvelopeDistanceTemplate,
+        name='Envelope Multiply', bl_idname = 'BooleanSocket',
+        blender_property='use_envelope_multiply', default_value=False,
+        prop_owner_group='edit_bone', is_animatable=False,),
+    EnvelopeHeadRadiusTemplate := replace(EnvelopeDistanceTemplate,
         name='Envelope Head Radius', blender_property='head_radius', default_value=0.0,),
         name='Envelope Head Radius', blender_property='head_radius', default_value=0.0,),
-    EnvelopeWeightTemplate := replace(EnvelopeDistanceTemplate,
+    EnvelopeTailRadiusTemplate := replace(EnvelopeDistanceTemplate,
         name='Envelope Tail Radius', blender_property='tail_radius', default_value=0.0,),
         name='Envelope Tail Radius', blender_property='tail_radius', default_value=0.0,),
     # BBone Stuff
     # BBone Stuff
     BBoneSegmentsTemplate := SockTemplate(name="BBone Segments", is_input=True,
     BBoneSegmentsTemplate := SockTemplate(name="BBone Segments", is_input=True,
         bl_idname='UnsignedIntSocket', category = 'bbone',
         bl_idname='UnsignedIntSocket', category = 'bbone',
-        blender_property='bbone_segments', default_value=1 ),
+        blender_property='bbone_segments', default_value=1,
+        prop_owner_group='bone', ),
     BBoneXSizeTemplate := replace(BBoneSegmentsTemplate, name='BBone X Size',
     BBoneXSizeTemplate := replace(BBoneSegmentsTemplate, name='BBone X Size',
-        bl_idname='FloatSocket', blender_property='bbone_x', default_value=0.0025, ),
+        bl_idname='FloatSocket', blender_property='bbone_x', default_value=0.0025,
+        prop_owner_group='bone',),
     BBoneZSizeTemplate := replace(BBoneXSizeTemplate, name='BBone Z Size',
     BBoneZSizeTemplate := replace(BBoneXSizeTemplate, name='BBone Z Size',
         blender_property='bbone_z', ),
         blender_property='bbone_z', ),
     BBoneHQDeformation := replace(BBoneSegmentsTemplate, name='BBone HQ Deformation',
     BBoneHQDeformation := replace(BBoneSegmentsTemplate, name='BBone HQ Deformation',
-        bl_idname='BooleanSocket', blender_property='', default_value=None ),
+        bl_idname='BooleanSocket', blender_property='', default_value=None, is_animatable=False, ),
     BBoneXCurveInTemplate := replace( BBoneXSizeTemplate, name="BBone X Curve-In",
     BBoneXCurveInTemplate := replace( BBoneXSizeTemplate, name="BBone X Curve-In",
-        bl_idname='FloatSocket', blender_property='bbone_curveinx', default_value=0.0, ),
+        bl_idname='FloatSocket', blender_property='bbone_curveinx', default_value=0.0,
+        prop_owner_group='bone',),
     BBoneZCurveInTemplate := replace(BBoneXCurveInTemplate, name="BBone Z Curve-In",
     BBoneZCurveInTemplate := replace(BBoneXCurveInTemplate, name="BBone Z Curve-In",
-        blender_property='bbone_curveinz', ),
+        blender_property='bbone_curveinz', prop_owner_group='bone', ),
     BBoneXCurveOutTemplate := replace(BBoneXCurveInTemplate, name="BBone X Curve-Out",
     BBoneXCurveOutTemplate := replace(BBoneXCurveInTemplate, name="BBone X Curve-Out",
-        blender_property='bbone_curveoutx', ),
+                                      default_value=0.0,
+        blender_property='bbone_curveoutx', prop_owner_group='bone', ),
     BBoneZCurveOutTemplate := replace(BBoneXCurveInTemplate, name="BBone Z Curve-Out",
     BBoneZCurveOutTemplate := replace(BBoneXCurveInTemplate, name="BBone Z Curve-Out",
-        blender_property='bbone_curveoutz', ),  # I'm tired of assigning variables, not gonna bother anymore lol
-                                                # it's just a conincidence that a lot of these are also unimplemented
-    replace(BBoneXCurveInTemplate, name="BBone Roll-In", blender_property='bbone_rollin', ), # CURRENTLY UNIMPLEMENTED
-    replace(BBoneXCurveInTemplate, name="BBone Roll-Out", blender_property='bbone_rollout', ), # CURRENTLY UNIMPLEMENTED
-    replace(BBoneXCurveInTemplate, name="BBone Inherit End Roll",
-            bl_idname='BooleanSocket', default_value=False,), # CURRENTLY UNIMPLEMENTED
-    replace(BBoneXCurveInTemplate, name="BBone Scale-In",
-            bl_idname='VectorSocket', default_value=(0.0,0.0,0.0),), # CURRENTLY DOESN'T WORK
-    replace(BBoneXCurveInTemplate, name="BBone Scale-Out",
-            bl_idname='VectorSocket', default_value=(0.0,0.0,0.0)), # CURRENTLY DOESN'T WORK
-    replace(BBoneXCurveInTemplate, name="BBone Ease-In",), # CURRENTLY DOESN'T WORK
-    replace(BBoneXCurveInTemplate, name="BBone Ease-Out",), # CURRENTLY DOESN'T WORK
-    replace(BBoneXCurveInTemplate, name="BBone Easing",
-            bl_idname='BooleanSocket', default_value=False,), # CURRENTLY DOESN'T WORK
-    replace(BBoneXCurveInTemplate, name="BBone Start Handle Type",
+        blender_property='bbone_curveoutz', prop_owner_group='bone', ), 
+    replace(bbone_template, name="BBone Roll-In",
+            default_value = 0.0,
+            blender_property='bbone_rollin',),
+    replace(bbone_template, name="BBone Roll-Out", blender_property='bbone_rollout', ),
+    replace(bbone_template, name="BBone Inherit End Roll",
+            bl_idname='BooleanSocket', default_value=False,
+            blender_property = 'use_endroll_as_inroll',
+            is_animatable=False, prop_owner_group='bone',),
+    replace(bbone_template, name="BBone Scale-In",
+            bl_idname='VectorSocket', default_value=(1.0,1.0,1.0),
+            blender_property = 'bbone_scalein'),
+    replace(bbone_template, name="BBone Scale-Out",
+            bl_idname='VectorSocket', default_value=(1.0,1.0,1.0),
+            blender_property = 'bbone_scaleout'),
+    replace(bbone_template, name="BBone Ease-In",
+            blender_property = 'bbone_easein'),
+    replace(bbone_template, name="BBone Ease-Out",
+            blender_property = 'bbone_easeout'),
+    replace(bbone_template, name="BBone Easing",
+            bl_idname='BooleanSocket', default_value=False,
+            blender_property='use_scale_easing',  prop_owner_group='bone',),
+    replace(bbone_template, name="BBone Start Handle Type",
             bl_idname="EnumBBoneHandleType",
             bl_idname="EnumBBoneHandleType",
-            blender_property='bbone_handle_type_start', default_value=None, ),
-    replace(BBoneXCurveInTemplate, name="BBone Custom Start Handle",
+            blender_property='bbone_handle_type_start', default_value=None,
+            prop_owner_group='bone', is_animatable=False,),
+    replace(bbone_template, name="BBone Custom Start Handle",
             bl_idname="StringSocket",
             bl_idname="StringSocket",
-            blender_property='bbone_custom_handle_start', default_value=None, ),
-    replace(BBoneXCurveInTemplate, name="BBone Start Handle Scale",
+            blender_property=None, # not handled by this system
+            default_value=None, is_animatable=False,),
+    replace(bbone_template, name="BBone Start Handle Scale",
             bl_idname="BooleanThreeTupleSocket",
             bl_idname="BooleanThreeTupleSocket",
-            blender_property='bbone_handle_use_scale_start', default_value=None, ),
-    replace(BBoneXCurveInTemplate, name="BBone Start Handle Ease",
-            bl_idname='BooleanSocket', default_value=False),# CURRENTLY DOESN'T WORK),
-    replace(BBoneXCurveInTemplate, name="BBone End Handle Type",
+            default_value = (False, False, False),
+            blender_property='bbone_handle_use_scale_start',
+            prop_owner_group='bone', ),
+    replace(bbone_template, name="BBone Start Handle Ease",
+            bl_idname='BooleanSocket', default_value=False,
+            blender_property = 'bbone_handle_use_ease_start',
+            prop_owner_group='bone',),
+    replace(bbone_template, name="BBone End Handle Type",
             bl_idname="EnumBBoneHandleType",
             bl_idname="EnumBBoneHandleType",
-            blender_property='bbone_handle_type_end', default_value=None, ),
-    replace(BBoneXCurveInTemplate, name="BBone Custom End Handle",
+            blender_property='bbone_handle_type_end', default_value=None,
+            prop_owner_group='bone', is_animatable=False,),
+    replace(bbone_template, name="BBone Custom End Handle",
             bl_idname="StringSocket",
             bl_idname="StringSocket",
-            blender_property='bbone_custom_handle_end', default_value=None, ),
-    replace(BBoneXCurveInTemplate, name="BBone End Handle Scale",
+            blender_property=None, # not handled by this system
+            default_value=None, is_animatable=False,),
+    replace(bbone_template, name="BBone End Handle Scale",
             bl_idname="BooleanThreeTupleSocket",
             bl_idname="BooleanThreeTupleSocket",
+            default_value= (False, False, False),
             blender_property='bbone_handle_use_scale_end',
             blender_property='bbone_handle_use_scale_end',
-            default_value= (False, False, False)),
-    replace(BBoneXCurveInTemplate, name="BBone End Handle Ease",
-            bl_idname='BooleanSocket', default_value=False),# CURRENTLY DOESN'T WORK),
+            prop_owner_group='bone',),
+    replace(bbone_template, name="BBone End Handle Ease",
+            bl_idname='BooleanSocket', default_value=False,
+            blender_property = 'bbone_handle_use_ease_end',
+            prop_owner_group='bone',),
     # locks
     # locks
     LockLocationTemplate := SockTemplate(name="Lock Location",
     LockLocationTemplate := SockTemplate(name="Lock Location",
         is_input=True, bl_idname='BooleanThreeTupleSocket', category = 'lock',
         is_input=True, bl_idname='BooleanThreeTupleSocket', category = 'lock',
-        blender_property='lock_location', default_value=[True, True, True] ),
+        blender_property='lock_location', default_value=[True, True, True],
+        prop_owner_group='pose_bone', ),
     LockRotationTemplate := replace(LockLocationTemplate, name="Lock Rotation",
     LockRotationTemplate := replace(LockLocationTemplate, name="Lock Rotation",
         blender_property='lock_rotation',),
         blender_property='lock_rotation',),
     LockRotationTemplate := replace(LockLocationTemplate, name="Lock Scale",
     LockRotationTemplate := replace(LockLocationTemplate, name="Lock Scale",
         blender_property='lock_scale',),
         blender_property='lock_scale',),
     # hide
     # hide
-    replace(HideTemplate, name='Hide', category='always_show',
-            blender_property='hide', default_value=False,),
+    bone_hide_template,
     CustomPropsTemplate,
     CustomPropsTemplate,
     # Output
     # Output
     xFormOutTemplate,
     xFormOutTemplate,
-]
+]