1
0

5 Revīzijas aa15119835 ... e40d0fdc9c

Autors SHA1 Ziņojums Datums
  Joseph Brandenburg e40d0fdc9c initial versioning for new interface classes 3 nedēļas atpakaļ
  Joseph Brandenburg 4845445001 fix: unbound local error when updating group interface 3 nedēļas atpakaļ
  Joseph Brandenburg 37976aaffb update interface draw for correct UI and clarity 3 nedēļas atpakaļ
  Joseph Brandenburg fa05172de2 Interface Classes set the multi and default value now 3 nedēļas atpakaļ
  Joseph Brandenburg dbb1998052 Add Custom Interface Socket Types 3 nedēļas atpakaļ
5 mainītis faili ar 187 papildinājumiem un 16 dzēšanām
  1. 5 2
      __init__.py
  2. 1 1
      blender_manifest.toml
  3. 126 0
      socket_definitions.py
  4. 22 12
      utilities.py
  5. 33 1
      versioning.py

+ 5 - 2
__init__.py

@@ -18,7 +18,7 @@ from .utilities import prRed
 
 MANTIS_VERSION_MAJOR=0
 MANTIS_VERSION_MINOR=12
-MANTIS_VERSION_SUB=26
+MANTIS_VERSION_SUB=27
 
 classLists = [module.TellClasses() for module in [
  link_nodes_ui,
@@ -312,7 +312,6 @@ def version_update_handler(filename):
     for node_tree in bpy.data.node_groups: # ensure it can update again after file load.
         if node_tree.bl_idname in ["MantisTree", "SchemaTree"]:
                 node_tree.is_exporting=False; node_tree.is_executing=False
-
     for node_tree in bpy.data.node_groups:
         if node_tree.bl_idname in ["MantisTree", "SchemaTree"]:
             if (node_tree.mantis_version[0] < MANTIS_VERSION_MAJOR) or \
@@ -386,6 +385,10 @@ def on_undo_post_handler(scene): # the undo will trigger a depsgraph update
 from .menu_classes import (node_context_menu_draw, node_add_menu_draw,
                            armature_add_menu_draw, import_menu_draw)
 
+from .socket_definitions import generate_custom_interface_types
+generated_classes = generate_custom_interface_types()
+classes.extend(generated_classes)
+
 def register():
     from bpy.utils import register_class
 

+ 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.12.26"
+version = "0.12.27"
 name = "Mantis"
 tagline = "Mantis is a rigging nodes toolkit"
 maintainer = "Nodespaghetti <josephbburg@protonmail.com>"

+ 126 - 0
socket_definitions.py

@@ -292,6 +292,132 @@ def tell_valid_bl_idnames():
     valid_classes = filter(lambda cls : cls.is_valid_interface_type, [cls for cls in TellClasses()])
     return (cls.bl_idname for cls in valid_classes)
 
+
+enum_default_xForm_values =(
+        ('NONE', "None", "None - fail if unconnected (RECOMMENDED)."),
+        ('ARMATURE', "Generated Armature", "Generate an armature automatically. "
+                     "PLEASE use this only for development and testing. "
+                     "If you use this as a feature in your rigs you will be sorry."),)
+    
+# Custom Interface Types give the user the ability to set properties for the interface
+# we'll define a base class, and generate the individual classes from the base class
+# but we'll leave the option to define a few of them directly.
+from bpy.types import NodeTreeInterfaceSocket
+
+def interface_socket_update(self, context):
+    # we're just gonna do this the dumb way for now and invalidate the tree
+    # BUG HACK TODO actually I am gonna do this stuff later
+    # later, I can do this based on the connections in the tree
+    # and the socket updater can use the same code for group interface modifications
+    # TODO do this stuff because the tree will be a lot snappier
+    pass
+
+
+interface_default_value_description="The default value of the socket when it is not connected."
+class MantisInterfaceSocketBaseClass():
+    is_array : bpy.props.BoolProperty(default =False, update=interface_socket_update,
+            description="Whether the socket is an array, otherwise it is constant." ) 
+    is_connection : bpy.props.BoolProperty(default =False, update=interface_socket_update,
+            description="If the socket is a connection or not. Ensure this is always paired"
+                        " with an input and an output." ) 
+    connected_to : bpy.props.StringProperty(default="", update=interface_socket_update,
+            description="The name of the socket this one is connected to." ) 
+    # we are just gonna use ONE base class (it's easier)
+    # so generate ALL properties and show only what is needed.
+    default_string : bpy.props.StringProperty(default="", update=interface_socket_update,
+            description=interface_default_value_description, ) 
+    default_float : bpy.props.FloatProperty(default=0.0, update=interface_socket_update,
+            description=interface_default_value_description, ) 
+    default_vector : bpy.props.FloatVectorProperty( size = 3, default = (0.0, 0.0, 0.0, ),
+            description=interface_default_value_description, update=interface_socket_update,) 
+    default_int : bpy.props.IntProperty(default=0, update=interface_socket_update,
+            description=interface_default_value_description, ) 
+    default_bool : bpy.props.BoolProperty(default=False, update=interface_socket_update,
+            description=interface_default_value_description, ) 
+    default_bool_vector : bpy.props.BoolVectorProperty(subtype = "XYZ", update=interface_socket_update,
+            description=interface_default_value_description, ) 
+    default_xForm : bpy.props.EnumProperty( default = 'NONE', update = interface_socket_update,
+        items=enum_default_xForm_values, description=interface_default_value_description,)
+
+def interface_draw(self, context, layout):
+    if not self.is_connection:
+        layout.prop(self, "is_array", text="Is Array", toggle=True,)
+    if not self.is_array and self.id_data.bl_idname == 'SchemaTree':
+        layout.prop(self, "is_connection", text="Is Connection", toggle=True,)
+        if self.is_connection: # only show this if in a Schema AND set to is_connection
+            layout.prop(self, "connected_to", text="Connected To", toggle=True,)
+
+# Different classes to handle different data types. In the future, these should also
+#  have settable min/max and such where appropriate
+def interface_string_draw(self, context, layout):
+    layout.prop(self, "default_string", text="Default Value", toggle=True,)
+    interface_draw(self, context, layout)
+
+def interface_float_draw(self, context, layout):
+    layout.prop(self, "default_float", text="Default Value", toggle=True,)
+    interface_draw(self, context, layout)
+
+def interface_vector_draw(self, context, layout):
+    layout.prop(self, "default_vector", text="Default Value", toggle=True,)
+    interface_draw(self, context, layout)
+
+def interface_int_draw(self, context, layout):
+    layout.prop(self, "default_int", text="Default Value", toggle=True,)
+    interface_draw(self, context, layout)
+
+def interface_bool_draw(self, context, layout):
+    layout.prop(self, "default_bool", text="Default Value", toggle=True,)
+    interface_draw(self, context, layout)
+
+def interface_bool_vector_draw(self, context, layout):
+    layout.prop(self, "default_bool_vector", text="Default Value", toggle=True,)
+    interface_draw(self, context, layout)
+
+def interface_xform_draw(self, context, layout):
+    if self.in_out == 'INPUT':
+        layout.prop(self, "default_xForm", text="Default Value", toggle=True,)
+    interface_draw(self, context, layout)
+
+
+def generate_custom_interface_types():
+    generated_classes = []
+    # copied from above
+    valid_classes = filter(lambda cls : cls.is_valid_interface_type, [cls for cls in TellClasses()])
+    for cls in valid_classes:
+        name = cls.__name__ + "Interface"
+            
+        my_interface_draw = interface_draw
+        # set the right draw function by the value's type
+        match map_color_to_socket_type(cls.color_simple): #there has to be a better way to do this
+            case "BooleanSocket":
+                my_interface_draw = interface_bool_draw
+            case "IntSocket":
+                my_interface_draw = interface_int_draw
+            case "FloatSocket":
+                my_interface_draw = interface_float_draw
+            case "BooleanThreeTupleSocket":
+                my_interface_draw = interface_bool_vector_draw
+            case "VectorSocket":
+                my_interface_draw = interface_vector_draw
+            case "StringSocket":
+                my_interface_draw = interface_string_draw
+            case "xFormSocket":
+                my_interface_draw = interface_xform_draw
+
+        interface = type(
+                      name,
+                      (MantisInterfaceSocketBaseClass, NodeTreeInterfaceSocket,),
+                      {
+                          "draw"             : my_interface_draw,
+                          "bl_idname"        : name,
+                          "bl_socket_idname" : cls.bl_idname,
+                          "socket_type"      : cls.bl_idname,
+                      },
+                  )
+        generated_classes.append(interface)
+    return generated_classes
+
+
 # Was setting color like this:
 # color : bpy.props.FloatVectorProperty(size = 4, default = cFCurve,)
 # but this didn't work when Blender automatically generated interface classes?

+ 22 - 12
utilities.py

@@ -271,21 +271,31 @@ def update_interface(interface, name, in_out, sock_type, parent_name):
 def relink_socket_map_add_socket(node, socket_collection, item, in_out=None,):
     from bpy.app import version as bpy_version
     if not in_out: in_out=item.in_out
+    multi=False
+    if hasattr(item, 'is_array'):
+        multi = item.is_array
     if node.bl_idname in ['MantisSchemaGroup'] and item.parent and item.parent.name == 'Array':
         multi = True if in_out == 'INPUT' else False
-        # have to work around a bug in 4.5.0 that prevents me from declaring custom socket types
-        # I have arbitrarily chosen to use the NodeSocketGeometry type to signal that this one is affected.
-        if bpy_version == (4, 5, 0) and item.bl_socket_idname == 'NodeSocketGeometry':
-            from .versioning import socket_add_workaround_for_4_5_0_LTS
-            s = socket_add_workaround_for_4_5_0_LTS(item, socket_collection, multi)
-        else:
-            s = socket_collection.new(type=item.bl_socket_idname, name=item.name, identifier=item.identifier,  use_multi_input=multi)
+    # have to work around a bug in 4.5.0 that prevents me from declaring custom socket types
+    # I have arbitrarily chosen to use the NodeSocketGeometry type to signal that this one is affected.
+    if bpy_version == (4, 5, 0) and item.bl_socket_idname == 'NodeSocketGeometry':
+        from .versioning import socket_add_workaround_for_4_5_0_LTS
+        s = socket_add_workaround_for_4_5_0_LTS(item, socket_collection, multi)
     else:
-        if bpy_version == (4, 5, 0) and item.bl_socket_idname == 'NodeSocketGeometry':
-            from .versioning import socket_add_workaround_for_4_5_0_LTS
-            s = socket_add_workaround_for_4_5_0_LTS(item, socket_collection, multi=False,)
-        else:
-            s = socket_collection.new(type=item.bl_socket_idname, name=item.name, identifier=item.identifier)
+        s = socket_collection.new(type=item.bl_socket_idname, name=item.name, identifier=item.identifier,  use_multi_input=multi)
+    if hasattr(s, 'default_value') and hasattr(s, 'valid_interface_type') and \
+          s.valid_interface_type == True:
+        from bpy.types import bpy_prop_array
+        from mathutils import Vector
+        default_value = 'REPORT BUG ON GITLAB' # default to bug string
+        val_type = type(s.default_value) # why tf can't I match/case here?
+        if val_type is bool: default_value = item.default_bool
+        if val_type is int: default_value = item.default_int
+        if val_type is float: default_value = item.default_float
+        if val_type is Vector: default_value = item.default_float
+        if val_type is str: default_value = item.default_string
+        if val_type is bpy_prop_array: default_value = item.default_float
+        s.default_value = default_value
     if item.parent.name == 'Array': s.display_shape = 'SQUARE_DOT'
     elif item.parent.name == 'Constant': s.display_shape='CIRCLE_DOT'
     return s

+ 33 - 1
versioning.py

@@ -220,7 +220,6 @@ def cleanup_4_5_0_LTS_interface_workaround(*args, **kwargs):
             interface_item.description = ''
     # that should be enough!
 
-
 def up_0_12_25_replace_floor_offset_type(*args, **kwargs):
     # add an inherit color input.
     node = kwargs['node']
@@ -244,6 +243,38 @@ def up_0_12_25_replace_floor_offset_type(*args, **kwargs):
         print(e)
 
 
+def schema_enable_custom_interface_types(*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 > 12: return# minor version must be 12 or less
+    if current_minor_version == 12 and current_sub_version > 27: return 
+    # we need to set the new interface values on the schema interface stuff
+    prGreen(f"Updating Schema tree {tree.name} to support new, improved UI!")
+    try:
+        for item in tree.interface.items_tree:
+            if item.item_type == 'PANEL':
+                continue
+            elif hasattr (item, 'is_array'):
+                if item.parent and item.parent.name == 'Array':
+                    item.is_array = True
+                # if is_array exists we're in the custom interface class
+                # so we'll assume the other attributes exist
+                if item.parent and item.parent.name == 'Connection':
+                    item.is_connection=True
+                    item.connected_to=item.name
+                    # since heretofore it has been a requirement that the names match
+    except Exception as e:
+        prRed(f"Error updating version in tree: {tree.name}; see error:")
+        print(e)
+            
+
+
+
+
+
 
 versioning_tasks = [
     # node bl_idname    task                required keyword arguments
@@ -253,6 +284,7 @@ versioning_tasks = [
     (['MantisTree', 'SchemaTree'], cleanup_4_5_0_LTS_interface_workaround, ['tree']),
     (['InputWidget'], up_0_12_13_add_widget_scale, ['node']),
     (['LinkFloor'], up_0_12_25_replace_floor_offset_type, ['node']),
+    (['SchemaTree'], schema_enable_custom_interface_types, ['tree']),
 ]