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 "
24 "3D Markers to Adobe After Effects CS3 and above",
25 "author": "Bartek Skorupa",
27 "blender": (2, 79, 0),
28 "location": "File > Export > Adobe After Effects (.jsx)",
30 "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
31 "Scripts/Import-Export/Adobe_After_Effects",
32 "category": "Import-Export",
38 from math
import degrees
, floor
39 from mathutils
import Matrix
42 # create list of static blender's data
43 def get_comp_data(context
):
45 aspect_x
= scene
.render
.pixel_aspect_x
46 aspect_y
= scene
.render
.pixel_aspect_y
47 aspect
= aspect_x
/ aspect_y
48 start
= scene
.frame_start
50 active_cam_frames
= get_active_cam_for_each_frame(scene
, start
, end
)
51 fps
= floor(scene
.render
.fps
/ (scene
.render
.fps_base
) * 1000.0) / 1000.0
55 'width': scene
.render
.resolution_x
,
56 'height': scene
.render
.resolution_y
,
61 'duration': (end
- start
+ 1.0) / fps
,
62 'active_cam_frames': active_cam_frames
,
63 'curframe': scene
.frame_current
,
67 # create list of active camera for each frame in case active camera is set by markers
68 def get_active_cam_for_each_frame(scene
, start
, end
):
69 active_cam_frames
= []
71 markers
= scene
.timeline_markers
73 for marker
in markers
:
75 sorted_markers
.append([marker
.frame
, marker
])
76 sorted_markers
= sorted(sorted_markers
)
79 for frame
in range(start
, end
+ 1):
80 for m
, marker
in enumerate(sorted_markers
):
83 active_cam_frames
.append(sorted_markers
[m
- 1][1].camera
)
85 active_cam_frames
.append(marker
[1].camera
)
87 elif m
== len(sorted_markers
) - 1:
88 active_cam_frames
.append(marker
[1].camera
)
89 if not active_cam_frames
:
91 # 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
92 active_cam_frames
.append(scene
.camera
)
94 return(active_cam_frames
)
97 # create managable list of selected objects
98 def get_selected(context
):
99 cameras
= [] # list of selected cameras
100 solids
= [] # list of all selected meshes that can be exported as AE's solids
101 lights
= [] # list of all selected lamps that can be exported as AE's lights
102 nulls
= [] # list of all selected objects exept cameras (will be used to create nulls in AE)
103 obs
= context
.selected_objects
106 if ob
.type == 'CAMERA':
107 cameras
.append([ob
, convert_name(ob
.name
)])
110 # not ready yet. is_plane(object) returns False in all cases. This is temporary
111 solids
.append([ob
, convert_name(ob
.name
)])
113 elif ob
.type == 'LIGHT':
114 lights
.append([ob
, ob
.data
.type + convert_name(ob
.name
)]) # Type of lamp added to name
117 nulls
.append([ob
, convert_name(ob
.name
)])
129 # check if object is plane and can be exported as AE's solid
130 def is_plane(object):
131 # work in progress. Not ready yet
135 # convert names of objects to avoid errors in AE.
136 def convert_name(name
):
139 # Digits are not allowed at beginning of AE vars names.
140 # This section is commented, as "_" is added at beginning of names anyway.
141 # Placeholder for this name modification is left so that it's not ignored if needed
142 if name[0].isdigit():
145 name
= bpy
.path
.clean_name(name
)
146 name
= name
.replace("-", "_")
151 # get object's blender's location rotation and scale and return AE's Position, Rotation/Orientation and scale
152 # this function will be called for every object for every frame
153 def convert_transform_matrix(matrix
, width
, height
, aspect
, x_rot_correction
=False, ae_size
=100.0):
155 # get blender transform data for ob
156 b_loc
= matrix
.to_translation()
157 b_rot
= matrix
.to_euler('ZYX') # ZYX euler matches AE's orientation and allows to use x_rot_correction
158 b_scale
= matrix
.to_scale()
160 # convert to AE Position Rotation and Scale
161 # 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
162 x
= (b_loc
.x
* ae_size
) / aspect
+ width
/ 2.0 # calculate AE's X position
163 y
= (-b_loc
.z
* ae_size
) + (height
/ 2.0) # calculate AE's Y position
164 z
= b_loc
.y
* ae_size
# calculate AE's Z position
165 # Convert rotations to match AE's orientation.
166 rx
= degrees(b_rot
.x
) # if not x_rot_correction - AE's X orientation = blender's X rotation if 'ZYX' euler.
167 ry
= -degrees(b_rot
.y
) # AE's Y orientation is negative blender's Y rotation if 'ZYX' euler
168 rz
= -degrees(b_rot
.z
) # AE's Z orientation is negative blender's Z rotation if 'ZYX' euler
170 rx
-= 90.0 # In blender - ob of zero rotation lay on floor. In AE layer of zero orientation "stands"
171 # Convert scale to AE scale
172 sx
= b_scale
.x
* 100.0 # scale of 1.0 is 100% in AE
173 sy
= b_scale
.z
* 100.0 # scale of 1.0 is 100% in AE
174 sz
= b_scale
.y
* 100.0 # scale of 1.0 is 100% in AE
176 return x
, y
, z
, rx
, ry
, rz
, sx
, sy
, sz
178 # get camera's lens and convert to AE's "zoom" value in pixels
179 # this function will be called for every camera for every frame
182 # AE's lens is defined by "zoom" in pixels. Zoom determines focal angle or focal length.
184 # ZOOM VALUE CALCULATIONS:
187 # - sensor width (camera.data.sensor_width)
188 # - sensor height (camera.data.sensor_height)
189 # - sensor fit (camera.data.sensor_fit)
190 # - lens (blender's lens in mm)
191 # - width (width of the composition/scene in pixels)
192 # - height (height of the composition/scene in pixels)
193 # - PAR (pixel aspect ratio)
195 # Calculations are made using sensor's size and scene/comp dimension (width or height).
196 # If camera.sensor_fit is set to 'AUTO' or 'HORIZONTAL' - sensor = camera.data.sensor_width, dimension = width.
197 # If camera.sensor_fit is set to 'VERTICAL' - sensor = camera.data.sensor_height, dimension = height
199 # zoom can be calculated using simple proportions.
217 # zoom / dimension = lens / sensor =>
218 # zoom = lens * dimension / sensor
220 # above is true if square pixels are used. If not - aspect compensation is needed, so final formula is:
221 # zoom = lens * dimension / sensor * aspect
224 def convert_lens(camera
, width
, height
, aspect
):
225 if camera
.data
.sensor_fit
== 'VERTICAL':
226 sensor
= camera
.data
.sensor_height
229 sensor
= camera
.data
.sensor_width
232 zoom
= camera
.data
.lens
* dimension
/ sensor
* aspect
236 # convert object bundle's matrix. Not ready yet. Temporarily not active
237 #def get_ob_bundle_matrix_world(cam_matrix_world, bundle_matrix):
238 # matrix = cam_matrix_basis
242 # jsx script for AE creation
243 def write_jsx_file(file, data
, selection
, include_animation
, include_active_cam
, include_selected_cams
, include_selected_objects
, include_cam_bundles
, ae_size
):
245 print("\n---------------------------\n- Export to After Effects -\n---------------------------")
246 # store the current frame to restore it at the end of export
247 curframe
= data
['curframe']
248 # create array which will contain all keyframes values
252 'solids': {}, # not ready yet
256 'bundles_ob': {}, # not ready yet
259 # create structure for active camera/cameras
261 if include_active_cam
and data
['active_cam_frames'] != []:
262 # check if more that one active cam exist (true if active cams set by markers)
263 if len(data
['active_cam_frames']) is 1:
264 name_ae
= convert_name(data
['active_cam_frames'][0].name
) # take name of the only active camera in scene
266 name_ae
= 'Active_Camera'
267 active_cam_name
= name_ae
# store name to be used when creating keyframes for active cam.
268 js_data
['cameras'][name_ae
] = {
270 'position_static': '',
271 'position_anim': False,
273 'orientation_static': '',
274 'orientation_anim': False,
280 # create camera structure for selected cameras
281 if include_selected_cams
:
282 for i
, cam
in enumerate(selection
['cameras']): # more than one camera can be selected
283 if cam
[1] != active_cam_name
:
284 name_ae
= selection
['cameras'][i
][1]
285 js_data
['cameras'][name_ae
] = {
287 'position_static': '',
288 'position_anim': False,
290 'orientation_static': '',
291 'orientation_anim': False,
297 # create structure for solids. Not ready yet. Temporarily not active
298 for i, obj in enumerate(selection['solids']):
299 name_ae = selection['solids'][i][1]
300 js_data['solids'][name_ae] = {
307 # create structure for lights
308 for i
, obj
in enumerate(selection
['lights']):
309 if include_selected_objects
:
310 name_ae
= selection
['lights'][i
][1]
311 js_data
['lights'][name_ae
] = {
312 'type': selection
['lights'][i
][0].data
.type,
315 'energy_anim': False,
317 'cone_angle_static': '',
318 'cone_angle_anim': False,
320 'cone_feather_static': '',
321 'cone_feather_anim': False,
326 'position_static': '',
327 'position_anim': False,
329 'orientation_static': '',
330 'orientation_anim': False,
333 # create structure for nulls
334 for i
, obj
in enumerate(selection
['nulls']): # nulls representing blender's obs except cameras, lamps and solids
335 if include_selected_objects
:
336 name_ae
= selection
['nulls'][i
][1]
337 js_data
['nulls'][name_ae
] = {
339 'position_static': '',
340 'position_anim': False,
342 'orientation_static': '',
343 'orientation_anim': False,
349 # create structure for cam bundles including positions (cam bundles don't move)
350 if include_cam_bundles
:
351 # go through each selected camera and active cameras
354 if include_active_cam
:
355 active_cams
= data
['active_cam_frames']
356 if include_selected_cams
:
357 for cam
in selection
['cameras']:
358 selected_cams
.append(cam
[0])
359 # list of cameras that will be checked for 'CAMERA SOLVER'
360 cams
= list(set.union(set(selected_cams
), set(active_cams
)))
363 # go through each constraints of this camera
364 for constraint
in cam
.constraints
:
365 # does the camera have a Camera Solver constraint
366 if constraint
.type == 'CAMERA_SOLVER':
367 # Which movie clip does it use
368 if constraint
.use_active_clip
:
369 clip
= data
['scn'].active_clip
371 clip
= constraint
.clip
373 # go through each tracking point
374 for track
in clip
.tracking
.tracks
:
375 # Does this tracking point have a bundle (has its 3D position been solved)
377 # get the name of the tracker
378 name_ae
= convert_name(str(cam
.name
) + '__' + str(track
.name
))
379 js_data
['bundles_cam'][name_ae
] = {
382 # bundles are in camera space. Transpose to world space
383 matrix
= Matrix
.Translation(cam
.matrix_basis
.copy() * track
.bundle
)
384 # convert the position into AE space
385 ae_transform
= convert_transform_matrix(matrix
, data
['width'], data
['height'], data
['aspect'], False, ae_size
)
386 js_data
['bundles_cam'][name_ae
]['position'] += '[%f,%f,%f],' % (ae_transform
[0], ae_transform
[1], ae_transform
[2])
388 # get all keyframes for each object and store in dico
389 if include_animation
:
390 end
= data
['end'] + 1
392 end
= data
['start'] + 1
393 for frame
in range(data
['start'], end
):
394 print("working on frame: " + str(frame
))
395 data
['scn'].frame_set(frame
)
397 # get time for this loop
398 js_data
['times'] += '%f ,' % ((frame
- data
['start']) / data
['fps'])
400 # keyframes for active camera/cameras
401 if include_active_cam
and data
['active_cam_frames'] != []:
402 if len(data
['active_cam_frames']) == 1:
405 cur_cam_index
= frame
- data
['start']
406 active_cam
= data
['active_cam_frames'][cur_cam_index
]
408 name_ae
= active_cam_name
409 # convert cam transform properties to AE space
410 ae_transform
= convert_transform_matrix(active_cam
.matrix_world
.copy(), data
['width'], data
['height'], data
['aspect'], True, ae_size
)
411 # convert Blender's lens to AE's zoom in pixels
412 zoom
= convert_lens(active_cam
, data
['width'], data
['height'], data
['aspect'])
413 # store all values in dico
414 position
= '[%f,%f,%f],' % (ae_transform
[0], ae_transform
[1], ae_transform
[2])
415 orientation
= '[%f,%f,%f],' % (ae_transform
[3], ae_transform
[4], ae_transform
[5])
416 zoom
= '%f,' % (zoom
)
417 js_data
['cameras'][name_ae
]['position'] += position
418 js_data
['cameras'][name_ae
]['orientation'] += orientation
419 js_data
['cameras'][name_ae
]['zoom'] += zoom
420 # Check if properties change values compared to previous frame
421 # If property don't change through out the whole animation - keyframes won't be added
422 if frame
!= data
['start']:
423 if position
!= js_data
['cameras'][name_ae
]['position_static']:
424 js_data
['cameras'][name_ae
]['position_anim'] = True
425 if orientation
!= js_data
['cameras'][name_ae
]['orientation_static']:
426 js_data
['cameras'][name_ae
]['orientation_anim'] = True
427 if zoom
!= js_data
['cameras'][name_ae
]['zoom_static']:
428 js_data
['cameras'][name_ae
]['zoom_anim'] = True
429 js_data
['cameras'][name_ae
]['position_static'] = position
430 js_data
['cameras'][name_ae
]['orientation_static'] = orientation
431 js_data
['cameras'][name_ae
]['zoom_static'] = zoom
433 # keyframes for selected cameras
434 if include_selected_cams
:
435 for i
, cam
in enumerate(selection
['cameras']):
436 if cam
[1] != active_cam_name
:
438 name_ae
= selection
['cameras'][i
][1]
439 # convert cam transform properties to AE space
440 ae_transform
= convert_transform_matrix(cam
[0].matrix_world
.copy(), data
['width'], data
['height'], data
['aspect'], True, ae_size
)
441 # convert Blender's lens to AE's zoom in pixels
442 zoom
= convert_lens(cam
[0], data
['width'], data
['height'], data
['aspect'])
443 # store all values in dico
444 position
= '[%f,%f,%f],' % (ae_transform
[0], ae_transform
[1], ae_transform
[2])
445 orientation
= '[%f,%f,%f],' % (ae_transform
[3], ae_transform
[4], ae_transform
[5])
446 zoom
= '%f,' % (zoom
)
447 js_data
['cameras'][name_ae
]['position'] += position
448 js_data
['cameras'][name_ae
]['orientation'] += orientation
449 js_data
['cameras'][name_ae
]['zoom'] += zoom
450 # Check if properties change values compared to previous frame
451 # If property don't change through out the whole animation - keyframes won't be added
452 if frame
!= data
['start']:
453 if position
!= js_data
['cameras'][name_ae
]['position_static']:
454 js_data
['cameras'][name_ae
]['position_anim'] = True
455 if orientation
!= js_data
['cameras'][name_ae
]['orientation_static']:
456 js_data
['cameras'][name_ae
]['orientation_anim'] = True
457 if zoom
!= js_data
['cameras'][name_ae
]['zoom_static']:
458 js_data
['cameras'][name_ae
]['zoom_anim'] = True
459 js_data
['cameras'][name_ae
]['position_static'] = position
460 js_data
['cameras'][name_ae
]['orientation_static'] = orientation
461 js_data
['cameras'][name_ae
]['zoom_static'] = zoom
464 # keyframes for all solids. Not ready yet. Temporarily not active
465 for i, ob in enumerate(selection['solids']):
467 name_ae = selection['solids'][i][1]
468 #convert ob position to AE space
471 # keyframes for all lights.
472 if include_selected_objects
:
473 for i
, ob
in enumerate(selection
['lights']):
475 name_ae
= selection
['lights'][i
][1]
476 type = selection
['lights'][i
][0].data
.type
477 # convert ob transform properties to AE space
478 ae_transform
= convert_transform_matrix(ob
[0].matrix_world
.copy(), data
['width'], data
['height'], data
['aspect'], True, ae_size
)
479 color
= ob
[0].data
.color
480 # store all values in dico
481 position
= '[%f,%f,%f],' % (ae_transform
[0], ae_transform
[1], ae_transform
[2])
482 orientation
= '[%f,%f,%f],' % (ae_transform
[3], ae_transform
[4], ae_transform
[5])
483 energy
= '[%f],' % (ob
[0].data
.energy
* 100.0)
484 color
= '[%f,%f,%f],' % (color
[0], color
[1], color
[2])
485 js_data
['lights'][name_ae
]['position'] += position
486 js_data
['lights'][name_ae
]['orientation'] += orientation
487 js_data
['lights'][name_ae
]['energy'] += energy
488 js_data
['lights'][name_ae
]['color'] += color
489 # Check if properties change values compared to previous frame
490 # If property don't change through out the whole animation - keyframes won't be added
491 if frame
!= data
['start']:
492 if position
!= js_data
['lights'][name_ae
]['position_static']:
493 js_data
['lights'][name_ae
]['position_anim'] = True
494 if orientation
!= js_data
['lights'][name_ae
]['orientation_static']:
495 js_data
['lights'][name_ae
]['orientation_anim'] = True
496 if energy
!= js_data
['lights'][name_ae
]['energy_static']:
497 js_data
['lights'][name_ae
]['energy_anim'] = True
498 if color
!= js_data
['lights'][name_ae
]['color_static']:
499 js_data
['lights'][name_ae
]['color_anim'] = True
500 js_data
['lights'][name_ae
]['position_static'] = position
501 js_data
['lights'][name_ae
]['orientation_static'] = orientation
502 js_data
['lights'][name_ae
]['energy_static'] = energy
503 js_data
['lights'][name_ae
]['color_static'] = color
505 cone_angle
= '[%f],' % (degrees(ob
[0].data
.spot_size
))
506 cone_feather
= '[%f],' % (ob
[0].data
.spot_blend
* 100.0)
507 js_data
['lights'][name_ae
]['cone_angle'] += cone_angle
508 js_data
['lights'][name_ae
]['cone_feather'] += cone_feather
509 # Check if properties change values compared to previous frame
510 # If property don't change through out the whole animation - keyframes won't be added
511 if frame
!= data
['start']:
512 if cone_angle
!= js_data
['lights'][name_ae
]['cone_angle_static']:
513 js_data
['lights'][name_ae
]['cone_angle_anim'] = True
514 if orientation
!= js_data
['lights'][name_ae
]['cone_feather_static']:
515 js_data
['lights'][name_ae
]['cone_feather_anim'] = True
516 js_data
['lights'][name_ae
]['cone_angle_static'] = cone_angle
517 js_data
['lights'][name_ae
]['cone_feather_static'] = cone_feather
519 # keyframes for all nulls
520 if include_selected_objects
:
521 for i
, ob
in enumerate(selection
['nulls']):
523 name_ae
= selection
['nulls'][i
][1]
524 # convert ob transform properties to AE space
525 ae_transform
= convert_transform_matrix(ob
[0].matrix_world
.copy(), data
['width'], data
['height'], data
['aspect'], True, ae_size
)
526 # store all values in dico
527 position
= '[%f,%f,%f],' % (ae_transform
[0], ae_transform
[1], ae_transform
[2])
528 orientation
= '[%f,%f,%f],' % (ae_transform
[3], ae_transform
[4], ae_transform
[5])
529 scale
= '[%f,%f,%f],' % (ae_transform
[6], ae_transform
[7], ae_transform
[8])
530 js_data
['nulls'][name_ae
]['position'] += position
531 js_data
['nulls'][name_ae
]['orientation'] += orientation
532 js_data
['nulls'][name_ae
]['scale'] += scale
533 # Check if properties change values compared to previous frame
534 # If property don't change through out the whole animation - keyframes won't be added
535 if frame
!= data
['start']:
536 if position
!= js_data
['nulls'][name_ae
]['position_static']:
537 js_data
['nulls'][name_ae
]['position_anim'] = True
538 if orientation
!= js_data
['nulls'][name_ae
]['orientation_static']:
539 js_data
['nulls'][name_ae
]['orientation_anim'] = True
540 if scale
!= js_data
['nulls'][name_ae
]['scale_static']:
541 js_data
['nulls'][name_ae
]['scale_anim'] = True
542 js_data
['nulls'][name_ae
]['position_static'] = position
543 js_data
['nulls'][name_ae
]['orientation_static'] = orientation
544 js_data
['nulls'][name_ae
]['scale_static'] = scale
546 # keyframes for all object bundles. Not ready yet.
551 # ---- write JSX file
552 jsx_file
= open(file, 'w')
554 # make the jsx executable in After Effects (enable double click on jsx)
555 jsx_file
.write('#target AfterEffects\n\n')
557 jsx_file
.write('/**************************************\n')
558 jsx_file
.write('Scene : %s\n' % data
['scn'].name
)
559 jsx_file
.write('Resolution : %i x %i\n' % (data
['width'], data
['height']))
560 jsx_file
.write('Duration : %f\n' % (data
['duration']))
561 jsx_file
.write('FPS : %f\n' % (data
['fps']))
562 jsx_file
.write('Date : %s\n' % datetime
.datetime
.now())
563 jsx_file
.write('Exported with io_export_after_effects.py\n')
564 jsx_file
.write('**************************************/\n\n\n\n')
567 jsx_file
.write("function compFromBlender(){\n")
569 jsx_file
.write('\nvar compName = prompt("Blender Comp\'s Name \\nEnter Name of newly created Composition","BlendComp","Composition\'s Name");\n')
570 jsx_file
.write('if (compName){') # Continue only if comp name is given. If not - terminate
571 jsx_file
.write('\nvar newComp = app.project.items.addComp(compName, %i, %i, %f, %f, %f);' %
572 (data
['width'], data
['height'], data
['aspect'], data
['duration'], data
['fps']))
573 jsx_file
.write('\nnewComp.displayStartTime = %f;\n\n\n' % ((data
['start'] + 1.0) / data
['fps']))
575 # create camera bundles (nulls)
576 jsx_file
.write('// ************** CAMERA 3D MARKERS **************\n\n\n')
577 for i
, obj
in enumerate(js_data
['bundles_cam']):
579 jsx_file
.write('var %s = newComp.layers.addNull();\n' % (name_ae
))
580 jsx_file
.write('%s.threeDLayer = true;\n' % name_ae
)
581 jsx_file
.write('%s.source.name = "%s";\n' % (name_ae
, name_ae
))
582 jsx_file
.write('%s.property("position").setValue(%s);\n\n\n' % (name_ae
, js_data
['bundles_cam'][obj
]['position']))
584 # create object bundles (not ready yet)
586 # create objects (nulls)
587 jsx_file
.write('// ************** OBJECTS **************\n\n\n')
588 for i
, obj
in enumerate(js_data
['nulls']):
590 jsx_file
.write('var %s = newComp.layers.addNull();\n' % (name_ae
))
591 jsx_file
.write('%s.threeDLayer = true;\n' % name_ae
)
592 jsx_file
.write('%s.source.name = "%s";\n' % (name_ae
, name_ae
))
593 # Set values of properties, add kyeframes only where needed
594 if include_animation
and js_data
['nulls'][name_ae
]['position_anim']:
595 jsx_file
.write('%s.property("position").setValuesAtTimes([%s],[%s]);\n' % (name_ae
, js_data
['times'], js_data
['nulls'][obj
]['position']))
597 jsx_file
.write('%s.property("position").setValue(%s);\n' % (name_ae
, js_data
['nulls'][obj
]['position_static']))
598 if include_animation
and js_data
['nulls'][name_ae
]['orientation_anim']:
599 jsx_file
.write('%s.property("orientation").setValuesAtTimes([%s],[%s]);\n' % (name_ae
, js_data
['times'], js_data
['nulls'][obj
]['orientation']))
601 jsx_file
.write('%s.property("orientation").setValue(%s);\n' % (name_ae
, js_data
['nulls'][obj
]['orientation_static']))
602 if include_animation
and js_data
['nulls'][name_ae
]['scale_anim']:
603 jsx_file
.write('%s.property("scale").setValuesAtTimes([%s],[%s]);\n\n\n' % (name_ae
, js_data
['times'], js_data
['nulls'][obj
]['scale']))
605 jsx_file
.write('%s.property("scale").setValue(%s);\n\n\n' % (name_ae
, js_data
['nulls'][obj
]['scale_static']))
606 # create solids (not ready yet)
609 jsx_file
.write('// ************** LIGHTS **************\n\n\n')
610 for i
, obj
in enumerate(js_data
['lights']):
612 jsx_file
.write('var %s = newComp.layers.addLight("%s", [0.0, 0.0]);\n' % (name_ae
, name_ae
))
613 jsx_file
.write('%s.autoOrient = AutoOrientType.NO_AUTO_ORIENT;\n' % name_ae
)
614 # Set values of properties, add kyeframes only where needed
615 if include_animation
and js_data
['lights'][name_ae
]['position_anim']:
616 jsx_file
.write('%s.property("position").setValuesAtTimes([%s],[%s]);\n' % (name_ae
, js_data
['times'], js_data
['lights'][obj
]['position']))
618 jsx_file
.write('%s.property("position").setValue(%s);\n' % (name_ae
, js_data
['lights'][obj
]['position_static']))
619 if include_animation
and js_data
['lights'][name_ae
]['orientation_anim']:
620 jsx_file
.write('%s.property("orientation").setValuesAtTimes([%s],[%s]);\n' % (name_ae
, js_data
['times'], js_data
['lights'][obj
]['orientation']))
622 jsx_file
.write('%s.property("orientation").setValue(%s);\n' % (name_ae
, js_data
['lights'][obj
]['orientation_static']))
623 if include_animation
and js_data
['lights'][name_ae
]['energy_anim']:
624 jsx_file
.write('%s.property("intensity").setValuesAtTimes([%s],[%s]);\n' % (name_ae
, js_data
['times'], js_data
['lights'][obj
]['energy']))
626 jsx_file
.write('%s.property("intensity").setValue(%s);\n' % (name_ae
, js_data
['lights'][obj
]['energy_static']))
627 if include_animation
and js_data
['lights'][name_ae
]['color_anim']:
628 jsx_file
.write('%s.property("Color").setValuesAtTimes([%s],[%s]);\n' % (name_ae
, js_data
['times'], js_data
['lights'][obj
]['color']))
630 jsx_file
.write('%s.property("Color").setValue(%s);\n' % (name_ae
, js_data
['lights'][obj
]['color_static']))
631 if js_data
['lights'][obj
]['type'] == 'SPOT':
632 if include_animation
and js_data
['lights'][name_ae
]['cone_angle_anim']:
633 jsx_file
.write('%s.property("Cone Angle").setValuesAtTimes([%s],[%s]);\n' % (name_ae
, js_data
['times'], js_data
['lights'][obj
]['cone_angle']))
635 jsx_file
.write('%s.property("Cone Angle").setValue(%s);\n' % (name_ae
, js_data
['lights'][obj
]['cone_angle_static']))
636 if include_animation
and js_data
['lights'][name_ae
]['cone_feather_anim']:
637 jsx_file
.write('%s.property("Cone Feather").setValuesAtTimes([%s],[%s]);\n' % (name_ae
, js_data
['times'], js_data
['lights'][obj
]['cone_feather']))
639 jsx_file
.write('%s.property("Cone Feather").setValue(%s);\n' % (name_ae
, js_data
['lights'][obj
]['cone_feather_static']))
640 jsx_file
.write('\n\n')
643 jsx_file
.write('// ************** CAMERAS **************\n\n\n')
644 for i
, cam
in enumerate(js_data
['cameras']): # more than one camera can be selected
646 jsx_file
.write('var %s = newComp.layers.addCamera("%s",[0,0]);\n' % (name_ae
, name_ae
))
647 jsx_file
.write('%s.autoOrient = AutoOrientType.NO_AUTO_ORIENT;\n' % name_ae
)
648 # Set values of properties, add kyeframes only where needed
649 if include_animation
and js_data
['cameras'][name_ae
]['position_anim']:
650 jsx_file
.write('%s.property("position").setValuesAtTimes([%s],[%s]);\n' % (name_ae
, js_data
['times'], js_data
['cameras'][cam
]['position']))
652 jsx_file
.write('%s.property("position").setValue(%s);\n' % (name_ae
, js_data
['cameras'][cam
]['position_static']))
653 if include_animation
and js_data
['cameras'][name_ae
]['orientation_anim']:
654 jsx_file
.write('%s.property("orientation").setValuesAtTimes([%s],[%s]);\n' % (name_ae
, js_data
['times'], js_data
['cameras'][cam
]['orientation']))
656 jsx_file
.write('%s.property("orientation").setValue(%s);\n' % (name_ae
, js_data
['cameras'][cam
]['orientation_static']))
657 if include_animation
and js_data
['cameras'][name_ae
]['zoom_anim']:
658 jsx_file
.write('%s.property("zoom").setValuesAtTimes([%s],[%s]);\n\n\n' % (name_ae
, js_data
['times'], js_data
['cameras'][cam
]['zoom']))
660 jsx_file
.write('%s.property("zoom").setValue(%s);\n\n\n' % (name_ae
, js_data
['cameras'][cam
]['zoom_static']))
662 # Exit import if no comp name given
663 jsx_file
.write('\n}else{alert ("Exit Import Blender animation data \\nNo Comp\'s name has been chosen","EXIT")};')
665 jsx_file
.write("}\n\n\n")
666 # Execute function. Wrap in "undo group" for easy undoing import process
667 jsx_file
.write('app.beginUndoGroup("Import Blender animation data");\n')
668 jsx_file
.write('compFromBlender();\n') # execute function
669 jsx_file
.write('app.endUndoGroup();\n\n\n')
672 data
['scn'].frame_set(curframe
) # set current frame of animation in blender to state before export
674 ##########################################
676 ##########################################
679 def main(file, context
, include_animation
, include_active_cam
, include_selected_cams
, include_selected_objects
, include_cam_bundles
, ae_size
):
680 data
= get_comp_data(context
)
681 selection
= get_selected(context
)
682 write_jsx_file(file, data
, selection
, include_animation
, include_active_cam
, include_selected_cams
, include_selected_objects
, include_cam_bundles
, ae_size
)
683 print ("\nExport to After Effects Completed")
686 ##########################################
687 # ExportJsx class register/unregister
688 ##########################################
690 from bpy_extras
.io_utils
import ExportHelper
691 from bpy
.props
import StringProperty
, BoolProperty
, FloatProperty
694 class ExportJsx(bpy
.types
.Operator
, ExportHelper
):
695 """Export selected cameras and objects animation to After Effects"""
696 bl_idname
= "export.jsx"
697 bl_label
= "Export to Adobe After Effects"
698 filename_ext
= ".jsx"
699 filter_glob
= StringProperty(default
="*.jsx", options
={'HIDDEN'})
701 include_animation
= BoolProperty(
703 description
="Animate Exported Cameras and Objects",
706 include_active_cam
= BoolProperty(
707 name
="Active Camera",
708 description
="Include Active Camera",
711 include_selected_cams
= BoolProperty(
712 name
="Selected Cameras",
713 description
="Add Selected Cameras",
716 include_selected_objects
= BoolProperty(
717 name
="Selected Objects",
718 description
="Export Selected Objects",
721 include_cam_bundles
= BoolProperty(
722 name
="Camera 3D Markers",
723 description
="Include 3D Markers of Camera Motion Solution for selected cameras",
726 # include_ob_bundles = BoolProperty(
727 # name="Objects 3D Markers",
728 # description="Include 3D Markers of Object Motion Solution for selected cameras",
731 ae_size
= FloatProperty(
733 description
="Size of AE Composition (pixels per 1BU)",
737 def draw(self
, context
):
741 box
.label('Size fo AE Comp (pixels per 1 BU)')
742 box
.prop(self
, 'ae_size')
743 box
.label('Animation:')
744 box
.prop(self
, 'include_animation')
745 box
.label('Include Cameras and Objects:')
746 box
.prop(self
, 'include_active_cam')
747 box
.prop(self
, 'include_selected_cams')
748 box
.prop(self
, 'include_selected_objects')
749 box
.label("Include Tracking Data:")
750 box
.prop(self
, 'include_cam_bundles')
751 # box.prop(self, 'include_ob_bundles')
754 def poll(cls
, context
):
755 active
= context
.active_object
756 selected
= context
.selected_objects
757 camera
= context
.scene
.camera
758 ok
= selected
or camera
761 def execute(self
, context
):
762 return main(self
.filepath
, context
, self
.include_animation
, self
.include_active_cam
, self
.include_selected_cams
, self
.include_selected_objects
, self
.include_cam_bundles
, self
.ae_size
)
765 def menu_func(self
, context
):
766 self
.layout
.operator(ExportJsx
.bl_idname
, text
="Adobe After Effects (.jsx)")
770 bpy
.utils
.register_class(ExportJsx
)
771 bpy
.types
.INFO_MT_file_export
.append(menu_func
)
775 bpy
.utils
.unregister_class(ExportJsx
)
776 bpy
.types
.INFO_MT_file_export
.remove(menu_func
)
778 if __name__
== "__main__":