From f7a492d460543fd42386cb0c941d247ea902f290 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Wed, 22 Sep 2021 14:02:19 +0100 Subject: Animation: Pose Slide Operator - Blend to Neighbour MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a new operator to the pose slider tools that blends the current pose with the neighbouring poses in the timeline. The operator can be called in pose mode with Shift+Alt+E or from the "pose" menu under "In betweens/Blend to Neighbour" Reviewed by: Sybren A. Stüvel Differential Revision: https://developer.blender.org/D9137#inline-105214 Ref: D9137 --- .../keyconfig/keymap_data/blender_default.py | 1 + release/scripts/startup/bl_ui/space_view3d.py | 2 + source/blender/editors/armature/armature_intern.h | 1 + source/blender/editors/armature/armature_ops.c | 1 + source/blender/editors/armature/pose_slide.c | 102 ++++++++++++++++++++- 5 files changed, 105 insertions(+), 2 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 15d6d44d240..1b0da23aa4a 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -4265,6 +4265,7 @@ def km_pose(params): ("pose.push", {"type": 'E', "value": 'PRESS', "ctrl": True}, None), ("pose.relax", {"type": 'E', "value": 'PRESS', "alt": True}, None), ("pose.breakdown", {"type": 'E', "value": 'PRESS', "shift": True}, None), + ("pose.blend_to_neighbour", {"type": 'E', "value": 'PRESS', "shift": True, "alt": True}, None), op_menu("VIEW3D_MT_pose_propagate", {"type": 'P', "value": 'PRESS', "alt": True}), *( (("object.hide_collection", diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index a332295715c..3879f7de250 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -3453,6 +3453,7 @@ class VIEW3D_MT_pose_slide(Menu): layout.operator("pose.push") layout.operator("pose.relax") layout.operator("pose.breakdown") + layout.operator("pose.blend_to_neighbour") class VIEW3D_MT_pose_propagate(Menu): @@ -3605,6 +3606,7 @@ class VIEW3D_MT_pose_context_menu(Menu): layout.operator("pose.push") layout.operator("pose.relax") layout.operator("pose.breakdown") + layout.operator("pose.blend_to_neighbour") layout.separator() diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index f9950d27e97..696355324e6 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -216,6 +216,7 @@ void POSE_OT_relax(struct wmOperatorType *ot); void POSE_OT_push_rest(struct wmOperatorType *ot); void POSE_OT_relax_rest(struct wmOperatorType *ot); void POSE_OT_breakdown(struct wmOperatorType *ot); +void POSE_OT_blend_to_neighbours(struct wmOperatorType *ot); void POSE_OT_propagate(struct wmOperatorType *ot); diff --git a/source/blender/editors/armature/armature_ops.c b/source/blender/editors/armature/armature_ops.c index fbd89106de5..a1070a8823a 100644 --- a/source/blender/editors/armature/armature_ops.c +++ b/source/blender/editors/armature/armature_ops.c @@ -150,6 +150,7 @@ void ED_operatortypes_armature(void) WM_operatortype_append(POSE_OT_push_rest); WM_operatortype_append(POSE_OT_relax_rest); WM_operatortype_append(POSE_OT_breakdown); + WM_operatortype_append(POSE_OT_blend_to_neighbours); } void ED_operatormacros_armature(void) diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index f23376867af..b273d3aac76 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -117,6 +117,7 @@ typedef enum ePoseSlide_Modes { POSESLIDE_BREAKDOWN, POSESLIDE_PUSH_REST, POSESLIDE_RELAX_REST, + POSESLIDE_BLEND, } ePoseSlide_Modes; /** Transforms/Channels to Affect. */ @@ -423,6 +424,25 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, flo (*val) = ((sVal * w2) + (eVal * w1)); break; } + case POSESLIDE_BLEND: /* Blend the current pose with the previous (<50%) or next key (>50%). */ + { + /* FCurve value on current frame. */ + const float cVal = evaluate_fcurve(fcu, cframe); + const float factor = ED_slider_factor_get(pso->slider); + /* Convert factor to absolute 0-1 range. */ + const float blend_factor = fabs((factor - 0.5f) * 2); + + if (factor < 0.5) { + /* Blend to previous key. */ + (*val) = (cVal * (1 - blend_factor)) + (sVal * blend_factor); + } + else { + /* Blend to next key. */ + (*val) = (cVal * (1 - blend_factor)) + (eVal * blend_factor); + } + + break; + } /* Those are handled in pose_slide_rest_pose_apply. */ case POSESLIDE_PUSH_REST: case POSESLIDE_RELAX_REST: { @@ -614,8 +634,7 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) interp_qt_qtqt(quat_final, quat_prev, quat_next, ED_slider_factor_get(pso->slider)); } - else { - /* POSESLIDE_PUSH and POSESLIDE_RELAX. */ + else if (pso->mode == POSESLIDE_PUSH || pso->mode == POSESLIDE_RELAX) { float quat_breakdown[4]; float quat_curr[4]; @@ -638,6 +657,32 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) interp_qt_qtqt(quat_final, quat_curr, quat_breakdown, ED_slider_factor_get(pso->slider)); } } + else if (pso->mode == POSESLIDE_BLEND) { + float quat_blend[4]; + float quat_curr[4]; + + copy_qt_qt(quat_curr, pchan->quat); + + if (ED_slider_factor_get(pso->slider) < 0.5) { + quat_blend[0] = evaluate_fcurve(fcu_w, prevFrameF); + quat_blend[1] = evaluate_fcurve(fcu_x, prevFrameF); + quat_blend[2] = evaluate_fcurve(fcu_y, prevFrameF); + quat_blend[3] = evaluate_fcurve(fcu_z, prevFrameF); + } + else { + quat_blend[0] = evaluate_fcurve(fcu_w, nextFrameF); + quat_blend[1] = evaluate_fcurve(fcu_x, nextFrameF); + quat_blend[2] = evaluate_fcurve(fcu_y, nextFrameF); + quat_blend[3] = evaluate_fcurve(fcu_z, nextFrameF); + } + + normalize_qt(quat_blend); + normalize_qt(quat_curr); + + const float blend_factor = fabs((ED_slider_factor_get(pso->slider) - 0.5f) * 2); + + interp_qt_qtqt(quat_final, quat_curr, quat_blend, blend_factor); + } /* Apply final to the pose bone, keeping compatible for similar keyframe positions. */ quat_to_compatible_quat(pchan->quat, quat_final, pchan->quat); @@ -868,6 +913,9 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso) case POSESLIDE_BREAKDOWN: strcpy(mode_str, TIP_("Breakdown")); break; + case POSESLIDE_BLEND: + strcpy(mode_str, TIP_("Blend To Neighbour")); + break; default: /* Unknown. */ @@ -1660,6 +1708,56 @@ void POSE_OT_breakdown(wmOperatorType *ot) pose_slide_opdef_properties(ot); } +/* ........................ */ +static int pose_slide_blend_to_neighbours_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + /* Initialize data. */ + if (pose_slide_init(C, op, POSESLIDE_BLEND) == 0) { + pose_slide_exit(C, op); + return OPERATOR_CANCELLED; + } + + /* Do common setup work. */ + return pose_slide_invoke_common(C, op, event); +} + +static int pose_slide_blend_to_neighbours_exec(bContext *C, wmOperator *op) +{ + tPoseSlideOp *pso; + + /* Initialize data (from RNA-props). */ + if (pose_slide_init(C, op, POSESLIDE_BLEND) == 0) { + pose_slide_exit(C, op); + return OPERATOR_CANCELLED; + } + + pso = op->customdata; + + /* Do common exec work. */ + return pose_slide_exec_common(C, op, pso); +} + +void POSE_OT_blend_to_neighbours(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Blend To Neighbour"; + ot->idname = "POSE_OT_blend_to_neighbour"; + ot->description = "Blend from current position to previous or next keyframe"; + + /* Callbacks. */ + ot->exec = pose_slide_blend_to_neighbours_exec; + ot->invoke = pose_slide_blend_to_neighbours_invoke; + ot->modal = pose_slide_modal; + ot->cancel = pose_slide_cancel; + ot->poll = ED_operator_posemode; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X; + + /* Properties. */ + pose_slide_opdef_properties(ot); +} + /* **************************************************** */ /* B) Pose Propagate */ -- cgit v1.2.3