diff options
21 files changed, 991 insertions, 48 deletions
diff --git a/source/blender/blenkernel/BKE_mask.h b/source/blender/blenkernel/BKE_mask.h index a6f2f73720a..ec2eb82a9eb 100644 --- a/source/blender/blenkernel/BKE_mask.h +++ b/source/blender/blenkernel/BKE_mask.h @@ -151,6 +151,7 @@ int BKE_mask_layer_shape_find_frame_range(struct MaskLayer *masklay, const float struct MaskLayerShape *BKE_mask_layer_shape_alloc(struct MaskLayer *masklay, const int frame); void BKE_mask_layer_shape_free(struct MaskLayerShape *masklay_shape); struct MaskLayerShape *BKE_mask_layer_shape_varify_frame(struct MaskLayer *masklay, const int frame); +struct MaskLayerShape *BKE_mask_layer_shape_duplicate(struct MaskLayerShape *masklay_shape); void BKE_mask_layer_shape_unlink(struct MaskLayer *masklay, struct MaskLayerShape *masklay_shape); void BKE_mask_layer_shape_sort(struct MaskLayer *masklay); diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index 02c1169892c..bb2940091e4 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -1820,6 +1820,19 @@ MaskLayerShape *BKE_mask_layer_shape_varify_frame(MaskLayer *masklay, const int return masklay_shape; } +MaskLayerShape *BKE_mask_layer_shape_duplicate(MaskLayerShape *masklay_shape) +{ + MaskLayerShape *masklay_shape_copy; + + masklay_shape_copy = MEM_dupallocN(masklay_shape); + + if (LIKELY(masklay_shape_copy->data)) { + masklay_shape_copy->data = MEM_dupallocN(masklay_shape_copy->data); + } + + return masklay_shape_copy; +} + void BKE_mask_layer_shape_unlink(MaskLayer *masklay, MaskLayerShape *masklay_shape) { BLI_remlink(&masklay->splines_shapes, masklay_shape); diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 7f7d269a7c3..f6b301c4594 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -54,6 +54,7 @@ #include "DNA_world_types.h" #include "DNA_gpencil_types.h" #include "DNA_speaker_types.h" +#include "DNA_mask_types.h" #include "RNA_access.h" @@ -2516,6 +2517,172 @@ static bAnimChannelType ACF_GPL = acf_gpl_setting_ptr /* pointer for setting */ }; + +/* Mask Datablock ------------------------------------------- */ + +/* get backdrop color for mask datablock widget */ +static void acf_mask_color(bAnimContext *UNUSED(ac), bAnimListElem *UNUSED(ale), float r_color[3]) +{ + /* these are ID-blocks, but not exactly standalone... */ + UI_GetThemeColorShade3fv(TH_DOPESHEET_CHANNELSUBOB, 20, r_color); +} + +// TODO: just get this from RNA? +static int acf_mask_icon(bAnimListElem *UNUSED(ale)) +{ + return ICON_GREASEPENCIL; // MASK_TODO - need real icon +} + +/* check if some setting exists for this channel */ +static short acf_mask_setting_valid(bAnimContext *UNUSED(ac), bAnimListElem *UNUSED(ale), int setting) +{ + switch (setting) { + /* only select and expand supported */ + case ACHANNEL_SETTING_SELECT: + case ACHANNEL_SETTING_EXPAND: + return 1; + + default: + return 0; + } +} + +/* get the appropriate flag(s) for the setting when it is valid */ +static int acf_mask_setting_flag(bAnimContext *UNUSED(ac), int setting, short *neg) +{ + /* clear extra return data first */ + *neg = 0; + + switch (setting) { + case ACHANNEL_SETTING_SELECT: /* selected */ + return AGRP_SELECTED; + + case ACHANNEL_SETTING_EXPAND: /* expanded */ + return MASK_ANIMF_EXPAND; + } + + /* this shouldn't happen */ + return 0; +} + +/* get pointer to the setting */ +static void *acf_mask_setting_ptr(bAnimListElem *ale, int UNUSED(setting), short *type) +{ + Mask *mask = (Mask *)ale->data; + + /* all flags are just in mask->flag for now... */ + return GET_ACF_FLAG_PTR(mask->flag, type); +} + +/* mask datablock type define */ +static bAnimChannelType ACF_MASKDATA = +{ + "Mask Datablock", /* type name */ + + acf_mask_color, /* backdrop color */ + acf_group_backdrop, /* backdrop */ + acf_generic_indention_0, /* indent level */ + acf_generic_group_offset, /* offset */ + + acf_generic_idblock_name, /* name */ + acf_generic_idfill_nameprop, /* name prop */ + acf_mask_icon, /* icon */ + + acf_mask_setting_valid, /* has setting */ + acf_mask_setting_flag, /* flag for setting */ + acf_mask_setting_ptr /* pointer for setting */ +}; + +/* Mask Layer ------------------------------------------- */ + +/* name for grease pencil layer entries */ +static void acf_masklay_name(bAnimListElem *ale, char *name) +{ + MaskLayer *masklay = (MaskLayer *)ale->data; + + if (masklay && name) + BLI_strncpy(name, masklay->name, ANIM_CHAN_NAME_SIZE); +} + +/* name property for grease pencil layer entries */ +static short acf_masklay_name_prop(bAnimListElem *ale, PointerRNA *ptr, PropertyRNA **prop) +{ + if (ale->data) { + RNA_pointer_create(ale->id, &RNA_MaskLayer, ale->data, ptr); + *prop = RNA_struct_name_property(ptr->type); + + return (*prop != NULL); + } + + return 0; +} + +/* check if some setting exists for this channel */ +static short acf_masklay_setting_valid(bAnimContext *UNUSED(ac), bAnimListElem *UNUSED(ale), int setting) +{ + switch (setting) { + /* unsupported */ + case ACHANNEL_SETTING_EXPAND: /* mask layers are more like F-Curves than groups */ + case ACHANNEL_SETTING_VISIBLE: /* graph editor only */ + return 0; + + /* always available */ + default: + return 1; + } +} + +/* get the appropriate flag(s) for the setting when it is valid */ +static int acf_masklay_setting_flag(bAnimContext *UNUSED(ac), int setting, short *neg) +{ + /* clear extra return data first */ + *neg = 0; + + switch (setting) { + case ACHANNEL_SETTING_SELECT: /* selected */ + return MASK_LAYERFLAG_SELECT; + +// case ACHANNEL_SETTING_MUTE: /* muted */ +// return GP_LAYER_HIDE; + + case ACHANNEL_SETTING_PROTECT: /* protected */ + // *neg = 1; - if we change this to edtiability + return MASK_LAYERFLAG_LOCKED; + + default: /* unsupported */ + return 0; + } +} + +/* get pointer to the setting */ +static void *acf_masklay_setting_ptr(bAnimListElem *ale, int UNUSED(setting), short *type) +{ + MaskLayer *masklay = (MaskLayer *)ale->data; + + /* all flags are just in agrp->flag for now... */ + return GET_ACF_FLAG_PTR(masklay->flag, type); +} + +/* grease pencil layer type define */ +static bAnimChannelType ACF_MASKLAYER = +{ + "Mask Layer", /* type name */ + + acf_generic_channel_color, /* backdrop color */ + acf_generic_channel_backdrop, /* backdrop */ + acf_generic_indention_flexible, /* indent level */ + acf_generic_group_offset, /* offset */ + + acf_masklay_name, /* name */ + acf_masklay_name_prop, /* name prop */ + NULL, /* icon */ + + acf_masklay_setting_valid, /* has setting */ + acf_masklay_setting_flag, /* flag for setting */ + acf_masklay_setting_ptr /* pointer for setting */ +}; + + /* *********************************************** */ /* Type Registration and General Access */ @@ -2566,6 +2733,9 @@ static void ANIM_init_channel_typeinfo_data(void) animchannelTypeInfo[type++] = &ACF_GPD; /* Grease Pencil Datablock */ animchannelTypeInfo[type++] = &ACF_GPL; /* Grease Pencil Layer */ + animchannelTypeInfo[type++] = &ACF_MASKDATA; /* Mask Datablock */ + animchannelTypeInfo[type++] = &ACF_MASKLAYER; /* Mask Layer */ + // TODO: these types still need to be implemented!!! // probably need a few extra flags for these special cases... animchannelTypeInfo[type++] = NULL; /* NLA Track */ diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index dc0886a473e..5c57407d14e 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -43,6 +43,7 @@ #include "DNA_scene_types.h" #include "DNA_key_types.h" #include "DNA_gpencil_types.h" +#include "DNA_mask_types.h" #include "RNA_access.h" #include "RNA_define.h" @@ -51,6 +52,7 @@ #include "BKE_fcurve.h" #include "BKE_gpencil.h" #include "BKE_context.h" +#include "BKE_mask.h" #include "BKE_global.h" #include "UI_view2d.h" @@ -258,6 +260,10 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, s if (ale->flag & GP_LAYER_SELECT) sel = ACHANNEL_SETFLAG_CLEAR; break; + case ANIMTYPE_MASKLAYER: + if (ale->flag & MASK_LAYERFLAG_SELECT) + sel = ACHANNEL_SETFLAG_CLEAR; + break; } } } @@ -354,6 +360,14 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, s ACHANNEL_SET_FLAG(gpl, sel, GP_LAYER_SELECT); } break; + + case ANIMTYPE_MASKLAYER: + { + MaskLayer *masklay = (MaskLayer *)ale->data; + + ACHANNEL_SET_FLAG(masklay, sel, MASK_LAYERFLAG_SELECT); + } + break; } } @@ -1055,6 +1069,10 @@ static int animchannels_rearrange_exec(bContext *C, wmOperator *op) /* Grease Pencil channels */ printf("Grease Pencil not supported for moving yet\n"); } + else if (ac.datatype == ANIMCONT_MASK) { + /* Grease Pencil channels */ + printf("Mask does not supported for moving yet\n"); + } else if (ac.datatype == ANIMCONT_ACTION) { /* Directly rearrange action's channels */ rearrange_action_channels(&ac, ac.data, mode); @@ -1205,6 +1223,17 @@ static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op)) BLI_freelinkN(&gpd->layers, gpl); } break; + + case ANIMTYPE_MASKLAYER: + { + /* Grease Pencil layer */ + Mask *mask = (Mask *)ale->id; + MaskLayer *masklay = (MaskLayer *)ale->data; + + /* try to delete the layer's data and the layer itself */ + BKE_mask_layer_remove(mask, masklay); + } + break; } } @@ -2287,6 +2316,36 @@ static int mouse_anim_channels(bAnimContext *ac, float UNUSED(x), int channel_in notifierFlags |= (ND_ANIMCHAN | NA_EDITED); } break; + case ANIMTYPE_MASKDATABLOCK: + { + Mask *mask = (Mask *)ale->data; + + /* toggle expand + * - although the triangle widget already allows this, the whole channel can also be used for this purpose + */ + mask->flag ^= MASK_ANIMF_EXPAND; + + notifierFlags |= (ND_ANIMCHAN | NA_EDITED); + } + break; + case ANIMTYPE_MASKLAYER: + { + MaskLayer *masklay = (MaskLayer *)ale->data; + + /* select/deselect */ + if (selectmode == SELECT_INVERT) { + /* invert selection status of this layer only */ + masklay->flag ^= MASK_LAYERFLAG_SELECT; + } + else { + /* select layer by itself */ + ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); + masklay->flag |= MASK_LAYERFLAG_SELECT; + } + + notifierFlags |= (ND_ANIMCHAN | NA_EDITED); + } + break; default: if (G.debug & G_DEBUG) printf("Error: Invalid channel type in mouse_anim_channels()\n"); diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index 160e6957513..752e458ef78 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -56,6 +56,7 @@ #include "DNA_lamp_types.h" #include "DNA_lattice_types.h" #include "DNA_key_types.h" +#include "DNA_mask_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meta_types.h" @@ -86,6 +87,7 @@ #include "BKE_main.h" #include "BKE_material.h" #include "BKE_node.h" +#include "BKE_mask.h" #include "BKE_sequencer.h" #include "BKE_utildefines.h" @@ -173,6 +175,22 @@ static short actedit_get_context(bAnimContext *ac, SpaceAction *saction) ac->mode = saction->mode; return 1; + case SACTCONT_MASK: /* Grease Pencil */ // XXX review how this mode is handled... + /* update scene-pointer (no need to check for pinning yet, as not implemented) */ +{ + // TODO, other methods to get the mask + // Sequence *seq = BKE_sequencer_active_get(ac->scene); + //MovieClip *clip = ac->scene->clip; +// struct Mask *mask = seq ? seq->mask : NULL; + + saction->ads.source = (ID *)ac->scene;; + + ac->datatype = ANIMCONT_MASK; + ac->data = &saction->ads; + + ac->mode = saction->mode; + return 1; +} case SACTCONT_DOPESHEET: /* DopeSheet */ /* update scene-pointer (no need to check for pinning yet, as not implemented) */ saction->ads.source = (ID *)ac->scene; @@ -807,7 +825,18 @@ static bAnimListElem *make_new_animlistelem(void *data, short datatype, ID *owne ale->datatype = ALE_GPFRAME; } break; - + + case ANIMTYPE_MASKLAYER: + { + MaskLayer *masklay = (MaskLayer *)data; + + ale->flag = masklay->flag; + + ale->key_data = NULL; + ale->datatype = ALE_MASKLAY; + } + break; + case ANIMTYPE_NLATRACK: { NlaTrack *nlt = (NlaTrack *)data; @@ -1353,6 +1382,83 @@ static size_t animdata_filter_gpencil(ListBase *anim_data, void *UNUSED(data), i return items; } +static size_t animdata_filter_mask_data(ListBase *anim_data, Mask *mask, const int filter_mode) +{ + MaskLayer *masklay_act = BKE_mask_layer_active(mask); + MaskLayer *masklay; + size_t items = 0; + + /* loop over layers as the conditions are acceptable */ + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + /* only if selected */ + if (ANIMCHANNEL_SELOK(SEL_MASKLAY(masklay)) ) { + /* only if editable */ +// if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_GPL(gpl)) { + /* active... */ + if (!(filter_mode & ANIMFILTER_ACTIVE) || (masklay_act == masklay)) { + /* add to list */ + ANIMCHANNEL_NEW_CHANNEL(masklay, ANIMTYPE_MASKLAYER, mask); + + +// if (filter_mode & ANIMFILTER_TMP_PEEK) +// return 1; +// else { +// bAnimListElem *ale = make_new_animlistelem(masklay, channel_type, (ID *)owner_id); +// if (ale) { +// BLI_addtail(anim_data, ale); +// items ++; +// } +// } + + } +// } + } + } + + return items; +} + +static size_t animdata_filter_mask(ListBase *anim_data, void *UNUSED(data), int filter_mode) +{ + Mask *mask; + size_t items = 0; + + /* for now, grab grease pencil datablocks directly from main */ + // XXX: this is not good... + for (mask = G.main->mask.first; mask; mask = mask->id.next) { + ListBase tmp_data = {NULL, NULL}; + size_t tmp_items = 0; + + /* only show if gpd is used by something... */ + if (ID_REAL_USERS(mask) < 1) + continue; + + /* add gpencil animation channels */ + BEGIN_ANIMFILTER_SUBCHANNELS(EXPANDED_MASK(mask)) + { + tmp_items += animdata_filter_mask_data(&tmp_data, mask, filter_mode); + } + END_ANIMFILTER_SUBCHANNELS; + + /* did we find anything? */ + if (tmp_items) { + /* include data-expand widget first */ + if (filter_mode & ANIMFILTER_LIST_CHANNELS) { + /* add gpd as channel too (if for drawing, and it has layers) */ + ANIMCHANNEL_NEW_CHANNEL(mask, ANIMTYPE_MASKDATABLOCK, NULL); + } + + /* now add the list of collected channels */ + BLI_movelisttolist(anim_data, &tmp_data); + BLI_assert((tmp_data.first == tmp_data.last) && (tmp_data.first == NULL)); + items += tmp_items; + } + } + + /* return the number of items added to the list */ + return items; +} + /* NOTE: owner_id is scene, material, or texture block, which is the direct owner of the node tree in question */ // TODO: how to handle group nodes is still unclear... static size_t animdata_filter_ds_nodetree(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, ID *owner_id, bNodeTree *ntree, int filter_mode) @@ -2280,7 +2386,7 @@ size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, int filter_mo items += animfilter_action(ac, anim_data, ads, data, filter_mode, (ID *)obact); } break; - + case ANIMCONT_SHAPEKEY: /* 'ShapeKey Editor' */ { /* the check for the DopeSheet summary is included here since the summary works here too */ @@ -2294,7 +2400,13 @@ size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, int filter_mo items = animdata_filter_gpencil(anim_data, data, filter_mode); } break; - + + case ANIMCONT_MASK: + { + items = animdata_filter_mask(anim_data, data, filter_mode); + } + break; + case ANIMCONT_DOPESHEET: /* 'DopeSheet Editor' */ { /* the DopeSheet editor is the primary place where the DopeSheet summaries are useful */ diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index cee8d15a807..e6fc4d5a168 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -209,7 +209,7 @@ static DLRBT_Node *nalloc_ak_masklayshape(void *data) /* store settings based on state of BezTriple */ ak->cfra = masklay_shape->frame; - ak->sel = (masklay_shape->flag & SELECT) ? SELECT : 0; + ak->sel = (masklay_shape->flag & MASK_SHAPE_SELECT) ? SELECT : 0; /* set 'modified', since this is used to identify long keyframes */ ak->modified = 1; @@ -224,7 +224,7 @@ static void nupdate_ak_masklayshape(void *node, void *data) MaskLayerShape *masklay_shape = (MaskLayerShape *)data; /* set selection status and 'touched' status */ - if (masklay_shape->flag & SELECT) ak->sel = SELECT; + if (masklay_shape->flag & MASK_SHAPE_SELECT) ak->sel = SELECT; ak->modified += 1; } @@ -818,6 +818,21 @@ void draw_gpl_channel(View2D *v2d, bDopeSheet *ads, bGPDlayer *gpl, float ypos) BLI_dlrbTree_free(&keys); } +void draw_masklay_channel(View2D *v2d, bDopeSheet *ads, MaskLayer *masklay, float ypos) +{ + DLRBT_Tree keys; + + BLI_dlrbTree_init(&keys); + + mask_to_keylist(ads, masklay, &keys); + + BLI_dlrbTree_linkedlist_sync(&keys); + + draw_keylist(v2d, &keys, NULL, ypos, (masklay->flag & MASK_LAYERFLAG_LOCKED)); + + BLI_dlrbTree_free(&keys); +} + /* *************************** Keyframe List Conversions *************************** */ void summary_to_keylist(bAnimContext *ac, DLRBT_Tree *keys, DLRBT_Tree *blocks) diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index d91b29bb281..24aa88a36bd 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -97,7 +97,8 @@ typedef enum eAnimCont_Types { ANIMCONT_FCURVES = 5, /* animation F-Curves (bDopesheet) */ ANIMCONT_DRIVERS = 6, /* drivers (bDopesheet) */ ANIMCONT_NLA = 7, /* nla (bDopesheet) */ - ANIMCONT_CHANNEL = 8 /* animation channel (bAnimListElem) */ + ANIMCONT_CHANNEL = 8, /* animation channel (bAnimListElem) */ + ANIMCONT_MASK = 9 /* mask dopesheet */ } eAnimCont_Types; /* --------------- Channels -------------------- */ @@ -160,6 +161,9 @@ typedef enum eAnim_ChannelType { ANIMTYPE_GPDATABLOCK, ANIMTYPE_GPLAYER, + + ANIMTYPE_MASKDATABLOCK, + ANIMTYPE_MASKLAYER, ANIMTYPE_NLATRACK, ANIMTYPE_NLAACTION, @@ -173,6 +177,7 @@ typedef enum eAnim_KeyType { ALE_NONE = 0, /* no keyframe data */ ALE_FCURVE, /* F-Curve */ ALE_GPFRAME, /* Grease Pencil Frames */ + ALE_MASKLAY, /* Mask */ ALE_NLASTRIP, /* NLA Strips */ ALE_ALL, /* All channels summary */ @@ -279,6 +284,15 @@ typedef enum eAnimFilter_Flags { #define EDITABLE_GPL(gpl) ((gpl->flag & GP_LAYER_LOCKED) == 0) #define SEL_GPL(gpl) (gpl->flag & GP_LAYER_SELECT) +/* Mask Only */ +/* Grease Pencil datablock settings */ +#define EXPANDED_MASK(mask) (mask->flag & MASK_ANIMF_EXPAND) +/* Grease Pencil Layer settings */ +#define EDITABLE_MASK(masklay) ((masklay->flag & MASK_LAYERFLAG_LOCKED) == 0) +#define SEL_MASKLAY(masklay) (masklay->flag & SELECT) + + + /* NLA only */ #define SEL_NLT(nlt) (nlt->flag & NLATRACK_SELECTED) #define EDITABLE_NLT(nlt) ((nlt->flag & NLATRACK_PROTECTED) == 0) diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index 2040d4b9c32..ad686588f17 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -83,7 +83,8 @@ void draw_gpencil_view3d(struct Scene *scene, struct View3D *v3d, struct ARegion void gpencil_panel_standard(const struct bContext *C, struct Panel *pa); /* ----------- Grease-Pencil AnimEdit API ------------------ */ -short gplayer_frames_looper(struct bGPDlayer *gpl, struct Scene *scene, short (*gpf_cb)(struct bGPDframe *, struct Scene *)); +short gplayer_frames_looper(struct bGPDlayer *gpl, struct Scene *scene, + short (*gpf_cb)(struct bGPDframe *, struct Scene *)); void gplayer_make_cfra_list(struct bGPDlayer *gpl, ListBase *elems, short onlysel); short is_gplayer_frame_selected(struct bGPDlayer *gpl); diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h index e24c21bc133..8a65699f404 100644 --- a/source/blender/editors/include/ED_keyframes_draw.h +++ b/source/blender/editors/include/ED_keyframes_draw.h @@ -123,6 +123,8 @@ void draw_summary_channel(struct View2D *v2d, struct bAnimContext *ac, float ypo /* Grease Pencil Layer */ // XXX not restored void draw_gpl_channel(struct View2D *v2d, struct bDopeSheet *ads, struct bGPDlayer *gpl, float ypos); +/* Mask Layer */ +void draw_masklay_channel(struct View2D *v2d, struct bDopeSheet *ads, struct MaskLayer *masklay, float ypos); /* Keydata Generation --------------- */ /* F-Curve */ diff --git a/source/blender/editors/include/ED_mask.h b/source/blender/editors/include/ED_mask.h index 0c2f6807632..bbfc47c582a 100644 --- a/source/blender/editors/include/ED_mask.h +++ b/source/blender/editors/include/ED_mask.h @@ -33,6 +33,7 @@ struct wmKeyConfig; struct MaskLayer; +struct MaskLayerShape; /* mask_editor.c */ void ED_operatortypes_mask(void); @@ -47,4 +48,25 @@ void ED_mask_layer_shape_auto_key(struct MaskLayer *masklay, const int frame); int ED_mask_layer_shape_auto_key_all(struct Mask *mask, const int frame); int ED_mask_layer_shape_auto_key_select(struct Mask *mask, const int frame); -#endif /* ED_TEXT_H */ +/* ----------- Mask AnimEdit API ------------------ */ +short masklayer_frames_looper(struct MaskLayer *masklay, struct Scene *scene, + short (*masklay_shape_cb)(struct MaskLayerShape *, struct Scene *)); +void masklayer_make_cfra_list(struct MaskLayer *masklay, ListBase *elems, short onlysel); + +short is_masklayer_frame_selected(struct MaskLayer *masklay); +void set_masklayer_frame_selection(struct MaskLayer *masklay, short mode); +void select_mask_frames(struct MaskLayer *masklay, short select_mode); +void select_mask_frame(struct MaskLayer *masklay, int selx, short select_mode); +void borderselect_masklayer_frames(struct MaskLayer *masklay, float min, float max, short select_mode); + +void delete_masklayer_frames(struct MaskLayer *masklay); +void duplicate_masklayer_frames(struct MaskLayer *masklay); + +//void free_gpcopybuf(void); +//void copy_gpdata(void); +//void paste_gpdata(void); + +void snap_masklayer_frames(struct MaskLayer *masklay, short mode); +void mirror_masklayer_frames(struct MaskLayer *masklay, short mode); + +#endif /* __ED_MASK_H__ */ diff --git a/source/blender/editors/mask/CMakeLists.txt b/source/blender/editors/mask/CMakeLists.txt index f15e9c27732..57be5a2234a 100644 --- a/source/blender/editors/mask/CMakeLists.txt +++ b/source/blender/editors/mask/CMakeLists.txt @@ -40,6 +40,7 @@ set(SRC mask_add.c mask_draw.c mask_edit.c + mask_editaction.c mask_ops.c mask_relationships.c mask_select.c diff --git a/source/blender/editors/mask/mask_editaction.c b/source/blender/editors/mask/mask_editaction.c new file mode 100644 index 00000000000..c773e13e2eb --- /dev/null +++ b/source/blender/editors/mask/mask_editaction.c @@ -0,0 +1,253 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/mask/mask_editaction.c + * \ingroup edgpencil + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "DNA_mask_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_fcurve.h" +#include "BKE_mask.h" + +#include "ED_anim_api.h" +#include "ED_keyframes_edit.h" + +/* ***************************************** */ +/* NOTE ABOUT THIS FILE: + * This file contains code for editing Grease Pencil data in the Action Editor + * as a 'keyframes', so that a user can adjust the timing of Grease Pencil drawings. + * Therefore, this file mostly contains functions for selecting Grease-Pencil frames. + */ +/* ***************************************** */ +/* Generics - Loopers */ + +/* Loops over the gp-frames for a gp-layer, and applies the given callback */ +short masklayer_frames_looper(MaskLayer *masklay, Scene *scene, short (*masklay_shape_cb)(MaskLayerShape *, Scene *)) +{ + MaskLayerShape *masklay_shape; + + /* error checker */ + if (masklay == NULL) + return 0; + + /* do loop */ + for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape->next) { + /* execute callback */ + if (masklay_shape_cb(masklay_shape, scene)) + return 1; + } + + /* nothing to return */ + return 0; +} + +/* ****************************************** */ +/* Data Conversion Tools */ + +/* make a listing all the gp-frames in a layer as cfraelems */ +void masklayer_make_cfra_list(MaskLayer *masklay, ListBase *elems, short onlysel) +{ + MaskLayerShape *masklay_shape; + CfraElem *ce; + + /* error checking */ + if (ELEM(NULL, masklay, elems)) + return; + + /* loop through gp-frames, adding */ + for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape->next) { + if ((onlysel == 0) || (masklay_shape->flag & MASK_SHAPE_SELECT)) { + ce = MEM_callocN(sizeof(CfraElem), "CfraElem"); + + ce->cfra = (float)masklay_shape->frame; + ce->sel = (masklay_shape->flag & MASK_SHAPE_SELECT) ? 1 : 0; + + BLI_addtail(elems, ce); + } + } +} + +/* ***************************************** */ +/* Selection Tools */ + +/* check if one of the frames in this layer is selected */ +short is_masklayer_frame_selected(MaskLayer *masklay) +{ + MaskLayerShape *masklay_shape; + + /* error checking */ + if (masklay == NULL) + return 0; + + /* stop at the first one found */ + for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape->next) { + if (masklay_shape->flag & MASK_SHAPE_SELECT) + return 1; + } + + /* not found */ + return 0; +} + +/* helper function - select gp-frame based on SELECT_* mode */ +static void masklayshape_select(MaskLayerShape *masklay_shape, short select_mode) +{ + if (masklay_shape == NULL) + return; + + switch (select_mode) { + case SELECT_ADD: + masklay_shape->flag |= MASK_SHAPE_SELECT; + break; + case SELECT_SUBTRACT: + masklay_shape->flag &= ~MASK_SHAPE_SELECT; + break; + case SELECT_INVERT: + masklay_shape->flag ^= MASK_SHAPE_SELECT; + break; + } +} + +/* set all/none/invert select (like above, but with SELECT_* modes) */ +void select_mask_frames(MaskLayer *masklay, short select_mode) +{ + MaskLayerShape *masklay_shape; + + /* error checking */ + if (masklay == NULL) + return; + + /* handle according to mode */ + for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape->next) { + masklayshape_select(masklay_shape, select_mode); + } +} + +/* set all/none/invert select */ +void set_masklayer_frame_selection(MaskLayer *masklay, short mode) +{ + /* error checking */ + if (masklay == NULL) + return; + + /* now call the standard function */ + select_mask_frames(masklay, mode); +} + +/* select the frame in this layer that occurs on this frame (there should only be one at most) */ +void select_mask_frame(MaskLayer *masklay, int selx, short select_mode) +{ + MaskLayerShape *masklay_shape; + + if (masklay == NULL) + return; + + /* search through frames for a match */ + for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape->next) { + /* there should only be one frame with this frame-number */ + if (masklay_shape->frame == selx) { + masklayshape_select(masklay_shape, select_mode); + break; + } + } +} + +/* select the frames in this layer that occur within the bounds specified */ +void borderselect_masklayer_frames(MaskLayer *masklay, float min, float max, short select_mode) +{ + MaskLayerShape *masklay_shape; + + if (masklay == NULL) + return; + + /* only select those frames which are in bounds */ + for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape->next) { + if (IN_RANGE(masklay_shape->frame, min, max)) + masklayshape_select(masklay_shape, select_mode); + } +} + +/* ***************************************** */ +/* Frame Editing Tools */ + +/* Delete selected frames */ +void delete_masklayer_frames(MaskLayer *masklay) +{ + MaskLayerShape *masklay_shape, *masklay_shape_next; + + /* error checking */ + if (masklay == NULL) + return; + + /* check for frames to delete */ + for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape_next) { + masklay_shape_next = masklay_shape->next; + + if (masklay_shape->flag & MASK_SHAPE_SELECT) + BKE_mask_layer_shape_unlink(masklay, masklay_shape); + } +} + +/* Duplicate selected frames from given gp-layer */ +void duplicate_masklayer_frames(MaskLayer *masklay) +{ + MaskLayerShape *masklay_shape, *gpfn; + + /* error checking */ + if (masklay == NULL) + return; + + /* duplicate selected frames */ + for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = gpfn) { + gpfn = masklay_shape->next; + + /* duplicate this frame */ + if (masklay_shape->flag & MASK_SHAPE_SELECT) { + MaskLayerShape *mask_shape_dupe; + + /* duplicate frame, and deselect self */ + mask_shape_dupe = BKE_mask_layer_shape_duplicate(masklay_shape); + masklay_shape->flag &= ~MASK_SHAPE_SELECT; + + // XXX - how to handle duplicate frames? + BLI_insertlinkafter(&masklay->splines_shapes, masklay_shape, mask_shape_dupe); + } + } +} diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c index 84ff038a050..edec57d9e93 100644 --- a/source/blender/editors/space_action/action_draw.c +++ b/source/blender/editors/space_action/action_draw.c @@ -290,6 +290,18 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) else glColor4ub(col2[0], col2[1], col2[2], 0x44); glRectf(v2d->cur.xmin, (float)y - ACHANNEL_HEIGHT_HALF, v2d->cur.xmax + EXTRA_SCROLL_PAD, (float)y + ACHANNEL_HEIGHT_HALF); } + else if (ac->datatype == ANIMCONT_MASK) { + /* TODO --- this is a copy of gpencil */ + /* frames less than one get less saturated background */ + if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x22); + else glColor4ub(col2[0], col2[1], col2[2], 0x22); + glRectf(0.0f, (float)y - ACHANNEL_HEIGHT_HALF, v2d->cur.xmin, (float)y + ACHANNEL_HEIGHT_HALF); + + /* frames one and higher get a saturated background */ + if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x44); + else glColor4ub(col2[0], col2[1], col2[2], 0x44); + glRectf(v2d->cur.xmin, (float)y - ACHANNEL_HEIGHT_HALF, v2d->cur.xmax + EXTRA_SCROLL_PAD, (float)y + ACHANNEL_HEIGHT_HALF); + } } } @@ -340,6 +352,9 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) case ALE_GPFRAME: draw_gpl_channel(v2d, ads, ale->data, y); break; + case ALE_MASKLAY: + draw_masklay_channel(v2d, ads, ale->data, y); + break; } } } diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 0c32ebe549d..8b26461cc4f 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -44,6 +44,7 @@ #include "DNA_gpencil_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_mask_types.h" #include "RNA_access.h" #include "RNA_define.h" @@ -64,6 +65,7 @@ #include "ED_screen.h" #include "ED_transform.h" #include "ED_markers.h" +#include "ED_mask.h" #include "WM_api.h" #include "WM_types.h" @@ -256,6 +258,19 @@ static void get_keyframe_extents(bAnimContext *ac, float *min, float *max, const *max = MAX2(*max, gpf->framenum); } } + else if (ale->datatype == ALE_MASKLAY) { + MaskLayer *masklay = ale->data; + MaskLayerShape *masklay_shape; + + /* find mask layer which is less than or equal to cframe */ + for (masklay_shape = masklay->splines_shapes.first; + masklay_shape; + masklay_shape = masklay_shape->next) + { + *min = MIN2(*min, masklay_shape->frame); + *max = MAX2(*max, masklay_shape->frame); + } + } else { FCurve *fcu = (FCurve *)ale->key_data; float tmin, tmax; @@ -476,11 +491,16 @@ static int actkeys_copy_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; /* copy keyframes */ - if (ac.datatype == ANIMCONT_GPENCIL) { + if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { // FIXME... BKE_report(op->reports, RPT_ERROR, "Keyframe pasting is not available for Grease Pencil mode"); return OPERATOR_CANCELLED; } + else if (ac.datatype == ANIMCONT_MASK) { + // FIXME... + BKE_report(op->reports, RPT_ERROR, "Keyframe pasting is not available for mask mode"); + return OPERATOR_CANCELLED; + } else { if (copy_action_keys(&ac)) { BKE_report(op->reports, RPT_ERROR, "No keyframes copied to keyframes copy/paste buffer"); @@ -521,9 +541,9 @@ static int actkeys_paste_exec(bContext *C, wmOperator *op) ac.reports = op->reports; /* paste keyframes */ - if (ac.datatype == ANIMCONT_GPENCIL) { + if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { // FIXME... - BKE_report(op->reports, RPT_ERROR, "Keyframe pasting is not available for Grease Pencil mode"); + BKE_report(op->reports, RPT_ERROR, "Keyframe pasting is not available for Grease Pencil or Mask mode"); return OPERATOR_CANCELLED; } else { @@ -625,7 +645,7 @@ static int actkeys_insertkey_exec(bContext *C, wmOperator *op) /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) return OPERATOR_CANCELLED; - if (ac.datatype == ANIMCONT_GPENCIL) + if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) return OPERATOR_CANCELLED; /* what channels to affect? */ @@ -671,7 +691,7 @@ static void duplicate_action_keys(bAnimContext *ac) int filter; /* filter data */ - if (ac->datatype == ANIMCONT_GPENCIL) + if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); else filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS); @@ -681,8 +701,12 @@ static void duplicate_action_keys(bAnimContext *ac) for (ale = anim_data.first; ale; ale = ale->next) { if (ale->type == ANIMTYPE_FCURVE) duplicate_fcurve_keys((FCurve *)ale->key_data); - else + else if (ale->type == ANIMTYPE_GPLAYER) duplicate_gplayer_frames((bGPDlayer *)ale->data); + else if (ale->type == ANIMTYPE_MASKLAYER) + duplicate_masklayer_frames((MaskLayer *)ale->data); + else + BLI_assert(0); } /* free filtered list */ @@ -703,7 +727,7 @@ static int actkeys_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) duplicate_action_keys(&ac); /* validate keyframes after editing */ - if (ac.datatype != ANIMCONT_GPENCIL) + if (!ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) ANIM_editkeyframes_refresh(&ac); /* set notifier that keyframes have changed */ @@ -744,7 +768,7 @@ static void delete_action_keys(bAnimContext *ac) int filter; /* filter data */ - if (ac->datatype == ANIMCONT_GPENCIL) + if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); else filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS); @@ -752,7 +776,13 @@ static void delete_action_keys(bAnimContext *ac) /* loop through filtered data and delete selected keys */ for (ale = anim_data.first; ale; ale = ale->next) { - if (ale->type != ANIMTYPE_GPLAYER) { + if (ale->type == ANIMTYPE_GPLAYER) { + delete_gplayer_frames((bGPDlayer *)ale->data); + } + else if (ale->type == ANIMTYPE_MASKLAYER) { + delete_masklayer_frames((MaskLayer *)ale->data); + } + else { FCurve *fcu = (FCurve *)ale->key_data; AnimData *adt = ale->adt; @@ -763,8 +793,6 @@ static void delete_action_keys(bAnimContext *ac) if ((fcu->totvert == 0) && (list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE) == 0)) ANIM_fcurve_delete_from_animdata(ac, adt, fcu); } - else - delete_gplayer_frames((bGPDlayer *)ale->data); } /* free filtered list */ @@ -785,7 +813,7 @@ static int actkeys_delete_exec(bContext *C, wmOperator *UNUSED(op)) delete_action_keys(&ac); /* validate keyframes after editing */ - if (ac.datatype != ANIMCONT_GPENCIL) + if (!ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) ANIM_editkeyframes_refresh(&ac); /* set notifier that keyframes have changed */ @@ -840,7 +868,7 @@ static int actkeys_clean_exec(bContext *C, wmOperator *op) /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) return OPERATOR_CANCELLED; - if (ac.datatype == ANIMCONT_GPENCIL) + if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) return OPERATOR_PASS_THROUGH; /* get cleaning threshold */ @@ -907,7 +935,7 @@ static int actkeys_sample_exec(bContext *C, wmOperator *UNUSED(op)) /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) return OPERATOR_CANCELLED; - if (ac.datatype == ANIMCONT_GPENCIL) + if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) return OPERATOR_PASS_THROUGH; /* sample keyframes */ @@ -1014,7 +1042,7 @@ static int actkeys_expo_exec(bContext *C, wmOperator *op) /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) return OPERATOR_CANCELLED; - if (ac.datatype == ANIMCONT_GPENCIL) + if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) return OPERATOR_PASS_THROUGH; /* get handle setting mode */ @@ -1085,7 +1113,7 @@ static int actkeys_ipo_exec(bContext *C, wmOperator *op) /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) return OPERATOR_CANCELLED; - if (ac.datatype == ANIMCONT_GPENCIL) + if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) return OPERATOR_PASS_THROUGH; /* get handle setting mode */ @@ -1165,7 +1193,7 @@ static int actkeys_handletype_exec(bContext *C, wmOperator *op) /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) return OPERATOR_CANCELLED; - if (ac.datatype == ANIMCONT_GPENCIL) + if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) return OPERATOR_PASS_THROUGH; /* get handle setting mode */ @@ -1236,7 +1264,7 @@ static int actkeys_keytype_exec(bContext *C, wmOperator *op) /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) return OPERATOR_CANCELLED; - if (ac.datatype == ANIMCONT_GPENCIL) + if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) return OPERATOR_PASS_THROUGH; /* get handle setting mode */ @@ -1359,7 +1387,7 @@ static void snap_action_keys(bAnimContext *ac, short mode) KeyframeEditFunc edit_cb; /* filter data */ - if (ac->datatype == ANIMCONT_GPENCIL) + if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); else filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS); @@ -1404,7 +1432,7 @@ static int actkeys_snap_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; // XXX... - if (ac.datatype == ANIMCONT_GPENCIL) + if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) return OPERATOR_PASS_THROUGH; /* get snapping mode */ @@ -1482,7 +1510,7 @@ static void mirror_action_keys(bAnimContext *ac, short mode) } /* filter data */ - if (ac->datatype == ANIMCONT_GPENCIL) + if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); else filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS); @@ -1518,7 +1546,7 @@ static int actkeys_mirror_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; // XXX... - if (ac.datatype == ANIMCONT_GPENCIL) + if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) return OPERATOR_PASS_THROUGH; /* get mirroring mode */ diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 0c6b0f5eb3d..bd73e35815b 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -43,6 +43,7 @@ #include "DNA_gpencil_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_mask_types.h" #include "RNA_access.h" #include "RNA_define.h" @@ -55,6 +56,7 @@ #include "ED_anim_api.h" #include "ED_gpencil.h" +#include "ED_mask.h" #include "ED_keyframes_draw.h" #include "ED_keyframes_edit.h" #include "ED_markers.h" @@ -92,7 +94,7 @@ static void deselect_action_keys(bAnimContext *ac, short test, short sel) KeyframeEditFunc test_cb, sel_cb; /* determine type-based settings */ - if (ac->datatype == ANIMCONT_GPENCIL) + if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); else filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS); @@ -112,6 +114,12 @@ static void deselect_action_keys(bAnimContext *ac, short test, short sel) break; } } + else if (ale->type == ANIMTYPE_MASKLAYER) { + if (is_masklayer_frame_selected(ale->data)) { + sel = SELECT_SUBTRACT; + break; + } + } else { if (ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, test_cb, NULL)) { sel = SELECT_SUBTRACT; @@ -128,6 +136,8 @@ static void deselect_action_keys(bAnimContext *ac, short test, short sel) for (ale = anim_data.first; ale; ale = ale->next) { if (ale->type == ANIMTYPE_GPLAYER) set_gplayer_frame_selection(ale->data, sel); + else if (ale->type == ANIMTYPE_MASKLAYER) + set_masklayer_frame_selection(ale->data, sel); else ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, sel_cb, NULL); } @@ -250,6 +260,8 @@ static void borderselect_action(bAnimContext *ac, rcti rect, short mode, short s /* loop over data selecting */ if (ale->type == ANIMTYPE_GPLAYER) borderselect_gplayer_frames(ale->data, rectf.xmin, rectf.xmax, selectmode); + else if (ale->type == ANIMTYPE_MASKLAYER) + borderselect_masklayer_frames(ale->data, rectf.xmin, rectf.xmax, selectmode); else ANIM_animchannel_keyframes_loop(&ked, ac->ads, ale, ok_cb, select_cb, NULL); } @@ -400,6 +412,9 @@ static void markers_selectkeys_between(bAnimContext *ac) else if (ale->type == ANIMTYPE_GPLAYER) { borderselect_gplayer_frames(ale->data, min, max, SELECT_ADD); } + else if (ale->type == ANIMTYPE_MASKLAYER) { + borderselect_masklayer_frames(ale->data, min, max, SELECT_ADD); + } else { ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); } @@ -467,7 +482,7 @@ static void columnselect_action_keys(bAnimContext *ac, short mode) /* loop through all of the keys and select additional keyframes * based on the keys found to be selected above */ - if (ac->datatype == ANIMCONT_GPENCIL) + if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE); else filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/); @@ -489,6 +504,8 @@ static void columnselect_action_keys(bAnimContext *ac, short mode) /* select elements with frame number matching cfraelem */ if (ale->type == ANIMTYPE_GPLAYER) select_gpencil_frame(ale->data, ce->cfra, SELECT_ADD); + else if (ale->type == ANIMTYPE_MASKLAYER) + select_mask_frame(ale->data, ce->cfra, SELECT_ADD); else ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); } @@ -755,7 +772,7 @@ static void actkeys_select_leftright(bAnimContext *ac, short leftright, short se } /* filter data */ - if (ac->datatype == ANIMCONT_GPENCIL) + if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); else filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS); @@ -772,6 +789,8 @@ static void actkeys_select_leftright(bAnimContext *ac, short leftright, short se } else if (ale->type == ANIMTYPE_GPLAYER) borderselect_gplayer_frames(ale->data, ked.f1, ked.f2, select_mode); + else if (ale->type == ANIMTYPE_MASKLAYER) + borderselect_masklayer_frames(ale->data, ked.f1, ked.f2, select_mode); else ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); } @@ -909,6 +928,8 @@ static void actkeys_mselect_single(bAnimContext *ac, bAnimListElem *ale, short s /* select the nominated keyframe on the given frame */ if (ale->type == ANIMTYPE_GPLAYER) select_gpencil_frame(ale->data, selx, select_mode); + else if (ale->type == ANIMTYPE_MASKLAYER) + select_mask_frame(ale->data, selx, select_mode); else ANIM_animchannel_keyframes_loop(&ked, ac->ads, ale, ok_cb, select_cb, NULL); } @@ -933,7 +954,7 @@ static void actkeys_mselect_column(bAnimContext *ac, short select_mode, float se /* loop through all of the keys and select additional keyframes * based on the keys found to be selected above */ - if (ac->datatype == ANIMCONT_GPENCIL) + if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ | ANIMFILTER_NODUPLIS); else filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); @@ -951,7 +972,9 @@ static void actkeys_mselect_column(bAnimContext *ac, short select_mode, float se /* select elements with frame number matching cfra */ if (ale->type == ANIMTYPE_GPLAYER) select_gpencil_frame(ale->key_data, selx, select_mode); - else + else if (ale->type == ANIMTYPE_MASKLAYER) + select_mask_frame(ale->key_data, selx, select_mode); + else ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); } @@ -1051,7 +1074,12 @@ static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_ bGPDlayer *gpl = (bGPDlayer *)ale->data; gpl_to_keylist(ads, gpl, &anim_keys); } - + else if (ale->type == ANIMTYPE_MASKLAYER) { + // TODO: why don't we just give masklayers key_data too? + MaskLayer *masklay = (MaskLayer *)ale->data; + mask_to_keylist(ads, masklay, &anim_keys); + } + /* start from keyframe at root of BST, traversing until we find one within the range that was clicked on */ for (ak = anim_keys.root; ak; ak = akn) { if (IN_RANGE(ak->cfra, rectf.xmin, rectf.xmax)) { @@ -1120,6 +1148,18 @@ static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_ //gpencil_layer_setactive(gpd, gpl); } } + else if (ac->datatype == ANIMCONT_MASK) { + /* deselect all other channels first */ + ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); + + /* Highlight GPencil Layer */ + if ((ale && ale->data) && (ale->type == ANIMTYPE_MASKLAYER)) { + MaskLayer *masklay = ale->data; + + masklay->flag |= MASK_LAYERFLAG_SELECT; + //gpencil_layer_setactive(gpd, gpl); + } + } } /* only select keyframes if we clicked on a valid channel and hit something */ diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 32a4d4ab1a3..fdc09c1bed0 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -555,7 +555,7 @@ struct wmKeyMap *transform_modal_keymap(struct wmKeyConfig *keyconf); /*********************** transform_conversions.c ********** */ struct ListBase; -void flushTransGPactionData(TransInfo *t); +void flushTransIntFrameActionData(TransInfo *t); void flushTransGraphData(TransInfo *t); void remake_graph_transdata(TransInfo *t, struct ListBase *anim_data); void flushTransUVs(TransInfo *t); diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index 1d4257ea3d3..ce65127c896 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -2797,6 +2797,99 @@ static void posttrans_gpd_clean (bGPdata *gpd) } } + +/* Called by special_aftertrans_update to make sure selected gp-frames replace + * any other gp-frames which may reside on that frame (that are not selected). + * It also makes sure sorted are still stored in chronological order after + * transform. + */ +static void posttrans_mask_clean(Mask *mask) +{ + MaskLayer *masklay; + + for (masklay = mask->masklayers.first; masklay; masklay= masklay->next) { + ListBase sel_buffer = {NULL, NULL}; + MaskLayerShape *masklay_shape, *masklay_shape_new; + MaskLayerShape *masklay_shape_sort, *masklay_shape_sort_new; + + /* loop 1: loop through and isolate selected gp-frames to buffer + * (these need to be sorted as they are isolated) + */ + for (masklay_shape= masklay->splines_shapes.first; masklay_shape; masklay_shape= masklay_shape_new) { + short added= 0; + masklay_shape_new= masklay_shape->next; + + if (masklay_shape->flag & GP_FRAME_SELECT) { + BLI_remlink(&masklay->splines_shapes, masklay_shape); + + /* find place to add them in buffer + * - go backwards as most frames will still be in order, + * so doing it this way will be faster + */ + for (masklay_shape_sort= sel_buffer.last; masklay_shape_sort; masklay_shape_sort= masklay_shape_sort->prev) { + /* if current (masklay_shape) occurs after this one in buffer, add! */ + if (masklay_shape_sort->frame < masklay_shape->frame) { + BLI_insertlinkafter(&sel_buffer, masklay_shape_sort, masklay_shape); + added= 1; + break; + } + } + if (added == 0) + BLI_addhead(&sel_buffer, masklay_shape); + } + } + + /* error checking: it is unlikely, but may be possible to have none selected */ + if (sel_buffer.first == NULL) + continue; + + /* if all were selected (i.e. masklay->splines_shapes is empty), then just transfer sel-buf over */ + if (masklay->splines_shapes.first == NULL) { + masklay->splines_shapes.first= sel_buffer.first; + masklay->splines_shapes.last= sel_buffer.last; + + continue; + } + + /* loop 2: remove duplicates of splines_shapes in buffers */ + for (masklay_shape= masklay->splines_shapes.first; masklay_shape && sel_buffer.first; masklay_shape= masklay_shape_new) { + masklay_shape_new= masklay_shape->next; + + /* loop through sel_buffer, emptying stuff from front of buffer if ok */ + for (masklay_shape_sort= sel_buffer.first; masklay_shape_sort && masklay_shape; masklay_shape_sort= masklay_shape_sort_new) { + masklay_shape_sort_new= masklay_shape_sort->next; + + /* if this buffer frame needs to go before current, add it! */ + if (masklay_shape_sort->frame < masklay_shape->frame) { + /* transfer buffer frame to splines_shapes list (before current) */ + BLI_remlink(&sel_buffer, masklay_shape_sort); + BLI_insertlinkbefore(&masklay->splines_shapes, masklay_shape, masklay_shape_sort); + } + /* if this buffer frame is on same frame, replace current with it and stop */ + else if (masklay_shape_sort->frame == masklay_shape->frame) { + /* transfer buffer frame to splines_shapes list (before current) */ + BLI_remlink(&sel_buffer, masklay_shape_sort); + BLI_insertlinkbefore(&masklay->splines_shapes, masklay_shape, masklay_shape_sort); + + /* get rid of current frame */ + BKE_mask_layer_shape_unlink(masklay, masklay_shape); + } + } + } + + /* if anything is still in buffer, append to end */ + for (masklay_shape_sort= sel_buffer.first; masklay_shape_sort; masklay_shape_sort= masklay_shape_sort_new) { + masklay_shape_sort_new= masklay_shape_sort->next; + + BLI_remlink(&sel_buffer, masklay_shape_sort); + BLI_addtail(&masklay->splines_shapes, masklay_shape_sort); + } + + /* NOTE: this is the only difference to grease pencil code above */ + BKE_mask_layer_shape_sort(masklay); + } +} + /* Called during special_aftertrans_update to make sure selected keyframes replace * any other keyframes which may reside on that frame (that is not selected). */ @@ -2936,6 +3029,27 @@ static int count_gplayer_frames(bGPDlayer *gpl, char side, float cfra) return count; } +/* fully select selected beztriples, but only include if it's on the right side of cfra */ +static int count_masklayer_frames(MaskLayer *masklay, char side, float cfra) +{ + MaskLayerShape *masklayer_shape; + int count = 0; + + if (masklay == NULL) + return count; + + /* only include points that occur on the right side of cfra */ + for (masklayer_shape= masklay->splines_shapes.first; masklayer_shape; masklayer_shape= masklayer_shape->next) { + if (masklayer_shape->flag & MASK_SHAPE_SELECT) { + if (FrameOnMouseSide(side, (float)masklayer_shape->frame, cfra)) + count++; + } + } + + return count; +} + + /* This function assigns the information to transdata */ static void TimeToTransData(TransData *td, float *time, AnimData *adt) { @@ -2998,7 +3112,7 @@ typedef struct tGPFtransdata { } tGPFtransdata; /* This function helps flush transdata written to tempdata into the gp-frames */ -void flushTransGPactionData(TransInfo *t) +void flushTransIntFrameActionData(TransInfo *t) { tGPFtransdata *tfd; int i; @@ -3049,6 +3163,35 @@ static int GPLayerToTransData (TransData *td, tGPFtransdata *tfd, bGPDlayer *gpl return count; } +/* refer to comment above #GPLayerToTransData, this is the same but for masks */ +static int MaskLayerToTransData(TransData *td, tGPFtransdata *tfd, MaskLayer *masklay, char side, float cfra) +{ + MaskLayerShape *masklay_shape; + int count= 0; + + /* check for select frames on right side of current frame */ + for (masklay_shape= masklay->splines_shapes.first; masklay_shape; masklay_shape= masklay_shape->next) { + if (masklay_shape->flag & MASK_SHAPE_SELECT) { + if (FrameOnMouseSide(side, (float)masklay_shape->frame, cfra)) { + /* memory is calloc'ed, so that should zero everything nicely for us */ + td->val= &tfd->val; + td->ival= (float)masklay_shape->frame; + + tfd->val= (float)masklay_shape->frame; + tfd->sdata= &masklay_shape->frame; + + /* advance td now */ + td++; + tfd++; + count++; + } + } + } + + return count; +} + + static void createTransActionData(bContext *C, TransInfo *t) { Scene *scene= t->scene; @@ -3069,7 +3212,7 @@ static void createTransActionData(bContext *C, TransInfo *t) return; /* filter data */ - if (ac.datatype == ANIMCONT_GPENCIL) + if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT); else filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/); @@ -3102,8 +3245,12 @@ static void createTransActionData(bContext *C, TransInfo *t) if (ale->type == ANIMTYPE_FCURVE) count += count_fcurve_keys(ale->key_data, t->frame_side, cfra); - else + else if (ale->type == ANIMTYPE_GPLAYER) count += count_gplayer_frames(ale->data, t->frame_side, cfra); + else if (ale->type == ANIMTYPE_MASKLAYER) + count += count_masklayer_frames(ale->data, t->frame_side, cfra); + else + BLI_assert(0); } /* stop if trying to build list if nothing selected */ @@ -3121,7 +3268,7 @@ static void createTransActionData(bContext *C, TransInfo *t) td= t->data; td2d = t->data2d; - if (ac.datatype == ANIMCONT_GPENCIL) { + if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { if (t->mode == TFM_TIME_SLIDE) { t->customData= MEM_callocN((sizeof(float)*2)+(sizeof(tGPFtransdata)*count), "TimeSlide + tGPFtransdata"); tfd= (tGPFtransdata *)((float *)(t->customData) + 2); @@ -3144,6 +3291,14 @@ static void createTransActionData(bContext *C, TransInfo *t) td += i; tfd += i; } + else if (ale->type == ANIMTYPE_MASKLAYER) { + MaskLayer *masklay = (MaskLayer *)ale->data; + int i; + + i = MaskLayerToTransData(td, tfd, masklay, t->frame_side, cfra); + td += i; + tfd += i; + } else { AnimData *adt= ANIM_nla_mapping_get(&ac, ale); FCurve *fcu= (FCurve *)ale->key_data; @@ -5004,6 +5159,26 @@ void special_aftertrans_update(bContext *C, TransInfo *t) } } } + else if (ac.datatype == ANIMCONT_MASK) { + /* remove duplicate frames and also make sure points are in order! */ + /* 3 cases here for curve cleanups: + * 1) NOTRANSKEYCULL on -> cleanup of duplicates shouldn't be done + * 2) canceled == 0 -> user confirmed the transform, so duplicates should be removed + * 3) canceled + duplicate -> user canceled the transform, but we made duplicates, so get rid of these + */ + if ((saction->flag & SACTION_NOTRANSKEYCULL)==0 && + ((canceled == 0) || (duplicate))) + { + Mask *mask; + + // XXX: BAD! this get gpencil datablocks directly from main db... + // but that's how this currently works :/ + for (mask = G.main->mask.first; mask; mask = mask->id.next) { + if (ID_REAL_USERS(mask)) + posttrans_mask_clean(mask); + } + } + } /* marker transform, not especially nice but we may want to move markers * at the same time as keyframes in the dope sheet. @@ -5027,7 +5202,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t) } /* make sure all F-Curves are set correctly */ - if (ac.datatype != ANIMCONT_GPENCIL) + if (!ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) ANIM_editkeyframes_refresh(&ac); /* clear flag that was set for time-slide drawing */ diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 3195fb0299d..e0920e323aa 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -350,9 +350,9 @@ static void recalcData_actedit(TransInfo *t) ANIM_animdata_context_getdata(&ac); /* perform flush */ - if (ac.datatype == ANIMCONT_GPENCIL) { + if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { /* flush transform values back to actual coordinates */ - flushTransGPactionData(t); + flushTransIntFrameActionData(t); } else { /* get animdata blocks visible in editor, assuming that these will be the ones where things changed */ diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index b61916f7bfa..08d5743bb88 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -632,7 +632,9 @@ typedef enum eAnimEdit_Context { /* editing of gpencil data */ SACTCONT_GPENCIL = 2, /* dopesheet (default) */ - SACTCONT_DOPESHEET = 3 + SACTCONT_DOPESHEET = 3, + /* mask */ + SACTCONT_MASK = 4 } eAnimEdit_Context; /* SpaceAction AutoSnap Settings (also used by other Animation Editors) */ diff --git a/source/blender/makesdna/DNA_mask_types.h b/source/blender/makesdna/DNA_mask_types.h index e4c27a57d85..20701f9adc0 100644 --- a/source/blender/makesdna/DNA_mask_types.h +++ b/source/blender/makesdna/DNA_mask_types.h @@ -48,6 +48,9 @@ typedef struct Mask { int masklay_tot; /* total number of mask layers */ int sfra, efra; /* frames, used by the sequencer */ + + int flag; /* for anim info */ + int pad; } Mask; typedef struct MaskParent { @@ -96,7 +99,7 @@ typedef struct MaskLayerShape { float *data; /* u coordinate along spline segment and weight of this point */ int tot_vert; /* to ensure no buffer overruns's: alloc size is (tot_vert * MASK_OBJECT_SHAPE_ELEM_SIZE) */ int frame; /* different flags of this point */ - char flag; + char flag; /* animation flag */ char pad[7]; } MaskLayerShape; @@ -125,9 +128,8 @@ typedef struct MaskLayer { char blend; char blend_flag; - //char flag; /* not used yet */ + char flag; /* for animation */ char restrictflag; /* matching 'Object' flag of the same name - eventually use in the outliner */ - char pad[1]; } MaskLayer; /* MaskParent->flag */ @@ -169,4 +171,21 @@ enum { MASK_BLENDFLAG_INVERT = (1 << 0) }; +/* masklay->flag */ +enum { + MASK_LAYERFLAG_LOCKED = (1 << 4), + MASK_LAYERFLAG_SELECT = (1 << 5) +}; + +/* masklay_shape->flag */ +enum { + MASK_SHAPE_SELECT = (1 << 0) +}; + + +/* mask->flag */ +enum { + MASK_ANIMF_EXPAND = (1 << 4) +}; + #endif // __DNA_MASK_TYPES_H__ diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index bbe9595029a..082c7c406e2 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -2229,6 +2229,7 @@ static void rna_def_space_dopesheet(BlenderRNA *brna) {SACTCONT_ACTION, "ACTION", ICON_OBJECT_DATA, "Action Editor", "Action Editor"}, {SACTCONT_SHAPEKEY, "SHAPEKEY", ICON_SHAPEKEY_DATA, "ShapeKey Editor", "ShapeKey Editor"}, {SACTCONT_GPENCIL, "GPENCIL", ICON_GREASEPENCIL, "Grease Pencil", "Grease Pencil"}, + {SACTCONT_MASK, "MASK", ICON_MOD_MASK, "Mask", "Mask Editor"}, {0, NULL, 0, NULL, NULL} }; |