diff options
author | Sybren A. Stüvel <sybren@blender.org> | 2021-07-02 14:47:53 +0300 |
---|---|---|
committer | Sybren A. Stüvel <sybren@blender.org> | 2021-07-15 17:12:36 +0300 |
commit | 28dc07a153dd2c16260fb0c824a4f65ab3a06493 (patch) | |
tree | bb567d48e6f47a8fe8f540ed233830ffda1e99cd /source/blender/blenkernel/intern/anim_sys.c | |
parent | 9473c61b366cb3d55bac873e4a6cebd77595ee0d (diff) |
Animation: add function to blend Action into pose
Add function `BKE_pose_apply_action_blend()`, which blends a given
Action into current pose. The Action is evaluated at a specified frame,
and the result is applied to the armature's pose.
A blend factor can be given to blend between the current pose and the
one in the Action. Quaternions are interpolated with SLERP; it is
assumed that their FCurves are consecutively stored in the Action.
This function will be used in the upcoming new Pose Library.
Diffstat (limited to 'source/blender/blenkernel/intern/anim_sys.c')
-rw-r--r-- | source/blender/blenkernel/intern/anim_sys.c | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 6164d4921b0..2879a995ad6 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -621,6 +621,115 @@ static void animsys_evaluate_fcurves(PointerRNA *ptr, } } +/* This function assumes that the quaternion is fully keyed, and is stored in array index order. */ +static void animsys_quaternion_evaluate_fcurves(PathResolvedRNA quat_rna, + FCurve *first_fcurve, + const AnimationEvalContext *anim_eval_context, + float r_quaternion[4]) +{ + FCurve *quat_curve_fcu = first_fcurve; + for (int prop_index = 0; prop_index < 4; ++prop_index, quat_curve_fcu = quat_curve_fcu->next) { + /* Big fat assumption that the quaternion is fully keyed, and stored in order. */ + BLI_assert(STREQ(quat_curve_fcu->rna_path, first_fcurve->rna_path) && + quat_curve_fcu->array_index == prop_index); + + quat_rna.prop_index = prop_index; + r_quaternion[prop_index] = calculate_fcurve(&quat_rna, quat_curve_fcu, anim_eval_context); + } +} + +/* This function assumes that the quaternion is fully keyed, and is stored in array index order. */ +static void animsys_blend_fcurves_quaternion(PathResolvedRNA *anim_rna, + FCurve *first_fcurve, + const AnimationEvalContext *anim_eval_context, + const float blend_factor) +{ + float current_quat[4]; + RNA_property_float_get_array(&anim_rna->ptr, anim_rna->prop, current_quat); + + float target_quat[4]; + animsys_quaternion_evaluate_fcurves(*anim_rna, first_fcurve, anim_eval_context, target_quat); + + float blended_quat[4]; + interp_qt_qtqt(blended_quat, current_quat, target_quat, blend_factor); + + RNA_property_float_set_array(&anim_rna->ptr, anim_rna->prop, blended_quat); +} + +/* LERP between current value (blend_factor=0.0) and the value from the FCurve (blend_factor=1.0) + */ +static void animsys_blend_in_fcurves(PointerRNA *ptr, + ListBase *fcurves, + const AnimationEvalContext *anim_eval_context, + const float blend_factor) +{ + char *channel_to_skip = NULL; + int num_channels_to_skip = 0; + LISTBASE_FOREACH (FCurve *, fcu, fcurves) { + + if (num_channels_to_skip) { + /* For skipping already-handled rotation channels. Rotation channels are handled per group, + * and not per individual channel. */ + BLI_assert(channel_to_skip != NULL); + if (STREQ(channel_to_skip, fcu->rna_path)) { + /* This is indeed the channel we want to skip. */ + num_channels_to_skip--; + continue; + } + } + + if (!is_fcurve_evaluatable(fcu)) { + continue; + } + + PathResolvedRNA anim_rna; + if (!BKE_animsys_rna_path_resolve(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { + continue; + } + + if (STREQ(RNA_property_identifier(anim_rna.prop), "rotation_quaternion")) { + animsys_blend_fcurves_quaternion(&anim_rna, fcu, anim_eval_context, blend_factor); + + /* Skip the next three channels, because those have already been handled here. */ + MEM_SAFE_FREE(channel_to_skip); + channel_to_skip = BLI_strdup(fcu->rna_path); + num_channels_to_skip = 3; + continue; + } + /* TODO(Sybren): do something similar as above for Euler and Axis/Angle representations. */ + + const float fcurve_value = calculate_fcurve(&anim_rna, fcu, anim_eval_context); + + float current_value; + float value_to_write; + if (BKE_animsys_read_from_rna_path(&anim_rna, ¤t_value)) { + value_to_write = (1 - blend_factor) * current_value + blend_factor * fcurve_value; + + switch (RNA_property_type(anim_rna.prop)) { + case PROP_BOOLEAN: + /* Without this, anything less than 1.0 is converted to 'False' by + * ANIMSYS_FLOAT_AS_BOOL(). This is probably not desirable for blends, where anything + * above a 50% blend should act more like the FCurve than like the current value. */ + case PROP_INT: + case PROP_ENUM: + value_to_write = roundf(value_to_write); + break; + default: + /* All other types are just handled as float, and value_to_write is already correct. */ + break; + } + } + else { + /* Unable to read the current value for blending, so just apply the FCurve value instead. */ + value_to_write = fcurve_value; + } + + BKE_animsys_write_to_rna_path(&anim_rna, value_to_write); + } + + MEM_SAFE_FREE(channel_to_skip); +} + /* ***************************************** */ /* Driver Evaluation */ @@ -769,6 +878,16 @@ void animsys_evaluate_action(PointerRNA *ptr, animsys_evaluate_fcurves(ptr, &act->curves, anim_eval_context, flush_to_original); } +/* Evaluate Action and blend it into the current values of the animated properties. */ +void animsys_blend_in_action(PointerRNA *ptr, + bAction *act, + const AnimationEvalContext *anim_eval_context, + const float blend_factor) +{ + action_idcode_patch_check(ptr->owner_id, act); + animsys_blend_in_fcurves(ptr, &act->curves, anim_eval_context, blend_factor); +} + /* ***************************************** */ /* NLA System - Evaluation */ |