Blender  V3.3
curves_sculpt_puff.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include "BKE_attribute_math.hh"
4 #include "BKE_brush.h"
5 #include "BKE_bvhutils.h"
6 #include "BKE_context.h"
7 #include "BKE_crazyspace.hh"
8 #include "BKE_mesh.h"
9 #include "BKE_mesh_runtime.h"
10 
11 #include "ED_screen.h"
12 #include "ED_view3d.h"
13 
14 #include "DEG_depsgraph.h"
15 
16 #include "DNA_brush_types.h"
17 #include "DNA_mesh_types.h"
18 #include "DNA_meshdata_types.h"
19 
20 #include "WM_api.h"
21 
23 
25 
26 #include "curves_sculpt_intern.hh"
27 
28 namespace blender::ed::sculpt_paint {
29 
31  private:
33  CurvesBrush3D brush_3d_;
34 
36  Array<float> segment_lengths_cu_;
37 
38  friend struct PuffOperationExecutor;
39 
40  public:
41  void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
42 };
43 
49  PuffOperation *self_ = nullptr;
51 
52  Object *object_ = nullptr;
53  Curves *curves_id_ = nullptr;
54  CurvesGeometry *curves_ = nullptr;
55 
59 
60  const CurvesSculpt *curves_sculpt_ = nullptr;
61  const Brush *brush_ = nullptr;
66 
68 
70 
71  Object *surface_ob_ = nullptr;
72  Mesh *surface_ = nullptr;
76 
78  {
79  }
80 
81  void execute(PuffOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
82  {
83  UNUSED_VARS(C, stroke_extension);
84  self_ = &self;
85 
87  curves_id_ = static_cast<Curves *>(object_->data);
89  if (curves_->curves_num() == 0) {
90  return;
91  }
92  if (curves_id_->surface == nullptr || curves_id_->surface->type != OB_MESH) {
93  return;
94  }
95 
99  brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension);
100  brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension);
101  brush_pos_re_ = stroke_extension.mouse_position;
102 
105 
107 
109  surface_ = static_cast<Mesh *>(surface_ob_->data);
110 
112 
115  }
117  reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)),
118  surface_->totloop};
119 
122 
125 
126  if (stroke_extension.is_first) {
128  if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) {
129  self.brush_3d_ = *sample_curves_3d_brush(*ctx_.depsgraph,
130  *ctx_.region,
131  *ctx_.v3d,
132  *ctx_.rv3d,
133  *object_,
136  }
137  }
138 
139  Array<float> curve_weights(curve_selection_.size(), 0.0f);
140 
142  this->find_curve_weights_projected_with_symmetry(curve_weights);
143  }
145  this->find_curves_weights_spherical_with_symmetry(curve_weights);
146  }
147  else {
149  }
150 
151  this->puff(curve_weights);
152  this->restore_segment_lengths();
153 
154  curves_->tag_positions_changed();
158  }
159 
161  {
162  const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
164  for (const float4x4 &brush_transform : symmetry_brush_transforms) {
165  this->find_curve_weights_projected(brush_transform, r_curve_weights);
166  }
167  }
168 
169  void find_curve_weights_projected(const float4x4 &brush_transform,
170  MutableSpan<float> r_curve_weights)
171  {
172  const float4x4 brush_transform_inv = brush_transform.inverted();
173 
174  float4x4 projection;
176 
177  const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
178  const float brush_radius_sq_re = pow2f(brush_radius_re);
179 
180  const bke::crazyspace::GeometryDeformation deformation =
182 
184  for (const int curve_selection_i : range) {
185  const int curve_i = curve_selection_[curve_selection_i];
186  const IndexRange points = curves_->points_for_curve(curve_i);
187  const float3 first_pos_cu = brush_transform_inv * deformation.positions[points[0]];
188  float2 prev_pos_re;
189  ED_view3d_project_float_v2_m4(ctx_.region, first_pos_cu, prev_pos_re, projection.values);
190  for (const int point_i : points.drop_front(1)) {
191  const float3 pos_cu = brush_transform_inv * deformation.positions[point_i];
192  float2 pos_re;
193  ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values);
194  BLI_SCOPED_DEFER([&]() { prev_pos_re = pos_re; });
195 
196  const float dist_to_brush_sq_re = dist_squared_to_line_segment_v2(
197  brush_pos_re_, prev_pos_re, pos_re);
198  if (dist_to_brush_sq_re > brush_radius_sq_re) {
199  continue;
200  }
201 
202  const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re);
203  const float radius_falloff = BKE_brush_curve_strength(
204  brush_, dist_to_brush_re, brush_radius_re);
205  const float weight = radius_falloff;
206  math::max_inplace(r_curve_weights[curve_selection_i], weight);
207  }
208  }
209  });
210  }
211 
213  {
214  float4x4 projection;
215  ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
216 
217  float3 brush_pos_wo;
218  ED_view3d_win_to_3d(ctx_.v3d,
219  ctx_.region,
220  transforms_.curves_to_world * self_->brush_3d_.position_cu,
221  brush_pos_re_,
222  brush_pos_wo);
223  const float3 brush_pos_cu = transforms_.world_to_curves * brush_pos_wo;
224  const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_;
225 
226  const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
227  eCurvesSymmetryType(curves_id_->symmetry));
228  for (const float4x4 &brush_transform : symmetry_brush_transforms) {
229  this->find_curves_weights_spherical(
230  brush_transform * brush_pos_cu, brush_radius_cu, r_curve_weights);
231  }
232  }
233 
234  void find_curves_weights_spherical(const float3 &brush_pos_cu,
235  const float brush_radius_cu,
236  MutableSpan<float> r_curve_weights)
237  {
238  const float brush_radius_sq_cu = pow2f(brush_radius_cu);
239 
240  const bke::crazyspace::GeometryDeformation deformation =
241  bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
242 
243  threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
244  for (const int curve_selection_i : range) {
245  const int curve_i = curve_selection_[curve_selection_i];
246  const IndexRange points = curves_->points_for_curve(curve_i);
247  for (const int point_i : points.drop_front(1)) {
248  const float3 &prev_pos_cu = deformation.positions[point_i - 1];
249  const float3 &pos_cu = deformation.positions[point_i];
250  const float dist_to_brush_sq_cu = dist_squared_to_line_segment_v3(
251  brush_pos_cu, prev_pos_cu, pos_cu);
252  if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
253  continue;
254  }
255 
256  const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu);
257  const float radius_falloff = BKE_brush_curve_strength(
258  brush_, dist_to_brush_cu, brush_radius_cu);
259  const float weight = radius_falloff;
260  math::max_inplace(r_curve_weights[curve_selection_i], weight);
261  }
262  }
263  });
264  }
265 
266  void puff(const Span<float> curve_weights)
267  {
268  BLI_assert(curve_weights.size() == curve_selection_.size());
269  MutableSpan<float3> positions_cu = curves_->positions_for_write();
270 
271  threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
272  Vector<float> accumulated_lengths_cu;
273  for (const int curve_selection_i : range) {
274  const int curve_i = curve_selection_[curve_selection_i];
275  const IndexRange points = curves_->points_for_curve(curve_i);
276  const int first_point_i = points[0];
277  const float3 first_pos_cu = positions_cu[first_point_i];
278  const float3 first_pos_su = transforms_.curves_to_surface * first_pos_cu;
279 
280  /* Find the nearest position on the surface. The curve will be aligned to the normal of
281  * that point. */
282  BVHTreeNearest nearest;
283  nearest.dist_sq = FLT_MAX;
284  BLI_bvhtree_find_nearest(surface_bvh_.tree,
285  first_pos_su,
286  &nearest,
287  surface_bvh_.nearest_callback,
288  &surface_bvh_);
289 
290  const MLoopTri &looptri = surface_looptris_[nearest.index];
291  const float3 closest_pos_su = nearest.co;
292  const float3 &v0_su = surface_->mvert[surface_->mloop[looptri.tri[0]].v].co;
293  const float3 &v1_su = surface_->mvert[surface_->mloop[looptri.tri[1]].v].co;
294  const float3 &v2_su = surface_->mvert[surface_->mloop[looptri.tri[2]].v].co;
295  float3 bary_coords;
296  interp_weights_tri_v3(bary_coords, v0_su, v1_su, v2_su, closest_pos_su);
297  const float3 normal_su = geometry::compute_surface_point_normal(
298  looptri, bary_coords, corner_normals_su_);
299  const float3 normal_cu = math::normalize(transforms_.surface_to_curves_normal * normal_su);
300 
301  accumulated_lengths_cu.reinitialize(points.size() - 1);
302  length_parameterize::accumulate_lengths<float3>(
303  positions_cu.slice(points), false, accumulated_lengths_cu);
304 
305  /* Align curve to the surface normal while making sure that the curve does not fold up much
306  * in the process (e.g. when the curve was pointing in the opposite direction before). */
307  for (const int i : IndexRange(points.size()).drop_front(1)) {
308  const int point_i = points[i];
309  const float3 old_pos_cu = positions_cu[point_i];
310 
311  /* Compute final position of the point. */
312  const float length_param_cu = accumulated_lengths_cu[i - 1];
313  const float3 goal_pos_cu = first_pos_cu + length_param_cu * normal_cu;
314 
315  const float weight = 0.01f * brush_strength_ * point_factors_[point_i] *
316  curve_weights[curve_selection_i];
317  float3 new_pos_cu = math::interpolate(old_pos_cu, goal_pos_cu, weight);
318 
319  /* Make sure the point does not move closer to the root point than it was initially. This
320  * makes the curve kind of "rotate up". */
321  const float old_dist_to_root_cu = math::distance(old_pos_cu, first_pos_cu);
322  const float new_dist_to_root_cu = math::distance(new_pos_cu, first_pos_cu);
323  if (new_dist_to_root_cu < old_dist_to_root_cu) {
324  const float3 offset = math::normalize(new_pos_cu - first_pos_cu);
325  new_pos_cu += (old_dist_to_root_cu - new_dist_to_root_cu) * offset;
326  }
327 
328  positions_cu[point_i] = new_pos_cu;
329  }
330  }
331  });
332  }
333 
335  {
336  const Span<float3> positions_cu = curves_->positions();
337  self_->segment_lengths_cu_.reinitialize(curves_->points_num());
338  threading::parallel_for(curves_->curves_range(), 128, [&](const IndexRange range) {
339  for (const int curve_i : range) {
340  const IndexRange points = curves_->points_for_curve(curve_i);
341  for (const int point_i : points.drop_back(1)) {
342  const float3 &p1_cu = positions_cu[point_i];
343  const float3 &p2_cu = positions_cu[point_i + 1];
344  const float length_cu = math::distance(p1_cu, p2_cu);
345  self_->segment_lengths_cu_[point_i] = length_cu;
346  }
347  }
348  });
349  }
350 
352  {
353  const Span<float> expected_lengths_cu = self_->segment_lengths_cu_;
354  MutableSpan<float3> positions_cu = curves_->positions_for_write();
355 
356  threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange range) {
357  for (const int curve_i : range) {
358  const IndexRange points = curves_->points_for_curve(curve_i);
359  for (const int segment_i : points.drop_back(1)) {
360  const float3 &p1_cu = positions_cu[segment_i];
361  float3 &p2_cu = positions_cu[segment_i + 1];
362  const float3 direction = math::normalize(p2_cu - p1_cu);
363  const float expected_length_cu = expected_lengths_cu[segment_i];
364  p2_cu = p1_cu + direction * expected_length_cu;
365  }
366  }
367  });
368  }
369 };
370 
371 void PuffOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension)
372 {
373  PuffOperationExecutor executor{C};
374  executor.execute(*this, C, stroke_extension);
375 }
376 
377 std::unique_ptr<CurvesSculptStrokeOperation> new_puff_operation()
378 {
379  return std::make_unique<PuffOperation>();
380 }
381 
382 } // namespace blender::ed::sculpt_paint
int BKE_brush_size_get(const struct Scene *scene, const struct Brush *brush)
void free_bvhtree_from_mesh(struct BVHTreeFromMesh *data)
Definition: bvhutils.cc:1410
@ BVHTREE_FROM_LOOPTRI
Definition: BKE_bvhutils.h:73
BVHTree * BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data, const struct Mesh *mesh, BVHCacheType bvh_cache_type, int tree_type)
Definition: bvhutils.cc:1213
struct Object * CTX_data_active_object(const bContext *C)
Definition: context.c:1353
bool CustomData_has_layer(const struct CustomData *data, int type)
void * CustomData_get_layer(const struct CustomData *data, int type)
void BKE_mesh_calc_normals_split(struct Mesh *mesh)
Definition: mesh.cc:1911
const struct MLoopTri * BKE_mesh_runtime_looptri_ensure(const struct Mesh *mesh)
int BKE_mesh_runtime_looptri_len(const struct Mesh *mesh)
const struct Brush * BKE_paint_brush_for_read(const struct Paint *p)
#define BLI_assert_unreachable()
Definition: BLI_assert.h:93
#define BLI_assert(a)
Definition: BLI_assert.h:46
MINLINE float pow2f(float x)
#define BLI_SCOPED_DEFER(function_to_defer)
#define UNUSED_VARS(...)
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
eCurvesSymmetryType
@ OB_MESH
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
int64_t size() const
IndexRange index_range() const
constexpr int64_t size() const
Definition: BLI_span.hh:240
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::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)
std::unique_ptr< CurvesSculptStrokeOperation > new_puff_operation()
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
char falloff_shape
CurvesGeometry geometry
struct Object * surface
int totloop
CustomData ldata
void * data
struct ToolSettings * toolsettings
CurvesSculpt * curves_sculpt
void execute(PuffOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
void find_curve_weights_projected_with_symmetry(MutableSpan< float > r_curve_weights)
void find_curves_weights_spherical_with_symmetry(MutableSpan< float > r_curve_weights)
void find_curves_weights_spherical(const float3 &brush_pos_cu, const float brush_radius_cu, MutableSpan< float > r_curve_weights)
void find_curve_weights_projected(const float4x4 &brush_transform, MutableSpan< float > r_curve_weights)
void puff(const Span< float > curve_weights)
float values[4][4]
Definition: BLI_float4x4.hh:13
float4x4 inverted() const
void WM_main_add_notifier(unsigned int type, void *reference)