Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc
| /* SPDX-License-Identifier: GPL-2.0-or-later */ | /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||||
| #include <algorithm> | #include <algorithm> | ||||
| #include "BLI_enumerable_thread_specific.hh" | #include "BLI_enumerable_thread_specific.hh" | ||||
| #include "BLI_float4x4.hh" | #include "BLI_float4x4.hh" | ||||
| #include "BLI_kdtree.h" | #include "BLI_length_parameterize.hh" | ||||
| #include "BLI_rand.hh" | |||||
| #include "BLI_vector.hh" | #include "BLI_vector.hh" | ||||
| #include "PIL_time.h" | #include "PIL_time.h" | ||||
| #include "DEG_depsgraph.h" | #include "DEG_depsgraph.h" | ||||
| #include "BKE_attribute_math.hh" | #include "BKE_attribute_math.hh" | ||||
| #include "BKE_brush.h" | #include "BKE_brush.h" | ||||
| #include "BKE_bvhutils.h" | |||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_curves.hh" | #include "BKE_curves.hh" | ||||
| #include "BKE_mesh.h" | |||||
| #include "BKE_mesh_runtime.h" | |||||
| #include "BKE_paint.h" | #include "BKE_paint.h" | ||||
| #include "BKE_spline.hh" | |||||
| #include "DNA_brush_enums.h" | #include "DNA_brush_enums.h" | ||||
| #include "DNA_brush_types.h" | #include "DNA_brush_types.h" | ||||
| #include "DNA_curves_types.h" | #include "DNA_curves_types.h" | ||||
| #include "DNA_mesh_types.h" | |||||
| #include "DNA_meshdata_types.h" | |||||
| #include "DNA_object_types.h" | #include "DNA_object_types.h" | ||||
| #include "DNA_screen_types.h" | #include "DNA_screen_types.h" | ||||
| #include "DNA_space_types.h" | #include "DNA_space_types.h" | ||||
| #include "ED_screen.h" | #include "ED_screen.h" | ||||
| #include "ED_view3d.h" | #include "ED_view3d.h" | ||||
| #include "WM_api.h" | #include "WM_api.h" | ||||
| #include "curves_sculpt_intern.hh" | #include "curves_sculpt_intern.hh" | ||||
| /** | /** | ||||
| * The code below uses a suffix naming convention to indicate the coordinate space: | * The code below uses a suffix naming convention to indicate the coordinate space: | ||||
| * - `cu`: Local space of the curves object that is being edited. | * - `cu`: Local space of the curves object that is being edited. | ||||
| * - `su`: Local space of the surface object. | * - `su`: Local space of the surface object. | ||||
| * - `wo`: World space. | * - `wo`: World space. | ||||
| * - `re`: 2D coordinates within the region. | * - `re`: 2D coordinates within the region. | ||||
| */ | */ | ||||
| namespace blender::ed::sculpt_paint { | namespace blender::ed::sculpt_paint { | ||||
| using bke::CurvesGeometry; | using bke::CurvesGeometry; | ||||
| namespace lp = length_parameterize; | |||||
| /** | /** | ||||
| * Utility class to wrap different grow/shrink behaviors. | * Utility class to wrap different grow/shrink behaviors. | ||||
| * It might be useful to use this for other future brushes as well, but better see if this | * It might be useful to use this for other future brushes as well, but better see if this | ||||
| * abstraction holds up for a while before using it in more places. | * abstraction holds up for a while before using it in more places. | ||||
| */ | */ | ||||
| class CurvesEffect { | class CurvesEffect { | ||||
| public: | public: | ||||
| virtual ~CurvesEffect() = default; | virtual ~CurvesEffect() = default; | ||||
| virtual void execute(CurvesGeometry &curves, | virtual void execute(CurvesGeometry &curves, | ||||
| Span<int> curve_indices, | Span<int> curve_indices, | ||||
| Span<float> move_distances_cu) = 0; | Span<float> move_distances_cu) = 0; | ||||
| }; | }; | ||||
| /** | /** | ||||
| * Make curves smaller by trimming the end off. | * Make curves smaller by trimming the end off. | ||||
| */ | */ | ||||
| class ShrinkCurvesEffect : public CurvesEffect { | class ShrinkCurvesEffect : public CurvesEffect { | ||||
| private: | private: | ||||
| const Brush &brush_; | const Brush &brush_; | ||||
| /** Storage of per-curve parameterization data to avoid reallocation. */ | |||||
| struct ParameterizationBuffers { | |||||
| Array<float3> old_positions; | |||||
| Array<float> old_lengths; | |||||
| Array<float> sample_lengths; | |||||
| Array<int> indices; | |||||
| Array<float> factors; | |||||
| void reinitialize(const int points_num) | |||||
| { | |||||
| this->old_positions.reinitialize(points_num); | |||||
| this->old_lengths.reinitialize(lp::lengths_num(points_num, false)); | |||||
| this->sample_lengths.reinitialize(points_num); | |||||
| this->indices.reinitialize(points_num); | |||||
| this->factors.reinitialize(points_num); | |||||
| } | |||||
| }; | |||||
| public: | public: | ||||
| ShrinkCurvesEffect(const Brush &brush) : brush_(brush) | ShrinkCurvesEffect(const Brush &brush) : brush_(brush) | ||||
| { | { | ||||
| } | } | ||||
| void execute(CurvesGeometry &curves, | void execute(CurvesGeometry &curves, | ||||
| const Span<int> curve_indices, | const Span<int> curve_indices, | ||||
| const Span<float> move_distances_cu) override | const Span<float> move_distances_cu) override | ||||
| { | { | ||||
| MutableSpan<float3> positions_cu = curves.positions_for_write(); | MutableSpan<float3> positions_cu = curves.positions_for_write(); | ||||
| threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) { | threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) { | ||||
| ParameterizationBuffers data; | |||||
| for (const int influence_i : range) { | for (const int influence_i : range) { | ||||
| const int curve_i = curve_indices[influence_i]; | const int curve_i = curve_indices[influence_i]; | ||||
| const float move_distance_cu = move_distances_cu[influence_i]; | const float move_distance_cu = move_distances_cu[influence_i]; | ||||
| const IndexRange curve_points = curves.points_for_curve(curve_i); | const IndexRange curve_points = curves.points_for_curve(curve_i); | ||||
| this->shrink_curve(positions_cu, curve_points, move_distance_cu); | this->shrink_curve(positions_cu, curve_points, move_distance_cu, data); | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| private: | |||||
| void shrink_curve(MutableSpan<float3> positions, | void shrink_curve(MutableSpan<float3> positions, | ||||
| const IndexRange curve_points, | const IndexRange points, | ||||
| const float shrink_length) const | const float shrink_length, | ||||
| ParameterizationBuffers &data) const | |||||
| { | { | ||||
| PolySpline spline; | data.reinitialize(points.size()); | ||||
| spline.resize(curve_points.size()); | |||||
| MutableSpan<float3> spline_positions = spline.positions(); | /* Copy the old positions to facilitate mixing from neighbors for the resulting curve. */ | ||||
| spline_positions.copy_from(positions.slice(curve_points)); | data.old_positions.as_mutable_span().copy_from(positions.slice(points)); | ||||
| spline.mark_cache_invalid(); | |||||
| lp::accumulate_lengths<float3>(data.old_positions, false, data.old_lengths); | |||||
| const float min_length = brush_.curves_sculpt_settings->minimum_length; | const float min_length = brush_.curves_sculpt_settings->minimum_length; | ||||
| const float old_length = spline.length(); | const float old_length = data.old_lengths.last(); | ||||
| const float new_length = std::max(min_length, old_length - shrink_length); | const float new_length = std::max(min_length, old_length - shrink_length); | ||||
| const float length_factor = std::clamp(new_length / old_length, 0.0f, 1.0f); | const float length_factor = std::clamp(new_length / old_length, 0.0f, 1.0f); | ||||
| Vector<float> old_point_lengths; | data.sample_lengths.first() = 0.0f; | ||||
| old_point_lengths.append(0.0f); | for (const int i : data.old_lengths.index_range()) { | ||||
| for (const int i : spline_positions.index_range().drop_back(1)) { | data.sample_lengths[i + 1] = data.old_lengths[i] * length_factor; | ||||
| const float3 &p1 = spline_positions[i]; | |||||
| const float3 &p2 = spline_positions[i + 1]; | |||||
| const float length = math::distance(p1, p2); | |||||
| old_point_lengths.append(old_point_lengths.last() + length); | |||||
| } | |||||
| for (const int i : spline_positions.index_range()) { | |||||
| const float eval_length = old_point_lengths[i] * length_factor; | |||||
| const Spline::LookupResult lookup = spline.lookup_evaluated_length(eval_length); | |||||
| const float index_factor = lookup.evaluated_index + lookup.factor; | |||||
| float3 p; | |||||
| spline.sample_with_index_factors<float3>(spline_positions, {&index_factor, 1}, {&p, 1}); | |||||
| positions[curve_points[i]] = p; | |||||
| } | } | ||||
| lp::create_samples_from_sorted_lengths( | |||||
| data.old_lengths, data.sample_lengths, false, data.indices, data.factors); | |||||
| lp::linear_interpolation<float3>( | |||||
| data.old_positions, data.indices, data.factors, positions.slice(points)); | |||||
| } | } | ||||
| }; | }; | ||||
| /** | /** | ||||
| * Make the curves longer by extrapolating them linearly. | * Make the curves longer by extrapolating them linearly. | ||||
| */ | */ | ||||
| class ExtrapolateCurvesEffect : public CurvesEffect { | class ExtrapolateCurvesEffect : public CurvesEffect { | ||||
| void execute(CurvesGeometry &curves, | void execute(CurvesGeometry &curves, | ||||
| ▲ Show 20 Lines • Show All 399 Lines • Show Last 20 Lines | |||||