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 from bpy.types import Operator
24 from mathutils import Vector, Matrix
27 def CLIP_spacees_walk(context, all_screens, tarea, tspace, callback, *args):
28 screens = bpy.data.screens if all_screens else [context.screen]
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)
38 def CLIP_set_viewport_background(context, all_screens, clip, clip_user):
39 def set_background(space_v3d, clip, user):
42 for x in space_v3d.background_images:
43 if x.source == 'MOVIE':
48 bgpic = space_v3d.background_images.new()
50 bgpic.source = 'MOVIE'
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'
57 space_v3d.show_background_images = True
59 CLIP_spacees_walk(context, all_screens, 'VIEW_3D', 'VIEW_3D',
60 set_background, clip, clip_user)
63 def CLIP_track_view_selected(sc, track):
64 if track.select_anchor:
67 if sc.show_marker_pattern and track.select_pattern:
70 if sc.show_marker_search and track.select_search:
76 class CLIP_OT_track_to_empty(Operator):
77 """Create an Empty object which will be copying movement of active track"""
79 bl_idname = "clip.track_to_empty"
80 bl_label = "Link Empty to Track"
81 bl_options = {'UNDO', 'REGISTER'}
83 def _link_track(self, context, track):
84 sc = context.space_data
88 ob = bpy.data.objects.new(name=track.name, object_data=None)
90 context.scene.objects.link(ob)
91 context.scene.objects.active = ob
93 for con in ob.constraints:
94 if con.type == 'FOLLOW_TRACK':
98 if constraint is None:
99 constraint = ob.constraints.new(type='FOLLOW_TRACK')
101 constraint.clip = sc.clip
102 constraint.track = track.name
103 constraint.use_3d_position = False
105 def execute(self, context):
106 sc = context.space_data
109 for track in clip.tracking.tracks:
110 if CLIP_track_view_selected(sc, track):
111 self._link_track(context, track)
116 class CLIP_OT_bundles_to_mesh(Operator):
117 """Create vertex cloud using coordinates of reconstructed tracks"""
119 bl_idname = "clip.bundles_to_mesh"
120 bl_label = "3D Markers to Mesh"
121 bl_options = {'UNDO', 'REGISTER'}
124 def poll(cls, context):
125 sc = context.space_data
126 return (sc.type == 'CLIP_EDITOR') and sc.clip
128 def execute(self, context):
129 from bpy_extras.io_utils import unpack_list
131 sc = context.space_data
136 mesh = bpy.data.meshes.new(name="Tracks")
137 for track in clip.tracking.tracks:
139 new_verts.append(track.bundle)
142 mesh.vertices.add(len(new_verts))
143 mesh.vertices.foreach_set("co", unpack_list(new_verts))
145 ob = bpy.data.objects.new(name="Tracks", object_data=mesh)
147 context.scene.objects.link(ob)
152 class CLIP_OT_delete_proxy(Operator):
153 """Delete movie clip proxy files from the hard drive"""
155 bl_idname = "clip.delete_proxy"
156 bl_label = "Delete Proxy"
157 bl_options = {'REGISTER'}
160 def poll(cls, context):
161 if context.space_data.type != 'CLIP_EDITOR':
164 sc = context.space_data
168 def invoke(self, context, event):
169 wm = context.window_manager
171 return wm.invoke_confirm(self, event)
173 def _rmproxy(self, abspath):
176 if not os.path.exists(abspath):
179 if os.path.isdir(abspath):
180 shutil.rmtree(abspath)
184 def execute(self, context):
185 sc = context.space_data
187 if clip.use_proxy_custom_directory:
188 proxydir = clip.proxy.directory
190 clipdir = os.path.dirname(clip.filepath)
191 proxydir = os.path.join(clipdir, 'BL_proxy')
193 clipfile = os.path.basename(clip.filepath)
194 proxy = os.path.join(proxydir, clipfile)
195 absproxy = bpy.path.abspath(proxy)
197 # proxy_<quality>[_undostorted]
198 for x in (25, 50, 75, 100):
199 d = os.path.join(absproxy, 'proxy_' + str(x))
202 self._rmproxy(d + '_undistorted')
203 self._rmproxy(os.path.join(absproxy, 'proxy_' + str(x) + '.avi'))
205 tc = ('free_run.blen_tc',
206 'interp_free_run.blen_tc',
207 'record_run.blen_tc')
210 self._rmproxy(os.path.join(absproxy, x))
212 # remove proxy per-clip directory
218 # remove [custom] proxy directory if empty
220 absdir = bpy.path.abspath(proxydir)
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)"""
232 bl_idname = "clip.set_viewport_background"
233 bl_label = "Set as Background"
234 bl_options = {'REGISTER'}
237 def poll(cls, context):
238 if context.space_data.type != 'CLIP_EDITOR':
241 sc = context.space_data
245 def execute(self, context):
246 sc = context.space_data
247 CLIP_set_viewport_background(context, False, sc.clip, sc.clip_user)
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"""
256 bl_idname = "clip.constraint_to_fcurve"
257 bl_label = "Constraint to F-Curve"
258 bl_options = {'UNDO', 'REGISTER'}
260 def _bake_object(self, scene, ob):
265 frame_current = scene.frame_current
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'):
276 self.report({'ERROR'},
277 "Motion Tracking constraint to be converted not found")
281 # Get clip used for parenting
282 if con.use_active_clip:
283 clip = scene.active_clip
288 self.report({'ERROR'},
289 "Movie clip to use tracking data from isn't set")
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
300 # Find start and end frames
301 for track in clip.tracking.tracks:
303 sfra = track.markers[0].frame
305 sfra = min(sfra, track.markers[0].frame)
308 efra = track.markers[-1].frame
310 efra = max(efra, track.markers[-1].frame)
312 if sfra is None or efra is None:
315 # Store object matrices
316 for x in range(sfra, efra + 1):
318 matrices.append(ob.matrix_world.copy())
320 ob.animation_data_create()
322 # Apply matrices on object and insert keyframes
324 for x in range(sfra, efra + 1):
326 ob.matrix_world = matrices[i]
328 ob.keyframe_insert("location")
330 if ob.rotation_mode == 'QUATERNION':
331 ob.keyframe_insert("rotation_quaternion")
333 ob.keyframe_insert("rotation_euler")
337 ob.constraints.remove(con)
339 scene.frame_set(frame_current)
341 def execute(self, context):
342 scene = context.scene
344 for ob in scene.objects:
346 self._bake_object(scene, ob)
351 class CLIP_OT_setup_tracking_scene(Operator):
352 """Prepare scene for compositing 3D objects into this footage"""
354 bl_idname = "clip.setup_tracking_scene"
355 bl_label = "Setup Tracking Scene"
356 bl_options = {'UNDO', 'REGISTER'}
359 def poll(cls, context):
360 sc = context.space_data
362 if sc.type != 'CLIP_EDITOR':
367 return clip and clip.tracking.reconstruction.is_valid
370 def _setupScene(context):
371 scene = context.scene
372 scene.active_clip = context.space_data.clip
375 def _setupWorld(context):
376 scene = context.scene
380 world = bpy.data.worlds.new(name="World")
383 world.light_settings.use_ambient_occlusion = True
384 world.light_settings.ao_blend_type = 'MULTIPLY'
386 world.light_settings.use_environment_light = True
387 world.light_settings.environment_energy = 0.1
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
395 def _findOrCreateCamera(context):
396 scene = context.scene
401 cam = bpy.data.cameras.new(name="Camera")
402 camob = bpy.data.objects.new(name="Camera", object_data=cam)
403 scene.objects.link(camob)
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'))
415 def _setupCamera(context):
416 camob = CLIP_OT_setup_tracking_scene._findOrCreateCamera(context)
418 # Remove all constraints to be sure motion is fine
419 camob.constraints.clear()
421 # Append camera solver constraint
422 con = camob.constraints.new(type='CAMERA_SOLVER')
423 con.use_active_clip = True
427 def _setupViewport(context):
428 sc = context.space_data
429 CLIP_set_viewport_background(context, True, sc.clip, sc.clip_user)
432 def _setupRenderLayers(context):
433 scene = context.scene
434 rlayers = scene.render.layers
436 if not scene.render.layers.get("Foreground"):
437 if len(rlayers) == 1:
439 fg.name = 'Foreground'
441 fg = scene.render.layers.new('Foreground')
444 fg.layers = [True] + [False] * 19
445 fg.layers_zmask = [False] * 10 + [True] + [False] * 9
446 fg.use_pass_vector = True
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
455 def _findNode(tree, type):
456 for node in tree.nodes:
457 if node.type == type:
463 def _findOrCreateNode(tree, type):
464 node = CLIP_OT_setup_tracking_scene._findNode(tree, type)
467 node = tree.nodes.new(type=type)
472 def _needSetupNodes(context):
473 scene = context.scene
474 tree = scene.node_tree
477 # No compositor node tree found, time to create it!
480 for node in tree.nodes:
481 if node.type in {'MOVIECLIP', 'MOVIEDISTORTION'}:
487 def _offsetNodes(tree):
490 if a != b and a.location == b.location:
491 b.location += Vector((40.0, 20.0))
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
499 # Enable backdrop for all compositor spaces
500 def setup_space(space):
501 space.show_backdrop = True
503 CLIP_spacees_walk(context, True, 'NODE_EDITOR', 'NODE_EDITOR',
506 sc = context.space_data
507 scene = context.scene
508 scene.use_nodes = True
509 tree = scene.node_tree
512 need_stabilization = False
515 rlayer_fg = self._findOrCreateNode(tree, 'R_LAYERS')
516 rlayer_bg = tree.nodes.new(type='R_LAYERS')
517 composite = self._findOrCreateNode(tree, 'COMPOSITE')
519 movieclip = tree.nodes.new(type='MOVIECLIP')
520 distortion = tree.nodes.new(type='MOVIEDISTORTION')
522 if need_stabilization:
523 stabilize = tree.nodes.new(type='STABILIZE2D')
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')
537 movieclip.clip = clip
539 distortion.clip = clip
540 distortion.distortion_type = 'UNDISTORT'
542 if need_stabilization:
543 stabilize.clip = clip
545 scale.space = 'RENDER_SIZE'
547 rlayer_bg.scene = scene
548 rlayer_bg.layer = "Background"
550 rlayer_fg.scene = scene
551 rlayer_fg.layer = "Foreground"
553 add_ao.blend_type = 'ADD'
554 add_shadow.blend_type = 'ADD'
556 mul_shadow.blend_type = 'MULTIPLY'
557 mul_shadow.inputs['Fac'].default_value = 0.8
559 mul_image.blend_type = 'MULTIPLY'
560 mul_image.inputs['Fac'].default_value = 0.8
562 vector_blur.factor = 0.75
564 zcomb.use_alpha = True
567 tree.links.new(movieclip.outputs['Image'], distortion.inputs['Image'])
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'])
574 tree.links.new(distortion.outputs['Image'], scale.inputs['Image'])
576 tree.links.new(rlayer_bg.outputs['Alpha'], invert.inputs['Color'])
578 tree.links.new(invert.outputs['Color'], add_shadow.inputs[1])
579 tree.links.new(rlayer_bg.outputs['Shadow'], add_shadow.inputs[2])
581 tree.links.new(invert.outputs['Color'], add_ao.inputs[1])
582 tree.links.new(rlayer_bg.outputs['AO'], add_ao.inputs[2])
584 tree.links.new(add_ao.outputs['Image'], mul_shadow.inputs[1])
585 tree.links.new(add_shadow.outputs['Image'], mul_shadow.inputs[2])
587 tree.links.new(scale.outputs['Image'], mul_image.inputs[1])
588 tree.links.new(mul_shadow.outputs['Image'], mul_image.inputs[2])
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'])
594 tree.links.new(vector_blur.outputs['Image'], zcomb.inputs[0])
595 tree.links.new(rlayer_fg.outputs['Z'], zcomb.inputs[1])
597 tree.links.new(mul_image.outputs['Image'], zcomb.inputs[2])
598 tree.links.new(rlayer_bg.outputs['Z'], zcomb.inputs[3])
600 tree.links.new(mul_image.outputs['Image'], alphaover.inputs[1])
601 tree.links.new(zcomb.outputs['Image'], alphaover.inputs[2])
603 tree.links.new(alphaover.outputs['Image'], composite.inputs['Image'])
604 tree.links.new(alphaover.outputs['Image'], viewer.inputs['Image'])
607 movieclip.location = Vector((-300.0, 350.0))
609 distortion.location = movieclip.location
610 distortion.location += Vector((200.0, 0.0))
612 if need_stabilization:
613 stabilize.location = distortion.location
614 stabilize.location += Vector((200.0, 0.0))
616 scale.location = stabilize.location
617 scale.location += Vector((200.0, 0.0))
619 scale.location = distortion.location
620 scale.location += Vector((200.0, 0.0))
622 rlayer_bg.location = movieclip.location
623 rlayer_bg.location -= Vector((0.0, 350.0))
625 invert.location = rlayer_bg.location
626 invert.location += Vector((250.0, 50.0))
628 add_ao.location = invert.location
629 add_ao.location[0] += 200
630 add_ao.location[1] = rlayer_bg.location[1]
632 add_shadow.location = add_ao.location
633 add_shadow.location -= Vector((0.0, 250.0))
635 mul_shadow.location = add_ao.location
636 mul_shadow.location += Vector((200.0, -50.0))
638 mul_image.location = mul_shadow.location
639 mul_image.location += Vector((300.0, 200.0))
641 rlayer_fg.location = rlayer_bg.location
642 rlayer_fg.location -= Vector((0.0, 500.0))
644 vector_blur.location[0] = mul_image.location[0] - 200
645 vector_blur.location[1] = rlayer_fg.location[1]
647 alphaover.location[0] = vector_blur.location[0] + 700
648 alphaover.location[1] = \
649 (vector_blur.location[1] + mul_image.location[1]) / 2
651 zcomb.location[0] = vector_blur.location[0] + 450
652 zcomb.location[1] = \
653 (vector_blur.location[1] + mul_image.location[1]) / 3 * 2
655 composite.location = alphaover.location
656 composite.location += Vector((200.0, -100.0))
658 viewer.location = composite.location
659 composite.location += Vector((0.0, 200.0))
661 # ensure no nodes were creates on position of existing node
662 self._offsetNodes(tree)
665 def _createMesh(scene, name, vertices, faces):
666 from bpy_extras.io_utils import unpack_list, unpack_face_list
668 mesh = bpy.data.meshes.new(name=name)
670 mesh.vertices.add(len(vertices))
671 mesh.vertices.foreach_set("co", unpack_list(vertices))
673 mesh.faces.add(len(faces))
674 mesh.faces.foreach_set("vertices_raw", unpack_face_list(faces))
676 mesh.update(calc_edges=True)
678 ob = bpy.data.objects.new(name=name, object_data=mesh)
680 scene.objects.link(ob)
685 def _getPlaneVertices(half_size, z):
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)]
692 def _createGround(self, scene):
693 vertices = self._getPlaneVertices(4.0, 0.0)
694 faces = [(0, 1, 2, 3)]
696 ob = self._createMesh(scene, "Ground", vertices, faces)
697 ob["is_ground"] = True
702 def _findGround(context):
703 scene = context.scene
705 for ob in scene.objects:
706 if ob.type == 'MESH' and "is_ground" in ob:
712 def _mergeLayers(layers_a, layers_b):
714 return [(layers_a[i] | layers_b[i]) for i in range(len(layers_a))]
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)
722 lampob.matrix_local = Matrix.Translation((4.076, 1.005, 5.904))
725 lamp.shadow_method = 'RAY_SHADOW'
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),
739 return self._createMesh(scene, "Cube", vertices, faces)
741 def _setupObjects(self, context):
742 scene = context.scene
744 fg = scene.render.layers.get("Foreground")
745 bg = scene.render.layers.get("Background")
747 all_layers = self._mergeLayers(fg.layers, bg.layers)
749 # enshure all lamps are active on foreground and background
752 for ob in scene.objects:
753 if ob.type == 'LAMP':
754 ob.layers = all_layers
756 elif ob.type == 'MESH' and "is_ground" not in ob:
759 # create sample lamp if there's no lamps in the scene
761 lamp = self._createLamp(scene)
762 lamp.layers = all_layers
764 # create sample object if there's no meshes in the scene
766 ob = self._createSampleObject(scene)
767 ob.layers = fg.layers
769 # create ground object if needed
770 ground = self._findGround(context)
772 ground = self._createGround(scene)
773 ground.layers = bg.layers
775 # make sure ground is available on Background layer
776 ground.layers = self._mergeLayers(ground.layers, bg.layers)
778 # layers with background and foreground should be rendered
779 scene.layers = self._mergeLayers(scene.layers, all_layers)
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)