Blender V5.0
anim_data.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009 Blender Authors, Joshua Leung. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8#include "MEM_guardedalloc.h"
9
10#include <cstring>
11#include <optional>
12
13#include "BKE_action.hh"
14#include "BKE_anim_data.hh"
15#include "BKE_animsys.h"
16#include "BKE_context.hh"
17#include "BKE_fcurve.hh"
18#include "BKE_fcurve_driver.h"
19#include "BKE_global.hh"
20#include "BKE_idtype.hh"
21#include "BKE_lib_id.hh"
22#include "BKE_lib_query.hh"
23#include "BKE_main.hh"
24#include "BKE_nla.hh"
25#include "BKE_node.hh"
26#include "BKE_report.hh"
27
28#include "DNA_ID.h"
29#include "DNA_anim_types.h"
30#include "DNA_light_types.h"
31#include "DNA_material_types.h"
32#include "DNA_node_types.h"
34#include "DNA_world_types.h"
35
36#include "BLI_alloca.h"
37#include "BLI_dynstr.h"
38#include "BLI_listbase.h"
39#include "BLI_string.h"
40#include "BLI_string_utf8.h"
41#include "BLI_utildefines.h"
42
43#include "DEG_depsgraph.hh"
44
45#include "BLO_read_write.hh"
46
47#include "RNA_access.hh"
48#include "RNA_path.hh"
49
51#include "ANIM_action_legacy.hh"
52
53#include "CLG_log.h"
54
55static CLG_LogRef LOG = {"anim.data"};
56
57using namespace blender;
58
59/* ***************************************** */
60/* AnimData API */
61
62/* Getter/Setter -------------------------------------------- */
63
64bool id_type_can_have_animdata(const short id_type)
65{
66 const IDTypeInfo *typeinfo = BKE_idtype_get_info_from_idcode(id_type);
67 if (typeinfo != nullptr) {
68 return (typeinfo->flags & IDTYPE_FLAGS_NO_ANIMDATA) == 0;
69 }
70 return false;
71}
72
73bool id_can_have_animdata(const ID *id)
74{
75 /* sanity check */
76 if (id == nullptr) {
77 return false;
78 }
79
81}
82
84{
85 /* In order for this to work, we assume that the #AnimData pointer is stored
86 * immediately after the given ID-block in the struct, as per IdAdtTemplate. */
87
88 /* Only some ID-blocks have this info for now, so we cast the types that do
89 * to be of type IdAdtTemplate, and add the AnimData to it using the template. */
90 if (id_can_have_animdata(id)) {
91 IdAdtTemplate *iat = (IdAdtTemplate *)id;
92 return iat->adt;
93 }
94 return nullptr;
95}
96
98{
99 /* In order for this to work, we assume that the #AnimData pointer is stored
100 * immediately after the given ID-block in the struct, as per IdAdtTemplate. */
101
102 /* Only some ID-blocks have this info for now, so we cast the types that do
103 * to be of type IdAdtTemplate, and add the AnimData to it using the template. */
104 if (id_can_have_animdata(id)) {
105 IdAdtTemplate *iat = (IdAdtTemplate *)id;
106
107 /* check if there's already AnimData, in which case, don't add */
108 if (iat->adt == nullptr) {
109 AnimData *adt;
110
111 /* add animdata */
112 adt = iat->adt = MEM_callocN<AnimData>("AnimData");
113
114 /* set default settings */
115 adt->act_influence = 1.0f;
116 }
117
118 return iat->adt;
119 }
120 return nullptr;
121}
122
123/* Action Setter --------------------------------------- */
124
126{
127 using namespace blender;
128
129 /* If we're unassigning (null action pointer) and there's no animdata, we can
130 * skip the whole song and dance of creating animdata just to "unassign" the
131 * action from it. */
132 if (act == nullptr && BKE_animdata_from_id(id) == nullptr) {
133 return true;
134 }
135
137 if (adt == nullptr) {
138 BKE_report(reports, RPT_WARNING, "Attempt to set action on non-animatable ID");
139 return false;
140 }
141
143 /* Cannot remove, otherwise things turn to custard. */
144 BKE_report(reports, RPT_ERROR, "Cannot change action, as it is still being edited in NLA");
145 return false;
146 }
147
148 return animrig::assign_action(act, {*id, *adt});
149}
150
152{
153 /* Active action is only editable when it is not a tweaking strip. */
154 const bool is_tweaking_strip = (adt->flag & ADT_NLA_EDIT_ON) || adt->actstrip != nullptr ||
155 adt->tmpact != nullptr;
156 return !is_tweaking_strip;
157}
158
160{
161 const int idcode = GS(owner->name);
162
163 if (action == nullptr) {
164 /* A nullptr action is usable by any ID type. */
165 return true;
166 }
167
169 /* TODO: for layered Actions, this function doesn't make sense. Once all Actions are
170 * auto-versioned to layered Actions, this entire function can be removed. */
171 action->idroot = 0;
172 /* Layered Actions can always be assigned to any ID type. It's the slots
173 * that are specialized. */
174 return true;
175 }
176
177 if (action->idroot == 0) {
178 /* First time this Action is assigned, lock it to this ID type. */
179 action->idroot = idcode;
180 return true;
181 }
182
183 return (action->idroot == idcode);
184}
185
186/* Freeing -------------------------------------------- */
187
188void BKE_animdata_free(ID *id, const bool do_id_user)
189{
190 if (!id_can_have_animdata(id)) {
191 return;
192 }
193
194 IdAdtTemplate *iat = (IdAdtTemplate *)id;
195 AnimData *adt = iat->adt;
196 if (!adt) {
197 return;
198 }
199
200 if (do_id_user) {
201 /* The ADT is going to be freed, which means that if it's in tweak mode, it'll have to exit
202 * that first. Otherwise we cannot un-assign its Action. */
203 BKE_nla_tweakmode_exit({*id, *adt});
204
205 if (adt->action) {
206 const bool unassign_ok = blender::animrig::unassign_action(*id);
207 BLI_assert_msg(unassign_ok,
208 "Expecting action un-assignment to always work when not in NLA tweak mode");
209 UNUSED_VARS_NDEBUG(unassign_ok);
210 }
211 /* same goes for the temporarily displaced action */
212 if (adt->tmpact) {
213 /* This should never happen, as we _just_ exited tweak mode. */
215 const bool unassign_ok = blender::animrig::assign_tmpaction(nullptr, {*id, *adt});
216 BLI_assert_msg(unassign_ok, "Expecting tmpaction un-assignment to always work");
217 UNUSED_VARS_NDEBUG(unassign_ok);
218 }
219 }
220
221 /* free nla data */
222 BKE_nla_tracks_free(&adt->nla_tracks, do_id_user);
223
224 /* free drivers - stored as a list of F-Curves */
226
227 /* free driver array cache */
229
230 /* free overrides */
231 /* TODO... */
232
233 /* free animdata now */
234 MEM_freeN(adt);
235 iat->adt = nullptr;
236}
237
239{
240 if (id == nullptr) {
241 return false;
242 }
243
244 const AnimData *adt = BKE_animdata_from_id((ID *)id);
245 if (adt == nullptr) {
246 return false;
247 }
248
249 if (adt->action) {
250 const blender::animrig::Action &action = adt->action->wrap();
251 if (action.is_action_layered() && action.is_slot_animated(adt->slot_handle)) {
252 return true;
253 }
254 if (action.is_action_legacy() && !BLI_listbase_is_empty(&action.curves)) {
255 return true;
256 }
257 }
258
261}
262
278
279/* Copying -------------------------------------------- */
280
282 std::optional<Library *> owner_library,
283 AnimData *adt,
284 const int flag)
285{
286 AnimData *dadt;
287
288 const bool do_action = (flag & LIB_ID_COPY_ACTIONS) != 0 && (flag & LIB_ID_CREATE_NO_MAIN) == 0;
289 const bool do_id_user = (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0;
290
291 /* sanity check before duplicating struct */
292 if (adt == nullptr) {
293 return nullptr;
294 }
295 dadt = static_cast<AnimData *>(MEM_dupallocN(adt));
296
297 /* make a copy of action - at worst, user has to delete copies... */
298 if (do_action) {
299 /* Recursive copy of 'real' IDs is a bit hairy. Even if do not want to deal with user-count
300 * when copying ID's data itself, we still need to do so with sub-IDs, since those will not be
301 * handled by later 'update user-counts of used IDs' code as used e.g. at end of
302 * #BKE_id_copy_ex().
303 * So in case we do copy the ID and its sub-IDs in bmain, silence the 'no user-count' flag for
304 * the sub-IDs copying.
305 * NOTE: This is a bit weak, as usually when it comes to recursive ID copy. Should work for
306 * now, but we may have to revisit this at some point and add a proper extra flag to deal with
307 * that situation. Or refactor completely the way we handle such recursion, by flattening it
308 * e.g. */
309 const int id_copy_flag = (flag & LIB_ID_CREATE_NO_MAIN) == 0 ?
311 flag;
312 BLI_assert(bmain != nullptr);
313 BLI_assert(dadt->action == nullptr || dadt->action != dadt->tmpact);
314 dadt->action = reinterpret_cast<bAction *>(
315 BKE_id_copy_in_lib(bmain,
316 owner_library,
317 reinterpret_cast<ID *>(dadt->action),
318 std::nullopt,
319 nullptr,
320 id_copy_flag));
321 dadt->tmpact = reinterpret_cast<bAction *>(
322 BKE_id_copy_in_lib(bmain,
323 owner_library,
324 reinterpret_cast<ID *>(dadt->tmpact),
325 std::nullopt,
326 nullptr,
327 id_copy_flag));
328 }
329 else if (do_id_user) {
330 id_us_plus((ID *)dadt->action);
331 id_us_plus((ID *)dadt->tmpact);
332 }
333
334 /* duplicate NLA data */
335 BKE_nla_tracks_copy_from_adt(bmain, dadt, adt, flag);
336
337 /* duplicate drivers (F-Curves) */
338 BKE_fcurves_copy(&dadt->drivers, &adt->drivers);
339 dadt->driver_array = nullptr;
340
341 /* don't copy overrides */
343
344 const bool is_main = (flag & LIB_ID_CREATE_NO_MAIN) == 0;
345 if (is_main) {
346 /* Action references were changed, so the Slot-to-user map is incomplete now. Only necessary
347 * when this happens in the main database though, as the user cache only tracks original IDs,
348 * not evaluated copies.
349 *
350 * This function does not have access to the animated ID, so it cannot just add that ID to the
351 * slot's users, hence the invalidation of the users map.
352 *
353 * TODO: refactor to pass the owner ID to this function, and just add it to the Slot's
354 * users. */
355 if (bmain) {
357 }
358 }
359
360 /* return */
361 return dadt;
362}
363
364AnimData *BKE_animdata_copy(Main *bmain, AnimData *adt, const int flag)
365{
366 return BKE_animdata_copy_in_lib(bmain, std::nullopt, adt, flag);
367}
368
369bool BKE_animdata_copy_id(Main *bmain, ID *id_to, ID *id_from, const int flag)
370{
371 AnimData *adt;
372
373 if ((id_to && id_from) && (GS(id_to->name) != GS(id_from->name))) {
374 return false;
375 }
376
378
379 adt = BKE_animdata_from_id(id_from);
380 if (adt) {
381 IdAdtTemplate *iat = (IdAdtTemplate *)id_to;
382 iat->adt = BKE_animdata_copy(bmain, adt, flag);
383 }
384
385 return true;
386}
387
388static void animdata_copy_id_action(Main *bmain,
389 ID *id,
390 const bool set_newid,
391 const bool do_linked_id)
392{
393 using namespace blender::animrig;
394
396 if (adt) {
397 if (adt->action && (do_linked_id || !ID_IS_LINKED(adt->action))) {
398 bAction *cloned_action = reinterpret_cast<bAction *>(BKE_id_copy(bmain, &adt->action->id));
399 if (set_newid) {
400 ID_NEW_SET(adt->action, cloned_action);
401 }
402
403 /* The Action was cloned, so this should find the same-named slot automatically. */
404 const slot_handle_t orig_slot_handle = adt->slot_handle;
405 const bool assign_ok = assign_action(&cloned_action->wrap(), *id);
406 BLI_assert_msg(assign_ok, "Expected action assignment to work when copying animdata");
407 BLI_assert(orig_slot_handle == adt->slot_handle);
408 UNUSED_VARS_NDEBUG(assign_ok, orig_slot_handle);
409 }
410 if (adt->tmpact && (do_linked_id || !ID_IS_LINKED(adt->tmpact))) {
411 bAction *cloned_action = reinterpret_cast<bAction *>(BKE_id_copy(bmain, &adt->tmpact->id));
412 if (set_newid) {
413 ID_NEW_SET(adt->tmpact, cloned_action);
414 }
415
416 /* The Action was cloned, so this should find the same-named slot automatically. */
417 const slot_handle_t orig_slot_handle = adt->tmp_slot_handle;
418 const bool assign_ok = assign_tmpaction(&cloned_action->wrap(), {*id, *adt});
419 BLI_assert_msg(assign_ok, "Expected tmp-action assignment to work when copying animdata");
420 BLI_assert(orig_slot_handle == adt->tmp_slot_handle);
421 UNUSED_VARS_NDEBUG(assign_ok, orig_slot_handle);
422 }
423 }
425 if (ntree) {
426 animdata_copy_id_action(bmain, &ntree->id, set_newid, do_linked_id);
427 }
428 /* Note that collections are not animatable currently, so no need to handle scenes' master
429 * collection here. */
430}
431
433{
434 const bool is_id_liboverride = ID_IS_OVERRIDE_LIBRARY(id);
435 animdata_copy_id_action(bmain, id, false, !is_id_liboverride);
436}
437
439 ID *id,
440 const /*eDupli_ID_Flags*/ uint duplicate_flags)
441{
442 if (duplicate_flags & USER_DUP_ACT) {
443 animdata_copy_id_action(bmain, id, true, (duplicate_flags & USER_DUP_LINKED_ID) != 0);
444 }
445}
446
448 Main *bmain, ID *dst_id, ID *src_id, eAnimData_MergeCopy_Modes action_mode, bool fix_drivers)
449{
450 AnimData *src = BKE_animdata_from_id(src_id);
451 AnimData *dst = BKE_animdata_from_id(dst_id);
452
453 /* sanity checks */
454 if (ELEM(nullptr, dst, src)) {
455 return;
456 }
457
458 /* TODO: we must unset all "tweak-mode" flags. */
459 if ((src->flag & ADT_NLA_EDIT_ON) || (dst->flag & ADT_NLA_EDIT_ON)) {
461 &LOG,
462 "Merging AnimData blocks while editing NLA is dangerous as it may cause data corruption");
463 return;
464 }
465
466 /* handle actions... */
467 if (action_mode == ADT_MERGECOPY_SRC_COPY) {
468 /* make a copy of the actions */
469 dst->action = (bAction *)BKE_id_copy(bmain, &src->action->id);
470 dst->tmpact = (bAction *)BKE_id_copy(bmain, &src->tmpact->id);
471 }
472 else if (action_mode == ADT_MERGECOPY_SRC_REF) {
473 /* make a reference to it */
474 dst->action = src->action;
475 id_us_plus((ID *)dst->action);
476
477 dst->tmpact = src->tmpact;
478 id_us_plus((ID *)dst->tmpact);
479 }
480 dst->slot_handle = src->slot_handle;
484
485 /* duplicate NLA data */
486 if (src->nla_tracks.first) {
487 ListBase tracks = {nullptr, nullptr};
488
489 BKE_nla_tracks_copy(bmain, &tracks, &src->nla_tracks, 0);
491 }
492
493 /* duplicate drivers (F-Curves) */
494 if (src->drivers.first) {
495 ListBase drivers = {nullptr, nullptr};
496
497 BKE_fcurves_copy(&drivers, &src->drivers);
498
499 /* Fix up all driver targets using the old target id
500 * - This assumes that the src ID is being merged into the dst ID
501 */
502 if (fix_drivers) {
503 LISTBASE_FOREACH (FCurve *, fcu, &drivers) {
504 ChannelDriver *driver = fcu->driver;
505 LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
507 if (dtar->id == src_id) {
508 dtar->id = dst_id;
509 }
510 }
512 }
513 }
514 }
515
516 BLI_movelisttolist(&dst->drivers, &drivers);
517 }
518}
519
520/* Sub-ID Regrouping ------------------------------------------- */
521
529static bool animpath_matches_basepath(const char path[], const char basepath[])
530{
531 /* we need start of path to be basepath */
532 return (path && basepath) && STRPREFIX(path, basepath);
533}
534
536 const char *old_basepath,
537 const char *new_basepath)
538{
539 BLI_assert(animpath_matches_basepath(fcu->rna_path, old_basepath));
540 if (STREQ(old_basepath, new_basepath)) {
541 return;
542 }
543
544 char *new_path = BLI_sprintfN("%s%s", new_basepath, fcu->rna_path + strlen(old_basepath));
545 MEM_freeN(fcu->rna_path);
546 fcu->rna_path = new_path;
547}
548
549/* Move F-Curves in src action to dst action, setting up all the necessary groups
550 * for this to happen, but only if the F-Curves being moved have the appropriate
551 * "base path".
552 * - This is used when data moves from one data-block to another, causing the
553 * F-Curves to need to be moved over too
554 */
556 const animrig::slot_handle_t src_slot_handle,
557 bAction *dstAct,
558 const animrig::slot_handle_t dst_slot_handle,
559 const char *src_basepath,
560 const char *dst_basepath)
561{
562 /* sanity checks */
563 if (ELEM(nullptr, srcAct, dstAct, src_basepath, dst_basepath)) {
564 if (G.debug & G_DEBUG) {
566 "srcAct: %p, dstAct: %p, src_basepath: %p, dst_basepath: %p has insufficient "
567 "info to work with",
568 (void *)srcAct,
569 (void *)dstAct,
570 (void *)src_basepath,
571 (void *)dst_basepath);
572 }
573 return;
574 }
575
576 animrig::Action &source_action = srcAct->wrap();
577 animrig::Action &dest_action = dstAct->wrap();
578
579 /* Get a list of all F-Curves to move. This is done in a separate step so we
580 * don't move the curves while iterating over them at the same time. */
581 Vector<FCurve *> fcurves_to_move;
582 animrig::foreach_fcurve_in_action_slot(source_action, src_slot_handle, [&](FCurve &fcurve) {
583 if (animpath_matches_basepath(fcurve.rna_path, src_basepath)) {
584 fcurves_to_move.append(&fcurve);
585 }
586 });
587
588 /* Move the curves from one Action to the other, and change its path to match the destination. */
589 for (FCurve *fcurve_to_move : fcurves_to_move) {
590 animpath_update_basepath(fcurve_to_move, src_basepath, dst_basepath);
591 animrig::action_fcurve_move(dest_action, dst_slot_handle, source_action, *fcurve_to_move);
592 }
593}
594
596 AnimData *dstAdt,
597 const char *src_basepath,
598 const char *dst_basepath)
599{
600 LISTBASE_FOREACH_MUTABLE (FCurve *, fcu, &srcAdt->drivers) {
601 if (animpath_matches_basepath(fcu->rna_path, src_basepath)) {
602 animpath_update_basepath(fcu, src_basepath, dst_basepath);
603 BLI_remlink(&srcAdt->drivers, fcu);
604 BLI_addtail(&dstAdt->drivers, fcu);
605
606 /* TODO: add depsgraph flushing calls? */
607 }
608 }
609}
610
611void BKE_animdata_transfer_by_basepath(Main *bmain, ID *srcID, ID *dstID, ListBase *basepaths)
612{
613 AnimData *srcAdt = nullptr, *dstAdt = nullptr;
614
615 /* sanity checks */
616 if (ELEM(nullptr, srcID, dstID)) {
617 if (G.debug & G_DEBUG) {
618 CLOG_ERROR(&LOG, "no source or destination ID to separate AnimData with");
619 }
620 return;
621 }
622
623 /* get animdata from src, and create for destination (if needed) */
624 srcAdt = BKE_animdata_from_id(srcID);
625 dstAdt = BKE_animdata_ensure_id(dstID);
626
627 if (ELEM(nullptr, srcAdt, dstAdt)) {
628 if (G.debug & G_DEBUG) {
629 CLOG_ERROR(&LOG, "no AnimData for this pair of ID's");
630 }
631 return;
632 }
633
634 /* active action */
635 if (srcAdt->action) {
636 const OwnedAnimData dst_owned_adt = {*dstID, *dstAdt};
637 if (dstAdt->action == srcAdt->action) {
638 CLOG_WARN(&LOG,
639 "Source and Destination share animation! "
640 "('%s' and '%s' both use '%s') Making new empty action",
641 srcID->name,
642 dstID->name,
643 srcAdt->action->id.name);
644
645 /* This sets dstAdt->action to nullptr. */
646 const bool unassign_ok = animrig::unassign_action(dst_owned_adt);
647 BLI_assert_msg(unassign_ok, "Expected Action unassignment to work");
648 UNUSED_VARS_NDEBUG(unassign_ok);
649 }
650
651 /* Set up an action if necessary, and name it in a similar way so that it
652 * can be easily found again. */
653 if (!dstAdt->action) {
654 animrig::Action &new_action = animrig::action_add(*bmain, srcAdt->action->id.name + 2);
655 new_action.slot_add_for_id(*dstID);
656
657 const bool assign_ok = animrig::assign_action(&new_action, dst_owned_adt);
658 BLI_assert_msg(assign_ok, "Expected Action assignment to work");
659 UNUSED_VARS_NDEBUG(assign_ok);
660 BLI_assert(dstAdt->slot_handle != animrig::Slot::unassigned);
661 }
662
663 /* loop over base paths, trying to fix for each one... */
664 LISTBASE_FOREACH (const AnimationBasePathChange *, basepath_change, basepaths) {
666 srcAdt->slot_handle,
667 dstAdt->action,
668 dstAdt->slot_handle,
669 basepath_change->src_basepath,
670 basepath_change->dst_basepath);
671 }
672 }
673
674 /* drivers */
675 if (srcAdt->drivers.first) {
676 LISTBASE_FOREACH (const AnimationBasePathChange *, basepath_change, basepaths) {
678 srcAdt, dstAdt, basepath_change->src_basepath, basepath_change->dst_basepath);
679 }
680 }
681 /* Tag source action because list of fcurves changed. */
683}
684
685/* Path Validation -------------------------------------------- */
686
687/* Check if a given RNA Path is valid, by tracing it from the given ID,
688 * and seeing if we can resolve it. */
689static bool check_rna_path_is_valid(ID *owner_id, const char *path)
690{
692 PropertyRNA *prop = nullptr;
693
694 /* make initial RNA pointer to start resolving from */
695 PointerRNA id_ptr = RNA_id_pointer_create(owner_id);
696
697 /* try to resolve */
698 return RNA_path_resolve_property(&id_ptr, path, &ptr, &prop);
699}
700
701/* Check if some given RNA Path needs fixing - free the given path and set a new one as appropriate
702 * NOTE: we assume that oldName and newName have [" "] padding around them
703 */
704static char *rna_path_rename_fix(ID *owner_id,
705 const char *prefix,
706 const char *oldName,
707 const char *newName,
708 char *oldpath,
709 bool verify_paths)
710{
711 char *prefixPtr = strstr(oldpath, prefix);
712 if (prefixPtr == nullptr) {
713 return oldpath;
714 }
715
716 char *oldNamePtr = strstr(oldpath, oldName);
717 if (oldNamePtr == nullptr) {
718 return oldpath;
719 }
720
721 int prefixLen = strlen(prefix);
722 int oldNameLen = strlen(oldName);
723
724 /* only start fixing the path if the prefix and oldName feature in the path,
725 * and prefix occurs immediately before oldName
726 */
727 if (prefixPtr + prefixLen == oldNamePtr) {
728 /* if we haven't aren't able to resolve the path now, try again after fixing it */
729 if (!verify_paths || check_rna_path_is_valid(owner_id, oldpath) == 0) {
730 DynStr *ds = BLI_dynstr_new();
731 const char *postfixPtr = oldNamePtr + oldNameLen;
732 char *newPath = nullptr;
733
734 /* add the part of the string that goes up to the start of the prefix */
735 if (prefixPtr > oldpath) {
736 BLI_dynstr_nappend(ds, oldpath, prefixPtr - oldpath);
737 }
738
739 /* add the prefix */
740 BLI_dynstr_append(ds, prefix);
741
742 /* add the new name (complete with brackets) */
743 BLI_dynstr_append(ds, newName);
744
745 /* add the postfix */
746 BLI_dynstr_append(ds, postfixPtr);
747
748 /* create new path, and cleanup old data */
749 newPath = BLI_dynstr_get_cstring(ds);
750 BLI_dynstr_free(ds);
751
752 /* check if the new path will solve our problems */
753 /* TODO: will need to check whether this step really helps in practice */
754 if (!verify_paths || check_rna_path_is_valid(owner_id, newPath)) {
755 /* free the old path, and return the new one, since we've solved the issues */
756 MEM_freeN(oldpath);
757 return newPath;
758 }
759
760 /* still couldn't resolve the path... so, might as well just leave it alone */
761 MEM_freeN(newPath);
762 }
763 }
764
765 /* the old path doesn't need to be changed */
766 return oldpath;
767}
768
769/* Check RNA-Paths for a list of F-Curves */
770static bool fcurves_path_rename_fix(ID *owner_id,
771 const char *prefix,
772 const char *oldName,
773 const char *newName,
774 const char *oldKey,
775 const char *newKey,
777 bool verify_paths)
778{
779 bool is_changed = false;
780 /* We need to check every curve. */
781 for (FCurve *fcu : curves) {
782 if (fcu->rna_path == nullptr) {
783 continue;
784 }
785 const char *old_path = fcu->rna_path;
786 /* Firstly, handle the F-Curve's own path. */
787 fcu->rna_path = rna_path_rename_fix(
788 owner_id, prefix, oldKey, newKey, fcu->rna_path, verify_paths);
789 /* if path changed and the F-Curve is grouped, check if its group also needs renaming
790 * (i.e. F-Curve is first of a bone's F-Curves;
791 * hence renaming this should also trigger rename) */
792 if (fcu->rna_path != old_path) {
793 bActionGroup *agrp = fcu->grp;
794 is_changed = true;
795 if (oldName != nullptr && (agrp != nullptr) && STREQ(oldName, agrp->name)) {
796 STRNCPY_UTF8(agrp->name, newName);
797 }
798 }
799 }
800 return is_changed;
801}
802
803/* Check RNA-Paths for a list of Drivers */
804static bool drivers_path_rename_fix(ID *owner_id,
805 ID *ref_id,
806 const char *prefix,
807 const char *oldName,
808 const char *newName,
809 const char *oldKey,
810 const char *newKey,
811 ListBase *curves,
812 bool verify_paths)
813{
814 bool is_changed = false;
815 /* We need to check every curve - drivers are F-Curves too. */
816 LISTBASE_FOREACH (FCurve *, fcu, curves) {
817 /* firstly, handle the F-Curve's own path */
818 if (fcu->rna_path != nullptr) {
819 const char *old_rna_path = fcu->rna_path;
820 fcu->rna_path = rna_path_rename_fix(
821 owner_id, prefix, oldKey, newKey, fcu->rna_path, verify_paths);
822 is_changed |= (fcu->rna_path != old_rna_path);
823 }
824 if (fcu->driver == nullptr) {
825 continue;
826 }
827 ChannelDriver *driver = fcu->driver;
828 /* driver variables */
829 LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
830 /* only change the used targets, since the others will need fixing manually anyway */
832 /* rename RNA path */
833 if (dtar->rna_path && dtar->id) {
834 const char *old_rna_path = dtar->rna_path;
835 dtar->rna_path = rna_path_rename_fix(
836 dtar->id, prefix, oldKey, newKey, dtar->rna_path, verify_paths);
837 is_changed |= (dtar->rna_path != old_rna_path);
838 }
839 /* also fix the bone-name (if applicable) */
840 if (strstr(prefix, "bones")) {
841 if (((dtar->id) && (GS(dtar->id->name) == ID_OB) &&
842 (!ref_id || ((Object *)(dtar->id))->data == ref_id)) &&
843 (dtar->pchan_name[0]) && STREQ(oldName, dtar->pchan_name))
844 {
845 is_changed = true;
846 STRNCPY(dtar->pchan_name, newName);
847 }
848 }
849 }
851 }
852 }
853 return is_changed;
854}
855
856/* Fix all RNA-Paths for Actions linked to NLA Strips */
857static bool nlastrips_path_rename_fix(ID *owner_id,
858 const char *prefix,
859 const char *oldName,
860 const char *newName,
861 const char *oldKey,
862 const char *newKey,
863 ListBase *strips,
864 bool verify_paths)
865{
866 bool is_changed = false;
867 /* Recursively check strips, fixing only actions. */
868 LISTBASE_FOREACH (NlaStrip *, strip, strips) {
869 /* fix strip's action */
870 if (strip->act != nullptr) {
872 strip->act, strip->action_slot_handle);
873 const bool is_changed_action = fcurves_path_rename_fix(
874 owner_id, prefix, oldName, newName, oldKey, newKey, fcurves, verify_paths);
875 if (is_changed_action) {
876 DEG_id_tag_update(&strip->act->id, ID_RECALC_ANIMATION);
877 }
878 is_changed |= is_changed_action;
879 }
880 /* Ignore own F-Curves, since those are local. */
881 /* Check sub-strips (if meta-strips). */
882 is_changed |= nlastrips_path_rename_fix(
883 owner_id, prefix, oldName, newName, oldKey, newKey, &strip->strips, verify_paths);
884 }
885 return is_changed;
886}
887
888/* Rename Sub-ID Entities in RNA Paths ----------------------- */
889
891 char *old_path,
892 const char *prefix,
893 const char *oldName,
894 const char *newName,
895 int oldSubscript,
896 int newSubscript,
897 bool verify_paths)
898{
899 char *oldN, *newN;
900 char *result;
901
902 /* if no action, no need to proceed */
903 if (ELEM(nullptr, owner_id, old_path)) {
904 if (G.debug & G_DEBUG) {
905 CLOG_WARN(&LOG, "early abort");
906 }
907 return old_path;
908 }
909
910 /* Name sanitation logic - copied from BKE_animdata_fix_paths_rename() */
911 if ((oldName != nullptr) && (newName != nullptr)) {
912 /* pad the names with [" "] so that only exact matches are made */
913 const size_t name_old_len = strlen(oldName);
914 const size_t name_new_len = strlen(newName);
915 char *name_old_esc = static_cast<char *>(
916 BLI_array_alloca(name_old_esc, (name_old_len * 2) + 1));
917 char *name_new_esc = static_cast<char *>(
918 BLI_array_alloca(name_new_esc, (name_new_len * 2) + 1));
919
920 BLI_str_escape(name_old_esc, oldName, (name_old_len * 2) + 1);
921 BLI_str_escape(name_new_esc, newName, (name_new_len * 2) + 1);
922 oldN = BLI_sprintfN("[\"%s\"]", name_old_esc);
923 newN = BLI_sprintfN("[\"%s\"]", name_new_esc);
924 }
925 else {
926 oldN = BLI_sprintfN("[%d]", oldSubscript);
927 newN = BLI_sprintfN("[%d]", newSubscript);
928 }
929
930 /* fix given path */
931 if (G.debug & G_DEBUG) {
932 printf("%s | %s | oldpath = %p ", oldN, newN, old_path);
933 }
934 result = rna_path_rename_fix(owner_id, prefix, oldN, newN, old_path, verify_paths);
935 if (G.debug & G_DEBUG) {
936 printf("path rename result = %p\n", result);
937 }
938
939 /* free the temp names */
940 MEM_freeN(oldN);
941 MEM_freeN(newN);
942
943 /* return the resulting path - may be the same path again if nothing changed */
944 return result;
945}
946
948 bAction *act,
949 animrig::slot_handle_t slot_handle,
950 const char *prefix,
951 const char *oldName,
952 const char *newName,
953 int oldSubscript,
954 int newSubscript,
955 bool verify_paths)
956{
957 char *oldN, *newN;
958
959 /* if no action, no need to proceed */
960 if (ELEM(nullptr, owner_id, act)) {
961 return;
962 }
963
964 /* Name sanitation logic - copied from BKE_animdata_fix_paths_rename() */
965 if ((oldName != nullptr) && (newName != nullptr)) {
966 /* pad the names with [" "] so that only exact matches are made */
967 const size_t name_old_len = strlen(oldName);
968 const size_t name_new_len = strlen(newName);
969 char *name_old_esc = static_cast<char *>(
970 BLI_array_alloca(name_old_esc, (name_old_len * 2) + 1));
971 char *name_new_esc = static_cast<char *>(
972 BLI_array_alloca(name_new_esc, (name_new_len * 2) + 1));
973
974 BLI_str_escape(name_old_esc, oldName, (name_old_len * 2) + 1);
975 BLI_str_escape(name_new_esc, newName, (name_new_len * 2) + 1);
976 oldN = BLI_sprintfN("[\"%s\"]", name_old_esc);
977 newN = BLI_sprintfN("[\"%s\"]", name_new_esc);
978 }
979 else {
980 oldN = BLI_sprintfN("[%d]", oldSubscript);
981 newN = BLI_sprintfN("[%d]", newSubscript);
982 }
983
984 /* fix paths in action */
986 prefix,
987 oldName,
988 newName,
989 oldN,
990 newN,
992 verify_paths);
993
994 /* free the temp names */
995 MEM_freeN(oldN);
996 MEM_freeN(newN);
997
999}
1000
1002 AnimData *adt,
1003 ID *ref_id,
1004 const char *prefix,
1005 const char *oldName,
1006 const char *newName,
1007 int oldSubscript,
1008 int newSubscript,
1009 bool verify_paths)
1010{
1011 char *oldN, *newN;
1012 /* If no AnimData, no need to proceed. */
1013 if (ELEM(nullptr, owner_id, adt)) {
1014 return;
1015 }
1016 bool is_self_changed = false;
1017 /* Name sanitation logic - shared with BKE_action_fix_paths_rename(). */
1018 if ((oldName != nullptr) && (newName != nullptr)) {
1019 /* Pad the names with [" "] so that only exact matches are made. */
1020 const size_t name_old_len = strlen(oldName);
1021 const size_t name_new_len = strlen(newName);
1022 char *name_old_esc = static_cast<char *>(
1023 BLI_array_alloca(name_old_esc, (name_old_len * 2) + 1));
1024 char *name_new_esc = static_cast<char *>(
1025 BLI_array_alloca(name_new_esc, (name_new_len * 2) + 1));
1026
1027 BLI_str_escape(name_old_esc, oldName, (name_old_len * 2) + 1);
1028 BLI_str_escape(name_new_esc, newName, (name_new_len * 2) + 1);
1029 oldN = BLI_sprintfN("[\"%s\"]", name_old_esc);
1030 newN = BLI_sprintfN("[\"%s\"]", name_new_esc);
1031 }
1032 else {
1033 oldN = BLI_sprintfN("[%d]", oldSubscript);
1034 newN = BLI_sprintfN("[%d]", newSubscript);
1035 }
1036 /* Active action and temp action. */
1037 if (adt->action != nullptr && adt->slot_handle != blender::animrig::Slot::unassigned) {
1039 adt->action, adt->slot_handle);
1041 owner_id, prefix, oldName, newName, oldN, newN, fcurves, verify_paths))
1042 {
1044 }
1045 }
1046 if (adt->tmpact) {
1048 adt->tmpact, adt->tmp_slot_handle);
1050 owner_id, prefix, oldName, newName, oldN, newN, fcurves, verify_paths))
1051 {
1053 }
1054 }
1055 /* Drivers - Drivers are really F-Curves */
1056 is_self_changed |= drivers_path_rename_fix(
1057 owner_id, ref_id, prefix, oldName, newName, oldN, newN, &adt->drivers, verify_paths);
1058 /* NLA Data - Animation Data for Strips */
1059 LISTBASE_FOREACH (NlaTrack *, nlt, &adt->nla_tracks) {
1060 is_self_changed |= nlastrips_path_rename_fix(
1061 owner_id, prefix, oldName, newName, oldN, newN, &nlt->strips, verify_paths);
1062 }
1063 /* Tag owner ID if it */
1064 if (is_self_changed) {
1066 }
1067 /* free the temp names */
1068 MEM_freeN(oldN);
1069 MEM_freeN(newN);
1070}
1071
1072/* Remove FCurves with Prefix -------------------------------------- */
1073
1075static bool fcurves_path_remove_from_listbase(const char *prefix, ListBase *curves)
1076{
1077 FCurve *fcu, *fcn;
1078 bool any_removed = false;
1079 if (!prefix) {
1080 return any_removed;
1081 }
1082
1083 /* we need to check every curve... */
1084 for (fcu = static_cast<FCurve *>(curves->first); fcu; fcu = fcn) {
1085 fcn = fcu->next;
1086
1087 if (fcu->rna_path) {
1088 if (STRPREFIX(fcu->rna_path, prefix)) {
1089 BLI_remlink(curves, fcu);
1090 BKE_fcurve_free(fcu);
1091 any_removed = true;
1092 }
1093 }
1094 }
1095 return any_removed;
1096}
1097
1098/* Check RNA-Paths for a list of F-Curves */
1099static bool nlastrips_path_remove_fix(const char *prefix, ListBase *strips)
1100{
1101 bool any_removed = false;
1102
1103 /* recursively check strips, fixing only actions... */
1104 LISTBASE_FOREACH (NlaStrip *, strip, strips) {
1105 /* fix strip's action */
1106 if (strip->act) {
1108 *strip->act, strip->action_slot_handle, prefix);
1109 }
1110
1111 /* Check sub-strips (if meta-strips). */
1112 any_removed |= nlastrips_path_remove_fix(prefix, &strip->strips);
1113 }
1114
1115 return any_removed;
1116}
1117
1118bool BKE_animdata_fix_paths_remove(ID *id, const char *prefix)
1119{
1120 AnimData *adt = BKE_animdata_from_id(id);
1121 if (!adt) {
1122 return false;
1123 }
1124
1125 bool any_removed = false;
1126
1127 /* Actions. */
1128 if (adt->action) {
1129 any_removed |= animrig::legacy::action_fcurves_remove(*adt->action, adt->slot_handle, prefix);
1130 }
1131 if (adt->tmpact) {
1133 *adt->action, adt->tmp_slot_handle, prefix);
1134 }
1135
1136 /* Drivers. */
1137 any_removed |= fcurves_path_remove_from_listbase(prefix, &adt->drivers);
1138
1139 /* NLA strips. */
1140 LISTBASE_FOREACH (NlaTrack *, nlt, &adt->nla_tracks) {
1141 any_removed |= nlastrips_path_remove_fix(prefix, &nlt->strips);
1142 }
1143
1144 return any_removed;
1145}
1146
1147bool BKE_animdata_driver_path_remove(ID *id, const char *prefix)
1148{
1149 AnimData *adt = BKE_animdata_from_id(id);
1150 if (!adt) {
1151 return false;
1152 }
1153
1154 const bool any_removed = fcurves_path_remove_from_listbase(prefix, &adt->drivers);
1155 return any_removed;
1156}
1157
1159{
1160 PointerRNA constraint_ptr = RNA_pointer_create_discrete(&owner_id, &type, data);
1161 const std::optional<std::string> base_path = RNA_path_from_ID_to_struct(&constraint_ptr);
1162 if (!base_path.has_value()) {
1163 /* The data should exist, so the path should always resolve. */
1165 }
1166
1167 return BKE_animdata_driver_path_remove(&owner_id, base_path.value().c_str());
1168}
1169
1170/* Apply Op to All FCurves in Database --------------------------- */
1171
1178
1179/* Helper for adt_apply_all_fcurves_cb() - Apply wrapped operator to list of F-Curves */
1181{
1182 for (FCurve *fcu : fcurves) {
1183 if (!func(id, fcu)) {
1184 return false;
1185 }
1186 }
1187 return true;
1188}
1189static bool fcurves_listbase_apply_cb(ID *id, ListBase *fcurves, const IDFCurveCallback func)
1190{
1191 LISTBASE_FOREACH (FCurve *, fcu, fcurves) {
1192 if (!func(id, fcu)) {
1193 return false;
1194 }
1195 }
1196 return true;
1197}
1198
1199/* Helper for adt_apply_all_fcurves_cb() - Recursively go through each NLA strip */
1200static bool nlastrips_apply_all_curves_cb(ID *id, ListBase *strips, const IDFCurveCallback func)
1201{
1202 /* This function is used (via `BKE_fcurves_id_cb()`) by the versioning system.
1203 * As such, legacy Actions should always be expected here. */
1204
1205 LISTBASE_FOREACH (NlaStrip *, strip, strips) {
1206 if (strip->act) {
1208 strip->act, strip->action_slot_handle);
1209 if (!fcurves_apply_cb(id, fcurves, func)) {
1210 return false;
1211 }
1212 }
1213
1214 /* Check sub-strips (if meta-strips). */
1215 if (!nlastrips_apply_all_curves_cb(id, &strip->strips, func)) {
1216 return false;
1217 }
1218 }
1219 return true;
1220}
1221
1229static bool adt_apply_all_fcurves_cb(ID *id, AnimData *adt, const IDFCurveCallback func)
1230{
1231 /* This function is used (via `BKE_fcurves_id_cb()`) by the versioning system.
1232 * As such, legacy Actions should always be expected here. */
1233
1234 if (adt->action) {
1235 if (!fcurves_apply_cb(
1236 id,
1238 func))
1239 {
1240 return false;
1241 }
1242 }
1243
1244 if (adt->tmpact) {
1245 if (!fcurves_apply_cb(
1246 id,
1248 func))
1249 {
1250 return false;
1251 }
1252 }
1253
1254 /* Drivers, stored as a list of F-Curves. */
1255 if (!fcurves_listbase_apply_cb(id, &adt->drivers, func)) {
1256 return false;
1257 }
1258
1259 /* NLA Data - Animation Data for Strips */
1260 LISTBASE_FOREACH (NlaTrack *, nlt, &adt->nla_tracks) {
1261 if (!BKE_nlatrack_is_enabled(*adt, *nlt)) {
1262 continue;
1263 }
1264 if (!nlastrips_apply_all_curves_cb(id, &nlt->strips, func)) {
1265 return false;
1266 }
1267 }
1268 return true;
1269}
1270
1271void BKE_fcurves_id_cb(ID *id, const FunctionRef<void(ID *, FCurve *)> func)
1272{
1273 AnimData *adt = BKE_animdata_from_id(id);
1274 if (adt != nullptr) {
1275 /* Use a little wrapper function to always return 'true' and thus keep the loop looping. */
1276 const auto wrapper = [&func](ID *id, FCurve *fcurve) {
1277 func(id, fcurve);
1278 return true;
1279 };
1280 adt_apply_all_fcurves_cb(id, adt, wrapper);
1281 }
1282}
1283
1284void BKE_fcurves_main_cb(Main *bmain, const FunctionRef<void(ID *, FCurve *)> func)
1285{
1286 /* Use a little wrapper function to always return 'true' and thus keep the loop looping. */
1287 const auto wrapper = [&func](ID *id, FCurve *fcurve) {
1288 func(id, fcurve);
1289 return true;
1290 };
1291
1292 /* Use the AnimData-based function so that we don't have to reimplement all that stuff */
1294 [&](ID *id, AnimData *adt) { adt_apply_all_fcurves_cb(id, adt, wrapper); });
1295}
1296
1297/* .blend file API -------------------------------------------- */
1298
1300{
1301 AnimData *adt = BKE_animdata_from_id(id);
1302 if (!adt) {
1303 return;
1304 }
1305
1306 /* firstly, just write the AnimData block */
1307 BLO_write_struct(writer, AnimData, adt);
1308
1309 /* write drivers */
1311
1312 /* write overrides */
1313 /* FIXME: are these needed? */
1314 LISTBASE_FOREACH (AnimOverride *, aor, &adt->overrides) {
1315 /* overrides consist of base data + rna_path */
1316 BLO_write_struct(writer, AnimOverride, aor);
1317 BLO_write_string(writer, aor->rna_path);
1318 }
1319
1320 /* TODO: write the remaps (if they are needed). */
1321
1322 /* write NLA data */
1323 BKE_nla_blend_write(writer, &adt->nla_tracks);
1324}
1325
1327{
1328 IdAdtTemplate *iat = id_can_have_animdata(id) ? reinterpret_cast<IdAdtTemplate *>(id) : nullptr;
1329 if (!iat || !iat->adt) {
1330 return;
1331 }
1332
1333 AnimData *adt = static_cast<AnimData *>(BLO_read_struct(reader, AnimData, &iat->adt));
1334 if (adt == nullptr) {
1335 return;
1336 }
1337
1338 /* link drivers */
1339 BLO_read_struct_list(reader, FCurve, &adt->drivers);
1341 adt->driver_array = nullptr;
1342
1343 /* link overrides */
1344 /* TODO... */
1345
1346 /* link NLA-data */
1348 BKE_nla_blend_read_data(reader, id, &adt->nla_tracks);
1349
1350 /* relink active track/strip - even though strictly speaking this should only be used
1351 * if we're in 'tweaking mode', we need to be able to have this loaded back for
1352 * undo, but also since users may not exit tweak-mode before saving (#24535).
1353 */
1354 /* TODO: it's not really nice that anyone should be able to save the file in this
1355 * state, but it's going to be too hard to enforce this single case. */
1356 BLO_read_struct(reader, NlaTrack, &adt->act_track);
1357 BLO_read_struct(reader, NlaStrip, &adt->actstrip);
1358
1359 if (ID_IS_LINKED(id)) {
1360 /* Linked NLAs should never be in tweak mode, as you cannot exit that on linked data. */
1362 }
1363}
1364
1366{
1367 AnimData *adt = BKE_animdata_from_id(id);
1368 if (!adt) {
1369 return;
1370 }
1371
1373}
1374
1375namespace blender::bke::animdata {
1376
1381
1382bool prop_is_animated(const AnimData *adt, const StringRefNull rna_path, const int array_index)
1383{
1384 if (!adt) {
1385 /* If there is no animdata, it's clear the property is not animated. */
1386 return false;
1387 }
1388
1389 /* The const_cast is used because adt_apply_all_fcurves_cb() wants to yield a
1390 * mutable F-Curve and thus gets a mutable AnimData. The function itself is
1391 * not modifying anything, so this case should be safe. */
1392 const bool looped_until_end = adt_apply_all_fcurves_cb(
1393 nullptr, const_cast<AnimData *>(adt), [&](const ID *, const FCurve *fcurve) {
1394 /* Looping should stop (so return false) when the F-Curve was found. */
1395 return !(array_index == fcurve->array_index && rna_path == fcurve->rna_path);
1396 });
1397
1398 return !looped_until_end;
1399}
1400
1401} // namespace blender::bke::animdata
Functionality to iterate an Action in various ways.
Functions for backward compatibility with the legacy Action API.
Blender kernel action and pose functionality.
eAnimData_MergeCopy_Modes
@ ADT_MERGECOPY_SRC_COPY
@ ADT_MERGECOPY_SRC_REF
void BKE_animdata_main_cb(struct Main *bmain, blender::FunctionRef< void(ID *, AnimData *)> func)
void BKE_fcurve_foreach_id(FCurve *fcu, LibraryForeachIDData *data)
void BKE_fcurve_blend_write_listbase(BlendWriter *writer, ListBase *fcurves)
void BKE_fcurve_blend_read_data_listbase(BlendDataReader *reader, ListBase *fcurves)
void BKE_fcurves_free(ListBase *list)
void BKE_fcurves_copy(ListBase *dst, ListBase *src)
void BKE_fcurve_free(FCurve *fcu)
#define DRIVER_TARGETS_USED_LOOPER_BEGIN(dvar)
#define DRIVER_TARGETS_LOOPER_END
@ G_DEBUG
@ IDTYPE_FLAGS_NO_ANIMDATA
Definition BKE_idtype.hh:49
const IDTypeInfo * BKE_idtype_get_info_from_idcode(short id_code)
Definition idtype.cc:141
struct ID * BKE_id_copy_in_lib(Main *bmain, std::optional< Library * > owner_library, const ID *id, std::optional< const ID * > new_owner_id, ID **new_id_p, int flag)
Definition lib_id.cc:675
void id_us_plus(ID *id)
Definition lib_id.cc:358
ID * BKE_id_copy(Main *bmain, const ID *id)
Definition lib_id.cc:782
@ LIB_ID_COPY_ACTIONS
@ LIB_ID_CREATE_NO_USER_REFCOUNT
@ LIB_ID_CREATE_NO_MAIN
#define BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data_, func_call_)
#define BKE_LIB_FOREACHID_PROCESS_IDSUPER(data_, id_super_, cb_flag_)
@ IDWALK_CB_USER
void BKE_nla_tracks_copy_from_adt(Main *bmain, AnimData *adt_dest, const AnimData *adt_source, int flag)
void BKE_nla_tweakmode_exit(OwnedAnimData owned_adt)
void BKE_nla_tweakmode_exit_nofollowptr(AnimData *adt)
void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, const ListBase *src, int flag)
void BKE_nla_blend_write(BlendWriter *writer, ListBase *tracks)
void BKE_nla_tracks_free(ListBase *tracks, bool do_id_user)
void BKE_nla_liboverride_post_process(ID *id, AnimData *adt)
bool BKE_nlatrack_is_enabled(const AnimData &adt, const NlaTrack &nlt)
void BKE_nla_blend_read_data(BlendDataReader *reader, ID *id_owner, ListBase *tracks)
void BKE_nla_strip_foreach_id(NlaStrip *strip, LibraryForeachIDData *data)
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:18
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
A dynamically sized string ADT.
char * BLI_dynstr_get_cstring(const DynStr *ds) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
void BLI_dynstr_nappend(DynStr *__restrict ds, const char *cstr, int len) ATTR_NONNULL()
Definition BLI_dynstr.cc:75
DynStr * BLI_dynstr_new(void) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_dynstr.cc:37
void BLI_dynstr_free(DynStr *ds) ATTR_NONNULL()
void BLI_dynstr_append(DynStr *__restrict ds, const char *cstr) ATTR_NONNULL()
Definition BLI_dynstr.cc:56
void void void BLI_movelisttolist(ListBase *dst, ListBase *src) ATTR_NONNULL(1
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(ListBase *lb)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
char * BLI_sprintfN(const char *__restrict format,...) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_PRINTF_FORMAT(1
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
size_t BLI_str_escape(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define STRNCPY_UTF8(dst, src)
unsigned int uint
#define STRPREFIX(a, b)
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define STREQ(a, b)
#define BLO_write_struct(writer, struct_name, data_ptr)
void BLO_write_string(BlendWriter *writer, const char *data_ptr)
#define BLO_read_struct_list(reader, struct_name, list)
#define BLO_read_struct(reader, struct_name, ptr_p)
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:188
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:189
void DEG_id_tag_update(ID *id, unsigned int flags)
ID and Library types, which are fundamental for SDNA.
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
@ ID_RECALC_ANIMATION
Definition DNA_ID.h:1077
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:694
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:730
#define ID_NEW_SET(_id, _idn)
Definition DNA_ID.h:756
@ ID_OB
@ ADT_NLA_EDIT_ON
@ USER_DUP_LINKED_ID
@ USER_DUP_ACT
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
static bool fcurves_path_remove_from_listbase(const char *prefix, ListBase *curves)
bool BKE_animdata_copy_id(Main *bmain, ID *id_to, ID *id_from, const int flag)
Definition anim_data.cc:369
bool BKE_animdata_id_is_animated(const ID *id)
Definition anim_data.cc:238
static bool adt_apply_all_fcurves_cb(ID *id, AnimData *adt, const IDFCurveCallback func)
static void animdata_copy_id_action(Main *bmain, ID *id, const bool set_newid, const bool do_linked_id)
Definition anim_data.cc:388
static bool nlastrips_apply_all_curves_cb(ID *id, ListBase *strips, const IDFCurveCallback func)
bool BKE_animdata_action_ensure_idroot(const ID *owner, bAction *action)
Definition anim_data.cc:159
void BKE_action_fix_paths_rename(ID *owner_id, bAction *act, animrig::slot_handle_t slot_handle, const char *prefix, const char *oldName, const char *newName, int oldSubscript, int newSubscript, bool verify_paths)
Definition anim_data.cc:947
bool BKE_animdata_fix_paths_remove(ID *id, const char *prefix)
void BKE_animdata_merge_copy(Main *bmain, ID *dst_id, ID *src_id, eAnimData_MergeCopy_Modes action_mode, bool fix_drivers)
Definition anim_data.cc:447
void BKE_animdata_blend_write(BlendWriter *writer, ID *id)
static bool drivers_path_rename_fix(ID *owner_id, ID *ref_id, const char *prefix, const char *oldName, const char *newName, const char *oldKey, const char *newKey, ListBase *curves, bool verify_paths)
Definition anim_data.cc:804
static bool check_rna_path_is_valid(ID *owner_id, const char *path)
Definition anim_data.cc:689
AnimData * BKE_animdata_ensure_id(ID *id)
Definition anim_data.cc:97
static bool nlastrips_path_rename_fix(ID *owner_id, const char *prefix, const char *oldName, const char *newName, const char *oldKey, const char *newKey, ListBase *strips, bool verify_paths)
Definition anim_data.cc:857
bool id_can_have_animdata(const ID *id)
Definition anim_data.cc:73
static bool fcurves_apply_cb(ID *id, blender::Span< FCurve * > fcurves, const IDFCurveCallback func)
void BKE_animdata_foreach_id(AnimData *adt, LibraryForeachIDData *data)
Definition anim_data.cc:263
bool id_type_can_have_animdata(const short id_type)
Definition anim_data.cc:64
void BKE_fcurves_id_cb(ID *id, const FunctionRef< void(ID *, FCurve *)> func)
bool BKE_animdata_action_editable(const AnimData *adt)
Definition anim_data.cc:151
bool BKE_animdata_drivers_remove_for_rna_struct(ID &owner_id, StructRNA &type, void *data)
FunctionRef< bool(ID *, FCurve *)> IDFCurveCallback
bool BKE_animdata_set_action(ReportList *reports, ID *id, bAction *act)
Definition anim_data.cc:125
void BKE_animdata_free(ID *id, const bool do_id_user)
Definition anim_data.cc:188
static bool fcurves_listbase_apply_cb(ID *id, ListBase *fcurves, const IDFCurveCallback func)
static bool animpath_matches_basepath(const char path[], const char basepath[])
Definition anim_data.cc:529
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:83
void BKE_fcurves_main_cb(Main *bmain, const FunctionRef< void(ID *, FCurve *)> func)
static bool nlastrips_path_remove_fix(const char *prefix, ListBase *strips)
void BKE_animdata_blend_read_data(BlendDataReader *reader, ID *id)
static char * rna_path_rename_fix(ID *owner_id, const char *prefix, const char *oldName, const char *newName, char *oldpath, bool verify_paths)
Definition anim_data.cc:704
AnimData * BKE_animdata_copy_in_lib(Main *bmain, std::optional< Library * > owner_library, AnimData *adt, const int flag)
Definition anim_data.cc:281
void BKE_animdata_liboverride_post_process(ID *id)
void BKE_animdata_duplicate_id_action(Main *bmain, ID *id, const uint duplicate_flags)
Definition anim_data.cc:438
static void animpath_update_basepath(FCurve *fcu, const char *old_basepath, const char *new_basepath)
Definition anim_data.cc:535
void BKE_animdata_transfer_by_basepath(Main *bmain, ID *srcID, ID *dstID, ListBase *basepaths)
Definition anim_data.cc:611
void BKE_animdata_copy_id_action(Main *bmain, ID *id)
Definition anim_data.cc:432
static void action_move_fcurves_by_basepath(bAction *srcAct, const animrig::slot_handle_t src_slot_handle, bAction *dstAct, const animrig::slot_handle_t dst_slot_handle, const char *src_basepath, const char *dst_basepath)
Definition anim_data.cc:555
char * BKE_animsys_fix_rna_path_rename(ID *owner_id, char *old_path, const char *prefix, const char *oldName, const char *newName, int oldSubscript, int newSubscript, bool verify_paths)
Definition anim_data.cc:890
AnimData * BKE_animdata_copy(Main *bmain, AnimData *adt, const int flag)
Definition anim_data.cc:364
bool BKE_animdata_driver_path_remove(ID *id, const char *prefix)
static void animdata_move_drivers_by_basepath(AnimData *srcAdt, AnimData *dstAdt, const char *src_basepath, const char *dst_basepath)
Definition anim_data.cc:595
void BKE_animdata_fix_paths_rename(ID *owner_id, AnimData *adt, ID *ref_id, const char *prefix, const char *oldName, const char *newName, int oldSubscript, int newSubscript, bool verify_paths)
static bool fcurves_path_rename_fix(ID *owner_id, const char *prefix, const char *oldName, const char *newName, const char *oldKey, const char *newKey, blender::Span< FCurve * > curves, bool verify_paths)
Definition anim_data.cc:770
BMesh const char void * data
void append(const T &value)
Slot & slot_add_for_id(const ID &animated_id)
bool is_slot_animated(slot_handle_t slot_handle) const
static void users_invalidate(Main &bmain)
static constexpr slot_handle_t unassigned
#define GS(x)
#define printf(...)
#define LOG(level)
Definition log.h:97
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define G(x, y, z)
Vector< FCurve * > fcurves_for_action_slot(bAction *action, slot_handle_t slot_handle)
bool action_fcurves_remove(bAction &action, slot_handle_t slot_handle, StringRefNull rna_path_prefix)
bool action_treat_as_legacy(const bAction &action)
void foreach_fcurve_in_action_slot(Action &action, slot_handle_t handle, FunctionRef< void(FCurve &fcurve)> callback)
void action_fcurve_move(Action &action_dst, slot_handle_t action_slot_dst, Action &action_src, FCurve &fcurve)
Action & action_add(Main &bmain, StringRefNull name)
decltype(::ActionSlot::handle) slot_handle_t
bool assign_tmpaction(bAction *action, OwnedAnimData owned_adt)
bool unassign_action(ID &animated_id)
bool assign_action(bAction *action, ID &animated_id)
void action_slots_user_cache_invalidate(Main &bmain)
bool prop_is_animated(const AnimData *adt, StringRefNull rna_path, int array_index)
bNodeTree * node_tree_from_id(ID *id)
Definition node.cc:4568
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
PointerRNA RNA_id_pointer_create(ID *id)
std::optional< std::string > RNA_path_from_ID_to_struct(const PointerRNA *ptr)
Definition rna_path.cc:1014
bool RNA_path_resolve_property(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop)
Definition rna_path.cc:560
char tmp_last_slot_identifier[258]
bAction * action
NlaStrip * actstrip
ListBase overrides
float act_influence
int32_t slot_handle
FCurve ** driver_array
NlaTrack * act_track
int32_t tmp_slot_handle
bAction * tmpact
ListBase drivers
ListBase nla_tracks
char last_slot_identifier[258]
struct FCurve * next
char * rna_path
int array_index
uint32_t flags
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
void * first
ListBase strips
ListBase curves
ListBase tracks
Definition tracking.cc:71
PointerRNA * ptr
Definition wm_files.cc:4238
uint8_t flag
Definition wm_window.cc:145