diff options
-rw-r--r-- | release/scripts/startup/bl_ui/space_view3d.py | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_curves_utils.hh | 26 | ||||
-rw-r--r-- | source/blender/blenkernel/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/curves_utils.cc | 38 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/curves_sculpt_add.cc | 97 | ||||
-rw-r--r-- | source/blender/geometry/intern/resample_curves.cc | 40 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_brush_enums.h | 1 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_brush.c | 7 |
8 files changed, 140 insertions, 72 deletions
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 122c7078c04..7bac7343bca 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -523,6 +523,7 @@ class _draw_tool_settings_context_mode: layout.prop(brush.curves_sculpt_settings, "curve_length") layout.prop(brush.curves_sculpt_settings, "interpolate_length") layout.prop(brush.curves_sculpt_settings, "interpolate_shape") + layout.prop(brush.curves_sculpt_settings, "interpolate_point_count") if brush.curves_sculpt_tool == 'GROW_SHRINK': layout.prop(brush, "direction", expand=True, text="") diff --git a/source/blender/blenkernel/BKE_curves_utils.hh b/source/blender/blenkernel/BKE_curves_utils.hh new file mode 100644 index 00000000000..62b060093e9 --- /dev/null +++ b/source/blender/blenkernel/BKE_curves_utils.hh @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BKE_curves.hh" + +/** \file + * \ingroup bke + * \brief Low-level operations for curves. + */ + +namespace blender::bke::curves { + +/** + * Copy the size of every curve in #curve_ranges to the corresponding index in #counts. + */ +void fill_curve_counts(const bke::CurvesGeometry &curves, + Span<IndexRange> curve_ranges, + MutableSpan<int> counts); + +/** + * Turn an array of sizes into the offset at each index including all previous sizes. + */ +void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets, int start_offset = 0); + +} // namespace blender::bke::curves diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index c9e88362b80..0b4f81df452 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -117,6 +117,7 @@ set(SRC intern/curveprofile.cc intern/curves.cc intern/curves_geometry.cc + intern/curves_utils.cc intern/customdata.cc intern/customdata_file.c intern/data_transfer.c @@ -356,6 +357,7 @@ set(SRC BKE_curveprofile.h BKE_curves.h BKE_curves.hh + BKE_curves_utils.hh BKE_customdata.h BKE_customdata_file.h BKE_data_transfer.h diff --git a/source/blender/blenkernel/intern/curves_utils.cc b/source/blender/blenkernel/intern/curves_utils.cc new file mode 100644 index 00000000000..78c2382b62f --- /dev/null +++ b/source/blender/blenkernel/intern/curves_utils.cc @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include "BKE_curves_utils.hh" + +namespace blender::bke::curves { + +void fill_curve_counts(const bke::CurvesGeometry &curves, + const Span<IndexRange> curve_ranges, + MutableSpan<int> counts) +{ + threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange ranges_range) { + for (const IndexRange curves_range : curve_ranges.slice(ranges_range)) { + threading::parallel_for(curves_range, 4096, [&](IndexRange range) { + for (const int i : range) { + counts[i] = curves.points_for_curve(i).size(); + } + }); + } + }); +} + +void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets, const int start_offset) +{ + int offset = start_offset; + for (const int i : counts_to_offsets.index_range().drop_back(1)) { + const int count = counts_to_offsets[i]; + BLI_assert(count > 0); + counts_to_offsets[i] = offset; + offset += count; + } + counts_to_offsets.last() = offset; +} + +} // namespace blender::bke::curves diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc index 6edc9194319..18bdbb88d8f 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc @@ -18,6 +18,7 @@ #include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" +#include "BKE_curves_utils.hh" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_paint.h" @@ -105,10 +106,11 @@ struct AddOperationExecutor { bool use_front_face_; bool interpolate_length_; bool interpolate_shape_; + bool interpolate_point_count_; bool use_interpolation_; float new_curve_length_; int add_amount_; - int points_per_curve_; + int constant_points_per_curve_; /** Various matrices to convert between coordinate spaces. */ float4x4 curves_to_world_mat_; @@ -129,6 +131,15 @@ struct AddOperationExecutor { Vector<int> looptri_indices; }; + struct NeighborInfo { + /* Curve index of the neighbor. */ + int index; + /* The weights of all neighbors of a new curve add up to 1. */ + float weight; + }; + static constexpr int max_neighbors = 5; + using NeighborsVector = Vector<NeighborInfo, max_neighbors>; + void execute(AddOperation &self, bContext *C, const StrokeExtension &stroke_extension) { self_ = &self; @@ -172,10 +183,12 @@ struct AddOperationExecutor { const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( brush_->falloff_shape); add_amount_ = std::max(0, brush_settings_->add_amount); - points_per_curve_ = std::max(2, brush_settings_->points_per_curve); + constant_points_per_curve_ = std::max(2, brush_settings_->points_per_curve); interpolate_length_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH; interpolate_shape_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE; - use_interpolation_ = interpolate_length_ || interpolate_shape_; + interpolate_point_count_ = brush_settings_->flag & + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT; + use_interpolation_ = interpolate_length_ || interpolate_shape_ || interpolate_point_count_; new_curve_length_ = brush_settings_->curve_length; tot_old_curves_ = curves_->curves_num(); @@ -215,18 +228,26 @@ struct AddOperationExecutor { return; } + Array<NeighborsVector> neighbors_per_curve; if (use_interpolation_) { this->ensure_curve_roots_kdtree(); + neighbors_per_curve = this->find_curve_neighbors(added_points); } + /* Resize to add the new curves, building the offests in the array owned by thge curves. */ const int tot_added_curves = added_points.bary_coords.size(); - const int tot_added_points = tot_added_curves * points_per_curve_; + curves_->resize(curves_->points_num(), curves_->curves_num() + tot_added_curves); + if (interpolate_point_count_) { + this->initialize_curve_offsets_with_interpolation(neighbors_per_curve); + } + else { + this->initialize_curve_offsets_without_interpolation(constant_points_per_curve_); + } - curves_->resize(curves_->points_num() + tot_added_points, - curves_->curves_num() + tot_added_curves); + /* Resize to add the correct point count calculated as part of building the offsets. */ + curves_->resize(curves_->offsets().last(), curves_->curves_num()); - threading::parallel_invoke([&]() { this->initialize_curve_offsets(tot_added_curves); }, - [&]() { this->initialize_attributes(added_points); }); + this->initialize_attributes(added_points, neighbors_per_curve); curves_->update_curve_types(); @@ -580,33 +601,37 @@ struct AddOperationExecutor { } } - void initialize_curve_offsets(const int tot_added_curves) + void initialize_curve_offsets_with_interpolation(const Span<NeighborsVector> neighbors_per_curve) { - MutableSpan<int> offsets = curves_->offsets_for_write(); - threading::parallel_for(IndexRange(tot_added_curves), 1024, [&](const IndexRange range) { - for (const int i : range) { - const int curve_i = tot_old_curves_ + i; - offsets[curve_i + 1] = tot_old_points_ + (i + 1) * points_per_curve_; + MutableSpan<int> new_offsets = curves_->offsets_for_write().drop_front(tot_old_curves_); + + attribute_math::DefaultMixer<int> mixer{new_offsets}; + threading::parallel_for(neighbors_per_curve.index_range(), 1024, [&](IndexRange curves_range) { + for (const int i : curves_range) { + for (const NeighborInfo &neighbor : neighbors_per_curve[i]) { + const int neighbor_points_num = curves_->points_for_curve(neighbor.index).size(); + mixer.mix_in(i, neighbor_points_num, neighbor.weight); + } } }); - } + mixer.finalize(); - struct NeighborInfo { - /* Curve index of the neighbor. */ - int index; - /* The weights of all neighbors of a new curve add up to 1. */ - float weight; - }; - static constexpr int max_neighbors = 5; - using NeighborsVector = Vector<NeighborInfo, max_neighbors>; + bke::curves::accumulate_counts_to_offsets(new_offsets, tot_old_points_); + } - void initialize_attributes(const AddedPoints &added_points) + void initialize_curve_offsets_without_interpolation(const int points_per_curve) { - Array<NeighborsVector> neighbors_per_curve; - if (use_interpolation_) { - neighbors_per_curve = this->find_curve_neighbors(added_points); + MutableSpan<int> new_offsets = curves_->offsets_for_write().drop_front(tot_old_curves_); + int offset = tot_old_points_; + for (const int i : new_offsets.index_range()) { + new_offsets[i] = offset; + offset += points_per_curve; } + } + void initialize_attributes(const AddedPoints &added_points, + const Span<NeighborsVector> neighbors_per_curve) + { Array<float> new_lengths_cu(added_points.bary_coords.size()); if (interpolate_length_) { this->interpolate_lengths(neighbors_per_curve, new_lengths_cu); @@ -735,15 +760,14 @@ struct AddOperationExecutor { threading::parallel_for( added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { for (const int i : range) { - const int first_point_i = tot_old_points_ + i * points_per_curve_; + const IndexRange points = curves_->points_for_curve(tot_old_curves_ + i); const float3 &root_cu = added_points.positions_cu[i]; const float length = lengths_cu[i]; const float3 &normal_su = normals_su[i]; const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); const float3 tip_cu = root_cu + length * normal_cu; - initialize_straight_curve_positions( - root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_)); + initialize_straight_curve_positions(root_cu, tip_cu, positions_cu.slice(points)); } }); } @@ -764,23 +788,22 @@ struct AddOperationExecutor { added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { for (const int i : range) { const Span<NeighborInfo> neighbors = neighbors_per_curve[i]; + const IndexRange points = curves_->points_for_curve(tot_old_curves_ + i); const float length_cu = new_lengths_cu[i]; const float3 &normal_su = new_normals_su[i]; const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); const float3 &root_cu = added_points.positions_cu[i]; - const int first_point_i = tot_old_points_ + i * points_per_curve_; if (neighbors.is_empty()) { /* If there are no neighbors, just make a straight line. */ const float3 tip_cu = root_cu + length_cu * normal_cu; - initialize_straight_curve_positions( - root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_)); + initialize_straight_curve_positions(root_cu, tip_cu, positions_cu.slice(points)); continue; } - positions_cu.slice(first_point_i, points_per_curve_).fill(root_cu); + positions_cu.slice(points).fill(root_cu); for (const NeighborInfo &neighbor : neighbors) { const int neighbor_curve_i = neighbor.index; @@ -814,8 +837,8 @@ struct AddOperationExecutor { const float neighbor_length_cu = neighbor_spline.length(); const float length_factor = std::min(1.0f, length_cu / neighbor_length_cu); - const float resample_factor = (1.0f / (points_per_curve_ - 1.0f)) * length_factor; - for (const int j : IndexRange(points_per_curve_)) { + const float resample_factor = (1.0f / (points.size() - 1.0f)) * length_factor; + for (const int j : IndexRange(points.size())) { const Spline::LookupResult lookup = neighbor_spline.lookup_evaluated_factor( j * resample_factor); const float index_factor = lookup.evaluated_index + lookup.factor; @@ -825,7 +848,7 @@ struct AddOperationExecutor { const float3 relative_coord = p - neighbor_root_cu; float3 rotated_relative_coord = relative_coord; mul_m3_v3(normal_rotation_cu, rotated_relative_coord); - positions_cu[first_point_i + j] += neighbor.weight * rotated_relative_coord; + positions_cu[points[j]] += neighbor.weight * rotated_relative_coord; } } } diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc index 5eba3ff3b3a..7895225a189 100644 --- a/source/blender/geometry/intern/resample_curves.cc +++ b/source/blender/geometry/intern/resample_curves.cc @@ -8,6 +8,7 @@ #include "BKE_attribute_math.hh" #include "BKE_curves.hh" +#include "BKE_curves_utils.hh" #include "BKE_geometry_fields.hh" #include "GEO_resample_curves.hh" @@ -196,37 +197,6 @@ static void copy_between_curves(const bke::CurvesGeometry &src_curves, }); } -/** - * Copy the size of every curve in #curve_ranges to the corresponding index in #counts. - */ -static void fill_curve_counts(const bke::CurvesGeometry &src_curves, - const Span<IndexRange> curve_ranges, - MutableSpan<int> counts) -{ - threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange ranges_range) { - for (const IndexRange curves_range : curve_ranges.slice(ranges_range)) { - for (const int i : curves_range) { - counts[i] = src_curves.points_for_curve(i).size(); - } - } - }); -} - -/** - * Turn an array of sizes into the offset at each index including all previous sizes. - */ -static void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets) -{ - int total = 0; - for (const int i : counts_to_offsets.index_range().drop_back(1)) { - const int count = counts_to_offsets[i]; - BLI_assert(count > 0); - counts_to_offsets[i] = total; - total += count; - } - counts_to_offsets.last() = total; -} - static Curves *resample_to_uniform(const CurveComponent &src_component, const fn::Field<bool> &selection_field, const fn::Field<int> &count_field) @@ -257,8 +227,8 @@ static Curves *resample_to_uniform(const CurveComponent &src_component, src_curves.curves_range(), nullptr); /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */ - fill_curve_counts(src_curves, unselected_ranges, dst_offsets); - accumulate_counts_to_offsets(dst_offsets); + bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_offsets); + bke::curves::accumulate_counts_to_offsets(dst_offsets); dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); /* All resampled curves are poly curves. */ @@ -428,8 +398,8 @@ Curves *resample_to_evaluated(const CurveComponent &src_component, dst_offsets[i] = src_curves.evaluated_points_for_curve(i).size(); } }); - fill_curve_counts(src_curves, unselected_ranges, dst_offsets); - accumulate_counts_to_offsets(dst_offsets); + bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_offsets); + bke::curves::accumulate_counts_to_offsets(dst_offsets); dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 1d5f1351de0..f409d1c0442 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -618,6 +618,7 @@ typedef enum eBrushCurvesSculptFlag { BRUSH_CURVES_SCULPT_FLAG_GROW_SHRINK_INVERT = (1 << 1), BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH = (1 << 2), BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE = (1 << 3), + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT = (1 << 4), } eBrushCurvesSculptFlag; #define MAX_BRUSH_PIXEL_RADIUS 500 diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index f7edba24dea..9161fee2584 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -1975,6 +1975,13 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Interpolate Length", "Use length of the curves in close proximity"); + prop = RNA_def_property(srna, "interpolate_point_count", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT); + RNA_def_property_ui_text(prop, + "Interpolate Point Count", + "Use the number of points from the curves in close proximity"); + prop = RNA_def_property(srna, "interpolate_shape", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE); RNA_def_property_ui_text( |