star-3d

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

commit 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:
Msrc/test_s3d_closest_point.c | 299++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
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);