Blender V4.3
animdata.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8#include "ANIM_action.hh"
9#include "ANIM_animdata.hh"
10
11#include "BKE_action.hh"
12#include "BKE_anim_data.hh"
13#include "BKE_fcurve.hh"
14#include "BKE_key.hh"
15#include "BKE_lib_id.hh"
16#include "BKE_main.hh"
17#include "BKE_material.h"
18#include "BKE_node.hh"
19
20#include "BLT_translation.hh"
21
22#include "BLI_listbase.h"
23#include "BLI_string.h"
24
25#include "DEG_depsgraph.hh"
27
28#include "DNA_anim_types.h"
29#include "DNA_key_types.h"
30#include "DNA_material_types.h"
31#include "DNA_mesh_types.h"
32#include "DNA_particle_types.h"
33
34#include "ED_anim_api.hh"
35
36#include "RNA_access.hh"
37#include "RNA_path.hh"
38
39namespace blender::animrig {
40
41/* -------------------------------------------------------------------- */
45/* Find the users of the given ID within the objects of `bmain` and add non-duplicates to the end
46 * of `related_ids`. */
47static void add_object_data_users(const Main &bmain, const ID &id, Vector<ID *> &related_ids)
48{
49 if (ID_REAL_USERS(&id) != 1) {
50 /* Only find objects if this ID is only used once. */
51 return;
52 }
53
54 Object *ob;
55 ID *object_id;
56 FOREACH_MAIN_LISTBASE_ID_BEGIN (&bmain.objects, object_id) {
57 ob = (Object *)object_id;
58 if (ob->data != &id) {
59 continue;
60 }
61 related_ids.append_non_duplicates(&ob->id);
62 }
64}
65
67{
68 Vector<ID *> related_ids({&id});
69
70 /* `related_ids` can grow during an iteration if the ID of the current iteration has associated
71 * code that defines relationships. */
72 for (int i = 0; i < related_ids.size(); i++) {
73 ID *related_id = related_ids[i];
74
75 if (related_id->flag & ID_FLAG_EMBEDDED_DATA) {
76 /* No matter the type of embedded ID, their owner can always be added to the related IDs. */
77 BLI_assert(ID_REAL_USERS(related_id) == 0);
78 ID *owner_id = BKE_id_owner_get(related_id);
79 /* Embedded IDs should always have an owner. */
80 BLI_assert(owner_id != nullptr);
81 related_ids.append_non_duplicates(owner_id);
82 }
83
84 /* No action found on current ID, add related IDs to the ID Vector. */
85 switch (GS(related_id->name)) {
86 case ID_OB: {
87 Object *ob = (Object *)related_id;
88 if (!ob->data) {
89 break;
90 }
91 ID *data = (ID *)ob->data;
92 if (ID_REAL_USERS(data) == 1) {
93 related_ids.append_non_duplicates(data);
94 }
95 LISTBASE_FOREACH (ParticleSystem *, particle_system, &ob->particlesystem) {
96 if (!particle_system) {
97 continue;
98 }
99 if (ID_REAL_USERS(&particle_system->part->id) != 1) {
100 continue;
101 }
102 related_ids.append_non_duplicates(&particle_system->part->id);
103 }
104 break;
105 }
106
107 case ID_KE: {
108 /* Shape-keys. */
109 Key *key = (Key *)related_id;
110 /* Shape-keys are not embedded but there is currently no way to reuse them. */
111 BLI_assert(ID_REAL_USERS(related_id) == 1);
112 related_ids.append_non_duplicates(key->from);
113 break;
114 }
115
116 case ID_MA: {
117 /* Explicitly not relating materials and material users. */
118 Material *mat = (Material *)related_id;
119 if (mat->nodetree && ID_REAL_USERS(&mat->nodetree->id) == 1) {
120 related_ids.append_non_duplicates(&mat->nodetree->id);
121 }
122 break;
123 }
124
125 case ID_PA: {
126 if (ID_REAL_USERS(related_id) != 1) {
127 continue;
128 }
129 Object *ob;
130 ID *object_id;
131 /* Find users of this particle setting. */
132 FOREACH_MAIN_LISTBASE_ID_BEGIN (&bmain.objects, object_id) {
133 ob = (Object *)object_id;
134 bool object_uses_particle_settings = false;
135 LISTBASE_FOREACH (ParticleSystem *, particle_system, &ob->particlesystem) {
136 if (!particle_system) {
137 continue;
138 }
139 if (&particle_system->part->id != related_id) {
140 continue;
141 }
142 object_uses_particle_settings = true;
143 break;
144 }
145 if (object_uses_particle_settings) {
146 related_ids.append_non_duplicates(&ob->id);
147 break;
148 }
149 }
151
152 break;
153 }
154
155 default: {
156 /* Just check if the ID is used as object data somewhere. */
157 add_object_data_users(bmain, *related_id, related_ids);
159 if (node_tree && ID_REAL_USERS(&node_tree->id) == 1) {
160 related_ids.append_non_duplicates(&node_tree->id);
161 }
162
163 Key *key = BKE_key_from_id(related_id);
164 if (key) {
165 /* No check for multi user because the shape-key cannot be shared. */
166 BLI_assert(ID_REAL_USERS(&key->id) == 1);
167 related_ids.append_non_duplicates(&key->id);
168 }
169 break;
170 }
171 }
172 }
173
174 return related_ids;
175}
176
177/* Find an action on an ID that is related to the given ID. Related things are e.g. Object<->Data,
178 * Mesh<->Material and so on. */
179static bAction *find_related_action(Main &bmain, ID &id)
180{
181 Vector<ID *> related_ids = find_related_ids(bmain, id);
182
183 for (ID *related_id : related_ids) {
184 Action *action = get_action(*related_id);
185 if (action && action->is_action_layered()) {
186 /* Returning the first action found means highest priority has the action closest in the
187 * relationship graph. */
188 return action;
189 }
190 }
191
192 return nullptr;
193}
194
196{
198 if (adt == nullptr) {
199 printf("ERROR: data-block type is not animatable (ID = %s)\n", (id) ? (id->name) : "<None>");
200 return nullptr;
201 }
202
203 /* init action if none available yet */
204 /* TODO: need some wizardry to handle NLA stuff correct */
205 if (adt->action == nullptr) {
206 bAction *action = nullptr;
207 if (USER_EXPERIMENTAL_TEST(&U, use_animation_baklava)) {
208 action = find_related_action(*bmain, *id);
209 }
210 if (action == nullptr) {
211 /* init action name from name of ID block */
212 char actname[sizeof(id->name) - 2];
213 if (id->flag & ID_FLAG_EMBEDDED_DATA && USER_EXPERIMENTAL_TEST(&U, use_animation_baklava)) {
214 /* When the ID is embedded, use the name of the owner ID for clarity. */
215 ID *owner_id = BKE_id_owner_get(id);
216 /* If the ID is embedded it should have an owner. */
217 BLI_assert(owner_id != nullptr);
218 SNPRINTF(actname, DATA_("%sAction"), owner_id->name + 2);
219 }
220 else if (GS(id->name) == ID_KE && USER_EXPERIMENTAL_TEST(&U, use_animation_baklava)) {
221 Key *key = (Key *)id;
222 SNPRINTF(actname, DATA_("%sAction"), key->from->name + 2);
223 }
224 else {
225 SNPRINTF(actname, DATA_("%sAction"), id->name + 2);
226 }
227
228 /* create action */
229 action = BKE_action_add(bmain, actname);
230
231 /* Decrement the default-1 user count, as assigning it will increase it again. */
232 BLI_assert(action->id.us == 1);
233 id_us_min(&action->id);
234 }
235
236 /* Assigning the Action should always work here. The only reason it wouldn't, is when a legacy
237 * Action of the wrong ID type is assigned, but since in this branch of the code we're only
238 * dealing with either new or layered Actions, this will never fail. */
239 const bool ok = animrig::assign_action(action, {*id, *adt});
240 BLI_assert_msg(ok, "Expecting Action assignment to work here");
242
243 /* Tag depsgraph to be rebuilt to include time dependency. */
245 }
246
248
249 /* return the action */
250 return adt->action;
251}
252
254{
255 /* - If no AnimData, we've got nowhere to remove the F-Curve from
256 * (this doesn't guarantee that the F-Curve is in there, but at least we tried
257 * - If no F-Curve, there is nothing to remove
258 */
259 if (ELEM(nullptr, adt, fcu)) {
260 return;
261 }
262
263 /* Remove from whatever list it came from
264 * - Action Group
265 * - Action
266 * - Drivers
267 * - TODO... some others?
268 */
269 if ((ac) && (ac->datatype == ANIMCONT_DRIVERS)) {
270 BLI_remlink(&adt->drivers, fcu);
271 }
272 else if (adt->action) {
273 Action &action = adt->action->wrap();
274
275 if (action.is_action_legacy()) {
276 /* Remove from group or action, whichever one "owns" the F-Curve. */
277 if (fcu->grp) {
278 bActionGroup *agrp = fcu->grp;
279
280 /* Remove F-Curve from group+action. */
281 action_groups_remove_channel(&action, fcu);
282
283 /* If group has no more channels, remove it too,
284 * otherwise can have many dangling groups #33541.
285 */
286 if (BLI_listbase_is_empty(&agrp->channels)) {
287 BLI_freelinkN(&action.groups, agrp);
288 }
289 }
290 else {
291 BLI_remlink(&action.curves, fcu);
292 }
293
294 /* If action has no more F-Curves as a result of this, unlink it from
295 * AnimData if it did not come from a NLA Strip being tweaked.
296 *
297 * This is done so that we don't have dangling Object+Action entries in
298 * channel list that are empty, and linger around long after the data they
299 * are for has disappeared (and probably won't come back).
300 */
302 }
303 else {
304 action_fcurve_remove(action, *fcu);
305 /* Return early to avoid the call to BKE_fcurve_free because the fcu has already been freed
306 * by action_fcurve_remove. */
307 return;
308 }
309 }
310 else {
312 }
313
314 BKE_fcurve_free(fcu);
315}
316
318{
319 if (adt->action != nullptr) {
320 bAction *act = adt->action;
322 if (BLI_listbase_is_empty(&act->curves) && (adt->flag & ADT_NLA_EDIT_ON) == 0) {
323 id_us_min(&act->id);
324 adt->action = nullptr;
325 return true;
326 }
327 }
328
329 return false;
330}
331
335{
336 /* Need to take off the flag before filtering, else the filter code would skip the FCurves, which
337 * have not yet been validated. */
338 const bool filtering_enabled = ac->ads->filterflag & ADS_FILTER_ONLY_ERRORS;
339 if (filtering_enabled) {
340 ac->ads->filterflag &= ~ADS_FILTER_ONLY_ERRORS;
341 }
342 ListBase anim_data = {nullptr, nullptr};
344 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, eAnimCont_Types(ac->datatype));
345
346 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
347 FCurve *fcu = (FCurve *)ale->key_data;
349 PropertyRNA *prop;
350 PointerRNA id_ptr = RNA_id_pointer_create(ale->id);
351 if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) {
352 fcu->flag &= ~FCURVE_DISABLED;
353 }
354 else {
355 fcu->flag |= FCURVE_DISABLED;
356 }
357 }
358
359 ANIM_animdata_freelist(&anim_data);
360 if (filtering_enabled) {
362 }
363}
364
366 const StringRefNull rna_path,
367 const int array_index)
368{
369 BLI_assert(adt.action);
370 if (!adt.action) {
371 return nullptr;
372 }
373
374 const Action &action = adt.action->wrap();
376
377 const Slot *slot = action.slot_for_handle(adt.slot_handle);
378 if (!slot) {
379 /* No need to inspect anything if this ID does not have an Action Slot. */
380 return nullptr;
381 }
382
383 /* No check for the slot's ID type. Not only do we not have the actual ID
384 * to do this check, but also, since the Action and the slot have been
385 * assigned, just trust that it's valid. */
386
387 /* Iterate the layers top-down, as higher-up animation overrides (or at least can override)
388 * lower-down animation. */
389 for (int layer_idx = action.layer_array_num - 1; layer_idx >= 0; layer_idx--) {
390 const Layer *layer = action.layer(layer_idx);
391
392 /* TODO: refactor this into something nicer once we have different strip types. */
393 for (const Strip *strip : layer->strips()) {
394 switch (strip->type()) {
395 case Strip::Type::Keyframe: {
396 const StripKeyframeData &strip_data = strip->data<StripKeyframeData>(action);
397 const ChannelBag *channelbag_for_slot = strip_data.channelbag_for_slot(*slot);
398 if (!channelbag_for_slot) {
399 continue;
400 }
401 const FCurve *fcu = channelbag_for_slot->fcurve_find({rna_path, array_index});
402 if (!fcu) {
403 continue;
404 }
405
406 /* This code assumes that there is only one strip, and that it's infinite. When that
407 * changes, this code needs to be expanded to check for strip boundaries. */
408 return fcu;
409 }
410 }
411 /* Explicit lack of 'default' clause, to get compiler warnings when strip types are added. */
412 }
413 }
414
415 return nullptr;
416}
417
418} // namespace blender::animrig
Functions and classes to work with Actions.
Functions to work with AnimData.
Blender kernel action and pose functionality.
void action_groups_remove_channel(bAction *act, FCurve *fcu)
bAction * BKE_action_add(Main *bmain, const char name[])
AnimData * BKE_animdata_ensure_id(ID *id)
Definition anim_data.cc:103
void BKE_fcurve_free(FCurve *fcu)
Key * BKE_key_from_id(ID *id)
Definition key.cc:1800
ID * BKE_id_owner_get(ID *id, const bool debug_relationship_assert=true)
Definition lib_id.cc:2444
void id_us_min(ID *id)
Definition lib_id.cc:359
#define FOREACH_MAIN_LISTBASE_ID_END
Definition BKE_main.hh:469
#define FOREACH_MAIN_LISTBASE_ID_BEGIN(_lb, _id)
Definition BKE_main.hh:463
General operations, lookup, etc. for materials.
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:269
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define DATA_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
@ ID_RECALC_ANIMATION_NO_FLUSH
Definition DNA_ID.h:1143
@ ID_FLAG_EMBEDDED_DATA
Definition DNA_ID.h:725
#define ID_REAL_USERS(id)
Definition DNA_ID.h:637
@ ID_KE
@ ID_MA
@ ID_OB
@ ID_PA
@ ADS_FILTER_ONLY_ERRORS
@ ADT_NLA_EDIT_ON
@ FCURVE_DISABLED
#define USER_EXPERIMENTAL_TEST(userdef, member)
eAnimCont_Types
@ ANIMCONT_DRIVERS
eAnimFilter_Flags
@ ANIMFILTER_DATA_VISIBLE
@ ANIMFILTER_FCURVESONLY
void ANIM_animdata_freelist(ListBase *anim_data)
Definition anim_deps.cc:457
size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode, void *data, const eAnimCont_Types datatype)
unsigned int U
Definition btGjkEpa3.h:78
void append_non_duplicates(const T &value)
const Layer * layer(int64_t index) const
Slot * slot_for_handle(slot_handle_t handle)
const ChannelBag * channelbag_for_slot(const Slot &slot) const
#define printf
#define GS(x)
Definition iris.cc:202
bool action_fcurve_remove(Action &action, FCurve &fcu)
Vector< ID * > find_related_ids(Main &bmain, ID &id)
Definition animdata.cc:66
bAction * id_action_ensure(Main *bmain, ID *id)
Definition animdata.cc:195
static void add_object_data_users(const Main &bmain, const ID &id, Vector< ID * > &related_ids)
Definition animdata.cc:47
Action * get_action(ID &animated_id)
void reevaluate_fcurve_errors(bAnimContext *ac)
Definition animdata.cc:334
bool animdata_remove_empty_action(AnimData *adt)
Definition animdata.cc:317
bool assign_action(bAction *action, ID &animated_id)
static bAction * find_related_action(Main &bmain, ID &id)
Definition animdata.cc:179
const FCurve * fcurve_find_by_rna_path(const AnimData &adt, StringRefNull rna_path, int array_index)
Definition animdata.cc:365
void animdata_fcurve_delete(bAnimContext *ac, AnimData *adt, FCurve *fcu)
Definition animdata.cc:253
bNodeTree * node_tree_from_id(ID *id)
Definition node.cc:3732
PointerRNA RNA_id_pointer_create(ID *id)
bool RNA_path_resolve_property(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop)
Definition rna_path.cc:553
bAction * action
int32_t slot_handle
ListBase drivers
bActionGroup * grp
char * rna_path
Definition DNA_ID.h:413
int us
Definition DNA_ID.h:435
short flag
Definition DNA_ID.h:430
char name[66]
Definition DNA_ID.h:425
ID * from
ListBase objects
Definition BKE_main.hh:212
struct bNodeTree * nodetree
ListBase particlesystem
ListBase curves
ListBase groups
eAnimCont_Types datatype
bDopeSheet * ads
PointerRNA * ptr
Definition wm_files.cc:4126