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
path: root/source
diff options
context:
space:
mode:
authorAlexander Gavrilov <angavrilov@gmail.com>2018-10-14 13:04:34 +0300
committerAlexander Gavrilov <angavrilov@gmail.com>2018-10-31 11:50:57 +0300
commit7bc84559aa80841591f1ccf5c09f843931d6dabb (patch)
tree867290fec9a706df649baac761620aa864a7c0c2 /source
parent2d37f555594e5487dc0bd3508887cd452891fbb8 (diff)
Add an option to do keyframe insertion in a cycle-aware fashion.
When enabled, inserting keyframes into F-Curves with simple cyclic extrapolation (the same conditions as required for cycle-aware auto handle smoothing to activate) will take the cycle into account: - Keyframes that are being inserted outside of the cycle bounds are remapped to be inside the cycle. Thus it is not necessary to be within the main iteration of the cycle when tweaking. This becomes especially useful in the final animation tweaking phase when the channel keys may be staggered for overlap, so the actual master period is different for different channels. - Modifying one of the end points of a cycle also changes the other end point when appropriate, to preserve smooth transition. This feature applies to both manual keyframe insertion using 'I', and auto-keyframing. Differential Revision: https://developer.blender.org/D3140
Diffstat (limited to 'source')
-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
7 files changed, 129 insertions, 28 deletions
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");