Maya to After Effects export

This is another small script to make an easy transition from Maya to After Effects. You can import Maya .ma files into After Effects, but one unit in Maya is one pixel in After Effects and the result is a very small (and hard to tweak) composition. Also, After Effects is very touchy about the kinds of .ma files it will import, and only accepts files with uniform keyframe times.

What this script does, is do all of the hard work for you. All you have to do is select the objects you need to export and tell it to go, almost.

In the first tab, you select the cameras to export and press the button. Then, you select the objects to export and press the button. This will load all of the things to export into memory.

In the second tab, you set the scene scale and keyframe times. The scale is the value that will depend on your scene. Like I said earlier, one unit in Maya is one pixel in After Effects. Usually, your scene will need to be scaled by 100+. A good way to find a nice value for this, is to look at your maximum values for your keys. If they’re something like (10, 2, 4), you would want a very high export value to get those numbers in the hundreds, if not thousands. If they’re something like (829, 200, 25), you probably only want to scale up by 5, if any scale at all is needed. You can export many times with different scales and see what works best for you. The keyframe times value will insert a keyframe every X frames. You can make this as accurate as you need it. I usually just use 1.

In the third tab, you set the timeframe of the scene and let the magic happen. The first button will scale everything up by the amount before. The second button will bake out all of the cameras you selected. The third button will bake out locater NULLs from the objects. This is important, because After Effects only cares about locators with NULL in their name, and will place an After Effects NULL Object wherever there is a Maya NULL Locator. The last button will select everything that it has baked, allowing you to Export Selected as a .ma for use with After Effects. Then, you can revert to your previous save to get back to your original scene.

NOTE: This script requires PyMEL. To use this script, you should paste the following into the Python section of the Script Window, and then execute the code. Or you could drag the selected code in the Python tab to the Shelf to create a shelf item for it.

## This script requires pymel

## This script offers a way of exporting
## a scene nicely to Adobe After Effects
## To do this, it has to scale everything
## so the values are easier to use in AE,
## and bake the animation to 1 frame intervals
## so AE can read them

## Import pymel and the random module
from pymel.core import *

## ---- VARIABLES ---- ##
originalCamerasList = []
originalCameras = []
originalObjectsList = []
originalObjects = []
exportList = []

## ---- FUNCTIONS ---- ##
def getOriginalCameras(*Args):
    ## Gets the selection
    originalCamerasListSelected = ls(selection=True)
    ## For each object in selection
    for selected in originalCamerasListSelected:
        ## Try to get it's shape node, if it has one and it's a camera
        try:
            if selected.getShape().nodeType() == "camera":
                ## Add it to our camera list
                originalCamerasList.append(selected)
        ## If that errored, it doesn't have a shape node
        except:
            print "Camera: "+selected+" has no shape node or is a shape node."
    ## Sort the list and then make a readable string for the bar
    originalCamerasList.sort()
    # But first, add it to a separate list that gets overwritten each time
    global originalCameras
    originalCameras = originalCamerasList[:]
    originalCamerasString = ', '.join( [str(x) for x in originalCamerasList] )
    ## Get rid of the camera selection list so we don't keep adding to it each time
    del originalCamerasList[:]
    ## Set the bar with the readable list
    originalCamerasLabel.setText(originalCamerasString)

def getOriginalObjects(*Args):
    ## Gets the selection
    originalObjectsListSelected = ls(selection=True)
    ## For each object in selection
    for selected in originalObjectsListSelected:
        ## Try to get it's shape node, if it has one and it's not a camera
        try:
            if selected.getShape().nodeType() != "camera":
                ## Add it to our object list
                originalObjectsList.append(selected)
        ## If that errored, it doesn't have a shape node
        except:
            print "Object: "+selected+" has no shape node or is a shape node"
    ## Sort the list and then make a readable string for the bar
    originalObjectsList.sort()
    ## But first, add it to a separate list that gets overwritten each time
    global originalObjects
    originalObjects = list(originalObjectsList)
    originalObjectsString = ', '.join( [str(x) for x in originalObjectsList] )
    ## Get rid of the object selection list so we don't keep adding to it each time
    del originalObjectsList[:]
    ## Set the bar with the readable list
    originalObjectsLabel.setText(originalObjectsString)

def scaleEverything(*Args):
    ## Make an array for the scale value
    exportScaleValue = [exportScale.getValue(), exportScale.getValue(), exportScale.getValue()]
    ## Empty the selection
    select(clear=True)
    ## Select the cameras
    for cam in originalCameras:
        select(cam, add=True)
    ## Select the objects
    for obj in originalObjects:
        select(obj, add=True)
    ## Group the selected objects
    exportScaleGroup = group(name="exportScale")
    ## Scale the group by the amount given
    exportScaleGroup.setScale(exportScaleValue)

def bakeCameras(*Args):
    for cam in originalCameras:
        print "Baking Camera: "+str(cam)
        ## Gets the name of the old camera
        newCamName = str(cam.name())+"__AE"
        ## Create a new camera
        newCam = createNode('camera')
        ## Name the new camera the new name
        rename(newCam.getParent(), newCamName)
        ## Get all the old camera settings and apply to new cam
        newCam.setFocalLength(cam.getFocalLength())
        newCam.setFStop(cam.getFStop())
        newCam.setShutterAngle(cam.getShutterAngle())
        newCam.setFocusDistance(cam.getFocusDistance())
        ## Constrain the new camera to the old camera so we get the
        ## Locally scaled values in world space for baking
        pC = parentConstraint(cam, newCam.getParent())
        sC = scaleConstraint(cam, newCam.getParent())
        ## Format the in/out time range
        timeRange = (inFrame.getValue(), outFrame.getValue())
        ## Bake the animation of the new camera
        bakeResults(newCam.getParent(), sampleBy=exportKeysAmount.getValue(), time=timeRange, simulation=True)
        ## Remove the constraints
        delete(pC, sC)
        ## Add object to export list
        global exportList
        exportList.append(newCam.getParent())

def bakeObjects(*Args):
    for obj in originalObjects:
        print "Baking Object: "+str(obj)
        ## Gets the name of the old object
        newLocName = str(obj.name())+"__AE__NULL"
        ## Create a new locator
        newLoc = spaceLocator()
        ## Name the new locator the new name
        rename(newLoc.getParent(), newLocName)
        ## Constrain the new locator to the old object so we
        ## get the locally scaled values in world space for baking
        pC = parentConstraint(obj, newLoc)
        sC = scaleConstraint(obj, newLoc)
        ## Format the in/out time range
        timeRange = (inFrame.getValue(), outFrame.getValue())
        ## Bake the animation of the new locator
        bakeResults(newLoc, sampleBy=exportKeysAmount.getValue(), time=timeRange, simulation=True)
        ## Remove the constraints
        delete(pC, sC)
        ## Add object to export list
        global exportList
        exportList.append(newLoc)
        
def export(*Args):
    ## Clear selection
    select(clear=True)
    ## Loop through the exported list generated from our baking
    global exportList
    for each in exportList:
        ## Select each object
        select(each, add=True)
    informBox("DONE!", "All objects prepared for exporting have been selected, you may now: File > Export Selected and save as a Maya Ascii for After Effects", ok="Sweet!")

## ---- GUI SECTION ---- ##
## Makes a flow layout to arrange two columns side by side
gui = window(title="After Effects Exporter", width=190, height=230, sizeable=False)
mainLayout = formLayout()
tabs = tabLayout(height=230, width=190)
formLayout(mainLayout, edit=True, attachForm=((tabs, 'top', 0), (tabs, 'left', 0), (tabs, 'bottom', 0), (tabs, 'right', 0)))

stepOne = rowColumnLayout(numberOfColumns=1, columnWidth=(1, 190), parent=tabs)
text(parent=stepOne, label="Select the cameras you want sent to After Effects:", height=40, wordWrap=True, align="left")
originalCamerasLabel = textField(parent=stepOne, editable=False, text="NONE", width=125)
button(parent=stepOne, command=getOriginalCameras, label="GET CAMERAS")
separator(height=10, parent=stepOne)
text(parent=stepOne, label="Select the objects you want sent to After Effects:", height=40, wordWrap=True, align="left")
originalObjectsLabel = textField(parent=stepOne, editable=False, text="NONE", width=125)
button(parent=stepOne, command=getOriginalObjects, label="GET OBJECTS")

stepTwo = rowColumnLayout(numberOfColumns=1, columnWidth=(1, 190), parent=tabs)
text(parent=stepTwo, label="Set the scale for everything. Keep in mind, one unit in Maya is one pixel in After Effects.", height=70, wordWrap=True, align="left")
exportScale = intField(width=60, value=1000, parent=stepTwo)
separator(height=10, parent=stepTwo)
text(parent=stepTwo, label="Set the keyframe bake amount. This will make keyframes every X frames.", height=70, wordWrap=True, align="left")
exportKeysAmount = intField(width=60, value=1, parent=stepTwo)

stepThree = rowColumnLayout(numberOfColumns=1, columnWidth=(1, 190), parent=tabs)
text(parent=stepThree, label="Set the time range to bake and export:", height=40, wordWrap=True, align="left")
inFrame = intField(width=60, value=1, parent=stepThree)
outFrame = intField(width=60, value=48, parent=stepThree)
separator(height=40, parent=stepThree)
button(parent=stepThree, label="Scale Everything", command=scaleEverything)
button(parent=stepThree, label="Bake Cameras", command=bakeCameras)
button(parent=stepThree, label="Bake Objects", command=bakeObjects)
button(parent=stepThree, label="Export", command=export)

tabLayout(tabs, edit=True, tabLabel=((stepOne, "Step 1"), (stepTwo, "Step 2"), (stepThree, "Step3")))

gui.show()

11 Responses

  1. buy scripts
    buy scripts at · Reply

    This article is very good!
    thanks, 😉

  2. Bleepurchin
    Bleepurchin at · Reply

    Not a good post because there is nothing telling you what to do with that text.

    I copied and pasted it into MEL and got ‘syntax error’ – NOT ALL OF US ARE SCRIPTERS – THINK!

  3. Eric
    Eric at · Reply

    yyyyyyyyyyyyyes. ahhh thank you

    1. Eric
      Eric at · Reply

      p.s. Those with Maya 2013 and up should NOT install pymel I believe. It is already included in Maya 2013. Manually installing Pymel – like I did :S – cause a loop in the application, and I had to reinstall Maya. If you are in this situation and you need to reinstall, make sure you delete everything in your Maya2013/bin foler before reinstalling (delete the wrong pymel files). Thanks again for this script, it really helps.

  4. Dave
    Dave at · Reply

    Cameron, great script…maybe you could post just the .py file too? As it is I have to remove the line numbers in order for it work properly. Thanks!

    1. Dave
      Dave at · Reply

      one question/comment though. The camera zoom information doesn’t seem to export correctly. Any thoughts?

  5. RobC
    RobC at · Reply

    I went ahead and wrote a mel script (for those still on older versions of Maya or are not familiar with how to import py files) that does this and more. Focal length is respected as well. Writes a javascript to do the importing and cleanup of the weird naming convention. give me thumbs up on

    http://www.creativecrash.com/maya/script/camera-and-object-positions-to-after-effects

    THANKS MAN! Im working on a full Javascript import to handle lights as well thanks for sharing your pyMEL

  6. Kyle
    Kyle at · Reply

    Hi Cameron,

    I have used this script in the past but its been so long I can’t get it to work again. I am using Maya 2014 and it is telling me that there is an error in the syntax. From what I understand pymel should have come preloaded with this version of maya. Is there anything that I should be taking a look at to get this to work? Thanks in advance.

Leave a Reply