/* SPDX-FileCopyrightText: 2009 Blender Authors, Joshua Leung. All rights reserved. * * SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup bke */ #include #include #include #include #include #include "MEM_guardedalloc.h" #include "ANIM_action.hh" #include "DNA_action_types.h" #include "DNA_anim_types.h" #include "DNA_curve_types.h" #include "BLI_easing.h" #include "BLI_ghash.h" #include "BLI_listbase.h" #include "BLI_math_vector.h" #include "BLI_math_vector_types.hh" #include "BLI_rect.h" #include "BLI_sort_utils.h" #include "BLI_string.h" #include "BLI_string_utils.hh" #include "BLI_task.hh" #include "BLI_vector_set.hh" #include "BLT_translation.hh" #include "BKE_anim_data.hh" #include "BKE_animsys.h" #include "BKE_context.hh" #include "BKE_curve.hh" #include "BKE_fcurve.hh" #include "BKE_fcurve_driver.h" #include "BKE_global.hh" #include "BKE_idprop.hh" #include "BKE_lib_query.hh" #include "BKE_nla.hh" #include "BLO_read_write.hh" #include "RNA_access.hh" #include "RNA_path.hh" #include "CLG_log.h" namespace blender { #define SMALL -1.0e-10 #define SELECT 1 static CLG_LogRef LOG = {"anim.fcurve"}; /* -------------------------------------------------------------------- */ /** \name F-Curve Data Create * \{ */ FCurve *BKE_fcurve_create() { FCurve *fcu = MEM_new(__func__); return fcu; } /** \} */ /* -------------------------------------------------------------------- */ /** \name F-Curve Data Free * \{ */ void BKE_fcurve_free(FCurve *fcu) { if (fcu == nullptr) { return; } /* Free curve data. */ MEM_SAFE_DELETE(fcu->bezt); MEM_SAFE_DELETE(fcu->fpt); /* Free RNA-path, as this were allocated when getting the path string. */ MEM_SAFE_DELETE(fcu->rna_path); /* Free extra data - i.e. modifiers, and driver. */ fcurve_free_driver(fcu); free_fmodifiers(&fcu->modifiers); /* Free the f-curve itself. */ MEM_delete(fcu); } void BKE_fcurves_free(ListBaseT *list) { /* Sanity check. */ if (list == nullptr) { return; } /* Free data, no need to call #BLI_remlink before freeing each curve, * as we store reference to next, and freeing only touches the curve it's given. */ FCurve *fcn = nullptr; for (FCurve *fcu = static_cast(list->first); fcu; fcu = fcn) { fcn = fcu->next; BKE_fcurve_free(fcu); } /* Clear pointers just in case. */ list->clear_no_delete(); } /** \} */ /* -------------------------------------------------------------------- */ /** \name F-Curve Data Copy * \{ */ FCurve *BKE_fcurve_copy(const FCurve *fcu) { /* Sanity check. */ if (fcu == nullptr) { return nullptr; } /* Make a copy. */ FCurve *fcu_d = MEM_dupalloc(fcu); fcu_d->next = fcu_d->prev = nullptr; fcu_d->grp = nullptr; /* Copy curve data. */ fcu_d->bezt = MEM_dupalloc(fcu_d->bezt); fcu_d->fpt = MEM_dupalloc(fcu_d->fpt); /* Copy rna-path. */ fcu_d->rna_path = MEM_dupalloc(fcu_d->rna_path); /* Copy driver. */ fcu_d->driver = fcurve_copy_driver(fcu_d->driver); /* Copy modifiers. */ copy_fmodifiers(&fcu_d->modifiers, &fcu->modifiers); /* Return new data. */ return fcu_d; } void BKE_fcurves_copy(ListBaseT *dst, ListBaseT *src) { /* Sanity checks. */ if (ELEM(nullptr, dst, src)) { return; } /* Clear destination list first. */ dst->clear_no_delete(); /* Copy one-by-one. */ for (FCurve &sfcu : *src) { FCurve *dfcu = BKE_fcurve_copy(&sfcu); BLI_addtail(dst, dfcu); } } void BKE_fcurve_rnapath_set(FCurve &fcu, StringRef rna_path) { MEM_SAFE_DELETE(fcu.rna_path); fcu.rna_path = BLI_strdupn(rna_path.data(), rna_path.size()); } void BKE_fmodifier_name_set(FModifier *fcm, const char *name) { /* Copy new Modifier name. */ STRNCPY(fcm->name, name); /* Set default modifier name when name parameter is an empty string. * Ensure the name is unique. */ const FModifierTypeInfo *fmi = get_fmodifier_typeinfo(fcm->type); ListBaseT list = {fcm, fcm}; BLI_uniquename(&list, fcm, CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, fmi->name), '.', offsetof(FModifier, name), sizeof(fcm->name)); } void BKE_fmodifier_ensure_flag(ListBaseT *modifiers) { for (FModifier &fcm : *modifiers) { const FModifierTypeInfo *fmi = get_fmodifier_typeinfo(fcm.type); if (fmi && fmi->requires_flag & FMI_REQUIRES_ORIGINAL_DATA) { SET_FLAG_FROM_TEST(fcm.flag, &fcm != modifiers->first, FMODIFIER_FLAG_DISABLED); } } } void BKE_fcurve_foreach_id(FCurve *fcu, LibraryForeachIDData *data) { ChannelDriver *driver = fcu->driver; if (driver != nullptr) { for (DriverVar &dvar : driver->variables) { /* only used targets */ DRIVER_TARGETS_USED_LOOPER_BEGIN (&dvar) { BKE_LIB_FOREACHID_PROCESS_ID(data, dtar->id, IDWALK_CB_NOP); } DRIVER_TARGETS_LOOPER_END; } } } /* ----------------- Finding F-Curves -------------------------- */ FCurve *id_data_find_fcurve( ID *id, void *data, StructRNA *type, const char *prop_name, int index, bool *r_driven) { /* Anim vars */ AnimData *adt = BKE_animdata_from_id(id); /* Rna vars */ PropertyRNA *prop; if (r_driven) { *r_driven = false; } /* Only use the current action ??? */ if (ELEM(nullptr, adt, adt->action)) { return nullptr; } PointerRNA ptr = RNA_pointer_create_discrete(id, type, data); prop = RNA_struct_find_property(&ptr, prop_name); if (prop == nullptr) { return nullptr; } const std::optional path = RNA_path_from_ID_to_property(&ptr, prop); if (!path) { return nullptr; } /* FIXME: The way drivers are handled here (always nullptr-ifying `fcu`) is very weird, this * needs to be re-checked I think?. */ bool is_driven = false; FCurve *fcu = BKE_animadata_fcurve_find_by_rna_path( adt, path->c_str(), index, nullptr, &is_driven); if (is_driven) { if (r_driven != nullptr) { *r_driven = is_driven; } fcu = nullptr; } return fcu; } FCurve *BKE_fcurve_find(ListBaseT *list, const char rna_path[], const int array_index) { /* Sanity checks. */ if (ELEM(nullptr, list, rna_path) || array_index < 0) { return nullptr; } /* Check paths of curves, then array indices... */ for (FCurve &fcu : *list) { /* Check indices first, much cheaper than a string comparison. */ /* Simple string-compare (this assumes that they have the same root...) */ if (UNLIKELY(fcu.array_index == array_index && fcu.rna_path && fcu.rna_path[0] == rna_path[0] && STREQ(fcu.rna_path, rna_path))) { return &fcu; } } return nullptr; } /** \} */ /* -------------------------------------------------------------------- */ /** \name FCurve Iteration * \{ */ FCurve *BKE_fcurve_iter_step(FCurve *fcu_iter, const char rna_path[]) { /* Sanity checks. */ if (ELEM(nullptr, fcu_iter, rna_path)) { return nullptr; } /* Check paths of curves, then array indices... */ for (FCurve *fcu = fcu_iter; fcu; fcu = fcu->next) { /* Simple string-compare (this assumes that they have the same root...) */ if (fcu->rna_path && STREQ(fcu->rna_path, rna_path)) { return fcu; } } return nullptr; } FCurve *BKE_animadata_fcurve_find_by_rna_path( AnimData *animdata, const char *rna_path, int rna_index, bAction **r_action, bool *r_driven) { if (r_driven != nullptr) { *r_driven = false; } if (r_action != nullptr) { *r_action = nullptr; } FCurve *fcurve = animrig::fcurve_find_in_action_slot( animdata->action, animdata->slot_handle, {rna_path, rna_index}); if (fcurve) { /* Action takes priority over drivers. */ if (r_action) { *r_action = animdata->action; } return fcurve; } /* If not animated, check if driven. */ const bool has_drivers = !animdata->drivers.is_empty(); if (has_drivers) { FCurve *fcu = BKE_fcurve_find(&animdata->drivers, rna_path, rna_index); if (fcu != nullptr) { if (r_driven != nullptr) { *r_driven = true; } return fcu; } } return nullptr; } FCurve *BKE_fcurve_find_by_rna(PointerRNA *ptr, PropertyRNA *prop, int rnaindex, AnimData **r_adt, bAction **r_action, bool *r_driven, bool *r_special) { return BKE_fcurve_find_by_rna_context_ui( nullptr, ptr, prop, rnaindex, r_adt, r_action, r_driven, r_special); } FCurve *BKE_fcurve_find_by_rna_context_ui(bContext * /*C*/, const PointerRNA *ptr, PropertyRNA *prop, int rnaindex, AnimData **r_animdata, bAction **r_action, bool *r_driven, bool *r_special) { if (r_animdata != nullptr) { *r_animdata = nullptr; } if (r_action != nullptr) { *r_action = nullptr; } if (r_driven != nullptr) { *r_driven = false; } if (r_special) { *r_special = false; } /* Special case for NLA Control Curves... */ if (BKE_nlastrip_has_curves_for_property(ptr, prop)) { NlaStrip *strip = static_cast(ptr->data); /* Set the special flag, since it cannot be a normal action/driver * if we've been told to start looking here... */ if (r_special) { *r_special = true; } if (r_driven) { *r_driven = false; } if (r_animdata) { *r_animdata = nullptr; } if (r_action) { *r_action = nullptr; } /* The F-Curve either exists or it doesn't here... */ return BKE_fcurve_find(&strip->fcurves, RNA_property_identifier(prop), rnaindex); } /* There must be some RNA-pointer + property combo. */ if (!prop || !ptr->owner_id || !RNA_property_animateable(ptr, prop)) { return nullptr; } AnimData *adt = BKE_animdata_from_id(ptr->owner_id); if (adt == nullptr) { return nullptr; } /* XXX This function call can become a performance bottleneck. */ const std::optional rna_path = RNA_path_from_ID_to_property(ptr, prop); if (!rna_path) { return nullptr; } /* Standard F-Curve from animdata - Animation (Action) or Drivers. */ FCurve *fcu = BKE_animadata_fcurve_find_by_rna_path( adt, rna_path->c_str(), rnaindex, r_action, r_driven); if (fcu != nullptr && r_animdata != nullptr) { *r_animdata = adt; } return fcu; } /** \} */ /* -------------------------------------------------------------------- */ /** \name Finding Keyframes/Extents * \{ */ /* Binary search algorithm for finding where to insert BezTriple, * with optional argument for precision required. * Returns the index to insert at (data already at that index will be offset if replace is 0) */ static int BKE_fcurve_bezt_binarysearch_index_ex(const BezTriple array[], const float frame, const int arraylen, const float threshold, bool *r_replace) { int start = 0, end = arraylen; /* Initialize replace-flag first. */ *r_replace = false; /* Sneaky optimizations (don't go through searching process if...): * - Keyframe to be added is to be added out of current bounds. * - Keyframe to be added would replace one of the existing ones on bounds. */ if (arraylen <= 0 || array == nullptr) { CLOG_WARN(&LOG, "encountered invalid array"); return 0; } /* Check whether to add before/after/on. */ /* 'First' Keyframe (when only one keyframe, this case is used) */ float framenum = array[0].vec[1][0]; if (IS_EQT(frame, framenum, threshold)) { *r_replace = true; return 0; } if (frame < framenum) { return 0; } /* 'Last' Keyframe */ framenum = array[(arraylen - 1)].vec[1][0]; if (IS_EQT(frame, framenum, threshold)) { *r_replace = true; return (arraylen - 1); } if (frame > framenum) { return arraylen; } while (start <= end) { /* Compute and get midpoint. */ /* We calculate the midpoint this way to avoid int overflows... */ const int mid = start + ((end - start) / 2); const float midfra = array[mid].vec[1][0]; /* Check if exactly equal to midpoint. */ if (IS_EQT(frame, midfra, threshold)) { *r_replace = true; return mid; } /* Repeat in upper/lower half. */ if (frame > midfra) { start = mid + 1; } else { end = mid - 1; } } /* Not found, so return where to place it. */ return start; } int BKE_fcurve_bezt_binarysearch_index(const BezTriple array[], const float frame, const int arraylen, bool *r_replace) { /* This is just a wrapper which uses the default threshold. */ return BKE_fcurve_bezt_binarysearch_index_ex( array, frame, arraylen, BEZT_BINARYSEARCH_THRESH, r_replace); } /* ...................................... */ /** * Get the first and last index to the bezt array that satisfies the given parameters. * * \param selected_keys_only: Only accept indices of bezt that are selected. * Is a subset of frame_range. * \param frame_range: Only consider keyframes in that frame interval. Can be nullptr. */ static bool get_bounding_bezt_indices(const FCurve *fcu, const bool selected_keys_only, const float frame_range[2], int *r_first, int *r_last) { /* Sanity checks. */ if (fcu->bezt == nullptr) { return false; } *r_first = 0; *r_last = fcu->totvert - 1; bool found = false; if (frame_range != nullptr) { /* If a range is passed in find the first and last keyframe within that range. */ bool replace = false; *r_first = BKE_fcurve_bezt_binarysearch_index( fcu->bezt, frame_range[0], fcu->totvert, &replace); *r_last = BKE_fcurve_bezt_binarysearch_index( fcu->bezt, frame_range[1], fcu->totvert, &replace); /* If first and last index are the same, no keyframes were found in the range. */ if (*r_first == *r_last) { return false; } /* The binary search returns an index where a keyframe would be inserted, * so it needs to be clamped to ensure it is in range of the array. */ *r_first = clamp_i(*r_first, 0, fcu->totvert - 1); *r_last = clamp_i(*r_last - 1, 0, fcu->totvert - 1); } /* Only include selected items? */ if (selected_keys_only) { /* Find first selected. */ for (int i = *r_first; i <= *r_last; i++) { BezTriple *bezt = &fcu->bezt[i]; if (BEZT_ISSEL_ANY(bezt)) { *r_first = i; found = true; break; } } /* Find last selected. */ for (int i = *r_last; i >= *r_first; i--) { BezTriple *bezt = &fcu->bezt[i]; if (BEZT_ISSEL_ANY(bezt)) { *r_last = i; found = true; break; } } } else { found = true; } return found; } static void calculate_bezt_bounds_x(BezTriple *bezt_array, const int index_range[2], const bool include_handles, float *r_min, float *r_max) { *r_min = bezt_array[index_range[0]].vec[1][0]; *r_max = bezt_array[index_range[1]].vec[1][0]; if (include_handles) { /* Need to check all handles because they might extend beyond their neighboring keys. */ for (int i = index_range[0]; i <= index_range[1]; i++) { const BezTriple *bezt = &bezt_array[i]; *r_min = std::min({*r_min, bezt->vec[0][0], bezt->vec[1][0]}); *r_max = std::max({*r_max, bezt->vec[1][0], bezt->vec[2][0]}); } } } static void calculate_bezt_bounds_y(BezTriple *bezt_array, const int index_range[2], const bool selected_keys_only, const bool include_handles, float *r_min, float *r_max) { *r_min = bezt_array[index_range[0]].vec[1][1]; *r_max = bezt_array[index_range[0]].vec[1][1]; for (int i = index_range[0]; i <= index_range[1]; i++) { const BezTriple *bezt = &bezt_array[i]; if (selected_keys_only && !BEZT_ISSEL_ANY(bezt)) { continue; } *r_min = min_ff(*r_min, bezt->vec[1][1]); *r_max = max_ff(*r_max, bezt->vec[1][1]); if (include_handles) { *r_min = std::min({*r_min, bezt->vec[0][1], bezt->vec[2][1]}); *r_max = std::max({*r_max, bezt->vec[0][1], bezt->vec[2][1]}); } } } static bool calculate_bezt_bounds(const FCurve *fcu, const bool selected_keys_only, const bool include_handles, const float frame_range[2], rctf *r_bounds) { int index_range[2]; const bool found_indices = get_bounding_bezt_indices( fcu, selected_keys_only, frame_range, &index_range[0], &index_range[1]); if (!found_indices) { return false; } calculate_bezt_bounds_x( fcu->bezt, index_range, include_handles, &r_bounds->xmin, &r_bounds->xmax); calculate_bezt_bounds_y(fcu->bezt, index_range, selected_keys_only, include_handles, &r_bounds->ymin, &r_bounds->ymax); return true; } static bool calculate_fpt_bounds(const FCurve *fcu, const float frame_range[2], rctf *r_bounds) { r_bounds->xmin = INFINITY; r_bounds->xmax = -INFINITY; r_bounds->ymin = INFINITY; r_bounds->ymax = -INFINITY; const int first_index = 0; const int last_index = fcu->totvert - 1; int start_index = first_index; int end_index = last_index; if (frame_range != nullptr) { /* Start index can be calculated because fpt has a key on every full frame. */ const float start_index_f = frame_range[0] - fcu->fpt[0].vec[0]; const float end_index_f = start_index_f + frame_range[1] - frame_range[0]; if (start_index_f > fcu->totvert - 1 || end_index_f < 0) { /* Range is outside of keyframe samples. */ return false; } /* Range might be partially covering keyframe samples. */ start_index = clamp_i(start_index_f, 0, fcu->totvert - 1); end_index = clamp_i(end_index_f, 0, fcu->totvert - 1); } /* X range can be directly calculated from end verts. */ r_bounds->xmin = fcu->fpt[start_index].vec[0]; r_bounds->xmax = fcu->fpt[end_index].vec[0]; for (int i = start_index; i <= end_index; i++) { r_bounds->ymin = min_ff(r_bounds->ymin, fcu->fpt[i].vec[1]); r_bounds->ymax = max_ff(r_bounds->ymax, fcu->fpt[i].vec[1]); } return BLI_rctf_is_valid(r_bounds); } bool BKE_fcurve_calc_bounds(const FCurve *fcu, const bool selected_keys_only, const bool include_handles, const float frame_range[2], rctf *r_bounds) { if (fcu->totvert == 0) { return false; } if (fcu->bezt) { const bool found_bounds = calculate_bezt_bounds( fcu, selected_keys_only, include_handles, frame_range, r_bounds); return found_bounds; } if (fcu->fpt) { const bool founds_bounds = calculate_fpt_bounds(fcu, frame_range, r_bounds); return founds_bounds; } return false; } bool BKE_fcurve_calc_range(const FCurve *fcu, float *r_min, float *r_max, const bool selected_keys_only) { float min = 0.0f; float max = 0.0f; bool foundvert = false; if (fcu->totvert == 0) { return false; } if (fcu->bezt) { int index_range[2]; foundvert = get_bounding_bezt_indices( fcu, selected_keys_only, nullptr, &index_range[0], &index_range[1]); if (!foundvert) { return false; } const bool include_handles = false; calculate_bezt_bounds_x(fcu->bezt, index_range, include_handles, &min, &max); } else if (fcu->fpt) { min = fcu->fpt[0].vec[0]; max = fcu->fpt[fcu->totvert - 1].vec[0]; foundvert = true; } *r_min = min; *r_max = max; return foundvert; } float *BKE_fcurves_calc_keyed_frames_ex(FCurve **fcurve_array, int fcurve_array_len, const float interval, int *r_frames_len) { /* Use `1e-3f` as the smallest possible value since these are converted to integers * and we can be sure `MAXFRAME / 1e-3f < INT_MAX` as it's around half the size. */ const double interval_db = max_ff(interval, 1e-3f); VectorSet frames_unique; for (int fcurve_index = 0; fcurve_index < fcurve_array_len; fcurve_index++) { const FCurve *fcu = fcurve_array[fcurve_index]; for (int i = 0; i < fcu->totvert; i++) { const BezTriple *bezt = &fcu->bezt[i]; const double value = round(double(bezt->vec[1][0]) / interval_db); BLI_assert(value > INT_MIN && value < INT_MAX); frames_unique.add(int(value)); } } const size_t frames_len = frames_unique.size(); float *frames = MEM_new_array_uninitialized(frames_len, __func__); for (const int i : frames_unique.index_range()) { const int value = frames_unique[i]; frames[i] = double(value) * interval_db; } qsort(frames, frames_len, sizeof(*frames), BLI_sortutil_cmp_float); *r_frames_len = frames_len; return frames; } float *BKE_fcurves_calc_keyed_frames(FCurve **fcurve_array, int fcurve_array_len, int *r_frames_len) { return BKE_fcurves_calc_keyed_frames_ex(fcurve_array, fcurve_array_len, 1.0f, r_frames_len); } /** \} */ /* -------------------------------------------------------------------- */ /** \name Active Keyframe * \{ */ void BKE_fcurve_active_keyframe_set(FCurve *fcu, const BezTriple *active_bezt) { if (active_bezt == nullptr) { fcu->active_keyframe_index = FCURVE_ACTIVE_KEYFRAME_NONE; return; } /* Gracefully handle out-of-bounds pointers. Ideally this would do a BLI_assert() as well, but * then the unit tests would break in debug mode. */ const ptrdiff_t offset = active_bezt - fcu->bezt; if (offset < 0 || offset >= fcu->totvert) { fcu->active_keyframe_index = FCURVE_ACTIVE_KEYFRAME_NONE; return; } /* The active keyframe should always be selected. */ BLI_assert_msg(BEZT_ISSEL_ANY(active_bezt), "active keyframe must be selected"); fcu->active_keyframe_index = int(offset); } int BKE_fcurve_active_keyframe_index(const FCurve *fcu) { const int active_keyframe_index = fcu->active_keyframe_index; /* Array access boundary checks. */ if (fcu->bezt == nullptr || active_keyframe_index >= fcu->totvert || active_keyframe_index < 0) { return FCURVE_ACTIVE_KEYFRAME_NONE; } const BezTriple *active_bezt = &fcu->bezt[active_keyframe_index]; if (((active_bezt->f1 | active_bezt->f2 | active_bezt->f3) & SELECT) == 0) { /* The active keyframe should always be selected. If it's not selected, it can't be active. */ return FCURVE_ACTIVE_KEYFRAME_NONE; } return active_keyframe_index; } /** \} */ void BKE_fcurve_keyframe_move_time_with_handles(BezTriple *keyframe, const float new_time) { const float time_delta = new_time - keyframe->vec[1][0]; keyframe->vec[0][0] += time_delta; keyframe->vec[1][0] = new_time; keyframe->vec[2][0] += time_delta; } void BKE_fcurve_keyframe_move_value_with_handles(BezTriple *keyframe, const float new_value) { const float value_delta = new_value - keyframe->vec[1][1]; keyframe->vec[0][1] += value_delta; keyframe->vec[1][1] = new_value; keyframe->vec[2][1] += value_delta; } /* -------------------------------------------------------------------- */ /** \name Status Checks * \{ */ bool BKE_fcurve_are_keyframes_usable(const FCurve &fcu) { /* F-Curve must not have samples - samples are mutually exclusive of keyframes. */ if (fcu.fpt) { return false; } /* If it has modifiers, none of these should "drastically" alter the curve. */ if (fcu.modifiers.first) { /* Check modifiers from last to first, as last will be more influential. */ /* TODO: optionally, only check modifier if it is the active one... (Joshua Leung 2010) */ for (const FModifier &fcm : fcu.modifiers.items_reversed()) { /* Ignore if muted/disabled. */ if (fcm.flag & (FMODIFIER_FLAG_DISABLED | FMODIFIER_FLAG_MUTED)) { continue; } /* Type checks. */ switch (fcm.type) { /* Clearly harmless - do nothing. */ case FMODIFIER_TYPE_CYCLES: case FMODIFIER_TYPE_STEPPED: case FMODIFIER_TYPE_NOISE: break; /* Sometimes harmful - depending on whether they're "additive" or not. */ case FMODIFIER_TYPE_GENERATOR: { FMod_Generator *data = static_cast(fcm.data); if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) { return false; } break; } case FMODIFIER_TYPE_FN_GENERATOR: { FMod_FunctionGenerator *data = static_cast(fcm.data); if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) { return false; } break; } /* Always harmful - cannot allow. */ default: return false; } } } /* Keyframes are usable. */ return true; } bool BKE_fcurve_is_protected(const FCurve &fcu) { return ((fcu.flag & FCURVE_PROTECTED) || (fcu.grp && (fcu.grp->flag & AGRP_PROTECTED))); } bool BKE_fcurve_has_selected_control_points(const FCurve &fcu) { int i; BezTriple *bezt; for (bezt = fcu.bezt, i = 0; i < fcu.totvert; ++i, ++bezt) { if ((bezt->f2 & SELECT) != 0) { return true; } } return false; } void BKE_fcurve_deselect_all_keys(FCurve &fcu) { if (!fcu.bezt) { return; } for (int i = 0; i < fcu.totvert; i++) { BEZT_DESEL_ALL(&fcu.bezt[i]); } } bool BKE_fcurve_is_keyframable(const FCurve &fcu) { /* F-Curve's keyframes must be "usable" (i.e. visible + have an effect on final result) */ if (BKE_fcurve_are_keyframes_usable(fcu) == 0) { return false; } /* F-Curve must currently be editable too. */ if (BKE_fcurve_is_protected(fcu)) { return false; } /* F-Curve is keyframable. */ return true; } /** \} */ /* -------------------------------------------------------------------- */ /** \name Samples Utilities * \{ */ /* Some utilities for working with FPoints (i.e. 'sampled' animation curve data, such as * data imported from BVH/motion-capture files), which are specialized for use with high density * datasets, which BezTriples/Keyframe data are ill equipped to do. */ float fcurve_samplingcb_evalcurve(FCurve *fcu, void * /*data*/, float evaltime) { /* Assume any interference from drivers on the curve is intended... */ return evaluate_fcurve(fcu, evaltime); } void fcurve_store_samples(FCurve *fcu, void *data, int start, int end, FcuSampleFunc sample_cb) { /* Sanity checks. */ /* TODO: make these tests report errors using reports not CLOG's (Joshua Leung 2009) */ if (ELEM(nullptr, fcu, sample_cb)) { CLOG_ERROR(&LOG, "No F-Curve with F-Curve Modifiers to Bake"); return; } if (start > end) { CLOG_ERROR(&LOG, "Error: Frame range for Sampled F-Curve creation is inappropriate"); return; } /* Set up sample data. */ FPoint *new_fpt; FPoint *fpt = new_fpt = MEM_new_array_zeroed((end - start + 1), "FPoint Samples"); /* Use the sampling callback at 1-frame intervals from start to end frames. */ for (int cfra = start; cfra <= end; cfra++, fpt++) { fpt->vec[0] = float(cfra); fpt->vec[1] = sample_cb(fcu, data, float(cfra)); } /* Free any existing sample/keyframe data on curve. */ if (fcu->bezt) { MEM_delete(fcu->bezt); } if (fcu->fpt) { MEM_delete(fcu->fpt); } /* Store the samples. */ fcu->bezt = nullptr; fcu->fpt = new_fpt; fcu->totvert = end - start + 1; } static void init_unbaked_bezt_data(BezTriple *bezt) { bezt->f1 = bezt->f2 = bezt->f3 = BEZT_FLAG_SELECT; /* Baked FCurve points always use linear interpolation. */ bezt->ipo = BEZT_IPO_LIN; bezt->h1 = bezt->h2 = HD_AUTO_ANIM; } void fcurve_samples_to_keyframes(FCurve *fcu, const int start, const int end) { /* Sanity checks. */ /* TODO: make these tests report errors using reports not CLOG's (Joshua Leung 2009). */ if (fcu == nullptr) { CLOG_ERROR(&LOG, "No F-Curve with F-Curve Modifiers to Un-Bake"); return; } if (start > end) { CLOG_ERROR(&LOG, "Error: Frame range to unbake F-Curve is inappropriate"); return; } if (fcu->fpt == nullptr) { /* No data to unbake. */ CLOG_ERROR(&LOG, "Error: Curve contains no baked keyframes"); return; } /* Free any existing sample/keyframe data on the curve. */ if (fcu->bezt) { MEM_delete(fcu->bezt); } FPoint *fpt = fcu->fpt; int keyframes_to_insert = end - start; int sample_points = fcu->totvert; BezTriple *bezt = fcu->bezt = MEM_new_array_zeroed(keyframes_to_insert, __func__); fcu->totvert = keyframes_to_insert; /* Get first sample point to 'copy' as keyframe. */ for (; sample_points && (fpt->vec[0] < start); fpt++, sample_points--) { /* pass */ } /* Current position in the timeline. */ int cur_pos = start; /* Add leading dummy flat points if needed. */ for (; keyframes_to_insert && (fpt->vec[0] > start); cur_pos++, bezt++, keyframes_to_insert--) { init_unbaked_bezt_data(bezt); bezt->vec[1][0] = float(cur_pos); bezt->vec[1][1] = fpt->vec[1]; } /* Copy actual sample points. */ for (; keyframes_to_insert && sample_points; cur_pos++, bezt++, keyframes_to_insert--, fpt++, sample_points--) { init_unbaked_bezt_data(bezt); copy_v2_v2(bezt->vec[1], fpt->vec); } /* Add trailing dummy flat points if needed. */ for (fpt--; keyframes_to_insert; cur_pos++, bezt++, keyframes_to_insert--) { init_unbaked_bezt_data(bezt); bezt->vec[1][0] = float(cur_pos); bezt->vec[1][1] = fpt->vec[1]; } MEM_SAFE_DELETE(fcu->fpt); /* Not strictly needed since we use linear interpolation, but better be consistent here. */ BKE_fcurve_handles_recalc(*fcu); } /* ***************************** F-Curve Sanity ********************************* */ /* The functions here are used in various parts of Blender, usually after some editing * of keyframe data has occurred. They ensure that keyframe data is properly ordered and * that the handles are correct. */ eFCU_Cycle_Type BKE_fcurve_get_cycle_type(const FCurve &fcu) { FModifier *fcm = static_cast(fcu.modifiers.first); if (!fcm || fcm->type != FMODIFIER_TYPE_CYCLES) { return FCU_CYCLE_NONE; } if (fcm->flag & (FMODIFIER_FLAG_DISABLED | FMODIFIER_FLAG_MUTED)) { return FCU_CYCLE_NONE; } if (fcm->flag & (FMODIFIER_FLAG_RANGERESTRICT | FMODIFIER_FLAG_USEINFLUENCE)) { return FCU_CYCLE_NONE; } FMod_Cycles *data = static_cast(fcm->data); 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; } bool BKE_fcurve_is_cyclic(const 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. * When 'to' and 'from' are end points of the loop, this moves the 'in' point one loop cycle. */ static BezTriple *cycle_offset_triple( bool cycle, BezTriple *out, const BezTriple *in, const BezTriple *from, const BezTriple *to) { if (!cycle) { return nullptr; } memcpy(out, in, sizeof(BezTriple)); float delta[3]; sub_v3_v3v3(delta, to->vec[1], from->vec[1]); for (int i = 0; i < 3; i++) { add_v3_v3(out->vec[i], delta); } return out; } void BKE_fcurve_handles_recalc_ex(FCurve &fcu, const eBezTriple_Flag handle_sel_flag) { /* Error checking: * - Need at least two points. * - Need bezier keys. * - Only bezier-interpolation has handles (for now). */ if (fcu.bezt == nullptr || (fcu.totvert < 2) /* || ELEM(fcu->ipo, BEZT_IPO_CONST, BEZT_IPO_LIN) */) { return; } /* If the first modifier is Cycles, smooth the curve through the cycle. */ BezTriple *first = &fcu.bezt[0]; BezTriple *last = &fcu.bezt[fcu.totvert - 1]; const bool cycle = BKE_fcurve_is_cyclic(fcu) && BEZT_IS_AUTOH(first) && BEZT_IS_AUTOH(last); threading::parallel_for(IndexRange(fcu.totvert), 256, [&](const IndexRange range) { BezTriple tmp; for (const int i : range) { BezTriple *bezt = &fcu.bezt[i]; BezTriple *prev = nullptr; BezTriple *next = nullptr; if (i > 0) { prev = (bezt - 1); } else { prev = cycle_offset_triple(cycle, &tmp, &fcu.bezt[fcu.totvert - 2], last, first); } if (i < fcu.totvert - 1) { next = (bezt + 1); } else { next = cycle_offset_triple(cycle, &tmp, &fcu.bezt[1], first, last); } /* Clamp timing of handles to be on either side of beztriple. The threshold with * increment/decrement ulp ensures that the handle length doesn't reach 0 at which point * there would be no way to ensure that handles stay aligned. This adds an issue where if a * handle is scaled to 0, the other side is set to be horizontal. * See #141029 for more info. */ const float threshold = 0.001; CLAMP_MAX(bezt->vec[0][0], decrement_ulp(bezt->vec[1][0] - threshold)); CLAMP_MIN(bezt->vec[2][0], increment_ulp(bezt->vec[1][0] + threshold)); /* Calculate auto-handles. */ BKE_nurb_handle_calc_ex(bezt, prev, next, handle_sel_flag, true, fcu.auto_smoothing); /* For automatic ease in and out. */ if (BEZT_IS_AUTOH(bezt) && !cycle) { /* Only do this on first or last beztriple. */ if (ELEM(i, 0, fcu.totvert - 1)) { /* Set both handles to have same horizontal value as keyframe. */ if (fcu.extend == FCURVE_EXTRAPOLATE_CONSTANT) { bezt->vec[0][1] = bezt->vec[2][1] = bezt->vec[1][1]; /* Remember that these keyframes are special, they don't need to be adjusted. */ bezt->auto_handle_type = HD_AUTOTYPE_LOCKED_FINAL; } } } /* Avoid total smoothing failure on duplicate keyframes (can happen during grab). */ if (prev && prev->vec[1][0] >= bezt->vec[1][0]) { prev->auto_handle_type = bezt->auto_handle_type = HD_AUTOTYPE_LOCKED_FINAL; } } }); /* If cyclic extrapolation and Auto Clamp has triggered, ensure it is symmetric. */ if (cycle && (first->auto_handle_type != HD_AUTOTYPE_NORMAL || last->auto_handle_type != HD_AUTOTYPE_NORMAL)) { first->vec[0][1] = first->vec[2][1] = first->vec[1][1]; last->vec[0][1] = last->vec[2][1] = last->vec[1][1]; first->auto_handle_type = last->auto_handle_type = HD_AUTOTYPE_LOCKED_FINAL; } /* Do a second pass for auto handle: compute the handle to have 0 acceleration step. */ if (fcu.auto_smoothing != FCURVE_SMOOTH_NONE) { BKE_nurb_handle_smooth_fcurve(fcu.bezt, fcu.totvert, cycle); } } void BKE_fcurve_update_handle_flag_from_opposite(BezTriple &key, const HandleSide source_side) { eBezTriple_Handle source; eBezTriple_Handle *target; switch (source_side) { case HandleSide::LEFT: { source = eBezTriple_Handle(key.h1); target = &key.h2; break; } case HandleSide::RIGHT: { source = eBezTriple_Handle(key.h2); target = &key.h1; break; } } switch (source) { /* Need to ensure that both sides are the same. */ case HD_AUTO: case HD_ALIGN: case HD_AUTO_ANIM: case HD_ALIGN_DOUBLESIDE: *target = source; break; case HD_FREE: case HD_VECT: /* If the source was set to either of those, the handle has to be either free or vector. */ if (!ELEM(*target, HD_FREE, HD_VECT)) { *target = HD_FREE; } break; } } void BKE_fcurve_handles_recalc(FCurve &fcu) { BKE_fcurve_handles_recalc_ex(fcu, BEZT_FLAG_SELECT); } void testhandles_fcurve(FCurve *fcu, eBezTriple_Flag sel_flag, const bool use_handle) { /* Only beztriples have handles (bpoints don't though). */ if (ELEM(nullptr, fcu, fcu->bezt)) { return; } /* Loop over beztriples. */ BezTriple *bezt; uint a; for (a = 0, bezt = fcu->bezt; a < fcu->totvert; a++, bezt++) { BKE_nurb_bezt_handle_test( bezt, sel_flag, use_handle ? NURB_HANDLE_TEST_EACH : NURB_HANDLE_TEST_KNOT_ONLY, false); } /* Recalculate handles. */ BKE_fcurve_handles_recalc_ex(*fcu, sel_flag); } void sort_time_fcurve(FCurve &fcu) { if (fcu.bezt == nullptr) { return; } /* Keep adjusting order of beztriples until nothing moves (bubble-sort). */ BezTriple *bezt; uint a; bool ok = true; while (ok) { ok = false; /* Currently, will only be needed when there are beztriples. */ /* Loop over ALL points to adjust position in array and recalculate handles. */ for (a = 0, bezt = fcu.bezt; a < fcu.totvert; a++, bezt++) { /* Check if there's a next beztriple which we could try to swap with current. */ if (a < (fcu.totvert - 1)) { /* Swap if one is after the other (and indicate that order has changed). */ if (bezt->vec[1][0] > (bezt + 1)->vec[1][0]) { std::swap(*bezt, *(bezt + 1)); if (a == fcu.active_keyframe_index) { fcu.active_keyframe_index++; } else if (a + 1 == fcu.active_keyframe_index) { fcu.active_keyframe_index--; } ok = true; } } } } for (a = 0, bezt = fcu.bezt; a < fcu.totvert; a++, bezt++) { /* If either one of both of the points exceeds crosses over the keyframe time... */ if ((bezt->vec[0][0] > bezt->vec[1][0]) && (bezt->vec[2][0] < bezt->vec[1][0])) { /* Swap handles if they have switched sides for some reason. */ swap_v2_v2(bezt->vec[0], bezt->vec[2]); } else { /* Clamp handles. */ CLAMP_MAX(bezt->vec[0][0], bezt->vec[1][0]); CLAMP_MIN(bezt->vec[2][0], bezt->vec[1][0]); } } } bool test_time_fcurve(FCurve &fcu) { uint a; /* Currently, only need to test beztriples. */ if (fcu.bezt) { BezTriple *bezt; /* Loop through all BezTriples, stopping when one exceeds the one after it. */ for (a = 0, bezt = fcu.bezt; a < (fcu.totvert - 1); a++, bezt++) { if (bezt->vec[1][0] > (bezt + 1)->vec[1][0]) { return true; } } } else if (fcu.fpt) { FPoint *fpt; /* Loop through all FPoints, stopping when one exceeds the one after it. */ for (a = 0, fpt = fcu.fpt; a < (fcu.totvert - 1); a++, fpt++) { if (fpt->vec[0] > (fpt + 1)->vec[0]) { return true; } } } /* None need any swapping. */ return false; } /** \} */ /* -------------------------------------------------------------------- */ /** \name F-Curve Calculations * \{ */ void BKE_fcurve_correct_bezpart(const float v1[2], float v2[2], float v3[2], const float v4[2]) { float h1[2], h2[2], len1, len2, len, fac; /* Calculate handle deltas. */ h1[0] = v1[0] - v2[0]; h1[1] = v1[1] - v2[1]; h2[0] = v4[0] - v3[0]; h2[1] = v4[1] - v3[1]; /* Calculate distances: * - len = Span of time between keyframes. * - len1 = Length of handle of start key. * - len2 = Length of handle of end key. */ len = v4[0] - v1[0]; len1 = fabsf(h1[0]); len2 = fabsf(h2[0]); /* If the handles have no length, no need to do any corrections. */ if ((len1 + len2) == 0.0f) { return; } /* To prevent looping or rewinding, handles cannot * exceed the adjacent key-frames time position. */ if (len1 > len) { fac = len / len1; v2[0] = (v1[0] - fac * h1[0]); v2[1] = (v1[1] - fac * h1[1]); } if (len2 > len) { fac = len / len2; v3[0] = (v4[0] - fac * h2[0]); v3[1] = (v4[1] - fac * h2[1]); } } /** * Find roots of cubic equation (c0 + c1 x + c2 x^2 + c3 x^3) * \return number of roots in `o`. * * \note it is up to the caller to allocate enough memory for `o`. */ static int solve_cubic(double c0, double c1, double c2, double c3, float *o) { double a, b, c, p, q, d, t, phi; int nr = 0; if (c3 != 0.0) { a = c2 / c3; b = c1 / c3; c = c0 / c3; a = a / 3; p = b / 3 - a * a; q = (2 * a * a * a - a * b + c) / 2; d = q * q + p * p * p; if (d > 0.0) { t = sqrt(d); o[0] = float(sqrt3d(-q + t) + sqrt3d(-q - t) - a); if ((o[0] >= float(SMALL)) && (o[0] <= 1.000001f)) { return 1; } return 0; } if (d == 0.0) { t = sqrt3d(-q); o[0] = float(2 * t - a); if ((o[0] >= float(SMALL)) && (o[0] <= 1.000001f)) { nr++; } o[nr] = float(-t - a); if ((o[nr] >= float(SMALL)) && (o[nr] <= 1.000001f)) { return nr + 1; } return nr; } phi = acos(-q / sqrt(-(p * p * p))); t = sqrt(-p); p = cos(phi / 3); q = sqrt(3 - 3 * p * p); o[0] = float(2 * t * p - a); if ((o[0] >= float(SMALL)) && (o[0] <= 1.000001f)) { nr++; } o[nr] = float(-t * (p + q) - a); if ((o[nr] >= float(SMALL)) && (o[nr] <= 1.000001f)) { nr++; } o[nr] = float(-t * (p - q) - a); if ((o[nr] >= float(SMALL)) && (o[nr] <= 1.000001f)) { return nr + 1; } return nr; } a = c2; b = c1; c = c0; if (a != 0.0) { /* Discriminant */ p = b * b - 4 * a * c; if (p > 0) { p = sqrt(p); o[0] = float((-b - p) / (2 * a)); if ((o[0] >= float(SMALL)) && (o[0] <= 1.000001f)) { nr++; } o[nr] = float((-b + p) / (2 * a)); if ((o[nr] >= float(SMALL)) && (o[nr] <= 1.000001f)) { return nr + 1; } return nr; } if (p == 0) { o[0] = float(-b / (2 * a)); if ((o[0] >= float(SMALL)) && (o[0] <= 1.000001f)) { return 1; } } return 0; } if (b != 0.0) { o[0] = float(-c / b); if ((o[0] >= float(SMALL)) && (o[0] <= 1.000001f)) { return 1; } return 0; } if (c == 0.0) { o[0] = 0.0; return 1; } return 0; } /* Find root(s) ('zero') of a Bezier curve. */ static int findzero(float x, float q0, float q1, float q2, float q3, float *o) { const double c0 = q0 - x; const double c1 = 3.0f * (q1 - q0); const double c2 = 3.0f * (q0 - 2.0f * q1 + q2); const double c3 = q3 - q0 + 3.0f * (q1 - q2); return solve_cubic(c0, c1, c2, c3, o); } static void berekeny(float f1, float f2, float f3, float f4, float *o, int b) { float t, c0, c1, c2, c3; int a; c0 = f1; c1 = 3.0f * (f2 - f1); c2 = 3.0f * (f1 - 2.0f * f2 + f3); c3 = f4 - f1 + 3.0f * (f2 - f3); for (a = 0; a < b; a++) { t = o[a]; o[a] = c0 + t * c1 + t * t * c2 + t * t * t * c3; } } static void fcurve_bezt_free(FCurve &fcu) { MEM_SAFE_DELETE(fcu.bezt); fcu.totvert = 0; } bool BKE_fcurve_bezt_subdivide_handles(BezTriple *bezt, BezTriple *prev, BezTriple *next, float *r_pdelta) { /* The four points that make up this section of the Bezier curve. */ const float *prev_coords = prev->vec[1]; float *prev_handle_right = prev->vec[2]; float *next_handle_left = next->vec[0]; const float *next_coords = next->vec[1]; float *new_handle_left = bezt->vec[0]; const float *new_coords = bezt->vec[1]; float *new_handle_right = bezt->vec[2]; if (new_coords[0] <= prev_coords[0] || new_coords[0] >= next_coords[0]) { /* The new keyframe is outside the (prev_coords, next_coords) range. */ return false; } /* Apply evaluation-time limits and compute the effective curve. */ BKE_fcurve_correct_bezpart(prev_coords, prev_handle_right, next_handle_left, next_coords); float roots[4]; if (!findzero(new_coords[0], prev_coords[0], prev_handle_right[0], next_handle_left[0], next_coords[0], roots)) { return false; } const float t = roots[0]; /* Percentage of the curve at which the split should occur. */ if (t <= 0.0f || t >= 1.0f) { /* The split would occur outside the curve, which isn't possible. */ return false; } /* De Casteljau split, requires three iterations of splitting. * See https://pomax.github.io/bezierinfo/#decasteljau */ float split1[3][2], split2[2][2], split3[2]; interp_v2_v2v2(split1[0], prev_coords, prev_handle_right, t); interp_v2_v2v2(split1[1], prev_handle_right, next_handle_left, t); interp_v2_v2v2(split1[2], next_handle_left, next_coords, t); interp_v2_v2v2(split2[0], split1[0], split1[1], t); interp_v2_v2v2(split2[1], split1[1], split1[2], t); interp_v2_v2v2(split3, split2[0], split2[1], t); /* Update the existing handles. */ copy_v2_v2(prev_handle_right, split1[0]); copy_v2_v2(next_handle_left, split1[2]); float diff_coords[2]; sub_v2_v2v2(diff_coords, new_coords, split3); add_v2_v2v2(new_handle_left, split2[0], diff_coords); add_v2_v2v2(new_handle_right, split2[1], diff_coords); *r_pdelta = diff_coords[1]; return true; } void BKE_fcurve_bezt_resize(FCurve &fcu, const int new_totvert) { BLI_assert(new_totvert >= 0); /* No early return when new_totvert == fcu->totvert. There is no way to know the intention of the * caller, nor the history of the FCurve so far, so `fcu->bezt` may actually have allocated space * for more than `fcu->totvert` keys. */ if (new_totvert == 0) { fcurve_bezt_free(fcu); return; } fcu.bezt = static_cast( MEM_realloc_uninitialized(fcu.bezt, new_totvert * sizeof(*(fcu.bezt)))); /* Zero out all the newly-allocated beztriples. This is necessary, as it is likely that only some * of the fields will actually be updated by the caller. */ const int old_totvert = fcu.totvert; if (new_totvert > old_totvert) { std::fill_n(fcu.bezt + old_totvert, new_totvert - old_totvert, BezTriple{}); } fcu.totvert = new_totvert; } void BKE_fcurve_delete_key(FCurve *fcu, int index) { /* sanity check */ if (fcu == nullptr) { return; } /* verify the index: * 1) cannot be greater than the number of available keyframes * 2) negative indices are for specifying a value from the end of the array */ if (abs(index) >= fcu->totvert) { return; } if (index < 0) { index += fcu->totvert; } /* Delete this keyframe */ memmove( &fcu->bezt[index], &fcu->bezt[index + 1], sizeof(BezTriple) * (fcu->totvert - index - 1)); fcu->totvert--; /* Free the array of BezTriples if there are not keyframes */ if (fcu->totvert == 0) { fcurve_bezt_free(*fcu); } } void BKE_fcurve_delete_keys(FCurve &fcu, uint2 index_range) { BLI_assert(fcu.bezt != nullptr); BLI_assert(index_range[1] > index_range[0]); BLI_assert(index_range[1] <= fcu.totvert); const int removed_index_count = index_range[1] - index_range[0]; memmove(&fcu.bezt[index_range[0]], &fcu.bezt[index_range[1]], sizeof(BezTriple) * (fcu.totvert - index_range[1])); fcu.totvert -= removed_index_count; if (fcu.totvert == 0) { fcurve_bezt_free(fcu); } } BezTriple *BKE_bezier_array_merge( const BezTriple *a, const int size_a, const BezTriple *b, const int size_b, int *r_merged_size) { BezTriple *large_array = MEM_new_array_zeroed(size_t(size_a + size_b), "beztriple"); int iterator_a = 0; int iterator_b = 0; *r_merged_size = 0; /* For comparing if keyframes are at the same x-value. */ const int max_ulps = 32; while (iterator_a < size_a || iterator_b < size_b) { if (iterator_a >= size_a) { const int remaining_keys = size_b - iterator_b; memcpy(&large_array[*r_merged_size], &b[iterator_b], sizeof(BezTriple) * remaining_keys); (*r_merged_size) += remaining_keys; break; } if (iterator_b >= size_b) { const int remaining_keys = size_a - iterator_a; memcpy(&large_array[*r_merged_size], &a[iterator_a], sizeof(BezTriple) * remaining_keys); (*r_merged_size) += remaining_keys; break; } if (compare_ff_relative( a[iterator_a].vec[1][0], b[iterator_b].vec[1][0], BEZT_BINARYSEARCH_THRESH, max_ulps)) { memcpy(&large_array[*r_merged_size], &a[iterator_a], sizeof(BezTriple)); iterator_a++; iterator_b++; } else if (a[iterator_a].vec[1][0] < b[iterator_b].vec[1][0]) { memcpy(&large_array[*r_merged_size], &a[iterator_a], sizeof(BezTriple)); iterator_a++; } else { memcpy(&large_array[*r_merged_size], &b[iterator_b], sizeof(BezTriple)); iterator_b++; } (*r_merged_size)++; } BezTriple *minimal_array; if (*r_merged_size < size_a + size_b) { minimal_array = static_cast( MEM_realloc_uninitialized(large_array, sizeof(BezTriple) * (*r_merged_size))); } else { minimal_array = large_array; } return minimal_array; } bool BKE_fcurve_delete_keys_selected(FCurve &fcu) { if (fcu.bezt == nullptr) { /* ignore baked curves */ return false; } bool changed = false; /* Delete selected BezTriples */ for (int i = 0; i < fcu.totvert; i++) { if (fcu.bezt[i].f2 & SELECT) { if (i == fcu.active_keyframe_index) { BKE_fcurve_active_keyframe_set(&fcu, nullptr); } memmove(&fcu.bezt[i], &fcu.bezt[i + 1], sizeof(BezTriple) * (fcu.totvert - i - 1)); fcu.totvert--; i--; changed = true; } } /* Free the array of BezTriples if there are not keyframes */ if (fcu.totvert == 0) { fcurve_bezt_free(fcu); } return changed; } void BKE_fcurve_delete_keys_all(FCurve &fcu) { fcurve_bezt_free(fcu); } /* Time + Average value */ struct tRetainedKeyframe { tRetainedKeyframe *next, *prev; float frame; /* frame to cluster around */ float val; /* average value */ size_t tot_count; /* number of keyframes that have been averaged */ size_t del_count; /* number of keyframes of this sort that have been deleted so far */ }; void BKE_fcurve_merge_duplicate_keys(FCurve *fcu, const int sel_flag, const bool use_handle) { /* NOTE: We assume that all keys are sorted */ ListBaseT retained_keys = {nullptr, nullptr}; const bool can_average_points = ((fcu->flag & (FCURVE_INT_VALUES | FCURVE_DISCRETE_VALUES)) == 0); /* sanity checks */ if ((fcu->totvert == 0) || (fcu->bezt == nullptr)) { return; } /* 1) Identify selected keyframes, and average the values on those * in case there are collisions due to multiple keys getting scaled * to all end up on the same frame */ for (int i = 0; i < fcu->totvert; i++) { BezTriple *bezt = &fcu->bezt[i]; if (BEZT_ISSEL_ANY(bezt)) { bool found = false; /* If there's another selected frame here, merge it */ for (tRetainedKeyframe &rk : retained_keys.items_reversed()) { if (IS_EQT(rk.frame, bezt->vec[1][0], BEZT_BINARYSEARCH_THRESH)) { rk.val += bezt->vec[1][1]; rk.tot_count++; found = true; break; } if (rk.frame < bezt->vec[1][0]) { /* Terminate early if have passed the supposed insertion point? */ break; } } /* If nothing found yet, create a new one */ if (found == false) { tRetainedKeyframe *rk = MEM_new_zeroed("tRetainedKeyframe"); rk->frame = bezt->vec[1][0]; rk->val = bezt->vec[1][1]; rk->tot_count = 1; BLI_addtail(&retained_keys, rk); } } } if (retained_keys.is_empty()) { /* This may happen if none of the points were selected... */ if (G.debug & G_DEBUG) { printf("%s: nothing to do for FCurve %p (rna_path = '%s')\n", __func__, fcu, fcu->rna_path); } return; } /* Compute the average values for each retained keyframe */ for (tRetainedKeyframe &rk : retained_keys) { rk.val = rk.val / float(rk.tot_count); } /* 2) Delete all keyframes duplicating the "retained keys" found above * - Most of these will be unselected keyframes * - Some will be selected keyframes though. For those, we only keep the last one * (or else everything is gone), and replace its value with the averaged value. */ for (int i = fcu->totvert - 1; i >= 0; i--) { BezTriple *bezt = &fcu->bezt[i]; /* Is this keyframe a candidate for deletion? */ /* TODO: Replace loop with an O(1) lookup instead */ for (tRetainedKeyframe &rk : retained_keys.items_reversed()) { if (IS_EQT(bezt->vec[1][0], rk.frame, BEZT_BINARYSEARCH_THRESH)) { /* Selected keys are treated with greater care than unselected ones... */ if (BEZT_ISSEL_ANY(bezt)) { /* - If this is the last selected key left (based on rk->del_count) ==> UPDATE IT * (or else we wouldn't have any keyframe left here) * - Otherwise, there are still other selected keyframes on this frame * to be merged down still ==> DELETE IT */ if (rk.del_count == rk.tot_count - 1) { /* Update keyframe... */ if (can_average_points) { /* TODO: update handles too? */ bezt->vec[1][1] = rk.val; } } else { /* Delete Keyframe */ BKE_fcurve_delete_key(fcu, i); } /* Update count of how many we've deleted * - It should only matter that we're doing this for all but the last one */ rk.del_count++; } else { /* Always delete - Unselected keys don't matter */ BKE_fcurve_delete_key(fcu, i); } /* Stop the RK search... we've found our match now */ break; } } } /* 3) Recalculate handles */ testhandles_fcurve(fcu, eBezTriple_Flag(sel_flag), use_handle); /* cleanup */ retained_keys.free_no_destruct(); } void BKE_fcurve_deduplicate_keys(FCurve *fcu) { if (fcu->totvert < 2) { return; } BLI_assert_msg(fcu->bezt, "this function only works with regular (non-sampled) FCurves"); if (fcu->bezt == nullptr) { return; } int prev_bezt_index = 0; for (int i = 1; i < fcu->totvert; i++) { BezTriple *bezt = &fcu->bezt[i]; BezTriple *prev_bezt = &fcu->bezt[prev_bezt_index]; const float bezt_x = bezt->vec[1][0]; const float prev_x = prev_bezt->vec[1][0]; if (bezt_x - prev_x <= BEZT_BINARYSEARCH_THRESH) { /* Replace 'prev_bezt', as it has the same X-coord as 'bezt' and the last one wins. */ *prev_bezt = *bezt; if (floor(bezt_x) == bezt_x) { /* Keep the 'bezt_x' coordinate, as being on a frame is more desirable * than being ever so slightly off. */ } else { /* Move the retained key to the old X-coordinate, to 'anchor' the X-coordinate used for * subsequent comparisons. Without this, the reference X-coordinate would keep moving * forward in time, potentially merging in more keys than desired. */ BKE_fcurve_keyframe_move_time_with_handles(prev_bezt, prev_x); } continue; } /* Next iteration should look at the current element. However, because of the deletions, that * may not be at index 'i'; after this increment, `prev_bezt_index` points at where the current * element should go. */ prev_bezt_index++; if (prev_bezt_index != i) { /* This bezt should be kept, so copy it to its new location in the array. */ fcu->bezt[prev_bezt_index] = *bezt; } } BKE_fcurve_bezt_resize(*fcu, prev_bezt_index + 1); } /** \} */ /* -------------------------------------------------------------------- */ /** \name F-Curve Evaluation * \{ */ static float fcurve_eval_keyframes_extrapolate(const FCurve *fcu, const BezTriple *bezts, float evaltime, int endpoint_offset, int direction_to_neighbor) { /* The first/last keyframe. */ const BezTriple *endpoint_bezt = bezts + endpoint_offset; /* The second (to last) keyframe. */ const BezTriple *neighbor_bezt = endpoint_bezt + direction_to_neighbor; if (endpoint_bezt->ipo == BEZT_IPO_CONST || fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT || (fcu->flag & FCURVE_DISCRETE_VALUES) != 0) { /* Constant (BEZT_IPO_HORIZ) extrapolation or constant interpolation, so just extend the * endpoint's value. */ return endpoint_bezt->vec[1][1]; } if (endpoint_bezt->ipo == BEZT_IPO_LIN) { /* Use the next center point instead of our own handle for linear interpolated extrapolate. */ if (fcu->totvert == 1) { return endpoint_bezt->vec[1][1]; } const float dx = endpoint_bezt->vec[1][0] - evaltime; float fac = neighbor_bezt->vec[1][0] - endpoint_bezt->vec[1][0]; /* Prevent division by zero. */ if (fac == 0.0f) { return endpoint_bezt->vec[1][1]; } fac = (neighbor_bezt->vec[1][1] - endpoint_bezt->vec[1][1]) / fac; return endpoint_bezt->vec[1][1] - (fac * dx); } /* Use the gradient of the second handle (later) of neighbor to calculate the gradient and thus * the value of the curve at evaluation time. */ const int handle = direction_to_neighbor > 0 ? 0 : 2; const float dx = endpoint_bezt->vec[1][0] - evaltime; float fac = endpoint_bezt->vec[1][0] - endpoint_bezt->vec[handle][0]; /* Prevent division by zero. */ if (fac == 0.0f) { return endpoint_bezt->vec[1][1]; } fac = (endpoint_bezt->vec[1][1] - endpoint_bezt->vec[handle][1]) / fac; return endpoint_bezt->vec[1][1] - (fac * dx); } static float fcurve_eval_keyframes_interpolate(const FCurve *fcu, const BezTriple *bezts, float evaltime) { const float eps = 1.e-8f; uint a; /* Evaluation-time occurs somewhere in the middle of the curve. */ bool exact = false; /* Use binary search to find appropriate keyframes... * * The threshold here has the following constraints: * - 0.001 is too coarse: * We get artifacts with 2cm driver movements at 1BU = 1m (see #40332). * * - 0.00001 is too fine: * Weird errors, like selecting the wrong keyframe range (see #39207), occur. * This lower bound was established in b888a32eee8147b028464336ad2404d8155c64dd. */ a = BKE_fcurve_bezt_binarysearch_index_ex(bezts, evaltime, fcu->totvert, 0.0001, &exact); const BezTriple *bezt = bezts + a; if (exact) { /* Index returned must be interpreted differently when it sits on top of an existing keyframe * - That keyframe is the start of the segment we need (see action_bug_2.blend in #39207). */ return bezt->vec[1][1]; } /* Index returned refers to the keyframe that the eval-time occurs *before* * - hence, that keyframe marks the start of the segment we're dealing with. */ const BezTriple *prevbezt = (a > 0) ? (bezt - 1) : bezt; /* Use if the key is directly on the frame, in rare cases this is needed else we get 0.0 instead. * XXX: consult #39207 for examples of files where failure of these checks can cause issues. */ if (fabsf(bezt->vec[1][0] - evaltime) < eps) { return bezt->vec[1][1]; } if (evaltime < prevbezt->vec[1][0] || bezt->vec[1][0] < evaltime) { if (G.debug & G_DEBUG) { printf(" ERROR: failed eval - p=%f b=%f, t=%f (%f)\n", prevbezt->vec[1][0], bezt->vec[1][0], evaltime, fabsf(bezt->vec[1][0] - evaltime)); } return 0.0f; } /* Evaluation-time occurs within the interval defined by these two keyframes. */ const float begin = prevbezt->vec[1][1]; const float change = bezt->vec[1][1] - prevbezt->vec[1][1]; const float duration = bezt->vec[1][0] - prevbezt->vec[1][0]; const float time = evaltime - prevbezt->vec[1][0]; const float amplitude = prevbezt->amplitude; const float period = prevbezt->period; /* Value depends on interpolation mode. */ if ((prevbezt->ipo == BEZT_IPO_CONST) || (fcu->flag & FCURVE_DISCRETE_VALUES) || (duration == 0)) { /* Constant (evaltime not relevant, so no interpolation needed). */ return prevbezt->vec[1][1]; } switch (prevbezt->ipo) { /* Interpolation ...................................... */ case BEZT_IPO_BEZ: { float v1[2], v2[2], v3[2], v4[2], opl[32]; /* Bezier interpolation. */ /* (v1, v2) are the first keyframe and its 2nd handle. */ v1[0] = prevbezt->vec[1][0]; v1[1] = prevbezt->vec[1][1]; v2[0] = prevbezt->vec[2][0]; v2[1] = prevbezt->vec[2][1]; /* (v3, v4) are the last keyframe's 1st handle + the last keyframe. */ v3[0] = bezt->vec[0][0]; v3[1] = bezt->vec[0][1]; v4[0] = bezt->vec[1][0]; v4[1] = bezt->vec[1][1]; if (fabsf(v1[1] - v4[1]) < FLT_EPSILON && fabsf(v2[1] - v3[1]) < FLT_EPSILON && fabsf(v3[1] - v4[1]) < FLT_EPSILON) { /* Optimization: If all the handles are flat/at the same values, * the value is simply the shared value (see #40372 -> F91346). */ return v1[1]; } /* Adjust handles so that they don't overlap (forming a loop). */ BKE_fcurve_correct_bezpart(v1, v2, v3, v4); /* Try to get a value for this position - if failure, try another set of points. */ if (!findzero(evaltime, v1[0], v2[0], v3[0], v4[0], opl)) { if (G.debug & G_DEBUG) { printf(" ERROR: findzero() failed at %f with %f %f %f %f\n", evaltime, v1[0], v2[0], v3[0], v4[0]); } return 0.0; } berekeny(v1[1], v2[1], v3[1], v4[1], opl, 1); return opl[0]; } case BEZT_IPO_LIN: /* Linear - simply linearly interpolate between values of the two keyframes. */ return BLI_easing_linear_ease(time, begin, change, duration); /* Easing ............................................ */ case BEZT_IPO_BACK: switch (prevbezt->easing) { case BEZT_IPO_EASE_IN: return BLI_easing_back_ease_in(time, begin, change, duration, prevbezt->back); case BEZT_IPO_EASE_OUT: return BLI_easing_back_ease_out(time, begin, change, duration, prevbezt->back); case BEZT_IPO_EASE_IN_OUT: return BLI_easing_back_ease_in_out(time, begin, change, duration, prevbezt->back); default: /* Default/Auto: same as ease out. */ return BLI_easing_back_ease_out(time, begin, change, duration, prevbezt->back); } break; case BEZT_IPO_BOUNCE: switch (prevbezt->easing) { case BEZT_IPO_EASE_IN: return BLI_easing_bounce_ease_in(time, begin, change, duration); case BEZT_IPO_EASE_OUT: return BLI_easing_bounce_ease_out(time, begin, change, duration); case BEZT_IPO_EASE_IN_OUT: return BLI_easing_bounce_ease_in_out(time, begin, change, duration); default: /* Default/Auto: same as ease out. */ return BLI_easing_bounce_ease_out(time, begin, change, duration); } break; case BEZT_IPO_CIRC: switch (prevbezt->easing) { case BEZT_IPO_EASE_IN: return BLI_easing_circ_ease_in(time, begin, change, duration); case BEZT_IPO_EASE_OUT: return BLI_easing_circ_ease_out(time, begin, change, duration); case BEZT_IPO_EASE_IN_OUT: return BLI_easing_circ_ease_in_out(time, begin, change, duration); default: /* Default/Auto: same as ease in. */ return BLI_easing_circ_ease_in(time, begin, change, duration); } break; case BEZT_IPO_CUBIC: switch (prevbezt->easing) { case BEZT_IPO_EASE_IN: return BLI_easing_cubic_ease_in(time, begin, change, duration); case BEZT_IPO_EASE_OUT: return BLI_easing_cubic_ease_out(time, begin, change, duration); case BEZT_IPO_EASE_IN_OUT: return BLI_easing_cubic_ease_in_out(time, begin, change, duration); default: /* Default/Auto: same as ease in. */ return BLI_easing_cubic_ease_in(time, begin, change, duration); } break; case BEZT_IPO_ELASTIC: switch (prevbezt->easing) { case BEZT_IPO_EASE_IN: return BLI_easing_elastic_ease_in(time, begin, change, duration, amplitude, period); case BEZT_IPO_EASE_OUT: return BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); case BEZT_IPO_EASE_IN_OUT: return BLI_easing_elastic_ease_in_out(time, begin, change, duration, amplitude, period); default: /* Default/Auto: same as ease out. */ return BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); } break; case BEZT_IPO_EXPO: switch (prevbezt->easing) { case BEZT_IPO_EASE_IN: return BLI_easing_expo_ease_in(time, begin, change, duration); case BEZT_IPO_EASE_OUT: return BLI_easing_expo_ease_out(time, begin, change, duration); case BEZT_IPO_EASE_IN_OUT: return BLI_easing_expo_ease_in_out(time, begin, change, duration); default: /* Default/Auto: same as ease in. */ return BLI_easing_expo_ease_in(time, begin, change, duration); } break; case BEZT_IPO_QUAD: switch (prevbezt->easing) { case BEZT_IPO_EASE_IN: return BLI_easing_quad_ease_in(time, begin, change, duration); case BEZT_IPO_EASE_OUT: return BLI_easing_quad_ease_out(time, begin, change, duration); case BEZT_IPO_EASE_IN_OUT: return BLI_easing_quad_ease_in_out(time, begin, change, duration); default: /* Default/Auto: same as ease in. */ return BLI_easing_quad_ease_in(time, begin, change, duration); } break; case BEZT_IPO_QUART: switch (prevbezt->easing) { case BEZT_IPO_EASE_IN: return BLI_easing_quart_ease_in(time, begin, change, duration); case BEZT_IPO_EASE_OUT: return BLI_easing_quart_ease_out(time, begin, change, duration); case BEZT_IPO_EASE_IN_OUT: return BLI_easing_quart_ease_in_out(time, begin, change, duration); default: /* Default/Auto: same as ease in. */ return BLI_easing_quart_ease_in(time, begin, change, duration); } break; case BEZT_IPO_QUINT: switch (prevbezt->easing) { case BEZT_IPO_EASE_IN: return BLI_easing_quint_ease_in(time, begin, change, duration); case BEZT_IPO_EASE_OUT: return BLI_easing_quint_ease_out(time, begin, change, duration); case BEZT_IPO_EASE_IN_OUT: return BLI_easing_quint_ease_in_out(time, begin, change, duration); default: /* Default/Auto: same as ease in. */ return BLI_easing_quint_ease_in(time, begin, change, duration); } break; case BEZT_IPO_SINE: switch (prevbezt->easing) { case BEZT_IPO_EASE_IN: return BLI_easing_sine_ease_in(time, begin, change, duration); case BEZT_IPO_EASE_OUT: return BLI_easing_sine_ease_out(time, begin, change, duration); case BEZT_IPO_EASE_IN_OUT: return BLI_easing_sine_ease_in_out(time, begin, change, duration); default: /* Default/Auto: same as ease in. */ return BLI_easing_sine_ease_in(time, begin, change, duration); } break; default: return prevbezt->vec[1][1]; } return 0.0f; } /* Calculate F-Curve value for 'evaltime' using #BezTriple keyframes. */ static float fcurve_eval_keyframes(const FCurve *fcu, const BezTriple *bezts, float evaltime) { if (evaltime <= bezts->vec[1][0]) { return fcurve_eval_keyframes_extrapolate(fcu, bezts, evaltime, 0, +1); } const BezTriple *lastbezt = bezts + fcu->totvert - 1; if (lastbezt->vec[1][0] <= evaltime) { return fcurve_eval_keyframes_extrapolate(fcu, bezts, evaltime, fcu->totvert - 1, -1); } return fcurve_eval_keyframes_interpolate(fcu, bezts, evaltime); } /* Calculate F-Curve value for 'evaltime' using #FPoint samples. */ static float fcurve_eval_samples(const FCurve *fcu, const FPoint *fpts, float evaltime) { float cvalue = 0.0f; /* Get pointers. */ const FPoint *prevfpt = fpts; const FPoint *lastfpt = prevfpt + fcu->totvert - 1; /* Evaluation time at or past endpoints? */ if (prevfpt->vec[0] >= evaltime) { /* Before or on first sample, so just extend value. */ cvalue = prevfpt->vec[1]; } else if (lastfpt->vec[0] <= evaltime) { /* After or on last sample, so just extend value. */ cvalue = lastfpt->vec[1]; } else { float t = fabsf(evaltime - floorf(evaltime)); /* Find the one on the right frame (assume that these are spaced on 1-frame intervals). */ const FPoint *fpt = prevfpt + (int(evaltime) - int(prevfpt->vec[0])); /* If not exactly on the frame, perform linear interpolation with the next one. */ if (t != 0.0f && t < 1.0f) { cvalue = interpf(fpt->vec[1], (fpt + 1)->vec[1], 1.0f - t); } else { cvalue = fpt->vec[1]; } } return cvalue; } /** \} */ /* -------------------------------------------------------------------- */ /** \name F-Curve - Evaluation * \{ */ /* Evaluate and return the value of the given F-Curve at the specified frame ("evaltime") * NOTE: this is also used for drivers. */ static float evaluate_fcurve_ex(const FCurve *fcu, float evaltime, float cvalue) { /* Evaluate modifiers which modify time to evaluate the base curve at. */ FModifiersStackStorage storage; storage.modifier_count = fcu->modifiers.count(); storage.size_per_modifier = evaluate_fmodifiers_storage_size_per_modifier(&fcu->modifiers); storage.buffer = alloca(storage.modifier_count * storage.size_per_modifier); const float devaltime = evaluate_time_fmodifiers( &storage, &fcu->modifiers, fcu, cvalue, evaltime); /* Evaluate curve-data * - 'devaltime' instead of 'evaltime', as this is the time that the last time-modifying * F-Curve modifier on the stack requested the curve to be evaluated at. */ if (fcu->bezt) { cvalue = fcurve_eval_keyframes(fcu, fcu->bezt, devaltime); } else if (fcu->fpt) { cvalue = fcurve_eval_samples(fcu, fcu->fpt, devaltime); } /* Evaluate modifiers. */ evaluate_value_fmodifiers(&storage, &fcu->modifiers, fcu, &cvalue, devaltime); /* If curve can only have integral values, perform truncation (i.e. drop the decimal part) * here so that the curve can be sampled correctly. */ if (fcu->flag & FCURVE_INT_VALUES) { cvalue = floorf(cvalue + 0.5f); } return cvalue; } float evaluate_fcurve(const FCurve *fcu, float evaltime) { BLI_assert(fcu->driver == nullptr); return evaluate_fcurve_ex(fcu, evaltime, 0.0); } float evaluate_fcurve_only_curve(const FCurve *fcu, float evaltime) { /* Can be used to evaluate the (key-framed) f-curve only. * Also works for driver-f-curves when the driver itself is not relevant. * E.g. when inserting a keyframe in a driver f-curve. */ return evaluate_fcurve_ex(fcu, evaltime, 0.0); } float evaluate_fcurve_unmodified(const FCurve *fcu, float evaltime) { if (fcu->bezt) { return fcurve_eval_keyframes(fcu, fcu->bezt, evaltime); } if (fcu->fpt) { return fcurve_eval_samples(fcu, fcu->fpt, evaltime); } BLI_assert_unreachable(); return 0; } float evaluate_fcurve_driver(PathResolvedRNA *anim_rna, FCurve *fcu, ChannelDriver *driver_orig, const AnimationEvalContext *anim_eval_context) { BLI_assert(fcu->driver != nullptr); float cvalue = 0.0f; float evaltime = anim_eval_context->eval_time; /* If there is a driver (only if this F-Curve is acting as 'driver'), * evaluate it to find value to use as `evaltime` since drivers essentially act as alternative * input (i.e. in place of 'time') for F-Curves. */ if (fcu->driver) { /* Evaluation-time now serves as input for the curve. */ evaltime = evaluate_driver(anim_rna, fcu->driver, driver_orig, anim_eval_context); /* Only do a default 1-1 mapping if it's unlikely that anything else will set a value... */ if (fcu->totvert == 0) { bool do_linear = true; /* Out-of-range F-Modifiers will block, as will those which just plain overwrite the values * XXX: additive is a bit more dicey; it really depends then if things are in range or not... */ for (FModifier &fcm : fcu->modifiers) { /* If there are range-restrictions, we must definitely block #36950. */ if ((fcm.flag & FMODIFIER_FLAG_RANGERESTRICT) == 0 || (fcm.sfra <= evaltime && fcm.efra >= evaltime)) { /* Within range: here it probably doesn't matter, * though we'd want to check on additive. */ } else { /* Outside range: modifier shouldn't contribute to the curve here, * though it does in other areas, so neither should the driver! */ do_linear = false; } } /* Only copy over results if none of the modifiers disagreed with this. */ if (do_linear) { cvalue = evaltime; } } } return evaluate_fcurve_ex(fcu, evaltime, cvalue); } bool BKE_fcurve_is_empty(const FCurve *fcu) { return fcu->totvert == 0 && fcu->driver == nullptr && !list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE); } float calculate_fcurve(PathResolvedRNA *anim_rna, FCurve *fcu, const AnimationEvalContext *anim_eval_context) { /* Only calculate + set curval (overriding the existing value) if curve has * any data which warrants this... */ if (BKE_fcurve_is_empty(fcu)) { return 0.0f; } /* Calculate and set curval (evaluates driver too if necessary). */ float curval; if (fcu->driver) { curval = evaluate_fcurve_driver(anim_rna, fcu, fcu->driver, anim_eval_context); } else { curval = evaluate_fcurve(fcu, anim_eval_context->eval_time); } fcu->curval = curval; /* Debug display only, not thread safe! */ return curval; } /** \} */ /* -------------------------------------------------------------------- */ /** \name F-Curve - .blend file API * \{ */ void BKE_fmodifiers_blend_write(BlendWriter *writer, ListBaseT *fmodifiers) { /* Write all modifiers first (for faster reloading) */ writer->write_struct_list(fmodifiers); /* Modifiers */ for (FModifier &fcm : *fmodifiers) { const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(&fcm); /* Write the specific data */ if (fmi && fcm.data) { /* firstly, just write the plain fmi->data struct */ writer->write_struct_by_name(fmi->struct_name, fcm.data); /* do any modifier specific stuff */ switch (fcm.type) { case FMODIFIER_TYPE_GENERATOR: { FMod_Generator *data = static_cast(fcm.data); /* write coefficients array */ if (data->coefficients) { writer->write_float_array(data->arraysize, data->coefficients); } break; } case FMODIFIER_TYPE_ENVELOPE: { FMod_Envelope *data = static_cast(fcm.data); /* write envelope data */ if (data->data) { writer->write_struct_array(data->totvert, data->data); } break; } default: break; } } } } void BKE_fmodifiers_blend_read_data(BlendDataReader *reader, ListBaseT *fmodifiers, FCurve *curve) { for (FModifier &fcm : *fmodifiers) { const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(&fcm); /* relink general data */ if (fmi) { fcm.data = BLO_read_struct_by_name_array(reader, fmi->struct_name, 1, fcm.data); } else { /* This can happen when the blend file has data for a modifier that doesn't exist in this * Blender version (when the blend file is newer). */ BLO_reportf_wrap(BLO_read_data_reports(reader), RPT_WARNING, RPT_("F-Curve modifier lost on '%s[%d]' because it has an unknown type"), curve->rna_path, curve->array_index); fcm.data = nullptr; } fcm.curve = curve; /* do relinking of data for specific types */ switch (fcm.type) { case FMODIFIER_TYPE_GENERATOR: { FMod_Generator *data = static_cast(fcm.data); BLO_read_array_and_validate_size(reader, &data->coefficients, &data->arraysize); break; } case FMODIFIER_TYPE_ENVELOPE: { FMod_Envelope *data = static_cast(fcm.data); BLO_read_array_and_validate_size(reader, &data->data, &data->totvert); break; } default: break; } } } void BKE_fcurve_blend_write_data(BlendWriter *writer, FCurve *fcu) { /* curve data */ if (fcu->bezt) { writer->write_struct_array(fcu->totvert, fcu->bezt); } if (fcu->fpt) { writer->write_struct_array(fcu->totvert, fcu->fpt); } if (fcu->rna_path) { writer->write_string(fcu->rna_path); } /* driver data */ if (fcu->driver) { ChannelDriver *driver = fcu->driver; writer->write_struct(driver); /* variables */ writer->write_struct_list(&driver->variables); for (DriverVar &dvar : driver->variables) { DRIVER_TARGETS_USED_LOOPER_BEGIN (&dvar) { if (dtar->rna_path) { writer->write_string(dtar->rna_path); } } DRIVER_TARGETS_LOOPER_END; } } /* write F-Modifiers */ BKE_fmodifiers_blend_write(writer, &fcu->modifiers); } void BKE_fcurve_blend_write_listbase(BlendWriter *writer, ListBaseT *fcurves) { writer->write_struct_list(fcurves); for (FCurve &fcu : *fcurves) { BKE_fcurve_blend_write_data(writer, &fcu); } } void BKE_fcurve_blend_read_data(BlendDataReader *reader, FCurve *fcu) { /* Curve data: only one of `bezt`/`fpt` is set, so guard validation to avoid clobbering * `totvert` when reading the unset pointer. */ if (fcu->bezt) { BLO_read_array_and_validate_size(reader, &fcu->bezt, &fcu->totvert); } if (fcu->fpt) { BLO_read_array_and_validate_size(reader, &fcu->fpt, &fcu->totvert); } /* rna path */ BLO_read_string(reader, &fcu->rna_path); /* group */ BLO_read_struct(reader, bActionGroup, &fcu->grp); /* clear disabled flag - allows disabled drivers to be tried again (#32155), * but also means that another method for "reviving disabled F-Curves" exists */ fcu->flag &= ~FCURVE_DISABLED; /* driver */ BLO_read_struct(reader, ChannelDriver, &fcu->driver); if (fcu->driver) { ChannelDriver *driver = fcu->driver; /* Compiled expression data will need to be regenerated * (old pointer may still be set here). */ driver->expr_comp = nullptr; driver->expr_simple = nullptr; /* Give the driver a fresh chance - the operating environment may be different now * (addons, etc. may be different) so the driver namespace may be sane now #32155. */ driver->flag &= ~(DRIVER_FLAG_INVALID | DRIVER_FLAG_PYTHON_BLOCKED); /* relink variables, targets and their paths */ BLO_read_struct_list(reader, DriverVar, &driver->variables); for (DriverVar &dvar : driver->variables) { DRIVER_TARGETS_LOOPER_BEGIN (&dvar) { /* only relink the targets being used */ if (tarIndex < dvar.num_targets) { BLO_read_string(reader, &dtar->rna_path); } else { dtar->rna_path = nullptr; dtar->id = nullptr; } } DRIVER_TARGETS_LOOPER_END; } } /* modifiers */ BLO_read_struct_list(reader, FModifier, &fcu->modifiers); BKE_fmodifiers_blend_read_data(reader, &fcu->modifiers, fcu); } void BKE_fcurve_blend_read_data_listbase(BlendDataReader *reader, ListBaseT *fcurves) { /* Link F-Curve data to F-Curve again (non ID-libraries). */ for (FCurve &fcu : *fcurves) { BKE_fcurve_blend_read_data(reader, &fcu); } } /** \} */ } // namespace blender