I just ran into a hideous problem when I was trying to bake out cameras and animation from one scene, and get the animation into another. I always keep my lighting scenes separate from the animators’ scenes so they can’t mess up my shots. Unfortunately, the animator had used a Scene Time Warp, and then baked out the animation, which of course never quite lined up correctly and we couldn’t figure out what was going on. Turns out that keys baked under a Time Warp will not actually bake the Time Warp itself.

Here’s a script, then, to bake the effects of the time warp on all selected objects. It then (optional, but recommended) deletes the time warp from the scene entirely:

import maya.cmds as cmds

# testing #
objects = cmds.ls(sl=1)
start = int(cmds.playbackOptions(q=1,min=1))
end = int(cmds.playbackOptions(q=1,max=1))

def bakeTimeWarp(objects,start,end,killWarp=True):
    # for each frame between start and end, query time1.outTime and time1.unwarpedTime
    # for each object, get each channel with at least one keyframe set
    # for each channel:
    #     get the value of the channel at outTime
    #     set the channel to this value at unwarpedTime and set a keyframe
    for i in objects:
        dupe = cmds.duplicate(i,po=1)[0]
        if not cmds.attributeQuery('bakeTimeWarpConnection',node=i,ex=1):
            cmds.addAttr(i,ln='bakeTimeWarpConnection',at='message')
        cmds.connectAttr(dupe+'.message',i+'.bakeTimeWarpConnection')
    for x in range(start,end+1):
        cmds.currentTime(x)
        outTime = cmds.getAttr('time1.outTime')
        unwarpedTime = cmds.getAttr('time1.unwarpedTime')
        for i in objects:
            # build a list of all keyed channels.
            keyables = cmds.listAttr(i,k=1)
            keyedChans = [f for f in keyables if cmds.keyframe(i+'.'+f,q=1,n=1)]
            dupe = cmds.listConnections(i+'.bakeTimeWarpConnection')[0]
            for chan in keyedChans:
                val = cmds.getAttr(i+'.'+chan,t=outTime)
                cmds.setAttr(dupe+'.'+chan,val)
                cmds.setKeyframe(dupe+'.'+chan,t=unwarpedTime)
    # now reconnect anim curves from the duplicate to the original. then delete the duplicates and finally remove the timewarp.
    for i in objects:
        dupe = cmds.listConnections(i+'.bakeTimeWarpConnection')[0]
        chans = [f for f in cmds.listAttr(dupe,k=1) if cmds.keyframe(dupe+'.'+f,q=1,n=1)]
        for chan in chans:
            animCurve = cmds.keyframe(dupe+'.'+chan,q=1,n=1)[0]
            oldCurve = cmds.keyframe(i+'.'+chan,q=1,n=1)
            cmds.connectAttr(animCurve+'.output',i+'.'+chan,f=1)
            cmds.delete(oldCurve)
        cmds.delete(dupe)
        cmds.deleteAttr(i+'.bakeTimeWarpConnection')
    if killWarp:
        timeWarp = cmds.listConnections('time1.timewarpIn_Raw')[0]
        cmds.delete(timeWarp)

If you take a look at what the script is doing, it first makes a duplicate of each selected object, including only the transform (none of the object’s children). It then creates a new “message” type attribute on the original object, and connects the ‘message’ property of the duplicate (all nodes have a ‘message’ output) to this attribute. This is to link the objects and their duplicates together, independent of either object’s name or order in the DAG.

Next up, the script cycles through every frame in the given range. It gets the default time1 node’s outTime and unwarpedTime outputs for the current frame, then for each object, gets the value of every keyed channel at the current frame’s warped time. It then sets a keyframe on the duplicate object using this value, but at the unwarped time. The duplicate is necessary to prevent overwriting keys we have yet to read on the original object.

Once every object has been processed, we once again cycle through every object. We use the ‘message’ connection between each object and its duplicate to find the name of each object’s duplicate. Then we get all keyed channels on the duplicate, get the name of the animCurve object attached to each channel, and connect the animCurve to the original object. Then just delete the duplicate and the object’s message attribute, and go on to the next object.

Not a terribly complex script, but it is definitely handy for these timewarped scenes. Why this kind of functionality isn’t built into Maya, I will never know.

To download the script, just right-click / save as the link here: hfBakeTimewarp.zip