GPv3: Opacity modifier #116946

Merged
Lukas Tönne merged 52 commits from LukasTonne/blender:gp3-opacity-modifier into main 2024-01-16 16:56:22 +01:00
11 changed files with 1079 additions and 1 deletions

View File

@ -71,7 +71,7 @@ class OBJECT_MT_modifier_add(ModifierAddMenu, Menu):
if geometry_nodes_supported:
self.operator_modifier_add(layout, 'NODES')
layout.separator()
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE', 'GREASEPENCIL'}:
layout.menu("OBJECT_MT_modifier_add_edit")
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'VOLUME'}:
layout.menu("OBJECT_MT_modifier_add_generate")
@ -105,6 +105,8 @@ class OBJECT_MT_modifier_add_edit(ModifierAddMenu, Menu):
self.operator_modifier_add(layout, 'VERTEX_WEIGHT_EDIT')
self.operator_modifier_add(layout, 'VERTEX_WEIGHT_MIX')
self.operator_modifier_add(layout, 'VERTEX_WEIGHT_PROXIMITY')
if ob_type == 'GREASEPENCIL':
self.operator_modifier_add(layout, 'GREASE_PENCIL_OPACITY')
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)

View File

@ -801,4 +801,11 @@
.mat_ofs = 0, \
}
#define _DNA_DEFAULT_GreasePencilOpacityModifierData \
{ \
.color_mode = MOD_GREASE_PENCIL_COLOR_BOTH, \
.color_factor = 1.0f, \
.hardness_factor = 1.0f, \
}
/* clang-format off */

View File

@ -93,6 +93,7 @@ typedef enum ModifierType {
eModifierType_MeshToVolume = 58,
eModifierType_VolumeDisplace = 59,
eModifierType_VolumeToMesh = 60,
eModifierType_GreasePencilOpacity = 61,
NUM_MODIFIER_TYPES,
} ModifierType;
@ -2484,3 +2485,65 @@ typedef enum VolumeToMeshResolutionMode {
typedef enum VolumeToMeshFlag {
VOLUME_TO_MESH_USE_SMOOTH_SHADE = 1 << 0,
} VolumeToMeshFlag;
/**
* Common influence data for grease pencil modifiers.
* Not all parts may be used by all modifier types.
*/
typedef struct GreasePencilModifierInfluenceData {
/** GreasePencilModifierInfluenceFlag */
int flag;
char _pad1[4];
/** Filter by layer name. */
char layer_name[64];
/** Filter by stroke material. */
struct Material *material;
/** Filter by layer pass. */
int layer_pass;
/** Filter by material pass. */
int material_pass;
/** #MAX_VGROUP_NAME. */
char vertex_group_name[64];
struct CurveMapping *custom_curve;
void *_pad2;
} GreasePencilModifierInfluenceData;
typedef enum GreasePencilModifierInfluenceFlag {
GREASE_PENCIL_INFLUENCE_INVERT_LAYER_FILTER = (1 << 0),
GREASE_PENCIL_INFLUENCE_USE_LAYER_PASS_FILTER = (1 << 1),
GREASE_PENCIL_INFLUENCE_INVERT_LAYER_PASS_FILTER = (1 << 2),
GREASE_PENCIL_INFLUENCE_INVERT_MATERIAL_FILTER = (1 << 3),
GREASE_PENCIL_INFLUENCE_USE_MATERIAL_PASS_FILTER = (1 << 4),
GREASE_PENCIL_INFLUENCE_INVERT_MATERIAL_PASS_FILTER = (1 << 5),
GREASE_PENCIL_INFLUENCE_INVERT_VERTEX_GROUP = (1 << 6),
GREASE_PENCIL_INFLUENCE_USE_CUSTOM_CURVE = (1 << 7),
} GreasePencilModifierInfluenceFlag;
typedef struct GreasePencilOpacityModifierData {
ModifierData modifier;
GreasePencilModifierInfluenceData influence;
/** GreasePencilOpacityModifierFlag */
int flag;
/** GreasePencilModifierColorMode */
char color_mode;
char _pad1[3];
float color_factor;
float hardness_factor;
void *_pad2;
} GreasePencilOpacityModifierData;
/** Which attributes are affected by color modifiers. */
typedef enum GreasePencilModifierColorMode {
MOD_GREASE_PENCIL_COLOR_STROKE = 0,
MOD_GREASE_PENCIL_COLOR_FILL = 1,
MOD_GREASE_PENCIL_COLOR_BOTH = 2,
MOD_GREASE_PENCIL_COLOR_HARDNESS = 3,
} GreasePencilModifierColorMode;
typedef enum GreasePencilOpacityModifierFlag {
MOD_GREASE_PENCIL_OPACITY_OPEN_INFLUENCE_PANEL = (1 << 0),
/* Use vertex group as opacity factors instead of influence. */
MOD_GREASE_PENCIL_OPACITY_USE_WEIGHT_AS_FACTOR = (1 << 1),
/* Set the opacity for every point in a stroke, otherwise multiply existing opacity. */
MOD_GREASE_PENCIL_OPACITY_USE_UNIFORM_OPACITY = (1 << 2),
} GreasePencilOpacityModifierFlag;

View File

@ -322,6 +322,7 @@ SDNA_DEFAULT_DECL_STRUCT(DashGpencilModifierData);
SDNA_DEFAULT_DECL_STRUCT(DashGpencilModifierSegment);
SDNA_DEFAULT_DECL_STRUCT(ShrinkwrapGpencilModifierData);
SDNA_DEFAULT_DECL_STRUCT(EnvelopeGpencilModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilOpacityModifierData);
#undef SDNA_DEFAULT_DECL_STRUCT
@ -566,6 +567,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
SDNA_DEFAULT_DECL(DashGpencilModifierSegment),
SDNA_DEFAULT_DECL(ShrinkwrapGpencilModifierData),
SDNA_DEFAULT_DECL(EnvelopeGpencilModifierData),
SDNA_DEFAULT_DECL(GreasePencilOpacityModifierData),
};
#undef SDNA_DEFAULT_DECL
#undef SDNA_DEFAULT_DECL_EX

View File

@ -100,6 +100,11 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
ICON_MOD_VERTEX_WEIGHT,
"Vertex Weight Proximity",
"Set the vertex group weights based on the distance to another target object"},
{eModifierType_GreasePencilOpacity,
"GREASE_PENCIL_OPACITY",
ICON_MOD_OPACITY,
"Opacity",
"Change the opacity of the strokes"},
LukasTonne marked this conversation as resolved Outdated

Opacity of the strokes -> Change the opacity of the strokes

I know this is just copying the old description, might as well fix it though

`Opacity of the strokes` -> `Change the opacity of the strokes` I know this is just copying the old description, might as well fix it though
RNA_ENUM_ITEM_HEADING(N_("Generate"), nullptr),
{eModifierType_Array,
@ -668,11 +673,13 @@ const EnumPropertyItem rna_enum_subdivision_boundary_smooth_items[] = {
#ifdef RNA_RUNTIME
# include "DNA_curve_types.h"
# include "DNA_fluid_types.h"
# include "DNA_material_types.h"
# include "DNA_particle_types.h"
# include "BKE_cachefile.h"
# include "BKE_context.hh"
# include "BKE_deform.h"
# include "BKE_material.h"
# include "BKE_mesh_runtime.hh"
# include "BKE_modifier.hh"
# include "BKE_object.hh"
@ -1705,6 +1712,78 @@ static IDProperty **rna_NodesModifier_properties(PointerRNA *ptr)
NodesModifierSettings *settings = &nmd->settings;
return &settings->properties;
}
bool rna_GreasePencilModifier_material_poll(PointerRNA *ptr, PointerRNA value)
{
Object *ob = reinterpret_cast<Object *>(ptr->owner_id);
Material *ma = reinterpret_cast<Material *>(value.owner_id);
return BKE_object_material_index_get(ob, ma) != -1;
}
/* Write material to a generic target pointer without the final modifier struct. */
static void rna_GreasePencilModifier_material_set(PointerRNA *ptr,
PointerRNA value,
ReportList *reports,
Material **ma_target)
{
Object *ob = reinterpret_cast<Object *>(ptr->owner_id);
Material *ma = reinterpret_cast<Material *>(value.owner_id);
if (ma == nullptr || BKE_object_material_index_get(ob, ma) != -1) {
id_lib_extern(reinterpret_cast<ID *>(ob));
*ma_target = ma;
}
else {
BKE_reportf(
reports,
RPT_ERROR,
"Cannot assign material '%s', it has to be used by the grease pencil object already",
ma->id.name);
}
}
# define RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(_type) \
static void rna_##_type##Modifier_material_filter_set( \
PointerRNA *ptr, PointerRNA value, ReportList *reports) \
{ \
_type##ModifierData *tmd = static_cast<_type##ModifierData *>(ptr->data); \
rna_GreasePencilModifier_material_set(ptr, value, reports, &tmd->influence.material); \
}
# define RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(_type) \
static void rna_##_type##Modifier_vertex_group_name_set(PointerRNA *ptr, const char *value) \
{ \
_type##ModifierData *tmd = static_cast<_type##ModifierData *>(ptr->data); \
rna_object_vgroup_name_set(ptr, \
value, \
tmd->influence.vertex_group_name, \
sizeof(tmd->influence.vertex_group_name)); \
}
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilOpacity);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilOpacity);
static void rna_GreasePencilOpacityModifier_opacity_factor_range(
PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax)
{
GreasePencilOpacityModifierData *omd = static_cast<GreasePencilOpacityModifierData *>(ptr->data);
*min = 0.0f;
*softmin = 0.0f;
*softmax = (omd->flag & MOD_GREASE_PENCIL_OPACITY_USE_UNIFORM_OPACITY) ? 1.0f : 2.0f;
*max = *softmax;
}
static void rna_GreasePencilOpacityModifier_opacity_factor_max_set(PointerRNA *ptr, float value)
{
GreasePencilOpacityModifierData *omd = static_cast<GreasePencilOpacityModifierData *>(ptr->data);
omd->color_factor = (omd->flag & MOD_GREASE_PENCIL_OPACITY_USE_UNIFORM_OPACITY) ?
std::min(value, 1.0f) :
value;
}
#else
static void rna_def_modifier_panel_open_prop(StructRNA *srna, const char *identifier, const int id)
@ -7423,6 +7502,186 @@ static void rna_def_modifier_volume_to_mesh(BlenderRNA *brna)
RNA_define_lib_overridable(false);
}
static void rna_def_modifier_grease_pencil_layer_filter(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "layer_filter", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, nullptr, "influence.layer_name");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_layer_pass_filter", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, nullptr, "influence.flag", GREASE_PENCIL_INFLUENCE_USE_LAYER_PASS_FILTER);
RNA_def_property_ui_text(prop, "Use Layer Pass", "Use layer pass filter");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "layer_pass_filter", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "influence.layer_pass");
RNA_def_property_range(prop, 0, 100);
RNA_def_property_ui_text(prop, "Layer Pass", "Layer pass filter");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "invert_layer_filter", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, nullptr, "influence.flag", GREASE_PENCIL_INFLUENCE_INVERT_LAYER_FILTER);
RNA_def_property_ui_text(prop, "Invert Layer", "Invert layer filter");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "invert_layer_pass_filter", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, nullptr, "influence.flag", GREASE_PENCIL_INFLUENCE_INVERT_LAYER_PASS_FILTER);
RNA_def_property_ui_text(prop, "Invert Layer Pass", "Invert layer pass filter");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
}
static void rna_def_modifier_grease_pencil_material_filter(StructRNA *srna,
const char *material_set_fn)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "material_filter", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, nullptr, "influence.material");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_pointer_funcs(
prop, nullptr, material_set_fn, nullptr, "rna_GreasePencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_material_pass_filter", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, nullptr, "influence.flag", GREASE_PENCIL_INFLUENCE_USE_MATERIAL_PASS_FILTER);
RNA_def_property_ui_text(prop, "Use Material Pass", "Use material pass filter");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "material_pass_filter", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "influence.material_pass");
RNA_def_property_range(prop, 0, 100);
RNA_def_property_ui_text(prop, "Material Pass", "Material pass");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "invert_material_filter", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, nullptr, "influence.flag", GREASE_PENCIL_INFLUENCE_INVERT_MATERIAL_FILTER);
RNA_def_property_ui_text(prop, "Invert Material", "Invert material filter");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "invert_material_pass_filter", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, nullptr, "influence.flag", GREASE_PENCIL_INFLUENCE_INVERT_MATERIAL_PASS_FILTER);
RNA_def_property_ui_text(prop, "Invert Material Pass", "Invert material pass filter");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
}
static void rna_def_modifier_grease_pencil_vertex_group(StructRNA *srna,
LukasTonne marked this conversation as resolved
Review

Should this be rna_def_modifier_grease_pencil_vertex_group_filter to be consistent with the other filters?

Should this be `rna_def_modifier_grease_pencil_vertex_group_filter` to be consistent with the other filters?
Review

Not really, the other "filters" are purely selection masks based on layers or materials. The vertex group is always either a multiplier or, in the case of the opacity modifier, an offset/factor/something.

I appended the "filter" suffix to layer/material mostly so it would be clear that these are just selection criteria and their settings don't otherwise affect the modifier.

Not really, the other "filters" are purely selection masks based on layers or materials. The vertex group is always either a multiplier or, in the case of the opacity modifier, an offset/factor/something. I appended the "filter" suffix to layer/material mostly so it would be clear that these are just selection criteria and their settings don't otherwise affect the modifier.
const char *vertex_group_name_set_fn)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "vertex_group_name", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, nullptr, "influence.vertex_group_name");
RNA_def_property_ui_text(prop, "Vertex Group", "Vertex group name for modulating the deform");
RNA_def_property_string_funcs(prop, nullptr, nullptr, vertex_group_name_set_fn);
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "invert_vertex_group", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, nullptr, "influence.flag", GREASE_PENCIL_INFLUENCE_INVERT_VERTEX_GROUP);
RNA_def_property_ui_text(prop, "Invert Vertex Group", "Invert vertex group weights");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
}
static void rna_def_modifier_grease_pencil_custom_curve(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "use_custom_curve", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, nullptr, "influence.flag", GREASE_PENCIL_INFLUENCE_USE_CUSTOM_CURVE);
RNA_def_property_ui_text(
prop, "Use Custom Curve", "Use a custom curve to define a factor along the strokes");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "custom_curve", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, nullptr, "influence.custom_curve");
RNA_def_property_ui_text(prop, "Curve", "Custom curve to apply effect");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
}
static void rna_def_modifier_grease_pencil_opacity(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
static const EnumPropertyItem color_mode_items[] = {
{MOD_GREASE_PENCIL_COLOR_BOTH, "BOTH", 0, "Stroke & Fill", "Modify fill and stroke colors"},
{MOD_GREASE_PENCIL_COLOR_STROKE, "STROKE", 0, "Stroke", "Modify stroke color only"},
{MOD_GREASE_PENCIL_COLOR_FILL, "FILL", 0, "Fill", "Modify fill color only"},
{MOD_GREASE_PENCIL_COLOR_HARDNESS, "HARDNESS", 0, "Hardness", "Modify stroke hardness"},
{0, nullptr, 0, nullptr, nullptr},
};
srna = RNA_def_struct(brna, "GreasePencilOpacityModifier", "Modifier");
RNA_def_struct_ui_text(srna, "Grease Pencil Opacity Modifier", "");
RNA_def_struct_sdna(srna, "GreasePencilOpacityModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_OPACITY);
rna_def_modifier_grease_pencil_layer_filter(srna);
rna_def_modifier_grease_pencil_material_filter(
srna, "rna_GreasePencilOpacityModifier_material_filter_set");
rna_def_modifier_grease_pencil_vertex_group(
srna, "rna_GreasePencilOpacityModifier_vertex_group_name_set");
rna_def_modifier_grease_pencil_custom_curve(srna);
prop = RNA_def_property(srna, "open_influence_panel", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, nullptr, "flag", MOD_GREASE_PENCIL_OPACITY_OPEN_INFLUENCE_PANEL);
RNA_def_property_ui_text(prop, "Open Influence Panel", "Open the influence panel");
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, nullptr);
RNA_define_lib_overridable(true);
prop = RNA_def_property(srna, "color_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, color_mode_items);
RNA_def_property_ui_text(prop, "Mode", "Attributes to modify");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "color_factor", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, nullptr, "color_factor");
RNA_def_property_ui_range(prop, 0, 2.0, 0.1, 2);
RNA_def_property_float_funcs(prop,
nullptr,
"rna_GreasePencilOpacityModifier_opacity_factor_max_set",
"rna_GreasePencilOpacityModifier_opacity_factor_range");
RNA_def_property_ui_text(prop, "Opacity Factor", "Factor of opacity");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "hardness_factor", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, nullptr, "hardness_factor");
RNA_def_property_range(prop, 0.0, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0, FLT_MAX, 0.1, 2);
RNA_def_property_ui_text(prop, "Hardness Factor", "Factor of stroke hardness");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_weight_as_factor", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, nullptr, "flag", MOD_GREASE_PENCIL_OPACITY_USE_WEIGHT_AS_FACTOR);
RNA_def_property_ui_text(
prop, "Use Weight as Factor", "Use vertex group weight as factor instead of influence");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_uniform_opacity", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, nullptr, "flag", MOD_GREASE_PENCIL_OPACITY_USE_UNIFORM_OPACITY);
RNA_def_property_ui_text(
prop, "Uniform Opacity", "Replace the stroke opacity instead of modulating each point");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
RNA_define_lib_overridable(false);
}
void RNA_def_modifier(BlenderRNA *brna)
{
StructRNA *srna;
@ -7583,6 +7842,7 @@ void RNA_def_modifier(BlenderRNA *brna)
rna_def_modifier_mesh_to_volume(brna);
rna_def_modifier_volume_displace(brna);
rna_def_modifier_volume_to_mesh(brna);
rna_def_modifier_grease_pencil_opacity(brna);
}
#endif

View File

@ -44,6 +44,8 @@ set(SRC
intern/MOD_edgesplit.cc
intern/MOD_explode.cc
intern/MOD_fluid.cc
intern/MOD_grease_pencil_opacity.cc
intern/MOD_grease_pencil_util.cc
intern/MOD_hook.cc
intern/MOD_laplaciandeform.cc
intern/MOD_laplaciansmooth.cc
@ -97,6 +99,7 @@ set(SRC
MOD_modifiertypes.hh
MOD_nodes.hh
intern/MOD_grease_pencil_util.hh
intern/MOD_meshcache_util.hh
intern/MOD_solidify_util.hh
intern/MOD_ui_common.hh

View File

@ -73,6 +73,7 @@ extern ModifierTypeInfo modifierType_Nodes;
extern ModifierTypeInfo modifierType_MeshToVolume;
extern ModifierTypeInfo modifierType_VolumeDisplace;
extern ModifierTypeInfo modifierType_VolumeToMesh;
extern ModifierTypeInfo modifierType_GreasePencilOpacity;
/* MOD_util.cc */

View File

@ -0,0 +1,340 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
LukasTonne marked this conversation as resolved Outdated

Copyright year can be updated

Copyright year can be updated
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup modifiers
*/
#include "MEM_guardedalloc.h"
#include "DNA_defaults.h"
#include "DNA_modifier_types.h"
#include "DNA_scene_types.h"
#include "BKE_colortools.hh"
#include "BKE_curves.hh"
#include "BKE_geometry_set.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_modifier.hh"
#include "BLO_read_write.hh"
#include "DEG_depsgraph_query.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "BLT_translation.h"
#include "WM_types.hh"
#include "RNA_access.hh"
#include "RNA_enum_types.hh"
#include "RNA_prototypes.h"
#include "MOD_grease_pencil_util.hh"
#include "MOD_modifiertypes.hh"
#include "MOD_ui_common.hh"
#include <iostream>
namespace blender {
using bke::greasepencil::Drawing;
using bke::greasepencil::FramesMapKey;
using bke::greasepencil::Layer;
static void init_data(ModifierData *md)
{
auto *omd = reinterpret_cast<GreasePencilOpacityModifierData *>(md);
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(omd, modifier));
MEMCPY_STRUCT_AFTER(omd, DNA_struct_default_get(GreasePencilOpacityModifierData), modifier);
modifier::greasepencil::init_influence_data(&omd->influence, true);
}
static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
{
const auto *omd = reinterpret_cast<const GreasePencilOpacityModifierData *>(md);
LukasTonne marked this conversation as resolved Outdated

You can write const auto * with the cast, it might keep it on one line

You can write `const auto *` with the cast, it might keep it on one line
auto *tomd = reinterpret_cast<GreasePencilOpacityModifierData *>(target);
modifier::greasepencil::free_influence_data(&tomd->influence);
BKE_modifier_copydata_generic(md, target, flag);
modifier::greasepencil::copy_influence_data(&omd->influence, &tomd->influence, flag);
}
static void free_data(ModifierData *md)
{
auto *omd = reinterpret_cast<GreasePencilOpacityModifierData *>(md);
modifier::greasepencil::free_influence_data(&omd->influence);
}
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
{
auto *omd = reinterpret_cast<GreasePencilOpacityModifierData *>(md);
modifier::greasepencil::foreach_influence_ID_link(&omd->influence, ob, walk, user_data);
}
static void modify_stroke_color(const GreasePencilOpacityModifierData &omd,
bke::CurvesGeometry &curves,
const IndexMask &curves_mask)
LukasTonne marked this conversation as resolved
Review

You should just be able to retrieve the vertex group as an attribute

You should just be able to retrieve the vertex group as an attribute
Review

Took some explaining from @filedescriptor how this works (maybe a good topic for technical docs?). As far as i understand the VArray wrapper for the curves->deform_verts() is ok here, since we're only using a single vertex group. For something like the armature modifier i'd guess we still want to use the MDeformVert directly.

Took some explaining from @filedescriptor how this works (maybe a good topic for technical docs?). As far as i understand the `VArray` wrapper for the `curves->deform_verts()` is ok here, since we're only using a single vertex group. For something like the armature modifier i'd guess we still want to use the `MDeformVert` directly.
Review

Right, exactly

Right, exactly
{
const bool use_uniform_opacity = (omd.flag & MOD_GREASE_PENCIL_OPACITY_USE_UNIFORM_OPACITY);
const bool use_weight_as_factor = (omd.flag & MOD_GREASE_PENCIL_OPACITY_USE_WEIGHT_AS_FACTOR);
const bool invert_vertex_group = (omd.influence.flag &
GREASE_PENCIL_INFLUENCE_INVERT_VERTEX_GROUP);
const bool use_curve = (omd.influence.flag & GREASE_PENCIL_INFLUENCE_USE_CUSTOM_CURVE);
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
bke::SpanAttributeWriter<float> opacities = attributes.lookup_or_add_for_write_span<float>(
"opacity", bke::AttrDomain::Point);
const VArray<float> vgroup_weights =
attributes
.lookup_or_default<float>(omd.influence.vertex_group_name, bke::AttrDomain::Point, 1.0f)
LukasTonne marked this conversation as resolved Outdated

Use the * operator overload to just store the VArray directly here

Use the * operator overload to just store the VArray directly here
.varray;
curves_mask.foreach_index(GrainSize(512), [&](const int64_t curve_i) {
const IndexRange points = points_by_curve[curve_i];
for (const int64_t point_i : points) {
const float curve_input = points.size() >= 2 ?
(float(point_i - points.first()) / float(points.size() - 1)) :
0.0f;
const float curve_factor = use_curve ? BKE_curvemapping_evaluateF(
omd.influence.custom_curve, 0, curve_input) :
1.0f;
if (use_uniform_opacity) {
opacities.span[point_i] = std::clamp(omd.color_factor * curve_factor, 0.0f, 1.0f);
}
else if (use_weight_as_factor) {
/* Use vertex group weights as opacity factors. */
opacities.span[point_i] = std::clamp(
omd.color_factor * curve_factor * vgroup_weights[point_i], 0.0f, 1.0f);
}
else {
/* Use vertex group weights as influence factors. */
const float vgroup_weight = vgroup_weights[point_i];
const float vgroup_influence = invert_vertex_group ? 1.0f - vgroup_weight : vgroup_weight;
opacities.span[point_i] = std::clamp(
opacities.span[point_i] + omd.color_factor * curve_factor * vgroup_influence - 1.0f,
0.0f,
1.0f);
LukasTonne marked this conversation as resolved Outdated

remove this and pass vgroup_weight to clamp fn? 😅

remove this and pass `vgroup_weight` to clamp fn? 😅

Haha yeah, this is the result of me trying to make sense of the logic of the old modifier.

Haha yeah, this is the result of me trying to make sense of the logic of the old modifier.
}
}
});
opacities.finish();
}
static void modify_fill_color(const GreasePencilOpacityModifierData &omd,
bke::CurvesGeometry &curves,

Wondering if this could be made much clean by using DefaultMixer?

Wondering if this could be made much clean by using `DefaultMixer`?

That seems a bit unnecessary here.

  • The attributes are all floats, no need to handle different types.
  • The math is weird and undocumented. In the "uniform" mode it uses 1 - current as some kind of cutoff point, and then allows ramping up the influence to 2.0, presumably so one can reach full opacity/hardness again. It all seems very ad-hoc.

I'd prefer to not try and "fix" this too much, since the primary purpose is compatibility with GP2.

That seems a bit unnecessary here. - The attributes are all floats, no need to handle different types. - The math is weird and undocumented. In the "uniform" mode it uses `1 - current` as some kind of cutoff point, and then allows ramping up the influence to 2.0, presumably so one can reach full opacity/hardness again. It all seems very ad-hoc. I'd prefer to not try and "fix" this too much, since the primary purpose is compatibility with GP2.
const IndexMask &curves_mask)
{
const bool use_vgroup_opacity = (omd.flag & MOD_GREASE_PENCIL_OPACITY_USE_WEIGHT_AS_FACTOR);
const bool invert_vertex_group = (omd.influence.flag &
GREASE_PENCIL_INFLUENCE_INVERT_VERTEX_GROUP);
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
/* Fill color opacity per stroke. */
bke::SpanAttributeWriter<float> fill_opacities = attributes.lookup_or_add_for_write_span<float>(
"fill_opacity", bke::AttrDomain::Curve);
VArray<float> vgroup_weights = attributes
.lookup_or_default<float>(omd.influence.vertex_group_name,
bke::AttrDomain::Point,
1.0f)
.varray;
curves_mask.foreach_index(GrainSize(512), [&](int64_t curve_i) {
if (use_vgroup_opacity) {
/* Use the first stroke point as vertex weight. */
const IndexRange points = points_by_curve[curve_i];
const float stroke_weight = points.is_empty() ? 1.0f : vgroup_weights[points.first()];
const float stroke_influence = invert_vertex_group ? 1.0f - stroke_weight : stroke_weight;
fill_opacities.span[curve_i] = std::clamp(stroke_influence, 0.0f, 1.0f);
}
else {
fill_opacities.span[curve_i] = std::clamp(omd.color_factor, 0.0f, 1.0f);
}
});
LukasTonne marked this conversation as resolved
Review

points_range -> points is the cononical variable name here. "range" is already described by the type

`points_range` -> `points` is the cononical variable name here. "range" is already described by the type
fill_opacities.finish();
LukasTonne marked this conversation as resolved
Review

You can count on the fact that curves always have at least one point

You can count on the fact that curves always have at least one point
Review

At least one or at least two? I need two for this length factor calculation.

At least one or at least two? I need two for this length factor calculation.
Review

Falk says curves can have 1 point only, so i still need to check.

Falk says curves can have 1 point only, so i still need to check.
}
static void modify_hardness(const GreasePencilOpacityModifierData &omd,
bke::CurvesGeometry &curves,
const IndexMask &curves_mask)
{
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
bke::SpanAttributeWriter<float> hardnesses = attributes.lookup_for_write_span<float>("hardness");
if (!hardnesses) {
return;
}
curves_mask.foreach_index(GrainSize(512), [&](int64_t curve_i) {
hardnesses.span[curve_i] = std::clamp(
hardnesses.span[curve_i] * omd.hardness_factor, 0.0f, 1.0f);
});
hardnesses.finish();
}
LukasTonne marked this conversation as resolved
Review

Could this be skipped if there's no hardness attribute already?

Could this be skipped if there's no hardness attribute already?
Review

I suppose it could. The old modifier is quite inconsistent in how the "uniform" setting is applied (confusingly called "normalized" internally). In the color modes (stroke, fill) it then applies the settings as an offset rather than a factor, with some unexplained bias and clamping. But this isn't used for the hardness mode, so 🤷

I suppose it could. The old modifier is quite inconsistent in how the "uniform" setting is applied (confusingly called "normalized" internally). In the color modes (stroke, fill) it then applies the settings as an _offset_ rather than a factor, with some unexplained bias and clamping. But this isn't used for the hardness mode, so 🤷
static void modify_curves(ModifierData *md,
const ModifierEvalContext *ctx,
bke::CurvesGeometry &curves)
{
auto *omd = reinterpret_cast<GreasePencilOpacityModifierData *>(md);
IndexMaskMemory mask_memory;
IndexMask curves_mask = modifier::greasepencil::get_filtered_stroke_mask(
ctx->object, curves, omd->influence, mask_memory);
switch (omd->color_mode) {
case MOD_GREASE_PENCIL_COLOR_STROKE:
modify_stroke_color(*omd, curves, curves_mask);
break;
case MOD_GREASE_PENCIL_COLOR_FILL:
LukasTonne marked this conversation as resolved
Review

C++ style casts (elsewhere in this file too)

C++ style casts (elsewhere in this file too)
modify_fill_color(*omd, curves, curves_mask);
break;
case MOD_GREASE_PENCIL_COLOR_BOTH:
modify_stroke_color(*omd, curves, curves_mask);
modify_fill_color(*omd, curves, curves_mask);
break;
case MOD_GREASE_PENCIL_COLOR_HARDNESS:
modify_hardness(*omd, curves, curves_mask);
break;
}
}
static void modify_geometry_set(ModifierData *md,
const ModifierEvalContext *ctx,
bke::GeometrySet *geometry_set)
{
auto *omd = reinterpret_cast<GreasePencilOpacityModifierData *>(md);
const Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph);
const int frame = scene->r.cfra;
GreasePencil *grease_pencil = geometry_set->get_grease_pencil_for_write();
if (grease_pencil == nullptr) {
return;
}
IndexMaskMemory mask_memory;
IndexMask layer_mask = modifier::greasepencil::get_filtered_layer_mask(
*grease_pencil, omd->influence, mask_memory);
Vector<Drawing *> drawings = modifier::greasepencil::get_drawings_for_write(
*grease_pencil, layer_mask, frame);
threading::parallel_for_each(drawings.index_range(), [&](int64_t i) {
modify_curves(md, ctx, drawings[i]->strokes_for_write());
});
}
static void panel_draw(const bContext *C, Panel *panel)
{
uiLayout *layout = panel->layout;
PointerRNA ob_ptr;
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
LukasTonne marked this conversation as resolved Outdated

threading::parallel_for ?

`threading::parallel_for` ?
uiLayoutSetPropSep(layout, true);
const GreasePencilModifierColorMode color_mode = GreasePencilModifierColorMode(
RNA_enum_get(ptr, "color_mode"));
uiItemR(layout, ptr, "color_mode", UI_ITEM_NONE, nullptr, ICON_NONE);
if (color_mode == MOD_GREASE_PENCIL_COLOR_HARDNESS) {
uiItemR(layout, ptr, "hardness_factor", UI_ITEM_NONE, nullptr, ICON_NONE);
}
else {
const bool use_uniform_opacity = RNA_boolean_get(ptr, "use_uniform_opacity");
const bool use_weight_as_factor = RNA_boolean_get(ptr, "use_weight_as_factor");
uiItemR(layout, ptr, "use_uniform_opacity", UI_ITEM_NONE, nullptr, ICON_NONE);
const char *text = (use_uniform_opacity) ? IFACE_("Opacity") : IFACE_("Opacity Factor");
uiLayout *row = uiLayoutRow(layout, true);
uiLayoutSetActive(row, !use_weight_as_factor || use_uniform_opacity);
uiItemR(row, ptr, "color_factor", UI_ITEM_NONE, text, ICON_NONE);
if (!use_uniform_opacity) {
uiLayout *sub = uiLayoutRow(row, true);
uiLayoutSetActive(sub, true);
uiItemR(row, ptr, "use_weight_as_factor", UI_ITEM_NONE, "", ICON_MOD_VERTEX_WEIGHT);
}
}
if (uiLayout *influence_panel = uiLayoutPanel(
C, layout, "Influence", ptr, "open_influence_panel"))
{
modifier::greasepencil::draw_layer_filter_settings(C, influence_panel, ptr);
modifier::greasepencil::draw_material_filter_settings(C, influence_panel, ptr);
modifier::greasepencil::draw_vertex_group_settings(C, influence_panel, ptr);
modifier::greasepencil::draw_custom_curve_settings(C, influence_panel, ptr);
}
modifier_panel_end(layout, ptr);
}
static void panel_register(ARegionType *region_type)
{
modifier_panel_register(region_type, eModifierType_GreasePencilOpacity, panel_draw);
}
static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
{
const auto *omd = reinterpret_cast<const GreasePencilOpacityModifierData *>(md);
BLO_write_struct(writer, GreasePencilOpacityModifierData, omd);
modifier::greasepencil::write_influence_data(writer, &omd->influence);
}
static void blend_read(BlendDataReader *reader, ModifierData *md)
{
auto *omd = reinterpret_cast<GreasePencilOpacityModifierData *>(md);
modifier::greasepencil::read_influence_data(reader, &omd->influence);
}
} // namespace blender
ModifierTypeInfo modifierType_GreasePencilOpacity = {
/*idname*/ "GreasePencilOpacity",
/*name*/ N_("GreasePencilOpacity"),
/*struct_name*/ "GreasePencilOpacityModifierData",
/*struct_size*/ sizeof(GreasePencilOpacityModifierData),
/*srna*/ &RNA_GreasePencilOpacityModifier,
/*type*/ ModifierTypeType::Nonconstructive,
/*flags*/
static_cast<ModifierTypeFlag>(
eModifierTypeFlag_AcceptsGreasePencil | eModifierTypeFlag_SupportsEditmode |
eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping),
/*icon*/ ICON_MOD_OPACITY,
/*copy_data*/ blender::copy_data,
/*deform_verts*/ nullptr,
/*deform_matrices*/ nullptr,
/*deform_verts_EM*/ nullptr,
/*deform_matrices_EM*/ nullptr,
/*modify_mesh*/ nullptr,
/*modify_geometry_set*/ blender::modify_geometry_set,
/*init_data*/ blender::init_data,
/*required_data_mask*/ nullptr,
/*free_data*/ blender::free_data,
/*is_disabled*/ nullptr,
/*update_depsgraph*/ nullptr,
/*depends_on_time*/ nullptr,
/*depends_on_normals*/ nullptr,
/*foreach_ID_link*/ blender::foreach_ID_link,
/*foreach_tex_link*/ nullptr,
/*free_runtime_data*/ nullptr,
/*panel_register*/ blender::panel_register,
/*blend_write*/ blender::blend_write,
/*blend_read*/ blender::blend_read,
};

View File

@ -0,0 +1,333 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup modifiers
*/
#include "MOD_grease_pencil_util.hh"
#include "BLI_set.hh"
#include "DNA_grease_pencil_types.h"
#include "DNA_material_types.h"
#include "DNA_modifier_types.h"
#include "DNA_screen_types.h"
#include "BKE_colortools.hh"
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_lib_query.h"
#include "BKE_material.h"
#include "BLO_read_write.hh"
#include "DNA_defaults.h"
#include "DEG_depsgraph_query.hh"
#include "MOD_ui_common.hh"
#include "RNA_access.hh"
#include "RNA_prototypes.h"
#include "UI_interface.hh"
namespace blender::modifier::greasepencil {
using bke::greasepencil::Drawing;
using bke::greasepencil::Layer;
void init_influence_data(GreasePencilModifierInfluenceData *influence_data,
const bool has_custom_curve)
{
if (has_custom_curve) {
influence_data->custom_curve = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
BKE_curvemapping_init(influence_data->custom_curve);
}
}
void copy_influence_data(const GreasePencilModifierInfluenceData *influence_data_src,
GreasePencilModifierInfluenceData *influence_data_dst,
const int /*flag*/)
{
memcpy(influence_data_dst, influence_data_src, sizeof(GreasePencilModifierInfluenceData));
influence_data_dst->custom_curve = BKE_curvemapping_copy(influence_data_src->custom_curve);
}
void free_influence_data(GreasePencilModifierInfluenceData *influence_data)
{
if (influence_data->custom_curve) {
BKE_curvemapping_free(influence_data->custom_curve);
influence_data->custom_curve = nullptr;
}
}
void foreach_influence_ID_link(GreasePencilModifierInfluenceData *influence_data,
Object *ob,
IDWalkFunc walk,
void *user_data)
{
walk(user_data, ob, (ID **)&influence_data->material, IDWALK_CB_USER);
}
void write_influence_data(BlendWriter *writer,
const GreasePencilModifierInfluenceData *influence_data)
{
if (influence_data->custom_curve) {
BKE_curvemapping_blend_write(writer, influence_data->custom_curve);
}
}
void read_influence_data(BlendDataReader *reader,
GreasePencilModifierInfluenceData *influence_data)
{
BLO_read_data_address(reader, &influence_data->custom_curve);
if (influence_data->custom_curve) {
BKE_curvemapping_blend_read(reader, influence_data->custom_curve);
/* Make sure the internal table exists. */
BKE_curvemapping_init(influence_data->custom_curve);
}
}
void draw_layer_filter_settings(const bContext * /*C*/, uiLayout *layout, PointerRNA *ptr)
{
PointerRNA ob_ptr = RNA_pointer_create(ptr->owner_id, &RNA_Object, ptr->owner_id);
PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data");
const bool use_layer_pass = RNA_boolean_get(ptr, "use_layer_pass_filter");
uiLayout *row, *col, *sub, *subsub;
uiLayoutSetPropSep(layout, true);
col = uiLayoutColumn(layout, true);
row = uiLayoutRow(col, true);
uiLayoutSetPropDecorate(row, false);
uiItemPointerR(row, ptr, "layer_filter", &obj_data_ptr, "layers", nullptr, ICON_GREASEPENCIL);
sub = uiLayoutRow(row, true);
uiItemR(sub, ptr, "invert_layer_filter", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT);
row = uiLayoutRowWithHeading(col, true, "Layer Pass");
uiLayoutSetPropDecorate(row, false);
sub = uiLayoutRow(row, true);
LukasTonne marked this conversation as resolved Outdated

Would be nice to have the checkbox in the same line as the pass button. I haven't found a good way to combine these buttons yet, could use help from UI experts.

Would be nice to have the checkbox in the same line as the pass button. I haven't found a good way to combine these buttons yet, could use help from UI experts.

It's ugly but possible, here's an example from the limit location constraint:

        row = col.row(heading="Y", align=True)
        row.use_property_decorate = False
        sub = row.row(align=True)
        sub.prop(con, "use_max_y", text="")
        subsub = sub.row(align=True)
        subsub.active = con.use_max_y
        subsub.prop(con, "max_y", text="")
        row.prop_decorator(con, "max_y")
It's ugly but possible, here's an example from the limit location constraint: ``` row = col.row(heading="Y", align=True) row.use_property_decorate = False sub = row.row(align=True) sub.prop(con, "use_max_y", text="") subsub = sub.row(align=True) subsub.active = con.use_max_y subsub.prop(con, "max_y", text="") row.prop_decorator(con, "max_y") ```

I Agree to have the checkbox on the same line. Hans proposal seems a right choice

I Agree to have the checkbox on the same line. Hans proposal seems a right choice

I didn't know about the uiLayoutRowWithHeading feature, thanks.

I didn't know about the `uiLayoutRowWithHeading` feature, thanks.
uiItemR(sub, ptr, "use_layer_pass_filter", UI_ITEM_NONE, "", ICON_NONE);
subsub = uiLayoutRow(sub, true);
uiLayoutSetActive(subsub, use_layer_pass);
uiItemR(subsub, ptr, "layer_pass_filter", UI_ITEM_NONE, "", ICON_NONE);
uiItemR(subsub, ptr, "invert_layer_pass_filter", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT);
}
void draw_material_filter_settings(const bContext * /*C*/, uiLayout *layout, PointerRNA *ptr)
{
PointerRNA ob_ptr = RNA_pointer_create(ptr->owner_id, &RNA_Object, ptr->owner_id);
PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data");
const bool use_material_pass = RNA_boolean_get(ptr, "use_material_pass_filter");
uiLayout *row, *col, *sub, *subsub;
uiLayoutSetPropSep(layout, true);
col = uiLayoutColumn(layout, true);
row = uiLayoutRow(col, true);
uiLayoutSetPropDecorate(row, false);
uiItemPointerR(
row, ptr, "material_filter", &obj_data_ptr, "materials", nullptr, ICON_SHADING_TEXTURE);
sub = uiLayoutRow(row, true);
uiItemR(sub, ptr, "invert_material_filter", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT);
row = uiLayoutRowWithHeading(col, true, "Material Pass");
uiLayoutSetPropDecorate(row, false);
sub = uiLayoutRow(row, true);
uiItemR(sub, ptr, "use_material_pass_filter", UI_ITEM_NONE, "", ICON_NONE);
subsub = uiLayoutRow(sub, true);
uiLayoutSetActive(subsub, use_material_pass);
uiItemR(subsub, ptr, "material_pass_filter", UI_ITEM_NONE, "", ICON_NONE);
uiItemR(subsub, ptr, "invert_material_pass_filter", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT);
}
void draw_vertex_group_settings(const bContext * /*C*/, uiLayout *layout, PointerRNA *ptr)
{
PointerRNA ob_ptr = RNA_pointer_create(ptr->owner_id, &RNA_Object, ptr->owner_id);
bool has_vertex_group = RNA_string_length(ptr, "vertex_group_name") != 0;
uiLayout *row, *sub;
uiLayoutSetPropSep(layout, true);
row = uiLayoutRow(layout, true);
uiItemPointerR(row, ptr, "vertex_group_name", &ob_ptr, "vertex_groups", nullptr, ICON_NONE);
sub = uiLayoutRow(row, true);
uiLayoutSetActive(sub, has_vertex_group);
uiLayoutSetPropDecorate(sub, false);
uiItemR(sub, ptr, "invert_vertex_group", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT);
}
void draw_custom_curve_settings(const bContext * /*C*/, uiLayout *layout, PointerRNA *ptr)
{
bool use_custom_curve = RNA_boolean_get(ptr, "use_custom_curve");
uiLayoutSetPropSep(layout, true);
uiItemR(layout, ptr, "use_custom_curve", UI_ITEM_NONE, nullptr, ICON_NONE);
if (use_custom_curve) {
uiTemplateCurveMapping(layout, ptr, "custom_curve", 0, false, false, false, false);
}
}
/**
* Get a list of pass IDs used by grease pencil materials.
* This way the material pass can be looked up by index instead of having to get the material for
* each curve.
*/
static Vector<int> get_grease_pencil_material_passes(const Object *ob)
{
short *totcol = BKE_object_material_len_p(const_cast<Object *>(ob));
Vector<int> result(*totcol);
Material *ma = nullptr;
for (short i = 0; i < *totcol; i++) {
ma = BKE_object_material_get(const_cast<Object *>(ob), i + 1);
/* Pass index of the grease pencil material. */
result[i] = ma->gp_style->index;
}
return result;
}
static IndexMask get_filtered_layer_mask(const GreasePencil &grease_pencil,
const std::optional<StringRef> layer_name_filter,
const std::optional<int> layer_pass_filter,
const bool layer_filter_invert,
const bool layer_pass_filter_invert,
IndexMaskMemory &memory)
{
const IndexMask full_mask = grease_pencil.layers().index_range();
if (!layer_name_filter && !layer_pass_filter) {
return full_mask;
}
bke::AttributeAccessor layer_attributes = grease_pencil.attributes();
const Span<const Layer *> layers = grease_pencil.layers();
const VArray<int> layer_passes =
layer_attributes.lookup_or_default<int>("pass", bke::AttrDomain::Layer, 0).varray;
IndexMask result = IndexMask::from_predicate(
full_mask, GrainSize(4096), memory, [&](const int64_t layer_i) {
if (layer_name_filter) {
const Layer &layer = *layers[layer_i];
const bool match = (layer.name() == layer_name_filter.value());
if (match == layer_filter_invert) {
return false;
}
}
if (layer_pass_filter) {
const int layer_pass = layer_passes.get(layer_i);
const bool match = (layer_pass == layer_pass_filter.value());
if (match == layer_pass_filter_invert) {
return false;
}
}
return true;
});
return result;
}
IndexMask get_filtered_layer_mask(const GreasePencil &grease_pencil,
const GreasePencilModifierInfluenceData &influence_data,
IndexMaskMemory &memory)
{
return get_filtered_layer_mask(
grease_pencil,
influence_data.layer_name[0] != '\0' ?
std::make_optional<StringRef>(influence_data.layer_name) :
std::nullopt,
(influence_data.flag & GREASE_PENCIL_INFLUENCE_USE_LAYER_PASS_FILTER) ?
std::make_optional<int>(influence_data.layer_pass) :
std::nullopt,
influence_data.flag & GREASE_PENCIL_INFLUENCE_INVERT_LAYER_FILTER,
influence_data.flag & GREASE_PENCIL_INFLUENCE_INVERT_LAYER_PASS_FILTER,
memory);
}
static IndexMask get_filtered_stroke_mask(const Object *ob,
const bke::CurvesGeometry &curves,
const Material *material_filter,
const std::optional<int> material_pass_filter,
const bool material_filter_invert,
const bool material_pass_filter_invert,
IndexMaskMemory &memory)
{
const IndexMask full_mask = curves.curves_range();
if (!material_filter && !material_pass_filter) {
return full_mask;
}
const int material_filter_index = BKE_object_material_index_get(
const_cast<Object *>(ob), const_cast<Material *>(material_filter));
const Vector<int> material_pass_by_index = get_grease_pencil_material_passes(ob);
bke::AttributeAccessor attributes = curves.attributes();
VArray<int> stroke_materials =
attributes.lookup_or_default<int>("material_index", bke::AttrDomain::Curve, 0).varray;
IndexMask result = IndexMask::from_predicate(
full_mask, GrainSize(4096), memory, [&](const int64_t stroke_i) {
const int material_index = stroke_materials.get(stroke_i);
if (material_filter != nullptr) {
const bool match = (material_index == material_filter_index);
if (match == material_filter_invert) {
return false;
}
}
if (material_pass_filter) {
const int material_pass = material_pass_by_index[material_index];
const bool match = (material_pass == material_pass_filter.value());
if (match == material_pass_filter_invert) {
return false;
}
}
return true;
});
return result;
}
IndexMask get_filtered_stroke_mask(const Object *ob,
const bke::CurvesGeometry &curves,
const GreasePencilModifierInfluenceData &influence_data,
IndexMaskMemory &memory)
{
return get_filtered_stroke_mask(
ob,
curves,
influence_data.material,
(influence_data.flag & GREASE_PENCIL_INFLUENCE_USE_MATERIAL_PASS_FILTER) ?
std::make_optional<int>(influence_data.material_pass) :
std::nullopt,
influence_data.flag & GREASE_PENCIL_INFLUENCE_INVERT_MATERIAL_FILTER,
influence_data.flag & GREASE_PENCIL_INFLUENCE_INVERT_MATERIAL_PASS_FILTER,
memory);
}
Vector<bke::greasepencil::Drawing *> get_drawings_for_write(GreasePencil &grease_pencil,
const IndexMask &layer_mask,
int frame)
{
/* Set of unique drawing indices. */
Set<int> drawing_indices;
for (const int64_t i : layer_mask.index_range()) {
Layer *layer = grease_pencil.layers_for_write()[layer_mask[i]];
const int drawing_index = layer->drawing_index_at(frame);
if (drawing_index >= 0) {
drawing_indices.add(drawing_index);
}
}
/* List of owned drawings, ignore drawing references to other data blocks. */
Vector<bke::greasepencil::Drawing *> drawings;
for (const int drawing_index : drawing_indices) {
GreasePencilDrawingBase *drawing_base = grease_pencil.drawing(drawing_index);
if (drawing_base->type == GP_DRAWING) {
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
drawings.append(&drawing->wrap());
}
}
return drawings;
}
} // namespace blender::modifier::greasepencil

View File

@ -0,0 +1,66 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup modifiers
*/
#pragma once
#include "BLI_index_mask.hh"
#include "BLI_vector.hh"
#include "BKE_modifier.hh"
struct ARegionType;
struct bContext;
struct GreasePencil;
struct GreasePencilModifierInfluenceData;
struct GreasePencilModifierLayerFilter;
struct GreasePencilModifierMaterialFilter;
struct PanelType;
struct PointerRNA;
struct uiLayout;
namespace blender::bke {
class CurvesGeometry;
namespace greasepencil {
class Drawing;
}
} // namespace blender::bke
namespace blender::modifier::greasepencil {
void init_influence_data(GreasePencilModifierInfluenceData *influence_data, bool has_custom_curve);
void copy_influence_data(const GreasePencilModifierInfluenceData *influence_data_src,
GreasePencilModifierInfluenceData *influence_data_dst,
int flag);
void free_influence_data(GreasePencilModifierInfluenceData *influence_data);
void foreach_influence_ID_link(GreasePencilModifierInfluenceData *influence_data,
Object *ob,
IDWalkFunc walk,
void *user_data);
void write_influence_data(BlendWriter *writer,
const GreasePencilModifierInfluenceData *influence_data);
void read_influence_data(BlendDataReader *reader,
GreasePencilModifierInfluenceData *influence_data);
void draw_layer_filter_settings(const bContext *C, uiLayout *layout, PointerRNA *ptr);
void draw_material_filter_settings(const bContext *C, uiLayout *layout, PointerRNA *ptr);
void draw_vertex_group_settings(const bContext *C, uiLayout *layout, PointerRNA *ptr);
void draw_custom_curve_settings(const bContext *C, uiLayout *layout, PointerRNA *ptr);
IndexMask get_filtered_layer_mask(const GreasePencil &grease_pencil,
const GreasePencilModifierInfluenceData &influence_data,
IndexMaskMemory &memory);
IndexMask get_filtered_stroke_mask(const Object *ob,
const bke::CurvesGeometry &curves,
const GreasePencilModifierInfluenceData &influence_data,
IndexMaskMemory &memory);
Vector<bke::greasepencil::Drawing *> get_drawings_for_write(GreasePencil &grease_pencil,
const IndexMask &layer_mask,
int frame);
} // namespace blender::modifier::greasepencil

View File

@ -270,5 +270,6 @@ void modifier_type_init(ModifierTypeInfo *types[])
INIT_TYPE(VolumeDisplace);
INIT_TYPE(VolumeToMesh);
INIT_TYPE(Nodes);
INIT_TYPE(GreasePencilOpacity);
#undef INIT_TYPE
}