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