Bone Manipulation with Python in 2.5

If I create an armature and “get” it using the following code:

bpy.ops.object. armature_add()
for ob in bpy.data.objects:
	if ob.selected:
		newArmature=ob
		newArmature.name="newArmature"

I can’t figure out where to go from here to create, rename, and reposition new bones. I can toggle it into editmode, but I still can’t figure out what to iterate through to get the initial bone in the armature (to change its head/tail location and name) and then to extrude it and make new bones and then change their locations. I realize this is a lot of data, but I feel like its just one simple concept of “getting” the correct object which would allow all of the rest of this to happen.

I can see from hovering over the bone name area, that I need to access Editbone.name, and from the API I know there’s an Editbone class/variable stored within the Armature datablock? I just can’t figure out how to access this info for the life of me and autocomplete doesn’t seem to be working in the console on the objects I create in scene… Any help to any of my ramblings would be much appreciated.

–Ryan

I just noticed that if I “get” the armature using the above method and rename it, it doesn’t actually change the name of the armature when you view it through the datablocks mode of the outliner. Under this part of the outliner is where you can also see the bones and edit-bones properties. You can change the name of the datablock if you instead iterate through bpy.data.armatures (instead of bpy.data.scene). This doesn’t change the name in the properties window however. I’m narrowing in on it at the rate of one minor break through a day:)

I have recently tried in Blender 2.5 to code a python command to clear bones location for more than one armature but failed as well :frowning:

the armature datablock has it own name: ob.data.name=“newnameforarmature” does the trick. This is the same for all Blender Objects, Each points to an object that is a full fledged entity in its own right, this way you can instantiate shared copies (with Alt-D) that differ only in location and rotatation for example but share the same mesh data, The name of such a datablock lives in it own namespace so you may have and object called newArmature and an armature called the same.

varkenvarken is completely right. The difference between objects and object data has existed in blender since the first version I think, it’s not something new in 2.5

Here’s a script example that creates and modifies an armature:

from math import pi

bpy.ops.object.armature_add()
for ob in bpy.data.objects:
    if ob.selected:
        break
arm = ob.data

bpy.ops.object.mode_set(mode='EDIT')
for i in range(3):
    bpy.ops.armature.extrude()
    bpy.ops.tfm.transform(mode='TRANSLATION', value=(0.0, 0.0, 1.0, 0))
bpy.ops.object.mode_set(mode='OBJECT')

bpy.ops.object.mode_set(mode='EDIT')
for i in range(len(arm.edit_bones)):
    eb = arm.edit_bones[i]
    eb.connected = True
    eb.roll = i*(20/180*pi)
    eb.tail[0] = eb.head[0] + 0.2*(i+1)
bpy.ops.object.mode_set(mode='OBJECT')

You can find a full explanation here.

Hey guys thanks for your replies:) I didn’t realize I wasn’t subscribed to the thread so I didn’t see anyone posted till just now. Seeing as how in very short order people are going to be looking to do stuff I’ll post my code as I have it right now to generate a Leg armature. maybe someone will have some questions answered. I used some of the same methods as Crouch but a few different. Maybe we can have some discussion as to pro’s and con’s. And yes if you do just run this, there might be errors:)

import bpy

prefix = "L_"

guides = [
		["Thigh", 0, 0, 4],
		["Knee", 0, -2, 1],
		["Ankle", 0, 0, -2],
		["Ball", 0, -1, -3],
		["Toe", 0,  -2, -3],
		["Heel", 0, 0,-3],
		]
empties = []

def createEmpty(name, x, y, z):
	bpy.ops.object.object_add(type = 'EMPTY')
	for ob in bpy.data.objects:
		if ob.selected:
			empty = ob
			empty.name = prefix + name + "_EMPTY"
			empty.location = (x, y, z)
			empties.append(empty)
			
def setBonePos(bone, headLoc, tailLoc):
	bone.head = headLoc
	bone.tail = tailLoc
	

def triangulatePos(locA, locB):
	dif = locA-locB
	dif = dif.normalize()
	newLoc = locA + dif
	return newLoc

for guide in guides:
	createEmpty(guide[0], guide[1], guide[2], guide[3])
		

The first part of the code goes through creating empties. Eventually the user will be able to move the empties and my rig will be based on these positions. There are some other basic functions for setting the positions of bones etc…


bpy.ops.object.armature_add()

b_names = ["Thigh", "Calf", "Foot", "Toes"]
control_bones = ["Foot_Control", "Ankle_Goal", "Calf_Rot", "Ankle_Angle", "Foot_Angle"]

for ob in bpy.data.objects:
	if ob.selected:
		newArmature = ob;
		newArmature.name = "ArmatureObject"
		newArmature.location = (0, 0, 0)

for arm in bpy.data.armatures:
	
	newArmature = arm
	newArmature.name = "New_Armature"
	newArmature.bones[0].name = "throwaway"
	newArmature.bones[0].location = (10,10,10)
	bpy.ops.object.editmode_toggle()
	
	e = 0

	#step through list of bone names and create all skeleton bones for leg

	for b_name in b_names:
		if e >= 0:
			bpy.ops.armature.bone_primitive_add(name = prefix + b_name + "_BONE")
		e += 1
		
	e = 0
	
	#step through control bone list and create all controll bones
	for c_bone in control_bones:
		bpy.ops.armature.bone_primitive_add(name = prefix + c_bone)
	
	e = 0
	

this part of the code creates all of the bones for the armature. I used a different method than the extrude method because this method allowed me to name the created bones right away. This is key for my eventual script. Is there a downside to using the add bone and parenting separately as opposed to extruding?

	#position each bone according to postion of empties or calculations
	
	setBonePos(newArmature.edit_bones["L_Thigh_BONE"], bpy.data.objects["L_Thigh_EMPTY"].location,
bpy.data.objects["L_Knee_EMPTY"].location)
	setBonePos(newArmature.edit_bones["L_Calf_BONE"], bpy.data.objects["L_Knee_EMPTY"].location,
bpy.data.objects["L_Ankle_EMPTY"].location)
	setBonePos(newArmature.edit_bones["L_Foot_BONE"], bpy.data.objects["L_Ankle_EMPTY"].location,
bpy.data.objects["L_Ball_EMPTY"].location)
	setBonePos(newArmature.edit_bones["L_Toes_BONE"], bpy.data.objects["L_Ball_EMPTY"].location,
bpy.data.objects["L_Toe_EMPTY"].location)
	
	newArmature.edit_bones["L_Calf_BONE"].parent = newArmature.edit_bones["L_Thigh_BONE"]
	newArmature.edit_bones["L_Foot_BONE"].parent = newArmature.edit_bones["L_Calf_BONE"]
	newArmature.edit_bones["L_Toes_BONE"].parent = newArmature.edit_bones["L_Foot_BONE"]
	newArmature.edit_bones["L_Calf_Rot"].parent = newArmature.edit_bones["L_Calf_BONE"]
	
	#compute distance from ball empty to toe empty and use that number to determine length of foot control bone
	fcCalc = (bpy.data.objects["L_Ball_EMPTY"].location - bpy.data.objects["L_Toe_EMPTY"].location) +
bpy.data.objects["L_Heel_EMPTY"].location
	setBonePos(newArmature.edit_bones["L_Foot_Control"], bpy.data.objects["L_Heel_EMPTY"].location, fcCalc)
	
	#compute vector of calf bone to create control bones in perfect allignment
	b_vector = newArmature.edit_bones["L_Calf_BONE"].tail - newArmature.edit_bones["L_Calf_BONE"].head
	b_vector = b_vector.normalize()
	b_loc = (newArmature.edit_bones["L_Calf_BONE"].tail + b_vector)
	
	setBonePos(newArmature.edit_bones["L_Ankle_Goal"], newArmature.edit_bones["L_Calf_BONE"].tail, b_loc)
	setBonePos(newArmature.edit_bones["L_Calf_Rot"], newArmature.edit_bones["L_Calf_BONE"].tail, b_loc)
	setBonePos(newArmature.edit_bones["L_Ankle_Angle"], newArmature.edit_bones["L_Foot_BONE"].tail,
newArmature.edit_bones["L_Foot_BONE"].head)
	setBonePos(newArmature.edit_bones["L_Foot_Angle"], newArmature.edit_bones["L_Toes_BONE"].head,
newArmature.edit_bones["L_Toes_BONE"].tail)
	
	newArmature.edit_bones["L_Ankle_Goal"].parent = newArmature.edit_bones["L_Foot_Control"]
	newArmature.edit_bones["L_Foot_BONE"].parent = newArmature.edit_bones["L_Foot_Control"]
	newArmature.edit_bones["L_Ankle_Angle"].parent = newArmature.edit_bones["L_Foot_Control"]
	newArmature.edit_bones["L_Ankle_Goal"].parent = newArmature.edit_bones["L_Ankle_Angle"]
	newArmature.edit_bones["L_Foot_Angle"].parent = newArmature.edit_bones["L_Foot_Control"]

this part goes through and sets up the parenting relationships for all bones–including what will eventually be control
bones.

bpy.ops.object.mode_set(mode='POSE')
	
	#bpy.ops.pose.ik_add(with_targets = True)
	
	#bpy.ops.pose.constraint_add_with_targets(type='COPY_ROTATION')

This is the part that currently has me stumped. As far as I can tell, there’s no way to select bones so these methods for
adding constraints won’t work. Does anyone have any advice?

1 Like

Had my question answered elsewhere:) The key to constraints is toggling the .selected and .active flags. I thought they were read only but apparently not. Both bones/objects should be .selected, and the driven bone/object should be .active (usually anyway).

well I come before you with a new interesting problem:) Any advice on why this is happening would be great. I tested this in a new scene where I created a basic leg right and then just tried to set up the IK using scripts and got the same results, so I know its not due to earlier code problems.

newArmature.bones["L_Ankle_Goal"].selected = True
	newArmature.bones["L_Calf_BONE"].selected = True
	newArmature.bones["L_Calf_BONE"].active = True	
	
	bpy.ops.pose.ik_add(with_targets = True)

What happens: In code I create my skeleton (I do set the connected flags up which aren’t in the code in preceeding posts), I shift to pose mode, I run that code. Instead of creating IK from L_Ankle_Goal to L_Calf_BONE, it creates a new/random empty and runs the IK From that to the calf bone. Is this because I forgot a step or forgot to set a flag or something? Any advice would be great.