How to build an Armature in 2.5x from XML data?

How do I use the Blender API to create an armature from XML data that contains bone coordinates, names, and parenting hierarchies? And as a follow-up question, how can I parent a mesh to the armature and apply bone weights (also pulled from an XML file) to the vertices?

I’m working on an Ogre importer script (specifically for Torchlight characters), and I’ve figured out how to load in a mesh with help from a Blender Cookie tutorial, but after searching through the Blender API, I haven’t been able to find similar commands to load in an armature.

Creating a mesh is simple, once you’ve used an XML parser to get lists of vertices and faces (designated here by the variables “verts” and “faces”). The command “from_pydata” takes a tuple of three lists (vertices, edges, and faces) and creates the mesh geometry. You can even pass an empty edge list, and use the command “update(calc_edges)” to fill them in, as long as you have your vertex and face lists.

import bpy

verts = [...] # this is the vertex position list generated by your XML parser
faces = [...] # this is the face list generated by your XML parser

## create a new mesh; all it needs is a name
mesh = bpy.data.meshes.new("OgreObject")

## create a new object; all it needs is a name and a mesh
object = bpy.data.objects.new("OgreObject", mesh)

## set the location for your object in your scene
object.location = bpy.context.scene.cursor_location

## link your object to the scene
bpy.context.scene.objects.link(object)

## add the vertex and face data to your mesh
mesh.from_pydata(verts, [], faces)

## update the mesh to flush the buffered data
mesh.update(calc_edges=True)

This is the simple way to create a mesh. Note that I haven’t figured out yet how to add things like bone assignments, normals, and UV coordinates. Those are on my to-do list, but of lower priority than importing the armature.

(If anyone is interested, I can post the code for my XML parser later; I have it stored on another computer.)

For parenting them. You need to use the bpy.ops > operator command to parent them. But it need to selected two objects first and second. You can see the log in the top of the blender panel or header by dragging it down. The panel is the info panel.

For creating armature object and bones.

armdata = bpy.data.armatures.new(objectname)
ob_new = bpy.data.objects.new(meshname, armdata)
bpy.ops.object.mode_set(mode=‘EDIT’) #note they have be select in object and pose edit mode to make the new bones.
newbone = ob_new.data.edit_bones.new(bone.name)

newbone.head
newbone.tail
newbone.roll
#note it effect the rotation or direction.

parenting them
newbone.parent = ob_new.data.edit_bones[string name] #note they have to exist first

Thanks for that, it helped a lot. I’m still missing something, though. Here’s what I have so far in a test script (just trying to load two bones with one parented to the other):


import bpy

# create the armature object
armdata = bpy.data.armatures.new("skeleton")
skelly = bpy.data.objects.new("skeleton", armdata)

# add it to the scene
skelly.location = bpy.context.scene.cursor_location
bpy.context.scene.objects.link(skelly)

# set to edit mode
bpy.ops.object.mode_set(mode='EDIT')    # CRASH

# add bones to the armature
bone1 = skelly.data.edit_bones.new("bone1")
bone1.head = (0.0, 0.0, 0.0)
bone1.tail = (0.0, 0.0, 1.0)
bone2 = skelly.data.edit_bones.new("bone2")
bone2.head = (0.0, 0.0, 1.0)
bone2.tail = (0.0, 0.0, 0.0)

# parent bone2 to bone1
bone2.parent = skelly.data.edit_bones["bone1"]  # CRASH

As you can see, I’ve got two bugs in the script. In the first one, Blender won’t let me put the armature into Edit mode. This does work, however, if I enter the script into the console line by line, and then select the armature with my mouse in the 3D view before attempting to set it to Edit mode using Python. So the first fix I need is to figure out how to set the armature to the active object in my scene from inside the script.

The next bug is at the end, when I try to set up the parent relationship. I thought that your instructions meant that I had to enter the parent bone name inside the square brackets, but apparently I misunderstood. What is the correct way to enter this command?

I suggest you look at the bvh import script. It both builds an armature and animates it from the bvh text file data. The script uses a python node class to build the rig. My suggestion is use the node structure created there as a template for your xml file.

Hey, thanks – I’m still getting used to the new API, but I can already see that most (if not all) of what I need is right there.

#Deselect all the objects.
for i in bpy.context.scene.objects: i.select = False #deselect all objects
#Set active object.
bpy.context.scene.objects.active = obj

You have to make sure the object is select and active work so it will not crash.

You can check out the blender add on export for psk/psa. I add a basic code that let you rebuild your armature.

Here part of the code. It just rebuild the armature.


        bselected = False
        for obj in bpy.data.objects:
            if obj.type == 'ARMATURE' and obj.select == True:
                currentbone = [] #select armature for roll copy
                print("Armature Name:",obj.name)
                objectname = "ArmatureDataPSK"
                meshname ="ArmatureObjectPSK"
                armdata = bpy.data.armatures.new(objectname)
                ob_new = bpy.data.objects.new(meshname, armdata)
                bpy.context.scene.objects.link(ob_new)
                bpy.ops.object.mode_set(mode='OBJECT')
                for i in bpy.context.scene.objects: i.select = False #deselect all objects
                ob_new.select = True
                bpy.context.scene.objects.active = obj
                
                bpy.ops.object.mode_set(mode='EDIT')
                for bone in obj.data.edit_bones:
                    if bone.parent != None:
                        currentbone.append([bone.name,bone.roll])
                    else:
                        currentbone.append([bone.name,bone.roll])
                bpy.ops.object.mode_set(mode='OBJECT')
                for i in bpy.context.scene.objects: i.select = False #deselect all objects
                bpy.context.scene.objects.active = ob_new
                bpy.ops.object.mode_set(mode='EDIT')
                
                for bone in obj.data.bones:
                    bpy.ops.object.mode_set(mode='EDIT')
                    newbone = ob_new.data.edit_bones.new(bone.name)
                    newbone.head = bone.head_local
                    newbone.tail = bone.tail_local
                    for bonelist in currentbone:
                        if bone.name == bonelist[0]:
                            newbone.roll = bonelist[1]
                            break
                    if bone.parent != None:
                        parentbone = ob_new.data.edit_bones[bone.parent.name]
                        newbone.parent = parentbone
                print("Bone Count:",len(obj.data.bones))
                print("Hold Bone Count",len(currentbone))
                print("New Bone Count",len(ob_new.data.edit_bones))
                print("Rebuild Armture Finish:",ob_new.name)
                bpy.context.scene.update()
                bselected = True
                break