1
0

8 Commits 83415c6b5d ... 003e6b573a

Autor SHA1 Nachricht Datum
  Joseph Brandenburg 003e6b573a Fix: Nested Choose fails when linked to group output vor 2 Wochen
  Joseph Brandenburg cac82c21f8 Cleanup Drivers vor 2 Wochen
  Joseph Brandenburg 83415c6b5d Cleanup Drivers vor 2 Wochen
  Joseph Brandenburg e40d0fdc9c initial versioning for new interface classes vor 3 Wochen
  Joseph Brandenburg 4845445001 fix: unbound local error when updating group interface vor 3 Wochen
  Joseph Brandenburg 37976aaffb update interface draw for correct UI and clarity vor 3 Wochen
  Joseph Brandenburg fa05172de2 Interface Classes set the multi and default value now vor 3 Wochen
  Joseph Brandenburg dbb1998052 Add Custom Interface Socket Types vor 3 Wochen
6 geänderte Dateien mit 30 neuen und 194 gelöschten Zeilen
  1. 2 5
      __init__.py
  2. 1 1
      blender_manifest.toml
  3. 14 7
      schema_solve.py
  4. 0 126
      socket_definitions.py
  5. 12 22
      utilities.py
  6. 1 33
      versioning.py

+ 2 - 5
__init__.py

@@ -18,7 +18,7 @@ from .utilities import prRed
 
 MANTIS_VERSION_MAJOR=0
 MANTIS_VERSION_MINOR=12
-MANTIS_VERSION_SUB=27
+MANTIS_VERSION_SUB=26
 
 classLists = [module.TellClasses() for module in [
  link_nodes_ui,
@@ -312,6 +312,7 @@ 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 \
@@ -385,10 +386,6 @@ 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.27"
+version = "0.12.26"
 name = "Mantis"
 tagline = "Mantis is a rigging nodes toolkit"
 maintainer = "Nodespaghetti <josephbburg@protonmail.com>"

+ 14 - 7
schema_solve.py

@@ -313,7 +313,7 @@ class SchemaSolver:
         from_node = self.schema_nodes[(*self.node.ui_signature, from_ui_node.bl_idname)]
         from collections import deque
         unprepared = deque(from_node.hierarchy_dependencies)
-        self.prepare_nodes(unprepared)
+        self.prepare_nodes(unprepared, frame_mantis_nodes)
         from .utilities import cap, wrap
         get_index = from_node.evaluate_input("Index", self.index) # get the most recent link
         # getting the link at self.index just saves the trouble of killing the old links
@@ -514,15 +514,22 @@ class SchemaSolver:
                 return False
         return True
 
-    def prepare_nodes(self, unprepared):
+    def prepare_nodes(self, unprepared, frame_nodes):
         # At this point, we've already run a pretty exhaustive preperation phase to prep the schema's dependencies
         # So we should not need to add any new dependencies unless there is a bug elsewhere.
         # and in fact, I could skip this in some cases, and should investigate if profiling reveals a slowdown here.
-        forbidden=set()
-        e = None
-        # forbid some nodes - they aren't necessary to solve the schema & cause problems.
+        from .misc_nodes import UtilityChoose, UtilityKDChoosePoint, UtilityKDChooseXForm
+        forbidden=set() # forbid some nodes - they aren't necessary to solve the schema & cause problems.
         while unprepared:
             nc = unprepared.pop()
+            if isinstance(nc, (UtilityChoose, UtilityKDChoosePoint, UtilityKDChooseXForm)): 
+                can_skip=True                 # NOTE: this bug can also be fixed by adding
+                for output in nc.outputs:     # a no-op node between the choose and the output.
+                    for l in output.links:    # try that instead if this causes problems.
+                        if l.to_node in frame_nodes.values():
+                            can_skip = False; break
+                    if can_skip == False: break # don't keep looking
+                if can_skip: continue # do nothing because its links are not ready.
             if nc.node_type == 'DUMMY_SCHEMA' and not self.test_is_sub_schema(nc):
                 forbidden.add(nc) # do NOT add this as a dependency.
             if nc in forbidden: continue # trying to resolve dependencies for.
@@ -670,7 +677,7 @@ class SchemaSolver:
             if node.node_type == 'DUMMY_SCHEMA' and (schema_len_in := node.inputs.get("Schema Length")):
                 for l in schema_len_in.links:
                     unprepared.append(l.from_node)
-            self.prepare_nodes(unprepared)
+            self.prepare_nodes(unprepared, frame_mantis_nodes)
 
         # We have to prepare the nodes leading to Array Input Get
         for ui_link in array_input_get_link:
@@ -700,7 +707,7 @@ class SchemaSolver:
                 unprepared.extend(from_node.hierarchy_dependencies)
             else:
                 raise RuntimeError(" 671 there has been an error parsing the tree. Please report this as a bug.")
-            self.prepare_nodes(unprepared) # prepare only the dependencies we need for this link
+            self.prepare_nodes(unprepared, frame_mantis_nodes) # prepare only the dependencies we need for this link
             # and handle the output by the specific type
             if isinstance(to_ui_node, (SchemaConstOutput, NodeGroupOutput)):
                 self.handle_link_to_constant_output(frame_mantis_nodes, self.index, ui_link,  to_ui_node)

+ 0 - 126
socket_definitions.py

@@ -292,132 +292,6 @@ 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?

+ 12 - 22
utilities.py

@@ -271,31 +271,21 @@ 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)
+        # 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)
     else:
-        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 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)
     if item.parent.name == 'Array': s.display_shape = 'SQUARE_DOT'
     elif item.parent.name == 'Constant': s.display_shape='CIRCLE_DOT'
     return s

+ 1 - 33
versioning.py

@@ -220,6 +220,7 @@ 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']
@@ -243,38 +244,6 @@ 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
@@ -284,7 +253,6 @@ 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']),
 ]