GPv3: Add layer masks operators and UI #119433

Merged
Falk David merged 8 commits from filedescriptor/blender:gpv3-layer-masks into main 2024-03-14 14:07:49 +01:00
8 changed files with 446 additions and 12 deletions

View File

@ -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'}
@ -101,6 +134,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"
@ -159,8 +231,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,

View File

@ -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) &&
filedescriptor marked this conversation as resolved Outdated

Is this flag the correct way around? Looks like it checks that GP_LAYER_TREE_NODE_USE_MASKS is not set.

Is this flag the correct way around? Looks like it checks that `GP_LAYER_TREE_NODE_USE_MASKS` is __not__ set.

Good catch! Thank you.

Good catch! Thank you.
(!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()) {

View File

@ -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<LayerRuntime>(__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<LayerMask>(__func__, *reinterpret_cast<LayerMask *>(other_mask));
BLI_addtail(&this->masks, reinterpret_cast<GreasePencilLayerMask *>(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<LayerMask *>(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)

View File

@ -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<LayerMask>(mask->name);
LayerMask *new_mask = MEM_new<LayerMask>(__func__, mask->name);
new_mask->flag = mask->flag;
BLI_addtail(&new_layer.masks, new_mask);
}

View File

@ -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<GreasePencil *>(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<LayerMask>(__func__, mask_name);
BLI_addtail(&active_layer.masks, reinterpret_cast<GreasePencilLayerMask *>(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<GreasePencil *>(object->data);
if (!grease_pencil.has_active_layer()) {
return OPERATOR_CANCELLED;
}
Layer &active_layer = *grease_pencil.get_active_layer();
if (GreasePencilLayerMask *mask = reinterpret_cast<GreasePencilLayerMask *>(
BLI_findlink(&active_layer.masks, active_layer.active_mask_index)))
{
BLI_remlink(&active_layer.masks, mask);
MEM_delete(reinterpret_cast<LayerMask *>(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<GreasePencil *>(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<GreasePencilLayerMask *>(
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);
}

View File

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

View File

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

View File

@ -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<GreasePencilLayerMask *>(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<GreasePencilLayerMask *>(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<GreasePencilLayerMask *>(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<GreasePencilLayer *>(ptr->data);
return layer->active_mask_index;
}
static void rna_grease_pencil_active_mask_index_set(PointerRNA *ptr, int value)
{
GreasePencilLayer *layer = static_cast<GreasePencilLayer *>(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<GreasePencilLayer *>(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;
@ -251,6 +359,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(
@ -282,6 +397,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");
@ -392,6 +517,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)
@ -468,6 +603,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);
}