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')
-rw-r--r--rigify/rigs/__init__.py0
-rw-r--r--rigify/rigs/biped/__init__.py0
-rw-r--r--rigify/rigs/biped/arm/__init__.py209
-rw-r--r--rigify/rigs/biped/arm/deform.py230
-rw-r--r--rigify/rigs/biped/arm/fk.py208
-rw-r--r--rigify/rigs/biped/arm/ik.py307
-rw-r--r--rigify/rigs/biped/leg/__init__.py237
-rw-r--r--rigify/rigs/biped/leg/deform.py264
-rw-r--r--rigify/rigs/biped/leg/fk.py246
-rw-r--r--rigify/rigs/biped/leg/ik.py520
-rw-r--r--rigify/rigs/copy.py114
-rw-r--r--rigify/rigs/finger.py413
-rw-r--r--rigify/rigs/misc/__init__.py0
-rw-r--r--rigify/rigs/misc/delta.py161
-rw-r--r--rigify/rigs/neck_short.py385
-rw-r--r--rigify/rigs/palm.py273
-rw-r--r--rigify/rigs/spine.py484
17 files changed, 4051 insertions, 0 deletions
diff --git a/rigify/rigs/__init__.py b/rigify/rigs/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/rigify/rigs/__init__.py
diff --git a/rigify/rigs/biped/__init__.py b/rigify/rigs/biped/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/rigify/rigs/biped/__init__.py
diff --git a/rigify/rigs/biped/arm/__init__.py b/rigify/rigs/biped/arm/__init__.py
new file mode 100644
index 00000000..13d9e430
--- /dev/null
+++ b/rigify/rigs/biped/arm/__init__.py
@@ -0,0 +1,209 @@
+#====================== 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
+import imp
+from . import fk, ik, deform
+from rigify.utils import MetarigError, get_layers
+
+imp.reload(fk)
+imp.reload(ik)
+imp.reload(deform)
+
+script = """
+fk_arm = ["%s", "%s", "%s"]
+ik_arm = ["%s", "%s"]
+if is_selected(fk_arm+ik_arm):
+ layout.prop(pose_bones[ik_arm[0]], '["ikfk_switch"]', text="FK / IK (" + ik_arm[0] + ")", slider=True)
+if is_selected(fk_arm):
+ layout.prop(pose_bones[fk_arm[0]], '["isolate"]', text="Isolate Rotation (" + fk_arm[0] + ")", slider=True)
+"""
+
+
+class Rig:
+ """ An arm rig, with IK/FK switching and hinge switch.
+
+ """
+ def __init__(self, obj, bone, params):
+ """ Gather and validate data about the rig.
+ Store any data or references to data that will be needed later on.
+ In particular, store names of bones that will be needed.
+ Do NOT change any data in the scene. This is a gathering phase only.
+
+ """
+ # Gather deform rig
+ self.deform_rig = deform.Rig(obj, bone, params)
+
+ # Gather FK rig
+ self.fk_rig = fk.Rig(obj, bone, params)
+
+ # Gather IK rig
+ self.ik_rig = ik.Rig(obj, bone, params, ikfk_switch=True)
+
+ 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.deform_rig.generate()
+ fk_controls = self.fk_rig.generate()
+ ik_controls = self.ik_rig.generate()
+ return [script % (fk_controls[0], fk_controls[1], fk_controls[2], ik_controls[0], ik_controls[1])]
+
+ @classmethod
+ def add_parameters(self, group):
+ """ Add the parameters of this rig type to the
+ RigifyParameters IDPropertyGroup
+
+ """
+ items = [('X', 'X', ''), ('Y', 'Y', ''), ('Z', 'Z', ''), ('-X', '-X', ''), ('-Y', '-Y', ''), ('-Z', '-Z', '')]
+ group.primary_rotation_axis = bpy.props.EnumProperty(items=items, name="Primary Rotation Axis", default='X')
+
+ group.separate_ik_layers = bpy.props.BoolProperty(name="Separate IK Control Layers:", default=False, description="Enable putting the ik controls on a separate layer from the fk controls.")
+ group.ik_layers = bpy.props.BoolVectorProperty(size=32, description="Layers for the ik controls to be on.")
+
+ group.use_upper_arm_twist = bpy.props.BoolProperty(name="Upper Arm Twist", default=True, description="Generate the dual-bone twist setup for the upper arm.")
+ group.use_forearm_twist = bpy.props.BoolProperty(name="Forearm Twist", default=True, description="Generate the dual-bone twist setup for the forearm.")
+
+ @classmethod
+ def parameters_ui(self, layout, obj, bone):
+ """ Create the ui for the rig parameters.
+
+ """
+ params = obj.pose.bones[bone].rigify_parameters[0]
+
+ r = layout.row()
+ r.prop(params, "separate_ik_layers")
+
+ r = layout.row()
+ r.active = params.separate_ik_layers
+
+ col = r.column(align=True)
+ row = col.row(align=True)
+ row.prop(params, "ik_layers", index=0, toggle=True, text="")
+ row.prop(params, "ik_layers", index=1, toggle=True, text="")
+ row.prop(params, "ik_layers", index=2, toggle=True, text="")
+ row.prop(params, "ik_layers", index=3, toggle=True, text="")
+ row.prop(params, "ik_layers", index=4, toggle=True, text="")
+ row.prop(params, "ik_layers", index=5, toggle=True, text="")
+ row.prop(params, "ik_layers", index=6, toggle=True, text="")
+ row.prop(params, "ik_layers", index=7, toggle=True, text="")
+ row = col.row(align=True)
+ row.prop(params, "ik_layers", index=16, toggle=True, text="")
+ row.prop(params, "ik_layers", index=17, toggle=True, text="")
+ row.prop(params, "ik_layers", index=18, toggle=True, text="")
+ row.prop(params, "ik_layers", index=19, toggle=True, text="")
+ row.prop(params, "ik_layers", index=20, toggle=True, text="")
+ row.prop(params, "ik_layers", index=21, toggle=True, text="")
+ row.prop(params, "ik_layers", index=22, toggle=True, text="")
+ row.prop(params, "ik_layers", index=23, toggle=True, text="")
+
+ col = r.column(align=True)
+ row = col.row(align=True)
+ row.prop(params, "ik_layers", index=8, toggle=True, text="")
+ row.prop(params, "ik_layers", index=9, toggle=True, text="")
+ row.prop(params, "ik_layers", index=10, toggle=True, text="")
+ row.prop(params, "ik_layers", index=11, toggle=True, text="")
+ row.prop(params, "ik_layers", index=12, toggle=True, text="")
+ row.prop(params, "ik_layers", index=13, toggle=True, text="")
+ row.prop(params, "ik_layers", index=14, toggle=True, text="")
+ row.prop(params, "ik_layers", index=15, toggle=True, text="")
+ row = col.row(align=True)
+ row.prop(params, "ik_layers", index=24, toggle=True, text="")
+ row.prop(params, "ik_layers", index=25, toggle=True, text="")
+ row.prop(params, "ik_layers", index=26, toggle=True, text="")
+ row.prop(params, "ik_layers", index=27, toggle=True, text="")
+ row.prop(params, "ik_layers", index=28, toggle=True, text="")
+ row.prop(params, "ik_layers", index=29, toggle=True, text="")
+ row.prop(params, "ik_layers", index=30, toggle=True, text="")
+ row.prop(params, "ik_layers", index=31, toggle=True, text="")
+
+ r = layout.row()
+ r.label(text="Elbow rotation axis:")
+ r.prop(params, "primary_rotation_axis", text="")
+
+ col = layout.column()
+ col.prop(params, "use_upper_arm_twist")
+ col.prop(params, "use_forearm_twist")
+
+ @classmethod
+ def create_sample(self, obj):
+ # generated by rigify.utils.write_meta_rig
+ bpy.ops.object.mode_set(mode='EDIT')
+ arm = obj.data
+
+ bones = {}
+
+ bone = arm.edit_bones.new('upper_arm')
+ bone.head[:] = 0.0000, 0.0000, 0.0000
+ bone.tail[:] = 0.3000, 0.0300, 0.0000
+ bone.roll = 1.5708
+ bone.use_connect = False
+ bones['upper_arm'] = bone.name
+ bone = arm.edit_bones.new('forearm')
+ bone.head[:] = 0.3000, 0.0300, 0.0000
+ bone.tail[:] = 0.6000, 0.0000, 0.0000
+ bone.roll = 1.5708
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['upper_arm']]
+ bones['forearm'] = bone.name
+ bone = arm.edit_bones.new('hand')
+ bone.head[:] = 0.6000, 0.0000, 0.0000
+ bone.tail[:] = 0.7000, 0.0000, 0.0000
+ bone.roll = 3.1416
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['forearm']]
+ bones['hand'] = bone.name
+
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pbone = obj.pose.bones[bones['upper_arm']]
+ pbone.rigify_type = 'biped.arm'
+ pbone.lock_location = (True, True, True)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone.rigify_parameters.add()
+ pbone = obj.pose.bones[bones['forearm']]
+ 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['hand']]
+ 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'
+
+ 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
+
diff --git a/rigify/rigs/biped/arm/deform.py b/rigify/rigs/biped/arm/deform.py
new file mode 100644
index 00000000..2a7b3109
--- /dev/null
+++ b/rigify/rigs/biped/arm/deform.py
@@ -0,0 +1,230 @@
+#====================== 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 math import acos, degrees
+from mathutils import Vector, Matrix
+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
+
+
+def align_roll(obj, bone1, bone2):
+ bone1_e = obj.data.edit_bones[bone1]
+ bone2_e = obj.data.edit_bones[bone2]
+
+ bone1_e.roll = 0.0
+
+ # Get the directions the bones are pointing in, as vectors
+ y1 = bone1_e.y_axis
+ x1 = bone1_e.x_axis
+ y2 = bone2_e.y_axis
+ x2 = bone2_e.x_axis
+
+ # Get the shortest axis to rotate bone1 on to point in the same direction as bone2
+ axis = y1.cross(y2)
+ axis.normalize()
+
+ # Angle to rotate on that shortest axis
+ angle = y1.angle(y2)
+
+ # Create rotation matrix to make bone1 point in the same direction as bone2
+ rot_mat = Matrix.Rotation(angle, 3, axis)
+
+ # Roll factor
+ x3 = x1 * rot_mat
+ dot = x2 * x3
+ if dot > 1.0:
+ dot = 1.0
+ elif dot < -1.0:
+ dot = -1.0
+ roll = acos(dot)
+
+ # Set the roll
+ bone1_e.roll = roll
+
+ # Check if we rolled in the right direction
+ x3 = bone1_e.x_axis * rot_mat
+ check = x2 * x3
+
+ # If not, reverse
+ if check < 0.9999:
+ bone1_e.roll = -roll
+
+
+class Rig:
+ """ An FK arm rig, with hinge switch.
+
+ """
+ def __init__(self, obj, bone, params):
+ """ Gather and validate data about the rig.
+ Store any data or references to data that will be needed later on.
+ In particular, store references to bones that will be needed, and
+ store names of bones that will be needed.
+ Do NOT change any data in the scene. This is a gathering phase only.
+
+ """
+ self.obj = obj
+ self.params = params
+
+ # Get the chain of 3 connected bones
+ self.org_bones = [bone] + connected_children_names(self.obj, bone)[:2]
+
+ if len(self.org_bones) != 3:
+ raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of 3 bones." % (strip_org(bone)))
+
+ # Get rig parameters
+ self.use_upper_arm_twist = params.use_upper_arm_twist
+ self.use_forearm_twist = params.use_forearm_twist
+
+ 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.
+
+ """
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ # Create upper arm bones
+ if self.use_upper_arm_twist:
+ uarm1 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0] + ".01")))
+ uarm2 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0] + ".02")))
+ utip = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(self.org_bones[0] + ".tip")))
+ else:
+ uarm = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0])))
+
+ # Create forearm bones
+ if self.use_forearm_twist:
+ farm1 = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1] + ".01")))
+ farm2 = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1] + ".02")))
+ ftip = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(self.org_bones[1] + ".tip")))
+ else:
+ farm = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1])))
+
+ # Create hand bone
+ hand = copy_bone(self.obj, self.org_bones[2], make_deformer_name(strip_org(self.org_bones[2])))
+
+ # Get edit bones
+ eb = self.obj.data.edit_bones
+
+ org_uarm_e = eb[self.org_bones[0]]
+ if self.use_upper_arm_twist:
+ uarm1_e = eb[uarm1]
+ uarm2_e = eb[uarm2]
+ utip_e = eb[utip]
+ else:
+ uarm_e = eb[uarm]
+
+ org_farm_e = eb[self.org_bones[1]]
+ if self.use_forearm_twist:
+ farm1_e = eb[farm1]
+ farm2_e = eb[farm2]
+ ftip_e = eb[ftip]
+ else:
+ farm_e = eb[farm]
+
+ org_hand_e = eb[self.org_bones[2]]
+ hand_e = eb[hand]
+
+ # Parent and position upper arm bones
+ if self.use_upper_arm_twist:
+ uarm1_e.use_connect = False
+ uarm2_e.use_connect = False
+ utip_e.use_connect = False
+
+ uarm1_e.parent = org_uarm_e.parent
+ uarm2_e.parent = org_uarm_e
+ utip_e.parent = org_uarm_e
+
+ center = Vector((org_uarm_e.head + org_uarm_e.tail) / 2)
+
+ uarm1_e.tail = center
+ uarm2_e.head = center
+ put_bone(self.obj, utip, org_uarm_e.tail)
+ utip_e.length = org_uarm_e.length / 8
+ else:
+ uarm_e.use_connect = False
+ uarm_e.parent = org_uarm_e
+
+ # Parent and position forearm bones
+ if self.use_forearm_twist:
+ farm1_e.use_connect = False
+ farm2_e.use_connect = False
+ ftip_e.use_connect = False
+
+ farm1_e.parent = org_farm_e
+ farm2_e.parent = org_farm_e
+ ftip_e.parent = org_farm_e
+
+ center = Vector((org_farm_e.head + org_farm_e.tail) / 2)
+
+ farm1_e.tail = center
+ farm2_e.head = center
+ put_bone(self.obj, ftip, org_farm_e.tail)
+ ftip_e.length = org_farm_e.length / 8
+
+ # Align roll of farm2 with hand
+ align_roll(self.obj, farm2, hand)
+ else:
+ farm_e.use_connect = False
+ farm_e.parent = org_farm_e
+
+ # Parent hand
+ hand_e.use_connect = False
+ hand_e.parent = org_hand_e
+
+ # Object mode, get pose bones
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+
+ if self.use_upper_arm_twist:
+ uarm1_p = pb[uarm1]
+ if self.use_forearm_twist:
+ farm2_p = pb[farm2]
+ hand_p = pb[hand]
+
+ # Upper arm constraints
+ if self.use_upper_arm_twist:
+ con = uarm1_p.constraints.new('COPY_LOCATION')
+ con.name = "copy_location"
+ con.target = self.obj
+ con.subtarget = self.org_bones[0]
+
+ con = uarm1_p.constraints.new('COPY_SCALE')
+ con.name = "copy_scale"
+ con.target = self.obj
+ con.subtarget = self.org_bones[0]
+
+ con = uarm1_p.constraints.new('DAMPED_TRACK')
+ con.name = "track_to"
+ con.target = self.obj
+ con.subtarget = utip
+
+ # Forearm constraints
+ if self.use_forearm_twist:
+ con = farm2_p.constraints.new('COPY_ROTATION')
+ con.name = "copy_rotation"
+ con.target = self.obj
+ con.subtarget = hand
+
+ con = farm2_p.constraints.new('DAMPED_TRACK')
+ con.name = "track_to"
+ con.target = self.obj
+ con.subtarget = ftip
+
diff --git a/rigify/rigs/biped/arm/fk.py b/rigify/rigs/biped/arm/fk.py
new file mode 100644
index 00000000..20ba89f2
--- /dev/null
+++ b/rigify/rigs/biped/arm/fk.py
@@ -0,0 +1,208 @@
+#====================== 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
+import math
+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 get_layers
+from rigify.utils import create_widget, create_limb_widget
+from rna_prop_ui import rna_idprop_ui_prop_get
+
+
+class Rig:
+ """ An FK arm rig, with hinge switch.
+
+ """
+ def __init__(self, obj, bone, params):
+ """ Gather and validate data about the rig.
+ Store any data or references to data that will be needed later on.
+ In particular, store references to bones that will be needed, and
+ store names of bones that will be needed.
+ Do NOT change any data in the scene. This is a gathering phase only.
+
+ """
+ self.obj = obj
+ self.params = params
+
+ # Get the chain of 3 connected bones
+ self.org_bones = [bone] + connected_children_names(self.obj, bone)[:2]
+
+ if len(self.org_bones) != 3:
+ raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of 3 bones." % (strip_org(bone)))
+
+ # Get (optional) parent
+ if self.obj.data.bones[bone].parent == None:
+ self.org_parent = None
+ else:
+ self.org_parent = self.obj.data.bones[bone].parent.name
+
+ # Get the rig parameters
+ if "layers" in params:
+ self.layers = get_layers(params["layers"])
+ else:
+ self.layers = None
+
+ self.primary_rotation_axis = params.primary_rotation_axis
+
+ 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.
+
+ """
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ # Create the control bones
+ uarm = copy_bone(self.obj, self.org_bones[0], strip_org(self.org_bones[0]))
+ farm = copy_bone(self.obj, self.org_bones[1], strip_org(self.org_bones[1]))
+ hand = copy_bone(self.obj, self.org_bones[2], strip_org(self.org_bones[2]))
+
+ # Create the hinge bones
+ if self.org_parent != None:
+ hinge = copy_bone(self.obj, self.org_parent, make_mechanism_name(uarm + ".hinge"))
+ socket1 = copy_bone(self.obj, uarm, make_mechanism_name(uarm + ".socket1"))
+ socket2 = copy_bone(self.obj, uarm, make_mechanism_name(uarm + ".socket2"))
+
+ # Get edit bones
+ eb = self.obj.data.edit_bones
+
+ uarm_e = eb[uarm]
+ farm_e = eb[farm]
+ hand_e = eb[hand]
+
+ if self.org_parent != None:
+ hinge_e = eb[hinge]
+ socket1_e = eb[socket1]
+ socket2_e = eb[socket2]
+
+ # Parenting
+ farm_e.parent = uarm_e
+ hand_e.parent = farm_e
+
+ if self.org_parent != None:
+ hinge_e.use_connect = False
+ socket1_e.use_connect = False
+ socket2_e.use_connect = False
+
+ uarm_e.parent = hinge_e
+ hinge_e.parent = socket2_e
+ socket2_e.parent = None
+
+ # Positioning
+ if self.org_parent != None:
+ center = (hinge_e.head + hinge_e.tail) / 2
+ hinge_e.head = center
+ socket1_e.length /= 4
+ socket2_e.length /= 3
+
+ # Object mode, get pose bones
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+
+ uarm_p = pb[uarm]
+ farm_p = pb[farm]
+ hand_p = pb[hand]
+
+ if self.org_parent != None:
+ socket1_p = pb[socket1]
+ socket2_p = pb[socket2]
+
+ # Set the elbow to only bend on the x-axis.
+ farm_p.rotation_mode = 'XYZ'
+ if 'X' in self.primary_rotation_axis:
+ farm_p.lock_rotation = (False, True, True)
+ elif 'Y' in self.primary_rotation_axis:
+ farm_p.lock_rotation = (True, False, True)
+ else:
+ farm_p.lock_rotation = (True, True, False)
+
+ # Set up custom properties
+ if self.org_parent != None:
+ prop = rna_idprop_ui_prop_get(uarm_p, "isolate", create=True)
+ uarm_p["isolate"] = 0.0
+ prop["soft_min"] = prop["min"] = 0.0
+ prop["soft_max"] = prop["max"] = 1.0
+
+ # Hinge constraints / drivers
+ if self.org_parent != None:
+ con = socket2_p.constraints.new('COPY_LOCATION')
+ con.name = "copy_location"
+ con.target = self.obj
+ con.subtarget = socket1
+
+ con = socket2_p.constraints.new('COPY_TRANSFORMS')
+ con.name = "isolate_off"
+ con.target = self.obj
+ con.subtarget = socket1
+
+ # Driver
+ fcurve = con.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 = uarm_p.path_from_id() + '["isolate"]'
+ mod = fcurve.modifiers[0]
+ mod.poly_order = 1
+ mod.coefficients[0] = 1.0
+ mod.coefficients[1] = -1.0
+
+ # Constrain org bones to controls
+ con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS')
+ con.name = "fk"
+ con.target = self.obj
+ con.subtarget = uarm
+
+ con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS')
+ con.name = "fk"
+ con.target = self.obj
+ con.subtarget = farm
+
+ con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS')
+ con.name = "fk"
+ con.target = self.obj
+ con.subtarget = hand
+
+ # Set layers if specified
+ if self.layers:
+ uarm_p.bone.layers = self.layers
+ farm_p.bone.layers = self.layers
+ hand_p.bone.layers = self.layers
+
+ # Create control widgets
+ create_limb_widget(self.obj, uarm)
+ create_limb_widget(self.obj, farm)
+
+ ob = create_widget(self.obj, hand)
+ if ob != None:
+ verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)]
+ edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)]
+ mesh = ob.data
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()
+
+ mod = ob.modifiers.new("subsurf", 'SUBSURF')
+ mod.levels = 2
+
+ return [uarm, farm, hand]
+
diff --git a/rigify/rigs/biped/arm/ik.py b/rigify/rigs/biped/arm/ik.py
new file mode 100644
index 00000000..2b941b9e
--- /dev/null
+++ b/rigify/rigs/biped/arm/ik.py
@@ -0,0 +1,307 @@
+#====================== 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 math import pi, acos
+from rigify.utils import MetarigError
+from rigify.utils import copy_bone
+from rigify.utils import connected_children_names
+from rigify.utils import strip_org, make_mechanism_name, insert_before_lr
+from rigify.utils import get_layers
+from rigify.utils import create_widget, create_line_widget, create_sphere_widget
+from rna_prop_ui import rna_idprop_ui_prop_get
+
+
+def angle_on_plane(plane, vec1, vec2):
+ """ Return the angle between two vectors projected onto a plane.
+ """
+ plane.normalize()
+ vec1 = vec1 - (plane * (vec1.dot(plane)))
+ vec2 = vec2 - (plane * (vec2.dot(plane)))
+ vec1.normalize()
+ vec2.normalize()
+
+ # Determine the angle
+ angle = acos(max(-1.0, min(1.0, vec1.dot(vec2))))
+
+ if angle < 0.00001: # close enough to zero that sign doesn't matter
+ return angle
+
+ # Determine the sign of the angle
+ vec3 = vec2.cross(vec1)
+ vec3.normalize()
+ sign = vec3.dot(plane)
+ if sign >= 0:
+ sign = 1
+ else:
+ sign = -1
+
+ return angle * sign
+
+
+class Rig:
+ """ An IK arm rig, with an optional ik/fk switch.
+
+ """
+ def __init__(self, obj, bone, params, ikfk_switch=False):
+ """ Gather and validate data about the rig.
+ Store any data or references to data that will be needed later on.
+ In particular, store references to bones that will be needed, and
+ store names of bones that will be needed.
+ Do NOT change any data in the scene. This is a gathering phase only.
+
+ ikfk_switch: if True, create an ik/fk switch slider
+ """
+ self.obj = obj
+ self.params = params
+ self.switch = ikfk_switch
+
+ # Get the chain of 3 connected bones
+ self.org_bones = [bone] + connected_children_names(self.obj, bone)[:2]
+
+ if len(self.org_bones) != 3:
+ raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of 3 bones." % (strip_org(bone)))
+
+ # Get the rig parameters
+ if params.separate_ik_layers:
+ self.layers = list(params.ik_layers)
+ else:
+ self.layers = None
+
+ self.primary_rotation_axis = params.primary_rotation_axis
+
+ 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.
+
+ """
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ # Create the bones
+ uarm = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], "_ik"))))
+ farm = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], "_ik"))))
+
+ hand = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], "_ik")))
+ pole = copy_bone(self.obj, self.org_bones[0], strip_org(insert_before_lr(self.org_bones[0], "_pole")))
+
+ vishand = copy_bone(self.obj, self.org_bones[2], "VIS-" + strip_org(insert_before_lr(self.org_bones[2], "_ik")))
+ vispole = copy_bone(self.obj, self.org_bones[1], "VIS-" + strip_org(insert_before_lr(self.org_bones[0], "_pole")))
+
+ # Get edit bones
+ eb = self.obj.data.edit_bones
+
+ uarm_e = eb[uarm]
+ farm_e = eb[farm]
+ hand_e = eb[hand]
+ pole_e = eb[pole]
+ vishand_e = eb[vishand]
+ vispole_e = eb[vispole]
+
+ # Parenting
+ farm_e.parent = uarm_e
+
+ hand_e.use_connect = False
+ hand_e.parent = None
+
+ pole_e.use_connect = False
+
+ vishand_e.use_connect = False
+ vishand_e.parent = None
+
+ vispole_e.use_connect = False
+ vispole_e.parent = None
+
+ # Misc
+ hand_e.use_local_location = False
+
+ vishand_e.hide_select = True
+ vispole_e.hide_select = True
+
+ # Positioning
+ v1 = farm_e.tail - uarm_e.head
+ if 'X' in self.primary_rotation_axis or 'Y' in self.primary_rotation_axis:
+ v2 = v1.cross(farm_e.x_axis)
+ if (v2 * farm_e.z_axis) > 0.0:
+ v2 *= -1.0
+ else:
+ v2 = v1.cross(farm_e.z_axis)
+ if (v2 * farm_e.x_axis) < 0.0:
+ v2 *= -1.0
+ v2.normalize()
+ v2 *= v1.length
+
+ if '-' in self.primary_rotation_axis:
+ v2 *= -1
+
+ pole_e.head = farm_e.head + v2
+ pole_e.tail = pole_e.head + (Vector((0, 1, 0)) * (v1.length / 8))
+ pole_e.roll = 0.0
+
+ vishand_e.tail = vishand_e.head + Vector((0, 0, v1.length / 32))
+ vispole_e.tail = vispole_e.head + Vector((0, 0, v1.length / 32))
+
+ # Determine the pole offset value
+ plane = (farm_e.tail - uarm_e.head).normalize()
+ vec1 = uarm_e.x_axis.normalize()
+ vec2 = (pole_e.head - uarm_e.head).normalize()
+ pole_offset = angle_on_plane(plane, vec1, vec2)
+
+ # Object mode, get pose bones
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+
+ uarm_p = pb[uarm]
+ farm_p = pb[farm]
+ hand_p = pb[hand]
+ pole_p = pb[pole]
+ vishand_p = pb[vishand]
+ vispole_p = pb[vispole]
+
+ # Set the elbow to only bend on the primary axis
+ if 'X' in self.primary_rotation_axis:
+ farm_p.lock_ik_y = True
+ farm_p.lock_ik_z = True
+ elif 'Y' in self.primary_rotation_axis:
+ farm_p.lock_ik_x = True
+ farm_p.lock_ik_z = True
+ else:
+ farm_p.lock_ik_x = True
+ farm_p.lock_ik_y = True
+
+ # Pole target only translates
+ pole_p.lock_location = False, False, False
+ pole_p.lock_rotation = True, True, True
+ pole_p.lock_rotation_w = True
+ pole_p.lock_scale = True, True, True
+
+ # Set up custom properties
+ if self.switch == True:
+ prop = rna_idprop_ui_prop_get(hand_p, "ikfk_switch", create=True)
+ hand_p["ikfk_switch"] = 0.0
+ prop["soft_min"] = prop["min"] = 0.0
+ prop["soft_max"] = prop["max"] = 1.0
+
+ # IK Constraint
+ con = farm_p.constraints.new('IK')
+ con.name = "ik"
+ con.target = self.obj
+ con.subtarget = hand
+ con.pole_target = self.obj
+ con.pole_subtarget = pole
+ con.pole_angle = pole_offset
+ con.chain_count = 2
+
+ # Constrain org bones to controls
+ con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS')
+ con.name = "ik"
+ con.target = self.obj
+ con.subtarget = uarm
+ if self.switch == True:
+ # IK/FK switch driver
+ fcurve = con.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 = hand_p.path_from_id() + '["ikfk_switch"]'
+
+ con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS')
+ con.name = "ik"
+ con.target = self.obj
+ con.subtarget = farm
+ if self.switch == True:
+ # IK/FK switch driver
+ fcurve = con.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 = hand_p.path_from_id() + '["ikfk_switch"]'
+
+ con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS')
+ con.name = "ik"
+ con.target = self.obj
+ con.subtarget = hand
+ if self.switch == True:
+ # IK/FK switch driver
+ fcurve = con.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 = hand_p.path_from_id() + '["ikfk_switch"]'
+
+ # VIS hand constraints
+ con = vishand_p.constraints.new('COPY_LOCATION')
+ con.name = "copy_loc"
+ con.target = self.obj
+ con.subtarget = self.org_bones[2]
+
+ con = vishand_p.constraints.new('STRETCH_TO')
+ con.name = "stretch_to"
+ con.target = self.obj
+ con.subtarget = hand
+ con.volume = 'NO_VOLUME'
+ con.rest_length = vishand_p.length
+
+ # VIS pole constraints
+ con = vispole_p.constraints.new('COPY_LOCATION')
+ con.name = "copy_loc"
+ con.target = self.obj
+ con.subtarget = self.org_bones[1]
+
+ con = vispole_p.constraints.new('STRETCH_TO')
+ con.name = "stretch_to"
+ con.target = self.obj
+ con.subtarget = pole
+ con.volume = 'NO_VOLUME'
+ con.rest_length = vispole_p.length
+
+ # Set layers if specified
+ if self.layers:
+ hand_p.bone.layers = self.layers
+ pole_p.bone.layers = self.layers
+ vishand_p.bone.layers = self.layers
+ vispole_p.bone.layers = self.layers
+
+ # Create widgets
+ create_line_widget(self.obj, vispole)
+ create_line_widget(self.obj, vishand)
+ create_sphere_widget(self.obj, pole)
+
+ ob = create_widget(self.obj, hand)
+ if ob != None:
+ verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)]
+ edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)]
+ mesh = ob.data
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()
+
+ mod = ob.modifiers.new("subsurf", 'SUBSURF')
+ mod.levels = 2
+
+ return [hand, pole]
+
diff --git a/rigify/rigs/biped/leg/__init__.py b/rigify/rigs/biped/leg/__init__.py
new file mode 100644
index 00000000..5cbf3b11
--- /dev/null
+++ b/rigify/rigs/biped/leg/__init__.py
@@ -0,0 +1,237 @@
+#====================== 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
+import imp
+from . import fk, ik, deform
+from rigify.utils import MetarigError, get_layers
+
+imp.reload(fk)
+imp.reload(ik)
+imp.reload(deform)
+
+script = """
+fk_leg = ["%s", "%s", "%s"]
+ik_leg = ["%s", "%s", "%s"]
+if is_selected(fk_leg+ik_leg):
+ layout.prop(pose_bones[ik_leg[0]], '["ikfk_switch"]', text="FK / IK (" + ik_leg[0] + ")", slider=True)
+if is_selected(fk_leg):
+ layout.prop(pose_bones[fk_leg[0]], '["isolate"]', text="Isolate Rotation (" + fk_leg[0] + ")", slider=True)
+"""
+
+
+class Rig:
+ """ A leg rig, with IK/FK switching, a hinge switch, and foot roll.
+
+ """
+ def __init__(self, obj, bone, params):
+ """ Gather and validate data about the rig.
+ Store any data or references to data that will be needed later on.
+ In particular, store names of bones that will be needed.
+ Do NOT change any data in the scene. This is a gathering phase only.
+
+ """
+ # Gather deform rig
+ self.deform_rig = deform.Rig(obj, bone, params)
+
+ # Gather FK rig
+ self.fk_rig = fk.Rig(obj, bone, params)
+
+ # Gather IK rig
+ self.ik_rig = ik.Rig(obj, bone, params, ikfk_switch=True)
+
+ 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.deform_rig.generate()
+ fk_controls = self.fk_rig.generate()
+ ik_controls = self.ik_rig.generate()
+ return [script % (fk_controls[0], fk_controls[1], fk_controls[2], ik_controls[0], ik_controls[1], ik_controls[2])]
+
+ @classmethod
+ def add_parameters(self, group):
+ """ Add the parameters of this rig type to the
+ RigifyParameters IDPropertyGroup
+
+ """
+ items = [('X', 'X', ''), ('Y', 'Y', ''), ('Z', 'Z', ''), ('-X', '-X', ''), ('-Y', '-Y', ''), ('-Z', '-Z', '')]
+ group.primary_rotation_axis = bpy.props.EnumProperty(items=items, name="Primary Rotation Axis", default='X')
+
+ group.separate_ik_layers = bpy.props.BoolProperty(name="Separate IK Control Layers:", default=False, description="Enable putting the ik controls on a separate layer from the fk controls.")
+ group.ik_layers = bpy.props.BoolVectorProperty(size=32, description="Layers for the ik controls to be on.")
+
+ group.use_thigh_twist = bpy.props.BoolProperty(name="Thigh Twist", default=True, description="Generate the dual-bone twist setup for the thigh.")
+ group.use_shin_twist = bpy.props.BoolProperty(name="Shin Twist", default=True, description="Generate the dual-bone twist setup for the shin.")
+
+ @classmethod
+ def parameters_ui(self, layout, obj, bone):
+ """ Create the ui for the rig parameters.
+
+ """
+ params = obj.pose.bones[bone].rigify_parameters[0]
+
+ r = layout.row()
+ r.prop(params, "separate_ik_layers")
+
+ r = layout.row()
+ r.active = params.separate_ik_layers
+
+ col = r.column(align=True)
+ row = col.row(align=True)
+ row.prop(params, "ik_layers", index=0, toggle=True, text="")
+ row.prop(params, "ik_layers", index=1, toggle=True, text="")
+ row.prop(params, "ik_layers", index=2, toggle=True, text="")
+ row.prop(params, "ik_layers", index=3, toggle=True, text="")
+ row.prop(params, "ik_layers", index=4, toggle=True, text="")
+ row.prop(params, "ik_layers", index=5, toggle=True, text="")
+ row.prop(params, "ik_layers", index=6, toggle=True, text="")
+ row.prop(params, "ik_layers", index=7, toggle=True, text="")
+ row = col.row(align=True)
+ row.prop(params, "ik_layers", index=16, toggle=True, text="")
+ row.prop(params, "ik_layers", index=17, toggle=True, text="")
+ row.prop(params, "ik_layers", index=18, toggle=True, text="")
+ row.prop(params, "ik_layers", index=19, toggle=True, text="")
+ row.prop(params, "ik_layers", index=20, toggle=True, text="")
+ row.prop(params, "ik_layers", index=21, toggle=True, text="")
+ row.prop(params, "ik_layers", index=22, toggle=True, text="")
+ row.prop(params, "ik_layers", index=23, toggle=True, text="")
+
+ col = r.column(align=True)
+ row = col.row(align=True)
+ row.prop(params, "ik_layers", index=8, toggle=True, text="")
+ row.prop(params, "ik_layers", index=9, toggle=True, text="")
+ row.prop(params, "ik_layers", index=10, toggle=True, text="")
+ row.prop(params, "ik_layers", index=11, toggle=True, text="")
+ row.prop(params, "ik_layers", index=12, toggle=True, text="")
+ row.prop(params, "ik_layers", index=13, toggle=True, text="")
+ row.prop(params, "ik_layers", index=14, toggle=True, text="")
+ row.prop(params, "ik_layers", index=15, toggle=True, text="")
+ row = col.row(align=True)
+ row.prop(params, "ik_layers", index=24, toggle=True, text="")
+ row.prop(params, "ik_layers", index=25, toggle=True, text="")
+ row.prop(params, "ik_layers", index=26, toggle=True, text="")
+ row.prop(params, "ik_layers", index=27, toggle=True, text="")
+ row.prop(params, "ik_layers", index=28, toggle=True, text="")
+ row.prop(params, "ik_layers", index=29, toggle=True, text="")
+ row.prop(params, "ik_layers", index=30, toggle=True, text="")
+ row.prop(params, "ik_layers", index=31, toggle=True, text="")
+
+ r = layout.row()
+ r.label(text="Knee rotation axis:")
+ r.prop(params, "primary_rotation_axis", text="")
+
+ col = layout.column()
+ col.prop(params, "use_thigh_twist")
+ col.prop(params, "use_shin_twist")
+
+ @classmethod
+ def create_sample(self, obj):
+ # generated by rigify.utils.write_meta_rig
+ bpy.ops.object.mode_set(mode='EDIT')
+ arm = obj.data
+
+ bones = {}
+
+ bone = arm.edit_bones.new('thigh')
+ bone.head[:] = -0.0000, 0.0000, 1.0000
+ bone.tail[:] = -0.0000, -0.0500, 0.5000
+ bone.roll = -0.0000
+ bone.use_connect = False
+ bones['thigh'] = bone.name
+ bone = arm.edit_bones.new('shin')
+ bone.head[:] = -0.0000, -0.0500, 0.5000
+ bone.tail[:] = -0.0000, 0.0000, 0.1000
+ bone.roll = -0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['thigh']]
+ bones['shin'] = bone.name
+ bone = arm.edit_bones.new('foot')
+ bone.head[:] = -0.0000, 0.0000, 0.1000
+ bone.tail[:] = 0.0000, -0.1200, 0.0300
+ bone.roll = 0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['shin']]
+ bones['foot'] = bone.name
+ bone = arm.edit_bones.new('heel')
+ bone.head[:] = -0.0000, 0.0000, 0.1000
+ bone.tail[:] = -0.0000, 0.0600, 0.0000
+ bone.roll = -0.0000
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['shin']]
+ bones['heel'] = bone.name
+ bone = arm.edit_bones.new('toe')
+ bone.head[:] = 0.0000, -0.1200, 0.0300
+ bone.tail[:] = 0.0000, -0.2000, 0.0300
+ bone.roll = 3.1416
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['foot']]
+ bones['toe'] = bone.name
+
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pbone = obj.pose.bones[bones['thigh']]
+ pbone.rigify_type = 'biped.leg'
+ pbone.lock_location = (True, True, True)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone.rigify_parameters.add()
+ pbone = obj.pose.bones[bones['shin']]
+ 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['foot']]
+ 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['heel']]
+ 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['toe']]
+ 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'
+
+ 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
+
diff --git a/rigify/rigs/biped/leg/deform.py b/rigify/rigs/biped/leg/deform.py
new file mode 100644
index 00000000..df6c6a60
--- /dev/null
+++ b/rigify/rigs/biped/leg/deform.py
@@ -0,0 +1,264 @@
+#====================== 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 math import acos, degrees
+from mathutils import Vector, Matrix
+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
+
+
+def align_roll(obj, bone1, bone2):
+ bone1_e = obj.data.edit_bones[bone1]
+ bone2_e = obj.data.edit_bones[bone2]
+
+ bone1_e.roll = 0.0
+
+ # Get the directions the bones are pointing in, as vectors
+ y1 = bone1_e.y_axis
+ x1 = bone1_e.x_axis
+ y2 = bone2_e.y_axis
+ x2 = bone2_e.x_axis
+
+ # Get the shortest axis to rotate bone1 on to point in the same direction as bone2
+ axis = y1.cross(y2)
+ axis.normalize()
+
+ # Angle to rotate on that shortest axis
+ angle = y1.angle(y2)
+
+ # Create rotation matrix to make bone1 point in the same direction as bone2
+ rot_mat = Matrix.Rotation(angle, 3, axis)
+
+ # Roll factor
+ x3 = x1 * rot_mat
+ dot = x2 * x3
+ if dot > 1.0:
+ dot = 1.0
+ elif dot < -1.0:
+ dot = -1.0
+ roll = acos(dot)
+
+ # Set the roll
+ bone1_e.roll = roll
+
+ # Check if we rolled in the right direction
+ x3 = bone1_e.x_axis * rot_mat
+ check = x2 * x3
+
+ # If not, reverse
+ if check < 0.9999:
+ bone1_e.roll = -roll
+
+
+class Rig:
+ """ A leg deform-bone setup.
+
+ """
+ def __init__(self, obj, bone, params):
+ """ Gather and validate data about the rig.
+ Store any data or references to data that will be needed later on.
+ In particular, store references to bones that will be needed, and
+ store names of bones that will be needed.
+ Do NOT change any data in the scene. This is a gathering phase only.
+
+ """
+ self.obj = obj
+ self.params = params
+
+ # Get the chain of 2 connected bones
+ leg_bones = [bone] + connected_children_names(self.obj, bone)[:2]
+
+ if len(leg_bones) != 2:
+ raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone)))
+
+ # Get the foot and heel
+ foot = None
+ heel = None
+ for b in self.obj.data.bones[leg_bones[1]].children:
+ if b.use_connect == True:
+ if len(b.children) == 0:
+ heel = b.name
+ else:
+ foot = b.name
+
+ if foot == None or heel == None:
+ raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone)))
+
+ # Get the toe
+ toe = None
+ for b in self.obj.data.bones[foot].children:
+ if b.use_connect == True:
+ toe = b.name
+
+ if toe == None:
+ raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone)))
+
+ self.org_bones = leg_bones + [foot, toe, heel]
+
+ # Get rig parameters
+ self.use_thigh_twist = params.use_thigh_twist
+ self.use_shin_twist = params.use_shin_twist
+
+ 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.
+
+ """
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ # Create upper arm bones
+ if self.use_thigh_twist:
+ thigh1 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0] + ".01")))
+ thigh2 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0] + ".02")))
+ utip = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(self.org_bones[0] + ".tip")))
+ else:
+ thigh = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0])))
+
+ # Create forearm bones
+ if self.use_shin_twist:
+ shin1 = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1] + ".01")))
+ shin2 = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1] + ".02")))
+ stip = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(self.org_bones[1] + ".tip")))
+ else:
+ shin = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1])))
+
+ # Create foot bone
+ foot = copy_bone(self.obj, self.org_bones[2], make_deformer_name(strip_org(self.org_bones[2])))
+
+ # Create toe bone
+ toe = copy_bone(self.obj, self.org_bones[3], make_deformer_name(strip_org(self.org_bones[3])))
+
+ # Get edit bones
+ eb = self.obj.data.edit_bones
+
+ org_thigh_e = eb[self.org_bones[0]]
+ if self.use_thigh_twist:
+ thigh1_e = eb[thigh1]
+ thigh2_e = eb[thigh2]
+ utip_e = eb[utip]
+ else:
+ thigh_e = eb[thigh]
+
+ org_shin_e = eb[self.org_bones[1]]
+ if self.use_shin_twist:
+ shin1_e = eb[shin1]
+ shin2_e = eb[shin2]
+ stip_e = eb[stip]
+ else:
+ shin_e = eb[shin]
+
+ org_foot_e = eb[self.org_bones[2]]
+ foot_e = eb[foot]
+
+ org_toe_e = eb[self.org_bones[3]]
+ toe_e = eb[toe]
+
+ # Parent and position thigh bones
+ if self.use_thigh_twist:
+ thigh1_e.use_connect = False
+ thigh2_e.use_connect = False
+ utip_e.use_connect = False
+
+ thigh1_e.parent = org_thigh_e.parent
+ thigh2_e.parent = org_thigh_e
+ utip_e.parent = org_thigh_e
+
+ center = Vector((org_thigh_e.head + org_thigh_e.tail) / 2)
+
+ thigh1_e.tail = center
+ thigh2_e.head = center
+ put_bone(self.obj, utip, org_thigh_e.tail)
+ utip_e.length = org_thigh_e.length / 8
+ else:
+ thigh_e.use_connect = False
+ thigh_e.parent = org_thigh_e
+
+ # Parent and position shin bones
+ if self.use_shin_twist:
+ shin1_e.use_connect = False
+ shin2_e.use_connect = False
+ stip_e.use_connect = False
+
+ shin1_e.parent = org_shin_e
+ shin2_e.parent = org_shin_e
+ stip_e.parent = org_shin_e
+
+ center = Vector((org_shin_e.head + org_shin_e.tail) / 2)
+
+ shin1_e.tail = center
+ shin2_e.head = center
+ put_bone(self.obj, stip, org_shin_e.tail)
+ stip_e.length = org_shin_e.length / 8
+
+ # Align roll of shin2 with foot
+ align_roll(self.obj, shin2, foot)
+ else:
+ shin_e.use_connect = False
+ shin_e.parent = org_shin_e
+
+ # Parent foot
+ foot_e.use_connect = False
+ foot_e.parent = org_foot_e
+
+ # Parent toe
+ toe_e.use_connect = False
+ toe_e.parent = org_toe_e
+
+ # Object mode, get pose bones
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+
+ if self.use_thigh_twist:
+ thigh1_p = pb[thigh1]
+ if self.use_shin_twist:
+ shin2_p = pb[shin2]
+ foot_p = pb[foot]
+
+ # Thigh constraints
+ if self.use_thigh_twist:
+ con = thigh1_p.constraints.new('COPY_LOCATION')
+ con.name = "copy_location"
+ con.target = self.obj
+ con.subtarget = self.org_bones[0]
+
+ con = thigh1_p.constraints.new('COPY_SCALE')
+ con.name = "copy_scale"
+ con.target = self.obj
+ con.subtarget = self.org_bones[0]
+
+ con = thigh1_p.constraints.new('DAMPED_TRACK')
+ con.name = "track_to"
+ con.target = self.obj
+ con.subtarget = utip
+
+ # Shin constraints
+ if self.use_shin_twist:
+ con = shin2_p.constraints.new('COPY_ROTATION')
+ con.name = "copy_rotation"
+ con.target = self.obj
+ con.subtarget = foot
+
+ con = shin2_p.constraints.new('DAMPED_TRACK')
+ con.name = "track_to"
+ con.target = self.obj
+ con.subtarget = stip
+
diff --git a/rigify/rigs/biped/leg/fk.py b/rigify/rigs/biped/leg/fk.py
new file mode 100644
index 00000000..a212d445
--- /dev/null
+++ b/rigify/rigs/biped/leg/fk.py
@@ -0,0 +1,246 @@
+#====================== 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
+import math
+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 get_layers
+from rigify.utils import create_widget, create_limb_widget
+from rna_prop_ui import rna_idprop_ui_prop_get
+
+
+class Rig:
+ """ An FK leg rig, with hinge switch.
+
+ """
+ def __init__(self, obj, bone, params):
+ """ Gather and validate data about the rig.
+ Store any data or references to data that will be needed later on.
+ In particular, store references to bones that will be needed, and
+ store names of bones that will be needed.
+ Do NOT change any data in the scene. This is a gathering phase only.
+
+ """
+ self.obj = obj
+ self.params = params
+
+ # Get the chain of 2 connected bones
+ leg_bones = [bone] + connected_children_names(self.obj, bone)[:2]
+
+ if len(leg_bones) != 2:
+ raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone)))
+
+ # Get the foot and heel
+ foot = None
+ heel = None
+ for b in self.obj.data.bones[leg_bones[1]].children:
+ if b.use_connect == True:
+ if len(b.children) == 0:
+ heel = b.name
+ else:
+ foot = b.name
+
+ if foot == None or heel == None:
+ raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone)))
+
+ # Get the toe
+ toe = None
+ for b in self.obj.data.bones[foot].children:
+ if b.use_connect == True:
+ toe = b.name
+
+ # Get the toe
+ if toe == None:
+ raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone)))
+
+ self.org_bones = leg_bones + [foot, toe, heel]
+
+ # Get (optional) parent
+ if self.obj.data.bones[bone].parent == None:
+ self.org_parent = None
+ else:
+ self.org_parent = self.obj.data.bones[bone].parent.name
+
+ # Get rig parameters
+ if "layers" in params:
+ self.layers = get_layers(params["layers"])
+ else:
+ self.layers = None
+
+ self.primary_rotation_axis = params.primary_rotation_axis
+
+ 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.
+
+ """
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ # Create the control bones
+ thigh = copy_bone(self.obj, self.org_bones[0], strip_org(self.org_bones[0]))
+ shin = copy_bone(self.obj, self.org_bones[1], strip_org(self.org_bones[1]))
+ foot = copy_bone(self.obj, self.org_bones[2], strip_org(self.org_bones[2]))
+
+ # Create the foot mechanism bone
+ foot_mch = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[2])))
+
+ # Create the hinge bones
+ if self.org_parent != None:
+ hinge = copy_bone(self.obj, self.org_parent, make_mechanism_name(thigh + ".hinge"))
+ socket1 = copy_bone(self.obj, thigh, make_mechanism_name(thigh + ".socket1"))
+ socket2 = copy_bone(self.obj, thigh, make_mechanism_name(thigh + ".socket2"))
+
+ # Get edit bones
+ eb = self.obj.data.edit_bones
+
+ thigh_e = eb[thigh]
+ shin_e = eb[shin]
+ foot_e = eb[foot]
+ foot_mch_e = eb[foot_mch]
+
+ if self.org_parent != None:
+ hinge_e = eb[hinge]
+ socket1_e = eb[socket1]
+ socket2_e = eb[socket2]
+
+ # Parenting
+ shin_e.parent = thigh_e
+ foot_e.parent = shin_e
+
+ foot_mch_e.use_connect = False
+ foot_mch_e.parent = foot_e
+
+ if self.org_parent != None:
+ hinge_e.use_connect = False
+ socket1_e.use_connect = False
+ socket2_e.use_connect = False
+
+ thigh_e.parent = hinge_e
+ hinge_e.parent = socket2_e
+ socket2_e.parent = None
+
+ # Positioning
+ vec = Vector(eb[self.org_bones[3]].vector)
+ vec = vec.normalize()
+ foot_e.tail = foot_e.head + (vec * foot_e.length)
+ foot_e.roll = eb[self.org_bones[3]].roll
+
+ if self.org_parent != None:
+ center = (hinge_e.head + hinge_e.tail) / 2
+ hinge_e.head = center
+ socket1_e.length /= 4
+ socket2_e.length /= 3
+
+ # Object mode, get pose bones
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+
+ thigh_p = pb[thigh]
+ shin_p = pb[shin]
+ foot_p = pb[foot]
+
+ if self.org_parent != None:
+ socket1_p = pb[socket1]
+ socket2_p = pb[socket2]
+
+ # Set the elbow to only bend on the x-axis.
+ shin_p.rotation_mode = 'XYZ'
+ if 'X' in self.primary_rotation_axis:
+ shin_p.lock_rotation = (False, True, True)
+ elif 'Y' in self.primary_rotation_axis:
+ shin_p.lock_rotation = (True, False, True)
+ else:
+ shin_p.lock_rotation = (True, True, False)
+
+ # Set up custom properties
+ if self.org_parent != None:
+ prop = rna_idprop_ui_prop_get(thigh_p, "isolate", create=True)
+ thigh_p["isolate"] = 0.0
+ prop["soft_min"] = prop["min"] = 0.0
+ prop["soft_max"] = prop["max"] = 1.0
+
+ # Hinge constraints / drivers
+ if self.org_parent != None:
+ con = socket2_p.constraints.new('COPY_LOCATION')
+ con.name = "copy_location"
+ con.target = self.obj
+ con.subtarget = socket1
+
+ con = socket2_p.constraints.new('COPY_TRANSFORMS')
+ con.name = "isolate_off"
+ con.target = self.obj
+ con.subtarget = socket1
+
+ # Driver
+ fcurve = con.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 = thigh_p.path_from_id() + '["isolate"]'
+ mod = fcurve.modifiers[0]
+ mod.poly_order = 1
+ mod.coefficients[0] = 1.0
+ mod.coefficients[1] = -1.0
+
+ # Constrain org bones to controls
+ con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS')
+ con.name = "fk"
+ con.target = self.obj
+ con.subtarget = thigh
+
+ con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS')
+ con.name = "fk"
+ con.target = self.obj
+ con.subtarget = shin
+
+ con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS')
+ con.name = "fk"
+ con.target = self.obj
+ con.subtarget = foot_mch
+
+ # Set layers if specified
+ if self.layers:
+ thigh_p.bone.layers = self.layers
+ shin_p.bone.layers = self.layers
+ foot_p.bone.layers = self.layers
+
+ # Create control widgets
+ create_limb_widget(self.obj, thigh)
+ create_limb_widget(self.obj, shin)
+
+ ob = create_widget(self.obj, foot)
+ if ob != None:
+ verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)]
+ edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)]
+ mesh = ob.data
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()
+
+ mod = ob.modifiers.new("subsurf", 'SUBSURF')
+ mod.levels = 2
+
+ return [thigh, shin, foot]
+
diff --git a/rigify/rigs/biped/leg/ik.py b/rigify/rigs/biped/leg/ik.py
new file mode 100644
index 00000000..a04b57dc
--- /dev/null
+++ b/rigify/rigs/biped/leg/ik.py
@@ -0,0 +1,520 @@
+#====================== 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 math import pi, acos
+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, insert_before_lr
+from rigify.utils import get_layers
+from rigify.utils import create_widget, create_line_widget, create_sphere_widget, create_circle_widget
+from rna_prop_ui import rna_idprop_ui_prop_get
+
+
+def align_x_axis(obj, bone, vec):
+ """ Aligns the x-axis of a bone to the given vector. This only works if it
+ can be an exact match.
+ Must be in edit mode.
+
+ """
+ vec.normalize()
+ bone_e = obj.data.edit_bones[bone]
+ dot = max(-1.0, min(1.0, bone_e.x_axis.dot(vec)))
+ angle = acos(dot)
+
+ bone_e.roll += angle
+
+ dot1 = bone_e.x_axis.dot(vec)
+
+ bone_e.roll -= angle * 2
+
+ dot2 = bone_e.x_axis.dot(vec)
+
+ if dot1 > dot2:
+ bone_e.roll += angle * 2
+
+
+def angle_on_plane(plane, vec1, vec2):
+ """ Return the angle between two vectors projected onto a plane.
+ """
+ plane.normalize()
+ vec1 = vec1 - (plane * (vec1.dot(plane)))
+ vec2 = vec2 - (plane * (vec2.dot(plane)))
+ vec1.normalize()
+ vec2.normalize()
+
+ # Determine the angle
+ angle = acos(max(-1.0, min(1.0, vec1.dot(vec2))))
+
+ if angle < 0.00001: # close enough to zero that sign doesn't matter
+ return angle
+
+ # Determine the sign of the angle
+ vec3 = vec2.cross(vec1)
+ vec3.normalize()
+ sign = vec3.dot(plane)
+ if sign >= 0:
+ sign = 1
+ else:
+ sign = -1
+
+ return angle * sign
+
+
+class Rig:
+ """ An IK leg rig, with an optional ik/fk switch.
+
+ """
+ def __init__(self, obj, bone, params, ikfk_switch=False):
+ """ Gather and validate data about the rig.
+ Store any data or references to data that will be needed later on.
+ In particular, store references to bones that will be needed, and
+ store names of bones that will be needed.
+ Do NOT change any data in the scene. This is a gathering phase only.
+ """
+ self.obj = obj
+ self.params = params
+ self.switch = ikfk_switch
+
+ # Get the chain of 2 connected bones
+ leg_bones = [bone] + connected_children_names(self.obj, bone)[:2]
+
+ if len(leg_bones) != 2:
+ raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone)))
+
+ # Get the foot and heel
+ foot = None
+ heel = None
+ for b in self.obj.data.bones[leg_bones[1]].children:
+ if b.use_connect == True:
+ if len(b.children) == 0:
+ heel = b.name
+ else:
+ foot = b.name
+
+ if foot == None or heel == None:
+ raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone)))
+
+ # Get the toe
+ toe = None
+ for b in self.obj.data.bones[foot].children:
+ if b.use_connect == True:
+ toe = b.name
+
+ # Get toe
+ if toe == None:
+ raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone)))
+
+ self.org_bones = leg_bones + [foot, toe, heel]
+
+ # Get rig parameters
+ if params.separate_ik_layers:
+ self.layers = list(params.ik_layers)
+ else:
+ self.layers = None
+
+ self.primary_rotation_axis = params.primary_rotation_axis
+
+ 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.
+
+ """
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ # Create the bones
+ thigh = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], "_ik"))))
+ shin = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], "_ik"))))
+
+ foot = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], "_ik")))
+ foot_ik_target = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[2], "_ik_target"))))
+ pole = copy_bone(self.obj, self.org_bones[0], strip_org(insert_before_lr(self.org_bones[0], "_pole")))
+
+ toe = copy_bone(self.obj, self.org_bones[3], strip_org(self.org_bones[3]))
+ toe_parent = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[3] + ".parent")))
+ toe_parent_socket1 = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[3] + ".socket1")))
+ toe_parent_socket2 = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[3] + ".socket2")))
+
+ foot_roll = copy_bone(self.obj, self.org_bones[4], strip_org(insert_before_lr(self.org_bones[2], "_roll")))
+ roll1 = copy_bone(self.obj, self.org_bones[4], make_mechanism_name(strip_org(self.org_bones[2] + ".roll")))
+ roll2 = copy_bone(self.obj, self.org_bones[4], make_mechanism_name(strip_org(self.org_bones[2] + ".roll")))
+
+ visfoot = copy_bone(self.obj, self.org_bones[2], "VIS-" + strip_org(insert_before_lr(self.org_bones[2], "_ik")))
+ vispole = copy_bone(self.obj, self.org_bones[1], "VIS-" + strip_org(insert_before_lr(self.org_bones[0], "_pole")))
+
+ # Get edit bones
+ eb = self.obj.data.edit_bones
+
+ org_foot_e = eb[self.org_bones[2]]
+ thigh_e = eb[thigh]
+ shin_e = eb[shin]
+ foot_e = eb[foot]
+ foot_ik_target_e = eb[foot_ik_target]
+ pole_e = eb[pole]
+ toe_e = eb[toe]
+ toe_parent_e = eb[toe_parent]
+ toe_parent_socket1_e = eb[toe_parent_socket1]
+ toe_parent_socket2_e = eb[toe_parent_socket2]
+ foot_roll_e = eb[foot_roll]
+ roll1_e = eb[roll1]
+ roll2_e = eb[roll2]
+ visfoot_e = eb[visfoot]
+ vispole_e = eb[vispole]
+
+ # Parenting
+ shin_e.parent = thigh_e
+
+ foot_e.use_connect = False
+ foot_e.parent = None
+ foot_ik_target_e.use_connect = False
+ foot_ik_target_e.parent = roll2_e
+
+ pole_e.use_connect = False
+ pole_e.parent = foot_e
+
+ toe_e.parent = toe_parent_e
+ toe_parent_e.use_connect = False
+ toe_parent_e.parent = toe_parent_socket1_e
+ toe_parent_socket1_e.use_connect = False
+ toe_parent_socket1_e.parent = roll1_e
+ toe_parent_socket2_e.use_connect = False
+ toe_parent_socket2_e.parent = eb[self.org_bones[2]]
+
+ foot_roll_e.use_connect = False
+ foot_roll_e.parent = foot_e
+
+ roll1_e.use_connect = False
+ roll1_e.parent = foot_e
+
+ roll2_e.use_connect = False
+ roll2_e.parent = roll1_e
+
+ visfoot_e.use_connect = False
+ visfoot_e.parent = None
+
+ vispole_e.use_connect = False
+ vispole_e.parent = None
+
+ # Misc
+ foot_e.use_local_location = False
+
+ visfoot_e.hide_select = True
+ vispole_e.hide_select = True
+
+ # Positioning
+ vec = Vector(toe_e.vector)
+ vec = vec.normalize()
+ foot_e.tail = foot_e.head + (vec * foot_e.length)
+ foot_e.roll = toe_e.roll
+
+ v1 = shin_e.tail - thigh_e.head
+
+ if 'X' in self.primary_rotation_axis or 'Y' in self.primary_rotation_axis:
+ v2 = v1.cross(shin_e.x_axis)
+ if (v2 * shin_e.z_axis) > 0.0:
+ v2 *= -1.0
+ else:
+ v2 = v1.cross(shin_e.z_axis)
+ if (v2 * shin_e.x_axis) < 0.0:
+ v2 *= -1.0
+ v2.normalize()
+ v2 *= v1.length
+
+ if '-' in self.primary_rotation_axis:
+ v2 *= -1
+
+ pole_e.head = shin_e.head + v2
+ pole_e.tail = pole_e.head + (Vector((0, 1, 0)) * (v1.length / 8))
+ pole_e.roll = 0.0
+
+ flip_bone(self.obj, toe_parent_socket1)
+ flip_bone(self.obj, toe_parent_socket2)
+ toe_parent_socket1_e.head = Vector(org_foot_e.tail)
+ toe_parent_socket2_e.head = Vector(org_foot_e.tail)
+ toe_parent_socket1_e.tail = Vector(org_foot_e.tail) + (Vector((0, 0, 1)) * foot_e.length / 2)
+ toe_parent_socket2_e.tail = Vector(org_foot_e.tail) + (Vector((0, 0, 1)) * foot_e.length / 3)
+ toe_parent_socket2_e.roll = toe_parent_socket1_e.roll
+
+ tail = Vector(roll1_e.tail)
+ roll1_e.tail = Vector(org_foot_e.tail)
+ roll1_e.tail = Vector(org_foot_e.tail)
+ roll1_e.head = tail
+ roll2_e.head = Vector(org_foot_e.tail)
+ foot_roll_e.head = Vector(org_foot_e.tail)
+ put_bone(self.obj, foot_roll, roll1_e.head)
+ foot_roll_e.length /= 2
+
+ roll_axis = roll1_e.vector.cross(org_foot_e.vector)
+ align_x_axis(self.obj, roll1, roll_axis)
+ align_x_axis(self.obj, roll2, roll_axis)
+ foot_roll_e.roll = roll2_e.roll
+
+ visfoot_e.tail = visfoot_e.head + Vector((0, 0, v1.length / 32))
+ vispole_e.tail = vispole_e.head + Vector((0, 0, v1.length / 32))
+
+ # Weird alignment issues. Fix.
+ toe_parent_e.head = Vector(org_foot_e.head)
+ toe_parent_e.tail = Vector(org_foot_e.tail)
+ toe_parent_e.roll = org_foot_e.roll
+
+ foot_e.head = Vector(org_foot_e.head)
+
+ foot_ik_target_e.head = Vector(org_foot_e.head)
+ foot_ik_target_e.tail = Vector(org_foot_e.tail)
+
+ # Determine the pole offset value
+ plane = (shin_e.tail - thigh_e.head).normalize()
+ vec1 = thigh_e.x_axis.normalize()
+ vec2 = (pole_e.head - thigh_e.head).normalize()
+ pole_offset = angle_on_plane(plane, vec1, vec2)
+
+ # Object mode, get pose bones
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+
+ thigh_p = pb[thigh]
+ shin_p = pb[shin]
+ foot_p = pb[foot]
+ pole_p = pb[pole]
+ foot_roll_p = pb[foot_roll]
+ roll1_p = pb[roll1]
+ roll2_p = pb[roll2]
+ toe_p = pb[toe]
+ toe_parent_p = pb[toe_parent]
+ toe_parent_socket1_p = pb[toe_parent_socket1]
+ visfoot_p = pb[visfoot]
+ vispole_p = pb[vispole]
+
+ # Set the knee to only bend on the primary axis.
+ if 'X' in self.primary_rotation_axis:
+ shin_p.lock_ik_y = True
+ shin_p.lock_ik_z = True
+ elif 'Y' in self.primary_rotation_axis:
+ shin_p.lock_ik_x = True
+ shin_p.lock_ik_z = True
+ else:
+ shin_p.lock_ik_x = True
+ shin_p.lock_ik_y = True
+
+ # Foot roll control only rotates on x-axis.
+ foot_roll_p.rotation_mode = 'XYZ'
+ foot_roll_p.lock_rotation = False, True, True
+ foot_roll_p.lock_location = True, True, True
+ foot_roll_p.lock_scale = True, True, True
+
+ # Pole target only translates
+ pole_p.lock_location = False, False, False
+ pole_p.lock_rotation = True, True, True
+ pole_p.lock_rotation_w = True
+ pole_p.lock_scale = True, True, True
+
+ # Set up custom properties
+ if self.switch == True:
+ prop = rna_idprop_ui_prop_get(foot_p, "ikfk_switch", create=True)
+ foot_p["ikfk_switch"] = 0.0
+ prop["soft_min"] = prop["min"] = 0.0
+ prop["soft_max"] = prop["max"] = 1.0
+
+ # IK Constraint
+ con = shin_p.constraints.new('IK')
+ con.name = "ik"
+ con.target = self.obj
+ con.subtarget = foot_ik_target
+ con.pole_target = self.obj
+ con.pole_subtarget = pole
+ con.pole_angle = pole_offset
+ con.chain_count = 2
+
+ # toe_parent constraint
+ con = toe_parent_socket1_p.constraints.new('COPY_LOCATION')
+ con.name = "copy_location"
+ con.target = self.obj
+ con.subtarget = toe_parent_socket2
+
+ con = toe_parent_socket1_p.constraints.new('COPY_SCALE')
+ con.name = "copy_scale"
+ con.target = self.obj
+ con.subtarget = toe_parent_socket2
+
+ con = toe_parent_socket1_p.constraints.new('COPY_TRANSFORMS') # drive with IK switch
+ con.name = "fk"
+ con.target = self.obj
+ con.subtarget = toe_parent_socket2
+
+ fcurve = con.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 = foot_p.path_from_id() + '["ikfk_switch"]'
+ mod = fcurve.modifiers[0]
+ mod.poly_order = 1
+ mod.coefficients[0] = 1.0
+ mod.coefficients[1] = -1.0
+
+ # Foot roll constraints
+ con = roll1_p.constraints.new('COPY_ROTATION')
+ con.name = "roll"
+ con.target = self.obj
+ con.subtarget = foot_roll
+ con.target_space = 'LOCAL'
+ con.owner_space = 'LOCAL'
+
+ con = roll1_p.constraints.new('LIMIT_ROTATION')
+ con.name = "limit_roll"
+ con.use_limit_x = True
+ con.min_x = -180
+ con.max_x = 0
+ con.owner_space = 'LOCAL'
+
+ con = roll2_p.constraints.new('COPY_ROTATION')
+ con.name = "roll"
+ con.target = self.obj
+ con.subtarget = foot_roll
+ con.target_space = 'LOCAL'
+ con.owner_space = 'LOCAL'
+
+ con = roll2_p.constraints.new('LIMIT_ROTATION')
+ con.name = "limit_roll"
+ con.use_limit_x = True
+ con.min_x = 0
+ con.max_x = 180
+ con.owner_space = 'LOCAL'
+
+ # Constrain org bones to controls
+ con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS')
+ con.name = "ik"
+ con.target = self.obj
+ con.subtarget = thigh
+ if self.switch == True:
+ # IK/FK switch driver
+ fcurve = con.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 = foot_p.path_from_id() + '["ikfk_switch"]'
+
+ con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS')
+ con.name = "ik"
+ con.target = self.obj
+ con.subtarget = shin
+ if self.switch == True:
+ # IK/FK switch driver
+ fcurve = con.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 = foot_p.path_from_id() + '["ikfk_switch"]'
+
+ con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS')
+ con.name = "ik"
+ con.target = self.obj
+ con.subtarget = foot_ik_target
+ if self.switch == True:
+ # IK/FK switch driver
+ fcurve = con.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 = foot_p.path_from_id() + '["ikfk_switch"]'
+
+ con = pb[self.org_bones[3]].constraints.new('COPY_TRANSFORMS')
+ con.name = "copy_transforms"
+ con.target = self.obj
+ con.subtarget = toe
+
+ # VIS foot constraints
+ con = visfoot_p.constraints.new('COPY_LOCATION')
+ con.name = "copy_loc"
+ con.target = self.obj
+ con.subtarget = self.org_bones[2]
+
+ con = visfoot_p.constraints.new('STRETCH_TO')
+ con.name = "stretch_to"
+ con.target = self.obj
+ con.subtarget = foot
+ con.volume = 'NO_VOLUME'
+ con.rest_length = visfoot_p.length
+
+ # VIS pole constraints
+ con = vispole_p.constraints.new('COPY_LOCATION')
+ con.name = "copy_loc"
+ con.target = self.obj
+ con.subtarget = self.org_bones[1]
+
+ con = vispole_p.constraints.new('STRETCH_TO')
+ con.name = "stretch_to"
+ con.target = self.obj
+ con.subtarget = pole
+ con.volume = 'NO_VOLUME'
+ con.rest_length = vispole_p.length
+
+ # Set layers if specified
+ if self.layers:
+ foot_p.bone.layers = self.layers
+ pole_p.bone.layers = self.layers
+ foot_roll_p.bone.layers = self.layers
+ visfoot_p.bone.layers = self.layers
+ vispole_p.bone.layers = self.layers
+
+ toe_p.bone.layers = [(i[0] or i[1]) for i in zip(toe_p.bone.layers, self.layers)] # Both FK and IK layers
+
+ # Create widgets
+ create_line_widget(self.obj, vispole)
+ create_line_widget(self.obj, visfoot)
+ create_sphere_widget(self.obj, pole)
+ create_circle_widget(self.obj, toe, radius=0.7, head_tail=0.5)
+
+ ob = create_widget(self.obj, foot)
+ if ob != None:
+ verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)]
+ edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)]
+ mesh = ob.data
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()
+
+ mod = ob.modifiers.new("subsurf", 'SUBSURF')
+ mod.levels = 2
+
+ ob = create_widget(self.obj, foot_roll)
+ if ob != None:
+ verts = [(0.3999999761581421, 0.766044557094574, 0.6427875757217407), (0.17668449878692627, 3.823702598992895e-08, 3.2084670920085046e-08), (-0.17668461799621582, 9.874240447516058e-08, 8.285470443070153e-08), (-0.39999961853027344, 0.7660449147224426, 0.6427879333496094), (0.3562471270561218, 0.6159579753875732, 0.5168500542640686), (-0.35624682903289795, 0.6159582138061523, 0.5168502926826477), (0.20492683351039886, 0.09688037633895874, 0.0812922865152359), (-0.20492687821388245, 0.0968804731965065, 0.08129236847162247)]
+ edges = [(1, 2), (0, 3), (0, 4), (3, 5), (1, 6), (4, 6), (2, 7), (5, 7)]
+ mesh = ob.data
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()
+
+ mod = ob.modifiers.new("subsurf", 'SUBSURF')
+ mod.levels = 2
+
+ return [foot, pole, foot_roll]
+
diff --git a/rigify/rigs/copy.py b/rigify/rigs/copy.py
new file mode 100644
index 00000000..61c4cc99
--- /dev/null
+++ b/rigify/rigs/copy.py
@@ -0,0 +1,114 @@
+#====================== 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 rigify.utils import MetarigError
+from rigify.utils import copy_bone
+from rigify.utils import strip_org, make_deformer_name
+from rigify.utils import create_bone_widget
+
+
+class Rig:
+ """ A "copy" rig. All it does is duplicate the original bone and
+ constrain it.
+ This is a control and deformation rig.
+
+ """
+ def __init__(self, obj, bone, params):
+ """ Gather and validate data about the rig.
+ """
+ self.obj = obj
+ self.org_bone = bone
+ self.org_name = strip_org(bone)
+ self.params = params
+
+ 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.
+
+ """
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ # Make a control bone (copy of original).
+ bone = copy_bone(self.obj, self.org_bone, self.org_name)
+
+ # Make a deformation bone (copy of original, child of original).
+ def_bone = copy_bone(self.obj, self.org_bone, make_deformer_name(self.org_name))
+
+ # Get edit bones
+ eb = self.obj.data.edit_bones
+ bone_e = eb[bone]
+ def_bone_e = eb[def_bone]
+
+ # Parent
+ def_bone_e.use_connect = False
+ def_bone_e.parent = eb[self.org_bone]
+
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+
+ # Constrain the original bone.
+ con = pb[self.org_bone].constraints.new('COPY_TRANSFORMS')
+ con.name = "copy_loc"
+ con.target = self.obj
+ con.subtarget = bone
+
+ # Create control widget
+ create_bone_widget(self.obj, bone)
+
+ @classmethod
+ def create_sample(self, obj):
+ """ Create a sample metarig for this rig type.
+
+ """
+ # generated by rigify.utils.write_metarig
+ bpy.ops.object.mode_set(mode='EDIT')
+ arm = obj.data
+
+ bones = {}
+
+ bone = arm.edit_bones.new('Bone')
+ bone.head[:] = 0.0000, 0.0000, 0.0000
+ bone.tail[:] = 0.0000, 0.0000, 0.2000
+ bone.roll = 0.0000
+ bone.use_connect = False
+ bones['Bone'] = bone.name
+
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pbone = obj.pose.bones[bones['Bone']]
+ pbone.rigify_type = 'copy'
+ 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.rigify_parameters.add()
+
+ 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
+
diff --git a/rigify/rigs/finger.py b/rigify/rigs/finger.py
new file mode 100644
index 00000000..f5788d02
--- /dev/null
+++ b/rigify/rigs/finger.py
@@ -0,0 +1,413 @@
+#====================== 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
+from rigify.utils import connected_children_names
+from rigify.utils import strip_org, make_mechanism_name, make_deformer_name
+from rigify.utils import get_layers
+from rigify.utils import create_widget, create_line_widget, create_limb_widget
+from rna_prop_ui import rna_idprop_ui_prop_get
+import re
+
+
+class Rig:
+ """ A finger rig. It takes a single chain of bones.
+ This is a control and deformation rig.
+ """
+ def __init__(self, obj, bone, params):
+ """ Gather and validate data about the rig.
+ """
+ self.obj = obj
+ self.org_bones = [bone] + connected_children_names(obj, bone)
+ 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)))
+
+
+ # Get user-specified layers, if they exist
+ if params.separate_extra_layers:
+ self.ex_layers = list(params.extra_layers)
+ else:
+ self.ex_layers = None
+
+ # Get other rig parameters
+ self.primary_rotation_axis = params.primary_rotation_axis
+ self.use_digit_twist = params.use_digit_twist
+
+ def deform(self):
+ """ Generate the deformation rig.
+ Just a copy of the original bones, except the first digit which is a twist bone.
+ """
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ # Create the bones
+ # First bone is a twist bone
+ if self.use_digit_twist:
+ b1a = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0] + ".01")))
+ b1b = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0] + ".02")))
+ b1tip = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(self.org_bones[0] + ".tip")))
+ else:
+ b1 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0])))
+
+ # The rest are normal
+ bones = []
+ for bone in self.org_bones[1:]:
+ bones += [copy_bone(self.obj, bone, make_deformer_name(strip_org(bone)))]
+
+ # Position bones
+ eb = self.obj.data.edit_bones
+ if self.use_digit_twist:
+ b1a_e = eb[b1a]
+ b1b_e = eb[b1b]
+ b1tip_e = eb[b1tip]
+
+ b1tip_e.use_connect = False
+ b1tip_e.tail += Vector((0.1, 0, 0))
+ b1tip_e.head = b1b_e.tail
+ b1tip_e.length = b1a_e.length / 4
+
+ center = (b1a_e.head + b1a_e.tail) / 2
+ b1a_e.tail = center
+ b1b_e.use_connect = False
+ b1b_e.head = center
+
+ # Parenting
+ if self.use_digit_twist:
+ b1b_e.parent = eb[self.org_bones[0]]
+ b1tip_e.parent = eb[self.org_bones[0]]
+ else:
+ eb[b1].use_connect = False
+ eb[b1].parent = eb[self.org_bones[0]]
+
+ for (ba, bb) in zip(bones, self.org_bones[1:]):
+ eb[ba].use_connect = False
+ eb[ba].parent = eb[bb]
+
+ # Constraints
+ if self.use_digit_twist:
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+
+ b1a_p = pb[b1a]
+
+ con = b1a_p.constraints.new('COPY_LOCATION')
+ con.name = "copy_location"
+ con.target = self.obj
+ con.subtarget = self.org_bones[0]
+
+ con = b1a_p.constraints.new('COPY_SCALE')
+ con.name = "copy_scale"
+ con.target = self.obj
+ con.subtarget = self.org_bones[0]
+
+ con = b1a_p.constraints.new('DAMPED_TRACK')
+ con.name = "track_to"
+ con.target = self.obj
+ con.subtarget = b1tip
+
+ def control(self):
+ """ Generate the control rig.
+ """
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ # Figure out the name for the control bone (remove the last .##)
+ ctrl_name = re.sub("([0-9]+\.)", "", strip_org(self.org_bones[0])[::-1], count=1)[::-1]
+
+ # Create the bones
+ ctrl = copy_bone(self.obj, self.org_bones[0], ctrl_name)
+
+ helpers = []
+ bones = []
+ for bone in self.org_bones:
+ bones += [copy_bone(self.obj, bone, strip_org(bone))]
+ helpers += [copy_bone(self.obj, bone, make_mechanism_name(strip_org(bone)))]
+
+ # Position bones
+ eb = self.obj.data.edit_bones
+
+ length = 0.0
+ for bone in helpers:
+ length += eb[bone].length
+ eb[bone].length /= 2
+
+ eb[ctrl].length = length * 1.5
+
+ # Parent bones
+ prev = eb[self.org_bones[0]].parent
+ for (b, h) in zip(bones, helpers):
+ b_e = eb[b]
+ h_e = eb[h]
+ b_e.use_connect = False
+ h_e.use_connect = False
+
+ b_e.parent = h_e
+ h_e.parent = prev
+
+ prev = b_e
+
+ # Transform locks and rotation mode
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+
+ for bone in bones[1:]:
+ pb[bone].lock_location = True, True, True
+
+ if pb[self.org_bones[0]].bone.use_connect == True:
+ pb[bones[0]].lock_location = True, True, True
+
+ pb[ctrl].lock_scale = True, False, True
+
+ for bone in helpers:
+ pb[bone].rotation_mode = 'XYZ'
+
+ # Drivers
+ i = 1
+ val = 1.2 / (len(self.org_bones) - 1)
+ for bone in helpers:
+ # Add custom prop
+ prop_name = "bend_%02d" % i
+ prop = rna_idprop_ui_prop_get(pb[ctrl], prop_name, create=True)
+ prop["min"] = 0.0
+ prop["max"] = 1.0
+ prop["soft_min"] = 0.0
+ prop["soft_max"] = 1.0
+ if i == 1:
+ pb[ctrl][prop_name] = 0.0
+ else:
+ pb[ctrl][prop_name] = val
+
+ # Add driver
+ if 'X' in self.primary_rotation_axis:
+ fcurve = pb[bone].driver_add("rotation_euler", 0)
+ elif 'Y' in self.primary_rotation_axis:
+ fcurve = pb[bone].driver_add("rotation_euler", 1)
+ else:
+ fcurve = pb[bone].driver_add("rotation_euler", 2)
+
+ driver = fcurve.driver
+ driver.type = 'SCRIPTED'
+
+ var = driver.variables.new()
+ var.name = "ctrl_y"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = pb[ctrl].path_from_id() + '.scale[1]'
+
+ var = driver.variables.new()
+ var.name = "bend"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = pb[ctrl].path_from_id() + '["' + prop_name + '"]'
+
+ if '-' in self.primary_rotation_axis:
+ driver.expression = "-(1.0-ctrl_y) * bend * 3.14159 * 2"
+ else:
+ driver.expression = "(1.0-ctrl_y) * bend * 3.14159 * 2"
+
+ i += 1
+
+ # Constraints
+ con = pb[helpers[0]].constraints.new('COPY_LOCATION')
+ con.name = "copy_location"
+ con.target = self.obj
+ con.subtarget = ctrl
+
+ con = pb[helpers[0]].constraints.new('COPY_ROTATION')
+ con.name = "copy_rotation"
+ con.target = self.obj
+ con.subtarget = ctrl
+
+ # Constrain org bones to the control bones
+ for (bone, org) in zip(bones, self.org_bones):
+ con = pb[org].constraints.new('COPY_TRANSFORMS')
+ con.name = "copy_transforms"
+ con.target = self.obj
+ con.subtarget = bone
+
+ # Set layers for extra control bones
+ if self.ex_layers:
+ for bone in bones:
+ pb[bone].bone.layers = self.ex_layers
+
+ # Create control widgets
+ w = create_widget(self.obj, ctrl)
+ if w != None:
+ mesh = w.data
+ verts = [(0, 0, 0), (0, 1, 0), (0.05, 1, 0), (0.05, 1.1, 0), (-0.05, 1.1, 0), (-0.05, 1, 0)]
+ if 'Z' in self.primary_rotation_axis:
+ # Flip x/z coordinates
+ temp = []
+ for v in verts:
+ temp += [(v[2], v[1], v[0])]
+ verts = temp
+ edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 1)]
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()
+
+ for bone in bones:
+ create_limb_widget(self.obj, bone)
+
+ 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.deform()
+ self.control()
+
+ @classmethod
+ def add_parameters(self, group):
+ """ Add the parameters of this rig type to the
+ RigifyParameters IDPropertyGroup
+ """
+ items = [('X', 'X', ''), ('Y', 'Y', ''), ('Z', 'Z', ''), ('-X', '-X', ''), ('-Y', '-Y', ''), ('-Z', '-Z', '')]
+ group.primary_rotation_axis = bpy.props.EnumProperty(items=items, name="Primary Rotation Axis", default='X')
+
+ group.separate_extra_layers = bpy.props.BoolProperty(name="Separate Secondary Control Layers:", default=False, description="Enable putting the secondary controls on a separate layer from the primary controls.")
+ group.extra_layers = bpy.props.BoolVectorProperty(size=32, description="Layers for the secondary controls to be on.")
+
+ group.use_digit_twist = bpy.props.BoolProperty(name="Digit Twist", default=True, description="Generate the dual-bone twist setup for the first finger digit.")
+
+ @classmethod
+ def parameters_ui(self, layout, obj, bone):
+ """ Create the ui for the rig parameters.
+ """
+ params = obj.pose.bones[bone].rigify_parameters[0]
+
+ r = layout.row()
+ r.prop(params, "separate_extra_layers")
+
+ r = layout.row()
+ r.active = params.separate_extra_layers
+
+ col = r.column(align=True)
+ row = col.row(align=True)
+ row.prop(params, "extra_layers", index=0, toggle=True, text="")
+ row.prop(params, "extra_layers", index=1, toggle=True, text="")
+ row.prop(params, "extra_layers", index=2, toggle=True, text="")
+ row.prop(params, "extra_layers", index=3, toggle=True, text="")
+ row.prop(params, "extra_layers", index=4, toggle=True, text="")
+ row.prop(params, "extra_layers", index=5, toggle=True, text="")
+ row.prop(params, "extra_layers", index=6, toggle=True, text="")
+ row.prop(params, "extra_layers", index=7, toggle=True, text="")
+ row = col.row(align=True)
+ row.prop(params, "extra_layers", index=16, toggle=True, text="")
+ row.prop(params, "extra_layers", index=17, toggle=True, text="")
+ row.prop(params, "extra_layers", index=18, toggle=True, text="")
+ row.prop(params, "extra_layers", index=19, toggle=True, text="")
+ row.prop(params, "extra_layers", index=20, toggle=True, text="")
+ row.prop(params, "extra_layers", index=21, toggle=True, text="")
+ row.prop(params, "extra_layers", index=22, toggle=True, text="")
+ row.prop(params, "extra_layers", index=23, toggle=True, text="")
+
+ col = r.column(align=True)
+ row = col.row(align=True)
+ row.prop(params, "ik_layers", index=8, toggle=True, text="")
+ row.prop(params, "ik_layers", index=9, toggle=True, text="")
+ row.prop(params, "ik_layers", index=10, toggle=True, text="")
+ row.prop(params, "ik_layers", index=11, toggle=True, text="")
+ row.prop(params, "ik_layers", index=12, toggle=True, text="")
+ row.prop(params, "ik_layers", index=13, toggle=True, text="")
+ row.prop(params, "ik_layers", index=14, toggle=True, text="")
+ row.prop(params, "ik_layers", index=15, toggle=True, text="")
+ row = col.row(align=True)
+ row.prop(params, "ik_layers", index=24, toggle=True, text="")
+ row.prop(params, "ik_layers", index=25, toggle=True, text="")
+ row.prop(params, "ik_layers", index=26, toggle=True, text="")
+ row.prop(params, "ik_layers", index=27, toggle=True, text="")
+ row.prop(params, "ik_layers", index=28, toggle=True, text="")
+ row.prop(params, "ik_layers", index=29, toggle=True, text="")
+ row.prop(params, "ik_layers", index=30, toggle=True, text="")
+ row.prop(params, "ik_layers", index=31, toggle=True, text="")
+
+ r = layout.row()
+ r.label(text="Bend rotation axis:")
+ r.prop(params, "primary_rotation_axis", text="")
+
+ col = layout.column()
+ col.prop(params, "use_digit_twist")
+
+ @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('finger.01')
+ bone.head[:] = 0.0000, 0.0000, 0.0000
+ bone.tail[:] = 0.2529, 0.0000, 0.0000
+ bone.roll = 3.1416
+ bone.use_connect = False
+ bones['finger.01'] = bone.name
+ bone = arm.edit_bones.new('finger.02')
+ bone.head[:] = 0.2529, 0.0000, 0.0000
+ bone.tail[:] = 0.4024, 0.0000, -0.0264
+ bone.roll = -2.9671
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['finger.01']]
+ bones['finger.02'] = bone.name
+ bone = arm.edit_bones.new('finger.03')
+ bone.head[:] = 0.4024, 0.0000, -0.0264
+ bone.tail[:] = 0.4975, -0.0000, -0.0610
+ bone.roll = -2.7925
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['finger.02']]
+ bones['finger.03'] = bone.name
+
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pbone = obj.pose.bones[bones['finger.01']]
+ pbone.rigify_type = 'finger'
+ pbone.lock_location = (True, True, True)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'YZX'
+ pbone.rigify_parameters.add()
+ pbone = obj.pose.bones[bones['finger.02']]
+ 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 = 'YZX'
+ pbone = obj.pose.bones[bones['finger.03']]
+ 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 = 'YZX'
+
+ 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
+
diff --git a/rigify/rigs/misc/__init__.py b/rigify/rigs/misc/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/rigify/rigs/misc/__init__.py
diff --git a/rigify/rigs/misc/delta.py b/rigify/rigs/misc/delta.py
new file mode 100644
index 00000000..2157970a
--- /dev/null
+++ b/rigify/rigs/misc/delta.py
@@ -0,0 +1,161 @@
+#====================== 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 math import acos
+from rigify.utils import MetarigError
+from rigify.utils import copy_bone
+from rigify.utils import org_name, make_mechanism_name
+
+
+class Rig:
+ """ A delta rig.
+ Creates a setup that will place its child at its position in pose mode,
+ but will not modifying its child's position in edit mode.
+ This is a mechanism-only rig (no control or deformation bones).
+
+ """
+ def __init__(self, obj, bone, params):
+ """ Gather and validate data about the rig.
+ Store any data or references to data that will be needed later on.
+ In particular, store references to bones that will be needed.
+ Do NOT change any data in the scene. This is a gathering phase only.
+
+ """
+ bb = obj.data.bones
+
+ if bb[bone].children == None:
+ raise MetarigError("RIGIFY ERROR: bone '%s': rig type requires one child." % org_name(bone.name))
+ if bb[bone].use_connect == True:
+ raise MetarigError("RIGIFY ERROR: bone '%s': rig type cannot be connected to parent." % org_name(bone.name))
+
+ self.obj = obj
+ self.org_bones = {"delta": bone, "child": bb[bone].children[0].name}
+ self.org_names = [org_name(bone), org_name(bb[bone].children[0].name)]
+
+ 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.
+
+ """
+ bpy.ops.object.mode_set(mode='EDIT')
+ eb = self.obj.data.edit_bones
+
+ org_delta = self.org_bones["delta"]
+ org_delta_e = eb[self.org_bones["delta"]]
+ org_child = self.org_bones["child"]
+ org_child_e = eb[self.org_bones["child"]]
+
+ # Calculate the matrix for achieving the delta
+ child_mat = org_delta_e.matrix.invert() * org_child_e.matrix
+ mat = org_delta_e.matrix * child_mat.invert()
+
+ # Create the delta bones.
+ delta_e = eb[copy_bone(self.obj, self.org_bones["delta"])]
+ delta_e.name = make_mechanism_name(self.org_names[0])
+ delta = delta_e.name
+
+ # Set the delta to the matrix's transforms
+ set_mat(self.obj, delta, mat)
+
+ bpy.ops.object.mode_set(mode='OBJECT')
+
+ # Constrain org_delta to delta
+ con = self.obj.pose.bones[org_delta].constraints.new('COPY_TRANSFORMS')
+ con.name = "delta"
+ con.target = self.obj
+ con.subtarget = delta
+
+ @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('delta')
+ bone.head[:] = 0.0000, -0.1198, 0.1253
+ bone.tail[:] = -0.0000, -0.2483, 0.2785
+ bone.roll = -0.0000
+ bone.use_connect = False
+ bones['delta'] = bone.name
+ bone = arm.edit_bones.new('Bone')
+ bone.head[:] = -0.0000, 0.0000, 0.0000
+ bone.tail[:] = -0.0000, 0.0000, 0.2000
+ bone.roll = 0.0000
+ bone.use_connect = False
+ bone.parent = arm.edit_bones[bones['delta']]
+ bones['Bone'] = bone.name
+
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pbone = obj.pose.bones[bones['delta']]
+ pbone.rigify_type = 'misc.delta'
+ 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.rigify_parameters.add()
+ pbone = obj.pose.bones[bones['Bone']]
+ 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'
+
+ 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
+
+
+def set_mat(obj, bone_name, matrix):
+ """ Sets the bone to have the given transform matrix.
+ """
+ a = obj.data.edit_bones[bone_name]
+
+ a.head = (0, 0, 0)
+ a.tail = (0, 1, 0)
+
+ a.transform(matrix)
+
+ d = acos(a.matrix.to_quat().dot(matrix.to_quat())) * 2
+
+ roll_1 = a.roll + d
+ roll_2 = a.roll - d
+
+ a.roll = roll_1
+ d1 = a.matrix.to_quat().dot(matrix.to_quat())
+ a.roll = roll_2
+ d2 = a.matrix.to_quat().dot(matrix.to_quat())
+
+ if d1 > d2:
+ a.roll = roll_1
+ else:
+ a.roll = roll_2
+
diff --git a/rigify/rigs/neck_short.py b/rigify/rigs/neck_short.py
new file mode 100644
index 00000000..3c34fbda
--- /dev/null
+++ b/rigify/rigs/neck_short.py
@@ -0,0 +1,385 @@
+#====================== 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
+
+script1 = """
+head_neck = ["%s", "%s"]
+"""
+
+script2 = """
+if is_selected(head_neck[0]):
+ layout.prop(pose_bones[head_neck[0]], '["isolate"]', text="Isolate (" + head_neck[0] + ")", slider=True)
+"""
+
+script3 = """
+if is_selected(head_neck):
+ layout.prop(pose_bones[head_neck[0]], '["neck_follow"]', text="Neck Follow Head (" + head_neck[0] + ")", 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)))
+
+ self.isolate = False
+ if self.obj.data.bones[bone_name].parent:
+ self.isolate = True
+
+ 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 neck and head controls
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ # Create bones
+ neck_ctrl = copy_bone(self.obj, self.org_bones[0], strip_org(self.org_bones[0]))
+ neck_follow = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[0] + ".follow")))
+
+ head_ctrl = copy_bone(self.obj, self.org_bones[-1], strip_org(self.org_bones[-1]))
+ head_mch = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(self.org_bones[-1])))
+ if self.isolate:
+ head_socket1 = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[-1] + ".socket1")))
+ head_socket2 = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[-1] + ".socket2")))
+
+ # Create neck chain bones
+ neck = []
+ helpers = []
+ for name in self.org_bones:
+ neck += [copy_bone(self.obj, name, make_mechanism_name(strip_org(name)))]
+ helpers += [copy_bone(self.obj, neck_ctrl, make_mechanism_name(strip_org(name + ".02")))]
+
+ # Fetch edit bones
+ eb = self.obj.data.edit_bones
+
+ neck_ctrl_e = eb[neck_ctrl]
+ neck_follow_e = eb[neck_follow]
+ head_ctrl_e = eb[head_ctrl]
+ head_mch_e = eb[head_mch]
+ if self.isolate:
+ head_socket1_e = eb[head_socket1]
+ head_socket2_e = eb[head_socket2]
+
+ # Parenting
+ head_ctrl_e.use_connect = False
+ head_ctrl_e.parent = neck_ctrl_e.parent
+ head_mch_e.use_connect = False
+ head_mch_e.parent = head_ctrl_e
+
+ if self.isolate:
+ head_socket1_e.use_connect = False
+ head_socket1_e.parent = neck_ctrl_e.parent
+
+ head_socket2_e.use_connect = False
+ head_socket2_e.parent = None
+
+ head_ctrl_e.parent = head_socket2_e
+
+ for (name1, name2) in zip(neck, helpers):
+ eb[name1].use_connect = False
+ eb[name1].parent = eb[name2]
+ eb[name2].use_connect = False
+ eb[name2].parent = neck_ctrl_e.parent
+
+ neck_follow_e.use_connect = False
+ neck_follow_e.parent = neck_ctrl_e.parent
+ neck_ctrl_e.parent = neck_follow_e
+
+ # Position
+ put_bone(self.obj, neck_follow, neck_ctrl_e.head)
+ put_bone(self.obj, head_ctrl, neck_ctrl_e.head)
+ put_bone(self.obj, head_mch, neck_ctrl_e.head)
+ head_mch_e.length /= 2
+
+ if self.isolate:
+ put_bone(self.obj, head_socket1, neck_ctrl_e.head)
+ head_mch_e.length /= 2
+
+ put_bone(self.obj, head_socket2, neck_ctrl_e.head)
+ head_mch_e.length /= 3
+
+ for (name1, name2) in zip(neck, helpers):
+ put_bone(self.obj, name2, eb[name1].head)
+ eb[name2].length /= 3
+
+ # Switch to object mode
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+ neck_ctrl_p = pb[neck_ctrl]
+ neck_follow_p = pb[neck_follow]
+ head_ctrl_p = pb[head_ctrl]
+ if self.isolate:
+ head_socket1_p = pb[head_socket1]
+ head_socket2_p = pb[head_socket2]
+
+ # Custom bone appearance
+ neck_ctrl_p.custom_shape_transform = pb[self.org_bones[(len(self.org_bones) - 1) // 2]]
+ head_ctrl_p.custom_shape_transform = pb[self.org_bones[-1]]
+
+ # Custom properties
+ prop = rna_idprop_ui_prop_get(head_ctrl_p, "inf_extent", create=True)
+ head_ctrl_p["inf_extent"] = 0.5
+ prop["min"] = 0.0
+ prop["max"] = 1.0
+ prop["soft_min"] = 0.0
+ prop["soft_max"] = 1.0
+
+ prop = rna_idprop_ui_prop_get(head_ctrl_p, "neck_follow", create=True)
+ head_ctrl_p["neck_follow"] = 1.0
+ prop["min"] = 0.0
+ prop["max"] = 2.0
+ prop["soft_min"] = 0.0
+ prop["soft_max"] = 1.0
+
+ if self.isolate:
+ prop = rna_idprop_ui_prop_get(head_ctrl_p, "isolate", create=True)
+ head_ctrl_p["isolate"] = 0.0
+ prop["min"] = 0.0
+ prop["max"] = 1.0
+ prop["soft_min"] = 0.0
+ prop["soft_max"] = 1.0
+
+ # Constraints
+
+ # Neck follow
+ con = neck_follow_p.constraints.new('COPY_ROTATION')
+ con.name = "copy_rotation"
+ con.target = self.obj
+ con.subtarget = head_ctrl
+
+ fcurve = con.driver_add("influence")
+ driver = fcurve.driver
+ var = driver.variables.new()
+ driver.type = 'SCRIPTED'
+ var.name = "follow"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = head_ctrl_p.path_from_id() + '["neck_follow"]'
+ driver.expression = "follow / 2"
+
+ # Isolate
+ if self.isolate:
+ con = head_socket2_p.constraints.new('COPY_LOCATION')
+ con.name = "copy_location"
+ con.target = self.obj
+ con.subtarget = head_socket1
+
+ con = head_socket2_p.constraints.new('COPY_TRANSFORMS')
+ con.name = "copy_transforms"
+ con.target = self.obj
+ con.subtarget = head_socket1
+
+ fcurve = con.driver_add("influence")
+ driver = fcurve.driver
+ var = driver.variables.new()
+ driver.type = 'SCRIPTED'
+ var.name = "isolate"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = head_ctrl_p.path_from_id() + '["isolate"]'
+ driver.expression = "1.0 - isolate"
+
+ # Neck chain
+ first = True
+ prev = None
+ i = 0
+ l = len(neck)
+ for (name1, name2, org_name) in zip(neck, helpers, self.org_bones):
+ con = pb[org_name].constraints.new('COPY_TRANSFORMS')
+ con.name = "copy_transforms"
+ con.target = self.obj
+ con.subtarget = name1
+
+ n_con = pb[name2].constraints.new('COPY_TRANSFORMS')
+ n_con.name = "neck"
+ n_con.target = self.obj
+ n_con.subtarget = neck_ctrl
+
+ h_con = pb[name2].constraints.new('COPY_TRANSFORMS')
+ h_con.name = "head"
+ h_con.target = self.obj
+ h_con.subtarget = head_mch
+
+ con = pb[name2].constraints.new('COPY_LOCATION')
+ con.name = "anchor"
+ con.target = self.obj
+ if first:
+ con.subtarget = neck_ctrl
+ else:
+ con.subtarget = prev
+ con.head_tail = 1.0
+
+ # Drivers
+ n = (i + 1) / l
+
+ # Neck influence
+ fcurve = n_con.driver_add("influence")
+ driver = fcurve.driver
+ var = driver.variables.new()
+ driver.type = 'SCRIPTED'
+ var.name = "ext"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = head_ctrl_p.path_from_id() + '["inf_extent"]'
+ driver.expression = "1.0 if (%.4f > (1.0-ext) or (1.0-ext) == 0.0) else (%.4f / (1.0-ext))" % (n, n)
+
+ # Head influence
+ if (i + 1) == l:
+ h_con.influence = 1.0
+ else:
+ fcurve = h_con.driver_add("influence")
+ driver = fcurve.driver
+ var = driver.variables.new()
+ driver.type = 'SCRIPTED'
+ var.name = "ext"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = head_ctrl_p.path_from_id() + '["inf_extent"]'
+ driver.expression = "0.0 if (%.4f <= (1.0-ext)) else ((%.4f - (1.0-ext)) / ext)" % (n, n)
+
+ first = False
+ prev = name1
+ i += 1
+
+ # Create control widgets
+ w1 = create_circle_widget(self.obj, neck_ctrl, radius=1.0, head_tail=0.5)
+ w2 = create_circle_widget(self.obj, head_ctrl, radius=1.0, head_tail=0.5)
+
+ if w1 != None:
+ obj_to_bone(w1, self.obj, self.org_bones[(len(self.org_bones) - 1) // 2])
+ if w2 != None:
+ obj_to_bone(w2, self.obj, self.org_bones[-1])
+
+ # Return control bones
+ return (head_ctrl, neck_ctrl)
+
+ 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()
+ (head, neck) = self.gen_control()
+
+ script = script1 % (head, neck)
+ if self.isolate:
+ script += script2
+ script += script3
+
+ return [script]
+
+ @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('neck')
+ bone.head[:] = 0.0000, 0.0000, 0.0000
+ bone.tail[:] = 0.0000, -0.0500, 0.1500
+ bone.roll = 0.0000
+ bone.use_connect = False
+ bones['neck'] = bone.name
+ bone = arm.edit_bones.new('head')
+ bone.head[:] = 0.0000, -0.0500, 0.1500
+ bone.tail[:] = 0.0000, -0.0500, 0.4000
+ bone.roll = 3.1416
+ bone.use_connect = True
+ bone.parent = arm.edit_bones[bones['neck']]
+ bones['head'] = bone.name
+
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pbone = obj.pose.bones[bones['neck']]
+ pbone.rigify_type = 'neck_short'
+ pbone.lock_location = (True, True, True)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone.rigify_parameters.add()
+ pbone = obj.pose.bones[bones['head']]
+ 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'
+
+ 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
+
diff --git a/rigify/rigs/palm.py b/rigify/rigs/palm.py
new file mode 100644
index 00000000..5a0fa773
--- /dev/null
+++ b/rigify/rigs/palm.py
@@ -0,0 +1,273 @@
+#====================== 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 math import sin, cos, pi
+from rigify.utils import MetarigError
+from rigify.utils import copy_bone
+from rigify.utils import strip_org, deformer, mch
+from rigify.utils import create_widget
+import re
+
+
+def bone_siblings(obj, bone):
+ """ Returns a list of the siblings of the given bone.
+ This requires that the bones has a parent.
+
+ """
+ parent = obj.data.bones[bone].parent
+
+ if parent == None:
+ return []
+
+ bones = []
+
+ for b in parent.children:
+ if b.name != bone:
+ bones += [b.name]
+
+ return bones
+
+
+def bone_distance(obj, bone1, bone2):
+ """ Returns the distance between two bones.
+
+ """
+ vec = obj.data.bones[bone1].head - obj.data.bones[bone2].head
+ return vec.length
+
+
+class Rig:
+ """ A "palm" rig. A set of sibling bones that bend with each other.
+ This is a control and deformation rig.
+
+ """
+ def __init__(self, obj, bone, params):
+ """ Gather and validate data about the rig.
+ """
+ self.obj = obj
+ self.params = params
+
+ siblings = bone_siblings(obj, bone)
+
+ if len(siblings) == 0:
+ raise MetarigError("RIGIFY ERROR: Bone '%s': must have a parent and at least one sibling." % (strip_org(bone)))
+
+ # Sort list by name and distance
+ siblings.sort()
+ siblings.sort(key=lambda b: bone_distance(obj, bone, b))
+
+ self.org_bones = [bone] + siblings
+
+ # Get rig parameters
+ self.palm_rotation_axis = params.palm_rotation_axis
+
+ 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.
+
+ """
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ # Figure out the name for the control bone (remove the last .##)
+ last_bone = self.org_bones[-1:][0]
+ ctrl_name = re.sub("([0-9]+\.)", "", strip_org(last_bone)[::-1], count=1)[::-1]
+
+ # Make control bone
+ ctrl = copy_bone(self.obj, last_bone, ctrl_name)
+
+ # Make deformation bones
+ def_bones = []
+ for bone in self.org_bones:
+ b = copy_bone(self.obj, bone, deformer(strip_org(bone)))
+ def_bones += [b]
+
+ # Parenting
+ eb = self.obj.data.edit_bones
+
+ for d, b in zip(def_bones, self.org_bones):
+ eb[d].use_connect = False
+ eb[d].parent = eb[b]
+
+ # Constraints
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+
+ i = 0
+ div = len(self.org_bones) - 1
+ for b in self.org_bones:
+ con = pb[b].constraints.new('COPY_TRANSFORMS')
+ con.name = "copy_transforms"
+ con.target = self.obj
+ con.subtarget = ctrl
+ con.target_space = 'LOCAL'
+ con.owner_space = 'LOCAL'
+ con.influence = i / div
+
+ con = pb[b].constraints.new('COPY_ROTATION')
+ con.name = "copy_rotation"
+ con.target = self.obj
+ con.subtarget = ctrl
+ con.target_space = 'LOCAL'
+ con.owner_space = 'LOCAL'
+ if 'X' in self.palm_rotation_axis:
+ con.invert_x = True
+ con.use_x = True
+ con.use_z = False
+ else:
+ con.invert_z = True
+ con.use_x = False
+ con.use_z = True
+ con.use_y = False
+
+ con.influence = (i / div) - (1 - cos((i * pi / 2) / div))
+
+ i += 1
+
+ # Create control widget
+ w = create_widget(self.obj, ctrl)
+ if w != None:
+ mesh = w.data
+ verts = [(0.15780271589756012, 2.086162567138672e-07, -0.30000004172325134), (0.15780259668827057, 1.0, -0.2000001072883606), (-0.15780280530452728, 0.9999999403953552, -0.20000004768371582), (-0.15780259668827057, -2.086162567138672e-07, -0.29999998211860657), (-0.15780256688594818, -2.7089754439657554e-07, 0.30000004172325134), (-0.1578027755022049, 0.9999998807907104, 0.19999995827674866), (0.15780262649059296, 0.9999999403953552, 0.19999989867210388), (0.1578027456998825, 1.4633496903115883e-07, 0.29999998211860657), (0.15780268609523773, 0.2500001788139343, -0.27500003576278687), (-0.15780264139175415, 0.24999985098838806, -0.2749999761581421), (0.15780262649059296, 0.7500000596046448, -0.22500008344650269), (-0.1578027606010437, 0.7499998807907104, -0.2250000238418579), (0.15780265629291534, 0.75, 0.22499991953372955), (0.15780271589756012, 0.2500000596046448, 0.2749999761581421), (-0.15780261158943176, 0.2499997615814209, 0.27500003576278687), (-0.1578027307987213, 0.7499998807907104, 0.22499997913837433)]
+ if 'Z' in self.palm_rotation_axis:
+ # Flip x/z coordinates
+ temp = []
+ for v in verts:
+ temp += [(v[2], v[1], v[0])]
+ verts = temp
+ edges = [(1, 2), (0, 3), (4, 7), (5, 6), (8, 0), (9, 3), (10, 1), (11, 2), (12, 6), (13, 7), (4, 14), (15, 5), (10, 8), (11, 9), (15, 14), (12, 13)]
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()
+
+ mod = w.modifiers.new("subsurf", 'SUBSURF')
+ mod.levels = 2
+
+ @classmethod
+ def add_parameters(self, group):
+ """ Add the parameters of this rig type to the
+ RigifyParameters IDPropertyGroup
+
+ """
+ items = [('X', 'X', ''), ('Z', 'Z', '')]
+ group.palm_rotation_axis = bpy.props.EnumProperty(items=items, name="Palm Rotation Axis", default='X')
+
+ @classmethod
+ def parameters_ui(self, layout, obj, bone):
+ """ Create the ui for the rig parameters.
+
+ """
+ params = obj.pose.bones[bone].rigify_parameters[0]
+
+ r = layout.row()
+ r.label(text="Primary rotation axis:")
+ r.prop(params, "palm_rotation_axis", text="")
+
+ @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('palm.parent')
+ bone.head[:] = 0.0000, 0.0000, 0.0000
+ bone.tail[:] = 0.0577, 0.0000, -0.0000
+ bone.roll = 3.1416
+ bone.use_connect = False
+ bones['palm.parent'] = bone.name
+ bone = arm.edit_bones.new('palm.04')
+ bone.head[:] = 0.0577, 0.0315, -0.0000
+ bone.tail[:] = 0.1627, 0.0315, -0.0000
+ bone.roll = 3.1416
+ bone.use_connect = False
+ bone.parent = arm.edit_bones[bones['palm.parent']]
+ bones['palm.04'] = bone.name
+ bone = arm.edit_bones.new('palm.03')
+ bone.head[:] = 0.0577, 0.0105, -0.0000
+ bone.tail[:] = 0.1627, 0.0105, -0.0000
+ bone.roll = 3.1416
+ bone.use_connect = False
+ bone.parent = arm.edit_bones[bones['palm.parent']]
+ bones['palm.03'] = bone.name
+ bone = arm.edit_bones.new('palm.02')
+ bone.head[:] = 0.0577, -0.0105, -0.0000
+ bone.tail[:] = 0.1627, -0.0105, -0.0000
+ bone.roll = 3.1416
+ bone.use_connect = False
+ bone.parent = arm.edit_bones[bones['palm.parent']]
+ bones['palm.02'] = bone.name
+ bone = arm.edit_bones.new('palm.01')
+ bone.head[:] = 0.0577, -0.0315, -0.0000
+ bone.tail[:] = 0.1627, -0.0315, -0.0000
+ bone.roll = 3.1416
+ bone.use_connect = False
+ bone.parent = arm.edit_bones[bones['palm.parent']]
+ bones['palm.01'] = bone.name
+
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pbone = obj.pose.bones[bones['palm.parent']]
+ 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['palm.04']]
+ pbone.rigify_type = ''
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, True, True)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'YXZ'
+ pbone = obj.pose.bones[bones['palm.03']]
+ pbone.rigify_type = ''
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, True, True)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'YXZ'
+ pbone = obj.pose.bones[bones['palm.02']]
+ pbone.rigify_type = ''
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, True, True)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'YXZ'
+ pbone = obj.pose.bones[bones['palm.01']]
+ pbone.rigify_type = 'palm'
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, True, True)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'YXZ'
+ pbone.rigify_parameters.add()
+
+ 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
+
diff --git a/rigify/rigs/spine.py b/rigify/rigs/spine.py
new file mode 100644
index 00000000..2318b3b9
--- /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 == 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 == 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 == 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
+