diff --git a/source/blender/animrig/ANIM_action.hh b/source/blender/animrig/ANIM_action.hh index 492442f9a98..96c9117be97 100644 --- a/source/blender/animrig/ANIM_action.hh +++ b/source/blender/animrig/ANIM_action.hh @@ -675,7 +675,8 @@ class KeyframeStrip : public ::KeyframeActionStrip { /** Return the channelbag's index, or -1 if there is none for this slot handle. */ int64_t find_channelbag_index(const ChannelBag &channelbag) const; - SingleKeyingResult keyframe_insert(const Slot &slot, + SingleKeyingResult keyframe_insert(Main *bmain, + const Slot &slot, FCurveDescriptor fcurve_descriptor, float2 time_value, const KeyframeSettings &settings, @@ -713,15 +714,27 @@ class ChannelBag : public ::ActionChannelBag { /** * Find an FCurve matching the fcurve descriptor, or create one if it doesn't * exist. + * + * \param bmain Used to tag the dependency graph(s) for relationship + * rebuilding. This is necessary when adding a new F-Curve, as a + * previously-unanimated depsgraph component may become animated now. Can be + * nullptr, in which case the tagging is skipped and is left as the + * responsibility of the caller. */ - FCurve &fcurve_ensure(FCurveDescriptor fcurve_descriptor); + FCurve &fcurve_ensure(Main *bmain, FCurveDescriptor fcurve_descriptor); /** * Create an F-Curve, but only if it doesn't exist yet in this ChannelBag. * * \return the F-Curve it it was created, or nullptr if it already existed. + * + * \param bmain Used to tag the dependency graph(s) for relationship + * rebuilding. This is necessary when adding a new F-Curve, as a + * previously-unanimated depsgraph component may become animated now. Can be + * nullptr, in which case the tagging is skipped and is left as the + * responsibility of the caller. */ - FCurve *fcurve_create_unique(FCurveDescriptor fcurve_descriptor); + FCurve *fcurve_create_unique(Main *bmain, FCurveDescriptor fcurve_descriptor); /** * Remove an F-Curve from the ChannelBag. @@ -742,9 +755,16 @@ class ChannelBag : public ::ActionChannelBag { /** * Create an F-Curve. * - * Assumes that there is no such F-Curve yet on this ChannelBag. + * Assumes that there is no such F-Curve yet on this ChannelBag. If it is + * uncertain whether this is the case, use `fcurve_create_unique()` instead. + * + * \param bmain Used to tag the dependency graph(s) for relationship + * rebuilding. This is necessary when adding a new F-Curve, as a + * previously-unanimated depsgraph component may become animated now. Can be + * nullptr, in which case the tagging is skipped and is left as the + * responsibility of the caller. */ - FCurve &fcurve_create(FCurveDescriptor fcurve_descriptor); + FCurve &fcurve_create(Main *bmain, FCurveDescriptor fcurve_descriptor); }; static_assert(sizeof(ChannelBag) == sizeof(::ActionChannelBag), "DNA struct and its C++ wrapper must have the same size"); @@ -848,6 +868,10 @@ Vector fcurves_all(Action &action); * nullptr for layered actions. See the comments in the implementation for more * details. * + * \note This function also ensures that dependency graph relationships are + * rebuilt. This is necessary when adding a new F-Curve, as a + * previously-unanimated depsgraph component may become animated now. + * * \param ptr: RNA pointer for the struct the fcurve is being looked up/created * for. For legacy actions this is optional and may be null. * diff --git a/source/blender/animrig/intern/action.cc b/source/blender/animrig/intern/action.cc index 3fd277b1388..466385139b0 100644 --- a/source/blender/animrig/intern/action.cc +++ b/source/blender/animrig/intern/action.cc @@ -1124,23 +1124,23 @@ FCurve *ChannelBag::fcurve_find(const FCurveDescriptor fcurve_descriptor) return animrig::fcurve_find(fcurves, fcurve_descriptor); } -FCurve &ChannelBag::fcurve_ensure(const FCurveDescriptor fcurve_descriptor) +FCurve &ChannelBag::fcurve_ensure(Main *bmain, const FCurveDescriptor fcurve_descriptor) { if (FCurve *existing_fcurve = this->fcurve_find(fcurve_descriptor)) { return *existing_fcurve; } - return this->fcurve_create(fcurve_descriptor); + return this->fcurve_create(bmain, fcurve_descriptor); } -FCurve *ChannelBag::fcurve_create_unique(FCurveDescriptor fcurve_descriptor) +FCurve *ChannelBag::fcurve_create_unique(Main *bmain, FCurveDescriptor fcurve_descriptor) { if (this->fcurve_find(fcurve_descriptor)) { return nullptr; } - return &this->fcurve_create(fcurve_descriptor); + return &this->fcurve_create(bmain, fcurve_descriptor); } -FCurve &ChannelBag::fcurve_create(FCurveDescriptor fcurve_descriptor) +FCurve &ChannelBag::fcurve_create(Main *bmain, FCurveDescriptor fcurve_descriptor) { FCurve *new_fcurve = create_fcurve_for_channel(fcurve_descriptor); @@ -1149,6 +1149,11 @@ FCurve &ChannelBag::fcurve_create(FCurveDescriptor fcurve_descriptor) } grow_array_and_append(&this->fcurve_array, &this->fcurve_array_num, new_fcurve); + + if (bmain) { + DEG_relations_tag_update(bmain); + } + return *new_fcurve; } @@ -1167,6 +1172,10 @@ bool ChannelBag::fcurve_remove(FCurve &fcurve_to_remove) dna::array::remove_index( &this->fcurve_array, &this->fcurve_array_num, nullptr, fcurve_index, fcurve_ptr_destructor); + /* As an optimisation, this function could call `DEG_relations_tag_update(bmain)` to prune any + * relationships that are now no longer necessary. This is not needed for correctness of the + * depsgraph evaluation results though. */ + return true; } @@ -1175,7 +1184,8 @@ void ChannelBag::fcurves_clear() dna::array::clear(&this->fcurve_array, &this->fcurve_array_num, nullptr, fcurve_ptr_destructor); } -SingleKeyingResult KeyframeStrip::keyframe_insert(const Slot &slot, +SingleKeyingResult KeyframeStrip::keyframe_insert(Main *bmain, + const Slot &slot, const FCurveDescriptor fcurve_descriptor, const float2 time_value, const KeyframeSettings &settings, @@ -1185,7 +1195,7 @@ SingleKeyingResult KeyframeStrip::keyframe_insert(const Slot &slot, * allow. */ FCurve *fcurve = nullptr; if (key_insertion_may_create_fcurve(insert_key_flags)) { - fcurve = &this->channelbag_for_slot_ensure(slot).fcurve_ensure(fcurve_descriptor); + fcurve = &this->channelbag_for_slot_ensure(slot).fcurve_ensure(bmain, fcurve_descriptor); } else { ChannelBag *channels = this->channelbag_for_slot(slot); @@ -1438,7 +1448,7 @@ FCurve *action_fcurve_ensure(Main *bmain, assert_baklava_phase_1_invariants(action); KeyframeStrip &strip = action.layer(0)->strip(0)->as(); - return &strip.channelbag_for_slot_ensure(slot).fcurve_ensure(fcurve_descriptor); + return &strip.channelbag_for_slot_ensure(slot).fcurve_ensure(bmain, fcurve_descriptor); } /* Try to find f-curve matching for this setting. diff --git a/source/blender/animrig/intern/action_test.cc b/source/blender/animrig/intern/action_test.cc index 20cd22af2ad..f3eacd286c2 100644 --- a/source/blender/animrig/intern/action_test.cc +++ b/source/blender/animrig/intern/action_test.cc @@ -144,9 +144,9 @@ TEST_F(ActionLayersTest, add_strip) /* Add some keys to check that also the strip data is freed correctly. */ const KeyframeSettings settings = get_keyframe_settings(false); Slot &slot = action->slot_add(); - strip.as().keyframe_insert(slot, {"location", 0}, {1.0f, 47.0f}, settings); + strip.as().keyframe_insert(bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings); another_strip.as().keyframe_insert( - slot, {"location", 0}, {1.0f, 47.0f}, settings); + bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings); } TEST_F(ActionLayersTest, remove_strip) @@ -159,9 +159,12 @@ TEST_F(ActionLayersTest, remove_strip) /* Add some keys to check that also the strip data is freed correctly. */ const KeyframeSettings settings = get_keyframe_settings(false); Slot &slot = action->slot_add(); - strip0.as().keyframe_insert(slot, {"location", 0}, {1.0f, 47.0f}, settings); - strip1.as().keyframe_insert(slot, {"location", 0}, {1.0f, 47.0f}, settings); - strip2.as().keyframe_insert(slot, {"location", 0}, {1.0f, 47.0f}, settings); + strip0.as().keyframe_insert( + bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings); + strip1.as().keyframe_insert( + bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings); + strip2.as().keyframe_insert( + bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings); EXPECT_TRUE(layer.strip_remove(strip1)); EXPECT_EQ(2, layer.strips().size()); @@ -555,7 +558,7 @@ TEST_F(ActionLayersTest, KeyframeStrip__keyframe_insert) const KeyframeSettings settings = get_keyframe_settings(false); SingleKeyingResult result_loc_a = key_strip.keyframe_insert( - slot, {"location", 0}, {1.0f, 47.0f}, settings); + bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings); ASSERT_EQ(SingleKeyingResult::SUCCESS, result_loc_a) << "Expected keyframe insertion to be successful"; @@ -566,7 +569,7 @@ TEST_F(ActionLayersTest, KeyframeStrip__keyframe_insert) /* Insert a second key, should insert into the same FCurve as before. */ SingleKeyingResult result_loc_b = key_strip.keyframe_insert( - slot, {"location", 0}, {5.0f, 47.1f}, settings); + bmain, slot, {"location", 0}, {5.0f, 47.1f}, settings); EXPECT_EQ(SingleKeyingResult::SUCCESS, result_loc_b); ASSERT_EQ(1, channels->fcurves().size()) << "Expect insertion with the same (slot/rna " "path/array index) tuple to go into the same FCurve"; @@ -579,7 +582,7 @@ TEST_F(ActionLayersTest, KeyframeStrip__keyframe_insert) /* Insert another key for another property, should create another FCurve. */ SingleKeyingResult result_rot = key_strip.keyframe_insert( - slot, {"rotation_quaternion", 0}, {1.0f, 0.25f}, settings); + bmain, slot, {"rotation_quaternion", 0}, {1.0f, 0.25f}, settings); EXPECT_EQ(SingleKeyingResult::SUCCESS, result_rot); ASSERT_EQ(2, channels->fcurves().size()) << "Expected a second FCurve to be created."; EXPECT_EQ(2, channels->fcurves()[0]->totvert); diff --git a/source/blender/animrig/intern/evaluation_test.cc b/source/blender/animrig/intern/evaluation_test.cc index cd1e5d0ab9b..1eefc8568c6 100644 --- a/source/blender/animrig/intern/evaluation_test.cc +++ b/source/blender/animrig/intern/evaluation_test.cc @@ -149,10 +149,10 @@ TEST_F(AnimationEvaluationTest, evaluate_layer__keyframes) KeyframeStrip &key_strip = strip.as(); /* Set some keys. */ - key_strip.keyframe_insert(*slot, {"location", 0}, {1.0f, 47.1f}, settings); - key_strip.keyframe_insert(*slot, {"location", 0}, {5.0f, 47.5f}, settings); - key_strip.keyframe_insert(*slot, {"rotation_euler", 1}, {1.0f, 0.0f}, settings); - key_strip.keyframe_insert(*slot, {"rotation_euler", 1}, {5.0f, 3.14f}, settings); + key_strip.keyframe_insert(bmain, *slot, {"location", 0}, {1.0f, 47.1f}, settings); + key_strip.keyframe_insert(bmain, *slot, {"location", 0}, {5.0f, 47.5f}, settings); + key_strip.keyframe_insert(bmain, *slot, {"rotation_euler", 1}, {1.0f, 0.0f}, settings); + key_strip.keyframe_insert(bmain, *slot, {"rotation_euler", 1}, {5.0f, 3.14f}, settings); /* Set the animated properties to some values. These should not be overwritten * by the evaluation itself. */ @@ -189,9 +189,9 @@ TEST_F(AnimationEvaluationTest, strip_boundaries__single_strip) /* Set some keys. */ KeyframeStrip &key_strip = strip.as(); - key_strip.keyframe_insert(*slot, {"location", 0}, {1.0f, 47.0f}, settings); - key_strip.keyframe_insert(*slot, {"location", 0}, {5.0f, 327.0f}, settings); - key_strip.keyframe_insert(*slot, {"location", 0}, {10.0f, 48.0f}, settings); + key_strip.keyframe_insert(bmain, *slot, {"location", 0}, {1.0f, 47.0f}, settings); + key_strip.keyframe_insert(bmain, *slot, {"location", 0}, {5.0f, 327.0f}, settings); + key_strip.keyframe_insert(bmain, *slot, {"location", 0}, {10.0f, 48.0f}, settings); /* Evaluate the layer to see how it handles the boundaries + something in between. */ EXPECT_TRUE(test_evaluate_layer("location", 0, {1.0f, 47.0f})); @@ -213,15 +213,15 @@ TEST_F(AnimationEvaluationTest, strip_boundaries__nonoverlapping) /* Set some keys. */ { KeyframeStrip &key_strip1 = strip1.as(); - key_strip1.keyframe_insert(*slot, {"location", 0}, {1.0f, 47.0f}, settings); - key_strip1.keyframe_insert(*slot, {"location", 0}, {5.0f, 327.0f}, settings); - key_strip1.keyframe_insert(*slot, {"location", 0}, {10.0f, 48.0f}, settings); + key_strip1.keyframe_insert(bmain, *slot, {"location", 0}, {1.0f, 47.0f}, settings); + key_strip1.keyframe_insert(bmain, *slot, {"location", 0}, {5.0f, 327.0f}, settings); + key_strip1.keyframe_insert(bmain, *slot, {"location", 0}, {10.0f, 48.0f}, settings); } { KeyframeStrip &key_strip2 = strip2.as(); - key_strip2.keyframe_insert(*slot, {"location", 0}, {1.0f, 47.0f}, settings); - key_strip2.keyframe_insert(*slot, {"location", 0}, {5.0f, 327.0f}, settings); - key_strip2.keyframe_insert(*slot, {"location", 0}, {10.0f, 48.0f}, settings); + key_strip2.keyframe_insert(bmain, *slot, {"location", 0}, {1.0f, 47.0f}, settings); + key_strip2.keyframe_insert(bmain, *slot, {"location", 0}, {5.0f, 327.0f}, settings); + key_strip2.keyframe_insert(bmain, *slot, {"location", 0}, {10.0f, 48.0f}, settings); } /* Check Strip 1. */ @@ -253,15 +253,15 @@ TEST_F(AnimationEvaluationTest, strip_boundaries__overlapping_edge) /* Set some keys. */ { KeyframeStrip &key_strip1 = strip1.as(); - key_strip1.keyframe_insert(*slot, {"location", 0}, {1.0f, 47.0f}, settings); - key_strip1.keyframe_insert(*slot, {"location", 0}, {5.0f, 327.0f}, settings); - key_strip1.keyframe_insert(*slot, {"location", 0}, {10.0f, 48.0f}, settings); + key_strip1.keyframe_insert(bmain, *slot, {"location", 0}, {1.0f, 47.0f}, settings); + key_strip1.keyframe_insert(bmain, *slot, {"location", 0}, {5.0f, 327.0f}, settings); + key_strip1.keyframe_insert(bmain, *slot, {"location", 0}, {10.0f, 48.0f}, settings); } { KeyframeStrip &key_strip2 = strip2.as(); - key_strip2.keyframe_insert(*slot, {"location", 0}, {1.0f, 47.0f}, settings); - key_strip2.keyframe_insert(*slot, {"location", 0}, {5.0f, 327.0f}, settings); - key_strip2.keyframe_insert(*slot, {"location", 0}, {10.0f, 48.0f}, settings); + key_strip2.keyframe_insert(bmain, *slot, {"location", 0}, {1.0f, 47.0f}, settings); + key_strip2.keyframe_insert(bmain, *slot, {"location", 0}, {5.0f, 327.0f}, settings); + key_strip2.keyframe_insert(bmain, *slot, {"location", 0}, {10.0f, 48.0f}, settings); } /* Check Strip 1. */ diff --git a/source/blender/animrig/intern/keyframing.cc b/source/blender/animrig/intern/keyframing.cc index b7adf0b3e02..b984b6557dd 100644 --- a/source/blender/animrig/intern/keyframing.cc +++ b/source/blender/animrig/intern/keyframing.cc @@ -877,7 +877,8 @@ struct KeyInsertData { int array_index; }; -static SingleKeyingResult insert_key_layer(Layer &layer, +static SingleKeyingResult insert_key_layer(Main *bmain, + Layer &layer, const Slot &slot, const std::string &rna_path, const std::optional prop_subtype, @@ -889,14 +890,16 @@ static SingleKeyingResult insert_key_layer(Layer &layer, BLI_assert(layer.strips().size() == 1); Strip *strip = layer.strip(0); - return strip->as().keyframe_insert(slot, + return strip->as().keyframe_insert(bmain, + slot, {rna_path, key_data.array_index, prop_subtype}, key_data.position, key_settings, insert_key_flags); } -static CombinedKeyingResult insert_key_layered_action(Action &action, +static CombinedKeyingResult insert_key_layered_action(Main *bmain, + Action &action, PointerRNA *rna_pointer, const blender::Span rna_paths, const float scene_frame, @@ -955,7 +958,8 @@ static CombinedKeyingResult insert_key_layered_action(Action &action, } const KeyInsertData key_data = {{scene_frame, rna_values[property_index]}, property_index}; - const SingleKeyingResult result = insert_key_layer(*layer, + const SingleKeyingResult result = insert_key_layer(bmain, + *layer, slot, *rna_path_id_to_prop, prop_subtype, @@ -1002,7 +1006,8 @@ CombinedKeyingResult insert_keyframes(Main *bmain, KeyframeSettings key_settings = get_keyframe_settings( (insert_key_flags & INSERTKEY_NO_USERPREF) == 0); key_settings.keyframe_type = key_type; - return insert_key_layered_action(action, + return insert_key_layered_action(bmain, + action, struct_pointer, rna_paths, scene_frame.value_or(anim_eval_context.eval_time), diff --git a/source/blender/editors/animation/anim_filter_test.cc b/source/blender/editors/animation/anim_filter_test.cc index 3d29da5ed3b..4e89098a2a3 100644 --- a/source/blender/editors/animation/anim_filter_test.cc +++ b/source/blender/editors/animation/anim_filter_test.cc @@ -77,13 +77,15 @@ TEST_F(ActionFilterTest, slots_expanded_or_not) /* Create multiple FCurves for multiple Slots. */ const KeyframeSettings settings = get_keyframe_settings(false); ASSERT_EQ(SingleKeyingResult::SUCCESS, - key_strip.keyframe_insert(bind_cube, {"location", 0}, {1.0f, 0.25f}, settings)); + key_strip.keyframe_insert(bmain, bind_cube, {"location", 0}, {1.0f, 0.25f}, settings)); ASSERT_EQ(SingleKeyingResult::SUCCESS, - key_strip.keyframe_insert(bind_cube, {"location", 1}, {1.0f, 0.25f}, settings)); - ASSERT_EQ(SingleKeyingResult::SUCCESS, - key_strip.keyframe_insert(bind_suzanne, {"location", 0}, {1.0f, 0.25f}, settings)); - ASSERT_EQ(SingleKeyingResult::SUCCESS, - key_strip.keyframe_insert(bind_suzanne, {"location", 1}, {1.0f, 0.25f}, settings)); + key_strip.keyframe_insert(bmain, bind_cube, {"location", 1}, {1.0f, 0.25f}, settings)); + ASSERT_EQ( + SingleKeyingResult::SUCCESS, + key_strip.keyframe_insert(bmain, bind_suzanne, {"location", 0}, {1.0f, 0.25f}, settings)); + ASSERT_EQ( + SingleKeyingResult::SUCCESS, + key_strip.keyframe_insert(bmain, bind_suzanne, {"location", 1}, {1.0f, 0.25f}, settings)); ChannelBag *cube_channel_bag = key_strip.channelbag_for_slot(bind_cube); ASSERT_NE(nullptr, cube_channel_bag); @@ -232,9 +234,9 @@ TEST_F(ActionFilterTest, layered_action_active_fcurves) /* Create multiple FCurves. */ const KeyframeSettings settings = get_keyframe_settings(false); ASSERT_EQ(SingleKeyingResult::SUCCESS, - key_strip.keyframe_insert(bind_cube, {"location", 0}, {1.0f, 0.25f}, settings)); + key_strip.keyframe_insert(bmain, bind_cube, {"location", 0}, {1.0f, 0.25f}, settings)); ASSERT_EQ(SingleKeyingResult::SUCCESS, - key_strip.keyframe_insert(bind_cube, {"location", 1}, {1.0f, 0.25f}, settings)); + key_strip.keyframe_insert(bmain, bind_cube, {"location", 1}, {1.0f, 0.25f}, settings)); /* Set one F-Curve as the active one, and the other as inactive. The latter is necessary because * by default the first curve is automatically marked active, but that's too trivial a test case diff --git a/source/blender/editors/animation/anim_ops.cc b/source/blender/editors/animation/anim_ops.cc index 8febfd43ae7..d175e0f2b3a 100644 --- a/source/blender/editors/animation/anim_ops.cc +++ b/source/blender/editors/animation/anim_ops.cc @@ -740,6 +740,7 @@ static int slot_unassign_object_exec(bContext *C, wmOperator * /*op*/) Object *object = CTX_data_active_object(C); animrig::unassign_slot(object->id); + DEG_relations_tag_update(CTX_data_main(C)); WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr); return OPERATOR_FINISHED; } diff --git a/source/blender/makesrna/intern/rna_action.cc b/source/blender/makesrna/intern/rna_action.cc index 07da59b96a9..9126943555b 100644 --- a/source/blender/makesrna/intern/rna_action.cc +++ b/source/blender/makesrna/intern/rna_action.cc @@ -513,7 +513,7 @@ static bool rna_KeyframeActionStrip_key_insert(ID *id, const animrig::KeyframeSettings settings = animrig::get_keyframe_settings(true); const animrig::SingleKeyingResult result = key_strip.keyframe_insert( - slot, {rna_path, array_index}, {time, value}, settings, INSERTKEY_NOFLAGS); + bmain, slot, {rna_path, array_index}, {time, value}, settings, INSERTKEY_NOFLAGS); const bool ok = result == animrig::SingleKeyingResult::SUCCESS; if (ok) { @@ -565,6 +565,7 @@ static int rna_iterator_ChannelBag_fcurves_length(PointerRNA *ptr) } static FCurve *rna_ChannelBag_fcurve_new(ActionChannelBag *dna_channelbag, + Main *bmain, ReportList *reports, const char *data_path, const int index) @@ -576,7 +577,7 @@ static FCurve *rna_ChannelBag_fcurve_new(ActionChannelBag *dna_channelbag, } animrig::ChannelBag &self = dna_channelbag->wrap(); - FCurve *fcurve = self.fcurve_create_unique({data_path, index}); + FCurve *fcurve = self.fcurve_create_unique(bmain, {data_path, index}); if (!fcurve) { BKE_reportf(reports, RPT_ERROR, @@ -1758,7 +1759,7 @@ static void rna_def_channelbag_fcurves(BlenderRNA *brna, PropertyRNA *cprop) func = RNA_def_function(srna, "new", "rna_ChannelBag_fcurve_new"); RNA_def_function_ui_description(func, "Add an F-Curve to the channelbag"); - RNA_def_function_flag(func, FUNC_USE_REPORTS); + RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS); parm = RNA_def_string(func, "data_path", nullptr, 0, "Data Path", "F-Curve data path to use"); RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); RNA_def_int(func, "index", 0, 0, INT_MAX, "Index", "Array index", 0, INT_MAX); diff --git a/source/blender/makesrna/intern/rna_animation.cc b/source/blender/makesrna/intern/rna_animation.cc index c2003096f22..52e3c469a6e 100644 --- a/source/blender/makesrna/intern/rna_animation.cc +++ b/source/blender/makesrna/intern/rna_animation.cc @@ -1714,6 +1714,7 @@ static void rna_def_animdata(BlenderRNA *brna) "Action Slot", "The slot identifies which sub-set of the Action is considered to be for this " "data-block, and its name is used to find the right slot when assigning an Action"); + RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN, "rna_AnimData_dependency_update"); # endif