diff options
author | Alexander Gavrilov <angavrilov@gmail.com> | 2019-02-16 13:57:57 +0300 |
---|---|---|
committer | Alexander Gavrilov <angavrilov@gmail.com> | 2019-03-14 14:39:16 +0300 |
commit | 36e8d00aec705b06008a0bc334fe266448b4f2c2 (patch) | |
tree | 02bf0290ec6423c611e3cd4ad49c69cb77acb67e /rigify/metarig_menu.py | |
parent | eabb5cddf79e5fae3ca429242cf2c6f5a272920e (diff) |
Rigify: add support for user-defined rig packages and related utilities.
As suggested by @icappielo, and after discussion with @meta-androcto,
I start a public request to commit third-party contributions already
accepted to https://github.com/eigen-value/rigify/tree/rigify_0.6_beta
Specifically, this includes:
* User-defined rig package (feature set) support by @pioverfour.
This allows users to install pre-packaged rig sets via zip
files, which become accessible together with built-in rigs,
as discussed in T52758.
https://github.com/eigen-value/rigify/pull/1
* Modularization of python script generation, allowing rigs to
add their own utility functions and operators to the generated
script. This is critical to make custom rig support really
useful.
https://github.com/eigen-value/rigify/pull/5
* The utils.py file is split into multiple modules with a backward
compatibility proxy for old functions.
* Automatic verification that different rigs don't try to create
different rig settings with the same name to alleviate increased
risk of namespace conflicts with custom rigs.
https://github.com/eigen-value/rigify/pull/7
* New utility class that implements bone layer selection UI.
https://github.com/eigen-value/rigify/pull/6
* New utilities to replace copy & pasted boilerplate code for
creating custom properties, constraints and drivers.
https://github.com/eigen-value/rigify/pull/11
Some other random changes by MAD have likely slipped through.
These changes have already been extensively discussed and accepted
into the branch by @luciorossi, so I see no reason not to commit
them to the official repository to be tested during 2.8 beta.
Reviewers: icappiello
Differential Revision: https://developer.blender.org/D4364
Diffstat (limited to 'rigify/metarig_menu.py')
-rw-r--r-- | rigify/metarig_menu.py | 165 |
1 files changed, 102 insertions, 63 deletions
diff --git a/rigify/metarig_menu.py b/rigify/metarig_menu.py index 102d366d..18d8e550 100644 --- a/rigify/metarig_menu.py +++ b/rigify/metarig_menu.py @@ -37,45 +37,40 @@ class ArmatureSubMenu(bpy.types.Menu): layout.operator(op, icon='OUTLINER_OB_ARMATURE', text=text) -def get_metarig_list(path, depth=0): +def get_metarigs(base_path, path, depth=0): """ Searches for metarig modules, and returns a list of the imported modules. """ - metarigs = [] - metarigs_dict = dict() - MODULE_DIR = os.path.dirname(__file__) - METARIG_DIR_ABS = os.path.join(MODULE_DIR, utils.METARIG_DIR) - SEARCH_DIR_ABS = os.path.join(METARIG_DIR_ABS, path) - files = os.listdir(SEARCH_DIR_ABS) + + metarigs = {} + + files = os.listdir(os.path.join(base_path, path)) files.sort() for f in files: - # Is it a directory? - complete_path = os.path.join(SEARCH_DIR_ABS, f) - if os.path.isdir(complete_path) and depth == 0: - if f[0] != '_': - metarigs_dict[f] = get_metarig_list(f, depth=1) - else: - continue - elif not f.endswith(".py"): + is_dir = os.path.isdir(os.path.join(base_path, path, f)) # Whether the file is a directory + + # Stop cases + if f[0] in [".", "_"]: continue - elif f == "__init__.py": + if f.count(".") >= 2 or (is_dir and "." in f): + print("Warning: %r, filename contains a '.', skipping" % os.path.join(path, f)) continue - else: - module_name = f[:-3] - try: - if depth == 1: - metarigs += [utils.get_metarig_module(module_name, utils.METARIG_DIR + '.' + path)] - else: - metarigs += [utils.get_metarig_module(module_name, utils.METARIG_DIR)] - except (ImportError): - pass - if depth == 1: - return metarigs + if is_dir: # Check directories + # Check for sub-metarigs + metarigs[f] = get_metarigs(base_path, os.path.join(path, f, ""), depth=1) # "" adds a final slash + elif f.endswith(".py"): + # Check straight-up python files + f = f[:-3] + module_name = os.path.join(path, f).replace(os.sep, ".") + metarig_module = utils.get_resource(module_name, base_path=base_path) + if depth == 1: + metarigs[f] = metarig_module + else: + metarigs[utils.METARIG_DIR] = {f: metarig_module} - metarigs_dict[utils.METARIG_DIR] = metarigs - return metarigs_dict + return metarigs def make_metarig_add_execute(m): @@ -117,47 +112,69 @@ def make_submenu_func(bl_idname, text): # Get the metarig modules -metarigs_dict = get_metarig_list("") -armature_submenus = [] +def get_internal_metarigs(): + MODULE_DIR = os.path.dirname(os.path.dirname(__file__)) -# Create metarig add Operators -metarig_ops = {} -for metarig_class in metarigs_dict: - metarig_ops[metarig_class] = [] - for m in metarigs_dict[metarig_class]: - 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_class].append((T, name)) + metarigs.update(get_metarigs(MODULE_DIR, os.path.join(os.path.basename(os.path.dirname(__file__)), utils.METARIG_DIR, ''))) +metarigs = {} +metarig_ops = {} +armature_submenus = [] menu_funcs = [] -for mop, name in metarig_ops[utils.METARIG_DIR]: - text = capwords(name.replace("_", " ")) + " (Meta-Rig)" - menu_funcs += [make_metarig_menu_func(mop.bl_idname, text)] +get_internal_metarigs() -metarigs_dict.pop(utils.METARIG_DIR) +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[utils.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 + if metarig_category == utils.METARIG_DIR: + continue -metarig_classes = list(metarigs_dict.keys()) -metarig_classes.sort() -for metarig_class in metarig_classes: - # Create menu functions + 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)] - armature_submenus.append(type('Class_' + metarig_class + '_submenu', (ArmatureSubMenu,), {})) - armature_submenus[-1].bl_label = metarig_class + ' (submenu)' - armature_submenus[-1].bl_idname = 'ARMATURE_MT_%s_class' % metarig_class - armature_submenus[-1].operators = [] - menu_funcs += [make_submenu_func(armature_submenus[-1].bl_idname, metarig_class)] + 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,)) - for mop, name in metarig_ops[metarig_class]: - arm_sub = next((e for e in armature_submenus if e.bl_label == metarig_class + ' (submenu)'), '') - arm_sub.operators.append((mop.bl_idname, name,)) +create_metarig_ops() +create_menu_funcs() +create_armature_submenus() ### Registering ### @@ -176,7 +193,6 @@ def register(): for mf in menu_funcs: bpy.types.VIEW3D_MT_armature_add.append(mf) - def unregister(): from bpy.utils import unregister_class @@ -189,3 +205,26 @@ def unregister(): for mf in menu_funcs: bpy.types.VIEW3D_MT_armature_add.remove(mf) + +def get_external_metarigs(feature_sets_path): + unregister() + + # Clear and fill metarigs public variables + metarigs.clear() + get_internal_metarigs() + metarigs['external'] = {} + + for feature_set in os.listdir(feature_sets_path): + if feature_set: + utils.get_resource(os.path.join(feature_set, '__init__'), base_path=feature_sets_path) + + metarigs['external'].update(get_metarigs(feature_sets_path, os.path.join(feature_set, utils.METARIG_DIR))) + + metarig_ops.clear() + armature_submenus.clear() + menu_funcs.clear() + + create_metarig_ops() + create_menu_funcs() + create_armature_submenus() + register() |