Blender V4.3
snake_hook.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
7#include "DNA_brush_types.h"
8#include "DNA_mesh_types.h"
9#include "DNA_object_types.h"
10#include "DNA_scene_types.h"
11
12#include "BKE_kelvinlet.h"
13#include "BKE_key.hh"
14#include "BKE_mesh.hh"
15#include "BKE_paint.hh"
16#include "BKE_pbvh.hh"
17#include "BKE_subdiv_ccg.hh"
18
19#include "BLI_array.hh"
21#include "BLI_math_matrix.hh"
23#include "BLI_math_rotation.h"
24#include "BLI_math_vector.h"
25#include "BLI_math_vector.hh"
26#include "BLI_task.h"
27#include "BLI_task.hh"
28
32
34
35inline namespace snake_hook_cc {
36
43
50
55{
56 spvc->plane = plane;
57 spvc->len_sq = math::length_squared(spvc->plane);
58 spvc->is_valid = (spvc->len_sq > FLT_EPSILON);
59 spvc->len_sq_inv_neg = (spvc->is_valid) ? -1.0f / spvc->len_sq : 0.0f;
60}
61
65static void sculpt_project_v3(const SculptProjectVector *spvc, const float3 &vec, float3 &r_vec)
66{
67#if 0
68 project_plane_v3_v3v3(r_vec, vec, spvc->plane);
69#else
70 /* inline the projection, cache `-1.0 / dot_v3_v3(v_proj, v_proj)` */
71 r_vec += spvc->plane * math::dot(vec, spvc->plane) * spvc->len_sq_inv_neg;
72#endif
73}
74
76 const float3 &sculpt_co,
77 const float3 &v_co,
78 float factor)
79{
80 float3 vec_rot = v_co - sculpt_co;
81 const math::Quaternion rotation = math::pow(*cache.rake_rotation_symm, factor);
82 vec_rot = math::transform_point(rotation, vec_rot);
83
84 vec_rot += sculpt_co;
85 return vec_rot - v_co;
86}
87
88BLI_NOINLINE static void calc_pinch_influence(const Brush &brush,
89 const StrokeCache &cache,
90 const float3 &grab_delta,
91 const SculptProjectVector *spvc,
92 const Span<float3> positions,
93 const Span<float> factors,
94 const MutableSpan<float3> translations)
95{
96 if (brush.crease_pinch_factor == 0.5f) {
97 return;
98 }
99
100 const float pinch = 2.0f * (0.5f - brush.crease_pinch_factor) * math::length(grab_delta) /
101 cache.radius;
102
103 for (const int i : positions.index_range()) {
104 /* Negative pinch will inflate, helps maintain volume. */
105 float3 delta_pinch = positions[i] - cache.location_symm;
106
108 project_plane_v3_v3v3(delta_pinch, delta_pinch, cache.view_normal);
109 }
110
111 /* Important to calculate based on the grabbed location
112 * (intentionally ignore fade here). */
113 delta_pinch += grab_delta;
114
115 sculpt_project_v3(spvc, delta_pinch, delta_pinch);
116
117 float3 delta_pinch_init = delta_pinch;
118
119 float pinch_fade = pinch * factors[i];
120 /* When reducing, scale reduction back by how close to the center we are,
121 * so we don't pinch into nothingness. */
122 if (pinch > 0.0f) {
123 /* Square to have even less impact for close vertices. */
124 pinch_fade *= pow2f(std::min(1.0f, math::length(delta_pinch) / cache.radius));
125 }
126 delta_pinch *= (1.0f + pinch_fade);
127 delta_pinch = delta_pinch_init - delta_pinch;
128 translations[i] += delta_pinch;
129 }
130}
131
133 const Span<float3> positions,
134 const Span<float> factors,
135 const MutableSpan<float3> translations)
136{
137 if (!cache.rake_rotation_symm) {
138 return;
139 }
140 for (const int i : positions.index_range()) {
141 translations[i] += sculpt_rake_rotate(cache, cache.location_symm, positions[i], factors[i]);
142 }
143}
144
146 const Span<float3> positions,
147 const Span<float> factors,
148 const MutableSpan<float3> translations)
149{
151 BKE_kelvinlet_init_params(&params, cache.radius, cache.bstrength, 1.0f, 0.4f);
152 for (const int i : positions.index_range()) {
153 float3 disp;
154 BKE_kelvinlet_grab_triscale(disp, &params, positions[i], cache.location_symm, translations[i]);
155 translations[i] = disp * factors[i];
156 }
157}
158
159static void calc_faces(const Depsgraph &depsgraph,
160 const Sculpt &sd,
161 Object &object,
162 const Brush &brush,
163 const SculptProjectVector *spvc,
164 const float3 &grab_delta,
165 const Span<float3> vert_normals,
166 const MeshAttributeData &attribute_data,
168 LocalData &tls,
169 const PositionDeformData &position_data)
170{
171 SculptSession &ss = *object.sculpt;
172 const StrokeCache &cache = *ss.cache;
173 const bool do_elastic = brush.snake_hook_deform_type == BRUSH_SNAKE_HOOK_DEFORM_ELASTIC;
174
175 const Span<int> verts = node.verts();
176 const MutableSpan positions = gather_data_mesh(position_data.eval, verts, tls.positions);
177
178 tls.factors.resize(verts.size());
179 const MutableSpan<float> factors = tls.factors;
180
181 if (do_elastic) {
182 factors.fill(1.0f);
183 }
184 else {
185 fill_factor_from_hide_and_mask(attribute_data.hide_vert, attribute_data.mask, verts, factors);
186 filter_region_clip_factors(ss, positions, factors);
187 if (brush.flag & BRUSH_FRONTFACE) {
188 calc_front_face(cache.view_normal_symm, vert_normals, verts, factors);
189 }
190
191 tls.distances.resize(verts.size());
192 const MutableSpan<float> distances = tls.distances;
193 calc_brush_distances(ss, positions, eBrushFalloffShape(brush.falloff_shape), distances);
194 filter_distances_with_radius(cache.radius, distances, factors);
195 apply_hardness_to_distances(cache, distances);
196 calc_brush_strength_factors(cache, brush, distances, factors);
197
198 auto_mask::calc_vert_factors(depsgraph, object, cache.automasking.get(), node, verts, factors);
199 calc_brush_texture_factors(ss, brush, positions, factors);
200 scale_factors(factors, cache.bstrength);
201 }
202
203 tls.translations.resize(verts.size());
204 const MutableSpan<float3> translations = tls.translations;
205
206 translations_from_offset_and_factors(grab_delta, factors, translations);
207
208 calc_pinch_influence(brush, cache, grab_delta, spvc, positions, factors, translations);
209
210 calc_rake_rotation_influence(cache, positions, factors, translations);
211
212 if (do_elastic) {
213 fill_factor_from_hide_and_mask(attribute_data.hide_vert, attribute_data.mask, verts, factors);
214 scale_factors(factors, cache.bstrength * 20.0f);
215 auto_mask::calc_vert_factors(depsgraph, object, cache.automasking.get(), node, verts, factors);
216
217 calc_kelvinet_translation(cache, positions, factors, translations);
218 }
219
220 clip_and_lock_translations(sd, ss, position_data.eval, verts, translations);
221 position_data.deform(translations, verts);
222}
223
224static void calc_grids(const Depsgraph &depsgraph,
225 const Sculpt &sd,
226 Object &object,
227 const Brush &brush,
229 const float3 &grab_delta,
231 LocalData &tls)
232{
233 SculptSession &ss = *object.sculpt;
234 const StrokeCache &cache = *ss.cache;
235 const bool do_elastic = brush.snake_hook_deform_type == BRUSH_SNAKE_HOOK_DEFORM_ELASTIC;
236 SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
237
238 const Span<int> grids = node.grids();
239 const MutableSpan positions = gather_grids_positions(subdiv_ccg, grids, tls.positions);
240
241 tls.factors.resize(positions.size());
242 const MutableSpan<float> factors = tls.factors;
243
244 if (do_elastic) {
245 factors.fill(1.0f);
246 }
247 else {
248 fill_factor_from_hide_and_mask(subdiv_ccg, grids, factors);
249 filter_region_clip_factors(ss, positions, factors);
250 if (brush.flag & BRUSH_FRONTFACE) {
251 calc_front_face(cache.view_normal_symm, subdiv_ccg, grids, factors);
252 }
253
254 tls.distances.resize(positions.size());
255 const MutableSpan<float> distances = tls.distances;
256 calc_brush_distances(ss, positions, eBrushFalloffShape(brush.falloff_shape), distances);
257 filter_distances_with_radius(cache.radius, distances, factors);
258 apply_hardness_to_distances(cache, distances);
259 calc_brush_strength_factors(cache, brush, distances, factors);
260
262 depsgraph, object, cache.automasking.get(), node, grids, factors);
263 calc_brush_texture_factors(ss, brush, positions, factors);
264 scale_factors(factors, cache.bstrength);
265 }
266
267 tls.translations.resize(positions.size());
268 const MutableSpan<float3> translations = tls.translations;
269
270 translations_from_offset_and_factors(grab_delta, factors, translations);
271
272 calc_pinch_influence(brush, cache, grab_delta, spvc, positions, factors, translations);
273
274 calc_rake_rotation_influence(cache, positions, factors, translations);
275
276 if (do_elastic) {
277 fill_factor_from_hide_and_mask(subdiv_ccg, grids, factors);
278 scale_factors(factors, cache.bstrength * 20.0f);
280 depsgraph, object, cache.automasking.get(), node, grids, factors);
281
282 calc_kelvinet_translation(cache, positions, factors, translations);
283 }
284
285 clip_and_lock_translations(sd, ss, positions, translations);
286 apply_translations(translations, grids, subdiv_ccg);
287}
288
289static void calc_bmesh(const Depsgraph &depsgraph,
290 const Sculpt &sd,
291 Object &object,
292 const Brush &brush,
294 const float3 &grab_delta,
296 LocalData &tls)
297{
298 SculptSession &ss = *object.sculpt;
299 const StrokeCache &cache = *ss.cache;
300 const bool do_elastic = brush.snake_hook_deform_type == BRUSH_SNAKE_HOOK_DEFORM_ELASTIC;
301
303 const MutableSpan positions = gather_bmesh_positions(verts, tls.positions);
304
305 tls.factors.resize(verts.size());
306 const MutableSpan<float> factors = tls.factors;
307
308 if (do_elastic) {
309 factors.fill(1.0f);
310 }
311 else {
313 filter_region_clip_factors(ss, positions, factors);
314 if (brush.flag & BRUSH_FRONTFACE) {
315 calc_front_face(cache.view_normal_symm, verts, factors);
316 }
317
318 tls.distances.resize(verts.size());
319 const MutableSpan<float> distances = tls.distances;
320 calc_brush_distances(ss, positions, eBrushFalloffShape(brush.falloff_shape), distances);
321 filter_distances_with_radius(cache.radius, distances, factors);
322 apply_hardness_to_distances(cache, distances);
323 calc_brush_strength_factors(cache, brush, distances, factors);
324
325 auto_mask::calc_vert_factors(depsgraph, object, cache.automasking.get(), node, verts, factors);
326 calc_brush_texture_factors(ss, brush, positions, factors);
327 scale_factors(factors, cache.bstrength);
328 }
329
330 tls.translations.resize(verts.size());
331 const MutableSpan<float3> translations = tls.translations;
332
333 translations_from_offset_and_factors(grab_delta, factors, translations);
334
335 calc_pinch_influence(brush, cache, grab_delta, spvc, positions, factors, translations);
336
337 calc_rake_rotation_influence(cache, positions, factors, translations);
338
339 if (do_elastic) {
341 scale_factors(factors, cache.bstrength * 20.0f);
342 auto_mask::calc_vert_factors(depsgraph, object, cache.automasking.get(), node, verts, factors);
343
344 calc_kelvinet_translation(cache, positions, factors, translations);
345 }
346
347 clip_and_lock_translations(sd, ss, positions, translations);
348 apply_translations(translations, verts);
349}
350
351} // namespace snake_hook_cc
352
353void do_snake_hook_brush(const Depsgraph &depsgraph,
354 const Sculpt &sd,
355 Object &object,
356 const IndexMask &node_mask)
357{
358 SculptSession &ss = *object.sculpt;
359 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
360 const Brush &brush = *BKE_paint_brush_for_read(&sd.paint);
361 const float bstrength = ss.cache->bstrength;
362
364
365 float3 grab_delta = ss.cache->grab_delta_symm;
366
367 if (bstrength < 0.0f) {
368 grab_delta *= -1.0f;
369 }
370
371 if (ss.cache->normal_weight > 0.0f) {
373 }
374
375 /* Optionally pinch while painting. */
376 if (brush.crease_pinch_factor != 0.5f) {
377 sculpt_project_v3_cache_init(&spvc, grab_delta);
378 }
379
381 switch (pbvh.type()) {
383 Mesh &mesh = *static_cast<Mesh *>(object.data);
384 const MeshAttributeData attribute_data(mesh.attributes());
385 const PositionDeformData position_data(depsgraph, object);
386 const Span<float3> vert_normals = bke::pbvh::vert_normals_eval(depsgraph, object);
388 node_mask.foreach_index(GrainSize(1), [&](const int i) {
389 LocalData &tls = all_tls.local();
391 sd,
392 object,
393 brush,
394 &spvc,
395 grab_delta,
396 vert_normals,
397 attribute_data,
398 nodes[i],
399 tls,
400 position_data);
401 bke::pbvh::update_node_bounds_mesh(position_data.eval, nodes[i]);
402 });
403 break;
404 }
406 SubdivCCG &subdiv_ccg = *object.sculpt->subdiv_ccg;
407 MutableSpan<float3> positions = subdiv_ccg.positions;
409 node_mask.foreach_index(GrainSize(1), [&](const int i) {
410 LocalData &tls = all_tls.local();
411 calc_grids(depsgraph, sd, object, brush, &spvc, grab_delta, nodes[i], tls);
412 bke::pbvh::update_node_bounds_grids(subdiv_ccg.grid_area, positions, nodes[i]);
413 });
414 break;
415 }
418 node_mask.foreach_index(GrainSize(1), [&](const int i) {
419 LocalData &tls = all_tls.local();
420 calc_bmesh(depsgraph, sd, object, brush, &spvc, grab_delta, nodes[i], tls);
422 });
423 break;
424 }
425 }
426 pbvh.tag_positions_changed(node_mask);
428}
429
430} // namespace blender::ed::sculpt_paint
void BKE_kelvinlet_grab_triscale(float radius_elem_disp[3], const KelvinletParams *params, const float elem_orig_co[3], const float brush_location[3], const float brush_delta[3])
Definition kelvinlet.cc:90
void BKE_kelvinlet_init_params(KelvinletParams *params, float radius, float force, float shear_modulus, float poisson_ratio)
Definition kelvinlet.cc:16
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:654
const blender::Set< BMVert *, 0 > & BKE_pbvh_bmesh_node_unique_verts(blender::bke::pbvh::BMeshNode *node)
#define BLI_NOINLINE
MINLINE float pow2f(float x)
void project_plane_v3_v3v3(float out[3], const float p[3], const float v_plane[3])
@ BRUSH_FRONTFACE
@ BRUSH_SNAKE_HOOK_DEFORM_ELASTIC
eBrushFalloffShape
@ PAINT_FALLOFF_SHAPE_TUBE
Object is a sort of wrapper for general info.
constexpr void fill(const T &value) const
Definition BLI_span.hh:518
void resize(const int64_t new_size)
void tag_positions_changed(const IndexMask &node_mask)
Definition pbvh.cc:549
Span< NodeT > nodes() const
void deform(MutableSpan< float3 > translations, Span< int > verts) const
Definition sculpt.cc:7139
void foreach_index(Fn &&fn) const
const Depsgraph * depsgraph
static float verts[][3]
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:2846
void update_node_bounds_bmesh(BMeshNode &node)
Definition pbvh.cc:1095
void update_node_bounds_mesh(Span< float3 > positions, MeshNode &node)
Definition pbvh.cc:1075
Span< float3 > vert_normals_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:2502
void update_node_bounds_grids(int grid_area, Span< float3 > positions, GridsNode &node)
Definition pbvh.cc:1084
void flush_bounds_to_parents(Tree &pbvh)
Definition pbvh.cc:1132
void calc_grids_factors(const Depsgraph &depsgraph, const Object &object, const Cache &cache, const bke::pbvh::GridsNode &node, Span< int > grids, MutableSpan< float > factors)
void calc_vert_factors(const Depsgraph &depsgraph, const Object &object, const Cache &cache, const bke::pbvh::MeshNode &node, Span< int > verts, MutableSpan< float > factors)
static void calc_bmesh(const Depsgraph &depsgraph, const Sculpt &sd, Object &object, const Brush &brush, const float3 &direction, const float strength, bke::pbvh::BMeshNode &node, LocalData &tls)
static void calc_faces(const Depsgraph &depsgraph, const Sculpt &sd, const Brush &brush, const float4 &test_plane, const float strength, const MeshAttributeData &attribute_data, const Span< float3 > vert_normals, const bke::pbvh::MeshNode &node, Object &object, LocalData &tls, const PositionDeformData &position_data)
Definition clay.cc:58
static void calc_grids(const Depsgraph &depsgraph, const Sculpt &sd, Object &object, const Brush &brush, const float4 &test_plane, const float strength, bke::pbvh::GridsNode &node, LocalData &tls)
Definition clay.cc:95
static BLI_NOINLINE void fill_factor_from_hide_and_mask(const Mesh &mesh, const Span< int > face_indices, const MutableSpan< float > r_factors)
static void sculpt_project_v3_cache_init(SculptProjectVector *spvc, const float3 &plane)
Definition snake_hook.cc:54
static void calc_grids(const Depsgraph &depsgraph, const Sculpt &sd, Object &object, const Brush &brush, SculptProjectVector *spvc, const float3 &grab_delta, bke::pbvh::GridsNode &node, LocalData &tls)
static void calc_faces(const Depsgraph &depsgraph, const Sculpt &sd, Object &object, const Brush &brush, const SculptProjectVector *spvc, const float3 &grab_delta, const Span< float3 > vert_normals, const MeshAttributeData &attribute_data, bke::pbvh::MeshNode &node, LocalData &tls, const PositionDeformData &position_data)
static BLI_NOINLINE void calc_pinch_influence(const Brush &brush, const StrokeCache &cache, const float3 &grab_delta, const SculptProjectVector *spvc, const Span< float3 > positions, const Span< float > factors, const MutableSpan< float3 > translations)
Definition snake_hook.cc:88
static BLI_NOINLINE void calc_rake_rotation_influence(const StrokeCache &cache, const Span< float3 > positions, const Span< float > factors, const MutableSpan< float3 > translations)
static void sculpt_project_v3(const SculptProjectVector *spvc, const float3 &vec, float3 &r_vec)
Definition snake_hook.cc:65
static void calc_bmesh(const Depsgraph &depsgraph, const Sculpt &sd, Object &object, const Brush &brush, SculptProjectVector *spvc, const float3 &grab_delta, bke::pbvh::BMeshNode &node, LocalData &tls)
static float3 sculpt_rake_rotate(const StrokeCache &cache, const float3 &sculpt_co, const float3 &v_co, float factor)
Definition snake_hook.cc:75
static BLI_NOINLINE void calc_kelvinet_translation(const StrokeCache &cache, const Span< float3 > positions, const Span< float > factors, const MutableSpan< float3 > translations)
void do_snake_hook_brush(const Depsgraph &depsgraph, const Sculpt &sd, Object &object, const IndexMask &node_mask)
MutableSpan< float3 > gather_grids_positions(const SubdivCCG &subdiv_ccg, const Span< int > grids, Vector< float3 > &positions)
void gather_bmesh_positions(const Set< BMVert *, 0 > &verts, MutableSpan< float3 > positions)
Definition sculpt.cc:6054
void calc_brush_strength_factors(const StrokeCache &cache, const Brush &brush, Span< float > distances, MutableSpan< float > factors)
Definition sculpt.cc:6889
void apply_hardness_to_distances(float radius, float hardness, MutableSpan< float > distances)
Definition sculpt.cc:6862
void filter_distances_with_radius(float radius, Span< float > distances, MutableSpan< float > factors)
Definition sculpt.cc:6772
void filter_region_clip_factors(const SculptSession &ss, Span< float3 > vert_positions, Span< int > verts, MutableSpan< float > factors)
Definition sculpt.cc:6639
void calc_brush_distances(const SculptSession &ss, Span< float3 > vert_positions, Span< int > vert_indices, eBrushFalloffShape falloff_shape, MutableSpan< float > r_distances)
Definition sculpt.cc:6722
void scale_factors(MutableSpan< float > factors, float strength)
Definition sculpt.cc:7227
void clip_and_lock_translations(const Sculpt &sd, const SculptSession &ss, Span< float3 > positions, Span< int > verts, MutableSpan< float3 > translations)
Definition sculpt.cc:7022
void apply_translations(Span< float3 > translations, Span< int > verts, MutableSpan< float3 > positions)
Definition sculpt.cc:6958
void gather_data_mesh(Span< T > src, Span< int > indices, MutableSpan< T > dst)
Definition sculpt.cc:6082
void calc_front_face(const float3 &view_normal, Span< float3 > normals, MutableSpan< float > factors)
Definition sculpt.cc:6581
void translations_from_offset_and_factors(const float3 &offset, Span< float > factors, MutableSpan< float3 > r_translations)
Definition sculpt.cc:7246
void calc_brush_texture_factors(const SculptSession &ss, const Brush &brush, Span< float3 > vert_positions, Span< int > vert_indices, MutableSpan< float > factors)
Definition sculpt.cc:6898
T length_squared(const VecBase< T, Size > &a)
T pow(const T &x, const T &power)
T length(const VecBase< T, Size > &a)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
void sculpt_project_v3_normal_align(const SculptSession &ss, const float normal_weight, float grab_delta[3])
Definition sculpt.cc:538
int snake_hook_deform_type
char falloff_shape
float crease_pinch_factor
blender::ed::sculpt_paint::StrokeCache * cache
Definition BKE_paint.hh:427
SubdivCCG * subdiv_ccg
Definition BKE_paint.hh:405
blender::Array< blender::float3 > positions
std::unique_ptr< auto_mask::Cache > automasking
std::optional< math::Quaternion > rake_rotation_symm