diff options
Diffstat (limited to 'rigify/generate.py')
-rw-r--r-- | rigify/generate.py | 967 |
1 files changed, 428 insertions, 539 deletions
diff --git a/rigify/generate.py b/rigify/generate.py index 22769a41..bebe4fc3 100644 --- a/rigify/generate.py +++ b/rigify/generate.py @@ -24,24 +24,21 @@ import time import traceback import sys from rna_prop_ui import rna_idprop_ui_prop_get -from collections import OrderedDict -from .utils import MetarigError, new_bone -from .utils import MCH_PREFIX, DEF_PREFIX, WGT_PREFIX, ROOT_NAME, make_original_name -from .utils import create_root_widget +from .utils.errors import MetarigError +from .utils.bones import new_bone +from .utils.layers import ORG_LAYER, MCH_LAYER, DEF_LAYER, ROOT_LAYER +from .utils.naming import ORG_PREFIX, MCH_PREFIX, DEF_PREFIX, ROOT_NAME, make_original_name +from .utils.widgets import WGT_PREFIX +from .utils.widgets_special import create_root_widget +from .utils.misc import copy_attributes, gamma_correct, select_object from .utils.collections import ensure_widget_collection, list_layer_collections, filter_layer_collections_by_object -from .utils import random_id -from .utils import copy_attributes -from .utils import gamma_correct -from . import rig_lists + +from . import base_generate from . import rig_ui_template +from . import rig_lists RIG_MODULE = "rigs" -ORG_LAYER = [n == 31 for n in range(0, 32)] # Armature layer that original bones should be moved to. -MCH_LAYER = [n == 30 for n in range(0, 32)] # Armature layer that mechanism bones should be moved to. -DEF_LAYER = [n == 29 for n in range(0, 32)] # Armature layer that deformation bones should be moved to. -ROOT_LAYER = [n == 28 for n in range(0, 32)] # Armature layer that root bone should be moved to. - class Timer: def __init__(self): @@ -53,512 +50,462 @@ class Timer: self.timez = t -# TODO: generalize to take a group as input instead of an armature. -def generate_rig(context, metarig): - """ Generates a rig from a metarig. - - """ - t = Timer() +class Generator(base_generate.BaseGenerator): + def __init__(self, context, metarig): + super().__init__(context, metarig) - # Random string with time appended so that - # different rigs don't collide id's - rig_id = random_id(16) + self.id_store = context.window_manager - # Initial configuration - # mode_orig = context.mode # UNUSED - rest_backup = metarig.data.pose_position - metarig.data.pose_position = 'REST' + self.rig_new_name = "" + self.rig_old_name = "" - bpy.ops.object.mode_set(mode='OBJECT') - scene = context.scene - view_layer = context.view_layer - layer_collection = context.layer_collection - id_store = context.window_manager + def find_rig_class(self, rig_type): + rig_module = rig_lists.rigs[rig_type]["module"] - usable_collections = list_layer_collections(view_layer.layer_collection, selectable=True) + return rig_module.Rig - if layer_collection not in usable_collections: - metarig_collections = filter_layer_collections_by_object(usable_collections, metarig) - layer_collection = (metarig_collections + [view_layer.layer_collection])[0] - collection = layer_collection.collection + def __create_rig_object(self): + scene = self.scene + id_store = self.id_store - #------------------------------------------ - # Create/find the rig object and set it up + # Check if the generated rig already exists, so we can + # regenerate in the same object. If not, create a new + # object to generate the rig in. + print("Fetch rig.") - # Check if the generated rig already exists, so we can - # regenerate in the same object. If not, create a new - # object to generate the rig in. - print("Fetch rig.") + if id_store.rigify_generate_mode == 'overwrite': + name = id_store.rigify_target_rig or "rig" + try: + obj = scene.objects[name] + self.rig_old_name = name + obj.name = self.rig_new_name or name - rig_new_name = "" - rig_old_name = "" - if id_store.rigify_rig_basename: - rig_new_name = id_store.rigify_rig_basename + "_rig" + rig_collections = filter_layer_collections_by_object(self.usable_collections, obj) + self.layer_collection = (rig_collections + [self.layer_collection])[0] + self.collection = self.layer_collection.collection - if id_store.rigify_generate_mode == 'overwrite': - name = id_store.rigify_target_rig or "rig" - try: - obj = scene.objects[name] - rig_old_name = name - obj.name = rig_new_name or name - - rig_collections = filter_layer_collections_by_object(usable_collections, obj) - layer_collection = (rig_collections + [layer_collection])[0] - collection = layer_collection.collection - - except KeyError: - rig_old_name = name - name = rig_new_name or name - obj = bpy.data.objects.new(name, bpy.data.armatures.new(name)) + except KeyError: + self.rig_old_name = name + name = self.rig_new_name or name + obj = bpy.data.objects.new(name, bpy.data.armatures.new(name)) + obj.display_type = 'WIRE' + self.collection.objects.link(obj) + else: + name = self.rig_new_name or "rig" + obj = bpy.data.objects.new(name, bpy.data.armatures.new(name)) # in case name 'rig' exists it will be rig.001 obj.display_type = 'WIRE' - collection.objects.link(obj) - else: - name = rig_new_name or "rig" - obj = bpy.data.objects.new(name, bpy.data.armatures.new(name)) # in case name 'rig' exists it will be rig.001 - obj.display_type = 'WIRE' - collection.objects.link(obj) + self.collection.objects.link(obj) - id_store.rigify_target_rig = obj.name - obj.data.pose_position = 'POSE' + id_store.rigify_target_rig = obj.name + obj.data.pose_position = 'POSE' - # Get rid of anim data in case the rig already existed - print("Clear rig animation data.") - obj.animation_data_clear() - obj.data.animation_data_clear() + self.obj = obj + return obj - # Select generated rig object - metarig.select_set(False) - obj.select_set(True) - view_layer.objects.active = obj - # Remove wgts if force update is set - wgts_group_name = "WGTS_" + (rig_old_name or obj.name) - if wgts_group_name in scene.objects and id_store.rigify_force_widget_update: + def __create_widget_group(self, new_group_name): + context = self.context + scene = self.scene + id_store = self.id_store + + # Create/find widge collection + self.widget_collection = ensure_widget_collection(context) + + # Remove wgts if force update is set + wgts_group_name = "WGTS_" + (self.rig_old_name or obj.name) + if wgts_group_name in scene.objects and id_store.rigify_force_widget_update: + bpy.ops.object.mode_set(mode='OBJECT') + bpy.ops.object.select_all(action='DESELECT') + for wgt in bpy.data.objects[wgts_group_name].children: + wgt.select_set(True) + bpy.ops.object.delete(use_global=False) + if self.rig_old_name: + bpy.data.objects[wgts_group_name].name = new_group_name + + # Create Group widget + wgts_group_name = new_group_name + if wgts_group_name not in scene.objects: + if wgts_group_name in bpy.data.objects: + bpy.data.objects[wgts_group_name].user_clear() + bpy.data.objects.remove(bpy.data.objects[wgts_group_name]) + mesh = bpy.data.meshes.new(wgts_group_name) + wgts_obj = bpy.data.objects.new(wgts_group_name, mesh) + self.widget_collection.objects.link(wgts_obj) + + self.wgts_group_name = new_group_name + + + def __duplicate_rig(self): + obj = self.obj + metarig = self.metarig + context = self.context + + # Remove all bones from the generated rig armature. + bpy.ops.object.mode_set(mode='EDIT') + for bone in obj.data.edit_bones: + obj.data.edit_bones.remove(bone) bpy.ops.object.mode_set(mode='OBJECT') - bpy.ops.object.select_all(action='DESELECT') - for wgt in bpy.data.objects[wgts_group_name].children: - wgt.select_set(True) - bpy.ops.object.delete(use_global=False) - if rig_old_name: - bpy.data.objects[wgts_group_name].name = "WGTS_" + obj.name - - wgts_group_name = "WGTS_" + obj.name - - # Get parented objects to restore later - childs = {} # {object: bone} - for child in obj.children: - childs[child] = child.parent_bone - - # Remove all bones from the generated rig armature. - bpy.ops.object.mode_set(mode='EDIT') - for bone in obj.data.edit_bones: - obj.data.edit_bones.remove(bone) - bpy.ops.object.mode_set(mode='OBJECT') - # Create temporary duplicates for merging - temp_rig_1 = metarig.copy() - temp_rig_1.data = metarig.data.copy() - collection.objects.link(temp_rig_1) + # Select and duplicate metarig + select_object(context, metarig, deselect_all=True) - temp_rig_2 = metarig.copy() - temp_rig_2.data = obj.data - collection.objects.link(temp_rig_2) + bpy.ops.object.duplicate() - # Select the temp rigs for merging - for objt in view_layer.objects: - objt.select_set(False) # deselect all objects - temp_rig_1.select_set(True) - temp_rig_2.select_set(True) - view_layer.objects.active = temp_rig_2 + # Select the target rig and join + select_object(context, obj) - # Merge the temporary rigs - bpy.ops.object.join() + bpy.ops.object.join() - # Delete the second temp rig - bpy.ops.object.delete() + # Select the generated rig + select_object(context, obj, deselect_all=True) + + # Clean up animation data + if obj.animation_data: + obj.animation_data.action = None + + for track in obj.animation_data.nla_tracks: + obj.animation_data.nla_tracks.remove(track) + + # Freeze drivers referring to custom properties + for d in obj.animation_data.drivers: + for var in d.driver.variables: + for tar in var.targets: + # If a custom property + if var.type == 'SINGLE_PROP' \ + and re.match('^pose.bones\["[^"\]]*"\]\["[^"\]]*"\]$', tar.data_path): + tar.data_path = "RIGIFY-" + tar.data_path + + + def __rename_org_bones(self): + obj = self.obj + + #---------------------------------- + # Make a list of the original bones so we can keep track of them. + original_bones = [bone.name for bone in obj.data.bones] + + # Add the ORG_PREFIX to the original bones. + for i in range(0, len(original_bones)): + new_name = make_original_name(original_bones[i]) + obj.data.bones[original_bones[i]].name = new_name + original_bones[i] = new_name + + self.original_bones = original_bones + + + def __create_root_bone(self): + obj = self.obj + metarig = self.metarig + + #---------------------------------- + # Create the root bone. + root_bone = new_bone(obj, ROOT_NAME) + spread = get_xy_spread(metarig.data.bones) or metarig.data.bones[0].length + spread = float('%.3g' % spread) + scale = spread/0.589 + obj.data.edit_bones[root_bone].head = (0, 0, 0) + obj.data.edit_bones[root_bone].tail = (0, scale, 0) + obj.data.edit_bones[root_bone].roll = 0 + self.root_bone = root_bone + self.bone_owners[root_bone] = None + + + def __parent_bones_to_root(self): + eb = self.obj.data.edit_bones + + # Parent loose bones to root + for bone in eb: + if bone.name in self.noparent_bones: + continue + elif bone.parent is None: + bone.use_connect = False + bone.parent = eb[self.root_bone] + + + def __lock_transforms(self): + # Lock transforms on all non-control bones + r = re.compile("[A-Z][A-Z][A-Z]-") + for pb in self.obj.pose.bones: + if r.match(pb.name): + pb.lock_location = (True, True, True) + pb.lock_rotation = (True, True, True) + pb.lock_rotation_w = True + pb.lock_scale = (True, True, True) + + + def __assign_layers(self): + bones = self.obj.data.bones + + bones[self.root_bone].layers = ROOT_LAYER + + # Every bone that has a name starting with "DEF-" make deforming. All the + # others make non-deforming. + for bone in bones: + name = bone.name + + bone.use_deform = name.startswith(DEF_PREFIX) + + # Move all the original bones to their layer. + if name.startswith(ORG_PREFIX): + bone.layers = ORG_LAYER + # Move all the bones with names starting with "MCH-" to their layer. + elif name.startswith(MCH_PREFIX): + bone.layers = MCH_LAYER + # Move all the bones with names starting with "DEF-" to their layer. + elif name.startswith(DEF_PREFIX): + bone.layers = DEF_LAYER + + + def __restore_driver_vars(self): + obj = self.obj + + # Alter marked driver targets + if obj.animation_data: + for d in obj.animation_data.drivers: + for v in d.driver.variables: + for tar in v.targets: + if tar.data_path.startswith("RIGIFY-"): + temp, bone, prop = tuple([x.strip('"]') for x in tar.data_path.split('["')]) + if bone in obj.data.bones \ + and prop in obj.pose.bones[bone].keys(): + tar.data_path = tar.data_path[7:] + else: + tar.data_path = 'pose.bones["%s"]["%s"]' % (make_original_name(bone), prop) + + + def __assign_widgets(self): + obj_table = {obj.name: obj for obj in self.scene.objects} + + # Assign shapes to bones + # Object's with name WGT-<bone_name> get used as that bone's shape. + for bone in self.obj.pose.bones: + # Object names are limited to 63 characters... arg + wgt_name = (WGT_PREFIX + self.obj.name + '_' + bone.name)[:63] + + if wgt_name in obj_table: + bone.custom_shape = obj_table[wgt_name] + + + def __compute_visible_layers(self): + # Reveal all the layers with control bones on them + vis_layers = [False for n in range(0, 32)] + + for bone in self.obj.data.bones: + for i in range(0, 32): + vis_layers[i] = vis_layers[i] or bone.layers[i] + + for i in range(0, 32): + vis_layers[i] = vis_layers[i] and not (ORG_LAYER[i] or MCH_LAYER[i] or DEF_LAYER[i]) + + self.obj.data.layers = vis_layers + + + def generate(self): + context = self.context + metarig = self.metarig + scene = self.scene + id_store = self.id_store + view_layer = self.view_layer + t = Timer() + + self.usable_collections = list_layer_collections(view_layer.layer_collection, selectable=True) + + if self.layer_collection not in self.usable_collections: + metarig_collections = filter_layer_collections_by_object(self.usable_collections, self.metarig) + self.layer_collection = (metarig_collections + [view_layer.layer_collection])[0] + self.collection = self.layer_collection.collection + + bpy.ops.object.mode_set(mode='OBJECT') + + #------------------------------------------ + # Create/find the rig object and set it up + if id_store.rigify_rig_basename: + self.rig_new_name = id_store.rigify_rig_basename + "_rig" + + obj = self.__create_rig_object() + + # Get rid of anim data in case the rig already existed + print("Clear rig animation data.") + + obj.animation_data_clear() + obj.data.animation_data_clear() + + select_object(context, obj, deselect_all=True) + + #------------------------------------------ + # Create Group widget + self.__create_widget_group("WGTS_" + obj.name) - # Select the generated rig - for objt in view_layer.objects: - objt.select_set(False) # deselect all objects - obj.select_set(True) - view_layer.objects.active = obj - - # Copy over bone properties - for bone in metarig.data.bones: - bone_gen = obj.data.bones[bone.name] - - # B-bone stuff - bone_gen.bbone_segments = bone.bbone_segments - bone_gen.bbone_easein = bone.bbone_easein - bone_gen.bbone_easeout = bone.bbone_easeout - - # Copy over the pose_bone properties - for bone in metarig.pose.bones: - bone_gen = obj.pose.bones[bone.name] - - # Rotation mode and transform locks - bone_gen.rotation_mode = bone.rotation_mode - bone_gen.lock_rotation = tuple(bone.lock_rotation) - bone_gen.lock_rotation_w = bone.lock_rotation_w - bone_gen.lock_rotations_4d = bone.lock_rotations_4d - bone_gen.lock_location = tuple(bone.lock_location) - bone_gen.lock_scale = tuple(bone.lock_scale) - - # rigify_type and rigify_parameters - bone_gen.rigify_type = bone.rigify_type - for prop in dir(bone_gen.rigify_parameters): - if (not prop.startswith("_")) \ - and (not prop.startswith("bl_")) \ - and (prop != "rna_type"): - try: - setattr(bone_gen.rigify_parameters, prop, \ - getattr(bone.rigify_parameters, prop)) - except AttributeError: - print("FAILED TO COPY PARAMETER: " + str(prop)) - - # Custom properties - for prop in bone.keys(): - try: - bone_gen[prop] = bone[prop] - except KeyError: - pass - - # Constraints - for con1 in bone.constraints: - con2 = bone_gen.constraints.new(type=con1.type) - copy_attributes(con1, con2) - - # Set metarig target to rig target - if "target" in dir(con2): - if con2.target == metarig: - con2.target = obj - - # Copy drivers - if metarig.animation_data: - for d1 in metarig.animation_data.drivers: - d2 = obj.driver_add(d1.data_path) - copy_attributes(d1, d2) - copy_attributes(d1.driver, d2.driver) - - # Remove default modifiers, variables, etc. - for m in d2.modifiers: - d2.modifiers.remove(m) - for v in d2.driver.variables: - d2.driver.variables.remove(v) - - # Copy modifiers - for m1 in d1.modifiers: - m2 = d2.modifiers.new(type=m1.type) - copy_attributes(m1, m2) - - # Copy variables - for v1 in d1.driver.variables: - v2 = d2.driver.variables.new() - copy_attributes(v1, v2) - for i in range(len(v1.targets)): - copy_attributes(v1.targets[i], v2.targets[i]) - # Switch metarig targets to rig targets - if v2.targets[i].id == metarig: - v2.targets[i].id = obj - - # Mark targets that may need to be altered after rig generation - tar = v2.targets[i] - # If a custom property - if v2.type == 'SINGLE_PROP' \ - and re.match('^pose.bones\["[^"\]]*"\]\["[^"\]]*"\]$', tar.data_path): - tar.data_path = "RIGIFY-" + tar.data_path - - # Copy key frames - for i in range(len(d1.keyframe_points)): - d2.keyframe_points.add() - k1 = d1.keyframe_points[i] - k2 = d2.keyframe_points[i] - copy_attributes(k1, k2) - - t.tick("Duplicate rig: ") - #---------------------------------- - # Make a list of the original bones so we can keep track of them. - original_bones = [bone.name for bone in obj.data.bones] - - # Add the ORG_PREFIX to the original bones. - bpy.ops.object.mode_set(mode='OBJECT') - for i in range(0, len(original_bones)): - obj.data.bones[original_bones[i]].name = make_original_name(original_bones[i]) - original_bones[i] = make_original_name(original_bones[i]) - - # Create a sorted list of the original bones, sorted in the order we're - # going to traverse them for rigging. - # (root-most -> leaf-most, alphabetical) - bones_sorted = [] - for name in original_bones: - bones_sorted += [name] - bones_sorted.sort() # first sort by names - bones_sorted.sort(key=lambda bone: len(obj.pose.bones[bone].parent_recursive)) # then parents before children - - t.tick("Make list of org bones: ") - #---------------------------------- - # Create the root bone. - bpy.ops.object.mode_set(mode='EDIT') - root_bone = new_bone(obj, ROOT_NAME) - spread = get_xy_spread(metarig.data.bones) or metarig.data.bones[0].length - spread = float('%.3g' % spread) - scale = spread/0.589 - obj.data.edit_bones[root_bone].head = (0, 0, 0) - obj.data.edit_bones[root_bone].tail = (0, scale, 0) - obj.data.edit_bones[root_bone].roll = 0 - bpy.ops.object.mode_set(mode='OBJECT') - obj.data.bones[root_bone].layers = ROOT_LAYER - - # Put the rig_name in the armature custom properties - rna_idprop_ui_prop_get(obj.data, "rig_id", create=True) - obj.data["rig_id"] = rig_id - - t.tick("Create root bone: ") - - # Create/find widge collection - widget_collection = ensure_widget_collection(context) - - # Create Group widget - # wgts_group_name = "WGTS" - if wgts_group_name not in scene.objects: - if wgts_group_name in bpy.data.objects: - bpy.data.objects[wgts_group_name].user_clear() - bpy.data.objects.remove(bpy.data.objects[wgts_group_name]) - mesh = bpy.data.meshes.new(wgts_group_name) - wgts_obj = bpy.data.objects.new(wgts_group_name, mesh) - widget_collection.objects.link(wgts_obj) t.tick("Create main WGTS: ") - # - # if id_store.rigify_generate_mode == 'new': - # bpy.ops.object.select_all(action='DESELECT') - # for wgt in bpy.data.objects[wgts_group_name].children: - # wgt.select_set(True) - # bpy.ops.object.make_single_user(obdata=True) - - #---------------------------------- - try: - # Collect/initialize all the rigs. - rigs = [] - for bone in bones_sorted: - bpy.ops.object.mode_set(mode='EDIT') - rigs += get_bone_rigs(obj, bone) + + #------------------------------------------ + # Get parented objects to restore later + childs = {} # {object: bone} + for child in obj.children: + childs[child] = child.parent_bone + + #------------------------------------------ + # Copy bones from metarig to obj + self.__duplicate_rig() + + t.tick("Duplicate rig: ") + + #------------------------------------------ + # Add the ORG_PREFIX to the original bones. + bpy.ops.object.mode_set(mode='OBJECT') + + self.__rename_org_bones() + + t.tick("Make list of org bones: ") + + #------------------------------------------ + # Put the rig_name in the armature custom properties + rna_idprop_ui_prop_get(obj.data, "rig_id", create=True) + obj.data["rig_id"] = self.rig_id + + self.script = rig_ui_template.ScriptGenerator(self) + + #------------------------------------------ + bpy.ops.object.mode_set(mode='OBJECT') + + self.instantiate_rig_tree() + + t.tick("Instantiate rigs: ") + + #------------------------------------------ + bpy.ops.object.mode_set(mode='OBJECT') + + self.invoke_initialize() + t.tick("Initialize rigs: ") - # Generate all the rigs. - ui_scripts = [] - ui_imports = rig_ui_template.UI_IMPORTS.copy() - ui_utilities = rig_ui_template.UI_UTILITIES.copy() - ui_register = rig_ui_template.UI_REGISTER.copy() - noparent_bones = [] - for rig in rigs: - # Go into editmode in the rig armature - bpy.ops.object.mode_set(mode='OBJECT') - context.view_layer.objects.active = obj - obj.select_set(True) - bpy.ops.object.mode_set(mode='EDIT') - scripts = rig.generate() - if isinstance(scripts, dict): - if 'script' in scripts: - ui_scripts += scripts['script'] - if 'imports' in scripts: - ui_imports += scripts['imports'] - if 'utilities' in scripts: - ui_utilities += scripts['utilities'] - if 'register' in scripts: - ui_register += scripts['register'] - if 'noparent_bones' in scripts: - noparent_bones += scripts['noparent_bones'] - elif scripts is not None: - ui_scripts += [scripts[0]] - t.tick("Generate rigs: ") + #------------------------------------------ + bpy.ops.object.mode_set(mode='EDIT') - except Exception as e: - # Cleanup if something goes wrong - print("Rigify: failed to generate rig.") - metarig.data.pose_position = rest_backup - obj.data.pose_position = 'POSE' + self.invoke_prepare_bones() + + t.tick("Prepare bones: ") + + #------------------------------------------ bpy.ops.object.mode_set(mode='OBJECT') + bpy.ops.object.mode_set(mode='EDIT') - # Continue the exception - raise e + self.__create_root_bone() - #---------------------------------- - bpy.ops.object.mode_set(mode='OBJECT') + self.invoke_generate_bones() - # Get a list of all the bones in the armature - bones = [bone.name for bone in obj.data.bones] + t.tick("Generate bones: ") - # Parent any free-floating bones to the root excluding noparent_bones - noparent_bones = dict.fromkeys(noparent_bones) + #------------------------------------------ + bpy.ops.object.mode_set(mode='OBJECT') + bpy.ops.object.mode_set(mode='EDIT') - bpy.ops.object.mode_set(mode='EDIT') - for bone in bones: - if bone in noparent_bones: - continue - elif obj.data.edit_bones[bone].parent is None: - obj.data.edit_bones[bone].use_connect = False - obj.data.edit_bones[bone].parent = obj.data.edit_bones[root_bone] + self.invoke_parent_bones() - bpy.ops.object.mode_set(mode='OBJECT') + self.__parent_bones_to_root() - # Lock transforms on all non-control bones - r = re.compile("[A-Z][A-Z][A-Z]-") - for bone in bones: - if r.match(bone): - pb = obj.pose.bones[bone] - pb.lock_location = (True, True, True) - pb.lock_rotation = (True, True, True) - pb.lock_rotation_w = True - pb.lock_scale = (True, True, True) - - # Every bone that has a name starting with "DEF-" make deforming. All the - # others make non-deforming. - for bone in bones: - if obj.data.bones[bone].name.startswith(DEF_PREFIX): - obj.data.bones[bone].use_deform = True - else: - obj.data.bones[bone].use_deform = False - - # Alter marked driver targets - if obj.animation_data: - for d in obj.animation_data.drivers: - for v in d.driver.variables: - for tar in v.targets: - if tar.data_path.startswith("RIGIFY-"): - temp, bone, prop = tuple([x.strip('"]') for x in tar.data_path.split('["')]) - if bone in obj.data.bones \ - and prop in obj.pose.bones[bone].keys(): - tar.data_path = tar.data_path[7:] - else: - tar.data_path = 'pose.bones["%s"]["%s"]' % (make_original_name(bone), prop) - - # Move all the original bones to their layer. - for bone in original_bones: - obj.data.bones[bone].layers = ORG_LAYER - - # Move all the bones with names starting with "MCH-" to their layer. - for bone in bones: - if obj.data.bones[bone].name.startswith(MCH_PREFIX): - obj.data.bones[bone].layers = MCH_LAYER - - # Move all the bones with names starting with "DEF-" to their layer. - for bone in bones: - if obj.data.bones[bone].name.startswith(DEF_PREFIX): - obj.data.bones[bone].layers = DEF_LAYER - - # Create root bone widget - create_root_widget(obj, "root") - - # Assign shapes to bones - # Object's with name WGT-<bone_name> get used as that bone's shape. - for bone in bones: - wgt_name = (WGT_PREFIX + obj.name + '_' + obj.data.bones[bone].name)[:63] # Object names are limited to 63 characters... arg - if wgt_name in context.scene.objects: - # Weird temp thing because it won't let me index by object name - for ob in context.scene.objects: - if ob.name == wgt_name: - obj.pose.bones[bone].custom_shape = ob - break - # This is what it should do: - # obj.pose.bones[bone].custom_shape = context.scene.objects[wgt_name] - # Reveal all the layers with control bones on them - vis_layers = [False for n in range(0, 32)] - for bone in bones: - for i in range(0, 32): - vis_layers[i] = vis_layers[i] or obj.data.bones[bone].layers[i] - for i in range(0, 32): - vis_layers[i] = vis_layers[i] and not (ORG_LAYER[i] or MCH_LAYER[i] or DEF_LAYER[i]) - obj.data.layers = vis_layers - - # Ensure the collection of layer names exists - for i in range(1 + len(metarig.data.rigify_layers), 29): - metarig.data.rigify_layers.add() - - # Create list of layer name/row pairs - layer_layout = [] - for l in metarig.data.rigify_layers: - print(l.name) - layer_layout += [(l.name, l.row)] - - # Generate the UI script - if id_store.rigify_generate_mode == 'overwrite': - rig_ui_name = id_store.rigify_rig_ui or 'rig_ui.py' - else: - rig_ui_name = 'rig_ui.py' + t.tick("Parent bones: ") - if id_store.rigify_generate_mode == 'overwrite' and rig_ui_name in bpy.data.texts.keys(): - script = bpy.data.texts[rig_ui_name] - script.clear() - else: - script = bpy.data.texts.new("rig_ui.py") - - rig_ui_old_name = "" - if id_store.rigify_rig_basename: - rig_ui_old_name = script.name - script.name = id_store.rigify_rig_basename + "_rig_ui.py" - - id_store.rigify_rig_ui = script.name - - for s in OrderedDict.fromkeys(ui_imports): - script.write(s + "\n") - script.write(rig_ui_template.UI_BASE_UTILITIES % rig_id) - for s in OrderedDict.fromkeys(ui_utilities): - script.write(s + "\n") - script.write(rig_ui_template.UI_SLIDERS) - for s in ui_scripts: - script.write("\n " + s.replace("\n", "\n ") + "\n") - script.write(rig_ui_template.layers_ui(vis_layers, layer_layout)) - script.write("\ndef register():\n") - ui_register = OrderedDict.fromkeys(ui_register) - for s in ui_register: - script.write(" bpy.utils.register_class("+s+");\n") - script.write("\ndef unregister():\n") - for s in ui_register: - script.write(" bpy.utils.unregister_class("+s+");\n") - script.write("\nregister()\n") - script.use_module = True - - # Run UI script - exec(script.as_string(), {}) - - # Create Selection Sets - create_selection_sets(obj, metarig) - - # Create Bone Groups - create_bone_groups(obj, metarig) - - # Add rig_ui to logic - create_persistent_rig_ui(obj, script) - - # Do final gluing - for rig in rigs: - if hasattr(rig, "glue"): - # update glue_bone rigs - bpy.ops.object.mode_set(mode='EDIT') - rig = rig.__class__(rig.obj, rig.base_bone, rig.params) - - rig.glue() - t.tick("Glue pass") - - t.tick("The rest: ") - #---------------------------------- - # Deconfigure - bpy.ops.object.mode_set(mode='OBJECT') - metarig.data.pose_position = rest_backup - obj.data.pose_position = 'POSE' + #------------------------------------------ + bpy.ops.object.mode_set(mode='OBJECT') + + self.invoke_configure_bones() + + t.tick("Configure bones: ") + + #------------------------------------------ + bpy.ops.object.mode_set(mode='EDIT') + + self.invoke_apply_bones() + + t.tick("Apply bones: ") + + #------------------------------------------ + bpy.ops.object.mode_set(mode='OBJECT') + + self.invoke_rig_bones() + + t.tick("Rig bones: ") + + #------------------------------------------ + bpy.ops.object.mode_set(mode='OBJECT') + + create_root_widget(obj, "root") + + self.invoke_generate_widgets() + + t.tick("Generate widgets: ") + + #------------------------------------------ + bpy.ops.object.mode_set(mode='OBJECT') + + self.__lock_transforms() + self.__assign_layers() + self.__compute_visible_layers() + self.__restore_driver_vars() + + t.tick("Assign layers: ") + + #------------------------------------------ + bpy.ops.object.mode_set(mode='OBJECT') + + self.invoke_finalize() + + t.tick("Finalize: ") + + #------------------------------------------ + bpy.ops.object.mode_set(mode='OBJECT') - # Restore parent to bones - for child, sub_parent in childs.items(): - if sub_parent in obj.pose.bones: - mat = child.matrix_world.copy() - child.parent_bone = sub_parent - child.matrix_world = mat + self.__assign_widgets() - #---------------------------------- - # Restore active collection - view_layer.active_layer_collection = layer_collection + # Create Selection Sets + create_selection_sets(obj, metarig) + + # Create Bone Groups + create_bone_groups(obj, metarig) + + t.tick("The rest: ") + + #---------------------------------- + # Deconfigure + bpy.ops.object.mode_set(mode='OBJECT') + obj.data.pose_position = 'POSE' + + # Restore parent to bones + for child, sub_parent in childs.items(): + if sub_parent in obj.pose.bones: + mat = child.matrix_world.copy() + child.parent_bone = sub_parent + child.matrix_world = mat + + #---------------------------------- + # Restore active collection + view_layer.active_layer_collection = self.layer_collection + + +def generate_rig(context, metarig): + """ Generates a rig from a metarig. + + """ + # Initial configuration + rest_backup = metarig.data.pose_position + metarig.data.pose_position = 'REST' + + try: + Generator(context, metarig).generate() + + metarig.data.pose_position = rest_backup + + except Exception as e: + # Cleanup if something goes wrong + print("Rigify: failed to generate rig.") + + bpy.ops.object.mode_set(mode='OBJECT') + metarig.data.pose_position = rest_backup + + # Continue the exception + raise e def create_selection_sets(obj, metarig): @@ -630,64 +577,6 @@ def create_bone_groups(obj, metarig): b.bone_group = obj.pose.bone_groups[name] -def create_persistent_rig_ui(obj, script): - """Make sure the ui script always follows the rig around""" - skip = False - driver = None - - if not obj.animation_data: - obj.animation_data_create() - - for fcurve in obj.animation_data.drivers: - if fcurve.data_path == 'pass_index': - driver = fcurve.driver - for variable in driver.variables: - if variable.name == script.name: - skip = True - break - break - - if not skip: - if not driver: - fcurve = obj.driver_add("pass_index") - driver = fcurve.driver - - variable = driver.variables.new() - variable.name = script.name - variable.targets[0].id_type = 'TEXT' - variable.targets[0].id = script - - -def get_bone_rigs(obj, bone_name, halt_on_missing=False): - """ Fetch all the rigs specified on a bone. - """ - rigs = [] - rig_type = obj.pose.bones[bone_name].rigify_type - rig_type = rig_type.replace(" ", "") - - if rig_type == "": - pass - else: - # Gather parameters - params = obj.pose.bones[bone_name].rigify_parameters - - # Get the rig - try: - rig = rig_lists.rigs[rig_type]["module"] - rig = rig.Rig(obj, bone_name, params) - except (KeyError, ImportError): - message = "Rig Type Missing: python module for type '%s' not found (bone: %s)" % (rig_type, bone_name) - if halt_on_missing: - raise MetarigError(message) - else: - print(message) - print('print_exc():') - traceback.print_exc(file=sys.stdout) - else: - rigs += [rig] - return rigs - - def get_xy_spread(bones): x_max = 0 y_max = 0 |