Added tongue and neck rigs. The neck rig is quite solid, I think, and is working well in production on Durian. The tongue rig is a bit hacky, but I'm adding it for now since we're using it in Durian.

Also added pupil dilation to the eye rig type, and made the finger rig type work with two-digit fingers.
This commit is contained in:
Nathan Vegdahl 2010-04-07 14:46:06 +00:00
parent 8bf6e2d09c
commit 54b12df3bc
4 changed files with 908 additions and 52 deletions

@ -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)

@ -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

@ -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

@ -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