Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/fcurve.c
| Show First 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | |||||
| /* Frees the F-Curve itself too, so make sure BLI_remlink is called before calling this... */ | /* Frees the F-Curve itself too, so make sure BLI_remlink is called before calling this... */ | ||||
| void BKE_fcurve_free(FCurve *fcu) | void BKE_fcurve_free(FCurve *fcu) | ||||
| { | { | ||||
| if (fcu == NULL) { | if (fcu == NULL) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* free curve data */ | /* Free curve data. */ | ||||
| MEM_SAFE_FREE(fcu->bezt); | MEM_SAFE_FREE(fcu->bezt); | ||||
| MEM_SAFE_FREE(fcu->fpt); | MEM_SAFE_FREE(fcu->fpt); | ||||
| /* free RNA-path, as this were allocated when getting the path string */ | /* Free RNA-path, as this were allocated when getting the path string. */ | ||||
| MEM_SAFE_FREE(fcu->rna_path); | MEM_SAFE_FREE(fcu->rna_path); | ||||
| /* free extra data - i.e. modifiers, and driver */ | /* Free extra data - i.e. modifiers, and driver. */ | ||||
| fcurve_free_driver(fcu); | fcurve_free_driver(fcu); | ||||
| free_fmodifiers(&fcu->modifiers); | free_fmodifiers(&fcu->modifiers); | ||||
| /* free f-curve itself */ | /* Free the f-curve itself. */ | ||||
| MEM_freeN(fcu); | MEM_freeN(fcu); | ||||
| } | } | ||||
| /* Frees a list of F-Curves */ | /* Frees a list of F-Curves. */ | ||||
| void BKE_fcurves_free(ListBase *list) | void BKE_fcurves_free(ListBase *list) | ||||
| { | { | ||||
| FCurve *fcu, *fcn; | 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) { | for (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 | ||||
| * \{ */ | * \{ */ | ||||
| /* duplicate an F-Curve */ | /* Duplicate a F-Curve. */ | ||||
| FCurve *BKE_fcurve_copy(const FCurve *fcu) | FCurve *BKE_fcurve_copy(const FCurve *fcu) | ||||
| { | { | ||||
| FCurve *fcu_d; | 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); | 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; | ||||
| } | } | ||||
| /* duplicate a list of F-Curves */ | /* Duplicate a list of F-Curves. */ | ||||
| void BKE_fcurves_copy(ListBase *dst, ListBase *src) | void BKE_fcurves_copy(ListBase *dst, ListBase *src) | ||||
| { | { | ||||
| FCurve *dfcu, *sfcu; | 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) { | for (sfcu = src->first; sfcu; sfcu = sfcu->next) { | ||||
| dfcu = BKE_fcurve_copy(sfcu); | dfcu = BKE_fcurve_copy(sfcu); | ||||
| BLI_addtail(dst, dfcu); | BLI_addtail(dst, dfcu); | ||||
| } | } | ||||
| } | } | ||||
| /** Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of | /** Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of | ||||
| * `IDTypeInfo` structure). */ | * `IDTypeInfo` structure). */ | ||||
| Show All 24 Lines | switch (fcm->type) { | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* ----------------- Finding F-Curves -------------------------- */ | /* ----------------- Finding F-Curves -------------------------- */ | ||||
| /* high level function to get an fcurve from C without having the rna */ | /* High level function to get an fcurve from C without having the RNA. */ | ||||
| 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; | FCurve *fcu = NULL; | ||||
| /* rna vars */ | /* Rna vars */ | ||||
| PointerRNA ptr; | PointerRNA ptr; | ||||
| PropertyRNA *prop; | PropertyRNA *prop; | ||||
| char *path; | 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); | path = RNA_path_from_ID_to_property(&ptr, prop); | ||||
| if (path == NULL) { | if (path == NULL) { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| /* animation takes priority over drivers */ | /* Animation takes priority over drivers. */ | ||||
| if (adt->action && adt->action->curves.first) { | if (adt->action && adt->action->curves.first) { | ||||
| fcu = BKE_fcurve_find(&adt->action->curves, path, index); | fcu = BKE_fcurve_find(&adt->action->curves, path, index); | ||||
| } | } | ||||
| /* if not animated, check if driven */ | /* If not animated, check if driven. */ | ||||
| if (fcu == NULL && adt->drivers.first) { | if (fcu == NULL && adt->drivers.first) { | ||||
| fcu = BKE_fcurve_find(&adt->drivers, path, index); | fcu = BKE_fcurve_find(&adt->drivers, path, index); | ||||
| if (fcu && r_driven) { | if (fcu && r_driven) { | ||||
| *r_driven = true; | *r_driven = true; | ||||
| } | } | ||||
| fcu = NULL; | fcu = NULL; | ||||
| } | } | ||||
| MEM_freeN(path); | MEM_freeN(path); | ||||
| return fcu; | return fcu; | ||||
| } | } | ||||
| /* Find the F-Curve affecting the given RNA-access path + index, | /* Find the F-Curve affecting the given RNA-access path + index, | ||||
| * in the list of F-Curves provided. */ | * in the list of F-Curves provided. */ | ||||
| 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; | 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) { | for (fcu = list->first; 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)) { | ||||
| /* now check indices */ | /* Now check indices. */ | ||||
| if (fcu->array_index == array_index) { | if (fcu->array_index == array_index) { | ||||
| return fcu; | return fcu; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* return */ | |||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| /* quick way to loop over all fcurves of a given 'path' */ | /* Quick way to loop over all fcurves of a given 'path'. */ | ||||
| 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; | 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 (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 */ | |||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| /** | /** | ||||
| * Get list of LinkData's containing pointers to the F-Curves | * Get list of LinkData's containing pointers to the F-Curves | ||||
| * which control the types of data indicated. | * which control the types of data indicated. | ||||
| * | * | ||||
| * Lists... | * Lists... | ||||
| * - dst: list of LinkData's matching the criteria returned. | * - dst: list of LinkData's matching the criteria returned. | ||||
| * List must be freed after use, and is assumed to be empty when passed. | * List must be freed after use, and is assumed to be empty when passed. | ||||
| * - src: list of F-Curves to search through | * - src: list of F-Curves to search through | ||||
| * Filters... | * Filters... | ||||
| * - dataPrefix: i.e. 'pose.bones[' or 'nodes[' | * - dataPrefix: i.e. 'pose.bones[' or 'nodes[' | ||||
| * - dataName: name of entity within "" immediately following the prefix | * - dataName: name of entity within "" immediately following the prefix | ||||
| */ | */ | ||||
| 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; | 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; | ||||
| } | } | ||||
| /* search each F-Curve one by one */ | /* Search each F-Curve one by one. */ | ||||
| for (fcu = src->first; fcu; fcu = fcu->next) { | for (fcu = src->first; fcu; fcu = fcu->next) { | ||||
| /* check if quoted string matches the path */ | /* Check if quoted string matches the path. */ | ||||
| if (fcu->rna_path == NULL || !strstr(fcu->rna_path, dataPrefix)) { | if (fcu->rna_path == NULL || !strstr(fcu->rna_path, dataPrefix)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| char *quotedName = BLI_str_quoted_substrN(fcu->rna_path, dataPrefix); | char *quotedName = BLI_str_quoted_substrN(fcu->rna_path, dataPrefix); | ||||
| if (quotedName == NULL) { | if (quotedName == NULL) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* check if the quoted name matches the required name */ | /* Check if the quoted name matches the required name. */ | ||||
| if (STREQ(quotedName, dataName)) { | if (STREQ(quotedName, dataName)) { | ||||
| LinkData *ld = MEM_callocN(sizeof(LinkData), __func__); | LinkData *ld = MEM_callocN(sizeof(LinkData), __func__); | ||||
| ld->data = fcu; | ld->data = fcu; | ||||
| BLI_addtail(dst, ld); | BLI_addtail(dst, ld); | ||||
| matches++; | matches++; | ||||
| } | } | ||||
| /* always free the quoted string, since it needs freeing */ | /* Always free the quoted string, since it needs freeing. */ | ||||
| MEM_freeN(quotedName); | MEM_freeN(quotedName); | ||||
| } | } | ||||
| /* return the number of matches */ | /* Return the number of matches. */ | ||||
| return matches; | return matches; | ||||
| } | } | ||||
| FCurve *BKE_fcurve_find_by_rna(PointerRNA *ptr, | FCurve *BKE_fcurve_find_by_rna(PointerRNA *ptr, | ||||
| PropertyRNA *prop, | PropertyRNA *prop, | ||||
| int rnaindex, | int rnaindex, | ||||
| AnimData **r_adt, | AnimData **r_adt, | ||||
| bAction **r_action, | bAction **r_action, | ||||
| Show All 35 Lines | if (BKE_nlastrip_has_curves_for_property(ptr, prop)) { | ||||
| */ | */ | ||||
| *r_special = true; | *r_special = true; | ||||
| /* The F-Curve either exists or it doesn't here... */ | /* The F-Curve either exists or it doesn't here... */ | ||||
| fcu = BKE_fcurve_find(&strip->fcurves, RNA_property_identifier(prop), rnaindex); | fcu = BKE_fcurve_find(&strip->fcurves, RNA_property_identifier(prop), rnaindex); | ||||
| return fcu; | return fcu; | ||||
| } | } | ||||
| /* there must be some RNA-pointer + property combon */ | /* There must be some RNA-pointer + property combo. */ | ||||
| if (prop && tptr.owner_id && RNA_property_animateable(&tptr, prop)) { | if (prop && tptr.owner_id && RNA_property_animateable(&tptr, prop)) { | ||||
| AnimData *adt = BKE_animdata_from_id(tptr.owner_id); | AnimData *adt = BKE_animdata_from_id(tptr.owner_id); | ||||
| int step = ( | int step = ( | ||||
| /* Always 1 in case we have no context (can't check in 'ancestors' of given RNA ptr). */ | /* Always 1 in case we have no context (can't check in 'ancestors' of given RNA ptr). */ | ||||
| C ? 2 : 1); | C ? 2 : 1); | ||||
| char *path = NULL; | char *path = NULL; | ||||
| if (!adt && C) { | if (!adt && C) { | ||||
| path = BKE_animdata_driver_path_hack(C, &tptr, prop, NULL); | path = BKE_animdata_driver_path_hack(C, &tptr, prop, NULL); | ||||
| adt = BKE_animdata_from_id(tptr.owner_id); | adt = BKE_animdata_from_id(tptr.owner_id); | ||||
| step--; | step--; | ||||
| } | } | ||||
| /* Standard F-Curve - Animation (Action) or Drivers */ | /* Standard F-Curve - Animation (Action) or Drivers. */ | ||||
| while (adt && step--) { | while (adt && step--) { | ||||
| if ((adt->action == NULL || adt->action->curves.first == NULL) && | if ((adt->action == NULL || adt->action->curves.first == NULL) && | ||||
| (adt->drivers.first == NULL)) { | (adt->drivers.first == NULL)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* XXX this function call can become a performance bottleneck */ | /* XXX This function call can become a performance bottleneck. */ | ||||
| if (step) { | if (step) { | ||||
| path = RNA_path_from_ID_to_property(&tptr, prop); | path = RNA_path_from_ID_to_property(&tptr, prop); | ||||
| } | } | ||||
| if (path == NULL) { | if (path == NULL) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* XXX: the logic here is duplicated with a function up above | /* XXX: The logic here is duplicated with a function up above. */ | ||||
| * animation takes priority over drivers. */ | /* animation takes priority over drivers. */ | ||||
| if (adt->action && adt->action->curves.first) { | if (adt->action && adt->action->curves.first) { | ||||
| fcu = BKE_fcurve_find(&adt->action->curves, path, rnaindex); | fcu = BKE_fcurve_find(&adt->action->curves, path, rnaindex); | ||||
| if (fcu && r_action) { | if (fcu && r_action) { | ||||
| *r_action = adt->action; | *r_action = adt->action; | ||||
| } | } | ||||
| } | } | ||||
| /* if not animated, check if driven */ | /* If not animated, check if driven. */ | ||||
| if (!fcu && (adt->drivers.first)) { | if (!fcu && (adt->drivers.first)) { | ||||
| fcu = BKE_fcurve_find(&adt->drivers, path, rnaindex); | fcu = BKE_fcurve_find(&adt->drivers, path, rnaindex); | ||||
| if (fcu) { | if (fcu) { | ||||
| if (r_animdata) { | if (r_animdata) { | ||||
| *r_animdata = adt; | *r_animdata = adt; | ||||
| } | } | ||||
| *r_driven = true; | *r_driven = true; | ||||
| Show All 37 Lines | |||||
| * Returns the index to insert at (data already at that index will be offset if replace is 0) | * 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( | static int BKE_fcurve_bezt_binarysearch_index_ex( | ||||
| BezTriple array[], float frame, int arraylen, float threshold, bool *r_replace) | BezTriple array[], float frame, int arraylen, float threshold, bool *r_replace) | ||||
| { | { | ||||
| int start = 0, end = arraylen; | int start = 0, end = arraylen; | ||||
| int loopbreaker = 0, maxloop = arraylen * 2; | int loopbreaker = 0, maxloop = arraylen * 2; | ||||
| /* 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; | 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]; | 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; | ||||
| } | } | ||||
| /* 'Last' Keyframe */ | /* 'Last' Keyframe */ | ||||
| framenum = array[(arraylen - 1)].vec[1][0]; | framenum = array[(arraylen - 1)].vec[1][0]; | ||||
| if (IS_EQT(frame, framenum, threshold)) { | if (IS_EQT(frame, framenum, threshold)) { | ||||
| *r_replace = true; | *r_replace = true; | ||||
| return (arraylen - 1); | return (arraylen - 1); | ||||
| } | } | ||||
| if (frame > framenum) { | if (frame > framenum) { | ||||
| return arraylen; | return arraylen; | ||||
| } | } | ||||
| /* 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); | int mid = start + ((end - start) / 2); | ||||
| float midfra = array[mid].vec[1][0]; | 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. */ | ||||
| if (frame > midfra) { | if (frame > midfra) { | ||||
| start = mid + 1; | start = mid + 1; | ||||
| } | } | ||||
| else if (frame < midfra) { | else if (frame < midfra) { | ||||
| end = mid - 1; | end = mid - 1; | ||||
| } | } | ||||
| } | } | ||||
| /* print error if loop-limit exceeded */ | /* Print error if loop-limit exceeded. */ | ||||
| if (loopbreaker == (maxloop - 1)) { | if (loopbreaker == (maxloop - 1)) { | ||||
| CLOG_ERROR(&LOG, "search taking too long"); | CLOG_ERROR(&LOG, "search taking too long"); | ||||
| /* include debug info */ | /* Include debug info. */ | ||||
| CLOG_ERROR(&LOG, | CLOG_ERROR(&LOG, | ||||
| "\tround = %d: start = %d, end = %d, arraylen = %d", | "\tround = %d: start = %d, end = %d, arraylen = %d", | ||||
| loopbreaker, | loopbreaker, | ||||
| start, | start, | ||||
| end, | end, | ||||
| arraylen); | arraylen); | ||||
| } | } | ||||
| /* not found, so return where to place it */ | /* Not found, so return where to place it. */ | ||||
| return start; | return start; | ||||
| } | } | ||||
| /* Binary search algorithm for finding where to insert BezTriple. (for use by insert_bezt_fcurve) | /* Binary search algorithm for finding where to insert BezTriple. (for use by insert_bezt_fcurve) | ||||
| * Returns the index to insert at (data already at that index will be offset if replace is 0) | * Returns the index to insert at (data already at that index will be offset if replace is 0) | ||||
| */ | */ | ||||
| int BKE_fcurve_bezt_binarysearch_index(BezTriple array[], | int BKE_fcurve_bezt_binarysearch_index(BezTriple array[], | ||||
| float frame, | float frame, | ||||
| int arraylen, | int arraylen, | ||||
| bool *r_replace) | bool *r_replace) | ||||
| { | { | ||||
| /* this is just a wrapper which uses the default threshold */ | /* This is just a wrapper which uses the default threshold. */ | ||||
| return BKE_fcurve_bezt_binarysearch_index_ex( | return BKE_fcurve_bezt_binarysearch_index_ex( | ||||
| array, frame, arraylen, BEZT_BINARYSEARCH_THRESH, r_replace); | array, frame, arraylen, BEZT_BINARYSEARCH_THRESH, r_replace); | ||||
| } | } | ||||
| /* ...................................... */ | /* ...................................... */ | ||||
| /* helper for calc_fcurve_* functions -> find first and last BezTriple to be used */ | /* Helper for calc_fcurve_* functions -> find first and last BezTriple to be used. */ | ||||
| static short get_fcurve_end_keyframes(FCurve *fcu, | static short get_fcurve_end_keyframes(FCurve *fcu, | ||||
| BezTriple **first, | BezTriple **first, | ||||
| BezTriple **last, | BezTriple **last, | ||||
| const bool do_sel_only) | const bool do_sel_only) | ||||
| { | { | ||||
| bool found = false; | bool found = false; | ||||
| /* init outputs */ | /* Init outputs. */ | ||||
| *first = NULL; | *first = NULL; | ||||
| *last = NULL; | *last = NULL; | ||||
| /* 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; | BezTriple *bezt; | ||||
| /* find first selected */ | /* Find first selected. */ | ||||
| bezt = fcu->bezt; | 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; | ||||
| } | } | ||||
| } | } | ||||
| /* find last selected */ | /* Find last selected. */ | ||||
| bezt = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, fcu->totvert); | bezt = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, fcu->totvert); | ||||
| 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)) { | ||||
| *last = bezt; | *last = bezt; | ||||
| found = true; | found = true; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| /* just full array */ | /* Use the whole array. */ | ||||
| *first = fcu->bezt; | *first = fcu->bezt; | ||||
| *last = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, fcu->totvert); | *last = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, fcu->totvert); | ||||
| found = true; | found = true; | ||||
| } | } | ||||
| return found; | return found; | ||||
| } | } | ||||
| /* Calculate the extents of F-Curve's data */ | /* Calculate the extents of F-Curve's data. */ | ||||
| bool BKE_fcurve_calc_bounds(FCurve *fcu, | bool BKE_fcurve_calc_bounds(FCurve *fcu, | ||||
| float *xmin, | float *xmin, | ||||
| float *xmax, | float *xmax, | ||||
| float *ymin, | float *ymin, | ||||
| float *ymax, | float *ymax, | ||||
| const bool do_sel_only, | const bool do_sel_only, | ||||
| const bool include_handles) | const bool include_handles) | ||||
| { | { | ||||
| float xminv = 999999999.0f, xmaxv = -999999999.0f; | float xminv = 999999999.0f, xmaxv = -999999999.0f; | ||||
| float yminv = 999999999.0f, ymaxv = -999999999.0f; | float yminv = 999999999.0f, ymaxv = -999999999.0f; | ||||
| bool foundvert = false; | bool foundvert = false; | ||||
| if (fcu->totvert) { | if (fcu->totvert) { | ||||
| if (fcu->bezt) { | if (fcu->bezt) { | ||||
| BezTriple *bezt_first = NULL, *bezt_last = NULL; | BezTriple *bezt_first = NULL, *bezt_last = NULL; | ||||
| if (xmin || xmax) { | if (xmin || xmax) { | ||||
| /* get endpoint keyframes */ | /* Get endpoint keyframes. */ | ||||
| foundvert = get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only); | foundvert = get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only); | ||||
| if (bezt_first) { | if (bezt_first) { | ||||
| BLI_assert(bezt_last != NULL); | BLI_assert(bezt_last != NULL); | ||||
| if (include_handles) { | if (include_handles) { | ||||
| xminv = min_fff(xminv, bezt_first->vec[0][0], bezt_first->vec[1][0]); | xminv = min_fff(xminv, bezt_first->vec[0][0], bezt_first->vec[1][0]); | ||||
| xmaxv = max_fff(xmaxv, bezt_last->vec[1][0], bezt_last->vec[2][0]); | xmaxv = max_fff(xmaxv, bezt_last->vec[1][0], bezt_last->vec[2][0]); | ||||
| } | } | ||||
| else { | else { | ||||
| xminv = min_ff(xminv, bezt_first->vec[1][0]); | xminv = min_ff(xminv, bezt_first->vec[1][0]); | ||||
| xmaxv = max_ff(xmaxv, bezt_last->vec[1][0]); | xmaxv = max_ff(xmaxv, bezt_last->vec[1][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) { | ||||
| BezTriple *bezt, *prevbezt = NULL; | BezTriple *bezt, *prevbezt = NULL; | ||||
| int i; | int i; | ||||
| for (bezt = fcu->bezt, i = 0; i < fcu->totvert; prevbezt = bezt, bezt++, i++) { | for (bezt = fcu->bezt, i = 0; i < fcu->totvert; prevbezt = bezt, bezt++, i++) { | ||||
| if ((do_sel_only == false) || BEZT_ISSEL_ANY(bezt)) { | if ((do_sel_only == false) || BEZT_ISSEL_ANY(bezt)) { | ||||
| /* keyframe itself */ | /* Keyframe itself. */ | ||||
| yminv = min_ff(yminv, bezt->vec[1][1]); | yminv = min_ff(yminv, bezt->vec[1][1]); | ||||
| ymaxv = max_ff(ymaxv, bezt->vec[1][1]); | ymaxv = max_ff(ymaxv, bezt->vec[1][1]); | ||||
| if (include_handles) { | if (include_handles) { | ||||
| /* left handle - only if applicable | /* Left handle - only if applicable. | ||||
| * NOTE: for the very first keyframe, | * NOTE: for the very first keyframe, | ||||
| * the left handle actually has no bearings on anything. */ | * the left handle actually has no bearings on anything. */ | ||||
| if (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ)) { | if (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ)) { | ||||
| yminv = min_ff(yminv, bezt->vec[0][1]); | yminv = min_ff(yminv, bezt->vec[0][1]); | ||||
| ymaxv = max_ff(ymaxv, bezt->vec[0][1]); | ymaxv = max_ff(ymaxv, bezt->vec[0][1]); | ||||
| } | } | ||||
| /* right handle - only if applicable */ | /* Right handle - only if applicable. */ | ||||
| if (bezt->ipo == BEZT_IPO_BEZ) { | if (bezt->ipo == BEZT_IPO_BEZ) { | ||||
| yminv = min_ff(yminv, bezt->vec[2][1]); | yminv = min_ff(yminv, bezt->vec[2][1]); | ||||
| ymaxv = max_ff(ymaxv, bezt->vec[2][1]); | ymaxv = max_ff(ymaxv, bezt->vec[2][1]); | ||||
| } | } | ||||
| } | } | ||||
| foundvert = true; | foundvert = true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| else if (fcu->fpt) { | 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; | FPoint *fpt; | ||||
| int i; | int i; | ||||
| for (fpt = fcu->fpt, i = 0; i < fcu->totvert; fpt++, i++) { | for (fpt = fcu->fpt, i = 0; i < fcu->totvert; fpt++, i++) { | ||||
| if (fpt->vec[1] < yminv) { | if (fpt->vec[1] < yminv) { | ||||
| yminv = fpt->vec[1]; | yminv = fpt->vec[1]; | ||||
| } | } | ||||
| Show All 40 Lines | else { | ||||
| if (ymax) { | if (ymax) { | ||||
| *ymax = 1.0f; | *ymax = 1.0f; | ||||
| } | } | ||||
| } | } | ||||
| return foundvert; | return foundvert; | ||||
| } | } | ||||
| /* Calculate the extents of F-Curve's keyframes */ | /* Calculate the extents of F-Curve's keyframes. */ | ||||
| bool BKE_fcurve_calc_range( | bool BKE_fcurve_calc_range( | ||||
| FCurve *fcu, float *start, float *end, const bool do_sel_only, const bool do_min_length) | FCurve *fcu, float *start, float *end, const bool do_sel_only, const bool do_min_length) | ||||
| { | { | ||||
| float min = 999999999.0f, max = -999999999.0f; | float min = 999999999.0f, max = -999999999.0f; | ||||
| bool foundvert = false; | bool foundvert = false; | ||||
| if (fcu->totvert) { | if (fcu->totvert) { | ||||
| if (fcu->bezt) { | if (fcu->bezt) { | ||||
| BezTriple *bezt_first = NULL, *bezt_last = NULL; | BezTriple *bezt_first = NULL, *bezt_last = NULL; | ||||
| /* get endpoint keyframes */ | /* Get endpoint keyframes. */ | ||||
| get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only); | get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only); | ||||
| if (bezt_first) { | if (bezt_first) { | ||||
| BLI_assert(bezt_last != NULL); | BLI_assert(bezt_last != NULL); | ||||
| min = min_ff(min, bezt_first->vec[1][0]); | min = min_ff(min, bezt_first->vec[1][0]); | ||||
| max = max_ff(max, bezt_last->vec[1][0]); | max = max_ff(max, bezt_last->vec[1][0]); | ||||
| foundvert = true; | foundvert = true; | ||||
| } | } | ||||
| } | } | ||||
| else if (fcu->fpt) { | else if (fcu->fpt) { | ||||
| min = min_ff(min, fcu->fpt[0].vec[0]); | min = min_ff(min, fcu->fpt[0].vec[0]); | ||||
| max = max_ff(max, fcu->fpt[fcu->totvert - 1].vec[0]); | max = max_ff(max, fcu->fpt[fcu->totvert - 1].vec[0]); | ||||
| foundvert = true; | foundvert = true; | ||||
| } | } | ||||
| } | } | ||||
| if (foundvert == false) { | if (foundvert == false) { | ||||
| min = max = 0.0f; | min = max = 0.0f; | ||||
| } | } | ||||
| if (do_min_length) { | if (do_min_length) { | ||||
| /* minimum length is 1 frame */ | /* Minimum length is 1 frame. */ | ||||
| if (min == max) { | if (min == max) { | ||||
| max += 1.0f; | max += 1.0f; | ||||
| } | } | ||||
| } | } | ||||
| *start = min; | *start = min; | ||||
| *end = max; | *end = max; | ||||
| ▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | |||||
| * \{ */ | * \{ */ | ||||
| /* Are keyframes on F-Curve of any use? | /* Are keyframes on F-Curve of any use? | ||||
| * Usability of keyframes refers to whether they should be displayed, | * Usability of keyframes refers to whether they should be displayed, | ||||
| * and also whether they will have any influence on the final result. | * and also whether they will have any influence on the final result. | ||||
| */ | */ | ||||
| bool BKE_fcurve_are_keyframes_usable(FCurve *fcu) | bool BKE_fcurve_are_keyframes_usable(FCurve *fcu) | ||||
| { | { | ||||
| /* F-Curve must exist */ | /* F-Curve must exist. */ | ||||
| if (fcu == NULL) { | if (fcu == NULL) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| /* 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; | 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... */ | /* TODO: optionally, only check modifier if it is the active one... (Joshua Leung 2010) */ | ||||
| for (fcm = fcu->modifiers.last; fcm; fcm = fcm->prev) { | for (fcm = fcu->modifiers.last; fcm; fcm = fcm->prev) { | ||||
| /* 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. */ | ||||
| case FMODIFIER_TYPE_CYCLES: | case FMODIFIER_TYPE_CYCLES: | ||||
| case FMODIFIER_TYPE_STEPPED: | case FMODIFIER_TYPE_STEPPED: | ||||
| case FMODIFIER_TYPE_NOISE: | case FMODIFIER_TYPE_NOISE: | ||||
| break; | break; | ||||
| /* sometimes harmful - depending on whether they're "additive" or not */ | /* Sometimes harmful - depending on whether they're "additive" or not. */ | ||||
| case FMODIFIER_TYPE_GENERATOR: { | case FMODIFIER_TYPE_GENERATOR: { | ||||
| FMod_Generator *data = (FMod_Generator *)fcm->data; | FMod_Generator *data = (FMod_Generator *)fcm->data; | ||||
| if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) { | if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| case FMODIFIER_TYPE_FN_GENERATOR: { | case FMODIFIER_TYPE_FN_GENERATOR: { | ||||
| FMod_FunctionGenerator *data = (FMod_FunctionGenerator *)fcm->data; | FMod_FunctionGenerator *data = (FMod_FunctionGenerator *)fcm->data; | ||||
| if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) { | if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| /* always harmful - cannot allow */ | /* Always harmful - cannot allow. */ | ||||
| default: | default: | ||||
| return false; | return false; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* keyframes are usable */ | /* Keyframes are usable. */ | ||||
| return true; | return true; | ||||
| } | } | ||||
| bool BKE_fcurve_is_protected(FCurve *fcu) | bool BKE_fcurve_is_protected(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))); | ||||
| } | } | ||||
| /* Can keyframes be added to F-Curve? | /* Can keyframes be added to F-Curve? | ||||
| * Keyframes can only be added if they are already visible | * Keyframes can only be added if they are already visible. | ||||
| */ | */ | ||||
| bool BKE_fcurve_is_keyframable(FCurve *fcu) | bool BKE_fcurve_is_keyframable(FCurve *fcu) | ||||
| { | { | ||||
| /* F-Curve's keyframes must be "usable" (i.e. visible + have an effect on final result) */ | /* F-Curve's keyframes must be "usable" (i.e. visible + have an effect on final result) */ | ||||
| if (BKE_fcurve_are_keyframes_usable(fcu) == 0) { | if (BKE_fcurve_are_keyframes_usable(fcu) == 0) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| /* F-Curve must currently be editable too */ | /* F-Curve must currently be editable too. */ | ||||
| if (BKE_fcurve_is_protected(fcu)) { | if (BKE_fcurve_is_protected(fcu)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| /* F-Curve is keyframable */ | /* F-Curve is keyframable. */ | ||||
| return true; | return true; | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Keyframe Column Tools | /** \name Keyframe Column Tools | ||||
| * \{ */ | * \{ */ | ||||
| /* add a BezTriple to a column */ | /* Add a BezTriple to a column. */ | ||||
| static void UNUSED_FUNCTION(bezt_add_to_cfra_elem)(ListBase *lb, BezTriple *bezt) | static void UNUSED_FUNCTION(bezt_add_to_cfra_elem)(ListBase *lb, BezTriple *bezt) | ||||
| { | { | ||||
| CfraElem *ce, *cen; | CfraElem *ce, *cen; | ||||
| for (ce = lb->first; ce; ce = ce->next) { | for (ce = lb->first; ce; ce = ce->next) { | ||||
| /* double key? */ | /* Double key? */ | ||||
| if (IS_EQT(ce->cfra, bezt->vec[1][0], BEZT_BINARYSEARCH_THRESH)) { | if (IS_EQT(ce->cfra, bezt->vec[1][0], BEZT_BINARYSEARCH_THRESH)) { | ||||
| if (bezt->f2 & SELECT) { | if (bezt->f2 & SELECT) { | ||||
| ce->sel = bezt->f2; | ce->sel = bezt->f2; | ||||
| } | } | ||||
| return; | return; | ||||
| } | } | ||||
| /* should key be inserted before this column? */ | /* Should key be inserted before this column? */ | ||||
| if (ce->cfra > bezt->vec[1][0]) { | if (ce->cfra > bezt->vec[1][0]) { | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| /* create a new column */ | /* Create a new column */ | ||||
| cen = MEM_callocN(sizeof(CfraElem), "add_to_cfra_elem"); | cen = MEM_callocN(sizeof(CfraElem), "add_to_cfra_elem"); | ||||
| if (ce) { | if (ce) { | ||||
| BLI_insertlinkbefore(lb, ce, cen); | BLI_insertlinkbefore(lb, ce, cen); | ||||
| } | } | ||||
| else { | else { | ||||
| BLI_addtail(lb, cen); | BLI_addtail(lb, cen); | ||||
| } | } | ||||
| Show All 12 Lines | |||||
| * which BezTriples/Keyframe data are ill equipped to do. | * which BezTriples/Keyframe data are ill equipped to do. | ||||
| */ | */ | ||||
| /* Basic sampling callback which acts as a wrapper for evaluate_fcurve() | /* Basic sampling callback which acts as a wrapper for evaluate_fcurve() | ||||
| * 'data' arg here is unneeded here... | * 'data' arg here is unneeded here... | ||||
| */ | */ | ||||
| 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); | ||||
| } | } | ||||
| /* Main API function for creating a set of sampled curve data, given some callback function | /* Main API function for creating a set of sampled curve data, given some callback function | ||||
| * used to retrieve the values to store. | * used to retrieve the values to store. | ||||
| */ | */ | ||||
| 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; | FPoint *fpt, *new_fpt; | ||||
| int cfra; | int cfra; | ||||
| /* sanity checks */ | /* Sanity checks. */ | ||||
| /* TODO: make these tests report errors using reports not CLOG's */ | /* 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"); | 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 (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); | ||||
| } | } | ||||
| if (fcu->fpt) { | if (fcu->fpt) { | ||||
| MEM_freeN(fcu->fpt); | MEM_freeN(fcu->fpt); | ||||
| } | } | ||||
| /* store the samples */ | /* Store the samples. */ | ||||
| fcu->bezt = NULL; | fcu->bezt = NULL; | ||||
| fcu->fpt = new_fpt; | fcu->fpt = new_fpt; | ||||
| fcu->totvert = end - start + 1; | fcu->totvert = end - start + 1; | ||||
| } | } | ||||
| /* ***************************** F-Curve Sanity ********************************* */ | /* ***************************** F-Curve Sanity ********************************* */ | ||||
| /* The functions here are used in various parts of Blender, usually after some editing | /* 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 | * of keyframe data has occurred. They ensure that keyframe data is properly ordered and | ||||
| * that the handles are correctly | * that the handles are correct. | ||||
| */ | */ | ||||
| /* Checks if the F-Curve has a Cycles modifier, and returns the type of the cycle behavior. */ | /* Checks if the F-Curve has a Cycles modifier, and returns the type of the cycle behavior. */ | ||||
| eFCU_Cycle_Type BKE_fcurve_get_cycle_type(FCurve *fcu) | eFCU_Cycle_Type BKE_fcurve_get_cycle_type(FCurve *fcu) | ||||
| { | { | ||||
| FModifier *fcm = fcu->modifiers.first; | FModifier *fcm = fcu->modifiers.first; | ||||
| if (!fcm || fcm->type != FMODIFIER_TYPE_CYCLES) { | if (!fcm || fcm->type != FMODIFIER_TYPE_CYCLES) { | ||||
| ▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | |||||
| * (if caller does not operate on selection). | * (if caller does not operate on selection). | ||||
| */ | */ | ||||
| void calchandles_fcurve_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag) | void calchandles_fcurve_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag) | ||||
| { | { | ||||
| BezTriple *bezt, *prev, *next; | 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); | bool cycle = BKE_fcurve_is_cyclic(fcu) && BEZT_IS_AUTOH(first) && BEZT_IS_AUTOH(last); | ||||
| /* get initial pointers */ | /* Get initial pointers. */ | ||||
| bezt = fcu->bezt; | bezt = fcu->bezt; | ||||
| prev = cycle_offset_triple(cycle, &tmp, &fcu->bezt[fcu->totvert - 2], last, first); | prev = cycle_offset_triple(cycle, &tmp, &fcu->bezt[fcu->totvert - 2], last, first); | ||||
| next = (bezt + 1); | 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]) { | ||||
| bezt->vec[2][0] = bezt->vec[1][0]; | bezt->vec[2][0] = bezt->vec[1][0]; | ||||
| } | } | ||||
| /* calculate auto-handles */ | /* Calculate auto-handles. */ | ||||
| BKE_nurb_handle_calc_ex(bezt, prev, next, handle_sel_flag, true, fcu->auto_smoothing); | BKE_nurb_handle_calc_ex(bezt, prev, next, handle_sel_flag, true, fcu->auto_smoothing); | ||||
| /* for automatic ease in and out */ | /* For automatic ease in and out. */ | ||||
| if (BEZT_IS_AUTOH(bezt) && !cycle) { | if (BEZT_IS_AUTOH(bezt) && !cycle) { | ||||
| /* only do this on first or last beztriple */ | /* Only do this on first or last beztriple. */ | ||||
| if ((a == 0) || (a == fcu->totvert - 1)) { | if ((a == 0) || (a == fcu->totvert - 1)) { | ||||
| /* set both handles to have same horizontal value as keyframe */ | /* Set both handles to have same horizontal value as keyframe. */ | ||||
| if (fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) { | if (fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) { | ||||
| bezt->vec[0][1] = bezt->vec[2][1] = bezt->vec[1][1]; | 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 */ | /* Remember that these keyframes are special, they don't need to be adjusted. */ | ||||
| bezt->f5 = HD_AUTOTYPE_SPECIAL; | bezt->f5 = HD_AUTOTYPE_SPECIAL; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* avoid total smoothing failure on duplicate keyframes (can happen during grab) */ | /* Avoid total smoothing failure on duplicate keyframes (can happen during grab). */ | ||||
| if (prev && prev->vec[1][0] >= bezt->vec[1][0]) { | if (prev && prev->vec[1][0] >= bezt->vec[1][0]) { | ||||
| prev->f5 = bezt->f5 = HD_AUTOTYPE_SPECIAL; | prev->f5 = bezt->f5 = HD_AUTOTYPE_SPECIAL; | ||||
| } | } | ||||
| /* advance pointers for next iteration */ | /* Advance pointers for next iteration. */ | ||||
| prev = bezt; | prev = bezt; | ||||
| if (a == 1) { | if (a == 1) { | ||||
| next = cycle_offset_triple(cycle, &tmp, &fcu->bezt[1], first, last); | next = cycle_offset_triple(cycle, &tmp, &fcu->bezt[1], first, last); | ||||
| } | } | ||||
| else { | else { | ||||
| next++; | next++; | ||||
| } | } | ||||
| bezt++; | bezt++; | ||||
| } | } | ||||
| /* if cyclic extrapolation and Auto Clamp has triggered, ensure it is symmetric */ | /* If cyclic extrapolation and Auto Clamp has triggered, ensure it is symmetric. */ | ||||
| if (cycle && (first->f5 != HD_AUTOTYPE_NORMAL || last->f5 != HD_AUTOTYPE_NORMAL)) { | if (cycle && (first->f5 != HD_AUTOTYPE_NORMAL || last->f5 != HD_AUTOTYPE_NORMAL)) { | ||||
| first->vec[0][1] = first->vec[2][1] = first->vec[1][1]; | first->vec[0][1] = first->vec[2][1] = first->vec[1][1]; | ||||
| last->vec[0][1] = last->vec[2][1] = last->vec[1][1]; | last->vec[0][1] = last->vec[2][1] = last->vec[1][1]; | ||||
| first->f5 = last->f5 = HD_AUTOTYPE_SPECIAL; | first->f5 = last->f5 = HD_AUTOTYPE_SPECIAL; | ||||
| } | } | ||||
| /* do a second pass for auto handle: compute the handle to have 0 acceleration step */ | /* Do a second pass for auto handle: compute the handle to have 0 acceleration step. */ | ||||
| if (fcu->auto_smoothing != FCURVE_SMOOTH_NONE) { | if (fcu->auto_smoothing != FCURVE_SMOOTH_NONE) { | ||||
| BKE_nurb_handle_smooth_fcurve(fcu->bezt, fcu->totvert, cycle); | BKE_nurb_handle_smooth_fcurve(fcu->bezt, fcu->totvert, cycle); | ||||
| } | } | ||||
| } | } | ||||
| /** | /** | ||||
| * This function recalculates the handles of an F-Curve. Acts based on selection with `SELECT` | * This function recalculates the handles of an F-Curve. Acts based on selection with `SELECT` | ||||
| * flag. To use a different flag, use #calchandles_fcurve_ex(). | * flag. To use a different flag, use #calchandles_fcurve_ex(). | ||||
| Show All 16 Lines | |||||
| * \param use_handle: Check selection state of individual handles, otherwise always update both | * \param use_handle: Check selection state of individual handles, otherwise always update both | ||||
| * handles if the key is selected. | * handles if the key is selected. | ||||
| */ | */ | ||||
| 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; | BezTriple *bezt; | ||||
| unsigned int a; | unsigned int 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. */ | ||||
| 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. */ | ||||
| calchandles_fcurve_ex(fcu, sel_flag); | calchandles_fcurve_ex(fcu, sel_flag); | ||||
| } | } | ||||
| /* This function sorts BezTriples so that they are arranged in chronological order, | /* This function sorts BezTriples so that they are arranged in chronological order, | ||||
| * as tools working on F-Curves expect that the BezTriples are in order. | * as tools working on F-Curves expect that the BezTriples are in order. | ||||
| */ | */ | ||||
| void sort_time_fcurve(FCurve *fcu) | void sort_time_fcurve(FCurve *fcu) | ||||
| { | { | ||||
| if (fcu->bezt == NULL) { | if (fcu->bezt == NULL) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* keep adjusting order of beztriples until nothing moves (bubble-sort) */ | /* Keep adjusting order of beztriples until nothing moves (bubble-sort). */ | ||||
| BezTriple *bezt; | BezTriple *bezt; | ||||
| uint a; | uint a; | ||||
| bool ok = true; | bool ok = true; | ||||
| while (ok) { | while (ok) { | ||||
| ok = 0; | ok = 0; | ||||
| /* currently, will only be needed when there are beztriples */ | /* Currently, will only be needed when there are beztriples. */ | ||||
| /* loop over ALL points to adjust position in array and recalculate handles */ | /* Loop over ALL points to adjust position in array and recalculate handles. */ | ||||
| for (a = 0, bezt = fcu->bezt; a < fcu->totvert; a++, bezt++) { | for (a = 0, bezt = fcu->bezt; a < fcu->totvert; a++, bezt++) { | ||||
| /* check if thee's a next beztriple which we could try to swap with current */ | /* Check if thee's a next beztriple which we could try to swap with current. */ | ||||
| if (a < (fcu->totvert - 1)) { | if (a < (fcu->totvert - 1)) { | ||||
| /* swap if one is after the other (and indicate that order has changed) */ | /* Swap if one is after the other (and indicate that order has changed). */ | ||||
| if (bezt->vec[1][0] > (bezt + 1)->vec[1][0]) { | if (bezt->vec[1][0] > (bezt + 1)->vec[1][0]) { | ||||
| SWAP(BezTriple, *bezt, *(bezt + 1)); | SWAP(BezTriple, *bezt, *(bezt + 1)); | ||||
| ok = 1; | ok = 1; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| for (a = 0, bezt = fcu->bezt; a < fcu->totvert; a++, bezt++) { | 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 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])) { | 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 handles if they have switched sides for some reason. */ | ||||
| swap_v2_v2(bezt->vec[0], bezt->vec[2]); | swap_v2_v2(bezt->vec[0], bezt->vec[2]); | ||||
| } | } | ||||
| else { | else { | ||||
| /* clamp handles */ | /* Clamp handles. */ | ||||
| CLAMP_MAX(bezt->vec[0][0], bezt->vec[1][0]); | CLAMP_MAX(bezt->vec[0][0], bezt->vec[1][0]); | ||||
| CLAMP_MIN(bezt->vec[2][0], bezt->vec[1][0]); | CLAMP_MIN(bezt->vec[2][0], bezt->vec[1][0]); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* This function tests if any BezTriples are out of order, thus requiring a sort */ | /* This function tests if any BezTriples are out of order, thus requiring a sort. */ | ||||
| bool test_time_fcurve(FCurve *fcu) | bool test_time_fcurve(FCurve *fcu) | ||||
| { | { | ||||
| unsigned int a; | unsigned int a; | ||||
| /* sanity checks */ | /* Sanity checks. */ | ||||
| if (fcu == NULL) { | if (fcu == NULL) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| /* currently, only need to test beztriples */ | /* Currently, only need to test beztriples. */ | ||||
| if (fcu->bezt) { | if (fcu->bezt) { | ||||
| BezTriple *bezt; | BezTriple *bezt; | ||||
| /* loop through all BezTriples, stopping when one exceeds the one after it */ | /* Loop through all BezTriples, stopping when one exceeds the one after it. */ | ||||
| for (a = 0, bezt = fcu->bezt; a < (fcu->totvert - 1); a++, bezt++) { | for (a = 0, bezt = fcu->bezt; a < (fcu->totvert - 1); a++, bezt++) { | ||||
| if (bezt->vec[1][0] > (bezt + 1)->vec[1][0]) { | if (bezt->vec[1][0] > (bezt + 1)->vec[1][0]) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| else if (fcu->fpt) { | else if (fcu->fpt) { | ||||
| FPoint *fpt; | FPoint *fpt; | ||||
| /* loop through all FPoints, stopping when one exceeds the one after it */ | /* Loop through all FPoints, stopping when one exceeds the one after it. */ | ||||
| for (a = 0, fpt = fcu->fpt; a < (fcu->totvert - 1); a++, fpt++) { | for (a = 0, fpt = fcu->fpt; a < (fcu->totvert - 1); a++, fpt++) { | ||||
| if (fpt->vec[0] > (fpt + 1)->vec[0]) { | if (fpt->vec[0] > (fpt + 1)->vec[0]) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* none need any swapping */ | /* None need any swapping. */ | ||||
| return false; | return false; | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name F-Curve Calculations | /** \name F-Curve Calculations | ||||
| * \{ */ | * \{ */ | ||||
| Show All 12 Lines | void BKE_fcurve_correct_bezpart(const float v1[2], float v2[2], float v3[2], const float v4[2]) | ||||
| /* Calculate handle deltas. */ | /* Calculate handle deltas. */ | ||||
| h1[0] = v1[0] - v2[0]; | h1[0] = v1[0] - v2[0]; | ||||
| h1[1] = v1[1] - v2[1]; | h1[1] = v1[1] - v2[1]; | ||||
| h2[0] = v4[0] - v3[0]; | h2[0] = v4[0] - v3[0]; | ||||
| h2[1] = v4[1] - v3[1]; | h2[1] = v4[1] - v3[1]; | ||||
| /* Calculate distances: | /* Calculate distances: | ||||
| * - len = span of time between keyframes | * - len = Span of time between keyframes. | ||||
| * - len1 = length of handle of start key | * - len1 = Length of handle of start key. | ||||
| * - len2 = length of handle of end key | * - len2 = Length of handle of end key. | ||||
| */ | */ | ||||
| len = v4[0] - v1[0]; | len = v4[0] - v1[0]; | ||||
| len1 = fabsf(h1[0]); | len1 = fabsf(h1[0]); | ||||
| len2 = fabsf(h2[0]); | len2 = fabsf(h2[0]); | ||||
| /* If the handles have no length, no need to do any corrections. */ | /* If the handles have no length, no need to do any corrections. */ | ||||
| if ((len1 + len2) == 0.0f) { | if ((len1 + len2) == 0.0f) { | ||||
| return; | return; | ||||
| ▲ Show 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | if (c3 != 0.0) { | ||||
| } | } | ||||
| return nr; | return nr; | ||||
| } | } | ||||
| a = c2; | a = c2; | ||||
| b = c1; | b = c1; | ||||
| c = c0; | c = c0; | ||||
| if (a != 0.0) { | if (a != 0.0) { | ||||
| /* discriminant */ | /* Discriminant */ | ||||
| p = b * b - 4 * a * c; | p = b * b - 4 * a * c; | ||||
| if (p > 0) { | if (p > 0) { | ||||
| p = sqrt(p); | p = sqrt(p); | ||||
| o[0] = (float)((-b - p) / (2 * a)); | o[0] = (float)((-b - p) / (2 * a)); | ||||
| if ((o[0] >= (float)SMALL) && (o[0] <= 1.000001f)) { | if ((o[0] >= (float)SMALL) && (o[0] <= 1.000001f)) { | ||||
| nr++; | nr++; | ||||
| ▲ Show 20 Lines • Show All 162 Lines • ▼ Show 20 Lines | if (endpoint_bezt->ipo == BEZT_IPO_LIN) { | ||||
| 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 neighbour 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; | int handle = direction_to_neighbor > 0 ? 0 : 2; | ||||
| float dx = endpoint_bezt->vec[1][0] - evaltime; | 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(FCurve *fcu, BezTriple *bezts, float evaltime) | ||||
| { | { | ||||
| const float eps = 1.e-8f; | const float eps = 1.e-8f; | ||||
| BezTriple *bezt, *prevbezt; | BezTriple *bezt, *prevbezt; | ||||
| unsigned int a; | unsigned int 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; | 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; | 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) { | ||||
| if (G.debug & G_DEBUG) { | if (G.debug & G_DEBUG) { | ||||
| printf(" ERROR: failed eval - p=%f b=%f, t=%f (%f)\n", | printf(" ERROR: failed eval - p=%f b=%f, t=%f (%f)\n", | ||||
| prevbezt->vec[1][0], | prevbezt->vec[1][0], | ||||
| bezt->vec[1][0], | bezt->vec[1][0], | ||||
| evaltime, | evaltime, | ||||
| fabsf(bezt->vec[1][0] - evaltime)); | fabsf(bezt->vec[1][0] - evaltime)); | ||||
| } | } | ||||
| return 0.0f; | return 0.0f; | ||||
| } | } | ||||
| /* Evaltime occurs within the interval defined by these two keyframes. */ | /* Evaltime occurs within the interval defined by these two keyframes. */ | ||||
| const float begin = prevbezt->vec[1][1]; | const float begin = prevbezt->vec[1][1]; | ||||
| const float change = bezt->vec[1][1] - 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 duration = bezt->vec[1][0] - prevbezt->vec[1][0]; | ||||
| const float time = evaltime - prevbezt->vec[1][0]; | const float time = evaltime - prevbezt->vec[1][0]; | ||||
| const float amplitude = prevbezt->amplitude; | const float amplitude = prevbezt->amplitude; | ||||
| const float period = prevbezt->period; | const float period = prevbezt->period; | ||||
| /* value depends on interpolation mode */ | /* Value depends on interpolation mode. */ | ||||
| if ((prevbezt->ipo == BEZT_IPO_CONST) || (fcu->flag & FCURVE_DISCRETE_VALUES) || | if ((prevbezt->ipo == BEZT_IPO_CONST) || (fcu->flag & FCURVE_DISCRETE_VALUES) || | ||||
| (duration == 0)) { | (duration == 0)) { | ||||
| /* constant (evaltime not relevant, so no interpolation needed) */ | /* Constant (evaltime not relevant, so no interpolation needed). */ | ||||
| return prevbezt->vec[1][1]; | return prevbezt->vec[1][1]; | ||||
| } | } | ||||
| switch (prevbezt->ipo) { | switch (prevbezt->ipo) { | ||||
| /* interpolation ...................................... */ | /* Interpolation ...................................... */ | ||||
| case BEZT_IPO_BEZ: { | case BEZT_IPO_BEZ: { | ||||
| float v1[2], v2[2], v3[2], v4[2], opl[32]; | float v1[2], v2[2], v3[2], v4[2], opl[32]; | ||||
| /* bezier interpolation */ | /* Bezier interpolation. */ | ||||
| /* (v1, v2) are the first keyframe and its 2nd handle */ | /* (v1, v2) are the first keyframe and its 2nd handle. */ | ||||
| v1[0] = prevbezt->vec[1][0]; | v1[0] = prevbezt->vec[1][0]; | ||||
| v1[1] = prevbezt->vec[1][1]; | v1[1] = prevbezt->vec[1][1]; | ||||
| v2[0] = prevbezt->vec[2][0]; | v2[0] = prevbezt->vec[2][0]; | ||||
| v2[1] = prevbezt->vec[2][1]; | v2[1] = prevbezt->vec[2][1]; | ||||
| /* (v3, v4) are the last keyframe's 1st handle + the last keyframe */ | /* (v3, v4) are the last keyframe's 1st handle + the last keyframe. */ | ||||
| v3[0] = bezt->vec[0][0]; | v3[0] = bezt->vec[0][0]; | ||||
| v3[1] = bezt->vec[0][1]; | v3[1] = bezt->vec[0][1]; | ||||
| v4[0] = bezt->vec[1][0]; | v4[0] = bezt->vec[1][0]; | ||||
| v4[1] = bezt->vec[1][1]; | v4[1] = bezt->vec[1][1]; | ||||
| if (fabsf(v1[1] - v4[1]) < FLT_EPSILON && fabsf(v2[1] - v3[1]) < FLT_EPSILON && | if (fabsf(v1[1] - v4[1]) < FLT_EPSILON && fabsf(v2[1] - v3[1]) < FLT_EPSILON && | ||||
| fabsf(v3[1] - v4[1]) < FLT_EPSILON) { | fabsf(v3[1] - v4[1]) < FLT_EPSILON) { | ||||
| /* Optimization: If all the handles are flat/at the same values, | /* Optimization: If all the handles are flat/at the same values, | ||||
| * the value is simply the shared value (see T40372 -> F91346) | * the value is simply the shared value (see T40372 -> F91346). | ||||
| */ | */ | ||||
| return v1[1]; | return v1[1]; | ||||
| } | } | ||||
| /* adjust handles so that they don't overlap (forming a loop) */ | /* Adjust handles so that they don't overlap (forming a loop). */ | ||||
| BKE_fcurve_correct_bezpart(v1, v2, v3, v4); | BKE_fcurve_correct_bezpart(v1, v2, v3, v4); | ||||
| /* try to get a value for this position - if failure, try another set of points */ | /* 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 (!findzero(evaltime, v1[0], v2[0], v3[0], v4[0], opl)) { | ||||
| if (G.debug & G_DEBUG) { | if (G.debug & G_DEBUG) { | ||||
| printf(" ERROR: findzero() failed at %f with %f %f %f %f\n", | printf(" ERROR: findzero() failed at %f with %f %f %f %f\n", | ||||
| evaltime, | evaltime, | ||||
| v1[0], | v1[0], | ||||
| v2[0], | v2[0], | ||||
| v3[0], | v3[0], | ||||
| v4[0]); | v4[0]); | ||||
| } | } | ||||
| return 0.0; | return 0.0; | ||||
| } | } | ||||
| berekeny(v1[1], v2[1], v3[1], v4[1], opl, 1); | berekeny(v1[1], v2[1], v3[1], v4[1], opl, 1); | ||||
| return opl[0]; | return opl[0]; | ||||
| } | } | ||||
| case BEZT_IPO_LIN: | case BEZT_IPO_LIN: | ||||
| /* linear - simply linearly interpolate between values of the two keyframes */ | /* Linear - simply linearly interpolate between values of the two keyframes. */ | ||||
| return BLI_easing_linear_ease(time, begin, change, duration); | return BLI_easing_linear_ease(time, begin, change, duration); | ||||
| /* easing ............................................ */ | /* Easing ............................................ */ | ||||
| case BEZT_IPO_BACK: | case BEZT_IPO_BACK: | ||||
| switch (prevbezt->easing) { | switch (prevbezt->easing) { | ||||
| case BEZT_IPO_EASE_IN: | case BEZT_IPO_EASE_IN: | ||||
| return BLI_easing_back_ease_in(time, begin, change, duration, prevbezt->back); | return BLI_easing_back_ease_in(time, begin, change, duration, prevbezt->back); | ||||
| case BEZT_IPO_EASE_OUT: | case BEZT_IPO_EASE_OUT: | ||||
| return BLI_easing_back_ease_out(time, begin, change, duration, prevbezt->back); | return BLI_easing_back_ease_out(time, begin, change, duration, prevbezt->back); | ||||
| case BEZT_IPO_EASE_IN_OUT: | case BEZT_IPO_EASE_IN_OUT: | ||||
| return BLI_easing_back_ease_in_out(time, begin, change, duration, prevbezt->back); | return BLI_easing_back_ease_in_out(time, begin, change, duration, prevbezt->back); | ||||
| default: /* default/auto: same as ease out */ | default: /* Default/Auto: same as ease out. */ | ||||
| return BLI_easing_back_ease_out(time, begin, change, duration, prevbezt->back); | return BLI_easing_back_ease_out(time, begin, change, duration, prevbezt->back); | ||||
| } | } | ||||
| break; | break; | ||||
| case BEZT_IPO_BOUNCE: | case BEZT_IPO_BOUNCE: | ||||
| switch (prevbezt->easing) { | switch (prevbezt->easing) { | ||||
| case BEZT_IPO_EASE_IN: | case BEZT_IPO_EASE_IN: | ||||
| return BLI_easing_bounce_ease_in(time, begin, change, duration); | return BLI_easing_bounce_ease_in(time, begin, change, duration); | ||||
| case BEZT_IPO_EASE_OUT: | case BEZT_IPO_EASE_OUT: | ||||
| return BLI_easing_bounce_ease_out(time, begin, change, duration); | return BLI_easing_bounce_ease_out(time, begin, change, duration); | ||||
| case BEZT_IPO_EASE_IN_OUT: | case BEZT_IPO_EASE_IN_OUT: | ||||
| return BLI_easing_bounce_ease_in_out(time, begin, change, duration); | return BLI_easing_bounce_ease_in_out(time, begin, change, duration); | ||||
| default: /* default/auto: same as ease out */ | default: /* Default/Auto: same as ease out. */ | ||||
| return BLI_easing_bounce_ease_out(time, begin, change, duration); | return BLI_easing_bounce_ease_out(time, begin, change, duration); | ||||
| } | } | ||||
| break; | break; | ||||
| case BEZT_IPO_CIRC: | case BEZT_IPO_CIRC: | ||||
| switch (prevbezt->easing) { | switch (prevbezt->easing) { | ||||
| case BEZT_IPO_EASE_IN: | case BEZT_IPO_EASE_IN: | ||||
| return BLI_easing_circ_ease_in(time, begin, change, duration); | return BLI_easing_circ_ease_in(time, begin, change, duration); | ||||
| case BEZT_IPO_EASE_OUT: | case BEZT_IPO_EASE_OUT: | ||||
| return BLI_easing_circ_ease_out(time, begin, change, duration); | return BLI_easing_circ_ease_out(time, begin, change, duration); | ||||
| case BEZT_IPO_EASE_IN_OUT: | case BEZT_IPO_EASE_IN_OUT: | ||||
| return BLI_easing_circ_ease_in_out(time, begin, change, duration); | return BLI_easing_circ_ease_in_out(time, begin, change, duration); | ||||
| default: /* default/auto: same as ease in */ | default: /* Default/Auto: same as ease in. */ | ||||
| return BLI_easing_circ_ease_in(time, begin, change, duration); | return BLI_easing_circ_ease_in(time, begin, change, duration); | ||||
| } | } | ||||
| break; | break; | ||||
| case BEZT_IPO_CUBIC: | case BEZT_IPO_CUBIC: | ||||
| switch (prevbezt->easing) { | switch (prevbezt->easing) { | ||||
| case BEZT_IPO_EASE_IN: | case BEZT_IPO_EASE_IN: | ||||
| return BLI_easing_cubic_ease_in(time, begin, change, duration); | return BLI_easing_cubic_ease_in(time, begin, change, duration); | ||||
| case BEZT_IPO_EASE_OUT: | case BEZT_IPO_EASE_OUT: | ||||
| return BLI_easing_cubic_ease_out(time, begin, change, duration); | return BLI_easing_cubic_ease_out(time, begin, change, duration); | ||||
| case BEZT_IPO_EASE_IN_OUT: | case BEZT_IPO_EASE_IN_OUT: | ||||
| return BLI_easing_cubic_ease_in_out(time, begin, change, duration); | return BLI_easing_cubic_ease_in_out(time, begin, change, duration); | ||||
| default: /* default/auto: same as ease in */ | default: /* Default/Auto: same as ease in. */ | ||||
| return BLI_easing_cubic_ease_in(time, begin, change, duration); | return BLI_easing_cubic_ease_in(time, begin, change, duration); | ||||
| } | } | ||||
| break; | break; | ||||
| case BEZT_IPO_ELASTIC: | case BEZT_IPO_ELASTIC: | ||||
| switch (prevbezt->easing) { | switch (prevbezt->easing) { | ||||
| case BEZT_IPO_EASE_IN: | case BEZT_IPO_EASE_IN: | ||||
| return BLI_easing_elastic_ease_in(time, begin, change, duration, amplitude, period); | return BLI_easing_elastic_ease_in(time, begin, change, duration, amplitude, period); | ||||
| case BEZT_IPO_EASE_OUT: | case BEZT_IPO_EASE_OUT: | ||||
| return BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); | return BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); | ||||
| case BEZT_IPO_EASE_IN_OUT: | case BEZT_IPO_EASE_IN_OUT: | ||||
| return BLI_easing_elastic_ease_in_out(time, begin, change, duration, amplitude, period); | return BLI_easing_elastic_ease_in_out(time, begin, change, duration, amplitude, period); | ||||
| default: /* default/auto: same as ease out */ | default: /* Default/Auto: same as ease out. */ | ||||
| return BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); | return BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); | ||||
| } | } | ||||
| break; | break; | ||||
| case BEZT_IPO_EXPO: | case BEZT_IPO_EXPO: | ||||
| switch (prevbezt->easing) { | switch (prevbezt->easing) { | ||||
| case BEZT_IPO_EASE_IN: | case BEZT_IPO_EASE_IN: | ||||
| return BLI_easing_expo_ease_in(time, begin, change, duration); | return BLI_easing_expo_ease_in(time, begin, change, duration); | ||||
| case BEZT_IPO_EASE_OUT: | case BEZT_IPO_EASE_OUT: | ||||
| return BLI_easing_expo_ease_out(time, begin, change, duration); | return BLI_easing_expo_ease_out(time, begin, change, duration); | ||||
| case BEZT_IPO_EASE_IN_OUT: | case BEZT_IPO_EASE_IN_OUT: | ||||
| return BLI_easing_expo_ease_in_out(time, begin, change, duration); | return BLI_easing_expo_ease_in_out(time, begin, change, duration); | ||||
| default: /* default/auto: same as ease in */ | default: /* Default/Auto: same as ease in. */ | ||||
| return BLI_easing_expo_ease_in(time, begin, change, duration); | return BLI_easing_expo_ease_in(time, begin, change, duration); | ||||
| } | } | ||||
| break; | break; | ||||
| case BEZT_IPO_QUAD: | case BEZT_IPO_QUAD: | ||||
| switch (prevbezt->easing) { | switch (prevbezt->easing) { | ||||
| case BEZT_IPO_EASE_IN: | case BEZT_IPO_EASE_IN: | ||||
| return BLI_easing_quad_ease_in(time, begin, change, duration); | return BLI_easing_quad_ease_in(time, begin, change, duration); | ||||
| case BEZT_IPO_EASE_OUT: | case BEZT_IPO_EASE_OUT: | ||||
| return BLI_easing_quad_ease_out(time, begin, change, duration); | return BLI_easing_quad_ease_out(time, begin, change, duration); | ||||
| case BEZT_IPO_EASE_IN_OUT: | case BEZT_IPO_EASE_IN_OUT: | ||||
| return BLI_easing_quad_ease_in_out(time, begin, change, duration); | return BLI_easing_quad_ease_in_out(time, begin, change, duration); | ||||
| default: /* default/auto: same as ease in */ | default: /* Default/Auto: same as ease in. */ | ||||
| return BLI_easing_quad_ease_in(time, begin, change, duration); | return BLI_easing_quad_ease_in(time, begin, change, duration); | ||||
| } | } | ||||
| break; | break; | ||||
| case BEZT_IPO_QUART: | case BEZT_IPO_QUART: | ||||
| switch (prevbezt->easing) { | switch (prevbezt->easing) { | ||||
| case BEZT_IPO_EASE_IN: | case BEZT_IPO_EASE_IN: | ||||
| return BLI_easing_quart_ease_in(time, begin, change, duration); | return BLI_easing_quart_ease_in(time, begin, change, duration); | ||||
| case BEZT_IPO_EASE_OUT: | case BEZT_IPO_EASE_OUT: | ||||
| return BLI_easing_quart_ease_out(time, begin, change, duration); | return BLI_easing_quart_ease_out(time, begin, change, duration); | ||||
| case BEZT_IPO_EASE_IN_OUT: | case BEZT_IPO_EASE_IN_OUT: | ||||
| return BLI_easing_quart_ease_in_out(time, begin, change, duration); | return BLI_easing_quart_ease_in_out(time, begin, change, duration); | ||||
| default: /* default/auto: same as ease in */ | default: /* Default/Auto: same as ease in. */ | ||||
| return BLI_easing_quart_ease_in(time, begin, change, duration); | return BLI_easing_quart_ease_in(time, begin, change, duration); | ||||
| } | } | ||||
| break; | break; | ||||
| case BEZT_IPO_QUINT: | case BEZT_IPO_QUINT: | ||||
| switch (prevbezt->easing) { | switch (prevbezt->easing) { | ||||
| case BEZT_IPO_EASE_IN: | case BEZT_IPO_EASE_IN: | ||||
| return BLI_easing_quint_ease_in(time, begin, change, duration); | return BLI_easing_quint_ease_in(time, begin, change, duration); | ||||
| case BEZT_IPO_EASE_OUT: | case BEZT_IPO_EASE_OUT: | ||||
| return BLI_easing_quint_ease_out(time, begin, change, duration); | return BLI_easing_quint_ease_out(time, begin, change, duration); | ||||
| case BEZT_IPO_EASE_IN_OUT: | case BEZT_IPO_EASE_IN_OUT: | ||||
| return BLI_easing_quint_ease_in_out(time, begin, change, duration); | return BLI_easing_quint_ease_in_out(time, begin, change, duration); | ||||
| default: /* default/auto: same as ease in */ | default: /* Default/Auto: same as ease in. */ | ||||
| return BLI_easing_quint_ease_in(time, begin, change, duration); | return BLI_easing_quint_ease_in(time, begin, change, duration); | ||||
| } | } | ||||
| break; | break; | ||||
| case BEZT_IPO_SINE: | case BEZT_IPO_SINE: | ||||
| switch (prevbezt->easing) { | switch (prevbezt->easing) { | ||||
| case BEZT_IPO_EASE_IN: | case BEZT_IPO_EASE_IN: | ||||
| return BLI_easing_sine_ease_in(time, begin, change, duration); | return BLI_easing_sine_ease_in(time, begin, change, duration); | ||||
| case BEZT_IPO_EASE_OUT: | case BEZT_IPO_EASE_OUT: | ||||
| return BLI_easing_sine_ease_out(time, begin, change, duration); | return BLI_easing_sine_ease_out(time, begin, change, duration); | ||||
| case BEZT_IPO_EASE_IN_OUT: | case BEZT_IPO_EASE_IN_OUT: | ||||
| return BLI_easing_sine_ease_in_out(time, begin, change, duration); | return BLI_easing_sine_ease_in_out(time, begin, change, duration); | ||||
| default: /* default/auto: same as ease in */ | default: /* Default/Auto: same as ease in. */ | ||||
| return BLI_easing_sine_ease_in(time, begin, change, duration); | return BLI_easing_sine_ease_in(time, begin, change, duration); | ||||
| } | } | ||||
| break; | break; | ||||
| default: | default: | ||||
| return prevbezt->vec[1][1]; | return prevbezt->vec[1][1]; | ||||
| } | } | ||||
| return 0.0f; | return 0.0f; | ||||
| } | } | ||||
| /* 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; | 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(FCurve *fcu, FPoint *fpts, float evaltime) | ||||
| { | { | ||||
| FPoint *prevfpt, *lastfpt, *fpt; | FPoint *prevfpt, *lastfpt, *fpt; | ||||
| float cvalue = 0.0f; | float cvalue = 0.0f; | ||||
| /* get pointers */ | /* Get pointers. */ | ||||
| prevfpt = fpts; | prevfpt = fpts; | ||||
| lastfpt = prevfpt + fcu->totvert - 1; | 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]); | 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 value */ | |||||
| 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; | 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); | 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); | ||||
| } | } | ||||
| else if (fcu->fpt) { | else if (fcu->fpt) { | ||||
| cvalue = fcurve_eval_samples(fcu, fcu->fpt, devaltime); | cvalue = fcurve_eval_samples(fcu, fcu->fpt, devaltime); | ||||
| } | } | ||||
| /* evaluate modifiers */ | /* Evaluate modifiers. */ | ||||
| evaluate_value_fmodifiers(&storage, &fcu->modifiers, fcu, &cvalue, devaltime); | evaluate_value_fmodifiers(&storage, &fcu->modifiers, fcu, &cvalue, devaltime); | ||||
| /* if curve can only have integral values, perform truncation (i.e. drop the decimal part) | /* If curve can only have integral values, perform truncation (i.e. drop the decimal part) | ||||
| * here so that the curve can be sampled correctly | * here so that the curve can be sampled correctly. | ||||
| */ | */ | ||||
| if (fcu->flag & FCURVE_INT_VALUES) { | if (fcu->flag & FCURVE_INT_VALUES) { | ||||
| cvalue = floorf(cvalue + 0.5f); | cvalue = floorf(cvalue + 0.5f); | ||||
| } | } | ||||
| /* return evaluated value */ | |||||
| return cvalue; | return cvalue; | ||||
| } | } | ||||
| float evaluate_fcurve(FCurve *fcu, float evaltime) | float evaluate_fcurve(FCurve *fcu, float evaltime) | ||||
| { | { | ||||
| BLI_assert(fcu->driver == NULL); | BLI_assert(fcu->driver == NULL); | ||||
| return evaluate_fcurve_ex(fcu, evaltime, 0.0); | return evaluate_fcurve_ex(fcu, evaltime, 0.0); | ||||
| Show All 15 Lines | float evaluate_fcurve_driver(PathResolvedRNA *anim_rna, | ||||
| BLI_assert(fcu->driver != NULL); | BLI_assert(fcu->driver != NULL); | ||||
| float cvalue = 0.0f; | float cvalue = 0.0f; | ||||
| float evaltime = anim_eval_context->eval_time; | float evaltime = anim_eval_context->eval_time; | ||||
| /* If there is a driver (only if this F-Curve is acting as 'driver'), | /* 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 | * 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; | 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) { | for (fcm = fcu->modifiers.first; fcm; fcm = fcm->next) { | ||||
| /* 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); | ||||
| } | } | ||||
| /* Checks if the curve has valid keys, drivers or modifiers that produce an actual curve. */ | /* Checks if the curve has valid keys, drivers or modifiers that produce an actual curve. */ | ||||
| bool BKE_fcurve_is_empty(FCurve *fcu) | bool BKE_fcurve_is_empty(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); | ||||
| } | } | ||||
| /* Calculate the value of the given F-Curve at the given frame, and set its curval */ | /* Calculate the value of the given F-Curve at the given frame, and set its curval. */ | ||||
| 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 | ||||
| * any data which warrants this... | * any data which warrants this... | ||||
| */ | */ | ||||
| if (BKE_fcurve_is_empty(fcu)) { | if (BKE_fcurve_is_empty(fcu)) { | ||||
| return 0.0f; | return 0.0f; | ||||
| } | } | ||||
| /* calculate and set curval (evaluates driver too if necessary) */ | /* Calculate and set curval (evaluates driver too if necessary). */ | ||||
| float curval; | float curval; | ||||
| if (fcu->driver) { | if (fcu->driver) { | ||||
| curval = evaluate_fcurve_driver(anim_rna, fcu, fcu->driver, anim_eval_context); | curval = evaluate_fcurve_driver(anim_rna, fcu, fcu->driver, anim_eval_context); | ||||
| } | } | ||||
| else { | else { | ||||
| curval = evaluate_fcurve(fcu, anim_eval_context->eval_time); | curval = evaluate_fcurve(fcu, anim_eval_context->eval_time); | ||||
| } | } | ||||
| fcu->curval = curval; /* debug display only, not thread safe! */ | fcu->curval = curval; /* Debug display only, not thread safe! */ | ||||
| return curval; | return curval; | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name F-Curve - .blend file API | /** \name F-Curve - .blend file API | ||||
| * \{ */ | * \{ */ | ||||
| ▲ Show 20 Lines • Show All 258 Lines • Show Last 20 Lines | |||||