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.
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
I need more information. Everything I do now just says “invalid syntax.”
#!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…
#!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?
#!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. 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.
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…
#!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…