GPv3: Mirror modifier #117637

Merged
Lukas Tönne merged 16 commits from LukasTonne/blender:gp3-mirror-modifier into main 2024-01-30 12:10:42 +01:00
9 changed files with 379 additions and 1 deletions

View File

@ -150,6 +150,7 @@ class OBJECT_MT_modifier_add_generate(ModifierAddMenu, Menu):
if ob_type == 'MESH':
self.operator_modifier_add(layout, 'WIREFRAME')
if ob_type == 'GREASEPENCIL':
self.operator_modifier_add(layout, 'GREASE_PENCIL_MIRROR')
self.operator_modifier_add(layout, 'GREASE_PENCIL_SUBDIV')
filedescriptor marked this conversation as resolved Outdated

I guess this line is not necessary.

I guess this line is not necessary.

Could put this inside the previous if statement

Could put this inside the previous if statement
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)

View File

@ -860,4 +860,10 @@
.seed = 1, \
}
#define _DNA_DEFAULT_GreasePencilMirrorModifierData \
{ \
.flag = MOD_GREASE_PENCIL_MIRROR_AXIS_X, \
}
/* clang-format off */

View File

@ -100,6 +100,7 @@ typedef enum ModifierType {
eModifierType_GreasePencilSmooth = 65,
eModifierType_GreasePencilOffset = 66,
eModifierType_GreasePencilNoise = 67,
eModifierType_GreasePencilMirror = 68,
NUM_MODIFIER_TYPES,
} ModifierType;
@ -2621,7 +2622,6 @@ typedef struct GreasePencilSmoothModifierData {
float factor;
/** How many times apply smooth. */
int step;
char _pad[4];
void *_pad1;
} GreasePencilSmoothModifierData;
@ -2693,3 +2693,18 @@ typedef struct GreasePencilNoiseModifierData {
void *_pad1;
} GreasePencilNoiseModifierData;
typedef struct GreasePencilMirrorModifierData {
ModifierData modifier;
GreasePencilModifierInfluenceData influence;
struct Object *object;
/** #GreasePencilMirrorModifierFlag */
int flag;
char _pad[4];
} GreasePencilMirrorModifierData;
typedef enum GreasePencilMirrorModifierFlag {
MOD_GREASE_PENCIL_MIRROR_AXIS_X = (1 << 0),
MOD_GREASE_PENCIL_MIRROR_AXIS_Y = (1 << 1),
MOD_GREASE_PENCIL_MIRROR_AXIS_Z = (1 << 2),
} GreasePencilMirrorModifierFlag;

View File

@ -331,6 +331,7 @@ SDNA_DEFAULT_DECL_STRUCT(GreasePencilOpacityModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilColorModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilTintModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilOffsetModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilMirrorModifierData);
#undef SDNA_DEFAULT_DECL_STRUCT
@ -584,6 +585,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
SDNA_DEFAULT_DECL(GreasePencilColorModifierData),
SDNA_DEFAULT_DECL(GreasePencilTintModifierData),
SDNA_DEFAULT_DECL(GreasePencilOffsetModifierData),
SDNA_DEFAULT_DECL(GreasePencilMirrorModifierData),
};
#undef SDNA_DEFAULT_DECL
#undef SDNA_DEFAULT_DECL_EX

View File

@ -216,6 +216,11 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
ICON_MOD_SUBSURF,
"Subdivide strokes",
"Grease Pencil subdivide modifier"},
{eModifierType_GreasePencilMirror,
"GREASE_PENCIL_MIRROR",
ICON_MOD_MIRROR,
"Mirror strokes",
"Duplicate strokes like a mirror"},
RNA_ENUM_ITEM_HEADING(N_("Deform"), nullptr),
{eModifierType_Armature,
@ -1829,6 +1834,7 @@ static void rna_GreasePencilModifier_material_set(PointerRNA *ptr,
}
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilColor);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilMirror);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilOffset);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilOpacity);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilSubdiv);
@ -1874,6 +1880,17 @@ static void rna_GreasePencilTintModifier_object_set(PointerRNA *ptr,
id_lib_extern(&ob->id);
}
static void rna_GreasePencilMirrorModifier_object_set(PointerRNA *ptr,
PointerRNA value,
ReportList * /*reports*/)
{
GreasePencilMirrorModifierData *mmd = static_cast<GreasePencilMirrorModifierData *>(ptr->data);
Object *ob = static_cast<Object *>(value.data);
mmd->object = ob;
id_lib_extern(&ob->id);
}
#else
static void rna_def_modifier_panel_open_prop(StructRNA *srna, const char *identifier, const int id)
@ -8227,6 +8244,47 @@ static void rna_def_modifier_grease_pencil_noise(BlenderRNA *brna)
RNA_define_lib_overridable(false);
}
static void rna_def_modifier_grease_pencil_mirror(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "GreasePencilMirrorModifier", "Modifier");
RNA_def_struct_ui_text(srna, "Grease Pencil Mirror Modifier", "");
RNA_def_struct_sdna(srna, "GreasePencilMirrorModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_MIRROR);
rna_def_modifier_grease_pencil_layer_filter(srna);
rna_def_modifier_grease_pencil_material_filter(
srna, "rna_GreasePencilMirrorModifier_material_filter_set");
RNA_define_lib_overridable(true);
prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
RNA_def_property_ui_text(prop, "Object", "Object used as center");
RNA_def_property_pointer_funcs(
prop, nullptr, "rna_GreasePencilMirrorModifier_object_set", nullptr, nullptr);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update");
prop = RNA_def_property(srna, "use_axis_x", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", MOD_GREASE_PENCIL_MIRROR_AXIS_X);
RNA_def_property_ui_text(prop, "X", "Mirror the X axis");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_axis_y", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", MOD_GREASE_PENCIL_MIRROR_AXIS_Y);
RNA_def_property_ui_text(prop, "Y", "Mirror the Y axis");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_axis_z", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", MOD_GREASE_PENCIL_MIRROR_AXIS_Z);
RNA_def_property_ui_text(prop, "Z", "Mirror the Z axis");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
RNA_define_lib_overridable(false);
}
void RNA_def_modifier(BlenderRNA *brna)
{
StructRNA *srna;
@ -8394,6 +8452,7 @@ void RNA_def_modifier(BlenderRNA *brna)
rna_def_modifier_grease_pencil_smooth(brna);
rna_def_modifier_grease_pencil_offset(brna);
rna_def_modifier_grease_pencil_noise(brna);
rna_def_modifier_grease_pencil_mirror(brna);
}
#endif

View File

@ -45,6 +45,7 @@ set(SRC
intern/MOD_explode.cc
intern/MOD_fluid.cc
intern/MOD_grease_pencil_color.cc
intern/MOD_grease_pencil_mirror.cc
intern/MOD_grease_pencil_noise.cc
intern/MOD_grease_pencil_offset.cc
intern/MOD_grease_pencil_opacity.cc

View File

@ -80,6 +80,7 @@ extern ModifierTypeInfo modifierType_GreasePencilTint;
extern ModifierTypeInfo modifierType_GreasePencilSmooth;
extern ModifierTypeInfo modifierType_GreasePencilOffset;
extern ModifierTypeInfo modifierType_GreasePencilNoise;
extern ModifierTypeInfo modifierType_GreasePencilMirror;
/* MOD_util.cc */

View File

@ -0,0 +1,292 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup modifiers
*/
#include "DNA_defaults.h"
LukasTonne marked this conversation as resolved Outdated

Some unused includes here now I think

Some unused includes here now I think
#include "DNA_modifier_types.h"
#include "BKE_curves.hh"
#include "BKE_geometry_set.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_instances.hh"
#include "BKE_lib_query.hh"
#include "BKE_material.h"
#include "BKE_modifier.hh"
#include "BKE_screen.hh"
#include "BLO_read_write.hh"
#include "GEO_realize_instances.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "BLT_translation.h"
#include "WM_types.hh"
#include "RNA_access.hh"
#include "RNA_prototypes.h"
#include "MOD_grease_pencil_util.hh"
#include "MOD_modifiertypes.hh"
#include "MOD_ui_common.hh"
namespace blender {
static void init_data(ModifierData *md)
{
auto *mmd = reinterpret_cast<GreasePencilMirrorModifierData *>(md);
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(mmd, modifier));
MEMCPY_STRUCT_AFTER(mmd, DNA_struct_default_get(GreasePencilMirrorModifierData), modifier);
modifier::greasepencil::init_influence_data(&mmd->influence, false);
}
static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
{
const auto *mmd = reinterpret_cast<const GreasePencilMirrorModifierData *>(md);
auto *tmmd = reinterpret_cast<GreasePencilMirrorModifierData *>(target);
modifier::greasepencil::free_influence_data(&tmmd->influence);
BKE_modifier_copydata_generic(md, target, flag);
modifier::greasepencil::copy_influence_data(&mmd->influence, &tmmd->influence, flag);
}
static void free_data(ModifierData *md)
{
auto *mmd = reinterpret_cast<GreasePencilMirrorModifierData *>(md);
modifier::greasepencil::free_influence_data(&mmd->influence);
}
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
{
auto *mmd = reinterpret_cast<GreasePencilMirrorModifierData *>(md);
walk(user_data, ob, (ID **)&mmd->object, IDWALK_CB_NOP);
modifier::greasepencil::foreach_influence_ID_link(&mmd->influence, ob, walk, user_data);
}
static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
auto *mmd = reinterpret_cast<GreasePencilMirrorModifierData *>(md);
if (mmd->object != nullptr) {
DEG_add_object_relation(
ctx->node, mmd->object, DEG_OB_COMP_TRANSFORM, "Grease Pencil Mirror Modifier");
DEG_add_depends_on_transform_relation(ctx->node, "Grease Pencil Mirror Modifier");
}
}
static float4x4 get_mirror_matrix(const Object &ob,
const GreasePencilMirrorModifierData &mmd,
const bool mirror_x,
const bool mirror_y,
const bool mirror_z)
{
float4x4 matrix = math::from_scale<float4x4, 3>(
float3(mirror_x ? -1.0f : 1.0f, mirror_y ? -1.0f : 1.0f, mirror_z ? -1.0f : 1.0f));
if (mmd.object) {
/* Transforms from parent object space to target object space. */
const float4x4 to_target = math::invert(float4x4(mmd.object->object_to_world)) *
float4x4(ob.object_to_world);
/* Mirror points in the target object space. */
matrix = math::invert(to_target) * matrix * to_target;
}
return matrix;
}
static bke::CurvesGeometry create_mirror_copies(const Object &ob,
const GreasePencilMirrorModifierData &mmd,
const bke::CurvesGeometry &base_curves,
const bke::CurvesGeometry &mirror_curves)
{
const bool use_mirror_x = (mmd.flag & MOD_GREASE_PENCIL_MIRROR_AXIS_X);
const bool use_mirror_y = (mmd.flag & MOD_GREASE_PENCIL_MIRROR_AXIS_Y);
const bool use_mirror_z = (mmd.flag & MOD_GREASE_PENCIL_MIRROR_AXIS_Z);
Curves *base_curves_id = bke::curves_new_nomain(base_curves);
Curves *mirror_curves_id = bke::curves_new_nomain(mirror_curves);
bke::GeometrySet base_geo = bke::GeometrySet::from_curves(base_curves_id);
bke::GeometrySet mirror_geo = bke::GeometrySet::from_curves(mirror_curves_id);
std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
const int base_handle = instances->add_reference(bke::InstanceReference{base_geo});
const int mirror_handle = instances->add_reference(bke::InstanceReference{mirror_geo});
for (const int mirror_x : IndexRange(use_mirror_x ? 2 : 1)) {
for (const int mirror_y : IndexRange(use_mirror_y ? 2 : 1)) {
for (const int mirror_z : IndexRange(use_mirror_z ? 2 : 1)) {
if (mirror_x == 0 && mirror_y == 0 && mirror_z == 0) {
instances->add_instance(base_handle, float4x4::identity());
}
else {
const float4x4 matrix = get_mirror_matrix(
ob, mmd, bool(mirror_x), bool(mirror_y), bool(mirror_z));
instances->add_instance(mirror_handle, matrix);
}
}
}
}
geometry::RealizeInstancesOptions options;
options.keep_original_ids = true;
options.realize_instance_attributes = false;
options.propagation_info = {};
bke::GeometrySet result_geo = geometry::realize_instances(
bke::GeometrySet::from_instances(instances.release()), options);
return std::move(result_geo.get_curves_for_write()->geometry.wrap());
}
static void modify_drawing(const GreasePencilMirrorModifierData &mmd,
const ModifierEvalContext &ctx,
bke::greasepencil::Drawing &drawing)
{
const bool use_mirror_x = (mmd.flag & MOD_GREASE_PENCIL_MIRROR_AXIS_X);
const bool use_mirror_y = (mmd.flag & MOD_GREASE_PENCIL_MIRROR_AXIS_Y);
const bool use_mirror_z = (mmd.flag & MOD_GREASE_PENCIL_MIRROR_AXIS_Z);
if (!use_mirror_x && !use_mirror_y && !use_mirror_z) {
return;
}
const bke::CurvesGeometry &src_curves = drawing.strokes();
if (src_curves.curve_num == 0) {
return;
}
/* Selected source curves. */
IndexMaskMemory curve_mask_memory;
const IndexMask curves_mask = modifier::greasepencil::get_filtered_stroke_mask(
ctx.object, src_curves, mmd.influence, curve_mask_memory);
if (curves_mask.size() == src_curves.curve_num) {
LukasTonne marked this conversation as resolved Outdated

To avoid the operator= assigment, I'd write it like this:

bke::CurvesGeometry result = [&]() {
  if (curves_mask.size() == src_curves.curve_num) {
    /* All geometry gets mirrored. */
    return create_mirror_copies(*ctx.object, mmd, src_curves, src_curves);
  }
  else {
    /* Create masked geometry, then mirror it. */
    bke::CurvesGeometry masked_curves = bke::curves_copy_curve_selection(
        src_curves, curves_mask, {});
    return create_mirror_copies(*ctx.object, mmd, src_curves, masked_curves);
  }
}();
To avoid the `operator=` assigment, I'd write it like this: ``` bke::CurvesGeometry result = [&]() { if (curves_mask.size() == src_curves.curve_num) { /* All geometry gets mirrored. */ return create_mirror_copies(*ctx.object, mmd, src_curves, src_curves); } else { /* Create masked geometry, then mirror it. */ bke::CurvesGeometry masked_curves = bke::curves_copy_curve_selection( src_curves, curves_mask, {}); return create_mirror_copies(*ctx.object, mmd, src_curves, masked_curves); } }(); ```

I just moved the assignment into each branch, no need for a local variable.

I just moved the assignment into each branch, no need for a local variable.
/* All geometry gets mirrored. */
drawing.strokes_for_write() = create_mirror_copies(*ctx.object, mmd, src_curves, src_curves);
}
else {
/* Create masked geometry, then mirror it. */
bke::CurvesGeometry masked_curves = bke::curves_copy_curve_selection(
src_curves, curves_mask, {});
drawing.strokes_for_write() = create_mirror_copies(
*ctx.object, mmd, src_curves, masked_curves);
}
drawing.tag_topology_changed();
}
static void modify_geometry_set(ModifierData *md,
LukasTonne marked this conversation as resolved Outdated

I think just passing {} to the function directly is a simpler way to do the same thing. Same above IMO

I think just passing `{}` to the function directly is a simpler way to do the same thing. Same above IMO
const ModifierEvalContext *ctx,
bke::GeometrySet *geometry_set)
{
using bke::greasepencil::Drawing;
auto *mmd = reinterpret_cast<GreasePencilMirrorModifierData *>(md);
if (!geometry_set->has_grease_pencil()) {
return;
}
GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write();
const int frame = grease_pencil.runtime->eval_frame;
IndexMaskMemory mask_memory;
const IndexMask layer_mask = modifier::greasepencil::get_filtered_layer_mask(
grease_pencil, mmd->influence, mask_memory);
const Vector<Drawing *> drawings = modifier::greasepencil::get_drawings_for_write(
grease_pencil, layer_mask, frame);
threading::parallel_for_each(drawings,
[&](Drawing *drawing) { modify_drawing(*mmd, *ctx, *drawing); });
}
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);
const eUI_Item_Flag toggles_flag = UI_ITEM_R_TOGGLE | UI_ITEM_R_FORCE_BLANK_DECORATE;
uiLayoutSetPropSep(layout, true);
uiLayout *row = uiLayoutRowWithHeading(layout, true, IFACE_("Axis"));
uiItemR(row, ptr, "use_axis_x", toggles_flag, nullptr, ICON_NONE);
uiItemR(row, ptr, "use_axis_y", toggles_flag, nullptr, ICON_NONE);
uiItemR(row, ptr, "use_axis_z", toggles_flag, nullptr, ICON_NONE);
uiItemR(layout, ptr, "object", UI_ITEM_NONE, nullptr, ICON_NONE);
LayoutPanelState *influence_panel_state = BKE_panel_layout_panel_state_ensure(
panel, "influence", true);
PointerRNA influence_state_ptr = RNA_pointer_create(
nullptr, &RNA_LayoutPanelState, influence_panel_state);
if (uiLayout *influence_panel = uiLayoutPanel(
C, layout, "Influence", &influence_state_ptr, "is_open"))
{
modifier::greasepencil::draw_layer_filter_settings(C, influence_panel, ptr);
modifier::greasepencil::draw_material_filter_settings(C, influence_panel, ptr);
}
modifier_panel_end(layout, ptr);
}
static void panel_register(ARegionType *region_type)
{
modifier_panel_register(region_type, eModifierType_GreasePencilMirror, panel_draw);
}
static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
{
const auto *mmd = reinterpret_cast<const GreasePencilMirrorModifierData *>(md);
BLO_write_struct(writer, GreasePencilMirrorModifierData, mmd);
modifier::greasepencil::write_influence_data(writer, &mmd->influence);
}
static void blend_read(BlendDataReader *reader, ModifierData *md)
{
auto *mmd = reinterpret_cast<GreasePencilMirrorModifierData *>(md);
modifier::greasepencil::read_influence_data(reader, &mmd->influence);
}
} // namespace blender
ModifierTypeInfo modifierType_GreasePencilMirror = {
/*idname*/ "GreasePencilMirror",
/*name*/ N_("Mirror"),
/*struct_name*/ "GreasePencilMirrorModifierData",
/*struct_size*/ sizeof(GreasePencilMirrorModifierData),
/*srna*/ &RNA_GreasePencilMirrorModifier,
/*type*/ ModifierTypeType::Constructive,
/*flags*/ eModifierTypeFlag_AcceptsGreasePencil | eModifierTypeFlag_SupportsEditmode |
eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping,
/*icon*/ ICON_MOD_MIRROR,
/*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*/ blender::update_depsgraph,
/*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

@ -277,5 +277,6 @@ void modifier_type_init(ModifierTypeInfo *types[])
INIT_TYPE(GreasePencilSmooth);
INIT_TYPE(GreasePencilOffset);
INIT_TYPE(GreasePencilNoise);
INIT_TYPE(GreasePencilMirror);
#undef INIT_TYPE
}