Blender V5.0
curves_sculpt_smooth.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 "BKE_brush.hh"
6#include "BKE_context.hh"
7#include "BKE_crazyspace.hh"
8#include "BKE_paint.hh"
9
10#include "ED_screen.hh"
11#include "ED_view3d.hh"
12
13#include "DEG_depsgraph.hh"
14
15#include "DNA_brush_types.h"
16
17#include "WM_api.hh"
18
19#include "BLI_task.hh"
20
22
24
26 private:
28 CurvesBrush3D brush_3d_;
29
31
32 public:
33 void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
34};
35
43
44 Object *object_ = nullptr;
45 Curves *curves_id_ = nullptr;
47
51
53 const Brush *brush_ = nullptr;
58
60
62
63 void execute(SmoothOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
64 {
65 UNUSED_VARS(C, stroke_extension);
66 self_ = &self;
67
69 curves_id_ = static_cast<Curves *>(object_->data);
70 curves_ = &curves_id_->geometry.wrap();
71 if (curves_->is_empty()) {
72 return;
73 }
74
75 curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
79 brush_strength_ = brush_strength_get(curves_sculpt_->paint, *brush_, stroke_extension);
80 brush_pos_re_ = stroke_extension.mouse_position;
81
82 point_factors_ = *curves_->attributes().lookup_or_default<float>(
83 ".selection", bke::AttrDomain::Point, 1.0f);
86
87 const eBrushFalloffShape falloff_shape = eBrushFalloffShape(brush_->falloff_shape);
88 if (stroke_extension.is_first) {
89 if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE || (U.uiflag & USER_ORBIT_SELECTION)) {
90 self.brush_3d_ = *sample_curves_3d_brush(*ctx_.depsgraph,
91 *ctx_.region,
92 *ctx_.v3d,
93 *ctx_.rv3d,
94 *object_,
99 math::transform_point(transforms_.curves_to_world, self_->brush_3d_.position_cu));
100 }
101 }
102
103 Array<float> point_smooth_factors(curves_->points_num(), 0.0f);
104
105 if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
106 this->find_projected_smooth_factors_with_symmetry(point_smooth_factors);
107 }
108 else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
109 this->find_spherical_smooth_factors_with_symmetry(point_smooth_factors);
110 }
111 else {
113 }
114
115 this->smooth(point_smooth_factors);
116 curves_->tag_positions_changed();
120 }
121
123 {
124 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
126 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
127 this->find_projected_smooth_factors(brush_transform, r_point_smooth_factors);
128 }
129 }
130
131 void find_projected_smooth_factors(const float4x4 &brush_transform,
132 MutableSpan<float> r_point_smooth_factors)
133 {
134 const float4x4 brush_transform_inv = math::invert(brush_transform);
135
136 const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
137 const float brush_radius_sq_re = pow2f(brush_radius_re);
138
139 const float4x4 projection = ED_view3d_ob_project_mat_get(ctx_.rv3d, object_);
140
141 const bke::crazyspace::GeometryDeformation deformation =
143 const OffsetIndices points_by_curve = curves_->points_by_curve();
144
145 curve_selection_.foreach_index(GrainSize(256), [&](const int curve_i) {
146 const IndexRange points = points_by_curve[curve_i];
147 for (const int point_i : points) {
148 const float3 &pos_cu = math::transform_point(brush_transform_inv,
149 deformation.positions[point_i]);
150 const float2 pos_re = ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, projection);
151 const float dist_to_brush_sq_re = math::distance_squared(pos_re, brush_pos_re_);
152 if (dist_to_brush_sq_re > brush_radius_sq_re) {
153 continue;
154 }
155
156 const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re);
157 const float radius_falloff = BKE_brush_curve_strength(
158 brush_, dist_to_brush_re, brush_radius_re);
159 /* Used to make the brush easier to use. Otherwise a strength of 1 would be way too
160 * large. */
161 const float weight_factor = 0.1f;
162 const float weight = weight_factor * brush_strength_ * radius_falloff *
163 point_factors_[point_i];
164 math::max_inplace(r_point_smooth_factors[point_i], weight);
165 }
166 });
167 }
168
170 {
171 float3 brush_pos_wo;
173 ctx_.v3d,
174 ctx_.region,
175 math::transform_point(transforms_.curves_to_world, self_->brush_3d_.position_cu),
177 brush_pos_wo);
178 const float3 brush_pos_cu = math::transform_point(transforms_.world_to_curves, brush_pos_wo);
179 const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_;
180
181 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
183 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
184 this->find_spherical_smooth_factors(math::transform_point(brush_transform, brush_pos_cu),
185 brush_radius_cu,
186 r_point_smooth_factors);
187 }
188 }
189
190 void find_spherical_smooth_factors(const float3 &brush_pos_cu,
191 const float brush_radius_cu,
192 MutableSpan<float> r_point_smooth_factors)
193 {
194 const float brush_radius_sq_cu = pow2f(brush_radius_cu);
195 const bke::crazyspace::GeometryDeformation deformation =
197 const OffsetIndices points_by_curve = curves_->points_by_curve();
198
199 curve_selection_.foreach_index(GrainSize(256), [&](const int curve_i) {
200 const IndexRange points = points_by_curve[curve_i];
201 for (const int point_i : points) {
202 const float3 &pos_cu = deformation.positions[point_i];
203 const float dist_to_brush_sq_cu = math::distance_squared(pos_cu, brush_pos_cu);
204 if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
205 continue;
206 }
207
208 const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu);
209 const float radius_falloff = BKE_brush_curve_strength(
210 brush_, dist_to_brush_cu, brush_radius_cu);
211 /* Used to make the brush easier to use. Otherwise a strength of 1 would be way too
212 * large. */
213 const float weight_factor = 0.1f;
214 const float weight = weight_factor * brush_strength_ * radius_falloff *
215 point_factors_[point_i];
216 math::max_inplace(r_point_smooth_factors[point_i], weight);
217 }
218 });
219 }
220
221 void smooth(const Span<float> point_smooth_factors)
222 {
223 const OffsetIndices points_by_curve = curves_->points_by_curve();
224 MutableSpan<float3> positions = curves_->positions_for_write();
225
226 curve_selection_.foreach_segment(GrainSize(256), [&](const IndexMaskSegment segment) {
227 Vector<float3> old_positions;
228 for (const int curve_i : segment) {
229 const IndexRange points = points_by_curve[curve_i];
230 old_positions.clear();
231 old_positions.extend(positions.slice(points));
232 for (const int i : IndexRange(points.size()).drop_front(1).drop_back(1)) {
233 const int point_i = points[i];
234 const float smooth_factor = point_smooth_factors[point_i];
235 if (smooth_factor == 0.0f) {
236 continue;
237 }
238 /* Move towards the middle of the neighboring points. */
239 const float3 old_pos = old_positions[i];
240 const float3 &prev_pos = old_positions[i - 1];
241 const float3 &next_pos = old_positions[i + 1];
242 const float3 goal_pos = math::midpoint(prev_pos, next_pos);
243 const float3 new_pos = math::interpolate(old_pos, goal_pos, smooth_factor);
244 positions[point_i] = new_pos;
245 }
246 }
247 });
248 }
249};
250
252 const StrokeExtension &stroke_extension)
253{
254 SmoothOperationExecutor executor{C};
255 executor.execute(*this, C, stroke_extension);
256}
257
258std::unique_ptr<CurvesSculptStrokeOperation> new_smooth_operation()
259{
260 return std::make_unique<SmoothOperation>();
261}
262
263} // 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)
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:650
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
MINLINE float pow2f(float x)
#define UNUSED_VARS(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
eBrushFalloffShape
@ PAINT_FALLOFF_SHAPE_SPHERE
@ PAINT_FALLOFF_SHAPE_TUBE
eCurvesSymmetryType
@ 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)
void ED_view3d_win_to_3d(const View3D *v3d, const ARegion *region, const float depth_pt[3], const float mval[2], float r_out[3])
blender::float4x4 ED_view3d_ob_project_mat_get(const RegionView3D *rv3d, const Object *ob)
#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
constexpr IndexRange drop_back(int64_t n) const
constexpr int64_t size() const
constexpr IndexRange drop_front(int64_t n) const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
void extend(Span< T > array)
void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override
GeometryDeformation get_evaluated_curves_deformation(const Object *ob_eval, const Object &ob_orig)
IndexMask retrieve_selected_curves(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
float brush_strength_get(const Paint &paint, const Brush &brush, const StrokeExtension &stroke_extension)
std::optional< CurvesBrush3D > sample_curves_3d_brush(const Depsgraph &depsgraph, const ARegion &region, const View3D &v3d, const RegionView3D &rv3d, const Object &curves_object, const float2 &brush_pos_re, const float brush_radius_re)
void remember_stroke_position(CurvesSculpt &curves_sculpt, const float3 &brush_position_wo)
std::unique_ptr< CurvesSculptStrokeOperation > new_smooth_operation()
Vector< float4x4 > get_symmetry_brush_transforms(const eCurvesSymmetryType symmetry)
float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension)
CartesianBasis invert(const CartesianBasis &basis)
T midpoint(const T &a, const T &b)
T interpolate(const T &a, const T &b, const FactorT &t)
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
void max_inplace(T &a, const T &b)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
MatBase< float, 4, 4 > float4x4
VecBase< float, 2 > float2
VecBase< float, 3 > float3
void find_spherical_smooth_factors(const float3 &brush_pos_cu, const float brush_radius_cu, MutableSpan< float > r_point_smooth_factors)
void find_projected_smooth_factors(const float4x4 &brush_transform, MutableSpan< float > r_point_smooth_factors)
void smooth(const Span< float > point_smooth_factors)
void execute(SmoothOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
void find_spherical_smooth_factors_with_symmetry(MutableSpan< float > r_point_smooth_factors)
void find_projected_smooth_factors_with_symmetry(MutableSpan< float > r_point_smooth_factors)
i
Definition text_draw.cc:230
void WM_main_add_notifier(uint type, void *reference)