#====================== BEGIN GPL LICENSE BLOCK ====================== # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # #======================= END GPL LICENSE BLOCK ======================== # import bpy import importlib import importlib.util import os 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 MODULE_NAME = "rigify" # Windows/Mac blender is weird, so __package__ doesn't work outdated_types = {"pitchipoy.limbs.super_limb": "limbs.super_limb", "pitchipoy.limbs.super_arm": "limbs.super_limb", "pitchipoy.limbs.super_leg": "limbs.super_limb", "pitchipoy.limbs.super_front_paw": "limbs.super_limb", "pitchipoy.limbs.super_rear_paw": "limbs.super_limb", "pitchipoy.limbs.super_finger": "limbs.super_finger", "pitchipoy.super_torso_turbo": "spines.super_spine", "pitchipoy.simple_tentacle": "limbs.simple_tentacle", "pitchipoy.super_face": "faces.super_face", "pitchipoy.super_palm": "limbs.super_palm", "pitchipoy.super_copy": "basic.super_copy", "pitchipoy.tentacle": "", "palm": "limbs.super_palm", "basic.copy": "basic.super_copy", "biped.arm": "", "biped.leg": "", "finger": "", "neck_short": "", "misc.delta": "", "spine": "" } 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) """ if revert: vals = list(outdated_types.values()) rig_defs = {v: k for k, v in outdated_types.items() if vals.count(v) == 1} else: rig_defs = outdated_types for bone in metarig.pose.bones: rig_type = bone.rigify_type if rig_type in rig_defs: bone.rigify_type = rig_defs[rig_type] if 'leg' in rig_type: bone.rigfy_parameters.limb_type = 'leg' if 'arm' in rig_type: bone.rigfy_parameters.limb_type = 'arm' if 'paw' in rig_type: bone.rigfy_parameters.limb_type = 'paw' if rig_type == "basic.copy": bone.rigify_parameters.make_widget = False #============================================= # Misc #============================================= def get_rig_type(rig_type, base_path=''): return get_resource(rig_type, base_path=base_path) def get_resource(resource_name, base_path=''): """ Fetches a rig module by name, and returns it. """ if '.' in resource_name: module_subpath = str.join(os.sep, resource_name.split('.')) package = resource_name.split('.')[0] for sub in resource_name.split('.')[1:]: package = '.'.join([package, sub]) submod = importlib.import_module(package) else: module_subpath = resource_name spec = importlib.util.spec_from_file_location(resource_name, os.path.join(base_path, module_subpath + '.py')) submod = importlib.util.module_from_spec(spec) spec.loader.exec_module(submod) return submod def get_metarig_module(metarig_name, path=METARIG_DIR): """ Fetches a rig module by name, and returns it. """ name = ".%s.%s" % (path, metarig_name) submod = importlib.import_module(name, package=MODULE_NAME) importlib.reload(submod) return submod def connected_children_names(obj, bone_name): """ 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. """ bone = obj.data.bones[bone_name] names = [] while True: connects = 0 con_name = "" for child in bone.children: if child.use_connect: connects += 1 con_name = child.name if connects == 1: names += [con_name] bone = obj.data.bones[con_name] else: break return names def has_connected_children(bone): """ Returns true/false whether a bone has connected children or not. """ t = False for b in bone.children: t = t or b.use_connect return t def write_metarig(obj, layers=False, func_name="create", groups=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\n") code.append("from mathutils import Color\n\n") code.append("def %s(obj):" % func_name) code.append(" # generated by rigify.utils.write_metarig") bpy.ops.object.mode_set(mode='EDIT') code.append(" bpy.ops.object.mode_set(mode='EDIT')") code.append(" arm = obj.data") 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)) + "):") 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 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)) # Rigify layer layout info if layers and len(arm.rigify_layers) > 0: code.append("\n for i in range(" + str(len(arm.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 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)) code.append(' arm.rigify_layers[' + str(i) + '].group = ' + str(group)) # write parents first bones = [(len(bone.parent_recursive), bone.name) for bone in arm.edit_bones] bones.sort(key=lambda item: item[0]) bones = [item[1] for item in bones] code.append("\n bones = {}\n") for bone_name in bones: bone = arm.edit_bones[bone_name] code.append(" bone = arm.edit_bones.new(%r)" % bone.name) code.append(" bone.head[:] = %.4f, %.4f, %.4f" % bone.head.to_tuple(4)) code.append(" bone.tail[:] = %.4f, %.4f, %.4f" % bone.tail.to_tuple(4)) code.append(" bone.roll = %.4f" % bone.roll) code.append(" bone.use_connect = %s" % str(bone.use_connect)) if bone.parent: code.append(" bone.parent = arm.edit_bones[bones[%r]]" % bone.parent.name) code.append(" bones[%r] = bone.name" % bone.name) bpy.ops.object.mode_set(mode='OBJECT') code.append("") code.append(" bpy.ops.object.mode_set(mode='OBJECT')") # Rig type and other pose properties for bone_name in bones: pbone = obj.pose.bones[bone_name] code.append(" pbone = obj.pose.bones[bones[%r]]" % bone_name) code.append(" pbone.rigify_type = %r" % pbone.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)) code.append(" pbone.lock_scale = %s" % str(tuple(pbone.lock_scale))) 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, '') if str(type(param)) == "": param = list(param) if type(param) == str: param = '"' + param + '"' code.append(" try:") code.append(" pbone.rigify_parameters.%s = %s" % (param_name, str(param))) code.append(" except AttributeError:") code.append(" pass") code.append("\n bpy.ops.object.mode_set(mode='EDIT')") code.append(" for bone in arm.edit_bones:") code.append(" bone.select = False") code.append(" bone.select_head = False") code.append(" bone.select_tail = False") code.append(" for b in bones:") code.append(" bone = arm.edit_bones[bones[b]]") code.append(" bone.select = True") code.append(" bone.select_head = True") code.append(" bone.select_tail = True") code.append(" arm.edit_bones.active = bone") # Set appropriate layers visible if layers: # Find what layers have bones on them active_layers = [] for bone_name in bones: bone = obj.data.bones[bone_name] for i in range(len(bone.layers)): if bone.layers[i]: if i not in active_layers: 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)) + ")]") if func_name == "create": active_template = arm.rigify_active_template template_name = arm.rigify_templates[active_template].name code.append("\n # Select proper UI template") code.append(" template_name = '{}'".format(template_name)) code.append(" arm_templates = arm.rigify_templates.items()") code.append(" template_index = None") code.append(" for i, template in enumerate(arm_templates):") code.append(" if template[0] == template_name:") code.append(" template_index = i") code.append(" break") code.append(" if template_index is None:") code.append(" template_index = 0 # Default to something...") code.append(" else:") code.append(" arm.rigify_active_template = template_index") code.append('\nif __name__ == "__main__":') code.append(" " + func_name + "(bpy.context.active_object)\n") return "\n".join(code)