Hi all,
I modified the script from post no 15 a bit for my purposes and thought it might be useful for others, so here it is.
Changes:
- put it in “Object” menu for easy access from the 3D menu header, acces via Object-> Script -> Path 2 IPO.
- added a GUI with a Step value for not recording keys every frame. Value 10 for example means: record every 10th frame. First and last frames get recorded in any case.
- also in GUI, “Speed IPO Name”. If the path has a speed ipo, set the name here, default is “speed”. The script then uses the speed ipo for path length, otherwise it uses the PathLen setting in curve edit buttons.
- made IPO clearing work better
- some small general cleanups and fixes
I hope I listed all authors. If not, please tell me. Thanks very much for your work on the script. Btw, it might be good to add a GPL header, would the other authors agree?
Here’s my version, object-path-to-ipo.py
#!BPY
"""
Name: 'Path 2 IPO'
Blender: 248
Group: 'Object'
Tooltip: 'Bezier curve to IPO'
"""
# By haramanai, FunkyWurm, Sanne
# http://blenderartists.org/forum/showthread.php?t=83942&highlight=path+to+ipo
# Changes by Sanne, 29. Nov 2008:
# - put it in "Object" menu for easy access from the 3D menu header, acces via Object-> Script -> Path 2 IPO.
# - added a GUI with a Step value for not recording keys every frame. Value 10 for example means: record every 10th frame. First and last frames get recorded in any case.
# - also in GUI, "Speed IPO Name". If the path has a speed ipo, set the name here. The script then uses the speed ipo for path length, otherwise it uses the PathLen setting in curve edit buttons.
# - made IPO clearing work better
# - some small general cleanups
import Blender
from Blender import *
def GetObjects():
""" Get selected objects
Make sure that a curve and a mesh have been selected
Return the curve and mesh objects
"""
# Initialise the variables
Curve = None
Mesh = None
ObjList = Blender.Object.GetSelected()
# Abort if too many objects are selected
if len(ObjList) > 2:
retval = Blender.Draw.PupMenu("Select 1 curve and 1 object only")
return
# Assign objects to their variables
for object in ObjList:
if object.type == "Curve":
Curve = object
elif object.type == "Mesh":
Mesh = object
elif object.type == "Empty":
Mesh = object
# Abort if required objects are not selected
if Curve == None or Mesh == None:
retval = Blender.Draw.PupMenu("You must select 1 curve and 1 object")
return
# Abort if curve not set to curve path
bitMask = 0x08 #the flag for curve path in Curve.data
if not Curve.data.getFlag() & bitMask:
retval = Blender.Draw.PupMenu("Curve must be set to a curve path")
return
# Return objects if all is well
return Curve, Mesh
def getEndFrame(Curve, speediponame):
""" Get the end frame of the path animation
If the curve has a speed IPO, get the speed IPO's last frame
number (truncted to int), else use the curve's PathLen value.
Appearantly Curve.getIpo() doesn't return a present speed ipo,
so we need to pass the name of the speed ipo in order to access it.
"""
speedipolist = [ipo for ipo in Ipo.Get() if ipo.name == speediponame]
if speedipolist:
speedipo = speedipolist[0]
speedcu = speedipo[Ipo.CU_SPEED]
if speedcu:
lastbeztr = speedcu.bezierPoints[-1]
end = int(lastbeztr.pt[0])
print 'Using endframe %d of speed ipo "%s".' % (end, speediponame)
else:
end = Curve.data.pathlen
print 'No speed curve in speed ipo "%s" found, using pathlen %d of curve.' % (speediponame, end)
else:
end = Curve.data.pathlen
print 'No speed ipo "%s" found, using pathlen %d of curve.' % (speediponame, end)
return end
def BakeIpoCurves(Curve, Mesh, step, speediponame):
""" Bake path animation to IPO curves
Thanks to haramanai for the concept of this function.
It seems to be necessary to copy the LocRot matrix from
one object to another in order to add the Ipo keys to a
second object. In this instance, I created an empty.
"""
# Check for existing Ipo block that could be used
# in order to avoid unnecessary duplication
try:
ipo = Ipo.Get("IpoFrom"+Curve.data.name)
except:
ipo = Ipo.New("Object","IpoFrom"+Curve.data.name)
# Delete IPO curves if present (Mesh.clearIpo() just unlinks,
# gets relinked later, so doesn't delete anything)
if Mesh.ipo and Mesh.ipo.name == ipo.name:
cc = dict(ipo.curveConsts.items())
for icu in ipo.curves:
ipo[cc["OB_"+icu.name.upper()]] = None
if not Mesh.ipo:
Mesh.ipo = ipo
# Create an empty and make it follow the curve path
empty = None
empty = Object.New('Empty','tempEmpty')
empty = Scene.GetCurrent().objects.new('Empty')
empty.constraints.append(Constraint.Type.FOLLOWPATH)
empty.constraints[0][Constraint.Settings.TARGET] = Curve
empty.constraints[0][Constraint.Settings.FOLLOW] = True
# Animate the empty along the curve path, copying loc/rot at
# each frame to the object and inserting Ipo keys for the object
start = 1
end = getEndFrame(Curve, speediponame)
hasendkey = False
for i in xrange(start, end+1, step):
Blender.Set("curframe", i)
Mesh.setMatrix(empty.matrix)
Mesh.insertIpoKey(Object.IpoKeyTypes.LOCROT)
if i == end:
hasendkey = True
if not hasendkey:
Blender.Set("curframe", end)
Mesh.insertIpoKey(Object.IpoKeyTypes.LOCROT)
Blender.Set("curframe", 1) # Change current frame to 1 again to be tidy
# Clear up the empty. Nice and tidy ;-)
#Scene.GetCurrent().unlink(empty)
Scene.GetCurrent().objects.unlink(empty)
return ipo
def main():
""" The main function """
try:
Curve, Mesh = GetObjects()
except:
retval = Blender.Draw.PupMenu("Script aborting")
return
PREF_STEP = Blender.Draw.Create(1)
PREF_SPEEDIPO = Blender.Draw.Create("speed")
block = []
# inputs: title, button, min/max values, tooltip
block.append(("Step ", PREF_STEP, 1, 10000, "Insert key step value"))
block.append(("Speed IPO name: ", PREF_SPEEDIPO, 0, 21, "Name of the path's speed IPO, if present"))
if not Blender.Draw.PupBlock("Path 2 IPO", block):
return
bakedIpo = BakeIpoCurves(Curve, Mesh, PREF_STEP.val, PREF_SPEEDIPO.val)
# ------------------------
if __name__ == "__main__":
main()