Browse Source

Add: Widget Scaling, Default Collections

bad practice to do multiple things in one go but they are kind of
related since the scaling thing has to duplicate the object
and the obejct has to go somewhere
whatever, it works!
this commit also does the versioning to add the socket
Joseph Brandenburg 2 months ago
parent
commit
2709886712
7 changed files with 117 additions and 16 deletions
  1. 1 1
      __init__.py
  2. 24 0
      geometry_node_graphgen.py
  3. 28 10
      misc_nodes.py
  4. 2 0
      misc_nodes_socket_templates.py
  5. 9 2
      preferences.py
  6. 31 3
      utilities.py
  7. 22 0
      versioning.py

+ 1 - 1
__init__.py

@@ -18,7 +18,7 @@ from .utilities import prRed
 
 
 MANTIS_VERSION_MAJOR=0
 MANTIS_VERSION_MAJOR=0
 MANTIS_VERSION_MINOR=12
 MANTIS_VERSION_MINOR=12
-MANTIS_VERSION_SUB=12
+MANTIS_VERSION_SUB=13
 
 
 classLists = [module.TellClasses() for module in [
 classLists = [module.TellClasses() for module in [
  link_nodes_ui,
  link_nodes_ui,

+ 24 - 0
geometry_node_graphgen.py

@@ -152,6 +152,30 @@ def gen_import_obj_node_group():
         pass
         pass
     return tree
     return tree
 
 
+def gen_scale_object_data_modifier():
+    # A basic modifier that simply scales the object's data (for widgets)
+    # I need to be able to scale at import so that the user can easily
+    # control widget scale as inputs to components.
+    import bpy
+    from bpy import data, types
+    tree=bpy.data.node_groups.new("Scale Object Data","GeometryNodeTree")
+    tree.is_modifier=True
+    tree.interface.new_socket(name="Geometry",description="",in_out="OUTPUT",socket_type="NodeSocketGeometry")
+    tree.interface.new_socket(name="Geometry",description="",in_out="INPUT",socket_type="NodeSocketGeometry")
+    tree.interface.new_socket(name="Scale",description="",in_out="INPUT",socket_type="NodeSocketVector")
+    Group_Input = tree.nodes.new("NodeGroupInput")
+    Group_Output = tree.nodes.new("NodeGroupOutput")
+    Transform_Geometry = tree.nodes.new("GeometryNodeTransform")
+    tree.links.new(Transform_Geometry.outputs[0],Group_Output.inputs[0])
+    tree.links.new(Group_Input.outputs[0],Transform_Geometry.inputs[0])
+    tree.links.new(Group_Input.outputs[1],Transform_Geometry.inputs[3])
+    try:
+        from .utilities import SugiyamaGraph
+        SugiyamaGraph(tree, 4)
+    except: # there should not ever be a user error if this fails
+        pass
+    return tree
+
 def gen_simple_flip_modifier():
 def gen_simple_flip_modifier():
     import bpy
     import bpy
     from bpy import data, types
     from bpy import data, types

+ 28 - 10
misc_nodes.py

@@ -253,7 +253,6 @@ class InputBoolean(SimpleInputNode):
 
 
 class InputBooleanThreeTuple(SimpleInputNode):
 class InputBooleanThreeTuple(SimpleInputNode):
     '''A node representing a tuple of three booleans'''
     '''A node representing a tuple of three booleans'''
-        
     def __init__(self, signature, base_tree):
     def __init__(self, signature, base_tree):
         super().__init__(signature, base_tree)
         super().__init__(signature, base_tree)
         outputs = [""]
         outputs = [""]
@@ -1273,6 +1272,10 @@ class InputWidget(MantisNode):
         print(wrapGreen("Executing ")+wrapOrange("InputWidget Node ")+wrapWhite(f"{self}"))
         print(wrapGreen("Executing ")+wrapOrange("InputWidget Node ")+wrapWhite(f"{self}"))
         path = self.evaluate_input('Name')
         path = self.evaluate_input('Name')
         axes_flipped = self.evaluate_input('Flip Axes')
         axes_flipped = self.evaluate_input('Flip Axes')
+        scaling = self.evaluate_input('Scale')
+        add_scale_modifier = False
+        if scaling[0] != 1.0 or scaling[1] != 1.0 or scaling[2] != 1.0:
+            add_scale_modifier = True
         do_mirror = True
         do_mirror = True
         from os import path as os_path
         from os import path as os_path
         from .preferences import get_bl_addon_object
         from .preferences import get_bl_addon_object
@@ -1280,19 +1283,24 @@ class InputWidget(MantisNode):
         widgets_path = bl_mantis_addon.preferences.WidgetsLibraryFolder
         widgets_path = bl_mantis_addon.preferences.WidgetsLibraryFolder
         path = widgets_path+path # this guards the widgets root so the end-user
         path = widgets_path+path # this guards the widgets root so the end-user
         #  can easily change the widgets directory without breaking things
         #  can easily change the widgets directory without breaking things
+        from .utilities import get_default_collection
+        collection = get_default_collection(collection_type='WIDGET')
         file_name = os_path.split(path)[-1]
         file_name = os_path.split(path)[-1]
         obj_name = os_path.splitext(file_name)[0]
         obj_name = os_path.splitext(file_name)[0]
         obj_name_full = obj_name
         obj_name_full = obj_name
+        if add_scale_modifier:
+            obj_name_full+="_scaled_"+".".join(self.ui_signature[1:])
         if any(axes_flipped):
         if any(axes_flipped):
             obj_name_full+="_flipped_"
             obj_name_full+="_flipped_"
         for i, axis in enumerate("XYZ"):
         for i, axis in enumerate("XYZ"):
             if axes_flipped[i]: obj_name_full+=axis
             if axes_flipped[i]: obj_name_full+=axis
         from bpy import data
         from bpy import data
+        # what is this code doing? I thought I already linked it... TODO find out
         if  obj_name in data.objects.keys() and not \
         if  obj_name in data.objects.keys() and not \
             obj_name_full in data.objects.keys():
             obj_name_full in data.objects.keys():
                 self.bObject = data.objects.get(obj_name).copy()
                 self.bObject = data.objects.get(obj_name).copy()
                 self.bObject.name = obj_name_full
                 self.bObject.name = obj_name_full
-                if bContext: bContext.collection.objects.link(self.bObject)
+                collection.objects.link(self.bObject)
         # now check to see if it exists
         # now check to see if it exists
         elif obj_name_full in data.objects.keys():
         elif obj_name_full in data.objects.keys():
             prWhite(f"INFO: {obj_name_full} is already in this .blend file; skipping import.")
             prWhite(f"INFO: {obj_name_full} is already in this .blend file; skipping import.")
@@ -1303,22 +1311,32 @@ class InputWidget(MantisNode):
         else:
         else:
             from .utilities import import_object_from_file
             from .utilities import import_object_from_file
             self.bObject = import_object_from_file(path)
             self.bObject = import_object_from_file(path)
-            if any(axes_flipped):
+            if any(axes_flipped) or add_scale_modifier:
                 self.bObject = self.bObject.copy()
                 self.bObject = self.bObject.copy()
                 self.bObject.name = obj_name_full
                 self.bObject.name = obj_name_full
-                if bContext: bContext.collection.objects.link(self.bObject)
+                collection.objects.link(self.bObject)
+        # do the scaling...
+        if add_scale_modifier:
+            if (scale_modifier := self.bObject.modifiers.get("Scale Object Data")) is None:
+                scale_modifier = self.bObject.modifiers.new("Scale Object Data", type='NODES')
+            ng = data.node_groups.get("Scale Object Data")
+            if ng is None:
+                from .geometry_node_graphgen import gen_scale_object_data_modifier
+                ng = gen_scale_object_data_modifier()
+            scale_modifier.node_group = ng
+            scale_modifier['Socket_2']=scaling
         # now we'll check for the mirrors.
         # now we'll check for the mirrors.
-        axes_flipped = self.evaluate_input('Flip Axes')
         if any(axes_flipped) and do_mirror:
         if any(axes_flipped) and do_mirror:
-            import_modifier = self.bObject.modifiers.new("Simple Flip", type="NODES")
+            if (flip_modifier := self.bObject.modifiers.get("Simple Flip")) is None:
+                flip_modifier = self.bObject.modifiers.new("Simple Flip", type="NODES")
             ng = data.node_groups.get("Simple Flip")
             ng = data.node_groups.get("Simple Flip")
             if ng is None:
             if ng is None:
                 from .geometry_node_graphgen import gen_simple_flip_modifier
                 from .geometry_node_graphgen import gen_simple_flip_modifier
                 ng = gen_simple_flip_modifier()
                 ng = gen_simple_flip_modifier()
-            import_modifier.node_group = ng
-            import_modifier["Socket_2"]=axes_flipped[0]
-            import_modifier["Socket_3"]=axes_flipped[1]
-            import_modifier["Socket_4"]=axes_flipped[2]
+            flip_modifier.node_group = ng
+            flip_modifier["Socket_2"]=axes_flipped[0]
+            flip_modifier["Socket_3"]=axes_flipped[1]
+            flip_modifier["Socket_4"]=axes_flipped[2]
         self.prepared, self.executed = True, True
         self.prepared, self.executed = True, True
     
     
     def bGetObject(self, mode=''):
     def bGetObject(self, mode=''):

+ 2 - 0
misc_nodes_socket_templates.py

@@ -7,6 +7,8 @@ SplineIndexTemplate = SockTemplate(name="Spline Index",
 InputWidgetSockets = [
 InputWidgetSockets = [
     WidgetName := SockTemplate(name='Name', is_input=True,
     WidgetName := SockTemplate(name='Name', is_input=True,
         bl_idname="EnumWidgetLibrarySocket",),
         bl_idname="EnumWidgetLibrarySocket",),
+    Scale := SockTemplate(name='Scale', is_input=True,
+        bl_idname="VectorScaleSocket", default_value=(1.0, 1.0, 1.0)),
     FlipAxes := SockTemplate(name='Flip Axes', is_input=True,
     FlipAxes := SockTemplate(name='Flip Axes', is_input=True,
         bl_idname="BooleanThreeTupleSocket",),
         bl_idname="BooleanThreeTupleSocket",),
     xFormOutput := SockTemplate(name='Widget',
     xFormOutput := SockTemplate(name='Widget',

+ 9 - 2
preferences.py

@@ -39,8 +39,6 @@ def filepath_idiot_test(path):
     except RecursionError:
     except RecursionError:
         do_error_popup()
         do_error_popup()
         return ''
         return ''
-    
-
 
 
 def widget_library_get(self):
 def widget_library_get(self):
     return self.widget_library_path
     return self.widget_library_path
@@ -69,6 +67,15 @@ class MantisPreferences(bpy.types.AddonPreferences):
         set=widget_library_idiot_test,
         set=widget_library_idiot_test,
         subtype = 'FILE_PATH',
         subtype = 'FILE_PATH',
         default = os.path.join(dir_path, 'widgets'),)
         default = os.path.join(dir_path, 'widgets'),)
+    WidgetDefaultCollection:bpy.props.StringProperty(
+        name = "Import Widgets into Collection",
+        default = "Widgets",)
+    CurveDefaultCollection:bpy.props.StringProperty(
+        name = "Import Curves into Collection",
+        default = "MetaCurves",)
+    MetaArmatureDefaultCollection:bpy.props.StringProperty(
+        name = "Import Meta-Armatures into Collection",
+        default = "MetaRigs",)
     ComponentsLibraryFolder:bpy.props.StringProperty(
     ComponentsLibraryFolder:bpy.props.StringProperty(
         name = "Widget Library Folder",
         name = "Widget Library Folder",
         subtype = 'FILE_PATH',
         subtype = 'FILE_PATH',

+ 31 - 3
utilities.py

@@ -352,12 +352,32 @@ def bind_modifier_operator(modifier, operator):
             prWhite(f"Binding Deformer {modifier.name} to target {target.name}")
             prWhite(f"Binding Deformer {modifier.name} to target {target.name}")
             operator(modifier=modifier.name)
             operator(modifier=modifier.name)
 
 
+def get_default_collection(collection_type='WIDGET'):
+    from .preferences import get_bl_addon_object
+    from bpy import data, context
+    mantis_addon = get_bl_addon_object(raise_error=True)
+    match collection_type:
+        case "WIDGET":
+            default_collection_name=mantis_addon.preferences.WidgetDefaultCollection
+        case "CURVE":
+            default_collection_name=mantis_addon.preferences.CurveDefaultCollection
+        case "ARMATURE":
+            default_collection_name=mantis_addon.preferences.MetaArmatureDefaultCollection
+    if default_collection_name:
+        if not (default_collection := data.collections.get(default_collection_name)):
+            default_collection = data.collections.new(default_collection_name)
+            context.scene.collection.children.link(default_collection)
+        collection = default_collection
+    else: collection = context.collection
+    return collection
+
 def import_widget_obj(path,):
 def import_widget_obj(path,):
     from bpy.app import version as bpy_version
     from bpy.app import version as bpy_version
     from bpy import context, data
     from bpy import context, data
     from os import path as os_path
     from os import path as os_path
     file_name = os_path.split(path)[-1]
     file_name = os_path.split(path)[-1]
     obj_name = os_path.splitext(file_name)[0]
     obj_name = os_path.splitext(file_name)[0]
+    collection = get_default_collection(collection_type='WIDGET')
     if bpy_version < (4,5,0):
     if bpy_version < (4,5,0):
         original_active = context.active_object
         original_active = context.active_object
         # for blender versions prior to 4.5.0, we have to import with an operator
         # for blender versions prior to 4.5.0, we have to import with an operator
@@ -377,6 +397,10 @@ def import_widget_obj(path,):
         ob = None
         ob = None
         for ob in data.objects:
         for ob in data.objects:
             if ob.name in ob_names_before: continue
             if ob.name in ob_names_before: continue
+            # this is easier than setting the active collection before import.
+            for other_collection in ob.users_collection:
+                other_collection.objects.unlink(ob)
+            collection.objects.link(ob)
             return ob # return the first one, that should be the one
             return ob # return the first one, that should be the one
         else: # no new object was found - fail.
         else: # no new object was found - fail.
             # I don't expect this to happen unless there is an error in the operator.
             # I don't expect this to happen unless there is an error in the operator.
@@ -387,8 +411,9 @@ def import_widget_obj(path,):
         mesh = data.meshes.new(obj_name)
         mesh = data.meshes.new(obj_name)
         ob = data.objects.new(name=obj_name, object_data=mesh)
         ob = data.objects.new(name=obj_name, object_data=mesh)
         # we'll do a geometry nodes import
         # we'll do a geometry nodes import
-        context.collection.objects.link(ob)
-        import_modifier = ob.modifiers.new("Import OBJ", type="NODES")
+        collection.objects.link(ob)
+        if (import_modifier := ob.modifiers.get("Import OBJ")) is None:
+            import_modifier = ob.modifiers.new("Import OBJ", type='NODES')
         ng = data.node_groups.get("Import OBJ")
         ng = data.node_groups.get("Import OBJ")
         if ng is None:
         if ng is None:
             from .geometry_node_graphgen import gen_import_obj_node_group
             from .geometry_node_graphgen import gen_import_obj_node_group
@@ -426,7 +451,8 @@ def import_metarig_data(metarig_data : dict, ):
         )
         )
     
     
     # have to add it to the view layer to switch modes.
     # have to add it to the view layer to switch modes.
-    context.collection.objects.link(armature_object)
+    collection = get_default_collection(collection_type="ARMATURE")
+    collection.objects.link(armature_object)
     switch_mode('EDIT', objects = [armature_object])
     switch_mode('EDIT', objects = [armature_object])
     
     
     while (children):
     while (children):
@@ -478,6 +504,8 @@ def import_curve_data_to_object(curve_name, curve_data):
             if prop in spline_data.keys():
             if prop in spline_data.keys():
                 if prop in ['points', 'type', 'index']: continue
                 if prop in ['points', 'type', 'index']: continue
                 setattr(spline, prop, spline_data[prop])
                 setattr(spline, prop, spline_data[prop])
+    collection = get_default_collection(collection_type="CURVE")
+    collection.objects.link(curve_object)
     return curve_object
     return curve_object
 
 
 
 

+ 22 - 0
versioning.py

@@ -173,6 +173,27 @@ def up_0_12_1_add_inherit_color(*args, **kwargs):
         prRed(f"Error updating version in node: {node.id_data.name}::{node.name}; see error:")
         prRed(f"Error updating version in node: {node.id_data.name}::{node.name}; see error:")
         print(e)
         print(e)
 
 
+def up_0_12_13_add_widget_scale(*args, **kwargs):
+    # add an inherit color input.
+    node = kwargs['node']
+    current_major_version = node.id_data.mantis_version[0]
+    current_minor_version = node.id_data.mantis_version[1]
+    current_sub_version = node.id_data.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 > 12: return
+    # sub version must be 12 or less
+    prPurple(f"Adding \"Scale\" socket to {node.name}")
+    try:
+        scale = node.inputs.get('Scale')
+        if scale is None:
+            s = node.inputs.new('VectorScaleSocket', 'Scale',)
+            s.default_value=[1.0,1.0,1.0]
+            node.inputs.move(len(node.inputs)-1, 1)
+    except Exception as e:
+        prRed(f"Error updating version in node: {node.id_data.name}::{node.name}; see error:")
+        print(e)
+
 # versioning tasks that involve Blender versions rather than Mantis versions:
 # versioning tasks that involve Blender versions rather than Mantis versions:
 error_description_4_5_0_LTS = 'There is a bug in Blender 4.5.0 LTS, that is why these sockets are blue.'\
 error_description_4_5_0_LTS = 'There is a bug in Blender 4.5.0 LTS, that is why these sockets are blue.'\
                              ' This will be fixed in future Blender versions.'
                              ' This will be fixed in future Blender versions.'
@@ -205,6 +226,7 @@ versioning_tasks = [
     (['xFormBoneNode'], version_upgrade_bone_0_12_0_from_older, ['node'],),
     (['xFormBoneNode'], version_upgrade_bone_0_12_0_from_older, ['node'],),
     (['xFormBoneNode'], up_0_12_1_add_inherit_color, ['node'],),
     (['xFormBoneNode'], up_0_12_1_add_inherit_color, ['node'],),
     (['MantisTree', 'SchemaTree'], cleanup_4_5_0_LTS_interface_workaround, ['tree']),
     (['MantisTree', 'SchemaTree'], cleanup_4_5_0_LTS_interface_workaround, ['tree']),
+    (['InputWidget'], up_0_12_13_add_widget_scale, ['node']),
 ]
 ]