From 65edb7341f999f576e98ca70b15a46985a6ed9df Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 5 Dec 2009 19:26:28 +0000 Subject: [PATCH] split up metarig hierarchy evaluation and modifying the metarig into 2 steps, original bone names cant be changed anymore but this means the bones can be re-parented without confusing scripts that run after the rig is modified. support for defining a bone to have multiple types and automatically blending between 2 generated rigs --- release/scripts/modules/rigify/__init__.py | 294 +++++++++++++-------- release/scripts/modules/rigify/arm.py | 78 +++--- release/scripts/modules/rigify/delta.py | 41 ++- release/scripts/modules/rigify/finger.py | 70 +++-- release/scripts/modules/rigify/leg.py | 209 +++++++++------ release/scripts/modules/rigify/neck.py | 56 ++-- release/scripts/modules/rigify/palm.py | 35 ++- release/scripts/modules/rigify/spine.py | 109 ++++---- 8 files changed, 556 insertions(+), 336 deletions(-) diff --git a/release/scripts/modules/rigify/__init__.py b/release/scripts/modules/rigify/__init__.py index 7ae2481619f..f6397fa94dc 100644 --- a/release/scripts/modules/rigify/__init__.py +++ b/release/scripts/modules/rigify/__init__.py @@ -91,6 +91,7 @@ def _bone_class_instance_copy(self, from_prefix="", to_prefix=""): new_slot_ls.append(attr) from_name_ls.append(bone_name) + bone_name_orig = bone_name_orig.replace("ORG-", "") # XXX - we need a better way to do this new_name_ls.append(to_prefix + bone_name_orig) new_bones = copy_bone_simple_list(self.obj.data, from_name_ls, new_name_ls, True) @@ -103,8 +104,10 @@ def _bone_class_instance_copy(self, from_prefix="", to_prefix=""): return new_bc +def _bone_class_instance_names(self): + return [getattr(self, attr) for attr in self.attr_names] -def _bone_class_instance_blend(self, from_bc, to_bc, target_bone=None, target_prop="blend", use_loc=True, use_rot=True): +def _bone_class_instance_blend(self, from_bc, to_bc, target_bone=None, target_prop="blend"): ''' Use for blending bone chains. @@ -113,78 +116,19 @@ def _bone_class_instance_blend(self, from_bc, to_bc, target_bone=None, target_pr XXX - toggles editmode, need to re-validate all editbones :( ''' + if self.attr_names != from_bc.attr_names or self.attr_names != to_bc.attr_names: raise Exception("can only blend between matching chains") - - obj = self.obj - if obj.mode == 'EDIT': - raise Exception("blending cant be called in editmode") + apply_bones = [getattr(self, attr) for attr in self.attr_names] + from_bones = [getattr(from_bc, attr) for attr in from_bc.attr_names] + to_bones = [getattr(to_bc, attr) for attr in to_bc.attr_names] - # setup the blend property - if target_bone is None: - target_bone = self.attr_names[-1] - - prop_pbone = obj.pose.bones[target_bone] - if prop_pbone.get(target_bone, None) is None: - prop = rna_idprop_ui_prop_get(prop_pbone, target_prop, create=True) - prop_pbone[target_prop] = 0.5 - prop["soft_min"] = 0.0 - prop["soft_max"] = 1.0 - - driver_path = prop_pbone.path_to_id() + ('["%s"]' % target_prop) - - def blend_target(driver): - tar = driver.targets.new() - tar.name = target_bone - tar.id_type = 'OBJECT' - tar.id = obj - tar.rna_path = driver_path - - for attr in self.attr_names: - new_pbone = getattr(self, attr + "_p") - from_bone_name = getattr(from_bc, attr) - to_bone_name = getattr(to_bc, attr) - - if from_bone_name == to_bone_name: - raise Exception("Matching from/to bone names:" + from_bone_name) - - if use_loc: - con = new_pbone.constraints.new('COPY_LOCATION') - con.target = obj - con.subtarget = from_bone_name - - con = new_pbone.constraints.new('COPY_LOCATION') - con.target = obj - con.subtarget = to_bone_name - - fcurve = con.driver_add("influence", 0) - driver = fcurve.driver - driver.type = 'AVERAGE' - fcurve.modifiers.remove(0) # grr dont need a modifier - - blend_target(driver) - - if use_rot: - con = new_pbone.constraints.new('COPY_ROTATION') - con.target = obj - con.subtarget = from_bone_name - - con = new_pbone.constraints.new('COPY_ROTATION') - con.target = obj - con.subtarget = to_bone_name - - fcurve = con.driver_add("influence", 0) - driver = fcurve.driver - driver.type = 'AVERAGE' - fcurve.modifiers.remove(0) # grr dont need a modifier - - blend_target(driver) - + blend_bone_list(self.obj, apply_bones, from_bones, to_bones, target_bone, target_prop) def bone_class_instance(obj, slots, name="BoneContainer"): attr_names = tuple(slots) # dont modify the original - slots = slots[:] # dont modify the original + slots = list(slots) # dont modify the original for i in range(len(slots)): member = slots[i] slots.append(member + "_b") # bone bone @@ -196,6 +140,7 @@ def bone_class_instance(obj, slots, name="BoneContainer"): "attr_names":attr_names, \ "update":_bone_class_instance_update, \ "rename":_bone_class_instance_rename, \ + "names":_bone_class_instance_names, \ "copy":_bone_class_instance_copy, \ "blend":_bone_class_instance_blend, \ } @@ -254,6 +199,78 @@ def copy_bone_simple_list(arm, from_bones, to_bones, parent=False): return copy_bones +def blend_bone_list(obj, apply_bones, from_bones, to_bones, target_bone=None, target_prop="blend"): + + if obj.mode == 'EDIT': + raise Exception("blending cant be called in editmode") + + # setup the blend property + if target_bone is None: + target_bone = apply_bones[-1] # default to the last bone + + prop_pbone = obj.pose.bones[target_bone] + if prop_pbone.get(target_bone, None) is None: + prop = rna_idprop_ui_prop_get(prop_pbone, target_prop, create=True) + prop_pbone[target_prop] = 0.5 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + + driver_path = prop_pbone.path_to_id() + ('["%s"]' % target_prop) + + def blend_target(driver): + tar = driver.targets.new() + tar.name = target_bone + tar.id_type = 'OBJECT' + tar.id = obj + tar.rna_path = driver_path + + def blend_location(new_pbone, from_bone_name, to_bone_name): + con = new_pbone.constraints.new('COPY_LOCATION') + con.target = obj + con.subtarget = from_bone_name + + con = new_pbone.constraints.new('COPY_LOCATION') + con.target = obj + con.subtarget = to_bone_name + + fcurve = con.driver_add("influence", 0) + driver = fcurve.driver + driver.type = 'AVERAGE' + fcurve.modifiers.remove(0) # grr dont need a modifier + + blend_target(driver) + + def blend_rotation(new_pbone, from_bone_name, to_bone_name): + con = new_pbone.constraints.new('COPY_ROTATION') + con.target = obj + con.subtarget = from_bone_name + + con = new_pbone.constraints.new('COPY_ROTATION') + con.target = obj + con.subtarget = to_bone_name + + fcurve = con.driver_add("influence", 0) + driver = fcurve.driver + driver.type = 'AVERAGE' + fcurve.modifiers.remove(0) # grr dont need a modifier + + blend_target(driver) + + for i, new_bone_name in enumerate(apply_bones): + from_bone_name = from_bones[i] + to_bone_name = to_bones[i] + + # allow skipping some bones by having None in the list + if None in (new_bone_name, from_bone_name, to_bone_name): + continue + + new_pbone = obj.pose.bones[new_bone_name] + + if not new_pbone.bone.connected: + blend_location(new_pbone, from_bone_name, to_bone_name) + + blend_rotation(new_pbone, from_bone_name, to_bone_name) + def add_stretch_to(obj, from_name, to_name, name): ''' @@ -342,8 +359,9 @@ def add_pole_target_bone(obj, base_name, name, mode='CROSS'): return poll_name -def generate_rig(context, ob): - +def generate_rig(context, obj_orig, prefix="ORG-"): + from collections import OrderedDict + global_undo = context.user_preferences.edit.global_undo context.user_preferences.edit.global_undo = False @@ -351,50 +369,118 @@ def generate_rig(context, ob): # copy object and data - ob.selected = False - ob_new = ob.copy() - ob_new.data = ob.data.copy() + obj_orig.selected = False + obj = obj_orig.copy() + obj.data = obj_orig.data.copy() scene = context.scene - scene.objects.link(ob_new) - scene.objects.active = ob_new - ob_new.selected = True + scene.objects.link(obj) + scene.objects.active = obj + obj.selected = True - # enter armature editmode + arm = obj.data - # Only reference bones that have a type, means we can rename any others without lookup errors - pose_names = [pbone.name for pbone in ob_new.pose.bones if "type" in pbone] + # original name mapping + base_names = {} - #for pbone_name in ob_new.pose.bones.keys(): - for pbone_name in pose_names: + bpy.ops.object.mode_set(mode='EDIT') + for bone in arm.edit_bones: + bone_name = bone.name + bone.name = prefix + bone_name + base_names[bone.name] = bone_name # new -> old mapping + bpy.ops.object.mode_set(mode='OBJECT') - bone_type = ob_new.pose.bones[pbone_name].get("type", "") + # key: bone name + # value: {type:definition, ...} + # where type is the submodule name - leg, arm etc + # and definition is a list of bone names + bone_definitions = {} + + # key: bone name + # value: [functions, ...] + # each function is from the module. eg leg.ik, arm.main + bone_typeinfos = {} + + # inspect all bones and assign their definitions before modifying + for pbone in obj.pose.bones: + bone_name = pbone.name + bone_type = obj.pose.bones[bone_name].get("type", "") + bone_type_list = [bt for bt in bone_type.replace(",", " ").split()] - if bone_type == "": - continue + for bone_type in bone_type_list: + type_pair = bone_type.split(".") + + # 'leg.ik' will look for an ik function in the leg module + # 'leg' will look up leg.main + if len(type_pair) == 1: + type_pair = type_pair[0], "main" + + submod_name, func_name = type_pair + + # from rigify import leg + submod = __import__(name="%s.%s" % (__package__, submod_name), fromlist=[submod_name]) + reload(submod) + + bone_def_dict = bone_definitions.setdefault(bone_name, {}) - # submodule = getattr(self, bone_type) - # exec("from rigify import %s as submodule") - submodule = __import__(name="%s.%s" % (__package__, bone_type), fromlist=[bone_type]) - - reload(submodule) # XXX, dev only + # Only calculate bone definitions once + if submod_name not in bone_def_dict: + metarig_definition_func = getattr(submod, "metarig_definition") + bone_def_dict[submod_name] = metarig_definition_func(obj, bone_name) + + + bone_typeinfo = bone_typeinfos.setdefault(bone_name, []) + type_func = getattr(submod, func_name) + bone_typeinfo.append((submod_name, type_func)) + + # now we have all the info about bones we can start operating on them + + for pbone in obj.pose.bones: + bone_name = pbone.name - # Toggle editmode so the pose data is always up to date - bpy.ops.object.mode_set(mode='EDIT') - submodule.main(ob_new, pbone_name) - bpy.ops.object.mode_set(mode='OBJECT') + if bone_name not in bone_typeinfos: + continue + + bone_def_dict = bone_definitions[bone_name] + + # Only blend results from the same submodule, eg. + # leg.ik and arm.fk could not be blended. + results = OrderedDict() + + for submod_name, type_func in bone_typeinfos[bone_name]: + # this bones definition of the current typeinfo + definition = bone_def_dict[submod_name] + + bpy.ops.object.mode_set(mode='EDIT') + ret = type_func(obj, definition, base_names) + bpy.ops.object.mode_set(mode='OBJECT') + + if ret: + result_submod = results.setdefault(submod_name, []) + + if result_submod and len(result_submod[-1]) != len(ret): + raise Exception("bone lists not compatible: %s, %s" % (result_submod[-1], ret)) + + result_submod.append(ret) + + for result_submod in results.values(): + # blend 2 chains + definition = bone_def_dict[submod_name] + + if len(result_submod) == 2: + blend_bone_list(obj, definition, result_submod[0], result_submod[1]) # needed to update driver deps # context.scene.update() # Only for demo'ing - # ob.restrict_view = True - ob_new.data.draw_axes = False + # obj.restrict_view = True + obj.data.draw_axes = False context.user_preferences.edit.global_undo = global_undo - return ob_new + return obj def write_meta_rig(obj, func_name="metarig_template"): @@ -462,11 +548,11 @@ def generate_test(context): scene = context.scene def create_empty_armature(name): - ob_new = bpy.data.add_object('ARMATURE', name) + obj_new = bpy.data.add_object('ARMATURE', name) armature = bpy.data.add_armature(name) - ob_new.data = armature - scene.objects.link(ob_new) - scene.objects.active = ob_new + obj_new.data = armature + scene.objects.link(obj_new) + scene.objects.active = obj_new files = os.listdir(os.path.dirname(__file__)) for f in files: @@ -484,10 +570,10 @@ def generate_test(context): if metarig_template: create_empty_armature("meta_" + module_name) # sets active metarig_template() - ob = context.object - ob_new = generate_rig(context, ob) + obj = context.object + obj_new = generate_rig(context, obj) - new_objects.append((ob, ob_new)) + new_objects.append((obj, obj_new)) else: print("note: rig type '%s' has no metarig_template(), can't test this", module_name) @@ -505,12 +591,12 @@ def generate_test_all(context): base_name = os.path.splitext(bpy.data.filename)[0] for obj, obj_new in new_objects: - for ob in (obj, obj_new): - fn = base_name + "-" + bpy.utils.clean_name(ob.name) + for obj in (obj, obj_new): + fn = base_name + "-" + bpy.utils.clean_name(obj.name) path_dot = fn + ".dot" path_png = fn + ".png" - saved = graphviz_export.graph_armature(ob, path_dot, CONSTRAINTS=True, DRIVERS=True) + saved = graphviz_export.graph_armature(obj, path_dot, CONSTRAINTS=True, DRIVERS=True) #if saved: # os.system("dot -Tpng %s > %s; eog %s" % (path_dot, path_png, path_png)) diff --git a/release/scripts/modules/rigify/arm.py b/release/scripts/modules/rigify/arm.py index 36217ed5247..af2d08307dd 100644 --- a/release/scripts/modules/rigify/arm.py +++ b/release/scripts/modules/rigify/arm.py @@ -20,6 +20,7 @@ import bpy from rigify import bone_class_instance, copy_bone_simple, add_pole_target_bone, add_stretch_to from rna_prop_ui import rna_idprop_ui_get, rna_idprop_ui_prop_get +METARIG_NAMES = "shoulder", "arm", "forearm", "hand" def metarig_template(): bpy.ops.object.mode_set(mode='EDIT') @@ -54,7 +55,38 @@ def metarig_template(): pbone['type'] = 'arm' -def main(obj, orig_bone_name): +def metarig_definition(obj, orig_bone_name): + mt = bone_class_instance(obj, METARIG_NAMES) # meta + mt.arm = orig_bone_name + mt.update() + + mt.shoulder_p = mt.arm_p.parent + mt.shoulder = mt.shoulder_p.name + + if not mt.shoulder_p: + raise Exception("could not find 'arm' parent, skipping:", orig_bone_name) + + # We could have some bones attached, find the bone that has this as its 2nd parent + hands = [] + for pbone in obj.pose.bones: + index = pbone.parent_index(mt.arm_p) + if index == 2: + hands.append(pbone) + + if len(hands) > 1: + raise Exception("more then 1 hand found on:", orig_bone_name) + + # first add the 2 new bones + mt.hand_p = hands[0] + mt.hand = mt.hand_p.name + + mt.forearm_p = mt.hand_p.parent + mt.forearm = mt.forearm_p.name + + return mt.names() + + +def main(obj, definitions, base_names): """ the bone with the 'arm' property is the upper arm, this assumes a chain as follows. [shoulder, upper_arm, forearm, hand] @@ -64,49 +96,19 @@ def main(obj, orig_bone_name): - Original - IK, MCH-%s_ik - IKSwitch, MCH-%s () + + """ # Since there are 3 chains, this gets confusing so divide into 3 chains # Initialize container classes for convenience - mt = bone_class_instance(obj, ["shoulder", "arm", "forearm", "hand"]) # meta + mt = bone_class_instance(obj, METARIG_NAMES) # meta + mt.shoulder, mt.arm, mt.forearm, mt.hand = definitions + ik = bone_class_instance(obj, ["arm", "forearm", "pole", "hand"]) # ik sw = bone_class_instance(obj, ["socket", "shoulder", "arm", "forearm", "hand"]) # hinge ex = bone_class_instance(obj, ["arm_hinge"]) # hinge & extras - - def chain_init(): - ''' - Sanity check and return the arm as a list of bone names. - ''' - # do a sanity check - mt.arm = orig_bone_name - mt.update() - - mt.shoulder_p = mt.arm_p.parent - mt.shoulder = mt.shoulder_p.name - - if not mt.shoulder_p: - print("could not find 'arm' parent, skipping:", orig_bone_name) - return - - # We could have some bones attached, find the bone that has this as its 2nd parent - hands = [] - for pbone in obj.pose.bones: - index = pbone.parent_index(mt.arm_p) - if index == 2: - hands.append(pbone) - - if len(hands) > 1: - print("more then 1 hand found on:", orig_bone_name) - return - - # first add the 2 new bones - mt.hand_p = hands[0] - mt.hand = mt.hand_p.name - - mt.forearm_p = mt.hand_p.parent - mt.forearm = mt.forearm_p.name - arm = obj.data @@ -345,4 +347,6 @@ def main(obj, orig_bone_name): chain_shoulder() # Shoulder with its delta and hinge. - + + # TODO - return a list for fk and IK + return None diff --git a/release/scripts/modules/rigify/delta.py b/release/scripts/modules/rigify/delta.py index a8458537c12..74cfb6150d2 100644 --- a/release/scripts/modules/rigify/delta.py +++ b/release/scripts/modules/rigify/delta.py @@ -19,23 +19,39 @@ import bpy from rigify import get_bone_data -def main(obj, delta_name): +# not used, defined for completeness +METARIG_NAMES = tuple() + +def metarig_definition(obj, orig_bone_name): ''' - Use this bone to define a delta thats applied to its child in pose mode. + The bone given is the head, its parent is the body, + # its only child the first of a chain with matching basenames. + eg. + body -> head -> neck_01 -> neck_02 -> neck_03.... etc ''' - arm = obj.data - - mode_orig = obj.mode - bpy.ops.object.mode_set(mode='OBJECT') - - delta_pbone = obj.pose.bones[delta_name] - children = delta_pbone.children + delta = arm.bones[orig_bone_name] + children = delta.children if len(children) != 1: print("only 1 child supported for delta") - child_name = children[0].name + bone_definition = [delta.name, children[0].name] + + return bone_definition + +def main(obj, bone_definition, base_names): + ''' + Use this bone to define a delta thats applied to its child in pose mode. + ''' + + mode_orig = obj.mode + bpy.ops.object.mode_set(mode='OBJECT') + + delta_name, child_name = bone_definition + + delta_pbone = obj.pose.bones[delta_name] + arm, child_pbone, child_bone = get_bone_data(obj, child_name) delta_phead = delta_pbone.head.copy() @@ -62,7 +78,7 @@ def main(obj, delta_name): child_tail = child_ebone.tail.copy() arm.edit_bones.remove(delta_ebone) - del delta_ebone # cant use thz + del delta_ebone # cant use this bpy.ops.object.mode_set(mode='OBJECT') @@ -107,3 +123,6 @@ def main(obj, delta_name): bpy.ops.object.mode_set(mode=mode_orig) + # no blendeing + return None + \ No newline at end of file diff --git a/release/scripts/modules/rigify/finger.py b/release/scripts/modules/rigify/finger.py index a9040830e4e..c839f20e71f 100644 --- a/release/scripts/modules/rigify/finger.py +++ b/release/scripts/modules/rigify/finger.py @@ -17,10 +17,11 @@ # ##### END GPL LICENSE BLOCK ##### import bpy -from rigify import get_bone_data, empty_layer +from rigify import get_bone_data, empty_layer, copy_bone_simple from rna_prop_ui import rna_idprop_ui_get, rna_idprop_ui_prop_get from functools import reduce +METARIG_NAMES = "finger_01", "finger_02", "finger_03" def metarig_template(): bpy.ops.object.mode_set(mode='EDIT') @@ -48,13 +49,43 @@ def metarig_template(): pbone = obj.pose.bones['finger.01'] pbone['type'] = 'finger' +def metarig_definition(obj, orig_bone_name): + ''' + The bone given is the first in a chain + Expects a chain of at least 2 children. + eg. + finger -> finger_01 -> finger_02 + ''' + + bone_definition = [] -def main(obj, orig_bone_name): + 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 Exception("expected the chain to have 2 children without a fork") + bone = children[0] + bone_definition.append(bone.name) # finger_02, finger_03 + chain += 1 + + if len(bone_definition) != len(METARIG_NAMES): + raise Exception("internal problem, expected %d bones" % len(METARIG_NAMES)) + + return bone_definition + + +def main(obj, bone_definition, base_names): # *** EDITMODE # get assosiated data - arm, orig_pbone, orig_ebone = get_bone_data(obj, orig_bone_name) + arm, orig_pbone, orig_ebone = get_bone_data(obj, bone_definition[0]) obj.animation_data_create() # needed if its a new armature with no keys @@ -63,22 +94,16 @@ def main(obj, orig_bone_name): children = orig_pbone.children_recursive tot_len = reduce(lambda f, pbone: f + pbone.bone.length, children, orig_pbone.bone.length) - base_name = orig_pbone.basename + base_name = base_names[bone_definition[0]].rsplit(".", 1)[0] # first make a new bone at the location of the finger - control_ebone = arm.edit_bones.new(base_name) + #control_ebone = arm.edit_bones.new(base_name) + control_ebone = copy_bone_simple(arm, base_name, base_name) control_bone_name = control_ebone.name # we dont know if we get the name requested control_ebone.connected = orig_ebone.connected control_ebone.parent = orig_ebone.parent - - # Place the finger bone - head = orig_ebone.head.copy() - tail = orig_ebone.tail.copy() - - control_ebone.head = head - control_ebone.tail = head + ((tail - head).normalize() * tot_len) - control_ebone.roll = orig_ebone.roll + control_ebone.length = tot_len # now add bones inbetween this and its children recursively @@ -99,19 +124,10 @@ def main(obj, orig_bone_name): driver_bone_name = child_bone_name.split('.') driver_bone_name = driver_bone_name[0] + "_driver." + ".".join(driver_bone_name[1:]) - driver_ebone = arm.edit_bones.new(driver_bone_name) - driver_bone_name = driver_ebone.name # cant be too sure! + driver_ebone = copy_bone_simple(arm, child_ebone.name, driver_bone_name) + driver_ebone.length *= 0.5 driver_ebone.layer = other_layer - new_len = pbone_child.bone.length / 2.0 - - head = child_ebone.head.copy() - tail = child_ebone.tail.copy() - - driver_ebone.head = head - driver_ebone.tail = head + ((tail - head).normalize() * new_len) - driver_ebone.roll = child_ebone.roll - # Insert driver_ebone in the chain without connected parents driver_ebone.connected = False driver_ebone.parent = child_ebone.parent @@ -129,7 +145,7 @@ def main(obj, orig_bone_name): bpy.ops.object.mode_set(mode='OBJECT') - arm, orig_pbone, orig_bone = get_bone_data(obj, orig_bone_name) + arm, orig_pbone, orig_bone = get_bone_data(obj, bone_definition[0]) arm, control_pbone, control_bone= get_bone_data(obj, control_bone_name) @@ -198,4 +214,6 @@ def main(obj, orig_bone_name): driver_pbone.lock_rotation = child_pbone.lock_rotation = (False, True, True) i += 1 - + + # no blending the result of this + return None diff --git a/release/scripts/modules/rigify/leg.py b/release/scripts/modules/rigify/leg.py index 54dc76dafcf..589e295101c 100644 --- a/release/scripts/modules/rigify/leg.py +++ b/release/scripts/modules/rigify/leg.py @@ -17,9 +17,11 @@ # ##### END GPL LICENSE BLOCK ##### import bpy -from rigify import bone_class_instance, copy_bone_simple, add_pole_target_bone, add_stretch_to +from rigify import bone_class_instance, copy_bone_simple, copy_bone_simple_list, add_pole_target_bone, add_stretch_to from rna_prop_ui import rna_idprop_ui_get, rna_idprop_ui_prop_get +METARIG_NAMES = "hips", "thigh", "shin", "foot", "toe", "heel" + def metarig_template(): # generated by rigify.write_meta_rig bpy.ops.object.mode_set(mode='EDIT') @@ -65,37 +67,60 @@ def metarig_template(): pbone = obj.pose.bones['thigh'] pbone['type'] = 'leg' - -def validate(obj, orig_bone_name): +def metarig_definition(obj, orig_bone_name): ''' The bone given is the first in a chain Expects a chain of at least 3 children. eg. thigh -> shin -> foot -> [toe, heel] ''' + + bone_definition = [] + orig_bone = obj.data.bones[orig_bone_name] + orig_bone_parent = orig_bone.parent + + if orig_bone_parent is None: + raise Exception("expected the thigh bone to have a parent hip bone") + + bone_definition.append(orig_bone_parent.name) + bone_definition.append(orig_bone.name) + bone = orig_bone chain = 0 - while chain < 3: # first 2 bones only have 1 child + while chain < 2: # first 2 bones only have 1 child children = bone.children + if len(children) != 1: - return "expected the thigh bone to have 3 children without a fork" + raise Exception("expected the thigh bone to have 3 children without a fork") bone = children[0] + bone_definition.append(bone.name) # shin, foot chain += 1 children = bone.children # Now there must be 2 children, only one connected if len(children) != 2: - return "expected the foot to have 2 children" + raise Exception("expected the foot to have 2 children") if children[0].connected == children[1].connected: - return "expected one bone to be connected" + raise Exception("expected one bone to be connected") - return '' + toe, heel = children + if heel.connected: + toe, heel = heel, toe + + + bone_definition.append(toe.name) + bone_definition.append(heel.name) + + if len(bone_definition) != len(METARIG_NAMES): + raise Exception("internal problem, expected %d bones" % len(METARIG_NAMES)) + + return bone_definition -def main(obj, orig_bone_name): +def ik(obj, bone_definition, base_names): from Mathutils import Vector arm = obj.data @@ -107,55 +132,26 @@ def main(obj, orig_bone_name): # children of ik_foot ik = bone_class_instance(obj, ["foot", "foot_roll", "foot_roll_01", "foot_roll_02", "knee_target"]) - mt_chain.thigh_e = arm.edit_bones[orig_bone_name] - mt_chain.thigh = orig_bone_name - - mt.hips_e = mt_chain.thigh_e.parent - mt.hips_e.name = "ORG-" + mt.hips_e.name - mt.hips = mt.hips_e.name - - mt_chain.shin_e = mt_chain.thigh_e.children[0] - mt_chain.shin = mt_chain.shin_e.name - - mt_chain.foot_e = mt_chain.shin_e.children[0] - mt_chain.foot = mt_chain.foot_e.name - - mt_chain.toe_e, mt.heel_e = mt_chain.foot_e.children - - # We dont know which is which, but know the heel is disconnected - if not mt_chain.toe_e.connected: - mt_chain.toe_e, mt.heel_e = mt.heel_e, mt_chain.toe_e - mt.heel_e.name = "ORG-" + mt.heel_e.name - mt_chain.toe, mt.heel = mt_chain.toe_e.name, mt.heel_e.name - - ex.thigh_socket_e = copy_bone_simple(arm, mt_chain.thigh, "MCH-%s_socket" % mt_chain.thigh, parent=True) - ex.thigh_socket = ex.thigh_socket_e.name - ex.thigh_socket_e.tail = ex.thigh_socket_e.head + Vector(0.0, 0.0, ex.thigh_socket_e.length / 4.0) - - ex.thigh_hinge_e = copy_bone_simple(arm, mt_chain.thigh, "MCH-%s_hinge" % mt_chain.thigh, parent=True) - ex.thigh_hinge = ex.thigh_hinge_e.name - ex.thigh_hinge_e.tail = ex.thigh_hinge_e.head + Vector(0.0, 0.0, mt_chain.thigh_e.head.length) - ex.thigh_hinge_e.translate(Vector(-(mt.hips_e.head.x - mt_chain.thigh_e.head.x), 0.0, 0.0)) - ex.thigh_hinge_e.length = mt.hips_e.length - + # XXX - duplicate below + for bone_class in (mt, mt_chain): + for attr in bone_class.attr_names: + i = METARIG_NAMES.index(attr) + ebone = arm.edit_bones[bone_definition[i]] + setattr(bone_class, attr, ebone.name) + bone_class.update() + # XXX - end dupe # Make a new chain, ORG are the original bones renamed. - fk_chain = mt_chain.copy(from_prefix="ORG-") # fk has no prefix! - ik_chain = fk_chain.copy(to_prefix="MCH-") - - fk_chain.thigh_e.connected = False - fk_chain.thigh_e.parent = ex.thigh_hinge_e - - # fk_chain.thigh_socket_e.parent = MCH-leg_hinge + ik_chain = mt_chain.copy(to_prefix="MCH-") # simple rename ik_chain.rename("thigh", ik_chain.thigh + "_ik") ik_chain.rename("shin", ik_chain.shin + "_ik") # ik foot, no parents - base_foot_name = fk_chain.foot # whatever the foot is called, use that! - ik.foot_e = copy_bone_simple(arm, fk_chain.foot, "%s_ik" % base_foot_name) + base_foot_name = base_names[mt_chain.foot] # whatever the foot is called, use that!, XXX - ORG! + ik.foot_e = copy_bone_simple(arm, mt_chain.foot, "%s_ik" % base_foot_name) ik.foot = ik.foot_e.name ik.foot_e.tail.z = ik.foot_e.head.z ik.foot_e.roll = 0.0 @@ -183,7 +179,7 @@ def main(obj, orig_bone_name): # rename 'MCH-toe' --> to 'toe_ik' and make the child of ik.foot_roll_01 # ------------------ FK or IK? - ik_chain.rename("toe", fk_chain.toe + "_ik") # only fk for the basename + ik_chain.rename("toe", base_names[mt_chain.toe] + "_ik") ik_chain.toe_e.connected = False ik_chain.toe_e.parent = ik.foot_roll_01_e @@ -213,43 +209,6 @@ def main(obj, orig_bone_name): ex.update() mt_chain.update() ik_chain.update() - fk_chain.update() - - con = fk_chain.thigh_p.constraints.new('COPY_LOCATION') - con.target = obj - con.subtarget = ex.thigh_socket - - # hinge - prop = rna_idprop_ui_prop_get(fk_chain.thigh_p, "hinge", create=True) - fk_chain.thigh_p["hinge"] = 0.5 - prop["soft_min"] = 0.0 - prop["soft_max"] = 1.0 - - con = ex.thigh_hinge_p.constraints.new('COPY_ROTATION') - con.target = obj - con.subtarget = mt.hips - - # add driver - hinge_driver_path = fk_chain.thigh_p.path_to_id() + '["hinge"]' - - 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.rna_path = hinge_driver_path - - mod = fcurve.modifiers[0] - mod.poly_order = 1 - mod.coefficients[0] = 1.0 - mod.coefficients[1] = -1.0 - - - # adds constraints to the original bones. - mt_chain.blend(fk_chain, ik_chain, target_bone=ik.foot, target_prop="ik", use_loc=False) - # IK con = ik_chain.shin_p.constraints.new('IK') @@ -290,3 +249,79 @@ def main(obj, orig_bone_name): else: con.minimum_x = -180.0 # XXX -deg con.maximum_x = 0.0 + + return None, ik_chain.thigh, ik_chain.shin, ik_chain.foot, ik_chain.toe, None + + +def fk(obj, bone_definition, base_names): + from Mathutils import Vector + arm = obj.data + + # these account for all bones in METARIG_NAMES + mt_chain = bone_class_instance(obj, ["thigh", "shin", "foot", "toe"]) + mt = bone_class_instance(obj, ["hips", "heel"]) + + # new bones + ex = bone_class_instance(obj, ["thigh_socket", "thigh_hinge"]) + + for bone_class in (mt, mt_chain): + for attr in bone_class.attr_names: + i = METARIG_NAMES.index(attr) + ebone = arm.edit_bones[bone_definition[i]] + setattr(bone_class, attr, ebone.name) + bone_class.update() + + ex.thigh_socket_e = copy_bone_simple(arm, mt_chain.thigh, "MCH-%s_socket" % base_names[mt_chain.thigh], parent=True) + ex.thigh_socket = ex.thigh_socket_e.name + ex.thigh_socket_e.tail = ex.thigh_socket_e.head + Vector(0.0, 0.0, ex.thigh_socket_e.length / 4.0) + + ex.thigh_hinge_e = copy_bone_simple(arm, mt_chain.thigh, "MCH-%s_hinge" % base_names[mt_chain.thigh], parent=True) + ex.thigh_hinge = ex.thigh_hinge_e.name + ex.thigh_hinge_e.tail = ex.thigh_hinge_e.head + Vector(0.0, 0.0, mt_chain.thigh_e.head.length) + ex.thigh_hinge_e.translate(Vector(-(mt.hips_e.head.x - mt_chain.thigh_e.head.x), 0.0, 0.0)) + ex.thigh_hinge_e.length = mt.hips_e.length + + fk_chain = mt_chain.copy() # fk has no prefix! + + fk_chain.thigh_e.connected = False + fk_chain.thigh_e.parent = ex.thigh_hinge_e + + bpy.ops.object.mode_set(mode='OBJECT') + + ex.update() + mt_chain.update() + fk_chain.update() + + con = fk_chain.thigh_p.constraints.new('COPY_LOCATION') + con.target = obj + con.subtarget = ex.thigh_socket + + # hinge + prop = rna_idprop_ui_prop_get(fk_chain.thigh_p, "hinge", create=True) + fk_chain.thigh_p["hinge"] = 0.5 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + + con = ex.thigh_hinge_p.constraints.new('COPY_ROTATION') + con.target = obj + con.subtarget = mt.hips + + # add driver + hinge_driver_path = fk_chain.thigh_p.path_to_id() + '["hinge"]' + + 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.rna_path = hinge_driver_path + + mod = fcurve.modifiers[0] + mod.poly_order = 1 + mod.coefficients[0] = 1.0 + mod.coefficients[1] = -1.0 + + # dont blend the hips or heel + return None, fk_chain.thigh, fk_chain.shin, fk_chain.foot, fk_chain.toe, None diff --git a/release/scripts/modules/rigify/neck.py b/release/scripts/modules/rigify/neck.py index 524de6e2f3d..a075b797c1a 100644 --- a/release/scripts/modules/rigify/neck.py +++ b/release/scripts/modules/rigify/neck.py @@ -20,6 +20,8 @@ import bpy from rigify 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(): bpy.ops.object.mode_set(mode='EDIT') @@ -72,30 +74,43 @@ def metarig_template(): pbone['type'] = 'neck' -def main(obj, orig_bone_name): +def metarig_definition(obj, orig_bone_name): + ''' + The bone given is the head, its parent is the body, + # its only child the first of a chain with matching basenames. + eg. + body -> head -> neck_01 -> neck_02 -> neck_03.... etc + ''' + arm = obj.data + head = arm.bones[orig_bone_name] + body = head.parent + + children = head.children + if len(children) != 1: + print("expected the head to have only 1 child.") + + child = children[0] + bone_definition = [body.name, head.name, child.name] + bone_definition.extend([child.name for child in child.children_recursive_basename]) + return bone_definition + + +def main(obj, bone_definition, base_names): from Mathutils import Vector arm = obj.data # Initialize container classes for convenience mt = bone_class_instance(obj, ["body", "head"]) # meta - mt.head = orig_bone_name - mt.update() - mt.body = mt.head_e.parent.name + mt.body = bone_definition[0] + mt.head = bone_definition[1] mt.update() - # child chain of the 'head' - children = mt.head_e.children - if len(children) != 1: - print("expected the head to have only 1 child.") + neck_chain = bone_definition[2:] - child = children[0] - neck_chain = [child] + child.children_recursive_basename - neck_chain = [child.name for child in neck_chain] - mt_chain = bone_class_instance(obj, [("neck_%.2d" % (i + 1)) for i in range(len(neck_chain))]) # 99 bones enough eh? - for i, child_name in enumerate(neck_chain): - setattr(mt_chain, ("neck_%.2d" % (i + 1)), child_name) + for i, attr in enumerate(mt_chain.attr_names): + setattr(mt_chain, attr, neck_chain[i]) mt_chain.update() neck_chain_basename = mt_chain.neck_01_e.basename @@ -135,8 +150,8 @@ def main(obj, orig_bone_name): mt.head_e.head.y += head_length / 4.0 mt.head_e.tail.y += head_length / 4.0 - for i in range(len(neck_chain)): - neck_e = getattr(mt_chain, "neck_%.2d_e" % (i + 1)) + for i, attr in enumerate(mt_chain.attr_names): + neck_e = getattr(mt_chain, attr + "_e") # dont store parent names, re-reference as each chain bones parent. neck_e_parent = arm.edit_bones.new("MCH-rot_%s" % neck_e.name) @@ -205,9 +220,9 @@ def main(obj, orig_bone_name): target_names = [("b%.2d" % (i + 1)) for i in range(len(neck_chain))] expression_suffix = "/max(0.001,%s)" % "+".join(target_names) - - for i in range(len(neck_chain)): - neck_p = getattr(mt_chain, "neck_%.2d_p" % (i + 1)) + + for i, attr in enumerate(mt_chain.attr_names): + neck_p = getattr(mt_chain, attr + "_p") neck_p.lock_location = True, True, True neck_p.lock_location = True, True, True neck_p.lock_rotations_4d = True @@ -243,3 +258,6 @@ def main(obj, orig_bone_name): tar.id_type = 'OBJECT' tar.id = obj tar.rna_path = head_driver_path + ('["bend_%.2d"]' % (j + 1)) + + # no blending the result of this + return None diff --git a/release/scripts/modules/rigify/palm.py b/release/scripts/modules/rigify/palm.py index b9df113167c..ea90133e8af 100644 --- a/release/scripts/modules/rigify/palm.py +++ b/release/scripts/modules/rigify/palm.py @@ -20,6 +20,8 @@ import bpy from rigify import get_bone_data, copy_bone_simple from rna_prop_ui import rna_idprop_ui_get, rna_idprop_ui_prop_get +# not used, defined for completeness +METARIG_NAMES = tuple() def metarig_template(): bpy.ops.object.mode_set(mode='EDIT') @@ -72,20 +74,39 @@ def metarig_template(): pbone['type'] = 'palm' -def main(obj, orig_bone_name): - arm, palm_pbone, palm_ebone = get_bone_data(obj, orig_bone_name) +def metarig_definition(obj, orig_bone_name): + ''' + The bone given is the first in a chain + Expects an array of children sorted with the little finger lowest. + eg. + parent -> [pinky, ring... etc] + ''' + arm = obj.data + bone_definition = [orig_bone_name] + palm_ebone = arm.bones[orig_bone_name] + children = [ebone.name for ebone in palm_ebone.children] children.sort() # simply assume the pinky has the lowest name + bone_definition.extend(children) + + return bone_definition + + +def main(obj, bone_definition, base_names): + arm, palm_pbone, palm_ebone = get_bone_data(obj, bone_definition[0]) + children = bone_definition[1:] # Make a copy of the pinky + # simply assume the pinky has the lowest name pinky_ebone = arm.edit_bones[children[0]] + ring_ebone = arm.edit_bones[children[1]] + control_ebone = copy_bone_simple(arm, pinky_ebone.name, "palm_control", parent=True) control_name = control_ebone.name - offset = (arm.edit_bones[children[0]].head - arm.edit_bones[children[1]].head) + offset = (pinky_ebone.head - ring_ebone.head) - control_ebone.head += offset - control_ebone.tail += offset + control_ebone.translate(offset) bpy.ops.object.mode_set(mode='OBJECT') @@ -179,4 +200,6 @@ def main(obj, orig_bone_name): child_pbone = obj.pose.bones[children[-1]] child_pbone.rotation_mode = 'QUATERNION' - + + # no blending the result of this + return None diff --git a/release/scripts/modules/rigify/spine.py b/release/scripts/modules/rigify/spine.py index f75575daacb..4fa8d363934 100644 --- a/release/scripts/modules/rigify/spine.py +++ b/release/scripts/modules/rigify/spine.py @@ -20,6 +20,8 @@ import bpy from rigify 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(): bpy.ops.object.mode_set(mode='EDIT') @@ -84,48 +86,59 @@ def metarig_template(): pbone['type'] = 'spine' -def validate(obj, orig_bone_name): +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. ''' - orig_bone = obj.data.bones[orig_bone_name] - if not orig_bone.parent: - return "expected spine bone '%s' to have a parent" % orig_bone_name + arm = obj.data + ribcage = arm.bones[orig_bone_name] + pelvis = ribcage.parent - children = orig_bone.children - + children = ribcage.children if len(children) != 1: - return "expected spine bone '%s' to have only 1 child for the sine chain" % orig_bone_name - - children_spine = children[0].children_recursive_basename + print("expected the ribcage to have only 1 child.") - if len(children_spine) == 0: - return "expected '%s' to define a chain of children with its basename (2 or more)" % children[0] + 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 - return '' +def fk(*args): + main(*args) -def main(obj, orig_bone_name): +def main(obj, bone_definition, base_names): from Mathutils import Vector, Matrix, RotationMatrix from math import radians, pi - + arm = obj.data # Initialize container classes for convenience mt = bone_class_instance(obj, ["pelvis", "ribcage"]) # meta - mt.ribcage = orig_bone_name - mt.update() - mt.pelvis = mt.ribcage_e.parent.name + mt.pelvis = bone_definition[0] + mt.ribcage = bone_definition[1] mt.update() + spine_chain_orig = 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) # probably 'ORG-spine.01' -> 'spine' + spine_chain_len = len(spine_chain_orig) + + ''' children = mt.ribcage_e.children child = children[0] # validate checks for 1 only. spine_chain_basename = child.basename # probably 'spine' spine_chain_segment_length = child.length spine_chain = [child] + child.children_recursive_basename spine_chain_orig = [child.name for child in spine_chain] + ''' + + 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 @@ -177,16 +190,17 @@ def main(obj, orig_bone_name): # - original (ORG_*) # - copy (*use original name*) # - reverse (MCH-rev_*) - spine_chain_attrs = [("spine_%.2d" % (i + 1)) for i in range(len(spine_chain_orig))] + 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 = spine_chain_orig[i] - attr = spine_chain_attrs[i] # eg. spine_04 + attr = mt_chain.attr_names[i] # eg. spine_04 setattr(mt_chain, attr, spine_chain[i]) # use the new name @@ -203,12 +217,12 @@ def main(obj, orig_bone_name): # Now we need to re-parent these chains for i, child_name in enumerate(spine_chain_orig): - attr = spine_chain_attrs[i] + "_e" + attr = ex_chain.attr_names[i] + "_e" if i == 0: getattr(ex_chain, attr).parent = mt.pelvis_e else: - attr_parent = spine_chain_attrs[i-1] + "_e" + attr_parent = ex_chain.attr_names[i-1] + "_e" getattr(ex_chain, attr).parent = getattr(ex_chain, attr_parent) # intentional! get the parent from the other paralelle chain member @@ -217,9 +231,9 @@ def main(obj, orig_bone_name): # ex_chain needs to interlace bones! # Note, skip the first bone - for i in range(1, len(spine_chain_attrs)): # similar to neck + for i in range(1, spine_chain_len): # similar to neck child_name_orig = spine_chain_orig[i] - spine_e = getattr(mt_chain, spine_chain_attrs[i] + "_e") + 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) @@ -227,7 +241,7 @@ def main(obj, orig_bone_name): spine_e_parent.tail = spine_e.head + Vector(0.0, 0.0, spine_chain_segment_length / 2.0) spine_e_parent.roll = 0.0 - spine_e = getattr(ex_chain, spine_chain_attrs[i] + "_e") + 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 @@ -239,8 +253,8 @@ def main(obj, orig_bone_name): # 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 in range(len(spine_chain_attrs)): # similar to neck - spine_e = getattr(rv_chain, spine_chain_attrs[i] + "_e") + 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 @@ -326,12 +340,12 @@ def main(obj, orig_bone_name): # ex.ribcage_p / MCH-wgt_rib_cage con = ex.ribcage_p.constraints.new('COPY_LOCATION') con.target = obj - con.subtarget = getattr(mt_chain, spine_chain_attrs[-1]) + 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, spine_chain_attrs[-1]) + con.subtarget = getattr(mt_chain, mt_chain.attr_names[-1]) # mt.pelvis_p / rib_cage con = mt.ribcage_p.constraints.new('COPY_LOCATION') @@ -347,10 +361,10 @@ def main(obj, orig_bone_name): prop = rna_idprop_ui_prop_get(mt.ribcage_p, "pivot_slide", create=True) mt.ribcage_p["pivot_slide"] = 0.5 - prop["soft_min"] = 1.0 / len(spine_chain_attrs) + prop["soft_min"] = 1.0 / spine_chain_len prop["soft_max"] = 1.0 - for i in range(len(spine_chain_attrs) - 1): + for i in range(spine_chain_len - 1): prop_name = "bend_%.2d" % (i + 1) prop = rna_idprop_ui_prop_get(mt.ribcage_p, prop_name, create=True) mt.ribcage_p[prop_name] = 1.0 @@ -361,9 +375,9 @@ def main(obj, orig_bone_name): # positioned at the tip. # reverse bones / MCH-rev_spine.## - for i in range(1, len(spine_chain_attrs)): - spine_p = getattr(rv_chain, spine_chain_attrs[i] + "_p") - spine_fake_parent_name = getattr(rv_chain, spine_chain_attrs[i - 1]) + 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 @@ -375,14 +389,14 @@ def main(obj, orig_bone_name): # Constrain 'inbetween' bones # b01/max(0.001,b01+b02+b03+b04+b05) - target_names = [("b%.2d" % (i + 1)) for i in range(len(spine_chain_attrs) - 1)] + target_names = [("b%.2d" % (i + 1)) for i in range(spine_chain_len - 1)] expression_suffix = "/max(0.001,%s)" % "+".join(target_names) rib_driver_path = mt.ribcage_p.path_to_id() - for i in range(1, len(spine_chain_attrs)): + for i in range(1, spine_chain_len): - spine_p = getattr(ex_chain, spine_chain_attrs[i] + "_p") + 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') @@ -400,7 +414,7 @@ def main(obj, orig_bone_name): driver.expression = target_names[i - 1] + expression_suffix fcurve.modifiers.remove(0) # grr dont need a modifier - for j in range(len(spine_chain_attrs) - 1): + for j in range(spine_chain_len - 1): tar = driver.targets.new() tar.name = target_names[j] tar.id_type = 'OBJECT' @@ -410,12 +424,12 @@ def main(obj, orig_bone_name): # original bone drivers # note: the first bone has a lot more constraints, but also this simple one is first. - for i in range(len(spine_chain_attrs)): - spine_p = getattr(mt_chain, spine_chain_attrs[i] + "_p") + for i in attr, 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, spine_chain_attrs[i]) # lock to the copy's rotation + con.subtarget = getattr(ex_chain, attr) # lock to the copy's rotation del spine_p # pivot slide: - lots of copy location constraints. @@ -425,19 +439,19 @@ def main(obj, orig_bone_name): con.target = obj con.subtarget = rv_chain.spine_01 # lock to the reverse location - for i in range(1, len(spine_chain_attrs) + 1): + 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 == len(spine_chain_attrs): - attr = spine_chain_attrs[i - 1] + if i == spine_chain_len: + attr = mt_chain.attr_names[i - 1] else: - attr = spine_chain_attrs[i] + attr = mt_chain.attr_names[i] con.subtarget = getattr(rv_chain, attr) # lock to the reverse location - if i == len(spine_chain_attrs): + if i == spine_chain_len: con.head_tail = 1.0 fcurve = con.driver_add("influence", 0) @@ -452,5 +466,8 @@ def main(obj, orig_bone_name): mod = fcurve.modifiers[0] mod.poly_order = 1 mod.coefficients[0] = - (i - 1) - mod.coefficients[1] = len(spine_chain_attrs) + mod.coefficients[1] = spine_chain_len + + # no support for blending chains + return None