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_MINOR=12
-MANTIS_VERSION_SUB=12
+MANTIS_VERSION_SUB=13
 
 classLists = [module.TellClasses() for module in [
  link_nodes_ui,

+ 24 - 0
geometry_node_graphgen.py

@@ -152,6 +152,30 @@ def gen_import_obj_node_group():
         pass
     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():
     import bpy
     from bpy import data, types

+ 28 - 10
misc_nodes.py

@@ -253,7 +253,6 @@ class InputBoolean(SimpleInputNode):
 
 class InputBooleanThreeTuple(SimpleInputNode):
     '''A node representing a tuple of three booleans'''
-        
     def __init__(self, signature, base_tree):
         super().__init__(signature, base_tree)
         outputs = [""]
@@ -1273,6 +1272,10 @@ class InputWidget(MantisNode):
         print(wrapGreen("Executing ")+wrapOrange("InputWidget Node ")+wrapWhite(f"{self}"))
         path = self.evaluate_input('Name')
         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
         from os import path as os_path
         from .preferences import get_bl_addon_object
@@ -1280,19 +1283,24 @@ class InputWidget(MantisNode):
         widgets_path = bl_mantis_addon.preferences.WidgetsLibraryFolder
         path = widgets_path+path # this guards the widgets root so the end-user
         #  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]
         obj_name = os_path.splitext(file_name)[0]
         obj_name_full = obj_name
+        if add_scale_modifier:
+            obj_name_full+="_scaled_"+".".join(self.ui_signature[1:])
         if any(axes_flipped):
             obj_name_full+="_flipped_"
         for i, axis in enumerate("XYZ"):
             if axes_flipped[i]: obj_name_full+=axis
         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 \
             obj_name_full in data.objects.keys():
                 self.bObject = data.objects.get(obj_name).copy()
                 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
         elif obj_name_full in data.objects.keys():
             prWhite(f"INFO: {obj_name_full} is already in this .blend file; skipping import.")
@@ -1303,22 +1311,32 @@ class InputWidget(MantisNode):
         else:
             from .utilities import import_object_from_file
             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.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.
-        axes_flipped = self.evaluate_input('Flip Axes')
         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")
             if ng is None:
                 from .geometry_node_graphgen import 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
     
     def bGetObject(self, mode=''):

+ 2 - 0
misc_nodes_socket_templates.py

@@ -7,6 +7,8 @@ SplineIndexTemplate = SockTemplate(name="Spline Index",
 InputWidgetSockets = [
     WidgetName := SockTemplate(name='Name', is_input=True,
         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,
         bl_idname="BooleanThreeTupleSocket",),
     xFormOutput := SockTemplate(name='Widget',

+ 9 - 2
preferences.py

@@ -39,8 +39,6 @@ def filepath_idiot_test(path):
     except RecursionError:
         do_error_popup()
         return ''
-    
-
 
 def widget_library_get(self):
     return self.widget_library_path
@@ -69,6 +67,15 @@ class MantisPreferences(bpy.types.AddonPreferences):
         set=widget_library_idiot_test,
         subtype = 'FILE_PATH',
         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(
         name = "Widget Library Folder",
         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}")
             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,):
     from bpy.app import version as bpy_version
     from bpy import context, data
     from os import path as os_path
     file_name = os_path.split(path)[-1]
     obj_name = os_path.splitext(file_name)[0]
+    collection = get_default_collection(collection_type='WIDGET')
     if bpy_version < (4,5,0):
         original_active = context.active_object
         # 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
         for ob in data.objects:
             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
         else: # no new object was found - fail.
             # 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)
         ob = data.objects.new(name=obj_name, object_data=mesh)
         # 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")
         if ng is None:
             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.
-    context.collection.objects.link(armature_object)
+    collection = get_default_collection(collection_type="ARMATURE")
+    collection.objects.link(armature_object)
     switch_mode('EDIT', objects = [armature_object])
     
     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 ['points', 'type', 'index']: continue
                 setattr(spline, prop, spline_data[prop])
+    collection = get_default_collection(collection_type="CURVE")
+    collection.objects.link(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:")
         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:
 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.'
@@ -205,6 +226,7 @@ versioning_tasks = [
     (['xFormBoneNode'], version_upgrade_bone_0_12_0_from_older, ['node'],),
     (['xFormBoneNode'], up_0_12_1_add_inherit_color, ['node'],),
     (['MantisTree', 'SchemaTree'], cleanup_4_5_0_LTS_interface_workaround, ['tree']),
+    (['InputWidget'], up_0_12_13_add_widget_scale, ['node']),
 ]