From 6ce31d173d02c88cdcdfc24ba331ff9e7edf4dee Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Fri, 20 Oct 2023 14:24:50 +0200 Subject: [PATCH] Fix #113505: Scale strips in nla with snap active seems broken Caused by bd305c8d18 `snap_transform_data` adds an offset to each element being transformed. However, this seems to only work for Move and Extend transformations. Therefore, the solution is to make the NLA snapping system more generic. Pull Request: https://projects.blender.org/blender/blender/pulls/113554 --- .../transform/transform_convert_nla.cc | 57 +----------- .../transform/transform_mode_timescale.cc | 25 ++++- .../editors/transform/transform_snap.cc | 17 ++++ .../editors/transform/transform_snap.hh | 1 + .../transform/transform_snap_animation.cc | 93 ++++++++++++++++++- 5 files changed, 130 insertions(+), 63 deletions(-) diff --git a/source/blender/editors/transform/transform_convert_nla.cc b/source/blender/editors/transform/transform_convert_nla.cc index 7751c7c6aee..41275b50616 100644 --- a/source/blender/editors/transform/transform_convert_nla.cc +++ b/source/blender/editors/transform/transform_convert_nla.cc @@ -634,67 +634,12 @@ static void createTransNlaData(bContext *C, TransInfo *t) ANIM_animdata_freelist(&anim_data); } -static void invert_snap(eSnapMode &snap_mode) -{ - if (snap_mode & SCE_SNAP_TO_FRAME) { - snap_mode &= ~SCE_SNAP_TO_FRAME; - snap_mode |= SCE_SNAP_TO_SECOND; - } - else if (snap_mode & SCE_SNAP_TO_SECOND) { - snap_mode &= ~SCE_SNAP_TO_SECOND; - snap_mode |= SCE_SNAP_TO_FRAME; - } -} - -static void snap_transform_data(TransInfo *t, TransDataContainer *tc) -{ - /* handle auto-snapping - * NOTE: only do this when transform is still running, or we can't restore - */ - if (t->state == TRANS_CANCEL) { - return; - } - if ((t->tsnap.flag & SCE_SNAP) == 0) { - return; - } - - eSnapMode snap_mode = t->tsnap.mode; - if (t->modifiers & MOD_SNAP_INVERT) { - invert_snap(snap_mode); - } - - float offset = 0; - float smallest_snap_delta = FLT_MAX; - - /* In order to move the strip in a block and not each end individually, - * find the minimal snap offset first and then shift the whole strip by that amount. */ - for (int i = 0; i < tc->data_len; i++) { - TransData td = tc->data[i]; - float snap_value; - transform_snap_anim_flush_data(t, &td, snap_mode, &snap_value); - - /* The snap_delta measures how far from the unsnapped position the value has moved. */ - const float snap_delta = *td.loc - snap_value; - if (fabs(snap_delta) < fabs(smallest_snap_delta)) { - offset = snap_value - td.iloc[0]; - smallest_snap_delta = snap_delta; - } - } - - for (int i = 0; i < tc->data_len; i++) { - TransData td = tc->data[i]; - *td.loc = td.iloc[0] + offset; - } -} - static void recalcData_nla(TransInfo *t) { SpaceNla *snla = (SpaceNla *)t->area->spacedata.first; - + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); - snap_transform_data(t, tc); - /* For each strip we've got, perform some additional validation of the values * that got set before using RNA to set the value (which does some special * operations when setting these values to make sure that everything works ok). diff --git a/source/blender/editors/transform/transform_mode_timescale.cc b/source/blender/editors/transform/transform_mode_timescale.cc index 23dbdd64171..44f7342efa6 100644 --- a/source/blender/editors/transform/transform_mode_timescale.cc +++ b/source/blender/editors/transform/transform_mode_timescale.cc @@ -33,6 +33,15 @@ /** \name Transform (Animation Time Scale) * \{ */ +static void timescale_snap_apply_fn(TransInfo *t, float vec[3]) +{ + float point[3]; + getSnapPoint(t, point); + const float fac = (point[0] - t->center_global[0]) / + (t->tsnap.snap_source[0] - t->center_global[0]); + vec[0] = fac; +} + static void headerTimeScale(TransInfo *t, char str[UI_MAX_DRAW_STR]) { char tvec[NUM_STR_REP_LEN * 3]; @@ -84,6 +93,9 @@ static void applyTimeScale(TransInfo *t) /* handle numeric-input stuff */ t->vec[0] = t->values[0]; applyNumInput(&t->num, &t->vec[0]); + + transform_snap_mixed_apply(t, &t->vec[0]); + t->values_final[0] = t->vec[0]; headerTimeScale(t, str); @@ -94,6 +106,15 @@ static void applyTimeScale(TransInfo *t) ED_area_status_text(t->area, str); } +static void timescale_transform_matrix_fn(TransInfo *t, float mat_xform[4][4]) +{ + const float i_loc = mat_xform[3][0]; + const float startx = t->center_global[0]; + const float fac = t->values_final[0]; + const float loc = ((i_loc - startx) * fac) + startx; + mat_xform[3][0] = loc; +} + static void initTimeScale(TransInfo *t, wmOperator * /*op*/) { float center[2]; @@ -141,9 +162,9 @@ TransModeInfo TransMode_timescale = { /*flags*/ T_NULL_ONE, /*init_fn*/ initTimeScale, /*transform_fn*/ applyTimeScale, - /*transform_matrix_fn*/ nullptr, + /*transform_matrix_fn*/ timescale_transform_matrix_fn, /*handle_event_fn*/ nullptr, /*snap_distance_fn*/ nullptr, - /*snap_apply_fn*/ nullptr, + /*snap_apply_fn*/ timescale_snap_apply_fn, /*draw_fn*/ nullptr, }; diff --git a/source/blender/editors/transform/transform_snap.cc b/source/blender/editors/transform/transform_snap.cc index 981c6a41429..136bf630cbf 100644 --- a/source/blender/editors/transform/transform_snap.cc +++ b/source/blender/editors/transform/transform_snap.cc @@ -68,6 +68,7 @@ static void snap_target_view3d_fn(TransInfo *t, float *vec); static void snap_target_uv_fn(TransInfo *t, float *vec); static void snap_target_node_fn(TransInfo *t, float *vec); static void snap_target_sequencer_fn(TransInfo *t, float *vec); +static void snap_target_nla_fn(TransInfo *t, float *vec); static void snap_source_median_fn(TransInfo *t); static void snap_source_center_fn(TransInfo *t); @@ -963,6 +964,11 @@ static void setSnappingCallback(TransInfo *t) /* The target is calculated along with the snap point. */ return; } + else if (t->spacetype == SPACE_NLA) { + t->tsnap.snap_target_fn = snap_target_nla_fn; + /* The target is calculated along with the snap point. */ + return; + } else { return; } @@ -1200,6 +1206,17 @@ static void snap_target_sequencer_fn(TransInfo *t, float * /*vec*/) } } +static void snap_target_nla_fn(TransInfo *t, float *vec) +{ + BLI_assert(t->spacetype == SPACE_NLA); + if (transform_snap_nla_calc(t, vec)) { + t->tsnap.status |= (SNAP_TARGET_FOUND | SNAP_SOURCE_FOUND); + } + else { + t->tsnap.status &= ~(SNAP_TARGET_FOUND | SNAP_SOURCE_FOUND); + } +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/transform/transform_snap.hh b/source/blender/editors/transform/transform_snap.hh index 335a89a0a54..79a23f518b9 100644 --- a/source/blender/editors/transform/transform_snap.hh +++ b/source/blender/editors/transform/transform_snap.hh @@ -85,3 +85,4 @@ void transform_snap_anim_flush_data(TransInfo *t, TransData *td, eSnapMode autosnap, float *r_val_final); +bool transform_snap_nla_calc(TransInfo *t, float *vec); diff --git a/source/blender/editors/transform/transform_snap_animation.cc b/source/blender/editors/transform/transform_snap_animation.cc index e3d1b135f4a..295e2b5dfda 100644 --- a/source/blender/editors/transform/transform_snap_animation.cc +++ b/source/blender/editors/transform/transform_snap_animation.cc @@ -8,6 +8,9 @@ #include "DNA_anim_types.h" +#include "BLI_math_matrix_types.hh" +#include "BLI_math_vector.h" + #include "BKE_context.h" #include "BKE_nla.h" @@ -17,6 +20,8 @@ #include "transform.hh" #include "transform_snap.hh" +using namespace blender; + /* -------------------------------------------------------------------- */ /** \name Snapping in Anim Editors * \{ */ @@ -68,14 +73,11 @@ void snapFrameTransform(TransInfo *t, } } -void transform_snap_anim_flush_data(TransInfo *t, - TransData *td, - const eSnapMode snap_mode, - float *r_val_final) +static void transform_snap_anim_flush_data_ex( + TransInfo *t, TransData *td, float val, const eSnapMode snap_mode, float *r_val_final) { BLI_assert(t->tsnap.flag); - float val = td->loc[0]; float ival = td->iloc[0]; AnimData *adt = static_cast(!ELEM(t->spacetype, SPACE_NLA, SPACE_SEQ) ? td->extra : nullptr); @@ -96,4 +98,85 @@ void transform_snap_anim_flush_data(TransInfo *t, *r_val_final = val; } +void transform_snap_anim_flush_data(TransInfo *t, + TransData *td, + const eSnapMode snap_mode, + float *r_val_final) +{ + transform_snap_anim_flush_data_ex(t, td, td->loc[0], snap_mode, r_val_final); +} + +static void invert_snap(eSnapMode &snap_mode) +{ + if (snap_mode & SCE_SNAP_TO_FRAME) { + snap_mode &= ~SCE_SNAP_TO_FRAME; + snap_mode |= SCE_SNAP_TO_SECOND; + } + else if (snap_mode & SCE_SNAP_TO_SECOND) { + snap_mode &= ~SCE_SNAP_TO_SECOND; + snap_mode |= SCE_SNAP_TO_FRAME; + } +} + +/* WORKAROUND: The source position is based on the transformed elements. + * However, at this stage, the transformation has not yet been applied. + * So apply the transformation here. */ +static float2 nla_transform_apply(TransInfo *t, float *vec, float2 &ival) +{ + float4x4 mat = float4x4::identity(); + + float values_final_prev[4]; + const size_t values_final_size = sizeof(*t->values_final) * size_t(t->idx_max + 1); + memcpy(values_final_prev, t->values_final, values_final_size); + memcpy(t->values_final, vec, values_final_size); + + mat[3][0] = ival[0]; + mat[3][1] = ival[1]; + transform_apply_matrix(t, mat.ptr()); + + memcpy(t->values_final, values_final_prev, values_final_size); + + return mat.location().xy(); +} + +bool transform_snap_nla_calc(TransInfo *t, float *vec) +{ + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + + eSnapMode snap_mode = t->tsnap.mode; + if (t->modifiers & MOD_SNAP_INVERT) { + invert_snap(snap_mode); + } + + float best_dist = FLT_MAX; + float2 best_source = float2(0); + float2 best_target = float2(0); + bool found = false; + + for (int i = 0; i < tc->data_len; i++) { + TransData *td = &tc->data[i]; + float2 snap_source = td->iloc; + float2 snap_target = nla_transform_apply(t, vec, snap_source); + + transform_snap_anim_flush_data_ex(t, td, snap_target[0], snap_mode, &snap_target[0]); + const int dist = abs(snap_target[0] - snap_source[0]); + if (dist < best_dist) { + if (dist != 0) { + /* Prioritize non-zero dist for scale. */ + best_dist = dist; + } + else if (found) { + continue; + } + best_source = snap_source; + best_target = snap_target; + found = true; + } + } + + copy_v2_v2(t->tsnap.snap_source, best_source); + copy_v2_v2(t->tsnap.snap_target, best_target); + return found; +} + /** \} */