How do I get the position and rotation of a bone in an armature?

Just so we’re clear, this is my first forray into Python and Blender’s API, and I have no idea what I’m doing.

I’m trying to get the position and rotation of every bone in my Armature. The script you see here is a frankenstein of two example scripts. You can see my failed attempts to print the matrix commented out. Also, the vestigial remains of one of this script’s ancestors can be seen commented out.


#!BPY
"""
Name: 'Fatty'
Blender: 246
Group: 'Mesh generation'
Tooltip: 'Builds a mesh around an Envelope Armature'
"""
# Add a licence here if you wish to re-distribute, we recommend the GPL
import Blender
from Blender import Armature
from Blender.Mathutils import *
#
def main():
 arms = Armature.Get()
 
 for arm in arms.values():
  arm.drawType = Armature.ENVELOPE #set the draw type
     arm.makeEditable() #enter editmode
 
  mb = Blender.Metaball.New()
 
  for bone in arm.bones.values():
    element= mb.elements.add()
   #print  bone.matrix['ARMATURESPACE']
   print bone.getMatrix(space='worldspace')
 
  sce = Blender.Scene.GetCurrent()
  sce.objects.new(mb)
 
     #generating new editbone
     #eb = Armature.Editbone()
     #eb.roll = 10
     #eb.parent = arm.bones['Bone.003']
     #eb.head = Vector(1,1,1)
     #eb.tail = Vector(0,0,1)
     #eb.options = [Armature.HINGE, Armature.CONNECTED]
     #add the bone
     #arm.bones['myNewBone'] = eb
 
     #delete an old bone
     #del arm.bones['Bone.002']
     #arm.update()  #save changes
     #for bone in arm.bones.values():
       #print bone.matrix['ARMATURESPACE']
       #print bone.parent, bone.name
       #print bone.children, bone.name
       #print bone.options, bone.name
 
# This lets you can import the script without running it
if __name__ == '__main__':
 main()
 

print bone.matrix[‘ARMATURESPACE’] gives me the error 'sequence index must be integer, not ‘str’. I don’t understand why this isn’t working. The documentation of the Armature.Bone class seems to suggest it should work, and anyway the line is ripped from an earlier working script. I also tried getting the matrix directly from the bone, but that doesn’t work either.

I guess I must be making some obvious mistake that only newbies make. Any suggestions?

The goal of the script (as you may have guessed) is to create a meta-ball at each bone. Eventually, I’d like to do more with it, like scale the balls to match the envelope ranges of each joint, or use tubes instead of balls. This is just first experimental step. I wish I could have gotten further along. Hopefully, someone here can help me figure it out.

you’ve got your metaphors confused.

Armatures have editbone positions, but that is what the armature snaps back to when you enter edit mode, not as it is posed as an object at a certain frame…

to get them as poses, you need to look at the objects, not the armatures http://www.blender.org/documentation/246PythonDoc/Object.Object-class.html#getPose
then for each posebone get the poseMatrix as it is the final result of all ipo and constranits applied to the bone at that frame.
arm.name=“Human”
arm_ob.name=“Roger”
my_ob.name = “Ruby”
where both arm_ob and my_ob are human armatures

Gah.

I need more information. Everything I do now just says “invalid syntax.” :frowning:


#!BPY
"""
Name: 'Fatty'
Blender: 246
Group: 'Mesh generation'
Tooltip: 'Builds a mesh around an Envelope Armature'
"""
# Add a licence here if you wish to re-distribute, we recommend the GPL
import Blender
from Blender import Armature
from Blender import Object
from Blender.Mathutils import *
#
def main():
 skeletonObject = Object.Get('Skeleton')
 
 defaultPose=skeletonObject.getPose()
 
 boneDict = defaultPose.PoseBonesDict
 
 for bones in boneDict.items()
  mb = Blender.Metaball.New()
 
  for bone in arm.bones.values():
    element= mb.elements.add()
     #element.co = Blender.Mathutils.Vector(i, 0, 0) 
   #element.matrix = bone.matrix['ARMATURESPACE']
   print bone.getMatrix(space='worldspace')
 
  sce = Blender.Scene.GetCurrent()
  sce.objects.new(mb)
 
# This lets you can import the script without running it
if __name__ == '__main__':
 main()
 

I’m pretty sure skeletonObject = Object.Get(‘Skeleton’) is right… everything below that is guesswork. If you could hold my hand as far as the for loop, I think I can take it from there… probably…

You can explore the python functions manually using blender’s internal python console. (hit scripts window. scripts>system>interactive console).

I you click on “resources” in my sig, you can scroll down to the bottom for a link to the Blender API (if you don’t have it already).

Update: So dang close now. :smiley:


#!BPY
"""
Name: 'Fatty'
Blender: 246
Group: 'Mesh generation'
Tooltip: 'Builds a mesh around an Envelope Armature'
"""
# Add a licence here if you wish to re-distribute, we recommend the GPL
import Blender
from Blender import Armature
from Blender import Object
from Blender.Mathutils import *
#
def main():
 
 skeletonObject = Object.Get('Skeleton')
 mb = Blender.Metaball.New()
 
 xbones=skeletonObject.data.bones.values()
 
 print xbones
 
 for bone in xbones:
  print bone.matrix['ARMATURESPACE']
  element= mb.elements.add()
    element.co = bone.matrix['ARMATURESPACE']
  #element.matrix = bone.matrix['ARMATURESPACE']
 
 sce = Blender.Scene.GetCurrent()
 sce.objects.new(mb)
 
# This lets you can import the script without running it
if __name__ == '__main__':
 main()

It was actually working when I stopped at the print statement. I guess it’s expecting a vector with 3 numbers instead of a vector with 4 numbers? I’m either pulling the wrong vector or I need to convert it from like a euler rotation to a normal rotation or something. I don’t know, I’ll keep reading.

Bump!
Okay, I think the problem is, I’m grabbing a Matrix, which includes rotation and other data, but then I’m trying to plug it into MetaBall.co which only accepts coordinates. I can’t think of a good way to extract a co from a Matrix. Any advice?

FINALLY! :smiley:


#!BPY
"""
Name: 'Fatty'
Blender: 246
Group: 'Mesh generation'
Tooltip: 'Builds a mesh around an Envelope Armature'
"""
# Add a licence here if you wish to re-distribute, we recommend the GPL
import Blender
from Blender import Armature
from Blender import Object
from Blender.Mathutils import *
#
def main():
 
 skeletonObject = Object.Get('Skeleton')
 mb = Blender.Metaball.New()
 
 xbones=skeletonObject.data.bones.values()
 
 print xbones
 
 for bone in xbones:
  print bone.matrix['ARMATURESPACE']
  element= mb.elements.add()
    element.co = bone.matrix['ARMATURESPACE'].translationPart() 
  #element.matrix = bone.matrix['ARMATURESPACE']
  
 sce = Blender.Scene.GetCurrent()
 sce.objects.new(mb)
 
# This lets you can import the script without running it
if __name__ == '__main__':
 main()


Don’t ask me why Matrix isn’t covered on the main page of the Blender API documentation. It’s only the most ubiquitous thing in Blender, other than perhaps Object. Oh well. I’ve got my metaballs properly distributed now. :smiley: My character looks more like a lump of stone than an animal, but once I start scaling these things, it should look a lot better… stay tuned. :smiley:

Okay, now I’m really confused.

Everything seems fine now, except no matter what I do, the metaball elements always appear scaled to 2.0.


#!BPY
"""
Name: 'Fatty'
Blender: 246
Group: 'Mesh generation'
Tooltip: 'Builds a mesh around an Envelope Armature'
"""
# Add a licence here if you wish to re-distribute, we recommend the GPL
import Blender
from Blender import Armature
from Blender import Object
from Blender.Mathutils import *
#
def main(): 
  skeletonObject = Object.Get('Skeleton')
  mb = Blender.Metaball.New()
  xbones=skeletonObject.data.bones.values()
  #print xbones
 
  for bone in xbones:
  #print bone.matrix['ARMATURESPACE']
   element= mb.elements.add()
    element.co = bone.matrix['ARMATURESPACE'].translationPart()
    elementScale = bone.tailRadius / 10
    print elementScale
    elementDims= Vector(elementScale,elementScale,elementScale)
    print elementDims
    element.dims = elementDims
  
  sce = Blender.Scene.GetCurrent()
  sce.objects.new(mb)
 
# This lets you can import the script without running it
if __name__ == '__main__':
 main()

The print statements confirm that elementScale is well within the 0-20 range, so I can’t figure out why the elements are always clamped to a radius of 2.0…

Okay, now it’s sorta working.


#!BPY
"""
Name: 'Fatty'
Blender: 246
Group: 'Mesh generation'
Tooltip: 'Builds a mesh around an Envelope Armature'
"""
# Add a licence here if you wish to re-distribute, we recommend the GPL
import Blender
from Blender import Window
from Blender import Metaball
from Blender import Armature
from Blender import Object
from Blender.Mathutils import *
#
def main(): 
  skeletonObject = Object.Get('Skeleton')
  xbones=skeletonObject.data.bones.values()
  #print xbones
  mb = Blender.Metaball.New()
 mb.update = Metaball.Update.ALWAYS
  sce = Blender.Scene.GetCurrent()
  sce.objects.new(mb)
 
  for bone in xbones:
  #print bone.matrix['ARMATURESPACE']
   element= mb.elements.add()
  element.type= Metaball.Types.CUBE
    element.co = bone.matrix['ARMATURESPACE'].translationPart()
    elementScale = bone.tailRadius
    print elementScale
    elementDims= Vector(elementScale,elementScale,elementScale)
    print elementDims
    element.dims = elementDims
  element.radius = elementScale
  
 Window.RedrawAll()
 
# This lets you can import the script without running it
if __name__ == '__main__':
 main()

Problem is, I can’t see the results unless I select the new meta object, go into edit mode, then go back into Object mode. It makes debugging a pain. Is there any way I can just force it to show me the proper results? Window.redrawAll() doesn’t seem to help.

Also, how do I use Python to convert one object type into another? Specifically, I want to convert my MetaBall into a Mesh so I can rig it automatically, too.

]ok, i just found out something rather interesting.


import Blender
from Blender import *
Vector= Blender.Mathutils.Vector

def makeBones(arm):
    bone= Blender.Armature.Editbone()
    arm.bones[bone.name]= bone #add it to the armature
    bone.head= Vector(0,0,0)
    bone.tail=Vector(1,1,1)

    ebone= Blender.Armature.Editbone()
    arm.bones[ebone.name]= ebone #add it to the armature
    ebone.head= Vector(2,1,1)
    ebone.tail=Vector(2,2,1)

    return arm

def makePoses(ob):
  pose=ob.getPose()
  for pbone in pose.bones.values():
    print pbone
  return ob

def make_arm():
  arm= Blender.Armature.New("Human") #make an armature
  arm.makeEditable()
  arm= makeBones(arm)

  scn = Blender.Scene.GetCurrent() #add it to the current scene.
  arm_ob= scn.objects.new(arm) #instance it in the scene. this is the new way for 2.46 to instance objects
  arm.update() #exit editmode. Arm must be instanced as an object before you can save changes or pose it 
  arm_ob= makePoses(arm_ob) #constrain arm_ob with these markers  
  scn.update(1) #make everyone behave themselves in the scene, and respect the new constraints
  return

def nogo_make_arm():
  arm= Blender.Armature.New("Human") #make an armature
  arm.makeEditable()
  arm= makeBones(arm)

  scn = Blender.Scene.GetCurrent() #add it to the current scene.
  arm_ob= scn.objects.new(arm) #instance it in the scene. this is the new way for 2.46 to instance objects
  arm_ob= makePoses(arm_ob) #constrain arm_ob with these markers  
  arm.update() #exit editmode. Arm must be instanced as an object before you can save changes or pose it 
  scn.update(1) #make everyone behave themselves in the scene, and respect the new constraints
  return

print
print "this works"
make_arm()
print
print "but this does not"
nogo_make_arm()

where the differnece is in the arm.update() has to be done before posing. I also think I told you wrong - you have to
1.create the armature,
2. add edit bones,
3. instance the amrature to an object,
4. THEN you can arm.update() to save it,
5. THEN you can pose the object

Thanks, but I’m not even worried about posing the mesh yet. The first step is to generate a mesh that surrounds the bones. Fatty was intended as a project like Skinny that procedurally generates a character from a skeleton.

Metaballs don’t seem to work well, but I’m learning python. I think next I’ll try building a mesh using verts and faces…