Sculpt: Data oriented refactor for nearest vertex search

Part of #118145.
This commit is contained in:
Hans Goudey
2024-07-10 12:25:03 -04:00
parent e9318360f1
commit 39c334582b
6 changed files with 185 additions and 57 deletions

View File

@@ -887,57 +887,185 @@ struct NearestVertexData {
float nearest_vertex_distance_sq;
};
static void nearest_vertex_get_node(PBVH &pbvh,
const float nearest_vertex_search_co[3],
const float max_distance_sq,
PBVHNode *node,
NearestVertexData *nvtd)
namespace blender::ed::sculpt_paint {
static std::optional<int> nearest_vert_calc_mesh(const PBVH &pbvh,
const Span<float3> vert_positions,
const Span<bool> hide_vert,
const float3 &location,
const float max_distance,
const bool use_original)
{
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) {
float distance_squared = len_squared_v3v3(vd.co, nearest_vertex_search_co);
if (distance_squared < nvtd->nearest_vertex_distance_sq && distance_squared < max_distance_sq)
{
nvtd->nearest_vertex = vd.vertex;
nvtd->nearest_vertex_distance_sq = distance_squared;
const float max_distance_sq = max_distance * max_distance;
Vector<PBVHNode *> nodes = bke::pbvh::search_gather(
const_cast<PBVH &>(pbvh), [&](PBVHNode &node) {
return node_in_sphere(node, location, max_distance_sq, use_original);
});
if (nodes.is_empty()) {
return std::nullopt;
}
struct NearestData {
int vert = -1;
float distance_sq = std::numeric_limits<float>::max();
};
const NearestData nearest = threading::parallel_reduce(
nodes.index_range(),
1,
NearestData(),
[&](const IndexRange range, NearestData nearest) {
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;
}
const float distance_sq = math::distance_squared(vert_positions[vert], location);
if (distance_sq < nearest.distance_sq) {
nearest = {vert, distance_sq};
}
}
}
return nearest;
},
[](const NearestData a, const NearestData b) {
return a.distance_sq < b.distance_sq ? a : b;
});
return nearest.vert;
}
static std::optional<SubdivCCGCoord> nearest_vert_calc_grids(PBVH &pbvh,
const SubdivCCG &subdiv_ccg,
const float3 &location,
const float max_distance,
const bool use_original)
{
const float max_distance_sq = max_distance * max_distance;
Vector<PBVHNode *> nodes = bke::pbvh::search_gather(
const_cast<PBVH &>(pbvh), [&](PBVHNode &node) {
return node_in_sphere(node, location, max_distance_sq, use_original);
});
if (nodes.is_empty()) {
return std::nullopt;
}
struct NearestData {
SubdivCCGCoord coord = {};
float distance_sq = std::numeric_limits<float>::max();
};
const BitGroupVector<> grid_hidden = subdiv_ccg.grid_hidden;
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
const Span<CCGElem *> elems = subdiv_ccg.grids;
const NearestData nearest = threading::parallel_reduce(
nodes.index_range(),
1,
NearestData(),
[&](const IndexRange range, NearestData nearest) {
for (const int i : range) {
for (const int grid : bke::pbvh::node_grid_indices(*nodes[i])) {
CCGElem *elem = elems[grid];
BKE_subdiv_ccg_foreach_visible_grid_vert(key, grid_hidden, grid, [&](const int i) {
const float distance_sq = math::distance_squared(CCG_elem_offset_co(key, elem, i),
location);
if (distance_sq < nearest.distance_sq) {
SubdivCCGCoord coord{};
coord.grid_index = grid;
coord.x = i % key.grid_size;
coord.y = i / key.grid_size;
nearest = {coord, distance_sq};
}
});
}
}
return nearest;
},
[](const NearestData a, const NearestData b) {
return a.distance_sq < b.distance_sq ? a : b;
});
return nearest.coord;
}
static std::optional<BMVert *> nearest_vert_calc_bmesh(PBVH &pbvh,
const float3 &location,
const float max_distance,
const bool use_original)
{
const float max_distance_sq = max_distance * max_distance;
Vector<PBVHNode *> nodes = bke::pbvh::search_gather(
const_cast<PBVH &>(pbvh), [&](PBVHNode &node) {
return node_in_sphere(node, location, max_distance_sq, use_original);
});
if (nodes.is_empty()) {
return std::nullopt;
}
struct NearestData {
BMVert *vert = nullptr;
float distance_sq = std::numeric_limits<float>::max();
};
const NearestData nearest = threading::parallel_reduce(
nodes.index_range(),
1,
NearestData(),
[&](const IndexRange range, NearestData nearest) {
for (const int i : range) {
for (BMVert *vert : BKE_pbvh_bmesh_node_unique_verts(nodes[i])) {
if (BM_elem_flag_test(vert, BM_ELEM_HIDDEN)) {
continue;
}
const float distance_sq = math::distance_squared(float3(vert->co), location);
if (distance_sq < nearest.distance_sq) {
nearest = {vert, distance_sq};
}
}
}
return nearest;
},
[](const NearestData a, const NearestData b) {
return a.distance_sq < b.distance_sq ? a : b;
});
return nearest.vert;
}
PBVHVertRef nearest_vert_calc(const Object &object,
const float3 &location,
const float max_distance,
const bool use_original)
{
const SculptSession &ss = *object.sculpt;
switch (BKE_pbvh_type(*ss.pbvh)) {
case PBVH_FACES: {
const Mesh &mesh = *static_cast<const Mesh *>(object.data);
const Span<float3> vert_positions = BKE_pbvh_get_vert_positions(*ss.pbvh);
const bke::AttributeAccessor attributes = mesh.attributes();
VArraySpan<bool> hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
const std::optional<int> nearest = nearest_vert_calc_mesh(
*ss.pbvh, vert_positions, hide_vert, location, max_distance, use_original);
return nearest ? PBVHVertRef{*nearest} : PBVHVertRef{PBVH_REF_NONE};
}
case PBVH_GRIDS: {
const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
const std::optional<SubdivCCGCoord> nearest = nearest_vert_calc_grids(
*ss.pbvh, subdiv_ccg, location, max_distance, use_original);
return nearest ? PBVHVertRef{key.grid_area * nearest->grid_index +
CCG_grid_xy_to_index(key.grid_size, nearest->x, nearest->y)} :
PBVHVertRef{PBVH_REF_NONE};
}
case PBVH_BMESH: {
const std::optional<BMVert *> nearest = nearest_vert_calc_bmesh(
*ss.pbvh, location, max_distance, use_original);
return nearest ? PBVHVertRef{intptr_t(*nearest)} : PBVHVertRef{PBVH_REF_NONE};
}
}
BKE_pbvh_vertex_iter_end;
BLI_assert_unreachable();
return BKE_pbvh_make_vref(PBVH_REF_NONE);
}
PBVHVertRef SCULPT_nearest_vertex_get(const Object &ob,
const float co[3],
float max_distance,
bool use_original)
{
using namespace blender;
using namespace blender::ed::sculpt_paint;
const SculptSession &ss = *ob.sculpt;
const float max_distance_sq = max_distance * max_distance;
Vector<PBVHNode *> nodes = bke::pbvh::search_gather(*ss.pbvh, [&](PBVHNode &node) {
return node_in_sphere(node, co, max_distance_sq, use_original);
});
if (nodes.is_empty()) {
return BKE_pbvh_make_vref(PBVH_REF_NONE);
}
return threading::parallel_reduce(
nodes.index_range(),
1,
NearestVertexData{{PBVH_REF_NONE}, FLT_MAX},
[&](const IndexRange range, NearestVertexData nearest) {
for (const int i : range) {
nearest_vertex_get_node(*ss.pbvh, co, max_distance_sq, nodes[i], &nearest);
}
return nearest;
},
[](const NearestVertexData a, const NearestVertexData b) {
return a.nearest_vertex_distance_sq < b.nearest_vertex_distance_sq ? a : b;
})
.nearest_vertex;
}
} // namespace blender::ed::sculpt_paint
bool SCULPT_is_symmetry_iteration_valid(char i, char symm)
{
@@ -1027,7 +1155,7 @@ void add_initial_with_symmetry(
float radius_squared = (radius == FLT_MAX) ? FLT_MAX : radius * radius;
float location[3];
flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), ePaintSymmetryFlags(i));
v = SCULPT_nearest_vertex_get(ob, location, radius_squared, false);
v = nearest_vert_calc(ob, location, radius_squared, false);
}
if (v.i != PBVH_REF_NONE) {
@@ -1053,7 +1181,7 @@ void add_active(const Object &ob, const SculptSession &ss, FillData &flood, floa
else if (radius > 0.0f) {
float location[3];
flip_v3_v3(location, SCULPT_active_vertex_co_get(ss), ePaintSymmetryFlags(i));
v = SCULPT_nearest_vertex_get(ob, location, radius, false);
v = nearest_vert_calc(ob, location, radius, false);
}
if (v.i != PBVH_REF_NONE) {

View File

@@ -868,7 +868,7 @@ void do_boundary_brush(const Sculpt &sd, Object &ob, Span<PBVHNode *> nodes)
else {
float location[3];
flip_v3_v3(location, SCULPT_active_vertex_co_get(ss), symm_area);
initial_vert = SCULPT_nearest_vertex_get(ob, location, ss.cache->radius_squared, false);
initial_vert = nearest_vert_calc(ob, location, ss.cache->radius_squared, false);
}
ss.cache->boundaries[symm_area] = data_init(

View File

@@ -445,7 +445,7 @@ static PBVHVertRef get_vert_index_for_symmetry_pass(Object &ob,
else {
float location[3];
flip_v3_v3(location, SCULPT_vertex_co_get(ss, original_vertex), ePaintSymmetryFlags(symm_it));
symm_vertex = SCULPT_nearest_vertex_get(ob, location, FLT_MAX, false);
symm_vertex = nearest_vert_calc(ob, location, FLT_MAX, false);
}
return symm_vertex;
}

View File

@@ -281,7 +281,7 @@ Array<float> distances_create_from_vert_and_symm(Object &ob,
else {
float location[3];
flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), ePaintSymmetryFlags(i));
v = SCULPT_nearest_vertex_get(ob, location, FLT_MAX, false);
v = nearest_vert_calc(ob, location, FLT_MAX, false);
}
if (v.i != PBVH_REF_NONE) {
initial_verts.add(BKE_pbvh_vertex_to_index(*ss.pbvh, v));

View File

@@ -1032,12 +1032,12 @@ void calc_area_center(const Brush &brush,
Span<PBVHNode *> nodes,
float r_area_co[3]);
}
PBVHVertRef nearest_vert_calc(const Object &object,
const float3 &location,
float max_distance,
bool use_original);
PBVHVertRef SCULPT_nearest_vertex_get(const Object &ob,
const float co[3],
float max_distance,
bool use_original);
}
int SCULPT_plane_point_side(const float co[3], const float plane[4]);
int SCULPT_plane_trim(const blender::ed::sculpt_paint::StrokeCache &cache,

View File

@@ -618,7 +618,7 @@ static std::unique_ptr<SculptPoseIKChain> pose_ik_chain_init_topology(
float next_chain_segment_target[3];
int totvert = SCULPT_vertex_count_get(ss);
PBVHVertRef nearest_vertex = SCULPT_nearest_vertex_get(ob, initial_location, FLT_MAX, true);
PBVHVertRef nearest_vertex = nearest_vert_calc(ob, initial_location, FLT_MAX, true);
int nearest_vertex_index = BKE_pbvh_vertex_to_index(*ss.pbvh, nearest_vertex);
/* Init the buffers used to keep track of the changes in the pose factors as more segments are