diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index c435983be19..f23f124b3cb 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -20,12 +20,14 @@ #include "DNA_anim_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_space_types.h" #include "BKE_action.h" #include "BKE_curve.h" #include "BKE_fcurve.h" #include "BKE_main.h" #include "BKE_report.h" +#include "BKE_scene.h" #include "RNA_access.h" #include "RNA_enum_types.h" @@ -1074,7 +1076,7 @@ static void do_curve_mirror_flippping(tAnimCopybufItem *aci, BezTriple *bezt) /* helper for paste_animedit_keys() - performs the actual pasting */ static void paste_animedit_keys_fcurve( - FCurve *fcu, tAnimCopybufItem *aci, float offset, const eKeyMergeMode merge_mode, bool flip) + FCurve *fcu, tAnimCopybufItem *aci, float offset[2], const eKeyMergeMode merge_mode, bool flip) { BezTriple *bezt; int i; @@ -1101,12 +1103,12 @@ static void paste_animedit_keys_fcurve( float f_max; if (merge_mode == KEYFRAME_PASTE_MERGE_OVER_RANGE) { - f_min = aci->bezt[0].vec[1][0] + offset; - f_max = aci->bezt[aci->totvert - 1].vec[1][0] + offset; + f_min = aci->bezt[0].vec[1][0] + offset[0]; + f_max = aci->bezt[aci->totvert - 1].vec[1][0] + offset[0]; } else { /* Entire Range */ - f_min = animcopy_firstframe + offset; - f_max = animcopy_lastframe + offset; + f_min = animcopy_firstframe + offset[0]; + f_max = animcopy_lastframe + offset[0]; } /* remove keys in range */ @@ -1132,9 +1134,9 @@ static void paste_animedit_keys_fcurve( do_curve_mirror_flippping(aci, bezt); } - bezt->vec[0][0] += offset; - bezt->vec[1][0] += offset; - bezt->vec[2][0] += offset; + add_v2_v2(bezt->vec[0], offset); + add_v2_v2(bezt->vec[1], offset); + add_v2_v2(bezt->vec[2], offset); /* insert the keyframe * NOTE: we do not want to inherit handles from existing keyframes in this case! @@ -1143,9 +1145,9 @@ static void paste_animedit_keys_fcurve( insert_bezt_fcurve(fcu, bezt, INSERTKEY_OVERWRITE_FULL); /* un-apply offset from src beztriple after copying */ - bezt->vec[0][0] -= offset; - bezt->vec[1][0] -= offset; - bezt->vec[2][0] -= offset; + sub_v2_v2(bezt->vec[0], offset); + sub_v2_v2(bezt->vec[1], offset); + sub_v2_v2(bezt->vec[2], offset); if (flip) { do_curve_mirror_flippping(aci, bezt); @@ -1172,6 +1174,34 @@ const EnumPropertyItem rna_enum_keyframe_paste_offset_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_keyframe_paste_offset_value[] = { + {KEYFRAME_PASTE_VALUE_OFFSET_LEFT_KEY, + "LEFT_KEY", + 0, + "Left Key", + "Paste keys with the first key matching the key left of the cursor"}, + {KEYFRAME_PASTE_VALUE_OFFSET_RIGHT_KEY, + "RIGHT_KEY", + 0, + "Right Key", + "Paste keys with the last key matching the key right of the cursor"}, + {KEYFRAME_PASTE_VALUE_OFFSET_CFRA, + "CURRENT_FRAME", + 0, + "Current Frame Value", + "Paste keys relative to the value of the curve under the cursor"}, + {KEYFRAME_PASTE_VALUE_OFFSET_CURSOR, + "CURSOR_VALUE", + 0, + "Cursor Value", + "Paste keys relative to the Y-Position of the cursor"}, + {KEYFRAME_PASTE_VALUE_OFFSET_NONE, + "NONE", + 0, + "No Offset", + "Paste keys with the same value as they were copied"}, +}; + const EnumPropertyItem rna_enum_keyframe_paste_merge_items[] = { {KEYFRAME_PASTE_MERGE_MIX, "MIX", 0, "Mix", "Overlay existing with new keys"}, {KEYFRAME_PASTE_MERGE_OVER, "OVER_ALL", 0, "Overwrite All", "Replace all keys"}, @@ -1188,9 +1218,56 @@ const EnumPropertyItem rna_enum_keyframe_paste_merge_items[] = { {0, NULL, 0, NULL, NULL}, }; +static float paste_get_y_offset(bAnimContext *ac, + tAnimCopybufItem *aci, + bAnimListElem *ale, + const eKeyPasteValueOffset value_offset_mode) +{ + FCurve *fcu = (FCurve *)ale->data; + const float cfra = BKE_scene_frame_get(ac->scene); + + switch (value_offset_mode) { + case KEYFRAME_PASTE_VALUE_OFFSET_CURSOR: { + SpaceGraph *sipo = (SpaceGraph *)ac->sl; + const float offset = sipo->cursorVal - aci->bezt[0].vec[1][1]; + return offset; + } + + case KEYFRAME_PASTE_VALUE_OFFSET_CFRA: { + const float cfra_y = evaluate_fcurve(fcu, cfra); + const float offset = cfra_y - aci->bezt[0].vec[1][1]; + return offset; + } + + case KEYFRAME_PASTE_VALUE_OFFSET_LEFT_KEY: { + bool replace; + const int fcu_index = BKE_fcurve_bezt_binarysearch_index( + fcu->bezt, cfra, fcu->totvert, &replace); + BezTriple left_key = fcu->bezt[max_ii(fcu_index - 1, 0)]; + const float offset = left_key.vec[1][1] - aci->bezt[0].vec[1][1]; + return offset; + } + + case KEYFRAME_PASTE_VALUE_OFFSET_RIGHT_KEY: { + bool replace; + const int fcu_index = BKE_fcurve_bezt_binarysearch_index( + fcu->bezt, cfra, fcu->totvert, &replace); + BezTriple right_key = fcu->bezt[min_ii(fcu_index, fcu->totvert - 1)]; + const float offset = right_key.vec[1][1] - aci->bezt[aci->totvert - 1].vec[1][1]; + return offset; + } + + case KEYFRAME_PASTE_VALUE_OFFSET_NONE: + break; + } + + return 0.0f; +} + eKeyPasteError paste_animedit_keys(bAnimContext *ac, ListBase *anim_data, const eKeyPasteOffset offset_mode, + const eKeyPasteValueOffset value_offset_mode, const eKeyMergeMode merge_mode, bool flip) { @@ -1201,7 +1278,7 @@ eKeyPasteError paste_animedit_keys(bAnimContext *ac, const bool from_single = BLI_listbase_is_single(&animcopybuf); const bool to_simple = BLI_listbase_is_single(anim_data); - float offset = 0.0f; + float offset[2]; int pass; /* check if buffer is empty */ @@ -1216,16 +1293,16 @@ eKeyPasteError paste_animedit_keys(bAnimContext *ac, /* methods of offset */ switch (offset_mode) { case KEYFRAME_PASTE_OFFSET_CFRA_START: - offset = (float)(scene->r.cfra - animcopy_firstframe); + offset[0] = (float)(scene->r.cfra - animcopy_firstframe); break; case KEYFRAME_PASTE_OFFSET_CFRA_END: - offset = (float)(scene->r.cfra - animcopy_lastframe); + offset[0] = (float)(scene->r.cfra - animcopy_lastframe); break; case KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE: - offset = (float)(scene->r.cfra - animcopy_cfra); + offset[0] = (float)(scene->r.cfra - animcopy_cfra); break; case KEYFRAME_PASTE_OFFSET_NONE: - offset = 0.0f; + offset[0] = 0.0f; break; } @@ -1238,6 +1315,7 @@ eKeyPasteError paste_animedit_keys(bAnimContext *ac, fcu = (FCurve *)ale->data; /* destination F-Curve */ aci = animcopybuf.first; + offset[1] = paste_get_y_offset(ac, aci, ale, value_offset_mode); paste_animedit_keys_fcurve(fcu, aci, offset, merge_mode, false); ale->update |= ANIM_UPDATE_DEFAULT; } @@ -1282,6 +1360,7 @@ eKeyPasteError paste_animedit_keys(bAnimContext *ac, if (aci) { totmatch++; + offset[1] = paste_get_y_offset(ac, aci, ale, value_offset_mode); if (adt) { ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); paste_animedit_keys_fcurve(fcu, aci, offset, merge_mode, flip); diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h index f027a46b470..5bd672072b1 100644 --- a/source/blender/editors/include/ED_keyframes_edit.h +++ b/source/blender/editors/include/ED_keyframes_edit.h @@ -218,6 +218,19 @@ typedef enum eKeyPasteOffset { KEYFRAME_PASTE_OFFSET_NONE, } eKeyPasteOffset; +typedef enum eKeyPasteValueOffset { + /* Paste keys with the first key matching the key left of the cursor. */ + KEYFRAME_PASTE_VALUE_OFFSET_LEFT_KEY, + /* Paste keys with the last key matching the key right of the cursor. */ + KEYFRAME_PASTE_VALUE_OFFSET_RIGHT_KEY, + /* Paste keys relative to the value of the curve under the cursor. */ + KEYFRAME_PASTE_VALUE_OFFSET_CFRA, + /* Paste values relative to the cursor position. */ + KEYFRAME_PASTE_VALUE_OFFSET_CURSOR, + /* Paste keys with the exact copied value. */ + KEYFRAME_PASTE_VALUE_OFFSET_NONE, +} eKeyPasteValueOffset; + typedef enum eKeyMergeMode { /* overlay existing with new keys */ KEYFRAME_PASTE_MERGE_MIX, @@ -427,6 +440,7 @@ short copy_animedit_keys(struct bAnimContext *ac, ListBase *anim_data); eKeyPasteError paste_animedit_keys(struct bAnimContext *ac, ListBase *anim_data, eKeyPasteOffset offset_mode, + eKeyPasteValueOffset value_offset_mode, eKeyMergeMode merge_mode, bool flip); diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 094eccf4114..d9772844358 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -533,8 +533,9 @@ static eKeyPasteError paste_action_keys(bAnimContext *ac, ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); } - /* paste keyframes */ - const eKeyPasteError ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode, flip); + /* Value offset is always None because the user cannot see the effect of it. */ + const eKeyPasteError ok = paste_animedit_keys( + ac, &anim_data, offset_mode, KEYFRAME_PASTE_VALUE_OFFSET_NONE, merge_mode, flip); /* clean up */ ANIM_animdata_freelist(&anim_data); diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index b1f4673f7eb..73e05bbe1e3 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -475,6 +475,7 @@ static short copy_graph_keys(bAnimContext *ac) static eKeyPasteError paste_graph_keys(bAnimContext *ac, const eKeyPasteOffset offset_mode, + const eKeyPasteValueOffset value_offset_mode, const eKeyMergeMode merge_mode, bool flip) { @@ -495,7 +496,8 @@ static eKeyPasteError paste_graph_keys(bAnimContext *ac, } /* Paste keyframes. */ - const eKeyPasteError ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode, flip); + const eKeyPasteError ok = paste_animedit_keys( + ac, &anim_data, offset_mode, value_offset_mode, merge_mode, flip); /* Clean up. */ ANIM_animdata_freelist(&anim_data); @@ -544,6 +546,7 @@ static int graphkeys_paste_exec(bContext *C, wmOperator *op) bAnimContext ac; const eKeyPasteOffset offset_mode = RNA_enum_get(op->ptr, "offset"); + const eKeyPasteValueOffset value_offset_mode = RNA_enum_get(op->ptr, "value_offset"); const eKeyMergeMode merge_mode = RNA_enum_get(op->ptr, "merge"); const bool flipped = RNA_boolean_get(op->ptr, "flipped"); @@ -555,7 +558,8 @@ static int graphkeys_paste_exec(bContext *C, wmOperator *op) /* Ac.reports by default will be the global reports list, which won't show warnings. */ ac.reports = op->reports; - const eKeyPasteError kf_empty = paste_graph_keys(&ac, offset_mode, merge_mode, flipped); + const eKeyPasteError kf_empty = paste_graph_keys( + &ac, offset_mode, value_offset_mode, merge_mode, flipped); switch (kf_empty) { case KEYFRAME_PASTE_OK: break; @@ -614,8 +618,14 @@ void GRAPH_OT_paste(wmOperatorType *ot) "offset", rna_enum_keyframe_paste_offset_items, KEYFRAME_PASTE_OFFSET_CFRA_START, - "Offset", + "Frame Offset", "Paste time offset of keys"); + RNA_def_enum(ot->srna, + "value_offset", + rna_enum_keyframe_paste_offset_value, + KEYFRAME_PASTE_VALUE_OFFSET_NONE, + "Value Offset", + "Paste keys with a value offset"); RNA_def_enum(ot->srna, "merge", rna_enum_keyframe_paste_merge_items, diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h index bc4d9282af0..510469c2756 100644 --- a/source/blender/makesrna/RNA_enum_items.h +++ b/source/blender/makesrna/RNA_enum_items.h @@ -242,6 +242,7 @@ DEF_ENUM(rna_enum_particle_edit_hair_brush_items) DEF_ENUM(rna_enum_particle_edit_disconnected_hair_brush_items) DEF_ENUM(rna_enum_keyframe_paste_offset_items) +DEF_ENUM(rna_enum_keyframe_paste_offset_value) DEF_ENUM(rna_enum_keyframe_paste_merge_items) DEF_ENUM(rna_enum_transform_pivot_items_full)