commit 8612a4bbebddf7cbd57d9049d3bace3a2b184f0b
parent 7b15260a20ba8199d3b699e7f8af2a0a93549eab
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Thu, 10 Oct 2019 11:42:12 +0200
Rename the point_query API in closest_point
Diffstat:
6 files changed, 1032 insertions(+), 1032 deletions(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -69,7 +69,7 @@ set(S3D_FILES_SRC
s3d_primitive.c
s3d_scene.c
s3d_scene_view.c
- s3d_scene_view_point_query.c
+ s3d_scene_view_closest_point.c
s3d_shape.c
s3d_sphere.c)
set(S3D_FILES_INC_API s3d.h)
@@ -131,6 +131,7 @@ if(NOT NO_TEST)
register_test(${_name} ${_name})
endfunction()
+ new_test(test_s3d_closest_point)
new_test(test_s3d_device)
new_test(test_s3d_sampler)
new_test(test_s3d_sample_sphere)
@@ -140,7 +141,6 @@ 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
@@ -331,7 +331,7 @@ s3d_scene_view_trace_rays
* reject a candidate position according to its own criteria. Cab ne called
* only if the scenview was created with the S3D_TRACE flag */
S3D_API res_T
-s3d_scene_view_point_query
+s3d_scene_view_closest_point
(struct s3d_scene_view* scnview,
const float pos[3], /* Position to query */
const float radius, /* Search distance in [0, radius[ */
diff --git a/src/s3d_scene_view_closest_point.c b/src/s3d_scene_view_closest_point.c
@@ -0,0 +1,440 @@
+/* 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 "s3d_device_c.h"
+#include "s3d_instance.h"
+#include "s3d_geometry.h"
+#include "s3d_mesh.h"
+#include "s3d_scene_view_c.h"
+#include "s3d_sphere.h"
+
+#include <rsys/float3.h>
+#include <rsys/float33.h>
+
+struct point_query_context {
+ struct RTCPointQueryContext rtc;
+ struct s3d_scene_view* scnview;
+ void* data; /* Per point query defined data */
+};
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+static INLINE float*
+closest_point_triangle
+ (const float p[3], /* Point */
+ const float a[3], /* 1st triangle vertex */
+ const float b[3], /* 2nd triangle vertex */
+ const float c[3], /* 3rd triangle vertex */
+ float closest_pt[3], /* Closest position */
+ float uv[2]) /* UV of the closest position */
+{
+ float ab[3], ac[3], ap[3], bp[3], cp[3];
+ float d1, d2, d3, d4, d5, d6;
+ float va, vb, vc;
+ float rcp_triangle_area;
+ float v, w;
+ ASSERT(p && a && b && c && closest_pt && uv);
+
+ f3_sub(ab, b, a);
+ f3_sub(ac, c, a);
+
+ /* Check if the closest point is the triangle vertex 'a' */
+ f3_sub(ap, p, a);
+ d1 = f3_dot(ab, ap);
+ d2 = f3_dot(ac, ap);
+ if(d1 <= 0.f && d2 <= 0.f) {
+ uv[0] = 1.f;
+ uv[1] = 0.f;
+ return f3_set(closest_pt, a);
+ }
+
+ /* Check if the closest point is the triangle vertex 'b' */
+ f3_sub(bp, p, b);
+ d3 = f3_dot(ab, bp);
+ d4 = f3_dot(ac, bp);
+ if(d3 >= 0.f && d4 <= d3) {
+ uv[0] = 0.f;
+ uv[1] = 1.f;
+ return f3_set(closest_pt, b);
+ }
+
+ /* Check if the closest point is the triangle vertex 'c' */
+ f3_sub(cp, p, c);
+ d5 = f3_dot(ab, cp);
+ d6 = f3_dot(ac, cp);
+ if(d6 >= 0.f && d5 <= d6) {
+ uv[0] = 0.f;
+ uv[1] = 0.f;
+ return f3_set(closest_pt, c);
+ }
+
+ /* Check if the closest point is on the triangle edge 'ab' */
+ vc = d1*d4 - d3*d2;
+ if(vc <= 0.f && d1 >= 0.f && d3 <= 0.f) {
+ const float s = d1 / (d1 - d3);
+ closest_pt[0] = a[0] + s*ab[0];
+ closest_pt[1] = a[1] + s*ab[1];
+ closest_pt[2] = a[2] + s*ab[2];
+ uv[0] = 1.f - s;
+ uv[1] = s;
+ return closest_pt;
+ }
+
+ /* Check if the closest point is on the triangle edge 'ac' */
+ vb = d5*d2 - d1*d6;
+ if(vb <= 0.f && d2 >= 0.f && d6 <= 0.f) {
+ const float s = d2 / (d2 - d6);
+ closest_pt[0] = a[0] + s*ac[0];
+ closest_pt[1] = a[1] + s*ac[1];
+ closest_pt[2] = a[2] + s*ac[2];
+ uv[0] = 1.f - s;
+ uv[1] = 0.f;
+ return closest_pt;
+ }
+
+ /* Check if the closest point is on the triangle edge 'bc' */
+ va = d3*d6 - d5*d4;
+ if(va <= 0.f && (d4 - d3) >= 0.f && (d5 - d6) >= 0.f) {
+ const float s = (d4 - d3) / ((d4 - d3) + (d5 - d6));
+ closest_pt[0] = b[0] + s*(c[0] - b[0]);
+ closest_pt[1] = b[1] + s*(c[1] - b[1]);
+ closest_pt[2] = b[2] + s*(c[2] - b[2]);
+ uv[0] = 0.f;
+ uv[1] = 1.f - s;
+ return closest_pt;
+ }
+
+ /* The closest point lies in the triangle: compute its barycentric
+ * coordinates */
+ rcp_triangle_area = 1.f / (va + vb + vc);
+ v = vb * rcp_triangle_area;
+ w = vc * rcp_triangle_area;
+
+ /* Save the uv barycentric coordinates */
+ uv[0] = 1.f - v - w;
+ uv[1] = v;
+
+ ASSERT(eq_epsf(uv[0] + uv[1] + w, 1.f, 1.e-4f));
+
+ /* Use the barycentric coordinates to compute the world space position of the
+ * closest point onto the triangle */
+ closest_pt[0] = a[0] + v*ab[0] + w*ac[0];
+ closest_pt[1] = a[1] + v*ab[1] + w*ac[1];
+ closest_pt[2] = a[2] + v*ab[2] + w*ac[2];
+ return closest_pt;
+}
+
+static bool
+closest_point_mesh
+ (struct RTCPointQueryFunctionArguments* args,
+ struct geometry* geom,
+ struct geometry* inst, /* Can be NULL */
+ void* query_data)
+{
+ struct s3d_hit hit = S3D_HIT_NULL;
+ struct s3d_hit* out_hit = NULL;
+ struct hit_filter* filter = NULL;
+ const uint32_t* ids = NULL;
+ float closest_point[3];
+ float query_pos[3];
+ float v0[3], v1[3], v2[3];
+ float E0[3], E1[3], Ng[3];
+ float vec[3];
+ float uv[2];
+ float dst;
+ int flip_surface = 0;
+ ASSERT(args && geom && geom->type == GEOM_MESH);
+ ASSERT(args->primID < mesh_get_ntris(geom->data.mesh));
+
+ /* Fetch triangle indices */
+ ids = mesh_get_ids(geom->data.mesh) + args->primID*3/*#indices per triangle*/;
+
+ /* Fetch triangle vertices */
+ ASSERT(geom->data.mesh->attribs_type[S3D_POSITION] == S3D_FLOAT3);
+ f3_set(v0, mesh_get_pos(geom->data.mesh) + ids[0]*3/*#coords per vertex*/);
+ f3_set(v1, mesh_get_pos(geom->data.mesh) + ids[1]*3/*#coords per vertex*/);
+ f3_set(v2, mesh_get_pos(geom->data.mesh) + ids[2]*3/*#coords per vertex*/);
+
+ /* Local copy of the query position */
+ query_pos[0] = args->query->x;
+ query_pos[1] = args->query->y;
+ query_pos[2] = args->query->z;
+
+ if(args->context->instStackSize) { /* The mesh is instantiated */
+ const float* transform;
+ transform = inst->data.instance->transform;
+ ASSERT(args->context->instStackSize == 1);
+ ASSERT(args->similarityScale == 1);
+ ASSERT(inst && inst->type == GEOM_INSTANCE);
+ ASSERT(f3_eq_eps(transform+0, args->context->inst2world[0]+0, 1.e-6f));
+ ASSERT(f3_eq_eps(transform+3, args->context->inst2world[0]+4, 1.e-6f));
+ ASSERT(f3_eq_eps(transform+6, args->context->inst2world[0]+8, 1.e-6f));
+ ASSERT(f3_eq_eps(transform+9, args->context->inst2world[0]+12,1.e-6f));
+
+ /* Transform the triangle vertices in world space */
+ f3_add(v0, f33_mulf3(v0, transform, v0), transform+9);
+ f3_add(v1, f33_mulf3(v1, transform, v1), transform+9);
+ f3_add(v2, f33_mulf3(v2, transform, v2), transform+9);
+
+ flip_surface = inst->flip_surface;
+ }
+
+ /* Compute the closest point onto the triangle from the submitted point */
+ closest_point_triangle(query_pos, v0, v1, v2, closest_point, uv);
+
+ f3_sub(vec, closest_point, query_pos);
+ dst = f3_len(vec);
+ if(dst >= args->query->radius) return 0;
+
+ /* Compute the triangle normal in world space */
+ f3_sub(E0, v2, v0);
+ f3_sub(E1, v1, v0);
+ f3_cross(Ng, E0, E1);
+
+ /* Flip the geometric normal wrt the flip surface flag */
+ flip_surface ^= geom->flip_surface;
+ if(flip_surface) f3_minus(Ng, Ng);
+
+ /* Setup the hit */
+ hit.prim.shape__ = geom;
+ hit.prim.inst__ = inst;
+ hit.distance = dst;
+ hit.uv[0] = uv[0];
+ hit.uv[1] = uv[1];
+ hit.normal[0] = Ng[0];
+ hit.normal[1] = Ng[1];
+ hit.normal[2] = Ng[2];
+ hit.prim.prim_id = args->primID;
+ hit.prim.geom_id = geom->name;
+ hit.prim.inst_id = inst ? inst->name : S3D_INVALID_ID;
+ hit.prim.scene_prim_id =
+ hit.prim.prim_id
+ + geom->scene_prim_id_offset
+ + (inst ? inst->scene_prim_id_offset : 0);
+
+ /* `vec' is the direction along which the closest point was found. We thus
+ * submit it to the filter function as the direction corresponding to the
+ * computed hit */
+ filter = &geom->data.mesh->filter;
+ if(filter->func
+ && filter->func(&hit, query_pos, vec, query_data, filter->data)) {
+ return 0; /* This point is filtered. Discard it! */
+ }
+
+ /* Update output data */
+ out_hit = args->userPtr;
+ *out_hit = hit;
+
+ /* Shrink the query radius */
+ args->query->radius = dst;
+
+ return 1; /* Notify that the query radius was updated */
+}
+
+static bool
+closest_point_sphere
+ (struct RTCPointQueryFunctionArguments* args,
+ struct geometry* geom,
+ struct geometry* inst,
+ void* query_data)
+{
+ struct s3d_hit hit = S3D_HIT_NULL;
+ struct s3d_hit* out_hit = NULL;
+ struct hit_filter* filter = NULL;
+ float query_pos[3];
+ float sphere_pos[3];
+ float Ng[3];
+ float dir[3];
+ float uv[2];
+ float dst;
+ float len;
+ int flip_surface = 0;
+ ASSERT(args && geom && geom->type == GEOM_SPHERE && args->primID == 1);
+
+ /* Local copy of the query position */
+ query_pos[0] = args->query->x;
+ query_pos[1] = args->query->y;
+ query_pos[2] = args->query->z;
+
+ f3_set(sphere_pos, geom->data.sphere->pos);
+ if(args->context->instStackSize) { /* The sphere is instantiated */
+ const float* transform;
+ transform = inst->data.instance->transform;
+ ASSERT(args->context->instStackSize == 1);
+ ASSERT(args->similarityScale == 1);
+ ASSERT(inst && inst->type == GEOM_INSTANCE);
+ ASSERT(f3_eq_eps(transform+0, args->context->inst2world[0]+0, 1.e-6f));
+ ASSERT(f3_eq_eps(transform+3, args->context->inst2world[0]+4, 1.e-6f));
+ ASSERT(f3_eq_eps(transform+6, args->context->inst2world[0]+8, 1.e-6f));
+ ASSERT(f3_eq_eps(transform+9, args->context->inst2world[0]+12,1.e-6f));
+
+ /* Transform the sphere position in world space */
+ f33_mulf3(sphere_pos, transform, sphere_pos);
+ f3_add(sphere_pos, transform+9, sphere_pos);
+
+ flip_surface = inst->flip_surface;
+ }
+
+ /* Compute the distance from the the query pos to the sphere center */
+ f3_sub(Ng, query_pos, sphere_pos);
+ len = f3_len(Ng);
+
+ /* Evaluate the distance from the query pos to the sphere surface */
+ dst = fabsf(len - geom->data.sphere->radius);
+
+ /* The closest point onto the sphere is outside the query radius */
+ if(dst >= args->query->radius)
+ return 0;
+
+ /* Compute the uv of the found point */
+ f3_divf(Ng, Ng, len); /* Normalize the hit normal */
+ sphere_normal_to_uv(Ng, uv);
+
+ /* Flip the geometric normal wrt the flip surface flag */
+ flip_surface ^= geom->flip_surface;
+ if(flip_surface) f3_minus(Ng, Ng);
+
+ /* Setup the hit */
+ hit.prim.shape__ = geom;
+ hit.prim.inst__ = inst;
+ hit.distance = dst;
+ hit.uv[0] = uv[0];
+ hit.uv[1] = uv[1];
+ hit.normal[0] = Ng[0];
+ hit.normal[1] = Ng[1];
+ hit.normal[2] = Ng[2];
+ hit.prim.prim_id = args->primID;
+ hit.prim.geom_id = geom->name;
+ hit.prim.inst_id = inst ? inst->name : S3D_INVALID_ID;
+ hit.prim.scene_prim_id =
+ hit.prim.prim_id
+ + geom->scene_prim_id_offset
+ + (inst ? inst->scene_prim_id_offset : 0);
+
+ /* Use the reversed geometric normal as the hit direction since it is along
+ * this vector that the closest point was effectively computed */
+ f3_minus(dir, Ng);
+ filter = &geom->data.sphere->filter;
+ if(filter->func
+ && filter->func(&hit, query_pos, dir, query_data, filter->data)) {
+ return 0;
+ }
+
+ /* Update output data */
+ out_hit = args->userPtr;
+ *out_hit = hit;
+
+ /* Shrink the query radius */
+ args->query->radius = dst;
+
+ return 1; /* Notify that the query radius was updated */
+}
+
+static bool
+closest_point(struct RTCPointQueryFunctionArguments* args)
+{
+ struct point_query_context* ctx = NULL;
+ struct geometry* geom = NULL;
+ struct geometry* inst = NULL;
+ bool query_radius_is_upd = false;
+ ASSERT(args);
+
+ ctx = CONTAINER_OF(args->context, struct point_query_context, rtc);
+ if(args->context->instStackSize == 0) {
+ geom = scene_view_geometry_from_embree_id
+ (ctx->scnview, args->geomID);
+ } else {
+ ASSERT(args->context->instStackSize == 1);
+ ASSERT(args->similarityScale == 1);
+ inst = scene_view_geometry_from_embree_id
+ (ctx->scnview, args->context->instID[0]);
+ geom = scene_view_geometry_from_embree_id
+ (inst->data.instance->scnview, args->geomID);
+ }
+
+ switch(geom->type) {
+ case GEOM_MESH:
+ query_radius_is_upd = closest_point_mesh(args, geom, inst, ctx->data);
+ break;
+ case GEOM_SPHERE:
+ query_radius_is_upd = closest_point_sphere(args, geom, inst, ctx->data);
+ break;
+ default: FATAL("Unreachable code\n"); break;
+ }
+ return query_radius_is_upd;
+}
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+res_T
+s3d_scene_view_closest_point
+ (struct s3d_scene_view* scnview,
+ const float pos[3],
+ const float radius,
+ void* query_data,
+ struct s3d_hit* hit)
+{
+ struct RTCPointQuery query;
+ struct point_query_context query_ctx;
+
+ if(!scnview || !pos || radius <= 0 || !hit)
+ return RES_BAD_ARG;
+ if((scnview->mask & S3D_TRACE) == 0) {
+ log_error(scnview->scn->dev,
+ "%s: the S3D_TRACE flag is not active onto the submitted scene view.\n",
+ FUNC_NAME);
+ return RES_BAD_OP;
+ }
+
+ *hit = S3D_HIT_NULL;
+
+ /* Initialise the point query */
+ query.x = pos[0];
+ query.y = pos[1];
+ query.z = pos[2];
+ query.radius = radius;
+ query.time = FLT_MAX; /* Invalid fields */
+
+ /* Initialise the point query context */
+ rtcInitPointQueryContext(&query_ctx.rtc);
+ query_ctx.scnview = scnview;
+ query_ctx.data = query_data;
+
+ /* Here we go! */
+ rtcPointQuery(scnview->rtc_scn, &query, &query_ctx.rtc, closest_point, hit);
+
+ return RES_OK;
+}
+
diff --git a/src/s3d_scene_view_point_query.c b/src/s3d_scene_view_point_query.c
@@ -1,440 +0,0 @@
-/* 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 "s3d_device_c.h"
-#include "s3d_instance.h"
-#include "s3d_geometry.h"
-#include "s3d_mesh.h"
-#include "s3d_scene_view_c.h"
-#include "s3d_sphere.h"
-
-#include <rsys/float3.h>
-#include <rsys/float33.h>
-
-struct point_query_context {
- struct RTCPointQueryContext rtc;
- struct s3d_scene_view* scnview;
- void* data; /* Per point query defined data */
-};
-
-/*******************************************************************************
- * Helper functions
- ******************************************************************************/
-static INLINE float*
-closest_point_triangle
- (const float p[3], /* Point */
- const float a[3], /* 1st triangle vertex */
- const float b[3], /* 2nd triangle vertex */
- const float c[3], /* 3rd triangle vertex */
- float closest_pt[3], /* Closest position */
- float uv[2]) /* UV of the closest position */
-{
- float ab[3], ac[3], ap[3], bp[3], cp[3];
- float d1, d2, d3, d4, d5, d6;
- float va, vb, vc;
- float rcp_triangle_area;
- float v, w;
- ASSERT(p && a && b && c && closest_pt && uv);
-
- f3_sub(ab, b, a);
- f3_sub(ac, c, a);
-
- /* Check if the closest point is the triangle vertex 'a' */
- f3_sub(ap, p, a);
- d1 = f3_dot(ab, ap);
- d2 = f3_dot(ac, ap);
- if(d1 <= 0.f && d2 <= 0.f) {
- uv[0] = 1.f;
- uv[1] = 0.f;
- return f3_set(closest_pt, a);
- }
-
- /* Check if the closest point is the triangle vertex 'b' */
- f3_sub(bp, p, b);
- d3 = f3_dot(ab, bp);
- d4 = f3_dot(ac, bp);
- if(d3 >= 0.f && d4 <= d3) {
- uv[0] = 0.f;
- uv[1] = 1.f;
- return f3_set(closest_pt, b);
- }
-
- /* Check if the closest point is the triangle vertex 'c' */
- f3_sub(cp, p, c);
- d5 = f3_dot(ab, cp);
- d6 = f3_dot(ac, cp);
- if(d6 >= 0.f && d5 <= d6) {
- uv[0] = 0.f;
- uv[1] = 0.f;
- return f3_set(closest_pt, c);
- }
-
- /* Check if the closest point is on the triangle edge 'ab' */
- vc = d1*d4 - d3*d2;
- if(vc <= 0.f && d1 >= 0.f && d3 <= 0.f) {
- const float s = d1 / (d1 - d3);
- closest_pt[0] = a[0] + s*ab[0];
- closest_pt[1] = a[1] + s*ab[1];
- closest_pt[2] = a[2] + s*ab[2];
- uv[0] = 1.f - s;
- uv[1] = s;
- return closest_pt;
- }
-
- /* Check if the closest point is on the triangle edge 'ac' */
- vb = d5*d2 - d1*d6;
- if(vb <= 0.f && d2 >= 0.f && d6 <= 0.f) {
- const float s = d2 / (d2 - d6);
- closest_pt[0] = a[0] + s*ac[0];
- closest_pt[1] = a[1] + s*ac[1];
- closest_pt[2] = a[2] + s*ac[2];
- uv[0] = 1.f - s;
- uv[1] = 0.f;
- return closest_pt;
- }
-
- /* Check if the closest point is on the triangle edge 'bc' */
- va = d3*d6 - d5*d4;
- if(va <= 0.f && (d4 - d3) >= 0.f && (d5 - d6) >= 0.f) {
- const float s = (d4 - d3) / ((d4 - d3) + (d5 - d6));
- closest_pt[0] = b[0] + s*(c[0] - b[0]);
- closest_pt[1] = b[1] + s*(c[1] - b[1]);
- closest_pt[2] = b[2] + s*(c[2] - b[2]);
- uv[0] = 0.f;
- uv[1] = 1.f - s;
- return closest_pt;
- }
-
- /* The closest point lies in the triangle: compute its barycentric
- * coordinates */
- rcp_triangle_area = 1.f / (va + vb + vc);
- v = vb * rcp_triangle_area;
- w = vc * rcp_triangle_area;
-
- /* Save the uv barycentric coordinates */
- uv[0] = 1.f - v - w;
- uv[1] = v;
-
- ASSERT(eq_epsf(uv[0] + uv[1] + w, 1.f, 1.e-4f));
-
- /* Use the barycentric coordinates to compute the world space position of the
- * closest point onto the triangle */
- closest_pt[0] = a[0] + v*ab[0] + w*ac[0];
- closest_pt[1] = a[1] + v*ab[1] + w*ac[1];
- closest_pt[2] = a[2] + v*ab[2] + w*ac[2];
- return closest_pt;
-}
-
-static bool
-closest_point_mesh
- (struct RTCPointQueryFunctionArguments* args,
- struct geometry* geom,
- struct geometry* inst, /* Can be NULL */
- void* query_data)
-{
- struct s3d_hit hit = S3D_HIT_NULL;
- struct s3d_hit* out_hit = NULL;
- struct hit_filter* filter = NULL;
- const uint32_t* ids = NULL;
- float closest_point[3];
- float query_pos[3];
- float v0[3], v1[3], v2[3];
- float E0[3], E1[3], Ng[3];
- float vec[3];
- float uv[2];
- float dst;
- int flip_surface = 0;
- ASSERT(args && geom && geom->type == GEOM_MESH);
- ASSERT(args->primID < mesh_get_ntris(geom->data.mesh));
-
- /* Fetch triangle indices */
- ids = mesh_get_ids(geom->data.mesh) + args->primID*3/*#indices per triangle*/;
-
- /* Fetch triangle vertices */
- ASSERT(geom->data.mesh->attribs_type[S3D_POSITION] == S3D_FLOAT3);
- f3_set(v0, mesh_get_pos(geom->data.mesh) + ids[0]*3/*#coords per vertex*/);
- f3_set(v1, mesh_get_pos(geom->data.mesh) + ids[1]*3/*#coords per vertex*/);
- f3_set(v2, mesh_get_pos(geom->data.mesh) + ids[2]*3/*#coords per vertex*/);
-
- /* Local copy of the query position */
- query_pos[0] = args->query->x;
- query_pos[1] = args->query->y;
- query_pos[2] = args->query->z;
-
- if(args->context->instStackSize) { /* The mesh is instantiated */
- const float* transform;
- transform = inst->data.instance->transform;
- ASSERT(args->context->instStackSize == 1);
- ASSERT(args->similarityScale == 1);
- ASSERT(inst && inst->type == GEOM_INSTANCE);
- ASSERT(f3_eq_eps(transform+0, args->context->inst2world[0]+0, 1.e-6f));
- ASSERT(f3_eq_eps(transform+3, args->context->inst2world[0]+4, 1.e-6f));
- ASSERT(f3_eq_eps(transform+6, args->context->inst2world[0]+8, 1.e-6f));
- ASSERT(f3_eq_eps(transform+9, args->context->inst2world[0]+12,1.e-6f));
-
- /* Transform the triangle vertices in world space */
- f3_add(v0, f33_mulf3(v0, transform, v0), transform+9);
- f3_add(v1, f33_mulf3(v1, transform, v1), transform+9);
- f3_add(v2, f33_mulf3(v2, transform, v2), transform+9);
-
- flip_surface = inst->flip_surface;
- }
-
- /* Compute the closest point onto the triangle from the submitted point */
- closest_point_triangle(query_pos, v0, v1, v2, closest_point, uv);
-
- f3_sub(vec, closest_point, query_pos);
- dst = f3_len(vec);
- if(dst >= args->query->radius) return 0;
-
- /* Compute the triangle normal in world space */
- f3_sub(E0, v2, v0);
- f3_sub(E1, v1, v0);
- f3_cross(Ng, E0, E1);
-
- /* Flip the geometric normal wrt the flip surface flag */
- flip_surface ^= geom->flip_surface;
- if(flip_surface) f3_minus(Ng, Ng);
-
- /* Setup the hit */
- hit.prim.shape__ = geom;
- hit.prim.inst__ = inst;
- hit.distance = dst;
- hit.uv[0] = uv[0];
- hit.uv[1] = uv[1];
- hit.normal[0] = Ng[0];
- hit.normal[1] = Ng[1];
- hit.normal[2] = Ng[2];
- hit.prim.prim_id = args->primID;
- hit.prim.geom_id = geom->name;
- hit.prim.inst_id = inst ? inst->name : S3D_INVALID_ID;
- hit.prim.scene_prim_id =
- hit.prim.prim_id
- + geom->scene_prim_id_offset
- + (inst ? inst->scene_prim_id_offset : 0);
-
- /* `vec' is the direction along which the closest point was found. We thus
- * submit it to the filter function as the direction corresponding to the
- * computed hit */
- filter = &geom->data.mesh->filter;
- if(filter->func
- && filter->func(&hit, query_pos, vec, query_data, filter->data)) {
- return 0; /* This point is filtered. Discard it! */
- }
-
- /* Update output data */
- out_hit = args->userPtr;
- *out_hit = hit;
-
- /* Shrink the query radius */
- args->query->radius = dst;
-
- return 1; /* Notify that the query radius was updated */
-}
-
-static bool
-closest_point_sphere
- (struct RTCPointQueryFunctionArguments* args,
- struct geometry* geom,
- struct geometry* inst,
- void* query_data)
-{
- struct s3d_hit hit = S3D_HIT_NULL;
- struct s3d_hit* out_hit = NULL;
- struct hit_filter* filter = NULL;
- float query_pos[3];
- float sphere_pos[3];
- float Ng[3];
- float dir[3];
- float uv[2];
- float dst;
- float len;
- int flip_surface = 0;
- ASSERT(args && geom && geom->type == GEOM_SPHERE && args->primID == 1);
-
- /* Local copy of the query position */
- query_pos[0] = args->query->x;
- query_pos[1] = args->query->y;
- query_pos[2] = args->query->z;
-
- f3_set(sphere_pos, geom->data.sphere->pos);
- if(args->context->instStackSize) { /* The sphere is instantiated */
- const float* transform;
- transform = inst->data.instance->transform;
- ASSERT(args->context->instStackSize == 1);
- ASSERT(args->similarityScale == 1);
- ASSERT(inst && inst->type == GEOM_INSTANCE);
- ASSERT(f3_eq_eps(transform+0, args->context->inst2world[0]+0, 1.e-6f));
- ASSERT(f3_eq_eps(transform+3, args->context->inst2world[0]+4, 1.e-6f));
- ASSERT(f3_eq_eps(transform+6, args->context->inst2world[0]+8, 1.e-6f));
- ASSERT(f3_eq_eps(transform+9, args->context->inst2world[0]+12,1.e-6f));
-
- /* Transform the sphere position in world space */
- f33_mulf3(sphere_pos, transform, sphere_pos);
- f3_add(sphere_pos, transform+9, sphere_pos);
-
- flip_surface = inst->flip_surface;
- }
-
- /* Compute the distance from the the query pos to the sphere center */
- f3_sub(Ng, query_pos, sphere_pos);
- len = f3_len(Ng);
-
- /* Evaluate the distance from the query pos to the sphere surface */
- dst = fabsf(len - geom->data.sphere->radius);
-
- /* The closest point onto the sphere is outside the query radius */
- if(dst >= args->query->radius)
- return 0;
-
- /* Compute the uv of the found point */
- f3_divf(Ng, Ng, len); /* Normalize the hit normal */
- sphere_normal_to_uv(Ng, uv);
-
- /* Flip the geometric normal wrt the flip surface flag */
- flip_surface ^= geom->flip_surface;
- if(flip_surface) f3_minus(Ng, Ng);
-
- /* Setup the hit */
- hit.prim.shape__ = geom;
- hit.prim.inst__ = inst;
- hit.distance = dst;
- hit.uv[0] = uv[0];
- hit.uv[1] = uv[1];
- hit.normal[0] = Ng[0];
- hit.normal[1] = Ng[1];
- hit.normal[2] = Ng[2];
- hit.prim.prim_id = args->primID;
- hit.prim.geom_id = geom->name;
- hit.prim.inst_id = inst ? inst->name : S3D_INVALID_ID;
- hit.prim.scene_prim_id =
- hit.prim.prim_id
- + geom->scene_prim_id_offset
- + (inst ? inst->scene_prim_id_offset : 0);
-
- /* Use the reversed geometric normal as the hit direction since it is along
- * this vector that the closest point was effectively computed */
- f3_minus(dir, Ng);
- filter = &geom->data.sphere->filter;
- if(filter->func
- && filter->func(&hit, query_pos, dir, query_data, filter->data)) {
- return 0;
- }
-
- /* Update output data */
- out_hit = args->userPtr;
- *out_hit = hit;
-
- /* Shrink the query radius */
- args->query->radius = dst;
-
- return 1; /* Notify that the query radius was updated */
-}
-
-static bool
-closest_point(struct RTCPointQueryFunctionArguments* args)
-{
- struct point_query_context* ctx = NULL;
- struct geometry* geom = NULL;
- struct geometry* inst = NULL;
- bool query_radius_is_upd = false;
- ASSERT(args);
-
- ctx = CONTAINER_OF(args->context, struct point_query_context, rtc);
- if(args->context->instStackSize == 0) {
- geom = scene_view_geometry_from_embree_id
- (ctx->scnview, args->geomID);
- } else {
- ASSERT(args->context->instStackSize == 1);
- ASSERT(args->similarityScale == 1);
- inst = scene_view_geometry_from_embree_id
- (ctx->scnview, args->context->instID[0]);
- geom = scene_view_geometry_from_embree_id
- (inst->data.instance->scnview, args->geomID);
- }
-
- switch(geom->type) {
- case GEOM_MESH:
- query_radius_is_upd = closest_point_mesh(args, geom, inst, ctx->data);
- break;
- case GEOM_SPHERE:
- query_radius_is_upd = closest_point_sphere(args, geom, inst, ctx->data);
- break;
- default: FATAL("Unreachable code\n"); break;
- }
- return query_radius_is_upd;
-}
-
-/*******************************************************************************
- * Exported functions
- ******************************************************************************/
-res_T
-s3d_scene_view_point_query
- (struct s3d_scene_view* scnview,
- const float pos[3],
- const float radius,
- void* query_data,
- struct s3d_hit* hit)
-{
- struct RTCPointQuery query;
- struct point_query_context query_ctx;
-
- if(!scnview || !pos || radius <= 0 || !hit)
- return RES_BAD_ARG;
- if((scnview->mask & S3D_TRACE) == 0) {
- log_error(scnview->scn->dev,
- "%s: the S3D_TRACE flag is not active onto the submitted scene view.\n",
- FUNC_NAME);
- return RES_BAD_OP;
- }
-
- *hit = S3D_HIT_NULL;
-
- /* Initialise the point query */
- query.x = pos[0];
- query.y = pos[1];
- query.z = pos[2];
- query.radius = radius;
- query.time = FLT_MAX; /* Invalid fields */
-
- /* Initialise the point query context */
- rtcInitPointQueryContext(&query_ctx.rtc);
- query_ctx.scnview = scnview;
- query_ctx.data = query_data;
-
- /* Here we go! */
- rtcPointQuery(scnview->rtc_scn, &query, &query_ctx.rtc, closest_point, hit);
-
- return RES_OK;
-}
-
diff --git a/src/test_s3d_closest_point.c b/src/test_s3d_closest_point.c
@@ -0,0 +1,589 @@
+/* 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_cbox.h"
+#include "test_s3d_utils.h"
+
+#include <rsys/float3.h>
+#include <limits.h>
+
+#define ON_EDGE_EPSILON 1.e-4f
+#define POSITION_EPSILON 1.e-3f
+
+struct closest_pt {
+ float pos[3];
+ float normal[3];
+ float dst;
+ unsigned iprim;
+};
+
+#define CLOSEST_PT_NULL__ {{0,0,0},{0,0,0},FLT_MAX,UINT_MAX}
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+/* 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_closest_point function that
+ * internally relies 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_normalize(Nbc, 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_normalize(Nbc, 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_normalize(Nca, 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));
+}
+
+static void
+closest_point_mesh
+ (const float pos[3],
+ const float* verts,
+ const unsigned* ids,
+ const unsigned ntris,
+ struct closest_pt* pt)
+{
+ unsigned itri;
+ CHK(pos && verts && ids && pt);
+
+ pt->pos[0] = pt->pos[1] = pt->pos[2] = FLT_MAX;
+ pt->dst = FLT_MAX;
+ pt->iprim = UINT_MAX;
+
+ /* Find the closest point on the mesh */
+ FOR_EACH(itri, 0, ntris) {
+ float v0[3];
+ float v1[3];
+ float v2[3];
+ float closest_pt[3];
+ float vec[3];
+ float dst;
+
+ f3_set(v0, verts+ids[itri*3+0]*3);
+ f3_set(v1, verts+ids[itri*3+1]*3);
+ f3_set(v2, verts+ids[itri*3+2]*3);
+
+ closest_point_triangle(pos, v0, v1, v2, closest_pt);
+ dst = f3_len(f3_sub(vec, closest_pt, pos));
+
+ if(dst < pt->dst) {
+ float E0[3], E1[3];
+ f3_set(pt->pos, closest_pt);
+ pt->dst = dst;
+ pt->iprim = itri;
+ f3_sub(E0, v1, v0);
+ f3_sub(E1, v2, v0);
+ f3_cross(pt->normal, E1, E0);
+ f3_normalize(pt->normal, pt->normal);
+ }
+ }
+}
+
+/* Check that `hit' roughly lies on an edge. */
+static int
+hit_on_edge(const struct s3d_hit* hit)
+{
+ struct s3d_attrib v0, v1, v2, pos;
+ float E0[3], E1[3], N[3];
+ float tri_2area;
+ float hit_2area0;
+ float hit_2area1;
+ float hit_2area2;
+ float hit_pos[3];
+
+ CHK(hit && !S3D_HIT_NONE(hit));
+
+ /* Retrieve the triangle vertices */
+ CHK(s3d_triangle_get_vertex_attrib(&hit->prim, 0, S3D_POSITION, &v0)==RES_OK);
+ CHK(s3d_triangle_get_vertex_attrib(&hit->prim, 1, S3D_POSITION, &v1)==RES_OK);
+ CHK(s3d_triangle_get_vertex_attrib(&hit->prim, 2, S3D_POSITION, &v2)==RES_OK);
+
+ /* Compute the triangle area * 2 */
+ f3_sub(E0, v1.value, v0.value);
+ f3_sub(E1, v2.value, v0.value);
+ tri_2area = f3_len(f3_cross(N, E0, E1));
+
+ /* Compute the hit position */
+ CHK(s3d_primitive_get_attrib(&hit->prim, S3D_POSITION, hit->uv, &pos) == RES_OK);
+ f3_set(hit_pos, pos.value);
+
+ /* Compute areas */
+ f3_sub(E0, v0.value, hit_pos);
+ f3_sub(E1, v1.value, hit_pos);
+ hit_2area0 = f3_len(f3_cross(N, E0, E1));
+ f3_sub(E0, v1.value, hit_pos);
+ f3_sub(E1, v2.value, hit_pos);
+ hit_2area1 = f3_len(f3_cross(N, E0, E1));
+ f3_sub(E0, v2.value, hit_pos);
+ f3_sub(E1, v0.value, hit_pos);
+ hit_2area2 = f3_len(f3_cross(N, E0, E1));
+
+ if(hit_2area0 / tri_2area < ON_EDGE_EPSILON
+ || hit_2area1 / tri_2area < ON_EDGE_EPSILON
+ || hit_2area2 / tri_2area < ON_EDGE_EPSILON)
+ return 1;
+
+ return 0;
+}
+
+/*******************************************************************************
+ * Cornell box test
+ ******************************************************************************/
+enum cbox_geom {
+ CBOX_WALLS,
+ CBOX_TALL_BLOCK,
+ CBOX_SHORT_BLOCK,
+ CBOX_GEOMS_COUNT__
+};
+
+struct cbox_filter_data {
+ float query_pos[3];
+ unsigned geom_to_filter[3];
+};
+
+static int
+cbox_filter
+ (const struct s3d_hit* hit,
+ const float org[3],
+ const float dir[3],
+ void* query_data,
+ void* filter_data)
+{
+ struct cbox_filter_data* data = query_data;
+ struct s3d_attrib attr;
+ float pos[3];
+ float vec[3];
+
+ CHK(hit && org && dir && !S3D_HIT_NONE(hit));
+ CHK((intptr_t)filter_data == (intptr_t)0xDECAFBAD);
+ CHK(f3_normalize(vec, dir) != 0);
+
+ f3_add(pos, org, f3_mulf(pos, vec, hit->distance));
+ CHK(s3d_primitive_get_attrib
+ (&hit->prim, S3D_POSITION, hit->uv, &attr) == RES_OK);
+ CHK(f3_eq_eps(attr.value, pos, POSITION_EPSILON));
+
+ if(!query_data) return 0;
+
+ CHK(f3_eq_eps(data->query_pos, org, POSITION_EPSILON));
+
+ return data->geom_to_filter[0] == hit->prim.geom_id
+ || data->geom_to_filter[1] == hit->prim.geom_id
+ || data->geom_to_filter[2] == hit->prim.geom_id;
+}
+
+static void
+check_closest_point_cbox
+ (const float pos[3],
+ const unsigned geom_id[3],
+ struct s3d_hit* hit)
+{
+ struct closest_pt pt[CBOX_GEOMS_COUNT__] = {
+ CLOSEST_PT_NULL__, CLOSEST_PT_NULL__, CLOSEST_PT_NULL__
+ };
+ enum cbox_geom geom;
+
+ CHK(pos && geom_id && hit);
+
+ if(geom_id[CBOX_WALLS] != S3D_INVALID_ID) { /* Are the walls filtered */
+ closest_point_mesh(pos, cbox_walls, cbox_walls_ids, cbox_walls_ntris,
+ &pt[CBOX_WALLS]);
+ geom = CBOX_WALLS;
+ }
+ if(geom_id[CBOX_TALL_BLOCK] != S3D_INVALID_ID) { /* Is the block filtered */
+ closest_point_mesh(pos, cbox_tall_block, cbox_block_ids, cbox_block_ntris,
+ &pt[CBOX_TALL_BLOCK]);
+ }
+ if(geom_id[CBOX_SHORT_BLOCK] != S3D_INVALID_ID) { /* Is the block filtered */
+ closest_point_mesh(pos, cbox_short_block, cbox_block_ids, cbox_block_ntris,
+ &pt[CBOX_SHORT_BLOCK]);
+ }
+ geom = pt[CBOX_WALLS].dst < pt[CBOX_TALL_BLOCK].dst
+ ? CBOX_WALLS : CBOX_TALL_BLOCK;
+ geom = pt[CBOX_SHORT_BLOCK].dst < pt[geom].dst
+ ? CBOX_SHORT_BLOCK : geom;
+
+ if(pt[geom].dst >= FLT_MAX) { /* All geometries were filtered */
+ CHK(S3D_HIT_NONE(hit));
+ } else {
+ struct s3d_attrib attr;
+ float N[3];
+
+ CHK(!S3D_HIT_NONE(hit));
+
+ CHK(s3d_primitive_get_attrib
+ (&hit->prim, S3D_POSITION, hit->uv, &attr) == RES_OK);
+ f3_normalize(N, hit->normal);
+
+ if(!hit_on_edge(hit)) CHK(hit->prim.prim_id == pt[geom].iprim);
+ if(hit->prim.prim_id == pt[geom].iprim) {
+ /* Due to numerical inaccuracies and the arbitrary order in which
+ * primitives are treated, 2 points on different primitive can have the
+ * same distance from the query position while their respective
+ * coordinates are not equal wrt POSITION_EPSILON. To avoid wrong
+ * assertion, we thus check the position returned by Star-3D against the
+ * manually computed position only if these positions lies on the same
+ * primitive */
+ CHK(f3_eq_eps(pt[geom].pos, attr.value, POSITION_EPSILON));
+ CHK(f3_eq_eps(pt[geom].normal, N, 1.e-4f));
+ }
+
+ CHK(hit->prim.geom_id == geom_id[geom]);
+ CHK(hit->prim.inst_id == S3D_INVALID_ID);
+ CHK(eq_epsf(hit->distance, pt[geom].dst, 1.e-4f));
+ }
+}
+
+static void
+test_cbox(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_shape* walls = NULL;
+ struct s3d_shape* tall_block = NULL;
+ struct s3d_shape* short_block = NULL;
+ struct s3d_scene_view* scnview = NULL;
+ struct cbox_desc walls_desc;
+ struct cbox_desc tall_block_desc;
+ struct cbox_desc short_block_desc;
+ struct cbox_filter_data filter_data;
+ void* ptr = (void*)((intptr_t)0xDECAFBAD);
+ float pos[3];
+ float low[3], upp[3], mid[3];
+ unsigned geom_id[CBOX_GEOMS_COUNT__];
+ size_t i;
+
+ CHK(s3d_scene_create(dev, &scn) == RES_OK);
+ CHK(s3d_shape_create_mesh(dev, &walls) == RES_OK);
+ CHK(s3d_shape_create_mesh(dev, &tall_block) == RES_OK);
+ CHK(s3d_shape_create_mesh(dev, &short_block) == RES_OK);
+ CHK(s3d_shape_get_id(walls, &geom_id[CBOX_WALLS]) == RES_OK);
+ CHK(s3d_shape_get_id(tall_block, &geom_id[CBOX_TALL_BLOCK]) == RES_OK);
+ CHK(s3d_shape_get_id(short_block, &geom_id[CBOX_SHORT_BLOCK]) == RES_OK);
+ CHK(s3d_mesh_set_hit_filter_function(walls, cbox_filter, ptr) == RES_OK);
+ CHK(s3d_mesh_set_hit_filter_function(tall_block, cbox_filter, ptr) == RES_OK);
+ CHK(s3d_mesh_set_hit_filter_function(short_block, cbox_filter, ptr) == RES_OK);
+ CHK(s3d_scene_attach_shape(scn, walls) == RES_OK);
+ CHK(s3d_scene_attach_shape(scn, tall_block) == RES_OK);
+ CHK(s3d_scene_attach_shape(scn, short_block) == RES_OK);
+
+ vdata.usage = S3D_POSITION;
+ vdata.type = S3D_FLOAT3;
+ vdata.get = cbox_get_position;
+
+ walls_desc.vertices = cbox_walls;
+ walls_desc.indices = cbox_walls_ids;
+ CHK(s3d_mesh_setup_indexed_vertices(walls, cbox_walls_ntris, cbox_get_ids,
+ cbox_walls_nverts, &vdata, 1, &walls_desc) == RES_OK);
+
+ tall_block_desc.vertices = cbox_tall_block;
+ tall_block_desc.indices = cbox_block_ids;
+ CHK(s3d_mesh_setup_indexed_vertices(tall_block, cbox_block_ntris, cbox_get_ids,
+ cbox_block_nverts, &vdata, 1, &tall_block_desc) == RES_OK);
+
+ short_block_desc.vertices = cbox_short_block;
+ short_block_desc.indices = cbox_block_ids;
+ CHK(s3d_mesh_setup_indexed_vertices(short_block, cbox_block_ntris, cbox_get_ids,
+ cbox_block_nverts, &vdata, 1, &short_block_desc) == RES_OK);
+
+ CHK(s3d_scene_view_create(scn, S3D_TRACE, &scnview) == RES_OK);
+ CHK(s3d_scene_view_get_aabb(scnview, low, upp) == RES_OK);
+ mid[0] = (low[0] + upp[0]) * 0.5f;
+ mid[1] = (low[1] + upp[1]) * 0.5f;
+ mid[2] = (low[2] + upp[2]) * 0.5f;
+
+ /* Check point query on Cornell box */
+ FOR_EACH(i, 0, 10000) {
+ /* Randomly generate a point in a bounding box that is 2 times the size of
+ * the triangle AABB */
+ pos[0] = mid[0] + (rand_canonic() * 2 - 1) * (upp[0] - low[0]);
+ pos[1] = mid[1] + (rand_canonic() * 2 - 1) * (upp[1] - low[1]);
+ pos[2] = mid[2] + (rand_canonic() * 2 - 1) * (upp[2] - low[2]);
+
+ CHK(s3d_scene_view_closest_point(scnview, pos, (float)INF, NULL, &hit) == RES_OK);
+ check_closest_point_cbox(pos, geom_id, &hit);
+ }
+
+ /* Filter the Cornell box blocks */
+ filter_data.geom_to_filter[0] = geom_id[CBOX_TALL_BLOCK];
+ filter_data.geom_to_filter[1] = geom_id[CBOX_SHORT_BLOCK];
+ filter_data.geom_to_filter[2] = S3D_INVALID_ID;
+ geom_id[CBOX_TALL_BLOCK] = S3D_INVALID_ID;
+ geom_id[CBOX_SHORT_BLOCK] = S3D_INVALID_ID;
+
+ /* Check point query filtering filtering */
+ FOR_EACH(i, 0, 10000) {
+ /* Randomly generate a point in a bounding box that is 2 times the size of
+ * the triangle AABB */
+ pos[0] = mid[0] + (rand_canonic() * 2 - 1) * (upp[0] - low[0]);
+ pos[1] = mid[1] + (rand_canonic() * 2 - 1) * (upp[1] - low[1]);
+ pos[2] = mid[2] + (rand_canonic() * 2 - 1) * (upp[2] - low[2]);
+
+ f3_set(filter_data.query_pos, pos);
+
+ CHK(s3d_scene_view_closest_point
+ (scnview, pos, (float)INF, &filter_data, &hit) == RES_OK);
+
+ check_closest_point_cbox(pos, geom_id, &hit);
+ }
+
+ CHK(s3d_shape_ref_put(walls) == RES_OK);
+ CHK(s3d_shape_ref_put(tall_block) == RES_OK);
+ CHK(s3d_shape_ref_put(short_block) == RES_OK);
+ CHK(s3d_scene_ref_put(scn) == RES_OK);
+ CHK(s3d_scene_view_ref_put(scnview) == RES_OK);
+}
+
+/*******************************************************************************
+ * 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], mid[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]);
+ mid[0] = (low[0] + upp[0]) * 0.5f;
+ mid[1] = (low[1] + upp[1]) * 0.5f;
+ mid[2] = (low[2] + upp[2]) * 0.5f;
+
+ 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] = mid[0] + (rand_canonic() * 2 - 1) * (upp[0] - low[0]) * 5.f;
+ pos[1] = mid[1] + (rand_canonic() * 2 - 1) * (upp[1] - low[1]) * 5.f;
+ pos[2] = mid[2] + (rand_canonic() * 2 - 1) * (upp[2] - low[2]) * 5.f;
+
+ 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);
+
+ /* 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] = mid[0] + (rand_canonic() * 2 - 1) * (upp[0] - low[0]) * 5.f;
+ pos[1] = mid[1] + (rand_canonic() * 2 - 1) * (upp[1] - low[1]) * 5.f;
+ pos[2] = mid[2] + (rand_canonic() * 2 - 1) * (upp[2] - low[2]) * 5.f;
+
+ CHK(s3d_scene_view_closest_point(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_closest_point(view, pos, radius, NULL, &hit) == RES_OK);
+ CHK(S3D_HIT_NONE(&hit));
+ radius = nextafterf(radius, FLT_MAX);
+ CHK(s3d_scene_view_closest_point(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_closest_point(NULL, pos, 1.f, NULL, &hit) == RES_BAD_ARG);
+ CHK(s3d_scene_view_closest_point(view, NULL, 1.f, NULL, &hit) == RES_BAD_ARG);
+ CHK(s3d_scene_view_closest_point(view, pos, 0.f, NULL, &hit) == RES_BAD_ARG);
+ CHK(s3d_scene_view_closest_point(view, pos, 1.f, NULL, NULL) == RES_BAD_ARG);
+ CHK(s3d_scene_view_closest_point(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_closest_point(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);
+ test_cbox(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;
+}
diff --git a/src/test_s3d_point_query.c b/src/test_s3d_point_query.c
@@ -1,589 +0,0 @@
-/* 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_cbox.h"
-#include "test_s3d_utils.h"
-
-#include <rsys/float3.h>
-#include <limits.h>
-
-#define ON_EDGE_EPSILON 1.e-4f
-#define POSITION_EPSILON 1.e-3f
-
-struct closest_pt {
- float pos[3];
- float normal[3];
- float dst;
- unsigned iprim;
-};
-
-#define CLOSEST_PT_NULL__ {{0,0,0},{0,0,0},FLT_MAX,UINT_MAX}
-
-/*******************************************************************************
- * Helper functions
- ******************************************************************************/
-/* 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 relies 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_normalize(Nbc, 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_normalize(Nbc, 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_normalize(Nca, 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));
-}
-
-static void
-closest_point_mesh
- (const float pos[3],
- const float* verts,
- const unsigned* ids,
- const unsigned ntris,
- struct closest_pt* pt)
-{
- unsigned itri;
- CHK(pos && verts && ids && pt);
-
- pt->pos[0] = pt->pos[1] = pt->pos[2] = FLT_MAX;
- pt->dst = FLT_MAX;
- pt->iprim = UINT_MAX;
-
- /* Find the closest point on the mesh */
- FOR_EACH(itri, 0, ntris) {
- float v0[3];
- float v1[3];
- float v2[3];
- float closest_pt[3];
- float vec[3];
- float dst;
-
- f3_set(v0, verts+ids[itri*3+0]*3);
- f3_set(v1, verts+ids[itri*3+1]*3);
- f3_set(v2, verts+ids[itri*3+2]*3);
-
- closest_point_triangle(pos, v0, v1, v2, closest_pt);
- dst = f3_len(f3_sub(vec, closest_pt, pos));
-
- if(dst < pt->dst) {
- float E0[3], E1[3];
- f3_set(pt->pos, closest_pt);
- pt->dst = dst;
- pt->iprim = itri;
- f3_sub(E0, v1, v0);
- f3_sub(E1, v2, v0);
- f3_cross(pt->normal, E1, E0);
- f3_normalize(pt->normal, pt->normal);
- }
- }
-}
-
-/* Check that `hit' roughly lies on an edge. */
-static int
-hit_on_edge(const struct s3d_hit* hit)
-{
- struct s3d_attrib v0, v1, v2, pos;
- float E0[3], E1[3], N[3];
- float tri_2area;
- float hit_2area0;
- float hit_2area1;
- float hit_2area2;
- float hit_pos[3];
-
- CHK(hit && !S3D_HIT_NONE(hit));
-
- /* Retrieve the triangle vertices */
- CHK(s3d_triangle_get_vertex_attrib(&hit->prim, 0, S3D_POSITION, &v0)==RES_OK);
- CHK(s3d_triangle_get_vertex_attrib(&hit->prim, 1, S3D_POSITION, &v1)==RES_OK);
- CHK(s3d_triangle_get_vertex_attrib(&hit->prim, 2, S3D_POSITION, &v2)==RES_OK);
-
- /* Compute the triangle area * 2 */
- f3_sub(E0, v1.value, v0.value);
- f3_sub(E1, v2.value, v0.value);
- tri_2area = f3_len(f3_cross(N, E0, E1));
-
- /* Compute the hit position */
- CHK(s3d_primitive_get_attrib(&hit->prim, S3D_POSITION, hit->uv, &pos) == RES_OK);
- f3_set(hit_pos, pos.value);
-
- /* Compute areas */
- f3_sub(E0, v0.value, hit_pos);
- f3_sub(E1, v1.value, hit_pos);
- hit_2area0 = f3_len(f3_cross(N, E0, E1));
- f3_sub(E0, v1.value, hit_pos);
- f3_sub(E1, v2.value, hit_pos);
- hit_2area1 = f3_len(f3_cross(N, E0, E1));
- f3_sub(E0, v2.value, hit_pos);
- f3_sub(E1, v0.value, hit_pos);
- hit_2area2 = f3_len(f3_cross(N, E0, E1));
-
- if(hit_2area0 / tri_2area < ON_EDGE_EPSILON
- || hit_2area1 / tri_2area < ON_EDGE_EPSILON
- || hit_2area2 / tri_2area < ON_EDGE_EPSILON)
- return 1;
-
- return 0;
-}
-
-/*******************************************************************************
- * Cornell box test
- ******************************************************************************/
-enum cbox_geom {
- CBOX_WALLS,
- CBOX_TALL_BLOCK,
- CBOX_SHORT_BLOCK,
- CBOX_GEOMS_COUNT__
-};
-
-struct cbox_filter_data {
- float query_pos[3];
- unsigned geom_to_filter[3];
-};
-
-static int
-cbox_filter
- (const struct s3d_hit* hit,
- const float org[3],
- const float dir[3],
- void* query_data,
- void* filter_data)
-{
- struct cbox_filter_data* data = query_data;
- struct s3d_attrib attr;
- float pos[3];
- float vec[3];
-
- CHK(hit && org && dir && !S3D_HIT_NONE(hit));
- CHK((intptr_t)filter_data == (intptr_t)0xDECAFBAD);
- f3_normalize(vec, dir);
-
- f3_add(pos, org, f3_mulf(pos, vec, hit->distance));
- CHK(s3d_primitive_get_attrib
- (&hit->prim, S3D_POSITION, hit->uv, &attr) == RES_OK);
- CHK(f3_eq_eps(attr.value, pos, POSITION_EPSILON));
-
- if(!query_data) return 0;
-
- CHK(f3_eq_eps(data->query_pos, org, POSITION_EPSILON));
-
- return data->geom_to_filter[0] == hit->prim.geom_id
- || data->geom_to_filter[1] == hit->prim.geom_id
- || data->geom_to_filter[2] == hit->prim.geom_id;
-}
-
-static void
-check_closest_point_cbox
- (const float pos[3],
- const unsigned geom_id[3],
- struct s3d_hit* hit)
-{
- struct closest_pt pt[CBOX_GEOMS_COUNT__] = {
- CLOSEST_PT_NULL__, CLOSEST_PT_NULL__, CLOSEST_PT_NULL__
- };
- enum cbox_geom geom;
-
- CHK(pos && geom_id && hit);
-
- if(geom_id[CBOX_WALLS] != S3D_INVALID_ID) {
- closest_point_mesh(pos, cbox_walls, cbox_walls_ids, cbox_walls_ntris,
- &pt[CBOX_WALLS]);
- geom = CBOX_WALLS;
- }
- if(geom_id[CBOX_TALL_BLOCK] != S3D_INVALID_ID) {
- closest_point_mesh(pos, cbox_tall_block, cbox_block_ids, cbox_block_ntris,
- &pt[CBOX_TALL_BLOCK]);
- }
- if(geom_id[CBOX_SHORT_BLOCK] != S3D_INVALID_ID) {
- closest_point_mesh(pos, cbox_short_block, cbox_block_ids, cbox_block_ntris,
- &pt[CBOX_SHORT_BLOCK]);
- }
- geom = pt[CBOX_WALLS].dst < pt[CBOX_TALL_BLOCK].dst
- ? CBOX_WALLS : CBOX_TALL_BLOCK;
- geom = pt[CBOX_SHORT_BLOCK].dst < pt[geom].dst
- ? CBOX_SHORT_BLOCK : geom;
-
- if(pt[geom].dst >= FLT_MAX) {
- CHK(S3D_HIT_NONE(hit));
- } else {
- struct s3d_attrib attr;
- float N[3];
-
- CHK(!S3D_HIT_NONE(hit));
-
- CHK(s3d_primitive_get_attrib
- (&hit->prim, S3D_POSITION, hit->uv, &attr) == RES_OK);
- f3_normalize(N, hit->normal);
-
- if(!hit_on_edge(hit)) { CHK(hit->prim.prim_id == pt[geom].iprim); }
- if(hit->prim.prim_id == pt[geom].iprim) {
- /* Due to numerical inaccuracies and the arbitrary order in which
- * primitives are treated, 2 points on different primitive can have the
- * same distance from the query position while their respective
- * coordinates are not equal wrt POSITION_EPSILON. To avoid wrong
- * assertion, we thus check the position returned by Star-3D against the
- * manually computed position only if these positions lies on the same
- * primitive */
- CHK(f3_eq_eps(pt[geom].pos, attr.value, POSITION_EPSILON));
- CHK(f3_eq_eps(pt[geom].normal, N, 1.e-4f));
- }
-
- CHK(hit->prim.geom_id == geom_id[geom]);
- CHK(hit->prim.inst_id == S3D_INVALID_ID);
- CHK(eq_epsf(hit->distance, pt[geom].dst, 1.e-4f));
- }
-}
-
-static void
-test_cbox(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_shape* walls = NULL;
- struct s3d_shape* tall_block = NULL;
- struct s3d_shape* short_block = NULL;
- struct s3d_scene_view* scnview = NULL;
- struct cbox_desc walls_desc;
- struct cbox_desc tall_block_desc;
- struct cbox_desc short_block_desc;
- struct cbox_filter_data filter_data;
- void* ptr = (void*)((intptr_t)0xDECAFBAD);
- float pos[3];
- float low[3], upp[3], mid[3];
- unsigned geom_id[CBOX_GEOMS_COUNT__];
- size_t i;
-
- CHK(s3d_scene_create(dev, &scn) == RES_OK);
- CHK(s3d_shape_create_mesh(dev, &walls) == RES_OK);
- CHK(s3d_shape_create_mesh(dev, &tall_block) == RES_OK);
- CHK(s3d_shape_create_mesh(dev, &short_block) == RES_OK);
- CHK(s3d_shape_get_id(walls, &geom_id[CBOX_WALLS]) == RES_OK);
- CHK(s3d_shape_get_id(tall_block, &geom_id[CBOX_TALL_BLOCK]) == RES_OK);
- CHK(s3d_shape_get_id(short_block, &geom_id[CBOX_SHORT_BLOCK]) == RES_OK);
- CHK(s3d_mesh_set_hit_filter_function(walls, cbox_filter, ptr) == RES_OK);
- CHK(s3d_mesh_set_hit_filter_function(tall_block, cbox_filter, ptr) == RES_OK);
- CHK(s3d_mesh_set_hit_filter_function(short_block, cbox_filter, ptr) == RES_OK);
- CHK(s3d_scene_attach_shape(scn, walls) == RES_OK);
- CHK(s3d_scene_attach_shape(scn, tall_block) == RES_OK);
- CHK(s3d_scene_attach_shape(scn, short_block) == RES_OK);
-
- vdata.usage = S3D_POSITION;
- vdata.type = S3D_FLOAT3;
- vdata.get = cbox_get_position;
-
- walls_desc.vertices = cbox_walls;
- walls_desc.indices = cbox_walls_ids;
- CHK(s3d_mesh_setup_indexed_vertices(walls, cbox_walls_ntris, cbox_get_ids,
- cbox_walls_nverts, &vdata, 1, &walls_desc) == RES_OK);
-
- tall_block_desc.vertices = cbox_tall_block;
- tall_block_desc.indices = cbox_block_ids;
- CHK(s3d_mesh_setup_indexed_vertices(tall_block, cbox_block_ntris, cbox_get_ids,
- cbox_block_nverts, &vdata, 1, &tall_block_desc) == RES_OK);
-
- short_block_desc.vertices = cbox_short_block;
- short_block_desc.indices = cbox_block_ids;
- CHK(s3d_mesh_setup_indexed_vertices(short_block, cbox_block_ntris, cbox_get_ids,
- cbox_block_nverts, &vdata, 1, &short_block_desc) == RES_OK);
-
- CHK(s3d_scene_view_create(scn, S3D_TRACE, &scnview) == RES_OK);
- CHK(s3d_scene_view_get_aabb(scnview, low, upp) == RES_OK);
- mid[0] = (low[0] + upp[0]) * 0.5f;
- mid[1] = (low[1] + upp[1]) * 0.5f;
- mid[2] = (low[2] + upp[2]) * 0.5f;
-
- /* Check point query on Cornell box */
- FOR_EACH(i, 0, 10000) {
- /* Randomly generate a point in a bounding box that is 2 times the size of
- * the triangle AABB */
- pos[0] = mid[0] + (rand_canonic() * 2 - 1) * (upp[0] - low[0]);
- pos[1] = mid[1] + (rand_canonic() * 2 - 1) * (upp[1] - low[1]);
- pos[2] = mid[2] + (rand_canonic() * 2 - 1) * (upp[2] - low[2]);
-
- CHK(s3d_scene_view_point_query(scnview, pos, (float)INF, NULL, &hit) == RES_OK);
- check_closest_point_cbox(pos, geom_id, &hit);
- }
-
- /* Setup filter data to filter the Cornell box blocks */
- filter_data.geom_to_filter[0] = geom_id[CBOX_TALL_BLOCK];
- filter_data.geom_to_filter[1] = geom_id[CBOX_SHORT_BLOCK];
- filter_data.geom_to_filter[2] = S3D_INVALID_ID;
- geom_id[CBOX_TALL_BLOCK] = S3D_INVALID_ID;
- geom_id[CBOX_SHORT_BLOCK] = S3D_INVALID_ID;
-
- /* Check point query filtering filtering */
- FOR_EACH(i, 0, 10000) {
- /* Randomly generate a point in a bounding box that is 2 times the size of
- * the triangle AABB */
- pos[0] = mid[0] + (rand_canonic() * 2 - 1) * (upp[0] - low[0]);
- pos[1] = mid[1] + (rand_canonic() * 2 - 1) * (upp[1] - low[1]);
- pos[2] = mid[2] + (rand_canonic() * 2 - 1) * (upp[2] - low[2]);
-
- f3_set(filter_data.query_pos, pos);
-
- CHK(s3d_scene_view_point_query
- (scnview, pos, (float)INF, &filter_data, &hit) == RES_OK);
-
- check_closest_point_cbox(pos, geom_id, &hit);
- }
-
- CHK(s3d_shape_ref_put(walls) == RES_OK);
- CHK(s3d_shape_ref_put(tall_block) == RES_OK);
- CHK(s3d_shape_ref_put(short_block) == RES_OK);
- CHK(s3d_scene_ref_put(scn) == RES_OK);
- CHK(s3d_scene_view_ref_put(scnview) == RES_OK);
-}
-
-/*******************************************************************************
- * 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], mid[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]);
- mid[0] = (low[0] + upp[0]) * 0.5f;
- mid[1] = (low[1] + upp[1]) * 0.5f;
- mid[2] = (low[2] + upp[2]) * 0.5f;
-
- 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] = mid[0] + (rand_canonic() * 2 - 1) * (upp[0] - low[0]) * 5.f;
- pos[1] = mid[1] + (rand_canonic() * 2 - 1) * (upp[1] - low[1]) * 5.f;
- pos[2] = mid[2] + (rand_canonic() * 2 - 1) * (upp[2] - low[2]) * 5.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] = mid[0] + (rand_canonic() * 2 - 1) * (upp[0] - low[0]) * 5.f;
- pos[1] = mid[1] + (rand_canonic() * 2 - 1) * (upp[1] - low[1]) * 5.f;
- pos[2] = mid[2] + (rand_canonic() * 2 - 1) * (upp[2] - low[2]) * 5.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);
- test_cbox(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;
-}