added autorig neck type
authorCampbell Barton <ideasman42@gmail.com>
Tue, 1 Dec 2009 00:44:53 +0000 (00:44 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Tue, 1 Dec 2009 00:44:53 +0000 (00:44 +0000)
new bone properties
- bone.basename (name without the extension), "Some.Bone.001" --> "Some.Bone"
- bone.children_recursive_basename, gives a chain of children that have the same basename

release/scripts/modules/bpy_types.py
release/scripts/modules/rigify/__init__.py
release/scripts/modules/rigify/finger.py
release/scripts/modules/rigify/neck.py [new file with mode: 0644]

index fb1d2978c90589b371d7b67af043e7787f0c98d1..7727d30328f35a92b78df5785d7b541584c4e7d9 100644 (file)
@@ -46,6 +46,7 @@ class _GenericBone:
     functions for bones, common between Armature/Pose/Edit bones.
     internal subclassing use only.
     '''
+    
     def parent_index(self, parent_test):
         '''
         The same as 'bone in other_bone.parent_recursive' but saved generating a list.
@@ -63,6 +64,10 @@ class _GenericBone:
         
         return 0
 
+    @property
+    def basename(self):
+        return self.name.rsplit(".", 1)[0]
+
     @property
     def parent_recursive(self):
         parent_list = []
@@ -96,6 +101,35 @@ class _GenericBone:
         bones_children.sort(key=lambda bone_pair: bone_pair[0])
         return [bone for index, bone in bones_children]
 
+    @property
+    def children_recursive_basename(self):
+        '''
+        Returns a chain of children with the same base name as this bone
+        Only direct chains are supported, forks caused by multiple children with matching basenames will.
+        '''
+        basename = self.basename
+        chain = []
+
+        child = self
+        while True:
+            children = child.children
+            children_basename = []
+
+            for child in children:
+                if basename == child.basename:
+                    children_basename.append(child)
+
+            if len(children_basename) == 1:
+                child = children_basename[0]
+                chain.append(child)
+            else:
+                if len(children_basename):
+                    print("multiple basenames found, this is probably not what you want!", bone.name, children_basename)
+
+                break
+        
+        return chain
+
     @property
     def _other_bones(self):
         id_data = self.id_data
index fa2a8040d0191630dad6f5f70cd33442e3a2b2ea..9d34f9a72ff94c70991ab545d182cb97ebf76c64 100644 (file)
@@ -79,9 +79,6 @@ def get_bone_data(obj, bone_name):
     
     return arm, pbone, bone
 
-def bone_basename(name):
-    return name.split(".")[0]
-
 def copy_bone_simple(arm, from_bone, name, parent=False):
     ebone = arm.edit_bones[from_bone]
     ebone_new = arm.edit_bones.new(name)
index a5e8f2d0ea168e8b15067928a73199d018d63f4a..c71473dca8efcd4de89e8dfc080693bd4acf2017 100644 (file)
@@ -17,7 +17,7 @@
 # ##### END GPL LICENSE BLOCK #####
 
 import bpy
-from rigify import get_bone_data, bone_basename, empty_layer
+from rigify import get_bone_data, empty_layer
 from rna_prop_ui import rna_idprop_ui_get, rna_idprop_ui_prop_get
 from functools import reduce
 
@@ -35,7 +35,7 @@ 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 = bone_basename(orig_pbone.name)
+    base_name = orig_pbone.basename
     
     # first make a new bone at the location of the finger
     control_ebone = arm.edit_bones.new(base_name)
diff --git a/release/scripts/modules/rigify/neck.py b/release/scripts/modules/rigify/neck.py
new file mode 100644 (file)
index 0000000..c9c732b
--- /dev/null
@@ -0,0 +1,194 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+import bpy
+from rigify import bone_class_instance, copy_bone_simple
+from rna_prop_ui import rna_idprop_ui_prop_get
+
+
+def main(obj, orig_bone_name):
+    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.update()
+
+    # child chain of the 'head'
+    children = mt.head_e.children
+    if len(children) != 1:
+        print("expected the head to have only 1 child.")
+
+    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)
+    mt_chain.update()
+
+    neck_chain_basename = mt_chain.neck_01_e.basename
+    neck_chain_segment_length = mt_chain.neck_01_e.length
+    
+    ex = bone_class_instance(obj, ["body", "head", "head_hinge","neck_socket"]) # hinge & extras
+    
+    # Add the head hinge at the bodys location, becomes the parent of the original head
+    
+    
+    # Copy the head bone and offset
+    ex.head_e = copy_bone_simple(arm, mt.head, "MCH_%s" % mt.head, parent=True)
+    ex.head = ex.head_e.name
+    # offset
+    head_length = ex.head_e.length
+    ex.head_e.head.y += head_length / 2.0
+    ex.head_e.tail.y += head_length / 2.0
+    
+    # Yes, use the body bone but call it a head hinge
+    ex.head_hinge_e = copy_bone_simple(arm, mt.body, "MCH_%s_hinge" % mt.head, parent=True)
+    ex.head_hinge = ex.head_hinge_e.name
+    ex.head_hinge_e.head.y += head_length / 4.0
+    ex.head_hinge_e.tail.y += head_length / 4.0
+
+    # reparent the head, assume its not connected
+    mt.head_e.parent = ex.head_hinge_e
+    
+    # Insert the neck socket, the head copys this loation
+    ex.neck_socket_e = arm.edit_bones.new("MCH-%s_socked" % neck_chain_basename)
+    ex.neck_socket = ex.neck_socket_e.name
+    ex.neck_socket_e.parent = mt.body_e
+    ex.neck_socket_e.head = mt.head_e.head
+    ex.neck_socket_e.tail = mt.head_e.head - Vector(0.0, neck_chain_segment_length / 2.0, 0.0)
+    ex.neck_socket_e.roll = 0.0
+    
+    # offset the head, not really needed since it has a copyloc constraint
+    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))
+        
+        # dont store parent names, re-reference as each chain bones parent.
+        neck_e_parent = arm.edit_bones.new("MCH-rot_%s" % neck_e.name)
+        neck_e_parent.head = neck_e.head
+        neck_e_parent.tail = neck_e.head + Vector(0.0, 0.0, neck_chain_segment_length / 2.0)
+        neck_e_parent.roll = 0.0
+        
+        orig_parent = neck_e.parent
+        neck_e.connected = False
+        neck_e.parent = neck_e_parent
+        neck_e_parent.connected = False
+        
+        if i == 0:
+            neck_e_parent.parent = mt.body_e
+        else:
+            neck_e_parent.parent = orig_parent
+    
+    
+    bpy.ops.object.mode_set(mode='OBJECT')
+    
+    mt.update()
+    mt_chain.update()
+    ex.update()
+    
+    # Simple one off constraints, no drivers
+    con = mt.head_p.constraints.new('COPY_LOCATION')
+    con.target = obj
+    con.subtarget = ex.neck_socket
+    
+    con = ex.head_p.constraints.new('COPY_ROTATION')
+    con.target = obj
+    con.subtarget = mt.head
+    
+    # driven hinge
+    prop = rna_idprop_ui_prop_get(mt.head_p, "hinge", create=True)
+    mt.head_p["hinge"] = 0.0
+    prop["soft_min"] = 0.0
+    prop["soft_max"] = 1.0
+    
+    con = ex.head_hinge_p.constraints.new('COPY_ROTATION')
+    con.name = "hinge"
+    con.target = obj
+    con.subtarget = mt.body
+    
+    # add driver
+    hinge_driver_path = mt.head_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_driver.modifiers.new('GENERATOR')
+    mod = fcurve.modifiers[0]
+    mod.poly_order = 1
+    mod.coefficients[0] = 1.0
+    mod.coefficients[1] = -1.0
+    
+    head_driver_path = mt.head_p.path_to_id()
+    
+    # b01/max(0.001,b01+b02+b03+b04+b05)
+    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))
+        neck_p.lock_location = True, True, True
+        neck_p.lock_location = True, True, True
+        neck_p.lock_rotations_4d = True
+        
+        # Add bend prop
+        prop_name = "bend_%.2d" % (i + 1)
+        prop = rna_idprop_ui_prop_get(mt.head_p, prop_name, create=True)
+        mt.head_p[prop_name] = 1.0
+        prop["soft_min"] = 0.0
+        prop["soft_max"] = 1.0
+        
+        # add parent constraint
+        neck_p_parent = neck_p.parent
+        
+        # add constraint
+        con = neck_p_parent.constraints.new('COPY_ROTATION')
+        con.name = "Copy Rotation"
+        con.target = obj
+        con.subtarget = ex.head
+        con.owner_space = 'LOCAL'
+        con.target_space = 'LOCAL'
+        
+        fcurve = con.driver_add("influence", 0)
+        driver = fcurve.driver
+        driver.type = 'SCRIPTED'
+        # b01/max(0.001,b01+b02+b03+b04+b05)
+        driver.expression = target_names[i] + expression_suffix
+        fcurve.modifiers.remove(0) # grr dont need a modifier
+
+        for j in range(len(neck_chain)):
+            tar = driver.targets.new()
+            tar.name = target_names[j]
+            tar.id_type = 'OBJECT'
+            tar.id = obj
+            tar.rna_path = head_driver_path + ('["bend_%.2d"]' % (j + 1))