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