these are some miscellaneous doodles and snippets i used when building the facial rigs in the rigging page of this blog. They would need lots of editing to be usable in different situations and will need tweaking to get them working with higher blender versions. They have some examples with looping through armature bones, editing bendy bone attributes, putting bones in layers etc. please modify/use at your own risk.
#changing all bones rotation mode to euler xyz (assumes in pose mode)
####
import bpy
#set deform off on selected edit bones
armatureName = bpy.context.object.name #"Armature"
for pb in bpy.data.objects[armatureName].pose.bones:
pb.rotation_mode = 'XYZ' #bpy.context.object.pose.bones["ANIM_spine_end.C"].rotation_mode = 'XYZ'
####
#putting some bones in last layer to hide for animation
######
#ex put all non anim bones in last layer
import bpy
armatureName = bpy.context.object.name
bonesToHideNames = [b.name for b in bpy.data.objects[armatureName].data.bones if not b.name.startswith('ANIM_')]
#hide backend bones
def UT_PutInLayer(items=[], layer=32, armatureName=None ):
"""put given bones into specified int layer. i think max is 32
"""
layerValues = [False]*32
layerValues[layer-1] = True
armObj = bpy.data.objects[armatureName]
bpy.context.scene.objects.active = armObj
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
for it in items:
itObj = armObj.data.edit_bones[it] #assumes it exists
itObj.layers = layerValues
#put widgets in last layer so they are not seen
#rootWidgetShape.layers = [False]*19+[True]
UT_PutInLayer(items=bonesToHideNames, layer=32, armatureName=armatureName)
#########
#lock rotation on selected bones (assumes in pose mode)
####
import bpy
lockRotation = True
lockScale = True
#set deform off on selected edit bones
armatureName = bpy.context.object.name #"Armature"
for bone in bpy.data.objects[armatureName].data.bones:
#skip bones not selected
if not bone.select:
continue
pb = bpy.data.objects[armatureName].pose.bones[bone.name]
if lockRotation:
pb.lock_rotation = [True, True, True]
if lockScale:
pb.lock_scale = [True, True, True]
####
#parenting some geo to some bones. assumes armature selected in object mode
#change this to names that exist. first is deformation bone, second is list of geo names to parent to bone
import bpy
boneToGeoDict = {
"ANIM_base.C":["base_geo.C","tail_sphere_a_geo.C"],
"ANIM_tail_a.C":["tail_a_geo.C","tail_sphere_b_geo.C"],
"ANIM_tail_b.C":["tail_b_geo.C","tail_sphere_c_geo.C"],
"ANIM_tail_c.C":["tail_c_geo.C"]
}
armatureName = bpy.context.object.name #"Armature"
def parentGeoToBone(amt = None, geo=None, bone=None):
"""#3. parent cube geo to a single bone, cube and bone exist
inputs object, object, str
lots of mode switching cause i think doing without ops will be longer
"""
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
amt.select = True
bpy.context.scene.objects.active = amt
bpy.ops.object.mode_set(mode='EDIT')
amt.data.edit_bones.active = amt.data.edit_bones[bone]
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
geo.select = True
amt.select = True
bpy.context.scene.objects.active = amt
bpy.ops.object.parent_set(type='BONE',keep_transform=True)
for bone, geos in boneToGeoDict.items():
for geo in geos:
parentGeoToBone(amt=bpy.data.objects[armatureName], geo=bpy.data.objects[geo], bone=bone)
####
###set all mesh objects selectability. helpful so animator not accidentally selecting geo
import bpy
isSelectable = False #change to True for setting all meshes to selectable
meshNames = [obj.name for obj in bpy.data.objects if obj.type=="MESH"]
#print(meshNames)
for mesh in meshNames:
meshObj = bpy.data.objects[mesh]
meshObj.hide_select = not isSelectable
####
import bpy
#set deform off on selected edit bones
armatureName = "Armature"
for eb in bpy.data.armatures[armatureName].edit_bones:
if not eb.select:
continue
eb.use_deform = False
#add .L suffix to selected bones
import bpy
#set deform off on selected edit bones
armatureName = bpy.context.object.name
for eb in bpy.data.armatures[armatureName].edit_bones:
if not eb.select:
continue
curName = eb.name
newName = "{0}.L".format(curName) #add left suffix
eb.name = newName
#snippet to add one armature to different scene:
#(use ui to append armature data)
import bpy
bpy.data.objects.new("Armature", bpy.data.armatures['Armature'])
bpy.context.scene.objects.link(bpy.data.objects['Armature']) #will show bones in viewport
import bpy
#remove right side of armature
#select right side of armature in edit mode
armatureName = "Armature"
for eb in bpy.data.armatures[armatureName].edit_bones:
if eb.matrix.translation.x >= 0:
continue
eb.select = True
import bpy
#select bendy bone ends to remove
armatureName = "Armature"
for eb in bpy.data.armatures[armatureName].edit_bones:
if ("_head" in eb.name) or ("_tail" in eb.name):
eb.select = True
import bpy
#select lid span bones to remove
armatureName = "Armature"
for eb in bpy.data.armatures[armatureName].edit_bones:
if ("eyeLid_" in eb.name):
eb.select = True
##for fitting helper (need to do fitting in edit mode so pose bone gets updated automatically)
import bpy
#reset curve offsets on selected bone in edit mode
armatureName = bpy.context.object.name
ebones = [eb for eb in bpy.data.armatures[armatureName].edit_bones if eb.select]
for eb in ebones:
for attr in ["bbone_curveinx", "bbone_curveoutx","bbone_curveiny", "bbone_curveouty"]:
setattr(eb,attr,0)
#ctrl + alt + s > mouse move #to scale bendy bone in edit mode
#testing stuff with actions
#removing all actions in scene
>>> for action in bpy.data.actions:
... bpy.data.actions.remove(action)
#loosing actions set on constraints. so after appending action also want to set action names in constraints.
import bpy
#create a dictionary. boneName:(constraintName, actionName)
armatureName = "Armature"
result = dict()
for pb in bpy.data.objects[armatureName].pose.bones:
#action constraints
actionConstraints = [cnt for cnt in pb.constraints if cnt.type == "ACTION"]
if not actionConstraints:
continue
#loop constraints getting action name
for cnt in actionConstraints:
#there can be more than one constraint for a single bone so need to store a list
result.setdefault(pb.name, []).append((cnt.name, cnt.action.name))
print(result)
#setting the constraints in scene to use missing actions. assumes have appended actions already
import bpy
result = {} #set this dictionary
armatureName = "Armature"
for k,val in result.items():
for v in val:
cnt, actionName = v
pb = bpy.data.objects[armatureName].pose.bones[k]
pb.constraints[cnt].action = bpy.data.actions[actionName]
##appending an armature object brings in constraints and actions
#v1 (skips bone if cannot find bone in destination armature)
import bpy
#copy constraint from source armature to destination
sourceArmature = "Armature.001"
destinationArmature = "Armature"
for pb in bpy.data.objects[destinationArmature].pose.bones:
if pb.name not in bpy.data.objects[sourceArmature].pose.bones:
print("could not find {} skipping".format(pb.name))
continue
src_pb = bpy.data.objects[sourceArmature].pose.bones[pb.name]
srcActionConstraints = [cnt for cnt in src_pb.constraints if cnt.type == "ACTION"]
if not srcActionConstraints:
continue
for cnt in srcActionConstraints:
new_cnt = pb.constraints.new(cnt.type)
#attributes of constraint
for prop in dir(cnt):
#dont copy target
if prop == "target":
setattr(new_cnt, prop, bpy.data.objects[destinationArmature])
continue
try:
setattr(new_cnt, prop, getattr(cnt, prop))
except:
pass
#v0
##copying constraints/and/Actions from one armature to a different armature with identical bone names
import bpy
#copy constraint from source armature to destination
sourceArmature = "Armature"
destinationArmature = "Armature.001"
for pb in bpy.data.objects[destinationArmature].pose.bones:
src_pb = bpy.data.objects[sourceArmature].pose.bones[pb.name]
srcActionConstraints = [cnt for cnt in src_pb.constraints if cnt.type == "ACTION"]
if not srcActionConstraints:
continue
for cnt in srcActionConstraints:
new_cnt = pb.constraints.new(cnt.type)
#attributes of constraint
for prop in dir(cnt):
#dont copy target
if prop == "target":
setattr(new_cnt, prop, bpy.data.objects[destinationArmature])
continue
try:
setattr(new_cnt, prop, getattr(cnt, prop))
except:
pass
#inspired by
#blender dot stack exchange dot com ‚How to copy constraints from one bone to another post
#########
#ungroup all channels for selected action
import bpy
obj = bpy.context.active_object
action = obj.animation_data.action
#get fcurve objects to ungroup
fcurvesGroupObjsToUngroup = []
for fcurve in action.fcurves:
if not fcurve.group:
continue
fcurvesGroupObjsToUngroup.append(fcurve.group)
#ungroup the given fcurves
for fcurveGroupObj in fcurvesGroupObjsToUngroup:
if not fcurveGroupObj.name in action.groups.keys():
continue
action.groups.remove(fcurveGroupObj)
#to refresh view
curFrame = bpy.context.scene.frame_current
bpy.context.scene.frame_set(curFrame)
#####
#mirror pose of selected bones. (example for jaw action. first copy pose, then paste pose flipped)
import bpy
bpy.ops.pose.copy()
bpy.ops.pose.paste(flipped=True)
#select all right side bones of current action
import re
import bpy
obj = bpy.context.active_object
action = obj.animation_data.action
dataPaths = [fcurve.data_path for fcurve in action.fcurves]
#print(dataPaths)
bonesInAction = [re.match('.*\["(.*)"\].*', dataPath).groups()[0] for dataPath in dataPaths] #'pose.bones["Bone"].location'
bonesInAction = list(set(bonesInAction)) #remove duplicates
bonesInActionLeftSide = [b for b in bonesInAction if b.endswith('.L')]
#print("bonesInAction", bonesInAction)
bonesRightSide = [bone.replace('.L', '.R') for bone in bonesInActionLeftSide]
print('right side', bonesRightSide)
#deselect all pose bones
bpy.ops.pose.select_all(action='DESELECT')
for b in bonesRightSide:
obj.pose.bones[b].bone.select = True
########
#go to frame 0 of current action
bpy.context.scene.frame_set(0)
#go to default ArmatureAction
######
#save out vertex selection
import bpy
verticesSelected = []
meshName = 'gorilla'
verticesSelected = [v.index for v in bpy.data.objects[meshName].data.vertices if v.select]
#reselect vertex selection (need to be in object mode)
#verticesSelected = [] #paste in vertex ids from above step
verticesSelected = [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 578, 579, 581, 616, 660, 661, 703, 704, 705, 773, 774, 775, 776, 781, 782, 828, 830, 937, 1279, 1280, 1281, 1282, 1283, 1305, 1306, 1307, 1308, 1309, 1323, 1324, 1325, 1326, 1327, 1342, 1344, 1347, 1348, 1349, 1350, 1351, 1352, 1353, 1354, 1365, 1366, 1367, 1368, 1369, 1426, 1437, 1554, 1555, 1556, 1557, 1558, 1559, 1560, 1561, 1562, 1563, 1564, 1565, 1566, 1567, 1568, 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1578, 1579, 1580, 1581, 1582, 1583, 1584, 1585, 1586, 1587, 1588, 1589, 1590, 1591, 1592, 1593, 1594, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1602, 1603, 1604, 1605, 1606, 1607, 1608, 1609, 1610, 1611, 1612, 1613, 1614, 1615, 1616, 1617, 1618, 1619, 1620, 1621, 1622, 1623, 1624, 1625, 1626, 1627, 1628, 1629, 1630, 1631, 1632, 1633, 1634, 1635, 1636, 1637, 1638, 1639, 1640, 1641]
meshName = 'gorilla'
for v in bpy.data.objects[meshName].data.vertices:
if v.index in verticesSelected:
v.select = True
else:
v.select = False
#######
### select all tail and head of given bones
import bpy
mainBones = ["eyeLid_up", "eyeLid_dn"] #put bones here
#eyeLid_up_a.L eyeLid_up_b.L
armatureName = "Armature"
#start by deselecting all bones
for eb in bpy.data.armatures[armatureName].edit_bones:
eb.select = False
for eb in bpy.data.armatures[armatureName].edit_bones:
for bone in mainBones:
#should include tail
if eb.name.startswith(bone):
eb.select = True
break
###
###copy edit bone positions from source armature to destination armature
#copy:
#head
#tail
#roll
#curve xy offsets
###
import bpy
sourceArmature = "Armature_b"
destinationArmature = "Armature"
def _enterEdit(armatureName):
bpy.data.objects[armatureName].hide = False
bpy.ops.object.mode_set(mode='OBJECT')
#enter edit mode of given armature
bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects[armatureName].select = True
bpy.context.scene.objects.active = bpy.data.objects[armatureName]
bpy.ops.object.mode_set(mode='EDIT')
destinationBoneNames = [b.name for b in bpy.data.armatures[destinationArmature].bones]
for b in destinationBoneNames:
if b not in bpy.data.armatures[sourceArmature].bones:
print("could not find {} skipping".format(b))
continue
#enter edit mode source to get position
_enterEdit(sourceArmature)
src_eb = bpy.data.armatures[sourceArmature].edit_bones[b]
src_head = src_eb.head.copy()
src_tail = src_eb.tail.copy()
src_roll = src_eb.roll
src_bboneAttrsDict = dict()
bboneAttrs = ['bbone_curveinx',
'bbone_curveoutx',
'bbone_curveiny',
'bbone_curveouty']
for attr in bboneAttrs:
src_bboneAttrsDict[attr] = eval("src_eb.{0}".format(attr))
#enter edit mode destination to apply
_enterEdit(destinationArmature)
eb = bpy.data.armatures[destinationArmature].edit_bones[b]
#head
eb.head[0] = src_head[0]
eb.head[1] = src_head[1]
eb.head[2] = src_head[2]
#tail
eb.tail[0] = src_tail[0]
eb.tail[1] = src_tail[1]
eb.tail[2] = src_tail[2]
#roll
eb.roll = src_roll
#bbone attrs
for at, val in src_bboneAttrsDict.items():
setattr(eb, at, val)
#end copy edit bone positions from one armature to another
#remove all fcurves of bones that dont exist anymore. this could happen if addded/removed bones in the middle of action creation
import bpy
import re
armatureName = 'lipArmature'
action = bpy.data.objects[armatureName].animation_data.action #use selected action
fcurvesToRemove = []
for fcurve in action.fcurves:
#print(fcurve.data_path)
dataPath = fcurve.data_path
boneName = re.match('.*\["(.*)"\].*', dataPath).groups()[0]
if boneName not in bpy.data.objects[armatureName].data.bones:
fcurvesToRemove.append(fcurve)
fcurvesToRemove = list(set(fcurvesToRemove)) #remove duplicates
print('removing fcurves:')
print(fcurvesToRemove)
for fcrv in fcurvesToRemove:
action.fcurves.remove(fcrv)
#### Thanks for looking