/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2016 Blender Foundation. All rights reserved. */ /** \file * \ingroup spclip */ #include "MEM_guardedalloc.h" #include "DNA_camera_types.h" #include "DNA_object_types.h" /* SELECT */ #include "DNA_screen_types.h" #include "DNA_space_types.h" #include "BLI_string.h" #include "BLI_utildefines.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_lib_id.h" #include "BKE_movieclip.h" #include "BKE_report.h" #include "BKE_tracking.h" #include "DEG_depsgraph.h" #include "WM_api.h" #include "WM_types.h" #include "ED_clip.h" #include "clip_intern.h" /********************** solve camera operator *********************/ typedef struct { struct wmWindowManager *wm; Scene *scene; MovieClip *clip; MovieClipUser user; ReportList *reports; char stats_message[256]; struct MovieReconstructContext *context; } SolveCameraJob; static bool solve_camera_initjob( bContext *C, SolveCameraJob *scj, wmOperator *op, char *error_msg, int max_error) { SpaceClip *sc = CTX_wm_space_clip(C); MovieClip *clip = ED_space_clip_get_clip(sc); Scene *scene = CTX_data_scene(C); MovieTracking *tracking = &clip->tracking; MovieTrackingObject *object = BKE_tracking_object_get_active(tracking); int width, height; if (!BKE_tracking_reconstruction_check(tracking, object, error_msg, max_error)) { return false; } /* Could fail if footage uses images with different sizes. */ BKE_movieclip_get_size(clip, &sc->user, &width, &height); scj->wm = CTX_wm_manager(C); scj->clip = clip; scj->scene = scene; scj->reports = op->reports; scj->user = sc->user; scj->context = BKE_tracking_reconstruction_context_new( clip, object, object->keyframe1, object->keyframe2, width, height); tracking->stats = MEM_callocN(sizeof(MovieTrackingStats), "solve camera stats"); WM_set_locked_interface(scj->wm, true); return true; } static void solve_camera_updatejob(void *scv) { SolveCameraJob *scj = (SolveCameraJob *)scv; MovieTracking *tracking = &scj->clip->tracking; BLI_strncpy(tracking->stats->message, scj->stats_message, sizeof(tracking->stats->message)); } static void solve_camera_startjob(void *scv, bool *stop, bool *do_update, float *progress) { SolveCameraJob *scj = (SolveCameraJob *)scv; BKE_tracking_reconstruction_solve( scj->context, stop, do_update, progress, scj->stats_message, sizeof(scj->stats_message)); } static void solve_camera_freejob(void *scv) { SolveCameraJob *scj = (SolveCameraJob *)scv; MovieTracking *tracking = &scj->clip->tracking; Scene *scene = scj->scene; MovieClip *clip = scj->clip; int solved; /* WindowManager is missing in the job when initialization is incomplete. * In this case the interface is not locked either. */ if (scj->wm != NULL) { WM_set_locked_interface(scj->wm, false); } if (!scj->context) { /* job weren't fully initialized due to some error */ MEM_freeN(scj); return; } solved = BKE_tracking_reconstruction_finish(scj->context, tracking); if (!solved) { const char *error_message = BKE_tracking_reconstruction_error_message_get(scj->context); if (error_message[0]) { BKE_report(scj->reports, RPT_ERROR, error_message); } else { BKE_report( scj->reports, RPT_WARNING, "Some data failed to reconstruct (see console for details)"); } } else { BKE_reportf(scj->reports, RPT_INFO, "Average re-projection error: %.2f px", BKE_tracking_get_active_reconstruction(tracking)->error); } /* Set currently solved clip as active for scene. */ if (scene->clip != NULL) { id_us_min(&clip->id); } scene->clip = clip; id_us_plus(&clip->id); /* Set blender camera focal length so result would look fine there. */ if (scene->camera != NULL && scene->camera->data && GS(((ID *)scene->camera->data)->name) == ID_CA) { Camera *camera = (Camera *)scene->camera->data; int width, height; BKE_movieclip_get_size(clip, &scj->user, &width, &height); BKE_tracking_camera_to_blender(tracking, scene, camera, width, height); DEG_id_tag_update(&camera->id, ID_RECALC_COPY_ON_WRITE); WM_main_add_notifier(NC_OBJECT, camera); } MEM_freeN(tracking->stats); tracking->stats = NULL; DEG_id_tag_update(&clip->id, 0); WM_main_add_notifier(NC_MOVIECLIP | NA_EVALUATED, clip); WM_main_add_notifier(NC_OBJECT | ND_TRANSFORM, NULL); /* Update active clip displayed in scene buttons. */ WM_main_add_notifier(NC_SCENE, scene); BKE_tracking_reconstruction_context_free(scj->context); MEM_freeN(scj); } static int solve_camera_exec(bContext *C, wmOperator *op) { SolveCameraJob *scj; char error_msg[256] = "\0"; scj = MEM_callocN(sizeof(SolveCameraJob), "SolveCameraJob data"); if (!solve_camera_initjob(C, scj, op, error_msg, sizeof(error_msg))) { if (error_msg[0]) { BKE_report(op->reports, RPT_ERROR, error_msg); } solve_camera_freejob(scj); return OPERATOR_CANCELLED; } solve_camera_startjob(scj, NULL, NULL, NULL); solve_camera_freejob(scj); return OPERATOR_FINISHED; } static int solve_camera_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { SolveCameraJob *scj; SpaceClip *sc = CTX_wm_space_clip(C); MovieClip *clip = ED_space_clip_get_clip(sc); MovieTracking *tracking = &clip->tracking; MovieTrackingReconstruction *reconstruction = BKE_tracking_get_active_reconstruction(tracking); wmJob *wm_job; char error_msg[256] = "\0"; if (WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C), WM_JOB_TYPE_CLIP_SOLVE_CAMERA)) { /* only one solve is allowed at a time */ return OPERATOR_CANCELLED; } scj = MEM_callocN(sizeof(SolveCameraJob), "SolveCameraJob data"); if (!solve_camera_initjob(C, scj, op, error_msg, sizeof(error_msg))) { if (error_msg[0]) { BKE_report(op->reports, RPT_ERROR, error_msg); } solve_camera_freejob(scj); return OPERATOR_CANCELLED; } BLI_strncpy(tracking->stats->message, "Solving camera | Preparing solve", sizeof(tracking->stats->message)); /* Hide reconstruction statistics from previous solve. */ reconstruction->flag &= ~TRACKING_RECONSTRUCTED; WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip); /* Setup job. */ wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), CTX_data_scene(C), "Solve Camera", WM_JOB_PROGRESS, WM_JOB_TYPE_CLIP_SOLVE_CAMERA); WM_jobs_customdata_set(wm_job, scj, solve_camera_freejob); WM_jobs_timer(wm_job, 0.1, NC_MOVIECLIP | NA_EVALUATED, 0); WM_jobs_callbacks(wm_job, solve_camera_startjob, NULL, solve_camera_updatejob, NULL); G.is_break = false; WM_jobs_start(CTX_wm_manager(C), wm_job); WM_cursor_wait(false); /* add modal handler for ESC */ WM_event_add_modal_handler(C, op); return OPERATOR_RUNNING_MODAL; } static int solve_camera_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { /* No running solver, remove handler and pass through. */ if (0 == WM_jobs_test(CTX_wm_manager(C), CTX_wm_area(C), WM_JOB_TYPE_CLIP_SOLVE_CAMERA)) { return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; } /* Running solver. */ switch (event->type) { case EVT_ESCKEY: return OPERATOR_RUNNING_MODAL; } return OPERATOR_PASS_THROUGH; } void CLIP_OT_solve_camera(wmOperatorType *ot) { /* identifiers */ ot->name = "Solve Camera"; ot->description = "Solve camera motion from tracks"; ot->idname = "CLIP_OT_solve_camera"; /* api callbacks */ ot->exec = solve_camera_exec; ot->invoke = solve_camera_invoke; ot->modal = solve_camera_modal; ot->poll = ED_space_clip_tracking_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /********************** clear solution operator *********************/ static int clear_solution_exec(bContext *C, wmOperator *UNUSED(op)) { SpaceClip *sc = CTX_wm_space_clip(C); MovieClip *clip = ED_space_clip_get_clip(sc); MovieTracking *tracking = &clip->tracking; ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking); MovieTrackingReconstruction *reconstruction = BKE_tracking_get_active_reconstruction(tracking); for (MovieTrackingTrack *track = tracksbase->first; track != NULL; track = track->next) { track->flag &= ~TRACK_HAS_BUNDLE; } MEM_SAFE_FREE(reconstruction->cameras); reconstruction->camnr = 0; reconstruction->flag &= ~TRACKING_RECONSTRUCTED; DEG_id_tag_update(&clip->id, 0); WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL); return OPERATOR_FINISHED; } void CLIP_OT_clear_solution(wmOperatorType *ot) { /* identifiers */ ot->name = "Clear Solution"; ot->description = "Clear all calculated data"; ot->idname = "CLIP_OT_clear_solution"; /* api callbacks */ ot->exec = clear_solution_exec; ot->poll = ED_space_clip_tracking_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; }