1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2016 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup spclip
22 */
23
24 #include "MEM_guardedalloc.h"
25
26 #include "DNA_camera_types.h"
27 #include "DNA_object_types.h" /* SELECT */
28 #include "DNA_screen_types.h"
29 #include "DNA_space_types.h"
30
31 #include "BLI_string.h"
32 #include "BLI_utildefines.h"
33
34 #include "BKE_context.h"
35 #include "BKE_global.h"
36 #include "BKE_lib_id.h"
37 #include "BKE_movieclip.h"
38 #include "BKE_report.h"
39 #include "BKE_tracking.h"
40
41 #include "DEG_depsgraph.h"
42
43 #include "WM_api.h"
44 #include "WM_types.h"
45
46 #include "ED_clip.h"
47
48 #include "clip_intern.h"
49
50 /********************** solve camera operator *********************/
51
52 typedef struct {
53 struct wmWindowManager *wm;
54 Scene *scene;
55 MovieClip *clip;
56 MovieClipUser user;
57
58 ReportList *reports;
59
60 char stats_message[256];
61
62 struct MovieReconstructContext *context;
63 } SolveCameraJob;
64
solve_camera_initjob(bContext * C,SolveCameraJob * scj,wmOperator * op,char * error_msg,int max_error)65 static bool solve_camera_initjob(
66 bContext *C, SolveCameraJob *scj, wmOperator *op, char *error_msg, int max_error)
67 {
68 SpaceClip *sc = CTX_wm_space_clip(C);
69 MovieClip *clip = ED_space_clip_get_clip(sc);
70 Scene *scene = CTX_data_scene(C);
71 MovieTracking *tracking = &clip->tracking;
72 MovieTrackingObject *object = BKE_tracking_object_get_active(tracking);
73 int width, height;
74
75 if (!BKE_tracking_reconstruction_check(tracking, object, error_msg, max_error)) {
76 return false;
77 }
78
79 /* Could fail if footage uses images with different sizes. */
80 BKE_movieclip_get_size(clip, &sc->user, &width, &height);
81
82 scj->wm = CTX_wm_manager(C);
83 scj->clip = clip;
84 scj->scene = scene;
85 scj->reports = op->reports;
86 scj->user = sc->user;
87
88 scj->context = BKE_tracking_reconstruction_context_new(
89 clip, object, object->keyframe1, object->keyframe2, width, height);
90
91 tracking->stats = MEM_callocN(sizeof(MovieTrackingStats), "solve camera stats");
92
93 WM_set_locked_interface(scj->wm, true);
94
95 return true;
96 }
97
solve_camera_updatejob(void * scv)98 static void solve_camera_updatejob(void *scv)
99 {
100 SolveCameraJob *scj = (SolveCameraJob *)scv;
101 MovieTracking *tracking = &scj->clip->tracking;
102
103 BLI_strncpy(tracking->stats->message, scj->stats_message, sizeof(tracking->stats->message));
104 }
105
solve_camera_startjob(void * scv,short * stop,short * do_update,float * progress)106 static void solve_camera_startjob(void *scv, short *stop, short *do_update, float *progress)
107 {
108 SolveCameraJob *scj = (SolveCameraJob *)scv;
109 BKE_tracking_reconstruction_solve(
110 scj->context, stop, do_update, progress, scj->stats_message, sizeof(scj->stats_message));
111 }
112
solve_camera_freejob(void * scv)113 static void solve_camera_freejob(void *scv)
114 {
115 SolveCameraJob *scj = (SolveCameraJob *)scv;
116 MovieTracking *tracking = &scj->clip->tracking;
117 Scene *scene = scj->scene;
118 MovieClip *clip = scj->clip;
119 int solved;
120
121 /* WindowManager is missing in the job when initialization is incomplete.
122 * In this case the interface is not locked either. */
123 if (scj->wm != NULL) {
124 WM_set_locked_interface(scj->wm, false);
125 }
126
127 if (!scj->context) {
128 /* job weren't fully initialized due to some error */
129 MEM_freeN(scj);
130 return;
131 }
132
133 solved = BKE_tracking_reconstruction_finish(scj->context, tracking);
134 if (!solved) {
135 const char *error_message = BKE_tracking_reconstruction_error_message_get(scj->context);
136 if (error_message[0]) {
137 BKE_report(scj->reports, RPT_ERROR, error_message);
138 }
139 else {
140 BKE_report(
141 scj->reports, RPT_WARNING, "Some data failed to reconstruct (see console for details)");
142 }
143 }
144 else {
145 BKE_reportf(scj->reports,
146 RPT_INFO,
147 "Average re-projection error: %.2f px",
148 tracking->reconstruction.error);
149 }
150
151 /* Set currently solved clip as active for scene. */
152 if (scene->clip != NULL) {
153 id_us_min(&clip->id);
154 }
155 scene->clip = clip;
156 id_us_plus(&clip->id);
157
158 /* Set blender camera focal length so result would look fine there. */
159 if (scene->camera != NULL && scene->camera->data &&
160 GS(((ID *)scene->camera->data)->name) == ID_CA) {
161 Camera *camera = (Camera *)scene->camera->data;
162 int width, height;
163 BKE_movieclip_get_size(clip, &scj->user, &width, &height);
164 BKE_tracking_camera_to_blender(tracking, scene, camera, width, height);
165 DEG_id_tag_update(&camera->id, ID_RECALC_COPY_ON_WRITE);
166 WM_main_add_notifier(NC_OBJECT, camera);
167 }
168
169 MEM_freeN(tracking->stats);
170 tracking->stats = NULL;
171
172 DEG_id_tag_update(&clip->id, 0);
173
174 WM_main_add_notifier(NC_MOVIECLIP | NA_EVALUATED, clip);
175 WM_main_add_notifier(NC_OBJECT | ND_TRANSFORM, NULL);
176
177 /* Update active clip displayed in scene buttons. */
178 WM_main_add_notifier(NC_SCENE, scene);
179
180 BKE_tracking_reconstruction_context_free(scj->context);
181 MEM_freeN(scj);
182 }
183
solve_camera_exec(bContext * C,wmOperator * op)184 static int solve_camera_exec(bContext *C, wmOperator *op)
185 {
186 SolveCameraJob *scj;
187 char error_msg[256] = "\0";
188 scj = MEM_callocN(sizeof(SolveCameraJob), "SolveCameraJob data");
189 if (!solve_camera_initjob(C, scj, op, error_msg, sizeof(error_msg))) {
190 if (error_msg[0]) {
191 BKE_report(op->reports, RPT_ERROR, error_msg);
192 }
193 solve_camera_freejob(scj);
194 return OPERATOR_CANCELLED;
195 }
196 solve_camera_startjob(scj, NULL, NULL, NULL);
197 solve_camera_freejob(scj);
198 return OPERATOR_FINISHED;
199 }
200
solve_camera_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))201 static int solve_camera_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
202 {
203 SolveCameraJob *scj;
204 SpaceClip *sc = CTX_wm_space_clip(C);
205 MovieClip *clip = ED_space_clip_get_clip(sc);
206 MovieTracking *tracking = &clip->tracking;
207 MovieTrackingReconstruction *reconstruction = BKE_tracking_get_active_reconstruction(tracking);
208 wmJob *wm_job;
209 char error_msg[256] = "\0";
210
211 if (WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C), WM_JOB_TYPE_CLIP_SOLVE_CAMERA)) {
212 /* only one solve is allowed at a time */
213 return OPERATOR_CANCELLED;
214 }
215
216 scj = MEM_callocN(sizeof(SolveCameraJob), "SolveCameraJob data");
217 if (!solve_camera_initjob(C, scj, op, error_msg, sizeof(error_msg))) {
218 if (error_msg[0]) {
219 BKE_report(op->reports, RPT_ERROR, error_msg);
220 }
221 solve_camera_freejob(scj);
222 return OPERATOR_CANCELLED;
223 }
224
225 BLI_strncpy(tracking->stats->message,
226 "Solving camera | Preparing solve",
227 sizeof(tracking->stats->message));
228
229 /* Hide reconstruction statistics from previous solve. */
230 reconstruction->flag &= ~TRACKING_RECONSTRUCTED;
231 WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
232
233 /* Setup job. */
234 wm_job = WM_jobs_get(CTX_wm_manager(C),
235 CTX_wm_window(C),
236 CTX_data_scene(C),
237 "Solve Camera",
238 WM_JOB_PROGRESS,
239 WM_JOB_TYPE_CLIP_SOLVE_CAMERA);
240 WM_jobs_customdata_set(wm_job, scj, solve_camera_freejob);
241 WM_jobs_timer(wm_job, 0.1, NC_MOVIECLIP | NA_EVALUATED, 0);
242 WM_jobs_callbacks(wm_job, solve_camera_startjob, NULL, solve_camera_updatejob, NULL);
243
244 G.is_break = false;
245
246 WM_jobs_start(CTX_wm_manager(C), wm_job);
247 WM_cursor_wait(0);
248
249 /* add modal handler for ESC */
250 WM_event_add_modal_handler(C, op);
251
252 return OPERATOR_RUNNING_MODAL;
253 }
254
solve_camera_modal(bContext * C,wmOperator * UNUSED (op),const wmEvent * event)255 static int solve_camera_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
256 {
257 /* No running solver, remove handler and pass through. */
258 if (0 == WM_jobs_test(CTX_wm_manager(C), CTX_wm_area(C), WM_JOB_TYPE_CLIP_SOLVE_CAMERA)) {
259 return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
260 }
261
262 /* Running solver. */
263 switch (event->type) {
264 case EVT_ESCKEY:
265 return OPERATOR_RUNNING_MODAL;
266 }
267
268 return OPERATOR_PASS_THROUGH;
269 }
270
CLIP_OT_solve_camera(wmOperatorType * ot)271 void CLIP_OT_solve_camera(wmOperatorType *ot)
272 {
273 /* identifiers */
274 ot->name = "Solve Camera";
275 ot->description = "Solve camera motion from tracks";
276 ot->idname = "CLIP_OT_solve_camera";
277
278 /* api callbacks */
279 ot->exec = solve_camera_exec;
280 ot->invoke = solve_camera_invoke;
281 ot->modal = solve_camera_modal;
282 ot->poll = ED_space_clip_tracking_poll;
283
284 /* flags */
285 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
286 }
287
288 /********************** clear solution operator *********************/
289
clear_solution_exec(bContext * C,wmOperator * UNUSED (op))290 static int clear_solution_exec(bContext *C, wmOperator *UNUSED(op))
291 {
292 SpaceClip *sc = CTX_wm_space_clip(C);
293 MovieClip *clip = ED_space_clip_get_clip(sc);
294 MovieTracking *tracking = &clip->tracking;
295 ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
296 MovieTrackingReconstruction *reconstruction = BKE_tracking_get_active_reconstruction(tracking);
297
298 for (MovieTrackingTrack *track = tracksbase->first; track != NULL; track = track->next) {
299 track->flag &= ~TRACK_HAS_BUNDLE;
300 }
301
302 if (reconstruction->cameras != NULL) {
303 MEM_freeN(reconstruction->cameras);
304 reconstruction->cameras = NULL;
305 }
306
307 reconstruction->camnr = 0;
308 reconstruction->flag &= ~TRACKING_RECONSTRUCTED;
309
310 DEG_id_tag_update(&clip->id, 0);
311
312 WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
313 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
314
315 return OPERATOR_FINISHED;
316 }
317
CLIP_OT_clear_solution(wmOperatorType * ot)318 void CLIP_OT_clear_solution(wmOperatorType *ot)
319 {
320 /* identifiers */
321 ot->name = "Clear Solution";
322 ot->description = "Clear all calculated data";
323 ot->idname = "CLIP_OT_clear_solution";
324
325 /* api callbacks */
326 ot->exec = clear_solution_exec;
327 ot->poll = ED_space_clip_tracking_poll;
328
329 /* flags */
330 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
331 }
332