1
$\begingroup$

When I do this in a script, both left and right handles begin at the (0.0, 0.0) point, so the animation curve looks like this: enter image description here

Handles fix themselves if I do sth, like dragging just one of them. Seems like the api is so low-level that setting keyframes that way doesn't let some callback for interpolation to be called. Here is the python script:

import bpy, re, sys

obj = bpy.context.active_object
obj.animation_data_create()
obj.animation_data.action = bpy.data.actions.new(name="MyAction")
fcu_x = obj.animation_data.action.fcurves.new(data_path="location", index=0)
fcu_y = obj.animation_data.action.fcurves.new(data_path="location", index=1)
fcu_z = obj.animation_data.action.fcurves.new(data_path="location", index=2)
f = open(".../example", "r")
for i in range(30):
    print(str(i))
    ln = f.readline()
    try:
        vec = [float(m) for m in re.split(r"[ \t\n]", ln)[:-1]]
    except ValueError:
        sys.stderr.write("ERROR")
    fcu_x.keyframe_points.add(1)
    fcu_x.keyframe_points[i].co = 24*vec[3], vec[0]
    fcu_y.keyframe_points.add(1)
    fcu_y.keyframe_points[i].co = 24*vec[3], vec[1]
    fcu_z.keyframe_points.add(1)
    fcu_z.keyframe_points[i].co = 24*vec[3], vec[2]

Here I parse a file string-by-string (3 coords + time in sec) and create corresponding location keyframes. I need to create animation in real-time and play it simultaneously, so moving the animated object and setting keyframes based on its real position is out of the question.

I could change the interpolation manually after each frame adding (for Blender to recalculate every handle each time), but I'm afraid that when the number of keyframes is in the thousands, it'll take too long.

Is there a way to set and calculate the interpolation for only the last keyframe point when adding it?

$\endgroup$
1
$\begingroup$

When using keyframe_insert() the default interpolation set in preferences is applied to each keyframe as it is added, this doesn't happen when using fcurves.new(), which always leaves it at bezier. If you don't want to use bezier interpolation then you will need to set the interpolation for each point as you create them.

fcu_x.keyframe_points[i].interpolation = 'LINEAR'

Then you will want to call fcurve.update().

update()
Ensure keyframes are sorted in chronological order and handles are set correctly

$\endgroup$
  • $\begingroup$ Thanks! update() is what's needed. Despite my fears, doesn't take too much resources even with tens of thousands and still growing number of keyframes with ongoing animation playing. Must check not all frames of a curve as an optimization. $\endgroup$ – alexey Feb 21 '16 at 21:07
  • $\begingroup$ Maybe there are some more optimizations to be done in my scenario with modal operator? I periodically read file as in the script above and add keyframes. Sadly, everything is done in one thread with GUI using timer. Looks like it's hard for Blender to keep up, although it works now. Maybe Game Engine would be of help due to native interactivity? $\endgroup$ – alexey Feb 21 '16 at 21:20
  • $\begingroup$ You should be able to use the multiprocess module to update your curves as blender is running. It doesn't sound like using a file is really helpful to you, if you need to stream data to blender then maybe open a network socket. The netrender addon might give inspiration, it uses socketserver and http to let each node talk to each other. $\endgroup$ – sambler Feb 22 '16 at 6:28
  • $\begingroup$ If only multiprocessing module worked in Blender! When I wrapped the script above in a function and start() it with m-p module, it didn't even give errors, it just did nothing. Maybe there's some special Blender way to do it? $\endgroup$ – alexey Feb 22 '16 at 10:06
  • $\begingroup$ Concerning the file, it's needed for simplicity as it's written with some other prog while being read by Blender and needs to be saved as a log. It's not the bottleneck here. $\endgroup$ – alexey Feb 22 '16 at 10:14
0
$\begingroup$

Consider setting the left and right handles.

I did not execute the Python below so you may need to improve it. The intention is that the handles on the left and right will be on the same y coordinate as co, yet the x coordinate is different. Horizontal handles.

pseudo code

fcu_x.keyframe_points[i].co = 24*vec[3], vec[0]
increment_for_x = .08 # revise to taste
f = fcu_x.keyframe_points[i]
f.left_handle  = f.co.x - increment_for_x, f.co.y;
f.right_handle = f.co.x + increment_for_x, f.co.y;
$\endgroup$
  • $\begingroup$ I actually meant I need not to manually set handles, but rather to make them interpolated on the fly, the moment keyframe is created. For some reason now it isn't so until some action is done (like dragging one of the handles from the position on the picture). $\endgroup$ – alexey Feb 20 '16 at 22:55

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.