F-Curve Modifiers: Time-Modifying F-Modifiers Cleanup
authorJoshua Leung <aligorith@gmail.com>
Sun, 3 May 2009 13:00:59 +0000 (13:00 +0000)
committerJoshua Leung <aligorith@gmail.com>
Sun, 3 May 2009 13:00:59 +0000 (13:00 +0000)
Time-Modifying F-Curve Modifiers now get special callbacks to allow them to specify what frame they need to be evaluated on, instead of forcing a re-evaluation of the preceeding curve + modifier-stack. This should be more robust than the old way in general.

It still remains to be seen if some tweaks to this are still needed, as the full consequences of the propogation of modified time-spaces have yet to be fully explored.

For now though, evaluation works by finding the last modifier on the stack which modifies time, and asks it what time it modifies the given time to. This modified time is used to evaluate the F-Curve data only. The modifier stack gets evaluated using the original time instead.

source/blender/blenkernel/BKE_fcurve.h
source/blender/blenkernel/intern/fcurve.c
source/blender/blenloader/intern/readfile.c
source/blender/makesdna/DNA_anim_types.h

index 3dd083476cee48b88dc45662598f91df279d1cfe..3d33b6158094727412437188341afc6d3fd10b91 100644 (file)
@@ -68,6 +68,8 @@ typedef struct FModifierTypeInfo {
        void (*verify_data)(struct FModifier *fcm);
        
        /* evaluation */
+               /* evaluate time that the modifier requires the F-Curve to be evaluated at */
+       float (*evaluate_modifier_time)(struct FCurve *fcu, struct FModifier *fcm, float cvalue, float evaltime);
                /* evaluate the modifier for the given time and 'accumulated' value */
        void (*evaluate_modifier)(struct FCurve *fcu, struct FModifier *fcm, float *cvalue, float evaltime);
 } FModifierTypeInfo;
index a0e631cfe8f5506752b240d8617ae56c2d699799..3bc80d6432955e82e5e49b3a887bfbdcf2deffa3 100644 (file)
@@ -1253,50 +1253,11 @@ static FModifierTypeInfo FMI_MODNAME = {
        fcm_modname_copy, /* copy data */
        fcm_modname_new_data, /* new data */
        fcm_modname_verify, /* verify */
+       fcm_modname_time, /* evaluate time */
        fcm_modname_evaluate /* evaluate */
 };
 #endif
 
-/* Utilities For F-Curve Modifiers ---------------------- */
-
-/* Recalculate the F-Curve at evaltime, as modified by the given F-Curve 
- * 
- * While this may sound wrong (and be potentially very slow), it is invalid for F-Curve modifiers to actually
- * modify the evaltime in such a way that those after it will end up evaluating in the wrong time space.
- */
-static float fcm_reevaluate_fcurve (FCurve *fcu, FModifier *fcm, float cvalue, float evaltime)
-{ 
-       ListBase modifiers = {NULL, NULL};
-       float new_value = 0.0f;
-       
-       /* sanity checking */
-       if ELEM(NULL, fcu, fcm)
-               return cvalue;
-       
-       /* unlink given modifier from previous modifiers, keeping the previous ones on the F-Curve,
-        * but ones after off the F-Curve (so that we avoid the infinitely re-entrant situation).
-        */
-       modifiers.first= fcm;
-       modifiers.last= fcu->modifiers.last;
-       
-       if (fcm->prev) {
-               fcm->prev->next= NULL;
-               fcu->modifiers.last= fcm->prev;
-       }
-       else
-               fcu->modifiers.first= fcu->modifiers.last= NULL;
-       fcm->prev= NULL;
-       
-       /* re-enter the evaluation loop (but without the burden of evaluating any modifiers, so 'should' be relatively quick) */
-       new_value= evaluate_fcurve(fcu, evaltime);
-       
-       /* restore modifiers (don't assume everything is still ok after being re-entrant) */
-       addlisttolist(&fcu->modifiers, &modifiers);
-       
-       /* return the new value */
-       return new_value;
-}
-
 /* Generator F-Curve Modifier --------------------------- */
 
 /* Generators available:
@@ -1548,7 +1509,7 @@ static void fcm_generator_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue,
                        
                        /* execute function callback to set value if appropriate */
                        if (fn) {
-                               float value= data->coefficients[0]*fn(arg) + data->coefficients[3];
+                               float value= (float)(data->coefficients[0]*fn(arg) + data->coefficients[3]);
                                
                                if (data->flag & FCM_GENERATOR_ADDITIVE)
                                        *cvalue += value;
@@ -1577,6 +1538,7 @@ static FModifierTypeInfo FMI_GENERATOR = {
        fcm_generator_copy, /* copy data */
        fcm_generator_new_data, /* new data */
        fcm_generator_verify, /* verify */
+       NULL, /* evaluate time */
        fcm_generator_evaluate /* evaluate */
 };
 
@@ -1683,6 +1645,7 @@ static FModifierTypeInfo FMI_ENVELOPE = {
        fcm_envelope_copy, /* copy data */
        fcm_envelope_new_data, /* new data */
        fcm_envelope_verify, /* verify */
+       NULL, /* evaluate time */
        fcm_envelope_evaluate /* evaluate */
 };
 
@@ -1698,6 +1661,11 @@ static FModifierTypeInfo FMI_ENVELOPE = {
  *                             as appropriate
  */
 
+/* temp data used during evaluation */
+typedef struct tFCMED_Cycles {
+       float cycyofs;          /* y-offset to apply */
+} tFCMED_Cycles;
 static void fcm_cycles_new_data (void *mdata)
 {
        FMod_Cycles *data= (FMod_Cycles *)mdata;
@@ -1705,8 +1673,8 @@ static void fcm_cycles_new_data (void *mdata)
        /* turn on cycles by default */
        data->before_mode= data->after_mode= FCM_EXTRAPOLATE_CYCLIC;
 }
-static void fcm_cycles_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
+
+static float fcm_cycles_time (FCurve *fcu, FModifier *fcm, float cvalue, float evaltime)
 {
        FMod_Cycles *data= (FMod_Cycles *)fcm->data;
        float prevkey[2], lastkey[2], cycyofs=0.0f;
@@ -1717,7 +1685,7 @@ static void fcm_cycles_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, flo
        // FIXME...
        if (fcm->prev) {
                fcm->flag |= FMODIFIER_FLAG_DISABLED;
-               return;
+               return evaltime;
        }
        
        /* calculate new evaltime due to cyclic interpolation */
@@ -1742,7 +1710,7 @@ static void fcm_cycles_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, flo
                lastkey[1]= lastfpt->vec[1];
        }
        else
-               return;
+               return evaltime;
                
        /* check if modifier will do anything
         *      1) if in data range, definitely don't do anything
@@ -1763,7 +1731,7 @@ static void fcm_cycles_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, flo
                }
        }
        if ELEM(0, side, mode)
-               return;
+               return evaltime;
                
        /* find relative place within a cycle */
        {
@@ -1778,7 +1746,7 @@ static void fcm_cycles_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, flo
                
                /* check if cycle is infinitely small, to be point of being impossible to use */
                if (cycdx == 0)
-                       return;
+                       return evaltime;
                        
                /* check that cyclic is still enabled for the specified time */
                if (cycles == 0) {
@@ -1790,7 +1758,7 @@ static void fcm_cycles_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, flo
                        /* we are too far away from range to evaluate
                         * TODO: but we should still hold last value... 
                         */
-                       return;
+                       return evaltime;
                }
                
                /* check if 'cyclic extrapolation', and thus calculate y-offset for this cycle */
@@ -1804,8 +1772,32 @@ static void fcm_cycles_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, flo
                if (evaltime < ofs) evaltime += cycdx;
        }
        
-       /* reevaluate F-Curve at the new time that we've decided on */
-       *cvalue= fcm_reevaluate_fcurve(fcu, fcm, *cvalue, evaltime) + cycyofs;
+       /* store temp data if needed */
+       if (mode == FCM_EXTRAPOLATE_CYCLIC_OFFSET) {
+               tFCMED_Cycles *edata;
+               
+               /* for now, this is just a float, but we could get more stuff... */
+               fcm->edata= edata= MEM_callocN(sizeof(tFCMED_Cycles), "tFCMED_Cycles");
+               edata->cycyofs= cycyofs;
+       }
+       
+       /* return the new frame to evaluate */
+       return evaltime;
+}
+static void fcm_cycles_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
+{
+       tFCMED_Cycles *edata= (tFCMED_Cycles *)fcm->edata;
+       
+       /* use temp data */
+       if (edata) {
+               /* add cyclic offset - no need to check for now, otherwise the data wouldn't exist! */
+               *cvalue += edata->cycyofs;
+               
+               /* free temp data */
+               MEM_freeN(edata);
+               fcm->edata= NULL;
+       }
 }
 
 static FModifierTypeInfo FMI_CYCLES = {
@@ -1819,6 +1811,7 @@ static FModifierTypeInfo FMI_CYCLES = {
        NULL, /* copy data */
        fcm_cycles_new_data, /* new data */
        NULL /*fcm_cycles_verify*/, /* verify */
+       fcm_cycles_time, /* evaluate time */
        fcm_cycles_evaluate /* evaluate */
 };
 
@@ -1871,6 +1864,7 @@ static FModifierTypeInfo FMI_NOISE = {
        NULL, /* copy data */
        fcm_noise_new_data, /* new data */
        NULL /*fcm_noise_verify*/, /* verify */
+       NULL, /* evaluate time */
        fcm_noise_evaluate /* evaluate */
 };
 
@@ -1888,6 +1882,7 @@ static FModifierTypeInfo FMI_FILTER = {
        NULL, /* copy data */
        NULL, /* new data */
        NULL /*fcm_filter_verify*/, /* verify */
+       NULL, /* evlauate time */
        fcm_filter_evaluate /* evaluate */
 };
 #endif // XXX not yet implemented
@@ -1943,21 +1938,30 @@ static FModifierTypeInfo FMI_PYTHON = {
        fcm_python_copy, /* copy data */
        fcm_python_new_data, /* new data */
        NULL /*fcm_python_verify*/, /* verify */
+       NULL /*fcm_python_time*/, /* evaluate time */
        fcm_python_evaluate /* evaluate */
 };
 
 
 /* Limits F-Curve Modifier --------------------------- */
 
-static void fcm_limits_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
+static float fcm_limits_time (FCurve *fcu, FModifier *fcm, float cvalue, float evaltime)
 {
        FMod_Limits *data= (FMod_Limits *)fcm->data;
        
-       /* time limits first */
+       /* check for the time limits */
        if ((data->flag & FCM_LIMIT_XMIN) && (evaltime < data->rect.xmin))
-               *cvalue= fcm_reevaluate_fcurve(fcu, fcm, *cvalue, data->rect.xmin);
+               return data->rect.xmin;
        if ((data->flag & FCM_LIMIT_XMAX) && (evaltime > data->rect.xmax))
-               *cvalue= fcm_reevaluate_fcurve(fcu, fcm, *cvalue, data->rect.xmax);
+               return data->rect.xmax;
+               
+       /* modifier doesn't change time */
+       return evaltime;
+}
+
+static void fcm_limits_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
+{
+       FMod_Limits *data= (FMod_Limits *)fcm->data;
        
        /* value limits now */
        if ((data->flag & FCM_LIMIT_YMIN) && (*cvalue < data->rect.ymin))
@@ -1976,7 +1980,8 @@ static FModifierTypeInfo FMI_LIMITS = {
        NULL, /* free data */
        NULL, /* copy data */
        NULL, /* new data */
-       NULL /*fcm_python_verify*/, /* verify */
+       NULL, /* verify */
+       fcm_limits_time, /* evaluate time */
        fcm_limits_evaluate /* evaluate */
 };
 
@@ -2216,9 +2221,11 @@ void fcurve_set_active_modifier (FCurve *fcu, FModifier *fcm)
 float evaluate_fcurve (FCurve *fcu, float evaltime) 
 {
        FModifier *fcm;
-       float cvalue = 0.0f;
+       float cvalue= 0.0f;
+       float devaltime;
        
        /* if there is a driver (only if this F-Curve is acting as 'driver'), evaluate it to find value to use as "evaltime" 
+        * since drivers essentially act as alternative input (i.e. in place of 'time') for F-Curves
         *      - this value will also be returned as the value of the 'curve', if there are no keyframes
         */
        if (fcu->driver) {
@@ -2226,11 +2233,37 @@ float evaluate_fcurve (FCurve *fcu, float evaltime)
                evaltime= cvalue= evaluate_driver(fcu->driver, evaltime);
        }
        
-       /* evaluate curve-data */
+       /* evaluate time modifications imposed by some F-Curve Modifiers
+        *      - this step acts as an optimisation to prevent the F-Curve stack being evaluated 
+        *        several times by modifiers requesting the time be modified, as the final result
+        *        would have required using the modified time
+        *      - modifiers only ever recieve the unmodified time, as subsequent modifiers should be
+        *        working on the 'global' result of the modified curve, not some localised segment,
+        *        so nevaltime gets set to whatever the last time-modifying modifier likes...
+        *      - we start from the end of the stack, as only the last one matters for now
+        */
+       devaltime= evaltime;
+       
+       for (fcm= fcu->modifiers.last; fcm; fcm= fcm->prev) {
+               FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
+               
+               /* only evaluate if there's a callback for this */
+               // TODO: implement the 'influence' control feature...
+               if (fmi && fmi->evaluate_modifier_time) {
+                       if ((fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) == 0)
+                               devaltime= fmi->evaluate_modifier_time(fcu, fcm, cvalue, evaltime);
+                       break;
+               }
+       }
+       
+       /* evaluate curve-data 
+        *      - 'devaltime' instead of 'evaltime', as this is the time that the last time-modifying 
+        *        F-Curve modifier on the stack requested the curve to be evaluated at
+        */
        if (fcu->bezt)
-               cvalue= fcurve_eval_keyframes(fcu, fcu->bezt, evaltime);
+               cvalue= fcurve_eval_keyframes(fcu, fcu->bezt, devaltime);
        else if (fcu->fpt)
-               cvalue= fcurve_eval_samples(fcu, fcu->fpt, evaltime);
+               cvalue= fcurve_eval_samples(fcu, fcu->fpt, devaltime);
        
        /* evaluate modifiers */
        for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) {
index 40a03bfdbbebf75f89e6b6faa49f262113a05a34..450161854ec07a005ee5eaf998d7150f2654c101 100644 (file)
@@ -1723,6 +1723,7 @@ static void direct_link_fcurves(FileData *fd, ListBase *list)
                for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) {
                        /* relink general data */
                        fcm->data = newdataadr(fd, fcm->data);
+                       fcm->edata= NULL;
                        
                        /* do relinking of data for specific types */
                        switch (fcm->type) {
index eefcf9f9ce54e8a35d3496f2cde73f4a8bf26ce7..755569fe0cb1e35fe7729f57be4aedf5fb7bad34 100644 (file)
@@ -28,6 +28,7 @@ typedef struct FModifier {
        struct FModifier *next, *prev;
        
        void *data;                     /* pointer to modifier data */
+       void *edata;            /* pointer to temporary data used during evaluation */
        
        char name[64];          /* user-defined description for the modifier */
        short type;                     /* type of f-curve modifier */