diff options
Diffstat (limited to 'release/scripts/modules/rigify/tongue.py')
-rw-r--r-- | release/scripts/modules/rigify/tongue.py | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/release/scripts/modules/rigify/tongue.py b/release/scripts/modules/rigify/tongue.py new file mode 100644 index 00000000000..b6dfe756b71 --- /dev/null +++ b/release/scripts/modules/rigify/tongue.py @@ -0,0 +1,361 @@ +# ##### 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 ##### + +# <pep8 compliant> + +import bpy +from rigify import RigifyError +from rigify_utils import bone_class_instance, copy_bone_simple +from rna_prop_ui import rna_idprop_ui_prop_get + +# not used, defined for completeness +METARIG_NAMES = ("body", "head") + + +def metarig_template(): + # TODO: + ## 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('body') + #bone.head[:] = 0.0000, -0.0276, -0.1328 + #bone.tail[:] = 0.0000, -0.0170, -0.0197 + #bone.roll = 0.0000 + #bone.connected = False + #bone = arm.edit_bones.new('head') + #bone.head[:] = 0.0000, -0.0170, -0.0197 + #bone.tail[:] = 0.0000, 0.0726, 0.1354 + #bone.roll = 0.0000 + #bone.connected = True + #bone.parent = arm.edit_bones['body'] + #bone = arm.edit_bones.new('neck.01') + #bone.head[:] = 0.0000, -0.0170, -0.0197 + #bone.tail[:] = 0.0000, -0.0099, 0.0146 + #bone.roll = 0.0000 + #bone.connected = False + #bone.parent = arm.edit_bones['head'] + #bone = arm.edit_bones.new('neck.02') + #bone.head[:] = 0.0000, -0.0099, 0.0146 + #bone.tail[:] = 0.0000, -0.0242, 0.0514 + #bone.roll = 0.0000 + #bone.connected = True + #bone.parent = arm.edit_bones['neck.01'] + #bone = arm.edit_bones.new('neck.03') + #bone.head[:] = 0.0000, -0.0242, 0.0514 + #bone.tail[:] = 0.0000, -0.0417, 0.0868 + #bone.roll = 0.0000 + #bone.connected = True + #bone.parent = arm.edit_bones['neck.02'] + #bone = arm.edit_bones.new('neck.04') + #bone.head[:] = 0.0000, -0.0417, 0.0868 + #bone.tail[:] = 0.0000, -0.0509, 0.1190 + #bone.roll = 0.0000 + #bone.connected = True + #bone.parent = arm.edit_bones['neck.03'] + #bone = arm.edit_bones.new('neck.05') + #bone.head[:] = 0.0000, -0.0509, 0.1190 + #bone.tail[:] = 0.0000, -0.0537, 0.1600 + #bone.roll = 0.0000 + #bone.connected = True + #bone.parent = arm.edit_bones['neck.04'] + # + #bpy.ops.object.mode_set(mode='OBJECT') + #pbone = obj.pose.bones['head'] + #pbone['type'] = 'neck_flex' + pass + + +def metarig_definition(obj, orig_bone_name): + ''' + The bone given is the tongue control, its parent is the body, + # its only child the first of a chain with matching basenames. + eg. + body -> tongue_control -> tongue_01 -> tongue_02 -> tongue_03.... etc + ''' + arm = obj.data + tongue = arm.bones[orig_bone_name] + body = tongue.parent + + children = tongue.children + if len(children) != 1: + raise RigifyError("expected the tongue bone '%s' to have only 1 child." % orig_bone_name) + + child = children[0] + bone_definition = [body.name, tongue.name, child.name] + bone_definition.extend([child.name for child in child.children_recursive_basename]) + return bone_definition + + +def deform(obj, definitions, base_names, options): + for org_bone_name in definitions[2:]: + bpy.ops.object.mode_set(mode='EDIT') + + # Create deform bone. + bone = copy_bone_simple(obj.data, org_bone_name, "DEF-%s" % base_names[org_bone_name], parent=True) + + # Store name before leaving edit mode + bone_name = bone.name + + # Leave edit mode + bpy.ops.object.mode_set(mode='OBJECT') + + # Get the pose bone + bone = obj.pose.bones[bone_name] + + # Constrain to the original bone + # XXX. Todo, is this needed if the bone is connected to its parent? + con = bone.constraints.new('COPY_TRANSFORMS') + con.name = "copy_loc" + con.target = obj + con.subtarget = org_bone_name + + +# TODO: rename all of the head/neck references to tongue +def main(obj, bone_definition, base_names, options): + from mathutils import Vector + + arm = obj.data + + # Initialize container classes for convenience + mt = bone_class_instance(obj, ["body", "head"]) # meta + mt.body = bone_definition[0] + mt.head = bone_definition[1] + mt.update() + + neck_chain = bone_definition[2:] + + mt_chain = bone_class_instance(obj, [("neck_%.2d" % (i + 1)) for i in range(len(neck_chain))]) # 99 bones enough eh? + for i, attr in enumerate(mt_chain.attr_names): + setattr(mt_chain, attr, neck_chain[i]) + mt_chain.update() + + neck_chain_basename = base_names[mt_chain.neck_01_e.name].split(".")[0] + neck_chain_segment_length = mt_chain.neck_01_e.length + + ex = bone_class_instance(obj, ["head", "head_hinge", "neck_socket", "head_ctrl"]) # hinge & extras + + # Add the head hinge at the bodys location, becomes the parent of the original head + + # apply everything to this copy of the chain + ex_chain = mt_chain.copy(base_names=base_names) + ex_chain.neck_01_e.parent = mt_chain.neck_01_e.parent + + + # Copy the head bone and offset + ex.head_e = copy_bone_simple(arm, mt.head, "MCH-%s" % base_names[mt.head], parent=True) + ex.head_e.connected = False + ex.head = ex.head_e.name + # offset + head_length = ex.head_e.length + ex.head_e.head.y += head_length / 2.0 + ex.head_e.tail.y += head_length / 2.0 + + # Yes, use the body bone but call it a head hinge + ex.head_hinge_e = copy_bone_simple(arm, mt.body, "MCH-%s_hinge" % base_names[mt.head], parent=False) + ex.head_hinge_e.connected = False + ex.head_hinge = ex.head_hinge_e.name + ex.head_hinge_e.head.y += head_length / 4.0 + ex.head_hinge_e.tail.y += head_length / 4.0 + + # Insert the neck socket, the head copys this loation + ex.neck_socket_e = arm.edit_bones.new("MCH-%s_socked" % neck_chain_basename) + ex.neck_socket = ex.neck_socket_e.name + ex.neck_socket_e.connected = False + ex.neck_socket_e.parent = mt.body_e + ex.neck_socket_e.head = mt.head_e.head + ex.neck_socket_e.tail = mt.head_e.head - Vector((0.0, neck_chain_segment_length / 2.0, 0.0)) + ex.neck_socket_e.roll = 0.0 + + + # copy of the head for controling + ex.head_ctrl_e = copy_bone_simple(arm, mt.head, base_names[mt.head]) + ex.head_ctrl = ex.head_ctrl_e.name + ex.head_ctrl_e.parent = ex.head_hinge_e + + for i, attr in enumerate(ex_chain.attr_names): + neck_e = getattr(ex_chain, attr + "_e") + + # dont store parent names, re-reference as each chain bones parent. + neck_e_parent = arm.edit_bones.new("MCH-rot_%s" % base_names[getattr(mt_chain, attr)]) + neck_e_parent.head = neck_e.head + neck_e_parent.tail = neck_e.head + (mt.head_e.vector.normalize() * neck_chain_segment_length / 2.0) + neck_e_parent.roll = mt.head_e.roll + + orig_parent = neck_e.parent + neck_e.connected = False + neck_e.parent = neck_e_parent + neck_e_parent.connected = False + + if i == 0: + neck_e_parent.parent = mt.body_e + else: + neck_e_parent.parent = orig_parent + + deform(obj, bone_definition, base_names, options) + + bpy.ops.object.mode_set(mode='OBJECT') + + mt.update() + mt_chain.update() + ex_chain.update() + ex.update() + + # Axis locks + ex.head_ctrl_p.lock_location = True, True, True + ex.head_ctrl_p.lock_scale = True, False, True + + # Simple one off constraints, no drivers + con = ex.head_ctrl_p.constraints.new('COPY_LOCATION') + con.target = obj + con.subtarget = ex.neck_socket + + con = ex.head_p.constraints.new('COPY_ROTATION') + con.target = obj + con.subtarget = ex.head_ctrl + + # driven hinge + prop = rna_idprop_ui_prop_get(ex.head_ctrl_p, "hinge", create=True) + ex.head_ctrl_p["hinge"] = 0.0 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + + con = ex.head_hinge_p.constraints.new('COPY_ROTATION') + con.name = "hinge" + con.target = obj + con.subtarget = mt.body + + # add driver + hinge_driver_path = ex.head_ctrl_p.path_to_id() + '["hinge"]' + + fcurve = con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'AVERAGE' + var.name = "var" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = obj + var.targets[0].data_path = hinge_driver_path + + #mod = fcurve_driver.modifiers.new('GENERATOR') + mod = fcurve.modifiers[0] + mod.poly_order = 1 + mod.coefficients[0] = 1.0 + mod.coefficients[1] = -1.0 + + head_driver_path = ex.head_ctrl_p.path_to_id() + + target_names = [("b%.2d" % (i + 1)) for i in range(len(neck_chain))] + + ex.head_ctrl_p["bend_tot"] = 0.0 + fcurve = ex.head_ctrl_p.driver_add('["bend_tot"]') + driver = fcurve.driver + driver.type = 'SUM' + fcurve.modifiers.remove(0) # grr dont need a modifier + + for i in range(len(neck_chain)): + var = driver.variables.new() + var.name = target_names[i] + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = obj + var.targets[0].data_path = head_driver_path + ('["bend_%.2d"]' % (i + 1)) + + + for i, attr in enumerate(ex_chain.attr_names): + neck_p = getattr(ex_chain, attr + "_p") + neck_p.lock_location = True, True, True + neck_p.lock_location = True, True, True + neck_p.lock_rotations_4d = True + + # Add bend prop + prop_name = "bend_%.2d" % (i + 1) + prop = rna_idprop_ui_prop_get(ex.head_ctrl_p, prop_name, create=True) + ex.head_ctrl_p[prop_name] = 1.0 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + + # add parent constraint + neck_p_parent = neck_p.parent + + # add constraints + if i == 0: + con = neck_p.constraints.new('COPY_SCALE') + con.name = "Copy Scale" + con.target = obj + con.subtarget = ex.head_ctrl + con.owner_space = 'LOCAL' + con.target_space = 'LOCAL' + + con = neck_p_parent.constraints.new('COPY_ROTATION') + con.name = "Copy Rotation" + con.target = obj + con.subtarget = ex.head + con.owner_space = 'LOCAL' + con.target_space = 'LOCAL' + + fcurve = con.driver_add("influence") + driver = fcurve.driver + driver.type = 'SCRIPTED' + driver.expression = "bend/bend_tot" + + fcurve.modifiers.remove(0) # grr dont need a modifier + + + # add target + var = driver.variables.new() + var.name = "bend_tot" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = obj + var.targets[0].data_path = head_driver_path + ('["bend_tot"]') + + var = driver.variables.new() + var.name = "bend" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = obj + var.targets[0].data_path = head_driver_path + ('["%s"]' % prop_name) + + + # finally constrain the original bone to this one + orig_neck_p = getattr(mt_chain, attr + "_p") + con = orig_neck_p.constraints.new('COPY_TRANSFORMS') + con.target = obj + con.subtarget = neck_p.name + + + # Set the head control's custom shape to use the last + # org neck bone for its transform + ex.head_ctrl_p.custom_shape_transform = obj.pose.bones[bone_definition[len(bone_definition)-1]] + + + # last step setup layers + if "ex_layer" in options: + layer = [n==options["ex_layer"] for n in range(0,32)] + else: + layer = list(arm.bones[bone_definition[1]].layer) + for attr in ex_chain.attr_names: + getattr(ex_chain, attr + "_b").layer = layer + for attr in ex.attr_names: + getattr(ex, attr + "_b").layer = layer + + layer = list(arm.bones[bone_definition[1]].layer) + ex.head_ctrl_b.layer = layer + + + # no blending the result of this + return None + |