1
$\begingroup$

I have a bone chain I would like to create an IK system on, but the chain is not flat on one of the cardinal axes (global x/y/z). How can I add a pole target bone to this chain without manually aligning the bones to the cardinal axes? For example, my character is imported with a chain in A-pose, but a T-pose would be better aligned.

$\endgroup$
1
$\begingroup$

To find a position to place the IK's pole target bone, one must do some basic vector math based on the existing bone chain.

Suppose you have a bone chain for an arm; one point is the shoulder; next, the elbow; lastly, the wrist. Using these three points, we can define a plane on which to create a new bone to act as the pole target. Using a point on this plane means the chain will not move when we add pole target influence to the IK constraint.

The first step in setting up an IK chain in this fashion is aligning bone rolls with Blender's built-in roll calculator (shortcut ctrl-n in bone edit mode). With correctly rolled bones, the IK solver behaves predictably, always pointing the chain at the pole target.

The second step is to find the plane defined by the three points in the bone chain. We can easily find a reasonable place to put a bone with the code snippet below.

Step three involves more vector math to find the proper pole angle for the IK constraint. This angle is important because it allows the rigger to create an offset between the bone chain's orientation and the pole target's direction. Using the right pole angle makes animating and rigging more visually intuitive. The code snippet below includes a segment to calculate and plug in the proper pole angle for the IK constraint.

Lastly, I included a segment to create the IK constraint and plug in the necessary bones and numbers. This makes the script a one-button step to create an IK from just shoulder, elbow and wrist bones.

Some Notes:

  • You will need to fill in the names of the bones and armature object you would like to use at the top of the script. Make sure these are the bones you want to use!
  • The IK target (not the pole target) must not be a child bone of the chain you are putting the IK constraint on.
  • Run this script from Blender's text editor.

Snippet:

import bpy
from math import degrees
from mathutils import *

# Set names here:
arm_name  = "Armature"
bone1     = "shoulder"
bone2     = "elbow"
ik_target = "ik.target"
pole_name = "ik.pole"

########################################
# Create and place IK pole target bone #
#          by Marco Giordano           #
########################################

bpy.ops.object.mode_set(mode='EDIT', toggle=False)

# Get points to define the plane on which to put the pole target
arm_obj = bpy.data.objects[arm_name]
arm_edit_bones = arm_obj.data.edit_bones
A = arm_edit_bones[bone1].head
B = arm_edit_bones[bone2].head
C = arm_edit_bones[bone2].tail

# Vector of chain root (shoulder) to chain tip (wrist)
AC = C - A

# Vector of chain root (shoulder) to second bone's head (elbow)
AB = B - A

# Multiply the two vectors to get the dot product
dot_prod = AB * AC

# Find the point on the vector AC projected from point B
proj = dot_prod / AC.length

# Normalize AC vector to keep it a reasonable magnitude
start_end_norm = AC.normalized()

# Project an arrow from AC projection point to point B
proj_vec  = start_end_norm * proj
arrow_vec = AB - proj_vec

# Place pole target at a reasonable distance from the chain
arrow_vec *= 2.0
final_vec = arrow_vec + B

# Add pole target bone and place it in the scene pointed to Z+
bpy.ops.armature.bone_primitive_add(name=pole_name)
arm_edit_bones[pole_name].head = final_vec
arm_edit_bones[pole_name].tail = final_vec + Vector((0.0, 0.5, 0.0))

# Enter Pose Mode to set up data for pole angle
bpy.ops.object.mode_set(mode='POSE', toggle=False)


##############
# Pole Angle #
# by Jerryno #
##############

def signed_angle(vector_u, vector_v, normal):
    # Normal specifies orientation
    angle = vector_u.angle(vector_v)
    if vector_u.cross(vector_v).angle(normal) < 1:
        angle = -angle
    return angle

def get_pole_angle(base_bone, ik_bone, pole_location):
    pole_normal = (ik_bone.tail - base_bone.head).cross(pole_location - base_bone.head)
    projected_pole_axis = pole_normal.cross(base_bone.tail - base_bone.head)
    return signed_angle(base_bone.x_axis, projected_pole_axis, base_bone.tail - base_bone.head)

base_bone = arm_obj.pose.bones[bone1]
ik_bone   = arm_obj.pose.bones[bone2]
pole_bone = arm_obj.pose.bones[pole_name]

pole_angle_in_radians = get_pole_angle(
    base_bone,
    ik_bone,
    pole_bone.matrix.translation)

print("Pole angle: {}".format(degrees(pole_angle_in_radians)))


#####################
# Add IK Constraint #
#####################

# Bone constraints are added in Pose Mode
bpy.ops.object.mode_set(mode='POSE', toggle=False)

# Add IK constraint
IK = arm_obj.pose.bones[bone2].constraints.new('IK')

# Set IK parameters
IK.target = arm_obj
IK.subtarget = arm_obj.pose.bones[ik_target].name
IK.pole_target = arm_obj
IK.pole_subtarget = arm_obj.pose.bones[pole_name].name
# Pole angle is set with radians, despite displaying as degrees in the UI
IK.pole_angle = pole_angle_in_radians

Pole angle: Jerryno
Vector math: Marco Giordano

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.