star-gs

Literate program for a geometric sensitivity calculation
git clone git://git.meso-star.fr/star-gs.git
Log | Files | Refs | README | LICENSE

commit 6e99b8586a6dbc78ede4f64a901ac012a28bad0e
parent bd205874d685ee877768ab632590db114e9b38c5
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Mon, 26 Apr 2021 18:50:11 +0200

Attach a ray-tracing filtering function to the geometry

Discard self hit and handle the inacurracy on the ray range

Diffstat:
Msrc/sgs_geometry.c | 90++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 89 insertions(+), 1 deletion(-)

diff --git a/src/sgs_geometry.c b/src/sgs_geometry.c @@ -25,6 +25,16 @@ #include <star/s3d.h> #include <rsys/cstr.h> +#include <rsys/float2.h> +#include <rsys/float3.h> + +struct hit_filter_context { + float range[2]; + struct s3d_hit hit_from; +}; +#define HIT_FILTER_CONTEXT_NULL__ {{0,FLT_MAX}, S3D_HIT_NULL__} +static const struct hit_filter_context HIT_FILTER_CONTEXT_NULL = + HIT_FILTER_CONTEXT_NULL__; struct mesh { const double* vertices; @@ -37,6 +47,82 @@ static const struct mesh MESH_NULL = {NULL, NULL, 0, 0}; /******************************************************************************* * Helper functions ******************************************************************************/ +/* Check that `hit' roughly lies on an edge. For triangular primitives, a + * simple but approximative way is to test that its position have at least one + * barycentric coordinate roughly equal to 0 or 1. */ +static FINLINE int +hit_on_edge(const struct s3d_hit* hit) +{ + const float on_edge_eps = 1.e-4f; + float w; + ASSERT(hit && !S3D_HIT_NONE(hit)); + w = 1.f - hit->uv[0] - hit->uv[1]; + return eq_epsf(hit->uv[0], 0.f, on_edge_eps) + || eq_epsf(hit->uv[0], 1.f, on_edge_eps) + || eq_epsf(hit->uv[1], 0.f, on_edge_eps) + || eq_epsf(hit->uv[1], 1.f, on_edge_eps) + || eq_epsf(w, 0.f, on_edge_eps) + || eq_epsf(w, 1.f, on_edge_eps); +} + +/* Return 1 if the submitted hit is actually a self hit, i.e. if the ray starts + * the primitive from which it starts */ +static INLINE int +self_hit + (const struct s3d_hit* hit, + const float ray_org[3], + const float ray_dir[3], + const float ray_range[2], + const struct s3d_hit* hit_from) +{ + ASSERT(hit && hit_from); + (void)ray_org, (void)ray_dir, (void)ray_range; + + /* The hit primitive is the one from which the the ray starts. Discard these + * hits */ + if(S3D_PRIMITIVE_EQ(&hit->prim, &hit_from->prim)) + return 1; + + /* If the targeted point is near of the origin, check that it lies on an + * edge/vertex shared by the 2 primitives. In such situation, we assume that + * it is a self intersection and discard this hit */ + if(!S3D_HIT_NONE(hit_from) + && eq_epsf(hit->distance, 0, 1.e-1f) + && hit_on_edge(hit_from) + && hit_on_edge(hit)) { + return 1; + } + + /* No self hit */ + return 0; +} + +static int +hit_filter + (const struct s3d_hit* hit, + const float ray_org[3], + const float ray_dir[3], + void* ray_data, + void* filter_data) +{ + const struct hit_filter_context* ctx = ray_data; + (void)filter_data; + + if(!ctx) /* Nothing to do */ + return 0; + + /* Internally, Star-3D relies on Embree that, due to numerically inaccuracy, + * can find hits whose distances are not strictly included in the submitted + * ray range. Discard these hits. */ + if(hit->distance <= ctx->range[0] || hit->distance >= ctx->range[1]) + return 1; + + if(self_hit(hit, ray_org, ray_dir, ctx->range, &ctx->hit_from)) + return 1; /* Discard this hit */ + + return 0; /* No filtering */ +} + static void mesh_get_vertex(const unsigned ivert, float pos[3], void* ctx) { @@ -81,6 +167,8 @@ mesh_create_view res = s3d_mesh_setup_indexed_vertices(shape, (unsigned)mesh->ntriangles, mesh_get_triangle, (unsigned)mesh->nvertices, &vdata, 1, mesh); if(res != RES_OK) goto error; + res = s3d_mesh_set_hit_filter_function(shape, hit_filter, NULL); + if(res != RES_OK) goto error; res = s3d_scene_create(sgs_get_s3d_device(sgs), &scene); if(res != RES_OK) goto error; res = s3d_scene_attach_shape(scene, shape); @@ -224,7 +312,7 @@ geometry_create res_T res = RES_OK; ASSERT(sgs && (unsigned)type < SGS_GEOMETRY_TYPES_COUNT__ && out_geom); ASSERT(sampling_mask != 0); - + allocator = sgs_get_allocator(sgs); geom = MEM_CALLOC(allocator, 1, sizeof(struct sgs_geometry)); if(!geom) {