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