Skip to content

Instantly share code, notes, and snippets.

@nathanielanozie
Created April 14, 2020 02:51
Show Gist options
  • Select an option

  • Save nathanielanozie/6ef322d7bec281934732e317180833c8 to your computer and use it in GitHub Desktop.

Select an option

Save nathanielanozie/6ef322d7bec281934732e317180833c8 to your computer and use it in GitHub Desktop.
for learning about scripting drawing bones in blender
"""blender 2.79 a more advanced example scripting bones in python for blender
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 boneDoodlesAdvA as mod
import imp
imp.reload(mod)
#for all examples below it assumes names exist in scene
#mod.drawBipedBodyDeformationBones()
#mod.drawQuadpedBodyDeformationBones()
#mod.drawBipedBodyDeformationBones(nameBones=True)
#mod.drawQuadpedBodyDeformationBones(nameBones=True)
#mod.drawFaceDeformationBones()
"""
import bpy
import math
def drawQuadpedBodyDeformationBones(nameBones = False):
"""#3. draw straight chains for quadruped spine 3, legs quad 2, foot quad 2, arms 3, no parenting, fixed positions, no naming
"""
#arm -- assume bone tail placed below head in z with different distances
#leg -- assume bone tail placed below head in z with different distances
#foot -- assume bone tail placed infront of head. ankle should be above toe
#spine -- assume bone tail placed in front head in y with different distances
##headnneck -- assume bone tail placed in front head in y with different distances
#pelvis/root -- assume bone tail placed behind head in y with distance 2
def drawArm(side='L'):
boneObjects = []
blen = [1,2,2,2] #clavicle,uparm,lowarm,hand bone length in z
startPos = (2,-4,0) #clavicle head position x,y,z world position. where arm begins
if side == 'R':
startPos = ( -startPos[0], startPos[1], startPos[2] )
#clavicle
pos = startPos
bone = amt.edit_bones.new('Bone') #name gets automatically incremented
bone.head = pos
bone.tail = (pos[0],pos[1],pos[2]-blen[0]) #z is offset by a bit
boneObjects.append(bone)
#uparm
pos = boneObjects[0].tail #use clavicle bones tail as starting point
bone = amt.edit_bones.new('Bone')
bone.head = pos
bone.tail = (pos[0],pos[1],pos[2]-blen[1])
boneObjects.append(bone)
#lowarm
pos = boneObjects[1].tail
bone = amt.edit_bones.new('Bone')
bone.head = pos
bone.tail = (pos[0],pos[1],pos[2]-blen[2])
boneObjects.append(bone)
#hand
pos = boneObjects[2].tail
bone = amt.edit_bones.new('Bone')
bone.head = pos
bone.tail = (pos[0],pos[1],pos[2]-blen[3])
boneObjects.append(bone)
return boneObjects #could be used for naming etc
def drawLeg(side='L'):
boneObjects = []
blen = [3,3] #upleg,lowleg length in z
startPos = (2,3,0) #upleg head position. where leg begins
if side == 'R':
startPos = ( -startPos[0], startPos[1], startPos[2] )
#upleg
pos = startPos
bone = amt.edit_bones.new('Bone') #name gets automatically incremented
bone.head = pos
bone.tail = (pos[0],pos[1],pos[2]-blen[0]) #z is offset by a bit
boneObjects.append(bone)
#lowleg
pos = boneObjects[0].tail #use upleg bones tail as starting point
bone = amt.edit_bones.new('Bone')
bone.head = pos
bone.tail = (pos[0],pos[1],pos[2]-blen[1])
boneObjects.append(bone)
return boneObjects #could be used for naming etc
def drawFoot(side='L', startPos = (3,0,-8.5) ):
"""#foot head position. could get z as input based on leg
"""
boneObjects = []
blen = [1,0.5] #foot,toe length in y
footHeight = 0.5 #distance foot and toe in z
if side == 'R':
startPos = ( -startPos[0], startPos[1], startPos[2] )
#foot
pos = startPos
bone = amt.edit_bones.new('Bone') #name gets automatically incremented
bone.head = pos
bone.tail = (pos[0],pos[1]-blen[0],pos[2]) #x is offset by a bit
boneObjects.append(bone)
#toe
pos = boneObjects[0].tail #use foot bones tail as starting point
bone = amt.edit_bones.new('Bone')
bone.head = (pos[0],pos[1],pos[2]-footHeight)
bone.tail = (pos[0],pos[1]-blen[1],pos[2]-footHeight)
boneObjects.append(bone)
return boneObjects #could be used for naming etc
def drawSpine(endPos = None):
boneObjects = []
numSpine = 3
blen = [1,1,1] #spine bone length in y
startPos = (0,0,0) #spine1 head position x,y,z world position. where spine begins
#spine1
pos = startPos
bone = amt.edit_bones.new('Bone') #name gets automatically incremented
bone.head = pos
bone.tail = (pos[0],pos[1]-blen[0],pos[2]) #z is offset by a bit
boneObjects.append(bone)
#rest of spines
for i in range(0,numSpine-1):
pos = boneObjects[i].tail #for i = 0 use spine1 bones tail as starting point for spine2
bone = amt.edit_bones.new('Bone')
bone.head = pos
bone.tail = (pos[0],pos[1]-blen[i+1],pos[2])
boneObjects.append(bone)
#make spine end at end position
if endPos:
boneObjects[-1].tail = endPos
return boneObjects #could be used for naming etc
def drawHeadNeck():
boneObjects = []
blen = [2,3] #neck,head length in y
startPos = (0,-4,0) #neck head position. where neck begins
#neck
pos = startPos
bone = amt.edit_bones.new('Bone') #name gets automatically incremented
bone.head = pos
bone.tail = (pos[0],pos[1]-blen[0],pos[2]) #y is offset by a bit
boneObjects.append(bone)
#head
pos = boneObjects[0].tail #use neck bones tail as starting point
bone = amt.edit_bones.new('Bone')
bone.head = pos
bone.tail = (pos[0],pos[1]-blen[1],pos[2])
boneObjects.append(bone)
return boneObjects #could be used for naming etc
def drawPelvis(startPos = (0,0,-3)):
boneObjects = []
blen = [2] #pelvis length in y
#startPos = (0,0,-3) #pelvis head position. where pelvis begins
#pelvis
pos = startPos
bone = amt.edit_bones.new('Bone') #name gets automatically incremented
bone.head = pos
bone.tail = (pos[0],pos[1]+blen[0],pos[2]) #y is offset by a bit
boneObjects.append(bone)
return boneObjects
def parentBipedBones(*args, **kwargs):
"""2. 1. with parenting
#start legs and spine parented to pelvis
#neck and arms parented to end of spine
#feet,arms,headneck parent their chains
"""
armBonesLeft = kwargs.get('armBonesLeft') or None
armBonesRight = kwargs.get('armBonesRight') or None
legBonesLeft = kwargs.get('legBonesLeft') or None
legBonesRight = kwargs.get('legBonesRight') or None
footBonesLeft = kwargs.get('footBonesLeft') or None
footBonesRight = kwargs.get('footBonesRight') or None
spineBones = kwargs.get('spineBones') or None
headneckBones = kwargs.get('headneckBones') or None
pelvisBone = kwargs.get('pelvisBone') or None
#within chain parenting
for chain in [ armBonesLeft,
armBonesRight,
legBonesLeft,
legBonesRight,
spineBones,
headneckBones]:
parentChain(chain)
for chain in [ footBonesLeft,
footBonesRight,]:
parentChain(chain,useConnect = False)
#handle additional parenting
#start legs parented to pelvis
parentPair( child=legBonesLeft, parent=pelvisBone, childIndex = 0, parentIndex = 0 )
parentPair( legBonesRight, pelvisBone, 0, 0 )
#neck and arms parented to end of spine
parentPair(headneckBones, spineBones, 0, 0 )
parentPair(armBonesLeft, spineBones, 0, 0 )
parentPair(armBonesRight, spineBones, 0, 0 )
#feet parented to end of legs
parentPair(footBonesLeft,legBonesLeft,0,1)
parentPair(footBonesRight,legBonesRight,0,1)
#spine parented to pelvis
parentPair( spineBones, pelvisBone, 0, 0)
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 be in edit mode
bpy.context.scene.objects.active = amtObj #select armature
bpy.ops.object.mode_set(mode='EDIT')
##creating bones
#arm
armLeft = drawArm(side='L')
armRight = drawArm(side='R')
#leg
legLeft = drawLeg(side='L')
legRight = drawLeg(side='R')
#foot
footLeft = drawFoot(side='L',startPos = legLeft[-1].tail )
footRight = drawFoot(side='R', startPos = legLeft[-1].tail )
#headnneck
headneck = drawHeadNeck()
#spine
spine = drawSpine()
#pelvis/root
pelvis = drawPelvis( startPos = spine[0].head )
#end of creating bones
##parenting bones
parentBipedBones(armBonesLeft=armLeft,
armBonesRight=armRight,
legBonesLeft = legLeft,
legBonesRight = legRight,
footBonesLeft = footLeft,
footBonesRight = footRight,
spineBones = spine,
headneckBones = headneck,
pelvisBone = pelvis
)
#end of parenting bones
#do naming of bones
if nameBones:
nameArmatureBones(armBonesLeft=armLeft,
armBonesRight=armRight,
legBonesLeft = legLeft,
legBonesRight = legRight,
footBonesLeft = footLeft,
footBonesRight = footRight,
spineBones = spine,
headneckBones = headneck,
pelvisBone = pelvis
)
#restore mode
if curMode:
bpy.ops.object.mode_set(mode=curMode)
def drawFaceDeformationBones():
"""#7. draw jaw,headnNeck,upSkull chains, fixed positions
"""
#jaw -- assume bone tail infront of head in y
#headneck -- assume bone tail above head in z
#skull -- assume bone tail inforn of head in y
def drawJaw():
boneObjects = []
blen = [2] #jaw length in y
startPos = (0,-1,0) #jaw head position. where jaw begins
pos = startPos
bone = amt.edit_bones.new('Bone') #name gets automatically incremented
bone.head = pos
bone.tail = (pos[0],pos[1]-blen[0],pos[2]) #y is offset by a bit
boneObjects.append(bone)
return boneObjects
def drawHeadNeck():
boneObjects = []
blen = [2,3] #neck,head length in z
startPos = (0,0,-2) #neck head position. where neck begins
#neck
pos = startPos
bone = amt.edit_bones.new('Bone') #name gets automatically incremented
bone.head = pos
bone.tail = (pos[0],pos[1],pos[2]+blen[0]) #z is offset by a bit
boneObjects.append(bone)
#head
pos = boneObjects[0].tail #use neck bones tail as starting point
bone = amt.edit_bones.new('Bone')
bone.head = pos
bone.tail = (pos[0],pos[1],pos[2]+blen[1])
boneObjects.append(bone)
return boneObjects #could be used for naming etc
def drawSkull():
boneObjects = []
blen = [2] #skull length in y
startPos = (0,-1,3) #skull head position. where jaw begins
pos = startPos
bone = amt.edit_bones.new('Bone') #name gets automatically incremented
bone.head = pos
bone.tail = (pos[0],pos[1]-blen[0],pos[2]) #y is offset by a bit
boneObjects.append(bone)
return boneObjects
def parentBones(*args, **kwargs):
"""2. 1. with parenting
#start legs and spine parented to pelvis
#neck and arms parented to end of spine
#feet,arms,headneck parent their chains
"""
jawBone = kwargs.get('jawBone') or None
headneckBones = kwargs.get('headneckBones') or None
skullBone = kwargs.get('skullBone') or None
#within chain parenting
for chain in [ headneckBones ]:
parentChain(chain)
#handle additional parenting
#jaw parented to head
#skull parented to head
parentPair( child=jawBone, parent=headneckBones, childIndex = 0, parentIndex = 1 )
parentPair( child=skullBone, parent=headneckBones, childIndex = 0, parentIndex = 1 )
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 be in edit mode
bpy.context.scene.objects.active = amtObj #select armature
bpy.ops.object.mode_set(mode='EDIT')
##creating bones
jawBone = drawJaw()
headneckBone = drawHeadNeck()
skullBone = drawSkull()
#parenting bones
parentBones( jawBone = jawBone,
headneckBones = headneckBone,
skullBone = skullBone )
#do naming of bones
if nameBones:
nameBones(jawBone,['jaw'])
nameBones(headneckBone,['head','neck'])
nameBones(skullBone,['skull'])
#restore mode
if curMode:
bpy.ops.object.mode_set(mode=curMode)
def drawBipedBodyDeformationBones(nameBones = False):
"""#1. draw straight chains for arm 4 bones,leg 2,foot 2, pelvis 1, spine 4,headnNeck 2, root single bone, no parenting, fixed positions
"""
#arm -- assume bone tail placed above right head in x with different distances
#leg -- assume bone tail placed below head in z with different distances
#foot -- assume bone tail placed infront of head. ankle should be above toe
#spine -- assume bone tail placed above head in z with different distances
##headnneck -- assume bone tail placed above head in z with different distances
#pelvis/root -- assume bone tail placed below head in z with distance 2
def drawArm(side='L'):
boneObjects = []
blen = [1,2,2,2] #clavicle,uparm,lowarm,hand bone length in x
startPos = (2,0,5) #clavicle head position x,y,z world position. where arm begins
if side == 'R':
blen = [-x for x in blen]
startPos = ( -startPos[0], startPos[1], startPos[2] )
#clavicle
pos = startPos
bone = amt.edit_bones.new('Bone') #name gets automatically incremented
bone.head = pos
bone.tail = (pos[0]+blen[0],pos[1],pos[2]) #x is offset by a bit
boneObjects.append(bone)
#uparm
pos = boneObjects[0].tail #use clavicle bones tail as starting point
bone = amt.edit_bones.new('Bone')
bone.head = pos
bone.tail = (pos[0]+blen[1],pos[1],pos[2])
boneObjects.append(bone)
#lowarm
pos = boneObjects[1].tail
bone = amt.edit_bones.new('Bone')
bone.head = pos
bone.tail = (pos[0]+blen[2],pos[1],pos[2])
boneObjects.append(bone)
#hand
pos = boneObjects[2].tail
bone = amt.edit_bones.new('Bone')
bone.head = pos
bone.tail = (pos[0]+blen[3],pos[1],pos[2])
boneObjects.append(bone)
return boneObjects #could be used for naming etc
def drawLeg(side='L'):
boneObjects = []
blen = [3,3] #upleg,lowleg length in z
startPos = (3,0,-2) #upleg head position. where leg begins
if side == 'R':
startPos = ( -startPos[0], startPos[1], startPos[2] )
#upleg
pos = startPos
bone = amt.edit_bones.new('Bone') #name gets automatically incremented
bone.head = pos
bone.tail = (pos[0],pos[1],pos[2]-blen[0]) #x is offset by a bit
boneObjects.append(bone)
#lowleg
pos = boneObjects[0].tail #use upleg bones tail as starting point
bone = amt.edit_bones.new('Bone')
bone.head = pos
bone.tail = (pos[0],pos[1],pos[2]-blen[1])
boneObjects.append(bone)
return boneObjects #could be used for naming etc
def drawFoot(side='L', startPos = (3,0,-8.5) ):
"""#foot head position. could get z as input based on leg
"""
boneObjects = []
blen = [1,0.5] #foot,toe length in y
footHeight = 0.5 #distance foot and toe in z
if side == 'R':
startPos = ( -startPos[0], startPos[1], startPos[2] )
#foot
pos = startPos
bone = amt.edit_bones.new('Bone') #name gets automatically incremented
bone.head = pos
bone.tail = (pos[0],pos[1]-blen[0],pos[2]) #x is offset by a bit
boneObjects.append(bone)
#toe
pos = boneObjects[0].tail #use foot bones tail as starting point
bone = amt.edit_bones.new('Bone')
bone.head = (pos[0],pos[1],pos[2]-footHeight)
bone.tail = (pos[0],pos[1]-blen[1],pos[2]-footHeight)
boneObjects.append(bone)
return boneObjects #could be used for naming etc
def drawSpine(endPos = None):
boneObjects = []
numSpine = 4
blen = [1,1,1,1] #spine bone length in z
startPos = (0,0,-1) #spine1 head position x,y,z world position. where spine begins
#spine1
pos = startPos
bone = amt.edit_bones.new('Bone') #name gets automatically incremented
bone.head = pos
bone.tail = (pos[0],pos[1],pos[2]+blen[0]) #z is offset by a bit
boneObjects.append(bone)
#rest of spines
for i in range(0,numSpine-1):
pos = boneObjects[i].tail #for i = 0 use spine1 bones tail as starting point for spine2
bone = amt.edit_bones.new('Bone')
bone.head = pos
bone.tail = (pos[0],pos[1],pos[2]+blen[i+1])
boneObjects.append(bone)
#make spine end at end position
if endPos:
boneObjects[-1].tail = endPos
return boneObjects #could be used for naming etc
def drawHeadNeck():
boneObjects = []
blen = [2,3] #neck,head length in z
startPos = (0,0,5) #neck head position. where neck begins
#neck
pos = startPos
bone = amt.edit_bones.new('Bone') #name gets automatically incremented
bone.head = pos
bone.tail = (pos[0],pos[1],pos[2]+blen[0]) #z is offset by a bit
boneObjects.append(bone)
#head
pos = boneObjects[0].tail #use neck bones tail as starting point
bone = amt.edit_bones.new('Bone')
bone.head = pos
bone.tail = (pos[0],pos[1],pos[2]+blen[1])
boneObjects.append(bone)
return boneObjects #could be used for naming etc
def drawPelvis(startPos = (0,0,-3)):
boneObjects = []
blen = [2] #pelvis length in z
#startPos = (0,0,-3) #pelvis head position. where pelvis begins
#pelvis
pos = startPos
bone = amt.edit_bones.new('Bone') #name gets automatically incremented
bone.head = pos
bone.tail = (pos[0],pos[1],pos[2]-blen[0]) #z is offset by a bit
boneObjects.append(bone)
return boneObjects
def parentBipedBones(*args, **kwargs):
"""2. 1. with parenting
#start legs and spine parented to pelvis
#neck and arms parented to end of spine
#feet,arms,headneck parent their chains
"""
armBonesLeft = kwargs.get('armBonesLeft') or None
armBonesRight = kwargs.get('armBonesRight') or None
legBonesLeft = kwargs.get('legBonesLeft') or None
legBonesRight = kwargs.get('legBonesRight') or None
footBonesLeft = kwargs.get('footBonesLeft') or None
footBonesRight = kwargs.get('footBonesRight') or None
spineBones = kwargs.get('spineBones') or None
headneckBones = kwargs.get('headneckBones') or None
pelvisBone = kwargs.get('pelvisBone') or None
#within chain parenting
for chain in [ armBonesLeft,
armBonesRight,
legBonesLeft,
legBonesRight,
spineBones,
headneckBones]:
parentChain(chain)
for chain in [ footBonesLeft,
footBonesRight,]:
parentChain(chain,useConnect=False)
#handle additional parenting
#start legs parented to pelvis
parentPair( child=legBonesLeft, parent=pelvisBone, childIndex = 0, parentIndex = 0 )
parentPair( legBonesRight, pelvisBone, 0, 0 )
#neck and arms parented to end of spine
parentPair(headneckBones, spineBones, 0, 0 )
parentPair(armBonesLeft, spineBones, 0, 0 )
parentPair(armBonesRight, spineBones, 0, 0 )
#feet parented to end of legs
parentPair(footBonesLeft,legBonesLeft,0,1)
parentPair(footBonesRight,legBonesRight,0,1)
#spine parented to pelvis
parentPair( spineBones, pelvisBone, 0, 0)
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 be in edit mode
bpy.context.scene.objects.active = amtObj #select armature
bpy.ops.object.mode_set(mode='EDIT')
##creating bones
#arm
armLeft = drawArm(side='L')
armRight = drawArm(side='R')
#leg
legLeft = drawLeg(side='L')
legRight = drawLeg(side='R')
#foot
footLeft = drawFoot(side='L',startPos = legLeft[-1].tail )
footRight = drawFoot(side='R', startPos = legLeft[-1].tail )
#headnneck
headneck = drawHeadNeck()
#spine
spine = drawSpine( endPos = headneck[0].head )
#pelvis/root
pelvis = drawPelvis( startPos = spine[0].head )
#end of creating bones
##parenting bones
parentBipedBones(armBonesLeft=armLeft,
armBonesRight=armRight,
legBonesLeft = legLeft,
legBonesRight = legRight,
footBonesLeft = footLeft,
footBonesRight = footRight,
spineBones = spine,
headneckBones = headneck,
pelvisBone = pelvis
)
#end of parenting bones
#do naming of bones
if nameBones:
nameArmatureBones(armBonesLeft=armLeft,
armBonesRight=armRight,
legBonesLeft = legLeft,
legBonesRight = legRight,
footBonesLeft = footLeft,
footBonesRight = footRight,
spineBones = spine,
headneckBones = headneck,
pelvisBone = pelvis
)
#restore mode
if curMode:
bpy.ops.object.mode_set(mode=curMode)
def parentChain( chain = [], useConnect = True ):
"""parent bones of chain assumes second parented to first etc
"""
if not chain:
return
if len(chain) < 2:
return
for i in range(1,len(chain)):
chain[i].parent = chain[i-1]
chain[i].use_connect = useConnect
def parentPair( child = None, parent = None, childIndex = 0, parentIndex = 0):
"""given child parent bones parent appropriate index
"""
if not child:
return
if not parent:
return
child[childIndex].parent = parent[parentIndex]
def nameArmatureBones(*args, **kwargs):
"""5. 2 with naming
"""
armBonesLeft = kwargs.get('armBonesLeft') or None
armBonesRight = kwargs.get('armBonesRight') or None
legBonesLeft = kwargs.get('legBonesLeft') or None
legBonesRight = kwargs.get('legBonesRight') or None
footBonesLeft = kwargs.get('footBonesLeft') or None
footBonesRight = kwargs.get('footBonesRight') or None
spineBones = kwargs.get('spineBones') or None
headneckBones = kwargs.get('headneckBones') or None
pelvisBone = kwargs.get('pelvisBone') or None
armNames = ['clavicle','upArm','lowArm','hand']
legNames = ['upLeg','lowLeg']
footNames = ['foot','toe']
spineNames = [ 'spine%s' %(i+1) for i in range(0,len(spineBones)) ]
headneckNames = ['neck','head']
pelvisName = ['pelvis']
nameBones(armBonesLeft,armNames,'L')
nameBones(armBonesRight,armNames,'R')
nameBones(legBonesLeft,legNames,'L')
nameBones(legBonesRight,legNames,'R')
nameBones(footBonesLeft,footNames,'L')
nameBones(footBonesRight,footNames,'R')
nameBones(spineBones,spineNames)
nameBones(headneckBones,headneckNames)
nameBones(pelvisBone,pelvisName)
def nameBones(bones=[],names=[], side = None):
if not bones:
return
namesToApply = names
if len(bones) != len(names):
return
if side == 'L':
namesToApply = ['%s.L' %x for x in names ]
elif side == 'R':
namesToApply = ['%s.R' %x for x in names ]
for i in range(0,len(bones)):
bones[i].name = namesToApply[i]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment