From d89a8c34f3dc1a2923b6853c260de4f1deab9466 Mon Sep 17 00:00:00 2001 From: Joshua Leung Date: Fri, 19 Mar 2010 03:38:14 +0000 Subject: [PATCH] More F-Modifier Tweaks: This commit started out aiming to make the "Stepped" F-Modifier (committed last night) even more useful, but ended up fixing a few other finer-points of how F-Modifiers work. Firstly, the new stuff: I've addded options to the Stepped F-Modifier to not affect frames before and/or after specified values, and renamed the 'start offset' to 'offset' for clarity. The main objective of this is to allow Stepped F-Modifiers to only affect certain time ranges, so that by layering/using multiple instances of the F-Modifier, it can be possible to have multiple stepping-sizes. This allows for effects like: http://www.pasteall.org/blend/2230 or in words, it provides a convenient mechanism for animators to specify whether sections of the animation is shown "on twos", "fours", or even "forty-second-ths plus a smidgen", as can be easily done with 2D. Assorted changes to support this: * Properly fixed up how F-Modifiers that work with time, evaluate the time to evaluate the curve at. Now layered time effects like this should be possible in a much nicer way. * Added proper value range validation/clamping to many properties. There are still a lot more that need checking, but at least more properties now do "the right thing". --- source/blender/blenkernel/intern/fmodifier.c | 31 +++++-- .../blender/editors/animation/fmodifier_ui.c | 64 +++++++++----- source/blender/makesdna/DNA_anim_types.h | 13 ++- source/blender/makesrna/intern/rna_fcurve.c | 85 ++++++++++++++++++- 4 files changed, 158 insertions(+), 35 deletions(-) diff --git a/source/blender/blenkernel/intern/fmodifier.c b/source/blender/blenkernel/intern/fmodifier.c index 02f35bb78f8..969a4208cd3 100644 --- a/source/blender/blenkernel/intern/fmodifier.c +++ b/source/blender/blenkernel/intern/fmodifier.c @@ -887,16 +887,26 @@ static float fcm_stepped_time (FCurve *fcu, FModifier *fcm, float cvalue, float FMod_Stepped *data= (FMod_Stepped *)fcm->data; int snapblock; + /* check range clamping to see if we should alter the timing to achieve the desired results */ + if (data->flag & FCM_STEPPED_NO_BEFORE) { + if (evaltime < data->start_frame) + return evaltime; + } + if (data->flag & FCM_STEPPED_NO_AFTER) { + if (evaltime > data->end_frame) + return evaltime; + } + /* we snap to the start of the previous closest block of 'step_size' frames * after the start offset has been discarded * - i.e. round down */ - snapblock = (int)((evaltime - data->start) / data->step_size); + snapblock = (int)((evaltime - data->offset) / data->step_size); /* reapply the offset, and multiple the snapblock by the size of the steps to get * the new time to evaluate at */ - return ((float)snapblock * data->step_size) + data->start; + return ((float)snapblock * data->step_size) + data->offset; } static FModifierTypeInfo FMI_STEPPED = { @@ -1201,14 +1211,20 @@ short list_has_suitable_fmodifier (ListBase *modifiers, int mtype, short acttype 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->last) return evaltime; - /* find the first modifier from end of stack that modifies time, and calculate the time the modifier - * would calculate time at + /* Starting from the end of the stack, calculate the time effects of various stacked modifiers + * on the time the F-Curve should be evaluated at. + * + * This is done in reverse order to standard evaluation, as when this is done in standard + * order, each modifier would cause jumps to other points in the curve, forcing all + * previous ones to be evaluated again for them to be correct. However, if we did in the + * reverse order as we have here, we can consider them a macro to micro type of waterfall + * effect, which should get us the desired effects when using layered time manipulations + * (such as multiple 'stepped' modifiers in sequence, causing different stepping rates) */ for (fcm= modifiers->last; fcm; fcm= fcm->prev) { FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); @@ -1217,13 +1233,12 @@ float evaluate_time_fmodifiers (ListBase *modifiers, FCurve *fcu, float cvalue, // 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; + evaltime= fmi->evaluate_modifier_time(fcu, fcm, cvalue, evaltime); } } /* return the modified evaltime */ - return m_evaltime; + return evaltime; } /* Evalautes the given set of F-Curve Modifiers using the given data diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c index 22bb5317383..06a4a2f980b 100644 --- a/source/blender/editors/animation/fmodifier_ui.c +++ b/source/blender/editors/animation/fmodifier_ui.c @@ -42,6 +42,8 @@ #include "MEM_guardedalloc.h" +#include "BLI_blenlib.h" + #include "BKE_context.h" #include "BKE_fcurve.h" @@ -252,15 +254,15 @@ static void draw_modifier__cycles(uiLayout *layout, ID *id, FModifier *fcm, shor /* before range */ col= uiLayoutColumn(split, 1); - uiItemL(col, "Before:", 0); - uiItemR(col, "", 0, &ptr, "before_mode", 0); - uiItemR(col, NULL, 0, &ptr, "before_cycles", 0); + uiItemL(col, "Before:", 0); + uiItemR(col, "", 0, &ptr, "before_mode", 0); + uiItemR(col, NULL, 0, &ptr, "before_cycles", 0); /* after range */ col= uiLayoutColumn(split, 1); - uiItemL(col, "After:", 0); - uiItemR(col, "", 0, &ptr, "after_mode", 0); - uiItemR(col, NULL, 0, &ptr, "after_cycles", 0); + uiItemL(col, "After:", 0); + uiItemR(col, "", 0, &ptr, "after_mode", 0); + uiItemR(col, NULL, 0, &ptr, "after_cycles", 0); } /* --------------- */ @@ -282,13 +284,13 @@ static void draw_modifier__noise(uiLayout *layout, ID *id, FModifier *fcm, short /* col 1 */ col= uiLayoutColumn(split, 0); - uiItemR(col, NULL, 0, &ptr, "size", 0); - uiItemR(col, NULL, 0, &ptr, "strength", 0); + uiItemR(col, NULL, 0, &ptr, "size", 0); + uiItemR(col, NULL, 0, &ptr, "strength", 0); /* col 2 */ col= uiLayoutColumn(split, 0); - uiItemR(col, NULL, 0, &ptr, "phase", 0); - uiItemR(col, NULL, 0, &ptr, "depth", 0); + uiItemR(col, NULL, 0, &ptr, "phase", 0); + uiItemR(col, NULL, 0, &ptr, "depth", 0); } /* --------------- */ @@ -525,16 +527,16 @@ static void draw_modifier__limits(uiLayout *layout, ID *id, FModifier *fcm, shor /* x-minimum */ col= uiLayoutColumn(split, 1); - uiItemR(col, NULL, 0, &ptr, "use_minimum_x", 0); - uiItemR(col, NULL, 0, &ptr, "minimum_x", 0); + uiItemR(col, NULL, 0, &ptr, "use_minimum_x", 0); + uiItemR(col, NULL, 0, &ptr, "minimum_x", 0); /* y-minimum*/ col= uiLayoutColumn(split, 1); - uiItemR(col, NULL, 0, &ptr, "use_minimum_y", 0); - uiItemR(col, NULL, 0, &ptr, "minimum_y", 0); + uiItemR(col, NULL, 0, &ptr, "use_minimum_y", 0); + uiItemR(col, NULL, 0, &ptr, "minimum_y", 0); } - /* row 2: minimum */ + /* row 2: maximum */ { row= uiLayoutRow(layout, 0); @@ -543,13 +545,13 @@ static void draw_modifier__limits(uiLayout *layout, ID *id, FModifier *fcm, shor /* x-minimum */ col= uiLayoutColumn(split, 1); - uiItemR(col, NULL, 0, &ptr, "use_maximum_x", 0); - uiItemR(col, NULL, 0, &ptr, "maximum_x", 0); + uiItemR(col, NULL, 0, &ptr, "use_maximum_x", 0); + uiItemR(col, NULL, 0, &ptr, "maximum_x", 0); /* y-minimum*/ col= uiLayoutColumn(split, 1); - uiItemR(col, NULL, 0, &ptr, "use_maximum_y", 0); - uiItemR(col, NULL, 0, &ptr, "maximum_y", 0); + uiItemR(col, NULL, 0, &ptr, "use_maximum_y", 0); + uiItemR(col, NULL, 0, &ptr, "maximum_y", 0); } } @@ -558,16 +560,32 @@ static void draw_modifier__limits(uiLayout *layout, ID *id, FModifier *fcm, shor /* draw settings for stepped interpolation modifier */ static void draw_modifier__stepped(uiLayout *layout, ID *id, FModifier *fcm, short width) { - uiLayout *col; + uiLayout *col, *subcol; PointerRNA ptr; /* init the RNA-pointer */ RNA_pointer_create(id, &RNA_FModifierStepped, fcm, &ptr); + /* block 1: "stepping" settings */ col= uiLayoutColumn(layout, 0); - - uiItemR(col, NULL, 0, &ptr, "step_size", 0); - uiItemR(col, NULL, 0, &ptr, "start_offset", 0); + uiItemR(col, NULL, 0, &ptr, "step_size", 0); + uiItemR(col, NULL, 0, &ptr, "offset", 0); + + /* block 2: start range settings */ + col= uiLayoutColumn(layout, 1); + uiItemR(col, NULL, 0, &ptr, "use_start_frame", 0); + + subcol = uiLayoutColumn(col, 1); + uiLayoutSetActive(subcol, RNA_boolean_get(&ptr, "use_start_frame")); + uiItemR(subcol, NULL, 0, &ptr, "start_frame", 0); + + /* block 3: end range settings */ + col= uiLayoutColumn(layout, 1); + uiItemR(col, NULL, 0, &ptr, "use_end_frame", 0); + + subcol = uiLayoutColumn(col, 1); + uiLayoutSetActive(subcol, RNA_boolean_get(&ptr, "use_end_frame")); + uiItemR(subcol, NULL, 0, &ptr, "end_frame", 0); } /* --------------- */ diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index 44727218f4f..83f68758da9 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -236,9 +236,20 @@ typedef enum eFMod_Noise_Modifications { /* stepped modifier data */ typedef struct FMod_Stepped { float step_size; /* Number of frames each interpolated value should be held */ - float start; /* Reference frame number that stepping starts from */ + float offset; /* Reference frame number that stepping starts from */ + + float start_frame; /* start frame of the frame range that modifier works in */ + float end_frame; /* end frame of the frame range that modifier works in */ + + int flag; /* various settings */ } FMod_Stepped; +/* stepped modifier range flags */ +typedef enum eFMod_Stepped_Flags { + FCM_STEPPED_NO_BEFORE = (1<<0), /* don't affect frames before the start frame */ + FCM_STEPPED_NO_AFTER = (1<<1), /* don't affect frames after the end frame */ +} eFMod_Stepped_Flags; + /* Drivers -------------------------------------- */ /* Driver Target (dtar) diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index 3ab4673212d..fcd5bb858c7 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -406,6 +406,61 @@ static void rna_FModifierGenerator_coefficients_set(PointerRNA *ptr, const float memcpy(gen->coefficients, values, gen->arraysize * sizeof(float)); } +static void rna_FModifierLimits_minx_range(PointerRNA *ptr, float *min, float *max) +{ + FModifier *fcm= (FModifier*)ptr->data; + FMod_Limits *data= fcm->data; + + *min= MINAFRAMEF; + *max= (data->flag & FCM_LIMIT_XMAX)? data->rect.xmax : MAXFRAMEF; +} + +static void rna_FModifierLimits_maxx_range(PointerRNA *ptr, float *min, float *max) +{ + FModifier *fcm= (FModifier*)ptr->data; + FMod_Limits *data= fcm->data; + + *min= (data->flag & FCM_LIMIT_XMIN)? data->rect.xmin : MINAFRAMEF; + *max= MAXFRAMEF; +} + +static void rna_FModifierLimits_miny_range(PointerRNA *ptr, float *min, float *max) +{ + FModifier *fcm= (FModifier*)ptr->data; + FMod_Limits *data= fcm->data; + + *min= -FLT_MAX; + *max= (data->flag & FCM_LIMIT_YMAX)? data->rect.ymax : FLT_MAX; +} + +static void rna_FModifierLimits_maxy_range(PointerRNA *ptr, float *min, float *max) +{ + FModifier *fcm= (FModifier*)ptr->data; + FMod_Limits *data= fcm->data; + + *min= (data->flag & FCM_LIMIT_YMIN)? data->rect.ymin : -FLT_MAX; + *max= FLT_MAX; +} + + +static void rna_FModifierStepped_start_frame_range(PointerRNA *ptr, float *min, float *max) +{ + FModifier *fcm= (FModifier*)ptr->data; + FMod_Stepped *data= fcm->data; + + *min= MINAFRAMEF; + *max= (data->flag & FCM_STEPPED_NO_AFTER)? data->end_frame : MAXFRAMEF; +} + +static void rna_FModifierStepped_end_frame_range(PointerRNA *ptr, float *min, float *max) +{ + FModifier *fcm= (FModifier*)ptr->data; + FMod_Stepped *data= fcm->data; + + *min= (data->flag & FCM_STEPPED_NO_BEFORE)? data->start_frame : MINAFRAMEF; + *max= MAXFRAMEF; +} + #else static void rna_def_fmodifier_generator(BlenderRNA *brna) @@ -651,21 +706,25 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna) prop= RNA_def_property(srna, "minimum_x", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "rect.xmin"); + RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifierLimits_minx_range"); RNA_def_property_ui_text(prop, "Minimum X", "Lowest X value to allow"); RNA_def_property_update(prop, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); prop= RNA_def_property(srna, "minimum_y", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "rect.ymin"); + RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifierLimits_miny_range"); RNA_def_property_ui_text(prop, "Minimum Y", "Lowest Y value to allow"); RNA_def_property_update(prop, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); prop= RNA_def_property(srna, "maximum_x", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "rect.xmax"); + RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifierLimits_maxx_range"); RNA_def_property_ui_text(prop, "Maximum X", "Highest X value to allow"); RNA_def_property_update(prop, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); prop= RNA_def_property(srna, "maximum_y", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "rect.ymax"); + RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifierLimits_maxy_range"); RNA_def_property_ui_text(prop, "Maximum Y", "Highest Y value to allow"); RNA_def_property_update(prop, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); } @@ -731,9 +790,29 @@ static void rna_def_fmodifier_stepped(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Step Size", "Number of frames to hold each value"); RNA_def_property_update(prop, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); - prop= RNA_def_property(srna, "start_offset", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "start"); - RNA_def_property_ui_text(prop, "Start Offset", "Reference number of frames before frames get held. Use to get hold for '1-3' vs '5-7' holding patterns"); + prop= RNA_def_property(srna, "offset", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "offset"); + RNA_def_property_ui_text(prop, "Offset", "Reference number of frames before frames get held. Use to get hold for '1-3' vs '5-7' holding patterns"); + RNA_def_property_update(prop, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); + + prop= RNA_def_property(srna, "use_start_frame", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_STEPPED_NO_BEFORE); + RNA_def_property_ui_text(prop, "Use Start Frame", "Restrict modifier to only act after its 'start' frame"); + RNA_def_property_update(prop, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); + + prop= RNA_def_property(srna, "use_end_frame", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_STEPPED_NO_AFTER); + RNA_def_property_ui_text(prop, "Use End Frame", "Restrict modifier to only act before its 'end' frame"); + RNA_def_property_update(prop, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); + + prop= RNA_def_property(srna, "start_frame", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifierStepped_start_frame_range"); + RNA_def_property_ui_text(prop, "Start Frame", "Frame that modifier's influence starts (if applicable)"); + RNA_def_property_update(prop, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); + + prop= RNA_def_property(srna, "end_frame", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifierStepped_end_frame_range"); + RNA_def_property_ui_text(prop, "End Frame", "Frame that modifier's influence ends (if applicable)"); RNA_def_property_update(prop, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); }