Blender V5.0
curves_sculpt_snake_hook.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
7#include "BLI_math_geom.h"
9#include "BLI_vector.hh"
10
11#include "DEG_depsgraph.hh"
12
13#include "BKE_attribute.hh"
14#include "BKE_brush.hh"
15#include "BKE_context.hh"
16#include "BKE_curves.hh"
17#include "BKE_mesh.hh"
18#include "BKE_mesh_runtime.hh"
19#include "BKE_paint.hh"
20
21#include "DNA_brush_enums.h"
22#include "DNA_brush_types.h"
23#include "DNA_curves_types.h"
24#include "DNA_object_types.h"
25#include "DNA_screen_types.h"
26
27#include "ED_screen.hh"
28#include "ED_view3d.hh"
29
30#include "WM_api.hh"
31
39
41
42using blender::bke::CurvesGeometry;
43
48 private:
49 float2 last_mouse_position_re_;
50
51 CurvesBrush3D brush_3d_;
52
54
55 public:
56 void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
57};
58
66
68 const Brush *brush_ = nullptr;
72
73 Object *object_ = nullptr;
74 Curves *curves_id_ = nullptr;
76
80
82
86
88
90 const bContext &C,
91 const StrokeExtension &stroke_extension)
92 {
93 BLI_SCOPED_DEFER([&]() { self.last_mouse_position_re_ = stroke_extension.mouse_position; });
94
95 self_ = &self;
97
98 curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
100
102 brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension);
103 brush_strength_ = brush_strength_get(curves_sculpt_->paint, *brush_, stroke_extension);
104
105 const eBrushFalloffShape falloff_shape = eBrushFalloffShape(brush_->falloff_shape);
106
107 curves_id_ = static_cast<Curves *>(object_->data);
108 curves_ = &curves_id_->geometry.wrap();
109 if (curves_->is_empty()) {
110 return;
111 }
112
114
115 curve_factors_ = *curves_->attributes().lookup_or_default(
116 ".selection", bke::AttrDomain::Curve, 1.0f);
118
119 brush_pos_prev_re_ = self.last_mouse_position_re_;
120 brush_pos_re_ = stroke_extension.mouse_position;
122
123 if (stroke_extension.is_first) {
124 if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE || (U.uiflag & USER_ORBIT_SELECTION)) {
125 std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush(*ctx_.depsgraph,
126 *ctx_.region,
127 *ctx_.v3d,
128 *ctx_.rv3d,
129 *object_,
132 if (brush_3d.has_value()) {
133 self_->brush_3d_ = *brush_3d;
136 math::transform_point(transforms_.curves_to_world, self_->brush_3d_.position_cu));
137 }
138 }
139 return;
140 }
141
142 if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
144 }
145 else if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
147 }
148 else {
150 }
151
152 curves_->tag_positions_changed();
156 }
157
159 {
160 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
162 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
163 this->projected_snake_hook(brush_transform);
164 }
165 }
166
167 void projected_snake_hook(const float4x4 &brush_transform)
168 {
169 const float4x4 brush_transform_inv = math::invert(brush_transform);
170 const bke::crazyspace::GeometryDeformation deformation =
172 const OffsetIndices points_by_curve = curves_->points_by_curve();
173
174 MutableSpan<float3> positions_cu = curves_->positions_for_write();
175
176 const float4x4 projection = ED_view3d_ob_project_mat_get(ctx_.rv3d, object_);
177
178 const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
179 const float brush_radius_sq_re = pow2f(brush_radius_re);
180
181 curve_selection_.foreach_segment(GrainSize(256), [&](const IndexMaskSegment segment) {
182 MoveAndResampleBuffers resample_buffer;
183 for (const int curve_i : segment) {
184 const IndexRange points = points_by_curve[curve_i];
185 const int last_point_i = points.last();
186 const float3 old_pos_cu = deformation.positions[last_point_i];
187 const float3 old_symm_pos_cu = math::transform_point(brush_transform_inv, old_pos_cu);
188
189 const float2 old_symm_pos_re = ED_view3d_project_float_v2_m4(
190 ctx_.region, old_symm_pos_cu, projection);
191
192 const float distance_to_brush_sq_re = math::distance_squared(old_symm_pos_re,
194 if (distance_to_brush_sq_re > brush_radius_sq_re) {
195 continue;
196 }
197
198 const float radius_falloff = BKE_brush_curve_strength(
199 brush_, std::sqrt(distance_to_brush_sq_re), brush_radius_re);
200 const float weight = brush_strength_ * radius_falloff * curve_factors_[curve_i];
201
202 const float2 new_symm_pos_re = old_symm_pos_re + brush_pos_diff_re_ * weight;
203 float3 new_symm_pos_wo;
205 ctx_.region,
206 math::transform_point(transforms_.curves_to_world, old_symm_pos_cu),
207 new_symm_pos_re,
208 new_symm_pos_wo);
209 const float3 new_pos_cu = math::transform_point(
210 brush_transform, math::transform_point(transforms_.world_to_curves, new_symm_pos_wo));
211 const float3 translation_eval = new_pos_cu - old_pos_cu;
212 const float3 translation_orig = deformation.translation_from_deformed_to_original(
213 last_point_i, translation_eval);
214
215 const float3 last_point_cu = positions_cu[last_point_i] + translation_orig;
216 move_last_point_and_resample(resample_buffer, positions_cu.slice(points), last_point_cu);
217 }
218 });
219 }
220
222 {
223 float3 brush_start_wo, brush_end_wo;
225 ctx_.v3d,
226 ctx_.region,
227 math::transform_point(transforms_.curves_to_world, self_->brush_3d_.position_cu),
229 brush_start_wo);
231 ctx_.v3d,
232 ctx_.region,
233 math::transform_point(transforms_.curves_to_world, self_->brush_3d_.position_cu),
235 brush_end_wo);
236 const float3 brush_start_cu = math::transform_point(transforms_.world_to_curves,
237 brush_start_wo);
238 const float3 brush_end_cu = math::transform_point(transforms_.world_to_curves, brush_end_wo);
239
240 const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_;
241
242 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
244 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
245 this->spherical_snake_hook(math::transform_point(brush_transform, brush_start_cu),
246 math::transform_point(brush_transform, brush_end_cu),
247 brush_radius_cu);
248 }
249 }
250
251 void spherical_snake_hook(const float3 &brush_start_cu,
252 const float3 &brush_end_cu,
253 const float brush_radius_cu)
254 {
255 const bke::crazyspace::GeometryDeformation deformation =
257 const OffsetIndices points_by_curve = curves_->points_by_curve();
258
259 MutableSpan<float3> positions_cu = curves_->positions_for_write();
260 const float3 brush_diff_cu = brush_end_cu - brush_start_cu;
261 const float brush_radius_sq_cu = pow2f(brush_radius_cu);
262
263 curve_selection_.foreach_segment(GrainSize(256), [&](const IndexMaskSegment segment) {
264 MoveAndResampleBuffers resample_buffer;
265 for (const int curve_i : segment) {
266 const IndexRange points = points_by_curve[curve_i];
267 const int last_point_i = points.last();
268 const float3 old_pos_cu = deformation.positions[last_point_i];
269
270 const float distance_to_brush_sq_cu = dist_squared_to_line_segment_v3(
271 old_pos_cu, brush_start_cu, brush_end_cu);
272 if (distance_to_brush_sq_cu > brush_radius_sq_cu) {
273 continue;
274 }
275
276 const float distance_to_brush_cu = std::sqrt(distance_to_brush_sq_cu);
277
278 const float radius_falloff = BKE_brush_curve_strength(
279 brush_, distance_to_brush_cu, brush_radius_cu);
280 const float weight = brush_strength_ * radius_falloff * curve_factors_[curve_i];
281
282 const float3 translation_eval = weight * brush_diff_cu;
283 const float3 translation_orig = deformation.translation_from_deformed_to_original(
284 last_point_i, translation_eval);
285
286 const float3 last_point_cu = positions_cu[last_point_i] + translation_orig;
287 move_last_point_and_resample(resample_buffer, positions_cu.slice(points), last_point_cu);
288 }
289 });
290 }
291};
292
294 const StrokeExtension &stroke_extension)
295{
297 executor.execute(*this, C, stroke_extension);
298}
299
300std::unique_ptr<CurvesSculptStrokeOperation> new_snake_hook_operation()
301{
302 return std::make_unique<SnakeHookOperation>();
303}
304
305} // 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)
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)
float dist_squared_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3])
Definition math_geom.cc:519
#define BLI_SCOPED_DEFER(function_to_defer)
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
Object is a sort of wrapper for general info.
@ 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 int64_t last(const int64_t n=0) const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
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)
std::unique_ptr< CurvesSculptStrokeOperation > new_snake_hook_operation()
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)
void move_last_point_and_resample(MoveAndResampleBuffers &buffer, MutableSpan< float3 > positions, const float3 &new_last_position)
CartesianBasis invert(const CartesianBasis &basis)
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
float3 translation_from_deformed_to_original(const int position_i, const float3 &translation) const
void spherical_snake_hook(const float3 &brush_start_cu, const float3 &brush_end_cu, const float brush_radius_cu)
void execute(SnakeHookOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
void WM_main_add_notifier(uint type, void *reference)