Blender V4.3
transform_convert_nla.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cstdio>
10
11#include "DNA_anim_types.h"
12#include "DNA_space_types.h"
13
14#include "MEM_guardedalloc.h"
15
16#include "BLI_listbase.h"
17#include "BLI_math_matrix.h"
18#include "BLI_math_vector.h"
19
20#include "BKE_anim_data.hh"
21#include "BKE_context.hh"
22#include "BKE_nla.hh"
23
24#include "ED_anim_api.hh"
25
26#include "WM_api.hh"
27#include "WM_types.hh"
28
29#include "RNA_access.hh"
30#include "RNA_prototypes.hh"
31
32#include "transform.hh"
33#include "transform_convert.hh"
34
39
44
47
48 /* Dummy values for transform to write in - must have 3 elements. */
50 float h1[3];
52 float h2[3];
53
56
62
64 int handle;
65};
66
67static bool is_overlap(const float left_bound_a,
68 const float right_bound_a,
69 const float left_bound_b,
70 const float right_bound_b)
71{
72 return (left_bound_a < right_bound_b) && (right_bound_a > left_bound_b);
73}
74
75static bool nlastrip_is_overlap(const NlaStrip *strip_a,
76 const float offset_a,
77 const NlaStrip *strip_b,
78 const float offset_b)
79{
80 return is_overlap(strip_a->start + offset_a,
81 strip_a->end + offset_a,
82 strip_b->start + offset_b,
83 strip_b->end + offset_b);
84}
85
93static float transdata_get_time_shuffle_offset_side(ListBase *trans_datas, const bool shuffle_left)
94{
95 float total_offset = 0;
96
97 float offset;
98 do {
99 offset = 0;
100
101 LISTBASE_FOREACH (LinkData *, link, trans_datas) {
102 TransDataNla *trans_data = (TransDataNla *)link->data;
103 NlaStrip *xformed_strip = trans_data->strip;
104
105 LISTBASE_FOREACH (NlaStrip *, non_xformed_strip, &trans_data->nlt->strips) {
106
107 if (non_xformed_strip->flag & NLASTRIP_FLAG_INVALID_LOCATION) {
108 continue;
109 }
110
111 /* Allow overlap with transitions. */
112 if (non_xformed_strip->type == NLASTRIP_TYPE_TRANSITION) {
113 continue;
114 }
115
116 if (!nlastrip_is_overlap(non_xformed_strip, 0, xformed_strip, total_offset)) {
117 continue;
118 }
119
120 offset = shuffle_left ?
121 fmin(offset, non_xformed_strip->start - (xformed_strip->end + total_offset)) :
122 fmax(offset, non_xformed_strip->end - (xformed_strip->start + total_offset));
123 }
124 }
125
126 total_offset += offset;
127 } while (!IS_EQT(offset, 0.0f, 1e-4));
128 /* Needs a epsilon greater than FLT_EPS because strip->start/end could be non-integral,
129 * and after those calculations, `offset` could fall outside of FLT_EPS. */
130
131 return total_offset;
132}
133
142{
143 const float offset_left = transdata_get_time_shuffle_offset_side(trans_datas, true);
144 const float offset_right = transdata_get_time_shuffle_offset_side(trans_datas, false);
145 BLI_assert(offset_left <= 0);
146 BLI_assert(offset_right >= 0);
147
148 return -offset_left < offset_right ? offset_left : offset_right;
149}
150
164 const int shuffle_direction,
165 int *r_total_offset)
166{
167 *r_total_offset = 0;
168 if (BLI_listbase_is_empty(trans_datas)) {
169 return false;
170 }
171
172 LinkData *first_link = static_cast<LinkData *>(trans_datas->first);
173 TransDataNla *first_transdata = static_cast<TransDataNla *>(first_link->data);
174 AnimData *adt = BKE_animdata_from_id(first_transdata->id);
175 ListBase *tracks = &adt->nla_tracks;
176
177 int offset;
178 do {
179 offset = 0;
180
181 LISTBASE_FOREACH (LinkData *, link, trans_datas) {
182 TransDataNla *trans_data = (TransDataNla *)link->data;
183
184 NlaTrack *dst_track = static_cast<NlaTrack *>(
185 BLI_findlink(tracks, trans_data->trackIndex + *r_total_offset));
186
187 /* Cannot keep moving strip in given track direction. No solution. */
188 if (dst_track == nullptr) {
189 return false;
190 }
191
192 /* Shuffle only if track is locked or library override. */
193 if (((dst_track->flag & NLATRACK_PROTECTED) == 0) &&
194 !BKE_nlatrack_is_nonlocal_in_liboverride(trans_data->id, dst_track))
195 {
196 continue;
197 }
198
199 offset = shuffle_direction;
200 break;
201 }
202
203 *r_total_offset += offset;
204 } while (offset != 0);
205
206 return true;
207}
208
218static bool transdata_get_track_shuffle_offset(ListBase *trans_datas, int *r_track_offset)
219{
220 int offset_down = 0;
221 const bool down_valid = transdata_get_track_shuffle_offset_side(trans_datas, 1, &offset_down);
222
223 int offset_up = 0;
224 const bool up_valid = transdata_get_track_shuffle_offset_side(trans_datas, -1, &offset_up);
225
226 if (down_valid && up_valid) {
227 if (offset_down < abs(offset_up)) {
228 *r_track_offset = offset_down;
229 }
230 else {
231 *r_track_offset = offset_up;
232 }
233 }
234 else if (down_valid) {
235 *r_track_offset = offset_down;
236 }
237 else if (up_valid) {
238 *r_track_offset = offset_up;
239 }
240
241 return down_valid || up_valid;
242}
243
244/* -------------------------------------------------------------------- */
249{
251 ListBase anim_data = {nullptr, nullptr};
253 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
254
255 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
256 if (!ale->adt) {
257 continue;
258 }
259 ListBase *nla_tracks = &ale->adt->nla_tracks;
260
262 LISTBASE_FOREACH_BACKWARD_MUTABLE (NlaTrack *, track, nla_tracks) {
263 if (!(track->flag & NLATRACK_TEMPORARILY_ADDED)) {
264 break;
265 }
266 if (track->strips.first != nullptr) {
267 break;
268 }
269 BKE_nlatrack_remove_and_free(nla_tracks, track, true);
270 }
271
273 LISTBASE_FOREACH_MUTABLE (NlaTrack *, track, nla_tracks) {
274 /* Library override tracks are the first N tracks. They're never temporary and determine
275 * where we start removing temporaries. */
276 if ((track->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0) {
277 continue;
278 }
279 if (!(track->flag & NLATRACK_TEMPORARILY_ADDED)) {
280 break;
281 }
282 if (track->strips.first != nullptr) {
283 break;
284 }
285 BKE_nlatrack_remove_and_free(nla_tracks, track, true);
286 }
287
289 LISTBASE_FOREACH_MUTABLE (NlaTrack *, track, nla_tracks) {
290 track->flag &= ~NLATRACK_TEMPORARILY_ADDED;
291 }
292 }
293
294 ANIM_animdata_freelist(&anim_data);
295}
296
299/* -------------------------------------------------------------------- */
308static void applyTransformNLA_translation(PointerRNA *strip_rna_ptr, const TransDataNla *transdata)
309{
310 /* NOTE: we write these twice to avoid truncation errors which can arise when
311 * moving the strips a large distance using numeric input #33852.
312 */
313 RNA_float_set(strip_rna_ptr, "frame_start", transdata->h1[0]);
314 RNA_float_set(strip_rna_ptr, "frame_end", transdata->h2[0]);
315
316 RNA_float_set(strip_rna_ptr, "frame_start", transdata->h1[0]);
317 RNA_float_set(strip_rna_ptr, "frame_end", transdata->h2[0]);
318}
319
320static void applyTransformNLA_timeScale(PointerRNA *strip_rna_ptr, const float value)
321{
322 RNA_float_set(strip_rna_ptr, "scale", value);
323}
324
327{
328 while (strip->prev != nullptr && tdn->h1[0] < strip->prev->start) {
329 BLI_listbase_swaplinks(&tdn->nlt->strips, strip, strip->prev);
330 }
331 while (strip->next != nullptr && tdn->h1[0] > strip->next->start) {
332 BLI_listbase_swaplinks(&tdn->nlt->strips, strip, strip->next);
333 }
334}
335
343{
344
345 NlaStrip *adj_strip = strip->prev;
346 if (adj_strip != nullptr && !(adj_strip->flag & NLASTRIP_FLAG_SELECT) &&
347 nlastrip_is_overlap(strip, 0, adj_strip, 0))
348 {
350 }
351 adj_strip = strip->next;
352 if (adj_strip != nullptr && !(adj_strip->flag & NLASTRIP_FLAG_SELECT) &&
353 nlastrip_is_overlap(strip, 0, adj_strip, 0))
354 {
356 }
357}
358
364{
365 /* Firstly, check if the proposed transform locations would overlap with any neighboring
366 * strips (barring transitions) which are absolute barriers since they are not being moved.
367 *
368 * This is done as a iterative procedure (done 5 times max for now). */
369 short iter_max = 4;
370 NlaStrip *prev = BKE_nlastrip_prev_in_track(strip, true);
372
373 for (short iter = 0; iter <= iter_max; iter++) {
374 const bool p_exceeded = (prev != nullptr) && (tdn->h1[0] < prev->end);
375 const bool n_exceeded = (next != nullptr) && (tdn->h2[0] > next->start);
376
377 if ((p_exceeded && n_exceeded) || (iter == iter_max)) {
378 /* Both endpoints exceeded (or iteration ping-pong'd meaning that we need a compromise).
379 * - Simply crop strip to fit within the bounds of the strips bounding it.
380 * - If there were no neighbors, clear the transforms
381 * (make it default to the strip's current values).
382 */
383 if (prev && next) {
384 tdn->h1[0] = prev->end;
385 tdn->h2[0] = next->start;
386 }
387 else {
388 tdn->h1[0] = strip->start;
389 tdn->h2[0] = strip->end;
390 }
391 }
392 else if (n_exceeded) {
393 /* Move backwards. */
394 float offset = tdn->h2[0] - next->start;
395
396 tdn->h1[0] -= offset;
397 tdn->h2[0] -= offset;
398 }
399 else if (p_exceeded) {
400 /* More forwards. */
401 float offset = prev->end - tdn->h1[0];
402
403 tdn->h1[0] += offset;
404 tdn->h2[0] += offset;
405 }
406 else { /* All is fine and well. */
407 break;
408 }
409 }
410
411 /* Use RNA to write the values to ensure that constraints on these are obeyed
412 * (e.g. for transition strips, the values are taken from the neighbors). */
413 PointerRNA strip_ptr = RNA_pointer_create(nullptr, &RNA_NlaStrip, strip);
414
415 switch (t->mode) {
416 case TFM_TIME_EXTEND:
417 case TFM_TIME_SCALE: {
418 /* The final scale is the product of the original strip scale (from before the
419 * transform operation started) and the current scale value of this transform operation. */
420 const float originalStripScale = tdn->h1[2];
421 const float newStripScale = originalStripScale * t->values_final[0];
422 applyTransformNLA_timeScale(&strip_ptr, newStripScale);
423 applyTransformNLA_translation(&strip_ptr, tdn);
424 break;
425 }
426 case TFM_TRANSLATION:
427 applyTransformNLA_translation(&strip_ptr, tdn);
428 break;
429 default:
430 printf("recalcData_nla: unsupported NLA transformation mode %d\n", t->mode);
431 break;
432 }
433}
434
437/* -------------------------------------------------------------------- */
442{
443 Scene *scene = t->scene;
444 SpaceNla *snla = nullptr;
445 TransData *td = nullptr;
446 TransDataNla *tdn = nullptr;
447
448 bAnimContext ac;
449 ListBase anim_data = {nullptr, nullptr};
450 int filter;
451
452 int count = 0;
453
455
456 /* Determine what type of data we are operating on. */
457 if (ANIM_animdata_get_context(C, &ac) == 0) {
458 return;
459 }
460 snla = (SpaceNla *)ac.sl;
461
462 /* Filter data. */
466 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
467
468 /* Which side of the current frame should be allowed. */
469 if (t->mode == TFM_TIME_EXTEND) {
470 t->frame_side = transform_convert_frame_side_dir_get(t, float(scene->r.cfra));
471 }
472 else {
473 /* Normal transform - both sides of current frame are considered. */
474 t->frame_side = 'B';
475 }
476
477 /* Loop 1: count how many strips are selected (consider each strip as 2 points). */
478 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
479 NlaTrack *nlt = (NlaTrack *)ale->data;
480
481 /* Make some meta-strips for chains of selected strips. */
482 BKE_nlastrips_make_metas(&nlt->strips, true);
483
484 /* Only consider selected strips. */
485 LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
486 /* TODO: we can make strips have handles later on. */
487 /* Transition strips can't get directly transformed. */
488 if (strip->type == NLASTRIP_TYPE_TRANSITION) {
489 continue;
490 }
491 if ((strip->flag & NLASTRIP_FLAG_SELECT) == 0) {
492 continue;
493 }
494 if (FrameOnMouseSide(t->frame_side, strip->start, float(scene->r.cfra))) {
495 count++;
496 }
497 if (FrameOnMouseSide(t->frame_side, strip->end, float(scene->r.cfra))) {
498 count++;
499 }
500 }
501 }
502
503 /* Stop if trying to build list if nothing selected. */
504 if (count == 0) {
505 /* Clear temp metas that may have been created but aren't needed now
506 * because they fell on the wrong side of `scene->r.cfra`. */
507 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
508 NlaTrack *nlt = (NlaTrack *)ale->data;
509 BKE_nlastrips_clear_metas(&nlt->strips, false, true);
510 }
511
512 /* Cleanup temp list. */
513 ANIM_animdata_freelist(&anim_data);
514 return;
515 }
516
517 /* Allocate memory for data. */
518 tc->data_len = count;
519
520 tc->data = static_cast<TransData *>(
521 MEM_callocN(tc->data_len * sizeof(TransData), "TransData(NLA Editor)"));
522 td = tc->data;
523 tc->custom.type.data = tdn = static_cast<TransDataNla *>(
524 MEM_callocN(tc->data_len * sizeof(TransDataNla), "TransDataNla (NLA Editor)"));
525 tc->custom.type.use_free = true;
526
527 /* Loop 2: build transdata array. */
528 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
529 /* Only if a real NLA-track. */
530 if (ale->type == ANIMTYPE_NLATRACK) {
531 AnimData *adt = ale->adt;
532 NlaTrack *nlt = (NlaTrack *)ale->data;
533
534 /* Only consider selected strips. */
535 LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
536 /* TODO: we can make strips have handles later on. */
537 /* Transition strips can't get directly transformed. */
538 if (strip->type == NLASTRIP_TYPE_TRANSITION) {
539 continue;
540 }
541 if ((strip->flag & NLASTRIP_FLAG_SELECT) == 0) {
542 continue;
543 }
544
545 /* Our transform data is constructed as follows:
546 * - Only the handles on the right side of the current-frame get included.
547 * - `td` structs are transform-elements operated on by the transform system and
548 * represent a single handle. The storage/pointer used (`val` or `loc`) depends
549 * on whether we're scaling or transforming. Ultimately though, the handles the `td`
550 * writes to will simply be a dummy in `tdn`.
551 * - For each strip being transformed, a single `tdn` struct is used, so in some
552 * cases, there will need to be 1 of these `tdn` elements in the array skipped.
553 */
554 float center[3], yval;
555
556 /* Firstly, initialize `tdn` settings. */
557 tdn->id = ale->id;
558 tdn->oldTrack = tdn->nlt = nlt;
559 tdn->strip = strip;
560 tdn->trackIndex = BLI_findindex(&adt->nla_tracks, nlt);
561 tdn->signed_track_index = tdn->trackIndex;
562
563 yval = float(tdn->trackIndex * NLATRACK_STEP(snla));
564
565 tdn->h1[0] = strip->start;
566 tdn->h1[1] = yval;
567 tdn->h2[0] = strip->end;
568 tdn->h2[1] = yval;
569 tdn->h1[2] = tdn->h2[2] = strip->scale;
570
571 center[0] = float(scene->r.cfra);
572 center[1] = yval;
573 center[2] = 0.0f;
574
575 /* Set td's based on which handles are applicable. */
576 if (FrameOnMouseSide(t->frame_side, strip->start, float(scene->r.cfra))) {
577 /* Just set `tdn` to assume that it only has one handle for now. */
578 tdn->handle = -1;
579
580 /* Now, link the transform data up to this data. */
581 td->loc = tdn->h1;
582 copy_v3_v3(td->iloc, tdn->h1);
583
585 /* Store all the other gunk that is required by transform. */
586 copy_v3_v3(td->center, center);
587 td->axismtx[2][2] = 1.0f;
588 td->flag |= TD_SELECTED;
589 unit_m3(td->mtx);
590 unit_m3(td->smtx);
591 }
592
593 td->extra = tdn;
594 td++;
595 }
596 if (FrameOnMouseSide(t->frame_side, strip->end, float(scene->r.cfra))) {
597 /* If `tdn` is already holding the start handle,
598 * then we're doing both, otherwise, only end. */
599 tdn->handle = (tdn->handle) ? 2 : 1;
600
601 /* Now, link the transform data up to this data. */
602 td->loc = tdn->h2;
603 copy_v3_v3(td->iloc, tdn->h2);
604
606 /* Store all the other gunk that is required by transform. */
607 copy_v3_v3(td->center, center);
608 td->axismtx[2][2] = 1.0f;
609 td->flag |= TD_SELECTED;
610 unit_m3(td->mtx);
611 unit_m3(td->smtx);
612 }
613
614 td->extra = tdn;
615 td++;
616 }
617
618 /* If both handles were used, skip the next tdn (i.e. leave it blank)
619 * since the counting code is dumb.
620 * Otherwise, just advance to the next one.
621 */
622 if (tdn->handle == 2) {
623 tdn += 2;
624 }
625 else if (tdn->handle) {
626 tdn++;
627 }
628 }
629 }
630 }
631
632 BLI_assert(tdn <= (((TransDataNla *)tc->custom.type.data) + tc->data_len));
633
634 /* Cleanup temp list. */
635 ANIM_animdata_freelist(&anim_data);
636}
637
639{
640 SpaceNla *snla = (SpaceNla *)t->area->spacedata.first;
641
643
644 /* For each strip we've got, perform some additional validation of the values
645 * that got set before using RNA to set the value (which does some special
646 * operations when setting these values to make sure that everything works ok).
647 */
648 TransDataNla *tdn = static_cast<TransDataNla *>(tc->custom.type.data);
649 for (int i = 0; i < tc->data_len; i++, tdn++) {
650 NlaStrip *strip = tdn->strip;
651 int delta_y1, delta_y2;
652
653 /* If this tdn has no handles, that means it is just a dummy that should be skipped. */
654 if (tdn->handle == 0) {
655 continue;
656 }
657 strip->flag &= ~NLASTRIP_FLAG_INVALID_LOCATION;
658
659 /* Set refresh tags for objects using this animation,
660 * BUT only if realtime updates are enabled. */
661
662 if ((snla->flag & SNLA_NOREALTIMEUPDATES) == 0) {
664 }
665
666 /* If canceling transform, just write the values without validating, then move on. */
667 if (t->state == TRANS_CANCEL) {
668 /* Clear the values by directly overwriting the originals, but also need to restore
669 * endpoints of neighboring transition-strips. */
670
671 /* Start. */
672 strip->start = tdn->h1[0];
673
674 if ((strip->prev) && (strip->prev->type == NLASTRIP_TYPE_TRANSITION)) {
675 strip->prev->end = tdn->h1[0];
676 }
677
678 /* End. */
679 strip->end = tdn->h2[0];
680
681 if ((strip->next) && (strip->next->type == NLASTRIP_TYPE_TRANSITION)) {
682 strip->next->start = tdn->h2[0];
683 }
684
685 strip->scale = tdn->h1[2];
686
687 /* Flush transforms to child strips (since this should be a meta). */
689
690 /* Restore to original track (if needed). */
691 if (tdn->oldTrack != tdn->nlt) {
692 /* Just append to end of list for now,
693 * since strips get sorted in special_aftertrans_update(). */
694 BLI_remlink(&tdn->nlt->strips, strip);
695 BLI_addtail(&tdn->oldTrack->strips, strip);
696 }
697
698 continue;
699 }
700
701 const bool nlatrack_isliboverride = BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, tdn->nlt);
702 const bool allow_overlap = !nlatrack_isliboverride && ELEM(t->mode, TFM_TRANSLATION);
703
704 if (allow_overlap) {
705 nlastrip_overlap_reorder(tdn, strip);
706
707 /* Directly flush. */
708 strip->start = tdn->h1[0];
709 strip->end = tdn->h2[0];
710 }
711 else {
712 nlastrip_fix_overlapping(t, tdn, strip);
713 }
714
715 /* Flush transforms to child strips (since this should be a meta). */
717
718 /* Now, check if we need to try and move track:
719 * - we need to calculate both,
720 * as only one may have been altered by transform if only 1 handle moved.
721 */
722 /* In LibOverride case, we cannot move strips across tracks that come from the linked data.
723 */
724 const bool is_liboverride = ID_IS_OVERRIDE_LIBRARY(tdn->id);
725 if (nlatrack_isliboverride) {
726 continue;
727 }
728
729 delta_y1 = (int(tdn->h1[1]) / NLATRACK_STEP(snla) - tdn->signed_track_index);
730 delta_y2 = (int(tdn->h2[1]) / NLATRACK_STEP(snla) - tdn->signed_track_index);
731
732 /* Move strip into track in the requested direction. */
733 /* If we cannot find the strip in the track, this strip has moved tracks already (if multiple
734 * strips using the same action from equal IDs such as meshes or shape-keys are selected)
735 * so can be skipped. */
736 if ((delta_y1 || delta_y2) && BLI_findindex(&tdn->nlt->strips, strip) != -1) {
737 int delta = (delta_y2) ? delta_y2 : delta_y1;
738
739 AnimData *anim_data = BKE_animdata_from_id(tdn->id);
740 ListBase *nla_tracks = &anim_data->nla_tracks;
741
742 NlaTrack *old_track = tdn->nlt;
743 NlaTrack *dst_track = nullptr;
744
745 /* Calculate the total new tracks needed
746 *
747 * Determine dst_track, which will end up being nullptr, the last library override
748 * track, or a normal local track. The first two cases lead to delta_new_tracks!=0.
749 * The last case leads to `delta_new_tracks == 0`. */
750 int delta_new_tracks = delta;
751
752 /* It's possible to drag a strip fast enough to make delta > |1|. We only want to process
753 * 1 track shift at a time. */
754
755 CLAMP(delta_new_tracks, -1, 1);
756 dst_track = old_track;
757
758 while (delta_new_tracks < 0) {
759 dst_track = dst_track->prev;
760 delta_new_tracks++;
761 }
762
763 /* We assume all library tracks are grouped at the bottom of the nla stack.
764 * Thus, no need to check for them when moving tracks upward. */
765 while (delta_new_tracks > 0) {
766 dst_track = dst_track->next;
767 delta_new_tracks--;
768 }
769
770 for (int j = 0; j < -delta_new_tracks; j++) {
771 NlaTrack *new_track = BKE_nlatrack_new();
772 new_track->flag |= NLATRACK_TEMPORARILY_ADDED;
774 nla_tracks, (NlaTrack *)nla_tracks->first, new_track, is_liboverride);
775 dst_track = new_track;
776 }
777
778 for (int j = 0; j < delta_new_tracks; j++) {
779 NlaTrack *new_track = BKE_nlatrack_new();
780 new_track->flag |= NLATRACK_TEMPORARILY_ADDED;
781
783 nla_tracks, (NlaTrack *)nla_tracks->last, new_track, is_liboverride);
784 dst_track = new_track;
785 }
786
787 /* If the destination track is null, then we need to go to the last track. */
788 if (dst_track == nullptr) {
789 dst_track = old_track;
790 }
791
792 /* Move strip from old_track to dst_track. */
793 if (dst_track != old_track) {
794 BKE_nlatrack_remove_strip(old_track, strip);
795 BKE_nlastrips_add_strip_unsafe(&dst_track->strips, strip);
796
797 tdn->nlt = dst_track;
798 tdn->signed_track_index += delta;
799 tdn->trackIndex = BLI_findindex(nla_tracks, dst_track);
800 }
801
802 /* Ensure we set the target track as active. */
803 BKE_nlatrack_set_active(nla_tracks, dst_track);
804
805 if (tdn->nlt->flag & NLATRACK_PROTECTED) {
807 }
808 }
809
811 }
812}
813
816/* -------------------------------------------------------------------- */
826
829{
830 /* Element: #IDGroupedTransData. */
831 ListBase grouped_trans_datas = {nullptr, nullptr};
832
833 /* Flag all non-library-override transformed strips so we can distinguish them when shuffling.
834 *
835 * Group trans_datas by ID so shuffling is unique per ID. */
836 {
837 TransDataNla *tdn = first_trans_data;
838 for (int i = 0; i < tc->data_len; i++, tdn++) {
839
840 /* Skip dummy handles. */
841 if (tdn->handle == 0) {
842 continue;
843 }
844
845 /* For strips within library override tracks, don't do any shuffling at all. Unsure how
846 * library overrides should behave so, for now, they're treated as mostly immutable. */
847 if ((tdn->nlt->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0) {
848 continue;
849 }
850
852
853 IDGroupedTransData *dst_group = nullptr;
854 /* Find dst_group with matching ID. */
855 LISTBASE_FOREACH (IDGroupedTransData *, group, &grouped_trans_datas) {
856 if (group->id == tdn->id) {
857 dst_group = group;
858 break;
859 }
860 }
861 if (dst_group == nullptr) {
862 dst_group = static_cast<IDGroupedTransData *>(
863 MEM_callocN(sizeof(IDGroupedTransData), __func__));
864 dst_group->id = tdn->id;
865 BLI_addhead(&grouped_trans_datas, dst_group);
866 }
867
868 BLI_addtail(&dst_group->trans_datas, BLI_genericNodeN(tdn));
869 }
870 }
871
872 /* Apply shuffling. */
873 LISTBASE_FOREACH (IDGroupedTransData *, group, &grouped_trans_datas) {
874 ListBase *trans_datas = &group->trans_datas;
875
876 /* Apply vertical shuffle. */
877 int minimum_track_offset = 0;
878 transdata_get_track_shuffle_offset(trans_datas, &minimum_track_offset);
879 if (minimum_track_offset != 0) {
880 ListBase *tracks = &BKE_animdata_from_id(group->id)->nla_tracks;
881
882 LISTBASE_FOREACH (LinkData *, link, trans_datas) {
883 TransDataNla *trans_data = (TransDataNla *)link->data;
884 NlaTrack *dst_track = static_cast<NlaTrack *>(
885 BLI_findlink(tracks, trans_data->trackIndex + minimum_track_offset));
886
887 NlaStrip *strip = trans_data->strip;
888 if ((dst_track->flag & NLATRACK_PROTECTED) != 0) {
889
890 BKE_nlatrack_remove_strip(trans_data->nlt, strip);
891 BKE_nlatrack_add_strip(dst_track, strip, false);
892
893 trans_data->nlt = dst_track;
894 }
895 else {
896 /* If destination track is locked, we need revert strip to source track. */
897 printf("Cannot moved. Target track '%s' is locked. \n", trans_data->nlt->name);
898 int old_track_index = BLI_findindex(tracks, trans_data->oldTrack);
899 NlaTrack *old_track = static_cast<NlaTrack *>(BLI_findlink(tracks, old_track_index));
900
901 BKE_nlatrack_remove_strip(trans_data->nlt, strip);
902 BKE_nlastrips_add_strip_unsafe(&old_track->strips, strip);
903
904 trans_data->nlt = old_track;
905 }
906 }
907 }
908
909 /* Apply horizontal shuffle. */
910 const float minimum_time_offset = transdata_get_time_shuffle_offset(trans_datas);
911 LISTBASE_FOREACH (LinkData *, link, trans_datas) {
912 TransDataNla *trans_data = (TransDataNla *)link->data;
913 NlaStrip *strip = trans_data->strip;
914
915 strip->start += minimum_time_offset;
916 strip->end += minimum_time_offset;
918 }
919 }
920
921 /* Memory cleanup. */
922 LISTBASE_FOREACH (IDGroupedTransData *, group, &grouped_trans_datas) {
923 BLI_freelistN(&group->trans_datas);
924 }
925 BLI_freelistN(&grouped_trans_datas);
926}
927
929{
930 bAnimContext ac;
931
932 /* Initialize relevant anim-context 'context' data. */
933 if (ANIM_animdata_get_context(C, &ac) == 0) {
934 return;
935 }
936
937 if (!ac.datatype) {
938 return;
939 }
940
942 TransDataNla *first_trans_data = static_cast<TransDataNla *>(tc->custom.type.data);
943
944 /* Shuffle transformed strips. */
945 if (ELEM(t->mode, TFM_TRANSLATION) && t->state != TRANS_CANCEL) {
946 nlastrip_shuffle_transformed(tc, first_trans_data);
947 }
948
949 /* Clear NLASTRIP_FLAG_INVALID_LOCATION flag. */
950 TransDataNla *tdn = first_trans_data;
951 for (int i = 0; i < tc->data_len; i++, tdn++) {
952 if (tdn->strip == nullptr) {
953 continue;
954 }
955
956 tdn->strip->flag &= ~NLASTRIP_FLAG_INVALID_LOCATION;
957 }
958
959 ListBase anim_data = {nullptr, nullptr};
961
962 /* Get tracks to work on. */
964 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
965
966 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
967 NlaTrack *nlt = (NlaTrack *)ale->data;
968
969 /* Make sure strips are in order again. */
971
972 /* Remove the temp metas. */
973 BKE_nlastrips_clear_metas(&nlt->strips, false, true);
974 }
975
976 /* General refresh for the outliner because the following might have happened:
977 * - strips moved between tracks
978 * - strips swapped order
979 * - duplicate-move moves to different track. */
981
982 /* Free temp memory. */
983 ANIM_animdata_freelist(&anim_data);
984
985 /* Truncate temporarily added tracks. */
987
988 /* Perform after-transform validation. */
990}
991
995 /*flags*/ (T_POINTS | T_2D_EDIT),
996 /*create_trans_data*/ createTransNlaData,
997 /*recalc_data*/ recalcData_nla,
998 /*special_aftertrans_update*/ special_aftertrans_update__nla,
999};
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:89
Main * CTX_data_main(const bContext *C)
void BKE_nlameta_flush_transforms(NlaStrip *mstrip)
bool BKE_nlatrack_add_strip(NlaTrack *nlt, NlaStrip *strip, bool is_liboverride)
void BKE_nlastrips_clear_metas(ListBase *strips, bool only_sel, bool only_temp)
NlaStrip * BKE_nlastrip_prev_in_track(NlaStrip *strip, bool skip_transitions)
void BKE_nlatrack_insert_after(ListBase *nla_tracks, NlaTrack *prev, NlaTrack *new_track, bool is_liboverride)
void BKE_nlatrack_set_active(ListBase *tracks, NlaTrack *nlt)
NlaStrip * BKE_nlastrip_next_in_track(NlaStrip *strip, bool skip_transitions)
void BKE_nlatrack_remove_strip(NlaTrack *track, NlaStrip *strip)
void BKE_nlastrips_add_strip_unsafe(ListBase *strips, NlaStrip *strip)
struct NlaTrack * BKE_nlatrack_new(void)
void BKE_nlatrack_sort_strips(NlaTrack *nlt)
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)
void BKE_nlatrack_remove_and_free(ListBase *tracks, NlaTrack *nlt, bool do_id_user)
void BKE_nlastrips_make_metas(ListBase *strips, bool is_temp)
#define BLI_assert(a)
Definition BLI_assert.h:50
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)
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
#define LISTBASE_FOREACH_BACKWARD_MUTABLE(type, var, list)
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)
struct LinkData * BLI_genericNodeN(void *data)
Definition listbase.cc:909
void BLI_listbase_swaplinks(struct ListBase *listbase, void *vlinka, void *vlinkb) ATTR_NONNULL(1
void unit_m3(float m[3][3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
#define CLAMP(a, b, c)
#define ELEM(...)
#define IS_EQT(a, b, c)
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:683
@ NLASTRIP_FLAG_INVALID_LOCATION
@ NLASTRIP_FLAG_SELECT
@ NLASTRIP_TYPE_TRANSITION
@ NLATRACK_PROTECTED
@ NLATRACK_TEMPORARILY_ADDED
@ NLATRACK_OVERRIDELIBRARY_LOCAL
@ SNLA_NOREALTIMEUPDATES
@ ANIMTYPE_NLATRACK
void ED_nla_postop_refresh(bAnimContext *ac)
Definition nla_edit.cc:59
#define NLATRACK_STEP(snla)
eAnimCont_Types
eAnimFilter_Flags
@ ANIMFILTER_FOREDIT
@ ANIMFILTER_ANIMDATA
@ ANIMFILTER_DATA_VISIBLE
@ ANIMFILTER_LIST_VISIBLE
@ ANIMFILTER_FCURVESONLY
@ TFM_TIME_SCALE
@ TFM_TIME_EXTEND
@ TFM_TRANSLATION
Read Guarded memory(de)allocation.
#define NC_ANIMATION
Definition WM_types.hh:355
#define NA_ADDED
Definition WM_types.hh:552
#define ND_NLA
Definition WM_types.hh:464
void ANIM_animdata_freelist(ListBase *anim_data)
Definition anim_deps.cc:457
void ANIM_id_update(Main *bmain, ID *id)
Definition anim_deps.cc:102
bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac)
size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode, void *data, const eAnimCont_Types datatype)
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
#define printf
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
DO_INLINE void filter(lfVector *V, fmatrix3x3 *S)
int count
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
static ulong * next
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
ListBase nla_tracks
IDGroupedTransData * next
IDGroupedTransData * prev
Definition DNA_ID.h:413
void * data
void * last
void * first
struct NlaStrip * next
struct NlaStrip * prev
ListBase strips
struct NlaTrack * next
char name[64]
struct NlaTrack * prev
ListBase spacedata
TransCustomData type
Definition transform.hh:425
unsigned int use_free
Definition transform.hh:410
TransCustomDataContainer custom
Definition transform.hh:501
TransData * data
Definition transform.hh:445
float smtx[3][3]
float axismtx[3][3]
float mtx[3][3]
eTfmMode mode
Definition transform.hh:517
char frame_side
Definition transform.hh:570
eTState state
Definition transform.hh:527
Scene * scene
Definition transform.hh:654
float values_final[4]
Definition transform.hh:632
bContext * context
Definition transform.hh:649
ScrArea * area
Definition transform.hh:651
SpaceLink * sl
eAnimCont_Types datatype
@ T_2D_EDIT
Definition transform.hh:104
@ T_POINTS
Definition transform.hh:93
#define TRANS_DATA_CONTAINER_FIRST_SINGLE(t)
Definition transform.hh:851
@ TRANS_CANCEL
Definition transform.hh:210
bool FrameOnMouseSide(char side, float frame, float cframe)
char transform_convert_frame_side_dir_get(TransInfo *t, float cframe)
conversion and adaptation of different datablocks to a common struct.
static bool nlastrip_is_overlap(const NlaStrip *strip_a, const float offset_a, const NlaStrip *strip_b, const float offset_b)
static float transdata_get_time_shuffle_offset_side(ListBase *trans_datas, const bool shuffle_left)
static void applyTransformNLA_translation(PointerRNA *strip_rna_ptr, const TransDataNla *transdata)
Applies a translation to the given NlaStrip.
static void nlastrip_shuffle_transformed(TransDataContainer *tc, TransDataNla *first_trans_data)
static void applyTransformNLA_timeScale(PointerRNA *strip_rna_ptr, const float value)
static bool transdata_get_track_shuffle_offset_side(ListBase *trans_datas, const int shuffle_direction, int *r_total_offset)
static void recalcData_nla(TransInfo *t)
TransConvertTypeInfo TransConvertType_NLA
static void createTransNlaData(bContext *C, TransInfo *t)
static bool is_overlap(const float left_bound_a, const float right_bound_a, const float left_bound_b, const float right_bound_b)
static void nlastrip_overlap_reorder(TransDataNla *tdn, NlaStrip *strip)
static bool transdata_get_track_shuffle_offset(ListBase *trans_datas, int *r_track_offset)
static float transdata_get_time_shuffle_offset(ListBase *trans_datas)
static void nlastrip_fix_overlapping(TransInfo *t, TransDataNla *tdn, NlaStrip *strip)
static void nlatrack_truncate_temporary_tracks(bAnimContext *ac)
static void nlastrip_flag_overlaps(NlaStrip *strip)
static void special_aftertrans_update__nla(bContext *C, TransInfo *t)
@ TD_SELECTED
ccl_device_inline int abs(int x)
Definition util/math.h:120
void WM_event_add_notifier(const bContext *C, uint type, void *reference)