0
$\begingroup$

I am working on a script that I'd like to share and ask for anyone willing to collaborate on making this an add-on, as I have no experience with add-ons (my last ignorant attempt failed).

workflow motivation: I often have scenes with several rigged characters that I'd like to add additional keyframes to continue an animation. An example might be a sequence where I've animated a couple facing each other who want to lean in and kiss while one touching a shoulder for balance. Or a fight sequence for you more violent types, LOL. So at "new frame" I would put all armatures in pose mode, make adjustments, and then be able to find and key all changed bones (generally IK bone position/rotation, but perhaps FK bones also).

Thus far I have a raw script (no class or method defs), that will look for rotation and location changes in bones from a previous frame's animation value, and add keyframes for any changed FCurve indices.

Possible enhancements:

  • insert keyframe for all indices (XYZ) even if only one has changed.
  • For any changed bones that do not have a previous keyframe on a bone, insert the initial keyframe values at frame 1 and also the keyframe at frame_current.
  • wrap into an add-on and publish

It is the last "wrap" task that I feel rather incapable (lost too much hair in hair pulling last time around).

I appreciate any criticisms or help!

Example test file with script:

I provide my script below in the answer section. I'm going to wager that there's already something existing out there!

$\endgroup$

1 Answer 1

0
$\begingroup$

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()
$\endgroup$
8
  • $\begingroup$ Thanks, very helpful. I was having problems animating bones, and now I see a way forward that’s different from other animation in blender. $\endgroup$ Commented Apr 30 at 16:10
  • 1
    $\begingroup$ I think to package an addon, all you need to do is create a zip. You may need an init.py. You’re welcome to look at the io_scene_x3d plugin, which appears very simple. I’m adding rigging to that, here: github.com/coderextreme/BlenderX3DSupport/tree/main/… $\endgroup$ Commented Apr 30 at 16:14
  • $\begingroup$ Be sure to include the folder your addon is in, no name collisions, please. $\endgroup$ Commented Apr 30 at 16:14
  • $\begingroup$ One thing that helps is having administrative privileges in order to directly update your addon, but first you need to install the zip through Edit Preferences, then enable on the list of addons $\endgroup$ Commented Apr 30 at 16:18
  • $\begingroup$ @JohnCarlson -- final fixes in progress that will include "only keyframe IK bones", "also keyframe first frame neutral position", "also keyframe from last marker" options. I'll update this with a git link when available, if you want to follow. $\endgroup$
    – james_t
    Commented Apr 30 at 16:41

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .