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