Blender V5.0
sculpt_transform.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2020 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "MEM_guardedalloc.h"
10
11#include "BLI_array_utils.hh"
13#include "BLI_math_matrix.h"
14#include "BLI_math_matrix.hh"
15#include "BLI_math_rotation.h"
16#include "BLI_math_vector.h"
18#include "BLI_span.hh"
19
20#include "BKE_attribute.hh"
21#include "BKE_brush.hh"
22#include "BKE_context.hh"
23#include "BKE_kelvinlet.h"
24#include "BKE_layer.hh"
25#include "BKE_mesh.hh"
26#include "BKE_paint.hh"
27#include "BKE_paint_bvh.hh"
28#include "BKE_paint_types.hh"
29#include "BKE_subdiv_ccg.hh"
30
31#include "WM_api.hh"
32#include "WM_types.hh"
33
34#include "ED_screen.hh"
35#include "ED_sculpt.hh"
36#include "ED_view3d.hh"
37
38#include "mesh_brush_common.hh"
39#include "paint_intern.hh"
40#include "paint_mask.hh"
41#include "sculpt_filter.hh"
42#include "sculpt_intern.hh"
43#include "sculpt_undo.hh"
44
45#include "RNA_access.hh"
46#include "RNA_define.hh"
47
48#include "bmesh.hh"
49
50#include <cmath>
51#include <cstdlib>
52
54
55void init_transform(bContext *C, Object &ob, const float mval_fl[2], const char *undo_name)
56{
57 const Scene &scene = *CTX_data_scene(C);
59 SculptSession &ss = *ob.sculpt;
61
65
69
71 undo::push_begin_ex(scene, ob, undo_name);
72
73 ss.pivot_rot[3] = 1.0f;
74
76
77 filter::cache_init(C, ob, sd, undo::Type::Position, mval_fl, 5.0, 1.0f);
78
81 }
82 else {
84 }
85}
86
87static std::array<float4x4, 8> transform_matrices_init(const SculptSession &ss,
88 const ePaintSymmetryFlags symm,
89 const TransformDisplacementMode t_mode)
90{
91 std::array<float4x4, 8> mats;
92
93 float3 final_pivot_pos, d_t, d_s;
94 float d_r[4];
95 float t_mat[4][4], r_mat[4][4], s_mat[4][4], pivot_mat[4][4], pivot_imat[4][4],
96 transform_mat[4][4];
97
98 float start_pivot_pos[3], start_pivot_rot[4], start_pivot_scale[3];
99 switch (t_mode) {
101 copy_v3_v3(start_pivot_pos, ss.init_pivot_pos);
102 copy_v4_v4(start_pivot_rot, ss.init_pivot_rot);
103 copy_v3_v3(start_pivot_scale, ss.init_pivot_scale);
104 break;
106 copy_v3_v3(start_pivot_pos, ss.prev_pivot_pos);
107 copy_v4_v4(start_pivot_rot, ss.prev_pivot_rot);
108 copy_v3_v3(start_pivot_scale, ss.prev_pivot_scale);
109 break;
110 }
111
112 for (int i = 0; i < PAINT_SYMM_AREAS; i++) {
114
115 copy_v3_v3(final_pivot_pos, ss.pivot_pos);
116
117 unit_m4(pivot_mat);
118
119 unit_m4(t_mat);
120 unit_m4(r_mat);
121 unit_m4(s_mat);
122
123 /* Translation matrix. */
124 sub_v3_v3v3(d_t, ss.pivot_pos, start_pivot_pos);
125 d_t = SCULPT_flip_v3_by_symm_area(d_t, symm, v_symm, ss.init_pivot_pos);
126 translate_m4(t_mat, d_t[0], d_t[1], d_t[2]);
127
128 /* Rotation matrix. */
129 sub_qt_qtqt(d_r, ss.pivot_rot, start_pivot_rot);
130 normalize_qt(d_r);
131 SCULPT_flip_quat_by_symm_area(d_r, symm, v_symm, ss.init_pivot_pos);
132 quat_to_mat4(r_mat, d_r);
133
134 /* Scale matrix. */
135 sub_v3_v3v3(d_s, ss.pivot_scale, start_pivot_scale);
136 add_v3_fl(d_s, 1.0f);
137 size_to_mat4(s_mat, d_s);
138
139 /* Pivot matrix. */
140 final_pivot_pos = SCULPT_flip_v3_by_symm_area(final_pivot_pos, symm, v_symm, start_pivot_pos);
141 translate_m4(pivot_mat, final_pivot_pos[0], final_pivot_pos[1], final_pivot_pos[2]);
142 invert_m4_m4(pivot_imat, pivot_mat);
143
144 /* Final transform matrix. */
145 mul_m4_m4m4(transform_mat, r_mat, t_mat);
146 mul_m4_m4m4(transform_mat, transform_mat, s_mat);
147 mul_m4_m4m4(mats[i].ptr(), transform_mat, pivot_imat);
148 mul_m4_m4m4(mats[i].ptr(), pivot_mat, mats[i].ptr());
149 }
150
151 return mats;
152}
153
154static constexpr float transform_mirror_max_distance_eps = 0.00002f;
155
161
163 const Span<float3> positions,
164 const std::array<float4x4, 8> &transform_mats,
165 const MutableSpan<float3> translations)
166{
167 for (const int i : positions.index_range()) {
168 const ePaintSymmetryAreas symm_area = SCULPT_get_vertex_symm_area(positions[i]);
169 const float3 transformed = math::transform_point(transform_mats[symm_area], positions[i]);
170 translations[i] = transformed - positions[i];
171 }
172}
173
175 const ePaintSymmetryFlags symm,
176 const MutableSpan<float3> translations)
177{
178 if ((symm & (PAINT_SYMM_X | PAINT_SYMM_Y | PAINT_SYMM_Z)) == 0) {
179 return;
180 }
181 for (const int i : positions.index_range()) {
182 if ((symm & PAINT_SYMM_X) && (std::abs(positions[i].x) < transform_mirror_max_distance_eps)) {
183 translations[i].x = 0.0f;
184 }
185 if ((symm & PAINT_SYMM_Y) && (std::abs(positions[i].y) < transform_mirror_max_distance_eps)) {
186 translations[i].y = 0.0f;
187 }
188 if ((symm & PAINT_SYMM_Z) && (std::abs(positions[i].z) < transform_mirror_max_distance_eps)) {
189 translations[i].z = 0.0f;
190 }
191 }
192}
193
194static void transform_node_mesh(const Sculpt &sd,
195 const std::array<float4x4, 8> &transform_mats,
196 const MeshAttributeData &attribute_data,
197 const bke::pbvh::MeshNode &node,
198 Object &object,
200 const PositionDeformData &position_data)
201{
202 SculptSession &ss = *object.sculpt;
203
204 const Span<int> verts = node.verts();
205 const OrigPositionData orig_data = orig_position_data_get_mesh(object, node);
206
207 tls.factors.resize(verts.size());
208 const MutableSpan<float> factors = tls.factors;
209 fill_factor_from_hide_and_mask(attribute_data.hide_vert, attribute_data.mask, verts, factors);
210
211 tls.translations.resize(verts.size());
212 const MutableSpan<float3> translations = tls.translations;
213 calc_symm_area_transform_translations(orig_data.positions, transform_mats, translations);
214 scale_translations(translations, factors);
215
217 filter_translations_with_symmetry(orig_data.positions, symm, translations);
218
219 clip_and_lock_translations(sd, ss, position_data.eval, verts, translations);
220 position_data.deform(translations, verts);
221}
222
223static void transform_node_grids(const Sculpt &sd,
224 const std::array<float4x4, 8> &transform_mats,
225 const bke::pbvh::GridsNode &node,
226 Object &object,
228{
229 SculptSession &ss = *object.sculpt;
230 SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
231 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
232
233 const Span<int> grids = node.grids();
234 const int grid_verts_num = grids.size() * key.grid_area;
235
236 const OrigPositionData orig_data = orig_position_data_get_grids(object, node);
237
238 tls.factors.resize(grid_verts_num);
239 const MutableSpan<float> factors = tls.factors;
240 fill_factor_from_hide_and_mask(subdiv_ccg, grids, factors);
241
242 tls.translations.resize(grid_verts_num);
243 const MutableSpan<float3> translations = tls.translations;
244 calc_symm_area_transform_translations(orig_data.positions, transform_mats, translations);
245
246 scale_translations(translations, factors);
247
249 filter_translations_with_symmetry(orig_data.positions, symm, translations);
250
251 clip_and_lock_translations(sd, ss, orig_data.positions, translations);
252 apply_translations(translations, grids, subdiv_ccg);
253}
254
255static void transform_node_bmesh(const Sculpt &sd,
256 const std::array<float4x4, 8> &transform_mats,
258 Object &object,
260{
261 SculptSession &ss = *object.sculpt;
262
264
265 Array<float3> orig_positions(verts.size());
266 Array<float3> orig_normals(verts.size());
267 orig_position_data_gather_bmesh(*ss.bm_log, verts, orig_positions, orig_normals);
268
269 tls.factors.resize(verts.size());
270 const MutableSpan<float> factors = tls.factors;
272
273 tls.translations.resize(verts.size());
274 const MutableSpan<float3> translations = tls.translations;
275 calc_symm_area_transform_translations(orig_positions, transform_mats, translations);
276
277 scale_translations(translations, factors);
278
280 filter_translations_with_symmetry(orig_positions, symm, translations);
281
282 clip_and_lock_translations(sd, ss, orig_positions, translations);
283 apply_translations(translations, verts);
284}
285
286static void sculpt_transform_all_vertices(const Depsgraph &depsgraph, const Sculpt &sd, Object &ob)
287{
289
290 SculptSession &ss = *ob.sculpt;
292
293 std::array<float4x4, 8> transform_mats = transform_matrices_init(
295
296 /* Regular transform applies all symmetry passes at once as it is split by symmetry areas
297 * (each vertex can only be transformed once by the transform matrix of its area). */
299 const IndexMask &node_mask = ss.filter_cache->node_mask;
300
302 switch (pbvh.type()) {
304 Mesh &mesh = *static_cast<Mesh *>(ob.data);
305 const MeshAttributeData attribute_data(mesh);
307 const PositionDeformData position_data(depsgraph, ob);
308 node_mask.foreach_index(GrainSize(1), [&](const int i) {
309 TransformLocalData &tls = all_tls.local();
310 transform_node_mesh(sd, transform_mats, attribute_data, nodes[i], ob, tls, position_data);
311 bke::pbvh::update_node_bounds_mesh(position_data.eval, nodes[i]);
312 });
313 break;
314 }
316 SubdivCCG &subdiv_ccg = *ob.sculpt->subdiv_ccg;
317 MutableSpan<float3> positions = subdiv_ccg.positions;
319 node_mask.foreach_index(GrainSize(1), [&](const int i) {
320 TransformLocalData &tls = all_tls.local();
321 transform_node_grids(sd, transform_mats, nodes[i], ob, tls);
322 bke::pbvh::update_node_bounds_grids(subdiv_ccg.grid_area, positions, nodes[i]);
323 });
324 break;
325 }
328 node_mask.foreach_index(GrainSize(1), [&](const int i) {
329 TransformLocalData &tls = all_tls.local();
330 transform_node_bmesh(sd, transform_mats, nodes[i], ob, tls);
332 });
333 break;
334 }
335 }
336 pbvh.tag_positions_changed(node_mask);
338}
339
340BLI_NOINLINE static void calc_transform_translations(const float4x4 &elastic_transform_mat,
341 const Span<float3> positions,
342 const MutableSpan<float3> r_translations)
343{
344 for (const int i : positions.index_range()) {
345 const float3 transformed = math::transform_point(elastic_transform_mat, positions[i]);
346 r_translations[i] = transformed - positions[i];
347 }
348}
349
351 const float3 &elastic_transform_pivot,
352 const Span<float3> positions,
353 const MutableSpan<float3> translations)
354{
355 for (const int i : positions.index_range()) {
357 translations[i], &params, positions[i], elastic_transform_pivot, translations[i]);
358 }
359}
360
362 const KelvinletParams &params,
363 const float4x4 &elastic_transform_mat,
364 const float3 &elastic_transform_pivot,
365 const MeshAttributeData &attribute_data,
366 const bke::pbvh::MeshNode &node,
367 Object &object,
369 const PositionDeformData &position_data)
370{
371 const SculptSession &ss = *object.sculpt;
372
373 const Span<int> verts = node.verts();
374 const MutableSpan positions = gather_data_mesh(position_data.eval, verts, tls.positions);
375
376 /* TODO: Using the factors array is unnecessary when there are no hidden vertices and no mask. */
377 tls.factors.resize(verts.size());
378 const MutableSpan<float> factors = tls.factors;
379 fill_factor_from_hide_and_mask(attribute_data.hide_vert, attribute_data.mask, verts, factors);
380 scale_factors(factors, 20.0f);
381
382 tls.translations.resize(verts.size());
383 const MutableSpan<float3> translations = tls.translations;
384 calc_transform_translations(elastic_transform_mat, positions, translations);
385 apply_kelvinet_to_translations(params, elastic_transform_pivot, positions, translations);
386
387 scale_translations(translations, factors);
388
389 clip_and_lock_translations(sd, ss, position_data.eval, verts, translations);
390 position_data.deform(translations, verts);
391}
392
394 const KelvinletParams &params,
395 const float4x4 &elastic_transform_mat,
396 const float3 &elastic_transform_pivot,
397 const bke::pbvh::GridsNode &node,
398 Object &object,
400{
401 SculptSession &ss = *object.sculpt;
402 SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
403
404 const Span<int> grids = node.grids();
405 const MutableSpan positions = gather_grids_positions(subdiv_ccg, grids, tls.positions);
406
407 /* TODO: Using the factors array is unnecessary when there are no hidden vertices and no mask. */
408 tls.factors.resize(positions.size());
409 const MutableSpan<float> factors = tls.factors;
410 fill_factor_from_hide_and_mask(subdiv_ccg, grids, factors);
411 scale_factors(factors, 20.0f);
412
413 tls.translations.resize(positions.size());
414 const MutableSpan<float3> translations = tls.translations;
415 calc_transform_translations(elastic_transform_mat, positions, translations);
416 apply_kelvinet_to_translations(params, elastic_transform_pivot, positions, translations);
417
418 scale_translations(translations, factors);
419
420 clip_and_lock_translations(sd, ss, positions, translations);
421 apply_translations(translations, grids, subdiv_ccg);
422}
423
425 const KelvinletParams &params,
426 const float4x4 &elastic_transform_mat,
427 const float3 &elastic_transform_pivot,
429 Object &object,
431{
432 SculptSession &ss = *object.sculpt;
433
435 const MutableSpan positions = gather_bmesh_positions(verts, tls.positions);
436
437 tls.factors.resize(verts.size());
438 const MutableSpan<float> factors = tls.factors;
440 scale_factors(factors, 20.0f);
441
442 tls.translations.resize(verts.size());
443 const MutableSpan<float3> translations = tls.translations;
444 calc_transform_translations(elastic_transform_mat, positions, translations);
445 apply_kelvinet_to_translations(params, elastic_transform_pivot, positions, translations);
446
447 scale_translations(translations, factors);
448
449 clip_and_lock_translations(sd, ss, positions, translations);
450 apply_translations(translations, verts);
451}
452
453static void transform_radius_elastic(const Depsgraph &depsgraph,
454 const Sculpt &sd,
455 Object &ob,
456 const float transform_radius)
457{
458 SculptSession &ss = *ob.sculpt;
461
463
464 std::array<float4x4, 8> transform_mats = transform_matrices_init(
466
468 const IndexMask &node_mask = ss.filter_cache->node_mask;
469
471 /* TODO(pablodp606): These parameters can be exposed if needed as transform strength and volume
472 * preservation like in the elastic deform brushes. Setting them to the same default as elastic
473 * deform triscale grab because they work well in most cases. */
474 const float force = 1.0f;
475 const float shear_modulus = 1.0f;
476 const float poisson_ratio = 0.4f;
477 BKE_kelvinlet_init_params(&params, transform_radius, force, shear_modulus, poisson_ratio);
478
480 for (ePaintSymmetryFlags symmpass = PAINT_SYMM_NONE; symmpass <= symm; symmpass++) {
481 if (!is_symmetry_iteration_valid(symmpass, symm)) {
482 continue;
483 }
484
485 const float3 elastic_transform_pivot = symmetry_flip(ss.pivot_pos, symmpass);
486
487 const int symm_area = SCULPT_get_vertex_symm_area(elastic_transform_pivot);
488 float4x4 elastic_transform_mat = transform_mats[symm_area];
489 switch (pbvh.type()) {
491 Mesh &mesh = *static_cast<Mesh *>(ob.data);
493 const PositionDeformData position_data(depsgraph, ob);
494 const MeshAttributeData attribute_data(mesh);
495 node_mask.foreach_index(GrainSize(1), [&](const int i) {
496 TransformLocalData &tls = all_tls.local();
498 params,
499 elastic_transform_mat,
500 elastic_transform_pivot,
501 attribute_data,
502 nodes[i],
503 ob,
504 tls,
505 position_data);
506 bke::pbvh::update_node_bounds_mesh(position_data.eval, nodes[i]);
507 });
508 break;
509 }
511 SubdivCCG &subdiv_ccg = *ob.sculpt->subdiv_ccg;
512 MutableSpan<float3> positions = subdiv_ccg.positions;
514 node_mask.foreach_index(GrainSize(1), [&](const int i) {
515 TransformLocalData &tls = all_tls.local();
517 sd, params, elastic_transform_mat, elastic_transform_pivot, nodes[i], ob, tls);
518 bke::pbvh::update_node_bounds_grids(subdiv_ccg.grid_area, positions, nodes[i]);
519 });
520 break;
521 }
524 node_mask.foreach_index(GrainSize(1), [&](const int i) {
525 TransformLocalData &tls = all_tls.local();
527 sd, params, elastic_transform_mat, elastic_transform_pivot, nodes[i], ob, tls);
529 });
530 break;
531 }
532 }
533 }
534 pbvh.tag_positions_changed(node_mask);
536}
537
539{
540 const Sculpt &sd = *CTX_data_tool_settings(C)->sculpt;
541 SculptSession &ss = *ob.sculpt;
543
546
547 switch (sd.transform_mode) {
550 break;
551 }
553 const Brush &brush = *BKE_paint_brush_for_read(&sd.paint);
554 float transform_radius;
555
556 if (BKE_brush_use_locked_size(&sd.paint, &brush)) {
557 transform_radius = BKE_brush_unprojected_radius_get(&sd.paint, &brush);
558 }
559 else {
561
562 transform_radius = paint_calc_object_space_radius(
563 vc, ss.init_pivot_pos, BKE_brush_radius_get(&sd.paint, &brush));
564 }
565
566 transform_radius_elastic(*depsgraph, sd, ob, transform_radius);
567 break;
568 }
569 }
570
574
576}
577
579{
580 /* Canceling "Elastic" transforms (due to its #TransformDisplacementMode::Incremental nature),
581 * requires restoring positions from undo. For "All Vertices" there is no benefit in using the
582 * transform system to update to original positions either. */
585
588 pbvh.update_bounds(depsgraph, ob);
589}
590
592{
593 SculptSession &ss = *ob.sculpt;
594 MEM_delete(ss.filter_cache);
595 ss.filter_cache = nullptr;
596 undo::push_end(ob);
598}
599
607
610 "ORIGIN",
611 0,
612 "Origin",
613 "Sets the pivot to the origin of the sculpt"},
615 "UNMASKED",
616 0,
617 "Unmasked",
618 "Sets the pivot position to the average position of the unmasked vertices"},
620 "BORDER",
621 0,
622 "Mask Border",
623 "Sets the pivot position to the center of the border of the mask"},
625 "ACTIVE",
626 0,
627 "Active Vertex",
628 "Sets the pivot position to the active vertex position"},
630 "SURFACE",
631 0,
632 "Surface",
633 "Sets the pivot position to the surface under the cursor"},
634 {0, nullptr, 0, nullptr, nullptr},
635};
636
638{
639 if (!ptr) {
640 return true;
641 }
644}
645
650
656
658 const Span<float> factors,
660{
661 BLI_assert(positions.size() == factors.size());
662
663 for (const int i : positions.index_range()) {
664 total.position += double3(positions[i] * factors[i]);
665 total.weight_total += factors[i];
666 }
667}
668
670 const Object &object,
671 const float3 &pivot,
672 const ePaintSymmetryFlags symm)
673{
674 const SculptSession &ss = *object.sculpt;
675 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
676
677 IndexMaskMemory memory;
678 const IndexMask node_mask = bke::pbvh::search_nodes(
679 pbvh, memory, [&](const bke::pbvh::Node &node) {
680 return !node_fully_masked_or_hidden(node);
681 });
682
683 struct LocalData {
684 Vector<float> factors;
685 Vector<float3> positions;
686 };
687
689 switch (pbvh.type()) {
692 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
693 const MeshAttributeData attribute_data(mesh);
694 const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, object);
696 node_mask.index_range(),
697 1,
699 [&](const IndexRange range, AveragePositionAccumulation sum) {
700 LocalData &tls = all_tls.local();
701 threading::isolate_task([&]() {
702 node_mask.slice(range).foreach_index([&](const int i) {
703 const Span<int> verts = nodes[i].verts();
704
705 tls.positions.resize(verts.size());
706 const MutableSpan<float3> positions = tls.positions;
707 array_utils::gather(vert_positions, verts, positions);
708
709 tls.factors.resize(verts.size());
710 const MutableSpan<float> factors = tls.factors;
711 fill_factor_from_hide_and_mask(
712 attribute_data.hide_vert, attribute_data.mask, verts, factors);
713 filter_verts_outside_symmetry_area(positions, pivot, symm, factors);
714
715 accumulate_weighted_average_position(positions, factors, sum);
716 });
717 });
718 return sum;
719 },
721 return float3(math::safe_divide(total.position, total.weight_total));
722 }
725 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
727 node_mask.index_range(),
728 1,
730 [&](const IndexRange range, AveragePositionAccumulation sum) {
731 LocalData &tls = all_tls.local();
732 node_mask.slice(range).foreach_index([&](const int i) {
733 const Span<int> grids = nodes[i].grids();
734 const MutableSpan positions = gather_grids_positions(
735 subdiv_ccg, grids, tls.positions);
736
737 tls.factors.resize(positions.size());
738 const MutableSpan<float> factors = tls.factors;
739 fill_factor_from_hide_and_mask(subdiv_ccg, grids, factors);
740 filter_verts_outside_symmetry_area(positions, pivot, symm, factors);
741
742 accumulate_weighted_average_position(positions, factors, sum);
743 });
744 return sum;
745 },
747 return float3(math::safe_divide(total.position, total.weight_total));
748 }
751 const AveragePositionAccumulation total = threading::parallel_reduce(
752 node_mask.index_range(),
753 1,
754 AveragePositionAccumulation{},
755 [&](const IndexRange range, AveragePositionAccumulation sum) {
756 LocalData &tls = all_tls.local();
757 node_mask.slice(range).foreach_index([&](const int i) {
758 const Set<BMVert *, 0> &verts = BKE_pbvh_bmesh_node_unique_verts(
759 &const_cast<bke::pbvh::BMeshNode &>(nodes[i]));
760 const MutableSpan positions = gather_bmesh_positions(verts, tls.positions);
761
762 tls.factors.resize(verts.size());
763 const MutableSpan<float> factors = tls.factors;
764 fill_factor_from_hide_and_mask(*ss.bm, verts, factors);
765 filter_verts_outside_symmetry_area(positions, pivot, symm, factors);
766
767 accumulate_weighted_average_position(positions, factors, sum);
768 });
769 return sum;
770 },
772 return float3(math::safe_divide(total.position, total.weight_total));
773 }
774 }
776 return float3(0);
777}
778
780 const MutableSpan<float> factors)
781{
782 constexpr float threshold = 0.2f;
783
784 for (const int i : masks.index_range()) {
785 if (std::abs(masks[i] - 0.5f) > threshold) {
786 factors[i] = 0.0f;
787 }
788 }
789};
790
792 const Object &object,
793 const float3 &pivot,
794 const ePaintSymmetryFlags symm)
795{
796 const SculptSession &ss = *object.sculpt;
797 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
798
799 IndexMaskMemory memory;
800 const IndexMask node_mask = bke::pbvh::search_nodes(
801 pbvh, memory, [&](const bke::pbvh::Node &node) {
802 return !node_fully_masked_or_hidden(node);
803 });
804
805 struct LocalData {
806 Vector<float> factors;
807 Vector<float> masks;
808 Vector<float3> positions;
809 };
810
812 switch (pbvh.type()) {
815 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
816 const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, object);
817 const bke::AttributeAccessor attributes = mesh.attributes();
818 const VArraySpan mask_attr = *attributes.lookup_or_default<float>(
819 ".sculpt_mask", bke::AttrDomain::Point, 0.0f);
820 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
822 node_mask.index_range(),
823 1,
825 [&](const IndexRange range, AveragePositionAccumulation sum) {
826 LocalData &tls = all_tls.local();
827 node_mask.slice(range).foreach_index([&](const int i) {
828 const Span<int> verts = nodes[i].verts();
829 MutableSpan positions = gather_data_mesh(vert_positions, verts, tls.positions);
830 MutableSpan masks = gather_data_mesh(mask_attr, verts, tls.masks);
831
832 tls.factors.resize(verts.size());
833 const MutableSpan<float> factors = tls.factors;
834 fill_factor_from_hide(hide_vert, verts, factors);
835
836 mask_border_weight_calc(masks, factors);
837 filter_verts_outside_symmetry_area(positions, pivot, symm, factors);
838
839 accumulate_weighted_average_position(positions, factors, sum);
840 });
841 return sum;
842 },
844 return float3(math::safe_divide(total.position, total.weight_total));
845 }
848 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
850 node_mask.index_range(),
851 1,
853 [&](const IndexRange range, AveragePositionAccumulation sum) {
854 LocalData &tls = all_tls.local();
855 node_mask.slice(range).foreach_index([&](const int i) {
856 const Span<int> grids = nodes[i].grids();
857 const MutableSpan positions = gather_grids_positions(
858 subdiv_ccg, grids, tls.positions);
859
860 tls.masks.resize(positions.size());
861 const MutableSpan<float> masks = tls.masks;
862 mask::gather_mask_grids(subdiv_ccg, grids, masks);
863
864 tls.factors.resize(positions.size());
865 const MutableSpan<float> factors = tls.factors;
866 fill_factor_from_hide(subdiv_ccg, grids, factors);
867 mask_border_weight_calc(masks, factors);
868 filter_verts_outside_symmetry_area(positions, pivot, symm, factors);
869
870 accumulate_weighted_average_position(positions, factors, sum);
871 });
872 return sum;
873 },
875 return float3(math::safe_divide(total.position, total.weight_total));
876 }
879 const AveragePositionAccumulation total = threading::parallel_reduce(
880 node_mask.index_range(),
881 1,
882 AveragePositionAccumulation{},
883 [&](const IndexRange range, AveragePositionAccumulation sum) {
884 LocalData &tls = all_tls.local();
885 node_mask.slice(range).foreach_index([&](const int i) {
886 const Set<BMVert *, 0> &verts = BKE_pbvh_bmesh_node_unique_verts(
887 &const_cast<bke::pbvh::BMeshNode &>(nodes[i]));
888 const MutableSpan positions = gather_bmesh_positions(verts, tls.positions);
889
890 tls.masks.resize(verts.size());
891 const MutableSpan<float> masks = tls.masks;
892 mask::gather_mask_bmesh(*ss.bm, verts, masks);
893
894 tls.factors.resize(verts.size());
895 const MutableSpan<float> factors = tls.factors;
896 fill_factor_from_hide(verts, factors);
897 mask_border_weight_calc(masks, factors);
898 filter_verts_outside_symmetry_area(positions, pivot, symm, factors);
899
900 accumulate_weighted_average_position(positions, factors, sum);
901 });
902 return sum;
903 },
904 combine_average_position_accumulation);
905 return float3(math::safe_divide(total.position, total.weight_total));
906 }
907 }
909 return float3(0);
910}
911
913{
915 SculptSession &ss = *ob.sculpt;
916 ARegion *region = CTX_wm_region(C);
919
920 const PivotPositionMode mode = PivotPositionMode(RNA_enum_get(op->ptr, "mode"));
921
922 const View3D *v3d = CTX_wm_view3d(C);
923 const Base *base = CTX_data_active_base(C);
924 if (!BKE_base_is_visible(v3d, base)) {
925 return OPERATOR_CANCELLED;
926 }
927
929
930 switch (mode) {
932 ss.pivot_pos = float3(0.0f);
933 break;
936 break;
939 break;
941 const float2 mval(RNA_float_get(op->ptr, "mouse_x"), RNA_float_get(op->ptr, "mouse_y"));
943 if (cursor_geometry_info_update(C, &cgi, mval, false)) {
945 }
946 break;
947 }
949 const float2 mval(RNA_float_get(op->ptr, "mouse_x"), RNA_float_get(op->ptr, "mouse_y"));
950 float3 stroke_location;
951 if (stroke_get_location_bvh(C, stroke_location, mval, false)) {
952 ss.pivot_pos = stroke_location;
953 }
954 break;
955 }
956 }
957
958 /* Update the viewport navigation rotation origin. */
960 bke::PaintRuntime *paint_runtime = paint->runtime;
961 paint_runtime->average_stroke_accum = ss.pivot_pos;
962 paint_runtime->average_stroke_counter = 1;
963 paint_runtime->last_stroke_valid = true;
964
965 ED_region_tag_redraw(region);
967
968 return OPERATOR_FINISHED;
969}
970
972 wmOperator *op,
973 const wmEvent *event)
974{
975 RNA_float_set(op->ptr, "mouse_x", event->mval[0]);
976 RNA_float_set(op->ptr, "mouse_y", event->mval[1]);
977 return set_pivot_position_exec(C, op);
978}
979
981 wmOperator *op,
982 const PropertyRNA *prop)
983{
984 if (STRPREFIX(RNA_property_identifier(prop), "mouse_")) {
985 const PivotPositionMode mode = PivotPositionMode(RNA_enum_get(op->ptr, "mode"));
987 }
988 return true;
989}
990
992{
993 ot->name = "Set Pivot Position";
994 ot->idname = "SCULPT_OT_set_pivot_position";
995 ot->description = "Sets the sculpt transform pivot position";
996
999 ot->poll = SCULPT_mode_poll;
1000 ot->depends_on_cursor = set_pivot_depends_on_cursor;
1001 ot->poll_property = set_pivot_position_poll_property;
1002
1003 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1004 RNA_def_enum(ot->srna,
1005 "mode",
1008 "Mode",
1009 "");
1010
1011 RNA_def_float(ot->srna,
1012 "mouse_x",
1013 0.0f,
1014 0.0f,
1015 FLT_MAX,
1016 "Mouse Position X",
1017 "Position of the mouse used for \"Surface\" and \"Active Vertex\" mode",
1018 0.0f,
1019 10000.0f);
1020 RNA_def_float(ot->srna,
1021 "mouse_y",
1022 0.0f,
1023 0.0f,
1024 FLT_MAX,
1025 "Mouse Position Y",
1026 "Position of the mouse used for \"Surface\" and \"Active Vertex\" mode",
1027 0.0f,
1028 10000.0f);
1029}
1030
1031} // namespace blender::ed::sculpt_paint
bool BKE_brush_use_locked_size(const Paint *paint, const Brush *brush)
Definition brush.cc:1277
float BKE_brush_radius_get(const Paint *paint, const Brush *brush)
Definition brush.cc:1272
float BKE_brush_unprojected_radius_get(const Paint *paint, const Brush *brush)
Definition brush.cc:1315
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Base * CTX_data_active_base(const bContext *C)
ToolSettings * CTX_data_tool_settings(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
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
bool BKE_base_is_visible(const View3D *v3d, const Base *base)
#define PAINT_SYMM_AREAS
Definition BKE_paint.hh:115
std::variant< std::monostate, int, BMVert * > ActiveVert
Definition BKE_paint.hh:360
ePaintSymmetryAreas
Definition BKE_paint.hh:108
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:650
void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph, Object *ob_orig, bool is_paint_tool)
Definition paint.cc:2797
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:476
A BVH for high poly meshes.
const blender::Set< BMVert *, 0 > & BKE_pbvh_bmesh_node_unique_verts(blender::bke::pbvh::BMeshNode *node)
CCGKey BKE_subdiv_ccg_key_top_level(const SubdivCCG &subdiv_ccg)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_NOINLINE
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void translate_m4(float mat[4][4], float Tx, float Ty, float Tz)
void size_to_mat4(float R[4][4], const float size[3])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
void unit_m4(float m[4][4])
void sub_qt_qtqt(float q[4], const float a[4], const float b[4])
float normalize_qt(float q[4])
void quat_to_mat4(float m[4][4], const float q[4])
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE void add_v3_fl(float r[3], float f)
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
#define STRPREFIX(a, b)
#define ELEM(...)
ePaintSymmetryFlags
@ PAINT_SYMM_Y
@ PAINT_SYMM_X
@ PAINT_SYMM_Z
@ PAINT_SYMM_NONE
@ SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC
@ SCULPT_TRANSFORM_MODE_ALL_VERTICES
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:393
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_SELECT
Definition WM_types.hh:508
BPy_StructRNA * depsgraph
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
static T sum(const btAlignedObjectArray< T > &items)
void resize(const int64_t new_size)
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
GAttributeReader lookup(const StringRef attribute_id) const
void tag_positions_changed(const IndexMask &node_mask)
Definition pbvh.cc:635
Span< NodeT > nodes() const
void update_bounds(const Depsgraph &depsgraph, const Object &object)
Definition pbvh.cc:1386
void flush_bounds_to_parents()
Definition pbvh.cc:1306
void foreach_index(Fn &&fn) const
static float verts[][3]
VecBase< float, 3 > float3
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:3052
IndexMask search_nodes(const Tree &pbvh, IndexMaskMemory &memory, FunctionRef< bool(const Node &)> filter_fn)
Definition pbvh.cc:2663
void update_normals(const Depsgraph &depsgraph, Object &object_orig, Tree &pbvh)
Definition pbvh.cc:1257
void update_node_bounds_bmesh(BMeshNode &node)
Definition pbvh.cc:1294
void update_node_bounds_mesh(Span< float3 > positions, MeshNode &node)
Definition pbvh.cc:1274
void update_node_bounds_grids(int grid_area, Span< float3 > positions, GridsNode &node)
Definition pbvh.cc:1283
Span< float3 > vert_positions_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:1040
void cache_init(bContext *C, Object &ob, Sculpt &sd, undo::Type undo_type, const float mval_fl[2], float area_normal_radius, float start_strength)
void push_begin_ex(const Scene &, Object &ob, const char *name)
void restore_position_from_undo_step(const Depsgraph &depsgraph, Object &object)
Definition sculpt.cc:1031
bool stroke_get_location_bvh(bContext *C, float out[3], const float mval[2], const bool force_original)
Definition sculpt.cc:4940
static void elastic_transform_node_bmesh(const Sculpt &sd, const KelvinletParams &params, const float4x4 &elastic_transform_mat, const float3 &elastic_transform_pivot, bke::pbvh::BMeshNode &node, Object &object, TransformLocalData &tls)
void fill_factor_from_hide_and_mask(Span< bool > hide_vert, Span< float > mask, Span< int > verts, MutableSpan< float > r_factors)
Definition sculpt.cc:6823
static BLI_NOINLINE void mask_border_weight_calc(const Span< float > masks, const MutableSpan< float > factors)
MutableSpan< float3 > gather_grids_positions(const SubdivCCG &subdiv_ccg, const Span< int > grids, Vector< float3 > &positions)
static wmOperatorStatus set_pivot_position_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void gather_bmesh_positions(const Set< BMVert *, 0 > &verts, MutableSpan< float3 > positions)
Definition sculpt.cc:6367
static std::array< float4x4, 8 > transform_matrices_init(const SculptSession &ss, const ePaintSymmetryFlags symm, const TransformDisplacementMode t_mode)
static bool set_pivot_position_poll_property(const bContext *, wmOperator *op, const PropertyRNA *prop)
void update_modal_transform(bContext *C, Object &ob)
void vert_random_access_ensure(Object &object)
Definition sculpt.cc:141
static void transform_node_mesh(const Sculpt &sd, const std::array< float4x4, 8 > &transform_mats, const MeshAttributeData &attribute_data, const bke::pbvh::MeshNode &node, Object &object, TransformLocalData &tls, const PositionDeformData &position_data)
bool node_fully_masked_or_hidden(const bke::pbvh::Node &node)
Definition sculpt.cc:2418
static void transform_node_grids(const Sculpt &sd, const std::array< float4x4, 8 > &transform_mats, const bke::pbvh::GridsNode &node, Object &object, TransformLocalData &tls)
float3 symmetry_flip(const float3 &src, const ePaintSymmetryFlags symm)
void orig_position_data_gather_bmesh(const BMLog &bm_log, const Set< BMVert *, 0 > &verts, MutableSpan< float3 > positions, MutableSpan< float3 > normals)
static BLI_NOINLINE void calc_symm_area_transform_translations(const Span< float3 > positions, const std::array< float4x4, 8 > &transform_mats, const MutableSpan< float3 > translations)
void scale_translations(MutableSpan< float3 > translations, Span< float > factors)
Definition sculpt.cc:7495
void end_transform(bContext *C, Object &ob)
static void elastic_transform_node_mesh(const Sculpt &sd, const KelvinletParams &params, const float4x4 &elastic_transform_mat, const float3 &elastic_transform_pivot, const MeshAttributeData &attribute_data, const bke::pbvh::MeshNode &node, Object &object, TransformLocalData &tls, const PositionDeformData &position_data)
void init_transform(bContext *C, Object &ob, const float mval_fl[2], const char *undo_name)
static wmOperatorStatus set_pivot_position_exec(bContext *C, wmOperator *op)
void scale_factors(MutableSpan< float > factors, float strength)
Definition sculpt.cc:7512
void cancel_modal_transform(bContext *C, Object &ob)
static BLI_NOINLINE void filter_translations_with_symmetry(const Span< float3 > positions, const ePaintSymmetryFlags symm, const MutableSpan< float3 > translations)
void clip_and_lock_translations(const Sculpt &sd, const SculptSession &ss, Span< float3 > positions, Span< int > verts, MutableSpan< float3 > translations)
Definition sculpt.cc:7335
bool cursor_geometry_info_update(bContext *C, CursorGeometryInfo *out, const float2 &mval, const bool use_sampled_normal)
Definition sculpt.cc:4685
static BLI_NOINLINE void accumulate_weighted_average_position(const Span< float3 > positions, const Span< float > factors, AveragePositionAccumulation &total)
void flush_update_done(const bContext *C, Object &ob, const UpdateType update_type)
Definition sculpt.cc:5146
static void elastic_transform_node_grids(const Sculpt &sd, const KelvinletParams &params, const float4x4 &elastic_transform_mat, const float3 &elastic_transform_pivot, const bke::pbvh::GridsNode &node, Object &object, TransformLocalData &tls)
static void transform_node_bmesh(const Sculpt &sd, const std::array< float4x4, 8 > &transform_mats, bke::pbvh::BMeshNode &node, Object &object, TransformLocalData &tls)
static constexpr float transform_mirror_max_distance_eps
void apply_translations(Span< float3 > translations, Span< int > verts, MutableSpan< float3 > positions)
Definition sculpt.cc:7268
OrigPositionData orig_position_data_get_mesh(const Object &object, const bke::pbvh::MeshNode &node)
void gather_data_mesh(Span< T > src, Span< int > indices, MutableSpan< T > dst)
Definition sculpt.cc:6395
OrigPositionData orig_position_data_get_grids(const Object &object, const bke::pbvh::GridsNode &node)
void flush_update_step(const bContext *C, const UpdateType update_type)
Definition sculpt.cc:5098
static void sculpt_transform_all_vertices(const Depsgraph &depsgraph, const Sculpt &sd, Object &ob)
static float3 average_unmasked_position(const Depsgraph &depsgraph, const Object &object, const float3 &pivot, const ePaintSymmetryFlags symm)
static BLI_NOINLINE void apply_kelvinet_to_translations(const KelvinletParams &params, const float3 &elastic_transform_pivot, const Span< float3 > positions, const MutableSpan< float3 > translations)
static void transform_radius_elastic(const Depsgraph &depsgraph, const Sculpt &sd, Object &ob, const float transform_radius)
static BLI_NOINLINE void calc_transform_translations(const float4x4 &elastic_transform_mat, const Span< float3 > positions, const MutableSpan< float3 > r_translations)
static AveragePositionAccumulation combine_average_position_accumulation(const AveragePositionAccumulation &a, const AveragePositionAccumulation &b)
static float3 average_mask_border_position(const Depsgraph &depsgraph, const Object &object, const float3 &pivot, const ePaintSymmetryFlags symm)
static bool set_pivot_depends_on_cursor(bContext &, wmOperatorType &, PointerRNA *ptr)
static EnumPropertyItem prop_sculpt_pivot_position_types[]
void SCULPT_OT_set_pivot_position(wmOperatorType *ot)
bool is_symmetry_iteration_valid(const char i, const char symm)
T safe_divide(const T &a, const T &b)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
Value parallel_reduce(IndexRange range, int64_t grain_size, const Value &identity, const Function &function, const Reduction &reduction)
Definition BLI_task.hh:151
MatBase< float, 4, 4 > float4x4
VecBase< float, 2 > float2
VecBase< double, 3 > double3
VecBase< float, 3 > float3
float paint_calc_object_space_radius(const ViewContext &vc, const blender::float3 &center, float pixel_radius)
float RNA_float_get(PointerRNA *ptr, const char *name)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
int RNA_enum_get(PointerRNA *ptr, const char *name)
const char * RNA_property_identifier(const PropertyRNA *prop)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
ePaintSymmetryFlags SCULPT_mesh_symmetry_xyz_get(const Object &object)
Definition sculpt.cc:184
float3 SCULPT_flip_v3_by_symm_area(const float3 &vector, const ePaintSymmetryFlags symm, const ePaintSymmetryAreas symmarea, const float3 &pivot)
Definition sculpt.cc:2868
bool SCULPT_mode_poll(bContext *C)
Definition sculpt.cc:3677
ePaintSymmetryAreas SCULPT_get_vertex_symm_area(const float co[3])
Definition sculpt.cc:2825
void SCULPT_flip_quat_by_symm_area(float quat[4], const ePaintSymmetryFlags symm, const ePaintSymmetryAreas symmarea, const float pivot[3])
Definition sculpt.cc:2889
#define FLT_MAX
Definition stdcycles.h:14
int grid_area
Definition BKE_ccg.hh:35
struct SculptSession * sculpt
blender::float4 prev_pivot_rot
Definition BKE_paint.hh:475
BMLog * bm_log
Definition BKE_paint.hh:392
blender::ed::sculpt_paint::filter::Cache * filter_cache
Definition BKE_paint.hh:418
blender::float3 prev_pivot_pos
Definition BKE_paint.hh:474
blender::float4 pivot_rot
Definition BKE_paint.hh:467
SubdivCCG * subdiv_ccg
Definition BKE_paint.hh:395
blender::float3 active_vert_position(const Depsgraph &depsgraph, const Object &object) const
Definition paint.cc:2403
blender::float3 pivot_pos
Definition BKE_paint.hh:466
blender::float3 pivot_scale
Definition BKE_paint.hh:468
blender::float3 init_pivot_pos
Definition BKE_paint.hh:470
blender::float4 init_pivot_rot
Definition BKE_paint.hh:471
blender::float3 prev_pivot_scale
Definition BKE_paint.hh:476
blender::float3 init_pivot_scale
Definition BKE_paint.hh:472
blender::Array< blender::float3 > positions
blender::float3 average_stroke_accum
TransformDisplacementMode transform_displacement_mode
int mval[2]
Definition WM_types.hh:763
struct PointerRNA * ptr
i
Definition text_draw.cc:230
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237