Blender V5.0
MOD_grease_pencil_multiply.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_math_matrix.hh"
10
11#include "DNA_defaults.h"
12#include "DNA_modifier_types.h"
13#include "DNA_scene_types.h"
14
15#include "BKE_attribute.hh"
16#include "BKE_curves.hh"
17#include "BKE_geometry_set.hh"
18#include "BKE_grease_pencil.hh"
19#include "BKE_instances.hh"
20#include "BKE_modifier.hh"
21#include "BKE_screen.hh"
22
23#include "BLO_read_write.hh"
24
26
28
30#include "UI_resources.hh"
31
32#include "BLT_translation.hh"
33
34#include "WM_types.hh"
35
36#include "RNA_access.hh"
37#include "RNA_enum_types.hh"
38#include "RNA_prototypes.hh"
39
41#include "MOD_modifiertypes.hh"
42#include "MOD_ui_common.hh"
43
44namespace blender {
45
47
57
58static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
59{
60 const auto *mmd = reinterpret_cast<const GreasePencilMultiModifierData *>(md);
61 auto *tmmd = reinterpret_cast<GreasePencilMultiModifierData *>(target);
62
64 modifier::greasepencil::copy_influence_data(&mmd->influence, &tmmd->influence, flag);
65}
66
67static void free_data(ModifierData *md)
68{
69 auto *mmd = reinterpret_cast<GreasePencilMultiModifierData *>(md);
71}
72
73static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
74{
75 auto *mmd = reinterpret_cast<GreasePencilMultiModifierData *>(md);
76 modifier::greasepencil::foreach_influence_ID_link(&mmd->influence, ob, walk, user_data);
77}
78
79static bool is_disabled(const Scene * /*scene*/, ModifierData *md, bool /*use_render_params*/)
80{
81 auto *mmd = reinterpret_cast<GreasePencilMultiModifierData *>(md);
82 if (mmd->duplications <= 1) {
83 return true;
84 }
85 return false;
86}
87
89 const IndexMask curves_mask,
90 const IndexMask unselected_mask,
91 const int count,
92 int &r_original_point_count,
93 int &r_original_curve_count)
94{
95 bke::CurvesGeometry masked_curves = bke::curves_copy_curve_selection(curves, curves_mask, {});
97 curves, unselected_mask, {});
98
99 r_original_point_count = masked_curves.points_num();
100 r_original_curve_count = masked_curves.curves_num();
101
102 Curves *masked_curves_id = bke::curves_new_nomain(masked_curves);
103 Curves *unselected_curves_id = bke::curves_new_nomain(unselected_curves);
104
105 bke::GeometrySet masked_geo = bke::GeometrySet::from_curves(masked_curves_id);
106 bke::GeometrySet unselected_geo = bke::GeometrySet::from_curves(unselected_curves_id);
107
108 std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
109 const int masked_handle = instances->add_reference(bke::InstanceReference{masked_geo});
110 const int unselected_handle = instances->add_reference(bke::InstanceReference{unselected_geo});
111
112 for ([[maybe_unused]] const int i : IndexRange(count)) {
113 instances->add_instance(masked_handle, float4x4::identity());
114 }
115 instances->add_instance(unselected_handle, float4x4::identity());
116
118 options.keep_original_ids = true;
119 options.realize_instance_attributes = true;
121 bke::GeometrySet::from_instances(instances.release()), options)
122 .geometry;
123 return std::move(result_geo.get_curves_for_write()->geometry.wrap());
124}
125
127 const ModifierEvalContext &ctx,
128 Drawing &drawing)
129{
130 bke::CurvesGeometry &curves = drawing.strokes_for_write();
131
132 IndexMaskMemory mask_memory;
134 ctx.object, curves, mmd.influence, mask_memory);
135
136 if (curves_mask.is_empty()) {
137 return;
138 }
139
140 const IndexMask unselected_mask = curves_mask.complement(curves.curves_range(), mask_memory);
141
142 int src_point_count, src_curve_count;
143 curves = duplicate_strokes(
144 curves, curves_mask, unselected_mask, mmd.duplications, src_point_count, src_curve_count);
145
146 const float offset = math::length(math::to_scale(ctx.object->object_to_world())) * mmd.offset;
147 const float distance = mmd.distance;
148 const bool use_fading = (mmd.flag & MOD_GREASE_PENCIL_MULTIPLY_ENABLE_FADING) != 0;
149 const float fading_thickness = mmd.fading_thickness;
150 const float fading_opacity = mmd.fading_opacity;
151 const float fading_center = mmd.fading_center;
152
153 MutableSpan<float3> positions = curves.positions_for_write();
154 const Span<float3> tangents = curves.evaluated_tangents();
155 const Span<float3> normals = drawing.curve_plane_normals();
156
159 "opacity", bke::AttrDomain::Point);
161 "radius", bke::AttrDomain::Point);
162
163 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
164
165 Array<float3> pos_l(src_point_count);
166 Array<float3> pos_r(src_point_count);
167
168 int src_point_i = 0;
169 for (const int src_curve_i : IndexRange(src_curve_count)) {
170 for (const int point : points_by_curve[src_curve_i]) {
171 const float3 miter = math::cross(normals[src_curve_i], tangents[point]) * distance;
172 pos_l[src_point_i] = positions[point] + miter;
173 pos_r[src_point_i] = positions[point] - miter;
174 src_point_i++;
175 }
176 }
177
178 const Span<float3> stroke_pos_l = pos_l.as_span();
179 const Span<float3> stroke_pos_r = pos_r.as_span();
180
181 for (const int i : IndexRange(mmd.duplications)) {
183 const IndexRange stroke = IndexRange(src_point_count * i, src_point_count);
184 MutableSpan<float3> instance_positions = positions.slice(stroke);
185 MutableSpan<float> instance_radii = radii.span.slice(stroke);
186 const float offset_fac = (mmd.duplications == 1) ?
187 0.5f :
188 (1.0f - (float(i) / float(mmd.duplications - 1)));
189 const float fading_fac = fabsf(offset_fac - fading_center);
190 const float thickness_factor = use_fading ? mix2(fading_fac, 1.0f, 1.0f - fading_thickness) :
191 1.0f;
192 threading::parallel_for(instance_positions.index_range(), 512, [&](const IndexRange range) {
193 for (const int point : range) {
194 const float fac = mix2(float(i) / float(mmd.duplications - 1), 1 + offset, offset);
195 const int old_point = point % src_point_count;
196 instance_positions[point] = mix2(fac, stroke_pos_l[old_point], stroke_pos_r[old_point]);
197 instance_radii[point] *= thickness_factor;
198 }
199 });
200
201 if (opacities) {
202 MutableSpan<float> instance_opacity = opacities.span.slice(stroke);
203 const float opacity_factor = use_fading ? mix2(fading_fac, 1.0f, 1.0f - fading_opacity) :
204 1.0f;
205 threading::parallel_for(instance_positions.index_range(), 512, [&](const IndexRange range) {
206 for (const int point : range) {
207 instance_opacity[point] *= opacity_factor;
208 }
209 });
210 }
211 }
212
213 radii.finish();
214 opacities.finish();
215
216 drawing.tag_topology_changed();
217}
218
220 const ModifierEvalContext *ctx,
221 bke::GeometrySet *geometry_set)
222{
223 auto *mmd = reinterpret_cast<GreasePencilMultiModifierData *>(md);
224
225 if (!geometry_set->has_grease_pencil()) {
226 return;
227 }
228 GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write();
229 const int frame = grease_pencil.runtime->eval_frame;
230
231 IndexMaskMemory memory;
233 grease_pencil, mmd->influence, memory);
235 grease_pencil, layer_mask, frame);
237 [&](Drawing *drawing) { generate_curves(*mmd, *ctx, *drawing); });
238}
239
240static void panel_draw(const bContext *C, Panel *panel)
241{
242 uiLayout *layout = panel->layout;
243
244 PointerRNA ob_ptr;
246
247 layout->use_property_split_set(true);
248
249 layout->prop(ptr, "duplicates", UI_ITEM_NONE, std::nullopt, ICON_NONE);
250
251 uiLayout *col = &layout->column(false);
252 col->active_set(RNA_int_get(ptr, "duplicates") > 0);
253 col->prop(ptr, "distance", UI_ITEM_NONE, std::nullopt, ICON_NONE);
254 col->prop(ptr, "offset", UI_ITEM_R_SLIDER, std::nullopt, ICON_NONE);
255 PanelLayout fade_panel_layout = layout->panel_prop_with_bool_header(
256 C, ptr, "open_fading_panel", ptr, "use_fade", IFACE_("Fade"));
257 if (uiLayout *fade_panel = fade_panel_layout.body) {
258 uiLayout *sub = &fade_panel->column(false);
259 sub->active_set(RNA_boolean_get(ptr, "use_fade"));
260
261 sub->prop(ptr, "fading_center", UI_ITEM_NONE, std::nullopt, ICON_NONE);
262 sub->prop(ptr, "fading_thickness", UI_ITEM_R_SLIDER, std::nullopt, ICON_NONE);
263 sub->prop(ptr, "fading_opacity", UI_ITEM_R_SLIDER, std::nullopt, ICON_NONE);
264 }
265
266 if (uiLayout *influence_panel = layout->panel_prop(
267 C, ptr, "open_influence_panel", IFACE_("Influence")))
268 {
271 }
272
274}
275
280
281static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
282{
283 const auto *mmd = reinterpret_cast<const GreasePencilMultiModifierData *>(md);
284
286 modifier::greasepencil::write_influence_data(writer, &mmd->influence);
287}
288
289static void blend_read(BlendDataReader *reader, ModifierData *md)
290{
291 auto *mmd = reinterpret_cast<GreasePencilMultiModifierData *>(md);
292
293 modifier::greasepencil::read_influence_data(reader, &mmd->influence);
294}
295
296} // namespace blender
297
299 /*idname*/ "GreasePencilMultiply",
300 /*name*/ N_("Multiple Strokes"),
301 /*struct_name*/ "GreasePencilMultiModifierData",
302 /*struct_size*/ sizeof(GreasePencilMultiModifierData),
303 /*srna*/ &RNA_GreasePencilMultiplyModifier,
307 /*icon*/ ICON_MOD_CURVE,
308
309 /*copy_data*/ blender::copy_data,
310
311 /*deform_verts*/ nullptr,
312 /*deform_matrices*/ nullptr,
313 /*deform_verts_EM*/ nullptr,
314 /*deform_matrices_EM*/ nullptr,
315 /*modify_mesh*/ nullptr,
316 /*modify_geometry_set*/ blender::modify_geometry_set,
317
318 /*init_data*/ blender::init_data,
319 /*required_data_mask*/ nullptr,
320 /*free_data*/ blender::free_data,
321 /*is_disabled*/ blender::is_disabled,
322 /*update_depsgraph*/ nullptr,
323 /*depends_on_time*/ nullptr,
324 /*depends_on_normals*/ nullptr,
325 /*foreach_ID_link*/ blender::foreach_ID_link,
326 /*foreach_tex_link*/ nullptr,
327 /*free_runtime_data*/ nullptr,
328 /*panel_register*/ blender::panel_register,
329 /*blend_write*/ blender::blend_write,
330 /*blend_read*/ blender::blend_read,
331};
Low-level operations for curves.
Low-level operations for grease pencil.
void(*)(void *user_data, Object *ob, ID **idpoin, LibraryForeachIDCallbackFlag cb_flag) IDWalkFunc
void BKE_modifier_copydata_generic(const ModifierData *md, ModifierData *md_dst, int flag)
@ eModifierTypeFlag_SupportsMapping
@ eModifierTypeFlag_AcceptsGreasePencil
@ eModifierTypeFlag_EnableInEditmode
@ eModifierTypeFlag_SupportsEditmode
#define BLI_assert(a)
Definition BLI_assert.h:46
#define MEMCMP_STRUCT_AFTER_IS_ZERO(struct_var, member)
#define MEMCPY_STRUCT_AFTER(struct_dst, struct_src, member)
#define BLO_write_struct(writer, struct_name, data_ptr)
#define IFACE_(msgid)
#define DNA_struct_default_get(struct_name)
@ MOD_GREASE_PENCIL_MULTIPLY_ENABLE_FADING
@ eModifierType_GreasePencilMultiply
static bool is_disabled
static void panel_register(ARegionType *region_type)
static void blend_read(BlendDataReader *, ModifierData *md)
static void panel_draw(const bContext *, Panel *panel)
static void blend_write(BlendWriter *writer, const ID *, const ModifierData *md)
Definition MOD_bevel.cc:433
ModifierTypeInfo modifierType_GreasePencilMultiply
static void modify_geometry_set(ModifierData *md, const ModifierEvalContext *ctx, blender::bke::GeometrySet *geometry_set)
PanelType * modifier_panel_register(ARegionType *region_type, ModifierType type, PanelDrawFn draw)
PointerRNA * modifier_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_ptr)
void modifier_error_message_draw(uiLayout *layout, PointerRNA *ptr)
#define C
Definition RandGen.cpp:29
@ UI_ITEM_R_SLIDER
#define UI_ITEM_NONE
Span< T > as_span() const
Definition BLI_array.hh:243
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
MutableSpan< float3 > positions_for_write()
OffsetIndices< int > points_by_curve() const
IndexRange curves_range() const
MutableAttributeAccessor attributes_for_write()
Span< float3 > evaluated_tangents() const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
Span< float3 > curve_plane_normals() const
bke::CurvesGeometry & strokes_for_write()
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
nullptr float
CCL_NAMESPACE_BEGIN struct Options options
static float normals[][3]
uint col
float distance(VecOp< float, D >, VecOp< float, D >) RET
int count
T mix2(float factor, const T &a, const T &b)
CurvesGeometry curves_copy_curve_selection(const CurvesGeometry &curves, const IndexMask &curves_to_copy, const AttributeFilter &attribute_filter)
Curves * curves_new_nomain(int points_num, int curves_num)
RealizeInstancesResult realize_instances(bke::GeometrySet geometry_set, const RealizeInstancesOptions &options)
T length(const VecBase< T, Size > &a)
AxisSigned cross(const AxisSigned a, const AxisSigned b)
VecBase< T, 3 > to_scale(const MatBase< T, NumCol, NumRow > &mat)
void read_influence_data(BlendDataReader *reader, GreasePencilModifierInfluenceData *influence_data)
void init_influence_data(GreasePencilModifierInfluenceData *influence_data, const bool has_custom_curve)
static IndexMask get_filtered_stroke_mask(const Object *ob, const bke::CurvesGeometry &curves, const Material *material_filter, const std::optional< int > material_pass_filter, const bool material_filter_invert, const bool material_pass_filter_invert, IndexMaskMemory &memory)
void write_influence_data(BlendWriter *writer, const GreasePencilModifierInfluenceData *influence_data)
static IndexMask get_filtered_layer_mask(const GreasePencil &grease_pencil, const std::optional< StringRef > tree_node_name_filter, const std::optional< int > layer_pass_filter, const bool layer_filter_invert, const bool layer_pass_filter_invert, IndexMaskMemory &memory)
Vector< bke::greasepencil::Drawing * > get_drawings_for_write(GreasePencil &grease_pencil, const IndexMask &layer_mask, const int frame)
void draw_material_filter_settings(const bContext *, uiLayout *layout, PointerRNA *ptr)
void draw_layer_filter_settings(const bContext *, uiLayout *layout, PointerRNA *ptr)
void free_influence_data(GreasePencilModifierInfluenceData *influence_data)
void foreach_influence_ID_link(GreasePencilModifierInfluenceData *influence_data, Object *ob, IDWalkFunc walk, void *user_data)
void copy_influence_data(const GreasePencilModifierInfluenceData *influence_data_src, GreasePencilModifierInfluenceData *influence_data_dst, const int)
void parallel_for_each(Range &&range, const Function &function)
Definition BLI_task.hh:56
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
static bke::CurvesGeometry duplicate_strokes(const bke::CurvesGeometry &curves, const IndexMask curves_mask, const IndexMask unselected_mask, const int count, int &r_original_point_count, int &r_original_curve_count)
static void blend_write(BlendWriter *writer, const ID *, const ModifierData *md)
static void init_data(ModifierData *md)
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
static void panel_draw(const bContext *C, Panel *panel)
static void modify_geometry_set(ModifierData *md, const ModifierEvalContext *ctx, bke::GeometrySet *geometry_set)
static void free_data(ModifierData *md)
static void panel_register(ARegionType *region_type)
VecBase< float, 3 > float3
static bool is_disabled(const Scene *, ModifierData *md, bool)
static void blend_read(BlendDataReader *reader, ModifierData *md)
static void generate_curves(GreasePencilMultiModifierData &mmd, const ModifierEvalContext &ctx, Drawing &drawing)
#define fabsf
int RNA_int_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
CurvesGeometry geometry
GreasePencilModifierInfluenceData influence
GreasePencilRuntimeHandle * runtime
Definition DNA_ID.h:414
struct uiLayout * layout
static GeometrySet from_instances(Instances *instances, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
static GeometrySet from_curves(Curves *curves, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
GreasePencil * get_grease_pencil_for_write()
PanelLayout panel_prop(const bContext *C, PointerRNA *open_prop_owner, blender::StringRefNull open_prop_name)
PanelLayout panel_prop_with_bool_header(const bContext *C, PointerRNA *open_prop_owner, blender::StringRefNull open_prop_name, PointerRNA *bool_prop_owner, blender::StringRefNull bool_prop_name, std::optional< blender::StringRef > label)
uiLayout & column(bool align)
void active_set(bool active)
void use_property_split_set(bool value)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
i
Definition text_draw.cc:230
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4238
uint8_t flag
Definition wm_window.cc:145