2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
19 * All rights reserved.
21 * Contributor(s): Joshua Leung (full recode)
23 * ***** END GPL LICENSE BLOCK *****
26 /** \file blender/blenkernel/intern/fmodifier.c
36 #include "MEM_guardedalloc.h"
38 #include "DNA_anim_types.h"
40 #include "BLF_translation.h"
42 #include "BLI_blenlib.h"
43 #include "BLI_ghash.h"
44 #include "BLI_noise.h"
45 #include "BLI_math.h" /* windows needs for M_PI */
46 #include "BLI_utildefines.h"
48 #include "BKE_fcurve.h"
49 #include "BKE_idprop.h"
51 /* ******************************** F-Modifiers ********************************* */
53 /* Forward declarations. */
54 void fmodifiers_storage_put(FModifierStackStorage *storage, FModifier *fcm, void *data);
55 void fmodifiers_storage_remove(FModifierStackStorage *storage, FModifier *fcm);
56 void *fmodifiers_storage_get(FModifierStackStorage *storage, FModifier *fcm);
58 /* Info ------------------------------- */
60 /* F-Modifiers are modifiers which operate on F-Curves. However, they can also be defined
61 * on NLA-Strips to affect all of the F-Curves referenced by the NLA-Strip.
64 /* Template --------------------------- */
66 /* Each modifier defines a set of functions, which will be called at the appropriate
67 * times. In addition to this, each modifier should have a type-info struct, where
68 * its functions are attached for use.
71 /* Template for type-info data:
72 * - make a copy of this when creating new modifiers, and just change the functions
73 * pointed to as necessary
74 * - although the naming of functions doesn't matter, it would help for code
75 * readability, to follow the same naming convention as is presented here
76 * - any functions that a constraint doesn't need to define, don't define
77 * for such cases, just use NULL
78 * - these should be defined after all the functions have been defined, so that
79 * forward-definitions/prototypes don't need to be used!
80 * - keep this copy #if-def'd so that future constraints can get based off this
83 static FModifierTypeInfo FMI_MODNAME = {
84 FMODIFIER_TYPE_MODNAME, /* type */
85 sizeof(FMod_ModName), /* size */
86 FMI_TYPE_SOME_ACTION, /* action type */
87 FMI_REQUIRES_SOME_REQUIREMENT, /* requirements */
88 "Modifier Name", /* name */
89 "FMod_ModName", /* struct name */
90 fcm_modname_free, /* free data */
91 fcm_modname_relink, /* relink data */
92 fcm_modname_copy, /* copy data */
93 fcm_modname_new_data, /* new data */
94 fcm_modname_verify, /* verify */
95 fcm_modname_time, /* evaluate time */
96 fcm_modname_evaluate, /* evaluate */
97 fcm_modname_time_storage, /* evaluate time with storage */
98 fcm_modname_evaluate_storage /* evaluate with storage */
102 /* Generator F-Curve Modifier --------------------------- */
104 /* Generators available:
105 * 1) simple polynomial generator:
106 * - Exanded form - (y = C[0]*(x^(n)) + C[1]*(x^(n-1)) + ... + C[n])
107 * - Factorized form - (y = (C[0][0]*x + C[0][1]) * (C[1][0]*x + C[1][1]) * ... * (C[n][0]*x + C[n][1]))
110 static void fcm_generator_free(FModifier *fcm)
112 FMod_Generator *data = (FMod_Generator *)fcm->data;
114 /* free polynomial coefficients array */
115 if (data->coefficients)
116 MEM_freeN(data->coefficients);
119 static void fcm_generator_copy(FModifier *fcm, FModifier *src)
121 FMod_Generator *gen = (FMod_Generator *)fcm->data;
122 FMod_Generator *ogen = (FMod_Generator *)src->data;
124 /* copy coefficients array? */
125 if (ogen->coefficients)
126 gen->coefficients = MEM_dupallocN(ogen->coefficients);
129 static void fcm_generator_new_data(void *mdata)
131 FMod_Generator *data = (FMod_Generator *)mdata;
134 /* set default generator to be linear 0-1 (gradient = 1, y-offset = 0) */
135 data->poly_order = 1;
137 cp = data->coefficients = MEM_callocN(sizeof(float) * 2, "FMod_Generator_Coefs");
138 cp[0] = 0; // y-offset
139 cp[1] = 1; // gradient
142 static void fcm_generator_verify(FModifier *fcm)
144 FMod_Generator *data = (FMod_Generator *)fcm->data;
146 /* requirements depend on mode */
147 switch (data->mode) {
148 case FCM_GENERATOR_POLYNOMIAL: /* expanded polynomial expression */
150 const int arraysize_new = data->poly_order + 1;
151 /* arraysize needs to be order+1, so resize if not */
152 if (data->arraysize != arraysize_new) {
153 data->coefficients = MEM_recallocN(data->coefficients,
154 sizeof(float) * arraysize_new);
155 data->arraysize = arraysize_new;
159 case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* expanded polynomial expression */
161 const int arraysize_new = data->poly_order * 2;
162 /* arraysize needs to be (2 * order), so resize if not */
163 if (data->arraysize != arraysize_new) {
164 data->coefficients = MEM_recallocN(data->coefficients,
165 sizeof(float) * arraysize_new);
166 data->arraysize = arraysize_new;
173 static void fcm_generator_evaluate(FCurve *UNUSED(fcu), FModifier *fcm, float *cvalue, float evaltime)
175 FMod_Generator *data = (FMod_Generator *)fcm->data;
177 /* behavior depends on mode
178 * NOTE: the data in its default state is fine too
180 switch (data->mode) {
181 case FCM_GENERATOR_POLYNOMIAL: /* expanded polynomial expression */
183 /* we overwrite cvalue with the sum of the polynomial */
184 float *powers = MEM_callocN(sizeof(float) * data->arraysize, "Poly Powers");
188 /* for each x^n, precalculate value based on previous one first... this should be
189 * faster that calling pow() for each entry
191 for (i = 0; i < data->arraysize; i++) {
192 /* first entry is x^0 = 1, otherwise, calculate based on previous */
194 powers[i] = powers[i - 1] * evaltime;
199 /* for each coefficient, add to value, which we'll write to *cvalue in one go */
200 for (i = 0; i < data->arraysize; i++)
201 value += data->coefficients[i] * powers[i];
203 /* only if something changed, write *cvalue in one go */
204 if (data->poly_order) {
205 if (data->flag & FCM_GENERATOR_ADDITIVE)
216 case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* Factorized polynomial */
218 float value = 1.0f, *cp = NULL;
221 /* for each coefficient pair, solve for that bracket before accumulating in value by multiplying */
222 for (cp = data->coefficients, i = 0; (cp) && (i < (unsigned int)data->poly_order); cp += 2, i++)
223 value *= (cp[0] * evaltime + cp[1]);
225 /* only if something changed, write *cvalue in one go */
226 if (data->poly_order) {
227 if (data->flag & FCM_GENERATOR_ADDITIVE)
237 static FModifierTypeInfo FMI_GENERATOR = {
238 FMODIFIER_TYPE_GENERATOR, /* type */
239 sizeof(FMod_Generator), /* size */
240 FMI_TYPE_GENERATE_CURVE, /* action type */
241 FMI_REQUIRES_NOTHING, /* requirements */
242 N_("Generator"), /* name */
243 "FMod_Generator", /* struct name */
244 fcm_generator_free, /* free data */
245 fcm_generator_copy, /* copy data */
246 fcm_generator_new_data, /* new data */
247 fcm_generator_verify, /* verify */
248 NULL, /* evaluate time */
249 fcm_generator_evaluate, /* evaluate */
250 NULL, /* evaluate time with storage */
251 NULL /* evaluate with storage */
254 /* Built-In Function Generator F-Curve Modifier --------------------------- */
256 /* This uses the general equation for equations:
257 * y = amplitude * fn(phase_multiplier * x + phase_offset) + y_offset
259 * where amplitude, phase_multiplier/offset, y_offset are user-defined coefficients,
260 * x is the evaluation 'time', and 'y' is the resultant value
262 * Functions available are
263 * sin, cos, tan, sinc (normalized sin), natural log, square root
266 static void fcm_fn_generator_new_data(void *mdata)
268 FMod_FunctionGenerator *data = (FMod_FunctionGenerator *)mdata;
270 /* set amplitude and phase multiplier to 1.0f so that something is generated */
271 data->amplitude = 1.0f;
272 data->phase_multiplier = 1.0f;
275 /* Unary 'normalized sine' function
276 * y = sin(PI + x) / (PI * x),
277 * except for x = 0 when y = 1.
279 static double sinc(double x)
281 if (fabs(x) < 0.0001)
284 return sin(M_PI * x) / (M_PI * x);
287 static void fcm_fn_generator_evaluate(FCurve *UNUSED(fcu), FModifier *fcm, float *cvalue, float evaltime)
289 FMod_FunctionGenerator *data = (FMod_FunctionGenerator *)fcm->data;
290 double arg = data->phase_multiplier * evaltime + data->phase_offset;
291 double (*fn)(double v) = NULL;
293 /* get function pointer to the func to use:
294 * WARNING: must perform special argument validation hereto guard against crashes
296 switch (data->type) {
298 case FCM_GENERATOR_FN_SIN: /* sine wave */
301 case FCM_GENERATOR_FN_COS: /* cosine wave */
304 case FCM_GENERATOR_FN_SINC: /* normalized sine wave */
308 /* validation required */
309 case FCM_GENERATOR_FN_TAN: /* tangent wave */
311 /* check that argument is not on one of the discontinuities (i.e. 90deg, 270 deg, etc) */
312 if (IS_EQ(fmod((arg - M_PI_2), M_PI), 0.0)) {
313 if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0)
314 *cvalue = 0.0f; /* no value possible here */
320 case FCM_GENERATOR_FN_LN: /* natural log */
322 /* check that value is greater than 1? */
327 if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0)
328 *cvalue = 0.0f; /* no value possible here */
332 case FCM_GENERATOR_FN_SQRT: /* square root */
334 /* no negative numbers */
339 if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0)
340 *cvalue = 0.0f; /* no value possible here */
345 printf("Invalid Function-Generator for F-Modifier - %d\n", data->type);
350 /* execute function callback to set value if appropriate */
352 float value = (float)(data->amplitude * (float)fn(arg) + data->value_offset);
354 if (data->flag & FCM_GENERATOR_ADDITIVE)
361 static FModifierTypeInfo FMI_FN_GENERATOR = {
362 FMODIFIER_TYPE_FN_GENERATOR, /* type */
363 sizeof(FMod_FunctionGenerator), /* size */
364 FMI_TYPE_GENERATE_CURVE, /* action type */
365 FMI_REQUIRES_NOTHING, /* requirements */
366 N_("Built-In Function"), /* name */
367 "FMod_FunctionGenerator", /* struct name */
368 NULL, /* free data */
369 NULL, /* copy data */
370 fcm_fn_generator_new_data, /* new data */
372 NULL, /* evaluate time */
373 fcm_fn_generator_evaluate, /* evaluate */
374 NULL, /* evaluate time with storage */
375 NULL /* evaluate with storage */
378 /* Envelope F-Curve Modifier --------------------------- */
380 static void fcm_envelope_free(FModifier *fcm)
382 FMod_Envelope *env = (FMod_Envelope *)fcm->data;
384 /* free envelope data array */
386 MEM_freeN(env->data);
389 static void fcm_envelope_copy(FModifier *fcm, FModifier *src)
391 FMod_Envelope *env = (FMod_Envelope *)fcm->data;
392 FMod_Envelope *oenv = (FMod_Envelope *)src->data;
394 /* copy envelope data array */
396 env->data = MEM_dupallocN(oenv->data);
399 static void fcm_envelope_new_data(void *mdata)
401 FMod_Envelope *env = (FMod_Envelope *)mdata;
403 /* set default min/max ranges */
408 static void fcm_envelope_verify(FModifier *fcm)
410 FMod_Envelope *env = (FMod_Envelope *)fcm->data;
412 /* if the are points, perform bubble-sort on them, as user may have changed the order */
418 static void fcm_envelope_evaluate(FCurve *UNUSED(fcu), FModifier *fcm, float *cvalue, float evaltime)
420 FMod_Envelope *env = (FMod_Envelope *)fcm->data;
421 FCM_EnvelopeData *fed, *prevfed, *lastfed;
422 float min = 0.0f, max = 0.0f, fac = 0.0f;
426 if (env->data == NULL) return;
429 lastfed = prevfed + (env->totvert - 1);
431 /* get min/max values for envelope at evaluation time (relative to mid-value) */
432 if (prevfed->time >= evaltime) {
433 /* before or on first sample, so just extend value */
437 else if (lastfed->time <= evaltime) {
438 /* after or on last sample, so just extend value */
443 /* evaltime occurs somewhere between segments */
444 /* TODO: implement binary search for this to make it faster? */
445 for (a = 0; prevfed && fed && (a < env->totvert - 1); a++, prevfed = fed, fed++) {
446 /* evaltime occurs within the interval defined by these two envelope points */
447 if ((prevfed->time <= evaltime) && (fed->time >= evaltime)) {
448 float afac, bfac, diff;
450 diff = fed->time - prevfed->time;
451 afac = (evaltime - prevfed->time) / diff;
452 bfac = (fed->time - evaltime) / diff;
454 min = bfac * prevfed->min + afac * fed->min;
455 max = bfac * prevfed->max + afac * fed->max;
463 * - fac is the ratio of how the current y-value corresponds to the reference range
464 * - thus, the new value is found by mapping the old range to the new!
466 fac = (*cvalue - (env->midval + env->min)) / (env->max - env->min);
467 *cvalue = min + fac * (max - min);
470 static FModifierTypeInfo FMI_ENVELOPE = {
471 FMODIFIER_TYPE_ENVELOPE, /* type */
472 sizeof(FMod_Envelope), /* size */
473 FMI_TYPE_REPLACE_VALUES, /* action type */
474 0, /* requirements */
475 N_("Envelope"), /* name */
476 "FMod_Envelope", /* struct name */
477 fcm_envelope_free, /* free data */
478 fcm_envelope_copy, /* copy data */
479 fcm_envelope_new_data, /* new data */
480 fcm_envelope_verify, /* verify */
481 NULL, /* evaluate time */
482 fcm_envelope_evaluate, /* evaluate */
483 NULL, /* evaluate time with storage */
484 NULL /* evaluate with storage */
487 /* exported function for finding points */
489 /* Binary search algorithm for finding where to insert Envelope Data Point.
490 * Returns the index to insert at (data already at that index will be offset if replace is 0)
492 #define BINARYSEARCH_FRAMEEQ_THRESH 0.0001f
494 int BKE_fcm_envelope_find_index(FCM_EnvelopeData array[], float frame, int arraylen, bool *r_exists)
496 int start = 0, end = arraylen;
497 int loopbreaker = 0, maxloop = arraylen * 2;
499 /* initialize exists-flag first */
502 /* sneaky optimizations (don't go through searching process if...):
503 * - keyframe to be added is to be added out of current bounds
504 * - keyframe to be added would replace one of the existing ones on bounds
506 if ((arraylen <= 0) || (array == NULL)) {
507 printf("Warning: binarysearch_fcm_envelopedata_index() encountered invalid array\n");
511 /* check whether to add before/after/on */
514 /* 'First' Point (when only one point, this case is used) */
515 framenum = array[0].time;
516 if (IS_EQT(frame, framenum, BINARYSEARCH_FRAMEEQ_THRESH)) {
520 else if (frame < framenum) {
525 framenum = array[(arraylen - 1)].time;
526 if (IS_EQT(frame, framenum, BINARYSEARCH_FRAMEEQ_THRESH)) {
528 return (arraylen - 1);
530 else if (frame > framenum) {
536 /* most of the time, this loop is just to find where to put it
537 * - 'loopbreaker' is just here to prevent infinite loops
539 for (loopbreaker = 0; (start <= end) && (loopbreaker < maxloop); loopbreaker++) {
540 /* compute and get midpoint */
541 int mid = start + ((end - start) / 2); /* we calculate the midpoint this way to avoid int overflows... */
542 float midfra = array[mid].time;
544 /* check if exactly equal to midpoint */
545 if (IS_EQT(frame, midfra, BINARYSEARCH_FRAMEEQ_THRESH)) {
550 /* repeat in upper/lower half */
551 if (frame > midfra) {
554 else if (frame < midfra) {
559 /* print error if loop-limit exceeded */
560 if (loopbreaker == (maxloop - 1)) {
561 printf("Error: binarysearch_fcm_envelopedata_index() was taking too long\n");
563 // include debug info
564 printf("\tround = %d: start = %d, end = %d, arraylen = %d\n", loopbreaker, start, end, arraylen);
567 /* not found, so return where to place it */
570 #undef BINARYSEARCH_FRAMEEQ_THRESH
573 /* Cycles F-Curve Modifier --------------------------- */
575 /* This modifier changes evaltime to something that exists within the curve's frame-range,
576 * then re-evaluates modifier stack up to this point using the new time. This re-entrant behavior
577 * is very likely to be more time-consuming than the original approach... (which was tightly integrated into
578 * the calculation code...).
580 * NOTE: this needs to be at the start of the stack to be of use, as it needs to know the extents of the
581 * keyframes/sample-data.
583 * Possible TODO - store length of cycle information that can be initialized from the extents of the
584 * keyframes/sample-data, and adjusted as appropriate.
587 /* temp data used during evaluation */
588 typedef struct tFCMED_Cycles {
589 float cycyofs; /* y-offset to apply */
592 static void fcm_cycles_new_data(void *mdata)
594 FMod_Cycles *data = (FMod_Cycles *)mdata;
596 /* turn on cycles by default */
597 data->before_mode = data->after_mode = FCM_EXTRAPOLATE_CYCLIC;
600 static float fcm_cycles_time(FModifierStackStorage *storage, FCurve *fcu, FModifier *fcm,
601 float UNUSED(cvalue), float evaltime)
603 FMod_Cycles *data = (FMod_Cycles *)fcm->data;
604 float prevkey[2], lastkey[2], cycyofs = 0.0f;
605 short side = 0, mode = 0;
609 /* check if modifier is first in stack, otherwise disable ourself... */
612 fcm->flag |= FMODIFIER_FLAG_DISABLED;
616 /* calculate new evaltime due to cyclic interpolation */
617 if (fcu && fcu->bezt) {
618 BezTriple *prevbezt = fcu->bezt;
619 BezTriple *lastbezt = prevbezt + fcu->totvert - 1;
621 prevkey[0] = prevbezt->vec[1][0];
622 prevkey[1] = prevbezt->vec[1][1];
624 lastkey[0] = lastbezt->vec[1][0];
625 lastkey[1] = lastbezt->vec[1][1];
627 else if (fcu && fcu->fpt) {
628 FPoint *prevfpt = fcu->fpt;
629 FPoint *lastfpt = prevfpt + fcu->totvert - 1;
631 prevkey[0] = prevfpt->vec[0];
632 prevkey[1] = prevfpt->vec[1];
634 lastkey[0] = lastfpt->vec[0];
635 lastkey[1] = lastfpt->vec[1];
640 /* check if modifier will do anything
641 * 1) if in data range, definitely don't do anything
642 * 2) if before first frame or after last frame, make sure some cycling is in use
644 if (evaltime < prevkey[0]) {
645 if (data->before_mode) {
647 mode = data->before_mode;
648 cycles = data->before_cycles;
652 else if (evaltime > lastkey[0]) {
653 if (data->after_mode) {
655 mode = data->after_mode;
656 cycles = data->after_cycles;
660 if ((ELEM(0, side, mode)))
663 /* find relative place within a cycle */
665 float cycdx = 0, cycdy = 0;
666 float cycle = 0, cyct = 0;
668 /* calculate period and amplitude (total height) of a cycle */
669 cycdx = lastkey[0] - prevkey[0];
670 cycdy = lastkey[1] - prevkey[1];
672 /* check if cycle is infinitely small, to be point of being impossible to use */
676 /* calculate the 'number' of the cycle */
677 cycle = ((float)side * (evaltime - ofs) / cycdx);
679 /* calculate the time inside the cycle */
680 cyct = fmod(evaltime - ofs, cycdx);
682 /* check that cyclic is still enabled for the specified time */
684 /* catch this case so that we don't exit when we have (cycles = 0)
685 * as this indicates infinite cycles...
688 else if (cycle > cycles) {
689 /* we are too far away from range to evaluate
690 * TODO: but we should still hold last value...
695 /* check if 'cyclic extrapolation', and thus calculate y-offset for this cycle */
696 if (mode == FCM_EXTRAPOLATE_CYCLIC_OFFSET) {
698 cycyofs = (float)floor((evaltime - ofs) / cycdx);
700 cycyofs = (float)ceil((evaltime - ofs) / cycdx);
704 /* special case for cycle start/end */
706 evaltime = (side == 1 ? lastkey[0] : prevkey[0]);
708 if ((mode == FCM_EXTRAPOLATE_MIRROR) && ((int)cycle % 2))
709 evaltime = (side == 1 ? prevkey[0] : lastkey[0]);
711 /* calculate where in the cycle we are (overwrite evaltime to reflect this) */
712 else if ((mode == FCM_EXTRAPOLATE_MIRROR) && ((int)(cycle + 1) % 2)) {
713 /* when 'mirror' option is used and cycle number is odd, this cycle is played in reverse
714 * - for 'before' extrapolation, we need to flip in a different way, otherwise values past
715 * then end of the curve get referenced (result of fmod will be negative, and with different phase)
718 evaltime = prevkey[0] - cyct;
720 evaltime = lastkey[0] - cyct;
723 /* the cycle is played normally... */
724 evaltime = prevkey[0] + cyct;
726 if (evaltime < prevkey[0]) evaltime += cycdx;
729 /* store temp data if needed */
730 if (mode == FCM_EXTRAPOLATE_CYCLIC_OFFSET) {
731 tFCMED_Cycles *edata;
733 /* for now, this is just a float, but we could get more stuff... */
734 edata = MEM_callocN(sizeof(tFCMED_Cycles), "tFCMED_Cycles");
735 edata->cycyofs = cycyofs;
737 fmodifiers_storage_put(storage, fcm, edata);
740 /* return the new frame to evaluate */
744 static void fcm_cycles_evaluate(FModifierStackStorage *storage, FCurve *UNUSED(fcu),
745 FModifier *fcm, float *cvalue, float UNUSED(evaltime))
747 tFCMED_Cycles *edata = fmodifiers_storage_get(storage, fcm);
751 /* add cyclic offset - no need to check for now, otherwise the data wouldn't exist! */
752 *cvalue += edata->cycyofs;
756 fmodifiers_storage_remove(storage, fcm);
760 static FModifierTypeInfo FMI_CYCLES = {
761 FMODIFIER_TYPE_CYCLES, /* type */
762 sizeof(FMod_Cycles), /* size */
763 FMI_TYPE_EXTRAPOLATION, /* action type */
764 FMI_REQUIRES_ORIGINAL_DATA | FMI_REQUIRES_STORAGE, /* requirements */
765 N_("Cycles"), /* name */
766 "FMod_Cycles", /* struct name */
767 NULL, /* free data */
768 NULL, /* copy data */
769 fcm_cycles_new_data, /* new data */
770 NULL /*fcm_cycles_verify*/, /* verify */
771 NULL, /* evaluate time */
773 fcm_cycles_time, /* evaluate time with storage */
774 fcm_cycles_evaluate /* evaluate with storage */
777 /* Noise F-Curve Modifier --------------------------- */
779 static void fcm_noise_new_data(void *mdata)
781 FMod_Noise *data = (FMod_Noise *)mdata;
785 data->strength = 1.0f;
789 data->modification = FCM_NOISE_MODIF_REPLACE;
792 static void fcm_noise_evaluate(FCurve *UNUSED(fcu), FModifier *fcm, float *cvalue, float evaltime)
794 FMod_Noise *data = (FMod_Noise *)fcm->data;
797 /* generate noise using good ol' Blender Noise
798 * - 0.1 is passed as the 'z' value, otherwise evaluation fails for size = phase = 1
799 * with evaltime being an integer (which happens when evaluating on frame by frame basis)
801 noise = BLI_turbulence(data->size, evaltime - data->offset, data->phase, 0.1f, data->depth);
803 /* combine the noise with existing motion data */
804 switch (data->modification) {
805 case FCM_NOISE_MODIF_ADD:
806 *cvalue = *cvalue + noise * data->strength;
808 case FCM_NOISE_MODIF_SUBTRACT:
809 *cvalue = *cvalue - noise * data->strength;
811 case FCM_NOISE_MODIF_MULTIPLY:
812 *cvalue = *cvalue * noise * data->strength;
814 case FCM_NOISE_MODIF_REPLACE:
816 *cvalue = *cvalue + (noise - 0.5f) * data->strength;
821 static FModifierTypeInfo FMI_NOISE = {
822 FMODIFIER_TYPE_NOISE, /* type */
823 sizeof(FMod_Noise), /* size */
824 FMI_TYPE_REPLACE_VALUES, /* action type */
825 0, /* requirements */
826 N_("Noise"), /* name */
827 "FMod_Noise", /* struct name */
828 NULL, /* free data */
829 NULL, /* copy data */
830 fcm_noise_new_data, /* new data */
831 NULL /*fcm_noise_verify*/, /* verify */
832 NULL, /* evaluate time */
833 fcm_noise_evaluate, /* evaluate */
834 NULL, /* evaluate time with storage */
835 NULL /* evaluate with storage */
838 /* Filter F-Curve Modifier --------------------------- */
840 #if 0 // XXX not yet implemented
841 static FModifierTypeInfo FMI_FILTER = {
842 FMODIFIER_TYPE_FILTER, /* type */
843 sizeof(FMod_Filter), /* size */
844 FMI_TYPE_REPLACE_VALUES, /* action type */
845 0, /* requirements */
846 N_("Filter"), /* name */
847 "FMod_Filter", /* struct name */
848 NULL, /* free data */
849 NULL, /* copy data */
851 NULL /*fcm_filter_verify*/, /* verify */
852 NULL, /* evlauate time */
853 fcm_filter_evaluate, /* evaluate */
854 NULL, /* evaluate time with storage */
855 NULL /* evaluate with storage */
857 #endif // XXX not yet implemented
860 /* Python F-Curve Modifier --------------------------- */
862 static void fcm_python_free(FModifier *fcm)
864 FMod_Python *data = (FMod_Python *)fcm->data;
867 IDP_FreeProperty(data->prop);
868 MEM_freeN(data->prop);
871 static void fcm_python_new_data(void *mdata)
873 FMod_Python *data = (FMod_Python *)mdata;
875 /* everything should be set correctly by calloc, except for the prop->type constant.*/
876 data->prop = MEM_callocN(sizeof(IDProperty), "PyFModifierProps");
877 data->prop->type = IDP_GROUP;
880 static void fcm_python_copy(FModifier *fcm, FModifier *src)
882 FMod_Python *pymod = (FMod_Python *)fcm->data;
883 FMod_Python *opymod = (FMod_Python *)src->data;
885 pymod->prop = IDP_CopyProperty(opymod->prop);
888 static void fcm_python_evaluate(FCurve *UNUSED(fcu), FModifier *UNUSED(fcm), float *UNUSED(cvalue), float UNUSED(evaltime))
891 //FMod_Python *data = (FMod_Python *)fcm->data;
893 /* FIXME... need to implement this modifier...
894 * It will need it execute a script using the custom properties
896 #endif /* WITH_PYTHON */
899 static FModifierTypeInfo FMI_PYTHON = {
900 FMODIFIER_TYPE_PYTHON, /* type */
901 sizeof(FMod_Python), /* size */
902 FMI_TYPE_GENERATE_CURVE, /* action type */
903 FMI_REQUIRES_RUNTIME_CHECK, /* requirements */
904 N_("Python"), /* name */
905 "FMod_Python", /* struct name */
906 fcm_python_free, /* free data */
907 fcm_python_copy, /* copy data */
908 fcm_python_new_data, /* new data */
909 NULL /*fcm_python_verify*/, /* verify */
910 NULL /*fcm_python_time*/, /* evaluate time */
911 fcm_python_evaluate, /* evaluate */
912 NULL, /* evaluate time with storage */
913 NULL /* evaluate with storage */
917 /* Limits F-Curve Modifier --------------------------- */
919 static float fcm_limits_time(FCurve *UNUSED(fcu), FModifier *fcm, float UNUSED(cvalue), float evaltime)
921 FMod_Limits *data = (FMod_Limits *)fcm->data;
923 /* check for the time limits */
924 if ((data->flag & FCM_LIMIT_XMIN) && (evaltime < data->rect.xmin))
925 return data->rect.xmin;
926 if ((data->flag & FCM_LIMIT_XMAX) && (evaltime > data->rect.xmax))
927 return data->rect.xmax;
929 /* modifier doesn't change time */
933 static void fcm_limits_evaluate(FCurve *UNUSED(fcu), FModifier *fcm, float *cvalue, float UNUSED(evaltime))
935 FMod_Limits *data = (FMod_Limits *)fcm->data;
937 /* value limits now */
938 if ((data->flag & FCM_LIMIT_YMIN) && (*cvalue < data->rect.ymin))
939 *cvalue = data->rect.ymin;
940 if ((data->flag & FCM_LIMIT_YMAX) && (*cvalue > data->rect.ymax))
941 *cvalue = data->rect.ymax;
944 static FModifierTypeInfo FMI_LIMITS = {
945 FMODIFIER_TYPE_LIMITS, /* type */
946 sizeof(FMod_Limits), /* size */
947 FMI_TYPE_GENERATE_CURVE, /* action type */ /* XXX... err... */
948 FMI_REQUIRES_RUNTIME_CHECK, /* requirements */
949 N_("Limits"), /* name */
950 "FMod_Limits", /* struct name */
951 NULL, /* free data */
952 NULL, /* copy data */
955 fcm_limits_time, /* evaluate time */
956 fcm_limits_evaluate, /* evaluate */
957 NULL, /* evaluate time with storage */
958 NULL /* evaluate with storage */
961 /* Stepped F-Curve Modifier --------------------------- */
963 static void fcm_stepped_new_data(void *mdata)
965 FMod_Stepped *data = (FMod_Stepped *)mdata;
967 /* just need to set the step-size to 2-frames by default */
968 /* XXX: or would 5 be more normal? */
969 data->step_size = 2.0f;
972 static float fcm_stepped_time(FCurve *UNUSED(fcu), FModifier *fcm, float UNUSED(cvalue), float evaltime)
974 FMod_Stepped *data = (FMod_Stepped *)fcm->data;
977 /* check range clamping to see if we should alter the timing to achieve the desired results */
978 if (data->flag & FCM_STEPPED_NO_BEFORE) {
979 if (evaltime < data->start_frame)
982 if (data->flag & FCM_STEPPED_NO_AFTER) {
983 if (evaltime > data->end_frame)
987 /* we snap to the start of the previous closest block of 'step_size' frames
988 * after the start offset has been discarded
991 snapblock = (int)((evaltime - data->offset) / data->step_size);
993 /* reapply the offset, and multiple the snapblock by the size of the steps to get
994 * the new time to evaluate at
996 return ((float)snapblock * data->step_size) + data->offset;
999 static FModifierTypeInfo FMI_STEPPED = {
1000 FMODIFIER_TYPE_STEPPED, /* type */
1001 sizeof(FMod_Limits), /* size */
1002 FMI_TYPE_GENERATE_CURVE, /* action type */ /* XXX... err... */
1003 FMI_REQUIRES_RUNTIME_CHECK, /* requirements */
1004 N_("Stepped"), /* name */
1005 "FMod_Stepped", /* struct name */
1006 NULL, /* free data */
1007 NULL, /* copy data */
1008 fcm_stepped_new_data, /* new data */
1010 fcm_stepped_time, /* evaluate time */
1011 NULL, /* evaluate */
1012 NULL, /* evaluate time with storage */
1013 NULL /* evaluate with storage */
1016 /* F-Curve Modifier API --------------------------- */
1017 /* All of the F-Curve Modifier api functions use FModifierTypeInfo structs to carry out
1018 * and operations that involve F-Curve modifier specific code.
1021 /* These globals only ever get directly accessed in this file */
1022 static FModifierTypeInfo *fmodifiersTypeInfo[FMODIFIER_NUM_TYPES];
1023 static short FMI_INIT = 1; /* when non-zero, the list needs to be updated */
1025 /* This function only gets called when FMI_INIT is non-zero */
1026 static void fmods_init_typeinfo(void)
1028 fmodifiersTypeInfo[0] = NULL; /* 'Null' F-Curve Modifier */
1029 fmodifiersTypeInfo[1] = &FMI_GENERATOR; /* Generator F-Curve Modifier */
1030 fmodifiersTypeInfo[2] = &FMI_FN_GENERATOR; /* Built-In Function Generator F-Curve Modifier */
1031 fmodifiersTypeInfo[3] = &FMI_ENVELOPE; /* Envelope F-Curve Modifier */
1032 fmodifiersTypeInfo[4] = &FMI_CYCLES; /* Cycles F-Curve Modifier */
1033 fmodifiersTypeInfo[5] = &FMI_NOISE; /* Apply-Noise F-Curve Modifier */
1034 fmodifiersTypeInfo[6] = NULL /*&FMI_FILTER*/; /* Filter F-Curve Modifier */ // XXX unimplemented
1035 fmodifiersTypeInfo[7] = &FMI_PYTHON; /* Custom Python F-Curve Modifier */
1036 fmodifiersTypeInfo[8] = &FMI_LIMITS; /* Limits F-Curve Modifier */
1037 fmodifiersTypeInfo[9] = &FMI_STEPPED; /* Stepped F-Curve Modifier */
1040 /* This function should be used for getting the appropriate type-info when only
1041 * a F-Curve modifier type is known
1043 const FModifierTypeInfo *get_fmodifier_typeinfo(int type)
1045 /* initialize the type-info list? */
1047 fmods_init_typeinfo();
1051 /* only return for valid types */
1052 if ((type >= FMODIFIER_TYPE_NULL) &&
1053 (type < FMODIFIER_NUM_TYPES))
1055 /* there shouldn't be any segfaults here... */
1056 return fmodifiersTypeInfo[type];
1059 printf("No valid F-Curve Modifier type-info data available. Type = %i\n", type);
1065 /* This function should always be used to get the appropriate type-info, as it
1066 * has checks which prevent segfaults in some weird cases.
1068 const FModifierTypeInfo *fmodifier_get_typeinfo(FModifier *fcm)
1070 /* only return typeinfo for valid modifiers */
1072 return get_fmodifier_typeinfo(fcm->type);
1077 /* API --------------------------- */
1079 /* Add a new F-Curve Modifier to the given F-Curve of a certain type */
1080 FModifier *add_fmodifier(ListBase *modifiers, int type)
1082 const FModifierTypeInfo *fmi = get_fmodifier_typeinfo(type);
1086 if (ELEM(NULL, modifiers, fmi))
1089 /* special checks for whether modifier can be added */
1090 if ((modifiers->first) && (type == FMODIFIER_TYPE_CYCLES)) {
1091 /* cycles modifier must be first in stack, so for now, don't add if it can't be */
1092 /* TODO: perhaps there is some better way, but for now, */
1093 printf("Error: Cannot add 'Cycles' modifier to F-Curve, as 'Cycles' modifier can only be first in stack.\n");
1097 /* add modifier itself */
1098 fcm = MEM_callocN(sizeof(FModifier), "F-Curve Modifier");
1100 fcm->flag = FMODIFIER_FLAG_EXPANDED;
1101 fcm->influence = 1.0f;
1102 BLI_addtail(modifiers, fcm);
1104 /* tag modifier as "active" if no other modifiers exist in the stack yet */
1105 if (BLI_listbase_is_single(modifiers))
1106 fcm->flag |= FMODIFIER_FLAG_ACTIVE;
1108 /* add modifier's data */
1109 fcm->data = MEM_callocN(fmi->size, fmi->structName);
1111 /* init custom settings if necessary */
1113 fmi->new_data(fcm->data);
1115 /* return modifier for further editing */
1119 /* Make a copy of the specified F-Modifier */
1120 FModifier *copy_fmodifier(FModifier *src)
1122 const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(src);
1129 /* copy the base data, clearing the links */
1130 dst = MEM_dupallocN(src);
1131 dst->next = dst->prev = NULL;
1133 /* make a new copy of the F-Modifier's data */
1134 dst->data = MEM_dupallocN(src->data);
1136 /* only do specific constraints if required */
1137 if (fmi && fmi->copy_data)
1138 fmi->copy_data(dst, src);
1140 /* return the new modifier */
1144 /* Duplicate all of the F-Modifiers in the Modifier stacks */
1145 void copy_fmodifiers(ListBase *dst, ListBase *src)
1147 FModifier *fcm, *srcfcm;
1149 if (ELEM(NULL, dst, src))
1152 BLI_listbase_clear(dst);
1153 BLI_duplicatelist(dst, src);
1155 for (fcm = dst->first, srcfcm = src->first; fcm && srcfcm; srcfcm = srcfcm->next, fcm = fcm->next) {
1156 const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm);
1158 /* make a new copy of the F-Modifier's data */
1159 fcm->data = MEM_dupallocN(fcm->data);
1161 /* only do specific constraints if required */
1162 if (fmi && fmi->copy_data)
1163 fmi->copy_data(fcm, srcfcm);
1167 /* Remove and free the given F-Modifier from the given stack */
1168 bool remove_fmodifier(ListBase *modifiers, FModifier *fcm)
1170 const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm);
1176 /* free modifier's special data (stored inside fcm->data) */
1178 if (fmi && fmi->free_data)
1179 fmi->free_data(fcm);
1181 /* free modifier's data (fcm->data) */
1182 MEM_freeN(fcm->data);
1185 /* remove modifier from stack */
1187 BLI_freelinkN(modifiers, fcm);
1191 /* XXX this case can probably be removed some day, as it shouldn't happen... */
1192 printf("remove_fmodifier() - no modifier stack given\n");
1198 /* Remove all of a given F-Curve's modifiers */
1199 void free_fmodifiers(ListBase *modifiers)
1201 FModifier *fcm, *fmn;
1204 if (modifiers == NULL)
1207 /* free each modifier in order - modifier is unlinked from list and freed */
1208 for (fcm = modifiers->first; fcm; fcm = fmn) {
1210 remove_fmodifier(modifiers, fcm);
1214 /* Find the active F-Modifier */
1215 FModifier *find_active_fmodifier(ListBase *modifiers)
1220 if (ELEM(NULL, modifiers, modifiers->first))
1223 /* loop over modifiers until 'active' one is found */
1224 for (fcm = modifiers->first; fcm; fcm = fcm->next) {
1225 if (fcm->flag & FMODIFIER_FLAG_ACTIVE)
1229 /* no modifier is active */
1233 /* Set the active F-Modifier */
1234 void set_active_fmodifier(ListBase *modifiers, FModifier *fcm)
1239 if (ELEM(NULL, modifiers, modifiers->first))
1242 /* deactivate all, and set current one active */
1243 for (fm = modifiers->first; fm; fm = fm->next)
1244 fm->flag &= ~FMODIFIER_FLAG_ACTIVE;
1246 /* make given modifier active */
1248 fcm->flag |= FMODIFIER_FLAG_ACTIVE;
1251 /* Do we have any modifiers which match certain criteria
1252 * - mtype - type of modifier (if 0, doesn't matter)
1253 * - acttype - type of action to perform (if -1, doesn't matter)
1255 bool list_has_suitable_fmodifier(ListBase *modifiers, int mtype, short acttype)
1259 /* if there are no specific filtering criteria, just skip */
1260 if ((mtype == 0) && (acttype == 0))
1261 return (modifiers && modifiers->first);
1264 if (ELEM(NULL, modifiers, modifiers->first))
1267 /* find the first mdifier fitting these criteria */
1268 for (fcm = modifiers->first; fcm; fcm = fcm->next) {
1269 const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm);
1270 short mOk = 1, aOk = 1; /* by default 1, so that when only one test, won't fail */
1272 /* check if applicable ones are fullfilled */
1274 mOk = (fcm->type == mtype);
1276 aOk = (fmi->acttype == acttype);
1278 /* if both are ok, we've found a hit */
1287 /* Evaluation API --------------------------- */
1289 FModifierStackStorage *evaluate_fmodifiers_storage_new(ListBase *modifiers)
1293 /* Sanity checks. */
1294 if (ELEM(NULL, modifiers, modifiers->last)) {
1298 for (fcm = modifiers->last; fcm; fcm = fcm->prev) {
1299 const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm);
1305 if (fmi->requires & FMI_REQUIRES_STORAGE) {
1306 return (FModifierStackStorage *) BLI_ghash_new(BLI_ghashutil_ptrhash,
1307 BLI_ghashutil_ptrcmp,
1308 "fmodifier stack temp storage");
1315 void evaluate_fmodifiers_storage_free(FModifierStackStorage *storage)
1317 if (storage != NULL) {
1318 BLI_ghash_free((GHash *) storage, NULL, NULL);
1322 void fmodifiers_storage_put(FModifierStackStorage *storage, FModifier *fcm, void *data)
1324 BLI_assert(storage != NULL);
1326 BLI_ghash_insert((GHash *) storage, fcm, data);
1329 void fmodifiers_storage_remove(FModifierStackStorage *storage, FModifier *fcm)
1331 BLI_assert(storage != NULL);
1333 BLI_ghash_remove((GHash *) storage, fcm, NULL, NULL);
1336 void *fmodifiers_storage_get(FModifierStackStorage *storage, FModifier *fcm)
1338 BLI_assert(storage != NULL);
1340 return BLI_ghash_lookup((GHash *) storage, fcm);
1343 /* helper function - calculate influence of FModifier */
1344 static float eval_fmodifier_influence(FModifier *fcm, float evaltime)
1352 /* should we use influence stored in modifier or not
1353 * NOTE: this is really just a hack so that we don't need to version patch old files ;)
1355 if (fcm->flag & FMODIFIER_FLAG_USEINFLUENCE)
1356 influence = fcm->influence;
1360 /* restricted range or full range? */
1361 if (fcm->flag & FMODIFIER_FLAG_RANGERESTRICT) {
1362 if ((evaltime <= fcm->sfra) || (evaltime >= fcm->efra)) {
1366 else if ((evaltime > fcm->sfra) && (evaltime < fcm->sfra + fcm->blendin)) {
1367 /* blend in range */
1368 float a = fcm->sfra;
1369 float b = fcm->sfra + fcm->blendin;
1370 return influence * (evaltime - a) / (b - a);
1372 else if ((evaltime < fcm->efra) && (evaltime > fcm->efra - fcm->blendout)) {
1373 /* blend out range */
1374 float a = fcm->efra;
1375 float b = fcm->efra - fcm->blendout;
1376 return influence * (evaltime - a) / (b - a);
1380 /* just return the influence of the modifier */
1384 /* evaluate time modifications imposed by some F-Curve Modifiers
1385 * - this step acts as an optimization to prevent the F-Curve stack being evaluated
1386 * several times by modifiers requesting the time be modified, as the final result
1387 * would have required using the modified time
1388 * - modifiers only ever receive the unmodified time, as subsequent modifiers should be
1389 * working on the 'global' result of the modified curve, not some localised segment,
1390 * so nevaltime gets set to whatever the last time-modifying modifier likes...
1391 * - we start from the end of the stack, as only the last one matters for now
1393 float evaluate_time_fmodifiers(FModifierStackStorage *storage, ListBase *modifiers,
1394 FCurve *fcu, float cvalue, float evaltime)
1399 if (ELEM(NULL, modifiers, modifiers->last))
1402 /* Starting from the end of the stack, calculate the time effects of various stacked modifiers
1403 * on the time the F-Curve should be evaluated at.
1405 * This is done in reverse order to standard evaluation, as when this is done in standard
1406 * order, each modifier would cause jumps to other points in the curve, forcing all
1407 * previous ones to be evaluated again for them to be correct. However, if we did in the
1408 * reverse order as we have here, we can consider them a macro to micro type of waterfall
1409 * effect, which should get us the desired effects when using layered time manipulations
1410 * (such as multiple 'stepped' modifiers in sequence, causing different stepping rates)
1412 for (fcm = modifiers->last; fcm; fcm = fcm->prev) {
1413 const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm);
1418 /* if modifier cannot be applied on this frame (whatever scale it is on, it won't affect the results)
1419 * hence we shouldn't bother seeing what it would do given the chance
1421 if ((fcm->flag & FMODIFIER_FLAG_RANGERESTRICT) == 0 ||
1422 ((fcm->sfra <= evaltime) && (fcm->efra >= evaltime)) )
1424 /* only evaluate if there's a callback for this */
1425 if (fmi->evaluate_modifier_time || fmi->evaluate_modifier_time_storage) {
1426 if ((fcm->flag & (FMODIFIER_FLAG_DISABLED | FMODIFIER_FLAG_MUTED)) == 0) {
1427 float influence = eval_fmodifier_influence(fcm, evaltime);
1430 if ((fmi->requires & FMI_REQUIRES_STORAGE) == 0) {
1431 nval = fmi->evaluate_modifier_time(fcu, fcm, cvalue, evaltime);
1434 nval = fmi->evaluate_modifier_time_storage(storage, fcu, fcm, cvalue, evaltime);
1437 evaltime = interpf(nval, evaltime, influence);
1443 /* return the modified evaltime */
1447 /* Evaluates the given set of F-Curve Modifiers using the given data
1448 * Should only be called after evaluate_time_fmodifiers() has been called...
1450 void evaluate_value_fmodifiers(FModifierStackStorage *storage, ListBase *modifiers,
1451 FCurve *fcu, float *cvalue, float evaltime)
1456 if (ELEM(NULL, modifiers, modifiers->first))
1459 /* evaluate modifiers */
1460 for (fcm = modifiers->first; fcm; fcm = fcm->next) {
1461 const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm);
1466 /* only evaluate if there's a callback for this, and if F-Modifier can be evaluated on this frame */
1467 if ((fcm->flag & FMODIFIER_FLAG_RANGERESTRICT) == 0 ||
1468 ((fcm->sfra <= evaltime) && (fcm->efra >= evaltime)) )
1470 if (fmi->evaluate_modifier || fmi->evaluate_modifier_storage) {
1471 if ((fcm->flag & (FMODIFIER_FLAG_DISABLED | FMODIFIER_FLAG_MUTED)) == 0) {
1472 float influence = eval_fmodifier_influence(fcm, evaltime);
1473 float nval = *cvalue;
1475 if ((fmi->requires & FMI_REQUIRES_STORAGE) == 0) {
1476 fmi->evaluate_modifier(fcu, fcm, &nval, evaltime);
1479 fmi->evaluate_modifier_storage(storage, fcu, fcm, &nval, evaltime);
1482 *cvalue = interpf(nval, *cvalue, influence);
1491 /* Bake modifiers for given F-Curve to curve sample data, in the frame range defined
1492 * by start and end (inclusive).
1494 void fcurve_bake_modifiers(FCurve *fcu, int start, int end)
1496 ChannelDriver *driver;
1499 /* TODO: make these tests report errors using reports not printf's */
1500 if (ELEM(NULL, fcu, fcu->modifiers.first)) {
1501 printf("Error: No F-Curve with F-Curve Modifiers to Bake\n");
1505 /* temporarily, disable driver while we sample, so that they don't influence the outcome */
1506 driver = fcu->driver;
1509 /* bake the modifiers, by sampling the curve at each frame */
1510 fcurve_store_samples(fcu, NULL, start, end, fcurve_samplingcb_evalcurve);
1512 /* free the modifiers now */
1513 free_fmodifiers(&fcu->modifiers);
1515 /* restore driver */
1516 fcu->driver = driver;