Blender V4.3
curves_sculpt_density.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <numeric>
6
8#include "BKE_brush.hh"
9#include "BKE_bvhutils.hh"
10#include "BKE_context.hh"
11#include "BKE_geometry_set.hh"
12#include "BKE_mesh.hh"
13#include "BKE_mesh_runtime.hh"
14#include "BKE_mesh_sample.hh"
15#include "BKE_modifier.hh"
16#include "BKE_object.hh"
17#include "BKE_paint.hh"
18#include "BKE_report.hh"
19
20#include "ED_screen.hh"
21#include "ED_view3d.hh"
22
23#include "DEG_depsgraph.hh"
25
26#include "BLI_array_utils.hh"
28#include "BLI_kdtree.h"
29#include "BLI_rand.hh"
30#include "BLI_task.hh"
31
33
34#include "DNA_brush_types.h"
35#include "DNA_mesh_types.h"
36
37#include "WM_api.hh"
38
40
42
44 private:
46 KDTree_3d *original_curve_roots_kdtree_ = nullptr;
48 KDTree_3d *deformed_curve_roots_kdtree_ = nullptr;
50 Vector<float3> new_deformed_root_positions_;
51 int original_curve_num_ = 0;
52
54
55 public:
57 {
58 if (original_curve_roots_kdtree_ != nullptr) {
59 BLI_kdtree_3d_free(original_curve_roots_kdtree_);
60 }
61 if (deformed_curve_roots_kdtree_ != nullptr) {
62 BLI_kdtree_3d_free(deformed_curve_roots_kdtree_);
63 }
64 }
65
66 void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
67};
68
72
76
78 const Mesh *surface_orig_ = nullptr;
79
81 Mesh *surface_eval_ = nullptr;
85
86 const CurvesSculpt *curves_sculpt_ = nullptr;
87 const Brush *brush_ = nullptr;
89
93
95
97
99 const bContext &C,
100 const StrokeExtension &stroke_extension)
101 {
102 self_ = &self;
104 curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data);
106
107 if (stroke_extension.is_first) {
108 self_->original_curve_num_ = curves_orig_->curves_num();
109 }
110
111 if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) {
112 report_missing_surface(stroke_extension.reports);
113 return;
114 }
115
117 surface_orig_ = static_cast<const Mesh *>(surface_ob_orig_->data);
118 if (surface_orig_->faces_num == 0) {
119 report_empty_original_surface(stroke_extension.reports);
120 return;
121 }
122
124 if (surface_ob_eval_ == nullptr) {
125 return;
126 }
128 if (surface_eval_->faces_num == 0) {
129 report_empty_evaluated_surface(stroke_extension.reports);
130 return;
131 }
132
136 /* Find UV map. */
137 VArraySpan<float2> surface_uv_map;
138 if (curves_id_orig_->surface_uv_map != nullptr) {
139 surface_uv_map = *surface_orig_->attributes().lookup<float2>(curves_id_orig_->surface_uv_map,
143 }
144 if (surface_uv_map.is_empty()) {
146 return;
147 }
148 if (surface_uv_map_eval_.is_empty()) {
150 return;
151 }
152
154
158 brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension);
159 brush_radius_re_ = brush_radius_get(*ctx_.scene, *brush_, stroke_extension);
160 brush_pos_re_ = stroke_extension.mouse_position;
161
163
164 Vector<float3> new_positions_cu;
165 Vector<float2> new_uvs;
167
168 /* Find potential new curve root points. */
169 if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
170 this->sample_projected_with_symmetry(rng, new_uvs, new_positions_cu);
171 }
172 else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
173 this->sample_spherical_with_symmetry(rng, new_uvs, new_positions_cu);
174 }
175 else {
177 }
178 for (float3 &pos : new_positions_cu) {
180 }
181
182 if (stroke_extension.is_first) {
184 }
185
186 const int already_added_curves = self_->new_deformed_root_positions_.size();
187 KDTree_3d *new_roots_kdtree = BLI_kdtree_3d_new(already_added_curves +
188 new_positions_cu.size());
189 BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(new_roots_kdtree); });
190
191 /* Used to tag all curves that are too close to existing curves or too close to other new
192 * curves. */
193 Array<bool> new_curve_skipped(new_positions_cu.size(), false);
195 512 < already_added_curves + new_positions_cu.size(),
196 /* Build kdtree from root points created by the current stroke. */
197 [&]() {
198 for (const int i : IndexRange(already_added_curves)) {
199 BLI_kdtree_3d_insert(new_roots_kdtree, -1, self_->new_deformed_root_positions_[i]);
200 }
201 for (const int new_i : new_positions_cu.index_range()) {
202 const float3 &root_pos_cu = new_positions_cu[new_i];
203 BLI_kdtree_3d_insert(new_roots_kdtree, new_i, root_pos_cu);
204 }
205 BLI_kdtree_3d_balance(new_roots_kdtree);
206 },
207 /* Check which new root points are close to roots that existed before the current stroke
208 * started. */
209 [&]() {
211 new_positions_cu.index_range(), 128, [&](const IndexRange range) {
212 for (const int new_i : range) {
213 const float3 &new_root_pos_cu = new_positions_cu[new_i];
214 KDTreeNearest_3d nearest;
215 nearest.dist = FLT_MAX;
216 BLI_kdtree_3d_find_nearest(
217 self_->deformed_curve_roots_kdtree_, new_root_pos_cu, &nearest);
218 if (nearest.dist < brush_settings_->minimum_distance) {
219 new_curve_skipped[new_i] = true;
220 }
221 }
222 });
223 });
224
225 /* Find new points that are too close too other new points. */
226 for (const int new_i : new_positions_cu.index_range()) {
227 if (new_curve_skipped[new_i]) {
228 continue;
229 }
230 const float3 &root_pos_cu = new_positions_cu[new_i];
231 BLI_kdtree_3d_range_search_cb_cpp(
232 new_roots_kdtree,
233 root_pos_cu,
234 brush_settings_->minimum_distance,
235 [&](const int other_new_i, const float * /*co*/, float /*dist_sq*/) {
236 if (other_new_i == -1) {
237 new_curve_skipped[new_i] = true;
238 return false;
239 }
240 if (new_i == other_new_i) {
241 return true;
242 }
243 new_curve_skipped[other_new_i] = true;
244 return true;
245 });
246 }
247
248 /* Remove points that are too close to others. */
249 for (int64_t i = new_positions_cu.size() - 1; i >= 0; i--) {
250 if (new_curve_skipped[i]) {
251 new_positions_cu.remove_and_reorder(i);
252 new_uvs.remove_and_reorder(i);
253 }
254 }
255 self_->new_deformed_root_positions_.extend(new_positions_cu);
256
257 const Span<float3> corner_normals_su = surface_orig_->corner_normals();
258 const Span<int3> surface_corner_tris_orig = surface_orig_->corner_tris();
259 const geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_corner_tris_orig};
260
261 geometry::AddCurvesOnMeshInputs add_inputs;
262 add_inputs.uvs = new_uvs;
263 add_inputs.interpolate_length = brush_settings_->flag &
265 add_inputs.interpolate_radius = brush_settings_->flag &
267 add_inputs.interpolate_shape = brush_settings_->flag &
269 add_inputs.interpolate_point_count = brush_settings_->flag &
271 add_inputs.interpolate_resolution = curves_orig_->attributes().contains("resolution");
272 add_inputs.fallback_curve_length = brush_settings_->curve_length;
273 add_inputs.fallback_curve_radius = brush_settings_->curve_radius;
274 add_inputs.fallback_point_count = std::max(2, brush_settings_->points_per_curve);
275 add_inputs.transforms = &transforms_;
276 add_inputs.surface = surface_orig_;
277 add_inputs.corner_normals_su = corner_normals_su;
278 add_inputs.surface_corner_tris = surface_corner_tris_orig;
279 add_inputs.reverse_uv_sampler = &reverse_uv_sampler;
280 add_inputs.old_roots_kdtree = self_->original_curve_roots_kdtree_;
281
282 const geometry::AddCurvesOnMeshOutputs add_outputs = geometry::add_curves_on_mesh(
283 *curves_orig_, add_inputs);
284 bke::MutableAttributeAccessor attributes = curves_orig_->attributes_for_write();
285 if (bke::GSpanAttributeWriter selection = attributes.lookup_for_write_span(".selection")) {
286 curves::fill_selection_true(selection.span.slice(selection.domain == bke::AttrDomain::Point ?
287 add_outputs.new_points_range :
288 add_outputs.new_curves_range));
289 selection.finish();
290 }
291
292 if (add_outputs.uv_error) {
293 report_invalid_uv_map(stroke_extension.reports);
294 }
295
296 DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY);
297 WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id);
299 }
300
302 {
303 const bke::crazyspace::GeometryDeformation deformation =
304 bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_);
305 const Span<int> curve_offsets = curves_orig_->offsets();
306 const Span<float3> original_positions = curves_orig_->positions();
307 const Span<float3> deformed_positions = deformation.positions;
308 BLI_assert(original_positions.size() == deformed_positions.size());
309
310 auto roots_kdtree_from_positions = [&](const Span<float3> positions) {
311 KDTree_3d *kdtree = BLI_kdtree_3d_new(curves_orig_->curves_num());
312 for (const int curve_i : curves_orig_->curves_range()) {
313 const int root_point_i = curve_offsets[curve_i];
314 BLI_kdtree_3d_insert(kdtree, curve_i, positions[root_point_i]);
315 }
316 BLI_kdtree_3d_balance(kdtree);
317 return kdtree;
318 };
319
320 threading::parallel_invoke(
321 1024 < original_positions.size() + deformed_positions.size(),
322 [&]() {
323 self_->original_curve_roots_kdtree_ = roots_kdtree_from_positions(original_positions);
324 },
325 [&]() {
326 self_->deformed_curve_roots_kdtree_ = roots_kdtree_from_positions(deformed_positions);
327 });
328 }
329
331 Vector<float2> &r_uvs,
332 Vector<float3> &r_positions_su)
333 {
334 const float4x4 projection = ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_);
335
336 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
337 eCurvesSymmetryType(curves_id_orig_->symmetry));
338 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
339 const float4x4 brush_transform_inv = math::invert(brush_transform);
340 const float4x4 transform = transforms_.curves_to_surface * brush_transform *
341 transforms_.world_to_curves;
342 Vector<float3> positions_su;
343 Vector<float3> bary_coords;
344 Vector<int> tri_indices;
345 const int new_points = bke::mesh_surface_sample::sample_surface_points_projected(
346 rng,
347 *surface_eval_,
348 surface_bvh_eval_,
349 brush_pos_re_,
350 brush_radius_re_,
351 [&](const float2 &pos_re, float3 &r_start_su, float3 &r_end_su) {
352 float3 start_wo, end_wo;
354 ctx_.depsgraph, ctx_.region, ctx_.v3d, pos_re, start_wo, end_wo, true);
355 r_start_su = math::transform_point(transform, start_wo);
356 r_end_su = math::transform_point(transform, end_wo);
357 },
358 true,
359 brush_settings_->density_add_attempts,
360 brush_settings_->density_add_attempts,
361 bary_coords,
362 tri_indices,
363 positions_su);
364
365 /* Remove some sampled points randomly based on the brush falloff and strength. */
366 for (int i = new_points - 1; i >= 0; i--) {
367 const float3 pos_su = positions_su[i];
368 const float3 pos_cu = math::transform_point(
369 brush_transform_inv, math::transform_point(transforms_.surface_to_curves, pos_su));
370 const float2 pos_re = ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, projection);
371 const float dist_to_brush_re = math::distance(brush_pos_re_, pos_re);
372 const float radius_falloff = BKE_brush_curve_strength(
373 brush_, dist_to_brush_re, brush_radius_re_);
374 const float weight = brush_strength_ * radius_falloff;
375 if (rng.get_float() > weight) {
376 bary_coords.remove_and_reorder(i);
377 tri_indices.remove_and_reorder(i);
378 positions_su.remove_and_reorder(i);
379 }
380 }
381
382 for (const int i : bary_coords.index_range()) {
383 const float2 uv = bke::mesh_surface_sample::sample_corner_attribute_with_bary_coords(
384 bary_coords[i], surface_corner_tris_eval_[tri_indices[i]], surface_uv_map_eval_);
385 r_uvs.append(uv);
386 }
387 r_positions_su.extend(positions_su);
388 }
389 }
390
392 Vector<float2> &r_uvs,
393 Vector<float3> &r_positions_su)
394 {
395 const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph,
396 *ctx_.region,
397 *ctx_.v3d,
398 transforms_,
399 surface_bvh_eval_,
400 brush_pos_re_,
401 brush_radius_re_);
402 if (!brush_3d.has_value()) {
403 return;
404 }
405
406 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
407 eCurvesSymmetryType(curves_id_orig_->symmetry));
408 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
409 const float3 brush_pos_cu = math::transform_point(brush_transform, brush_3d->position_cu);
410 const float3 brush_pos_su = math::transform_point(transforms_.curves_to_surface,
411 brush_pos_cu);
412 const float brush_radius_su = transform_brush_radius(
413 transforms_.curves_to_surface, brush_pos_cu, brush_3d->radius_cu);
414 const float brush_radius_sq_su = pow2f(brush_radius_su);
415
416 Vector<int> selected_corner_tri_indices;
417 BLI_bvhtree_range_query_cpp(
418 *surface_bvh_eval_.tree,
419 brush_pos_su,
420 brush_radius_su,
421 [&](const int index, const float3 & /*co*/, const float /*dist_sq*/) {
422 selected_corner_tri_indices.append(index);
423 });
424
425 const float brush_plane_area_su = M_PI * brush_radius_sq_su;
426 const float approximate_density_su = brush_settings_->density_add_attempts /
427 brush_plane_area_su;
428
429 Vector<float3> positions_su;
430 Vector<float3> bary_coords;
431 Vector<int> tri_indices;
432 const int new_points = bke::mesh_surface_sample::sample_surface_points_spherical(
433 rng,
434 *surface_eval_,
435 selected_corner_tri_indices,
436 brush_pos_su,
437 brush_radius_su,
438 approximate_density_su,
439 bary_coords,
440 tri_indices,
441 positions_su);
442
443 /* Remove some sampled points randomly based on the brush falloff and strength. */
444 for (int i = new_points - 1; i >= 0; i--) {
445 const float3 pos_su = positions_su[i];
446 const float3 pos_cu = math::transform_point(transforms_.surface_to_curves, pos_su);
447 const float dist_to_brush_cu = math::distance(pos_cu, brush_pos_cu);
448 const float radius_falloff = BKE_brush_curve_strength(
449 brush_, dist_to_brush_cu, brush_3d->radius_cu);
450 const float weight = brush_strength_ * radius_falloff;
451 if (rng.get_float() > weight) {
452 bary_coords.remove_and_reorder(i);
453 tri_indices.remove_and_reorder(i);
454 positions_su.remove_and_reorder(i);
455 }
456 }
457
458 for (const int i : bary_coords.index_range()) {
459 const float2 uv = bke::mesh_surface_sample::sample_corner_attribute_with_bary_coords(
460 bary_coords[i], surface_corner_tris_eval_[tri_indices[i]], surface_uv_map_eval_);
461 r_uvs.append(uv);
462 }
463 r_positions_su.extend(positions_su);
464 }
465 }
466};
467
468void DensityAddOperation::on_stroke_extended(const bContext &C,
469 const StrokeExtension &stroke_extension)
470{
471 DensityAddOperationExecutor executor{C};
472 executor.execute(*this, C, stroke_extension);
473}
474
476 private:
478
484 Vector<float3> deformed_root_positions_;
485
486 public:
487 void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
488};
489
495 DensitySubtractOperation *self_ = nullptr;
497
498 Object *object_ = nullptr;
499 Curves *curves_id_ = nullptr;
500 CurvesGeometry *curves_ = nullptr;
501
504
505 Object *surface_ob_orig_ = nullptr;
506 Mesh *surface_orig_ = nullptr;
507
508 Object *surface_ob_eval_ = nullptr;
509 Mesh *surface_eval_ = nullptr;
511
512 const CurvesSculpt *curves_sculpt_ = nullptr;
513 const Brush *brush_ = nullptr;
518
520
522
524
526
528 const bContext &C,
529 const StrokeExtension &stroke_extension)
530 {
531 self_ = &self;
532
533 object_ = CTX_data_active_object(&C);
534
535 curves_id_ = static_cast<Curves *>(object_->data);
536 curves_ = &curves_id_->geometry.wrap();
537 if (curves_->curves_num() == 0) {
538 return;
539 }
540
541 surface_ob_orig_ = curves_id_->surface;
542 if (surface_ob_orig_ == nullptr) {
543 return;
544 }
545 surface_orig_ = static_cast<Mesh *>(surface_ob_orig_->data);
546
547 surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, surface_ob_orig_);
548 if (surface_ob_eval_ == nullptr) {
549 return;
550 }
551 surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_);
552
553 BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_CORNER_TRIS, 2);
554 BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); });
555
556 curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
557 brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint);
558 brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_);
559 brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension);
560 brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension);
561 brush_pos_re_ = stroke_extension.mouse_position;
562
563 minimum_distance_ = brush_->curves_sculpt_settings->minimum_distance;
564
565 curve_selection_ = curves::retrieve_selected_curves(*curves_id_, selected_curve_memory_);
566
567 transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface);
568 const eBrushFalloffShape falloff_shape = eBrushFalloffShape(brush_->falloff_shape);
569
570 if (stroke_extension.is_first) {
571 const bke::crazyspace::GeometryDeformation deformation =
572 bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
573 for (const int curve_i : curves_->curves_range()) {
574 const int first_point_i = curves_->offsets()[curve_i];
575 self_->deformed_root_positions_.append(deformation.positions[first_point_i]);
576 }
577 }
578
579 root_points_kdtree_ = BLI_kdtree_3d_new(curve_selection_.size());
580 BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(root_points_kdtree_); });
581 curve_selection_.foreach_index([&](const int curve_i) {
582 const float3 &pos_cu = self_->deformed_root_positions_[curve_i];
583 BLI_kdtree_3d_insert(root_points_kdtree_, curve_i, pos_cu);
584 });
585 BLI_kdtree_3d_balance(root_points_kdtree_);
586
587 /* Find all curves that should be deleted. */
588 Array<bool> curves_to_keep(curves_->curves_num(), true);
589 if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
590 this->reduce_density_projected_with_symmetry(curves_to_keep);
591 }
592 else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
593 this->reduce_density_spherical_with_symmetry(curves_to_keep);
594 }
595 else {
597 }
598
599 IndexMaskMemory mask_memory;
600 const IndexMask mask_to_keep = IndexMask::from_bools(curves_to_keep, mask_memory);
601
602 /* Remove deleted curves from the stored deformed root positions. */
603 BLI_assert(curves_->curves_num() == self_->deformed_root_positions_.size());
604 Vector<float3> new_deformed_positions(mask_to_keep.size());
605 array_utils::gather(self_->deformed_root_positions_.as_span(),
606 mask_to_keep,
607 new_deformed_positions.as_mutable_span());
608 self_->deformed_root_positions_ = std::move(new_deformed_positions);
609
610 *curves_ = bke::curves_copy_curve_selection(*curves_, mask_to_keep, {});
611 BLI_assert(curves_->curves_num() == self_->deformed_root_positions_.size());
612
614 WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id);
616 }
617
619 {
620 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
621 eCurvesSymmetryType(curves_id_->symmetry));
622 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
623 this->reduce_density_projected(brush_transform, curves_to_keep);
624 }
625 }
626
627 void reduce_density_projected(const float4x4 &brush_transform, MutableSpan<bool> curves_to_keep)
628 {
629 const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
630 const float brush_radius_sq_re = pow2f(brush_radius_re);
631
632 const float4x4 projection = ED_view3d_ob_project_mat_get(ctx_.rv3d, object_);
633
634 /* Randomly select the curves that are allowed to be removed, based on the brush radius and
635 * strength. */
636 Array<bool> allow_remove_curve(curves_->curves_num(), false);
637 threading::parallel_for(curves_->curves_range(), 512, [&](const IndexRange range) {
638 RandomNumberGenerator rng = RandomNumberGenerator::from_random_seed();
639
640 for (const int curve_i : range) {
641 if (!curves_to_keep[curve_i]) {
642 allow_remove_curve[curve_i] = true;
643 continue;
644 }
645 const float3 pos_cu = math::transform_point(brush_transform,
646 self_->deformed_root_positions_[curve_i]);
647
648 const float2 pos_re = ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, projection);
649 const float dist_to_brush_sq_re = math::distance_squared(brush_pos_re_, pos_re);
650 if (dist_to_brush_sq_re > brush_radius_sq_re) {
651 continue;
652 }
653 const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re);
654 const float radius_falloff = BKE_brush_curve_strength(
655 brush_, dist_to_brush_re, brush_radius_re);
656 const float weight = brush_strength_ * radius_falloff;
657 if (rng.get_float() < weight) {
658 allow_remove_curve[curve_i] = true;
659 }
660 }
661 });
662
663 /* Detect curves that are too close to other existing curves. */
664 curve_selection_.foreach_segment([&](const IndexMaskSegment segment) {
665 for (const int curve_i : segment) {
666 if (!curves_to_keep[curve_i]) {
667 continue;
668 }
669 if (!allow_remove_curve[curve_i]) {
670 continue;
671 }
672 const float3 orig_pos_cu = self_->deformed_root_positions_[curve_i];
673 const float3 pos_cu = math::transform_point(brush_transform, orig_pos_cu);
674 const float2 pos_re = ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, projection);
675 const float dist_to_brush_sq_re = math::distance_squared(brush_pos_re_, pos_re);
676 if (dist_to_brush_sq_re > brush_radius_sq_re) {
677 continue;
678 }
679 BLI_kdtree_3d_range_search_cb_cpp(
680 root_points_kdtree_,
681 orig_pos_cu,
682 minimum_distance_,
683 [&](const int other_curve_i, const float * /*co*/, float /*dist_sq*/) {
684 if (other_curve_i == curve_i) {
685 return true;
686 }
687 if (allow_remove_curve[other_curve_i]) {
688 curves_to_keep[other_curve_i] = false;
689 }
690 return true;
691 });
692 }
693 });
694 }
695
697 {
698 const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
699 const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph,
700 *ctx_.region,
701 *ctx_.v3d,
702 transforms_,
703 surface_bvh_eval_,
704 brush_pos_re_,
705 brush_radius_re);
706 if (!brush_3d.has_value()) {
707 return;
708 }
709
710 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
711 eCurvesSymmetryType(curves_id_->symmetry));
712 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
713 const float3 brush_pos_cu = math::transform_point(brush_transform, brush_3d->position_cu);
714 this->reduce_density_spherical(brush_pos_cu, brush_3d->radius_cu, curves_to_keep);
715 }
716 }
717
718 void reduce_density_spherical(const float3 &brush_pos_cu,
719 const float brush_radius_cu,
720 MutableSpan<bool> curves_to_keep)
721 {
722 const float brush_radius_sq_cu = pow2f(brush_radius_cu);
723
724 /* Randomly select the curves that are allowed to be removed, based on the brush radius and
725 * strength. */
726 Array<bool> allow_remove_curve(curves_->curves_num(), false);
727 threading::parallel_for(curves_->curves_range(), 512, [&](const IndexRange range) {
728 RandomNumberGenerator rng = RandomNumberGenerator::from_random_seed();
729
730 for (const int curve_i : range) {
731 if (!curves_to_keep[curve_i]) {
732 allow_remove_curve[curve_i] = true;
733 continue;
734 }
735 const float3 pos_cu = self_->deformed_root_positions_[curve_i];
736
737 const float dist_to_brush_sq_cu = math::distance_squared(brush_pos_cu, pos_cu);
738 if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
739 continue;
740 }
741 const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu);
742 const float radius_falloff = BKE_brush_curve_strength(
743 brush_, dist_to_brush_cu, brush_radius_cu);
744 const float weight = brush_strength_ * radius_falloff;
745 if (rng.get_float() < weight) {
746 allow_remove_curve[curve_i] = true;
747 }
748 }
749 });
750
751 /* Detect curves that are too close to other existing curves. */
752 curve_selection_.foreach_segment([&](const IndexMaskSegment segment) {
753 for (const int curve_i : segment) {
754 if (!curves_to_keep[curve_i]) {
755 continue;
756 }
757 if (!allow_remove_curve[curve_i]) {
758 continue;
759 }
760 const float3 &pos_cu = self_->deformed_root_positions_[curve_i];
761 const float dist_to_brush_sq_cu = math::distance_squared(pos_cu, brush_pos_cu);
762 if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
763 continue;
764 }
765
766 BLI_kdtree_3d_range_search_cb_cpp(
767 root_points_kdtree_,
768 pos_cu,
769 minimum_distance_,
770 [&](const int other_curve_i, const float * /*co*/, float /*dist_sq*/) {
771 if (other_curve_i == curve_i) {
772 return true;
773 }
774 if (allow_remove_curve[other_curve_i]) {
775 curves_to_keep[other_curve_i] = false;
776 }
777 return true;
778 });
779 }
780 });
781 }
782};
783
784void DensitySubtractOperation::on_stroke_extended(const bContext &C,
785 const StrokeExtension &stroke_extension)
786{
788 executor.execute(*this, C, stroke_extension);
789}
790
794static bool use_add_density_mode(const BrushStrokeMode brush_mode,
795 const bContext &C,
796 const StrokeExtension &stroke_start)
797{
798 const Scene &scene = *CTX_data_scene(&C);
799 const Brush &brush = *BKE_paint_brush_for_read(&scene.toolsettings->curves_sculpt->paint);
800 const Depsgraph &depsgraph = *CTX_data_depsgraph_on_load(&C);
801 const ARegion &region = *CTX_wm_region(&C);
802 const View3D &v3d = *CTX_wm_view3d(&C);
803
806 const bool use_invert = brush_mode == BRUSH_STROKE_INVERT;
807
808 if (density_mode == BRUSH_CURVES_SCULPT_DENSITY_MODE_ADD) {
809 return !use_invert;
810 }
811 if (density_mode == BRUSH_CURVES_SCULPT_DENSITY_MODE_REMOVE) {
812 return use_invert;
813 }
814
815 const Object &curves_ob_orig = *CTX_data_active_object(&C);
816 const Curves &curves_id_orig = *static_cast<Curves *>(curves_ob_orig.data);
817 Object *surface_ob_orig = curves_id_orig.surface;
818 if (surface_ob_orig == nullptr) {
819 return true;
820 }
821 Object *surface_ob_eval = DEG_get_evaluated_object(&depsgraph, surface_ob_orig);
822 if (surface_ob_eval == nullptr) {
823 return true;
824 }
825 const CurvesGeometry &curves = curves_id_orig.geometry.wrap();
826 if (curves.curves_num() <= 1) {
827 return true;
828 }
829 const Mesh *surface_mesh_eval = BKE_object_get_evaluated_mesh(surface_ob_eval);
830 if (surface_mesh_eval == nullptr) {
831 return true;
832 }
833
834 const CurvesSurfaceTransforms transforms(curves_ob_orig, curves_id_orig.surface);
835 BVHTreeFromMesh surface_bvh_eval;
836 BKE_bvhtree_from_mesh_get(&surface_bvh_eval, surface_mesh_eval, BVHTREE_FROM_CORNER_TRIS, 2);
837 BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval); });
838
839 const float2 brush_pos_re = stroke_start.mouse_position;
840 /* Reduce radius so that only an inner circle is used to determine the existing density. */
841 const float brush_radius_re = BKE_brush_size_get(&scene, &brush) * 0.5f;
842
843 /* Find the surface point under the brush. */
844 const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(
845 depsgraph, region, v3d, transforms, surface_bvh_eval, brush_pos_re, brush_radius_re);
846 if (!brush_3d.has_value()) {
847 return true;
848 }
849
850 const float3 brush_pos_cu = brush_3d->position_cu;
851 const float brush_radius_cu = brush_3d->radius_cu;
852 const float brush_radius_sq_cu = pow2f(brush_radius_cu);
853
854 const bke::crazyspace::GeometryDeformation deformation =
855 bke::crazyspace::get_evaluated_curves_deformation(depsgraph, curves_ob_orig);
856 const Span<int> offsets = curves.offsets();
857
858 /* Compute distance from brush to curve roots. */
859 Array<std::pair<float, int>> distances_sq_to_brush(curves.curves_num());
860 threading::EnumerableThreadSpecific<int> valid_curve_count_by_thread([&]() { return 0; });
861 threading::parallel_for(curves.curves_range(), 512, [&](const IndexRange range) {
862 int &valid_curve_count = valid_curve_count_by_thread.local();
863 for (const int curve_i : range) {
864 const int root_point_i = offsets[curve_i];
865 const float3 &root_pos_cu = deformation.positions[root_point_i];
866 const float dist_sq_cu = math::distance_squared(root_pos_cu, brush_pos_cu);
867 if (dist_sq_cu < brush_radius_sq_cu) {
868 distances_sq_to_brush[curve_i] = {math::distance_squared(root_pos_cu, brush_pos_cu),
869 curve_i};
870 valid_curve_count++;
871 }
872 else {
873 distances_sq_to_brush[curve_i] = {FLT_MAX, -1};
874 }
875 }
876 });
877 const int valid_curve_count = std::accumulate(
878 valid_curve_count_by_thread.begin(), valid_curve_count_by_thread.end(), 0);
879
880 /* Find a couple of curves that are closest to the brush center. */
881 const int check_curve_count = std::min<int>(8, valid_curve_count);
882 std::partial_sort(distances_sq_to_brush.begin(),
883 distances_sq_to_brush.begin() + check_curve_count,
884 distances_sq_to_brush.end());
885
886 /* Compute the minimum pair-wise distance between the curve roots that are close to the brush
887 * center. */
888 float min_dist_sq_cu = FLT_MAX;
889 for (const int i : IndexRange(check_curve_count)) {
890 const float3 &pos_i = deformation.positions[offsets[distances_sq_to_brush[i].second]];
891 for (int j = i + 1; j < check_curve_count; j++) {
892 const float3 &pos_j = deformation.positions[offsets[distances_sq_to_brush[j].second]];
893 const float dist_sq_cu = math::distance_squared(pos_i, pos_j);
894 math::min_inplace(min_dist_sq_cu, dist_sq_cu);
895 }
896 }
897
898 const float min_dist_cu = std::sqrt(min_dist_sq_cu);
899 if (min_dist_cu > brush.curves_sculpt_settings->minimum_distance) {
900 return true;
901 }
902
903 return false;
904}
905
906std::unique_ptr<CurvesSculptStrokeOperation> new_density_operation(
907 const BrushStrokeMode brush_mode, const bContext &C, const StrokeExtension &stroke_start)
908{
909 if (use_add_density_mode(brush_mode, C, stroke_start)) {
910 return std::make_unique<DensityAddOperation>();
911 }
912 return std::make_unique<DensitySubtractOperation>();
913}
914
915} // namespace blender::ed::sculpt_paint
int BKE_brush_size_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1075
float BKE_brush_curve_strength(eBrushCurvePreset preset, const CurveMapping *cumap, float distance, float brush_radius)
Definition brush.cc:1388
void free_bvhtree_from_mesh(BVHTreeFromMesh *data)
Definition bvhutils.cc:1160
BVHTree * BKE_bvhtree_from_mesh_get(BVHTreeFromMesh *data, const Mesh *mesh, BVHCacheType bvh_cache_type, int tree_type)
Definition bvhutils.cc:899
@ BVHTREE_FROM_CORNER_TRIS
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
Depsgraph * CTX_data_depsgraph_on_load(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
General operations, lookup, etc. for blender objects.
Mesh * BKE_object_get_evaluated_mesh(const Object *object_eval)
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:654
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
A KD-tree for nearest neighbor search.
MINLINE float pow2f(float x)
#define M_PI
#define BLI_SCOPED_DEFER(function_to_defer)
void DEG_id_tag_update(ID *id, unsigned int flags)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
eBrushFalloffShape
@ PAINT_FALLOFF_SHAPE_SPHERE
@ PAINT_FALLOFF_SHAPE_TUBE
@ BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT
@ BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_RADIUS
@ BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH
@ BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE
eBrushCurvesSculptDensityMode
@ BRUSH_CURVES_SCULPT_DENSITY_MODE_REMOVE
@ BRUSH_CURVES_SCULPT_DENSITY_MODE_ADD
eCurvesSymmetryType
@ OB_MESH
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
blender::float2 ED_view3d_project_float_v2_m4(const ARegion *region, const float co[3], const blender::float4x4 &mat)
blender::float4x4 ED_view3d_ob_project_mat_get(const RegionView3D *rv3d, const Object *ob)
bool ED_view3d_win_to_segment_clipped(const Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const float mval[2], float r_ray_start[3], float r_ray_end[3], bool do_clip_planes)
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
PyObject * self
AttributeSet attributes
static RandomNumberGenerator from_random_seed()
Definition rand.cc:365
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr bool is_empty() const
Definition BLI_span.hh:261
int64_t size() const
void remove_and_reorder(const int64_t index)
void append(const T &value)
IndexRange index_range() const
MutableSpan< T > as_mutable_span()
void extend(Span< T > array)
bool contains(const StringRef attribute_id) const
IndexRange curves_range() const
MutableAttributeAccessor attributes_for_write()
Span< float3 > positions() const
void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override
void foreach_index(Fn &&fn) const
void foreach_segment(Fn &&fn) const
const Depsgraph * depsgraph
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_invalid_uv_map(ReportList *reports)
void report_empty_evaluated_surface(ReportList *reports)
float brush_radius_get(const Scene &scene, const Brush &brush, const StrokeExtension &stroke_extension)
void report_missing_uv_map_on_original_surface(ReportList *reports)
static bool use_add_density_mode(const BrushStrokeMode brush_mode, const bContext &C, const StrokeExtension &stroke_start)
void report_missing_uv_map_on_evaluated_surface(ReportList *reports)
void report_missing_surface(ReportList *reports)
void report_empty_original_surface(ReportList *reports)
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)
std::unique_ptr< CurvesSculptStrokeOperation > new_density_operation(const BrushStrokeMode brush_mode, const bContext &C, const StrokeExtension &stroke_start)
float transform_brush_radius(const float4x4 &transform, const float3 &brush_position, const float old_radius)
T distance(const T &a, const T &b)
void min_inplace(T &a, const T &b)
CartesianBasis invert(const CartesianBasis &basis)
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
void parallel_invoke(Functions &&...functions)
Definition BLI_task.hh:199
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
BrushStrokeMode
@ BRUSH_STROKE_INVERT
#define FLT_MAX
Definition stdcycles.h:14
__int64 int64_t
Definition stdint.h:89
char falloff_shape
struct BrushCurvesSculptSettings * curves_sculpt_settings
CurvesGeometry geometry
struct Object * surface
char * surface_uv_map
int faces_num
struct ToolSettings * toolsettings
CurvesSculpt * curves_sculpt
void sample_projected_with_symmetry(RandomNumberGenerator &rng, Vector< float2 > &r_uvs, Vector< float3 > &r_positions_su)
void sample_spherical_with_symmetry(RandomNumberGenerator &rng, Vector< float2 > &r_uvs, Vector< float3 > &r_positions_su)
void execute(DensityAddOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
void reduce_density_spherical_with_symmetry(MutableSpan< bool > curves_to_keep)
void execute(DensitySubtractOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
void reduce_density_spherical(const float3 &brush_pos_cu, const float brush_radius_cu, MutableSpan< bool > curves_to_keep)
void reduce_density_projected_with_symmetry(MutableSpan< bool > curves_to_keep)
void reduce_density_projected(const float4x4 &brush_transform, MutableSpan< bool > curves_to_keep)
void WM_main_add_notifier(uint type, void *reference)