Blender V5.0
curves_sculpt_pinch.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
6
8#include "BLI_task.hh"
9#include "BLI_vector.hh"
10
11#include "DEG_depsgraph.hh"
12
13#include "BKE_brush.hh"
14#include "BKE_context.hh"
15#include "BKE_curves.hh"
16#include "BKE_paint.hh"
17
18#include "DNA_brush_enums.h"
19#include "DNA_brush_types.h"
20#include "DNA_curves_types.h"
21#include "DNA_object_types.h"
22#include "DNA_screen_types.h"
23
24#include "ED_screen.hh"
25#include "ED_view3d.hh"
26
27#include "WM_api.hh"
28
36
38
40 private:
41 bool invert_pinch_;
42
44 CurvesConstraintSolver constraint_solver_;
45
47 CurvesBrush3D brush_3d_;
48
50
51 public:
52 PinchOperation(const bool invert_pinch) : invert_pinch_(invert_pinch) {}
53
54 void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
55};
56
60
61 Object *object_ = nullptr;
62 Curves *curves_id_ = nullptr;
64
68
70
72 const Brush *brush_ = nullptr;
76
78
80
82
83 void execute(PinchOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
84 {
85 self_ = &self;
86
88 curves_id_ = static_cast<Curves *>(object_->data);
89 curves_ = &curves_id_->geometry.wrap();
90 if (curves_->is_empty()) {
91 return;
92 }
93
94 curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
99
100 invert_factor_ = self_->invert_pinch_ ? -1.0f : 1.0f;
101
103
104 point_factors_ = *curves_->attributes().lookup_or_default<float>(
105 ".selection", bke::AttrDomain::Point, 1.0f);
107
108 brush_pos_re_ = stroke_extension.mouse_position;
109 const eBrushFalloffShape falloff_shape = eBrushFalloffShape(brush_->falloff_shape);
110
111 if (stroke_extension.is_first) {
112 if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE || (U.uiflag & USER_ORBIT_SELECTION)) {
113 self_->brush_3d_ = *sample_curves_3d_brush(*ctx_.depsgraph,
114 *ctx_.region,
115 *ctx_.v3d,
116 *ctx_.rv3d,
117 *object_,
122 math::transform_point(transforms_.curves_to_world, self_->brush_3d_.position_cu));
123 }
124
125 self_->constraint_solver_.initialize(*curves_,
128 curves_id_->surface_collision_distance);
129 }
130
131 Array<bool> changed_curves(curves_->curves_num(), false);
132 if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
133 this->pinch_projected_with_symmetry(changed_curves);
134 }
135 else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
136 this->pinch_spherical_with_symmetry(changed_curves);
137 }
138 else {
140 }
141
142 IndexMaskMemory memory;
143 const IndexMask changed_curves_mask = IndexMask::from_bools(changed_curves, memory);
144 const Mesh *surface = curves_id_->surface && curves_id_->surface->type == OB_MESH ?
145 static_cast<const Mesh *>(curves_id_->surface->data) :
146 nullptr;
147 self_->constraint_solver_.solve_step(*curves_, changed_curves_mask, surface, transforms_);
148
149 curves_->tag_positions_changed();
153 }
154
156 {
157 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
159 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
160 this->pinch_projected(brush_transform, r_changed_curves);
161 }
162 }
163
164 void pinch_projected(const float4x4 &brush_transform, MutableSpan<bool> r_changed_curves)
165 {
166 const float4x4 brush_transform_inv = math::invert(brush_transform);
167
168 const bke::crazyspace::GeometryDeformation deformation =
170 const OffsetIndices points_by_curve = curves_->points_by_curve();
171
172 const float4x4 projection = ED_view3d_ob_project_mat_get(ctx_.rv3d, object_);
173 MutableSpan<float3> positions_cu = curves_->positions_for_write();
174 const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
175 const float brush_radius_sq_re = pow2f(brush_radius_re);
176
177 curve_selection_.foreach_segment(GrainSize(256), [&](const IndexMaskSegment segment) {
178 for (const int curve_i : segment) {
179 const IndexRange points = points_by_curve[curve_i];
180 for (const int point_i : points.drop_front(1)) {
181 const float3 old_pos_cu = deformation.positions[point_i];
182 const float3 old_symm_pos_cu = math::transform_point(brush_transform_inv, old_pos_cu);
183 const float2 old_symm_pos_re = ED_view3d_project_float_v2_m4(
184 ctx_.region, old_symm_pos_cu, projection);
185
186 const float dist_to_brush_sq_re = math::distance_squared(old_symm_pos_re, brush_pos_re_);
187 if (dist_to_brush_sq_re > brush_radius_sq_re) {
188 continue;
189 }
190
191 const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re);
192 const float t = math::safe_divide(dist_to_brush_re, brush_radius_base_re_);
193 const float radius_falloff = t * BKE_brush_curve_strength(brush_, t, 1.0f);
194 const float weight = invert_factor_ * 0.1f * brush_strength_ * radius_falloff *
195 point_factors_[point_i];
196
197 const float2 new_symm_pos_re = math::interpolate(old_symm_pos_re, brush_pos_re_, weight);
198
199 float3 new_symm_pos_wo;
201 ctx_.region,
202 math::transform_point(transforms_.curves_to_world, old_symm_pos_cu),
203 new_symm_pos_re,
204 new_symm_pos_wo);
205
206 float3 new_pos_cu = math::transform_point(transforms_.world_to_curves, new_symm_pos_wo);
207 new_pos_cu = math::transform_point(brush_transform, new_pos_cu);
208 const float3 translation_eval = new_pos_cu - old_pos_cu;
209 const float3 translation_orig = deformation.translation_from_deformed_to_original(
210 point_i, translation_eval);
211 positions_cu[point_i] += translation_orig;
212 r_changed_curves[curve_i] = true;
213 }
214 }
215 });
216 }
217
219 {
220 float3 brush_pos_wo;
222 ctx_.v3d,
223 ctx_.region,
224 math::transform_point(transforms_.curves_to_world, self_->brush_3d_.position_cu),
226 brush_pos_wo);
227 const float3 brush_pos_cu = math::transform_point(transforms_.world_to_curves, brush_pos_wo);
228 const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_;
229
230 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
232 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
233 this->pinch_spherical(
234 math::transform_point(brush_transform, brush_pos_cu), brush_radius_cu, r_changed_curves);
235 }
236 }
237
238 void pinch_spherical(const float3 &brush_pos_cu,
239 const float brush_radius_cu,
240 MutableSpan<bool> r_changed_curves)
241 {
242 MutableSpan<float3> positions_cu = curves_->positions_for_write();
243 const float brush_radius_sq_cu = pow2f(brush_radius_cu);
244
245 const bke::crazyspace::GeometryDeformation deformation =
247 const OffsetIndices points_by_curve = curves_->points_by_curve();
248
249 curve_selection_.foreach_segment(GrainSize(256), [&](const IndexMaskSegment segment) {
250 for (const int curve_i : segment) {
251 const IndexRange points = points_by_curve[curve_i];
252 for (const int point_i : points.drop_front(1)) {
253 const float3 old_pos_cu = deformation.positions[point_i];
254
255 const float dist_to_brush_sq_cu = math::distance_squared(old_pos_cu, brush_pos_cu);
256 if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
257 continue;
258 }
259
260 const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu);
261 const float t = math::safe_divide(dist_to_brush_cu, brush_radius_cu);
262 const float radius_falloff = t * BKE_brush_curve_strength(brush_, t, 1.0f);
263 const float weight = invert_factor_ * 0.1f * brush_strength_ * radius_falloff *
264 point_factors_[point_i];
265
266 const float3 new_pos_cu = math::interpolate(old_pos_cu, brush_pos_cu, weight);
267 const float3 translation_eval = new_pos_cu - old_pos_cu;
268 const float3 translation_orig = deformation.translation_from_deformed_to_original(
269 point_i, translation_eval);
270 positions_cu[point_i] += translation_orig;
271
272 r_changed_curves[curve_i] = true;
273 }
274 }
275 });
276 }
277};
278
280{
281 PinchOperationExecutor executor{C};
282 executor.execute(*this, C, stroke_extension);
283}
284
285std::unique_ptr<CurvesSculptStrokeOperation> new_pinch_operation(const BrushStrokeMode brush_mode,
286 const bContext &C)
287{
288 const Scene &scene = *CTX_data_scene(&C);
290
291 const bool invert_pinch = (brush_mode == BRUSH_STROKE_INVERT) !=
292 ((brush.flag & BRUSH_DIR_IN) != 0);
293 return std::make_unique<PinchOperation>(invert_pinch);
294}
295
296} // namespace blender::ed::sculpt_paint
float BKE_brush_alpha_get(const Paint *paint, const Brush *brush)
Definition brush.cc:1357
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)
Low-level operations for curves.
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)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ BRUSH_DIR_IN
eBrushFalloffShape
@ PAINT_FALLOFF_SHAPE_SPHERE
@ PAINT_FALLOFF_SHAPE_TUBE
@ CV_SCULPT_COLLISION_ENABLED
eCurvesSymmetryType
Object is a sort of wrapper for general info.
@ 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)
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
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
constexpr IndexRange drop_front(int64_t n) const
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)
std::unique_ptr< CurvesSculptStrokeOperation > new_pinch_operation(const BrushStrokeMode brush_mode, const bContext &C)
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)
Vector< float4x4 > get_symmetry_brush_transforms(const eCurvesSymmetryType symmetry)
float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension)
T safe_divide(const T &a, const T &b)
CartesianBasis invert(const CartesianBasis &basis)
T interpolate(const T &a, const T &b, const FactorT &t)
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)
MatBase< float, 4, 4 > float4x4
VecBase< float, 2 > float2
VecBase< float, 3 > float3
BrushStrokeMode
@ BRUSH_STROKE_INVERT
struct ToolSettings * toolsettings
CurvesSculpt * curves_sculpt
float3 translation_from_deformed_to_original(const int position_i, const float3 &translation) const
void execute(PinchOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
void pinch_spherical_with_symmetry(MutableSpan< bool > r_changed_curves)
void pinch_projected(const float4x4 &brush_transform, MutableSpan< bool > r_changed_curves)
void pinch_projected_with_symmetry(MutableSpan< bool > r_changed_curves)
void pinch_spherical(const float3 &brush_pos_cu, const float brush_radius_cu, MutableSpan< bool > r_changed_curves)
void WM_main_add_notifier(uint type, void *reference)