Pose Library: allow negative blend factors to flip the pose

Interactive blending of poses can now use negative factors (i.e. drag to
the left) to blend in the flipped pose.

This reverts rB6eb1c98b15bb7ea6cf184b08617f24c7afb91b6c where the F key
was used to flip the pose. That was already described as stop-gap
measure, and the new behaviour is more intuitive & direct.

Functionality over-the-shoulder reviewed by @hjalti.
This commit is contained in:
Sybren A. Stüvel 2023-01-03 12:31:05 +01:00
parent 88748e5cbc
commit c82311bb8e
Notes: blender-bot 2023-06-21 19:23:24 +02:00
Referenced by issue #103435, Pose Blend to support flipping poses
1 changed files with 67 additions and 69 deletions

View File

@ -66,13 +66,14 @@ typedef struct PoseBlendData {
/* For temp-loading the Action from the pose library. */
AssetTempIDConsumer *temp_id_consumer;
/* Blend factor, interval [0, 1] for interpolating between current and given pose. */
/* Blend factor, interval [-1, 1] for interpolating between current and given pose.
* Positive factors will blend in `act`, whereas negative factors will blend in `act_flipped`. */
float blend_factor;
struct PoseBackup *pose_backup;
Object *ob; /* Object to work on. */
bAction *act; /* Pose to blend into the current pose. */
bool free_action;
Object *ob; /* Object to work on. */
bAction *act; /* Pose to blend into the current pose. */
bAction *act_flipped; /* Flipped copy of `act`. */
Scene *scene; /* For auto-keying. */
ScrArea *area; /* For drawing status text. */
@ -83,12 +84,19 @@ typedef struct PoseBlendData {
char headerstr[UI_MAX_DRAW_STR];
} PoseBlendData;
static void poselib_blend_flip_pose(bContext *C, wmOperator *op);
/** Return the bAction that should be blended.
* This is either pbd->act or pbd->act_flipped, depending on the sign of the blend factor.
*/
static bAction *poselib_action_to_blend(PoseBlendData *pbd)
{
return (pbd->blend_factor >= 0) ? pbd->act : pbd->act_flipped;
}
/* Makes a copy of the current pose for restoration purposes - doesn't do constraints currently */
static void poselib_backup_posecopy(PoseBlendData *pbd)
{
pbd->pose_backup = BKE_pose_backup_create_selected_bones(pbd->ob, pbd->act);
const bAction *action = poselib_action_to_blend(pbd);
pbd->pose_backup = BKE_pose_backup_create_selected_bones(pbd->ob, action);
if (pbd->state == POSE_BLEND_INIT) {
/* Ready for blending now. */
@ -168,19 +176,32 @@ static void poselib_blend_apply(bContext *C, wmOperator *op)
/* Perform the actual blending. */
struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, 0.0f);
BKE_pose_apply_action_blend(pbd->ob, pbd->act, &anim_eval_context, pbd->blend_factor);
bAction *to_blend = poselib_action_to_blend(pbd);
BKE_pose_apply_action_blend(pbd->ob, to_blend, &anim_eval_context, fabs(pbd->blend_factor));
}
/* ---------------------------- */
static void poselib_blend_set_factor(PoseBlendData *pbd, const float new_factor)
{
pbd->blend_factor = CLAMPIS(new_factor, 0.0f, 1.0f);
const bool sign_changed = signf(new_factor) != signf(pbd->blend_factor);
if (sign_changed) {
/* The zero point was crossed, meaning that the pose will be flipped. This means the pose
* backup has to change, as it only contains the bones for one side. */
BKE_pose_backup_restore(pbd->pose_backup);
BKE_pose_backup_free(pbd->pose_backup);
}
pbd->blend_factor = CLAMPIS(new_factor, -1.0f, 1.0f);
pbd->needs_redraw = true;
if (sign_changed) {
poselib_backup_posecopy(pbd);
}
}
/* Return operator return value. */
static int poselib_blend_handle_event(bContext *C, wmOperator *op, const wmEvent *event)
static int poselib_blend_handle_event(bContext *UNUSED(C), wmOperator *op, const wmEvent *event)
{
PoseBlendData *pbd = op->customdata;
@ -226,10 +247,6 @@ static int poselib_blend_handle_event(bContext *C, wmOperator *op, const wmEvent
pbd->state = pbd->state == POSE_BLEND_BLENDING ? POSE_BLEND_ORIGINAL : POSE_BLEND_BLENDING;
pbd->needs_redraw = true;
break;
case EVT_FKEY:
poselib_blend_flip_pose(C, op);
break;
}
return OPERATOR_RUNNING_MODAL;
@ -280,30 +297,6 @@ static bAction *flip_pose(bContext *C, Object *ob, bAction *action)
return action_copy;
}
/* Flip the target pose the interactive blend operator is currently using. */
static void poselib_blend_flip_pose(bContext *C, wmOperator *op)
{
PoseBlendData *pbd = op->customdata;
bAction *old_action = pbd->act;
bAction *new_action = flip_pose(C, pbd->ob, old_action);
/* Before flipping over to the other side, this side needs to be restored. */
BKE_pose_backup_restore(pbd->pose_backup);
BKE_pose_backup_free(pbd->pose_backup);
pbd->pose_backup = NULL;
if (pbd->free_action) {
BKE_id_free(NULL, old_action);
}
pbd->free_action = true;
pbd->act = new_action;
pbd->needs_redraw = true;
/* Refresh the pose backup to use the flipped bones. */
poselib_backup_posecopy(pbd);
}
/* Return true on success, false if the context isn't suitable. */
static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent *event)
{
@ -320,18 +313,21 @@ static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent *
PoseBlendData *pbd;
op->customdata = pbd = MEM_callocN(sizeof(PoseBlendData), "PoseLib Preview Data");
bAction *action = poselib_blend_init_get_action(C, op);
if (action == NULL) {
pbd->act = poselib_blend_init_get_action(C, op);
if (pbd->act == NULL) {
return false;
}
/* Maybe flip the Action. */
/* Passing `flipped=True` is the same as flipping the sign of the blend factor. */
const bool apply_flipped = RNA_boolean_get(op->ptr, "flipped");
if (apply_flipped) {
action = flip_pose(C, ob, action);
pbd->free_action = true;
const float multiply_factor = apply_flipped ? -1.0f : 1.0f;
pbd->blend_factor = multiply_factor * RNA_float_get(op->ptr, "blend_factor");
/* Only construct the flipped pose if there is a chance it's actually needed. */
const bool is_interactive = (event != NULL);
if (is_interactive || pbd->blend_factor < 0) {
pbd->act_flipped = flip_pose(C, ob, pbd->act);
}
pbd->act = action;
/* Get the basic data. */
pbd->ob = ob;
@ -342,12 +338,12 @@ static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent *
pbd->state = POSE_BLEND_INIT;
pbd->needs_redraw = true;
pbd->blend_factor = RNA_float_get(op->ptr, "blend_factor");
/* Just to avoid a clang-analyzer warning (false positive), it's set properly below. */
pbd->release_confirm_info.use_release_confirm = false;
/* Release confirm data. Only available if there's an event to work with. */
if (event != NULL) {
if (is_interactive) {
PropertyRNA *release_confirm_prop = RNA_struct_find_property(op->ptr, "release_confirm");
pbd->release_confirm_info.use_release_confirm = (release_confirm_prop != NULL) &&
RNA_property_boolean_get(op->ptr,
@ -356,10 +352,11 @@ static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent *
ED_slider_init(pbd->slider, event);
ED_slider_factor_set(pbd->slider, pbd->blend_factor);
ED_slider_allow_overshoot_set(pbd->slider, false);
ED_slider_is_bidirectional_set(pbd->slider, true);
}
if (pbd->release_confirm_info.use_release_confirm) {
BLI_assert(event != NULL);
BLI_assert(is_interactive);
pbd->release_confirm_info.init_event_type = WM_userdef_event_type_from_keymap_type(
event->type);
}
@ -397,7 +394,8 @@ static void poselib_blend_cleanup(bContext *C, wmOperator *op)
poselib_keytag_pose(C, scene, pbd);
/* Ensure the redo panel has the actually-used value, instead of the initial value. */
RNA_float_set(op->ptr, "blend_factor", pbd->blend_factor);
RNA_float_set(op->ptr, "blend_factor", fabs(pbd->blend_factor));
RNA_boolean_set(op->ptr, "flipped", pbd->blend_factor < 0);
break;
}
@ -426,10 +424,8 @@ static void poselib_blend_free(wmOperator *op)
return;
}
if (pbd->free_action) {
/* Run before #poselib_tempload_exit to avoid any problems from indirectly
* referenced ID pointers. */
BKE_id_free(NULL, pbd->act);
if (pbd->act_flipped) {
BKE_id_free(NULL, pbd->act_flipped);
}
poselib_tempload_exit(pbd);
@ -489,11 +485,7 @@ static int poselib_blend_modal(bContext *C, wmOperator *op, const wmEvent *event
strcpy(tab_string, TIP_("[Tab] - Show blended pose"));
}
BLI_snprintf(status_string,
sizeof(status_string),
"[F] - Flip pose | %s | %s",
tab_string,
slider_string);
BLI_snprintf(status_string, sizeof(status_string), "%s | %s", tab_string, slider_string);
ED_workspace_status_text(C, status_string);
poselib_blend_apply(C, op);
@ -574,17 +566,19 @@ void POSELIB_OT_apply_pose_asset(wmOperatorType *ot)
RNA_def_float_factor(ot->srna,
"blend_factor",
1.0f,
0.0f,
-1.0f,
1.0f,
"Blend Factor",
"Amount that the pose is applied on top of the existing poses",
0.0f,
"Amount that the pose is applied on top of the existing poses. A negative "
"value will apply the pose flipped over the X-axis",
-1.0f,
1.0f);
RNA_def_boolean(ot->srna,
"flipped",
false,
"Apply Flipped",
"When enabled, applies the pose flipped over the X-axis");
"When enabled, applies the pose flipped over the X-axis. This is the same as "
"passing a negative `blend_factor`");
}
void POSELIB_OT_blend_pose_asset(wmOperatorType *ot)
@ -610,22 +604,26 @@ void POSELIB_OT_blend_pose_asset(wmOperatorType *ot)
prop = RNA_def_float_factor(ot->srna,
"blend_factor",
0.0f,
0.0f,
-1.0f,
1.0f,
"Blend Factor",
"Amount that the pose is applied on top of the existing poses",
0.0f,
"Amount that the pose is applied on top of the existing poses. A "
"negative value will apply the pose flipped over the X-axis",
-1.0f,
1.0f);
/* Blending should always start at 0%, and not at whatever percentage was last used. This RNA
* property just exists for symmetry with the Apply operator (and thus simplicity of the rest of
* the code, which can assume this property exists). */
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
RNA_def_boolean(ot->srna,
"flipped",
false,
"Apply Flipped",
"When enabled, applies the pose flipped over the X-axis");
prop = RNA_def_boolean(ot->srna,
"flipped",
false,
"Apply Flipped",
"When enabled, applies the pose flipped over the X-axis. This is the "
"same as passing a negative `blend_factor`");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
prop = RNA_def_boolean(ot->srna,
"release_confirm",
false,