1
# ##### BEGIN GPL LICENSE BLOCK #####
3
# This program is free software; you can redistribute it and/or
4
# modify it under the terms of the GNU General Public License
5
# as published by the Free Software Foundation; either version 2
6
# of the License, or (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software Foundation,
15
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
# ##### END GPL LICENSE BLOCK #####
22
'name': 'Export: Adobe After Effects (.jsx)',
23
'description': 'Export cameras, selected objects & camera solution 3D Markers to Adobe After Effects CS3 and above',
24
'author': 'Bartek Skorupa',
27
'location': 'File > Export > Adobe After Effects (.jsx)',
29
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/"\
30
"Scripts/Import-Export/Adobe_After_Effects",
31
"tracker_url": "https://projects.blender.org/tracker/index.php?"\
32
"func=detail&aid=29858",
33
'category': 'Import-Export',
39
from math import degrees
40
from mathutils import Matrix
43
# create list of static blender's data
44
def get_comp_data(context):
46
aspect_x = scene.render.pixel_aspect_x
47
aspect_y = scene.render.pixel_aspect_y
48
aspect = aspect_x / aspect_y
49
start = scene.frame_start
51
active_cam_frames = get_active_cam_for_each_frame(scene, start, end)
52
fps = scene.render.fps
56
'width': scene.render.resolution_x,
57
'height': scene.render.resolution_y,
62
'duration': (end - start + 1.0) / fps,
63
'active_cam_frames': active_cam_frames,
64
'curframe': scene.frame_current,
68
# create list of active camera for each frame in case active camera is set by markers
69
def get_active_cam_for_each_frame(scene, start, end):
70
active_cam_frames = []
72
markers = scene.timeline_markers
74
for marker in markers:
76
sorted_markers.append([marker.frame, marker])
77
sorted_markers = sorted(sorted_markers)
80
for frame in range(start, end + 1):
81
for m, marker in enumerate(sorted_markers):
84
active_cam_frames.append(sorted_markers[m - 1][1].camera)
86
active_cam_frames.append(marker[1].camera)
88
elif m == len(sorted_markers) - 1:
89
active_cam_frames.append(marker[1].camera)
90
if not active_cam_frames:
92
# in this case active_cam_frames array will have legth of 1. This will indicate that there is only one active cam in all frames
93
active_cam_frames.append(scene.camera)
95
return(active_cam_frames)
98
# create managable list of selected objects
99
def get_selected(context):
100
cameras = [] # list of selected cameras
101
solids = [] # list of all selected meshes that can be exported as AE's solids
102
lights = [] # list of all selected lamps that can be exported as AE's lights
103
nulls = [] # list of all selected objects exept cameras (will be used to create nulls in AE)
104
obs = context.selected_objects
107
if ob.type == 'CAMERA':
108
cameras.append([ob, convert_name(ob.name)])
111
# not ready yet. is_plane(object) returns False in all cases. This is temporary
112
solids.append([ob, convert_name(ob.name)])
114
elif ob.type == 'LAMP':
115
lights.append([ob, ob.data.type + convert_name(ob.name)]) # Type of lamp added to name
118
nulls.append([ob, convert_name(ob.name)])
130
# check if object is plane and can be exported as AE's solid
131
def is_plane(object):
132
# work in progress. Not ready yet
136
# convert names of objects to avoid errors in AE.
137
def convert_name(name):
140
# Digits are not allowed at beginning of AE vars names.
141
# This section is commented, as "_" is added at beginning of names anyway.
142
# Placeholder for this name modification is left so that it's not ignored if needed
143
if name[0].isdigit():
146
name = bpy.path.clean_name(name)
147
name = name.replace("-", "_")
152
# get object's blender's location rotation and scale and return AE's Position, Rotation/Orientation and scale
153
# this function will be called for every object for every frame
154
def convert_transform_matrix(matrix, width, height, aspect, x_rot_correction=False):
156
# get blender transform data for ob
157
b_loc = matrix.to_translation()
158
b_rot = matrix.to_euler('ZYX') # ZYX euler matches AE's orientation and allows to use x_rot_correction
159
b_scale = matrix.to_scale()
161
# convert to AE Position Rotation and Scale
162
# Axes in AE are different. AE's X is blender's X, AE's Y is negative Blender's Z, AE's Z is Blender's Y
163
x = (b_loc.x * 100.0) / aspect + width / 2.0 # calculate AE's X position
164
y = (-b_loc.z * 100.0) + (height / 2.0) # calculate AE's Y position
165
z = b_loc.y * 100.0 # calculate AE's Z position
166
# Convert rotations to match AE's orientation.
167
rx = degrees(b_rot.x) # if not x_rot_correction - AE's X orientation = blender's X rotation if 'ZYX' euler.
168
ry = -degrees(b_rot.y) # AE's Y orientation is negative blender's Y rotation if 'ZYX' euler
169
rz = -degrees(b_rot.z) # AE's Z orientation is negative blender's Z rotation if 'ZYX' euler
171
rx -= 90.0 # In blender - ob of zero rotation lay on floor. In AE layer of zero orientation "stands"
172
# Convert scale to AE scale
173
sx = b_scale.x * 100.0 # scale of 1.0 is 100% in AE
174
sy = b_scale.z * 100.0 # scale of 1.0 is 100% in AE
175
sz = b_scale.y * 100.0 # scale of 1.0 is 100% in AE
177
return x, y, z, rx, ry, rz, sx, sy, sz
179
# get camera's lens and convert to AE's "zoom" value in pixels
180
# this function will be called for every camera for every frame
183
# AE's lens is defined by "zoom" in pixels. Zoom determines focal angle or focal length.
185
# ZOOM VALUE CALCULATIONS:
188
# - sensor width (camera.data.sensor_width)
189
# - sensor height (camera.data.sensor_height)
190
# - sensor fit (camera.data.sensor_fit)
191
# - lens (blender's lens in mm)
192
# - width (width of the composition/scene in pixels)
193
# - height (height of the composition/scene in pixels)
194
# - PAR (pixel aspect ratio)
196
# Calculations are made using sensor's size and scene/comp dimension (width or height).
197
# If camera.sensor_fit is set to 'AUTO' or 'HORIZONTAL' - sensor = camera.data.sensor_width, dimension = width.
198
# If camera.sensor_fit is set to 'VERTICAL' - sensor = camera.data.sensor_height, dimension = height
200
# zoom can be calculated using simple proportions.
218
# zoom / dimension = lens / sensor =>
219
# zoom = lens * dimension / sensor
221
# above is true if square pixels are used. If not - aspect compensation is needed, so final formula is:
222
# zoom = lens * dimension / sensor * aspect
225
def convert_lens(camera, width, height, aspect):
226
if camera.data.sensor_fit == 'VERTICAL':
227
sensor = camera.data.sensor_height
230
sensor = camera.data.sensor_width
233
zoom = camera.data.lens * dimension / sensor * aspect
237
# convert object bundle's matrix. Not ready yet. Temporarily not active
238
#def get_ob_bundle_matrix_world(cam_matrix_world, bundle_matrix):
239
# matrix = cam_matrix_basis
243
# jsx script for AE creation
244
def write_jsx_file(file, data, selection, include_animation, include_active_cam, include_selected_cams, include_selected_objects, include_cam_bundles):
246
print("\n---------------------------\n- Export to After Effects -\n---------------------------")
247
# store the current frame to restore it at the end of export
248
curframe = data['curframe']
249
# create array which will contain all keyframes values
253
'solids': {}, # not ready yet
257
'bundles_ob': {}, # not ready yet
260
# create structure for active camera/cameras
262
if include_active_cam and data['active_cam_frames'] != []:
263
# check if more that one active cam exist (true if active cams set by markers)
264
if len(data['active_cam_frames']) is 1:
265
name_ae = convert_name(data['active_cam_frames'][0].name) # take name of the only active camera in scene
267
name_ae = 'Active_Camera'
268
active_cam_name = name_ae # store name to be used when creating keyframes for active cam.
269
js_data['cameras'][name_ae] = {
271
'position_static': '',
272
'position_anim': False,
274
'orientation_static': '',
275
'orientation_anim': False,
281
# create camera structure for selected cameras
282
if include_selected_cams:
283
for i, cam in enumerate(selection['cameras']): # more than one camera can be selected
284
if cam[1] != active_cam_name:
285
name_ae = selection['cameras'][i][1]
286
js_data['cameras'][name_ae] = {
288
'position_static': '',
289
'position_anim': False,
291
'orientation_static': '',
292
'orientation_anim': False,
298
# create structure for solids. Not ready yet. Temporarily not active
299
for i, obj in enumerate(selection['solids']):
300
name_ae = selection['solids'][i][1]
301
js_data['solids'][name_ae] = {
308
# create structure for lights
309
for i, obj in enumerate(selection['lights']):
310
if include_selected_objects:
311
name_ae = selection['lights'][i][1]
312
js_data['lights'][name_ae] = {
313
'type': selection['lights'][i][0].data.type,
316
'energy_anim': False,
318
'cone_angle_static': '',
319
'cone_angle_anim': False,
321
'cone_feather_static': '',
322
'cone_feather_anim': False,
327
'position_static': '',
328
'position_anim': False,
330
'orientation_static': '',
331
'orientation_anim': False,
334
# create structure for nulls
335
for i, obj in enumerate(selection['nulls']): # nulls representing blender's obs except cameras, lamps and solids
336
if include_selected_objects:
337
name_ae = selection['nulls'][i][1]
338
js_data['nulls'][name_ae] = {
340
'position_static': '',
341
'position_anim': False,
343
'orientation_static': '',
344
'orientation_anim': False,
350
# create structure for cam bundles including positions (cam bundles don't move)
351
if include_cam_bundles:
352
# go through each selected camera and active cameras
355
if include_active_cam:
356
active_cams = data['active_cam_frames']
357
if include_selected_cams:
358
for cam in selection['cameras']:
359
selected_cams.append(cam[0])
360
# list of cameras that will be checked for 'CAMERA SOLVER'
361
cams = list(set.union(set(selected_cams), set(active_cams)))
364
# go through each constraints of this camera
365
for constraint in cam.constraints:
366
# does the camera have a Camera Solver constraint
367
if constraint.type == 'CAMERA_SOLVER':
368
# Which movie clip does it use
369
if constraint.use_active_clip:
370
clip = data['scn'].active_clip
372
clip = constraint.clip
374
# go through each tracking point
375
for track in clip.tracking.tracks:
376
# Does this tracking point have a bundle (has its 3D position been solved)
378
# get the name of the tracker
379
name_ae = convert_name(str(cam.name) + '__' + str(track.name))
380
js_data['bundles_cam'][name_ae] = {
383
# bundles are in camera space. Transpose to world space
384
matrix = Matrix.Translation(cam.matrix_basis.copy() * track.bundle)
385
# convert the position into AE space
386
ae_transform = convert_transform_matrix(matrix, data['width'], data['height'], data['aspect'], x_rot_correction=False)
387
js_data['bundles_cam'][name_ae]['position'] += '[%f,%f,%f],' % (ae_transform[0], ae_transform[1], ae_transform[2])
389
# get all keyframes for each object and store in dico
390
if include_animation:
391
end = data['end'] + 1
393
end = data['start'] + 1
394
for frame in range(data['start'], end):
395
print("working on frame: " + str(frame))
396
data['scn'].frame_set(frame)
398
# get time for this loop
399
js_data['times'] += '%f ,' % ((frame - data['start']) / data['fps'])
401
# keyframes for active camera/cameras
402
if include_active_cam and data['active_cam_frames'] != []:
403
if len(data['active_cam_frames']) == 1:
406
cur_cam_index = frame - data['start']
407
active_cam = data['active_cam_frames'][cur_cam_index]
409
name_ae = active_cam_name
410
# convert cam transform properties to AE space
411
ae_transform = convert_transform_matrix(active_cam.matrix_world.copy(), data['width'], data['height'], data['aspect'], x_rot_correction=True)
412
# convert Blender's lens to AE's zoom in pixels
413
zoom = convert_lens(active_cam, data['width'], data['height'], data['aspect'])
414
# store all values in dico
415
position = '[%f,%f,%f],' % (ae_transform[0], ae_transform[1], ae_transform[2])
416
orientation = '[%f,%f,%f],' % (ae_transform[3], ae_transform[4], ae_transform[5])
417
zoom = '%f,' % (zoom)
418
js_data['cameras'][name_ae]['position'] += position
419
js_data['cameras'][name_ae]['orientation'] += orientation
420
js_data['cameras'][name_ae]['zoom'] += zoom
421
# Check if properties change values compared to previous frame
422
# If property don't change through out the whole animation - keyframes won't be added
423
if frame != data['start']:
424
if position != js_data['cameras'][name_ae]['position_static']:
425
js_data['cameras'][name_ae]['position_anim'] = True
426
if orientation != js_data['cameras'][name_ae]['orientation_static']:
427
js_data['cameras'][name_ae]['orientation_anim'] = True
428
if zoom != js_data['cameras'][name_ae]['zoom_static']:
429
js_data['cameras'][name_ae]['zoom_anim'] = True
430
js_data['cameras'][name_ae]['position_static'] = position
431
js_data['cameras'][name_ae]['orientation_static'] = orientation
432
js_data['cameras'][name_ae]['zoom_static'] = zoom
434
# keyframes for selected cameras
435
if include_selected_cams:
436
for i, cam in enumerate(selection['cameras']):
437
if cam[1] != active_cam_name:
439
name_ae = selection['cameras'][i][1]
440
# convert cam transform properties to AE space
441
ae_transform = convert_transform_matrix(cam[0].matrix_world.copy(), data['width'], data['height'], data['aspect'], x_rot_correction=True)
442
# convert Blender's lens to AE's zoom in pixels
443
zoom = convert_lens(cam[0], data['width'], data['height'], data['aspect'])
444
# store all values in dico
445
position = '[%f,%f,%f],' % (ae_transform[0], ae_transform[1], ae_transform[2])
446
orientation = '[%f,%f,%f],' % (ae_transform[3], ae_transform[4], ae_transform[5])
447
zoom = '%f,' % (zoom)
448
js_data['cameras'][name_ae]['position'] += position
449
js_data['cameras'][name_ae]['orientation'] += orientation
450
js_data['cameras'][name_ae]['zoom'] += zoom
451
# Check if properties change values compared to previous frame
452
# If property don't change through out the whole animation - keyframes won't be added
453
if frame != data['start']:
454
if position != js_data['cameras'][name_ae]['position_static']:
455
js_data['cameras'][name_ae]['position_anim'] = True
456
if orientation != js_data['cameras'][name_ae]['orientation_static']:
457
js_data['cameras'][name_ae]['orientation_anim'] = True
458
if zoom != js_data['cameras'][name_ae]['zoom_static']:
459
js_data['cameras'][name_ae]['zoom_anim'] = True
460
js_data['cameras'][name_ae]['position_static'] = position
461
js_data['cameras'][name_ae]['orientation_static'] = orientation
462
js_data['cameras'][name_ae]['zoom_static'] = zoom
465
# keyframes for all solids. Not ready yet. Temporarily not active
466
for i, ob in enumerate(selection['solids']):
468
name_ae = selection['solids'][i][1]
469
#convert ob position to AE space
472
# keyframes for all lights.
473
if include_selected_objects:
474
for i, ob in enumerate(selection['lights']):
476
name_ae = selection['lights'][i][1]
477
type = selection['lights'][i][0].data.type
478
# convert ob transform properties to AE space
479
ae_transform = convert_transform_matrix(ob[0].matrix_world.copy(), data['width'], data['height'], data['aspect'], x_rot_correction=True)
480
color = ob[0].data.color
481
# store all values in dico
482
position = '[%f,%f,%f],' % (ae_transform[0], ae_transform[1], ae_transform[2])
483
orientation = '[%f,%f,%f],' % (ae_transform[3], ae_transform[4], ae_transform[5])
484
energy = '[%f],' % (ob[0].data.energy * 100.0)
485
color = '[%f,%f,%f],' % (color[0], color[1], color[2])
486
js_data['lights'][name_ae]['position'] += position
487
js_data['lights'][name_ae]['orientation'] += orientation
488
js_data['lights'][name_ae]['energy'] += energy
489
js_data['lights'][name_ae]['color'] += color
490
# Check if properties change values compared to previous frame
491
# If property don't change through out the whole animation - keyframes won't be added
492
if frame != data['start']:
493
if position != js_data['lights'][name_ae]['position_static']:
494
js_data['lights'][name_ae]['position_anim'] = True
495
if orientation != js_data['lights'][name_ae]['orientation_static']:
496
js_data['lights'][name_ae]['orientation_anim'] = True
497
if energy != js_data['lights'][name_ae]['energy_static']:
498
js_data['lights'][name_ae]['energy_anim'] = True
499
if color != js_data['lights'][name_ae]['color_static']:
500
js_data['lights'][name_ae]['color_anim'] = True
501
js_data['lights'][name_ae]['position_static'] = position
502
js_data['lights'][name_ae]['orientation_static'] = orientation
503
js_data['lights'][name_ae]['energy_static'] = energy
504
js_data['lights'][name_ae]['color_static'] = color
506
cone_angle = '[%f],' % (degrees(ob[0].data.spot_size))
507
cone_feather = '[%f],' % (ob[0].data.spot_blend * 100.0)
508
js_data['lights'][name_ae]['cone_angle'] += cone_angle
509
js_data['lights'][name_ae]['cone_feather'] += cone_feather
510
# Check if properties change values compared to previous frame
511
# If property don't change through out the whole animation - keyframes won't be added
512
if frame != data['start']:
513
if cone_angle != js_data['lights'][name_ae]['cone_angle_static']:
514
js_data['lights'][name_ae]['cone_angle_anim'] = True
515
if orientation != js_data['lights'][name_ae]['cone_feather_static']:
516
js_data['lights'][name_ae]['cone_feather_anim'] = True
517
js_data['lights'][name_ae]['cone_angle_static'] = cone_angle
518
js_data['lights'][name_ae]['cone_feather_static'] = cone_feather
520
# keyframes for all nulls
521
if include_selected_objects:
522
for i, ob in enumerate(selection['nulls']):
524
name_ae = selection['nulls'][i][1]
525
# convert ob transform properties to AE space
526
ae_transform = convert_transform_matrix(ob[0].matrix_world.copy(), data['width'], data['height'], data['aspect'], x_rot_correction=True)
527
# store all values in dico
528
position = '[%f,%f,%f],' % (ae_transform[0], ae_transform[1], ae_transform[2])
529
orientation = '[%f,%f,%f],' % (ae_transform[3], ae_transform[4], ae_transform[5])
530
scale = '[%f,%f,%f],' % (ae_transform[6], ae_transform[7], ae_transform[8])
531
js_data['nulls'][name_ae]['position'] += position
532
js_data['nulls'][name_ae]['orientation'] += orientation
533
js_data['nulls'][name_ae]['scale'] += scale
534
# Check if properties change values compared to previous frame
535
# If property don't change through out the whole animation - keyframes won't be added
536
if frame != data['start']:
537
if position != js_data['nulls'][name_ae]['position_static']:
538
js_data['nulls'][name_ae]['position_anim'] = True
539
if orientation != js_data['nulls'][name_ae]['orientation_static']:
540
js_data['nulls'][name_ae]['orientation_anim'] = True
541
if scale != js_data['nulls'][name_ae]['scale_static']:
542
js_data['nulls'][name_ae]['scale_anim'] = True
543
js_data['nulls'][name_ae]['position_static'] = position
544
js_data['nulls'][name_ae]['orientation_static'] = orientation
545
js_data['nulls'][name_ae]['scale_static'] = scale
547
# keyframes for all object bundles. Not ready yet.
552
# ---- write JSX file
553
jsx_file = open(file, 'w')
555
# make the jsx executable in After Effects (enable double click on jsx)
556
jsx_file.write('#target AfterEffects\n\n')
558
jsx_file.write('/**************************************\n')
559
jsx_file.write('Scene : %s\n' % data['scn'].name)
560
jsx_file.write('Resolution : %i x %i\n' % (data['width'], data['height']))
561
jsx_file.write('Duration : %f\n' % (data['duration']))
562
jsx_file.write('FPS : %f\n' % (data['fps']))
563
jsx_file.write('Date : %s\n' % datetime.datetime.now())
564
jsx_file.write('Exported with io_export_after_effects.py\n')
565
jsx_file.write('**************************************/\n\n\n\n')
568
jsx_file.write("function compFromBlender(){\n")
570
jsx_file.write('\nvar compName = prompt("Blender Comp\'s Name \\nEnter Name of newly created Composition","BlendComp","Composition\'s Name");\n')
571
jsx_file.write('if (compName){') # Continue only if comp name is given. If not - terminate
572
jsx_file.write('\nvar newComp = app.project.items.addComp(compName, %i, %i, %f, %f, %i);' %
573
(data['width'], data['height'], data['aspect'], data['duration'], data['fps']))
574
jsx_file.write('\nnewComp.displayStartTime = %f;\n\n\n' % ((data['start'] + 1.0) / data['fps']))
576
# create camera bundles (nulls)
577
jsx_file.write('// ************** CAMERA 3D MARKERS **************\n\n\n')
578
for i, obj in enumerate(js_data['bundles_cam']):
580
jsx_file.write('var %s = newComp.layers.addNull();\n' % (name_ae))
581
jsx_file.write('%s.threeDLayer = true;\n' % name_ae)
582
jsx_file.write('%s.source.name = "%s";\n' % (name_ae, name_ae))
583
jsx_file.write('%s.property("position").setValue(%s);\n\n\n' % (name_ae, js_data['bundles_cam'][obj]['position']))
585
# create object bundles (not ready yet)
587
# create objects (nulls)
588
jsx_file.write('// ************** OBJECTS **************\n\n\n')
589
for i, obj in enumerate(js_data['nulls']):
591
jsx_file.write('var %s = newComp.layers.addNull();\n' % (name_ae))
592
jsx_file.write('%s.threeDLayer = true;\n' % name_ae)
593
jsx_file.write('%s.source.name = "%s";\n' % (name_ae, name_ae))
594
# Set values of properties, add kyeframes only where needed
595
if include_animation and js_data['nulls'][name_ae]['position_anim']:
596
jsx_file.write('%s.property("position").setValuesAtTimes([%s],[%s]);\n' % (name_ae, js_data['times'], js_data['nulls'][obj]['position']))
598
jsx_file.write('%s.property("position").setValue(%s);\n' % (name_ae, js_data['nulls'][obj]['position_static']))
599
if include_animation and js_data['nulls'][name_ae]['orientation_anim']:
600
jsx_file.write('%s.property("orientation").setValuesAtTimes([%s],[%s]);\n' % (name_ae, js_data['times'], js_data['nulls'][obj]['orientation']))
602
jsx_file.write('%s.property("orientation").setValue(%s);\n' % (name_ae, js_data['nulls'][obj]['orientation_static']))
603
if include_animation and js_data['nulls'][name_ae]['scale_anim']:
604
jsx_file.write('%s.property("scale").setValuesAtTimes([%s],[%s]);\n\n\n' % (name_ae, js_data['times'], js_data['nulls'][obj]['scale']))
606
jsx_file.write('%s.property("scale").setValue(%s);\n\n\n' % (name_ae, js_data['nulls'][obj]['scale_static']))
607
# create solids (not ready yet)
610
jsx_file.write('// ************** LIGHTS **************\n\n\n')
611
for i, obj in enumerate(js_data['lights']):
613
jsx_file.write('var %s = newComp.layers.addLight("%s", [0.0, 0.0]);\n' % (name_ae, name_ae))
614
jsx_file.write('%s.autoOrient = AutoOrientType.NO_AUTO_ORIENT;\n' % name_ae)
615
# Set values of properties, add kyeframes only where needed
616
if include_animation and js_data['lights'][name_ae]['position_anim']:
617
jsx_file.write('%s.property("position").setValuesAtTimes([%s],[%s]);\n' % (name_ae, js_data['times'], js_data['lights'][obj]['position']))
619
jsx_file.write('%s.property("position").setValue(%s);\n' % (name_ae, js_data['lights'][obj]['position_static']))
620
if include_animation and js_data['lights'][name_ae]['orientation_anim']:
621
jsx_file.write('%s.property("orientation").setValuesAtTimes([%s],[%s]);\n' % (name_ae, js_data['times'], js_data['lights'][obj]['orientation']))
623
jsx_file.write('%s.property("orientation").setValue(%s);\n' % (name_ae, js_data['lights'][obj]['orientation_static']))
624
if include_animation and js_data['lights'][name_ae]['energy_anim']:
625
jsx_file.write('%s.property("intensity").setValuesAtTimes([%s],[%s]);\n' % (name_ae, js_data['times'], js_data['lights'][obj]['energy']))
627
jsx_file.write('%s.property("intensity").setValue(%s);\n' % (name_ae, js_data['lights'][obj]['energy_static']))
628
if include_animation and js_data['lights'][name_ae]['color_anim']:
629
jsx_file.write('%s.property("Color").setValuesAtTimes([%s],[%s]);\n' % (name_ae, js_data['times'], js_data['lights'][obj]['color']))
631
jsx_file.write('%s.property("Color").setValue(%s);\n' % (name_ae, js_data['lights'][obj]['color_static']))
632
if js_data['lights'][obj]['type'] == 'SPOT':
633
if include_animation and js_data['lights'][name_ae]['cone_angle_anim']:
634
jsx_file.write('%s.property("Cone Angle").setValuesAtTimes([%s],[%s]);\n' % (name_ae, js_data['times'], js_data['lights'][obj]['cone_angle']))
636
jsx_file.write('%s.property("Cone Angle").setValue(%s);\n' % (name_ae, js_data['lights'][obj]['cone_angle_static']))
637
if include_animation and js_data['lights'][name_ae]['cone_feather_anim']:
638
jsx_file.write('%s.property("Cone Feather").setValuesAtTimes([%s],[%s]);\n' % (name_ae, js_data['times'], js_data['lights'][obj]['cone_feather']))
640
jsx_file.write('%s.property("Cone Feather").setValue(%s);\n' % (name_ae, js_data['lights'][obj]['cone_feather_static']))
641
jsx_file.write('\n\n')
644
jsx_file.write('// ************** CAMERAS **************\n\n\n')
645
for i, cam in enumerate(js_data['cameras']): # more than one camera can be selected
647
jsx_file.write('var %s = newComp.layers.addCamera("%s",[0,0]);\n' % (name_ae, name_ae))
648
jsx_file.write('%s.autoOrient = AutoOrientType.NO_AUTO_ORIENT;\n' % name_ae)
649
# Set values of properties, add kyeframes only where needed
650
if include_animation and js_data['cameras'][name_ae]['position_anim']:
651
jsx_file.write('%s.property("position").setValuesAtTimes([%s],[%s]);\n' % (name_ae, js_data['times'], js_data['cameras'][cam]['position']))
653
jsx_file.write('%s.property("position").setValue(%s);\n' % (name_ae, js_data['cameras'][cam]['position_static']))
654
if include_animation and js_data['cameras'][name_ae]['orientation_anim']:
655
jsx_file.write('%s.property("orientation").setValuesAtTimes([%s],[%s]);\n' % (name_ae, js_data['times'], js_data['cameras'][cam]['orientation']))
657
jsx_file.write('%s.property("orientation").setValue(%s);\n' % (name_ae, js_data['cameras'][cam]['orientation_static']))
658
if include_animation and js_data['cameras'][name_ae]['zoom_anim']:
659
jsx_file.write('%s.property("zoom").setValuesAtTimes([%s],[%s]);\n\n\n' % (name_ae, js_data['times'], js_data['cameras'][cam]['zoom']))
661
jsx_file.write('%s.property("zoom").setValue(%s);\n\n\n' % (name_ae, js_data['cameras'][cam]['zoom_static']))
663
# Exit import if no comp name given
664
jsx_file.write('\n}else{alert ("Exit Import Blender animation data \\nNo Comp\'s name has been chosen","EXIT")};')
666
jsx_file.write("}\n\n\n")
667
# Execute function. Wrap in "undo group" for easy undoing import process
668
jsx_file.write('app.beginUndoGroup("Import Blender animation data");\n')
669
jsx_file.write('compFromBlender();\n') # execute function
670
jsx_file.write('app.endUndoGroup();\n\n\n')
673
data['scn'].frame_set(curframe) # set current frame of animation in blender to state before export
675
##########################################
677
##########################################
680
def main(file, context, include_animation, include_active_cam, include_selected_cams, include_selected_objects, include_cam_bundles):
681
data = get_comp_data(context)
682
selection = get_selected(context)
683
write_jsx_file(file, data, selection, include_animation, include_active_cam, include_selected_cams, include_selected_objects, include_cam_bundles)
684
print ("\nExport to After Effects Completed")
687
##########################################
688
# ExportJsx class register/unregister
689
##########################################
691
from bpy_extras.io_utils import ExportHelper
692
from bpy.props import StringProperty, BoolProperty
695
class ExportJsx(bpy.types.Operator, ExportHelper):
696
'''Export selected cameras and objects animation to After Effects'''
697
bl_idname = "export.jsx"
698
bl_label = "Export to Adobe After Effects"
699
filename_ext = ".jsx"
700
filter_glob = StringProperty(default="*.jsx", options={'HIDDEN'})
702
include_animation = BoolProperty(
704
description="Animate Exported Cameras and Objects",
707
include_active_cam = BoolProperty(
708
name="Active Camera",
709
description="Include Active Camera",
712
include_selected_cams = BoolProperty(
713
name="Selected Cameras",
714
description="Add Selected Cameras",
717
include_selected_objects = BoolProperty(
718
name="Selected Objects",
719
description="Export Selected Objects",
722
include_cam_bundles = BoolProperty(
723
name="Camera 3D Markers",
724
description="Include 3D Markers of Camera Motion Solution for selected cameras",
727
# include_ob_bundles = BoolProperty(
728
# name="Objects 3D Markers",
729
# description="Include 3D Markers of Object Motion Solution for selected cameras",
733
def draw(self, context):
737
box.label('Animation:')
738
box.prop(self, 'include_animation')
739
box.label('Include Cameras and Objects:')
740
box.prop(self, 'include_active_cam')
741
box.prop(self, 'include_selected_cams')
742
box.prop(self, 'include_selected_objects')
743
box.label("Include Tracking Data:")
744
box.prop(self, 'include_cam_bundles')
745
# box.prop(self, 'include_ob_bundles')
748
def poll(cls, context):
749
active = context.active_object
750
selected = context.selected_objects
751
camera = context.scene.camera
752
ok = selected or camera
755
def execute(self, context):
756
return main(self.filepath, context, self.include_animation, self.include_active_cam, self.include_selected_cams, self.include_selected_objects, self.include_cam_bundles)
759
def menu_func(self, context):
760
self.layout.operator(ExportJsx.bl_idname, text="Adobe After Effects (.jsx)")
764
bpy.utils.register_class(ExportJsx)
765
bpy.types.INFO_MT_file_export.append(menu_func)
769
bpy.utils.unregister_class(ExportJsx)
770
bpy.types.INFO_MT_file_export.remove(menu_func)
772
if __name__ == "__main__":