Skip to content
Snippets Groups Projects
metarig_menu.py 6.81 KiB
Newer Older
  • Learn to ignore specific revisions
  • # SPDX-License-Identifier: GPL-2.0-or-later
    
    from collections import defaultdict
    
    import bpy
    
    from .utils.rig import METARIG_DIR, get_resource
    
    
    class ArmatureSubMenu(bpy.types.Menu):
        # bl_idname = 'ARMATURE_MT_armature_class'
    
        def draw(self, context):
            layout = self.layout
    
            layout.label(text=self.bl_label)
    
            for op, name in self.operators:
                text = capwords(name.replace("_", " ")) + " (Meta-Rig)"
                layout.operator(op, icon='OUTLINER_OB_ARMATURE', text=text)
    
    
    
    def get_metarigs(metarigs, base_dir, base_path, *, path=[], nested=False):
    
        """ Searches for metarig modules, and returns a list of the
            imported modules.
        """
    
        dir_path = os.path.join(base_dir, *path)
    
        except FileNotFoundError:
            files = []
    
    
            is_dir = os.path.isdir(os.path.join(dir_path, f))  # Whether the file is a directory
    
            if f.count(".") >= 2 or (is_dir and "." in f):
                print("Warning: %r, filename contains a '.', skipping" % os.path.join(path, f))
    
                get_metarigs(metarigs[f], base_dir, base_path, path=[*path, f], nested=True)  # "" adds a final slash
    
            elif f.endswith(".py"):
                # Check straight-up python files
                f = f[:-3]
    
                module = get_resource('.'.join([*base_path, *path, f]))
    
    def make_metarig_add_execute(m):
        """ Create an execute method for a metarig creation operator.
        """
    
        def execute(self, context):
    
            bpy.ops.object.armature_add()
            obj = context.active_object
    
            obj.data.name = "metarig"
    
            # Remove default bone
            bpy.ops.object.mode_set(mode='EDIT')
    
            bones = context.active_object.data.edit_bones
            bones.remove(bones[0])
    
            bpy.ops.object.mode_set(mode='OBJECT')
    
            return {'FINISHED'}
    
    def make_metarig_menu_func(bl_idname, text):
        """ For some reason lambda's don't work for adding multiple menu
            items, so we use this instead to generate the functions.
        """
        def metarig_menu(self, context):
            self.layout.operator(bl_idname, icon='OUTLINER_OB_ARMATURE', text=text)
        return metarig_menu
    
    def make_submenu_func(bl_idname, text):
        def metarig_menu(self, context):
            self.layout.menu(bl_idname, icon='OUTLINER_OB_ARMATURE', text=text)
        return metarig_menu
    
    
    
        BASE_RIGIFY_DIR = os.path.dirname(__file__)
        BASE_RIGIFY_PATH = __name__.split('.')[:-1]
    
    
        get_metarigs(metarigs, os.path.join(BASE_RIGIFY_DIR, METARIG_DIR), [*BASE_RIGIFY_PATH, METARIG_DIR])
    
    def infinite_defaultdict():
        return defaultdict(infinite_defaultdict)
    
    metarigs = infinite_defaultdict()
    
    def create_metarig_ops(dic=metarigs):
        """Create metarig add Operators"""
        for metarig_category in dic:
            if metarig_category == "external":
                create_metarig_ops(dic[metarig_category])
                continue
            if not metarig_category in metarig_ops:
                metarig_ops[metarig_category] = []
            for m in dic[metarig_category].values():
                name = m.__name__.rsplit('.', 1)[1]
    
                # Dynamically construct an Operator
                T = type("Add_" + name + "_Metarig", (bpy.types.Operator,), {})
                T.bl_idname = "object.armature_" + name + "_metarig_add"
                T.bl_label = "Add " + name.replace("_", " ").capitalize() + " (metarig)"
                T.bl_options = {'REGISTER', 'UNDO'}
                T.execute = make_metarig_add_execute(m)
    
                metarig_ops[metarig_category].append((T, name))
    
    def create_menu_funcs():
        global menu_funcs
    
        for mop, name in metarig_ops[METARIG_DIR]:
    
            text = capwords(name.replace("_", " ")) + " (Meta-Rig)"
            menu_funcs += [make_metarig_menu_func(mop.bl_idname, text)]
    
    def create_armature_submenus(dic=metarigs):
        global menu_funcs
        metarig_categories = list(dic.keys())
        metarig_categories.sort()
        for metarig_category in metarig_categories:
            # Create menu functions
            if metarig_category == "external":
                create_armature_submenus(dic=metarigs["external"])
                continue
    
            armature_submenus.append(type('Class_' + metarig_category + '_submenu', (ArmatureSubMenu,), {}))
            armature_submenus[-1].bl_label = metarig_category + ' (submenu)'
            armature_submenus[-1].bl_idname = 'ARMATURE_MT_%s_class' % metarig_category
            armature_submenus[-1].operators = []
            menu_funcs += [make_submenu_func(armature_submenus[-1].bl_idname, metarig_category)]
    
            for mop, name in metarig_ops[metarig_category]:
                arm_sub = next((e for e in armature_submenus if e.bl_label == metarig_category + ' (submenu)'), '')
                arm_sub.operators.append((mop.bl_idname, name,))
    
    def init_metarig_menu():
        get_internal_metarigs()
        create_metarig_ops()
        create_menu_funcs()
        create_armature_submenus()
    
        from bpy.utils import register_class
    
    
        for cl in metarig_ops:
            for mop, name in metarig_ops[cl]:
    
                register_class(mop)
    
            register_class(arm_sub)
    
            bpy.types.VIEW3D_MT_armature_add.append(mf)
    
    
    def unregister():
    
        from bpy.utils import unregister_class
    
    
        for cl in metarig_ops:
            for mop, name in metarig_ops[cl]:
    
                unregister_class(mop)
    
            unregister_class(arm_sub)
    
            bpy.types.VIEW3D_MT_armature_add.remove(mf)
    
    def get_external_metarigs(feature_module_names):
    
        unregister()
    
        # Clear and fill metarigs public variables
        metarigs.clear()
        get_internal_metarigs()
    
        for module_name in feature_module_names:
    
                base_dir, base_path = feature_set_list.get_dir_path(module_name, METARIG_DIR)
    
    
                get_metarigs(metarigs['external'], base_dir, base_path)
            except Exception:
                print("Rigify Error: Could not load feature set '%s' metarigs: exception occurred.\n" % (feature_set))
                traceback.print_exc()
                print("")
                continue
    
    
        metarig_ops.clear()
        armature_submenus.clear()
        menu_funcs.clear()
    
        create_metarig_ops()
        create_menu_funcs()
        create_armature_submenus()
        register()