Armature creation (2.53)

Hey all,

I’ve spent the last couple days reading through the 2.53 API as well as just about all the import scripts included with the windows release but still haven’t quite gotten where I need to. Before you say anything, yes, I’ve read the example script for armature creation on these forums, but it didn’t solve my problem.

Anyway, in 2.49, I could create a bone, link it to an armature object, parent it, then change the .head to the armature space coords, then change the .matrix to the armature space rotation matrix, like in the following code:


#Taken from ms3d_import.py in Blender 2.49
if bone.hasParent():
	bone.head =  Vector(pos) * bone.parent.matrix + bone.parent.head
	tempM = RM(rot) * bone.parent.matrix
	#tempM.transpose;
	bone.matrix = tempM
else:
	bone.head = Vector(pos)
	bone.matrix = RM(rot)
# set tail
bvec = bone.tail - bone.head
bvec.normalize()
bone.tail = bone.head + 0.01 * bvec

All the import scripts for 2.53 only manipulate .head and .tail and I take it these are in armature coords (the API doesn’t specify afaik). EditBone.matrix is readonly from the docs, so… I guess my question is, how does one go about doing this same thing in 2.53? Any help would be greatly appreciated!

You may want to try the following program. It does add armatures and pose them using bone matrices, although I am still confused about the relation between coordinate systems. But at least the program runs.

#---------------------------------------------------
# File armature.py
#---------------------------------------------------
import bpy
import math
import mathutils
from mathutils import Vector, Matrix

def createRig(name, origin, boneTable):
    # Create armature and object
    bpy.ops.object.add(
        type='ARMATURE', 
        enter_editmode=True,
        location=origin)
    ob = bpy.context.object
    ob.x_ray = True
    ob.name = name
    amt = ob.data
    amt.name = name+'Amt'
    amt.draw_axes = True
    
    # Create bones
    bpy.ops.object.mode_set(mode='EDIT')
    for (bname, pname, vector) in boneTable:        
        bone = amt.edit_bones.new(bname)
        if pname:
            parent = amt.edit_bones[pname]
            bone.parent = parent
            bone.head = parent.tail
            bone.connected = False
            mat = parent.matrix.rotation_part()
        else:
            bone.head = (0,0,0)
            mat = Matrix().identity().to_3x3()
        bone.tail = mat * Vector(vector) + bone.head
    bpy.ops.object.mode_set(mode='OBJECT')
    return ob
    
def poseRig(ob, poseTable):
    bpy.context.scene.objects.active = ob
    bpy.ops.object.mode_set(mode='POSE')
    deg2rad = 2*math.pi/360
        
    for (bname, axis, angle) in poseTable:
        pbone = ob.pose.bones[bname]
        # Set rotation mode to Euler XYZ, easier to understand
        # than default quaternions
        pbone.rotation_mode = 'XYZ'
        # Documentation bug: Euler.rotate(angle,axis):
        # axis in ['x','y','z'] and not ['X','Y','Z']
        pbone.rotation_euler.rotate(angle*deg2rad, axis.lower())
    bpy.ops.object.mode_set(mode='OBJECT')
    return
    

def run(origo):
    origin = Vector(origo)
    # Table of bones in the form (bone, parent, vector)
    # The vector is given in local coordinates
    boneTable1 = [
        ('Base', None, (1,0,0)),
        ('Mid', 'Base', (1,0,0)),
        ('Tip', 'Mid', (0,0,1))
    ]
    bent = createRig('Bent', origin, boneTable1)

    # The second rig is a straight line, i.e. bones run along local Y axis
    boneTable2 = [
        ('Base', None, (1,0,0)),
        ('Mid', 'Base', (0,0.5,0)),
        ('Mid2', 'Mid', (0,0.5,0)),
        ('Tip', 'Mid2', (0,1,0))
    ]
    straight = createRig('Straight', origin+Vector((0,2,0)), boneTable2)
    
    # Pose second rig
    poseTable2 = [
        ('Base', 'X', 90),
        ('Mid2', 'Z', 45),
        ('Tip', 'Y', -45)
    ]
    poseRig(straight, poseTable2)
    
    # Pose first rig
    poseTable1 = [
        ('Tip', 'Y', 45),
        ('Mid', 'Y', 45),
        ('Base', 'Y', 45)
    ]
    poseRig(bent, poseTable1)
    return
    

if __name__ == "__main__":
    run((0,5,0))

Thanks for the reply! That is actually one of two options I was brainstorming over, unfortunately I don’t have the vectors that you include in your boneTables. I’m assuming that they’re unit vectors in the direction of tail relative to the local bone and your matrix operations convert them to armature space (world coords). The only relevant information I have from my files are the local rotations and positions of each bone, meaning I couldn’t tell you the length of the bone itself, let alone it’s unit direction.

The other option I’ve been mauling over is to access the Bone(bpy_struct), as that gives write access to the matrix (at least, so the API says). I’m not sure this is right, as all the import scripts I’ve seen use EditBone – is this the right way to do what I’m looking for?