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.
This commit is contained in:
parent
9473c61b36
commit
28dc07a153
|
@ -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,
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue