star-uvm

Spatial structuring of unstructured volumetric meshes
git clone git://git.meso-star.fr/star-uvm.git
Log | Files | Refs | README | LICENSE

commit f79261fed9077859666f05c97d27bb64b9ce3894
parent 21b67b362f5bf7750699bdbcf4fc627f729c3b1f
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed,  9 Dec 2020 17:54:09 +0100

First draft of the aabb/tetrahedron intersection test

Diffstat:
Msrc/suvm.h | 8+++-----
Msrc/suvm_volume.h | 40++++++++++++++++++++++++++++++++++++----
Msrc/suvm_volume_intersect_aabb.c | 296+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 335 insertions(+), 9 deletions(-)

diff --git a/src/suvm.h b/src/suvm.h @@ -151,11 +151,9 @@ suvm_volume_at struct suvm_primitive* prim, /* Geometric primitive where `pos' lies */ double barycentric_coords[4]); /* `pos' into the primitive */ -/* Iterate over the volumetric primitives that intersect the submitted Axis - * Aligned Bounding Box and invoke the `clbk' function onto them. A primitive - * is included in the AABB if at least one of its vertices is _strictly_ - * included into the submitted boundaries; a vertex lying exactly on `low' or - * `upp' are considered outside the AABB */ +/* Iterate over the volumetric primitives whose axis aligned bounding box + * intersects the submitted Axis Aligned Bounding Box and invoke the `clbk' + * function onto them. */ SUVM_API res_T suvm_volume_intersect_aabb (struct suvm_volume* volume, diff --git a/src/suvm_volume.h b/src/suvm_volume.h @@ -23,6 +23,22 @@ #include <embree3/rtcore.h> +/* + * Tetrahedron geometric layout + * + * 3 (top vertex) facet 0 = {0,1,2} + * + facet 1 = {0,3,1} + * /|`. facet 2 = {1,3,2} + * / | `. facet 3 = {2,3,0} + * / | `. + * / | `. + * 0 +....|.........+ 2 + * `. | _,-' + * `,|_,-' + * + + * 1 + */ + /* Forward declarations */ struct suvm_device; struct mem_allocator; @@ -68,7 +84,7 @@ struct ALIGN(16) node_leaf { struct suvm_volume { struct darray_u32 indices; /* List of uint32_t[4] */ - struct darray_float positions; /* List of double[3] */ + struct darray_float positions; /* List of float[3] */ struct darray_float normals; /* List of per tetrahedron facet normals */ struct buffer prim_data; /* Per primitive data */ struct buffer vert_data; /* Per vertex data */ @@ -105,6 +121,22 @@ volume_get_vertices_count(const struct suvm_volume* vol) return sz / 3; } +static FINLINE float* +volume_get_primitive_vertex_position + (const struct suvm_volume* vol, + const size_t iprim, + const size_t ivert, /* In [0, 3] */ + float pos[3]) +{ + const uint32_t* ids; + ASSERT(vol && iprim < volume_get_primitives_count(vol) && pos); + ids = darray_u32_cdata_get(&vol->indices) + iprim*4/*#vertex per tetra*/; + pos[0] = darray_float_cdata_get(&vol->positions)[ids[ivert]*3 + 0]; + pos[1] = darray_float_cdata_get(&vol->positions)[ids[ivert]*3 + 1]; + pos[2] = darray_float_cdata_get(&vol->positions)[ids[ivert]*3 + 2]; + return pos; +} + static INLINE float* volume_compute_tetrahedron_normal (const struct suvm_volume* vol, @@ -145,16 +177,16 @@ static FINLINE float* volume_get_tetrahedron_normal (const struct suvm_volume* vol, const size_t itetra, - const int ifacet /* In [0, 3] */, + const int ifacet, /* In [0, 3] */ float normal[3]) { - const size_t id = - (itetra*4/*#facets per tetra*/ + (size_t)ifacet)*3/*#coords per normal*/; ASSERT(vol && itetra < volume_get_primitives_count(vol) && normal); if(!darray_float_size_get(&vol->normals)) { volume_compute_tetrahedron_normal(vol, itetra, ifacet, normal); } else { + const size_t id = + (itetra*4/*#facets per tetra*/ + (size_t)ifacet)*3/*#coords per normal*/; ASSERT(id + 3 <= darray_float_size_get(&vol->normals)); normal[0] = darray_float_cdata_get(&vol->normals)[id+0]; normal[1] = darray_float_cdata_get(&vol->normals)[id+1]; diff --git a/src/suvm_volume_intersect_aabb.c b/src/suvm_volume_intersect_aabb.c @@ -17,9 +17,305 @@ #include "suvm_device.h" #include "suvm_volume.h" +#include <rsys/float2.h> +#include <rsys/float3.h> + +enum { X, Y, Z, AXES_COUNT }; /* Syntactic sugar */ + +struct tetrahedron { + float v[4][AXES_COUNT]; /* Tetrahedron vertices */ + float N[4][AXES_COUNT]; /* Tetrahedron normals */ + float D[4]; /* Slope parameter of the plane equation Ax + By + Cz + D = 0 */ + + /* 2D equation of the silhouette edges projected along the X, Y and Z axis of + * the form Ax + By + C = 0, with (A, B) the normals of the edge */ + float Ep[AXES_COUNT][4/*#edges max*/][3]; + + /* Number of silhouette edges in the ZY, XZ and XY planes (3 or 4) */ + int nEp[AXES_COUNT]; + + /* Tetrahedron axis aligned bounding box */ + float lower[AXES_COUNT]; + float upper[AXES_COUNT]; +}; + /******************************************************************************* * Helper functions ******************************************************************************/ +static INLINE void +tetrahedron_setup + (const struct suvm_volume* vol, + const float low[3], + const float upp[3], + const size_t itetra, + struct tetrahedron* tetra) +{ +#ifndef NDEBUG + float center[AXES_COUNT]; /* Center of the tetrahedron */ +#endif + float e[6][AXES_COUNT]; + float (*v)[AXES_COUNT]; + float (*N)[AXES_COUNT]; + float (*Ep)[4][3]; + int i; + ASSERT(vol && low && upp && tetra); + + /* Fetch tetrahedron vertices */ + volume_get_primitive_vertex_position(vol, itetra, 0, tetra->v[0]); + volume_get_primitive_vertex_position(vol, itetra, 1, tetra->v[1]); + volume_get_primitive_vertex_position(vol, itetra, 2, tetra->v[2]); + volume_get_primitive_vertex_position(vol, itetra, 4, tetra->v[3]); + v = tetra->v; + +#ifndef NDEBUG + /* Compute the center of the tetrahedron */ + center[X] = (v[0][X] + v[1][X] + v[2][X] + v[3][X]) * 0.25f; + center[Y] = (v[0][Y] + v[1][Y] + v[2][Y] + v[3][Y]) * 0.25f; + center[Z] = (v[0][Z] + v[1][Z] + v[2][Z] + v[3][Z]) * 0.25f; +#endif + + /* Check argument consistency */ + ASSERT(MMIN(MMIN(v[0][X], v[1][X]), MMIN(v[2][X], v[3][X])) == low[X]); + ASSERT(MMIN(MMIN(v[0][Y], v[1][Y]), MMIN(v[2][Y], v[3][Y])) == low[Y]); + ASSERT(MMIN(MMIN(v[0][Z], v[1][Z]), MMIN(v[2][Z], v[3][Z])) == low[Z]); + ASSERT(MMAX(MMAX(v[0][X], v[1][X]), MMAX(v[2][X], v[3][X])) == upp[X]); + ASSERT(MMAX(MMAX(v[0][Y], v[1][Y]), MMAX(v[2][Y], v[3][Y])) == upp[Y]); + ASSERT(MMAX(MMAX(v[0][Z], v[1][Z]), MMAX(v[2][Z], v[3][Z])) == upp[Z]); + + /* Setup tetrahedron AABB */ + f3_set(tetra->lower, low); + f3_set(tetra->upper, upp); + + /* Fetch tetrahedron normals */ + volume_get_tetrahedron_normal(vol, itetra, 0, tetra->N[0]); + volume_get_tetrahedron_normal(vol, itetra, 1, tetra->N[1]); + volume_get_tetrahedron_normal(vol, itetra, 2, tetra->N[2]); + volume_get_tetrahedron_normal(vol, itetra, 3, tetra->N[3]); + N = tetra->N; + + /* Compute the slope of the planes */ + tetra->D[0] = -f3_dot(tetra->N[0], v[0]); + tetra->D[1] = -f3_dot(tetra->N[1], v[1]); + tetra->D[2] = -f3_dot(tetra->N[2], v[2]); + tetra->D[3] = -f3_dot(tetra->N[3], v[3]); + + /* Compute tetrahedron edges */ + f3_sub(e[0], v[1], v[0]); + f3_sub(e[1], v[2], v[1]); + f3_sub(e[2], v[0], v[2]); + f3_sub(e[3], v[0], v[3]); + f3_sub(e[4], v[1], v[3]); + f3_sub(e[5], v[2], v[3]); + + /* Detect the silhouette edges of the tetrahedron once projected in the YZ, + * ZX and XY planes, and compute their 2D equation in the projected plane. The + * variable 'i' defines the projection axis which is successively X (YZ + * plane), Y (ZX plane) and Z (XY plane). The axes of the corresponding 2D + * repairs are defined by the 'j' and 'k' variables. */ + Ep = tetra->Ep; + FOR_EACH(i, 0, AXES_COUNT) { + /* On 1st iteration 'j' and 'k' define the YZ plane + * On 2nd iteration 'j' and 'k' define the ZX plane + * On 3rd ietration 'j' and 'k' define the XY plane */ + const int j = (i + 1) % AXES_COUNT; + const int k = (j + 1) % AXES_COUNT; + + /* Register the number of detected silhouette edges */ + int n = 0; + + /* To detect the silhouette edges, check the sign of the normals of two + * adjacent facets for the coordinate of the projection axis. If the signs + * are the same, the facets look at the same direction regarding the + * projection axis. The other case means that one facet points toward the + * projection axis whole the other one points to the opposite direction: + * this is the definition of a silhouette edge. + * + * Once detected, we compute the equation of the silhouette edge: + * + * Ax + By + C = 0 + * + * with A and B the coordinates of the edge normals and C the slope of the + * edge. Computing the normal is actually as simple as swizzling the + * coordinates of the edges projected in 2D and sign tuning. Let the + * projection axis X (i==X) and thus the YZ projection plane (j==Y && + * k==Z). Assuming that the first edge 'e[0]' is a silhouette edge, ie the + * edge between the facet0 and the facet1 (refers to the suvm_volume.h for + * the geometric layout of a tetrahedron) once projected in the YZ plane + * the edge is {0, e[0][Y], e[0][Z]}. Its normal N can is defined as the + * cross product between this projected edge ands the projection axis: + * + * | 0 | | 1 | | 0 | + * N = | e[0][Y] | X | 0 | = | e[0][Z] | + * | e[0][Z] | | 0 | |-e[0][Y] | + * + * In the projection plane, the _unormalized_ normal of the {e[0][Y], + * e[0][Z]} edge is thus {e[0][Z], -e[0][Y]}. More generally, the normal of + * an edge 'I' {e[I][j], e[I][k]} projected along the 'i' axis in the 'jk' + * plane is {e[I][k],-e[I]j]}. Anyway, one has to revert the normal + * orientation to ensure a specific convention wrt the tetrahedron + * footprint. In our case we ensure that the normal points toward the + * footprint. Finalle the edge slope 'C' can be computed as: + * | A | + * C =- | B | . | Pj, Pk | + * + * with {Pj, Pk} a point belonging to the edge as for instance one of its + * vertices. */ + if(signf(N[0][i]) != signf(N[1][i])) { /* The edge 0 is silhouette */ + f2_normalize(Ep[i][n], f2(Ep[i][n], e[0][k], -e[0][j])); + if(N[0][i] > 0) f2_minus(Ep[i][n], Ep[i][n]); + Ep[i][n][2] = -f2_dot(Ep[i][n], v[0]); + ++n; + } + if(signf(N[0][i]) != signf(N[2][i])) { /* The edge 1 is silhouette */ + f2_normalize(Ep[i][n], f2(Ep[i][n], e[1][k], -e[1][j])); + if(N[0][i] > 0) f2_minus(Ep[i][n], Ep[i][n]); + Ep[i][n][2] = -f2_dot(Ep[i][n], v[1]); + ++n; + } + if(signf(N[0][i]) != signf(N[3][i])) { /* The edge 2 is silhouette */ + f2_normalize(Ep[i][n], f2(Ep[i][n], e[2][k], -e[2][j])); + if(N[0][i] > 0) f2_minus(Ep[i][n], Ep[i][n]); + Ep[i][n][2] = -f2_dot(Ep[i][n], v[2]); + ++n; + } + if(signf(N[1][i]) != signf(N[3][i])) { /* The edge 3 is silhouette */ + f2_normalize(Ep[i][n], f2(Ep[i][n], e[3][k], -e[3][j])); + if(N[1][i] > 0) f2_minus(Ep[i][n], Ep[i][n]); + Ep[i][n][2] = -f2_dot(Ep[i][n], v[0]); + ++n; + } + if(signf(N[1][i]) != signf(N[2][i])) { /* The edge 4 is silhouette */ + f2_normalize(Ep[X][n], f2(Ep[i][n], e[4][k], -e[4][j])); + if(N[2][i] > 0) f2_minus(Ep[i][n], Ep[i][n]); + Ep[i][n][2] = -f2_dot(Ep[i][n], v[1]); + ++n; + } + if(signf(N[2][i]) != signf(N[3][i])) { /* The edge 5 is silhouette */ + f2_normalize(Ep[i][n], f2(Ep[i][n], e[5][k], -e[5][j])); + if(N[3][i] > 0) f2_minus(Ep[i][n], Ep[i][n]); + Ep[i][n][2] = -f2_dot(Ep[i][n], v[2]); + ++n; + } + /* A tetrahedron can have 3 or 4 silhouette edges once projected in 2D */ + ASSERT(n == 3 || n == 4); + tetra->nEp[i] = n; /* Register the #silouhette edges for this project axis */ + +#ifndef NDEBUG + /* Assert that the normals points toward the expected half space */ + { + int iedge; + FOR_EACH(iedge, 0, n) { + /* Evaluate the edge equation regarding the projected polyhedron center + * and check that its sign is positive */ + const float dst = + Ep[i][iedge][0] * center[j] + + Ep[i][iedge][1] * center[k] + + Ep[i][iedge][2]; + ASSERT(dst > 0); + } +#endif + } +} + +/* Define if an axis aligned bounding box and a tetrahedron are intersected. + * Its implementation follows the algorithms proposed by N. Green in "Detecting + * intersection of a rectangular solid and a convex polyhedron" (Graphics Gems + * IV, 1994, p74--82) and by H. Ratschek and J. Rokne in "Test for intersection + * between box and tetrahedron" (Internation Journal of Computer Mathematics, + * 1997, p191--204) */ +static INLINE int +aabb_intersect_tetrahedron + (const float low[3], + const float upp[3], + const struct tetrahedron* tetra) +{ + float intersect_aabb_low[3]; + float intersect_aabb_upp[3]; + int i; + int nplanes_including_aabb; + ASSERT(low && upp && tetra); + ASSERT(low[X] < upp[X]); + ASSERT(low[Y] < upp[Y]); + ASSERT(low[Z] < upp[Z]); + + /* Check the intersection between the AABB and the tetra AABB */ + FOR_EACH(i, 0, AXES_COUNT) { + intersect_aabb_low[i] = MMAX(low[i], tetra->lower[i]); + intersect_aabb_upp[i] = MMIN(upp[i], tetra->upper[i]); + if(intersect_aabb_low[i] > intersect_aabb_upp[i]) /* Do not intersect */ + return 0; + } + + /* Check if the tetrahedron is included into the aabb */ + if(intersect_aabb_low[X] == low[X] + && intersect_aabb_low[Y] == low[Y] + && intersect_aabb_low[Z] == low[Z]) { + return 1; + } + + /* #planes that totally includes the aabb on its positive side */ + nplanes_including_aabb = 0; + + /* Check the tetrahedron planes against the aabb */ + FOR_EACH(i, 0, 4) { + float p[3], n[3]; + + /* Define the aabb vertices that is the farthest in the positive/negative + * direction of the face normal. */ + f3_set(p, upp); + f3_set(n, low); + if(tetra->N[i][X] < 0) SWAP(float, p[X], n[X]); + if(tetra->N[i][Y] < 0) SWAP(float, p[Y], n[Y]); + if(tetra->N[i][Z] < 0) SWAP(float, p[Z], n[Z]); + + /* Check that the box is totally outside the plane, To do this, check that + * 'p', aka the farthest aabb vertex in the positive direction of the plane + * normal, is not in the positive half space. In this case, the aabb is + * entirely outside the tetrahedron */ + if((f3_dot(tetra->N[i], p) + tetra->D[i]) < 0) return 0; + + /* Check if the box is totally inside the given plane. To do this, check + * that 'n', aka the farthest aabb vertex in the negative direction of the + * plane normal, is inside the positive half space */ + if((f3_dot(tetra->N[i], n) + tetra->D[i]) > 0) { + /* Register this plane as a plane including the aabb */ + nplanes_including_aabb += 1; + } + } + + /* Check if the aabb is entirely included into the tetrahedron */ + if(nplanes_including_aabb == 4) return 1; + + /* For each silhouette edge in each projection plane, check if it totally + * exclude the projected aabb */ + FOR_EACH(i, 0, AXES_COUNT) { + /* Compute the remaining axes. + * On 1st iteration 'j' and 'k' define the YZ plane + * On 2nd iteration 'j' and 'k' define the ZX plane + * On 3rd ietration 'j' and 'k' define the XY plane */ + const int j = (i + 1) % AXES_COUNT; + const int k = (j + 1) % AXES_COUNT; + + int iedge; + FOR_EACH(iedge, 0, tetra->nEp[i]) { + float p[2]; + + /* Define the aabb vertices that is the farthest in the positive/negative + * direction of the normal of the silhouette edge. */ + p[0] = tetra->Ep[i][iedge][0] > 0 ? upp[j] : low[j]; + p[1] = tetra->Ep[i][iedge][1] > 0 ? upp[k] : low[k]; + + /* Check that the projected aabb along the 'i'th axis is totally outside + * the current silhouette edge. That means that the aabb is entirely + * outside the tetrahedron and thus that they do not intersect */ + if((f2_dot(tetra->Ep[i][iedge], p) + tetra->Ep[i][iedge][2]) < 0) return 0; + } + } + + /* The tetrahedron and the aabb are partially intersecting */ + return 1; +} + static INLINE int aabb_intersect_aabb (const float low0[3],