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