Blender V4.5
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_subdiv_ccg.hh"
29
30#include "WM_api.hh"
31#include "WM_types.hh"
32
33#include "ED_screen.hh"
34#include "ED_sculpt.hh"
35#include "ED_view3d.hh"
36
37#include "mesh_brush_common.hh"
38#include "paint_intern.hh"
39#include "paint_mask.hh"
40#include "sculpt_automask.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 Scene *scene = CTX_data_scene(C);
555 float transform_radius;
556
557 if (BKE_brush_use_locked_size(scene, &brush)) {
558 transform_radius = BKE_brush_unprojected_radius_get(scene, &brush);
559 }
560 else {
562
563 transform_radius = paint_calc_object_space_radius(
564 vc, ss.init_pivot_pos, BKE_brush_size_get(scene, &brush));
565 }
566
567 transform_radius_elastic(*depsgraph, sd, ob, transform_radius);
568 break;
569 }
570 }
571
575
577}
578
580{
581 /* Canceling "Elastic" transforms (due to its #TransformDisplacementMode::Incremental nature),
582 * requires restoring positions from undo. For "All Vertices" there is no benefit in using the
583 * transform system to update to original positions either. */
586
589 pbvh.update_bounds(depsgraph, ob);
590}
591
593{
594 SculptSession &ss = *ob.sculpt;
595 MEM_delete(ss.filter_cache);
596 ss.filter_cache = nullptr;
597 undo::push_end(ob);
599}
600
608
611 "ORIGIN",
612 0,
613 "Origin",
614 "Sets the pivot to the origin of the sculpt"},
616 "UNMASKED",
617 0,
618 "Unmasked",
619 "Sets the pivot position to the average position of the unmasked vertices"},
621 "BORDER",
622 0,
623 "Mask Border",
624 "Sets the pivot position to the center of the border of the mask"},
626 "ACTIVE",
627 0,
628 "Active Vertex",
629 "Sets the pivot position to the active vertex position"},
631 "SURFACE",
632 0,
633 "Surface",
634 "Sets the pivot position to the surface under the cursor"},
635 {0, nullptr, 0, nullptr, nullptr},
636};
637
639{
640 if (!ptr) {
641 return true;
642 }
645}
646
651
657
659 const Span<float> factors,
661{
662 BLI_assert(positions.size() == factors.size());
663
664 for (const int i : positions.index_range()) {
665 total.position += double3(positions[i] * factors[i]);
666 total.weight_total += factors[i];
667 }
668}
669
671 const Object &object,
672 const float3 &pivot,
673 const ePaintSymmetryFlags symm)
674{
675 const SculptSession &ss = *object.sculpt;
676 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
677
678 IndexMaskMemory memory;
679 const IndexMask node_mask = bke::pbvh::search_nodes(
680 pbvh, memory, [&](const bke::pbvh::Node &node) {
681 return !node_fully_masked_or_hidden(node);
682 });
683
684 struct LocalData {
685 Vector<float> factors;
686 Vector<float3> positions;
687 };
688
690 switch (pbvh.type()) {
693 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
694 const MeshAttributeData attribute_data(mesh);
695 const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, object);
697 node_mask.index_range(),
698 1,
700 [&](const IndexRange range, AveragePositionAccumulation sum) {
701 LocalData &tls = all_tls.local();
702 threading::isolate_task([&]() {
703 node_mask.slice(range).foreach_index([&](const int i) {
704 const Span<int> verts = nodes[i].verts();
705
706 tls.positions.resize(verts.size());
707 const MutableSpan<float3> positions = tls.positions;
708 array_utils::gather(vert_positions, verts, positions);
709
710 tls.factors.resize(verts.size());
711 const MutableSpan<float> factors = tls.factors;
712 fill_factor_from_hide_and_mask(
713 attribute_data.hide_vert, attribute_data.mask, verts, factors);
714 filter_verts_outside_symmetry_area(positions, pivot, symm, factors);
715
716 accumulate_weighted_average_position(positions, factors, sum);
717 });
718 });
719 return sum;
720 },
722 return float3(math::safe_divide(total.position, total.weight_total));
723 }
726 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
728 node_mask.index_range(),
729 1,
731 [&](const IndexRange range, AveragePositionAccumulation sum) {
732 LocalData &tls = all_tls.local();
733 node_mask.slice(range).foreach_index([&](const int i) {
734 const Span<int> grids = nodes[i].grids();
735 const MutableSpan positions = gather_grids_positions(
736 subdiv_ccg, grids, tls.positions);
737
738 tls.factors.resize(positions.size());
739 const MutableSpan<float> factors = tls.factors;
740 fill_factor_from_hide_and_mask(subdiv_ccg, grids, factors);
741 filter_verts_outside_symmetry_area(positions, pivot, symm, factors);
742
743 accumulate_weighted_average_position(positions, factors, sum);
744 });
745 return sum;
746 },
748 return float3(math::safe_divide(total.position, total.weight_total));
749 }
752 const AveragePositionAccumulation total = threading::parallel_reduce(
753 node_mask.index_range(),
754 1,
755 AveragePositionAccumulation{},
756 [&](const IndexRange range, AveragePositionAccumulation sum) {
757 LocalData &tls = all_tls.local();
758 node_mask.slice(range).foreach_index([&](const int i) {
759 const Set<BMVert *, 0> &verts = BKE_pbvh_bmesh_node_unique_verts(
760 &const_cast<bke::pbvh::BMeshNode &>(nodes[i]));
761 const MutableSpan positions = gather_bmesh_positions(verts, tls.positions);
762
763 tls.factors.resize(verts.size());
764 const MutableSpan<float> factors = tls.factors;
765 fill_factor_from_hide_and_mask(*ss.bm, verts, factors);
766 filter_verts_outside_symmetry_area(positions, pivot, symm, factors);
767
768 accumulate_weighted_average_position(positions, factors, sum);
769 });
770 return sum;
771 },
773 return float3(math::safe_divide(total.position, total.weight_total));
774 }
775 }
777 return float3(0);
778}
779
781 const MutableSpan<float> factors)
782{
783 constexpr float threshold = 0.2f;
784
785 for (const int i : masks.index_range()) {
786 if (std::abs(masks[i] - 0.5f) > threshold) {
787 factors[i] = 0.0f;
788 }
789 }
790};
791
793 const Object &object,
794 const float3 &pivot,
795 const ePaintSymmetryFlags symm)
796{
797 const SculptSession &ss = *object.sculpt;
798 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
799
800 IndexMaskMemory memory;
801 const IndexMask node_mask = bke::pbvh::search_nodes(
802 pbvh, memory, [&](const bke::pbvh::Node &node) {
803 return !node_fully_masked_or_hidden(node);
804 });
805
806 struct LocalData {
807 Vector<float> factors;
808 Vector<float> masks;
809 Vector<float3> positions;
810 };
811
813 switch (pbvh.type()) {
816 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
817 const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, object);
818 const bke::AttributeAccessor attributes = mesh.attributes();
819 const VArraySpan mask_attr = *attributes.lookup_or_default<float>(
820 ".sculpt_mask", bke::AttrDomain::Point, 0.0f);
821 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
823 node_mask.index_range(),
824 1,
826 [&](const IndexRange range, AveragePositionAccumulation sum) {
827 LocalData &tls = all_tls.local();
828 node_mask.slice(range).foreach_index([&](const int i) {
829 const Span<int> verts = nodes[i].verts();
830 MutableSpan positions = gather_data_mesh(vert_positions, verts, tls.positions);
831 MutableSpan masks = gather_data_mesh(mask_attr, verts, tls.masks);
832
833 tls.factors.resize(verts.size());
834 const MutableSpan<float> factors = tls.factors;
835 fill_factor_from_hide(hide_vert, verts, factors);
836
837 mask_border_weight_calc(masks, factors);
838 filter_verts_outside_symmetry_area(positions, pivot, symm, factors);
839
840 accumulate_weighted_average_position(positions, factors, sum);
841 });
842 return sum;
843 },
845 return float3(math::safe_divide(total.position, total.weight_total));
846 }
849 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
851 node_mask.index_range(),
852 1,
854 [&](const IndexRange range, AveragePositionAccumulation sum) {
855 LocalData &tls = all_tls.local();
856 node_mask.slice(range).foreach_index([&](const int i) {
857 const Span<int> grids = nodes[i].grids();
858 const MutableSpan positions = gather_grids_positions(
859 subdiv_ccg, grids, tls.positions);
860
861 tls.masks.resize(positions.size());
862 const MutableSpan<float> masks = tls.masks;
863 mask::gather_mask_grids(subdiv_ccg, grids, masks);
864
865 tls.factors.resize(positions.size());
866 const MutableSpan<float> factors = tls.factors;
867 fill_factor_from_hide(subdiv_ccg, grids, factors);
868 mask_border_weight_calc(masks, factors);
869 filter_verts_outside_symmetry_area(positions, pivot, symm, factors);
870
871 accumulate_weighted_average_position(positions, factors, sum);
872 });
873 return sum;
874 },
876 return float3(math::safe_divide(total.position, total.weight_total));
877 }
880 const AveragePositionAccumulation total = threading::parallel_reduce(
881 node_mask.index_range(),
882 1,
883 AveragePositionAccumulation{},
884 [&](const IndexRange range, AveragePositionAccumulation sum) {
885 LocalData &tls = all_tls.local();
886 node_mask.slice(range).foreach_index([&](const int i) {
887 const Set<BMVert *, 0> &verts = BKE_pbvh_bmesh_node_unique_verts(
888 &const_cast<bke::pbvh::BMeshNode &>(nodes[i]));
889 const MutableSpan positions = gather_bmesh_positions(verts, tls.positions);
890
891 tls.masks.resize(verts.size());
892 const MutableSpan<float> masks = tls.masks;
893 mask::gather_mask_bmesh(*ss.bm, verts, masks);
894
895 tls.factors.resize(verts.size());
896 const MutableSpan<float> factors = tls.factors;
897 fill_factor_from_hide(verts, factors);
898 mask_border_weight_calc(masks, factors);
899 filter_verts_outside_symmetry_area(positions, pivot, symm, factors);
900
901 accumulate_weighted_average_position(positions, factors, sum);
902 });
903 return sum;
904 },
905 combine_average_position_accumulation);
906 return float3(math::safe_divide(total.position, total.weight_total));
907 }
908 }
910 return float3(0);
911}
912
914{
916 SculptSession &ss = *ob.sculpt;
917 ARegion *region = CTX_wm_region(C);
920
921 const PivotPositionMode mode = PivotPositionMode(RNA_enum_get(op->ptr, "mode"));
922
923 const View3D *v3d = CTX_wm_view3d(C);
924 const Base *base = CTX_data_active_base(C);
925 if (!BKE_base_is_visible(v3d, base)) {
926 return OPERATOR_CANCELLED;
927 }
928
930
931 /* Pivot to center. */
932 if (mode == PivotPositionMode::Origin) {
933 zero_v3(ss.pivot_pos);
934 }
935 /* Pivot to active vertex. */
936 else if (mode == PivotPositionMode::ActiveVert) {
937 const float2 mval(RNA_float_get(op->ptr, "mouse_x"), RNA_float_get(op->ptr, "mouse_y"));
939 if (cursor_geometry_info_update(C, &cgi, mval, false)) {
941 }
942 }
943 /* Pivot to ray-cast surface. */
944 else if (mode == PivotPositionMode::CursorSurface) {
945 float stroke_location[3];
946 const float mval[2] = {
947 RNA_float_get(op->ptr, "mouse_x"),
948 RNA_float_get(op->ptr, "mouse_y"),
949 };
950 if (stroke_get_location_bvh(C, stroke_location, mval, false)) {
951 copy_v3_v3(ss.pivot_pos, stroke_location);
952 }
953 }
954 else if (mode == PivotPositionMode::Unmasked) {
956 }
957 else {
959 }
960
961 /* Update the viewport navigation rotation origin. */
964 ups->average_stroke_counter = 1;
965 ups->last_stroke_valid = true;
966
967 ED_region_tag_redraw(region);
969
970 return OPERATOR_FINISHED;
971}
972
974 wmOperator *op,
975 const wmEvent *event)
976{
977 RNA_float_set(op->ptr, "mouse_x", event->mval[0]);
978 RNA_float_set(op->ptr, "mouse_y", event->mval[1]);
979 return set_pivot_position_exec(C, op);
980}
981
983 wmOperator *op,
984 const PropertyRNA *prop)
985{
986 if (STRPREFIX(RNA_property_identifier(prop), "mouse_")) {
987 const PivotPositionMode mode = PivotPositionMode(RNA_enum_get(op->ptr, "mode"));
989 }
990 return true;
991}
992
994{
995 ot->name = "Set Pivot Position";
996 ot->idname = "SCULPT_OT_set_pivot_position";
997 ot->description = "Sets the sculpt transform pivot position";
998
1001 ot->poll = SCULPT_mode_poll;
1002 ot->depends_on_cursor = set_pivot_depends_on_cursor;
1003 ot->poll_property = set_pivot_position_poll_property;
1004
1005 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1006 RNA_def_enum(ot->srna,
1007 "mode",
1010 "Mode",
1011 "");
1012
1013 RNA_def_float(ot->srna,
1014 "mouse_x",
1015 0.0f,
1016 0.0f,
1017 FLT_MAX,
1018 "Mouse Position X",
1019 "Position of the mouse used for \"Surface\" and \"Active Vertex\" mode",
1020 0.0f,
1021 10000.0f);
1022 RNA_def_float(ot->srna,
1023 "mouse_y",
1024 0.0f,
1025 0.0f,
1026 FLT_MAX,
1027 "Mouse Position Y",
1028 "Position of the mouse used for \"Surface\" and \"Active Vertex\" mode",
1029 0.0f,
1030 10000.0f);
1031}
1032
1033} // namespace blender::ed::sculpt_paint
float BKE_brush_unprojected_radius_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1249
int BKE_brush_size_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1210
bool BKE_brush_use_locked_size(const Scene *scene, const Brush *brush)
Definition brush.cc:1218
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:143
std::variant< std::monostate, int, BMVert * > ActiveVert
Definition BKE_paint.hh:380
ePaintSymmetryAreas
Definition BKE_paint.hh:136
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:641
void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph, Object *ob_orig, bool is_paint_tool)
Definition paint.cc:2657
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])
MINLINE void zero_v3(float r[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:639
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:390
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_SELECT
Definition WM_types.hh:505
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(const StringRef attribute_id) const
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
void tag_positions_changed(const IndexMask &node_mask)
Definition pbvh.cc:559
Span< NodeT > nodes() const
void update_bounds(const Depsgraph &depsgraph, const Object &object)
Definition pbvh.cc:1202
void flush_bounds_to_parents()
Definition pbvh.cc:1122
void deform(MutableSpan< float3 > translations, Span< int > verts) const
Definition sculpt.cc:7443
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:2912
IndexMask search_nodes(const Tree &pbvh, IndexMaskMemory &memory, FunctionRef< bool(const Node &)> filter_fn)
Definition pbvh.cc:2579
void update_normals(const Depsgraph &depsgraph, Object &object_orig, Tree &pbvh)
Definition pbvh.cc:1073
void update_node_bounds_bmesh(BMeshNode &node)
Definition pbvh.cc:1110
void update_node_bounds_mesh(Span< float3 > positions, MeshNode &node)
Definition pbvh.cc:1090
void update_node_bounds_grids(int grid_area, Span< float3 > positions, GridsNode &node)
Definition pbvh.cc:1099
Span< float3 > vert_positions_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:2416
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:1032
bool stroke_get_location_bvh(bContext *C, float out[3], const float mval[2], const bool force_original)
Definition sculpt.cc:4919
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:6807
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:6351
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:142
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:2399
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:7476
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:7493
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:7316
bool cursor_geometry_info_update(bContext *C, CursorGeometryInfo *out, const float2 &mval, const bool use_sampled_normal)
Definition sculpt.cc:4662
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:5129
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:7249
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:6379
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:5081
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:185
float3 SCULPT_flip_v3_by_symm_area(const float3 &vector, const ePaintSymmetryFlags symm, const ePaintSymmetryAreas symmarea, const float3 &pivot)
Definition sculpt.cc:2849
bool SCULPT_mode_poll(bContext *C)
Definition sculpt.cc:3660
ePaintSymmetryAreas SCULPT_get_vertex_symm_area(const float co[3])
Definition sculpt.cc:2806
void SCULPT_flip_quat_by_symm_area(float quat[4], const ePaintSymmetryFlags symm, const ePaintSymmetryAreas symmarea, const float pivot[3])
Definition sculpt.cc:2870
#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:495
BMLog * bm_log
Definition BKE_paint.hh:412
blender::ed::sculpt_paint::filter::Cache * filter_cache
Definition BKE_paint.hh:438
blender::float3 prev_pivot_pos
Definition BKE_paint.hh:494
blender::float4 pivot_rot
Definition BKE_paint.hh:487
SubdivCCG * subdiv_ccg
Definition BKE_paint.hh:415
blender::float3 active_vert_position(const Depsgraph &depsgraph, const Object &object) const
Definition paint.cc:2263
blender::float3 pivot_pos
Definition BKE_paint.hh:486
blender::float3 pivot_scale
Definition BKE_paint.hh:488
blender::float3 init_pivot_pos
Definition BKE_paint.hh:490
blender::float4 init_pivot_rot
Definition BKE_paint.hh:491
blender::float3 prev_pivot_scale
Definition BKE_paint.hh:496
blender::float3 init_pivot_scale
Definition BKE_paint.hh:492
blender::Array< blender::float3 > positions
struct UnifiedPaintSettings unified_paint_settings
TransformDisplacementMode transform_displacement_mode
int mval[2]
Definition WM_types.hh:760
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:4226
wmOperatorType * ot
Definition wm_files.cc:4225