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
5 changed files with 450 additions and 2 deletions
Showing only changes of commit dddd4eb52a - Show all commits

View File

@ -151,8 +151,8 @@ typedef enum {
*/
eModifierFlag_Active = (1 << 2),
/**
* Only set on modifiers in evaluated objects. The flag indicates that the user modified inputs
* to the modifier which might invalidate simulation caches.
* Only set on modifiers in evaluated objects. The flag indicates that the user modified
* inputs to the modifier which might invalidate simulation caches.
*/
LukasTonne marked this conversation as resolved Outdated

Looks like a clang-format issue here.

Looks like a `clang-format` issue here.
eModifierFlag_UserModified = (1 << 3),
} ModifierFlag;
@ -2484,3 +2484,26 @@ typedef enum VolumeToMeshResolutionMode {
typedef enum VolumeToMeshFlag {
VOLUME_TO_MESH_USE_SMOOTH_SHADE = 1 << 0,
} VolumeToMeshFlag;
typedef struct GreasePencilModifierFilterData {
/** 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;
/** GreasePencilModifierFilterFlag */
int flag;
char _pad[4];
} GreasePencilModifierFilterData;
typedef enum GreasePencilModifierFilterFlag {
GREASE_PENCIL_FILTER_USE_LAYER_PASS = (1 << 0),
GREASE_PENCIL_FILTER_USE_MATERIAL_PASS = (1 << 1),
GREASE_PENCIL_FILTER_INVERT_LAYER = (1 << 2),
GREASE_PENCIL_FILTER_INVERT_LAYER_PASS = (1 << 3),
GREASE_PENCIL_FILTER_INVERT_MATERIAL = (1 << 4),
GREASE_PENCIL_FILTER_INVERT_MATERIAL_PASS = (1 << 5),
} GreasePencilModifierFilterFlag;

View File

@ -668,11 +668,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_grease_pencil.hh"
# include "BKE_mesh_runtime.hh"
# include "BKE_modifier.hh"
# include "BKE_object.hh"
@ -1705,6 +1707,37 @@ static IDProperty **rna_NodesModifier_properties(PointerRNA *ptr)
NodesModifierSettings *settings = &nmd->settings;
return &settings->properties;
}
bool rna_GreasePencilModifierFilter_material_poll(PointerRNA *ptr, PointerRNA value)
{
Object *ob = reinterpret_cast<Object *>(ptr->owner_id);
Material *ma = reinterpret_cast<Material *>(value.owner_id);
return BKE_grease_pencil_object_material_index_get(ob, ma) != -1;
}
static void rna_GreasePencilModifierFilter_material_set(PointerRNA *ptr,
PointerRNA value,
ReportList *reports)
{
Object *ob = reinterpret_cast<Object *>(ptr->owner_id);
GreasePencilModifierFilterData *filter_data = static_cast<GreasePencilModifierFilterData *>(
ptr->data);
Material *ma = reinterpret_cast<Material *>(value.owner_id);
if (ma == nullptr || BKE_grease_pencil_object_material_index_get(ob, ma) != -1) {
id_lib_extern(reinterpret_cast<ID *>(ob));
filter_data->material = 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);
}
}
#else
static void rna_def_modifier_panel_open_prop(StructRNA *srna, const char *identifier, const int id)
@ -7423,6 +7456,78 @@ static void rna_def_modifier_volume_to_mesh(BlenderRNA *brna)
RNA_define_lib_overridable(false);
}
static void rna_def_modifier_grease_pencil_filter(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "GreasePencilModifierFilter", nullptr);
RNA_def_struct_ui_text(
srna, "Grease Pencil Modifier Filter", "Filter settings for grease pencil modifiers");
RNA_def_struct_sdna(srna, "GreasePencilModifierFilterData");
RNA_define_lib_overridable(true);
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, nullptr, "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", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GREASE_PENCIL_FILTER_USE_LAYER_PASS);
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", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "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", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GREASE_PENCIL_FILTER_INVERT_LAYER);
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", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GREASE_PENCIL_FILTER_INVERT_LAYER_PASS);
RNA_def_property_ui_text(prop, "Invert Layer Pass", "Invert layer pass filter");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_pointer_funcs(prop,
nullptr,
"rna_GreasePencilModifierFilter_material_set",
nullptr,
"rna_GreasePencilModifierFilter_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", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GREASE_PENCIL_FILTER_USE_MATERIAL_PASS);
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", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "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", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GREASE_PENCIL_FILTER_INVERT_MATERIAL);
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", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GREASE_PENCIL_FILTER_INVERT_MATERIAL_PASS);
RNA_def_property_ui_text(prop, "Invert Material Pass", "Invert material pass filter");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
RNA_define_lib_overridable(false);
}
void RNA_def_modifier(BlenderRNA *brna)
{
StructRNA *srna;
@ -7583,6 +7688,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_filter(brna);
}
#endif

View File

@ -44,6 +44,7 @@ set(SRC
intern/MOD_edgesplit.cc
intern/MOD_explode.cc
intern/MOD_fluid.cc
intern/MOD_grease_pencil_util.cc
intern/MOD_hook.cc
intern/MOD_laplaciandeform.cc
intern/MOD_laplaciansmooth.cc
@ -97,6 +98,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

@ -0,0 +1,260 @@
/* SPDX-FileCopyrightText: 2011 by Bastien Montagne. All rights reserved.
*
* 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_curves.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_lib_query.h"
#include "BKE_material.h"
#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::greasepencil {
using bke::greasepencil::Drawing;
using bke::greasepencil::Layer;
void init_filter_data(GreasePencilModifierFilterData * /*filter_data*/) {}
void copy_filter_data(const GreasePencilModifierFilterData *filter_data_src,
GreasePencilModifierFilterData *filter_data_dst,
const int /*flag*/)
{
memcpy(filter_data_dst, filter_data_src, sizeof(GreasePencilModifierFilterData));
}
void free_filter_data(GreasePencilModifierFilterData * /*filter_data*/) {}
void foreach_filter_ID_link(GreasePencilModifierFilterData *filter_data,
Object *ob,
IDWalkFunc walk,
void *user_data)
{
walk(user_data, ob, (ID **)&filter_data->material, IDWALK_CB_USER);
}
void draw_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");
const bool use_material_pass = RNA_boolean_get(ptr, "use_material_pass");
uiLayout *row, *col, *sub;
uiLayoutSetPropSep(layout, true);
col = uiLayoutColumn(layout, true);
row = uiLayoutRow(col, true);
uiItemPointerR(row, ptr, "layer", &obj_data_ptr, "layers", nullptr, ICON_GREASEPENCIL);
sub = uiLayoutRow(row, true);
uiLayoutSetPropDecorate(sub, false);
uiItemR(sub, ptr, "invert_layer", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT);
/* TODO Would be nice to have the checkbox in the same line as the pass button. */
row = uiLayoutRow(col, true);
uiItemR(row, ptr, "use_layer_pass", UI_ITEM_NONE, "Filter by layer pass", ICON_NONE);
row = uiLayoutRow(col, true);
uiLayoutSetActive(row, use_layer_pass);
uiItemR(row, ptr, "layer_pass", UI_ITEM_NONE, nullptr, ICON_NONE);
sub = uiLayoutRow(row, true);
uiLayoutSetPropDecorate(sub, false);
uiItemR(sub, ptr, "invert_layer_pass", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT);
col = uiLayoutColumn(layout, true);
row = uiLayoutRow(col, true);
uiItemPointerR(row, ptr, "material", &obj_data_ptr, "materials", nullptr, ICON_SHADING_TEXTURE);
sub = uiLayoutRow(row, true);
uiLayoutSetPropDecorate(sub, false);
uiItemR(sub, ptr, "invert_material", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT);
/* TODO Would be nice to have the checkbox in the same line as the pass button. */
row = uiLayoutRow(col, true);
uiItemR(row, ptr, "use_material_pass", UI_ITEM_NONE, "Filter by material pass", ICON_NONE);
row = uiLayoutRow(col, true);
uiLayoutSetActive(row, use_material_pass);
uiItemR(row, ptr, "material_pass", UI_ITEM_NONE, nullptr, ICON_NONE);
sub = uiLayoutRow(row, true);
uiLayoutSetPropDecorate(sub, false);
uiItemR(sub, ptr, "invert_material_pass", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT);
}
/**
* 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;
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.
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 GreasePencilModifierFilterData &filter_data,
IndexMaskMemory &memory)
{
return get_filtered_layer_mask(grease_pencil,
filter_data.layer_name[0] != '\0' ?
std::make_optional<StringRef>(filter_data.layer_name) :
std::nullopt,
(filter_data.flag & GREASE_PENCIL_FILTER_USE_LAYER_PASS) ?
std::make_optional<int>(filter_data.layer_pass) :
std::nullopt,
filter_data.flag & GREASE_PENCIL_FILTER_INVERT_LAYER,
filter_data.flag & GREASE_PENCIL_FILTER_INVERT_LAYER_PASS,
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_grease_pencil_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 GreasePencilModifierFilterData &filter_data,
IndexMaskMemory &memory)
{
/* TODO Add an option to toggle pass filter on and off, instead of using "pass > 0". */
return get_filtered_stroke_mask(ob,
curves,
filter_data.material,
(filter_data.flag & GREASE_PENCIL_FILTER_USE_MATERIAL_PASS) ?
std::make_optional<int>(filter_data.material_pass) :
std::nullopt,
filter_data.flag & GREASE_PENCIL_FILTER_INVERT_MATERIAL,
filter_data.flag & GREASE_PENCIL_FILTER_INVERT_MATERIAL_PASS,
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::greasepencil

View File

@ -0,0 +1,57 @@
/* SPDX-FileCopyrightText: 2011 by Bastien Montagne. All rights reserved.
*
* 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 GreasePencilModifierFilterData;
struct PanelType;
struct PointerRNA;
struct uiLayout;
namespace blender::bke {
class CurvesGeometry;
namespace greasepencil {
class Drawing;
}
} // namespace blender::bke
namespace blender::greasepencil {
void init_filter_data(GreasePencilModifierFilterData *filter_data);
void copy_filter_data(const GreasePencilModifierFilterData *filter_data_src,
GreasePencilModifierFilterData *filter_data_dst,
int flag);
void free_filter_data(GreasePencilModifierFilterData *filter_data);
void foreach_filter_ID_link(GreasePencilModifierFilterData *filter_data,
Object *ob,
IDWalkFunc walk,
void *user_data);
void draw_filter_settings(const bContext *C, uiLayout *layout, PointerRNA *ptr);
IndexMask get_filtered_layer_mask(const GreasePencil &grease_pencil,
const GreasePencilModifierFilterData &filter_data,
IndexMaskMemory &memory);
IndexMask get_filtered_stroke_mask(const Object *ob,
const bke::CurvesGeometry &curves,
const GreasePencilModifierFilterData &filter_data,
IndexMaskMemory &memory);
Vector<bke::greasepencil::Drawing *> get_drawings_for_write(GreasePencil &grease_pencil,
const IndexMask &layer_mask,
int frame);
} // namespace blender::greasepencil