Prechádzať zdrojové kódy

Add Node: Curve Pin

Joseph Brandenburg 6 mesiacov pred
rodič
commit
d024ccfaf3
5 zmenil súbory, kde vykonal 154 pridanie a 10 odobranie
  1. 1 0
      __init__.py
  2. 2 8
      node_container_common.py
  3. 31 0
      socket_definitions.py
  4. 104 2
      xForm_containers.py
  5. 16 0
      xForm_definitions.py

+ 1 - 0
__init__.py

@@ -98,6 +98,7 @@ xForm_category = [
         NodeItem("xFormBoneNode"),
         NodeItem("xFormArmatureNode"),
         NodeItem("xFormObjectInstance"),
+        NodeItem("xFormCurvePin"),
     ]
 driver_category = [
         NodeItem("LinkDrivenParameter"),

+ 2 - 8
node_container_common.py

@@ -133,10 +133,6 @@ def evaluate_sockets(nc, c, props_sockets):
                 setattr(ob, att_name, val)
             except Exception as e:
                 prRed(ob, att_name, val); raise e
-    # HACK I think I should do this in __init__
-    if not hasattr(nc, "drivers"):
-        nc.drivers = {}
-    # end HACK
     for prop, (sock, default) in props_sockets.items():
         # c = nc.bObject
         # annoyingly, sometimes the socket is an array
@@ -250,12 +246,12 @@ def finish_driver(nc, driver_item, prop):
             else:
                 bone_col = nc.bGetParentArmature().pose.bones
             driver["owner"] = bone_col[nc.bObject] # we use "unsafe" brackets instead of get() because we want to see any errors that occur
+        elif nc.node_type in ['XFORM',] and nc.__class__.__name__ in ['xFormCurvePin']:
+            driver["owner"] = nc.bObject.constraints[0]
         else:
             driver["owner"] = nc.bObject
-        # prPurple("Successfully created driver for %s" % prop)
         driver["prop"] = prop
         return driver
-        # prWhite(driver)
     else:
         prOrange("Provider", driver_provider)
         prGreen("socket", driver_socket)
@@ -265,10 +261,8 @@ def finish_driver(nc, driver_item, prop):
         return None
 
 def finish_drivers(nc):
-    # gonna make this into a common function...
     drivers = []
     if not hasattr(nc, "drivers"):
-        # prGreen(f"No Drivers to construct for {nc}")
         return # HACK
     for driver_item, prop in nc.drivers.items():
         if isinstance(prop, list):

+ 31 - 0
socket_definitions.py

@@ -154,6 +154,7 @@ def TellClasses() -> List[MantisSocket]:
              EnumRotationStretchTo,
              EnumTrackAxis,
              EnumUpAxis,
+             EnumFollowPathForwardAxis,
              EnumLockAxis,
              EnumLimitMode,
              EnumYScaleMode,
@@ -1390,6 +1391,36 @@ class EnumUpAxis(MantisSocket):
     def draw_color_simple(self):
         return self.color_simple
 
+# Follow Track Forward axis
+eForwardAxis = (('FORWARD_X',        "X",  "X",  0),
+                ('FORWARD_Y',        "Y",  "Y",  1),
+                ('FORWARD_Z',        "Z",  "Z",  2),
+                ('TRACK_NEGATIVE_X', "-X", "-X", 3),
+                ('TRACK_NEGATIVE_Y', "-Y", "-Y", 4),
+                ('TRACK_NEGATIVE_Z', "-Z", "-Z", 5),)
+
+class EnumFollowPathForwardAxis(MantisSocket):
+    '''Custom node socket type'''
+    bl_idname = 'EnumFollowPathForwardAxis'
+    bl_label = "Forward Axis"
+    
+    default_value: bpy.props.EnumProperty(
+        items=eForwardAxis,
+        name="Forward Axis",
+        description="Forward Axis",
+        default = 'FORWARD_X',
+        update = update_socket,)
+    color_simple = cString
+    color : bpy.props.FloatVectorProperty(default=cString, size=4)
+    input : bpy.props.BoolProperty(default =False,)
+    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
+
 # Locked Track
 
 eLockAxis = (('LOCK_X', "X", "X", 1),

+ 104 - 2
xForm_containers.py

@@ -9,6 +9,7 @@ def TellClasses():
              xFormBone,
              xFormGeometryObject,
              xFormObjectInstance,
+             xFormCurvePin,
            ]
 
 #*#-------------------------------#++#-------------------------------#*#
@@ -840,7 +841,108 @@ class xFormObjectInstance(MantisNode):
             print (wrapGreen(i), wrapWhite(self), wrapPurple(driver_key))
             prOrange(driver_item)
         finish_drivers(self)
-            
-        
+
     def bGetObject(self, mode = 'POSE'):
         return self.bObject
+
+from .base_definitions import MantisSocketTemplate as SockTemplate
+xFormCurvePinSockets = [
+    NameTemplate := SockTemplate(
+        name="Name", is_input=True,  bl_idname='StringSocket',
+        default_value='Curve Pin', blender_property='name' ),
+    ParentCurveTemplate := SockTemplate(
+        name="Parent Curve", is_input=True,  bl_idname='xFormSocket', ),
+    FactorTemplate := SockTemplate(
+        name="Curve Pin Factor", is_input=True,  bl_idname='FloatFactorSocket',
+        default_value=0.0, blender_property='offset_factor' ),
+    ForwardAxisTemplate := SockTemplate(
+        name="Forward Axis", is_input=True,  bl_idname='EnumFollowPathForwardAxis',
+        default_value="FORWARD_Y", blender_property='forward_axis' ),
+    UpAxisTemplate := SockTemplate(
+        name="Up Axis", is_input=True,  bl_idname='EnumUpAxis',
+        default_value="UP_Z", blender_property='up_axis' ),
+    xFormOutTemplate := SockTemplate(
+        name="xForm Out", is_input=False,  bl_idname='xFormSocket', ),
+]
+
+class xFormCurvePin(MantisNode):
+    """An xForm pinned to a specific location on a curve."""
+    def __init__(self, signature, base_tree):
+        super().__init__(signature, base_tree,xFormCurvePinSockets)
+        self.init_parameters(additional_parameters={"Matrix":None})
+        self.node_type = "XFORM"
+        self.bObject = None
+
+    def prep_driver_values(self, constraint):
+        from .misc_nodes import UtilityDriver, UtilitySwitch
+        for socket_name in ["Curve Pin Factor", "Forward Axis","Up Axis",]:
+            if self.inputs[socket_name].is_linked:
+                link = self.inputs[socket_name].links[0]
+                driver = link.from_node
+                if isinstance(driver, UtilityDriver):
+                    prop_amount = driver.evaluate_input("Property")
+                elif isinstance(driver, UtilitySwitch):
+                    xf=driver.GetxForm()
+                    prop_amount = xf.evaluate_input(driver.evaluate_input('Parameter'))
+                for template in self.socket_templates:
+                    if template.name == socket_name: break
+                setattr(constraint, template.blender_property, prop_amount )
+
+    def bPrepare(self, bContext = None,):
+        from bpy import data
+        
+        if not bContext: # lol
+            import bpy
+            bContext = bpy.context
+        ob = data.objects.get(self.evaluate_input("Name"))
+        if not ob:
+            ob = data.objects.new(self.evaluate_input("Name"), None)
+        self.bObject = ob
+        
+        reset_object_data(ob)
+        # Link to Scene:
+        if (ob.name not in bContext.view_layer.active_layer_collection.collection.objects):
+            bContext.view_layer.active_layer_collection.collection.objects.link(ob)
+        
+        node_line = trace_single_line(self, "Parent Curve")[0][1:] # slice excludes self
+        for other_node in node_line:
+            if other_node.node_type == 'XFORM':
+                break
+        else:
+            raise GraphError(f"ERROR: {self} is not connected to a parent curve")
+        if isinstance(other_node, (xFormArmature, xFormBone, xFormObjectInstance,)):
+            raise GraphError(f"ERROR: {self} must be connected to curve,"
+                              " not {other_node.__class__.__name__}")
+        curve=other_node.bGetObject()
+        if curve.type != 'CURVE':
+            raise GraphError(f"ERROR: {self} must be connected to curve,"
+                              " not {curve.type}")
+        c = ob.constraints.new("FOLLOW_PATH")
+        c.target = curve
+        c.use_fixed_location = True
+        c.use_curve_radius = True
+        c.use_curve_follow = True
+        c.name = "Curve Pin"
+
+        props_sockets = self.gen_property_socket_map()
+        del props_sockets['name']
+        evaluate_sockets(self, c, props_sockets)
+        # this isn't usually run on xForm nodes so for now I need to set the
+        #   driver's default values manually if I want a matrix now.
+        # because the drivers may not have initialized yet.
+        self.prep_driver_values(c)
+        # now if all goes well... the matrix will be correct.
+        dg = bContext.view_layer.depsgraph
+        dg.update()
+        # and the matrix should be correct now.
+        self.parameters['Matrix'] = ob.matrix_world
+        self.prepared = True
+    
+    def bExecute(self, bContext=None):
+        print( wrapGreen("Created Curve Pin: ") + wrapOrange(self.bObject.name) )
+
+    def bFinalize(self, bContext = None):
+        finish_drivers(self)
+            
+    def bGetObject(self, mode = 'POSE'):
+        return self.bObject

+ 16 - 0
xForm_definitions.py

@@ -15,6 +15,7 @@ def TellClasses():
         xFormArmatureNode,
         xFormGeometryObjectNode,
         xFormObjectInstance,
+        xFormCurvePin,
         ]
 
 def default_traverse(self, socket):
@@ -366,5 +367,20 @@ class xFormObjectInstance(Node, xFormNode):
             if nc:
                 self.inputs['Name'].display_text = nc.evaluate_input("Name")
 
+from .xForm_containers import xFormCurvePinSockets
+class xFormCurvePin(Node, xFormNode):
+    """"A node representing a curve pin"""
+    bl_idname = "xFormCurvePin"
+    bl_label = "Curve Pin"
+    bl_icon = "FORCE_CURVE"
+    initialized : bpy.props.BoolProperty(default = False)
+    mantis_node_class_name=bl_idname
+    
+    def init(self, context):
+        self.init_sockets(xFormCurvePinSockets)
+        self.use_custom_color = True
+        self.color = xFormColor
+        self.initialized = True
+
 for cls in TellClasses():
     cls.set_mantis_class()