diff options
Diffstat (limited to 'source/blender')
24 files changed, 566 insertions, 162 deletions
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index b5e20671cac..6435fe461cc 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -28,7 +28,7 @@ * and keep comment above the defines. * Use STRINGIFY() rather than defining with quotes */ #define BLENDER_VERSION 280 -#define BLENDER_SUBVERSION 27 +#define BLENDER_SUBVERSION 28 /* Several breakages with 280, e.g. collections vs layers */ #define BLENDER_MINVERSION 280 #define BLENDER_MINSUBVERSION 0 diff --git a/source/blender/blenkernel/intern/anim.c b/source/blender/blenkernel/intern/anim.c index d70f3459324..89291b158b7 100644 --- a/source/blender/blenkernel/intern/anim.c +++ b/source/blender/blenkernel/intern/anim.c @@ -65,8 +65,8 @@ // XXX bad level call... extern short compare_ak_cfraPtr(void *node, void *data); -extern void agroup_to_keylist(struct AnimData *adt, struct bActionGroup *agrp, struct DLRBT_Tree *keys); -extern void action_to_keylist(struct AnimData *adt, struct bAction *act, struct DLRBT_Tree *keys); +extern void agroup_to_keylist(struct AnimData *adt, struct bActionGroup *agrp, struct DLRBT_Tree *keys, int saction_flag); +extern void action_to_keylist(struct AnimData *adt, struct bAction *act, struct DLRBT_Tree *keys, int saction_flag); /* --------------------- */ /* forward declarations */ @@ -485,11 +485,11 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, bActionGroup *agrp = BKE_action_group_find_name(adt->action, mpt->pchan->name); if (agrp) { - agroup_to_keylist(adt, agrp, &mpt->keys); + agroup_to_keylist(adt, agrp, &mpt->keys, 0); } } else { - action_to_keylist(adt, adt->action, &mpt->keys); + action_to_keylist(adt, adt->action, &mpt->keys, 0); } } } diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 2f98d068e7d..b622bc016f5 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -77,6 +77,10 @@ static void do_versions_theme(UserDef *userdef, bTheme *btheme) copy_v4_v4_char(btheme->tact.keytype_movehold, U_theme_default.tact.keytype_movehold); copy_v4_v4_char(btheme->tact.keytype_movehold_select, U_theme_default.tact.keytype_movehold_select); } + + if (!USER_VERSION_ATLEAST(280, 28)) { + copy_v4_v4_char(btheme->tact.ds_ipoline, U_theme_default.tact.ds_ipoline); + } #undef USER_VERSION_ATLEAST } diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index 48dd310e2b4..a2046985a36 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -551,11 +551,11 @@ static bool find_prev_next_keyframes(struct bContext *C, int *nextfra, int *prev } /* populate tree with keyframe nodes */ - scene_to_keylist(&ads, scene, &keys); + scene_to_keylist(&ads, scene, &keys, 0); gpencil_to_keylist(&ads, scene->gpd, &keys, false); if (ob) { - ob_to_keylist(&ads, ob, &keys); + ob_to_keylist(&ads, ob, &keys, 0); gpencil_to_keylist(&ads, ob->data, &keys, false); } diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index 33e2a11f4c5..008160e2459 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -42,6 +42,7 @@ #include "BLI_dlrbTree.h" #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_rect.h" #include "DNA_anim_types.h" #include "DNA_cachefile_types.h" @@ -96,24 +97,108 @@ short compare_ak_cfraPtr(void *node, void *data) /* --------------- */ -/* Comparator callback used for ActKeyColumns and BezTriple */ +/* Set of references to three logically adjacent keys. */ +typedef struct BezTripleChain { + /* Current keyframe. */ + BezTriple *cur; + + /* Logical neighbors. May be NULL. */ + BezTriple *prev, *next; +} BezTripleChain; + +/* Categorize the interpolation & handle type of the keyframe. */ +static eKeyframeHandleDrawOpts bezt_handle_type(BezTriple *bezt) +{ + if (bezt->h1 == HD_AUTO_ANIM && bezt->h2 == HD_AUTO_ANIM) { + return KEYFRAME_HANDLE_AUTO_CLAMP; + } + else if (ELEM(bezt->h1, HD_AUTO_ANIM, HD_AUTO) && ELEM(bezt->h2, HD_AUTO_ANIM, HD_AUTO)) { + return KEYFRAME_HANDLE_AUTO; + } + else if (bezt->h1 == HD_VECT && bezt->h2 == HD_VECT) { + return KEYFRAME_HANDLE_VECTOR; + } + else if (ELEM(HD_FREE, bezt->h1, bezt->h2)) { + return KEYFRAME_HANDLE_FREE; + } + else { + return KEYFRAME_HANDLE_ALIGNED; + } +} + +/* Determine if the keyframe is an extreme by comparing with neighbors. + * Ends of fixed-value sections and of the whole curve are also marked. + */ +static eKeyframeExtremeDrawOpts bezt_extreme_type(BezTripleChain *chain) +{ + if (chain->prev == NULL && chain->next == NULL) { + return KEYFRAME_EXTREME_NONE; + } + + /* Keyframe values for the current one and neighbors. */ + float cur_y = chain->cur->vec[1][1]; + float prev_y = cur_y, next_y = cur_y; + + if (chain->prev && !IS_EQF(cur_y, chain->prev->vec[1][1])) { + prev_y = chain->prev->vec[1][1]; + } + if (chain->next && !IS_EQF(cur_y, chain->next->vec[1][1])) { + next_y = chain->next->vec[1][1]; + } + + /* Static hold. */ + if (prev_y == cur_y && next_y == cur_y) { + return KEYFRAME_EXTREME_FLAT; + } + + /* Middle of an incline. */ + if ((prev_y < cur_y && next_y > cur_y) || (prev_y > cur_y && next_y < cur_y)) { + return KEYFRAME_EXTREME_NONE; + } + + /* Bezier handle values for the overshoot check. */ + bool l_bezier = chain->prev && chain->prev->ipo == BEZT_IPO_BEZ; + bool r_bezier = chain->next && chain->cur->ipo == BEZT_IPO_BEZ; + float handle_l = l_bezier ? chain->cur->vec[0][1] : cur_y; + float handle_r = r_bezier ? chain->cur->vec[2][1] : cur_y; + + /* Detect extremes. One of the neighbors is allowed to be equal to current. */ + if (prev_y < cur_y || next_y < cur_y) { + bool is_overshoot = (handle_l > cur_y || handle_r > cur_y); + + return KEYFRAME_EXTREME_MAX | (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0); + } + + if (prev_y > cur_y || next_y > cur_y) { + bool is_overshoot = (handle_l < cur_y || handle_r < cur_y); + + return KEYFRAME_EXTREME_MIN | (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0); + } + + return KEYFRAME_EXTREME_NONE; +} + +/* Comparator callback used for ActKeyColumns and BezTripleChain */ static short compare_ak_bezt(void *node, void *data) { - BezTriple *bezt = (BezTriple *)data; + BezTripleChain *chain = (BezTripleChain*)data; - return compare_ak_cfraPtr(node, &bezt->vec[1][0]); + return compare_ak_cfraPtr(node, &chain->cur->vec[1][0]); } -/* New node callback used for building ActKeyColumns from BezTriples */ +/* New node callback used for building ActKeyColumns from BezTripleChain */ static DLRBT_Node *nalloc_ak_bezt(void *data) { ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn"); - BezTriple *bezt = (BezTriple *)data; + BezTripleChain *chain = (BezTripleChain*)data; + BezTriple *bezt = chain->cur; /* store settings based on state of BezTriple */ ak->cfra = bezt->vec[1][0]; ak->sel = BEZT_ISSEL_ANY(bezt) ? SELECT : 0; ak->key_type = BEZKEYTYPE(bezt); + ak->handle_type = bezt_handle_type(bezt); + ak->extreme_type = bezt_extreme_type(chain); /* count keyframes in this column */ ak->totkey = 1; @@ -121,11 +206,12 @@ static DLRBT_Node *nalloc_ak_bezt(void *data) return (DLRBT_Node *)ak; } -/* Node updater callback used for building ActKeyColumns from BezTriples */ +/* Node updater callback used for building ActKeyColumns from BezTripleChain */ static void nupdate_ak_bezt(void *node, void *data) { ActKeyColumn *ak = (ActKeyColumn *)node; - BezTriple *bezt = (BezTriple *)data; + BezTripleChain *chain = (BezTripleChain*)data; + BezTriple *bezt = chain->cur; /* set selection status and 'touched' status */ if (BEZT_ISSEL_ANY(bezt)) ak->sel = SELECT; @@ -136,6 +222,22 @@ static void nupdate_ak_bezt(void *node, void *data) /* for keyframe type, 'proper' keyframes have priority over breakdowns (and other types for now) */ if (BEZKEYTYPE(bezt) == BEZT_KEYTYPE_KEYFRAME) ak->key_type = BEZT_KEYTYPE_KEYFRAME; + + /* For interpolation type, select the highest value (enum is sorted). */ + ak->handle_type = MAX2(ak->handle_type, bezt_handle_type(bezt)); + + /* For extremes, detect when combining different states. */ + char new_extreme = bezt_extreme_type(chain); + + if (new_extreme != ak->extreme_type) { + /* Replace the flat status without adding mixed. */ + if (ak->extreme_type == KEYFRAME_EXTREME_FLAT) { + ak->extreme_type = new_extreme; + } + else if (new_extreme != KEYFRAME_EXTREME_FLAT) { + ak->extreme_type |= (new_extreme | KEYFRAME_EXTREME_MIXED); + } + } } /* ......... */ @@ -225,7 +327,7 @@ static void nupdate_ak_masklayshape(void *node, void *data) /* --------------- */ /* Add the given BezTriple to the given 'list' of Keyframes */ -static void add_bezt_to_keycolumns_list(DLRBT_Tree *keys, BezTriple *bezt) +static void add_bezt_to_keycolumns_list(DLRBT_Tree *keys, BezTripleChain *bezt) { if (ELEM(NULL, keys, bezt)) return; @@ -291,6 +393,11 @@ static void compute_keyblock_data(ActKeyBlockInfo *info, BezTriple *prev, BezTri } } + /* Remember non-bezier interpolation info. */ + if (prev->ipo != BEZT_IPO_BEZ) { + info->flag |= ACTKEYBLOCK_FLAG_NON_BEZIER; + } + info->sel = BEZT_ISSEL_ANY(prev) || BEZT_ISSEL_ANY(beztn); } @@ -410,11 +517,16 @@ static void update_keyblocks(DLRBT_Tree *keys, BezTriple *bezt, int bezt_len) /* --------- */ +bool actkeyblock_is_valid(ActKeyColumn *ac) +{ + return ac != NULL && ac->next != NULL && ac->totblock > 0; +} + /* Checks if ActKeyBlock should exist... */ int actkeyblock_get_valid_hold(ActKeyColumn *ac) { /* check that block is valid */ - if (ac == NULL || ac->next == NULL || ac->totblock == 0) + if (!actkeyblock_is_valid(ac)) return 0; const int hold_mask = (ACTKEYBLOCK_FLAG_ANY_HOLD | ACTKEYBLOCK_FLAG_STATIC_HOLD | ACTKEYBLOCK_FLAG_ANY_HOLD); @@ -424,7 +536,8 @@ int actkeyblock_get_valid_hold(ActKeyColumn *ac) /* *************************** Keyframe Drawing *************************** */ void draw_keyframe_shape(float x, float y, float size, bool sel, short key_type, short mode, float alpha, - unsigned int pos_id, unsigned int size_id, unsigned int color_id, unsigned int outline_color_id) + unsigned int pos_id, unsigned int size_id, unsigned int color_id, unsigned int outline_color_id, + unsigned int flags_id, short handle_type, short extreme_type) { bool draw_fill = ELEM(mode, KEYFRAME_SHAPE_INSIDE, KEYFRAME_SHAPE_BOTH); bool draw_outline = ELEM(mode, KEYFRAME_SHAPE_FRAME, KEYFRAME_SHAPE_BOTH); @@ -456,6 +569,7 @@ void draw_keyframe_shape(float x, float y, float size, bool sel, short key_type, unsigned char fill_col[4]; unsigned char outline_col[4]; + unsigned int flags = 0; /* draw! */ if (draw_fill) { @@ -504,19 +618,44 @@ void draw_keyframe_shape(float x, float y, float size, bool sel, short key_type, fill_col[2] = outline_col[2]; fill_col[3] = 0; } + + /* Handle type to outline shape. */ + switch (handle_type) { + case KEYFRAME_HANDLE_AUTO_CLAMP: flags = 0x2; break; /* circle */ + case KEYFRAME_HANDLE_AUTO: flags = 0x12; break; /* circle with dot */ + case KEYFRAME_HANDLE_VECTOR: flags = 0xC; break; /* square */ + case KEYFRAME_HANDLE_ALIGNED: flags = 0x5; break; /* clipped diamond */ + + case KEYFRAME_HANDLE_FREE: + default: + flags = 1; /* diamond */ + } + + /* Extreme type to arrow-like shading. */ + if (extreme_type & KEYFRAME_EXTREME_MAX) { + flags |= 0x100; + } + if (extreme_type & KEYFRAME_EXTREME_MIN) { + flags |= 0x200; + } + if (extreme_type & KEYFRAME_EXTREME_MIXED) { + flags |= 0x400; + } } immAttr1f(size_id, size); immAttr4ubv(color_id, fill_col); immAttr4ubv(outline_color_id, outline_col); + immAttr1u(flags_id, flags); immVertex2f(pos_id, x, y); } -static void draw_keylist(View2D *v2d, DLRBT_Tree *keys, float ypos, float yscale_fac, bool channelLocked) +static void draw_keylist(View2D *v2d, DLRBT_Tree *keys, float ypos, float yscale_fac, bool channelLocked, int saction_flag) { const float icon_sz = U.widget_unit * 0.5f * yscale_fac; const float half_icon_sz = 0.5f * icon_sz; const float smaller_sz = 0.35f * icon_sz; + const float ipo_sz = 0.1f * icon_sz; GPU_blend(true); @@ -524,28 +663,39 @@ static void draw_keylist(View2D *v2d, DLRBT_Tree *keys, float ypos, float yscale /* TODO: allow this opacity factor to be themed? */ float alpha = channelLocked ? 0.25f : 1.0f; + /* Show interpolation and handle type? */ + bool show_ipo = (saction_flag & SACTION_SHOW_INTERPOLATION) != 0; + /* draw keyblocks */ if (keys) { float sel_color[4], unsel_color[4]; float sel_mhcol[4], unsel_mhcol[4]; + float ipo_color[4], ipo_color_mix[4]; /* cache colours first */ UI_GetThemeColor4fv(TH_STRIP_SELECT, sel_color); UI_GetThemeColor4fv(TH_STRIP, unsel_color); + UI_GetThemeColor4fv(TH_DOPESHEET_IPOLINE, ipo_color); sel_color[3] *= alpha; unsel_color[3] *= alpha; + ipo_color[3] *= alpha; copy_v4_v4(sel_mhcol, sel_color); sel_mhcol[3] *= 0.8f; copy_v4_v4(unsel_mhcol, unsel_color); unsel_mhcol[3] *= 0.8f; + copy_v4_v4(ipo_color_mix, ipo_color); + ipo_color_mix[3] *= 0.5f; uint block_len = 0; for (ActKeyColumn *ab = keys->first; ab; ab = ab->next) { if (actkeyblock_get_valid_hold(ab)) { block_len++; } + if (show_ipo && actkeyblock_is_valid(ab) && (ab->block.flag & ACTKEYBLOCK_FLAG_NON_BEZIER)) { + block_len++; + } } if (block_len > 0) { @@ -571,6 +721,12 @@ static void draw_keylist(View2D *v2d, DLRBT_Tree *keys, float ypos, float yscale (ab->block.sel) ? sel_color : unsel_color); } } + if (show_ipo && actkeyblock_is_valid(ab) && (ab->block.flag & ACTKEYBLOCK_FLAG_NON_BEZIER)) { + /* draw an interpolation line */ + immRectf_fast_with_color(pos_id, color_id, + ab->cfra, ypos - ipo_sz, ab->next->cfra, ypos + ipo_sz, + (ab->block.conflict & ACTKEYBLOCK_FLAG_NON_BEZIER) ? ipo_color_mix : ipo_color); + } } immEnd(); immUnbindProgram(); @@ -595,14 +751,25 @@ static void draw_keylist(View2D *v2d, DLRBT_Tree *keys, float ypos, float yscale uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); uint color_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); uint outline_color_id = GPU_vertformat_attr_add(format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); GPU_enable_program_point_size(); + immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1); immBegin(GPU_PRIM_POINTS, key_len); + short handle_type = KEYFRAME_HANDLE_NONE, extreme_type = KEYFRAME_EXTREME_NONE; + for (ActKeyColumn *ak = keys->first; ak; ak = ak->next) { if (IN_RANGE_INCL(ak->cfra, v2d->cur.xmin, v2d->cur.xmax)) { + if (show_ipo) { + handle_type = ak->handle_type; + } + if (saction_flag & SACTION_SHOW_EXTREMES) { + extreme_type = ak->extreme_type; + } + draw_keyframe_shape(ak->cfra, ypos, icon_sz, (ak->sel & SELECT), ak->key_type, KEYFRAME_SHAPE_BOTH, alpha, - pos_id, size_id, color_id, outline_color_id); + pos_id, size_id, color_id, outline_color_id, flags_id, handle_type, extreme_type); } } @@ -617,46 +784,52 @@ static void draw_keylist(View2D *v2d, DLRBT_Tree *keys, float ypos, float yscale /* *************************** Channel Drawing Funcs *************************** */ -void draw_summary_channel(View2D *v2d, bAnimContext *ac, float ypos, float yscale_fac) +void draw_summary_channel(View2D *v2d, bAnimContext *ac, float ypos, float yscale_fac, int saction_flag) { DLRBT_Tree keys; + saction_flag &= ~SACTION_SHOW_EXTREMES; + BLI_dlrbTree_init(&keys); - summary_to_keylist(ac, &keys); + summary_to_keylist(ac, &keys, saction_flag); - draw_keylist(v2d, &keys, ypos, yscale_fac, false); + draw_keylist(v2d, &keys, ypos, yscale_fac, false, saction_flag); BLI_dlrbTree_free(&keys); } -void draw_scene_channel(View2D *v2d, bDopeSheet *ads, Scene *sce, float ypos, float yscale_fac) +void draw_scene_channel(View2D *v2d, bDopeSheet *ads, Scene *sce, float ypos, float yscale_fac, int saction_flag) { DLRBT_Tree keys; + saction_flag &= ~SACTION_SHOW_EXTREMES; + BLI_dlrbTree_init(&keys); - scene_to_keylist(ads, sce, &keys); + scene_to_keylist(ads, sce, &keys, saction_flag); - draw_keylist(v2d, &keys, ypos, yscale_fac, false); + draw_keylist(v2d, &keys, ypos, yscale_fac, false, saction_flag); BLI_dlrbTree_free(&keys); } -void draw_object_channel(View2D *v2d, bDopeSheet *ads, Object *ob, float ypos, float yscale_fac) +void draw_object_channel(View2D *v2d, bDopeSheet *ads, Object *ob, float ypos, float yscale_fac, int saction_flag) { DLRBT_Tree keys; + saction_flag &= ~SACTION_SHOW_EXTREMES; + BLI_dlrbTree_init(&keys); - ob_to_keylist(ads, ob, &keys); + ob_to_keylist(ads, ob, &keys, saction_flag); - draw_keylist(v2d, &keys, ypos, yscale_fac, false); + draw_keylist(v2d, &keys, ypos, yscale_fac, false, saction_flag); BLI_dlrbTree_free(&keys); } -void draw_fcurve_channel(View2D *v2d, AnimData *adt, FCurve *fcu, float ypos, float yscale_fac) +void draw_fcurve_channel(View2D *v2d, AnimData *adt, FCurve *fcu, float ypos, float yscale_fac, int saction_flag) { DLRBT_Tree keys; @@ -666,14 +839,14 @@ void draw_fcurve_channel(View2D *v2d, AnimData *adt, FCurve *fcu, float ypos, fl BLI_dlrbTree_init(&keys); - fcurve_to_keylist(adt, fcu, &keys); + fcurve_to_keylist(adt, fcu, &keys, saction_flag); - draw_keylist(v2d, &keys, ypos, yscale_fac, locked); + draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag); BLI_dlrbTree_free(&keys); } -void draw_agroup_channel(View2D *v2d, AnimData *adt, bActionGroup *agrp, float ypos, float yscale_fac) +void draw_agroup_channel(View2D *v2d, AnimData *adt, bActionGroup *agrp, float ypos, float yscale_fac, int saction_flag) { DLRBT_Tree keys; @@ -682,42 +855,46 @@ void draw_agroup_channel(View2D *v2d, AnimData *adt, bActionGroup *agrp, float y BLI_dlrbTree_init(&keys); - agroup_to_keylist(adt, agrp, &keys); + agroup_to_keylist(adt, agrp, &keys, saction_flag); - draw_keylist(v2d, &keys, ypos, yscale_fac, locked); + draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag); BLI_dlrbTree_free(&keys); } -void draw_action_channel(View2D *v2d, AnimData *adt, bAction *act, float ypos, float yscale_fac) +void draw_action_channel(View2D *v2d, AnimData *adt, bAction *act, float ypos, float yscale_fac, int saction_flag) { DLRBT_Tree keys; bool locked = (act && ID_IS_LINKED(act)); + saction_flag &= ~SACTION_SHOW_EXTREMES; + BLI_dlrbTree_init(&keys); - action_to_keylist(adt, act, &keys); + action_to_keylist(adt, act, &keys, saction_flag); - draw_keylist(v2d, &keys, ypos, yscale_fac, locked); + draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag); BLI_dlrbTree_free(&keys); } -void draw_gpencil_channel(View2D *v2d, bDopeSheet *ads, bGPdata *gpd, float ypos, float yscale_fac) +void draw_gpencil_channel(View2D *v2d, bDopeSheet *ads, bGPdata *gpd, float ypos, float yscale_fac, int saction_flag) { DLRBT_Tree keys; + saction_flag &= ~SACTION_SHOW_EXTREMES; + BLI_dlrbTree_init(&keys); gpencil_to_keylist(ads, gpd, &keys, false); - draw_keylist(v2d, &keys, ypos, yscale_fac, false); + draw_keylist(v2d, &keys, ypos, yscale_fac, false, saction_flag); BLI_dlrbTree_free(&keys); } -void draw_gpl_channel(View2D *v2d, bDopeSheet *ads, bGPDlayer *gpl, float ypos, float yscale_fac) +void draw_gpl_channel(View2D *v2d, bDopeSheet *ads, bGPDlayer *gpl, float ypos, float yscale_fac, int saction_flag) { DLRBT_Tree keys; @@ -727,12 +904,12 @@ void draw_gpl_channel(View2D *v2d, bDopeSheet *ads, bGPDlayer *gpl, float ypos, gpl_to_keylist(ads, gpl, &keys); - draw_keylist(v2d, &keys, ypos, yscale_fac, locked); + draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag); BLI_dlrbTree_free(&keys); } -void draw_masklay_channel(View2D *v2d, bDopeSheet *ads, MaskLayer *masklay, float ypos, float yscale_fac) +void draw_masklay_channel(View2D *v2d, bDopeSheet *ads, MaskLayer *masklay, float ypos, float yscale_fac, int saction_flag) { DLRBT_Tree keys; @@ -742,14 +919,14 @@ void draw_masklay_channel(View2D *v2d, bDopeSheet *ads, MaskLayer *masklay, floa mask_to_keylist(ads, masklay, &keys); - draw_keylist(v2d, &keys, ypos, yscale_fac, locked); + draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag); BLI_dlrbTree_free(&keys); } /* *************************** Keyframe List Conversions *************************** */ -void summary_to_keylist(bAnimContext *ac, DLRBT_Tree *keys) +void summary_to_keylist(bAnimContext *ac, DLRBT_Tree *keys, int saction_flag) { if (ac) { ListBase anim_data = {NULL, NULL}; @@ -769,7 +946,7 @@ void summary_to_keylist(bAnimContext *ac, DLRBT_Tree *keys) switch (ale->datatype) { case ALE_FCURVE: - fcurve_to_keylist(ale->adt, ale->data, keys); + fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); break; case ALE_MASKLAY: mask_to_keylist(ac->ads, ale->data, keys); @@ -787,7 +964,7 @@ void summary_to_keylist(bAnimContext *ac, DLRBT_Tree *keys) } } -void scene_to_keylist(bDopeSheet *ads, Scene *sce, DLRBT_Tree *keys) +void scene_to_keylist(bDopeSheet *ads, Scene *sce, DLRBT_Tree *keys, int saction_flag) { bAnimContext ac = {NULL}; ListBase anim_data = {NULL, NULL}; @@ -815,12 +992,12 @@ void scene_to_keylist(bDopeSheet *ads, Scene *sce, DLRBT_Tree *keys) /* loop through each F-Curve, grabbing the keyframes */ for (ale = anim_data.first; ale; ale = ale->next) - fcurve_to_keylist(ale->adt, ale->data, keys); + fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); ANIM_animdata_freelist(&anim_data); } -void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys) +void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, int saction_flag) { bAnimContext ac = {NULL}; ListBase anim_data = {NULL, NULL}; @@ -851,12 +1028,12 @@ void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys) /* loop through each F-Curve, grabbing the keyframes */ for (ale = anim_data.first; ale; ale = ale->next) - fcurve_to_keylist(ale->adt, ale->data, keys); + fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); ANIM_animdata_freelist(&anim_data); } -void cachefile_to_keylist(bDopeSheet *ads, CacheFile *cache_file, DLRBT_Tree *keys) +void cachefile_to_keylist(bDopeSheet *ads, CacheFile *cache_file, DLRBT_Tree *keys, int saction_flag) { if (cache_file == NULL) { return; @@ -881,25 +1058,36 @@ void cachefile_to_keylist(bDopeSheet *ads, CacheFile *cache_file, DLRBT_Tree *ke /* loop through each F-Curve, grabbing the keyframes */ for (bAnimListElem *ale = anim_data.first; ale; ale = ale->next) { - fcurve_to_keylist(ale->adt, ale->data, keys); + fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); } ANIM_animdata_freelist(&anim_data); } -void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys) +void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys, int saction_flag) { - BezTriple *bezt; - unsigned int v; - if (fcu && fcu->totvert && fcu->bezt) { /* apply NLA-mapping (if applicable) */ if (adt) ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0); + /* Check if the curve is cyclic. */ + bool is_cyclic = BKE_fcurve_is_cyclic(fcu) && (fcu->totvert >= 2); + bool do_extremes = (saction_flag & SACTION_SHOW_EXTREMES) != 0; + /* loop through beztriples, making ActKeysColumns */ - for (v = 0, bezt = fcu->bezt; v < fcu->totvert; v++, bezt++) { - add_bezt_to_keycolumns_list(keys, bezt); + BezTripleChain chain = { 0 }; + + for (int v = 0; v < fcu->totvert; v++) { + chain.cur = &fcu->bezt[v]; + + /* Neighbor keys, accounting for being cyclic. */ + if (do_extremes) { + chain.prev = (v > 0) ? &fcu->bezt[v - 1] : is_cyclic ? &fcu->bezt[fcu->totvert - 2] : NULL; + chain.next = (v + 1 < fcu->totvert) ? &fcu->bezt[v + 1] : is_cyclic ? &fcu->bezt[1] : NULL; + } + + add_bezt_to_keycolumns_list(keys, &chain); } /* Update keyblocks. */ @@ -911,26 +1099,26 @@ void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys) } } -void agroup_to_keylist(AnimData *adt, bActionGroup *agrp, DLRBT_Tree *keys) +void agroup_to_keylist(AnimData *adt, bActionGroup *agrp, DLRBT_Tree *keys, int saction_flag) { FCurve *fcu; if (agrp) { /* loop through F-Curves */ for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next) { - fcurve_to_keylist(adt, fcu, keys); + fcurve_to_keylist(adt, fcu, keys, saction_flag); } } } -void action_to_keylist(AnimData *adt, bAction *act, DLRBT_Tree *keys) +void action_to_keylist(AnimData *adt, bAction *act, DLRBT_Tree *keys, int saction_flag) { FCurve *fcu; if (act) { /* loop through F-Curves */ for (fcu = act->curves.first; fcu; fcu = fcu->next) { - fcurve_to_keylist(adt, fcu, keys); + fcurve_to_keylist(adt, fcu, keys, saction_flag); } } } diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index 673ca587811..280aa73b1c8 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -312,7 +312,7 @@ static int poselib_sanitize_exec(bContext *C, wmOperator *op) /* determine which frames have keys */ BLI_dlrbTree_init(&keys); - action_to_keylist(NULL, act, &keys); + action_to_keylist(NULL, act, &keys, 0); /* for each key, make sure there is a corresponding pose */ for (ak = keys.first; ak; ak = ak->next) { diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index bae794e84c4..be3914b2054 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -789,7 +789,7 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p /* do this for each F-Curve */ for (ld = pfl->fcurves.first; ld; ld = ld->next) { FCurve *fcu = (FCurve *)ld->data; - fcurve_to_keylist(pfl->ob->adt, fcu, &pso->keys); + fcurve_to_keylist(pfl->ob->adt, fcu, &pso->keys, 0); } } @@ -1350,7 +1350,7 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st for (ld = pfl->fcurves.first; ld; ld = ld->next) { FCurve *fcu = (FCurve *)ld->data; - fcurve_to_keylist(adt, fcu, &keys); + fcurve_to_keylist(adt, fcu, &keys, 0); } /* find the long keyframe (i.e. hold), and hence obtain the endFrame value diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h index 93fd8238fdd..6421c5817b0 100644 --- a/source/blender/editors/include/ED_keyframes_draw.h +++ b/source/blender/editors/include/ED_keyframes_draw.h @@ -73,6 +73,8 @@ typedef struct ActKeyColumn { /* keyframe info */ char key_type; /* eBezTripe_KeyframeType */ + char handle_type; /* eKeyframeHandleDrawOpts */ + char extreme_type; /* eKeyframeExtremeDrawOpts */ short sel; float cfra; @@ -91,6 +93,8 @@ typedef enum eActKeyBlock_Hold { ACTKEYBLOCK_FLAG_STATIC_HOLD = (1 << 1), /* Key block represents any kind of hold */ ACTKEYBLOCK_FLAG_ANY_HOLD = (1 << 2), + /* The curve segment uses non-bezier interpolation */ + ACTKEYBLOCK_FLAG_NON_BEZIER = (1 << 3), } eActKeyBlock_Flag; /* *********************** Keyframe Drawing ****************************** */ @@ -105,48 +109,73 @@ typedef enum eKeyframeShapeDrawOpts { KEYFRAME_SHAPE_BOTH } eKeyframeShapeDrawOpts; +/* Handle type. */ +typedef enum eKeyframeHandleDrawOpts { + /* Don't draw */ + KEYFRAME_HANDLE_NONE = 0, + /* Various marks in order of increasing display priority. */ + KEYFRAME_HANDLE_AUTO_CLAMP, + KEYFRAME_HANDLE_AUTO, + KEYFRAME_HANDLE_VECTOR, + KEYFRAME_HANDLE_ALIGNED, + KEYFRAME_HANDLE_FREE, +} eKeyframeHandleDrawOpts; + +/* Extreme type. */ +typedef enum eKeyframeExtremeDrawOpts { + KEYFRAME_EXTREME_NONE = 0, + /* Minimum/maximum present. */ + KEYFRAME_EXTREME_MIN = (1 << 0), + KEYFRAME_EXTREME_MAX = (1 << 1), + /* Grouped keys have different states. */ + KEYFRAME_EXTREME_MIXED = (1 << 2), + /* Both neigbors are equal to this key. */ + KEYFRAME_EXTREME_FLAT = (1 << 3), +} eKeyframeExtremeDrawOpts; + /* draw simple diamond-shape keyframe */ /* caller should set up vertex format, bind GPU_SHADER_KEYFRAME_DIAMOND, immBegin(GPU_PRIM_POINTS, n), then call this n times */ void draw_keyframe_shape(float x, float y, float size, bool sel, short key_type, short mode, float alpha, - unsigned int pos_id, unsigned int size_id, unsigned int color_id, unsigned int outline_color_id); + unsigned int pos_id, unsigned int size_id, unsigned int color_id, unsigned int outline_color_id, + unsigned int linemask_id, short ipo_type, short extreme_type); /* ******************************* Methods ****************************** */ /* Channel Drawing ------------------ */ /* F-Curve */ -void draw_fcurve_channel(struct View2D *v2d, struct AnimData *adt, struct FCurve *fcu, float ypos, float yscale_fac); +void draw_fcurve_channel(struct View2D *v2d, struct AnimData *adt, struct FCurve *fcu, float ypos, float yscale_fac, int saction_flag); /* Action Group Summary */ -void draw_agroup_channel(struct View2D *v2d, struct AnimData *adt, struct bActionGroup *agrp, float ypos, float yscale_fac); +void draw_agroup_channel(struct View2D *v2d, struct AnimData *adt, struct bActionGroup *agrp, float ypos, float yscale_fac, int saction_flag); /* Action Summary */ -void draw_action_channel(struct View2D *v2d, struct AnimData *adt, struct bAction *act, float ypos, float yscale_fac); +void draw_action_channel(struct View2D *v2d, struct AnimData *adt, struct bAction *act, float ypos, float yscale_fac, int saction_flag); /* Object Summary */ -void draw_object_channel(struct View2D *v2d, struct bDopeSheet *ads, struct Object *ob, float ypos, float yscale_fac); +void draw_object_channel(struct View2D *v2d, struct bDopeSheet *ads, struct Object *ob, float ypos, float yscale_fac, int saction_flag); /* Scene Summary */ -void draw_scene_channel(struct View2D *v2d, struct bDopeSheet *ads, struct Scene *sce, float ypos, float yscale_fac); +void draw_scene_channel(struct View2D *v2d, struct bDopeSheet *ads, struct Scene *sce, float ypos, float yscale_fac, int saction_flag); /* DopeSheet Summary */ -void draw_summary_channel(struct View2D *v2d, struct bAnimContext *ac, float ypos, float yscale_fac); +void draw_summary_channel(struct View2D *v2d, struct bAnimContext *ac, float ypos, float yscale_fac, int saction_flag); /* Grease Pencil datablock summary */ -void draw_gpencil_channel(struct View2D *v2d, struct bDopeSheet *ads, struct bGPdata *gpd, float ypos, float yscale_fac); +void draw_gpencil_channel(struct View2D *v2d, struct bDopeSheet *ads, struct bGPdata *gpd, float ypos, float yscale_fac, int saction_flag); /* Grease Pencil Layer */ -void draw_gpl_channel(struct View2D *v2d, struct bDopeSheet *ads, struct bGPDlayer *gpl, float ypos, float yscale_fac); +void draw_gpl_channel(struct View2D *v2d, struct bDopeSheet *ads, struct bGPDlayer *gpl, float ypos, float yscale_fac, int saction_flag); /* Mask Layer */ -void draw_masklay_channel(struct View2D *v2d, struct bDopeSheet *ads, struct MaskLayer *masklay, float ypos, float yscale_fac); +void draw_masklay_channel(struct View2D *v2d, struct bDopeSheet *ads, struct MaskLayer *masklay, float ypos, float yscale_fac, int saction_flag); /* Keydata Generation --------------- */ /* F-Curve */ -void fcurve_to_keylist(struct AnimData *adt, struct FCurve *fcu, struct DLRBT_Tree *keys); +void fcurve_to_keylist(struct AnimData *adt, struct FCurve *fcu, struct DLRBT_Tree *keys, int saction_flag); /* Action Group */ -void agroup_to_keylist(struct AnimData *adt, struct bActionGroup *agrp, struct DLRBT_Tree *keys); +void agroup_to_keylist(struct AnimData *adt, struct bActionGroup *agrp, struct DLRBT_Tree *keys, int saction_flag); /* Action */ -void action_to_keylist(struct AnimData *adt, struct bAction *act, struct DLRBT_Tree *keys); +void action_to_keylist(struct AnimData *adt, struct bAction *act, struct DLRBT_Tree *keys, int saction_flag); /* Object */ -void ob_to_keylist(struct bDopeSheet *ads, struct Object *ob, struct DLRBT_Tree *keys); +void ob_to_keylist(struct bDopeSheet *ads, struct Object *ob, struct DLRBT_Tree *keys, int saction_flag); /* Cache File */ -void cachefile_to_keylist(struct bDopeSheet *ads, struct CacheFile *cache_file, struct DLRBT_Tree *keys); +void cachefile_to_keylist(struct bDopeSheet *ads, struct CacheFile *cache_file, struct DLRBT_Tree *keys, int saction_flag); /* Scene */ -void scene_to_keylist(struct bDopeSheet *ads, struct Scene *sce, struct DLRBT_Tree *keys); +void scene_to_keylist(struct bDopeSheet *ads, struct Scene *sce, struct DLRBT_Tree *keys, int saction_flag); /* DopeSheet Summary */ -void summary_to_keylist(struct bAnimContext *ac, struct DLRBT_Tree *keys); +void summary_to_keylist(struct bAnimContext *ac, struct DLRBT_Tree *keys, int saction_flag); /* Grease Pencil datablock summary */ void gpencil_to_keylist(struct bDopeSheet *ads, struct bGPdata *gpd, struct DLRBT_Tree *keys, const bool active); /* Grease Pencil Layer */ @@ -160,6 +189,9 @@ void mask_to_keylist(struct bDopeSheet *UNUSED(ads), struct MaskLayer *masklay, /* Comparator callback used for ActKeyColumns and cframe float-value pointer */ short compare_ak_cfraPtr(void *node, void *data); +/* Checks if ActKeyColumn has any block data */ +bool actkeyblock_is_valid(ActKeyColumn *ab); + /* Checks if ActKeyColumn can be used as a block (i.e. drawn/used to detect "holds") */ int actkeyblock_get_valid_hold(ActKeyColumn *ab); diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index b5e910a650a..cde88e13b1f 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -982,6 +982,12 @@ DEF_ICON_VECTOR(KEYTYPE_EXTREME_VEC) DEF_ICON_VECTOR(KEYTYPE_JITTER_VEC) DEF_ICON_VECTOR(KEYTYPE_MOVING_HOLD_VEC) +DEF_ICON_VECTOR(HANDLETYPE_FREE_VEC) +DEF_ICON_VECTOR(HANDLETYPE_ALIGNED_VEC) +DEF_ICON_VECTOR(HANDLETYPE_VECTOR_VEC) +DEF_ICON_VECTOR(HANDLETYPE_AUTO_VEC) +DEF_ICON_VECTOR(HANDLETYPE_AUTO_CLAMP_VEC) + DEF_ICON_VECTOR(COLORSET_01_VEC) DEF_ICON_VECTOR(COLORSET_02_VEC) DEF_ICON_VECTOR(COLORSET_03_VEC) diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index fd6fa3add30..277aae923d6 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -217,6 +217,7 @@ typedef enum ThemeColorID { TH_DOPESHEET_CHANNELOB, TH_DOPESHEET_CHANNELSUBOB, + TH_DOPESHEET_IPOLINE, TH_PREVIEW_BACK, diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 6289bc09f5a..09e16b806f3 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -286,7 +286,7 @@ static void vicon_small_tri_right_draw(int x, int y, int w, int UNUSED(h), float immUnbindProgram(); } -static void vicon_keytype_draw_wrapper(int x, int y, int w, int h, float alpha, short key_type) +static void vicon_keytype_draw_wrapper(int x, int y, int w, int h, float alpha, short key_type, short handle_type) { /* init dummy theme state for Action Editor - where these colors are defined * (since we're doing this offscreen, free from any particular space_id) @@ -300,25 +300,30 @@ static void vicon_keytype_draw_wrapper(int x, int y, int w, int h, float alpha, * while the draw_keyframe_shape() function needs the midpoint for * the keyframe */ - int xco = x + w / 2; - int yco = y + h / 2; + float xco = x + w / 2 + 0.5f; + float yco = y + h / 2 + 0.5f; GPUVertFormat *format = immVertexFormat(); uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); uint color_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); uint outline_color_id = GPU_vertformat_attr_add(format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); GPU_enable_program_point_size(); + immUniform2f("ViewportSize", -1.0f, -1.0f); immBegin(GPU_PRIM_POINTS, 1); /* draw keyframe - * - size: 0.6 * h (found out experimentally... dunno why!) - * - sel: true (so that "keyframe" state shows the iconic yellow icon) + * - size: (default icon size == 16, default dopesheet icon size == 10) + * - sel: true unless in handletype icons (so that "keyframe" state shows the iconic yellow icon) */ - draw_keyframe_shape(xco, yco, 0.6f * h, true, key_type, KEYFRAME_SHAPE_BOTH, alpha, - pos_id, size_id, color_id, outline_color_id); + bool sel = (handle_type == KEYFRAME_HANDLE_NONE); + + draw_keyframe_shape(xco, yco, (10.0f / 16.0f) * h, sel, key_type, KEYFRAME_SHAPE_BOTH, alpha, + pos_id, size_id, color_id, outline_color_id, + flags_id, handle_type, KEYFRAME_EXTREME_NONE); immEnd(); GPU_disable_program_point_size(); @@ -329,27 +334,52 @@ static void vicon_keytype_draw_wrapper(int x, int y, int w, int h, float alpha, static void vicon_keytype_keyframe_draw(int x, int y, int w, int h, float alpha) { - vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_KEYFRAME); + vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_KEYFRAME, KEYFRAME_HANDLE_NONE); } static void vicon_keytype_breakdown_draw(int x, int y, int w, int h, float alpha) { - vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_BREAKDOWN); + vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_BREAKDOWN, KEYFRAME_HANDLE_NONE); } static void vicon_keytype_extreme_draw(int x, int y, int w, int h, float alpha) { - vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_EXTREME); + vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_EXTREME, KEYFRAME_HANDLE_NONE); } static void vicon_keytype_jitter_draw(int x, int y, int w, int h, float alpha) { - vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_JITTER); + vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_JITTER, KEYFRAME_HANDLE_NONE); } static void vicon_keytype_moving_hold_draw(int x, int y, int w, int h, float alpha) { - vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_MOVEHOLD); + vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_MOVEHOLD, KEYFRAME_HANDLE_NONE); +} + +static void vicon_handletype_free_draw(int x, int y, int w, int h, float alpha) +{ + vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_KEYFRAME, KEYFRAME_HANDLE_FREE); +} + +static void vicon_handletype_aligned_draw(int x, int y, int w, int h, float alpha) +{ + vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_KEYFRAME, KEYFRAME_HANDLE_ALIGNED); +} + +static void vicon_handletype_vector_draw(int x, int y, int w, int h, float alpha) +{ + vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_KEYFRAME, KEYFRAME_HANDLE_VECTOR); +} + +static void vicon_handletype_auto_draw(int x, int y, int w, int h, float alpha) +{ + vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_KEYFRAME, KEYFRAME_HANDLE_AUTO); +} + +static void vicon_handletype_auto_clamp_draw(int x, int y, int w, int h, float alpha) +{ + vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_KEYFRAME, KEYFRAME_HANDLE_AUTO_CLAMP); } static void vicon_colorset_draw(int index, int x, int y, int w, int h, float UNUSED(alpha)) @@ -770,6 +800,12 @@ static void init_internal_icons(void) def_internal_vicon(ICON_KEYTYPE_JITTER_VEC, vicon_keytype_jitter_draw); def_internal_vicon(ICON_KEYTYPE_MOVING_HOLD_VEC, vicon_keytype_moving_hold_draw); + def_internal_vicon(ICON_HANDLETYPE_FREE_VEC, vicon_handletype_free_draw); + def_internal_vicon(ICON_HANDLETYPE_ALIGNED_VEC, vicon_handletype_aligned_draw); + def_internal_vicon(ICON_HANDLETYPE_VECTOR_VEC, vicon_handletype_vector_draw); + def_internal_vicon(ICON_HANDLETYPE_AUTO_VEC, vicon_handletype_auto_draw); + def_internal_vicon(ICON_HANDLETYPE_AUTO_CLAMP_VEC, vicon_handletype_auto_clamp_draw); + def_internal_vicon(ICON_COLORSET_01_VEC, vicon_colorset_draw_01); def_internal_vicon(ICON_COLORSET_02_VEC, vicon_colorset_draw_02); def_internal_vicon(ICON_COLORSET_03_VEC, vicon_colorset_draw_03); diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index 220d7064a5d..0ac534e5fcf 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -548,6 +548,9 @@ const unsigned char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colo case TH_DOPESHEET_CHANNELSUBOB: cp = ts->ds_subchannel; break; + case TH_DOPESHEET_IPOLINE: + cp = ts->ds_ipoline; + break; case TH_PREVIEW_BACK: cp = ts->preview_back; diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 44c2d3191e6..e570340698f 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -2627,10 +2627,10 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) } /* populate tree with keyframe nodes */ - scene_to_keylist(&ads, scene, &keys); + scene_to_keylist(&ads, scene, &keys, 0); if (ob) { - ob_to_keylist(&ads, ob, &keys); + ob_to_keylist(&ads, ob, &keys, 0); if (ob->type == OB_GPENCIL) { const bool active = !(scene->flag & SCE_KEYS_NO_SELONLY); diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c index 3ea3d067216..ef2694ec70a 100644 --- a/source/blender/editors/space_action/action_draw.c +++ b/source/blender/editors/space_action/action_draw.c @@ -357,28 +357,28 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) /* draw 'keyframes' for each specific datatype */ switch (ale->datatype) { case ALE_ALL: - draw_summary_channel(v2d, ale->data, y, ac->yscale_fac); + draw_summary_channel(v2d, ale->data, y, ac->yscale_fac, saction->flag); break; case ALE_SCE: - draw_scene_channel(v2d, ads, ale->key_data, y, ac->yscale_fac); + draw_scene_channel(v2d, ads, ale->key_data, y, ac->yscale_fac, saction->flag); break; case ALE_OB: - draw_object_channel(v2d, ads, ale->key_data, y, ac->yscale_fac); + draw_object_channel(v2d, ads, ale->key_data, y, ac->yscale_fac, saction->flag); break; case ALE_ACT: - draw_action_channel(v2d, adt, ale->key_data, y, ac->yscale_fac); + draw_action_channel(v2d, adt, ale->key_data, y, ac->yscale_fac, saction->flag); break; case ALE_GROUP: - draw_agroup_channel(v2d, adt, ale->data, y, ac->yscale_fac); + draw_agroup_channel(v2d, adt, ale->data, y, ac->yscale_fac, saction->flag); break; case ALE_FCURVE: - draw_fcurve_channel(v2d, adt, ale->key_data, y, ac->yscale_fac); + draw_fcurve_channel(v2d, adt, ale->key_data, y, ac->yscale_fac, saction->flag); break; case ALE_GPFRAME: - draw_gpl_channel(v2d, ads, ale->data, y, ac->yscale_fac); + draw_gpl_channel(v2d, ads, ale->data, y, ac->yscale_fac, saction->flag); break; case ALE_MASKLAY: - draw_masklay_channel(v2d, ads, ale->data, y, ac->yscale_fac); + draw_masklay_channel(v2d, ads, ale->data, y, ac->yscale_fac, saction->flag); break; } } diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 59211ba01d7..c00be5f4c39 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -1449,37 +1449,37 @@ static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_ case ALE_SCE: { Scene *scene = (Scene *)ale->key_data; - scene_to_keylist(ads, scene, &anim_keys); + scene_to_keylist(ads, scene, &anim_keys, 0); break; } case ALE_OB: { Object *ob = (Object *)ale->key_data; - ob_to_keylist(ads, ob, &anim_keys); + ob_to_keylist(ads, ob, &anim_keys, 0); break; } case ALE_ACT: { bAction *act = (bAction *)ale->key_data; - action_to_keylist(adt, act, &anim_keys); + action_to_keylist(adt, act, &anim_keys, 0); break; } case ALE_FCURVE: { FCurve *fcu = (FCurve *)ale->key_data; - fcurve_to_keylist(adt, fcu, &anim_keys); + fcurve_to_keylist(adt, fcu, &anim_keys, 0); break; } } } else if (ale->type == ANIMTYPE_SUMMARY) { /* dopesheet summary covers everything */ - summary_to_keylist(ac, &anim_keys); + summary_to_keylist(ac, &anim_keys, 0); } else if (ale->type == ANIMTYPE_GROUP) { // TODO: why don't we just give groups key_data too? bActionGroup *agrp = (bActionGroup *)ale->data; - agroup_to_keylist(adt, agrp, &anim_keys); + agroup_to_keylist(adt, agrp, &anim_keys, 0); } else if (ale->type == ANIMTYPE_GPLAYER) { // TODO: why don't we just give gplayers key_data too? diff --git a/source/blender/editors/space_clip/clip_dopesheet_draw.c b/source/blender/editors/space_clip/clip_dopesheet_draw.c index dec7487a754..c05a4c1b64c 100644 --- a/source/blender/editors/space_clip/clip_dopesheet_draw.c +++ b/source/blender/editors/space_clip/clip_dopesheet_draw.c @@ -219,14 +219,17 @@ void clip_draw_dopesheet_main(SpaceClip *sc, ARegion *ar, Scene *scene) uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); uint color_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); uint outline_color_id = GPU_vertformat_attr_add(format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); GPU_enable_program_point_size(); + immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1); immBegin(GPU_PRIM_POINTS, keyframe_len); /* all same size with black outline */ immAttr1f(size_id, 2.0f * STRIP_HEIGHT_HALF); immAttr4ub(outline_color_id, 0, 0, 0, 255); + immAttr1u(flags_id, 0); y = (float) CHANNEL_FIRST; /* start again at the top */ for (channel = dopesheet->channels.first; channel; channel = channel->next) { diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index 88f2766a915..15a66b225c9 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -101,12 +101,12 @@ void nla_action_get_color(AnimData *adt, bAction *act, float color[4]) } /* draw the keyframes in the specified Action */ -static void nla_action_draw_keyframes(AnimData *adt, bAction *act, float y, float ymin, float ymax) +static void nla_action_draw_keyframes(View2D *v2d, AnimData *adt, bAction *act, float y, float ymin, float ymax) { /* get a list of the keyframes with NLA-scaling applied */ DLRBT_Tree keys; BLI_dlrbTree_init(&keys); - action_to_keylist(adt, act, &keys); + action_to_keylist(adt, act, &keys, 0); if (ELEM(NULL, act, keys.first)) return; @@ -145,8 +145,10 @@ static void nla_action_draw_keyframes(AnimData *adt, bAction *act, float y, floa uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); uint color_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); uint outline_color_id = GPU_vertformat_attr_add(format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); GPU_enable_program_point_size(); + immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1); immBegin(GPU_PRIM_POINTS, key_len); /* - disregard the selection status of keyframes so they draw a certain way @@ -154,7 +156,8 @@ static void nla_action_draw_keyframes(AnimData *adt, bAction *act, float y, floa */ for (ActKeyColumn *ak = keys.first; ak; ak = ak->next) { draw_keyframe_shape(ak->cfra, y, 6.0f, false, ak->key_type, KEYFRAME_SHAPE_FRAME, 1.0f, - pos_id, size_id, color_id, outline_color_id); + pos_id, size_id, color_id, outline_color_id, + flags_id, KEYFRAME_HANDLE_NONE, KEYFRAME_EXTREME_NONE); } immEnd(); @@ -750,7 +753,7 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *ar) immUnbindProgram(); /* draw keyframes in the action */ - nla_action_draw_keyframes(adt, ale->data, y, yminc + NLACHANNEL_SKIP, ymaxc - NLACHANNEL_SKIP); + nla_action_draw_keyframes(v2d, adt, ale->data, y, yminc + NLACHANNEL_SKIP, ymaxc - NLACHANNEL_SKIP); GPU_blend(false); break; diff --git a/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_frag.glsl b/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_frag.glsl index 7f445369833..1c12a4f942d 100644 --- a/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_frag.glsl @@ -1,31 +1,87 @@ +flat in vec4 radii; +flat in vec4 thresholds; + +flat in vec4 finalColor; +flat in vec4 finalOutlineColor; + +flat in int finalFlags; -in vec4 radii; -in vec4 finalColor; -in vec4 finalOutlineColor; out vec4 fragColor; +const float diagonal_scale = sqrt(0.5); + +const float minmax_bias = 0.7; +const float minmax_scale = sqrt(1.0 / (1.0 + 1.0/minmax_bias)); + +bool test(int bit) { + return (finalFlags & bit) != 0; +} + void main() { - vec2 quad = abs(gl_PointCoord - vec2(0.5)); - float dist = quad.x + quad.y; - -// transparent outside of point -// --- 0 --- -// smooth transition -// --- 1 --- -// pure outline color -// --- 2 --- -// smooth transition -// --- 3 --- -// pure point color -// ... -// dist = 0 at center of point - - float mid_stroke = 0.5 * (radii[1] + radii[2]); - - vec4 backgroundColor = vec4(finalOutlineColor.rgb, 0.0); - - if (dist > mid_stroke) - fragColor = mix(finalOutlineColor, backgroundColor, smoothstep(radii[1], radii[0], dist)); - else - fragColor = mix(finalColor, finalOutlineColor, smoothstep(radii[3], radii[2], dist)); + vec2 pos = gl_PointCoord - vec2(0.5); + vec2 absPos = abs(pos); + float radius = (absPos.x + absPos.y) * diagonal_scale; + + float outline_dist = -1.0; + + /* Diamond outline */ + if (test(0x1)) { + outline_dist = max(outline_dist, radius - radii[0]); + } + + /* Circle outline */ + if (test(0x2)) { + radius = length(absPos); + + outline_dist = max(outline_dist, radius - radii[1]); + } + + /* Top & Bottom clamp */ + if (test(0x4)) { + outline_dist = max(outline_dist, absPos.y - radii[2]); + } + + /* Left & Right clamp */ + if (test(0x8)) { + outline_dist = max(outline_dist, absPos.x - radii[2]); + } + + float alpha = 1 - smoothstep(thresholds[0], thresholds[1], abs(outline_dist)); + + /* Inside the outline. */ + if (outline_dist < 0) { + /* Middle dot */ + if (test(0x10)) { + alpha = max(alpha, 1 - smoothstep(thresholds[2], thresholds[3], radius)); + } + + /* Up and down arrow-like shading. */ + if (test(0x300)) { + float ypos = -1.0; + + /* Up arrow (maximum) */ + if (test(0x100)) { + ypos = max(ypos, pos.y); + } + /* Down arrow (minimum) */ + if (test(0x200)) { + ypos = max(ypos, -pos.y); + } + + /* Arrow shape threshold. */ + float minmax_dist = (ypos - radii[3]) - absPos.x * minmax_bias; + float minmax_step = smoothstep(thresholds[0], thresholds[1], minmax_dist * minmax_scale); + + /* Reduced alpha for uncertain extremes. */ + float minmax_alpha = test(0x400) ? 0.55 : 0.85; + + alpha = max(alpha, minmax_step * minmax_alpha); + } + + fragColor = mix(finalColor, finalOutlineColor, alpha); + } + /* Outside the outline. */ + else { + fragColor = vec4(finalOutlineColor.rgb, finalOutlineColor.a * alpha); + } } diff --git a/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_vert.glsl b/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_vert.glsl index c49832bf9b4..26eb864821a 100644 --- a/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_vert.glsl @@ -1,34 +1,83 @@ uniform mat4 ModelViewProjectionMatrix; +uniform vec2 ViewportSize = vec2(-1, -1); -const float pixel_fudge = sqrt(2.0); -const float outline_width = 1.15 * pixel_fudge; +const float line_falloff = 1.0; +const float circle_scale = sqrt(2.0 / 3.1416); +const float square_scale = sqrt(0.5); + +const float diagonal_scale = sqrt(0.5); in vec2 pos; in float size; in vec4 color; in vec4 outlineColor; -out vec4 finalColor; -out vec4 finalOutlineColor; -out vec4 radii; +in int flags; + +flat out vec4 finalColor; +flat out vec4 finalOutlineColor; + +flat out int finalFlags; + +flat out vec4 radii; +flat out vec4 thresholds; + +bool test(int bit) { + return (flags & bit) != 0; +} + +vec2 line_thresholds(float width) { + return vec2(max(0, width - line_falloff), width); +} void main() { gl_Position = ModelViewProjectionMatrix * vec4(pos, 0.0, 1.0); - // pass through unchanged - gl_PointSize = size + pixel_fudge; // 0.5 pixel_fudge on either side + /* Align to pixel grid if the viewport size is known. */ + if (ViewportSize.x > 0) { + vec2 scale = ViewportSize * 0.5; + vec2 px_pos = (gl_Position.xy + 1) * scale; + vec2 adj_pos = round(px_pos - 0.5) + 0.5; + gl_Position.xy = adj_pos / scale - 1; + } + + /* Pass through parameters. */ finalColor = color; finalOutlineColor = outlineColor; + finalFlags = flags; + + if (!test(0xF)) { + finalFlags |= 1; + } + + /* Size-dependent line thickness. */ + float half_width = (0.06 + (size - 10) * 0.04); + float line_width = half_width + line_falloff; + + /* Outline thresholds. */ + thresholds.xy = line_thresholds(line_width); + + /* Inner dot thresholds. */ + thresholds.zw = line_thresholds(line_width * 1.6); + + /* Extend the primitive size by half line width on either side; odd for symmetry. */ + float ext_radius = round(0.5 * size) + thresholds.x; + + gl_PointSize = ceil(ext_radius + thresholds.y) * 2 + 1; + + /* Diamond radius. */ + radii[0] = ext_radius * diagonal_scale; + + /* Circle radius. */ + radii[1] = ext_radius * circle_scale; - // calculate concentric radii in pixels - float radius = 0.5 * gl_PointSize; + /* Square radius. */ + radii[2] = round(ext_radius * square_scale); - // start at the outside and progress toward the center - radii[0] = radius; - radii[1] = radius - pixel_fudge; - radii[2] = radius - outline_width; - radii[3] = radius - outline_width - pixel_fudge; + /* Min/max cutout offset. */ + radii[3] = -line_falloff; - // convert to PointCoord units - radii /= size; + /* Convert to PointCoord units. */ + radii /= gl_PointSize; + thresholds /= gl_PointSize; } diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index d6991041ec6..7d4ac170489 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -724,7 +724,11 @@ typedef enum eSAction_Flag { /* don't perform realtime updates */ SACTION_NOREALTIMEUPDATES = (1 << 10), /* move markers as well as keyframes */ - SACTION_MARKERS_MOVE = (1 << 11) + SACTION_MARKERS_MOVE = (1 << 11), + /* show interpolation type */ + SACTION_SHOW_INTERPOLATION = (1 << 12), + /* show extremes */ + SACTION_SHOW_EXTREMES = (1 << 13), } eSAction_Flag; /* SpaceAction Mode Settings */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 3fd6fff932b..d03cd1517c0 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -286,10 +286,11 @@ typedef struct ThemeSpace { char handle_free[4], handle_auto[4], handle_vect[4], handle_align[4], handle_auto_clamped[4]; char handle_sel_free[4], handle_sel_auto[4], handle_sel_vect[4], handle_sel_align[4], handle_sel_auto_clamped[4]; - char ds_channel[4], ds_subchannel[4]; /* dopesheet */ + char ds_channel[4], ds_subchannel[4], ds_ipoline[4]; /* dopesheet */ char keytype_keyframe[4], keytype_extreme[4], keytype_breakdown[4], keytype_jitter[4], keytype_movehold[4]; /* keytypes */ char keytype_keyframe_select[4], keytype_extreme_select[4], keytype_breakdown_select[4], keytype_jitter_select[4], keytype_movehold_select[4]; /* keytypes */ char keyborder[4], keyborder_select[4]; + char pad[4]; char console_output[4], console_input[4], console_info[4], console_error[4]; char console_cursor[4], console_select[4]; diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index f576ad4866c..e9671754288 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -57,11 +57,11 @@ static const EnumPropertyItem beztriple_handle_type_items[] = { #endif const EnumPropertyItem rna_enum_keyframe_handle_type_items[] = { - {HD_FREE, "FREE", 0, "Free", ""}, - {HD_VECT, "VECTOR", 0, "Vector", ""}, - {HD_ALIGN, "ALIGNED", 0, "Aligned", ""}, - {HD_AUTO, "AUTO", 0, "Automatic", ""}, - {HD_AUTO_ANIM, "AUTO_CLAMPED", 0, "Auto Clamped", "Auto handles clamped to not overshoot"}, + {HD_FREE, "FREE", ICON_HANDLETYPE_FREE_VEC, "Free", "Completely independent manually set handle"}, + {HD_ALIGN, "ALIGNED", ICON_HANDLETYPE_ALIGNED_VEC, "Aligned", "Manually set handle with rotation locked together with its pair"}, + {HD_VECT, "VECTOR", ICON_HANDLETYPE_VECTOR_VEC, "Vector", "Automatic handles that create straight lines"}, + {HD_AUTO, "AUTO", ICON_HANDLETYPE_AUTO_VEC, "Automatic", "Automatic handles that create smooth curves"}, + {HD_AUTO_ANIM, "AUTO_CLAMPED", ICON_HANDLETYPE_AUTO_CLAMP_VEC, "Auto Clamped", "Automatic handles that create smooth curves which only change direction at keyframes"}, {0, NULL, 0, NULL, NULL} }; diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 9c1ced01bd1..56385ee5447 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -3960,6 +3960,18 @@ static void rna_def_space_dopesheet(BlenderRNA *brna) "(pose bones only currently)"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_DOPESHEET, NULL); + prop = RNA_def_property(srna, "show_interpolation", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SACTION_SHOW_INTERPOLATION); + RNA_def_property_ui_text(prop, "Show Handles And Interpolation", + "Display keyframe handle types and non-bezier interpolation modes"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_DOPESHEET, NULL); + + prop = RNA_def_property(srna, "show_extremes", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SACTION_SHOW_EXTREMES); + RNA_def_property_ui_text(prop, "Show Curve Extremes", + "Mark keyframes where the key value flow changes direction, based on comparison with adjacent keys"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_DOPESHEET, NULL); + /* editing */ prop = RNA_def_property(srna, "use_auto_merge_keyframes", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SACTION_NOTRANSKEYCULL); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 1acba55549a..bbad0ebf26b 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -2849,6 +2849,12 @@ static void rna_def_userdef_theme_space_action(BlenderRNA *brna) RNA_def_property_array(prop, 4); RNA_def_property_ui_text(prop, "Preview Range", "Color of preview range overlay"); RNA_def_property_update(prop, 0, "rna_userdef_update"); + + prop = RNA_def_property(srna, "interpolation_line", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_float_sdna(prop, NULL, "ds_ipoline"); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "Interpolation Line", "Color of lines showing non-bezier interpolation modes"); + RNA_def_property_update(prop, 0, "rna_userdef_update"); } static void rna_def_userdef_theme_space_nla(BlenderRNA *brna) |