Blender V5.0
animrig/intern/keyframing.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cmath>
10#include <string>
11
12#include <fmt/format.h>
13
14#include "ANIM_action.hh"
16#include "ANIM_action_legacy.hh"
17#include "ANIM_animdata.hh"
18#include "ANIM_fcurve.hh"
19#include "ANIM_keyframing.hh"
20#include "ANIM_rna.hh"
21#include "ANIM_visualkey.hh"
22
23#include "BKE_action.hh"
24#include "BKE_anim_data.hh"
25#include "BKE_animsys.h"
26#include "BKE_fcurve.hh"
27#include "BKE_idtype.hh"
28#include "BKE_lib_id.hh"
29#include "BKE_nla.hh"
30#include "BKE_report.hh"
31
32#include "DNA_scene_types.h"
33
34#include "BLI_bit_vector.hh"
35#include "BLI_dynstr.h"
36#include "BLI_math_base.h"
37#include "BLI_utildefines.h"
38#include "BLT_translation.hh"
39
40#include "DEG_depsgraph.hh"
42#include "DNA_anim_types.h"
43#include "MEM_guardedalloc.h"
44#include "RNA_access.hh"
45#include "RNA_path.hh"
46#include "RNA_prototypes.hh"
47
48#include "WM_types.hh"
49
50namespace blender::animrig {
51
53{
54 result_counter.fill(0);
55}
56
58{
59 result_counter[int(result)] += count;
60}
61
63{
64 for (int i = 0; i < result_counter.size(); i++) {
65 result_counter[i] += other.result_counter[i];
66 }
67}
68
70{
71 return result_counter[int(result)];
72}
73
75{
76 /* For loop starts at 1 to skip the SUCCESS flag. Assumes that SUCCESS is 0 and the rest of the
77 * enum are sequential values. */
78 static_assert(int(SingleKeyingResult::SUCCESS) == 0);
79 for (int i = 1; i < result_counter.size(); i++) {
80 if (result_counter[i] > 0) {
81 return true;
82 }
83 }
84 return false;
85}
86
88{
89 if (!this->has_errors() && this->get_count(SingleKeyingResult::SUCCESS) == 0) {
91 reports, RPT_WARNING, "No keys have been inserted and no errors have been reported.");
92 return;
93 }
94
97 const int error_count = this->get_count(SingleKeyingResult::UNKNOWN_FAILURE);
98 errors.append(fmt::format(
99 fmt::runtime(RPT_("There were {:d} keying failures for unknown reasons.")), error_count));
100 }
101
103 const int error_count = this->get_count(SingleKeyingResult::CANNOT_CREATE_FCURVE);
104 errors.append(fmt::format(
105 fmt::runtime(RPT_("Could not create {:d} F-Curve(s). This can happen when only "
106 "inserting to available F-Curves.")),
107 error_count));
108 }
109
111 const int error_count = this->get_count(SingleKeyingResult::FCURVE_NOT_KEYFRAMEABLE);
112 errors.append(
113 fmt::format(fmt::runtime(RPT_(
114 "{:d} F-Curve(s) are not keyframeable. They might be locked or sampled.")),
115 error_count));
116 }
117
119 const int error_count = this->get_count(SingleKeyingResult::NO_KEY_NEEDED);
120 errors.append(fmt::format(
121 fmt::runtime(RPT_(
122 "Due to the setting 'Only Insert Needed', {:d} keyframe(s) have not been inserted.")),
123 error_count));
124 }
125
128 errors.append(fmt::format(
129 fmt::runtime(RPT_("Due to the NLA stack setup, {:d} keyframe(s) have not been inserted.")),
130 error_count));
131 }
132
134 const int error_count = this->get_count(SingleKeyingResult::ID_NOT_EDITABLE);
135 errors.append(fmt::format(
136 fmt::runtime(RPT_("Inserting keys on {:d} data-block(s) has been skipped because "
137 "they are not editable.")),
138 error_count));
139 }
140
142 const int error_count = this->get_count(SingleKeyingResult::ID_NOT_ANIMATABLE);
143 errors.append(fmt::format(
144 fmt::runtime(RPT_("Inserting keys on {:d} data-block(s) has been skipped because "
145 "they cannot be animated.")),
146 error_count));
147 }
148
150 const int error_count = this->get_count(SingleKeyingResult::CANNOT_RESOLVE_PATH);
151 errors.append(fmt::format(
152 fmt::runtime(RPT_("Inserting keys on {:d} data-block(s) has been skipped because "
153 "the RNA path wasn't valid for them.")),
154 error_count));
155 }
156
158 const int error_count = this->get_count(SingleKeyingResult::NO_VALID_LAYER);
159 errors.append(fmt::format(
160 fmt::runtime(RPT_("Inserting keys on {:d} data-block(s) has been skipped because "
161 "there were no layers that could accept the keys.")),
162 error_count));
163 }
164
166 const int error_count = this->get_count(SingleKeyingResult::NO_VALID_STRIP);
167 errors.append(fmt::format(
168 fmt::runtime(RPT_("Inserting keys on {:d} data-block(s) has been skipped because "
169 "there were no strips that could accept the keys.")),
170 error_count));
171 }
172
174 const int error_count = this->get_count(SingleKeyingResult::NO_VALID_SLOT);
175 errors.append(fmt::format(
176 fmt::runtime(RPT_("Inserting keys on {:d} data-block(s) has been skipped because "
177 "of missing action slots.")),
178 error_count));
179 }
180
181 if (errors.is_empty()) {
182 BKE_report(reports, RPT_WARNING, "Encountered unhandled error during keyframing");
183 return;
184 }
185
186 if (errors.size() == 1) {
187 BKE_report(reports, report_level, errors[0].c_str());
188 return;
189 }
190
191 std::string error_message = RPT_("Inserting keyframes failed:");
192 for (const std::string &error : errors) {
193 error_message.append(fmt::format("\n- {}", error));
194 }
195 BKE_report(reports, report_level, error_message.c_str());
196}
197
198std::optional<StringRefNull> default_channel_group_for_path(const PointerRNA *animated_struct,
199 const StringRef prop_rna_path)
200{
201 if (animated_struct->type == &RNA_PoseBone) {
202 bPoseChannel *pose_channel = static_cast<bPoseChannel *>(animated_struct->data);
203 return pose_channel->name;
204 }
205
206 if (animated_struct->type == &RNA_Object) {
207 if (prop_rna_path.find("location") != StringRef::not_found ||
208 prop_rna_path.find("rotation") != StringRef::not_found ||
209 prop_rna_path.find("scale") != StringRef::not_found)
210 {
211 /* NOTE: Keep this label in sync with the "ID" case in
212 * keyingsets_utils.py :: get_transform_generators_base_info()
213 */
214 return "Object Transforms";
215 }
216 }
217
218 return std::nullopt;
219}
220
222{
223 /* First clear out all the flags that should be updated by this function, before setting just the
224 * ones suitable for this property type. */
226 fcu->flag |= fcurve_flags_for_property_type(prop_type);
227}
228
229bool is_keying_flag(const Scene *scene, const eKeying_Flag flag)
230{
231 if (scene) {
232 return (scene->toolsettings->keying_flag & flag) || (U.keying_flag & flag);
233 }
234 return U.keying_flag & flag;
235}
236
238{
240
241 /* Visual keying. */
244 }
245
246 /* Cycle-aware keyframe insertion - preserve cycle period and flow. */
249 }
250
253 }
254
255 return flag;
256}
257
265static bool assigned_action_has_keyframe_at(AnimData &adt, const float frame)
266{
267 if (adt.action == nullptr) {
268 return false;
269 }
270
271 if (adt.action->flag & ACT_MUTED) {
272 return false;
273 }
274
276 if (fcurve_frame_has_keyframe(fcu, frame)) {
277 return true;
278 }
279 }
280
281 return false;
282}
283
284/* Checks whether an Object has a keyframe for a given frame. */
285static bool object_frame_has_keyframe(Object *ob, const float frame)
286{
287 if (ob == nullptr) {
288 return false;
289 }
290
291 /* Check its own animation data - specifically, the action it contains. */
292 if ((ob->adt) && (ob->adt->action)) {
293 /* #41525 - When the active action is a NLA strip being edited,
294 * we need to correct the frame number to "look inside" the
295 * remapped action
296 */
297 const float ob_frame = BKE_nla_tweakedit_remap(ob->adt, frame, NLATIME_CONVERT_UNMAP);
298
299 if (assigned_action_has_keyframe_at(*ob->adt, ob_frame)) {
300 return true;
301 }
302 }
303
304 /* nothing found */
305 return false;
306}
307
308bool id_frame_has_keyframe(ID *id, float frame)
309{
310 if (id == nullptr) {
311 return false;
312 }
313
314 /* Perform special checks for 'macro' types. */
315 switch (GS(id->name)) {
316 case ID_OB:
317 return object_frame_has_keyframe((Object *)id, frame);
318
319 default: {
321
322 /* only check keyframes in active action */
323 if (adt) {
324 return assigned_action_has_keyframe_at(*adt, frame);
325 }
326 break;
327 }
328 }
329
330 return false;
331}
332
334{
335 return (insert_key_flags & (INSERTKEY_REPLACE | INSERTKEY_AVAILABLE)) == 0;
336}
337
339static void make_new_fcurve_cyclic(FCurve *fcu, const blender::float2 &action_range)
340{
341 /* The curve must contain one (newly-added) keyframe. */
342 if (fcu->totvert != 1 || !fcu->bezt) {
343 return;
344 }
345
346 const float period = action_range[1] - action_range[0];
347
348 if (period < 0.1f) {
349 return;
350 }
351
352 /* Move the keyframe into the range. */
353 const float frame_offset = fcu->bezt[0].vec[1][0] - action_range[0];
354 const float fix = floorf(frame_offset / period) * period;
355
356 fcu->bezt[0].vec[0][0] -= fix;
357 fcu->bezt[0].vec[1][0] -= fix;
358 fcu->bezt[0].vec[2][0] -= fix;
359
360 /* Duplicate and offset the keyframe. */
361 fcu->bezt = static_cast<BezTriple *>(MEM_reallocN(fcu->bezt, sizeof(BezTriple) * 2));
362 fcu->totvert = 2;
363
364 fcu->bezt[1] = fcu->bezt[0];
365 fcu->bezt[1].vec[0][0] += period;
366 fcu->bezt[1].vec[1][0] += period;
367 fcu->bezt[1].vec[2][0] += period;
368
369 if (!fcu->modifiers.first) {
371 }
372}
373
374/* Check indices that were intended to be remapped and report any failed remaps. */
377 PropertyRNA *prop,
378 const int index,
379 const int count,
380 const bool force_all,
381 const BitSpan successful_remaps)
382{
383
384 DynStr *ds_failed_indices = BLI_dynstr_new();
385
386 int total_failed = 0;
387 for (int i = 0; i < count; i++) {
388 const bool cur_index_evaluated = ELEM(index, i, -1) || force_all;
389 if (!cur_index_evaluated) {
390 /* `values[i]` was never intended to be remapped. */
391 continue;
392 }
393
394 if (successful_remaps[i]) {
395 /* `values[i]` successfully remapped. */
396 continue;
397 }
398
399 total_failed++;
400 /* Report that `values[i]` were intended to be remapped but failed remapping process. */
401 BLI_dynstr_appendf(ds_failed_indices, "%d, ", i);
402 }
403
404 if (total_failed == 0) {
405 BLI_dynstr_free(ds_failed_indices);
406 return;
407 }
408
409 char *str_failed_indices = BLI_dynstr_get_cstring(ds_failed_indices);
410 BLI_dynstr_free(ds_failed_indices);
411
412 BKE_reportf(reports,
414 "Could not insert %i keyframe(s) due to zero NLA influence, base value, or value "
415 "remapping failed: %s.%s for indices [%s]",
416 total_failed,
417 ptr.owner_id->name,
419 str_failed_indices);
420
421 MEM_freeN(str_failed_indices);
422}
423
424static Vector<float> get_keyframe_values(PointerRNA *ptr, PropertyRNA *prop, const bool visual_key)
425{
426 Vector<float> values;
427
428 if (visual_key && visualkey_can_use(ptr, prop)) {
429 /* Visual-keying is only available for object data-blocks and pose-channels,
430 * as it works by key-framing using a value extracted from the final matrix
431 * instead of using the kt system to extract a value. */
432 values = visualkey_get_values(ptr, prop);
433 }
434 else {
435 values = get_rna_values(ptr, prop);
436 }
437 return values;
438}
439
441 const MutableSpan<float> values,
442 const int index,
444 PropertyRNA &prop,
445 NlaKeyframingContext *nla_context,
446 const AnimationEvalContext *anim_eval_context,
447 ReportList *reports,
448 bool *force_all)
449{
450 BitVector<> successful_remaps(values.size(), false);
452 nla_context, &ptr, &prop, values, index, anim_eval_context, force_all, successful_remaps);
454 reports, ptr, &prop, index, values.size(), false, successful_remaps);
455 return successful_remaps;
456}
457
458static float nla_time_remap(float time,
459 const AnimationEvalContext *anim_eval_context,
460 PointerRNA *id_ptr,
461 AnimData *adt,
462 bAction *act,
463 ListBase *nla_cache,
464 NlaKeyframingContext **r_nla_context)
465{
466 if (adt && adt->action == act) {
468 nla_cache, id_ptr, adt, anim_eval_context);
469
470 const float remapped_frame = BKE_nla_tweakedit_remap(adt, time, NLATIME_CONVERT_UNMAP);
471 return remapped_frame;
472 }
473
474 *r_nla_context = nullptr;
475 return time;
476}
477
478/* Insert the specified keyframe value into a single F-Curve. */
480 FCurve *fcu, float cfra, float curval, eBezTriple_KeyframeType keytype, eInsertKeyFlags flag)
481{
482 if (!BKE_fcurve_is_keyframable(fcu)) {
484 }
485
487 settings.keyframe_type = keytype;
488
489 return insert_vert_fcurve(fcu, {cfra, curval}, settings, flag);
490}
491
494 PropertyRNA *prop,
495 FCurve *fcu,
496 const AnimationEvalContext *anim_eval_context,
498 NlaKeyframingContext *nla_context,
500{
501
502 if (fcu == nullptr) {
503 BKE_report(reports, RPT_ERROR, "No F-Curve to add keyframes to");
504 return false;
505 }
506
507 if ((ptr.owner_id == nullptr) && (ptr.data == nullptr)) {
509 reports, RPT_ERROR, "No RNA pointer available to retrieve values for keyframing from");
510 return false;
511 }
512
513 if (prop == nullptr) {
514 PointerRNA tmp_ptr;
515
516 if (RNA_path_resolve_property(&ptr, fcu->rna_path, &tmp_ptr, &prop) == false) {
517 const char *idname = (ptr.owner_id) ? ptr.owner_id->name : RPT_("<No ID pointer>");
518
519 BKE_reportf(reports,
520 RPT_ERROR,
521 "Could not insert keyframe, as RNA path is invalid for the given ID (ID = %s, "
522 "path = %s)",
523 idname,
524 fcu->rna_path);
525 return false;
526 }
527
528 /* Property found, so overwrite 'ptr' to make later code easier. */
529 ptr = tmp_ptr;
530 }
531
532 /* Update F-Curve flags to ensure proper behavior for property type. */
534
535 const int index = fcu->array_index;
536 const bool visual_keyframing = flag & INSERTKEY_MATRIX;
537 Vector<float> values = get_keyframe_values(&ptr, prop, visual_keyframing);
538
540 values.as_mutable_span(),
541 index,
542 ptr,
543 *prop,
544 nla_context,
545 anim_eval_context,
546 reports,
547 nullptr);
548
549 float current_value = 0.0f;
550 if (index >= 0 && index < values.size()) {
551 current_value = values[index];
552 }
553
554 /* This happens if NLA rejects this insertion. */
555 if (!successful_remaps[index]) {
556 return false;
557 }
558
559 const float cfra = anim_eval_context->eval_time;
560 const SingleKeyingResult result = insert_keyframe_value(fcu, cfra, current_value, keytype, flag);
561
563 BKE_reportf(reports,
564 RPT_ERROR,
565 "Failed to insert keys on F-Curve with path '%s[%d]', ensure that it is not "
566 "locked or sampled, and try removing F-Modifiers",
567 fcu->rna_path,
568 fcu->array_index);
569 }
571}
572
576 PropertyRNA *prop,
577 bAction *act,
578 const char group[],
579 const char rna_path[],
580 int array_index,
581 const float fcurve_frame,
582 float curval,
585{
586 BLI_assert(rna_path != nullptr);
587
588 /* Make sure the F-Curve exists.
589 * - if we're replacing keyframes only, DO NOT create new F-Curves if they do not exist yet
590 * but still try to get the F-Curve if it exists...
591 */
592
594 action_fcurve_ensure_ex(bmain, act, group, ptr, {rna_path, array_index}) :
595 fcurve_find_in_action(act, {rna_path, array_index});
596
597 /* We may not have a F-Curve when we're replacing only. */
598 if (!fcu) {
600 }
601
602 const bool is_new_curve = (fcu->totvert == 0);
603
604 /* If the curve has only one key, make it cyclic if appropriate. */
605 const bool is_cyclic_action = (flag & INSERTKEY_CYCLE_AWARE) && act->wrap().is_cyclic();
606
607 if (is_cyclic_action && fcu->totvert == 1) {
609 }
610
611 /* Update F-Curve flags to ensure proper behavior for property type. */
613
615 fcu, fcurve_frame, curval, keytype, flag);
616
617 /* If the curve is new, make it cyclic if appropriate. */
618 if (is_cyclic_action && is_new_curve) {
620 }
621
622 return result;
623}
624
625/* ************************************************** */
626/* KEYFRAME DELETION */
627
628/* Main Keyframing API call:
629 * Use this when validation of necessary animation data isn't necessary as it
630 * already exists. It will delete a keyframe at the current frame.
631 *
632 * The flag argument is used for special settings that alter the behavior of
633 * the keyframe deletion. These include the quick refresh options.
634 */
635
636static void deg_tag_after_keyframe_delete(Main *bmain, ID *id, AnimData *adt)
637{
638 if (adt->action == nullptr) {
639 /* In the case last f-curve was removed need to inform dependency graph
640 * about relations update, since it needs to get rid of animation operation
641 * for this data-block. */
644 }
645 else {
647 }
648}
649
650int delete_keyframe(Main *bmain, ReportList *reports, ID *id, const RNAPath &rna_path, float cfra)
651{
653
654 if (ELEM(nullptr, id, adt)) {
655 BKE_report(reports, RPT_ERROR, "No ID block and/or AnimData to delete keyframe from");
656 return 0;
657 }
658
660 PropertyRNA *prop;
662 if (RNA_path_resolve_property(&id_ptr, rna_path.path.c_str(), &ptr, &prop) == false) {
664 reports,
665 RPT_ERROR,
666 "Could not delete keyframe, as RNA path is invalid for the given ID (ID = %s, path = %s)",
667 id->name,
668 rna_path.path.c_str());
669 return 0;
670 }
671
672 if (!adt->action) {
673 BKE_reportf(reports, RPT_ERROR, "No action to delete keyframes from for ID = %s", id->name);
674 return 0;
675 }
676 bAction *act = adt->action;
678 int array_index = rna_path.index.value_or(0);
679 int array_index_max = array_index + 1;
680
681 if (!rna_path.index.has_value()) {
682 array_index_max = RNA_property_array_length(&ptr, prop);
683 /* For single properties, increase max_index so that the property itself gets included,
684 * but don't do this for standard arrays since that can cause corruption issues
685 * (extra unused curves).
686 */
687 if (array_index_max == array_index) {
688 array_index_max++;
689 }
690 }
691
692 Action &action = act->wrap();
693 Vector<FCurve *> modified_fcurves;
694 if (action.is_action_layered()) {
695 /* Just being defensive in the face of the NLA shenanigans above. This
696 * probably isn't necessary, but it doesn't hurt. */
697 BLI_assert(adt->action == act && action.slot_for_handle(adt->slot_handle) != nullptr);
698
699 Span<FCurve *> fcurves = fcurves_for_action_slot(action, adt->slot_handle);
700 /* This loop's clause is copied from the pre-existing code for legacy
701 * actions below, to ensure behavioral consistency between the two code
702 * paths. In the future when legacy actions are removed, we can restructure
703 * it to be clearer. */
704 for (; array_index < array_index_max; array_index++) {
705 FCurve *fcurve = fcurve_find(fcurves, {rna_path.path, array_index});
706 if (fcurve == nullptr) {
707 continue;
708 }
709 if (fcurve_delete_keyframe_at_time(fcurve, cfra)) {
710 modified_fcurves.append(fcurve);
711 }
712 }
713 }
714 else {
715 /* Will only loop once unless the array index was -1. */
716 for (; array_index < array_index_max; array_index++) {
717 FCurve *fcu = fcurve_find_in_action(act, {rna_path.path, array_index});
718
719 if (fcu == nullptr) {
720 continue;
721 }
722
723 if (BKE_fcurve_is_protected(fcu)) {
724 BKE_reportf(reports,
726 "Not deleting keyframe for locked F-Curve '%s' for %s '%s'",
727 fcu->rna_path,
729 id->name + 2);
730 continue;
731 }
732
733 if (fcurve_delete_keyframe_at_time(fcu, cfra)) {
734 modified_fcurves.append(fcu);
735 }
736 }
737 }
738
739 if (!modified_fcurves.is_empty()) {
740 for (FCurve *fcurve : modified_fcurves) {
741 if (BKE_fcurve_is_empty(fcurve)) {
742 animdata_fcurve_delete(adt, fcurve);
743 }
744 }
745 deg_tag_after_keyframe_delete(bmain, id, adt);
746 }
747
748 return modified_fcurves.size();
749}
750
751/* ************************************************** */
752/* KEYFRAME CLEAR */
753
754int clear_keyframe(Main *bmain, ReportList *reports, ID *id, const RNAPath &rna_path)
755{
757
758 if (ELEM(nullptr, id, adt)) {
759 BKE_report(reports, RPT_ERROR, "No ID block and/or AnimData to delete keyframe from");
760 return 0;
761 }
762
764 PropertyRNA *prop;
766 if (RNA_path_resolve_property(&id_ptr, rna_path.path.c_str(), &ptr, &prop) == false) {
768 reports,
769 RPT_ERROR,
770 "Could not clear keyframe, as RNA path is invalid for the given ID (ID = %s, path = %s)",
771 id->name,
772 rna_path.path.c_str());
773 return 0;
774 }
775
776 if (!adt->action) {
777 BKE_reportf(reports, RPT_ERROR, "No action to delete keyframes from for ID = %s", id->name);
778 return 0;
779 }
780 bAction *act = adt->action;
781
782 Action &action = act->wrap();
783 int key_count = 0;
784
785 if (action.is_action_layered()) {
786 if (adt->slot_handle) {
787 Vector<FCurve *> fcurves;
788 foreach_fcurve_in_action_slot(action, adt->slot_handle, [&](FCurve &fcurve) {
789 if (rna_path.index.has_value() && rna_path.index.value() != fcurve.array_index) {
790 return;
791 }
792 if (rna_path.path != fcurve.rna_path) {
793 return;
794 }
795 fcurves.append(&fcurve);
796 });
797
798 for (FCurve *fcu : fcurves) {
799 if (action_fcurve_remove(action, *fcu)) {
800 key_count++;
801 }
802 }
803 }
804 }
805 else {
806 int array_index = rna_path.index.value_or(0);
807 int array_index_max = array_index + 1;
808 if (!rna_path.index.has_value()) {
809 array_index_max = RNA_property_array_length(&ptr, prop);
810
811 /* For single properties, increase max_index so that the property itself gets included,
812 * but don't do this for standard arrays since that can cause corruption issues
813 * (extra unused curves).
814 */
815 if (array_index_max == array_index) {
816 array_index_max++;
817 }
818 }
819 /* Will only loop once unless the array index was -1. */
820 for (; array_index < array_index_max; array_index++) {
821 FCurve *fcu = fcurve_find_in_action(act, {rna_path.path, array_index});
822
823 if (fcu == nullptr) {
824 continue;
825 }
826
827 if (BKE_fcurve_is_protected(fcu)) {
828 BKE_reportf(reports,
830 "Not clearing all keyframes from locked F-Curve '%s' for %s '%s'",
831 fcu->rna_path,
832 BKE_idtype_idcode_to_name(GS(id->name)),
833 id->name + 2);
834 continue;
835 }
836
837 animdata_fcurve_delete(adt, fcu);
838
839 key_count++;
840 }
841 }
842
843 if (key_count) {
844 deg_tag_after_keyframe_delete(bmain, id, adt);
845 }
846
847 return key_count;
848}
849
851 Main *bmain,
852 bAction *action,
854 PropertyRNA *prop,
855 const std::optional<StringRefNull> channel_group,
856 const std::string &rna_path,
857 const float frame,
858 const Span<float> values,
859 eInsertKeyFlags insert_key_flag,
861 const BitSpan keying_mask)
862{
863 BLI_assert(bmain != nullptr);
864 BLI_assert(action != nullptr);
865 BLI_assert(action->wrap().is_action_legacy());
866
867 const char *group;
868 if (channel_group.has_value()) {
869 group = channel_group->c_str();
870 }
871 else {
872 const std::optional<StringRefNull> default_group = default_channel_group_for_path(ptr,
873 rna_path);
874 group = default_group.has_value() ? default_group->c_str() : nullptr;
875 }
876
877 int property_array_index = 0;
878 CombinedKeyingResult combined_result;
879 for (float value : values) {
880 if (!keying_mask[property_array_index]) {
882 property_array_index++;
883 continue;
884 }
885 const SingleKeyingResult keying_result = insert_keyframe_fcurve_value(bmain,
886 ptr,
887 prop,
888 action,
889 group,
890 rna_path.c_str(),
891 property_array_index,
892 frame,
893 value,
894 key_type,
895 insert_key_flag);
896 combined_result.add(keying_result);
897 property_array_index++;
898 }
899 return combined_result;
900}
901
906
908 Main *bmain,
909 Action &action,
910 Layer &layer,
911 const Slot &slot,
912 const std::string &rna_path,
913 PropertyRNA *prop,
914 const std::optional<blender::StringRefNull> channel_group,
915 const KeyInsertData &key_data,
916 const KeyframeSettings &key_settings,
917 const eInsertKeyFlags insert_key_flags)
918{
920 BLI_assert(layer.strips().size() == 1);
921
922 const bool do_cyclic = (insert_key_flags & INSERTKEY_CYCLE_AWARE) && action.is_cyclic();
923
924 const PropertyType prop_type = RNA_property_type(prop);
925 const PropertySubType prop_subtype = RNA_property_subtype(prop);
926
927 Strip *strip = layer.strip(0);
928 return strip->data<StripKeyframeData>(action).keyframe_insert(
929 bmain,
930 slot,
931 {rna_path, key_data.array_index, prop_type, prop_subtype, channel_group},
932 key_data.position,
933 key_settings,
934 insert_key_flags,
935 do_cyclic ? std::optional(action.get_frame_range()) : std::nullopt);
936}
937
938static std::pair<Layer *, Slot *> prep_action_layer_for_keying(Action &action, ID &animated_id)
939{
942 ELEM(get_action(animated_id), &action, nullptr),
943 "The animated ID should not be using another Action than the one passed to this function");
944
945 Slot *slot = assign_action_ensure_slot_for_keying(action, animated_id);
947 slot,
948 "The conditions that would cause this Slot assignment to fail (such as the ID not being "
949 "animatible) should have been caught and handled by higher-level functions.");
950
951 action.layer_keystrip_ensure();
952
953 /* TODO: we currently assume this will always successfully find a layer.
954 * However, that may not be true in the future when we implement features like
955 * layer locking: if layers already exist, but they are all locked, then the
956 * default layer won't be added by the line above, but there also won't be any
957 * layers we can insert keys into. */
958 Layer *layer = action.get_layer_for_keyframing();
959 BLI_assert(layer != nullptr);
960
961 return std::make_pair(layer, slot);
962}
963
965 Main *bmain,
966 Action &action,
967 Layer &layer,
968 const Slot &slot,
969 PropertyRNA *prop,
970 const std::optional<StringRefNull> channel_group,
971 const std::string &rna_path,
972 const float frame,
973 const Span<float> values,
974 const eInsertKeyFlags insert_key_flags,
975 const KeyframeSettings &key_settings,
976 const BitSpan keying_mask)
977{
978 BLI_assert(bmain != nullptr);
980
981 int property_array_index = 0;
982 CombinedKeyingResult combined_result;
983 for (float value : values) {
984 if (!keying_mask[property_array_index]) {
986 property_array_index++;
987 continue;
988 }
989 const KeyInsertData key_data = {{frame, value}, property_array_index};
991 action,
992 layer,
993 slot,
994 rna_path,
995 prop,
996 channel_group,
997 key_data,
998 key_settings,
999 insert_key_flags);
1000
1001 combined_result.add(result);
1002 property_array_index++;
1003 }
1004 return combined_result;
1005}
1006
1008 PointerRNA *struct_pointer,
1009 const std::optional<StringRefNull> channel_group,
1010 const blender::Span<RNAPath> rna_paths,
1011 const std::optional<float> scene_frame,
1012 const AnimationEvalContext &anim_eval_context,
1013 const eBezTriple_KeyframeType key_type,
1014 const eInsertKeyFlags insert_key_flags)
1015
1016{
1017 ID *id = struct_pointer->owner_id;
1018 PointerRNA id_pointer = RNA_id_pointer_create(id);
1019 CombinedKeyingResult combined_result;
1020
1021 /* Init animdata if none available yet. */
1023 if (adt == nullptr) {
1025 return combined_result;
1026 }
1027
1028 if ((adt->action == nullptr) && (insert_key_flags & INSERTKEY_AVAILABLE)) {
1029 combined_result.add(SingleKeyingResult::CANNOT_CREATE_FCURVE, rna_paths.size());
1030 return combined_result;
1031 }
1032
1033 if (const bAction *action = adt->action) {
1034 if (ID_IS_LINKED(action) || ID_IS_OVERRIDE_LIBRARY(action)) {
1035 combined_result.add(SingleKeyingResult::ID_NOT_EDITABLE, rna_paths.size());
1036 return combined_result;
1037 }
1038 }
1039
1040 bAction *dna_action = id_action_ensure(bmain, id);
1041 BLI_assert(dna_action != nullptr);
1042 Action &action = dna_action->wrap();
1043 const bool is_action_legacy = animrig::legacy::action_treat_as_legacy(action);
1044
1046 (insert_key_flags & INSERTKEY_NO_USERPREF) == 0);
1047 key_settings.keyframe_type = key_type;
1048
1049 /* NOTE: keyframing functions can deal with the nla_context being a nullptr. */
1050 ListBase nla_cache = {nullptr, nullptr};
1051 NlaKeyframingContext *nla_context = nullptr;
1052 const float nla_frame = nla_time_remap(scene_frame.value_or(anim_eval_context.eval_time),
1053 &anim_eval_context,
1054 &id_pointer,
1055 adt,
1056 dna_action,
1057 &nla_cache,
1058 &nla_context);
1059 const bool visual_keyframing = insert_key_flags & INSERTKEY_MATRIX;
1060
1061 for (const RNAPath &rna_path : rna_paths) {
1063 PropertyRNA *prop = nullptr;
1064 const bool path_resolved = RNA_path_resolve_property(
1065 struct_pointer, rna_path.path.c_str(), &ptr, &prop);
1066 if (!path_resolved) {
1068 continue;
1069 }
1070
1071 Vector<float> rna_values = get_keyframe_values(&ptr, prop, visual_keyframing);
1072 BitVector<> rna_values_mask(rna_values.size(), false);
1073 bool force_all;
1074
1075 /* NOTE: this function call is complex with interesting/non-obvious effects.
1076 * Please see its documentation for details. */
1078 &ptr,
1079 prop,
1080 rna_values.as_mutable_span(),
1081 rna_path.index.value_or(-1),
1082 &anim_eval_context,
1083 &force_all,
1084 rna_values_mask);
1085
1086 std::optional<std::string> rna_path_id_to_prop = RNA_path_from_ID_to_property(&ptr, prop);
1087 if (!rna_path_id_to_prop.has_value()) {
1088 /* In the case of nested RNA properties the path cannot be reconstructed in all cases. There
1089 * may be a system in place in the future, see #122427. */
1090 if (struct_pointer->data != id) {
1091 continue;
1092 }
1093 /* However if the struct pointer happens to be an ID pointer we can use the path that was
1094 * passed in. This fixes issues like #132195. */
1095 rna_path_id_to_prop = rna_path.path;
1096 }
1097
1098 /* Handle the `force_all` condition mentioned above, ensuring the
1099 * "all-or-nothing" behavior if needed.
1100 *
1101 * TODO: this currently doesn't account for the "Only Insert Available"
1102 * flag, which also needs to be accounted for to actually ensure
1103 * all-or-nothing behavior. This is because the function this part of the
1104 * code originally came from (see #122053) also didn't account for it.
1105 * Presumably that was an oversight, and should be addressed. But for now
1106 * we're faithfully reproducing the original behavior.
1107 */
1108 eInsertKeyFlags insert_key_flags_adjusted = insert_key_flags;
1109 if (force_all && (insert_key_flags & (INSERTKEY_REPLACE | INSERTKEY_AVAILABLE))) {
1110 /* Determine if at least one element would succeed getting keyed. */
1111 bool at_least_one_would_succeed = false;
1112 for (int i = 0; i < rna_values.size(); i++) {
1113 const FCurve *fcu = fcurve_find_in_action(dna_action, {*rna_path_id_to_prop, i});
1114 if (!fcu) {
1115 continue;
1116 }
1117
1118 /* We found an fcurve, and "Only Replace" is not on, so a key insertion
1119 * would succeed according to the two flags we're accounting for. */
1120 if (!(insert_key_flags & INSERTKEY_REPLACE)) {
1121 at_least_one_would_succeed = true;
1122 break;
1123 }
1124
1125 /* "Only Replace" *is* on, so a key insertion would succeed only if we
1126 * actually replace an existing keyframe. */
1127 bool replace;
1128 BKE_fcurve_bezt_binarysearch_index(fcu->bezt, nla_frame, fcu->totvert, &replace);
1129 if (replace) {
1130 at_least_one_would_succeed = true;
1131 break;
1132 }
1133 }
1134
1135 /* If at least one would succeed, then we disable all keying flags that
1136 * would prevent the other elements from getting keyed as well. */
1137 if (at_least_one_would_succeed) {
1138 insert_key_flags_adjusted &= ~(INSERTKEY_REPLACE | INSERTKEY_AVAILABLE);
1139 }
1140 }
1141
1143 if (is_action_legacy) {
1145 dna_action,
1146 struct_pointer,
1147 prop,
1148 channel_group,
1149 *rna_path_id_to_prop,
1150 nla_frame,
1151 rna_values.as_span(),
1152 insert_key_flags_adjusted,
1153 key_type,
1154 rna_values_mask);
1155 }
1156 else {
1157 /* When getting rid of legacy code & WITH_ANIM_BAKLAVA, this line can be
1158 * moved out of the for-loop. */
1159 auto [layer, slot] = prep_action_layer_for_keying(action, *struct_pointer->owner_id);
1160
1161 const std::optional<blender::StringRefNull> this_rna_path_channel_group =
1162 channel_group.has_value() ? *channel_group :
1163 default_channel_group_for_path(&ptr, *rna_path_id_to_prop);
1164
1166 action,
1167 *layer,
1168 *slot,
1169 prop,
1170 this_rna_path_channel_group,
1171 *rna_path_id_to_prop,
1172 nla_frame,
1173 rna_values,
1174 insert_key_flags,
1175 key_settings,
1176 rna_values_mask);
1177 }
1178
1179 combined_result.merge(result);
1180 }
1181
1183
1184 if (combined_result.get_count(SingleKeyingResult::SUCCESS) > 0) {
1185 /* NOTE: this is NOT using ID_RECALC_ANIMATION on purpose, because that would be quite annoying
1186 * in the following case:
1187 *
1188 * - Key Cube's loc/rot/scale.
1189 * - Go to another frame.
1190 * - Translate, rotate, and scale the cube.
1191 * - Hover over the loc/rot/scale properties and one by one press 'I' to
1192 * insert a key there.
1193 *
1194 * If ID_RECALC_ANIMATION were used, keying the location would immediately cause a flush of the
1195 * animation data, popping the rotation and scale back to their animated values. */
1197
1198 /* TODO: it's not entirely clear why the action we got wouldn't be the same
1199 * as the action in AnimData. Further, it's not clear why it would need to
1200 * be tagged for a depsgraph update regardless. This code is here because it
1201 * was part of the function this one was refactored from, but at some point
1202 * this should be investigated and either documented or removed. */
1203 if (!ELEM(adt->action, nullptr, dna_action)) {
1205 }
1206 }
1207
1208 return combined_result;
1209}
1210
1211} // namespace blender::animrig
Functions and classes to work with Actions.
Functionality to iterate an Action in various ways.
Functions for backward compatibility with the legacy Action API.
Functions to work with AnimData.
Functions to modify FCurves.
Functions to insert, delete or modify keyframes.
Helper functions for animation to interact with the RNA system.
Functions to work with the visual keying system.
Blender kernel action and pose functionality.
AnimData * BKE_animdata_ensure_id(ID *id)
Definition anim_data.cc:97
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:83
void BKE_animsys_free_nla_keyframing_context_cache(struct ListBase *cache)
Definition anim_sys.cc:3887
void BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context, struct PointerRNA *prop_ptr, struct PropertyRNA *prop, const blender::MutableSpan< float > values, int index, const struct AnimationEvalContext *anim_eval_context, bool *r_force_all, blender::BitVector<> &r_values_mask)
struct NlaKeyframingContext * BKE_animsys_get_nla_keyframing_context(struct ListBase *cache, struct PointerRNA *ptr, struct AnimData *adt, const struct AnimationEvalContext *anim_eval_context)
bool BKE_fcurve_is_keyframable(const FCurve *fcu)
int BKE_fcurve_bezt_binarysearch_index(const BezTriple array[], float frame, int arraylen, bool *r_replace)
FModifier * add_fmodifier(ListBase *modifiers, int type, FCurve *owner_fcu)
bool BKE_fcurve_is_empty(const FCurve *fcu)
bool BKE_fcurve_is_protected(const FCurve *fcu)
const char * BKE_idtype_idcode_to_name(short idcode)
Definition idtype.cc:164
@ NLATIME_CONVERT_UNMAP
Definition BKE_nla.hh:549
float BKE_nla_tweakedit_remap(AnimData *adt, float cframe, eNlaTime_ConvertModes mode)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
eReportType
Definition BKE_report.hh:33
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
A dynamically sized string ADT.
char * BLI_dynstr_get_cstring(const DynStr *ds) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
DynStr * BLI_dynstr_new(void) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_dynstr.cc:37
void BLI_dynstr_free(DynStr *ds) ATTR_NONNULL()
void BLI_dynstr_appendf(DynStr *__restrict ds, const char *__restrict format,...) ATTR_PRINTF_FORMAT(2
#define ELEM(...)
#define RPT_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
@ ID_RECALC_ANIMATION_NO_FLUSH
Definition DNA_ID.h:1176
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:694
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:730
@ ID_OB
@ ACT_MUTED
eInsertKeyFlags
@ INSERTKEY_CYCLE_AWARE
@ INSERTKEY_REPLACE
@ INSERTKEY_MATRIX
@ INSERTKEY_NEEDED
@ INSERTKEY_NO_USERPREF
@ INSERTKEY_AVAILABLE
@ INSERTKEY_NOFLAGS
@ FMODIFIER_TYPE_CYCLES
@ FCURVE_INT_VALUES
@ FCURVE_DISCRETE_VALUES
struct FCurve FCurve
eBezTriple_KeyframeType
@ MANUALKEY_FLAG_INSERTNEEDED
@ KEYING_FLAG_VISUALKEY
@ KEYING_FLAG_CYCLEAWARE
Read Guarded memory(de)allocation.
#define MEM_reallocN(vmemh, len)
PropertyType
Definition RNA_types.hh:161
PropertySubType
Definition RNA_types.hh:232
#define U
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr int64_t size() const
Definition BLI_span.hh:252
static constexpr int64_t not_found
constexpr int64_t find(char c, int64_t pos=0) const
int64_t size() const
void append(const T &value)
bool is_empty() const
MutableSpan< T > as_mutable_span()
Span< T > as_span() const
float2 get_frame_range() const ATTR_WARN_UNUSED_RESULT
bool is_cyclic() const ATTR_WARN_UNUSED_RESULT
Slot * slot_for_handle(slot_handle_t handle)
void merge(const CombinedKeyingResult &other)
int get_count(const SingleKeyingResult result) const
void generate_reports(ReportList *reports, eReportType report_level=RPT_ERROR)
void add(SingleKeyingResult result, int count=1)
blender::Span< const Strip * > strips() const
const Strip * strip(int64_t index) const
const T & data(const Action &owning_action) const
#define GS(x)
int count
DEG_id_tag_update_ex(cb_data->bmain, cb_data->owner_id, ID_RECALC_TAG_FOR_UNDO|ID_RECALC_SYNC_TO_EVAL)
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static void error(const char *str)
Vector< FCurve * > fcurves_for_assigned_action(AnimData *adt)
bool action_treat_as_legacy(const bAction &action)
void foreach_fcurve_in_action_slot(Action &action, slot_handle_t handle, FunctionRef< void(FCurve &fcurve)> callback)
bool action_fcurve_remove(Action &action, FCurve &fcu)
void assert_baklava_phase_1_invariants(const Action &action)
std::optional< StringRefNull > default_channel_group_for_path(const PointerRNA *animated_struct, StringRef prop_rna_path)
static Vector< float > get_keyframe_values(PointerRNA *ptr, PropertyRNA *prop, const bool visual_key)
void animdata_fcurve_delete(AnimData *adt, FCurve *fcu)
Definition animdata.cc:251
KeyframeSettings get_keyframe_settings(bool from_userprefs)
Slot * assign_action_ensure_slot_for_keying(Action &action, ID &animated_id)
static bool object_frame_has_keyframe(Object *ob, const float frame)
bool fcurve_frame_has_keyframe(const FCurve *fcu, float frame)
bAction * id_action_ensure(Main *bmain, ID *id)
Definition animdata.cc:195
eFCurve_Flags fcurve_flags_for_property_type(PropertyType prop_type)
Vector< float > visualkey_get_values(PointerRNA *ptr, PropertyRNA *prop)
Definition visualkey.cc:197
bool visualkey_can_use(PointerRNA *ptr, PropertyRNA *prop)
Definition visualkey.cc:37
static SingleKeyingResult insert_key_layer(Main *bmain, Action &action, Layer &layer, const Slot &slot, const std::string &rna_path, PropertyRNA *prop, const std::optional< blender::StringRefNull > channel_group, const KeyInsertData &key_data, const KeyframeSettings &key_settings, const eInsertKeyFlags insert_key_flags)
void update_autoflags_fcurve_direct(FCurve *fcu, PropertyType prop_type)
static SingleKeyingResult insert_keyframe_value(FCurve *fcu, float cfra, float curval, eBezTriple_KeyframeType keytype, eInsertKeyFlags flag)
static CombinedKeyingResult insert_key_legacy_action(Main *bmain, bAction *action, PointerRNA *ptr, PropertyRNA *prop, const std::optional< StringRefNull > channel_group, const std::string &rna_path, const float frame, const Span< float > values, eInsertKeyFlags insert_key_flag, eBezTriple_KeyframeType key_type, const BitSpan keying_mask)
Vector< float > get_rna_values(PointerRNA *ptr, PropertyRNA *prop)
Definition anim_rna.cc:25
static void get_keyframe_values_create_reports(ReportList *reports, PointerRNA ptr, PropertyRNA *prop, const int index, const int count, const bool force_all, const BitSpan successful_remaps)
int clear_keyframe(Main *bmain, ReportList *reports, ID *id, const RNAPath &rna_path)
int delete_keyframe(Main *bmain, ReportList *reports, ID *id, const RNAPath &rna_path, float cfra)
Main Delete Key-Framing API call.
bool fcurve_delete_keyframe_at_time(FCurve *fcurve, float time)
bool id_frame_has_keyframe(ID *id, float frame)
static bool assigned_action_has_keyframe_at(AnimData &adt, const float frame)
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.
SingleKeyingResult insert_vert_fcurve(FCurve *fcu, const float2 position, const KeyframeSettings &settings, eInsertKeyFlags flag)
Main Key-framing API call.
Span< FCurve * > fcurves_for_action_slot(Action &action, slot_handle_t slot_handle)
const FCurve * fcurve_find(Span< const FCurve * > fcurves, const FCurveDescriptor &fcurve_descriptor)
static void make_new_fcurve_cyclic(FCurve *fcu, const blender::float2 &action_range)
Action * get_action(ID &animated_id)
FCurve * action_fcurve_ensure_ex(Main *bmain, bAction *act, const char group[], PointerRNA *ptr, const FCurveDescriptor &fcurve_descriptor)
static CombinedKeyingResult insert_key_layered_action(Main *bmain, Action &action, Layer &layer, const Slot &slot, PropertyRNA *prop, const std::optional< StringRefNull > channel_group, const std::string &rna_path, const float frame, const Span< float > values, const eInsertKeyFlags insert_key_flags, const KeyframeSettings &key_settings, const BitSpan keying_mask)
static BitVector nla_map_keyframe_values_and_generate_reports(const MutableSpan< float > values, const int index, PointerRNA &ptr, PropertyRNA &prop, NlaKeyframingContext *nla_context, const AnimationEvalContext *anim_eval_context, ReportList *reports, bool *force_all)
static float nla_time_remap(float time, const AnimationEvalContext *anim_eval_context, PointerRNA *id_ptr, AnimData *adt, bAction *act, ListBase *nla_cache, NlaKeyframingContext **r_nla_context)
bool insert_keyframe_direct(ReportList *reports, PointerRNA ptr, PropertyRNA *prop, FCurve *fcu, const AnimationEvalContext *anim_eval_context, eBezTriple_KeyframeType keytype, NlaKeyframingContext *nla_context, eInsertKeyFlags flag)
Secondary Insert Key-framing API call.
bool is_keying_flag(const Scene *scene, eKeying_Flag flag)
static std::pair< Layer *, Slot * > prep_action_layer_for_keying(Action &action, ID &animated_id)
bool key_insertion_may_create_fcurve(eInsertKeyFlags insert_key_flags)
static SingleKeyingResult insert_keyframe_fcurve_value(Main *bmain, PointerRNA *ptr, PropertyRNA *prop, bAction *act, const char group[], const char rna_path[], int array_index, const float fcurve_frame, float curval, eBezTriple_KeyframeType keytype, eInsertKeyFlags flag)
FCurve * fcurve_find_in_action(bAction *act, const FCurveDescriptor &fcurve_descriptor)
static void deg_tag_after_keyframe_delete(Main *bmain, ID *id, AnimData *adt)
eInsertKeyFlags get_keyframing_flags(Scene *scene)
VecBase< float, 2 > float2
#define floorf
PropertyType RNA_property_type(PropertyRNA *prop)
const char * RNA_property_ui_name(const PropertyRNA *prop, const PointerRNA *ptr)
int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop)
PropertySubType RNA_property_subtype(PropertyRNA *prop)
PointerRNA RNA_id_pointer_create(ID *id)
std::optional< std::string > RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop)
Definition rna_path.cc:1173
bool RNA_path_resolve_property(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop)
Definition rna_path.cc:560
bAction * action
int32_t slot_handle
float vec[3][3]
char * rna_path
BezTriple * bezt
int array_index
unsigned int totvert
ListBase modifiers
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
void * first
struct AnimData * adt
ID * owner_id
Definition RNA_types.hh:51
StructRNA * type
Definition RNA_types.hh:52
void * data
Definition RNA_types.hh:53
std::optional< int > index
Definition RNA_path.hh:66
std::string path
Definition RNA_path.hh:59
struct ToolSettings * toolsettings
float frame_start
eBezTriple_KeyframeType keyframe_type
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4238
uint8_t flag
Definition wm_window.cc:145