From 387cc06c1200e59c588e6ed52c8a08f92683d0c8 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 1 Aug 2024 10:53:29 -0400 Subject: [PATCH] Sculpt: Use new API for weight/vertex paint factors Part of #118145. For computation of factors, use the new API functions that handle more than one vertex at a time. --- .../editors/sculpt_paint/paint_vertex.cc | 255 +++++++++++------- .../editors/sculpt_paint/paint_weight.cc | 204 ++++++++------ 2 files changed, 289 insertions(+), 170 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index 07f00e52007..3a4358b4998 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -18,6 +18,7 @@ #include "BLI_array_utils.h" #include "BLI_color.hh" #include "BLI_color_mix.hh" +#include "BLI_enumerable_thread_specific.hh" #include "BLI_listbase.h" #include "BLI_math_geom.h" #include "BLI_math_rotation.h" @@ -74,6 +75,7 @@ #include "BKE_ccg.hh" #include "bmesh.hh" +#include "mesh_brush_common.hh" #include "paint_intern.hh" /* own include */ #include "sculpt_intern.hh" @@ -1012,6 +1014,19 @@ static bool vpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo return true; } +static void filter_factors_with_selection(const Span select_vert, + const Span verts, + const MutableSpan factors) +{ + BLI_assert(verts.size() == factors.size()); + + for (const int i : verts.index_range()) { + if (!select_vert[verts[i]]) { + factors[i] = 0.0f; + } + } +} + static void do_vpaint_brush_blur_loops(const bContext *C, const VPaint &vp, VPaintData &vpd, @@ -1021,12 +1036,11 @@ static void do_vpaint_brush_blur_loops(const bContext *C, GMutableSpan attribute) { SculptSession &ss = *ob.sculpt; + const StrokeCache &cache = *ss.cache; const Brush &brush = *ob.sculpt->cache->brush; const Scene &scene = *CTX_data_scene(C); - const GroupedSpan vert_to_face = mesh.vert_to_face_map(); - const StrokeCache *cache = ss.cache; float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; vwpaint::get_brush_alpha_data( scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); @@ -1035,9 +1049,6 @@ static void do_vpaint_brush_blur_loops(const bContext *C, 0; const bool use_face_sel = (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - SculptBrushTest test_init; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, test_init, brush.falloff_shape); const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( ss, brush.falloff_shape); @@ -1046,6 +1057,7 @@ static void do_vpaint_brush_blur_loops(const bContext *C, const Span vert_positions = BKE_pbvh_get_vert_positions(*ss.pbvh); const OffsetIndices faces = mesh.faces(); const Span corner_verts = mesh.corner_verts(); + const GroupedSpan vert_to_face = mesh.vert_to_face_map(); const Span vert_normals = BKE_pbvh_get_vert_normals(*ss.pbvh); const bke::AttributeAccessor attributes = mesh.attributes(); const VArraySpan hide_vert = *attributes.lookup(".hide_vert", bke::AttrDomain::Point); @@ -1058,21 +1070,36 @@ static void do_vpaint_brush_blur_loops(const bContext *C, select_poly = *attributes.lookup(".select_poly", bke::AttrDomain::Face); } + struct LocalData { + Vector factors; + Vector distances; + }; + threading::EnumerableThreadSpecific all_tls; blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) { - SculptBrushTest test = test_init; + LocalData &tls = all_tls.local(); for (const int i : range) { - for (const int vert : bke::pbvh::node_unique_verts(*nodes[i])) { - if (!hide_vert.is_empty() && hide_vert[vert]) { - continue; - } - if (!select_vert.is_empty() && !select_vert[vert]) { - continue; - } - if (!sculpt_brush_test_sq_fn(test, vert_positions[vert])) { + const Span verts = bke::pbvh::node_unique_verts(*nodes[i]); + tls.factors.resize(verts.size()); + const MutableSpan factors = tls.factors; + fill_factor_from_hide(mesh, verts, factors); + if (!select_vert.is_empty()) { + filter_factors_with_selection(select_vert, verts, factors); + } + + tls.distances.resize(verts.size()); + const MutableSpan distances = tls.distances; + calc_brush_distances( + ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances); + filter_distances_with_radius(cache.radius, distances, factors); + calc_brush_strength_factors(cache, brush, distances, factors); + + for (const int i : verts.index_range()) { + const int vert = verts[i]; + if (factors[i] == 0.0f) { continue; } - float brush_strength = cache->bstrength; + float brush_strength = cache.bstrength; const float angle_cos = use_normal ? dot_v3v3(sculpt_normal_frontface, vert_normals[vert]) : 1.0f; @@ -1082,7 +1109,7 @@ static void do_vpaint_brush_blur_loops(const bContext *C, continue; } - const float brush_fade = BKE_brush_curve_strength(&brush, sqrtf(test.dist), cache->radius); + const float brush_fade = factors[i]; to_static_color_type(vpd.type, [&](auto dummy) { using T = decltype(dummy); @@ -1165,12 +1192,11 @@ static void do_vpaint_brush_blur_verts(const bContext *C, GMutableSpan attribute) { SculptSession &ss = *ob.sculpt; + const StrokeCache &cache = *ss.cache; const Brush &brush = *ss.cache->brush; const Scene &scene = *CTX_data_scene(C); - const GroupedSpan vert_to_face = mesh.vert_to_face_map(); - const StrokeCache *cache = ss.cache; float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; vwpaint::get_brush_alpha_data( scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); @@ -1179,9 +1205,6 @@ static void do_vpaint_brush_blur_verts(const bContext *C, 0; const bool use_face_sel = (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - SculptBrushTest test_init; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, test_init, brush.falloff_shape); const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( ss, brush.falloff_shape); @@ -1190,6 +1213,7 @@ static void do_vpaint_brush_blur_verts(const bContext *C, const Span vert_positions = BKE_pbvh_get_vert_positions(*ss.pbvh); const OffsetIndices faces = mesh.faces(); const Span corner_verts = mesh.corner_verts(); + const GroupedSpan vert_to_face = mesh.vert_to_face_map(); const Span vert_normals = BKE_pbvh_get_vert_normals(*ss.pbvh); const bke::AttributeAccessor attributes = mesh.attributes(); const VArraySpan hide_vert = *attributes.lookup(".hide_vert", bke::AttrDomain::Point); @@ -1202,21 +1226,36 @@ static void do_vpaint_brush_blur_verts(const bContext *C, select_poly = *attributes.lookup(".select_poly", bke::AttrDomain::Face); } + struct LocalData { + Vector factors; + Vector distances; + }; + threading::EnumerableThreadSpecific all_tls; blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) { - SculptBrushTest test = test_init; + LocalData &tls = all_tls.local(); for (const int i : range) { - for (const int vert : bke::pbvh::node_unique_verts(*nodes[i])) { - if (!hide_vert.is_empty() && hide_vert[vert]) { - continue; - } - if (!select_vert.is_empty() && !select_vert[vert]) { - continue; - } - if (!sculpt_brush_test_sq_fn(test, vert_positions[vert])) { + const Span verts = bke::pbvh::node_unique_verts(*nodes[i]); + tls.factors.resize(verts.size()); + const MutableSpan factors = tls.factors; + fill_factor_from_hide(mesh, verts, factors); + if (!select_vert.is_empty()) { + filter_factors_with_selection(select_vert, verts, factors); + } + + tls.distances.resize(verts.size()); + const MutableSpan distances = tls.distances; + calc_brush_distances( + ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances); + filter_distances_with_radius(cache.radius, distances, factors); + calc_brush_strength_factors(cache, brush, distances, factors); + + for (const int i : verts.index_range()) { + const int vert = verts[i]; + if (factors[i] == 0.0f) { continue; } - float brush_strength = cache->bstrength; + float brush_strength = cache.bstrength; const float angle_cos = use_normal ? dot_v3v3(sculpt_normal_frontface, vert_normals[vert]) : 1.0f; @@ -1225,7 +1264,7 @@ static void do_vpaint_brush_blur_verts(const bContext *C, { continue; } - const float brush_fade = BKE_brush_curve_strength(&brush, sqrtf(test.dist), cache->radius); + const float brush_fade = factors[i]; /* Get the average face color */ to_static_color_type(vpd.type, [&](auto dummy) { @@ -1300,18 +1339,16 @@ static void do_vpaint_brush_smear(const bContext *C, GMutableSpan attribute) { SculptSession &ss = *ob.sculpt; - - const GroupedSpan vert_to_face = mesh.vert_to_face_map(); - const StrokeCache *cache = ss.cache; - if (!cache->is_last_valid) { + StrokeCache &cache = *ss.cache; + if (!cache.is_last_valid) { return; } - const Brush &brush = *ob.sculpt->cache->brush; + const Brush &brush = *cache.brush; const Scene &scene = *CTX_data_scene(C); GMutableSpan g_color_curr = vpd.smear.color_curr; GMutableSpan g_color_prev_smear = vpd.smear.color_prev; - GMutableSpan g_color_prev = ss.cache->prev_colors_vpaint; + GMutableSpan g_color_prev = cache.prev_colors_vpaint; float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; @@ -1323,21 +1360,19 @@ static void do_vpaint_brush_smear(const bContext *C, const bool use_face_sel = (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) != 0; float brush_dir[3]; - sub_v3_v3v3(brush_dir, cache->location, cache->last_location); - project_plane_v3_v3v3(brush_dir, brush_dir, cache->view_normal); + sub_v3_v3v3(brush_dir, cache.location, cache.last_location); + project_plane_v3_v3v3(brush_dir, brush_dir, cache.view_normal); if (normalize_v3(brush_dir) == 0.0f) { return; } - SculptBrushTest test_init; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, test_init, brush.falloff_shape); const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( ss, brush.falloff_shape); const Span vert_positions = BKE_pbvh_get_vert_positions(*ss.pbvh); const OffsetIndices faces = mesh.faces(); const Span corner_verts = mesh.corner_verts(); + const GroupedSpan vert_to_face = mesh.vert_to_face_map(); const Span vert_normals = BKE_pbvh_get_vert_normals(*ss.pbvh); const bke::AttributeAccessor attributes = mesh.attributes(); const VArraySpan hide_vert = *attributes.lookup(".hide_vert", bke::AttrDomain::Point); @@ -1350,23 +1385,38 @@ static void do_vpaint_brush_smear(const bContext *C, select_poly = *attributes.lookup(".select_poly", bke::AttrDomain::Face); } + struct LocalData { + Vector factors; + Vector distances; + }; + threading::EnumerableThreadSpecific all_tls; blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) { - SculptBrushTest test = test_init; + LocalData &tls = all_tls.local(); for (const int i : range) { - for (const int vert : bke::pbvh::node_unique_verts(*nodes[i])) { - if (!hide_vert.is_empty() && hide_vert[vert]) { - continue; - } - if (!select_vert.is_empty() && !select_vert[vert]) { - continue; - } - if (!sculpt_brush_test_sq_fn(test, vert_positions[vert])) { + const Span verts = bke::pbvh::node_unique_verts(*nodes[i]); + tls.factors.resize(verts.size()); + const MutableSpan factors = tls.factors; + fill_factor_from_hide(mesh, verts, factors); + if (!select_vert.is_empty()) { + filter_factors_with_selection(select_vert, verts, factors); + } + + tls.distances.resize(verts.size()); + const MutableSpan distances = tls.distances; + calc_brush_distances( + ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances); + filter_distances_with_radius(cache.radius, distances, factors); + calc_brush_strength_factors(cache, brush, distances, factors); + + for (const int i : verts.index_range()) { + const int vert = verts[i]; + if (factors[i] == 0.0f) { continue; } /* Calculate the dot prod. between ray norm on surf and current vert * (ie splash prevention factor), and only paint front facing verts. */ - float brush_strength = cache->bstrength; + float brush_strength = cache.bstrength; const float angle_cos = use_normal ? dot_v3v3(sculpt_normal_frontface, vert_normals[vert]) : 1.0f; @@ -1375,7 +1425,7 @@ static void do_vpaint_brush_smear(const bContext *C, { continue; } - const float brush_fade = BKE_brush_curve_strength(&brush, sqrtf(test.dist), cache->radius); + const float brush_fade = factors[i]; bool do_color = false; /* Minimum dot product between brush direction and current @@ -1411,7 +1461,7 @@ static void do_vpaint_brush_smear(const bContext *C, * selected vert to the neighbor. */ float other_dir[3]; sub_v3_v3v3(other_dir, vert_positions[vert], vert_positions[v_other_index]); - project_plane_v3_v3v3(other_dir, other_dir, cache->view_normal); + project_plane_v3_v3v3(other_dir, other_dir, cache.view_normal); normalize_v3(other_dir); @@ -1490,19 +1540,15 @@ static void calculate_average_color(VPaintData &vpd, const Span nodes) { SculptSession &ss = *ob.sculpt; - const GroupedSpan vert_to_face = mesh.vert_to_face_map(); + const StrokeCache &cache = *ss.cache; - StrokeCache *cache = ss.cache; const bool use_vert_sel = (mesh.editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; - SculptBrushTest test_init; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, test_init, brush.falloff_shape); - const Span vert_positions = BKE_pbvh_get_vert_positions(*ss.pbvh); const OffsetIndices faces = mesh.faces(); const Span corner_verts = mesh.corner_verts(); + const GroupedSpan vert_to_face = mesh.vert_to_face_map(); const bke::AttributeAccessor attributes = mesh.attributes(); const VArraySpan hide_vert = *attributes.lookup(".hide_vert", bke::AttrDomain::Point); VArraySpan select_vert; @@ -1510,6 +1556,11 @@ static void calculate_average_color(VPaintData &vpd, select_vert = *attributes.lookup(".select_vert", bke::AttrDomain::Point); } + struct LocalData { + Vector factors; + Vector distances; + }; + threading::EnumerableThreadSpecific all_tls; to_static_color_type(vpd.type, [&](auto dummy) { using T = decltype(dummy); using Color = @@ -1520,23 +1571,30 @@ static void calculate_average_color(VPaintData &vpd, Array> accum(nodes.size()); blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) { - SculptBrushTest test = test_init; + LocalData &tls = all_tls.local(); for (const int i : range) { VPaintAverageAccum &accum2 = accum[i]; accum2.len = 0; memset(accum2.value, 0, sizeof(accum2.value)); - for (const int vert : bke::pbvh::node_unique_verts(*nodes[i])) { - if (!hide_vert.is_empty() && hide_vert[vert]) { - continue; - } - if (!select_vert.is_empty() && !select_vert[vert]) { - continue; - } - if (!sculpt_brush_test_sq_fn(test, vert_positions[vert])) { - continue; - } - if (BKE_brush_curve_strength(&brush, 0.0, cache->radius) <= 0.0f) { + const Span verts = bke::pbvh::node_unique_verts(*nodes[i]); + tls.factors.resize(verts.size()); + const MutableSpan factors = tls.factors; + fill_factor_from_hide(mesh, verts, factors); + if (!select_vert.is_empty()) { + filter_factors_with_selection(select_vert, verts, factors); + } + + tls.distances.resize(verts.size()); + const MutableSpan distances = tls.distances; + calc_brush_distances( + ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances); + filter_distances_with_radius(cache.radius, distances, factors); + calc_brush_strength_factors(cache, brush, distances, factors); + + for (const int i : verts.index_range()) { + const int vert = verts[i]; + if (factors[i] == 0.0f) { continue; } @@ -1607,13 +1665,11 @@ static void vpaint_do_draw(const bContext *C, GMutableSpan attribute) { SculptSession &ss = *ob.sculpt; + const StrokeCache &cache = *ss.cache; const Brush &brush = *ob.sculpt->cache->brush; const Scene &scene = *CTX_data_scene(C); - const GroupedSpan vert_to_face = mesh.vert_to_face_map(); - - const StrokeCache *cache = ss.cache; float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; vwpaint::get_brush_alpha_data( scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); @@ -1622,9 +1678,6 @@ static void vpaint_do_draw(const bContext *C, 0; const bool use_face_sel = (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - SculptBrushTest test_init; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, test_init, brush.falloff_shape); const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( ss, brush.falloff_shape); @@ -1634,6 +1687,7 @@ static void vpaint_do_draw(const bContext *C, const OffsetIndices faces = mesh.faces(); const Span corner_verts = mesh.corner_verts(); const Span vert_normals = BKE_pbvh_get_vert_normals(*ss.pbvh); + const GroupedSpan vert_to_face = mesh.vert_to_face_map(); const bke::AttributeAccessor attributes = mesh.attributes(); const VArraySpan hide_vert = *attributes.lookup(".hide_vert", bke::AttrDomain::Point); VArraySpan select_vert; @@ -1645,23 +1699,38 @@ static void vpaint_do_draw(const bContext *C, select_poly = *attributes.lookup(".select_poly", bke::AttrDomain::Face); } + struct LocalData { + Vector factors; + Vector distances; + }; + threading::EnumerableThreadSpecific all_tls; blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) { + LocalData &tls = all_tls.local(); for (const int i : range) { - SculptBrushTest test = test_init; - for (const int vert : bke::pbvh::node_unique_verts(*nodes[i])) { - if (!hide_vert.is_empty() && hide_vert[vert]) { - continue; - } - if (!select_vert.is_empty() && !select_vert[vert]) { - continue; - } - if (!sculpt_brush_test_sq_fn(test, vert_positions[vert])) { + const Span verts = bke::pbvh::node_unique_verts(*nodes[i]); + tls.factors.resize(verts.size()); + const MutableSpan factors = tls.factors; + fill_factor_from_hide(mesh, verts, factors); + if (!select_vert.is_empty()) { + filter_factors_with_selection(select_vert, verts, factors); + } + + tls.distances.resize(verts.size()); + const MutableSpan distances = tls.distances; + calc_brush_distances( + ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances); + filter_distances_with_radius(cache.radius, distances, factors); + calc_brush_strength_factors(cache, brush, distances, factors); + + for (const int i : verts.index_range()) { + const int vert = verts[i]; + if (factors[i] == 0.0f) { continue; } /* Calculate the dot product between ray normal on surface and current vertex * (ie splash prevention factor), and only paint front facing verts. */ - float brush_strength = cache->bstrength; + float brush_strength = cache.bstrength; const float angle_cos = use_normal ? dot_v3v3(sculpt_normal_frontface, vert_normals[vert]) : 1.0f; @@ -1670,7 +1739,7 @@ static void vpaint_do_draw(const bContext *C, { continue; } - const float brush_fade = BKE_brush_curve_strength(&brush, sqrtf(test.dist), cache->radius); + const float brush_fade = factors[i]; to_static_color_type(vpd.type, [&](auto dummy) { using T = decltype(dummy); @@ -1692,10 +1761,10 @@ static void vpaint_do_draw(const bContext *C, * brush texture will be oriented correctly. * This is the method also used in #sculpt_apply_texture(). */ float symm_point[3]; - if (cache->radial_symmetry_pass) { - mul_m4_v3(cache->symm_rot_mat_inv.ptr(), vpd.vertexcosnos[vert].co); + if (cache.radial_symmetry_pass) { + mul_m4_v3(cache.symm_rot_mat_inv.ptr(), vpd.vertexcosnos[vert].co); } - flip_v3_v3(symm_point, vpd.vertexcosnos[vert].co, cache->mirror_symmetry_pass); + flip_v3_v3(symm_point, vpd.vertexcosnos[vert].co, cache.mirror_symmetry_pass); tex_alpha = paint_and_tex_color_alpha(vp, vpd, symm_point, &color_final); } diff --git a/source/blender/editors/sculpt_paint/paint_weight.cc b/source/blender/editors/sculpt_paint/paint_weight.cc index 7cbd7b16d9a..c359d3a03f6 100644 --- a/source/blender/editors/sculpt_paint/paint_weight.cc +++ b/source/blender/editors/sculpt_paint/paint_weight.cc @@ -16,6 +16,7 @@ #include "BLI_array_utils.h" #include "BLI_color.hh" #include "BLI_color_mix.hh" +#include "BLI_enumerable_thread_specific.hh" #include "BLI_listbase.h" #include "BLI_math_base.hh" #include "BLI_rect.h" @@ -66,6 +67,7 @@ #include "BKE_ccg.hh" #include "bmesh.hh" +#include "mesh_brush_common.hh" #include "paint_intern.hh" /* own include */ #include "sculpt_intern.hh" @@ -1074,6 +1076,19 @@ static void parallel_nodes_loop_with_mirror_check(const Mesh &mesh, } } +static void filter_factors_with_selection(const Span select_vert, + const Span verts, + const MutableSpan factors) +{ + BLI_assert(verts.size() == factors.size()); + + for (const int i : verts.index_range()) { + if (!select_vert[verts[i]]) { + factors[i] = 0.0f; + } + } +} + static void do_wpaint_brush_blur(const Scene &scene, Object &ob, const Brush &brush, @@ -1085,10 +1100,9 @@ static void do_wpaint_brush_blur(const Scene &scene, { using namespace blender; SculptSession &ss = *ob.sculpt; + const StrokeCache &cache = *ss.cache; const GroupedSpan vert_to_face = mesh.vert_to_face_map(); - const StrokeCache *cache = ss.cache; - float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; vwpaint::get_brush_alpha_data( scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); @@ -1096,9 +1110,6 @@ static void do_wpaint_brush_blur(const Scene &scene, const bool use_face_sel = (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) != 0; const bool use_vert_sel = (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - SculptBrushTest test_init; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, test_init, brush.falloff_shape); const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( ss, brush.falloff_shape); @@ -1113,17 +1124,32 @@ static void do_wpaint_brush_blur(const Scene &scene, select_vert = *attributes.lookup(".select_vert", bke::AttrDomain::Point); } + struct LocalData { + Vector factors; + Vector distances; + }; + threading::EnumerableThreadSpecific all_tls; parallel_nodes_loop_with_mirror_check(mesh, nodes, [&](const IndexRange range) { - SculptBrushTest test = test_init; + LocalData &tls = all_tls.local(); for (const int i : range) { - for (const int vert : bke::pbvh::node_unique_verts(*nodes[i])) { - if (!hide_vert.is_empty() && hide_vert[vert]) { - continue; - } - if (!select_vert.is_empty() && !select_vert[vert]) { - continue; - } - if (!sculpt_brush_test_sq_fn(test, vert_positions[vert])) { + const Span verts = bke::pbvh::node_unique_verts(*nodes[i]); + tls.factors.resize(verts.size()); + const MutableSpan factors = tls.factors; + fill_factor_from_hide(mesh, verts, factors); + if (!select_vert.is_empty()) { + filter_factors_with_selection(select_vert, verts, factors); + } + + tls.distances.resize(verts.size()); + const MutableSpan distances = tls.distances; + calc_brush_distances( + ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances); + filter_distances_with_radius(cache.radius, distances, factors); + calc_brush_strength_factors(cache, brush, distances, factors); + + for (const int i : verts.index_range()) { + const int vert = verts[i]; + if (factors[i] == 0.0f) { continue; } @@ -1141,7 +1167,7 @@ static void do_wpaint_brush_blur(const Scene &scene, continue; } - float brush_strength = cache->bstrength; + float brush_strength = cache.bstrength; const float angle_cos = use_normal ? dot_v3v3(sculpt_normal_frontface, vert_normals[vert]) : 1.0f; @@ -1151,8 +1177,7 @@ static void do_wpaint_brush_blur(const Scene &scene, continue; } - const float brush_fade = BKE_brush_curve_strength(&brush, sqrtf(test.dist), cache->radius); - const float final_alpha = brush_fade * brush_strength * brush_alpha_pressure; + const float final_alpha = factors[i] * brush_strength * brush_alpha_pressure; if ((brush.flag & BRUSH_ACCUMULATE) == 0) { if (ss.mode.wpaint.alpha_weight[vert] < final_alpha) { @@ -1182,8 +1207,8 @@ static void do_wpaint_brush_smear(const Scene &scene, using namespace blender; SculptSession &ss = *ob.sculpt; const GroupedSpan vert_to_face = mesh.vert_to_face_map(); - const StrokeCache *cache = ss.cache; - if (!cache->is_last_valid) { + const StrokeCache &cache = *ss.cache; + if (!cache.is_last_valid) { return; } @@ -1195,8 +1220,8 @@ static void do_wpaint_brush_smear(const Scene &scene, const bool use_vert_sel = (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) != 0; float brush_dir[3]; - sub_v3_v3v3(brush_dir, cache->location, cache->last_location); - project_plane_v3_v3v3(brush_dir, brush_dir, cache->view_normal); + sub_v3_v3v3(brush_dir, cache.location, cache.last_location); + project_plane_v3_v3v3(brush_dir, brush_dir, cache.view_normal); if (normalize_v3(brush_dir) == 0.0f) { return; } @@ -1212,27 +1237,39 @@ static void do_wpaint_brush_smear(const Scene &scene, select_vert = *attributes.lookup(".select_vert", bke::AttrDomain::Point); } - SculptBrushTest test_init; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, test_init, brush.falloff_shape); const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( ss, brush.falloff_shape); + struct LocalData { + Vector factors; + Vector distances; + }; + threading::EnumerableThreadSpecific all_tls; parallel_nodes_loop_with_mirror_check(mesh, nodes, [&](const IndexRange range) { - SculptBrushTest test = test_init; + LocalData &tls = all_tls.local(); for (const int i : range) { - for (const int vert : bke::pbvh::node_unique_verts(*nodes[i])) { - if (!hide_vert.is_empty() && hide_vert[vert]) { - continue; - } - if (!select_vert.is_empty() && !select_vert[vert]) { - continue; - } - if (!sculpt_brush_test_sq_fn(test, vert_positions[vert])) { + const Span verts = bke::pbvh::node_unique_verts(*nodes[i]); + tls.factors.resize(verts.size()); + const MutableSpan factors = tls.factors; + fill_factor_from_hide(mesh, verts, factors); + if (!select_vert.is_empty()) { + filter_factors_with_selection(select_vert, verts, factors); + } + + tls.distances.resize(verts.size()); + const MutableSpan distances = tls.distances; + calc_brush_distances( + ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances); + filter_distances_with_radius(cache.radius, distances, factors); + calc_brush_strength_factors(cache, brush, distances, factors); + + for (const int i : verts.index_range()) { + const int vert = verts[i]; + if (factors[i] == 0.0f) { continue; } - float brush_strength = cache->bstrength; + float brush_strength = cache.bstrength; const float angle_cos = use_normal ? dot_v3v3(sculpt_normal_frontface, vert_normals[vert]) : 1.0f; @@ -1259,7 +1296,7 @@ static void do_wpaint_brush_smear(const Scene &scene, /* Get the direction from the selected vert to the neighbor. */ float other_dir[3]; sub_v3_v3v3(other_dir, vert_positions[vert], vert_positions[vert_other]); - project_plane_v3_v3v3(other_dir, other_dir, cache->view_normal); + project_plane_v3_v3v3(other_dir, other_dir, cache.view_normal); normalize_v3(other_dir); @@ -1274,14 +1311,7 @@ static void do_wpaint_brush_smear(const Scene &scene, if (!do_color) { continue; } - const float brush_fade = BKE_brush_curve_strength( - &brush, sqrtf(test.dist), cache->radius); - const float final_alpha = brush_fade * brush_strength * brush_alpha_pressure; - - if (final_alpha <= 0.0f) { - continue; - } - + const float final_alpha = factors[i] * brush_strength * brush_alpha_pressure; do_weight_paint_vertex(vp, ob, wpi, vert, final_alpha, float(weight_final)); } } @@ -1302,7 +1332,7 @@ static void do_wpaint_brush_draw(const Scene &scene, using namespace blender; SculptSession &ss = *ob.sculpt; - const StrokeCache *cache = ss.cache; + const StrokeCache &cache = *ss.cache; /* NOTE: normally `BKE_brush_weight_get(scene, brush)` is used, * however in this case we calculate a new weight each time. */ const float paintweight = strength; @@ -1313,9 +1343,6 @@ static void do_wpaint_brush_draw(const Scene &scene, const bool use_face_sel = (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) != 0; const bool use_vert_sel = (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - SculptBrushTest test_init; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, test_init, brush.falloff_shape); const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( ss, brush.falloff_shape); @@ -1328,20 +1355,35 @@ static void do_wpaint_brush_draw(const Scene &scene, select_vert = *attributes.lookup(".select_vert", bke::AttrDomain::Point); } + struct LocalData { + Vector factors; + Vector distances; + }; + threading::EnumerableThreadSpecific all_tls; parallel_nodes_loop_with_mirror_check(mesh, nodes, [&](const IndexRange range) { - SculptBrushTest test = test_init; + LocalData &tls = all_tls.local(); for (const int i : range) { - for (const int vert : bke::pbvh::node_unique_verts(*nodes[i])) { - if (!hide_vert.is_empty() && hide_vert[vert]) { + const Span verts = bke::pbvh::node_unique_verts(*nodes[i]); + tls.factors.resize(verts.size()); + const MutableSpan factors = tls.factors; + fill_factor_from_hide(mesh, verts, factors); + if (!select_vert.is_empty()) { + filter_factors_with_selection(select_vert, verts, factors); + } + + tls.distances.resize(verts.size()); + const MutableSpan distances = tls.distances; + calc_brush_distances( + ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances); + filter_distances_with_radius(cache.radius, distances, factors); + calc_brush_strength_factors(cache, brush, distances, factors); + + for (const int i : verts.index_range()) { + const int vert = verts[i]; + if (factors[i] == 0.0f) { continue; } - if (!select_vert.is_empty() && !select_vert[vert]) { - continue; - } - if (!sculpt_brush_test_sq_fn(test, vert_positions[vert])) { - continue; - } - float brush_strength = cache->bstrength; + float brush_strength = cache.bstrength; const float angle_cos = use_normal ? dot_v3v3(sculpt_normal_frontface, vert_normals[vert]) : 1.0f; @@ -1350,8 +1392,7 @@ static void do_wpaint_brush_draw(const Scene &scene, { continue; } - const float brush_fade = BKE_brush_curve_strength(&brush, sqrtf(test.dist), cache->radius); - const float final_alpha = brush_fade * brush_strength * brush_alpha_pressure; + const float final_alpha = factors[i] * brush_strength * brush_alpha_pressure; if ((brush.flag & BRUSH_ACCUMULATE) == 0) { if (ss.mode.wpaint.alpha_weight[vert] < final_alpha) { @@ -1377,15 +1418,12 @@ static float calculate_average_weight(Object &ob, { using namespace blender; SculptSession &ss = *ob.sculpt; - StrokeCache *cache = ss.cache; + const StrokeCache &cache = *ss.cache; const bool use_normal = vwpaint::use_normal(vp); const bool use_face_sel = (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) != 0; const bool use_vert_sel = (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, test, brush.falloff_shape); const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( ss, brush.falloff_shape); @@ -1398,32 +1436,44 @@ static float calculate_average_weight(Object &ob, select_vert = *attributes.lookup(".select_vert", bke::AttrDomain::Point); } + struct LocalData { + Vector factors; + Vector distances; + }; + threading::EnumerableThreadSpecific all_tls; const WPaintAverageAccum value = threading::parallel_reduce( nodes.index_range(), 1, WPaintAverageAccum{}, [&](const IndexRange range, WPaintAverageAccum accum) { + LocalData &tls = all_tls.local(); for (const int i : range) { - for (const int vert : bke::pbvh::node_unique_verts(*nodes[i])) { - if (!hide_vert.is_empty() && hide_vert[vert]) { - continue; - } - if (!select_vert.is_empty() && !select_vert[vert]) { - continue; - } - if (!sculpt_brush_test_sq_fn(test, vert_positions[vert])) { - continue; - } + const Span verts = bke::pbvh::node_unique_verts(*nodes[i]); + tls.factors.resize(verts.size()); + const MutableSpan factors = tls.factors; + fill_factor_from_hide(mesh, verts, factors); + if (!select_vert.is_empty()) { + filter_factors_with_selection(select_vert, verts, factors); + } + tls.distances.resize(verts.size()); + const MutableSpan distances = tls.distances; + calc_brush_distances( + ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances); + filter_distances_with_radius(cache.radius, distances, factors); + calc_brush_strength_factors(cache, brush, distances, factors); + + for (const int i : verts.index_range()) { + const int vert = verts[i]; + if (factors[i] == 0.0f) { + continue; + } const float angle_cos = use_normal ? dot_v3v3(sculpt_normal_frontface, vert_normals[vert]) : 1.0f; - if (angle_cos <= 0.0f || - BKE_brush_curve_strength(&brush, sqrtf(test.dist), cache->radius) <= 0.0f) - { + if (angle_cos <= 0.0f) { continue; } - const MDeformVert &dv = wpi.dvert[vert]; accum.len++; accum.value += wpaint_get_active_weight(dv, wpi);