Added tongue and neck rigs. The neck rig is quite solid, I think, and is working...
authorNathan Vegdahl <cessen@cessen.com>
Wed, 7 Apr 2010 14:46:06 +0000 (14:46 +0000)
committerNathan Vegdahl <cessen@cessen.com>
Wed, 7 Apr 2010 14:46:06 +0000 (14:46 +0000)
Also added pupil dilation to the eye rig type, and made the finger rig type work with two-digit fingers.

release/scripts/modules/rigify/eye_balls.py
release/scripts/modules/rigify/finger_curl.py
release/scripts/modules/rigify/neck.py [new file with mode: 0644]
release/scripts/modules/rigify/tongue.py [new file with mode: 0644]

index c1302a5cbca0e796b9b22691192b771584275d93..90e309bbc387e6514cb113007054abe9098aa7a9 100644 (file)
@@ -27,6 +27,85 @@ from rigify_utils import copy_bone_simple
 #METARIG_NAMES = ("cpy",)
 RIG_TYPE = "eye_balls"
 
+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
+   
+    
+def create_shape_and_driver(obj, bone, meshes, shape_name, var_name, var_path, 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
+        expression = python expression for the driver
+    """
+    pb = obj.pose.bones
+    bpy.ops.object.mode_set(mode='OBJECT')
+
+    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
+
+        # 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
+
+
 def mark_actions():
     for action in bpy.data.actions:
         action.tag = True
@@ -120,6 +199,12 @@ def control(obj, definitions, base_names, options):
     head = definitions[0]
     eye_target = definitions[1]
 
+    # Get list of pupil mesh objects
+    if "mesh" in options:
+        pupil_meshes = options["mesh"].replace(" ", "").split(",")
+    else:
+        pupil_meshes = []
+
     # Get list of eyes
     if "eyes" in options:
         eye_base_names = options["eyes"].replace(" ", "").split(",")
@@ -246,6 +331,50 @@ def control(obj, definitions, base_names, options):
         con.minimum = 0.0
         con.maximum = 2.0
         con.target_space = 'LOCAL'
+    
+    
+    # Get/create the shape keys and drivers for pupil dilation
+    shape_names = ["PUPILS-dilate_wide", "PUPILS-dilate_narrow"]
+    slider_name = "pupil_dilate"
+    
+    # Set up the custom property on the bone
+    prop = rna_idprop_ui_prop_get(pb[target_ctrl], slider_name, create=True)
+    pb[target_ctrl][slider_name] = 0.0
+    prop["min"] = 0.0
+    prop["max"] = 1.0
+    prop["soft_min"] = 0.0
+    prop["soft_max"] = 1.0
+    if len(shape_names) > 1:
+        prop["min"] = -1.0
+        prop["soft_min"] = -1.0
+
+    # Add the shape drivers
+    # Positive
+    if shape_names[0] != "":
+        # Set up the variables for creating the shape key driver
+        shape_name = shape_names[0]
+        var_name = slider_name.replace(".", "_").replace("-", "_")
+        var_path = '["' + slider_name + '"]'
+        if slider_name + "_fac" in options:
+            fac = options[slider_name + "_fac"]
+        else:
+            fac = 1.0
+        expression = var_name + " * " + str(fac)
+        # Create the shape key driver
+        create_shape_and_driver(obj, target_ctrl, pupil_meshes, shape_name, var_name, var_path, expression)
+    # Negative
+    if shape_names[0] != "" and len(shape_names) > 1:
+        # Set up the variables for creating the shape key driver
+        shape_name = shape_names[1]
+        var_name = slider_name.replace(".", "_").replace("-", "_")
+        var_path = '["' + slider_name + '"]'
+        if slider_name + "_fac" in options:
+            fac = options[slider_name + "_fac"]
+        else:
+            fac = 1.0
+        expression = var_name + " * " + str(fac) + " * -1"
+        # Create the shape key driver
+        create_shape_and_driver(obj, target_ctrl, pupil_meshes, shape_name, var_name, var_path, expression)
 
 
 
index 1f4d3e985f3656ac3d2bd9fc4d45a37a1ad10598..27ed437b5ac0fd39c727fd64c1548018b23c4607 100644 (file)
@@ -57,31 +57,20 @@ def metarig_template():
 def metarig_definition(obj, orig_bone_name):
     '''
     The bone given is the first in a chain
-    Expects a chain of at least 2 children.
+    Expects a chain with at least 1 child of the same base name.
     eg.
-        finger -> finger_01 -> finger_02
+        finger_01 -> finger_02
     '''
 
-    bone_definition = []
-
     orig_bone = obj.data.bones[orig_bone_name]
 
-    bone_definition.append(orig_bone.name)
-
-    bone = orig_bone
-    chain = 0
-    while chain < 2: # first 2 bones only have 1 child
-        children = bone.children
-
-        if len(children) != 1:
-            raise RigifyError("expected the chain to have 2 children from bone '%s' without a fork" % orig_bone_name)
-        bone = children[0]
-        bone_definition.append(bone.name) # finger_02, finger_03
-        chain += 1
-
-    if len(bone_definition) != len(METARIG_NAMES):
-        raise RigifyError("internal problem, expected %d bones" % len(METARIG_NAMES))
-
+    bone_definition = [orig_bone.name]
+    
+    bone_definition.extend([child.name for child in orig_bone.children_recursive_basename])
+    
+    if len(bone_definition) < 2:
+        raise RigifyError("expected the chain to have at least 1 child from bone '%s' without the same base name" % orig_bone_name)
+    
     return bone_definition
 
 
@@ -90,6 +79,8 @@ def deform(obj, definitions, base_names, options):
     """
     bpy.ops.object.mode_set(mode='EDIT')
 
+    three_digits = True if len(definitions) > 2 else False
+
     # Create base digit bones: two bones, each half of the base digit.
     f1a = copy_bone_simple(obj.data, definitions[0], "DEF-%s.01" % base_names[definitions[0]], parent=True)
     f1b = copy_bone_simple(obj.data, definitions[0], "DEF-%s.02" % base_names[definitions[0]], parent=True)
@@ -102,13 +93,15 @@ def deform(obj, definitions, base_names, options):
 
     # Create the other deform bones.
     f2 = copy_bone_simple(obj.data, definitions[1], "DEF-%s" % base_names[definitions[1]], parent=True)
-    f3 = copy_bone_simple(obj.data, definitions[2], "DEF-%s" % base_names[definitions[2]], parent=True)
+    if three_digits:
+        f3 = copy_bone_simple(obj.data, definitions[2], "DEF-%s" % base_names[definitions[2]], parent=True)
 
     # Store names before leaving edit mode
     f1a_name = f1a.name
     f1b_name = f1b.name
     f2_name = f2.name
-    f3_name = f3.name
+    if three_digits:
+        f3_name = f3.name
 
     # Leave edit mode
     bpy.ops.object.mode_set(mode='OBJECT')
@@ -117,7 +110,8 @@ def deform(obj, definitions, base_names, options):
     f1a = obj.pose.bones[f1a_name]
     f1b = obj.pose.bones[f1b_name]
     f2 = obj.pose.bones[f2_name]
-    f3 = obj.pose.bones[f3_name]
+    if three_digits:
+        f3 = obj.pose.bones[f3_name]
 
     # Constrain the base digit's bones
     con = f1a.constraints.new('DAMPED_TRACK')
@@ -141,15 +135,18 @@ def deform(obj, definitions, base_names, options):
     con.target = obj
     con.subtarget = definitions[1]
 
-    con = f3.constraints.new('COPY_TRANSFORMS')
-    con.name = "copy_transforms"
-    con.target = obj
-    con.subtarget = definitions[2]
+    if three_digits:
+        con = f3.constraints.new('COPY_TRANSFORMS')
+        con.name = "copy_transforms"
+        con.target = obj
+        con.subtarget = definitions[2]
 
 
 def main(obj, bone_definition, base_names, options):
     # *** EDITMODE
     bpy.ops.object.mode_set(mode='EDIT')
+    
+    three_digits = True if len(bone_definition) > 2 else False
 
     # get assosiated data
     arm = obj.data
@@ -159,7 +156,8 @@ def main(obj, bone_definition, base_names, options):
 
     org_f1 = bone_definition[0] # Original finger bone 01
     org_f2 = bone_definition[1] # Original finger bone 02
-    org_f3 = bone_definition[2] # Original finger bone 03
+    if three_digits:
+        org_f3 = bone_definition[2] # Original finger bone 03
 
     # Check options
     if "bend_ratio" in options:
@@ -179,7 +177,10 @@ def main(obj, bone_definition, base_names, options):
 
     # Create the control bone
     base_name = base_names[bone_definition[0]].split(".", 1)[0]
-    tot_len = eb[org_f1].length + eb[org_f2].length + eb[org_f3].length
+    if three_digits:
+        tot_len = eb[org_f1].length + eb[org_f2].length + eb[org_f3].length
+    else:
+        tot_len = eb[org_f1].length + eb[org_f2].length
     control = copy_bone_simple(arm, bone_definition[0], base_name + get_side_name(base_names[bone_definition[0]]), parent=True).name
     eb[control].connected = eb[org_f1].connected
     eb[control].parent = eb[org_f1].parent
@@ -188,26 +189,30 @@ def main(obj, bone_definition, base_names, options):
     # Create secondary control bones
     f1 = copy_bone_simple(arm, bone_definition[0], base_names[bone_definition[0]]).name
     f2 = copy_bone_simple(arm, bone_definition[1], base_names[bone_definition[1]]).name
-    f3 = copy_bone_simple(arm, bone_definition[2], base_names[bone_definition[2]]).name
+    if three_digits:
+        f3 = copy_bone_simple(arm, bone_definition[2], base_names[bone_definition[2]]).name
 
     # Create driver bones
     df1 = copy_bone_simple(arm, bone_definition[0], "MCH-" + base_names[bone_definition[0]]).name
     eb[df1].length /= 2
     df2 = copy_bone_simple(arm, bone_definition[1], "MCH-" + base_names[bone_definition[1]]).name
     eb[df2].length /= 2
-    df3 = copy_bone_simple(arm, bone_definition[2], "MCH-" + base_names[bone_definition[2]]).name
-    eb[df3].length /= 2
+    if three_digits:
+        df3 = copy_bone_simple(arm, bone_definition[2], "MCH-" + base_names[bone_definition[2]]).name
+        eb[df3].length /= 2
 
     # Set parents of the bones, interleaving the driver bones with the secondary control bones
-    eb[f3].connected = False
-    eb[df3].connected = False
+    if three_digits:
+        eb[f3].connected = False
+        eb[df3].connected = False
     eb[f2].connected = False
     eb[df2].connected = False
     eb[f1].connected = False
     eb[df1].connected = eb[org_f1].connected
 
-    eb[f3].parent = eb[df3]
-    eb[df3].parent = eb[f2]
+    if three_digits:
+        eb[f3].parent = eb[df3]
+        eb[df3].parent = eb[f2]
     eb[f2].parent = eb[df2]
     eb[df2].parent = eb[f1]
     eb[f1].parent = eb[df1]
@@ -215,8 +220,8 @@ def main(obj, bone_definition, base_names, options):
 
     # Set up bones for hinge
     if make_hinge:
-        socket = copy_bone_simple(arm, org_f1, "MCH-socket_" + control, parent=True).name
-        hinge = copy_bone_simple(arm, eb[org_f1].parent.name, "MCH-hinge_" + control).name
+        socket = copy_bone_simple(arm, org_f1, "MCH-socket_"+control, parent=True).name
+        hinge = copy_bone_simple(arm, eb[org_f1].parent.name, "MCH-hinge_"+control).name
 
         eb[control].connected = False
         eb[control].parent = eb[hinge]
@@ -234,12 +239,15 @@ def main(obj, bone_definition, base_names, options):
     pb[control].lock_scale = True, False, True
     pb[f1].rotation_mode = 'YZX'
     pb[f2].rotation_mode = 'YZX'
-    pb[f3].rotation_mode = 'YZX'
+    if three_digits:
+        pb[f3].rotation_mode = 'YZX'
     pb[f1].lock_location = True, True, True
     pb[f2].lock_location = True, True, True
-    pb[f3].lock_location = True, True, True
+    if three_digits:
+        pb[f3].lock_location = True, True, True
     pb[df2].rotation_mode = 'YZX'
-    pb[df3].rotation_mode = 'YZX'
+    if three_digits:
+        pb[df3].rotation_mode = 'YZX'
 
     # Add the bend_ratio property to the control bone
     pb[control]["bend_ratio"] = bend_ratio
@@ -271,9 +279,10 @@ def main(obj, bone_definition, base_names, options):
     con.target = obj
     con.subtarget = f2
 
-    con = pb[org_f3].constraints.new('COPY_TRANSFORMS')
-    con.target = obj
-    con.subtarget = f3
+    if three_digits:
+        con = pb[org_f3].constraints.new('COPY_TRANSFORMS')
+        con.target = obj
+        con.subtarget = f3
 
     if make_hinge:
         con = pb[hinge].constraints.new('COPY_TRANSFORMS')
@@ -303,8 +312,13 @@ def main(obj, bone_definition, base_names, options):
     # Create the drivers for the driver bones (control bone scale rotates driver bones)
     controller_path = pb[control].path_from_id() # 'pose.bones["%s"]' % control_bone_name
 
+    if three_digits:
+        finger_digits = [df2, df3]
+    else:
+        finger_digits = [df2]
+
     i = 0
-    for bone in [df2, df3]:
+    for bone in finger_digits:
 
         # XXX - todo, any number
         if i == 2:
@@ -334,23 +348,31 @@ def main(obj, bone_definition, base_names, options):
         var.targets[0].data_path = controller_path + '["bend_ratio"]'
 
         # XXX - todo, any number
-        if i == 0:
-            driver.expression = '(-scale+1.0)*pi*2.0*(1.0-br)'
-        elif i == 1:
-            driver.expression = '(-scale+1.0)*pi*2.0*br'
+        if three_digits:
+            if i == 0:
+                driver.expression = '(-scale+1.0)*pi*2.0*(1.0-br)'
+            elif i == 1:
+                driver.expression = '(-scale+1.0)*pi*2.0*br'
+        else:
+            driver.expression = driver.expression = '(-scale+1.0)*pi*2.0'
 
         i += 1
 
     # Last step setup layers
     if "ex_layer" in options:
-        layer = [n == options["ex_layer"] for n in range(0, 32)]
+        layer = [n==options["ex_layer"] for n in range(0,32)]
     else:
         layer = list(arm.bones[bone_definition[0]].layer)
-    for bone_name in [f1, f2, f3]:
-        arm.bones[bone_name].layer = layer
+    #for bone_name in [f1, f2, f3]:
+    #    arm.bones[bone_name].layer = layer
+    arm.bones[f1].layer = layer
+    arm.bones[f2].layer = layer
+    if three_digits:
+        arm.bones[f3].layer = layer
 
     layer = list(arm.bones[bone_definition[0]].layer)
     bb[control].layer = layer
 
     # no blending the result of this
     return None
+
diff --git a/release/scripts/modules/rigify/neck.py b/release/scripts/modules/rigify/neck.py
new file mode 100644 (file)
index 0000000..747e839
--- /dev/null
@@ -0,0 +1,344 @@
+# ##### 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
+
+
+
+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 neck_01, its parent is the body
+    eg.
+        body -> neck_01 -> neck_02 -> neck_03.... etc
+    '''
+    arm = obj.data
+    neck = arm.bones[orig_bone_name]
+    body = neck.parent
+
+    bone_definition = [body.name, neck.name]
+    bone_definition.extend([child.name for child in neck.children_recursive_basename])
+    return bone_definition
+
+
+def deform(obj, definitions, base_names, options):
+    for org_bone_name in definitions[1:]:
+        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
+
+
+def main(obj, bone_definition, base_names, options):
+    from Mathutils import Vector
+
+    arm = obj.data
+    eb = obj.data.edit_bones
+    bb = obj.data.bones
+    pb = obj.pose.bones
+    
+    body = bone_definition[0]
+    
+    # Create the neck and head control bones
+    if "head_name" in options:
+        head_name = options["head_name"]
+    else:
+        head_name = "head"
+    
+    neck_name = base_names[bone_definition[1]].split(".")[0]
+    
+    neck_ctrl = copy_bone_simple(arm, bone_definition[1], neck_name).name
+    head_ctrl = copy_bone_simple(arm, bone_definition[len(bone_definition)-1], head_name).name
+    eb[head_ctrl].tail += eb[neck_ctrl].head - eb[head_ctrl].head
+    eb[head_ctrl].head = eb[neck_ctrl].head
+    
+    # Create hinge and socket bones
+    neck_hinge = copy_bone_simple(arm, bone_definition[0], "MCH-" + neck_name + "_hinge").name
+    head_hinge = copy_bone_simple(arm, neck_ctrl, "MCH-" + head_name + "_hinge").name
+    eb[neck_hinge].tail += eb[neck_ctrl].head - eb[neck_hinge].head
+    eb[neck_hinge].head = eb[neck_ctrl].head
+    eb[head_hinge].tail += eb[neck_ctrl].head - eb[head_hinge].head
+    eb[head_hinge].head = eb[neck_ctrl].head
+    
+    neck_socket = copy_bone_simple(arm, bone_definition[1], "MCH-" + neck_name + "_socket").name
+    head_socket = copy_bone_simple(arm, bone_definition[1], "MCH-" + head_name + "_socket").name
+    
+    # Parent-child relationships between the body, hinges, controls, and sockets
+    eb[neck_ctrl].parent = eb[neck_hinge]
+    eb[head_ctrl].parent = eb[head_hinge]
+    
+    eb[neck_socket].parent = eb[body]
+    eb[head_socket].parent = eb[body]
+    
+    # Create neck bones
+    neck = [] # neck bones
+    neck_neck = [] # bones constrained to neck control
+    neck_head = [] # bones constrained to head control
+    for i in range(1, len(bone_definition)):
+        # Create bones
+        neck_bone = copy_bone_simple(arm, bone_definition[i], base_names[bone_definition[i]]).name
+        neck_neck_bone = copy_bone_simple(arm, neck_ctrl, "MCH-" + base_names[bone_definition[i]] + ".neck").name
+        neck_head_bone = copy_bone_simple(arm, head_ctrl, "MCH-" + base_names[bone_definition[i]] + ".head").name
+        
+        # Move them all to the same place
+        eb[neck_neck_bone].tail += eb[neck_bone].head - eb[neck_neck_bone].head
+        eb[neck_head_bone].tail += eb[neck_bone].head - eb[neck_neck_bone].head
+        eb[neck_neck_bone].head = eb[neck_bone].head
+        eb[neck_head_bone].head = eb[neck_bone].head
+        
+        # Parent/child relationships
+        eb[neck_bone].parent = eb[neck_head_bone]
+        eb[neck_head_bone].parent = eb[neck_neck_bone]
+        
+        if i > 1:
+            eb[neck_neck_bone].parent = eb[neck[i-2]]
+        else:
+            eb[neck_neck_bone].parent = eb[body]
+        
+        # Add them to the lists
+        neck += [neck_bone]
+        neck_neck += [neck_neck_bone]
+        neck_head += [neck_head_bone]
+
+    # Create deformation rig
+    deform(obj, bone_definition, base_names, options)
+
+
+    bpy.ops.object.mode_set(mode='OBJECT')
+
+    # Axis locks
+    pb[neck_ctrl].lock_location = True, True, True
+    pb[head_ctrl].lock_location = True, True, True
+    
+    for bone in neck:
+        pb[bone].lock_location = True, True, True
+
+    # Neck hinge
+    prop = rna_idprop_ui_prop_get(pb[neck_ctrl], "hinge", create=True)
+    pb[neck_ctrl]["hinge"] = 0.0
+    prop["soft_min"] = 0.0
+    prop["soft_max"] = 1.0
+    prop["hard_min"] = 0.0
+    prop["hard_max"] = 1.0
+
+    con = pb[neck_hinge].constraints.new('COPY_LOCATION')
+    con.name = "socket"
+    con.target = obj
+    con.subtarget = neck_socket
+
+    con = pb[neck_hinge].constraints.new('COPY_ROTATION')
+    con.name = "hinge"
+    con.target = obj
+    con.subtarget = body
+
+    hinge_driver_path = pb[neck_ctrl].path_from_id() + '["hinge"]'
+
+    fcurve = con.driver_add("influence", 0)
+    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.modifiers[0]
+    mod.poly_order = 1
+    mod.coefficients[0] = 1.0
+    mod.coefficients[1] = -1.0
+    
+    # Head hinge
+    prop = rna_idprop_ui_prop_get(pb[head_ctrl], "hinge", create=True)
+    pb[head_ctrl]["hinge"] = 0.0
+    prop["soft_min"] = 0.0
+    prop["soft_max"] = 1.0
+    prop["hard_min"] = 0.0
+    prop["hard_max"] = 1.0
+
+    con = pb[head_hinge].constraints.new('COPY_LOCATION')
+    con.name = "socket"
+    con.target = obj
+    con.subtarget = head_socket
+
+    con = pb[head_hinge].constraints.new('COPY_ROTATION')
+    con.name = "hinge"
+    con.target = obj
+    con.subtarget = neck_ctrl
+
+    hinge_driver_path = pb[head_ctrl].path_from_id() + '["hinge"]'
+
+    fcurve = con.driver_add("influence", 0)
+    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.modifiers[0]
+    mod.poly_order = 1
+    mod.coefficients[0] = 1.0
+    mod.coefficients[1] = -1.0
+    
+    # Neck rotation constraints
+    for i in range(0, len(neck_neck)):
+        con = pb[neck_neck[i]].constraints.new('COPY_ROTATION')
+        con.name = "neck rotation"
+        con.target = obj
+        con.subtarget = neck_ctrl
+        con.influence = (i+1) / len(neck_neck)
+        
+    
+    # Head rotation constraints/drivers
+    prop = rna_idprop_ui_prop_get(pb[head_ctrl], "extent", create=True)
+    if "extent" in options:
+        pb[head_ctrl]["extent"] = options["extent"]
+    else:
+        pb[head_ctrl]["extent"] = 0.5
+    prop["soft_min"] = 0.0
+    prop["soft_max"] = 1.0
+    prop["hard_min"] = 0.0
+    prop["hard_max"] = 1.0
+    
+    extent_prop_path = pb[head_ctrl].path_from_id() + '["extent"]'
+    
+    for i in range(0, len(neck_head)):
+        con = pb[neck_head[i]].constraints.new('COPY_ROTATION')
+        con.name = "head rotation"
+        con.target = obj
+        con.subtarget = head_ctrl
+        
+        if i < (len(neck_head)-1):
+            inf = (i+1) / len(neck_head)
+
+            fcurve = con.driver_add("influence", 0)
+            driver = fcurve.driver
+            var = driver.variables.new()
+            var.name = "ext"
+            var.targets[0].id_type = 'OBJECT'
+            var.targets[0].id = obj
+            var.targets[0].data_path = extent_prop_path
+            
+            driver.expression = "0 if ext == 0 else (((%s-1)/ext)+1)" % inf
+        else:
+            con.influence = 1.0
+    
+    # Constrain original bones to the neck bones
+    for i in range(0, len(neck)):
+        con = pb[bone_definition[i+1]].constraints.new('COPY_TRANSFORMS')
+        con.name = "copy_transform"
+        con.target = obj
+        con.subtarget = neck[i]
+        
+    
+    # Set the controls' custom shapes to use other bones for transforms
+    pb[neck_ctrl].custom_shape_transform = pb[bone_definition[len(bone_definition)//2]]
+    pb[head_ctrl].custom_shape_transform = pb[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 bone in neck:
+        bb[bone].layer = layer
+
+    layer = list(arm.bones[bone_definition[1]].layer)
+    bb[neck_ctrl].layer = layer
+    bb[head_ctrl].layer = layer
+
+
+    # no blending the result of this
+    return None
+
diff --git a/release/scripts/modules/rigify/tongue.py b/release/scripts/modules/rigify/tongue.py
new file mode 100644 (file)
index 0000000..5e21930
--- /dev/null
@@ -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", 0)
+    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"]', 0)
+    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", 0)
+        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
+