diff options
-rw-r--r-- | release/scripts/startup/bl_ui/space_time.py | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_fcurve.h | 11 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/fcurve.c | 40 | ||||
-rw-r--r-- | source/blender/editors/animation/keyframing.c | 91 | ||||
-rw-r--r-- | source/blender/editors/animation/keyingsets.c | 7 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_anim_types.h | 1 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_userdef_types.h | 1 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_scene.c | 6 |
8 files changed, 131 insertions, 28 deletions
diff --git a/release/scripts/startup/bl_ui/space_time.py b/release/scripts/startup/bl_ui/space_time.py index 10cd2f6d47b..79c0c5264e1 100644 --- a/release/scripts/startup/bl_ui/space_time.py +++ b/release/scripts/startup/bl_ui/space_time.py @@ -298,6 +298,8 @@ class TIME_PT_keyframing_settings(TimelinePanelButtons, Panel): if not userprefs.edit.use_keyframe_insert_available: col.prop(toolsettings, "use_record_with_nla", text="Layered Recording") + layout.prop(toolsettings, "use_keyframe_cycle_aware") + ################################### diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index 3ae7ebf5d80..ae87009b4a1 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -273,6 +273,17 @@ bool BKE_fcurve_is_protected(struct FCurve *fcu); /* The curve is an infinite cycle via Cycles modifier */ bool BKE_fcurve_is_cyclic(struct FCurve *fcu); +/* Type of infinite cycle for a curve. */ +typedef enum eFCU_Cycle_Type { + FCU_CYCLE_NONE = 0, + /* The cycle repeats identically to the base range. */ + FCU_CYCLE_PERFECT, + /* The cycle accumulates the change between start and end keys. */ + FCU_CYCLE_OFFSET +} eFCU_Cycle_Type; + +eFCU_Cycle_Type BKE_fcurve_get_cycle_type(struct FCurve *fcu); + /* -------- Curve Sanity -------- */ void calchandles_fcurve(struct FCurve *fcu); diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index e5a04d13aab..abb288b0910 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -886,25 +886,43 @@ void fcurve_store_samples(FCurve *fcu, void *data, int start, int end, FcuSample * that the handles are correctly */ -/* Checks if the F-Curve has a Cycles modifier with simple settings that warrant transition smoothing */ -bool BKE_fcurve_is_cyclic(FCurve *fcu) +/* Checks if the F-Curve has a Cycles modifier, and returns the type of the cycle behavior. */ +eFCU_Cycle_Type BKE_fcurve_get_cycle_type(FCurve *fcu) { FModifier *fcm = fcu->modifiers.first; - if (!fcm || fcm->type != FMODIFIER_TYPE_CYCLES) - return false; + if (!fcm || fcm->type != FMODIFIER_TYPE_CYCLES) { + return FCU_CYCLE_NONE; + } - if (fcm->flag & (FMODIFIER_FLAG_DISABLED | FMODIFIER_FLAG_MUTED)) - return false; + if (fcm->flag & (FMODIFIER_FLAG_DISABLED | FMODIFIER_FLAG_MUTED)) { + return FCU_CYCLE_NONE; + } - if (fcm->flag & (FMODIFIER_FLAG_RANGERESTRICT | FMODIFIER_FLAG_USEINFLUENCE)) - return false; + if (fcm->flag & (FMODIFIER_FLAG_RANGERESTRICT | FMODIFIER_FLAG_USEINFLUENCE)) { + return FCU_CYCLE_NONE; + } FMod_Cycles *data = (FMod_Cycles *)fcm->data; - return data && data->after_cycles == 0 && data->before_cycles == 0 && - ELEM(data->before_mode, FCM_EXTRAPOLATE_CYCLIC, FCM_EXTRAPOLATE_CYCLIC_OFFSET) && - ELEM(data->after_mode, FCM_EXTRAPOLATE_CYCLIC, FCM_EXTRAPOLATE_CYCLIC_OFFSET); + if (data && data->after_cycles == 0 && data->before_cycles == 0) { + if (data->before_mode == FCM_EXTRAPOLATE_CYCLIC && data->after_mode == FCM_EXTRAPOLATE_CYCLIC) { + return FCU_CYCLE_PERFECT; + } + + if (ELEM(data->before_mode, FCM_EXTRAPOLATE_CYCLIC, FCM_EXTRAPOLATE_CYCLIC_OFFSET) && + ELEM(data->after_mode, FCM_EXTRAPOLATE_CYCLIC, FCM_EXTRAPOLATE_CYCLIC_OFFSET)) { + return FCU_CYCLE_OFFSET; + } + } + + return FCU_CYCLE_NONE; +} + +/* Checks if the F-Curve has a Cycles modifier with simple settings that warrant transition smoothing */ +bool BKE_fcurve_is_cyclic(FCurve *fcu) +{ + return BKE_fcurve_get_cycle_type(fcu) != FCU_CYCLE_NONE; } /* Shifts 'in' by the difference in coordinates between 'to' and 'from', using 'out' as the output buffer. diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 4199213d219..fee69b01c48 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -116,6 +116,10 @@ short ANIM_get_keyframing_flags(Scene *scene, short incl_mode) /* keyframing mode - only replace existing keyframes */ if (IS_AUTOKEY_MODE(scene, EDITKEYS)) flag |= INSERTKEY_REPLACE; + + /* cycle-aware keyframe insertion - preserve cycle period and flow */ + if (IS_AUTOKEY_FLAG(scene, CYCLEAWARE)) + flag |= INSERTKEY_CYCLE_AWARE; } return flag; @@ -303,8 +307,67 @@ void update_autoflags_fcurve(FCurve *fcu, bContext *C, ReportList *reports, Poin /* ************************************************** */ /* KEYFRAME INSERTION */ +/* Move the point where a key is about to be inserted to be inside the main cycle range. + * Returns the type of the cycle if it is enabled and valid. + */ +static eFCU_Cycle_Type remap_cyclic_keyframe_location(FCurve *fcu, float *px, float *py) +{ + if (fcu->totvert < 2 || !fcu->bezt) { + return FCU_CYCLE_NONE; + } + + eFCU_Cycle_Type type = BKE_fcurve_get_cycle_type(fcu); + + if (type == FCU_CYCLE_NONE) { + return FCU_CYCLE_NONE; + } + + BezTriple *first = &fcu->bezt[0], *last = &fcu->bezt[fcu->totvert-1]; + float start = first->vec[1][0], end = last->vec[1][0]; + + if (start >= end) { + return FCU_CYCLE_NONE; + } + + if (*px < start || *px > end) { + float period = end - start; + float step = floorf((*px - start) / period); + *px -= step * period; + + if (type == FCU_CYCLE_OFFSET) { + /* Nasty check to handle the case when the modes are different better. */ + FMod_Cycles *data = (FMod_Cycles *)((FModifier*)fcu->modifiers.first)->data; + short mode = (step >= 0) ? data->after_mode : data->before_mode; + + if (mode == FCM_EXTRAPOLATE_CYCLIC_OFFSET) { + *py -= step * (last->vec[1][1] - first->vec[1][1]); + } + } + } + + return type; +} + /* -------------- BezTriple Insertion -------------------- */ +/* Change the Y position of a keyframe to match the input, adjusting handles. */ +static void replace_bezt_keyframe_ypos(BezTriple *dst, const BezTriple *bezt) +{ + /* just change the values when replacing, so as to not overwrite handles */ + float dy = bezt->vec[1][1] - dst->vec[1][1]; + + /* just apply delta value change to the handle values */ + dst->vec[0][1] += dy; + dst->vec[1][1] += dy; + dst->vec[2][1] += dy; + + dst->f1 = bezt->f1; + dst->f2 = bezt->f2; + dst->f3 = bezt->f3; + + /* TODO: perform some other operations? */ +} + /* This function adds a given BezTriple to an F-Curve. It will allocate * memory for the array if needed, and will insert the BezTriple into a * suitable place in chronological order. @@ -329,20 +392,14 @@ int insert_bezt_fcurve(FCurve *fcu, const BezTriple *bezt, eInsertKeyFlags flag) fcu->bezt[i] = *bezt; } else { - /* just change the values when replacing, so as to not overwrite handles */ - BezTriple *dst = (fcu->bezt + i); - float dy = bezt->vec[1][1] - dst->vec[1][1]; - - /* just apply delta value change to the handle values */ - dst->vec[0][1] += dy; - dst->vec[1][1] += dy; - dst->vec[2][1] += dy; - - dst->f1 = bezt->f1; - dst->f2 = bezt->f2; - dst->f3 = bezt->f3; + replace_bezt_keyframe_ypos(&fcu->bezt[i], bezt); + } - /* TODO: perform some other operations? */ + if (flag & INSERTKEY_CYCLE_AWARE) { + /* If replacing an end point of a cyclic curve without offset, modify the other end too. */ + if ((i == 0 || i == fcu->totvert - 1) && BKE_fcurve_get_cycle_type(fcu) == FCU_CYCLE_PERFECT) { + replace_bezt_keyframe_ypos(&fcu->bezt[i == 0 ? fcu->totvert - 1 : 0], bezt); + } } } } @@ -980,6 +1037,14 @@ bool insert_keyframe_direct(Depsgraph *depsgraph, ReportList *reports, PointerRN curval = setting_get_rna_value(depsgraph, &ptr, prop, fcu->array_index, false); } + /* adjust coordinates for cycle aware insertion */ + if (flag & INSERTKEY_CYCLE_AWARE) { + if (remap_cyclic_keyframe_location(fcu, &cfra, &curval) != FCU_CYCLE_PERFECT) { + /* inhibit action from insert_vert_fcurve unless it's a perfect cycle */ + flag &= ~INSERTKEY_CYCLE_AWARE; + } + } + /* only insert keyframes where they are needed */ if (flag & INSERTKEY_NEEDED) { short insert_mode; diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c index 1d6ced02332..e97f88afffd 100644 --- a/source/blender/editors/animation/keyingsets.c +++ b/source/blender/editors/animation/keyingsets.c @@ -924,7 +924,8 @@ short ANIM_validate_keyingset(bContext *C, ListBase *dsources, KeyingSet *ks) /* Determine which keying flags apply based on the override flags */ static short keyingset_apply_keying_flags(const short base_flags, const short overrides, const short own_flags) { - short result = 0; + /* Pass through all flags by default (i.e. even not explicitly listed ones). */ + short result = base_flags; /* The logic for whether a keying flag applies is as follows: * - If the flag in question is set in "overrides", that means that the @@ -934,10 +935,8 @@ static short keyingset_apply_keying_flags(const short base_flags, const short ov */ #define APPLY_KEYINGFLAG_OVERRIDE(kflag) \ if (overrides & kflag) { \ + result &= ~kflag; \ result |= (own_flags & kflag); \ - } \ - else { \ - result |= (base_flags & kflag); \ } /* Apply the flags one by one... diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index eed84ac7aac..4fee5fc6d70 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -855,6 +855,7 @@ typedef enum eInsertKeyFlags { * Used by copy/paste code. */ INSERTKEY_OVERWRITE_FULL = (1<<7), INSERTKEY_DRIVER = (1<<8), /* for driver FCurves, use driver's "input" value - for easier corrective driver setup */ + INSERTKEY_CYCLE_AWARE = (1<<9), /* for cyclic FCurves, adjust key timing to preserve the cycle period and flow */ } eInsertKeyFlags; /* ************************************************ */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index ebe27cd3b38..512481a75b5 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -827,6 +827,7 @@ typedef enum eAutokey_Flag { /* toolsettings->autokey_flag */ AUTOKEY_FLAG_ONLYKEYINGSET = (1 << 6), AUTOKEY_FLAG_NOWARNING = (1 << 7), + AUTOKEY_FLAG_CYCLEAWARE = (1 << 8), ANIMRECORD_FLAG_WITHNLA = (1 << 10), } eAutokey_Flag; diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 46f29267929..8e7075ca91a 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -2593,6 +2593,12 @@ static void rna_def_tool_settings(BlenderRNA *brna) "Automatic keyframe insertion using active Keying Set only"); RNA_def_property_ui_icon(prop, ICON_KEYINGSET, 0); + prop = RNA_def_property(srna, "use_keyframe_cycle_aware", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "autokey_flag", AUTOKEY_FLAG_CYCLEAWARE); + RNA_def_property_ui_text(prop, "Cycle-Aware Keying", + "For channels with cyclic extrapolation, keyframe insertion is automatically " + "remapped inside the cycle time range, and keeps ends in sync"); + /* Keyframing */ prop = RNA_def_property(srna, "keyframe_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "keyframe_type"); |