Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'rigify/rigs/spine.py')
-rw-r--r--rigify/rigs/spine.py484
1 files changed, 484 insertions, 0 deletions
diff --git a/rigify/rigs/spine.py b/rigify/rigs/spine.py
new file mode 100644
index 00000000..37dda1b1
--- /dev/null
+++ b/rigify/rigs/spine.py
@@ -0,0 +1,484 @@
+#====================== BEGIN GPL LICENSE BLOCK ======================
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+#======================= END GPL LICENSE BLOCK ========================
+
+import bpy
+from mathutils import Vector
+from rigify.utils import MetarigError
+from rigify.utils import copy_bone, flip_bone, put_bone
+from rigify.utils import connected_children_names
+from rigify.utils import strip_org, make_mechanism_name, make_deformer_name
+from rigify.utils import obj_to_bone, create_circle_widget
+from rna_prop_ui import rna_idprop_ui_prop_get
+
+
+script = """
+hips = "%s"
+ribs = "%s"
+if is_selected([hips, ribs]):
+ layout.prop(pose_bones[ribs], '["pivot_slide"]', text="Pivot Slide (" + ribs + ")", slider=True)
+if is_selected(ribs):
+ layout.prop(pose_bones[ribs], '["isolate"]', text="Isolate Rotation (" + ribs + ")", slider=True)
+"""
+
+
+class Rig:
+ """ A "spine" rig. It turns a chain of bones into a rig with two controls:
+ One for the hips, and one for the rib cage.
+
+ """
+ def __init__(self, obj, bone_name, params):
+ """ Gather and validate data about the rig.
+
+ """
+ self.obj = obj
+ self.org_bones = [bone_name] + connected_children_names(obj, bone_name)
+ self.params = params
+
+ if len(self.org_bones) <= 1:
+ raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of 2 or more bones." % (strip_org(bone)))
+
+ def gen_deform(self):
+ """ Generate the deformation rig.
+
+ """
+ for name in self.org_bones:
+ bpy.ops.object.mode_set(mode='EDIT')
+ eb = self.obj.data.edit_bones
+
+ # Create deform bone
+ bone_e = eb[copy_bone(self.obj, name)]
+
+ # Change its name
+ bone_e.name = make_deformer_name(strip_org(name))
+ bone_name = bone_e.name
+
+ # Leave edit mode
+ bpy.ops.object.mode_set(mode='OBJECT')
+
+ # Get the pose bone
+ bone = self.obj.pose.bones[bone_name]
+
+ # Constrain to the original bone
+ con = bone.constraints.new('COPY_TRANSFORMS')
+ con.name = "copy_transforms"
+ con.target = self.obj
+ con.subtarget = name
+
+ def gen_control(self):
+ """ Generate the control rig.
+
+ """
+ #---------------------------------
+ # Create the hip and rib controls
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ # Copy org bones
+ hip_control = copy_bone(self.obj, self.org_bones[0], strip_org(self.org_bones[0]))
+ rib_control = copy_bone(self.obj, self.org_bones[-1], strip_org(self.org_bones[-1]))
+ rib_mch = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[-1] + ".follow")))
+ hinge = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(self.org_bones[-1]) + ".hinge"))
+
+ eb = self.obj.data.edit_bones
+
+ hip_control_e = eb[hip_control]
+ rib_control_e = eb[rib_control]
+ rib_mch_e = eb[rib_mch]
+ hinge_e = eb[hinge]
+
+ # Parenting
+ hip_control_e.use_connect = False
+ rib_control_e.use_connect = False
+ rib_mch_e.use_connect = False
+ hinge_e.use_connect = False
+
+ hinge_e.parent = None
+ rib_control_e.parent = hinge_e
+ rib_mch_e.parent = rib_control_e
+
+ # Position
+ flip_bone(self.obj, hip_control)
+ flip_bone(self.obj, hinge)
+
+ hinge_e.length /= 2
+ rib_mch_e.length /= 2
+
+ put_bone(self.obj, rib_control, hip_control_e.head)
+ put_bone(self.obj, rib_mch, hip_control_e.head)
+
+ bpy.ops.object.mode_set(mode='POSE')
+ bpy.ops.object.mode_set(mode='EDIT')
+ eb = self.obj.data.edit_bones
+
+ # Switch to object mode
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+ hip_control_p = pb[hip_control]
+ rib_control_p = pb[rib_control]
+ hinge_p = pb[hinge]
+
+ # No translation on rib control
+ rib_control_p.lock_location = [True, True, True]
+
+ # Hip does not use local location
+ hip_control_p.bone.use_local_location = False
+
+ # Custom hinge property
+ prop = rna_idprop_ui_prop_get(rib_control_p, "isolate", create=True)
+ rib_control_p["isolate"] = 1.0
+ prop["soft_min"] = prop["min"] = 0.0
+ prop["soft_max"] = prop["max"] = 1.0
+
+ # Constraints
+ con = hinge_p.constraints.new('COPY_LOCATION')
+ con.name = "copy_location"
+ con.target = self.obj
+ con.subtarget = hip_control
+
+ con1 = hinge_p.constraints.new('COPY_ROTATION')
+ con1.name = "isolate_off.01"
+ con1.target = self.obj
+ con1.subtarget = hip_control
+
+ con2 = rib_control_p.constraints.new('COPY_SCALE')
+ con2.name = "isolate_off.02"
+ con2.target = self.obj
+ con2.subtarget = hip_control
+ con2.use_offset = True
+ con2.target_space = 'LOCAL'
+ con2.owner_space = 'LOCAL'
+
+ # Drivers for "isolate_off"
+ fcurve = con1.driver_add("influence")
+ driver = fcurve.driver
+ var = driver.variables.new()
+ driver.type = 'AVERAGE'
+ var.name = "var"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = rib_control_p.path_from_id() + '["isolate"]'
+ mod = fcurve.modifiers[0]
+ mod.poly_order = 1
+ mod.coefficients[0] = 1.0
+ mod.coefficients[1] = -1.0
+
+ fcurve = con2.driver_add("influence")
+ driver = fcurve.driver
+ var = driver.variables.new()
+ driver.type = 'AVERAGE'
+ var.name = "var"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = rib_control_p.path_from_id() + '["isolate"]'
+ mod = fcurve.modifiers[0]
+ mod.poly_order = 1
+ mod.coefficients[0] = 1.0
+ mod.coefficients[1] = -1.0
+
+ # Appearence
+ hip_control_p.custom_shape_transform = pb[self.org_bones[0]]
+ rib_control_p.custom_shape_transform = pb[self.org_bones[-1]]
+
+ #-------------------------
+ # Create flex spine chain
+
+ # Create bones/parenting/positiong
+ bpy.ops.object.mode_set(mode='EDIT')
+ flex_bones = []
+ flex_helpers = []
+ prev_bone = None
+ for b in self.org_bones:
+ # Create bones
+ bone = copy_bone(self.obj, b, make_mechanism_name(strip_org(b) + ".flex"))
+ helper = copy_bone(self.obj, rib_mch, make_mechanism_name(strip_org(b) + ".flex_h"))
+ flex_bones += [bone]
+ flex_helpers += [helper]
+
+ eb = self.obj.data.edit_bones
+ bone_e = eb[bone]
+ helper_e = eb[helper]
+
+ # Parenting
+ bone_e.use_connect = False
+ helper_e.use_connect = False
+ if prev_bone is None:
+ helper_e.parent = eb[hip_control]
+ bone_e.parent = helper_e
+
+ # Position
+ put_bone(self.obj, helper, bone_e.head)
+ helper_e.length /= 4
+
+ prev_bone = bone
+
+ # Constraints
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+ rib_control_p = pb[rib_control]
+ rib_mch_p = pb[rib_mch]
+
+ inc = 1.0 / (len(flex_helpers) - 1)
+ inf = 1.0 / (len(flex_helpers) - 1)
+ for b in zip(flex_helpers[1:], flex_bones[:-1], self.org_bones[1:]):
+ bone_p = pb[b[0]]
+
+ # Scale constraints
+ con = bone_p.constraints.new('COPY_SCALE')
+ con.name = "copy_scale1"
+ con.target = self.obj
+ con.subtarget = flex_helpers[0]
+ con.influence = 1.0
+
+ con = bone_p.constraints.new('COPY_SCALE')
+ con.name = "copy_scale2"
+ con.target = self.obj
+ con.subtarget = rib_mch
+ con.influence = inf
+
+ # Bend constraints
+ con = bone_p.constraints.new('COPY_ROTATION')
+ con.name = "bend1"
+ con.target = self.obj
+ con.subtarget = flex_helpers[0]
+ con.influence = 1.0
+
+ con = bone_p.constraints.new('COPY_ROTATION')
+ con.name = "bend2"
+ con.target = self.obj
+ con.subtarget = rib_mch
+ con.influence = inf
+
+ # If not the rib control
+ if b[0] != flex_helpers[-1]:
+ # Custom bend property
+ prop_name = "bend_" + strip_org(b[2])
+ prop = rna_idprop_ui_prop_get(rib_control_p, prop_name, create=True)
+ rib_control_p[prop_name] = inf
+ prop["min"] = 0.0
+ prop["max"] = 1.0
+ prop["soft_min"] = 0.0
+ prop["soft_max"] = 1.0
+
+ # Bend driver
+ fcurve = con.driver_add("influence")
+ driver = fcurve.driver
+ var = driver.variables.new()
+ driver.type = 'AVERAGE'
+ var.name = prop_name
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = rib_control_p.path_from_id() + '["' + prop_name + '"]'
+
+ # Location constraint
+ con = bone_p.constraints.new('COPY_LOCATION')
+ con.name = "copy_location"
+ con.target = self.obj
+ con.subtarget = b[1]
+ con.head_tail = 1.0
+
+ inf += inc
+
+ #----------------------------
+ # Create reverse spine chain
+
+ # Create bones/parenting/positioning
+ bpy.ops.object.mode_set(mode='EDIT')
+ rev_bones = []
+ prev_bone = None
+ for b in zip(flex_bones, self.org_bones):
+ # Create bones
+ bone = copy_bone(self.obj, b[1], make_mechanism_name(strip_org(b[1]) + ".reverse"))
+ rev_bones += [bone]
+ eb = self.obj.data.edit_bones
+ bone_e = eb[bone]
+
+ # Parenting
+ bone_e.use_connect = False
+ bone_e.parent = eb[b[0]]
+
+ # Position
+ flip_bone(self.obj, bone)
+ bone_e.tail = Vector(eb[b[0]].head)
+ #bone_e.head = Vector(eb[b[0]].tail)
+ if prev_bone is None:
+ pass # Position base bone wherever you want, for now do nothing (i.e. position at hips)
+ else:
+ put_bone(self.obj, bone, eb[prev_bone].tail)
+
+ prev_bone = bone
+
+ # Constraints
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+ prev_bone = None
+ for bone in rev_bones:
+ bone_p = pb[bone]
+
+ con = bone_p.constraints.new('COPY_LOCATION')
+ con.name = "copy_location"
+ con.target = self.obj
+ if prev_bone is None:
+ con.subtarget = hip_control # Position base bone wherever you want, for now hips
+ else:
+ con.subtarget = prev_bone
+ con.head_tail = 1.0
+ prev_bone = bone
+
+ #---------------------------------------------
+ # Constrain org bones to flex bone's rotation
+ pb = self.obj.pose.bones
+ for b in zip(self.org_bones, flex_bones):
+ con = pb[b[0]].constraints.new('COPY_TRANSFORMS')
+ con.name = "copy_rotation"
+ con.target = self.obj
+ con.subtarget = b[1]
+
+ #---------------------------
+ # Create pivot slide system
+ pb = self.obj.pose.bones
+ bone_p = pb[self.org_bones[0]]
+ rib_control_p = pb[rib_control]
+
+ # Custom pivot_slide property
+ prop = rna_idprop_ui_prop_get(rib_control_p, "pivot_slide", create=True)
+ rib_control_p["pivot_slide"] = 1.0 / len(self.org_bones)
+ prop["min"] = 0.0
+ prop["max"] = 1.0
+ prop["soft_min"] = 1.0 / len(self.org_bones)
+ prop["soft_max"] = 1.0 - (1.0 / len(self.org_bones))
+
+ # Anchor constraint
+ con = bone_p.constraints.new('COPY_LOCATION')
+ con.name = "copy_location"
+ con.target = self.obj
+ con.subtarget = rev_bones[0]
+
+ # Slide constraints
+ i = 1
+ tot = len(rev_bones)
+ for rb in rev_bones:
+ con = bone_p.constraints.new('COPY_LOCATION')
+ con.name = "slide." + str(i)
+ con.target = self.obj
+ con.subtarget = rb
+ con.head_tail = 1.0
+
+ # Driver
+ fcurve = con.driver_add("influence")
+ driver = fcurve.driver
+ var = driver.variables.new()
+ driver.type = 'AVERAGE'
+ var.name = "slide"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = rib_control_p.path_from_id() + '["pivot_slide"]'
+ mod = fcurve.modifiers[0]
+ mod.poly_order = 1
+ mod.coefficients[0] = 1 - i
+ mod.coefficients[1] = tot
+
+ i += 1
+
+ # Create control widgets
+ w1 = create_circle_widget(self.obj, hip_control, radius=1.0, head_tail=1.0)
+ w2 = create_circle_widget(self.obj, rib_control, radius=1.0, head_tail=0.0)
+
+ if w1 != None:
+ obj_to_bone(w1, self.obj, self.org_bones[0])
+ if w2 != None:
+ obj_to_bone(w2, self.obj, self.org_bones[-1])
+
+ # Return control names
+ return hip_control, rib_control
+
+ def generate(self):
+ """ Generate the rig.
+ Do NOT modify any of the original bones, except for adding constraints.
+ The main armature should be selected and active before this is called.
+
+ """
+ self.gen_deform()
+ hips, ribs = self.gen_control()
+
+ return [script % (hips, ribs)]
+
+ @classmethod
+ def create_sample(self, obj):
+ # generated by rigify.utils.write_metarig
+ bpy.ops.object.mode_set(mode='EDIT')
+ arm = obj.data
+
+ bones = {}
+
+ bone = arm.edit_bones.new('hips')
+ bone.head[:] = 0.0000, 0.0000, 0.0000
+ bone.tail[:] = -0.0000, -0.0590, 0.2804
+ bone.roll = -0.0000
+ bone.use_connect = False
+ bones['hips'] = bone.name
+ bone = arm.edit_bones.new('spine')
+ bone.head[:] = -0.0000, -0.0590, 0.2804
+ bone.tail[:] = 0.0000, 0.0291, 0.5324
+ bone.roll = 0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['hips']]
+ bones['spine'] = bone.name
+ bone = arm.edit_bones.new('ribs')
+ bone.head[:] = 0.0000, 0.0291, 0.5324
+ bone.tail[:] = -0.0000, 0.0000, 1.0000
+ bone.roll = -0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['spine']]
+ bones['ribs'] = bone.name
+
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pbone = obj.pose.bones[bones['hips']]
+ pbone.rigify_type = 'spine'
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone = obj.pose.bones[bones['spine']]
+ pbone.rigify_type = ''
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone = obj.pose.bones[bones['ribs']]
+ pbone.rigify_type = ''
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone = obj.pose.bones[bones['hips']]
+ pbone['rigify_type'] = 'spine'
+
+ bpy.ops.object.mode_set(mode='EDIT')
+ for bone in arm.edit_bones:
+ bone.select = False
+ bone.select_head = False
+ bone.select_tail = False
+ for b in bones:
+ bone = arm.edit_bones[bones[b]]
+ bone.select = True
+ bone.select_head = True
+ bone.select_tail = True
+ arm.edit_bones.active = bone
+