Blender  V3.3
curves_sculpt_selection_paint.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include <algorithm>
4 #include <numeric>
5 
6 #include "BLI_memory_utils.hh"
7 #include "BLI_task.hh"
8 
9 #include "DNA_brush_types.h"
10 
11 #include "BKE_brush.h"
12 #include "BKE_context.h"
13 #include "BKE_curves.hh"
14 
15 #include "DEG_depsgraph.h"
16 
17 #include "ED_screen.h"
18 #include "ED_view3d.h"
19 
20 #include "WM_api.h"
21 
22 #include "curves_sculpt_intern.hh"
23 
31 namespace blender::ed::sculpt_paint {
32 
34 
36  private:
37  bool use_select_;
38  bool clear_selection_;
39 
40  CurvesBrush3D brush_3d_;
41 
43 
44  public:
45  SelectionPaintOperation(const bool use_select, const bool clear_selection)
46  : use_select_(use_select), clear_selection_(clear_selection)
47  {
48  }
49  void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
50 };
51 
55 
56  Object *object_ = nullptr;
57  Curves *curves_id_ = nullptr;
58  CurvesGeometry *curves_ = nullptr;
59 
60  const Brush *brush_ = nullptr;
64 
66 
68 
70 
72  {
73  }
74 
76  const bContext &C,
77  const StrokeExtension &stroke_extension)
78  {
79  self_ = &self;
81 
82  curves_id_ = static_cast<Curves *>(object_->data);
85  if (curves_->curves_num() == 0) {
86  return;
87  }
88 
91  brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension);
93 
94  brush_pos_re_ = stroke_extension.mouse_position;
95 
96  if (self.clear_selection_) {
97  if (stroke_extension.is_first) {
100  }
103  }
104  }
105  }
106 
108 
109  const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>(
111 
112  selection_goal_ = self_->use_select_ ? 1.0f : 0.0f;
113 
114  if (stroke_extension.is_first) {
115  if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
117  }
118  }
119 
120  if (curves_id_->selection_domain == ATTR_DOMAIN_POINT) {
122  if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
124  }
125  else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
127  }
128  }
129  else {
131  if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
133  }
134  else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
136  }
137  }
138 
139  /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because
140  * selection is handled as a generic attribute for now. */
145  }
146 
148  {
149  const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
151  for (const float4x4 &brush_transform : symmetry_brush_transforms) {
152  this->paint_point_selection_projected(brush_transform, selection);
153  }
154  }
155 
156  void paint_point_selection_projected(const float4x4 &brush_transform,
157  MutableSpan<float> selection)
158  {
159  const float4x4 brush_transform_inv = brush_transform.inverted();
160 
161  float4x4 projection;
163 
164  const bke::crazyspace::GeometryDeformation deformation =
166 
167  const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
168  const float brush_radius_sq_re = pow2f(brush_radius_re);
169 
170  threading::parallel_for(curves_->points_range(), 1024, [&](const IndexRange point_range) {
171  for (const int point_i : point_range) {
172  const float3 pos_cu = brush_transform_inv * deformation.positions[point_i];
173 
174  /* Find the position of the point in screen space. */
175  float2 pos_re;
176  ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values);
177 
178  const float distance_to_brush_sq_re = math::distance_squared(pos_re, brush_pos_re_);
179  if (distance_to_brush_sq_re > brush_radius_sq_re) {
180  /* Ignore the point because it's too far away. */
181  continue;
182  }
183 
184  const float distance_to_brush_re = std::sqrt(distance_to_brush_sq_re);
185  /* A falloff that is based on how far away the point is from the stroke. */
186  const float radius_falloff = BKE_brush_curve_strength(
187  brush_, distance_to_brush_re, brush_radius_re);
188  /* Combine the falloff and brush strength. */
189  const float weight = brush_strength_ * radius_falloff;
190 
191  selection[point_i] = math::interpolate(selection[point_i], selection_goal_, weight);
192  }
193  });
194  }
195 
197  {
198  float4x4 projection;
199  ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
200 
201  float3 brush_wo;
202  ED_view3d_win_to_3d(ctx_.v3d,
203  ctx_.region,
204  transforms_.curves_to_world * self_->brush_3d_.position_cu,
205  brush_pos_re_,
206  brush_wo);
207  const float3 brush_cu = transforms_.world_to_curves * brush_wo;
208 
209  const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
210  eCurvesSymmetryType(curves_id_->symmetry));
211 
212  for (const float4x4 &brush_transform : symmetry_brush_transforms) {
213  this->paint_point_selection_spherical(selection, brush_transform * brush_cu);
214  }
215  }
216 
218  {
219  const bke::crazyspace::GeometryDeformation deformation =
220  bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
221 
222  const float brush_radius_cu = self_->brush_3d_.radius_cu;
223  const float brush_radius_sq_cu = pow2f(brush_radius_cu);
224 
225  threading::parallel_for(curves_->points_range(), 1024, [&](const IndexRange point_range) {
226  for (const int i : point_range) {
227  const float3 pos_old_cu = deformation.positions[i];
228 
229  /* Compute distance to the brush. */
230  const float distance_to_brush_sq_cu = math::distance_squared(pos_old_cu, brush_cu);
231  if (distance_to_brush_sq_cu > brush_radius_sq_cu) {
232  /* Ignore the point because it's too far away. */
233  continue;
234  }
235 
236  const float distance_to_brush_cu = std::sqrt(distance_to_brush_sq_cu);
237 
238  /* A falloff that is based on how far away the point is from the stroke. */
239  const float radius_falloff = BKE_brush_curve_strength(
240  brush_, distance_to_brush_cu, brush_radius_cu);
241  /* Combine the falloff and brush strength. */
242  const float weight = brush_strength_ * radius_falloff;
243 
244  selection[i] = math::interpolate(selection[i], selection_goal_, weight);
245  }
246  });
247  }
248 
250  {
251  const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
252  eCurvesSymmetryType(curves_id_->symmetry));
253  for (const float4x4 &brush_transform : symmetry_brush_transforms) {
254  this->paint_curve_selection_projected(brush_transform, selection);
255  }
256  }
257 
258  void paint_curve_selection_projected(const float4x4 &brush_transform,
259  MutableSpan<float> selection)
260  {
261  const float4x4 brush_transform_inv = brush_transform.inverted();
262 
263  const bke::crazyspace::GeometryDeformation deformation =
264  bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
265 
266  float4x4 projection;
267  ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
268 
269  const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
270  const float brush_radius_sq_re = pow2f(brush_radius_re);
271 
272  threading::parallel_for(curves_->curves_range(), 1024, [&](const IndexRange curves_range) {
273  for (const int curve_i : curves_range) {
274  const float max_weight = threading::parallel_reduce(
275  curves_->points_for_curve(curve_i).drop_back(1),
276  1024,
277  0.0f,
278  [&](const IndexRange segment_range, const float init) {
279  float max_weight = init;
280  for (const int segment_i : segment_range) {
281  const float3 pos1_cu = brush_transform_inv * deformation.positions[segment_i];
282  const float3 pos2_cu = brush_transform_inv * deformation.positions[segment_i + 1];
283 
284  float2 pos1_re;
285  float2 pos2_re;
286  ED_view3d_project_float_v2_m4(ctx_.region, pos1_cu, pos1_re, projection.values);
287  ED_view3d_project_float_v2_m4(ctx_.region, pos2_cu, pos2_re, projection.values);
288 
289  const float distance_sq_re = dist_squared_to_line_segment_v2(
290  brush_pos_re_, pos1_re, pos2_re);
291  if (distance_sq_re > brush_radius_sq_re) {
292  continue;
293  }
294  const float radius_falloff = BKE_brush_curve_strength(
295  brush_, std::sqrt(distance_sq_re), brush_radius_re);
296  const float weight = brush_strength_ * radius_falloff;
297  max_weight = std::max(max_weight, weight);
298  }
299  return max_weight;
300  },
301  [](float a, float b) { return std::max(a, b); });
302  selection[curve_i] = math::interpolate(selection[curve_i], selection_goal_, max_weight);
303  }
304  });
305  }
306 
308  {
309  float4x4 projection;
310  ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
311 
312  float3 brush_wo;
313  ED_view3d_win_to_3d(ctx_.v3d,
314  ctx_.region,
315  transforms_.curves_to_world * self_->brush_3d_.position_cu,
316  brush_pos_re_,
317  brush_wo);
318  const float3 brush_cu = transforms_.world_to_curves * brush_wo;
319 
320  const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
321  eCurvesSymmetryType(curves_id_->symmetry));
322 
323  for (const float4x4 &brush_transform : symmetry_brush_transforms) {
324  this->paint_curve_selection_spherical(selection, brush_transform * brush_cu);
325  }
326  }
327 
329  {
330  const bke::crazyspace::GeometryDeformation deformation =
331  bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
332 
333  const float brush_radius_cu = self_->brush_3d_.radius_cu;
334  const float brush_radius_sq_cu = pow2f(brush_radius_cu);
335 
336  threading::parallel_for(curves_->curves_range(), 1024, [&](const IndexRange curves_range) {
337  for (const int curve_i : curves_range) {
338  const float max_weight = threading::parallel_reduce(
339  curves_->points_for_curve(curve_i).drop_back(1),
340  1024,
341  0.0f,
342  [&](const IndexRange segment_range, const float init) {
343  float max_weight = init;
344  for (const int segment_i : segment_range) {
345  const float3 &pos1_cu = deformation.positions[segment_i];
346  const float3 &pos2_cu = deformation.positions[segment_i + 1];
347 
348  const float distance_sq_cu = dist_squared_to_line_segment_v3(
349  brush_cu, pos1_cu, pos2_cu);
350  if (distance_sq_cu > brush_radius_sq_cu) {
351  continue;
352  }
353  const float radius_falloff = BKE_brush_curve_strength(
354  brush_, std::sqrt(distance_sq_cu), brush_radius_cu);
355  const float weight = brush_strength_ * radius_falloff;
356  max_weight = std::max(max_weight, weight);
357  }
358  return max_weight;
359  },
360  [](float a, float b) { return std::max(a, b); });
361  selection[curve_i] = math::interpolate(selection[curve_i], selection_goal_, max_weight);
362  }
363  });
364  }
365 
367  {
368  std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush(*ctx_.depsgraph,
369  *ctx_.region,
370  *ctx_.v3d,
371  *ctx_.rv3d,
372  *object_,
373  brush_pos_re_,
374  brush_radius_base_re_);
375  if (brush_3d.has_value()) {
376  self_->brush_3d_ = *brush_3d;
377  }
378  }
379 };
380 
381 void SelectionPaintOperation::on_stroke_extended(const bContext &C,
382  const StrokeExtension &stroke_extension)
383 {
385  executor.execute(*this, C, stroke_extension);
386 }
387 
388 std::unique_ptr<CurvesSculptStrokeOperation> new_selection_paint_operation(
389  const BrushStrokeMode brush_mode, const bContext &C)
390 {
391  Scene &scene = *CTX_data_scene(&C);
393  const bool use_select = ELEM(brush_mode, BRUSH_STROKE_INVERT) ==
394  ((brush.flag & BRUSH_DIR_IN) != 0);
395  const bool clear_selection = use_select && brush_mode != BRUSH_STROKE_SMOOTH;
396 
397  return std::make_unique<SelectionPaintOperation>(use_select, clear_selection);
398 }
399 
400 } // namespace blender::ed::sculpt_paint
@ ATTR_DOMAIN_CURVE
Definition: BKE_attribute.h:31
@ ATTR_DOMAIN_POINT
Definition: BKE_attribute.h:27
float BKE_brush_alpha_get(const struct Scene *scene, const struct Brush *brush)
int BKE_brush_size_get(const struct Scene *scene, const struct Brush *brush)
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1090
struct Object * CTX_data_active_object(const bContext *C)
Definition: context.c:1353
Low-level operations for curves.
struct Brush * BKE_paint_brush(struct Paint *paint)
Definition: paint.c:607
const struct Brush * BKE_paint_brush_for_read(const struct Paint *p)
MINLINE float pow2f(float x)
#define ELEM(...)
void DEG_id_tag_update(struct ID *id, int flag)
@ ID_RECALC_GEOMETRY
Definition: DNA_ID.h:791
@ BRUSH_DIR_IN
eBrushFalloffShape
@ PAINT_FALLOFF_SHAPE_SPHERE
@ PAINT_FALLOFF_SHAPE_TUBE
struct CurvesGeometry CurvesGeometry
eCurvesSymmetryType
@ CV_SCULPT_SELECTION_ENABLED
#define RV3D_PAINTING
void ED_region_tag_redraw(struct ARegion *region)
Definition: area.c:655
void ED_view3d_ob_project_mat_get(const struct RegionView3D *v3d, const struct Object *ob, float r_pmat[4][4])
void ED_view3d_win_to_3d(const struct View3D *v3d, const struct ARegion *region, const float depth_pt[3], const float mval[2], float r_out[3])
#define C
Definition: RandGen.cpp:25
#define NC_GEOM
Definition: WM_types.h:343
#define ND_DATA
Definition: WM_types.h:456
MutableSpan< float > selection_point_float_for_write()
MutableSpan< float > selection_curve_float_for_write()
IndexRange points_range() const
Definition: BKE_curves.hh:791
static CurvesGeometry & wrap(::CurvesGeometry &dna_struct)
Definition: BKE_curves.hh:138
void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override
SelectionPaintOperation(const bool use_select, const bool clear_selection)
Scene scene
GeometryDeformation get_evaluated_curves_deformation(const Depsgraph &depsgraph, const Object &ob_orig)
Definition: crazyspace.cc:595
std::optional< CurvesBrush3D > sample_curves_3d_brush(const Depsgraph &depsgraph, const ARegion &region, const View3D &v3d, const RegionView3D &rv3d, const Object &curves_object, const float2 &brush_pos_re, const float brush_radius_re)
std::unique_ptr< CurvesSculptStrokeOperation > new_selection_paint_operation(const BrushStrokeMode brush_mode, const bContext &C)
Vector< float4x4 > get_symmetry_brush_transforms(const eCurvesSymmetryType symmetry)
float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension)
void parallel_for(IndexRange range, int64_t grain_size, const Function &function)
Definition: BLI_task.hh:51
BrushStrokeMode
Definition: paint_intern.h:448
@ BRUSH_STROKE_SMOOTH
Definition: paint_intern.h:451
@ BRUSH_STROKE_INVERT
Definition: paint_intern.h:450
char falloff_shape
CurvesGeometry geometry
char selection_domain
struct Object * surface
void * data
struct ToolSettings * toolsettings
CurvesSculpt * curves_sculpt
void paint_point_selection_spherical_with_symmetry(MutableSpan< float > selection)
void paint_point_selection_spherical(MutableSpan< float > selection, const float3 &brush_cu)
void paint_curve_selection_projected(const float4x4 &brush_transform, MutableSpan< float > selection)
void paint_point_selection_projected_with_symmetry(MutableSpan< float > selection)
void paint_curve_selection_spherical(MutableSpan< float > selection, const float3 &brush_cu)
void paint_point_selection_projected(const float4x4 &brush_transform, MutableSpan< float > selection)
void paint_curve_selection_spherical_with_symmetry(MutableSpan< float > selection)
void paint_curve_selection_projected_with_symmetry(MutableSpan< float > selection)
void execute(SelectionPaintOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
float values[4][4]
Definition: BLI_float4x4.hh:13
float4x4 inverted() const
void WM_main_add_notifier(unsigned int type, void *reference)