Blender V4.3
grease_pencil_sculpt_randomize.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
5#include "BLI_hash.h"
6#include "BLI_math_vector.hh"
7#include "BLI_rand.hh"
8#include "BLI_task.hh"
9
10#include "BKE_context.hh"
11#include "BKE_crazyspace.hh"
12#include "BKE_curves.hh"
13#include "BKE_grease_pencil.hh"
14#include "BKE_paint.hh"
15
16#include "ED_grease_pencil.hh"
17#include "ED_view3d.hh"
18
19#include "WM_api.hh"
20#include "WM_types.hh"
21
23#include "paint_intern.hh"
24
26
27/* Use a hash to generate random numbers. */
28static float hash_rng(uint32_t seed1, uint32_t seed2, int index)
29{
30 return BLI_hash_int_01(BLI_hash_int_3d(seed1, seed2, uint32_t(index)));
31}
32
34 public:
36
37 /* Get a different seed value for each stroke. */
38 uint32_t unique_seed() const;
39
40 void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
41 void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
42 void on_stroke_done(const bContext & /*C*/) override {}
43};
44
49
50void RandomizeOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
51{
52 this->init_stroke(C, start_sample);
53}
54
55void RandomizeOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
56{
57 const Scene &scene = *CTX_data_scene(&C);
59 const Brush &brush = *BKE_paint_brush(&paint);
60 const int sculpt_mode_flag = brush.gpencil_settings->sculpt_mode_flag;
61
62 const bool is_masking = GPENCIL_ANY_SCULPT_MASK(
63 eGP_Sculpt_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_sculpt));
64
66 C, [&](const GreasePencilStrokeParams &params, const DeltaProjectionFunc &projection_fn) {
67 const uint32_t seed = this->unique_seed();
68
69 IndexMaskMemory selection_memory;
70 const IndexMask selection = point_selection_mask(params, is_masking, selection_memory);
71 if (selection.is_empty()) {
72 return false;
73 }
74
76 Array<float2> view_positions = calculate_view_positions(params, selection);
77 bke::CurvesGeometry &curves = params.drawing.strokes_for_write();
78 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
79
80 bool changed = false;
81 if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_POSITION) {
82 MutableSpan<float3> positions = curves.positions_for_write();
83
84 /* Jitter is applied perpendicular to the mouse movement vector. */
85 const float2 forward = math::normalize(this->mouse_delta(extension_sample));
86 const float2 sideways = float2(-forward.y, forward.x);
87
88 selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
89 const float2 &co = view_positions[point_i];
90 const float influence = brush_point_influence(
91 scene, brush, co, extension_sample, params.multi_frame_falloff);
92 if (influence <= 0.0f) {
93 return;
94 }
95 const float noise = 2.0f * hash_rng(seed, 5678, point_i) - 1.0f;
96 positions[point_i] = projection_fn(deformation.positions[point_i],
97 sideways * influence * noise);
98 });
99
100 params.drawing.tag_positions_changed();
101 changed = true;
102 }
103 if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_STRENGTH) {
104 MutableSpan<float> opacities = params.drawing.opacities_for_write();
105 selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
106 const float2 &co = view_positions[point_i];
107 const float influence = brush_point_influence(
108 scene, brush, co, extension_sample, params.multi_frame_falloff);
109 if (influence <= 0.0f) {
110 return;
111 }
112 const float noise = 2.0f * hash_rng(seed, 1212, point_i) - 1.0f;
113 opacities[point_i] = math::clamp(opacities[point_i] + influence * noise, 0.0f, 1.0f);
114 });
115 changed = true;
116 }
117 if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_THICKNESS) {
118 const MutableSpan<float> radii = params.drawing.radii_for_write();
119 selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
120 const float2 &co = view_positions[point_i];
121 const float influence = brush_point_influence(
122 scene, brush, co, extension_sample, params.multi_frame_falloff);
123 if (influence <= 0.0f) {
124 return;
125 }
126 const float noise = 2.0f * hash_rng(seed, 1212, point_i) - 1.0f;
127 radii[point_i] = math::max(radii[point_i] + influence * noise * 0.001f, 0.0f);
128 });
129 curves.tag_radii_changed();
130 changed = true;
131 }
132 if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_UV) {
134 attributes.lookup_or_add_for_write_span<float>("rotation", bke::AttrDomain::Point);
135 selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
136 const float2 &co = view_positions[point_i];
137 const float influence = brush_point_influence(
138 scene, brush, co, extension_sample, params.multi_frame_falloff);
139 if (influence <= 0.0f) {
140 return;
141 }
142 const float noise = 2.0f * hash_rng(seed, 1212, point_i) - 1.0f;
143 rotations.span[point_i] = math::clamp(
144 rotations.span[point_i] + influence * noise, -float(M_PI_2), float(M_PI_2));
145 });
146 rotations.finish();
147 changed = true;
148 }
149 return changed;
150 });
151 this->stroke_extended(extension_sample);
152}
153
154std::unique_ptr<GreasePencilStrokeOperation> new_randomize_operation(
155 const BrushStrokeMode stroke_mode)
156{
157 return std::make_unique<RandomizeOperation>(stroke_mode);
158}
159
160} // namespace blender::ed::sculpt_paint::greasepencil
Scene * CTX_data_scene(const bContext *C)
Low-level operations for curves.
Low-level operations for grease pencil.
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:477
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:649
BLI_INLINE float BLI_hash_int_01(unsigned int k)
Definition BLI_hash.h:96
BLI_INLINE unsigned int BLI_hash_int_3d(unsigned int kx, unsigned int ky, unsigned int kz)
Definition BLI_hash.h:42
#define M_PI_2
@ GP_SCULPT_FLAGMODE_APPLY_UV
@ GP_SCULPT_FLAGMODE_APPLY_POSITION
@ GP_SCULPT_FLAGMODE_APPLY_THICKNESS
@ GP_SCULPT_FLAGMODE_APPLY_STRENGTH
#define GPENCIL_ANY_SCULPT_MASK(flag)
eGP_Sculpt_SelectMaskFlag
static unsigned long seed
Definition btSoftBody.h:39
static RandomNumberGenerator from_random_seed()
Definition rand.cc:365
void foreach_editable_drawing(const bContext &C, FunctionRef< bool(const GreasePencilStrokeParams &params)> fn) const
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
IndexMask point_selection_mask(const GreasePencilStrokeParams &params, const bool use_masking, IndexMaskMemory &memory)
std::unique_ptr< GreasePencilStrokeOperation > new_randomize_operation(BrushStrokeMode stroke_mode)
Array< float2 > calculate_view_positions(const GreasePencilStrokeParams &params, const IndexMask &selection)
float brush_point_influence(const Scene &scene, const Brush &brush, const float2 &co, const InputSample &sample, float multi_frame_falloff)
bke::crazyspace::GeometryDeformation get_drawing_deformation(const GreasePencilStrokeParams &params)
static float hash_rng(uint32_t seed1, uint32_t seed2, int index)
std::function< float3(const float3 position, const float2 &screen_delta)> DeltaProjectionFunc
T clamp(const T &a, const T &min, const T &max)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
T max(const T &a, const T &b)
VecBase< float, 2 > float2
BrushStrokeMode
unsigned int uint32_t
Definition stdint.h:80
__int64 int64_t
Definition stdint.h:89
struct BrushGpencilSettings * gpencil_settings