Blender V4.3
sculpt_expand.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8#include "sculpt_expand.hh"
9
10#include <cmath>
11#include <cstdlib>
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_array_utils.hh"
16#include "BLI_bit_vector.hh"
17#include "BLI_linklist_stack.h"
18#include "BLI_math_vector.hh"
19#include "BLI_task.h"
20
21#include "DNA_brush_types.h"
22#include "DNA_object_types.h"
23
24#include "BKE_attribute.hh"
25#include "BKE_brush.hh"
26#include "BKE_ccg.hh"
27#include "BKE_colortools.hh"
28#include "BKE_context.hh"
29#include "BKE_image.hh"
30#include "BKE_layer.hh"
31#include "BKE_mesh.hh"
32#include "BKE_mesh_mapping.hh"
33#include "BKE_paint.hh"
34#include "BKE_pbvh_api.hh"
35#include "BKE_report.hh"
36#include "BKE_subdiv_ccg.hh"
37
38#include "WM_api.hh"
39#include "WM_types.hh"
40
41#include "RNA_access.hh"
42#include "RNA_define.hh"
43
44#include "ED_screen.hh"
45#include "ED_sculpt.hh"
46
47#include "paint_intern.hh"
48#include "paint_mask.hh"
49#include "sculpt_boundary.hh"
50#include "sculpt_color.hh"
51#include "sculpt_face_set.hh"
52#include "sculpt_flood_fill.hh"
53#include "sculpt_geodesic.hh"
54#include "sculpt_intern.hh"
55#include "sculpt_islands.hh"
56#include "sculpt_smooth.hh"
57#include "sculpt_undo.hh"
58
60#include "IMB_imbuf.hh"
61
62#include "bmesh.hh"
63
65
66/* Sculpt Expand. */
67/* Operator for creating selections and patterns in Sculpt Mode. Expand can create masks, face sets
68 * and fill vertex colors. */
69/* The main functionality of the operator
70 * - The operator initializes a value per vertex, called "falloff". There are multiple algorithms
71 * to generate these falloff values which will create different patterns in the result when using
72 * the operator. These falloff values require algorithms that rely on mesh connectivity, so they
73 * are only valid on parts of the mesh that are in the same connected component as the given
74 * initial vertices. If needed, these falloff values are propagated from vertex or grids into the
75 * base mesh faces.
76 *
77 * - On each modal callback, the operator gets the active vertex and face and gets its falloff
78 * value from its precalculated falloff. This is now the active falloff value.
79 * - Using the active falloff value and the settings of the expand operation (which can be modified
80 * during execution using the modal key-map), the operator loops over all elements in the mesh to
81 * check if they are enabled of not.
82 * - Based on each element state after evaluating the settings, the desired mesh data (mask, face
83 * sets, colors...) is updated.
84 */
85
89#define SCULPT_EXPAND_VERTEX_NONE -1
90
92#define EXPAND_ACTIVE_COMPONENT_NONE -1
97#define SCULPT_EXPAND_TEXTURE_DISTORTION_STEP 0.01f
98
104#define SCULPT_EXPAND_LOOP_THRESHOLD 0.00001f
105
111#define SCULPT_EXPAND_NORMALS_FALLOFF_EDGE_SENSITIVITY 300
112
113/* Expand Modal Key-map. */
114enum {
134};
135
136/* Functions for getting the state of mesh elements (vertices and base mesh faces). When the main
137 * functions for getting the state of an element return true it means that data associated to that
138 * element will be modified by expand. */
139
145 const Cache &expand_cache,
146 const int vert)
147{
148 for (int i = 0; i < EXPAND_SYMM_AREAS; i++) {
149 if (islands::vert_id_get(ss, vert) == expand_cache.active_connected_islands[i]) {
150 return true;
151 }
152 }
153 return false;
154}
155
159static bool is_face_in_active_component(const Object &object,
160 const OffsetIndices<int> faces,
161 const Span<int> corner_verts,
162 const Cache &expand_cache,
163 const int f)
164{
165 const SculptSession &ss = *object.sculpt;
166 switch (bke::object::pbvh_get(object)->type()) {
168 return is_vert_in_active_component(ss, expand_cache, corner_verts[faces[f].start()]);
171 ss,
172 expand_cache,
173 faces[f].start() * BKE_subdiv_ccg_key_top_level(*ss.subdiv_ccg).grid_area);
176 ss, expand_cache, BM_elem_index_get(ss.bm->ftable[f]->l_first->v));
177 }
179 return false;
180}
181
187 const Cache &expand_cache,
188 const float3 &position,
189 const int vert)
190{
191 if (expand_cache.texture_distortion_strength == 0.0f) {
192 return expand_cache.vert_falloff[vert];
193 }
194 const Brush *brush = expand_cache.brush;
195 const MTex *mtex = BKE_brush_mask_texture_get(brush, OB_MODE_SCULPT);
196 if (!mtex->tex) {
197 return expand_cache.vert_falloff[vert];
198 }
199
200 float rgba[4];
201 const float avg = BKE_brush_sample_tex_3d(
202 expand_cache.scene, brush, mtex, position, rgba, 0, ss.tex_pool);
203
204 const float distortion = (avg - 0.5f) * expand_cache.texture_distortion_strength *
205 expand_cache.max_vert_falloff;
206 return expand_cache.vert_falloff[vert] + distortion;
207}
208
213static float max_vert_falloff_get(const Cache &expand_cache)
214{
215 if (expand_cache.texture_distortion_strength == 0.0f) {
216 return expand_cache.max_vert_falloff;
217 }
218
219 const MTex *mask_tex = BKE_brush_mask_texture_get(expand_cache.brush, OB_MODE_SCULPT);
220 if (!mask_tex->tex) {
221 return expand_cache.max_vert_falloff;
222 }
223
224 return expand_cache.max_vert_falloff +
225 (0.5f * expand_cache.texture_distortion_strength * expand_cache.max_vert_falloff);
226}
227
229 const Cache &expand_cache,
230 const float3 &position,
231 const int vert)
232{
233 const float max_falloff_factor = max_vert_falloff_get(expand_cache);
234 const float loop_len = (max_falloff_factor / expand_cache.loop_count) +
236
237 const float vertex_falloff_factor = falloff_value_vertex_get(ss, expand_cache, position, vert);
238 const float active_factor = fmod(expand_cache.active_falloff, loop_len);
239 const float falloff_factor = fmod(vertex_falloff_factor, loop_len);
240
241 return falloff_factor < active_factor;
242}
243
248static bool face_state_get(const Object &object,
249 const OffsetIndices<int> faces,
250 const Span<int> corner_verts,
251 const Span<bool> hide_poly,
252 const Span<int> face_sets,
253 const Cache &expand_cache,
254 const int face)
255{
256 if (!hide_poly.is_empty() && hide_poly[face]) {
257 return false;
258 }
259
260 if (!is_face_in_active_component(object, faces, corner_verts, expand_cache, face)) {
261 return false;
262 }
263
264 if (expand_cache.all_enabled) {
265 if (expand_cache.invert) {
266 return false;
267 }
268 return true;
269 }
270
271 bool enabled = false;
272
273 if (expand_cache.snap_enabled_face_sets) {
274 const int face_set = expand_cache.original_face_sets[face];
275 enabled = expand_cache.snap_enabled_face_sets->contains(face_set);
276 }
277 else {
278 const float loop_len = (expand_cache.max_face_falloff / expand_cache.loop_count) +
280
281 const float active_factor = fmod(expand_cache.active_falloff, loop_len);
282 const float falloff_factor = fmod(expand_cache.face_falloff[face], loop_len);
283 enabled = falloff_factor < active_factor;
284 }
285
286 if (expand_cache.falloff_type == FalloffType::ActiveFaceSet) {
287 if (face_sets[face] == expand_cache.initial_active_face_set) {
288 enabled = false;
289 }
290 }
291
292 if (expand_cache.invert) {
293 enabled = !enabled;
294 }
295
296 return enabled;
297}
298
303static float gradient_value_get(const SculptSession &ss,
304 const Cache &expand_cache,
305 const float3 &position,
306 const int vert)
307{
308 if (!expand_cache.falloff_gradient) {
309 return 1.0f;
310 }
311
312 const float max_falloff_factor = max_vert_falloff_get(expand_cache);
313 const float loop_len = (max_falloff_factor / expand_cache.loop_count) +
315
316 const float vertex_falloff_factor = falloff_value_vertex_get(ss, expand_cache, position, vert);
317 const float active_factor = fmod(expand_cache.active_falloff, loop_len);
318 const float falloff_factor = fmod(vertex_falloff_factor, loop_len);
319
320 float linear_falloff;
321
322 if (expand_cache.invert) {
323 /* Active factor is the result of a modulus operation using loop_len, so they will never be
324 * equal and loop_len - active_factor should never be 0. */
325 BLI_assert((loop_len - active_factor) != 0.0f);
326 linear_falloff = (falloff_factor - active_factor) / (loop_len - active_factor);
327 }
328 else {
329 linear_falloff = 1.0f - (falloff_factor / active_factor);
330 }
331
332 if (!expand_cache.brush_gradient) {
333 return linear_falloff;
334 }
335
336 return BKE_brush_curve_strength(expand_cache.brush, linear_falloff, 1.0f);
337}
338
339/* Utility functions for getting all vertices state during expand. */
340
346 const Object &object,
347 const Cache &expand_cache)
348{
349 const SculptSession &ss = *object.sculpt;
350 const int totvert = SCULPT_vertex_count_get(object);
351 BitVector<> enabled_verts(totvert);
352 if (expand_cache.all_enabled) {
353 if (!expand_cache.invert) {
354 enabled_verts.fill(true);
355 }
356 return enabled_verts;
357 }
358 switch (bke::object::pbvh_get(object)->type()) {
360 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
361 const Span<float3> positions = bke::pbvh::vert_positions_eval(depsgraph, object);
362 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
363 const bke::AttributeAccessor attributes = mesh.attributes();
364 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
365 const VArraySpan face_sets = *attributes.lookup_or_default<int>(
366 ".sculpt_face_set", bke::AttrDomain::Face, 0);
368 IndexRange(totvert), 1024, bits::BitsPerInt, [&](const IndexRange range) {
369 for (const int vert : range) {
370 if (!hide_vert.is_empty() && hide_vert[vert]) {
371 continue;
372 }
373 if (!is_vert_in_active_component(ss, expand_cache, vert)) {
374 continue;
375 }
376 if (expand_cache.snap) {
377 const int face_set = face_set::vert_face_set_get(
378 vert_to_face_map, face_sets, vert);
379 enabled_verts[vert].set(expand_cache.snap_enabled_face_sets->contains(face_set));
380 continue;
381 }
382 enabled_verts[vert].set(
383 vert_falloff_is_enabled(ss, expand_cache, positions[vert], vert));
384 }
385 });
386 break;
387 }
389 const Mesh &base_mesh = *static_cast<const Mesh *>(object.data);
390 const bke::AttributeAccessor attributes = base_mesh.attributes();
391 const VArraySpan face_sets = *attributes.lookup_or_default<int>(
392 ".sculpt_face_set", bke::AttrDomain::Face, 0);
393
394 SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
395 const Span<int> grid_to_face_map = subdiv_ccg.grid_to_face_map;
396 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
397 const Span<float3> positions = subdiv_ccg.positions;
398 BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
399 for (const int grid : IndexRange(subdiv_ccg.grids_num)) {
400 const int start = grid * key.grid_area;
401 const int face_set = face_sets[grid_to_face_map[grid]];
402 BKE_subdiv_ccg_foreach_visible_grid_vert(key, grid_hidden, grid, [&](const int offset) {
403 const int vert = start + offset;
404 if (!is_vert_in_active_component(ss, expand_cache, vert)) {
405 return;
406 }
407 if (expand_cache.snap) {
408 enabled_verts[vert].set(expand_cache.snap_enabled_face_sets->contains(face_set));
409 return;
410 }
411 enabled_verts[vert].set(
412 vert_falloff_is_enabled(ss, expand_cache, positions[vert], vert));
413 });
414 }
415 break;
416 }
418 BMesh &bm = *ss.bm;
419 for (const int vert : IndexRange(totvert)) {
420 const BMVert *bm_vert = BM_vert_at_index(&bm, vert);
421 if (BM_elem_flag_test(bm_vert, BM_ELEM_HIDDEN)) {
422 continue;
423 }
424 if (!is_vert_in_active_component(ss, expand_cache, vert)) {
425 continue;
426 }
427 if (expand_cache.snap) {
428 /* TODO: Support face sets for BMesh. */
429 const int face_set = 0;
430 enabled_verts[vert].set(expand_cache.snap_enabled_face_sets->contains(face_set));
431 continue;
432 }
433 enabled_verts[vert].set(vert_falloff_is_enabled(ss, expand_cache, bm_vert->co, vert));
434 }
435 break;
436 }
437 }
438 if (expand_cache.invert) {
439 bits::invert(MutableBoundedBitSpan(enabled_verts));
440 }
441 return enabled_verts;
442}
443
450 const BitSpan enabled_verts,
451 const bool use_mesh_boundary,
452 IndexMaskMemory &memory)
453{
454 SculptSession &ss = *object.sculpt;
455 const int totvert = SCULPT_vertex_count_get(object);
456
457 const IndexMask enabled_mask = IndexMask::from_bits(enabled_verts, memory);
458
459 BitVector<> boundary_verts(totvert);
460 switch (bke::object::pbvh_get(object)->type()) {
462 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
463 const OffsetIndices faces = mesh.faces();
464 const Span<int> corner_verts = mesh.corner_verts();
465 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
466 const bke::AttributeAccessor attributes = mesh.attributes();
467 const VArraySpan hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
468 return IndexMask::from_predicate(enabled_mask, GrainSize(1024), memory, [&](const int vert) {
469 Vector<int> neighbors;
470 for (const int neighbor : vert_neighbors_get_mesh(
471 faces, corner_verts, vert_to_face_map, hide_poly, vert, neighbors))
472 {
473 if (!enabled_verts[neighbor]) {
474 return true;
475 }
476 }
477
478 if (use_mesh_boundary &&
479 boundary::vert_is_boundary(vert_to_face_map, hide_poly, ss.vertex_info.boundary, vert))
480 {
481 return true;
482 }
483
484 return false;
485 });
486 }
488 const Mesh &base_mesh = *static_cast<const Mesh *>(object.data);
489 const OffsetIndices faces = base_mesh.faces();
490 const Span<int> corner_verts = base_mesh.corner_verts();
491
492 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
493 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
494 return IndexMask::from_predicate(enabled_mask, GrainSize(1024), memory, [&](const int vert) {
495 const SubdivCCGCoord coord = SubdivCCGCoord::from_index(key, vert);
496 SubdivCCGNeighbors neighbors;
497 BKE_subdiv_ccg_neighbor_coords_get(subdiv_ccg, coord, false, neighbors);
498 for (const SubdivCCGCoord neighbor : neighbors.coords) {
499 if (!enabled_verts[neighbor.to_index(key)]) {
500 return true;
501 }
502 }
503
504 if (use_mesh_boundary &&
506 faces, corner_verts, ss.vertex_info.boundary, subdiv_ccg, coord))
507 {
508 return true;
509 }
510
511 return false;
512 });
513 }
515 return IndexMask::from_predicate(enabled_mask, GrainSize(1024), memory, [&](const int vert) {
516 BMVert *bm_vert = BM_vert_at_index(ss.bm, vert);
517 Vector<BMVert *, 64> neighbors;
518 for (const BMVert *neighbor : vert_neighbors_get_bmesh(*bm_vert, neighbors)) {
519 if (!enabled_verts[BM_elem_index_get(neighbor)]) {
520 return true;
521 }
522 }
523
524 if (use_mesh_boundary && BM_vert_is_boundary(bm_vert)) {
525 return true;
526 }
527
528 return false;
529 });
530 }
531 }
533 return {};
534}
535
536static void check_topology_islands(Object &ob, FalloffType falloff_type)
537{
538 SculptSession &ss = *ob.sculpt;
539 Cache &expand_cache = *ss.expand_cache;
540
541 expand_cache.check_islands = ELEM(falloff_type,
547
548 if (expand_cache.check_islands) {
550 }
551}
552
553} // namespace blender::ed::sculpt_paint::expand
554
556
557/* Functions implementing different algorithms for initializing falloff values. */
558
560 const Object &object,
561 const int original_vert,
562 const float max_distance)
563{
565 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
566 const bool use_original = false;
567
568 Vector<int> symm_verts;
569 symm_verts.append(original_vert);
570
571 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
572 const Span<float3> positions = bke::pbvh::vert_positions_eval(depsgraph, object);
573 const bke::AttributeAccessor attributes = mesh.attributes();
574 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
575
576 const float3 location = positions[original_vert];
577 for (char symm_it = 1; symm_it <= symm; symm_it++) {
578 if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
579 continue;
580 }
581 const float3 symm_location = symmetry_flip(location, ePaintSymmetryFlags(symm_it));
582 const std::optional<int> nearest = nearest_vert_calc_mesh(
583 pbvh, positions, hide_vert, symm_location, max_distance, use_original);
584 if (!nearest) {
585 continue;
586 }
587 symm_verts.append(*nearest);
588 }
589
590 std::sort(symm_verts.begin(), symm_verts.end());
591 return symm_verts;
592}
593
595 const int original_vert,
596 const float max_distance)
597{
599 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
600 const bool use_original = false;
601
602 Vector<int> symm_verts;
603 symm_verts.append(original_vert);
604
605 const SculptSession &ss = *object.sculpt;
606 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
607 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
608 const Span<float3> positions = subdiv_ccg.positions;
609 const float3 location = positions[original_vert];
610 for (char symm_it = 1; symm_it <= symm; symm_it++) {
611 if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
612 continue;
613 }
614 const float3 symm_location = symmetry_flip(location, ePaintSymmetryFlags(symm_it));
615 const std::optional<SubdivCCGCoord> nearest = nearest_vert_calc_grids(
616 pbvh, subdiv_ccg, symm_location, max_distance, use_original);
617 if (!nearest) {
618 continue;
619 }
620 symm_verts.append(nearest->to_index(key));
621 }
622
623 std::sort(symm_verts.begin(), symm_verts.end());
624 return symm_verts;
625}
626
628 const int original_vert,
629 const float max_distance)
630{
632 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
633 const bool use_original = false;
634
635 Vector<int> symm_verts;
636 symm_verts.append(original_vert);
637
638 const SculptSession &ss = *object.sculpt;
639 BMesh &bm = *ss.bm;
640 const BMVert *original_bm_vert = BM_vert_at_index(&bm, original_vert);
641 const float3 location = original_bm_vert->co;
642 for (char symm_it = 1; symm_it <= symm; symm_it++) {
643 if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
644 continue;
645 }
646 const float3 symm_location = symmetry_flip(location, ePaintSymmetryFlags(symm_it));
647 const std::optional<BMVert *> nearest = nearest_vert_calc_bmesh(
648 pbvh, symm_location, max_distance, use_original);
649 if (!nearest) {
650 continue;
651 }
652 symm_verts.append(BM_elem_index_get(*nearest));
653 }
654
655 std::sort(symm_verts.begin(), symm_verts.end());
656 return symm_verts;
657}
658
660 const Object &object,
661 const int original_vert,
662 const float max_distance)
663{
664 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
665 switch (pbvh.type()) {
667 return find_symm_verts_mesh(depsgraph, object, original_vert, max_distance);
669 return find_symm_verts_grids(object, original_vert, max_distance);
671 return find_symm_verts_bmesh(object, original_vert, max_distance);
672 }
674 return {};
675}
676
677} // namespace blender::ed::sculpt_paint
678
680
686 Object &ob,
687 const IndexMask &initial_verts)
688{
689 const Mesh &mesh = *static_cast<const Mesh *>(ob.data);
690 const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, ob);
691 const Span<int2> edges = mesh.edges();
692 const OffsetIndices faces = mesh.faces();
693 const Span<int> corner_verts = mesh.corner_verts();
694 const Span<int> corner_edges = mesh.corner_edges();
695 const bke::AttributeAccessor attributes = mesh.attributes();
696 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
697
698 SculptSession &ss = *ob.sculpt;
699 if (ss.edge_to_face_map.is_empty()) {
701 faces, corner_edges, edges.size(), ss.edge_to_face_offsets, ss.edge_to_face_indices);
702 }
703 if (ss.vert_to_edge_map.is_empty()) {
704 ss.vert_to_edge_map = bke::mesh::build_vert_to_edge_map(
705 edges, mesh.verts_num, ss.vert_to_edge_offsets, ss.vert_to_edge_indices);
706 }
707
709 initial_verts.foreach_index([&](const int vert) { verts.add(vert); });
710
711 return geodesic::distances_create(vert_positions,
712 edges,
713 faces,
714 corner_verts,
715 ss.vert_to_edge_map,
716 ss.edge_to_face_map,
717 hide_poly,
718 verts,
719 FLT_MAX);
720}
722 Object &ob,
723 const int initial_vert)
724{
725 const Vector<int> symm_verts = find_symm_verts(depsgraph, ob, initial_vert);
726
727 IndexMaskMemory memory;
728 const IndexMask mask = IndexMask::from_indices(symm_verts.as_span(), memory);
729
730 return geodesic_falloff_create(depsgraph, ob, mask);
731}
732
738 const IndexMask &initial_verts,
739 MutableSpan<float> distances)
740{
741 SculptSession &ss = *ob.sculpt;
742 const Mesh &mesh = *static_cast<const Mesh *>(ob.data);
743 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
744 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(ob);
745 const int totvert = SCULPT_vertex_count_get(ob);
746
747 switch (pbvh.type()) {
749 flood_fill::FillDataMesh flood(totvert);
750 initial_verts.foreach_index([&](const int vert) { flood.add_and_skip_initial(vert); });
751 flood.execute(ob, vert_to_face_map, [&](const int from_vert, const int to_vert) {
752 distances[to_vert] = distances[from_vert] + 1.0f;
753 return true;
754 });
755 break;
756 }
758 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
759 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
760
761 flood_fill::FillDataGrids flood(totvert);
762 initial_verts.foreach_index([&](const int vert) {
763 const SubdivCCGCoord orig_coord = SubdivCCGCoord::from_index(key, vert);
764 flood.add_and_skip_initial(orig_coord, vert);
765 });
766 flood.execute(
767 ob,
768 subdiv_ccg,
769 [&](const SubdivCCGCoord from, const SubdivCCGCoord to, const bool is_duplicate) {
770 const int from_vert = from.to_index(key);
771 const int to_vert = to.to_index(key);
772 if (is_duplicate) {
773 distances[to_vert] = distances[from_vert];
774 }
775 else {
776 distances[to_vert] = distances[from_vert] + 1.0f;
777 }
778 return true;
779 });
780 break;
781 }
783 BMesh &bm = *ss.bm;
784 flood_fill::FillDataBMesh flood(totvert);
785 initial_verts.foreach_index(
786 [&](const int vert) { flood.add_and_skip_initial(BM_vert_at_index(&bm, vert), vert); });
787 flood.execute(ob, [&](BMVert *from_bm_vert, BMVert *to_bm_vert) {
788 const int from_vert = BM_elem_index_get(from_bm_vert);
789 const int to_vert = BM_elem_index_get(to_bm_vert);
790 distances[to_vert] = distances[from_vert] + 1.0f;
791 return true;
792 });
793 break;
794 }
795 }
796}
797
799 Object &ob,
800 const int initial_vert)
801{
802 const Vector<int> symm_verts = find_symm_verts(depsgraph, ob, initial_vert);
803
804 IndexMaskMemory memory;
805 const IndexMask mask = IndexMask::from_indices(symm_verts.as_span(), memory);
806
807 Array<float> dists(SCULPT_vertex_count_get(ob), 0.0f);
808 calc_topology_falloff_from_verts(ob, mask, dists);
809 return dists;
810}
811
818 Object &ob,
819 const int vert,
820 const float edge_sensitivity,
821 const int blur_steps)
822{
823 SculptSession &ss = *ob.sculpt;
824 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(ob);
825 const int totvert = SCULPT_vertex_count_get(ob);
826 Array<float> dists(totvert, 0.0f);
827 Array<float> edge_factors(totvert, 1.0f);
828
829 switch (pbvh.type()) {
831 const Mesh &mesh = *static_cast<const Mesh *>(ob.data);
832 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
833 const Span<float3> vert_normals = bke::pbvh::vert_normals_eval(depsgraph, ob);
834
835 const float3 orig_normal = vert_normals[vert];
836 flood_fill::FillDataMesh flood(totvert);
837 flood.add_initial(find_symm_verts(depsgraph, ob, vert));
838 flood.execute(ob, vert_to_face_map, [&](const int from_vert, const int to_vert) {
839 const float3 &from_normal = vert_normals[from_vert];
840 const float3 &to_normal = vert_normals[to_vert];
841 const float from_edge_factor = edge_factors[from_vert];
842 const float dist = math::dot(orig_normal, to_normal) *
843 powf(from_edge_factor, edge_sensitivity);
844 edge_factors[to_vert] = math::dot(to_normal, from_normal) * from_edge_factor;
845 dists[to_vert] = std::clamp(dist, 0.0f, 1.0f);
846 return true;
847 });
848 break;
849 }
851 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
852 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
853 const Span<float3> normals = subdiv_ccg.normals;
854 const float3 orig_normal = normals[vert];
855 flood_fill::FillDataGrids flood(totvert);
856 flood.add_initial(key, find_symm_verts(depsgraph, ob, vert));
857 flood.execute(
858 ob,
859 subdiv_ccg,
860 [&](const SubdivCCGCoord from, const SubdivCCGCoord to, const bool is_duplicate) {
861 const int from_vert = from.to_index(key);
862 const int to_vert = to.to_index(key);
863 if (is_duplicate) {
864 edge_factors[to_vert] = edge_factors[from_vert];
865 dists[to_vert] = dists[from_vert];
866 }
867 else {
868 const float3 &from_normal = normals[from_vert];
869 const float3 &to_normal = normals[to_vert];
870 const float from_edge_factor = edge_factors[from_vert];
871 const float dist = math::dot(orig_normal, to_normal) *
872 powf(from_edge_factor, edge_sensitivity);
873 edge_factors[to_vert] = math::dot(to_normal, from_normal) * from_edge_factor;
874 dists[to_vert] = std::clamp(dist, 0.0f, 1.0f);
875 }
876 return true;
877 });
878 break;
879 }
881 flood_fill::FillDataBMesh flood(totvert);
882 BMVert *orig_vert = BM_vert_at_index(ss.bm, vert);
883 const float3 orig_normal = orig_vert->no;
884 flood.add_initial(*ss.bm, find_symm_verts(depsgraph, ob, vert));
885 flood.execute(ob, [&](BMVert *from_bm_vert, BMVert *to_bm_vert) {
886 const float3 &from_normal = from_bm_vert->no;
887 const float3 &to_normal = to_bm_vert->no;
888 const int from_vert = BM_elem_index_get(from_bm_vert);
889 const int to_vert = BM_elem_index_get(to_bm_vert);
890 const float from_edge_factor = edge_factors[from_vert];
891 const float dist = math::dot(orig_normal, to_normal) *
892 powf(from_edge_factor, edge_sensitivity);
893 edge_factors[to_vert] = math::dot(to_normal, from_normal) * from_edge_factor;
894 dists[to_vert] = std::clamp(dist, 0.0f, 1.0f);
895 return true;
896 });
897 break;
898 }
899 }
900
901 smooth::blur_geometry_data_array(ob, blur_steps, dists);
902
903 for (int i = 0; i < totvert; i++) {
904 dists[i] = 1.0 - dists[i];
905 }
906
907 return dists;
908}
909
915 const Object &object,
916 const int vert)
917{
918 SculptSession &ss = *object.sculpt;
919 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
921
922 const Vector<int> symm_verts = find_symm_verts(depsgraph, object, vert);
923
924 switch (pbvh.type()) {
926 const Span<float3> positions = bke::pbvh::vert_positions_eval(depsgraph, object);
927
928 Array<float3> locations(symm_verts.size());
929 array_utils::gather(positions, symm_verts.as_span(), locations.as_mutable_span());
930
931 threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
932 for (const int vert : range) {
933 float dist = std::numeric_limits<float>::max();
934 for (const float3 &location : locations) {
935 dist = std::min(dist, math::distance(positions[vert], location));
936 }
937 dists[vert] = dist;
938 }
939 });
940 break;
941 }
943 SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
944 const Span<float3> positions = subdiv_ccg.positions;
945
946 Array<float3> locations(symm_verts.size());
947 for (const int i : symm_verts.index_range()) {
948 locations[i] = positions[symm_verts[i]];
949 }
950
951 threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
952 for (const int vert : range) {
953 float dist = std::numeric_limits<float>::max();
954 for (const float3 &location : locations) {
955 dist = std::min(dist, math::distance(positions[vert], location));
956 }
957 dists[vert] = dist;
958 }
959 });
960 break;
961 }
963 BMesh &bm = *ss.bm;
964
965 Array<float3> locations(symm_verts.size());
966 for (const int i : symm_verts.index_range()) {
967 locations[i] = BM_vert_at_index(&bm, symm_verts[i])->co;
968 }
969
970 threading::parallel_for(IndexRange(bm.totvert), 1024, [&](const IndexRange range) {
971 for (const int vert : range) {
972 float dist = std::numeric_limits<float>::max();
973 for (const float3 &location : locations) {
974 dist = std::min(dist,
975 math::distance(float3(BM_vert_at_index(&bm, vert)->co), location));
976 }
977 dists[vert] = dist;
978 }
979 });
980 break;
981 }
982 }
983
984 return dists;
985}
986
993 Object &ob,
994 const int inititial_vert)
995{
996 const Vector<int> symm_verts = find_symm_verts(depsgraph, ob, inititial_vert);
997
998 BitVector<> boundary_verts(SCULPT_vertex_count_get(ob));
999 for (const int vert : symm_verts) {
1000 if (std::unique_ptr<boundary::SculptBoundary> boundary = boundary::data_init(
1001 depsgraph, ob, nullptr, vert, FLT_MAX))
1002 {
1003 for (const int vert : boundary->verts) {
1004 boundary_verts[vert].set();
1005 }
1006 }
1007 }
1008
1009 IndexMaskMemory memory;
1010 const IndexMask boundary_mask = IndexMask::from_bits(boundary_verts, memory);
1011
1012 Array<float> dists(SCULPT_vertex_count_get(ob), 0.0f);
1013 calc_topology_falloff_from_verts(ob, boundary_mask, dists);
1014 return dists;
1015}
1016
1023 Object &ob,
1024 const int vert)
1025{
1026 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(ob);
1027 const Mesh &mesh = *static_cast<const Mesh *>(ob.data);
1028 const OffsetIndices<int> faces = mesh.faces();
1029 const Span<int> corner_verts = mesh.corner_verts();
1030 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
1031 const int totvert = SCULPT_vertex_count_get(ob);
1032 Array<float> dists(totvert, 0.0f);
1033
1034 /* This algorithm uses mesh data (faces and loops), so this falloff type can't be initialized for
1035 * Multires. It also does not make sense to implement it for dyntopo as the result will be the
1036 * same as Topology falloff. */
1037 if (pbvh.type() != bke::pbvh::Type::Mesh) {
1038 return dists;
1039 }
1040
1041 const Vector<int> symm_verts = find_symm_verts(depsgraph, ob, vert);
1042
1043 /* Search and mask as visited the initial vertices using the enabled symmetry passes. */
1044 BitVector<> visited_verts(totvert);
1045 std::queue<int> queue;
1046 for (const int vert : symm_verts) {
1047 queue.push(vert);
1048 visited_verts[vert].set();
1049 }
1050
1051 if (queue.empty()) {
1052 return dists;
1053 }
1054
1055 /* Propagate the falloff increasing the value by 1 each time a new vertex is visited. */
1056 while (!queue.empty()) {
1057 const int next_vert = queue.front();
1058 queue.pop();
1059
1060 for (const int face : vert_to_face_map[next_vert]) {
1061 for (const int vert : corner_verts.slice(faces[face])) {
1062 if (visited_verts[vert]) {
1063 continue;
1064 }
1065 dists[vert] = dists[next_vert] + 1.0f;
1066 visited_verts[vert].set();
1067 queue.push(vert);
1068 }
1069 }
1070 }
1071
1072 return dists;
1073}
1074
1079static void update_max_vert_falloff_value(const Object &object, Cache &expand_cache)
1080{
1081 SculptSession &ss = *object.sculpt;
1082 expand_cache.max_vert_falloff = threading::parallel_reduce(
1084 4096,
1085 std::numeric_limits<float>::lowest(),
1086 [&](const IndexRange range, float max) {
1087 for (const int vert : range) {
1088 if (expand_cache.vert_falloff[vert] == FLT_MAX) {
1089 continue;
1090 }
1091 if (!is_vert_in_active_component(ss, expand_cache, vert)) {
1092 continue;
1093 }
1094 max = std::max(max, expand_cache.vert_falloff[vert]);
1095 }
1096 return max;
1097 },
1098 [](const float a, const float b) { return std::max(a, b); });
1099}
1100
1105static void update_max_face_falloff_factor(const Object &object, Mesh &mesh, Cache &expand_cache)
1106{
1107 const OffsetIndices<int> faces = mesh.faces();
1108 const Span<int> corner_verts = mesh.corner_verts();
1109 expand_cache.max_face_falloff = threading::parallel_reduce(
1110 faces.index_range(),
1111 4096,
1112 std::numeric_limits<float>::lowest(),
1113 [&](const IndexRange range, float max) {
1114 for (const int face : range) {
1115 if (expand_cache.face_falloff[face] == FLT_MAX) {
1116 continue;
1117 }
1118 if (!is_face_in_active_component(object, faces, corner_verts, expand_cache, face)) {
1119 continue;
1120 }
1121 max = std::max(max, expand_cache.face_falloff[face]);
1122 }
1123 return max;
1124 },
1125 [](const float a, const float b) { return std::max(a, b); });
1126}
1127
1134static void vert_to_face_falloff_grids(SculptSession &ss, Mesh *mesh, Cache &expand_cache)
1135{
1136 const OffsetIndices faces = mesh->faces();
1138
1139 threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
1140 for (const int face : range) {
1141 float accum = 0.0f;
1142 for (const int corner : faces[face]) {
1143 const int grid_loop_index = corner * key.grid_area;
1144 for (int g = 0; g < key.grid_area; g++) {
1145 accum += expand_cache.vert_falloff[grid_loop_index + g];
1146 }
1147 }
1148 expand_cache.face_falloff[face] = accum / (faces[face].size() * key.grid_area);
1149 }
1150 });
1151}
1152
1153static void vert_to_face_falloff_mesh(Mesh *mesh, Cache &expand_cache)
1154{
1155 const OffsetIndices faces = mesh->faces();
1156 const Span<int> corner_verts = mesh->corner_verts();
1157
1158 threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
1159 for (const int face : range) {
1160 const Span<int> face_verts = corner_verts.slice(faces[face]);
1161 float accum = 0.0f;
1162 for (const int vert : face_verts) {
1163 accum += expand_cache.vert_falloff[vert];
1164 }
1165 expand_cache.face_falloff[face] = accum / face_verts.size();
1166 }
1167 });
1168}
1169
1173static void vert_to_face_falloff(Object &object, Mesh *mesh, Cache &expand_cache)
1174{
1175 BLI_assert(!expand_cache.vert_falloff.is_empty());
1176
1177 expand_cache.face_falloff.reinitialize(mesh->faces_num);
1178 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
1179
1180 if (pbvh.type() == bke::pbvh::Type::Mesh) {
1181 vert_to_face_falloff_mesh(mesh, expand_cache);
1182 }
1183 else if (pbvh.type() == bke::pbvh::Type::Grids) {
1184 vert_to_face_falloff_grids(*object.sculpt, mesh, expand_cache);
1185 }
1186 else {
1187 BLI_assert(false);
1188 }
1189}
1190
1191/* Recursions. These functions will generate new falloff values based on the state of the vertices
1192 * from the current Cache options and falloff values. */
1193
1198static void geodesics_from_state_boundary(const Depsgraph &depsgraph,
1199 Object &ob,
1200 Cache &expand_cache,
1201 const BitSpan enabled_verts)
1202{
1203 BLI_assert(bke::object::pbvh_get(ob)->type() == bke::pbvh::Type::Mesh);
1204
1205 IndexMaskMemory memory;
1206 const IndexMask boundary_verts = boundary_from_enabled(ob, enabled_verts, false, memory);
1207
1208 expand_cache.face_falloff = {};
1209
1210 expand_cache.vert_falloff = geodesic_falloff_create(depsgraph, ob, boundary_verts);
1211}
1212
1218 Cache &expand_cache,
1219 const BitSpan enabled_verts)
1220{
1221 expand_cache.face_falloff = {};
1222
1224 expand_cache.vert_falloff.fill(0);
1225
1226 IndexMaskMemory memory;
1227 const IndexMask boundary_verts = boundary_from_enabled(ob, enabled_verts, false, memory);
1228
1229 calc_topology_falloff_from_verts(ob, boundary_verts, expand_cache.vert_falloff);
1230}
1231
1235static void resursion_step_add(const Depsgraph &depsgraph,
1236 Object &ob,
1237 Cache &expand_cache,
1238 const RecursionType recursion_type)
1239{
1240 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(ob);
1241 if (pbvh.type() != bke::pbvh::Type::Mesh) {
1242 return;
1243 }
1244
1245 const BitVector<> enabled_verts = enabled_state_to_bitmap(depsgraph, ob, expand_cache);
1246
1247 /* Each time a new recursion step is created, reset the distortion strength. This is the expected
1248 * result from the recursion, as otherwise the new falloff will render with undesired distortion
1249 * from the beginning. */
1250 expand_cache.texture_distortion_strength = 0.0f;
1251
1252 switch (recursion_type) {
1253 case RecursionType::Geodesic:
1254 geodesics_from_state_boundary(depsgraph, ob, expand_cache, enabled_verts);
1255 break;
1256 case RecursionType::Topology:
1257 topology_from_state_boundary(ob, expand_cache, enabled_verts);
1258 break;
1259 }
1260
1261 update_max_vert_falloff_value(ob, expand_cache);
1262 if (expand_cache.target == TargetType::FaceSets) {
1263 Mesh &mesh = *static_cast<Mesh *>(ob.data);
1264 vert_to_face_falloff(ob, &mesh, expand_cache);
1265 update_max_face_falloff_factor(ob, mesh, expand_cache);
1266 }
1267}
1268
1269/* Face Set Boundary falloff. */
1270
1275static void init_from_face_set_boundary(const Depsgraph &depsgraph,
1276 Object &ob,
1277 Cache &expand_cache,
1278 const int active_face_set,
1279 const bool internal_falloff)
1280{
1281 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(ob);
1282 const int totvert = SCULPT_vertex_count_get(ob);
1283
1284 Array<bool> vert_has_face_set(totvert);
1285 Array<bool> vert_has_unique_face_set(totvert);
1286 switch (pbvh.type()) {
1287 case bke::pbvh::Type::Mesh: {
1288 const Mesh &mesh = *static_cast<const Mesh *>(ob.data);
1289 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
1290 const bke::AttributeAccessor attributes = mesh.attributes();
1291 const VArraySpan face_sets = *attributes.lookup<int>(".sculpt_face_set",
1292 bke::AttrDomain::Face);
1293 threading::parallel_for(IndexRange(totvert), 1024, [&](const IndexRange range) {
1294 for (const int vert : range) {
1295 vert_has_face_set[vert] = face_set::vert_has_face_set(
1296 vert_to_face_map, face_sets, vert, active_face_set);
1297 vert_has_unique_face_set[vert] = face_set::vert_has_unique_face_set(
1298 vert_to_face_map, face_sets, vert);
1299 }
1300 });
1301 break;
1302 }
1303 case bke::pbvh::Type::Grids: {
1304 const Mesh &base_mesh = *static_cast<const Mesh *>(ob.data);
1305 const OffsetIndices<int> faces = base_mesh.faces();
1306 const Span<int> corner_verts = base_mesh.corner_verts();
1307 const GroupedSpan<int> vert_to_face_map = base_mesh.vert_to_face_map();
1308 const bke::AttributeAccessor attributes = base_mesh.attributes();
1309 const VArraySpan face_sets = *attributes.lookup<int>(".sculpt_face_set",
1310 bke::AttrDomain::Face);
1311 const SubdivCCG &subdiv_ccg = *ob.sculpt->subdiv_ccg;
1312 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
1313 threading::parallel_for(IndexRange(totvert), 1024, [&](const IndexRange range) {
1314 for (const int vert : range) {
1315 const SubdivCCGCoord coord = SubdivCCGCoord::from_index(key, vert);
1316 vert_has_face_set[vert] = face_set::vert_has_face_set(
1317 subdiv_ccg, face_sets, coord.grid_index, active_face_set);
1318 vert_has_unique_face_set[vert] = face_set::vert_has_unique_face_set(
1319 faces, corner_verts, vert_to_face_map, face_sets, subdiv_ccg, coord);
1320 }
1321 });
1322 break;
1323 }
1324 case bke::pbvh::Type::BMesh: {
1325 BMesh &bm = *ob.sculpt->bm;
1326 const int offset = CustomData_get_offset_named(&bm.pdata, CD_PROP_INT32, ".sculpt_face_set");
1328 threading::parallel_for(IndexRange(totvert), 1024, [&](const IndexRange range) {
1329 for (const int vert : range) {
1330 const BMVert *bm_vert = BM_vert_at_index(&bm, vert);
1331 vert_has_face_set[vert] = face_set::vert_has_face_set(offset, *bm_vert, active_face_set);
1332 vert_has_unique_face_set[vert] = face_set::vert_has_unique_face_set(offset, *bm_vert);
1333 }
1334 });
1335 break;
1336 }
1337 }
1338
1339 BitVector<> enabled_verts(totvert);
1340 for (int i = 0; i < totvert; i++) {
1341 if (!vert_has_unique_face_set[i]) {
1342 continue;
1343 }
1344 if (!vert_has_face_set[i]) {
1345 continue;
1346 }
1347 enabled_verts[i].set();
1348 }
1349
1350 if (pbvh.type() == bke::pbvh::Type::Mesh) {
1351 geodesics_from_state_boundary(depsgraph, ob, expand_cache, enabled_verts);
1352 }
1353 else {
1354 topology_from_state_boundary(ob, expand_cache, enabled_verts);
1355 }
1356
1357 if (internal_falloff) {
1358 for (int i = 0; i < totvert; i++) {
1359 if (!(vert_has_face_set[i] && vert_has_unique_face_set[i])) {
1360 continue;
1361 }
1362 expand_cache.vert_falloff[i] *= -1.0f;
1363 }
1364
1365 float min_factor = FLT_MAX;
1366 for (int i = 0; i < totvert; i++) {
1367 min_factor = min_ff(expand_cache.vert_falloff[i], min_factor);
1368 }
1369
1370 const float additional_falloff = fabsf(min_factor);
1371 for (int i = 0; i < totvert; i++) {
1372 expand_cache.vert_falloff[i] += additional_falloff;
1373 }
1374 }
1375 else {
1376 for (int i = 0; i < totvert; i++) {
1377 if (!vert_has_face_set[i]) {
1378 continue;
1379 }
1380 expand_cache.vert_falloff[i] = 0.0f;
1381 }
1382 }
1383}
1384
1390 Cache &expand_cache,
1391 Object &ob,
1392 const int vert,
1393 FalloffType falloff_type)
1394{
1395 expand_cache.falloff_type = falloff_type;
1396
1397 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(ob);
1398 const bool has_topology_info = pbvh.type() == bke::pbvh::Type::Mesh;
1399
1400 switch (falloff_type) {
1401 case FalloffType::Geodesic:
1402 expand_cache.vert_falloff = has_topology_info ?
1405 break;
1406 case FalloffType::Topology:
1407 expand_cache.vert_falloff = topology_falloff_create(depsgraph, ob, vert);
1408 break;
1409 case FalloffType::TopologyNormals:
1410 expand_cache.vert_falloff = has_topology_info ?
1413 break;
1414 case FalloffType::Normals:
1415 expand_cache.vert_falloff = normals_falloff_create(
1416 depsgraph,
1417 ob,
1418 vert,
1420 expand_cache.normal_falloff_blur_steps);
1421 break;
1422 case FalloffType::Sphere:
1423 expand_cache.vert_falloff = spherical_falloff_create(depsgraph, ob, vert);
1424 break;
1425 case FalloffType::BoundaryTopology:
1427 break;
1428 case FalloffType::BoundaryFaceSet:
1430 depsgraph, ob, expand_cache, expand_cache.initial_active_face_set, true);
1431 break;
1432 case FalloffType::ActiveFaceSet:
1434 depsgraph, ob, expand_cache, expand_cache.initial_active_face_set, false);
1435 break;
1436 }
1437
1438 /* Update max falloff values and propagate to base mesh faces if needed. */
1439 update_max_vert_falloff_value(ob, expand_cache);
1440 if (expand_cache.target == TargetType::FaceSets) {
1441 Mesh &mesh = *static_cast<Mesh *>(ob.data);
1442 vert_to_face_falloff(ob, &mesh, expand_cache);
1443 update_max_face_falloff_factor(ob, mesh, expand_cache);
1444 }
1445}
1446
1452static void snap_init_from_enabled(const Depsgraph &depsgraph,
1453 const Object &object,
1454 Cache &expand_cache)
1455{
1456 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
1457 if (pbvh.type() != bke::pbvh::Type::Mesh) {
1458 return;
1459 }
1460 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
1461 const OffsetIndices<int> faces = mesh.faces();
1462 const Span<int> corner_verts = mesh.corner_verts();
1463 /* Make sure this code runs with snapping and invert disabled. This simplifies the code and
1464 * prevents using this function with snapping already enabled. */
1465 const bool prev_snap_state = expand_cache.snap;
1466 const bool prev_invert_state = expand_cache.invert;
1467 expand_cache.snap = false;
1468 expand_cache.invert = false;
1469
1470 const BitVector<> enabled_verts = enabled_state_to_bitmap(depsgraph, object, expand_cache);
1471
1472 for (const int i : faces.index_range()) {
1473 const int face_set = expand_cache.original_face_sets[i];
1474 expand_cache.snap_enabled_face_sets->add(face_set);
1475 }
1476
1477 for (const int i : faces.index_range()) {
1478 const Span<int> face_verts = corner_verts.slice(faces[i]);
1479 const bool any_disabled = std::any_of(face_verts.begin(),
1480 face_verts.end(),
1481 [&](const int vert) { return !enabled_verts[vert]; });
1482 if (any_disabled) {
1483 const int face_set = expand_cache.original_face_sets[i];
1484 expand_cache.snap_enabled_face_sets->remove(face_set);
1485 }
1486 }
1487
1488 expand_cache.snap = prev_snap_state;
1489 expand_cache.invert = prev_invert_state;
1490}
1491
1493{
1494 MEM_delete<Cache>(ss.expand_cache);
1495 /* Needs to be set to nullptr as the paint cursor relies on checking this pointer detecting if an
1496 * expand operation is running. */
1497 ss.expand_cache = nullptr;
1498}
1499
1503static void restore_face_set_data(Object &object, Cache &expand_cache)
1504{
1505 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
1506 bke::SpanAttributeWriter<int> face_sets = face_set::ensure_face_sets_mesh(
1507 *static_cast<Mesh *>(object.data));
1508 face_sets.span.copy_from(expand_cache.original_face_sets);
1509 face_sets.finish();
1510
1511 IndexMaskMemory memory;
1512 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1513 pbvh.tag_face_sets_changed(node_mask);
1514}
1515
1516static void restore_color_data(Object &ob, Cache &expand_cache)
1517{
1518 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(ob);
1520 Mesh &mesh = *static_cast<Mesh *>(ob.data);
1521 IndexMaskMemory memory;
1522 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1523
1524 const OffsetIndices<int> faces = mesh.faces();
1525 const Span<int> corner_verts = mesh.corner_verts();
1526 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
1527 bke::GSpanAttributeWriter color_attribute = color::active_color_attribute_for_write(mesh);
1528 node_mask.foreach_index([&](const int i) {
1529 for (const int vert : nodes[i].verts()) {
1530 color::color_vert_set(faces,
1531 corner_verts,
1532 vert_to_face_map,
1533 color_attribute.domain,
1534 vert,
1535 expand_cache.original_colors[vert],
1536 color_attribute.span);
1537 }
1538 });
1539 pbvh.tag_attribute_changed(node_mask, mesh.active_color_attribute);
1540 color_attribute.finish();
1541}
1542
1543static void write_mask_data(Object &object, const Span<float> mask)
1544{
1545 SculptSession &ss = *object.sculpt;
1546 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
1547 IndexMaskMemory memory;
1548 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1549 switch (pbvh.type()) {
1550 case bke::pbvh::Type::Mesh: {
1551 Mesh &mesh = *static_cast<Mesh *>(object.data);
1552 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
1553 attributes.remove(".sculpt_mask");
1554 attributes.add<float>(".sculpt_mask",
1555 bke::AttrDomain::Point,
1557 bke::pbvh::update_mask_mesh(mesh, node_mask, pbvh);
1558 break;
1559 }
1560 case bke::pbvh::Type::BMesh: {
1561 BMesh &bm = *ss.bm;
1562 const int offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask");
1564 for (const int i : mask.index_range()) {
1565 BM_ELEM_CD_SET_FLOAT(BM_vert_at_index(&bm, i), offset, mask[i]);
1566 }
1567 bke::pbvh::update_mask_bmesh(bm, node_mask, pbvh);
1568 break;
1569 }
1570 case bke::pbvh::Type::Grids: {
1571 SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
1572 subdiv_ccg.masks.as_mutable_span().copy_from(mask);
1573 bke::pbvh::update_mask_grids(subdiv_ccg, node_mask, pbvh);
1574 break;
1575 }
1576 }
1577 pbvh.tag_masks_changed(node_mask);
1578}
1579
1580/* Main function to restore the original state of the data to how it was before starting the expand
1581 * operation. */
1582static void restore_original_state(bContext *C, Object &ob, Cache &expand_cache)
1583{
1584 switch (expand_cache.target) {
1585 case TargetType::Mask:
1586 write_mask_data(ob, expand_cache.original_mask);
1587 flush_update_step(C, UpdateType::Mask);
1588 flush_update_done(C, ob, UpdateType::Mask);
1590 break;
1591 case TargetType::FaceSets:
1592 restore_face_set_data(ob, expand_cache);
1593 flush_update_step(C, UpdateType::FaceSet);
1594 flush_update_done(C, ob, UpdateType::FaceSet);
1596 break;
1597 case TargetType::Colors:
1598 restore_color_data(ob, expand_cache);
1599 flush_update_step(C, UpdateType::Color);
1600 flush_update_done(C, ob, UpdateType::Color);
1601 break;
1602 }
1603}
1604
1609{
1611 SculptSession &ss = *ob.sculpt;
1612
1614
1615 undo::push_end(ob);
1617}
1618
1619/* Functions to update the sculpt mesh data. */
1620
1621static void calc_new_mask_mesh(const SculptSession &ss,
1622 const Span<float3> positions,
1623 const BitSpan enabled_verts,
1624 const Span<int> verts,
1625 const MutableSpan<float> mask)
1626{
1627 const Cache &expand_cache = *ss.expand_cache;
1628
1629 for (const int i : verts.index_range()) {
1630 const int vert = verts[i];
1631 const bool enabled = enabled_verts[vert];
1632
1633 if (expand_cache.check_islands && !is_vert_in_active_component(ss, expand_cache, vert)) {
1634 continue;
1635 }
1636
1637 if (enabled) {
1638 mask[i] = gradient_value_get(ss, expand_cache, positions[vert], vert);
1639 }
1640 else {
1641 mask[i] = 0.0f;
1642 }
1643
1644 if (expand_cache.preserve) {
1645 if (expand_cache.invert) {
1646 mask[i] = min_ff(mask[i], expand_cache.original_mask[vert]);
1647 }
1648 else {
1649 mask[i] = max_ff(mask[i], expand_cache.original_mask[vert]);
1650 }
1651 }
1652 }
1653
1654 mask::clamp_mask(mask);
1655}
1656
1657static bool update_mask_grids(const SculptSession &ss,
1658 const BitSpan enabled_verts,
1660 SubdivCCG &subdiv_ccg)
1661{
1662 const Cache &expand_cache = *ss.expand_cache;
1663 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
1664 const Span<float3> positions = subdiv_ccg.positions;
1665 MutableSpan<float> masks = subdiv_ccg.masks;
1666
1667 bool any_changed = false;
1668 for (const int grid : node.grids()) {
1669 for (const int vert : bke::ccg::grid_range(key, grid)) {
1670 const float initial_mask = masks[vert];
1671
1672 if (expand_cache.check_islands && !is_vert_in_active_component(ss, expand_cache, vert)) {
1673 continue;
1674 }
1675
1676 float new_mask;
1677
1678 if (enabled_verts[vert]) {
1679 new_mask = gradient_value_get(ss, expand_cache, positions[vert], vert);
1680 }
1681 else {
1682 new_mask = 0.0f;
1683 }
1684
1685 if (expand_cache.preserve) {
1686 if (expand_cache.invert) {
1687 new_mask = min_ff(new_mask, expand_cache.original_mask[vert]);
1688 }
1689 else {
1690 new_mask = max_ff(new_mask, expand_cache.original_mask[vert]);
1691 }
1692 }
1693
1694 if (new_mask == initial_mask) {
1695 continue;
1696 }
1697
1698 masks[vert] = clamp_f(new_mask, 0.0f, 1.0f);
1699 any_changed = true;
1700 }
1701 }
1702 if (any_changed) {
1703 bke::pbvh::node_update_mask_grids(key, masks, node);
1704 }
1705 return any_changed;
1706}
1707
1708static bool update_mask_bmesh(SculptSession &ss,
1709 const BitSpan enabled_verts,
1710 const int mask_offset,
1712{
1713 const Cache &expand_cache = *ss.expand_cache;
1714
1715 bool any_changed = false;
1716 for (BMVert *vert : BKE_pbvh_bmesh_node_unique_verts(node)) {
1717 const int vert_index = BM_elem_index_get(vert);
1718 const float initial_mask = BM_ELEM_CD_GET_FLOAT(vert, mask_offset);
1719
1720 if (expand_cache.check_islands && !is_vert_in_active_component(ss, expand_cache, vert_index)) {
1721 continue;
1722 }
1723
1724 float new_mask;
1725
1726 if (enabled_verts[vert_index]) {
1727 new_mask = gradient_value_get(ss, expand_cache, vert->co, vert_index);
1728 }
1729 else {
1730 new_mask = 0.0f;
1731 }
1732
1733 if (expand_cache.preserve) {
1734 if (expand_cache.invert) {
1735 new_mask = min_ff(new_mask, expand_cache.original_mask[BM_elem_index_get(vert)]);
1736 }
1737 else {
1738 new_mask = max_ff(new_mask, expand_cache.original_mask[BM_elem_index_get(vert)]);
1739 }
1740 }
1741
1742 if (new_mask == initial_mask) {
1743 continue;
1744 }
1745
1746 BM_ELEM_CD_SET_FLOAT(vert, mask_offset, clamp_f(new_mask, 0.0f, 1.0f));
1747 any_changed = true;
1748 }
1749 if (any_changed) {
1750 bke::pbvh::node_update_mask_bmesh(mask_offset, *node);
1751 }
1752 return any_changed;
1753}
1754
1758static void face_sets_update(Object &object, Cache &expand_cache)
1759{
1760 Mesh &mesh = *static_cast<Mesh *>(object.data);
1761 bke::SpanAttributeWriter<int> face_sets = face_set::ensure_face_sets_mesh(mesh);
1762 const OffsetIndices<int> faces = mesh.faces();
1763 const Span<int> corner_verts = mesh.corner_verts();
1764 const bke::AttributeAccessor attributes = mesh.attributes();
1765 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
1766 for (const int f : face_sets.span.index_range()) {
1767 const bool enabled = face_state_get(
1768 object, faces, corner_verts, hide_poly, face_sets.span, expand_cache, f);
1769 if (!enabled) {
1770 continue;
1771 }
1772 if (expand_cache.preserve) {
1773 face_sets.span[f] += expand_cache.next_face_set;
1774 }
1775 else {
1776 face_sets.span[f] = expand_cache.next_face_set;
1777 }
1778 }
1779
1780 face_sets.finish();
1781
1782 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
1783 pbvh.tag_face_sets_changed(expand_cache.node_mask);
1784}
1785
1789static bool colors_update_task(const Depsgraph &depsgraph,
1790 Object &object,
1791 const Span<float3> vert_positions,
1792 const OffsetIndices<int> faces,
1793 const Span<int> corner_verts,
1794 const GroupedSpan<int> vert_to_face_map,
1795 const Span<bool> hide_vert,
1796 const Span<float> mask,
1797 bke::pbvh::MeshNode *node,
1798 bke::GSpanAttributeWriter &color_attribute)
1799{
1800 const SculptSession &ss = *object.sculpt;
1801 const Cache &expand_cache = *ss.expand_cache;
1802
1803 const BitVector<> enabled_verts = enabled_state_to_bitmap(depsgraph, object, expand_cache);
1804
1805 bool any_changed = false;
1806 const Span<int> verts = node->verts();
1807 for (const int i : verts.index_range()) {
1808 const int vert = verts[i];
1809 if (!hide_vert.is_empty() && hide_vert[vert]) {
1810 continue;
1811 }
1812
1813 float4 initial_color = color::color_vert_get(
1814 faces, corner_verts, vert_to_face_map, color_attribute.span, color_attribute.domain, vert);
1815
1816 float fade;
1817
1818 if (enabled_verts[vert]) {
1819 fade = gradient_value_get(ss, expand_cache, vert_positions[vert], vert);
1820 }
1821 else {
1822 fade = 0.0f;
1823 }
1824
1825 if (!mask.is_empty()) {
1826 fade *= 1.0f - mask[vert];
1827 }
1828 fade = clamp_f(fade, 0.0f, 1.0f);
1829
1830 float4 final_color;
1831 float4 final_fill_color;
1832 mul_v4_v4fl(final_fill_color, expand_cache.fill_color, fade);
1833 IMB_blend_color_float(final_color,
1834 expand_cache.original_colors[vert],
1835 final_fill_color,
1836 IMB_BlendMode(expand_cache.blend_mode));
1837
1838 if (initial_color == final_color) {
1839 continue;
1840 }
1841
1842 color::color_vert_set(faces,
1843 corner_verts,
1844 vert_to_face_map,
1845 color_attribute.domain,
1846 vert,
1847 final_color,
1848 color_attribute.span);
1849
1850 any_changed = true;
1851 }
1852 return any_changed;
1853}
1854
1855/* Store the original mesh data state in the expand cache. */
1856static void original_state_store(Object &ob, Cache &expand_cache)
1857{
1858 Mesh &mesh = *static_cast<Mesh *>(ob.data);
1859 const int totvert = SCULPT_vertex_count_get(ob);
1860
1861 face_set::create_face_sets_mesh(ob);
1862
1863 /* Face Sets are always stored as they are needed for snapping. */
1864 expand_cache.initial_face_sets = face_set::duplicate_face_sets(mesh);
1865 expand_cache.original_face_sets = face_set::duplicate_face_sets(mesh);
1866
1867 if (expand_cache.target == TargetType::Mask) {
1868 expand_cache.original_mask = mask::duplicate_mask(ob);
1869 }
1870
1871 if (expand_cache.target == TargetType::Colors) {
1872 const Mesh &mesh = *static_cast<const Mesh *>(ob.data);
1873 const OffsetIndices<int> faces = mesh.faces();
1874 const Span<int> corner_verts = mesh.corner_verts();
1875 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
1876 const bke::GAttributeReader color_attribute = color::active_color_attribute(mesh);
1877 const GVArraySpan colors = *color_attribute;
1878
1879 expand_cache.original_colors = Array<float4>(totvert);
1880 for (int i = 0; i < totvert; i++) {
1881 expand_cache.original_colors[i] = color::color_vert_get(
1882 faces, corner_verts, vert_to_face_map, colors, color_attribute.domain, i);
1883 }
1884 }
1885}
1886
1890static void face_sets_restore(Object &object, Cache &expand_cache)
1891{
1892 Mesh &mesh = *static_cast<Mesh *>(object.data);
1893 const OffsetIndices<int> faces = mesh.faces();
1894 const Span<int> corner_verts = mesh.corner_verts();
1895 bke::SpanAttributeWriter<int> face_sets = face_set::ensure_face_sets_mesh(mesh);
1896 for (const int i : faces.index_range()) {
1897 if (expand_cache.original_face_sets[i] <= 0) {
1898 /* Do not modify hidden Face Sets, even when restoring the IDs state. */
1899 continue;
1900 }
1901 if (!is_face_in_active_component(object, faces, corner_verts, expand_cache, i)) {
1902 continue;
1903 }
1904 face_sets.span[i] = expand_cache.initial_face_sets[i];
1905 }
1906 face_sets.finish();
1907}
1908
1909static void update_for_vert(bContext *C, Object &ob, const std::optional<int> vertex)
1910{
1911 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
1912 SculptSession &ss = *ob.sculpt;
1913 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(ob);
1914 Cache &expand_cache = *ss.expand_cache;
1915
1916 /* Update the active factor in the cache. */
1917 if (!vertex) {
1918 /* This means that the cursor is not over the mesh, so a valid active falloff can't be
1919 * determined. In this situations, don't evaluate enabled states and default all vertices in
1920 * connected components to enabled. */
1921 expand_cache.active_falloff = expand_cache.max_vert_falloff;
1922 expand_cache.all_enabled = true;
1923 }
1924 else {
1925 expand_cache.active_falloff = expand_cache.vert_falloff[*vertex];
1926 expand_cache.all_enabled = false;
1927 }
1928
1929 if (expand_cache.target == TargetType::FaceSets) {
1930 /* Face sets needs to be restored their initial state on each iteration as the overwrite
1931 * existing data. */
1932 face_sets_restore(ob, expand_cache);
1933 }
1934
1935 const IndexMask &node_mask = expand_cache.node_mask;
1936
1937 const BitVector<> enabled_verts = enabled_state_to_bitmap(depsgraph, ob, expand_cache);
1938
1939 switch (expand_cache.target) {
1940 case TargetType::Mask: {
1941 switch (pbvh.type()) {
1942 case bke::pbvh::Type::Mesh: {
1943 const Span<float3> positions = bke::pbvh::vert_positions_eval(depsgraph, ob);
1944 mask::update_mask_mesh(
1945 depsgraph, ob, node_mask, [&](const MutableSpan<float> mask, const Span<int> verts) {
1946 calc_new_mask_mesh(ss, positions, enabled_verts, verts, mask);
1947 });
1948 break;
1949 }
1950 case bke::pbvh::Type::Grids: {
1951 Array<bool> node_changed(node_mask.min_array_size(), false);
1952
1954 node_mask.foreach_index(GrainSize(1), [&](const int i) {
1955 node_changed[i] = update_mask_grids(ss, enabled_verts, nodes[i], *ss.subdiv_ccg);
1956 });
1957
1958 IndexMaskMemory memory;
1959 pbvh.tag_masks_changed(IndexMask::from_bools(node_changed, memory));
1960 break;
1961 }
1962 case bke::pbvh::Type::BMesh: {
1963 const int mask_offset = CustomData_get_offset_named(
1964 &ss.bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
1966
1967 Array<bool> node_changed(node_mask.min_array_size(), false);
1968 node_mask.foreach_index(GrainSize(1), [&](const int i) {
1969 node_changed[i] = update_mask_bmesh(ss, enabled_verts, mask_offset, &nodes[i]);
1970 });
1971
1972 IndexMaskMemory memory;
1973 pbvh.tag_masks_changed(IndexMask::from_bools(node_changed, memory));
1974 break;
1975 }
1976 }
1977 flush_update_step(C, UpdateType::Mask);
1978 break;
1979 }
1980 case TargetType::FaceSets:
1981 face_sets_update(ob, expand_cache);
1982 flush_update_step(C, UpdateType::FaceSet);
1983 break;
1984 case TargetType::Colors: {
1985 Mesh &mesh = *static_cast<Mesh *>(ob.data);
1986 const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, ob);
1987 const OffsetIndices<int> faces = mesh.faces();
1988 const Span<int> corner_verts = mesh.corner_verts();
1989 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
1990 const bke::AttributeAccessor attributes = mesh.attributes();
1991 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
1992 const VArraySpan mask = *attributes.lookup<float>(".sculpt_mask", bke::AttrDomain::Point);
1993 bke::GSpanAttributeWriter color_attribute = color::active_color_attribute_for_write(mesh);
1994
1995 Array<bool> node_changed(node_mask.min_array_size(), false);
1996
1998 node_mask.foreach_index(GrainSize(1), [&](const int i) {
1999 node_changed[i] = colors_update_task(depsgraph,
2000 ob,
2001 vert_positions,
2002 faces,
2003 corner_verts,
2004 vert_to_face_map,
2005 hide_vert,
2006 mask,
2007 &nodes[i],
2008 color_attribute);
2009 });
2010
2011 IndexMaskMemory memory;
2012 pbvh.tag_attribute_changed(IndexMask::from_bools(node_changed, memory),
2013 mesh.active_color_attribute);
2014
2015 color_attribute.finish();
2016 flush_update_step(C, UpdateType::Color);
2017 break;
2018 }
2019 }
2020}
2021
2026static std::optional<int> target_vert_update_and_get(bContext *C, Object &ob, const float mval[2])
2027{
2028 SculptSession &ss = *ob.sculpt;
2030 if (SCULPT_cursor_geometry_info_update(C, &sgi, mval, false)) {
2031 return ss.active_vert_index();
2032 }
2033 return std::nullopt;
2034}
2035
2040static void reposition_pivot(bContext *C, Object &ob, Cache &expand_cache)
2041{
2042 SculptSession &ss = *ob.sculpt;
2043 const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
2044 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
2045
2046 const bool initial_invert_state = expand_cache.invert;
2047 expand_cache.invert = false;
2048 const BitVector<> enabled_verts = enabled_state_to_bitmap(depsgraph, ob, expand_cache);
2049
2050 /* For boundary topology, position the pivot using only the boundary of the enabled vertices,
2051 * without taking mesh boundary into account. This allows to create deformations like bending the
2052 * mesh from the boundary of the mask that was just created. */
2053 const float use_mesh_boundary = expand_cache.falloff_type != FalloffType::BoundaryTopology;
2054
2055 IndexMaskMemory memory;
2056 const IndexMask boundary_verts = boundary_from_enabled(
2057 ob, enabled_verts, use_mesh_boundary, memory);
2058
2059 /* Ignore invert state, as this is the expected behavior in most cases and mask are created in
2060 * inverted state by default. */
2061 expand_cache.invert = initial_invert_state;
2062
2063 double3 average(0);
2064 int total = 0;
2065 switch (bke::object::pbvh_get(ob)->type()) {
2066 case bke::pbvh::Type::Mesh: {
2067 const Span<float3> positions = bke::pbvh::vert_positions_eval(depsgraph, ob);
2068 const float3 expand_init_co = positions[expand_cache.initial_active_vert];
2069 boundary_verts.foreach_index([&](const int vert) {
2070 if (!is_vert_in_active_component(ss, expand_cache, vert)) {
2071 return;
2072 }
2073 const float3 &position = positions[vert];
2074 if (!SCULPT_check_vertex_pivot_symmetry(position, expand_init_co, symm)) {
2075 return;
2076 }
2077 average += double3(position);
2078 total++;
2079 });
2080 break;
2081 }
2082 case bke::pbvh::Type::Grids: {
2083 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
2084 const Span<float3> positions = subdiv_ccg.positions;
2085 const float3 expand_init_co = positions[expand_cache.initial_active_vert];
2086 boundary_verts.foreach_index([&](const int vert) {
2087 if (!is_vert_in_active_component(ss, expand_cache, vert)) {
2088 return;
2089 }
2090 const float3 position = positions[vert];
2091 if (!SCULPT_check_vertex_pivot_symmetry(position, expand_init_co, symm)) {
2092 return;
2093 }
2094 average += double3(position);
2095 total++;
2096 });
2097 break;
2098 }
2099 case bke::pbvh::Type::BMesh: {
2100 BMesh &bm = *ss.bm;
2101 const float3 expand_init_co = BM_vert_at_index(&bm, expand_cache.initial_active_vert)->co;
2102 boundary_verts.foreach_index([&](const int vert) {
2103 if (!is_vert_in_active_component(ss, expand_cache, vert)) {
2104 return;
2105 }
2106 const float3 position = BM_vert_at_index(&bm, vert)->co;
2107 if (!SCULPT_check_vertex_pivot_symmetry(position, expand_init_co, symm)) {
2108 return;
2109 }
2110 average += double3(position);
2111 total++;
2112 });
2113 break;
2114 }
2115 }
2116
2117 if (total > 0) {
2118 ss.pivot_pos = float3(average / total);
2119 }
2120
2122}
2123
2124static void finish(bContext *C)
2125{
2127 SculptSession &ss = *ob.sculpt;
2128 undo::push_end(ob);
2129
2130 switch (ss.expand_cache->target) {
2131 case TargetType::Mask:
2132 flush_update_done(C, ob, UpdateType::Mask);
2133 break;
2134 case TargetType::FaceSets:
2135 flush_update_done(C, ob, UpdateType::FaceSet);
2136 break;
2137 case TargetType::Colors:
2138 flush_update_done(C, ob, UpdateType::Color);
2139 break;
2140 }
2141
2143 ED_workspace_status_text(C, nullptr);
2144}
2145
2151 Object &ob,
2152 Cache &expand_cache,
2153 const int initial_vertex)
2154{
2155 SculptSession &ss = *ob.sculpt;
2156 for (int i = 0; i < EXPAND_SYMM_AREAS; i++) {
2158 }
2159
2161
2162 const Vector<int> symm_verts = find_symm_verts(depsgraph, ob, initial_vertex);
2163
2164 int valid_index = 0;
2165 for (char symm_it = 0; symm_it <= symm; symm_it++) {
2166 if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
2167 continue;
2168 }
2169 expand_cache.active_connected_islands[symm_it] = islands::vert_id_get(ss,
2170 symm_verts[valid_index]);
2171 valid_index++;
2172 }
2173}
2174
2180 Object &ob,
2181 Cache &expand_cache,
2182 const float mval[2])
2183{
2184 SculptSession &ss = *ob.sculpt;
2185 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
2186
2187 std::optional<int> initial_vert = target_vert_update_and_get(C, ob, mval);
2188 if (!initial_vert) {
2189 /* Cursor not over the mesh, for creating valid initial falloffs, fallback to the last active
2190 * vertex in the sculpt session. */
2191 const int last_active_vert_index = ss.last_active_vert_index();
2192 /* It still may be the case that there is no last active vert in rare circumstances for
2193 * everyday usage.
2194 * (i.e. if the cursor has never been over the mesh at all. A solution to both this problem
2195 * and needing to store this data is to figure out which is the nearest vertex to the current
2196 * cursor position */
2197 if (last_active_vert_index == -1) {
2198 return false;
2199 }
2200 initial_vert = last_active_vert_index;
2201 }
2202
2204 expand_cache.initial_active_vert = *initial_vert;
2205 expand_cache.initial_active_face_set = face_set::active_face_set_get(ob);
2206
2207 if (expand_cache.next_face_set == SCULPT_FACE_SET_NONE) {
2208 /* Only set the next face set once, otherwise this ID will constantly update to a new one each
2209 * time this function is called for using a new initial vertex from a different cursor
2210 * position. */
2211 if (expand_cache.modify_active_face_set) {
2212 expand_cache.next_face_set = face_set::active_face_set_get(ob);
2213 }
2214 else {
2215 expand_cache.next_face_set = face_set::find_next_available_id(ob);
2216 }
2217 }
2218
2219 /* The new mouse position can be over a different connected component, so this needs to be
2220 * updated. */
2221 find_active_connected_components_from_vert(depsgraph, ob, expand_cache, *initial_vert);
2222 return true;
2223}
2224
2230 Object &ob,
2231 const wmEvent *event,
2232 Cache &expand_cache)
2233{
2234 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
2235 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
2236 float move_disp[2];
2237 sub_v2_v2v2(move_disp, mval_fl, expand_cache.initial_mouse_move);
2238
2239 float new_mval[2];
2240 add_v2_v2v2(new_mval, move_disp, expand_cache.original_mouse_move);
2241
2242 set_initial_components_for_mouse(C, ob, expand_cache, new_mval);
2244 expand_cache,
2245 ob,
2246 expand_cache.initial_active_vert,
2247 expand_cache.move_preview_falloff_type);
2248}
2249
2254{
2255 SculptSession &ss = *ob.sculpt;
2256 islands::ensure_cache(ob);
2258 boundary::ensure_boundary_info(ob);
2259 if (!ss.tex_pool) {
2261 }
2262}
2263
2267static int active_face_set_id_get(Object &object, Cache &expand_cache)
2268{
2269 SculptSession &ss = *object.sculpt;
2270 switch (bke::object::pbvh_get(object)->type()) {
2271 case bke::pbvh::Type::Mesh:
2272 if (!ss.active_face_index) {
2273 return SCULPT_FACE_SET_NONE;
2274 }
2275 return expand_cache.original_face_sets[*ss.active_face_index];
2276 case bke::pbvh::Type::Grids: {
2277 if (!ss.active_grid_index) {
2278 return SCULPT_FACE_SET_NONE;
2279 }
2280 const int face_index = BKE_subdiv_ccg_grid_to_face_index(*ss.subdiv_ccg,
2281 *ss.active_grid_index);
2282 return expand_cache.original_face_sets[face_index];
2283 }
2284 case bke::pbvh::Type::BMesh: {
2285 /* Dyntopo does not support Face Set functionality. */
2286 BLI_assert(false);
2287 }
2288 }
2289 return SCULPT_FACE_SET_NONE;
2290}
2291
2292static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event)
2293{
2295 SculptSession &ss = *ob.sculpt;
2296
2297 /* Skips INBETWEEN_MOUSEMOVE events and other events that may cause unnecessary updates. */
2298 if (!ELEM(event->type, MOUSEMOVE, EVT_MODAL_MAP)) {
2300 }
2301
2302 /* Update SculptSession data. */
2303 Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
2306
2307 /* Update and get the active vertex (and face) from the cursor. */
2308 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
2309 const std::optional<int> target_expand_vertex = target_vert_update_and_get(C, ob, mval_fl);
2310
2311 /* Handle the modal keymap state changes. */
2312 Cache &expand_cache = *ss.expand_cache;
2313 if (event->type == EVT_MODAL_MAP) {
2314 switch (event->val) {
2316 sculpt_expand_cancel(C, op);
2317 return OPERATOR_FINISHED;
2318 }
2320 expand_cache.invert = !expand_cache.invert;
2321 break;
2322 }
2324 expand_cache.preserve = !expand_cache.preserve;
2325 break;
2326 }
2328 expand_cache.falloff_gradient = !expand_cache.falloff_gradient;
2329 break;
2330 }
2332 expand_cache.brush_gradient = !expand_cache.brush_gradient;
2333 if (expand_cache.brush_gradient) {
2334 expand_cache.falloff_gradient = true;
2335 }
2336 break;
2337 }
2339 if (expand_cache.snap) {
2340 expand_cache.snap = false;
2341 if (expand_cache.snap_enabled_face_sets) {
2342 expand_cache.snap_enabled_face_sets.reset();
2343 }
2344 }
2345 else {
2346 expand_cache.snap = true;
2347 expand_cache.snap_enabled_face_sets = std::make_unique<Set<int>>();
2348 snap_init_from_enabled(*depsgraph, ob, expand_cache);
2349 }
2350 break;
2351 }
2353 if (expand_cache.move) {
2354 expand_cache.move = false;
2356 expand_cache,
2357 ob,
2358 expand_cache.initial_active_vert,
2359 expand_cache.move_original_falloff_type);
2360 break;
2361 }
2362 expand_cache.move = true;
2363 expand_cache.move_original_falloff_type = expand_cache.falloff_type;
2364 copy_v2_v2(expand_cache.initial_mouse_move, mval_fl);
2365 copy_v2_v2(expand_cache.original_mouse_move, expand_cache.initial_mouse);
2366 if (expand_cache.falloff_type == FalloffType::Geodesic &&
2368 {
2369 /* Set to spherical falloff for preview in high poly meshes as it is the fastest one.
2370 * In most cases it should match closely the preview from geodesic. */
2371 expand_cache.move_preview_falloff_type = FalloffType::Sphere;
2372 }
2373 else {
2374 expand_cache.move_preview_falloff_type = expand_cache.falloff_type;
2375 }
2376 break;
2377 }
2379 resursion_step_add(*depsgraph, ob, expand_cache, RecursionType::Geodesic);
2380 break;
2381 }
2383 resursion_step_add(*depsgraph, ob, expand_cache, RecursionType::Topology);
2384 break;
2385 }
2387 update_for_vert(C, ob, target_expand_vertex);
2388
2389 if (expand_cache.reposition_pivot) {
2390 reposition_pivot(C, ob, expand_cache);
2391 }
2392
2393 finish(C);
2394 return OPERATOR_FINISHED;
2395 }
2397 check_topology_islands(ob, FalloffType::Geodesic);
2398
2400 *depsgraph, expand_cache, ob, expand_cache.initial_active_vert, FalloffType::Geodesic);
2401 break;
2402 }
2404 check_topology_islands(ob, FalloffType::Topology);
2405
2407 *depsgraph, expand_cache, ob, expand_cache.initial_active_vert, FalloffType::Topology);
2408 break;
2409 }
2411 check_topology_islands(ob, FalloffType::TopologyNormals);
2412
2414 expand_cache,
2415 ob,
2416 expand_cache.initial_active_vert,
2417 FalloffType::TopologyNormals);
2418 break;
2419 }
2421 expand_cache.check_islands = false;
2423 *depsgraph, expand_cache, ob, expand_cache.initial_active_vert, FalloffType::Sphere);
2424 break;
2425 }
2427 expand_cache.loop_count += 1;
2428 break;
2429 }
2431 expand_cache.loop_count -= 1;
2432 expand_cache.loop_count = max_ii(expand_cache.loop_count, 1);
2433 break;
2434 }
2436 if (expand_cache.texture_distortion_strength == 0.0f) {
2437 const MTex *mask_tex = BKE_brush_mask_texture_get(expand_cache.brush, OB_MODE_SCULPT);
2438 if (mask_tex->tex == nullptr) {
2439 BKE_report(op->reports,
2441 "Active brush does not contain any texture to distort the expand boundary");
2442 break;
2443 }
2444 if (mask_tex->brush_map_mode != MTEX_MAP_MODE_3D) {
2445 BKE_report(op->reports,
2447 "Texture mapping not set to 3D, results may be unpredictable");
2448 }
2449 }
2451 break;
2452 }
2456 0.0f);
2457 break;
2458 }
2459 }
2460 }
2461
2462 /* Handle expand origin movement if enabled. */
2463 if (expand_cache.move) {
2464 move_propagation_origin(C, ob, event, expand_cache);
2465 }
2466
2467 /* Add new Face Sets IDs to the snapping set if enabled. */
2468 if (expand_cache.snap) {
2469 const int active_face_set_id = active_face_set_id_get(ob, expand_cache);
2470 /* The key may exist, in that case this does nothing. */
2471 expand_cache.snap_enabled_face_sets->add(active_face_set_id);
2472 }
2473
2474 /* Update the sculpt data with the current state of the #Cache. */
2475 update_for_vert(C, ob, target_expand_vertex);
2476
2478}
2479
2487 int *r_face_sets, Object &object, Cache &expand_cache, Mesh *mesh, const int delete_id)
2488{
2489 const GroupedSpan<int> vert_to_face_map = mesh->vert_to_face_map();
2490 const OffsetIndices faces = mesh->faces();
2491 const Span<int> corner_verts = mesh->corner_verts();
2492
2493 /* Check that all the face sets IDs in the mesh are not equal to `delete_id`
2494 * before attempting to delete it. */
2495 bool all_same_id = true;
2496 for (const int i : faces.index_range()) {
2497 if (!is_face_in_active_component(object, faces, corner_verts, expand_cache, i)) {
2498 continue;
2499 }
2500 if (r_face_sets[i] != delete_id) {
2501 all_same_id = false;
2502 break;
2503 }
2504 }
2505 if (all_same_id) {
2506 return;
2507 }
2508
2509 BLI_LINKSTACK_DECLARE(queue, void *);
2510 BLI_LINKSTACK_DECLARE(queue_next, void *);
2511
2512 BLI_LINKSTACK_INIT(queue);
2513 BLI_LINKSTACK_INIT(queue_next);
2514
2515 for (const int i : faces.index_range()) {
2516 if (r_face_sets[i] == delete_id) {
2518 }
2519 }
2520
2521 while (BLI_LINKSTACK_SIZE(queue)) {
2522 bool any_updated = false;
2523 while (BLI_LINKSTACK_SIZE(queue)) {
2524 const int f_index = POINTER_AS_INT(BLI_LINKSTACK_POP(queue));
2525 int other_id = delete_id;
2526 for (const int vert : corner_verts.slice(faces[f_index])) {
2527 for (const int neighbor_face_index : vert_to_face_map[vert]) {
2528 if (expand_cache.original_face_sets[neighbor_face_index] <= 0) {
2529 /* Skip picking IDs from hidden Face Sets. */
2530 continue;
2531 }
2532 if (r_face_sets[neighbor_face_index] != delete_id) {
2533 other_id = r_face_sets[neighbor_face_index];
2534 }
2535 }
2536 }
2537
2538 if (other_id != delete_id) {
2539 any_updated = true;
2540 r_face_sets[f_index] = other_id;
2541 }
2542 else {
2543 BLI_LINKSTACK_PUSH(queue_next, POINTER_FROM_INT(f_index));
2544 }
2545 }
2546 if (!any_updated) {
2547 /* No Face Sets where updated in this iteration, which means that no more content to keep
2548 * filling the faces of the deleted Face Set was found. Break to avoid entering an infinite
2549 * loop trying to search for those faces again. */
2550 break;
2551 }
2552
2553 BLI_LINKSTACK_SWAP(queue, queue_next);
2554 }
2555
2556 BLI_LINKSTACK_FREE(queue);
2557 BLI_LINKSTACK_FREE(queue_next);
2558}
2559
2560static void cache_initial_config_set(bContext *C, wmOperator *op, Cache &expand_cache)
2561{
2562 expand_cache.normal_falloff_blur_steps = RNA_int_get(op->ptr, "normal_falloff_smooth");
2563 expand_cache.invert = RNA_boolean_get(op->ptr, "invert");
2564 expand_cache.preserve = RNA_boolean_get(op->ptr, "use_mask_preserve");
2565 expand_cache.auto_mask = RNA_boolean_get(op->ptr, "use_auto_mask");
2566 expand_cache.falloff_gradient = RNA_boolean_get(op->ptr, "use_falloff_gradient");
2567 expand_cache.target = TargetType(RNA_enum_get(op->ptr, "target"));
2568 expand_cache.modify_active_face_set = RNA_boolean_get(op->ptr, "use_modify_active");
2569 expand_cache.reposition_pivot = RNA_boolean_get(op->ptr, "use_reposition_pivot");
2570 expand_cache.max_geodesic_move_preview = RNA_int_get(op->ptr, "max_geodesic_move_preview");
2571
2572 /* These can be exposed in RNA if needed. */
2573 expand_cache.loop_count = 1;
2574 expand_cache.brush_gradient = false;
2575
2576 /* Texture and color data from the active Brush. */
2577 Scene &scene = *CTX_data_scene(C);
2578 const Paint *paint = BKE_paint_get_active_from_context(C);
2579 const Sculpt &sd = *CTX_data_tool_settings(C)->sculpt;
2580 expand_cache.brush = BKE_paint_brush_for_read(&sd.paint);
2581 BKE_curvemapping_init(expand_cache.brush->curve);
2582 copy_v4_fl(expand_cache.fill_color, 1.0f);
2583 copy_v3_v3(expand_cache.fill_color, BKE_brush_color_get(&scene, paint, expand_cache.brush));
2585
2586 expand_cache.scene = CTX_data_scene(C);
2587 expand_cache.texture_distortion_strength = 0.0f;
2588 expand_cache.blend_mode = expand_cache.brush->blend;
2589}
2590
2594static void undo_push(const Depsgraph &depsgraph, Object &ob, Cache &expand_cache)
2595{
2596 IndexMaskMemory memory;
2597 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(*bke::object::pbvh_get(ob), memory);
2598
2599 switch (expand_cache.target) {
2600 case TargetType::Mask:
2601 undo::push_nodes(depsgraph, ob, node_mask, undo::Type::Mask);
2602 break;
2603 case TargetType::FaceSets:
2604 undo::push_nodes(depsgraph, ob, node_mask, undo::Type::FaceSet);
2605 break;
2606 case TargetType::Colors: {
2607 undo::push_nodes(depsgraph, ob, node_mask, undo::Type::Color);
2608 break;
2609 }
2610 }
2611}
2612
2613static bool any_nonzero_mask(const Object &object)
2614{
2615 const SculptSession &ss = *object.sculpt;
2616 switch (bke::object::pbvh_get(object)->type()) {
2617 case bke::pbvh::Type::Mesh: {
2618 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
2619 const bke::AttributeAccessor attributes = mesh.attributes();
2620 const VArraySpan mask = *attributes.lookup<float>(".sculpt_mask");
2621 if (mask.is_empty()) {
2622 return false;
2623 }
2624 return std::any_of(
2625 mask.begin(), mask.end(), [&](const float value) { return value > 0.0f; });
2626 }
2627 case bke::pbvh::Type::Grids: {
2628 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
2629 const Span<float> mask = subdiv_ccg.masks;
2630 if (mask.is_empty()) {
2631 return false;
2632 }
2633 return std::any_of(
2634 mask.begin(), mask.end(), [&](const float value) { return value > 0.0f; });
2635 }
2636 case bke::pbvh::Type::BMesh: {
2637 BMesh &bm = *ss.bm;
2638 const int offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask");
2639 if (offset == -1) {
2640 return false;
2641 }
2642 BMIter iter;
2643 BMVert *vert;
2644 BM_ITER_MESH (vert, &iter, &bm, BM_VERTS_OF_MESH) {
2645 if (BM_ELEM_CD_GET_FLOAT(vert, offset) > 0.0f) {
2646 return true;
2647 }
2648 }
2649 return false;
2650 }
2651 }
2652 return false;
2653}
2654
2655static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2656{
2657 const Scene &scene = *CTX_data_scene(C);
2660 SculptSession &ss = *ob.sculpt;
2661 Mesh *mesh = static_cast<Mesh *>(ob.data);
2662
2663 const View3D *v3d = CTX_wm_view3d(C);
2664 const Base *base = CTX_data_active_base(C);
2665 if (!BKE_base_is_visible(v3d, base)) {
2666 return OPERATOR_CANCELLED;
2667 }
2668
2669 /* Create and configure the Expand Cache. */
2670 ss.expand_cache = MEM_new<Cache>(__func__);
2672
2673 /* Update object. */
2674 const bool needs_colors = ss.expand_cache->target == TargetType::Colors;
2675
2676 if (needs_colors) {
2677 /* CTX_data_ensure_evaluated_depsgraph should be used at the end to include the updates of
2678 * earlier steps modifying the data. */
2681 }
2682
2683 if (ss.expand_cache->target == TargetType::Mask) {
2684 Scene &scene = *CTX_data_scene(C);
2687
2688 if (RNA_boolean_get(op->ptr, "use_auto_mask")) {
2689 if (any_nonzero_mask(ob)) {
2691 }
2692 }
2693 }
2694
2695 BKE_sculpt_update_object_for_edit(depsgraph, &ob, needs_colors);
2696
2697 /* Do nothing when the mesh has 0 vertices. */
2698 const int totvert = SCULPT_vertex_count_get(ob);
2699 if (totvert == 0) {
2701 return OPERATOR_CANCELLED;
2702 }
2703 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(ob);
2704
2705 /* Face Set operations are not supported in dyntopo. */
2706 if (ss.expand_cache->target == TargetType::FaceSets && pbvh.type() == bke::pbvh::Type::BMesh) {
2708 return OPERATOR_CANCELLED;
2709 }
2710
2712
2713 /* Set the initial element for expand from the event position. */
2714 const float mouse[2] = {float(event->mval[0]), float(event->mval[1])};
2715
2716 /* When getting the initial active vert, in cases where the cursor is not over the mesh and
2717 * the mesh type has changed, we cannot proceed with the expand operator, as there is no
2718 * sensible last active vertex when switching between backing implementations. */
2719 if (!set_initial_components_for_mouse(C, ob, *ss.expand_cache, mouse)) {
2721 return OPERATOR_CANCELLED;
2722 }
2723
2724 /* Initialize undo. */
2725 undo::push_begin(scene, ob, op);
2726 undo_push(*depsgraph, ob, *ss.expand_cache);
2727
2728 /* Cache bke::pbvh::Tree nodes. */
2729 ss.expand_cache->node_mask = bke::pbvh::all_leaf_nodes(pbvh, ss.expand_cache->node_mask_memory);
2730
2731 /* Store initial state. */
2733
2736 ob,
2737 *ss.expand_cache,
2738 mesh,
2740 }
2741
2742 const int initial_vert = ss.expand_cache->initial_active_vert;
2743
2744 /* Initialize the falloff. */
2745 FalloffType falloff_type = FalloffType(RNA_enum_get(op->ptr, "falloff_type"));
2746
2747 /* When starting from a boundary vertex, set the initial falloff to boundary. */
2748 switch (pbvh.type()) {
2749 case bke::pbvh::Type::Mesh: {
2750 const Mesh &mesh = *static_cast<const Mesh *>(ob.data);
2751 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
2752 const bke::AttributeAccessor attributes = mesh.attributes();
2753 const VArraySpan hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
2754 if (boundary::vert_is_boundary(
2755 vert_to_face_map, hide_poly, ss.vertex_info.boundary, initial_vert))
2756 {
2757 falloff_type = FalloffType::BoundaryTopology;
2758 }
2759 break;
2760 }
2761 case bke::pbvh::Type::Grids: {
2762 const Mesh &base_mesh = *static_cast<const Mesh *>(ob.data);
2763 const OffsetIndices<int> faces = base_mesh.faces();
2764 const Span<int> corner_verts = base_mesh.corner_verts();
2765 const bke::AttributeAccessor attributes = base_mesh.attributes();
2766 const VArraySpan face_sets = *attributes.lookup_or_default<int>(
2767 ".sculpt_face_set", bke::AttrDomain::Face, 0);
2768 const SubdivCCG &subdiv_ccg = *ob.sculpt->subdiv_ccg;
2769 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
2770 if (boundary::vert_is_boundary(faces,
2771 corner_verts,
2773 subdiv_ccg,
2774 SubdivCCGCoord::from_index(key, initial_vert)))
2775 {
2776 falloff_type = FalloffType::BoundaryTopology;
2777 }
2778 break;
2779 }
2780 case bke::pbvh::Type::BMesh: {
2781 BMesh &bm = *ob.sculpt->bm;
2783 if (boundary::vert_is_boundary(BM_vert_at_index(&bm, initial_vert))) {
2784 falloff_type = FalloffType::BoundaryTopology;
2785 }
2786 break;
2787 }
2788 }
2789
2791 *depsgraph, *ss.expand_cache, ob, initial_vert, falloff_type);
2792
2793 check_topology_islands(ob, falloff_type);
2794
2795 /* Initial mesh data update, resets all target data in the sculpt mesh. */
2796 update_for_vert(C, ob, initial_vert);
2797
2800}
2801
2803{
2804 static const EnumPropertyItem modal_items[] = {
2805 {SCULPT_EXPAND_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
2806 {SCULPT_EXPAND_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
2807 {SCULPT_EXPAND_MODAL_INVERT, "INVERT", 0, "Invert", ""},
2808 {SCULPT_EXPAND_MODAL_PRESERVE_TOGGLE, "PRESERVE", 0, "Toggle Preserve State", ""},
2809 {SCULPT_EXPAND_MODAL_GRADIENT_TOGGLE, "GRADIENT", 0, "Toggle Gradient", ""},
2811 "RECURSION_STEP_GEODESIC",
2812 0,
2813 "Geodesic recursion step",
2814 ""},
2816 "RECURSION_STEP_TOPOLOGY",
2817 0,
2818 "Topology recursion Step",
2819 ""},
2820 {SCULPT_EXPAND_MODAL_MOVE_TOGGLE, "MOVE_TOGGLE", 0, "Move Origin", ""},
2821 {SCULPT_EXPAND_MODAL_FALLOFF_GEODESIC, "FALLOFF_GEODESICS", 0, "Geodesic Falloff", ""},
2822 {SCULPT_EXPAND_MODAL_FALLOFF_TOPOLOGY, "FALLOFF_TOPOLOGY", 0, "Topology Falloff", ""},
2824 "FALLOFF_TOPOLOGY_DIAGONALS",
2825 0,
2826 "Diagonals Falloff",
2827 ""},
2828 {SCULPT_EXPAND_MODAL_FALLOFF_SPHERICAL, "FALLOFF_SPHERICAL", 0, "Spherical Falloff", ""},
2829 {SCULPT_EXPAND_MODAL_SNAP_TOGGLE, "SNAP_TOGGLE", 0, "Snap expand to Face Sets", ""},
2831 "LOOP_COUNT_INCREASE",
2832 0,
2833 "Loop Count Increase",
2834 ""},
2836 "LOOP_COUNT_DECREASE",
2837 0,
2838 "Loop Count Decrease",
2839 ""},
2841 "BRUSH_GRADIENT_TOGGLE",
2842 0,
2843 "Toggle Brush Gradient",
2844 ""},
2846 "TEXTURE_DISTORTION_INCREASE",
2847 0,
2848 "Texture Distortion Increase",
2849 ""},
2851 "TEXTURE_DISTORTION_DECREASE",
2852 0,
2853 "Texture Distortion Decrease",
2854 ""},
2855 {0, nullptr, 0, nullptr, nullptr},
2856 };
2857
2858 static const char *name = "Sculpt Expand Modal";
2859 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, name);
2860
2861 /* This function is called for each space-type, only needs to add map once. */
2862 if (keymap && keymap->modal_items) {
2863 return;
2864 }
2865
2866 keymap = WM_modalkeymap_ensure(keyconf, name, modal_items);
2867 WM_modalkeymap_assign(keymap, "SCULPT_OT_expand");
2868}
2869
2871{
2872 ot->name = "Expand";
2873 ot->idname = "SCULPT_OT_expand";
2874 ot->description = "Generic sculpt expand operator";
2875
2880
2882
2883 static EnumPropertyItem prop_sculpt_expand_falloff_type_items[] = {
2884 {int(FalloffType::Geodesic), "GEODESIC", 0, "Geodesic", ""},
2885 {int(FalloffType::Topology), "TOPOLOGY", 0, "Topology", ""},
2886 {int(FalloffType::TopologyNormals), "TOPOLOGY_DIAGONALS", 0, "Topology Diagonals", ""},
2887 {int(FalloffType::Normals), "NORMALS", 0, "Normals", ""},
2888 {int(FalloffType::Sphere), "SPHERICAL", 0, "Spherical", ""},
2889 {int(FalloffType::BoundaryTopology), "BOUNDARY_TOPOLOGY", 0, "Boundary Topology", ""},
2890 {int(FalloffType::BoundaryFaceSet), "BOUNDARY_FACE_SET", 0, "Boundary Face Set", ""},
2891 {int(FalloffType::ActiveFaceSet), "ACTIVE_FACE_SET", 0, "Active Face Set", ""},
2892 {0, nullptr, 0, nullptr, nullptr},
2893 };
2894
2895 static EnumPropertyItem prop_sculpt_expand_target_type_items[] = {
2896 {int(TargetType::Mask), "MASK", 0, "Mask", ""},
2897 {int(TargetType::FaceSets), "FACE_SETS", 0, "Face Sets", ""},
2898 {int(TargetType::Colors), "COLOR", 0, "Color", ""},
2899 {0, nullptr, 0, nullptr, nullptr},
2900 };
2901
2903 "target",
2904 prop_sculpt_expand_target_type_items,
2905 int(TargetType::Mask),
2906 "Data Target",
2907 "Data that is going to be modified in the expand operation");
2908
2910 "falloff_type",
2911 prop_sculpt_expand_falloff_type_items,
2912 int(FalloffType::Geodesic),
2913 "Falloff Type",
2914 "Initial falloff of the expand operation");
2915
2917 ot->srna, "invert", false, "Invert", "Invert the expand active elements");
2919 "use_mask_preserve",
2920 false,
2921 "Preserve Previous",
2922 "Preserve the previous state of the target data");
2924 "use_falloff_gradient",
2925 false,
2926 "Falloff Gradient",
2927 "Expand Using a linear falloff");
2928
2930 "use_modify_active",
2931 false,
2932 "Modify Active",
2933 "Modify the active Face Set instead of creating a new one");
2934
2936 ot->srna,
2937 "use_reposition_pivot",
2938 true,
2939 "Reposition Pivot",
2940 "Reposition the sculpt transform pivot to the boundary of the expand active area");
2941
2943 "max_geodesic_move_preview",
2944 10000,
2945 0,
2946 INT_MAX,
2947 "Max Vertex Count for Geodesic Move Preview",
2948 "Maximum number of vertices in the mesh for using geodesic falloff when "
2949 "moving the origin of expand. If the total number of vertices is greater "
2950 "than this value, the falloff will be set to spherical when moving",
2951 0,
2952 1000000);
2954 "use_auto_mask",
2955 false,
2956 "Auto Create",
2957 "Fill in mask if nothing is already masked");
2959 "normal_falloff_smooth",
2960 2,
2961 0,
2962 10,
2963 "Normal Smooth",
2964 "Blurring steps for normal falloff",
2965 0,
2966 10);
2967}
2968
2969} // namespace blender::ed::sculpt_paint::expand
const MTex * BKE_brush_mask_texture_get(const Brush *brush, const eObjectMode object_mode)
Definition brush.cc:762
float BKE_brush_curve_strength(eBrushCurvePreset preset, const CurveMapping *cumap, float distance, float brush_radius)
Definition brush.cc:1388
float BKE_brush_sample_tex_3d(const Scene *scene, const Brush *br, const MTex *mtex, const float point[3], float rgba[4], int thread, ImagePool *pool)
Definition brush.cc:778
const float * BKE_brush_color_get(const Scene *scene, const Paint *paint, const Brush *brush)
Definition brush.cc:1029
void BKE_curvemapping_init(CurveMapping *cumap)
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)
Main * CTX_data_main(const bContext *C)
ToolSettings * CTX_data_tool_settings(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
int CustomData_get_offset_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
ImagePool * BKE_image_pool_new(void)
bool BKE_base_is_visible(const View3D *v3d, const Base *base)
#define SCULPT_FACE_SET_NONE
Definition BKE_paint.hh:341
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:654
MultiresModifierData * BKE_sculpt_multires_active(const Scene *scene, Object *ob)
Definition paint.cc:2316
void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph, Object *ob_orig, bool is_paint_tool)
Definition paint.cc:2601
void BKE_sculpt_color_layer_create_if_needed(Object *object)
Definition paint.cc:2577
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:477
void BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, Main *bmain, Object *ob, MultiresModifierData *mmd)
Definition paint.cc:2610
A BVH for high poly meshes.
const blender::Set< BMVert *, 0 > & BKE_pbvh_bmesh_node_unique_verts(blender::bke::pbvh::BMeshNode *node)
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
int BKE_subdiv_ccg_grid_to_face_index(const SubdivCCG &subdiv_ccg, const int grid_index)
CCGKey BKE_subdiv_ccg_key_top_level(const SubdivCCG &subdiv_ccg)
void BKE_subdiv_ccg_foreach_visible_grid_vert(const CCGKey &key, const blender::BitGroupVector<> &grid_hidden, const int grid, const Fn &fn)
void BKE_subdiv_ccg_neighbor_coords_get(const SubdivCCG &subdiv_ccg, const SubdivCCGCoord &coord, bool include_duplicates, SubdivCCGNeighbors &r_neighbors)
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
MINLINE float max_ff(float a, float b)
MINLINE float clamp_f(float value, float min, float max)
MINLINE float min_ff(float a, float b)
MINLINE int max_ii(int a, int b)
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v4_v4fl(float r[4], const float a[4], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void copy_v4_fl(float r[4], float f)
#define POINTER_FROM_INT(i)
#define POINTER_AS_INT(i)
#define ELEM(...)
@ CD_PROP_FLOAT
@ CD_PROP_INT32
@ OB_MODE_SCULPT
Object is a sort of wrapper for general info.
ePaintSymmetryFlags
@ MTEX_MAP_MODE_3D
@ OPERATOR_RUNNING_MODAL
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:966
BLI_INLINE void IMB_colormanagement_srgb_to_scene_linear_v3(float scene_linear[3], const float srgb[3])
IMB_BlendMode
Definition IMB_imbuf.hh:186
void IMB_blend_color_float(float dst[4], const float src1[4], const float src2[4], IMB_BlendMode mode)
Definition rectop.cc:115
Read Guarded memory(de)allocation.
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:198
@ 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
#define BM_ELEM_CD_SET_FLOAT(ele, offset, f)
#define BM_ELEM_CD_GET_FLOAT(ele, offset)
@ BM_ELEM_HIDDEN
#define BM_elem_index_get(ele)
#define BM_elem_flag_test(ele, hflag)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_VERTS_OF_MESH
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
BLI_INLINE BMVert * BM_vert_at_index(BMesh *bm, const int index)
#define BM_FACE
#define BM_VERT
bool BM_vert_is_boundary(const BMVert *v)
AttributeSet attributes
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
const T * data() const
Definition BLI_array.hh:301
void fill(const T &value) const
Definition BLI_array.hh:261
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:388
bool is_empty() const
Definition BLI_array.hh:253
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:726
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr const T * end() const
Definition BLI_span.hh:225
constexpr const T * begin() const
Definition BLI_span.hh:221
constexpr bool is_empty() const
Definition BLI_span.hh:261
int64_t size() const
void append(const T &value)
IndexRange index_range() const
Span< T > as_span() const
void fill(const bool value)
bool remove(const StringRef attribute_id)
void tag_attribute_changed(const IndexMask &node_mask, StringRef attribute_name)
Definition pbvh.cc:593
Span< NodeT > nodes() const
void tag_face_sets_changed(const IndexMask &node_mask)
Definition pbvh.cc:579
void tag_masks_changed(const IndexMask &node_mask)
Definition pbvh.cc:586
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
static IndexMask from_bits(BitSpan bits, IndexMaskMemory &memory)
static IndexMask from_indices(Span< T > indices, IndexMaskMemory &memory)
void foreach_index(Fn &&fn) const
local_group_size(16, 16) .push_constant(Type b
const Depsgraph * depsgraph
#define powf(x, y)
#define fabsf(x)
draw_view in_light_buf[] float
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]
IndexRange range
bool enabled
ccl_device_inline float2 fmod(const float2 a, const float b)
ccl_device_inline float average(const float2 a)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
static constexpr int64_t BitsPerInt
void invert(BitSpanT &&data)
GroupedSpan< int > build_edge_to_face_map(OffsetIndices< int > faces, Span< int > corner_edges, int edges_num, Array< int > &r_offsets, Array< int > &r_indices)
GroupedSpan< int > build_vert_to_edge_map(Span< int2 > edges, int verts_num, Array< int > &r_offsets, Array< int > &r_indices)
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:2846
Span< float3 > vert_normals_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:2502
Span< float3 > vert_positions_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:2482
bool vert_is_boundary(const GroupedSpan< int > vert_to_face_map, const Span< bool > hide_poly, const BitSpan boundary, const int vert)
Definition sculpt.cc:454
static Array< float > geodesic_falloff_create(const Depsgraph &depsgraph, Object &ob, const IndexMask &initial_verts)
static void calc_falloff_from_vert_and_symmetry(const Depsgraph &depsgraph, Cache &expand_cache, Object &ob, const int vert, FalloffType falloff_type)
static bool any_nonzero_mask(const Object &object)
static void ensure_sculptsession_data(Object &ob)
static void vert_to_face_falloff_mesh(Mesh *mesh, Cache &expand_cache)
static Array< float > spherical_falloff_create(const Depsgraph &depsgraph, const Object &object, const int vert)
static void calc_topology_falloff_from_verts(Object &ob, const IndexMask &initial_verts, MutableSpan< float > distances)
static void geodesics_from_state_boundary(const Depsgraph &depsgraph, Object &ob, Cache &expand_cache, const BitSpan enabled_verts)
static void undo_push(const Depsgraph &depsgraph, Object &ob, Cache &expand_cache)
static void topology_from_state_boundary(Object &ob, Cache &expand_cache, const BitSpan enabled_verts)
static void move_propagation_origin(bContext *C, Object &ob, const wmEvent *event, Cache &expand_cache)
static void check_topology_islands(Object &ob, FalloffType falloff_type)
static BitVector enabled_state_to_bitmap(const Depsgraph &depsgraph, const Object &object, const Cache &expand_cache)
static float falloff_value_vertex_get(const SculptSession &ss, const Cache &expand_cache, const float3 &position, const int vert)
static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void original_state_store(Object &ob, Cache &expand_cache)
static void finish(bContext *C)
static void update_max_vert_falloff_value(const Object &object, Cache &expand_cache)
static void vert_to_face_falloff(Object &object, Mesh *mesh, Cache &expand_cache)
void modal_keymap(wmKeyConfig *keyconf)
static void init_from_face_set_boundary(const Depsgraph &depsgraph, Object &ob, Cache &expand_cache, const int active_face_set, const bool internal_falloff)
static std::optional< int > target_vert_update_and_get(bContext *C, Object &ob, const float mval[2])
static void restore_color_data(Object &ob, Cache &expand_cache)
static bool face_state_get(const Object &object, const OffsetIndices< int > faces, const Span< int > corner_verts, const Span< bool > hide_poly, const Span< int > face_sets, const Cache &expand_cache, const int face)
static bool is_vert_in_active_component(const SculptSession &ss, const Cache &expand_cache, const int vert)
static void expand_cache_free(SculptSession &ss)
static Array< float > normals_falloff_create(const Depsgraph &depsgraph, Object &ob, const int vert, const float edge_sensitivity, const int blur_steps)
static void delete_face_set_id(int *r_face_sets, Object &object, Cache &expand_cache, Mesh *mesh, const int delete_id)
static void reposition_pivot(bContext *C, Object &ob, Cache &expand_cache)
static void update_for_vert(bContext *C, Object &ob, const std::optional< int > vertex)
static void face_sets_restore(Object &object, Cache &expand_cache)
static void calc_new_mask_mesh(const SculptSession &ss, const Span< float3 > positions, const BitSpan enabled_verts, const Span< int > verts, const MutableSpan< float > mask)
static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void face_sets_update(Object &object, Cache &expand_cache)
static void find_active_connected_components_from_vert(const Depsgraph &depsgraph, Object &ob, Cache &expand_cache, const int initial_vertex)
void SCULPT_OT_expand(wmOperatorType *ot)
static void update_max_face_falloff_factor(const Object &object, Mesh &mesh, Cache &expand_cache)
static void vert_to_face_falloff_grids(SculptSession &ss, Mesh *mesh, Cache &expand_cache)
static bool is_face_in_active_component(const Object &object, const OffsetIndices< int > faces, const Span< int > corner_verts, const Cache &expand_cache, const int f)
static Array< float > diagonals_falloff_create(const Depsgraph &depsgraph, Object &ob, const int vert)
static void restore_original_state(bContext *C, Object &ob, Cache &expand_cache)
static Array< float > topology_falloff_create(const Depsgraph &depsgraph, Object &ob, const int initial_vert)
static void resursion_step_add(const Depsgraph &depsgraph, Object &ob, Cache &expand_cache, const RecursionType recursion_type)
static IndexMask boundary_from_enabled(Object &object, const BitSpan enabled_verts, const bool use_mesh_boundary, IndexMaskMemory &memory)
static void cache_initial_config_set(bContext *C, wmOperator *op, Cache &expand_cache)
static float gradient_value_get(const SculptSession &ss, const Cache &expand_cache, const float3 &position, const int vert)
static bool vert_falloff_is_enabled(const SculptSession &ss, const Cache &expand_cache, const float3 &position, const int vert)
static bool colors_update_task(const Depsgraph &depsgraph, Object &object, const Span< float3 > vert_positions, const OffsetIndices< int > faces, const Span< int > corner_verts, const GroupedSpan< int > vert_to_face_map, const Span< bool > hide_vert, const Span< float > mask, bke::pbvh::MeshNode *node, bke::GSpanAttributeWriter &color_attribute)
static void restore_face_set_data(Object &object, Cache &expand_cache)
static void sculpt_expand_cancel(bContext *C, wmOperator *)
static void snap_init_from_enabled(const Depsgraph &depsgraph, const Object &object, Cache &expand_cache)
static Array< float > boundary_topology_falloff_create(const Depsgraph &depsgraph, Object &ob, const int inititial_vert)
static int active_face_set_id_get(Object &object, Cache &expand_cache)
static float max_vert_falloff_get(const Cache &expand_cache)
static void write_mask_data(Object &object, const Span< float > mask)
static bool set_initial_components_for_mouse(bContext *C, Object &ob, Cache &expand_cache, const float mval[2])
int vert_face_set_get(const GroupedSpan< int > vert_to_face_map, const Span< int > face_sets, const int vert)
Definition sculpt.cc:232
Array< float > distances_create(const Span< float3 > vert_positions, const Span< int2 > edges, const OffsetIndices< int > faces, const Span< int > corner_verts, const GroupedSpan< int > vert_to_edge_map, const GroupedSpan< int > edge_to_face_map, const Span< bool > hide_poly, const Set< int > &initial_verts, const float limit_radius)
void ensure_cache(Object &object)
Definition sculpt.cc:6013
int vert_id_get(const SculptSession &ss, const int vert)
Definition sculpt.cc:5880
void blur_geometry_data_array(const Object &object, const int iterations, const MutableSpan< float > data)
std::optional< int > nearest_vert_calc_mesh(const bke::pbvh::Tree &pbvh, const Span< float3 > vert_positions, const Span< bool > hide_vert, const float3 &location, const float max_distance, const bool use_original)
Definition sculpt.cc:563
Vector< int > find_symm_verts_grids(const Object &object, const int original_vert, const float max_distance)
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 flush_update_step(bContext *C, UpdateType update_type)
Definition sculpt.cc:4960
Vector< int > find_symm_verts_mesh(const Depsgraph &depsgraph, const Object &object, const int original_vert, const float max_distance)
Span< BMVert * > vert_neighbors_get_bmesh(BMVert &vert, Vector< BMVert *, 64 > &r_neighbors)
Definition sculpt.cc:389
std::optional< BMVert * > nearest_vert_calc_bmesh(const bke::pbvh::Tree &pbvh, const float3 &location, const float max_distance, const bool use_original)
Definition sculpt.cc:664
std::optional< SubdivCCGCoord > nearest_vert_calc_grids(const bke::pbvh::Tree &pbvh, const SubdivCCG &subdiv_ccg, const float3 &location, const float max_distance, const bool use_original)
Definition sculpt.cc:610
Vector< int > find_symm_verts(const Depsgraph &depsgraph, const Object &object, const int original_vert, const float max_distance)
Span< int > vert_neighbors_get_mesh(const OffsetIndices< int > faces, const Span< int > corner_verts, const GroupedSpan< int > vert_to_face, const Span< bool > hide_poly, const int vert, Vector< int > &r_neighbors)
Definition sculpt.cc:431
Vector< int > find_symm_verts_bmesh(const Object &object, const int original_vert, const float max_distance)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
void parallel_for_aligned(const IndexRange range, const int64_t grain_size, const int64_t alignment, const Function &function)
Definition BLI_task.hh:141
CCL_NAMESPACE_BEGIN ccl_device float fade(float t)
Definition noise.h:14
int RNA_int_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
ePaintSymmetryFlags SCULPT_mesh_symmetry_xyz_get(const Object &object)
Definition sculpt.cc:186
bool SCULPT_cursor_geometry_info_update(bContext *C, SculptCursorGeometryInfo *out, const float mval[2], bool use_sampled_normal)
Definition sculpt.cc:4580
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
bool SCULPT_mode_poll(bContext *C)
Definition sculpt.cc:3560
int SCULPT_vertex_count_get(const Object &object)
Definition sculpt.cc:153
bool SCULPT_check_vertex_pivot_symmetry(const float vco[3], const float pco[3], const char symm)
Definition sculpt.cc:519
void SCULPT_tag_update_overlays(bContext *C)
Definition sculpt.cc:735
#define SCULPT_EXPAND_LOOP_THRESHOLD
#define SCULPT_EXPAND_TEXTURE_DISTORTION_STEP
#define EXPAND_ACTIVE_COMPONENT_NONE
#define SCULPT_EXPAND_NORMALS_FALLOFF_EDGE_SENSITIVITY
#define EXPAND_SYMM_AREAS
#define FLT_MAX
Definition stdcycles.h:14
BMLoop * l_first
struct BMVert * v
float co[3]
float no[3]
int totvert
CustomData vdata
CustomData pdata
BMFace ** ftable
struct CurveMapping * curve
short blend
int grid_area
Definition BKE_ccg.hh:35
char brush_map_mode
struct Tex * tex
struct SculptSession * sculpt
std::optional< int > active_grid_index
Definition BKE_paint.hh:433
std::optional< int > active_face_index
Definition BKE_paint.hh:432
SculptVertexInfo vertex_info
Definition BKE_paint.hh:458
SubdivCCG * subdiv_ccg
Definition BKE_paint.hh:405
blender::float3 pivot_pos
Definition BKE_paint.hh:462
int last_active_vert_index() const
Definition paint.cc:2207
ImagePool * tex_pool
Definition BKE_paint.hh:425
blender::GroupedSpan< int > edge_to_face_map
Definition BKE_paint.hh:392
blender::ed::sculpt_paint::expand::Cache * expand_cache
Definition BKE_paint.hh:429
int active_vert_index() const
Definition paint.cc:2190
blender::BitVector boundary
Definition BKE_paint.hh:351
static SubdivCCGCoord from_index(const CCGKey &key, int index)
blender::Vector< SubdivCCGCoord, 256 > coords
blender::Array< blender::float3 > normals
blender::Array< float > masks
blender::Array< blender::float3 > positions
std::unique_ptr< Set< int > > snap_enabled_face_sets
int active_connected_islands[EXPAND_SYMM_AREAS]
void execute(Object &object, FunctionRef< bool(BMVert *from_v, BMVert *to_v)> func)
void execute(Object &object, const SubdivCCG &subdiv_ccg, FunctionRef< bool(SubdivCCGCoord from_v, SubdivCCGCoord to_v, bool is_duplicate)> func)
void add_and_skip_initial(SubdivCCGCoord vertex, int index)
void execute(Object &object, GroupedSpan< int > vert_to_face_map, FunctionRef< bool(int from_v, int to_v)> func)
short val
Definition WM_types.hh:724
int mval[2]
Definition WM_types.hh:728
short type
Definition WM_types.hh:722
const void * modal_items
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
const char * description
Definition WM_types.hh:996
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct ReportList * reports
struct PointerRNA * ptr
float max
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ EVT_MODAL_MAP
@ MOUSEMOVE
wmOperatorType * ot
Definition wm_files.cc:4125
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:933
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:960