From a947314bf99b8f129f71cabf2fbe266fb6a7eef3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Wed, 31 Jan 2024 16:07:45 +0100 Subject: [PATCH 01/16] Boilerplate for the Dash modifier. --- .../startup/bl_ui/properties_data_modifier.py | 1 + .../blender/makesdna/DNA_modifier_defaults.h | 18 + source/blender/makesdna/DNA_modifier_types.h | 35 ++ source/blender/makesdna/intern/dna_defaults.c | 2 + .../blender/makesrna/intern/rna_modifier.cc | 188 ++++++++++ source/blender/modifiers/CMakeLists.txt | 1 + source/blender/modifiers/MOD_modifiertypes.hh | 1 + .../intern/MOD_grease_pencil_dash.cc | 334 ++++++++++++++++++ source/blender/modifiers/intern/MOD_util.cc | 1 + 9 files changed, 581 insertions(+) create mode 100644 source/blender/modifiers/intern/MOD_grease_pencil_dash.cc diff --git a/scripts/startup/bl_ui/properties_data_modifier.py b/scripts/startup/bl_ui/properties_data_modifier.py index 3f82086ccd3..0f02ea65b44 100644 --- a/scripts/startup/bl_ui/properties_data_modifier.py +++ b/scripts/startup/bl_ui/properties_data_modifier.py @@ -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_DASH') self.operator_modifier_add(layout, 'GREASE_PENCIL_MIRROR') self.operator_modifier_add(layout, 'GREASE_PENCIL_SUBDIV') layout.template_modifier_asset_menu_items(catalog_path=self.bl_label) diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h index fe87f96b2ea..0ec56cbaa21 100644 --- a/source/blender/makesdna/DNA_modifier_defaults.h +++ b/source/blender/makesdna/DNA_modifier_defaults.h @@ -872,4 +872,22 @@ .thickness = 0.02, \ } +#define _DNA_DEFAULT_GreasePencilDashModifierData \ + { \ + .dash_offset = 0, \ + .segments_array = NULL, \ + .segments_num = 1, \ + .segment_active_index = 0, \ + } + +#define _DNA_DEFAULT_GreasePencilDashModifierSegment \ + { \ + .name = "", \ + .dash = 2, \ + .gap = 1, \ + .radius = 1.0f, \ + .opacity = 1.0f, \ + .mat_nr = -1, \ + } + /* clang-format off */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index c53dc3d90e9..7d37bb72f86 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -15,6 +15,8 @@ #include "DNA_session_uid_types.h" #ifdef __cplusplus +# include "BLI_span.hh" + namespace blender { struct NodesModifierRuntime; } @@ -102,6 +104,7 @@ typedef enum ModifierType { eModifierType_GreasePencilNoise = 67, eModifierType_GreasePencilMirror = 68, eModifierType_GreasePencilThickness = 69, + eModifierType_GreasePencilDash = 70, NUM_MODIFIER_TYPES, } ModifierType; @@ -2727,3 +2730,35 @@ typedef enum GreasePencilThicknessModifierFlag { MOD_GREASE_PENCIL_THICK_NORMALIZE = (1 << 0), MOD_GREASE_PENCIL_THICK_WEIGHT_FACTOR = (1 << 1), } GreasePencilThicknessModifierFlag; + +typedef struct GreasePencilDashModifierSegment { + char name[64]; + int dash; + int gap; + float radius; + float opacity; + int mat_nr; + int flag; +} GreasePencilDashModifierSegment; + +typedef struct GreasePencilDashModifierData { + ModifierData modifier; + GreasePencilModifierInfluenceData influence; + /** #GreasePencilDashModifierFlag */ + int flag; + + int dash_offset; + + GreasePencilDashModifierSegment *segments_array; + int segments_num; + int segment_active_index; + +#ifdef __cplusplus + blender::Span segments() const; + blender::MutableSpan segments(); +#endif +} GreasePencilDashModifierData; + +typedef enum GreasePencilDashModifierFlag { + MOD_GREASE_PENCIL_DASH_USE_CYCLIC = (1 << 0), +} GreasePencilDashModifierFlag; diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index 4e363153c0e..d47af15e7be 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -333,6 +333,7 @@ SDNA_DEFAULT_DECL_STRUCT(GreasePencilTintModifierData); SDNA_DEFAULT_DECL_STRUCT(GreasePencilOffsetModifierData); SDNA_DEFAULT_DECL_STRUCT(GreasePencilMirrorModifierData); SDNA_DEFAULT_DECL_STRUCT(GreasePencilThickModifierData); +SDNA_DEFAULT_DECL_STRUCT(GreasePencilDashModifierData); #undef SDNA_DEFAULT_DECL_STRUCT @@ -588,6 +589,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = { SDNA_DEFAULT_DECL(GreasePencilOffsetModifierData), SDNA_DEFAULT_DECL(GreasePencilMirrorModifierData), SDNA_DEFAULT_DECL(GreasePencilThickModifierData), + SDNA_DEFAULT_DECL(GreasePencilDashModifierData), }; #undef SDNA_DEFAULT_DECL #undef SDNA_DEFAULT_DECL_EX diff --git a/source/blender/makesrna/intern/rna_modifier.cc b/source/blender/makesrna/intern/rna_modifier.cc index 864f7f56556..b66d4d777df 100644 --- a/source/blender/makesrna/intern/rna_modifier.cc +++ b/source/blender/makesrna/intern/rna_modifier.cc @@ -321,6 +321,11 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = { ICON_MOD_THICKNESS, "Thickness", "Change stroke thickness"}, + {eModifierType_GreasePencilDash, + "GREASE_PENCIL_DASH", + ICON_MOD_DASH, + "Dot Dash", + "Generate dot-dash styled strokes"}, RNA_ENUM_ITEM_HEADING(N_("Physics"), nullptr), {eModifierType_Cloth, "CLOTH", ICON_MOD_CLOTH, "Cloth", ""}, @@ -730,6 +735,7 @@ const EnumPropertyItem rna_enum_subdivision_boundary_smooth_items[] = { # include "BKE_particle.h" # include "BLI_sort_utils.h" +# include "BLI_string_utils.hh" # include "DEG_depsgraph.hh" # include "DEG_depsgraph_build.hh" @@ -1847,6 +1853,7 @@ RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilTint); RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilSmooth); RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilNoise); RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilThick); +RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilDash); RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilOffset); RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilOpacity); @@ -1898,6 +1905,80 @@ static void rna_GreasePencilMirrorModifier_object_set(PointerRNA *ptr, id_lib_extern(&ob->id); } +static const GreasePencilDashModifierData *find_grease_pencil_dash_modifier_of_segment( + const Object &ob, const GreasePencilDashModifierSegment &dash_segment) +{ + LISTBASE_FOREACH (const ModifierData *, md, &ob.modifiers) { + if (md->type == eModifierType_GreasePencilDash) { + const auto *dmd = reinterpret_cast(md); + if (dmd->segments().contains_ptr(&dash_segment)) { + return dmd; + } + } + } + return nullptr; +} + +static char *rna_GreasePencilDashModifierSegment_path(const PointerRNA *ptr) + +{ + const Object *ob = reinterpret_cast(ptr->owner_id); + const auto *dash_segment = static_cast(ptr->data); + const GreasePencilDashModifierData *dmd = find_grease_pencil_dash_modifier_of_segment( + *ob, *dash_segment); + BLI_assert(dmd != nullptr); + + char name_esc[sizeof(dmd->modifier.name) * 2]; + BLI_str_escape(name_esc, dmd->modifier.name, sizeof(name_esc)); + + char ds_name_esc[sizeof(dash_segment->name) * 2]; + BLI_str_escape(ds_name_esc, dash_segment->name, sizeof(ds_name_esc)); + + return BLI_sprintfN("modifiers[\"%s\"].segments[\"%s\"]", name_esc, ds_name_esc); +} + +static void rna_GreasePencilDashModifierSegment_name_set(PointerRNA *ptr, const char *value) +{ + const Object *ob = reinterpret_cast(ptr->owner_id); + auto *dash_segment = static_cast(ptr->data); + const GreasePencilDashModifierData *dmd = find_grease_pencil_dash_modifier_of_segment( + *ob, *dash_segment); + BLI_assert(dmd != nullptr); + + const std::string oldname = dash_segment->name; + STRNCPY_UTF8(dash_segment->name, value); + BLI_uniquename_cb( + [dmd, dash_segment](const blender::StringRef name) { + for (const GreasePencilDashModifierSegment &ds : dmd->segments()) { + if (&ds != dash_segment && ds.name == name) { + return true; + } + } + return false; + }, + '.', + dash_segment->name); + + /* Fix all the animation data which may link to this. */ + char name_esc[sizeof(dmd->modifier.name) * 2]; + BLI_str_escape(name_esc, dmd->modifier.name, sizeof(name_esc)); + char rna_path_prefix[36 + sizeof(name_esc) + 1]; + SNPRINTF(rna_path_prefix, "modifiers[\"%s\"].segments", name_esc); + BKE_animdata_fix_paths_rename_all(nullptr, rna_path_prefix, oldname.c_str(), dash_segment->name); +} + +static void rna_GreasePencilDashModifier_segments_begin(CollectionPropertyIterator *iter, + PointerRNA *ptr) +{ + auto *dmd = static_cast(ptr->data); + rna_iterator_array_begin(iter, + dmd->segments_array, + sizeof(GreasePencilDashModifierSegment), + dmd->segments_num, + false, + nullptr); +} + #else static void rna_def_modifier_panel_open_prop(StructRNA *srna, const char *identifier, const int id) @@ -8340,6 +8421,111 @@ static void rna_def_modifier_grease_pencil_thickness(BlenderRNA *brna) RNA_define_lib_overridable(false); } +static void rna_def_modifier_grease_pencil_dash_segment(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "GreasePencilDashModifierSegment", nullptr); + RNA_def_struct_ui_text(srna, "Dash Modifier Segment", "Configuration for a single dash segment"); + RNA_def_struct_sdna(srna, "GreasePencilDashModifierSegment"); + RNA_def_struct_path_func(srna, "rna_GreasePencilDashModifierSegment_path"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", "Name of the dash segment"); + RNA_def_property_string_funcs( + prop, nullptr, nullptr, "rna_GreasePencilDashModifierSegment_name_set"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER | NA_RENAME, nullptr); + RNA_def_struct_name_property(srna, prop); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "dash", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 1, INT16_MAX); + RNA_def_property_ui_text( + prop, + "Dash", + "The number of consecutive points from the original stroke to include in this segment"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "gap", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 0, INT16_MAX); + RNA_def_property_ui_text(prop, "Gap", "The number of points skipped after this segment"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "radius", PROP_FLOAT, PROP_FACTOR | PROP_UNSIGNED); + RNA_def_property_ui_range(prop, 0, 1, 0.1, 2); + RNA_def_property_ui_text( + prop, "Radius", "The factor to apply to the original point's radius for the new points"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "opacity", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_ui_range(prop, 0, 1, 0.1, 2); + RNA_def_property_ui_text( + prop, "Opacity", "The factor to apply to the original point's opacity for the new points"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, nullptr, "mat_nr"); + RNA_def_property_range(prop, -1, INT16_MAX); + RNA_def_property_ui_text( + prop, + "Material Index", + "Use this index on generated segment. -1 means using the existing material"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_cyclic", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", MOD_GREASE_PENCIL_DASH_USE_CYCLIC); + RNA_def_property_ui_text(prop, "Cyclic", "Enable cyclic on individual stroke dashes"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); +} + +static void rna_def_modifier_grease_pencil_dash(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "GreasePencilDashModifierData", "Modifier"); + RNA_def_struct_ui_text( + srna, "Grease Pencil Dash Modifier", "Create dot-dash effect for strokes"); + RNA_def_struct_sdna(srna, "GreasePencilDashModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_DASH); + + rna_def_modifier_grease_pencil_layer_filter(srna); + rna_def_modifier_grease_pencil_material_filter( + srna, "rna_GreasePencilDashModifier_material_filter_set"); + + rna_def_modifier_panel_open_prop(srna, "open_influence_panel", 0); + + RNA_define_lib_overridable(true); + + prop = RNA_def_property(srna, "segments", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "GreasePencilDashModifierSegment"); + RNA_def_property_collection_sdna(prop, nullptr, "segments_array", nullptr); + RNA_def_property_collection_funcs(prop, + "rna_GreasePencilDashModifier_segments_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_get", + nullptr, + nullptr, + nullptr, + nullptr); + RNA_def_property_ui_text(prop, "Segments", ""); + + prop = RNA_def_property(srna, "segment_active_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Active Dash Segment Index", "Active index in the segment list"); + + prop = RNA_def_property(srna, "dash_offset", PROP_INT, PROP_NONE); + RNA_def_property_ui_text( + prop, + "Offset", + "Offset into each stroke before the beginning of the dashed segment generation"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + RNA_define_lib_overridable(false); +} + void RNA_def_modifier(BlenderRNA *brna) { StructRNA *srna; @@ -8509,6 +8695,8 @@ void RNA_def_modifier(BlenderRNA *brna) rna_def_modifier_grease_pencil_noise(brna); rna_def_modifier_grease_pencil_mirror(brna); rna_def_modifier_grease_pencil_thickness(brna); + rna_def_modifier_grease_pencil_dash_segment(brna); + rna_def_modifier_grease_pencil_dash(brna); } #endif diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 1c78f7895c1..11e1ff5bb04 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -45,6 +45,7 @@ set(SRC intern/MOD_explode.cc intern/MOD_fluid.cc intern/MOD_grease_pencil_color.cc + intern/MOD_grease_pencil_dash.cc intern/MOD_grease_pencil_mirror.cc intern/MOD_grease_pencil_noise.cc intern/MOD_grease_pencil_offset.cc diff --git a/source/blender/modifiers/MOD_modifiertypes.hh b/source/blender/modifiers/MOD_modifiertypes.hh index 351abe1a0bd..623ed4f5487 100644 --- a/source/blender/modifiers/MOD_modifiertypes.hh +++ b/source/blender/modifiers/MOD_modifiertypes.hh @@ -82,6 +82,7 @@ extern ModifierTypeInfo modifierType_GreasePencilOffset; extern ModifierTypeInfo modifierType_GreasePencilNoise; extern ModifierTypeInfo modifierType_GreasePencilMirror; extern ModifierTypeInfo modifierType_GreasePencilThickness; +extern ModifierTypeInfo modifierType_GreasePencilDash; /* MOD_util.cc */ diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc new file mode 100644 index 00000000000..45bbca60430 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc @@ -0,0 +1,334 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup modifiers + */ + +#include "BLI_string_utils.hh" + +#include "DNA_defaults.h" +#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 "UI_interface.hh" +#include "UI_resources.hh" + +#include "BLT_translation.h" + +#include "WM_api.hh" +#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 *dmd = reinterpret_cast(md); + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(dmd, modifier)); + + MEMCPY_STRUCT_AFTER(dmd, DNA_struct_default_get(GreasePencilDashModifierData), modifier); + modifier::greasepencil::init_influence_data(&dmd->influence, false); +} + +static void copy_data(const ModifierData *md, ModifierData *target, const int flag) +{ + const auto *dmd = reinterpret_cast(md); + auto *tmmd = reinterpret_cast(target); + + modifier::greasepencil::free_influence_data(&tmmd->influence); + + BKE_modifier_copydata_generic(md, target, flag); + modifier::greasepencil::copy_influence_data(&dmd->influence, &tmmd->influence, flag); +} + +static void free_data(ModifierData *md) +{ + auto *dmd = reinterpret_cast(md); + modifier::greasepencil::free_influence_data(&dmd->influence); +} + +static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data) +{ + auto *dmd = reinterpret_cast(md); + modifier::greasepencil::foreach_influence_ID_link(&dmd->influence, ob, walk, user_data); +} + +// static bke::CurvesGeometry create_mirror_copies(const Object &ob, +// const GreasePencilDashModifierData &dmd, +// const bke::CurvesGeometry &base_curves, +// const bke::CurvesGeometry &mirror_curves) +// { +// const bool use_mirror_x = (dmd.flag & MOD_GREASE_PENCIL_MIRROR_AXIS_X); +// const bool use_mirror_y = (dmd.flag & MOD_GREASE_PENCIL_MIRROR_AXIS_Y); +// const bool use_mirror_z = (dmd.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 instances = std::make_unique(); +// 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, dmd, 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 GreasePencilDashModifierData &dmd, + const ModifierEvalContext &ctx, + bke::greasepencil::Drawing &drawing) +{ + UNUSED_VARS(dmd, ctx); + // const bool use_mirror_x = (dmd.flag & MOD_GREASE_PENCIL_MIRROR_AXIS_X); + // const bool use_mirror_y = (dmd.flag & MOD_GREASE_PENCIL_MIRROR_AXIS_Y); + // const bool use_mirror_z = (dmd.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, dmd.influence, curve_mask_memory); + + // if (curves_mask.size() == src_curves.curve_num) { + // /* All geometry gets mirrored. */ + // drawing.strokes_for_write() = create_mirror_copies(*ctx.object, dmd, 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, dmd, src_curves, masked_curves); + // } + + drawing.tag_topology_changed(); +} + +static void modify_geometry_set(ModifierData *md, + const ModifierEvalContext *ctx, + bke::GeometrySet *geometry_set) +{ + using bke::greasepencil::Drawing; + + auto *dmd = reinterpret_cast(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, dmd->influence, mask_memory); + + const Vector drawings = modifier::greasepencil::get_drawings_for_write( + grease_pencil, layer_mask, frame); + threading::parallel_for_each(drawings, + [&](Drawing *drawing) { modify_drawing(*dmd, *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); + auto *dmd = static_cast(ptr->data); + + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, ptr, "dash_offset", UI_ITEM_NONE, nullptr, ICON_NONE); + + uiLayout *row = uiLayoutRow(layout, false); + uiLayoutSetPropSep(row, false); + + uiTemplateList(row, + (bContext *)C, + "MOD_UL_grease_pencil_dash_modifier_segments", + "", + ptr, + "segments", + ptr, + "segment_active_index", + nullptr, + 3, + 10, + 0, + 1, + UI_TEMPLATE_LIST_FLAG_NONE); + + uiLayout *col = uiLayoutColumn(row, false); + uiLayout *sub = uiLayoutColumn(col, true); + uiItemO(sub, "", ICON_ADD, "GPENCIL_OT_segment_add"); + uiItemO(sub, "", ICON_REMOVE, "GPENCIL_OT_segment_remove"); + uiItemS(col); + sub = uiLayoutColumn(col, true); + uiItemEnumO_string(sub, "", ICON_TRIA_UP, "GPENCIL_OT_segment_move", "type", "UP"); + uiItemEnumO_string(sub, "", ICON_TRIA_DOWN, "GPENCIL_OT_segment_move", "type", "DOWN"); + + if (dmd->segment_active_index >= 0 && dmd->segment_active_index < dmd->segments_num) { + PointerRNA ds_ptr = RNA_pointer_create(ptr->owner_id, + &RNA_GreasePencilDashModifierSegment, + &dmd->segments()[dmd->segment_active_index]); + + sub = uiLayoutColumn(layout, true); + uiItemR(sub, &ds_ptr, "dash", UI_ITEM_NONE, nullptr, ICON_NONE); + uiItemR(sub, &ds_ptr, "gap", UI_ITEM_NONE, nullptr, ICON_NONE); + + sub = uiLayoutColumn(layout, false); + uiItemR(sub, &ds_ptr, "radius", UI_ITEM_NONE, nullptr, ICON_NONE); + uiItemR(sub, &ds_ptr, "opacity", UI_ITEM_NONE, nullptr, ICON_NONE); + uiItemR(sub, &ds_ptr, "material_index", UI_ITEM_NONE, nullptr, ICON_NONE); + uiItemR(sub, &ds_ptr, "use_cyclic", 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 segment_list_item_draw(uiList * /*ui_list*/, + const bContext * /*C*/, + uiLayout *layout, + PointerRNA * /*idataptr*/, + PointerRNA *itemptr, + int /*icon*/, + PointerRNA * /*active_dataptr*/, + const char * /*active_propname*/, + int /*index*/, + int /*flt_flag*/) +{ + uiLayout *row = uiLayoutRow(layout, true); + uiItemR(row, itemptr, "name", UI_ITEM_R_NO_BG, "", ICON_NONE); +} + +static void panel_register(ARegionType *region_type) +{ + modifier_panel_register(region_type, eModifierType_GreasePencilMirror, panel_draw); + + uiListType *list_type = static_cast( + MEM_callocN(sizeof(uiListType), "Grease Pencil Dash modifier segments")); + STRNCPY(list_type->idname, "MOD_UL_grease_pencil_dash_modifier_segments"); + list_type->draw_item = segment_list_item_draw; + WM_uilisttype_add(list_type); +} + +static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md) +{ + const auto *dmd = reinterpret_cast(md); + + BLO_write_struct(writer, GreasePencilDashModifierData, dmd); + modifier::greasepencil::write_influence_data(writer, &dmd->influence); +} + +static void blend_read(BlendDataReader *reader, ModifierData *md) +{ + auto *dmd = reinterpret_cast(md); + + modifier::greasepencil::read_influence_data(reader, &dmd->influence); +} + +} // namespace blender + +ModifierTypeInfo modifierType_GreasePencilDash = { + /*idname*/ "GreasePencilDash", + /*name*/ N_("Dot Dash"), + /*struct_name*/ "GreasePencilDashModifierData", + /*struct_size*/ sizeof(GreasePencilDashModifierData), + /*srna*/ &RNA_GreasePencilDashModifier, + /*type*/ ModifierTypeType::Nonconstructive, + /*flags*/ eModifierTypeFlag_AcceptsGreasePencil | eModifierTypeFlag_SupportsEditmode | + eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping, + /*icon*/ ICON_MOD_DASH, + + /*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, +}; + +Span GreasePencilDataModifierData::segments() const +{ + return {this->segments_array, this->segments_num}; +} + +MutableSpan GreasePencilDashModifierData::segments() +{ + return {this->segments_array, this->segments_num}; +} diff --git a/source/blender/modifiers/intern/MOD_util.cc b/source/blender/modifiers/intern/MOD_util.cc index a6fc875c5b0..acc41af3c36 100644 --- a/source/blender/modifiers/intern/MOD_util.cc +++ b/source/blender/modifiers/intern/MOD_util.cc @@ -279,5 +279,6 @@ void modifier_type_init(ModifierTypeInfo *types[]) INIT_TYPE(GreasePencilNoise); INIT_TYPE(GreasePencilMirror); INIT_TYPE(GreasePencilThickness); + INIT_TYPE(GreasePencilDash); #undef INIT_TYPE } -- 2.30.2 From 4ce502163602403a7dd3c34658a2e7feec186f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Wed, 31 Jan 2024 16:27:28 +0100 Subject: [PATCH 02/16] Fix build errors. --- .../modifiers/intern/MOD_grease_pencil_dash.cc | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc index 45bbca60430..9426cec910f 100644 --- a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc +++ b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc @@ -6,7 +6,7 @@ * \ingroup modifiers */ -#include "BLI_string_utils.hh" +#include "BLI_string.h" #include "DNA_defaults.h" #include "DNA_modifier_types.h" @@ -230,13 +230,8 @@ static void panel_draw(const bContext *C, Panel *panel) uiItemR(sub, &ds_ptr, "use_cyclic", 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")) + 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); @@ -293,7 +288,7 @@ ModifierTypeInfo modifierType_GreasePencilDash = { /*name*/ N_("Dot Dash"), /*struct_name*/ "GreasePencilDashModifierData", /*struct_size*/ sizeof(GreasePencilDashModifierData), - /*srna*/ &RNA_GreasePencilDashModifier, + /*srna*/ &RNA_GreasePencilDashModifierData, /*type*/ ModifierTypeType::Nonconstructive, /*flags*/ eModifierTypeFlag_AcceptsGreasePencil | eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping, @@ -323,12 +318,12 @@ ModifierTypeInfo modifierType_GreasePencilDash = { /*blend_read*/ blender::blend_read, }; -Span GreasePencilDataModifierData::segments() const +blender::Span GreasePencilDashModifierData::segments() const { return {this->segments_array, this->segments_num}; } -MutableSpan GreasePencilDashModifierData::segments() +blender::MutableSpan GreasePencilDashModifierData::segments() { return {this->segments_array, this->segments_num}; } -- 2.30.2 From d110886228e091e25854a71414ab832cc054db08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Wed, 31 Jan 2024 16:34:58 +0100 Subject: [PATCH 03/16] `is_disabled` callback for Dash modifier. --- .../intern/MOD_grease_pencil_dash.cc | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc index 9426cec910f..3059d08b707 100644 --- a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc +++ b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc @@ -72,6 +72,29 @@ static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void modifier::greasepencil::foreach_influence_ID_link(&dmd->influence, ob, walk, user_data); } +/** + * Gap==0 means to start the next segment at the immediate next point, which will leave a visual + * gap of "1 point". This makes the algorithm give the same visual appearance as displayed on the + * UI and also simplifies the check for "no-length" situation where SEG==0 (which will not produce + * any effective dash). + */ +static int real_gap(const GreasePencilDashModifierSegment &dash_segment) +{ + return dash_segment.gap - 1; +} + +static bool is_disabled(const Scene * /*scene*/, ModifierData *md, bool /*use_render_params*/) +{ + const auto *dmd = reinterpret_cast(md); + /* Enable if at least one segment has non-zero length. */ + for (const GreasePencilDashModifierSegment &dash_segment : dmd->segments()) { + if (dash_segment.dash + real_gap(dash_segment) > 0) { + return false; + } + } + return true; +} + // static bke::CurvesGeometry create_mirror_copies(const Object &ob, // const GreasePencilDashModifierData &dmd, // const bke::CurvesGeometry &base_curves, @@ -306,7 +329,7 @@ ModifierTypeInfo modifierType_GreasePencilDash = { /*init_data*/ blender::init_data, /*required_data_mask*/ nullptr, /*free_data*/ blender::free_data, - /*is_disabled*/ nullptr, + /*is_disabled*/ blender::is_disabled, /*update_depsgraph*/ nullptr, /*depends_on_time*/ nullptr, /*depends_on_normals*/ nullptr, -- 2.30.2 From 2a45e3ea05769e95787874c655eeb2a24c6d5886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Fri, 2 Feb 2024 14:51:37 +0100 Subject: [PATCH 04/16] Basic implementation. --- source/blender/editors/object/object_intern.h | 3 + .../blender/editors/object/object_modifier.cc | 236 +++++++++++ source/blender/editors/object/object_ops.cc | 3 + source/blender/geometry/GEO_reorder.hh | 1 + .../blender/makesdna/DNA_modifier_defaults.h | 2 +- source/blender/makesdna/intern/dna_defaults.c | 2 + .../blender/makesrna/intern/rna_modifier.cc | 4 +- .../intern/MOD_grease_pencil_dash.cc | 386 ++++++++++++++---- 8 files changed, 547 insertions(+), 90 deletions(-) diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 1d0c62c5300..8fdf98b3d4f 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -225,6 +225,9 @@ void OBJECT_OT_laplaciandeform_bind(struct wmOperatorType *ot); void OBJECT_OT_surfacedeform_bind(struct wmOperatorType *ot); void OBJECT_OT_geometry_nodes_input_attribute_toggle(struct wmOperatorType *ot); void OBJECT_OT_geometry_node_tree_copy_assign(struct wmOperatorType *ot); +void OBJECT_OT_grease_pencil_dash_modifier_segment_add(struct wmOperatorType *ot); +void OBJECT_OT_grease_pencil_dash_modifier_segment_remove(struct wmOperatorType *ot); +void OBJECT_OT_grease_pencil_dash_modifier_segment_move(struct wmOperatorType *ot); /* object_gpencil_modifiers.c */ diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc index 129942afbac..321c96bb19d 100644 --- a/source/blender/editors/object/object_modifier.cc +++ b/source/blender/editors/object/object_modifier.cc @@ -18,6 +18,7 @@ #include "DNA_armature_types.h" #include "DNA_array_utils.hh" #include "DNA_curve_types.h" +#include "DNA_defaults.h" #include "DNA_dynamicpaint_types.h" #include "DNA_fluid_types.h" #include "DNA_key_types.h" @@ -34,6 +35,7 @@ #include "BLI_path_util.h" #include "BLI_string.h" #include "BLI_string_utf8.h" +#include "BLI_string_utils.hh" #include "BLI_utildefines.h" #include "BKE_DerivedMesh.hh" @@ -3725,3 +3727,237 @@ void OBJECT_OT_geometry_node_tree_copy_assign(wmOperatorType *ot) } /** \} */ + +/* ------------------------------------------------------------------- */ +/** \name Dash Modifier + * \{ */ + +static bool grease_pencil_dash_modifier_segment_poll(bContext *C) +{ + return edit_modifier_poll_generic(C, &RNA_GreasePencilDashModifierData, 0, false, false); +} + +static int grease_pencil_dash_modifier_segment_add_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + auto *dmd = reinterpret_cast( + edit_modifier_property_get(op, ob, eModifierType_GreasePencilDash)); + + if (dmd == nullptr) { + return OPERATOR_CANCELLED; + } + + GreasePencilDashModifierSegment *new_segments = static_cast( + MEM_malloc_arrayN(dmd->segments_num + 1, sizeof(GreasePencilDashModifierSegment), __func__)); + + const int new_active_index = dmd->segment_active_index + 1; + if (dmd->segments_num != 0) { + /* Copy the segments before the new segment. */ + memcpy(new_segments, + dmd->segments_array, + sizeof(GreasePencilDashModifierSegment) * new_active_index); + /* Copy the segments after the new segment. */ + memcpy(new_segments + new_active_index + 1, + dmd->segments_array + new_active_index, + sizeof(GreasePencilDashModifierSegment) * (dmd->segments_num - new_active_index)); + } + + /* Create the new segment. */ + GreasePencilDashModifierSegment *ds = &new_segments[new_active_index]; + memcpy(ds, + DNA_struct_default_get(GreasePencilDashModifierSegment), + sizeof(GreasePencilDashModifierSegment)); + BLI_uniquename_cb( + [&](const blender::StringRef name) { + for (const GreasePencilDashModifierSegment &ds : dmd->segments()) { + if (STREQ(ds.name, name.data())) { + return true; + } + } + return false; + }, + '.', + ds->name); + + MEM_SAFE_FREE(dmd->segments_array); + dmd->segments_array = new_segments; + dmd->segments_num++; + dmd->segment_active_index++; + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int grease_pencil_dash_modifier_segment_add_invoke(bContext *C, + wmOperator *op, + const wmEvent * /*event*/) +{ + if (edit_modifier_invoke_properties(C, op)) { + return grease_pencil_dash_modifier_segment_add_exec(C, op); + } + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_grease_pencil_dash_modifier_segment_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Segment"; + ot->description = "Add a segment to the dash modifier"; + ot->idname = "OBJECT_OT_grease_pencil_dash_modifier_segment_add"; + + /* api callbacks */ + ot->poll = grease_pencil_dash_modifier_segment_poll; + ot->invoke = grease_pencil_dash_modifier_segment_add_invoke; + ot->exec = grease_pencil_dash_modifier_segment_add_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); +} + +static void grease_pencil_dash_modifier_segment_free(GreasePencilDashModifierSegment * /*ds*/) {} + +static int grease_pencil_dash_modifier_segment_remove_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + auto *dmd = reinterpret_cast( + edit_modifier_property_get(op, ob, eModifierType_GreasePencilDash)); + + if (dmd == nullptr) { + return OPERATOR_CANCELLED; + } + + if (!dmd->segments().index_range().contains(dmd->segment_active_index)) { + return OPERATOR_CANCELLED; + } + + blender::dna::array::remove_index(&dmd->segments_array, + &dmd->segments_num, + &dmd->segment_active_index, + dmd->segment_active_index, + grease_pencil_dash_modifier_segment_free); + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int grease_pencil_dash_modifier_segment_remove_invoke(bContext *C, + wmOperator *op, + const wmEvent * /*event*/) +{ + if (edit_modifier_invoke_properties(C, op)) { + return grease_pencil_dash_modifier_segment_remove_exec(C, op); + } + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_grease_pencil_dash_modifier_segment_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Dash Segment"; + ot->description = "Remove the active segment from the dash modifier"; + ot->idname = "OBJECT_OT_grease_pencil_dash_modifier_segment_remove"; + + /* api callbacks */ + ot->poll = grease_pencil_dash_modifier_segment_poll; + ot->invoke = grease_pencil_dash_modifier_segment_remove_invoke; + ot->exec = grease_pencil_dash_modifier_segment_remove_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); + + RNA_def_int( + ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of the segment to remove", 0, INT_MAX); +} + +enum { + MOD_GREASE_PENCIL_DASH_SEGMENT_MOVE_UP = -1, + MOD_GREASE_PENCIL_DASH_SEGMENT_MOVE_DOWN = 1, +}; + +static int grease_pencil_dash_modifier_segment_move_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + auto *dmd = reinterpret_cast( + edit_modifier_property_get(op, ob, eModifierType_GreasePencilDash)); + + if (dmd == nullptr) { + return OPERATOR_CANCELLED; + } + + if (dmd->segments_num < 2) { + return OPERATOR_CANCELLED; + } + + const int direction = RNA_enum_get(op->ptr, "type"); + if (direction == MOD_GREASE_PENCIL_DASH_SEGMENT_MOVE_UP) { + if (dmd->segment_active_index == 0) { + return OPERATOR_CANCELLED; + } + + std::swap(dmd->segments_array[dmd->segment_active_index], + dmd->segments_array[dmd->segment_active_index - 1]); + + dmd->segment_active_index--; + } + else if (direction == MOD_GREASE_PENCIL_DASH_SEGMENT_MOVE_DOWN) { + if (dmd->segment_active_index == dmd->segments_num - 1) { + return OPERATOR_CANCELLED; + } + + std::swap(dmd->segments_array[dmd->segment_active_index], + dmd->segments_array[dmd->segment_active_index + 1]); + + dmd->segment_active_index++; + } + else { + return OPERATOR_CANCELLED; + } + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int grease_pencil_dash_modifier_segment_move_invoke(bContext *C, + wmOperator *op, + const wmEvent * /*event*/) +{ + if (edit_modifier_invoke_properties(C, op)) { + return grease_pencil_dash_modifier_segment_move_exec(C, op); + } + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_grease_pencil_dash_modifier_segment_move(wmOperatorType *ot) +{ + static const EnumPropertyItem segment_move[] = { + {MOD_GREASE_PENCIL_DASH_SEGMENT_MOVE_UP, "UP", 0, "Up", ""}, + {MOD_GREASE_PENCIL_DASH_SEGMENT_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + /* identifiers */ + ot->name = "Move Dash Segment"; + ot->description = "Move the active dash segment up or down"; + ot->idname = "OBJECT_OT_grease_pencil_dash_modifier_segment_move"; + + /* api callbacks */ + ot->poll = grease_pencil_dash_modifier_segment_poll; + ot->invoke = grease_pencil_dash_modifier_segment_move_invoke; + ot->exec = grease_pencil_dash_modifier_segment_move_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); + + ot->prop = RNA_def_enum(ot->srna, "type", segment_move, 0, "Type", ""); +} + +/** \} */ diff --git a/source/blender/editors/object/object_ops.cc b/source/blender/editors/object/object_ops.cc index df916ed2f34..7956c1957c9 100644 --- a/source/blender/editors/object/object_ops.cc +++ b/source/blender/editors/object/object_ops.cc @@ -141,6 +141,9 @@ void ED_operatortypes_object() WM_operatortype_append(OBJECT_OT_skin_armature_create); WM_operatortype_append(OBJECT_OT_geometry_nodes_input_attribute_toggle); WM_operatortype_append(OBJECT_OT_geometry_node_tree_copy_assign); + WM_operatortype_append(OBJECT_OT_grease_pencil_dash_modifier_segment_add); + WM_operatortype_append(OBJECT_OT_grease_pencil_dash_modifier_segment_remove); + WM_operatortype_append(OBJECT_OT_grease_pencil_dash_modifier_segment_move); /* grease pencil modifiers */ WM_operatortype_append(OBJECT_OT_gpencil_modifier_add); diff --git a/source/blender/geometry/GEO_reorder.hh b/source/blender/geometry/GEO_reorder.hh index 2bc7a347c74..9ae83771788 100644 --- a/source/blender/geometry/GEO_reorder.hh +++ b/source/blender/geometry/GEO_reorder.hh @@ -9,6 +9,7 @@ #include "BKE_anonymous_attribute_id.hh" #include "BKE_attribute.hh" +#include "BKE_curves.hh" #include "BKE_geometry_set.hh" namespace blender::geometry { diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h index 6165d1ec9cd..140e8b9f484 100644 --- a/source/blender/makesdna/DNA_modifier_defaults.h +++ b/source/blender/makesdna/DNA_modifier_defaults.h @@ -890,7 +890,7 @@ { \ .dash_offset = 0, \ .segments_array = NULL, \ - .segments_num = 1, \ + .segments_num = 0, \ .segment_active_index = 0, \ } diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index fc16f8aea01..0cb65299dbd 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -334,6 +334,7 @@ SDNA_DEFAULT_DECL_STRUCT(GreasePencilOffsetModifierData); SDNA_DEFAULT_DECL_STRUCT(GreasePencilMirrorModifierData); SDNA_DEFAULT_DECL_STRUCT(GreasePencilThickModifierData); SDNA_DEFAULT_DECL_STRUCT(GreasePencilArrayModifierData); +SDNA_DEFAULT_DECL_STRUCT(GreasePencilDashModifierSegment); SDNA_DEFAULT_DECL_STRUCT(GreasePencilDashModifierData); #undef SDNA_DEFAULT_DECL_STRUCT @@ -591,6 +592,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = { SDNA_DEFAULT_DECL(GreasePencilMirrorModifierData), SDNA_DEFAULT_DECL(GreasePencilThickModifierData), SDNA_DEFAULT_DECL(GreasePencilArrayModifierData), + SDNA_DEFAULT_DECL(GreasePencilDashModifierSegment), SDNA_DEFAULT_DECL(GreasePencilDashModifierData), }; #undef SDNA_DEFAULT_DECL diff --git a/source/blender/makesrna/intern/rna_modifier.cc b/source/blender/makesrna/intern/rna_modifier.cc index 9e53eb87c72..8844055dd90 100644 --- a/source/blender/makesrna/intern/rna_modifier.cc +++ b/source/blender/makesrna/intern/rna_modifier.cc @@ -1932,7 +1932,7 @@ static const GreasePencilDashModifierData *find_grease_pencil_dash_modifier_of_s return nullptr; } -static char *rna_GreasePencilDashModifierSegment_path(const PointerRNA *ptr) +static std::optional rna_GreasePencilDashModifierSegment_path(const PointerRNA *ptr) { const Object *ob = reinterpret_cast(ptr->owner_id); @@ -1947,7 +1947,7 @@ static char *rna_GreasePencilDashModifierSegment_path(const PointerRNA *ptr) char ds_name_esc[sizeof(dash_segment->name) * 2]; BLI_str_escape(ds_name_esc, dash_segment->name, sizeof(ds_name_esc)); - return BLI_sprintfN("modifiers[\"%s\"].segments[\"%s\"]", name_esc, ds_name_esc); + return fmt::format("modifiers[\"{}\"].segments[\"{}\"]", name_esc, ds_name_esc); } static void rna_GreasePencilDashModifierSegment_name_set(PointerRNA *ptr, const char *value) diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc index 3059d08b707..9df14e0743e 100644 --- a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc +++ b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc @@ -6,7 +6,10 @@ * \ingroup modifiers */ +#include "BLI_index_range.hh" +#include "BLI_span.hh" #include "BLI_string.h" +#include "BLI_string_utf8.h" #include "DNA_defaults.h" #include "DNA_modifier_types.h" @@ -37,6 +40,8 @@ #include "MOD_modifiertypes.hh" #include "MOD_ui_common.hh" +#include + namespace blender { static void init_data(ModifierData *md) @@ -47,6 +52,11 @@ static void init_data(ModifierData *md) MEMCPY_STRUCT_AFTER(dmd, DNA_struct_default_get(GreasePencilDashModifierData), modifier); modifier::greasepencil::init_influence_data(&dmd->influence, false); + + GreasePencilDashModifierSegment *ds = DNA_struct_default_alloc(GreasePencilDashModifierSegment); + STRNCPY_UTF8(ds->name, DATA_("Segment")); + dmd->segments_array = ds; + dmd->segments_num = 1; } static void copy_data(const ModifierData *md, ModifierData *target, const int flag) @@ -58,12 +68,17 @@ static void copy_data(const ModifierData *md, ModifierData *target, const int fl BKE_modifier_copydata_generic(md, target, flag); modifier::greasepencil::copy_influence_data(&dmd->influence, &tmmd->influence, flag); + + tmmd->segments_array = static_cast( + MEM_dupallocN(dmd->segments_array)); } static void free_data(ModifierData *md) { auto *dmd = reinterpret_cast(md); modifier::greasepencil::free_influence_data(&dmd->influence); + + MEM_SAFE_FREE(dmd->segments_array); } static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data) @@ -72,105 +87,289 @@ static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void modifier::greasepencil::foreach_influence_ID_link(&dmd->influence, ob, walk, user_data); } -/** - * Gap==0 means to start the next segment at the immediate next point, which will leave a visual - * gap of "1 point". This makes the algorithm give the same visual appearance as displayed on the - * UI and also simplifies the check for "no-length" situation where SEG==0 (which will not produce - * any effective dash). - */ -static int real_gap(const GreasePencilDashModifierSegment &dash_segment) -{ - return dash_segment.gap - 1; -} - static bool is_disabled(const Scene * /*scene*/, ModifierData *md, bool /*use_render_params*/) { const auto *dmd = reinterpret_cast(md); /* Enable if at least one segment has non-zero length. */ for (const GreasePencilDashModifierSegment &dash_segment : dmd->segments()) { - if (dash_segment.dash + real_gap(dash_segment) > 0) { + if (dash_segment.dash + dash_segment.gap - 1 > 0) { return false; } } return true; } -// static bke::CurvesGeometry create_mirror_copies(const Object &ob, -// const GreasePencilDashModifierData &dmd, -// const bke::CurvesGeometry &base_curves, -// const bke::CurvesGeometry &mirror_curves) -// { -// const bool use_mirror_x = (dmd.flag & MOD_GREASE_PENCIL_MIRROR_AXIS_X); -// const bool use_mirror_y = (dmd.flag & MOD_GREASE_PENCIL_MIRROR_AXIS_Y); -// const bool use_mirror_z = (dmd.flag & MOD_GREASE_PENCIL_MIRROR_AXIS_Z); +static int floored_modulo(const int a, const int b) +{ + return a - math::floor(a / b) * b; +} -// 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); +/* Combined segment info used by all strokes. */ +struct PatternInfo { + /* Segment indices per point over the length of the pattern. + * -1: Gap, discard point. + * >= 0: Segment index, same indices form a curve. + */ + Array dash_ids; + int dash_offset = 0; -// std::unique_ptr instances = std::make_unique(); -// 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, dmd, bool(mirror_x), bool(mirror_y), bool(mirror_z)); -// instances->add_instance(mirror_handle, matrix); -// } -// } -// } -// } + int point_num = 0; + int curve_num = 0; +}; -// 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 PatternInfo get_pattern_info(const GreasePencilDashModifierData &dmd) +{ + int pattern_length = 0; + for (const GreasePencilDashModifierSegment &dash_segment : dmd.segments()) { + pattern_length += dash_segment.dash + dash_segment.gap; + } + + PatternInfo info; + info.dash_ids.reinitialize(pattern_length); + info.dash_offset = floored_modulo(dmd.dash_offset, pattern_length); + info.curve_num = dmd.segments().size(); + + IndexRange dash_range(0); + IndexRange gap_range(0); + for (const int i : dmd.segments().index_range()) { + const GreasePencilDashModifierSegment &dash_segment = dmd.segments()[i]; + dash_range = gap_range.after(dash_segment.dash); + gap_range = dash_range.after(dash_segment.gap); + info.dash_ids.as_mutable_span().slice(dash_range).fill(i); + info.dash_ids.as_mutable_span().slice(gap_range).fill(-1); + + info.point_num += dash_range.size(); + } + return info; +} + +/** + * Iterate over all dash curves. + * \param fn: Function taking an \a IndexMask of source points, describing new curves. + */ +template +static void foreach_dash(const PatternInfo &pattern_info, const int src_point_num, Fn fn) +{ + BLI_assert(src_point_num > 0); + + const Span dash_ids = pattern_info.dash_ids; + const int pattern_length = dash_ids.size(); + /* Points range in "pattern space". */ + const IndexRange points = {pattern_info.dash_offset, src_point_num}; + + int prev_dash_id = -1; + IndexRange src_curve_points(0); + for (const int i : points.index_range()) { + const int repeat = points[i] / pattern_length; + const int pattern_i = points[i] - repeat * pattern_length; + BLI_assert(pattern_i < pattern_length); + /* Unique ID, to ensure curve breaks in case there is just one dash. */ + const int dash_id = dash_ids[pattern_i] + repeat * pattern_info.curve_num; + if (dash_id >= 0 && dash_id == prev_dash_id) { + /* Extend curve. */ + src_curve_points = IndexRange(src_curve_points.start(), src_curve_points.size() + 1); + } + else { + if (!src_curve_points.is_empty()) { + /* Report finished curve. */ + fn(IndexMask(src_curve_points)); + } + + if (dash_id >= 0) { + /* New curve. */ + src_curve_points = IndexRange(i, 1); + } + } + + prev_dash_id = dash_id; + } +} + +#if 0 +/** Count how many points and curves are generated from an input curve of length \a point_num. */ +static auto count_dash_points_and_curves(const PatternInfo &pattern_info, const int src_point_num) +{ + BLI_assert(src_point_num > 0); + + int dst_point_num = 0; + int dst_curve_num = 0; + + const Span dash_ids = pattern_info.dash_ids; + const int pattern_length = dash_ids.size(); + /* Number of pattern repetitions to cover the entire point range. */ + const int repeats = (pattern_info.dash_offset + src_point_num + pattern_length - 1) / + pattern_length; + BLI_assert(repeats > 0); + + /* Points range in "pattern space". */ + const IndexRange points = {pattern_info.dash_offset, src_point_num}; + + /* Index range of the first pattern instance. */ + const IndexRange first_pattern_range = IndexRange(pattern_length).intersect(points); + for (const int i : first_pattern_range.index_range()) { + const int pattern_i = first_pattern_range[i]; + if (dash_ids[pattern_i] >= 0) { + dst_point_num++; + } + if (i == 0 || dash_ids[pattern_i] != dash_ids[pattern_i - 1]) { + dst_curve_num++; + } + } + + if (repeats > 1) { + /* Add full pattern repetitions. */ + dst_point_num += pattern_info.point_num * (repeats - 2); + dst_curve_num += pattern_info.curve_num * (repeats - 2); + + const IndexRange last_pattern_range = IndexRange((repeats - 1) * pattern_length, + pattern_length) + .intersect(points) + .shift(-(repeats - 1) * pattern_length); + for (const int i : last_pattern_range.index_range()) { + const int pattern_i = last_pattern_range[i]; + if (dash_ids[pattern_i] >= 0) { + dst_point_num++; + } + if (i == 0 || dash_ids[pattern_i] != dash_ids[pattern_i - 1]) { + dst_curve_num++; + } + } + } + + return std::make_tuple(dst_point_num, dst_curve_num); +} + +static void build_dashes(const PatternInfo &pattern_info, + const int src_point_num, + MutableSpan offsets) +{ + BLI_assert(src_point_num > 0); + + const Span dash_ids = pattern_info.dash_ids; + const int pattern_length = dash_ids.size(); + /* Number of pattern repetitions to cover the entire point range. */ + const int repeats = (pattern_info.dash_offset + src_point_num + pattern_length - 1) / + pattern_length; + BLI_assert(repeats > 0); + + /* Points range in "pattern space". */ + const IndexRange points = {pattern_info.dash_offset, src_point_num}; + + int dst_point_num = 0; + int dst_curve_num = 0; + + /* Index range of the first pattern instance. */ + const IndexRange first_pattern_range = IndexRange(pattern_length).intersect(points); + for (const int i : first_pattern_range.index_range()) { + const int pattern_i = first_pattern_range[i]; + if (dash_ids[pattern_i] >= 0) { + dst_point_num++; + } + if (i == 0 || dash_ids[pattern_i] != dash_ids[pattern_i - 1]) { + dst_curve_num++; + } + } + + if (repeats > 1) { + /* Add full pattern repetitions. */ + dst_point_num += pattern_info.point_num * (repeats - 2); + dst_curve_num += pattern_info.curve_num * (repeats - 2); + + const IndexRange last_pattern_range = IndexRange((repeats - 1) * pattern_length, + pattern_length) + .intersect(points) + .shift(-(repeats - 1) * pattern_length); + for (const int i : last_pattern_range.index_range()) { + const int pattern_i = last_pattern_range[i]; + if (dash_ids[pattern_i] >= 0) { + dst_point_num++; + } + if (i == 0 || dash_ids[pattern_i] != dash_ids[pattern_i - 1]) { + dst_curve_num++; + } + } + } + + return std::make_tuple(dst_point_num, dst_curve_num); +} +#endif + +static bke::CurvesGeometry create_dashes(const GreasePencilDashModifierData &dmd, + const PatternInfo &pattern_info, + const bke::CurvesGeometry &src_curves, + const IndexMask &curves_mask) +{ + /* Count new curves and points. */ + int dst_point_num = 0; + int dst_curve_num = 0; + for (const int src_curve_i : src_curves.curves_range()) { + const int src_point_num = src_curves.points_by_curve()[src_curve_i].size(); + foreach_dash(pattern_info, src_point_num, [&](const IndexMask &src_points) { + dst_point_num += src_points.size(); + dst_curve_num += 1; + }); + } + + bke::CurvesGeometry dst_curves(dst_point_num, dst_curve_num); + /* Map each destination point and curve to its source. */ + Array src_point_indices(dst_point_num); + Array src_curve_indices(dst_curve_num); + + std::cout << "Dash Curves (points=" << dst_point_num << ", curves=" << dst_curve_num << ")" + << std::endl; + { + IndexRange dst_point_range(0); + int dst_curve_i = 0; + for (const int src_curve_i : src_curves.curves_range()) { + const int src_point_num = src_curves.points_by_curve()[src_curve_i].size(); + foreach_dash(pattern_info, src_point_num, [&](const IndexMask &src_points) { + dst_point_range = dst_point_range.after(src_points.size()); + dst_curves.offsets_for_write()[dst_curve_i] = dst_point_range.start(); + std::cout << dst_curve_i << ": " << dst_point_range << std::endl; + + src_points.to_indices(src_point_indices.as_mutable_span().slice(dst_point_range)); + src_curve_indices[dst_curve_i] = src_curve_i; + + ++dst_curve_i; + }); + } + dst_curves.offsets_for_write()[dst_curve_i] = dst_point_range.one_after_last(); + std::cout << dst_curve_i << ": " << dst_point_range.one_after_last() << std::endl; + } + std::flush(std::cout); + + bke::gather_attributes(src_curves.attributes(), + bke::AttrDomain::Point, + {}, + {}, + src_point_indices, + dst_curves.attributes_for_write()); + bke::gather_attributes(src_curves.attributes(), + bke::AttrDomain::Curve, + {}, + {}, + src_curve_indices, + dst_curves.attributes_for_write()); + + return std::move(dst_curves); +} static void modify_drawing(const GreasePencilDashModifierData &dmd, const ModifierEvalContext &ctx, + const PatternInfo &pattern_info, bke::greasepencil::Drawing &drawing) { UNUSED_VARS(dmd, ctx); - // const bool use_mirror_x = (dmd.flag & MOD_GREASE_PENCIL_MIRROR_AXIS_X); - // const bool use_mirror_y = (dmd.flag & MOD_GREASE_PENCIL_MIRROR_AXIS_Y); - // const bool use_mirror_z = (dmd.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, dmd.influence, curve_mask_memory); - - // if (curves_mask.size() == src_curves.curve_num) { - // /* All geometry gets mirrored. */ - // drawing.strokes_for_write() = create_mirror_copies(*ctx.object, dmd, 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, dmd, src_curves, masked_curves); - // } + 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, dmd.influence, curve_mask_memory); + drawing.strokes_for_write() = create_dashes(dmd, pattern_info, src_curves, curves_mask); drawing.tag_topology_changed(); } @@ -188,14 +387,16 @@ static void modify_geometry_set(ModifierData *md, GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write(); const int frame = grease_pencil.runtime->eval_frame; + const PatternInfo pattern_info = get_pattern_info(*dmd); + IndexMaskMemory mask_memory; const IndexMask layer_mask = modifier::greasepencil::get_filtered_layer_mask( grease_pencil, dmd->influence, mask_memory); const Vector drawings = modifier::greasepencil::get_drawings_for_write( grease_pencil, layer_mask, frame); - threading::parallel_for_each(drawings, - [&](Drawing *drawing) { modify_drawing(*dmd, *ctx, *drawing); }); + threading::parallel_for_each( + drawings, [&](Drawing *drawing) { modify_drawing(*dmd, *ctx, pattern_info, *drawing); }); } static void panel_draw(const bContext *C, Panel *panel) @@ -230,12 +431,18 @@ static void panel_draw(const bContext *C, Panel *panel) uiLayout *col = uiLayoutColumn(row, false); uiLayout *sub = uiLayoutColumn(col, true); - uiItemO(sub, "", ICON_ADD, "GPENCIL_OT_segment_add"); - uiItemO(sub, "", ICON_REMOVE, "GPENCIL_OT_segment_remove"); + uiItemO(sub, "", ICON_ADD, "OBJECT_OT_grease_pencil_dash_modifier_segment_add"); + uiItemO(sub, "", ICON_REMOVE, "OBJECT_OT_grease_pencil_dash_modifier_segment_remove"); uiItemS(col); sub = uiLayoutColumn(col, true); - uiItemEnumO_string(sub, "", ICON_TRIA_UP, "GPENCIL_OT_segment_move", "type", "UP"); - uiItemEnumO_string(sub, "", ICON_TRIA_DOWN, "GPENCIL_OT_segment_move", "type", "DOWN"); + uiItemEnumO_string( + sub, "", ICON_TRIA_UP, "OBJECT_OT_grease_pencil_dash_modifier_segment_move", "type", "UP"); + uiItemEnumO_string(sub, + "", + ICON_TRIA_DOWN, + "OBJECT_OT_grease_pencil_dash_modifier_segment_move", + "type", + "DOWN"); if (dmd->segment_active_index >= 0 && dmd->segment_active_index < dmd->segments_num) { PointerRNA ds_ptr = RNA_pointer_create(ptr->owner_id, @@ -253,8 +460,8 @@ static void panel_draw(const bContext *C, Panel *panel) uiItemR(sub, &ds_ptr, "use_cyclic", UI_ITEM_NONE, nullptr, ICON_NONE); } - if (uiLayout *influence_panel = uiLayoutPanel( - C, layout, "Influence", ptr, "open_influence_panel")) + if (uiLayout *influence_panel = uiLayoutPanelProp( + C, layout, ptr, "open_influence_panel", "Influence")) { modifier::greasepencil::draw_layer_filter_settings(C, influence_panel, ptr); modifier::greasepencil::draw_material_filter_settings(C, influence_panel, ptr); @@ -280,7 +487,7 @@ static void segment_list_item_draw(uiList * /*ui_list*/, static void panel_register(ARegionType *region_type) { - modifier_panel_register(region_type, eModifierType_GreasePencilMirror, panel_draw); + modifier_panel_register(region_type, eModifierType_GreasePencilDash, panel_draw); uiListType *list_type = static_cast( MEM_callocN(sizeof(uiListType), "Grease Pencil Dash modifier segments")); @@ -295,6 +502,9 @@ static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const Modi BLO_write_struct(writer, GreasePencilDashModifierData, dmd); modifier::greasepencil::write_influence_data(writer, &dmd->influence); + + BLO_write_struct_array( + writer, GreasePencilDashModifierSegment, dmd->segments_num, dmd->segments_array); } static void blend_read(BlendDataReader *reader, ModifierData *md) @@ -302,6 +512,8 @@ static void blend_read(BlendDataReader *reader, ModifierData *md) auto *dmd = reinterpret_cast(md); modifier::greasepencil::read_influence_data(reader, &dmd->influence); + + BLO_read_data_address(reader, &dmd->segments_array); } } // namespace blender -- 2.30.2 From ba806e67e778ad2d9cb29129ed963c9ac5f5a5a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Fri, 2 Feb 2024 16:18:52 +0100 Subject: [PATCH 05/16] progress. --- .../intern/MOD_grease_pencil_dash.cc | 167 +++++------------- 1 file changed, 42 insertions(+), 125 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc index 9df14e0743e..8188059390b 100644 --- a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc +++ b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc @@ -148,14 +148,14 @@ static PatternInfo get_pattern_info(const GreasePencilDashModifierData &dmd) * \param fn: Function taking an \a IndexMask of source points, describing new curves. */ template -static void foreach_dash(const PatternInfo &pattern_info, const int src_point_num, Fn fn) +static void foreach_dash(const PatternInfo &pattern_info, const IndexRange src_points, Fn fn) { - BLI_assert(src_point_num > 0); + BLI_assert(!src_points.is_empty()); const Span dash_ids = pattern_info.dash_ids; const int pattern_length = dash_ids.size(); /* Points range in "pattern space". */ - const IndexRange points = {pattern_info.dash_offset, src_point_num}; + const IndexRange points = {pattern_info.dash_offset, src_points.size()}; int prev_dash_id = -1; IndexRange src_curve_points(0); @@ -164,7 +164,9 @@ static void foreach_dash(const PatternInfo &pattern_info, const int src_point_nu const int pattern_i = points[i] - repeat * pattern_length; BLI_assert(pattern_i < pattern_length); /* Unique ID, to ensure curve breaks in case there is just one dash. */ - const int dash_id = dash_ids[pattern_i] + repeat * pattern_info.curve_num; + const int dash_id = (dash_ids[pattern_i] >= 0 ? + dash_ids[pattern_i] + repeat * pattern_info.curve_num : + -1); if (dash_id >= 0 && dash_id == prev_dash_id) { /* Extend curve. */ src_curve_points = IndexRange(src_curve_points.start(), src_curve_points.size() + 1); @@ -172,128 +174,17 @@ static void foreach_dash(const PatternInfo &pattern_info, const int src_point_nu else { if (!src_curve_points.is_empty()) { /* Report finished curve. */ - fn(IndexMask(src_curve_points)); + fn(IndexMask(src_curve_points.shift(src_points.start()))); } - if (dash_id >= 0) { - /* New curve. */ - src_curve_points = IndexRange(i, 1); - } + /* New curve. */ + src_curve_points = (dash_id >= 0 ? IndexRange(i, 1) : IndexRange(0)); } prev_dash_id = dash_id; } } -#if 0 -/** Count how many points and curves are generated from an input curve of length \a point_num. */ -static auto count_dash_points_and_curves(const PatternInfo &pattern_info, const int src_point_num) -{ - BLI_assert(src_point_num > 0); - - int dst_point_num = 0; - int dst_curve_num = 0; - - const Span dash_ids = pattern_info.dash_ids; - const int pattern_length = dash_ids.size(); - /* Number of pattern repetitions to cover the entire point range. */ - const int repeats = (pattern_info.dash_offset + src_point_num + pattern_length - 1) / - pattern_length; - BLI_assert(repeats > 0); - - /* Points range in "pattern space". */ - const IndexRange points = {pattern_info.dash_offset, src_point_num}; - - /* Index range of the first pattern instance. */ - const IndexRange first_pattern_range = IndexRange(pattern_length).intersect(points); - for (const int i : first_pattern_range.index_range()) { - const int pattern_i = first_pattern_range[i]; - if (dash_ids[pattern_i] >= 0) { - dst_point_num++; - } - if (i == 0 || dash_ids[pattern_i] != dash_ids[pattern_i - 1]) { - dst_curve_num++; - } - } - - if (repeats > 1) { - /* Add full pattern repetitions. */ - dst_point_num += pattern_info.point_num * (repeats - 2); - dst_curve_num += pattern_info.curve_num * (repeats - 2); - - const IndexRange last_pattern_range = IndexRange((repeats - 1) * pattern_length, - pattern_length) - .intersect(points) - .shift(-(repeats - 1) * pattern_length); - for (const int i : last_pattern_range.index_range()) { - const int pattern_i = last_pattern_range[i]; - if (dash_ids[pattern_i] >= 0) { - dst_point_num++; - } - if (i == 0 || dash_ids[pattern_i] != dash_ids[pattern_i - 1]) { - dst_curve_num++; - } - } - } - - return std::make_tuple(dst_point_num, dst_curve_num); -} - -static void build_dashes(const PatternInfo &pattern_info, - const int src_point_num, - MutableSpan offsets) -{ - BLI_assert(src_point_num > 0); - - const Span dash_ids = pattern_info.dash_ids; - const int pattern_length = dash_ids.size(); - /* Number of pattern repetitions to cover the entire point range. */ - const int repeats = (pattern_info.dash_offset + src_point_num + pattern_length - 1) / - pattern_length; - BLI_assert(repeats > 0); - - /* Points range in "pattern space". */ - const IndexRange points = {pattern_info.dash_offset, src_point_num}; - - int dst_point_num = 0; - int dst_curve_num = 0; - - /* Index range of the first pattern instance. */ - const IndexRange first_pattern_range = IndexRange(pattern_length).intersect(points); - for (const int i : first_pattern_range.index_range()) { - const int pattern_i = first_pattern_range[i]; - if (dash_ids[pattern_i] >= 0) { - dst_point_num++; - } - if (i == 0 || dash_ids[pattern_i] != dash_ids[pattern_i - 1]) { - dst_curve_num++; - } - } - - if (repeats > 1) { - /* Add full pattern repetitions. */ - dst_point_num += pattern_info.point_num * (repeats - 2); - dst_curve_num += pattern_info.curve_num * (repeats - 2); - - const IndexRange last_pattern_range = IndexRange((repeats - 1) * pattern_length, - pattern_length) - .intersect(points) - .shift(-(repeats - 1) * pattern_length); - for (const int i : last_pattern_range.index_range()) { - const int pattern_i = last_pattern_range[i]; - if (dash_ids[pattern_i] >= 0) { - dst_point_num++; - } - if (i == 0 || dash_ids[pattern_i] != dash_ids[pattern_i - 1]) { - dst_curve_num++; - } - } - } - - return std::make_tuple(dst_point_num, dst_curve_num); -} -#endif - static bke::CurvesGeometry create_dashes(const GreasePencilDashModifierData &dmd, const PatternInfo &pattern_info, const bke::CurvesGeometry &src_curves, @@ -303,8 +194,8 @@ static bke::CurvesGeometry create_dashes(const GreasePencilDashModifierData &dmd int dst_point_num = 0; int dst_curve_num = 0; for (const int src_curve_i : src_curves.curves_range()) { - const int src_point_num = src_curves.points_by_curve()[src_curve_i].size(); - foreach_dash(pattern_info, src_point_num, [&](const IndexMask &src_points) { + const IndexRange src_points = src_curves.points_by_curve()[src_curve_i]; + foreach_dash(pattern_info, src_points, [&](const IndexMask &src_points) { dst_point_num += src_points.size(); dst_curve_num += 1; }); @@ -318,14 +209,27 @@ static bke::CurvesGeometry create_dashes(const GreasePencilDashModifierData &dmd std::cout << "Dash Curves (points=" << dst_point_num << ", curves=" << dst_curve_num << ")" << std::endl; { + /* Start at curve offset and add points for each dash. */ IndexRange dst_point_range(0); int dst_curve_i = 0; for (const int src_curve_i : src_curves.curves_range()) { - const int src_point_num = src_curves.points_by_curve()[src_curve_i].size(); - foreach_dash(pattern_info, src_point_num, [&](const IndexMask &src_points) { + const IndexRange src_points = src_curves.points_by_curve()[src_curve_i]; + std::cout << "Original curve " << src_curve_i << " (" << src_points.size() << " points)" + << std::endl; + foreach_dash(pattern_info, src_points, [&](const IndexMask &src_points) { + { + std::cout << "Dash source points: "; + Array src_indices(src_points.size()); + src_points.to_indices(src_indices.as_mutable_span()); + for (const int i : src_indices) { + std::cout << i << ", "; + } + std::cout << std::endl; + } + dst_point_range = dst_point_range.after(src_points.size()); dst_curves.offsets_for_write()[dst_curve_i] = dst_point_range.start(); - std::cout << dst_curve_i << ": " << dst_point_range << std::endl; + // std::cout << dst_curve_i << ": " << dst_point_range << std::endl; src_points.to_indices(src_point_indices.as_mutable_span().slice(dst_point_range)); src_curve_indices[dst_curve_i] = src_curve_i; @@ -333,8 +237,19 @@ static bke::CurvesGeometry create_dashes(const GreasePencilDashModifierData &dmd ++dst_curve_i; }); } - dst_curves.offsets_for_write()[dst_curve_i] = dst_point_range.one_after_last(); - std::cout << dst_curve_i << ": " << dst_point_range.one_after_last() << std::endl; + if (dst_curve_i > 0) { + /* Last offset entry is total point count. */ + dst_curves.offsets_for_write()[dst_curve_i] = dst_point_range.one_after_last(); + } + // std::cout << dst_curve_i << ": " << dst_point_range.one_after_last() << std::endl; + } + std::cout << "Dash Points: " << std::endl; + for (const int i : src_point_indices.index_range()) { + std::cout << i << " <- " << src_point_indices[i] << std::endl; + } + std::cout << "Dash Curves: " << std::endl; + for (const int i : src_curve_indices.index_range()) { + std::cout << i << " <- " << src_curve_indices[i] << std::endl; } std::flush(std::cout); @@ -351,6 +266,8 @@ static bke::CurvesGeometry create_dashes(const GreasePencilDashModifierData &dmd src_curve_indices, dst_curves.attributes_for_write()); + dst_curves.update_curve_types(); + return std::move(dst_curves); } -- 2.30.2 From ad9e1811bc04ddcf25b0ddba1c62a3a86b6c144d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Fri, 2 Feb 2024 16:33:58 +0100 Subject: [PATCH 06/16] Fix floored_modulo function for integers. --- source/blender/modifiers/intern/MOD_grease_pencil_dash.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc index 8188059390b..30db33bf50e 100644 --- a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc +++ b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc @@ -101,7 +101,7 @@ static bool is_disabled(const Scene * /*scene*/, ModifierData *md, bool /*use_re static int floored_modulo(const int a, const int b) { - return a - math::floor(a / b) * b; + return a - math::floor(float(a) / float(b)) * b; } /* Combined segment info used by all strokes. */ -- 2.30.2 From 28e3e02072197e1e43ae100439da9318b683e230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Mon, 5 Feb 2024 12:55:18 +0100 Subject: [PATCH 07/16] Temp: Adding support for cyclic input curves. --- .../intern/MOD_grease_pencil_dash.cc | 72 ++++++++++--------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc index 30db33bf50e..41a6d3e0841 100644 --- a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc +++ b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc @@ -129,6 +129,7 @@ static PatternInfo get_pattern_info(const GreasePencilDashModifierData &dmd) info.dash_offset = floored_modulo(dmd.dash_offset, pattern_length); info.curve_num = dmd.segments().size(); + /* Bake segments into a single pattern array for simplicity. */ IndexRange dash_range(0); IndexRange gap_range(0); for (const int i : dmd.segments().index_range()) { @@ -148,7 +149,10 @@ static PatternInfo get_pattern_info(const GreasePencilDashModifierData &dmd) * \param fn: Function taking an \a IndexMask of source points, describing new curves. */ template -static void foreach_dash(const PatternInfo &pattern_info, const IndexRange src_points, Fn fn) +static void foreach_dash(const PatternInfo &pattern_info, + const IndexRange src_points, + const bool cyclic, + Fn fn) { BLI_assert(!src_points.is_empty()); @@ -190,18 +194,25 @@ static bke::CurvesGeometry create_dashes(const GreasePencilDashModifierData &dmd const bke::CurvesGeometry &src_curves, const IndexMask &curves_mask) { + const bke::AttributeAccessor src_attributes = src_curves.attributes(); + const VArray src_cyclic = *src_attributes.lookup_or_default( + "cyclic", bke::AttrDomain::Curve, false); + /* Count new curves and points. */ int dst_point_num = 0; int dst_curve_num = 0; for (const int src_curve_i : src_curves.curves_range()) { const IndexRange src_points = src_curves.points_by_curve()[src_curve_i]; - foreach_dash(pattern_info, src_points, [&](const IndexMask &src_points) { - dst_point_num += src_points.size(); - dst_curve_num += 1; - }); + + foreach_dash( + pattern_info, src_points, src_cyclic[src_curve_i], [&](const IndexMask &src_points) { + dst_point_num += src_points.size(); + dst_curve_num += 1; + }); } bke::CurvesGeometry dst_curves(dst_point_num, dst_curve_num); + bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); /* Map each destination point and curve to its source. */ Array src_point_indices(dst_point_num); Array src_curve_indices(dst_curve_num); @@ -216,26 +227,27 @@ static bke::CurvesGeometry create_dashes(const GreasePencilDashModifierData &dmd const IndexRange src_points = src_curves.points_by_curve()[src_curve_i]; std::cout << "Original curve " << src_curve_i << " (" << src_points.size() << " points)" << std::endl; - foreach_dash(pattern_info, src_points, [&](const IndexMask &src_points) { - { - std::cout << "Dash source points: "; - Array src_indices(src_points.size()); - src_points.to_indices(src_indices.as_mutable_span()); - for (const int i : src_indices) { - std::cout << i << ", "; - } - std::cout << std::endl; - } + foreach_dash( + pattern_info, src_points, src_cyclic[src_curve_i], [&](const IndexMask &src_points) { + { + std::cout << "Dash source points: "; + Array src_indices(src_points.size()); + src_points.to_indices(src_indices.as_mutable_span()); + for (const int i : src_indices) { + std::cout << i << ", "; + } + std::cout << std::endl; + } - dst_point_range = dst_point_range.after(src_points.size()); - dst_curves.offsets_for_write()[dst_curve_i] = dst_point_range.start(); - // std::cout << dst_curve_i << ": " << dst_point_range << std::endl; + dst_point_range = dst_point_range.after(src_points.size()); + dst_curves.offsets_for_write()[dst_curve_i] = dst_point_range.start(); + // std::cout << dst_curve_i << ": " << dst_point_range << std::endl; - src_points.to_indices(src_point_indices.as_mutable_span().slice(dst_point_range)); - src_curve_indices[dst_curve_i] = src_curve_i; + src_points.to_indices(src_point_indices.as_mutable_span().slice(dst_point_range)); + src_curve_indices[dst_curve_i] = src_curve_i; - ++dst_curve_i; - }); + ++dst_curve_i; + }); } if (dst_curve_i > 0) { /* Last offset entry is total point count. */ @@ -253,18 +265,10 @@ static bke::CurvesGeometry create_dashes(const GreasePencilDashModifierData &dmd } std::flush(std::cout); - bke::gather_attributes(src_curves.attributes(), - bke::AttrDomain::Point, - {}, - {}, - src_point_indices, - dst_curves.attributes_for_write()); - bke::gather_attributes(src_curves.attributes(), - bke::AttrDomain::Curve, - {}, - {}, - src_curve_indices, - dst_curves.attributes_for_write()); + bke::gather_attributes( + src_attributes, bke::AttrDomain::Point, {}, {}, src_point_indices, dst_attributes); + bke::gather_attributes( + src_attributes, bke::AttrDomain::Curve, {}, {}, src_curve_indices, dst_attributes); dst_curves.update_curve_types(); -- 2.30.2 From 09d285c5342fa1460a0e50c7384bfcb4c42a5fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Mon, 5 Feb 2024 15:19:47 +0100 Subject: [PATCH 08/16] Support cyclic input curves. --- .../intern/MOD_grease_pencil_dash.cc | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc index 41a6d3e0841..c04f4aaf777 100644 --- a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc +++ b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc @@ -161,9 +161,33 @@ static void foreach_dash(const PatternInfo &pattern_info, /* Points range in "pattern space". */ const IndexRange points = {pattern_info.dash_offset, src_points.size()}; + /* Range of points at the start of the curve which connects to the last dash + * (remains empty for non-cyclic curves). */ + IndexRange cyclic_start_range(0); + if (cyclic) { + int prev_dash_id = -1; + for (const int i : points.index_range()) { + /* Only count the first pattern. */ + if (points[i] >= pattern_length) { + break; + } + const int pattern_i = points[i]; + const int dash_id = dash_ids[pattern_i]; + if (dash_id >= 0 && dash_id == prev_dash_id) { + /* Extend curve. */ + cyclic_start_range = IndexRange(cyclic_start_range.start(), cyclic_start_range.size() + 1); + } + else { + break; + } + + prev_dash_id = dash_id; + } + } + int prev_dash_id = -1; IndexRange src_curve_points(0); - for (const int i : points.index_range()) { + for (const int i : points.index_range().drop_front(cyclic_start_range.size())) { const int repeat = points[i] / pattern_length; const int pattern_i = points[i] - repeat * pattern_length; BLI_assert(pattern_i < pattern_length); @@ -177,8 +201,17 @@ static void foreach_dash(const PatternInfo &pattern_info, } else { if (!src_curve_points.is_empty()) { - /* Report finished curve. */ - fn(IndexMask(src_curve_points.shift(src_points.start()))); + const IndexMask src_points_mask(src_curve_points.shift(src_points.start())); + if (i < points.size() - 1) { + /* Report finished curve. */ + fn(src_points_mask); + } + else { + /* Last dash, combine with start range in case of cyclic curves. */ + const IndexMask cyclic_start_mask(cyclic_start_range.shift(src_points.start())); + IndexMaskMemory mask_memory; + fn(IndexMask::from_union(src_points_mask, cyclic_start_mask, mask_memory)); + } } /* New curve. */ -- 2.30.2 From 340d218a075d068d3edf63b08929f04a9f38e249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Tue, 6 Feb 2024 15:31:23 +0100 Subject: [PATCH 09/16] Nicer implementation. --- .../intern/MOD_grease_pencil_dash.cc | 161 +++++++----------- 1 file changed, 57 insertions(+), 104 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc index c04f4aaf777..8d4387a977a 100644 --- a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc +++ b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc @@ -106,44 +106,50 @@ static int floored_modulo(const int a, const int b) /* Combined segment info used by all strokes. */ struct PatternInfo { - /* Segment indices per point over the length of the pattern. - * -1: Gap, discard point. - * >= 0: Segment index, same indices form a curve. - */ - Array dash_ids; - int dash_offset = 0; - - int point_num = 0; - int curve_num = 0; + int offset = 0; + int length = 0; + Array segments; }; static PatternInfo get_pattern_info(const GreasePencilDashModifierData &dmd) { - int pattern_length = 0; + PatternInfo info; for (const GreasePencilDashModifierSegment &dash_segment : dmd.segments()) { - pattern_length += dash_segment.dash + dash_segment.gap; + info.length += dash_segment.dash + dash_segment.gap; } - PatternInfo info; - info.dash_ids.reinitialize(pattern_length); - info.dash_offset = floored_modulo(dmd.dash_offset, pattern_length); - info.curve_num = dmd.segments().size(); + info.segments.reinitialize(dmd.segments().size()); + info.offset = floored_modulo(dmd.dash_offset, info.length); - /* Bake segments into a single pattern array for simplicity. */ + /* Store segments as ranges. */ IndexRange dash_range(0); IndexRange gap_range(0); for (const int i : dmd.segments().index_range()) { const GreasePencilDashModifierSegment &dash_segment = dmd.segments()[i]; dash_range = gap_range.after(dash_segment.dash); gap_range = dash_range.after(dash_segment.gap); - info.dash_ids.as_mutable_span().slice(dash_range).fill(i); - info.dash_ids.as_mutable_span().slice(gap_range).fill(-1); - - info.point_num += dash_range.size(); + info.segments[i] = dash_range; } return info; } +/* Returns the segment covering the given index, including repetitions.*/ +static int find_dash_segment(const PatternInfo &pattern_info, const int index) +{ + const int repeat = index / pattern_info.length; + const int segments_num = pattern_info.segments.size(); + + const int local_index = index - repeat * pattern_info.length; + for (const int i : pattern_info.segments.index_range().drop_back(1)) { + const IndexRange segment = pattern_info.segments[i]; + const IndexRange next_segment = pattern_info.segments[i + 1]; + if (local_index >= segment.start() && local_index < next_segment.start()) { + return i + repeat * segments_num; + } + } + return segments_num - 1 + repeat * segments_num; +} + /** * Iterate over all dash curves. * \param fn: Function taking an \a IndexMask of source points, describing new curves. @@ -154,71 +160,43 @@ static void foreach_dash(const PatternInfo &pattern_info, const bool cyclic, Fn fn) { - BLI_assert(!src_points.is_empty()); + const int points_num = src_points.size(); + const int segments_num = pattern_info.segments.size(); - const Span dash_ids = pattern_info.dash_ids; - const int pattern_length = dash_ids.size(); - /* Points range in "pattern space". */ - const IndexRange points = {pattern_info.dash_offset, src_points.size()}; + auto segment_to_points_mask = [&](const int segment, IndexMaskMemory &memory) -> IndexMask { + const int repeat = segment / segments_num; + const IndexRange range = pattern_info.segments[segment - repeat * segments_num].shift( + repeat * pattern_info.length); - /* Range of points at the start of the curve which connects to the last dash - * (remains empty for non-cyclic curves). */ - IndexRange cyclic_start_range(0); - if (cyclic) { - int prev_dash_id = -1; - for (const int i : points.index_range()) { - /* Only count the first pattern. */ - if (points[i] >= pattern_length) { - break; - } - const int pattern_i = points[i]; - const int dash_id = dash_ids[pattern_i]; - if (dash_id >= 0 && dash_id == prev_dash_id) { - /* Extend curve. */ - cyclic_start_range = IndexRange(cyclic_start_range.start(), cyclic_start_range.size() + 1); - } - else { - break; - } + const int64_t point_shift = src_points.start() - pattern_info.offset; + const int64_t min_point = src_points.start(); + const int64_t max_point = cyclic ? src_points.last() : src_points.one_after_last(); + const int64_t start = std::clamp(range.start() + point_shift, min_point, max_point); + const int64_t end = std::clamp(range.one_after_last() + point_shift, min_point, max_point); - prev_dash_id = dash_id; - } - } - - int prev_dash_id = -1; - IndexRange src_curve_points(0); - for (const int i : points.index_range().drop_front(cyclic_start_range.size())) { - const int repeat = points[i] / pattern_length; - const int pattern_i = points[i] - repeat * pattern_length; - BLI_assert(pattern_i < pattern_length); - /* Unique ID, to ensure curve breaks in case there is just one dash. */ - const int dash_id = (dash_ids[pattern_i] >= 0 ? - dash_ids[pattern_i] + repeat * pattern_info.curve_num : - -1); - if (dash_id >= 0 && dash_id == prev_dash_id) { - /* Extend curve. */ - src_curve_points = IndexRange(src_curve_points.start(), src_curve_points.size() + 1); + if (cyclic && end == max_point) { + return IndexMask::from_union(IndexRange(start, std::max(end - start - 1, int64_t(0))), + IndexRange(min_point, 1), + memory); } else { - if (!src_curve_points.is_empty()) { - const IndexMask src_points_mask(src_curve_points.shift(src_points.start())); - if (i < points.size() - 1) { - /* Report finished curve. */ - fn(src_points_mask); - } - else { - /* Last dash, combine with start range in case of cyclic curves. */ - const IndexMask cyclic_start_mask(cyclic_start_range.shift(src_points.start())); - IndexMaskMemory mask_memory; - fn(IndexMask::from_union(src_points_mask, cyclic_start_mask, mask_memory)); - } - } - - /* New curve. */ - src_curve_points = (dash_id >= 0 ? IndexRange(i, 1) : IndexRange(0)); + return IndexRange(start, end - start); } + }; - prev_dash_id = dash_id; + const int first_segment = find_dash_segment(pattern_info, pattern_info.offset); + const int last_segment = find_dash_segment(pattern_info, pattern_info.offset + points_num - 1); + BLI_assert(first_segment < segments_num); + BLI_assert(last_segment >= first_segment); + const IndexRange first_segment_points = pattern_info.segments[first_segment]; + + const IndexRange all_segments = IndexRange(first_segment, last_segment - first_segment + 1); + for (const int i : all_segments) { + IndexMaskMemory mask_memory; + const IndexMask points_mask = segment_to_points_mask(i, mask_memory); + if (!points_mask.is_empty()) { + fn(points_mask); + } } } @@ -250,31 +228,16 @@ static bke::CurvesGeometry create_dashes(const GreasePencilDashModifierData &dmd Array src_point_indices(dst_point_num); Array src_curve_indices(dst_curve_num); - std::cout << "Dash Curves (points=" << dst_point_num << ", curves=" << dst_curve_num << ")" - << std::endl; { /* Start at curve offset and add points for each dash. */ IndexRange dst_point_range(0); int dst_curve_i = 0; for (const int src_curve_i : src_curves.curves_range()) { const IndexRange src_points = src_curves.points_by_curve()[src_curve_i]; - std::cout << "Original curve " << src_curve_i << " (" << src_points.size() << " points)" - << std::endl; foreach_dash( pattern_info, src_points, src_cyclic[src_curve_i], [&](const IndexMask &src_points) { - { - std::cout << "Dash source points: "; - Array src_indices(src_points.size()); - src_points.to_indices(src_indices.as_mutable_span()); - for (const int i : src_indices) { - std::cout << i << ", "; - } - std::cout << std::endl; - } - dst_point_range = dst_point_range.after(src_points.size()); dst_curves.offsets_for_write()[dst_curve_i] = dst_point_range.start(); - // std::cout << dst_curve_i << ": " << dst_point_range << std::endl; src_points.to_indices(src_point_indices.as_mutable_span().slice(dst_point_range)); src_curve_indices[dst_curve_i] = src_curve_i; @@ -286,17 +249,7 @@ static bke::CurvesGeometry create_dashes(const GreasePencilDashModifierData &dmd /* Last offset entry is total point count. */ dst_curves.offsets_for_write()[dst_curve_i] = dst_point_range.one_after_last(); } - // std::cout << dst_curve_i << ": " << dst_point_range.one_after_last() << std::endl; } - std::cout << "Dash Points: " << std::endl; - for (const int i : src_point_indices.index_range()) { - std::cout << i << " <- " << src_point_indices[i] << std::endl; - } - std::cout << "Dash Curves: " << std::endl; - for (const int i : src_curve_indices.index_range()) { - std::cout << i << " <- " << src_curve_indices[i] << std::endl; - } - std::flush(std::cout); bke::gather_attributes( src_attributes, bke::AttrDomain::Point, {}, {}, src_point_indices, dst_attributes); @@ -305,7 +258,7 @@ static bke::CurvesGeometry create_dashes(const GreasePencilDashModifierData &dmd dst_curves.update_curve_types(); - return std::move(dst_curves); + return dst_curves; } static void modify_drawing(const GreasePencilDashModifierData &dmd, -- 2.30.2 From 77c8060f74dafce501848a37e031ce313a1bb3a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Tue, 6 Feb 2024 16:16:52 +0100 Subject: [PATCH 10/16] More fix for cyclic curve. --- .../intern/MOD_grease_pencil_dash.cc | 77 +++++++++---------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc index 8d4387a977a..98d536bdc52 100644 --- a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc +++ b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc @@ -40,8 +40,6 @@ #include "MOD_modifiertypes.hh" #include "MOD_ui_common.hh" -#include - namespace blender { static void init_data(ModifierData *md) @@ -152,38 +150,17 @@ static int find_dash_segment(const PatternInfo &pattern_info, const int index) /** * Iterate over all dash curves. - * \param fn: Function taking an \a IndexMask of source points, describing new curves. + * \param fn: Function taking an index range of source points describing new curves. + * \note Point range can be larger than the source point range in case of cyclic curves. */ -template static void foreach_dash(const PatternInfo &pattern_info, const IndexRange src_points, const bool cyclic, - Fn fn) + FunctionRef fn) { const int points_num = src_points.size(); const int segments_num = pattern_info.segments.size(); - auto segment_to_points_mask = [&](const int segment, IndexMaskMemory &memory) -> IndexMask { - const int repeat = segment / segments_num; - const IndexRange range = pattern_info.segments[segment - repeat * segments_num].shift( - repeat * pattern_info.length); - - const int64_t point_shift = src_points.start() - pattern_info.offset; - const int64_t min_point = src_points.start(); - const int64_t max_point = cyclic ? src_points.last() : src_points.one_after_last(); - const int64_t start = std::clamp(range.start() + point_shift, min_point, max_point); - const int64_t end = std::clamp(range.one_after_last() + point_shift, min_point, max_point); - - if (cyclic && end == max_point) { - return IndexMask::from_union(IndexRange(start, std::max(end - start - 1, int64_t(0))), - IndexRange(min_point, 1), - memory); - } - else { - return IndexRange(start, end - start); - } - }; - const int first_segment = find_dash_segment(pattern_info, pattern_info.offset); const int last_segment = find_dash_segment(pattern_info, pattern_info.offset + points_num - 1); BLI_assert(first_segment < segments_num); @@ -192,10 +169,19 @@ static void foreach_dash(const PatternInfo &pattern_info, const IndexRange all_segments = IndexRange(first_segment, last_segment - first_segment + 1); for (const int i : all_segments) { - IndexMaskMemory mask_memory; - const IndexMask points_mask = segment_to_points_mask(i, mask_memory); - if (!points_mask.is_empty()) { - fn(points_mask); + const int repeat = i / segments_num; + const IndexRange range = pattern_info.segments[i - repeat * segments_num].shift( + repeat * pattern_info.length); + + const int64_t point_shift = src_points.start() - pattern_info.offset; + const int64_t min_point = src_points.start(); + const int64_t max_point = cyclic ? src_points.one_after_last() : src_points.last(); + const int64_t start = std::clamp(range.start() + point_shift, min_point, max_point); + const int64_t end = std::clamp(range.one_after_last() + point_shift, min_point, max_point + 1); + + IndexRange points(start, end - start); + if (!points.is_empty()) { + fn(points); } } } @@ -234,16 +220,29 @@ static bke::CurvesGeometry create_dashes(const GreasePencilDashModifierData &dmd int dst_curve_i = 0; for (const int src_curve_i : src_curves.curves_range()) { const IndexRange src_points = src_curves.points_by_curve()[src_curve_i]; - foreach_dash( - pattern_info, src_points, src_cyclic[src_curve_i], [&](const IndexMask &src_points) { - dst_point_range = dst_point_range.after(src_points.size()); - dst_curves.offsets_for_write()[dst_curve_i] = dst_point_range.start(); + foreach_dash(pattern_info, + src_points, + src_cyclic[src_curve_i], + [&](const IndexRange &src_points_range) { + dst_point_range = dst_point_range.after(src_points_range.size()); + dst_curves.offsets_for_write()[dst_curve_i] = dst_point_range.start(); - src_points.to_indices(src_point_indices.as_mutable_span().slice(dst_point_range)); - src_curve_indices[dst_curve_i] = src_curve_i; + if (src_points.contains(src_points_range.last())) { + array_utils::fill_index_range( + src_point_indices.as_mutable_span().slice(dst_point_range), + int(src_points_range.start())); + } + else { + /* Cyclic curve. */ + array_utils::fill_index_range( + src_point_indices.as_mutable_span().slice(dst_point_range.drop_back(1)), + int(src_points_range.start())); + src_point_indices[dst_point_range.last()] = src_points.first(); + } + src_curve_indices[dst_curve_i] = src_curve_i; - ++dst_curve_i; - }); + ++dst_curve_i; + }); } if (dst_curve_i > 0) { /* Last offset entry is total point count. */ @@ -254,7 +253,7 @@ static bke::CurvesGeometry create_dashes(const GreasePencilDashModifierData &dmd bke::gather_attributes( src_attributes, bke::AttrDomain::Point, {}, {}, src_point_indices, dst_attributes); bke::gather_attributes( - src_attributes, bke::AttrDomain::Curve, {}, {}, src_curve_indices, dst_attributes); + src_attributes, bke::AttrDomain::Curve, {}, {"cyclic"}, src_curve_indices, dst_attributes); dst_curves.update_curve_types(); -- 2.30.2 From a63858f6b8e716587b91e57bee52c4e41d138b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Tue, 6 Feb 2024 16:59:06 +0100 Subject: [PATCH 11/16] Added attribute transfer from segments. --- source/blender/makesdna/DNA_modifier_types.h | 8 +- .../intern/MOD_grease_pencil_dash.cc | 91 +++++++++++++++---- 2 files changed, 76 insertions(+), 23 deletions(-) diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 4c29fbdd306..4cb47d9a071 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -2775,21 +2775,21 @@ typedef struct GreasePencilDashModifierSegment { float radius; float opacity; int mat_nr; + /** #GreasePencilDashModifierFlag */ int flag; } GreasePencilDashModifierSegment; typedef struct GreasePencilDashModifierData { ModifierData modifier; GreasePencilModifierInfluenceData influence; - /** #GreasePencilDashModifierFlag */ - int flag; - - int dash_offset; GreasePencilDashModifierSegment *segments_array; int segments_num; int segment_active_index; + int dash_offset; + char _pad[4]; + #ifdef __cplusplus blender::Span segments() const; blender::MutableSpan segments(); diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc index 98d536bdc52..ff6c947b00d 100644 --- a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc +++ b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc @@ -107,6 +107,10 @@ struct PatternInfo { int offset = 0; int length = 0; Array segments; + Array cyclic; + Array material; + Array radius; + Array opacity; }; static PatternInfo get_pattern_info(const GreasePencilDashModifierData &dmd) @@ -117,6 +121,10 @@ static PatternInfo get_pattern_info(const GreasePencilDashModifierData &dmd) } info.segments.reinitialize(dmd.segments().size()); + info.cyclic.reinitialize(dmd.segments().size()); + info.material.reinitialize(dmd.segments().size()); + info.radius.reinitialize(dmd.segments().size()); + info.opacity.reinitialize(dmd.segments().size()); info.offset = floored_modulo(dmd.dash_offset, info.length); /* Store segments as ranges. */ @@ -127,6 +135,10 @@ static PatternInfo get_pattern_info(const GreasePencilDashModifierData &dmd) dash_range = gap_range.after(dash_segment.dash); gap_range = dash_range.after(dash_segment.gap); info.segments[i] = dash_range; + info.cyclic[i] = dash_segment.flag & MOD_GREASE_PENCIL_DASH_USE_CYCLIC; + info.material[i] = dash_segment.mat_nr; + info.radius[i] = dash_segment.radius; + info.opacity[i] = dash_segment.opacity; } return info; } @@ -156,7 +168,7 @@ static int find_dash_segment(const PatternInfo &pattern_info, const int index) static void foreach_dash(const PatternInfo &pattern_info, const IndexRange src_points, const bool cyclic, - FunctionRef fn) + FunctionRef fn) { const int points_num = src_points.size(); const int segments_num = pattern_info.segments.size(); @@ -165,13 +177,13 @@ static void foreach_dash(const PatternInfo &pattern_info, const int last_segment = find_dash_segment(pattern_info, pattern_info.offset + points_num - 1); BLI_assert(first_segment < segments_num); BLI_assert(last_segment >= first_segment); - const IndexRange first_segment_points = pattern_info.segments[first_segment]; const IndexRange all_segments = IndexRange(first_segment, last_segment - first_segment + 1); for (const int i : all_segments) { const int repeat = i / segments_num; - const IndexRange range = pattern_info.segments[i - repeat * segments_num].shift( - repeat * pattern_info.length); + const int segment_index = i - repeat * segments_num; + const IndexRange range = pattern_info.segments[segment_index].shift(repeat * + pattern_info.length); const int64_t point_shift = src_points.start() - pattern_info.offset; const int64_t min_point = src_points.start(); @@ -181,19 +193,25 @@ static void foreach_dash(const PatternInfo &pattern_info, IndexRange points(start, end - start); if (!points.is_empty()) { - fn(points); + fn(points, + pattern_info.cyclic[segment_index], + pattern_info.material[segment_index], + pattern_info.radius[segment_index], + pattern_info.opacity[segment_index]); } } } -static bke::CurvesGeometry create_dashes(const GreasePencilDashModifierData &dmd, - const PatternInfo &pattern_info, +static bke::CurvesGeometry create_dashes(const PatternInfo &pattern_info, const bke::CurvesGeometry &src_curves, const IndexMask &curves_mask) { const bke::AttributeAccessor src_attributes = src_curves.attributes(); const VArray src_cyclic = *src_attributes.lookup_or_default( "cyclic", bke::AttrDomain::Curve, false); + const VArray src_radius = *src_attributes.lookup("radius", bke::AttrDomain::Point); + const VArray src_opacity = *src_attributes.lookup("opacity", + bke::AttrDomain::Point); /* Count new curves and points. */ int dst_point_num = 0; @@ -201,15 +219,29 @@ static bke::CurvesGeometry create_dashes(const GreasePencilDashModifierData &dmd for (const int src_curve_i : src_curves.curves_range()) { const IndexRange src_points = src_curves.points_by_curve()[src_curve_i]; - foreach_dash( - pattern_info, src_points, src_cyclic[src_curve_i], [&](const IndexMask &src_points) { - dst_point_num += src_points.size(); - dst_curve_num += 1; - }); + foreach_dash(pattern_info, + src_points, + src_cyclic[src_curve_i], + [&](const IndexMask &src_points, + bool /*cyclic*/, + int /*material*/, + float /*radius*/, + float /*opacity*/) { + dst_point_num += src_points.size(); + dst_curve_num += 1; + }); } bke::CurvesGeometry dst_curves(dst_point_num, dst_curve_num); bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); + bke::SpanAttributeWriter dst_cyclic = dst_attributes.lookup_or_add_for_write_span( + "cyclic", bke::AttrDomain::Curve); + bke::SpanAttributeWriter dst_material = dst_attributes.lookup_or_add_for_write_span( + "material_index", bke::AttrDomain::Curve); + bke::SpanAttributeWriter dst_radius = dst_attributes.lookup_or_add_for_write_span( + "radius", bke::AttrDomain::Point); + bke::SpanAttributeWriter dst_opacity = dst_attributes.lookup_or_add_for_write_span( + "opacity", bke::AttrDomain::Point); /* Map each destination point and curve to its source. */ Array src_point_indices(dst_point_num); Array src_curve_indices(dst_curve_num); @@ -223,7 +255,11 @@ static bke::CurvesGeometry create_dashes(const GreasePencilDashModifierData &dmd foreach_dash(pattern_info, src_points, src_cyclic[src_curve_i], - [&](const IndexRange &src_points_range) { + [&](const IndexRange &src_points_range, + bool cyclic, + int material, + float radius, + float opacity) { dst_point_range = dst_point_range.after(src_points_range.size()); dst_curves.offsets_for_write()[dst_curve_i] = dst_point_range.start(); @@ -240,6 +276,12 @@ static bke::CurvesGeometry create_dashes(const GreasePencilDashModifierData &dmd src_point_indices[dst_point_range.last()] = src_points.first(); } src_curve_indices[dst_curve_i] = src_curve_i; + dst_cyclic.span[dst_curve_i] = cyclic; + dst_material.span[dst_curve_i] = material; + for (const int i : dst_point_range) { + dst_radius.span[i] = src_radius[src_point_indices[i]] * radius; + dst_opacity.span[i] = src_opacity[src_point_indices[i]] * opacity; + } ++dst_curve_i; }); @@ -250,11 +292,23 @@ static bke::CurvesGeometry create_dashes(const GreasePencilDashModifierData &dmd } } - bke::gather_attributes( - src_attributes, bke::AttrDomain::Point, {}, {}, src_point_indices, dst_attributes); - bke::gather_attributes( - src_attributes, bke::AttrDomain::Curve, {}, {"cyclic"}, src_curve_indices, dst_attributes); + bke::gather_attributes(src_attributes, + bke::AttrDomain::Point, + {}, + {"radius", "opacity"}, + src_point_indices, + dst_attributes); + bke::gather_attributes(src_attributes, + bke::AttrDomain::Curve, + {}, + {"cyclic", "material_index"}, + src_curve_indices, + dst_attributes); + dst_cyclic.finish(); + dst_material.finish(); + dst_radius.finish(); + dst_opacity.finish(); dst_curves.update_curve_types(); return dst_curves; @@ -265,7 +319,6 @@ static void modify_drawing(const GreasePencilDashModifierData &dmd, const PatternInfo &pattern_info, bke::greasepencil::Drawing &drawing) { - UNUSED_VARS(dmd, ctx); const bke::CurvesGeometry &src_curves = drawing.strokes(); if (src_curves.curve_num == 0) { return; @@ -275,7 +328,7 @@ static void modify_drawing(const GreasePencilDashModifierData &dmd, const IndexMask curves_mask = modifier::greasepencil::get_filtered_stroke_mask( ctx.object, src_curves, dmd.influence, curve_mask_memory); - drawing.strokes_for_write() = create_dashes(dmd, pattern_info, src_curves, curves_mask); + drawing.strokes_for_write() = create_dashes(pattern_info, src_curves, curves_mask); drawing.tag_topology_changed(); } -- 2.30.2 From 7b52fa007b1ad63d4cc2183569df6198e720a86d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 8 Feb 2024 12:55:20 +0100 Subject: [PATCH 12/16] Put operator callbacks into `blender::ed::grease_pencil` namespace. --- .../blender/editors/object/object_modifier.cc | 64 +++++++++++-------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc index 37456845a40..117d6b0df05 100644 --- a/source/blender/editors/object/object_modifier.cc +++ b/source/blender/editors/object/object_modifier.cc @@ -3735,12 +3735,14 @@ void OBJECT_OT_geometry_node_tree_copy_assign(wmOperatorType *ot) /** \name Dash Modifier * \{ */ -static bool grease_pencil_dash_modifier_segment_poll(bContext *C) +namespace blender::ed::grease_pencil { + +static bool dash_modifier_segment_poll(bContext *C) { return edit_modifier_poll_generic(C, &RNA_GreasePencilDashModifierData, 0, false, false); } -static int grease_pencil_dash_modifier_segment_add_exec(bContext *C, wmOperator *op) +static int dash_modifier_segment_add_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_active_context(C); auto *dmd = reinterpret_cast( @@ -3793,16 +3795,16 @@ static int grease_pencil_dash_modifier_segment_add_exec(bContext *C, wmOperator return OPERATOR_FINISHED; } -static int grease_pencil_dash_modifier_segment_add_invoke(bContext *C, - wmOperator *op, - const wmEvent * /*event*/) +static int dash_modifier_segment_add_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) { if (edit_modifier_invoke_properties(C, op)) { - return grease_pencil_dash_modifier_segment_add_exec(C, op); + return dash_modifier_segment_add_exec(C, op); } return OPERATOR_CANCELLED; } +} // namespace blender::ed::grease_pencil + void OBJECT_OT_grease_pencil_dash_modifier_segment_add(wmOperatorType *ot) { /* identifiers */ @@ -3811,18 +3813,20 @@ void OBJECT_OT_grease_pencil_dash_modifier_segment_add(wmOperatorType *ot) ot->idname = "OBJECT_OT_grease_pencil_dash_modifier_segment_add"; /* api callbacks */ - ot->poll = grease_pencil_dash_modifier_segment_poll; - ot->invoke = grease_pencil_dash_modifier_segment_add_invoke; - ot->exec = grease_pencil_dash_modifier_segment_add_exec; + ot->poll = blender::ed::grease_pencil::dash_modifier_segment_poll; + ot->invoke = blender::ed::grease_pencil::dash_modifier_segment_add_invoke; + ot->exec = blender::ed::grease_pencil::dash_modifier_segment_add_exec; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; edit_modifier_properties(ot); } -static void grease_pencil_dash_modifier_segment_free(GreasePencilDashModifierSegment * /*ds*/) {} +namespace blender::ed::grease_pencil { -static int grease_pencil_dash_modifier_segment_remove_exec(bContext *C, wmOperator *op) +static void dash_modifier_segment_free(GreasePencilDashModifierSegment * /*ds*/) {} + +static int dash_modifier_segment_remove_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_active_context(C); auto *dmd = reinterpret_cast( @@ -3840,7 +3844,7 @@ static int grease_pencil_dash_modifier_segment_remove_exec(bContext *C, wmOperat &dmd->segments_num, &dmd->segment_active_index, dmd->segment_active_index, - grease_pencil_dash_modifier_segment_free); + dash_modifier_segment_free); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); @@ -3848,16 +3852,18 @@ static int grease_pencil_dash_modifier_segment_remove_exec(bContext *C, wmOperat return OPERATOR_FINISHED; } -static int grease_pencil_dash_modifier_segment_remove_invoke(bContext *C, - wmOperator *op, - const wmEvent * /*event*/) +static int dash_modifier_segment_remove_invoke(bContext *C, + wmOperator *op, + const wmEvent * /*event*/) { if (edit_modifier_invoke_properties(C, op)) { - return grease_pencil_dash_modifier_segment_remove_exec(C, op); + return dash_modifier_segment_remove_exec(C, op); } return OPERATOR_CANCELLED; } +} // namespace blender::ed::grease_pencil + void OBJECT_OT_grease_pencil_dash_modifier_segment_remove(wmOperatorType *ot) { /* identifiers */ @@ -3866,9 +3872,9 @@ void OBJECT_OT_grease_pencil_dash_modifier_segment_remove(wmOperatorType *ot) ot->idname = "OBJECT_OT_grease_pencil_dash_modifier_segment_remove"; /* api callbacks */ - ot->poll = grease_pencil_dash_modifier_segment_poll; - ot->invoke = grease_pencil_dash_modifier_segment_remove_invoke; - ot->exec = grease_pencil_dash_modifier_segment_remove_exec; + ot->poll = blender::ed::grease_pencil::dash_modifier_segment_poll; + ot->invoke = blender::ed::grease_pencil::dash_modifier_segment_remove_invoke; + ot->exec = blender::ed::grease_pencil::dash_modifier_segment_remove_exec; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; @@ -3883,7 +3889,9 @@ enum { MOD_GREASE_PENCIL_DASH_SEGMENT_MOVE_DOWN = 1, }; -static int grease_pencil_dash_modifier_segment_move_exec(bContext *C, wmOperator *op) +namespace blender::ed::grease_pencil { + +static int dash_modifier_segment_move_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_active_context(C); auto *dmd = reinterpret_cast( @@ -3928,16 +3936,18 @@ static int grease_pencil_dash_modifier_segment_move_exec(bContext *C, wmOperator return OPERATOR_FINISHED; } -static int grease_pencil_dash_modifier_segment_move_invoke(bContext *C, - wmOperator *op, - const wmEvent * /*event*/) +static int dash_modifier_segment_move_invoke(bContext *C, + wmOperator *op, + const wmEvent * /*event*/) { if (edit_modifier_invoke_properties(C, op)) { - return grease_pencil_dash_modifier_segment_move_exec(C, op); + return dash_modifier_segment_move_exec(C, op); } return OPERATOR_CANCELLED; } +} // namespace blender::ed::grease_pencil + void OBJECT_OT_grease_pencil_dash_modifier_segment_move(wmOperatorType *ot) { static const EnumPropertyItem segment_move[] = { @@ -3952,9 +3962,9 @@ void OBJECT_OT_grease_pencil_dash_modifier_segment_move(wmOperatorType *ot) ot->idname = "OBJECT_OT_grease_pencil_dash_modifier_segment_move"; /* api callbacks */ - ot->poll = grease_pencil_dash_modifier_segment_poll; - ot->invoke = grease_pencil_dash_modifier_segment_move_invoke; - ot->exec = grease_pencil_dash_modifier_segment_move_exec; + ot->poll = blender::ed::grease_pencil::dash_modifier_segment_poll; + ot->invoke = blender::ed::grease_pencil::dash_modifier_segment_move_invoke; + ot->exec = blender::ed::grease_pencil::dash_modifier_segment_move_exec; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; -- 2.30.2 From 2ede23a643203576bd9129e62f12d35faaaca346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 8 Feb 2024 13:00:06 +0100 Subject: [PATCH 13/16] Enum class for the DashSegmentMoveDirection. --- .../blender/editors/object/object_modifier.cc | 62 ++++++++++--------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc index 117d6b0df05..ac400da42a8 100644 --- a/source/blender/editors/object/object_modifier.cc +++ b/source/blender/editors/object/object_modifier.cc @@ -3884,13 +3884,13 @@ void OBJECT_OT_grease_pencil_dash_modifier_segment_remove(wmOperatorType *ot) ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of the segment to remove", 0, INT_MAX); } -enum { - MOD_GREASE_PENCIL_DASH_SEGMENT_MOVE_UP = -1, - MOD_GREASE_PENCIL_DASH_SEGMENT_MOVE_DOWN = 1, -}; - namespace blender::ed::grease_pencil { +enum class DashSegmentMoveDirection { + Up = -1, + Down = 1, +}; + static int dash_modifier_segment_move_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_active_context(C); @@ -3905,29 +3905,31 @@ static int dash_modifier_segment_move_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - const int direction = RNA_enum_get(op->ptr, "type"); - if (direction == MOD_GREASE_PENCIL_DASH_SEGMENT_MOVE_UP) { - if (dmd->segment_active_index == 0) { + const DashSegmentMoveDirection direction = DashSegmentMoveDirection( + RNA_enum_get(op->ptr, "type")); + switch (direction) { + case DashSegmentMoveDirection::Up: + if (dmd->segment_active_index == 0) { + return OPERATOR_CANCELLED; + } + + std::swap(dmd->segments_array[dmd->segment_active_index], + dmd->segments_array[dmd->segment_active_index - 1]); + + dmd->segment_active_index--; + break; + case DashSegmentMoveDirection::Down: + if (dmd->segment_active_index == dmd->segments_num - 1) { + return OPERATOR_CANCELLED; + } + + std::swap(dmd->segments_array[dmd->segment_active_index], + dmd->segments_array[dmd->segment_active_index + 1]); + + dmd->segment_active_index++; + break; + default: return OPERATOR_CANCELLED; - } - - std::swap(dmd->segments_array[dmd->segment_active_index], - dmd->segments_array[dmd->segment_active_index - 1]); - - dmd->segment_active_index--; - } - else if (direction == MOD_GREASE_PENCIL_DASH_SEGMENT_MOVE_DOWN) { - if (dmd->segment_active_index == dmd->segments_num - 1) { - return OPERATOR_CANCELLED; - } - - std::swap(dmd->segments_array[dmd->segment_active_index], - dmd->segments_array[dmd->segment_active_index + 1]); - - dmd->segment_active_index++; - } - else { - return OPERATOR_CANCELLED; } DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); @@ -3950,9 +3952,11 @@ static int dash_modifier_segment_move_invoke(bContext *C, void OBJECT_OT_grease_pencil_dash_modifier_segment_move(wmOperatorType *ot) { + using blender::ed::grease_pencil::DashSegmentMoveDirection; + static const EnumPropertyItem segment_move[] = { - {MOD_GREASE_PENCIL_DASH_SEGMENT_MOVE_UP, "UP", 0, "Up", ""}, - {MOD_GREASE_PENCIL_DASH_SEGMENT_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {int(DashSegmentMoveDirection::Up), "UP", 0, "Up", ""}, + {int(DashSegmentMoveDirection::Down), "DOWN", 0, "Down", ""}, {0, nullptr, 0, nullptr, nullptr}, }; -- 2.30.2 From c75822a43c3cf3c997f369c5b4c2f50b0cdbfb7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 8 Feb 2024 13:58:45 +0100 Subject: [PATCH 14/16] Move dash copy lambda out of the for loop. --- .../intern/MOD_grease_pencil_dash.cc | 72 +++++++++++-------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc index ff6c947b00d..ec840f9c835 100644 --- a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc +++ b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc @@ -216,21 +216,21 @@ static bke::CurvesGeometry create_dashes(const PatternInfo &pattern_info, /* Count new curves and points. */ int dst_point_num = 0; int dst_curve_num = 0; - for (const int src_curve_i : src_curves.curves_range()) { + curves_mask.foreach_index([&](const int64_t src_curve_i) { const IndexRange src_points = src_curves.points_by_curve()[src_curve_i]; foreach_dash(pattern_info, src_points, src_cyclic[src_curve_i], - [&](const IndexMask &src_points, + [&](const IndexRange copy_points, bool /*cyclic*/, int /*material*/, float /*radius*/, float /*opacity*/) { - dst_point_num += src_points.size(); + dst_point_num += copy_points.size(); dst_curve_num += 1; }); - } + }); bke::CurvesGeometry dst_curves(dst_point_num, dst_curve_num); bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); @@ -250,42 +250,52 @@ static bke::CurvesGeometry create_dashes(const PatternInfo &pattern_info, /* Start at curve offset and add points for each dash. */ IndexRange dst_point_range(0); int dst_curve_i = 0; - for (const int src_curve_i : src_curves.curves_range()) { + auto add_dash_curve = [&](const int src_curve, + const IndexRange src_points, + const IndexRange copy_points, + bool cyclic, + int material, + float radius, + float opacity) { + dst_point_range = dst_point_range.after(copy_points.size()); + dst_curves.offsets_for_write()[dst_curve_i] = dst_point_range.start(); + + if (src_points.contains(copy_points.last())) { + array_utils::fill_index_range(src_point_indices.as_mutable_span().slice(dst_point_range), + int(copy_points.start())); + } + else { + /* Cyclic curve. */ + array_utils::fill_index_range( + src_point_indices.as_mutable_span().slice(dst_point_range.drop_back(1)), + int(copy_points.start())); + src_point_indices[dst_point_range.last()] = src_points.first(); + } + src_curve_indices[dst_curve_i] = src_curve; + dst_cyclic.span[dst_curve_i] = cyclic; + dst_material.span[dst_curve_i] = material; + for (const int i : dst_point_range) { + dst_radius.span[i] = src_radius[src_point_indices[i]] * radius; + dst_opacity.span[i] = src_opacity[src_point_indices[i]] * opacity; + } + + ++dst_curve_i; + }; + + curves_mask.foreach_index([&](const int64_t src_curve_i) { const IndexRange src_points = src_curves.points_by_curve()[src_curve_i]; foreach_dash(pattern_info, src_points, src_cyclic[src_curve_i], - [&](const IndexRange &src_points_range, + [&](const IndexRange copy_points, bool cyclic, int material, float radius, float opacity) { - dst_point_range = dst_point_range.after(src_points_range.size()); - dst_curves.offsets_for_write()[dst_curve_i] = dst_point_range.start(); - - if (src_points.contains(src_points_range.last())) { - array_utils::fill_index_range( - src_point_indices.as_mutable_span().slice(dst_point_range), - int(src_points_range.start())); - } - else { - /* Cyclic curve. */ - array_utils::fill_index_range( - src_point_indices.as_mutable_span().slice(dst_point_range.drop_back(1)), - int(src_points_range.start())); - src_point_indices[dst_point_range.last()] = src_points.first(); - } - src_curve_indices[dst_curve_i] = src_curve_i; - dst_cyclic.span[dst_curve_i] = cyclic; - dst_material.span[dst_curve_i] = material; - for (const int i : dst_point_range) { - dst_radius.span[i] = src_radius[src_point_indices[i]] * radius; - dst_opacity.span[i] = src_opacity[src_point_indices[i]] * opacity; - } - - ++dst_curve_i; + add_dash_curve( + src_curve_i, src_points, copy_points, cyclic, material, radius, opacity); }); - } + }); if (dst_curve_i > 0) { /* Last offset entry is total point count. */ dst_curves.offsets_for_write()[dst_curve_i] = dst_point_range.one_after_last(); -- 2.30.2 From 1ecc246e34403f7838e086449b32df64661e74b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 8 Feb 2024 14:04:23 +0100 Subject: [PATCH 15/16] Handle negative material index of dashes as "use source material". --- source/blender/modifiers/intern/MOD_grease_pencil_dash.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc index ec840f9c835..448d8cd11a7 100644 --- a/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc +++ b/source/blender/modifiers/intern/MOD_grease_pencil_dash.cc @@ -209,6 +209,8 @@ static bke::CurvesGeometry create_dashes(const PatternInfo &pattern_info, const bke::AttributeAccessor src_attributes = src_curves.attributes(); const VArray src_cyclic = *src_attributes.lookup_or_default( "cyclic", bke::AttrDomain::Curve, false); + const VArray src_material = *src_attributes.lookup_or_default( + "material_index", bke::AttrDomain::Curve, 0); const VArray src_radius = *src_attributes.lookup("radius", bke::AttrDomain::Point); const VArray src_opacity = *src_attributes.lookup("opacity", bke::AttrDomain::Point); @@ -273,7 +275,7 @@ static bke::CurvesGeometry create_dashes(const PatternInfo &pattern_info, } src_curve_indices[dst_curve_i] = src_curve; dst_cyclic.span[dst_curve_i] = cyclic; - dst_material.span[dst_curve_i] = material; + dst_material.span[dst_curve_i] = material >= 0 ? material : src_material[src_curve]; for (const int i : dst_point_range) { dst_radius.span[i] = src_radius[src_point_indices[i]] * radius; dst_opacity.span[i] = src_opacity[src_point_indices[i]] * opacity; -- 2.30.2 From 0b3248cb3b419910dac14cf8f95d15eeaa3c8010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 8 Feb 2024 14:07:23 +0100 Subject: [PATCH 16/16] namespace grease_pencil -> greasepencil. --- .../blender/editors/object/object_modifier.cc | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc index ac400da42a8..3fbee8340ad 100644 --- a/source/blender/editors/object/object_modifier.cc +++ b/source/blender/editors/object/object_modifier.cc @@ -3735,7 +3735,7 @@ void OBJECT_OT_geometry_node_tree_copy_assign(wmOperatorType *ot) /** \name Dash Modifier * \{ */ -namespace blender::ed::grease_pencil { +namespace blender::ed::greasepencil { static bool dash_modifier_segment_poll(bContext *C) { @@ -3803,7 +3803,7 @@ static int dash_modifier_segment_add_invoke(bContext *C, wmOperator *op, const w return OPERATOR_CANCELLED; } -} // namespace blender::ed::grease_pencil +} // namespace blender::ed::greasepencil void OBJECT_OT_grease_pencil_dash_modifier_segment_add(wmOperatorType *ot) { @@ -3813,16 +3813,16 @@ void OBJECT_OT_grease_pencil_dash_modifier_segment_add(wmOperatorType *ot) ot->idname = "OBJECT_OT_grease_pencil_dash_modifier_segment_add"; /* api callbacks */ - ot->poll = blender::ed::grease_pencil::dash_modifier_segment_poll; - ot->invoke = blender::ed::grease_pencil::dash_modifier_segment_add_invoke; - ot->exec = blender::ed::grease_pencil::dash_modifier_segment_add_exec; + ot->poll = blender::ed::greasepencil::dash_modifier_segment_poll; + ot->invoke = blender::ed::greasepencil::dash_modifier_segment_add_invoke; + ot->exec = blender::ed::greasepencil::dash_modifier_segment_add_exec; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; edit_modifier_properties(ot); } -namespace blender::ed::grease_pencil { +namespace blender::ed::greasepencil { static void dash_modifier_segment_free(GreasePencilDashModifierSegment * /*ds*/) {} @@ -3862,7 +3862,7 @@ static int dash_modifier_segment_remove_invoke(bContext *C, return OPERATOR_CANCELLED; } -} // namespace blender::ed::grease_pencil +} // namespace blender::ed::greasepencil void OBJECT_OT_grease_pencil_dash_modifier_segment_remove(wmOperatorType *ot) { @@ -3872,9 +3872,9 @@ void OBJECT_OT_grease_pencil_dash_modifier_segment_remove(wmOperatorType *ot) ot->idname = "OBJECT_OT_grease_pencil_dash_modifier_segment_remove"; /* api callbacks */ - ot->poll = blender::ed::grease_pencil::dash_modifier_segment_poll; - ot->invoke = blender::ed::grease_pencil::dash_modifier_segment_remove_invoke; - ot->exec = blender::ed::grease_pencil::dash_modifier_segment_remove_exec; + ot->poll = blender::ed::greasepencil::dash_modifier_segment_poll; + ot->invoke = blender::ed::greasepencil::dash_modifier_segment_remove_invoke; + ot->exec = blender::ed::greasepencil::dash_modifier_segment_remove_exec; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; @@ -3884,7 +3884,7 @@ void OBJECT_OT_grease_pencil_dash_modifier_segment_remove(wmOperatorType *ot) ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of the segment to remove", 0, INT_MAX); } -namespace blender::ed::grease_pencil { +namespace blender::ed::greasepencil { enum class DashSegmentMoveDirection { Up = -1, @@ -3948,11 +3948,11 @@ static int dash_modifier_segment_move_invoke(bContext *C, return OPERATOR_CANCELLED; } -} // namespace blender::ed::grease_pencil +} // namespace blender::ed::greasepencil void OBJECT_OT_grease_pencil_dash_modifier_segment_move(wmOperatorType *ot) { - using blender::ed::grease_pencil::DashSegmentMoveDirection; + using blender::ed::greasepencil::DashSegmentMoveDirection; static const EnumPropertyItem segment_move[] = { {int(DashSegmentMoveDirection::Up), "UP", 0, "Up", ""}, @@ -3966,9 +3966,9 @@ void OBJECT_OT_grease_pencil_dash_modifier_segment_move(wmOperatorType *ot) ot->idname = "OBJECT_OT_grease_pencil_dash_modifier_segment_move"; /* api callbacks */ - ot->poll = blender::ed::grease_pencil::dash_modifier_segment_poll; - ot->invoke = blender::ed::grease_pencil::dash_modifier_segment_move_invoke; - ot->exec = blender::ed::grease_pencil::dash_modifier_segment_move_exec; + ot->poll = blender::ed::greasepencil::dash_modifier_segment_poll; + ot->invoke = blender::ed::greasepencil::dash_modifier_segment_move_invoke; + ot->exec = blender::ed::greasepencil::dash_modifier_segment_move_exec; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; -- 2.30.2