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