Blender V4.5
grease_pencil_weight_paint.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#pragma once
6
7#include "BKE_brush.hh"
8#include "BKE_colortools.hh"
9#include "BKE_context.hh"
10#include "BKE_crazyspace.hh"
11#include "BKE_curves.hh"
12#include "BKE_deform.hh"
14#include "BKE_modifier.hh"
15#include "BKE_object_deform.h"
16#include "BKE_paint.hh"
17#include "BKE_scene.hh"
18
20
21#include "BLI_kdtree.h"
22#include "BLI_listbase.h"
23#include "BLI_rect.h"
24
25#include "DNA_brush_types.h"
26#include "DNA_meshdata_types.h"
27
28#include "ED_grease_pencil.hh"
29#include "ED_view3d.hh"
30
32
34
35static constexpr float FIND_NEAREST_POINT_EPSILON = 1e-6f;
36static constexpr int BLUR_NEIGHBOUR_NUM = 5;
37static constexpr int SMEAR_NEIGHBOUR_NUM = 8;
38
40 public:
45
51
54
56
57 /* A stroke point can be read-only in case of material locking. Read-only means that the
58 * vertex weight can't be changed, but the weight does count for average, blur and smear. */
60
61 /* Flag for all stroke points in a drawing: true when the point was touched by the brush during
62 * a #GreasePencilStrokeOperation. */
65
66 /* Collected points under the brush in one #on_stroke_extended action. */
68 };
69
74
87
88 /* Flag for Auto-normalize weights of bone deformed vertex groups. */
90 /* Brush mode: normal, invert or smooth. */
92 /* Add or subtract weight? */
94 /* Active vertex group in GP object. */
96
97 /* Weight paint data per editable drawing. Stored per frame group. */
99
100 /* Set of bone-deformed vertex groups (object level). */
102 /* Set of locked vertex groups (object level). */
104
105 ~WeightPaintOperation() override = default;
106
107 /* Apply a weight to a point under the brush. */
109 const float target_weight,
110 DrawingWeightData &drawing_weight)
111 {
112 /* Blend the current point weight with the target weight. */
113 const float old_weight = drawing_weight.deform_weights[point.drawing_point_index];
114 const float weight_delta = (this->invert_brush_weight ? (1.0f - target_weight) :
115 target_weight) -
116 old_weight;
117 drawing_weight.deform_weights.set(
120 old_weight + math::interpolate(0.0f, weight_delta, point.influence), 0.0f, 1.0f));
121 }
122
123 /* Get brush settings (radius, strength etc.) */
124 void get_brush_settings(const bContext &C, const InputSample &start_sample)
125 {
126 using namespace blender::ed::greasepencil;
127
128 const Scene *scene = CTX_data_scene(&C);
129 this->object = CTX_data_active_object(&C);
130 this->grease_pencil = static_cast<GreasePencil *>(this->object->data);
133
134 this->brush = brush;
135 this->initial_brush_radius = BKE_brush_size_get(scene, brush);
136 this->initial_brush_strength = BKE_brush_alpha_get(scene, brush);
137 this->brush_weight = BKE_brush_weight_get(scene, brush);
138 this->mouse_position_previous = start_sample.mouse_position;
139 this->invert_brush_weight = false;
140
142
143 /* Auto-normalize weights is only applied when the object is deformed by an armature. */
145 this->auto_normalize = ts->auto_normalize &&
146 (BKE_modifiers_is_deformed_by_armature(this->object) != nullptr);
147 }
148
149 /* Get or create active vertex group in GP object. */
151 {
152 int object_defgroup_nr = BKE_object_defgroup_active_index_get(this->object) - 1;
153 if (object_defgroup_nr == -1) {
154 const ListBase *defbase = BKE_object_defgroup_list(this->object);
155 if (const Object *modob = BKE_modifiers_is_deformed_by_armature(this->object)) {
156 /* This happens on a Bone select, when no vgroup existed yet. */
157 const Bone *actbone = static_cast<bArmature *>(modob->data)->act_bone;
158 if (actbone) {
159 const bPoseChannel *pchan = BKE_pose_channel_find_name(modob->pose, actbone->name);
160
161 if (pchan) {
162 bDeformGroup *dg = BKE_object_defgroup_find_name(this->object, pchan->name);
163 if (dg == nullptr) {
164 dg = BKE_object_defgroup_add_name(this->object, pchan->name);
165 object_defgroup_nr = BLI_findindex(defbase, dg);
166 }
167 else {
168 const int actdef = BLI_findindex(defbase, dg);
169 BLI_assert(actdef >= 0);
170 this->grease_pencil->vertex_group_active_index = actdef + 1;
171 object_defgroup_nr = actdef;
172 }
173 }
174 }
175 }
176 if (BLI_listbase_is_empty(defbase)) {
177 BKE_object_defgroup_add(this->object);
178 object_defgroup_nr = 0;
179 }
180 }
181 this->object_defgroup = static_cast<bDeformGroup *>(
182 BLI_findlink(BKE_object_defgroup_list(this->object), object_defgroup_nr));
183 }
184
185 /* Get locked and bone-deformed vertex groups in GP object. */
187 {
188 const ListBase *defgroups = BKE_object_defgroup_list(this->object);
189 LISTBASE_FOREACH (bDeformGroup *, dg, defgroups) {
190 if ((dg->flag & DG_LOCK_WEIGHT) != 0) {
191 this->object_locked_defgroups.add(dg->name);
192 }
193 }
194 this->object_bone_deformed_defgroups = ed::greasepencil::get_bone_deformed_vertex_group_names(
195 *this->object);
196 }
197
198 /* For each drawing, retrieve pointers to the vertex weight data of the active vertex group,
199 * so that we can read and write to them later. And create buffers for points under the brush
200 * during one #on_stroke_extended action. */
203 const int frame_group)
204 {
205 const Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C);
206 const Object *ob_eval = DEG_get_evaluated(depsgraph, this->object);
207 const RegionView3D *rv3d = CTX_wm_region_view3d(&C);
208 const ARegion *region = CTX_wm_region(&C);
209
210 this->drawing_weight_data[frame_group].reinitialize(drawings.size());
211
212 threading::parallel_for(drawings.index_range(), 1, [&](const IndexRange range) {
213 for (const int drawing_index : range) {
214 const ed::greasepencil::MutableDrawingInfo &drawing_info = drawings[drawing_index];
215 bke::CurvesGeometry &curves = drawing_info.drawing.strokes_for_write();
216
217 /* Find or create the active vertex group in the drawing. */
218 DrawingWeightData &drawing_weight_data =
219 this->drawing_weight_data[frame_group][drawing_index];
220 drawing_weight_data.active_vertex_group = bke::greasepencil::ensure_vertex_group(
221 this->object_defgroup->name, curves.vertex_group_names);
222
223 drawing_weight_data.multi_frame_falloff = drawing_info.multi_frame_falloff;
224 drawing_weight_data.deform_verts = curves.deform_verts_for_write();
225 drawing_weight_data.deform_weights = bke::varray_for_mutable_deform_verts(
226 drawing_weight_data.deform_verts, drawing_weight_data.active_vertex_group);
227
228 /* Create boolean arrays indicating whether a vertex group is locked/bone deformed
229 * or not. */
230 if (this->auto_normalize) {
231 LISTBASE_FOREACH (bDeformGroup *, dg, &curves.vertex_group_names) {
232 drawing_weight_data.locked_vgroups.append(
233 this->object_locked_defgroups.contains(dg->name));
234 drawing_weight_data.bone_deformed_vgroups.append(
235 this->object_bone_deformed_defgroups.contains(dg->name));
236 }
237 }
238
239 /* Convert stroke points to screen space positions. */
240 const bke::greasepencil::Layer &layer = this->grease_pencil->layer(
241 drawing_info.layer_index);
242 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
243 const float4x4 projection = ED_view3d_ob_project_mat_get_from_obmat(rv3d, layer_to_world);
244
245 bke::crazyspace::GeometryDeformation deformation =
246 bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
247 ob_eval, *this->object, drawing_info.drawing);
248 drawing_weight_data.point_positions.reinitialize(deformation.positions.size());
249 threading::parallel_for(curves.points_range(), 1024, [&](const IndexRange point_range) {
250 for (const int point : point_range) {
251 drawing_weight_data.point_positions[point] = ED_view3d_project_float_v2_m4(
252 region, deformation.positions[point], projection);
253 }
254 });
255
256 /* Get the read-only state of stroke points (can be true in case of material locking). */
257 drawing_weight_data.point_is_read_only.reinitialize(deformation.positions.size());
258 drawing_weight_data.point_is_read_only.fill(true);
259 IndexMaskMemory memory;
260 const IndexMask editable_points = ed::greasepencil::retrieve_editable_points(
261 *this->object, drawing_info.drawing, drawing_info.layer_index, memory);
262 editable_points.foreach_index(GrainSize(1024), [&](const int64_t index) {
263 drawing_weight_data.point_is_read_only[index] = false;
264 });
265
266 /* Initialize the flag for stroke points being touched by the brush. */
267 drawing_weight_data.points_touched_by_brush_num = 0;
268 drawing_weight_data.points_touched_by_brush = Array<bool>(deformation.positions.size(),
269 false);
270 }
271 });
272 }
273
274 /* Get mouse position and pressure. */
275 void get_mouse_input_sample(const InputSample &input_sample,
276 const float brush_widen_factor = 1.0f)
277 {
278 this->mouse_position = input_sample.mouse_position;
281 this->brush_radius *= input_sample.pressure;
282 }
285 this->brush_strength *= input_sample.pressure;
286 }
287 this->brush_radius_wide = this->brush_radius * brush_widen_factor;
288
290 this->mouse_position.x - this->brush_radius_wide,
291 this->mouse_position.x + this->brush_radius_wide,
292 this->mouse_position.y - this->brush_radius_wide,
293 this->mouse_position.y + this->brush_radius_wide);
294 }
295
296 /* Add a point to the brush buffer when it is within the brush radius. */
298 DrawingWeightData &drawing_weight,
299 const int point_index)
300 {
301 if (!BLI_rctf_isect_pt_v(&this->brush_bbox, point_position)) {
302 return;
303 }
304 const float dist_point_to_brush_center = math::distance(point_position, this->mouse_position);
305 if (dist_point_to_brush_center > this->brush_radius_wide) {
306 return;
307 }
308
309 /* Point is touched by the (wide) brush, set flag for that. */
310 if (!drawing_weight.points_touched_by_brush[point_index]) {
311 drawing_weight.points_touched_by_brush_num++;
312 }
313 drawing_weight.points_touched_by_brush[point_index] = true;
314
315 if (dist_point_to_brush_center > this->brush_radius) {
316 return;
317 }
318
319 /* When the point is under the brush, add it to the brush buffer. */
320 const float influence = drawing_weight.multi_frame_falloff * this->brush_strength *
322 this->brush, dist_point_to_brush_center, this->brush_radius);
323 if (influence != 0.0f) {
324 drawing_weight.points_in_brush.append({influence, point_index});
325 }
326 }
327
328 /* Create KDTree for all stroke points touched by the brush during a weight paint operation. */
330 {
331 /* Get number of stroke points touched by the brush. */
332 int point_num = 0;
333 for (const DrawingWeightData &drawing_weight : drawing_weights) {
334 point_num += drawing_weight.points_touched_by_brush_num;
335 }
336
337 /* Create KDTree of stroke points touched by the brush. */
338 KDTree_2d *touched_points = BLI_kdtree_2d_new(point_num);
339 Array<float> touched_points_weights(point_num);
340 int kdtree_index = 0;
341 for (const DrawingWeightData &drawing_weight : drawing_weights) {
342 for (const int point_index : drawing_weight.point_positions.index_range()) {
343 if (drawing_weight.points_touched_by_brush[point_index]) {
344 BLI_kdtree_2d_insert(
345 touched_points, kdtree_index, drawing_weight.point_positions[point_index]);
346 touched_points_weights[kdtree_index] = drawing_weight.deform_weights[point_index];
347 kdtree_index++;
348 }
349 }
350 }
351 BLI_kdtree_2d_balance(touched_points);
352
353 return {touched_points, touched_points_weights};
354 }
355};
356
357} // namespace blender::ed::sculpt_paint::greasepencil
bPoseChannel * BKE_pose_channel_find_name(const bPose *pose, const char *name)
bool BKE_brush_use_alpha_pressure(const Brush *brush)
Definition brush.cc:1231
float BKE_brush_weight_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1276
int BKE_brush_size_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1210
float BKE_brush_curve_strength(eBrushCurvePreset preset, const CurveMapping *cumap, float distance, float brush_radius)
Definition brush.cc:1510
bool BKE_brush_use_size_pressure(const Brush *brush)
Definition brush.cc:1226
float BKE_brush_alpha_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1269
void BKE_curvemapping_init(CurveMapping *cumap)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
ToolSettings * CTX_data_tool_settings(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
Low-level operations for curves.
support for deformation groups and hooks.
int BKE_object_defgroup_active_index_get(const Object *ob)
Definition deform.cc:596
const ListBase * BKE_object_defgroup_list(const Object *ob)
Definition deform.cc:574
bDeformGroup * BKE_object_defgroup_find_name(const Object *ob, blender::StringRef name)
Definition deform.cc:515
Utility functions for vertex groups in grease pencil objects.
Object * BKE_modifiers_is_deformed_by_armature(Object *ob)
Functions for dealing with objects and deform verts, used by painting and tools.
struct bDeformGroup * BKE_object_defgroup_add(struct Object *ob)
struct bDeformGroup * BKE_object_defgroup_add_name(struct Object *ob, const char *name)
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:467
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:636
#define BLI_assert(a)
Definition BLI_assert.h:46
A KD-tree for nearest neighbor search.
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
bool BLI_rctf_isect_pt_v(const struct rctf *rect, const float xy[2])
void BLI_rctf_init(struct rctf *rect, float xmin, float xmax, float ymin, float ymax)
Definition rct.cc:404
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ DG_LOCK_WEIGHT
#define C
Definition RandGen.cpp:29
BPy_StructRNA * depsgraph
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:398
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
void set(const int64_t index, T value)
void get_mouse_input_sample(const InputSample &input_sample, const float brush_widen_factor=1.0f)
PointsTouchedByBrush create_affected_points_kdtree(const Span< DrawingWeightData > drawing_weights)
void add_point_under_brush_to_brush_buffer(const float2 point_position, DrawingWeightData &drawing_weight, const int point_index)
void get_brush_settings(const bContext &C, const InputSample &start_sample)
void init_weight_data_for_drawings(const bContext &C, const Span< ed::greasepencil::MutableDrawingInfo > &drawings, const int frame_group)
void apply_weight_to_point(const BrushPoint &point, const float target_weight, DrawingWeightData &drawing_weight)
#define this
Set< std::string > get_bone_deformed_vertex_group_names(const Object &object)
T clamp(const T &a, const T &min, const T &max)
T distance(const T &a, const T &b)
T interpolate(const T &a, const T &b, const FactorT &t)
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
VecBase< float, 2 > float2
BrushStrokeMode
char name[64]