NLA: insert keyframes correctly for strips with non-Replace mode.

NLA strips support using the keyframe values in a variety of ways:
adding, subtracting, multiplying, linearly mixing with the result
of strips located below in the stack. This is intended for layering
tweaks on top of a base animation.

However, when inserting keyframes into such strips, it simply inserts
the final value of the property, irrespective of these settings. This
in fact makes the feature nearly useless.

To fix this it is necessary to evaluate the NLA stack below the
edited strip and correctly compute the raw key that would produce
the intended final value, according to the mode and influence.

Differential Revision: https://developer.blender.org/D3927
This commit is contained in:
Alexander Gavrilov 2018-11-12 19:41:53 +03:00
parent fc74903f27
commit de662e7cd3
Notes: blender-bot 2023-02-14 11:24:03 +01:00
Referenced by issue #69105, NLA - Action baking broken
13 changed files with 309 additions and 61 deletions

View File

@ -48,6 +48,7 @@ struct Scene;
struct bAction;
struct bActionGroup;
struct bContext;
struct NlaKeyframingContext;
/* ************************************* */
/* AnimData API */
@ -171,6 +172,14 @@ void BKE_fcurves_main_cb(struct Main *bmain, ID_FCurve_Edit_Callback func, void
/* ************************************* */
// TODO: overrides, remapping, and path-finding api's
/* ------------ NLA Keyframing --------------- */
typedef struct NlaKeyframingContext NlaKeyframingContext;
struct NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(struct ListBase *cache, struct Depsgraph *depsgraph, struct PointerRNA *ptr, struct AnimData *adt, float ctime);
bool BKE_animsys_nla_remap_keyframe_value(struct NlaKeyframingContext *context, struct PointerRNA *prop_ptr, struct PropertyRNA *prop, int index, float *r_value);
void BKE_animsys_free_nla_keyframing_context_cache(struct ListBase *cache);
/* ************************************* */
/* Evaluation API */

View File

@ -2120,6 +2120,8 @@ static NlaEvalChannel *nlaevalchan_verify(PointerRNA *ptr, ListBase *channels, F
return nec;
}
static float nla_blend_value(int blendmode, float old_value, float value, float inf);
/* accumulate (i.e. blend) the given value on to the channel it affects */
static void nlaevalchan_accumulate(NlaEvalChannel *nec, NlaEvalStrip *nes, float value, bool newChan)
{
@ -2140,28 +2142,33 @@ static void nlaevalchan_accumulate(NlaEvalChannel *nec, NlaEvalStrip *nes, float
if (nes->strip_mode == NES_TIME_TRANSITION_END)
inf *= nes->strip_time;
nec->value = nla_blend_value(blendmode, nec->value, value, inf);
}
/* accumulate the old and new values of a channel according to mode and influence */
static float nla_blend_value(int blendmode, float old_value, float value, float inf)
{
/* optimisation: no need to try applying if there is no influence */
if (IS_EQF(inf, 0.0f)) return;
if (IS_EQF(inf, 0.0f)) {
return old_value;
}
/* perform blending */
switch (blendmode) {
case NLASTRIP_MODE_ADD:
/* simply add the scaled value on to the stack */
nec->value += (value * inf);
break;
return old_value + (value * inf);
case NLASTRIP_MODE_SUBTRACT:
/* simply subtract the scaled value from the stack */
nec->value -= (value * inf);
break;
return old_value - (value * inf);
case NLASTRIP_MODE_MULTIPLY:
/* multiply the scaled value with the stack */
/* Formula Used:
* result = fac * (a * b) + (1 - fac) * a
*/
nec->value = inf * (nec->value * value) + (1 - inf) * nec->value;
break;
return inf * (old_value * value) + (1 - inf) * old_value;
case NLASTRIP_MODE_REPLACE:
default: /* TODO: do we really want to blend by default? it seems more uses might prefer add... */
@ -2169,8 +2176,41 @@ static void nlaevalchan_accumulate(NlaEvalChannel *nec, NlaEvalStrip *nes, float
* - the influence of the accumulated data (elsewhere, that is called dstweight)
* is 1 - influence, since the strip's influence is srcweight
*/
nec->value = nec->value * (1.0f - inf) + (value * inf);
break;
return old_value * (1.0f - inf) + (value * inf);
}
}
/* compute the value that would blend to the desired target value using nla_blend_value */
static bool nla_invert_blend_value(int blend_mode, float old_value, float target_value, float influence, float *r_value)
{
switch (blend_mode) {
case NLASTRIP_MODE_ADD:
*r_value = (target_value - old_value) / influence;
return true;
case NLASTRIP_MODE_SUBTRACT:
*r_value = (old_value - target_value) / influence;
return true;
case NLASTRIP_MODE_MULTIPLY:
if (old_value == 0.0f) {
/* Resolve 0/0 to 1. */
if (target_value == 0.0f) {
*r_value = 1.0f;
return true;
}
/* Division by zero. */
return false;
}
else {
*r_value = (target_value - old_value) / influence / old_value + 1.0f;
return true;
}
case NLASTRIP_MODE_REPLACE:
default:
*r_value = (target_value - old_value) / influence + old_value;
return true;
}
}
@ -2481,11 +2521,10 @@ void nladata_flush_channels(Depsgraph *depsgraph, PointerRNA *ptr, ListBase *cha
/**
* NLA Evaluation function - values are calculated and stored in temporary "NlaEvalChannels"
*
* \note This is exported so that keyframing code can use this for make use of it for anim layers support
*
* \param[out] echannels Evaluation channels with calculated values
* \param[out] r_context If not NULL, data about the currently edited strip is stored here and excluded from value calculation.
*/
static void animsys_evaluate_nla(Depsgraph *depsgraph, ListBase *echannels, PointerRNA *ptr, AnimData *adt, float ctime)
static void animsys_evaluate_nla(Depsgraph *depsgraph, ListBase *echannels, PointerRNA *ptr, AnimData *adt, float ctime, NlaKeyframingContext *r_context)
{
NlaTrack *nlt;
short track_index = 0;
@ -2493,9 +2532,12 @@ static void animsys_evaluate_nla(Depsgraph *depsgraph, ListBase *echannels, Poin
ListBase estrips = {NULL, NULL};
NlaEvalStrip *nes;
NlaStrip dummy_strip_buf;
NlaStrip dummy_strip = {NULL}; /* dummy strip for active action */
/* dummy strip for active action */
NlaStrip *dummy_strip = r_context ? &r_context->strip : &dummy_strip_buf;
memset(dummy_strip, 0, sizeof(*dummy_strip));
/* 1. get the stack of strips to evaluate at current time (influence calculated here) */
for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next, track_index++) {
@ -2538,39 +2580,69 @@ static void animsys_evaluate_nla(Depsgraph *depsgraph, ListBase *echannels, Poin
/* make dummy NLA strip, and add that to the stack */
ListBase dummy_trackslist;
dummy_trackslist.first = dummy_trackslist.last = &dummy_strip;
dummy_trackslist.first = dummy_trackslist.last = dummy_strip;
if ((nlt) && !(adt->flag & ADT_NLA_EDIT_NOMAP)) {
/* Strips with a user-defined time curve don't get properly remapped for editing
* at the moment, so mapping them just for display may be confusing. */
bool is_inplace_tweak = (nlt) && !(adt->flag & ADT_NLA_EDIT_NOMAP) && !(adt->actstrip->flag & NLASTRIP_FLAG_USR_TIME);
if (is_inplace_tweak) {
/* edit active action in-place according to its active strip, so copy the data */
memcpy(&dummy_strip, adt->actstrip, sizeof(NlaStrip));
dummy_strip.next = dummy_strip.prev = NULL;
memcpy(dummy_strip, adt->actstrip, sizeof(NlaStrip));
dummy_strip->next = dummy_strip->prev = NULL;
}
else {
/* set settings of dummy NLA strip from AnimData settings */
dummy_strip.act = adt->action;
dummy_strip->act = adt->action;
/* action range is calculated taking F-Modifiers into account (which making new strips doesn't do due to the troublesome nature of that) */
calc_action_range(dummy_strip.act, &dummy_strip.actstart, &dummy_strip.actend, 1);
dummy_strip.start = dummy_strip.actstart;
dummy_strip.end = (IS_EQF(dummy_strip.actstart, dummy_strip.actend)) ? (dummy_strip.actstart + 1.0f) : (dummy_strip.actend);
calc_action_range(dummy_strip->act, &dummy_strip->actstart, &dummy_strip->actend, 1);
dummy_strip->start = dummy_strip->actstart;
dummy_strip->end = (IS_EQF(dummy_strip->actstart, dummy_strip->actend)) ? (dummy_strip->actstart + 1.0f) : (dummy_strip->actend);
dummy_strip.blendmode = adt->act_blendmode;
dummy_strip.extendmode = adt->act_extendmode;
dummy_strip.influence = adt->act_influence;
/* Always use the blend mode of the strip in tweak mode, even if not in-place. */
if (nlt && adt->actstrip) {
dummy_strip->blendmode = adt->actstrip->blendmode;
dummy_strip->extendmode = adt->actstrip->extendmode;
}
else {
dummy_strip->blendmode = adt->act_blendmode;
dummy_strip->extendmode = adt->act_extendmode;
}
dummy_strip->influence = adt->act_influence;
/* NOTE: must set this, or else the default setting overrides, and this setting doesn't work */
dummy_strip.flag |= NLASTRIP_FLAG_USR_INFLUENCE;
dummy_strip->flag |= NLASTRIP_FLAG_USR_INFLUENCE;
}
/* add this to our list of evaluation strips */
nlastrips_ctime_get_strip(depsgraph, &estrips, &dummy_trackslist, -1, ctime);
if (r_context == NULL) {
nlastrips_ctime_get_strip(depsgraph, &estrips, &dummy_trackslist, -1, ctime);
}
/* If computing the context for keyframing, store data there instead of the list. */
else {
/* The extend mode here effectively controls whether it is possible to keyframe beyond the ends. */
dummy_strip->extendmode = is_inplace_tweak ? NLASTRIP_EXTEND_NOTHING : NLASTRIP_EXTEND_HOLD;
r_context->eval_strip = nes = nlastrips_ctime_get_strip(depsgraph, NULL, &dummy_trackslist, -1, ctime);
/* These setting combinations require no data from strips below, so exit immediately. */
if ((nes == NULL) || (dummy_strip->blendmode == NLASTRIP_MODE_REPLACE && dummy_strip->influence == 1.0f)) {
BLI_freelistN(&estrips);
return;
}
}
}
else {
/* special case - evaluate as if there isn't any NLA data */
/* TODO: this is really just a stop-gap measure... */
if (G.debug & G_DEBUG) printf("NLA Eval: Stopgap for active action on NLA Stack - no strips case\n");
animsys_evaluate_action(depsgraph, ptr, adt->action, ctime);
if (r_context == NULL) {
animsys_evaluate_action(depsgraph, ptr, adt->action, ctime);
}
BLI_freelistN(&estrips);
return;
}
@ -2601,7 +2673,7 @@ static void animsys_calculate_nla(Depsgraph *depsgraph, PointerRNA *ptr, AnimDat
* and also when the user jumps between different times instead of moving sequentially... */
/* evaluate the NLA stack, obtaining a set of values to flush */
animsys_evaluate_nla(depsgraph, &echannels, ptr, adt, ctime);
animsys_evaluate_nla(depsgraph, &echannels, ptr, adt, ctime, NULL);
/* flush effects of accumulating channels in NLA to the actual data they affect */
nladata_flush_channels(depsgraph, ptr, &echannels);
@ -2610,6 +2682,108 @@ static void animsys_calculate_nla(Depsgraph *depsgraph, PointerRNA *ptr, AnimDat
BLI_freelistN(&echannels);
}
/* ---------------------- */
/**
* Prepare data necessary to compute correct keyframe values for NLA strips
* with non-Replace mode or influence different from 1.
*
* @param cache List used to cache contexts for reuse when keying multiple channels in one operation.
* @param ptr RNA pointer to the Object with the animation.
* @return Keyframing context, or NULL if not necessary.
*/
NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(
struct ListBase *cache, struct Depsgraph *depsgraph, struct PointerRNA *ptr, struct AnimData *adt, float ctime)
{
/* No remapping needed if NLA is off or no action. */
if ((adt == NULL) || (adt->action == NULL) || (adt->nla_tracks.first == NULL) || (adt->flag & ADT_NLA_EVAL_OFF)) {
return NULL;
}
/* No remapping if editing an ordinary Replace action with full influence. */
if (!(adt->flag & ADT_NLA_EDIT_ON) && (adt->act_blendmode == NLASTRIP_MODE_REPLACE && adt->act_influence == 1.0f)) {
return NULL;
}
/* Try to find a cached context. */
NlaKeyframingContext *ctx = BLI_findptr(cache, adt, offsetof(NlaKeyframingContext, adt));
if (ctx == NULL) {
/* Allocate and evaluate a new context. */
ctx = MEM_callocN(sizeof(*ctx), "NlaKeyframingContext");
ctx->adt = adt;
animsys_evaluate_nla(depsgraph, &ctx->nla_channels, ptr, adt, ctime, ctx);
BLI_assert(ELEM(ctx->strip.act, NULL, adt->action));
BLI_addtail(cache, ctx);
}
return ctx;
}
/**
* Apply correction from the NLA context to the value about to be keyframed.
*
* @param context Context to use (may be NULL).
* @param prop_ptr Property about to be keyframed.
* @param index Array index within the property.
* @param[in,out] r_value Value to correct.
* @return False if correction fails due to a division by zero.
*/
bool BKE_animsys_nla_remap_keyframe_value(struct NlaKeyframingContext *context, struct PointerRNA *prop_ptr, struct PropertyRNA *prop, int index, float *r_value)
{
/* No context means no correction. */
if (context == NULL || context->strip.act == NULL) {
return true;
}
/* If the strip is not evaluated, it is the same as zero influence. */
if (context->eval_strip == NULL) {
return false;
}
/* Full influence Replace strips also require no correction. */
int blend_mode = context->strip.blendmode;
float influence = context->strip.influence;
if (blend_mode == NLASTRIP_MODE_REPLACE && influence == 1.0f) {
return true;
}
/* Zero influence is division by zero. */
if (influence <= 0.0f) {
return false;
}
/* Find the evaluation channel for the NLA stack below current strip. */
PathResolvedRNA rna = { .ptr = *prop_ptr, .prop = prop, .prop_index = index };
NlaEvalChannel *nec = nlaevalchan_find_match(&context->nla_channels, &rna);
/* Replace strips ignore influence when they are the first to modify this channel. */
if (nec == NULL && blend_mode == NLASTRIP_MODE_REPLACE) {
return true;
}
/* Invert the effect of blending modes. */
float old_value = nec ? nec->value : nlaevalchan_init_value(&rna);
return nla_invert_blend_value(blend_mode, old_value, *r_value, influence, r_value);
}
/**
* Free all cached contexts from the list.
*/
void BKE_animsys_free_nla_keyframing_context_cache(struct ListBase *cache)
{
for (NlaKeyframingContext *ctx = cache->first; ctx; ctx = ctx->next) {
MEM_SAFE_FREE(ctx->eval_strip);
BLI_freelistN(&ctx->nla_channels);
}
BLI_freelistN(cache);
}
/* ***************************************** */
/* Overrides System - Public API */

View File

@ -79,6 +79,21 @@ typedef struct NlaEvalChannel {
float value; /* value of this channel */
} NlaEvalChannel;
/* Information about the currently edited strip and ones below it for keyframing. */
typedef struct NlaKeyframingContext {
struct NlaKeyframingContext *next, *prev;
/* AnimData for which this context was built. */
struct AnimData *adt;
/* Data of the currently edited strip (copy, or fake strip for the main action). */
NlaStrip strip;
NlaEvalStrip *eval_strip;
/* Evaluated NLA stack below the current strip. */
ListBase nla_channels;
} NlaKeyframingContext;
/* --------------- NLA Functions (not to be used as a proper API) ----------------------- */
/* convert from strip time <-> global time */

View File

@ -4125,21 +4125,25 @@ static void achannel_setting_slider_cb(bContext *C, void *id_poin, void *fcu_poi
ReportList *reports = CTX_wm_reports(C);
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = scene->toolsettings;
ListBase nla_cache = {NULL, NULL};
PointerRNA id_ptr, ptr;
PropertyRNA *prop;
short flag = 0;
bool done = false;
float cfra;
/* Get RNA pointer */
RNA_id_pointer_create(id, &id_ptr);
/* Get NLA context for value remapping */
NlaKeyframingContext *nla_context = BKE_animsys_get_nla_keyframing_context(&nla_cache, depsgraph, &id_ptr, adt, (float)CFRA);
/* get current frame and apply NLA-mapping to it (if applicable) */
cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
/* get flags for keyframing */
flag = ANIM_get_keyframing_flags(scene, 1);
/* get RNA pointer, and resolve the path */
RNA_id_pointer_create(id, &id_ptr);
/* try to resolve the path stored in the F-Curve */
if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) {
/* set the special 'replace' flag if on a keyframe */
@ -4147,11 +4151,13 @@ static void achannel_setting_slider_cb(bContext *C, void *id_poin, void *fcu_poi
flag |= INSERTKEY_REPLACE;
/* insert a keyframe for this F-Curve */
done = insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, ts->keyframe_type, flag);
done = insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, ts->keyframe_type, nla_context, flag);
if (done)
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
}
BKE_animsys_free_nla_keyframing_context_cache(&nla_cache);
}
/* callback for shapekey widget sliders - insert keyframes */
@ -4166,21 +4172,25 @@ static void achannel_setting_slider_shapekey_cb(bContext *C, void *key_poin, voi
ReportList *reports = CTX_wm_reports(C);
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = scene->toolsettings;
ListBase nla_cache = {NULL, NULL};
PointerRNA id_ptr, ptr;
PropertyRNA *prop;
short flag = 0;
bool done = false;
float cfra;
/* Get RNA pointer */
RNA_id_pointer_create((ID *)key, &id_ptr);
/* Get NLA context for value remapping */
NlaKeyframingContext *nla_context = BKE_animsys_get_nla_keyframing_context(&nla_cache, depsgraph, &id_ptr, key->adt, (float)CFRA);
/* get current frame and apply NLA-mapping to it (if applicable) */
cfra = BKE_nla_tweakedit_remap(key->adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
/* get flags for keyframing */
flag = ANIM_get_keyframing_flags(scene, 1);
/* get RNA pointer, and resolve the path */
RNA_id_pointer_create((ID *)key, &id_ptr);
/* try to resolve the path stored in the F-Curve */
if (RNA_path_resolve_property(&id_ptr, rna_path, &ptr, &prop)) {
/* find or create new F-Curve */
@ -4193,7 +4203,7 @@ static void achannel_setting_slider_shapekey_cb(bContext *C, void *key_poin, voi
flag |= INSERTKEY_REPLACE;
/* insert a keyframe for this F-Curve */
done = insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, ts->keyframe_type, flag);
done = insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, ts->keyframe_type, nla_context, flag);
if (done)
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
@ -4202,6 +4212,8 @@ static void achannel_setting_slider_shapekey_cb(bContext *C, void *key_poin, voi
/* free the path */
if (rna_path)
MEM_freeN(rna_path);
BKE_animsys_free_nla_keyframing_context_cache(&nla_cache);
}
/* callback for NLA Control Curve widget sliders - insert keyframes */
@ -4237,7 +4249,7 @@ static void achannel_setting_slider_nla_curve_cb(bContext *C, void *UNUSED(id_po
flag |= INSERTKEY_REPLACE;
/* insert a keyframe for this F-Curve */
done = insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, ts->keyframe_type, flag);
done = insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, ts->keyframe_type, NULL, flag);
if (done)
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);

View File

@ -965,7 +965,7 @@ static float visualkey_get_value(Depsgraph *depsgraph, PointerRNA *ptr, Property
* the keyframe insertion. These include the 'visual' keyframing modes, quick refresh,
* and extra keyframe filtering.
*/
bool insert_keyframe_direct(Depsgraph *depsgraph, ReportList *reports, PointerRNA ptr, PropertyRNA *prop, FCurve *fcu, float cfra, eBezTriple_KeyframeType keytype, eInsertKeyFlags flag)
bool insert_keyframe_direct(Depsgraph *depsgraph, ReportList *reports, PointerRNA ptr, PropertyRNA *prop, FCurve *fcu, float cfra, eBezTriple_KeyframeType keytype, struct NlaKeyframingContext *nla_context, eInsertKeyFlags flag)
{
float curval = 0.0f;
@ -1038,6 +1038,12 @@ bool insert_keyframe_direct(Depsgraph *depsgraph, ReportList *reports, PointerRN
curval = setting_get_rna_value(depsgraph, &ptr, prop, fcu->array_index, false);
}
/* adjust the value for NLA factors */
if (!BKE_animsys_nla_remap_keyframe_value(nla_context, &ptr, prop, fcu->array_index, &curval)) {
BKE_report(reports, RPT_ERROR, "Could not insert keyframe due to zero NLA influence or base value");
return false;
}
/* adjust coordinates for cycle aware insertion */
if (flag & INSERTKEY_CYCLE_AWARE) {
if (remap_cyclic_keyframe_location(fcu, &cfra, &curval) != FCU_CYCLE_PERFECT) {
@ -1094,12 +1100,14 @@ bool insert_keyframe_direct(Depsgraph *depsgraph, ReportList *reports, PointerRN
*/
short insert_keyframe(
Main *bmain, Depsgraph *depsgraph, ReportList *reports, ID *id, bAction *act,
const char group[], const char rna_path[], int array_index, float cfra, eBezTriple_KeyframeType keytype, eInsertKeyFlags flag)
const char group[], const char rna_path[], int array_index, float cfra, eBezTriple_KeyframeType keytype, ListBase *nla_cache, eInsertKeyFlags flag)
{
PointerRNA id_ptr, ptr;
PropertyRNA *prop = NULL;
AnimData *adt;
FCurve *fcu;
ListBase tmp_nla_cache = {NULL, NULL};
NlaKeyframingContext *nla_context = NULL;
int array_index_max = array_index + 1;
int ret = 0;
@ -1132,7 +1140,14 @@ short insert_keyframe(
/* apply NLA-mapping to frame to use (if applicable) */
adt = BKE_animdata_from_id(id);
cfra = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
if (adt && adt->action == act) {
/* Get NLA context for value remapping. */
nla_context = BKE_animsys_get_nla_keyframing_context(nla_cache ? nla_cache : &tmp_nla_cache, depsgraph, &id_ptr, adt, cfra);
/* Apply NLA-mapping to frame. */
cfra = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
}
/* key entire array convenience method */
if (array_index == -1) {
@ -1172,10 +1187,12 @@ short insert_keyframe(
}
/* insert keyframe */
ret += insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, keytype, flag);
ret += insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, keytype, nla_context, flag);
}
}
BKE_animsys_free_nla_keyframing_context_cache(&tmp_nla_cache);
if (ret) {
if (act != NULL) {
DEG_id_tag_update(&act->id, ID_RECALC_COPY_ON_WRITE);
@ -1904,7 +1921,7 @@ static int insert_key_button_exec(bContext *C, wmOperator *op)
FCurve *fcu = list_find_fcurve(&strip->fcurves, RNA_property_identifier(prop), index);
if (fcu) {
success = insert_keyframe_direct(depsgraph, op->reports, ptr, prop, fcu, cfra, ts->keyframe_type, 0);
success = insert_keyframe_direct(depsgraph, op->reports, ptr, prop, fcu, cfra, ts->keyframe_type, NULL, 0);
}
else {
BKE_report(op->reports, RPT_ERROR,
@ -1919,7 +1936,7 @@ static int insert_key_button_exec(bContext *C, wmOperator *op)
fcu = rna_get_fcurve_context_ui(C, &ptr, prop, index, NULL, NULL, &driven, &special);
if (fcu && driven) {
success = insert_keyframe_direct(depsgraph, op->reports, ptr, prop, fcu, cfra, ts->keyframe_type, INSERTKEY_DRIVER);
success = insert_keyframe_direct(depsgraph, op->reports, ptr, prop, fcu, cfra, ts->keyframe_type, NULL, INSERTKEY_DRIVER);
}
}
else {
@ -1955,7 +1972,7 @@ static int insert_key_button_exec(bContext *C, wmOperator *op)
index = -1;
}
success = insert_keyframe(bmain, depsgraph, op->reports, ptr.id.data, NULL, group, path, index, cfra, ts->keyframe_type, flag);
success = insert_keyframe(bmain, depsgraph, op->reports, ptr.id.data, NULL, group, path, index, cfra, ts->keyframe_type, NULL, flag);
MEM_freeN(path);
}

View File

@ -962,6 +962,7 @@ int ANIM_apply_keyingset(bContext *C, ListBase *dsources, bAction *act, KeyingSe
Scene *scene = CTX_data_scene(C);
ReportList *reports = CTX_wm_reports(C);
KS_Path *ksp;
ListBase nla_cache = {NULL, NULL};
const short base_kflags = ANIM_get_keyframing_flags(scene, 1);
const char *groupname = NULL;
short kflag = 0, success = 0;
@ -1039,7 +1040,7 @@ int ANIM_apply_keyingset(bContext *C, ListBase *dsources, bAction *act, KeyingSe
for (; i < arraylen; i++) {
/* action to take depends on mode */
if (mode == MODIFYKEY_MODE_INSERT)
success += insert_keyframe(bmain, depsgraph, reports, ksp->id, act, groupname, ksp->rna_path, i, cfra, keytype, kflag2);
success += insert_keyframe(bmain, depsgraph, reports, ksp->id, act, groupname, ksp->rna_path, i, cfra, keytype, &nla_cache, kflag2);
else if (mode == MODIFYKEY_MODE_DELETE)
success += delete_keyframe(bmain, reports, ksp->id, act, groupname, ksp->rna_path, i, cfra, kflag2);
}
@ -1063,6 +1064,8 @@ int ANIM_apply_keyingset(bContext *C, ListBase *dsources, bAction *act, KeyingSe
WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL);
}
BKE_animsys_free_nla_keyframing_context_cache(&nla_cache);
/* return the number of channels successfully affected */
return success;
}

View File

@ -423,7 +423,7 @@ static void gp_stroke_path_animation_add_keyframes(
if ((cfra - last_valid_time) < MIN_TIME_DELTA) {
cfra = last_valid_time + MIN_TIME_DELTA;
}
insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_FAST);
insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, NULL, INSERTKEY_FAST);
last_valid_time = cfra;
}
else if (G.debug & G_DEBUG) {
@ -435,7 +435,7 @@ static void gp_stroke_path_animation_add_keyframes(
if ((cfra - last_valid_time) < MIN_TIME_DELTA) {
cfra = last_valid_time + MIN_TIME_DELTA;
}
insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_FAST);
insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, NULL, INSERTKEY_FAST);
last_valid_time = cfra;
}
else {
@ -443,7 +443,7 @@ static void gp_stroke_path_animation_add_keyframes(
* and also far enough from (not yet added!) end_stroke keyframe!
*/
if ((cfra - last_valid_time) > MIN_TIME_DELTA && (end_stroke_time - cfra) > MIN_TIME_DELTA) {
insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_BREAKDOWN, INSERTKEY_FAST);
insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_BREAKDOWN, NULL, INSERTKEY_FAST);
last_valid_time = cfra;
}
else if (G.debug & G_DEBUG) {
@ -499,7 +499,7 @@ static void gp_stroke_path_animation(bContext *C, ReportList *reports, Curve *cu
cu->ctime = 0.0f;
cfra = (float)gtd->start_frame;
insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_FAST);
insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, NULL, INSERTKEY_FAST);
cu->ctime = cu->pathlen;
if (gtd->realtime) {
@ -508,7 +508,7 @@ static void gp_stroke_path_animation(bContext *C, ReportList *reports, Curve *cu
else {
cfra = (float)gtd->end_frame;
}
insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_FAST);
insert_keyframe_direct(depsgraph, reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, NULL, INSERTKEY_FAST);
}
else {
/* Use actual recorded timing! */

View File

@ -56,6 +56,8 @@ struct PointerRNA;
struct PropertyRNA;
struct EnumPropertyItem;
struct NlaKeyframingContext;
#include "DNA_anim_types.h"
#include "RNA_types.h"
@ -109,7 +111,7 @@ int insert_vert_fcurve(struct FCurve *fcu, float x, float y, eBezTriple_Keyframe
* Use this to insert a keyframe using the current value being keyframed, in the
* nominated F-Curve (no creation of animation data performed). Returns success.
*/
bool insert_keyframe_direct(struct Depsgraph *depsgraph, struct ReportList *reports, struct PointerRNA ptr, struct PropertyRNA *prop, struct FCurve *fcu, float cfra, eBezTriple_KeyframeType keytype, eInsertKeyFlags flag);
bool insert_keyframe_direct(struct Depsgraph *depsgraph, struct ReportList *reports, struct PointerRNA ptr, struct PropertyRNA *prop, struct FCurve *fcu, float cfra, eBezTriple_KeyframeType keytype, struct NlaKeyframingContext *nla, eInsertKeyFlags flag);
/* -------- */
@ -119,7 +121,7 @@ bool insert_keyframe_direct(struct Depsgraph *depsgraph, struct ReportList *repo
*/
short insert_keyframe(
struct Main *bmain, struct Depsgraph *depsgraph, struct ReportList *reports, struct ID *id, struct bAction *act,
const char group[], const char rna_path[], int array_index, float cfra, eBezTriple_KeyframeType keytype, eInsertKeyFlags flag);
const char group[], const char rna_path[], int array_index, float cfra, eBezTriple_KeyframeType keytype, struct ListBase *nla_cache, eInsertKeyFlags flag);
/* Main Keyframing API call:
* Use this to delete keyframe on current frame for relevant channel. Will perform checks just in case.

View File

@ -274,7 +274,7 @@ void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra)
ReportList *reports = CTX_wm_reports(C);
ToolSettings *ts = scene->toolsettings;
insert_keyframe_direct(depsgraph, reports, but->rnapoin, but->rnaprop, fcu, cfra, ts->keyframe_type, 0);
insert_keyframe_direct(depsgraph, reports, but->rnapoin, but->rnaprop, fcu, cfra, ts->keyframe_type, NULL, 0);
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
}
}
@ -287,7 +287,7 @@ void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra)
ReportList *reports = CTX_wm_reports(C);
ToolSettings *ts = scene->toolsettings;
insert_keyframe_direct(depsgraph, reports, but->rnapoin, but->rnaprop, fcu, cfra, ts->keyframe_type, INSERTKEY_DRIVER);
insert_keyframe_direct(depsgraph, reports, but->rnapoin, but->rnaprop, fcu, cfra, ts->keyframe_type, NULL, INSERTKEY_DRIVER);
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
}
}
@ -309,7 +309,7 @@ void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra)
BLI_assert((fcu->array_index == but->rnaindex) || (but->rnaindex == -1));
insert_keyframe(bmain, depsgraph, reports, id, action,
((fcu->grp) ? (fcu->grp->name) : (NULL)),
fcu->rna_path, but->rnaindex, cfra, ts->keyframe_type, flag);
fcu->rna_path, but->rnaindex, cfra, ts->keyframe_type, NULL, flag);
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
}

View File

@ -54,6 +54,7 @@
#include "RNA_enum_types.h"
#include "BKE_action.h"
#include "BKE_animsys.h"
#include "BKE_context.h"
#include "BKE_fcurve.h"
#include "BKE_gpencil.h"
@ -679,6 +680,7 @@ static const EnumPropertyItem prop_actkeys_insertkey_types[] = {
static void insert_action_keys(bAnimContext *ac, short mode)
{
ListBase anim_data = {NULL, NULL};
ListBase nla_cache = {NULL, NULL};
bAnimListElem *ale;
int filter;
@ -711,7 +713,7 @@ static void insert_action_keys(bAnimContext *ac, short mode)
*/
if (ale->id && !ale->owner) {
insert_keyframe(ac->bmain, depsgraph, reports, ale->id, NULL, ((fcu->grp) ? (fcu->grp->name) : (NULL)),
fcu->rna_path, fcu->array_index, cfra, ts->keyframe_type, flag);
fcu->rna_path, fcu->array_index, cfra, ts->keyframe_type, &nla_cache, flag);
}
else {
AnimData *adt = ANIM_nla_mapping_get(ac, ale);
@ -727,6 +729,8 @@ static void insert_action_keys(bAnimContext *ac, short mode)
ale->update |= ANIM_UPDATE_DEFAULT;
}
BKE_animsys_free_nla_keyframing_context_cache(&nla_cache);
ANIM_animdata_update(ac, &anim_data);
ANIM_animdata_freelist(&anim_data);
}

View File

@ -54,6 +54,7 @@
#include "BLT_translation.h"
#include "BKE_animsys.h"
#include "BKE_context.h"
#include "BKE_fcurve.h"
#include "BKE_global.h"
@ -521,6 +522,7 @@ static const EnumPropertyItem prop_graphkeys_insertkey_types[] = {
static void insert_graph_keys(bAnimContext *ac, eGraphKeys_InsertKey_Types mode)
{
ListBase anim_data = {NULL, NULL};
ListBase nla_cache = {NULL, NULL};
bAnimListElem *ale;
int filter;
size_t num_items;
@ -602,7 +604,7 @@ static void insert_graph_keys(bAnimContext *ac, eGraphKeys_InsertKey_Types mode)
*/
if (ale->id && !ale->owner && !fcu->driver) {
insert_keyframe(ac->bmain, depsgraph, reports, ale->id, NULL, ((fcu->grp) ? (fcu->grp->name) : (NULL)),
fcu->rna_path, fcu->array_index, cfra, ts->keyframe_type, flag);
fcu->rna_path, fcu->array_index, cfra, ts->keyframe_type, &nla_cache, flag);
}
else {
AnimData *adt = ANIM_nla_mapping_get(ac, ale);
@ -621,6 +623,8 @@ static void insert_graph_keys(bAnimContext *ac, eGraphKeys_InsertKey_Types mode)
}
}
BKE_animsys_free_nla_keyframing_context_cache(&nla_cache);
ANIM_animdata_update(ac, &anim_data);
ANIM_animdata_freelist(&anim_data);
}

View File

@ -62,6 +62,7 @@
#include "BLI_rect.h"
#include "BKE_action.h"
#include "BKE_animsys.h"
#include "BKE_armature.h"
#include "BKE_constraint.h"
#include "BKE_context.h"
@ -6019,13 +6020,17 @@ void autokeyframe_object(bContext *C, Scene *scene, ViewLayer *view_layer, Objec
/* only key on available channels */
if (adt && adt->action) {
ListBase nla_cache = {NULL, NULL};
for (fcu = adt->action->curves.first; fcu; fcu = fcu->next) {
fcu->flag &= ~FCURVE_SELECTED;
insert_keyframe(bmain, depsgraph, reports, id, adt->action,
(fcu->grp ? fcu->grp->name : NULL),
fcu->rna_path, fcu->array_index, cfra,
ts->keyframe_type, flag);
ts->keyframe_type, &nla_cache, flag);
}
BKE_animsys_free_nla_keyframing_context_cache(&nla_cache);
}
}
else if (IS_AUTOKEY_FLAG(scene, INSERTNEEDED)) {
@ -6124,6 +6129,7 @@ void autokeyframe_pose(bContext *C, Scene *scene, Object *ob, int tmode, short t
ReportList *reports = CTX_wm_reports(C);
ToolSettings *ts = scene->toolsettings;
KeyingSet *active_ks = ANIM_scene_get_active_keyingset(scene);
ListBase nla_cache = {NULL, NULL};
float cfra = (float)CFRA;
short flag = 0;
@ -6167,7 +6173,7 @@ void autokeyframe_pose(bContext *C, Scene *scene, Object *ob, int tmode, short t
insert_keyframe(bmain, depsgraph, reports, id, act,
((fcu->grp) ? (fcu->grp->name) : (NULL)),
fcu->rna_path, fcu->array_index, cfra,
ts->keyframe_type, flag);
ts->keyframe_type, &nla_cache, flag);
}
if (pchanName) MEM_freeN(pchanName);
@ -6228,6 +6234,8 @@ void autokeyframe_pose(bContext *C, Scene *scene, Object *ob, int tmode, short t
BLI_freelistN(&dsources);
}
}
BKE_animsys_free_nla_keyframing_context_cache(&nla_cache);
}
else {
/* tag channels that should have unkeyed data */

View File

@ -255,7 +255,7 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb
NlaStrip *strip = (NlaStrip *)ptr.data;
FCurve *fcu = list_find_fcurve(&strip->fcurves, RNA_property_identifier(prop), index);
result = insert_keyframe_direct(depsgraph, &reports, ptr, prop, fcu, cfra, keytype, options);
result = insert_keyframe_direct(depsgraph, &reports, ptr, prop, fcu, cfra, keytype, NULL, options);
}
else {
BKE_reportf(&reports, RPT_ERROR, "Could not resolve path (%s)", path_full);
@ -276,7 +276,7 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb
BKE_reports_init(&reports, RPT_STORE);
BLI_assert(BKE_id_is_in_global_main(id));
result = insert_keyframe(G_MAIN, depsgraph, &reports, id, NULL, group_name, path_full, index, cfra, keytype, options);
result = insert_keyframe(G_MAIN, depsgraph, &reports, id, NULL, group_name, path_full, index, cfra, keytype, NULL, options);
MEM_freeN((void *)path_full);
if (BPy_reports_to_error(&reports, PyExc_RuntimeError, true) == -1)