split up metarig hierarchy evaluation and modifying the metarig into 2 steps,
authorCampbell Barton <ideasman42@gmail.com>
Sat, 5 Dec 2009 19:26:28 +0000 (19:26 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Sat, 5 Dec 2009 19:26:28 +0000 (19:26 +0000)
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
release/scripts/modules/rigify/arm.py
release/scripts/modules/rigify/delta.py
release/scripts/modules/rigify/finger.py
release/scripts/modules/rigify/leg.py
release/scripts/modules/rigify/neck.py
release/scripts/modules/rigify/palm.py
release/scripts/modules/rigify/spine.py

index 7ae2481619f3a3b6e1b57ad8af73f78546118e86..f6397fa94dc92b12842543f7137b24c18964a22d 100644 (file)
@@ -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()]
+
+        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, {})
 
-        if bone_type == "":
-               continue
+            # 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))
 
-        # submodule = getattr(self, bone_type)
-        # exec("from rigify import %s as submodule")
-        submodule = __import__(name="%s.%s" % (__package__, bone_type), fromlist=[bone_type])
+    
+    # now we have all the info about bones we can start operating on them
+    
+    for pbone in obj.pose.bones:
+        bone_name = pbone.name
+        
+        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')
 
-        reload(submodule) # XXX, dev only
+            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)
         
-        # 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')
+        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))
index 36217ed5247206aa247cebda63d8b0757b2b9393..af2d08307dd86d005932170277315be06c4b3e3f 100644 (file)
@@ -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
index a8458537c121435d96674ba56e78c6cf08fa1bf7..74cfb6150d286e25d2f5f0bca57f5a6169cbb772 100644 (file)
 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
+    delta = arm.bones[orig_bone_name]
+    children = delta.children
+    
+    if len(children) != 1:
+        print("only 1 child supported for delta")
+    
+    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_pbone = obj.pose.bones[delta_name]
-    children = delta_pbone.children
+    delta_name, child_name = bone_definition
     
-    if len(children) != 1:
-        print("only 1 child supported for delta")
-    
-    child_name = children[0].name
+    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
index a9040830e4e587d76ed682a2f87dd245291243fb..c839f20e71f1ae125bd92fbb05105c143bc3f720 100644 (file)
 # ##### 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 = []
+
+    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, orig_bone_name):
+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
index 54dc76dafcfbc63acf7011bdd51db45a3ce15c89..589e295101c3862761c043f8b95a45d5d198aef1 100644 (file)
 # ##### 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
index 524de6e2f3df428d8abb55fce7a3b38a264b0132..a075b797c1a377900c40ebe0a6d0d1e1b9687f2a 100644 (file)
@@ -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
index b9df113167c2cf69861c5453c0b451d490c28a24..ea90133e8af1a50722fd03428dbc9de58224aefb 100644 (file)
@@ -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
index f75575daacb7ba1276263e296546d7c74be91d64..4fa8d3639347356ba5441490f000dfd4fa556c62 100644 (file)
@@ -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