Bläddra i källkod

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 månad sedan
förälder
incheckning
9fba5be67f
9 ändrade filer med 429 tillägg och 280 borttagningar
  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():
             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 = {}
         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:
                 continue
+            filler = MantisSocketFiller(socket_name=s_template.name)
             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)):
                 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
 
     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_max = 10
             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):
             mt_node = t.links[0].from_node; sk_ob = mt_node.GetxForm().bGetObject()
             if sk_ob is None: continue
@@ -574,9 +580,6 @@ class DeformerMorphTargetDeform(MantisDeformerNode):
         finish_drivers(self)
         prWhite(f"Initializing morph target took {time() -start_time} seconds")
 
-
-        
-
     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: 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_max = 10
             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):
             mt_node = t.links[0].from_node; sk_ob = mt_node.GetxForm().bGetObject()
             if sk_ob is None: continue

+ 24 - 7
link_nodes.py

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

+ 21 - 2
mantis_dataclasses.py

@@ -1,5 +1,5 @@
 from dataclasses import dataclass, field
-from typing import List, Any, Tuple
+from typing import List, Any, Tuple, Optional
 
 @dataclass
 class MantisSocketTemplate():
@@ -19,6 +19,7 @@ class MantisSocketTemplate():
     #  where multiple blender objects own the properties, like e.g. bone/posebone
     prop_owner_group : str = field(default="")
     # 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
 class xForm_info():
@@ -78,4 +79,22 @@ class MantisBBoneTransform():
     # Custom Handles
     use_bbone_custom_handles: bool = field(default=False)
     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"])
         add_these.append(scale1)
         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")
     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':
         from ..base_definitions import 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__}"))
         return
     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:
         setup_custom_props_from_np(mantis_node, ui_node)
     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):
     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]
     # 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')
 
-# 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,):
     # this is neccesary because some things use dict properties for dynamic properties and setattr doesn't work
     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
             prop=att_name.split('[')[0]
             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:
             try:
                 setattr(ob, att_name, val)
             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
             # TODO: deduplicate this terrible hack
             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]
                     sub_prop = (split_prop[0])
                     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:
                         sub_props.append(getattr(sub_props[-1], sub_prop))
                 safe_setattr(sub_props[-1], prop, default)
             # this is really stupid
             else:
                 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"]:
-                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"]:
-                mantis_node.drivers[sock] = "key_blocks[\""+original_prop+"\"].value"
+                mantis_node.drivers[driver_key] = "key_blocks[\""+original_prop+"\"].value"
             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
-            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'
                 # UGLY HACK          # because it is available in older
                 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)))
                 else:
                     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
                     # 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__"):
@@ -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.
                                 from .drivers import 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))
                                     try:
                                         mantis_node.drivers[sock].append((prop, val_index))
@@ -257,24 +266,50 @@ def evaluate_sockets(mantis_node, b_object, props_sockets,):
                                 else:
                                     getattr(b_object,prop)[val_index] =  value
                         else:
-                            # prOrange("Skipping the Thing", getattr(b_object, prop))
                             safe_setattr(b_object, prop, mantis_node.evaluate_input(sock))
                     except Exception as e:
                         prRed(b_object, mantis_node, prop, sock, mantis_node.evaluate_input(sock))
                         raise e
 
 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]
+    
+    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:
-        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
-        driver["ind"] = index
+        if not combo_property:
+            driver["ind"] = index
     else:
         driver = driver_provider.parameters[driver_socket.name].copy()
     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...
             sub_props = [b_object]
             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']:
             # 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.
-            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
-            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
             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
@@ -323,11 +361,13 @@ def finish_drivers(mantis_node):
         if mantis_node.node_type == 'LINK':
             b_objects = mantis_node.bObject # this is already a list
         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):
                 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:
-                    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:
                 drivers.append(finish_driver(mantis_node, b_object, driver_item, prop))
     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]
         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:
 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.'
@@ -459,6 +489,7 @@ versioning_tasks = [
     (['SchemaTree'], schema_enable_custom_interface_types, ['tree']),
     (['UtilityFCurve', 'LinkArmature'],
            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):
         super().reset_execution()
         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):
         from .mantis_dataclasses import custom_prop_template
@@ -149,9 +169,13 @@ class xFormArmature(xFormNode):
 
         #check if an object by the name exists
         if (name) and (ob := bpy.data.objects.get(name)):
+            # Clear the drivers from BOTH the object/pose and the skeleton
             if (ob.animation_data):
                 while (ob.animation_data.drivers):
                         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:
                 # clear it, even after deleting the edit bones,
                 #  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")
 
     def bPrepare(self, bContext=None):
+        self.check_drivers()
         self.parameters['Matrix'] = get_matrix(self)
         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."))
 
         # 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))
 
         parent_xForm_info = get_parent_xForm_info(self)
@@ -360,7 +381,6 @@ class xFormBone(xFormNode):
             self_edit_name=eb.name,
         )
         self.parameters['xForm Out'] = my_info
-
         self.executed = True
 
     def set_bone_color(self, b, inherit_color, bContext):
@@ -401,155 +421,68 @@ class xFormBone(xFormNode):
 
 
     def bFinalize(self, bContext = None):
+
         b = self.bGetParentArmature().data.bones[self.bObject]
         # let's do bone colors first
         inherit_color = self.evaluate_input("Inherit Color")
         if len(self.inputs['Color'].links) > 0:
             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)
-        # 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
-
         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)
-        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)
-        #
-        # OK, visual settings
-        #
-        # Get the override xform's bone:
-
-
 
         pb.custom_shape_transform = None
         pb.custom_shape = None
-
         if len(self.inputs["Custom Object xForm Override"].links) > 0:
             trace = trace_single_line(self, "Custom Object xForm Override")
             try:
                 pb.custom_shape_transform = trace[0][1].bGetObject()
             except AttributeError:
                 pass
-
         if len(self.inputs["Custom Object"].links) > 0:
             trace = trace_single_line(self, "Custom Object")
             try:
@@ -668,10 +601,7 @@ class xFormGeometryObject(xFormNode):
         self.has_shape_keys = False
         # putting this in bTransformPass simply prevents it from being run more than once.
         # 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)
         self.executed = True
 

+ 160 - 77
xForm_socket_templates.py

@@ -1,6 +1,7 @@
 from .mantis_dataclasses import MantisSocketTemplate as SockTemplate
 from .misc_nodes_socket_templates import SplineIndexTemplate
 from dataclasses import replace
+from bpy.app import version as bpy_version
 
 xFormArmatureSockets=[
     NameTemplate := SockTemplate(
@@ -16,7 +17,7 @@ xFormArmatureSockets=[
         name="Relationship", is_input=True,  bl_idname='RelationshipSocket', ),
     CustomPropsTemplate := SockTemplate(
         name="Custom Properties", is_input=True,  bl_idname='CustomPropSocket',
-            use_multi_input=True, ),
+            use_multi_input=True, is_animatable=False, ),
     xFormOutTemplate := SockTemplate(
         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...
 from math import pi
 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
     IKStretch := SockTemplate(
         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(
         name="Lock IK", is_input=True,  bl_idname='BooleanThreeTupleSocket',
         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(
         name="IK Stiffness", is_input=True,  bl_idname='BooleanThreeTupleSocket',
         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(
         name="Limit IK", is_input=True,  bl_idname='BooleanThreeTupleSocket',
         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(
         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,
-                      blender_property='ik_max_x'),
+                      blender_property='ik_max_x',),
     IKYMax := replace(IKXMin, name="Y Min", blender_property='ik_min_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(IKXMax, name="Z Max", blender_property='ik_max_z'),
     # Visual Stuff
     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',
-                                    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,
-            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,
             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",
             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",
             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",
             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",
             is_input=True, bl_idname='ColorSetSocket',
-            category='Display',),
+            category='Display', is_animatable=False,),
     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
     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',
-            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,
+        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,),
-    EnvelopeWeightTemplate := replace(EnvelopeDistanceTemplate,
+    EnvelopeTailRadiusTemplate := replace(EnvelopeDistanceTemplate,
         name='Envelope Tail Radius', blender_property='tail_radius', default_value=0.0,),
     # BBone Stuff
     BBoneSegmentsTemplate := SockTemplate(name="BBone Segments", is_input=True,
         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',
-        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',
         blender_property='bbone_z', ),
     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",
-        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",
-        blender_property='bbone_curveinz', ),
+        blender_property='bbone_curveinz', prop_owner_group='bone', ),
     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",
-        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",
-            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",
-            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",
-            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",
-            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",
-            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",
+            default_value= (False, False, False),
             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
     LockLocationTemplate := SockTemplate(name="Lock Location",
         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",
         blender_property='lock_rotation',),
     LockRotationTemplate := replace(LockLocationTemplate, name="Lock Scale",
         blender_property='lock_scale',),
     # hide
-    replace(HideTemplate, name='Hide', category='always_show',
-            blender_property='hide', default_value=False,),
+    bone_hide_template,
     CustomPropsTemplate,
     # Output
     xFormOutTemplate,
-]
+]