Dope Sheet: new option to display keyframe interpolation mode and extremes.

With the new automatic handle algorithm, it is possible to do a lot
of the animation via keyframes without touching the curves. It is
however necessary to change the keyframe interpolation and handle
types in certain cases. Currently the dopesheet/action editor
allows changing the types, but does not show them in any way.

To fix, add a new menu option to display this information. For handle
type, it is represented using the shape of the key icons: diamond for
Free, clipped diamond for Aligned, square for Vector, circle for Auto
Clamp, and cirle with dot for Automatic.

Non-bezier interpolation is a property of intervals between keys,
so it is marked by drawing lines, similar to holds. In this initial
version, only the fact of non-bezier interpolation is displayed,
without distinguishing types. For summaries, the line is drawn at
half alpha if not all curves in the group are non-bezier.

In addition, it is sometimes helpful to know the general direction
of change of the curve, and which keys are extremes. This commit
also adds an option to highlight extremes, based on comparing the
keyed values with adjacent keys. Half-intensity display is used
for overshot bezier extremes, or non-uniform summaries.

Reviewers: brecht, aligorith, billreynish

Differential Revision: https://developer.blender.org/D3788
This commit is contained in:
Alexander Gavrilov 2018-10-19 18:55:19 +03:00
parent b18ac77df3
commit a0dfa320cd
26 changed files with 570 additions and 163 deletions

View File

@ -512,6 +512,7 @@ const bTheme U_theme_default = {
.cframe = RGBA(0x5680c2ff),
.ds_channel = RGBA(0x547ca624),
.ds_subchannel = RGBA(0x748da624),
.ds_ipoline = RGBA(0x94e575cc),
.keytype_keyframe = RGBA(0xe8e8e8ff),
.keytype_extreme = RGBA(0xe8b3ccff),
.keytype_breakdown = RGBA(0xb3dbe8ff),

View File

@ -335,6 +335,8 @@ class DOPESHEET_MT_view(Menu):
layout.prop(st, "show_frame_indicator")
layout.prop(st, "show_sliders")
layout.prop(st, "show_group_colors")
layout.prop(st, "show_interpolation")
layout.prop(st, "show_extremes")
layout.prop(st, "use_auto_merge_keyframes")
layout.prop(st, "use_marker_sync")

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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
}

View File

@ -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);
}

View File

@ -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 */
static short compare_ak_bezt(void *node, void *data)
{
BezTriple *bezt = (BezTriple *)data;
/* Set of references to three logically adjacent keys. */
typedef struct BezTripleChain {
/* Current keyframe. */
BezTriple *cur;
return compare_ak_cfraPtr(node, &bezt->vec[1][0]);
/* 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;
}
}
/* New node callback used for building ActKeyColumns from BezTriples */
/* 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)
{
BezTripleChain *chain = (BezTripleChain*)data;
return compare_ak_cfraPtr(node, &chain->cur->vec[1][0]);
}
/* 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);
}
}
}

View File

@ -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) {

View File

@ -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

View File

@ -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);

View File

@ -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)

View File

@ -217,6 +217,7 @@ typedef enum ThemeColorID {
TH_DOPESHEET_CHANNELOB,
TH_DOPESHEET_CHANNELSUBOB,
TH_DOPESHEET_IPOLINE,
TH_PREVIEW_BACK,

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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?

View File

@ -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) {

View File

@ -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;

View File

@ -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;
void main() {
vec2 quad = abs(gl_PointCoord - vec2(0.5));
float dist = quad.x + quad.y;
const float diagonal_scale = sqrt(0.5);
// transparent outside of point
// --- 0 ---
// smooth transition
// --- 1 ---
// pure outline color
// --- 2 ---
// smooth transition
// --- 3 ---
// pure point color
// ...
// dist = 0 at center of point
const float minmax_bias = 0.7;
const float minmax_scale = sqrt(1.0 / (1.0 + 1.0/minmax_bias));
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));
bool test(int bit) {
return (finalFlags & bit) != 0;
}
void main() {
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);
}
}

View File

@ -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;
// calculate concentric radii in pixels
float radius = 0.5 * gl_PointSize;
if (!test(0xF)) {
finalFlags |= 1;
}
// 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;
/* Size-dependent line thickness. */
float half_width = (0.06 + (size - 10) * 0.04);
float line_width = half_width + line_falloff;
// convert to PointCoord units
radii /= size;
/* 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;
/* Square radius. */
radii[2] = round(ext_radius * square_scale);
/* Min/max cutout offset. */
radii[3] = -line_falloff;
/* Convert to PointCoord units. */
radii /= gl_PointSize;
thresholds /= gl_PointSize;
}

View File

@ -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 */

View File

@ -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];

View File

@ -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}
};

View File

@ -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);

View File

@ -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)