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 if (set_newid) {
399 ID_NEW_SET(adt->action, cloned_action);
400 }
401
402 /* The Action was cloned, so this should find the same-named slot automatically. */
403 const slot_handle_t orig_slot_handle = adt->slot_handle;
404 const bool assign_ok = assign_action(&cloned_action->wrap(), *id);
405 BLI_assert_msg(assign_ok, "Expected action assignment to work when copying animdata");
406 BLI_assert(orig_slot_handle == adt->slot_handle);
407 UNUSED_VARS_NDEBUG(assign_ok, orig_slot_handle);
408 }
409 if (adt->tmpact && (do_linked_id || !ID_IS_LINKED(adt->tmpact))) {
410 bAction *cloned_action = reinterpret_cast<bAction *>(BKE_id_copy(bmain, &adt->tmpact->id));
411 if (set_newid) {
412 ID_NEW_SET(adt->tmpact, cloned_action);
413 }
414
415 /* The Action was cloned, so this should find the same-named slot automatically. */
416 const slot_handle_t orig_slot_handle = adt->tmp_slot_handle;
417 const bool assign_ok = assign_tmpaction(&cloned_action->wrap(), {*id, *adt});
418 BLI_assert_msg(assign_ok, "Expected tmp-action assignment to work when copying animdata");
419 BLI_assert(orig_slot_handle == adt->tmp_slot_handle);
420 UNUSED_VARS_NDEBUG(assign_ok, orig_slot_handle);
421 }
422 }
424 if (ntree) {
425 animdata_copy_id_action(bmain, &ntree->id, set_newid, do_linked_id);
426 }
427 /* Note that collections are not animatable currently, so no need to handle scenes' master
428 * collection here. */
429}
430
432{
433 const bool is_id_liboverride = ID_IS_OVERRIDE_LIBRARY(id);
434 animdata_copy_id_action(bmain, id, false, !is_id_liboverride);
435}
436
438 ID *id,
439 const /*eDupli_ID_Flags*/ uint duplicate_flags)
440{
441 if (duplicate_flags & USER_DUP_ACT) {
442 animdata_copy_id_action(bmain, id, true, (duplicate_flags & USER_DUP_LINKED_ID) != 0);
443 }
444}
445
447 Main *bmain, ID *dst_id, ID *src_id, eAnimData_MergeCopy_Modes action_mode, bool fix_drivers)
448{
449 AnimData *src = BKE_animdata_from_id(src_id);
450 AnimData *dst = BKE_animdata_from_id(dst_id);
451
452 /* sanity checks */
453 if (ELEM(nullptr, dst, src)) {
454 return;
455 }
456
457 /* TODO: we must unset all "tweak-mode" flags. */
458 if ((src->flag & ADT_NLA_EDIT_ON) || (dst->flag & ADT_NLA_EDIT_ON)) {
460 &LOG,
461 "Merging AnimData blocks while editing NLA is dangerous as it may cause data corruption");
462 return;
463 }
464
465 /* handle actions... */
466 if (action_mode == ADT_MERGECOPY_SRC_COPY) {
467 /* make a copy of the actions */
468 dst->action = (bAction *)BKE_id_copy(bmain, &src->action->id);
469 dst->tmpact = (bAction *)BKE_id_copy(bmain, &src->tmpact->id);
470 }
471 else if (action_mode == ADT_MERGECOPY_SRC_REF) {
472 /* make a reference to it */
473 dst->action = src->action;
474 id_us_plus((ID *)dst->action);
475
476 dst->tmpact = src->tmpact;
477 id_us_plus((ID *)dst->tmpact);
478 }
479 dst->slot_handle = src->slot_handle;
483
484 /* duplicate NLA data */
485 if (src->nla_tracks.first) {
486 ListBase tracks = {nullptr, nullptr};
487
488 BKE_nla_tracks_copy(bmain, &tracks, &src->nla_tracks, 0);
490 }
491
492 /* duplicate drivers (F-Curves) */
493 if (src->drivers.first) {
494 ListBase drivers = {nullptr, nullptr};
495
496 BKE_fcurves_copy(&drivers, &src->drivers);
497
498 /* Fix up all driver targets using the old target id
499 * - This assumes that the src ID is being merged into the dst ID
500 */
501 if (fix_drivers) {
502 LISTBASE_FOREACH (FCurve *, fcu, &drivers) {
503 ChannelDriver *driver = fcu->driver;
504 LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
506 if (dtar->id == src_id) {
507 dtar->id = dst_id;
508 }
509 }
511 }
512 }
513 }
514
515 BLI_movelisttolist(&dst->drivers, &drivers);
516 }
517}
518
519/* Sub-ID Regrouping ------------------------------------------- */
520
528static bool animpath_matches_basepath(const char path[], const char basepath[])
529{
530 /* we need start of path to be basepath */
531 return (path && basepath) && STRPREFIX(path, basepath);
532}
533
535 const char *old_basepath,
536 const char *new_basepath)
537{
538 BLI_assert(animpath_matches_basepath(fcu->rna_path, old_basepath));
539 if (STREQ(old_basepath, new_basepath)) {
540 return;
541 }
542
543 char *new_path = BLI_sprintfN("%s%s", new_basepath, fcu->rna_path + strlen(old_basepath));
544 MEM_freeN(fcu->rna_path);
545 fcu->rna_path = new_path;
546}
547
548/* Move F-Curves in src action to dst action, setting up all the necessary groups
549 * for this to happen, but only if the F-Curves being moved have the appropriate
550 * "base path".
551 * - This is used when data moves from one data-block to another, causing the
552 * F-Curves to need to be moved over too
553 */
555 const animrig::slot_handle_t src_slot_handle,
556 bAction *dstAct,
557 const animrig::slot_handle_t dst_slot_handle,
558 const char *src_basepath,
559 const char *dst_basepath)
560{
561 /* sanity checks */
562 if (ELEM(nullptr, srcAct, dstAct, src_basepath, dst_basepath)) {
563 if (G.debug & G_DEBUG) {
565 "srcAct: %p, dstAct: %p, src_basepath: %p, dst_basepath: %p has insufficient "
566 "info to work with",
567 (void *)srcAct,
568 (void *)dstAct,
569 (void *)src_basepath,
570 (void *)dst_basepath);
571 }
572 return;
573 }
574
575 animrig::Action &source_action = srcAct->wrap();
576 animrig::Action &dest_action = dstAct->wrap();
577
578 /* Get a list of all F-Curves to move. This is done in a separate step so we
579 * don't move the curves while iterating over them at the same time. */
580 Vector<FCurve *> fcurves_to_move;
581 animrig::foreach_fcurve_in_action_slot(source_action, src_slot_handle, [&](FCurve &fcurve) {
582 if (animpath_matches_basepath(fcurve.rna_path, src_basepath)) {
583 fcurves_to_move.append(&fcurve);
584 }
585 });
586
587 /* Move the curves from one Action to the other, and change its path to match the destination. */
588 for (FCurve *fcurve_to_move : fcurves_to_move) {
589 animpath_update_basepath(fcurve_to_move, src_basepath, dst_basepath);
590 animrig::action_fcurve_move(dest_action, dst_slot_handle, source_action, *fcurve_to_move);
591 }
592}
593
595 AnimData *dstAdt,
596 const char *src_basepath,
597 const char *dst_basepath)
598{
599 LISTBASE_FOREACH_MUTABLE (FCurve *, fcu, &srcAdt->drivers) {
600 if (animpath_matches_basepath(fcu->rna_path, src_basepath)) {
601 animpath_update_basepath(fcu, src_basepath, dst_basepath);
602 BLI_remlink(&srcAdt->drivers, fcu);
603 BLI_addtail(&dstAdt->drivers, fcu);
604
605 /* TODO: add depsgraph flushing calls? */
606 }
607 }
608}
609
610void BKE_animdata_transfer_by_basepath(Main *bmain, ID *srcID, ID *dstID, ListBase *basepaths)
611{
612 AnimData *srcAdt = nullptr, *dstAdt = nullptr;
613
614 /* sanity checks */
615 if (ELEM(nullptr, srcID, dstID)) {
616 if (G.debug & G_DEBUG) {
617 CLOG_ERROR(&LOG, "no source or destination ID to separate AnimData with");
618 }
619 return;
620 }
621
622 /* get animdata from src, and create for destination (if needed) */
623 srcAdt = BKE_animdata_from_id(srcID);
624 dstAdt = BKE_animdata_ensure_id(dstID);
625
626 if (ELEM(nullptr, srcAdt, dstAdt)) {
627 if (G.debug & G_DEBUG) {
628 CLOG_ERROR(&LOG, "no AnimData for this pair of ID's");
629 }
630 return;
631 }
632
633 /* active action */
634 if (srcAdt->action) {
635 const OwnedAnimData dst_owned_adt = {*dstID, *dstAdt};
636 if (dstAdt->action == srcAdt->action) {
637 CLOG_WARN(&LOG,
638 "Source and Destination share animation! "
639 "('%s' and '%s' both use '%s') Making new empty action",
640 srcID->name,
641 dstID->name,
642 srcAdt->action->id.name);
643
644 /* This sets dstAdt->action to nullptr. */
645 const bool unassign_ok = animrig::unassign_action(dst_owned_adt);
646 BLI_assert_msg(unassign_ok, "Expected Action unassignment to work");
647 UNUSED_VARS_NDEBUG(unassign_ok);
648 }
649
650 /* Set up an action if necessary, and name it in a similar way so that it
651 * can be easily found again. */
652 if (!dstAdt->action) {
653 animrig::Action &new_action = animrig::action_add(*bmain, srcAdt->action->id.name + 2);
654 new_action.slot_add_for_id(*dstID);
655
656 const bool assign_ok = animrig::assign_action(&new_action, dst_owned_adt);
657 BLI_assert_msg(assign_ok, "Expected Action assignment to work");
658 UNUSED_VARS_NDEBUG(assign_ok);
659 BLI_assert(dstAdt->slot_handle != animrig::Slot::unassigned);
660 }
661
662 /* loop over base paths, trying to fix for each one... */
663 LISTBASE_FOREACH (const AnimationBasePathChange *, basepath_change, basepaths) {
665 srcAdt->slot_handle,
666 dstAdt->action,
667 dstAdt->slot_handle,
668 basepath_change->src_basepath,
669 basepath_change->dst_basepath);
670 }
671 }
672
673 /* drivers */
674 if (srcAdt->drivers.first) {
675 LISTBASE_FOREACH (const AnimationBasePathChange *, basepath_change, basepaths) {
677 srcAdt, dstAdt, basepath_change->src_basepath, basepath_change->dst_basepath);
678 }
679 }
680 /* Tag source action because list of fcurves changed. */
682}
683
684/* Path Validation -------------------------------------------- */
685
686/* Check if a given RNA Path is valid, by tracing it from the given ID,
687 * and seeing if we can resolve it. */
688static bool check_rna_path_is_valid(ID *owner_id, const char *path)
689{
691 PropertyRNA *prop = nullptr;
692
693 /* make initial RNA pointer to start resolving from */
694 PointerRNA id_ptr = RNA_id_pointer_create(owner_id);
695
696 /* try to resolve */
697 return RNA_path_resolve_property(&id_ptr, path, &ptr, &prop);
698}
699
700/* Check if some given RNA Path needs fixing - free the given path and set a new one as appropriate
701 * NOTE: we assume that oldName and newName have [" "] padding around them
702 */
703static char *rna_path_rename_fix(ID *owner_id,
704 const char *prefix,
705 const char *oldName,
706 const char *newName,
707 char *oldpath,
708 bool verify_paths)
709{
710 char *prefixPtr = strstr(oldpath, prefix);
711 if (prefixPtr == nullptr) {
712 return oldpath;
713 }
714
715 char *oldNamePtr = strstr(oldpath, oldName);
716 if (oldNamePtr == nullptr) {
717 return oldpath;
718 }
719
720 int prefixLen = strlen(prefix);
721 int oldNameLen = strlen(oldName);
722
723 /* only start fixing the path if the prefix and oldName feature in the path,
724 * and prefix occurs immediately before oldName
725 */
726 if (prefixPtr + prefixLen == oldNamePtr) {
727 /* if we haven't aren't able to resolve the path now, try again after fixing it */
728 if (!verify_paths || check_rna_path_is_valid(owner_id, oldpath) == 0) {
729 DynStr *ds = BLI_dynstr_new();
730 const char *postfixPtr = oldNamePtr + oldNameLen;
731 char *newPath = nullptr;
732
733 /* add the part of the string that goes up to the start of the prefix */
734 if (prefixPtr > oldpath) {
735 BLI_dynstr_nappend(ds, oldpath, prefixPtr - oldpath);
736 }
737
738 /* add the prefix */
739 BLI_dynstr_append(ds, prefix);
740
741 /* add the new name (complete with brackets) */
742 BLI_dynstr_append(ds, newName);
743
744 /* add the postfix */
745 BLI_dynstr_append(ds, postfixPtr);
746
747 /* create new path, and cleanup old data */
748 newPath = BLI_dynstr_get_cstring(ds);
749 BLI_dynstr_free(ds);
750
751 /* check if the new path will solve our problems */
752 /* TODO: will need to check whether this step really helps in practice */
753 if (!verify_paths || check_rna_path_is_valid(owner_id, newPath)) {
754 /* free the old path, and return the new one, since we've solved the issues */
755 MEM_freeN(oldpath);
756 return newPath;
757 }
758
759 /* still couldn't resolve the path... so, might as well just leave it alone */
760 MEM_freeN(newPath);
761 }
762 }
763
764 /* the old path doesn't need to be changed */
765 return oldpath;
766}
767
768/* Check RNA-Paths for a list of F-Curves */
769static bool fcurves_path_rename_fix(ID *owner_id,
770 const char *prefix,
771 const char *oldName,
772 const char *newName,
773 const char *oldKey,
774 const char *newKey,
776 bool verify_paths)
777{
778 bool is_changed = false;
779 /* We need to check every curve. */
780 for (FCurve *fcu : curves) {
781 if (fcu->rna_path == nullptr) {
782 continue;
783 }
784 const char *old_path = fcu->rna_path;
785 /* Firstly, handle the F-Curve's own path. */
786 fcu->rna_path = rna_path_rename_fix(
787 owner_id, prefix, oldKey, newKey, fcu->rna_path, verify_paths);
788 /* if path changed and the F-Curve is grouped, check if its group also needs renaming
789 * (i.e. F-Curve is first of a bone's F-Curves;
790 * hence renaming this should also trigger rename) */
791 if (fcu->rna_path != old_path) {
792 bActionGroup *agrp = fcu->grp;
793 is_changed = true;
794 if (oldName != nullptr && (agrp != nullptr) && STREQ(oldName, agrp->name)) {
795 STRNCPY(agrp->name, newName);
796 }
797 }
798 }
799 return is_changed;
800}
801
802/* Check RNA-Paths for a list of Drivers */
803static bool drivers_path_rename_fix(ID *owner_id,
804 ID *ref_id,
805 const char *prefix,
806 const char *oldName,
807 const char *newName,
808 const char *oldKey,
809 const char *newKey,
810 ListBase *curves,
811 bool verify_paths)
812{
813 bool is_changed = false;
814 /* We need to check every curve - drivers are F-Curves too. */
815 LISTBASE_FOREACH (FCurve *, fcu, curves) {
816 /* firstly, handle the F-Curve's own path */
817 if (fcu->rna_path != nullptr) {
818 const char *old_rna_path = fcu->rna_path;
819 fcu->rna_path = rna_path_rename_fix(
820 owner_id, prefix, oldKey, newKey, fcu->rna_path, verify_paths);
821 is_changed |= (fcu->rna_path != old_rna_path);
822 }
823 if (fcu->driver == nullptr) {
824 continue;
825 }
826 ChannelDriver *driver = fcu->driver;
827 /* driver variables */
828 LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
829 /* only change the used targets, since the others will need fixing manually anyway */
831 /* rename RNA path */
832 if (dtar->rna_path && dtar->id) {
833 const char *old_rna_path = dtar->rna_path;
834 dtar->rna_path = rna_path_rename_fix(
835 dtar->id, prefix, oldKey, newKey, dtar->rna_path, verify_paths);
836 is_changed |= (dtar->rna_path != old_rna_path);
837 }
838 /* also fix the bone-name (if applicable) */
839 if (strstr(prefix, "bones")) {
840 if (((dtar->id) && (GS(dtar->id->name) == ID_OB) &&
841 (!ref_id || ((Object *)(dtar->id))->data == ref_id)) &&
842 (dtar->pchan_name[0]) && STREQ(oldName, dtar->pchan_name))
843 {
844 is_changed = true;
845 STRNCPY(dtar->pchan_name, newName);
846 }
847 }
848 }
850 }
851 }
852 return is_changed;
853}
854
855/* Fix all RNA-Paths for Actions linked to NLA Strips */
856static bool nlastrips_path_rename_fix(ID *owner_id,
857 const char *prefix,
858 const char *oldName,
859 const char *newName,
860 const char *oldKey,
861 const char *newKey,
862 ListBase *strips,
863 bool verify_paths)
864{
865 bool is_changed = false;
866 /* Recursively check strips, fixing only actions. */
867 LISTBASE_FOREACH (NlaStrip *, strip, strips) {
868 /* fix strip's action */
869 if (strip->act != nullptr) {
870 const bool is_changed_action = fcurves_path_rename_fix(
871 owner_id,
872 prefix,
873 oldName,
874 newName,
875 oldKey,
876 newKey,
878 verify_paths);
879 if (is_changed_action) {
880 DEG_id_tag_update(&strip->act->id, ID_RECALC_ANIMATION);
881 }
882 is_changed |= is_changed_action;
883 }
884 /* Ignore own F-Curves, since those are local. */
885 /* Check sub-strips (if meta-strips). */
886 is_changed |= nlastrips_path_rename_fix(
887 owner_id, prefix, oldName, newName, oldKey, newKey, &strip->strips, verify_paths);
888 }
889 return is_changed;
890}
891
892/* Rename Sub-ID Entities in RNA Paths ----------------------- */
893
895 char *old_path,
896 const char *prefix,
897 const char *oldName,
898 const char *newName,
899 int oldSubscript,
900 int newSubscript,
901 bool verify_paths)
902{
903 char *oldN, *newN;
904 char *result;
905
906 /* if no action, no need to proceed */
907 if (ELEM(nullptr, owner_id, old_path)) {
908 if (G.debug & G_DEBUG) {
909 CLOG_WARN(&LOG, "early abort");
910 }
911 return old_path;
912 }
913
914 /* Name sanitation logic - copied from BKE_animdata_fix_paths_rename() */
915 if ((oldName != nullptr) && (newName != nullptr)) {
916 /* pad the names with [" "] so that only exact matches are made */
917 const size_t name_old_len = strlen(oldName);
918 const size_t name_new_len = strlen(newName);
919 char *name_old_esc = static_cast<char *>(
920 BLI_array_alloca(name_old_esc, (name_old_len * 2) + 1));
921 char *name_new_esc = static_cast<char *>(
922 BLI_array_alloca(name_new_esc, (name_new_len * 2) + 1));
923
924 BLI_str_escape(name_old_esc, oldName, (name_old_len * 2) + 1);
925 BLI_str_escape(name_new_esc, newName, (name_new_len * 2) + 1);
926 oldN = BLI_sprintfN("[\"%s\"]", name_old_esc);
927 newN = BLI_sprintfN("[\"%s\"]", name_new_esc);
928 }
929 else {
930 oldN = BLI_sprintfN("[%d]", oldSubscript);
931 newN = BLI_sprintfN("[%d]", newSubscript);
932 }
933
934 /* fix given path */
935 if (G.debug & G_DEBUG) {
936 printf("%s | %s | oldpath = %p ", oldN, newN, old_path);
937 }
938 result = rna_path_rename_fix(owner_id, prefix, oldN, newN, old_path, verify_paths);
939 if (G.debug & G_DEBUG) {
940 printf("path rename result = %p\n", result);
941 }
942
943 /* free the temp names */
944 MEM_freeN(oldN);
945 MEM_freeN(newN);
946
947 /* return the resulting path - may be the same path again if nothing changed */
948 return result;
949}
950
952 bAction *act,
953 animrig::slot_handle_t slot_handle,
954 const char *prefix,
955 const char *oldName,
956 const char *newName,
957 int oldSubscript,
958 int newSubscript,
959 bool verify_paths)
960{
961 char *oldN, *newN;
962
963 /* if no action, no need to proceed */
964 if (ELEM(nullptr, owner_id, act)) {
965 return;
966 }
967
968 /* Name sanitation logic - copied from BKE_animdata_fix_paths_rename() */
969 if ((oldName != nullptr) && (newName != nullptr)) {
970 /* pad the names with [" "] so that only exact matches are made */
971 const size_t name_old_len = strlen(oldName);
972 const size_t name_new_len = strlen(newName);
973 char *name_old_esc = static_cast<char *>(
974 BLI_array_alloca(name_old_esc, (name_old_len * 2) + 1));
975 char *name_new_esc = static_cast<char *>(
976 BLI_array_alloca(name_new_esc, (name_new_len * 2) + 1));
977
978 BLI_str_escape(name_old_esc, oldName, (name_old_len * 2) + 1);
979 BLI_str_escape(name_new_esc, newName, (name_new_len * 2) + 1);
980 oldN = BLI_sprintfN("[\"%s\"]", name_old_esc);
981 newN = BLI_sprintfN("[\"%s\"]", name_new_esc);
982 }
983 else {
984 oldN = BLI_sprintfN("[%d]", oldSubscript);
985 newN = BLI_sprintfN("[%d]", newSubscript);
986 }
987
988 /* fix paths in action */
990 prefix,
991 oldName,
992 newName,
993 oldN,
994 newN,
996 verify_paths);
997
998 /* free the temp names */
999 MEM_freeN(oldN);
1000 MEM_freeN(newN);
1001}
1002
1004 AnimData *adt,
1005 ID *ref_id,
1006 const char *prefix,
1007 const char *oldName,
1008 const char *newName,
1009 int oldSubscript,
1010 int newSubscript,
1011 bool verify_paths)
1012{
1013 char *oldN, *newN;
1014 /* If no AnimData, no need to proceed. */
1015 if (ELEM(nullptr, owner_id, adt)) {
1016 return;
1017 }
1018 bool is_self_changed = false;
1019 /* Name sanitation logic - shared with BKE_action_fix_paths_rename(). */
1020 if ((oldName != nullptr) && (newName != nullptr)) {
1021 /* Pad the names with [" "] so that only exact matches are made. */
1022 const size_t name_old_len = strlen(oldName);
1023 const size_t name_new_len = strlen(newName);
1024 char *name_old_esc = static_cast<char *>(
1025 BLI_array_alloca(name_old_esc, (name_old_len * 2) + 1));
1026 char *name_new_esc = static_cast<char *>(
1027 BLI_array_alloca(name_new_esc, (name_new_len * 2) + 1));
1028
1029 BLI_str_escape(name_old_esc, oldName, (name_old_len * 2) + 1);
1030 BLI_str_escape(name_new_esc, newName, (name_new_len * 2) + 1);
1031 oldN = BLI_sprintfN("[\"%s\"]", name_old_esc);
1032 newN = BLI_sprintfN("[\"%s\"]", name_new_esc);
1033 }
1034 else {
1035 oldN = BLI_sprintfN("[%d]", oldSubscript);
1036 newN = BLI_sprintfN("[%d]", newSubscript);
1037 }
1038 /* Active action and temp action. */
1039 if (adt->action != nullptr) {
1040 if (fcurves_path_rename_fix(owner_id,
1041 prefix,
1042 oldName,
1043 newName,
1044 oldN,
1045 newN,
1047 verify_paths))
1048 {
1050 }
1051 }
1052 if (adt->tmpact) {
1053 if (fcurves_path_rename_fix(owner_id,
1054 prefix,
1055 oldName,
1056 newName,
1057 oldN,
1058 newN,
1060 verify_paths))
1061 {
1063 }
1064 }
1065 /* Drivers - Drivers are really F-Curves */
1066 is_self_changed |= drivers_path_rename_fix(
1067 owner_id, ref_id, prefix, oldName, newName, oldN, newN, &adt->drivers, verify_paths);
1068 /* NLA Data - Animation Data for Strips */
1069 LISTBASE_FOREACH (NlaTrack *, nlt, &adt->nla_tracks) {
1070 is_self_changed |= nlastrips_path_rename_fix(
1071 owner_id, prefix, oldName, newName, oldN, newN, &nlt->strips, verify_paths);
1072 }
1073 /* Tag owner ID if it */
1074 if (is_self_changed) {
1076 }
1077 /* free the temp names */
1078 MEM_freeN(oldN);
1079 MEM_freeN(newN);
1080}
1081
1082/* Remove FCurves with Prefix -------------------------------------- */
1083
1085static bool fcurves_path_remove_from_listbase(const char *prefix, ListBase *curves)
1086{
1087 FCurve *fcu, *fcn;
1088 bool any_removed = false;
1089 if (!prefix) {
1090 return any_removed;
1091 }
1092
1093 /* we need to check every curve... */
1094 for (fcu = static_cast<FCurve *>(curves->first); fcu; fcu = fcn) {
1095 fcn = fcu->next;
1096
1097 if (fcu->rna_path) {
1098 if (STRPREFIX(fcu->rna_path, prefix)) {
1099 BLI_remlink(curves, fcu);
1100 BKE_fcurve_free(fcu);
1101 any_removed = true;
1102 }
1103 }
1104 }
1105 return any_removed;
1106}
1107
1108/* Check RNA-Paths for a list of F-Curves */
1109static bool nlastrips_path_remove_fix(const char *prefix, ListBase *strips)
1110{
1111 bool any_removed = false;
1112
1113 /* recursively check strips, fixing only actions... */
1114 LISTBASE_FOREACH (NlaStrip *, strip, strips) {
1115 /* fix strip's action */
1116 if (strip->act) {
1118 *strip->act, strip->action_slot_handle, prefix);
1119 }
1120
1121 /* Check sub-strips (if meta-strips). */
1122 any_removed |= nlastrips_path_remove_fix(prefix, &strip->strips);
1123 }
1124
1125 return any_removed;
1126}
1127
1128bool BKE_animdata_fix_paths_remove(ID *id, const char *prefix)
1129{
1130 AnimData *adt = BKE_animdata_from_id(id);
1131 if (!adt) {
1132 return false;
1133 }
1134
1135 bool any_removed = false;
1136
1137 /* Actions. */
1138 if (adt->action) {
1139 any_removed |= animrig::legacy::action_fcurves_remove(*adt->action, adt->slot_handle, prefix);
1140 }
1141 if (adt->tmpact) {
1143 *adt->action, adt->tmp_slot_handle, prefix);
1144 }
1145
1146 /* Drivers. */
1147 any_removed |= fcurves_path_remove_from_listbase(prefix, &adt->drivers);
1148
1149 /* NLA strips. */
1150 LISTBASE_FOREACH (NlaTrack *, nlt, &adt->nla_tracks) {
1151 any_removed |= nlastrips_path_remove_fix(prefix, &nlt->strips);
1152 }
1153
1154 return any_removed;
1155}
1156
1157bool BKE_animdata_driver_path_remove(ID *id, const char *prefix)
1158{
1159 AnimData *adt = BKE_animdata_from_id(id);
1160 if (!adt) {
1161 return false;
1162 }
1163
1164 const bool any_removed = fcurves_path_remove_from_listbase(prefix, &adt->drivers);
1165 return any_removed;
1166}
1167
1169{
1170 PointerRNA constraint_ptr = RNA_pointer_create_discrete(&owner_id, &type, data);
1171 const std::optional<std::string> base_path = RNA_path_from_ID_to_struct(&constraint_ptr);
1172 if (!base_path.has_value()) {
1173 /* The data should exist, so the path should always resolve. */
1175 }
1176
1177 return BKE_animdata_driver_path_remove(&owner_id, base_path.value().c_str());
1178}
1179
1180/* Apply Op to All FCurves in Database --------------------------- */
1181
1188
1189/* Helper for adt_apply_all_fcurves_cb() - Apply wrapped operator to list of F-Curves */
1191{
1192 for (FCurve *fcu : fcurves) {
1193 if (!func(id, fcu)) {
1194 return false;
1195 }
1196 }
1197 return true;
1198}
1199static bool fcurves_listbase_apply_cb(ID *id, ListBase *fcurves, const IDFCurveCallback func)
1200{
1201 LISTBASE_FOREACH (FCurve *, fcu, fcurves) {
1202 if (!func(id, fcu)) {
1203 return false;
1204 }
1205 }
1206 return true;
1207}
1208
1209/* Helper for adt_apply_all_fcurves_cb() - Recursively go through each NLA strip */
1210static bool nlastrips_apply_all_curves_cb(ID *id, ListBase *strips, const IDFCurveCallback func)
1211{
1212 /* This function is used (via `BKE_fcurves_id_cb()`) by the versioning system.
1213 * As such, legacy Actions should always be expected here. */
1214
1215 LISTBASE_FOREACH (NlaStrip *, strip, strips) {
1216 if (strip->act) {
1218 strip->act, strip->action_slot_handle);
1219 if (!fcurves_apply_cb(id, fcurves, func)) {
1220 return false;
1221 }
1222 }
1223
1224 /* Check sub-strips (if meta-strips). */
1225 if (!nlastrips_apply_all_curves_cb(id, &strip->strips, func)) {
1226 return false;
1227 }
1228 }
1229 return true;
1230}
1231
1239static bool adt_apply_all_fcurves_cb(ID *id, AnimData *adt, const IDFCurveCallback func)
1240{
1241 /* This function is used (via `BKE_fcurves_id_cb()`) by the versioning system.
1242 * As such, legacy Actions should always be expected here. */
1243
1244 if (adt->action) {
1245 if (!fcurves_apply_cb(
1246 id,
1248 func))
1249 {
1250 return false;
1251 }
1252 }
1253
1254 if (adt->tmpact) {
1255 if (!fcurves_apply_cb(
1256 id,
1258 func))
1259 {
1260 return false;
1261 }
1262 }
1263
1264 /* Drivers, stored as a list of F-Curves. */
1265 if (!fcurves_listbase_apply_cb(id, &adt->drivers, func)) {
1266 return false;
1267 }
1268
1269 /* NLA Data - Animation Data for Strips */
1270 LISTBASE_FOREACH (NlaTrack *, nlt, &adt->nla_tracks) {
1271 if (!BKE_nlatrack_is_enabled(*adt, *nlt)) {
1272 continue;
1273 }
1274 if (!nlastrips_apply_all_curves_cb(id, &nlt->strips, func)) {
1275 return false;
1276 }
1277 }
1278 return true;
1279}
1280
1281void BKE_fcurves_id_cb(ID *id, const FunctionRef<void(ID *, FCurve *)> func)
1282{
1283 AnimData *adt = BKE_animdata_from_id(id);
1284 if (adt != nullptr) {
1285 /* Use a little wrapper function to always return 'true' and thus keep the loop looping. */
1286 const auto wrapper = [&func](ID *id, FCurve *fcurve) {
1287 func(id, fcurve);
1288 return true;
1289 };
1290 adt_apply_all_fcurves_cb(id, adt, wrapper);
1291 }
1292}
1293
1294void BKE_fcurves_main_cb(Main *bmain, const FunctionRef<void(ID *, FCurve *)> func)
1295{
1296 /* Use a little wrapper function to always return 'true' and thus keep the loop looping. */
1297 const auto wrapper = [&func](ID *id, FCurve *fcurve) {
1298 func(id, fcurve);
1299 return true;
1300 };
1301
1302 /* Use the AnimData-based function so that we don't have to reimplement all that stuff */
1304 [&](ID *id, AnimData *adt) { adt_apply_all_fcurves_cb(id, adt, wrapper); });
1305}
1306
1307/* .blend file API -------------------------------------------- */
1308
1310{
1311 AnimData *adt = BKE_animdata_from_id(id);
1312 if (!adt) {
1313 return;
1314 }
1315
1316 /* firstly, just write the AnimData block */
1317 BLO_write_struct(writer, AnimData, adt);
1318
1319 /* write drivers */
1321
1322 /* write overrides */
1323 /* FIXME: are these needed? */
1324 LISTBASE_FOREACH (AnimOverride *, aor, &adt->overrides) {
1325 /* overrides consist of base data + rna_path */
1326 BLO_write_struct(writer, AnimOverride, aor);
1327 BLO_write_string(writer, aor->rna_path);
1328 }
1329
1330 /* TODO: write the remaps (if they are needed). */
1331
1332 /* write NLA data */
1333 BKE_nla_blend_write(writer, &adt->nla_tracks);
1334}
1335
1337{
1338 IdAdtTemplate *iat = id_can_have_animdata(id) ? reinterpret_cast<IdAdtTemplate *>(id) : nullptr;
1339 if (!iat || !iat->adt) {
1340 return;
1341 }
1342
1343 AnimData *adt = static_cast<AnimData *>(BLO_read_struct(reader, AnimData, &iat->adt));
1344 if (adt == nullptr) {
1345 return;
1346 }
1347
1348 /* link drivers */
1349 BLO_read_struct_list(reader, FCurve, &adt->drivers);
1351 adt->driver_array = nullptr;
1352
1353 /* link overrides */
1354 /* TODO... */
1355
1356 /* link NLA-data */
1358 BKE_nla_blend_read_data(reader, id, &adt->nla_tracks);
1359
1360 /* relink active track/strip - even though strictly speaking this should only be used
1361 * if we're in 'tweaking mode', we need to be able to have this loaded back for
1362 * undo, but also since users may not exit tweak-mode before saving (#24535).
1363 */
1364 /* TODO: it's not really nice that anyone should be able to save the file in this
1365 * state, but it's going to be too hard to enforce this single case. */
1366 BLO_read_struct(reader, NlaTrack, &adt->act_track);
1367 BLO_read_struct(reader, NlaStrip, &adt->actstrip);
1368
1369 if (ID_IS_LINKED(id)) {
1370 /* Linked NLAs should never be in tweak mode, as you cannot exit that on linked data. */
1372 }
1373}
1374
1376{
1377 AnimData *adt = BKE_animdata_from_id(id);
1378 if (!adt) {
1379 return;
1380 }
1381
1383}
1384
1385namespace blender::bke::animdata {
1386
1391
1392bool prop_is_animated(const AnimData *adt, const StringRefNull rna_path, const int array_index)
1393{
1394 if (!adt) {
1395 /* If there is no animdata, it's clear the property is not animated. */
1396 return false;
1397 }
1398
1399 /* The const_cast is used because adt_apply_all_fcurves_cb() wants to yield a
1400 * mutable F-Curve and thus gets a mutable AnimData. The function itself is
1401 * not modifying anything, so this case should be safe. */
1402 const bool looped_until_end = adt_apply_all_fcurves_cb(
1403 nullptr, const_cast<AnimData *>(adt), [&](const ID *, const FCurve *fcurve) {
1404 /* Looping should stop (so return false) when the F-Curve was found. */
1405 return !(array_index == fcurve->array_index && rna_path == fcurve->rna_path);
1406 });
1407
1408 return !looped_until_end;
1409}
1410
1411} // 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:46
const IDTypeInfo * BKE_idtype_get_info_from_idcode(short id_code)
Definition idtype.cc:142
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
@ LIB_ID_COPY_ACTIONS
@ LIB_ID_CREATE_NO_USER_REFCOUNT
@ LIB_ID_CREATE_NO_MAIN
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:951
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:446
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:803
static bool check_rna_path_is_valid(ID *owner_id, const char *path)
Definition anim_data.cc:688
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:856
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:528
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:703
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:437
static void animpath_update_basepath(FCurve *fcu, const char *old_basepath, const char *new_basepath)
Definition anim_data.cc:534
void BKE_animdata_transfer_by_basepath(Main *bmain, ID *srcID, ID *dstID, ListBase *basepaths)
Definition anim_data.cc:610
void BKE_animdata_copy_id_action(Main *bmain, ID *id)
Definition anim_data.cc:431
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:554
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:894
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:594
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:769
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
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:4226
uint8_t flag
Definition wm_window.cc:139