Blender  V3.3
curves_sculpt_comb.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include <algorithm>
4 
6 
7 #include "BLI_float4x4.hh"
8 #include "BLI_index_mask_ops.hh"
9 #include "BLI_kdtree.h"
10 #include "BLI_rand.hh"
11 #include "BLI_vector.hh"
12 
13 #include "PIL_time.h"
14 
15 #include "DEG_depsgraph.h"
16 #include "DEG_depsgraph_query.h"
17 
18 #include "BKE_attribute_math.hh"
19 #include "BKE_brush.h"
20 #include "BKE_bvhutils.h"
21 #include "BKE_context.h"
22 #include "BKE_crazyspace.hh"
23 #include "BKE_curves.hh"
24 #include "BKE_geometry_set.hh"
25 #include "BKE_mesh.h"
26 #include "BKE_mesh_runtime.h"
27 #include "BKE_paint.h"
28 
29 #include "DNA_brush_enums.h"
30 #include "DNA_brush_types.h"
31 #include "DNA_curves_types.h"
32 #include "DNA_mesh_types.h"
33 #include "DNA_meshdata_types.h"
34 #include "DNA_object_types.h"
35 #include "DNA_screen_types.h"
36 #include "DNA_space_types.h"
37 
38 #include "ED_screen.h"
39 #include "ED_view3d.h"
40 
41 #include "UI_interface.h"
42 
43 #include "WM_api.h"
44 
53 namespace blender::ed::sculpt_paint {
54 
56 using threading::EnumerableThreadSpecific;
57 
62  private:
64  float2 brush_pos_last_re_;
65 
67  CurvesBrush3D brush_3d_;
68 
70  Array<float> segment_lengths_cu_;
71 
72  friend struct CombOperationExecutor;
73 
74  public:
75  void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
76 };
77 
83  CombOperation *self_ = nullptr;
85 
86  const CurvesSculpt *curves_sculpt_ = nullptr;
87  const Brush *brush_ = nullptr;
91 
93 
94  Object *curves_ob_orig_ = nullptr;
95  Curves *curves_id_orig_ = nullptr;
97 
101 
105 
107 
109  {
110  }
111 
112  void execute(CombOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
113  {
114  self_ = &self;
115 
116  BLI_SCOPED_DEFER([&]() { self_->brush_pos_last_re_ = stroke_extension.mouse_position; });
117 
119  curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data);
121  if (curves_orig_->curves_num() == 0) {
122  return;
123  }
124 
128  brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension);
129  brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension);
130 
132 
134 
137 
138  brush_pos_prev_re_ = self_->brush_pos_last_re_;
139  brush_pos_re_ = stroke_extension.mouse_position;
141 
142  if (stroke_extension.is_first) {
145  }
147  /* Combing does nothing when there is no mouse movement, so return directly. */
148  return;
149  }
150 
151  EnumerableThreadSpecific<Vector<int>> changed_curves;
152 
154  this->comb_projected_with_symmetry(changed_curves);
155  }
157  this->comb_spherical_with_symmetry(changed_curves);
158  }
159  else {
161  }
162 
163  this->restore_segment_lengths(changed_curves);
164 
169  }
170 
175  {
176  const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
178  for (const float4x4 &brush_transform : symmetry_brush_transforms) {
179  this->comb_projected(r_changed_curves, brush_transform);
180  }
181  }
182 
184  const float4x4 &brush_transform)
185  {
186  const float4x4 brush_transform_inv = brush_transform.inverted();
187 
189  const bke::crazyspace::GeometryDeformation deformation =
191 
192  float4x4 projection;
194 
195  const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
196  const float brush_radius_sq_re = pow2f(brush_radius_re);
197 
199  Vector<int> &local_changed_curves = r_changed_curves.local();
200  for (const int curve_i : curve_selection_.slice(range)) {
201  bool curve_changed = false;
202  const IndexRange points = curves_orig_->points_for_curve(curve_i);
203  for (const int point_i : points.drop_front(1)) {
204  const float3 old_pos_cu = deformation.positions[point_i];
205  const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu;
206 
207  /* Find the position of the point in screen space. */
208  float2 old_symm_pos_re;
209  ED_view3d_project_float_v2_m4(
210  ctx_.region, old_symm_pos_cu, old_symm_pos_re, projection.values);
211 
212  const float distance_to_brush_sq_re = dist_squared_to_line_segment_v2(
213  old_symm_pos_re, brush_pos_prev_re_, brush_pos_re_);
214  if (distance_to_brush_sq_re > brush_radius_sq_re) {
215  /* Ignore the point because it's too far away. */
216  continue;
217  }
218 
219  const float distance_to_brush_re = std::sqrt(distance_to_brush_sq_re);
220  /* A falloff that is based on how far away the point is from the stroke. */
221  const float radius_falloff = BKE_brush_curve_strength(
222  brush_, distance_to_brush_re, brush_radius_re);
223  /* Combine the falloff and brush strength. */
224  const float weight = brush_strength_ * radius_falloff * point_factors_[point_i];
225 
226  /* Offset the old point position in screen space and transform it back into 3D space.
227  */
228  const float2 new_symm_pos_re = old_symm_pos_re + brush_pos_diff_re_ * weight;
229  float3 new_symm_pos_wo;
230  ED_view3d_win_to_3d(ctx_.v3d,
231  ctx_.region,
232  transforms_.curves_to_world * old_symm_pos_cu,
233  new_symm_pos_re,
234  new_symm_pos_wo);
235  const float3 new_pos_cu = brush_transform *
236  (transforms_.world_to_curves * new_symm_pos_wo);
237 
238  const float3 translation_eval = new_pos_cu - old_pos_cu;
239  const float3 translation_orig = deformation.translation_from_deformed_to_original(
240  point_i, translation_eval);
241  positions_cu_orig[point_i] += translation_orig;
242 
243  curve_changed = true;
244  }
245  if (curve_changed) {
246  local_changed_curves.append(curve_i);
247  }
248  }
249  });
250  }
251 
256  {
257  float4x4 projection;
258  ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values);
259 
260  float3 brush_start_wo, brush_end_wo;
261  ED_view3d_win_to_3d(ctx_.v3d,
262  ctx_.region,
263  transforms_.curves_to_world * self_->brush_3d_.position_cu,
264  brush_pos_prev_re_,
265  brush_start_wo);
266  ED_view3d_win_to_3d(ctx_.v3d,
267  ctx_.region,
268  transforms_.curves_to_world * self_->brush_3d_.position_cu,
269  brush_pos_re_,
270  brush_end_wo);
271  const float3 brush_start_cu = transforms_.world_to_curves * brush_start_wo;
272  const float3 brush_end_cu = transforms_.world_to_curves * brush_end_wo;
273 
274  const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_;
275 
276  const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
277  eCurvesSymmetryType(curves_id_orig_->symmetry));
278  for (const float4x4 &brush_transform : symmetry_brush_transforms) {
279  this->comb_spherical(r_changed_curves,
280  brush_transform * brush_start_cu,
281  brush_transform * brush_end_cu,
282  brush_radius_cu);
283  }
284  }
285 
287  const float3 &brush_start_cu,
288  const float3 &brush_end_cu,
289  const float brush_radius_cu)
290  {
291  MutableSpan<float3> positions_cu = curves_orig_->positions_for_write();
292  const float brush_radius_sq_cu = pow2f(brush_radius_cu);
293  const float3 brush_diff_cu = brush_end_cu - brush_start_cu;
294 
295  const bke::crazyspace::GeometryDeformation deformation =
296  bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_);
297 
298  threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
299  Vector<int> &local_changed_curves = r_changed_curves.local();
300  for (const int curve_i : curve_selection_.slice(range)) {
301  bool curve_changed = false;
302  const IndexRange points = curves_orig_->points_for_curve(curve_i);
303  for (const int point_i : points.drop_front(1)) {
304  const float3 pos_old_cu = deformation.positions[point_i];
305 
306  /* Compute distance to the brush. */
307  const float distance_to_brush_sq_cu = dist_squared_to_line_segment_v3(
308  pos_old_cu, brush_start_cu, brush_end_cu);
309  if (distance_to_brush_sq_cu > brush_radius_sq_cu) {
310  /* Ignore the point because it's too far away. */
311  continue;
312  }
313 
314  const float distance_to_brush_cu = std::sqrt(distance_to_brush_sq_cu);
315 
316  /* A falloff that is based on how far away the point is from the stroke. */
317  const float radius_falloff = BKE_brush_curve_strength(
318  brush_, distance_to_brush_cu, brush_radius_cu);
319  /* Combine the falloff and brush strength. */
320  const float weight = brush_strength_ * radius_falloff * point_factors_[point_i];
321 
322  const float3 translation_eval_cu = weight * brush_diff_cu;
323  const float3 translation_orig_cu = deformation.translation_from_deformed_to_original(
324  point_i, translation_eval_cu);
325 
326  /* Update the point position. */
327  positions_cu[point_i] += translation_orig_cu;
328  curve_changed = true;
329  }
330  if (curve_changed) {
331  local_changed_curves.append(curve_i);
332  }
333  }
334  });
335  }
336 
341  {
342  std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush(*ctx_.depsgraph,
343  *ctx_.region,
344  *ctx_.v3d,
345  *ctx_.rv3d,
346  *curves_ob_orig_,
347  brush_pos_re_,
348  brush_radius_base_re_);
349  if (brush_3d.has_value()) {
350  self_->brush_3d_ = *brush_3d;
351  }
352  }
353 
359  {
360  const Span<float3> positions_cu = curves_orig_->positions();
361  self_->segment_lengths_cu_.reinitialize(curves_orig_->points_num());
362  threading::parallel_for(curves_orig_->curves_range(), 128, [&](const IndexRange range) {
363  for (const int curve_i : range) {
364  const IndexRange points = curves_orig_->points_for_curve(curve_i);
365  for (const int point_i : points.drop_back(1)) {
366  const float3 &p1_cu = positions_cu[point_i];
367  const float3 &p2_cu = positions_cu[point_i + 1];
368  const float length_cu = math::distance(p1_cu, p2_cu);
369  self_->segment_lengths_cu_[point_i] = length_cu;
370  }
371  }
372  });
373  }
374 
379  {
380  const Span<float> expected_lengths_cu = self_->segment_lengths_cu_;
381  MutableSpan<float3> positions_cu = curves_orig_->positions_for_write();
382 
383  threading::parallel_for_each(changed_curves, [&](const Vector<int> &changed_curves) {
384  threading::parallel_for(changed_curves.index_range(), 256, [&](const IndexRange range) {
385  for (const int curve_i : changed_curves.as_span().slice(range)) {
386  const IndexRange points = curves_orig_->points_for_curve(curve_i);
387  for (const int segment_i : points.drop_back(1)) {
388  const float3 &p1_cu = positions_cu[segment_i];
389  float3 &p2_cu = positions_cu[segment_i + 1];
390  const float3 direction = math::normalize(p2_cu - p1_cu);
391  const float expected_length_cu = expected_lengths_cu[segment_i];
392  p2_cu = p1_cu + direction * expected_length_cu;
393  }
394  }
395  });
396  });
397  }
398 };
399 
400 void CombOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension)
401 {
402  CombOperationExecutor executor{C};
403  executor.execute(*this, C, stroke_extension);
404 }
405 
406 std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation()
407 {
408  return std::make_unique<CombOperation>();
409 }
410 
411 } // namespace blender::ed::sculpt_paint
int BKE_brush_size_get(const struct Scene *scene, const struct Brush *brush)
struct Object * CTX_data_active_object(const bContext *C)
Definition: context.c:1353
Low-level operations for curves.
const struct Brush * BKE_paint_brush_for_read(const struct Paint *p)
#define BLI_assert_unreachable()
Definition: BLI_assert.h:93
A KD-tree for nearest neighbor search.
MINLINE float pow2f(float x)
#define BLI_SCOPED_DEFER(function_to_defer)
void DEG_id_tag_update(struct ID *id, int flag)
@ ID_RECALC_GEOMETRY
Definition: DNA_ID.h:791
eBrushFalloffShape
@ PAINT_FALLOFF_SHAPE_SPHERE
@ PAINT_FALLOFF_SHAPE_TUBE
struct CurvesGeometry CurvesGeometry
eCurvesSymmetryType
Object is a sort of wrapper for general info.
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])
Platform independent time functions.
#define C
Definition: RandGen.cpp:25
#define NC_GEOM
Definition: WM_types.h:343
#define ND_DATA
Definition: WM_types.h:456
IndexRange index_range() const
IndexRange index_range() const
Definition: BLI_vector.hh:920
MutableSpan< float3 > positions_for_write()
static CurvesGeometry & wrap(::CurvesGeometry &dna_struct)
Definition: BKE_curves.hh:138
void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override
GeometryDeformation get_evaluated_curves_deformation(const Depsgraph &depsgraph, const Object &ob_orig)
Definition: crazyspace.cc:595
std::unique_ptr< CurvesSculptStrokeOperation > new_comb_operation()
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)
VArray< float > get_point_selection(const Curves &curves_id)
static IndexMask retrieve_selected_curves(const CurvesGeometry &curves, const eAttrDomain domain, Vector< int64_t > &r_indices)
float brush_strength_get(const Scene &scene, const Brush &brush, const StrokeExtension &stroke_extension)
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
void parallel_for_each(Range &range, const Function &function)
Definition: BLI_task.hh:39
char falloff_shape
CurvesGeometry geometry
struct Object * surface
void * data
struct ToolSettings * toolsettings
CurvesSculpt * curves_sculpt
void comb_projected(EnumerableThreadSpecific< Vector< int >> &r_changed_curves, const float4x4 &brush_transform)
void comb_spherical(EnumerableThreadSpecific< Vector< int >> &r_changed_curves, const float3 &brush_start_cu, const float3 &brush_end_cu, const float brush_radius_cu)
void restore_segment_lengths(EnumerableThreadSpecific< Vector< int >> &changed_curves)
void comb_spherical_with_symmetry(EnumerableThreadSpecific< Vector< int >> &r_changed_curves)
void comb_projected_with_symmetry(EnumerableThreadSpecific< Vector< int >> &r_changed_curves)
void execute(CombOperation &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)