Project Pampa request: FCurves normalized display

Added two options to a header of FCurve editor:

- Normalize which makes it so every individual
  curve is fit into -1..1 space.

- Auto-normalize, which probably is to be called
  "Lock" which "locks" curve normalization scale.
  This is useful to prevent curves from jumping
  around when tweaking it.

It's debatable whether it need to be a button to
normalize curves n purpose only, and it's fully
depends on animator's workflow.

Here during Project Pampa we've got Francesco
who get used to auto-renormalization and Hjalti
who prefers locked behavior.

Docs are to be ready soon by Francesco.
Thanks Brecht for the review!
This commit is contained in:
Sergey Sharybin 2013-10-29 18:10:52 +00:00
parent f6e504cee9
commit 16d13e0db9
11 changed files with 137 additions and 22 deletions

View File

@ -46,6 +46,11 @@ class GRAPH_HT_header(Header):
dopesheet_filter(layout, context)
layout.prop(st, "use_normalization", text="Normalize")
row = layout.row()
row.active = st.use_normalization
row.prop(st, "use_auto_normalization", text="Auto")
layout.prop(st, "auto_snap", text="")
layout.prop(st, "pivot_point", text="", icon_only=True)

View File

@ -192,6 +192,7 @@ enum {
#define G_TRANSFORM_OBJ 1
#define G_TRANSFORM_EDIT 2
#define G_TRANSFORM_SEQ 4
#define G_TRANSFORM_FCURVES 8
/* G.special1 */

View File

@ -33,6 +33,7 @@
#include "DNA_anim_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_space_types.h"
#include "DNA_userdef_types.h"
#include "BLI_math.h"
@ -359,9 +360,68 @@ void ANIM_nla_mapping_apply_fcurve(AnimData *adt, FCurve *fcu, short restore, sh
/* *************************************************** */
/* UNITS CONVERSION MAPPING (required for drawing and editing keyframes) */
/* Get unit conversion factor for given ID + F-Curve */
float ANIM_unit_mapping_get_factor(Scene *scene, ID *id, FCurve *fcu, short restore)
/* Get flags used for normalization in ANIM_unit_mapping_get_factor. */
short ANIM_get_normalization_flags(bAnimContext *ac)
{
if (ac->sl->spacetype == SPACE_IPO) {
SpaceIpo *sipo = (SpaceIpo *) ac->sl;
bool use_normalization = (sipo->flag & SIPO_NORMALIZE) != 0;
bool freeze_normalization = (sipo->flag & SIPO_NORMALIZE_FREEZE) != 0;
return use_normalization
? (ANIM_UNITCONV_NORMALIZE | (freeze_normalization ? ANIM_UNITCONV_NORMALIZE_FREEZE : 0))
: 0;
}
return 0;
}
static float normalzation_factor_get(FCurve *fcu, short flag)
{
float factor;
if (flag & ANIM_UNITCONV_RESTORE) {
return 1.0f / fcu->prev_norm_factor;
}
if (flag & ANIM_UNITCONV_NORMALIZE_FREEZE) {
return fcu->prev_norm_factor;
}
if (G.moving & G_TRANSFORM_FCURVES) {
return fcu->prev_norm_factor;
}
fcu->prev_norm_factor = 1.0f;
if (fcu->bezt) {
BezTriple *bezt;
int i;
float max_coord = -FLT_MAX;
if (fcu->totvert < 1) {
return 1.0f;
}
for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
max_coord = max_ff(max_coord, fabsf(bezt->vec[0][1]));
max_coord = max_ff(max_coord, fabsf(bezt->vec[1][1]));
max_coord = max_ff(max_coord, fabsf(bezt->vec[2][1]));
}
if (max_coord > FLT_EPSILON) {
factor = 1.0f / max_coord;
}
}
fcu->prev_norm_factor = factor;
return factor;
}
/* Get unit conversion factor for given ID + F-Curve */
float ANIM_unit_mapping_get_factor(Scene *scene, ID *id, FCurve *fcu, short flag)
{
if (flag & ANIM_UNITCONV_NORMALIZE) {
return normalzation_factor_get(fcu, flag);
}
/* sanity checks */
if (id && fcu && fcu->rna_path) {
PointerRNA ptr, id_ptr;
@ -374,7 +434,7 @@ float ANIM_unit_mapping_get_factor(Scene *scene, ID *id, FCurve *fcu, short rest
if (RNA_SUBTYPE_UNIT(RNA_property_subtype(prop)) == PROP_UNIT_ROTATION) {
/* if the radians flag is not set, default to using degrees which need conversions */
if ((scene) && (scene->unit.system_rotation == USER_UNIT_ROT_RADIANS) == 0) {
if (restore)
if (flag & ANIM_UNITCONV_RESTORE)
return DEG2RADF(1.0f); /* degrees to radians */
else
return RAD2DEGF(1.0f); /* radians to degrees */

View File

@ -559,10 +559,19 @@ typedef enum eAnimUnitConv_Flags {
/* only touch selected vertices */
ANIM_UNITCONV_SELVERTS = (1 << 3),
ANIM_UNITCONV_SKIPKNOTS = (1 << 4),
/* Scale FCurve i a way it fits to -1..1 space */
ANIM_UNITCONV_NORMALIZE = (1 << 5),
/* Only whennormalization is used: use scale factor from previous run,
* prevents curves from jumping all over the place when tweaking them.
*/
ANIM_UNITCONV_NORMALIZE_FREEZE = (1 << 6),
} eAnimUnitConv_Flags;
/* Normalizatin flags from Space Graph passing to ANIM_unit_mapping_get_factor */
short ANIM_get_normalization_flags(bAnimContext *ac);
/* Get unit conversion factor for given ID + F-Curve */
float ANIM_unit_mapping_get_factor(struct Scene *scene, struct ID *id, struct FCurve *fcu, short restore);
float ANIM_unit_mapping_get_factor(struct Scene *scene, struct ID *id, struct FCurve *fcu, short flag);
/* ------------- Utility macros ----------------------- */

View File

@ -490,6 +490,7 @@ static void draw_fcurve_curve(bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d
float stime, etime;
float unitFac;
float dx, dy;
short mapping_flag = ANIM_get_normalization_flags(ac);
/* when opening a blend file on a different sized screen or while dragging the toolbar this can happen
* best just bail out in this case */
@ -503,7 +504,7 @@ static void draw_fcurve_curve(bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d
fcu->driver = NULL;
/* compute unit correction factor */
unitFac = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, 0);
unitFac = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag);
/* Note about sampling frequency:
* Ideally, this is chosen such that we have 1-2 pixels = 1 segment
@ -551,10 +552,11 @@ static void draw_fcurve_curve_samples(bAnimContext *ac, ID *id, FCurve *fcu, Vie
float fac, v[2];
int b = fcu->totvert - 1;
float unit_scale;
short mapping_flag = ANIM_get_normalization_flags(ac);
/* apply unit mapping */
glPushMatrix();
unit_scale = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, 0);
unit_scale = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag);
glScalef(1.0f, unit_scale, 1.0f);
glBegin(GL_LINE_STRIP);
@ -632,10 +634,11 @@ static void draw_fcurve_curve_bezts(bAnimContext *ac, ID *id, FCurve *fcu, View2
int b = fcu->totvert - 1;
int resol;
float unit_scale;
short mapping_flag = ANIM_get_normalization_flags(ac);
/* apply unit mapping */
glPushMatrix();
unit_scale = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, 0);
unit_scale = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag);
glScalef(1.0f, unit_scale, 1.0f);
glBegin(GL_LINE_STRIP);
@ -788,7 +791,8 @@ static void graph_draw_driver_debug(bAnimContext *ac, ID *id, FCurve *fcu)
{
ChannelDriver *driver = fcu->driver;
View2D *v2d = &ac->ar->v2d;
float unitfac = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, false);
short mapping_flag = ANIM_get_normalization_flags(ac);
float unitfac = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag);
/* for now, only show when debugging driver... */
//if ((driver->flag & DRIVER_FLAG_SHOWDEBUG) == 0)
@ -1019,7 +1023,8 @@ void graph_draw_curves(bAnimContext *ac, SpaceIpo *sipo, ARegion *ar, View2DGrid
}
}
else if (((fcu->bezt) || (fcu->fpt)) && (fcu->totvert)) {
float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, 0);
short mapping_flag = ANIM_get_normalization_flags(ac);
float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag);
glPushMatrix();
glScalef(1.0f, unit_scale, 1.0f);

View File

@ -116,6 +116,8 @@ void get_graph_keyframe_extents(bAnimContext *ac, float *xmin, float *xmax, floa
/* get range */
if (calc_fcurve_bounds(fcu, &txmin, &txmax, &tymin, &tymax, do_sel_only, include_handles)) {
short mapping_flag = ANIM_get_normalization_flags(ac);
/* apply NLA scaling */
if (adt) {
txmin = BKE_nla_tweakedit_remap(adt, txmin, NLATIME_CONVERT_MAP);
@ -123,7 +125,7 @@ void get_graph_keyframe_extents(bAnimContext *ac, float *xmin, float *xmax, floa
}
/* apply unit corrections */
unitFac = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, 0);
unitFac = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag);
tymin *= unitFac;
tymax *= unitFac;
@ -330,12 +332,14 @@ static void create_ghost_curves(bAnimContext *ac, int start, int end)
FPoint *fpt;
float unitFac;
int cfra;
SpaceIpo *sipo = (SpaceIpo *) ac->sl;
short mapping_flag = ANIM_get_normalization_flags(ac);
/* disable driver so that it don't muck up the sampling process */
fcu->driver = NULL;
/* calculate unit-mapping factor */
unitFac = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, 0);
unitFac = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag);
/* create samples, but store them in a new curve
* - we cannot use fcurve_store_samples() as that will only overwrite the original curve
@ -578,6 +582,8 @@ static int graphkeys_click_insert_exec(bContext *C, wmOperator *op)
* keyframes if these will be visible after doing so...
*/
if (fcurve_is_keyframable(fcu)) {
short mapping_flag = ANIM_get_normalization_flags(&ac);
/* get frame and value from props */
frame = RNA_float_get(op->ptr, "frame");
val = RNA_float_get(op->ptr, "value");
@ -587,7 +593,7 @@ static int graphkeys_click_insert_exec(bContext *C, wmOperator *op)
frame = BKE_nla_tweakedit_remap(adt, frame, NLATIME_CONVERT_UNMAP);
/* apply inverse unit-mapping to value to get correct value for F-Curves */
val *= ANIM_unit_mapping_get_factor(ac.scene, ale->id, fcu, 1);
val *= ANIM_unit_mapping_get_factor(ac.scene, ale->id, fcu, mapping_flag | ANIM_UNITCONV_RESTORE);
/* insert keyframe on the specified frame + value */
insert_vert_fcurve(fcu, frame, val, 0);
@ -1787,8 +1793,9 @@ static int graphkeys_framejump_exec(bContext *C, wmOperator *UNUSED(op))
for (ale = anim_data.first; ale; ale = ale->next) {
AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
short mapping_flag = ANIM_get_normalization_flags(&ac);
KeyframeEditData current_ked;
float unit_scale = ANIM_unit_mapping_get_factor(ac.scene, ale->id, ale->key_data, ANIM_UNITCONV_ONLYKEYS);
float unit_scale = ANIM_unit_mapping_get_factor(ac.scene, ale->id, ale->key_data, mapping_flag | ANIM_UNITCONV_ONLYKEYS);
memset(&current_ked, 0, sizeof(current_ked));
@ -1891,7 +1898,8 @@ static void snap_graph_keys(bAnimContext *ac, short mode)
/* snap keyframes */
for (ale = anim_data.first; ale; ale = ale->next) {
AnimData *adt = ANIM_nla_mapping_get(ac, ale);
float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, ale->key_data, 0);
short mapping_flag = ANIM_get_normalization_flags(ac);
float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, ale->key_data, mapping_flag);
ked.f1 = cursor_value / unit_scale;
@ -2012,7 +2020,8 @@ static void mirror_graph_keys(bAnimContext *ac, short mode)
/* mirror keyframes */
for (ale = anim_data.first; ale; ale = ale->next) {
AnimData *adt = ANIM_nla_mapping_get(ac, ale);
float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, ale->key_data, ANIM_UNITCONV_ONLYKEYS);
short mapping_flag = ANIM_get_normalization_flags(ac);
float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, ale->key_data, mapping_flag | ANIM_UNITCONV_ONLYKEYS);
/* apply unit corrections */
ked.f1 = cursor_value * unit_scale;

View File

@ -253,6 +253,8 @@ static void borderselect_graphkeys(bAnimContext *ac, rcti rect, short mode, shor
else
mapping_flag = ANIM_UNITCONV_ONLYKEYS;
mapping_flag |= ANIM_get_normalization_flags(ac);
/* loop over data, doing border select */
for (ale = anim_data.first; ale; ale = ale->next) {
AnimData *adt = ANIM_nla_mapping_get(ac, ale);
@ -996,7 +998,8 @@ static void get_nearest_fcurve_verts_list(bAnimContext *ac, const int mval[2], L
SpaceIpo *sipo = (SpaceIpo *)ac->sl;
View2D *v2d = &ac->ar->v2d;
short mapping_flag = 0;
/* get curves to search through
* - if the option to only show keyframes that belong to selected F-Curves is enabled,
* include the 'only selected' flag...
@ -1004,12 +1007,13 @@ static void get_nearest_fcurve_verts_list(bAnimContext *ac, const int mval[2], L
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
if (sipo->flag & SIPO_SELCUVERTSONLY) // FIXME: this should really be check for by the filtering code...
filter |= ANIMFILTER_SEL;
mapping_flag |= ANIM_get_normalization_flags(ac);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
FCurve *fcu = (FCurve *)ale->key_data;
AnimData *adt = ANIM_nla_mapping_get(ac, ale);
float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, 0);
float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag);
/* apply NLA mapping to all the keyframes */
if (adt)

View File

@ -3714,12 +3714,14 @@ static void createTransGraphEditData(bContext *C, TransInfo *t)
float mtx[3][3], smtx[3][3];
const bool use_handle = !(sipo->flag & SIPO_NOHANDLES);
const bool use_local_center = checkUseLocalCenter_GraphEdit(t);
const short anim_map_flag = ANIM_UNITCONV_ONLYSEL | ANIM_UNITCONV_SELVERTS;
short anim_map_flag = ANIM_UNITCONV_ONLYSEL | ANIM_UNITCONV_SELVERTS;
/* determine what type of data we are operating on */
if (ANIM_animdata_get_context(C, &ac) == 0)
return;
anim_map_flag |= ANIM_get_normalization_flags(&ac);
/* filter data */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
@ -3854,8 +3856,8 @@ static void createTransGraphEditData(bContext *C, TransInfo *t)
copy_m3_m3(scaled_mtx, mtx);
copy_m3_m3(scaled_smtx, smtx);
mul_v3_fl(scaled_mtx[1], 1.0f / unit_scale);
mul_v3_fl(scaled_smtx[1], unit_scale);
mul_v3_fl(scaled_mtx[1], unit_scale);
mul_v3_fl(scaled_smtx[1], 1.0f / unit_scale);
/* only include BezTriples whose 'keyframe' occurs on the same side of the current frame as mouse (if applicable) */
for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
@ -5861,6 +5863,9 @@ int special_transform_moving(TransInfo *t)
if (t->spacetype == SPACE_SEQ) {
return G_TRANSFORM_SEQ;
}
else if (t->spacetype == SPACE_IPO) {
return G_TRANSFORM_FCURVES;
}
else if (t->obedit || ((t->flag & T_POSE) && (t->poseobj))) {
return G_TRANSFORM_EDIT;
}

View File

@ -459,6 +459,8 @@ typedef struct FCurve {
/* curve coloring (for editor) */
int color_mode; /* coloring method to use (eFCurve_Coloring) */
float color[3]; /* the last-color this curve took */
float prev_norm_factor, pad;
} FCurve;

View File

@ -366,6 +366,9 @@ typedef enum eGraphEdit_Flag {
SIPO_BEAUTYDRAW_OFF = (1 << 12),
/* draw grouped channels with colors set in group */
SIPO_NODRAWGCOLORS = (1 << 13),
/* normalize curves on display */
SIPO_NORMALIZE = (1 << 14),
SIPO_NORMALIZE_FREEZE = (1 << 15),
} eGraphEdit_Flag;
/* SpaceIpo->mode (Graph Editor Mode) */

View File

@ -2880,6 +2880,18 @@ static void rna_def_space_graph(BlenderRNA *brna)
RNA_def_property_boolean_funcs(prop, "rna_SpaceGraphEditor_has_ghost_curves_get", NULL);
RNA_def_property_ui_text(prop, "Has Ghost Curves", "Graph Editor instance has some ghost curves stored");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, NULL);
/* nromalize curves */
prop = RNA_def_property(srna, "use_normalization", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SIPO_NORMALIZE);
RNA_def_property_ui_text(prop, "use Normalization", "Display curves in normalized to -1..1 range, "
"for easier editing of multiple curves with different ranges");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, NULL);
prop = RNA_def_property(srna, "use_auto_normalization", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SIPO_NORMALIZE_FREEZE);
RNA_def_property_ui_text(prop, "Auto Normalization", "Automatically recalculate curve normalization on every curve edit");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, NULL);
}
static void rna_def_space_nla(BlenderRNA *brna)