Blender V5.0
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.hh"
18#include "BKE_node.hh"
19
20#include "BLT_translation.hh"
21
22#include "BLI_listbase.h"
23#include "BLI_string_utf8.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_particle_types.h"
32
33#include "RNA_access.hh"
34
35namespace blender::animrig {
36
37/* -------------------------------------------------------------------- */
40
41/* Find the users of the given ID within the objects of `bmain` and add non-duplicates to the end
42 * of `related_ids`. */
43static void add_object_data_users(const Main &bmain, const ID &id, Vector<ID *> &related_ids)
44{
45 if (ID_REAL_USERS(&id) != 1) {
46 /* Only find objects if this ID is only used once. */
47 return;
48 }
49
50 Object *ob;
51 ID *object_id;
52 FOREACH_MAIN_LISTBASE_ID_BEGIN (&bmain.objects, object_id) {
53 ob = reinterpret_cast<Object *>(object_id);
54 if (ob->data != &id) {
55 continue;
56 }
57 related_ids.append_non_duplicates(&ob->id);
58 }
60}
61
63{
64 Vector<ID *> related_ids({&id});
65
66 /* `related_ids` can grow during an iteration if the ID of the current iteration has associated
67 * code that defines relationships. */
68 for (int i = 0; i < related_ids.size(); i++) {
69 ID *related_id = related_ids[i];
70
71 if (related_id->flag & ID_FLAG_EMBEDDED_DATA) {
72 /* No matter the type of embedded ID, their owner can always be added to the related IDs. */
73
74 /* User counting is irrelevant for the logic here, because embedded IDs cannot be shared.
75 * Embedded IDs do exist (sometimes) with a non-zero user count, hence the assertion that the
76 * user count is not greater than 1. */
77 BLI_assert(ID_REAL_USERS(related_id) <= 1);
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 = reinterpret_cast<Object *>(related_id);
88 if (!ob->data) {
89 break;
90 }
91 ID *data = static_cast<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->part) {
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 = reinterpret_cast<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 = reinterpret_cast<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 = reinterpret_cast<Object *>(object_id);
134 bool object_uses_particle_settings = false;
135 LISTBASE_FOREACH (ParticleSystem *, particle_system, &ob->particlesystem) {
136 if (!particle_system->part) {
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);
158 bNodeTree *node_tree = bke::node_tree_from_id(related_id);
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 = find_related_action(*bmain, *id);
207
208 if (action == nullptr) {
209 /* init action name from name of ID block */
210 char actname[sizeof(id->name) - 2];
211 if (id->flag & ID_FLAG_EMBEDDED_DATA) {
212 /* When the ID is embedded, use the name of the owner ID for clarity. */
213 ID *owner_id = BKE_id_owner_get(id);
214 /* If the ID is embedded it should have an owner. */
215 BLI_assert(owner_id != nullptr);
216 SNPRINTF_UTF8(actname, DATA_("%sAction"), owner_id->name + 2);
217 }
218 else if (GS(id->name) == ID_KE) {
219 Key *key = reinterpret_cast<Key *>(id);
220 SNPRINTF_UTF8(actname, DATA_("%sAction"), key->from->name + 2);
221 }
222 else {
223 SNPRINTF_UTF8(actname, DATA_("%sAction"), id->name + 2);
224 }
225
226 /* create action */
227 action = BKE_action_add(bmain, actname);
228
229 /* Decrement the default-1 user count, as assigning it will increase it again. */
230 BLI_assert(action->id.us == 1);
231 id_us_min(&action->id);
232 }
233
234 /* Assigning the Action should always work here. The only reason it wouldn't, is when a legacy
235 * Action of the wrong ID type is assigned, but since in this branch of the code we're only
236 * dealing with either new or layered Actions, this will never fail. */
237 const bool ok = animrig::assign_action(action, {*id, *adt});
238 BLI_assert_msg(ok, "Expecting Action assignment to work here");
240
241 /* Tag depsgraph to be rebuilt to include time dependency. */
243 }
244
246
247 /* return the action */
248 return adt->action;
249}
250
252{
253 /* - If no AnimData, we've got nowhere to remove the F-Curve from
254 * (this doesn't guarantee that the F-Curve is in there, but at least we tried
255 * - If no F-Curve, there is nothing to remove
256 */
257 if (ELEM(nullptr, adt, fcu)) {
258 return;
259 }
260
261 const bool is_driver = fcu->driver != nullptr;
262 if (is_driver) {
263 BLI_remlink(&adt->drivers, fcu);
264 }
265 else if (adt->action) {
266 Action &action = adt->action->wrap();
267
268 if (action.is_action_legacy()) {
269 /* Remove from group or action, whichever one "owns" the F-Curve. */
270 if (fcu->grp) {
271 bActionGroup *agrp = fcu->grp;
272
273 /* Remove F-Curve from group+action. */
274 action_groups_remove_channel(&action, fcu);
275
276 /* If group has no more channels, remove it too,
277 * otherwise can have many dangling groups #33541.
278 */
279 if (BLI_listbase_is_empty(&agrp->channels)) {
280 BLI_freelinkN(&action.groups, agrp);
281 }
282 }
283 else {
284 BLI_remlink(&action.curves, fcu);
285 }
286
287 /* If action has no more F-Curves as a result of this, unlink it from
288 * AnimData if it did not come from a NLA Strip being tweaked.
289 *
290 * This is done so that we don't have dangling Object+Action entries in
291 * channel list that are empty, and linger around long after the data they
292 * are for has disappeared (and probably won't come back).
293 */
295 }
296 else {
297 action_fcurve_remove(action, *fcu);
298 /* Return early to avoid the call to BKE_fcurve_free because the fcu has already been freed
299 * by action_fcurve_remove. */
300 return;
301 }
302 }
303 else {
305 }
306
307 BKE_fcurve_free(fcu);
308}
309
311{
312 if (adt->action != nullptr) {
313 bAction *act = adt->action;
315 Action &action = act->wrap();
316 if (action.is_empty() && (adt->flag & ADT_NLA_EDIT_ON) == 0) {
317 id_us_min(&act->id);
318 adt->action = nullptr;
319 return true;
320 }
321 }
322
323 return false;
324}
325
327
329 const StringRefNull rna_path,
330 const int array_index)
331{
332 BLI_assert(adt.action);
333 if (!adt.action) {
334 return nullptr;
335 }
336
337 const Action &action = adt.action->wrap();
339
340 const Slot *slot = action.slot_for_handle(adt.slot_handle);
341 if (!slot) {
342 /* No need to inspect anything if this ID does not have an Action Slot. */
343 return nullptr;
344 }
345
346 /* No check for the slot's ID type. Not only do we not have the actual ID
347 * to do this check, but also, since the Action and the slot have been
348 * assigned, just trust that it's valid. */
349
350 /* Iterate the layers top-down, as higher-up animation overrides (or at least can override)
351 * lower-down animation. */
352 for (int layer_idx = action.layer_array_num - 1; layer_idx >= 0; layer_idx--) {
353 const Layer *layer = action.layer(layer_idx);
354
355 /* TODO: refactor this into something nicer once we have different strip types. */
356 for (const Strip *strip : layer->strips()) {
357 switch (strip->type()) {
358 case Strip::Type::Keyframe: {
359 const StripKeyframeData &strip_data = strip->data<StripKeyframeData>(action);
360 const Channelbag *channelbag_for_slot = strip_data.channelbag_for_slot(*slot);
361 if (!channelbag_for_slot) {
362 continue;
363 }
364 const FCurve *fcu = channelbag_for_slot->fcurve_find({rna_path, array_index});
365 if (!fcu) {
366 continue;
367 }
368
369 /* This code assumes that there is only one strip, and that it's infinite. When that
370 * changes, this code needs to be expanded to check for strip boundaries. */
371 return fcu;
372 }
373 }
374 /* Explicit lack of 'default' clause, to get compiler warnings when strip types are added. */
375 }
376 }
377
378 return nullptr;
379}
380
381} // 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:97
void BKE_fcurve_free(FCurve *fcu)
Key * BKE_key_from_id(ID *id)
Definition key.cc:1771
ID * BKE_id_owner_get(ID *id, const bool debug_relationship_assert=true)
Definition lib_id.cc:2511
void id_us_min(ID *id)
Definition lib_id.cc:366
#define FOREACH_MAIN_LISTBASE_ID_END
Definition BKE_main.hh:552
#define FOREACH_MAIN_LISTBASE_ID_BEGIN(_lb, _id)
Definition BKE_main.hh:546
General operations, lookup, etc. for materials.
#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
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
void BLI_freelinkN(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:270
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
#define SNPRINTF_UTF8(dst, format,...)
#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:1176
#define ID_REAL_USERS(id)
Definition DNA_ID.h:676
@ ID_FLAG_EMBEDDED_DATA
Definition DNA_ID.h:774
@ ID_KE
@ ID_MA
@ ID_OB
@ ID_PA
@ ADT_NLA_EDIT_ON
BMesh const char void * data
void append_non_duplicates(const T &value)
const Layer * layer(int64_t index) const
Slot * slot_for_handle(slot_handle_t handle)
blender::Span< const Strip * > strips() const
const Channelbag * channelbag_for_slot(const Slot &slot) const
#define GS(x)
#define printf(...)
bool action_fcurve_remove(Action &action, FCurve &fcu)
void animdata_fcurve_delete(AnimData *adt, FCurve *fcu)
Definition animdata.cc:251
Vector< ID * > find_related_ids(Main &bmain, ID &id)
Definition animdata.cc:62
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:43
Action * get_action(ID &animated_id)
bool animdata_remove_empty_action(AnimData *adt)
Definition animdata.cc:310
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:328
bNodeTree * node_tree_from_id(ID *id)
Definition node.cc:4568
bAction * action
int32_t slot_handle
ListBase drivers
bActionGroup * grp
ChannelDriver * driver
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
int us
Definition DNA_ID.h:443
short flag
Definition DNA_ID.h:438
ID * from
ListBase objects
Definition BKE_main.hh:280
struct bNodeTree * nodetree
ListBase particlesystem
ListBase curves
ListBase groups
i
Definition text_draw.cc:230