Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/fcurve.c
| Show First 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | void BKE_fcurve_free(FCurve *fcu) | ||||
| free_fmodifiers(&fcu->modifiers); | free_fmodifiers(&fcu->modifiers); | ||||
| /* Free the f-curve itself. */ | /* Free the f-curve itself. */ | ||||
| MEM_freeN(fcu); | MEM_freeN(fcu); | ||||
| } | } | ||||
| void BKE_fcurves_free(ListBase *list) | void BKE_fcurves_free(ListBase *list) | ||||
| { | { | ||||
| FCurve *fcu, *fcn; | |||||
| /* Sanity check. */ | /* Sanity check. */ | ||||
| if (list == NULL) { | if (list == NULL) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* Free data - no need to call remlink before freeing each curve, | /* Free data - no need to call remlink before freeing each curve, | ||||
| * as we store reference to next, and freeing only touches the curve | * as we store reference to next, and freeing only touches the curve | ||||
| * it's given. | * it's given. | ||||
| */ | */ | ||||
| for (fcu = list->first; fcu; fcu = fcn) { | FCurve *fcn = NULL; | ||||
| for (FCurve *fcu = list->first; fcu; fcu = fcn) { | |||||
| fcn = fcu->next; | fcn = fcu->next; | ||||
| BKE_fcurve_free(fcu); | BKE_fcurve_free(fcu); | ||||
| } | } | ||||
| /* Clear pointers just in case. */ | /* Clear pointers just in case. */ | ||||
| BLI_listbase_clear(list); | BLI_listbase_clear(list); | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name F-Curve Data Copy | /** \name F-Curve Data Copy | ||||
| * \{ */ | * \{ */ | ||||
| FCurve *BKE_fcurve_copy(const FCurve *fcu) | FCurve *BKE_fcurve_copy(const FCurve *fcu) | ||||
| { | { | ||||
| FCurve *fcu_d; | |||||
| /* Sanity check. */ | /* Sanity check. */ | ||||
| if (fcu == NULL) { | if (fcu == NULL) { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| /* Make a copy. */ | /* Make a copy. */ | ||||
| fcu_d = MEM_dupallocN(fcu); | FCurve *fcu_d = MEM_dupallocN(fcu); | ||||
| fcu_d->next = fcu_d->prev = NULL; | fcu_d->next = fcu_d->prev = NULL; | ||||
| fcu_d->grp = NULL; | fcu_d->grp = NULL; | ||||
| /* Copy curve data. */ | /* Copy curve data. */ | ||||
| fcu_d->bezt = MEM_dupallocN(fcu_d->bezt); | fcu_d->bezt = MEM_dupallocN(fcu_d->bezt); | ||||
| fcu_d->fpt = MEM_dupallocN(fcu_d->fpt); | fcu_d->fpt = MEM_dupallocN(fcu_d->fpt); | ||||
| /* Copy rna-path. */ | /* Copy rna-path. */ | ||||
| fcu_d->rna_path = MEM_dupallocN(fcu_d->rna_path); | fcu_d->rna_path = MEM_dupallocN(fcu_d->rna_path); | ||||
| /* Copy driver. */ | /* Copy driver. */ | ||||
| fcu_d->driver = fcurve_copy_driver(fcu_d->driver); | fcu_d->driver = fcurve_copy_driver(fcu_d->driver); | ||||
| /* Copy modifiers. */ | /* Copy modifiers. */ | ||||
| copy_fmodifiers(&fcu_d->modifiers, &fcu->modifiers); | copy_fmodifiers(&fcu_d->modifiers, &fcu->modifiers); | ||||
| /* Return new data. */ | /* Return new data. */ | ||||
| return fcu_d; | return fcu_d; | ||||
| } | } | ||||
| void BKE_fcurves_copy(ListBase *dst, ListBase *src) | void BKE_fcurves_copy(ListBase *dst, ListBase *src) | ||||
| { | { | ||||
| FCurve *dfcu, *sfcu; | |||||
| /* Sanity checks. */ | /* Sanity checks. */ | ||||
| if (ELEM(NULL, dst, src)) { | if (ELEM(NULL, dst, src)) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* Clear destination list first. */ | /* Clear destination list first. */ | ||||
| BLI_listbase_clear(dst); | BLI_listbase_clear(dst); | ||||
| /* Copy one-by-one. */ | /* Copy one-by-one. */ | ||||
| for (sfcu = src->first; sfcu; sfcu = sfcu->next) { | LISTBASE_FOREACH (FCurve *, sfcu, src) { | ||||
| dfcu = BKE_fcurve_copy(sfcu); | FCurve *dfcu = BKE_fcurve_copy(sfcu); | ||||
| BLI_addtail(dst, dfcu); | BLI_addtail(dst, dfcu); | ||||
| } | } | ||||
| } | } | ||||
| void BKE_fcurve_foreach_id(FCurve *fcu, LibraryForeachIDData *data) | void BKE_fcurve_foreach_id(FCurve *fcu, LibraryForeachIDData *data) | ||||
| { | { | ||||
| ChannelDriver *driver = fcu->driver; | ChannelDriver *driver = fcu->driver; | ||||
| Show All 29 Lines | |||||
| /* ----------------- Finding F-Curves -------------------------- */ | /* ----------------- Finding F-Curves -------------------------- */ | ||||
| FCurve *id_data_find_fcurve( | FCurve *id_data_find_fcurve( | ||||
| ID *id, void *data, StructRNA *type, const char *prop_name, int index, bool *r_driven) | ID *id, void *data, StructRNA *type, const char *prop_name, int index, bool *r_driven) | ||||
| { | { | ||||
| /* Anim vars */ | /* Anim vars */ | ||||
| AnimData *adt = BKE_animdata_from_id(id); | AnimData *adt = BKE_animdata_from_id(id); | ||||
| FCurve *fcu = NULL; | |||||
| /* Rna vars */ | /* Rna vars */ | ||||
| PointerRNA ptr; | PointerRNA ptr; | ||||
| PropertyRNA *prop; | PropertyRNA *prop; | ||||
| char *path; | |||||
| if (r_driven) { | if (r_driven) { | ||||
| *r_driven = false; | *r_driven = false; | ||||
| } | } | ||||
| /* Only use the current action ??? */ | /* Only use the current action ??? */ | ||||
| if (ELEM(NULL, adt, adt->action)) { | if (ELEM(NULL, adt, adt->action)) { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| RNA_pointer_create(id, type, data, &ptr); | RNA_pointer_create(id, type, data, &ptr); | ||||
| prop = RNA_struct_find_property(&ptr, prop_name); | prop = RNA_struct_find_property(&ptr, prop_name); | ||||
| if (prop == NULL) { | if (prop == NULL) { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| path = RNA_path_from_ID_to_property(&ptr, prop); | char *path = RNA_path_from_ID_to_property(&ptr, prop); | ||||
| if (path == NULL) { | if (path == NULL) { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| /* FIXME: The way drivers are handled here (always NULL-ifying `fcu`) is very weird, this needs | /* FIXME: The way drivers are handled here (always NULL-ifying `fcu`) is very weird, this needs | ||||
| * to be re-checked I think?. */ | * to be re-checked I think?. */ | ||||
| bool is_driven = false; | bool is_driven = false; | ||||
| fcu = BKE_animadata_fcurve_find_by_rna_path(adt, path, index, NULL, &is_driven); | FCurve *fcu = BKE_animadata_fcurve_find_by_rna_path(adt, path, index, NULL, &is_driven); | ||||
| if (is_driven) { | if (is_driven) { | ||||
| if (r_driven != NULL) { | if (r_driven != NULL) { | ||||
| *r_driven = is_driven; | *r_driven = is_driven; | ||||
| } | } | ||||
| fcu = NULL; | fcu = NULL; | ||||
| } | } | ||||
| MEM_freeN(path); | MEM_freeN(path); | ||||
| return fcu; | return fcu; | ||||
| } | } | ||||
| FCurve *BKE_fcurve_find(ListBase *list, const char rna_path[], const int array_index) | FCurve *BKE_fcurve_find(ListBase *list, const char rna_path[], const int array_index) | ||||
| { | { | ||||
| FCurve *fcu; | |||||
| /* Sanity checks. */ | /* Sanity checks. */ | ||||
| if (ELEM(NULL, list, rna_path) || (array_index < 0)) { | if (ELEM(NULL, list, rna_path) || array_index < 0) { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| /* Check paths of curves, then array indices... */ | /* Check paths of curves, then array indices... */ | ||||
| for (fcu = list->first; fcu; fcu = fcu->next) { | LISTBASE_FOREACH (FCurve *, fcu, list) { | ||||
| /* Check indices first, much cheaper than a string comparison. */ | /* Check indices first, much cheaper than a string comparison. */ | ||||
| /* Simple string-compare (this assumes that they have the same root...) */ | /* Simple string-compare (this assumes that they have the same root...) */ | ||||
| if (UNLIKELY(fcu->array_index == array_index && fcu->rna_path && | if (UNLIKELY(fcu->array_index == array_index && fcu->rna_path && | ||||
| fcu->rna_path[0] == rna_path[0] && STREQ(fcu->rna_path, rna_path))) { | fcu->rna_path[0] == rna_path[0] && STREQ(fcu->rna_path, rna_path))) { | ||||
| return fcu; | return fcu; | ||||
| } | } | ||||
| } | } | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name FCurve Iteration | /** \name FCurve Iteration | ||||
| * \{ */ | * \{ */ | ||||
| FCurve *BKE_fcurve_iter_step(FCurve *fcu_iter, const char rna_path[]) | FCurve *BKE_fcurve_iter_step(FCurve *fcu_iter, const char rna_path[]) | ||||
| { | { | ||||
| FCurve *fcu; | |||||
| /* Sanity checks. */ | /* Sanity checks. */ | ||||
| if (ELEM(NULL, fcu_iter, rna_path)) { | if (ELEM(NULL, fcu_iter, rna_path)) { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| /* Check paths of curves, then array indices... */ | /* Check paths of curves, then array indices... */ | ||||
| for (fcu = fcu_iter; fcu; fcu = fcu->next) { | for (FCurve *fcu = fcu_iter; fcu; fcu = fcu->next) { | ||||
| /* Simple string-compare (this assumes that they have the same root...) */ | /* Simple string-compare (this assumes that they have the same root...) */ | ||||
| if (fcu->rna_path && STREQ(fcu->rna_path, rna_path)) { | if (fcu->rna_path && STREQ(fcu->rna_path, rna_path)) { | ||||
| return fcu; | return fcu; | ||||
| } | } | ||||
| } | } | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| int BKE_fcurves_filter(ListBase *dst, ListBase *src, const char *dataPrefix, const char *dataName) | int BKE_fcurves_filter(ListBase *dst, ListBase *src, const char *dataPrefix, const char *dataName) | ||||
| { | { | ||||
| FCurve *fcu; | |||||
| int matches = 0; | int matches = 0; | ||||
| /* Sanity checks. */ | /* Sanity checks. */ | ||||
| if (ELEM(NULL, dst, src, dataPrefix, dataName)) { | if (ELEM(NULL, dst, src, dataPrefix, dataName)) { | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| if ((dataPrefix[0] == 0) || (dataName[0] == 0)) { | if ((dataPrefix[0] == 0) || (dataName[0] == 0)) { | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| const size_t quotedName_size = strlen(dataName) + 1; | const size_t quotedName_size = strlen(dataName) + 1; | ||||
| char *quotedName = alloca(quotedName_size); | char *quotedName = alloca(quotedName_size); | ||||
| /* Search each F-Curve one by one. */ | /* Search each F-Curve one by one. */ | ||||
| for (fcu = src->first; fcu; fcu = fcu->next) { | LISTBASE_FOREACH (FCurve *, fcu, src) { | ||||
| /* Check if quoted string matches the path. */ | /* Check if quoted string matches the path. */ | ||||
| if (fcu->rna_path == NULL) { | if (fcu->rna_path == NULL) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* Skipping names longer than `quotedName_size` is OK since we're after an exact match. */ | /* Skipping names longer than `quotedName_size` is OK since we're after an exact match. */ | ||||
| if (!BLI_str_quoted_substr(fcu->rna_path, dataPrefix, quotedName, quotedName_size)) { | if (!BLI_str_quoted_substr(fcu->rna_path, dataPrefix, quotedName, quotedName_size)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 159 Lines • ▼ Show 20 Lines | static int BKE_fcurve_bezt_binarysearch_index_ex(const BezTriple array[], | ||||
| /* Initialize replace-flag first. */ | /* Initialize replace-flag first. */ | ||||
| *r_replace = false; | *r_replace = false; | ||||
| /* Sneaky optimizations (don't go through searching process if...): | /* 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 is to be added out of current bounds. | ||||
| * - Keyframe to be added would replace one of the existing ones on bounds. | * - Keyframe to be added would replace one of the existing ones on bounds. | ||||
| */ | */ | ||||
| if ((arraylen <= 0) || (array == NULL)) { | if (arraylen <= 0 || array == NULL) { | ||||
| CLOG_WARN(&LOG, "encountered invalid array"); | CLOG_WARN(&LOG, "encountered invalid array"); | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| /* Check whether to add before/after/on. */ | /* Check whether to add before/after/on. */ | ||||
| float framenum; | |||||
| /* 'First' Keyframe (when only one keyframe, this case is used) */ | /* 'First' Keyframe (when only one keyframe, this case is used) */ | ||||
| framenum = array[0].vec[1][0]; | float framenum = array[0].vec[1][0]; | ||||
| if (IS_EQT(frame, framenum, threshold)) { | if (IS_EQT(frame, framenum, threshold)) { | ||||
| *r_replace = true; | *r_replace = true; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| if (frame < framenum) { | if (frame < framenum) { | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| Show All 9 Lines | static int BKE_fcurve_bezt_binarysearch_index_ex(const BezTriple array[], | ||||
| /* Most of the time, this loop is just to find where to put it | /* Most of the time, this loop is just to find where to put it | ||||
| * 'loopbreaker' is just here to prevent infinite loops. | * 'loopbreaker' is just here to prevent infinite loops. | ||||
| */ | */ | ||||
| for (loopbreaker = 0; (start <= end) && (loopbreaker < maxloop); loopbreaker++) { | for (loopbreaker = 0; (start <= end) && (loopbreaker < maxloop); loopbreaker++) { | ||||
| /* Compute and get midpoint. */ | /* Compute and get midpoint. */ | ||||
| /* We calculate the midpoint this way to avoid int overflows... */ | /* We calculate the midpoint this way to avoid int overflows... */ | ||||
| int mid = start + ((end - start) / 2); | const int mid = start + ((end - start) / 2); | ||||
| float midfra = array[mid].vec[1][0]; | const float midfra = array[mid].vec[1][0]; | ||||
| /* Check if exactly equal to midpoint. */ | /* Check if exactly equal to midpoint. */ | ||||
| if (IS_EQT(frame, midfra, threshold)) { | if (IS_EQT(frame, midfra, threshold)) { | ||||
| *r_replace = true; | *r_replace = true; | ||||
| return mid; | return mid; | ||||
| } | } | ||||
| /* Repeat in upper/lower half. */ | /* Repeat in upper/lower half. */ | ||||
| ▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | static short get_fcurve_end_keyframes(const FCurve *fcu, | ||||
| /* Sanity checks. */ | /* Sanity checks. */ | ||||
| if (fcu->bezt == NULL) { | if (fcu->bezt == NULL) { | ||||
| return found; | return found; | ||||
| } | } | ||||
| /* Only include selected items? */ | /* Only include selected items? */ | ||||
| if (do_sel_only) { | if (do_sel_only) { | ||||
| BezTriple *bezt; | |||||
| /* Find first selected. */ | /* Find first selected. */ | ||||
| bezt = fcu->bezt; | BezTriple *bezt = fcu->bezt; | ||||
| for (int i = 0; i < fcu->totvert; bezt++, i++) { | for (int i = 0; i < fcu->totvert; bezt++, i++) { | ||||
| if (BEZT_ISSEL_ANY(bezt)) { | if (BEZT_ISSEL_ANY(bezt)) { | ||||
| *first = bezt; | *first = bezt; | ||||
| found = true; | found = true; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | else if (fcu->fpt) { | ||||
| /* Frame range can be directly calculated from end verts. */ | /* Frame range can be directly calculated from end verts. */ | ||||
| if (xmin || xmax) { | if (xmin || xmax) { | ||||
| xminv = min_ff(xminv, fcu->fpt[0].vec[0]); | xminv = min_ff(xminv, fcu->fpt[0].vec[0]); | ||||
| xmaxv = max_ff(xmaxv, fcu->fpt[fcu->totvert - 1].vec[0]); | xmaxv = max_ff(xmaxv, fcu->fpt[fcu->totvert - 1].vec[0]); | ||||
| } | } | ||||
| /* Only loop over keyframes to find extents for values if needed. */ | /* Only loop over keyframes to find extents for values if needed. */ | ||||
| if (ymin || ymax) { | if (ymin || ymax) { | ||||
| FPoint *fpt; | int i = 0; | ||||
| int i; | |||||
| for (fpt = fcu->fpt, i = 0; i < fcu->totvert; fpt++, i++) { | for (FPoint *fpt = fcu->fpt; i < fcu->totvert; fpt++, i++) { | ||||
| if (fpt->vec[1] < yminv) { | if (fpt->vec[1] < yminv) { | ||||
| yminv = fpt->vec[1]; | yminv = fpt->vec[1]; | ||||
| } | } | ||||
| if (fpt->vec[1] > ymaxv) { | if (fpt->vec[1] > ymaxv) { | ||||
| ymaxv = fpt->vec[1]; | ymaxv = fpt->vec[1]; | ||||
| } | } | ||||
| foundvert = true; | foundvert = true; | ||||
| ▲ Show 20 Lines • Show All 139 Lines • ▼ Show 20 Lines | |||||
| { | { | ||||
| if (active_bezt == NULL) { | if (active_bezt == NULL) { | ||||
| fcu->active_keyframe_index = FCURVE_ACTIVE_KEYFRAME_NONE; | fcu->active_keyframe_index = FCURVE_ACTIVE_KEYFRAME_NONE; | ||||
| return; | return; | ||||
| } | } | ||||
| /* Gracefully handle out-of-bounds pointers. Ideally this would do a BLI_assert() as well, but | /* 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. */ | * then the unit tests would break in debug mode. */ | ||||
| ptrdiff_t offset = active_bezt - fcu->bezt; | const ptrdiff_t offset = active_bezt - fcu->bezt; | ||||
| if (offset < 0 || offset >= fcu->totvert) { | if (offset < 0 || offset >= fcu->totvert) { | ||||
| fcu->active_keyframe_index = FCURVE_ACTIVE_KEYFRAME_NONE; | fcu->active_keyframe_index = FCURVE_ACTIVE_KEYFRAME_NONE; | ||||
| return; | return; | ||||
| } | } | ||||
| /* The active keyframe should always be selected. */ | /* The active keyframe should always be selected. */ | ||||
| BLI_assert_msg(BEZT_ISSEL_ANY(active_bezt), "active keyframe must be selected"); | BLI_assert_msg(BEZT_ISSEL_ANY(active_bezt), "active keyframe must be selected"); | ||||
| fcu->active_keyframe_index = (int)offset; | fcu->active_keyframe_index = (int)offset; | ||||
| } | } | ||||
| int BKE_fcurve_active_keyframe_index(const FCurve *fcu) | int BKE_fcurve_active_keyframe_index(const FCurve *fcu) | ||||
| { | { | ||||
| const int active_keyframe_index = fcu->active_keyframe_index; | const int active_keyframe_index = fcu->active_keyframe_index; | ||||
| /* Array access boundary checks. */ | /* Array access boundary checks. */ | ||||
| if ((fcu->bezt == NULL) || (active_keyframe_index >= fcu->totvert) || | if (fcu->bezt == NULL || active_keyframe_index >= fcu->totvert || active_keyframe_index < 0) { | ||||
| (active_keyframe_index < 0)) { | |||||
| return FCURVE_ACTIVE_KEYFRAME_NONE; | return FCURVE_ACTIVE_KEYFRAME_NONE; | ||||
| } | } | ||||
| const BezTriple *active_bezt = &fcu->bezt[active_keyframe_index]; | const BezTriple *active_bezt = &fcu->bezt[active_keyframe_index]; | ||||
| if (((active_bezt->f1 | active_bezt->f2 | active_bezt->f3) & SELECT) == 0) { | 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. */ | /* The active keyframe should always be selected. If it's not selected, it can't be active. */ | ||||
| return FCURVE_ACTIVE_KEYFRAME_NONE; | return FCURVE_ACTIVE_KEYFRAME_NONE; | ||||
| } | } | ||||
| Show All 24 Lines | bool BKE_fcurve_are_keyframes_usable(const FCurve *fcu) | ||||
| /* F-Curve must not have samples - samples are mutually exclusive of keyframes. */ | /* F-Curve must not have samples - samples are mutually exclusive of keyframes. */ | ||||
| if (fcu->fpt) { | if (fcu->fpt) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| /* If it has modifiers, none of these should "drastically" alter the curve. */ | /* If it has modifiers, none of these should "drastically" alter the curve. */ | ||||
| if (fcu->modifiers.first) { | if (fcu->modifiers.first) { | ||||
| FModifier *fcm; | |||||
| /* Check modifiers from last to first, as last will be more influential. */ | /* 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) */ | /* TODO: optionally, only check modifier if it is the active one... (Joshua Leung 2010) */ | ||||
| for (fcm = fcu->modifiers.last; fcm; fcm = fcm->prev) { | LISTBASE_FOREACH_BACKWARD (FModifier *, fcm, &fcu->modifiers) { | ||||
| /* Ignore if muted/disabled. */ | /* Ignore if muted/disabled. */ | ||||
| if (fcm->flag & (FMODIFIER_FLAG_DISABLED | FMODIFIER_FLAG_MUTED)) { | if (fcm->flag & (FMODIFIER_FLAG_DISABLED | FMODIFIER_FLAG_MUTED)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* Type checks. */ | /* Type checks. */ | ||||
| switch (fcm->type) { | switch (fcm->type) { | ||||
| /* Clearly harmless - do nothing. */ | /* Clearly harmless - do nothing. */ | ||||
| Show All 27 Lines | bool BKE_fcurve_are_keyframes_usable(const FCurve *fcu) | ||||
| } | } | ||||
| /* Keyframes are usable. */ | /* Keyframes are usable. */ | ||||
| return true; | return true; | ||||
| } | } | ||||
| bool BKE_fcurve_is_protected(const FCurve *fcu) | bool BKE_fcurve_is_protected(const FCurve *fcu) | ||||
| { | { | ||||
| return ((fcu->flag & FCURVE_PROTECTED) || ((fcu->grp) && (fcu->grp->flag & AGRP_PROTECTED))); | return ((fcu->flag & FCURVE_PROTECTED) || (fcu->grp && (fcu->grp->flag & AGRP_PROTECTED))); | ||||
| } | } | ||||
| bool BKE_fcurve_has_selected_control_points(const FCurve *fcu) | bool BKE_fcurve_has_selected_control_points(const FCurve *fcu) | ||||
| { | { | ||||
| int i; | int i; | ||||
| BezTriple *bezt; | BezTriple *bezt; | ||||
| for (bezt = fcu->bezt, i = 0; i < fcu->totvert; ++i, ++bezt) { | for (bezt = fcu->bezt, i = 0; i < fcu->totvert; ++i, ++bezt) { | ||||
| if ((bezt->f2 & SELECT) != 0) { | if ((bezt->f2 & SELECT) != 0) { | ||||
| ▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | |||||
| float fcurve_samplingcb_evalcurve(FCurve *fcu, void *UNUSED(data), float evaltime) | float fcurve_samplingcb_evalcurve(FCurve *fcu, void *UNUSED(data), float evaltime) | ||||
| { | { | ||||
| /* Assume any interference from drivers on the curve is intended... */ | /* Assume any interference from drivers on the curve is intended... */ | ||||
| return evaluate_fcurve(fcu, evaltime); | return evaluate_fcurve(fcu, evaltime); | ||||
| } | } | ||||
| void fcurve_store_samples(FCurve *fcu, void *data, int start, int end, FcuSampleFunc sample_cb) | void fcurve_store_samples(FCurve *fcu, void *data, int start, int end, FcuSampleFunc sample_cb) | ||||
| { | { | ||||
| FPoint *fpt, *new_fpt; | |||||
| int cfra; | |||||
| /* Sanity checks. */ | /* Sanity checks. */ | ||||
| /* TODO: make these tests report errors using reports not CLOG's (Joshua Leung 2009) */ | /* TODO: make these tests report errors using reports not CLOG's (Joshua Leung 2009) */ | ||||
| if (ELEM(NULL, fcu, sample_cb)) { | if (ELEM(NULL, fcu, sample_cb)) { | ||||
| CLOG_ERROR(&LOG, "No F-Curve with F-Curve Modifiers to Bake"); | CLOG_ERROR(&LOG, "No F-Curve with F-Curve Modifiers to Bake"); | ||||
| return; | return; | ||||
| } | } | ||||
| if (start > end) { | if (start > end) { | ||||
| CLOG_ERROR(&LOG, "Error: Frame range for Sampled F-Curve creation is inappropriate"); | CLOG_ERROR(&LOG, "Error: Frame range for Sampled F-Curve creation is inappropriate"); | ||||
| return; | return; | ||||
| } | } | ||||
| /* Set up sample data. */ | /* Set up sample data. */ | ||||
| fpt = new_fpt = MEM_callocN(sizeof(FPoint) * (end - start + 1), "FPoint Samples"); | FPoint *new_fpt; | ||||
| FPoint *fpt = new_fpt = MEM_callocN(sizeof(FPoint) * (end - start + 1), "FPoint Samples"); | |||||
| /* Use the sampling callback at 1-frame intervals from start to end frames. */ | /* Use the sampling callback at 1-frame intervals from start to end frames. */ | ||||
| for (cfra = start; cfra <= end; cfra++, fpt++) { | for (int cfra = start; cfra <= end; cfra++, fpt++) { | ||||
| fpt->vec[0] = (float)cfra; | fpt->vec[0] = (float)cfra; | ||||
| fpt->vec[1] = sample_cb(fcu, data, (float)cfra); | fpt->vec[1] = sample_cb(fcu, data, (float)cfra); | ||||
| } | } | ||||
| /* Free any existing sample/keyframe data on curve. */ | /* Free any existing sample/keyframe data on curve. */ | ||||
| if (fcu->bezt) { | if (fcu->bezt) { | ||||
| MEM_freeN(fcu->bezt); | MEM_freeN(fcu->bezt); | ||||
| } | } | ||||
| Show All 36 Lines | if (fcu->fpt == NULL) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* Free any existing sample/keyframe data on the curve. */ | /* Free any existing sample/keyframe data on the curve. */ | ||||
| if (fcu->bezt) { | if (fcu->bezt) { | ||||
| MEM_freeN(fcu->bezt); | MEM_freeN(fcu->bezt); | ||||
| } | } | ||||
| BezTriple *bezt; | |||||
| FPoint *fpt = fcu->fpt; | FPoint *fpt = fcu->fpt; | ||||
| int keyframes_to_insert = end - start; | int keyframes_to_insert = end - start; | ||||
| int sample_points = fcu->totvert; | int sample_points = fcu->totvert; | ||||
| bezt = fcu->bezt = MEM_callocN(sizeof(*fcu->bezt) * (size_t)keyframes_to_insert, __func__); | BezTriple *bezt = fcu->bezt = MEM_callocN(sizeof(*fcu->bezt) * (size_t)keyframes_to_insert, | ||||
| __func__); | |||||
| fcu->totvert = keyframes_to_insert; | fcu->totvert = keyframes_to_insert; | ||||
| /* Get first sample point to 'copy' as keyframe. */ | /* Get first sample point to 'copy' as keyframe. */ | ||||
| for (; sample_points && (fpt->vec[0] < start); fpt++, sample_points--) { | for (; sample_points && (fpt->vec[0] < start); fpt++, sample_points--) { | ||||
| /* pass */ | /* pass */ | ||||
| } | } | ||||
| /* Current position in the timeline. */ | /* Current position in the timeline. */ | ||||
| ▲ Show 20 Lines • Show All 90 Lines • ▼ Show 20 Lines | for (int i = 0; i < 3; i++) { | ||||
| add_v3_v3(out->vec[i], delta); | add_v3_v3(out->vec[i], delta); | ||||
| } | } | ||||
| return out; | return out; | ||||
| } | } | ||||
| void BKE_fcurve_handles_recalc_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag) | void BKE_fcurve_handles_recalc_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag) | ||||
| { | { | ||||
| BezTriple *bezt, *prev, *next; | |||||
| int a = fcu->totvert; | int a = fcu->totvert; | ||||
| /* Error checking: | /* Error checking: | ||||
| * - Need at least two points. | * - Need at least two points. | ||||
| * - Need bezier keys. | * - Need bezier keys. | ||||
| * - Only bezier-interpolation has handles (for now). | * - Only bezier-interpolation has handles (for now). | ||||
| */ | */ | ||||
| if (ELEM(NULL, fcu, fcu->bezt) || (a < 2) /*|| ELEM(fcu->ipo, BEZT_IPO_CONST, BEZT_IPO_LIN) */) { | if (ELEM(NULL, fcu, fcu->bezt) || (a < 2) /*|| ELEM(fcu->ipo, BEZT_IPO_CONST, BEZT_IPO_LIN) */) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* If the first modifier is Cycles, smooth the curve through the cycle. */ | /* If the first modifier is Cycles, smooth the curve through the cycle. */ | ||||
| BezTriple *first = &fcu->bezt[0], *last = &fcu->bezt[fcu->totvert - 1]; | BezTriple *first = &fcu->bezt[0], *last = &fcu->bezt[fcu->totvert - 1]; | ||||
| BezTriple tmp; | BezTriple tmp; | ||||
| bool cycle = BKE_fcurve_is_cyclic(fcu) && BEZT_IS_AUTOH(first) && BEZT_IS_AUTOH(last); | const bool cycle = BKE_fcurve_is_cyclic(fcu) && BEZT_IS_AUTOH(first) && BEZT_IS_AUTOH(last); | ||||
| /* Get initial pointers. */ | /* Get initial pointers. */ | ||||
| bezt = fcu->bezt; | BezTriple *bezt = fcu->bezt; | ||||
| prev = cycle_offset_triple(cycle, &tmp, &fcu->bezt[fcu->totvert - 2], last, first); | BezTriple *prev = cycle_offset_triple(cycle, &tmp, &fcu->bezt[fcu->totvert - 2], last, first); | ||||
| next = (bezt + 1); | BezTriple *next = (bezt + 1); | ||||
| /* Loop over all beztriples, adjusting handles. */ | /* Loop over all beztriples, adjusting handles. */ | ||||
| while (a--) { | while (a--) { | ||||
| /* Clamp timing of handles to be on either side of beztriple. */ | /* Clamp timing of handles to be on either side of beztriple. */ | ||||
| if (bezt->vec[0][0] > bezt->vec[1][0]) { | if (bezt->vec[0][0] > bezt->vec[1][0]) { | ||||
| bezt->vec[0][0] = bezt->vec[1][0]; | bezt->vec[0][0] = bezt->vec[1][0]; | ||||
| } | } | ||||
| if (bezt->vec[2][0] < bezt->vec[1][0]) { | if (bezt->vec[2][0] < bezt->vec[1][0]) { | ||||
| ▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | |||||
| void BKE_fcurve_handles_recalc(FCurve *fcu) | void BKE_fcurve_handles_recalc(FCurve *fcu) | ||||
| { | { | ||||
| BKE_fcurve_handles_recalc_ex(fcu, SELECT); | BKE_fcurve_handles_recalc_ex(fcu, SELECT); | ||||
| } | } | ||||
| void testhandles_fcurve(FCurve *fcu, eBezTriple_Flag sel_flag, const bool use_handle) | void testhandles_fcurve(FCurve *fcu, eBezTriple_Flag sel_flag, const bool use_handle) | ||||
| { | { | ||||
| BezTriple *bezt; | |||||
| uint a; | |||||
| /* Only beztriples have handles (bpoints don't though). */ | /* Only beztriples have handles (bpoints don't though). */ | ||||
| if (ELEM(NULL, fcu, fcu->bezt)) { | if (ELEM(NULL, fcu, fcu->bezt)) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* Loop over beztriples. */ | /* Loop over beztriples. */ | ||||
| BezTriple *bezt; | |||||
| uint a; | |||||
| for (a = 0, bezt = fcu->bezt; a < fcu->totvert; a++, bezt++) { | for (a = 0, bezt = fcu->bezt; a < fcu->totvert; a++, bezt++) { | ||||
| BKE_nurb_bezt_handle_test(bezt, sel_flag, use_handle, false); | BKE_nurb_bezt_handle_test(bezt, sel_flag, use_handle, false); | ||||
| } | } | ||||
| /* Recalculate handles. */ | /* Recalculate handles. */ | ||||
| BKE_fcurve_handles_recalc_ex(fcu, sel_flag); | BKE_fcurve_handles_recalc_ex(fcu, sel_flag); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 357 Lines • ▼ Show 20 Lines | void BKE_fcurve_delete_key(FCurve *fcu, int index) | ||||
| /* Free the array of BezTriples if there are not keyframes */ | /* Free the array of BezTriples if there are not keyframes */ | ||||
| if (fcu->totvert == 0) { | if (fcu->totvert == 0) { | ||||
| fcurve_bezt_free(fcu); | fcurve_bezt_free(fcu); | ||||
| } | } | ||||
| } | } | ||||
| bool BKE_fcurve_delete_keys_selected(FCurve *fcu) | bool BKE_fcurve_delete_keys_selected(FCurve *fcu) | ||||
| { | { | ||||
| bool changed = false; | |||||
| if (fcu->bezt == NULL) { /* ignore baked curves */ | if (fcu->bezt == NULL) { /* ignore baked curves */ | ||||
| return false; | return false; | ||||
| } | } | ||||
| bool changed = false; | |||||
| /* Delete selected BezTriples */ | /* Delete selected BezTriples */ | ||||
| for (int i = 0; i < fcu->totvert; i++) { | for (int i = 0; i < fcu->totvert; i++) { | ||||
| if (fcu->bezt[i].f2 & SELECT) { | if (fcu->bezt[i].f2 & SELECT) { | ||||
| if (i == fcu->active_keyframe_index) { | if (i == fcu->active_keyframe_index) { | ||||
| BKE_fcurve_active_keyframe_set(fcu, NULL); | BKE_fcurve_active_keyframe_set(fcu, NULL); | ||||
| } | } | ||||
| memmove(&fcu->bezt[i], &fcu->bezt[i + 1], sizeof(BezTriple) * (fcu->totvert - i - 1)); | memmove(&fcu->bezt[i], &fcu->bezt[i + 1], sizeof(BezTriple) * (fcu->totvert - i - 1)); | ||||
| fcu->totvert--; | fcu->totvert--; | ||||
| Show All 19 Lines | |||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name F-Curve Evaluation | /** \name F-Curve Evaluation | ||||
| * \{ */ | * \{ */ | ||||
| static float fcurve_eval_keyframes_extrapolate( | static float fcurve_eval_keyframes_extrapolate( | ||||
| FCurve *fcu, BezTriple *bezts, float evaltime, int endpoint_offset, int direction_to_neighbor) | FCurve *fcu, BezTriple *bezts, float evaltime, int endpoint_offset, int direction_to_neighbor) | ||||
| { | { | ||||
| BezTriple *endpoint_bezt = bezts + endpoint_offset; /* The first/last keyframe. */ | const BezTriple *endpoint_bezt = bezts + endpoint_offset; /* The first/last keyframe. */ | ||||
| BezTriple *neighbor_bezt = endpoint_bezt + | const BezTriple *neighbor_bezt = endpoint_bezt + | ||||
| direction_to_neighbor; /* The second (to last) keyframe. */ | direction_to_neighbor; /* The second (to last) keyframe. */ | ||||
| if (endpoint_bezt->ipo == BEZT_IPO_CONST || fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT || | if (endpoint_bezt->ipo == BEZT_IPO_CONST || fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT || | ||||
| (fcu->flag & FCURVE_DISCRETE_VALUES) != 0) { | (fcu->flag & FCURVE_DISCRETE_VALUES) != 0) { | ||||
| /* Constant (BEZT_IPO_HORIZ) extrapolation or constant interpolation, so just extend the | /* Constant (BEZT_IPO_HORIZ) extrapolation or constant interpolation, so just extend the | ||||
| * endpoint's value. */ | * endpoint's value. */ | ||||
| return endpoint_bezt->vec[1][1]; | return endpoint_bezt->vec[1][1]; | ||||
| } | } | ||||
| if (endpoint_bezt->ipo == BEZT_IPO_LIN) { | if (endpoint_bezt->ipo == BEZT_IPO_LIN) { | ||||
| /* Use the next center point instead of our own handle for linear interpolated extrapolate. */ | /* Use the next center point instead of our own handle for linear interpolated extrapolate. */ | ||||
| if (fcu->totvert == 1) { | if (fcu->totvert == 1) { | ||||
| return endpoint_bezt->vec[1][1]; | return endpoint_bezt->vec[1][1]; | ||||
| } | } | ||||
| float dx = endpoint_bezt->vec[1][0] - evaltime; | const float dx = endpoint_bezt->vec[1][0] - evaltime; | ||||
| float fac = neighbor_bezt->vec[1][0] - endpoint_bezt->vec[1][0]; | float fac = neighbor_bezt->vec[1][0] - endpoint_bezt->vec[1][0]; | ||||
| /* Prevent division by zero. */ | /* Prevent division by zero. */ | ||||
| if (fac == 0.0f) { | if (fac == 0.0f) { | ||||
| return endpoint_bezt->vec[1][1]; | return endpoint_bezt->vec[1][1]; | ||||
| } | } | ||||
| fac = (neighbor_bezt->vec[1][1] - endpoint_bezt->vec[1][1]) / fac; | fac = (neighbor_bezt->vec[1][1] - endpoint_bezt->vec[1][1]) / fac; | ||||
| return endpoint_bezt->vec[1][1] - (fac * dx); | return endpoint_bezt->vec[1][1] - (fac * dx); | ||||
| } | } | ||||
| /* Use the gradient of the second handle (later) of neighbor to calculate the gradient and thus | /* Use the gradient of the second handle (later) of neighbor to calculate the gradient and thus | ||||
| * the value of the curve at evaluation time. */ | * the value of the curve at evaluation time. */ | ||||
| int handle = direction_to_neighbor > 0 ? 0 : 2; | const int handle = direction_to_neighbor > 0 ? 0 : 2; | ||||
| float dx = endpoint_bezt->vec[1][0] - evaltime; | const float dx = endpoint_bezt->vec[1][0] - evaltime; | ||||
| float fac = endpoint_bezt->vec[1][0] - endpoint_bezt->vec[handle][0]; | float fac = endpoint_bezt->vec[1][0] - endpoint_bezt->vec[handle][0]; | ||||
| /* Prevent division by zero. */ | /* Prevent division by zero. */ | ||||
| if (fac == 0.0f) { | if (fac == 0.0f) { | ||||
| return endpoint_bezt->vec[1][1]; | return endpoint_bezt->vec[1][1]; | ||||
| } | } | ||||
| fac = (endpoint_bezt->vec[1][1] - endpoint_bezt->vec[handle][1]) / fac; | fac = (endpoint_bezt->vec[1][1] - endpoint_bezt->vec[handle][1]) / fac; | ||||
| return endpoint_bezt->vec[1][1] - (fac * dx); | return endpoint_bezt->vec[1][1] - (fac * dx); | ||||
| } | } | ||||
| static float fcurve_eval_keyframes_interpolate(FCurve *fcu, BezTriple *bezts, float evaltime) | static float fcurve_eval_keyframes_interpolate(const FCurve *fcu, | ||||
| const BezTriple *bezts, | |||||
| float evaltime) | |||||
| { | { | ||||
| const float eps = 1.e-8f; | const float eps = 1.e-8f; | ||||
| BezTriple *bezt, *prevbezt; | |||||
| uint a; | uint a; | ||||
| /* Evaltime occurs somewhere in the middle of the curve. */ | /* Evaltime occurs somewhere in the middle of the curve. */ | ||||
| bool exact = false; | bool exact = false; | ||||
| /* Use binary search to find appropriate keyframes... | /* Use binary search to find appropriate keyframes... | ||||
| * | * | ||||
| * The threshold here has the following constraints: | * The threshold here has the following constraints: | ||||
| * - 0.001 is too coarse: | * - 0.001 is too coarse: | ||||
| * We get artifacts with 2cm driver movements at 1BU = 1m (see T40332). | * We get artifacts with 2cm driver movements at 1BU = 1m (see T40332). | ||||
| * | * | ||||
| * - 0.00001 is too fine: | * - 0.00001 is too fine: | ||||
| * Weird errors, like selecting the wrong keyframe range (see T39207), occur. | * Weird errors, like selecting the wrong keyframe range (see T39207), occur. | ||||
| * This lower bound was established in b888a32eee8147b028464336ad2404d8155c64dd. | * This lower bound was established in b888a32eee8147b028464336ad2404d8155c64dd. | ||||
| */ | */ | ||||
| a = BKE_fcurve_bezt_binarysearch_index_ex(bezts, evaltime, fcu->totvert, 0.0001, &exact); | a = BKE_fcurve_bezt_binarysearch_index_ex(bezts, evaltime, fcu->totvert, 0.0001, &exact); | ||||
| bezt = bezts + a; | const BezTriple *bezt = bezts + a; | ||||
| if (exact) { | if (exact) { | ||||
| /* Index returned must be interpreted differently when it sits on top of an existing keyframe | /* 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 T39207). | * - That keyframe is the start of the segment we need (see action_bug_2.blend in T39207). | ||||
| */ | */ | ||||
| return bezt->vec[1][1]; | return bezt->vec[1][1]; | ||||
| } | } | ||||
| /* Index returned refers to the keyframe that the eval-time occurs *before* | /* 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. | * - hence, that keyframe marks the start of the segment we're dealing with. | ||||
| */ | */ | ||||
| prevbezt = (a > 0) ? (bezt - 1) : bezt; | 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. | /* Use if the key is directly on the frame, in rare cases this is needed else we get 0.0 instead. | ||||
| * XXX: consult T39207 for examples of files where failure of these checks can cause issues. */ | * XXX: consult T39207 for examples of files where failure of these checks can cause issues. */ | ||||
| if (fabsf(bezt->vec[1][0] - evaltime) < eps) { | if (fabsf(bezt->vec[1][0] - evaltime) < eps) { | ||||
| return bezt->vec[1][1]; | return bezt->vec[1][1]; | ||||
| } | } | ||||
| if (evaltime < prevbezt->vec[1][0] || bezt->vec[1][0] < evaltime) { | if (evaltime < prevbezt->vec[1][0] || bezt->vec[1][0] < evaltime) { | ||||
| ▲ Show 20 Lines • Show All 219 Lines • ▼ Show 20 Lines | |||||
| /* Calculate F-Curve value for 'evaltime' using #BezTriple keyframes. */ | /* Calculate F-Curve value for 'evaltime' using #BezTriple keyframes. */ | ||||
| static float fcurve_eval_keyframes(FCurve *fcu, BezTriple *bezts, float evaltime) | static float fcurve_eval_keyframes(FCurve *fcu, BezTriple *bezts, float evaltime) | ||||
| { | { | ||||
| if (evaltime <= bezts->vec[1][0]) { | if (evaltime <= bezts->vec[1][0]) { | ||||
| return fcurve_eval_keyframes_extrapolate(fcu, bezts, evaltime, 0, +1); | return fcurve_eval_keyframes_extrapolate(fcu, bezts, evaltime, 0, +1); | ||||
| } | } | ||||
| BezTriple *lastbezt = bezts + fcu->totvert - 1; | const BezTriple *lastbezt = bezts + fcu->totvert - 1; | ||||
| if (lastbezt->vec[1][0] <= evaltime) { | if (lastbezt->vec[1][0] <= evaltime) { | ||||
| return fcurve_eval_keyframes_extrapolate(fcu, bezts, evaltime, fcu->totvert - 1, -1); | return fcurve_eval_keyframes_extrapolate(fcu, bezts, evaltime, fcu->totvert - 1, -1); | ||||
| } | } | ||||
| return fcurve_eval_keyframes_interpolate(fcu, bezts, evaltime); | return fcurve_eval_keyframes_interpolate(fcu, bezts, evaltime); | ||||
| } | } | ||||
| /* Calculate F-Curve value for 'evaltime' using #FPoint samples. */ | /* Calculate F-Curve value for 'evaltime' using #FPoint samples. */ | ||||
| static float fcurve_eval_samples(FCurve *fcu, FPoint *fpts, float evaltime) | static float fcurve_eval_samples(const FCurve *fcu, const FPoint *fpts, float evaltime) | ||||
| { | { | ||||
| FPoint *prevfpt, *lastfpt, *fpt; | |||||
| float cvalue = 0.0f; | float cvalue = 0.0f; | ||||
| /* Get pointers. */ | /* Get pointers. */ | ||||
| prevfpt = fpts; | const FPoint *prevfpt = fpts; | ||||
| lastfpt = prevfpt + fcu->totvert - 1; | const FPoint *lastfpt = prevfpt + fcu->totvert - 1; | ||||
| /* Evaluation time at or past endpoints? */ | /* Evaluation time at or past endpoints? */ | ||||
| if (prevfpt->vec[0] >= evaltime) { | if (prevfpt->vec[0] >= evaltime) { | ||||
| /* Before or on first sample, so just extend value. */ | /* Before or on first sample, so just extend value. */ | ||||
| cvalue = prevfpt->vec[1]; | cvalue = prevfpt->vec[1]; | ||||
| } | } | ||||
| else if (lastfpt->vec[0] <= evaltime) { | else if (lastfpt->vec[0] <= evaltime) { | ||||
| /* After or on last sample, so just extend value. */ | /* After or on last sample, so just extend value. */ | ||||
| cvalue = lastfpt->vec[1]; | cvalue = lastfpt->vec[1]; | ||||
| } | } | ||||
| else { | else { | ||||
| float t = fabsf(evaltime - floorf(evaltime)); | float t = fabsf(evaltime - floorf(evaltime)); | ||||
| /* Find the one on the right frame (assume that these are spaced on 1-frame intervals). */ | /* Find the one on the right frame (assume that these are spaced on 1-frame intervals). */ | ||||
| fpt = prevfpt + ((int)evaltime - (int)prevfpt->vec[0]); | const FPoint *fpt = prevfpt + ((int)evaltime - (int)prevfpt->vec[0]); | ||||
| /* If not exactly on the frame, perform linear interpolation with the next one. */ | /* If not exactly on the frame, perform linear interpolation with the next one. */ | ||||
| if ((t != 0.0f) && (t < 1.0f)) { | if (t != 0.0f && t < 1.0f) { | ||||
| cvalue = interpf(fpt->vec[1], (fpt + 1)->vec[1], 1.0f - t); | cvalue = interpf(fpt->vec[1], (fpt + 1)->vec[1], 1.0f - t); | ||||
| } | } | ||||
| else { | else { | ||||
| cvalue = fpt->vec[1]; | cvalue = fpt->vec[1]; | ||||
| } | } | ||||
| } | } | ||||
| return cvalue; | return cvalue; | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name F-Curve - Evaluation | /** \name F-Curve - Evaluation | ||||
| * \{ */ | * \{ */ | ||||
| /* Evaluate and return the value of the given F-Curve at the specified frame ("evaltime") | /* Evaluate and return the value of the given F-Curve at the specified frame ("evaltime") | ||||
| * NOTE: this is also used for drivers. | * NOTE: this is also used for drivers. | ||||
| */ | */ | ||||
| static float evaluate_fcurve_ex(FCurve *fcu, float evaltime, float cvalue) | static float evaluate_fcurve_ex(FCurve *fcu, float evaltime, float cvalue) | ||||
| { | { | ||||
| float devaltime; | |||||
| /* Evaluate modifiers which modify time to evaluate the base curve at. */ | /* Evaluate modifiers which modify time to evaluate the base curve at. */ | ||||
| FModifiersStackStorage storage; | FModifiersStackStorage storage; | ||||
| storage.modifier_count = BLI_listbase_count(&fcu->modifiers); | storage.modifier_count = BLI_listbase_count(&fcu->modifiers); | ||||
| storage.size_per_modifier = evaluate_fmodifiers_storage_size_per_modifier(&fcu->modifiers); | storage.size_per_modifier = evaluate_fmodifiers_storage_size_per_modifier(&fcu->modifiers); | ||||
| storage.buffer = alloca(storage.modifier_count * storage.size_per_modifier); | storage.buffer = alloca(storage.modifier_count * storage.size_per_modifier); | ||||
| devaltime = evaluate_time_fmodifiers(&storage, &fcu->modifiers, fcu, cvalue, evaltime); | const float devaltime = evaluate_time_fmodifiers( | ||||
| &storage, &fcu->modifiers, fcu, cvalue, evaltime); | |||||
| /* Evaluate curve-data | /* Evaluate curve-data | ||||
| * - 'devaltime' instead of 'evaltime', as this is the time that the last time-modifying | * - '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. | * F-Curve modifier on the stack requested the curve to be evaluated at. | ||||
| */ | */ | ||||
| if (fcu->bezt) { | if (fcu->bezt) { | ||||
| cvalue = fcurve_eval_keyframes(fcu, fcu->bezt, devaltime); | cvalue = fcurve_eval_keyframes(fcu, fcu->bezt, devaltime); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | float evaluate_fcurve_driver(PathResolvedRNA *anim_rna, | ||||
| * evaluate it to find value to use as "evaltime" since drivers essentially act as alternative | * 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. */ | * input (i.e. in place of 'time') for F-Curves. */ | ||||
| if (fcu->driver) { | if (fcu->driver) { | ||||
| /* Evaltime now serves as input for the curve. */ | /* Evaltime now serves as input for the curve. */ | ||||
| evaltime = evaluate_driver(anim_rna, fcu->driver, driver_orig, anim_eval_context); | 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... */ | /* Only do a default 1-1 mapping if it's unlikely that anything else will set a value... */ | ||||
| if (fcu->totvert == 0) { | if (fcu->totvert == 0) { | ||||
| FModifier *fcm; | |||||
| bool do_linear = true; | bool do_linear = true; | ||||
| /* Out-of-range F-Modifiers will block, as will those which just plain overwrite the values | /* 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... | * XXX: additive is a bit more dicey; it really depends then if things are in range or not... | ||||
| */ | */ | ||||
| for (fcm = fcu->modifiers.first; fcm; fcm = fcm->next) { | LISTBASE_FOREACH (FModifier *, fcm, &fcu->modifiers) { | ||||
| /* If there are range-restrictions, we must definitely block T36950. */ | /* If there are range-restrictions, we must definitely block T36950. */ | ||||
| if ((fcm->flag & FMODIFIER_FLAG_RANGERESTRICT) == 0 || | if ((fcm->flag & FMODIFIER_FLAG_RANGERESTRICT) == 0 || | ||||
| ((fcm->sfra <= evaltime) && (fcm->efra >= evaltime))) { | (fcm->sfra <= evaltime && fcm->efra >= evaltime)) { | ||||
| /* Within range: here it probably doesn't matter, | /* Within range: here it probably doesn't matter, | ||||
| * though we'd want to check on additive. */ | * though we'd want to check on additive. */ | ||||
| } | } | ||||
| else { | else { | ||||
| /* Outside range: modifier shouldn't contribute to the curve here, | /* Outside range: modifier shouldn't contribute to the curve here, | ||||
| * though it does in other areas, so neither should the driver! */ | * though it does in other areas, so neither should the driver! */ | ||||
| do_linear = false; | do_linear = false; | ||||
| } | } | ||||
| } | } | ||||
| /* Only copy over results if none of the modifiers disagreed with this. */ | /* Only copy over results if none of the modifiers disagreed with this. */ | ||||
| if (do_linear) { | if (do_linear) { | ||||
| cvalue = evaltime; | cvalue = evaltime; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| return evaluate_fcurve_ex(fcu, evaltime, cvalue); | return evaluate_fcurve_ex(fcu, evaltime, cvalue); | ||||
| } | } | ||||
| bool BKE_fcurve_is_empty(const FCurve *fcu) | bool BKE_fcurve_is_empty(const FCurve *fcu) | ||||
| { | { | ||||
| return (fcu->totvert == 0) && (fcu->driver == NULL) && | return fcu->totvert == 0 && fcu->driver == NULL && | ||||
| !list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE); | !list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE); | ||||
| } | } | ||||
| float calculate_fcurve(PathResolvedRNA *anim_rna, | float calculate_fcurve(PathResolvedRNA *anim_rna, | ||||
| FCurve *fcu, | FCurve *fcu, | ||||
| const AnimationEvalContext *anim_eval_context) | const AnimationEvalContext *anim_eval_context) | ||||
| { | { | ||||
| /* Only calculate + set curval (overriding the existing value) if curve has | /* Only calculate + set curval (overriding the existing value) if curve has | ||||
| ▲ Show 20 Lines • Show All 281 Lines • Show Last 20 Lines | |||||