diff options
Diffstat (limited to 'source/blender/editors/space_clip/tracking_ops_track.c')
-rw-r--r-- | source/blender/editors/space_clip/tracking_ops_track.c | 492 |
1 files changed, 492 insertions, 0 deletions
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..368cbeaf955 --- /dev/null +++ b/source/blender/editors/space_clip/tracking_ops_track.c @@ -0,0 +1,492 @@ +/* + * ***** 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, + bool backwards, + bool sequence) +{ + 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 (sequence) { + if (backwards) { + tmj->efra = SFRA; + } + else { + tmj->efra = EFRA; + } + } + else { + if (backwards) { + tmj->efra = tmj->sfra - 1; + } + else { + tmj->efra = tmj->sfra + 1; + } + } + + /* 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); +} + +static int track_markers(bContext *C, wmOperator *op, bool use_job) +{ + TrackMarkersJob *tmj; + ScrArea *sa = CTX_wm_area(C); + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + wmJob *wm_job; + bool backwards = RNA_boolean_get(op->ptr, "backwards"); + bool sequence = RNA_boolean_get(op->ptr, "sequence"); + int 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; + } + + tmj = MEM_callocN(sizeof(TrackMarkersJob), "TrackMarkersJob data"); + if (!track_markers_initjob(C, tmj, backwards, sequence)) { + track_markers_freejob(tmj); + return OPERATOR_CANCELLED; + } + + /* Setup job. */ + if (use_job && sequence) { + 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; + } + else { + short stop = 0, do_update = 0; + float progress = 0.0f; + track_markers_startjob(tmj, &stop, &do_update, &progress); + track_markers_endjob(tmj); + track_markers_freejob(tmj); + return OPERATOR_FINISHED; + } +} + +static int track_markers_exec(bContext *C, wmOperator *op) +{ + return track_markers(C, op, false); +} + +static int track_markers_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + return track_markers(C, op, true); +} + +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) +{ + /* 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"); +} + +/********************** 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"); +} |