From 7ce067d73bafe558d2cf4b4d65fae5880a3146ff Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 15 Jul 2024 19:14:29 +0200 Subject: [PATCH] Sculpt: Data oriented refactor for enhance details brush Part of #118145. This brush first stores an array of translations for each vertex and then reuses those translations for the rest of the stroke. I added a few utilities to simplify storing the data for all vertices for the multires and BMesh implementations. Compared to the old code, computing the translations is skipped for completely hidden or masked nodes. Also we don't skip hidden neighbors since that gives better results. That wasn't as easy with the old API. Pull Request: https://projects.blender.org/blender/blender/pulls/124569 --- .../editors/sculpt_paint/CMakeLists.txt | 1 + .../sculpt_paint/brushes/enhance_details.cc | 380 ++++++++++++++++++ .../editors/sculpt_paint/brushes/types.hh | 1 + .../editors/sculpt_paint/mesh_brush_common.hh | 18 + source/blender/editors/sculpt_paint/sculpt.cc | 58 ++- .../editors/sculpt_paint/sculpt_intern.hh | 6 +- .../editors/sculpt_paint/sculpt_smooth.cc | 120 +++--- 7 files changed, 511 insertions(+), 73 deletions(-) create mode 100644 source/blender/editors/sculpt_paint/brushes/enhance_details.cc diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index a48234feeb0..55ad3df86f7 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -130,6 +130,7 @@ set(SRC brushes/draw_sharp.cc brushes/draw.cc brushes/elastic_deform.cc + brushes/enhance_details.cc brushes/fill.cc brushes/flatten.cc brushes/grab.cc diff --git a/source/blender/editors/sculpt_paint/brushes/enhance_details.cc b/source/blender/editors/sculpt_paint/brushes/enhance_details.cc new file mode 100644 index 00000000000..52222f587b2 --- /dev/null +++ b/source/blender/editors/sculpt_paint/brushes/enhance_details.cc @@ -0,0 +1,380 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "editors/sculpt_paint/brushes/types.hh" + +#include "DNA_brush_types.h" +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_key.hh" +#include "BKE_mesh.hh" +#include "BKE_paint.hh" +#include "BKE_pbvh.hh" +#include "BKE_subdiv_ccg.hh" + +#include "BLI_array.hh" +#include "BLI_array_utils.hh" +#include "BLI_enumerable_thread_specific.hh" +#include "BLI_math_vector.hh" +#include "BLI_task.hh" + +#include "editors/sculpt_paint/mesh_brush_common.hh" +#include "editors/sculpt_paint/sculpt_intern.hh" + +namespace blender::ed::sculpt_paint { + +inline namespace enhance_details_cc { + +struct LocalData { + Vector positions; + Vector new_positions; + Vector factors; + Vector distances; + Vector> vert_neighbors; + Vector translations; +}; + +static void calc_faces(const Sculpt &sd, + const Brush &brush, + const Span positions_eval, + const Span vert_normals, + const Span all_translations, + const float strength, + const PBVHNode &node, + Object &object, + LocalData &tls, + const MutableSpan positions_orig) +{ + SculptSession &ss = *object.sculpt; + const StrokeCache &cache = *ss.cache; + Mesh &mesh = *static_cast(object.data); + + const Span verts = bke::pbvh::node_unique_verts(node); + + tls.factors.reinitialize(verts.size()); + const MutableSpan factors = tls.factors; + fill_factor_from_hide_and_mask(mesh, verts, factors); + filter_region_clip_factors(ss, positions_eval, verts, factors); + if (brush.flag & BRUSH_FRONTFACE) { + calc_front_face(cache.view_normal, vert_normals, verts, factors); + } + + tls.distances.reinitialize(verts.size()); + const MutableSpan distances = tls.distances; + calc_brush_distances( + ss, positions_eval, verts, 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_vert_factors(object, *cache.automasking, node, verts, factors); + } + + calc_brush_texture_factors(ss, brush, positions_eval, verts, factors); + + scale_factors(factors, strength); + + tls.translations.reinitialize(verts.size()); + const MutableSpan translations = tls.translations; + array_utils::gather(all_translations, verts, translations); + scale_translations(translations, factors); + + write_translations(sd, object, positions_eval, verts, translations, positions_orig); +} + +static void calc_grids(const Sculpt &sd, + Object &object, + const Brush &brush, + const Span all_translations, + const float strength, + const PBVHNode &node, + LocalData &tls) +{ + 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); + const MutableSpan positions = tls.positions; + gather_grids_positions(subdiv_ccg, grids, positions); + + tls.factors.reinitialize(grid_verts_num); + const MutableSpan factors = tls.factors; + 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.translations.reinitialize(grid_verts_num); + const MutableSpan translations = tls.translations; + gather_data_grids(subdiv_ccg, all_translations, grids, translations); + scale_translations(translations, factors); + + clip_and_lock_translations(sd, ss, positions, translations); + apply_translations(translations, grids, subdiv_ccg); +} + +static void calc_bmesh(const Sculpt &sd, + Object &object, + const Brush &brush, + const Span all_translations, + const float strength, + PBVHNode &node, + LocalData &tls) +{ + SculptSession &ss = *object.sculpt; + const StrokeCache &cache = *ss.cache; + + const Set &verts = BKE_pbvh_bmesh_node_unique_verts(&node); + + tls.positions.reinitialize(verts.size()); + const MutableSpan positions = tls.positions; + gather_bmesh_positions(verts, positions); + + tls.factors.reinitialize(verts.size()); + const MutableSpan factors = tls.factors; + fill_factor_from_hide_and_mask(*ss.bm, verts, factors); + filter_region_clip_factors(ss, positions, factors); + if (brush.flag & BRUSH_FRONTFACE) { + calc_front_face(cache.view_normal, verts, factors); + } + + tls.distances.reinitialize(verts.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); + + if (cache.automasking) { + auto_mask::calc_vert_factors(object, *cache.automasking, node, verts, factors); + } + + calc_brush_texture_factors(ss, brush, positions, factors); + + scale_factors(factors, strength); + + tls.translations.reinitialize(verts.size()); + const MutableSpan translations = tls.translations; + gather_data_vert_bmesh(all_translations, verts, translations); + scale_translations(translations, factors); + + clip_and_lock_translations(sd, ss, positions, translations); + apply_translations(translations, verts); +} + +static void calc_translations_faces(const Span vert_positions, + const OffsetIndices faces, + const Span corner_verts, + const GroupedSpan vert_to_face_map, + const PBVHNode &node, + LocalData &tls, + const MutableSpan all_translations) +{ + const Span verts = bke::pbvh::node_unique_verts(node); + + tls.vert_neighbors.reinitialize(verts.size()); + const MutableSpan> neighbors = tls.vert_neighbors; + calc_vert_neighbors(faces, corner_verts, vert_to_face_map, {}, verts, neighbors); + + tls.new_positions.reinitialize(verts.size()); + const MutableSpan new_positions = tls.new_positions; + smooth::neighbor_position_average_mesh(vert_positions, verts, neighbors, new_positions); + + tls.translations.reinitialize(verts.size()); + const MutableSpan translations = tls.translations; + translations_from_new_positions(new_positions, verts, vert_positions, translations); + array_utils::scatter(translations.as_span(), verts, all_translations); +} + +static void calc_translations_grids(const SubdivCCG &subdiv_ccg, + const PBVHNode &node, + LocalData &tls, + const MutableSpan all_translations) +{ + 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); + const MutableSpan positions = tls.positions; + gather_grids_positions(subdiv_ccg, grids, positions); + + tls.new_positions.reinitialize(grid_verts_num); + const MutableSpan new_positions = tls.new_positions; + smooth::neighbor_position_average_grids(subdiv_ccg, grids, new_positions); + + tls.translations.reinitialize(grid_verts_num); + const MutableSpan translations = tls.translations; + translations_from_new_positions(new_positions, positions, translations); + scatter_data_grids(subdiv_ccg, translations, grids, all_translations); +} + +static void calc_translations_bmesh(PBVHNode &node, + LocalData &tls, + const MutableSpan all_translations) +{ + const Set &verts = BKE_pbvh_bmesh_node_unique_verts(&node); + + tls.positions.reinitialize(verts.size()); + const MutableSpan positions = tls.positions; + gather_bmesh_positions(verts, positions); + + tls.new_positions.reinitialize(verts.size()); + const MutableSpan new_positions = tls.new_positions; + smooth::neighbor_position_average_bmesh(verts, new_positions); + + tls.translations.reinitialize(verts.size()); + const MutableSpan translations = tls.translations; + translations_from_new_positions(new_positions, positions, translations); + scatter_data_vert_bmesh(translations.as_span(), verts, all_translations); +} + +/** + * The brush uses translations calculated at the beginning of the stroke. They can't be calculated + * dynamically because changing positions will influence neighboring translations. However we can + * reduce the cost in some cases by skipping initializing values for vertices in hidden or masked + * nodes. + */ +static void precalc_translations(Object &object, const MutableSpan translations) +{ + SculptSession &ss = *object.sculpt; + PBVH &pbvh = *ss.pbvh; + + Vector effective_nodes = bke::pbvh::search_gather( + pbvh, [&](PBVHNode &node) { return !node_fully_masked_or_hidden(node); }); + + threading::EnumerableThreadSpecific all_tls; + switch (BKE_pbvh_type(pbvh)) { + case PBVH_FACES: { + Mesh &mesh = *static_cast(object.data); + const Span positions_eval = BKE_pbvh_get_vert_positions(pbvh); + const OffsetIndices faces = mesh.faces(); + const Span corner_verts = mesh.corner_verts(); + threading::parallel_for(effective_nodes.index_range(), 1, [&](const IndexRange range) { + LocalData &tls = all_tls.local(); + threading::isolate_task([&]() { + for (const int i : range) { + calc_translations_faces(positions_eval, + faces, + corner_verts, + ss.vert_to_face_map, + *effective_nodes[i], + tls, + translations); + } + }); + }); + break; + } + case PBVH_GRIDS: { + SubdivCCG &subdiv_ccg = *ss.subdiv_ccg; + threading::parallel_for(effective_nodes.index_range(), 1, [&](const IndexRange range) { + LocalData &tls = all_tls.local(); + for (const int i : range) { + calc_translations_grids(subdiv_ccg, *effective_nodes[i], tls, translations); + } + }); + break; + } + case PBVH_BMESH: + BM_mesh_elem_index_ensure(ss.bm, BM_VERT); + BM_mesh_elem_table_ensure(ss.bm, BM_VERT); + threading::parallel_for(effective_nodes.index_range(), 1, [&](const IndexRange range) { + LocalData &tls = all_tls.local(); + for (const int i : range) { + calc_translations_bmesh(*effective_nodes[i], tls, translations); + } + }); + break; + } +} + +} // namespace enhance_details_cc + +void do_enhance_details_brush(const Sculpt &sd, Object &object, Span nodes) +{ + SculptSession &ss = *object.sculpt; + const Brush &brush = *BKE_paint_brush_for_read(&sd.paint); + PBVH &pbvh = *ss.pbvh; + + if (SCULPT_stroke_is_first_brush_step(*ss.cache)) { + ss.cache->detail_directions.reinitialize(SCULPT_vertex_count_get(ss)); + precalc_translations(object, ss.cache->detail_directions); + } + + const float strength = std::clamp(ss.cache->bstrength, -1.0f, 1.0f); + MutableSpan translations = ss.cache->detail_directions; + + threading::EnumerableThreadSpecific all_tls; + switch (BKE_pbvh_type(pbvh)) { + case PBVH_FACES: { + Mesh &mesh = *static_cast(object.data); + const Span positions_eval = BKE_pbvh_get_vert_positions(pbvh); + const Span vert_normals = BKE_pbvh_get_vert_normals(pbvh); + MutableSpan positions_orig = mesh.vert_positions_for_write(); + threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { + LocalData &tls = all_tls.local(); + threading::isolate_task([&]() { + for (const int i : range) { + calc_faces(sd, + brush, + positions_eval, + vert_normals, + translations, + strength, + *nodes[i], + object, + tls, + positions_orig); + BKE_pbvh_node_mark_positions_update(nodes[i]); + } + }); + }); + break; + } + case PBVH_GRIDS: + threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { + LocalData &tls = all_tls.local(); + for (const int i : range) { + calc_grids(sd, object, brush, translations, strength, *nodes[i], tls); + } + }); + break; + case PBVH_BMESH: + threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { + LocalData &tls = all_tls.local(); + for (const int i : range) { + calc_bmesh(sd, object, brush, translations, strength, *nodes[i], tls); + } + }); + 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 8d243849aea..a1da458be10 100644 --- a/source/blender/editors/sculpt_paint/brushes/types.hh +++ b/source/blender/editors/sculpt_paint/brushes/types.hh @@ -31,6 +31,7 @@ void do_draw_brush(const Sculpt &sd, Object &object, Span nodes); void do_draw_vector_displacement_brush(const Sculpt &sd, Object &object, Span nodes); void do_draw_sharp_brush(const Sculpt &sd, Object &object, Span nodes); void do_elastic_deform_brush(const Sculpt &sd, Object &object, Span nodes); +void do_enhance_details_brush(const Sculpt &sd, Object &object, Span nodes); void do_fill_brush(const Sculpt &sd, Object &object, Span nodes); void do_flatten_brush(const Sculpt &sd, Object &ob, Span nodes); void do_grab_brush(const Sculpt &sd, Object &ob, Span nodes); diff --git a/source/blender/editors/sculpt_paint/mesh_brush_common.hh b/source/blender/editors/sculpt_paint/mesh_brush_common.hh index f2643052c9d..aff4ba75e0a 100644 --- a/source/blender/editors/sculpt_paint/mesh_brush_common.hh +++ b/source/blender/editors/sculpt_paint/mesh_brush_common.hh @@ -96,6 +96,24 @@ void gather_grids_normals(const SubdivCCG &subdiv_ccg, MutableSpan normals); void gather_bmesh_normals(const Set &verts, MutableSpan normals); +/** Gather data from an array aligned with all geometry vertices. */ +void gather_data_grids(const SubdivCCG &subdiv_ccg, + Span src, + Span grids, + MutableSpan node_data); +void gather_data_vert_bmesh(Span src, + const Set &verts, + MutableSpan node_data); + +/** Scatter data from an array of the node's data to the referenced geometry vertices. */ +void scatter_data_grids(const SubdivCCG &subdiv_ccg, + Span node_data, + Span grids, + MutableSpan dst); +void scatter_data_vert_bmesh(Span node_data, + const Set &verts, + MutableSpan dst); + /** * Calculate initial influence factors based on vertex visibility. */ diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 47ecce08753..d405101ed0e 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -3938,7 +3938,7 @@ static void do_brush_action(const Scene &scene, * enhance brush in the middle of the stroke. */ if (ss.cache->bstrength < 0.0f) { /* Invert mode, intensify details. */ - smooth::enhance_details_brush(sd, ob, nodes); + do_enhance_details_brush(sd, ob, nodes); } else { do_smooth_brush(sd, ob, nodes, std::clamp(ss.cache->bstrength, 0.0f, 1.0f)); @@ -6724,6 +6724,62 @@ void gather_bmesh_normals(const Set &verts, const MutableSpan src, + const Span grids, + const MutableSpan node_data) +{ + const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg); + BLI_assert(grids.size() * key.grid_area == node_data.size()); + + for (const int i : grids.index_range()) { + const int node_start = i * key.grid_area; + const int grids_start = grids[i] * key.grid_area; + node_data.slice(node_start, key.grid_area).copy_from(src.slice(grids_start, key.grid_area)); + } +} + +void gather_data_vert_bmesh(const Span src, + const Set &verts, + const MutableSpan node_data) +{ + BLI_assert(verts.size() == node_data.size()); + + int i = 0; + for (const BMVert *vert : verts) { + node_data[i] = src[BM_elem_index_get(vert)]; + i++; + } +} + +void scatter_data_grids(const SubdivCCG &subdiv_ccg, + const Span node_data, + const Span grids, + const MutableSpan dst) +{ + const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg); + BLI_assert(grids.size() * key.grid_area == node_data.size()); + + for (const int i : grids.index_range()) { + const int node_start = i * key.grid_area; + const int grids_start = grids[i] * key.grid_area; + dst.slice(grids_start, key.grid_area).copy_from(node_data.slice(node_start, key.grid_area)); + } +} + +void scatter_data_vert_bmesh(const Span node_data, + const Set &verts, + const MutableSpan dst) +{ + BLI_assert(verts.size() == node_data.size()); + + int i = 0; + for (const BMVert *vert : verts) { + dst[BM_elem_index_get(vert)] = node_data[i]; + i++; + } +} + void fill_factor_from_hide(const Mesh &mesh, const Span verts, const MutableSpan r_factors) diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 5b6be2b5b65..7425c4cc51c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1565,6 +1565,9 @@ float4 neighbor_color_average(SculptSession &ss, */ float3 neighbor_coords_average_interior(const SculptSession &ss, PBVHVertRef vertex); +void neighbor_position_average_grids(const SubdivCCG &subdiv_ccg, + Span grids, + MutableSpan new_positions); void neighbor_position_average_interior_grids(OffsetIndices faces, Span corner_verts, BitSpan boundary_verts, @@ -1572,6 +1575,8 @@ void neighbor_position_average_interior_grids(OffsetIndices faces, Span grids, MutableSpan new_positions); +void neighbor_position_average_bmesh(const Set &verts, + MutableSpan new_positions); void neighbor_position_average_interior_bmesh(const Set &verts, MutableSpan new_positions); @@ -1580,7 +1585,6 @@ void neighbor_position_average_mesh(Span positions, Span> vert_neighbors, MutableSpan new_positions); -void enhance_details_brush(const Sculpt &sd, Object &ob, Span nodes); /* Surface Smooth Brush. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index a44a8ed5de6..6f79feee581 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -67,6 +67,40 @@ static float3 average_positions(const CCGKey &key, return result; } +void neighbor_position_average_grids(const SubdivCCG &subdiv_ccg, + const Span grids, + const MutableSpan new_positions) +{ + const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg); + const Span elems = subdiv_ccg.grids; + + BLI_assert(grids.size() * key.grid_area == new_positions.size()); + + for (const int i : grids.index_range()) { + const int grid = grids[i]; + const int node_verts_start = i * key.grid_area; + + /* TODO: This loop could be optimized in the future by skipping unnecessary logic for + * non-boundary grid vertices. */ + for (const int y : IndexRange(key.grid_size)) { + for (const int x : IndexRange(key.grid_size)) { + const int offset = CCG_grid_xy_to_index(key.grid_size, x, y); + const int node_vert_index = node_verts_start + offset; + + SubdivCCGCoord coord{}; + coord.grid_index = grid; + coord.x = x; + coord.y = y; + + SubdivCCGNeighbors neighbors; + BKE_subdiv_ccg_neighbor_coords_get(subdiv_ccg, coord, false, neighbors); + + new_positions[node_vert_index] = average_positions(key, elems, neighbors.coords); + } + } + } +} + void neighbor_position_average_interior_grids(const OffsetIndices faces, const Span corner_verts, const BitSpan boundary_verts, @@ -136,6 +170,21 @@ static float3 average_positions(const Span verts) return result; } +void neighbor_position_average_bmesh(const Set &verts, + const MutableSpan new_positions) +{ + BLI_assert(verts.size() == new_positions.size()); + Vector neighbor_data; + + int i = 0; + for (BMVert *vert : verts) { + neighbor_data.clear(); + const Span neighbors = vert_neighbors_get_bmesh(*vert, neighbor_data); + new_positions[i] = average_positions(neighbors); + i++; + } +} + void neighbor_position_average_interior_bmesh(const Set &verts, const MutableSpan new_positions) { @@ -326,77 +375,6 @@ float4 neighbor_color_average(SculptSession &ss, faces, corner_verts, vert_to_face_map, color_attribute, color_domain, vert); } -static void do_enhance_details_brush_task(Object &ob, - const Sculpt &sd, - const Brush &brush, - PBVHNode *node) -{ - SculptSession &ss = *ob.sculpt; - - PBVHVertexIter vd; - - float bstrength = ss.cache->bstrength; - CLAMP(bstrength, -1.0f, 1.0f); - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, test, brush.falloff_shape); - - const int thread_id = BLI_task_parallel_thread_id(nullptr); - auto_mask::NodeData automask_data = auto_mask::node_begin( - ob, ss.cache->automasking.get(), *node); - - BKE_pbvh_vertex_iter_begin (*ss.pbvh, node, vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(test, vd.co)) { - continue; - } - - auto_mask::node_update(automask_data, vd); - - 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); - - float disp[3]; - madd_v3_v3v3fl(disp, vd.co, ss.cache->detail_directions[vd.index], fade); - SCULPT_clip(sd, ss, vd.co, disp); - } - BKE_pbvh_vertex_iter_end; -} - -void enhance_details_brush(const Sculpt &sd, Object &ob, Span nodes) -{ - SculptSession &ss = *ob.sculpt; - const Brush &brush = *BKE_paint_brush_for_read(&sd.paint); - - SCULPT_vertex_random_access_ensure(ss); - SCULPT_boundary_info_ensure(ob); - - if (SCULPT_stroke_is_first_brush_step(*ss.cache)) { - const int totvert = SCULPT_vertex_count_get(ss); - ss.cache->detail_directions.reinitialize(totvert); - - for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(*ss.pbvh, i); - const float3 avg = neighbor_coords_average(ss, vertex); - sub_v3_v3v3(ss.cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); - } - } - - threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { - for (const int i : range) { - do_enhance_details_brush_task(ob, sd, brush, nodes[i]); - } - }); -} - /* HC Smooth Algorithm. */ /* From: Improved Laplacian Smoothing of Noisy Surface Meshes */