Blender V4.3
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
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_brush.hh"
21#include "BKE_context.hh"
22#include "BKE_kelvinlet.h"
23#include "BKE_layer.hh"
24#include "BKE_mesh.hh"
25#include "BKE_paint.hh"
26#include "BKE_pbvh_api.hh"
27#include "BKE_subdiv_ccg.hh"
28
29#include "WM_api.hh"
30#include "WM_types.hh"
31
32#include "ED_screen.hh"
33#include "ED_sculpt.hh"
34#include "ED_view3d.hh"
35
36#include "mesh_brush_common.hh"
37#include "paint_intern.hh"
38#include "paint_mask.hh"
39#include "sculpt_automask.hh"
40#include "sculpt_filter.hh"
41#include "sculpt_intern.hh"
42#include "sculpt_undo.hh"
43
44#include "RNA_access.hh"
45#include "RNA_define.hh"
46
47#include "bmesh.hh"
48
49#include <cmath>
50#include <cstdlib>
51
53
54void init_transform(bContext *C, Object &ob, const float mval_fl[2], const char *undo_name)
55{
56 const Scene &scene = *CTX_data_scene(C);
57 const Sculpt &sd = *CTX_data_tool_settings(C)->sculpt;
58 SculptSession &ss = *ob.sculpt;
60
64
68
70 undo::push_begin_ex(scene, ob, undo_name);
71
72 ss.pivot_rot[3] = 1.0f;
73
75
76 filter::cache_init(C, ob, sd, undo::Type::Position, mval_fl, 5.0, 1.0f);
77
80 }
81 else {
83 }
84}
85
86static std::array<float4x4, 8> transform_matrices_init(const SculptSession &ss,
87 const ePaintSymmetryFlags symm,
88 const TransformDisplacementMode t_mode)
89{
90 std::array<float4x4, 8> mats;
91
92 float3 final_pivot_pos, d_t, d_s;
93 float d_r[4];
94 float t_mat[4][4], r_mat[4][4], s_mat[4][4], pivot_mat[4][4], pivot_imat[4][4],
95 transform_mat[4][4];
96
97 float start_pivot_pos[3], start_pivot_rot[4], start_pivot_scale[3];
98 switch (t_mode) {
100 copy_v3_v3(start_pivot_pos, ss.init_pivot_pos);
101 copy_v4_v4(start_pivot_rot, ss.init_pivot_rot);
102 copy_v3_v3(start_pivot_scale, ss.init_pivot_scale);
103 break;
105 copy_v3_v3(start_pivot_pos, ss.prev_pivot_pos);
106 copy_v4_v4(start_pivot_rot, ss.prev_pivot_rot);
107 copy_v3_v3(start_pivot_scale, ss.prev_pivot_scale);
108 break;
109 }
110
111 for (int i = 0; i < PAINT_SYMM_AREAS; i++) {
113
114 copy_v3_v3(final_pivot_pos, ss.pivot_pos);
115
116 unit_m4(pivot_mat);
117
118 unit_m4(t_mat);
119 unit_m4(r_mat);
120 unit_m4(s_mat);
121
122 /* Translation matrix. */
123 sub_v3_v3v3(d_t, ss.pivot_pos, start_pivot_pos);
124 d_t = SCULPT_flip_v3_by_symm_area(d_t, symm, v_symm, ss.init_pivot_pos);
125 translate_m4(t_mat, d_t[0], d_t[1], d_t[2]);
126
127 /* Rotation matrix. */
128 sub_qt_qtqt(d_r, ss.pivot_rot, start_pivot_rot);
129 normalize_qt(d_r);
130 SCULPT_flip_quat_by_symm_area(d_r, symm, v_symm, ss.init_pivot_pos);
131 quat_to_mat4(r_mat, d_r);
132
133 /* Scale matrix. */
134 sub_v3_v3v3(d_s, ss.pivot_scale, start_pivot_scale);
135 add_v3_fl(d_s, 1.0f);
136 size_to_mat4(s_mat, d_s);
137
138 /* Pivot matrix. */
139 final_pivot_pos = SCULPT_flip_v3_by_symm_area(final_pivot_pos, symm, v_symm, start_pivot_pos);
140 translate_m4(pivot_mat, final_pivot_pos[0], final_pivot_pos[1], final_pivot_pos[2]);
141 invert_m4_m4(pivot_imat, pivot_mat);
142
143 /* Final transform matrix. */
144 mul_m4_m4m4(transform_mat, r_mat, t_mat);
145 mul_m4_m4m4(transform_mat, transform_mat, s_mat);
146 mul_m4_m4m4(mats[i].ptr(), transform_mat, pivot_imat);
147 mul_m4_m4m4(mats[i].ptr(), pivot_mat, mats[i].ptr());
148 }
149
150 return mats;
151}
152
153static constexpr float transform_mirror_max_distance_eps = 0.00002f;
154
160
162 const Span<float3> positions,
163 const std::array<float4x4, 8> &transform_mats,
164 const MutableSpan<float3> translations)
165{
166 for (const int i : positions.index_range()) {
167 const ePaintSymmetryAreas symm_area = SCULPT_get_vertex_symm_area(positions[i]);
168 const float3 transformed = math::transform_point(transform_mats[symm_area], positions[i]);
169 translations[i] = transformed - positions[i];
170 }
171}
172
174 const ePaintSymmetryFlags symm,
175 const MutableSpan<float3> translations)
176{
177 if ((symm & (PAINT_SYMM_X | PAINT_SYMM_Y | PAINT_SYMM_Z)) == 0) {
178 return;
179 }
180 for (const int i : positions.index_range()) {
181 if ((symm & PAINT_SYMM_X) && (std::abs(positions[i].x) < transform_mirror_max_distance_eps)) {
182 translations[i].x = 0.0f;
183 }
184 if ((symm & PAINT_SYMM_Y) && (std::abs(positions[i].y) < transform_mirror_max_distance_eps)) {
185 translations[i].y = 0.0f;
186 }
187 if ((symm & PAINT_SYMM_Z) && (std::abs(positions[i].z) < transform_mirror_max_distance_eps)) {
188 translations[i].z = 0.0f;
189 }
190 }
191}
192
193static void transform_node_mesh(const Sculpt &sd,
194 const std::array<float4x4, 8> &transform_mats,
195 const MeshAttributeData &attribute_data,
196 const bke::pbvh::MeshNode &node,
197 Object &object,
199 const PositionDeformData &position_data)
200{
201 SculptSession &ss = *object.sculpt;
202
203 const Span<int> verts = node.verts();
204 const OrigPositionData orig_data = orig_position_data_get_mesh(object, node);
205
206 tls.factors.resize(verts.size());
207 const MutableSpan<float> factors = tls.factors;
208 fill_factor_from_hide_and_mask(attribute_data.hide_vert, attribute_data.mask, verts, factors);
209
210 tls.translations.resize(verts.size());
211 const MutableSpan<float3> translations = tls.translations;
212 calc_symm_area_transform_translations(orig_data.positions, transform_mats, translations);
213 scale_translations(translations, factors);
214
216 filter_translations_with_symmetry(orig_data.positions, symm, translations);
217
218 clip_and_lock_translations(sd, ss, position_data.eval, verts, translations);
219 position_data.deform(translations, verts);
220}
221
222static void transform_node_grids(const Sculpt &sd,
223 const std::array<float4x4, 8> &transform_mats,
224 const bke::pbvh::GridsNode &node,
225 Object &object,
227{
228 SculptSession &ss = *object.sculpt;
229 SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
230 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
231
232 const Span<int> grids = node.grids();
233 const int grid_verts_num = grids.size() * key.grid_area;
234
235 const OrigPositionData orig_data = orig_position_data_get_grids(object, node);
236
237 tls.factors.resize(grid_verts_num);
238 const MutableSpan<float> factors = tls.factors;
239 fill_factor_from_hide_and_mask(subdiv_ccg, grids, factors);
240
241 tls.translations.resize(grid_verts_num);
242 const MutableSpan<float3> translations = tls.translations;
243 calc_symm_area_transform_translations(orig_data.positions, transform_mats, translations);
244
245 scale_translations(translations, factors);
246
248 filter_translations_with_symmetry(orig_data.positions, symm, translations);
249
250 clip_and_lock_translations(sd, ss, orig_data.positions, translations);
251 apply_translations(translations, grids, subdiv_ccg);
252}
253
254static void transform_node_bmesh(const Sculpt &sd,
255 const std::array<float4x4, 8> &transform_mats,
257 Object &object,
259{
260 SculptSession &ss = *object.sculpt;
261
263
264 Array<float3> orig_positions(verts.size());
265 Array<float3> orig_normals(verts.size());
266 orig_position_data_gather_bmesh(*ss.bm_log, verts, orig_positions, orig_normals);
267
268 tls.factors.resize(verts.size());
269 const MutableSpan<float> factors = tls.factors;
271
272 tls.translations.resize(verts.size());
273 const MutableSpan<float3> translations = tls.translations;
274 calc_symm_area_transform_translations(orig_positions, transform_mats, translations);
275
276 scale_translations(translations, factors);
277
279 filter_translations_with_symmetry(orig_positions, symm, translations);
280
281 clip_and_lock_translations(sd, ss, orig_positions, translations);
282 apply_translations(translations, verts);
283}
284
285static void sculpt_transform_all_vertices(const Depsgraph &depsgraph, const Sculpt &sd, Object &ob)
286{
288
289 SculptSession &ss = *ob.sculpt;
291
292 std::array<float4x4, 8> transform_mats = transform_matrices_init(
294
295 /* Regular transform applies all symmetry passes at once as it is split by symmetry areas
296 * (each vertex can only be transformed once by the transform matrix of its area). */
298 const IndexMask &node_mask = ss.filter_cache->node_mask;
299
301 switch (pbvh.type()) {
303 Mesh &mesh = *static_cast<Mesh *>(ob.data);
304 const MeshAttributeData attribute_data(mesh.attributes());
306 const PositionDeformData position_data(depsgraph, ob);
307 node_mask.foreach_index(GrainSize(1), [&](const int i) {
308 TransformLocalData &tls = all_tls.local();
309 transform_node_mesh(sd, transform_mats, attribute_data, nodes[i], ob, tls, position_data);
310 bke::pbvh::update_node_bounds_mesh(position_data.eval, nodes[i]);
311 });
312 break;
313 }
315 SubdivCCG &subdiv_ccg = *ob.sculpt->subdiv_ccg;
316 MutableSpan<float3> positions = subdiv_ccg.positions;
318 node_mask.foreach_index(GrainSize(1), [&](const int i) {
319 TransformLocalData &tls = all_tls.local();
320 transform_node_grids(sd, transform_mats, nodes[i], ob, tls);
321 bke::pbvh::update_node_bounds_grids(subdiv_ccg.grid_area, positions, nodes[i]);
322 });
323 break;
324 }
327 node_mask.foreach_index(GrainSize(1), [&](const int i) {
328 TransformLocalData &tls = all_tls.local();
329 transform_node_bmesh(sd, transform_mats, nodes[i], ob, tls);
331 });
332 break;
333 }
334 }
335 pbvh.tag_positions_changed(node_mask);
337}
338
339BLI_NOINLINE static void calc_transform_translations(const float4x4 &elastic_transform_mat,
340 const Span<float3> positions,
341 const MutableSpan<float3> r_translations)
342{
343 for (const int i : positions.index_range()) {
344 const float3 transformed = math::transform_point(elastic_transform_mat, positions[i]);
345 r_translations[i] = transformed - positions[i];
346 }
347}
348
350 const float3 &elastic_transform_pivot,
351 const Span<float3> positions,
352 const MutableSpan<float3> translations)
353{
354 for (const int i : positions.index_range()) {
356 translations[i], &params, positions[i], elastic_transform_pivot, translations[i]);
357 }
358}
359
361 const KelvinletParams &params,
362 const float4x4 &elastic_transform_mat,
363 const float3 &elastic_transform_pivot,
364 const MeshAttributeData &attribute_data,
365 const bke::pbvh::MeshNode &node,
366 Object &object,
368 const PositionDeformData &position_data)
369{
370 const SculptSession &ss = *object.sculpt;
371
372 const Span<int> verts = node.verts();
373 const MutableSpan positions = gather_data_mesh(position_data.eval, verts, tls.positions);
374
375 /* TODO: Using the factors array is unnecessary when there are no hidden vertices and no mask. */
376 tls.factors.resize(verts.size());
377 const MutableSpan<float> factors = tls.factors;
378 fill_factor_from_hide_and_mask(attribute_data.hide_vert, attribute_data.mask, verts, factors);
379 scale_factors(factors, 20.0f);
380
381 tls.translations.resize(verts.size());
382 const MutableSpan<float3> translations = tls.translations;
383 calc_transform_translations(elastic_transform_mat, positions, translations);
384 apply_kelvinet_to_translations(params, elastic_transform_pivot, positions, translations);
385
386 scale_translations(translations, factors);
387
388 clip_and_lock_translations(sd, ss, position_data.eval, verts, translations);
389 position_data.deform(translations, verts);
390}
391
393 const KelvinletParams &params,
394 const float4x4 &elastic_transform_mat,
395 const float3 &elastic_transform_pivot,
396 const bke::pbvh::GridsNode &node,
397 Object &object,
399{
400 SculptSession &ss = *object.sculpt;
401 SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
402
403 const Span<int> grids = node.grids();
404 const MutableSpan positions = gather_grids_positions(subdiv_ccg, grids, tls.positions);
405
406 /* TODO: Using the factors array is unnecessary when there are no hidden vertices and no mask. */
407 tls.factors.resize(positions.size());
408 const MutableSpan<float> factors = tls.factors;
409 fill_factor_from_hide_and_mask(subdiv_ccg, grids, factors);
410 scale_factors(factors, 20.0f);
411
412 tls.translations.resize(positions.size());
413 const MutableSpan<float3> translations = tls.translations;
414 calc_transform_translations(elastic_transform_mat, positions, translations);
415 apply_kelvinet_to_translations(params, elastic_transform_pivot, positions, translations);
416
417 scale_translations(translations, factors);
418
419 clip_and_lock_translations(sd, ss, positions, translations);
420 apply_translations(translations, grids, subdiv_ccg);
421}
422
424 const KelvinletParams &params,
425 const float4x4 &elastic_transform_mat,
426 const float3 &elastic_transform_pivot,
428 Object &object,
430{
431 SculptSession &ss = *object.sculpt;
432
434 const MutableSpan positions = gather_bmesh_positions(verts, tls.positions);
435
436 tls.factors.resize(verts.size());
437 const MutableSpan<float> factors = tls.factors;
439 scale_factors(factors, 20.0f);
440
441 tls.translations.resize(verts.size());
442 const MutableSpan<float3> translations = tls.translations;
443 calc_transform_translations(elastic_transform_mat, positions, translations);
444 apply_kelvinet_to_translations(params, elastic_transform_pivot, positions, translations);
445
446 scale_translations(translations, factors);
447
448 clip_and_lock_translations(sd, ss, positions, translations);
449 apply_translations(translations, verts);
450}
451
452static void transform_radius_elastic(const Depsgraph &depsgraph,
453 const Sculpt &sd,
454 Object &ob,
455 const float transform_radius)
456{
457 SculptSession &ss = *ob.sculpt;
460
462
463 std::array<float4x4, 8> transform_mats = transform_matrices_init(
465
467 const IndexMask &node_mask = ss.filter_cache->node_mask;
468
470 /* TODO(pablodp606): These parameters can be exposed if needed as transform strength and volume
471 * preservation like in the elastic deform brushes. Setting them to the same default as elastic
472 * deform triscale grab because they work well in most cases. */
473 const float force = 1.0f;
474 const float shear_modulus = 1.0f;
475 const float poisson_ratio = 0.4f;
476 BKE_kelvinlet_init_params(&params, transform_radius, force, shear_modulus, poisson_ratio);
477
479 for (ePaintSymmetryFlags symmpass = PAINT_SYMM_NONE; symmpass <= symm; symmpass++) {
480 if (!SCULPT_is_symmetry_iteration_valid(symmpass, symm)) {
481 continue;
482 }
483
484 const float3 elastic_transform_pivot = symmetry_flip(ss.pivot_pos, symmpass);
485
486 const int symm_area = SCULPT_get_vertex_symm_area(elastic_transform_pivot);
487 float4x4 elastic_transform_mat = transform_mats[symm_area];
488 switch (pbvh.type()) {
490 Mesh &mesh = *static_cast<Mesh *>(ob.data);
492 const PositionDeformData position_data(depsgraph, ob);
493 const MeshAttributeData attribute_data(mesh.attributes());
494 node_mask.foreach_index(GrainSize(1), [&](const int i) {
495 TransformLocalData &tls = all_tls.local();
497 params,
498 elastic_transform_mat,
499 elastic_transform_pivot,
500 attribute_data,
501 nodes[i],
502 ob,
503 tls,
504 position_data);
505 bke::pbvh::update_node_bounds_mesh(position_data.eval, nodes[i]);
506 });
507 break;
508 }
510 SubdivCCG &subdiv_ccg = *ob.sculpt->subdiv_ccg;
511 MutableSpan<float3> positions = subdiv_ccg.positions;
513 node_mask.foreach_index(GrainSize(1), [&](const int i) {
514 TransformLocalData &tls = all_tls.local();
516 sd, params, elastic_transform_mat, elastic_transform_pivot, nodes[i], ob, tls);
517 bke::pbvh::update_node_bounds_grids(subdiv_ccg.grid_area, positions, nodes[i]);
518 });
519 break;
520 }
523 node_mask.foreach_index(GrainSize(1), [&](const int i) {
524 TransformLocalData &tls = all_tls.local();
526 sd, params, elastic_transform_mat, elastic_transform_pivot, nodes[i], ob, tls);
528 });
529 break;
530 }
531 }
532 }
533 pbvh.tag_positions_changed(node_mask);
535}
536
538{
539 const Sculpt &sd = *CTX_data_tool_settings(C)->sculpt;
540 SculptSession &ss = *ob.sculpt;
542
545
546 switch (sd.transform_mode) {
549 break;
550 }
552 const Brush &brush = *BKE_paint_brush_for_read(&sd.paint);
553 Scene *scene = CTX_data_scene(C);
554 float transform_radius;
555
556 if (BKE_brush_use_locked_size(scene, &brush)) {
557 transform_radius = BKE_brush_unprojected_radius_get(scene, &brush);
558 }
559 else {
561
562 transform_radius = paint_calc_object_space_radius(
563 vc, ss.init_pivot_pos, BKE_brush_size_get(scene, &brush));
564 }
565
566 transform_radius_elastic(*depsgraph, sd, ob, transform_radius);
567 break;
568 }
569 }
570
574
576}
577
579{
580 SculptSession &ss = *ob.sculpt;
581 MEM_delete(ss.filter_cache);
582 ss.filter_cache = nullptr;
583 undo::push_end(ob);
585}
586
588 Origin = 0,
589 Unmasked = 1,
590 MaskBorder = 2,
591 ActiveVert = 3,
592 CursorSurface = 4,
593};
594
597 "ORIGIN",
598 0,
599 "Origin",
600 "Sets the pivot to the origin of the sculpt"},
602 "UNMASKED",
603 0,
604 "Unmasked",
605 "Sets the pivot position to the average position of the unmasked vertices"},
607 "BORDER",
608 0,
609 "Mask Border",
610 "Sets the pivot position to the center of the border of the mask"},
612 "ACTIVE",
613 0,
614 "Active Vertex",
615 "Sets the pivot position to the active vertex position"},
617 "SURFACE",
618 0,
619 "Surface",
620 "Sets the pivot position to the surface under the cursor"},
621 {0, nullptr, 0, nullptr, nullptr},
622};
623
625{
626 if (!ptr) {
627 return true;
628 }
631}
632
637
643
645 const Span<float> factors,
647{
648 BLI_assert(positions.size() == factors.size());
649
650 for (const int i : positions.index_range()) {
651 total.position += double3(positions[i] * factors[i]);
652 total.weight_total += factors[i];
653 }
654}
655
657 const Object &object,
658 const float3 &pivot,
659 const ePaintSymmetryFlags symm)
660{
661 const SculptSession &ss = *object.sculpt;
662 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
663
664 IndexMaskMemory memory;
665 const IndexMask node_mask = bke::pbvh::search_nodes(
666 pbvh, memory, [&](const bke::pbvh::Node &node) {
667 return !node_fully_masked_or_hidden(node);
668 });
669
670 struct LocalData {
673 };
674
676 switch (pbvh.type()) {
679 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
680 const MeshAttributeData attribute_data(mesh.attributes());
681 const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, object);
683 node_mask.index_range(),
684 1,
686 [&](const IndexRange range, AveragePositionAccumulation sum) {
687 LocalData &tls = all_tls.local();
688 threading::isolate_task([&]() {
689 node_mask.slice(range).foreach_index([&](const int i) {
690 const Span<int> verts = nodes[i].verts();
691
692 tls.positions.resize(verts.size());
693 const MutableSpan<float3> positions = tls.positions;
694 array_utils::gather(vert_positions, verts, positions);
695
696 tls.factors.resize(verts.size());
697 const MutableSpan<float> factors = tls.factors;
698 fill_factor_from_hide_and_mask(
699 attribute_data.hide_vert, attribute_data.mask, verts, factors);
700 filter_verts_outside_symmetry_area(positions, pivot, symm, factors);
701
702 accumulate_weighted_average_position(positions, factors, sum);
703 });
704 });
705 return sum;
706 },
708 return float3(math::safe_divide(total.position, total.weight_total));
709 }
712 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
714 node_mask.index_range(),
715 1,
717 [&](const IndexRange range, AveragePositionAccumulation sum) {
718 LocalData &tls = all_tls.local();
719 node_mask.slice(range).foreach_index([&](const int i) {
720 const Span<int> grids = nodes[i].grids();
721 const MutableSpan positions = gather_grids_positions(
722 subdiv_ccg, grids, tls.positions);
723
724 tls.factors.resize(positions.size());
725 const MutableSpan<float> factors = tls.factors;
726 fill_factor_from_hide_and_mask(subdiv_ccg, grids, factors);
727 filter_verts_outside_symmetry_area(positions, pivot, symm, factors);
728
729 accumulate_weighted_average_position(positions, factors, sum);
730 });
731 return sum;
732 },
734 return float3(math::safe_divide(total.position, total.weight_total));
735 }
737 const Span<bke::pbvh::BMeshNode> nodes = pbvh.nodes<bke::pbvh::BMeshNode>();
738 const AveragePositionAccumulation total = threading::parallel_reduce(
739 node_mask.index_range(),
740 1,
741 AveragePositionAccumulation{},
742 [&](const IndexRange range, AveragePositionAccumulation sum) {
743 LocalData &tls = all_tls.local();
744 node_mask.slice(range).foreach_index([&](const int i) {
745 const Set<BMVert *, 0> &verts = BKE_pbvh_bmesh_node_unique_verts(
746 &const_cast<bke::pbvh::BMeshNode &>(nodes[i]));
747 const MutableSpan positions = gather_bmesh_positions(verts, tls.positions);
748
749 tls.factors.resize(verts.size());
750 const MutableSpan<float> factors = tls.factors;
751 fill_factor_from_hide_and_mask(*ss.bm, verts, factors);
752 filter_verts_outside_symmetry_area(positions, pivot, symm, factors);
753
754 accumulate_weighted_average_position(positions, factors, sum);
755 });
756 return sum;
757 },
759 return float3(math::safe_divide(total.position, total.weight_total));
760 }
761 }
763 return float3(0);
764}
765
767 const MutableSpan<float> factors)
768{
769 constexpr float threshold = 0.2f;
770
771 for (const int i : masks.index_range()) {
772 if (std::abs(masks[i] - 0.5f) > threshold) {
773 factors[i] = 0.0f;
774 }
775 }
776};
777
779 const Object &object,
780 const float3 &pivot,
781 const ePaintSymmetryFlags symm)
782{
783 const SculptSession &ss = *object.sculpt;
784 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
785
786 IndexMaskMemory memory;
787 const IndexMask node_mask = bke::pbvh::search_nodes(
788 pbvh, memory, [&](const bke::pbvh::Node &node) {
789 return !node_fully_masked_or_hidden(node);
790 });
791
792 struct LocalData {
793 Vector<float> factors;
794 Vector<float> masks;
795 Vector<float3> positions;
796 };
797
799 switch (pbvh.type()) {
800 case bke::pbvh::Type::Mesh: {
802 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
803 const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, object);
804 const bke::AttributeAccessor attributes = mesh.attributes();
805 const VArraySpan mask_attr = *attributes.lookup_or_default<float>(
806 ".sculpt_mask", bke::AttrDomain::Point, 0.0f);
807 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
808 const AveragePositionAccumulation total = threading::parallel_reduce(
809 node_mask.index_range(),
810 1,
812 [&](const IndexRange range, AveragePositionAccumulation sum) {
813 LocalData &tls = all_tls.local();
814 node_mask.slice(range).foreach_index([&](const int i) {
815 const Span<int> verts = nodes[i].verts();
816 MutableSpan positions = gather_data_mesh(vert_positions, verts, tls.positions);
817 MutableSpan masks = gather_data_mesh(mask_attr, verts, tls.masks);
818
819 tls.factors.resize(verts.size());
820 const MutableSpan<float> factors = tls.factors;
821 fill_factor_from_hide(hide_vert, verts, factors);
822
823 mask_border_weight_calc(masks, factors);
824 filter_verts_outside_symmetry_area(positions, pivot, symm, factors);
825
826 accumulate_weighted_average_position(positions, factors, sum);
827 });
828 return sum;
829 },
831 return float3(math::safe_divide(total.position, total.weight_total));
832 }
833 case bke::pbvh::Type::Grids: {
835 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
836 const AveragePositionAccumulation total = threading::parallel_reduce(
837 node_mask.index_range(),
838 1,
840 [&](const IndexRange range, AveragePositionAccumulation sum) {
841 LocalData &tls = all_tls.local();
842 node_mask.slice(range).foreach_index([&](const int i) {
843 const Span<int> grids = nodes[i].grids();
844 const MutableSpan positions = gather_grids_positions(
845 subdiv_ccg, grids, tls.positions);
846
847 tls.masks.resize(positions.size());
848 const MutableSpan<float> masks = tls.masks;
849 mask::gather_mask_grids(subdiv_ccg, grids, masks);
850
851 tls.factors.resize(positions.size());
852 const MutableSpan<float> factors = tls.factors;
853 fill_factor_from_hide(subdiv_ccg, grids, factors);
854 mask_border_weight_calc(masks, factors);
855 filter_verts_outside_symmetry_area(positions, pivot, symm, factors);
856
857 accumulate_weighted_average_position(positions, factors, sum);
858 });
859 return sum;
860 },
862 return float3(math::safe_divide(total.position, total.weight_total));
863 }
864 case bke::pbvh::Type::BMesh: {
865 const Span<bke::pbvh::BMeshNode> nodes = pbvh.nodes<bke::pbvh::BMeshNode>();
866 const AveragePositionAccumulation total = threading::parallel_reduce(
867 node_mask.index_range(),
868 1,
869 AveragePositionAccumulation{},
870 [&](const IndexRange range, AveragePositionAccumulation sum) {
871 LocalData &tls = all_tls.local();
872 node_mask.slice(range).foreach_index([&](const int i) {
873 const Set<BMVert *, 0> &verts = BKE_pbvh_bmesh_node_unique_verts(
874 &const_cast<bke::pbvh::BMeshNode &>(nodes[i]));
875 const MutableSpan positions = gather_bmesh_positions(verts, tls.positions);
876
877 tls.masks.resize(verts.size());
878 const MutableSpan<float> masks = tls.masks;
879 mask::gather_mask_bmesh(*ss.bm, verts, masks);
880
881 tls.factors.resize(verts.size());
882 const MutableSpan<float> factors = tls.factors;
883 fill_factor_from_hide(verts, factors);
884 mask_border_weight_calc(masks, factors);
885 filter_verts_outside_symmetry_area(positions, pivot, symm, factors);
886
887 accumulate_weighted_average_position(positions, factors, sum);
888 });
889 return sum;
890 },
891 combine_average_position_accumulation);
892 return float3(math::safe_divide(total.position, total.weight_total));
893 }
894 }
896 return float3(0);
897}
898
900{
902 SculptSession &ss = *ob.sculpt;
903 ARegion *region = CTX_wm_region(C);
906
907 const PivotPositionMode mode = PivotPositionMode(RNA_enum_get(op->ptr, "mode"));
908
909 const View3D *v3d = CTX_wm_view3d(C);
910 const Base *base = CTX_data_active_base(C);
911 if (!BKE_base_is_visible(v3d, base)) {
912 return OPERATOR_CANCELLED;
913 }
914
916
917 /* Pivot to center. */
918 if (mode == PivotPositionMode::Origin) {
919 zero_v3(ss.pivot_pos);
920 }
921 /* Pivot to active vertex. */
922 else if (mode == PivotPositionMode::ActiveVert) {
924 }
925 /* Pivot to ray-cast surface. */
926 else if (mode == PivotPositionMode::CursorSurface) {
927 float stroke_location[3];
928 const float mval[2] = {
929 RNA_float_get(op->ptr, "mouse_x"),
930 RNA_float_get(op->ptr, "mouse_y"),
931 };
932 if (SCULPT_stroke_get_location(C, stroke_location, mval, false)) {
933 copy_v3_v3(ss.pivot_pos, stroke_location);
934 }
935 }
936 else if (mode == PivotPositionMode::Unmasked) {
938 }
939 else {
941 }
942
943 /* Update the viewport navigation rotation origin. */
946 ups->average_stroke_counter = 1;
947 ups->last_stroke_valid = true;
948
949 ED_region_tag_redraw(region);
951
952 return OPERATOR_FINISHED;
953}
954
955static int set_pivot_position_invoke(bContext *C, wmOperator *op, const wmEvent *event)
956{
957 RNA_float_set(op->ptr, "mouse_x", event->mval[0]);
958 RNA_float_set(op->ptr, "mouse_y", event->mval[1]);
959 return set_pivot_position_exec(C, op);
960}
961
963 wmOperator *op,
964 const PropertyRNA *prop)
965{
966 if (STRPREFIX(RNA_property_identifier(prop), "mouse_")) {
967 const PivotPositionMode mode = PivotPositionMode(RNA_enum_get(op->ptr, "mode"));
968 return mode == PivotPositionMode::CursorSurface;
969 }
970 return true;
971}
972
974{
975 ot->name = "Set Pivot Position";
976 ot->idname = "SCULPT_OT_set_pivot_position";
977 ot->description = "Sets the sculpt transform pivot position";
978
984
987 "mode",
989 int(PivotPositionMode::Unmasked),
990 "Mode",
991 "");
992
994 "mouse_x",
995 0.0f,
996 0.0f,
997 FLT_MAX,
998 "Mouse Position X",
999 "Position of the mouse used for \"Surface\" mode",
1000 0.0f,
1001 10000.0f);
1003 "mouse_y",
1004 0.0f,
1005 0.0f,
1006 FLT_MAX,
1007 "Mouse Position Y",
1008 "Position of the mouse used for \"Surface\" mode",
1009 0.0f,
1010 10000.0f);
1011}
1012
1013} // namespace blender::ed::sculpt_paint
float BKE_brush_unprojected_radius_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1133
int BKE_brush_size_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1075
bool BKE_brush_use_locked_size(const Scene *scene, const Brush *brush)
Definition brush.cc:1083
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:151
std::variant< std::monostate, int, SubdivCCGCoord, BMVert * > ActiveVert
Definition BKE_paint.hh:377
ePaintSymmetryAreas
Definition BKE_paint.hh:144
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:654
void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph, Object *ob_orig, bool is_paint_tool)
Definition paint.cc:2601
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:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_NOINLINE
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void unit_m4(float m[4][4])
Definition rct.c:1127
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 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])
MINLINE void zero_v3(float r[3])
#define STRPREFIX(a, b)
ePaintSymmetryFlags
@ PAINT_SYMM_Y
@ PAINT_SYMM_X
@ PAINT_SYMM_Z
@ PAINT_SYMM_NONE
@ SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC
@ SCULPT_TRANSFORM_MODE_ALL_VERTICES
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
Read Guarded memory(de)allocation.
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define ND_SELECT
Definition WM_types.hh:474
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
static T sum(const btAlignedObjectArray< T > &items)
constexpr int64_t size() const
Definition BLI_span.hh:253
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
local_group_size(16, 16) .push_constant(Type b
const Depsgraph * depsgraph
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
static float verts[][3]
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:2846
IndexMask search_nodes(const Tree &pbvh, IndexMaskMemory &memory, FunctionRef< bool(const Node &)> filter_fn)
Definition pbvh.cc:2647
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
void update_node_bounds_grids(int grid_area, Span< float3 > positions, GridsNode &node)
Definition pbvh.cc:1084
Span< float3 > vert_positions_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:2482
void flush_bounds_to_parents(Tree &pbvh)
Definition pbvh.cc:1132
static BLI_NOINLINE void fill_factor_from_hide_and_mask(const Mesh &mesh, const Span< int > face_indices, const MutableSpan< float > r_factors)
void cache_init(bContext *C, Object &ob, const 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:1014
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)
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)
void gather_bmesh_positions(const Set< BMVert *, 0 > &verts, MutableSpan< float3 > positions)
Definition sculpt.cc:6054
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)
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:2315
static int set_pivot_position_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void transform_node_grids(const Sculpt &sd, const std::array< float4x4, 8 > &transform_mats, const bke::pbvh::GridsNode &node, Object &object, TransformLocalData &tls)
void flush_update_done(const bContext *C, Object &ob, UpdateType update_type)
Definition sculpt.cc:5055
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:7210
void end_transform(bContext *C, Object &ob)
void flush_update_step(bContext *C, UpdateType update_type)
Definition sculpt.cc:4960
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)
void scale_factors(MutableSpan< float > factors, float strength)
Definition sculpt.cc:7227
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:7022
static BLI_NOINLINE void accumulate_weighted_average_position(const Span< float3 > positions, const Span< float > factors, AveragePositionAccumulation &total)
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:6958
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:6082
OrigPositionData orig_position_data_get_grids(const Object &object, const bke::pbvh::GridsNode &node)
static void sculpt_transform_all_vertices(const Depsgraph &depsgraph, const Sculpt &sd, Object &ob)
static int set_pivot_position_exec(bContext *C, wmOperator *op)
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)
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:153
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:186
bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2], bool force_original)
Definition sculpt.cc:4732
bool SCULPT_is_symmetry_iteration_valid(char i, char symm)
Definition sculpt.cc:713
void SCULPT_vertex_random_access_ensure(Object &object)
Definition sculpt.cc:144
float3 SCULPT_flip_v3_by_symm_area(const float3 &vector, const ePaintSymmetryFlags symm, const ePaintSymmetryAreas symmarea, const float3 &pivot)
Definition sculpt.cc:2714
bool SCULPT_mode_poll(bContext *C)
Definition sculpt.cc:3560
ePaintSymmetryAreas SCULPT_get_vertex_symm_area(const float co[3])
Definition sculpt.cc:2671
void SCULPT_flip_quat_by_symm_area(float quat[4], const ePaintSymmetryFlags symm, const ePaintSymmetryAreas symmarea, const float pivot[3])
Definition sculpt.cc:2735
#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:471
BMLog * bm_log
Definition BKE_paint.hh:402
blender::ed::sculpt_paint::filter::Cache * filter_cache
Definition BKE_paint.hh:428
blender::float3 prev_pivot_pos
Definition BKE_paint.hh:470
blender::float4 pivot_rot
Definition BKE_paint.hh:463
SubdivCCG * subdiv_ccg
Definition BKE_paint.hh:405
blender::float3 active_vert_position(const Depsgraph &depsgraph, const Object &object) const
Definition paint.cc:2224
blender::float3 pivot_pos
Definition BKE_paint.hh:462
blender::float3 pivot_scale
Definition BKE_paint.hh:464
blender::float3 init_pivot_pos
Definition BKE_paint.hh:466
blender::float4 init_pivot_rot
Definition BKE_paint.hh:467
blender::float3 prev_pivot_scale
Definition BKE_paint.hh:472
blender::float3 init_pivot_scale
Definition BKE_paint.hh:468
blender::Array< blender::float3 > positions
struct UnifiedPaintSettings unified_paint_settings
TransformDisplacementMode transform_displacement_mode
int mval[2]
Definition WM_types.hh:728
const char * name
Definition WM_types.hh:990
bool(* poll_property)(const bContext *C, wmOperator *op, const PropertyRNA *prop) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1048
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
bool(* depends_on_cursor)(bContext &C, wmOperatorType &ot, PointerRNA *ptr)
Definition WM_types.hh:1077
const char * idname
Definition WM_types.hh:992
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
struct PointerRNA * ptr
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4126
wmOperatorType * ot
Definition wm_files.cc:4125