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