Blender V5.0
versioning.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/* This is versioning code, so it's allowed to touch on deprecated DNA fields. */
10
11#define DNA_DEPRECATED_ALLOW
12
13#include "ANIM_action.hh"
15#include "ANIM_action_legacy.hh"
16#include "ANIM_versioning.hh"
17
18#include "DNA_action_defaults.h"
19#include "DNA_action_types.h"
20
21#include "BKE_lib_id.hh"
22#include "BKE_main.hh"
23#include "BKE_node.hh"
24#include "BKE_report.hh"
25
26#include "BLI_listbase.h"
27#include "BLI_string_utf8.h"
28
29#include "BLT_translation.hh"
30
31#include "BLO_readfile.hh"
32
33#include "RNA_access.hh"
34#include "RNA_prototypes.hh"
35
37
38bool action_is_layered(const bAction &dna_action)
39{
40 /* NOTE: due to how forward-compatibility is handled when writing Actions to
41 * blend files, it is important that this function does NOT check
42 * `Action.idroot` as part of its determination of whether this is a layered
43 * action or not.
44 *
45 * See: `action_blend_write()` and `action_blend_read_data()`
46 */
47
48 const animrig::Action &action = dna_action.wrap();
49
50 const bool has_layered_data = action.layer_array_num > 0 || action.slot_array_num > 0;
51 const bool has_animato_data = !(BLI_listbase_is_empty(&action.curves) &&
53
54 return has_layered_data || !has_animato_data;
55}
56
58{
59 LISTBASE_FOREACH (bAction *, dna_action, &bmain.actions) {
60 blender::animrig::Action &action = dna_action->wrap();
61
62 if (action_is_layered(action) && !action.is_empty()) {
63 /* This is just a safety net. Blender files that trigger this versioning code are not
64 * expected to have any layered/slotted Actions.
65 *
66 * Empty Actions, even though they are valid "layered" Actions, should still get through
67 * versioning, though, to ensure they have the default "Legacy Slot" and a zero idroot. */
68 continue;
69 }
70
72 }
73}
74
76{
77 Action &action = dna_action.wrap();
79
80 /* Store this ahead of time, because adding the slot sets the action's idroot
81 * to 0. We also set the action's idroot to 0 manually, just to be defensive
82 * so we don't depend on esoteric behavior in `slot_add()`. */
83 const int16_t idtype = action.idroot;
84 action.idroot = 0;
85
86 /* Initialize the Action's last_slot_handle field to its default value, before
87 * we create a new slot. */
89
90 Slot &slot = action.slot_add();
91 slot.idtype = idtype;
92
93 const std::string slot_identifier{slot.idtype_string() +
95 action.slot_identifier_define(slot, slot_identifier);
96
98 blender::animrig::Strip &strip = layer.strip_add(action,
99 blender::animrig::Strip::Type::Keyframe);
100 Channelbag &bag = strip.data<StripKeyframeData>(action).channelbag_for_slot_ensure(slot);
101 const int fcu_count = BLI_listbase_count(&action.curves);
102 const int group_count = BLI_listbase_count(&action.groups);
103 bag.fcurve_array = MEM_calloc_arrayN<FCurve *>(fcu_count, "Action versioning - fcurves");
104 bag.fcurve_array_num = fcu_count;
105 bag.group_array = MEM_calloc_arrayN<bActionGroup *>(group_count, "Action versioning - groups");
106 bag.group_array_num = group_count;
107
108 int group_index = 0;
109 int fcurve_index = 0;
110 LISTBASE_FOREACH_INDEX (bActionGroup *, group, &action.groups, group_index) {
111 bag.group_array[group_index] = group;
112
113 group->channelbag = &bag;
114 group->fcurve_range_start = fcurve_index;
115
116 LISTBASE_FOREACH (FCurve *, fcu, &group->channels) {
117 if (fcu->grp != group) {
118 break;
119 }
120 bag.fcurve_array[fcurve_index++] = fcu;
121 }
122
123 group->fcurve_range_length = fcurve_index - group->fcurve_range_start;
124 }
125
126 LISTBASE_FOREACH (FCurve *, fcu, &action.curves) {
127 /* Any fcurves with groups have already been added to the fcurve array. */
128 if (fcu->grp) {
129 continue;
130 }
131 bag.fcurve_array[fcurve_index++] = fcu;
132 }
133
134 BLI_assert(fcurve_index == fcu_count);
135
136 action.curves = {nullptr, nullptr};
137 action.groups = {nullptr, nullptr};
138}
139
141{
142 animated_id.runtime->readfile_data->tags.action_assignment_needs_slot = true;
143}
144
146{
147 /* This function is only called when the blend-file is old enough to NOT use
148 * slotted Actions, so we can safely tag anything that uses an Action. */
149
150 auto flag_adt = [](ID &animated_id,
151 bAction *& /*action_ptr_ref*/,
152 slot_handle_t & /*slot_handle_ref*/,
153 char * /*last_slot_identifier*/) -> bool {
155
156 /* Once tagged, the foreach loop can stop, because more tagging of the same
157 * ID doesn't do anything. */
158 return false;
159 };
160
161 ID *id;
162 FOREACH_MAIN_ID_BEGIN (&bmain, id) {
164
165 /* Process embedded IDs, as these are not listed in bmain, but still can
166 * have their own Action+Slot. Unfortunately there is no generic looper
167 * for embedded IDs. At this moment the only animatable embedded ID is a
168 * node tree. */
170 if (node_tree) {
171 foreach_action_slot_use_with_references(node_tree->id, flag_adt);
172 }
173 }
175}
176
178{
179 auto version_slot_assignment = [&](ID &animated_id,
180 bAction *dna_action,
181 PointerRNA &action_slot_owner_ptr,
182 PropertyRNA &action_slot_prop,
183 char *last_used_slot_identifier) {
184 BLI_assert(dna_action); /* Ensured by the foreach loop. */
185 Action &action = dna_action->wrap();
186
187 if (action.slot_array_num == 0) {
188 /* There's a few reasons why this Action doesn't have a slot. It could simply be a slotted
189 * Action without slots, or a legacy-but-not-yet-versioned Action, or it could be it is a
190 * _really_ old (pre-2.50) Action. The latter are upgraded in do_versions_after_setup(), but
191 * this function can be called earlier than that. So better gracefully skip those. */
192 return true;
193 }
194
195 /* If there is already a slot assigned, there's nothing to do here. */
196 PointerRNA current_slot_ptr = RNA_property_pointer_get(&action_slot_owner_ptr,
197 &action_slot_prop);
198 if (current_slot_ptr.data) {
199 return true;
200 }
201
202 /* Reset the "last used slot identifier" to the default "Legacy Slot". That way
203 * generic_slot_for_autoassign() will pick up on legacy slots automatically.
204 *
205 * Note that this function should only run on legacy users of Actions, i.e. they are not
206 * expected to have any last-used slot at all. The field in DNA can still be set, though,
207 * because the 4.3 code already has the data model for slotted Actions. */
208
209 /* Ensure that the identifier has the correct ID type prefix. */
210 *reinterpret_cast<short *>(last_used_slot_identifier) = GS(animated_id.name);
211
212 static_assert(Slot::identifier_length_max > 2); /* Because of the -2 below. */
213 BLI_strncpy_utf8(last_used_slot_identifier + 2,
216
217 Slot *slot_to_assign = generic_slot_for_autoassign(
218 animated_id, action, last_used_slot_identifier);
219 if (!slot_to_assign) {
220 /* This means that there is no slot that can be found by name, not even the "Legacy Slot"
221 * name. Keep the ID unanimated, as this means that the referenced Action has changed
222 * significantly since this file was opened. */
223 BKE_reportf(reports,
225 "\"%s\" is using Action \"%s\", which does not have a slot with identifier "
226 "\"%s\" or \"%s\". Manually assign the right action slot to \"%s\".\n",
227 animated_id.name,
228 action.id.name + 2,
229 last_used_slot_identifier,
230 animated_id.name,
231 animated_id.name + 2);
232 return true;
233 }
234
235 PointerRNA slot_to_assign_ptr = RNA_pointer_create_discrete(
236 &action.id, &RNA_ActionSlot, slot_to_assign);
238 &action_slot_owner_ptr, &action_slot_prop, slot_to_assign_ptr, reports);
239 RNA_property_update_main(&bmain, nullptr, &action_slot_owner_ptr, &action_slot_prop);
240
241 return true;
242 };
243
244 /* Note that the code below does not remove the `action_assignment_needs_slot` tag. One ID can
245 * use multiple Actions (via NLA, Action constraints, etc.); if one of those Action is a legacy
246 * one from a linked datablock, this ID may needs to be re-visited after the library file was
247 * versioned. Rather than trying to figure out if re-visiting is necessary, this function is safe
248 * to call multiple times, and all that's lost is a little bit of CPU time. */
249
250 ID *id;
251 FOREACH_MAIN_ID_BEGIN (&bmain, id) {
252 /* Process the ID itself. */
254 foreach_action_slot_use_with_rna(*id, version_slot_assignment);
255 }
256
257 /* Process embedded IDs, as these are not listed in bmain, but still can
258 * have their own Action+Slot. Unfortunately there is no generic looper
259 * for embedded IDs. At this moment the only animatable embedded ID is a
260 * node tree. */
262 if (node_tree && BLO_readfile_id_runtime_tags(node_tree->id).action_assignment_needs_slot) {
263 foreach_action_slot_use_with_rna(node_tree->id, version_slot_assignment);
264 }
265 }
267}
268
269} // namespace blender::animrig::versioning
Functions and classes to work with Actions.
Functionality to iterate an Action in various ways.
Functions for backward compatibility with the legacy Action API.
Versioning of old animation data. Most animation versioning code lives in the versioning_xxx....
#define FOREACH_MAIN_ID_END
Definition BKE_main.hh:583
#define FOREACH_MAIN_ID_BEGIN(_bmain, _id)
Definition BKE_main.hh:577
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_WARNING
Definition BKE_report.hh:38
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
char * BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
external readfile function prototypes.
ID_Readfile_Data::Tags BLO_readfile_id_runtime_tags(ID &id)
Definition readfile.cc:2206
#define DATA_(msgid)
#define DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE
void slot_identifier_define(Slot &slot, StringRefNull new_identifier)
Layer & layer_add(std::optional< StringRefNull > name)
Strip & strip_add(Action &owning_action, Strip::Type strip_type)
std::string idtype_string() const
static constexpr int identifier_length_max
const T & data(const Action &owning_action) const
#define GS(x)
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
constexpr const char * DEFAULT_LEGACY_LAYER_NAME
constexpr const char * DEFAULT_LEGACY_SLOT_NAME
void convert_legacy_animato_action(bAction &dna_action)
Definition versioning.cc:75
void tag_action_user_for_slotted_actions_conversion(ID &animated_id)
bool action_is_layered(const bAction &dna_action)
Definition versioning.cc:38
void convert_legacy_action_assignments(Main &bmain, ReportList *reports)
void convert_legacy_animato_actions(Main &bmain)
Definition versioning.cc:57
void tag_action_users_for_slotted_actions_conversion(Main &bmain)
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)
Slot * generic_slot_for_autoassign(const ID &animated_id, Action &action, StringRefNull last_slot_identifier)
bNodeTree * node_tree_from_id(ID *id)
Definition node.cc:4568
void RNA_property_pointer_set(PointerRNA *ptr, PropertyRNA *prop, PointerRNA ptr_value, ReportList *reports)
PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_update_main(Main *bmain, Scene *scene, PointerRNA *ptr, PropertyRNA *prop)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
struct bActionGroup ** group_array
struct FCurve ** fcurve_array
Definition DNA_ID.h:414
ID_RuntimeHandle * runtime
Definition DNA_ID.h:538
char name[258]
Definition DNA_ID.h:432
ListBase actions
Definition BKE_main.hh:300
void * data
Definition RNA_types.hh:53
struct ActionChannelbag * channelbag
ListBase curves
int32_t last_slot_handle
ListBase groups