~siretart/ubuntu/utopic/blender/libav10

« back to all changes in this revision

Viewing changes to release/scripts/addons/io_export_after_effects.py

  • Committer: Package Import Robot
  • Author(s): Matteo F. Vescovi
  • Date: 2012-07-23 08:54:18 UTC
  • mfrom: (14.2.16 sid)
  • mto: (14.2.19 sid)
  • mto: This revision was merged to the branch mainline in revision 42.
  • Revision ID: package-import@ubuntu.com-20120723085418-9foz30v6afaf5ffs
Tags: 2.63a-2
* debian/: Cycles support added (Closes: #658075)
  For now, this top feature has been enabled only
  on [any-amd64 any-i386] architectures because
  of OpenImageIO failing on all others
* debian/: scripts installation path changed
  from /usr/lib to /usr/share:
  + debian/patches/: patchset re-worked for path changing
  + debian/control: "Breaks" field added on yafaray-exporter

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# ##### BEGIN GPL LICENSE BLOCK #####
 
2
#
 
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.
 
7
#
 
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.
 
12
#
 
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.
 
16
#
 
17
# ##### END GPL LICENSE BLOCK #####
 
18
 
 
19
# <pep8 compliant>
 
20
 
 
21
bl_info = {
 
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',
 
25
    'version': (0, 6, 3),
 
26
    'blender': (2, 6, 2),
 
27
    'location': 'File > Export > Adobe After Effects (.jsx)',
 
28
    "warning": "",
 
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',
 
34
    }
 
35
 
 
36
 
 
37
import bpy
 
38
import datetime
 
39
from math import degrees
 
40
from mathutils import Matrix
 
41
 
 
42
 
 
43
# create list of static blender's data
 
44
def get_comp_data(context):
 
45
    scene = context.scene
 
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
 
50
    end = scene.frame_end
 
51
    active_cam_frames = get_active_cam_for_each_frame(scene, start, end)
 
52
    fps = scene.render.fps
 
53
 
 
54
    return {
 
55
        'scn': scene,
 
56
        'width': scene.render.resolution_x,
 
57
        'height': scene.render.resolution_y,
 
58
        'aspect': aspect,
 
59
        'fps': fps,
 
60
        'start': start,
 
61
        'end': end,
 
62
        'duration': (end - start + 1.0) / fps,
 
63
        'active_cam_frames': active_cam_frames,
 
64
        'curframe': scene.frame_current,
 
65
        }
 
66
 
 
67
 
 
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 = []
 
71
    sorted_markers = []
 
72
    markers = scene.timeline_markers
 
73
    if markers:
 
74
        for marker in markers:
 
75
            if marker.camera:
 
76
                sorted_markers.append([marker.frame, marker])
 
77
        sorted_markers = sorted(sorted_markers)
 
78
 
 
79
        if sorted_markers:
 
80
            for frame in range(start, end + 1):
 
81
                for m, marker in enumerate(sorted_markers):
 
82
                    if marker[0] > frame:
 
83
                        if m != 0:
 
84
                            active_cam_frames.append(sorted_markers[m - 1][1].camera)
 
85
                        else:
 
86
                            active_cam_frames.append(marker[1].camera)
 
87
                        break
 
88
                    elif m == len(sorted_markers) - 1:
 
89
                        active_cam_frames.append(marker[1].camera)
 
90
    if not active_cam_frames:
 
91
        if scene.camera:
 
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)
 
94
 
 
95
    return(active_cam_frames)
 
96
 
 
97
 
 
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
 
105
 
 
106
    for ob in obs:
 
107
        if ob.type == 'CAMERA':
 
108
            cameras.append([ob, convert_name(ob.name)])
 
109
 
 
110
        elif is_plane(ob):
 
111
            # not ready yet. is_plane(object) returns False in all cases. This is temporary
 
112
            solids.append([ob, convert_name(ob.name)])
 
113
 
 
114
        elif ob.type == 'LAMP':
 
115
            lights.append([ob, ob.data.type + convert_name(ob.name)])  # Type of lamp added to name
 
116
 
 
117
        else:
 
118
            nulls.append([ob, convert_name(ob.name)])
 
119
 
 
120
    selection = {
 
121
        'cameras': cameras,
 
122
        'solids': solids,
 
123
        'lights': lights,
 
124
        'nulls': nulls,
 
125
        }
 
126
 
 
127
    return selection
 
128
 
 
129
 
 
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
 
133
    return False
 
134
 
 
135
 
 
136
# convert names of objects to avoid errors in AE.
 
137
def convert_name(name):
 
138
    name = "_" + name
 
139
    '''
 
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():
 
144
        name = "_" + name
 
145
    '''
 
146
    name = bpy.path.clean_name(name)
 
147
    name = name.replace("-", "_")
 
148
 
 
149
    return name
 
150
 
 
151
 
 
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):
 
155
 
 
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()
 
160
 
 
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
 
170
    if x_rot_correction:
 
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
 
176
 
 
177
    return x, y, z, rx, ry, rz, sx, sy, sz
 
178
 
 
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
 
181
#
 
182
#
 
183
# AE's lens is defined by "zoom" in pixels. Zoom determines focal angle or focal length.
 
184
#
 
185
# ZOOM VALUE CALCULATIONS:
 
186
#
 
187
# Given values:
 
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)
 
195
#
 
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
 
199
#
 
200
# zoom can be calculated using simple proportions.
 
201
#
 
202
#                             |
 
203
#                           / |
 
204
#                         /   |
 
205
#                       /     | d
 
206
#       s  |\         /       | i
 
207
#       e  |  \     /         | m
 
208
#       n  |    \ /           | e
 
209
#       s  |    / \           | n
 
210
#       o  |  /     \         | s
 
211
#       r  |/         \       | i
 
212
#                       \     | o
 
213
#          |     |        \   | n
 
214
#          |     |          \ |
 
215
#          |     |            |
 
216
#           lens |    zoom
 
217
#
 
218
#    zoom / dimension = lens / sensor   =>
 
219
#    zoom = lens * dimension / sensor
 
220
#
 
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
 
223
 
 
224
 
 
225
def convert_lens(camera, width, height, aspect):
 
226
    if camera.data.sensor_fit == 'VERTICAL':
 
227
        sensor = camera.data.sensor_height
 
228
        dimension = height
 
229
    else:
 
230
        sensor = camera.data.sensor_width
 
231
        dimension = width
 
232
 
 
233
    zoom = camera.data.lens * dimension / sensor * aspect
 
234
 
 
235
    return zoom
 
236
 
 
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
 
240
#    return matrix
 
241
 
 
242
 
 
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):
 
245
 
 
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
 
250
    js_data = {
 
251
        'times': '',
 
252
        'cameras': {},
 
253
        'solids': {},  # not ready yet
 
254
        'lights': {},
 
255
        'nulls': {},
 
256
        'bundles_cam': {},
 
257
        'bundles_ob': {},  # not ready yet
 
258
        }
 
259
 
 
260
    # create structure for active camera/cameras
 
261
    active_cam_name = ''
 
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
 
266
        else:
 
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] = {
 
270
            'position': '',
 
271
            'position_static': '',
 
272
            'position_anim': False,
 
273
            'orientation': '',
 
274
            'orientation_static': '',
 
275
            'orientation_anim': False,
 
276
            'zoom': '',
 
277
            'zoom_static': '',
 
278
            'zoom_anim': False,
 
279
            }
 
280
 
 
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] = {
 
287
                    'position': '',
 
288
                    'position_static': '',
 
289
                    'position_anim': False,
 
290
                    'orientation': '',
 
291
                    'orientation_static': '',
 
292
                    'orientation_anim': False,
 
293
                    'zoom': '',
 
294
                    'zoom_static': '',
 
295
                    'zoom_anim': False,
 
296
                    }
 
297
    '''
 
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] = {
 
302
            'position': '',
 
303
            'orientation': '',
 
304
            'rotationX': '',
 
305
            'scale': '',
 
306
            }
 
307
    '''
 
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,
 
314
                'energy': '',
 
315
                'energy_static': '',
 
316
                'energy_anim': False,
 
317
                'cone_angle': '',
 
318
                'cone_angle_static': '',
 
319
                'cone_angle_anim': False,
 
320
                'cone_feather': '',
 
321
                'cone_feather_static': '',
 
322
                'cone_feather_anim': False,
 
323
                'color': '',
 
324
                'color_static': '',
 
325
                'color_anim': False,
 
326
                'position': '',
 
327
                'position_static': '',
 
328
                'position_anim': False,
 
329
                'orientation': '',
 
330
                'orientation_static': '',
 
331
                'orientation_anim': False,
 
332
                }
 
333
 
 
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] = {
 
339
                'position': '',
 
340
                'position_static': '',
 
341
                'position_anim': False,
 
342
                'orientation': '',
 
343
                'orientation_static': '',
 
344
                'orientation_anim': False,
 
345
                'scale': '',
 
346
                'scale_static': '',
 
347
                'scale_anim': False,
 
348
                }
 
349
 
 
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
 
353
        selected_cams = []
 
354
        active_cams = []
 
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)))
 
362
 
 
363
        for cam in 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
 
371
                    else:
 
372
                        clip = constraint.clip
 
373
 
 
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)
 
377
                        if track.has_bundle:
 
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] = {
 
381
                                'position': '',
 
382
                                }
 
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])
 
388
 
 
389
    # get all keyframes for each object and store in dico
 
390
    if include_animation:
 
391
        end = data['end'] + 1
 
392
    else:
 
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)
 
397
 
 
398
        # get time for this loop
 
399
        js_data['times'] += '%f ,' % ((frame - data['start']) / data['fps'])
 
400
 
 
401
        # keyframes for active camera/cameras
 
402
        if include_active_cam and data['active_cam_frames'] != []:
 
403
            if len(data['active_cam_frames']) == 1:
 
404
                cur_cam_index = 0
 
405
            else:
 
406
                cur_cam_index = frame - data['start']
 
407
            active_cam = data['active_cam_frames'][cur_cam_index]
 
408
            # get cam name
 
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
 
433
 
 
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:
 
438
                    # get 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
 
463
 
 
464
        '''
 
465
        # keyframes for all solids. Not ready yet. Temporarily not active
 
466
        for i, ob in enumerate(selection['solids']):
 
467
            #get object name
 
468
            name_ae = selection['solids'][i][1]
 
469
            #convert ob position to AE space
 
470
        '''
 
471
 
 
472
        # keyframes for all lights.
 
473
        if include_selected_objects:
 
474
            for i, ob in enumerate(selection['lights']):
 
475
                #get object name
 
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
 
505
                if type == 'SPOT':
 
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
 
519
 
 
520
        # keyframes for all nulls
 
521
        if include_selected_objects:
 
522
            for i, ob in enumerate(selection['nulls']):
 
523
                # get object name
 
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
 
546
 
 
547
        # keyframes for all object bundles. Not ready yet.
 
548
        #
 
549
        #
 
550
        #
 
551
 
 
552
    # ---- write JSX file
 
553
    jsx_file = open(file, 'w')
 
554
 
 
555
    # make the jsx executable in After Effects (enable double click on jsx)
 
556
    jsx_file.write('#target AfterEffects\n\n')
 
557
    # Script's header
 
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')
 
566
 
 
567
    # wrap in function
 
568
    jsx_file.write("function compFromBlender(){\n")
 
569
    # create new comp
 
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']))
 
575
 
 
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']):
 
579
        name_ae = obj
 
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']))
 
584
 
 
585
    # create object bundles (not ready yet)
 
586
 
 
587
    # create objects (nulls)
 
588
    jsx_file.write('// **************  OBJECTS  **************\n\n\n')
 
589
    for i, obj in enumerate(js_data['nulls']):
 
590
        name_ae = obj
 
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']))
 
597
        else:
 
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']))
 
601
        else:
 
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']))
 
605
        else:
 
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)
 
608
 
 
609
    # create lights
 
610
    jsx_file.write('// **************  LIGHTS  **************\n\n\n')
 
611
    for i, obj in enumerate(js_data['lights']):
 
612
        name_ae = obj
 
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']))
 
618
        else:
 
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']))
 
622
        else:
 
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']))
 
626
        else:
 
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']))
 
630
        else:
 
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']))
 
635
                else:
 
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']))
 
639
                else:
 
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')
 
642
 
 
643
    # create cameras
 
644
    jsx_file.write('// **************  CAMERAS  **************\n\n\n')
 
645
    for i, cam in enumerate(js_data['cameras']):  # more than one camera can be selected
 
646
        name_ae = cam
 
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']))
 
652
        else:
 
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']))
 
656
        else:
 
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']))
 
660
        else:
 
661
            jsx_file.write('%s.property("zoom").setValue(%s);\n\n\n' % (name_ae, js_data['cameras'][cam]['zoom_static']))
 
662
 
 
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")};')
 
665
    # Close function
 
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')
 
671
    jsx_file.close()
 
672
 
 
673
    data['scn'].frame_set(curframe)  # set current frame of animation in blender to state before export
 
674
 
 
675
##########################################
 
676
# DO IT
 
677
##########################################
 
678
 
 
679
 
 
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")
 
685
    return {'FINISHED'}
 
686
 
 
687
##########################################
 
688
# ExportJsx class register/unregister
 
689
##########################################
 
690
 
 
691
from bpy_extras.io_utils import ExportHelper
 
692
from bpy.props import StringProperty, BoolProperty
 
693
 
 
694
 
 
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'})
 
701
 
 
702
    include_animation = BoolProperty(
 
703
            name="Animation",
 
704
            description="Animate Exported Cameras and Objects",
 
705
            default=True,
 
706
            )
 
707
    include_active_cam = BoolProperty(
 
708
            name="Active Camera",
 
709
            description="Include Active Camera",
 
710
            default=True,
 
711
            )
 
712
    include_selected_cams = BoolProperty(
 
713
            name="Selected Cameras",
 
714
            description="Add Selected Cameras",
 
715
            default=True,
 
716
            )
 
717
    include_selected_objects = BoolProperty(
 
718
            name="Selected Objects",
 
719
            description="Export Selected Objects",
 
720
            default=True,
 
721
            )
 
722
    include_cam_bundles = BoolProperty(
 
723
            name="Camera 3D Markers",
 
724
            description="Include 3D Markers of Camera Motion Solution for selected cameras",
 
725
            default=True,
 
726
            )
 
727
#    include_ob_bundles = BoolProperty(
 
728
#            name="Objects 3D Markers",
 
729
#            description="Include 3D Markers of Object Motion Solution for selected cameras",
 
730
#            default=True,
 
731
#            )
 
732
 
 
733
    def draw(self, context):
 
734
        layout = self.layout
 
735
 
 
736
        box = layout.box()
 
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')
 
746
 
 
747
    @classmethod
 
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
 
753
        return ok
 
754
 
 
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)
 
757
 
 
758
 
 
759
def menu_func(self, context):
 
760
    self.layout.operator(ExportJsx.bl_idname, text="Adobe After Effects (.jsx)")
 
761
 
 
762
 
 
763
def register():
 
764
    bpy.utils.register_class(ExportJsx)
 
765
    bpy.types.INFO_MT_file_export.append(menu_func)
 
766
 
 
767
 
 
768
def unregister():
 
769
    bpy.utils.unregister_class(ExportJsx)
 
770
    bpy.types.INFO_MT_file_export.remove(menu_func)
 
771
 
 
772
if __name__ == "__main__":
 
773
    register()