Blender V5.0
animrig/intern/keyingsets.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_keyframing.hh"
10#include "ANIM_keyingsets.hh"
11
12#include "BKE_animsys.h"
13#include "BKE_context.hh"
14#include "BKE_main.hh"
15#include "BKE_report.hh"
16
17#include "BLI_listbase.h"
18#include "BLI_string.h"
19
20#include "DEG_depsgraph.hh"
21
22#include "DNA_anim_types.h"
23#include "DNA_scene_types.h"
24
25#include "RNA_access.hh"
26
27#include "WM_api.hh"
28
29/* Keying Set Type Info declarations. */
30static ListBase keyingset_type_infos = {nullptr, nullptr};
31ListBase builtin_keyingsets = {nullptr, nullptr};
32
33namespace blender::animrig {
34
36{
37 /* Create a new KeyingSet
38 * - inherit name and keyframing settings from the typeinfo
39 */
41 keyingset_info->idname,
42 keyingset_info->name,
43 1,
44 keyingset_info->keyingflag);
45
46 /* Link this KeyingSet with its typeinfo. */
47 memcpy(&keyingset->typeinfo, keyingset_info->idname, sizeof(keyingset->typeinfo));
48
49 /* Copy description. */
50 STRNCPY(keyingset->description, keyingset_info->description);
51
52 /* Add type-info to the list. */
53 BLI_addtail(&keyingset_type_infos, keyingset_info);
54}
55
56void keyingset_info_unregister(Main *bmain, KeyingSetInfo *keyingset_info)
57{
58 /* Find relevant builtin KeyingSets which use this, and remove them. */
59 /* TODO: this isn't done now, since unregister is really only used at the moment when we
60 * reload the scripts, which kind of defeats the purpose of "builtin"? */
62 /* Remove if matching typeinfo name. */
63 if (!STREQ(keyingset->typeinfo, keyingset_info->idname)) {
64 continue;
65 }
66 Scene *scene;
67 BKE_keyingset_free_paths(keyingset);
69
70 for (scene = static_cast<Scene *>(bmain->scenes.first); scene;
71 scene = static_cast<Scene *>(scene->id.next))
72 {
73 BLI_remlink_safe(&scene->keyingsets, keyingset);
74 }
75
76 MEM_freeN(keyingset);
77 }
78
79 BLI_freelinkN(&keyingset_type_infos, keyingset_info);
80}
81
83{
84 /* Free type infos. */
86 /* Free extra RNA data, and remove from list. */
87 if (keyingset_info->rna_ext.free) {
88 keyingset_info->rna_ext.free(keyingset_info->rna_ext.data);
89 }
90 BLI_freelinkN(&keyingset_type_infos, keyingset_info);
91 }
92
94}
95
96bool keyingset_find_id(KeyingSet *keyingset, ID *id)
97{
98 if (ELEM(nullptr, keyingset, id)) {
99 return false;
100 }
101
102 return BLI_findptr(&keyingset->paths, id, offsetof(KS_Path, id)) != nullptr;
103}
104
106{
107 if ((name == nullptr) || (name[0] == 0)) {
108 return nullptr;
109 }
110
111 /* Search by comparing names. */
112 return static_cast<KeyingSetInfo *>(
114}
115
117{
118 if (name[0] == 0) {
119 return nullptr;
120 }
121
122 /* Loop over KeyingSets checking names. */
124 if (STREQ(name, keyingset->idname)) {
125 return keyingset;
126 }
127 }
128
129/* Complain about missing keying sets on debug builds. */
130#ifndef NDEBUG
131 printf("%s: '%s' not found\n", __func__, name);
132#endif
133
134 return nullptr;
135}
136
137KeyingSet *get_keyingset_for_autokeying(const Scene *scene, const char *transformKSName)
138{
139 /* Get KeyingSet to use
140 * - use the active KeyingSet if defined (and user wants to use it for all autokeying),
141 * or otherwise key transforms only
142 */
144 return scene_get_active_keyingset(scene);
145 }
146
149 }
150
151 return builtin_keyingset_get_named(transformKSName);
152}
153
155{
156 /* If no scene, we've got no hope of finding the Keying Set. */
157 if (scene == nullptr) {
158 return nullptr;
159 }
160
161 /* Currently, there are several possibilities here:
162 * - 0: no active keying set
163 * - > 0: one of the user-defined Keying Sets, but indices start from 0 (hence the -1)
164 * - < 0: a builtin keying set
165 */
166 if (scene->active_keyingset > 0) {
167 return static_cast<KeyingSet *>(BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1));
168 }
169 return static_cast<KeyingSet *>(
171}
172
174 ID *id,
175 StructRNA *srna,
176 void *data)
177{
178 if (ELEM(nullptr, srna, data, id)) {
179 return;
180 }
181 sources.append(RNA_pointer_create_discrete(id, srna, data));
182}
183
185{
186 if (id == nullptr) {
187 return;
188 }
189 sources.append(RNA_id_pointer_create(id));
190}
191
192/* Special 'Overrides' Iterator for Relative KeyingSets ------ */
193
194/* Iterator used for overriding the behavior of iterators defined for
195 * relative Keying Sets, with the main usage of this being operators
196 * requiring Auto Keyframing. Internal Use Only!
197 */
198static void RKS_ITER_overrides_list(KeyingSetInfo *keyingset_info,
199 bContext *C,
200 KeyingSet *keyingset,
202{
203 for (PointerRNA ptr : sources) {
204 /* Run generate callback on this data. */
205 keyingset_info->generate(keyingset_info, C, keyingset, &ptr);
206 }
207}
208
211 KeyingSet *keyingset)
212{
213 if (keyingset == nullptr) {
215 }
216
217 /* If relative Keying Sets, poll and build up the paths. */
218 if (keyingset->flag & KEYINGSET_ABSOLUTE) {
220 }
221
222 KeyingSetInfo *keyingset_info = keyingset_info_find_name(keyingset->typeinfo);
223
224 /* Clear all existing paths
225 * NOTE: BKE_keyingset_free_paths() frees all of the paths for the KeyingSet, but not the set
226 * itself.
227 */
228 BKE_keyingset_free_paths(keyingset);
229
230 /* Get the associated 'type info' for this KeyingSet. */
231 if (keyingset_info == nullptr) {
233 }
234 /* TODO: check for missing callbacks! */
235
236 /* Check if it can be used in the current context. */
237 if (!keyingset_info->poll(keyingset_info, C)) {
238 /* Poll callback tells us that KeyingSet is useless in current context. */
239 /* FIXME: the poll callback needs to give us more info why. */
241 }
242
243 /* If a list of data sources are provided, run a special iterator over them,
244 * otherwise, just continue per normal.
245 */
246 if (sources != nullptr) {
247 RKS_ITER_overrides_list(keyingset_info, C, keyingset, *sources);
248 }
249 else {
250 keyingset_info->iter(keyingset_info, C, keyingset);
251 }
252
253 /* If we don't have any paths now, then this still qualifies as invalid context. */
254 /* FIXME: we need some error conditions (to be retrieved from the iterator why this failed!)
255 */
256 if (BLI_listbase_is_empty(&keyingset->paths)) {
258 }
259
261}
262
263/* Determine which keying flags apply based on the override flags. */
265 const eInsertKeyFlags overrides,
266 const eInsertKeyFlags own_flags)
267{
268 /* Pass through all flags by default (i.e. even not explicitly listed ones). */
269 eInsertKeyFlags result = base_flags;
270
271/* The logic for whether a keying flag applies is as follows:
272 * - If the flag in question is set in "overrides", that means that the
273 * status of that flag in "own_flags" is used
274 * - If however the flag isn't set, then its value in "base_flags" is used
275 * instead (i.e. no override)
276 */
277#define APPLY_KEYINGFLAG_OVERRIDE(kflag) \
278 if (overrides & kflag) { \
279 result &= ~kflag; \
280 result |= (own_flags & kflag); \
281 }
282
283 /* Apply the flags one by one...
284 * (See rna_def_common_keying_flags() for the supported flags)
285 */
288
289#undef APPLY_KEYINGFLAG_OVERRIDE
290
291 return result;
292}
293
295 KS_Path *keyingset_path,
296 KeyingSet *keyingset,
297 const eInsertKeyFlags insert_key_flags,
298 const ModifyKeyMode mode,
299 const float frame)
300{
301 /* Since keying settings can be defined on the paths too,
302 * apply the settings for this path first. */
303 const eInsertKeyFlags path_insert_key_flags = keyingset_apply_keying_flags(
304 insert_key_flags,
305 eInsertKeyFlags(keyingset_path->keyingoverride),
306 eInsertKeyFlags(keyingset_path->keyingflag));
307
308 const char *groupname = nullptr;
309 /* Get pointer to name of group to add channels to. */
310 if (keyingset_path->groupmode == KSP_GROUP_NONE) {
311 groupname = nullptr;
312 }
313 else if (keyingset_path->groupmode == KSP_GROUP_KSNAME) {
314 groupname = keyingset->name;
315 }
316 else {
317 groupname = keyingset_path->group;
318 }
319
320 /* Init - array_length should be greater than array_index so that
321 * normal non-array entries get keyframed correctly.
322 */
323 int array_index = keyingset_path->array_index;
324 int array_length = array_index;
325
326 /* Get length of array if whole array option is enabled. */
327 if (keyingset_path->flag & KSP_FLAG_WHOLE_ARRAY) {
329 PropertyRNA *prop;
330
331 PointerRNA id_ptr = RNA_id_pointer_create(keyingset_path->id);
332 if (RNA_path_resolve_property(&id_ptr, keyingset_path->rna_path, &ptr, &prop)) {
333 array_length = RNA_property_array_length(&ptr, prop);
334 /* Start from start of array, instead of the previously specified index - #48020 */
335 array_index = 0;
336 }
337 }
338
339 /* We should do at least one step. */
340 if (array_length == array_index) {
341 array_length++;
342 }
343
344 Main *bmain = CTX_data_main(C);
345 ReportList *reports = CTX_wm_reports(C);
346 Scene *scene = CTX_data_scene(C);
349 /* For each possible index, perform operation
350 * - Assume that array-length is greater than index. */
353 frame);
354 int keyed_channels = 0;
355
356 CombinedKeyingResult combined_result;
357 for (; array_index < array_length; array_index++) {
358 if (mode == ModifyKeyMode::INSERT) {
359 const std::optional<blender::StringRefNull> group = groupname ? std::optional(groupname) :
360 std::nullopt;
361 const std::optional<int> index = array_index >= 0 ? std::optional(array_index) :
362 std::nullopt;
363 PointerRNA id_rna_pointer = RNA_id_pointer_create(keyingset_path->id);
365 &id_rna_pointer,
366 group,
367 {{keyingset_path->rna_path, {}, index}},
368 std::nullopt,
369 anim_eval_context,
370 keytype,
371 path_insert_key_flags);
372 keyed_channels += result.get_count(SingleKeyingResult::SUCCESS);
373 combined_result.merge(result);
374 }
375 else if (mode == ModifyKeyMode::DELETE_KEY) {
376 RNAPath rna_path = {keyingset_path->rna_path, std::nullopt, array_index};
377 if (array_index < 0) {
378 rna_path.index = std::nullopt;
379 }
380 keyed_channels += delete_keyframe(bmain, reports, keyingset_path->id, rna_path, frame);
381 }
382 }
383
384 if (combined_result.get_count(SingleKeyingResult::SUCCESS) == 0) {
385 combined_result.generate_reports(reports);
386 }
387
388 switch (GS(keyingset_path->id->name)) {
389 case ID_OB: /* Object (or Object-Related) Keyframes */
390 {
391 Object *ob = reinterpret_cast<Object *>(keyingset_path->id);
392
393 /* XXX: only object transforms? */
395 break;
396 }
397 default:
399 break;
400 }
401
403
404 return keyed_channels;
405}
406
409 KeyingSet *keyingset,
410 const ModifyKeyMode mode,
411 const float cfra)
412{
413 if (keyingset == nullptr) {
414 return 0;
415 }
416
417 Scene *scene = CTX_data_scene(C);
418 const eInsertKeyFlags base_kflags = get_keyframing_flags(scene);
420 if (mode == ModifyKeyMode::INSERT) {
421 /* Use context settings as base. */
422 kflag = keyingset_apply_keying_flags(base_kflags,
424 eInsertKeyFlags(keyingset->keyingflag));
425 }
426 else if (mode == ModifyKeyMode::DELETE_KEY) {
427 kflag = INSERTKEY_NOFLAGS;
428 }
429
430 /* If relative Keying Sets, poll and build up the paths. */
431 {
432 const ModifyKeyReturn error = validate_keyingset(C, sources, keyingset);
434 BLI_assert(int(error) < 0);
435 return int(error);
436 }
437 }
438
439 ReportList *reports = CTX_wm_reports(C);
440 int keyed_channels = 0;
441
442 /* Apply the paths as specified in the KeyingSet now. */
443 LISTBASE_FOREACH (KS_Path *, keyingset_path, &keyingset->paths) {
444 /* Skip path if no ID pointer is specified. */
445 if (keyingset_path->id == nullptr) {
446 BKE_reportf(reports,
448 "Skipping path in keying set, as it has no ID (KS = '%s', path = '%s[%d]')",
449 keyingset->name,
450 keyingset_path->rna_path,
451 keyingset_path->array_index);
452 continue;
453 }
454
455 keyed_channels += insert_key_to_keying_set_path(
456 C, keyingset_path, keyingset, kflag, mode, cfra);
457 }
458
459 /* Return the number of channels successfully affected. */
460 BLI_assert(keyed_channels >= 0);
461 return keyed_channels;
462}
463
464} // namespace blender::animrig
Functions to insert, delete or modify keyframes.
Functionality to interact with keying sets.
static constexpr const char * ANIM_KS_AVAILABLE_ID
void BKE_keyingsets_free(struct ListBase *list)
Definition anim_sys.cc:283
AnimationEvalContext BKE_animsys_eval_context_construct(struct Depsgraph *depsgraph, float eval_time) ATTR_WARN_UNUSED_RESULT
Definition anim_sys.cc:738
void BKE_keyingset_free_paths(struct KeyingSet *ks)
Definition anim_sys.cc:267
struct KeyingSet * BKE_keyingset_add(struct ListBase *list, const char idname[], const char name[], short flag, short keyingflag)
Definition anim_sys.cc:134
ReportList * CTX_wm_reports(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
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
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
void * BLI_findstring(const ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:608
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
void BLI_freelinkN(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:270
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
void * BLI_findptr(const struct ListBase *listbase, const void *ptr, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
bool BLI_remlink_safe(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:154
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
#define ELEM(...)
#define STREQ(a, b)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1054
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ ID_RECALC_ANIMATION_NO_FLUSH
Definition DNA_ID.h:1176
@ ID_OB
@ KEYINGSET_ABSOLUTE
eInsertKeyFlags
@ INSERTKEY_MATRIX
@ INSERTKEY_NEEDED
@ INSERTKEY_NOFLAGS
@ KSP_GROUP_KSNAME
@ KSP_GROUP_NONE
@ KSP_FLAG_WHOLE_ARRAY
eBezTriple_KeyframeType
@ AUTOKEY_FLAG_ONLYKEYINGSET
@ AUTOKEY_FLAG_INSERTAVAILABLE
#define C
Definition RandGen.cpp:29
#define NC_ANIMATION
Definition WM_types.hh:388
#define NA_ADDED
Definition WM_types.hh:586
#define ND_KEYFRAME
Definition WM_types.hh:494
static ListBase keyingset_type_infos
ListBase builtin_keyingsets
#define APPLY_KEYINGFLAG_OVERRIDE(kflag)
BMesh const char void * data
BPy_StructRNA * depsgraph
void append(const T &value)
void merge(const CombinedKeyingResult &other)
int get_count(const SingleKeyingResult result) const
void generate_reports(ReportList *reports, eReportType report_level=RPT_ERROR)
#define offsetof(t, d)
#define GS(x)
#define printf(...)
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static void error(const char *str)
static int insert_key_to_keying_set_path(bContext *C, KS_Path *keyingset_path, KeyingSet *keyingset, const eInsertKeyFlags insert_key_flags, const ModifyKeyMode mode, const float frame)
static void RKS_ITER_overrides_list(KeyingSetInfo *keyingset_info, bContext *C, KeyingSet *keyingset, blender::Vector< PointerRNA > &sources)
KeyingSet * builtin_keyingset_get_named(const char name[])
void relative_keyingset_add_source(blender::Vector< PointerRNA > &sources, ID *id, StructRNA *srna, void *data)
KeyingSet * scene_get_active_keyingset(const Scene *scene)
static eInsertKeyFlags keyingset_apply_keying_flags(const eInsertKeyFlags base_flags, const eInsertKeyFlags overrides, const eInsertKeyFlags own_flags)
KeyingSetInfo * keyingset_info_find_name(const char name[])
KeyingSet * get_keyingset_for_autokeying(const Scene *scene, const char *transformKSName)
int delete_keyframe(Main *bmain, ReportList *reports, ID *id, const RNAPath &rna_path, float cfra)
Main Delete Key-Framing API call.
void keyingset_info_register(KeyingSetInfo *keyingset_info)
CombinedKeyingResult insert_keyframes(Main *bmain, PointerRNA *struct_pointer, std::optional< StringRefNull > channel_group, const blender::Span< RNAPath > rna_paths, std::optional< float > scene_frame, const AnimationEvalContext &anim_eval_context, eBezTriple_KeyframeType key_type, eInsertKeyFlags insert_key_flags)
Main key-frame insertion API.
void keyingset_info_unregister(Main *bmain, KeyingSetInfo *keyingset_info)
bool keyingset_find_id(KeyingSet *keyingset, ID *id)
bool is_keying_flag(const Scene *scene, eKeying_Flag flag)
int apply_keyingset(bContext *C, blender::Vector< PointerRNA > *sources, KeyingSet *keyingset, ModifyKeyMode mode, float cfra)
ModifyKeyReturn validate_keyingset(bContext *C, blender::Vector< PointerRNA > *sources, KeyingSet *keyingset)
eInsertKeyFlags get_keyframing_flags(Scene *scene)
const char * name
int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
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:560
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
void * next
Definition DNA_ID.h:417
short keyingoverride
char group[64]
short keyingflag
short groupmode
char * rna_path
cbKeyingSet_Generate generate
char description[1024]
cbKeyingSet_Iterator iter
cbKeyingSet_Poll poll
char name[64]
char typeinfo[64]
ListBase paths
short keyingoverride
char description[1024]
void * first
ListBase scenes
Definition BKE_main.hh:278
std::optional< int > index
Definition RNA_path.hh:66
int active_keyingset
ListBase keyingsets
struct ToolSettings * toolsettings
void WM_main_add_notifier(uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4238