Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWayde Moss <GuiltyGhost>2021-01-13 08:51:47 +0300
committerWayde Moss <wbmoss_dev@yahoo.com>2021-01-13 08:51:47 +0300
commit8bc2b9468a224410ca2dfc4d2eaffffd517df8e5 (patch)
tree46ff03f865d6fb7198971494312e77a012828ce0
parent6b5e4ad5899d87a183c4a3d5eb129f12d1aecd5c (diff)
NLA: Strip Post-transform Vertical Shuffle and Auto-Grow Track List
**Not ready for review** Differential Revision: https://developer.blender.org/D10103
-rw-r--r--source/blender/blenkernel/BKE_nla.h51
-rw-r--r--source/blender/blenkernel/intern/ipo.c6
-rw-r--r--source/blender/blenkernel/intern/nla.c262
-rw-r--r--source/blender/editors/object/object_add.c4
-rw-r--r--source/blender/editors/space_action/action_data.c4
-rw-r--r--source/blender/editors/space_nla/nla_channels.c8
-rw-r--r--source/blender/editors/space_nla/nla_draw.c9
-rw-r--r--source/blender/editors/space_nla/nla_edit.c43
-rw-r--r--source/blender/editors/transform/transform_convert_nla.c622
-rw-r--r--source/blender/makesdna/DNA_anim_types.h7
-rw-r--r--source/blender/makesrna/intern/rna_animation.c5
-rw-r--r--source/blender/makesrna/intern/rna_nla.c6
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);