# ##### 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 from rigify import RigifyError from rigify_utils import copy_bone_simple from rna_prop_ui import rna_idprop_ui_prop_get #METARIG_NAMES = ("cpy",) RIG_TYPE = "shape_key_transforms" def addget_shape_key(obj, name="Key"): """ Fetches a shape key, or creates it if it doesn't exist """ # Create a shapekey set if it doesn't already exist if obj.data.shape_keys is None: shape = obj.add_shape_key(name="Basis", from_mix=False) obj.active_shape_key_index = 0 # Get the shapekey, or create it if it doesn't already exist if name in obj.data.shape_keys.keys: shape_key = obj.data.shape_keys.keys[name] else: shape_key = obj.add_shape_key(name=name, from_mix=False) return shape_key def addget_shape_key_driver(obj, name="Key"): """ Fetches the driver for the shape key, or creates it if it doesn't already exist. """ driver_path = 'keys["' + name + '"].value' fcurve = None driver = None new = False if obj.data.shape_keys.animation_data is not None: for driver_s in obj.data.shape_keys.animation_data.drivers: if driver_s.data_path == driver_path: fcurve = driver_s if fcurve == None: fcurve = obj.data.shape_keys.keys[name].driver_add("value", 0) fcurve.driver.type = 'AVERAGE' new = True return fcurve, new # TODO: def metarig_template(): # generated by rigify.write_meta_rig #bpy.ops.object.mode_set(mode='EDIT') #obj = bpy.context.active_object #arm = obj.data #bone = arm.edit_bones.new('Bone') #bone.head[:] = 0.0000, 0.0000, 0.0000 #bone.tail[:] = 0.0000, 0.0000, 1.0000 #bone.roll = 0.0000 #bone.connected = False # #bpy.ops.object.mode_set(mode='OBJECT') #pbone = obj.pose.bones['Bone'] #pbone['type'] = 'copy' pass def metarig_definition(obj, orig_bone_name): bone = obj.data.bones[orig_bone_name] return [bone.name] def main(obj, definitions, base_names, options): """ A rig that drives shape keys with the local transforms of a single bone. Required options: mesh: name of mesh object(s) to add/get shapekeys to/from (if multiple objects, make a comma-separated list) Optional options: loc_: name of the shape key to tie to translation of the bone loc__fac: default multiplier of the bone influence on the shape key rot_: name of the shape key to tie to rotation of the bone rot__fac: default multiplier of the bone influence on the shape key scale_: name of the shape key to tie to scale of the bone scale__fac: default multiplier of the bone influence on the shape key """ bpy.ops.object.mode_set(mode='EDIT') eb = obj.data.edit_bones pb = obj.pose.bones org_bone = definitions[0] # Options req_options = ["mesh"] for option in req_options: if option not in options: raise RigifyError("'%s' rig type requires a '%s' option (bone: %s)" % (RIG_TYPE, option, base_names[definitions[0]])) meshes = options["mesh"].replace(" ", "").split(",") bone = copy_bone_simple(obj.data, org_bone, base_names[org_bone], parent=True).name bpy.ops.object.mode_set(mode='OBJECT') # Set rotation mode and axis locks pb[bone].rotation_mode = pb[org_bone].rotation_mode pb[bone].lock_location = tuple(pb[org_bone].lock_location) pb[bone].lock_rotation = tuple(pb[org_bone].lock_rotation) pb[bone].lock_rotation_w = pb[org_bone].lock_rotation_w pb[bone].lock_rotations_4d = pb[org_bone].lock_rotations_4d pb[bone].lock_scale = tuple(pb[org_bone].lock_scale) # List of rig options for specifying shape keys # Append '_fac' to the end for the name of the correspond 'factor default' # option for that shape shape_key_options = ["loc_x", "loc_y", "loc_z", "rot_x", "rot_y", "rot_z", "scale_x", "scale_y", "scale_z"] driver_paths = {"loc_x":".location[0]", "loc_y":".location[1]", "loc_z":".location[2]", "rot_x":".rotation_euler[0]", "rot_y":".rotation_euler[1]", "rot_z":".rotation_euler[2]", "qrot_x":".rotation_quaternion[1]", "qrot_y":".rotation_quaternion[2]", "qrot_z":".rotation_quaternion[3]", "scale_x":".scale[0]", "scale_y":".scale[1]", "scale_z":".scale[2]"} # Create the shape keys and drivers shape_info = [] for option in shape_key_options: if option in options: shape_name = options[option] var_name = bone + "_" + option # Different paths for euler vs quat if option in shape_key_options[3:6] and pb[bone].rotation_mode == 'QUATERNION': var_path = driver_paths['q' + option] else: var_path = driver_paths[option] fac_name = option + "_factor" if (option+"_fac") in options: fac_default = options[option+"_fac"] else: fac_default = 1.0 # Different expressions for loc/rot/scale if option in shape_key_options[:3]: expression = var_name + " * " + fac_name elif option in shape_key_options[:6]: # Different expressions for euler vs quats if pb[bone].rotation_mode == 'QUATERNION': expression = "2 * asin(" + var_name + ") * " + fac_name else: expression = var_name + " * " + fac_name else: expression = "(1.0 - " + var_name + ") * " + fac_name + " * -2" create_shape_and_driver(obj, bone, meshes, shape_name, var_name, var_path, fac_name, fac_default, expression) return (None,) def create_shape_and_driver(obj, bone, meshes, shape_name, var_name, var_path, fac_name, fac_default, expression): """ Creates/gets a shape key and sets up a driver for it. obj = armature object bone = driving bone name meshes = list of meshes to create the shapekey/driver on shape_name = name of the shape key var_name = name of the driving variable var_path = path to the property on the bone to drive with fac_name = name of the "factor" custom property on the bone fac_default = default starting value of the factor property expression = python expression for the driver """ pb = obj.pose.bones bpy.ops.object.mode_set(mode='OBJECT') # Set up the "factor" custom property on the bone prop = rna_idprop_ui_prop_get(pb[bone], fac_name, create=True) pb[bone][fac_name] = fac_default prop["min"] = -1000.0 prop["max"] = 1000.0 prop["soft_min"] = -1000.0 prop["soft_max"] = 1000.0 for mesh_name in meshes: mesh_obj = bpy.data.objects[mesh_name] # Add/get the shape key shape = addget_shape_key(mesh_obj, name=shape_name) # Add/get the shape key driver fcurve, a = addget_shape_key_driver(mesh_obj, name=shape_name) # Set up the driver driver = fcurve.driver driver.type = 'SCRIPTED' driver.expression = expression # Get the variable, or create it if it doesn't already exist if var_name in driver.variables: var = driver.variables[var_name] else: var = driver.variables.new() var.name = var_name # Get the fac variable, or create it if it doesn't already exist if fac_name in driver.variables: var_fac = driver.variables[fac_name] else: var_fac = driver.variables.new() var_fac.name = fac_name # Set up the variable var.type = "SINGLE_PROP" var.targets[0].id_type = 'OBJECT' var.targets[0].id = obj var.targets[0].data_path = 'pose.bones["' + bone + '"]' + var_path # Set up the fac variable var_fac.type = "SINGLE_PROP" var_fac.targets[0].id_type = 'OBJECT' var_fac.targets[0].id = obj var_fac.targets[0].data_path = 'pose.bones["' + bone + '"]["' + fac_name + '"]'