Files
test2/source/blender/blenkernel/intern/editmesh_bvh.c
Sergey Sharybin c1bc70b711 Cleanup: Add a copyright notice to files and use SPDX format
A lot of files were missing copyright field in the header and
the Blender Foundation contributed to them in a sense of bug
fixing and general maintenance.

This change makes it explicit that those files are at least
partially copyrighted by the Blender Foundation.

Note that this does not make it so the Blender Foundation is
the only holder of the copyright in those files, and developers
who do not have a signed contract with the foundation still
hold the copyright as well.

Another aspect of this change is using SPDX format for the
header. We already used it for the license specification,
and now we state it for the copyright as well, following the
FAQ:

    https://reuse.software/faq/
2023-05-31 16:19:06 +02:00

587 lines
16 KiB
C

/* SPDX-FileCopyrightText: 2010 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include "MEM_guardedalloc.h"
#include "BLI_kdopbvh.h"
#include "BLI_math.h"
#include "BKE_editmesh.h"
#include "BKE_editmesh_bvh.h" /* own include */
struct BMBVHTree {
BVHTree *tree;
BMLoop *(*looptris)[3];
int looptris_tot;
BMesh *bm;
const float (*cos_cage)[3];
bool cos_cage_free;
int flag;
};
BMBVHTree *BKE_bmbvh_new_from_editmesh(BMEditMesh *em,
int flag,
const float (*cos_cage)[3],
const bool cos_cage_free)
{
return BKE_bmbvh_new(em->bm, em->looptris, em->tottri, flag, cos_cage, cos_cage_free);
}
BMBVHTree *BKE_bmbvh_new_ex(BMesh *bm,
BMLoop *(*looptris)[3],
int looptris_tot,
int flag,
const float (*cos_cage)[3],
const bool cos_cage_free,
bool (*test_fn)(BMFace *, void *user_data),
void *user_data)
{
/* could become argument */
const float epsilon = FLT_EPSILON * 2.0f;
BMBVHTree *bmtree = MEM_callocN(sizeof(*bmtree), "BMBVHTree");
float cos[3][3];
int tottri;
/* avoid testing every tri */
BMFace *f_test, *f_test_prev;
bool test_fn_ret;
/* BKE_editmesh_looptri_calc() must be called already */
BLI_assert(looptris_tot != 0 || bm->totface == 0);
if (cos_cage) {
BM_mesh_elem_index_ensure(bm, BM_VERT);
}
bmtree->looptris = looptris;
bmtree->looptris_tot = looptris_tot;
bmtree->bm = bm;
bmtree->cos_cage = cos_cage;
bmtree->cos_cage_free = cos_cage_free;
bmtree->flag = flag;
if (test_fn) {
/* callback must do... */
BLI_assert(!(flag & (BMBVH_RESPECT_SELECT | BMBVH_RESPECT_HIDDEN)));
f_test_prev = NULL;
test_fn_ret = false;
tottri = 0;
for (int i = 0; i < looptris_tot; i++) {
f_test = looptris[i][0]->f;
if (f_test != f_test_prev) {
test_fn_ret = test_fn(f_test, user_data);
f_test_prev = f_test;
}
if (test_fn_ret) {
tottri++;
}
}
}
else {
tottri = looptris_tot;
}
bmtree->tree = BLI_bvhtree_new(tottri, epsilon, 8, 8);
f_test_prev = NULL;
test_fn_ret = false;
for (int i = 0; i < looptris_tot; i++) {
if (test_fn) {
/* NOTE: the arrays won't align now! Take care. */
f_test = looptris[i][0]->f;
if (f_test != f_test_prev) {
test_fn_ret = test_fn(f_test, user_data);
f_test_prev = f_test;
}
if (!test_fn_ret) {
continue;
}
}
if (cos_cage) {
copy_v3_v3(cos[0], cos_cage[BM_elem_index_get(looptris[i][0]->v)]);
copy_v3_v3(cos[1], cos_cage[BM_elem_index_get(looptris[i][1]->v)]);
copy_v3_v3(cos[2], cos_cage[BM_elem_index_get(looptris[i][2]->v)]);
}
else {
copy_v3_v3(cos[0], looptris[i][0]->v->co);
copy_v3_v3(cos[1], looptris[i][1]->v->co);
copy_v3_v3(cos[2], looptris[i][2]->v->co);
}
BLI_bvhtree_insert(bmtree->tree, i, (float *)cos, 3);
}
BLI_bvhtree_balance(bmtree->tree);
return bmtree;
}
static bool bm_face_is_select(BMFace *f, void *UNUSED(user_data))
{
return (BM_elem_flag_test(f, BM_ELEM_SELECT) != 0);
}
static bool bm_face_is_not_hidden(BMFace *f, void *UNUSED(user_data))
{
return (BM_elem_flag_test(f, BM_ELEM_HIDDEN) == 0);
}
BMBVHTree *BKE_bmbvh_new(BMesh *bm,
BMLoop *(*looptris)[3],
int looptris_tot,
int flag,
const float (*cos_cage)[3],
const bool cos_cage_free)
{
bool (*test_fn)(BMFace *, void *user_data);
if (flag & BMBVH_RESPECT_SELECT) {
test_fn = bm_face_is_select;
}
else if (flag & BMBVH_RESPECT_HIDDEN) {
test_fn = bm_face_is_not_hidden;
}
else {
test_fn = NULL;
}
flag &= ~(BMBVH_RESPECT_SELECT | BMBVH_RESPECT_HIDDEN);
return BKE_bmbvh_new_ex(
bm, looptris, looptris_tot, flag, cos_cage, cos_cage_free, test_fn, NULL);
}
void BKE_bmbvh_free(BMBVHTree *bmtree)
{
BLI_bvhtree_free(bmtree->tree);
if (bmtree->cos_cage && bmtree->cos_cage_free) {
MEM_freeN((void *)bmtree->cos_cage);
}
MEM_freeN(bmtree);
}
BVHTree *BKE_bmbvh_tree_get(BMBVHTree *bmtree)
{
return bmtree->tree;
}
/* -------------------------------------------------------------------- */
/* Utility BMesh cast/intersect functions */
/**
* Return the coords from a triangle.
*/
static void bmbvh_tri_from_face(const float *cos[3],
const BMLoop **ltri,
const float (*cos_cage)[3])
{
if (cos_cage == NULL) {
cos[0] = ltri[0]->v->co;
cos[1] = ltri[1]->v->co;
cos[2] = ltri[2]->v->co;
}
else {
cos[0] = cos_cage[BM_elem_index_get(ltri[0]->v)];
cos[1] = cos_cage[BM_elem_index_get(ltri[1]->v)];
cos[2] = cos_cage[BM_elem_index_get(ltri[2]->v)];
}
}
/* Taken from `bvhutils.c`. */
/* -------------------------------------------------------------------- */
/* BKE_bmbvh_ray_cast */
struct RayCastUserData {
/* from the bmtree */
const BMLoop *(*looptris)[3];
const float (*cos_cage)[3];
/* from the hit */
float uv[2];
};
static BMFace *bmbvh_ray_cast_handle_hit(BMBVHTree *bmtree,
struct RayCastUserData *bmcb_data,
const BVHTreeRayHit *hit,
float *r_dist,
float r_hitout[3],
float r_cagehit[3])
{
if (r_hitout) {
if (bmtree->flag & BMBVH_RETURN_ORIG) {
BMLoop **ltri = bmtree->looptris[hit->index];
interp_v3_v3v3v3_uv(r_hitout, ltri[0]->v->co, ltri[1]->v->co, ltri[2]->v->co, bmcb_data->uv);
}
else {
copy_v3_v3(r_hitout, hit->co);
}
if (r_cagehit) {
copy_v3_v3(r_cagehit, hit->co);
}
}
if (r_dist) {
*r_dist = hit->dist;
}
return bmtree->looptris[hit->index][0]->f;
}
static void bmbvh_ray_cast_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
{
struct RayCastUserData *bmcb_data = userdata;
const BMLoop **ltri = bmcb_data->looptris[index];
float dist, uv[2];
const float *tri_cos[3];
bool isect;
bmbvh_tri_from_face(tri_cos, ltri, bmcb_data->cos_cage);
isect =
(ray->radius > 0.0f ?
isect_ray_tri_epsilon_v3(ray->origin,
ray->direction,
tri_cos[0],
tri_cos[1],
tri_cos[2],
&dist,
uv,
ray->radius) :
#ifdef USE_KDOPBVH_WATERTIGHT
isect_ray_tri_watertight_v3(
ray->origin, ray->isect_precalc, tri_cos[0], tri_cos[1], tri_cos[2], &dist, uv));
#else
isect_ray_tri_v3(
ray->origin, ray->direction, tri_cos[0], tri_cos[1], tri_cos[2], &dist, uv));
#endif
if (isect && dist < hit->dist) {
hit->dist = dist;
hit->index = index;
copy_v3_v3(hit->no, ltri[0]->f->no);
madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist);
copy_v2_v2(bmcb_data->uv, uv);
}
}
BMFace *BKE_bmbvh_ray_cast(BMBVHTree *bmtree,
const float co[3],
const float dir[3],
const float radius,
float *r_dist,
float r_hitout[3],
float r_cagehit[3])
{
BVHTreeRayHit hit;
struct RayCastUserData bmcb_data;
const float dist = r_dist ? *r_dist : FLT_MAX;
if (bmtree->cos_cage) {
BLI_assert(!(bmtree->bm->elem_index_dirty & BM_VERT));
}
hit.dist = dist;
hit.index = -1;
/* ok to leave 'uv' uninitialized */
bmcb_data.looptris = (const BMLoop *(*)[3])bmtree->looptris;
bmcb_data.cos_cage = (const float(*)[3])bmtree->cos_cage;
BLI_bvhtree_ray_cast(bmtree->tree, co, dir, radius, &hit, bmbvh_ray_cast_cb, &bmcb_data);
if (hit.index != -1 && hit.dist != dist) {
return bmbvh_ray_cast_handle_hit(bmtree, &bmcb_data, &hit, r_dist, r_hitout, r_cagehit);
}
return NULL;
}
/* -------------------------------------------------------------------- */
/* bmbvh_ray_cast_cb_filter */
/* Same as BKE_bmbvh_ray_cast but takes a callback to filter out faces.
*/
struct RayCastUserData_Filter {
struct RayCastUserData bmcb_data;
BMBVHTree_FaceFilter filter_cb;
void *filter_userdata;
};
static void bmbvh_ray_cast_cb_filter(void *userdata,
int index,
const BVHTreeRay *ray,
BVHTreeRayHit *hit)
{
struct RayCastUserData_Filter *bmcb_data_filter = userdata;
struct RayCastUserData *bmcb_data = &bmcb_data_filter->bmcb_data;
const BMLoop **ltri = bmcb_data->looptris[index];
if (bmcb_data_filter->filter_cb(ltri[0]->f, bmcb_data_filter->filter_userdata)) {
bmbvh_ray_cast_cb(bmcb_data, index, ray, hit);
}
}
BMFace *BKE_bmbvh_ray_cast_filter(BMBVHTree *bmtree,
const float co[3],
const float dir[3],
const float radius,
float *r_dist,
float r_hitout[3],
float r_cagehit[3],
BMBVHTree_FaceFilter filter_cb,
void *filter_userdata)
{
BVHTreeRayHit hit;
struct RayCastUserData_Filter bmcb_data_filter;
struct RayCastUserData *bmcb_data = &bmcb_data_filter.bmcb_data;
const float dist = r_dist ? *r_dist : FLT_MAX;
bmcb_data_filter.filter_cb = filter_cb;
bmcb_data_filter.filter_userdata = filter_userdata;
if (bmtree->cos_cage) {
BLI_assert(!(bmtree->bm->elem_index_dirty & BM_VERT));
}
hit.dist = dist;
hit.index = -1;
/* ok to leave 'uv' uninitialized */
bmcb_data->looptris = (const BMLoop *(*)[3])bmtree->looptris;
bmcb_data->cos_cage = (const float(*)[3])bmtree->cos_cage;
BLI_bvhtree_ray_cast(
bmtree->tree, co, dir, radius, &hit, bmbvh_ray_cast_cb_filter, &bmcb_data_filter);
if (hit.index != -1 && hit.dist != dist) {
return bmbvh_ray_cast_handle_hit(bmtree, bmcb_data, &hit, r_dist, r_hitout, r_cagehit);
}
return NULL;
}
/* -------------------------------------------------------------------- */
/* BKE_bmbvh_find_vert_closest */
struct VertSearchUserData {
/* from the bmtree */
const BMLoop *(*looptris)[3];
const float (*cos_cage)[3];
/* from the hit */
float dist_max_sq;
int index_tri;
};
static void bmbvh_find_vert_closest_cb(void *userdata,
int index,
const float co[3],
BVHTreeNearest *hit)
{
struct VertSearchUserData *bmcb_data = userdata;
const BMLoop **ltri = bmcb_data->looptris[index];
const float dist_max_sq = bmcb_data->dist_max_sq;
const float *tri_cos[3];
bmbvh_tri_from_face(tri_cos, ltri, bmcb_data->cos_cage);
for (int i = 0; i < 3; i++) {
const float dist_sq = len_squared_v3v3(co, tri_cos[i]);
if (dist_sq < hit->dist_sq && dist_sq < dist_max_sq) {
copy_v3_v3(hit->co, tri_cos[i]);
/* XXX, normal ignores cage */
copy_v3_v3(hit->no, ltri[i]->v->no);
hit->dist_sq = dist_sq;
hit->index = index;
bmcb_data->index_tri = i;
}
}
}
BMVert *BKE_bmbvh_find_vert_closest(BMBVHTree *bmtree, const float co[3], const float dist_max)
{
BVHTreeNearest hit;
struct VertSearchUserData bmcb_data;
const float dist_max_sq = dist_max * dist_max;
if (bmtree->cos_cage) {
BLI_assert(!(bmtree->bm->elem_index_dirty & BM_VERT));
}
hit.dist_sq = dist_max_sq;
hit.index = -1;
bmcb_data.looptris = (const BMLoop *(*)[3])bmtree->looptris;
bmcb_data.cos_cage = (const float(*)[3])bmtree->cos_cage;
bmcb_data.dist_max_sq = dist_max_sq;
BLI_bvhtree_find_nearest(bmtree->tree, co, &hit, bmbvh_find_vert_closest_cb, &bmcb_data);
if (hit.index != -1) {
BMLoop **ltri = bmtree->looptris[hit.index];
return ltri[bmcb_data.index_tri]->v;
}
return NULL;
}
struct FaceSearchUserData {
/* from the bmtree */
const BMLoop *(*looptris)[3];
const float (*cos_cage)[3];
/* from the hit */
float dist_max_sq;
};
static void bmbvh_find_face_closest_cb(void *userdata,
int index,
const float co[3],
BVHTreeNearest *hit)
{
struct FaceSearchUserData *bmcb_data = userdata;
const BMLoop **ltri = bmcb_data->looptris[index];
const float dist_max_sq = bmcb_data->dist_max_sq;
const float *tri_cos[3];
bmbvh_tri_from_face(tri_cos, ltri, bmcb_data->cos_cage);
float co_close[3];
closest_on_tri_to_point_v3(co_close, co, UNPACK3(tri_cos));
const float dist_sq = len_squared_v3v3(co, co_close);
if (dist_sq < hit->dist_sq && dist_sq < dist_max_sq) {
/* XXX, normal ignores cage */
copy_v3_v3(hit->no, ltri[0]->f->no);
hit->dist_sq = dist_sq;
hit->index = index;
}
}
struct BMFace *BKE_bmbvh_find_face_closest(BMBVHTree *bmtree,
const float co[3],
const float dist_max)
{
BVHTreeNearest hit;
struct FaceSearchUserData bmcb_data;
const float dist_max_sq = dist_max * dist_max;
if (bmtree->cos_cage) {
BLI_assert(!(bmtree->bm->elem_index_dirty & BM_VERT));
}
hit.dist_sq = dist_max_sq;
hit.index = -1;
bmcb_data.looptris = (const BMLoop *(*)[3])bmtree->looptris;
bmcb_data.cos_cage = (const float(*)[3])bmtree->cos_cage;
bmcb_data.dist_max_sq = dist_max_sq;
BLI_bvhtree_find_nearest(bmtree->tree, co, &hit, bmbvh_find_face_closest_cb, &bmcb_data);
if (hit.index != -1) {
BMLoop **ltri = bmtree->looptris[hit.index];
return ltri[0]->f;
}
return NULL;
}
/* -------------------------------------------------------------------- */
/* BKE_bmbvh_overlap */
struct BMBVHTree_OverlapData {
const BMBVHTree *tree_pair[2];
float epsilon;
};
static bool bmbvh_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread))
{
struct BMBVHTree_OverlapData *data = userdata;
const BMBVHTree *bmtree_a = data->tree_pair[0];
const BMBVHTree *bmtree_b = data->tree_pair[1];
BMLoop **tri_a = bmtree_a->looptris[index_a];
BMLoop **tri_b = bmtree_b->looptris[index_b];
const float *tri_a_co[3] = {tri_a[0]->v->co, tri_a[1]->v->co, tri_a[2]->v->co};
const float *tri_b_co[3] = {tri_b[0]->v->co, tri_b[1]->v->co, tri_b[2]->v->co};
float ix_pair[2][3];
int verts_shared = 0;
if (bmtree_a->looptris == bmtree_b->looptris) {
if (UNLIKELY(tri_a[0]->f == tri_b[0]->f)) {
return false;
}
verts_shared = (ELEM(tri_a_co[0], UNPACK3(tri_b_co)) + ELEM(tri_a_co[1], UNPACK3(tri_b_co)) +
ELEM(tri_a_co[2], UNPACK3(tri_b_co)));
/* if 2 points are shared, bail out */
if (verts_shared >= 2) {
return false;
}
}
return (isect_tri_tri_v3(UNPACK3(tri_a_co), UNPACK3(tri_b_co), ix_pair[0], ix_pair[1]) &&
/* if we share a vertex, check the intersection isn't a 'point' */
((verts_shared == 0) || (len_squared_v3v3(ix_pair[0], ix_pair[1]) > data->epsilon)));
}
BVHTreeOverlap *BKE_bmbvh_overlap(const BMBVHTree *bmtree_a,
const BMBVHTree *bmtree_b,
uint *r_overlap_tot)
{
struct BMBVHTree_OverlapData data;
data.tree_pair[0] = bmtree_a;
data.tree_pair[1] = bmtree_b;
data.epsilon = max_ff(BLI_bvhtree_get_epsilon(bmtree_a->tree),
BLI_bvhtree_get_epsilon(bmtree_b->tree));
return BLI_bvhtree_overlap(
bmtree_a->tree, bmtree_b->tree, r_overlap_tot, bmbvh_overlap_cb, &data);
}
static bool bmbvh_overlap_self_cb(void *userdata, int index_a, int index_b, int thread)
{
if (index_a < index_b) {
return bmbvh_overlap_cb(userdata, index_a, index_b, thread);
}
return false;
}
BVHTreeOverlap *BKE_bmbvh_overlap_self(const BMBVHTree *bmtree, uint *r_overlap_tot)
{
struct BMBVHTree_OverlapData data;
data.tree_pair[0] = bmtree;
data.tree_pair[1] = bmtree;
data.epsilon = BLI_bvhtree_get_epsilon(bmtree->tree);
return BLI_bvhtree_overlap(
bmtree->tree, bmtree->tree, r_overlap_tot, bmbvh_overlap_self_cb, &data);
}