diff options
Diffstat (limited to 'release/scripts/modules/rigify/spine_pivot_flex.py')
-rw-r--r-- | release/scripts/modules/rigify/spine_pivot_flex.py | 514 |
1 files changed, 514 insertions, 0 deletions
diff --git a/release/scripts/modules/rigify/spine_pivot_flex.py b/release/scripts/modules/rigify/spine_pivot_flex.py new file mode 100644 index 00000000000..7f74028ddfb --- /dev/null +++ b/release/scripts/modules/rigify/spine_pivot_flex.py @@ -0,0 +1,514 @@ +# ##### 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 get_layer_dict +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 = ("pelvis", "ribcage") + + +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('pelvis') + bone.head[:] = 0.0000, -0.0306, 0.1039 + bone.tail[:] = 0.0000, -0.0306, -0.0159 + bone.roll = 0.0000 + bone.connected = False + bone = arm.edit_bones.new('rib_cage') + bone.head[:] = 0.0000, -0.0306, 0.1039 + bone.tail[:] = 0.0000, -0.0306, 0.2236 + bone.roll = -0.0000 + bone.connected = False + bone.parent = arm.edit_bones['pelvis'] + bone = arm.edit_bones.new('spine.01') + bone.head[:] = 0.0000, 0.0000, -0.0000 + bone.tail[:] = 0.0000, -0.0306, 0.1039 + bone.roll = -0.0000 + bone.connected = False + bone.parent = arm.edit_bones['rib_cage'] + bone = arm.edit_bones.new('spine.02') + bone.head[:] = 0.0000, -0.0306, 0.1039 + bone.tail[:] = -0.0000, -0.0398, 0.2045 + bone.roll = -0.0000 + bone.connected = True + bone.parent = arm.edit_bones['spine.01'] + bone = arm.edit_bones.new('spine.03') + bone.head[:] = -0.0000, -0.0398, 0.2045 + bone.tail[:] = -0.0000, -0.0094, 0.2893 + bone.roll = -0.0000 + bone.connected = True + bone.parent = arm.edit_bones['spine.02'] + bone = arm.edit_bones.new('spine.04') + bone.head[:] = -0.0000, -0.0094, 0.2893 + bone.tail[:] = -0.0000, 0.0335, 0.3595 + bone.roll = -0.0000 + bone.connected = True + bone.parent = arm.edit_bones['spine.03'] + bone = arm.edit_bones.new('spine.05') + bone.head[:] = -0.0000, 0.0335, 0.3595 + bone.tail[:] = -0.0000, 0.0555, 0.4327 + bone.roll = -0.0000 + bone.connected = True + bone.parent = arm.edit_bones['spine.04'] + bone = arm.edit_bones.new('spine.06') + bone.head[:] = -0.0000, 0.0555, 0.4327 + bone.tail[:] = -0.0000, 0.0440, 0.5207 + bone.roll = -0.0000 + bone.connected = True + bone.parent = arm.edit_bones['spine.05'] + bone = arm.edit_bones.new('spine.07') + bone.head[:] = -0.0000, 0.0440, 0.5207 + bone.tail[:] = -0.0000, 0.0021, 0.5992 + bone.roll = -0.0000 + bone.connected = True + bone.parent = arm.edit_bones['spine.06'] + + bpy.ops.object.mode_set(mode='OBJECT') + pbone = obj.pose.bones['rib_cage'] + pbone['type'] = 'spine_pivot_flex' + +def metarig_definition(obj, orig_bone_name): + ''' + The bone given is the second in a chain. + Expects at least 1 parent and a chain of children withe the same basename + eg. + pelvis -> rib_cage -> spine.01 -> spine.02 -> spine.03 + + note: same as neck. + ''' + arm = obj.data + ribcage = arm.bones[orig_bone_name] + pelvis = ribcage.parent + + if pelvis is None: + raise RigifyError("expected the ribcage bone:'%s' to have a parent (ribcage)." % ribcage.name) + + children = ribcage.children + if len(children) != 1: + raise RigifyError("expected the ribcage to have only 1 child.") + + child = children[0] + + bone_definition = [pelvis.name, ribcage.name, child.name] + bone_definition.extend([child.name for child in child.children_recursive_basename]) + return bone_definition + + +def fk(*args): + main(*args) + + +def main(obj, bone_definition, base_names, options): + from Mathutils import Vector, RotationMatrix + from math import radians, pi + + arm = obj.data + + # Initialize container classes for convenience + mt = bone_class_instance(obj, ["pelvis", "ribcage"]) # meta + mt.pelvis = bone_definition[0] + mt.ribcage = bone_definition[1] + mt.update() + + spine_chain_orig = tuple(bone_definition[2:]) + spine_chain = [arm.edit_bones[child_name] for child_name in spine_chain_orig] + spine_chain_basename = base_names[spine_chain[0].name].rsplit(".", 1)[0] # probably 'ORG-spine.01' -> 'spine' + spine_chain_len = len(spine_chain_orig) + + child = spine_chain[0] + spine_chain_segment_length = child.length + #child.parent = mt.pelvis_e # was mt.ribcage + + # The first bone in the chain happens to be the basis of others, create them now + ex = bone_class_instance(obj, ["pelvis", "pelvis_copy", "ribcage", "ribcage_hinge", "ribcage_copy", "spine_rotate"]) + df = bone_class_instance(obj, ["pelvis", "ribcage"]) # DEF-wgt_pelvis, DEF-wgt_rib_cage + + ex.pelvis_copy_e = copy_bone_simple(arm, mt.pelvis, base_names[mt.pelvis]) # no parent + ex.pelvis_copy = ex.pelvis_copy_e.name + ex.pelvis_copy_e.local_location = False + + # copy the pelvis, offset to make MCH-spine_rotate and MCH-ribcage_hinge + ex.ribcage_hinge_e = copy_bone_simple(arm, mt.pelvis, "MCH-%s_hinge" % base_names[mt.ribcage]) + ex.ribcage_hinge = ex.ribcage_hinge_e.name + ex.ribcage_hinge_e.translate(Vector(0.0, spine_chain_segment_length / 4.0, 0.0)) + + ex.spine_rotate_e = copy_bone_simple(arm, mt.ribcage, "MCH-%s_rotate" % spine_chain_basename) + ex.spine_rotate = ex.spine_rotate_e.name + ex.spine_rotate_e.translate(Vector(0.0, spine_chain_segment_length / 2.0, 0.0)) + ex.spine_rotate_e.connected = False + ex.spine_rotate_e.parent = ex.pelvis_copy_e + + df.pelvis_e = copy_bone_simple(arm, child.name, "DEF-wgt_%s" % base_names[mt.pelvis]) + df.pelvis = df.pelvis_e.name + df.pelvis_e.translate(Vector(spine_chain_segment_length * 2.0, - spine_chain_segment_length, 0.0)) + + ex.pelvis_e = copy_bone_simple(arm, child.name, "MCH-wgt_%s" % base_names[mt.pelvis]) + ex.pelvis = ex.pelvis_e.name + ex.pelvis_e.translate(Vector(0.0, - spine_chain_segment_length, 0.0)) + ex.pelvis_e.connected = False + ex.pelvis_e.parent = ex.pelvis_copy_e + + # Copy the last bone now + child = spine_chain[-1] + + df.ribcage_e = copy_bone_simple(arm, child.name, "DEF-wgt_%s" % base_names[mt.ribcage]) + df.ribcage = df.ribcage_e.name + df.ribcage_e.translate(Vector(spine_chain_segment_length * 2.0, - df.ribcage_e.length / 2.0, 0.0)) + + ex.ribcage_copy_e = copy_bone_simple(arm, mt.ribcage, base_names[mt.ribcage]) + ex.ribcage_copy = ex.ribcage_copy_e.name + ex.ribcage_copy_e.connected = False + ex.ribcage_copy_e.parent = ex.ribcage_hinge_e + + ex.ribcage_e = copy_bone_simple(arm, child.name, "MCH-wgt_%s" % base_names[mt.ribcage]) + ex.ribcage = ex.ribcage_e.name + ex.ribcage_e.translate(Vector(0.0, - ex.ribcage_e.length / 2.0, 0.0)) + ex.ribcage_e.parent = ex.ribcage_copy_e + + spine_chain = [child.name for child in spine_chain] + + # We have 3 spine chains + # - original (ORG_*) + # - copy (*use original name*) + # - reverse (MCH-rev_*) + spine_chain_attrs = [("spine_%.2d" % (i + 1)) for i in range(spine_chain_len)] + + mt_chain = bone_class_instance(obj, spine_chain_attrs) # ORG_* + rv_chain = bone_class_instance(obj, spine_chain_attrs) # * + ex_chain = bone_class_instance(obj, spine_chain_attrs) # MCH-rev_* + del spine_chain_attrs + + for i, child_name in enumerate(spine_chain): + child_name_orig = base_names[spine_chain_orig[i]] + + attr = mt_chain.attr_names[i] # eg. spine_04 + + setattr(mt_chain, attr, spine_chain_orig[i]) # the original bone + + ebone = copy_bone_simple(arm, child_name, child_name_orig) # use the original name + setattr(ex_chain, attr, ebone.name) + + ebone = copy_bone_simple(arm, child_name, "MCH-rev_%s" % child_name_orig) + setattr(rv_chain, attr, ebone.name) + ebone.connected = False + + mt_chain.update() + ex_chain.update() + rv_chain.update() + + # Now we need to re-parent these chains + for i, child_name in enumerate(spine_chain_orig): + attr = ex_chain.attr_names[i] + "_e" + ebone = getattr(ex_chain, attr) + if i == 0: + ebone.connected = False + ebone.parent = ex.pelvis_copy_e + else: + attr_parent = ex_chain.attr_names[i - 1] + "_e" + ebone.parent = getattr(ex_chain, attr_parent) + + # intentional! get the parent from the other paralelle chain member + getattr(rv_chain, attr).parent = ebone + + + # ex_chain needs to interlace bones! + # Note, skip the first bone + for i in range(1, spine_chain_len): # similar to neck + child_name_orig = base_names[spine_chain_orig[i]] + spine_e = getattr(mt_chain, mt_chain.attr_names[i] + "_e") + + # dont store parent names, re-reference as each chain bones parent. + spine_e_parent = arm.edit_bones.new("MCH-rot_%s" % child_name_orig) + spine_e_parent.head = spine_e.head + spine_e_parent.tail = spine_e.head + (mt.ribcage_e.vector.normalize() * spine_chain_segment_length / 2.0) + spine_e_parent.roll = mt.ribcage_e.roll + + + spine_e = getattr(ex_chain, ex_chain.attr_names[i] + "_e") + orig_parent = spine_e.parent + spine_e.connected = False + spine_e.parent = spine_e_parent + spine_e_parent.connected = False + + spine_e_parent.parent = orig_parent + + + # Rotate the rev chain 180 about the by the first bones center point + pivot = (rv_chain.spine_01_e.head + rv_chain.spine_01_e.tail) * 0.5 + matrix = RotationMatrix(radians(180), 3, 'X') + for i, attr in enumerate(rv_chain.attr_names): # similar to neck + spine_e = getattr(rv_chain, attr + "_e") + # use the first bone as the pivot + + spine_e.head = ((spine_e.head - pivot) * matrix) + pivot + spine_e.tail = ((spine_e.tail - pivot) * matrix) + pivot + spine_e.roll += pi # 180d roll + del spine_e + + + bpy.ops.object.mode_set(mode='OBJECT') + + # refresh pose bones + mt.update() + ex.update() + df.update() + mt_chain.update() + ex_chain.update() + rv_chain.update() + + # df.pelvis_p / DEF-wgt_pelvis + con = df.pelvis_p.constraints.new('COPY_LOCATION') + con.target = obj + con.subtarget = ex.pelvis + con.owner_space = 'LOCAL' + con.target_space = 'LOCAL' + + con = df.pelvis_p.constraints.new('COPY_ROTATION') + con.target = obj + con.subtarget = ex.pelvis + con.owner_space = 'LOCAL' + con.target_space = 'LOCAL' + + # df.ribcage_p / DEF-wgt_rib_cage + df.ribcage_p.lock_location = True, True, True + + con = df.ribcage_p.constraints.new('COPY_ROTATION') + con.target = obj + con.subtarget = ex.ribcage + con.owner_space = 'LOCAL' + con.target_space = 'LOCAL' + + con = df.ribcage_p.constraints.new('COPY_LOCATION') + con.target = obj + con.subtarget = ex.ribcage + con.owner_space = 'LOCAL' + con.target_space = 'LOCAL' + + con = ex.ribcage_hinge_p.constraints.new('COPY_ROTATION') + con.name = "hinge" + con.target = obj + con.subtarget = ex.pelvis_copy + + # add driver + fcurve = con.driver_add("influence", 0) + driver = fcurve.driver + tar = driver.targets.new() + driver.type = 'AVERAGE' + tar.name = "var" + tar.id_type = 'OBJECT' + tar.id = obj + tar.data_path = ex.ribcage_copy_p.path_to_id() + '["hinge"]' + + mod = fcurve.modifiers[0] + mod.poly_order = 1 + mod.coefficients[0] = 1.0 + mod.coefficients[1] = -1.0 + + + con = ex.spine_rotate_p.constraints.new('COPY_ROTATION') + con.target = obj + con.subtarget = ex.ribcage_copy + + + # ex.pelvis_p / MCH-wgt_pelvis + con = ex.pelvis_p.constraints.new('COPY_LOCATION') + con.target = obj + con.subtarget = mt_chain.spine_01 + con.owner_space = 'WORLD' + con.target_space = 'WORLD' + + con = ex.pelvis_p.constraints.new('COPY_ROTATION') + con.target = obj + con.subtarget = mt_chain.spine_01 + con.owner_space = 'WORLD' + con.target_space = 'WORLD' + + # ex.ribcage_p / MCH-wgt_rib_cage + con = ex.ribcage_p.constraints.new('COPY_LOCATION') + con.target = obj + con.subtarget = getattr(mt_chain, mt_chain.attr_names[-1]) + con.head_tail = 0.0 + + con = ex.ribcage_p.constraints.new('COPY_ROTATION') + con.target = obj + con.subtarget = getattr(mt_chain, mt_chain.attr_names[-1]) + + # ex.pelvis_copy_p / rib_cage + con = ex.ribcage_copy_p.constraints.new('COPY_LOCATION') + con.target = obj + con.subtarget = ex.pelvis_copy + con.head_tail = 0.0 + + # This stores all important ID props + prop = rna_idprop_ui_prop_get(ex.ribcage_copy_p, "hinge", create=True) + ex.ribcage_copy_p["hinge"] = 1.0 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + + prop = rna_idprop_ui_prop_get(ex.ribcage_copy_p, "pivot_slide", create=True) + ex.ribcage_copy_p["pivot_slide"] = 1.0 / spine_chain_len + prop["soft_min"] = 1.0 / spine_chain_len + prop["soft_max"] = 1.0 + + + # Create a fake connected parent/child relationship with bone location constraints + # positioned at the tip. + + # reverse bones / MCH-rev_spine.## + for i in range(1, spine_chain_len): + spine_p = getattr(rv_chain, rv_chain.attr_names[i] + "_p") + spine_fake_parent_name = getattr(rv_chain, rv_chain.attr_names[i - 1]) + + con = spine_p.constraints.new('COPY_LOCATION') + con.target = obj + con.subtarget = spine_fake_parent_name + con.head_tail = 1.0 + del spine_p, spine_fake_parent_name, con + + + # Constrain 'inbetween' bones + target_names = [("b%.2d" % (i + 1)) for i in range(spine_chain_len - 1)] + rib_driver_path = ex.ribcage_copy_p.path_to_id() + + ex.ribcage_copy_p["bend_tot"] = 0.0 + fcurve = ex.ribcage_copy_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(spine_chain_len - 1): + tar = driver.targets.new() + tar.name = target_names[i] + tar.id_type = 'OBJECT' + tar.id = obj + tar.data_path = rib_driver_path + ('["bend_%.2d"]' % (i + 1)) + + for i in range(1, spine_chain_len): + + # Add bend prop + prop_name = "bend_%.2d" % i + prop = rna_idprop_ui_prop_get(ex.ribcage_copy_p, prop_name, create=True) + ex.ribcage_copy_p[prop_name] = 1.0 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + + spine_p = getattr(ex_chain, ex_chain.attr_names[i] + "_p") + spine_p_parent = spine_p.parent # interlaced bone + + con = spine_p_parent.constraints.new('COPY_ROTATION') + con.target = obj + con.subtarget = ex.spine_rotate + con.owner_space = 'LOCAL' + con.target_space = 'LOCAL' + del spine_p + + # add driver + 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 + tar = driver.targets.new() + tar.name = "bend_tot" + tar.id_type = 'OBJECT' + tar.id = obj + tar.data_path = rib_driver_path + ('["bend_tot"]') + + tar = driver.targets.new() + tar.name = "bend" + tar.id_type = 'OBJECT' + tar.id = obj + tar.data_path = rib_driver_path + ('["%s"]' % prop_name) + + + + # original bone drivers + # note: the first bone has a lot more constraints, but also this simple one is first. + for i, attr in enumerate(mt_chain.attr_names): + spine_p = getattr(mt_chain, attr + "_p") + + con = spine_p.constraints.new('COPY_ROTATION') + con.target = obj + con.subtarget = getattr(ex_chain, attr) # lock to the copy's rotation + del spine_p + + # pivot slide: - lots of copy location constraints. + + con = mt_chain.spine_01_p.constraints.new('COPY_LOCATION') + con.name = "base" + con.target = obj + con.subtarget = rv_chain.spine_01 # lock to the reverse location + + for i in range(1, spine_chain_len + 1): + con = mt_chain.spine_01_p.constraints.new('COPY_LOCATION') + con.name = "slide_%d" % i + con.target = obj + + if i == spine_chain_len: + attr = mt_chain.attr_names[i - 1] + else: + attr = mt_chain.attr_names[i] + + con.subtarget = getattr(rv_chain, attr) # lock to the reverse location + + if i == spine_chain_len: + con.head_tail = 1.0 + + fcurve = con.driver_add("influence", 0) + driver = fcurve.driver + tar = driver.targets.new() + driver.type = 'AVERAGE' + tar.name = "var" + tar.id_type = 'OBJECT' + tar.id = obj + tar.data_path = rib_driver_path + '["pivot_slide"]' + + mod = fcurve.modifiers[0] + mod.poly_order = 1 + mod.coefficients[0] = - (i - 1) + mod.coefficients[1] = spine_chain_len + + + # last step setup layers + layers = get_layer_dict(options) + lay = layers["extra"] + for attr in ex.attr_names: + getattr(ex, attr + "_b").layer = lay + for attr in ex_chain.attr_names: + getattr(ex_chain, attr + "_b").layer = lay + + lay = layers["main"] + for attr in df.attr_names: + getattr(df, attr + "_b").layer = lay + for attr in rv_chain.attr_names: + getattr(rv_chain, attr + "_b").layer = lay + + # no support for blending chains + return None |