b805178bdc80c346ec384f63f0aff64e2d17a6ba
[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 import shutil
23 from bpy.types import Operator
24 from bpy_extras.io_utils import unpack_list
25
26
27 def CLIP_track_view_selected(sc, track):
28     if track.select_anchor:
29         return True
30
31     if sc.show_marker_pattern and track.select_pattern:
32         return True
33
34     if sc.show_marker_search and track.select_search:
35         return True
36
37     return False
38
39
40 class CLIP_OT_track_to_empty(Operator):
41     """Create an Empty object which will be copying movement of active track"""
42
43     bl_idname = "clip.track_to_empty"
44     bl_label = "Link Empty to Track"
45     bl_options = {'UNDO', 'REGISTER'}
46
47     def _link_track(self, context, track):
48         sc = context.space_data
49         constraint = None
50         ob = None
51
52         ob = bpy.data.objects.new(name=track.name, object_data=None)
53         ob.select = True
54         bpy.context.scene.objects.link(ob)
55         bpy.context.scene.objects.active = ob
56
57         for con in ob.constraints:
58             if con.type == 'FOLLOW_TRACK':
59                 constraint = con
60                 break
61
62         if constraint is None:
63             constraint = ob.constraints.new(type='FOLLOW_TRACK')
64
65         constraint.clip = sc.clip
66         constraint.track = track.name
67         constraint.use_3d_position = False
68
69     def execute(self, context):
70         sc = context.space_data
71         clip = sc.clip
72
73         for track in clip.tracking.tracks:
74             if CLIP_track_view_selected(sc, track):
75                 self._link_track(context, track)
76
77         return {'FINISHED'}
78
79
80 class CLIP_OT_tracks_to_mesh(Operator):
81     """Create vertex cloud using coordinates of tracks"""
82
83     bl_idname = "clip.tracks_to_mesh"
84     bl_label = "Tracks to Mesh"
85     bl_options = {'UNDO', 'REGISTER'}
86
87     @classmethod
88     def poll(cls, context):
89         if context.space_data.type != 'CLIP_EDITOR':
90             return False
91
92         sc = context.space_data
93         clip = sc.clip
94
95         return clip
96
97     def execute(self, context):
98         sc = context.space_data
99         clip = sc.clip
100
101         new_verts = []
102
103         mesh = bpy.data.meshes.new(name="Tracks")
104         for track in clip.tracking.tracks:
105             if track.has_bundle:
106                 new_verts.append(track.bundle)
107
108         if new_verts:
109             mesh.vertices.add(len(new_verts))
110             mesh.vertices.foreach_set("co", unpack_list(new_verts))
111
112         ob = bpy.data.objects.new(name="Tracks", object_data=mesh)
113
114         bpy.context.scene.objects.link(ob)
115
116         return {'FINISHED'}
117
118
119 class CLIP_OT_delete_proxy(Operator):
120     """Delete movie clip proxy files from the hard drive"""
121
122     bl_idname = "clip.delete_proxy"
123     bl_label = "Delete Proxy"
124     bl_options = {'UNDO', 'REGISTER'}
125
126     @classmethod
127     def poll(cls, context):
128         if context.space_data.type != 'CLIP_EDITOR':
129             return False
130
131         sc = context.space_data
132
133         return sc.clip
134
135     def invoke(self, context, event):
136         wm = context.window_manager
137
138         return wm.invoke_confirm(self, event)
139
140     def _rmproxy(self, abspath):
141         if not os.path.exists(abspath):
142             return
143
144         if os.path.isdir(abspath):
145             shutil.rmtree(abspath)
146         else:
147             os.remove(abspath)
148
149     def execute(self, context):
150         sc = context.space_data
151         clip = sc.clip
152         if clip.use_proxy_custom_directory:
153             proxydir = clip.proxy.directory
154         else:
155             clipdir = os.path.dirname(clip.filepath)
156             proxydir = os.path.join(clipdir, 'BL_proxy')
157
158         clipfile = os.path.basename(clip.filepath)
159         proxy = os.path.join(proxydir, clipfile)
160         absproxy = bpy.path.abspath(proxy)
161
162         # proxy_<quality>[_undostorted]
163         for x in (25, 50, 75, 100):
164             d = os.path.join(absproxy, 'proxy_' + str(x))
165
166             self._rmproxy(d)
167             self._rmproxy(d + '_undistorted')
168             self._rmproxy(os.path.join(absproxy, 'proxy_' + str(x) + '.avi'))
169
170         tc = ('free_run.blen_tc',
171               'interp_free_run.blen_tc',
172               'record_run.blen_tc')
173
174         for x in tc:
175             self._rmproxy(os.path.join(absproxy, x))
176
177         # remove proxy per-clip directory
178         try:
179             os.rmdir(absproxy)
180         except OSError:
181             pass
182
183         # remove [custom] proxy directory if empty
184         try:
185             absdir = bpy.path.abspath(proxydir)
186             os.rmdir(absdir)
187         except OSError:
188             pass
189
190         return {'FINISHED'}
191
192
193 class CLIP_OT_set_viewport_background(Operator):
194     """Set current movie clip as a camera background in 3D viewport \
195 (works only when a 3D viewport is visible)"""
196
197     bl_idname = "clip.set_viewport_background"
198     bl_label = "Set as Background"
199     bl_options = {'UNDO', 'REGISTER'}
200
201     @classmethod
202     def poll(cls, context):
203         if context.space_data.type != 'CLIP_EDITOR':
204             return False
205
206         sc = context.space_data
207
208         return sc.clip
209
210     def _set_background(self, space_v3d, clip, user):
211         bgpic = None
212
213         for x in space_v3d.background_images:
214             if x.source == 'MOVIE':
215                 bgpic = x
216                 break
217
218         if not bgpic:
219             bgpic = space_v3d.background_images.add()
220
221         bgpic.source = 'MOVIE'
222         bgpic.clip = clip
223         bgpic.clip_user.proxy_render_size = user.proxy_render_size
224         bgpic.clip_user.use_render_undistorted = user.use_render_undistorted
225         bgpic.use_camera_clip = False
226         bgpic.view_axis = 'CAMERA'
227
228         space_v3d.show_background_images = True
229
230     def execute(self, context):
231         sc = context.space_data
232         clip = sc.clip
233
234         for area in context.window.screen.areas:
235             if area.type == 'VIEW_3D':
236                 for space in area.spaces:
237                     if space.type == 'VIEW_3D':
238                         self._set_background(space, clip, sc.clip_user)
239
240         return {'FINISHED'}
241
242
243 class CLIP_OT_constraint_to_fcurve(Operator):
244     """Create F-Curves for object which will copy \
245 object's movement caused by this constraint"""
246
247     bl_idname = "clip.constraint_to_fcurve"
248     bl_label = "Constraint to F-Curve"
249     bl_options = {'UNDO', 'REGISTER'}
250
251     def _bake_object(self, scene, ob):
252         con = None
253         clip = None
254         sfra = None
255         efra = None
256         frame_current = scene.frame_current
257         matrices = []
258
259         # Find constraint which would eb converting
260         # TODO: several camera solvers and track followers would fail,
261         #       but can't think about eal workflow where it'll be useful
262         for x in ob.constraints:
263             if x.type in ('CAMERA_SOLVER', 'FOLLOW_TRACK'):
264                 con = x
265
266         if not con:
267             return
268
269         if con.type == 'FOLLOW_TRACK' and con.use_3d_position:
270             mat = ob.matrix_world.copy()
271             ob.constraints.remove(con)
272             ob.matrix_world = mat
273
274             return
275
276         # Get clip used for parenting
277         if con.use_active_clip:
278             clip = scene.active_clip
279         else:
280             clip = con.clip
281
282         if not clip:
283             return
284
285         # Find start and end frames
286         for track in clip.tracking.tracks:
287             if sfra is None:
288                 sfra = track.markers[0].frame
289             else:
290                 sfra = min(sfra, track.markers[0].frame)
291
292             if efra is None:
293                 efra = track.markers[-1].frame
294             else:
295                 efra = max(efra, track.markers[-1].frame)
296
297         if sfra is None or efra is None:
298             return
299
300         # Store object matrices
301         for x in range(sfra, efra + 1):
302             scene.frame_set(x)
303             matrices.append(ob.matrix_world.copy())
304
305         ob.animation_data_create()
306
307         # Apply matrices on object and insert keyframes
308         i = 0
309         for x in range(sfra, efra + 1):
310             scene.frame_set(x)
311             ob.matrix_world = matrices[i]
312
313             ob.keyframe_insert("location")
314
315             if ob.rotation_mode == 'QUATERNION':
316                 ob.keyframe_insert("rotation_quaternion")
317             else:
318                 ob.keyframe_insert("rotation_euler")
319
320             i += 1
321
322         ob.constraints.remove(con)
323
324         scene.frame_set(frame_current)
325
326     def execute(self, context):
327         scene = context.scene
328
329         for ob in scene.objects:
330             if ob.select:
331                 self._bake_object(scene, ob)
332
333         return {'FINISHED'}