Blender V5.0
action_legacy.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
5#include "ANIM_action.hh"
7
9
10#include "BKE_fcurve.hh"
11
12#include "BLT_translation.hh"
13
15
17{
18 for (Layer *layer : action.layers()) {
19 for (Strip *strip : layer->strips()) {
20 if (strip->type() == Strip::Type::Keyframe) {
21 return strip;
22 }
23 }
24 }
25
26 return nullptr;
27}
28
30{
32
33 if (!action.slots().is_empty()) {
34 return *action.slot(0);
35 }
36
37 Slot &slot = action.slot_add();
39 return slot;
40}
41
43{
44 if (action.slots().is_empty()) {
45 return nullptr;
46 }
47
48 Strip *keystrip = first_keyframe_strip(action);
49 if (!keystrip) {
50 return nullptr;
51 }
52
53 return keystrip->data<StripKeyframeData>(action).channelbag_for_slot(*action.slot(0));
54}
55
57{
59
60 Slot &slot = slot_ensure(action);
61
62 /* Ensure a Layer + keyframe Strip.
63 *
64 * Normally we would use `Action::layer_keystrip_ensure()` for this, but that
65 * doesn't let us specify the name of the layer if newly created. */
66 if (action.layers().is_empty()) {
68 }
69 if (action.layer(0)->strips().is_empty()) {
70 action.layer(0)->strip_add(action, Strip::Type::Keyframe);
71 }
72
73 Strip &keystrip = *action.layer(0)->strip(0);
74
75 /* Ensure a Channelbag. */
76 return keystrip.data<StripKeyframeData>(action).channelbag_for_slot_ensure(slot);
77}
78
79/* Lots of template args to support transparent non-const and const versions. */
80template<typename ActionType,
81 typename FCurveType,
82 typename LayerType,
83 typename StripType,
84 typename StripKeyframeDataType,
85 typename ChannelbagType>
87{
88 /* Legacy Action. */
89 if (action.is_action_legacy()) {
90 Vector<FCurveType *> legacy_fcurves;
91 LISTBASE_FOREACH (FCurveType *, fcurve, &action.curves) {
92 legacy_fcurves.append(fcurve);
93 }
94 return legacy_fcurves;
95 }
96
97 /* Layered Action. */
98 BLI_assert(action.is_action_layered());
99
100 Vector<FCurveType *> all_fcurves;
101 for (LayerType *layer : action.layers()) {
102 for (StripType *strip : layer->strips()) {
103 switch (strip->type()) {
104 case Strip::Type::Keyframe: {
105 StripKeyframeDataType &strip_data = strip->template data<StripKeyframeData>(action);
106 for (ChannelbagType *bag : strip_data.channelbags()) {
107 for (FCurveType *fcurve : bag->fcurves()) {
108 all_fcurves.append(fcurve);
109 }
110 }
111 }
112 }
113 }
114 }
115 return all_fcurves;
116}
117
119{
120 if (!action) {
121 return {};
122 }
124 action->wrap());
125}
126
128{
129 if (!action) {
130 return {};
131 }
132 return fcurves_all_templated<const Action,
133 const FCurve,
134 const Layer,
135 const Strip,
136 const StripKeyframeData,
137 const Channelbag>(action->wrap());
138}
139
141{
142 if (!action) {
143 return {};
144 }
145 Action &action_wrap = action->wrap();
146
147 if (action_wrap.is_action_legacy()) {
148 return fcurves_all(action);
149 }
150
151 if (action_wrap.slots().is_empty()) {
152 return {};
153 }
154 return fcurves_for_action_slot(action, action_wrap.slot(0)->handle);
155}
156
157/* Lots of template args to support transparent non-const and const versions. */
158template<typename ActionType,
159 typename FCurveType,
160 typename LayerType,
161 typename StripType,
162 typename StripKeyframeDataType,
163 typename ChannelbagType>
165 const slot_handle_t slot_handle)
166{
167 /* Legacy Action. */
168 if (action.is_action_legacy()) {
169 return listbase_to_vector<FCurveType>(action.curves);
170 }
171
172 /* Layered Action. */
173 Vector<FCurveType *> as_vector(animrig::fcurves_for_action_slot(action, slot_handle));
174 return as_vector;
175}
176
178{
179 if (!action) {
180 return {};
181 }
183 FCurve,
184 Layer,
185 Strip,
187 Channelbag>(action->wrap(), slot_handle);
188}
190 const slot_handle_t slot_handle)
191{
192 if (!action) {
193 return {};
194 }
196 const FCurve,
197 const Layer,
198 const Strip,
199 const StripKeyframeData,
200 const Channelbag>(action->wrap(), slot_handle);
201}
202
204{
205 if (!adt || !adt->action) {
206 return {};
207 }
209}
211{
212 if (!adt || !adt->action) {
213 return {};
214 }
215 return legacy::fcurves_for_action_slot(const_cast<const bAction *>(adt->action),
216 adt->slot_handle);
217}
218
220{
221 if (adt == nullptr || adt->action == nullptr) {
222 return false;
223 }
224
225 Action &action = adt->action->wrap();
226
227 if (action.is_action_legacy()) {
228 return action.curves.first != nullptr;
229 }
230
231 return action.has_keyframes(adt->slot_handle);
232}
233
235{
236 if (!action) {
237 return {};
238 }
239
240 Action &action_wrap = action->wrap();
241
242 /* Legacy Action. */
243 if (action_wrap.is_action_legacy()) {
244 Vector<bActionGroup *> legacy_groups;
245 LISTBASE_FOREACH (bActionGroup *, group, &action_wrap.groups) {
246 legacy_groups.append(group);
247 }
248 return legacy_groups;
249 }
250
251 /* Layered Action. */
252 Vector<bActionGroup *> all_groups;
253 for (Layer *layer : action_wrap.layers()) {
254 for (Strip *strip : layer->strips()) {
255 switch (strip->type()) {
256 case Strip::Type::Keyframe: {
257 StripKeyframeData &strip_data = strip->template data<StripKeyframeData>(action_wrap);
258 for (Channelbag *bag : strip_data.channelbags()) {
259 all_groups.extend(bag->channel_groups());
260 }
261 }
262 }
263 }
264 }
265 return all_groups;
266}
267
269{
270 if (!adt || !adt->action) {
271 return {};
272 }
273
274 Action &action = adt->action->wrap();
275
276 /* Legacy Action. */
277 if (action.is_action_legacy()) {
278 return channel_groups_all(adt->action);
279 }
280
281 /* Layered Action. */
283 if (!bag) {
284 return {};
285 }
286
287 Vector<bActionGroup *> slot_groups(bag->channel_groups());
288 return slot_groups;
289}
290
292{
293 /* At runtime, legacy Actions should have been versioned to layered/slotted Actions. However,
294 * unit tests can still create legacy Actions, and so this function still has to distinguish
295 * between them.
296 *
297 * Note that empty Actions also count as 'layered'. */
298 return !action.wrap().is_action_layered();
299}
300
302 const slot_handle_t slot_handle,
303 const StringRefNull rna_path_prefix)
304{
305 BLI_assert(!rna_path_prefix.is_empty());
306 if (rna_path_prefix.is_empty()) {
307 return false;
308 }
309
310 Action &action_wrapped = action.wrap();
311
312 /* Legacy Action. Code is 'borrowed' from fcurves_path_remove_fix() in
313 * blenkernel/intern/anim_data.cc */
314 if (action_wrapped.is_action_legacy()) {
315 bool any_removed = false;
316 LISTBASE_FOREACH_MUTABLE (FCurve *, fcurve, &action.curves) {
317 if (!fcurve->rna_path) {
318 continue;
319 }
320
321 if (STRPREFIX(fcurve->rna_path, rna_path_prefix.c_str())) {
322 BLI_remlink(&action.curves, fcurve);
323 BKE_fcurve_free(fcurve);
324 any_removed = true;
325 }
326 }
327 return any_removed;
328 }
329
330 /* Layered Action. */
331 Channelbag *bag = channelbag_for_action_slot(action.wrap(), slot_handle);
332 if (!bag) {
333 return false;
334 }
335
336 bool any_removed = false;
337 for (int64_t fcurve_index = 0; fcurve_index < bag->fcurve_array_num; fcurve_index++) {
338 FCurve *fcurve = bag->fcurve(fcurve_index);
339 if (!fcurve->rna_path) {
340 continue;
341 }
342
343 if (STRPREFIX(fcurve->rna_path, rna_path_prefix.c_str())) {
344 bag->fcurve_remove_by_index(fcurve_index);
345 fcurve_index--;
346 any_removed = true;
347 }
348 }
349 return any_removed;
350}
351
352} // namespace blender::animrig::legacy
Functions and classes to work with Actions.
Functions for backward compatibility with the legacy Action API.
void BKE_fcurve_free(FCurve *fcu)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
#define STRPREFIX(a, b)
#define DATA_(msgid)
BMesh const char void * data
long long int int64_t
constexpr bool is_empty() const
constexpr const char * c_str() const
void append(const T &value)
void extend(Span< T > array)
void slot_display_name_define(Slot &slot, StringRefNull new_display_name)
const Layer * layer(int64_t index) const
const Slot * slot(int64_t index) const
blender::Span< const Layer * > layers() const
bool has_keyframes(slot_handle_t action_slot_handle) const ATTR_WARN_UNUSED_RESULT
blender::Span< const Slot * > slots() const
Layer & layer_add(std::optional< StringRefNull > name)
const FCurve * fcurve(int64_t index) const
void fcurve_remove_by_index(int64_t fcurve_index)
blender::Span< const bActionGroup * > channel_groups() const
blender::Span< const Strip * > strips() const
const Strip * strip(int64_t index) const
Strip & strip_add(Action &owning_action, Strip::Type strip_type)
blender::Span< const Channelbag * > channelbags() const
const T & data(const Action &owning_action) const
Slot & slot_ensure(Action &action)
Channelbag & channelbag_ensure(Action &action)
constexpr const char * DEFAULT_LEGACY_LAYER_NAME
Vector< FCurve * > fcurves_for_action_slot(bAction *action, slot_handle_t slot_handle)
static Vector< FCurveType * > fcurves_all_templated(ActionType &action)
Vector< bActionGroup * > channel_groups_for_assigned_slot(AnimData *adt)
Vector< const FCurve * > fcurves_all(const bAction *action)
static Strip * first_keyframe_strip(Action &action)
Channelbag * channelbag_get(Action &action)
bool action_fcurves_remove(bAction &action, slot_handle_t slot_handle, StringRefNull rna_path_prefix)
Vector< FCurve * > fcurves_first_slot(bAction *action)
Vector< FCurve * > fcurves_for_assigned_action(AnimData *adt)
bool assigned_action_has_keyframes(AnimData *adt)
constexpr const char * DEFAULT_LEGACY_SLOT_NAME
Vector< bActionGroup * > channel_groups_all(bAction *action)
static Vector< FCurveType * > fcurves_for_action_slot_templated(ActionType &action, const slot_handle_t slot_handle)
bool action_treat_as_legacy(const bAction &action)
void assert_baklava_phase_1_invariants(const Action &action)
const animrig::Channelbag * channelbag_for_action_slot(const Action &action, slot_handle_t slot_handle)
decltype(::ActionSlot::handle) slot_handle_t
Span< FCurve * > fcurves_for_action_slot(Action &action, slot_handle_t slot_handle)
Vector< T * > listbase_to_vector(ListBase &list)
bAction * action
int32_t slot_handle
char * rna_path
void * first
ListBase curves
ListBase groups