Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/anim_sys.c
| Show First 20 Lines • Show All 356 Lines • ▼ Show 20 Lines | LISTBASE_FOREACH (KS_Path *, ksp, &ks->paths) { | ||||
| BLO_expand(expander, ksp->id); | BLO_expand(expander, ksp->id); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* ***************************************** */ | /* ***************************************** */ | ||||
| /* Evaluation Data-Setting Backend */ | /* Evaluation Data-Setting Backend */ | ||||
| static bool is_fcurve_evaluatable(FCurve *fcu) | |||||
| { | |||||
| if (fcu->flag & (FCURVE_MUTED | FCURVE_DISABLED)) { | |||||
| return false; | |||||
| } | |||||
| if (fcu->grp != NULL && (fcu->grp->flag & AGRP_MUTED)) { | |||||
| return false; | |||||
| } | |||||
| if (BKE_fcurve_is_empty(fcu)) { | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| bool BKE_animsys_store_rna_setting(PointerRNA *ptr, | bool BKE_animsys_store_rna_setting(PointerRNA *ptr, | ||||
| /* typically 'fcu->rna_path', 'fcu->array_index' */ | /* typically 'fcu->rna_path', 'fcu->array_index' */ | ||||
| const char *rna_path, | const char *rna_path, | ||||
| const int array_index, | const int array_index, | ||||
| PathResolvedRNA *r_result) | PathResolvedRNA *r_result) | ||||
| { | { | ||||
| bool success = false; | bool success = false; | ||||
| const char *path = rna_path; | const char *path = rna_path; | ||||
| ▲ Show 20 Lines • Show All 216 Lines • ▼ Show 20 Lines | |||||
| */ | */ | ||||
| static void animsys_evaluate_fcurves(PointerRNA *ptr, | static void animsys_evaluate_fcurves(PointerRNA *ptr, | ||||
| ListBase *list, | ListBase *list, | ||||
| const AnimationEvalContext *anim_eval_context, | const AnimationEvalContext *anim_eval_context, | ||||
| bool flush_to_original) | bool flush_to_original) | ||||
| { | { | ||||
| /* Calculate then execute each curve. */ | /* Calculate then execute each curve. */ | ||||
| LISTBASE_FOREACH (FCurve *, fcu, list) { | LISTBASE_FOREACH (FCurve *, fcu, list) { | ||||
| /* Check if this F-Curve doesn't belong to a muted group. */ | |||||
| if ((fcu->grp != NULL) && (fcu->grp->flag & AGRP_MUTED)) { | if (!is_fcurve_evaluatable(fcu)) { | ||||
| continue; | |||||
| } | |||||
| /* Check if this curve should be skipped. */ | |||||
| if ((fcu->flag & (FCURVE_MUTED | FCURVE_DISABLED))) { | |||||
| continue; | |||||
| } | |||||
| /* Skip empty curves, as if muted. */ | |||||
| if (BKE_fcurve_is_empty(fcu)) { | |||||
| continue; | continue; | ||||
| } | } | ||||
| PathResolvedRNA anim_rna; | PathResolvedRNA anim_rna; | ||||
| if (BKE_animsys_store_rna_setting(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { | if (BKE_animsys_store_rna_setting(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { | ||||
| const float curval = calculate_fcurve(&anim_rna, fcu, anim_eval_context); | const float curval = calculate_fcurve(&anim_rna, fcu, anim_eval_context); | ||||
| BKE_animsys_write_rna_setting(&anim_rna, curval); | BKE_animsys_write_rna_setting(&anim_rna, curval); | ||||
| if (flush_to_original) { | if (flush_to_original) { | ||||
| animsys_write_orig_anim_rna(ptr, fcu->rna_path, fcu->array_index, curval); | animsys_write_orig_anim_rna(ptr, fcu->rna_path, fcu->array_index, curval); | ||||
| } | } | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 357 Lines • ▼ Show 20 Lines | NlaEvalStrip *nlastrips_ctime_get_strip(ListBase *list, | ||||
| if (list) { | if (list) { | ||||
| BLI_addtail(list, nes); | BLI_addtail(list, nes); | ||||
| } | } | ||||
| return nes; | return nes; | ||||
| } | } | ||||
| static NlaEvalStrip *nlastrips_ctime_get_strip_single( | |||||
| ListBase *dst_list, | |||||
| NlaStrip *single_strip, | |||||
| const AnimationEvalContext *anim_eval_context, | |||||
| const bool flush_to_original) | |||||
| { | |||||
| ListBase single_tracks_list; | |||||
| single_tracks_list.first = single_tracks_list.last = single_strip; | |||||
| return nlastrips_ctime_get_strip( | |||||
| dst_list, &single_tracks_list, -1, anim_eval_context, flush_to_original); | |||||
| } | |||||
| /* ---------------------- */ | /* ---------------------- */ | ||||
| /* Initialize a valid mask, allocating memory if necessary. */ | /* Initialize a valid mask, allocating memory if necessary. */ | ||||
| static void nlavalidmask_init(NlaValidMask *mask, int bits) | static void nlavalidmask_init(NlaValidMask *mask, int bits) | ||||
| { | { | ||||
| if (BLI_BITMAP_SIZE(bits) > sizeof(mask->buffer)) { | if (BLI_BITMAP_SIZE(bits) > sizeof(mask->buffer)) { | ||||
| mask->ptr = BLI_BITMAP_NEW(bits, "NlaValidMask"); | mask->ptr = BLI_BITMAP_NEW(bits, "NlaValidMask"); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 157 Lines • ▼ Show 20 Lines | static void nlaeval_snapshot_free_data(NlaEvalSnapshot *snapshot) | ||||
| snapshot->channels = NULL; | snapshot->channels = NULL; | ||||
| } | } | ||||
| /* ---------------------- */ | /* ---------------------- */ | ||||
| /* Free memory owned by this evaluation channel. */ | /* Free memory owned by this evaluation channel. */ | ||||
| static void nlaevalchan_free_data(NlaEvalChannel *nec) | static void nlaevalchan_free_data(NlaEvalChannel *nec) | ||||
| { | { | ||||
| nlavalidmask_free(&nec->valid); | nlavalidmask_free(&nec->domain); | ||||
| if (nec->blend_snapshot != NULL) { | |||||
| nlaevalchan_snapshot_free(nec->blend_snapshot); | |||||
| } | |||||
| } | } | ||||
| /* Initialize a full NLA evaluation state structure. */ | /* Initialize a full NLA evaluation state structure. */ | ||||
| static void nlaeval_init(NlaEvalData *nlaeval) | static void nlaeval_init(NlaEvalData *nlaeval) | ||||
| { | { | ||||
| memset(nlaeval, 0, sizeof(*nlaeval)); | memset(nlaeval, 0, sizeof(*nlaeval)); | ||||
| nlaeval->path_hash = BLI_ghash_str_new("NlaEvalData::path_hash"); | nlaeval->path_hash = BLI_ghash_str_new("NlaEvalData::path_hash"); | ||||
| ▲ Show 20 Lines • Show All 180 Lines • ▼ Show 20 Lines | static NlaEvalChannel *nlaevalchan_verify_key(NlaEvalData *nlaeval, | ||||
| nec->key = *key; | nec->key = *key; | ||||
| nec->owner = nlaeval; | nec->owner = nlaeval; | ||||
| nec->index = nlaeval->num_channels++; | nec->index = nlaeval->num_channels++; | ||||
| nec->is_array = is_array; | nec->is_array = is_array; | ||||
| nec->mix_mode = nlaevalchan_detect_mix_mode(key, length); | nec->mix_mode = nlaevalchan_detect_mix_mode(key, length); | ||||
| nlavalidmask_init(&nec->valid, length); | nlavalidmask_init(&nec->domain, length); | ||||
| nec->base_snapshot.channel = nec; | nec->base_snapshot.channel = nec; | ||||
| nec->base_snapshot.length = length; | nec->base_snapshot.length = length; | ||||
| nec->base_snapshot.is_base = true; | nec->base_snapshot.is_base = true; | ||||
| nlaevalchan_get_default_values(nec, nec->base_snapshot.values); | nlaevalchan_get_default_values(nec, nec->base_snapshot.values); | ||||
| /* Store channel in data structures. */ | /* Store channel in data structures. */ | ||||
| ▲ Show 20 Lines • Show All 271 Lines • ▼ Show 20 Lines | static bool nla_combine_quaternion_get_inverted_strip_values(const float lower_values[4], | ||||
| invert_qt_normalized(tmp_lower); | invert_qt_normalized(tmp_lower); | ||||
| mul_qt_qtqt(r_strip_values, tmp_lower, tmp_blended); | mul_qt_qtqt(r_strip_values, tmp_lower, tmp_blended); | ||||
| pow_qt_fl_normalized(r_strip_values, 1.0f / influence); | pow_qt_fl_normalized(r_strip_values, 1.0f / influence); | ||||
| return true; | return true; | ||||
| } | } | ||||
| /* Data about the current blend mode. */ | |||||
| typedef struct NlaBlendData { | |||||
| NlaEvalSnapshot *snapshot; | |||||
| int mode; | |||||
| float influence; | |||||
| NlaEvalChannel *blend_queue; | |||||
| } NlaBlendData; | |||||
| /* Queue the channel for deferred blending. */ | |||||
| static NlaEvalChannelSnapshot *nlaevalchan_queue_blend(NlaBlendData *blend, NlaEvalChannel *nec) | |||||
| { | |||||
| if (!nec->in_blend) { | |||||
| if (nec->blend_snapshot == NULL) { | |||||
| nec->blend_snapshot = nlaevalchan_snapshot_new(nec); | |||||
| } | |||||
| nec->in_blend = true; | |||||
| nlaevalchan_snapshot_copy(nec->blend_snapshot, &nec->base_snapshot); | |||||
| nec->next_blend = blend->blend_queue; | |||||
| blend->blend_queue = nec; | |||||
| } | |||||
| return nec->blend_snapshot; | |||||
| } | |||||
| /* Accumulate (i.e. blend) the given value on to the channel it affects. */ | |||||
| static bool nlaeval_blend_value(NlaBlendData *blend, | |||||
| NlaEvalChannel *nec, | |||||
| int array_index, | |||||
| float value) | |||||
| { | |||||
| if (nec == NULL) { | |||||
| return false; | |||||
| } | |||||
| if (!nlaevalchan_validate_index_ex(nec, array_index)) { | |||||
| return false; | |||||
| } | |||||
| if (nec->mix_mode == NEC_MIX_QUATERNION) { | |||||
| /* For quaternion properties, always output all sub-channels. */ | |||||
| BLI_bitmap_set_all(nec->valid.ptr, true, 4); | |||||
| } | |||||
| else { | |||||
| BLI_BITMAP_ENABLE(nec->valid.ptr, array_index); | |||||
| } | |||||
| NlaEvalChannelSnapshot *nec_snapshot = nlaeval_snapshot_ensure_channel(blend->snapshot, nec); | |||||
| float *p_value = &nec_snapshot->values[array_index]; | |||||
| if (blend->mode == NLASTRIP_MODE_COMBINE) { | |||||
| /* Quaternion blending is deferred until all sub-channel values are known. */ | |||||
| if (nec->mix_mode == NEC_MIX_QUATERNION) { | |||||
| NlaEvalChannelSnapshot *blend_snapshot = nlaevalchan_queue_blend(blend, nec); | |||||
| blend_snapshot->values[array_index] = value; | |||||
| } | |||||
| else { | |||||
| float base_value = nec->base_snapshot.values[array_index]; | |||||
| *p_value = nla_combine_value(nec->mix_mode, base_value, *p_value, value, blend->influence); | |||||
| } | |||||
| } | |||||
| else { | |||||
| *p_value = nla_blend_value(blend->mode, *p_value, value, blend->influence); | |||||
| } | |||||
| return true; | |||||
| } | |||||
| /* Finish deferred quaternion blending. */ | |||||
| static void nlaeval_blend_flush(NlaBlendData *blend) | |||||
| { | |||||
| NlaEvalChannel *nec; | |||||
| while ((nec = blend->blend_queue)) { | |||||
| blend->blend_queue = nec->next_blend; | |||||
| nec->in_blend = false; | |||||
| NlaEvalChannelSnapshot *nec_snapshot = nlaeval_snapshot_ensure_channel(blend->snapshot, nec); | |||||
| NlaEvalChannelSnapshot *blend_snapshot = nec->blend_snapshot; | |||||
| if (nec->mix_mode == NEC_MIX_QUATERNION) { | |||||
| nla_combine_quaternion( | |||||
| nec_snapshot->values, blend_snapshot->values, blend->influence, nec_snapshot->values); | |||||
| } | |||||
| else { | |||||
| BLI_assert(!"mix quaternion"); | |||||
| } | |||||
| } | |||||
| } | |||||
| /* Blend the specified snapshots into the target, and free the input snapshots. */ | /* Blend the specified snapshots into the target, and free the input snapshots. */ | ||||
| static void nlaeval_snapshot_mix_and_free(NlaEvalData *nlaeval, | static void nlaeval_snapshot_mix_and_free(NlaEvalData *nlaeval, | ||||
| NlaEvalSnapshot *out, | NlaEvalSnapshot *out, | ||||
| NlaEvalSnapshot *in1, | NlaEvalSnapshot *in1, | ||||
| NlaEvalSnapshot *in2, | NlaEvalSnapshot *in2, | ||||
| float alpha) | float alpha) | ||||
| { | { | ||||
| BLI_assert(in1->base == out && in2->base == out); | BLI_assert(in1->base == out && in2->base == out); | ||||
| ▲ Show 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | static void nlaeval_fmodifiers_split_stacks(ListBase *list1, ListBase *list2) | ||||
| /* clear their links */ | /* clear their links */ | ||||
| fcm1->next = NULL; | fcm1->next = NULL; | ||||
| fcm2->prev = NULL; | fcm2->prev = NULL; | ||||
| } | } | ||||
| /* ---------------------- */ | /* ---------------------- */ | ||||
| /** Fills \a r_snapshot with the \a action's evaluated fcurve values with modifiers applied. */ | |||||
| static void nlasnapshot_from_action(PointerRNA *ptr, | |||||
| NlaEvalData *channels, | |||||
| ListBase *modifiers, | |||||
| bAction *action, | |||||
| const float evaltime, | |||||
| NlaEvalSnapshot *r_snapshot) | |||||
| { | |||||
| FCurve *fcu; | |||||
| action_idcode_patch_check(ptr->owner_id, action); | |||||
| /* Evaluate modifiers which modify time to evaluate the base curves at. */ | |||||
| FModifiersStackStorage storage; | |||||
| storage.modifier_count = BLI_listbase_count(modifiers); | |||||
| storage.size_per_modifier = evaluate_fmodifiers_storage_size_per_modifier(modifiers); | |||||
| storage.buffer = alloca(storage.modifier_count * storage.size_per_modifier); | |||||
| const float modified_evaltime = evaluate_time_fmodifiers( | |||||
| &storage, modifiers, NULL, 0.0f, evaltime); | |||||
| for (fcu = action->curves.first; fcu; fcu = fcu->next) { | |||||
| if (!is_fcurve_evaluatable(fcu)) { | |||||
| continue; | |||||
| } | |||||
| NlaEvalChannel *nec = nlaevalchan_verify(ptr, channels, fcu->rna_path); | |||||
| NlaEvalChannelSnapshot *necs = nlaeval_snapshot_ensure_channel(r_snapshot, nec); | |||||
| if (!nlaevalchan_validate_index_ex(nec, fcu->array_index)) { | |||||
| continue; | |||||
| } | |||||
| float value = evaluate_fcurve(fcu, modified_evaltime); | |||||
| evaluate_value_fmodifiers(&storage, modifiers, fcu, &value, evaltime); | |||||
| necs->values[fcu->array_index] = value; | |||||
| } | |||||
| } | |||||
| /* evaluate action-clip strip */ | /* evaluate action-clip strip */ | ||||
| static void nlastrip_evaluate_actionclip(PointerRNA *ptr, | static void nlastrip_evaluate_actionclip(PointerRNA *ptr, | ||||
| NlaEvalData *channels, | NlaEvalData *channels, | ||||
| ListBase *modifiers, | ListBase *modifiers, | ||||
| NlaEvalStrip *nes, | NlaEvalStrip *nes, | ||||
| NlaEvalSnapshot *snapshot) | NlaEvalSnapshot *snapshot) | ||||
| { | { | ||||
| ListBase tmp_modifiers = {NULL, NULL}; | |||||
| NlaStrip *strip = nes->strip; | NlaStrip *strip = nes->strip; | ||||
| FCurve *fcu; | |||||
| float evaltime; | |||||
| /* sanity checks for action */ | /* sanity checks for action */ | ||||
| if (strip == NULL) { | if (strip == NULL) { | ||||
| return; | return; | ||||
| } | } | ||||
| if (strip->act == NULL) { | if (strip->act == NULL) { | ||||
| CLOG_ERROR(&LOG, "NLA-Strip Eval Error: Strip '%s' has no Action", strip->name); | CLOG_ERROR(&LOG, "NLA-Strip Eval Error: Strip '%s' has no Action", strip->name); | ||||
| return; | return; | ||||
| } | } | ||||
| action_idcode_patch_check(ptr->owner_id, strip->act); | ListBase tmp_modifiers = {NULL, NULL}; | ||||
| /* join this strip's modifiers to the parent's modifiers (own modifiers first) */ | /* join this strip's modifiers to the parent's modifiers (own modifiers first) */ | ||||
| nlaeval_fmodifiers_join_stacks(&tmp_modifiers, &strip->modifiers, modifiers); | nlaeval_fmodifiers_join_stacks(&tmp_modifiers, &strip->modifiers, modifiers); | ||||
| /* evaluate strip's modifiers which modify time to evaluate the base curves at */ | NlaEvalSnapshot strip_snapshot; | ||||
| FModifiersStackStorage storage; | nlaeval_snapshot_init(&strip_snapshot, channels, NULL); | ||||
| storage.modifier_count = BLI_listbase_count(&tmp_modifiers); | |||||
| storage.size_per_modifier = evaluate_fmodifiers_storage_size_per_modifier(&tmp_modifiers); | |||||
| storage.buffer = alloca(storage.modifier_count * storage.size_per_modifier); | |||||
| evaltime = evaluate_time_fmodifiers(&storage, &tmp_modifiers, NULL, 0.0f, strip->strip_time); | |||||
| NlaBlendData blend = { | nlasnapshot_from_action( | ||||
| .snapshot = snapshot, | ptr, channels, &tmp_modifiers, strip->act, strip->strip_time, &strip_snapshot); | ||||
| .mode = strip->blendmode, | nlasnapshot_blend( | ||||
| .influence = strip->influence, | channels, snapshot, &strip_snapshot, strip->blendmode, strip->influence, snapshot); | ||||
| }; | |||||
| /* Evaluate all the F-Curves in the action, | |||||
| * saving the relevant pointers to data that will need to be used. */ | |||||
| for (fcu = strip->act->curves.first; fcu; fcu = fcu->next) { | |||||
| float value = 0.0f; | |||||
| /* check if this curve should be skipped */ | nlaeval_snapshot_free_data(&strip_snapshot); | ||||
| if (fcu->flag & (FCURVE_MUTED | FCURVE_DISABLED)) { | |||||
| continue; | |||||
| } | |||||
| if ((fcu->grp) && (fcu->grp->flag & AGRP_MUTED)) { | |||||
| continue; | |||||
| } | |||||
| if (BKE_fcurve_is_empty(fcu)) { | |||||
| continue; | |||||
| } | |||||
| /* evaluate the F-Curve's value for the time given in the strip | |||||
| * NOTE: we use the modified time here, since strip's F-Curve Modifiers | |||||
| * are applied on top of this. | |||||
| */ | |||||
| value = evaluate_fcurve(fcu, evaltime); | |||||
| /* apply strip's F-Curve Modifiers on this value | |||||
| * NOTE: we apply the strip's original evaluation time not the modified one | |||||
| * (as per standard F-Curve eval) | |||||
| */ | |||||
| evaluate_value_fmodifiers(&storage, &tmp_modifiers, fcu, &value, strip->strip_time); | |||||
| /* Get an NLA evaluation channel to work with, | |||||
| * and accumulate the evaluated value with the value(s) | |||||
| * stored in this channel if it has been used already. */ | |||||
| NlaEvalChannel *nec = nlaevalchan_verify(ptr, channels, fcu->rna_path); | |||||
| nlaeval_blend_value(&blend, nec, fcu->array_index, value); | |||||
| } | |||||
| nlaeval_blend_flush(&blend); | |||||
| /* unlink this strip's modifiers from the parent's modifiers again */ | /* unlink this strip's modifiers from the parent's modifiers again */ | ||||
| nlaeval_fmodifiers_split_stacks(&strip->modifiers, modifiers); | nlaeval_fmodifiers_split_stacks(&strip->modifiers, modifiers); | ||||
| } | } | ||||
| /* evaluate transition strip */ | /* evaluate transition strip */ | ||||
| static void nlastrip_evaluate_transition(PointerRNA *ptr, | static void nlastrip_evaluate_transition(PointerRNA *ptr, | ||||
| NlaEvalData *channels, | NlaEvalData *channels, | ||||
| ▲ Show 20 Lines • Show All 160 Lines • ▼ Show 20 Lines | |||||
| { | { | ||||
| /* sanity checks */ | /* sanity checks */ | ||||
| if (channels == NULL) { | if (channels == NULL) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* for each channel with accumulated values, write its value on the property it affects */ | /* for each channel with accumulated values, write its value on the property it affects */ | ||||
| LISTBASE_FOREACH (NlaEvalChannel *, nec, &channels->channels) { | LISTBASE_FOREACH (NlaEvalChannel *, nec, &channels->channels) { | ||||
| /** | |||||
| * The bitmask is set for all channels touched by NLA due to the domain() function. | |||||
| * Channels touched by current set of evaluated strips will have a snapshot channel directly | |||||
| * from the evaluation snapshot. | |||||
| * | |||||
| * This function falls back to the default value if the snapshot channel doesn't exist. | |||||
| * Thus channels, touched by NLA but not by the current set of evaluated strips, will be | |||||
| * reset to default. If channel not touched by NLA then it's value is unchanged. | |||||
| */ | |||||
| NlaEvalChannelSnapshot *nec_snapshot = nlaeval_snapshot_find_channel(snapshot, nec); | NlaEvalChannelSnapshot *nec_snapshot = nlaeval_snapshot_find_channel(snapshot, nec); | ||||
| PathResolvedRNA rna = {nec->key.ptr, nec->key.prop, -1}; | PathResolvedRNA rna = {nec->key.ptr, nec->key.prop, -1}; | ||||
| for (int i = 0; i < nec_snapshot->length; i++) { | for (int i = 0; i < nec_snapshot->length; i++) { | ||||
| if (BLI_BITMAP_TEST(nec->valid.ptr, i)) { | if (BLI_BITMAP_TEST(nec->domain.ptr, i)) { | ||||
| float value = nec_snapshot->values[i]; | float value = nec_snapshot->values[i]; | ||||
| if (nec->is_array) { | if (nec->is_array) { | ||||
| rna.prop_index = i; | rna.prop_index = i; | ||||
| } | } | ||||
| BKE_animsys_write_rna_setting(&rna, value); | BKE_animsys_write_rna_setting(&rna, value); | ||||
| if (flush_to_original) { | if (flush_to_original) { | ||||
| animsys_write_orig_anim_rna(ptr, nec->rna_path, rna.prop_index, value); | animsys_write_orig_anim_rna(ptr, nec->rna_path, rna.prop_index, value); | ||||
| } | } | ||||
| Show All 10 Lines | static void nla_eval_domain_action(PointerRNA *ptr, | ||||
| GSet *touched_actions) | GSet *touched_actions) | ||||
| { | { | ||||
| if (!BLI_gset_add(touched_actions, act)) { | if (!BLI_gset_add(touched_actions, act)) { | ||||
| return; | return; | ||||
| } | } | ||||
| LISTBASE_FOREACH (FCurve *, fcu, &act->curves) { | LISTBASE_FOREACH (FCurve *, fcu, &act->curves) { | ||||
| /* check if this curve should be skipped */ | /* check if this curve should be skipped */ | ||||
| if (fcu->flag & (FCURVE_MUTED | FCURVE_DISABLED)) { | if (!is_fcurve_evaluatable(fcu)) { | ||||
| continue; | |||||
| } | |||||
| if ((fcu->grp) && (fcu->grp->flag & AGRP_MUTED)) { | |||||
| continue; | |||||
| } | |||||
| if (BKE_fcurve_is_empty(fcu)) { | |||||
| continue; | continue; | ||||
| } | } | ||||
| NlaEvalChannel *nec = nlaevalchan_verify(ptr, channels, fcu->rna_path); | NlaEvalChannel *nec = nlaevalchan_verify(ptr, channels, fcu->rna_path); | ||||
| if (nec != NULL) { | if (nec != NULL) { | ||||
| /* For quaternion properties, enable all sub-channels. */ | /* For quaternion properties, enable all sub-channels. */ | ||||
| if (nec->mix_mode == NEC_MIX_QUATERNION) { | if (nec->mix_mode == NEC_MIX_QUATERNION) { | ||||
| BLI_bitmap_set_all(nec->valid.ptr, true, 4); | BLI_bitmap_set_all(nec->domain.ptr, true, 4); | ||||
| continue; | continue; | ||||
| } | } | ||||
| int idx = nlaevalchan_validate_index(nec, fcu->array_index); | int idx = nlaevalchan_validate_index(nec, fcu->array_index); | ||||
| if (idx >= 0) { | if (idx >= 0) { | ||||
| BLI_BITMAP_ENABLE(nec->valid.ptr, idx); | BLI_BITMAP_ENABLE(nec->domain.ptr, idx); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| static void nla_eval_domain_strips(PointerRNA *ptr, | static void nla_eval_domain_strips(PointerRNA *ptr, | ||||
| NlaEvalData *channels, | NlaEvalData *channels, | ||||
| ListBase *strips, | ListBase *strips, | ||||
| ▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | LISTBASE_FOREACH (NlaTrack *, nlt, &adt->nla_tracks) { | ||||
| nla_eval_domain_strips(ptr, channels, &nlt->strips, touched_actions); | nla_eval_domain_strips(ptr, channels, &nlt->strips, touched_actions); | ||||
| } | } | ||||
| BLI_gset_free(touched_actions, NULL); | BLI_gset_free(touched_actions, NULL); | ||||
| } | } | ||||
| /* ---------------------- */ | /* ---------------------- */ | ||||
| /** Tweaked strip is evaluated differently from other strips. Adjacent strips are ignored | |||||
| * and includes a workaround for when user is not editing in place. */ | |||||
| static void animsys_create_tweak_strip(const AnimData *adt, | |||||
| const bool keyframing_to_strip, | |||||
| NlaStrip *r_tweak_strip) | |||||
| { | |||||
| /* Copy active strip so we can modify how it evaluates without affecting user data. */ | |||||
| memcpy(r_tweak_strip, adt->actstrip, sizeof(NlaStrip)); | |||||
| r_tweak_strip->next = r_tweak_strip->prev = NULL; | |||||
| /* If tweaked strip is syncing action length, then evaluate using action length. */ | |||||
| if (r_tweak_strip->flag & NLASTRIP_FLAG_SYNC_LENGTH) { | |||||
| BKE_nlastrip_recalculate_bounds_sync_action(r_tweak_strip); | |||||
| } | |||||
| /* 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. */ | |||||
| const bool is_inplace_tweak = !(adt->flag & ADT_NLA_EDIT_NOMAP) && | |||||
| !(adt->actstrip->flag & NLASTRIP_FLAG_USR_TIME); | |||||
| if (!is_inplace_tweak) { | |||||
| /* Use Hold due to no proper remapping yet (the note above). */ | |||||
| r_tweak_strip->extendmode = NLASTRIP_EXTEND_HOLD; | |||||
| /* Disable range. */ | |||||
| r_tweak_strip->flag |= NLASTRIP_FLAG_NO_TIME_MAP; | |||||
| } | |||||
| /** Controls whether able to keyframe outside range of tweaked strip. */ | |||||
| if (keyframing_to_strip) { | |||||
| r_tweak_strip->extendmode = (is_inplace_tweak && | |||||
| !(r_tweak_strip->flag & NLASTRIP_FLAG_SYNC_LENGTH)) ? | |||||
| NLASTRIP_EXTEND_NOTHING : | |||||
| NLASTRIP_EXTEND_HOLD; | |||||
| } | |||||
| } | |||||
| /** Action track and strip are associated with the non-pushed action. */ | |||||
| static void animsys_create_action_track_strip(const AnimData *adt, | |||||
| const bool keyframing_to_strip, | |||||
| NlaStrip *r_action_strip) | |||||
| { | |||||
| memset(r_action_strip, 0, sizeof(NlaStrip)); | |||||
| bAction *action = adt->action; | |||||
| if ((adt->flag & ADT_NLA_EDIT_ON)) { | |||||
| action = adt->tmpact; | |||||
| } | |||||
| /* Set settings of dummy NLA strip from AnimData settings. */ | |||||
| r_action_strip->act = 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(r_action_strip->act, &r_action_strip->actstart, &r_action_strip->actend, 1); | |||||
| r_action_strip->start = r_action_strip->actstart; | |||||
| r_action_strip->end = (IS_EQF(r_action_strip->actstart, r_action_strip->actend)) ? | |||||
| (r_action_strip->actstart + 1.0f) : | |||||
| (r_action_strip->actend); | |||||
| r_action_strip->blendmode = adt->act_blendmode; | |||||
| r_action_strip->extendmode = adt->act_extendmode; | |||||
| r_action_strip->influence = adt->act_influence; | |||||
| /* NOTE: must set this, or else the default setting overrides, | |||||
| * and this setting doesn't work. */ | |||||
| r_action_strip->flag |= NLASTRIP_FLAG_USR_INFLUENCE; | |||||
| /* Unless extendmode is Nothing (might be useful for flattening NLA evaluation), disable range. | |||||
| * Extendmode Nothing and Hold will behave as normal. Hold Forward will behave just like Hold. | |||||
| */ | |||||
| if (r_action_strip->extendmode != NLASTRIP_EXTEND_NOTHING) { | |||||
| r_action_strip->flag |= NLASTRIP_FLAG_NO_TIME_MAP; | |||||
| } | |||||
| const bool tweaking = (adt->flag & ADT_NLA_EDIT_ON) != 0; | |||||
| const bool soloing = (adt->flag & ADT_NLA_SOLO_TRACK) != 0; | |||||
| const bool actionstrip_evaluated = r_action_strip->act && !soloing && !tweaking; | |||||
| if (!actionstrip_evaluated) { | |||||
| r_action_strip->flag |= NLASTRIP_FLAG_MUTED; | |||||
| } | |||||
| /** If we're keyframing, then we must allow keyframing outside fcurve bounds. */ | |||||
| if (keyframing_to_strip) { | |||||
| r_action_strip->extendmode = NLASTRIP_EXTEND_HOLD; | |||||
| } | |||||
| } | |||||
| static bool is_nlatrack_evaluatable(const AnimData *adt, const NlaTrack *nlt) | |||||
| { | |||||
| /* Skip disabled tracks unless it contains the tweaked strip. */ | |||||
| const bool contains_tweak_strip = (adt->flag & ADT_NLA_EDIT_ON) && | |||||
| (nlt->index == adt->act_track->index); | |||||
| if ((nlt->flag & NLATRACK_DISABLED) && !contains_tweak_strip) { | |||||
| return false; | |||||
| } | |||||
| /* Solo and muting are mutually exclusive. */ | |||||
| if (adt->flag & ADT_NLA_SOLO_TRACK) { | |||||
| /* Skip if there is a solo track, but this isn't it. */ | |||||
| if ((nlt->flag & NLATRACK_SOLO) == 0) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| else { | |||||
| /* Skip track if muted. */ | |||||
| if (nlt->flag & NLATRACK_MUTED) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return true; | |||||
| } | |||||
| /** Check for special case of non-pushed action being evaluated with no NLA influence (off and no | |||||
| * strips evaluated) nor NLA interference (ensure NLA not soloing). */ | |||||
| static bool is_action_track_evaluated_without_nla(const AnimData *adt, | |||||
| const bool any_strip_evaluated) | |||||
| { | |||||
| if (adt->action == NULL) { | |||||
| return false; | |||||
| } | |||||
| if (any_strip_evaluated) { | |||||
| return false; | |||||
| } | |||||
| /** NLA settings interference. */ | |||||
| if ((adt->flag & (ADT_NLA_SOLO_TRACK | ADT_NLA_EDIT_ON)) == 0) { | |||||
| return false; | |||||
| } | |||||
| /** Allow action track to evaluate as if there isn't any NLA data. */ | |||||
| return true; | |||||
| } | |||||
| /** | /** | ||||
| * NLA Evaluation function - values are calculated and stored in temporary "NlaEvalChannels" | * XXX(Wayde Moss): #BKE_nlatrack_find_tweaked() exists within nla.c, but it doesn't appear to | ||||
| * work as expected. From #animsys_evaluate_nla_for_flush(), it returns NULL in tweak mode. I'm not | |||||
| * sure why. Preferably, it would be as simple as checking for `(adt->act_Track == nlt)` but that | |||||
| * doesn't work either, neither does comparing indices. | |||||
| * | * | ||||
| * This function is a temporary work around. The first disabled track is always the tweaked track. | |||||
| */ | |||||
| static NlaTrack *nlatrack_find_tweaked(const AnimData *adt) | |||||
| { | |||||
| NlaTrack *nlt; | |||||
| if (adt == NULL) { | |||||
| return NULL; | |||||
| } | |||||
| /* Since the track itself gets disabled, we want the first disabled. */ | |||||
| for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) { | |||||
| if (nlt->flag & NLATRACK_DISABLED) { | |||||
| return nlt; | |||||
| } | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| /** | |||||
| * NLA Evaluation function - values are calculated and stored in temporary "NlaEvalChannels" | |||||
| * \param[out] echannels: Evaluation channels with calculated values | * \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. | |||||
| * \return false if NLA evaluation isn't actually applicable. | |||||
| */ | */ | ||||
| static bool animsys_evaluate_nla(NlaEvalData *echannels, | static bool animsys_evaluate_nla_for_flush(NlaEvalData *echannels, | ||||
| PointerRNA *ptr, | PointerRNA *ptr, | ||||
| AnimData *adt, | const AnimData *adt, | ||||
| const AnimationEvalContext *anim_eval_context, | const AnimationEvalContext *anim_eval_context, | ||||
| const bool flush_to_original, | const bool flush_to_original) | ||||
| NlaKeyframingContext *r_context) | |||||
| { | { | ||||
| NlaTrack *nlt; | NlaTrack *nlt; | ||||
| short track_index = 0; | short track_index = 0; | ||||
| bool has_strips = false; | bool has_strips = false; | ||||
| ListBase estrips = {NULL, NULL}; | ListBase estrips = {NULL, NULL}; | ||||
| NlaEvalStrip *nes; | NlaEvalStrip *nes; | ||||
| NlaStrip dummy_strip_buf; | |||||
| /* dummy strip for active action */ | NlaStrip tweak_strip; | ||||
| NlaStrip *dummy_strip = r_context ? &r_context->strip : &dummy_strip_buf; | |||||
| memset(dummy_strip, 0, sizeof(*dummy_strip)); | NlaTrack *tweaked_track = nlatrack_find_tweaked(adt); | ||||
| /* 1. get the stack of strips to evaluate at current time (influence calculated here) */ | /* 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++) { | for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next, track_index++) { | ||||
| /* stop here if tweaking is on and this strip is the tweaking track | |||||
| * (it will be the first one that's 'disabled')... */ | |||||
| if ((adt->flag & ADT_NLA_EDIT_ON) && (nlt->flag & NLATRACK_DISABLED)) { | |||||
| break; | |||||
| } | |||||
| /* solo and muting are mutually exclusive... */ | if (!is_nlatrack_evaluatable(adt, nlt)) { | ||||
| if (adt->flag & ADT_NLA_SOLO_TRACK) { | |||||
| /* skip if there is a solo track, but this isn't it */ | |||||
| if ((nlt->flag & NLATRACK_SOLO) == 0) { | |||||
| continue; | |||||
| } | |||||
| /* else - mute doesn't matter */ | |||||
| } | |||||
| else { | |||||
| /* no solo tracks - skip track if muted */ | |||||
| if (nlt->flag & NLATRACK_MUTED) { | |||||
| continue; | continue; | ||||
| } | } | ||||
| } | |||||
| /* if this track has strips (but maybe they won't be suitable), set has_strips | |||||
| * - used for mainly for still allowing normal action evaluation... | |||||
| */ | |||||
| if (nlt->strips.first) { | if (nlt->strips.first) { | ||||
| has_strips = true; | has_strips = true; | ||||
| } | } | ||||
| /* otherwise, get strip to evaluate for this channel */ | /** Append strip to evaluate for this track. */ | ||||
| if (nlt == tweaked_track) { | |||||
| /** Tweaked strip is evaluated differently. */ | |||||
| animsys_create_tweak_strip(adt, false, &tweak_strip); | |||||
| nes = nlastrips_ctime_get_strip_single( | |||||
| &estrips, &tweak_strip, anim_eval_context, flush_to_original); | |||||
| } | |||||
| else { | |||||
| nes = nlastrips_ctime_get_strip( | nes = nlastrips_ctime_get_strip( | ||||
| &estrips, &nlt->strips, track_index, anim_eval_context, flush_to_original); | &estrips, &nlt->strips, track_index, anim_eval_context, flush_to_original); | ||||
| } | |||||
| if (nes) { | if (nes) { | ||||
| nes->track = nlt; | nes->track = nlt; | ||||
| } | } | ||||
| } | } | ||||
| /* add 'active' Action (may be tweaking track) as last strip to evaluate in NLA stack | if (is_action_track_evaluated_without_nla(adt, has_strips)) { | ||||
| * - only do this if we're not exclusively evaluating the 'solo' NLA-track | BLI_freelistN(&estrips); | ||||
| * - however, if the 'solo' track houses the current 'tweaking' strip, | return false; | ||||
| * then we should allow this to play, otherwise nothing happens | } | ||||
| */ | |||||
| if ((adt->action) && ((adt->flag & ADT_NLA_SOLO_TRACK) == 0 || (adt->flag & ADT_NLA_EDIT_ON))) { | |||||
| /* if there are strips, evaluate action as per NLA rules */ | |||||
| if ((has_strips) || (adt->actstrip)) { | |||||
| /* make dummy NLA strip, and add that to the stack */ | |||||
| ListBase dummy_trackslist; | |||||
| dummy_trackslist.first = dummy_trackslist.last = dummy_strip; | |||||
| /* 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) { | NlaStrip action_strip = {0}; | ||||
| /* edit active action in-place according to its active strip, so copy the data */ | animsys_create_action_track_strip(adt, false, &action_strip); | ||||
| memcpy(dummy_strip, adt->actstrip, sizeof(NlaStrip)); | nlastrips_ctime_get_strip_single(&estrips, &action_strip, anim_eval_context, flush_to_original); | ||||
| /* Prevents nla eval from considering active strip's adj strips. | |||||
| * For user, this means entering tweak mode on a strip ignores evaluating adjacent strips | |||||
| * in the same track. */ | |||||
| dummy_strip->next = dummy_strip->prev = NULL; | |||||
| /* If tweaked strip is syncing action length, then evaluate using action length. */ | /* Per strip, evaluate and accumulate on top of existing channels. */ | ||||
| if (dummy_strip->flag & NLASTRIP_FLAG_SYNC_LENGTH) { | for (nes = estrips.first; nes; nes = nes->next) { | ||||
| BKE_nlastrip_recalculate_bounds_sync_action(dummy_strip); | nlastrip_evaluate(ptr, | ||||
| echannels, | |||||
| NULL, | |||||
| nes, | |||||
| &echannels->eval_snapshot, | |||||
| anim_eval_context, | |||||
| flush_to_original); | |||||
| } | } | ||||
| /* Free temporary evaluation data that's not used elsewhere. */ | |||||
| BLI_freelistN(&estrips); | |||||
| return true; | |||||
| } | } | ||||
| else { | |||||
| /* set settings of dummy NLA strip from AnimData settings */ | |||||
| dummy_strip->act = adt->action; | |||||
| /* action range is calculated taking F-Modifiers into account | /** Lower blended values are calculated and accumulated into r_context->lower_eval_data. */ | ||||
| * (which making new strips doesn't do due to the troublesome nature of that) */ | static void animsys_evaluate_nla_for_keyframing(PointerRNA *ptr, | ||||
| calc_action_range(dummy_strip->act, &dummy_strip->actstart, &dummy_strip->actend, 1); | const AnimData *adt, | ||||
| dummy_strip->start = dummy_strip->actstart; | const AnimationEvalContext *anim_eval_context, | ||||
| dummy_strip->end = (IS_EQF(dummy_strip->actstart, dummy_strip->actend)) ? | NlaKeyframingContext *r_context) | ||||
| (dummy_strip->actstart + 1.0f) : | { | ||||
| (dummy_strip->actend); | if (!r_context) { | ||||
| return; | |||||
| } | |||||
| /* Always use the blend mode of the strip in tweak mode, even if not in-place. */ | /* Early out. If NLA track is soloing and tweaked action isn't it, then don't allow keyframe | ||||
| if (nlt && adt->actstrip) { | * insertion. */ | ||||
| dummy_strip->blendmode = adt->actstrip->blendmode; | if (adt->flag & ADT_NLA_SOLO_TRACK) { | ||||
| dummy_strip->extendmode = NLASTRIP_EXTEND_HOLD; | if (!(adt->act_track && (adt->act_track->flag & NLATRACK_SOLO))) { | ||||
| r_context->eval_strip = NULL; | |||||
| return; | |||||
| } | } | ||||
| else { | |||||
| dummy_strip->blendmode = adt->act_blendmode; | |||||
| dummy_strip->extendmode = adt->act_extendmode; | |||||
| } | } | ||||
| /* Unless extend-mode is Nothing (might be useful for flattening NLA evaluation), | NlaTrack *nlt; | ||||
| * disable range. */ | short track_index = 0; | ||||
| if (dummy_strip->extendmode != NLASTRIP_EXTEND_NOTHING) { | bool has_strips = false; | ||||
| dummy_strip->flag |= NLASTRIP_FLAG_NO_TIME_MAP; | |||||
| } | |||||
| dummy_strip->influence = adt->act_influence; | ListBase lower_estrips = {NULL, NULL}; | ||||
| NlaEvalStrip *nes; | |||||
| /* NOTE: must set this, or else the default setting overrides, | NlaTrack *tweaked_track = nlatrack_find_tweaked(adt); | ||||
| * and this setting doesn't work. */ | |||||
| dummy_strip->flag |= NLASTRIP_FLAG_USR_INFLUENCE; | /* Get the lower stack of strips to evaluate at current time (influence calculated here). */ | ||||
| for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next, track_index++) { | |||||
| if (!is_nlatrack_evaluatable(adt, nlt)) { | |||||
| continue; | |||||
| } | } | ||||
| /* add this to our list of evaluation strips */ | /* Tweaked strip effect should not be stored in any snapshot. */ | ||||
| if (r_context == NULL) { | if (nlt == tweaked_track) { | ||||
| nlastrips_ctime_get_strip( | break; | ||||
| &estrips, &dummy_trackslist, -1, anim_eval_context, flush_to_original); | |||||
| } | } | ||||
| /* 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 key-frame beyond the ends.*/ | |||||
| dummy_strip->extendmode = (is_inplace_tweak && | |||||
| !(dummy_strip->flag & NLASTRIP_FLAG_SYNC_LENGTH)) ? | |||||
| NLASTRIP_EXTEND_NOTHING : | |||||
| NLASTRIP_EXTEND_HOLD; | |||||
| r_context->eval_strip = nes = nlastrips_ctime_get_strip( | if (nlt->strips.first) { | ||||
| NULL, &dummy_trackslist, -1, anim_eval_context, flush_to_original); | has_strips = true; | ||||
| } | |||||
| /* These setting combinations require no data from strips below, so exit immediately. */ | /* Get strip to evaluate for this channel. */ | ||||
| if ((nes == NULL) || | nes = nlastrips_ctime_get_strip( | ||||
| (dummy_strip->blendmode == NLASTRIP_MODE_REPLACE && dummy_strip->influence == 1.0f)) { | &lower_estrips, &nlt->strips, track_index, anim_eval_context, false); | ||||
| BLI_freelistN(&estrips); | if (nes) { | ||||
| return true; | nes->track = nlt; | ||||
| } | |||||
| } | } | ||||
| /** Note: Although we early out, we can still keyframe to the non-pushed action since the | |||||
| * keyframe remap function detects (r_context->strip.act == NULL) and will keyframe without | |||||
| * remapping. | |||||
| */ | |||||
| if (is_action_track_evaluated_without_nla(adt, has_strips)) { | |||||
| BLI_freelistN(&lower_estrips); | |||||
| return; | |||||
| } | } | ||||
| /* Write r_context->eval_strip. */ | |||||
| if (adt->flag & ADT_NLA_EDIT_ON) { | |||||
| NlaStrip *tweak_strip = &r_context->strip; | |||||
| animsys_create_tweak_strip(adt, true, tweak_strip); | |||||
| r_context->eval_strip = nlastrips_ctime_get_strip_single( | |||||
| NULL, tweak_strip, anim_eval_context, false); | |||||
| } | } | ||||
| else { | else { | ||||
| /* special case - evaluate as if there isn't any NLA data */ | |||||
| BLI_freelistN(&estrips); | NlaStrip *action_strip = &r_context->strip; | ||||
| return false; | animsys_create_action_track_strip(adt, true, action_strip); | ||||
| r_context->eval_strip = nlastrips_ctime_get_strip_single( | |||||
| NULL, action_strip, anim_eval_context, false); | |||||
| } | } | ||||
| /* If NULL, then keyframing will fail. No need to do any more processing. */ | |||||
| if (!r_context->eval_strip) { | |||||
| BLI_freelistN(&lower_estrips); | |||||
| return; | |||||
| } | } | ||||
| /* only continue if there are strips to evaluate */ | /* If tweak strip is full REPLACE, then lower strips not needed. */ | ||||
| if (BLI_listbase_is_empty(&estrips)) { | if (r_context->strip.blendmode == NLASTRIP_MODE_REPLACE && | ||||
| return true; | IS_EQF(r_context->strip.influence, 1.0f)) { | ||||
| BLI_freelistN(&lower_estrips); | |||||
| return; | |||||
| } | } | ||||
| /* 2. for each strip, evaluate then accumulate on top of existing channels, | /* For each strip, evaluate then accumulate on top of existing channels. */ | ||||
| * but don't set values yet. */ | for (nes = lower_estrips.first; nes; nes = nes->next) { | ||||
| for (nes = estrips.first; nes; nes = nes->next) { | |||||
| nlastrip_evaluate(ptr, | nlastrip_evaluate(ptr, | ||||
| echannels, | &r_context->lower_eval_data, | ||||
| NULL, | NULL, | ||||
| nes, | nes, | ||||
| &echannels->eval_snapshot, | &r_context->lower_eval_data.eval_snapshot, | ||||
| anim_eval_context, | anim_eval_context, | ||||
| flush_to_original); | false); | ||||
| } | } | ||||
| /* 3. free temporary evaluation data that's not used elsewhere */ | /* Free temporary evaluation data that's not used elsewhere. */ | ||||
| BLI_freelistN(&estrips); | BLI_freelistN(&lower_estrips); | ||||
| return true; | |||||
| } | } | ||||
| /* NLA Evaluation function (mostly for use through do_animdata) | /* NLA Evaluation function (mostly for use through do_animdata) | ||||
| * - All channels that will be affected are not cleared anymore. Instead, we just evaluate into | * - All channels that will be affected are not cleared anymore. Instead, we just evaluate into | ||||
| * some temp channels, where values can be accumulated in one go. | * some temp channels, where values can be accumulated in one go. | ||||
| */ | */ | ||||
| static void animsys_calculate_nla(PointerRNA *ptr, | static void animsys_calculate_nla(PointerRNA *ptr, | ||||
| AnimData *adt, | AnimData *adt, | ||||
| const AnimationEvalContext *anim_eval_context, | const AnimationEvalContext *anim_eval_context, | ||||
| const bool flush_to_original) | const bool flush_to_original) | ||||
| { | { | ||||
| NlaEvalData echannels; | NlaEvalData echannels; | ||||
| nlaeval_init(&echannels); | nlaeval_init(&echannels); | ||||
| /* evaluate the NLA stack, obtaining a set of values to flush */ | /* evaluate the NLA stack, obtaining a set of values to flush */ | ||||
| if (animsys_evaluate_nla(&echannels, ptr, adt, anim_eval_context, flush_to_original, NULL)) { | if (animsys_evaluate_nla_for_flush(&echannels, ptr, adt, anim_eval_context, flush_to_original)) { | ||||
| /* reset any channels touched by currently inactive actions to default value */ | /* reset any channels touched by currently inactive actions to default value */ | ||||
| animsys_evaluate_nla_domain(ptr, &echannels, adt); | animsys_evaluate_nla_domain(ptr, &echannels, adt); | ||||
| /* flush effects of accumulating channels in NLA to the actual data they affect */ | /* flush effects of accumulating channels in NLA to the actual data they affect */ | ||||
| nladata_flush_channels(ptr, &echannels, &echannels.eval_snapshot, flush_to_original); | nladata_flush_channels(ptr, &echannels, &echannels.eval_snapshot, flush_to_original); | ||||
| } | } | ||||
| else { | else { | ||||
| /* special case - evaluate as if there isn't any NLA data */ | /* special case - evaluate as if there isn't any NLA data */ | ||||
| /* TODO: this is really just a stop-gap measure... */ | /* TODO: this is really just a stop-gap measure... */ | ||||
| if (G.debug & G_DEBUG) { | if (G.debug & G_DEBUG) { | ||||
| CLOG_WARN(&LOG, "NLA Eval: Stopgap for active action on NLA Stack - no strips case"); | CLOG_WARN(&LOG, "NLA Eval: Stopgap for active action on NLA Stack - no strips case"); | ||||
| } | } | ||||
| animsys_evaluate_action(ptr, adt->action, anim_eval_context, flush_to_original); | animsys_evaluate_action(ptr, adt->action, anim_eval_context, flush_to_original); | ||||
| } | } | ||||
| /* free temp data */ | /* free temp data */ | ||||
| nlaeval_free(&echannels); | nlaeval_free(&echannels); | ||||
| } | } | ||||
| /* ---------------------- */ | /* ---------------------- */ | ||||
| /** Blends the \a lower_snapshot with the \a upper_snapshot into \a r_blended_snapshot according | |||||
| * to the given \a upper_blendmode and \a upper_influence. */ | |||||
| void nlasnapshot_blend(NlaEvalData *eval_data, | |||||
| NlaEvalSnapshot *lower_snapshot, | |||||
| NlaEvalSnapshot *upper_snapshot, | |||||
| const short upper_blendmode, | |||||
| const float upper_influence, | |||||
| NlaEvalSnapshot *r_blended_snapshot) | |||||
| { | |||||
| nlaeval_snapshot_ensure_size(r_blended_snapshot, eval_data->num_channels); | |||||
| const bool zero_upper_influence = IS_EQF(upper_influence, 0.0f); | |||||
| LISTBASE_FOREACH (NlaEvalChannel *, nec, &eval_data->channels) { | |||||
| const int length = nec->base_snapshot.length; | |||||
| NlaEvalChannelSnapshot *upper_necs = nlaeval_snapshot_get(upper_snapshot, nec->index); | |||||
| NlaEvalChannelSnapshot *lower_necs = nlaeval_snapshot_get(lower_snapshot, nec->index); | |||||
| if (upper_necs == NULL && lower_necs == NULL) { | |||||
| continue; | |||||
| } | |||||
| /** Blend with lower_snapshot's base or default. */ | |||||
| if (lower_necs == NULL) { | |||||
| lower_necs = nlaeval_snapshot_find_channel(lower_snapshot->base, nec); | |||||
| } | |||||
| NlaEvalChannelSnapshot *result_necs = nlaeval_snapshot_ensure_channel(r_blended_snapshot, nec); | |||||
| if (upper_necs == NULL || zero_upper_influence) { | |||||
| memcpy(result_necs->values, lower_necs->values, length * sizeof(float)); | |||||
| continue; | |||||
| } | |||||
| if (upper_blendmode == NLASTRIP_MODE_COMBINE) { | |||||
| const int mix_mode = nec->mix_mode; | |||||
| if (mix_mode == NEC_MIX_QUATERNION) { | |||||
| nla_combine_quaternion( | |||||
| lower_necs->values, upper_necs->values, upper_influence, result_necs->values); | |||||
| } | |||||
| else { | |||||
| for (int j = 0; j < length; j++) { | |||||
| result_necs->values[j] = nla_combine_value(mix_mode, | |||||
| nec->base_snapshot.values[j], | |||||
| lower_necs->values[j], | |||||
| upper_necs->values[j], | |||||
| upper_influence); | |||||
| } | |||||
| } | |||||
| } | |||||
| else { | |||||
| for (int j = 0; j < length; j++) { | |||||
| result_necs->values[j] = nla_blend_value( | |||||
| upper_blendmode, lower_necs->values[j], upper_necs->values[j], upper_influence); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| /* ---------------------- */ | |||||
| /** | /** | ||||
| * Prepare data necessary to compute correct keyframe values for NLA strips | * Prepare data necessary to compute correct keyframe values for NLA strips | ||||
| * with non-Replace mode or influence different from 1. | * with non-Replace mode or influence different from 1. | ||||
| * | * | ||||
| * \param cache: List used to cache contexts for reuse when keying | * \param cache: List used to cache contexts for reuse when keying | ||||
| * multiple channels in one operation. | * multiple channels in one operation. | ||||
| * \param ptr: RNA pointer to the Object with the animation. | * \param ptr: RNA pointer to the Object with the animation. | ||||
| * \return Keyframing context, or NULL if not necessary. | * \return Keyframing context, or NULL if not necessary. | ||||
| */ | */ | ||||
| NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context( | NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context( | ||||
| struct ListBase *cache, | struct ListBase *cache, | ||||
| struct PointerRNA *ptr, | struct PointerRNA *ptr, | ||||
| struct AnimData *adt, | struct AnimData *adt, | ||||
| const AnimationEvalContext *anim_eval_context, | const AnimationEvalContext *anim_eval_context) | ||||
| const bool flush_to_original) | |||||
| { | { | ||||
| /* No remapping needed if NLA is off or no action. */ | /* No remapping needed if NLA is off or no action. */ | ||||
| if ((adt == NULL) || (adt->action == NULL) || (adt->nla_tracks.first == NULL) || | if ((adt == NULL) || (adt->action == NULL) || (adt->nla_tracks.first == NULL) || | ||||
| (adt->flag & ADT_NLA_EVAL_OFF)) { | (adt->flag & ADT_NLA_EVAL_OFF)) { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| /* No remapping if editing an ordinary Replace action with full influence. */ | /* No remapping if editing an ordinary Replace action with full influence. */ | ||||
| if (!(adt->flag & ADT_NLA_EDIT_ON) && | if (!(adt->flag & ADT_NLA_EDIT_ON) && | ||||
| (adt->act_blendmode == NLASTRIP_MODE_REPLACE && adt->act_influence == 1.0f)) { | (adt->act_blendmode == NLASTRIP_MODE_REPLACE && adt->act_influence == 1.0f)) { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| /* Try to find a cached context. */ | /* Try to find a cached context. */ | ||||
| NlaKeyframingContext *ctx = BLI_findptr(cache, adt, offsetof(NlaKeyframingContext, adt)); | NlaKeyframingContext *ctx = BLI_findptr(cache, adt, offsetof(NlaKeyframingContext, adt)); | ||||
| if (ctx == NULL) { | if (ctx == NULL) { | ||||
| /* Allocate and evaluate a new context. */ | /* Allocate and evaluate a new context. */ | ||||
| ctx = MEM_callocN(sizeof(*ctx), "NlaKeyframingContext"); | ctx = MEM_callocN(sizeof(*ctx), "NlaKeyframingContext"); | ||||
| ctx->adt = adt; | ctx->adt = adt; | ||||
| nlaeval_init(&ctx->lower_eval_data); | nlaeval_init(&ctx->lower_eval_data); | ||||
| animsys_evaluate_nla( | animsys_evaluate_nla_for_keyframing(ptr, adt, anim_eval_context, ctx); | ||||
| &ctx->lower_eval_data, ptr, adt, anim_eval_context, flush_to_original, ctx); | |||||
| BLI_assert(ELEM(ctx->strip.act, NULL, adt->action)); | BLI_assert(ELEM(ctx->strip.act, NULL, adt->action)); | ||||
| BLI_addtail(cache, ctx); | BLI_addtail(cache, ctx); | ||||
| } | } | ||||
| return ctx; | return ctx; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 512 Lines • Show Last 20 Lines | |||||