Blender V5.0
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 this->object = CTX_data_active_object(&C);
129 this->grease_pencil = static_cast<GreasePencil *>(this->object->data);
132
133 this->brush = brush;
134 this->initial_brush_radius = BKE_brush_radius_get(paint, brush);
135 this->initial_brush_strength = BKE_brush_alpha_get(paint, brush);
136 this->brush_weight = BKE_brush_weight_get(paint, brush);
137 this->mouse_position_previous = start_sample.mouse_position;
138 this->invert_brush_weight = false;
139
140 BKE_curvemapping_init(brush->curve_distance_falloff);
141
142 /* Auto-normalize weights is only applied when the object is deformed by an armature. */
144 this->auto_normalize = ts->auto_normalize &&
145 (BKE_modifiers_is_deformed_by_armature(this->object) != nullptr);
146 }
147
148 /* Get or create active vertex group in GP object. */
150 {
151 int object_defgroup_nr = BKE_object_defgroup_active_index_get(this->object) - 1;
152 if (object_defgroup_nr == -1) {
153 const ListBase *defbase = BKE_object_defgroup_list(this->object);
154 if (const Object *modob = BKE_modifiers_is_deformed_by_armature(this->object)) {
155 /* This happens on a Bone select, when no vgroup existed yet. */
156 const Bone *actbone = static_cast<bArmature *>(modob->data)->act_bone;
157 if (actbone) {
158 const bPoseChannel *pchan = BKE_pose_channel_find_name(modob->pose, actbone->name);
159
160 if (pchan) {
161 bDeformGroup *dg = BKE_object_defgroup_find_name(this->object, pchan->name);
162 if (dg == nullptr) {
163 dg = BKE_object_defgroup_add_name(this->object, pchan->name);
164 object_defgroup_nr = BLI_findindex(defbase, dg);
165 }
166 else {
167 const int actdef = BLI_findindex(defbase, dg);
168 BLI_assert(actdef >= 0);
169 this->grease_pencil->vertex_group_active_index = actdef + 1;
170 object_defgroup_nr = actdef;
171 }
172 }
173 }
174 }
175 if (BLI_listbase_is_empty(defbase)) {
176 BKE_object_defgroup_add(this->object);
177 object_defgroup_nr = 0;
178 }
179 }
180 this->object_defgroup = static_cast<bDeformGroup *>(
181 BLI_findlink(BKE_object_defgroup_list(this->object), object_defgroup_nr));
182 }
183
184 /* Get locked and bone-deformed vertex groups in GP object. */
186 {
187 const ListBase *defgroups = BKE_object_defgroup_list(this->object);
188 LISTBASE_FOREACH (bDeformGroup *, dg, defgroups) {
189 if ((dg->flag & DG_LOCK_WEIGHT) != 0) {
190 this->object_locked_defgroups.add(dg->name);
191 }
192 }
193 this->object_bone_deformed_defgroups = ed::greasepencil::get_bone_deformed_vertex_group_names(
194 *this->object);
195 }
196
197 /* For each drawing, retrieve pointers to the vertex weight data of the active vertex group,
198 * so that we can read and write to them later. And create buffers for points under the brush
199 * during one #on_stroke_extended action. */
202 const int frame_group)
203 {
204 const Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C);
205 const Object *ob_eval = DEG_get_evaluated(depsgraph, this->object);
206 const RegionView3D *rv3d = CTX_wm_region_view3d(&C);
207 const ARegion *region = CTX_wm_region(&C);
208
209 this->drawing_weight_data[frame_group].reinitialize(drawings.size());
210
211 threading::parallel_for(drawings.index_range(), 1, [&](const IndexRange range) {
212 for (const int drawing_index : range) {
213 const ed::greasepencil::MutableDrawingInfo &drawing_info = drawings[drawing_index];
214 bke::CurvesGeometry &curves = drawing_info.drawing.strokes_for_write();
215
216 /* Find or create the active vertex group in the drawing. */
217 DrawingWeightData &drawing_weight_data =
218 this->drawing_weight_data[frame_group][drawing_index];
219 drawing_weight_data.active_vertex_group = bke::greasepencil::ensure_vertex_group(
220 this->object_defgroup->name, curves.vertex_group_names);
221
222 drawing_weight_data.multi_frame_falloff = drawing_info.multi_frame_falloff;
223 drawing_weight_data.deform_verts = curves.deform_verts_for_write();
224 drawing_weight_data.deform_weights = bke::varray_for_mutable_deform_verts(
225 drawing_weight_data.deform_verts, drawing_weight_data.active_vertex_group);
226
227 /* Create boolean arrays indicating whether a vertex group is locked/bone deformed
228 * or not. */
229 if (this->auto_normalize) {
230 LISTBASE_FOREACH (bDeformGroup *, dg, &curves.vertex_group_names) {
231 drawing_weight_data.locked_vgroups.append(
232 this->object_locked_defgroups.contains(dg->name));
233 drawing_weight_data.bone_deformed_vgroups.append(
234 this->object_bone_deformed_defgroups.contains(dg->name));
235 }
236 }
237
238 /* Convert stroke points to screen space positions. */
239 const bke::greasepencil::Layer &layer = this->grease_pencil->layer(
240 drawing_info.layer_index);
241 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
242 const float4x4 projection = ED_view3d_ob_project_mat_get_from_obmat(rv3d, layer_to_world);
243
244 bke::crazyspace::GeometryDeformation deformation =
245 bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
246 ob_eval, *this->object, drawing_info.drawing);
247 drawing_weight_data.point_positions.reinitialize(deformation.positions.size());
248 threading::parallel_for(curves.points_range(), 1024, [&](const IndexRange point_range) {
249 for (const int point : point_range) {
250 drawing_weight_data.point_positions[point] = ED_view3d_project_float_v2_m4(
251 region, deformation.positions[point], projection);
252 }
253 });
254
255 /* Get the read-only state of stroke points (can be true in case of material locking). */
256 drawing_weight_data.point_is_read_only.reinitialize(deformation.positions.size());
257 drawing_weight_data.point_is_read_only.fill(true);
258 IndexMaskMemory memory;
259 const IndexMask editable_points = ed::greasepencil::retrieve_editable_points(
260 *this->object, drawing_info.drawing, drawing_info.layer_index, memory);
261 editable_points.foreach_index(GrainSize(1024), [&](const int64_t index) {
262 drawing_weight_data.point_is_read_only[index] = false;
263 });
264
265 /* Initialize the flag for stroke points being touched by the brush. */
266 drawing_weight_data.points_touched_by_brush_num = 0;
267 drawing_weight_data.points_touched_by_brush = Array<bool>(deformation.positions.size(),
268 false);
269 }
270 });
271 }
272
273 /* Get mouse position and pressure. */
274 void get_mouse_input_sample(const InputSample &input_sample,
275 const float brush_widen_factor = 1.0f)
276 {
277 this->mouse_position = input_sample.mouse_position;
280 this->brush_radius *= input_sample.pressure;
281 }
284 this->brush_strength *= input_sample.pressure;
285 }
286 this->brush_radius_wide = this->brush_radius * brush_widen_factor;
287
289 this->mouse_position.x - this->brush_radius_wide,
290 this->mouse_position.x + this->brush_radius_wide,
291 this->mouse_position.y - this->brush_radius_wide,
292 this->mouse_position.y + this->brush_radius_wide);
293 }
294
295 /* Add a point to the brush buffer when it is within the brush radius. */
297 DrawingWeightData &drawing_weight,
298 const int point_index)
299 {
300 if (!BLI_rctf_isect_pt_v(&this->brush_bbox, point_position)) {
301 return;
302 }
303 const float dist_point_to_brush_center = math::distance(point_position, this->mouse_position);
304 if (dist_point_to_brush_center > this->brush_radius_wide) {
305 return;
306 }
307
308 /* Point is touched by the (wide) brush, set flag for that. */
309 if (!drawing_weight.points_touched_by_brush[point_index]) {
310 drawing_weight.points_touched_by_brush_num++;
311 }
312 drawing_weight.points_touched_by_brush[point_index] = true;
313
314 if (dist_point_to_brush_center > this->brush_radius) {
315 return;
316 }
317
318 /* When the point is under the brush, add it to the brush buffer. */
319 const float influence = drawing_weight.multi_frame_falloff * this->brush_strength *
321 this->brush, dist_point_to_brush_center, this->brush_radius);
322 if (influence != 0.0f) {
323 drawing_weight.points_in_brush.append({influence, point_index});
324 }
325 }
326
327 /* Create KDTree for all stroke points touched by the brush during a weight paint operation. */
329 {
330 /* Get number of stroke points touched by the brush. */
331 int point_num = 0;
332 for (const DrawingWeightData &drawing_weight : drawing_weights) {
333 point_num += drawing_weight.points_touched_by_brush_num;
334 }
335
336 /* Create KDTree of stroke points touched by the brush. */
337 KDTree_2d *touched_points = BLI_kdtree_2d_new(point_num);
338 Array<float> touched_points_weights(point_num);
339 int kdtree_index = 0;
340 for (const DrawingWeightData &drawing_weight : drawing_weights) {
341 for (const int point_index : drawing_weight.point_positions.index_range()) {
342 if (drawing_weight.points_touched_by_brush[point_index]) {
343 BLI_kdtree_2d_insert(
344 touched_points, kdtree_index, drawing_weight.point_positions[point_index]);
345 touched_points_weights[kdtree_index] = drawing_weight.deform_weights[point_index];
346 kdtree_index++;
347 }
348 }
349 }
350 BLI_kdtree_2d_balance(touched_points);
351
352 return {touched_points, touched_points_weights};
353 }
354};
355
356} // 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:1290
float BKE_brush_alpha_get(const Paint *paint, const Brush *brush)
Definition brush.cc:1357
float BKE_brush_curve_strength(eBrushCurvePreset preset, const CurveMapping *cumap, float distance, float brush_radius)
Definition brush.cc:1577
bool BKE_brush_use_size_pressure(const Brush *brush)
Definition brush.cc:1285
float BKE_brush_radius_get(const Paint *paint, const Brush *brush)
Definition brush.cc:1272
float BKE_brush_weight_get(const Paint *paint, const Brush *brush)
Definition brush.cc:1364
void BKE_curvemapping_init(CurveMapping *cumap)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(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:607
const ListBase * BKE_object_defgroup_list(const Object *ob)
Definition deform.cc:585
bDeformGroup * BKE_object_defgroup_find_name(const Object *ob, blender::StringRef name)
Definition deform.cc:526
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:476
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:645
#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:419
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)
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]