Эх сурвалжийг харах

Update Hook Deformer

hook deformer now has an input for strength, and can automatically set Bezier Handle hooks for easier use (indexing by bezier point instead of NURBS).
the ability to directly hook bezier handles is retained if you turn off the auto-bezier option.

If the strength of two hook deformers sharing a common controller is the same, the modifier
will be reused. But if there is a driver, or the strength is different, it will make a new modifier
TODO: I should check and see if the drivers are the same to reuse the modifer even if
there is a driver. but this isn't very important, so I haven't done it.
Joseph Brandenburg 6 сар өмнө
parent
commit
a1f215e3d3

+ 1 - 1
__init__.py

@@ -16,7 +16,7 @@ from .utilities import prRed
 
 
 MANTIS_VERSION_MAJOR=0
 MANTIS_VERSION_MAJOR=0
 MANTIS_VERSION_MINOR=10
 MANTIS_VERSION_MINOR=10
-MANTIS_VERSION_SUB=6
+MANTIS_VERSION_SUB=7
 
 
 classLists = [module.TellClasses() for module in [
 classLists = [module.TellClasses() for module in [
  link_definitions,
  link_definitions,

+ 6 - 3
base_definitions.py

@@ -572,14 +572,17 @@ SOCKETS_REMOVED=[("UtilityDriverVariable", "Transform Channel"),
                  ("xFormRootNode","World Out"),
                  ("xFormRootNode","World Out"),
                  ("UtilitySwitch","xForm"),
                  ("UtilitySwitch","xForm"),
                  ("LinkDrivenParameter", "Enable")]
                  ("LinkDrivenParameter", "Enable")]
-                  # Node Class           #Prior bl_idname  # prior name # new bl_idname # new name, # Multi
-SOCKETS_RENAMED=[ ("LinkDrivenParameter", "DriverSocket",   "Driver",     "FloatSocket",  "Value",  False)]
+                  # Node Class           #Prior bl_idname  # prior name # new bl_idname #       new name,          # Multi
+SOCKETS_RENAMED=[ ("LinkDrivenParameter", "DriverSocket",   "Driver",     "FloatSocket",        "Value",              False),
+                  ("DeformerHook",        "IntSocket",      "Index",      "UnsignedIntSocket",  "Curve Point Index",  False)]
 
 
                 # NODE CLASS NAME             IN_OUT    SOCKET TYPE     SOCKET NAME     INDEX   MULTI     DEFAULT
                 # NODE CLASS NAME             IN_OUT    SOCKET TYPE     SOCKET NAME     INDEX   MULTI     DEFAULT
 SOCKETS_ADDED=[("DeformerMorphTargetDeform", 'INPUT', 'BooleanSocket', "Use Shape Key", 1,      False,    False),
 SOCKETS_ADDED=[("DeformerMorphTargetDeform", 'INPUT', 'BooleanSocket', "Use Shape Key", 1,      False,    False),
                ("DeformerMorphTargetDeform", 'INPUT', 'BooleanSocket', "Use Offset",    2,      False,    True),
                ("DeformerMorphTargetDeform", 'INPUT', 'BooleanSocket', "Use Offset",    2,      False,    True),
                ("UtilityFCurve",             'INPUT',  "eFCrvExtrapolationMode", "Extrapolation Mode", 0, False, 'CONSTANT'),
                ("UtilityFCurve",             'INPUT',  "eFCrvExtrapolationMode", "Extrapolation Mode", 0, False, 'CONSTANT'),
-               ("LinkCopyScale",             'INPUT',  "BooleanSocket", "Additive",     3,      False, False)]
+               ("LinkCopyScale",             'INPUT',  "BooleanSocket", "Additive",     3,      False,    False),
+               ("DeformerHook",              'INPUT',  "FloatFactorSocket", "Influence", 3,     False,    1.0),
+               ("DeformerHook",              'INPUT',  "BooleanSocket", "Auto-Bezier",   4,     False,    True),]
 
 
 # replace names with bl_idnames for reading the tree and solving schemas.
 # replace names with bl_idnames for reading the tree and solving schemas.
 replace_types = ["NodeGroupInput", "NodeGroupOutput", "SchemaIncomingConnection",
 replace_types = ["NodeGroupInput", "NodeGroupOutput", "SchemaIncomingConnection",

+ 28 - 20
deformer_containers.py

@@ -6,6 +6,8 @@ from .utilities import (prRed, prGreen, prPurple, prWhite, prOrange,
                         wrapRed, wrapGreen, wrapPurple, wrapWhite,
                         wrapRed, wrapGreen, wrapPurple, wrapWhite,
                         wrapOrange,)
                         wrapOrange,)
 
 
+from .deformer_socket_templates import *
+
 from bpy.types import NodeTree
 from bpy.types import NodeTree
 
 
 def TellClasses():
 def TellClasses():
@@ -224,25 +226,12 @@ class DeformerHook(MantisDeformerNode):
     '''A node representing a hook deformer'''
     '''A node representing a hook deformer'''
 
 
     def __init__(self, signature, base_tree):
     def __init__(self, signature, base_tree):
-        super().__init__(signature, base_tree)
-        inputs = [
-            "Hook Target",
-            "Index",
-            "Deformer",
-        ]
-        outputs = [
-          "Deformer"
-        ]
+        super().__init__(signature, base_tree, HookSockets)
         # now set up the traverse target...
         # now set up the traverse target...
-        self.outputs.init_sockets(outputs)
-        self.inputs.init_sockets(inputs)
         self.init_parameters(additional_parameters={"Name":None})
         self.init_parameters(additional_parameters={"Name":None})
         self.set_traverse([("Deformer", "Deformer")])
         self.set_traverse([("Deformer", "Deformer")])
-        self.node_type = "LINK"
         self.prepared = True
         self.prepared = True
 
 
-
-
     def GetxForm(self, socket="Deformer"):
     def GetxForm(self, socket="Deformer"):
         if socket == "Deformer":
         if socket == "Deformer":
             return GetxForm(self)
             return GetxForm(self)
@@ -258,17 +247,30 @@ class DeformerHook(MantisDeformerNode):
         mod_name = self.evaluate_input("Name")
         mod_name = self.evaluate_input("Name")
         target_node = self.evaluate_input('Hook Target')
         target_node = self.evaluate_input('Hook Target')
         target = target_node.bGetObject(); subtarget = ""
         target = target_node.bGetObject(); subtarget = ""
+        props_sockets = self.gen_property_socket_map()
         if isinstance(target, Bone) or isinstance(target, PoseBone):
         if isinstance(target, Bone) or isinstance(target, PoseBone):
             subtarget = target.name; target = target.id_data
             subtarget = target.name; target = target.id_data
         ob=self.GetxForm().bGetObject()
         ob=self.GetxForm().bGetObject()
         reuse = False
         reuse = False
         for m in ob.modifiers:
         for m in ob.modifiers:
             if  m.type == 'HOOK' and m.object == target and m.subtarget == subtarget:
             if  m.type == 'HOOK' and m.object == target and m.subtarget == subtarget:
-                d = m; reuse = True; break
+                if self.evaluate_input("Influence") != m.strength:
+                    continue # make a new modifier so they can have different strengths
+                if ob.animation_data: # this can be None
+                    drivers = ob.animation_data.drivers
+                    for k in props_sockets.keys():
+                        if driver := drivers.find(k):
+                            # TODO: I should check to see if the drivers are the same...
+                            break # continue searching for an equivalent modifier
+                    else: # There was no driver - use this one.
+                        d = m; reuse = True; break
+                else: # use this one, there can't be drivers without animation_data.
+                    d = m; reuse = True; break
         else:
         else:
             d = ob.modifiers.new(mod_name, type='HOOK')
             d = ob.modifiers.new(mod_name, type='HOOK')
             if d is None:
             if d is None:
                 raise RuntimeError(f"Modifier was not created in node {self} -- the object is invalid.")
                 raise RuntimeError(f"Modifier was not created in node {self} -- the object is invalid.")
+        self.bObject = d
         self.get_target_and_subtarget(d, input_name="Hook Target")
         self.get_target_and_subtarget(d, input_name="Hook Target")
         vertices_used=[]
         vertices_used=[]
         if reuse: # Get the verts in the list... filter out all the unneeded 0's
         if reuse: # Get the verts in the list... filter out all the unneeded 0's
@@ -277,12 +279,18 @@ class DeformerHook(MantisDeformerNode):
             vertices_used = list(filter(lambda a : a != 0, vertices_used))
             vertices_used = list(filter(lambda a : a != 0, vertices_used))
             if include_0: vertices_used.append(0)
             if include_0: vertices_used.append(0)
         # now we add the selected vertex to the list, too
         # now we add the selected vertex to the list, too
-        vertices_used.append(self.evaluate_input("Index"))
+        vertex = self.evaluate_input("Curve Point Index")
+        if ob.type == 'CURVE' and ob.data.splines[0].type == 'BEZIER' and \
+                      self.evaluate_input("Auto-Bezier"):
+            vertex*=3
+            vertices_used.extend([vertex, vertex+1, vertex+2])
+        else:
+            vertices_used.append(vertex)
         d.vertex_indices_set(vertices_used)
         d.vertex_indices_set(vertices_used)
-        # todo: this should be able to take many indices in the future.
-        # since this only takes a single index, I can always hack together a shape-key solution to tilt...
-        # GN solution would work too, provided a separate object is made for it
-        # I like this, it is perhaps a little innefficient but can be improved later on
+        evaluate_sockets(self, d, props_sockets)
+        finish_drivers(self)
+        # todo: this node should be able to take many indices in the future.
+        # Also: I have a Geometry Nodes implementation of this I can use... maybe...
 
 
 
 
 class DeformerMorphTarget(MantisDeformerNode):
 class DeformerMorphTarget(MantisDeformerNode):

+ 2 - 8
deformer_definitions.py

@@ -1,6 +1,7 @@
 import bpy
 import bpy
 from bpy.types import NodeTree, Node, NodeSocket
 from bpy.types import NodeTree, Node, NodeSocket
 from .base_definitions import MantisUINode, DeformerNode, get_signature_from_edited_tree
 from .base_definitions import MantisUINode, DeformerNode, get_signature_from_edited_tree
+from.deformer_socket_templates import *
 
 
 from .utilities import (prRed, prGreen, prPurple, prWhite, prOrange,
 from .utilities import (prRed, prGreen, prPurple, prWhite, prOrange,
                         wrapRed, wrapGreen, wrapPurple, wrapWhite,
                         wrapRed, wrapGreen, wrapPurple, wrapWhite,
@@ -70,15 +71,8 @@ class DeformerHook(Node, DeformerNode):
     mantis_node_class_name=bl_idname
     mantis_node_class_name=bl_idname
 
 
     def init(self, context):
     def init(self, context):
-        self.inputs.new("DeformerSocket", "Deformer")
-        self.inputs.new('xFormSocket', "Hook Target")
-        self.inputs.new('IntSocket', "Index")
-        # self.inputs.new('StringSocket', "Blend Vertex Group")
-        # self.inputs.new('BooleanSocket', "Invert Vertex Group")
-        self.outputs.new('DeformerSocket', "Deformer")
+        self.init_sockets(HookSockets)
         self.initialized = True
         self.initialized = True
-    
-    # def display_update(self, parsed_tree, context):
 
 
 
 
 from .utilities import get_socket_maps, relink_socket_map
 from .utilities import get_socket_maps, relink_socket_map

+ 23 - 0
deformer_socket_templates.py

@@ -0,0 +1,23 @@
+from .base_definitions import MantisSocketTemplate as SockTemplate
+from dataclasses import replace
+
+
+
+
+Target = SockTemplate(name="Target", bl_idname='xFormSocket',
+        is_input=True,
+    )
+
+HookSockets= [
+    DeformerInput := SockTemplate(name="Deformer", bl_idname='DeformerSocket',
+        is_input=True,),
+    HookTarget := replace(Target, name="Hook Target"),
+    CurvePointIndex := SockTemplate(name="Curve Point Index", bl_idname='UnsignedIntSocket',
+        is_input=True, default_value=0, ),
+    Influence := SockTemplate(name="Influence", bl_idname='FloatFactorSocket',
+        is_input=True, default_value=1.0, blender_property='strength'),
+    HookAutoBezier := SockTemplate(name="Auto-Bezier", bl_idname='BooleanSocket',
+        is_input=True, default_value=True, ),
+    DeformerOutput := SockTemplate(name="Deformer", bl_idname='DeformerSocket',
+        is_input=False,), 
+]