GPv3: Add layer masks operators and UI #119433
|
@ -2,7 +2,7 @@
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
import bpy
|
import bpy
|
||||||
from bpy.types import Panel, Menu
|
from bpy.types import Panel, Menu, UIList
|
||||||
from rna_prop_ui import PropertyPanel
|
from rna_prop_ui import PropertyPanel
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,6 +27,39 @@ class LayerDataButtonsPanel:
|
||||||
return grease_pencil and grease_pencil.layers.active
|
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):
|
class DATA_PT_context_grease_pencil(DataButtonsPanel, Panel):
|
||||||
bl_label = ""
|
bl_label = ""
|
||||||
bl_options = {'HIDE_HEADER'}
|
bl_options = {'HIDE_HEADER'}
|
||||||
|
@ -101,6 +134,45 @@ class DATA_PT_grease_pencil_layers(DataButtonsPanel, Panel):
|
||||||
row.prop(layer, "opacity", text="Opacity", slider=True)
|
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):
|
class DATA_PT_grease_pencil_layer_transform(LayerDataButtonsPanel, Panel):
|
||||||
bl_label = "Transform"
|
bl_label = "Transform"
|
||||||
bl_parent_id = "DATA_PT_grease_pencil_layers"
|
bl_parent_id = "DATA_PT_grease_pencil_layers"
|
||||||
|
@ -159,8 +231,11 @@ class DATA_PT_grease_pencil_custom_props(DataButtonsPanel, PropertyPanel, Panel)
|
||||||
|
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
|
GREASE_PENCIL_UL_masks,
|
||||||
|
GREASE_PENCIL_MT_layer_mask_add,
|
||||||
DATA_PT_context_grease_pencil,
|
DATA_PT_context_grease_pencil,
|
||||||
DATA_PT_grease_pencil_layers,
|
DATA_PT_grease_pencil_layers,
|
||||||
|
DATA_PT_grease_pencil_layer_masks,
|
||||||
DATA_PT_grease_pencil_layer_transform,
|
DATA_PT_grease_pencil_layer_transform,
|
||||||
DATA_PT_grease_pencil_layer_relations,
|
DATA_PT_grease_pencil_layer_relations,
|
||||||
DATA_PT_grease_pencil_custom_props,
|
DATA_PT_grease_pencil_custom_props,
|
||||||
|
|
|
@ -148,6 +148,7 @@ class Layer;
|
||||||
bool is_selected() const; \
|
bool is_selected() const; \
|
||||||
void set_selected(bool selected); \
|
void set_selected(bool selected); \
|
||||||
bool use_onion_skinning() const; \
|
bool use_onion_skinning() const; \
|
||||||
|
bool use_masks() const; \
|
||||||
bool is_child_of(const LayerGroup &group) const;
|
bool is_child_of(const LayerGroup &group) const;
|
||||||
|
|
||||||
/* Implements the forwarding of the methods defined by #TREENODE_COMMON_METHODS. */
|
/* Implements the forwarding of the methods defined by #TREENODE_COMMON_METHODS. */
|
||||||
|
@ -192,6 +193,10 @@ class Layer;
|
||||||
{ \
|
{ \
|
||||||
return this->as_node().use_onion_skinning(); \
|
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 \
|
inline bool class_name::is_child_of(const LayerGroup &group) const \
|
||||||
{ \
|
{ \
|
||||||
return this->as_node().is_child_of(group); \
|
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);
|
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
|
|||||||
|
(!this->parent_group() || this->parent_group()->as_node().use_masks());
|
||||||
|
}
|
||||||
inline bool TreeNode::is_child_of(const LayerGroup &group) const
|
inline bool TreeNode::is_child_of(const LayerGroup &group) const
|
||||||
{
|
{
|
||||||
if (const LayerGroup *parent = this->parent_group()) {
|
if (const LayerGroup *parent = this->parent_group()) {
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include "BKE_action.h"
|
#include "BKE_action.h"
|
||||||
#include "BKE_anim_data.hh"
|
#include "BKE_anim_data.hh"
|
||||||
|
#include "BKE_animsys.h"
|
||||||
#include "BKE_curves.hh"
|
#include "BKE_curves.hh"
|
||||||
#include "BKE_customdata.hh"
|
#include "BKE_customdata.hh"
|
||||||
#include "BKE_deform.hh"
|
#include "BKE_deform.hh"
|
||||||
|
@ -628,14 +629,12 @@ LayerMask::LayerMask()
|
||||||
|
|
||||||
LayerMask::LayerMask(StringRefNull name) : 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()
|
LayerMask::LayerMask(const LayerMask &other) : LayerMask()
|
||||||
{
|
{
|
||||||
if (other.layer_name) {
|
this->layer_name = BLI_strdup_null(other.layer_name);
|
||||||
this->layer_name = BLI_strdup(other.layer_name);
|
|
||||||
}
|
|
||||||
this->flag = other.flag;
|
this->flag = other.flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -675,6 +674,7 @@ Layer::Layer()
|
||||||
this->viewlayername = nullptr;
|
this->viewlayername = nullptr;
|
||||||
|
|
||||||
BLI_listbase_clear(&this->masks);
|
BLI_listbase_clear(&this->masks);
|
||||||
|
this->active_mask_index = 0;
|
||||||
|
|
||||||
this->runtime = MEM_new<LayerRuntime>(__func__);
|
this->runtime = MEM_new<LayerRuntime>(__func__);
|
||||||
}
|
}
|
||||||
|
@ -688,7 +688,11 @@ Layer::Layer(const Layer &other) : Layer()
|
||||||
{
|
{
|
||||||
new (&this->base) TreeNode(other.base.wrap());
|
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->blend_mode = other.blend_mode;
|
||||||
this->opacity = other.opacity;
|
this->opacity = other.opacity;
|
||||||
|
@ -719,9 +723,9 @@ Layer::~Layer()
|
||||||
MEM_SAFE_FREE(this->frames_storage.values);
|
MEM_SAFE_FREE(this->frames_storage.values);
|
||||||
|
|
||||||
LISTBASE_FOREACH_MUTABLE (GreasePencilLayerMask *, mask, &this->masks) {
|
LISTBASE_FOREACH_MUTABLE (GreasePencilLayerMask *, mask, &this->masks) {
|
||||||
MEM_SAFE_FREE(mask->layer_name);
|
MEM_delete(reinterpret_cast<LayerMask *>(mask));
|
||||||
MEM_freeN(mask);
|
|
||||||
}
|
}
|
||||||
|
BLI_listbase_clear(&this->masks);
|
||||||
|
|
||||||
MEM_SAFE_FREE(this->parsubstr);
|
MEM_SAFE_FREE(this->parsubstr);
|
||||||
MEM_SAFE_FREE(this->viewlayername);
|
MEM_SAFE_FREE(this->viewlayername);
|
||||||
|
@ -2525,8 +2529,21 @@ void GreasePencil::rename_node(blender::bke::greasepencil::TreeNode &node,
|
||||||
if (node.name() == new_name) {
|
if (node.name() == new_name) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
node.set_name(node.is_layer() ? unique_layer_name(*this, new_name) :
|
std::string old_name = node.name();
|
||||||
unique_layer_group_name(*this, new_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)
|
static void shrink_customdata(CustomData &data, const int index_to_remove, const int size)
|
||||||
|
|
|
@ -426,6 +426,8 @@ void legacy_gpencil_to_grease_pencil(Main &bmain, GreasePencil &grease_pencil, b
|
||||||
SET_FLAG_FROM_TEST(new_layer.base.flag,
|
SET_FLAG_FROM_TEST(new_layer.base.flag,
|
||||||
(gpl->onion_flag & GP_LAYER_ONIONSKIN),
|
(gpl->onion_flag & GP_LAYER_ONIONSKIN),
|
||||||
GP_LAYER_TREE_NODE_USE_ONION_SKINNING);
|
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);
|
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. */
|
/* Convert the layer masks. */
|
||||||
LISTBASE_FOREACH (bGPDlayer_Mask *, mask, &gpl->mask_layers) {
|
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;
|
new_mask->flag = mask->flag;
|
||||||
BLI_addtail(&new_layer.masks, new_mask);
|
BLI_addtail(&new_layer.masks, new_mask);
|
||||||
}
|
}
|
||||||
|
|
|
@ -524,6 +524,169 @@ static void GREASE_PENCIL_OT_layer_duplicate(wmOperatorType *ot)
|
||||||
/* properties */
|
/* properties */
|
||||||
RNA_def_boolean(ot->srna, "empty_keyframes", false, "Empty Keyframes", "Add Empty Keyframes");
|
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
|
} // namespace blender::ed::greasepencil
|
||||||
|
|
||||||
void ED_operatortypes_grease_pencil_layers()
|
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_duplicate);
|
||||||
|
|
||||||
WM_operatortype_append(GREASE_PENCIL_OT_layer_group_add);
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -255,6 +255,27 @@ class LayerViewItem : public AbstractTreeViewItem {
|
||||||
PointerRNA layer_ptr = RNA_pointer_create(&grease_pencil_.id, &RNA_GreasePencilLayer, &layer_);
|
PointerRNA layer_ptr = RNA_pointer_create(&grease_pencil_.id, &RNA_GreasePencilLayer, &layer_);
|
||||||
|
|
||||||
uiBlock *block = uiLayoutGetBlock(&row);
|
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,
|
but = uiDefIconButR(block,
|
||||||
UI_BTYPE_ICON_TOGGLE,
|
UI_BTYPE_ICON_TOGGLE,
|
||||||
0,
|
0,
|
||||||
|
@ -359,6 +380,9 @@ class LayerGroupViewItem : public AbstractTreeViewItem {
|
||||||
PointerRNA group_ptr = RNA_pointer_create(
|
PointerRNA group_ptr = RNA_pointer_create(
|
||||||
&grease_pencil_.id, &RNA_GreasePencilLayerGroup, &group_);
|
&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, "hide", UI_ITEM_R_ICON_ONLY, nullptr, ICON_NONE);
|
||||||
uiItemR(&row, &group_ptr, "lock", UI_ITEM_R_ICON_ONLY, nullptr, ICON_NONE);
|
uiItemR(&row, &group_ptr, "lock", UI_ITEM_R_ICON_ONLY, nullptr, ICON_NONE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,6 +240,7 @@ typedef enum GreasePencilLayerTreeNodeFlag {
|
||||||
GP_LAYER_TREE_NODE_USE_LIGHTS = (1 << 4),
|
GP_LAYER_TREE_NODE_USE_LIGHTS = (1 << 4),
|
||||||
GP_LAYER_TREE_NODE_USE_ONION_SKINNING = (1 << 5),
|
GP_LAYER_TREE_NODE_USE_ONION_SKINNING = (1 << 5),
|
||||||
GP_LAYER_TREE_NODE_EXPANDED = (1 << 6),
|
GP_LAYER_TREE_NODE_EXPANDED = (1 << 6),
|
||||||
|
GP_LAYER_TREE_NODE_USE_MASKS = (1 << 7),
|
||||||
} GreasePencilLayerTreeNodeFlag;
|
} GreasePencilLayerTreeNodeFlag;
|
||||||
|
|
||||||
struct GreasePencilLayerTreeGroup;
|
struct GreasePencilLayerTreeGroup;
|
||||||
|
@ -292,6 +293,8 @@ typedef struct GreasePencilLayer {
|
||||||
* List of `GreasePencilLayerMask`.
|
* List of `GreasePencilLayerMask`.
|
||||||
*/
|
*/
|
||||||
ListBase masks;
|
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.
|
* 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.
|
* Use the functions is the `bke::greasepencil::Layer` class instead.
|
||||||
*/
|
*/
|
||||||
float translation[3], rotation[3], scale[3];
|
float translation[3], rotation[3], scale[3];
|
||||||
char _pad2[4];
|
char _pad3[4];
|
||||||
/** Name of the view layer used to filter render output. */
|
/** Name of the view layer used to filter render output. */
|
||||||
char *viewlayername;
|
char *viewlayername;
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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));
|
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,
|
static void rna_iterator_grease_pencil_layers_begin(CollectionPropertyIterator *iter,
|
||||||
PointerRNA *ptr)
|
PointerRNA *ptr)
|
||||||
{
|
{
|
||||||
|
@ -229,6 +283,60 @@ static int rna_iterator_grease_pencil_layer_groups_length(PointerRNA *ptr)
|
||||||
|
|
||||||
#else
|
#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)
|
static void rna_def_grease_pencil_layer(BlenderRNA *brna)
|
||||||
{
|
{
|
||||||
StructRNA *srna;
|
StructRNA *srna;
|
||||||
|
@ -251,6 +359,13 @@ static void rna_def_grease_pencil_layer(BlenderRNA *brna)
|
||||||
RNA_def_struct_name_property(srna, prop);
|
RNA_def_struct_name_property(srna, prop);
|
||||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA | NA_RENAME, "rna_grease_pencil_update");
|
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 */
|
/* Visibility */
|
||||||
prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE);
|
prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE);
|
||||||
RNA_def_property_boolean_sdna(
|
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");
|
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");
|
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 */
|
/* pass index for compositing and modifiers */
|
||||||
prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_UNSIGNED);
|
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");
|
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(
|
RNA_def_property_ui_text(
|
||||||
prop, "Locked", "Protect group from further editing and/or frame changes");
|
prop, "Locked", "Protect group from further editing and/or frame changes");
|
||||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update");
|
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)
|
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_data(brna);
|
||||||
rna_def_grease_pencil_layer(brna);
|
rna_def_grease_pencil_layer(brna);
|
||||||
|
rna_def_grease_pencil_layer_mask(brna);
|
||||||
rna_def_grease_pencil_layer_group(brna);
|
rna_def_grease_pencil_layer_group(brna);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
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.