Blender V4.3
MOD_grease_pencil_length.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2005 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BLI_hash.h"
10#include "BLI_rand.h"
11#include "BLI_task.h"
12
13#include "BLT_translation.hh"
14
15#include "BLO_read_write.hh"
16
17#include "DNA_defaults.h"
19#include "DNA_node_types.h" /* For `GeometryNodeCurveSampleMode` */
20#include "DNA_object_types.h"
21
22#include "BKE_curves.hh"
23#include "BKE_geometry_set.hh"
24#include "BKE_grease_pencil.hh"
25#include "BKE_lib_query.hh"
26#include "BKE_modifier.hh"
27
28#include "UI_interface.hh"
29#include "UI_resources.hh"
30
32#include "MOD_modifiertypes.hh"
33#include "MOD_ui_common.hh"
34
35#include "RNA_access.hh"
36#include "RNA_prototypes.hh"
37
38#include "GEO_extend_curves.hh"
39#include "GEO_trim_curves.hh"
40
41namespace blender {
42
52
53static void copy_data(const ModifierData *md, ModifierData *target, int flags)
54{
55 const auto *omd = reinterpret_cast<const GreasePencilLengthModifierData *>(md);
56 auto *tomd = reinterpret_cast<GreasePencilLengthModifierData *>(target);
57
59
60 BKE_modifier_copydata_generic(md, target, flags);
61 modifier::greasepencil::copy_influence_data(&omd->influence, &tomd->influence, flags);
62}
63
64static void free_data(ModifierData *md)
65{
66 auto *omd = reinterpret_cast<GreasePencilLengthModifierData *>(md);
68}
69
70static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
71{
72 auto *omd = reinterpret_cast<GreasePencilLengthModifierData *>(md);
73 modifier::greasepencil::foreach_influence_ID_link(&omd->influence, ob, walk, user_data);
74}
75
76static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
77{
79 reinterpret_cast<const GreasePencilLengthModifierData *>(md);
80
83}
84
91
92static Array<float> noise_table(int len, int offset, int seed)
93{
94 Array<float> table(len);
95 for (const int i : table.index_range()) {
96 table[i] = BLI_hash_int_01(BLI_hash_int_2d(seed, i + offset + 1));
97 }
98 return table;
99}
100
101static float table_sample(MutableSpan<float> table, float x)
102{
103 return math::interpolate(table[int(math::ceil(x))], table[int(math::floor(x))], math::fract(x));
104}
105
106static void deform_drawing(const ModifierData &md,
107 const Object &ob,
109 const int current_time)
110{
112 reinterpret_cast<const GreasePencilLengthModifierData &>(md);
114 bke::CurvesGeometry &curves = drawing.strokes_for_write();
115
116 if (curves.points_num() == 0) {
117 return;
118 }
119
120 IndexMaskMemory memory;
122 &ob, curves, mmd.influence, memory);
123
124 const int curves_num = curves.curves_num();
125
126 /* Variable for tagging shrinking when values are adjusted after random. */
127 std::atomic<bool> needs_additional_shrinking = false;
128
129 VArray<float> use_starts = VArray<float>::ForSingle(mmd.start_fac, curves_num);
130 VArray<float> use_ends = VArray<float>::ForSingle(mmd.end_fac, curves_num);
131
132 Array<float> modified_starts;
133 Array<float> modified_ends;
134 if (mmd.rand_start_fac != 0.0 || mmd.rand_end_fac != 0.0) {
135 modified_starts = Array<float>(curves.curves_num(), mmd.start_fac);
136 modified_ends = Array<float>(curves.curves_num(), mmd.end_fac);
137
138 /* Use random to modify start/end factors. Put the modified values outside the
139 * branch so it could be accessed in later stretching/shrinking stages. */
140 use_starts = VArray<float>::ForSpan(modified_starts.as_mutable_span());
141 use_ends = VArray<float>::ForSpan(modified_ends.as_mutable_span());
142
143 int seed = mmd.seed;
144
145 /* Make sure different modifiers get different seeds. */
146 seed += BLI_hash_string(ob.id.name + 2);
148
149 if (mmd.flag & GP_LENGTH_USE_RANDOM) {
150 seed += current_time / mmd.step;
151 }
152
153 float rand_offset = BLI_hash_int_01(seed);
154
155 Array<float> noise_table_length = noise_table(
156 4 + curves_num, int(math::floor(mmd.rand_offset)), seed + 2);
157
158 threading::parallel_for(IndexRange(curves_num), 512, [&](const IndexRange parallel_range) {
159 for (const int i : parallel_range) {
160 /* To ensure a nice distribution, we use halton sequence and offset using the seed. */
161 double r[2];
162 const uint primes[2] = {2, 3};
163 double offset[2] = {0.0f, 0.0f};
164 BLI_halton_2d(primes, offset, i, r);
165
166 float rand[2] = {0.0f, 0.0f};
167 for (int j = 0; j < 2; j++) {
168 float noise = table_sample(noise_table_length, i + j * 2 + math::fract(mmd.rand_offset));
169
170 rand[j] = math::mod(float(r[j] + rand_offset), 1.0f);
171 rand[j] = math::abs(
172 math::mod(sin(rand[j] * 12.9898f + j * 78.233f) * 43758.5453f, 1.0f) + noise);
173 }
174
175 modified_starts[i] = modified_starts[i] + rand[0] * mmd.rand_start_fac;
176 modified_ends[i] = modified_ends[i] + rand[1] * mmd.rand_end_fac;
177
178 if (modified_starts[i] <= 0.0f || modified_ends[i] <= 0.0f) {
179 needs_additional_shrinking.store(true, std::memory_order_relaxed);
180 }
181 }
182 });
183 }
184
185 curves = geometry::extend_curves(curves,
186 selection,
187 use_starts,
188 use_ends,
189 mmd.overshoot_fac,
190 (mmd.flag & GP_LENGTH_USE_CURVATURE) != 0,
191 mmd.point_density,
193 mmd.max_angle,
194 (mmd.flag & GP_LENGTH_INVERT_CURVATURE) != 0,
195 ((mmd.mode & GP_LENGTH_ABSOLUTE) != 0) ?
198 {});
199
200 /* Always do the stretching first since it might depend on points which could be deleted by the
201 * shrink. */
202 if (mmd.start_fac < 0.0f || mmd.end_fac < 0.0f || needs_additional_shrinking) {
203 /* #trim_curves() accepts the `end` values if it's sampling from the beginning of the
204 * curve, so we need to get the lengths of the curves and subtract it from the back when the
205 * modifier is in Absolute mode. For convenience, we always call #trim_curves() in LENGTH
206 * mode since the function itself will need length to be sampled anyway. */
207 Array<float> starts(curves.curves_num());
208 Array<float> ends(curves.curves_num());
209 Array<bool> needs_removal(curves.curves_num());
210 needs_removal.fill(false);
211
212 curves.ensure_evaluated_lengths();
213
214 threading::parallel_for(curves.curves_range(), 512, [&](const IndexRange parallel_range) {
215 for (const int curve : parallel_range) {
216 float length = curves.evaluated_length_total_for_curve(curve, false);
217 if (mmd.mode & GP_LENGTH_ABSOLUTE) {
218 starts[curve] = -math::min(use_starts[curve], 0.0f);
219 ends[curve] = length - -math::min(use_ends[curve], 0.0f);
220 }
221 else {
222 starts[curve] = -math::min(use_starts[curve], 0.0f) * length;
223 ends[curve] = (1 + math::min(use_ends[curve], 0.0f)) * length;
224 }
225 if (starts[curve] > ends[curve]) {
226 needs_removal[curve] = true;
227 }
228 }
229 });
230 curves = geometry::trim_curves(curves,
231 selection,
232 VArray<float>::ForSpan(starts.as_span()),
233 VArray<float>::ForSpan(ends.as_span()),
235 {});
236
237 /* #trim_curves() will leave the last segment there when trimmed length is greater than
238 * curve original length, thus we need to remove those curves afterwards. */
239 IndexMaskMemory memory_remove;
240 const IndexMask to_remove = IndexMask::from_bools(needs_removal.as_span(), memory_remove);
241 if (!to_remove.is_empty()) {
242 curves.remove_curves(to_remove, {});
243 }
244 }
245
246 drawing.tag_topology_changed();
247}
248
250 const ModifierEvalContext *ctx,
251 blender::bke::GeometrySet *geometry_set)
252{
254
255 if (!geometry_set->has_grease_pencil()) {
256 return;
257 }
258
259 GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write();
260
261 IndexMaskMemory mask_memory;
262 const IndexMask layer_mask = modifier::greasepencil::get_filtered_layer_mask(
263 grease_pencil, mmd->influence, mask_memory);
265 modifier::greasepencil::get_drawings_for_write(
266 grease_pencil, layer_mask, grease_pencil.runtime->eval_frame);
267
268 threading::parallel_for_each(drawings, [&](bke::greasepencil::Drawing *drawing) {
269 deform_drawing(*md, *ctx->object, *drawing, grease_pencil.runtime->eval_frame);
270 });
271}
272
273static void panel_draw(const bContext *C, Panel *panel)
274{
275 uiLayout *layout = panel->layout;
276
278
279 uiLayoutSetPropSep(layout, true);
280 uiItemR(layout, ptr, "mode", UI_ITEM_NONE, nullptr, ICON_NONE);
281
282 uiLayout *col = uiLayoutColumn(layout, true);
283
284 if (RNA_enum_get(ptr, "mode") == GP_LENGTH_RELATIVE) {
285 uiItemR(col, ptr, "start_factor", UI_ITEM_NONE, IFACE_("Start"), ICON_NONE);
286 uiItemR(col, ptr, "end_factor", UI_ITEM_NONE, IFACE_("End"), ICON_NONE);
287 }
288 else {
289 uiItemR(col, ptr, "start_length", UI_ITEM_NONE, IFACE_("Start"), ICON_NONE);
290 uiItemR(col, ptr, "end_length", UI_ITEM_NONE, IFACE_("End"), ICON_NONE);
291 }
292
293 uiItemR(layout, ptr, "overshoot_factor", UI_ITEM_R_SLIDER, IFACE_("Used Length"), ICON_NONE);
294
295 if (uiLayout *random_layout = uiLayoutPanelProp(
296 C, layout, ptr, "open_random_panel", IFACE_("Randomize")))
297 {
298 uiItemR(random_layout, ptr, "use_random", UI_ITEM_NONE, IFACE_("Randomize"), ICON_NONE);
299
300 uiLayout *subcol = uiLayoutColumn(random_layout, false);
301 uiLayoutSetPropSep(subcol, true);
302 uiLayoutSetActive(subcol, RNA_boolean_get(ptr, "use_random"));
303
304 uiItemR(subcol, ptr, "step", UI_ITEM_NONE, nullptr, ICON_NONE);
305
306 uiItemR(subcol, ptr, "random_start_factor", UI_ITEM_NONE, IFACE_("Offset Start"), ICON_NONE);
307 uiItemR(subcol, ptr, "random_end_factor", UI_ITEM_NONE, IFACE_("End"), ICON_NONE);
308 uiItemR(subcol, ptr, "random_offset", UI_ITEM_NONE, IFACE_("Noise Offset"), ICON_NONE);
309 uiItemR(subcol, ptr, "seed", UI_ITEM_NONE, nullptr, ICON_NONE);
310 }
311
312 if (uiLayout *curvature_layout = uiLayoutPanelProp(
313 C, layout, ptr, "open_curvature_panel", IFACE_("Curvature")))
314 {
315 uiItemR(curvature_layout, ptr, "use_curvature", UI_ITEM_NONE, IFACE_("Curvature"), ICON_NONE);
316
317 uiLayout *subcol = uiLayoutColumn(curvature_layout, false);
318 uiLayoutSetPropSep(subcol, true);
319 uiLayoutSetActive(subcol, RNA_boolean_get(ptr, "use_curvature"));
320
321 uiItemR(subcol, ptr, "point_density", UI_ITEM_NONE, nullptr, ICON_NONE);
322 uiItemR(subcol, ptr, "segment_influence", UI_ITEM_NONE, nullptr, ICON_NONE);
323 uiItemR(subcol, ptr, "max_angle", UI_ITEM_NONE, nullptr, ICON_NONE);
324 uiItemR(subcol, ptr, "invert_curvature", UI_ITEM_NONE, IFACE_("Invert"), ICON_NONE);
325 }
326
327 if (uiLayout *influence_panel = uiLayoutPanelProp(
328 C, layout, ptr, "open_influence_panel", IFACE_("Influence")))
329 {
330 modifier::greasepencil::draw_layer_filter_settings(C, influence_panel, ptr);
331 modifier::greasepencil::draw_material_filter_settings(C, influence_panel, ptr);
332 }
333
334 modifier_panel_end(layout, ptr);
335}
336
341
342} // namespace blender
343
345 /*idname*/ "GreasePencilLengthModifier",
346 /*name*/ N_("Length"),
347 /*struct_name*/ "GreasePencilLengthModifierData",
348 /*struct_size*/ sizeof(GreasePencilLengthModifierData),
349 /*srna*/ &RNA_GreasePencilLengthModifier,
351 /*flags*/
354 /*icon*/ ICON_MOD_LENGTH,
355
356 /*copy_data*/ blender::copy_data,
357
358 /*deform_verts*/ nullptr,
359 /*deform_matrices*/ nullptr,
360 /*deform_verts_EM*/ nullptr,
361 /*deform_matrices_EM*/ nullptr,
362 /*modify_mesh*/ nullptr,
363 /*modify_geometry_set*/ blender::modify_geometry_set,
364
365 /*init_data*/ blender::init_data,
366 /*required_data_mask*/ nullptr,
367 /*free_data*/ blender::free_data,
368 /*is_disabled*/ nullptr,
369 /*update_depsgraph*/ nullptr,
370 /*depends_on_time*/ nullptr,
371 /*depends_on_normals*/ nullptr,
372 /*foreach_ID_link*/ blender::foreach_ID_link,
373 /*foreach_tex_link*/ nullptr,
374 /*free_runtime_data*/ nullptr,
375 /*panel_register*/ blender::panel_register,
376 /*blend_write*/ blender::blend_write,
377 /*blend_read*/ blender::blend_read,
378};
Low-level operations for curves.
Low-level operations for grease pencil.
void BKE_modifier_copydata_generic(const ModifierData *md, ModifierData *md_dst, int flag)
@ eModifierTypeFlag_AcceptsGreasePencil
@ eModifierTypeFlag_EnableInEditmode
@ eModifierTypeFlag_SupportsEditmode
void(*)(void *user_data, Object *ob, ID **idpoin, int cb_flag) IDWalkFunc
#define BLI_assert(a)
Definition BLI_assert.h:50
BLI_INLINE float BLI_hash_int_01(unsigned int k)
Definition BLI_hash.h:96
BLI_INLINE unsigned int BLI_hash_string(const char *str)
Definition BLI_hash.h:71
BLI_INLINE unsigned int BLI_hash_int_2d(unsigned int kx, unsigned int ky)
Definition BLI_hash.h:55
Random number functions.
void BLI_halton_2d(const unsigned int prime[2], double offset[2], int n, double *r)
Definition rand.cc:295
unsigned int uint
#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)
@ GP_LENGTH_INVERT_CURVATURE
@ eModifierType_GreasePencilLength
struct GreasePencilLengthModifierData GreasePencilLengthModifierData
@ GEO_NODE_CURVE_SAMPLE_FACTOR
@ GEO_NODE_CURVE_SAMPLE_LENGTH
Object is a sort of wrapper for general info.
static void panel_register(ARegionType *region_type)
static void panel_draw(const bContext *, Panel *panel)
ModifierTypeInfo modifierType_GreasePencilLength
static void modify_geometry_set(ModifierData *md, const ModifierEvalContext *ctx, blender::bke::GeometrySet *geometry_set)
void modifier_panel_end(uiLayout *layout, PointerRNA *ptr)
PanelType * modifier_panel_register(ARegionType *region_type, ModifierType type, PanelDrawFn draw)
PointerRNA * modifier_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_ptr)
void uiLayoutSetActive(uiLayout *layout, bool active)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
PanelLayout uiLayoutPanelProp(const bContext *C, uiLayout *layout, PointerRNA *open_prop_owner, const char *open_prop_name)
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_ITEM_R_SLIDER
static unsigned long seed
Definition btSoftBody.h:39
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
IndexRange index_range() const
Definition BLI_array.hh:349
void fill(const T &value) const
Definition BLI_array.hh:261
static VArray ForSingle(T value, const int64_t size)
static VArray ForSpan(Span< T > values)
bke::CurvesGeometry & strokes_for_write()
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
int len
uint col
bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves, const IndexMask &selection, const VArray< float > &starts, const VArray< float > &ends, GeometryNodeCurveSampleMode mode, const bke::AttributeFilter &attribute_filter)
bke::CurvesGeometry extend_curves(bke::CurvesGeometry &src_curves, const IndexMask &selection, const VArray< float > &start_lengths, const VArray< float > &end_lengths, float overshoot_fac, bool follow_curvature, float point_density, float segment_influence, float max_angle, bool invert_curvature, GeometryNodeCurveSampleMode sample_mode, const bke::AttributeFilter &attribute_filter)
T floor(const T &a)
T interpolate(const T &a, const T &b, const FactorT &t)
T fract(const T &a)
T ceil(const T &a)
T mod(const T &a, const T &b)
T abs(const T &a)
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)
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 ensure_no_bezier_curves(Drawing &drawing)
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:95
static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
static float table_sample(MutableSpan< float > table, float x)
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 modify_geometry_set(ModifierData *md, const ModifierEvalContext *ctx, bke::GeometrySet *geometry_set)
static void deform_drawing(const ModifierData &md, const Object &ob, bke::greasepencil::Drawing &drawing)
static void free_data(ModifierData *md)
static void panel_register(ARegionType *region_type)
static void deform_drawing(const ModifierData &md, const Object &ob, bke::greasepencil::Drawing &drawing, const int current_time)
static void blend_read(BlendDataReader *reader, ModifierData *md)
static Array< float > noise_table(int len, int offset, int seed)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
GreasePencilModifierInfluenceData influence
GreasePencilRuntimeHandle * runtime
Definition DNA_ID.h:413
char name[66]
Definition DNA_ID.h:425
struct uiLayout * layout
GreasePencil * get_grease_pencil_for_write()
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4126