Blender  V3.3
curves_sculpt_pinch.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_vector.hh"
9 
10 #include "PIL_time.h"
11 
12 #include "DEG_depsgraph.h"
13 
14 #include "BKE_brush.h"
15 #include "BKE_context.h"
16 #include "BKE_curves.hh"
17 #include "BKE_paint.h"
18 
19 #include "DNA_brush_enums.h"
20 #include "DNA_brush_types.h"
21 #include "DNA_curves_types.h"
22 #include "DNA_object_types.h"
23 #include "DNA_screen_types.h"
24 #include "DNA_space_types.h"
25 
26 #include "ED_screen.h"
27 #include "ED_view3d.h"
28 
29 #include "WM_api.h"
30 
39 namespace blender::ed::sculpt_paint {
40 
42  private:
43  bool invert_pinch_;
44  Array<float> segment_lengths_cu_;
45 
47  CurvesBrush3D brush_3d_;
48 
49  friend struct PinchOperationExecutor;
50 
51  public:
52  PinchOperation(const bool invert_pinch) : invert_pinch_(invert_pinch)
53  {
54  }
55 
56  void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
57 };
58 
60  PinchOperation *self_ = nullptr;
62 
63  Object *object_ = nullptr;
64  Curves *curves_id_ = nullptr;
65  CurvesGeometry *curves_ = nullptr;
66 
70 
72 
73  const CurvesSculpt *curves_sculpt_ = nullptr;
74  const Brush *brush_ = nullptr;
78 
80 
82 
84  {
85  }
86 
87  void execute(PinchOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
88  {
89  self_ = &self;
90 
92  curves_id_ = static_cast<Curves *>(object_->data);
94  if (curves_->curves_num() == 0) {
95  return;
96  }
97 
101  brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension);
103 
104  invert_factor_ = self_->invert_pinch_ ? -1.0f : 1.0f;
105 
107 
110 
111  brush_pos_re_ = stroke_extension.mouse_position;
112  const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>(
114 
115  if (stroke_extension.is_first) {
117 
118  if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
120  *ctx_.region,
121  *ctx_.v3d,
122  *ctx_.rv3d,
123  *object_,
126  }
127  }
128 
129  Array<bool> changed_curves(curves_->curves_num(), false);
130  if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
131  this->pinch_projected_with_symmetry(changed_curves);
132  }
133  else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
134  this->pinch_spherical_with_symmetry(changed_curves);
135  }
136  else {
138  }
139 
140  this->restore_segment_lengths(changed_curves);
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->pinch_projected(brush_transform, r_changed_curves);
153  }
154  }
155 
156  void pinch_projected(const float4x4 &brush_transform, MutableSpan<bool> r_changed_curves)
157  {
158  const float4x4 brush_transform_inv = brush_transform.inverted();
159 
160  const bke::crazyspace::GeometryDeformation deformation =
162 
163  float4x4 projection;
166  const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
167  const float brush_radius_sq_re = pow2f(brush_radius_re);
168 
170  for (const int curve_i : curve_selection_.slice(range)) {
171  const IndexRange points = curves_->points_for_curve(curve_i);
172  for (const int point_i : points.drop_front(1)) {
173  const float3 old_pos_cu = deformation.positions[point_i];
174  const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu;
175  float2 old_symm_pos_re;
176  ED_view3d_project_float_v2_m4(
177  ctx_.region, old_symm_pos_cu, old_symm_pos_re, projection.values);
178 
179  const float dist_to_brush_sq_re = math::distance_squared(old_symm_pos_re, brush_pos_re_);
180  if (dist_to_brush_sq_re > brush_radius_sq_re) {
181  continue;
182  }
183 
184  const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re);
185  const float t = safe_divide(dist_to_brush_re, brush_radius_base_re_);
186  const float radius_falloff = t * BKE_brush_curve_strength(brush_, t, 1.0f);
187  const float weight = invert_factor_ * 0.1f * brush_strength_ * radius_falloff *
188  point_factors_[point_i];
189 
190  const float2 new_symm_pos_re = math::interpolate(old_symm_pos_re, brush_pos_re_, weight);
191 
192  float3 new_symm_pos_wo;
193  ED_view3d_win_to_3d(ctx_.v3d,
194  ctx_.region,
195  transforms_.curves_to_world * old_symm_pos_cu,
196  new_symm_pos_re,
197  new_symm_pos_wo);
198 
199  const float3 new_pos_cu = brush_transform * transforms_.world_to_curves *
200  new_symm_pos_wo;
201  const float3 translation_eval = new_pos_cu - old_pos_cu;
202  const float3 translation_orig = deformation.translation_from_deformed_to_original(
203  point_i, translation_eval);
204  positions_cu[point_i] += translation_orig;
205  r_changed_curves[curve_i] = true;
206  }
207  }
208  });
209  }
210 
212  {
213  float3 brush_pos_wo;
214  ED_view3d_win_to_3d(ctx_.v3d,
215  ctx_.region,
216  transforms_.curves_to_world * self_->brush_3d_.position_cu,
217  brush_pos_re_,
218  brush_pos_wo);
219  const float3 brush_pos_cu = transforms_.world_to_curves * brush_pos_wo;
220  const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_;
221 
222  const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
223  eCurvesSymmetryType(curves_id_->symmetry));
224  for (const float4x4 &brush_transform : symmetry_brush_transforms) {
225  this->pinch_spherical(brush_transform * brush_pos_cu, brush_radius_cu, r_changed_curves);
226  }
227  }
228 
229  void pinch_spherical(const float3 &brush_pos_cu,
230  const float brush_radius_cu,
231  MutableSpan<bool> r_changed_curves)
232  {
233  MutableSpan<float3> positions_cu = curves_->positions_for_write();
234  const float brush_radius_sq_cu = pow2f(brush_radius_cu);
235 
236  const bke::crazyspace::GeometryDeformation deformation =
237  bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
238 
239  threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
240  for (const int curve_i : curve_selection_.slice(range)) {
241  const IndexRange points = curves_->points_for_curve(curve_i);
242  for (const int point_i : points.drop_front(1)) {
243  const float3 old_pos_cu = deformation.positions[point_i];
244 
245  const float dist_to_brush_sq_cu = math::distance_squared(old_pos_cu, brush_pos_cu);
246  if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
247  continue;
248  }
249 
250  const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu);
251  const float t = safe_divide(dist_to_brush_cu, brush_radius_cu);
252  const float radius_falloff = t * BKE_brush_curve_strength(brush_, t, 1.0f);
253  const float weight = invert_factor_ * 0.1f * brush_strength_ * radius_falloff *
254  point_factors_[point_i];
255 
256  const float3 new_pos_cu = math::interpolate(old_pos_cu, brush_pos_cu, weight);
257  const float3 translation_eval = new_pos_cu - old_pos_cu;
258  const float3 translation_orig = deformation.translation_from_deformed_to_original(
259  point_i, translation_eval);
260  positions_cu[point_i] += translation_orig;
261 
262  r_changed_curves[curve_i] = true;
263  }
264  }
265  });
266  }
267 
269  {
270  const Span<float3> positions_cu = curves_->positions();
271  self_->segment_lengths_cu_.reinitialize(curves_->points_num());
272  threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
273  for (const int curve_i : curve_selection_.slice(range)) {
274  const IndexRange points = curves_->points_for_curve(curve_i);
275  for (const int point_i : points.drop_back(1)) {
276  const float3 &p1_cu = positions_cu[point_i];
277  const float3 &p2_cu = positions_cu[point_i + 1];
278  const float length_cu = math::distance(p1_cu, p2_cu);
279  self_->segment_lengths_cu_[point_i] = length_cu;
280  }
281  }
282  });
283  }
284 
285  void restore_segment_lengths(const Span<bool> changed_curves)
286  {
287  const Span<float> expected_lengths_cu = self_->segment_lengths_cu_;
288  MutableSpan<float3> positions_cu = curves_->positions_for_write();
289 
290  threading::parallel_for(changed_curves.index_range(), 256, [&](const IndexRange range) {
291  for (const int curve_i : range) {
292  if (!changed_curves[curve_i]) {
293  continue;
294  }
295  const IndexRange points = curves_->points_for_curve(curve_i);
296  for (const int segment_i : IndexRange(points.size() - 1)) {
297  const float3 &p1_cu = positions_cu[points[segment_i]];
298  float3 &p2_cu = positions_cu[points[segment_i] + 1];
299  const float3 direction = math::normalize(p2_cu - p1_cu);
300  const float expected_length_cu = expected_lengths_cu[points[segment_i]];
301  p2_cu = p1_cu + direction * expected_length_cu;
302  }
303  }
304  });
305  }
306 };
307 
308 void PinchOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension)
309 {
310  PinchOperationExecutor executor{C};
311  executor.execute(*this, C, stroke_extension);
312 }
313 
314 std::unique_ptr<CurvesSculptStrokeOperation> new_pinch_operation(const BrushStrokeMode brush_mode,
315  const bContext &C)
316 {
317  const Scene &scene = *CTX_data_scene(&C);
319 
320  const bool invert_pinch = (brush_mode == BRUSH_STROKE_INVERT) !=
321  ((brush.flag & BRUSH_DIR_IN) != 0);
322  return std::make_unique<PinchOperation>(invert_pinch);
323 }
324 
325 } // namespace blender::ed::sculpt_paint
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.
const struct Brush * BKE_paint_brush_for_read(const struct Paint *p)
#define BLI_assert_unreachable()
Definition: BLI_assert.h:93
MINLINE float pow2f(float x)
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
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
constexpr IndexRange index_range() const
Definition: BLI_span.hh:401
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
Scene scene
GeometryDeformation get_evaluated_curves_deformation(const Depsgraph &depsgraph, const Object &ob_orig)
Definition: crazyspace.cc:595
std::unique_ptr< CurvesSculptStrokeOperation > new_pinch_operation(const BrushStrokeMode brush_mode, const bContext &C)
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)
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_INVERT
Definition: paint_intern.h:450
char falloff_shape
CurvesGeometry geometry
struct Object * surface
void * data
struct ToolSettings * toolsettings
CurvesSculpt * curves_sculpt
void execute(PinchOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
void pinch_spherical_with_symmetry(MutableSpan< bool > r_changed_curves)
void pinch_projected(const float4x4 &brush_transform, MutableSpan< bool > r_changed_curves)
void restore_segment_lengths(const Span< bool > changed_curves)
void pinch_projected_with_symmetry(MutableSpan< bool > r_changed_curves)
void pinch_spherical(const float3 &brush_pos_cu, const float brush_radius_cu, MutableSpan< bool > r_changed_curves)
float values[4][4]
Definition: BLI_float4x4.hh:13
float4x4 inverted() const
void WM_main_add_notifier(unsigned int type, void *reference)