Blender V4.5
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
7#include "BKE_brush.hh"
8#include "BKE_bvhutils.hh"
9#include "BKE_context.hh"
10#include "BKE_geometry_set.hh"
11#include "BKE_mesh.hh"
12#include "BKE_mesh_runtime.hh"
13#include "BKE_mesh_sample.hh"
14#include "BKE_modifier.hh"
15#include "BKE_object.hh"
16#include "BKE_paint.hh"
17#include "BLI_bounds.hh"
18
19#include "ED_screen.hh"
20#include "ED_view3d.hh"
21
22#include "DEG_depsgraph.hh"
24
25#include "BLI_array_utils.hh"
27#include "BLI_kdtree.h"
28#include "BLI_rand.hh"
29#include "BLI_task.hh"
30
32
33#include "DNA_brush_types.h"
34#include "DNA_mesh_types.h"
35
36#include "WM_api.hh"
37
39
41
43 private:
45 KDTree_3d *original_curve_roots_kdtree_ = nullptr;
47 KDTree_3d *deformed_curve_roots_kdtree_ = nullptr;
49 Vector<float3> new_deformed_root_positions_;
50 int original_curve_num_ = 0;
51
53
54 public:
56 {
57 if (original_curve_roots_kdtree_ != nullptr) {
58 BLI_kdtree_3d_free(original_curve_roots_kdtree_);
59 }
60 if (deformed_curve_roots_kdtree_ != nullptr) {
61 BLI_kdtree_3d_free(deformed_curve_roots_kdtree_);
62 }
63 }
64
65 void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
66};
67
71
75
77 const Mesh *surface_orig_ = nullptr;
78
80 Mesh *surface_eval_ = nullptr;
84
85 const CurvesSculpt *curves_sculpt_ = nullptr;
86 const Brush *brush_ = nullptr;
88
92
94
96
98 const bContext &C,
99 const StrokeExtension &stroke_extension)
100 {
101 self_ = &self;
103 curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data);
104 curves_orig_ = &curves_id_orig_->geometry.wrap();
105
106 if (stroke_extension.is_first) {
107 self_->original_curve_num_ = curves_orig_->curves_num();
108 }
109
110 if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) {
111 report_missing_surface(stroke_extension.reports);
112 return;
113 }
114
116 surface_orig_ = static_cast<const Mesh *>(surface_ob_orig_->data);
117 if (surface_orig_->faces_num == 0) {
118 report_empty_original_surface(stroke_extension.reports);
119 return;
120 }
121
123 if (surface_ob_eval_ == nullptr) {
124 return;
125 }
127 if (surface_eval_->faces_num == 0) {
128 report_empty_evaluated_surface(stroke_extension.reports);
129 return;
130 }
131
132 surface_bvh_eval_ = surface_eval_->bvh_corner_tris();
134 /* Find UV map. */
135 VArraySpan<float2> surface_uv_map;
136 if (curves_id_orig_->surface_uv_map != nullptr) {
137 surface_uv_map = *surface_orig_->attributes().lookup<float2>(curves_id_orig_->surface_uv_map,
139 surface_uv_map_eval_ = *surface_eval_->attributes().lookup<float2>(
141 }
142 if (surface_uv_map.is_empty()) {
144 return;
145 }
146 if (surface_uv_map_eval_.is_empty()) {
148 return;
149 }
150
152
153 curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
155 brush_settings_ = brush_->curves_sculpt_settings;
156 brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension);
157 brush_radius_re_ = brush_radius_get(*ctx_.scene, *brush_, stroke_extension);
158 brush_pos_re_ = stroke_extension.mouse_position;
159
160 const eBrushFalloffShape falloff_shape = eBrushFalloffShape(brush_->falloff_shape);
161
162 Vector<float3> new_positions_cu;
163 Vector<float2> new_uvs;
165
166 /* Find potential new curve root points. */
167 if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
168 this->sample_projected_with_symmetry(rng, new_uvs, new_positions_cu);
169 }
170 else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
171 this->sample_spherical_with_symmetry(rng, new_uvs, new_positions_cu);
172 }
173 else {
175 }
176 for (float3 &pos : new_positions_cu) {
177 pos = math::transform_point(transforms_.surface_to_curves, pos);
178 }
179
180 if (stroke_extension.is_first) {
182 }
183
184 const int already_added_curves = self_->new_deformed_root_positions_.size();
185 KDTree_3d *new_roots_kdtree = BLI_kdtree_3d_new(already_added_curves +
186 new_positions_cu.size());
187 BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(new_roots_kdtree); });
188
189 /* Used to tag all curves that are too close to existing curves or too close to other new
190 * curves. */
191 Array<bool> new_curve_skipped(new_positions_cu.size(), false);
193 512 < already_added_curves + new_positions_cu.size(),
194 /* Build kdtree from root points created by the current stroke. */
195 [&]() {
196 for (const int i : IndexRange(already_added_curves)) {
197 BLI_kdtree_3d_insert(new_roots_kdtree, -1, self_->new_deformed_root_positions_[i]);
198 }
199 for (const int new_i : new_positions_cu.index_range()) {
200 const float3 &root_pos_cu = new_positions_cu[new_i];
201 BLI_kdtree_3d_insert(new_roots_kdtree, new_i, root_pos_cu);
202 }
203 BLI_kdtree_3d_balance(new_roots_kdtree);
204 },
205 /* Check which new root points are close to roots that existed before the current stroke
206 * started. */
207 [&]() {
209 new_positions_cu.index_range(), 128, [&](const IndexRange range) {
210 for (const int new_i : range) {
211 const float3 &new_root_pos_cu = new_positions_cu[new_i];
212 KDTreeNearest_3d nearest;
213 nearest.dist = FLT_MAX;
214 BLI_kdtree_3d_find_nearest(
215 self_->deformed_curve_roots_kdtree_, new_root_pos_cu, &nearest);
216 if (nearest.dist < brush_settings_->minimum_distance) {
217 new_curve_skipped[new_i] = true;
218 }
219 }
220 });
221 });
222
223 /* Find new points that are too close too other new points. */
224 for (const int new_i : new_positions_cu.index_range()) {
225 if (new_curve_skipped[new_i]) {
226 continue;
227 }
228 const float3 &root_pos_cu = new_positions_cu[new_i];
229 BLI_kdtree_3d_range_search_cb_cpp(
230 new_roots_kdtree,
231 root_pos_cu,
232 brush_settings_->minimum_distance,
233 [&](const int other_new_i, const float * /*co*/, float /*dist_sq*/) {
234 if (other_new_i == -1) {
235 new_curve_skipped[new_i] = true;
236 return false;
237 }
238 if (new_i == other_new_i) {
239 return true;
240 }
241 new_curve_skipped[other_new_i] = true;
242 return true;
243 });
244 }
245
246 /* Remove points that are too close to others. */
247 for (int64_t i = new_positions_cu.size() - 1; i >= 0; i--) {
248 if (new_curve_skipped[i]) {
249 new_positions_cu.remove_and_reorder(i);
250 new_uvs.remove_and_reorder(i);
251 }
252 }
253 self_->new_deformed_root_positions_.extend(new_positions_cu);
254
255 const Span<float3> corner_normals_su = surface_orig_->corner_normals();
256 const Span<int3> surface_corner_tris_orig = surface_orig_->corner_tris();
257 const geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_corner_tris_orig};
258
259 geometry::AddCurvesOnMeshInputs add_inputs;
260 add_inputs.uvs = new_uvs;
261 add_inputs.interpolate_length = brush_settings_->flag &
263 add_inputs.interpolate_radius = brush_settings_->flag &
265 add_inputs.interpolate_shape = brush_settings_->flag &
267 add_inputs.interpolate_point_count = brush_settings_->flag &
269 add_inputs.interpolate_resolution = curves_orig_->attributes().contains("resolution");
270 add_inputs.fallback_curve_length = brush_settings_->curve_length;
271 add_inputs.fallback_curve_radius = brush_settings_->curve_radius;
272 add_inputs.fallback_point_count = std::max(2, brush_settings_->points_per_curve);
273 add_inputs.transforms = &transforms_;
274 add_inputs.surface = surface_orig_;
275 add_inputs.corner_normals_su = corner_normals_su;
276 add_inputs.surface_corner_tris = surface_corner_tris_orig;
277 add_inputs.reverse_uv_sampler = &reverse_uv_sampler;
278 add_inputs.old_roots_kdtree = self_->original_curve_roots_kdtree_;
279
280 const geometry::AddCurvesOnMeshOutputs add_outputs = geometry::add_curves_on_mesh(
281 *curves_orig_, add_inputs);
282 bke::MutableAttributeAccessor attributes = curves_orig_->attributes_for_write();
283 if (bke::GSpanAttributeWriter selection = attributes.lookup_for_write_span(".selection")) {
284 curves::fill_selection_true(selection.span.slice(selection.domain == bke::AttrDomain::Point ?
285 add_outputs.new_points_range :
286 add_outputs.new_curves_range));
287 selection.finish();
288 }
289 if (U.uiflag & USER_ORBIT_SELECTION) {
290 if (const std::optional<Bounds<float3>> center_cu = bounds::min_max(
291 curves_orig_->positions().slice(add_outputs.new_points_range)))
292 {
294 *ctx_.scene, math::transform_point(transforms_.curves_to_world, center_cu->center()));
295 }
296 }
297
298 if (add_outputs.uv_error) {
299 report_invalid_uv_map(stroke_extension.reports);
300 }
301
302 DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY);
303 WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id);
305 }
306
308 {
309 const bke::crazyspace::GeometryDeformation deformation =
311 const Span<int> curve_offsets = curves_orig_->offsets();
312 const Span<float3> original_positions = curves_orig_->positions();
313 const Span<float3> deformed_positions = deformation.positions;
314 BLI_assert(original_positions.size() == deformed_positions.size());
315
316 auto roots_kdtree_from_positions = [&](const Span<float3> positions) {
317 KDTree_3d *kdtree = BLI_kdtree_3d_new(curves_orig_->curves_num());
318 for (const int curve_i : curves_orig_->curves_range()) {
319 const int root_point_i = curve_offsets[curve_i];
320 BLI_kdtree_3d_insert(kdtree, curve_i, positions[root_point_i]);
321 }
322 BLI_kdtree_3d_balance(kdtree);
323 return kdtree;
324 };
325
327 1024 < original_positions.size() + deformed_positions.size(),
328 [&]() {
329 self_->original_curve_roots_kdtree_ = roots_kdtree_from_positions(original_positions);
330 },
331 [&]() {
332 self_->deformed_curve_roots_kdtree_ = roots_kdtree_from_positions(deformed_positions);
333 });
334 }
335
337 Vector<float2> &r_uvs,
338 Vector<float3> &r_positions_su)
339 {
341
342 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
344 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
345 const float4x4 brush_transform_inv = math::invert(brush_transform);
346 const float4x4 transform = transforms_.curves_to_surface * brush_transform *
347 transforms_.world_to_curves;
348 Vector<float3> positions_su;
349 Vector<float3> bary_coords;
350 Vector<int> tri_indices;
352 rng,
357 [&](const float2 &pos_re, float3 &r_start_su, float3 &r_end_su) {
358 float3 start_wo, end_wo;
360 ctx_.depsgraph, ctx_.region, ctx_.v3d, pos_re, start_wo, end_wo, true);
361 r_start_su = math::transform_point(transform, start_wo);
362 r_end_su = math::transform_point(transform, end_wo);
363 },
364 true,
365 brush_settings_->density_add_attempts,
366 brush_settings_->density_add_attempts,
367 bary_coords,
368 tri_indices,
369 positions_su);
370
371 /* Remove some sampled points randomly based on the brush falloff and strength. */
372 for (int i = new_points - 1; i >= 0; i--) {
373 const float3 pos_su = positions_su[i];
374 const float3 pos_cu = math::transform_point(
375 brush_transform_inv, math::transform_point(transforms_.surface_to_curves, pos_su));
376 const float2 pos_re = ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, projection);
377 const float dist_to_brush_re = math::distance(brush_pos_re_, pos_re);
378 const float radius_falloff = BKE_brush_curve_strength(
379 brush_, dist_to_brush_re, brush_radius_re_);
380 const float weight = brush_strength_ * radius_falloff;
381 if (rng.get_float() > weight) {
382 bary_coords.remove_and_reorder(i);
383 tri_indices.remove_and_reorder(i);
384 positions_su.remove_and_reorder(i);
385 }
386 }
387
388 for (const int i : bary_coords.index_range()) {
390 bary_coords[i], surface_corner_tris_eval_[tri_indices[i]], surface_uv_map_eval_);
391 r_uvs.append(uv);
392 }
393 r_positions_su.extend(positions_su);
394 }
395 }
396
398 Vector<float2> &r_uvs,
399 Vector<float3> &r_positions_su)
400 {
401 const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph,
402 *ctx_.region,
403 *ctx_.v3d,
408 if (!brush_3d.has_value()) {
409 return;
410 }
411
412 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
414 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
415 const float3 brush_pos_cu = math::transform_point(brush_transform, brush_3d->position_cu);
416 const float3 brush_pos_su = math::transform_point(transforms_.curves_to_surface,
417 brush_pos_cu);
418 const float brush_radius_su = transform_brush_radius(
419 transforms_.curves_to_surface, brush_pos_cu, brush_3d->radius_cu);
420 const float brush_radius_sq_su = pow2f(brush_radius_su);
421
422 Vector<int> selected_corner_tri_indices;
424 *surface_bvh_eval_.tree,
425 brush_pos_su,
426 brush_radius_su,
427 [&](const int index, const float3 & /*co*/, const float /*dist_sq*/) {
428 selected_corner_tri_indices.append(index);
429 });
430
431 const float brush_plane_area_su = M_PI * brush_radius_sq_su;
432 const float approximate_density_su = brush_settings_->density_add_attempts /
433 brush_plane_area_su;
434
435 Vector<float3> positions_su;
436 Vector<float3> bary_coords;
437 Vector<int> tri_indices;
439 rng,
441 selected_corner_tri_indices,
442 brush_pos_su,
443 brush_radius_su,
444 approximate_density_su,
445 bary_coords,
446 tri_indices,
447 positions_su);
448
449 /* Remove some sampled points randomly based on the brush falloff and strength. */
450 for (int i = new_points - 1; i >= 0; i--) {
451 const float3 pos_su = positions_su[i];
452 const float3 pos_cu = math::transform_point(transforms_.surface_to_curves, pos_su);
453 const float dist_to_brush_cu = math::distance(pos_cu, brush_pos_cu);
454 const float radius_falloff = BKE_brush_curve_strength(
455 brush_, dist_to_brush_cu, brush_3d->radius_cu);
456 const float weight = brush_strength_ * radius_falloff;
457 if (rng.get_float() > weight) {
458 bary_coords.remove_and_reorder(i);
459 tri_indices.remove_and_reorder(i);
460 positions_su.remove_and_reorder(i);
461 }
462 }
463
464 for (const int i : bary_coords.index_range()) {
466 bary_coords[i], surface_corner_tris_eval_[tri_indices[i]], surface_uv_map_eval_);
467 r_uvs.append(uv);
468 }
469 r_positions_su.extend(positions_su);
470 }
471 }
472};
473
475 const StrokeExtension &stroke_extension)
476{
478 executor.execute(*this, C, stroke_extension);
479}
480
482 private:
484
490 Vector<float3> deformed_root_positions_;
491
492 public:
493 void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
494};
495
503
504 Object *object_ = nullptr;
505 Curves *curves_id_ = nullptr;
507
510
512 Mesh *surface_orig_ = nullptr;
513
515 Mesh *surface_eval_ = nullptr;
517
518 const CurvesSculpt *curves_sculpt_ = nullptr;
519 const Brush *brush_ = nullptr;
524
526
528
530
532
534 const bContext &C,
535 const StrokeExtension &stroke_extension)
536 {
537 self_ = &self;
538
540
541 curves_id_ = static_cast<Curves *>(object_->data);
542 curves_ = &curves_id_->geometry.wrap();
543 if (curves_->is_empty()) {
544 return;
545 }
546
547 surface_ob_orig_ = curves_id_->surface;
548 if (surface_ob_orig_ == nullptr) {
549 return;
550 }
551 surface_orig_ = static_cast<Mesh *>(surface_ob_orig_->data);
552
554 if (surface_ob_eval_ == nullptr) {
555 return;
556 }
558
559 surface_bvh_eval_ = surface_eval_->bvh_corner_tris();
560
561 curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
564 brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension);
565 brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension);
566 brush_pos_re_ = stroke_extension.mouse_position;
567
568 minimum_distance_ = brush_->curves_sculpt_settings->minimum_distance;
569
571
573 const eBrushFalloffShape falloff_shape = eBrushFalloffShape(brush_->falloff_shape);
574
575 if (stroke_extension.is_first) {
576 const bke::crazyspace::GeometryDeformation deformation =
578 for (const int curve_i : curves_->curves_range()) {
579 const int first_point_i = curves_->offsets()[curve_i];
580 self_->deformed_root_positions_.append(deformation.positions[first_point_i]);
581 }
582 }
583
584 root_points_kdtree_ = BLI_kdtree_3d_new(curve_selection_.size());
585 BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(root_points_kdtree_); });
586 curve_selection_.foreach_index([&](const int curve_i) {
587 const float3 &pos_cu = self_->deformed_root_positions_[curve_i];
588 BLI_kdtree_3d_insert(root_points_kdtree_, curve_i, pos_cu);
589 });
590 BLI_kdtree_3d_balance(root_points_kdtree_);
591
592 /* Find all curves that should be deleted. */
593 Array<bool> curves_to_keep(curves_->curves_num(), true);
594 if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
595 this->reduce_density_projected_with_symmetry(curves_to_keep);
596 }
597 else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
598 this->reduce_density_spherical_with_symmetry(curves_to_keep);
599 }
600 else {
602 }
603
604 IndexMaskMemory mask_memory;
605 const IndexMask mask_to_keep = IndexMask::from_bools(curves_to_keep, mask_memory);
606
607 /* Remove deleted curves from the stored deformed root positions. */
608 BLI_assert(curves_->curves_num() == self_->deformed_root_positions_.size());
609 Vector<float3> new_deformed_positions(mask_to_keep.size());
610 array_utils::gather(self_->deformed_root_positions_.as_span(),
611 mask_to_keep,
612 new_deformed_positions.as_mutable_span());
613 self_->deformed_root_positions_ = std::move(new_deformed_positions);
614
615 *curves_ = bke::curves_copy_curve_selection(*curves_, mask_to_keep, {});
616 BLI_assert(curves_->curves_num() == self_->deformed_root_positions_.size());
617
621 }
622
624 {
625 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
627 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
628 this->reduce_density_projected(brush_transform, curves_to_keep);
629 }
630 }
631
632 void reduce_density_projected(const float4x4 &brush_transform, MutableSpan<bool> curves_to_keep)
633 {
634 const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
635 const float brush_radius_sq_re = pow2f(brush_radius_re);
636
637 const float4x4 projection = ED_view3d_ob_project_mat_get(ctx_.rv3d, object_);
638
639 /* Randomly select the curves that are allowed to be removed, based on the brush radius and
640 * strength. */
641 Array<bool> allow_remove_curve(curves_->curves_num(), false);
642 threading::parallel_for(curves_->curves_range(), 512, [&](const IndexRange range) {
643 RandomNumberGenerator rng = RandomNumberGenerator::from_random_seed();
644
645 for (const int curve_i : range) {
646 if (!curves_to_keep[curve_i]) {
647 allow_remove_curve[curve_i] = true;
648 continue;
649 }
650 const float3 pos_cu = math::transform_point(brush_transform,
651 self_->deformed_root_positions_[curve_i]);
652
653 const float2 pos_re = ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, projection);
654 const float dist_to_brush_sq_re = math::distance_squared(brush_pos_re_, pos_re);
655 if (dist_to_brush_sq_re > brush_radius_sq_re) {
656 continue;
657 }
658 const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re);
659 const float radius_falloff = BKE_brush_curve_strength(
660 brush_, dist_to_brush_re, brush_radius_re);
661 const float weight = brush_strength_ * radius_falloff;
662 if (rng.get_float() < weight) {
663 allow_remove_curve[curve_i] = true;
664 }
665 }
666 });
667
668 /* Detect curves that are too close to other existing curves. */
669 curve_selection_.foreach_segment([&](const IndexMaskSegment segment) {
670 for (const int curve_i : segment) {
671 if (!curves_to_keep[curve_i]) {
672 continue;
673 }
674 if (!allow_remove_curve[curve_i]) {
675 continue;
676 }
677 const float3 orig_pos_cu = self_->deformed_root_positions_[curve_i];
678 const float3 pos_cu = math::transform_point(brush_transform, orig_pos_cu);
679 const float2 pos_re = ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, projection);
680 const float dist_to_brush_sq_re = math::distance_squared(brush_pos_re_, pos_re);
681 if (dist_to_brush_sq_re > brush_radius_sq_re) {
682 continue;
683 }
684 BLI_kdtree_3d_range_search_cb_cpp(
685 root_points_kdtree_,
686 orig_pos_cu,
687 minimum_distance_,
688 [&](const int other_curve_i, const float * /*co*/, float /*dist_sq*/) {
689 if (other_curve_i == curve_i) {
690 return true;
691 }
692 if (allow_remove_curve[other_curve_i]) {
693 curves_to_keep[other_curve_i] = false;
694 }
695 return true;
696 });
697 }
698 });
699 }
700
702 {
703 const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
704 const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph,
705 *ctx_.region,
706 *ctx_.v3d,
710 brush_radius_re);
711 if (!brush_3d.has_value()) {
712 return;
713 }
714
715 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
717 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
718 const float3 brush_pos_cu = math::transform_point(brush_transform, brush_3d->position_cu);
719 this->reduce_density_spherical(brush_pos_cu, brush_3d->radius_cu, curves_to_keep);
720 }
721 }
722
723 void reduce_density_spherical(const float3 &brush_pos_cu,
724 const float brush_radius_cu,
725 MutableSpan<bool> curves_to_keep)
726 {
727 const float brush_radius_sq_cu = pow2f(brush_radius_cu);
728
729 /* Randomly select the curves that are allowed to be removed, based on the brush radius and
730 * strength. */
731 Array<bool> allow_remove_curve(curves_->curves_num(), false);
732 threading::parallel_for(curves_->curves_range(), 512, [&](const IndexRange range) {
733 RandomNumberGenerator rng = RandomNumberGenerator::from_random_seed();
734
735 for (const int curve_i : range) {
736 if (!curves_to_keep[curve_i]) {
737 allow_remove_curve[curve_i] = true;
738 continue;
739 }
740 const float3 pos_cu = self_->deformed_root_positions_[curve_i];
741
742 const float dist_to_brush_sq_cu = math::distance_squared(brush_pos_cu, pos_cu);
743 if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
744 continue;
745 }
746 const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu);
747 const float radius_falloff = BKE_brush_curve_strength(
748 brush_, dist_to_brush_cu, brush_radius_cu);
749 const float weight = brush_strength_ * radius_falloff;
750 if (rng.get_float() < weight) {
751 allow_remove_curve[curve_i] = true;
752 }
753 }
754 });
755
756 /* Detect curves that are too close to other existing curves. */
757 curve_selection_.foreach_segment([&](const IndexMaskSegment segment) {
758 for (const int curve_i : segment) {
759 if (!curves_to_keep[curve_i]) {
760 continue;
761 }
762 if (!allow_remove_curve[curve_i]) {
763 continue;
764 }
765 const float3 &pos_cu = self_->deformed_root_positions_[curve_i];
766 const float dist_to_brush_sq_cu = math::distance_squared(pos_cu, brush_pos_cu);
767 if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
768 continue;
769 }
770
771 BLI_kdtree_3d_range_search_cb_cpp(
772 root_points_kdtree_,
773 pos_cu,
774 minimum_distance_,
775 [&](const int other_curve_i, const float * /*co*/, float /*dist_sq*/) {
776 if (other_curve_i == curve_i) {
777 return true;
778 }
779 if (allow_remove_curve[other_curve_i]) {
780 curves_to_keep[other_curve_i] = false;
781 }
782 return true;
783 });
784 }
785 });
786 }
787};
788
790 const StrokeExtension &stroke_extension)
791{
793 executor.execute(*this, C, stroke_extension);
794}
795
799static bool use_add_density_mode(const BrushStrokeMode brush_mode,
800 const bContext &C,
801 const StrokeExtension &stroke_start)
802{
803 const Scene &scene = *CTX_data_scene(&C);
805 const Depsgraph &depsgraph = *CTX_data_depsgraph_on_load(&C);
806 const ARegion &region = *CTX_wm_region(&C);
807 const View3D &v3d = *CTX_wm_view3d(&C);
808
811 const bool use_invert = brush_mode == BRUSH_STROKE_INVERT;
812
813 if (density_mode == BRUSH_CURVES_SCULPT_DENSITY_MODE_ADD) {
814 return !use_invert;
815 }
816 if (density_mode == BRUSH_CURVES_SCULPT_DENSITY_MODE_REMOVE) {
817 return use_invert;
818 }
819
820 const Object &curves_ob_orig = *CTX_data_active_object(&C);
821 const Curves &curves_id_orig = *static_cast<Curves *>(curves_ob_orig.data);
822 Object *surface_ob_orig = curves_id_orig.surface;
823 if (surface_ob_orig == nullptr) {
824 return true;
825 }
826 Object *surface_ob_eval = DEG_get_evaluated(&depsgraph, surface_ob_orig);
827 if (surface_ob_eval == nullptr) {
828 return true;
829 }
830 const CurvesGeometry &curves = curves_id_orig.geometry.wrap();
831 if (curves.curves_num() <= 1) {
832 return true;
833 }
834 const Mesh *surface_mesh_eval = BKE_object_get_evaluated_mesh(surface_ob_eval);
835 if (surface_mesh_eval == nullptr) {
836 return true;
837 }
838
839 const CurvesSurfaceTransforms transforms(curves_ob_orig, curves_id_orig.surface);
840 bke::BVHTreeFromMesh surface_bvh_eval = surface_mesh_eval->bvh_corner_tris();
841
842 const float2 brush_pos_re = stroke_start.mouse_position;
843 /* Reduce radius so that only an inner circle is used to determine the existing density. */
844 const float brush_radius_re = BKE_brush_size_get(&scene, &brush) * 0.5f;
845
846 /* Find the surface point under the brush. */
847 const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(
848 depsgraph, region, v3d, transforms, surface_bvh_eval, brush_pos_re, brush_radius_re);
849 if (!brush_3d.has_value()) {
850 return true;
851 }
852
853 const float3 brush_pos_cu = brush_3d->position_cu;
854 const float brush_radius_cu = brush_3d->radius_cu;
855 const float brush_radius_sq_cu = pow2f(brush_radius_cu);
856
857 const bke::crazyspace::GeometryDeformation deformation =
859 const Span<int> offsets = curves.offsets();
860
861 /* Compute distance from brush to curve roots. */
862 Array<std::pair<float, int>> distances_sq_to_brush(curves.curves_num());
863 threading::EnumerableThreadSpecific<int> valid_curve_count_by_thread([&]() { return 0; });
864 threading::parallel_for(curves.curves_range(), 512, [&](const IndexRange range) {
865 int &valid_curve_count = valid_curve_count_by_thread.local();
866 for (const int curve_i : range) {
867 const int root_point_i = offsets[curve_i];
868 const float3 &root_pos_cu = deformation.positions[root_point_i];
869 const float dist_sq_cu = math::distance_squared(root_pos_cu, brush_pos_cu);
870 if (dist_sq_cu < brush_radius_sq_cu) {
871 distances_sq_to_brush[curve_i] = {math::distance_squared(root_pos_cu, brush_pos_cu),
872 curve_i};
873 valid_curve_count++;
874 }
875 else {
876 distances_sq_to_brush[curve_i] = {FLT_MAX, -1};
877 }
878 }
879 });
880 const int valid_curve_count = std::accumulate(
881 valid_curve_count_by_thread.begin(), valid_curve_count_by_thread.end(), 0);
882
883 /* Find a couple of curves that are closest to the brush center. */
884 const int check_curve_count = std::min<int>(8, valid_curve_count);
885 std::partial_sort(distances_sq_to_brush.begin(),
886 distances_sq_to_brush.begin() + check_curve_count,
887 distances_sq_to_brush.end());
888
889 /* Compute the minimum pair-wise distance between the curve roots that are close to the brush
890 * center. */
891 float min_dist_sq_cu = FLT_MAX;
892 for (const int i : IndexRange(check_curve_count)) {
893 const float3 &pos_i = deformation.positions[offsets[distances_sq_to_brush[i].second]];
894 for (int j = i + 1; j < check_curve_count; j++) {
895 const float3 &pos_j = deformation.positions[offsets[distances_sq_to_brush[j].second]];
896 const float dist_sq_cu = math::distance_squared(pos_i, pos_j);
897 math::min_inplace(min_dist_sq_cu, dist_sq_cu);
898 }
899 }
900
901 const float min_dist_cu = std::sqrt(min_dist_sq_cu);
902 if (min_dist_cu > brush.curves_sculpt_settings->minimum_distance) {
903 return true;
904 }
905
906 return false;
907}
908
909std::unique_ptr<CurvesSculptStrokeOperation> new_density_operation(
910 const BrushStrokeMode brush_mode, const bContext &C, const StrokeExtension &stroke_start)
911{
912 if (use_add_density_mode(brush_mode, C, stroke_start)) {
913 return std::make_unique<DensityAddOperation>();
914 }
915 return std::make_unique<DensitySubtractOperation>();
916}
917
918} // namespace blender::ed::sculpt_paint
int BKE_brush_size_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1210
float BKE_brush_curve_strength(eBrushCurvePreset preset, const CurveMapping *cumap, float distance, float brush_radius)
Definition brush.cc:1510
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:641
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
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)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
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
@ USER_ORBIT_SELECTION
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:639
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 C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:390
#define ND_DATA
Definition WM_types.hh:506
#define U
for(;discarded_id_iter !=nullptr;discarded_id_iter=static_cast< ID * >(discarded_id_iter->next))
Definition blendfile.cc:634
PyObject * self
BPy_StructRNA * depsgraph
long long int int64_t
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
static RandomNumberGenerator from_random_seed()
Definition rand.cc:288
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr bool is_empty() const
Definition BLI_span.hh:260
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(StringRef attribute_id) const
MutableAttributeAccessor attributes_for_write()
Span< float3 > positions() const
AttributeAccessor attributes() const
void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override
void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override
void foreach_segment(Fn &&fn) const
uint pos
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
GeometryDeformation get_evaluated_curves_deformation(const Object *ob_eval, const Object &ob_orig)
int sample_surface_points_projected(RandomNumberGenerator &rng, const Mesh &mesh, bke::BVHTreeFromMesh &mesh_bvhtree, const float2 &sample_pos_re, float sample_radius_re, FunctionRef< void(const float2 &pos_re, float3 &r_start, float3 &r_end)> region_position_to_ray, bool front_face_only, int tries_num, int max_points, Vector< float3 > &r_bary_coords, Vector< int > &r_tri_indices, Vector< float3 > &r_positions)
int sample_surface_points_spherical(RandomNumberGenerator &rng, const Mesh &mesh, Span< int > tris_to_sample, const float3 &sample_pos, float sample_radius, float approximate_density, Vector< float3 > &r_bary_coords, Vector< int > &r_tri_indices, Vector< float3 > &r_positions)
T sample_corner_attribute_with_bary_coords(const float3 &bary_weights, const int3 &corner_tri, const Span< T > corner_attribute)
CurvesGeometry curves_copy_curve_selection(const CurvesGeometry &curves, const IndexMask &curves_to_copy, const AttributeFilter &attribute_filter)
IndexMask retrieve_selected_curves(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
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 remember_stroke_position(Scene &scene, const float3 &brush_position_wo)
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)
std::optional< CurvesBrush3D > sample_curves_surface_3d_brush(const Depsgraph &depsgraph, const ARegion &region, const View3D &v3d, const CurvesSurfaceTransforms &transforms, const bke::BVHTreeFromMesh &surface_bvh, const float2 &brush_pos_re, const float brush_radius_re)
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:221
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:93
MatBase< float, 4, 4 > float4x4
VecBase< float, 2 > float2
void BLI_bvhtree_range_query_cpp(const BVHTree &tree, const float3 co, float radius, BVHTree_RangeQuery_CPP fn)
VecBase< float, 3 > float3
BrushStrokeMode
@ BRUSH_STROKE_INVERT
#define FLT_MAX
Definition stdcycles.h:14
struct BrushCurvesSculptSettings * curves_sculpt_settings
CurvesGeometry geometry
struct Object * surface
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)
i
Definition text_draw.cc:230
void WM_main_add_notifier(uint type, void *reference)