From 83840e5713eaeaf3b8ec0c779d0dffe8666cd85c Mon Sep 17 00:00:00 2001
From: Nathan Vegdahl <cessen@cessen.com>
Date: Sat, 18 Jun 2011 07:04:53 +0000
Subject: [PATCH] Rigify: added bend hinting to the arm and leg rigs.

This allows IK arms and legs to still function properly even when they
start with an entirely straight chain.
---
 rigify/CREDITS                    |  1 +
 rigify/README                     | 36 +++++++++++++++----------------
 rigify/rigs/biped/arm/__init__.py |  5 +++++
 rigify/rigs/biped/arm/ik.py       | 32 +++++++++++++++++++++++++++
 rigify/rigs/biped/leg/__init__.py |  5 +++++
 rigify/rigs/biped/leg/ik.py       | 32 +++++++++++++++++++++++++++
 6 files changed, 93 insertions(+), 18 deletions(-)

diff --git a/rigify/CREDITS b/rigify/CREDITS
index 04bf07312..dd517fb16 100644
--- a/rigify/CREDITS
+++ b/rigify/CREDITS
@@ -6,6 +6,7 @@ Original prototyping and development, and Python API support:
 General financial support:
 - Benjamin Tolputt
 - Nesterenko Viktoriya
+- Jeff Hogan
 
 IK/FK snapping financial support:
 - Benjamin Tolputt
diff --git a/rigify/README b/rigify/README
index 69118e3ac..790942f97 100644
--- a/rigify/README
+++ b/rigify/README
@@ -11,14 +11,14 @@ armature that contains data about how to construct the rig.  In particular, it
 contains bones in the basic configuration of the rig, with some bones tagged
 to indicate the rig type.
 
-For example, a metaig might contain a chain of three bones, the root-most of
+For example, a metarig might contain a chain of three bones, the root-most of
 which is tagged as being a biped arm.  When given as input to Rigify, Rigify
 will then generate a fully-featured biped arm rig in the same position and
 proportions as the 3-bone chain.
 
 One could also have another chain of bones, the root-most of which is tagged as
 being a spine.  And the root-most bone of the arm chain could be the child of
-any of those spine bones.  Then the rig that Rigify would generate would be a
+any of those spine bones.  Then the rig that Rigify generates would be a
 spine rig with an arm rig attached to it.
 
 
@@ -45,7 +45,7 @@ information about it.
 It traverses the bones in a root-most to leaf-most order, and whenever it
 stumbles upon a bone that has a rig type tagged on it, it creates a rig-type
 python object (rig types will be explained further down) for that rig type,
-and executes the resulting object's information gathering code.
+and executes the resulting python object's information gathering code.
 
 At the end of the information gathering stage, Rigify has a collection of
 python objects, each of which know all the information they need to generate
@@ -77,33 +77,33 @@ __init__() is the "information gathering" code for the rig type.  When Rigify
 loops through the bones and finds a tagged bone, it will create a python
 object from the Rig class, executing this method.
 In addition to the default "self" parameter, __init__() needs to take the
-armature object, the name of the bone that was tagged, and the bone's rig type
-parameters object.
+armature object, the name of the bone that was tagged, and a parameters object.
 
 A proper rig-type __init__() will look like this:
 
-    def __init__(self, obj, bone, params):
+    def __init__(self, obj, bone_name, params):
         # code goes here
 
 At the bare minimum, you are going to want to store the object and bone name
 in the rig type object for later reference in the generate method.  So:
 
-    def __init__(self, obj, bone, params):
+    def __init__(self, obj, bone_name, params):
         self.obj = obj
-        self.org_bone = bone
+        self.org_bone = bone_name
 
 Most rig types involve more than just that one bone, though, so you will also
 want to store the names of any other relevant bones.  For example, maybe the
-parent of the tagged bone:
+parent of the tagged bone is important to the rig type:
 
-    def __init__(self, obj, bone, params):
+    def __init__(self, obj, bone_name, params):
         self.obj = obj
-        self.org_bone = bone
-        self.org_parent = obj.data.bones[bone].parent.name
+        self.org_bone = bone_name
+        self.org_parent = obj.data.bones[bone_name].parent.name
 
-It is important that store the _names_ of the bones, and not direct references.
-Due to how the armature system in Blender works, when flipping in and out of
-edit mode, pose-bone and edit-bone references get lost. (Arg...)
+It is important that you store the _names_ of the bones, and not direct
+references.  Due to the inner workings of Blender's armature system, direct
+edit-bone and pose-bone references are lost when flipping in and out of
+armature edit mode. (Arg...)
 
 Remember that it is critical that the information-gathering method does _not_
 modify the armature in any way.  This way all of the rig type's info-gathering
@@ -118,13 +118,13 @@ actually generate the rig.  It takes the form:
     def generate(self):
         # code goes here
 
-It doesn't take any parameters beyond "self".  So you really need to store any
+It doesn't take any parameters beyond "self".  So you have to store any
 information you need with the __init__() method.
 
-Generate pretty much has free reign to do whatever it wants, with the exception
+generate() pretty much has free reign to do whatever it wants, with the exception
 of two simple rules:
 1. Other than the "ORG-" bones, do not touch anything that is not created by
-this method (this prevents rig types from messing each other up).
+the rig type (this prevents rig types from messing each other up).
 2. Even with "ORG-" bones, the only thing you are allowed to do is add children
 and add constraints.  Do not rename them, do not remove children or
 constraints, and especially do not change their parents.  (Adding constraints
diff --git a/rigify/rigs/biped/arm/__init__.py b/rigify/rigs/biped/arm/__init__.py
index c09b39f46..93e757f4e 100644
--- a/rigify/rigs/biped/arm/__init__.py
+++ b/rigify/rigs/biped/arm/__init__.py
@@ -96,6 +96,8 @@ class Rig:
         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.bend_hint = bpy.props.BoolProperty(name="Bend Hint", default=True, description="Give IK chain a hint about which way to bend.  Useful for perfectly straight chains.")
+
         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.")
 
@@ -159,6 +161,9 @@ class Rig:
         r.label(text="Elbow rotation axis:")
         r.prop(params, "primary_rotation_axis", text="")
 
+        r = layout.row()
+        r.prop(params, "bend_hint")
+
         col = layout.column()
         col.prop(params, "use_upper_arm_twist")
         col.prop(params, "use_forearm_twist")
diff --git a/rigify/rigs/biped/arm/ik.py b/rigify/rigs/biped/arm/ik.py
index b16221d8b..0ecf70e75 100644
--- a/rigify/rigs/biped/arm/ik.py
+++ b/rigify/rigs/biped/arm/ik.py
@@ -84,6 +84,8 @@ class Rig:
         else:
             self.layers = None
 
+        self.bend_hint = params.bend_hint
+
         self.primary_rotation_axis = params.primary_rotation_axis
 
     def generate(self):
@@ -198,6 +200,36 @@ class Rig:
             prop["soft_min"] = prop["min"] = 0.0
             prop["soft_max"] = prop["max"] = 1.0
 
+        # Bend direction hint
+        if self.bend_hint:
+            con = farm_p.constraints.new('LIMIT_ROTATION')
+            con.name = "bend_hint"
+            con.owner_space = 'LOCAL'
+            if self.primary_rotation_axis == 'X':
+                con.use_limit_x = True
+                con.min_x = pi / 10
+                con.max_x = pi / 10
+            elif self.primary_rotation_axis == '-X':
+                con.use_limit_x = True
+                con.min_x = -pi / 10
+                con.max_x = -pi / 10
+            elif self.primary_rotation_axis == 'Y':
+                con.use_limit_y = True
+                con.min_y = pi / 10
+                con.max_y = pi / 10
+            elif self.primary_rotation_axis == '-Y':
+                con.use_limit_y = True
+                con.min_y = -pi / 10
+                con.max_y = -pi / 10
+            elif self.primary_rotation_axis == 'Z':
+                con.use_limit_z = True
+                con.min_z = pi / 10
+                con.max_z = pi / 10
+            elif self.primary_rotation_axis == '-Z':
+                con.use_limit_z = True
+                con.min_z = -pi / 10
+                con.max_z = -pi / 10
+
         # IK Constraint
         con = farm_p.constraints.new('IK')
         con.name = "ik"
diff --git a/rigify/rigs/biped/leg/__init__.py b/rigify/rigs/biped/leg/__init__.py
index f2ea2e51f..8f9286c71 100644
--- a/rigify/rigs/biped/leg/__init__.py
+++ b/rigify/rigs/biped/leg/__init__.py
@@ -98,6 +98,8 @@ class Rig:
         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.bend_hint = bpy.props.BoolProperty(name="Bend Hint", default=True, description="Give IK chain a hint about which way to bend.  Useful for perfectly straight chains.")
+
         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.")
 
@@ -161,6 +163,9 @@ class Rig:
         r.label(text="Knee rotation axis:")
         r.prop(params, "primary_rotation_axis", text="")
 
+        r = layout.row()
+        r.prop(params, "bend_hint")
+
         col = layout.column()
         col.prop(params, "use_thigh_twist")
         col.prop(params, "use_shin_twist")
diff --git a/rigify/rigs/biped/leg/ik.py b/rigify/rigs/biped/leg/ik.py
index 265b4e32c..54869b8e4 100644
--- a/rigify/rigs/biped/leg/ik.py
+++ b/rigify/rigs/biped/leg/ik.py
@@ -134,6 +134,8 @@ class Rig:
         else:
             self.layers = None
 
+        self.bend_hint = params.bend_hint
+
         self.primary_rotation_axis = params.primary_rotation_axis
 
     def generate(self):
@@ -376,6 +378,36 @@ class Rig:
             prop["soft_min"] = prop["min"] = 0.0
             prop["soft_max"] = prop["max"] = 1.0
 
+        # Bend direction hint
+        if self.bend_hint:
+            con = shin_p.constraints.new('LIMIT_ROTATION')
+            con.name = "bend_hint"
+            con.owner_space = 'LOCAL'
+            if self.primary_rotation_axis == 'X':
+                con.use_limit_x = True
+                con.min_x = pi / 10
+                con.max_x = pi / 10
+            elif self.primary_rotation_axis == '-X':
+                con.use_limit_x = True
+                con.min_x = -pi / 10
+                con.max_x = -pi / 10
+            elif self.primary_rotation_axis == 'Y':
+                con.use_limit_y = True
+                con.min_y = pi / 10
+                con.max_y = pi / 10
+            elif self.primary_rotation_axis == '-Y':
+                con.use_limit_y = True
+                con.min_y = -pi / 10
+                con.max_y = -pi / 10
+            elif self.primary_rotation_axis == 'Z':
+                con.use_limit_z = True
+                con.min_z = pi / 10
+                con.max_z = pi / 10
+            elif self.primary_rotation_axis == '-Z':
+                con.use_limit_z = True
+                con.min_z = -pi / 10
+                con.max_z = -pi / 10
+
         # IK Constraint
         con = shin_p.constraints.new('IK')
         con.name = "ik"
-- 
GitLab