Blender V4.3
MOD_grease_pencil_noise.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_math_vector.hh"
11
12#include "BLT_translation.hh"
13
14#include "BLO_read_write.hh"
15
16#include "DNA_defaults.h"
18
19#include "BKE_colortools.hh"
20#include "BKE_curves.hh"
21#include "BKE_geometry_set.hh"
22#include "BKE_grease_pencil.hh"
23
24#include "UI_interface.hh"
25#include "UI_resources.hh"
26
28#include "MOD_ui_common.hh"
29
30#include "RNA_access.hh"
31#include "RNA_prototypes.hh"
32
33namespace blender {
34
44
51
52static void copy_data(const ModifierData *md, ModifierData *target, int flag)
53{
55 reinterpret_cast<const GreasePencilNoiseModifierData *>(md);
56 GreasePencilNoiseModifierData *tgmd = reinterpret_cast<GreasePencilNoiseModifierData *>(target);
57
60}
61
62static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
63{
65 reinterpret_cast<const GreasePencilNoiseModifierData *>(md);
66
69}
70
76
77static bool depends_on_time(Scene * /*scene*/, ModifierData *md)
78{
80 return (mmd->flag & GP_NOISE_USE_RANDOM) != 0;
81}
82
83static Array<float> noise_table(const int len, const int offset, const int seed)
84{
85 Array<float> table(len);
86 for (int i = 0; i < len; i++) {
87 table[i] = BLI_hash_int_01(BLI_hash_int_2d(seed, i + offset + 1));
88 }
89 return table;
90}
91
96 const Object &ob,
97 const int ctime,
98 const int start_frame_number,
100{
102 bke::CurvesGeometry &strokes = drawing.strokes_for_write();
104 if (strokes.points_num() == 0) {
105 return;
106 }
107
108 IndexMaskMemory memory;
110 &ob, strokes, mmd.influence, memory);
111
112 const bool use_curve = (mmd.influence.flag & GREASE_PENCIL_INFLUENCE_USE_CUSTOM_CURVE) != 0;
113 const bool is_keyframe = (mmd.noise_mode == GP_NOISE_RANDOM_KEYFRAME);
114
115 /* Sanitize as it can create out of bound reads. */
116 const float noise_scale = math::clamp(mmd.noise_scale, 0.0f, 1.0f);
117
118 if (filtered_strokes.is_empty()) {
119 return;
120 }
121
122 int seed = mmd.seed;
123 /* Make sure different modifiers get different seeds. */
124 seed += BLI_hash_string(ob.id.name + 2);
126 if (mmd.flag & GP_NOISE_USE_RANDOM) {
127 if (!is_keyframe) {
128 seed += math::floor(ctime / mmd.step);
129 }
130 else {
131 /* If change every keyframe, use the last keyframe. */
132 seed += start_frame_number;
133 }
134 }
135
136 const OffsetIndices<int> points_by_curve = strokes.points_by_curve();
138 strokes, mmd.influence);
139
140 auto get_weight = [&](const IndexRange points, const int point_i) {
141 const float vertex_weight = vgroup_weights[points[point_i]];
142 if (!use_curve) {
143 return vertex_weight;
144 }
145 const float value = float(point_i) / float(points.size() - 1);
146 return vertex_weight * BKE_curvemapping_evaluateF(mmd.influence.custom_curve, 0, value);
147 };
148
149 auto get_noise = [](const Span<float> noise_table, const float value) {
150 return math::interpolate(noise_table[int(math::ceil(value))],
151 noise_table[int(math::floor(value))],
152 math::fract(value));
153 };
154
155 if (mmd.factor > 0.0f) {
156 const Span<float3> curve_plane_normals = drawing.curve_plane_normals();
157 const Span<float3> tangents = strokes.evaluated_tangents();
158 MutableSpan<float3> positions = strokes.positions_for_write();
159
160 filtered_strokes.foreach_index(GrainSize(512), [&](const int stroke_i) {
161 const IndexRange points = points_by_curve[stroke_i];
162 const int noise_len = math::ceil(points.size() * noise_scale) + 2;
163 const Array<float> table = noise_table(
164 noise_len, int(math::floor(mmd.noise_offset)), seed + 2 + stroke_i);
165 for (const int i : points.index_range()) {
166 const int point = points[i];
167 float weight = get_weight(points, i);
168 /* Vector orthogonal to normal. */
169 const float3 bi_normal = math::normalize(
170 math::cross(tangents[point], curve_plane_normals[stroke_i]));
171 const float noise = get_noise(table, i * noise_scale + math::fract(mmd.noise_offset));
172 positions[point] += bi_normal * (noise * 2.0f - 1.0f) * weight * mmd.factor * 0.1f;
173 }
174 });
175 drawing.tag_positions_changed();
176 }
177
178 if (mmd.factor_thickness > 0.0f) {
179 MutableSpan<float> radii = drawing.radii_for_write();
180
181 filtered_strokes.foreach_index(GrainSize(512), [&](const int stroke_i) {
182 const IndexRange points = points_by_curve[stroke_i];
183 const int noise_len = math::ceil(points.size() * noise_scale) + 2;
184 const Array<float> table = noise_table(
185 noise_len, int(math::floor(mmd.noise_offset)), seed + stroke_i);
186 for (const int i : points.index_range()) {
187 const int point = points[i];
188 const float weight = get_weight(points, i);
189 const float noise = get_noise(table, i * noise_scale + math::fract(mmd.noise_offset));
190 radii[point] *= math::max(1.0f + (noise * 2.0f - 1.0f) * weight * mmd.factor_thickness,
191 0.0f);
192 }
193 });
194 }
195
196 if (mmd.factor_strength > 0.0f) {
197 MutableSpan<float> opacities = drawing.opacities_for_write();
198
199 filtered_strokes.foreach_index(GrainSize(512), [&](const int stroke_i) {
200 const IndexRange points = points_by_curve[stroke_i];
201 const int noise_len = math::ceil(points.size() * noise_scale) + 2;
202 const Array<float> table = noise_table(
203 noise_len, int(math::floor(mmd.noise_offset)), seed + 3 + stroke_i);
204 for (const int i : points.index_range()) {
205 const int point = points[i];
206 const float weight = get_weight(points, i);
207 const float noise = get_noise(table, i * noise_scale + math::fract(mmd.noise_offset));
208 opacities[point] *= math::max(1.0f - noise * weight * mmd.factor_strength, 0.0f);
209 }
210 });
211 }
212
213 if (mmd.factor_uvs > 0.0f) {
214 bke::SpanAttributeWriter<float> rotations = attributes.lookup_or_add_for_write_span<float>(
215 "rotation", bke::AttrDomain::Point);
216
217 filtered_strokes.foreach_index(GrainSize(512), [&](const int stroke_i) {
218 const IndexRange points = points_by_curve[stroke_i];
219 const int noise_len = math::ceil(points.size() * noise_scale) + 2;
220 const Array<float> table = noise_table(
221 noise_len, int(math::floor(mmd.noise_offset)), seed + 4 + stroke_i);
222 for (const int i : points.index_range()) {
223 const int point = points[i];
224 const float weight = get_weight(points, i);
225 const float noise = get_noise(table, i * noise_scale + math::fract(mmd.noise_offset));
226 const float delta_rot = (noise * 2.0f - 1.0f) * weight * mmd.factor_uvs * M_PI_2;
227 rotations.span[point] = math::clamp(
228 rotations.span[point] + delta_rot, float(-M_PI_2), float(M_PI_2));
229 }
230 });
231 rotations.finish();
232 }
233}
234
236 const ModifierEvalContext *ctx,
237 bke::GeometrySet *geometry_set)
238{
239 const auto *mmd = reinterpret_cast<GreasePencilNoiseModifierData *>(md);
240
241 if (!geometry_set->has_grease_pencil()) {
242 return;
243 }
244
245 if (!mmd->factor && !mmd->factor_strength && !mmd->factor_thickness && !mmd->factor_uvs) {
246 return;
247 }
248
249 GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write();
250 const int current_frame = grease_pencil.runtime->eval_frame;
251
252 IndexMaskMemory mask_memory;
254 grease_pencil, mmd->influence, mask_memory);
256 modifier::greasepencil::get_drawing_infos_by_frame(grease_pencil, layer_mask, current_frame);
257
259 drawing_infos, [&](const modifier::greasepencil::FrameDrawingInfo &info) {
260 deform_drawing(*mmd, *ctx->object, current_frame, info.start_frame_number, *info.drawing);
261 });
262}
263
264static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
265{
267
269}
270
271static void panel_draw(const bContext *C, Panel *panel)
272{
273 uiLayout *col;
274 uiLayout *layout = panel->layout;
275
277
278 uiLayoutSetPropSep(layout, true);
279
280 col = uiLayoutColumn(layout, false);
281 uiItemR(col, ptr, "factor", UI_ITEM_NONE, IFACE_("Position"), ICON_NONE);
282 uiItemR(col, ptr, "factor_strength", UI_ITEM_NONE, IFACE_("Strength"), ICON_NONE);
283 uiItemR(col, ptr, "factor_thickness", UI_ITEM_NONE, IFACE_("Thickness"), ICON_NONE);
284 uiItemR(col, ptr, "factor_uvs", UI_ITEM_NONE, IFACE_("UV"), ICON_NONE);
285 uiItemR(col, ptr, "noise_scale", UI_ITEM_NONE, nullptr, ICON_NONE);
286 uiItemR(col, ptr, "noise_offset", UI_ITEM_NONE, nullptr, ICON_NONE);
287 uiItemR(col, ptr, "seed", UI_ITEM_NONE, nullptr, ICON_NONE);
288
289 if (uiLayout *random_layout = uiLayoutPanelProp(
290 C, layout, ptr, "open_random_panel", IFACE_("Random")))
291 {
292 uiItemR(random_layout, ptr, "use_random", UI_ITEM_NONE, IFACE_("Randomize"), ICON_NONE);
293
294 uiLayout *random_col = uiLayoutColumn(random_layout, false);
295
296 uiLayoutSetPropSep(random_col, true);
297 uiLayoutSetActive(random_col, RNA_boolean_get(ptr, "use_random"));
298
299 uiItemR(random_col, ptr, "random_mode", UI_ITEM_NONE, nullptr, ICON_NONE);
300 const int mode = RNA_enum_get(ptr, "random_mode");
301 if (mode != GP_NOISE_RANDOM_KEYFRAME) {
302 uiItemR(random_col, ptr, "step", UI_ITEM_NONE, nullptr, ICON_NONE);
303 }
304 }
305
306 if (uiLayout *influence_panel = uiLayoutPanelProp(
307 C, layout, ptr, "open_influence_panel", IFACE_("Influence")))
308 {
313 }
314
315 modifier_panel_end(layout, ptr);
316}
317
322
323} // namespace blender
324
326 /*idname*/ "GreasePencilNoiseModifier",
327 /*name*/ N_("Noise"),
328 /*struct_name*/ "GreasePencilNoiseModifierData",
329 /*struct_size*/ sizeof(GreasePencilNoiseModifierData),
330 /*srna*/ &RNA_GreasePencilNoiseModifier,
332 /*flags*/
335 /*icon*/ ICON_GREASEPENCIL,
336
337 /*copy_data*/ blender::copy_data,
338
339 /*deform_verts*/ nullptr,
340 /*deform_matrices*/ nullptr,
341 /*deform_verts_EM*/ nullptr,
342 /*deform_matrices_EM*/ nullptr,
343 /*modify_mesh*/ nullptr,
344 /*modify_geometry_set*/ blender::modify_geometry_set,
345
346 /*init_data*/ blender::init_data,
347 /*required_data_mask*/ nullptr,
348 /*free_data*/ blender::free_data,
349 /*is_disabled*/ nullptr,
350 /*update_depsgraph*/ nullptr,
351 /*depends_on_time*/ blender::depends_on_time,
352 /*depends_on_normals*/ nullptr,
353 /*foreach_ID_link*/ blender::foreach_ID_link,
354 /*foreach_tex_link*/ nullptr,
355 /*free_runtime_data*/ nullptr,
356 /*panel_register*/ blender::panel_register,
357 /*blend_write*/ blender::blend_write,
358 /*blend_read*/ blender::blend_read,
359};
float BKE_curvemapping_evaluateF(const CurveMapping *cumap, int cur, float value)
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
#define M_PI_2
#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)
@ GREASE_PENCIL_INFLUENCE_USE_CUSTOM_CURVE
@ eModifierType_GreasePencilNoise
struct GreasePencilNoiseModifierData GreasePencilNoiseModifierData
ModifierTypeInfo modifierType_GreasePencilNoise
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)
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
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)
static unsigned long seed
Definition btSoftBody.h:39
MutableSpan< float3 > positions_for_write()
OffsetIndices< int > points_by_curve() const
MutableAttributeAccessor attributes_for_write()
Span< float3 > evaluated_tangents() const
Span< float3 > curve_plane_normals() const
MutableSpan< float > opacities_for_write()
MutableSpan< float > radii_for_write()
bke::CurvesGeometry & strokes_for_write()
void foreach_index(Fn &&fn) const
int len
draw_view in_light_buf[] float
uint col
T clamp(const T &a, const T &min, const T &max)
T floor(const T &a)
AxisSigned cross(const AxisSigned a, const AxisSigned b)
T interpolate(const T &a, const T &b, const FactorT &t)
T fract(const T &a)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
T ceil(const T &a)
T max(const T &a, const T &b)
void read_influence_data(BlendDataReader *reader, GreasePencilModifierInfluenceData *influence_data)
void init_influence_data(GreasePencilModifierInfluenceData *influence_data, const bool has_custom_curve)
Vector< FrameDrawingInfo > get_drawing_infos_by_frame(GreasePencil &grease_pencil, const IndexMask &layer_mask, const int frame)
static IndexMask get_filtered_layer_mask(const GreasePencil &grease_pencil, const std::optional< StringRef > layer_name_filter, const std::optional< int > layer_pass_filter, const bool layer_filter_invert, const bool layer_pass_filter_invert, IndexMaskMemory &memory)
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 draw_vertex_group_settings(const bContext *, uiLayout *layout, PointerRNA *ptr)
VArray< float > get_influence_vertex_weights(const bke::CurvesGeometry &curves, const GreasePencilModifierInfluenceData &influence_data)
void draw_material_filter_settings(const bContext *, uiLayout *layout, PointerRNA *ptr)
void draw_layer_filter_settings(const bContext *, uiLayout *layout, PointerRNA *ptr)
void draw_custom_curve_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 ensure_no_bezier_curves(Drawing &drawing)
void parallel_for_each(Range &&range, const Function &function)
Definition BLI_task.hh:58
static bool depends_on_time(Scene *, ModifierData *md)
static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
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 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 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
uint8_t flag
Definition wm_window.cc:138