Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/startup/bl_ui/space_time.py2
-rw-r--r--source/blender/blenkernel/BKE_fcurve.h11
-rw-r--r--source/blender/blenkernel/intern/fcurve.c40
-rw-r--r--source/blender/editors/animation/keyframing.c91
-rw-r--r--source/blender/editors/animation/keyingsets.c7
-rw-r--r--source/blender/makesdna/DNA_anim_types.h1
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h1
-rw-r--r--source/blender/makesrna/intern/rna_scene.c6
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");