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
4 changed files with 161 additions and 6 deletions
Showing only changes of commit 2fe5dd11a1 - Show all commits

View File

@ -2486,7 +2486,29 @@ typedef enum VolumeToMeshFlag {
VOLUME_TO_MESH_USE_SMOOTH_SHADE = 1 << 0,
} VolumeToMeshFlag;
typedef struct GreasePencilModifierFilterData {
/** Filter by layer name. */
char layername[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_INVERT_LAYER = (1 << 0),
GREASE_PENCIL_FILTER_INVERT_LAYER_PASS = (1 << 1),
GREASE_PENCIL_FILTER_INVERT_MATERIAL = (1 << 2),
GREASE_PENCIL_FILTER_INVERT_MATERIAL_PASS = (1 << 3),
} GreasePencilModifierFilterFlag;
typedef struct GreasePencilOpacityModifierData {
ModifierData modifier;
GreasePencilModifierFilterData filter;
void *_pad;
} GreasePencilOpacityModifierData;

View File

@ -36,6 +36,8 @@
#include "MOD_modifiertypes.hh"
#include "MOD_ui_common.hh"
#include <iostream>
namespace blender {
using bke::greasepencil::Drawing;
@ -48,7 +50,14 @@ static void init_data(ModifierData *md)
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(omd, modifier));
MEMCPY_STRUCT_AFTER(omd, DNA_struct_default_get(GreasePencilOpacityModifierData), modifier);
// XXX Why is this crashing, but the expanded code below works fine?!?
// MEMCPY_STRUCT_AFTER(omd, DNA_struct_default_get(GreasePencilOpacityModifierData), modifier);
{
CHECK_TYPE_NONCONST(omd);
memcpy((char *)(omd) + OFFSETOF_STRUCT_AFTER(omd, modifier),
(const char *)(md) + OFFSETOF_STRUCT_AFTER(omd, modifier),
sizeof(*(omd)) - OFFSETOF_STRUCT_AFTER(omd, modifier));
}
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
// TODO
}
@ -113,7 +122,11 @@ static void modify_geometry_set(ModifierData *md,
return;
}
Vector<Drawing *> drawings = greasepencil::get_drawings_for_write(*grease_pencil, frame);
IndexMaskMemory mask_memory;
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.
IndexMask layer_mask = greasepencil::get_filtered_layer_mask(
*grease_pencil, omd->filter, mask_memory);
Vector<Drawing *> drawings = greasepencil::get_drawings_for_write(
*grease_pencil, layer_mask, frame);
for (Drawing *drawing : drawings) {
modify_curves(md, ctx, drawing->strokes_for_write());
}

View File

@ -11,19 +11,132 @@
#include "BLI_set.hh"
#include "DNA_grease_pencil_types.h"
#include "DNA_material_types.h"
#include "DNA_modifier_types.h"
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_material.h"
#include "DEG_depsgraph_query.hh"
namespace blender::greasepencil {
using bke::greasepencil::Drawing;
using bke::greasepencil::Layer;
Vector<bke::greasepencil::Drawing *> get_drawings_for_write(GreasePencil &grease_pencil, int frame)
/**
* 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)
{
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(
grease_pencil.layers().index_range(), 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)
{
/* TODO Add an option to toggle pass filter on and off, instead of using "pass > 0". */
return get_filtered_layer_mask(
grease_pencil,
filter_data.layername[0] != '\0' ? std::make_optional<StringRef>(filter_data.layername) :
std::nullopt,
filter_data.layer_pass > 0 ? 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 Vector<bool> is_material_affected_by_modifier(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)
{
bke::AttributeAccessor attributes = curves.attributes();
VArray<int> stroke_materials =
attributes.lookup_or_default<int>("material_index", bke::AttrDomain::Curve, 0).varray;
Vector<bool> result(curves.curves_num(), true);
if (material_filter != nullptr) {
const int material_filter_index = BKE_grease_pencil_object_material_index_get(
const_cast<Object *>(ob), const_cast<Material *>(material_filter));
for (const int stroke_i : result.index_range()) {
const int material_index = stroke_materials.get(stroke_i);
const bool match = (material_index == material_filter_index);
if (match ^ material_filter_invert) {
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.
result[stroke_i] = false;
}
}
}
if (material_pass_filter) {
const Vector<int> material_pass_by_index = get_grease_pencil_material_passes(ob);
for (const int stroke_i : result.index_range()) {
const int material_index = stroke_materials.get(stroke_i);
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) {
result[stroke_i] = false;
}
}
}
return result;
}
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 (Layer *layer : grease_pencil.layers_for_write()) {
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);

View File

@ -8,16 +8,23 @@
#pragma once
#include "BLI_index_mask.hh"
#include "BLI_vector.hh"
struct GreasePencil;
struct GreasePencilModifierFilterData;
namespace blender::bke::greasepencil {
class Drawing;
}
namespace blender::greasepencil {
IndexMask get_filtered_layer_mask(const GreasePencil &grease_pencil,
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