Fix T95276: Effect strip animation not updated when moving strips

Since effect strips can't be transformed directly, their selection had
to be forced in order to process them. This often failed in more
complicated scenarios, because there was no attempt to parse hierarchy
completely. In worst case only one effect in chain would be selected.
This code was marked by `XXX_DURIAN_ANIM_TX_HACK`.

Instead solution described above, a collection of strips that depend
on non effect strip position is built by function
`query_time_dependent_strips_strips` and it is stored in `TransSeq`.
In `flushTransSeq` this collection is iterated and transformation offset
is applied to effect strip animation. Strips in collection should be
consistent with true state of dependency and should be complete.

Functional changes:
- When 2-input effect strip changes position, animation is offset even
if only handles are moved. This only applies to 2-input effect however.
- Selection is not extended to include effect strips anymore. If effects
are to be moved, they must be selected manually. This is because
previously it was very hard to reorganize effects in chain, since moving
first strip in chain would always select anywhere from 1 to n effects.
So creating or filling gap in channel would almos always result in
collision especially if their order in timeline doesn't perfectly
represent their order in chain.
This commit is contained in:
Richard Antalik 2022-04-08 19:21:47 +02:00
parent a3827d4b29
commit 32da64c17e
Notes: blender-bot 2023-02-14 06:49:54 +01:00
Referenced by issue #95276, Keyframes of Guassian Blur don't move while I'm moving the whole meta strip with GuassianBlur attached to it (under it)
1 changed files with 146 additions and 30 deletions

View File

@ -66,6 +66,9 @@ typedef struct TransSeq {
/* Initial rect of the view2d, used for computing offset during edge panning */
rctf initial_v2d_cur;
View2DEdgePanData edge_pan;
/* Strips that aren't selected, but their position entirely depends on transformed strips. */
SeqCollection *time_dependent_strips;
} TransSeq;
/* -------------------------------------------------------------------- */
@ -256,6 +259,7 @@ static void free_transform_custom_data(TransCustomData *custom_data)
{
if ((custom_data->data != NULL) && custom_data->use_free) {
TransSeq *ts = custom_data->data;
SEQ_collection_free(ts->time_dependent_strips);
MEM_freeN(ts->tdseq);
MEM_freeN(custom_data->data);
custom_data->data = NULL;
@ -630,10 +634,114 @@ static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *c
free_transform_custom_data(custom_data);
}
SeqCollection *query_selected_strips_no_handles(ListBase *seqbase)
{
SeqCollection *strips = SEQ_collection_create(__func__);
LISTBASE_FOREACH (Sequence *, seq, seqbase) {
if ((seq->flag & SELECT) != 0 && ((seq->flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL)) == 0)) {
SEQ_collection_append_strip(seq, strips);
}
}
return strips;
}
typedef enum SeqInputSide {
SEQ_INPUT_LEFT = -1,
SEQ_INPUT_RIGHT = 1,
} SeqInputSide;
static Sequence *effect_input_get(Sequence *effect, SeqInputSide side)
{
Sequence *input = effect->seq1;
if (effect->seq2 && (effect->seq2->startdisp - effect->seq1->startdisp) * side > 0) {
input = effect->seq2;
}
return input;
}
static Sequence *effect_base_input_get(Sequence *effect, SeqInputSide side)
{
Sequence *input = effect, *seq_iter = effect;
while (seq_iter != NULL) {
input = seq_iter;
seq_iter = effect_input_get(seq_iter, side);
}
return input;
}
/* Strips that aren't selected, but their position entirely depends on transformed strips.
* This collection is used to offset animation.*/
static SeqCollection *query_time_dependent_strips_strips(TransInfo *t)
{
ListBase *seqbase = seqbase_active_get(t);
/* Query dependent strips where used strips do not have handles selected.
* If all inputs of any effect even indirectly(through another effect) points to selected strip,
* it's position will change. */
SeqCollection *strips_no_handles = query_selected_strips_no_handles(seqbase);
/* Selection is needed as reference for related strips. */
SeqCollection *dependent = SEQ_collection_duplicate(strips_no_handles);
SEQ_collection_expand(seqbase, strips_no_handles, SEQ_query_strip_effect_chain);
bool strip_added = true;
while (strip_added) {
strip_added = false;
Sequence *seq;
SEQ_ITERATOR_FOREACH (seq, strips_no_handles) {
if (SEQ_collection_has_strip(seq, dependent)) {
continue; /* Strip is already in collection, skip it. */
}
/* If both seq1 and seq2 exist, both must be selected. */
if (seq->seq1 && SEQ_collection_has_strip(seq->seq1, dependent)) {
if (seq->seq2 && !SEQ_collection_has_strip(seq->seq2, dependent)) {
continue;
}
strip_added = true;
SEQ_collection_append_strip(seq, dependent);
}
}
}
SEQ_collection_free(strips_no_handles);
/* Query dependent strips where used strips do have handles selected.
* If any 2-input effect changes position because handles were moved, animation should be offset.
* With single input effect, it is less likely desirable to move animation. */
SeqCollection *selected_strips = SEQ_query_selected_strips(seqbase);
SEQ_collection_expand(seqbase, selected_strips, SEQ_query_strip_effect_chain);
Sequence *seq;
SEQ_ITERATOR_FOREACH (seq, selected_strips) {
/* Check only 2 input effects. */
if (seq->seq1 == NULL || seq->seq2 == NULL) {
continue;
}
/* Find immediate base inputs(left and right side). */
Sequence *input_left = effect_base_input_get(seq, SEQ_INPUT_LEFT);
Sequence *input_right = effect_base_input_get(seq, SEQ_INPUT_RIGHT);
if ((input_left->flag & SEQ_RIGHTSEL) != 0 && (input_right->flag & SEQ_LEFTSEL) != 0) {
SEQ_collection_append_strip(seq, dependent);
}
}
SEQ_collection_free(selected_strips);
/* Remove all non-effects. */
SEQ_ITERATOR_FOREACH (seq, dependent) {
if (SEQ_transform_sequence_can_be_translated(seq)) {
SEQ_collection_remove_strip(seq, dependent);
}
}
return dependent;
}
void createTransSeqData(TransInfo *t)
{
#define XXX_DURIAN_ANIM_TX_HACK
Scene *scene = t->scene;
Editing *ed = SEQ_editing_get(t->scene);
TransData *td = NULL;
@ -653,26 +761,6 @@ void createTransSeqData(TransInfo *t)
tc->custom.type.free_cb = freeSeqData;
t->frame_side = transform_convert_frame_side_dir_get(t, (float)CFRA);
#ifdef XXX_DURIAN_ANIM_TX_HACK
{
Sequence *seq;
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
/* hack */
if ((seq->flag & SELECT) == 0 && seq->type & SEQ_TYPE_EFFECT) {
Sequence *seq_user;
int i;
for (i = 0; i < 3; i++) {
seq_user = *((&seq->seq1) + i);
if (seq_user && (seq_user->flag & SELECT) && !(seq_user->flag & SEQ_LOCK) &&
!(seq_user->flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL))) {
seq->flag |= SELECT;
}
}
}
}
}
#endif
count = SeqTransCount(t, ed->seqbasep);
/* allocate memory for data */
@ -712,7 +800,7 @@ void createTransSeqData(TransInfo *t)
}
}
#undef XXX_DURIAN_ANIM_TX_HACK
ts->time_dependent_strips = query_time_dependent_strips_strips(t);
}
/** \} */
@ -771,7 +859,8 @@ static void flushTransSeq(TransInfo *t)
/* Editing null check already done */
ListBase *seqbasep = seqbase_active_get(t);
int a, new_frame;
int a, new_frame, offset;
TransData *td = NULL;
TransData2D *td2d = NULL;
TransDataSeq *tdsq = NULL;
@ -779,6 +868,15 @@ static void flushTransSeq(TransInfo *t)
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
/* This is calculated for offsetting animation of effects that change position with inputs.
* Maximum(positive or negative) value is used, because individual strips can be clamped. This
* works fairly well in most scenarios, but there can be some edge cases.
*
* Better solution would be to store effect position and calculate real offset. However with many
* (>5) effects in chain, there is visible lag in strip position update, because during
* recalculation, hierarchy is not taken into account. */
int max_offset = 0;
/* Flush to 2D vector from internally used 3D vector. */
for (a = 0, td = tc->data, td2d = tc->data_2d; a < tc->data_len; a++, td++, td2d++) {
tdsq = (TransDataSeq *)td->extra;
@ -788,31 +886,49 @@ static void flushTransSeq(TransInfo *t)
new_frame = round_fl_to_int(loc[0]);
switch (tdsq->sel_flag) {
case SELECT:
case SELECT: {
if (SEQ_transform_sequence_can_be_translated(seq)) {
const int offset = new_frame - tdsq->start_offset - seq->start;
offset = new_frame - tdsq->start_offset - seq->start;
SEQ_transform_translate_sequence(t->scene, seq, offset);
if (abs(offset) > abs(max_offset)) {
max_offset = offset;
}
}
seq->machine = round_fl_to_int(loc[1]);
CLAMP(seq->machine, 1, MAXSEQ);
break;
case SEQ_LEFTSEL: /* No vertical transform. */
}
case SEQ_LEFTSEL: { /* No vertical transform. */
int old_startdisp = seq->startdisp;
SEQ_transform_set_left_handle_frame(seq, new_frame);
SEQ_transform_handle_xlimits(seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL);
SEQ_transform_fix_single_image_seq_offsets(seq);
SEQ_time_update_sequence(t->scene, seqbasep, seq);
if (abs(seq->startdisp - old_startdisp) > abs(max_offset)) {
max_offset = seq->startdisp - old_startdisp;
}
break;
case SEQ_RIGHTSEL: /* No vertical transform. */
}
case SEQ_RIGHTSEL: { /* No vertical transform. */
int old_enddisp = seq->enddisp;
SEQ_transform_set_right_handle_frame(seq, new_frame);
SEQ_transform_handle_xlimits(seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL);
SEQ_transform_fix_single_image_seq_offsets(seq);
SEQ_time_update_sequence(t->scene, seqbasep, seq);
if (abs(seq->enddisp - old_enddisp) > abs(max_offset)) {
max_offset = seq->enddisp - old_enddisp;
}
break;
}
}
}
/* Update all effects. */
/* Update animation for effects. */
SEQ_ITERATOR_FOREACH (seq, ts->time_dependent_strips) {
SEQ_offset_animdata(t->scene, seq, max_offset);
}
/* Update effect length and position. */
if (ELEM(t->mode, TFM_SEQ_SLIDE, TFM_TIME_TRANSLATE)) {
for (seq = seqbasep->first; seq; seq = seq->next) {
if (seq->seq1 || seq->seq2 || seq->seq3) {