This article explains how to write a Python script in Blender that creates multiple IK constraints for a selected armature. This script will do the following:
- Ensure that the selected object is an armature.
- Find IK bones indicated with '.ik' at the end of their names.
- Create and label target and pole bones for each IK bone.
- Apply an IK constraint and set the target and pole.
Auto-IK script caveats:
- The pole angle and chain length must be set manually.
- The script assumes the armature is facing along the -X axis (left) in Edit Mode.
Prerequisite knowledge: creating armatures with IK constraints, Python programming, and using the Blender console.
1) Ensure that the selected object is an armature
This article uses thebpy module to access and modify the properties of Blender objects. Import the module at the top of the script:
import bpyAll modules and sub-modules of bpy can be viewed by hovering the cursor over GUI elements in Blender. You can enable this feature under User Preferences > Interface and check 'Python Tooltips' under Development.
Get selected object
Set obj as the current selected object with bpy.context.active_object:
obj = bpy.context.active_object The selected object obj is not explicltly an armature because bpy.context.active_object can be an object of any type.
You can check obj's object type with .type:
print(obj.type)This will print an all-caps string of the selected object's type (eg. MESH or ARMATURE).
Check the selected object's type
Define a new method check_obj() with obj as a parameter:
def check_obj(obj):This method must be the first entry point of your script so that obj's type is an armature before other methods attempt to modify it.
Add the following conditional statement to check obj's type with .type and print a message to the console if it is not an armature type:
def check_obj(obj):
if obj.type != 'ARMATURE':
print("No armature selected.")
else:
# proceed with codeGet armature data from selected object
Define two separate variables to modify the armature in different contexts:
obj.datato get the armature's readonly data.obj.poseto modify the armature in Pose Mode.
Set armature as the variable to access the armature's readonly data with obj.data:
def check_obj(obj):
if obj.type != 'ARMATURE':
print("No armature selected.")
else:
armature = obj.dataSet armature_pose as the variable for modifying the armature in Pose Mode with obj.pose:
def check_obj(obj):
if obj.type != 'ARMATURE':
print("No armature selected.")
else:
armature = obj.data
armature_pose = obj.pose2) Identify IK bones within the armature
Access individual bones
There are multiple modules for editing or getting the properties of individual bones. This article uses the following modules to access the bones within an armature:
obj.data.bonesprovides readonly data of bones.obj.pose.bonesallows us to get and set bone constraints while in Pose Mode.obj.data.edit_bonesallows us to add new bones or edit bones in Edit Mode.
Each module takes a bone's name as a key. For example:
obj.data.bones["Bone.001"]
obj.pose.bones["Bone.001"]
obj.data.edit_bones["Bone.001"] Check for '.ik' at the end of bone names
Define a new method ik_bone_names() with armature as a parameter:
def ik_bone_names(armature):Create an empty list names to store identified IK bone names as strings:
def ik_bone_names(armature):
names = []Iterate through each bone name in armature.bones and use a substring of bone.name to check if the bone's name ends in '.ik'. Then add the names of IK bones to names and return the list:
def ik_bone_names(armature):
names = []
for bone in armature.bones:
if bone.name[-3:] == ".ik":
names.append(bone.name)
return names If names is returned as empty, then no IK bones were found. In the check_obj() method, add a conditional statement to print a message to the console if ik_bone_names()'s length is 0:
# check_obj()
...
armature = obj.data
armature_pose = obj.pose
if len(ik_bone_names(armature)) == 0:
print("No IK bones found. Did you add '.ik' to the end of the bone names?")If the length of ik_bone_names() is not 0, then proceed with a new autoik() method that takes armature, armature_pose, and ik_bone_names(armature) as arguments:
# check_obj()
...
if len(ik_bone_names(armature)) == 0:
print("No IK bones found. Did you add '.ik' to the end of the bone names?")
else:
autoik(armature, armature_pose, ik_bone_names(armature))The resulting code for check_obj() should be as shown below:
def check_obj(obj):
if obj.type != 'ARMATURE':
print("No armature selected.")
else:
armature = obj.data # armature
armature_pose = obj.pose # armature for editing bones in pose mode
if len(ik_bone_names(armature)) == 0:
print("No IK bones found. Did you add '.ik' to the end of the bone names?")
else:
autoik(armature, armature_pose, ik_bone_names(armature))3) Create target and pole
An IK constraint requires a target and a pole. The following steps explain how to create a target and pole bone.
Define the method autoik() with armature, armature_pose, and ik_bone_names as parameters:
def autoik(armature, armature_pose, ik_bone_names):The autoik() method will create the target and pole bones and apply an IK constraint to each IK bone.
Modifying individual bones
The following example represents a single IK bone with two variables:
ik_bonefor accessing the bone's readonly data.ik_bone_posefor modifying the bone's constraints in Pose Mode.
Iterate through the names in ik_bone_names and use them as keys for armature.bones and armature_pose.bones. Set ik_bone and ik_bone_pose to be our variables for modifying the current IK bone:
# autoik()
...
for name in ik_bone_names:
ik_bone = armature.bones[name] # current IK bone's readonly data
ik_bone_pose = armature_pose.bones[name] # current IK bone's constraintsEnumerate target and pole names
Since your script will be creating target and pole bones for each IK bone, there will be multiple target and pole pairs total. You want to the target and pole names enumerated with respect to each IK bone. For example:
target1, pole1, target2, pole2, target3, pole3 ...
In the autoik() method before the for name in ik_bone_names: statement, set a new variable n to 0 and increment it by 1 for every new IK bone:
def autoik(armature, armature_pose, ik_bone_names:)
n = 0
for name in ik_bone_names:
ik_bone = armature.bones[name]
ik_bone_pose = armature_pose.bones[name]
n += 1This is your counter for labeling targets and pole pairs in accordance with the order of each IK bone. Concatenate n to the target and pole names, t and p:
# autoik()
...
ik_bone = armature.bones[name]
ik_bone_pose = armature_pose.bones[name]
n += 1
t = 'target' + str(n) # pole name
p = 'pole' + str(n) # target nameNow t and p are the names of each target and pole enumerated with respect to every IK bone.
Create target bone
To add new bones to the selected armature, set the object interaction mode to Edit Mode with bpy.ops.object.mode_set():
# autoik()
...
for name in ik_bone_names:
...
bpy.ops.object.mode_set(mode='EDIT')Set edit as the variable to modify the armature in Edit Mode:
edit = bpy.context.object.data.edit_bonesYou want the target's head to intersect the IK bone's tail and its tail to extend along the X-axis by 1 unit as shown in the diagram below:
Positioning bones requires the following:
ik_boneprovides the local locations (relative to the armature) of the current IK bone's head and tail with.head_localand.tail_local.editprovides the global locations of a bone's head and tail with.headand.tail.- All properties return a vector of X, Y, and Z coordinates.
Create a new bone with .new and set its name to t:
edit.new(t)Set the global location of t's head to the local location of the current IK bone's tail:
edit[t].head = ik_bone.tail_localSet the global location of t's tail to the local location of the current IK bone's tail:
edit[t].tail = ik_bone.tail_localNote that if a bone has no length, it is automatically deleted. Use .x to move t's tail from its head in the X direction by 1 unit:
t.tail.x += 1Create the pole bone
To maintain control over the IK bone's rotation, you want the pole's head to intersect the IK bone's head and its tail to extend along the X-axis by 1 unit as shown in the image below:
Create a new bone with .new and set its name to p:
edit.new(p)Set the global location of p's head to the local location of the current IK bone's head:
edit[p].head = ik_bone.head_localSet the global location of p's tail to the local location of the current IK bone's head:
edit[p].tail = ik_bone.heaad_localUse .x to move t's tail from its head in the X direction by 1 unit:
t.tail.x += 1The resulting code for autoik() should be as shown:
def autoik(armature, armature_pose, ik_bone_names):
n = 0
for name in ik_bone_names:
ik_bone = armature.bones[name]
ik_bone_pose = armature_pose.bones[name]
n += 1
t = 'target'+str(n)
p = 'pole'+str(n)
bpy.ops.object.mode_set(mode='EDIT')
edit = bpy.context.object.data.edit_bones
# create target bone
edit.new(t)
edit[t].head = ik_bone.tail_local
edit[t].tail = ik_bone.tail_local
edit[t].tail.x += 1
# create pole bone
edit.new(p)
edit[p].head = ik_bone.head_local
edit[p].tail = ik_bone.head_local
edit[p].tail.x += 1 4) Apply IK constraint
In the autoik() method, set the object interaction mode to Pose Mode:
# autoik()
...
bpy.ops.object.mode_set(mode='POSE')Now that you are in Pose Mode, the variable for modifying the current IK bone is ik_bone_pose. Add a new constraint 'IK' to the current IK bone with .constraints.new():
ik_bone_pose.constraints.new('IK') Use 'IK' as a key for .constraints and set the selected armature object, obj, as the target of the IK constraint with .target:
ik_bone_pose.constraints['IK'].target = obj(Note: obj is not a parameter of autoik(), but is still accessible through check_obj() which encapsulates autoik().)
Set the target bone's .subtarget to the target bone's name, t:
ik_bone_pose.constraints['IK'].subtarget = tSet the .pole_target to obj. Then set the pole's .pole_subtarget to the pole bone's name, p:
ik_bone_pose.constraints['IK'].pole_target = obj
ik_bone_pose.constraints['IK'].pole_subtarget = pEntire script:
This is the final auto-IK Python script which applies IK constraints to a selected armature:
import bpy
# Get list of names of ik bones
def ik_bone_names(armature):
names = [] # name of ik bones
for bone in armature.bones:
if bone.name[-3:] == ".ik":
names.append(bone.name)
return names
# Apply IK constraints to armature
def autoik(armature, armature_pose, ik_bone_names):
n = 0
for name in ik_bone_names:
# current IK bone
ik_bone = armature.bones[name]
ik_bone_pose = armature_pose.bones[name]
n += 1
t = 'target'+str(n)
p = 'pole'+str(n)
# create target bone
bpy.ops.object.mode_set(mode='EDIT')
edit = bpy.context.object.data.edit_bones
edit.new(t)
edit[t].head = ik_bone.tail_local
edit[t].tail = ik_bone.tail_local
edit[t].tail.x += 1
# create pole bone
edit.new(p)
edit[p].head = ik_bone.head_local
edit[p].tail = ik_bone.head_local
edit[p].tail.x += 1
# add ik constraint to ik bone
bpy.ops.object.mode_set(mode='POSE')
ik_bone_pose.constraints.new('IK')
ik_bone_pose.constraints['IK'].target = obj
ik_bone_pose.constraints['IK'].subtarget = t
ik_bone_pose.constraints['IK'].pole_target = obj
ik_bone_pose.constraints['IK'].pole_subtarget = p
# Check if selected object is an armature
def check_obj(obj):
if obj.type != 'ARMATURE':
print("No armature selected.")
else:
armature = obj.data # armature
armature_pose = obj.pose # armature for editing bones in pose mode
if len(ik_bone_names(armature)) == 0:
print("No IK bones found. Did you add '.ik' to the end of the bone names?")
else:
autoik(armature, armature_pose, ik_bone_names(armature))
obj = bpy.context.active_object
check_obj(obj)


Comments
Post a Comment