ソースを参照

Add: Extrapolation Mode to fCurves

Joseph Brandenburg 8 ヶ月 前
コミット
b5a7676b3e
7 ファイル変更67 行追加40 行削除
  1. 2 2
      __init__.py
  2. 2 1
      base_definitions.py
  3. 1 1
      blender_manifest.toml
  4. 2 0
      drivers.py
  5. 33 35
      misc_containers.py
  6. 2 1
      nodes_generic.py
  7. 25 0
      socket_definitions.py

+ 2 - 2
__init__.py

@@ -17,7 +17,7 @@ from .utilities import prRed
 
 MANTIS_VERSION_MAJOR=0
 MANTIS_VERSION_MINOR=9
-MANTIS_VERSION_SUB=10
+MANTIS_VERSION_SUB=11
 
 
 classLists = [module.TellClasses() for module in [
@@ -275,7 +275,7 @@ def do_version_update(node_tree):
                 print(f"INFO: adding socket \"{socket_name}\" of type {socket_type} to node {n.name} of type {n.bl_idname}.")
                 s = n.inputs.new(socket_type, socket_name, use_multi_input=use_multi_input)
                 s.default_value = default_val
-                n.inputs.move(len(n.inputs), index)
+                n.inputs.move(len(n.inputs)-1, index)
         socket_map = None
         if rename_jobs:
             from .utilities import get_socket_maps

+ 2 - 1
base_definitions.py

@@ -362,7 +362,8 @@ SOCKETS_RENAMED=[ ("LinkDrivenParameter", "DriverSocket",   "Driver",     "Float
 
                 # 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),]
+               ("DeformerMorphTargetDeform", 'INPUT', 'BooleanSocket', "Use Offset", 2,         False,     True),
+               ("UtilityFCurve",             'INPUT',  "eFCrvExtrapolationMode", "Extrapolation Mode", 0, False, 'CONSTANT')]
 
 # replace names with bl_idnames for reading the tree and solving schemas.
 replace_types = ["NodeGroupInput", "NodeGroupOutput", "SchemaIncomingConnection",

+ 1 - 1
blender_manifest.toml

@@ -3,7 +3,7 @@ schema_version = "1.0.0"
 # Example of manifest file for a Blender extension
 # Change the values according to your extension
 id = "mantis"
-version = "0.9.10"
+version = "0.9.11"
 name = "Mantis"
 tagline = "Mantis is a rigging nodes toolkit"
 maintainer = "Nodespaghetti <josephbburg@protonmail.com>"

+ 2 - 0
drivers.py

@@ -69,6 +69,8 @@ def CreateDrivers(drivers):
         if (expr := driver.get("expression")) and isinstance(expr, str):
             drv.expression = expr
         
+        fc.extrapolation = driver["extrapolation"]
+
         # logic for handling type can go here
         
         # start by clearing

+ 33 - 35
misc_containers.py

@@ -741,12 +741,14 @@ class UtilityFCurve:
         self.base_tree=base_tree
         self.executed = False
         self.signature = signature
-        self.inputs = {}
+        self.inputs = {
+            "Extrapolation Mode" : NodeSocket(is_input = True, name = "Extrapolation Mode", node = self),
+        }
         self.outputs = {
           "fCurve" : NodeSocket(name = "fCurve", node=self),
         }
         self.parameters = {
-          "Use_KeyFrame_Nodes": True, # HACK
+          "Extrapolation Mode":None,
           "fCurve":None, 
         }
         self.node_type = "UTILITY"
@@ -765,31 +767,22 @@ class UtilityFCurve:
         prepare_parameters(self)
         from .utilities import get_node_prototype
         np = get_node_prototype(self.signature, self.base_tree)
-
-
-        keys = []
+        extrap_mode = self.evaluate_input("Extrapolation Mode")
+        keys = [] # ugly but whatever
         #['amplitude', 'back', 'bl_rna', 'co', 'co_ui', 'easing', 'handle_left', 'handle_left_type', 'handle_right', 'handle_right_type',
         # 'interpolation', 'period', 'rna_type', 'select_control_point', 'select_left_handle', 'select_right_handle', 'type']
-
-        if True: #np.use_kf_nodes:
-            for k in self.inputs.keys():
-                # print (self.inputs[k])
-                if (key := self.evaluate_input(k)) is None:
-                    prOrange(f"WARN: No enough keyframes connected to {self}:{k}. Skipping Link.")
-                else:
-                    keys.append(key)
-
-        else:
-            raise NotImplementedError("Getting the keys from an fCurve isn't really working anymore lol need to fix")
-            fc_ob = np.fake_fcurve_ob
-            fc = fc_ob.animation_data.action.fcurves[0]
-            for k in fc.keyframe_points:
-                key = {}
-                for prop in dir(k):
-                    if ("__" in prop) or ("bl_" in prop): continue
-                    #it's __name__ or bl_rna or something
-                    key[prop] = getattr(k, prop)
+        for k in self.inputs.keys():
+            if k == 'Extrapolation Mode' : continue
+            # print (self.inputs[k])
+            if (key := self.evaluate_input(k)) is None:
+                prOrange(f"WARN: No enough keyframes connected to {self}:{k}. Skipping Link.")
+            else:
                 keys.append(key)
+        if len(keys) <1:
+            prOrange(f"WARN: no keys in fCurve {self}.")
+        keys.append(extrap_mode)
+        
+
         
         # Push parameter to downstream connected node.connected:
         # TODO: find out if this is necesary, even
@@ -799,8 +792,6 @@ class UtilityFCurve:
                 link.to_node.parameters[link.to_socket] = keys
         # the above was a HACK. I don't want nodes modiying their descenedents.
         # If the above was necessary, I want to get an error from it so I can fix it in the descendent's class
-        if len(keys) <1:
-            prOrange(f"WARN: no keys in fCurve {self}.")
         self.prepared = True
         self.executed = True
                 
@@ -841,27 +832,33 @@ class UtilityDriver:
         from .drivers import MantisDriver
         #prPurple("Executing Driver Node")
         my_vars = []
-        if (keys := self.evaluate_input("fCurve")) is None:
+        if len(keys := self.evaluate_input("fCurve")) <2:
             keys={}
             prWhite(f"INFO: no fCurve connected to {self}; using default fCurve.")
             from mathutils import Vector
             keys = [
                 {"co":Vector( (0, 0,)), "type":"GENERATED", "interpolation":"LINEAR" },
                 {"co":Vector( (1, 1,)), "type":"GENERATED", "interpolation":"LINEAR" },
+                "CONSTANT",
             ]
+        try:
+            extrap_mode = keys.pop() # this is a silly way of doing things but it maintains the interface
+        except IndexError:
+            extrap_mode = 'CONSTANT'
         for inp in list(self.inputs.keys() )[3:]:
             if (new_var := self.evaluate_input(inp)):
                 new_var["name"] = inp
                 my_vars.append(new_var)
             else:
                 raise RuntimeError(f"Failed to initialize Driver variable for {self}")
-        my_driver ={ "owner"      :  None,
-                     "prop"       :  None, # will be filled out in the node that uses the driver
-                     "expression" :  self.evaluate_input("Expression"),
-                     "ind"        :  -1, # same here
-                     "type"       :  self.evaluate_input("Driver Type"),
-                     "vars"       :  my_vars,
-                     "keys"       :  keys, }
+        my_driver ={ "owner"         :  None,
+                     "prop"          :  None, # will be filled out in the node that uses the driver
+                     "expression"    :  self.evaluate_input("Expression"),
+                     "ind"           :  -1, # same here
+                     "type"          :  self.evaluate_input("Driver Type"),
+                     "vars"          :  my_vars,
+                     "keys"          :  keys,
+                     "extrapolation" : extrap_mode }
         
         my_driver = MantisDriver(my_driver)
         
@@ -938,7 +935,8 @@ class UtilitySwitch:
                                    "type":"KEYFRAME",}, #display type
                                  { "co":(1,1),
                                    "interpolation": "LINEAR",
-                                   "type":"KEYFRAME",},], }
+                                   "type":"KEYFRAME",},],
+                      "extrapolation": 'CONSTANT', }
         my_driver   ["expression"] = "a"
         
         my_driver = MantisDriver(my_driver)

+ 2 - 1
nodes_generic.py

@@ -448,12 +448,13 @@ class UtilityFCurveNode(Node, MantisNode):
     initialized : bpy.props.BoolProperty(default = False)
     
     def init(self, context):
+        self.inputs.new("eFCrvExtrapolationMode", "Extrapolation Mode")
         self.outputs.new("FCurveSocket", "fCurve")
         self.initialized = True
             
     def draw_buttons(self, context, layout):
         layout.operator( 'mantis.fcurve_node_add_kf' )
-        if (len(self.inputs) > 0):
+        if (len(self.inputs) > 1):
             layout.operator( 'mantis.fcurve_node_remove_kf' )
 
 

+ 25 - 0
socket_definitions.py

@@ -163,6 +163,7 @@ def TellClasses():
              KeyframeSocket,
              EnumKeyframeInterpolationTypeSocket,
              EnumKeyframeBezierHandleTypeSocket,
+             eFCrvExtrapolationMode,
              
              # Math
              MathFloatOperation,
@@ -2114,8 +2115,32 @@ class EnumKeyframeBezierHandleTypeSocket(NodeSocket):
         return self.color_simple
 
 
+enumExtrapolationMode = (('CONSTANT', 'Constant', 'Constant'),
+                         ('LINEAR', "Linear", "Linear"),)
 
 
+class eFCrvExtrapolationMode(NodeSocket):
+    '''FCurve Extrapolation Mode'''
+    bl_idname = 'eFCrvExtrapolationMode'
+    bl_label = "Extrapolation Mode"
+    default_value :bpy.props.EnumProperty(
+        name="",
+        description="Handle Type",
+        items=enumExtrapolationMode,
+        default='CONSTANT',
+        update = update_socket,)
+    
+    color_simple = cString
+    color : bpy.props.FloatVectorProperty(default=cString, size=4)
+    input : bpy.props.BoolProperty(default =False, update = update_socket)
+    def draw(self, context, layout, node, text):
+        ChooseDraw(self, context, layout, node, text)
+    def draw_color(self, context, node):
+        return self.color
+    @classmethod
+    def draw_color_simple(self):
+        return self.color_simple
+
 
 enumFloatOperations = (('ADD', 'Add', 'Add'),
                       ('SUBTRACT', "Subtract", "Subtract"),