4
$\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
    Commented May 8, 2021 at 10:21

1 Answer 1

4
$\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$

You must log in to answer this question.

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