i'm learning about scripting with bones/armatures in blender 2.79 and wanted to share some exercises i created to improve learning more about scripting with armatures hope you find this helpful.
1.given armature draw bone at given position
2.given armature draw bone at given empty position
3.given armature draw bone with roll such that up is z
4. 1,2,3 creating new armature for each
5. draw a straight bone chain with any given number of bones — use fixed positions/ use fixed axis / create armature
6. given 2 bones parent one to the other with offset
7. 6, but use connected
8. given some bones add them to a given layer
9. given expected bones for biped arm automatically name them upArm.L, lowArm.L, hand.L
10. 9, but expect bones to be spine and name them spine1, spine2 … etc
notes creating an armature:
- need to create armature
- need to create object
- need to set objects data to armature
- need to link object to scene ( i think this may be different in higher blender versions)
- need to create edit_bone in armature in edit mode
- need to set tail of bone to a position to see bone in viewport
and here are some notes i created for solving the above exercises, as i'm still learning in this area there may be better ways to approach solving the exercises but here are some tips.
"""blender 2.79
import bpy
import sys
#example if wanted to test script without addon part. change to your path here
sys.path.append('/Users/Nathaniel/Documents/src_blender/python/introPipeForArtistsRigging')
import boneDoodles as mod
import imp
imp.reload(mod)
#for all examples below it assumes names exist in scene
#mod.drawBoneAtPosition( 'Armature',(0,0,0) )
#mod.drawBoneAtEmpty('Armature','Empty')
#mod.drawBoneAtPositionUpZ( 'Armature',(0,0,0) )
#mod.drawBoneAtPositionCreateArmature(pos = (0,0,0))
#mod.drawBoneAtEmptyCreateArmature(emptyName = 'Empty')
#mod.drawBoneChain( numBones = 2 )
#mod.parentBones( armatureName = 'Armature',
parentName = 'Bone',
childName = 'Bone.001',
useConnected = False )
#mod.parentBones( armatureName = 'Armature',
parentName = 'Bone',
childName = 'Bone.001',
useConnected = True )
#mod.putBoneInLayer( armatureName = 'Armature',
boneNames = ['Bone'],
layerIndex = 19 )
#mod.nameBones( armatureObj = bpy.data.objects['Armature'],
bones=['Bone','Bone.001','Bone.002'],
mode = 'ARM',
side = 'L')
mod.nameBones( armatureObj = bpy.data.objects['Armature'],
bones=['Bone','Bone.001','Bone.002'],
mode = 'SPINE',
side = 'L')
"""
import bpy
import math
def drawBoneAtPosition(armatureName = '', pos = (0,0,0)):
"""1.given armature draw bone at given position
#assume bone tail placed above head in z with 1 unit distance
"""
curMode = bpy.context.object.mode
#need to create bone
#need to be in edit mode
bpy.ops.object.mode_set(mode='EDIT')
bone = bpy.data.armatures[armatureName].edit_bones.new('Bone') #name gets automatically incremented
bone.head = pos
bone.tail = (pos[0],pos[1],pos[2]+1) #z is offset by a bit
#restore mode
bpy.ops.object.mode_set(mode=curMode)
def drawBoneAtEmpty(armatureName = '', emptyName = ''):
"""#2.given armature draw bone at given empty position
"""
#need armature to be only active object to go into edit mode to create bone
#because empty doesnt have edit mode
bpy.context.scene.objects.active = bpy.data.objects[armatureName]
pos = bpy.data.objects[emptyName].location
drawBoneAtPosition(armatureName = armatureName, pos = pos)
def drawBoneAtPositionUpZ(armatureName = '', pos = (0,0,0)):
"""3.given armature draw bone with roll such that up is z
#assume bone tail placed besides head in x
"""
curMode = bpy.context.object.mode
#need to create bone
#need to be in edit mode
bpy.ops.object.mode_set(mode='EDIT')
bone = bpy.data.armatures[armatureName].edit_bones.new('Bone') #name gets automatically incremented
bone.head = pos
bone.tail = (pos[0]+1,pos[1],pos[2]) #x is offset by a bit
#change roll so up is z
bone.roll = -math.pi/2
bone.roll = 0
#restore mode
bpy.ops.object.mode_set(mode=curMode)
#4. 1,2,3 creating new armature for each
def drawBoneAtPositionCreateArmature(pos = (0,0,0)):
"""4.1.create armature draw bone at given position
#assume bone tail placed above head in z with 1 unit distance
"""
curMode = None
if bpy.context.object:
curMode = bpy.context.object.mode
bpy.ops.object.mode_set(mode='OBJECT') #if something is selected go to object mode
#create armature, armature object, link to scene
amt = None
amt = bpy.data.armatures.new('Armature') #name end up with may be different if its already used
amtObj = bpy.data.objects.new('Armature',amt)
bpy.context.scene.objects.link(amtObj)
#need to create bone
#need to be in edit mode
bpy.context.scene.objects.active = amtObj #select armature
bpy.ops.object.mode_set(mode='EDIT')
bone = amt.edit_bones.new('Bone') #name gets automatically incremented
bone.head = pos
bone.tail = (pos[0],pos[1],pos[2]+1) #z is offset by a bit
#restore mode
if curMode:
bpy.ops.object.mode_set(mode=curMode)
def drawBoneAtEmptyCreateArmature( emptyName = ''):
"""#4.2.create armature draw bone at given empty position
"""
pos = bpy.data.objects[emptyName].location
drawBoneAtPositionCreateArmature( pos = pos)
def drawBoneChain( numBones = 2 ):
"""#5. draw a straight bone chain with any given number of bones, use fixed positions, use fixed axis create armature
no parenting
"""
curMode = None
if bpy.context.object:
curMode = bpy.context.object.mode
bpy.ops.object.mode_set(mode='OBJECT') #if something is selected go to object mode
#create armature, armature object, link to scene
amt = None
amt = bpy.data.armatures.new('Armature') #name end up with may be different if its already used
amtObj = bpy.data.objects.new('Armature',amt)
bpy.context.scene.objects.link(amtObj)
#loop creating bones
#need to be in edit mode
bpy.context.scene.objects.active = amtObj #select armature
bpy.ops.object.mode_set(mode='EDIT')
pos = (0,0,0)
for i in range(0,numBones):
pos = (0,0,i)
bone = amt.edit_bones.new('Bone') #name gets automatically incremented
bone.head = pos
bone.tail = (pos[0],pos[1],pos[2]+1) #z is offset by a bit
#restore mode
if curMode:
bpy.ops.object.mode_set(mode=curMode)
def parentBones( armatureName = None,
parentName = None,
childName = None,
useConnected = False ):
"""
#6. given 2 bones parent one to the other with offset.
assumes all inputs exist
#7. 6, but use connected
"""
#i think getting armature from object.data is more efficient
amtObj = bpy.data.objects[armatureName]
bpy.context.scene.objects.active = amtObj
bpy.ops.object.mode_set(mode='EDIT')
amt = amtObj.data
amt.edit_bones[childName].parent = amt.edit_bones[parentName]
if useConnected:
amt.edit_bones[childName].use_connect = True
def putBoneInLayer( armatureName = None,
boneNames = [],
layerIndex = 31 ):
"""
#8. given some bones add them to a given layer
add them to given layer
assumes armature and bones exist.
need to be in armature icon to see different layers
"""
amtObj = bpy.data.objects[armatureName]
amt = amtObj.data
for bone in boneNames:
amt.bones[bone].layers[layerIndex] = True
#set all other layers to false
numLayers = len(amt.bones[bone].layers)
for j in range(0,numLayers):
if j != layerIndex:
amt.bones[bone].layers[j] = False
def nameBones( armatureObj = None,
bones=[],
mode = 'ARM',
side = 'L'):
"""
#9. given expected bones for biped arm automatically name them upArm.L, lowArm.L, hand.L
#10. 9, but expect bones to be spine and name them spine1, spine2 etc
mode 'ARM' or 'SPINE'
bones bone names
assumes all input exist
"""
amt = armatureObj.data
for i in range(0,len(bones)):
if bones[i] not in amt.bones:
print("couldnt find bone %s" %(bones[i]))
return
if mode == 'ARM':
if len(bones) != 3:
return
amt.bones[ bones[0] ].name = 'upArm.%s' %(side)
amt.bones[ bones[1] ].name = 'lowArm.%s' %(side)
amt.bones[ bones[2] ].name = 'hand.%s' %(side)
elif mode == 'SPINE':
for i in range(0,len(bones)):
amt.bones[ bones[i] ].name = 'spine%s' %(i+1)
Happy Scripting and Sketching!
Nate
inspired by:
Sybren Stüvel's "Scripting for Artists" talk