1
$\begingroup$

I have an object that has a location driver, so the location of this object is constantly changing. I want to keyframe its motion throughout a range of frames, with each frame being a keyframe (essentially creating an fcurve). The method I have been using in python is going frame by frame and inserting a keyframe each frame.

import bpy
from bpy import *

bpy.context.scene.frame_current = 0
i = 0

for i in range(0,1900):
    bpy.ops.anim.keyframe_insert_menu(type='Location', confirm_success=False, always_prompt=False)
    bpy.context.scene.frame_current = bpy.context.scene.frame_current + 1
    i += 1

Is there an easier way to keyframe a group of frames via python instead of going frame by frame? The more objects added, and a bigger range of frames means blender getting slower over time to add keyframes one by one, so this method can take many hours depending on how many different objects I have involved.

$\endgroup$
2
$\begingroup$

You should avoid operators if possible and do something like this:

import bpy
from bpy import context

scene = context.scene
ob = context.object  # object to keyframe should be selected/active
# ob = bpy.data.objects["name"]  # or referenced by name


for i in range(0, 1900):
    scene.frame_set(i)
    ob.keyframe_insert(data_path="location", index=-1)
$\endgroup$
2
$\begingroup$

zeffii's answer is better than using bpy.ops, but if you want to get closer to the metal, it gets a little clumsy. Blender's action API is missing several convenience functions.

It boils down to these steps

  • make sure the datablock has animation_data
  • get the animation_data.action or .action=bpy.data.actions.new(name)
  • find the action_data.fcurves[i] that matches the data_path and array_index, or create the fcurve
  • make sure there are enough fcurve.keyframe_points to load our data into
  • assign the fcurve.keyframe_points[j].co and possibly .interpolation, .handle_left, .handle_right, etc.

There are a couple of examples embedded in http://web.purplefrog.com/~thoth/blender/python-cookbook/movie-card-stack.html and http://web.purplefrog.com/~thoth/blender/python-cookbook/csv-to-fcurve.html which I will summarize here:

def make_or_get_fcurve(obj, data_path, index=-1):
    """
    :param obj: the data block from which we'll get the fcurve
    :param data_path:
    :param index:
    :return:
    """

    # freshly created objects don't have animation_data yet.
    if obj.animation_data is None:
        obj.animation_data_create()
    ad=obj.animation_data
    if ad.action is None:
        ad.action = bpy.data.actions.new(obj.name+"Action")

    for fc in ad.action.fcurves:
        if (fc.data_path != data_path):
            continue
        if index<0 or index==fc.array_index:
            return fc
    # the action didn't have the fcurve we needed, yet
    return ad.action.fcurves.new(data_path, index)

and from the other example

def find_or_create_fcurve(action, data_path, array_index= -1):
    for fc in action.fcurves:
        if fc.data_path == data_path and ( array_index<0 or fc.array_index==array_index):
            return fc
    fc = action.fcurves.new(data_path, array_index)
    return fc


def make_eyeball_look_at(armature, frame):

    eyeball = get_eyeball()

    cns = find_constraint_track_to(eyeball, armature, "card release")
    data_path = 'constraints["%s"].influence' % cns.name
    fc = find_or_create_fcurve(eyeball.animation_data.action, data_path)

    i0 = len(fc.keyframe_points)
    fc.keyframe_points.add(4)
    kp0 = fc.keyframe_points[i0]
    kp1 = fc.keyframe_points[i0 + 1]
    kp2 = fc.keyframe_points[i0 + 2]
    kp3 = fc.keyframe_points[i0 + 3]

    kp0.handle_left = [frame - 7, 0]
    kp0.co = [frame - 6, 0]
    kp0.handle_right = [frame - 5, 0]

    kp1.handle_left = [frame - 4, 1]
    kp1.co = [frame - 3, 1]
    kp1.handle_right = [frame - 2, 1]

    kp2.handle_left = [frame + 2, 1]
    kp2.co = [frame + 3, 1]
    kp2.handle_right = [frame + 4, 1]

    kp3.handle_left = [frame + 5, 0]
    kp3.co = [frame + 6, 0]
    kp3.handle_right = [frame + 7, 0]


def add_keyframe_points(fcurve, kps):
    idx0 = len(fcurve.keyframe_points)
    fcurve.keyframe_points.add(len(kps))
    rval = []
    for i in range(len(kps)):
        kp = fcurve.keyframe_points[idx0 + i]
        kp.co = kps[i]
        rval.append(kp)
    return rval
$\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.