diff --git a/scripts/startup/bl_ui/properties_data_grease_pencil.py b/scripts/startup/bl_ui/properties_data_grease_pencil.py index 1dc6cd79d0f..0832603dc22 100644 --- a/scripts/startup/bl_ui/properties_data_grease_pencil.py +++ b/scripts/startup/bl_ui/properties_data_grease_pencil.py @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later import bpy -from bpy.types import Panel, Menu +from bpy.types import Panel, Menu, UIList from rna_prop_ui import PropertyPanel @@ -27,6 +27,39 @@ class LayerDataButtonsPanel: return grease_pencil and grease_pencil.layers.active +class GREASE_PENCIL_UL_masks(UIList): + def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): + mask = item + if self.layout_type in {'DEFAULT', 'COMPACT'}: + row = layout.row(align=True) + row.prop(mask, "name", text="", emboss=False, icon_value=icon) + row.prop(mask, "invert", text="", emboss=False) + row.prop(mask, "hide", text="", emboss=False) + elif self.layout_type == 'GRID': + layout.alignment = 'CENTER' + layout.prop(mask, "name", text="", emboss=False, icon_value=icon) + + +class GREASE_PENCIL_MT_layer_mask_add(Menu): + bl_label = "Add Mask" + + def draw(self, context): + layout = self.layout + + grease_pencil = context.grease_pencil + active_layer = grease_pencil.layers.active + found = False + for layer in grease_pencil.layers: + if layer == active_layer or layer.name in active_layer.mask_layers: + continue + + found = True + layout.operator("grease_pencil.layer_mask_add", text=layer.name).name = layer.name + + if not found: + layout.label(text="No layers to add") + + class DATA_PT_context_grease_pencil(DataButtonsPanel, Panel): bl_label = "" bl_options = {'HIDE_HEADER'} @@ -106,6 +139,45 @@ class DATA_PT_grease_pencil_layers(DataButtonsPanel, Panel): row.prop(layer, "opacity", text="Opacity", slider=True) +class DATA_PT_grease_pencil_layer_masks(LayerDataButtonsPanel, Panel): + bl_label = "Masks" + bl_parent_id = "DATA_PT_grease_pencil_layers" + bl_options = {'DEFAULT_CLOSED'} + + def draw_header(self, context): + grease_pencil = context.grease_pencil + layer = grease_pencil.layers.active + + self.layout.prop(layer, "use_masks", text="") + + def draw(self, context): + layout = self.layout + grease_pencil = context.grease_pencil + layer = grease_pencil.layers.active + + layout = self.layout + layout.enabled = layer.use_masks + + if not layer: + return + + rows = 4 + row = layout.row() + col = row.column() + col.template_list("GREASE_PENCIL_UL_masks", "", layer, "mask_layers", layer.mask_layers, + "active_mask_index", rows=rows, sort_lock=True) + + col = row.column(align=True) + col.menu("GREASE_PENCIL_MT_layer_mask_add", icon='ADD', text="") + col.operator("grease_pencil.layer_mask_remove", icon='REMOVE', text="") + + col.separator() + + sub = col.column(align=True) + sub.operator("grease_pencil.layer_mask_reorder", icon='TRIA_UP', text="").direction = 'UP' + sub.operator("grease_pencil.layer_mask_reorder", icon='TRIA_DOWN', text="").direction = 'DOWN' + + class DATA_PT_grease_pencil_layer_transform(LayerDataButtonsPanel, Panel): bl_label = "Transform" bl_parent_id = "DATA_PT_grease_pencil_layers" @@ -164,8 +236,11 @@ class DATA_PT_grease_pencil_custom_props(DataButtonsPanel, PropertyPanel, Panel) classes = ( + GREASE_PENCIL_UL_masks, + GREASE_PENCIL_MT_layer_mask_add, DATA_PT_context_grease_pencil, DATA_PT_grease_pencil_layers, + DATA_PT_grease_pencil_layer_masks, DATA_PT_grease_pencil_layer_transform, DATA_PT_grease_pencil_layer_relations, DATA_PT_grease_pencil_custom_props, diff --git a/source/blender/blenkernel/BKE_grease_pencil.hh b/source/blender/blenkernel/BKE_grease_pencil.hh index 6b53f9f92d7..6e66dde8b3d 100644 --- a/source/blender/blenkernel/BKE_grease_pencil.hh +++ b/source/blender/blenkernel/BKE_grease_pencil.hh @@ -148,6 +148,7 @@ class Layer; bool is_selected() const; \ void set_selected(bool selected); \ bool use_onion_skinning() const; \ + bool use_masks() const; \ bool is_child_of(const LayerGroup &group) const; /* Implements the forwarding of the methods defined by #TREENODE_COMMON_METHODS. */ @@ -192,6 +193,10 @@ class Layer; { \ return this->as_node().use_onion_skinning(); \ } \ + inline bool class_name::use_masks() const \ + { \ + return this->as_node().use_masks(); \ + } \ inline bool class_name::is_child_of(const LayerGroup &group) const \ { \ return this->as_node().is_child_of(group); \ @@ -685,6 +690,11 @@ inline bool TreeNode::use_onion_skinning() const { return ((this->flag & GP_LAYER_TREE_NODE_USE_ONION_SKINNING) != 0); } +inline bool TreeNode::use_masks() const +{ + return ((this->flag & GP_LAYER_TREE_NODE_USE_MASKS) != 0) && + (!this->parent_group() || this->parent_group()->as_node().use_masks()); +} inline bool TreeNode::is_child_of(const LayerGroup &group) const { if (const LayerGroup *parent = this->parent_group()) { diff --git a/source/blender/blenkernel/intern/grease_pencil.cc b/source/blender/blenkernel/intern/grease_pencil.cc index 2c9aa5141c8..d6afd9bf203 100644 --- a/source/blender/blenkernel/intern/grease_pencil.cc +++ b/source/blender/blenkernel/intern/grease_pencil.cc @@ -11,6 +11,7 @@ #include "BKE_action.h" #include "BKE_anim_data.hh" +#include "BKE_animsys.h" #include "BKE_curves.hh" #include "BKE_customdata.hh" #include "BKE_deform.hh" @@ -628,14 +629,12 @@ LayerMask::LayerMask() LayerMask::LayerMask(StringRefNull name) : LayerMask() { - this->layer_name = BLI_strdup(name.c_str()); + this->layer_name = BLI_strdup_null(name.c_str()); } LayerMask::LayerMask(const LayerMask &other) : LayerMask() { - if (other.layer_name) { - this->layer_name = BLI_strdup(other.layer_name); - } + this->layer_name = BLI_strdup_null(other.layer_name); this->flag = other.flag; } @@ -675,6 +674,7 @@ Layer::Layer() this->viewlayername = nullptr; BLI_listbase_clear(&this->masks); + this->active_mask_index = 0; this->runtime = MEM_new(__func__); } @@ -688,7 +688,11 @@ Layer::Layer(const Layer &other) : Layer() { new (&this->base) TreeNode(other.base.wrap()); - /* TODO: duplicate masks. */ + LISTBASE_FOREACH (GreasePencilLayerMask *, other_mask, &other.masks) { + LayerMask *new_mask = MEM_new(__func__, *reinterpret_cast(other_mask)); + BLI_addtail(&this->masks, reinterpret_cast(new_mask)); + } + this->active_mask_index = other.active_mask_index; this->blend_mode = other.blend_mode; this->opacity = other.opacity; @@ -719,9 +723,9 @@ Layer::~Layer() MEM_SAFE_FREE(this->frames_storage.values); LISTBASE_FOREACH_MUTABLE (GreasePencilLayerMask *, mask, &this->masks) { - MEM_SAFE_FREE(mask->layer_name); - MEM_freeN(mask); + MEM_delete(reinterpret_cast(mask)); } + BLI_listbase_clear(&this->masks); MEM_SAFE_FREE(this->parsubstr); MEM_SAFE_FREE(this->viewlayername); @@ -2525,8 +2529,21 @@ void GreasePencil::rename_node(blender::bke::greasepencil::TreeNode &node, if (node.name() == new_name) { return; } - node.set_name(node.is_layer() ? unique_layer_name(*this, new_name) : - unique_layer_group_name(*this, new_name)); + std::string old_name = node.name(); + if (node.is_layer()) { + node.set_name(unique_layer_name(*this, new_name)); + BKE_animdata_fix_paths_rename_all(&this->id, "layers", old_name.c_str(), node.name().c_str()); + for (bke::greasepencil::Layer *layer : this->layers_for_write()) { + LISTBASE_FOREACH (GreasePencilLayerMask *, mask, &layer->masks) { + if (STREQ(mask->layer_name, old_name.c_str())) { + mask->layer_name = BLI_strdup(node.name().c_str()); + } + } + } + } + else if (node.is_group()) { + node.set_name(unique_layer_group_name(*this, new_name)); + } } static void shrink_customdata(CustomData &data, const int index_to_remove, const int size) diff --git a/source/blender/blenkernel/intern/grease_pencil_convert_legacy.cc b/source/blender/blenkernel/intern/grease_pencil_convert_legacy.cc index be355156de3..8cad1af001b 100644 --- a/source/blender/blenkernel/intern/grease_pencil_convert_legacy.cc +++ b/source/blender/blenkernel/intern/grease_pencil_convert_legacy.cc @@ -426,6 +426,8 @@ void legacy_gpencil_to_grease_pencil(Main &bmain, GreasePencil &grease_pencil, b SET_FLAG_FROM_TEST(new_layer.base.flag, (gpl->onion_flag & GP_LAYER_ONIONSKIN), GP_LAYER_TREE_NODE_USE_ONION_SKINNING); + SET_FLAG_FROM_TEST( + new_layer.base.flag, (gpl->flag & GP_LAYER_USE_MASK), GP_LAYER_TREE_NODE_USE_MASKS); new_layer.blend_mode = int8_t(gpl->blend_mode); @@ -440,7 +442,7 @@ void legacy_gpencil_to_grease_pencil(Main &bmain, GreasePencil &grease_pencil, b /* Convert the layer masks. */ LISTBASE_FOREACH (bGPDlayer_Mask *, mask, &gpl->mask_layers) { - LayerMask *new_mask = MEM_new(mask->name); + LayerMask *new_mask = MEM_new(__func__, mask->name); new_mask->flag = mask->flag; BLI_addtail(&new_layer.masks, new_mask); } diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_layers.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_layers.cc index c72be8e78e8..aefe1b71dd4 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_layers.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_layers.cc @@ -524,6 +524,169 @@ static void GREASE_PENCIL_OT_layer_duplicate(wmOperatorType *ot) /* properties */ RNA_def_boolean(ot->srna, "empty_keyframes", false, "Empty Keyframes", "Add Empty Keyframes"); } + +static int grease_pencil_layer_mask_add_exec(bContext *C, wmOperator *op) +{ + using namespace ::blender::bke::greasepencil; + Object *object = CTX_data_active_object(C); + GreasePencil &grease_pencil = *static_cast(object->data); + + if (!grease_pencil.has_active_layer()) { + return OPERATOR_CANCELLED; + } + Layer &active_layer = *grease_pencil.get_active_layer(); + + int mask_name_length; + char *mask_name = RNA_string_get_alloc(op->ptr, "name", nullptr, 0, &mask_name_length); + BLI_SCOPED_DEFER([&] { MEM_SAFE_FREE(mask_name); }); + + if (TreeNode *node = grease_pencil.find_node_by_name(mask_name)) { + if (grease_pencil.is_layer_active(&node->as_layer())) { + BKE_report(op->reports, RPT_ERROR, "Cannot add active layer as mask"); + return OPERATOR_CANCELLED; + } + + if (BLI_findstring(&active_layer.masks, + mask_name, + offsetof(GreasePencilLayerMask, layer_name)) != nullptr) + { + BKE_report(op->reports, RPT_ERROR, "Layer already added"); + return OPERATOR_CANCELLED; + } + + LayerMask *new_mask = MEM_new(__func__, mask_name); + BLI_addtail(&active_layer.masks, reinterpret_cast(new_mask)); + /* Make the newly added mask active. */ + active_layer.active_mask_index = BLI_listbase_count(&active_layer.masks) - 1; + } + else { + BKE_report(op->reports, RPT_ERROR, "Unable to find layer to add"); + return OPERATOR_CANCELLED; + } + + DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_SELECTED, &grease_pencil); + + return OPERATOR_FINISHED; +} + +static void GREASE_PENCIL_OT_layer_mask_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add New Mask Layer"; + ot->idname = "GREASE_PENCIL_OT_layer_mask_add"; + ot->description = "Add new layer as masking"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = grease_pencil_layer_mask_add_exec; + ot->poll = active_grease_pencil_layer_poll; + + /* properties */ + RNA_def_string(ot->srna, "name", nullptr, 0, "Layer", "Name of the layer"); +} + +static int grease_pencil_layer_mask_remove_exec(bContext *C, wmOperator * /*op*/) +{ + using namespace ::blender::bke::greasepencil; + Object *object = CTX_data_active_object(C); + GreasePencil &grease_pencil = *static_cast(object->data); + + if (!grease_pencil.has_active_layer()) { + return OPERATOR_CANCELLED; + } + + Layer &active_layer = *grease_pencil.get_active_layer(); + if (GreasePencilLayerMask *mask = reinterpret_cast( + BLI_findlink(&active_layer.masks, active_layer.active_mask_index))) + { + BLI_remlink(&active_layer.masks, mask); + MEM_delete(reinterpret_cast(mask)); + active_layer.active_mask_index = std::max(active_layer.active_mask_index - 1, 0); + } + else { + return OPERATOR_CANCELLED; + } + + DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_SELECTED, &grease_pencil); + + return OPERATOR_FINISHED; +} + +static void GREASE_PENCIL_OT_layer_mask_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Mask Layer"; + ot->idname = "GREASE_PENCIL_OT_layer_mask_remove"; + ot->description = "Remove Layer Mask"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = grease_pencil_layer_mask_remove_exec; + ot->poll = active_grease_pencil_layer_poll; +} + +enum class LayerMaskMoveDirection : int8_t { Up = -1, Down = 1 }; + +static int grease_pencil_layer_mask_reorder_exec(bContext *C, wmOperator *op) +{ + using namespace ::blender::bke::greasepencil; + Object *object = CTX_data_active_object(C); + GreasePencil &grease_pencil = *static_cast(object->data); + + if (!grease_pencil.has_active_layer()) { + return OPERATOR_CANCELLED; + } + Layer &active_layer = *grease_pencil.get_active_layer(); + const int direction = RNA_enum_get(op->ptr, "direction"); + + bool changed = false; + if (GreasePencilLayerMask *mask = reinterpret_cast( + BLI_findlink(&active_layer.masks, active_layer.active_mask_index))) + { + if (BLI_listbase_link_move(&active_layer.masks, mask, direction)) { + active_layer.active_mask_index = std::max(active_layer.active_mask_index + direction, 0); + changed = true; + } + } + else { + return OPERATOR_CANCELLED; + } + + if (changed) { + DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_SELECTED, &grease_pencil); + } + + return OPERATOR_FINISHED; +} + +static void GREASE_PENCIL_OT_layer_mask_reorder(wmOperatorType *ot) +{ + static const EnumPropertyItem enum_direction[] = { + {int(LayerMaskMoveDirection::Up), "UP", 0, "Up", ""}, + {int(LayerMaskMoveDirection::Down), "DOWN", 0, "Down", ""}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + /* identifiers */ + ot->name = "Reorder Grease Pencil Layer Mask"; + ot->idname = "GREASE_PENCIL_OT_layer_mask_reorder"; + ot->description = "Reorder the active Grease Pencil mask layer up/down in the list"; + + /* api callbacks */ + ot->exec = grease_pencil_layer_mask_reorder_exec; + ot->poll = active_grease_pencil_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "direction", enum_direction, 0, "Direction", ""); +} + } // namespace blender::ed::greasepencil void ED_operatortypes_grease_pencil_layers() @@ -540,4 +703,8 @@ void ED_operatortypes_grease_pencil_layers() WM_operatortype_append(GREASE_PENCIL_OT_layer_duplicate); WM_operatortype_append(GREASE_PENCIL_OT_layer_group_add); + + WM_operatortype_append(GREASE_PENCIL_OT_layer_mask_add); + WM_operatortype_append(GREASE_PENCIL_OT_layer_mask_remove); + WM_operatortype_append(GREASE_PENCIL_OT_layer_mask_reorder); } diff --git a/source/blender/editors/interface/interface_template_grease_pencil_layer_tree.cc b/source/blender/editors/interface/interface_template_grease_pencil_layer_tree.cc index d4acbd2c305..3ab5af240ce 100644 --- a/source/blender/editors/interface/interface_template_grease_pencil_layer_tree.cc +++ b/source/blender/editors/interface/interface_template_grease_pencil_layer_tree.cc @@ -255,6 +255,27 @@ class LayerViewItem : public AbstractTreeViewItem { PointerRNA layer_ptr = RNA_pointer_create(&grease_pencil_.id, &RNA_GreasePencilLayer, &layer_); uiBlock *block = uiLayoutGetBlock(&row); + + const int icon = (layer_.base.flag & GP_LAYER_TREE_NODE_USE_MASKS) != 0 ? ICON_CLIPUV_DEHLT : + ICON_CLIPUV_HLT; + but = uiDefIconButR(block, + UI_BTYPE_ICON_TOGGLE, + 0, + icon, + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y, + &layer_ptr, + "use_masks", + 0, + 0.0f, + 0.0f, + nullptr); + if (layer_.parent_group().use_masks()) { + UI_but_flag_enable(but, UI_BUT_INACTIVE); + } + but = uiDefIconButR(block, UI_BTYPE_ICON_TOGGLE, 0, @@ -359,6 +380,9 @@ class LayerGroupViewItem : public AbstractTreeViewItem { PointerRNA group_ptr = RNA_pointer_create( &grease_pencil_.id, &RNA_GreasePencilLayerGroup, &group_); + const int icon = (group_.base.flag & GP_LAYER_TREE_NODE_USE_MASKS) != 0 ? ICON_CLIPUV_DEHLT : + ICON_CLIPUV_HLT; + uiItemR(&row, &group_ptr, "use_masks", UI_ITEM_R_ICON_ONLY, nullptr, icon); uiItemR(&row, &group_ptr, "hide", UI_ITEM_R_ICON_ONLY, nullptr, ICON_NONE); uiItemR(&row, &group_ptr, "lock", UI_ITEM_R_ICON_ONLY, nullptr, ICON_NONE); } diff --git a/source/blender/makesdna/DNA_grease_pencil_types.h b/source/blender/makesdna/DNA_grease_pencil_types.h index fda24c0ab1f..abde76adbbd 100644 --- a/source/blender/makesdna/DNA_grease_pencil_types.h +++ b/source/blender/makesdna/DNA_grease_pencil_types.h @@ -240,6 +240,7 @@ typedef enum GreasePencilLayerTreeNodeFlag { GP_LAYER_TREE_NODE_USE_LIGHTS = (1 << 4), GP_LAYER_TREE_NODE_USE_ONION_SKINNING = (1 << 5), GP_LAYER_TREE_NODE_EXPANDED = (1 << 6), + GP_LAYER_TREE_NODE_USE_MASKS = (1 << 7), } GreasePencilLayerTreeNodeFlag; struct GreasePencilLayerTreeGroup; @@ -292,6 +293,8 @@ typedef struct GreasePencilLayer { * List of `GreasePencilLayerMask`. */ ListBase masks; + int active_mask_index; + char _pad2[4]; /** * Layer parent object. Can be an armature in which case the `parsubstr` is the bone name. */ @@ -302,7 +305,7 @@ typedef struct GreasePencilLayer { * Use the functions is the `bke::greasepencil::Layer` class instead. */ float translation[3], rotation[3], scale[3]; - char _pad2[4]; + char _pad3[4]; /** Name of the view layer used to filter render output. */ char *viewlayername; /** diff --git a/source/blender/makesrna/intern/rna_grease_pencil.cc b/source/blender/makesrna/intern/rna_grease_pencil.cc index cac331333cf..b324f76e6ce 100644 --- a/source/blender/makesrna/intern/rna_grease_pencil.cc +++ b/source/blender/makesrna/intern/rna_grease_pencil.cc @@ -64,6 +64,60 @@ static void rna_grease_pencil_dependency_update(Main *bmain, Scene * /*scene*/, WM_main_add_notifier(NC_GPENCIL | NA_EDITED, rna_grease_pencil(ptr)); } +static void rna_grease_pencil_layer_mask_name_get(PointerRNA *ptr, char *dst) +{ + using namespace blender; + GreasePencilLayerMask *mask = static_cast(ptr->data); + if (mask->layer_name != nullptr) { + strcpy(dst, mask->layer_name); + } + else { + dst[0] = '\0'; + } +} + +static int rna_grease_pencil_layer_mask_name_length(PointerRNA *ptr) +{ + using namespace blender; + GreasePencilLayerMask *mask = static_cast(ptr->data); + if (mask->layer_name != nullptr) { + return strlen(mask->layer_name); + } + return 0; +} + +static void rna_grease_pencil_layer_mask_name_set(PointerRNA *ptr, const char *value) +{ + using namespace blender; + GreasePencil *grease_pencil = rna_grease_pencil(ptr); + GreasePencilLayerMask *mask = static_cast(ptr->data); + + const std::string oldname(mask->layer_name); + if (bke::greasepencil::TreeNode *node = grease_pencil->find_node_by_name(oldname)) { + grease_pencil->rename_node(*node, value); + } +} + +static int rna_grease_pencil_active_mask_index_get(PointerRNA *ptr) +{ + GreasePencilLayer *layer = static_cast(ptr->data); + return layer->active_mask_index; +} + +static void rna_grease_pencil_active_mask_index_set(PointerRNA *ptr, int value) +{ + GreasePencilLayer *layer = static_cast(ptr->data); + layer->active_mask_index = value; +} + +static void rna_grease_pencil_active_mask_index_range( + PointerRNA *ptr, int *min, int *max, int * /*softmin*/, int * /*softmax*/) +{ + GreasePencilLayer *layer = static_cast(ptr->data); + *min = 0; + *max = max_ii(0, BLI_listbase_count(&layer->masks) - 1); +} + static void rna_iterator_grease_pencil_layers_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { @@ -229,6 +283,60 @@ static int rna_iterator_grease_pencil_layer_groups_length(PointerRNA *ptr) #else +static void rna_def_grease_pencil_layers_mask_api(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + PropertyRNA *prop; + + RNA_def_property_srna(cprop, "GreasePencilLayerMasks"); + srna = RNA_def_struct(brna, "GreasePencilLayerMasks", nullptr); + RNA_def_struct_sdna(srna, "GreasePencilLayer"); + RNA_def_struct_ui_text( + srna, "Grease Pencil Mask Layers", "Collection of grease pencil masking layers"); + + prop = RNA_def_property(srna, "active_mask_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_int_funcs(prop, + "rna_grease_pencil_active_mask_index_get", + "rna_grease_pencil_active_mask_index_set", + "rna_grease_pencil_active_mask_index_range"); + RNA_def_property_ui_text(prop, "Active Layer Mask Index", "Active index in layer mask array"); +} + +static void rna_def_grease_pencil_layer_mask(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "GreasePencilLayerMask", nullptr); + RNA_def_struct_sdna(srna, "GreasePencilLayerMask"); + RNA_def_struct_ui_text(srna, "Grease Pencil Masking Layers", "List of Mask Layers"); + // RNA_def_struct_path_func(srna, "rna_GreasePencilLayerMask_path"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Layer", "Mask layer name"); + RNA_def_property_string_sdna(prop, nullptr, "layer_name"); + RNA_def_property_string_funcs(prop, + "rna_grease_pencil_layer_mask_name_get", + "rna_grease_pencil_layer_mask_name_length", + "rna_grease_pencil_layer_mask_name_set"); + RNA_def_struct_name_property(srna, prop); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA | NA_RENAME, nullptr); + + prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", GP_LAYER_MASK_HIDE); + RNA_def_property_ui_icon(prop, ICON_HIDE_OFF, -1); + RNA_def_property_ui_text(prop, "Hide", "Set mask Visibility"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update"); + + prop = RNA_def_property(srna, "invert", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", GP_LAYER_MASK_INVERT); + RNA_def_property_ui_icon(prop, ICON_SELECT_INTERSECT, 1); + RNA_def_property_ui_text(prop, "Invert", "Invert mask"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update"); +} + static void rna_def_grease_pencil_layer(BlenderRNA *brna) { StructRNA *srna; @@ -260,6 +368,13 @@ static void rna_def_grease_pencil_layer(BlenderRNA *brna) RNA_def_struct_name_property(srna, prop); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA | NA_RENAME, "rna_grease_pencil_update"); + /* Mask Layers */ + prop = RNA_def_property(srna, "mask_layers", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, nullptr, "masks", nullptr); + RNA_def_property_struct_type(prop, "GreasePencilLayerMask"); + RNA_def_property_ui_text(prop, "Masks", "List of Masking Layers"); + rna_def_grease_pencil_layers_mask_api(brna, prop); + /* Visibility */ prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( @@ -291,6 +406,16 @@ static void rna_def_grease_pencil_layer(BlenderRNA *brna) prop, "Onion Skinning", "Display onion skins before and after the current frame"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update"); + /* Use Masks. */ + prop = RNA_def_property(srna, "use_masks", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, "GreasePencilLayerTreeNode", "flag", GP_LAYER_TREE_NODE_USE_MASKS); + RNA_def_property_ui_text( + prop, + "Use Masks", + "The visibility of drawings on this layer is affected by the layers in its masks list"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update"); + /* pass index for compositing and modifiers */ prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_ui_text(prop, "Pass Index", "Index number for the \"Layer Index\" pass"); @@ -407,6 +532,16 @@ static void rna_def_grease_pencil_layer_group(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Locked", "Protect group from further editing and/or frame changes"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update"); + + /* Use Masks. */ + prop = RNA_def_property(srna, "use_masks", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, "GreasePencilLayerTreeNode", "flag", GP_LAYER_TREE_NODE_USE_MASKS); + RNA_def_property_ui_text(prop, + "Use Masks", + "The visibility of drawings in the layers in this group is affected by " + "the layers in the masks lists"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update"); } static void rna_def_grease_pencil_data(BlenderRNA *brna) @@ -483,6 +618,7 @@ void RNA_def_grease_pencil(BlenderRNA *brna) { rna_def_grease_pencil_data(brna); rna_def_grease_pencil_layer(brna); + rna_def_grease_pencil_layer_mask(brna); rna_def_grease_pencil_layer_group(brna); }