diff options
Diffstat (limited to 'rigify/utils/rig.py')
-rw-r--r-- | rigify/utils/rig.py | 185 |
1 files changed, 123 insertions, 62 deletions
diff --git a/rigify/utils/rig.py b/rigify/utils/rig.py index 6340e4d1..d8f88430 100644 --- a/rigify/utils/rig.py +++ b/rigify/utils/rig.py @@ -3,18 +3,27 @@ import bpy import importlib import importlib.util -import os import re from itertools import count +from typing import TYPE_CHECKING, Any, Optional, Sequence, Mapping +from bpy.types import bpy_struct, Constraint, Object, PoseBone, Bone, Armature + +# noinspection PyUnresolvedReferences +from bpy.types import bpy_prop_array + +from .misc import ArmatureObject + +if TYPE_CHECKING: + from ..base_rig import BaseRig + from .. import RigifyColorSet, RigifyArmatureLayer -from bpy.types import bpy_struct, bpy_prop_array, Constraint RIG_DIR = "rigs" # Name of the directory where rig types are kept METARIG_DIR = "metarigs" # Name of the directory where metarigs are kept TEMPLATE_DIR = "ui_templates" # Name of the directory where ui templates are kept - +# noinspection SpellCheckingInspection outdated_types = {"pitchipoy.limbs.super_limb": "limbs.super_limb", "pitchipoy.limbs.super_arm": "limbs.super_limb", "pitchipoy.limbs.super_leg": "limbs.super_limb", @@ -37,16 +46,37 @@ outdated_types = {"pitchipoy.limbs.super_limb": "limbs.super_limb", "spine": "" } -def get_rigify_type(pose_bone): + +def get_rigify_type(pose_bone: PoseBone) -> str: + # noinspection PyUnresolvedReferences return pose_bone.rigify_type.replace(" ", "") -def is_rig_base_bone(obj, name): + +def get_rigify_params(pose_bone: PoseBone) -> Any: + # noinspection PyUnresolvedReferences + return pose_bone.rigify_parameters + + +def get_rigify_colors(arm: Armature) -> Sequence['RigifyColorSet']: + # noinspection PyUnresolvedReferences + return arm.rigify_colors + + +def get_rigify_layers(arm: Armature) -> Sequence['RigifyArmatureLayer']: + # noinspection PyUnresolvedReferences + return arm.rigify_layers + + +def is_rig_base_bone(obj: Object, name): return bool(get_rigify_type(obj.pose.bones[name])) -def upgradeMetarigTypes(metarig, revert=False): - """Replaces rigify_type properties from old versions with their current names - :param revert: revert types to previous version (if old type available) +def upgrade_metarig_types(metarig: Object, revert=False): + """ + Replaces rigify_type properties from old versions with their current names. + + metarig: rig to update. + revert: revert types to previous version (if old type available) """ if revert: @@ -59,22 +89,24 @@ def upgradeMetarigTypes(metarig, revert=False): rig_type = bone.rigify_type if rig_type in rig_defs: bone.rigify_type = rig_defs[rig_type] + + parameters = get_rigify_params(bone) + if 'leg' in rig_type: - bone.rigfy_parameters.limb_type = 'leg' + parameters.limb_type = 'leg' if 'arm' in rig_type: - bone.rigfy_parameters.limb_type = 'arm' + parameters.limb_type = 'arm' if 'paw' in rig_type: - bone.rigfy_parameters.limb_type = 'paw' + parameters.limb_type = 'paw' if rig_type == "basic.copy": - bone.rigify_parameters.make_widget = False + parameters.make_widget = False -#============================================= +############################################## # Misc -#============================================= - +############################################## -def rig_is_child(rig, parent, *, strict=False): +def rig_is_child(rig: 'BaseRig', parent: Optional['BaseRig'], *, strict=False): """ Checks if the rig is a child of the parent. Unless strict is True, returns true if the rig and parent are the same. @@ -94,7 +126,7 @@ def rig_is_child(rig, parent, *, strict=False): return False -def get_parent_rigs(rig): +def get_parent_rigs(rig: 'BaseRig') -> list['BaseRig']: """Returns a list containing the rig and all of its parents.""" result = [] while rig: @@ -106,13 +138,12 @@ def get_parent_rigs(rig): def get_resource(resource_name): """ Fetches a rig module by name, and returns it. """ - module = importlib.import_module(resource_name) importlib.reload(module) return module -def connected_children_names(obj, bone_name): +def connected_children_names(obj: ArmatureObject, bone_name: str) -> list[str]: """ Returns a list of bone names (in order) of the bones that form a single connected chain starting with the given bone as a parent. If there is a connected branch, the list stops there. @@ -138,7 +169,7 @@ def connected_children_names(obj, bone_name): return names -def has_connected_children(bone): +def has_connected_children(bone: Bone): """ Returns true/false whether a bone has connected children or not. """ t = False @@ -147,13 +178,14 @@ def has_connected_children(bone): return t -def _list_bone_names_depth_first_sorted_rec(result_list, bone): +def _list_bone_names_depth_first_sorted_rec(result_list: list[str], bone: Bone): result_list.append(bone.name) for child in sorted(list(bone.children), key=lambda b: b.name): _list_bone_names_depth_first_sorted_rec(result_list, child) -def list_bone_names_depth_first_sorted(obj): + +def list_bone_names_depth_first_sorted(obj: ArmatureObject): """Returns a list of bone names in depth first name sorted order.""" result_list = [] @@ -164,16 +196,25 @@ def list_bone_names_depth_first_sorted(obj): return result_list -def _get_property_value(obj, name): +def _get_property_value(obj, name: str): value = getattr(obj, name, None) if isinstance(value, bpy_prop_array): value = tuple(value) return value -def _generate_properties(lines, prefix, obj, base_class, *, defaults={}, objects={}): - block_props = set(prop.identifier for prop in base_class.bl_rna.properties) - set(defaults.keys()) - for prop in type(obj).bl_rna.properties: +# noinspection PyDefaultArgument +def _generate_properties(lines, prefix, obj: bpy_struct, base_class: type, *, + defaults: dict[str, Any] = {}, + objects: dict[Any, str] = {}): + # noinspection PyUnresolvedReferences + obj_rna: bpy.types.Struct = type(obj).bl_rna + # noinspection PyUnresolvedReferences + base_rna: bpy.types.Struct = base_class.bl_rna + + block_props = set(prop.identifier for prop in base_rna.properties) - set(defaults.keys()) + + for prop in obj_rna.properties: if prop.identifier not in block_props and not prop.is_readonly: cur_value = _get_property_value(obj, prop.identifier) @@ -188,7 +229,7 @@ def _generate_properties(lines, prefix, obj, base_class, *, defaults={}, objects lines.append('%s.%s = %r' % (prefix, prop.identifier, cur_value)) -def write_metarig_widgets(obj): +def write_metarig_widgets(obj: Object): from .widgets import write_widget widget_set = set() @@ -217,15 +258,17 @@ def write_metarig_widgets(obj): return widget_map, code -def write_metarig(obj, layers=False, func_name="create", groups=False, widgets=False): +# noinspection SpellCheckingInspection +def write_metarig(obj: ArmatureObject, layers=False, func_name="create", + groups=False, widgets=False): """ Write a metarig as a python script, this rig is to have all info needed for generating the real rig with rigify. """ - code = [] - - code.append("import bpy\n") - code.append("from mathutils import Color\n") + code = [ + "import bpy\n", + "from mathutils import Color\n", + ] # Widget object creation functions if requested if widgets: @@ -234,6 +277,8 @@ def write_metarig(obj, layers=False, func_name="create", groups=False, widgets=F if widget_map: code.append("from rigify.utils.widgets import widget_generator\n\n") code += widget_code + else: + widget_map = {} # Start of the metarig function code.append("def %s(obj):" % func_name) @@ -245,32 +290,40 @@ def write_metarig(obj, layers=False, func_name="create", groups=False, widgets=F arm = obj.data # Rigify bone group colors info - if groups and len(arm.rigify_colors) > 0: - code.append("\n for i in range(" + str(len(arm.rigify_colors)) + "):") + rigify_colors = get_rigify_colors(arm) + + if groups and len(rigify_colors) > 0: + code.append("\n for i in range(" + str(len(rigify_colors)) + "):") code.append(" arm.rigify_colors.add()\n") - for i in range(len(arm.rigify_colors)): - name = arm.rigify_colors[i].name - active = arm.rigify_colors[i].active - normal = arm.rigify_colors[i].normal - select = arm.rigify_colors[i].select - standard_colors_lock = arm.rigify_colors[i].standard_colors_lock + for i in range(len(rigify_colors)): + name = rigify_colors[i].name + active = rigify_colors[i].active + normal = rigify_colors[i].normal + select = rigify_colors[i].select + standard_colors_lock = rigify_colors[i].standard_colors_lock code.append(' arm.rigify_colors[' + str(i) + '].name = "' + name + '"') - code.append(' arm.rigify_colors[' + str(i) + '].active = Color(' + str(active[:]) + ')') - code.append(' arm.rigify_colors[' + str(i) + '].normal = Color(' + str(normal[:]) + ')') - code.append(' arm.rigify_colors[' + str(i) + '].select = Color(' + str(select[:]) + ')') - code.append(' arm.rigify_colors[' + str(i) + '].standard_colors_lock = ' + str(standard_colors_lock)) + code.append(' arm.rigify_colors[' + str(i) + + '].active = Color(' + str(active[:]) + ')') + code.append(' arm.rigify_colors[' + str(i) + + '].normal = Color(' + str(normal[:]) + ')') + code.append(' arm.rigify_colors[' + str(i) + + '].select = Color(' + str(select[:]) + ')') + code.append(' arm.rigify_colors[' + str(i) + + '].standard_colors_lock = ' + str(standard_colors_lock)) # Rigify layer layout info - if layers and len(arm.rigify_layers) > 0: - code.append("\n for i in range(" + str(len(arm.rigify_layers)) + "):") + rigify_layers = get_rigify_layers(arm) + + if layers and len(rigify_layers) > 0: + code.append("\n for i in range(" + str(len(rigify_layers)) + "):") code.append(" arm.rigify_layers.add()\n") - for i in range(len(arm.rigify_layers)): - name = arm.rigify_layers[i].name - row = arm.rigify_layers[i].row - selset = arm.rigify_layers[i].selset - group = arm.rigify_layers[i].group + for i in range(len(rigify_layers)): + name = rigify_layers[i].name + row = rigify_layers[i].row + selset = rigify_layers[i].selset + group = rigify_layers[i].group code.append(' arm.rigify_layers[' + str(i) + '].name = "' + name + '"') code.append(' arm.rigify_layers[' + str(i) + '].row = ' + str(row)) code.append(' arm.rigify_layers[' + str(i) + '].selset = ' + str(selset)) @@ -307,8 +360,11 @@ def write_metarig(obj, layers=False, func_name="create", groups=False, widgets=F for bone_name in bones: pbone = obj.pose.bones[bone_name] + rigify_type = get_rigify_type(pbone) + rigify_parameters = get_rigify_params(pbone) + code.append(" pbone = obj.pose.bones[bones[%r]]" % bone_name) - code.append(" pbone.rigify_type = %r" % pbone.rigify_type) + code.append(" pbone.rigify_type = %r" % rigify_type) code.append(" pbone.lock_location = %s" % str(tuple(pbone.lock_location))) code.append(" pbone.lock_rotation = %s" % str(tuple(pbone.lock_rotation))) code.append(" pbone.lock_rotation_w = %s" % str(pbone.lock_rotation_w)) @@ -316,9 +372,10 @@ def write_metarig(obj, layers=False, func_name="create", groups=False, widgets=F code.append(" pbone.rotation_mode = %r" % pbone.rotation_mode) if layers: code.append(" pbone.bone.layers = %s" % str(list(pbone.bone.layers))) + # Rig type parameters - for param_name in pbone.rigify_parameters.keys(): - param = getattr(pbone.rigify_parameters, param_name, '') + for param_name in rigify_parameters.keys(): + param = getattr(rigify_parameters, param_name, '') if str(type(param)) == "<class 'bpy_prop_array'>": param = list(param) if type(param) == str: @@ -327,17 +384,18 @@ def write_metarig(obj, layers=False, func_name="create", groups=False, widgets=F code.append(" pbone.rigify_parameters.%s = %s" % (param_name, str(param))) code.append(" except AttributeError:") code.append(" pass") + # Constraints for con in pbone.constraints: - code.append(" con = pbone.constraints.new(%r)" % (con.type)) - code.append(" con.name = %r" % (con.name)) + code.append(" con = pbone.constraints.new(%r)" % con.type) + code.append(" con.name = %r" % con.name) # Add target first because of target_space handling if con.type == 'ARMATURE': for tgt in con.targets: code.append(" tgt = con.targets.new()") code.append(" tgt.target = obj") - code.append(" tgt.subtarget = %r" % (tgt.subtarget)) - code.append(" tgt.weight = %.3f" % (tgt.weight)) + code.append(" tgt.subtarget = %r" % tgt.subtarget) + code.append(" tgt.weight = %.3f" % tgt.weight) elif getattr(con, 'target', None) == obj: code.append(" con.target = obj") # Generic properties @@ -353,9 +411,11 @@ def write_metarig(obj, layers=False, func_name="create", groups=False, widgets=F # Custom widgets if widgets and pbone.custom_shape: widget_id = widget_map[pbone.custom_shape] - code.append(" if %r not in widget_map:" % (widget_id)) - code.append(" widget_map[%r] = create_%s_widget(obj, pbone.name, widget_name=%r, widget_force_new=True)" % (widget_id, widget_id, pbone.custom_shape.name)) - code.append(" pbone.custom_shape = widget_map[%r]" % (widget_id)) + code.append(" if %r not in widget_map:" % widget_id) + code.append((" widget_map[%r] = create_%s_widget(obj, pbone.name, " + "widget_name=%r, widget_force_new=True)") + % (widget_id, widget_id, pbone.custom_shape.name)) + code.append(" pbone.custom_shape = widget_map[%r]" % widget_id) code.append("\n bpy.ops.object.mode_set(mode='EDIT')") code.append(" for bone in arm.edit_bones:") @@ -383,7 +443,8 @@ def write_metarig(obj, layers=False, func_name="create", groups=False, widgets=F active_layers.append(i) active_layers.sort() - code.append("\n arm.layers = [(x in " + str(active_layers) + ") for x in range(" + str(len(arm.layers)) + ")]") + code.append("\n arm.layers = [(x in " + str(active_layers) + + ") for x in range(" + str(len(arm.layers)) + ")]") code.append("\n return bones") |