Blender V4.5
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 if (!keyingset_path->rna_path) {
302 /* In case the path is incomplete/not filled in by the user. */
303 return 0;
304 }
305 /* Since keying settings can be defined on the paths too,
306 * apply the settings for this path first. */
307 const eInsertKeyFlags path_insert_key_flags = keyingset_apply_keying_flags(
308 insert_key_flags,
309 eInsertKeyFlags(keyingset_path->keyingoverride),
310 eInsertKeyFlags(keyingset_path->keyingflag));
311
312 const char *groupname = nullptr;
313 /* Get pointer to name of group to add channels to. */
314 if (keyingset_path->groupmode == KSP_GROUP_NONE) {
315 groupname = nullptr;
316 }
317 else if (keyingset_path->groupmode == KSP_GROUP_KSNAME) {
318 groupname = keyingset->name;
319 }
320 else {
321 groupname = keyingset_path->group;
322 }
323
324 /* Init - array_length should be greater than array_index so that
325 * normal non-array entries get keyframed correctly.
326 */
327 int array_index = keyingset_path->array_index;
328 int array_length = array_index;
329
330 /* Get length of array if whole array option is enabled. */
331 if (keyingset_path->flag & KSP_FLAG_WHOLE_ARRAY) {
333 PropertyRNA *prop;
334
335 PointerRNA id_ptr = RNA_id_pointer_create(keyingset_path->id);
336 if (RNA_path_resolve_property(&id_ptr, keyingset_path->rna_path, &ptr, &prop)) {
337 array_length = RNA_property_array_length(&ptr, prop);
338 /* Start from start of array, instead of the previously specified index - #48020 */
339 array_index = 0;
340 }
341 }
342
343 /* We should do at least one step. */
344 if (array_length == array_index) {
345 array_length++;
346 }
347
348 Main *bmain = CTX_data_main(C);
350 Scene *scene = CTX_data_scene(C);
353 /* For each possible index, perform operation
354 * - Assume that array-length is greater than index. */
357 frame);
358 int keyed_channels = 0;
359
360 CombinedKeyingResult combined_result;
361 for (; array_index < array_length; array_index++) {
362 if (mode == ModifyKeyMode::INSERT) {
363 const std::optional<blender::StringRefNull> group = groupname ? std::optional(groupname) :
364 std::nullopt;
365 const std::optional<int> index = array_index >= 0 ? std::optional(array_index) :
366 std::nullopt;
367 PointerRNA id_rna_pointer = RNA_id_pointer_create(keyingset_path->id);
369 &id_rna_pointer,
370 group,
371 {{keyingset_path->rna_path, {}, index}},
372 std::nullopt,
373 anim_eval_context,
374 keytype,
375 path_insert_key_flags);
376 keyed_channels += result.get_count(SingleKeyingResult::SUCCESS);
377 combined_result.merge(result);
378 }
379 else if (mode == ModifyKeyMode::DELETE_KEY) {
380 RNAPath rna_path = {keyingset_path->rna_path, std::nullopt, array_index};
381 if (array_index < 0) {
382 rna_path.index = std::nullopt;
383 }
384 keyed_channels += delete_keyframe(bmain, reports, keyingset_path->id, rna_path, frame);
385 }
386 }
387
388 if (combined_result.get_count(SingleKeyingResult::SUCCESS) == 0) {
389 combined_result.generate_reports(reports);
390 }
391
392 switch (GS(keyingset_path->id->name)) {
393 case ID_OB: /* Object (or Object-Related) Keyframes */
394 {
395 Object *ob = reinterpret_cast<Object *>(keyingset_path->id);
396
397 /* XXX: only object transforms? */
399 break;
400 }
401 default:
403 break;
404 }
405
407
408 return keyed_channels;
409}
410
413 KeyingSet *keyingset,
414 const ModifyKeyMode mode,
415 const float cfra)
416{
417 if (keyingset == nullptr) {
418 return 0;
419 }
420
421 Scene *scene = CTX_data_scene(C);
422 const eInsertKeyFlags base_kflags = get_keyframing_flags(scene);
424 if (mode == ModifyKeyMode::INSERT) {
425 /* Use context settings as base. */
426 kflag = keyingset_apply_keying_flags(base_kflags,
428 eInsertKeyFlags(keyingset->keyingflag));
429 }
430 else if (mode == ModifyKeyMode::DELETE_KEY) {
431 kflag = INSERTKEY_NOFLAGS;
432 }
433
434 /* If relative Keying Sets, poll and build up the paths. */
435 {
436 const ModifyKeyReturn error = validate_keyingset(C, sources, keyingset);
438 BLI_assert(int(error) < 0);
439 return int(error);
440 }
441 }
442
444 int keyed_channels = 0;
445
446 /* Apply the paths as specified in the KeyingSet now. */
447 LISTBASE_FOREACH (KS_Path *, keyingset_path, &keyingset->paths) {
448 /* Skip path if no ID pointer is specified. */
449 if (keyingset_path->id == nullptr) {
452 "Skipping path in keying set, as it has no ID (KS = '%s', path = '%s[%d]')",
453 keyingset->name,
454 keyingset_path->rna_path,
455 keyingset_path->array_index);
456 continue;
457 }
458
459 keyed_channels += insert_key_to_keying_set_path(
460 C, keyingset_path, keyingset, kflag, mode, cfra);
461 }
462
463 /* Return the number of channels successfully affected. */
464 BLI_assert(keyed_channels >= 0);
465 return keyed_channels;
466}
467
468} // 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:280
AnimationEvalContext BKE_animsys_eval_context_construct(struct Depsgraph *depsgraph, float eval_time) ATTR_WARN_UNUSED_RESULT
Definition anim_sys.cc:735
void BKE_keyingset_free_paths(struct KeyingSet *ks)
Definition anim_sys.cc:264
struct KeyingSet * BKE_keyingset_add(struct ListBase *list, const char idname[], const char name[], short flag, short keyingflag)
Definition anim_sys.cc:131
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
#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:688
#define ELEM(...)
#define STREQ(a, b)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:962
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
@ ID_RECALC_ANIMATION_NO_FLUSH
Definition DNA_ID.h:1084
@ 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:385
#define NA_ADDED
Definition WM_types.hh:583
ReportList * reports
Definition WM_types.hh:1025
#define ND_KEYFRAME
Definition WM_types.hh:491
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 printf(...)
#define GS(a)
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)
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:404
void * next
Definition DNA_ID.h:407
char name[66]
Definition DNA_ID.h:415
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:245
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:4227