commit 94741eb855008802f77dd2a966a27e3c16c8d442
parent d3c777cdd5e42da09033627c31b78bbaed84c080
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Thu, 10 Oct 2019 16:46:59 +0200
Push further the closest point tests
Test the closest point query on a scene with a mesh and a sphere and on
this scene instantiated 2 times.
Diffstat:
1 file changed, 266 insertions(+), 33 deletions(-)
diff --git a/src/test_s3d_closest_point.c b/src/test_s3d_closest_point.c
@@ -45,9 +45,20 @@ struct closest_pt {
float normal[3];
float dst;
unsigned iprim;
+ unsigned igeom;
+ unsigned iinst;
};
-#define CLOSEST_PT_NULL__ {{0,0,0},{0,0,0},FLT_MAX,UINT_MAX}
+#define CLOSEST_PT_NULL__ { \
+ {0,0,0}, \
+ {0,0,0}, \
+ FLT_MAX, \
+ S3D_INVALID_ID, \
+ S3D_INVALID_ID, \
+ S3D_INVALID_ID \
+}
+
+static const struct closest_pt CLOSEST_PT_NULL = CLOSEST_PT_NULL__;
/*******************************************************************************
* Helper functions
@@ -127,14 +138,17 @@ closest_point_mesh
const float* verts,
const unsigned* ids,
const unsigned ntris,
+ const unsigned geom_id,
+ const unsigned inst_id,
struct closest_pt* pt)
{
unsigned itri;
CHK(pos && verts && ids && pt);
- pt->pos[0] = pt->pos[1] = pt->pos[2] = FLT_MAX;
+ *pt = CLOSEST_PT_NULL;
+ pt->igeom = geom_id;
+ pt->iinst = inst_id;
pt->dst = FLT_MAX;
- pt->iprim = UINT_MAX;
/* Find the closest point on the mesh */
FOR_EACH(itri, 0, ntris) {
@@ -165,6 +179,30 @@ closest_point_mesh
}
}
+static void
+closest_point_sphere
+ (const float pos[3],
+ const float sphere_org[3],
+ const float sphere_radius,
+ const unsigned geom_id,
+ const unsigned inst_id,
+ struct closest_pt* pt)
+{
+ float vec[3];
+ float len;
+ CHK(pos && sphere_org && sphere_radius > 0 && pt);
+
+ f3_sub(vec, pos, sphere_org);
+ len = f3_normalize(vec, vec);
+
+ pt->dst = (float)fabs(len - sphere_radius);
+ f3_set(pt->normal, vec);
+ f3_add(pt->pos, sphere_org, f3_mulf(pt->pos, vec, sphere_radius));
+ pt->iprim = 0;
+ pt->igeom = geom_id;
+ pt->iinst = inst_id;
+}
+
/* Check that `hit' roughly lies on an edge. */
static int
hit_on_edge(const struct s3d_hit* hit)
@@ -212,6 +250,220 @@ hit_on_edge(const struct s3d_hit* hit)
return 0;
}
+static void
+check_closest_point
+ (const struct s3d_hit* hit,
+ const struct closest_pt* pt,
+ const int hit_triangle) /* Define if `hit' lies on a triangle */
+{
+ struct s3d_attrib attr;
+ float N[3];
+
+ CHK(hit && pt);
+ 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_triangle || !hit_on_edge(hit)) {
+ CHK(hit->prim.prim_id == pt->iprim);
+ }
+
+ if(hit->prim.prim_id == pt->iprim
+ && hit->prim.geom_id == pt->igeom
+ && hit->prim.inst_id == pt->iinst) {
+ /* Due to numerical inaccuracies and/or 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->pos, attr.value, POSITION_EPSILON));
+ CHK(f3_eq_eps(pt->normal, N, 1.e-4f));
+ }
+ CHK(eq_epsf(hit->distance, pt->dst, 1.e-3f));
+}
+
+/*******************************************************************************
+ * Cornell box and sphere test
+ ******************************************************************************/
+struct instance {
+ float translation[3];
+ unsigned id;
+};
+
+static void
+check_closest_point_cbox_sphere
+ (const float pos[3],
+ const float sphere_org[3],
+ const float sphere_radius,
+ const unsigned walls_id,
+ const unsigned sphere_id,
+ const struct instance* instances,
+ const size_t ninstances,
+ struct s3d_hit* hit)
+{
+ struct closest_pt pt_walls = CLOSEST_PT_NULL;
+ struct closest_pt pt_sphere = CLOSEST_PT_NULL;
+ const struct closest_pt* pt = NULL;
+ CHK(pos && hit);
+
+ if(!ninstances) {
+ closest_point_mesh(pos, cbox_walls, cbox_walls_ids, cbox_walls_ntris,
+ walls_id, S3D_INVALID_ID, &pt_walls);
+ closest_point_sphere(pos, sphere_org, sphere_radius, sphere_id,
+ S3D_INVALID_ID, &pt_sphere);
+ } else {
+ size_t iinst;
+
+ pt_walls.dst = FLT_MAX;
+ FOR_EACH(iinst, 0, ninstances) {
+ struct closest_pt pt_walls_tmp;
+ struct closest_pt pt_sphere_tmp;
+ float pos_instance_space[3];
+
+ /* Transform query position in instance space */
+ f3_sub(pos_instance_space, pos, instances[iinst].translation);
+
+ closest_point_mesh(pos_instance_space, cbox_walls, cbox_walls_ids,
+ cbox_walls_ntris, walls_id, instances[iinst].id, &pt_walls_tmp);
+ closest_point_sphere(pos_instance_space, sphere_org, sphere_radius,
+ sphere_id, instances[iinst].id, &pt_sphere_tmp);
+
+ if(pt_walls_tmp.dst < pt_walls.dst) {
+ pt_walls = pt_walls_tmp;
+ /* Transform query closest point in world space */
+ f3_add(pt_walls.pos, pt_walls.pos, instances[iinst].translation);
+ }
+ if(pt_sphere_tmp.dst < pt_sphere.dst) {
+ pt_sphere = pt_sphere_tmp;
+ /* Transform query closest point in world space */
+ f3_add(pt_sphere.pos, pt_sphere.pos, instances[iinst].translation);
+ }
+ }
+ }
+
+ if(pt_walls.dst< pt_sphere.dst) {
+ pt = &pt_walls;
+ } else {
+ pt = &pt_sphere;
+ }
+
+ check_closest_point(hit, pt, hit->prim.geom_id == walls_id);
+}
+
+static void
+test_cbox_sphere(struct s3d_device* dev)
+{
+ struct s3d_hit hit = S3D_HIT_NULL;
+ struct s3d_vertex_data vdata = S3D_VERTEX_DATA_NULL;
+ struct s3d_scene* scn = NULL;
+ struct s3d_shape* walls = NULL;
+ struct s3d_shape* sphere = NULL;
+ struct s3d_shape* inst0 = NULL;
+ struct s3d_shape* inst1 = NULL;
+ struct s3d_scene_view* scnview = NULL;
+ struct instance instances[2];
+ struct cbox_desc cbox_desc;
+ size_t i;
+ float low[3], upp[3], mid[3], sz[3];
+ float pos[3];
+ float sphere_org[3];
+ float sphere_radius;
+ unsigned walls_id, sphere_id;
+
+ CHK(s3d_scene_create(dev, &scn) == RES_OK);
+
+ /* Setup the cornell box walls */
+ vdata.usage = S3D_POSITION;
+ vdata.type = S3D_FLOAT3;
+ vdata.get = cbox_get_position;
+ cbox_desc.vertices = cbox_walls;
+ cbox_desc.indices = cbox_walls_ids;
+ CHK(s3d_shape_create_mesh(dev, &walls) == RES_OK);
+ CHK(s3d_shape_get_id(walls, &walls_id) == RES_OK);
+ CHK(s3d_scene_attach_shape(scn, walls) == RES_OK);
+ CHK(s3d_mesh_setup_indexed_vertices(walls, cbox_walls_ntris, cbox_get_ids,
+ cbox_walls_nverts, &vdata, 1, &cbox_desc) == RES_OK);
+
+ /* Compute the Cornell box AABB */
+ CHK(s3d_scene_view_create(scn, S3D_GET_PRIMITIVE, &scnview) == RES_OK);
+ CHK(s3d_scene_view_get_aabb(scnview, low, upp) == RES_OK);
+ CHK(s3d_scene_view_ref_put(scnview) == RES_OK);
+
+ /* Setup the sphere at the center of the cornell box */
+ f3_mulf(mid, f3_add(mid, low, upp), 0.5f);
+ f3_sub(sz, upp, low);
+ f3_set(sphere_org, mid);
+ sphere_radius = MMIN(MMIN(sz[0], sz[1]), sz[2]) * 0.125f; /* 1/8 of the box */
+ CHK(s3d_shape_create_sphere(dev, &sphere) == RES_OK);
+ CHK(s3d_shape_get_id(sphere, &sphere_id) == RES_OK);
+ CHK(s3d_scene_attach_shape(scn, sphere) == RES_OK);
+ CHK(s3d_sphere_setup(sphere, sphere_org, sphere_radius) == RES_OK);
+
+ CHK(s3d_scene_view_create(scn, S3D_TRACE, &scnview) == RES_OK);
+
+ /* Check point query on the scene */
+ FOR_EACH(i, 0, 10000) {
+ /* Randomly generate a point in a bounding box that is 2 times the size of
+ * the scene 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_sphere(pos, sphere_org, sphere_radius, walls_id,
+ sphere_id, NULL, 0, &hit);
+ }
+
+ CHK(s3d_scene_view_ref_put(scnview) == RES_OK);
+
+ /* Instantiate the cbox sphere scene */
+ CHK(s3d_scene_instantiate(scn, &inst0) == RES_OK);
+ CHK(s3d_scene_instantiate(scn, &inst1) == RES_OK);
+ CHK(s3d_shape_get_id(inst0, &instances[0].id) == RES_OK);
+ CHK(s3d_shape_get_id(inst1, &instances[1].id) == RES_OK);
+ f3_mulf(instances[0].translation, sz, 0.5f);
+ CHK(s3d_instance_translate
+ (inst0, S3D_WORLD_TRANSFORM, instances[0].translation) == RES_OK);
+ f3_mulf(instances[1].translation, sz,-0.5f);
+ CHK(s3d_instance_translate
+ (inst1, S3D_WORLD_TRANSFORM, instances[1].translation) == RES_OK);
+
+ /* Create a new scene with instantiated cbox sphere scenes */
+ CHK(s3d_scene_ref_put(scn) == RES_OK);
+ CHK(s3d_scene_create(dev, &scn) == RES_OK);
+ CHK(s3d_scene_attach_shape(scn, inst0) == RES_OK);
+ CHK(s3d_scene_attach_shape(scn, inst1) == RES_OK);
+
+ CHK(s3d_scene_view_create(scn, S3D_TRACE, &scnview) == RES_OK);
+ CHK(s3d_scene_view_get_aabb(scnview, low, upp) == RES_OK);
+ f3_mulf(mid, f3_add(mid, low, upp), 0.5f);
+
+ /* Check point query on instances */
+ FOR_EACH(i, 0, 10000) {
+ /* Randomly generate a point in a bounding box that is 2 times the size of
+ * the scene 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_sphere(pos, sphere_org, sphere_radius, walls_id,
+ sphere_id, instances, 2/*#instances*/, &hit);
+ }
+
+ /* Clean up */
+ CHK(s3d_shape_ref_put(inst0) == RES_OK);
+ CHK(s3d_shape_ref_put(inst1) == RES_OK);
+ CHK(s3d_shape_ref_put(walls) == RES_OK);
+ CHK(s3d_shape_ref_put(sphere) == RES_OK);
+ CHK(s3d_scene_view_ref_put(scnview) == RES_OK);
+ CHK(s3d_scene_ref_put(scn) == RES_OK);
+}
+
/*******************************************************************************
* Cornell box test
******************************************************************************/
@@ -273,16 +525,15 @@ check_closest_point_cbox
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;
+ geom_id[CBOX_WALLS], S3D_INVALID_ID, &pt[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]);
+ geom_id[CBOX_TALL_BLOCK], S3D_INVALID_ID, &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_id[CBOX_SHORT_BLOCK], S3D_INVALID_ID, &pt[CBOX_SHORT_BLOCK]);
}
geom = pt[CBOX_WALLS].dst < pt[CBOX_TALL_BLOCK].dst
? CBOX_WALLS : CBOX_TALL_BLOCK;
@@ -292,31 +543,7 @@ check_closest_point_cbox
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));
+ check_closest_point(hit, &pt[geom], 1);
}
}
@@ -340,6 +567,7 @@ test_cbox(struct s3d_device* dev)
unsigned geom_id[CBOX_GEOMS_COUNT__];
size_t i;
+ /* Create the Star-3D scene */
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);
@@ -358,16 +586,19 @@ test_cbox(struct s3d_device* dev)
vdata.type = S3D_FLOAT3;
vdata.get = cbox_get_position;
+ /* Setup the Cornell box walls */
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);
+ /* Setup the Cornell box tall block */
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);
+ /* Setup the Cornell box short block */
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,
@@ -398,7 +629,7 @@ test_cbox(struct s3d_device* dev)
geom_id[CBOX_TALL_BLOCK] = S3D_INVALID_ID;
geom_id[CBOX_SHORT_BLOCK] = S3D_INVALID_ID;
- /* Check point query filtering filtering */
+ /* Check point query filtering */
FOR_EACH(i, 0, 10000) {
/* Randomly generate a point in a bounding box that is 2 times the size of
* the triangle AABB */
@@ -414,6 +645,7 @@ test_cbox(struct s3d_device* dev)
check_closest_point_cbox(pos, geom_id, &hit);
}
+ /* Clean up */
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);
@@ -579,6 +811,7 @@ main(int argc, char** argv)
test_api(dev);
test_single_triangle(dev);
test_cbox(dev);
+ test_cbox_sphere(dev);
CHK(s3d_device_ref_put(dev) == RES_OK);