From 0f24f76bc870146aec5c0c6fb15daac24c0ae1cc Mon Sep 17 00:00:00 2001
From: Nathan Vegdahl <cessen@cessen.com>
Date: Thu, 24 Feb 2011 02:16:38 +0000
Subject: [PATCH] The leg/foot rig can now rock from side to side.  Requires a
 special metarig setup (not yet added to the default human metarig).

---
 rigify/rigs/biped/leg/__init__.py |   7 ++
 rigify/rigs/biped/leg/deform.py   |   8 +-
 rigify/rigs/biped/leg/fk.py       |   8 +-
 rigify/rigs/biped/leg/ik.py       | 132 +++++++++++++++++++++---------
 rigify/utils.py                   |   9 ++
 5 files changed, 119 insertions(+), 45 deletions(-)

diff --git a/rigify/rigs/biped/leg/__init__.py b/rigify/rigs/biped/leg/__init__.py
index 4c9997bf5..65af500aa 100644
--- a/rigify/rigs/biped/leg/__init__.py
+++ b/rigify/rigs/biped/leg/__init__.py
@@ -177,6 +177,13 @@ class Rig:
         bone.use_connect = True
         bone.parent = arm.edit_bones[bones['shin']]
         bones['heel'] = bone.name
+        bone = arm.edit_bones.new('heel.02')
+        bone.head[:] = -0.0500, -0.0200, 0.0000
+        bone.tail[:] = 0.0500, -0.0200, 0.0000
+        bone.roll = 0.0000
+        bone.use_connect = False
+        bone.parent = arm.edit_bones[bones['heel']]
+        bones['heel.02'] = bone.name
         bone = arm.edit_bones.new('toe')
         bone.head[:] = 0.0000, -0.1200, 0.0300
         bone.tail[:] = 0.0000, -0.2000, 0.0300
diff --git a/rigify/rigs/biped/leg/deform.py b/rigify/rigs/biped/leg/deform.py
index df6c6a604..2f9b2b028 100644
--- a/rigify/rigs/biped/leg/deform.py
+++ b/rigify/rigs/biped/leg/deform.py
@@ -21,7 +21,7 @@ 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 connected_children_names, has_connected_children
 from rigify.utils import strip_org, make_mechanism_name, make_deformer_name
 
 
@@ -94,10 +94,10 @@ class Rig:
         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:
+                if len(b.children) >= 1 and has_connected_children(b):
                     foot = b.name
+                else:
+                    heel = b.name
 
         if foot == None or heel == None:
             raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone)))
diff --git a/rigify/rigs/biped/leg/fk.py b/rigify/rigs/biped/leg/fk.py
index 628798f8b..f32fc36dd 100644
--- a/rigify/rigs/biped/leg/fk.py
+++ b/rigify/rigs/biped/leg/fk.py
@@ -21,7 +21,7 @@ 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 connected_children_names, has_connected_children
 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
@@ -54,10 +54,10 @@ class Rig:
         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:
+                if len(b.children) >= 1 and has_connected_children(b):
                     foot = b.name
+                else:
+                    heel = b.name
 
         if foot == None or heel == None:
             raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone)))
diff --git a/rigify/rigs/biped/leg/ik.py b/rigify/rigs/biped/leg/ik.py
index 6dc232dfd..b2328f4d4 100644
--- a/rigify/rigs/biped/leg/ik.py
+++ b/rigify/rigs/biped/leg/ik.py
@@ -21,7 +21,7 @@ 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 connected_children_names, has_connected_children
 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
@@ -102,14 +102,19 @@ class Rig:
         # Get the foot and heel
         foot = None
         heel = None
+        rocker = 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:
+                if len(b.children) >= 1 and has_connected_children(b):
                     foot = b.name
+                else:
+                    heel = b.name
+                    if len(b.children) > 0:
+                        rocker = b.children[0].name
+                    
 
         if foot == None or heel == None:
+            print("blah")
             raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone)))
 
         # Get the toe
@@ -122,7 +127,7 @@ class Rig:
         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]
+        self.org_bones = leg_bones + [foot, toe, heel, rocker]
 
         # Get rig parameters
         if params.separate_ik_layers:
@@ -140,6 +145,10 @@ class Rig:
         """
         bpy.ops.object.mode_set(mode='EDIT')
 
+        make_rocker = False
+        if self.org_bones[5] is not None:
+            make_rocker = True
+
         # 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"))))
@@ -154,8 +163,12 @@ class Rig:
         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")))
+        roll1 = copy_bone(self.obj, self.org_bones[4], make_mechanism_name(strip_org(self.org_bones[2] + ".roll.01")))
+        roll2 = copy_bone(self.obj, self.org_bones[4], make_mechanism_name(strip_org(self.org_bones[2] + ".roll.02")))
+
+        if make_rocker:
+            rocker1 = copy_bone(self.obj, self.org_bones[5], make_mechanism_name(strip_org(self.org_bones[2] + ".rocker.01")))
+            rocker2 = copy_bone(self.obj, self.org_bones[5], make_mechanism_name(strip_org(self.org_bones[2] + ".rocker.02")))
 
         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")))
@@ -176,6 +189,9 @@ class Rig:
         foot_roll_e = eb[foot_roll]
         roll1_e = eb[roll1]
         roll2_e = eb[roll2]
+        if make_rocker:
+            rocker1_e = eb[rocker1]
+            rocker2_e = eb[rocker2]
         visfoot_e = eb[visfoot]
         vispole_e = eb[vispole]
 
@@ -213,6 +229,14 @@ class Rig:
         vispole_e.use_connect = False
         vispole_e.parent = None
 
+        if make_rocker:
+            rocker1_e.use_connect = False
+            rocker2_e.use_connect = False
+
+            roll1_e.parent = rocker2_e
+            rocker2_e.parent = rocker1_e
+            rocker1_e.parent = foot_e
+
         # Misc
         foot_e.use_local_location = False
 
@@ -270,6 +294,14 @@ class Rig:
         visfoot_e.tail = visfoot_e.head + Vector((0, 0, v1.length / 32))
         vispole_e.tail = vispole_e.head + Vector((0, 0, v1.length / 32))
 
+        if make_rocker:
+            d = toe_e.y_axis.dot(rocker1_e.x_axis)
+            if d >= 0.0:
+                flip_bone(self.obj, rocker2)
+            else:
+                flip_bone(self.obj, rocker1)
+            
+
         # Weird alignment issues.  Fix.
         toe_parent_e.head = Vector(org_foot_e.head)
         toe_parent_e.tail = Vector(org_foot_e.tail)
@@ -297,6 +329,9 @@ class Rig:
         foot_roll_p = pb[foot_roll]
         roll1_p = pb[roll1]
         roll2_p = pb[roll2]
+        if make_rocker:
+            rocker1_p = pb[rocker1]
+            rocker2_p = pb[rocker2]
         toe_p = pb[toe]
         toe_parent_p = pb[toe_parent]
         toe_parent_socket1_p = pb[toe_parent_socket1]
@@ -314,12 +349,22 @@ class Rig:
             shin_p.lock_ik_x = True
             shin_p.lock_ik_y = True
 
-        # Foot roll control only rotates on x-axis.
+        # Foot roll control only rotates on x-axis, or x and y if rocker.
         foot_roll_p.rotation_mode = 'XYZ'
-        foot_roll_p.lock_rotation = False, True, True
+        if make_rocker:
+            foot_roll_p.lock_rotation = False, False, True
+        else:
+            foot_roll_p.lock_rotation = False, True, True
         foot_roll_p.lock_location = True, True, True
         foot_roll_p.lock_scale = True, True, True
 
+        # roll and rocker bones set to euler rotation
+        roll1_p.rotation_mode = 'XYZ'
+        roll2_p.rotation_mode = 'XYZ'
+        if make_rocker:
+            rocker1_p.rotation_mode = 'XYZ'
+            rocker2_p.rotation_mode = 'XYZ'
+
         # Pole target only translates
         pole_p.lock_location = False, False, False
         pole_p.lock_rotation = True, True, True
@@ -372,34 +417,47 @@ class Rig:
         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'
+        # Foot roll drivers
+        fcurve = roll1_p.driver_add("rotation_euler", 0)
+        driver = fcurve.driver
+        var = driver.variables.new()
+        driver.type = 'SCRIPTED'
+        driver.expression = "min(0,var)"
+        var.name = "var"
+        var.targets[0].id_type = 'OBJECT'
+        var.targets[0].id = self.obj
+        var.targets[0].data_path = foot_roll_p.path_from_id() + '.rotation_euler[0]'
+
+        fcurve = roll2_p.driver_add("rotation_euler", 0)
+        driver = fcurve.driver
+        var = driver.variables.new()
+        driver.type = 'SCRIPTED'
+        driver.expression = "max(0,var)"
+        var.name = "var"
+        var.targets[0].id_type = 'OBJECT'
+        var.targets[0].id = self.obj
+        var.targets[0].data_path = foot_roll_p.path_from_id() + '.rotation_euler[0]'
+
+        if make_rocker:
+            fcurve = rocker1_p.driver_add("rotation_euler", 0)
+            driver = fcurve.driver
+            var = driver.variables.new()
+            driver.type = 'SCRIPTED'
+            driver.expression = "max(0,-var)"
+            var.name = "var"
+            var.targets[0].id_type = 'OBJECT'
+            var.targets[0].id = self.obj
+            var.targets[0].data_path = foot_roll_p.path_from_id() + '.rotation_euler[1]'
+
+            fcurve = rocker2_p.driver_add("rotation_euler", 0)
+            driver = fcurve.driver
+            var = driver.variables.new()
+            driver.type = 'SCRIPTED'
+            driver.expression = "max(0,var)"
+            var.name = "var"
+            var.targets[0].id_type = 'OBJECT'
+            var.targets[0].id = self.obj
+            var.targets[0].data_path = foot_roll_p.path_from_id() + '.rotation_euler[1]'
 
         # Constrain org bones to controls
         con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS')
diff --git a/rigify/utils.py b/rigify/utils.py
index f0a19aac6..f7a33c220 100644
--- a/rigify/utils.py
+++ b/rigify/utils.py
@@ -385,6 +385,15 @@ def connected_children_names(obj, bone_name):
     return names
 
 
+def has_connected_children(bone):
+    """ Returns true/false whether a bone has connected children or not.
+    """
+    t = False
+    for b in bone.children:
+        t = t or b.use_connect
+    return t
+
+
 def get_layers(layers):
     """ Does it's best to exctract a set of layers from any data thrown at it.
     """
-- 
GitLab