NLA Branch: Setting up evaluation system for action strips

* Refactored F-Curve Modifier stack evaluation into functions which can be reused for F-Curve Modifiers on NLA-Strips 
* Started setting up temporary accumulation buffer system for use when evaluating strips
This commit is contained in:
Joshua Leung
2009-05-15 13:23:03 +00:00
parent 1304d230a3
commit e295cfd4d5
3 changed files with 203 additions and 73 deletions

View File

@@ -108,11 +108,15 @@ struct FModifier *fcurve_add_modifier(struct FCurve *fcu, int type);
void fcurve_copy_modifiers(ListBase *dst, ListBase *src);
void fcurve_remove_modifier(struct FCurve *fcu, struct FModifier *fcm);
void fcurve_free_modifiers(struct FCurve *fcu);
void fcurve_bake_modifiers(struct FCurve *fcu, int start, int end);
struct FModifier *fcurve_find_active_modifier(struct FCurve *fcu);
void fcurve_set_active_modifier(struct FCurve *fcu, struct FModifier *fcm);
float evaluate_time_fmodifiers(ListBase *modifiers, struct FCurve *fcu, float cvalue, float evaltime);
void evaluate_value_fmodifiers(ListBase *modifiers, struct FCurve *fcu, float *cvalue, float evaltime);
void fcurve_bake_modifiers(struct FCurve *fcu, int start, int end);
/* ************** F-Curves API ******************** */
/* -------- Data Managemnt -------- */

View File

@@ -6,6 +6,7 @@
#include <string.h>
#include <stddef.h>
#include <float.h>
#include <math.h>
#include "MEM_guardedalloc.h"
@@ -564,7 +565,7 @@ enum {
typedef struct NlaEvalChannel {
struct NlaEvalChannel *next, *prev;
PointerRNA *ptr; /* pointer to struct containing property to use */
PointerRNA ptr; /* pointer to struct containing property to use */
PropertyRNA *prop; /* RNA-property type to use (should be in the struct given) */
int index; /* array index (where applicable) */
@@ -638,13 +639,13 @@ static float nlastrip_get_influence (NlaStrip *strip, float cframe)
}
/* evaluate the evaluation time and influence for the strip, storing the results in the strip */
void nlastrip_evaluate_controls (NlaStrip *strip, float cframe)
void nlastrip_evaluate_controls (NlaStrip *strip, float ctime)
{
/* firstly, analytically generate values for influence and time (if applicable) */
if ((strip->flag & NLASTRIP_FLAG_USR_TIME) == 0)
strip->strip_time= nlastrip_get_frame(strip, cframe, 1); /* last arg '1' means current time to 'strip'/action time */
strip->strip_time= nlastrip_get_frame(strip, ctime, 1); /* last arg '1' means current time to 'strip'/action time */
if ((strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) == 0)
strip->influence= nlastrip_get_influence(strip, cframe);
strip->influence= nlastrip_get_influence(strip, ctime);
/* now strip's evaluate F-Curves for these settings (if applicable) */
if (strip->fcurves.first) {
@@ -766,13 +767,104 @@ static void nlatrack_ctime_get_strip (ListBase *list, NlaTrack *nlt, short index
/* ---------------------- */
/* evaluates the given evaluation strip */
// FIXME: will we need the evaluation cache table set up to blend stuff in?
// TODO: only evaluate here, but flush in one go using the accumulated channels at end...
static void nlastrip_ctime_evaluate (ListBase *channels, NlaEvalStrip *nes, float ctime)
/* verify that an appropriate NlaEvalChannel for this F-Curve */
static NlaEvalChannel *nlaevalchan_verify (PointerRNA *ptr, ListBase *channels, NlaEvalStrip *nes, FCurve *fcu, short *newChan)
{
// 1. (in old code) was to extract 'IPO-channels' from actions
// 2. blend between the 'accumulated' data, and the new data
NlaEvalChannel *nec= NULL;
NlaStrip *strip= nes->strip;
PropertyRNA *prop;
PointerRNA new_ptr;
char *path = NULL;
short free_path=0;
/* sanity checks */
if (channels == NULL)
return NULL;
/* get RNA pointer+property info from F-Curve for more convenient handling */
/* get path, remapped as appropriate to work in its new environment */
free_path= animsys_remap_path(strip->remap, fcu->rna_path, &path);
/* get property to write to */
if (RNA_path_resolve(ptr, path, &new_ptr, &prop) == 0)
return NULL;
/* only ok if animateable */
else if (RNA_property_animateable(&new_ptr, prop) == 0)
return NULL;
/* loop through existing channels, checking for a channel which affects the same property */
for (nec= channels->first; nec; nec= nec->next) {
if ((nec->ptr.data == new_ptr.data) && (nec->prop == prop))
return nec;
}
/* allocate a new struct for this */
nec= MEM_callocN(sizeof(NlaEvalChannel), "NlaEvalChannel");
*newChan= 1;
BLI_addtail(channels, nec);
nec->ptr= new_ptr;
nec->prop= prop;
nec->index= fcu->array_index;
return nec;
}
/* ---------------------- */
/* evaluate action-clip strip */
static void nlastrip_evaluate_actionclip (PointerRNA *ptr, ListBase *channels, NlaEvalStrip *nes)
{
NlaStrip *strip= nes->strip;
FCurve *fcu;
float evaltime;
/* evaluate strip's modifiers which modify time to evaluate the base curves at */
evaltime= evaluate_time_fmodifiers(&strip->modifiers, NULL, 0.0f, strip->strip_time);
/* evaluate all the F-Curves in the action, saving the relevant pointers to data that will need to be used */
for (fcu= strip->act->curves.first; fcu; fcu= fcu->next) {
NlaEvalChannel *nec;
float value = 0.0f;
short newChan = -1;
/* check if this curve should be skipped */
if (fcu->flag & (FCURVE_MUTED|FCURVE_DISABLED))
continue;
/* evaluate the F-Curve's value for the time given in the strip
* NOTE: we use the modified time here, since strip's F-Curve Modifiers are applied on top of this
*/
value= evaluate_fcurve(fcu, evaltime);
/* apply strip's F-Curve Modifiers on this value
* NOTE: we apply the strip's original evaluation time not the modified one (as per standard F-Curve eval)
*/
evaluate_value_fmodifiers(&strip->modifiers, fcu, &value, strip->strip_time);
/* get an NLA evaluation channel to work with, and accumulate the evaluated value with the value(s)
* stored in this channel if it has been used already
*/
nec= nlaevalchan_verify(ptr, channels, nes, fcu, &newChan);
//if (nec)
// nlaevalchan_accumulate(ptr, nec, nes, newChan, value);
}
}
/* evaluates the given evaluation strip */
// TODO: only evaluate here, but flush in one go using the accumulated channels at end...
static void nlastrip_evaluate (PointerRNA *ptr, ListBase *channels, NlaEvalStrip *nes)
{
/* actions to take depend on the type of strip */
switch (nes->strip->type) {
case NLASTRIP_TYPE_CLIP: /* action-clip */
nlastrip_evaluate_actionclip(ptr, channels, nes);
break;
case NLASTRIP_TYPE_TRANSITION: /* transition */
// XXX code this...
break;
}
}
/* write the accumulated settings to */
@@ -807,7 +899,7 @@ static void animsys_evaluate_nla (PointerRNA *ptr, AnimData *adt, float ctime)
/* 2. for each strip, evaluate then accumulate on top of existing channels, but don't set values yet */
for (nes= estrips.first; nes; nes= nes->next)
nlastrip_ctime_evaluate(&echannels, nes, ctime);
nlastrip_evaluate(ptr, &echannels, nes);
/* 3. flush effects of accumulating channels in NLA to the actual data they affect */
nladata_flush_channels(ptr, &echannels);

View File

@@ -2166,34 +2166,6 @@ void fcurve_free_modifiers (FCurve *fcu)
}
}
/* Bake modifiers for given F-Curve to curve sample data, in the frame range defined
* by start and end (inclusive).
*/
void fcurve_bake_modifiers (FCurve *fcu, int start, int end)
{
ChannelDriver *driver;
/* sanity checks */
// TODO: make these tests report errors using reports not printf's
if ELEM(NULL, fcu, fcu->modifiers.first) {
printf("Error: No F-Curve with F-Curve Modifiers to Bake\n");
return;
}
/* temporarily, disable driver while we sample, so that they don't influence the outcome */
driver= fcu->driver;
fcu->driver= NULL;
/* bake the modifiers, by sampling the curve at each frame */
fcurve_store_samples(fcu, NULL, start, end, fcurve_samplingcb_evalcurve);
/* free the modifiers now */
fcurve_free_modifiers(fcu);
/* restore driver */
fcu->driver= driver;
}
/* Find the active F-Curve Modifier */
FModifier *fcurve_find_active_modifier (FCurve *fcu)
{
@@ -2231,6 +2203,98 @@ void fcurve_set_active_modifier (FCurve *fcu, FModifier *fcm)
fcm->flag |= FMODIFIER_FLAG_ACTIVE;
}
/* Evaluation API --------------------------- */
/* 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
*/
float evaluate_time_fmodifiers (ListBase *modifiers, FCurve *fcu, float cvalue, float evaltime)
{
FModifier *fcm;
float m_evaltime= evaltime;
/* sanity checks */
if ELEM(NULL, modifiers, modifiers->first)
return evaltime;
/* find the first modifier from end of stack that modifies time, and calculate the time the modifier
* would calculate time at
*/
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)
m_evaltime= fmi->evaluate_modifier_time(fcu, fcm, cvalue, evaltime);
break;
}
}
/* return the modified evaltime */
return m_evaltime;
}
/* Evalautes the given set of F-Curve Modifiers using the given data
* Should only be called after evaluate_time_fmodifiers() has been called...
*/
void evaluate_value_fmodifiers (ListBase *modifiers, FCurve *fcu, float *cvalue, float evaltime)
{
FModifier *fcm;
/* sanity checks */
if ELEM(NULL, modifiers, modifiers->first)
return;
/* evaluate modifiers */
for (fcm= modifiers->first; fcm; fcm= fcm->next) {
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) {
if ((fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) == 0)
fmi->evaluate_modifier(fcu, fcm, cvalue, evaltime);
}
}
}
/* Bake modifiers for given F-Curve to curve sample data, in the frame range defined
* by start and end (inclusive).
*/
void fcurve_bake_modifiers (FCurve *fcu, int start, int end)
{
ChannelDriver *driver;
/* sanity checks */
// TODO: make these tests report errors using reports not printf's
if ELEM(NULL, fcu, fcu->modifiers.first) {
printf("Error: No F-Curve with F-Curve Modifiers to Bake\n");
return;
}
/* temporarily, disable driver while we sample, so that they don't influence the outcome */
driver= fcu->driver;
fcu->driver= NULL;
/* bake the modifiers, by sampling the curve at each frame */
fcurve_store_samples(fcu, NULL, start, end, fcurve_samplingcb_evalcurve);
/* free the modifiers now */
fcurve_free_modifiers(fcu);
/* restore driver */
fcu->driver= driver;
}
/* ***************************** F-Curve - Evaluation ********************************* */
/* Evaluate and return the value of the given F-Curve at the specified frame ("evaltime")
@@ -2238,7 +2302,6 @@ void fcurve_set_active_modifier (FCurve *fcu, FModifier *fcm)
*/
float evaluate_fcurve (FCurve *fcu, float evaltime)
{
FModifier *fcm;
float cvalue= 0.0f;
float devaltime;
@@ -2251,28 +2314,8 @@ float evaluate_fcurve (FCurve *fcu, float evaltime)
evaltime= cvalue= evaluate_driver(fcu->driver, evaltime);
}
/* 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 modifiers which modify time to evaluate the base curve at */
devaltime= evaluate_time_fmodifiers(&fcu->modifiers, fcu, cvalue, evaltime);
/* evaluate curve-data
* - 'devaltime' instead of 'evaltime', as this is the time that the last time-modifying
@@ -2284,16 +2327,7 @@ float evaluate_fcurve (FCurve *fcu, float evaltime)
cvalue= fcurve_eval_samples(fcu, fcu->fpt, devaltime);
/* evaluate modifiers */
for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) {
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) {
if ((fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) == 0)
fmi->evaluate_modifier(fcu, fcm, &cvalue, evaltime);
}
}
evaluate_value_fmodifiers(&fcu->modifiers, fcu, &cvalue, evaltime);
/* if curve can only have integral values, perform truncation (i.e. drop the decimal part)
* here so that the curve can be sampled correctly