diff options
author | Nathan Vegdahl <cessen@cessen.com> | 2010-02-03 18:40:56 +0300 |
---|---|---|
committer | Nathan Vegdahl <cessen@cessen.com> | 2010-02-03 18:40:56 +0300 |
commit | 11ce6eb4d46ac99adab073047d041b8bf104967b (patch) | |
tree | 3839df9a1130ba54afbaa3c1fcfe47cc2beb5cd4 /release/scripts/modules/rigify | |
parent | 1f3e277121795aaa37b7b719a0b5d0b8185b0d1c (diff) |
Added a new rig type "shape_key_transforms" that drives shape keys based on the local transforms of a single bone. Quite useful in a variety of situations.
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_<x/y/z>: name of the shape key to tie to translation of the bone
loc_<x/y/z>_fac: default multiplier of the bone influence on the shape key
rot_<x/y/z>: name of the shape key to tie to rotation of the bone
rot_<x/y/z>_fac: default multiplier of the bone influence on the shape key
scale_<x/y/z>: name of the shape key to tie to scale of the bone
scale_<x/y/z>_fac: default multiplier of the bone influence on the shape key
Diffstat (limited to 'release/scripts/modules/rigify')
-rw-r--r-- | release/scripts/modules/rigify/shape_key_transforms.py | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/release/scripts/modules/rigify/shape_key_transforms.py b/release/scripts/modules/rigify/shape_key_transforms.py new file mode 100644 index 00000000000..8e81776bf0c --- /dev/null +++ b/release/scripts/modules/rigify/shape_key_transforms.py @@ -0,0 +1,263 @@ +# ##### 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +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_<x/y/z>: name of the shape key to tie to translation of the bone + loc_<x/y/z>_fac: default multiplier of the bone influence on the shape key + rot_<x/y/z>: name of the shape key to tie to rotation of the bone + rot_<x/y/z>_fac: default multiplier of the bone influence on the shape key + scale_<x/y/z>: name of the shape key to tie to scale of the bone + scale_<x/y/z>_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 + '"]' + + +def main(obj, bone_definition, base_names, options): + control(obj, bone_definition, base_names, options) + + + return (None,) + |