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