Tuesday, February 16, 2016

Blender Script for creating animated motion-trail ribbon

Given an object for which a motion-path has been generated, will create a Beziér-spline ribbon that follows the motion of the object for a given tail length (designated in frames).

# ---------
# Tested in Blender 2.75a and 2.76
# Makes a Bezier-spline motion trail for an object.
# Set the length of the tail in frames via the tail_length variable below.
# To run, select object, generate a motion path for the object (Editor>Motion Paths) across the total desired frame range, and Run Script.
# Warning: this can take a long time to run with long motion paths and long tail sizes
# Once the spline has been generated, extrude and/or add bevel and taper objects to the Geometry settings of the spline.
# Bret Battey / BatHatMedia.com September 2015
# ---------

import bpy
from bpy.props import *
from mathutils import *
from math import *
import time


# Time utils thanks to http://blenderscripting.blogspot.co.uk/search/label/time

def get_last_time():  
    if len(time_list) < 2:  
        return "ERROR: must have two time entries to calculate the difference"  
    return time_list[-1] - time_list[-2]      
def mark_time():  
time_list = [] 

#### Curve creation functions
# sets bezierhandles to auto
def setBezierHandles(obj, mode = 'AUTOMATIC'):
    scene = bpy.context.scene
    if obj.type != 'CURVE':
    scene.objects.active = obj
    bpy.ops.object.mode_set(mode='EDIT', toggle=True)
    bpy.ops.object.mode_set(mode='OBJECT', toggle=True)

# create new CurveObject 
def createCurve(verts, name):
    # create curve
    scene = bpy.context.scene  
    newCurve = bpy.data.curves.new(name, type = 'CURVE') # curvedatablock
    newSpline = newCurve.splines.new(type = 'BEZIER') # spline

    # The new spline already has one point. Add the remaining needed.

    # set curveOptions
    newCurve.dimensions = '3D'

    # create object with newCurve
    new_obj = bpy.data.objects.new(name, newCurve) # object
    scene.objects.link(new_obj) # place in active scene
    new_obj.select = True # set as selected
    scene.objects.active = new_obj  # set as active

    # set bezierhandles

    return new_obj

#based on animation_rotobezier.py
def keyframeBezier(Obj, frame):

    Data = Obj.data
    for Spline in Data.splines:
        for CV in Spline.bezier_points:
            CV.keyframe_insert(data_path='co', frame = frame)
            CV.keyframe_insert(data_path='handle_left', frame = frame)
            CV.keyframe_insert(data_path='handle_right', frame = frame)
# -------------  SCRIPT START

tail_length = 60   # length of the spline in frames
object = bpy.context.object
path = object.motion_path
frame_start = path.frame_start
frame_end = path.frame_end
path_length = path.length

# --- create the Bezier curve
trail = createCurve(tail_length,object.name+" motion_trail")

mark_time() # start measuring elapsed time

# --- key frame the curve at every frame in the motion path

for i in range(tail_length-1,path_length): # motion path index
    print("Point "+str(i))
    for j in range(tail_length): # curve point index 
        trail.data.splines[0].bezier_points[j].co = path.points[(i-tail_length)+j].co # set the coordinates of the motion tail to the corresponding point on the motion path
    # keyframe it
    keyframeBezier(trail, frame_start+i)

print('The motion trail keyframing took {: 5g} seconds'.format(get_last_time()))


  1. This works perfectly up to 2.79, but not in 2.8. I'm still trying to figure out how to make it work properly, any ideas?

  2. Unfortunately, no! I have not touched this (or even Blender much) since I completed the project I made this for.