Blender V4.3
action_data.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2015 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cfloat>
10#include <cmath>
11#include <cstdlib>
12#include <cstring>
13
14#include "BLI_utildefines.h"
15
16#include "BLT_translation.hh"
17
18#include "DNA_anim_types.h"
19#include "DNA_key_types.h"
20#include "DNA_object_types.h"
21#include "DNA_scene_types.h"
22
23#include "RNA_access.hh"
24#include "RNA_define.hh"
25#include "RNA_prototypes.hh"
26
27#include "BKE_action.hh"
28#include "BKE_context.hh"
29#include "BKE_key.hh"
30#include "BKE_lib_id.hh"
31#include "BKE_nla.hh"
32#include "BKE_report.hh"
33#include "BKE_scene.hh"
34
35#include "ANIM_action.hh"
36
37#include "ED_anim_api.hh"
38#include "ED_screen.hh"
39
40#include "DEG_depsgraph.hh"
41
42#include "WM_api.hh"
43#include "WM_types.hh"
44
45#include "UI_interface.hh"
46#include "UI_interface_c.hh"
47
48#include "action_intern.hh"
49
50/* -------------------------------------------------------------------- */
55{
56 { /* Support use from the layout.template_action() UI template. */
57 PointerRNA ptr = {nullptr};
58 PropertyRNA *prop = nullptr;
60 /* template_action() sets a RNA_AnimData pointer, whereas other code may set
61 * other pointer types. This code here only deals with the former. */
62 if (prop && ptr.type == &RNA_AnimData) {
63 if (!RNA_property_editable(&ptr, prop)) {
64 return nullptr;
65 }
66 if (r_adt_id_owner) {
67 *r_adt_id_owner = ptr.owner_id;
68 }
69 AnimData *adt = static_cast<AnimData *>(ptr.data);
70 return adt;
71 }
72 }
73
74 SpaceLink *space_data = CTX_wm_space_data(C);
75 if (!space_data || space_data->spacetype != SPACE_ACTION) {
76 return nullptr;
77 }
78
79 SpaceAction *saction = (SpaceAction *)space_data;
81 AnimData *adt = nullptr;
82
83 /* Get AnimData block to use */
84 if (saction->mode == SACTCONT_ACTION) {
85 /* Currently, "Action Editor" means object-level only... */
86 if (ob) {
87 adt = ob->adt;
88 if (r_adt_id_owner) {
89 *r_adt_id_owner = &ob->id;
90 }
91 }
92 }
93 else if (saction->mode == SACTCONT_SHAPEKEY) {
94 Key *key = BKE_key_from_object(ob);
95 if (key) {
96 adt = key->adt;
97 if (r_adt_id_owner) {
98 *r_adt_id_owner = &key->id;
99 }
100 }
101 }
102
103 return adt;
104}
105
108/* -------------------------------------------------------------------- */
113{
114 ScrArea *area = CTX_wm_area(C);
115 bAction *action;
116
117 /* create action - the way to do this depends on whether we've got an
118 * existing one there already, in which case we make a copy of it
119 * (which is useful for "versioning" actions within the same file)
120 */
121 if (oldact && GS(oldact->id.name) == ID_AC) {
122 /* make a copy of the existing action */
123 action = (bAction *)BKE_id_copy(CTX_data_main(C), &oldact->id);
124 }
125 else {
126 /* just make a new (empty) action */
127 action = BKE_action_add(CTX_data_main(C), "Action");
128 }
129
130 /* when creating new ID blocks, there is already 1 user (as for all new datablocks),
131 * but the RNA pointer code will assign all the proper users instead, so we compensate
132 * for that here
133 */
134 BLI_assert(action->id.us == 1);
135 id_us_min(&action->id);
136
137 /* set ID-Root type */
138 if (area->spacetype == SPACE_ACTION) {
139 SpaceAction *saction = (SpaceAction *)area->spacedata.first;
140
141 if (saction->mode == SACTCONT_SHAPEKEY) {
142 action->idroot = ID_KE;
143 }
144 else {
145 action->idroot = ID_OB;
146 }
147 }
148
149 return action;
150}
151
152/* Change the active action used by the action editor */
154{
155 bScreen *screen = CTX_wm_screen(C);
157
158 PropertyRNA *prop;
159
160 /* create RNA pointers and get the property */
161 PointerRNA ptr = RNA_pointer_create(&screen->id, &RNA_SpaceDopeSheetEditor, saction);
162 prop = RNA_struct_find_property(&ptr, "action");
163
164 /* NOTE: act may be nullptr here, so better to just use a cast here */
165 PointerRNA idptr = RNA_id_pointer_create((ID *)act);
166
167 /* set the new pointer, and force a refresh */
168 RNA_property_pointer_set(&ptr, prop, idptr, nullptr);
169 RNA_property_update(C, &ptr, prop);
170}
171
174/* -------------------------------------------------------------------- */
185{
186 { /* Support use from the layout.template_action() UI template. */
187 PointerRNA ptr = {nullptr};
188 PropertyRNA *prop = nullptr;
190 if (prop) {
191 return RNA_property_editable(&ptr, prop);
192 }
193 }
194
195 Scene *scene = CTX_data_scene(C);
196
197 /* Check tweak-mode is off (as you don't want to be tampering with the action in that case) */
198 /* NOTE: unlike for pushdown,
199 * this operator needs to be run when creating an action from nothing... */
203
204 /* For now, actions are only for the active object, and on object and shape-key levels... */
205 if (saction->mode == SACTCONT_ACTION) {
206 /* XXX: This assumes that actions are assigned to the active object in this mode */
207 if (ob) {
208 if ((ob->adt == nullptr) || (ob->adt->flag & ADT_NLA_EDIT_ON) == 0) {
209 return true;
210 }
211 }
212 }
213 else if (saction->mode == SACTCONT_SHAPEKEY) {
214 Key *key = BKE_key_from_object(ob);
215 if (key) {
216 if ((key->adt == nullptr) || (key->adt->flag & ADT_NLA_EDIT_ON) == 0) {
217 return true;
218 }
219 }
220 }
221 }
222 else if (ED_operator_nla_active(C)) {
223 if (!(scene->flag & SCE_NLA_EDIT_ON)) {
224 return true;
225 }
226 }
227
228 /* something failed... */
229 return false;
230}
231
232static int action_new_exec(bContext *C, wmOperator * /*op*/)
233{
235 PropertyRNA *prop;
236
237 bAction *oldact = nullptr;
238 AnimData *adt = nullptr;
239 ID *adt_id_owner = nullptr;
240 /* hook into UI */
242
243 if (prop) {
244 /* The operator was called from a button. */
245 PointerRNA oldptr;
246
247 oldptr = RNA_property_pointer_get(&ptr, prop);
248 oldact = (bAction *)oldptr.owner_id;
249
250 /* stash the old action to prevent it from being lost */
251 if (ptr.type == &RNA_AnimData) {
252 adt = static_cast<AnimData *>(ptr.data);
253 adt_id_owner = ptr.owner_id;
254 }
255 else if (ptr.type == &RNA_SpaceDopeSheetEditor) {
256 adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
257 }
258 }
259 else {
260 adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
261 oldact = adt->action;
262 }
263 {
264 bAction *action = nullptr;
265
266 /* Perform stashing operation - But only if there is an action */
267 if (adt && oldact) {
268 BLI_assert(adt_id_owner != nullptr);
269 /* stash the action */
270 if (BKE_nla_action_stash({*adt_id_owner, *adt}, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) {
271 /* The stash operation will remove the user already
272 * (and unlink the action from the AnimData action slot).
273 * Hence, we must unset the ref to the action in the
274 * action editor too (if this is where we're being called from)
275 * first before setting the new action once it is created,
276 * or else the user gets decremented twice!
277 */
278 if (ptr.type == &RNA_SpaceDopeSheetEditor) {
279 SpaceAction *saction = static_cast<SpaceAction *>(ptr.data);
280 saction->action = nullptr;
281 }
282 }
283 else {
284#if 0
285 printf("WARNING: Failed to stash %s. It may already exist in the NLA stack though\n",
286 oldact->id.name);
287#endif
288 }
289 }
290
291 /* create action */
292 action = action_create_new(C, oldact);
293
294 if (prop) {
295 /* set this new action
296 * NOTE: we can't use actedit_change_action, as this function is also called from the NLA
297 */
298 PointerRNA idptr = RNA_id_pointer_create(&action->id);
299 RNA_property_pointer_set(&ptr, prop, idptr, nullptr);
300 RNA_property_update(C, &ptr, prop);
301 }
302 }
303
304 /* set notifier that keyframes have changed */
306
307 return OPERATOR_FINISHED;
308}
309
311{
312 /* identifiers */
313 ot->name = "New Action";
314 ot->idname = "ACTION_OT_new";
315 ot->description = "Create new action";
316
317 /* api callbacks */
320
321 /* flags */
323}
324
327/* -------------------------------------------------------------------- */
337{
339 return false;
340 }
341
344
345 if (!adt || !saction->action) {
346 return false;
347 }
348
349 /* NOTE: We check this for the AnimData block in question and not the global flag,
350 * as the global flag may be left dirty by some of the browsing ops here.
351 */
352 return (adt->flag & ADT_NLA_EDIT_ON) == 0;
353}
354
356{
358 ID *adt_id_owner = nullptr;
359 AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
360
361 /* Do the deed... */
362 if (adt && adt->action) {
363 blender::animrig::Action &action = adt->action->wrap();
364
365 /* Perform the push-down operation
366 * - This will deal with all the AnimData-side user-counts. */
367 if (!action.has_keyframes(adt->slot_handle)) {
368 /* action may not be suitable... */
369 BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier");
370 return OPERATOR_CANCELLED;
371 }
372
373 /* action can be safely added */
374 BKE_nla_action_pushdown({*adt_id_owner, *adt}, ID_IS_OVERRIDE_LIBRARY(adt_id_owner));
375
376 Main *bmain = CTX_data_main(C);
377 DEG_id_tag_update_ex(bmain, adt_id_owner, ID_RECALC_ANIMATION);
378
379 /* The action needs updating too, as FCurve modifiers are to be reevaluated. They won't extend
380 * beyond the NLA strip after pushing down to the NLA. */
382
383 /* Stop displaying this action in this editor
384 * NOTE: The editor itself doesn't set a user...
385 */
386 saction->action = nullptr;
387 }
388
389 /* Send notifiers that stuff has changed */
391 return OPERATOR_FINISHED;
392}
393
395{
396 /* identifiers */
397 ot->name = "Push Down Action";
398 ot->idname = "ACTION_OT_push_down";
399 ot->description = "Push action down on to the NLA stack as a new strip";
400
401 /* callbacks */
404
405 /* flags */
407}
408
411/* -------------------------------------------------------------------- */
416{
418 ID *adt_id_owner = nullptr;
419 AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
420
421 /* Perform stashing operation */
422 if (adt) {
423 /* don't do anything if this action is empty... */
424 if (!adt->action->wrap().has_keyframes(adt->slot_handle)) {
425 /* action may not be suitable... */
426 BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier");
427 return OPERATOR_CANCELLED;
428 }
429
430 /* stash the action */
431 if (BKE_nla_action_stash({*adt_id_owner, *adt}, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) {
432 /* The stash operation will remove the user already,
433 * so the flushing step later shouldn't double up
434 * the user-count fixes. Hence, we must unset this ref
435 * first before setting the new action.
436 */
437 saction->action = nullptr;
438 }
439 else {
440 /* action has already been added - simply warn about this, and clear */
441 BKE_report(op->reports, RPT_ERROR, "Action has already been stashed");
442 }
443
444 /* clear action refs from editor, and then also the backing data (not necessary) */
445 actedit_change_action(C, nullptr);
446 }
447
448 /* Send notifiers that stuff has changed */
450 return OPERATOR_FINISHED;
451}
452
454{
455 /* identifiers */
456 ot->name = "Stash Action";
457 ot->idname = "ACTION_OT_stash";
458 ot->description = "Store this action in the NLA stack as a non-contributing strip for later use";
459
460 /* callbacks */
463
464 /* flags */
466
467 /* properties */
469 "create_new",
470 true,
471 "Create New Action",
472 "Create a new action once the existing one has been safely stored");
473}
474
477/* -------------------------------------------------------------------- */
486{
489
490 /* Check tweak-mode is off (as you don't want to be tampering with the action in that case) */
491 /* NOTE: unlike for pushdown,
492 * this operator needs to be run when creating an action from nothing... */
493 if (adt) {
494 if (!(adt->flag & ADT_NLA_EDIT_ON)) {
495 return true;
496 }
497 }
498 else {
499 /* There may not be any action/animdata yet, so, just fallback to the global setting
500 * (which may not be totally valid yet if the action editor was used and things are
501 * now in an inconsistent state)
502 */
504 Scene *scene = CTX_data_scene(C);
505
506 if (!(scene->flag & SCE_NLA_EDIT_ON)) {
507 /* For now, actions are only for the active object, and on object and shape-key levels...
508 */
509 return ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY);
510 }
511 }
512 }
513
514 /* something failed... */
515 return false;
516}
517
519{
521 ID *adt_id_owner = nullptr;
522 AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
523
524 /* Check for no action... */
525 if (saction->action == nullptr) {
526 /* just create a new action */
527 bAction *action = action_create_new(C, nullptr);
528 actedit_change_action(C, action);
529 }
530 else if (adt) {
531 /* Perform stashing operation */
532 if (!adt->action->wrap().has_keyframes(adt->slot_handle)) {
533 /* don't do anything if this action is empty... */
534 BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier");
535 return OPERATOR_CANCELLED;
536 }
537
538 /* stash the action */
539 if (BKE_nla_action_stash({*adt_id_owner, *adt}, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) {
540 bAction *new_action = nullptr;
541
542 /* Create new action not based on the old one
543 * (since the "new" operator already does that). */
544 new_action = action_create_new(C, nullptr);
545
546 /* The stash operation will remove the user already,
547 * so the flushing step later shouldn't double up
548 * the user-count fixes. Hence, we must unset this ref
549 * first before setting the new action.
550 */
551 saction->action = nullptr;
552 actedit_change_action(C, new_action);
553 }
554 else {
555 /* action has already been added - simply warn about this, and clear */
556 BKE_report(op->reports, RPT_ERROR, "Action has already been stashed");
557 actedit_change_action(C, nullptr);
558 }
559 }
560
561 /* Send notifiers that stuff has changed */
563 return OPERATOR_FINISHED;
564}
565
567{
568 /* identifiers */
569 ot->name = "Stash Action";
570 ot->idname = "ACTION_OT_stash_and_create";
571 ot->description =
572 "Store this action in the NLA stack as a non-contributing strip for later use, and create a "
573 "new action";
574
575 /* callbacks */
578
579 /* flags */
581}
582
585/* -------------------------------------------------------------------- */
597 bContext *C, ID *id, AnimData *adt, bAction *act, ReportList *reports, bool force_delete)
598{
599 BLI_assert(id);
600 ScrArea *area = CTX_wm_area(C);
601
602 /* If the old action only has a single user (that it's about to lose),
603 * warn user about it
604 *
605 * TODO: Maybe we should just save it for them? But then, there's the problem of
606 * trying to get rid of stuff that's actually unwanted!
607 */
608 if (act->id.us == 1) {
609 BKE_reportf(reports,
611 "Action '%s' will not be saved, create Fake User or Stash in NLA Stack to retain",
612 act->id.name + 2);
613 }
614
615 /* Clear Fake User and remove action stashing strip (if present) */
616 if (force_delete) {
617 /* Remove stashed strip binding this action to this datablock */
618 /* XXX: we cannot unlink it from *OTHER* datablocks that may also be stashing it,
619 * but GE users only seem to use/care about single-object binding for now so this
620 * should be fine
621 */
622 if (adt) {
623 NlaTrack *nlt, *nlt_next;
624 NlaStrip *strip, *nstrip;
625
626 for (nlt = static_cast<NlaTrack *>(adt->nla_tracks.first); nlt; nlt = nlt_next) {
627 nlt_next = nlt->next;
628
629 if (strstr(nlt->name, DATA_("[Action Stash]"))) {
630 for (strip = static_cast<NlaStrip *>(nlt->strips.first); strip; strip = nstrip) {
631 nstrip = strip->next;
632
633 if (strip->act == act) {
634 /* Remove this strip, and the track too if it doesn't have anything else */
635 BKE_nlastrip_remove_and_free(&nlt->strips, strip, true);
636
637 if (nlt->strips.first == nullptr) {
638 BLI_assert(nstrip == nullptr);
640 }
641 }
642 }
643 }
644 }
645 }
646
647 /* Clear Fake User */
648 id_fake_user_clear(&act->id);
649 }
650
651 /* If in Tweak Mode, don't unlink. Instead, this becomes a shortcut to exit Tweak Mode. */
652 if ((adt) && (adt->flag & ADT_NLA_EDIT_ON)) {
653 BKE_nla_tweakmode_exit({*id, *adt});
654
655 Scene *scene = CTX_data_scene(C);
656 if (scene != nullptr) {
657 scene->flag &= ~SCE_NLA_EDIT_ON;
658 }
659 }
660 else {
661 /* Unlink normally - Setting it to nullptr should be enough to get the old one unlinked */
662 if (area->spacetype == SPACE_ACTION) {
663 /* clear action editor -> action */
664 actedit_change_action(C, nullptr);
665 }
666 else {
667 /* clear AnimData -> action */
668 PropertyRNA *prop;
669
670 /* create AnimData RNA pointers */
671 PointerRNA ptr = RNA_pointer_create(id, &RNA_AnimData, adt);
672 prop = RNA_struct_find_property(&ptr, "action");
673
674 /* clear... */
676 RNA_property_update(C, &ptr, prop);
677 }
678 }
679}
680
681/* -------------------------- */
682
684{
685 {
686 ID *animated_id = nullptr;
687 AnimData *adt = ED_actedit_animdata_from_context(C, &animated_id);
688 if (animated_id) {
689 if (!BKE_id_is_editable(CTX_data_main(C), animated_id)) {
690 return false;
691 }
692 if (!adt) {
693 return false;
694 }
695 return adt->action != nullptr;
696 }
697 }
698
702
703 /* Only when there's an active action, in the right modes... */
704 if (saction->action && adt) {
705 return true;
706 }
707 }
708
709 /* something failed... */
710 return false;
711}
712
714{
715 ID *animated_id = nullptr;
716 AnimData *adt = ED_actedit_animdata_from_context(C, &animated_id);
717 bool force_delete = RNA_boolean_get(op->ptr, "force_delete");
718
719 if (adt && adt->action) {
720 ED_animedit_unlink_action(C, animated_id, adt, adt->action, op->reports, force_delete);
721 }
722
723 /* Unlink is also abused to exit NLA tweak mode. */
725
726 return OPERATOR_FINISHED;
727}
728
729static int action_unlink_invoke(bContext *C, wmOperator *op, const wmEvent *event)
730{
731 /* NOTE: this is hardcoded to match the behavior for the unlink button
732 * (in `interface_templates.cc`). */
733 RNA_boolean_set(op->ptr, "force_delete", event->modifier & KM_SHIFT);
734 return action_unlink_exec(C, op);
735}
736
738{
739 PropertyRNA *prop;
740
741 /* identifiers */
742 ot->name = "Unlink Action";
743 ot->idname = "ACTION_OT_unlink";
744 ot->description = "Unlink this action from the active action slot (and/or exit Tweak Mode)";
745
746 /* callbacks */
750
751 /* properties */
752 prop = RNA_def_boolean(ot->srna,
753 "force_delete",
754 false,
755 "Force Delete",
756 "Clear Fake User and remove "
757 "copy stashed in this data-block's NLA stack");
759
760 /* flags */
762}
763
766/* -------------------------------------------------------------------- */
770/* Try to find NLA Strip to use for action layer up/down tool */
771static NlaStrip *action_layer_get_nlastrip(ListBase *strips, float ctime)
772{
773 LISTBASE_FOREACH (NlaStrip *, strip, strips) {
774 /* Can we use this? */
775 if (IN_RANGE_INCL(ctime, strip->start, strip->end)) {
776 /* in range - use this one */
777 return strip;
778 }
779 if ((ctime < strip->start) && (strip->prev == nullptr)) {
780 /* before first - use this one */
781 return strip;
782 }
783 if ((ctime > strip->end) && (strip->next == nullptr)) {
784 /* after last - use this one */
785 return strip;
786 }
787 }
788
789 /* nothing suitable found... */
790 return nullptr;
791}
792
793/* Switch NLA Strips/Actions. */
794static void action_layer_switch_strip(const OwnedAnimData owned_adt,
795 NlaTrack *old_track,
796 NlaStrip *old_strip,
797 NlaTrack *nlt,
798 NlaStrip *strip)
799{
800 AnimData *adt = &owned_adt.adt;
801
802 /* Exit tweak-mode on old strip
803 * NOTE: We need to manually clear this stuff ourselves, as tweak-mode exit doesn't do it
804 */
805 BKE_nla_tweakmode_exit(owned_adt);
806
807 if (old_strip) {
809 }
810 if (old_track) {
811 old_track->flag &= ~(NLATRACK_ACTIVE | NLATRACK_SELECTED);
812 }
813
814 /* Make this one the active one instead */
816 nlt->flag |= NLATRACK_ACTIVE;
817
818 /* Copy over "solo" flag - This is useful for stashed actions... */
819 if (old_track) {
820 if (old_track->flag & NLATRACK_SOLO) {
821 old_track->flag &= ~NLATRACK_SOLO;
822 nlt->flag |= NLATRACK_SOLO;
823 }
824 }
825 else {
826 /* NLA muting <==> Solo Tracks */
827 if (adt->flag & ADT_NLA_EVAL_OFF) {
828 /* disable NLA muting */
829 adt->flag &= ~ADT_NLA_EVAL_OFF;
830
831 /* mark this track as being solo */
832 adt->flag |= ADT_NLA_SOLO_TRACK;
833 nlt->flag |= NLATRACK_SOLO;
834
835 /* TODO: Needs rest-pose flushing (when we get reference track) */
836 }
837 }
838
839 /* Enter tweak-mode again - hopefully we're now "it" */
840 BKE_nla_tweakmode_enter(owned_adt);
841 BLI_assert(adt->actstrip == strip);
842}
843
846/* -------------------------------------------------------------------- */
851{
852 /* Action Editor's action editing modes only */
855 if (adt) {
856 /* only allow if we're in tweak-mode, and there's something above us... */
857 if (adt->flag & ADT_NLA_EDIT_ON) {
858 /* We need to check if there are any tracks above the active one
859 * since the track the action comes from is not stored in AnimData
860 */
861 if (adt->nla_tracks.last) {
862 NlaTrack *nlt = (NlaTrack *)adt->nla_tracks.last;
863
864 if (nlt->flag & NLATRACK_DISABLED) {
865 /* A disabled track will either be the track itself,
866 * or one of the ones above it.
867 *
868 * If this is the top-most one, there is the possibility
869 * that there is no active action. For now, we let this
870 * case return true too, so that there is a natural way
871 * to "move to an empty layer", even though this means
872 * that we won't actually have an action.
873 */
874 // return (adt->tmpact != nullptr);
875 return true;
876 }
877 }
878 }
879 }
880 }
881
882 /* something failed... */
883 return false;
884}
885
887{
888 ID *animated_id = nullptr;
889 AnimData *adt = ED_actedit_animdata_from_context(C, &animated_id);
890 const OwnedAnimData owned_adt{*animated_id, *adt};
891
892 Scene *scene = CTX_data_scene(C);
893 float ctime = BKE_scene_ctime_get(scene);
894
895 /* Get active track */
896 NlaTrack *act_track = BKE_nlatrack_find_tweaked(adt);
897
898 if (act_track == nullptr) {
899 BKE_report(op->reports, RPT_ERROR, "Could not find current NLA Track");
900 return OPERATOR_CANCELLED;
901 }
902
903 /* Find next action, and hook it up */
904 if (act_track->next) {
905 NlaTrack *nlt;
906
907 /* Find next action to use */
908 for (nlt = act_track->next; nlt; nlt = nlt->next) {
909 NlaStrip *strip = action_layer_get_nlastrip(&nlt->strips, ctime);
910
911 if (strip) {
912 action_layer_switch_strip(owned_adt, act_track, adt->actstrip, nlt, strip);
913 break;
914 }
915 }
916 }
917 else {
918 /* No more actions (strips) - Go back to editing the original active action
919 * NOTE: This will mean exiting tweak-mode...
920 */
921 BKE_nla_tweakmode_exit(owned_adt);
922
923 /* Deal with solo flags...
924 * Assume: Solo Track == NLA Muting
925 */
926 if (adt->flag & ADT_NLA_SOLO_TRACK) {
927 /* turn off solo flags on tracks */
928 act_track->flag &= ~NLATRACK_SOLO;
929 adt->flag &= ~ADT_NLA_SOLO_TRACK;
930
931 /* turn on NLA muting (to keep same effect) */
932 adt->flag |= ADT_NLA_EVAL_OFF;
933
934 /* TODO: Needs rest-pose flushing (when we get reference track) */
935 }
936 }
937
938 /* Update the action that this editor now uses
939 * NOTE: The calls above have already handled the user-count/anim-data side of things. */
941 return OPERATOR_FINISHED;
942}
943
945{
946 /* identifiers */
947 ot->name = "Next Layer";
948 ot->idname = "ACTION_OT_layer_next";
949 ot->description =
950 "Switch to editing action in animation layer above the current action in the NLA Stack";
951
952 /* callbacks */
955
956 /* flags */
958}
959
962/* -------------------------------------------------------------------- */
967{
968 /* Action Editor's action editing modes only */
971 if (adt) {
972 if (adt->flag & ADT_NLA_EDIT_ON) {
973 /* Tweak Mode: We need to check if there are any tracks below the active one
974 * that we can move to */
975 if (adt->nla_tracks.first) {
976 NlaTrack *nlt = (NlaTrack *)adt->nla_tracks.first;
977
978 /* Since the first disabled track is the track being tweaked/edited,
979 * we can simplify things by only checking the first track:
980 * - If it is disabled, this is the track being tweaked,
981 * so there can't be anything below it
982 * - Otherwise, there is at least 1 track below the tweaking
983 * track that we can descend to
984 */
985 if ((nlt->flag & NLATRACK_DISABLED) == 0) {
986 /* not disabled = there are actions below the one being tweaked */
987 return true;
988 }
989 }
990 }
991 else {
992 /* Normal Mode: If there are any tracks, we can try moving to those */
993 return (adt->nla_tracks.first != nullptr);
994 }
995 }
996 }
997
998 /* something failed... */
999 return false;
1000}
1001
1003{
1004 ID *animated_id = nullptr;
1005 AnimData *adt = ED_actedit_animdata_from_context(C, &animated_id);
1006 NlaTrack *act_track;
1007 NlaTrack *nlt;
1008
1009 Scene *scene = CTX_data_scene(C);
1010 float ctime = BKE_scene_ctime_get(scene);
1011
1012 /* Sanity Check */
1013 if (adt == nullptr) {
1014 BKE_report(
1015 op->reports, RPT_ERROR, "Internal Error: Could not find Animation Data/NLA Stack to use");
1016 return OPERATOR_CANCELLED;
1017 }
1018
1019 /* Get active track */
1020 act_track = BKE_nlatrack_find_tweaked(adt);
1021
1022 /* If there is no active track, that means we are using the active action... */
1023 if (act_track) {
1024 /* Active Track - Start from the one below it */
1025 nlt = act_track->prev;
1026 }
1027 else {
1028 /* Active Action - Use the top-most track */
1029 nlt = static_cast<NlaTrack *>(adt->nla_tracks.last);
1030 }
1031
1032 /* Find previous action and hook it up */
1033 for (; nlt; nlt = nlt->prev) {
1034 NlaStrip *strip = action_layer_get_nlastrip(&nlt->strips, ctime);
1035
1036 if (strip) {
1037 action_layer_switch_strip({*animated_id, *adt}, act_track, adt->actstrip, nlt, strip);
1038 break;
1039 }
1040 }
1041
1042 /* Update the action that this editor now uses
1043 * NOTE: The calls above have already handled the user-count/animdata side of things. */
1045 return OPERATOR_FINISHED;
1046}
1047
1049{
1050 /* identifiers */
1051 ot->name = "Previous Layer";
1052 ot->idname = "ACTION_OT_layer_prev";
1053 ot->description =
1054 "Switch to editing action in animation layer below the current action in the NLA Stack";
1055
1056 /* callbacks */
1059
1060 /* flags */
1062}
1063
Functions and classes to work with Actions.
Blender kernel action and pose functionality.
bAction * BKE_action_add(Main *bmain, const char name[])
bScreen * CTX_wm_screen(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
SpaceLink * CTX_wm_space_data(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
Key * BKE_key_from_object(Object *ob)
Definition key.cc:1820
bool BKE_id_is_editable(const Main *bmain, const ID *id)
Definition lib_id.cc:2456
void id_fake_user_clear(ID *id)
Definition lib_id.cc:397
ID * BKE_id_copy(Main *bmain, const ID *id)
Definition lib_id.cc:765
void id_us_min(ID *id)
Definition lib_id.cc:359
void BKE_nlastrip_remove_and_free(ListBase *strips, NlaStrip *strip, const bool do_id_user)
void BKE_nla_action_pushdown(OwnedAnimData owned_adt, bool is_liboverride)
void BKE_nla_tweakmode_exit(OwnedAnimData owned_adt)
NlaTrack * BKE_nlatrack_find_tweaked(AnimData *adt)
bool BKE_nla_action_stash(OwnedAnimData owned_adt, bool is_liboverride)
bool BKE_nla_tweakmode_enter(OwnedAnimData owned_adt)
void BKE_nlatrack_remove_and_free(ListBase *tracks, NlaTrack *nlt, bool do_id_user)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
float BKE_scene_ctime_get(const Scene *scene)
Definition scene.cc:2317
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
#define ELEM(...)
#define IN_RANGE_INCL(a, b, c)
#define DATA_(msgid)
void DEG_id_tag_update_ex(Main *bmain, ID *id, unsigned int flags)
@ ID_RECALC_ANIMATION
Definition DNA_ID.h:1044
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:683
@ ID_KE
@ ID_AC
@ ID_OB
@ SACTCONT_ACTION
@ SACTCONT_SHAPEKEY
@ NLASTRIP_FLAG_ACTIVE
@ NLASTRIP_FLAG_SELECT
@ ADT_NLA_SOLO_TRACK
@ ADT_NLA_EVAL_OFF
@ ADT_NLA_EDIT_ON
@ NLATRACK_SOLO
@ NLATRACK_ACTIVE
@ NLATRACK_DISABLED
@ NLATRACK_SELECTED
Object is a sort of wrapper for general info.
@ SCE_NLA_EDIT_ON
@ SPACE_ACTION
bool ED_operator_action_active(bContext *C)
bool ED_operator_nla_active(bContext *C)
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
constexpr PointerRNA PointerRNA_NULL
Definition RNA_types.hh:45
void UI_context_active_but_prop_get_templateID(const bContext *C, PointerRNA *r_ptr, PropertyRNA **r_prop)
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define ND_NLA_ACTCHANGE
Definition WM_types.hh:465
#define NC_ANIMATION
Definition WM_types.hh:355
#define NA_ADDED
Definition WM_types.hh:552
@ KM_SHIFT
Definition WM_types.hh:255
#define ND_KEYFRAME
Definition WM_types.hh:461
static bool action_unlink_poll(bContext *C)
static void action_layer_switch_strip(const OwnedAnimData owned_adt, NlaTrack *old_track, NlaStrip *old_strip, NlaTrack *nlt, NlaStrip *strip)
AnimData * ED_actedit_animdata_from_context(const bContext *C, ID **r_adt_id_owner)
void ACTION_OT_layer_prev(wmOperatorType *ot)
void ACTION_OT_push_down(wmOperatorType *ot)
static bAction * action_create_new(bContext *C, bAction *oldact)
static bool action_new_poll(bContext *C)
static void actedit_change_action(bContext *C, bAction *act)
static int action_stash_exec(bContext *C, wmOperator *op)
void ACTION_OT_unlink(wmOperatorType *ot)
static bool action_layer_prev_poll(bContext *C)
static bool action_stash_create_poll(bContext *C)
void ED_animedit_unlink_action(bContext *C, ID *id, AnimData *adt, bAction *act, ReportList *reports, bool force_delete)
static bool action_pushdown_poll(bContext *C)
void ACTION_OT_stash_and_create(wmOperatorType *ot)
static int action_layer_next_exec(bContext *C, wmOperator *op)
static int action_new_exec(bContext *C, wmOperator *)
void ACTION_OT_stash(wmOperatorType *ot)
static int action_unlink_exec(bContext *C, wmOperator *op)
static NlaStrip * action_layer_get_nlastrip(ListBase *strips, float ctime)
static int action_stash_create_exec(bContext *C, wmOperator *op)
void ACTION_OT_new(wmOperatorType *ot)
void ACTION_OT_layer_next(wmOperatorType *ot)
static int action_pushdown_exec(bContext *C, wmOperator *op)
static int action_layer_prev_exec(bContext *C, wmOperator *op)
static bool action_layer_next_poll(bContext *C)
static int action_unlink_invoke(bContext *C, wmOperator *op, const wmEvent *event)
bool has_keyframes(slot_handle_t action_slot_handle) const ATTR_WARN_UNUSED_RESULT
#define printf
#define GS(x)
Definition iris.cc:202
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_property_pointer_set(PointerRNA *ptr, PropertyRNA *prop, PointerRNA ptr_value, ReportList *reports)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
bool RNA_property_editable(const PointerRNA *ptr, PropertyRNA *prop)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
PointerRNA RNA_id_pointer_create(ID *id)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
bAction * action
NlaStrip * actstrip
int32_t slot_handle
ListBase nla_tracks
Definition DNA_ID.h:413
int us
Definition DNA_ID.h:435
char name[66]
Definition DNA_ID.h:425
struct AnimData * adt
void * last
void * first
struct NlaStrip * next
bAction * act
ListBase strips
struct NlaTrack * next
char name[64]
struct NlaTrack * prev
struct AnimData * adt
AnimData & adt
ID * owner_id
Definition RNA_types.hh:40
StructRNA * type
Definition RNA_types.hh:41
void * data
Definition RNA_types.hh:42
uint8_t modifier
Definition WM_types.hh:739
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct PointerRNA * ptr
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4126
wmOperatorType * ot
Definition wm_files.cc:4125