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/biped')
-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
9 files changed, 2221 insertions, 0 deletions
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]
+