Blender V5.0
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
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(curves_sculpt_->paint, *brush_, stroke_extension);
157 brush_radius_re_ = brush_radius_get(curves_sculpt_->paint, *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 math::transform_points(transforms_.surface_to_curves, new_positions_cu);
177
178 if (stroke_extension.is_first) {
180 }
181
182 const int already_added_curves = self_->new_deformed_root_positions_.size();
183 KDTree_3d *new_roots_kdtree = BLI_kdtree_3d_new(already_added_curves +
184 new_positions_cu.size());
185 BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(new_roots_kdtree); });
186
187 /* Used to tag all curves that are too close to existing curves or too close to other new
188 * curves. */
189 Array<bool> new_curve_skipped(new_positions_cu.size(), false);
191 512 < already_added_curves + new_positions_cu.size(),
192 /* Build kdtree from root points created by the current stroke. */
193 [&]() {
194 for (const int i : IndexRange(already_added_curves)) {
195 BLI_kdtree_3d_insert(new_roots_kdtree, -1, self_->new_deformed_root_positions_[i]);
196 }
197 for (const int new_i : new_positions_cu.index_range()) {
198 const float3 &root_pos_cu = new_positions_cu[new_i];
199 BLI_kdtree_3d_insert(new_roots_kdtree, new_i, root_pos_cu);
200 }
201 BLI_kdtree_3d_balance(new_roots_kdtree);
202 },
203 /* Check which new root points are close to roots that existed before the current stroke
204 * started. */
205 [&]() {
207 new_positions_cu.index_range(), 128, [&](const IndexRange range) {
208 for (const int new_i : range) {
209 const float3 &new_root_pos_cu = new_positions_cu[new_i];
210 KDTreeNearest_3d nearest;
211 nearest.dist = FLT_MAX;
212 BLI_kdtree_3d_find_nearest(
213 self_->deformed_curve_roots_kdtree_, new_root_pos_cu, &nearest);
214 if (nearest.dist < brush_settings_->minimum_distance) {
215 new_curve_skipped[new_i] = true;
216 }
217 }
218 });
219 });
220
221 /* Find new points that are too close too other new points. */
222 for (const int new_i : new_positions_cu.index_range()) {
223 if (new_curve_skipped[new_i]) {
224 continue;
225 }
226 const float3 &root_pos_cu = new_positions_cu[new_i];
227 BLI_kdtree_3d_range_search_cb_cpp(
228 new_roots_kdtree,
229 root_pos_cu,
230 brush_settings_->minimum_distance,
231 [&](const int other_new_i, const float * /*co*/, float /*dist_sq*/) {
232 if (other_new_i == -1) {
233 new_curve_skipped[new_i] = true;
234 return false;
235 }
236 if (new_i == other_new_i) {
237 return true;
238 }
239 new_curve_skipped[other_new_i] = true;
240 return true;
241 });
242 }
243
244 /* Remove points that are too close to others. */
245 for (int64_t i = new_positions_cu.size() - 1; i >= 0; i--) {
246 if (new_curve_skipped[i]) {
247 new_positions_cu.remove_and_reorder(i);
248 new_uvs.remove_and_reorder(i);
249 }
250 }
251 self_->new_deformed_root_positions_.extend(new_positions_cu);
252
253 const Span<float3> corner_normals_su = surface_orig_->corner_normals();
254 const Span<int3> surface_corner_tris_orig = surface_orig_->corner_tris();
255 const geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_corner_tris_orig};
256
257 geometry::AddCurvesOnMeshInputs add_inputs;
258 add_inputs.uvs = new_uvs;
259 add_inputs.interpolate_length = brush_settings_->flag &
261 add_inputs.interpolate_radius = brush_settings_->flag &
263 add_inputs.interpolate_shape = brush_settings_->flag &
265 add_inputs.interpolate_point_count = brush_settings_->flag &
267 add_inputs.interpolate_resolution = curves_orig_->attributes().contains("resolution");
268 add_inputs.fallback_curve_length = brush_settings_->curve_length;
269 add_inputs.fallback_curve_radius = brush_settings_->curve_radius;
270 add_inputs.fallback_point_count = std::max(2, brush_settings_->points_per_curve);
271 add_inputs.transforms = &transforms_;
272 add_inputs.surface = surface_orig_;
273 add_inputs.corner_normals_su = corner_normals_su;
274 add_inputs.surface_corner_tris = surface_corner_tris_orig;
275 add_inputs.reverse_uv_sampler = &reverse_uv_sampler;
276 add_inputs.old_roots_kdtree = self_->original_curve_roots_kdtree_;
277
278 const geometry::AddCurvesOnMeshOutputs add_outputs = geometry::add_curves_on_mesh(
279 *curves_orig_, add_inputs);
280 bke::MutableAttributeAccessor attributes = curves_orig_->attributes_for_write();
281 if (bke::GSpanAttributeWriter selection = attributes.lookup_for_write_span(".selection")) {
282 curves::fill_selection_true(selection.span.slice(selection.domain == bke::AttrDomain::Point ?
283 add_outputs.new_points_range :
284 add_outputs.new_curves_range));
285 selection.finish();
286 }
287 if (U.uiflag & USER_ORBIT_SELECTION) {
288 if (const std::optional<Bounds<float3>> center_cu = bounds::min_max(
289 curves_orig_->positions().slice(add_outputs.new_points_range)))
290 {
292 *curves_sculpt_,
293 math::transform_point(transforms_.curves_to_world, center_cu->center()));
294 }
295 }
296
297 if (add_outputs.uv_error) {
298 report_invalid_uv_map(stroke_extension.reports);
299 }
300
301 DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY);
302 WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id);
304 }
305
307 {
308 const bke::crazyspace::GeometryDeformation deformation =
310 const Span<int> curve_offsets = curves_orig_->offsets();
311 const Span<float3> original_positions = curves_orig_->positions();
312 const Span<float3> deformed_positions = deformation.positions;
313 BLI_assert(original_positions.size() == deformed_positions.size());
314
315 auto roots_kdtree_from_positions = [&](const Span<float3> positions) {
316 KDTree_3d *kdtree = BLI_kdtree_3d_new(curves_orig_->curves_num());
317 for (const int curve_i : curves_orig_->curves_range()) {
318 const int root_point_i = curve_offsets[curve_i];
319 BLI_kdtree_3d_insert(kdtree, curve_i, positions[root_point_i]);
320 }
321 BLI_kdtree_3d_balance(kdtree);
322 return kdtree;
323 };
324
326 1024 < original_positions.size() + deformed_positions.size(),
327 [&]() {
328 self_->original_curve_roots_kdtree_ = roots_kdtree_from_positions(original_positions);
329 },
330 [&]() {
331 self_->deformed_curve_roots_kdtree_ = roots_kdtree_from_positions(deformed_positions);
332 });
333 }
334
336 Vector<float2> &r_uvs,
337 Vector<float3> &r_positions_su)
338 {
340
341 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
343 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
344 const float4x4 brush_transform_inv = math::invert(brush_transform);
345 const float4x4 transform = transforms_.curves_to_surface * brush_transform *
346 transforms_.world_to_curves;
347 Vector<float3> positions_su;
348 Vector<float3> bary_coords;
349 Vector<int> tri_indices;
351 rng,
356 [&](const float2 &pos_re, float3 &r_start_su, float3 &r_end_su) {
357 float3 start_wo, end_wo;
359 ctx_.depsgraph, ctx_.region, ctx_.v3d, pos_re, start_wo, end_wo, true);
360 r_start_su = math::transform_point(transform, start_wo);
361 r_end_su = math::transform_point(transform, end_wo);
362 },
363 true,
364 brush_settings_->density_add_attempts,
365 brush_settings_->density_add_attempts,
366 bary_coords,
367 tri_indices,
368 positions_su);
369
370 /* Remove some sampled points randomly based on the brush falloff and strength. */
371 for (int i = new_points - 1; i >= 0; i--) {
372 const float3 pos_su = positions_su[i];
373 const float3 pos_cu = math::transform_point(
374 brush_transform_inv, math::transform_point(transforms_.surface_to_curves, pos_su));
375 const float2 pos_re = ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, projection);
376 const float dist_to_brush_re = math::distance(brush_pos_re_, pos_re);
377 const float radius_falloff = BKE_brush_curve_strength(
378 brush_, dist_to_brush_re, brush_radius_re_);
379 const float weight = brush_strength_ * radius_falloff;
380 if (rng.get_float() > weight) {
381 bary_coords.remove_and_reorder(i);
382 tri_indices.remove_and_reorder(i);
383 positions_su.remove_and_reorder(i);
384 }
385 }
386
387 for (const int i : bary_coords.index_range()) {
389 bary_coords[i], surface_corner_tris_eval_[tri_indices[i]], surface_uv_map_eval_);
390 r_uvs.append(uv);
391 }
392 r_positions_su.extend(positions_su);
393 }
394 }
395
397 Vector<float2> &r_uvs,
398 Vector<float3> &r_positions_su)
399 {
400 const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph,
401 *ctx_.region,
402 *ctx_.v3d,
407 if (!brush_3d.has_value()) {
408 return;
409 }
410
411 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
413 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
414 const float3 brush_pos_cu = math::transform_point(brush_transform, brush_3d->position_cu);
415 const float3 brush_pos_su = math::transform_point(transforms_.curves_to_surface,
416 brush_pos_cu);
417 const float brush_radius_su = transform_brush_radius(
418 transforms_.curves_to_surface, brush_pos_cu, brush_3d->radius_cu);
419 const float brush_radius_sq_su = pow2f(brush_radius_su);
420
421 Vector<int> selected_corner_tri_indices;
423 *surface_bvh_eval_.tree,
424 brush_pos_su,
425 brush_radius_su,
426 [&](const int index, const float3 & /*co*/, const float /*dist_sq*/) {
427 selected_corner_tri_indices.append(index);
428 });
429
430 const float brush_plane_area_su = M_PI * brush_radius_sq_su;
431 const float approximate_density_su = brush_settings_->density_add_attempts /
432 brush_plane_area_su;
433
434 Vector<float3> positions_su;
435 Vector<float3> bary_coords;
436 Vector<int> tri_indices;
438 rng,
440 selected_corner_tri_indices,
441 brush_pos_su,
442 brush_radius_su,
443 approximate_density_su,
444 bary_coords,
445 tri_indices,
446 positions_su);
447
448 /* Remove some sampled points randomly based on the brush falloff and strength. */
449 for (int i = new_points - 1; i >= 0; i--) {
450 const float3 pos_su = positions_su[i];
451 const float3 pos_cu = math::transform_point(transforms_.surface_to_curves, pos_su);
452 const float dist_to_brush_cu = math::distance(pos_cu, brush_pos_cu);
453 const float radius_falloff = BKE_brush_curve_strength(
454 brush_, dist_to_brush_cu, brush_3d->radius_cu);
455 const float weight = brush_strength_ * radius_falloff;
456 if (rng.get_float() > weight) {
457 bary_coords.remove_and_reorder(i);
458 tri_indices.remove_and_reorder(i);
459 positions_su.remove_and_reorder(i);
460 }
461 }
462
463 for (const int i : bary_coords.index_range()) {
465 bary_coords[i], surface_corner_tris_eval_[tri_indices[i]], surface_uv_map_eval_);
466 r_uvs.append(uv);
467 }
468 r_positions_su.extend(positions_su);
469 }
470 }
471};
472
474 const StrokeExtension &stroke_extension)
475{
477 executor.execute(*this, C, stroke_extension);
478}
479
481 private:
483
489 Vector<float3> deformed_root_positions_;
490
491 public:
492 void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
493};
494
502
503 Object *object_ = nullptr;
504 Curves *curves_id_ = nullptr;
506
509
511 Mesh *surface_orig_ = nullptr;
512
514 Mesh *surface_eval_ = nullptr;
516
517 const CurvesSculpt *curves_sculpt_ = nullptr;
518 const Brush *brush_ = nullptr;
523
525
527
529
531
533 const bContext &C,
534 const StrokeExtension &stroke_extension)
535 {
536 self_ = &self;
537
539
540 curves_id_ = static_cast<Curves *>(object_->data);
541 curves_ = &curves_id_->geometry.wrap();
542 if (curves_->is_empty()) {
543 return;
544 }
545
546 surface_ob_orig_ = curves_id_->surface;
547 if (surface_ob_orig_ == nullptr) {
548 return;
549 }
550 surface_orig_ = static_cast<Mesh *>(surface_ob_orig_->data);
551
553 if (surface_ob_eval_ == nullptr) {
554 return;
555 }
557
558 surface_bvh_eval_ = surface_eval_->bvh_corner_tris();
559
560 curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
563 brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension);
564 brush_strength_ = brush_strength_get(curves_sculpt_->paint, *brush_, stroke_extension);
565 brush_pos_re_ = stroke_extension.mouse_position;
566
567 minimum_distance_ = brush_->curves_sculpt_settings->minimum_distance;
568
570
572 const eBrushFalloffShape falloff_shape = eBrushFalloffShape(brush_->falloff_shape);
573
574 if (stroke_extension.is_first) {
575 const bke::crazyspace::GeometryDeformation deformation =
577 for (const int curve_i : curves_->curves_range()) {
578 const int first_point_i = curves_->offsets()[curve_i];
579 self_->deformed_root_positions_.append(deformation.positions[first_point_i]);
580 }
581 }
582
583 root_points_kdtree_ = BLI_kdtree_3d_new(curve_selection_.size());
584 BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(root_points_kdtree_); });
585 curve_selection_.foreach_index([&](const int curve_i) {
586 const float3 &pos_cu = self_->deformed_root_positions_[curve_i];
587 BLI_kdtree_3d_insert(root_points_kdtree_, curve_i, pos_cu);
588 });
589 BLI_kdtree_3d_balance(root_points_kdtree_);
590
591 /* Find all curves that should be deleted. */
592 Array<bool> curves_to_keep(curves_->curves_num(), true);
593 if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
594 this->reduce_density_projected_with_symmetry(curves_to_keep);
595 }
596 else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
597 this->reduce_density_spherical_with_symmetry(curves_to_keep);
598 }
599 else {
601 }
602
603 IndexMaskMemory mask_memory;
604 const IndexMask mask_to_keep = IndexMask::from_bools(curves_to_keep, mask_memory);
605
606 /* Remove deleted curves from the stored deformed root positions. */
607 BLI_assert(curves_->curves_num() == self_->deformed_root_positions_.size());
608 Vector<float3> new_deformed_positions(mask_to_keep.size());
609 array_utils::gather(self_->deformed_root_positions_.as_span(),
610 mask_to_keep,
611 new_deformed_positions.as_mutable_span());
612 self_->deformed_root_positions_ = std::move(new_deformed_positions);
613
614 *curves_ = bke::curves_copy_curve_selection(*curves_, mask_to_keep, {});
615 BLI_assert(curves_->curves_num() == self_->deformed_root_positions_.size());
616
620 }
621
623 {
624 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
626 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
627 this->reduce_density_projected(brush_transform, curves_to_keep);
628 }
629 }
630
631 void reduce_density_projected(const float4x4 &brush_transform, MutableSpan<bool> curves_to_keep)
632 {
633 const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
634 const float brush_radius_sq_re = pow2f(brush_radius_re);
635
636 const float4x4 projection = ED_view3d_ob_project_mat_get(ctx_.rv3d, object_);
637
638 /* Randomly select the curves that are allowed to be removed, based on the brush radius and
639 * strength. */
640 Array<bool> allow_remove_curve(curves_->curves_num(), false);
641 threading::parallel_for(curves_->curves_range(), 512, [&](const IndexRange range) {
642 RandomNumberGenerator rng = RandomNumberGenerator::from_random_seed();
643
644 for (const int curve_i : range) {
645 if (!curves_to_keep[curve_i]) {
646 allow_remove_curve[curve_i] = true;
647 continue;
648 }
649 const float3 pos_cu = math::transform_point(brush_transform,
650 self_->deformed_root_positions_[curve_i]);
651
652 const float2 pos_re = ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, projection);
653 const float dist_to_brush_sq_re = math::distance_squared(brush_pos_re_, pos_re);
654 if (dist_to_brush_sq_re > brush_radius_sq_re) {
655 continue;
656 }
657 const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re);
658 const float radius_falloff = BKE_brush_curve_strength(
659 brush_, dist_to_brush_re, brush_radius_re);
660 const float weight = brush_strength_ * radius_falloff;
661 if (rng.get_float() < weight) {
662 allow_remove_curve[curve_i] = true;
663 }
664 }
665 });
666
667 /* Detect curves that are too close to other existing curves. */
668 curve_selection_.foreach_segment([&](const IndexMaskSegment segment) {
669 for (const int curve_i : segment) {
670 if (!curves_to_keep[curve_i]) {
671 continue;
672 }
673 if (!allow_remove_curve[curve_i]) {
674 continue;
675 }
676 const float3 orig_pos_cu = self_->deformed_root_positions_[curve_i];
677 const float3 pos_cu = math::transform_point(brush_transform, orig_pos_cu);
678 const float2 pos_re = ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, projection);
679 const float dist_to_brush_sq_re = math::distance_squared(brush_pos_re_, pos_re);
680 if (dist_to_brush_sq_re > brush_radius_sq_re) {
681 continue;
682 }
683 BLI_kdtree_3d_range_search_cb_cpp(
684 root_points_kdtree_,
685 orig_pos_cu,
686 minimum_distance_,
687 [&](const int other_curve_i, const float * /*co*/, float /*dist_sq*/) {
688 if (other_curve_i == curve_i) {
689 return true;
690 }
691 if (allow_remove_curve[other_curve_i]) {
692 curves_to_keep[other_curve_i] = false;
693 }
694 return true;
695 });
696 }
697 });
698 }
699
701 {
702 const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
703 const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph,
704 *ctx_.region,
705 *ctx_.v3d,
709 brush_radius_re);
710 if (!brush_3d.has_value()) {
711 return;
712 }
713
714 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
716 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
717 const float3 brush_pos_cu = math::transform_point(brush_transform, brush_3d->position_cu);
718 this->reduce_density_spherical(brush_pos_cu, brush_3d->radius_cu, curves_to_keep);
719 }
720 }
721
722 void reduce_density_spherical(const float3 &brush_pos_cu,
723 const float brush_radius_cu,
724 MutableSpan<bool> curves_to_keep)
725 {
726 const float brush_radius_sq_cu = pow2f(brush_radius_cu);
727
728 /* Randomly select the curves that are allowed to be removed, based on the brush radius and
729 * strength. */
730 Array<bool> allow_remove_curve(curves_->curves_num(), false);
731 threading::parallel_for(curves_->curves_range(), 512, [&](const IndexRange range) {
732 RandomNumberGenerator rng = RandomNumberGenerator::from_random_seed();
733
734 for (const int curve_i : range) {
735 if (!curves_to_keep[curve_i]) {
736 allow_remove_curve[curve_i] = true;
737 continue;
738 }
739 const float3 pos_cu = self_->deformed_root_positions_[curve_i];
740
741 const float dist_to_brush_sq_cu = math::distance_squared(brush_pos_cu, pos_cu);
742 if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
743 continue;
744 }
745 const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu);
746 const float radius_falloff = BKE_brush_curve_strength(
747 brush_, dist_to_brush_cu, brush_radius_cu);
748 const float weight = brush_strength_ * radius_falloff;
749 if (rng.get_float() < weight) {
750 allow_remove_curve[curve_i] = true;
751 }
752 }
753 });
754
755 /* Detect curves that are too close to other existing curves. */
756 curve_selection_.foreach_segment([&](const IndexMaskSegment segment) {
757 for (const int curve_i : segment) {
758 if (!curves_to_keep[curve_i]) {
759 continue;
760 }
761 if (!allow_remove_curve[curve_i]) {
762 continue;
763 }
764 const float3 &pos_cu = self_->deformed_root_positions_[curve_i];
765 const float dist_to_brush_sq_cu = math::distance_squared(pos_cu, brush_pos_cu);
766 if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
767 continue;
768 }
769
770 BLI_kdtree_3d_range_search_cb_cpp(
771 root_points_kdtree_,
772 pos_cu,
773 minimum_distance_,
774 [&](const int other_curve_i, const float * /*co*/, float /*dist_sq*/) {
775 if (other_curve_i == curve_i) {
776 return true;
777 }
778 if (allow_remove_curve[other_curve_i]) {
779 curves_to_keep[other_curve_i] = false;
780 }
781 return true;
782 });
783 }
784 });
785 }
786};
787
789 const StrokeExtension &stroke_extension)
790{
792 executor.execute(*this, C, stroke_extension);
793}
794
798static bool use_add_density_mode(const BrushStrokeMode brush_mode,
799 const bContext &C,
800 const StrokeExtension &stroke_start)
801{
802 const Scene &scene = *CTX_data_scene(&C);
803 const Paint &paint = scene.toolsettings->curves_sculpt->paint;
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_radius_get(&paint, &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
float BKE_brush_curve_strength(eBrushCurvePreset preset, const CurveMapping *cumap, float distance, float brush_radius)
Definition brush.cc:1577
float BKE_brush_radius_get(const Paint *paint, const Brush *brush)
Definition brush.cc:1272
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:650
#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:1074
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:618
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:393
#define ND_DATA
Definition WM_types.hh:509
#define U
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
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)
float brush_strength_get(const Paint &paint, const Brush &brush, const StrokeExtension &stroke_extension)
void report_empty_evaluated_surface(ReportList *reports)
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 remember_stroke_position(CurvesSculpt &curves_sculpt, const float3 &brush_position_wo)
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)
float brush_radius_get(const Paint &paint, const Brush &brush, const StrokeExtension &stroke_extension)
void report_empty_original_surface(ReportList *reports)
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)
void transform_points(const float4x4 &transform, MutableSpan< float3 > points, bool use_threading=true)
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)