From 8bc2b9468a224410ca2dfc4d2eaffffd517df8e5 Mon Sep 17 00:00:00 2001 From: Wayde Moss Date: Wed, 13 Jan 2021 00:51:47 -0500 Subject: NLA: Strip Post-transform Vertical Shuffle and Auto-Grow Track List **Not ready for review** Differential Revision: https://developer.blender.org/D10103 --- source/blender/blenkernel/BKE_nla.h | 51 +- source/blender/blenkernel/intern/ipo.c | 6 +- source/blender/blenkernel/intern/nla.c | 262 ++++++--- source/blender/editors/object/object_add.c | 4 +- source/blender/editors/space_action/action_data.c | 4 +- source/blender/editors/space_nla/nla_channels.c | 8 +- source/blender/editors/space_nla/nla_draw.c | 9 +- source/blender/editors/space_nla/nla_edit.c | 43 +- .../editors/transform/transform_convert_nla.c | 622 +++++++++++++++++---- source/blender/makesdna/DNA_anim_types.h | 7 + source/blender/makesrna/intern/rna_animation.c | 5 +- source/blender/makesrna/intern/rna_nla.c | 6 +- 12 files changed, 801 insertions(+), 226 deletions(-) diff --git a/source/blender/blenkernel/BKE_nla.h b/source/blender/blenkernel/BKE_nla.h index 16d48024d07..f3b6d584e31 100644 --- a/source/blender/blenkernel/BKE_nla.h +++ b/source/blender/blenkernel/BKE_nla.h @@ -46,8 +46,8 @@ struct PropertyRNA; /* ----------------------------- */ /* Data Management */ -void BKE_nlastrip_free(ListBase *strips, struct NlaStrip *strip, bool do_id_user); -void BKE_nlatrack_free(ListBase *tracks, struct NlaTrack *nlt, bool do_id_user); +void BKE_nlastrip_free(struct NlaStrip *strip, bool do_id_user); +void BKE_nlatrack_free(struct NlaTrack *nlt, bool do_id_user); void BKE_nla_tracks_free(ListBase *tracks, bool do_id_user); struct NlaStrip *BKE_nlastrip_copy(struct Main *bmain, @@ -60,10 +60,39 @@ struct NlaTrack *BKE_nlatrack_copy(struct Main *bmain, const int flag); void BKE_nla_tracks_copy(struct Main *bmain, ListBase *dst, ListBase *src, const int flag); -struct NlaTrack *BKE_nlatrack_add(struct AnimData *adt, - struct NlaTrack *prev, - bool is_liboverride); +struct NlaTrack *BKE_nlatrack_new(); +void BKE_nlatrack_remove(ListBase *tracks, struct NlaTrack *nlt); +void BKE_nlatrack_remove_and_free(ListBase *tracks, struct NlaTrack *nlt, const bool do_id_user); + +void BKE_nlatrack_insert_after(ListBase *nla_tracks, + struct NlaTrack *prev, + struct NlaTrack *new_track, + const bool is_liboverride); + +void BKE_nlatrack_insert_before(ListBase *nla_tracks, + struct NlaTrack *next, + struct NlaTrack *new_track, + const bool is_liboverride); + +struct NlaTrack *BKE_nlatrack_new_after_and_set_active(ListBase *nla_tracks, + struct NlaTrack *prev, + const bool is_liboverride); + +struct NlaTrack *BKE_nlatrack_new_before_and_set_active(ListBase *nla_tracks, + struct NlaTrack *next, + const bool is_liboverride); + +struct NlaTrack *BKE_nlatrack_new_tail_and_set_active(ListBase *nla_tracks, + const bool is_liboverride); +struct NlaTrack *BKE_nlatrack_new_head_and_set_active(ListBase *nla_tracks, + const bool is_liboverride); + struct NlaStrip *BKE_nlastrip_new(struct bAction *act); + +void BKE_nlatrack_remove_strip(struct NlaTrack *track, struct NlaStrip *strip); +void BKE_nlastrip_remove(ListBase *strips, struct NlaStrip *strip); +void BKE_nlastrip_remove_and_free(ListBase *strips, struct NlaStrip *strip, const bool do_id_user); + struct NlaStrip *BKE_nlastack_add_strip(struct AnimData *adt, struct bAction *act, const bool is_liboverride); @@ -79,12 +108,13 @@ void BKE_nla_strip_foreach_id(struct NlaStrip *strip, struct LibraryForeachIDDat bool BKE_nlastrips_has_space(ListBase *strips, float start, float end); void BKE_nlastrips_sort_strips(ListBase *strips); -bool BKE_nlastrips_add_strip(ListBase *strips, struct NlaStrip *strip); +void BKE_nlastrips_add_strip(ListBase *strips, struct NlaStrip *strip); +bool BKE_nlastrips_try_add_strip(ListBase *strips, struct NlaStrip *strip); void BKE_nlastrips_make_metas(ListBase *strips, bool is_temp); void BKE_nlastrips_clear_metas(ListBase *strips, bool only_sel, bool only_temp); void BKE_nlastrips_clear_metastrip(ListBase *strips, struct NlaStrip *strip); -bool BKE_nlameta_add_strip(struct NlaStrip *mstrip, struct NlaStrip *strip); +bool BKE_nlameta_try_add_strip(struct NlaStrip *mstrip, struct NlaStrip *strip); void BKE_nlameta_flush_transforms(struct NlaStrip *mstrip); /* ............ */ @@ -99,9 +129,10 @@ void BKE_nlatrack_solo_toggle(struct AnimData *adt, struct NlaTrack *nlt); bool BKE_nlatrack_has_space(struct NlaTrack *nlt, float start, float end); void BKE_nlatrack_sort_strips(struct NlaTrack *nlt); -bool BKE_nlatrack_add_strip(struct NlaTrack *nlt, - struct NlaStrip *strip, - const bool is_liboverride); +void BKE_nlatrack_add_strip(struct NlaTrack *nlt, struct NlaStrip *strip); +bool BKE_nlatrack_try_add_strip(struct NlaTrack *nlt, + struct NlaStrip *strip, + const bool is_liboverride); bool BKE_nlatrack_get_bounds(struct NlaTrack *nlt, float bounds[2]); diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 1ab6e61e20e..a2979c86b8a 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -2012,12 +2012,12 @@ static void nlastrips_to_animdata(ID *id, ListBase *strips) } /* try to add this strip to the current NLA-Track (i.e. the 'last' one on the stack atm) */ - if (BKE_nlatrack_add_strip(nlt, strip, false) == 0) { + if (!BKE_nlatrack_try_add_strip(nlt, strip, false)) { /* trying to add to the current failed (no space), * so add a new track to the stack, and add to that... */ - nlt = BKE_nlatrack_add(adt, NULL, false); - BKE_nlatrack_add_strip(nlt, strip, false); + nlt = BKE_nlatrack_new_tail_and_set_active(&adt->nla_tracks, false); + BKE_nlatrack_add_strip(nlt, strip); } /* ensure that strip has a name */ diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index ecafc76c84d..a77e7a2d800 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -66,10 +66,9 @@ static CLG_LogRef LOG = {"bke.nla"}; /* Freeing ------------------------------------------- */ -/* Remove the given NLA strip from the NLA track it occupies, free the strip's data, - * and the strip itself. +/* Free the strip's data and the strip itself. */ -void BKE_nlastrip_free(ListBase *strips, NlaStrip *strip, bool do_id_user) +void BKE_nlastrip_free(NlaStrip *strip, bool do_id_user) { NlaStrip *cs, *csn; @@ -81,7 +80,7 @@ void BKE_nlastrip_free(ListBase *strips, NlaStrip *strip, bool do_id_user) /* free child-strips */ for (cs = strip->strips.first; cs; cs = csn) { csn = cs->next; - BKE_nlastrip_free(&strip->strips, cs, do_id_user); + BKE_nlastrip_remove_and_free(&strip->strips, cs, do_id_user); } /* remove reference to action */ @@ -98,20 +97,17 @@ void BKE_nlastrip_free(ListBase *strips, NlaStrip *strip, bool do_id_user) /* free own F-Modifiers */ free_fmodifiers(&strip->modifiers); +} - /* free the strip itself */ - if (strips) { - BLI_freelinkN(strips, strip); - } - else { - MEM_freeN(strip); - } +void BKE_nlatrack_remove_strip(NlaTrack *track, NlaStrip *strip) +{ + BLI_assert(track); + BKE_nlastrip_remove(&track->strips, strip); } -/* Remove the given NLA track from the set of NLA tracks, free the track's data, - * and the track itself. +/* Free the track's data and the track itself. */ -void BKE_nlatrack_free(ListBase *tracks, NlaTrack *nlt, bool do_id_user) +void BKE_nlatrack_free(NlaTrack *nlt, const bool do_id_user) { NlaStrip *strip, *stripn; @@ -123,16 +119,38 @@ void BKE_nlatrack_free(ListBase *tracks, NlaTrack *nlt, bool do_id_user) /* free strips */ for (strip = nlt->strips.first; strip; strip = stripn) { stripn = strip->next; - BKE_nlastrip_free(&nlt->strips, strip, do_id_user); + BKE_nlastrip_free(strip, do_id_user); } /* free NLA track itself now */ - if (tracks) { - BLI_freelinkN(tracks, nlt); - } - else { - MEM_freeN(nlt); - } + MEM_freeN(nlt); +} + +void BKE_nlastrip_remove(ListBase *strips, NlaStrip *strip) +{ + BLI_assert(strips); + BLI_remlink(strips, strip); +} + +void BKE_nlastrip_remove_and_free(ListBase *strips, NlaStrip *strip, const bool do_id_user) +{ + BKE_nlastrip_remove(strips, strip); + BKE_nlastrip_free(strip, do_id_user); +} + +void BKE_nlatrack_remove(ListBase *tracks, NlaTrack *nlt) +{ + BLI_assert(tracks); + BLI_remlink(tracks, nlt); +} + +/* Remove the given NLA track from the set of NLA tracks, free the track's data, + * and the track itself. + */ +void BKE_nlatrack_remove_and_free(ListBase *tracks, NlaTrack *nlt, const bool do_id_user) +{ + BKE_nlatrack_remove(tracks, nlt); + BKE_nlatrack_free(nlt, do_id_user); } /* Free the elements of type NLA Tracks provided in the given list, but do not free @@ -150,7 +168,7 @@ void BKE_nla_tracks_free(ListBase *tracks, bool do_id_user) /* free tracks one by one */ for (nlt = tracks->first; nlt; nlt = nltn) { nltn = nlt->next; - BKE_nlatrack_free(tracks, nlt, do_id_user); + BKE_nlatrack_remove_and_free(tracks, nlt, do_id_user); } /* clear the list's pointers to be safe */ @@ -277,27 +295,35 @@ void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, ListBase *src, const int fl /* Adding ------------------------------------------- */ -/* Add a NLA Track to the given AnimData - * - prev: NLA-Track to add the new one after - */ -NlaTrack *BKE_nlatrack_add(AnimData *adt, NlaTrack *prev, const bool is_liboverride) +NlaTrack *BKE_nlatrack_new() { - NlaTrack *nlt; - - /* sanity checks */ - if (adt == NULL) { - return NULL; - } - /* allocate new track */ - nlt = MEM_callocN(sizeof(NlaTrack), "NlaTrack"); + NlaTrack *nlt = MEM_callocN(sizeof(NlaTrack), "NlaTrack"); /* set settings requiring the track to not be part of the stack yet */ nlt->flag = NLATRACK_SELECTED | NLATRACK_OVERRIDELIBRARY_LOCAL; - nlt->index = BLI_listbase_count(&adt->nla_tracks); - /* In liboverride case, we only add local tracks after all those comming from the linked data, so - * we need to find the first local track. */ + return nlt; +} + +void BKE_nlatrack_insert_after(ListBase *nla_tracks, + NlaTrack *prev, + NlaTrack *new_track, + const bool is_liboverride) +{ + BLI_assert(!ELEM(NULL, nla_tracks, new_track)); + + /** If NULL, then caller intends to insert a new head. But, tracks are not allowed to be placed + * before library overrides. So it must inserted after the last override. */ + if (prev == NULL) { + NlaTrack *first_track = (NlaTrack *)nla_tracks->first; + if ((first_track->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0) { + prev = first_track; + } + } + + /* In liboverride case, we only add local tracks after all those comming from the linked data, + * so we need to find the first local track. */ if (is_liboverride && prev != NULL && (prev->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0) { NlaTrack *first_local = prev->next; for (; first_local != NULL && (first_local->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0; @@ -306,21 +332,81 @@ NlaTrack *BKE_nlatrack_add(AnimData *adt, NlaTrack *prev, const bool is_liboverr prev = first_local != NULL ? first_local->prev : NULL; } /* Add track to stack, and make it the active one. */ - if (prev != NULL) { - BLI_insertlinkafter(&adt->nla_tracks, prev, nlt); - } - else { - BLI_addtail(&adt->nla_tracks, nlt); - } - BKE_nlatrack_set_active(&adt->nla_tracks, nlt); + BLI_insertlinkafter(nla_tracks, prev, new_track); + new_track->index = BLI_findindex(nla_tracks, new_track); /* must have unique name, but we need to seed this */ - strcpy(nlt->name, "NlaTrack"); - BLI_uniquename( - &adt->nla_tracks, nlt, DATA_("NlaTrack"), '.', offsetof(NlaTrack, name), sizeof(nlt->name)); + strcpy(new_track->name, "NlaTrack"); + BLI_uniquename(nla_tracks, + new_track, + DATA_("NlaTrack"), + '.', + offsetof(NlaTrack, name), + sizeof(new_track->name)); +} - /* return the new track */ - return nlt; +void BKE_nlatrack_insert_before(ListBase *nla_tracks, + NlaTrack *next, + NlaTrack *new_track, + const bool is_liboverride) +{ + if (is_liboverride) { + + /** Currently, all library override tracks are assumed to be grouped together at the start of + * the list. So we can only add the new track after the last library track. */ + if (next != NULL && (next->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0) { + BKE_nlatrack_insert_after(nla_tracks, next, new_track, is_liboverride); + return; + } + } + + BLI_insertlinkbefore(nla_tracks, next, new_track); + new_track->index = BLI_findindex(nla_tracks, new_track); + + /* Must have unique name, but we need to seed this. */ + strcpy(new_track->name, "NlaTrack"); + BLI_uniquename(nla_tracks, + new_track, + DATA_("NlaTrack"), + '.', + offsetof(NlaTrack, name), + sizeof(new_track->name)); +} + +NlaTrack *BKE_nlatrack_new_after_and_set_active(ListBase *nla_tracks, + NlaTrack *prev, + const bool is_liboverride) +{ + NlaTrack *new_track = BKE_nlatrack_new(); + + BKE_nlatrack_insert_after(nla_tracks, prev, new_track, is_liboverride); + BKE_nlatrack_set_active(nla_tracks, new_track); + + return new_track; +} + +NlaTrack *BKE_nlatrack_new_before_and_set_active(ListBase *nla_tracks, + NlaTrack *next, + const bool is_liboverride) +{ + NlaTrack *new_track = BKE_nlatrack_new(); + + BKE_nlatrack_insert_before(nla_tracks, next, new_track, is_liboverride); + BKE_nlatrack_set_active(nla_tracks, new_track); + + return new_track; +} + +NlaTrack *BKE_nlatrack_new_tail_and_set_active(ListBase *nla_tracks, const bool is_liboverride) +{ + return BKE_nlatrack_new_after_and_set_active( + nla_tracks, (NlaTrack *)nla_tracks->last, is_liboverride); +} + +NlaTrack *BKE_nlatrack_new_head_and_set_active(ListBase *nla_tracks, const bool is_liboverride) +{ + return BKE_nlatrack_new_before_and_set_active( + nla_tracks, (NlaTrack *)nla_tracks->first, is_liboverride); } /* Create a NLA Strip referencing the given Action */ @@ -382,12 +468,12 @@ NlaStrip *BKE_nlastack_add_strip(AnimData *adt, bAction *act, const bool is_libo } /* firstly try adding strip to last track, but if that fails, add to a new track */ - if (BKE_nlatrack_add_strip(adt->nla_tracks.last, strip, is_liboverride) == 0) { + if (!BKE_nlatrack_try_add_strip(adt->nla_tracks.last, strip, is_liboverride)) { /* trying to add to the last track failed (no track or no space), * so add a new track to the stack, and add to that... */ - nlt = BKE_nlatrack_add(adt, NULL, is_liboverride); - BKE_nlatrack_add_strip(nlt, strip, is_liboverride); + nlt = BKE_nlatrack_new_tail_and_set_active(&adt->nla_tracks, is_liboverride); + BKE_nlatrack_add_strip(nlt, strip); } /* automatically name it too */ @@ -688,7 +774,7 @@ void BKE_nlastrips_sort_strips(ListBase *strips) for (sstrip = tmp.last; sstrip; sstrip = sstrip->prev) { /* check if add after */ - if (sstrip->end <= strip->start) { + if (sstrip->start <= strip->start) { BLI_insertlinkafter(&tmp, sstrip, strip); not_added = 0; break; @@ -709,25 +795,18 @@ void BKE_nlastrips_sort_strips(ListBase *strips) /* Add the given NLA-Strip to the given list of strips, assuming that it * isn't currently a member of another list */ -bool BKE_nlastrips_add_strip(ListBase *strips, NlaStrip *strip) +void BKE_nlastrips_add_strip(ListBase *strips, NlaStrip *strip) { NlaStrip *ns; bool not_added = true; /* sanity checks */ - if (ELEM(NULL, strips, strip)) { - return false; - } - - /* check if any space to add */ - if (BKE_nlastrips_has_space(strips, strip->start, strip->end) == 0) { - return false; - } + BLI_assert(!ELEM(NULL, strips, strip)); /* find the right place to add the strip to the nominated track */ for (ns = strips->first; ns; ns = ns->next) { /* if current strip occurs after the new strip, add it before */ - if (ns->start >= strip->end) { + if (ns->start >= strip->start) { BLI_insertlinkbefore(strips, ns, strip); not_added = 0; break; @@ -737,8 +816,20 @@ bool BKE_nlastrips_add_strip(ListBase *strips, NlaStrip *strip) /* just add to the end of the list of the strips then... */ BLI_addtail(strips, strip); } +} + +/** This version does additional checks (NULL check and space check). */ +bool BKE_nlastrips_try_add_strip(ListBase *strips, NlaStrip *strip) +{ + if (ELEM(NULL, strips, strip)) { + return false; + } + + if (BKE_nlastrips_has_space(strips, strip->start, strip->end) == 0) { + return false; + } - /* added... */ + BKE_nlastrips_add_strip(strips, strip); return true; } @@ -821,7 +912,7 @@ void BKE_nlastrips_clear_metastrip(ListBase *strips, NlaStrip *strip) } /* free the meta-strip now */ - BKE_nlastrip_free(strips, strip, true); + BKE_nlastrip_remove_and_free(strips, strip, true); } /* Remove meta-strips (i.e. flatten the list of strips) from the top-level of the list of strips @@ -856,7 +947,7 @@ void BKE_nlastrips_clear_metas(ListBase *strips, bool only_sel, bool only_temp) /* Add the given NLA-Strip to the given Meta-Strip, assuming that the * strip isn't attached to any list of strips */ -bool BKE_nlameta_add_strip(NlaStrip *mstrip, NlaStrip *strip) +bool BKE_nlameta_try_add_strip(NlaStrip *mstrip, NlaStrip *strip) { /* sanity checks */ if (ELEM(NULL, mstrip, strip)) { @@ -901,7 +992,7 @@ bool BKE_nlameta_add_strip(NlaStrip *mstrip, NlaStrip *strip) } /* just try to add to the meta-strip (no dimension changes needed) */ - return BKE_nlastrips_add_strip(&mstrip->strips, strip); + return BKE_nlastrips_try_add_strip(&mstrip->strips, strip); } /* Adjust the settings of NLA-Strips contained within a Meta-Strip (recursively), @@ -1141,10 +1232,10 @@ void BKE_nlatrack_sort_strips(NlaTrack *nlt) BKE_nlastrips_sort_strips(&nlt->strips); } -/* Add the given NLA-Strip to the given NLA-Track, assuming that it - * isn't currently attached to another one +/** Compared to non-try version, this function does checks (NULL, track flags, whether track has + * space for strip, etc). */ -bool BKE_nlatrack_add_strip(NlaTrack *nlt, NlaStrip *strip, const bool is_liboverride) +bool BKE_nlatrack_try_add_strip(NlaTrack *nlt, NlaStrip *strip, const bool is_liboverride) { /* sanity checks */ if (ELEM(NULL, nlt, strip)) { @@ -1158,7 +1249,13 @@ bool BKE_nlatrack_add_strip(NlaTrack *nlt, NlaStrip *strip, const bool is_libove } /* try to add the strip to the track using a more generic function */ - return BKE_nlastrips_add_strip(&nlt->strips, strip); + return BKE_nlastrips_try_add_strip(&nlt->strips, strip, is_liboverride); +} + +void BKE_nlatrack_add_strip(NlaTrack *nlt, NlaStrip *strip) +{ + BLI_assert(!ELEM(NULL, nlt, strip)); + BKE_nlastrips_add_strip(&nlt->strips, strip); } /* Get the extents of the given NLA-Track including gaps between strips, @@ -1803,6 +1900,25 @@ void BKE_nla_validate_state(AnimData *adt) return; } + /** Ensure every transition's start/end properly set. */ + LISTBASE_FOREACH_MUTABLE (NlaTrack *, track, &adt->nla_tracks) { + LISTBASE_FOREACH_MUTABLE (NlaStrip *, strip, &track->strips) { + if (strip->type & NLASTRIP_TYPE_TRANSITION) { + if (strip->prev) { + strip->start = strip->prev->end; + } + + if (strip->next) { + strip->end = strip->next->start; + } + + if (strip->start >= strip->end || strip->prev == NULL || strip->next == NULL) { + BKE_nlastrip_remove_and_free(&track->strips, strip, true); + } + } + } + } + /* Adjust blending values for auto-blending, * and also do an initial pass to find the earliest strip. */ for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) { @@ -1900,7 +2016,7 @@ bool BKE_nla_action_stash(AnimData *adt, const bool is_liboverride) } } - nlt = BKE_nlatrack_add(adt, prev_track, is_liboverride); + nlt = BKE_nlatrack_new_after_and_set_active(&adt->nla_tracks, prev_track, is_liboverride); BLI_assert(nlt != NULL); /* We need to ensure that if there wasn't any previous instance, @@ -1920,7 +2036,7 @@ bool BKE_nla_action_stash(AnimData *adt, const bool is_liboverride) strip = BKE_nlastrip_new(adt->action); BLI_assert(strip != NULL); - BKE_nlatrack_add_strip(nlt, strip, is_liboverride); + BKE_nlatrack_add_strip(nlt, strip); BKE_nlastrip_validate_name(adt, strip); /* mark the stash track and strip so that they doesn't disturb the stack animation, diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 90f9b83ba67..78442a7e9a7 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1717,13 +1717,13 @@ static int object_speaker_add_exec(bContext *C, wmOperator *op) { /* create new data for NLA hierarchy */ AnimData *adt = BKE_animdata_add_id(&ob->id); - NlaTrack *nlt = BKE_nlatrack_add(adt, NULL, is_liboverride); + NlaTrack *nlt = BKE_nlatrack_new_tail_and_set_active(&adt->nla_tracks, is_liboverride); NlaStrip *strip = BKE_nla_add_soundstrip(bmain, scene, ob->data); strip->start = CFRA; strip->end += strip->start; /* hook them up */ - BKE_nlatrack_add_strip(nlt, strip, is_liboverride); + BKE_nlatrack_add_strip(nlt, strip); /* auto-name the strip, and give the track an interesting name */ BLI_strncpy(nlt->name, DATA_("SoundTrack"), sizeof(nlt->name)); diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c index 4d5a93f75e0..153be5a1220 100644 --- a/source/blender/editors/space_action/action_data.c +++ b/source/blender/editors/space_action/action_data.c @@ -639,11 +639,11 @@ void ED_animedit_unlink_action( if (strip->act == act) { /* Remove this strip, and the track too if it doesn't have anything else */ - BKE_nlastrip_free(&nlt->strips, strip, true); + BKE_nlastrip_remove_and_free(&nlt->strips, strip, true); if (nlt->strips.first == NULL) { BLI_assert(nstrip == NULL); - BKE_nlatrack_free(&adt->nla_tracks, nlt, true); + BKE_nlatrack_remove_and_free(&adt->nla_tracks, nlt, true); } } } diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 763a3fd63e6..9d6ef103dfa 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -655,14 +655,14 @@ bool nlaedit_add_tracks_existing(bAnimContext *ac, bool above_sel) */ if (above_sel) { /* just add a new one above this one */ - BKE_nlatrack_add(adt, nlt, is_liboverride); + BKE_nlatrack_new_after_and_set_active(&adt->nla_tracks, nlt, is_liboverride); ale->update = ANIM_UPDATE_DEPS; added = true; } else if ((lastAdt == NULL) || (adt != lastAdt)) { /* add one track to the top of the owning AnimData's stack, * then don't add anymore to this stack */ - BKE_nlatrack_add(adt, NULL, is_liboverride); + BKE_nlatrack_new_tail_and_set_active(&adt->nla_tracks, is_liboverride); lastAdt = adt; ale->update = ANIM_UPDATE_DEPS; added = true; @@ -700,7 +700,7 @@ bool nlaedit_add_tracks_empty(bAnimContext *ac) /* ensure it is empty */ if (BLI_listbase_is_empty(&adt->nla_tracks)) { /* add new track to this AnimData block then */ - BKE_nlatrack_add(adt, NULL, ID_IS_OVERRIDE_LIBRARY(ale->id)); + BKE_nlatrack_new_tail_and_set_active(&adt->nla_tracks, ID_IS_OVERRIDE_LIBRARY(ale->id)); ale->update = ANIM_UPDATE_DEPS; added = true; } @@ -811,7 +811,7 @@ static int nlaedit_delete_tracks_exec(bContext *C, wmOperator *UNUSED(op)) } /* call delete on this track - deletes all strips too */ - BKE_nlatrack_free(&adt->nla_tracks, nlt, true); + BKE_nlatrack_remove_and_free(&adt->nla_tracks, nlt, true); ale->update = ANIM_UPDATE_DEPS; } } diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index 6fe980cf657..ff0ca2f3698 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -488,7 +488,8 @@ static void nla_draw_strip(SpaceNla *snla, } /* draw 'inside' of strip itself */ - if (non_solo == 0 && is_nlastrip_enabled(adt, nlt, strip)) { + if (non_solo == 0 && is_nlastrip_enabled(adt, nlt, strip) && + !(strip->flag & NLASTRIP_FLAG_FIX_LOCATION)) { immUnbindProgram(); /* strip is in normal track */ @@ -526,7 +527,11 @@ static void nla_draw_strip(SpaceNla *snla, /* draw strip outline * - color used here is to indicate active vs non-active */ - if (strip->flag & NLASTRIP_FLAG_ACTIVE) { + if (strip->flag & NLASTRIP_FLAG_FIX_LOCATION) { + color[0] = 1.0f; + color[1] = color[2] = 0.15f; + } + else if (strip->flag & NLASTRIP_FLAG_ACTIVE) { /* strip should appear 'sunken', so draw a light border around it */ color[0] = 0.9f; /* FIXME: hardcoded temp-hack colors */ color[1] = 1.0f; diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index 3fa1b614a03..9e02d430d98 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -672,12 +672,12 @@ static int nlaedit_add_actionclip_exec(bContext *C, wmOperator *op) strip->start = cfra; /* firstly try adding strip to our current track, but if that fails, add to a new track */ - if (BKE_nlatrack_add_strip(nlt, strip, is_liboverride) == 0) { + if (!BKE_nlatrack_try_add_strip(nlt, strip, is_liboverride)) { /* trying to add to the current failed (no space), * so add a new track to the stack, and add to that... */ - nlt = BKE_nlatrack_add(adt, NULL, is_liboverride); - BKE_nlatrack_add_strip(nlt, strip, is_liboverride); + nlt = BKE_nlatrack_new_tail_and_set_active(&adt->nla_tracks, is_liboverride); + BKE_nlatrack_add_strip(nlt, strip); } /* auto-name it */ @@ -901,12 +901,12 @@ static int nlaedit_add_sound_exec(bContext *C, wmOperator *UNUSED(op)) strip->end += cfra; /* firstly try adding strip to our current track, but if that fails, add to a new track */ - if (BKE_nlatrack_add_strip(nlt, strip, is_liboverride) == 0) { + if (!BKE_nlatrack_try_add_strip(nlt, strip, is_liboverride)) { /* trying to add to the current failed (no space), * so add a new track to the stack, and add to that... */ - nlt = BKE_nlatrack_add(adt, NULL, is_liboverride); - BKE_nlatrack_add_strip(nlt, strip, is_liboverride); + nlt = BKE_nlatrack_new_tail_and_set_active(&adt->nla_tracks, is_liboverride); + BKE_nlatrack_add_strip(nlt, strip); } /* auto-name it */ @@ -1123,13 +1123,14 @@ static int nlaedit_duplicate_exec(bContext *C, wmOperator *op) /* in case there's no space in the track above, * or we haven't got a reference to it yet, try adding */ - if (BKE_nlatrack_add_strip(nlt->next, nstrip, is_liboverride) == 0) { + if (!BKE_nlatrack_try_add_strip(nlt->next, nstrip, is_liboverride)) { /* need to add a new track above the one above the current one * - if the current one is the last one, nlt->next will be NULL, which defaults to adding * at the top of the stack anyway... */ - track = BKE_nlatrack_add(adt, nlt->next, is_liboverride); - BKE_nlatrack_add_strip(track, nstrip, is_liboverride); + track = BKE_nlatrack_new_after_and_set_active( + &adt->nla_tracks, nlt->next, is_liboverride); + BKE_nlatrack_add_strip(track, nstrip); } /* deselect the original and the active flag */ @@ -1238,15 +1239,15 @@ static int nlaedit_delete_exec(bContext *C, wmOperator *UNUSED(op)) if (strip->flag & NLASTRIP_FLAG_SELECT) { /* if a strip either side of this was a transition, delete those too */ if ((strip->prev) && (strip->prev->type == NLASTRIP_TYPE_TRANSITION)) { - BKE_nlastrip_free(&nlt->strips, strip->prev, true); + BKE_nlastrip_remove_and_free(&nlt->strips, strip->prev, true); } if ((nstrip) && (nstrip->type == NLASTRIP_TYPE_TRANSITION)) { nstrip = nstrip->next; - BKE_nlastrip_free(&nlt->strips, strip->next, true); + BKE_nlastrip_remove_and_free(&nlt->strips, strip->next, true); } /* finally, delete this strip */ - BKE_nlastrip_free(&nlt->strips, strip, true); + BKE_nlastrip_remove_and_free(&nlt->strips, strip, true); } } } @@ -1643,8 +1644,8 @@ static int nlaedit_swap_exec(bContext *C, wmOperator *op) } /* add strips back to track now */ - BKE_nlatrack_add_strip(nlt, area, is_liboverride); - BKE_nlatrack_add_strip(nlt, sb, is_liboverride); + BKE_nlatrack_try_add_strip(nlt, area, is_liboverride); + BKE_nlatrack_try_add_strip(nlt, sb, is_liboverride); } /* clear (temp) metastrips */ @@ -1729,8 +1730,8 @@ static int nlaedit_move_up_exec(bContext *C, wmOperator *UNUSED(op)) if (BKE_nlatrack_has_space(nltn, strip->start, strip->end)) { /* remove from its current track, and add to the one above * (it 'should' work, so no need to worry) */ - BLI_remlink(&nlt->strips, strip); - BKE_nlatrack_add_strip(nltn, strip, is_liboverride); + BKE_nlatrack_remove_strip(nlt, strip); + BKE_nlatrack_add_strip(nltn, strip); } } } @@ -1814,8 +1815,8 @@ static int nlaedit_move_down_exec(bContext *C, wmOperator *UNUSED(op)) if (BKE_nlatrack_has_space(nltp, strip->start, strip->end)) { /* remove from its current track, and add to the one above * (it 'should' work, so no need to worry) */ - BLI_remlink(&nlt->strips, strip); - BKE_nlatrack_add_strip(nltp, strip, is_liboverride); + BKE_nlatrack_remove_strip(nlt, strip); + BKE_nlatrack_add_strip(nltp, strip); } } } @@ -2306,10 +2307,10 @@ static int nlaedit_snap_exec(bContext *C, wmOperator *op) BLI_remlink(&tmp_strips, strip); /* in case there's no space in the current track, try adding */ - if (BKE_nlatrack_add_strip(nlt, strip, is_liboverride) == 0) { + if (!BKE_nlatrack_try_add_strip(nlt, strip, is_liboverride)) { /* need to add a new track above the current one */ - track = BKE_nlatrack_add(adt, nlt, is_liboverride); - BKE_nlatrack_add_strip(track, strip, is_liboverride); + track = BKE_nlatrack_new_after_and_set_active(&adt->nla_tracks, nlt, is_liboverride); + BKE_nlatrack_add_strip(track, strip); /* clear temp meta-strips on this new track, * as we may not be able to get back to it */ diff --git a/source/blender/editors/transform/transform_convert_nla.c b/source/blender/editors/transform/transform_convert_nla.c index ada668bcd04..850a5ab9a0c 100644 --- a/source/blender/editors/transform/transform_convert_nla.c +++ b/source/blender/editors/transform/transform_convert_nla.c @@ -29,6 +29,7 @@ #include "BLI_listbase.h" #include "BLI_math.h" +#include "BKE_anim_data.h" #include "BKE_context.h" #include "BKE_nla.h" @@ -63,10 +64,188 @@ typedef struct TransDataNla { /** index of track that strip is currently in. */ int trackIndex; + + /* Important: this index is relative to the initial first track at the start of transforming and + * thus can be negative when the tracks list grows downward. */ + int signed_track_index; /** handle-index: 0 for dummy entry, -1 for start, 1 for end, 2 for both ends. */ int handle; } TransDataNla; +static bool is_overlap(const float left_bound_a, + const float right_bound_a, + const float left_bound_b, + const float right_bound_b) +{ + return (left_bound_a < right_bound_b) && (right_bound_a > left_bound_b); +} + +static bool nlastrip_is_overlap(NlaStrip *strip_a, + float offset_a, + NlaStrip *strip_b, + float offset_b) +{ + return is_overlap(strip_a->start + offset_a, + strip_a->end + offset_a, + strip_b->start + offset_b, + strip_b->end + offset_b); +} + +/** Assumes strips to shuffle are tagged with NLASTRIP_FLAG_FIX_LOCATION. + * + * \returns The total sided offset that results in no overlaps between tagged strips and non-tagged + * strips. + */ +static float transdata_get_time_shuffle_offset_side(ListBase *trans_datas, const bool shuffle_left) +{ + float total_offset = 0; + + float offset; + do { + offset = 0; + + LISTBASE_FOREACH (LinkData *, link, trans_datas) { + TransDataNla *trans_data = (TransDataNla *)link->data; + NlaStrip *xformed_strip = trans_data->strip; + + LISTBASE_FOREACH (NlaStrip *, non_xformed_strip, &trans_data->nlt->strips) { + if (non_xformed_strip->flag & NLASTRIP_FLAG_FIX_LOCATION) { + continue; + } + + /* Allow overlap with transitions. */ + if (non_xformed_strip->type & NLASTRIP_TYPE_TRANSITION) { + continue; + } + + if (!nlastrip_is_overlap(non_xformed_strip, 0, xformed_strip, total_offset)) { + continue; + } + + if (shuffle_left) { + offset = min(offset, non_xformed_strip->start - (xformed_strip->end + total_offset)); + } + else { + offset = max(offset, non_xformed_strip->end - (xformed_strip->start + total_offset)); + } + } + } + + total_offset += offset; + } while (!IS_EQF(offset, 0.0f)); + + return total_offset; +} + +/** Assumes strips to shuffle are tagged with NLASTRIP_FLAG_FIX_LOCATION. + * + * \returns The minimal total signed offset that results in no overlaps between tagged strips and + * non-tagged strips. + */ +static float transdata_get_time_shuffle_offset(ListBase *trans_datas) +{ + const float offset_left = transdata_get_time_shuffle_offset_side(trans_datas, true); + const float offset_right = transdata_get_time_shuffle_offset_side(trans_datas, false); + + if (fabs(offset_left) < offset_right) { + return offset_left; + } + else { + return offset_right; + } +} + +/** Assumes all of given trans_datas are part of the same ID. + * + * \param r_total_offset: The minimal total signed offset that results in valid strip track-moves + * for all strips from \a trans_datas. + * + * \returns true if \a r_total_offset results in a valid offset, false if no solution exists in the + * desired direction. + */ +static bool transdata_get_track_shuffle_offset_side(ListBase *trans_datas, + const bool shuffle_down, + int *r_total_offset) +{ + *r_total_offset = 0; + if (BLI_listbase_is_empty(trans_datas)) { + return false; + } + + ListBase *tracks = &BKE_animdata_from_id( + ((TransDataNla *)((LinkData *)trans_datas->first)->data)->id) + ->nla_tracks; + + int offset; + do { + offset = 0; + + LISTBASE_FOREACH (LinkData *, link, trans_datas) { + TransDataNla *trans_data = (TransDataNla *)link->data; + NlaStrip *xformed_strip = trans_data->strip; + + NlaTrack *dst_track = BLI_findlink(tracks, trans_data->trackIndex + *r_total_offset); + + /** Cannot keep moving strip in given track direction. No solution. */ + if (dst_track == NULL) { + return false; + } + + /** Shuffle only if track is locked or library override. */ + if (((dst_track->flag & NLATRACK_PROTECTED) == 0) && + !BKE_nlatrack_is_nonlocal_in_liboverride(trans_data->id, dst_track)) { + continue; + } + + if (shuffle_down) { + offset = -1; + } + else { + offset = 1; + } + break; + } + + *r_total_offset += offset; + } while (offset != 0); + + return true; +} + +/** Assumes all of given trans_datas are part of the same ID. + * + * \param r_track_offset: The minimal total signed offset that results in valid strip track-moves + * for all strips from \a trans_datas. + * + * \returns true if \a r_track_offset results in a valid offset, false if no solution exists in + * either direction. + */ +static bool transdata_get_track_shuffle_offset(ListBase *trans_datas, int *r_track_offset) +{ + int offset_down = 0; + const bool down_valid = transdata_get_track_shuffle_offset_side(trans_datas, true, &offset_down); + + int offset_up = 0; + const bool up_valid = transdata_get_track_shuffle_offset_side(trans_datas, false, &offset_up); + + if (down_valid && up_valid) { + if (fabs(offset_down) < offset_up) { + *r_track_offset = offset_down; + } + else { + *r_track_offset = offset_up; + } + } + else if (down_valid) { + *r_track_offset = offset_down; + } + else if (up_valid) { + *r_track_offset = offset_up; + } + + return down_valid || up_valid; +} + /* -------------------------------------------------------------------- */ /** \name NLA Transform Creation * @@ -186,6 +365,7 @@ void createTransNlaData(bContext *C, TransInfo *t) tdn->oldTrack = tdn->nlt = nlt; tdn->strip = strip; tdn->trackIndex = BLI_findindex(&adt->nla_tracks, nlt); + tdn->signed_track_index = tdn->trackIndex; yval = (float)(tdn->trackIndex * NLACHANNEL_STEP(snla)); @@ -293,6 +473,8 @@ void recalcData_nla(TransInfo *t) double secf = FPS; int i; + const bool is_translating = ELEM(t->mode, TFM_TRANSLATION); + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); TransDataNla *tdn = tc->custom.type.data; @@ -302,7 +484,6 @@ void recalcData_nla(TransInfo *t) */ for (i = 0; i < tc->data_len; i++, tdn++) { NlaStrip *strip = tdn->strip; - PointerRNA strip_ptr; short pExceeded, nExceeded, iter; int delta_y1, delta_y2; @@ -310,6 +491,7 @@ void recalcData_nla(TransInfo *t) if (tdn->handle == 0) { continue; } + strip->flag &= ~NLASTRIP_FLAG_FIX_LOCATION; /* set refresh tags for objects using this animation, * BUT only if realtime updates are enabled @@ -352,48 +534,61 @@ void recalcData_nla(TransInfo *t) continue; } - /* firstly, check if the proposed transform locations would overlap with any neighboring strips - * (barring transitions) which are absolute barriers since they are not being moved - * - * this is done as a iterative procedure (done 5 times max for now) - */ - for (iter = 0; iter < 5; iter++) { - pExceeded = ((strip->prev) && (strip->prev->type != NLASTRIP_TYPE_TRANSITION) && - (tdn->h1[0] < strip->prev->end)); - nExceeded = ((strip->next) && (strip->next->type != NLASTRIP_TYPE_TRANSITION) && - (tdn->h2[0] > strip->next->start)); - - if ((pExceeded && nExceeded) || (iter == 4)) { - /* both endpoints exceeded (or iteration ping-pong'd meaning that we need a compromise) - * - Simply crop strip to fit within the bounds of the strips bounding it - * - If there were no neighbors, clear the transforms - * (make it default to the strip's current values). - */ - if (strip->prev && strip->next) { - tdn->h1[0] = strip->prev->end; - tdn->h2[0] = strip->next->start; - } - else { - tdn->h1[0] = strip->start; - tdn->h2[0] = strip->end; - } + const bool nlatrack_isliboverride = BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, tdn->nlt); + const bool allow_overlap = !nlatrack_isliboverride && is_translating; + if (allow_overlap) { + /** Reorder strips for proper nla stack evaluation while dragging. */ + while (strip->prev != NULL && tdn->h1[0] < strip->prev->start) { + BLI_listbase_swaplinks(&tdn->nlt->strips, strip, strip->prev); } - else if (nExceeded) { - /* move backwards */ - float offset = tdn->h2[0] - strip->next->start; - - tdn->h1[0] -= offset; - tdn->h2[0] -= offset; + while (strip->next != NULL && tdn->h1[0] > strip->next->start) { + BLI_listbase_swaplinks(&tdn->nlt->strips, strip, strip->next); } - else if (pExceeded) { - /* more forwards */ - float offset = strip->prev->end - tdn->h1[0]; + } + else { + /* firstly, check if the proposed transform locations would overlap with any neighboring + * strips (barring transitions) which are absolute barriers since they are not being moved + * + * this is done as a iterative procedure (done 5 times max for now) + */ + for (iter = 0; iter < 5; iter++) { + pExceeded = ((strip->prev) && (strip->prev->type != NLASTRIP_TYPE_TRANSITION) && + (tdn->h1[0] < strip->prev->end)); + nExceeded = ((strip->next) && (strip->next->type != NLASTRIP_TYPE_TRANSITION) && + (tdn->h2[0] > strip->next->start)); + + if ((pExceeded && nExceeded) || (iter == 4)) { + /* both endpoints exceeded (or iteration ping-pong'd meaning that we need a compromise) + * - Simply crop strip to fit within the bounds of the strips bounding it + * - If there were no neighbors, clear the transforms + * (make it default to the strip's current values). + */ + if (strip->prev && strip->next) { + tdn->h1[0] = strip->prev->end; + tdn->h2[0] = strip->next->start; + } + else { + tdn->h1[0] = strip->start; + tdn->h2[0] = strip->end; + } + } + else if (nExceeded) { + /* move backwards */ + float offset = tdn->h2[0] - strip->next->start; - tdn->h1[0] += offset; - tdn->h2[0] += offset; - } - else { /* all is fine and well */ - break; + tdn->h1[0] -= offset; + tdn->h2[0] -= offset; + } + else if (pExceeded) { + /* more forwards */ + float offset = strip->prev->end - tdn->h1[0]; + + tdn->h1[0] += offset; + tdn->h2[0] += offset; + } + else { /* all is fine and well */ + break; + } } } @@ -441,19 +636,30 @@ void recalcData_nla(TransInfo *t) } } - /* Use RNA to write the values to ensure that constraints on these are obeyed - * (e.g. for transition strips, the values are taken from the neighbors) - * - * NOTE: we write these twice to avoid truncation errors which can arise when - * moving the strips a large distance using numeric input T33852. - */ - RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &strip_ptr); + if (allow_overlap) { + /* Directly flush. */ + strip->start = tdn->h1[0]; + strip->end = tdn->h2[0]; + } + else { + /* Use RNA to write the values to ensure that constraints on these are obeyed + * (e.g. for transition strips, the values are taken from the neighbors) + * + * NOTE: we write these twice to avoid truncation errors which can arise when + * moving the strips a large distance using numeric input T33852. + * + * This also results in transition boundaries updating real-time and prevents + * transitions from being deleted due to invalid start/end frame. + */ + PointerRNA strip_ptr; + RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &strip_ptr); - RNA_float_set(&strip_ptr, "frame_start", tdn->h1[0]); - RNA_float_set(&strip_ptr, "frame_end", tdn->h2[0]); + RNA_float_set(&strip_ptr, "frame_start", tdn->h1[0]); + RNA_float_set(&strip_ptr, "frame_end", tdn->h2[0]); - RNA_float_set(&strip_ptr, "frame_start", tdn->h1[0]); - RNA_float_set(&strip_ptr, "frame_end", tdn->h2[0]); + RNA_float_set(&strip_ptr, "frame_start", tdn->h1[0]); + RNA_float_set(&strip_ptr, "frame_end", tdn->h2[0]); + } /* flush transforms to child strips (since this should be a meta) */ BKE_nlameta_flush_transforms(strip); @@ -463,71 +669,123 @@ void recalcData_nla(TransInfo *t) * as only one may have been altered by transform if only 1 handle moved. */ /* In LibOverride case, we cannot move strips across tracks that come from the linked data. */ - const bool is_liboverride = ID_IS_OVERRIDE_LIBRARY(tdn->id); - if (BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, tdn->nlt)) { + const bool id_is_liboverride = ID_IS_OVERRIDE_LIBRARY(tdn->id); + if (nlatrack_isliboverride) { continue; } - delta_y1 = ((int)tdn->h1[1] / NLACHANNEL_STEP(snla) - tdn->trackIndex); - delta_y2 = ((int)tdn->h2[1] / NLACHANNEL_STEP(snla) - tdn->trackIndex); + delta_y1 = ((int)tdn->h1[1] / NLACHANNEL_STEP(snla) - tdn->signed_track_index); + delta_y2 = ((int)tdn->h2[1] / NLACHANNEL_STEP(snla) - tdn->signed_track_index); + /* Move strip into track in the requested direction. */ if (delta_y1 || delta_y2) { NlaTrack *track; int delta = (delta_y2) ? delta_y2 : delta_y1; int n; - /* Move in the requested direction, - * checking at each layer if there's space for strip to pass through, - * stopping on the last track available or that we're able to fit in. + AnimData *anim_data = BKE_animdata_from_id(tdn->id); + ListBase *nla_tracks = &anim_data->nla_tracks; + + NlaTrack *old_track = tdn->nlt; + NlaTrack *dst_track = NULL; + + /** Calculate the total new tracks needed. + * + * Determine dst_track, which will end up being NULL, the last library override + * track, or a normal local track. The first two cases lead to delta_new_tracks!=0. + * The last case leads to delta_new_tracks==0. */ - if (delta > 0) { - for (track = tdn->nlt->next, n = 0; (track) && (n < delta); track = track->next, n++) { - /* check if space in this track for the strip */ - if (BKE_nlatrack_has_space(track, strip->start, strip->end) && - !BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, track)) { - /* move strip to this track */ - BLI_remlink(&tdn->nlt->strips, strip); - BKE_nlatrack_add_strip(track, strip, is_liboverride); - - tdn->nlt = track; - tdn->trackIndex++; - } - else { /* can't move any further */ + int delta_new_tracks = delta; + dst_track = old_track; + { + while (delta_new_tracks < 0) { + dst_track = dst_track->prev; + if (dst_track == NULL || BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, dst_track)) { break; } + delta_new_tracks++; } - } - else { - /* make delta 'positive' before using it, since we now know to go backwards */ - delta = -delta; - - for (track = tdn->nlt->prev, n = 0; (track) && (n < delta); track = track->prev, n++) { - /* check if space in this track for the strip */ - if (BKE_nlatrack_has_space(track, strip->start, strip->end) && - !BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, track)) { - /* move strip to this track */ - BLI_remlink(&tdn->nlt->strips, strip); - BKE_nlatrack_add_strip(track, strip, is_liboverride); - - tdn->nlt = track; - tdn->trackIndex--; - } - else { /* can't move any further */ + + /** We assume all library tracks are grouped at the bottom of the nla stack. Thus, no need + * to check for them when moving tracks upward. */ + while (delta_new_tracks > 0) { + dst_track = dst_track->next; + if (dst_track == NULL) { break; } + delta_new_tracks--; + } + } + + /** Auto-grow track list. */ + { + for (int i = 0; i < -delta_new_tracks; i++) { + NlaTrack *new_track = BKE_nlatrack_new(); + new_track->flag |= NLATRACK_TEMPORARILY_ADDED; + + BKE_nlatrack_insert_before( + nla_tracks, (NlaTrack *)nla_tracks->first, new_track, id_is_liboverride); + dst_track = new_track; + } + + for (int i = 0; i < delta_new_tracks; i++) { + NlaTrack *new_track = BKE_nlatrack_new(); + new_track->flag |= NLATRACK_TEMPORARILY_ADDED; + + BKE_nlatrack_insert_after( + nla_tracks, (NlaTrack *)nla_tracks->last, new_track, id_is_liboverride); + dst_track = new_track; } } + + /** Move strip from old_track to dst_track. */ + if (dst_track != old_track) { + BKE_nlatrack_remove_strip(old_track, strip); + BKE_nlastrips_add_strip(&dst_track->strips, strip, false); + + tdn->nlt = dst_track; + tdn->signed_track_index += delta; + tdn->trackIndex = BLI_findindex(nla_tracks, dst_track); + } + } + + if (tdn->nlt->flag & NLATRACK_PROTECTED) { + strip->flag |= NLASTRIP_FLAG_FIX_LOCATION; + } + + /** Flag overlaps with adjacent strips. + * + * Since the strips are re-ordered as they're transformed, we only have to check adjacent + * strips for overlap instead of all of them. */ + { + NlaStrip *adj_strip = strip->prev; + if (adj_strip != NULL && !(adj_strip->flag & NLASTRIP_FLAG_SELECT) && + nlastrip_is_overlap(strip, 0, adj_strip, 0)) { + strip->flag |= NLASTRIP_FLAG_FIX_LOCATION; + } + + adj_strip = strip->next; + if (adj_strip != NULL && !(adj_strip->flag & NLASTRIP_FLAG_SELECT) && + nlastrip_is_overlap(strip, 0, adj_strip, 0)) { + strip->flag |= NLASTRIP_FLAG_FIX_LOCATION; + } } } } - /** \} */ /* -------------------------------------------------------------------- */ /** \name Special After Transform NLA * \{ */ -void special_aftertrans_update__nla(bContext *C, TransInfo *UNUSED(t)) +typedef struct IDGroupedTransData { + struct IDGroupedTransData *next, *prev; + + ID *id; + ListBase trans_datas; +} IDGroupedTransData; + +void special_aftertrans_update__nla(bContext *C, TransInfo *t) { bAnimContext ac; @@ -536,30 +794,186 @@ void special_aftertrans_update__nla(bContext *C, TransInfo *UNUSED(t)) return; } - if (ac.datatype) { - ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT); + if (!ac.datatype) { + return; + } + + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + TransDataNla *first_trans_data = tc->custom.type.data; + const bool is_translating = ELEM(t->mode, TFM_TRANSLATION); + + /** Shuffle transformed strips. */ + if (is_translating) { + + /** Element: (IDGroupedTransData*) */ + ListBase grouped_trans_datas = {NULL, NULL}; + + /** Flag all non-library-override transformed strips so we can distinguish them when + * shuffling. + * + * Group trans_datas by ID so shuffling is unique per ID. + */ + { + TransDataNla *tdn = first_trans_data; + for (int i = 0; i < tc->data_len; i++, tdn++) { + + /* Skip dummy handles. */ + if (tdn->handle == 0) { + continue; + } + + /* For strips within library override tracks, don't do any shuffling at all. Unsure how + * library overrides should behave so, for now, they're treated as mostly immutable. */ + if ((tdn->nlt->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0) { + continue; + } + + tdn->strip->flag |= NLASTRIP_FLAG_FIX_LOCATION; + + IDGroupedTransData *dst_group = NULL; + /* Find dst_group with matching ID. */ + LISTBASE_FOREACH (IDGroupedTransData *, group, &grouped_trans_datas) { + if (group->id == tdn->id) { + dst_group = group; + break; + } + } + if (dst_group == NULL) { + dst_group = MEM_callocN(sizeof(IDGroupedTransData), __func__); + dst_group->id = tdn->id; + BLI_addhead(&grouped_trans_datas, dst_group); + } + + BLI_addtail(&dst_group->trans_datas, BLI_genericNodeN(tdn)); + } + } + + /** Apply shuffling. */ + LISTBASE_FOREACH (IDGroupedTransData *, group, &grouped_trans_datas) { + ListBase *trans_datas = &group->trans_datas; - /* get channels to work on */ + /** Apply vertical shuffle. */ + int minimum_track_offset = 0; + const bool track_offset_valid = transdata_get_track_shuffle_offset(trans_datas, + &minimum_track_offset); + /** Debug assert to ensure strips preserved their relative track offsets from eachother and + * none were compressed. Otherwise, no amount of vertical shuffling is a solution. + * + * This is considered a bug. */ + BLI_assert(track_offset_valid); + + if (minimum_track_offset != 0) { + ListBase *tracks = &BKE_animdata_from_id(group->id)->nla_tracks; + + LISTBASE_FOREACH (LinkData *, link, trans_datas) { + TransDataNla *trans_data = (TransDataNla *)link->data; + + trans_data->trackIndex = trans_data->trackIndex + minimum_track_offset; + NlaTrack *dst_track = BLI_findlink(tracks, trans_data->trackIndex); + + NlaStrip *strip = trans_data->strip; + BKE_nlatrack_remove_strip(trans_data->nlt, strip); + BKE_nlatrack_add_strip(dst_track, strip); + + trans_data->nlt = dst_track; + } + } + + /** Apply horizontal shuffle. */ + const float minimum_time_offset = transdata_get_time_shuffle_offset(trans_datas); + LISTBASE_FOREACH (LinkData *, link, trans_datas) { + TransDataNla *trans_data = (TransDataNla *)link->data; + NlaStrip *strip = trans_data->strip; + + strip->start += minimum_time_offset; + strip->end += minimum_time_offset; + BKE_nlameta_flush_transforms(strip); + } + } + + /** Memory cleanup. */ + LISTBASE_FOREACH (IDGroupedTransData *, group, &grouped_trans_datas) { + BLI_freelistN(&group->trans_datas); + } + BLI_freelistN(&grouped_trans_datas); + } + + /** Clear NLASTRIP_FLAG_FIX_LOCATION flag. */ + TransDataNla *tdn = first_trans_data; + for (int i = 0; i < tc->data_len; i++, tdn++) { + if (tdn->strip == NULL) { + continue; + } + + tdn->strip->flag &= ~NLASTRIP_FLAG_FIX_LOCATION; + } + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT); + + /* get channels to work on */ + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + for (ale = anim_data.first; ale; ale = ale->next) { + NlaTrack *nlt = (NlaTrack *)ale->data; + + /* make sure strips are in order again */ + BKE_nlatrack_sort_strips(nlt); + + /* remove the temp metas */ + BKE_nlastrips_clear_metas(&nlt->strips, 0, 1); + } + + /* free temp memory */ + ANIM_animdata_freelist(&anim_data); + + /** Truncate temporarily added tracks. */ + { + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ANIMDATA); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); for (ale = anim_data.first; ale; ale = ale->next) { - NlaTrack *nlt = (NlaTrack *)ale->data; + ListBase *nla_tracks = &ale->adt->nla_tracks; - /* make sure strips are in order again */ - BKE_nlatrack_sort_strips(nlt); + /** Remove top tracks that weren't necessary. */ + LISTBASE_FOREACH_BACKWARD_MUTABLE (NlaTrack *, track, nla_tracks) { + if (!(track->flag & NLATRACK_TEMPORARILY_ADDED)) { + break; + } + if (track->strips.first != NULL) { + break; + } + BKE_nlatrack_remove_and_free(nla_tracks, track, true); + } - /* remove the temp metas */ - BKE_nlastrips_clear_metas(&nlt->strips, 0, 1); + /** Remove bottom tracks that weren't necessary. */ + LISTBASE_FOREACH_MUTABLE (NlaTrack *, track, nla_tracks) { + /** Library override tracks are the first N tracks. They're never temporary and determine + * where we start removing temporaries.*/ + if ((track->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0) { + continue; + } + if (!(track->flag & NLATRACK_TEMPORARILY_ADDED)) { + break; + } + if (track->strips.first != NULL) { + break; + } + BKE_nlatrack_remove_and_free(nla_tracks, track, true); + } + + /** Clear temporary flag. */ + LISTBASE_FOREACH_MUTABLE (NlaTrack *, track, nla_tracks) { + track->flag &= ~NLATRACK_TEMPORARILY_ADDED; + } } - /* free temp memory */ ANIM_animdata_freelist(&anim_data); - - /* perform after-transfrom validation */ - ED_nla_postop_refresh(&ac); } + + /* perform after-transfrom validation */ + ED_nla_postop_refresh(&ac); } /** \} */ diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index 1eafa655195..6447cfc6d3a 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -821,6 +821,10 @@ typedef enum eNlaStrip_Flag { /* NLASTRIP_FLAG_MIRROR = (1 << 13), */ /* UNUSED */ /* temporary editing flags */ + /** When transforming strips, this flag is set when the strip is placed in an invalid location + * such as overlapping another strip or moved to a locked track. In such cases, the strip's + * location must be fixed. */ + NLASTRIP_FLAG_FIX_LOCATION = (1 << 28), /** NLA strip should ignore frame range and hold settings, and evaluate at global time. */ NLASTRIP_FLAG_NO_TIME_MAP = (1 << 29), /** NLA-Strip is really just a temporary meta used to facilitate easier transform code */ @@ -884,6 +888,9 @@ typedef enum eNlaTrack_Flag { /** track is not allowed to execute, * usually as result of tweaking being enabled (internal flag) */ NLATRACK_DISABLED = (1 << 10), + /** Marks tracks automatically added for space while dragging strips vertically. + * Internal flag that's only set during transform operator. */ + NLATRACK_TEMPORARILY_ADDED = (1 << 11), /** This NLA track is added to an override ID, which means it is fully editable. * Irrelevant in case the owner ID is not an override. */ diff --git a/source/blender/makesrna/intern/rna_animation.c b/source/blender/makesrna/intern/rna_animation.c index 10f86fe2671..9b16c366825 100644 --- a/source/blender/makesrna/intern/rna_animation.c +++ b/source/blender/makesrna/intern/rna_animation.c @@ -587,7 +587,8 @@ static void rna_KeyingSet_paths_clear(KeyingSet *keyingset, ReportList *reports) /* needs wrapper function to push notifier */ static NlaTrack *rna_NlaTrack_new(ID *id, AnimData *adt, Main *bmain, bContext *C, NlaTrack *track) { - NlaTrack *new_track = BKE_nlatrack_add(adt, track, ID_IS_OVERRIDE_LIBRARY(id)); + NlaTrack *new_track = BKE_nlatrack_new_after_and_set_active( + &adt->nla_tracks, track, ID_IS_OVERRIDE_LIBRARY(id)); WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_ADDED, NULL); @@ -607,7 +608,7 @@ static void rna_NlaTrack_remove( return; } - BKE_nlatrack_free(&adt->nla_tracks, track, true); + BKE_nlatrack_remove_and_free(&adt->nla_tracks, track, true); RNA_POINTER_INVALIDATE(track_ptr); WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_REMOVED, NULL); diff --git a/source/blender/makesrna/intern/rna_nla.c b/source/blender/makesrna/intern/rna_nla.c index 2642ba82bc0..042b701865c 100644 --- a/source/blender/makesrna/intern/rna_nla.c +++ b/source/blender/makesrna/intern/rna_nla.c @@ -404,12 +404,12 @@ static NlaStrip *rna_NlaStrip_new(ID *id, strip->end += (start - strip->start); strip->start = start; - if (BKE_nlastrips_add_strip(&track->strips, strip) == 0) { + if (!BKE_nlastrips_try_add_strip(&track->strips, strip, true)) { BKE_report( reports, RPT_ERROR, "Unable to add strip (the track does not have any space to accommodate this new strip)"); - BKE_nlastrip_free(NULL, strip, true); + BKE_nlastrip_free(strip, true); return NULL; } @@ -460,7 +460,7 @@ static void rna_NlaStrip_remove( return; } - BKE_nlastrip_free(&track->strips, strip, true); + BKE_nlastrip_remove_and_free(&track->strips, strip, true); RNA_POINTER_INVALIDATE(strip_ptr); WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_REMOVED, NULL); -- cgit v1.2.3