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, 69, 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 "tracker_url": "https://developer.blender.org/T29858",
33 "category": "Import-Export",
39 from math
import degrees
, floor
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
= floor(scene
.render
.fps
/ (scene
.render
.fps_base
) * 1000.0) / 1000.0
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, %f);' %
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__":