star-3d

Surface structuring for efficient 3D geometric queries
git clone git://git.meso-star.fr/star-3d.git
Log | Files | Refs | README | LICENSE

commit eb0b5365e5a9ab5692e350700cf77610cf4fcc9b
parent 9091cf25808a0c164c1ea2555ddf5eef895f985f
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Tue, 12 Jan 2021 15:58:34 +0100

Add tests on raytrace and closest_point accuracy

Diffstat:
Msrc/test_s3d_closest_point.c | 129++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/test_s3d_trace_ray.c | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 246 insertions(+), 3 deletions(-)

diff --git a/src/test_s3d_closest_point.c b/src/test_s3d_closest_point.c @@ -34,6 +34,7 @@ #include "test_s3d_cbox.h" #include "test_s3d_utils.h" +#include <rsys/float2.h> #include <rsys/float3.h> #include <rsys/float33.h> #include <limits.h> @@ -853,7 +854,7 @@ test_single_triangle(struct s3d_device* dev) float closest_pos[3] = {0,0,0}; float low[3], upp[3], mid[3]; union { float f; uint32_t ui32; } ucast; - size_t i; + size_t a, i; f3(vertices+0, -0.5f, -0.3f, 0.1f); f3(vertices+3, -0.4f, 0.2f, 0.3f); @@ -950,6 +951,132 @@ test_single_triangle(struct s3d_device* dev) CHK(s3d_shape_ref_put(msh) == RES_OK); CHK(s3d_scene_view_ref_put(view) == RES_OK); CHK(s3d_scene_ref_put(scn) == RES_OK); + + /* Check accuracy on a configuration whose analytic distance is known */ + FOR_EACH(a, 0, 16) { + const float amplitude = exp2f((float)a); + const float eps = 5e-6f * amplitude; + FOR_EACH(i, 0, 1000) { + float A[3], B[3], C[3], AB[3], AC[3], BC[3], N[3]; + int j, n; + + /* Randomly generate a triangle ABC */ + FOR_EACH(n, 0, 3) + A[n] = (rand_canonic() - 0.5f) * amplitude; + do { + FOR_EACH(n, 0, 3) B[n] = (rand_canonic() - 0.5f) * amplitude; + } while (f3_eq_eps(A, B, eps)); + do { + FOR_EACH(n, 0, 3) C[n] = (rand_canonic() - 0.5f) * amplitude; + } while (f3_eq_eps(A, C, eps) || f3_eq_eps(B, C, eps)); + + f3_sub(AB, B, A); + f3_sub(AC, C, A); + f3_sub(BC, C, B); + f3_cross(N, AB, AC); + f3_normalize(N, N); + + f3_set(vertices + 0, A); + f3_set(vertices + 3, B); + f3_set(vertices + 6, C); + + CHK(s3d_scene_create(dev, &scn) == RES_OK); + CHK(s3d_shape_create_mesh(dev, &msh) == RES_OK); + CHK(s3d_scene_attach_shape(scn, msh) == RES_OK); + + vdata.usage = S3D_POSITION; + vdata.type = S3D_FLOAT3; + vdata.get = triangle_get_pos; + CHK(s3d_mesh_setup_indexed_vertices + (msh, 1, triangle_get_ids, 3, &vdata, 1, vertices) == RES_OK); + + CHK(s3d_scene_view_create(scn, S3D_TRACE, &view) == RES_OK); + + FOR_EACH(j, 0, 1000) { + float proj[3]; /* Projection of pos on the line */ + float AP[3], BP[3], CP[3], closest[3], tmp[3]; + float u, v, w, h, x, dist, d; + + /* Randomly generate a pos not on the triangle + * with know position wrt the problem: pos = A + u.AB + v.AC + k.N */ + u = 3 * rand_canonic() - 1; + v = 3 * rand_canonic() - 1; + w = 1 - u - v; + h = (2 * rand_canonic() - 1) * amplitude; + f3_add(proj, A, f3_add(proj, f3_mulf(proj, AB, u), f3_mulf(tmp, AC, v))); + f3_add(pos, proj, f3_mulf(pos, N, h)); + f3_sub(AP, proj, A); + f3_sub(BP, proj, B); + f3_sub(CP, proj, C); + + /* Compute closest point */ + CHK(s3d_scene_view_closest_point(view, pos, (float)INF, NULL, &hit) + == RES_OK); + CHK(!S3D_HIT_NONE(&hit)); + CHK(s3d_primitive_get_attrib(&hit.prim, S3D_POSITION, hit.uv, &attr) + == RES_OK); + + /* Check result + * Due to known uv lack of accuracy we mainly check distance */ + if(u >= 0 && v >= 0 && w >= 0) { + /* proj is inside the triangle and is the closest point */ + f3_set(closest, proj); + dist = fabsf(h); + } else { + /* proj is outside the triangle */ + float lab2 = f3_dot(AB, AB); + float lac2 = f3_dot(AC, AC); + float lbc2 = f3_dot(BC, BC); + if(w >= 0 && u < 0) { + /* proj is closest to either AB or AC */ + x = f3_dot(AP, AB); + if(v < 0 && x > 0) { + /* proj is closest to AB */ + f3_add(closest, A, f3_mulf(tmp, AB, MMIN(1, x / lab2))); + } else { + /* proj is closest to AC */ + f3_add(closest, A, + f3_mulf(tmp, AC, MMIN(1, MMAX(0, f3_dot(AP, AC) / lac2)))); + } + } + else if(u >= 0 && v < 0) { + /* proj is closest to either BC or BA */ + x = f3_dot(BP, BC); + if(w < 0 && x > 0) { + /* proj is closest to BC */ + f3_add(closest, B, f3_mulf(tmp, BC, MMIN(1, x / lbc2))); + } else { + /* proj is closest to BA */ + f3_add(closest, B, + f3_mulf(tmp, AB, -MMIN(1, MMAX(0, -f3_dot(BP, AB) / lab2)))); + } + } + else if(v >= 0 && w < 0) { + /* proj is closest to either CA or CB */ + x = -f3_dot(CP, AC); + if(u < 0 && x > 0) { + /* proj is closest to CA */ + f3_add(closest, C, f3_mulf(tmp, AC, -MMIN(1, x / lac2))); + } else { + /* proj is closest to CB */ + f3_add(closest, C, + f3_mulf(tmp, BC, -MMIN(1, MMAX(0, -f3_dot(CP, BC) / lbc2)))); + } + } + else { ASSERT(0); } + dist = f3_len(f3_sub(tmp, pos, closest)); + } + CHK(eq_epsf(hit.distance, dist, eps)); + /* Intersection-point's position is less accurate than hit distance */ + d = f3_len(f3_sub(tmp, closest, attr.value)); + CHK(d <= 10 * eps); + } + + CHK(s3d_shape_ref_put(msh) == RES_OK); + CHK(s3d_scene_view_ref_put(view) == RES_OK); + CHK(s3d_scene_ref_put(scn) == RES_OK); + } + } } static void diff --git a/src/test_s3d_trace_ray.c b/src/test_s3d_trace_ray.c @@ -60,6 +60,32 @@ filter_func return hit->prim.prim_id % 2 == 0; } +static void +triangle_get_ids(const unsigned itri, unsigned ids[3], void* ctx) +{ + (void)ctx; + CHK(itri == 0); + CHK(ids); + ids[0] = 0; + ids[1] = 1; + ids[2] = 2; +} + +static void +triangle_get_pos(const unsigned ivert, float pos[3], void* ctx) +{ + float* vertices = ctx; + CHK(ctx); + CHK(ivert < 3); + CHK(pos); + switch (ivert) { /* Setup a random triangle */ + case 0: f3_set(pos, vertices + 0); break; + case 1: f3_set(pos, vertices + 3); break; + case 2: f3_set(pos, vertices + 6); break; + default: FATAL("Unreachable code\n"); break; + } +} + int main(int argc, char** argv) { @@ -93,7 +119,7 @@ main(int argc, char** argv) unsigned walls_id; unsigned tall_block_id; unsigned short_block_id; - size_t i; + size_t a, i; char filter = 0; mem_init_proxy_allocator(&allocator, &mem_default_allocator); @@ -354,7 +380,6 @@ main(int argc, char** argv) CHK(image_write_ppm_stream(&img, 0, stdout) == RES_OK); image_release(&img); - CHK(s3d_device_ref_put(dev) == RES_OK); CHK(s3d_shape_ref_put(inst) == RES_OK); CHK(s3d_shape_ref_put(short_block) == RES_OK); CHK(s3d_shape_ref_put(tall_block) == RES_OK); @@ -362,6 +387,97 @@ main(int argc, char** argv) CHK(s3d_scene_ref_put(scn) == RES_OK); CHK(s3d_scene_ref_put(scn2) == RES_OK); + /* Check accuracy on a configuration whose analytic distance is known */ + FOR_EACH(a, 0, 16) { + const float amplitude = exp2f((float)a); + const float eps = 5e-6f * amplitude; + float vertices[9]; + struct s3d_vertex_data vdata = S3D_VERTEX_DATA_NULL; + struct s3d_scene_view* view = NULL; + struct s3d_shape* msh = NULL; + FOR_EACH(i, 0, 1000) { + float A[3], B[3], C[3], AB[3], AC[3], N[3]; + int j, n; + + /* Randomly generate a triangle ABC */ + FOR_EACH(n, 0, 3) + A[n] = (rand_canonic() - 0.5f) * amplitude; + do { + FOR_EACH(n, 0, 3) B[n] = (rand_canonic() - 0.5f) * amplitude; + } while (f3_eq_eps(A, B, eps)); + do { + FOR_EACH(n, 0, 3) C[n] = (rand_canonic() - 0.5f) * amplitude; + } while (f3_eq_eps(A, C, eps) || f3_eq_eps(B, C, eps)); + + f3_sub(AB, B, A); + f3_sub(AC, C, A); + f3_cross(N, AB, AC); + f3_normalize(N, N); + + f3_set(vertices + 0, A); + f3_set(vertices + 3, B); + f3_set(vertices + 6, C); + + CHK(s3d_scene_create(dev, &scn) == RES_OK); + CHK(s3d_shape_create_mesh(dev, &msh) == RES_OK); + CHK(s3d_scene_attach_shape(scn, msh) == RES_OK); + + vdata.usage = S3D_POSITION; + vdata.type = S3D_FLOAT3; + vdata.get = triangle_get_pos; + CHK(s3d_mesh_setup_indexed_vertices + (msh, 1, triangle_get_ids, 3, &vdata, 1, vertices) == RES_OK); + + CHK(s3d_scene_view_create(scn, S3D_TRACE, &view) == RES_OK); + + FOR_EACH(j, 0, 1000) { + float proj[3]; /* Projection of pos on the line */ + float tmp[3]; + float u, v, w, h; + + /* Randomly generate a pos not on the triangle + * with know position wrt the problem: pos = A + u.AB + v.AC + k.N */ + u = 3 * rand_canonic() - 1; + v = 3 * rand_canonic() - 1; + w = 1 - u - v; + h = (2 * rand_canonic() - 1) * amplitude; + f3_add(proj, A, f3_add(proj, f3_mulf(proj, AB, u), f3_mulf(tmp, AC, v))); + f3_add(pos, proj, f3_mulf(pos, N, h)); + + /* Raytrace from pos towards proj */ + f3_mulf(dir, N, (h > 0 ? -1.f : 1.f)); + f3_normalize(dir, dir); + CHK(s3d_scene_view_trace_ray(view, pos, dir, range, NULL, &hit) + == RES_OK); + + /* Check result */ + if(u < 0 || v < 0 || w < 0) { + if(!S3D_HIT_NONE(&hit)) + CHK(u >= -FLT_EPSILON && v >= -FLT_EPSILON && w >= -FLT_EPSILON); + } else { + if(S3D_HIT_NONE(&hit)) + CHK(u <= FLT_EPSILON || v <= FLT_EPSILON || w <= FLT_EPSILON); + } + if(!S3D_HIT_NONE(&hit)) { + struct s3d_attrib attr; + float d; + CHK(eq_epsf(hit.distance, fabsf(h), eps)); + CHK(s3d_primitive_get_attrib(&hit.prim, S3D_POSITION, hit.uv, &attr) + == RES_OK); + /* Intersection-point's position is less accurate than hit distance */ + d = f3_len(f3_sub(tmp, attr.value, proj)); + CHK(d <= 10 * eps); + } + } + + CHK(s3d_shape_ref_put(msh) == RES_OK); + CHK(s3d_scene_view_ref_put(view) == RES_OK); + CHK(s3d_scene_ref_put(scn) == RES_OK); + } + } + + CHK(s3d_device_ref_put(dev) == RES_OK); + check_memory_allocator(&allocator); mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0);