diff options
-rw-r--r-- | source/blender/editors/space_clip/CMakeLists.txt | 8 | ||||
-rw-r--r-- | source/blender/editors/space_clip/tracking_ops.c | 2356 | ||||
-rw-r--r-- | source/blender/editors/space_clip/tracking_ops_detect.c | 171 | ||||
-rw-r--r-- | source/blender/editors/space_clip/tracking_ops_intern.h | 45 | ||||
-rw-r--r-- | source/blender/editors/space_clip/tracking_ops_orient.c | 860 | ||||
-rw-r--r-- | source/blender/editors/space_clip/tracking_ops_plane.c | 432 | ||||
-rw-r--r-- | source/blender/editors/space_clip/tracking_ops_solve.c | 351 | ||||
-rw-r--r-- | source/blender/editors/space_clip/tracking_ops_stabilize.c | 242 | ||||
-rw-r--r-- | source/blender/editors/space_clip/tracking_ops_track.c | 579 | ||||
-rw-r--r-- | source/blender/editors/space_clip/tracking_ops_utils.c | 79 |
10 files changed, 2778 insertions, 2345 deletions
diff --git a/source/blender/editors/space_clip/CMakeLists.txt b/source/blender/editors/space_clip/CMakeLists.txt index d17d1856502..32d48c9c564 100644 --- a/source/blender/editors/space_clip/CMakeLists.txt +++ b/source/blender/editors/space_clip/CMakeLists.txt @@ -53,9 +53,17 @@ set(SRC clip_utils.c space_clip.c tracking_ops.c + tracking_ops_detect.c + tracking_ops_orient.c + tracking_ops_plane.c + tracking_ops_solve.c + tracking_ops_track.c + tracking_ops_stabilize.c + tracking_ops_utils.c tracking_select.c clip_intern.h + tracking_ops_intern.h ) add_definitions(${GL_DEFINITIONS}) diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c index 088c316c9ff..af1a5404ee3 100644 --- a/source/blender/editors/space_clip/tracking_ops.c +++ b/source/blender/editors/space_clip/tracking_ops.c @@ -31,28 +31,18 @@ #include "MEM_guardedalloc.h" -#include "DNA_camera_types.h" -#include "DNA_constraint_types.h" -#include "DNA_gpencil_types.h" -#include "DNA_movieclip_types.h" -#include "DNA_object_types.h" /* SELECT */ -#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" #include "BLI_utildefines.h" #include "BLI_math.h" -#include "BLI_listbase.h" #include "BLI_blenlib.h" -#include "BKE_main.h" #include "BKE_context.h" -#include "BKE_constraint.h" #include "BKE_movieclip.h" #include "BKE_tracking.h" -#include "BKE_global.h" #include "BKE_depsgraph.h" -#include "BKE_object.h" #include "BKE_report.h" -#include "BKE_library.h" #include "BKE_sound.h" #include "WM_api.h" @@ -61,19 +51,13 @@ #include "ED_screen.h" #include "ED_clip.h" -#include "IMB_imbuf_types.h" -#include "IMB_imbuf.h" - - #include "RNA_access.h" #include "RNA_define.h" #include "BLT_translation.h" -#include "PIL_time.h" - - -#include "clip_intern.h" // own include +#include "clip_intern.h" +#include "tracking_ops_intern.h" /********************** add marker operator *********************/ @@ -117,7 +101,9 @@ static int add_marker_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* reset offset from locked position, so frame jumping wouldn't be so confusing */ + /* Reset offset from locked position, so frame jumping wouldn't be so + * confusing. + */ sc->xlockof = 0; sc->ylockof = 0; @@ -599,20 +585,6 @@ static bool slide_check_corners(float (*corners)[2]) return true; } -static void hide_cursor(bContext *C) -{ - wmWindow *win = CTX_wm_window(C); - - WM_cursor_set(win, CURSOR_NONE); -} - -static void show_cursor(bContext *C) -{ - wmWindow *win = CTX_wm_window(C); - - WM_cursor_set(win, CURSOR_STD); -} - MovieTrackingTrack *tracking_marker_check_slide(bContext *C, const wmEvent *event, int *area_r, @@ -816,7 +788,7 @@ static int slide_marker_invoke(bContext *C, wmOperator *op, const wmEvent *event op->customdata = slidedata; - hide_cursor(C); + clip_tracking_hide_cursor(C); WM_event_add_modal_handler(C, op); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); @@ -1084,7 +1056,7 @@ static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event) apply_mouse_slide(C, op->customdata); free_slide_data(op->customdata); - show_cursor(C); + clip_tracking_show_cursor(C); return OPERATOR_FINISHED; } @@ -1096,7 +1068,7 @@ static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event) free_slide_data(op->customdata); - show_cursor(C); + clip_tracking_show_cursor(C); WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, NULL); @@ -1128,821 +1100,6 @@ void CLIP_OT_slide_marker(wmOperatorType *ot) -FLT_MAX, FLT_MAX); } -/********************** track operator *********************/ - -typedef struct TrackMarkersJob { - struct AutoTrackContext *context; /* Tracking context */ - int sfra, efra, lastfra; /* Start, end and recently tracked frames */ - int backwards; /* Backwards tracking flag */ - MovieClip *clip; /* Clip which is tracking */ - float delay; /* Delay in milliseconds to allow tracking at fixed FPS */ - - struct Main *main; - struct Scene *scene; - struct bScreen *screen; -} TrackMarkersJob; - -static bool track_markers_testbreak(void) -{ - return G.is_break; -} - -static int track_count_markers(SpaceClip *sc, - MovieClip *clip, - int framenr) -{ - int tot = 0; - ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking); - for (MovieTrackingTrack *track = tracksbase->first; - track != NULL; - track = track->next) - { - bool selected = sc ? TRACK_VIEW_SELECTED(sc, track) : TRACK_SELECTED(track); - if (selected && (track->flag & TRACK_LOCKED) == 0) { - MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); - if (!marker || (marker->flag & MARKER_DISABLED) == 0) { - tot++; - } - } - } - return tot; -} - -static void clear_invisible_track_selection(SpaceClip *sc, MovieClip *clip) -{ - ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking); - int hidden = 0; - if ((sc->flag & SC_SHOW_MARKER_PATTERN) == 0) { - hidden |= TRACK_AREA_PAT; - } - if ((sc->flag & SC_SHOW_MARKER_SEARCH) == 0) { - hidden |= TRACK_AREA_SEARCH; - } - if (hidden) { - for (MovieTrackingTrack *track = tracksbase->first; - track != NULL; - track = track->next) - { - if ((track->flag & TRACK_HIDDEN) == 0) { - BKE_tracking_track_flag_clear(track, hidden, SELECT); - } - } - } -} - -static void track_init_markers(SpaceClip *sc, - MovieClip *clip, - int framenr, - int *frames_limit_r) -{ - ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking); - int frames_limit = 0; - if (sc != NULL) { - clear_invisible_track_selection(sc, clip); - } - for (MovieTrackingTrack *track = tracksbase->first; - track != NULL; - track = track->next) - { - bool selected = (sc != NULL) ? TRACK_VIEW_SELECTED(sc, track) - : TRACK_SELECTED(track); - if (selected) { - if ((track->flag & TRACK_HIDDEN) == 0 && - (track->flag & TRACK_LOCKED) == 0) - { - BKE_tracking_marker_ensure(track, framenr); - if (track->frames_limit) { - if (frames_limit == 0) { - frames_limit = track->frames_limit; - } - else { - frames_limit = min_ii(frames_limit, - (int)track->frames_limit); - } - } - } - } - } - *frames_limit_r = frames_limit; -} - -static bool track_markers_check_direction(int backwards, int curfra, int efra) -{ - if (backwards) { - if (curfra < efra) { - return false; - } - } - else { - if (curfra > efra) { - return false; - } - } - - return true; -} - -static int track_markers_initjob(bContext *C, - TrackMarkersJob *tmj, - int backwards) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - Scene *scene = CTX_data_scene(C); - MovieTrackingSettings *settings = &clip->tracking.settings; - int frames_limit; - int framenr = ED_space_clip_get_clip_frame_number(sc); - - track_init_markers(sc, clip, framenr, &frames_limit); - - tmj->sfra = ED_space_clip_get_clip_frame_number(sc); - tmj->clip = clip; - tmj->backwards = backwards; - - if (backwards) { - tmj->efra = SFRA; - } - else { - tmj->efra = EFRA; - } - - /* Limit frames to be tracked by user setting. */ - if (frames_limit) { - if (backwards) { - tmj->efra = MAX2(tmj->efra, tmj->sfra - frames_limit); - } - else { - tmj->efra = MIN2(tmj->efra, tmj->sfra + frames_limit); - } - } - - tmj->efra = BKE_movieclip_remap_scene_to_clip_frame(clip, tmj->efra); - - if (settings->speed != TRACKING_SPEED_FASTEST) { - tmj->delay = 1.0f / scene->r.frs_sec * 1000.0f; - - if (settings->speed == TRACKING_SPEED_HALF) { - tmj->delay *= 2; - } - else if (settings->speed == TRACKING_SPEED_QUARTER) { - tmj->delay *= 4; - } - else if (settings->speed == TRACKING_SPEED_DOUBLE) { - tmj->delay /= 2; - } - } - - tmj->context = BKE_autotrack_context_new(clip, &sc->user, backwards, 1); - - clip->tracking_context = tmj->context; - - tmj->lastfra = tmj->sfra; - - /* XXX: silly to store this, but this data is needed to update scene and movie-clip - * frame numbers when tracking is finished. This introduces better feedback for artists. - * Maybe there's another way to solve this problem, but can't think better way atm. - * Anyway, this way isn't more unstable as animation rendering animation - * which uses the same approach (except storing screen). */ - tmj->scene = scene; - tmj->main = CTX_data_main(C); - tmj->screen = CTX_wm_screen(C); - - return track_markers_check_direction(backwards, tmj->sfra, tmj->efra); -} - -static void track_markers_startjob(void *tmv, - short *stop, - short *do_update, - float *progress) -{ - TrackMarkersJob *tmj = (TrackMarkersJob *)tmv; - int framenr = tmj->sfra; - - while (framenr != tmj->efra) { - if (tmj->delay > 0) { - /* Tracking should happen with fixed fps. Calculate time - * using current timer value before tracking frame and after. - * - * Small (and maybe unneeded optimization): do not calculate exec_time - * for "Fastest" tracking - */ - - double start_time = PIL_check_seconds_timer(), exec_time; - - if (!BKE_autotrack_context_step(tmj->context)) { - break; - } - - exec_time = PIL_check_seconds_timer() - start_time; - if (tmj->delay > (float)exec_time) { - PIL_sleep_ms(tmj->delay - (float)exec_time); - } - } - else if (!BKE_autotrack_context_step(tmj->context)) { - break; - } - - *do_update = true; - *progress = (float)(framenr - tmj->sfra) / (tmj->efra - tmj->sfra); - - if (tmj->backwards) { - framenr--; - } - else { - framenr++; - } - - tmj->lastfra = framenr; - - if (*stop || track_markers_testbreak()) { - break; - } - } -} - -static void track_markers_updatejob(void *tmv) -{ - TrackMarkersJob *tmj = (TrackMarkersJob *)tmv; - BKE_autotrack_context_sync(tmj->context); -} - -static void track_markers_endjob(void *tmv) -{ - TrackMarkersJob *tmj = (TrackMarkersJob *)tmv; - wmWindowManager *wm = tmj->main->wm.first; - - tmj->clip->tracking_context = NULL; - tmj->scene->r.cfra = BKE_movieclip_remap_clip_to_scene_frame(tmj->clip, - tmj->lastfra); - if (wm != NULL) { - ED_update_for_newframe(tmj->main, tmj->scene, 0); - } - - BKE_autotrack_context_sync(tmj->context); - BKE_autotrack_context_finish(tmj->context); - - WM_main_add_notifier(NC_SCENE | ND_FRAME, tmj->scene); -} - -static void track_markers_freejob(void *tmv) -{ - TrackMarkersJob *tmj = (TrackMarkersJob *)tmv; - BKE_autotrack_context_free(tmj->context); - MEM_freeN(tmj); -} - -/* TODO(sergey): Majority of the code here can be de-duplicated with the job. */ -static int track_markers_exec(bContext *C, wmOperator *op) -{ - SpaceClip *sc; - MovieClip *clip; - Scene *scene = CTX_data_scene(C); - struct AutoTrackContext *context; - MovieClipUser *user, fake_user = {0}; - int framenr, sfra, efra; - const bool backwards = RNA_boolean_get(op->ptr, "backwards"); - const bool sequence = RNA_boolean_get(op->ptr, "sequence"); - int frames_limit; - - if (RNA_struct_property_is_set(op->ptr, "clip")) { - Main *bmain = CTX_data_main(C); - char clip_name[MAX_ID_NAME - 2]; - - RNA_string_get(op->ptr, "clip", clip_name); - clip = (MovieClip *)BLI_findstring(&bmain->movieclip, clip_name, offsetof(ID, name) + 2); - sc = NULL; - - if (clip == NULL) { - return OPERATOR_CANCELLED; - } - framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, CFRA); - fake_user.framenr = framenr; - user = &fake_user; - } - else { - sc = CTX_wm_space_clip(C); - - if (sc == NULL) { - return OPERATOR_CANCELLED; - } - - clip = ED_space_clip_get_clip(sc); - framenr = ED_space_clip_get_clip_frame_number(sc); - user = &sc->user; - } - - sfra = framenr; - - if (track_count_markers(sc, clip, framenr) == 0) { - return OPERATOR_CANCELLED; - } - - track_init_markers(sc, clip, framenr, &frames_limit); - - if (backwards) { - efra = SFRA; - } - else { - efra = EFRA; - } - - /* Limit frames to be tracked by user setting. */ - if (frames_limit) { - if (backwards) - efra = MAX2(efra, sfra - frames_limit); - else - efra = MIN2(efra, sfra + frames_limit); - } - - efra = BKE_movieclip_remap_scene_to_clip_frame(clip, efra); - - if (!track_markers_check_direction(backwards, framenr, efra)) { - return OPERATOR_CANCELLED; - } - - /* Do not disable tracks due to threshold when tracking frame-by-frame. */ - context = BKE_autotrack_context_new(clip, user, backwards, sequence); - while (framenr != efra) { - if (!BKE_autotrack_context_step(context)) - break; - - if (backwards) framenr--; - else framenr++; - - if (!sequence) - break; - } - - BKE_autotrack_context_sync(context); - BKE_autotrack_context_finish(context); - BKE_autotrack_context_free(context); - - /* Update scene current frame to the lastes tracked frame. */ - scene->r.cfra = BKE_movieclip_remap_clip_to_scene_frame(clip, framenr); - - WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip); - WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); - - return OPERATOR_FINISHED; -} - -static int track_markers_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - TrackMarkersJob *tmj; - ScrArea *sa = CTX_wm_area(C); - SpaceClip *sc = CTX_wm_space_clip(C); - MovieClip *clip; - wmJob *wm_job; - bool backwards = RNA_boolean_get(op->ptr, "backwards"); - bool sequence = RNA_boolean_get(op->ptr, "sequence"); - int framenr; - - if (sc == NULL) { - /* TODO(sergey): Support clip for invoke as well. */ - BKE_report(op->reports, RPT_ERROR, - "Invoking this operator only supported from Clip Editor space"); - return OPERATOR_CANCELLED; - } - - clip = ED_space_clip_get_clip(sc); - BLI_assert(clip != NULL); - framenr = ED_space_clip_get_clip_frame_number(sc); - - if (WM_jobs_test(CTX_wm_manager(C), sa, WM_JOB_TYPE_ANY)) { - /* Only one tracking is allowed at a time. */ - return OPERATOR_CANCELLED; - } - - if (clip->tracking_context) { - return OPERATOR_CANCELLED; - } - - if (track_count_markers(sc, clip, framenr) == 0) { - return OPERATOR_CANCELLED; - } - - if (!sequence) { - return track_markers_exec(C, op); - } - - tmj = MEM_callocN(sizeof(TrackMarkersJob), "TrackMarkersJob data"); - if (!track_markers_initjob(C, tmj, backwards)) { - track_markers_freejob(tmj); - return OPERATOR_CANCELLED; - } - - /* Setup job. */ - wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), sa, "Track Markers", - WM_JOB_PROGRESS, WM_JOB_TYPE_CLIP_TRACK_MARKERS); - WM_jobs_customdata_set(wm_job, tmj, track_markers_freejob); - - /* If there's delay set in tracking job, tracking should happen - * with fixed FPS. To deal with editor refresh we have to synchronize - * tracks from job and tracks in clip. Do this in timer callback - * to prevent threading conflicts. */ - if (tmj->delay > 0) - WM_jobs_timer(wm_job, tmj->delay / 1000.0f, NC_MOVIECLIP | NA_EVALUATED, 0); - else - WM_jobs_timer(wm_job, 0.2, NC_MOVIECLIP | NA_EVALUATED, 0); - - WM_jobs_callbacks(wm_job, - track_markers_startjob, - NULL, - track_markers_updatejob, - track_markers_endjob); - - G.is_break = false; - - WM_jobs_start(CTX_wm_manager(C), wm_job); - WM_cursor_wait(0); - - /* Add modal handler for ESC. */ - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; -} - -static int track_markers_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) -{ - /* No running tracking, remove handler and pass through. */ - if (0 == WM_jobs_test(CTX_wm_manager(C), CTX_wm_area(C), WM_JOB_TYPE_ANY)) { - return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; - } - - /* Running tracking. */ - switch (event->type) { - case ESCKEY: - return OPERATOR_RUNNING_MODAL; - } - - return OPERATOR_PASS_THROUGH; -} - -void CLIP_OT_track_markers(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Track Markers"; - ot->description = "Track selected markers"; - ot->idname = "CLIP_OT_track_markers"; - - /* api callbacks */ - ot->exec = track_markers_exec; - ot->invoke = track_markers_invoke; - ot->modal = track_markers_modal; - ot->poll = ED_space_clip_tracking_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean(ot->srna, "backwards", 0, "Backwards", "Do backwards tracking"); - RNA_def_boolean(ot->srna, "sequence", 0, "Track Sequence", "Track marker during image sequence rather than single image"); - prop = RNA_def_string(ot->srna, "clip", NULL, MAX_NAME, "Movie Clip", "Movie Clip to be tracked"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); -} - -/********************** refine track position operator *********************/ - -static int refine_marker_exec(bContext *C, wmOperator *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(tracking); - bool backwards = RNA_boolean_get(op->ptr, "backwards"); - int framenr = ED_space_clip_get_clip_frame_number(sc); - - for (MovieTrackingTrack *track = tracksbase->first; - track != NULL; - track = track->next) - { - if (TRACK_VIEW_SELECTED(sc, track)) { - MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); - BKE_tracking_refine_marker(clip, track, marker, backwards); - } - } - - WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip); - - return OPERATOR_FINISHED; -} - -void CLIP_OT_refine_markers(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Refine Markers"; - ot->description = "Refine selected markers positions " - "by running the tracker from track's reference to current frame"; - ot->idname = "CLIP_OT_refine_markers"; - - /* api callbacks */ - ot->exec = refine_marker_exec; - ot->poll = ED_space_clip_tracking_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean(ot->srna, "backwards", 0, "Backwards", "Do backwards tracking"); -} - -/********************** solve camera operator *********************/ - -typedef struct { - 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->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"); - - 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, short *stop, short *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; - - 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) { - 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: %.3f", - tracking->reconstruction.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); - WM_main_add_notifier(NC_OBJECT, camera); - } - - MEM_freeN(tracking->stats); - tracking->stats = NULL; - - DAG_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; - ScrArea *sa = CTX_wm_area(C); - 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), sa, WM_JOB_TYPE_ANY)) { - /* 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), sa, "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(0); - - /* 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_ANY)) - return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; - - /* Running solver. */ - switch (event->type) { - case 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; - } - - if (reconstruction->cameras != NULL) { - MEM_freeN(reconstruction->cameras); - reconstruction->cameras = NULL; - } - - reconstruction->camnr = 0; - reconstruction->flag &= ~TRACKING_RECONSTRUCTED; - - DAG_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; -} - /********************** clear track operator *********************/ static int clear_track_path_exec(bContext *C, wmOperator *op) @@ -2068,807 +1225,6 @@ void CLIP_OT_disable_markers(wmOperatorType *ot) RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Disable action to execute"); } -/********************** set origin operator *********************/ - -static Object *get_camera_with_movieclip(Scene *scene, MovieClip *clip) -{ - Object *camera = scene->camera; - - if (camera != NULL && - BKE_object_movieclip_get(scene, camera, false) == clip) - { - return camera; - } - - for (Base *base = scene->base.first; - base != NULL; - base = base->next) - { - if (base->object->type == OB_CAMERA) { - if (BKE_object_movieclip_get(scene, base->object, false) == clip) { - camera = base->object; - break; - } - } - } - - return camera; -} - -static Object *get_orientation_object(bContext *C) -{ - Scene *scene = CTX_data_scene(C); - SpaceClip *sc = CTX_wm_space_clip(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - MovieTracking *tracking = &clip->tracking; - MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking); - Object *object = NULL; - - if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { - object = get_camera_with_movieclip(scene, clip); - } - else { - object = OBACT; - } - - if (object != NULL && object->parent != NULL) { - object = object->parent; - } - - return object; -} - -static int set_orientation_poll(bContext *C) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - if (sc != NULL) { - Scene *scene = CTX_data_scene(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - if (clip != NULL) { - MovieTracking *tracking = &clip->tracking; - MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking); - if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { - return true; - } - else { - return OBACT != NULL; - } - } - } - return false; -} - -static int count_selected_bundles(bContext *C) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking); - int tot = 0; - for (MovieTrackingTrack *track = tracksbase->first; - track != NULL; - track = track->next) - { - if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_HAS_BUNDLE)) { - tot++; - } - } - return tot; -} - -static void object_solver_inverted_matrix(Scene *scene, - Object *ob, - float invmat[4][4]) -{ - bool found = false; - for (bConstraint *con = ob->constraints.first; - con != NULL; - con = con->next) - { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); - if (cti == NULL) { - continue; - } - if (cti->type == CONSTRAINT_TYPE_OBJECTSOLVER) { - bObjectSolverConstraint *data = (bObjectSolverConstraint *)con->data; - if (!found) { - Object *cam = data->camera ? data->camera : scene->camera; - BKE_object_where_is_calc_mat4(scene, cam, invmat); - } - mul_m4_m4m4(invmat, invmat, data->invmat); - found = true; - } - } - if (found) { - invert_m4(invmat); - } - else { - unit_m4(invmat); - } -} - -static Object *object_solver_camera(Scene *scene, Object *ob) -{ - for (bConstraint *con = ob->constraints.first; - con != NULL; - con = con->next) - { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); - if (cti == NULL) { - continue; - } - if (cti->type == CONSTRAINT_TYPE_OBJECTSOLVER) { - bObjectSolverConstraint *data = (bObjectSolverConstraint *)con->data; - return (data->camera != NULL) ? data->camera : scene->camera; - } - } - return NULL; -} - -static int set_origin_exec(bContext *C, wmOperator *op) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - MovieTracking *tracking = &clip->tracking; - Scene *scene = CTX_data_scene(C); - Object *camera = get_camera_with_movieclip(scene, clip); - int selected_count = count_selected_bundles(C); - - if (selected_count == 0) { - BKE_report(op->reports, - RPT_ERROR, - "At least one track with bundle should be selected to " - "define origin position"); - - return OPERATOR_CANCELLED; - } - - Object *object = get_orientation_object(C); - if (object == NULL) { - BKE_report(op->reports, RPT_ERROR, "No object to apply orientation on"); - return OPERATOR_CANCELLED; - } - - MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking); - ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object); - - float median[3] = {0.0f, 0.0f, 0.0f}; - zero_v3(median); - for (MovieTrackingTrack *track = tracksbase->first; - track != NULL; - track = track->next) - { - if (TRACK_VIEW_SELECTED(sc, track) && - (track->flag & TRACK_HAS_BUNDLE)) - { - add_v3_v3(median, track->bundle_pos); - } - } - mul_v3_fl(median, 1.0f / selected_count); - - float mat[4][4], vec[3]; - BKE_tracking_get_camera_object_matrix(scene, camera, mat); - mul_v3_m4v3(vec, mat, median); - - if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { - sub_v3_v3(object->loc, vec); - } - else { - object_solver_inverted_matrix(scene, object, mat); - mul_v3_m4v3(vec, mat, vec); - copy_v3_v3(object->loc, vec); - } - - DAG_id_tag_update(&clip->id, 0); - DAG_id_tag_update(&object->id, OB_RECALC_OB); - - WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); - - return OPERATOR_FINISHED; -} - -void CLIP_OT_set_origin(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Set Origin"; - ot->description = "Set active marker as origin by moving camera (or its parent if present) in 3D space"; - ot->idname = "CLIP_OT_set_origin"; - - /* api callbacks */ - ot->exec = set_origin_exec; - ot->poll = set_orientation_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean(ot->srna, "use_median", 0, "Use Median", - "Set origin to median point of selected bundles"); -} - -/********************** set floor operator *********************/ - -static void set_axis(Scene *scene, - Object *ob, - MovieClip *clip, - MovieTrackingObject *tracking_object, - MovieTrackingTrack *track, - char axis) -{ - Object *camera = get_camera_with_movieclip(scene, clip); - const bool is_camera = (tracking_object->flag & TRACKING_OBJECT_CAMERA) != 0; - bool flip = false; - float mat[4][4], vec[3], obmat[4][4], dvec[3]; - - BKE_object_to_mat4(ob, obmat); - - BKE_tracking_get_camera_object_matrix(scene, camera, mat); - mul_v3_m4v3(vec, mat, track->bundle_pos); - copy_v3_v3(dvec, vec); - - if (!is_camera) { - float imat[4][4]; - - object_solver_inverted_matrix(scene, ob, imat); - mul_v3_m4v3(vec, imat, vec); - - invert_m4_m4(imat, obmat); - mul_v3_m4v3(dvec, imat, vec); - - sub_v3_v3(vec, obmat[3]); - } - - if (len_squared_v2(vec) < (1e-3f * 1e-3f)) { - return; - } - - unit_m4(mat); - - if (axis == 'X') { - if (fabsf(dvec[1]) < 1e-3f) { - flip = true; - - mat[0][0] = -1.0f; mat[0][1] = 0.0f; mat[0][2] = 0.0f; - mat[1][0] = 0.0f; mat[1][1] = -1.0f; mat[1][2] = 0.0f; - mat[2][0] = 0.0f; mat[2][1] = 0.0f; mat[2][2] = 1.0f; - } - else { - copy_v3_v3(mat[0], vec); - - if (is_camera || fabsf(vec[2]) < 1e-3f) { - mat[0][2] = 0.0f; - mat[2][0] = 0.0f; mat[2][1] = 0.0f; mat[2][2] = 1.0f; - cross_v3_v3v3(mat[1], mat[2], mat[0]); - } - else { - vec[2] = 0.0f; - - cross_v3_v3v3(mat[1], mat[0], vec); - cross_v3_v3v3(mat[2], mat[0], mat[1]); - } - } - } - else { - if (fabsf(dvec[0]) < 1e-3f) { - flip = true; - - mat[0][0] = -1.0f; mat[0][1] = 0.0f; mat[0][2] = 0.0f; - mat[1][0] = 0.0f; mat[1][1] = -1.0f; mat[1][2] = 0.0f; - mat[2][0] = 0.0f; mat[2][1] = 0.0f; mat[2][2] = 1.0f; - } - else { - copy_v3_v3(mat[1], vec); - - if (is_camera || fabsf(vec[2]) < 1e-3f) { - mat[1][2] = 0.0f; - mat[2][0] = 0.0f; mat[2][1] = 0.0f; mat[2][2] = 1.0f; - cross_v3_v3v3(mat[0], mat[1], mat[2]); - } - else { - vec[2] = 0.0f; - - cross_v3_v3v3(mat[0], vec, mat[1]); - cross_v3_v3v3(mat[2], mat[0], mat[1]); - } - } - } - - normalize_v3(mat[0]); - normalize_v3(mat[1]); - normalize_v3(mat[2]); - - if (is_camera) { - invert_m4(mat); - - mul_m4_m4m4(mat, mat, obmat); - } - else { - if (!flip) { - float lmat[4][4], ilmat[4][4], rmat[3][3]; - - BKE_object_rot_to_mat3(ob, rmat, true); - invert_m3(rmat); - mul_m4_m4m3(mat, mat, rmat); - - unit_m4(lmat); - copy_v3_v3(lmat[3], obmat[3]); - invert_m4_m4(ilmat, lmat); - - mul_m4_series(mat, lmat, mat, ilmat, obmat); - } - else { - mul_m4_m4m4(mat, obmat, mat); - } - } - - BKE_object_apply_mat4(ob, mat, 0, 0); -} - -static int set_plane_exec(bContext *C, wmOperator *op) -{ - 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 *tracking_object; - MovieTrackingTrack *track, *axis_track = NULL, *act_track; - ListBase *tracksbase; - Object *object; - Object *camera = get_camera_with_movieclip(scene, clip); - int tot = 0; - float vec[3][3], mat[4][4], obmat[4][4], newmat[4][4], orig[3] = {0.0f, 0.0f, 0.0f}; - int plane = RNA_enum_get(op->ptr, "plane"); - float rot[4][4] = {{0.0f, 0.0f, -1.0f, 0.0f}, - {0.0f, 1.0f, 0.0f, 0.0f}, - {1.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f, 1.0f}}; /* 90 degrees Y-axis rotation matrix */ - - if (count_selected_bundles(C) != 3) { - BKE_report(op->reports, RPT_ERROR, "Three tracks with bundles are needed to orient the floor"); - - return OPERATOR_CANCELLED; - } - - tracking_object = BKE_tracking_object_get_active(tracking); - tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object); - act_track = BKE_tracking_track_get_active(tracking); - - object = get_orientation_object(C); - if (object == NULL) { - BKE_report(op->reports, RPT_ERROR, "No object to apply orientation on"); - return OPERATOR_CANCELLED; - } - - BKE_tracking_get_camera_object_matrix(scene, camera, mat); - - /* Get 3 bundles to use as reference. */ - track = tracksbase->first; - while (track && tot < 3) { - if (track->flag & TRACK_HAS_BUNDLE && TRACK_VIEW_SELECTED(sc, track)) { - mul_v3_m4v3(vec[tot], mat, track->bundle_pos); - if (tot == 0 || track == act_track) { - copy_v3_v3(orig, vec[tot]); - } - else { - axis_track = track; - } - tot++; - } - track = track->next; - } - - sub_v3_v3(vec[1], vec[0]); - sub_v3_v3(vec[2], vec[0]); - - /* Construct ortho-normal basis. */ - unit_m4(mat); - if (plane == 0) { /* floor */ - cross_v3_v3v3(mat[0], vec[1], vec[2]); - copy_v3_v3(mat[1], vec[1]); - cross_v3_v3v3(mat[2], mat[0], mat[1]); - } - else if (plane == 1) { /* wall */ - cross_v3_v3v3(mat[2], vec[1], vec[2]); - copy_v3_v3(mat[1], vec[1]); - cross_v3_v3v3(mat[0], mat[1], mat[2]); - } - - normalize_v3(mat[0]); - normalize_v3(mat[1]); - normalize_v3(mat[2]); - - /* Move to origin point. */ - mat[3][0] = orig[0]; - mat[3][1] = orig[1]; - mat[3][2] = orig[2]; - - if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { - invert_m4(mat); - - BKE_object_to_mat4(object, obmat); - mul_m4_m4m4(mat, mat, obmat); - mul_m4_m4m4(newmat, rot, mat); - BKE_object_apply_mat4(object, newmat, 0, 0); - - /* Make camera have positive z-coordinate. */ - if (object->loc[2] < 0) { - invert_m4(rot); - mul_m4_m4m4(newmat, rot, mat); - BKE_object_apply_mat4(object, newmat, 0, 0); - } - } - else { - BKE_object_apply_mat4(object, mat, 0, 0); - } - - BKE_object_where_is_calc(scene, object); - set_axis(scene, object, clip, tracking_object, axis_track, 'X'); - - DAG_id_tag_update(&clip->id, 0); - DAG_id_tag_update(&object->id, OB_RECALC_OB); - - WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); - - return OPERATOR_FINISHED; -} - -void CLIP_OT_set_plane(wmOperatorType *ot) -{ - static EnumPropertyItem plane_items[] = { - {0, "FLOOR", 0, "Floor", "Set floor plane"}, - {1, "WALL", 0, "Wall", "Set wall plane"}, - {0, NULL, 0, NULL, NULL} - }; - - /* identifiers */ - ot->name = "Set Plane"; - ot->description = "Set plane based on 3 selected bundles by moving camera " - "(or its parent if present) in 3D space"; - ot->idname = "CLIP_OT_set_plane"; - - /* api callbacks */ - ot->exec = set_plane_exec; - ot->poll = set_orientation_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_enum(ot->srna, "plane", plane_items, 0, "Plane", - "Plane to be used for orientation"); -} - -/********************** set axis operator *********************/ - -static int set_axis_exec(bContext *C, wmOperator *op) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - MovieTracking *tracking = &clip->tracking; - MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking); - Scene *scene = CTX_data_scene(C); - Object *object; - int axis = RNA_enum_get(op->ptr, "axis"); - - if (count_selected_bundles(C) != 1) { - BKE_report(op->reports, - RPT_ERROR, - "Single track with bundle should be selected to define axis"); - return OPERATOR_CANCELLED; - } - - object = get_orientation_object(C); - if (object == NULL) { - BKE_report(op->reports, RPT_ERROR, "No object to apply orientation on"); - return OPERATOR_CANCELLED; - } - - ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, - tracking_object); - MovieTrackingTrack *track = tracksbase->first; - while (track) { - if (TRACK_VIEW_SELECTED(sc, track) && - (track->flag & TRACK_HAS_BUNDLE)) - { - break; - } - track = track->next; - } - - set_axis(scene, object, clip, tracking_object, track, axis == 0 ? 'X' : 'Y'); - - DAG_id_tag_update(&clip->id, 0); - DAG_id_tag_update(&object->id, OB_RECALC_OB); - - WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); - - return OPERATOR_FINISHED; -} - -void CLIP_OT_set_axis(wmOperatorType *ot) -{ - static EnumPropertyItem axis_actions[] = { - {0, "X", 0, "X", "Align bundle align X axis"}, - {1, "Y", 0, "Y", "Align bundle align Y axis"}, - {0, NULL, 0, NULL, NULL} - }; - - /* identifiers */ - ot->name = "Set Axis"; - ot->description = "Set direction of scene axis rotating camera " - "(or its parent if present) and assume selected track " - "lies on real axis, joining it with the origin"; - ot->idname = "CLIP_OT_set_axis"; - - /* api callbacks */ - ot->exec = set_axis_exec; - ot->poll = set_orientation_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_enum(ot->srna, "axis", axis_actions, 0, "Axis", - "Axis to use to align bundle along"); -} - -/********************** set scale operator *********************/ - -static int do_set_scale(bContext *C, - wmOperator *op, - bool scale_solution, - bool apply_scale) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - MovieTracking *tracking = &clip->tracking; - MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking); - MovieTrackingTrack *track; - Scene *scene = CTX_data_scene(C); - Object *object = NULL; - Object *camera = get_camera_with_movieclip(scene, clip); - ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); - int tot = 0; - float vec[2][3], mat[4][4], scale; - float dist = RNA_float_get(op->ptr, "distance"); - - if (count_selected_bundles(C) != 2) { - BKE_report(op->reports, - RPT_ERROR, - "Two tracks with bundles should be selected to set scale"); - return OPERATOR_CANCELLED; - } - - if (!scale_solution && !apply_scale) { - object = get_orientation_object(C); - if (object == NULL) { - BKE_report(op->reports, - RPT_ERROR, - "No object to apply orientation on"); - return OPERATOR_CANCELLED; - } - } - - BKE_tracking_get_camera_object_matrix(scene, camera, mat); - - track = tracksbase->first; - while (track) { - if (TRACK_VIEW_SELECTED(sc, track)) { - mul_v3_m4v3(vec[tot], mat, track->bundle_pos); - tot++; - } - track = track->next; - } - - sub_v3_v3(vec[0], vec[1]); - - if (len_v3(vec[0]) > 1e-5f) { - scale = dist / len_v3(vec[0]); - if (apply_scale) { - /* Apply scale on reconstructed scene itself. */ - MovieTrackingReconstruction *reconstruction = - BKE_tracking_get_active_reconstruction(tracking); - MovieReconstructedCamera *reconstructed_cameras; - int i; - - for (track = tracksbase->first; track; track = track->next) { - mul_v3_fl(track->bundle_pos, scale); - } - - reconstructed_cameras = reconstruction->cameras; - for (i = 0; i < reconstruction->camnr; i++) { - mul_v3_fl(reconstructed_cameras[i].mat[3], scale); - } - - WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); - } - else { - if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { - mul_v3_fl(object->size, scale); - mul_v3_fl(object->loc, scale); - } - else if (!scale_solution) { - Object *solver_camera = object_solver_camera(scene, object); - - object->size[0] = object->size[1] = object->size[2] = 1.0f / scale; - - if (solver_camera) { - object->size[0] /= solver_camera->size[0]; - object->size[1] /= solver_camera->size[1]; - object->size[2] /= solver_camera->size[2]; - } - } - else { - tracking_object->scale = scale; - } - - DAG_id_tag_update(&clip->id, 0); - - if (object) - DAG_id_tag_update(&object->id, OB_RECALC_OB); - - WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); - } - } - - return OPERATOR_FINISHED; -} - -static int set_scale_exec(bContext *C, wmOperator *op) -{ - return do_set_scale(C, op, false, false); -} - -static int set_scale_invoke(bContext *C, - wmOperator *op, - const wmEvent *UNUSED(event)) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - - if (!RNA_struct_property_is_set(op->ptr, "distance")) - RNA_float_set(op->ptr, "distance", clip->tracking.settings.dist); - - return set_scale_exec(C, op); -} - -void CLIP_OT_set_scale(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Set Scale"; - ot->description = "Set scale of scene by scaling camera (or its parent if present)"; - ot->idname = "CLIP_OT_set_scale"; - - /* api callbacks */ - ot->exec = set_scale_exec; - ot->invoke = set_scale_invoke; - ot->poll = set_orientation_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_float(ot->srna, "distance", 0.0f, -FLT_MAX, FLT_MAX, - "Distance", "Distance between selected tracks", -100.0f, 100.0f); -} - -/********************** set solution scale operator *********************/ - -static int set_solution_scale_poll(bContext *C) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - if (sc != NULL) { - MovieClip *clip = ED_space_clip_get_clip(sc); - if (clip != NULL) { - MovieTracking *tracking = &clip->tracking; - MovieTrackingObject *tracking_object = - BKE_tracking_object_get_active(tracking); - return (tracking_object->flag & TRACKING_OBJECT_CAMERA) == 0; - } - } - return false; -} - -static int set_solution_scale_exec(bContext *C, wmOperator *op) -{ - return do_set_scale(C, op, true, false); -} - -static int set_solution_scale_invoke(bContext *C, - wmOperator *op, - const wmEvent *UNUSED(event)) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - - if (!RNA_struct_property_is_set(op->ptr, "distance")) { - RNA_float_set(op->ptr, - "distance", - clip->tracking.settings.object_distance); - } - - return set_solution_scale_exec(C, op); -} - -void CLIP_OT_set_solution_scale(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Set Solution Scale"; - ot->description = "Set object solution scale using distance between " - "two selected tracks"; - ot->idname = "CLIP_OT_set_solution_scale"; - - /* api callbacks */ - ot->exec = set_solution_scale_exec; - ot->invoke = set_solution_scale_invoke; - ot->poll = set_solution_scale_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_float(ot->srna, "distance", 0.0f, -FLT_MAX, FLT_MAX, - "Distance", "Distance between selected tracks", - -100.0f, 100.0f); -} - -/********************** apply solution scale operator *********************/ - -static int apply_solution_scale_poll(bContext *C) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - if (sc != NULL) { - MovieClip *clip = ED_space_clip_get_clip(sc); - if (clip != NULL) { - MovieTracking *tracking = &clip->tracking; - MovieTrackingObject *tracking_object = - BKE_tracking_object_get_active(tracking); - return (tracking_object->flag & TRACKING_OBJECT_CAMERA) != 0; - } - } - return 0; -} - -static int apply_solution_scale_exec(bContext *C, wmOperator *op) -{ - return do_set_scale(C, op, false, true); -} - -static int apply_solution_scale_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - if (!RNA_struct_property_is_set(op->ptr, "distance")) { - RNA_float_set(op->ptr, "distance", clip->tracking.settings.dist); - } - return apply_solution_scale_exec(C, op); -} - -void CLIP_OT_apply_solution_scale(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Apply Solution Scale"; - ot->description = "Apply scale on solution itself to make distance between " - "selected tracks equals to desired"; - ot->idname = "CLIP_OT_apply_solution_scale"; - - /* api callbacks */ - ot->exec = apply_solution_scale_exec; - ot->invoke = apply_solution_scale_invoke; - ot->poll = apply_solution_scale_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_float(ot->srna, "distance", 0.0f, -FLT_MAX, FLT_MAX, - "Distance", "Distance between selected tracks", -100.0f, 100.0f); -} - /********************** set principal center operator *********************/ static int set_center_principal_exec(bContext *C, wmOperator *UNUSED(op)) @@ -3034,121 +1390,6 @@ void CLIP_OT_hide_tracks_clear(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** detect features operator *********************/ - -static bGPDlayer *detect_get_layer(MovieClip *clip) -{ - if (clip->gpd == NULL) { - return NULL; - } - for (bGPDlayer *layer = clip->gpd->layers.first; - layer != NULL; - layer = layer->next) - { - if (layer->flag & GP_LAYER_ACTIVE) { - return layer; - } - } - return NULL; -} - -static int detect_features_exec(bContext *C, wmOperator *op) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - int clip_flag = clip->flag & MCLIP_TIMECODE_FLAGS; - ImBuf *ibuf = BKE_movieclip_get_ibuf_flag(clip, - &sc->user, - clip_flag, - MOVIECLIP_CACHE_SKIP); - MovieTracking *tracking = &clip->tracking; - ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); - int placement = RNA_enum_get(op->ptr, "placement"); - int margin = RNA_int_get(op->ptr, "margin"); - int min_distance = RNA_int_get(op->ptr, "min_distance"); - float threshold = RNA_float_get(op->ptr, "threshold"); - int place_outside_layer = 0; - int framenr = ED_space_clip_get_clip_frame_number(sc); - bGPDlayer *layer = NULL; - - if (!ibuf) { - BKE_report(op->reports, - RPT_ERROR, - "Feature detection requires valid clip frame"); - return OPERATOR_CANCELLED; - } - - if (placement != 0) { - layer = detect_get_layer(clip); - place_outside_layer = placement == 2; - } - - /* Deselect existing tracks. */ - /* TODO(sergey): Could use deselect oeprator function for this. */ - for (MovieTrackingTrack *track = tracksbase->first; - track != NULL; - track = track->next) - { - track->flag &= ~SELECT; - track->pat_flag &= ~SELECT; - track->search_flag &= ~SELECT; - } - - BKE_tracking_detect_harris(tracking, - tracksbase, - ibuf, - framenr, - margin, - threshold / 100000.0f, - min_distance, - layer, - place_outside_layer); - - IMB_freeImBuf(ibuf); - - BKE_tracking_dopesheet_tag_update(tracking); - WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void CLIP_OT_detect_features(wmOperatorType *ot) -{ - static EnumPropertyItem placement_items[] = { - {0, "FRAME", 0, "Whole Frame", - "Place markers across the whole frame"}, - {1, "INSIDE_GPENCIL", 0, "Inside grease pencil", - "Place markers only inside areas outlined with grease pencil"}, - {2, "OUTSIDE_GPENCIL", 0, "Outside grease pencil", - "Place markers only outside areas outlined with grease pencil"}, - {0, NULL, 0, NULL, NULL} - }; - - /* identifiers */ - ot->name = "Detect Features"; - ot->description = "Automatically detect features and place markers to track"; - ot->idname = "CLIP_OT_detect_features"; - - /* api callbacks */ - ot->exec = detect_features_exec; - ot->poll = ED_space_clip_tracking_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_enum(ot->srna, "placement", placement_items, 0, "Placement", - "Placement for detected features"); - RNA_def_int(ot->srna, "margin", 16, 0, INT_MAX, "Margin", - "Only features further than margin pixels from the image " - "edges are considered", 0, 300); - RNA_def_float(ot->srna, "threshold", 0.5f, 0.0001f, FLT_MAX, "Threshold", - "Threshold level to consider feature good enough for tracking", - 0.0001f, FLT_MAX); - RNA_def_int(ot->srna, "min_distance", 120, 0, INT_MAX, "Distance", - "Minimal distance accepted between two features", 0, 300); -} - /********************** frame jump operator *********************/ static int frame_jump_exec(bContext *C, wmOperator *op) @@ -3462,200 +1703,6 @@ void CLIP_OT_track_copy_color(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************* add 2d stabilization tracks operator ********************/ - -static int stabilize_2d_poll(bContext *C) -{ - if (ED_space_clip_tracking_poll(C)) { - SpaceClip *sc = CTX_wm_space_clip(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - MovieTrackingObject *tracking_object = - BKE_tracking_object_get_active(&clip->tracking); - return (tracking_object->flag & TRACKING_OBJECT_CAMERA) != 0; - } - return 0; -} - -static int stabilize_2d_add_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(tracking); - MovieTrackingStabilization *stab = &tracking->stabilization; - - bool update = false; - for (MovieTrackingTrack *track = tracksbase->first; - track != NULL; - track = track->next) - { - if (TRACK_VIEW_SELECTED(sc, track) && - (track->flag & TRACK_USE_2D_STAB) == 0) - { - track->flag |= TRACK_USE_2D_STAB; - stab->tot_track++; - update = true; - } - } - - if (update) { - stab->ok = 0; - DAG_id_tag_update(&clip->id, 0); - WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); - } - - return OPERATOR_FINISHED; -} - -void CLIP_OT_stabilize_2d_add(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Stabilization Tracks"; - ot->description = "Add selected tracks to 2D stabilization tool"; - ot->idname = "CLIP_OT_stabilize_2d_add"; - - /* api callbacks */ - ot->exec = stabilize_2d_add_exec; - ot->poll = stabilize_2d_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/******************* remove 2d stabilization tracks operator ******************/ - -static int stabilize_2d_remove_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; - MovieTrackingStabilization *stab = &tracking->stabilization; - ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); - int a = 0; - bool update = false; - - for (MovieTrackingTrack *track = tracksbase->first; - track != NULL; - track = track->next) - { - if (track->flag & TRACK_USE_2D_STAB) { - if (a == stab->act_track) { - track->flag &= ~TRACK_USE_2D_STAB; - stab->act_track--; - stab->tot_track--; - if (stab->act_track < 0) { - stab->act_track = 0; - } - update = true; - break; - } - a++; - } - } - - if (update) { - stab->ok = 0; - DAG_id_tag_update(&clip->id, 0); - WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); - } - - return OPERATOR_FINISHED; -} - -void CLIP_OT_stabilize_2d_remove(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Remove Stabilization Track"; - ot->description = "Remove selected track from stabilization"; - ot->idname = "CLIP_OT_stabilize_2d_remove"; - - /* api callbacks */ - ot->exec = stabilize_2d_remove_exec; - ot->poll = stabilize_2d_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/********************** select 2d stabilization tracks operator *********************/ - -static int stabilize_2d_select_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(tracking); - bool update = false; - - for (MovieTrackingTrack *track = tracksbase->first; - track != NULL; - track = track->next) - { - if (track->flag & TRACK_USE_2D_STAB) { - BKE_tracking_track_flag_set(track, TRACK_AREA_ALL, SELECT); - update = true; - } - } - - if (update) { - WM_event_add_notifier(C, NC_MOVIECLIP | ND_SELECT, clip); - } - - return OPERATOR_FINISHED; -} - -void CLIP_OT_stabilize_2d_select(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Stabilization Tracks"; - ot->description = "Select tracks which are used for stabilization"; - ot->idname = "CLIP_OT_stabilize_2d_select"; - - /* api callbacks */ - ot->exec = stabilize_2d_select_exec; - ot->poll = stabilize_2d_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/***************** set 2d stabilization rotation track operator ****************/ - -static int stabilize_2d_set_rotation_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; - MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking); - - if (act_track != NULL) { - MovieTrackingStabilization *stab = &tracking->stabilization; - stab->rot_track = act_track; - stab->ok = 0; - - DAG_id_tag_update(&clip->id, 0); - WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); - } - - return OPERATOR_FINISHED; -} - -void CLIP_OT_stabilize_2d_set_rotation(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Set Rotation Track"; - ot->description = "Use active track to compensate rotation when " - "doing 2D stabilization"; - ot->idname = "CLIP_OT_stabilize_2d_set_rotation"; - - /* api callbacks */ - ot->exec = stabilize_2d_set_rotation_exec; - ot->poll = stabilize_2d_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - /********************** clean tracks operator *********************/ static bool is_track_clean(MovieTrackingTrack *track, int frames, int del) @@ -3971,7 +2018,7 @@ static int copy_tracks_exec(bContext *C, wmOperator *UNUSED(op)) MovieTracking *tracking = &clip->tracking; MovieTrackingObject *object = BKE_tracking_object_get_active(tracking); - clear_invisible_track_selection(sc, clip); + clip_tracking_clear_invisible_track_selection(sc, clip); BKE_tracking_clipboard_copy_tracks(tracking, object); @@ -4035,387 +2082,6 @@ void CLIP_OT_paste_tracks(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** Create plane track operator *********************/ - -static int create_plane_track_tracks_exec(bContext *C, wmOperator *op) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - MovieTracking *tracking = &clip->tracking; - MovieTrackingPlaneTrack *plane_track; - ListBase *tracks_base = BKE_tracking_get_active_tracks(tracking); - ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking); - int framenr = ED_space_clip_get_clip_frame_number(sc); - - plane_track = BKE_tracking_plane_track_add(tracking, - plane_tracks_base, - tracks_base, - framenr); - - if (plane_track == NULL) { - BKE_report(op->reports, RPT_ERROR, "Need at least 4 selected point tracks to create a plane"); - return OPERATOR_CANCELLED; - } - else { - BKE_tracking_tracks_deselect_all(tracks_base); - - plane_track->flag |= SELECT; - clip->tracking.act_track = NULL; - clip->tracking.act_plane_track = plane_track; - - /* Compute homoraphies and apply them on marker's corner, so we've got - * quite nice motion from the very beginning. - */ - BKE_tracking_track_plane_from_existing_motion(plane_track, framenr); - } - - WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip); - - return OPERATOR_FINISHED; -} - -void CLIP_OT_create_plane_track(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Create Plane Track"; - ot->description = "Create new plane track out of selected point tracks"; - ot->idname = "CLIP_OT_create_plane_track"; - - /* api callbacks */ - ot->exec = create_plane_track_tracks_exec; - ot->poll = ED_space_clip_tracking_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/********************** Slide plane marker corner operator *********************/ - -typedef struct SlidePlaneMarkerData { - int event_type; - MovieTrackingPlaneTrack *plane_track; - MovieTrackingPlaneMarker *plane_marker; - int width, height; - int corner_index; - float *corner; - int previous_mval[2]; - float previous_corner[2]; - float old_corner[2]; - bool accurate; -} SlidePlaneMarkerData; - -/* TODO(sergey): Use closest sliding zone semantic here. */ -static bool mouse_on_plane_slide_zone(SpaceClip *sc, - float co[2], - float slide_zone[2], - int width, - int height) -{ - const float size = 12.0f; - float dx, dy; - - dx = size / width / sc->zoom; - dy = size / height / sc->zoom; - - return IN_RANGE_INCL(co[0], slide_zone[0] - dx, slide_zone[0] + dx) && - IN_RANGE_INCL(co[1], slide_zone[1] - dy, slide_zone[1] + dy); -} - -static MovieTrackingPlaneTrack *tracking_plane_marker_check_slide( - bContext *C, - const wmEvent *event, - int *corner_r) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - ARegion *ar = CTX_wm_region(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - MovieTracking *tracking = &clip->tracking; - int width, height; - float co[2]; - ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking); - int framenr = ED_space_clip_get_clip_frame_number(sc); - - ED_space_clip_get_size(sc, &width, &height); - if (width == 0 || height == 0) { - return NULL; - } - - ED_clip_mouse_pos(sc, ar, event->mval, co); - - for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first; - plane_track != NULL; - plane_track = plane_track->next) - { - if (PLANE_TRACK_VIEW_SELECTED(plane_track)) { - MovieTrackingPlaneMarker *plane_marker = - BKE_tracking_plane_marker_get(plane_track, framenr); - bool ok = false; - int i; - - for (i = 0; i < 4; i++) { - if (mouse_on_plane_slide_zone(sc, - co, - plane_marker->corners[i], - width, - height)) - { - if (corner_r) { - *corner_r = i; - } - ok = true; - break; - } - } - - if (ok) { - return plane_track; - } - } - } - - return NULL; -} - -static void *slide_plane_marker_customdata(bContext *C, const wmEvent *event) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - ARegion *ar = CTX_wm_region(C); - MovieTrackingPlaneTrack *plane_track; - int width, height; - float co[2]; - SlidePlaneMarkerData *customdata = NULL; - int framenr = ED_space_clip_get_clip_frame_number(sc); - int corner; - - ED_space_clip_get_size(sc, &width, &height); - if (width == 0 || height == 0) { - return NULL; - } - - ED_clip_mouse_pos(sc, ar, event->mval, co); - - plane_track = tracking_plane_marker_check_slide(C, event, &corner); - if (plane_track) { - MovieTrackingPlaneMarker *plane_marker; - - customdata = MEM_callocN(sizeof(SlidePlaneMarkerData), "slide plane marker data"); - - customdata->event_type = event->type; - - plane_marker = BKE_tracking_plane_marker_ensure(plane_track, framenr); - - customdata->plane_track = plane_track; - customdata->plane_marker = plane_marker; - customdata->width = width; - customdata->height = height; - - customdata->previous_mval[0] = event->mval[0]; - customdata->previous_mval[1] = event->mval[1]; - - customdata->corner_index = corner; - customdata->corner = plane_marker->corners[corner]; - - copy_v2_v2(customdata->previous_corner, customdata->corner); - copy_v2_v2(customdata->old_corner, customdata->corner); - } - - return customdata; -} - -static int slide_plane_marker_invoke(bContext *C, - wmOperator *op, - const wmEvent *event) -{ - SlidePlaneMarkerData *slidedata = slide_plane_marker_customdata(C, event); - - if (slidedata) { - SpaceClip *sc = CTX_wm_space_clip(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - MovieTracking *tracking = &clip->tracking; - - tracking->act_plane_track = slidedata->plane_track; - tracking->act_track = NULL; - - op->customdata = slidedata; - - hide_cursor(C); - WM_event_add_modal_handler(C, op); - - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); - - return OPERATOR_RUNNING_MODAL; - } - - return OPERATOR_PASS_THROUGH; -} - -static void cancel_mouse_slide_plane_marker(SlidePlaneMarkerData *data) -{ - copy_v2_v2(data->corner, data->old_corner); -} - -static void free_slide_plane_marker_data(SlidePlaneMarkerData *data) -{ - MEM_freeN(data); -} - -static void slide_plane_marker_update_homographies(SpaceClip *sc, - SlidePlaneMarkerData *data) -{ - int framenr = ED_space_clip_get_clip_frame_number(sc); - - BKE_tracking_track_plane_from_existing_motion(data->plane_track, framenr); -} - -static int slide_plane_marker_modal(bContext *C, - wmOperator *op, - const wmEvent *event) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - SlidePlaneMarkerData *data = (SlidePlaneMarkerData *) op->customdata; - float dx, dy, mdelta[2]; - int next_corner_index, prev_corner_index, diag_corner_index; - const float *next_corner, *prev_corner, *diag_corner; - float next_edge[2], prev_edge[2], next_diag_edge[2], prev_diag_edge[2]; - - switch (event->type) { - case LEFTCTRLKEY: - case RIGHTCTRLKEY: - case LEFTSHIFTKEY: - case RIGHTSHIFTKEY: - if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY)) { - data->accurate = event->val == KM_PRESS; - } - - /* fall-through */ - case MOUSEMOVE: - mdelta[0] = event->mval[0] - data->previous_mval[0]; - mdelta[1] = event->mval[1] - data->previous_mval[1]; - - dx = mdelta[0] / data->width / sc->zoom; - dy = mdelta[1] / data->height / sc->zoom; - - if (data->accurate) { - dx /= 5.0f; - dy /= 5.0f; - } - - data->corner[0] = data->previous_corner[0] + dx; - data->corner[1] = data->previous_corner[1] + dy; - - - /* - prev_edge - (Corner 3, current) <----------------------- (Corner 2, previous) - | ^ - | | - | | - | | - next_edge | | next_diag_edge - | | - | | - | | - v | - (Corner 0, next) -----------------------> (Corner 1, diagonal) - prev_diag_edge - */ - - next_corner_index = (data->corner_index + 1) % 4; - prev_corner_index = (data->corner_index + 3) % 4; - diag_corner_index = (data->corner_index + 2) % 4; - - next_corner = data->plane_marker->corners[next_corner_index]; - prev_corner = data->plane_marker->corners[prev_corner_index]; - diag_corner = data->plane_marker->corners[diag_corner_index]; - - sub_v2_v2v2(next_edge, next_corner, data->corner); - sub_v2_v2v2(prev_edge, data->corner, prev_corner); - sub_v2_v2v2(next_diag_edge, prev_corner, diag_corner); - sub_v2_v2v2(prev_diag_edge, diag_corner, next_corner); - - if (cross_v2v2(prev_edge, next_edge) < 0.0f) { - closest_to_line_v2(data->corner, - data->corner, - prev_corner, - next_corner); - } - - if (cross_v2v2(next_diag_edge, prev_edge) < 0.0f) { - closest_to_line_v2(data->corner, - data->corner, - prev_corner, - diag_corner); - } - - if (cross_v2v2(next_edge, prev_diag_edge) < 0.0f) { - closest_to_line_v2(data->corner, - data->corner, - next_corner, - diag_corner); - } - - data->previous_mval[0] = event->mval[0]; - data->previous_mval[1] = event->mval[1]; - copy_v2_v2(data->previous_corner, data->corner); - - DAG_id_tag_update(&sc->clip->id, 0); - - WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, NULL); - - break; - - case LEFTMOUSE: - case RIGHTMOUSE: - if (event->type == data->event_type && event->val == KM_RELEASE) { - /* Marker is now keyframed. */ - data->plane_marker->flag &= ~PLANE_MARKER_TRACKED; - - slide_plane_marker_update_homographies(sc, data); - - free_slide_plane_marker_data(op->customdata); - - show_cursor(C); - - DAG_id_tag_update(&sc->clip->id, 0); - WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip); - - return OPERATOR_FINISHED; - } - - break; - - case ESCKEY: - cancel_mouse_slide_plane_marker(op->customdata); - - free_slide_plane_marker_data(op->customdata); - - show_cursor(C); - - WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip); - - return OPERATOR_CANCELLED; - } - - return OPERATOR_RUNNING_MODAL; -} - -void CLIP_OT_slide_plane_marker(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Slide Plane Marker"; - ot->description = "Slide plane marker areas"; - ot->idname = "CLIP_OT_slide_plane_marker"; - - /* api callbacks */ - ot->poll = ED_space_clip_tracking_poll; - ot->invoke = slide_plane_marker_invoke; - ot->modal = slide_plane_marker_modal; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR | OPTYPE_BLOCKING; -} - /********************** Insert track keyframe operator *********************/ static void keyframe_set_flag(bContext *C, bool set) diff --git a/source/blender/editors/space_clip/tracking_ops_detect.c b/source/blender/editors/space_clip/tracking_ops_detect.c new file mode 100644 index 00000000000..1af6bbef0bc --- /dev/null +++ b/source/blender/editors/space_clip/tracking_ops_detect.c @@ -0,0 +1,171 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, + * Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/space_clip/tracking_ops_detect.c + * \ingroup spclip + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_gpencil_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_movieclip.h" +#include "BKE_tracking.h" +#include "BKE_report.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_clip.h" + +#include "IMB_imbuf_types.h" +#include "IMB_imbuf.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "clip_intern.h" + +/********************** detect features operator *********************/ + +static bGPDlayer *detect_get_layer(MovieClip *clip) +{ + if (clip->gpd == NULL) { + return NULL; + } + for (bGPDlayer *layer = clip->gpd->layers.first; + layer != NULL; + layer = layer->next) + { + if (layer->flag & GP_LAYER_ACTIVE) { + return layer; + } + } + return NULL; +} + +static int detect_features_exec(bContext *C, wmOperator *op) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + int clip_flag = clip->flag & MCLIP_TIMECODE_FLAGS; + ImBuf *ibuf = BKE_movieclip_get_ibuf_flag(clip, + &sc->user, + clip_flag, + MOVIECLIP_CACHE_SKIP); + MovieTracking *tracking = &clip->tracking; + ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); + int placement = RNA_enum_get(op->ptr, "placement"); + int margin = RNA_int_get(op->ptr, "margin"); + int min_distance = RNA_int_get(op->ptr, "min_distance"); + float threshold = RNA_float_get(op->ptr, "threshold"); + int place_outside_layer = 0; + int framenr = ED_space_clip_get_clip_frame_number(sc); + bGPDlayer *layer = NULL; + + if (!ibuf) { + BKE_report(op->reports, + RPT_ERROR, + "Feature detection requires valid clip frame"); + return OPERATOR_CANCELLED; + } + + if (placement != 0) { + layer = detect_get_layer(clip); + place_outside_layer = placement == 2; + } + + /* Deselect existing tracks. */ + /* TODO(sergey): Could use deselect oeprator function for this. */ + for (MovieTrackingTrack *track = tracksbase->first; + track != NULL; + track = track->next) + { + track->flag &= ~SELECT; + track->pat_flag &= ~SELECT; + track->search_flag &= ~SELECT; + } + + BKE_tracking_detect_harris(tracking, + tracksbase, + ibuf, + framenr, + margin, + threshold / 100000.0f, + min_distance, + layer, + place_outside_layer); + + IMB_freeImBuf(ibuf); + + BKE_tracking_dopesheet_tag_update(tracking); + WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void CLIP_OT_detect_features(wmOperatorType *ot) +{ + static EnumPropertyItem placement_items[] = { + {0, "FRAME", 0, "Whole Frame", + "Place markers across the whole frame"}, + {1, "INSIDE_GPENCIL", 0, "Inside grease pencil", + "Place markers only inside areas outlined with grease pencil"}, + {2, "OUTSIDE_GPENCIL", 0, "Outside grease pencil", + "Place markers only outside areas outlined with grease pencil"}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Detect Features"; + ot->description = "Automatically detect features and place markers to track"; + ot->idname = "CLIP_OT_detect_features"; + + /* api callbacks */ + ot->exec = detect_features_exec; + ot->poll = ED_space_clip_tracking_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_enum(ot->srna, "placement", placement_items, 0, "Placement", + "Placement for detected features"); + RNA_def_int(ot->srna, "margin", 16, 0, INT_MAX, "Margin", + "Only features further than margin pixels from the image " + "edges are considered", 0, 300); + RNA_def_float(ot->srna, "threshold", 0.5f, 0.0001f, FLT_MAX, "Threshold", + "Threshold level to consider feature good enough for tracking", + 0.0001f, FLT_MAX); + RNA_def_int(ot->srna, "min_distance", 120, 0, INT_MAX, "Distance", + "Minimal distance accepted between two features", 0, 300); +} diff --git a/source/blender/editors/space_clip/tracking_ops_intern.h b/source/blender/editors/space_clip/tracking_ops_intern.h new file mode 100644 index 00000000000..d9aff100416 --- /dev/null +++ b/source/blender/editors/space_clip/tracking_ops_intern.h @@ -0,0 +1,45 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, + * Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/space_clip/tracking_ops_intern.h + * \ingroup spclip + */ + +#ifndef __CLIP_TRACKING_INTERN__ +#define __CLIP_TRACKING_INTERN__ + +struct bContext; +struct SpaceClip; +struct MovieClip; + +void clip_tracking_clear_invisible_track_selection(struct SpaceClip *sc, + struct MovieClip *clip); + +void clip_tracking_show_cursor(struct bContext *C); +void clip_tracking_hide_cursor(struct bContext *C); + +#endif /* __CLIP_TRACKING_INTERN__ */
\ No newline at end of file diff --git a/source/blender/editors/space_clip/tracking_ops_orient.c b/source/blender/editors/space_clip/tracking_ops_orient.c new file mode 100644 index 00000000000..f81180d65a9 --- /dev/null +++ b/source/blender/editors/space_clip/tracking_ops_orient.c @@ -0,0 +1,860 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, + * Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/space_clip/tracking_ops_orient.c + * \ingroup spclip + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_constraint_types.h" +#include "DNA_object_types.h" /* SELECT */ +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "BLI_utildefines.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_constraint.h" +#include "BKE_tracking.h" +#include "BKE_global.h" +#include "BKE_depsgraph.h" +#include "BKE_object.h" +#include "BKE_report.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_clip.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "clip_intern.h" + +/********************** set origin operator *********************/ + +static Object *get_camera_with_movieclip(Scene *scene, MovieClip *clip) +{ + Object *camera = scene->camera; + + if (camera != NULL && + BKE_object_movieclip_get(scene, camera, false) == clip) + { + return camera; + } + + for (Base *base = scene->base.first; + base != NULL; + base = base->next) + { + if (base->object->type == OB_CAMERA) { + if (BKE_object_movieclip_get(scene, base->object, false) == clip) { + camera = base->object; + break; + } + } + } + + return camera; +} + +static Object *get_orientation_object(bContext *C) +{ + Scene *scene = CTX_data_scene(C); + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + MovieTracking *tracking = &clip->tracking; + MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking); + Object *object = NULL; + + if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { + object = get_camera_with_movieclip(scene, clip); + } + else { + object = OBACT; + } + + if (object != NULL && object->parent != NULL) { + object = object->parent; + } + + return object; +} + +static int set_orientation_poll(bContext *C) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + if (sc != NULL) { + Scene *scene = CTX_data_scene(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + if (clip != NULL) { + MovieTracking *tracking = &clip->tracking; + MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking); + if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { + return true; + } + else { + return OBACT != NULL; + } + } + } + return false; +} + +static int count_selected_bundles(bContext *C) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking); + int tot = 0; + for (MovieTrackingTrack *track = tracksbase->first; + track != NULL; + track = track->next) + { + if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_HAS_BUNDLE)) { + tot++; + } + } + return tot; +} + +static void object_solver_inverted_matrix(Scene *scene, + Object *ob, + float invmat[4][4]) +{ + bool found = false; + for (bConstraint *con = ob->constraints.first; + con != NULL; + con = con->next) + { + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + if (cti == NULL) { + continue; + } + if (cti->type == CONSTRAINT_TYPE_OBJECTSOLVER) { + bObjectSolverConstraint *data = (bObjectSolverConstraint *)con->data; + if (!found) { + Object *cam = data->camera ? data->camera : scene->camera; + BKE_object_where_is_calc_mat4(scene, cam, invmat); + } + mul_m4_m4m4(invmat, invmat, data->invmat); + found = true; + } + } + if (found) { + invert_m4(invmat); + } + else { + unit_m4(invmat); + } +} + +static Object *object_solver_camera(Scene *scene, Object *ob) +{ + for (bConstraint *con = ob->constraints.first; + con != NULL; + con = con->next) + { + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + if (cti == NULL) { + continue; + } + if (cti->type == CONSTRAINT_TYPE_OBJECTSOLVER) { + bObjectSolverConstraint *data = (bObjectSolverConstraint *)con->data; + return (data->camera != NULL) ? data->camera : scene->camera; + } + } + return NULL; +} + +static int set_origin_exec(bContext *C, wmOperator *op) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + MovieTracking *tracking = &clip->tracking; + Scene *scene = CTX_data_scene(C); + Object *camera = get_camera_with_movieclip(scene, clip); + int selected_count = count_selected_bundles(C); + + if (selected_count == 0) { + BKE_report(op->reports, + RPT_ERROR, + "At least one track with bundle should be selected to " + "define origin position"); + + return OPERATOR_CANCELLED; + } + + Object *object = get_orientation_object(C); + if (object == NULL) { + BKE_report(op->reports, RPT_ERROR, "No object to apply orientation on"); + return OPERATOR_CANCELLED; + } + + MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking); + ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object); + + float median[3] = {0.0f, 0.0f, 0.0f}; + zero_v3(median); + for (MovieTrackingTrack *track = tracksbase->first; + track != NULL; + track = track->next) + { + if (TRACK_VIEW_SELECTED(sc, track) && + (track->flag & TRACK_HAS_BUNDLE)) + { + add_v3_v3(median, track->bundle_pos); + } + } + mul_v3_fl(median, 1.0f / selected_count); + + float mat[4][4], vec[3]; + BKE_tracking_get_camera_object_matrix(scene, camera, mat); + mul_v3_m4v3(vec, mat, median); + + if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { + sub_v3_v3(object->loc, vec); + } + else { + object_solver_inverted_matrix(scene, object, mat); + mul_v3_m4v3(vec, mat, vec); + copy_v3_v3(object->loc, vec); + } + + DAG_id_tag_update(&clip->id, 0); + DAG_id_tag_update(&object->id, OB_RECALC_OB); + + WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + + return OPERATOR_FINISHED; +} + +void CLIP_OT_set_origin(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set Origin"; + ot->description = "Set active marker as origin by moving camera (or its parent if present) in 3D space"; + ot->idname = "CLIP_OT_set_origin"; + + /* api callbacks */ + ot->exec = set_origin_exec; + ot->poll = set_orientation_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "use_median", 0, "Use Median", + "Set origin to median point of selected bundles"); +} + +/********************** set floor operator *********************/ + +static void set_axis(Scene *scene, + Object *ob, + MovieClip *clip, + MovieTrackingObject *tracking_object, + MovieTrackingTrack *track, + char axis) +{ + Object *camera = get_camera_with_movieclip(scene, clip); + const bool is_camera = (tracking_object->flag & TRACKING_OBJECT_CAMERA) != 0; + bool flip = false; + float mat[4][4], vec[3], obmat[4][4], dvec[3]; + + BKE_object_to_mat4(ob, obmat); + + BKE_tracking_get_camera_object_matrix(scene, camera, mat); + mul_v3_m4v3(vec, mat, track->bundle_pos); + copy_v3_v3(dvec, vec); + + if (!is_camera) { + float imat[4][4]; + + object_solver_inverted_matrix(scene, ob, imat); + mul_v3_m4v3(vec, imat, vec); + + invert_m4_m4(imat, obmat); + mul_v3_m4v3(dvec, imat, vec); + + sub_v3_v3(vec, obmat[3]); + } + + if (len_squared_v2(vec) < (1e-3f * 1e-3f)) { + return; + } + + unit_m4(mat); + + if (axis == 'X') { + if (fabsf(dvec[1]) < 1e-3f) { + flip = true; + + mat[0][0] = -1.0f; mat[0][1] = 0.0f; mat[0][2] = 0.0f; + mat[1][0] = 0.0f; mat[1][1] = -1.0f; mat[1][2] = 0.0f; + mat[2][0] = 0.0f; mat[2][1] = 0.0f; mat[2][2] = 1.0f; + } + else { + copy_v3_v3(mat[0], vec); + + if (is_camera || fabsf(vec[2]) < 1e-3f) { + mat[0][2] = 0.0f; + mat[2][0] = 0.0f; mat[2][1] = 0.0f; mat[2][2] = 1.0f; + cross_v3_v3v3(mat[1], mat[2], mat[0]); + } + else { + vec[2] = 0.0f; + + cross_v3_v3v3(mat[1], mat[0], vec); + cross_v3_v3v3(mat[2], mat[0], mat[1]); + } + } + } + else { + if (fabsf(dvec[0]) < 1e-3f) { + flip = true; + + mat[0][0] = -1.0f; mat[0][1] = 0.0f; mat[0][2] = 0.0f; + mat[1][0] = 0.0f; mat[1][1] = -1.0f; mat[1][2] = 0.0f; + mat[2][0] = 0.0f; mat[2][1] = 0.0f; mat[2][2] = 1.0f; + } + else { + copy_v3_v3(mat[1], vec); + + if (is_camera || fabsf(vec[2]) < 1e-3f) { + mat[1][2] = 0.0f; + mat[2][0] = 0.0f; mat[2][1] = 0.0f; mat[2][2] = 1.0f; + cross_v3_v3v3(mat[0], mat[1], mat[2]); + } + else { + vec[2] = 0.0f; + + cross_v3_v3v3(mat[0], vec, mat[1]); + cross_v3_v3v3(mat[2], mat[0], mat[1]); + } + } + } + + normalize_v3(mat[0]); + normalize_v3(mat[1]); + normalize_v3(mat[2]); + + if (is_camera) { + invert_m4(mat); + + mul_m4_m4m4(mat, mat, obmat); + } + else { + if (!flip) { + float lmat[4][4], ilmat[4][4], rmat[3][3]; + + BKE_object_rot_to_mat3(ob, rmat, true); + invert_m3(rmat); + mul_m4_m4m3(mat, mat, rmat); + + unit_m4(lmat); + copy_v3_v3(lmat[3], obmat[3]); + invert_m4_m4(ilmat, lmat); + + mul_m4_series(mat, lmat, mat, ilmat, obmat); + } + else { + mul_m4_m4m4(mat, obmat, mat); + } + } + + BKE_object_apply_mat4(ob, mat, 0, 0); +} + +static int set_plane_exec(bContext *C, wmOperator *op) +{ + 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 *tracking_object; + MovieTrackingTrack *track, *axis_track = NULL, *act_track; + ListBase *tracksbase; + Object *object; + Object *camera = get_camera_with_movieclip(scene, clip); + int tot = 0; + float vec[3][3], mat[4][4], obmat[4][4], newmat[4][4], orig[3] = {0.0f, 0.0f, 0.0f}; + int plane = RNA_enum_get(op->ptr, "plane"); + float rot[4][4] = {{0.0f, 0.0f, -1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f}}; /* 90 degrees Y-axis rotation matrix */ + + if (count_selected_bundles(C) != 3) { + BKE_report(op->reports, RPT_ERROR, "Three tracks with bundles are needed to orient the floor"); + + return OPERATOR_CANCELLED; + } + + tracking_object = BKE_tracking_object_get_active(tracking); + tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object); + act_track = BKE_tracking_track_get_active(tracking); + + object = get_orientation_object(C); + if (object == NULL) { + BKE_report(op->reports, RPT_ERROR, "No object to apply orientation on"); + return OPERATOR_CANCELLED; + } + + BKE_tracking_get_camera_object_matrix(scene, camera, mat); + + /* Get 3 bundles to use as reference. */ + track = tracksbase->first; + while (track && tot < 3) { + if (track->flag & TRACK_HAS_BUNDLE && TRACK_VIEW_SELECTED(sc, track)) { + mul_v3_m4v3(vec[tot], mat, track->bundle_pos); + if (tot == 0 || track == act_track) { + copy_v3_v3(orig, vec[tot]); + } + else { + axis_track = track; + } + tot++; + } + track = track->next; + } + + sub_v3_v3(vec[1], vec[0]); + sub_v3_v3(vec[2], vec[0]); + + /* Construct ortho-normal basis. */ + unit_m4(mat); + if (plane == 0) { /* floor */ + cross_v3_v3v3(mat[0], vec[1], vec[2]); + copy_v3_v3(mat[1], vec[1]); + cross_v3_v3v3(mat[2], mat[0], mat[1]); + } + else if (plane == 1) { /* wall */ + cross_v3_v3v3(mat[2], vec[1], vec[2]); + copy_v3_v3(mat[1], vec[1]); + cross_v3_v3v3(mat[0], mat[1], mat[2]); + } + + normalize_v3(mat[0]); + normalize_v3(mat[1]); + normalize_v3(mat[2]); + + /* Move to origin point. */ + mat[3][0] = orig[0]; + mat[3][1] = orig[1]; + mat[3][2] = orig[2]; + + if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { + invert_m4(mat); + + BKE_object_to_mat4(object, obmat); + mul_m4_m4m4(mat, mat, obmat); + mul_m4_m4m4(newmat, rot, mat); + BKE_object_apply_mat4(object, newmat, 0, 0); + + /* Make camera have positive z-coordinate. */ + if (object->loc[2] < 0) { + invert_m4(rot); + mul_m4_m4m4(newmat, rot, mat); + BKE_object_apply_mat4(object, newmat, 0, 0); + } + } + else { + BKE_object_apply_mat4(object, mat, 0, 0); + } + + BKE_object_where_is_calc(scene, object); + set_axis(scene, object, clip, tracking_object, axis_track, 'X'); + + DAG_id_tag_update(&clip->id, 0); + DAG_id_tag_update(&object->id, OB_RECALC_OB); + + WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + + return OPERATOR_FINISHED; +} + +void CLIP_OT_set_plane(wmOperatorType *ot) +{ + static EnumPropertyItem plane_items[] = { + {0, "FLOOR", 0, "Floor", "Set floor plane"}, + {1, "WALL", 0, "Wall", "Set wall plane"}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Set Plane"; + ot->description = "Set plane based on 3 selected bundles by moving camera " + "(or its parent if present) in 3D space"; + ot->idname = "CLIP_OT_set_plane"; + + /* api callbacks */ + ot->exec = set_plane_exec; + ot->poll = set_orientation_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_enum(ot->srna, "plane", plane_items, 0, "Plane", + "Plane to be used for orientation"); +} + +/********************** set axis operator *********************/ + +static int set_axis_exec(bContext *C, wmOperator *op) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + MovieTracking *tracking = &clip->tracking; + MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking); + Scene *scene = CTX_data_scene(C); + Object *object; + int axis = RNA_enum_get(op->ptr, "axis"); + + if (count_selected_bundles(C) != 1) { + BKE_report(op->reports, + RPT_ERROR, + "Single track with bundle should be selected to define axis"); + return OPERATOR_CANCELLED; + } + + object = get_orientation_object(C); + if (object == NULL) { + BKE_report(op->reports, RPT_ERROR, "No object to apply orientation on"); + return OPERATOR_CANCELLED; + } + + ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, + tracking_object); + MovieTrackingTrack *track = tracksbase->first; + while (track) { + if (TRACK_VIEW_SELECTED(sc, track) && + (track->flag & TRACK_HAS_BUNDLE)) + { + break; + } + track = track->next; + } + + set_axis(scene, object, clip, tracking_object, track, axis == 0 ? 'X' : 'Y'); + + DAG_id_tag_update(&clip->id, 0); + DAG_id_tag_update(&object->id, OB_RECALC_OB); + + WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + + return OPERATOR_FINISHED; +} + +void CLIP_OT_set_axis(wmOperatorType *ot) +{ + static EnumPropertyItem axis_actions[] = { + {0, "X", 0, "X", "Align bundle align X axis"}, + {1, "Y", 0, "Y", "Align bundle align Y axis"}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Set Axis"; + ot->description = "Set direction of scene axis rotating camera " + "(or its parent if present) and assume selected track " + "lies on real axis, joining it with the origin"; + ot->idname = "CLIP_OT_set_axis"; + + /* api callbacks */ + ot->exec = set_axis_exec; + ot->poll = set_orientation_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_enum(ot->srna, "axis", axis_actions, 0, "Axis", + "Axis to use to align bundle along"); +} + +/********************** set scale operator *********************/ + +static int do_set_scale(bContext *C, + wmOperator *op, + bool scale_solution, + bool apply_scale) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + MovieTracking *tracking = &clip->tracking; + MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking); + MovieTrackingTrack *track; + Scene *scene = CTX_data_scene(C); + Object *object = NULL; + Object *camera = get_camera_with_movieclip(scene, clip); + ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); + int tot = 0; + float vec[2][3], mat[4][4], scale; + float dist = RNA_float_get(op->ptr, "distance"); + + if (count_selected_bundles(C) != 2) { + BKE_report(op->reports, + RPT_ERROR, + "Two tracks with bundles should be selected to set scale"); + return OPERATOR_CANCELLED; + } + + if (!scale_solution && !apply_scale) { + object = get_orientation_object(C); + if (object == NULL) { + BKE_report(op->reports, + RPT_ERROR, + "No object to apply orientation on"); + return OPERATOR_CANCELLED; + } + } + + BKE_tracking_get_camera_object_matrix(scene, camera, mat); + + track = tracksbase->first; + while (track) { + if (TRACK_VIEW_SELECTED(sc, track)) { + mul_v3_m4v3(vec[tot], mat, track->bundle_pos); + tot++; + } + track = track->next; + } + + sub_v3_v3(vec[0], vec[1]); + + if (len_v3(vec[0]) > 1e-5f) { + scale = dist / len_v3(vec[0]); + if (apply_scale) { + /* Apply scale on reconstructed scene itself. */ + MovieTrackingReconstruction *reconstruction = + BKE_tracking_get_active_reconstruction(tracking); + MovieReconstructedCamera *reconstructed_cameras; + int i; + + for (track = tracksbase->first; track; track = track->next) { + mul_v3_fl(track->bundle_pos, scale); + } + + reconstructed_cameras = reconstruction->cameras; + for (i = 0; i < reconstruction->camnr; i++) { + mul_v3_fl(reconstructed_cameras[i].mat[3], scale); + } + + WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + } + else { + if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { + mul_v3_fl(object->size, scale); + mul_v3_fl(object->loc, scale); + } + else if (!scale_solution) { + Object *solver_camera = object_solver_camera(scene, object); + + object->size[0] = object->size[1] = object->size[2] = 1.0f / scale; + + if (solver_camera) { + object->size[0] /= solver_camera->size[0]; + object->size[1] /= solver_camera->size[1]; + object->size[2] /= solver_camera->size[2]; + } + } + else { + tracking_object->scale = scale; + } + + DAG_id_tag_update(&clip->id, 0); + + if (object) + DAG_id_tag_update(&object->id, OB_RECALC_OB); + + WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + } + } + + return OPERATOR_FINISHED; +} + +static int set_scale_exec(bContext *C, wmOperator *op) +{ + return do_set_scale(C, op, false, false); +} + +static int set_scale_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + + if (!RNA_struct_property_is_set(op->ptr, "distance")) + RNA_float_set(op->ptr, "distance", clip->tracking.settings.dist); + + return set_scale_exec(C, op); +} + +void CLIP_OT_set_scale(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set Scale"; + ot->description = "Set scale of scene by scaling camera (or its parent if present)"; + ot->idname = "CLIP_OT_set_scale"; + + /* api callbacks */ + ot->exec = set_scale_exec; + ot->invoke = set_scale_invoke; + ot->poll = set_orientation_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_float(ot->srna, "distance", 0.0f, -FLT_MAX, FLT_MAX, + "Distance", "Distance between selected tracks", -100.0f, 100.0f); +} + +/********************** set solution scale operator *********************/ + +static int set_solution_scale_poll(bContext *C) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + if (sc != NULL) { + MovieClip *clip = ED_space_clip_get_clip(sc); + if (clip != NULL) { + MovieTracking *tracking = &clip->tracking; + MovieTrackingObject *tracking_object = + BKE_tracking_object_get_active(tracking); + return (tracking_object->flag & TRACKING_OBJECT_CAMERA) == 0; + } + } + return false; +} + +static int set_solution_scale_exec(bContext *C, wmOperator *op) +{ + return do_set_scale(C, op, true, false); +} + +static int set_solution_scale_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + + if (!RNA_struct_property_is_set(op->ptr, "distance")) { + RNA_float_set(op->ptr, + "distance", + clip->tracking.settings.object_distance); + } + + return set_solution_scale_exec(C, op); +} + +void CLIP_OT_set_solution_scale(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set Solution Scale"; + ot->description = "Set object solution scale using distance between " + "two selected tracks"; + ot->idname = "CLIP_OT_set_solution_scale"; + + /* api callbacks */ + ot->exec = set_solution_scale_exec; + ot->invoke = set_solution_scale_invoke; + ot->poll = set_solution_scale_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_float(ot->srna, "distance", 0.0f, -FLT_MAX, FLT_MAX, + "Distance", "Distance between selected tracks", + -100.0f, 100.0f); +} + +/********************** apply solution scale operator *********************/ + +static int apply_solution_scale_poll(bContext *C) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + if (sc != NULL) { + MovieClip *clip = ED_space_clip_get_clip(sc); + if (clip != NULL) { + MovieTracking *tracking = &clip->tracking; + MovieTrackingObject *tracking_object = + BKE_tracking_object_get_active(tracking); + return (tracking_object->flag & TRACKING_OBJECT_CAMERA) != 0; + } + } + return 0; +} + +static int apply_solution_scale_exec(bContext *C, wmOperator *op) +{ + return do_set_scale(C, op, false, true); +} + +static int apply_solution_scale_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + if (!RNA_struct_property_is_set(op->ptr, "distance")) { + RNA_float_set(op->ptr, "distance", clip->tracking.settings.dist); + } + return apply_solution_scale_exec(C, op); +} + +void CLIP_OT_apply_solution_scale(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Apply Solution Scale"; + ot->description = "Apply scale on solution itself to make distance between " + "selected tracks equals to desired"; + ot->idname = "CLIP_OT_apply_solution_scale"; + + /* api callbacks */ + ot->exec = apply_solution_scale_exec; + ot->invoke = apply_solution_scale_invoke; + ot->poll = apply_solution_scale_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_float(ot->srna, "distance", 0.0f, -FLT_MAX, FLT_MAX, + "Distance", "Distance between selected tracks", + -100.0f, 100.0f); +} diff --git a/source/blender/editors/space_clip/tracking_ops_plane.c b/source/blender/editors/space_clip/tracking_ops_plane.c new file mode 100644 index 00000000000..c5dc1c14dea --- /dev/null +++ b/source/blender/editors/space_clip/tracking_ops_plane.c @@ -0,0 +1,432 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2011 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, + * Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/space_clip/tracking_ops_plane.c + * \ingroup spclip + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "BLI_utildefines.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_tracking.h" +#include "BKE_depsgraph.h" +#include "BKE_report.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_clip.h" + +#include "clip_intern.h" +#include "tracking_ops_intern.h" + +/********************** Create plane track operator *********************/ + +static int create_plane_track_tracks_exec(bContext *C, wmOperator *op) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + MovieTracking *tracking = &clip->tracking; + MovieTrackingPlaneTrack *plane_track; + ListBase *tracks_base = BKE_tracking_get_active_tracks(tracking); + ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking); + int framenr = ED_space_clip_get_clip_frame_number(sc); + + plane_track = BKE_tracking_plane_track_add(tracking, + plane_tracks_base, + tracks_base, + framenr); + + if (plane_track == NULL) { + BKE_report(op->reports, RPT_ERROR, "Need at least 4 selected point tracks to create a plane"); + return OPERATOR_CANCELLED; + } + else { + BKE_tracking_tracks_deselect_all(tracks_base); + + plane_track->flag |= SELECT; + clip->tracking.act_track = NULL; + clip->tracking.act_plane_track = plane_track; + + /* Compute homoraphies and apply them on marker's corner, so we've got + * quite nice motion from the very beginning. + */ + BKE_tracking_track_plane_from_existing_motion(plane_track, framenr); + } + + WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip); + + return OPERATOR_FINISHED; +} + +void CLIP_OT_create_plane_track(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Create Plane Track"; + ot->description = "Create new plane track out of selected point tracks"; + ot->idname = "CLIP_OT_create_plane_track"; + + /* api callbacks */ + ot->exec = create_plane_track_tracks_exec; + ot->poll = ED_space_clip_tracking_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/********************** Slide plane marker corner operator *********************/ + +typedef struct SlidePlaneMarkerData { + int event_type; + MovieTrackingPlaneTrack *plane_track; + MovieTrackingPlaneMarker *plane_marker; + int width, height; + int corner_index; + float *corner; + int previous_mval[2]; + float previous_corner[2]; + float old_corner[2]; + bool accurate; +} SlidePlaneMarkerData; + +/* TODO(sergey): Use closest sliding zone semantic here. */ +static bool mouse_on_plane_slide_zone(SpaceClip *sc, + float co[2], + float slide_zone[2], + int width, + int height) +{ + const float size = 12.0f; + float dx, dy; + + dx = size / width / sc->zoom; + dy = size / height / sc->zoom; + + return IN_RANGE_INCL(co[0], slide_zone[0] - dx, slide_zone[0] + dx) && + IN_RANGE_INCL(co[1], slide_zone[1] - dy, slide_zone[1] + dy); +} + +static MovieTrackingPlaneTrack *tracking_plane_marker_check_slide( + bContext *C, + const wmEvent *event, + int *corner_r) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + ARegion *ar = CTX_wm_region(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + MovieTracking *tracking = &clip->tracking; + int width, height; + float co[2]; + ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking); + int framenr = ED_space_clip_get_clip_frame_number(sc); + + ED_space_clip_get_size(sc, &width, &height); + if (width == 0 || height == 0) { + return NULL; + } + + ED_clip_mouse_pos(sc, ar, event->mval, co); + + for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first; + plane_track != NULL; + plane_track = plane_track->next) + { + if (PLANE_TRACK_VIEW_SELECTED(plane_track)) { + MovieTrackingPlaneMarker *plane_marker = + BKE_tracking_plane_marker_get(plane_track, framenr); + bool ok = false; + int i; + + for (i = 0; i < 4; i++) { + if (mouse_on_plane_slide_zone(sc, + co, + plane_marker->corners[i], + width, + height)) + { + if (corner_r) { + *corner_r = i; + } + ok = true; + break; + } + } + + if (ok) { + return plane_track; + } + } + } + + return NULL; +} + +static void *slide_plane_marker_customdata(bContext *C, const wmEvent *event) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + ARegion *ar = CTX_wm_region(C); + MovieTrackingPlaneTrack *plane_track; + int width, height; + float co[2]; + SlidePlaneMarkerData *customdata = NULL; + int framenr = ED_space_clip_get_clip_frame_number(sc); + int corner; + + ED_space_clip_get_size(sc, &width, &height); + if (width == 0 || height == 0) { + return NULL; + } + + ED_clip_mouse_pos(sc, ar, event->mval, co); + + plane_track = tracking_plane_marker_check_slide(C, event, &corner); + if (plane_track) { + MovieTrackingPlaneMarker *plane_marker; + + customdata = MEM_callocN(sizeof(SlidePlaneMarkerData), "slide plane marker data"); + + customdata->event_type = event->type; + + plane_marker = BKE_tracking_plane_marker_ensure(plane_track, framenr); + + customdata->plane_track = plane_track; + customdata->plane_marker = plane_marker; + customdata->width = width; + customdata->height = height; + + customdata->previous_mval[0] = event->mval[0]; + customdata->previous_mval[1] = event->mval[1]; + + customdata->corner_index = corner; + customdata->corner = plane_marker->corners[corner]; + + copy_v2_v2(customdata->previous_corner, customdata->corner); + copy_v2_v2(customdata->old_corner, customdata->corner); + } + + return customdata; +} + +static int slide_plane_marker_invoke(bContext *C, + wmOperator *op, + const wmEvent *event) +{ + SlidePlaneMarkerData *slidedata = slide_plane_marker_customdata(C, event); + + if (slidedata) { + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + MovieTracking *tracking = &clip->tracking; + + tracking->act_plane_track = slidedata->plane_track; + tracking->act_track = NULL; + + op->customdata = slidedata; + + clip_tracking_hide_cursor(C); + WM_event_add_modal_handler(C, op); + + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + + return OPERATOR_RUNNING_MODAL; + } + + return OPERATOR_PASS_THROUGH; +} + +static void cancel_mouse_slide_plane_marker(SlidePlaneMarkerData *data) +{ + copy_v2_v2(data->corner, data->old_corner); +} + +static void free_slide_plane_marker_data(SlidePlaneMarkerData *data) +{ + MEM_freeN(data); +} + +static void slide_plane_marker_update_homographies(SpaceClip *sc, + SlidePlaneMarkerData *data) +{ + int framenr = ED_space_clip_get_clip_frame_number(sc); + + BKE_tracking_track_plane_from_existing_motion(data->plane_track, framenr); +} + +static int slide_plane_marker_modal(bContext *C, + wmOperator *op, + const wmEvent *event) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + SlidePlaneMarkerData *data = (SlidePlaneMarkerData *) op->customdata; + float dx, dy, mdelta[2]; + int next_corner_index, prev_corner_index, diag_corner_index; + const float *next_corner, *prev_corner, *diag_corner; + float next_edge[2], prev_edge[2], next_diag_edge[2], prev_diag_edge[2]; + + switch (event->type) { + case LEFTCTRLKEY: + case RIGHTCTRLKEY: + case LEFTSHIFTKEY: + case RIGHTSHIFTKEY: + if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY)) { + data->accurate = event->val == KM_PRESS; + } + + /* fall-through */ + case MOUSEMOVE: + mdelta[0] = event->mval[0] - data->previous_mval[0]; + mdelta[1] = event->mval[1] - data->previous_mval[1]; + + dx = mdelta[0] / data->width / sc->zoom; + dy = mdelta[1] / data->height / sc->zoom; + + if (data->accurate) { + dx /= 5.0f; + dy /= 5.0f; + } + + data->corner[0] = data->previous_corner[0] + dx; + data->corner[1] = data->previous_corner[1] + dy; + + + /* + prev_edge + (Corner 3, current) <----------------------- (Corner 2, previous) + | ^ + | | + | | + | | + next_edge | | next_diag_edge + | | + | | + | | + v | + (Corner 0, next) -----------------------> (Corner 1, diagonal) + prev_diag_edge + */ + + next_corner_index = (data->corner_index + 1) % 4; + prev_corner_index = (data->corner_index + 3) % 4; + diag_corner_index = (data->corner_index + 2) % 4; + + next_corner = data->plane_marker->corners[next_corner_index]; + prev_corner = data->plane_marker->corners[prev_corner_index]; + diag_corner = data->plane_marker->corners[diag_corner_index]; + + sub_v2_v2v2(next_edge, next_corner, data->corner); + sub_v2_v2v2(prev_edge, data->corner, prev_corner); + sub_v2_v2v2(next_diag_edge, prev_corner, diag_corner); + sub_v2_v2v2(prev_diag_edge, diag_corner, next_corner); + + if (cross_v2v2(prev_edge, next_edge) < 0.0f) { + closest_to_line_v2(data->corner, + data->corner, + prev_corner, + next_corner); + } + + if (cross_v2v2(next_diag_edge, prev_edge) < 0.0f) { + closest_to_line_v2(data->corner, + data->corner, + prev_corner, + diag_corner); + } + + if (cross_v2v2(next_edge, prev_diag_edge) < 0.0f) { + closest_to_line_v2(data->corner, + data->corner, + next_corner, + diag_corner); + } + + data->previous_mval[0] = event->mval[0]; + data->previous_mval[1] = event->mval[1]; + copy_v2_v2(data->previous_corner, data->corner); + + DAG_id_tag_update(&sc->clip->id, 0); + + WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, NULL); + + break; + + case LEFTMOUSE: + case RIGHTMOUSE: + if (event->type == data->event_type && event->val == KM_RELEASE) { + /* Marker is now keyframed. */ + data->plane_marker->flag &= ~PLANE_MARKER_TRACKED; + + slide_plane_marker_update_homographies(sc, data); + + free_slide_plane_marker_data(op->customdata); + + clip_tracking_show_cursor(C); + + DAG_id_tag_update(&sc->clip->id, 0); + WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip); + + return OPERATOR_FINISHED; + } + + break; + + case ESCKEY: + cancel_mouse_slide_plane_marker(op->customdata); + + free_slide_plane_marker_data(op->customdata); + + clip_tracking_show_cursor(C); + + WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip); + + return OPERATOR_CANCELLED; + } + + return OPERATOR_RUNNING_MODAL; +} + +void CLIP_OT_slide_plane_marker(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Slide Plane Marker"; + ot->description = "Slide plane marker areas"; + ot->idname = "CLIP_OT_slide_plane_marker"; + + /* api callbacks */ + ot->poll = ED_space_clip_tracking_poll; + ot->invoke = slide_plane_marker_invoke; + ot->modal = slide_plane_marker_modal; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR | OPTYPE_BLOCKING; +} diff --git a/source/blender/editors/space_clip/tracking_ops_solve.c b/source/blender/editors/space_clip/tracking_ops_solve.c new file mode 100644 index 00000000000..5c74c1947e3 --- /dev/null +++ b/source/blender/editors/space_clip/tracking_ops_solve.c @@ -0,0 +1,351 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, + * Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/space_clip/tracking_ops_solve.c + * \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_utildefines.h" +#include "BLI_string.h" + +#include "BKE_context.h" +#include "BKE_movieclip.h" +#include "BKE_tracking.h" +#include "BKE_global.h" +#include "BKE_depsgraph.h" +#include "BKE_report.h" +#include "BKE_library.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_clip.h" + + +#include "clip_intern.h" + +/********************** solve camera operator *********************/ + +typedef struct { + 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->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"); + + 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, short *stop, short *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; + + 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) { + 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: %.3f", + tracking->reconstruction.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); + WM_main_add_notifier(NC_OBJECT, camera); + } + + MEM_freeN(tracking->stats); + tracking->stats = NULL; + + DAG_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; + ScrArea *sa = CTX_wm_area(C); + 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), sa, WM_JOB_TYPE_ANY)) { + /* 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), sa, "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(0); + + /* 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_ANY)) + return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; + + /* Running solver. */ + switch (event->type) { + case 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; + } + + if (reconstruction->cameras != NULL) { + MEM_freeN(reconstruction->cameras); + reconstruction->cameras = NULL; + } + + reconstruction->camnr = 0; + reconstruction->flag &= ~TRACKING_RECONSTRUCTED; + + DAG_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; +} diff --git a/source/blender/editors/space_clip/tracking_ops_stabilize.c b/source/blender/editors/space_clip/tracking_ops_stabilize.c new file mode 100644 index 00000000000..8d6173e1cea --- /dev/null +++ b/source/blender/editors/space_clip/tracking_ops_stabilize.c @@ -0,0 +1,242 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, + * Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/space_clip/tracking_ops_stabilize.c + * \ingroup spclip + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_tracking.h" +#include "BKE_depsgraph.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_clip.h" + +#include "clip_intern.h" + +/********************* add 2d stabilization tracks operator ********************/ + +static int stabilize_2d_poll(bContext *C) +{ + if (ED_space_clip_tracking_poll(C)) { + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + MovieTrackingObject *tracking_object = + BKE_tracking_object_get_active(&clip->tracking); + return (tracking_object->flag & TRACKING_OBJECT_CAMERA) != 0; + } + return 0; +} + +static int stabilize_2d_add_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(tracking); + MovieTrackingStabilization *stab = &tracking->stabilization; + + bool update = false; + for (MovieTrackingTrack *track = tracksbase->first; + track != NULL; + track = track->next) + { + if (TRACK_VIEW_SELECTED(sc, track) && + (track->flag & TRACK_USE_2D_STAB) == 0) + { + track->flag |= TRACK_USE_2D_STAB; + stab->tot_track++; + update = true; + } + } + + if (update) { + stab->ok = 0; + DAG_id_tag_update(&clip->id, 0); + WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); + } + + return OPERATOR_FINISHED; +} + +void CLIP_OT_stabilize_2d_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Stabilization Tracks"; + ot->description = "Add selected tracks to 2D stabilization tool"; + ot->idname = "CLIP_OT_stabilize_2d_add"; + + /* api callbacks */ + ot->exec = stabilize_2d_add_exec; + ot->poll = stabilize_2d_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/******************* remove 2d stabilization tracks operator ******************/ + +static int stabilize_2d_remove_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; + MovieTrackingStabilization *stab = &tracking->stabilization; + ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); + int a = 0; + bool update = false; + + for (MovieTrackingTrack *track = tracksbase->first; + track != NULL; + track = track->next) + { + if (track->flag & TRACK_USE_2D_STAB) { + if (a == stab->act_track) { + track->flag &= ~TRACK_USE_2D_STAB; + stab->act_track--; + stab->tot_track--; + if (stab->act_track < 0) { + stab->act_track = 0; + } + update = true; + break; + } + a++; + } + } + + if (update) { + stab->ok = 0; + DAG_id_tag_update(&clip->id, 0); + WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); + } + + return OPERATOR_FINISHED; +} + +void CLIP_OT_stabilize_2d_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Stabilization Track"; + ot->description = "Remove selected track from stabilization"; + ot->idname = "CLIP_OT_stabilize_2d_remove"; + + /* api callbacks */ + ot->exec = stabilize_2d_remove_exec; + ot->poll = stabilize_2d_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/******************* select 2d stabilization tracks operator ******************/ + +static int stabilize_2d_select_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(tracking); + bool update = false; + + for (MovieTrackingTrack *track = tracksbase->first; + track != NULL; + track = track->next) + { + if (track->flag & TRACK_USE_2D_STAB) { + BKE_tracking_track_flag_set(track, TRACK_AREA_ALL, SELECT); + update = true; + } + } + + if (update) { + WM_event_add_notifier(C, NC_MOVIECLIP | ND_SELECT, clip); + } + + return OPERATOR_FINISHED; +} + +void CLIP_OT_stabilize_2d_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Stabilization Tracks"; + ot->description = "Select tracks which are used for stabilization"; + ot->idname = "CLIP_OT_stabilize_2d_select"; + + /* api callbacks */ + ot->exec = stabilize_2d_select_exec; + ot->poll = stabilize_2d_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/***************** set 2d stabilization rotation track operator ****************/ + +static int stabilize_2d_set_rotation_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; + MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking); + + if (act_track != NULL) { + MovieTrackingStabilization *stab = &tracking->stabilization; + stab->rot_track = act_track; + stab->ok = 0; + + DAG_id_tag_update(&clip->id, 0); + WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); + } + + return OPERATOR_FINISHED; +} + +void CLIP_OT_stabilize_2d_set_rotation(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set Rotation Track"; + ot->description = "Use active track to compensate rotation when " + "doing 2D stabilization"; + ot->idname = "CLIP_OT_stabilize_2d_set_rotation"; + + /* api callbacks */ + ot->exec = stabilize_2d_set_rotation_exec; + ot->poll = stabilize_2d_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/space_clip/tracking_ops_track.c b/source/blender/editors/space_clip/tracking_ops_track.c new file mode 100644 index 00000000000..580eff08e2c --- /dev/null +++ b/source/blender/editors/space_clip/tracking_ops_track.c @@ -0,0 +1,579 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, + * Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/space_clip/tracking_ops_track.c + * \ingroup spclip + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_math.h" +#include "BLI_blenlib.h" + +#include "BKE_main.h" +#include "BKE_context.h" +#include "BKE_movieclip.h" +#include "BKE_tracking.h" +#include "BKE_global.h" +#include "BKE_report.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_screen.h" +#include "ED_clip.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "PIL_time.h" + +#include "clip_intern.h" // own include +#include "tracking_ops_intern.h" + +/********************** Track operator *********************/ + +typedef struct TrackMarkersJob { + struct AutoTrackContext *context; /* Tracking context */ + int sfra, efra, lastfra; /* Start, end and recently tracked frames */ + int backwards; /* Backwards tracking flag */ + MovieClip *clip; /* Clip which is tracking */ + float delay; /* Delay in milliseconds to allow + * tracking at fixed FPS */ + + struct Main *main; + struct Scene *scene; + struct bScreen *screen; +} TrackMarkersJob; + +static bool track_markers_testbreak(void) +{ + return G.is_break; +} + +static int track_count_markers(SpaceClip *sc, + MovieClip *clip, + int framenr) +{ + int tot = 0; + ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking); + for (MovieTrackingTrack *track = tracksbase->first; + track != NULL; + track = track->next) + { + bool selected = (sc != NULL) ? TRACK_VIEW_SELECTED(sc, track) + : TRACK_SELECTED(track); + if (selected && (track->flag & TRACK_LOCKED) == 0) { + MovieTrackingMarker *marker = BKE_tracking_marker_get(track, + framenr); + if (!marker || (marker->flag & MARKER_DISABLED) == 0) { + tot++; + } + } + } + return tot; +} + +static void track_init_markers(SpaceClip *sc, + MovieClip *clip, + int framenr, + int *frames_limit_r) +{ + ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking); + int frames_limit = 0; + if (sc != NULL) { + clip_tracking_clear_invisible_track_selection(sc, clip); + } + for (MovieTrackingTrack *track = tracksbase->first; + track != NULL; + track = track->next) + { + bool selected = (sc != NULL) ? TRACK_VIEW_SELECTED(sc, track) + : TRACK_SELECTED(track); + if (selected) { + if ((track->flag & TRACK_HIDDEN) == 0 && + (track->flag & TRACK_LOCKED) == 0) + { + BKE_tracking_marker_ensure(track, framenr); + if (track->frames_limit) { + if (frames_limit == 0) { + frames_limit = track->frames_limit; + } + else { + frames_limit = min_ii(frames_limit, + (int)track->frames_limit); + } + } + } + } + } + *frames_limit_r = frames_limit; +} + +static bool track_markers_check_direction(int backwards, int curfra, int efra) +{ + if (backwards) { + if (curfra < efra) { + return false; + } + } + else { + if (curfra > efra) { + return false; + } + } + + return true; +} + +static int track_markers_initjob(bContext *C, + TrackMarkersJob *tmj, + int backwards) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + Scene *scene = CTX_data_scene(C); + MovieTrackingSettings *settings = &clip->tracking.settings; + int frames_limit; + int framenr = ED_space_clip_get_clip_frame_number(sc); + + track_init_markers(sc, clip, framenr, &frames_limit); + + tmj->sfra = ED_space_clip_get_clip_frame_number(sc); + tmj->clip = clip; + tmj->backwards = backwards; + + if (backwards) { + tmj->efra = SFRA; + } + else { + tmj->efra = EFRA; + } + + /* Limit frames to be tracked by user setting. */ + if (frames_limit) { + if (backwards) { + tmj->efra = MAX2(tmj->efra, tmj->sfra - frames_limit); + } + else { + tmj->efra = MIN2(tmj->efra, tmj->sfra + frames_limit); + } + } + + tmj->efra = BKE_movieclip_remap_scene_to_clip_frame(clip, tmj->efra); + + if (settings->speed != TRACKING_SPEED_FASTEST) { + tmj->delay = 1.0f / scene->r.frs_sec * 1000.0f; + + if (settings->speed == TRACKING_SPEED_HALF) { + tmj->delay *= 2; + } + else if (settings->speed == TRACKING_SPEED_QUARTER) { + tmj->delay *= 4; + } + else if (settings->speed == TRACKING_SPEED_DOUBLE) { + tmj->delay /= 2; + } + } + + tmj->context = BKE_autotrack_context_new(clip, &sc->user, backwards, 1); + + clip->tracking_context = tmj->context; + + tmj->lastfra = tmj->sfra; + + /* XXX: silly to store this, but this data is needed to update scene and + * movie-clip numbers when tracking is finished. This introduces + * better feedback for artists. + * Maybe there's another way to solve this problem, but can't think + * better way atm. + * Anyway, this way isn't more unstable as animation rendering + * animation which uses the same approach (except storing screen). + */ + tmj->scene = scene; + tmj->main = CTX_data_main(C); + tmj->screen = CTX_wm_screen(C); + + return track_markers_check_direction(backwards, tmj->sfra, tmj->efra); +} + +static void track_markers_startjob(void *tmv, + short *stop, + short *do_update, + float *progress) +{ + TrackMarkersJob *tmj = (TrackMarkersJob *)tmv; + int framenr = tmj->sfra; + + while (framenr != tmj->efra) { + if (tmj->delay > 0) { + /* Tracking should happen with fixed fps. Calculate time + * using current timer value before tracking frame and after. + * + * Small (and maybe unneeded optimization): do not calculate + * exec_time for "Fastest" tracking + */ + + double start_time = PIL_check_seconds_timer(), exec_time; + + if (!BKE_autotrack_context_step(tmj->context)) { + break; + } + + exec_time = PIL_check_seconds_timer() - start_time; + if (tmj->delay > (float)exec_time) { + PIL_sleep_ms(tmj->delay - (float)exec_time); + } + } + else if (!BKE_autotrack_context_step(tmj->context)) { + break; + } + + *do_update = true; + *progress = (float)(framenr - tmj->sfra) / (tmj->efra - tmj->sfra); + + if (tmj->backwards) { + framenr--; + } + else { + framenr++; + } + + tmj->lastfra = framenr; + + if (*stop || track_markers_testbreak()) { + break; + } + } +} + +static void track_markers_updatejob(void *tmv) +{ + TrackMarkersJob *tmj = (TrackMarkersJob *)tmv; + BKE_autotrack_context_sync(tmj->context); +} + +static void track_markers_endjob(void *tmv) +{ + TrackMarkersJob *tmj = (TrackMarkersJob *)tmv; + wmWindowManager *wm = tmj->main->wm.first; + + tmj->clip->tracking_context = NULL; + tmj->scene->r.cfra = BKE_movieclip_remap_clip_to_scene_frame(tmj->clip, + tmj->lastfra); + if (wm != NULL) { + ED_update_for_newframe(tmj->main, tmj->scene, 0); + } + + BKE_autotrack_context_sync(tmj->context); + BKE_autotrack_context_finish(tmj->context); + + WM_main_add_notifier(NC_SCENE | ND_FRAME, tmj->scene); +} + +static void track_markers_freejob(void *tmv) +{ + TrackMarkersJob *tmj = (TrackMarkersJob *)tmv; + BKE_autotrack_context_free(tmj->context); + MEM_freeN(tmj); +} + +/* TODO(sergey): Majority of the code here can be de-duplicated with the job. */ +static int track_markers_exec(bContext *C, wmOperator *op) +{ + SpaceClip *sc; + MovieClip *clip; + Scene *scene = CTX_data_scene(C); + struct AutoTrackContext *context; + MovieClipUser *user, fake_user = {0}; + int framenr, sfra, efra; + const bool backwards = RNA_boolean_get(op->ptr, "backwards"); + const bool sequence = RNA_boolean_get(op->ptr, "sequence"); + int frames_limit; + + if (RNA_struct_property_is_set(op->ptr, "clip")) { + Main *bmain = CTX_data_main(C); + char clip_name[MAX_ID_NAME - 2]; + + RNA_string_get(op->ptr, "clip", clip_name); + clip = (MovieClip *)BLI_findstring(&bmain->movieclip, + clip_name, + offsetof(ID, name) + 2); + sc = NULL; + + if (clip == NULL) { + return OPERATOR_CANCELLED; + } + framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, CFRA); + fake_user.framenr = framenr; + user = &fake_user; + } + else { + sc = CTX_wm_space_clip(C); + + if (sc == NULL) { + return OPERATOR_CANCELLED; + } + + clip = ED_space_clip_get_clip(sc); + framenr = ED_space_clip_get_clip_frame_number(sc); + user = &sc->user; + } + + sfra = framenr; + + if (track_count_markers(sc, clip, framenr) == 0) { + return OPERATOR_CANCELLED; + } + + track_init_markers(sc, clip, framenr, &frames_limit); + + if (backwards) { + efra = SFRA; + } + else { + efra = EFRA; + } + + /* Limit frames to be tracked by user setting. */ + if (frames_limit) { + if (backwards) + efra = MAX2(efra, sfra - frames_limit); + else + efra = MIN2(efra, sfra + frames_limit); + } + + efra = BKE_movieclip_remap_scene_to_clip_frame(clip, efra); + + if (!track_markers_check_direction(backwards, framenr, efra)) { + return OPERATOR_CANCELLED; + } + + /* Do not disable tracks due to threshold when tracking frame-by-frame. */ + context = BKE_autotrack_context_new(clip, user, backwards, sequence); + while (framenr != efra) { + if (!BKE_autotrack_context_step(context)) + break; + + if (backwards) framenr--; + else framenr++; + + if (!sequence) + break; + } + + BKE_autotrack_context_sync(context); + BKE_autotrack_context_finish(context); + BKE_autotrack_context_free(context); + + /* Update scene current frame to the lastes tracked frame. */ + scene->r.cfra = BKE_movieclip_remap_clip_to_scene_frame(clip, framenr); + + WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip); + WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); + + return OPERATOR_FINISHED; +} + +static int track_markers_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + TrackMarkersJob *tmj; + ScrArea *sa = CTX_wm_area(C); + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip; + wmJob *wm_job; + bool backwards = RNA_boolean_get(op->ptr, "backwards"); + bool sequence = RNA_boolean_get(op->ptr, "sequence"); + int framenr; + + if (sc == NULL) { + /* TODO(sergey): Support clip for invoke as well. */ + BKE_report(op->reports, RPT_ERROR, + "Invoking this operator only supported from " + "Clip Editor space"); + return OPERATOR_CANCELLED; + } + + clip = ED_space_clip_get_clip(sc); + BLI_assert(clip != NULL); + framenr = ED_space_clip_get_clip_frame_number(sc); + + if (WM_jobs_test(CTX_wm_manager(C), sa, WM_JOB_TYPE_ANY)) { + /* Only one tracking is allowed at a time. */ + return OPERATOR_CANCELLED; + } + + if (clip->tracking_context) { + return OPERATOR_CANCELLED; + } + + if (track_count_markers(sc, clip, framenr) == 0) { + return OPERATOR_CANCELLED; + } + + if (!sequence) { + return track_markers_exec(C, op); + } + + tmj = MEM_callocN(sizeof(TrackMarkersJob), "TrackMarkersJob data"); + if (!track_markers_initjob(C, tmj, backwards)) { + track_markers_freejob(tmj); + return OPERATOR_CANCELLED; + } + + /* Setup job. */ + wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), sa, + "Track Markers", + WM_JOB_PROGRESS, WM_JOB_TYPE_CLIP_TRACK_MARKERS); + WM_jobs_customdata_set(wm_job, tmj, track_markers_freejob); + + /* If there's delay set in tracking job, tracking should happen + * with fixed FPS. To deal with editor refresh we have to synchronize + * tracks from job and tracks in clip. Do this in timer callback + * to prevent threading conflicts. */ + if (tmj->delay > 0) { + WM_jobs_timer(wm_job, + tmj->delay / 1000.0f, NC_MOVIECLIP | NA_EVALUATED, 0); + } + else { + WM_jobs_timer(wm_job, 0.2, NC_MOVIECLIP | NA_EVALUATED, 0); + } + + WM_jobs_callbacks(wm_job, + track_markers_startjob, + NULL, + track_markers_updatejob, + track_markers_endjob); + + G.is_break = false; + + WM_jobs_start(CTX_wm_manager(C), wm_job); + WM_cursor_wait(0); + + /* Add modal handler for ESC. */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static int track_markers_modal(bContext *C, + wmOperator *UNUSED(op), + const wmEvent *event) +{ + /* No running tracking, remove handler and pass through. */ + if (0 == WM_jobs_test(CTX_wm_manager(C), CTX_wm_area(C), WM_JOB_TYPE_ANY)) { + return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; + } + + /* Running tracking. */ + switch (event->type) { + case ESCKEY: + return OPERATOR_RUNNING_MODAL; + } + + return OPERATOR_PASS_THROUGH; +} + +void CLIP_OT_track_markers(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Track Markers"; + ot->description = "Track selected markers"; + ot->idname = "CLIP_OT_track_markers"; + + /* api callbacks */ + ot->exec = track_markers_exec; + ot->invoke = track_markers_invoke; + ot->modal = track_markers_modal; + ot->poll = ED_space_clip_tracking_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "backwards", 0, "Backwards", + "Do backwards tracking"); + RNA_def_boolean(ot->srna, "sequence", 0, "Track Sequence", + "Track marker during image sequence rather than " + "single image"); + prop = RNA_def_string(ot->srna, "clip", NULL, MAX_NAME, "Movie Clip", + "Movie Clip to be tracked"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + +/********************** Refine track position operator *********************/ + +static int refine_marker_exec(bContext *C, wmOperator *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(tracking); + bool backwards = RNA_boolean_get(op->ptr, "backwards"); + int framenr = ED_space_clip_get_clip_frame_number(sc); + + for (MovieTrackingTrack *track = tracksbase->first; + track != NULL; + track = track->next) + { + if (TRACK_VIEW_SELECTED(sc, track)) { + MovieTrackingMarker *marker = BKE_tracking_marker_get(track, + framenr); + BKE_tracking_refine_marker(clip, track, marker, backwards); + } + } + + WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip); + + return OPERATOR_FINISHED; +} + +void CLIP_OT_refine_markers(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Refine Markers"; + ot->description = "Refine selected markers positions " + "by running the tracker from track's reference " + "to current frame"; + ot->idname = "CLIP_OT_refine_markers"; + + /* api callbacks */ + ot->exec = refine_marker_exec; + ot->poll = ED_space_clip_tracking_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "backwards", 0, "Backwards", + "Do backwards tracking"); +} diff --git a/source/blender/editors/space_clip/tracking_ops_utils.c b/source/blender/editors/space_clip/tracking_ops_utils.c new file mode 100644 index 00000000000..4fbee51407d --- /dev/null +++ b/source/blender/editors/space_clip/tracking_ops_utils.c @@ -0,0 +1,79 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, + * Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/space_clip/tracking_ops_utils.c + * \ingroup spclip + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_space_types.h" + +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_tracking.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "tracking_ops_intern.h" // own include + +void clip_tracking_clear_invisible_track_selection(SpaceClip *sc, + MovieClip *clip) +{ + ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking); + int hidden = 0; + if ((sc->flag & SC_SHOW_MARKER_PATTERN) == 0) { + hidden |= TRACK_AREA_PAT; + } + if ((sc->flag & SC_SHOW_MARKER_SEARCH) == 0) { + hidden |= TRACK_AREA_SEARCH; + } + if (hidden) { + for (MovieTrackingTrack *track = tracksbase->first; + track != NULL; + track = track->next) + { + if ((track->flag & TRACK_HIDDEN) == 0) { + BKE_tracking_track_flag_clear(track, hidden, SELECT); + } + } + } +} + +void clip_tracking_hide_cursor(bContext *C) +{ + wmWindow *win = CTX_wm_window(C); + WM_cursor_set(win, CURSOR_NONE); +} + +void clip_tracking_show_cursor(bContext *C) +{ + wmWindow *win = CTX_wm_window(C); + WM_cursor_set(win, CURSOR_STD); +} |