Blender  V3.3
curves_sculpt_slide.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_float3x3.hh"
8 #include "BLI_float4x4.hh"
9 #include "BLI_vector.hh"
10 
11 #include "PIL_time.h"
12 
13 #include "DEG_depsgraph.h"
14 
15 #include "BKE_attribute_math.hh"
16 #include "BKE_brush.h"
17 #include "BKE_bvhutils.h"
18 #include "BKE_context.h"
19 #include "BKE_curves.hh"
20 #include "BKE_geometry_set.hh"
21 #include "BKE_mesh.h"
22 #include "BKE_mesh_runtime.h"
23 #include "BKE_mesh_sample.hh"
24 #include "BKE_modifier.h"
25 #include "BKE_object.h"
26 #include "BKE_paint.h"
27 #include "BKE_report.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_screen_types.h"
35 #include "DNA_space_types.h"
36 
37 #include "ED_screen.h"
38 #include "ED_view3d.h"
39 
40 #include "UI_interface.h"
41 
42 #include "WM_api.h"
43 
44 #include "DEG_depsgraph_query.h"
45 
48 
49 #include "BLT_translation.h"
50 
51 namespace blender::ed::sculpt_paint {
52 
53 using geometry::ReverseUVSampler;
54 
57  int curve_i;
65 };
66 
67 struct SlideInfo {
71 };
72 
74  private:
75  float2 initial_brush_pos_re_;
77  Vector<SlideInfo> slide_info_;
79  Array<float3> initial_positions_cu_;
81  Array<float3> initial_deformed_positions_cu_;
82 
83  friend struct SlideOperationExecutor;
84 
85  public:
86  void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
87 };
88 
94  SlideOperation *self_ = nullptr;
96 
97  const CurvesSculpt *curves_sculpt_ = nullptr;
98  const Brush *brush_ = nullptr;
102 
106 
108  Mesh *surface_orig_ = nullptr;
112 
114  Mesh *surface_eval_ = nullptr;
118 
122 
124 
126 
127  std::atomic<bool> found_invalid_uv_mapping_{false};
128 
130  {
131  }
132 
133  void execute(SlideOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
134  {
135  UNUSED_VARS(C, stroke_extension);
136  self_ = &self;
137 
139  curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data);
141  if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) {
142  report_missing_surface(stroke_extension.reports);
143  return;
144  }
145  if (curves_orig_->curves_num() == 0) {
146  return;
147  }
148  if (curves_id_orig_->surface_uv_map == nullptr) {
150  return;
151  }
152  if (curves_orig_->surface_uv_coords().is_empty()) {
153  BKE_report(stroke_extension.reports,
154  RPT_WARNING,
155  TIP_("Curves do not have surface attachment information"));
156  return;
157  }
158  const StringRefNull uv_map_name = curves_id_orig_->surface_uv_map;
159 
163  brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension);
164  brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension);
165 
168 
169  brush_pos_re_ = stroke_extension.mouse_position;
170 
172 
174  surface_orig_ = static_cast<Mesh *>(surface_ob_orig_->data);
175  if (surface_orig_->totpoly == 0) {
176  report_empty_original_surface(stroke_extension.reports);
177  return;
178  }
182  bke::mesh_attributes(*surface_orig_).lookup<float2>(uv_map_name, ATTR_DOMAIN_CORNER);
183  if (surface_uv_map_orig_.is_empty()) {
185  return;
186  }
189  }
191  reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_orig_->ldata, CD_NORMAL)),
193 
195  if (surface_ob_eval_ == nullptr) {
196  return;
197  }
199  if (surface_eval_ == nullptr) {
200  return;
201  }
202  if (surface_eval_->totpoly == 0) {
203  report_empty_evaluated_surface(stroke_extension.reports);
204  return;
205  }
209  bke::mesh_attributes(*surface_eval_).lookup<float2>(uv_map_name, ATTR_DOMAIN_CORNER);
210  if (surface_uv_map_eval_.is_empty()) {
212  return;
213  }
216 
217  if (stroke_extension.is_first) {
218  self_->initial_brush_pos_re_ = brush_pos_re_;
219  /* Remember original and deformed positions of all points. Otherwise this information is lost
220  * when sliding starts, but it's still used. */
221  const bke::crazyspace::GeometryDeformation deformation =
223  self_->initial_positions_cu_ = curves_orig_->positions();
224  self_->initial_deformed_positions_cu_ = deformation.positions;
225 
226  /* First find all curves to slide. When the mouse moves, only those curves will be moved. */
228  return;
229  }
230  this->slide_with_symmetry();
231 
233  BKE_report(
234  stroke_extension.reports, RPT_WARNING, TIP_("UV map or surface attachment is invalid"));
235  }
236 
241  }
242 
244  {
245  const Vector<float4x4> brush_transforms = get_symmetry_brush_transforms(
247  const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
248  const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph,
249  *ctx_.region,
250  *ctx_.v3d,
251  transforms_,
254  brush_radius_re);
255  if (!brush_3d.has_value()) {
256  return;
257  }
258  const ReverseUVSampler reverse_uv_sampler_orig{surface_uv_map_orig_, surface_looptris_orig_};
259  for (const float4x4 &brush_transform : brush_transforms) {
260  self_->slide_info_.append_as();
261  SlideInfo &slide_info = self_->slide_info_.last();
262  slide_info.brush_transform = brush_transform;
263  this->find_curves_to_slide(brush_transform * brush_3d->position_cu,
264  brush_3d->radius_cu,
265  reverse_uv_sampler_orig,
266  slide_info.curves_to_slide);
267  }
268  }
269 
270  void find_curves_to_slide(const float3 &brush_pos_cu,
271  const float brush_radius_cu,
272  const ReverseUVSampler &reverse_uv_sampler_orig,
273  Vector<SlideCurveInfo> &r_curves_to_slide)
274  {
275  const Span<float2> surface_uv_coords = curves_orig_->surface_uv_coords();
276  const float brush_radius_sq_cu = pow2f(brush_radius_cu);
277 
278  const Span<int> offsets = curves_orig_->offsets();
279  for (const int curve_i : curve_selection_) {
280  const int first_point_i = offsets[curve_i];
281  const float3 old_pos_cu = self_->initial_deformed_positions_cu_[first_point_i];
282  const float dist_to_brush_sq_cu = math::distance_squared(old_pos_cu, brush_pos_cu);
283  if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
284  /* Root point is too far away from curve center. */
285  continue;
286  }
287  const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu);
288  const float radius_falloff = BKE_brush_curve_strength(
289  brush_, dist_to_brush_cu, brush_radius_cu);
290 
291  const float2 uv = surface_uv_coords[curve_i];
292  ReverseUVSampler::Result result = reverse_uv_sampler_orig.sample(uv);
294  /* The curve does not have a valid surface attachment. */
295  found_invalid_uv_mapping_.store(true);
296  continue;
297  }
298  /* Compute the normal at the initial surface position. */
299  const float3 normal_cu = math::normalize(
302  *result.looptri, result.bary_weights, corner_normals_orig_su_));
303 
304  r_curves_to_slide.append({curve_i, radius_falloff, normal_cu});
305  }
306  }
307 
309  {
310  const ReverseUVSampler reverse_uv_sampler_orig{surface_uv_map_orig_, surface_looptris_orig_};
311  for (const SlideInfo &slide_info : self_->slide_info_) {
312  this->slide(slide_info.curves_to_slide, reverse_uv_sampler_orig, slide_info.brush_transform);
313  }
314  }
315 
316  void slide(const Span<SlideCurveInfo> slide_curves,
317  const ReverseUVSampler &reverse_uv_sampler_orig,
318  const float4x4 &brush_transform)
319  {
320  const float4x4 brush_transform_inv = brush_transform.inverted();
321 
322  const Span<MVert> verts_orig_su{surface_orig_->mvert, surface_orig_->totvert};
323  const Span<MLoop> loops_orig{surface_orig_->mloop, surface_orig_->totloop};
324 
327 
328  float4x4 projection;
330 
331  const float2 brush_pos_diff_re = brush_pos_re_ - self_->initial_brush_pos_re_;
332 
333  /* The brush transformation has to be applied in curves space. */
334  const float4x4 world_to_surface_with_symmetry_mat = transforms_.curves_to_surface *
335  brush_transform *
337 
338  threading::parallel_for(slide_curves.index_range(), 256, [&](const IndexRange range) {
339  for (const SlideCurveInfo &slide_curve_info : slide_curves.slice(range)) {
340  const int curve_i = slide_curve_info.curve_i;
341  const IndexRange points = curves_orig_->points_for_curve(curve_i);
342  const int first_point_i = points[0];
343 
344  const float3 old_first_pos_eval_cu = self_->initial_deformed_positions_cu_[first_point_i];
345  const float3 old_first_symm_pos_eval_cu = brush_transform_inv * old_first_pos_eval_cu;
346  const float3 old_first_pos_eval_su = transforms_.curves_to_surface * old_first_pos_eval_cu;
347 
348  float2 old_first_symm_pos_eval_re;
349  ED_view3d_project_float_v2_m4(ctx_.region,
350  old_first_symm_pos_eval_cu,
351  old_first_symm_pos_eval_re,
352  projection.values);
353 
354  const float radius_falloff = slide_curve_info.radius_falloff;
355  const float curve_weight = brush_strength_ * radius_falloff * curve_factors_[curve_i];
356  const float2 new_first_symm_pos_eval_re = old_first_symm_pos_eval_re +
357  curve_weight * brush_pos_diff_re;
358 
359  /* Compute the ray that will be used to find the new position on the surface. */
360  float3 ray_start_wo, ray_end_wo;
361  ED_view3d_win_to_segment_clipped(ctx_.depsgraph,
362  ctx_.region,
363  ctx_.v3d,
364  new_first_symm_pos_eval_re,
365  ray_start_wo,
366  ray_end_wo,
367  true);
368  const float3 ray_start_su = world_to_surface_with_symmetry_mat * ray_start_wo;
369  const float3 ray_end_su = world_to_surface_with_symmetry_mat * ray_end_wo;
370  const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su);
371 
372  /* Find the ray hit that is closest to the initial curve root position. */
373  int looptri_index_eval;
374  float3 hit_pos_eval_su;
375  if (!this->find_closest_ray_hit(ray_start_su,
376  ray_direction_su,
377  old_first_pos_eval_su,
378  looptri_index_eval,
379  hit_pos_eval_su)) {
380  continue;
381  }
382 
383  /* Compute the uv of the new surface position on the evaluated mesh. */
384  const MLoopTri &looptri_eval = surface_looptris_eval_[looptri_index_eval];
385  const float3 bary_weights_eval = bke::mesh_surface_sample::compute_bary_coord_in_triangle(
386  *surface_eval_, looptri_eval, hit_pos_eval_su);
387  const float2 uv = attribute_math::mix3(bary_weights_eval,
388  surface_uv_map_eval_[looptri_eval.tri[0]],
389  surface_uv_map_eval_[looptri_eval.tri[1]],
390  surface_uv_map_eval_[looptri_eval.tri[2]]);
391 
392  /* Try to find the same uv on the original surface. */
393  const ReverseUVSampler::Result result = reverse_uv_sampler_orig.sample(uv);
394  if (result.type != ReverseUVSampler::ResultType::Ok) {
395  found_invalid_uv_mapping_.store(true);
396  continue;
397  }
398  const MLoopTri &looptri_orig = *result.looptri;
399  const float3 &bary_weights_orig = result.bary_weights;
400 
401  /* Gather old and new surface normal. */
402  const float3 &initial_normal_cu = slide_curve_info.initial_normal_cu;
403  const float3 new_normal_cu = math::normalize(
404  transforms_.surface_to_curves_normal *
405  geometry::compute_surface_point_normal(
406  *result.looptri, result.bary_weights, corner_normals_orig_su_));
407 
408  /* Gather old and new surface position. */
409  const float3 old_first_pos_orig_cu = self_->initial_positions_cu_[first_point_i];
410  const float3 new_first_pos_orig_cu =
411  transforms_.surface_to_curves *
412  attribute_math::mix3<float3>(bary_weights_orig,
413  verts_orig_su[loops_orig[looptri_orig.tri[0]].v].co,
414  verts_orig_su[loops_orig[looptri_orig.tri[1]].v].co,
415  verts_orig_su[loops_orig[looptri_orig.tri[2]].v].co);
416 
417  /* Actually transform curve points. */
418  const float4x4 slide_transform = this->get_slide_transform(
419  old_first_pos_orig_cu, new_first_pos_orig_cu, initial_normal_cu, new_normal_cu);
420  for (const int point_i : points) {
421  positions_orig_cu[point_i] = slide_transform * self_->initial_positions_cu_[point_i];
422  }
423  surface_uv_coords[curve_i] = uv;
424  }
425  });
426  }
427 
428  bool find_closest_ray_hit(const float3 &ray_start_su,
429  const float3 &ray_direction_su,
430  const float3 &point_su,
431  int &r_looptri_index,
432  float3 &r_hit_pos)
433  {
434  float best_dist_sq_su = FLT_MAX;
435  int best_looptri_index_eval;
436  float3 best_hit_pos_su;
437  BLI_bvhtree_ray_cast_all_cpp(
438  *surface_bvh_eval_.tree,
439  ray_start_su,
440  ray_direction_su,
441  0.0f,
442  FLT_MAX,
443  [&](const int looptri_index, const BVHTreeRay &ray, BVHTreeRayHit &hit) {
444  surface_bvh_eval_.raycast_callback(&surface_bvh_eval_, looptri_index, &ray, &hit);
445  if (hit.index < 0) {
446  return;
447  }
448  const float3 &hit_pos_su = hit.co;
449  const float dist_sq_su = math::distance_squared(hit_pos_su, point_su);
450  if (dist_sq_su < best_dist_sq_su) {
451  best_dist_sq_su = dist_sq_su;
452  best_hit_pos_su = hit_pos_su;
453  best_looptri_index_eval = hit.index;
454  }
455  });
456 
457  if (best_dist_sq_su == FLT_MAX) {
458  return false;
459  }
460  r_looptri_index = best_looptri_index_eval;
461  r_hit_pos = best_hit_pos_su;
462  return true;
463  }
464 
465  float4x4 get_slide_transform(const float3 &old_root_pos,
466  const float3 &new_root_pos,
467  const float3 &old_normal,
468  const float3 &new_normal)
469  {
470  float3x3 rotation_3x3;
471  rotation_between_vecs_to_mat3(rotation_3x3.values, old_normal, new_normal);
472  float4x4 rotation_4x4;
473  copy_m4_m3(rotation_4x4.values, rotation_3x3.values);
474 
475  float4x4 transform = float4x4::identity();
476  sub_v3_v3(transform.values[3], old_root_pos);
477  mul_m4_m4_pre(transform.values, rotation_4x4.values);
478  add_v3_v3(transform.values[3], new_root_pos);
479  return transform;
480  }
481 };
482 
483 void SlideOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension)
484 {
485  SlideOperationExecutor executor{C};
486  executor.execute(*this, C, stroke_extension);
487 }
488 
489 std::unique_ptr<CurvesSculptStrokeOperation> new_slide_operation()
490 {
491  return std::make_unique<SlideOperation>();
492 }
493 
494 } // namespace blender::ed::sculpt_paint
@ ATTR_DOMAIN_CORNER
Definition: BKE_attribute.h:30
float BKE_brush_curve_strength(const struct Brush *br, float p, float len)
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
Low-level operations for curves.
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)
General operations, lookup, etc. for blender objects.
struct Mesh * BKE_object_get_evaluated_mesh(const struct Object *object)
const struct Brush * BKE_paint_brush_for_read(const struct Paint *p)
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition: report.c:83
sqrt(x)+1/max(0
MINLINE float pow2f(float x)
void mul_m4_m4_pre(float R[4][4], const float A[4][4])
Definition: math_matrix.c:372
void copy_m4_m3(float m1[4][4], const float m2[3][3])
Definition: math_matrix.c:102
void rotation_between_vecs_to_mat3(float m[3][3], const float v1[3], const float v2[3])
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
#define BLI_SCOPED_DEFER(function_to_defer)
#define UNUSED_VARS(...)
#define TIP_(msgid)
void DEG_id_tag_update(struct ID *id, int flag)
struct Object * DEG_get_evaluated_object(const struct Depsgraph *depsgraph, struct Object *object)
@ ID_RECALC_GEOMETRY
Definition: DNA_ID.h:791
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])
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
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
constexpr IndexRange index_range() const
Definition: BLI_span.hh:401
void append(const T &value)
Definition: BLI_vector.hh:433
GAttributeReader lookup(const AttributeIDRef &attribute_id) const
MutableSpan< float3 > positions_for_write()
MutableSpan< float2 > surface_uv_coords_for_write()
static CurvesGeometry & wrap(::CurvesGeometry &dna_struct)
Definition: BKE_curves.hh:138
Span< float3 > positions() const
Span< float2 > surface_uv_coords() const
void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override
Result sample(const float2 &query_uv) const
GeometryDeformation get_evaluated_curves_deformation(const Depsgraph &depsgraph, const Object &ob_orig)
Definition: crazyspace.cc:595
AttributeAccessor mesh_attributes(const Mesh &mesh)
std::optional< CurvesBrush3D > sample_curves_surface_3d_brush(const Depsgraph &depsgraph, const ARegion &region, const View3D &v3d, const CurvesSurfaceTransforms &transforms, const BVHTreeFromMesh &surface_bvh, const float2 &brush_pos_re, const float brush_radius_re)
void report_empty_evaluated_surface(ReportList *reports)
VArray< float > get_curves_selection(const Curves &curves_id)
void report_missing_uv_map_on_original_surface(ReportList *reports)
void report_missing_uv_map_on_evaluated_surface(ReportList *reports)
void report_missing_surface(ReportList *reports)
void report_empty_original_surface(ReportList *reports)
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_slide_operation()
float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension)
float3 compute_surface_point_normal(const MLoopTri &looptri, const float3 &bary_coord, const Span< float3 > corner_normals)
T distance_squared(const vec_base< T, Size > &a, const vec_base< T, Size > &b)
vec_base< T, Size > normalize(const vec_base< T, Size > &v)
void parallel_for(IndexRange range, int64_t grain_size, const Function &function)
Definition: BLI_task.hh:51
float co[3]
Definition: BLI_kdopbvh.h:68
CurvesGeometry geometry
struct Object * surface
char * surface_uv_map
struct MVert * mvert
int totvert
struct MLoop * mloop
int totpoly
int totloop
CustomData ldata
void * data
struct ToolSettings * toolsettings
CurvesSculpt * curves_sculpt
Vector< SlideCurveInfo > curves_to_slide
void execute(SlideOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
void find_curves_to_slide(const float3 &brush_pos_cu, const float brush_radius_cu, const ReverseUVSampler &reverse_uv_sampler_orig, Vector< SlideCurveInfo > &r_curves_to_slide)
void slide(const Span< SlideCurveInfo > slide_curves, const ReverseUVSampler &reverse_uv_sampler_orig, const float4x4 &brush_transform)
bool find_closest_ray_hit(const float3 &ray_start_su, const float3 &ray_direction_su, const float3 &point_su, int &r_looptri_index, float3 &r_hit_pos)
float4x4 get_slide_transform(const float3 &old_root_pos, const float3 &new_root_pos, const float3 &old_normal, const float3 &new_normal)
float values[3][3]
Definition: BLI_float3x3.hh:18
float values[4][4]
Definition: BLI_float4x4.hh:13
float4x4 inverted() const
void WM_main_add_notifier(unsigned int type, void *reference)