1
$\begingroup$

I have over 1000 animation files that I want to "crop" off keyframes outside of a specified range.

Keyframes all start at frame 1 instead of frame 0, as such I want to shift all keyframes by one frame such that the animations start at frame 0.

I have an int variable named "framelength" which stores the framenumber of the last good frame. I want to remove every keyframe after this value.

The most similar code I was able to find online was:

How to remove all only rotation keyframes from current action with Python? and Editing fcurve.keyframe points in FAST mode? Both were written by BatFINGER.

I tried using a combination of the two to achieve what I want, but am struggling with deleting only certain keyframes.

ob = context.object
ad = ob.animation_data
action = ad.action if ad else None
if action:
    for fc in action.fcurves:
        sel = list(bool(i > int(framelength)) for i in range(len(fc.keyframe_points)))
        for i in range(len(fc.keyframe_points)):
            if i>35:
                fc.pop()

Above is my attempt at solving the problem.

$\endgroup$
1
  • 1
    $\begingroup$ Here's another that may be useful, creates a new sliced action. To make the default start frame 0 change to _kfs[:,0] -= start_frame. For when use shift is set. blender.stackexchange.com/a/214899/15543 $\endgroup$ – batFINGER May 8 at 10:21
1
$\begingroup$

I got it working thanks to batFINGER's comment.

My final code looked like this:

import numpy as np
def action_slice(action, start_frame, end_frame, shift=True): 
    copy = bpy.data.actions.new(action.name)
    copy.id_root = action.id_root
    
    for fcurve in action.fcurves:
        kfs = np.empty(len(fcurve.keyframe_points) << 1)
        fcurve.keyframe_points.foreach_get("co", kfs)
        kfs = kfs.reshape((-1, 2))
        _fc = copy.fcurves.new(
                fcurve.data_path,
                index = fcurve.array_index
                )
        _fc.keyframe_points.add(0)
        _kfs = kfs[
                np.logical_and(
                    kfs[:,0] >= start_frame, 
                    kfs[:,0] <= end_frame
                    )
                ]
        if shift:
            _kfs[:,0] -= start_frame
        _fc.keyframe_points.add(_kfs.shape[0])
        _fc.keyframe_points.foreach_set("co", _kfs.ravel())
    return copy

def object_do_slice(string, endframe):
    ob = bpy.data.objects.get(string)#context.object
    ad = ob.animation_data
    action = ad.action if ad else None
    if action:
        ad.action = action_slice(action, 1, endframe, shift=True)

object_do_slice("Root",framelength+1)  
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.