From b2ec10184c532e02421a2d75f918de2cbb0b0ec1 Mon Sep 17 00:00:00 2001 From: Sean Kim Date: Wed, 3 Jul 2024 20:46:36 +0200 Subject: [PATCH] Sculpt: Initial data oriented refactor for draw face sets brush Part of #118145. Performance after this change results in an 156% speedup. This is measured on the function `stroke_update_step` on a mesh with 24k verts across 20k instances of the function being timed. In absolute values, this represents a 0.09ms speedup, from 0.16ms to 0.25ms. For BMesh, the brush now takes mask values into consideration by averaging all of the vert mask values for a face. Co-authored-by: Hans Goudey Pull Request: https://projects.blender.org/blender/blender/pulls/123811 --- .../editors/sculpt_paint/CMakeLists.txt | 1 + .../sculpt_paint/brushes/draw_face_sets.cc | 457 ++++++++++++++++++ .../editors/sculpt_paint/brushes/types.hh | 1 + .../editors/sculpt_paint/mesh_brush_common.hh | 15 + source/blender/editors/sculpt_paint/sculpt.cc | 23 +- .../sculpt_paint/sculpt_automasking.cc | 29 ++ .../editors/sculpt_paint/sculpt_face_set.cc | 256 +--------- .../editors/sculpt_paint/sculpt_intern.hh | 2 +- 8 files changed, 539 insertions(+), 245 deletions(-) create mode 100644 source/blender/editors/sculpt_paint/brushes/draw_face_sets.cc diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 24441bebe4e..4199cc7049e 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -118,6 +118,7 @@ set(SRC brushes/clay_strips.cc brushes/clay_thumb.cc brushes/crease.cc + brushes/draw_face_sets.cc brushes/draw_vector_displacement.cc brushes/draw.cc brushes/fill.cc diff --git a/source/blender/editors/sculpt_paint/brushes/draw_face_sets.cc b/source/blender/editors/sculpt_paint/brushes/draw_face_sets.cc new file mode 100644 index 00000000000..478656dadad --- /dev/null +++ b/source/blender/editors/sculpt_paint/brushes/draw_face_sets.cc @@ -0,0 +1,457 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "editors/sculpt_paint/brushes/types.hh" +#include "editors/sculpt_paint/mesh_brush_common.hh" + +#include "DNA_brush_types.h" + +#include "BKE_mesh.hh" +#include "BKE_pbvh.hh" +#include "BKE_subdiv_ccg.hh" + +#include "BLI_array_utils.hh" +#include "BLI_enumerable_thread_specific.hh" +#include "BLI_math_base.hh" +#include "BLI_task.h" +#include "BLI_task.hh" + +#include "editors/sculpt_paint/sculpt_intern.hh" + +namespace blender::ed::sculpt_paint { +inline namespace draw_face_sets_cc { + +constexpr float FACE_SET_BRUSH_MIN_FADE = 0.05f; + +struct MeshLocalData { + Vector face_indices; + Vector positions; + Vector normals; + Vector factors; + Vector distances; +}; + +static void calc_face_centers(const OffsetIndices faces, + const Span corner_verts, + const Span vert_positions, + const Span face_indices, + const MutableSpan positions) +{ + BLI_assert(face_indices.size() == positions.size()); + + for (const int i : face_indices.index_range()) { + positions[i] = bke::mesh::face_center_calc(vert_positions, + corner_verts.slice(faces[face_indices[i]])); + } +} + +static void calc_face_normals(const OffsetIndices faces, + const Span corner_verts, + const Span vert_positions, + const Span face_indices, + const MutableSpan normals) +{ + BLI_assert(face_indices.size() == normals.size()); + + for (const int i : face_indices.index_range()) { + normals[i] = bke::mesh::face_normal_calc(vert_positions, + corner_verts.slice(faces[face_indices[i]])); + } +} + +BLI_NOINLINE static void fill_factor_from_hide_and_mask(const Mesh &mesh, + const Span face_indices, + const MutableSpan r_factors) +{ + BLI_assert(face_indices.size() == r_factors.size()); + + const OffsetIndices faces = mesh.faces(); + const Span corner_verts = mesh.corner_verts(); + + /* TODO: Avoid overhead of accessing attributes for every PBVH node. */ + const bke::AttributeAccessor attributes = mesh.attributes(); + if (const VArray mask = *attributes.lookup(".sculpt_mask", bke::AttrDomain::Point)) { + const VArraySpan span(mask); + for (const int i : face_indices.index_range()) { + const Span face_verts = corner_verts.slice(faces[face_indices[i]]); + const float inv_size = math::rcp(float(face_verts.size())); + float sum = 0.0f; + for (const int vert : face_verts) { + sum += span[vert]; + } + r_factors[i] = 1.0f - sum * inv_size; + } + } + else { + r_factors.fill(1.0f); + } + + if (const VArray hide_poly = *attributes.lookup(".hide_poly", bke::AttrDomain::Point)) { + const VArraySpan span(hide_poly); + for (const int i : face_indices.index_range()) { + if (span[face_indices[i]]) { + r_factors[i] = 0.0f; + } + } + } +} + +BLI_NOINLINE static void apply_face_set(const int face_set_id, + const Span face_indices, + const Span factors, + const MutableSpan face_sets) +{ + BLI_assert(face_indices.size() == factors.size()); + + for (const int i : face_indices.index_range()) { + if (factors[i] > FACE_SET_BRUSH_MIN_FADE) { + face_sets[face_indices[i]] = face_set_id; + } + } +} + +static void calc_faces(Object &object, + const Brush &brush, + const float strength, + const int face_set_id, + Span positions_eval, + const PBVHNode &node, + const Span face_indices, + MeshLocalData &tls, + const MutableSpan face_sets) +{ + SculptSession &ss = *object.sculpt; + const StrokeCache &cache = *ss.cache; + Mesh &mesh = *static_cast(object.data); + const OffsetIndices faces = mesh.faces(); + const Span corner_verts = mesh.corner_verts(); + + tls.positions.reinitialize(face_indices.size()); + const MutableSpan face_centers = tls.positions; + calc_face_centers(faces, corner_verts, positions_eval, face_indices, face_centers); + + tls.normals.reinitialize(face_indices.size()); + const MutableSpan face_normals = tls.normals; + calc_face_normals(faces, corner_verts, positions_eval, face_indices, face_normals); + + tls.factors.reinitialize(face_indices.size()); + const MutableSpan factors = tls.factors; + + fill_factor_from_hide_and_mask(mesh, face_indices, factors); + + filter_region_clip_factors(ss, face_centers, factors); + if (brush.flag & BRUSH_FRONTFACE) { + calc_front_face(cache.view_normal, face_normals, factors); + } + + tls.distances.reinitialize(face_indices.size()); + const MutableSpan distances = tls.distances; + calc_brush_distances(ss, face_centers, eBrushFalloffShape(brush.falloff_shape), distances); + filter_distances_with_radius(cache.radius, distances, factors); + apply_hardness_to_distances(cache, distances); + calc_brush_strength_factors(cache, brush, distances, factors); + + if (cache.automasking) { + const OffsetIndices faces = mesh.faces(); + const Span corner_verts = mesh.corner_verts(); + auto_mask::calc_face_factors( + object, faces, corner_verts, *cache.automasking, node, face_indices, factors); + } + + calc_brush_texture_factors(ss, brush, face_centers, factors); + scale_factors(factors, strength); + + apply_face_set(face_set_id, face_indices, factors, face_sets); +} + +static void do_draw_face_sets_brush_mesh(Object &object, + const Brush &brush, + const Span nodes) +{ + const SculptSession &ss = *object.sculpt; + const PBVH &pbvh = *ss.pbvh; + const Span positions_eval = BKE_pbvh_get_vert_positions(pbvh); + + Mesh &mesh = *static_cast(object.data); + const Span corner_tris = mesh.corner_tri_faces(); + + bke::SpanAttributeWriter attribute = face_set::ensure_face_sets_mesh(object); + MutableSpan face_sets = attribute.span; + + threading::EnumerableThreadSpecific all_tls; + threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { + MeshLocalData &tls = all_tls.local(); + for (const int i : range) { + const Span face_indices = bke::pbvh::node_face_indices_calc_mesh( + corner_tris, *nodes[i], tls.face_indices); + + undo::push_node(object, nodes[i], undo::Type::FaceSet); + + calc_faces(object, + brush, + ss.cache->bstrength, + ss.cache->paint_face_set, + positions_eval, + *nodes[i], + face_indices, + tls, + face_sets); + } + }); + + attribute.finish(); +} + +struct GridLocalData { + Vector face_indices; + Vector positions; + Vector factors; + Vector distances; +}; + +BLI_NOINLINE static void calc_face_indices_grids(const SubdivCCG &subdiv_ccg, + const Span grids, + const MutableSpan face_indices) +{ + const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg); + BLI_assert(grids.size() * key.grid_area == face_indices.size()); + + for (const int i : grids.index_range()) { + const int start = i * key.grid_area; + for (const int offset : IndexRange(key.grid_area)) { + face_indices[start + offset] = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, grids[i]); + } + } +} + +static void calc_grids(Object &object, + const Brush &brush, + const float strength, + const int face_set_id, + const PBVHNode &node, + GridLocalData &tls, + const MutableSpan face_sets) +{ + SculptSession &ss = *object.sculpt; + const StrokeCache &cache = *ss.cache; + SubdivCCG &subdiv_ccg = *ss.subdiv_ccg; + const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg); + + const Span grids = bke::pbvh::node_grid_indices(node); + const int grid_verts_num = grids.size() * key.grid_area; + + tls.positions.reinitialize(grid_verts_num); + MutableSpan positions = tls.positions; + gather_grids_positions(subdiv_ccg, grids, positions); + + tls.factors.reinitialize(grid_verts_num); + const MutableSpan factors = tls.factors; + blender::ed::sculpt_paint::fill_factor_from_hide_and_mask(subdiv_ccg, grids, factors); + filter_region_clip_factors(ss, positions, factors); + if (brush.flag & BRUSH_FRONTFACE) { + calc_front_face(cache.view_normal, subdiv_ccg, grids, factors); + } + + tls.distances.reinitialize(grid_verts_num); + const MutableSpan distances = tls.distances; + calc_brush_distances(ss, positions, eBrushFalloffShape(brush.falloff_shape), distances); + filter_distances_with_radius(cache.radius, distances, factors); + apply_hardness_to_distances(cache, distances); + calc_brush_strength_factors(cache, brush, distances, factors); + + if (cache.automasking) { + auto_mask::calc_grids_factors(object, *cache.automasking, node, grids, factors); + } + + calc_brush_texture_factors(ss, brush, positions, factors); + scale_factors(factors, strength); + + tls.face_indices.reinitialize(grid_verts_num); + MutableSpan face_indices = tls.face_indices; + + calc_face_indices_grids(subdiv_ccg, grids, face_indices); + apply_face_set(face_set_id, face_indices, factors, face_sets); +} + +static void do_draw_face_sets_brush_grids(Object &object, + const Brush &brush, + const Span nodes) +{ + SculptSession &ss = *object.sculpt; + + bke::SpanAttributeWriter attribute = face_set::ensure_face_sets_mesh(object); + MutableSpan face_sets = attribute.span; + + threading::EnumerableThreadSpecific all_tls; + threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { + GridLocalData &tls = all_tls.local(); + for (PBVHNode *node : nodes.slice(range)) { + for (const int i : range) { + undo::push_node(object, node, undo::Type::FaceSet); + + calc_grids(object, + brush, + ss.cache->bstrength, + ss.cache->paint_face_set, + *nodes[i], + tls, + face_sets); + } + } + }); + attribute.finish(); +} +struct BMeshLocalData { + Vector positions; + Vector factors; + Vector distances; +}; + +BLI_NOINLINE static void fill_factor_from_hide_and_mask(const BMesh &bm, + const Set &faces, + const MutableSpan r_factors) +{ + BLI_assert(faces.size() == r_factors.size()); + + /* TODO: Avoid overhead of accessing attributes for every PBVH node. */ + const int mask_offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask"); + int i = 0; + for (BMFace *f : faces) { + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + r_factors[i] = 0.0f; + continue; + } + if (mask_offset == -1) { + r_factors[i] = 1.0f; + continue; + } + + const BMLoop *l_iter = f->l_first = BM_FACE_FIRST_LOOP(f); + int total_verts = 0; + float sum; + do { + BMVert *vert = l_iter->v; + sum += BM_ELEM_CD_GET_FLOAT(vert, mask_offset); + total_verts++; + } while ((l_iter = l_iter->next) != f->l_first); + r_factors[i] = 1.0f - sum * math::rcp(float(total_verts)); + i++; + } +} + +static void calc_face_centers(const Set &faces, const MutableSpan centers) +{ + BLI_assert(faces.size() == centers.size()); + + int i = 0; + for (const BMFace *f : faces) { + float3 face_center; + BM_face_calc_center_median(f, face_center); + + centers[i] = face_center; + i++; + } +} + +BLI_NOINLINE static void apply_face_set(const int face_set_id, + const Set &faces, + const MutableSpan factors, + const int cd_offset) +{ + int i = 0; + for (BMFace *face : faces) { + if (factors[i] > FACE_SET_BRUSH_MIN_FADE) { + BM_ELEM_CD_SET_INT(face, cd_offset, face_set_id); + } + i++; + } +} + +static void calc_bmesh(Object &object, + const Brush &brush, + const float strength, + const int face_set_id, + PBVHNode &node, + BMeshLocalData &tls, + const int cd_offset) +{ + SculptSession &ss = *object.sculpt; + const StrokeCache &cache = *ss.cache; + + const Set &faces = BKE_pbvh_bmesh_node_faces(&node); + tls.positions.reinitialize(faces.size()); + const MutableSpan positions = tls.positions; + calc_face_centers(faces, positions); + + tls.factors.reinitialize(faces.size()); + const MutableSpan factors = tls.factors; + fill_factor_from_hide_and_mask(*ss.bm, faces, factors); + filter_region_clip_factors(ss, positions, factors); + if (brush.flag & BRUSH_FRONTFACE) { + calc_front_face(cache.view_normal, faces, factors); + } + + tls.distances.reinitialize(faces.size()); + const MutableSpan distances = tls.distances; + calc_brush_distances(ss, positions, eBrushFalloffShape(brush.falloff_shape), distances); + filter_distances_with_radius(cache.radius, distances, factors); + apply_hardness_to_distances(cache, distances); + calc_brush_strength_factors(cache, brush, distances, factors); + + /* Disable auto-masking code path which rely on an undo step to access original data. + * + * This is because the dynamic topology uses BMesh Log based undo system, which creates a + * single node for the undo step, and its type could be different for the needs of the + * brush undo and the original data access. + * + * For the brushes like Draw the ss.cache->automasking is set to nullptr at the first step + * of the brush, as there is an explicit check there for the brushes which support dynamic + * topology. Do it locally here for the Draw Face Set brush here, to mimic the behavior of + * the other brushes but without marking the brush as supporting dynamic topology. */ + auto_mask::node_begin(object, nullptr, node); + + calc_brush_texture_factors(ss, brush, positions, factors); + scale_factors(factors, strength); + + apply_face_set(face_set_id, faces, factors, cd_offset); +} + +static void do_draw_face_sets_brush_bmesh(Object &object, + const Brush &brush, + const Span nodes) +{ + SculptSession &ss = *object.sculpt; + const int cd_offset = face_set::ensure_face_sets_bmesh(object); + + threading::EnumerableThreadSpecific all_tls; + threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { + BMeshLocalData &tls = all_tls.local(); + for (const int i : range) { + undo::push_node(object, nodes[i], undo::Type::FaceSet); + calc_bmesh( + object, brush, ss.cache->bstrength, ss.cache->paint_face_set, *nodes[i], tls, cd_offset); + } + }); +} + +} // namespace draw_face_sets_cc + +void do_draw_face_sets_brush(const Sculpt &sd, Object &object, Span nodes) +{ + SculptSession &ss = *object.sculpt; + const Brush &brush = *BKE_paint_brush_for_read(&sd.paint); + + switch (BKE_pbvh_type(*ss.pbvh)) { + case PBVH_FACES: + do_draw_face_sets_brush_mesh(object, brush, nodes); + break; + case PBVH_GRIDS: + do_draw_face_sets_brush_grids(object, brush, nodes); + break; + case PBVH_BMESH: + do_draw_face_sets_brush_bmesh(object, brush, nodes); + break; + } +} +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/brushes/types.hh b/source/blender/editors/sculpt_paint/brushes/types.hh index 8b1925c3590..5a643c37da3 100644 --- a/source/blender/editors/sculpt_paint/brushes/types.hh +++ b/source/blender/editors/sculpt_paint/brushes/types.hh @@ -20,6 +20,7 @@ void do_crease_brush(const Scene &scene, const Sculpt &sd, Object &ob, Span nodes); void do_displacement_eraser_brush(const Sculpt &sd, Object &ob, Span nodes); void do_displacement_smear_brush(const Sculpt &sd, Object &ob, Span nodes); +void do_draw_face_sets_brush(const Sculpt &sd, Object &object, Span nodes); /** A simple normal-direction displacement. */ void do_draw_brush(const Sculpt &sd, Object &object, Span nodes); /** A simple normal-direction displacement based on image texture RGB/XYZ values. */ diff --git a/source/blender/editors/sculpt_paint/mesh_brush_common.hh b/source/blender/editors/sculpt_paint/mesh_brush_common.hh index a18fd26dcf1..86766743663 100644 --- a/source/blender/editors/sculpt_paint/mesh_brush_common.hh +++ b/source/blender/editors/sculpt_paint/mesh_brush_common.hh @@ -34,6 +34,7 @@ struct BMesh; struct BMVert; +struct BMFace; struct Brush; struct Mesh; struct Object; @@ -115,6 +116,9 @@ void calc_front_face(const float3 &view_normal, void calc_front_face(const float3 &view_normal, const Set &verts, const MutableSpan factors); +void calc_front_face(const float3 &view_normal, + const Set &faces, + const MutableSpan factors); /** * When the 3D view's clipping planes are enabled, brushes shouldn't have any effect on vertices @@ -217,6 +221,17 @@ void calc_vert_factors(const Object &object, const Set &verts, MutableSpan factors); +/** + * Calculate all auto-masking influence on each face. + */ +void calc_face_factors(const Object &object, + const OffsetIndices faces, + const Span corner_verts, + const Cache &cache, + const PBVHNode &node, + const Span face_indices, + const MutableSpan factors); + } // namespace auto_mask /** diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index dc81f9ab4ac..9f564186091 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -3893,7 +3893,12 @@ static void do_brush_action(const Scene &scene, cloth::do_cloth_brush(sd, ob, nodes); break; case SCULPT_TOOL_DRAW_FACE_SETS: - face_set::do_draw_face_sets_brush(sd, ob, nodes); + if (!ss.cache->alt_smooth) { + do_draw_face_sets_brush(sd, ob, nodes); + } + else { + face_set::do_relax_face_sets_brush(sd, ob, nodes); + } break; case SCULPT_TOOL_DISPLACEMENT_ERASER: do_displacement_eraser_brush(sd, ob, nodes); @@ -6859,6 +6864,20 @@ void calc_front_face(const float3 &view_normal, } } +void calc_front_face(const float3 &view_normal, + const Set &faces, + const MutableSpan factors) +{ + BLI_assert(faces.size() == factors.size()); + + int i = 0; + for (const BMFace *face : faces) { + const float dot = math::dot(view_normal, float3(face->no)); + factors[i] *= std::max(dot, 0.0f); + i++; + } +} + void filter_region_clip_factors(const SculptSession &ss, const Span positions, const Span verts, @@ -6892,6 +6911,8 @@ void filter_region_clip_factors(const SculptSession &ss, const Span positions, const MutableSpan factors) { + BLI_assert(positions.size() == factors.size()); + const RegionView3D *rv3d = ss.cache ? ss.cache->vc->rv3d : ss.rv3d; const View3D *v3d = ss.cache ? ss.cache->vc->v3d : ss.v3d; if (!RV3D_CLIPPING_ENABLED(v3d, rv3d)) { diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index f874078e03a..c4f40e01ed2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -11,6 +11,7 @@ #include "BLI_array.hh" #include "BLI_hash.h" #include "BLI_index_range.hh" +#include "BLI_math_base.hh" #include "BLI_math_base_safe.h" #include "BLI_math_vector.h" #include "BLI_math_vector_types.hh" @@ -613,6 +614,34 @@ void calc_vert_factors(const Object &object, } } +void calc_face_factors(const Object &object, + const OffsetIndices faces, + const Span corner_verts, + const Cache &cache, + const PBVHNode &node, + const Span face_indices, + const MutableSpan factors) +{ + SculptSession &ss = *object.sculpt; + + NodeData data = node_begin(object, &cache, node); + /* NOTE: We explicitly nullify data.orig_data here as we currently cannot go from mesh vert index + * to the undo node array index. The only brush this method is currently used for is the Draw + * Face Set brush, which never modifies the position of the vertices in a brush stroke. This + * needs to be implemented in the future if brushes that iterate over faces need original + * position and normal data. */ + data.orig_data = std::nullopt; + + for (const int i : face_indices.index_range()) { + const Span face_verts = corner_verts.slice(faces[face_indices[i]]); + float sum = 0.0f; + for (const int vert : face_verts) { + sum += factor_get(&cache, ss, BKE_pbvh_make_vref(vert), &data); + } + factors[i] *= sum * math::rcp(float(face_verts.size())); + } +} + void calc_grids_factors(const Object &object, const Cache &cache, const PBVHNode &node, diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 09277800c84..c5d019b2a77 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -211,222 +211,6 @@ Array duplicate_face_sets(const Mesh &mesh) /** \name Draw Face Sets Brush * \{ */ -constexpr float FACE_SET_BRUSH_MIN_FADE = 0.05f; - -static void do_draw_face_sets_brush_faces(Object &ob, - const Brush &brush, - const Span nodes) -{ - SculptSession &ss = *ob.sculpt; - const Span positions = SCULPT_mesh_deformed_positions_get(ss); - - Mesh &mesh = *static_cast(ob.data); - const OffsetIndices faces = mesh.faces(); - const Span corner_verts = mesh.corner_verts(); - const bke::AttributeAccessor attributes = mesh.attributes(); - const VArraySpan hide_poly = *attributes.lookup(".hide_poly", bke::AttrDomain::Face); - - bke::SpanAttributeWriter attribute = ensure_face_sets_mesh(ob); - MutableSpan face_sets = attribute.span; - - threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { - const float bstrength = ss.cache->bstrength; - const int thread_id = BLI_task_parallel_thread_id(nullptr); - for (PBVHNode *node : nodes.slice(range)) { - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, test, brush.falloff_shape); - - auto_mask::NodeData automask_data = auto_mask::node_begin( - ob, ss.cache->automasking.get(), *node); - - bool changed = false; - - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (*ss.pbvh, node, vd, PBVH_ITER_UNIQUE) { - auto_mask::node_update(automask_data, vd); - - for (const int face_i : ss.vert_to_face_map[vd.index]) { - const IndexRange face = faces[face_i]; - const float3 center = bke::mesh::face_center_calc(positions, corner_verts.slice(face)); - - if (!sculpt_brush_test_sq_fn(test, center)) { - continue; - } - if (!hide_poly.is_empty() && hide_poly[face_i]) { - continue; - } - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask, - vd.vertex, - thread_id, - &automask_data); - - if (fade > FACE_SET_BRUSH_MIN_FADE) { - face_sets[face_i] = ss.cache->paint_face_set; - changed = true; - } - } - } - BKE_pbvh_vertex_iter_end; - - if (changed) { - undo::push_node(ob, node, undo::Type::FaceSet); - } - } - }); - attribute.finish(); -} - -static void do_draw_face_sets_brush_grids(Object &ob, - const Brush &brush, - const Span nodes) -{ - SculptSession &ss = *ob.sculpt; - const float bstrength = ss.cache->bstrength; - SubdivCCG &subdiv_ccg = *ss.subdiv_ccg; - - bke::SpanAttributeWriter attribute = ensure_face_sets_mesh(ob); - MutableSpan face_sets = attribute.span; - - threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { - const int thread_id = BLI_task_parallel_thread_id(nullptr); - for (PBVHNode *node : nodes.slice(range)) { - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, test, brush.falloff_shape); - - auto_mask::NodeData automask_data = auto_mask::node_begin( - ob, ss.cache->automasking.get(), *node); - - bool changed = false; - - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (*ss.pbvh, node, vd, PBVH_ITER_UNIQUE) { - auto_mask::node_update(automask_data, vd); - - if (!sculpt_brush_test_sq_fn(test, vd.co)) { - continue; - } - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask, - vd.vertex, - thread_id, - &automask_data); - - if (fade > FACE_SET_BRUSH_MIN_FADE) { - const int face_index = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, - vd.grid_indices[vd.g]); - face_sets[face_index] = ss.cache->paint_face_set; - changed = true; - } - } - BKE_pbvh_vertex_iter_end; - - if (changed) { - undo::push_node(ob, node, undo::Type::FaceSet); - } - } - }); - attribute.finish(); -} - -static void do_draw_face_sets_brush_bmesh(Object &ob, - const Brush &brush, - const Span nodes) -{ - SculptSession &ss = *ob.sculpt; - const float bstrength = ss.cache->bstrength; - const int cd_offset = ensure_face_sets_bmesh(ob); - - threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { - const int thread_id = BLI_task_parallel_thread_id(nullptr); - for (PBVHNode *node : nodes.slice(range)) { - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, test, brush.falloff_shape); - - /* Disable auto-masking code path which rely on an undo step to access original data. - * - * This is because the dynamic topology uses BMesh Log based undo system, which creates a - * single node for the undo step, and its type could be different for the needs of the brush - * undo and the original data access. - * - * For the brushes like Draw the ss.cache->automasking is set to nullptr at the first step - * of the brush, as there is an explicit check there for the brushes which support dynamic - * topology. Do it locally here for the Draw Face Set brush here, to mimic the behavior of - * the other brushes but without marking the brush as supporting dynamic topology. */ - auto_mask::NodeData automask_data = auto_mask::node_begin(ob, nullptr, *node); - - bool changed = false; - - for (BMFace *f : BKE_pbvh_bmesh_node_faces(node)) { - if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - continue; - } - - float3 face_center; - BM_face_calc_center_median(f, face_center); - - const BMLoop *l_iter = f->l_first = BM_FACE_FIRST_LOOP(f); - do { - if (!sculpt_brush_test_sq_fn(test, l_iter->v->co)) { - continue; - } - - BMVert *vert = l_iter->v; - - /* There is no need to update the automasking data as it is disabled above. Additionally, - * there is no access to the PBVHVertexIter as iteration happens over faces. - * - * The full auto-masking support would be very good to be implemented here, so keeping - * the typical code flow for it here for the reference, and ease of looking at what needs - * to be done for such integration. - * - * auto_mask::node_update(automask_data, vd); */ - - const float fade = bstrength * - SCULPT_brush_strength_factor(ss, - brush, - face_center, - sqrtf(test.dist), - f->no, - f->no, - 0.0f, - BKE_pbvh_make_vref(intptr_t(vert)), - thread_id, - &automask_data); - - if (fade <= FACE_SET_BRUSH_MIN_FADE) { - continue; - } - - int &fset = *static_cast(POINTER_OFFSET(f->head.data, cd_offset)); - fset = ss.cache->paint_face_set; - changed = true; - break; - - } while ((l_iter = l_iter->next) != f->l_first); - } - - if (changed) { - undo::push_node(ob, node, undo::Type::FaceSet); - } - } - }); -} - static void do_relax_face_sets_brush_task(Object &ob, const Brush &brush, const float strength, @@ -483,38 +267,24 @@ static std::array iteration_strengths(const float strength, const int return {modified_strength, modified_strength, strength, strength}; } -void do_draw_face_sets_brush(const Sculpt &sd, Object &ob, Span nodes) +void do_relax_face_sets_brush(const Sculpt &sd, Object &ob, Span nodes) { - SculptSession &ss = *ob.sculpt; const Brush &brush = *BKE_paint_brush_for_read(&sd.paint); BKE_curvemapping_init(brush.curve); - if (ss.cache->alt_smooth) { - SCULPT_boundary_info_ensure(ob); - const SculptSession &ss = *ob.sculpt; - const std::array strengths = iteration_strengths(ss.cache->bstrength, - ss.cache->iteration_count); - for (const float strength : strengths) { - threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { - for (const int i : range) { - do_relax_face_sets_brush_task(ob, brush, strength, nodes[i]); - } - }); - } - } - else { - switch (BKE_pbvh_type(*ss.pbvh)) { - case PBVH_FACES: - do_draw_face_sets_brush_faces(ob, brush, nodes); - break; - case PBVH_GRIDS: - do_draw_face_sets_brush_grids(ob, brush, nodes); - break; - case PBVH_BMESH: - do_draw_face_sets_brush_bmesh(ob, brush, nodes); - break; - } + SCULPT_boundary_info_ensure(ob); + + const SculptSession &ss = *ob.sculpt; + const std::array strengths = iteration_strengths(ss.cache->bstrength, + ss.cache->iteration_count); + for (const float strength : strengths) { + + threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { + for (const int i : range) { + do_relax_face_sets_brush_task(ob, brush, strength, nodes[i]); + } + }); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 95f3546dc68..799ef0806ec 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1968,7 +1968,7 @@ void multiplane_scrape_preview_draw(uint gpuattr, namespace face_set { -void do_draw_face_sets_brush(const Sculpt &sd, Object &ob, Span nodes); +void do_relax_face_sets_brush(const Sculpt &sd, Object &ob, Span nodes); }