Blender V5.0
blenkernel/intern/nla.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009 Blender Authors, Joshua Leung. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cfloat>
10#include <cmath>
11#include <cstddef>
12#include <cstdio>
13#include <cstdlib>
14#include <cstring>
15
16#include "CLG_log.h"
17
18#include "MEM_guardedalloc.h"
19
20#include "BLI_ghash.h"
21#include "BLI_listbase.h"
22#include "BLI_string.h"
23#include "BLI_string_utf8.h"
24#include "BLI_string_utils.hh"
25#include "BLI_utildefines.h"
26
27#include "BLT_translation.hh"
28
29#include "DNA_anim_types.h"
30#include "DNA_scene_types.h"
31#include "DNA_speaker_types.h"
32
33#include "BKE_action.hh"
34#include "BKE_anim_data.hh"
35#include "BKE_fcurve.hh"
36#include "BKE_global.hh"
37#include "BKE_lib_id.hh"
38#include "BKE_lib_query.hh"
39#include "BKE_main.hh"
40#include "BKE_nla.hh"
41#include "BKE_sound.h"
42
43#include "BLO_read_write.hh"
44
45#include "RNA_access.hh"
46#include "RNA_prototypes.hh"
47
48#include "ANIM_nla.hh"
49
50#include "nla_private.h"
51
52static CLG_LogRef LOG = {"anim.nla"};
53
54using namespace blender;
55
61static void nla_tweakmode_find_active(const ListBase /*NlaTrack*/ *nla_tracks,
62 NlaTrack **r_track_of_active_strip,
63 NlaStrip **r_active_strip);
64
65/* *************************************************** */
66/* Data Management */
67
68/* Freeing ------------------------------------------- */
69
70void BKE_nlastrip_free(NlaStrip *strip, const bool do_id_user)
71{
72 NlaStrip *cs, *csn;
73
74 /* sanity checks */
75 if (strip == nullptr) {
76 return;
77 }
78
79 /* free child-strips */
80 for (cs = static_cast<NlaStrip *>(strip->strips.first); cs; cs = csn) {
81 csn = cs->next;
82 BKE_nlastrip_remove_and_free(&strip->strips, cs, do_id_user);
83 }
84
85 /* remove reference to action */
86 if (strip->act != nullptr && do_id_user) {
87 id_us_min(&strip->act->id);
88 }
89
90 /* free own F-Curves */
92
93 /* free own F-Modifiers */
95
96 /* free the strip itself */
97 MEM_freeN(strip);
98}
99
100void BKE_nlatrack_free(NlaTrack *nlt, const bool do_id_user)
101{
102 NlaStrip *strip, *stripn;
103
104 /* sanity checks */
105 if (nlt == nullptr) {
106 return;
107 }
108
109 /* free strips */
110 for (strip = static_cast<NlaStrip *>(nlt->strips.first); strip; strip = stripn) {
111 stripn = strip->next;
112 BKE_nlastrip_remove_and_free(&nlt->strips, strip, do_id_user);
113 }
114
115 /* free NLA track itself now */
116 MEM_freeN(nlt);
117}
118
119void BKE_nla_tracks_free(ListBase *tracks, bool do_id_user)
120{
121 NlaTrack *nlt, *nltn;
122
123 /* sanity checks */
124 if (ELEM(nullptr, tracks, tracks->first)) {
125 return;
126 }
127
128 /* free tracks one by one */
129 for (nlt = static_cast<NlaTrack *>(tracks->first); nlt; nlt = nltn) {
130 nltn = nlt->next;
131 BKE_nlatrack_remove_and_free(tracks, nlt, do_id_user);
132 }
133
134 /* clear the list's pointers to be safe */
136}
137
138/* Copying ------------------------------------------- */
139
141 NlaStrip *strip,
142 const bool use_same_action,
143 const int flag)
144{
145 NlaStrip *strip_d;
146 NlaStrip *cs_d;
147
148 const bool do_id_user = (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0;
149
150 /* sanity check */
151 if (strip == nullptr) {
152 return nullptr;
153 }
154
155 /* make a copy */
156 strip_d = static_cast<NlaStrip *>(MEM_dupallocN(strip));
157 strip_d->next = strip_d->prev = nullptr;
158
159 /* handle action */
160 if (strip_d->act) {
161 if (use_same_action) {
162 if (do_id_user) {
163 /* increase user-count of action */
164 id_us_plus(&strip_d->act->id);
165 }
166 }
167 else {
168 /* use a copy of the action instead (user count shouldn't have changed yet) */
169 BKE_id_copy_ex(bmain, &strip_d->act->id, (ID **)&strip_d->act, flag);
170 }
171 }
172
173 /* copy F-Curves and modifiers */
174 BKE_fcurves_copy(&strip_d->fcurves, &strip->fcurves);
175 copy_fmodifiers(&strip_d->modifiers, &strip->modifiers);
176
177 /* make a copy of all the child-strips, one at a time */
178 BLI_listbase_clear(&strip_d->strips);
179
180 LISTBASE_FOREACH (NlaStrip *, cs, &strip->strips) {
181 cs_d = BKE_nlastrip_copy(bmain, cs, use_same_action, flag);
182 BLI_addtail(&strip_d->strips, cs_d);
183 }
184
185 /* return the strip */
186 return strip_d;
187}
188
190 NlaTrack *nlt,
191 const bool use_same_actions,
192 const int flag)
193{
194 NlaStrip *strip_d;
195 NlaTrack *nlt_d;
196
197 /* sanity check */
198 if (nlt == nullptr) {
199 return nullptr;
200 }
201
202 /* make a copy */
203 nlt_d = static_cast<NlaTrack *>(MEM_dupallocN(nlt));
204 nlt_d->next = nlt_d->prev = nullptr;
205
206 /* make a copy of all the strips, one at a time */
207 BLI_listbase_clear(&nlt_d->strips);
208
209 LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
210 strip_d = BKE_nlastrip_copy(bmain, strip, use_same_actions, flag);
211 BLI_addtail(&nlt_d->strips, strip_d);
212 }
213
214 /* return the copy */
215 return nlt_d;
216}
217
218void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, const ListBase *src, const int flag)
219{
220 NlaTrack *nlt_d;
221
222 /* sanity checks */
223 if (ELEM(nullptr, dst, src)) {
224 return;
225 }
226
227 /* clear out the destination list first for precautions... */
229
230 /* copy each NLA-track, one at a time */
231 LISTBASE_FOREACH (NlaTrack *, nlt, src) {
232 /* make a copy, and add the copy to the destination list */
233 /* XXX: we need to fix this sometime. */
234 nlt_d = BKE_nlatrack_copy(bmain, nlt, true, flag);
235 BLI_addtail(dst, nlt_d);
236 }
237}
238
244 const ListBase /*NlaStrip*/ *strips_source,
245 const ListBase /*NlaStrip*/ *strips_dest)
246{
247 BLI_assert_msg(BLI_listbase_count(strips_source) == BLI_listbase_count(strips_dest),
248 "Expecting the same number of source and destination strips");
249
250 NlaStrip *strip_dest = static_cast<NlaStrip *>(strips_dest->first);
251 LISTBASE_FOREACH (const NlaStrip *, strip_source, strips_source) {
252 if (strip_dest == nullptr) {
253 /* The tracks are assumed to have an equal number of strips, but this is
254 * not the case. Not sure when this might happen, but it's better to not
255 * crash. */
256 break;
257 }
258 if (strip_source == active_strip) {
259 return strip_dest;
260 }
261
262 const bool src_is_meta = strip_source->type == NLASTRIP_TYPE_META;
263 const bool dst_is_meta = strip_dest->type == NLASTRIP_TYPE_META;
264 BLI_assert_msg(src_is_meta == dst_is_meta,
265 "Expecting topology of source and destination strips to be equal");
266 if (src_is_meta && dst_is_meta) {
268 active_strip, &strip_source->strips, &strip_dest->strips);
269 if (found_in_meta != nullptr) {
270 return found_in_meta;
271 }
272 }
273
274 strip_dest = strip_dest->next;
275 }
276
277 return nullptr;
278}
279
280/* Set adt_dest->actstrip to the strip with the same index as
281 * adt_source->actstrip. Note that this always sets `adt_dest->actstrip`; sets
282 * to nullptr when `adt_source->actstrip` cannot be found. */
283static void update_active_strip(AnimData *adt_dest,
284 NlaTrack *track_dest,
285 const AnimData *adt_source,
286 const NlaTrack *track_source)
287{
288 BLI_assert(BLI_listbase_count(&track_source->strips) == BLI_listbase_count(&track_dest->strips));
289
291 adt_source->actstrip, &track_source->strips, &track_dest->strips);
292 adt_dest->actstrip = active_strip;
293}
294
295/* Set adt_dest->act_track to the track with the same index as adt_source->act_track. */
296static void update_active_track(AnimData *adt_dest, const AnimData *adt_source)
297{
298 adt_dest->act_track = nullptr;
299 adt_dest->actstrip = nullptr;
300 if (adt_source->act_track == nullptr && adt_source->actstrip == nullptr) {
301 return;
302 }
303
305 BLI_listbase_count(&adt_dest->nla_tracks));
306
307 NlaTrack *track_dest = static_cast<NlaTrack *>(adt_dest->nla_tracks.first);
308 LISTBASE_FOREACH (NlaTrack *, track_source, &adt_source->nla_tracks) {
309 if (track_source == adt_source->act_track) {
310 adt_dest->act_track = track_dest;
311 }
312
313 /* Only search for the active strip if it hasn't been found yet. */
314 if (adt_dest->actstrip == nullptr && adt_source->actstrip != nullptr) {
315 update_active_strip(adt_dest, track_dest, adt_source, track_source);
316 }
317
318 track_dest = track_dest->next;
319 }
320
321#ifndef NDEBUG
322 {
323 const bool source_has_actstrip = adt_source->actstrip != nullptr;
324 const bool dest_has_actstrip = adt_dest->actstrip != nullptr;
325 BLI_assert_msg(source_has_actstrip == dest_has_actstrip,
326 "Active strip did not copy correctly");
327 }
328#endif
329}
330
332 AnimData *adt_dest,
333 const AnimData *adt_source,
334 const int flag)
335{
336 adt_dest->act_track = nullptr;
337 adt_dest->actstrip = nullptr;
338
339 BKE_nla_tracks_copy(bmain, &adt_dest->nla_tracks, &adt_source->nla_tracks, flag);
340 update_active_track(adt_dest, adt_source);
341}
342
343/* Adding ------------------------------------------- */
344
346{
347 /* allocate new track */
348 NlaTrack *nlt = MEM_callocN<NlaTrack>("NlaTrack");
349
350 /* set settings requiring the track to not be part of the stack yet */
352
353 return nlt;
354}
355
357 NlaTrack *next,
358 NlaTrack *new_track,
359 bool is_liboverride)
360{
361
362 if (is_liboverride) {
363 /* Currently, all library override tracks are assumed to be grouped together at the start of
364 * the list. Non overridden must be placed after last library track. */
365 if (next != nullptr && (next->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0) {
366 BKE_nlatrack_insert_after(nla_tracks, next, new_track, is_liboverride);
367 return;
368 }
369 }
370
371 BLI_insertlinkbefore(nla_tracks, next, new_track);
372 new_track->index = BLI_findindex(nla_tracks, new_track);
373
374 /* Must have unique name, but we need to seed this. */
375 STRNCPY_UTF8(new_track->name, "NlaTrack");
376
377 BLI_uniquename(nla_tracks,
378 new_track,
379 DATA_("NlaTrack"),
380 '.',
382 sizeof(new_track->name));
383}
384
386 NlaTrack *prev,
387 NlaTrack *new_track,
388 const bool is_liboverride)
389{
390 BLI_assert(nla_tracks);
391 BLI_assert(new_track);
392
393 /* If nullptr, then caller intends to insert a new head. But, tracks are not allowed to be
394 * placed before library overrides. So it must inserted after the last override. */
395 if (prev == nullptr) {
396 NlaTrack *first_track = (NlaTrack *)nla_tracks->first;
397 if (first_track != nullptr && (first_track->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0) {
398 prev = first_track;
399 }
400 }
401
402 /* In liboverride case, we only add local tracks after all those coming from the linked data,
403 * so we need to find the first local track. */
404 if (is_liboverride && prev != nullptr && (prev->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0) {
405 NlaTrack *first_local = prev->next;
406 for (; first_local != nullptr && (first_local->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0;
407 first_local = first_local->next)
408 {
409 }
410 prev = first_local != nullptr ? first_local->prev : prev;
411 }
412
413 /* Add track to stack, and make it the active one. */
414 BLI_insertlinkafter(nla_tracks, prev, new_track);
415 new_track->index = BLI_findindex(nla_tracks, new_track);
416
417 /* must have unique name, but we need to seed this */
418 BLI_uniquename(nla_tracks,
419 new_track,
420 DATA_("NlaTrack"),
421 '.',
423 sizeof(new_track->name));
424}
425
426NlaTrack *BKE_nlatrack_new_before(ListBase *nla_tracks, NlaTrack *next, bool is_liboverride)
427{
428 NlaTrack *new_track = BKE_nlatrack_new();
429
430 BKE_nlatrack_insert_before(nla_tracks, next, new_track, is_liboverride);
431
432 return new_track;
433}
434
435NlaTrack *BKE_nlatrack_new_after(ListBase *nla_tracks, NlaTrack *prev, bool is_liboverride)
436{
437 NlaTrack *new_track = BKE_nlatrack_new();
438
439 BKE_nlatrack_insert_after(nla_tracks, prev, new_track, is_liboverride);
440
441 return new_track;
442}
443
444NlaTrack *BKE_nlatrack_new_head(ListBase *nla_tracks, bool is_liboverride)
445{
446 return BKE_nlatrack_new_before(nla_tracks, (NlaTrack *)nla_tracks->first, is_liboverride);
447}
448
449NlaTrack *BKE_nlatrack_new_tail(ListBase *nla_tracks, const bool is_liboverride)
450{
451 return BKE_nlatrack_new_after(nla_tracks, (NlaTrack *)nla_tracks->last, is_liboverride);
452}
453
455{
456 if (strip->actend <= strip->actstart) {
457 return 1.0f;
458 }
459 return strip->actend - strip->actstart;
460}
461
462void BKE_nla_clip_length_ensure_nonzero(const float *actstart, float *r_actend)
463{
464 if (*r_actend <= *actstart) {
465 *r_actend = *actstart + 1.0f;
466 }
467}
468
475static NlaStrip *nlastrip_new(bAction *act, ID &animated_id)
476{
477 using namespace blender::animrig;
478
479 NlaStrip *strip;
480
481 /* sanity checks */
482 if (act == nullptr) {
483 return nullptr;
484 }
485
486 /* allocate new strip */
487 strip = MEM_callocN<NlaStrip>("NlaStrip");
488
489 /* generic settings
490 * - selected flag to highlight this to the user
491 * - (XXX) disabled Auto-Blends, as this was often causing some unwanted effects
492 */
494
495 /* Disable sync for actions with a manual frame range, since it only syncs to range anyway. */
496 if (act->flag & ACT_FRAME_RANGE) {
498 }
499
500 /* Enable cyclic time for known cyclic actions. */
501 Action &action = act->wrap();
502 if (action.is_cyclic()) {
504 }
505
506 /* Assign the Action, and automatically choose a suitable slot. The caller can change the slot to
507 * something more specific later, if necessary. */
508 nla::assign_action(*strip, action, animated_id);
509
510 /* strip should be referenced as-is */
511 strip->scale = 1.0f;
512 strip->repeat = 1.0f;
513
514 return strip;
515}
516
522{
523 const float2 frame_range = strip->act->wrap().get_frame_range_of_slot(strip->action_slot_handle);
524 strip->actstart = frame_range[0];
525 strip->actend = frame_range[1];
527 strip->start = strip->actstart;
528 strip->end = strip->actend;
529}
530
532{
533 NlaStrip *strip = nlastrip_new(act, animated_id);
534 if (!strip) {
535 return nullptr;
536 }
538 return strip;
539}
540
543 ID &animated_id)
544{
545 using namespace blender::animrig;
546
547 NlaStrip *strip = BKE_nlastrip_new(act, animated_id);
548 if (!strip) {
549 return nullptr;
550 }
551
553 *strip, slot_handle, animated_id);
554
555 switch (result) {
557 break;
561 [[fallthrough]];
563 BKE_nlastrip_free(strip, true);
564 return nullptr;
565 }
566
568 return strip;
569}
570
571NlaStrip *BKE_nlastack_add_strip(const OwnedAnimData owned_adt, const bool is_liboverride)
572{
573 NlaStrip *strip;
574 NlaTrack *nlt;
575 AnimData *adt = &owned_adt.adt;
576
577 /* sanity checks */
578 if (ELEM(nullptr, adt, adt->action)) {
579 return nullptr;
580 }
581
582 /* create a new NLA strip */
583 strip = BKE_nlastrip_new_for_slot(adt->action, adt->slot_handle, owned_adt.owner_id);
584 if (strip == nullptr) {
585 return nullptr;
586 }
587
588 /* firstly try adding strip to last track, but if that fails, add to a new track */
590 static_cast<NlaTrack *>(adt->nla_tracks.last), strip, is_liboverride) == 0)
591 {
592 /* trying to add to the last track failed (no track or no space),
593 * so add a new track to the stack, and add to that...
594 */
595 nlt = BKE_nlatrack_new_tail(&adt->nla_tracks, is_liboverride);
597 BKE_nlatrack_add_strip(nlt, strip, is_liboverride);
598 STRNCPY_UTF8(nlt->name, adt->action->id.name + 2);
599 }
600
601 /* automatically name it too */
602 BKE_nlastrip_validate_name(adt, strip);
603
604 /* returns the strip added */
605 return strip;
606}
607
609{
610 NlaStrip *strip = MEM_callocN<NlaStrip>("NlaSoundStrip");
611
612/* if speaker has a sound, set the strip length to the length of the sound,
613 * otherwise default to length of 10 frames
614 */
615#ifdef WITH_AUDASPACE
616 if (speaker->sound) {
617 SoundInfo info;
618 if (BKE_sound_info_get(bmain, speaker->sound, &info)) {
619 strip->end = float(ceil(double(info.length) * scene->frames_per_second()));
620 }
621 }
622 else
623#endif
624 {
625 strip->end = 10.0f;
626 /* quiet compiler warnings */
627 UNUSED_VARS(bmain, scene, speaker);
628 }
629
630 /* general settings */
631 strip->type = NLASTRIP_TYPE_SOUND;
632
633 strip->flag = NLASTRIP_FLAG_SELECT;
634 strip->extendmode = NLASTRIP_EXTEND_NOTHING; /* nothing to extend... */
635
636 /* strip should be referenced as-is */
637 strip->scale = 1.0f;
638 strip->repeat = 1.0f;
639
640 /* return this strip */
641 return strip;
642}
643
656
657/* Removing ------------------------------------------ */
658
664
666{
668 BKE_nlatrack_free(nlt, do_id_user);
669}
670
671bool BKE_nlatrack_is_enabled(const AnimData &adt, const NlaTrack &nlt)
672{
673 if (adt.flag & ADT_NLA_SOLO_TRACK) {
674 return (nlt.flag & NLATRACK_SOLO);
675 }
676
677 return !(nlt.flag & NLATRACK_MUTED);
678}
679
680/* *************************************************** */
681/* NLA Evaluation <-> Editing Stuff */
682
683/* Strip Mapping ------------------------------------- */
684
685/* non clipped mapping for strip-time <-> global time (for Action-Clips)
686 * invert = convert action-strip time to global time
687 */
688static float nlastrip_get_frame_actionclip(NlaStrip *strip, float cframe, short mode)
689{
690 float scale;
691 // float repeat; // UNUSED
692
693 /* get number of repeats */
694 if (IS_EQF(strip->repeat, 0.0f)) {
695 strip->repeat = 1.0f;
696 }
697 // repeat = strip->repeat; /* UNUSED */
698
699 /* scaling */
700 if (IS_EQF(strip->scale, 0.0f)) {
701 strip->scale = 1.0f;
702 }
703
704 /* Scale must be positive - we've got a special flag for reversing. */
705 scale = fabsf(strip->scale);
706
707 /* length of referenced action */
708 const float actlength = BKE_nla_clip_length_get_nonzero(strip);
709
710 /* reversed = play strip backwards */
711 if (strip->flag & NLASTRIP_FLAG_REVERSE) {
712 /* FIXME: this won't work right with Graph Editor? */
713 if (mode == NLATIME_CONVERT_MAP) {
714 return strip->end - scale * (cframe - strip->actstart);
715 }
716 if (mode == NLATIME_CONVERT_UNMAP) {
717 return (strip->end + (strip->actstart * scale - cframe)) / scale;
718 }
719 /* if (mode == NLATIME_CONVERT_EVAL) */
720 if (IS_EQF(float(cframe), strip->end) && IS_EQF(strip->repeat, floorf(strip->repeat))) {
721 /* This case prevents the motion snapping back to the first frame at the end of the strip
722 * by catching the case where repeats is a whole number, which means that the end of the
723 * strip could also be interpreted as the end of the start of a repeat. */
724 return strip->actstart;
725 }
726
727 /* - The `fmod(..., actlength * scale)` is needed to get the repeats working.
728 * - The `/ scale` is needed to ensure that scaling influences the timing within the repeat.
729 */
730 return strip->actend - fmodf(cframe - strip->start, actlength * scale) / scale;
731 }
732
733 if (mode == NLATIME_CONVERT_MAP) {
734 return strip->start + scale * (cframe - strip->actstart);
735 }
736 if (mode == NLATIME_CONVERT_UNMAP) {
737 return strip->actstart + (cframe - strip->start) / scale;
738 }
739 /* if (mode == NLATIME_CONVERT_EVAL) */
740 if (IS_EQF(cframe, strip->end) && IS_EQF(strip->repeat, floorf(strip->repeat))) {
741 /* This case prevents the motion snapping back to the first frame at the end of the strip
742 * by catching the case where repeats is a whole number, which means that the end of the
743 * strip could also be interpreted as the end of the start of a repeat. */
744 return strip->actend;
745 }
746
747 /* - The `fmod(..., actlength * scale)` is needed to get the repeats working.
748 * - The `/ scale` is needed to ensure that scaling influences the timing within the repeat.
749 */
750 return strip->actstart + fmodf(cframe - strip->start, actlength * scale) / scale;
751}
752
753/* non clipped mapping for strip-time <-> global time (for Transitions)
754 * invert = convert action-strip time to global time
755 */
756static float nlastrip_get_frame_transition(NlaStrip *strip, float cframe, short mode)
757{
758 float length;
759
760 /* length of strip */
761 length = strip->end - strip->start;
762
763 /* reversed = play strip backwards */
764 if (strip->flag & NLASTRIP_FLAG_REVERSE) {
765 if (mode == NLATIME_CONVERT_MAP) {
766 return strip->end - (length * cframe);
767 }
768
769 return (strip->end - cframe) / length;
770 }
771
772 if (mode == NLATIME_CONVERT_MAP) {
773 return (length * cframe) + strip->start;
774 }
775
776 return (cframe - strip->start) / length;
777}
778
779float nlastrip_get_frame(NlaStrip *strip, float cframe, short mode)
780{
781 switch (strip->type) {
782 case NLASTRIP_TYPE_META: /* Meta - for now, does the same as transition
783 * (is really just an empty container). */
784 case NLASTRIP_TYPE_TRANSITION: /* transition */
785 return nlastrip_get_frame_transition(strip, cframe, mode);
786
787 case NLASTRIP_TYPE_CLIP: /* action-clip (default) */
788 default:
789 return nlastrip_get_frame_actionclip(strip, cframe, mode);
790 }
791}
792
793float BKE_nla_tweakedit_remap(AnimData *adt, const float cframe, const eNlaTime_ConvertModes mode)
794{
795 NlaStrip *strip;
796
797 /* Sanity checks:
798 * - Obviously we've got to have some starting data.
799 * - When not in tweak-mode, the active Action does not have any scaling applied :)
800 * - When in tweak-mode, if the no-mapping flag is set, do not map.
801 */
802 if ((adt == nullptr) || (adt->flag & ADT_NLA_EDIT_ON) == 0 || (adt->flag & ADT_NLA_EDIT_NOMAP)) {
803 return cframe;
804 }
805
806 /* if the active-strip info has been stored already, access this, otherwise look this up
807 * and store for (very probable) future usage
808 */
809 if (adt->act_track == nullptr) {
810 if (adt->actstrip) {
812 }
813 else {
815 }
816 }
817 if (adt->actstrip == nullptr) {
819 }
820 strip = adt->actstrip;
821
822 /* Sanity checks:
823 * - In rare cases, we may not be able to find this strip for some reason (internal error)
824 * - For now, if the user has defined a curve to control the time, this correction cannot be
825 * performed reliably.
826 */
827 if ((strip == nullptr) || (strip->flag & NLASTRIP_FLAG_USR_TIME)) {
828 return cframe;
829 }
830
831 /* perform the correction now... */
832 return nlastrip_get_frame(strip, cframe, mode);
833}
834
835/* *************************************************** */
836/* NLA API */
837
838/* List of Strips ------------------------------------ */
839/* (these functions are used for NLA-Tracks and also for nested/meta-strips) */
840
841bool BKE_nlastrips_has_space(ListBase *strips, float start, float end)
842{
843 /* sanity checks */
844 if ((strips == nullptr) || IS_EQF(start, end)) {
845 return false;
846 }
847 if (start > end) {
848 puts("BKE_nlastrips_has_space() error... start and end arguments swapped");
849 std::swap(start, end);
850 }
851
852 /* loop over NLA strips checking for any overlaps with this area... */
853 LISTBASE_FOREACH (NlaStrip *, strip, strips) {
854 /* if start frame of strip is past the target end-frame, that means that
855 * we've gone past the window we need to check for, so things are fine
856 */
857 if (strip->start >= end) {
858 return true;
859 }
860
861 /* if the end of the strip is greater than either of the boundaries, the range
862 * must fall within the extents of the strip
863 */
864 if ((strip->end > start) || (strip->end > end)) {
865 return false;
866 }
867 }
868
869 /* if we are still here, we haven't encountered any overlapping strips */
870 return true;
871}
872
874{
875 ListBase tmp = {nullptr, nullptr};
876 NlaStrip *strip, *stripn;
877
878 /* sanity checks */
879 if (ELEM(nullptr, strips, strips->first)) {
880 return;
881 }
882
883 /* we simply perform insertion sort on this list, since it is assumed that per track,
884 * there are only likely to be at most 5-10 strips
885 */
886 for (strip = static_cast<NlaStrip *>(strips->first); strip; strip = stripn) {
887 short not_added = 1;
888
889 stripn = strip->next;
890
891 /* remove this strip from the list, and add it to the new list, searching from the end of
892 * the list, assuming that the lists are in order
893 */
894 BLI_remlink(strips, strip);
895
896 LISTBASE_FOREACH_BACKWARD (NlaStrip *, sstrip, &tmp) {
897 /* check if add after */
898 if (sstrip->start <= strip->start) {
899 BLI_insertlinkafter(&tmp, sstrip, strip);
900 not_added = 0;
901 break;
902 }
903 }
904
905 /* add before first? */
906 if (not_added) {
907 BLI_addhead(&tmp, strip);
908 }
909 }
910
911 /* reassign the start and end points of the strips */
912 strips->first = tmp.first;
913 strips->last = tmp.last;
914}
915
917{
918 bool not_added = true;
919
920 /* sanity checks */
921 BLI_assert(!ELEM(nullptr, strips, strip));
922
923 /* find the right place to add the strip to the nominated track */
924 LISTBASE_FOREACH (NlaStrip *, ns, strips) {
925 /* if current strip occurs after the new strip, add it before */
926 if (ns->start >= strip->start) {
927 BLI_insertlinkbefore(strips, ns, strip);
928 not_added = false;
929 break;
930 }
931 }
932 if (not_added) {
933 /* just add to the end of the list of the strips then... */
934 BLI_addtail(strips, strip);
935 }
936}
937
939{
940 if (ELEM(nullptr, strips, strip)) {
941 return false;
942 }
943
944 if (!BKE_nlastrips_has_space(strips, strip->start, strip->end)) {
945 return false;
946 }
947
948 BKE_nlastrips_add_strip_unsafe(strips, strip);
949 return true;
950}
951
952/* Meta-Strips ------------------------------------ */
953
954void BKE_nlastrips_make_metas(ListBase *strips, bool is_temp)
955{
956 NlaStrip *mstrip = nullptr;
957 NlaStrip *strip, *stripn;
958
959 /* sanity checks */
960 if (ELEM(nullptr, strips, strips->first)) {
961 return;
962 }
963
964 /* group all continuous chains of selected strips into meta-strips */
965 for (strip = static_cast<NlaStrip *>(strips->first); strip; strip = stripn) {
966 stripn = strip->next;
967
968 if (strip->flag & NLASTRIP_FLAG_SELECT) {
969 /* if there is an existing meta-strip, add this strip to it, otherwise, create a new one */
970 if (mstrip == nullptr) {
971 /* add a new meta-strip, and add it before the current strip that it will replace... */
972 mstrip = MEM_callocN<NlaStrip>("Meta-NlaStrip");
973 mstrip->type = NLASTRIP_TYPE_META;
974 BLI_insertlinkbefore(strips, strip, mstrip);
975
976 /* set flags */
977 mstrip->flag = NLASTRIP_FLAG_SELECT;
978
979 /* set temp flag if appropriate (i.e. for transform-type editing) */
980 if (is_temp) {
981 mstrip->flag |= NLASTRIP_FLAG_TEMP_META;
982 }
983
984 /* set default repeat/scale values to prevent warnings */
985 mstrip->repeat = mstrip->scale = 1.0f;
986
987 /* make its start frame be set to the start frame of the current strip */
988 mstrip->start = strip->start;
989 }
990
991 /* remove the selected strips from the track, and add to the meta */
992 BLI_remlink(strips, strip);
993 BLI_addtail(&mstrip->strips, strip);
994
995 /* expand the meta's dimensions to include the newly added strip- i.e. its last frame */
996 mstrip->end = strip->end;
997 }
998 else {
999 /* current strip wasn't selected, so the end of 'island' of selected strips has been
1000 * reached, so stop adding strips to the current meta.
1001 */
1002 mstrip = nullptr;
1003 }
1004 }
1005}
1006
1008{
1009 NlaStrip *cs, *csn;
1010
1011 /* sanity check */
1012 if (ELEM(nullptr, strips, strip)) {
1013 return;
1014 }
1015
1016 /* move each one of the meta-strip's children before the meta-strip
1017 * in the list of strips after unlinking them from the meta-strip
1018 */
1019 for (cs = static_cast<NlaStrip *>(strip->strips.first); cs; cs = csn) {
1020 csn = cs->next;
1021 BLI_remlink(&strip->strips, cs);
1022 BLI_insertlinkbefore(strips, strip, cs);
1023 }
1024
1025 /* free the meta-strip now */
1026 BKE_nlastrip_remove_and_free(strips, strip, true);
1027}
1028
1029void BKE_nlastrips_clear_metas(ListBase *strips, bool only_sel, bool only_temp)
1030{
1031 NlaStrip *strip, *stripn;
1032
1033 /* sanity checks */
1034 if (ELEM(nullptr, strips, strips->first)) {
1035 return;
1036 }
1037
1038 /* remove meta-strips fitting the criteria of the arguments */
1039 for (strip = static_cast<NlaStrip *>(strips->first); strip; strip = stripn) {
1040 stripn = strip->next;
1041
1042 /* check if strip is a meta-strip */
1043 if (strip->type == NLASTRIP_TYPE_META) {
1044 /* if check if selection and 'temporary-only' considerations are met */
1045 if ((!only_sel) || (strip->flag & NLASTRIP_FLAG_SELECT)) {
1046 if ((!only_temp) || (strip->flag & NLASTRIP_FLAG_TEMP_META)) {
1047 BKE_nlastrips_clear_metastrip(strips, strip);
1048 }
1049 }
1050 }
1051 }
1052}
1053
1055{
1056 /* sanity checks */
1057 if (ELEM(nullptr, mstrip, strip)) {
1058 return false;
1059 }
1060
1061 /* firstly, check if the meta-strip has space for this */
1062 if (BKE_nlastrips_has_space(&mstrip->strips, strip->start, strip->end) == 0) {
1063 return false;
1064 }
1065
1066 /* check if this would need to be added to the ends of the meta,
1067 * and subsequently, if the neighboring strips allow us enough room
1068 */
1069 if (strip->start < mstrip->start) {
1070 /* check if strip to the left (if it exists) ends before the
1071 * start of the strip we're trying to add
1072 */
1073 if ((mstrip->prev == nullptr) || (mstrip->prev->end <= strip->start)) {
1074 /* add strip to start of meta's list, and expand dimensions */
1075 BLI_addhead(&mstrip->strips, strip);
1076 mstrip->start = strip->start;
1077
1078 return true;
1079 }
1080 /* failed... no room before */
1081 return false;
1082 }
1083 if (strip->end > mstrip->end) {
1084 /* check if strip to the right (if it exists) starts before the
1085 * end of the strip we're trying to add
1086 */
1087 if ((mstrip->next == nullptr) || (mstrip->next->start >= strip->end)) {
1088 /* add strip to end of meta's list, and expand dimensions */
1089 BLI_addtail(&mstrip->strips, strip);
1090 mstrip->end = strip->end;
1091
1092 return true;
1093 }
1094 /* failed... no room after */
1095 return false;
1096 }
1097
1098 /* just try to add to the meta-strip (no dimension changes needed) */
1099 return BKE_nlastrips_add_strip(&mstrip->strips, strip);
1100}
1101
1103{
1104 float oStart, oEnd, offset;
1105 float oLen, nLen;
1106 short scaleChanged = 0;
1107
1108 /* sanity checks
1109 * - strip must exist
1110 * - strip must be a meta-strip with some contents
1111 */
1112 if (ELEM(nullptr, mstrip, mstrip->strips.first)) {
1113 return;
1114 }
1115 if (mstrip->type != NLASTRIP_TYPE_META) {
1116 return;
1117 }
1118
1119 /* get the original start/end points, and calculate the start-frame offset
1120 * - these are simply the start/end frames of the child strips,
1121 * since we assume they weren't transformed yet
1122 */
1123 oStart = ((NlaStrip *)mstrip->strips.first)->start;
1124 oEnd = ((NlaStrip *)mstrip->strips.last)->end;
1125 offset = mstrip->start - oStart;
1126
1127 /* check if scale changed */
1128 oLen = oEnd - oStart;
1129 nLen = mstrip->end - mstrip->start;
1130 scaleChanged = !IS_EQF(oLen, nLen);
1131
1132 /* optimization:
1133 * don't flush if nothing changed yet
1134 * TODO: maybe we need a flag to say always flush?
1135 */
1136 if (IS_EQF(oStart, mstrip->start) && IS_EQF(oEnd, mstrip->end) && !scaleChanged) {
1137 return;
1138 }
1139
1140 /* for each child-strip, calculate new start/end points based on this new info */
1141 LISTBASE_FOREACH (NlaStrip *, strip, &mstrip->strips) {
1142 if (scaleChanged) {
1143 float p1, p2;
1144
1145 if (oLen) {
1146 /* Compute positions of endpoints relative to old extents of strip. */
1147 p1 = (strip->start - oStart) / oLen;
1148 p2 = (strip->end - oStart) / oLen;
1149 }
1150 else {
1151 /* WORKAROUND: in theory, a strip should never be zero length. However,
1152 * zero-length strips are nevertheless showing up here (see issue #113552).
1153 * This is a stop-gap fix to handle that and prevent a divide by zero. A
1154 * proper fix will need to track down and fix the source(s) of these
1155 * zero-length strips. */
1156 p1 = 0.0f;
1157 p2 = 1.0f;
1158 }
1159
1160 /* Apply new strip endpoints using the proportions,
1161 * then wait for second pass to flush scale properly. */
1162 strip->start = (p1 * nLen) + mstrip->start;
1163 strip->end = (p2 * nLen) + mstrip->start;
1164
1165 /* Recompute the playback scale, given the new start & end frame of the strip. */
1166 const double action_len = strip->actend - strip->actstart;
1167 const double repeated_len = action_len * strip->repeat;
1168 const double strip_len = strip->end - strip->start;
1169 strip->scale = strip_len / repeated_len;
1170 }
1171 else {
1172 /* just apply the changes in offset to both ends of the strip */
1173 strip->start += offset;
1174 strip->end += offset;
1175 }
1176 }
1177
1178 /* apply a second pass over child strips, to finish up unfinished business */
1179 LISTBASE_FOREACH (NlaStrip *, strip, &mstrip->strips) {
1180 /* only if scale changed, need to perform RNA updates */
1181 if (scaleChanged) {
1182 /* use RNA updates to compute scale properly */
1183 PointerRNA ptr = RNA_pointer_create_discrete(nullptr, &RNA_NlaStrip, strip);
1184
1185 RNA_float_set(&ptr, "frame_start", strip->start);
1186 RNA_float_set(&ptr, "frame_end", strip->end);
1187 }
1188
1189 /* finally, make sure the strip's children (if it is a meta-itself), get updated */
1191 }
1192}
1193
1194/* NLA-Tracks ---------------------------------------- */
1195
1197{
1198 /* sanity check */
1199 if (ELEM(nullptr, tracks, tracks->first)) {
1200 return nullptr;
1201 }
1202
1203 /* try to find the first active track */
1204 LISTBASE_FOREACH (NlaTrack *, nlt, tracks) {
1205 if (nlt->flag & NLATRACK_ACTIVE) {
1206 return nlt;
1207 }
1208 }
1209
1210 /* none found */
1211 return nullptr;
1212}
1213
1215{
1216 /* sanity check */
1217 if (adt == nullptr) {
1218 return nullptr;
1219 }
1220
1221 /* Since the track itself gets disabled, we want the first disabled... */
1222 LISTBASE_FOREACH (NlaTrack *, nlt, &adt->nla_tracks) {
1223 if (nlt->flag & (NLATRACK_ACTIVE | NLATRACK_DISABLED)) {
1224 /* For good measure, make sure that strip actually exists there */
1225 if (BLI_findindex(&nlt->strips, adt->actstrip) != -1) {
1226 return nlt;
1227 }
1228 if (G.debug & G_DEBUG) {
1229 printf("%s: Active strip (%p, %s) not in NLA track found (%p, %s)\n",
1230 __func__,
1231 adt->actstrip,
1232 (adt->actstrip) ? adt->actstrip->name : "<None>",
1233 nlt,
1234 nlt->name);
1235 }
1236 }
1237 }
1238
1239 /* Not found! */
1240 return nullptr;
1241}
1242
1244{
1245 /* sanity check */
1246 if (ELEM(nullptr, adt, adt->nla_tracks.first)) {
1247 return;
1248 }
1249
1250 /* firstly, make sure 'solo' flag for all tracks is disabled */
1251 LISTBASE_FOREACH (NlaTrack *, nt, &adt->nla_tracks) {
1252 if (nt != nlt) {
1253 nt->flag &= ~NLATRACK_SOLO;
1254 }
1255 }
1256
1257 /* now, enable 'solo' for the given track if appropriate */
1258 if (nlt) {
1259 /* toggle solo status */
1260 nlt->flag ^= NLATRACK_SOLO;
1261
1262 /* set or clear solo-status on AnimData */
1263 if (nlt->flag & NLATRACK_SOLO) {
1264 adt->flag |= ADT_NLA_SOLO_TRACK;
1265 }
1266 else {
1267 adt->flag &= ~ADT_NLA_SOLO_TRACK;
1268 }
1269 }
1270 else {
1271 adt->flag &= ~ADT_NLA_SOLO_TRACK;
1272 }
1273}
1274
1276{
1277 /* sanity check */
1278 if (ELEM(nullptr, tracks, tracks->first)) {
1279 return;
1280 }
1281
1282 /* deactivate all the rest */
1283 LISTBASE_FOREACH (NlaTrack *, nlt, tracks) {
1284 nlt->flag &= ~NLATRACK_ACTIVE;
1285 }
1286
1287 /* set the given one as the active one */
1288 if (nlt_a) {
1289 nlt_a->flag |= NLATRACK_ACTIVE;
1290 }
1291}
1292
1293bool BKE_nlatrack_has_space(NlaTrack *nlt, float start, float end)
1294{
1295 /* sanity checks
1296 * - track must exist
1297 * - track must be editable
1298 * - bounds cannot be equal (0-length is nasty)
1299 */
1300 if ((nlt == nullptr) || (nlt->flag & NLATRACK_PROTECTED) || IS_EQF(start, end)) {
1301 return false;
1302 }
1303
1304 if (start > end) {
1305 puts("BKE_nlatrack_has_space() error... start and end arguments swapped");
1306 std::swap(start, end);
1307 }
1308
1309 /* check if there's any space left in the track for a strip of the given length */
1310 return BKE_nlastrips_has_space(&nlt->strips, start, end);
1311}
1312
1314{
1315 /* sanity checks */
1317 return false;
1318 }
1319
1320 /* Check each track for NLA strips. */
1321 LISTBASE_FOREACH (NlaTrack *, track, tracks) {
1322 if (BLI_listbase_count(&track->strips) > 0) {
1323 return true;
1324 }
1325 }
1326
1327 /* none found */
1328 return false;
1329}
1330
1332{
1333 /* sanity checks */
1334 if (ELEM(nullptr, nlt, nlt->strips.first)) {
1335 return;
1336 }
1337
1338 /* sort the strips with a more generic function */
1340}
1341
1342bool BKE_nlatrack_add_strip(NlaTrack *nlt, NlaStrip *strip, const bool is_liboverride)
1343{
1344 /* sanity checks */
1345 if (ELEM(nullptr, nlt, strip)) {
1346 return false;
1347 }
1348
1349 /*
1350 * Do not allow adding strips if this track is locked, or not a local one in liboverride case.
1351 */
1352 if (nlt->flag & NLATRACK_PROTECTED ||
1353 (is_liboverride && (nlt->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0))
1354 {
1355 return false;
1356 }
1357
1358 /* try to add the strip to the track using a more generic function */
1359 return BKE_nlastrips_add_strip(&nlt->strips, strip);
1360}
1361
1363{
1364 BLI_assert(track);
1365 BKE_nlastrip_remove(&track->strips, strip);
1366}
1367
1369{
1370 NlaStrip *strip;
1371
1372 /* initialize bounds */
1373 if (bounds) {
1374 bounds[0] = bounds[1] = 0.0f;
1375 }
1376 else {
1377 return false;
1378 }
1379
1380 /* sanity checks */
1381 if (ELEM(nullptr, nlt, nlt->strips.first)) {
1382 return false;
1383 }
1384
1385 /* lower bound is first strip's start frame */
1386 strip = static_cast<NlaStrip *>(nlt->strips.first);
1387 bounds[0] = strip->start;
1388
1389 /* upper bound is last strip's end frame */
1390 strip = static_cast<NlaStrip *>(nlt->strips.last);
1391 bounds[1] = strip->end;
1392
1393 /* done */
1394 return true;
1395}
1396
1398{
1399 return (ID_IS_OVERRIDE_LIBRARY(id) &&
1400 (nlt == nullptr || (nlt->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0));
1401}
1402
1403/* NLA Strips -------------------------------------- */
1404
1405static NlaStrip *nlastrip_find_active(ListBase /*NlaStrip*/ *strips)
1406{
1407 LISTBASE_FOREACH (NlaStrip *, strip, strips) {
1408 if (strip->flag & NLASTRIP_FLAG_ACTIVE) {
1409 return strip;
1410 }
1411
1412 if (strip->type != NLASTRIP_TYPE_META) {
1413 continue;
1414 }
1415
1416 NlaStrip *inner_active = nlastrip_find_active(&strip->strips);
1417 if (inner_active != nullptr) {
1418 return inner_active;
1419 }
1420 }
1421
1422 return nullptr;
1423}
1424
1426{
1427 float limit_prev = MINAFRAMEF;
1428
1429 /* Find the previous end frame, with a special case if the previous strip was a transition : */
1430 if (strip->prev) {
1431 if (strip->prev->type == NLASTRIP_TYPE_TRANSITION) {
1432 limit_prev = strip->prev->start + NLASTRIP_MIN_LEN_THRESH;
1433 }
1434 else {
1435 limit_prev = strip->prev->end;
1436 }
1437 }
1438
1439 return limit_prev;
1440}
1441
1443{
1444 float limit_next = MAXFRAMEF;
1445
1446 /* Find the next begin frame, with a special case if the next strip's a transition : */
1447 if (strip->next) {
1448 if (strip->next->type == NLASTRIP_TYPE_TRANSITION) {
1449 limit_next = strip->next->end - NLASTRIP_MIN_LEN_THRESH;
1450 }
1451 else {
1452 limit_next = strip->next->start;
1453 }
1454 }
1455
1456 return limit_next;
1457}
1458
1459NlaStrip *BKE_nlastrip_next_in_track(NlaStrip *strip, bool skip_transitions)
1460{
1461 NlaStrip *next = strip->next;
1462 while (next != nullptr) {
1463 if (skip_transitions && (next->type == NLASTRIP_TYPE_TRANSITION)) {
1464 next = next->next;
1465 }
1466 else {
1467 return next;
1468 }
1469 }
1470 return nullptr;
1471}
1472
1473NlaStrip *BKE_nlastrip_prev_in_track(NlaStrip *strip, bool skip_transitions)
1474{
1475 NlaStrip *prev = strip->prev;
1476 while (prev != nullptr) {
1477 if (skip_transitions && (prev->type == NLASTRIP_TYPE_TRANSITION)) {
1478 prev = prev->prev;
1479 }
1480 else {
1481 return prev;
1482 }
1483 }
1484 return nullptr;
1485}
1486
1488{
1489 if (nlt == nullptr) {
1490 return nullptr;
1491 }
1492
1493 return nlastrip_find_active(&nlt->strips);
1494}
1495
1497{
1498 BLI_assert(strips);
1499 BLI_remlink(strips, strip);
1500}
1501
1502void BKE_nlastrip_remove_and_free(ListBase *strips, NlaStrip *strip, const bool do_id_user)
1503{
1504 BKE_nlastrip_remove(strips, strip);
1505 BKE_nlastrip_free(strip, do_id_user);
1506}
1507
1509{
1510 /* sanity checks */
1511 if (adt == nullptr) {
1512 return;
1513 }
1514
1515 /* Loop over tracks, deactivating. */
1516 LISTBASE_FOREACH (NlaTrack *, nlt, &adt->nla_tracks) {
1517 LISTBASE_FOREACH (NlaStrip *, nls, &nlt->strips) {
1518 if (nls != strip) {
1519 nls->flag &= ~NLASTRIP_FLAG_ACTIVE;
1520 }
1521 else {
1522 nls->flag |= NLASTRIP_FLAG_ACTIVE;
1523 }
1524 }
1525 }
1526}
1527
1528static NlaStrip *nlastrip_find_by_name(ListBase /*NlaStrip*/ *strips, const char *name)
1529{
1530 LISTBASE_FOREACH (NlaStrip *, strip, strips) {
1531 if (STREQ(strip->name, name)) {
1532 return strip;
1533 }
1534
1535 if (strip->type != NLASTRIP_TYPE_META) {
1536 continue;
1537 }
1538
1539 NlaStrip *inner_strip = nlastrip_find_by_name(&strip->strips, name);
1540 if (inner_strip != nullptr) {
1541 return inner_strip;
1542 }
1543 }
1544
1545 return nullptr;
1546}
1547
1549{
1550 return nlastrip_find_by_name(&nlt->strips, name);
1551}
1552
1553bool BKE_nlastrip_within_bounds(NlaStrip *strip, float min, float max)
1554{
1555 const float stripLen = (strip) ? strip->end - strip->start : 0.0f;
1556 const float boundsLen = fabsf(max - min);
1557
1558 /* sanity checks */
1559 if ((strip == nullptr) || IS_EQF(stripLen, 0.0f) || IS_EQF(boundsLen, 0.0f)) {
1560 return false;
1561 }
1562
1563 /* only ok if at least part of the strip is within the bounding window
1564 * - first 2 cases cover when the strip length is less than the bounding area
1565 * - second 2 cases cover when the strip length is greater than the bounding area
1566 */
1567 if ((stripLen < boundsLen) &&
1568 !(IN_RANGE(strip->start, min, max) || IN_RANGE(strip->end, min, max)))
1569 {
1570 return false;
1571 }
1572 if ((stripLen > boundsLen) &&
1573 !(IN_RANGE(min, strip->start, strip->end) || IN_RANGE(max, strip->start, strip->end)))
1574 {
1575 return false;
1576 }
1577
1578 /* should be ok! */
1579 return true;
1580}
1581
1582float BKE_nlastrip_distance_to_frame(const NlaStrip *strip, const float timeline_frame)
1583{
1584 if (timeline_frame < strip->start) {
1585 return strip->start - timeline_frame;
1586 }
1587 if (strip->end < timeline_frame) {
1588 return timeline_frame - strip->end;
1589 }
1590 return 0.0f;
1591}
1592
1593/* Ensure that strip doesn't overlap those around it after resizing
1594 * by offsetting those which follow. */
1596{
1597 /* next strips - do this first, since we're often just getting longer */
1598 if (strip->next) {
1599 NlaStrip *nls = strip->next;
1600 float offset = 0.0f;
1601
1602 if (nls->type == NLASTRIP_TYPE_TRANSITION) {
1603 /* transition strips should grow/shrink to accommodate the resized strip,
1604 * but if the strip's bounds now exceed the transition, we're forced to
1605 * offset everything to maintain the balance
1606 */
1607 if (strip->end <= nls->start) {
1608 /* grow the transition to fill the void */
1609 nls->start = strip->end;
1610 }
1611 else if (strip->end < nls->end) {
1612 /* shrink the transition to give the strip room */
1613 nls->start = strip->end;
1614 }
1615 else {
1616 /* Shrink transition down to 1 frame long (so that it can still be found),
1617 * then offset everything else by the remaining deficit to give the strip room. */
1618 nls->start = nls->end - 1.0f;
1619
1620 /* XXX: review whether preventing fractional values is good here... */
1621 offset = ceilf(strip->end - nls->start);
1622
1623 /* apply necessary offset to ensure that the strip has enough space */
1624 for (; nls; nls = nls->next) {
1625 nls->start += offset;
1626 nls->end += offset;
1627 }
1628 }
1629 }
1630 else if (strip->end > nls->start) {
1631 /* NOTE: need to ensure we don't have a fractional frame offset, even if that leaves a gap,
1632 * otherwise it will be very hard to get rid of later
1633 */
1634 offset = ceilf(strip->end - nls->start);
1635
1636 /* apply to times of all strips in this direction */
1637 for (; nls; nls = nls->next) {
1638 nls->start += offset;
1639 nls->end += offset;
1640 }
1641 }
1642 }
1643
1644 /* previous strips - same routine as before */
1645 /* NOTE: when strip bounds are recalculated, this is not considered! */
1646 if (strip->prev) {
1647 NlaStrip *nls = strip->prev;
1648 float offset = 0.0f;
1649
1650 if (nls->type == NLASTRIP_TYPE_TRANSITION) {
1651 /* transition strips should grow/shrink to accommodate the resized strip,
1652 * but if the strip's bounds now exceed the transition, we're forced to
1653 * offset everything to maintain the balance
1654 */
1655 if (strip->start >= nls->end) {
1656 /* grow the transition to fill the void */
1657 nls->end = strip->start;
1658 }
1659 else if (strip->start > nls->start) {
1660 /* shrink the transition to give the strip room */
1661 nls->end = strip->start;
1662 }
1663 else {
1664 /* Shrink transition down to 1 frame long (so that it can still be found),
1665 * then offset everything else by the remaining deficit to give the strip room. */
1666 nls->end = nls->start + 1.0f;
1667
1668 /* XXX: review whether preventing fractional values is good here... */
1669 offset = ceilf(nls->end - strip->start);
1670
1671 /* apply necessary offset to ensure that the strip has enough space */
1672 for (; nls; nls = nls->prev) {
1673 nls->start -= offset;
1674 nls->end -= offset;
1675 }
1676 }
1677 }
1678 else if (strip->start < nls->end) {
1679 /* NOTE: need to ensure we don't have a fractional frame offset, even if that leaves a gap,
1680 * otherwise it will be very hard to get rid of later
1681 */
1682 offset = ceilf(nls->end - strip->start);
1683
1684 /* apply to times of all strips in this direction */
1685 for (; nls; nls = nls->prev) {
1686 nls->start -= offset;
1687 nls->end -= offset;
1688 }
1689 }
1690 }
1691}
1692
1694{
1695 float prev_actstart;
1696
1697 if (strip == nullptr || strip->type != NLASTRIP_TYPE_CLIP) {
1698 return;
1699 }
1700
1701 prev_actstart = strip->actstart;
1702
1703 const float2 frame_range = strip->act->wrap().get_frame_range_of_slot(strip->action_slot_handle);
1704 strip->actstart = frame_range[0];
1705 strip->actend = frame_range[1];
1706
1708
1709 /* Set start such that key's do not visually move, to preserve the overall animation result. */
1710 strip->start += (strip->actstart - prev_actstart) * strip->scale;
1711
1713}
1715{
1716 float mapping;
1717
1718 /* sanity checks
1719 * - must have a strip
1720 * - can only be done for action clips
1721 */
1722 if ((strip == nullptr) || (strip->type != NLASTRIP_TYPE_CLIP)) {
1723 return;
1724 }
1725
1726 /* calculate new length factors */
1727 const float actlen = BKE_nla_clip_length_get_nonzero(strip);
1728
1729 mapping = strip->scale * strip->repeat;
1730
1731 /* adjust endpoint of strip in response to this */
1732 if (IS_EQF(mapping, 0.0f) == 0) {
1733 strip->end = (actlen * mapping) + strip->start;
1734 }
1735
1736 /* make sure we don't overlap our neighbors */
1738}
1739
1741{
1742
1743 /* check if values need to be re-calculated. */
1744 if (strip->blendin == 0 && strip->blendout == 0) {
1745 return;
1746 }
1747
1748 const double strip_len = strip->end - strip->start;
1749 double blend_in = strip->blendin;
1750 double blend_out = strip->blendout;
1751
1752 double blend_in_max = strip_len - blend_out;
1753
1754 CLAMP_MIN(blend_in_max, 0);
1755
1756 /* blend-out is limited to the length of the strip. */
1757 CLAMP(blend_in, 0, blend_in_max);
1758 CLAMP(blend_out, 0, strip_len - blend_in);
1759
1760 strip->blendin = blend_in;
1761 strip->blendout = blend_out;
1762}
1763
1764/* Animated Strips ------------------------------------------- */
1765
1767{
1768 /* sanity checks */
1769 if (ELEM(nullptr, nlt, nlt->strips.first)) {
1770 return false;
1771 }
1772
1773 /* check each strip for F-Curves only (don't care about whether the flags are set) */
1774 LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
1775 if (strip->fcurves.first) {
1776 return true;
1777 }
1778 }
1779
1780 /* none found */
1781 return false;
1782}
1783
1785{
1786 /* sanity checks */
1787 if (ELEM(nullptr, tracks, tracks->first)) {
1788 return false;
1789 }
1790
1791 /* check each track, stopping on the first hit */
1792 LISTBASE_FOREACH (NlaTrack *, nlt, tracks) {
1794 return true;
1795 }
1796 }
1797
1798 /* none found */
1799 return false;
1800}
1801
1803{
1804 FCurve *fcu;
1805
1806 /* sanity checks */
1807 if (strip == nullptr) {
1808 return;
1809 }
1810
1811 /* if controlling influence... */
1812 if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) {
1813 /* try to get F-Curve */
1814 fcu = BKE_fcurve_find(&strip->fcurves, "influence", 0);
1815
1816 /* add one if not found */
1817 if (fcu == nullptr) {
1818 /* make new F-Curve */
1819 fcu = BKE_fcurve_create();
1820 BLI_addtail(&strip->fcurves, fcu);
1821
1822 /* set default flags */
1824 fcu->auto_smoothing = U.auto_smoothing_new;
1825
1826 /* store path - make copy, and store that */
1827 fcu->rna_path = BLI_strdupn("influence", 9);
1828
1829 /* insert keyframe to ensure current value stays on first refresh */
1830 fcu->bezt = MEM_callocN<BezTriple>("nlastrip influence bezt");
1831 fcu->totvert = 1;
1832
1833 fcu->bezt->vec[1][0] = strip->start;
1834 fcu->bezt->vec[1][1] = strip->influence;
1835
1836 /* Respect User Preferences for default interpolation and handles. */
1837 fcu->bezt->h1 = fcu->bezt->h2 = U.keyhandles_new;
1838 fcu->bezt->ipo = U.ipo_new;
1839 }
1840 }
1841
1842 /* if controlling time... */
1843 if (strip->flag & NLASTRIP_FLAG_USR_TIME) {
1844 /* try to get F-Curve */
1845 fcu = BKE_fcurve_find(&strip->fcurves, "strip_time", 0);
1846
1847 /* add one if not found */
1848 if (fcu == nullptr) {
1849 /* make new F-Curve */
1850 fcu = BKE_fcurve_create();
1851 BLI_addtail(&strip->fcurves, fcu);
1852
1853 /* set default flags */
1855 fcu->auto_smoothing = U.auto_smoothing_new;
1856
1857 /* store path - make copy, and store that */
1858 fcu->rna_path = BLI_strdupn("strip_time", 10);
1859
1860 /* TODO: insert a few keyframes to ensure default behavior? */
1861 }
1862 }
1863}
1864
1866{
1867 if (STREQ(fcurve->rna_path, "strip_time")) {
1868 strip->flag &= ~NLASTRIP_FLAG_USR_TIME;
1869 }
1870 else if (STREQ(fcurve->rna_path, "influence")) {
1872 }
1873 else {
1874 return false;
1875 }
1876
1877 BLI_remlink(&strip->fcurves, fcurve);
1878 BKE_fcurve_free(fcurve);
1879 return true;
1880}
1881
1883{
1884 /* sanity checks */
1885 if (ELEM(nullptr, ptr, prop)) {
1886 return false;
1887 }
1888
1889 /* 1) Must be NLA strip */
1890 if (ptr->type == &RNA_NlaStrip) {
1891 /* 2) Must be one of the predefined properties */
1892 static PropertyRNA *prop_influence = nullptr;
1893 static PropertyRNA *prop_time = nullptr;
1894 static bool needs_init = true;
1895
1896 /* Init the properties on first use */
1897 if (needs_init) {
1898 prop_influence = RNA_struct_type_find_property(&RNA_NlaStrip, "influence");
1899 prop_time = RNA_struct_type_find_property(&RNA_NlaStrip, "strip_time");
1900
1901 needs_init = false;
1902 }
1903
1904 /* Check if match */
1905 if (ELEM(prop, prop_influence, prop_time)) {
1906 return true;
1907 }
1908 }
1909
1910 /* No criteria met */
1911 return false;
1912}
1913
1914/* Sanity Validation ------------------------------------ */
1915
1917{
1918 GHash *gh;
1919
1920 /* sanity checks */
1921 if (ELEM(nullptr, adt, strip)) {
1922 return;
1923 }
1924
1925 /* give strip a default name if none already */
1926 if (strip->name[0] == 0) {
1927 switch (strip->type) {
1928 case NLASTRIP_TYPE_CLIP: /* act-clip */
1929 STRNCPY_UTF8(strip->name, (strip->act) ? (strip->act->id.name + 2) : DATA_("<No Action>"));
1930 break;
1931 case NLASTRIP_TYPE_TRANSITION: /* transition */
1932 STRNCPY_UTF8(strip->name, DATA_("Transition"));
1933 break;
1934 case NLASTRIP_TYPE_META: /* meta */
1936 break;
1937 default:
1938 STRNCPY_UTF8(strip->name, DATA_("NLA Strip"));
1939 break;
1940 }
1941 }
1942
1943 /* build a hash-table of all the strips in the tracks
1944 * - this is easier than iterating over all the tracks+strips hierarchy every time
1945 * (and probably faster)
1946 */
1947 gh = BLI_ghash_str_new("nlastrip_validate_name gh");
1948
1949 LISTBASE_FOREACH (NlaTrack *, nlt, &adt->nla_tracks) {
1950 LISTBASE_FOREACH (NlaStrip *, tstrip, &nlt->strips) {
1951 /* don't add the strip of interest */
1952 if (tstrip == strip) {
1953 continue;
1954 }
1955
1956 /* Use the name of the strip as the key, and the strip as the value,
1957 * since we're mostly interested in the keys. */
1958 BLI_ghash_insert(gh, tstrip->name, tstrip);
1959 }
1960 }
1961
1962 /* If the hash-table has a match for this name, try other names...
1963 * - In an extreme case, it might not be able to find a name,
1964 * but then everything else in Blender would fail too :).
1965 */
1967 [&](const blender::StringRefNull check_name) {
1968 return BLI_ghash_haskey(gh, check_name.c_str());
1969 },
1970 DATA_("NlaStrip"),
1971 '.',
1972 strip->name,
1973 sizeof(strip->name));
1974
1975 /* free the hash... */
1976 BLI_ghash_free(gh, nullptr, nullptr);
1977}
1978
1979/* ---- */
1980
1981/* Get strips which overlap the given one at the start/end of its range
1982 * - strip: strip that we're finding overlaps for
1983 * - track: nla-track that the overlapping strips should be found from
1984 * - start, end: frames for the offending endpoints
1985 */
1987 NlaTrack *track,
1988 float **start,
1989 float **end)
1990{
1991 /* find strips that overlap over the start/end of the given strip,
1992 * but which don't cover the entire length
1993 */
1994 /* TODO: this scheme could get quite slow for doing this on many strips... */
1995 LISTBASE_FOREACH (NlaStrip *, nls, &track->strips) {
1996 /* Check if strip overlaps (extends over or exactly on)
1997 * the entire range of the strip we're validating. */
1998 if ((nls->start <= strip->start) && (nls->end >= strip->end)) {
1999 *start = nullptr;
2000 *end = nullptr;
2001 return;
2002 }
2003
2004 /* check if strip doesn't even occur anywhere near... */
2005 if (nls->end < strip->start) {
2006 continue; /* skip checking this strip... not worthy of mention */
2007 }
2008 if (nls->start > strip->end) {
2009 return; /* the range we're after has already passed */
2010 }
2011
2012 /* if this strip is not part of an island of continuous strips, it can be used
2013 * - this check needs to be done for each end of the strip we try and use...
2014 */
2015 if ((nls->next == nullptr) || IS_EQF(nls->next->start, nls->end) == 0) {
2016 if ((nls->end > strip->start) && (nls->end < strip->end)) {
2017 *start = &nls->end;
2018 }
2019 }
2020 if ((nls->prev == nullptr) || IS_EQF(nls->prev->end, nls->start) == 0) {
2021 if ((nls->start < strip->end) && (nls->start > strip->start)) {
2022 *end = &nls->start;
2023 }
2024 }
2025 }
2026}
2027
2028/* Determine auto-blending for the given strip */
2030{
2031 float *ps = nullptr, *pe = nullptr;
2032 float *ns = nullptr, *ne = nullptr;
2033
2034 /* sanity checks */
2035 if (ELEM(nullptr, nls, nlt)) {
2036 return;
2037 }
2038 if ((nlt->prev == nullptr) && (nlt->next == nullptr)) {
2039 return;
2040 }
2041 if ((nls->flag & NLASTRIP_FLAG_AUTO_BLENDS) == 0) {
2042 return;
2043 }
2044
2045 /* get test ranges */
2046 if (nlt->prev) {
2047 nlastrip_get_endpoint_overlaps(nls, nlt->prev, &ps, &pe);
2048 }
2049 if (nlt->next) {
2050 nlastrip_get_endpoint_overlaps(nls, nlt->next, &ns, &ne);
2051 }
2052
2053 /* set overlaps for this strip
2054 * - don't use the values obtained though if the end in question
2055 * is directly followed/preceded by another strip, forming an
2056 * 'island' of continuous strips
2057 */
2058 if ((ps || ns) && ((nls->prev == nullptr) || IS_EQF(nls->prev->end, nls->start) == 0)) {
2059 /* start overlaps - pick the largest overlap */
2060 if (((ps && ns) && (*ps > *ns)) || (ps)) {
2061 nls->blendin = *ps - nls->start;
2062 }
2063 else {
2064 nls->blendin = *ns - nls->start;
2065 }
2066 }
2067 else { /* no overlap allowed/needed */
2068 nls->blendin = 0.0f;
2069 }
2070
2071 if ((pe || ne) && ((nls->next == nullptr) || IS_EQF(nls->next->start, nls->end) == 0)) {
2072 /* end overlaps - pick the largest overlap */
2073 if (((pe && ne) && (*pe > *ne)) || (pe)) {
2074 nls->blendout = nls->end - *pe;
2075 }
2076 else {
2077 nls->blendout = nls->end - *ne;
2078 }
2079 }
2080 else { /* no overlap allowed/needed */
2081 nls->blendout = 0.0f;
2082 }
2083}
2084
2091{
2092
2093 if (!(strip->type == NLASTRIP_TYPE_TRANSITION)) {
2094 return true;
2095 }
2096 if (strip->prev) {
2097 strip->start = strip->prev->end;
2098 }
2099 if (strip->next) {
2100 strip->end = strip->next->start;
2101 }
2102 if (strip->start >= strip->end || strip->prev == nullptr || strip->next == nullptr) {
2103 BKE_nlastrip_remove_and_free(strips, strip, true);
2104 return false;
2105 }
2106 return true;
2107}
2108
2110{
2111 /* sanity checks */
2112 if (ELEM(nullptr, adt, adt->nla_tracks.first)) {
2113 return;
2114 }
2115
2116 /* Adjust blending values for auto-blending,
2117 * and also do an initial pass to find the earliest strip. */
2118 LISTBASE_FOREACH (NlaTrack *, nlt, &adt->nla_tracks) {
2119 LISTBASE_FOREACH_MUTABLE (NlaStrip *, strip, &nlt->strips) {
2120
2121 if (!nlastrip_validate_transition_start_end(&nlt->strips, strip)) {
2122 printf(
2123 "While moving NLA strips, a transition strip could no longer be applied to the new "
2124 "positions and was removed.\n");
2125 continue;
2126 }
2127
2128 /* auto-blending first */
2131 }
2132 }
2133}
2134
2135/* Action Stashing -------------------------------------- */
2136
2137/* name of stashed tracks - the translation stuff is included here to save extra work */
2138#define STASH_TRACK_NAME DATA_("[Action Stash]")
2139
2141 bAction *act,
2142 const blender::animrig::slot_handle_t slot_handle)
2143{
2144 LISTBASE_FOREACH (NlaTrack *, nlt, &adt->nla_tracks) {
2145 if (strstr(nlt->name, STASH_TRACK_NAME)) {
2146 LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
2147 if (strip->act == act && strip->action_slot_handle == slot_handle) {
2148 return true;
2149 }
2150 }
2151 }
2152 }
2153
2154 return false;
2155}
2156
2157bool BKE_nla_action_stash(const OwnedAnimData owned_adt, const bool is_liboverride)
2158{
2159 NlaTrack *prev_track = nullptr;
2160 NlaTrack *nlt;
2161 NlaStrip *strip;
2162 AnimData *adt = &owned_adt.adt;
2163
2164 /* sanity check */
2165 if (ELEM(nullptr, adt, adt->action)) {
2166 CLOG_ERROR(&LOG, "Invalid argument - %p %p", adt, adt->action);
2167 return false;
2168 }
2169
2170 /* do not add if it is already stashed */
2171 if (BKE_nla_action_slot_is_stashed(adt, adt->action, adt->slot_handle)) {
2172 return false;
2173 }
2174
2175 /* create a new track, and add this immediately above the previous stashing track */
2176 for (prev_track = static_cast<NlaTrack *>(adt->nla_tracks.last); prev_track;
2177 prev_track = prev_track->prev)
2178 {
2179 if (strstr(prev_track->name, STASH_TRACK_NAME)) {
2180 break;
2181 }
2182 }
2183
2184 nlt = BKE_nlatrack_new_after(&adt->nla_tracks, prev_track, is_liboverride);
2186 BLI_assert(nlt != nullptr);
2187
2188 /* We need to ensure that if there wasn't any previous instance,
2189 * it must go to be bottom of the stack. */
2190 if (prev_track == nullptr) {
2191 BLI_remlink(&adt->nla_tracks, nlt);
2192 BLI_addhead(&adt->nla_tracks, nlt);
2193 }
2194
2197 &adt->nla_tracks, nlt, STASH_TRACK_NAME, '.', offsetof(NlaTrack, name), sizeof(nlt->name));
2198
2199 /* add the action as a strip in this new track
2200 * NOTE: a new user is created here
2201 */
2202 strip = BKE_nlastrip_new_for_slot(adt->action, adt->slot_handle, owned_adt.owner_id);
2203 BLI_assert(strip != nullptr);
2204
2205 BKE_nlatrack_add_strip(nlt, strip, is_liboverride);
2206 BKE_nlastrip_validate_name(adt, strip);
2207
2208 /* mark the stash track and strip so that they doesn't disturb the stack animation,
2209 * and are unlikely to draw attention to itself (or be accidentally bumped around)
2210 *
2211 * NOTE: this must be done *after* adding the strip to the track, or else
2212 * the strip locking will prevent the strip from getting added
2213 */
2216
2217 /* also mark the strip for auto syncing the length, so that the strips accurately
2218 * reflect the length of the action
2219 * XXX: we could do with some extra flags here to prevent repeats/scaling options from working!
2220 */
2222
2223 /* succeeded */
2224 return true;
2225}
2226
2227/* Core Tools ------------------------------------------- */
2228
2229void BKE_nla_action_pushdown(const OwnedAnimData owned_adt, const bool is_liboverride)
2230{
2231 NlaStrip *strip;
2232 AnimData *adt = &owned_adt.adt;
2233
2234 /* sanity checks */
2235 /* TODO: need to report the error for this */
2236 if (ELEM(nullptr, adt, adt->action)) {
2237 return;
2238 }
2239
2240 /* Add a new NLA strip to the track, which references the active action + slot. */
2241 strip = BKE_nlastack_add_strip(owned_adt, is_liboverride);
2242 if (strip == nullptr) {
2243 return;
2244 }
2245
2246 /* Clear reference to action now that we've pushed it onto the stack. */
2247 const bool unassign_ok = animrig::unassign_action(owned_adt.owner_id);
2248 BLI_assert_msg(unassign_ok,
2249 "Expecting un-assigning an action to always work when pushing down an NLA strip");
2250 UNUSED_VARS_NDEBUG(unassign_ok);
2251
2252 /* copy current "action blending" settings from adt to the strip,
2253 * as it was keyframed with these settings, so omitting them will
2254 * change the effect [#54233]. */
2255 strip->blendmode = adt->act_blendmode;
2256 strip->influence = adt->act_influence;
2257 strip->extendmode = adt->act_extendmode;
2258
2259 if (adt->act_influence < 1.0f) {
2260 /* enable "user-controlled" influence (which will insert a default keyframe)
2261 * so that the influence doesn't get lost on the new update
2262 *
2263 * NOTE: An alternative way would have been to instead hack the influence
2264 * to not get always get reset to full strength if NLASTRIP_FLAG_USR_INFLUENCE
2265 * is disabled but auto-blending isn't being used. However, that approach
2266 * is a bit hacky/hard to discover, and may cause backwards compatibility issues,
2267 * so it's better to just do it this way.
2268 */
2271 }
2272
2273 /* make strip the active one... */
2274 BKE_nlastrip_set_active(adt, strip);
2275}
2276
2277static void nla_tweakmode_find_active(const ListBase /*NlaTrack*/ *nla_tracks,
2278 NlaTrack **r_track_of_active_strip,
2279 NlaStrip **r_active_strip)
2280{
2281 NlaTrack *activeTrack = nullptr;
2282 NlaStrip *activeStrip = nullptr;
2283
2284 /* go over the tracks, finding the active one, and its active strip
2285 * - if we cannot find both, then there's nothing to do
2286 */
2287 LISTBASE_FOREACH (NlaTrack *, nlt, nla_tracks) {
2288 /* check if active */
2289 if (nlt->flag & NLATRACK_ACTIVE) {
2290 /* store reference to this active track */
2291 activeTrack = nlt;
2292
2293 /* now try to find active strip */
2294 activeStrip = BKE_nlastrip_find_active(nlt);
2295 break;
2296 }
2297 }
2298
2299 /* There are situations where we may have multiple strips selected and we want to enter
2300 * tweak-mode on all of those at once. Usually in those cases,
2301 * it will usually just be a single strip per AnimData.
2302 * In such cases, compromise and take the last selected track and/or last selected strip,
2303 * #28468.
2304 */
2305 if (activeTrack == nullptr) {
2306 /* try last selected track for active strip */
2307 LISTBASE_FOREACH_BACKWARD (NlaTrack *, nlt, nla_tracks) {
2308 if (nlt->flag & NLATRACK_SELECTED) {
2309 /* assume this is the active track */
2310 activeTrack = nlt;
2311
2312 /* try to find active strip */
2313 activeStrip = BKE_nlastrip_find_active(nlt);
2314 break;
2315 }
2316 }
2317 }
2318 if ((activeTrack) && (activeStrip == nullptr)) {
2319 /* No active strip in active or last selected track;
2320 * compromise for first selected (assuming only single). */
2321 LISTBASE_FOREACH (NlaStrip *, strip, &activeTrack->strips) {
2322 if (strip->flag & (NLASTRIP_FLAG_SELECT | NLASTRIP_FLAG_ACTIVE)) {
2323 activeStrip = strip;
2324 break;
2325 }
2326 }
2327 }
2328
2329 *r_track_of_active_strip = activeTrack;
2330 *r_active_strip = activeStrip;
2331}
2332
2334{
2335 NlaTrack *activeTrack = nullptr;
2336 NlaStrip *activeStrip = nullptr;
2337 AnimData &adt = owned_adt.adt;
2338
2339 /* verify that data is valid */
2340 if (ELEM(nullptr, adt.nla_tracks.first)) {
2341 return false;
2342 }
2343
2344 /* If block is already in tweak-mode, just leave, but we should report
2345 * that this block is in tweak-mode (as our return-code). */
2346 if (adt.flag & ADT_NLA_EDIT_ON) {
2347 return true;
2348 }
2349
2350 nla_tweakmode_find_active(&adt.nla_tracks, &activeTrack, &activeStrip);
2351
2352 if (ELEM(nullptr, activeTrack, activeStrip, activeStrip->act)) {
2353 if (G.debug & G_DEBUG) {
2354 printf("NLA tweak-mode enter - neither active requirement found\n");
2355 printf("\tactiveTrack = %p, activeStrip = %p\n", (void *)activeTrack, (void *)activeStrip);
2356 }
2357 return false;
2358 }
2359
2360 /* Go over all the tracks, tagging each strip that uses the same
2361 * action as the active strip, but leaving everything else alone.
2362 */
2363 LISTBASE_FOREACH (NlaTrack *, nlt, &adt.nla_tracks) {
2364 LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
2365 if (strip->act == activeStrip->act) {
2366 strip->flag |= NLASTRIP_FLAG_TWEAKUSER;
2367 }
2368 else {
2369 strip->flag &= ~NLASTRIP_FLAG_TWEAKUSER;
2370 }
2371 }
2372 }
2373
2374 /* Untag tweaked track. This leads to non tweaked actions being drawn differently than the
2375 * tweaked action. */
2376 activeStrip->flag &= ~NLASTRIP_FLAG_TWEAKUSER;
2377
2378 /* go over all the tracks after AND INCLUDING the active one, tagging them as being disabled
2379 * - the active track needs to also be tagged, otherwise, it'll overlap with the tweaks going
2380 * on.
2381 */
2382 activeTrack->flag |= NLATRACK_DISABLED;
2383 if ((adt.flag & ADT_NLA_EVAL_UPPER_TRACKS) == 0) {
2384 for (NlaTrack *nlt = activeTrack->next; nlt; nlt = nlt->next) {
2385 nlt->flag |= NLATRACK_DISABLED;
2386 }
2387 }
2388
2389 /* Remember which Action + Slot was previously assigned. This will be stored in adt.tmpact and
2390 * related properties further down. This doesn't manipulate `adt.tmpact` quite yet, so that the
2391 * assignment of the tweaked NLA strip's Action can happen normally (we're not in tweak mode
2392 * yet). */
2393 bAction *prev_action = adt.action;
2394 const animrig::slot_handle_t prev_slot_handle = adt.slot_handle;
2395
2396 if (activeStrip->act) {
2397 animrig::Action &strip_action = activeStrip->act->wrap();
2398 if (strip_action.is_action_layered()) {
2399 animrig::Slot *strip_slot = strip_action.slot_for_handle(activeStrip->action_slot_handle);
2400 if (animrig::assign_action_and_slot(&strip_action, strip_slot, owned_adt.owner_id) !=
2402 {
2403 printf("NLA tweak-mode enter - could not assign slot %s\n",
2404 strip_slot ? strip_slot->identifier : "-unassigned-");
2405 /* There is one other reason this could fail: when already in NLA tweak mode. But since
2406 * we're here in the code, the ADT_NLA_EDIT_ON flag is not yet set, and thus that shouldn't
2407 * be the case.
2408 *
2409 * Because this ADT is not in tweak mode, it means that the Action assignment will have
2410 * succeeded (I know, too much coupling here, would be better to have another
2411 * SlotAssignmentResult value for this). */
2412 }
2413 }
2414 else {
2415 adt.action = activeStrip->act;
2416 id_us_plus(&adt.action->id);
2417 }
2418 }
2419 else {
2420 /* This is a strange situation to be in, as every *tweak-able* NLA strip should have an Action.
2421 */
2423 const bool unassign_ok = animrig::unassign_action(owned_adt);
2424 BLI_assert_msg(unassign_ok,
2425 "Expecting un-assigning the Action to work (while entering NLA tweak mode)");
2426 UNUSED_VARS_NDEBUG(unassign_ok);
2427 }
2428
2429 /* Actually set those properties that make this 'NLA Tweak Mode'. */
2431 prev_action, prev_slot_handle, owned_adt);
2434 "Expecting the Action+Slot of an NLA strip to be suitable for direct assignment as well");
2436 adt.act_track = activeTrack;
2437 adt.actstrip = activeStrip;
2438 adt.flag |= ADT_NLA_EDIT_ON;
2439
2440 return true;
2441}
2442
2444{
2445 return adt && (adt->flag & ADT_NLA_EDIT_ON);
2446}
2447
2449{
2450 NlaStrip *active_strip = adt->actstrip;
2451 if (!active_strip || !active_strip->act) {
2452 return;
2453 }
2454
2455 /* sync the length of the user-strip with the new state of the action
2456 * but only if the user has explicitly asked for this to happen
2457 * (see #34645 for things to be careful about)
2458 */
2459 if (active_strip->flag & NLASTRIP_FLAG_SYNC_LENGTH) {
2461 }
2462
2463 /* Sync strip extents of strips using the same Action as the tweaked strip. */
2464 LISTBASE_FOREACH (NlaTrack *, nlt, &adt->nla_tracks) {
2465 LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
2466 if ((strip->flag & NLASTRIP_FLAG_SYNC_LENGTH) && active_strip->act == strip->act) {
2468 }
2469 }
2470 }
2471}
2472
2478{
2480
2481 adt->action = adt->tmpact;
2482 adt->slot_handle = adt->tmp_slot_handle;
2483
2484 adt->tmpact = nullptr;
2487
2488 adt->act_track = nullptr;
2489 adt->actstrip = nullptr;
2490}
2491
2493{
2494 if (!is_nla_in_tweakmode(adt)) {
2495 return;
2496 }
2497
2499}
2500
2502{
2503 if (!is_nla_in_tweakmode(&owned_adt.adt)) {
2504 return;
2505 }
2506
2507 if (owned_adt.adt.action) {
2508 /* When a strip has no slot assigned, it can still enter tweak mode. Inserting a key will then
2509 * create a slot. When exiting tweak mode, this slot has to be assigned to the strip.
2510 * At this moment in time, the adt->action is still the one being tweaked. */
2511 NlaStrip *active_strip = owned_adt.adt.actstrip;
2512 if (active_strip && active_strip->action_slot_handle != owned_adt.adt.slot_handle) {
2514 *active_strip, owned_adt.adt.slot_handle, owned_adt.owner_id);
2516 "When exiting tweak mode, syncing the tweaked Action slot should work");
2518 }
2519
2520 /* The Action will be replaced with adt->tmpact, and thus needs to be unassigned first. */
2521
2522 /* The high-level function animrig::unassign_action() will check whether NLA tweak mode is
2523 * enabled, and if so, refuse to work (and rightfully so). However, exiting tweak mode is not
2524 * just setting a flag (see BKE_animdata_action_editable(), it checks for the tmpact pointer as
2525 * well). Because of that, here we call the low-level generic assignment function, to
2526 * circumvent that check and unconditionally unassign the tweaked Action. */
2527 const bool unassign_ok = animrig::generic_assign_action(owned_adt.owner_id,
2528 nullptr,
2529 owned_adt.adt.action,
2530 owned_adt.adt.slot_handle,
2531 owned_adt.adt.last_slot_identifier);
2532 BLI_assert_msg(unassign_ok,
2533 "When exiting tweak mode, unassigning the tweaked Action should work");
2534 UNUSED_VARS_NDEBUG(unassign_ok);
2535 }
2536
2539
2540 /* nla_tweakmode_exit_nofollowptr() does not follow any pointers, and thus cannot update the slot
2541 * user map. So it has to be done here now. This is safe to do here, as slot->users_add()
2542 * gracefully handles duplicates. */
2543 if (owned_adt.adt.action && owned_adt.adt.slot_handle != animrig::Slot::unassigned) {
2544 animrig::Action &action = owned_adt.adt.action->wrap();
2546 "when a slot is assigned, the action should layered");
2547 animrig::Slot *slot = action.slot_for_handle(owned_adt.adt.slot_handle);
2548 if (slot) {
2549 slot->users_add(owned_adt.owner_id);
2550 }
2551 }
2552}
2553
2555{
2556 LISTBASE_FOREACH (NlaTrack *, nlt, &adt->nla_tracks) {
2557 nlt->flag &= ~NLATRACK_DISABLED;
2558
2559 LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
2560 strip->flag &= ~NLASTRIP_FLAG_TWEAKUSER;
2561 }
2562 }
2563 adt->flag &= ~ADT_NLA_EDIT_ON;
2564}
2565
2567{
2568 if (!adt) {
2569 adt = BKE_animdata_from_id(owner_id);
2570 }
2571 printf("\033[38;5;214mNLA state");
2572 if (owner_id) {
2573 printf(" for %s", owner_id->name);
2574 }
2575 printf("\033[0m\n");
2576
2577 if (!adt) {
2578 printf(" - ADT is nil!\n");
2579 return;
2580 }
2581
2582 printf(" - ADT flags:");
2583 if (adt->flag & ADT_NLA_SOLO_TRACK) {
2584 printf(" SOLO_TRACK");
2585 }
2586 if (adt->flag & ADT_NLA_EVAL_OFF) {
2587 printf(" EVAL_OFF");
2588 }
2589 if (adt->flag & ADT_NLA_EDIT_ON) {
2590 printf(" EDIT_ON");
2591 }
2592 if (adt->flag & ADT_NLA_EDIT_NOMAP) {
2593 printf(" EDIT_NOMAP");
2594 }
2595 if (adt->flag & ADT_NLA_SKEYS_COLLAPSED) {
2596 printf(" SKEYS_COLLAPSED");
2597 }
2598 if (adt->flag & ADT_NLA_EVAL_UPPER_TRACKS) {
2599 printf(" EVAL_UPPER_TRACKS");
2600 }
2603 {
2604 printf(" -");
2605 }
2606 printf("\n");
2607
2608 if (BLI_listbase_is_empty(&adt->nla_tracks)) {
2609 printf(" - No tracks\n");
2610 return;
2611 }
2612 printf(" - Active track: %s (#%d)\n",
2613 adt->act_track ? adt->act_track->name : "-nil-",
2614 adt->act_track ? adt->act_track->index : 0);
2615 printf(" - Active strip: %s\n", adt->actstrip ? adt->actstrip->name : "-nil-");
2616
2617 LISTBASE_FOREACH (NlaTrack *, nlt, &adt->nla_tracks) {
2618 printf(" - Track #%d %s: ", nlt->index, nlt->name);
2619 if (nlt->flag & NLATRACK_ACTIVE) {
2620 printf("ACTIVE ");
2621 }
2622 if (nlt->flag & NLATRACK_SELECTED) {
2623 printf("SELECTED ");
2624 }
2625 if (nlt->flag & NLATRACK_MUTED) {
2626 printf("MUTED ");
2627 }
2628 if (nlt->flag & NLATRACK_SOLO) {
2629 printf("SOLO ");
2630 }
2631 if (nlt->flag & NLATRACK_PROTECTED) {
2632 printf("PROTECTED ");
2633 }
2634 if (nlt->flag & NLATRACK_DISABLED) {
2635 printf("DISABLED ");
2636 }
2637 if (nlt->flag & NLATRACK_TEMPORARILY_ADDED) {
2638 printf("TEMPORARILY_ADDED ");
2639 }
2640 if (nlt->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) {
2641 printf("OVERRIDELIBRARY_LOCAL ");
2642 }
2643 printf("\n");
2644
2645 LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
2646 printf(" - Strip %s: ", strip->name);
2647 if (strip->flag & NLASTRIP_FLAG_ACTIVE) {
2648 printf("ACTIVE ");
2649 }
2650 if (strip->flag & NLASTRIP_FLAG_SELECT) {
2651 printf("SELECT ");
2652 }
2653 if (strip->flag & NLASTRIP_FLAG_TWEAKUSER) {
2654 printf("TWEAKUSER ");
2655 }
2656 if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) {
2657 printf("USR_INFLUENCE ");
2658 }
2659 if (strip->flag & NLASTRIP_FLAG_USR_TIME) {
2660 printf("USR_TIME ");
2661 }
2662 if (strip->flag & NLASTRIP_FLAG_USR_TIME_CYCLIC) {
2663 printf("USR_TIME_CYCLIC ");
2664 }
2665 if (strip->flag & NLASTRIP_FLAG_SYNC_LENGTH) {
2666 printf("SYNC_LENGTH ");
2667 }
2668 if (strip->flag & NLASTRIP_FLAG_AUTO_BLENDS) {
2669 printf("AUTO_BLENDS ");
2670 }
2671 if (strip->flag & NLASTRIP_FLAG_REVERSE) {
2672 printf("REVERSE ");
2673 }
2674 if (strip->flag & NLASTRIP_FLAG_MUTED) {
2675 printf("MUTED ");
2676 }
2677 if (strip->flag & NLASTRIP_FLAG_INVALID_LOCATION) {
2678 printf("INVALID_LOCATION ");
2679 }
2680 if (strip->flag & NLASTRIP_FLAG_NO_TIME_MAP) {
2681 printf("NO_TIME_MAP ");
2682 }
2683 if (strip->flag & NLASTRIP_FLAG_TEMP_META) {
2684 printf("TEMP_META ");
2685 }
2686 if (strip->flag & NLASTRIP_FLAG_EDIT_TOUCHED) {
2687 printf("EDIT_TOUCHED ");
2688 }
2689 printf("\n");
2690 }
2691 }
2692}
2693
2694static void blend_write_nla_strips(BlendWriter *writer, ListBase *strips)
2695{
2696 BLO_write_struct_list(writer, NlaStrip, strips);
2697 LISTBASE_FOREACH (NlaStrip *, strip, strips) {
2698 /* write the strip's F-Curves and modifiers */
2699 BKE_fcurve_blend_write_listbase(writer, &strip->fcurves);
2700 BKE_fmodifiers_blend_write(writer, &strip->modifiers);
2701
2702 /* write the strip's children */
2703 blend_write_nla_strips(writer, &strip->strips);
2704 }
2705}
2706
2708{
2709 LISTBASE_FOREACH (NlaStrip *, strip, strips) {
2710 /* strip's child strips */
2711 BLO_read_struct_list(reader, NlaStrip, &strip->strips);
2712 blend_data_read_nla_strips(reader, &strip->strips);
2713
2714 /* strip's F-Curves */
2715 BLO_read_struct_list(reader, FCurve, &strip->fcurves);
2716 BKE_fcurve_blend_read_data_listbase(reader, &strip->fcurves);
2717
2718 /* strip's F-Modifiers */
2719 BLO_read_struct_list(reader, FModifier, &strip->modifiers);
2720 BKE_fmodifiers_blend_read_data(reader, &strip->modifiers, nullptr);
2721 }
2722}
2723
2725{
2726 /* write all the tracks */
2727 LISTBASE_FOREACH (NlaTrack *, nlt, tracks) {
2728 /* write the track first */
2729 BLO_write_struct(writer, NlaTrack, nlt);
2730
2731 /* write the track's strips */
2732 blend_write_nla_strips(writer, &nlt->strips);
2733 }
2734}
2735
2737{
2738 LISTBASE_FOREACH (NlaTrack *, nlt, tracks) {
2739 /* If linking from a library, clear 'local' library override flag. */
2740 if (ID_IS_LINKED(id_owner)) {
2742 }
2743
2744 /* relink list of strips */
2745 BLO_read_struct_list(reader, NlaStrip, &nlt->strips);
2746
2747 /* relink strip data */
2748 blend_data_read_nla_strips(reader, &nlt->strips);
2749 }
2750}
2751
2753{
2754 /* TODO(Sybren): replace these two parameters with an OwnedAnimData struct. */
2755 BLI_assert(id);
2756 BLI_assert(adt);
2757
2758 /* The 'id' parameter is unused as it's not necessary here, but still can be useful when
2759 * debugging. And with the NLA complexity the way it is, debugging comfort is kinda nice. */
2761
2762 const bool is_tweak_mode = (adt->flag & ADT_NLA_EDIT_ON);
2763 const bool has_tracks = !BLI_listbase_is_empty(&adt->nla_tracks);
2764
2765 if (!has_tracks) {
2766 if (is_tweak_mode) {
2767 /* No tracks, so it's impossible to actually be in tweak mode. */
2768 BKE_nla_tweakmode_exit({*id, *adt});
2769 }
2770 return;
2771 }
2772
2773 if (!is_tweak_mode) {
2774 /* Library-linked data should already have been cleared of NLA Tweak Mode flags, and the
2775 * overrides also didn't set tweak mode enabled. Assuming here that the override-added
2776 * tracks/strips are consistent with the override's tweak mode flag. */
2777 return;
2778 }
2779
2780 /* In tweak mode, with tracks, so ensure that the active track/strip pointers are correct. Since
2781 * these pointers may come from a library, but the override may have added other tracks and
2782 * strips (one of which is in tweak mode), always look up the current pointer values. */
2784 if (!adt->act_track || !adt->actstrip) {
2785 /* Could not find the active track/strip, so better to exit tweak mode. */
2786 BKE_nla_tweakmode_exit({*id, *adt});
2787 }
2788}
2789
2790static bool visit_strip(NlaStrip *strip, blender::FunctionRef<bool(NlaStrip *)> callback)
2791{
2792 if (!callback(strip)) {
2793 return false;
2794 }
2795
2796 /* Recurse into sub-strips. */
2797 LISTBASE_FOREACH (NlaStrip *, sub_strip, &strip->strips) {
2798 if (!visit_strip(sub_strip, callback)) {
2799 return false;
2800 }
2801 }
2802 return true;
2803}
2804
2805namespace blender::bke::nla {
2806
2807bool foreach_strip(ID *id, blender::FunctionRef<bool(NlaStrip *)> callback)
2808{
2809 const AnimData *adt = BKE_animdata_from_id(id);
2810 if (!adt) {
2811 /* Having no NLA trivially means that we've looped through all the strips. */
2812 return true;
2813 }
2814 return foreach_strip_adt(*adt, callback);
2815}
2816
2817bool foreach_strip_adt(const AnimData &adt, blender::FunctionRef<bool(NlaStrip *)> callback)
2818{
2819 LISTBASE_FOREACH (NlaTrack *, nlt, &adt.nla_tracks) {
2820 LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
2821 if (!visit_strip(strip, callback)) {
2822 return false;
2823 }
2824 }
2825 }
2826 return true;
2827}
2828
2829} // namespace blender::bke::nla
Blender kernel action and pose functionality.
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:83
FCurve * BKE_fcurve_create()
void BKE_fcurve_foreach_id(FCurve *fcu, LibraryForeachIDData *data)
void copy_fmodifiers(ListBase *dst, const ListBase *src)
void BKE_fcurve_blend_write_listbase(BlendWriter *writer, ListBase *fcurves)
FCurve * BKE_fcurve_find(ListBase *list, const char rna_path[], int array_index)
void BKE_fmodifiers_blend_write(BlendWriter *writer, ListBase *fmodifiers)
void BKE_fcurve_blend_read_data_listbase(BlendDataReader *reader, ListBase *fcurves)
void BKE_fmodifiers_blend_read_data(BlendDataReader *reader, ListBase *fmodifiers, FCurve *curve)
void BKE_fcurves_free(ListBase *list)
void BKE_fcurves_copy(ListBase *dst, ListBase *src)
void free_fmodifiers(ListBase *modifiers)
void BKE_fcurve_free(FCurve *fcu)
@ G_DEBUG
void id_us_plus(ID *id)
Definition lib_id.cc:358
ID * BKE_id_copy_ex(Main *bmain, const ID *id, ID **new_id_p, int flag)
Definition lib_id.cc:777
@ LIB_ID_CREATE_NO_USER_REFCOUNT
void id_us_min(ID *id)
Definition lib_id.cc:366
#define BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data_, func_call_)
#define BKE_LIB_FOREACHID_PROCESS_IDSUPER(data_, id_super_, cb_flag_)
@ IDWALK_CB_USER
eNlaTime_ConvertModes
Definition BKE_nla.hh:544
@ NLATIME_CONVERT_MAP
Definition BKE_nla.hh:552
@ NLATIME_CONVERT_UNMAP
Definition BKE_nla.hh:549
#define NLASTRIP_MIN_LEN_THRESH
Definition BKE_nla.hh:12
bool BKE_sound_info_get(struct Main *main, struct bSound *sound, SoundInfo *sound_info)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
bool BLI_ghash_haskey(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:819
GHash * BLI_ghash_str_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition BLI_ghash.cc:707
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.cc:860
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(ListBase *lb)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_insertlinkafter(ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:332
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
void BLI_addhead(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:91
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
void BLI_insertlinkbefore(ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:371
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.cc:30
#define STRNCPY_UTF8(dst, src)
size_t void BLI_uniquename_cb(blender::FunctionRef< bool(blender::StringRefNull)> unique_check, const char *defname, char delim, char *name, size_t name_maxncpy) ATTR_NONNULL(2
void BLI_uniquename(const struct ListBase *list, void *vlink, const char *defname, char delim, int name_offset, size_t name_maxncpy) ATTR_NONNULL(1
#define CLAMP(a, b, c)
#define UNUSED_VARS(...)
#define IN_RANGE(a, b, c)
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define IS_EQF(a, b)
#define STREQ(a, b)
#define CLAMP_MIN(a, b)
#define BLO_write_struct(writer, struct_name, data_ptr)
#define BLO_read_struct_list(reader, struct_name, list)
#define BLO_write_struct_list(writer, struct_name, list_ptr)
#define BLT_I18NCONTEXT_ID_ACTION
#define CTX_DATA_(context, msgid)
#define DATA_(msgid)
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:188
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:694
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:730
@ ACT_FRAME_RANGE
@ NLASTRIP_FLAG_ACTIVE
@ NLASTRIP_FLAG_USR_INFLUENCE
@ NLASTRIP_FLAG_USR_TIME
@ NLASTRIP_FLAG_INVALID_LOCATION
@ NLASTRIP_FLAG_TEMP_META
@ NLASTRIP_FLAG_AUTO_BLENDS
@ NLASTRIP_FLAG_REVERSE
@ NLASTRIP_FLAG_MUTED
@ NLASTRIP_FLAG_USR_TIME_CYCLIC
@ NLASTRIP_FLAG_NO_TIME_MAP
@ NLASTRIP_FLAG_SELECT
@ NLASTRIP_FLAG_TWEAKUSER
@ NLASTRIP_FLAG_EDIT_TOUCHED
@ NLASTRIP_FLAG_SYNC_LENGTH
@ ADT_NLA_SOLO_TRACK
@ ADT_NLA_SKEYS_COLLAPSED
@ ADT_NLA_EVAL_OFF
@ ADT_NLA_EDIT_NOMAP
@ ADT_NLA_EVAL_UPPER_TRACKS
@ ADT_NLA_EDIT_ON
@ NLASTRIP_EXTEND_NOTHING
@ NLASTRIP_TYPE_SOUND
@ NLASTRIP_TYPE_META
@ NLASTRIP_TYPE_TRANSITION
@ NLASTRIP_TYPE_CLIP
@ FCURVE_SELECTED
@ FCURVE_VISIBLE
@ NLATRACK_SOLO
@ NLATRACK_ACTIVE
@ NLATRACK_MUTED
@ NLATRACK_DISABLED
@ NLATRACK_SELECTED
@ NLATRACK_PROTECTED
@ NLATRACK_TEMPORARILY_ADDED
@ NLATRACK_OVERRIDELIBRARY_LOCAL
#define MAXFRAMEF
#define MINAFRAMEF
Read Guarded memory(de)allocation.
#define U
float nlastrip_get_frame(NlaStrip *strip, float cframe, short mode)
bool BKE_nlatrack_get_bounds(NlaTrack *nlt, float bounds[2])
static void blend_write_nla_strips(BlendWriter *writer, ListBase *strips)
static void nla_tweakmode_exit_nofollowptr(AnimData *adt)
void BKE_nlameta_flush_transforms(NlaStrip *mstrip)
static bool is_nla_in_tweakmode(AnimData *adt)
float BKE_nlastrip_distance_to_frame(const NlaStrip *strip, const float timeline_frame)
bool BKE_nlatrack_has_animated_strips(NlaTrack *nlt)
void BKE_nlastrip_remove_and_free(ListBase *strips, NlaStrip *strip, const bool do_id_user)
void BKE_nlastrips_clear_metastrip(ListBase *strips, NlaStrip *strip)
static bool nlastrip_validate_transition_start_end(ListBase *strips, NlaStrip *strip)
static void nla_tweakmode_find_active(const ListBase *nla_tracks, NlaTrack **r_track_of_active_strip, NlaStrip **r_active_strip)
void BKE_nla_debug_print_flags(AnimData *adt, ID *owner_id)
static void update_active_track(AnimData *adt_dest, const AnimData *adt_source)
void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, const ListBase *src, const int flag)
void BKE_nla_clip_length_ensure_nonzero(const float *actstart, float *r_actend)
void BKE_nlastrip_recalculate_bounds_sync_action(NlaStrip *strip)
static float nlastrip_get_frame_actionclip(NlaStrip *strip, float cframe, short mode)
static NlaStrip * nlastrip_new(bAction *act, ID &animated_id)
void BKE_nlastrips_sort_strips(ListBase *strips)
void BKE_nlastrips_clear_metas(ListBase *strips, bool only_sel, bool only_temp)
float BKE_nlastrip_compute_frame_to_next_strip(NlaStrip *strip)
float BKE_nla_tweakedit_remap(AnimData *adt, const float cframe, const eNlaTime_ConvertModes mode)
static void nlastrip_get_endpoint_overlaps(NlaStrip *strip, NlaTrack *track, float **start, float **end)
void BKE_nla_tracks_copy_from_adt(Main *bmain, AnimData *adt_dest, const AnimData *adt_source, const int flag)
static NlaStrip * nlastrip_find_active(ListBase *strips)
void BKE_nla_validate_state(AnimData *adt)
void BKE_nla_tweakmode_exit_nofollowptr(AnimData *adt)
NlaStrip * BKE_nlastrip_prev_in_track(NlaStrip *strip, bool skip_transitions)
NlaTrack * BKE_nlatrack_new()
void BKE_nlatrack_solo_toggle(AnimData *adt, NlaTrack *nlt)
bool BKE_nla_tweakmode_enter(const OwnedAnimData owned_adt)
bool BKE_nlastrip_controlcurve_remove(NlaStrip *strip, FCurve *fcurve)
static NlaStrip * find_active_strip_from_listbase(const NlaStrip *active_strip, const ListBase *strips_source, const ListBase *strips_dest)
void BKE_nlatrack_free(NlaTrack *nlt, const bool do_id_user)
NlaStrip * BKE_nlastrip_next_in_track(NlaStrip *strip, bool skip_transitions)
static float nlastrip_get_frame_transition(NlaStrip *strip, float cframe, short mode)
NlaStrip * BKE_nlastrip_new(bAction *act, ID &animated_id)
NlaStrip * BKE_nlastrip_find_by_name(NlaTrack *nlt, const char *name)
void BKE_nlatrack_remove_strip(NlaTrack *track, NlaStrip *strip)
void BKE_nlastrips_add_strip_unsafe(ListBase *strips, NlaStrip *strip)
static void blend_data_read_nla_strips(BlendDataReader *reader, ListBase *strips)
void BKE_nlastrip_recalculate_bounds(NlaStrip *strip)
NlaTrack * BKE_nlatrack_new_after(ListBase *nla_tracks, NlaTrack *prev, bool is_liboverride)
bool BKE_nlastrips_has_space(ListBase *strips, float start, float end)
void BKE_nla_tweakmode_exit(const OwnedAnimData owned_adt)
bool BKE_nlastrips_add_strip(ListBase *strips, NlaStrip *strip)
NlaTrack * BKE_nlatrack_new_tail(ListBase *nla_tracks, const bool is_liboverride)
bool BKE_nlastrip_has_curves_for_property(const PointerRNA *ptr, const PropertyRNA *prop)
static NlaStrip * nlastrip_find_by_name(ListBase *strips, const char *name)
void BKE_nlastrip_remove(ListBase *strips, NlaStrip *strip)
void BKE_nlastrip_recalculate_blend(NlaStrip *strip)
NlaStrip * BKE_nlastrip_new_for_slot(bAction *act, blender::animrig::slot_handle_t slot_handle, ID &animated_id)
NlaTrack * BKE_nlatrack_find_tweaked(AnimData *adt)
void BKE_nlastrip_set_active(AnimData *adt, NlaStrip *strip)
void BKE_nlatrack_insert_after(ListBase *nla_tracks, NlaTrack *prev, NlaTrack *new_track, const bool is_liboverride)
#define STASH_TRACK_NAME
NlaTrack * BKE_nlatrack_new_head(ListBase *nla_tracks, bool is_liboverride)
static void nla_tweakmode_exit_sync_strip_lengths(AnimData *adt)
float BKE_nla_clip_length_get_nonzero(const NlaStrip *strip)
void BKE_nla_blend_write(BlendWriter *writer, ListBase *tracks)
bool BKE_nla_action_stash(const OwnedAnimData owned_adt, const bool is_liboverride)
void BKE_nla_tracks_free(ListBase *tracks, bool do_id_user)
bool BKE_nlatrack_has_space(NlaTrack *nlt, float start, float end)
void BKE_nlatrack_sort_strips(NlaTrack *nlt)
NlaStrip * BKE_nlastrip_find_active(NlaTrack *nlt)
void BKE_nla_liboverride_post_process(ID *id, AnimData *adt)
NlaStrip * BKE_nla_add_soundstrip(Main *bmain, Scene *scene, Speaker *speaker)
bool BKE_nla_action_slot_is_stashed(AnimData *adt, bAction *act, const blender::animrig::slot_handle_t slot_handle)
static void BKE_nlastrip_validate_autoblends(NlaTrack *nlt, NlaStrip *nls)
NlaStrip * BKE_nlastrip_copy(Main *bmain, NlaStrip *strip, const bool use_same_action, const int flag)
NlaTrack * BKE_nlatrack_find_active(ListBase *tracks)
void BKE_nlatrack_set_active(ListBase *tracks, NlaTrack *nlt_a)
static void nlastrip_fix_resize_overlaps(NlaStrip *strip)
NlaTrack * BKE_nlatrack_copy(Main *bmain, NlaTrack *nlt, const bool use_same_actions, const int flag)
void BKE_nlatrack_insert_before(ListBase *nla_tracks, NlaTrack *next, NlaTrack *new_track, bool is_liboverride)
bool BKE_nlatrack_is_nonlocal_in_liboverride(const ID *id, const NlaTrack *nlt)
float BKE_nlastrip_compute_frame_from_previous_strip(NlaStrip *strip)
bool BKE_nlastrip_within_bounds(NlaStrip *strip, float min, float max)
static void update_active_strip(AnimData *adt_dest, NlaTrack *track_dest, const AnimData *adt_source, const NlaTrack *track_source)
NlaStrip * BKE_nlastack_add_strip(const OwnedAnimData owned_adt, const bool is_liboverride)
void BKE_nla_tweakmode_clear_flags(AnimData *adt)
bool BKE_nlatrack_add_strip(NlaTrack *nlt, NlaStrip *strip, const bool is_liboverride)
void BKE_nlatrack_remove(ListBase *tracks, NlaTrack *nlt)
void BKE_nlastrip_validate_name(AnimData *adt, NlaStrip *strip)
bool BKE_nlameta_add_strip(NlaStrip *mstrip, NlaStrip *strip)
bool BKE_nlatrack_has_strips(ListBase *tracks)
void BKE_nla_action_pushdown(const OwnedAnimData owned_adt, const bool is_liboverride)
bool BKE_nlatracks_have_animated_strips(ListBase *tracks)
void BKE_nlastrip_free(NlaStrip *strip, const bool do_id_user)
static bool visit_strip(NlaStrip *strip, blender::FunctionRef< bool(NlaStrip *)> callback)
void BKE_nlatrack_remove_and_free(ListBase *tracks, NlaTrack *nlt, bool do_id_user)
void BKE_nlastrip_validate_fcurves(NlaStrip *strip)
bool BKE_nlatrack_is_enabled(const AnimData &adt, const NlaTrack &nlt)
NlaTrack * BKE_nlatrack_new_before(ListBase *nla_tracks, NlaTrack *next, bool is_liboverride)
void BKE_nla_blend_read_data(BlendDataReader *reader, ID *id_owner, ListBase *tracks)
void BKE_nla_strip_foreach_id(NlaStrip *strip, LibraryForeachIDData *data)
void BKE_nlastrips_make_metas(ListBase *strips, bool is_temp)
static void nlastrip_set_initial_length(NlaStrip *strip)
BMesh const char void * data
constexpr const char * c_str() const
bool is_cyclic() const ATTR_WARN_UNUSED_RESULT
Slot * slot_for_handle(slot_handle_t handle)
void users_add(ID &animated_id)
static constexpr slot_handle_t unassigned
nullptr float
#define fmodf(x, y)
#define offsetof(t, d)
#define printf(...)
#define ceil
float length(VecOp< float, D >) RET
#define LOG(level)
Definition log.h:97
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong * next
#define G(x, y, z)
ActionSlotAssignmentResult assign_action_slot_handle(NlaStrip &strip, slot_handle_t slot_handle, ID &animated_id)
bool assign_action(NlaStrip &strip, Action &action, ID &animated_id)
ActionSlotAssignmentResult assign_tmpaction_and_slot_handle(bAction *action, slot_handle_t slot_handle, OwnedAnimData owned_adt)
bool generic_assign_action(ID &animated_id, bAction *action_to_assign, bAction *&action_ptr_ref, slot_handle_t &slot_handle_ref, char *slot_identifier)
ActionSlotAssignmentResult assign_action_and_slot(Action *action, Slot *slot_to_assign, ID &animated_id)
decltype(::ActionSlot::handle) slot_handle_t
bool unassign_action(ID &animated_id)
bool foreach_strip(ID *id, blender::FunctionRef< bool(NlaStrip *)> callback)
bool foreach_strip_adt(const AnimData &adt, blender::FunctionRef< bool(NlaStrip *)> callback)
const char * name
#define floorf
#define fabsf
#define ceilf
PropertyRNA * RNA_struct_type_find_property(StructRNA *srna, const char *identifier)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
#define min(a, b)
Definition sort.cc:36
char identifier[258]
char tmp_last_slot_identifier[258]
bAction * action
short act_blendmode
NlaStrip * actstrip
float act_influence
int32_t slot_handle
NlaTrack * act_track
int32_t tmp_slot_handle
bAction * tmpact
short act_extendmode
ListBase nla_tracks
char last_slot_identifier[258]
float vec[3][3]
char * rna_path
BezTriple * bezt
unsigned int totvert
char auto_smoothing
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
void * last
void * first
struct NlaStrip * next
short blendmode
ListBase fcurves
char name[64]
float influence
ListBase strips
int32_t action_slot_handle
ListBase modifiers
struct NlaStrip * prev
short extendmode
bAction * act
ListBase strips
struct NlaTrack * next
char name[64]
struct NlaTrack * prev
AnimData & adt
float length
Definition BKE_sound.h:79
struct bSound * sound
max
Definition text_draw.cc:251
ListBase tracks
Definition tracking.cc:71
PointerRNA * ptr
Definition wm_files.cc:4238
uint8_t flag
Definition wm_window.cc:145