6ab480b4e23af18ca6b5c0d010a56fc13ec8a8a8
[blender.git] / release / scripts / startup / bl_operators / clip.py
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 import bpy
21 import os
22 from bpy.types import Operator
23
24 from mathutils import Vector, Matrix
25
26
27 def CLIP_spacees_walk(context, all_screens, tarea, tspace, callback, *args):
28     screens = bpy.data.screens if all_screens else [context.screen]
29
30     for screen in screens:
31         for area in screen.areas:
32             if area.type == tarea:
33                 for space in area.spaces:
34                     if space.type == tspace:
35                         callback(space, *args)
36
37
38 def CLIP_set_viewport_background(context, all_screens, clip, clip_user):
39     def set_background(space_v3d, clip, user):
40         bgpic = None
41
42         for x in space_v3d.background_images:
43             if x.source == 'MOVIE':
44                 bgpic = x
45                 break
46
47         if not bgpic:
48             bgpic = space_v3d.background_images.new()
49
50         bgpic.source = 'MOVIE'
51         bgpic.clip = clip
52         bgpic.clip_user.proxy_render_size = user.proxy_render_size
53         bgpic.clip_user.use_render_undistorted = True
54         bgpic.use_camera_clip = False
55         bgpic.view_axis = 'CAMERA'
56
57         space_v3d.show_background_images = True
58
59     CLIP_spacees_walk(context, all_screens, 'VIEW_3D', 'VIEW_3D',
60                       set_background, clip, clip_user)
61
62
63 def CLIP_track_view_selected(sc, track):
64     if track.select_anchor:
65         return True
66
67     if sc.show_marker_pattern and track.select_pattern:
68         return True
69
70     if sc.show_marker_search and track.select_search:
71         return True
72
73     return False
74
75
76 class CLIP_OT_track_to_empty(Operator):
77     """Create an Empty object which will be copying movement of active track"""
78
79     bl_idname = "clip.track_to_empty"
80     bl_label = "Link Empty to Track"
81     bl_options = {'UNDO', 'REGISTER'}
82
83     def _link_track(self, context, track):
84         sc = context.space_data
85         constraint = None
86         ob = None
87
88         ob = bpy.data.objects.new(name=track.name, object_data=None)
89         ob.select = True
90         context.scene.objects.link(ob)
91         context.scene.objects.active = ob
92
93         for con in ob.constraints:
94             if con.type == 'FOLLOW_TRACK':
95                 constraint = con
96                 break
97
98         if constraint is None:
99             constraint = ob.constraints.new(type='FOLLOW_TRACK')
100
101         constraint.clip = sc.clip
102         constraint.track = track.name
103         constraint.use_3d_position = False
104
105     def execute(self, context):
106         sc = context.space_data
107         clip = sc.clip
108
109         for track in clip.tracking.tracks:
110             if CLIP_track_view_selected(sc, track):
111                 self._link_track(context, track)
112
113         return {'FINISHED'}
114
115
116 class CLIP_OT_bundles_to_mesh(Operator):
117     """Create vertex cloud using coordinates of reconstructed tracks"""
118
119     bl_idname = "clip.bundles_to_mesh"
120     bl_label = "3D Markers to Mesh"
121     bl_options = {'UNDO', 'REGISTER'}
122
123     @classmethod
124     def poll(cls, context):
125         sc = context.space_data
126         return (sc.type == 'CLIP_EDITOR') and sc.clip
127
128     def execute(self, context):
129         from bpy_extras.io_utils import unpack_list
130
131         sc = context.space_data
132         clip = sc.clip
133
134         new_verts = []
135
136         mesh = bpy.data.meshes.new(name="Tracks")
137         for track in clip.tracking.tracks:
138             if track.has_bundle:
139                 new_verts.append(track.bundle)
140
141         if new_verts:
142             mesh.vertices.add(len(new_verts))
143             mesh.vertices.foreach_set("co", unpack_list(new_verts))
144
145         ob = bpy.data.objects.new(name="Tracks", object_data=mesh)
146
147         context.scene.objects.link(ob)
148
149         return {'FINISHED'}
150
151
152 class CLIP_OT_delete_proxy(Operator):
153     """Delete movie clip proxy files from the hard drive"""
154
155     bl_idname = "clip.delete_proxy"
156     bl_label = "Delete Proxy"
157     bl_options = {'REGISTER'}
158
159     @classmethod
160     def poll(cls, context):
161         if context.space_data.type != 'CLIP_EDITOR':
162             return False
163
164         sc = context.space_data
165
166         return sc.clip
167
168     def invoke(self, context, event):
169         wm = context.window_manager
170
171         return wm.invoke_confirm(self, event)
172
173     def _rmproxy(self, abspath):
174         import shutil
175
176         if not os.path.exists(abspath):
177             return
178
179         if os.path.isdir(abspath):
180             shutil.rmtree(abspath)
181         else:
182             os.remove(abspath)
183
184     def execute(self, context):
185         sc = context.space_data
186         clip = sc.clip
187         if clip.use_proxy_custom_directory:
188             proxydir = clip.proxy.directory
189         else:
190             clipdir = os.path.dirname(clip.filepath)
191             proxydir = os.path.join(clipdir, 'BL_proxy')
192
193         clipfile = os.path.basename(clip.filepath)
194         proxy = os.path.join(proxydir, clipfile)
195         absproxy = bpy.path.abspath(proxy)
196
197         # proxy_<quality>[_undostorted]
198         for x in (25, 50, 75, 100):
199             d = os.path.join(absproxy, 'proxy_' + str(x))
200
201             self._rmproxy(d)
202             self._rmproxy(d + '_undistorted')
203             self._rmproxy(os.path.join(absproxy, 'proxy_' + str(x) + '.avi'))
204
205         tc = ('free_run.blen_tc',
206               'interp_free_run.blen_tc',
207               'record_run.blen_tc')
208
209         for x in tc:
210             self._rmproxy(os.path.join(absproxy, x))
211
212         # remove proxy per-clip directory
213         try:
214             os.rmdir(absproxy)
215         except OSError:
216             pass
217
218         # remove [custom] proxy directory if empty
219         try:
220             absdir = bpy.path.abspath(proxydir)
221             os.rmdir(absdir)
222         except OSError:
223             pass
224
225         return {'FINISHED'}
226
227
228 class CLIP_OT_set_viewport_background(Operator):
229     """Set current movie clip as a camera background in 3D viewport \
230 (works only when a 3D viewport is visible)"""
231
232     bl_idname = "clip.set_viewport_background"
233     bl_label = "Set as Background"
234     bl_options = {'REGISTER'}
235
236     @classmethod
237     def poll(cls, context):
238         if context.space_data.type != 'CLIP_EDITOR':
239             return False
240
241         sc = context.space_data
242
243         return sc.clip
244
245     def execute(self, context):
246         sc = context.space_data
247         CLIP_set_viewport_background(context, False, sc.clip, sc.clip_user)
248
249         return {'FINISHED'}
250
251
252 class CLIP_OT_constraint_to_fcurve(Operator):
253     """Create F-Curves for object which will copy \
254 object's movement caused by this constraint"""
255
256     bl_idname = "clip.constraint_to_fcurve"
257     bl_label = "Constraint to F-Curve"
258     bl_options = {'UNDO', 'REGISTER'}
259
260     def _bake_object(self, scene, ob):
261         con = None
262         clip = None
263         sfra = None
264         efra = None
265         frame_current = scene.frame_current
266         matrices = []
267
268         # Find constraint which would eb converting
269         # TODO: several camera solvers and track followers would fail,
270         #       but can't think about eal workflow where it'll be useful
271         for x in ob.constraints:
272             if x.type in ('CAMERA_SOLVER', 'FOLLOW_TRACK'):
273                 con = x
274
275         if not con:
276             self.report({'ERROR'},
277                 "Motion Tracking constraint to be converted not found")
278
279             return {'CANCELLED'}
280
281         # Get clip used for parenting
282         if con.use_active_clip:
283             clip = scene.active_clip
284         else:
285             clip = con.clip
286
287         if not clip:
288             self.report({'ERROR'},
289                 "Movie clip to use tracking data from isn't set")
290
291             return {'CANCELLED'}
292
293         if con.type == 'FOLLOW_TRACK' and con.use_3d_position:
294             mat = ob.matrix_world.copy()
295             ob.constraints.remove(con)
296             ob.matrix_world = mat
297
298             return {'FINISHED'}
299
300         # Find start and end frames
301         for track in clip.tracking.tracks:
302             if sfra is None:
303                 sfra = track.markers[0].frame
304             else:
305                 sfra = min(sfra, track.markers[0].frame)
306
307             if efra is None:
308                 efra = track.markers[-1].frame
309             else:
310                 efra = max(efra, track.markers[-1].frame)
311
312         if sfra is None or efra is None:
313             return
314
315         # Store object matrices
316         for x in range(sfra, efra + 1):
317             scene.frame_set(x)
318             matrices.append(ob.matrix_world.copy())
319
320         ob.animation_data_create()
321
322         # Apply matrices on object and insert keyframes
323         i = 0
324         for x in range(sfra, efra + 1):
325             scene.frame_set(x)
326             ob.matrix_world = matrices[i]
327
328             ob.keyframe_insert("location")
329
330             if ob.rotation_mode == 'QUATERNION':
331                 ob.keyframe_insert("rotation_quaternion")
332             else:
333                 ob.keyframe_insert("rotation_euler")
334
335             i += 1
336
337         ob.constraints.remove(con)
338
339         scene.frame_set(frame_current)
340
341     def execute(self, context):
342         scene = context.scene
343
344         for ob in scene.objects:
345             if ob.select:
346                 self._bake_object(scene, ob)
347
348         return {'FINISHED'}
349
350
351 class CLIP_OT_setup_tracking_scene(Operator):
352     """Prepare scene for compositing 3D objects into this footage"""
353
354     bl_idname = "clip.setup_tracking_scene"
355     bl_label = "Setup Tracking Scene"
356     bl_options = {'UNDO', 'REGISTER'}
357
358     @classmethod
359     def poll(cls, context):
360         sc = context.space_data
361
362         if sc.type != 'CLIP_EDITOR':
363             return False
364
365         clip = sc.clip
366
367         return clip and clip.tracking.reconstruction.is_valid
368
369     @staticmethod
370     def _setupScene(context):
371         scene = context.scene
372         scene.active_clip = context.space_data.clip
373
374     @staticmethod
375     def _setupWorld(context):
376         scene = context.scene
377         world = scene.world
378
379         if not world:
380             world = bpy.data.worlds.new(name="World")
381             scene.world = world
382
383         world.light_settings.use_ambient_occlusion = True
384         world.light_settings.ao_blend_type = 'MULTIPLY'
385
386         world.light_settings.use_environment_light = True
387         world.light_settings.environment_energy = 0.1
388
389         world.light_settings.distance = 1.0
390         world.light_settings.sample_method = 'ADAPTIVE_QMC'
391         world.light_settings.samples = 7
392         world.light_settings.threshold = 0.005
393
394     @staticmethod
395     def _findOrCreateCamera(context):
396         scene = context.scene
397
398         if scene.camera:
399             return scene.camera
400
401         cam = bpy.data.cameras.new(name="Camera")
402         camob = bpy.data.objects.new(name="Camera", object_data=cam)
403         scene.objects.link(camob)
404
405         scene.camera = camob
406
407         camob.matrix_local = (Matrix.Translation((7.481, -6.508, 5.344)) *
408             Matrix.Rotation(0.815, 4, 'Z') *
409             Matrix.Rotation(0.011, 4, 'Y') *
410             Matrix.Rotation(1.109, 4, 'X'))
411
412         return camob
413
414     @staticmethod
415     def _setupCamera(context):
416         camob = CLIP_OT_setup_tracking_scene._findOrCreateCamera(context)
417
418         # Remove all constraints to be sure motion is fine
419         camob.constraints.clear()
420
421         # Append camera solver constraint
422         con = camob.constraints.new(type='CAMERA_SOLVER')
423         con.use_active_clip = True
424         con.influence = 1.0
425
426     @staticmethod
427     def _setupViewport(context):
428         sc = context.space_data
429         CLIP_set_viewport_background(context, True, sc.clip, sc.clip_user)
430
431     @staticmethod
432     def _setupRenderLayers(context):
433         scene = context.scene
434         rlayers = scene.render.layers
435
436         if not scene.render.layers.get("Foreground"):
437             if len(rlayers) == 1:
438                 fg = rlayers[0]
439                 fg.name = 'Foreground'
440             else:
441                 fg = scene.render.layers.new('Foreground')
442
443             fg.use_sky = False
444             fg.layers = [True] + [False] * 19
445             fg.layers_zmask = [False] * 10 + [True] + [False] * 9
446             fg.use_pass_vector = True
447
448         if not scene.render.layers.get("Background"):
449             bg = scene.render.layers.new('Background')
450             bg.use_pass_shadow = True
451             bg.use_pass_ambient_occlusion = True
452             bg.layers = [False] * 10 + [True] + [False] * 9
453
454     @staticmethod
455     def _findNode(tree, type):
456         for node in tree.nodes:
457             if node.type == type:
458                 return node
459
460         return None
461
462     @staticmethod
463     def _findOrCreateNode(tree, type):
464         node = CLIP_OT_setup_tracking_scene._findNode(tree, type)
465
466         if not node:
467             node = tree.nodes.new(type=type)
468
469         return node
470
471     @staticmethod
472     def _needSetupNodes(context):
473         scene = context.scene
474         tree = scene.node_tree
475
476         if not tree:
477             # No compositor node tree found, time to create it!
478             return True
479
480         for node in tree.nodes:
481             if node.type in {'MOVIECLIP', 'MOVIEDISTORTION'}:
482                 return False
483
484         return True
485
486     @staticmethod
487     def _offsetNodes(tree):
488         for a in tree.nodes:
489             for b in tree.nodes:
490                 if a != b and a.location == b.location:
491                     b.location += Vector((40.0, 20.0))
492
493     def _setupNodes(self, context):
494         if not self._needSetupNodes(context):
495             # compositor nodes were already setup or even changes already
496             # do nothing to prevent nodes damage
497             return
498
499         # Enable backdrop for all compositor spaces
500         def setup_space(space):
501             space.show_backdrop = True
502
503         CLIP_spacees_walk(context, True, 'NODE_EDITOR', 'NODE_EDITOR',
504                           setup_space)
505
506         sc = context.space_data
507         scene = context.scene
508         scene.use_nodes = True
509         tree = scene.node_tree
510         clip = sc.clip
511
512         need_stabilization = False
513
514         # create nodes
515         rlayer_fg = self._findOrCreateNode(tree, 'R_LAYERS')
516         rlayer_bg = tree.nodes.new(type='R_LAYERS')
517         composite = self._findOrCreateNode(tree, 'COMPOSITE')
518
519         movieclip = tree.nodes.new(type='MOVIECLIP')
520         distortion = tree.nodes.new(type='MOVIEDISTORTION')
521
522         if need_stabilization:
523             stabilize = tree.nodes.new(type='STABILIZE2D')
524
525         scale = tree.nodes.new(type='SCALE')
526         invert = tree.nodes.new(type='INVERT')
527         add_ao = tree.nodes.new(type='MIX_RGB')
528         add_shadow = tree.nodes.new(type='MIX_RGB')
529         mul_shadow = tree.nodes.new(type='MIX_RGB')
530         mul_image = tree.nodes.new(type='MIX_RGB')
531         vector_blur = tree.nodes.new(type='VECBLUR')
532         alphaover = tree.nodes.new(type='ALPHAOVER')
533         viewer = tree.nodes.new(type='VIEWER')
534         zcomb = tree.nodes.new(type='ZCOMBINE')
535
536         # setup nodes
537         movieclip.clip = clip
538
539         distortion.clip = clip
540         distortion.distortion_type = 'UNDISTORT'
541
542         if need_stabilization:
543             stabilize.clip = clip
544
545         scale.space = 'RENDER_SIZE'
546
547         rlayer_bg.scene = scene
548         rlayer_bg.layer = "Background"
549
550         rlayer_fg.scene = scene
551         rlayer_fg.layer = "Foreground"
552
553         add_ao.blend_type = 'ADD'
554         add_shadow.blend_type = 'ADD'
555
556         mul_shadow.blend_type = 'MULTIPLY'
557         mul_shadow.inputs['Fac'].default_value = 0.8
558
559         mul_image.blend_type = 'MULTIPLY'
560         mul_image.inputs['Fac'].default_value = 0.8
561
562         vector_blur.factor = 0.75
563
564         zcomb.use_alpha = True
565
566         # create links
567         tree.links.new(movieclip.outputs['Image'], distortion.inputs['Image'])
568
569         if need_stabilization:
570             tree.links.new(distortion.outputs['Image'],
571                 stabilize.inputs['Image'])
572             tree.links.new(stabilize.outputs['Image'], scale.inputs['Image'])
573         else:
574             tree.links.new(distortion.outputs['Image'], scale.inputs['Image'])
575
576         tree.links.new(rlayer_bg.outputs['Alpha'], invert.inputs['Color'])
577
578         tree.links.new(invert.outputs['Color'], add_shadow.inputs[1])
579         tree.links.new(rlayer_bg.outputs['Shadow'], add_shadow.inputs[2])
580
581         tree.links.new(invert.outputs['Color'], add_ao.inputs[1])
582         tree.links.new(rlayer_bg.outputs['AO'], add_ao.inputs[2])
583
584         tree.links.new(add_ao.outputs['Image'], mul_shadow.inputs[1])
585         tree.links.new(add_shadow.outputs['Image'], mul_shadow.inputs[2])
586
587         tree.links.new(scale.outputs['Image'], mul_image.inputs[1])
588         tree.links.new(mul_shadow.outputs['Image'], mul_image.inputs[2])
589
590         tree.links.new(rlayer_fg.outputs['Image'], vector_blur.inputs['Image'])
591         tree.links.new(rlayer_fg.outputs['Z'], vector_blur.inputs['Z'])
592         tree.links.new(rlayer_fg.outputs['Speed'], vector_blur.inputs['Speed'])
593
594         tree.links.new(vector_blur.outputs['Image'], zcomb.inputs[0])
595         tree.links.new(rlayer_fg.outputs['Z'], zcomb.inputs[1])
596
597         tree.links.new(mul_image.outputs['Image'], zcomb.inputs[2])
598         tree.links.new(rlayer_bg.outputs['Z'], zcomb.inputs[3])
599
600         tree.links.new(mul_image.outputs['Image'], alphaover.inputs[1])
601         tree.links.new(zcomb.outputs['Image'], alphaover.inputs[2])
602
603         tree.links.new(alphaover.outputs['Image'], composite.inputs['Image'])
604         tree.links.new(alphaover.outputs['Image'], viewer.inputs['Image'])
605
606         # place nodes
607         movieclip.location = Vector((-300.0, 350.0))
608
609         distortion.location = movieclip.location
610         distortion.location += Vector((200.0, 0.0))
611
612         if need_stabilization:
613             stabilize.location = distortion.location
614             stabilize.location += Vector((200.0, 0.0))
615
616             scale.location = stabilize.location
617             scale.location += Vector((200.0, 0.0))
618         else:
619             scale.location = distortion.location
620             scale.location += Vector((200.0, 0.0))
621
622         rlayer_bg.location = movieclip.location
623         rlayer_bg.location -= Vector((0.0, 350.0))
624
625         invert.location = rlayer_bg.location
626         invert.location += Vector((250.0, 50.0))
627
628         add_ao.location = invert.location
629         add_ao.location[0] += 200
630         add_ao.location[1] = rlayer_bg.location[1]
631
632         add_shadow.location = add_ao.location
633         add_shadow.location -= Vector((0.0, 250.0))
634
635         mul_shadow.location = add_ao.location
636         mul_shadow.location += Vector((200.0, -50.0))
637
638         mul_image.location = mul_shadow.location
639         mul_image.location += Vector((300.0, 200.0))
640
641         rlayer_fg.location = rlayer_bg.location
642         rlayer_fg.location -= Vector((0.0, 500.0))
643
644         vector_blur.location[0] = mul_image.location[0] - 200
645         vector_blur.location[1] = rlayer_fg.location[1]
646
647         alphaover.location[0] = vector_blur.location[0] + 700
648         alphaover.location[1] = \
649             (vector_blur.location[1] + mul_image.location[1]) / 2
650
651         zcomb.location[0] = vector_blur.location[0] + 450
652         zcomb.location[1] = \
653             (vector_blur.location[1] + mul_image.location[1]) / 3 * 2
654
655         composite.location = alphaover.location
656         composite.location += Vector((200.0, -100.0))
657
658         viewer.location = composite.location
659         composite.location += Vector((0.0, 200.0))
660
661         # ensure no nodes were creates on position of existing node
662         self._offsetNodes(tree)
663
664     @staticmethod
665     def _createMesh(scene, name, vertices, faces):
666         from bpy_extras.io_utils import unpack_list, unpack_face_list
667
668         mesh = bpy.data.meshes.new(name=name)
669
670         mesh.vertices.add(len(vertices))
671         mesh.vertices.foreach_set("co", unpack_list(vertices))
672
673         mesh.faces.add(len(faces))
674         mesh.faces.foreach_set("vertices_raw", unpack_face_list(faces))
675
676         mesh.update(calc_edges=True)
677
678         ob = bpy.data.objects.new(name=name, object_data=mesh)
679
680         scene.objects.link(ob)
681
682         return ob
683
684     @staticmethod
685     def _getPlaneVertices(half_size, z):
686
687         return [(-half_size, -half_size, z),
688                 (-half_size, half_size, z),
689                 (half_size, half_size, z),
690                 (half_size, -half_size, z)]
691
692     def _createGround(self, scene):
693         vertices = self._getPlaneVertices(4.0, 0.0)
694         faces = [(0, 1, 2, 3)]
695
696         ob = self._createMesh(scene, "Ground", vertices, faces)
697         ob["is_ground"] = True
698
699         return ob
700
701     @staticmethod
702     def _findGround(context):
703         scene = context.scene
704
705         for ob in scene.objects:
706             if ob.type == 'MESH' and "is_ground" in ob:
707                 return ob
708
709         return None
710
711     @staticmethod
712     def _mergeLayers(layers_a, layers_b):
713
714         return [(layers_a[i] | layers_b[i]) for i in range(len(layers_a))]
715
716     @staticmethod
717     def _createLamp(scene):
718         lamp = bpy.data.lamps.new(name="Lamp", type='POINT')
719         lampob = bpy.data.objects.new(name="Lamp", object_data=lamp)
720         scene.objects.link(lampob)
721
722         lampob.matrix_local = Matrix.Translation((4.076, 1.005, 5.904))
723
724         lamp.distance = 30
725         lamp.shadow_method = 'RAY_SHADOW'
726
727         return lampob
728
729     def _createSampleObject(self, scene):
730         vertices = self._getPlaneVertices(1.0, -1.0) + \
731             self._getPlaneVertices(1.0, 1.0)
732         faces = ((0, 1, 2, 3),
733                  (4, 7, 6, 5),
734                  (0, 4, 5, 1),
735                  (1, 5, 6, 2),
736                  (2, 6, 7, 3),
737                  (3, 7, 4, 0))
738
739         return self._createMesh(scene, "Cube", vertices, faces)
740
741     def _setupObjects(self, context):
742         scene = context.scene
743
744         fg = scene.render.layers.get("Foreground")
745         bg = scene.render.layers.get("Background")
746
747         all_layers = self._mergeLayers(fg.layers, bg.layers)
748
749         # enshure all lamps are active on foreground and background
750         has_lamp = False
751         has_mesh = False
752         for ob in scene.objects:
753             if ob.type == 'LAMP':
754                 ob.layers = all_layers
755                 has_lamp = True
756             elif ob.type == 'MESH' and "is_ground" not in ob:
757                 has_mesh = True
758
759         # create sample lamp if there's no lamps in the scene
760         if not has_lamp:
761             lamp = self._createLamp(scene)
762             lamp.layers = all_layers
763
764         # create sample object if there's no meshes in the scene
765         if not has_mesh:
766             ob = self._createSampleObject(scene)
767             ob.layers = fg.layers
768
769         # create ground object if needed
770         ground = self._findGround(context)
771         if not ground:
772             ground = self._createGround(scene)
773             ground.layers = bg.layers
774         else:
775             # make sure ground is available on Background layer
776             ground.layers = self._mergeLayers(ground.layers, bg.layers)
777
778         # layers with background and foreground should be rendered
779         scene.layers = self._mergeLayers(scene.layers, all_layers)
780
781     def execute(self, context):
782         self._setupScene(context)
783         self._setupWorld(context)
784         self._setupCamera(context)
785         self._setupViewport(context)
786         self._setupRenderLayers(context)
787         self._setupNodes(context)
788         self._setupObjects(context)
789
790         return {'FINISHED'}