tornavis/source/blender/blenkernel/intern/bvhutils.c

983 lines
23 KiB
C

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Andr Pinto.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenkernel/intern/bvhutils.c
* \ingroup bke
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <assert.h>
#include "DNA_meshdata_types.h"
#include "BLI_utildefines.h"
#include "BLI_linklist.h"
#include "BLI_math.h"
#include "BLI_threads.h"
#include "BKE_DerivedMesh.h"
#include "BKE_editmesh.h"
#include "MEM_guardedalloc.h"
static ThreadRWMutex cache_rwlock = BLI_RWLOCK_INITIALIZER;
/* Math stuff for ray casting on mesh faces and for nearest surface */
float bvhtree_ray_tri_intersection(const BVHTreeRay *ray, const float UNUSED(m_dist), const float v0[3], const float v1[3], const float v2[3])
{
float dist;
if (isect_ray_tri_epsilon_v3(ray->origin, ray->direction, v0, v1, v2, &dist, NULL, FLT_EPSILON))
return dist;
return FLT_MAX;
}
static float sphereray_tri_intersection(const BVHTreeRay *ray, float radius, const float m_dist, const float v0[3], const float v1[3], const float v2[3])
{
float idist;
float p1[3];
float plane_normal[3], hit_point[3];
normal_tri_v3(plane_normal, v0, v1, v2);
madd_v3_v3v3fl(p1, ray->origin, ray->direction, m_dist);
if (isect_sweeping_sphere_tri_v3(ray->origin, p1, radius, v0, v1, v2, &idist, hit_point)) {
return idist * m_dist;
}
return FLT_MAX;
}
/*
* Function adapted from David Eberly's distance tools (LGPL)
* http://www.geometrictools.com/LibFoundation/Distance/Distance.html
*/
float nearest_point_in_tri_surface_squared(
const float v0[3], const float v1[3], const float v2[3],
const float p[3], int *v, int *e, float nearest[3])
{
float diff[3];
float e0[3];
float e1[3];
float A00;
float A01;
float A11;
float B0;
float B1;
float C;
float Det;
float S;
float T;
float sqrDist;
int lv = -1, le = -1;
sub_v3_v3v3(diff, v0, p);
sub_v3_v3v3(e0, v1, v0);
sub_v3_v3v3(e1, v2, v0);
A00 = dot_v3v3(e0, e0);
A01 = dot_v3v3(e0, e1);
A11 = dot_v3v3(e1, e1);
B0 = dot_v3v3(diff, e0);
B1 = dot_v3v3(diff, e1);
C = dot_v3v3(diff, diff);
Det = fabs(A00 * A11 - A01 * A01);
S = A01 * B1 - A11 * B0;
T = A01 * B0 - A00 * B1;
if (S + T <= Det) {
if (S < 0.0f) {
if (T < 0.0f) { /* Region 4 */
if (B0 < 0.0f) {
T = 0.0f;
if (-B0 >= A00) {
S = 1.0f;
sqrDist = A00 + 2.0f * B0 + C;
lv = 1;
}
else {
if (fabsf(A00) > FLT_EPSILON)
S = -B0 / A00;
else
S = 0.0f;
sqrDist = B0 * S + C;
le = 0;
}
}
else {
S = 0.0f;
if (B1 >= 0.0f) {
T = 0.0f;
sqrDist = C;
lv = 0;
}
else if (-B1 >= A11) {
T = 1.0f;
sqrDist = A11 + 2.0f * B1 + C;
lv = 2;
}
else {
if (fabsf(A11) > FLT_EPSILON)
T = -B1 / A11;
else
T = 0.0f;
sqrDist = B1 * T + C;
le = 1;
}
}
}
else { /* Region 3 */
S = 0.0f;
if (B1 >= 0.0f) {
T = 0.0f;
sqrDist = C;
lv = 0;
}
else if (-B1 >= A11) {
T = 1.0f;
sqrDist = A11 + 2.0f * B1 + C;
lv = 2;
}
else {
if (fabsf(A11) > FLT_EPSILON)
T = -B1 / A11;
else
T = 0.0;
sqrDist = B1 * T + C;
le = 1;
}
}
}
else if (T < 0.0f) { /* Region 5 */
T = 0.0f;
if (B0 >= 0.0f) {
S = 0.0f;
sqrDist = C;
lv = 0;
}
else if (-B0 >= A00) {
S = 1.0f;
sqrDist = A00 + 2.0f * B0 + C;
lv = 1;
}
else {
if (fabsf(A00) > FLT_EPSILON)
S = -B0 / A00;
else
S = 0.0f;
sqrDist = B0 * S + C;
le = 0;
}
}
else { /* Region 0 */
/* Minimum at interior lv */
float invDet;
if (fabsf(Det) > FLT_EPSILON)
invDet = 1.0f / Det;
else
invDet = 0.0f;
S *= invDet;
T *= invDet;
sqrDist = S * (A00 * S + A01 * T + 2.0f * B0) +
T * (A01 * S + A11 * T + 2.0f * B1) + C;
}
}
else {
float tmp0, tmp1, numer, denom;
if (S < 0.0f) { /* Region 2 */
tmp0 = A01 + B0;
tmp1 = A11 + B1;
if (tmp1 > tmp0) {
numer = tmp1 - tmp0;
denom = A00 - 2.0f * A01 + A11;
if (numer >= denom) {
S = 1.0f;
T = 0.0f;
sqrDist = A00 + 2.0f * B0 + C;
lv = 1;
}
else {
if (fabsf(denom) > FLT_EPSILON)
S = numer / denom;
else
S = 0.0f;
T = 1.0f - S;
sqrDist = S * (A00 * S + A01 * T + 2.0f * B0) +
T * (A01 * S + A11 * T + 2.0f * B1) + C;
le = 2;
}
}
else {
S = 0.0f;
if (tmp1 <= 0.0f) {
T = 1.0f;
sqrDist = A11 + 2.0f * B1 + C;
lv = 2;
}
else if (B1 >= 0.0f) {
T = 0.0f;
sqrDist = C;
lv = 0;
}
else {
if (fabsf(A11) > FLT_EPSILON)
T = -B1 / A11;
else
T = 0.0f;
sqrDist = B1 * T + C;
le = 1;
}
}
}
else if (T < 0.0f) { /* Region 6 */
tmp0 = A01 + B1;
tmp1 = A00 + B0;
if (tmp1 > tmp0) {
numer = tmp1 - tmp0;
denom = A00 - 2.0f * A01 + A11;
if (numer >= denom) {
T = 1.0f;
S = 0.0f;
sqrDist = A11 + 2.0f * B1 + C;
lv = 2;
}
else {
if (fabsf(denom) > FLT_EPSILON)
T = numer / denom;
else
T = 0.0f;
S = 1.0f - T;
sqrDist = S * (A00 * S + A01 * T + 2.0f * B0) +
T * (A01 * S + A11 * T + 2.0f * B1) + C;
le = 2;
}
}
else {
T = 0.0f;
if (tmp1 <= 0.0f) {
S = 1.0f;
sqrDist = A00 + 2.0f * B0 + C;
lv = 1;
}
else if (B0 >= 0.0f) {
S = 0.0f;
sqrDist = C;
lv = 0;
}
else {
if (fabsf(A00) > FLT_EPSILON)
S = -B0 / A00;
else
S = 0.0f;
sqrDist = B0 * S + C;
le = 0;
}
}
}
else { /* Region 1 */
numer = A11 + B1 - A01 - B0;
if (numer <= 0.0f) {
S = 0.0f;
T = 1.0f;
sqrDist = A11 + 2.0f * B1 + C;
lv = 2;
}
else {
denom = A00 - 2.0f * A01 + A11;
if (numer >= denom) {
S = 1.0f;
T = 0.0f;
sqrDist = A00 + 2.0f * B0 + C;
lv = 1;
}
else {
if (fabsf(denom) > FLT_EPSILON)
S = numer / denom;
else
S = 0.0f;
T = 1.0f - S;
sqrDist = S * (A00 * S + A01 * T + 2.0f * B0) +
T * (A01 * S + A11 * T + 2.0f * B1) + C;
le = 2;
}
}
}
}
/* Account for numerical round-off error */
if (sqrDist < FLT_EPSILON)
sqrDist = 0.0f;
{
float w[3], x[3], y[3], z[3];
copy_v3_v3(w, v0);
copy_v3_v3(x, e0);
mul_v3_fl(x, S);
copy_v3_v3(y, e1);
mul_v3_fl(y, T);
add_v3_v3v3(z, w, x);
add_v3_v3v3(z, z, y);
//sub_v3_v3v3(d, p, z);
copy_v3_v3(nearest, z);
//d = p - ( v0 + S * e0 + T * e1 );
}
*v = lv;
*e = le;
return sqrDist;
}
/*
* BVH from meshes callbacks
*/
/* Callback to bvh tree nearest point. The tree must bust have been built using bvhtree_from_mesh_faces.
* userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. */
static void mesh_faces_nearest_point(void *userdata, int index, const float co[3], BVHTreeNearest *nearest)
{
const BVHTreeFromMesh *data = (BVHTreeFromMesh *) userdata;
MVert *vert = data->vert;
MFace *face = data->face + index;
float *t0, *t1, *t2, *t3;
t0 = vert[face->v1].co;
t1 = vert[face->v2].co;
t2 = vert[face->v3].co;
t3 = face->v4 ? vert[face->v4].co : NULL;
do {
float nearest_tmp[3], dist_sq;
int vertex, edge;
dist_sq = nearest_point_in_tri_surface_squared(t0, t1, t2, co, &vertex, &edge, nearest_tmp);
if (dist_sq < nearest->dist_sq) {
nearest->index = index;
nearest->dist_sq = dist_sq;
copy_v3_v3(nearest->co, nearest_tmp);
normal_tri_v3(nearest->no, t0, t1, t2);
if (t1 == vert[face->v3].co)
nearest->flags |= BVH_ONQUAD;
}
t1 = t2;
t2 = t3;
t3 = NULL;
} while (t2);
}
/* copy of function above (warning, should de-duplicate with editmesh_bvh.c) */
static void editmesh_faces_nearest_point(void *userdata, int index, const float co[3], BVHTreeNearest *nearest)
{
const BVHTreeFromMesh *data = (BVHTreeFromMesh *) userdata;
BMEditMesh *em = data->em_evil;
const BMLoop **ltri = (const BMLoop **)em->looptris[index];
float *t0, *t1, *t2;
t0 = ltri[0]->v->co;
t1 = ltri[1]->v->co;
t2 = ltri[2]->v->co;
{
float nearest_tmp[3], dist_sq;
int vertex, edge;
dist_sq = nearest_point_in_tri_surface_squared(t0, t1, t2, co, &vertex, &edge, nearest_tmp);
if (dist_sq < nearest->dist_sq) {
nearest->index = index;
nearest->dist_sq = dist_sq;
copy_v3_v3(nearest->co, nearest_tmp);
normal_tri_v3(nearest->no, t0, t1, t2);
}
}
}
/* Callback to bvh tree raycast. The tree must bust have been built using bvhtree_from_mesh_faces.
* userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. */
static void mesh_faces_spherecast(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
{
const BVHTreeFromMesh *data = (BVHTreeFromMesh *) userdata;
MVert *vert = data->vert;
MFace *face = data->face + index;
float *t0, *t1, *t2, *t3;
t0 = vert[face->v1].co;
t1 = vert[face->v2].co;
t2 = vert[face->v3].co;
t3 = face->v4 ? vert[face->v4].co : NULL;
do {
float dist;
if (data->sphere_radius == 0.0f)
dist = bvhtree_ray_tri_intersection(ray, hit->dist, t0, t1, t2);
else
dist = sphereray_tri_intersection(ray, data->sphere_radius, hit->dist, t0, t1, t2);
if (dist >= 0 && dist < hit->dist) {
hit->index = index;
hit->dist = dist;
madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist);
normal_tri_v3(hit->no, t0, t1, t2);
if (t1 == vert[face->v3].co)
hit->flags |= BVH_ONQUAD;
}
t1 = t2;
t2 = t3;
t3 = NULL;
} while (t2);
}
/* copy of function above (warning, should de-duplicate with editmesh_bvh.c) */
static void editmesh_faces_spherecast(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
{
const BVHTreeFromMesh *data = (BVHTreeFromMesh *) userdata;
BMEditMesh *em = data->em_evil;
const BMLoop **ltri = (const BMLoop **)em->looptris[index];
float *t0, *t1, *t2;
t0 = ltri[0]->v->co;
t1 = ltri[1]->v->co;
t2 = ltri[2]->v->co;
{
float dist;
if (data->sphere_radius == 0.0f)
dist = bvhtree_ray_tri_intersection(ray, hit->dist, t0, t1, t2);
else
dist = sphereray_tri_intersection(ray, data->sphere_radius, hit->dist, t0, t1, t2);
if (dist >= 0 && dist < hit->dist) {
hit->index = index;
hit->dist = dist;
madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist);
normal_tri_v3(hit->no, t0, t1, t2);
}
}
}
/* Callback to bvh tree nearest point. The tree must bust have been built using bvhtree_from_mesh_edges.
* userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. */
static void mesh_edges_nearest_point(void *userdata, int index, const float co[3], BVHTreeNearest *nearest)
{
const BVHTreeFromMesh *data = (BVHTreeFromMesh *) userdata;
MVert *vert = data->vert;
MEdge *edge = data->edge + index;
float nearest_tmp[3], dist_sq;
float *t0, *t1;
t0 = vert[edge->v1].co;
t1 = vert[edge->v2].co;
closest_to_line_segment_v3(nearest_tmp, co, t0, t1);
dist_sq = len_squared_v3v3(nearest_tmp, co);
if (dist_sq < nearest->dist_sq) {
nearest->index = index;
nearest->dist_sq = dist_sq;
copy_v3_v3(nearest->co, nearest_tmp);
sub_v3_v3v3(nearest->no, t0, t1);
normalize_v3(nearest->no);
}
}
static MVert *get_dm_vert_data_array(DerivedMesh *dm, bool *allocated)
{
CustomData *vert_data = dm->getVertDataLayout(dm);
MVert *mvert = CustomData_get_layer(vert_data, CD_MVERT);
*allocated = false;
if (mvert == NULL) {
mvert = MEM_mallocN(sizeof(MVert) * dm->getNumVerts(dm), "bvh vert data array");
dm->copyVertArray(dm, mvert);
*allocated = true;
}
return mvert;
}
static MEdge *get_dm_edge_data_array(DerivedMesh *dm, bool *allocated)
{
CustomData *edge_data = dm->getEdgeDataLayout(dm);
MEdge *medge = CustomData_get_layer(edge_data, CD_MEDGE);
*allocated = false;
if (medge == NULL) {
medge = MEM_mallocN(sizeof(MEdge) * dm->getNumEdges(dm), "bvh medge data array");
dm->copyEdgeArray(dm, medge);
*allocated = true;
}
return medge;
}
static MFace *get_dm_tessface_data_array(DerivedMesh *dm, bool *allocated)
{
CustomData *tessface_data = dm->getTessFaceDataLayout(dm);
MFace *mface = CustomData_get_layer(tessface_data, CD_MFACE);
*allocated = false;
if (mface == NULL) {
int numTessFaces = dm->getNumTessFaces(dm);
if (numTessFaces > 0) {
mface = MEM_mallocN(sizeof(MFace) * numTessFaces, "bvh mface data array");
dm->copyTessFaceArray(dm, mface);
*allocated = true;
}
}
return mface;
}
/*
* BVH builders
*/
/* Builds a bvh tree.. where nodes are the vertexs of the given mesh */
BVHTree *bvhtree_from_mesh_verts(BVHTreeFromMesh *data, DerivedMesh *dm, float epsilon, int tree_type, int axis)
{
BVHTree *tree;
MVert *vert;
bool vert_allocated;
BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_READ);
tree = bvhcache_find(&dm->bvhCache, BVHTREE_FROM_VERTICES);
BLI_rw_mutex_unlock(&cache_rwlock);
vert = get_dm_vert_data_array(dm, &vert_allocated);
/* Not in cache */
if (tree == NULL) {
BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_WRITE);
tree = bvhcache_find(&dm->bvhCache, BVHTREE_FROM_VERTICES);
if (tree == NULL) {
int i;
int numVerts = dm->getNumVerts(dm);
if (vert != NULL) {
tree = BLI_bvhtree_new(numVerts, epsilon, tree_type, axis);
if (tree != NULL) {
for (i = 0; i < numVerts; i++) {
BLI_bvhtree_insert(tree, i, vert[i].co, 1);
}
BLI_bvhtree_balance(tree);
/* Save on cache for later use */
// printf("BVHTree built and saved on cache\n");
bvhcache_insert(&dm->bvhCache, tree, BVHTREE_FROM_VERTICES);
}
}
}
BLI_rw_mutex_unlock(&cache_rwlock);
}
else {
// printf("BVHTree is already build, using cached tree\n");
}
/* Setup BVHTreeFromMesh */
memset(data, 0, sizeof(*data));
data->tree = tree;
if (data->tree) {
data->cached = true;
/* a NULL nearest callback works fine
* remember the min distance to point is the same as the min distance to BV of point */
data->nearest_callback = NULL;
data->raycast_callback = NULL;
data->vert = vert;
data->vert_allocated = vert_allocated;
data->face = get_dm_tessface_data_array(dm, &data->face_allocated);
data->sphere_radius = epsilon;
}
else {
if (vert_allocated) {
MEM_freeN(vert);
}
}
return data->tree;
}
/* Builds a bvh tree.. where nodes are the faces of the given dm. */
BVHTree *bvhtree_from_mesh_faces(BVHTreeFromMesh *data, DerivedMesh *dm, float epsilon, int tree_type, int axis)
{
BMEditMesh *em = data->em_evil;
const int bvhcache_type = em ? BVHTREE_FROM_FACES_EDITMESH : BVHTREE_FROM_FACES;
BVHTree *tree;
MVert *vert;
MFace *face;
bool vert_allocated = false, face_allocated = false;
BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_READ);
tree = bvhcache_find(&dm->bvhCache, bvhcache_type);
BLI_rw_mutex_unlock(&cache_rwlock);
if (em == NULL) {
vert = get_dm_vert_data_array(dm, &vert_allocated);
face = get_dm_tessface_data_array(dm, &face_allocated);
}
/* Not in cache */
if (tree == NULL) {
BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_WRITE);
tree = bvhcache_find(&dm->bvhCache, bvhcache_type);
if (tree == NULL) {
int i;
int numFaces;
/* BMESH specific check that we have tessfaces,
* we _could_ tessellate here but rather not - campbell
*
* this assert checks we have tessfaces,
* if not caller should use DM_ensure_tessface() */
if (em) {
numFaces = em->tottri;
}
else {
numFaces = dm->getNumTessFaces(dm);
BLI_assert(!(numFaces == 0 && dm->getNumPolys(dm) != 0));
}
if (numFaces != 0) {
/* Create a bvh-tree of the given target */
// printf("%s: building BVH, total=%d\n", __func__, numFaces);
tree = BLI_bvhtree_new(numFaces, epsilon, tree_type, axis);
if (tree != NULL) {
if (em) {
const struct BMLoop *(*looptris)[3] = (void *)em->looptris;
/* avoid double-up on face searches for quads-ngons */
bool insert_prev = false;
BMFace *f_prev = NULL;
/* data->em_evil is only set for snapping, and only for the mesh of the object
* which is currently open in edit mode. When set, the bvhtree should not contain
* faces that will interfere with snapping (e.g. faces that are hidden/selected
* or faces that have selected verts).*/
/* Insert BMesh-tessellation triangles into the bvh tree, unless they are hidden
* and/or selected. Even if the faces themselves are not selected for the snapped
* transform, having a vertex selected means the face (and thus it's tessellated
* triangles) will be moving and will not be a good snap targets.*/
for (i = 0; i < em->tottri; i++) {
const BMLoop **ltri = looptris[i];
BMFace *f = ltri[0]->f;
bool insert;
/* Start with the assumption the triangle should be included for snapping. */
if (f == f_prev) {
insert = insert_prev;
}
else {
if (BM_elem_flag_test(f, BM_ELEM_SELECT) || BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
/* Don't insert triangles tessellated from faces that are hidden
* or selected*/
insert = false;
}
else {
BMLoop *l_iter, *l_first;
insert = true;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
if (BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) {
/* Don't insert triangles tessellated from faces that have
* any selected verts.*/
insert = false;
break;
}
} while ((l_iter = l_iter->next) != l_first);
}
/* skip if face doesn't change */
f_prev = f;
insert_prev = insert;
}
if (insert) {
/* No reason found to block hit-testing the triangle for snap,
* so insert it now.*/
float co[3][3];
copy_v3_v3(co[0], ltri[0]->v->co);
copy_v3_v3(co[1], ltri[1]->v->co);
copy_v3_v3(co[2], ltri[2]->v->co);
BLI_bvhtree_insert(tree, i, co[0], 3);
}
}
}
else {
if (vert != NULL && face != NULL) {
for (i = 0; i < numFaces; i++) {
float co[4][3];
copy_v3_v3(co[0], vert[face[i].v1].co);
copy_v3_v3(co[1], vert[face[i].v2].co);
copy_v3_v3(co[2], vert[face[i].v3].co);
if (face[i].v4)
copy_v3_v3(co[3], vert[face[i].v4].co);
BLI_bvhtree_insert(tree, i, co[0], face[i].v4 ? 4 : 3);
}
}
}
BLI_bvhtree_balance(tree);
/* Save on cache for later use */
// printf("BVHTree built and saved on cache\n");
bvhcache_insert(&dm->bvhCache, tree, bvhcache_type);
}
}
}
BLI_rw_mutex_unlock(&cache_rwlock);
}
else {
// printf("BVHTree is already build, using cached tree\n");
}
/* Setup BVHTreeFromMesh */
memset(data, 0, sizeof(*data));
data->tree = tree;
data->em_evil = em;
if (data->tree) {
data->cached = true;
if (em) {
data->nearest_callback = editmesh_faces_nearest_point;
data->raycast_callback = editmesh_faces_spherecast;
}
else {
data->nearest_callback = mesh_faces_nearest_point;
data->raycast_callback = mesh_faces_spherecast;
data->vert = vert;
data->vert_allocated = vert_allocated;
data->face = face;
data->face_allocated = face_allocated;
}
data->sphere_radius = epsilon;
}
else {
if (vert_allocated) {
MEM_freeN(vert);
}
if (face_allocated) {
MEM_freeN(face);
}
}
return data->tree;
}
/* Builds a bvh tree.. where nodes are the faces of the given dm. */
BVHTree *bvhtree_from_mesh_edges(BVHTreeFromMesh *data, DerivedMesh *dm, float epsilon, int tree_type, int axis)
{
BVHTree *tree;
MVert *vert;
MEdge *edge;
bool vert_allocated, edge_allocated;
BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_READ);
tree = bvhcache_find(&dm->bvhCache, BVHTREE_FROM_EDGES);
BLI_rw_mutex_unlock(&cache_rwlock);
vert = get_dm_vert_data_array(dm, &vert_allocated);
edge = get_dm_edge_data_array(dm, &edge_allocated);
/* Not in cache */
if (tree == NULL) {
BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_WRITE);
tree = bvhcache_find(&dm->bvhCache, BVHTREE_FROM_EDGES);
if (tree == NULL) {
int i;
int numEdges = dm->getNumEdges(dm);
if (vert != NULL && edge != NULL) {
/* Create a bvh-tree of the given target */
tree = BLI_bvhtree_new(numEdges, epsilon, tree_type, axis);
if (tree != NULL) {
for (i = 0; i < numEdges; i++) {
float co[4][3];
copy_v3_v3(co[0], vert[edge[i].v1].co);
copy_v3_v3(co[1], vert[edge[i].v2].co);
BLI_bvhtree_insert(tree, i, co[0], 2);
}
BLI_bvhtree_balance(tree);
/* Save on cache for later use */
// printf("BVHTree built and saved on cache\n");
bvhcache_insert(&dm->bvhCache, tree, BVHTREE_FROM_EDGES);
}
}
}
BLI_rw_mutex_unlock(&cache_rwlock);
}
else {
// printf("BVHTree is already build, using cached tree\n");
}
/* Setup BVHTreeFromMesh */
memset(data, 0, sizeof(*data));
data->tree = tree;
if (data->tree) {
data->cached = true;
data->nearest_callback = mesh_edges_nearest_point;
data->raycast_callback = NULL;
data->vert = vert;
data->vert_allocated = vert_allocated;
data->edge = edge;
data->edge_allocated = edge_allocated;
data->sphere_radius = epsilon;
}
else {
if (vert_allocated) {
MEM_freeN(vert);
}
if (edge_allocated) {
MEM_freeN(edge);
}
}
return data->tree;
}
/* Frees data allocated by a call to bvhtree_from_mesh_*. */
void free_bvhtree_from_mesh(struct BVHTreeFromMesh *data)
{
if (data->tree) {
if (!data->cached)
BLI_bvhtree_free(data->tree);
if (data->vert_allocated) {
MEM_freeN(data->vert);
}
if (data->edge_allocated) {
MEM_freeN(data->edge);
}
if (data->face_allocated) {
MEM_freeN(data->face);
}
memset(data, 0, sizeof(*data));
}
}
/* BVHCache */
typedef struct BVHCacheItem {
int type;
BVHTree *tree;
} BVHCacheItem;
static void bvhcacheitem_set_if_match(void *_cached, void *_search)
{
BVHCacheItem *cached = (BVHCacheItem *)_cached;
BVHCacheItem *search = (BVHCacheItem *)_search;
if (search->type == cached->type) {
search->tree = cached->tree;
}
}
BVHTree *bvhcache_find(BVHCache *cache, int type)
{
BVHCacheItem item;
item.type = type;
item.tree = NULL;
BLI_linklist_apply(*cache, bvhcacheitem_set_if_match, &item);
return item.tree;
}
void bvhcache_insert(BVHCache *cache, BVHTree *tree, int type)
{
BVHCacheItem *item = NULL;
assert(tree != NULL);
assert(bvhcache_find(cache, type) == NULL);
item = MEM_mallocN(sizeof(BVHCacheItem), "BVHCacheItem");
assert(item != NULL);
item->type = type;
item->tree = tree;
BLI_linklist_prepend(cache, item);
}
void bvhcache_init(BVHCache *cache)
{
*cache = NULL;
}
static void bvhcacheitem_free(void *_item)
{
BVHCacheItem *item = (BVHCacheItem *)_item;
BLI_bvhtree_free(item->tree);
MEM_freeN(item);
}
void bvhcache_free(BVHCache *cache)
{
BLI_linklist_free(*cache, (LinkNodeFreeFP)bvhcacheitem_free);
*cache = NULL;
}