diff options
-rw-r--r-- | source/blender/blenkernel/BKE_animsys.h | 6 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_armature.h | 4 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/anim_sys.c | 119 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/armature_pose.cc | 14 |
4 files changed, 143 insertions, 0 deletions
diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h index 030560015a9..07da9d75e59 100644 --- a/source/blender/blenkernel/BKE_animsys.h +++ b/source/blender/blenkernel/BKE_animsys.h @@ -268,6 +268,12 @@ void animsys_evaluate_action(struct PointerRNA *ptr, const struct AnimationEvalContext *anim_eval_context, bool flush_to_original); +/* Evaluate action, and blend the result into the current values (instead of overwriting fully). */ +void animsys_blend_in_action(struct PointerRNA *ptr, + struct bAction *act, + const AnimationEvalContext *anim_eval_context, + float blend_factor); + /* Evaluate Action Group */ void animsys_evaluate_action_group(struct PointerRNA *ptr, struct bAction *act, diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h index a09c3e361b6..1f9e304c7a3 100644 --- a/source/blender/blenkernel/BKE_armature.h +++ b/source/blender/blenkernel/BKE_armature.h @@ -215,6 +215,10 @@ void BKE_pose_apply_action_all_bones(struct Object *ob, struct bAction *action, struct AnimationEvalContext *anim_eval_context); +void BKE_pose_apply_action_blend(struct Object *ob, + struct bAction *action, + struct AnimationEvalContext *anim_eval_context, + float blend_factor); void vec_roll_to_mat3(const float vec[3], const float roll, float r_mat[3][3]); void vec_roll_to_mat3_normalized(const float nor[3], const float roll, float r_mat[3][3]); 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 */ diff --git a/source/blender/blenkernel/intern/armature_pose.cc b/source/blender/blenkernel/intern/armature_pose.cc index 9f4b1a31114..09e1c7d6615 100644 --- a/source/blender/blenkernel/intern/armature_pose.cc +++ b/source/blender/blenkernel/intern/armature_pose.cc @@ -76,6 +76,20 @@ void BKE_pose_apply_action_all_bones(struct Object *ob, animsys_evaluate_action(&pose_owner_ptr, action, anim_eval_context, false); } +void BKE_pose_apply_action_blend(struct Object *ob, + struct bAction *action, + struct AnimationEvalContext *anim_eval_context, + const float blend_factor) +{ + auto evaluate_and_blend = [blend_factor](PointerRNA *ptr, + bAction *act, + const AnimationEvalContext *anim_eval_context) { + animsys_blend_in_action(ptr, act, anim_eval_context, blend_factor); + }; + + pose_apply(ob, action, anim_eval_context, evaluate_and_blend); +} + namespace { void pose_apply(struct Object *ob, struct bAction *action, |