From a9b8baffa30ffe495bdb0ff04152d12b4782e337 Mon Sep 17 00:00:00 2001 From: Eli Date: Sun, 12 Mar 2023 18:06:08 -0400 Subject: [PATCH 1/2] Custom Edit Context Views Adds custom edit context views and closes #7 The views added are "Armature Mode", "Skeleton Mode", "Muscle Mode", and "Skin Mode" Additionally, there's a "Smart Object Mode" which functions as "Bone Edit Context" would but can be run on any object, not just bones. Smart Object Mode will make visible any selected objects, plus any objects in any geometry nodes present on these selected objects. --- scripts/ui/__init__.py | 6 +- scripts/ui/mode_selection.py | 170 +++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 scripts/ui/mode_selection.py diff --git a/scripts/ui/__init__.py b/scripts/ui/__init__.py index 02baa13..e02be14 100644 --- a/scripts/ui/__init__.py +++ b/scripts/ui/__init__.py @@ -3,13 +3,17 @@ """Contains all user interface elements for the Blender plugin.""" from . import bone_properties - +from . import mode_selection def register_all(): bone_properties.register() + mode_selection.register() + #object_type_selection.register() print("Registered scripts.ui") def unregister_all(): bone_properties.unregister() + mode_selection.unregister() + #object_type_selection.unregister() print("Unregistered scripts.ui") diff --git a/scripts/ui/mode_selection.py b/scripts/ui/mode_selection.py new file mode 100644 index 0000000..a8dba36 --- /dev/null +++ b/scripts/ui/mode_selection.py @@ -0,0 +1,170 @@ +import bpy + +class ARF_OT_Armature_Mode(bpy.types.Operator): + bl_label = "Armature Mode" + bl_idname = "arf.armature_mode" + + def execute(self, context): + for obj in context.scene.objects: + if (obj.type == "ARMATURE"): + obj.hide_set(False) + else: + obj.hide_set(True) + return {'FINISHED'} + + +class ARF_OT_Skeleton_Mode(bpy.types.Operator): + bl_label = "Skeleton Mode" + bl_idname = "arf.skeleton_mode" + + def execute(self, context): + for obj in context.scene.objects: + if (obj.ARF.object_type == "BONE"): + obj.hide_set(False) + elif (obj.ARF.object_type != "NONE"): + obj.hide_set(True) + return {'FINISHED'} + + +class ARF_OT_Smart_Object_Mode(bpy.types.Operator): + bl_label = "Smart Object Mode" + bl_idname = "arf.smart_object_mode" + + def execute(self, context): + # First check if we selected at least one object. + # To not hide all objects by mistake. + if context.selected_objects == []: + return {'FINISHED'} + + selected = context.selected_objects + # Then, hide all objects + for obj in context.scene.objects: + obj.hide_set(True) + + # Now get all objects tied to geom nodes tied to the selected object to unhide those + for selection in selected: + for modifier in selection.modifiers: + if modifier.type != 'NODES': + continue + # Disabled geom node modifiers sometimes do not contain a node_group + if (modifier.node_group == None): + continue + + for input in modifier.node_group.inputs: + if not (isinstance(input, bpy.types.NodeSocketInterfaceObject)): + continue + + # For some reason input.default_value is a copy of the object and not the actual + # object in the scene, so we need to get the actual object in the scene. + inputobj = bpy.context.scene.objects.get(input.default_value.name) + inputobj.hide_set(False) + # We also need to set every parent to also be visible + parent = inputobj.parent + while (parent != None): + parent.hide_set(False) + parent = parent.parent + + # Now unhide the object itself + selection.hide_set(False) + # If the selection has any parents they must also be made visible to undo + # hiding all objects so the selection can be seen + parent = selection.parent + while (parent != None): + parent.hide_set(False) + parent = parent.parent + return {'FINISHED'} + + +class ARF_OT_Muscle_Mode(bpy.types.Operator): + bl_label = "Muscle Mode" + bl_idname = "arf.muscle_mode" + + def execute(self, context): + for obj in context.scene.objects: + if (obj.ARF.object_type == "MUSCLE" or obj.ARF.object_type == "BONE"): + obj.hide_set(False) + elif (obj.ARF.object_type != "NONE"): + obj.hide_set(True) + return {'FINISHED'} + + +class ARF_OT_Skin_Mode(bpy.types.Operator): + bl_label = "Skin Mode" + bl_idname = "arf.skin_mode" + + def execute(self, context): + for obj in context.scene.objects: + if (obj.ARF.object_type == "SKIN"): + obj.hide_set(False) + elif (obj.ARF.object_type != "NONE"): + obj.hide_set(True) + return {'FINISHED'} + + +# A submenu with just the mode select options. +# In case this is needed by any other classes. +class ARF_MT_Mode_Select_Submenu(bpy.types.Menu): + bl_label = "Mode Select Submenu" + bl_idname = "ARF_MT_Mode_Select_Submenu" + + def draw(self, context): + layout = self.layout + layout.operator("arf.smart_object_mode") + layout.operator("arf.armature_mode") + layout.operator("arf.skeleton_mode") + layout.operator("arf.muscle_mode") + layout.operator("arf.skin_mode") + + +# The core ARF menu. When more options besides +# mode selection are desired this should be moved to its own class +# probably +class ARF_MT_ARF_Menu(bpy.types.Menu): + bl_label = "ARF" + bl_idname = "ARF_MT_ARF_Menu" + + def draw(self, context): + layout = self.layout + layout.operator("arf.smart_object_mode") + layout.operator("arf.armature_mode") + layout.operator("arf.skeleton_mode") + layout.operator("arf.muscle_mode") + layout.operator("arf.skin_mode") + # If mode options are desired in a submenu instead this can be uncommented + # layout.menu("ARF_MT_Mode_Select_Submenu", icon="COLLAPSEMENU") + + def execute(self, context): + print("Executing context ") + + return {'FINISHED'} + + +def draw_item(self, context): + layout = self.layout + layout.menu(ARF_MT_ARF_Menu.bl_idname) + + +def register(): + bpy.utils.register_class(ARF_OT_Armature_Mode) + bpy.utils.register_class(ARF_OT_Skeleton_Mode) + bpy.utils.register_class(ARF_OT_Smart_Object_Mode) + bpy.utils.register_class(ARF_OT_Muscle_Mode) + bpy.utils.register_class(ARF_OT_Skin_Mode) + bpy.utils.register_class(ARF_MT_Mode_Select_Submenu) + bpy.utils.register_class(ARF_MT_ARF_Menu) + bpy.types.TOPBAR_MT_editor_menus.append(draw_item) + + +def unregister(): + bpy.utils.unregister_class(ARF_OT_Armature_Mode) + bpy.utils.unregister_class(ARF_OT_Skeleton_Mode) + bpy.utils.unregister_class(ARF_OT_Smart_Object_Mode) + bpy.utils.unregister_class(ARF_OT_Muscle_Mode) + bpy.utils.unregister_class(ARF_OT_Skin_Mode) + bpy.utils.unregister_class(ARF_MT_Mode_Select_Submenu) + bpy.utils.unregister_class(ARF_MT_ARF_Menu) + bpy.types.TOPBAR_MT_editor_menus.remove(draw_item) + + +if __name__ == "__main__": + register() From 1125e82c8019d8fd39071c4fc6d5aa6089e4fd5b Mon Sep 17 00:00:00 2001 From: Pine Date: Sat, 1 Apr 2023 20:54:23 -0400 Subject: [PATCH 2/2] Smart Object Select now relies on geometry_constraints as explicitly set in the ARF menu rather than trying to intuit them from the geometry nodes. Also adds asserts for ARF existing, removes any code going over line 80, and adds doc strings to the top of the file. --- scripts/ui/__init__.py | 2 -- scripts/ui/mode_selection.py | 45 ++++++++++++++++++------------------ 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/scripts/ui/__init__.py b/scripts/ui/__init__.py index e02be14..278b587 100644 --- a/scripts/ui/__init__.py +++ b/scripts/ui/__init__.py @@ -8,12 +8,10 @@ def register_all(): bone_properties.register() mode_selection.register() - #object_type_selection.register() print("Registered scripts.ui") def unregister_all(): bone_properties.unregister() mode_selection.unregister() - #object_type_selection.unregister() print("Unregistered scripts.ui") diff --git a/scripts/ui/mode_selection.py b/scripts/ui/mode_selection.py index a8dba36..88f9b09 100644 --- a/scripts/ui/mode_selection.py +++ b/scripts/ui/mode_selection.py @@ -1,3 +1,9 @@ +#!/usr/bin/python3 + +"""Contains code for drawing the ARF menu at the top of the screen +As well as the code for mode selection. Eventually when the ARF menu contains +more things, it should probably be moved to its own file.""" + import bpy class ARF_OT_Armature_Mode(bpy.types.Operator): @@ -6,6 +12,7 @@ class ARF_OT_Armature_Mode(bpy.types.Operator): def execute(self, context): for obj in context.scene.objects: + assert hasattr(obj, 'ARF') if (obj.type == "ARMATURE"): obj.hide_set(False) else: @@ -19,6 +26,7 @@ class ARF_OT_Skeleton_Mode(bpy.types.Operator): def execute(self, context): for obj in context.scene.objects: + assert hasattr(obj, 'ARF') if (obj.ARF.object_type == "BONE"): obj.hide_set(False) elif (obj.ARF.object_type != "NONE"): @@ -41,33 +49,22 @@ def execute(self, context): for obj in context.scene.objects: obj.hide_set(True) - # Now get all objects tied to geom nodes tied to the selected object to unhide those + # Now get all objects tied to geom nodes + # tied to the selected object to unhide those. for selection in selected: - for modifier in selection.modifiers: - if modifier.type != 'NODES': - continue - # Disabled geom node modifiers sometimes do not contain a node_group - if (modifier.node_group == None): - continue - - for input in modifier.node_group.inputs: - if not (isinstance(input, bpy.types.NodeSocketInterfaceObject)): - continue - - # For some reason input.default_value is a copy of the object and not the actual - # object in the scene, so we need to get the actual object in the scene. - inputobj = bpy.context.scene.objects.get(input.default_value.name) - inputobj.hide_set(False) - # We also need to set every parent to also be visible - parent = inputobj.parent - while (parent != None): - parent.hide_set(False) - parent = parent.parent + assert hasattr(selection, 'ARF') + for geom_c in selection.ARF.bone_properties.geometry_constraints: + geom_c.constraint_object.hide_set(False) + # We also need to set every parent to also be visible + parent = geom_c.constraint_object.parent + while (parent != None): + parent.hide_set(False) + parent = parent.parent # Now unhide the object itself selection.hide_set(False) - # If the selection has any parents they must also be made visible to undo - # hiding all objects so the selection can be seen + # If the selection has any parents they must also be made visible + # undoing hiding all objects so the selection can be seen parent = selection.parent while (parent != None): parent.hide_set(False) @@ -81,6 +78,7 @@ class ARF_OT_Muscle_Mode(bpy.types.Operator): def execute(self, context): for obj in context.scene.objects: + assert hasattr(obj, 'ARF') if (obj.ARF.object_type == "MUSCLE" or obj.ARF.object_type == "BONE"): obj.hide_set(False) elif (obj.ARF.object_type != "NONE"): @@ -94,6 +92,7 @@ class ARF_OT_Skin_Mode(bpy.types.Operator): def execute(self, context): for obj in context.scene.objects: + assert hasattr(obj, 'ARF') if (obj.ARF.object_type == "SKIN"): obj.hide_set(False) elif (obj.ARF.object_type != "NONE"):