Selaa lähdekoodia

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 kuukautta sitten
vanhempi
commit
a1f215e3d3
5 muutettua tiedostoa jossa 60 lisäystä ja 32 poistoa
  1. 1 1
      __init__.py
  2. 6 3
      base_definitions.py
  3. 28 20
      deformer_containers.py
  4. 2 8
      deformer_definitions.py
  5. 23 0
      deformer_socket_templates.py

+ 1 - 1
__init__.py

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

+ 6 - 3
base_definitions.py

@@ -572,14 +572,17 @@ SOCKETS_REMOVED=[("UtilityDriverVariable", "Transform Channel"),
                  ("xFormRootNode","World Out"),
                  ("UtilitySwitch","xForm"),
                  ("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
 SOCKETS_ADDED=[("DeformerMorphTargetDeform", 'INPUT', 'BooleanSocket', "Use Shape Key", 1,      False,    False),
                ("DeformerMorphTargetDeform", 'INPUT', 'BooleanSocket', "Use Offset",    2,      False,    True),
                ("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_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,
                         wrapOrange,)
 
+from .deformer_socket_templates import *
+
 from bpy.types import NodeTree
 
 def TellClasses():
@@ -224,25 +226,12 @@ class DeformerHook(MantisDeformerNode):
     '''A node representing a hook deformer'''
 
     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...
-        self.outputs.init_sockets(outputs)
-        self.inputs.init_sockets(inputs)
         self.init_parameters(additional_parameters={"Name":None})
         self.set_traverse([("Deformer", "Deformer")])
-        self.node_type = "LINK"
         self.prepared = True
 
-
-
     def GetxForm(self, socket="Deformer"):
         if socket == "Deformer":
             return GetxForm(self)
@@ -258,17 +247,30 @@ class DeformerHook(MantisDeformerNode):
         mod_name = self.evaluate_input("Name")
         target_node = self.evaluate_input('Hook Target')
         target = target_node.bGetObject(); subtarget = ""
+        props_sockets = self.gen_property_socket_map()
         if isinstance(target, Bone) or isinstance(target, PoseBone):
             subtarget = target.name; target = target.id_data
         ob=self.GetxForm().bGetObject()
         reuse = False
         for m in ob.modifiers:
             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:
             d = ob.modifiers.new(mod_name, type='HOOK')
             if d is None:
                 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")
         vertices_used=[]
         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))
             if include_0: vertices_used.append(0)
         # 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)
-        # 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):

+ 2 - 8
deformer_definitions.py

@@ -1,6 +1,7 @@
 import bpy
 from bpy.types import NodeTree, Node, NodeSocket
 from .base_definitions import MantisUINode, DeformerNode, get_signature_from_edited_tree
+from.deformer_socket_templates import *
 
 from .utilities import (prRed, prGreen, prPurple, prWhite, prOrange,
                         wrapRed, wrapGreen, wrapPurple, wrapWhite,
@@ -70,15 +71,8 @@ class DeformerHook(Node, DeformerNode):
     mantis_node_class_name=bl_idname
 
     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
-    
-    # def display_update(self, parsed_tree, context):
 
 
 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,), 
+]