Animation: allow marking actions as cyclic for Cycle-Aware Keying.

When a manual frame range is set, allow marking an action as having
Cyclic Animation. This does not affect how the action is evaluated,
but the Cycle-Aware Keying option will automatically make any newly
added F-Curves cyclic. This allows using the option from the start
to build the cycle, rather than only for tweaking an existing loop.

The curves are made cyclic when they have only one key, either
after inserting the first key, or before adding the second one.
The latter case avoids the need to manually make the first added
curve cyclic after marking a newly added action cyclic.

Differential Revision: https://developer.blender.org/D11803
This commit is contained in:
Alexander Gavrilov 2021-07-27 10:39:52 +03:00
parent 5d59b38605
commit 72acce43bc
Notes: blender-bot 2023-02-14 08:08:54 +01:00
Referenced by issue #93310, Compositor: Crash due to broken image paths
Referenced by issue #54724, Workflow improvements for creating Animation Loops
8 changed files with 86 additions and 3 deletions

View File

@ -556,6 +556,8 @@ class DopesheetActionPanelBase:
row.prop(action, "frame_start", text="Start")
row.prop(action, "frame_end", text="End")
col.prop(action, "use_cyclic")
class DOPESHEET_PT_action(DopesheetActionPanelBase, Panel):
bl_space_type = 'DOPESHEET_EDITOR'

View File

@ -98,6 +98,9 @@ void BKE_action_get_frame_range(const struct bAction *act, float *r_start, float
/* Does action have any motion data at all? */
bool action_has_motion(const struct bAction *act);
/* Is the action configured as cyclic. */
bool BKE_action_is_cyclic(const struct bAction *act);
/* Action Groups API ----------------- */
/* Get the active action-group for an Action */

View File

@ -1591,6 +1591,12 @@ void BKE_action_get_frame_range(const struct bAction *act, float *r_start, float
}
}
/* Is the action configured as cyclic. */
bool BKE_action_is_cyclic(const struct bAction *act)
{
return act && (act->flag & ACT_FRAME_RANGE) && (act->flag & ACT_CYCLIC);
}
/* Return flags indicating which transforms the given object/posechannel has
* - if 'curves' is provided, a list of links to these curves are also returned
*/

View File

@ -395,6 +395,11 @@ NlaStrip *BKE_nlastrip_new(bAction *act)
strip->flag &= ~NLASTRIP_FLAG_SYNC_LENGTH;
}
/* Enable cyclic time for known cyclic actions. */
if (BKE_action_is_cyclic(act)) {
strip->flag |= NLASTRIP_FLAG_USR_TIME_CYCLIC;
}
/* assign the action reference */
strip->act = act;
id_us_plus(&act->id);

View File

@ -35,6 +35,7 @@
#include "BLT_translation.h"
#include "DNA_action_types.h"
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_constraint_types.h"
@ -375,6 +376,43 @@ static eFCU_Cycle_Type remap_cyclic_keyframe_location(FCurve *fcu, float *px, fl
return type;
}
/* Used to make curves newly added to a cyclic Action cycle with the correct period. */
static void make_new_fcurve_cyclic(const bAction *act, FCurve *fcu)
{
/* The curve must contain one (newly-added) keyframe. */
if (fcu->totvert != 1 || !fcu->bezt) {
return;
}
const float period = act->frame_end - act->frame_start;
if (period < 0.1f) {
return;
}
/* Move the keyframe into the range. */
const float frame_offset = fcu->bezt[0].vec[1][0] - act->frame_start;
const float fix = floorf(frame_offset / period) * period;
fcu->bezt[0].vec[0][0] -= fix;
fcu->bezt[0].vec[1][0] -= fix;
fcu->bezt[0].vec[2][0] -= fix;
/* Duplicate and offset the keyframe. */
fcu->bezt = MEM_reallocN(fcu->bezt, sizeof(BezTriple) * 2);
fcu->totvert = 2;
fcu->bezt[1] = fcu->bezt[0];
fcu->bezt[1].vec[0][0] += period;
fcu->bezt[1].vec[1][0] += period;
fcu->bezt[1].vec[2][0] += period;
/* Add the cycles modifier. */
if (!fcu->modifiers.first) {
add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES, fcu);
}
}
/* -------------- BezTriple Insertion -------------------- */
/* Change the Y position of a keyframe to match the input, adjusting handles. */
@ -1352,8 +1390,10 @@ static bool insert_keyframe_fcurve_value(Main *bmain,
/* we may not have a F-Curve when we're replacing only... */
if (fcu) {
const bool is_new_curve = (fcu->totvert == 0);
/* set color mode if the F-Curve is new (i.e. without any keyframes) */
if ((fcu->totvert == 0) && (flag & INSERTKEY_XYZ2RGB)) {
if (is_new_curve && (flag & INSERTKEY_XYZ2RGB)) {
/* for Loc/Rot/Scale and also Color F-Curves, the color of the F-Curve in the Graph Editor,
* is determined by the array index for the F-Curve
*/
@ -1366,12 +1406,26 @@ static bool insert_keyframe_fcurve_value(Main *bmain,
}
}
/* If the curve has only one key, make it cyclic if appropriate. */
const bool is_cyclic_action = (flag & INSERTKEY_CYCLE_AWARE) && BKE_action_is_cyclic(act);
if (is_cyclic_action && fcu->totvert == 1) {
make_new_fcurve_cyclic(act, fcu);
}
/* update F-Curve flags to ensure proper behavior for property type */
update_autoflags_fcurve_direct(fcu, prop);
/* insert keyframe */
return insert_keyframe_value(
const bool success = insert_keyframe_value(
reports, ptr, prop, fcu, anim_eval_context, curval, keytype, flag);
/* If the curve is new, make it cyclic if appropriate. */
if (is_cyclic_action && is_new_curve) {
make_new_fcurve_cyclic(act, fcu);
}
return success;
}
return false;

View File

@ -701,6 +701,8 @@ typedef enum eAction_Flags {
/* ACT_DISABLED = (1 << 11), */ /* UNUSED */
/** The action has a manually set intended playback frame range. */
ACT_FRAME_RANGE = (1 << 12),
/** The action is intended to be a cycle (requires ACT_FRAME_RANGE). */
ACT_CYCLIC = (1 << 13),
} eAction_Flags;
/* ************************************************ */

View File

@ -893,6 +893,16 @@ static void rna_def_action(BlenderRNA *brna)
"(this range is used by some tools, but does not affect animation evaluation)");
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
prop = RNA_def_property(srna, "use_cyclic", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ACT_CYCLIC);
RNA_def_property_ui_text(
prop,
"Cyclic Animation",
"The action is intended to be used as a cycle looping over its manually set "
"playback frame range (enabling this doesn't automatically make it loop)");
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_TIME);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_float_sdna(prop, NULL, "frame_start");

View File

@ -3425,7 +3425,8 @@ static void rna_def_tool_settings(BlenderRNA *brna)
prop,
"Cycle-Aware Keying",
"For channels with cyclic extrapolation, keyframe insertion is automatically "
"remapped inside the cycle time range, and keeps ends in sync");
"remapped inside the cycle time range, and keeps ends in sync. Curves newly added to "
"actions with a Manual Frame Range and Cyclic Animation are automatically made cyclic");
/* Keyframing */
prop = RNA_def_property(srna, "keyframe_type", PROP_ENUM, PROP_NONE);