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