Blender V5.0
action_iterators.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "ANIM_action.hh"
11
12#include "BLI_assert.h"
13#include "BLI_listbase.h"
14
15#include "BKE_anim_data.hh"
16#include "BKE_nla.hh"
17
19#include "DNA_object_types.h"
20
21#include "RNA_access.hh"
22#include "RNA_prototypes.hh"
23
24namespace blender::animrig {
25
26void foreach_fcurve_in_action(Action &action, FunctionRef<void(FCurve &fcurve)> callback)
27{
28 if (action.is_action_legacy()) {
29 LISTBASE_FOREACH (FCurve *, fcurve, &action.curves) {
30 callback(*fcurve);
31 }
32 return;
33 }
34
35 for (Layer *layer : action.layers()) {
36 for (Strip *strip : layer->strips()) {
37 if (strip->type() != Strip::Type::Keyframe) {
38 continue;
39 }
40 for (Channelbag *bag : strip->data<StripKeyframeData>(action).channelbags()) {
41 for (FCurve *fcu : bag->fcurves()) {
42 callback(*fcu);
43 }
44 }
45 }
46 }
47}
48
50 slot_handle_t handle,
51 FunctionRef<void(FCurve &fcurve)> callback)
52{
53 if (action.is_action_legacy()) {
54 LISTBASE_FOREACH (FCurve *, fcurve, &action.curves) {
55 callback(*fcurve);
56 }
57 }
58 else if (action.is_action_layered()) {
59 for (Layer *layer : action.layers()) {
60 for (Strip *strip : layer->strips()) {
61 if (strip->type() != Strip::Type::Keyframe) {
62 continue;
63 }
64 for (Channelbag *bag : strip->data<StripKeyframeData>(action).channelbags()) {
65 if (bag->slot_handle != handle) {
66 continue;
67 }
68 for (FCurve *fcu : bag->fcurves()) {
69 BLI_assert(fcu != nullptr);
70 callback(*fcu);
71 }
72 }
73 }
74 }
75 }
76}
77
79 const ID &animated_id,
80 FunctionRef<bool(const Action &action, slot_handle_t slot_handle)> callback)
81{
82
83 const auto forward_to_callback = [&](ID & /* animated_id */,
84 bAction *&action_ptr_ref,
85 const slot_handle_t &slot_handle_ref,
86 char * /*last_slot_identifier*/) -> bool {
87 if (!action_ptr_ref) {
88 return true;
89 }
90 return callback(const_cast<const Action &>(action_ptr_ref->wrap()), slot_handle_ref);
91 };
92
93 return foreach_action_slot_use_with_references(const_cast<ID &>(animated_id),
94 forward_to_callback);
95}
96
98 ID &animated_id,
99 FunctionRef<bool(ID &animated_id,
100 bAction *&action_ptr_ref,
101 slot_handle_t &slot_handle_ref,
102 char *last_slot_identifier)> callback)
103{
104 AnimData *adt = BKE_animdata_from_id(&animated_id);
105
106 if (adt) {
107 if (adt->action) {
108 /* Direct assignment. */
109 if (!callback(animated_id, adt->action, adt->slot_handle, adt->last_slot_identifier)) {
110 return false;
111 }
112 }
113
114 /* NLA strips. */
115 const bool looped_until_last_strip = bke::nla::foreach_strip_adt(*adt, [&](NlaStrip *strip) {
116 if (strip->act) {
117 if (!callback(
118 animated_id, strip->act, strip->action_slot_handle, strip->last_slot_identifier))
119 {
120 return false;
121 }
122 }
123 return true;
124 });
125 if (!looped_until_last_strip) {
126 return false;
127 }
128 }
129
130 /* The rest of the code deals with constraints, so only relevant when this is an Object. */
131 if (GS(animated_id.name) != ID_OB) {
132 return true;
133 }
134
135 const Object &object = reinterpret_cast<const Object &>(animated_id);
136
143 auto visit_constraint = [&](const bConstraint &constraint) -> bool {
144 if (constraint.type != CONSTRAINT_TYPE_ACTION) {
145 return true;
146 }
147 bActionConstraint *constraint_data = static_cast<bActionConstraint *>(constraint.data);
148 if (!constraint_data->act) {
149 return true;
150 }
151 return callback(animated_id,
152 constraint_data->act,
153 constraint_data->action_slot_handle,
154 constraint_data->last_slot_identifier);
155 };
156
157 /* Visit Object constraints. */
158 LISTBASE_FOREACH (bConstraint *, con, &object.constraints) {
159 if (!visit_constraint(*con)) {
160 return false;
161 }
162 }
163
164 /* Visit Pose Bone constraints. */
165 if (object.type == OB_ARMATURE) {
166 LISTBASE_FOREACH (bPoseChannel *, pchan, &object.pose->chanbase) {
167 LISTBASE_FOREACH (bConstraint *, con, &pchan->constraints) {
168 if (!visit_constraint(*con)) {
169 return false;
170 }
171 }
172 }
173 }
174
175 return true;
176}
177
179 FunctionRef<bool(ID &animated_id,
180 bAction *action,
181 PointerRNA &action_slot_ptr,
182 PropertyRNA &action_slot_prop,
183 char *last_slot_identifier)> callback)
184{
185 /* This function has to copy the logic of #foreach_action_slot_use_with_references(),
186 * as it needs to know where exactly those pointers came from. */
187
188 AnimData *adt = BKE_animdata_from_id(&animated_id);
189
190 if (adt) {
191 if (adt->action) {
192 /* Direct assignment. */
193 PointerRNA ptr = RNA_pointer_create_discrete(&animated_id, &RNA_AnimData, adt);
194 PropertyRNA *prop = RNA_struct_find_property(&ptr, "action_slot");
195 if (!callback(animated_id, adt->action, ptr, *prop, adt->last_slot_identifier)) {
196 return false;
197 }
198 }
199
200 /* NLA strips. */
201 const bool looped_until_last_strip = bke::nla::foreach_strip_adt(*adt, [&](NlaStrip *strip) {
202 if (strip->act) {
203 PointerRNA ptr = RNA_pointer_create_discrete(&animated_id, &RNA_NlaStrip, strip);
204 PropertyRNA *prop = RNA_struct_find_property(&ptr, "action_slot");
205
206 if (!callback(animated_id, strip->act, ptr, *prop, strip->last_slot_identifier)) {
207 return false;
208 }
209 }
210 return true;
211 });
212 if (!looped_until_last_strip) {
213 return false;
214 }
215 }
216
217 /* The rest of the code deals with constraints, so only relevant when this is an Object. */
218 if (GS(animated_id.name) != ID_OB) {
219 return true;
220 }
221
222 const Object &object = reinterpret_cast<const Object &>(animated_id);
223
230 auto visit_constraint = [&](bConstraint &constraint) -> bool {
231 if (constraint.type != CONSTRAINT_TYPE_ACTION) {
232 return true;
233 }
234 bActionConstraint *constraint_data = static_cast<bActionConstraint *>(constraint.data);
235 if (!constraint_data->act) {
236 return true;
237 }
238
239 PointerRNA ptr = RNA_pointer_create_discrete(&animated_id, &RNA_ActionConstraint, &constraint);
240 PropertyRNA *prop = RNA_struct_find_property(&ptr, "action_slot");
241
242 return callback(
243 animated_id, constraint_data->act, ptr, *prop, constraint_data->last_slot_identifier);
244 };
245
246 /* Visit Object constraints. */
247 LISTBASE_FOREACH (bConstraint *, con, &object.constraints) {
248 if (!visit_constraint(*con)) {
249 return false;
250 }
251 }
252
253 /* Visit Pose Bone constraints. */
254 if (object.type == OB_ARMATURE) {
255 LISTBASE_FOREACH (bPoseChannel *, pchan, &object.pose->chanbase) {
256 LISTBASE_FOREACH (bConstraint *, con, &pchan->constraints) {
257 if (!visit_constraint(*con)) {
258 return false;
259 }
260 }
261 }
262 }
263
264 return true;
265}
266
267} // namespace blender::animrig
Functions and classes to work with Actions.
Functionality to iterate an Action in various ways.
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:83
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
@ ID_OB
@ CONSTRAINT_TYPE_ACTION
Object is a sort of wrapper for general info.
@ OB_ARMATURE
btSequentialImpulseConstraintSolverMt int btPersistentManifold int btTypedConstraint ** constraints
blender::Span< const Layer * > layers() const
blender::Span< const Channelbag * > channelbags() const
#define GS(x)
void foreach_fcurve_in_action_slot(Action &action, slot_handle_t handle, FunctionRef< void(FCurve &fcurve)> callback)
void foreach_fcurve_in_action(Action &action, FunctionRef< void(FCurve &fcurve)> callback)
decltype(::ActionSlot::handle) slot_handle_t
bool foreach_action_slot_use_with_rna(ID &animated_id, FunctionRef< bool(ID &animated_id, bAction *action, PointerRNA &action_slot_owner_ptr, PropertyRNA &action_slot_prop, char *last_slot_identifier)> callback)
bool foreach_action_slot_use_with_references(ID &animated_id, FunctionRef< bool(ID &animated_id, bAction *&action_ptr_ref, slot_handle_t &slot_handle_ref, char *last_slot_identifier)> callback)
bool foreach_action_slot_use(const ID &animated_id, FunctionRef< bool(const Action &action, slot_handle_t slot_handle)> callback)
bool foreach_strip_adt(const AnimData &adt, blender::FunctionRef< bool(NlaStrip *)> callback)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
bAction * action
int32_t slot_handle
char last_slot_identifier[258]
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
char last_slot_identifier[258]
int32_t action_slot_handle
bAction * act
ListBase curves
PointerRNA * ptr
Definition wm_files.cc:4238