Optimization: use BLI_bvhtree_intersect_plane to detect faces that will be affected by the knife tool

The knife code currently calls the `BLI_bvhtree_overlap` function that
tests the overlap between the mesh tree and an AABB that encompasses the
points projected in the clip_start, clip_end and or clip_planes of the
view.

This resulted in many false positives since the AABB is very large.
Often all the triangles "overlapped".

The solution was to create a new function that actually tests the
intersection of AABB with a plane.

Even not considering the clip_planes of the view, this solution is more
appropriate than using overlap.

Differential Revision: https://developer.blender.org/D8229
This commit is contained in:
2020-07-07 09:45:53 -03:00
parent 630c6226e2
commit 20558848d3
3 changed files with 89 additions and 16 deletions

View File

@@ -174,6 +174,8 @@ BVHTreeOverlap *BLI_bvhtree_overlap(const BVHTree *tree1,
BVHTree_OverlapCallback callback,
void *userdata);
int *BLI_bvhtree_intersect_plane(BVHTree *tree, float plane[4], uint *r_intersect_tot);
int BLI_bvhtree_get_len(const BVHTree *tree);
int BLI_bvhtree_get_tree_type(const BVHTree *tree);
float BLI_bvhtree_get_epsilon(const BVHTree *tree);

View File

@@ -169,6 +169,12 @@ typedef struct BVHNearestProjectedData {
} BVHNearestProjectedData;
typedef struct BVHIntersectPlaneData {
const BVHTree *tree;
float plane[4];
struct BLI_Stack *intersect; /* Store indexes. */
} BVHIntersectPlaneData;
/** \} */
/**
@@ -1392,6 +1398,71 @@ BVHTreeOverlap *BLI_bvhtree_overlap(
/** \} */
/* -------------------------------------------------------------------- */
/** \name BLI_bvhtree_intersect_plane
* \{ */
static bool tree_intersect_plane_test(const float *bv, const float plane[4])
{
/* TODO(germano): Support other kdop geometries. */
const float bb_min[3] = {bv[0], bv[2], bv[4]};
const float bb_max[3] = {bv[1], bv[3], bv[5]};
float bb_near[3], bb_far[3];
aabb_get_near_far_from_plane(plane, bb_min, bb_max, bb_near, bb_far);
if ((plane_point_side_v3(plane, bb_near) > 0.0f) !=
(plane_point_side_v3(plane, bb_far) > 0.0f)) {
return true;
}
return false;
}
static void bvhtree_intersect_plane_dfs_recursive(BVHIntersectPlaneData *__restrict data,
const BVHNode *node)
{
if (tree_intersect_plane_test(node->bv, data->plane)) {
/* check if node is a leaf */
if (!node->totnode) {
int *intersect = BLI_stack_push_r(data->intersect);
*intersect = node->index;
}
else {
for (int j = 0; j < data->tree->tree_type; j++) {
if (node->children[j]) {
bvhtree_intersect_plane_dfs_recursive(data, node->children[j]);
}
}
}
}
}
int *BLI_bvhtree_intersect_plane(BVHTree *tree, float plane[4], uint *r_intersect_tot)
{
int *intersect = NULL;
size_t total = 0;
BVHNode *root = tree->nodes[tree->totleaf];
if (root != NULL) {
BVHIntersectPlaneData data;
data.tree = tree;
copy_v4_v4(data.plane, plane);
data.intersect = BLI_stack_new(sizeof(int), __func__);
bvhtree_intersect_plane_dfs_recursive(&data, root);
total = BLI_stack_count(data.intersect);
if (total) {
intersect = MEM_mallocN(sizeof(int) * total, __func__);
BLI_stack_pop_n(data.intersect, intersect, (uint)total);
}
BLI_stack_free(data.intersect);
}
*r_intersect_tot = (uint)total;
return intersect;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name BLI_bvhtree_find_nearest
* \{ */

View File

@@ -1548,8 +1548,8 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
{
SmallHash faces, kfes, kfvs;
float v1[3], v2[3], v3[3], v4[3], s1[2], s2[2];
BVHTree *planetree, *tree;
BVHTreeOverlap *results, *result;
BVHTree *tree;
int *results, *result;
BMLoop **ls;
BMFace *f;
KnifeEdge *kfe;
@@ -1562,7 +1562,6 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
KnifeLineHit hit;
void *val;
void **val_p;
float plane_cos[12];
float s[2], se1[2], se2[2], sint[2];
float r1[3], r2[3];
float d, d1, d2, lambda;
@@ -1623,22 +1622,24 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
clip_to_ortho_planes(v2, v4, kcd->ortho_extent_center, kcd->ortho_extent + 10.0f);
}
float plane[4];
{
float v1_v2[3], v1_v3[3];
copy_v3_v3(v1, kcd->prev.cage);
copy_v3_v3(v2, kcd->curr.cage);
sub_v3_v3v3(v1_v2, v2, v1);
sub_v3_v3v3(v1_v3, v3, v1);
cross_v3_v3v3(plane, v1_v2, v1_v3);
plane_from_point_normal_v3(plane, v1, plane);
}
/* First use bvh tree to find faces, knife edges, and knife verts that might
* intersect the cut plane with rays v1-v3 and v2-v4.
* This deduplicates the candidates before doing more expensive intersection tests. */
tree = BKE_bmbvh_tree_get(kcd->bmbvh);
planetree = BLI_bvhtree_new(4, FLT_EPSILON * 4, 8, 8);
copy_v3_v3(plane_cos + 0, v1);
copy_v3_v3(plane_cos + 3, v2);
copy_v3_v3(plane_cos + 6, v3);
copy_v3_v3(plane_cos + 9, v4);
BLI_bvhtree_insert(planetree, 0, plane_cos, 4);
BLI_bvhtree_balance(planetree);
results = BLI_bvhtree_overlap(tree, planetree, &tot, NULL, NULL);
results = BLI_bvhtree_intersect_plane(tree, plane, &tot);
if (!results) {
BLI_bvhtree_free(planetree);
return;
}
@@ -1647,9 +1648,9 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
BLI_smallhash_init(&kfvs);
for (i = 0, result = results; i < tot; i++, result++) {
ls = (BMLoop **)kcd->em->looptris[result->indexA];
ls = (BMLoop **)kcd->em->looptris[*result];
f = ls[0]->f;
set_lowest_face_tri(kcd, f, result->indexA);
set_lowest_face_tri(kcd, f, *result);
/* occlude but never cut unselected faces (when only_select is used) */
if (kcd->only_select && !BM_elem_flag_test(f, BM_ELEM_SELECT)) {
@@ -1834,7 +1835,6 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
BLI_smallhash_release(&faces);
BLI_smallhash_release(&kfes);
BLI_smallhash_release(&kfvs);
BLI_bvhtree_free(planetree);
if (results) {
MEM_freeN(results);
}