1

I am writing a script to offset some animations of subsequent objects from an array.

The idea is that I will take the 'old' keyframe data, add some number to it, then insert the 'new' keyframe with the updated data.

import bpy

active_obj = bpy.context.active_object

tracks = active_obj.animation_data.nla_tracks

for track in tracks: for strip in track.strips: action = strip.action

    for fcu in action.fcurves:
        data_path = fcu.data_path  # Name of parameter
        for keyframe in fcu.keyframe_points:
            old_keyframe = keyframe.co
            new_keyframe = (old_keyframe.x + 2, old_keyframe.y + 2)

            fcu.keyframe_delete(data_path, 0, old_keyframe.x)

When I run this code, I get the error: Traceback (most recent call last): File "X:\path\to\file\offset.py", line 17, in <module> TypeError: bpy_struct.keyframe_insert() property "location" not found.

My scene is just a single cube that goes from Location Z=9 to Location Z=1 over 30 frames, but I can provide the blend file if you need that.

I'm unsure why it is trying to run keyframe_insert() when I am not referencing it at all in my script..

Also, I am using Blender 2.93

Documentation for keyframe_delete()

Rug
  • 833
  • 9
  • 26
  • 1
    The keyframe_insert and keyframe_delete are methods on blender object properties s that can be animated, . for example the location property of the "Cube". An fcurve has neither a location property (the error message), nor can it be animated. Suggest if you wish to shift the z location keyframe, search for datapath and array index and set that fcurves value with keyframe.co.y += 2 (The x part of a keyframe is the frame) – batFINGER Jul 27 '21 at 17:38
  • Ah, so you are saying that I should adjust the actual keyframe values instead of deleting and creating new ones? That makes a lot of sense here. Cheers man – Rug Jul 27 '21 at 17:46

1 Answers1

3

Using foreach_get and foreach_set

The keyframe_insert and keyframe_delete are methods on blender object properties that can be animated, . for example the location property of the "Cube". On the default file with no animations, adding a keyframe to cube using cube.keyframe_insert("location", 0, 1) creates an action, creates an fcurve, inserts the keyframe, returns a boolean, True if successful.

An fcurve has neither a location property (the error message), nor can it be animated. (It is the animation) Suggest if you wish to shift the z location keyframe, search for datapath and array index and set that fcurves value with keyframe.co.y += 2 (The x part of a keyframe is the frame)

Note: if the action is in the NLA then shifting the x (frame) can be done by setting the strip start. Adding to the value y, can be done via adding a simple action that is simply a constant

Another thing to consider is the keyframe handles.

Here is a test script to shift all keyframes in an fcurve.

import bpy
import numpy as np

def fcurve_shift(fcurve, shift=(0, 0), shift_handles=True): props = ("co", "handle_left", "handle_right") if shift_handles else ("co",) for prop in props: kfps = np.empty(len(fcurve.keyframe_points) << 1) fcurve.keyframe_points.foreach_get(prop, kfps) kfps = kfps.reshape((2, -1)) + shift fcurve.keyframe_points.foreach_set(prop, kfps.ravel())

test call

action = bpy.data.actions["CubeAction"] for fc in action.fcurves: fcurve_shift(fc, (2, 2))

Copying specific frames from one action to another ( python )

How to obtain max and min of f-curve in python

batFINGER
  • 84,216
  • 10
  • 108
  • 233
  • This looks really promising, thank you. Can you explain why you are bitshifting in the line np.empty(len(fcurve.keyframe_points)? That confuses me a bit. But this is way better than my loops haha. – Rug Jul 28 '21 at 12:59
  • 1
    Basically just doubling for x and y. The foreach methods require a flat list. See https://blender.stackexchange.com/a/214510/15543 image has 4 r, g, b, a.. bit shifting is a squeak quicker than multiplying. – batFINGER Jul 28 '21 at 13:05