[Scripts]A script to load a camera exported from AE to maya

Hi!

Working on a scifi live action movie (lots of greenscreen footage), so I’ve created some scripts for things we are doing frequently. We are using ae cs 6 for parts of the camera tracking and since I’ve only found an ae script to export to maya/3dsmax or lightwave I created a script which takes the exported .ma (maya) file and creates the transforms and cameras from it. I have no intention to develop this script past the current state where it works (it only supports cameras, and not target cameras, but the exporter does not use that so for my purpose it’s not needed). I might in the future modify the AE script to create a python script for blender directly instead.

The code is quite ugly, and does not make a lot of error testing - it’s expecting a valid file (as the exporter creates them valid it should not be a problem). Usage: I tend to append the script to the file, remove the # from the beginning of the last line (the loadfile line), and change the path to the file I want loaded.

Please let me know if you find it useful! Or find bugs that are not part of the limitations.

Best wishes and happy blending!
/Torbjörn

PS I could not remember the password for the server I usually use, so for the moment I’ll post the script split into several posts here (as it was too long)


# NOTES:
# only supports cameras. Expects animation curves to be named in the pattern objectname_variable (the exporter
# follows that pattern, so it should not be a problem)
# UNSUPPORTED:
# does not check if created objects get the expected names. One could argue for using a dictionary
# to be sure to use the final name, but this has been omitted due to the fact that I don't personally need this.
# Maya files support the same names existing under different paths (ie in different groups). Blender probably does not.
# target cameras are not supported (but are not used by the exporter, so it does not matter)
# I have not tried if it's working to have several nested transforms before the camera, but can't see that the exporter will
# and it's not part of our workflow anyway.
#
# Limitations:
# really expects a correct file, very little error detection is created.

import bpy
import re
from mathutils import Vector, Euler

# to do if you want it to be good : add a lookup table for intended names and final names.
tw_main_node="tw_main_node"


def splitArguments(line):
argumentssplit = str.split(line,"-")
arguments = {}
for a in argumentssplit:
# print (a)
tmp = a.partition(' ')
# print (tmp)
arguments[ tmp[0] ] = tmp[2]
# print(arguments)
return arguments
def stripCitation(line):
tmp = line.strip()
if not tmp.startswith('\"'):
raise ValueError("Stripping citations, but not a citation")
return tmp[1:].partition('\"')[0]

def splitXYZ(line):
x, y, z = line.strip().split() # note very weird! even splits = line.strip().split() changes the order
return [float(x),-float(z),float(y)]

#createNode animCurveTL -n "L3DTrackerCameCC1_TranslateY";
# setAttr ".tan" 9;
# setAttr ".wgt" no;
# setAttr -s 424 ".ktv[1:424]" 1 3.2121153944043 2 3.20538667823737 3 3.1916467368099 4 3.17486470189342 5 3.16492863722789 6 3.15168543781801 7 3.14271647613811 8 3.13756700958293 9 3.
currentAnimCurveObject =0
currentAnimCurveParameter = ""




def processKeyframes(line):
    global currentAnimCurveObject
    global currentAnimCurveParameter
    arguments = splitArguments(line)
    name = stripCitation( arguments['n'] )
    #print(name)
    splits = name.rpartition('_')
    #print(splits)
    try:
        currentAnimCurveObject = bpy.data.objects[splits[0]]
        if(currentAnimCurveObject is None):
            ValueError("Object for animationcurve not found")
        currentAnimCurveParameter = splits[2]
        
    except:
        print("Object for animationcurve not found.")
        print (name)
        currentAnimCurveObject = 0;
        currentAnimCurveParameter = "Not found"
    return

def processSetAttrLine(line):
#    global currentAnimCurveObject
#    global currentAnimCurveParameter
    TRANSLATION_STRING_CONST = '\".t\" -type \"double3\" '
    ROTATION_STRING_CONST =    '\".r\" -type \"double3\" '
    SCALE_STRING_CONST = '\".s\" -type \"double3\" '
    CAMERA_APPERTURE_STRING_CONST = '\".cap\" -type \"double2\" '
    CAMERA_FAR_CLIP_PLANE = '\".fcp\" '
    CAMERA_FOCAL_LENGTH = '\".fl\" '
    #CAMERA_
    parts  = line.strip().partition(' ')
    #print(parts[2][0:30])
    to_process = None
    if parts[2].startswith(TRANSLATION_STRING_CONST):
        #print("TRANSLATION")
        location = parts[2][len(TRANSLATION_STRING_CONST): ]
        #print (location)
        #if location :        
        location = splitXYZ( location )
        #print("Translating to: "+location)
        #bpy.context.active_object.location.x = location[0]
        #bpy.context.active_object.location.y = location[1]
        #bpy.context.active_object.location.z = location[2]
        bpy.ops.transform.translate(value=(location[0], location[1], location[2]))
        #else :
        #    raise ValueError("Missing location for translation!")
    elif parts[2].startswith(SCALE_STRING_CONST):
        #print("SCALE")
        location = parts[2][len(SCALE_STRING_CONST): ]
        #print (location)
        location = splitXYZ( location )
        bpy.ops.transform.resize(value=(location[0], location[1], location[2]))
            
    elif parts[2].startswith(ROTATION_STRING_CONST):
        #print("ROTATION")
        rotation = parts[2][len(ROTATION_STRING_CONST): ]
        #print (rotation)
        if rotation :        
            rotation = splitXYZ( rotation )
            #print(rotation)
            bpy.ops.transform.rotate(value= rotation[0]* 0.017453292523928, axis=(1,0,0) );
            bpy.ops.transform.rotate(value= rotation[1]* 0.017453292523928, axis=(0,1,0) );
            bpy.ops.transform.rotate(value= rotation[2]* 0.017453292523928, axis=(0,0,1) );
            
            
        else :
            raise ValueError("Missing location for rotation!")
    elif parts[2].startswith(CAMERA_APPERTURE_STRING_CONST):
        to_process = parts[2][len(CAMERA_APPERTURE_STRING_CONST): ]
        width, junk, height = to_process.partition(' ')
        #bpy.context.object.data.sensor_fit = 'HORIZONTAL' # let it stay at auto
        bpy.context.object.data.sensor_width = float(width) * 25.4
        bpy.context.object.data.sensor_height = float(height)*25.4
    elif parts[2].startswith(CAMERA_FAR_CLIP_PLANE):
        to_process = parts[2][len(CAMERA_FAR_CLIP_PLANE): ]
        bpy.context.object.data.clip_end = float(to_process)
    elif parts[2].startswith(CAMERA_FOCAL_LENGTH):
        to_process = parts[2][len(CAMERA_FOCAL_LENGTH): ]
        bpy.context.object.data.lens_unit = 'MILLIMETERS'
        bpy.context.object.data.lens = float(to_process)
    elif parts[2].startswith("-s") or parts[2].startswith('\".ktv['):
        #process keyframes
        if(currentAnimCurveObject == 0):
            #print("Ignoring uncreated objecttype")
            return
        keys = 0        
        if( parts[2].startswith("-s") ):
            #print("Animcurve by -s")    
            to_process = parts[2][3:]
            #print(to_process)
            to_process = to_process.partition(' ')
            keys = int( to_process[0] )
            to_process = to_process[2]
            if(not to_process.startswith("\".ktv")):
                #print("Ignoring")
                return
            if(to_process.strip() == '\".ktv\"') :            
                # this means the keyframes will be passed on a different line.  ignore?
                return
        if(parts[2].startswith('\".ktv[') ):
            # get the amount of keyframes to create from
            print(parts[2])     
            process =    parts[2][len('\".ktv['):]
            keytext, junk, to_process = process.partition(']\" ')
            print("K: "+keytext + " j: "+junk+" TP: "+to_process)
            keystart, junk, keystop = keytext.partition(':')
            if  junk==':':
               keys = len( range( int(keystart), int(keystop) ) ) 
            else:
                keys =1 
            print ("No_keys : " +str(keys))
        obj = currentAnimCurveObject
        if(obj.animation_data is None):
            print("Creating animation data and action")
            obj.animation_data_create()
            obj.animation_data.action =  bpy.data.actions.new(name=currentAnimCurveObject.parent.name+"."+currentAnimCurveObject.name+"_"+currentAnimCurveParameter)
        else:
            print("Animation data already exists")
        curve = 0
        conversion_rate = 1.0 
        print("Processing keyframes for object : " + currentAnimCurveObject.name)
        print("Parameter: "+currentAnimCurveParameter)

        if(currentAnimCurveParameter.startswith("translate") or 
           currentAnimCurveParameter.startswith("Translate")):
            if(currentAnimCurveParameter.endswith("X")):
                curve = obj.animation_data.action.fcurves.new(data_path="location", index=0)
            elif(currentAnimCurveParameter.endswith("Y")):
                curve = obj.animation_data.action.fcurves.new(data_path="location", index=2)
            elif(currentAnimCurveParameter.endswith("Z")):
                curve = obj.animation_data.action.fcurves.new(data_path="location", index=1)
                conversion_rate = -1.0
            else:
                ValueError("Wrong translation axis!")        
        
        
            s="""mindex =0
            if(currentAnimCurveParameter.endswith("X")):
                mindex = 0
            elif(currentAnimCurveParameter.endswith("Y")):
                mindex = 1
            elif(currentAnimCurveParameter.endswith("Z")):
                mindex = 2
            else:
                ValueError("Got translation but weird axis!")
            try:          
                #matches = ( x for x in  obj.animation_data.action.fcurves if (x.data_path=="location" and  x.array_index==mindex) )      
                for x in obj.animation_data.action.fcurves:
                    if(x.data_path=="location" and x.array_index==mindex):
                        curve = x
                        print("Found curve!")
                        print("Curve has : "+ curve
                        
                        break
                #curve = matches[0] #obj.animation_data.action.fcurves[0] #index=mindex)
                #if(curve.data_path=="location" and curve.array_index==mindex)
                #    print("has right curve!")
                #else: 
                #    ValueError()
            except: 
                print("No curve found, creating new")
                curve = obj.animation_data.action.fcurves.new(data_path="location", index=mindex)
                """
        if(currentAnimCurveParameter.startswith("rotate") or 
           currentAnimCurveParameter.startswith("Rotate")):    
            conversion_rate = 0.017453292523928
            if(currentAnimCurveParameter.endswith("X")):
                curve = obj.animation_data.action.fcurves.new(data_path="rotation_euler", index=0)
            elif(currentAnimCurveParameter.endswith("Y")):
                curve = obj.animation_data.action.fcurves.new(data_path="rotation_euler", index=2)
            elif(currentAnimCurveParameter.endswith("Z")):
                curve = obj.animation_data.action.fcurves.new(data_path="rotation_euler", index=1)
                conversion_rate*=-1
            else:
                ValueError("Wrong rotation axis!")        
        if(currentAnimCurveParameter.startswith("focalLength") or 
           currentAnimCurveParameter.startswith("FocalLength")):
                curve = obj.animation_data.action.fcurves.new(data_path="data.lens")
                obj.data.lens_unit = 'MILLIMETERS'
        
        if curve==0:
            print("Ignoring unsupported animation curve "+currentAnimCurveParameter)
            return
        if(curve is not 0):
    

            to_process = to_process.partition(' ')[2].strip()
            print(to_process[0:30])
        
            curve.keyframe_points.add(keys - len(curve.keyframe_points) )
            for i in range (0, keys):
                frame, skip, value = to_process.partition(' ')
                to_process = value.partition(' ')
                value = to_process[0]
                try:
                    #print("Adding keyframe "+str(i)+"/"+str(keys)+" f: "+frame+" v: "+value)
                    f = float(frame)
                    v = float(value) * conversion_rate
                    curve.keyframe_points[i].co = f, v
                    curve.keyframe_points[i].handle_left = (f-2), v
                    curve.keyframe_points[i].handle_left_type = 'AUTO'
                    curve.keyframe_points[i].handle_right = (f+2),v
                    curve.keyframe_points[i].handle_right_type = 'AUTO'
                    curve.keyframe_points[i].interpolation = 'LINEAR'
                    
                except ValueError as e:
                    print("Failed to convert strings f: "+frame+" v: "+value)
                    raise e
                to_process = to_process[2]
                
        
    return


#createNode camera -n "CamShape762" -p "Cam762"
def processCameraLine(line):
    global tw_main_node
    args = splitArguments(line)
    name = stripCitation( args['n'] )
    parent = stripCitation( args['p'] )
    bpy.ops.object.camera_add()
    #just insert some default values
    bpy.context.object.data.sensor_width = 1.417* 25.4
    bpy.context.object.data.sensor_height = 0.945*25.4
    bpy.context.object.data.clip_end = 1000000.0
#    bpy.context.object.rotation_mode = 'XZY'
    bpy.ops.transform.rotate(value= 90* 0.017453292523928, axis=(1,0,0) );    
    camera = bpy.context.active_object
    camera.name = name
    try:
        parent_obj = bpy.data.objects[ parent ]
        if(parent_obj) :
            camera.parent = parent_obj
    except:
        bpy.context.active_object.parent = tw_main_node
    return

def processTransformLine(line):
    global tw_main_node
    arguments = splitArguments(line)
    name = stripCitation( arguments['n'] )
    print("Creating transform "+name)
    bpy.ops.object.empty_add()
    bpy.context.active_object.name = name
    bpy.context.object.rotation_mode = 'XZY'
    try :
        parent = stripCitation( arguments['p'] )
        parent_obj = bpy.data.objects[ parent ]
        if(parent_obj) :
            bpy.context.active_object.parent = parent_obj
    except:
        bpy.context.active_object.parent = tw_main_node
    return

tw_file_line_number = 0 # not really meaningful unless the file ends each ; ended line with a newline

def loadFile(filename):
    # create main node to parent everything to
    global tw_main_node  
    tw_main_node = "tw_main_node_"+filename
    bpy.ops.object.empty_add()
    bpy.context.active_object.name = tw_main_node
    tw_main_node = bpy.context.active_object
    
    global tw_file_line_number    
    with open(filename,'r') as file:
        filecontents = file.read()
        lines = str.split(filecontents, ';')
        for line in lines:
            tw_file_line_number+=1
            text_to_process = str.strip(line)
            if(text_to_process.startswith("createNode transform")):
                 processTransformLine(line)             
            elif(text_to_process.startswith("createNode camera")):
                 processCameraLine(line)
            elif(text_to_process.startswith("createNode animCurve")):
                processKeyframes(text_to_process)
            elif(text_to_process.startswith("setAttr")):
                processSetAttrLine(line)
            
        file.close()
    
    
print("SCRIPT STARTING")
print("=======================================")
#bpy.ops.view3d.snap_cursor_to_center()
#loadFile("f:\\blender\	est\\7_6_cam_export.ma")