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