Blender V5.0
fmodifier.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009 Blender Authors, Joshua Leung. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm> /* For `min/max`. */
10#include <cfloat>
11#include <cmath>
12#include <cstdio>
13#include <cstring>
14
15#include "MEM_guardedalloc.h"
16
17#include "CLG_log.h"
18
19#include "DNA_anim_types.h"
20#include "DNA_screen_types.h"
21
22#include "BLT_translation.hh"
23
24#include "BLI_listbase.h"
25#include "BLI_math_base.h"
26#include "BLI_noise.h"
27#include "BLI_noise.hh"
28#include "BLI_utildefines.h"
29
30#include "BKE_fcurve.hh"
31
32static CLG_LogRef LOG = {"anim.fmodifier"};
33
34/* -------------------------------------------------------------------- */
37
38/* Info ------------------------------- */
39
40/* F-Modifiers are modifiers which operate on F-Curves. However, they can also be defined
41 * on NLA-Strips to affect all of the F-Curves referenced by the NLA-Strip.
42 */
43
44/* Template --------------------------- */
45
46/* Each modifier defines a set of functions, which will be called at the appropriate
47 * times. In addition to this, each modifier should have a type-info struct, where
48 * its functions are attached for use.
49 */
50
51/* Template for type-info data:
52 * - make a copy of this when creating new modifiers, and just change the functions
53 * pointed to as necessary
54 * - although the naming of functions doesn't matter, it would help for code
55 * readability, to follow the same naming convention as is presented here
56 * - any functions that a constraint doesn't need to define, don't define
57 * for such cases, just use nullptr
58 * - these should be defined after all the functions have been defined, so that
59 * forward-definitions/prototypes don't need to be used!
60 * - keep this copy #if-def'd so that future modifier can get based off this
61 */
62#if 0
63static FModifierTypeInfo FMI_MODNAME = {
64 /*type*/ FMODIFIER_TYPE_MODNAME,
65 /*size*/ sizeof(FMod_ModName),
66 /*acttype*/ FMI_TYPE_SOME_ACTION,
67 /*requires_flag*/ FMI_REQUIRES_SOME_REQUIREMENT,
68 /*name*/ "Modifier Name",
69 /*struct_name*/ "FMod_ModName",
70 /*storage_size*/ 0,
71 /*free_data*/ fcm_modname_free,
72 /*copy_data*/ fcm_modname_copy,
73 /*new_data*/ fcm_modname_new_data,
74 /*verify_data*/ fcm_modname_verify,
75 /*evaluate_modifier_time*/ fcm_modname_time,
76 /*evaluate_modifier*/ fcm_modname_evaluate,
77};
78#endif
79
80/* Generator F-Curve Modifier --------------------------- */
81
82/* Generators available:
83 * 1) simple polynomial generator:
84 * - Expanded form:
85 * (y = C[0]*(x^(n)) + C[1]*(x^(n-1)) + ... + C[n])
86 * - Factorized form:
87 * (y = (C[0][0]*x + C[0][1]) * (C[1][0]*x + C[1][1]) * ... * (C[n][0]*x + C[n][1]))
88 */
89
91{
93
94 /* free polynomial coefficients array */
95 if (data->coefficients) {
96 MEM_freeN(data->coefficients);
97 }
98}
99
100static void fcm_generator_copy(FModifier *fcm, const FModifier *src)
101{
102 FMod_Generator *gen = (FMod_Generator *)fcm->data;
103 FMod_Generator *ogen = (FMod_Generator *)src->data;
104
105 /* copy coefficients array? */
106 if (ogen->coefficients) {
107 gen->coefficients = static_cast<float *>(MEM_dupallocN(ogen->coefficients));
108 }
109}
110
111static void fcm_generator_new_data(void *mdata)
112{
114 float *cp;
115
116 /* set default generator to be linear 0-1 (gradient = 1, y-offset = 0) */
117 data->poly_order = 1;
118 data->arraysize = 2;
119 cp = data->coefficients = MEM_calloc_arrayN<float>(2, "FMod_Generator_Coefs");
120 cp[0] = 0; /* y-offset */
121 cp[1] = 1; /* gradient */
122}
123
125{
127
128 /* requirements depend on mode */
129 switch (data->mode) {
130 case FCM_GENERATOR_POLYNOMIAL: /* expanded polynomial expression */
131 {
132 const int arraysize_new = data->poly_order + 1;
133 /* arraysize needs to be order+1, so resize if not */
134 if (data->arraysize != arraysize_new) {
135 data->coefficients = static_cast<float *>(
136 MEM_recallocN(data->coefficients, sizeof(float) * arraysize_new));
137 data->arraysize = arraysize_new;
138 }
139 break;
140 }
141 case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* expanded polynomial expression */
142 {
143 const int arraysize_new = data->poly_order * 2;
144 /* arraysize needs to be (2 * order), so resize if not */
145 if (data->arraysize != arraysize_new) {
146 data->coefficients = static_cast<float *>(
147 MEM_recallocN(data->coefficients, sizeof(float) * arraysize_new));
148 data->arraysize = arraysize_new;
149 }
150 break;
151 }
152 }
153}
154
155static void fcm_generator_evaluate(const FCurve * /*fcu*/,
156 const FModifier *fcm,
157 float *cvalue,
158 float evaltime,
159 void * /*storage*/)
160{
162
163 /* behavior depends on mode
164 * NOTE: the data in its default state is fine too
165 */
166 switch (data->mode) {
167 case FCM_GENERATOR_POLYNOMIAL: /* expanded polynomial expression */
168 {
169 /* we overwrite cvalue with the sum of the polynomial */
170 float *powers = MEM_calloc_arrayN<float>(data->arraysize, "Poly Powers");
171 float value = 0.0f;
172
173 /* for each x^n, precalculate value based on previous one first... this should be
174 * faster that calling pow() for each entry
175 */
176 for (uint i = 0; i < data->arraysize; i++) {
177 /* first entry is x^0 = 1, otherwise, calculate based on previous */
178 if (i) {
179 powers[i] = powers[i - 1] * evaltime;
180 }
181 else {
182 powers[0] = 1;
183 }
184 }
185
186 /* for each coefficient, add to value, which we'll write to *cvalue in one go */
187 for (uint i = 0; i < data->arraysize; i++) {
188 value += data->coefficients[i] * powers[i];
189 }
190
191 /* only if something changed, write *cvalue in one go */
192 if (data->poly_order) {
193 if (data->flag & FCM_GENERATOR_ADDITIVE) {
194 *cvalue += value;
195 }
196 else {
197 *cvalue = value;
198 }
199 }
200
201 /* cleanup */
202 if (powers) {
203 MEM_freeN(powers);
204 }
205 break;
206 }
207 case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* Factorized polynomial */
208 {
209 float value = 1.0f, *cp = nullptr;
210 uint i;
211
212 /* For each coefficient pair,
213 * solve for that bracket before accumulating in value by multiplying. */
214 for (cp = data->coefficients, i = 0; (cp) && (i < uint(data->poly_order)); cp += 2, i++) {
215 value *= (cp[0] * evaltime + cp[1]);
216 }
217
218 /* only if something changed, write *cvalue in one go */
219 if (data->poly_order) {
220 if (data->flag & FCM_GENERATOR_ADDITIVE) {
221 *cvalue += value;
222 }
223 else {
224 *cvalue = value;
225 }
226 }
227 break;
228 }
229 }
230}
231
234 /*size*/ sizeof(FMod_Generator),
235 /*acttype*/ FMI_TYPE_GENERATE_CURVE,
236 /*requires_flag*/ FMI_REQUIRES_NOTHING,
237 /*name*/ CTX_N_(BLT_I18NCONTEXT_ID_ACTION, "Generator"),
238 /*struct_name*/ "FMod_Generator",
239 /*storage_size*/ 0,
240 /*free_data*/ fcm_generator_free,
241 /*copy_data*/ fcm_generator_copy,
242 /*new_data*/ fcm_generator_new_data,
243 /*verify_data*/ fcm_generator_verify,
244 /*evaluate_modifier_time*/ nullptr,
245 /*evaluate_modifier*/ fcm_generator_evaluate,
246};
247
248/* Built-In Function Generator F-Curve Modifier --------------------------- */
249
250/* This uses the general equation for equations:
251 * y = amplitude * fn(phase_multiplier * x + phase_offset) + y_offset
252 *
253 * where amplitude, phase_multiplier/offset, y_offset are user-defined coefficients,
254 * x is the evaluation 'time', and 'y' is the resultant value
255 *
256 * Functions available are
257 * sin, cos, tan, sinc (normalized sin), natural log, square root
258 */
259
260static void fcm_fn_generator_new_data(void *mdata)
261{
263
264 /* set amplitude and phase multiplier to 1.0f so that something is generated */
265 data->amplitude = 1.0f;
266 data->phase_multiplier = 1.0f;
267}
268
269/* Unary 'normalized sine' function
270 * y = sin(PI + x) / (PI * x),
271 * except for x = 0 when y = 1.
272 */
273static double sinc(double x)
274{
275 if (fabs(x) < 0.0001) {
276 return 1.0;
277 }
278
279 return sin(M_PI * x) / (M_PI * x);
280}
281
282static void fcm_fn_generator_evaluate(const FCurve * /*fcu*/,
283 const FModifier *fcm,
284 float *cvalue,
285 float evaltime,
286 void * /*storage*/)
287{
289 double arg = data->phase_multiplier * evaltime + data->phase_offset;
290 double (*fn)(double v) = nullptr;
291
292 /* get function pointer to the func to use:
293 * WARNING: must perform special argument validation hereto guard against crashes
294 */
295 switch (data->type) {
296 /* simple ones */
297 case FCM_GENERATOR_FN_SIN: /* sine wave */
298 fn = sin;
299 break;
300 case FCM_GENERATOR_FN_COS: /* cosine wave */
301 fn = cos;
302 break;
303 case FCM_GENERATOR_FN_SINC: /* normalized sine wave */
304 fn = sinc;
305 break;
306
307 /* validation required */
308 case FCM_GENERATOR_FN_TAN: /* tangent wave */
309 {
310 /* check that argument is not on one of the discontinuities (i.e. 90deg, 270 deg, etc) */
311 if (IS_EQ(fmod((arg - M_PI_2), M_PI), 0.0)) {
312 if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) {
313 *cvalue = 0.0f; /* no value possible here */
314 }
315 }
316 else {
317 fn = tan;
318 }
319 break;
320 }
321 case FCM_GENERATOR_FN_LN: /* natural log */
322 {
323 /* check that value is greater than 1? */
324 if (arg > 1.0) {
325 fn = log;
326 }
327 else {
328 if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) {
329 *cvalue = 0.0f; /* no value possible here */
330 }
331 }
332 break;
333 }
334 case FCM_GENERATOR_FN_SQRT: /* square root */
335 {
336 /* no negative numbers */
337 if (arg > 0.0) {
338 fn = sqrt;
339 }
340 else {
341 if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) {
342 *cvalue = 0.0f; /* no value possible here */
343 }
344 }
345 break;
346 }
347 default:
348 CLOG_ERROR(&LOG, "Invalid Function-Generator for F-Modifier - %d", data->type);
349 break;
350 }
351
352 /* execute function callback to set value if appropriate */
353 if (fn) {
354 float value = (data->amplitude * float(fn(arg)) + data->value_offset);
355
356 if (data->flag & FCM_GENERATOR_ADDITIVE) {
357 *cvalue += value;
358 }
359 else {
360 *cvalue = value;
361 }
362 }
363}
364
367 /*size*/ sizeof(FMod_FunctionGenerator),
368 /*acttype*/ FMI_TYPE_GENERATE_CURVE,
369 /*requires_flag*/ FMI_REQUIRES_NOTHING,
370 /*name*/ CTX_N_(BLT_I18NCONTEXT_ID_ACTION, "Built-In Function"),
371 /*struct_name*/ "FMod_FunctionGenerator",
372 /*storage_size*/ 0,
373 /*free_data*/ nullptr,
374 /*copy_data*/ nullptr,
375 /*new_data*/ fcm_fn_generator_new_data,
376 /*verify_data*/ nullptr,
377 /*evaluate_modifier_time*/ nullptr,
378 /*evaluate_modifier*/ fcm_fn_generator_evaluate,
379};
380
381/* Envelope F-Curve Modifier --------------------------- */
382
384{
385 FMod_Envelope *env = (FMod_Envelope *)fcm->data;
386
387 /* free envelope data array */
388 if (env->data) {
389 MEM_freeN(env->data);
390 }
391}
392
393static void fcm_envelope_copy(FModifier *fcm, const FModifier *src)
394{
395 FMod_Envelope *env = (FMod_Envelope *)fcm->data;
396 FMod_Envelope *oenv = (FMod_Envelope *)src->data;
397
398 /* copy envelope data array */
399 if (oenv->data) {
400 env->data = static_cast<FCM_EnvelopeData *>(MEM_dupallocN(oenv->data));
401 }
402}
403
404static void fcm_envelope_new_data(void *mdata)
405{
406 FMod_Envelope *env = (FMod_Envelope *)mdata;
407
408 /* set default min/max ranges */
409 env->min = -1.0f;
410 env->max = 1.0f;
411}
412
414{
415 FMod_Envelope *env = (FMod_Envelope *)fcm->data;
416
417 /* if the are points, perform bubble-sort on them, as user may have changed the order */
418 if (env->data) {
419 /* XXX todo... */
420 }
421}
422
423static void fcm_envelope_evaluate(const FCurve * /*fcu*/,
424 const FModifier *fcm,
425 float *cvalue,
426 float evaltime,
427 void * /*storage*/)
428{
429 FMod_Envelope *env = (FMod_Envelope *)fcm->data;
430 FCM_EnvelopeData *fed, *prevfed, *lastfed;
431 float min = 0.0f, max = 0.0f, fac = 0.0f;
432 int a;
433
434 /* get pointers */
435 if (env->data == nullptr) {
436 return;
437 }
438 prevfed = env->data;
439 fed = prevfed + 1;
440 lastfed = prevfed + (env->totvert - 1);
441
442 /* get min/max values for envelope at evaluation time (relative to mid-value) */
443 if (prevfed->time >= evaltime) {
444 /* before or on first sample, so just extend value */
445 min = prevfed->min;
446 max = prevfed->max;
447 }
448 else if (lastfed->time <= evaltime) {
449 /* after or on last sample, so just extend value */
450 min = lastfed->min;
451 max = lastfed->max;
452 }
453 else {
454 /* evaltime occurs somewhere between segments */
455 /* TODO: implement binary search for this to make it faster? */
456 for (a = 0; prevfed && fed && (a < env->totvert - 1); a++, prevfed = fed, fed++) {
457 /* evaltime occurs within the interval defined by these two envelope points */
458 if ((prevfed->time <= evaltime) && (fed->time >= evaltime)) {
459 float afac, bfac, diff;
460
461 diff = fed->time - prevfed->time;
462 afac = (evaltime - prevfed->time) / diff;
463 bfac = (fed->time - evaltime) / diff;
464
465 min = bfac * prevfed->min + afac * fed->min;
466 max = bfac * prevfed->max + afac * fed->max;
467
468 break;
469 }
470 }
471 }
472
473 /* adjust *cvalue
474 * - fac is the ratio of how the current y-value corresponds to the reference range
475 * - thus, the new value is found by mapping the old range to the new!
476 */
477 fac = (*cvalue - (env->midval + env->min)) / (env->max - env->min);
478 *cvalue = min + fac * (max - min);
479}
480
483 /*size*/ sizeof(FMod_Envelope),
484 /*acttype*/ FMI_TYPE_REPLACE_VALUES,
485 /*requires_flag*/ 0,
486 /*name*/ CTX_N_(BLT_I18NCONTEXT_ID_ACTION, "Envelope"),
487 /*struct_name*/ "FMod_Envelope",
488 /*storage_size*/ 0,
489 /*free_data*/ fcm_envelope_free,
490 /*copy_data*/ fcm_envelope_copy,
491 /*new_data*/ fcm_envelope_new_data,
492 /*verify_data*/ fcm_envelope_verify,
493 /*evaluate_modifier_time*/ nullptr,
494 /*evaluate_modifier*/ fcm_envelope_evaluate,
495};
496
497/* exported function for finding points */
498
499/* Binary search algorithm for finding where to insert Envelope Data Point.
500 * Returns the index to insert at (data already at that index will be offset if replace is 0)
501 */
502#define BINARYSEARCH_FRAMEEQ_THRESH 0.0001f
503
505 float frame,
506 int arraylen,
507 bool *r_exists)
508{
509 int start = 0, end = arraylen;
510 int loopbreaker = 0, maxloop = arraylen * 2;
511
512 /* initialize exists-flag first */
513 *r_exists = false;
514
515 /* sneaky optimizations (don't go through searching process if...):
516 * - keyframe to be added is to be added out of current bounds
517 * - keyframe to be added would replace one of the existing ones on bounds
518 */
519 if ((arraylen <= 0) || (array == nullptr)) {
520 CLOG_WARN(&LOG, "encountered invalid array");
521 return 0;
522 }
523
524 /* check whether to add before/after/on */
525 float framenum;
526
527 /* 'First' Point (when only one point, this case is used) */
528 framenum = array[0].time;
529 if (IS_EQT(frame, framenum, BINARYSEARCH_FRAMEEQ_THRESH)) {
530 *r_exists = true;
531 return 0;
532 }
533 if (frame < framenum) {
534 return 0;
535 }
536
537 /* 'Last' Point */
538 framenum = array[(arraylen - 1)].time;
539 if (IS_EQT(frame, framenum, BINARYSEARCH_FRAMEEQ_THRESH)) {
540 *r_exists = true;
541 return (arraylen - 1);
542 }
543 if (frame > framenum) {
544 return arraylen;
545 }
546
547 /* most of the time, this loop is just to find where to put it
548 * - 'loopbreaker' is just here to prevent infinite loops
549 */
550 for (loopbreaker = 0; (start <= end) && (loopbreaker < maxloop); loopbreaker++) {
551 /* compute and get midpoint */
552
553 /* we calculate the midpoint this way to avoid int overflows... */
554 int mid = start + ((end - start) / 2);
555
556 float midfra = array[mid].time;
557
558 /* check if exactly equal to midpoint */
559 if (IS_EQT(frame, midfra, BINARYSEARCH_FRAMEEQ_THRESH)) {
560 *r_exists = true;
561 return mid;
562 }
563
564 /* repeat in upper/lower half */
565 if (frame > midfra) {
566 start = mid + 1;
567 }
568 else if (frame < midfra) {
569 end = mid - 1;
570 }
571 }
572
573 /* print error if loop-limit exceeded */
574 if (loopbreaker == (maxloop - 1)) {
575 CLOG_ERROR(&LOG, "binary search was taking too long");
576
577 /* Include debug info. */
579 "\tround = %d: start = %d, end = %d, arraylen = %d",
580 loopbreaker,
581 start,
582 end,
583 arraylen);
584 }
585
586 /* not found, so return where to place it */
587 return start;
588}
589#undef BINARYSEARCH_FRAMEEQ_THRESH
590
591/* Cycles F-Curve Modifier --------------------------- */
592
593/* This modifier changes evaltime to something that exists within the curve's frame-range,
594 * then re-evaluates modifier stack up to this point using the new time. This re-entrant behavior
595 * is very likely to be more time-consuming than the original approach...
596 * (which was tightly integrated into the calculation code...).
597 *
598 * NOTE: this needs to be at the start of the stack to be of use,
599 * as it needs to know the extents of the keyframes/sample-data.
600 *
601 * Possible TODO: store length of cycle information that can be initialized from the extents of
602 * the keyframes/sample-data, and adjusted as appropriate.
603 */
604
605/* temp data used during evaluation */
607 float cycyofs; /* y-offset to apply */
608};
609
610static void fcm_cycles_new_data(void *mdata)
611{
612 FMod_Cycles *data = (FMod_Cycles *)mdata;
613
614 /* turn on cycles by default */
615 data->before_mode = data->after_mode = FCM_EXTRAPOLATE_CYCLIC;
616}
617
618static float fcm_cycles_time(
619 const FCurve *fcu, const FModifier *fcm, float /*cvalue*/, float evaltime, void *storage_)
620{
621 const FMod_Cycles *data = (FMod_Cycles *)fcm->data;
622 tFCMED_Cycles *storage = static_cast<tFCMED_Cycles *>(storage_);
623 float firstkey[2], lastkey[2], cycyofs = 0.0f;
624 short side = 0, mode = 0;
625 int cycles = 0;
626 float ofs = 0;
627
628 /* Initialize storage. */
629 storage->cycyofs = 0;
630
631 /* It shouldn't be possible for this modifier type to be anywhere other than
632 * the top of the stack. If it is, something's wrong. */
633 BLI_assert(fcm->prev == nullptr);
634
635 if (fcu == nullptr || (fcu->bezt == nullptr && fcu->fpt == nullptr)) {
636 return evaltime;
637 }
638
639 /* calculate new evaltime due to cyclic interpolation */
640 if (fcu->bezt) {
641 const BezTriple *firstbezt = &fcu->bezt[0];
642 const BezTriple *lastbezt = &fcu->bezt[fcu->totvert - 1];
643
644 firstkey[0] = firstbezt->vec[1][0];
645 firstkey[1] = firstbezt->vec[1][1];
646
647 lastkey[0] = lastbezt->vec[1][0];
648 lastkey[1] = lastbezt->vec[1][1];
649 }
650 else {
651 BLI_assert(fcu->fpt != nullptr);
652 const FPoint *prevfpt = fcu->fpt;
653 const FPoint *lastfpt = prevfpt + fcu->totvert - 1;
654
655 firstkey[0] = prevfpt->vec[0];
656 firstkey[1] = prevfpt->vec[1];
657
658 lastkey[0] = lastfpt->vec[0];
659 lastkey[1] = lastfpt->vec[1];
660 }
661
662 /* check if modifier will do anything
663 * 1) if in data range, definitely don't do anything
664 * 2) if before first frame or after last frame, make sure some cycling is in use
665 */
666 if (evaltime < firstkey[0]) {
667 if (data->before_mode) {
668 side = -1;
669 mode = data->before_mode;
670 cycles = data->before_cycles;
671 ofs = firstkey[0];
672 }
673 }
674 else if (evaltime > lastkey[0]) {
675 if (data->after_mode) {
676 side = 1;
677 mode = data->after_mode;
678 cycles = data->after_cycles;
679 ofs = lastkey[0];
680 }
681 }
682 if (ELEM(0, side, mode)) {
683 return evaltime;
684 }
685
686 /* find relative place within a cycle */
687 {
688 /* calculate period and amplitude (total height) of a cycle */
689 const float cycdx = lastkey[0] - firstkey[0];
690 const float cycdy = lastkey[1] - firstkey[1];
691
692 /* check if cycle is infinitely small, to be point of being impossible to use */
693 if (cycdx == 0) {
694 return evaltime;
695 }
696
697 /* Calculate the 'number' of the cycle. Needs to be a double to combat precision issues like
698 * #119360. With floats it can happen that the `cycle` jumps to the next full number, while
699 * `cyct` below is still behind. */
700 const double cycle = side * (double(evaltime) - double(ofs)) / double(cycdx);
701 /* calculate the time inside the cycle */
702 const float cyct = fmod(evaltime - ofs, cycdx);
703
704 /* check that cyclic is still enabled for the specified time */
705 if (cycles == 0) {
706 /* catch this case so that we don't exit when we have (cycles = 0)
707 * as this indicates infinite cycles...
708 */
709 }
710 else if (cycle > cycles) {
711 /* we are too far away from range to evaluate
712 * TODO: but we should still hold last value...
713 */
714 return evaltime;
715 }
716
717 /* check if 'cyclic extrapolation', and thus calculate y-offset for this cycle */
718 if (mode == FCM_EXTRAPOLATE_CYCLIC_OFFSET) {
719 if (side < 0) {
720 cycyofs = floor((evaltime - ofs) / cycdx);
721 }
722 else {
723 cycyofs = ceil((evaltime - ofs) / cycdx);
724 }
725 cycyofs *= cycdy;
726 }
727
728 /* special case for cycle start/end */
729 if (cyct == 0.0f) {
730 evaltime = (side == 1 ? lastkey[0] : firstkey[0]);
731
732 if ((mode == FCM_EXTRAPOLATE_MIRROR) && (int(cycle) % 2)) {
733 evaltime = (side == 1 ? firstkey[0] : lastkey[0]);
734 }
735 }
736 /* calculate where in the cycle we are (overwrite evaltime to reflect this) */
737 else if ((mode == FCM_EXTRAPOLATE_MIRROR) && (int(cycle + 1) % 2)) {
738 /* When 'mirror' option is used and cycle number is odd, this cycle is played in reverse
739 * - for 'before' extrapolation, we need to flip in a different way, otherwise values past
740 * then end of the curve get referenced
741 * (result of fmod will be negative, and with different phase).
742 */
743 if (side < 0) {
744 evaltime = firstkey[0] - cyct;
745 }
746 else {
747 evaltime = lastkey[0] - cyct;
748 }
749 }
750 else {
751 /* the cycle is played normally... */
752 evaltime = firstkey[0] + cyct;
753 }
754 if (evaltime < firstkey[0]) {
755 evaltime += cycdx;
756 }
757 }
758
759 /* store temp data if needed */
760 if (mode == FCM_EXTRAPOLATE_CYCLIC_OFFSET) {
761 storage->cycyofs = cycyofs;
762 }
763
764 /* return the new frame to evaluate */
765 return evaltime;
766}
767
768static void fcm_cycles_evaluate(const FCurve * /*fcu*/,
769 const FModifier * /*fcm*/,
770 float *cvalue,
771 float /*evaltime*/,
772 void *storage_)
773{
774 tFCMED_Cycles *storage = static_cast<tFCMED_Cycles *>(storage_);
775 *cvalue += storage->cycyofs;
776}
777
779 /*type*/ FMODIFIER_TYPE_CYCLES,
780 /*size*/ sizeof(FMod_Cycles),
781 /*acttype*/ FMI_TYPE_EXTRAPOLATION,
782 /*requires_flag*/ FMI_REQUIRES_ORIGINAL_DATA,
783 /*name*/ CTX_N_(BLT_I18NCONTEXT_ID_ACTION, "Cycles"),
784 /*struct_name*/ "FMod_Cycles",
785 /*storage_size*/ sizeof(tFCMED_Cycles),
786 /*free_data*/ nullptr,
787 /*copy_data*/ nullptr,
788 /*new_data*/ fcm_cycles_new_data,
789 /*verify_data*/ nullptr /*fcm_cycles_verify*/,
790 /*evaluate_modifier_time*/ fcm_cycles_time,
791 /*evaluate_modifier*/ fcm_cycles_evaluate,
792};
793
794/* Noise F-Curve Modifier --------------------------- */
795
796static void fcm_noise_new_data(void *mdata)
797{
798 FMod_Noise *data = (FMod_Noise *)mdata;
799
800 /* defaults */
801 data->size = 1.0f;
802 data->strength = 1.0f;
803 data->phase = 1.0f;
804 data->offset = 0.0f;
805 data->depth = 0;
806 data->modification = FCM_NOISE_MODIF_REPLACE;
807 data->lacunarity = 2.0f;
808 data->roughness = 0.5f;
809 data->legacy_noise = 0;
810}
811
812static void fcm_noise_evaluate(const FCurve * /*fcu*/,
813 const FModifier *fcm,
814 float *cvalue,
815 float evaltime,
816 void * /*storage*/)
817{
818 FMod_Noise *data = (FMod_Noise *)fcm->data;
819 float noise;
820 if (data->legacy_noise) {
821 /* Generate legacy noise. This is deprecated, see #123875.
822 * - 0.1 is passed as the 'z' value, otherwise evaluation fails for size = phase = 1
823 * with evaltime being an integer (which happens when evaluating on frame by frame basis)
824 */
826 data->size, evaltime - data->offset, data->phase, 0.1f, data->depth);
827 }
828 else {
829 float scale;
830 if (data->size == 0.0f) {
831 scale = 0.0;
832 }
833 else {
834 scale = 1.0 / data->size;
835 }
836 /* Adding an offset so the 0 positions are unlikely to be on full frames. The user can counter
837 * that, which is why the value is chosen to be quite obscure. */
838 const float offset = 0.61803398874;
839 /* Using float2 to generate a phase offset. Offsetting the evaltime by `offset` to ensure that
840 * the noise at full frames isn't always at 0. */
842 blender::float2((evaltime - data->offset) * scale + offset, data->phase),
843 data->depth,
844 data->roughness,
845 data->lacunarity,
846 true);
847 }
848
849 /* combine the noise with existing motion data */
850 switch (data->modification) {
852 *cvalue = *cvalue + noise * data->strength;
853 break;
855 *cvalue = *cvalue - noise * data->strength;
856 break;
858 *cvalue = *cvalue * noise * data->strength;
859 break;
861 default:
862 *cvalue = *cvalue + (noise - 0.5f) * data->strength;
863 break;
864 }
865}
866
868 /*type*/ FMODIFIER_TYPE_NOISE,
869 /*size*/ sizeof(FMod_Noise),
870 /*acttype*/ FMI_TYPE_REPLACE_VALUES,
871 /*requires_flag*/ 0,
872 /*name*/ CTX_N_(BLT_I18NCONTEXT_ID_ACTION, "Noise"),
873 /*struct_name*/ "FMod_Noise",
874 /*storage_size*/ 0,
875 /*free_data*/ nullptr,
876 /*copy_data*/ nullptr,
877 /*new_data*/ fcm_noise_new_data,
878 /*verify_data*/ nullptr /*fcm_noise_verify*/,
879 /*evaluate_modifier_time*/ nullptr,
880 /*evaluate_modifier*/ fcm_noise_evaluate,
881};
882
883/* Limits F-Curve Modifier --------------------------- */
884
885static float fcm_limits_time(const FCurve * /*fcu*/,
886 const FModifier *fcm,
887 float /*cvalue*/,
888 float evaltime,
889 void * /*storage*/)
890{
891 FMod_Limits *data = (FMod_Limits *)fcm->data;
892
893 /* check for the time limits */
894 if ((data->flag & FCM_LIMIT_XMIN) && (evaltime < data->rect.xmin)) {
895 return data->rect.xmin;
896 }
897 if ((data->flag & FCM_LIMIT_XMAX) && (evaltime > data->rect.xmax)) {
898 return data->rect.xmax;
899 }
900
901 /* modifier doesn't change time */
902 return evaltime;
903}
904
905static void fcm_limits_evaluate(const FCurve * /*fcu*/,
906 const FModifier *fcm,
907 float *cvalue,
908 float /*evaltime*/,
909 void * /*storage*/)
910{
911 FMod_Limits *data = (FMod_Limits *)fcm->data;
912
913 /* value limits now */
914 if ((data->flag & FCM_LIMIT_YMIN) && (*cvalue < data->rect.ymin)) {
915 *cvalue = data->rect.ymin;
916 }
917 if ((data->flag & FCM_LIMIT_YMAX) && (*cvalue > data->rect.ymax)) {
918 *cvalue = data->rect.ymax;
919 }
920}
921
923 /*type*/ FMODIFIER_TYPE_LIMITS,
924 /*size*/ sizeof(FMod_Limits),
925 /*acttype*/ FMI_TYPE_GENERATE_CURVE,
926 /*requires_flag*/ FMI_REQUIRES_RUNTIME_CHECK, /* XXX... err... */
927 /*name*/ CTX_N_(BLT_I18NCONTEXT_ID_ACTION, "Limits"),
928 /*struct_name*/ "FMod_Limits",
929 /*storage_size*/ 0,
930 /*free_data*/ nullptr,
931 /*copy_data*/ nullptr,
932 /*new_data*/ nullptr,
933 /*verify_data*/ nullptr,
934 /*evaluate_modifier_time*/ fcm_limits_time,
935 /*evaluate_modifier*/ fcm_limits_evaluate,
936};
937
938/* Stepped F-Curve Modifier --------------------------- */
939
940static void fcm_stepped_new_data(void *mdata)
941{
942 FMod_Stepped *data = (FMod_Stepped *)mdata;
943
944 /* just need to set the step-size to 2-frames by default */
945 /* XXX: or would 5 be more normal? */
946 data->step_size = 2.0f;
947}
948
949static float fcm_stepped_time(const FCurve * /*fcu*/,
950 const FModifier *fcm,
951 float /*cvalue*/,
952 float evaltime,
953 void * /*storage*/)
954{
956 int snapblock;
957
958 /* check range clamping to see if we should alter the timing to achieve the desired results */
959 if (data->flag & FCM_STEPPED_NO_BEFORE) {
960 if (evaltime < data->start_frame) {
961 return evaltime;
962 }
963 }
964 if (data->flag & FCM_STEPPED_NO_AFTER) {
965 if (evaltime > data->end_frame) {
966 return evaltime;
967 }
968 }
969
970 /* we snap to the start of the previous closest block of 'step_size' frames
971 * after the start offset has been discarded
972 * - i.e. round down
973 */
974 snapblock = int((evaltime - data->offset) / data->step_size);
975
976 /* reapply the offset, and multiple the snapblock by the size of the steps to get
977 * the new time to evaluate at
978 */
979 return (float(snapblock) * data->step_size) + data->offset;
980}
981
983 /*type*/ FMODIFIER_TYPE_STEPPED,
984 /*size*/ sizeof(FMod_Limits),
985 /*acttype*/ FMI_TYPE_GENERATE_CURVE,
986 /*requires_flag*/ FMI_REQUIRES_RUNTIME_CHECK, /* XXX... err... */
987 /*name*/ CTX_N_(BLT_I18NCONTEXT_ID_ACTION, "Stepped"),
988 /*struct_name*/ "FMod_Stepped",
989 /*storage_size*/ 0,
990 /*free_data*/ nullptr,
991 /*copy_data*/ nullptr,
992 /*new_data*/ fcm_stepped_new_data,
993 /*verify_data*/ nullptr,
994 /*evaluate_modifier_time*/ fcm_stepped_time,
995 /*evaluate_modifier*/ nullptr,
996};
997
999
1000/* -------------------------------------------------------------------- */
1006
1007/* These globals only ever get directly accessed in this file */
1009static short FMI_INIT = 1; /* when non-zero, the list needs to be updated */
1010
1013{
1024
1025#ifndef NDEBUG
1026 /* Check that the array indices are correct. */
1027 for (int i = 0; i < FMODIFIER_NUM_TYPES; i++) {
1028 if (!fmodifiersTypeInfo[i]) {
1029 continue;
1030 }
1032 "fmodifiersTypeInfo should be indexed by the modifier type number");
1033 }
1034#endif
1035}
1036
1038{
1039 /* initialize the type-info list? */
1040 if (FMI_INIT) {
1042 FMI_INIT = 0;
1043 }
1044
1045 /* only return for valid types */
1046 if ((type >= FMODIFIER_TYPE_NULL) && (type < FMODIFIER_NUM_TYPES)) {
1047 /* there shouldn't be any segfaults here... */
1048 return fmodifiersTypeInfo[type];
1049 }
1050
1051 CLOG_ERROR(&LOG, "No valid F-Curve Modifier type-info data available. Type = %i", type);
1052
1053 return nullptr;
1054}
1055
1057{
1058 /* only return typeinfo for valid modifiers */
1059 if (fcm) {
1060 return get_fmodifier_typeinfo(fcm->type);
1061 }
1062
1063 return nullptr;
1064}
1065
1067
1068/* -------------------------------------------------------------------- */
1071
1072FModifier *add_fmodifier(ListBase *modifiers, int type, FCurve *owner_fcu)
1073{
1074 const FModifierTypeInfo *fmi = get_fmodifier_typeinfo(type);
1075 FModifier *fcm;
1076
1077 /* sanity checks */
1078 if (ELEM(nullptr, modifiers, fmi)) {
1079 return nullptr;
1080 }
1081
1082 /* special checks for whether modifier can be added */
1083 if ((modifiers->first) && (type == FMODIFIER_TYPE_CYCLES)) {
1084 /* cycles modifier must be first in stack, so for now, don't add if it can't be */
1085 /* TODO: perhaps there is some better way, but for now, */
1087 "Cannot add 'Cycles' modifier to F-Curve, as 'Cycles' modifier can only be "
1088 "first in stack.");
1089 return nullptr;
1090 }
1091
1092 /* add modifier itself */
1093 fcm = MEM_callocN<FModifier>("F-Curve Modifier");
1094 fcm->type = type;
1095 fcm->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT; /* Expand the main panel, not the sub-panels. */
1096 fcm->curve = owner_fcu;
1097 fcm->influence = 1.0f;
1098 BLI_addtail(modifiers, fcm);
1099
1100 /* Set modifier name and make sure it is unique. */
1101 BKE_fmodifier_name_set(fcm, "");
1102
1103 /* tag modifier as "active" if no other modifiers exist in the stack yet */
1104 if (BLI_listbase_is_single(modifiers)) {
1106 }
1107
1108 /* add modifier's data */
1109 fcm->data = MEM_callocN(fmi->size, fmi->struct_name);
1110
1111 /* init custom settings if necessary */
1112 if (fmi->new_data) {
1113 fmi->new_data(fcm->data);
1114 }
1115
1116 /* update the fcurve if the Cycles modifier is added */
1117 if ((owner_fcu) && (type == FMODIFIER_TYPE_CYCLES)) {
1118 BKE_fcurve_handles_recalc(owner_fcu);
1119 }
1120
1121 /* return modifier for further editing */
1122 return fcm;
1123}
1124
1126{
1127 const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(src);
1128 FModifier *dst;
1129
1130 /* sanity check */
1131 if (src == nullptr) {
1132 return nullptr;
1133 }
1134
1135 /* copy the base data, clearing the links */
1136 dst = static_cast<FModifier *>(MEM_dupallocN(src));
1137 dst->next = dst->prev = nullptr;
1138 dst->curve = nullptr;
1139
1140 /* make a new copy of the F-Modifier's data */
1141 dst->data = MEM_dupallocN(src->data);
1142
1143 /* only do specific constraints if required */
1144 if (fmi && fmi->copy_data) {
1145 fmi->copy_data(dst, src);
1146 }
1147
1148 /* return the new modifier */
1149 return dst;
1150}
1151
1152void copy_fmodifiers(ListBase *dst, const ListBase *src)
1153{
1154 FModifier *fcm, *srcfcm;
1155
1156 if (ELEM(nullptr, dst, src)) {
1157 return;
1158 }
1159
1160 BLI_listbase_clear(dst);
1161 BLI_duplicatelist(dst, src);
1162
1163 for (fcm = static_cast<FModifier *>(dst->first), srcfcm = static_cast<FModifier *>(src->first);
1164 fcm && srcfcm;
1165 srcfcm = srcfcm->next, fcm = fcm->next)
1166 {
1167 const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm);
1168
1169 /* make a new copy of the F-Modifier's data */
1170 fcm->data = MEM_dupallocN(fcm->data);
1171 fcm->curve = nullptr;
1172
1173 /* only do specific constraints if required */
1174 if (fmi && fmi->copy_data) {
1175 fmi->copy_data(fcm, srcfcm);
1176 }
1177 }
1178}
1179
1181{
1182 const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm);
1183
1184 /* sanity check */
1185 if (fcm == nullptr) {
1186 return false;
1187 }
1188
1189 /* removing the cycles modifier requires a handle update */
1190 FCurve *update_fcu = (fcm->type == FMODIFIER_TYPE_CYCLES) ? fcm->curve : nullptr;
1191
1192 /* free modifier's special data (stored inside fcm->data) */
1193 if (fcm->data) {
1194 if (fmi && fmi->free_data) {
1195 fmi->free_data(fcm);
1196 }
1197
1198 /* free modifier's data (fcm->data) */
1199 MEM_freeN(fcm->data);
1200 }
1201
1202 /* remove modifier from stack */
1203 if (modifiers) {
1204 BLI_freelinkN(modifiers, fcm);
1205
1206 /* update the fcurve if the Cycles modifier is removed */
1207 if (update_fcu) {
1208 BKE_fcurve_handles_recalc(update_fcu);
1209 }
1210
1211 return true;
1212 }
1213
1214 /* XXX this case can probably be removed some day, as it shouldn't happen... */
1215 CLOG_STR_ERROR(&LOG, "no modifier stack given");
1216 MEM_freeN(fcm);
1217 return false;
1218}
1219
1221{
1222 FModifier *fcm, *fmn;
1223
1224 /* sanity check */
1225 if (modifiers == nullptr) {
1226 return;
1227 }
1228
1229 /* free each modifier in order - modifier is unlinked from list and freed */
1230 for (fcm = static_cast<FModifier *>(modifiers->first); fcm; fcm = fmn) {
1231 fmn = fcm->next;
1232 remove_fmodifier(modifiers, fcm);
1233 }
1234}
1235
1237{
1238 /* sanity checks */
1239 if (ELEM(nullptr, modifiers, modifiers->first)) {
1240 return nullptr;
1241 }
1242
1243 /* loop over modifiers until 'active' one is found */
1244 LISTBASE_FOREACH (FModifier *, fcm, modifiers) {
1245 if (fcm->flag & FMODIFIER_FLAG_ACTIVE) {
1246 return fcm;
1247 }
1248 }
1249
1250 /* no modifier is active */
1251 return nullptr;
1252}
1253
1255{
1256 /* sanity checks */
1257 if (ELEM(nullptr, modifiers, modifiers->first)) {
1258 return;
1259 }
1260
1261 /* deactivate all, and set current one active */
1262 LISTBASE_FOREACH (FModifier *, fm, modifiers) {
1264 }
1265
1266 /* make given modifier active */
1267 if (fcm) {
1269 }
1270}
1271
1272bool list_has_suitable_fmodifier(const ListBase *modifiers, int mtype, short acttype)
1273{
1274 /* if there are no specific filtering criteria, just skip */
1275 if ((mtype == 0) && (acttype == 0)) {
1276 return (modifiers && modifiers->first);
1277 }
1278
1279 /* sanity checks */
1280 if (ELEM(nullptr, modifiers, modifiers->first)) {
1281 return false;
1282 }
1283
1284 /* Find the first modifier fitting these criteria. */
1285 LISTBASE_FOREACH (FModifier *, fcm, modifiers) {
1286 const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm);
1287 short mOk = 1, aOk = 1; /* by default 1, so that when only one test, won't fail */
1288
1289 /* check if applicable ones are fulfilled */
1290 if (mtype) {
1291 mOk = (fcm->type == mtype);
1292 }
1293 if (acttype > -1) {
1294 aOk = (fmi->acttype == acttype);
1295 }
1296
1297 /* if both are ok, we've found a hit */
1298 if (mOk && aOk) {
1299 return true;
1300 }
1301 }
1302
1303 /* no matches */
1304 return false;
1305}
1306
1307/* Evaluation API --------------------------- */
1308
1310{
1311 /* Sanity checks. */
1312 if (ELEM(nullptr, modifiers, modifiers->first)) {
1313 return 0;
1314 }
1315
1316 uint max_size = 0;
1317
1318 LISTBASE_FOREACH (FModifier *, fcm, modifiers) {
1319 const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm);
1320
1321 if (fmi == nullptr) {
1322 continue;
1323 }
1324
1325 max_size = std::max(max_size, fmi->storage_size);
1326 }
1327
1328 return max_size;
1329}
1330
1335{
1336 float influence;
1337
1338 /* sanity check */
1339 if (fcm == nullptr) {
1340 return 0.0f;
1341 }
1342
1343 /* should we use influence stored in modifier or not
1344 * NOTE: this is really just a hack so that we don't need to version patch old files ;)
1345 */
1346 if (fcm->flag & FMODIFIER_FLAG_USEINFLUENCE) {
1347 influence = fcm->influence;
1348 }
1349 else {
1350 influence = 1.0f;
1351 }
1352
1353 /* restricted range or full range? */
1355 if ((evaltime < fcm->sfra) || (evaltime > fcm->efra)) {
1356 /* out of range */
1357 return 0.0f;
1358 }
1359 if ((fcm->blendin != 0.0f) && (evaltime >= fcm->sfra) &&
1360 (evaltime <= fcm->sfra + fcm->blendin))
1361 {
1362 /* blend in range */
1363 float a = fcm->sfra;
1364 float b = fcm->sfra + fcm->blendin;
1365 return influence * (evaltime - a) / (b - a);
1366 }
1367 if ((fcm->blendout != 0.0f) && (evaltime <= fcm->efra) &&
1368 (evaltime >= fcm->efra - fcm->blendout))
1369 {
1370 /* blend out range */
1371 float a = fcm->efra;
1372 float b = fcm->efra - fcm->blendout;
1373 return influence * (evaltime - a) / (b - a);
1374 }
1375 }
1376
1377 /* just return the influence of the modifier */
1378 return influence;
1379}
1380
1382 const ListBase *modifiers,
1383 const FCurve *fcu,
1384 float cvalue,
1385 float evaltime)
1386{
1387 /* sanity checks */
1388 if (ELEM(nullptr, modifiers, modifiers->last)) {
1389 return evaltime;
1390 }
1391
1392 if (fcu && fcu->flag & FCURVE_MOD_OFF) {
1393 return evaltime;
1394 }
1395
1396 /* Starting from the end of the stack, calculate the time effects of various stacked modifiers
1397 * on the time the F-Curve should be evaluated at.
1398 *
1399 * This is done in reverse order to standard evaluation, as when this is done in standard
1400 * order, each modifier would cause jumps to other points in the curve, forcing all
1401 * previous ones to be evaluated again for them to be correct. However, if we did in the
1402 * reverse order as we have here, we can consider them a macro to micro type of waterfall
1403 * effect, which should get us the desired effects when using layered time manipulations
1404 * (such as multiple 'stepped' modifiers in sequence, causing different stepping rates)
1405 */
1406 uint fcm_index = storage->modifier_count - 1;
1407 for (FModifier *fcm = static_cast<FModifier *>(modifiers->last); fcm;
1408 fcm = fcm->prev, fcm_index--)
1409 {
1410 const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm);
1411
1412 if (fmi == nullptr) {
1413 continue;
1414 }
1415
1416 /* If modifier cannot be applied on this frame
1417 * (whatever scale it is on, it won't affect the results)
1418 * hence we shouldn't bother seeing what it would do given the chance. */
1419 if ((fcm->flag & FMODIFIER_FLAG_RANGERESTRICT) == 0 ||
1420 ((fcm->sfra <= evaltime) && (fcm->efra >= evaltime)))
1421 {
1422 /* only evaluate if there's a callback for this */
1423 if (fmi->evaluate_modifier_time) {
1424 if ((fcm->flag & (FMODIFIER_FLAG_DISABLED | FMODIFIER_FLAG_MUTED)) == 0) {
1425 void *storage_ptr = POINTER_OFFSET(storage->buffer,
1426 fcm_index * storage->size_per_modifier);
1427
1428 float nval = fmi->evaluate_modifier_time(fcu, fcm, cvalue, evaltime, storage_ptr);
1429
1430 float influence = eval_fmodifier_influence(fcm, evaltime);
1431 evaltime = interpf(nval, evaltime, influence);
1432 }
1433 }
1434 }
1435 }
1436
1437 /* return the modified evaltime */
1438 return evaltime;
1439}
1440
1442 const ListBase *modifiers,
1443 const FCurve *fcu,
1444 float *cvalue,
1445 float evaltime)
1446{
1447 FModifier *fcm;
1448
1449 /* sanity checks */
1450 if (ELEM(nullptr, modifiers, modifiers->first)) {
1451 return;
1452 }
1453
1454 if (fcu->flag & FCURVE_MOD_OFF) {
1455 return;
1456 }
1457
1458 /* evaluate modifiers */
1459 uint fcm_index = 0;
1460 for (fcm = static_cast<FModifier *>(modifiers->first); fcm; fcm = fcm->next, fcm_index++) {
1461 const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm);
1462
1463 if (fmi == nullptr) {
1464 continue;
1465 }
1466
1467 /* Only evaluate if there's a callback for this,
1468 * and if F-Modifier can be evaluated on this frame. */
1469 if ((fcm->flag & FMODIFIER_FLAG_RANGERESTRICT) == 0 ||
1470 ((fcm->sfra <= evaltime) && (fcm->efra >= evaltime)))
1471 {
1472 if (fmi->evaluate_modifier) {
1473 if ((fcm->flag & (FMODIFIER_FLAG_DISABLED | FMODIFIER_FLAG_MUTED)) == 0) {
1474 void *storage_ptr = POINTER_OFFSET(storage->buffer,
1475 fcm_index * storage->size_per_modifier);
1476
1477 float nval = *cvalue;
1478 fmi->evaluate_modifier(fcu, fcm, &nval, evaltime, storage_ptr);
1479
1480 float influence = eval_fmodifier_influence(fcm, evaltime);
1481 *cvalue = interpf(nval, *cvalue, influence);
1482 }
1483 }
1484 }
1485 }
1486}
1487
1488/* ---------- */
1489
1490void fcurve_bake_modifiers(FCurve *fcu, int start, int end)
1491{
1492 ChannelDriver *driver;
1493
1494 /* sanity checks */
1495 /* TODO: make these tests report errors using reports not CLOG's */
1496 if (ELEM(nullptr, fcu, fcu->modifiers.first)) {
1497 CLOG_ERROR(&LOG, "No F-Curve with F-Curve Modifiers to Bake");
1498 return;
1499 }
1500
1501 /* temporarily, disable driver while we sample, so that they don't influence the outcome */
1502 driver = fcu->driver;
1503 fcu->driver = nullptr;
1504
1505 /* bake the modifiers, by sampling the curve at each frame */
1506 fcurve_store_samples(fcu, nullptr, start, end, fcurve_samplingcb_evalcurve);
1507
1508 /* free the modifiers now */
1510
1511 /* restore driver */
1512 fcu->driver = driver;
1513}
1514
@ FMI_TYPE_EXTRAPOLATION
Definition BKE_fcurve.hh:90
@ FMI_TYPE_REPLACE_VALUES
Definition BKE_fcurve.hh:94
@ FMI_TYPE_GENERATE_CURVE
Definition BKE_fcurve.hh:96
void BKE_fmodifier_name_set(FModifier *fcm, const char *name)
void BKE_fcurve_handles_recalc(FCurve *fcu)
@ FMI_REQUIRES_NOTHING
@ FMI_REQUIRES_RUNTIME_CHECK
@ FMI_REQUIRES_ORIGINAL_DATA
void fcurve_store_samples(FCurve *fcu, void *data, int start, int end, FcuSampleFunc sample_cb)
float fcurve_samplingcb_evalcurve(FCurve *fcu, void *data, float evaltime)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(ListBase *lb)
void BLI_freelinkN(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:270
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void void void void void void BLI_duplicatelist(ListBase *dst, const ListBase *src) ATTR_NONNULL(1
void void BLI_INLINE bool BLI_listbase_is_single(const ListBase *lb)
MINLINE float interpf(float target, float origin, float t)
#define M_PI_2
#define M_PI
float BLI_noise_turbulence(float noisesize, float x, float y, float z, int nr)
Definition noise_c.cc:437
unsigned int uint
#define IS_EQ(a, b)
#define ELEM(...)
#define POINTER_OFFSET(v, ofs)
#define IS_EQT(a, b, c)
#define BLT_I18NCONTEXT_ID_ACTION
#define CTX_N_(context, msgid)
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:188
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:189
#define CLOG_STR_ERROR(clg_ref, str)
Definition CLG_log.h:196
@ FCM_STEPPED_NO_AFTER
@ FCM_STEPPED_NO_BEFORE
@ FCM_EXTRAPOLATE_MIRROR
@ FCM_EXTRAPOLATE_CYCLIC
@ FCM_EXTRAPOLATE_CYCLIC_OFFSET
@ FCM_GENERATOR_ADDITIVE
@ FCM_LIMIT_XMIN
@ FCM_LIMIT_YMAX
@ FCM_LIMIT_XMAX
@ FCM_LIMIT_YMIN
@ FCM_NOISE_MODIF_REPLACE
@ FCM_NOISE_MODIF_SUBTRACT
@ FCM_NOISE_MODIF_ADD
@ FCM_NOISE_MODIF_MULTIPLY
@ FMODIFIER_TYPE_CYCLES
@ FMODIFIER_TYPE_FILTER
@ FMODIFIER_TYPE_STEPPED
@ FMODIFIER_TYPE_FN_GENERATOR
@ FMODIFIER_TYPE_NOISE
@ FMODIFIER_TYPE_NULL
@ FMODIFIER_TYPE_GENERATOR
@ FMODIFIER_NUM_TYPES
@ FMODIFIER_TYPE_ENVELOPE
@ FMODIFIER_TYPE_PYTHON
@ FMODIFIER_TYPE_LIMITS
@ FCM_GENERATOR_POLYNOMIAL_FACTORISED
@ FCM_GENERATOR_POLYNOMIAL
@ FMODIFIER_FLAG_MUTED
@ FMODIFIER_FLAG_USEINFLUENCE
@ FMODIFIER_FLAG_ACTIVE
@ FMODIFIER_FLAG_DISABLED
@ FMODIFIER_FLAG_RANGERESTRICT
@ FCURVE_MOD_OFF
@ FCM_GENERATOR_FN_LN
@ FCM_GENERATOR_FN_SIN
@ FCM_GENERATOR_FN_SQRT
@ FCM_GENERATOR_FN_SINC
@ FCM_GENERATOR_FN_COS
@ FCM_GENERATOR_FN_TAN
@ UI_PANEL_DATA_EXPAND_ROOT
Read Guarded memory(de)allocation.
#define MEM_recallocN(vmemh, len)
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v
float evaltime
nullptr float
void fcurve_bake_modifiers(FCurve *fcu, int start, int end)
static float eval_fmodifier_influence(FModifier *fcm, float evaltime)
static FModifierTypeInfo FMI_ENVELOPE
Definition fmodifier.cc:481
static void fcm_envelope_new_data(void *mdata)
Definition fmodifier.cc:404
FModifier * copy_fmodifier(const FModifier *src)
static void fcm_cycles_new_data(void *mdata)
Definition fmodifier.cc:610
float evaluate_time_fmodifiers(FModifiersStackStorage *storage, const ListBase *modifiers, const FCurve *fcu, float cvalue, float evaltime)
void copy_fmodifiers(ListBase *dst, const ListBase *src)
static float fcm_cycles_time(const FCurve *fcu, const FModifier *fcm, float, float evaltime, void *storage_)
Definition fmodifier.cc:618
static void fcm_fn_generator_new_data(void *mdata)
Definition fmodifier.cc:260
static void fcm_envelope_verify(FModifier *fcm)
Definition fmodifier.cc:413
uint evaluate_fmodifiers_storage_size_per_modifier(const ListBase *modifiers)
void evaluate_value_fmodifiers(FModifiersStackStorage *storage, const ListBase *modifiers, const FCurve *fcu, float *cvalue, float evaltime)
static void fcm_limits_evaluate(const FCurve *, const FModifier *fcm, float *cvalue, float, void *)
Definition fmodifier.cc:905
static FModifierTypeInfo FMI_STEPPED
Definition fmodifier.cc:982
static FModifierTypeInfo FMI_GENERATOR
Definition fmodifier.cc:232
static float fcm_limits_time(const FCurve *, const FModifier *fcm, float, float evaltime, void *)
Definition fmodifier.cc:885
FModifier * add_fmodifier(ListBase *modifiers, int type, FCurve *owner_fcu)
bool remove_fmodifier(ListBase *modifiers, FModifier *fcm)
static double sinc(double x)
Definition fmodifier.cc:273
bool list_has_suitable_fmodifier(const ListBase *modifiers, int mtype, short acttype)
static float fcm_stepped_time(const FCurve *, const FModifier *fcm, float, float evaltime, void *)
Definition fmodifier.cc:949
static void fcm_fn_generator_evaluate(const FCurve *, const FModifier *fcm, float *cvalue, float evaltime, void *)
Definition fmodifier.cc:282
void set_active_fmodifier(ListBase *modifiers, FModifier *fcm)
static void fcm_noise_new_data(void *mdata)
Definition fmodifier.cc:796
static FModifierTypeInfo FMI_CYCLES
Definition fmodifier.cc:778
static void fcm_stepped_new_data(void *mdata)
Definition fmodifier.cc:940
const FModifierTypeInfo * get_fmodifier_typeinfo(const int type)
static void fcm_cycles_evaluate(const FCurve *, const FModifier *, float *cvalue, float, void *storage_)
Definition fmodifier.cc:768
static FModifierTypeInfo * fmodifiersTypeInfo[FMODIFIER_NUM_TYPES]
static short FMI_INIT
static void fcm_generator_new_data(void *mdata)
Definition fmodifier.cc:111
static void fcm_generator_free(FModifier *fcm)
Definition fmodifier.cc:90
int BKE_fcm_envelope_find_index(FCM_EnvelopeData array[], float frame, int arraylen, bool *r_exists)
Definition fmodifier.cc:504
FModifier * find_active_fmodifier(ListBase *modifiers)
static FModifierTypeInfo FMI_LIMITS
Definition fmodifier.cc:922
static void fmods_init_typeinfo()
static void fcm_generator_evaluate(const FCurve *, const FModifier *fcm, float *cvalue, float evaltime, void *)
Definition fmodifier.cc:155
static void fcm_generator_verify(FModifier *fcm)
Definition fmodifier.cc:124
void free_fmodifiers(ListBase *modifiers)
static void fcm_generator_copy(FModifier *fcm, const FModifier *src)
Definition fmodifier.cc:100
static void fcm_envelope_free(FModifier *fcm)
Definition fmodifier.cc:383
static void fcm_envelope_copy(FModifier *fcm, const FModifier *src)
Definition fmodifier.cc:393
static void fcm_envelope_evaluate(const FCurve *, const FModifier *fcm, float *cvalue, float evaltime, void *)
Definition fmodifier.cc:423
const FModifierTypeInfo * fmodifier_get_typeinfo(const FModifier *fcm)
static FModifierTypeInfo FMI_NOISE
Definition fmodifier.cc:867
static FModifierTypeInfo FMI_FN_GENERATOR
Definition fmodifier.cc:365
#define BINARYSEARCH_FRAMEEQ_THRESH
Definition fmodifier.cc:502
static void fcm_noise_evaluate(const FCurve *, const FModifier *fcm, float *cvalue, float evaltime, void *)
Definition fmodifier.cc:812
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
#define tan
#define log
#define sin
#define cos
#define floor
#define ceil
#define sqrt
#define LOG(level)
Definition log.h:97
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float2 fmod(const float2 a, const float b)
ccl_device_inline float2 fabs(const float2 a)
float perlin_fbm(T p, float detail, float roughness, float lacunarity, bool normalize)
Definition noise.cc:696
VecBase< float, 2 > float2
static float noise(int n)
#define min(a, b)
Definition sort.cc:36
float vec[3][3]
FPoint * fpt
ChannelDriver * driver
BezTriple * bezt
unsigned int totvert
ListBase modifiers
FCM_EnvelopeData * data
void(* copy_data)(FModifier *fcm, const FModifier *src)
Definition BKE_fcurve.hh:70
char struct_name[64]
Definition BKE_fcurve.hh:62
void(* new_data)(void *mdata)
Definition BKE_fcurve.hh:74
void(* free_data)(FModifier *fcm)
Definition BKE_fcurve.hh:68
void(* evaluate_modifier)(const FCurve *fcu, const FModifier *fcm, float *cvalue, float evaltime, void *storage)
Definition BKE_fcurve.hh:83
float(* evaluate_modifier_time)(const FCurve *fcu, const FModifier *fcm, float cvalue, float evaltime, void *storage)
Definition BKE_fcurve.hh:80
struct FCurve * curve
float influence
struct FModifier * next
short ui_expand_flag
struct FModifier * prev
float vec[2]
void * last
void * first
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251