diff options
Diffstat (limited to 'source/blender/editors/armature/pose_slide.c')
-rw-r--r-- | source/blender/editors/armature/pose_slide.c | 339 |
1 files changed, 291 insertions, 48 deletions
diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 8e8345d34c9..e5331aff12b 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -39,6 +39,7 @@ #include "DNA_scene_types.h" #include "BKE_fcurve.h" +#include "BKE_nla.h" #include "BKE_context.h" #include "BKE_object.h" @@ -94,12 +95,19 @@ typedef struct tPoseSlideOp { ListBase pfLinks; /* links between posechannels and f-curves */ DLRBT_Tree keys; /* binary tree for quicker searching for keyframes (when applicable) */ - int cframe; /* current frame number */ - int prevFrame; /* frame before current frame (blend-from) */ - int nextFrame; /* frame after current frame (blend-to) */ + int cframe; /* current frame number - global time */ - int mode; /* sliding mode (ePoseSlide_Modes) */ - int flag; /* unused for now, but can later get used for storing runtime settings.... */ + int prevFrame; /* frame before current frame (blend-from) - global time */ + int nextFrame; /* frame after current frame (blend-to) - global time */ + + float prevFrameF; /* prevFrame, but in local action time (for F-Curve lookups to work) */ + float nextFrameF; /* nextFrame, but in local action time (for F-Curve lookups to work) */ + + short mode; /* sliding mode (ePoseSlide_Modes) */ + short flag; /* unused for now, but can later get used for storing runtime settings.... */ + + short channels; /* which transforms/channels are affected (ePoseSlide_Channels) */ + short axislock; /* axis-limits for transforms (ePoseSlide_AxisLock) */ float percentage; /* 0-1 value for determining the influence of whatever is relevant */ @@ -113,6 +121,49 @@ typedef enum ePoseSlide_Modes { POSESLIDE_BREAKDOWN, /* slide between the endpoint poses, finding a 'soft' spot */ } ePoseSlide_Modes; + +/* Transforms/Channels to Affect */ +typedef enum ePoseSlide_Channels { + PS_TFM_ALL = 0, /* All transforms and properties */ + + PS_TFM_LOC, /* Loc/Rot/Scale */ + PS_TFM_ROT, + PS_TFM_SIZE, + + PS_TFM_BBONE_SHAPE, /* Bendy Bones */ + + PS_TFM_PROPS /* Custom Properties */ +} ePoseSlide_Channels; + +/* Property enum for ePoseSlide_Channels */ +static const EnumPropertyItem prop_channels_types[] = { + {PS_TFM_ALL, "ALL", 0, "All Properties", + "All properties, including transforms, bendy bone shape, and custom properties"}, + {PS_TFM_LOC, "LOC", 0, "Location", "Location only"}, + {PS_TFM_ROT, "ROT", 0, "Rotation", "Rotation only"}, + {PS_TFM_SIZE, "SIZE", 0, "Scale", "Scale only"}, + {PS_TFM_BBONE_SHAPE, "BBONE", 0, "Bendy Bone", "Bendy Bone shape properties"}, + {PS_TFM_PROPS, "CUSTOM", 0, "Custom Properties", "Custom properties"}, + {0, NULL, 0, NULL, NULL} +}; + +/* Axis Locks */ +typedef enum ePoseSlide_AxisLock { + PS_LOCK_X = (1 << 0), + PS_LOCK_Y = (1 << 1), + PS_LOCK_Z = (1 << 2) +} ePoseSlide_AxisLock; + +/* Property enum for ePoseSlide_AxisLock */ +static const EnumPropertyItem prop_axis_lock_types[] = { + {0, "FREE", 0, "Free", "All axes are affected"}, + {PS_LOCK_X, "X", 0, "X", "Only X-axis transforms are affected"}, + {PS_LOCK_Y, "Y", 0, "Y", "Only Y-axis transforms are affected"}, + {PS_LOCK_Z, "Z", 0, "Z", "Only Z-axis transforms are affected"}, + /* TODO: Combinations? */ + {0, NULL, 0, NULL, NULL} +}; + /* ------------------------------------ */ /* operator init */ @@ -139,11 +190,19 @@ static int pose_slide_init(bContext *C, wmOperator *op, short mode) pso->prevFrame = RNA_int_get(op->ptr, "prev_frame"); pso->nextFrame = RNA_int_get(op->ptr, "next_frame"); - /* check the settings from the context */ + /* get the set of properties/axes that can be operated on */ + pso->channels = RNA_enum_get(op->ptr, "channels"); + pso->axislock = RNA_enum_get(op->ptr, "axis_lock"); + + /* ensure validity of the settings from the context */ if (ELEM(NULL, pso->ob, pso->arm, pso->ob->adt, pso->ob->adt->action)) return 0; - else - act = pso->ob->adt->action; + + act = pso->ob->adt->action; + + /* apply NLA mapping corrections so the frame lookups work */ + pso->prevFrameF = BKE_nla_tweakedit_remap(pso->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP); + pso->nextFrameF = BKE_nla_tweakedit_remap(pso->ob->adt, pso->nextFrame, NLATIME_CONVERT_UNMAP); /* for each Pose-Channel which gets affected, get the F-Curves for that channel * and set the relevant transform flags... @@ -209,9 +268,9 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, float *val) /* get keyframe values for endpoint poses to blend with */ /* previous/start */ - sVal = evaluate_fcurve(fcu, (float)pso->prevFrame); + sVal = evaluate_fcurve(fcu, pso->prevFrameF); /* next/end */ - eVal = evaluate_fcurve(fcu, (float)pso->nextFrame); + eVal = evaluate_fcurve(fcu, pso->nextFrameF); /* if both values are equal, don't do anything */ if (IS_EQF(sVal, eVal)) { @@ -293,10 +352,20 @@ static void pose_slide_apply_vec3(tPoseSlideOp *pso, tPChanFCurveLink *pfl, floa /* using this path, find each matching F-Curve for the variables we're interested in */ while ( (ld = poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) { FCurve *fcu = (FCurve *)ld->data; - - /* just work on these channels one by one... there's no interaction between values */ + const int idx = fcu->array_index; + const int lock = pso->axislock; + + /* check if this F-Curve is ok given the current axis locks */ BLI_assert(fcu->array_index < 3); - pose_slide_apply_val(pso, fcu, &vec[fcu->array_index]); + + if ((lock == 0) || + ((lock & PS_LOCK_X) && (idx == 0)) || + ((lock & PS_LOCK_Y) && (idx == 1)) || + ((lock & PS_LOCK_Z) && (idx == 2))) + { + /* just work on these channels one by one... there's no interaction between values */ + pose_slide_apply_val(pso, fcu, &vec[fcu->array_index]); + } } /* free the temp path we got */ @@ -423,15 +492,15 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) float quat_prev[4], quat_next[4]; /* get 2 quats */ - quat_prev[0] = evaluate_fcurve(fcu_w, pso->prevFrame); - quat_prev[1] = evaluate_fcurve(fcu_x, pso->prevFrame); - quat_prev[2] = evaluate_fcurve(fcu_y, pso->prevFrame); - quat_prev[3] = evaluate_fcurve(fcu_z, pso->prevFrame); + quat_prev[0] = evaluate_fcurve(fcu_w, pso->prevFrameF); + quat_prev[1] = evaluate_fcurve(fcu_x, pso->prevFrameF); + quat_prev[2] = evaluate_fcurve(fcu_y, pso->prevFrameF); + quat_prev[3] = evaluate_fcurve(fcu_z, pso->prevFrameF); - quat_next[0] = evaluate_fcurve(fcu_w, pso->nextFrame); - quat_next[1] = evaluate_fcurve(fcu_x, pso->nextFrame); - quat_next[2] = evaluate_fcurve(fcu_y, pso->nextFrame); - quat_next[3] = evaluate_fcurve(fcu_z, pso->nextFrame); + quat_next[0] = evaluate_fcurve(fcu_w, pso->nextFrameF); + quat_next[1] = evaluate_fcurve(fcu_x, pso->nextFrameF); + quat_next[2] = evaluate_fcurve(fcu_y, pso->nextFrameF); + quat_next[3] = evaluate_fcurve(fcu_z, pso->nextFrameF); /* perform blending */ if (pso->mode == POSESLIDE_BREAKDOWN) { @@ -483,6 +552,10 @@ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso) /* move out one step either side */ pso->prevFrame--; pso->nextFrame++; + + /* apply NLA mapping corrections so the frame lookups work */ + pso->prevFrameF = BKE_nla_tweakedit_remap(pso->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP); + pso->nextFrameF = BKE_nla_tweakedit_remap(pso->ob->adt, pso->nextFrame, NLATIME_CONVERT_UNMAP); } /* for each link, handle each set of transforms */ @@ -494,17 +567,17 @@ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso) */ bPoseChannel *pchan = pfl->pchan; - if (pchan->flag & POSE_LOC) { + if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_LOC) && (pchan->flag & POSE_LOC)) { /* calculate these for the 'location' vector, and use location curves */ pose_slide_apply_vec3(pso, pfl, pchan->loc, "location"); } - if (pchan->flag & POSE_SIZE) { + if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_SIZE) && (pchan->flag & POSE_SIZE)) { /* calculate these for the 'scale' vector, and use scale curves */ pose_slide_apply_vec3(pso, pfl, pchan->size, "scale"); } - if (pchan->flag & POSE_ROT) { + if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_ROT) && (pchan->flag & POSE_ROT)) { /* everything depends on the rotation mode */ if (pchan->rotmode > 0) { /* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */ @@ -519,12 +592,12 @@ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso) } } - if (pchan->flag & POSE_BBONE_SHAPE) { + if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_BBONE_SHAPE) && (pchan->flag & POSE_BBONE_SHAPE)) { /* bbone properties - they all start a "bbone_" prefix */ pose_slide_apply_props(pso, pfl, "bbone_"); } - if (pfl->oldprops) { + if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_PROPS) && (pfl->oldprops)) { /* not strictly a transform, but custom properties contribute to the pose produced in many rigs * (e.g. the facial rigs used in Sintel) */ @@ -553,9 +626,12 @@ static void pose_slide_reset(tPoseSlideOp *pso) /* ------------------------------------ */ /* draw percentage indicator in header */ +// TODO: Include hints about locks here... static void pose_slide_draw_status(tPoseSlideOp *pso) { char status_str[UI_MAX_DRAW_STR]; + char limits_str[UI_MAX_DRAW_STR]; + char axis_str[50]; char mode_str[32]; switch (pso->mode) { @@ -575,16 +651,58 @@ static void pose_slide_draw_status(tPoseSlideOp *pso) break; } + switch (pso->axislock) { + case PS_LOCK_X: + BLI_strncpy(axis_str, "[X]/Y/Z axis only (X to clear)", sizeof(axis_str)); + break; + case PS_LOCK_Y: + BLI_strncpy(axis_str, "X/[Y]/Z axis only (Y to clear)", sizeof(axis_str)); + break; + case PS_LOCK_Z: + BLI_strncpy(axis_str, "X/Y/[Z] axis only (Z to clear)", sizeof(axis_str)); + break; + + default: + if (ELEM(pso->channels, PS_TFM_LOC, PS_TFM_ROT, PS_TFM_SIZE)) { + BLI_strncpy(axis_str, "X/Y/Z = Axis Constraint", sizeof(axis_str)); + } + else { + axis_str[0] = '\0'; + } + break; + } + + switch (pso->channels) { + case PS_TFM_LOC: + BLI_snprintf(limits_str, sizeof(limits_str), "[G]/R/S/B/C - Location only (G to clear) | %s", axis_str); + break; + case PS_TFM_ROT: + BLI_snprintf(limits_str, sizeof(limits_str), "G/[R]/S/B/C - Rotation only (R to clear) | %s", axis_str); + break; + case PS_TFM_SIZE: + BLI_snprintf(limits_str, sizeof(limits_str), "G/R/[S]/B/C - Scale only (S to clear) | %s", axis_str); + break; + case PS_TFM_BBONE_SHAPE: + BLI_strncpy(limits_str, "G/R/S/[B]/C - Bendy Bone properties only (B to clear) | %s", sizeof(limits_str)); + break; + case PS_TFM_PROPS: + BLI_strncpy(limits_str, "G/R/S/B/[C] - Custom Properties only (C to clear) | %s", sizeof(limits_str)); + break; + default: + BLI_strncpy(limits_str, "G/R/S/B/C - Limit to Transform/Property Set", sizeof(limits_str)); + break; + } + if (hasNumInput(&pso->num)) { Scene *scene = pso->scene; char str_offs[NUM_STR_REP_LEN]; outputNumInput(&pso->num, str_offs, &scene->unit); - BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_offs); + BLI_snprintf(status_str, sizeof(status_str), "%s: %s | %s", mode_str, str_offs, limits_str); } else { - BLI_snprintf(status_str, sizeof(status_str), "%s: %d %%", mode_str, (int)(pso->percentage * 100.0f)); + BLI_snprintf(status_str, sizeof(status_str), "%s: %d %% | %s", mode_str, (int)(pso->percentage * 100.0f), limits_str); } ED_area_headerprint(pso->sa, status_str); @@ -641,6 +759,10 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p pso->nextFrame = (ak->next) ? (ak->next->cfra) : (pso->cframe + 1); RNA_int_set(op->ptr, "next_frame", pso->nextFrame); } + + /* apply NLA mapping corrections so the frame lookups work */ + pso->prevFrameF = BKE_nla_tweakedit_remap(pso->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP); + pso->nextFrameF = BKE_nla_tweakedit_remap(pso->ob->adt, pso->nextFrame, NLATIME_CONVERT_UNMAP); } else { BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between"); @@ -675,11 +797,58 @@ static void pose_slide_mouse_update_percentage(tPoseSlideOp *pso, wmOperator *op RNA_float_set(op->ptr, "percentage", pso->percentage); } +/* handle an event to toggle channels mode */ +static void pose_slide_toggle_channels_mode(wmOperator *op, tPoseSlideOp *pso, ePoseSlide_Channels channel) +{ + /* Turn channel on or off? */ + if (pso->channels == channel) { + /* Already limiting to transform only, so pressing this again turns it off */ + pso->channels = PS_TFM_ALL; + } + else { + /* Only this set of channels */ + pso->channels = channel; + } + RNA_enum_set(op->ptr, "channels", pso->channels); + + + /* Reset axis limits too for good measure */ + pso->axislock = 0; + RNA_enum_set(op->ptr, "axis_lock", pso->axislock); +} + +/* handle an event to toggle axis locks - returns whether any change in state is needed */ +static bool pose_slide_toggle_axis_locks(wmOperator *op, tPoseSlideOp *pso, ePoseSlide_AxisLock axis) +{ + /* Axis can only be set when a transform is set - it doesn't make sense otherwise */ + if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_BBONE_SHAPE, PS_TFM_PROPS)) { + pso->axislock = 0; + RNA_enum_set(op->ptr, "axis_lock", pso->axislock); + return false; + } + + /* Turn on or off? */ + if (pso->axislock == axis) { + /* Already limiting on this axis, so turn off */ + pso->axislock = 0; + } + else { + /* Only this axis */ + pso->axislock = axis; + } + RNA_enum_set(op->ptr, "axis_lock", pso->axislock); + + /* Setting changed, so pose update is needed */ + return true; +} + /* common code for modal() */ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) { tPoseSlideOp *pso = op->customdata; wmWindow *win = CTX_wm_window(C); + bool do_pose_update = false; + const bool has_numinput = hasNumInput(&pso->num); switch (event->type) { @@ -718,7 +887,8 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) /* canceled! */ return OPERATOR_CANCELLED; } - + + /* Percentage Chane... */ case MOUSEMOVE: /* calculate new position */ { /* only handle mousemove if not doing numinput */ @@ -726,18 +896,13 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) /* update percentage based on position of mouse */ pose_slide_mouse_update_percentage(pso, op, event); - /* update percentage indicator in header */ - pose_slide_draw_status(pso); - - /* reset transforms (to avoid accumulation errors) */ - pose_slide_reset(pso); - - /* apply... */ - pose_slide_apply(C, pso); + /* update pose to reflect the new values (see below) */ + do_pose_update = true; } break; } default: + { if ((event->val == KM_PRESS) && handleNumInput(C, &pso->num, event)) { float value; @@ -751,21 +916,94 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) CLAMP(pso->percentage, 0.0f, 1.0f); RNA_float_set(op->ptr, "percentage", pso->percentage); - /* update percentage indicator in header */ - pose_slide_draw_status(pso); - - /* reset transforms (to avoid accumulation errors) */ - pose_slide_reset(pso); - - /* apply... */ - pose_slide_apply(C, pso); + /* Update pose to reflect the new values (see below) */ + do_pose_update = true; break; } + else if (event->val == KM_PRESS) { + switch (event->type) { + /* Transform Channel Limits */ + /* XXX: Replace these hardcoded hotkeys with a modalmap that can be customised */ + case GKEY: /* Location */ + { + pose_slide_toggle_channels_mode(op, pso, PS_TFM_LOC); + do_pose_update = true; + break; + } + case RKEY: /* Rotation */ + { + pose_slide_toggle_channels_mode(op, pso, PS_TFM_ROT); + do_pose_update = true; + break; + } + case SKEY: /* Scale */ + { + pose_slide_toggle_channels_mode(op, pso, PS_TFM_SIZE); + do_pose_update = true; + break; + } + case BKEY: /* Bendy Bones */ + { + pose_slide_toggle_channels_mode(op, pso, PS_TFM_BBONE_SHAPE); + do_pose_update = true; + break; + } + case CKEY: /* Custom Properties */ + { + pose_slide_toggle_channels_mode(op, pso, PS_TFM_PROPS); + do_pose_update = true; + break; + } + + + /* Axis Locks */ + /* XXX: Hardcoded... */ + case XKEY: + { + if (pose_slide_toggle_axis_locks(op, pso, PS_LOCK_X)) { + do_pose_update = true; + } + break; + } + case YKEY: + { + if (pose_slide_toggle_axis_locks(op, pso, PS_LOCK_Y)) { + do_pose_update = true; + } + break; + } + case ZKEY: + { + if (pose_slide_toggle_axis_locks(op, pso, PS_LOCK_Z)) { + do_pose_update = true; + } + break; + } + + + default: /* Some other unhandled key... */ + break; + } + } else { /* unhandled event - maybe it was some view manip? */ /* allow to pass through */ return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH; } + } + } + + + /* perform pose updates - in response to some user action (e.g. pressing a key or moving the mouse) */ + if (do_pose_update) { + /* update percentage indicator in header */ + pose_slide_draw_status(pso); + + /* reset transforms (to avoid accumulation errors) */ + pose_slide_reset(pso); + + /* apply... */ + pose_slide_apply(C, pso); } /* still running... */ @@ -795,11 +1033,16 @@ static int pose_slide_exec_common(bContext *C, wmOperator *op, tPoseSlideOp *pso } /* common code for defining RNA properties */ +/* TODO: Skip save on these? */ static void pose_slide_opdef_properties(wmOperatorType *ot) { + RNA_def_float_percentage(ot->srna, "percentage", 0.5f, 0.0f, 1.0f, "Percentage", "Weighting factor for which keyframe is favored more", 0.3, 0.7); + RNA_def_int(ot->srna, "prev_frame", 0, MINAFRAME, MAXFRAME, "Previous Keyframe", "Frame number of keyframe immediately before the current frame", 0, 50); RNA_def_int(ot->srna, "next_frame", 0, MINAFRAME, MAXFRAME, "Next Keyframe", "Frame number of keyframe immediately after the current frame", 0, 50); - RNA_def_float_percentage(ot->srna, "percentage", 0.5f, 0.0f, 1.0f, "Percentage", "Weighting factor for the sliding operation", 0.3, 0.7); + + RNA_def_enum(ot->srna, "channels", prop_channels_types, PS_TFM_ALL, "Channels", "Set of properties that are affected"); + RNA_def_enum(ot->srna, "axis_lock", prop_axis_lock_types, 0, "Axis Lock", "Transform axis to restrict effects to"); } /* ------------------------------------ */ @@ -1238,7 +1481,7 @@ static void pose_propagate_fcurve(wmOperator *op, Object *ob, FCurve *fcu, /* stop on matching marker if there is one */ for (ce = modeData.sel_markers.first; ce; ce = ce->next) { - if (ce->cfra == iroundf(bezt->vec[1][0])) + if (ce->cfra == round_fl_to_int(bezt->vec[1][0])) break; } @@ -1332,7 +1575,7 @@ static int pose_propagate_exec(bContext *C, wmOperator *op) void POSE_OT_propagate(wmOperatorType *ot) { - static EnumPropertyItem terminate_items[] = { + static const EnumPropertyItem terminate_items[] = { {POSE_PROPAGATE_SMART_HOLDS, "WHILE_HELD", 0, "While Held", "Propagate pose to all keyframes after current frame that don't change (Default behavior)"}, {POSE_PROPAGATE_NEXT_KEY, "NEXT_KEY", 0, "To Next Keyframe", |