Here is my script thus far:
bl_info = {
"name": "Insert Multiple Bone Keys",
"author": "Tomkinson",
"version": (1, 0),
"blender": (2, 80, 0),
"location": "View3D > Pose",
"description": "Add keys to any transformed bones",
"warning": "",
"doc_url": "",
"category": "Animation",
}
import bpy
################################################################################################
# for several bones in selected object armatures, add keys to any bones with changed transforms
################################################################################################
class MY_MT_MultiKeyInserts(bpy.types.Operator):
"""add keys to any bones with changed transforms"""
bl_idname = "pose.multi_key_inserts"
bl_label = "Add keys to any transformed bones"
bl_description = "add keys to any bones with changed transforms"
# @classmethod
# def poll(cls, context):
# return (context.active_object is not None) and (context.active_object is 'ARMATURE')
def draw(self, context):
layout = self.layout
def execute(cls, context):
for o in bpy.context.selected_objects:
if o.type!= 'ARMATURE':
continue
arm = o.data
for ag in o.animation_data.action.groups:
pb = o.pose.bones[ ag.name ]
for fc in ag.channels: # n-rotation, 3-location, 3-scale, or any that exist
fc_Idx = fc.array_index
if fc.driver != None:
continue
fcPsthDotSplit = fc.data_path.split('.') # location, rotation, scale
fcPoseName = fcPsthDotSplit[ len(fcPsthDotSplit) - 1 ]
values = fc.keyframe_points.values()
last_keyframe = values[len(values)-1]
# time:
last_time = last_keyframe.co.x
# value:
last_value = last_keyframe.co.y
if fcPoseName.find('location')!=-1:
if fc_Idx>2:
continue
locA = pb.location[fc_Idx]
if ( locA != last_value ):
print( o.name, ag.name, fc.data_path, last_value, locA, fc_Idx )
pb.keyframe_insert( 'location', index=fc_Idx )
elif fcPoseName.find('rotation')!=-1:
rmode = pb.rotation_mode
rotA = None
type = None
match rmode:
case 'QUATERNION':
rotA = pb.rotation_quaternion[fc_Idx]
type = 'rotation_quaternion'
case 'AXIS_ANGLE':
rotA = pb.rotation_axis_angle[fc_Idx]
type = 'rotation_axis_angle'
case _: # one of several Euler types
# verify one of several Euler type
if (rmode.find('X')!=-1) and (rmode.find('Y')!=-1) and (rmode.find('Z')!=-1):
if fc_Idx>2: ## sometimes a fourth (idx=3) is keyed, possible due to a QUATERNION to Euler conversion
continue
rotA = pb.rotation_euler[fc_Idx]
type = 'rotation_euler'
else:
print( 'unknown rotation type', o.name, pb.name, rmode )
if ( type != None ):
if rotA != last_value :
print( o.name, ag.name, fc.data_path, rmode, type, fc_Idx, last_value, rotA )
pb.keyframe_insert( type, index=fc_Idx )
return {'FINISHED'}
def menu_func(self, context):
self.layout.operator( MY_MT_MultiKeyInserts.bl_idname, text="Add keys to any transformed bones" )
def register():
bpy.utils.register_class( MY_MT_MultiKeyInserts )
bpy.types.VIEW3D_MT_pose_context_menu.append( menu_func )
bpy.types.VIEW3D_MT_pose.append( menu_func )
def unregister():
bpy.types.VIEW3D_MT_pose_context_menu.remove( menu_func )
bpy.types.VIEW3D_MT_pose.remove( menu_func )
bpy.utils.unregister_class( MY_MT_MultiKeyInserts )
if __name__ == "__main__":
register()