commit 6382123ac1077a616def64a6504dd30dfdc9a25f
parent 75f1f08a29585a9b425f144e86d84e82cb92fda4
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Fri, 4 Oct 2019 15:50:05 +0200
Perform basic tests on s3d_scene_view_point_query
Diffstat:
3 files changed, 273 insertions(+), 1 deletion(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -140,6 +140,7 @@ if(NOT NO_TEST)
new_test(test_s3d_shape)
new_test(test_s3d_sphere)
new_test(test_s3d_sphere_box)
+ new_test(test_s3d_point_query)
new_test(test_s3d_primitive)
new_test(test_s3d_trace_ray_instance)
new_test(test_s3d_trace_ray_sphere)
diff --git a/src/s3d.h b/src/s3d.h
@@ -333,7 +333,7 @@ S3D_API res_T
s3d_scene_view_point_query
(struct s3d_scene_view* scnview,
const float pos[3], /* Position to query */
- const float radius, /* Maxium search distance around pos */
+ const float radius, /* Search distance in [0, radius[ */
void* query_data, /* User data sent to the hit filter func. May be NULL */
struct s3d_hit* hit);
diff --git a/src/test_s3d_point_query.c b/src/test_s3d_point_query.c
@@ -0,0 +1,271 @@
+/* Copyright (C) 2015-2019 |Meso|Star> (contact@meso-star.com)
+ *
+ * This software is a computer program whose purpose is to describe a
+ * virtual 3D environment that can be ray-traced and sampled both robustly
+ * and efficiently.
+ *
+ * This software is governed by the CeCILL license under French law and
+ * abiding by the rules of distribution of free software. You can use,
+ * modify and/or redistribute the software under the terms of the CeCILL
+ * license as circulated by CEA, CNRS and INRIA at the following URL
+ * "http://www.cecill.info".
+ *
+ * As a counterpart to the access to the source code and rights to copy,
+ * modify and redistribute granted by the license, users are provided only
+ * with a limited warranty and the software's author, the holder of the
+ * economic rights, and the successive licensors have only limited
+ * liability.
+ *
+ * In this respect, the user's attention is drawn to the risks associated
+ * with loading, using, modifying and/or developing or reproducing the
+ * software by the user in light of its specific status of free software,
+ * that may mean that it is complicated to manipulate, and that also
+ * therefore means that it is reserved for developers and experienced
+ * professionals having in-depth computer knowledge. Users are therefore
+ * encouraged to load and test the software's suitability as regards their
+ * requirements in conditions enabling the security of their systems and/or
+ * data to be ensured and, more generally, to use and operate it in the
+ * same conditions as regards security.
+ *
+ * The fact that you are presently reading this means that you have had
+ * knowledge of the CeCILL license and that you accept its terms. */
+
+#include "s3d.h"
+#include "test_s3d_utils.h"
+
+#include <rsys/float3.h>
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+/* Inefficient function that computes the point onto the triangle that ensures
+ * the minimum distance between the submitted `pos' and the triangle. Use this
+ * routine to cross check the result of the s3d_scene_view_point_query function
+ * that internally rely on a more efficient implementation */
+static float*
+closest_point_triangle
+ (const float p[3], /* Input pos */
+ const float a[3], /* 1st triangle vertex */
+ const float b[3], /* 2nd triangle vertex */
+ const float c[3], /* 3rd triangle vertex */
+ float pt[3]) /* Closest point of pos onto the triangle */
+{
+ float N[3]; /* Triangle normal */
+ float Nab[3], Nbc[3], Nca[3]; /* Edge normals */
+ float ab[3], ac[3], bc[3];
+ float ap[3], bp[3], cp[3];
+ float d1, d2, d3, d4, d5, d6, d;
+ CHK(p && a && b && c && pt);
+
+ f3_normalize(ab, f3_sub(ab, b, a));
+ f3_normalize(ac, f3_sub(ac, c, a));
+ f3_normalize(bc, f3_sub(bc, c, b));
+
+ /* Compute the triangle normal */
+ f3_cross(N, ac, ab);
+
+ /* Check if the nearest point is the 1st triangle vertex */
+ f3_sub(ap, p, a);
+ d1 = f3_dot(ab, ap);
+ d2 = f3_dot(ac, ap);
+ if(d1 <= 0 && d2 <= 0) return f3_set(pt, a);
+
+ /* Check if the nearest point is the 2nd triangle vertex */
+ f3_sub(bp, p, b);
+ d3 = f3_dot(bc, bp);
+ d4 =-f3_dot(ab, bp);
+ if(d3 <= 0 && d4 <= 0) return f3_set(pt, b);
+
+ /* Check if the nearest point is the 3rd triangle vertex */
+ f3_sub(cp, p, c);
+ d5 =-f3_dot(ac, cp);
+ d6 =-f3_dot(bc, cp);
+ if(d5 <= 0 && d6 <= 0) return f3_set(pt, c);
+
+ /* Check if the nearest point is on the 1st triangle edge */
+ f3_cross(Nab, ab, N);
+ if(f3_dot(Nab, ap) <= 0) {
+ return f3_add(pt, a, f3_mulf(pt, ab, d1));
+ }
+
+ /* Check if the nearest point is on the 2nd triangle edge */
+ f3_cross(Nbc, bc, N);
+ if(f3_dot(Nbc, bp) <= 0) {
+ return f3_add(pt, b, f3_mulf(pt, bc, d3));
+ }
+
+ /* Check if the nearest point is on the 3rd triangle edge */
+ f3_cross(Nca, ac, N);
+ f3_minus(Nca, Nca);
+ if(f3_dot(Nca, cp) <= 0) {
+ return f3_add(pt, c, f3_mulf(pt, ac,-d5));
+ }
+
+ /* The nearest point is in the triangle */
+ f3_normalize(N, N);
+ d = f3_dot(N, ap);
+ return f3_add(pt, p, f3_mulf(pt, N, -d));
+}
+
+/*******************************************************************************
+ * Single triangle test
+ ******************************************************************************/
+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)
+{
+ (void)ctx;
+ CHK(ivert < 3);
+ CHK(pos);
+ switch(ivert) { /* Setup a random triangle */
+ case 0: f3(pos, -0.5f, -0.3f, 0.1f); break;
+ case 1: f3(pos, -0.4f, 0.2f, 0.3f); break;
+ case 2: f3(pos, 0.7f, 0.01f, -0.5f); break;
+ default: FATAL("Unreachable code\n"); break;
+ }
+}
+
+static void
+test_single_triangle(struct s3d_device* dev)
+{
+ struct s3d_vertex_data vdata = S3D_VERTEX_DATA_NULL;
+ struct s3d_hit hit = S3D_HIT_NULL;
+ struct s3d_scene* scn = NULL;
+ struct s3d_scene_view* view = NULL;
+ struct s3d_shape* msh = NULL;
+ struct s3d_attrib attr;
+ float v0[3], v1[3], v2[3];
+ float pos[3] = {0,0,0};
+ float closest_pos[3] = {0,0,0};
+ float low[3], upp[3];
+ size_t i;
+
+ 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, NULL) == RES_OK);
+
+ CHK(s3d_scene_view_create(scn, S3D_TRACE, &view) == RES_OK);
+
+ triangle_get_pos(0, v0, NULL);
+ triangle_get_pos(1, v1, NULL);
+ triangle_get_pos(2, v2, NULL);
+
+ /* Compute the triangle AABB */
+ low[0] = MMIN(MMIN(v0[0], v1[0]), v2[0]);
+ low[1] = MMIN(MMIN(v0[1], v1[1]), v2[1]);
+ low[2] = MMIN(MMIN(v0[2], v1[2]), v2[2]);
+ upp[0] = MMAX(MMAX(v0[0], v1[0]), v2[0]);
+ upp[1] = MMAX(MMAX(v0[1], v1[1]), v2[1]);
+ upp[2] = MMAX(MMAX(v0[2], v1[2]), v2[2]);
+
+ FOR_EACH(i, 0, 10000) {
+ /* Randomly generate a point in a bounding box that is 10 times the size of
+ * the triangle AABB */
+ pos[0] = (rand_canonic() * 2 - 1) * (upp[0] - low[0]) * 10.f;
+ pos[1] = (rand_canonic() * 2 - 1) * (upp[1] - low[1]) * 10.f;
+ pos[2] = (rand_canonic() * 2 - 1) * (upp[2] - low[2]) * 10.f;
+
+ CHK(s3d_scene_view_point_query(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);
+
+ /* Cross check the point query result */
+ closest_point_triangle(pos, v0, v1, v2, closest_pos);
+ CHK(f3_eq_eps(closest_pos, attr.value, 1.e-4f));
+ }
+
+ FOR_EACH(i, 0, 10000) {
+ float radius;
+
+ /* Randomly generate a point in a bounding box that is 10 times the size of
+ * the triangle AABB */
+ pos[0] = (rand_canonic() * 2 - 1) * (upp[0] - low[0]) * 10.f;
+ pos[1] = (rand_canonic() * 2 - 1) * (upp[1] - low[1]) * 10.f;
+ pos[2] = (rand_canonic() * 2 - 1) * (upp[2] - low[2]) * 10.f;
+
+ CHK(s3d_scene_view_point_query(view, pos, (float)INF, NULL, &hit) == RES_OK);
+ CHK(!S3D_HIT_NONE(&hit));
+
+ /* Check that the radius is an exclusive upper bound */
+ radius = hit.distance;
+ CHK(s3d_scene_view_point_query(view, pos, radius, NULL, &hit) == RES_OK);
+ CHK(S3D_HIT_NONE(&hit));
+ radius = nextafterf(radius, FLT_MAX);
+ CHK(s3d_scene_view_point_query(view, pos, radius, NULL, &hit) == RES_OK);
+ CHK(!S3D_HIT_NONE(&hit));
+ CHK(hit.distance == nextafterf(radius, 0.f));
+ }
+
+ 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);
+}
+
+/*******************************************************************************
+ * Miscellaneous test
+ ******************************************************************************/
+static void
+test_api(struct s3d_device* dev)
+{
+ struct s3d_hit hit = S3D_HIT_NULL;
+ struct s3d_scene* scn = NULL;
+ struct s3d_scene_view* view = NULL;
+ float pos[3] = {0,0,0};
+
+ CHK(s3d_scene_create(dev, &scn) == RES_OK);
+ CHK(s3d_scene_view_create(scn, S3D_TRACE, &view) == RES_OK);
+
+ CHK(s3d_scene_view_point_query(NULL, pos, 1.f, NULL, &hit) == RES_BAD_ARG);
+ CHK(s3d_scene_view_point_query(view, NULL, 1.f, NULL, &hit) == RES_BAD_ARG);
+ CHK(s3d_scene_view_point_query(view, pos, 0.f, NULL, &hit) == RES_BAD_ARG);
+ CHK(s3d_scene_view_point_query(view, pos, 1.f, NULL, NULL) == RES_BAD_ARG);
+ CHK(s3d_scene_view_point_query(view, pos, 1.f, NULL, &hit) == RES_OK);
+ CHK(S3D_HIT_NONE(&hit));
+
+ CHK(s3d_scene_view_ref_put(view) == RES_OK);
+ CHK(s3d_scene_view_create(scn, S3D_SAMPLE, &view) == RES_OK);
+ CHK(s3d_scene_view_point_query(view, pos, 1.f, NULL, &hit) == RES_BAD_OP);
+
+ CHK(s3d_scene_view_ref_put(view) == RES_OK);
+ CHK(s3d_scene_ref_put(scn) == RES_OK);
+}
+
+/*******************************************************************************
+ * Main function
+ ******************************************************************************/
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct s3d_device* dev = NULL;
+ (void)argc, (void)argv;
+
+ mem_init_proxy_allocator(&allocator, &mem_default_allocator);
+ CHK(s3d_device_create(NULL, &allocator, 1, &dev) == RES_OK);
+
+ test_api(dev);
+ test_single_triangle(dev);
+
+ CHK(s3d_device_ref_put(dev) == RES_OK);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+ return 0;
+}