Blender V4.3
grease_pencil_vertex_smear.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 <optional>
6
7#include "BKE_context.hh"
8#include "BKE_curves.hh"
10#include "BKE_paint.hh"
11
12#include "BLI_task.hh"
13
15
17
18struct ColorGrid {
19 /* Flat array of colors. The length of this is size^2. */
21 /* Size of the grid. Used as the width and height. Should be divisible by 2. */
22 int size;
23 /* The size of each cell in pixels (screen space). Used as the cell width and height. */
25 /* The center position of the grid (screen space). */
27
28 /* Compute the screen space position based on a grid position and a center. */
30 {
31 const float2 centered = float2(pos - this->size / 2) + float2(0.5f);
32 return (centered * this->cell_size_px) + center;
33 }
34
35 /* Compute a grid position based on a screen space position and a center. */
36 int2 coords_to_pos(const float2 coord, const float2 center) const
37 {
38 const int2 pos = int2(math::floor((coord - center) / float(this->cell_size_px)));
39 return pos + ((this->size + 1) / 2);
40 }
41
42 /* Compute a grid index (into the colors array) based on a grid position. Returns -1 if the
43 * position is out of bounds. */
44 int pos_to_index(const int2 pos) const
45 {
46 if (pos.x >= 0 && pos.x < this->size && pos.y >= 0 && pos.y < this->size) {
47 return pos.y * this->size + pos.x;
48 }
49 return -1;
50 }
51};
52
55
56 public:
57 void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
58 void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
59 void on_stroke_done(const bContext &C) override;
60
61 private:
62 ColorGrid color_grid_;
63 void init_color_grid(const bContext &C, float2 start_position);
64};
65
66void VertexSmearOperation::init_color_grid(const bContext &C, const float2 start_position)
67{
68 const Scene &scene = *CTX_data_scene(&C);
70 const Brush &brush = *BKE_paint_brush(&paint);
71 const bool is_masking = GPENCIL_ANY_VERTEX_MASK(
72 eGP_vertex_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_vertex));
73 const float radius = brush_radius(scene, brush, 1.0f);
74
75 /* Setup grid values. */
76 /* TODO: Make this a setting. */
77 color_grid_.cell_size_px = 10.0f;
78 color_grid_.center = start_position;
79 color_grid_.size = int(math::ceil((radius * 2.0f) / color_grid_.cell_size_px));
80
81 /* Initialize the color array. */
82 const int grid_array_length = color_grid_.size * color_grid_.size;
83 color_grid_.colors.reinitialize(grid_array_length);
84 color_grid_.colors.fill(float4(0.0f));
85
86 /* Initialize grid values. */
88 IndexMaskMemory memory;
89 const IndexMask point_selection = point_selection_mask(params, is_masking, memory);
90 if (point_selection.is_empty()) {
91 return false;
92 }
93 const Array<float2> view_positions = calculate_view_positions(params, point_selection);
94 const Array<float> radii = calculate_view_radii(params, point_selection);
95 const VArray<ColorGeometry4f> vertex_colors = params.drawing.vertex_colors();
96 /* Compute the colors in the grid by averaging the vertex colors of the points that
97 * intersect each cell. */
98 Array<int> points_per_cell(grid_array_length, 0);
99 point_selection.foreach_index([&](const int point_i) {
100 const float2 view_pos = view_positions[point_i];
101 const float view_radius = radii[point_i];
102 const ColorGeometry4f color = vertex_colors[point_i];
103
104 const int bounds_size = math::floor(view_radius / color_grid_.cell_size_px) * 2 + 1;
105 const int2 bounds_center = color_grid_.coords_to_pos(view_pos, color_grid_.center);
106 const int2 bounds_min = bounds_center - (bounds_size / 2);
107 const int2 bounds_max = bounds_center + (bounds_size / 2);
108 if (!(bounds_min.x < color_grid_.size && bounds_max.x >= 0 &&
109 bounds_min.y < color_grid_.size && bounds_max.y >= 0))
110 {
111 /* Point is out of bounds. */
112 return;
113 }
114 for (int y = bounds_min.y; y <= bounds_max.y; y++) {
115 for (int x = bounds_min.x; x <= bounds_max.x; x++) {
116 const int2 grid_pos = int2(x, y);
117 const int cell_i = color_grid_.pos_to_index(grid_pos);
118 if (cell_i == -1) {
119 continue;
120 }
121 const float2 cell_pos = color_grid_.pos_to_coords(grid_pos, color_grid_.center);
122 if (math::distance_squared(cell_pos, view_pos) <= view_radius * view_radius) {
123 color_grid_.colors[cell_i] += float4(color.r, color.g, color.b, 1.0f);
124 points_per_cell[cell_i]++;
125 }
126 }
127 }
128 });
129 /* Divide by the total to get the average color per cell. */
130 for (const int cell_i : color_grid_.colors.index_range()) {
131 if (points_per_cell[cell_i] > 0) {
132 color_grid_.colors[cell_i] *= 1.0f / float(points_per_cell[cell_i]);
133 }
134 }
135 /* Don't trigger updates for the grid initialization. */
136 return false;
137 });
138}
139
141{
142 this->init_stroke(C, start_sample);
143 this->init_color_grid(C, start_sample.mouse_position);
144}
145
147 const InputSample &extension_sample)
148{
149 const Scene &scene = *CTX_data_scene(&C);
151 const Brush &brush = *BKE_paint_brush(&paint);
152 const float radius = brush_radius(scene, brush, extension_sample.pressure);
153
154 const bool is_masking = GPENCIL_ANY_VERTEX_MASK(
155 eGP_vertex_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_vertex));
156
158 IndexMaskMemory memory;
159 const IndexMask point_selection = point_selection_mask(params, is_masking, memory);
160 if (point_selection.is_empty()) {
161 return false;
162 }
163 const Array<float2> view_positions = calculate_view_positions(params, point_selection);
164 MutableSpan<ColorGeometry4f> vertex_colors = params.drawing.vertex_colors_for_write();
165 point_selection.foreach_index(GrainSize(1024), [&](const int64_t point_i) {
166 const float2 view_pos = view_positions[point_i];
167 const int2 grid_pos = color_grid_.coords_to_pos(view_pos, extension_sample.mouse_position);
168 const int cell_i = color_grid_.pos_to_index(grid_pos);
169 if (cell_i == -1 || color_grid_.colors[cell_i][3] == 0.0f) {
170 return;
171 }
172 const ColorGeometry4f mix_color = ColorGeometry4f(color_grid_.colors[cell_i]);
173
174 const float distance_falloff = math::clamp(
175 1.0f - (math::distance(color_grid_.center, view_pos) / radius * 2), 0.0f, 1.0f);
176 const float influence = brush_point_influence(scene,
177 brush,
178 view_pos,
179 extension_sample,
180 params.multi_frame_falloff) *
181 distance_falloff;
182 if (influence > 0.0f) {
183 ColorGeometry4f &color = vertex_colors[point_i];
184 const float alpha = color.a;
185 color = math::interpolate(color, mix_color, influence);
186 color.a = alpha;
187 }
188 });
189 return true;
190 });
191}
192
194
195std::unique_ptr<GreasePencilStrokeOperation> new_vertex_smear_operation()
196{
197 return std::make_unique<VertexSmearOperation>();
198}
199
200} // 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
#define GPENCIL_ANY_VERTEX_MASK(flag)
eGP_vertex_SelectMaskFlag
void foreach_editable_drawing(const bContext &C, FunctionRef< bool(const GreasePencilStrokeParams &params)> fn) const
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override
void foreach_index(Fn &&fn) const
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
Array< float > calculate_view_radii(const GreasePencilStrokeParams &params, const IndexMask &selection)
IndexMask point_selection_mask(const GreasePencilStrokeParams &params, const bool use_masking, IndexMaskMemory &memory)
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)
std::unique_ptr< GreasePencilStrokeOperation > new_vertex_smear_operation()
float brush_radius(const Scene &scene, const Brush &brush, float pressure)
T clamp(const T &a, const T &min, const T &max)
T floor(const T &a)
T distance(const T &a, const T &b)
T interpolate(const T &a, const T &b, const FactorT &t)
T ceil(const T &a)
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:337
__int64 int64_t
Definition stdint.h:89
float2 pos_to_coords(const int2 pos, const float2 center) const
int2 coords_to_pos(const float2 coord, const float2 center) const