Fix: Follow Curve constraint/parenting doesn't animate with layered actions

Both the Follow Curve constraint and parenting with the follow curve option
require a property on the Curve ID to be animated if the user wants the
follower object to animate along the curve. However, the fcurve for this
was always getting created as a legacy action fcurve, regardless of whether
the action was actually legacy or not. If the action was layered, this
resulted in the follower object not animating along the path.

The underlying issue was that the code that created the fcurve was using
`action_fcurve_ensure()`, which hadn't yet been updated to work correctly with
layered actions, and thus the fcurve was created as if the action was legacy.

This commit fixes that by updating `action_fcurve_ensure()` to work with
Baklava phase-1 layered actions in limited circumstances, and also
ensuring that the follow curve code passes an appropriate ID RNA pointer
(required to find/create an appropriate Slot).

Pull Request: https://projects.blender.org/blender/blender/pulls/124353
This commit is contained in:
Nathan Vegdahl
2024-07-12 10:47:32 +02:00
committed by Nathan Vegdahl
parent b76a1db907
commit cb38406a94
4 changed files with 62 additions and 6 deletions

View File

@@ -766,9 +766,22 @@ Vector<const FCurve *> fcurves_all(const Action &action);
Vector<FCurve *> fcurves_all(Action &action);
/**
* Get (or add relevant data to be able to do so) an F-Curve from the given Action,
* for the given animated data-block. This assumes that all the destinations are valid.
* \param ptr: can be a null pointer.
* Get (or add relevant data to be able to do so) an F-Curve from the given
* Action. This assumes that all the destinations are valid.
*
* NOTE: this function is primarily intended for use with legacy actions, but
* for reasons of expedience it now also works with layered actions under the
* following limited circumstances: `ptr` must be non-null and must have an
* `owner_id` that already uses `act`. Otherwise this function will return
* nullptr for layered actions. See the comments in the implementation for more
* details.
*
* \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.
*
* \param fcurve_descriptor: description of the fcurve to lookup/create. Note
* that this is *not* relative to `ptr` (e.g. if `ptr` is not an ID). It should
* contain the exact data path of the fcurve to be looked up/created.
*/
FCurve *action_fcurve_ensure(Main *bmain,
bAction *act,

View File

@@ -1311,6 +1311,46 @@ FCurve *action_fcurve_ensure(Main *bmain,
if (act == nullptr) {
return nullptr;
}
Action &action = act->wrap();
if (USER_EXPERIMENTAL_TEST(&U, use_animation_baklava) && action.is_action_layered()) {
/* NOTE: for layered actions we require the following:
*
* - `ptr` is non-null.
* - `ptr` has an `owner_id` that already uses `act`.
*
* This isn't for any principled reason, but rather is because adding
* support for layered actions to this function was a fix to make Follow
* Path animation work properly with layered actions (see PR #124353), and
* those are the requirements the Follow Path code conveniently met.
* Moreover those requirements were also already met by the other call sites
* that potentially call this function with layered actions.
*
* Trying to puzzle out what "should" happen when these requirements don't
* hold, or if this is even the best place to handle the layered action
* cases at all, was leading to discussion of larger changes than made sense
* to tackle at that point. */
BLI_assert(ptr != nullptr);
if (ptr == nullptr) {
return nullptr;
}
AnimData *adt = BKE_animdata_from_id(ptr->owner_id);
BLI_assert(adt != nullptr && adt->action == act);
if (adt == nullptr || adt->action != act) {
return nullptr;
}
/* Ensure the id has an assigned slot. */
Slot &slot = action.slot_ensure_for_id(*ptr->owner_id);
action.assign_id(&slot, *ptr->owner_id);
action.layer_ensure_at_least_one();
assert_baklava_phase_1_invariants(action);
KeyframeStrip &strip = action.layer(0)->strip(0)->as<KeyframeStrip>();
return &strip.fcurve_find_or_create(slot, fcurve_descriptor);
}
/* Try to find f-curve matching for this setting.
* - add if not found and allowed to add one

View File

@@ -1074,7 +1074,8 @@ static int followpath_path_animate_exec(bContext *C, wmOperator *op)
{
/* create F-Curve for path animation */
act = animrig::id_action_ensure(bmain, &cu->id);
fcu = animrig::action_fcurve_ensure(bmain, act, nullptr, nullptr, {"eval_time", 0});
PointerRNA id_ptr = RNA_id_pointer_create(&cu->id);
fcu = animrig::action_fcurve_ensure(bmain, act, nullptr, &id_ptr, {"eval_time", 0});
/* standard vertical range - 1:1 = 100 frames */
standardRange = 100.0f;
@@ -1098,7 +1099,8 @@ static int followpath_path_animate_exec(bContext *C, wmOperator *op)
/* create F-Curve for constraint */
act = animrig::id_action_ensure(bmain, &ob->id);
fcu = animrig::action_fcurve_ensure(bmain, act, nullptr, nullptr, {path->c_str(), 0});
PointerRNA id_ptr = RNA_id_pointer_create(&ob->id);
fcu = animrig::action_fcurve_ensure(bmain, act, nullptr, &id_ptr, {path->c_str(), 0});
/* standard vertical range - 0.0 to 1.0 */
standardRange = 1.0f;

View File

@@ -544,8 +544,9 @@ bool parent_set(ReportList *reports,
if (partype == PAR_FOLLOW) {
/* get or create F-Curve */
bAction *act = animrig::id_action_ensure(bmain, &cu->id);
PointerRNA id_ptr = RNA_id_pointer_create(&cu->id);
FCurve *fcu = animrig::action_fcurve_ensure(
bmain, act, nullptr, nullptr, {"eval_time", 0});
bmain, act, nullptr, &id_ptr, {"eval_time", 0});
/* setup dummy 'generator' modifier here to get 1-1 correspondence still working */
if (!fcu->bezt && !fcu->fpt && !fcu->modifiers.first) {