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 f52d7d20e5b977124eed59e9a71b5e0887b9cef6
parent 051edceedef0ea76b70263dc9c65a57cd59693b7
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Tue, 17 May 2016 14:56:51 +0200

Implement and test the s3d_shape_set_hit_filter_function routine

This routine is used to setup a function that discards intersections
with respect to user defined criteria.

Diffstat:
Mcmake/CMakeLists.txt | 21++++++++++++++++-----
Msrc/s3d.h | 21+++++++++++++++++++++
Msrc/s3d_geometry.h | 8++++++++
Msrc/s3d_scene.c | 170++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/s3d_shape.c | 12++++++++++++
Msrc/s3d_shape_c.h | 5++++-
Msrc/test_s3d_shape.c | 16++++++++++++++++
Msrc/test_s3d_trace_ray.c | 37+++++++++++++++++++++++++++++++++++--
8 files changed, 215 insertions(+), 75 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -114,7 +114,7 @@ rcmake_setup_devel(s3d Star3D ${VERSION} star/s3d_version.h) # Add tests ################################################################################ if(NOT NO_TEST) - function(new_test _name) + function(build_test _name) add_executable(${_name} ${S3D_SOURCE_DIR}/${_name}.c ${S3D_SOURCE_DIR}/test_s3d_utils.h) @@ -122,17 +122,28 @@ if(NOT NO_TEST) set(_libraries ${ARGN}) foreach(_lib ${_libraries}) target_link_libraries(${_name} ${_lib}) - endforeach(_lib) - add_test(${_name} ${_name}) + endforeach() + endfunction() + + function(register_test _name) + add_test(${_name} ${ARGN}) rcmake_set_test_runtime_dirs(${_name} _runtime_dirs) - endfunction(new_test) + endfunction() + + function(new_test _name) + build_test(${_name} ${ARGN}) + register_test(${_name} ${_name}) + endfunction() new_test(test_s3d_device) new_test(test_s3d_primitive) new_test(test_s3d_sampler) new_test(test_s3d_scene) new_test(test_s3d_shape) - new_test(test_s3d_trace_ray) + + build_test(test_s3d_trace_ray) + register_test(test_s3d_trace_ray_legacy test_s3d_trace_ray) + register_test(test_s3d_trace_ray_filter test_s3d_trace_ray filter) endif(NOT NO_TEST) ################################################################################ diff --git a/src/s3d.h b/src/s3d.h @@ -168,6 +168,17 @@ enum s3d_session_flag { * intersects a shape or not */ #define S3D_HIT_NONE(Hit) ((Hit)->distance >= FLT_MAX) +/* Filter function data type. One can define such function to discard + * intersections along a ray with respect to user defined criteria, e.g.: + * masked/transparent primitive, etc. Return 0 or the intersection is not + * discarded and a value not equal to zero otherwise. */ +typedef int +(*s3d_hit_filter_function_T) + (const struct s3d_hit* hit, + const float ray_org[3], + const float ray_dir[3], + void* user_data); + /* Forward declaration of s3d opaque data types */ struct s3d_device; /* Entry point of the library */ struct s3d_scene; /* Collection of shapes */ @@ -400,6 +411,16 @@ S3D_API res_T s3d_shape_flip_surface (struct s3d_shape* shape); +/* Define a intersection filter function. The filter function is invoked at + * each intersection found during the s3d_scene_trace_ray(s) calls. If func + * does not return 0, then the intersection is ignored and the ray pursues its + * traversal. */ +S3D_API res_T +s3d_shape_set_hit_filter_function + (struct s3d_shape* shape, + s3d_hit_filter_function_T func, + void* filter_data); + /******************************************************************************* * Primitive API - Define a geometric primitive of a shape ******************************************************************************/ diff --git a/src/s3d_geometry.h b/src/s3d_geometry.h @@ -33,6 +33,7 @@ #ifndef S3D_GEOMETRY_H #define S3D_GEOMETRY_H +#include "s3d.h" #include "s3d_backend.h" #include <rsys/ref_count.h> @@ -43,6 +44,12 @@ enum geometry_type { GEOM_NONE = GEOM_TYPES_COUNT__ }; +/* Filter function and its associated user defined data */ +struct hit_filter { + s3d_hit_filter_function_T func; + void* data; +}; + /* Backend geometry */ struct geometry { unsigned name; /* Client side identifier */ @@ -50,6 +57,7 @@ struct geometry { unsigned scene_prim_id_offset; /* Offset from local to scene prim_id */ char flip_surface; /* Is the geometry surface flipped? */ char is_enabled; /* Is the geometry enabled? */ + struct hit_filter filter; enum geometry_type type; union { diff --git a/src/s3d_scene.c b/src/s3d_scene.c @@ -45,9 +45,102 @@ /* Flag used to define session of enabled on instantiated scene */ #define S3D_INSTANCE (BIT(sizeof(int)*8 - 1)) +struct ray_extended : public RTCRay { + struct s3d_scene* scene; +}; + /******************************************************************************* * Helper functions ******************************************************************************/ +static INLINE void +hit_setup(struct s3d_scene* scn, const RTCRay* ray, struct s3d_hit* hit) +{ + float w; + char flip_surface = 0; + + ASSERT(scn && hit && ray); + + if((unsigned)ray->geomID == RTC_INVALID_GEOMETRY_ID) { /* No hit */ + *hit = S3D_HIT_NULL; + return; + } + + f3_set(hit->normal, ray->Ng); + hit->distance = ray->tfar; + + hit->uv[0] = ray->u; + hit->uv[1] = ray->v; + w = 1.f - hit->uv[0] - hit->uv[1]; + ASSERT(w <= 1.f); /* This may not occurs */ + if(w < 0.f) { /* Handle precision error */ + if(hit->uv[0] > hit->uv[1]) hit->uv[0] += w; + else hit->uv[1] += w; + w = 0.f; + } + + /* Embree stores on the u and v ray parameters the barycentric coordinates of + * the hit with respect to the second and third triangle vertices, + * respectively. The following code computes the barycentric coordinates of + * the hit for the first and second triangle vertices */ + hit->uv[1] = hit->uv[0]; + hit->uv[0] = w; + + if((unsigned)ray->instID == RTC_INVALID_GEOMETRY_ID) { + struct geometry* geom_mesh; + ASSERT((unsigned)ray->geomID < darray_geom_size_get(&scn->embree2geoms)); + geom_mesh = darray_geom_data_get(&scn->embree2geoms)[ray->geomID]; + hit->prim.mesh__ = geom_mesh; + hit->prim.inst__ = NULL; + hit->prim.prim_id = ray->primID; + hit->prim.geom_id = geom_mesh->name; + hit->prim.inst_id = S3D_INVALID_ID; + hit->prim.scene_prim_id = /* Compute the "scene space" primitive id */ + hit->prim.prim_id /* Mesh space */ + + geom_mesh->scene_prim_id_offset; /* Scene space */ + + } else { /* The hit shape is instantiated */ + /* Retrieve the hit instance */ + struct geometry* geom_inst; + struct geometry* geom_mesh; + ASSERT((unsigned)ray->instID < darray_geom_size_get(&scn->embree2geoms)); + geom_inst = darray_geom_data_get(&scn->embree2geoms)[ray->instID]; + geom_mesh = scene_get_mesh(geom_inst->data.instance->scene, ray->geomID); + hit->prim.mesh__ = geom_mesh; + hit->prim.inst__ = geom_inst; + hit->prim.prim_id = ray->primID; + hit->prim.geom_id = geom_mesh->name; + hit->prim.inst_id = geom_inst->name; + hit->prim.scene_prim_id = /* Compute the "scene space" */ + hit->prim.prim_id /* Mesh space */ + + geom_mesh->scene_prim_id_offset /* Inst space */ + + geom_inst->scene_prim_id_offset; /* Scene space */ + + flip_surface = geom_inst->flip_surface; + ASSERT(hit->prim.inst__); + ASSERT(((struct geometry*)hit->prim.inst__)->type == GEOM_INSTANCE); + } + ASSERT(hit->prim.mesh__); + ASSERT(((struct geometry*)hit->prim.mesh__)->type == GEOM_MESH); + + /* Flip geometric normal with respect to the flip surface flag */ + flip_surface ^= ((struct geometry*)hit->prim.mesh__)->flip_surface; + if(flip_surface) f3_minus(hit->normal, hit->normal); +} + +/* Wrapper between an Embree and a Star-3D filter function */ +static void +filter_wrapper(void* user_ptr, RTCRay& ray) +{ + struct s3d_hit hit; + struct hit_filter* filter = (struct hit_filter*)user_ptr; + struct ray_extended* r = static_cast<struct ray_extended*>(&ray); + + hit_setup(r->scene, &ray, &hit); + if(filter->func(&hit, ray.org, ray.dir, filter->data)) { /* Discard the hit */ + ray.geomID = RTC_INVALID_GEOMETRY_ID; + } +} + static res_T scene_sync (struct s3d_scene* scn, @@ -213,6 +306,8 @@ scene_register_mesh /* Update the cached mesh states */ geom->flip_surface = shape->flip_surface; + geom->filter.func = shape->filter; + geom->filter.data = shape->filter_data; res = scene_register_embree_geometry(scn, geom); if(res != RES_OK) goto error; @@ -229,6 +324,10 @@ scene_register_mesh rtcUpdateBuffer(scn->rtc_scn, geom->irtc, RTC_INDEX_BUFFER); scn->is_rtc_scn_outdated = 1; } + if(geom->filter.func) { /* Update the filter func of the geometry */ + rtcSetIntersectionFilterFunction(scn->rtc_scn, geom->irtc, filter_wrapper); + rtcSetUserData(scn->rtc_scn, geom->irtc, &geom->filter); + } scene_geometry_flush_enable_state(scn, geom, shape); @@ -787,7 +886,7 @@ s3d_scene_trace_ray const float range[2], struct s3d_hit* hit) { - struct RTCRay ray; + struct ray_extended ray; if(!scn || !org || !dir || !range || !hit) return RES_BAD_ARG; if(!f3_is_normalized(dir)) @@ -808,75 +907,12 @@ s3d_scene_trace_ray ray.instID = RTC_INVALID_GEOMETRY_ID; ray.mask = 0xFFFFFFFF; ray.time = 0.f; + ray.scene = scn; rtcIntersect(scn->rtc_scn, ray); - if((unsigned)ray.geomID == RTC_INVALID_GEOMETRY_ID) { /* No hit */ - *hit = S3D_HIT_NULL; - } else { - float w; - char flip_surface = 0; - f3_set(hit->normal, ray.Ng); - hit->distance = ray.tfar; - - hit->uv[0] = ray.u; - hit->uv[1] = ray.v; - w = 1.f - hit->uv[0] - hit->uv[1]; - ASSERT(w <= 1.f); /* This may not occurs */ - if(w < 0.f) { /* Handle precision error */ - if(hit->uv[0] > hit->uv[1]) hit->uv[0] += w; - else hit->uv[1] += w; - w = 0.f; - } - /* Embree stores on the u and v ray parameters the barycentric coordinates - * of the hit with respect to the second and third triangle vertices, - * respectively. The following code computes the barycentric coordinates - * of the hit for the first and second triangle vertices */ - hit->uv[1] = hit->uv[0]; - hit->uv[0] = w; - - if((unsigned)ray.instID == RTC_INVALID_GEOMETRY_ID) { - struct geometry* geom_mesh; - ASSERT((unsigned)ray.geomID < darray_geom_size_get(&scn->embree2geoms)); - geom_mesh = darray_geom_data_get(&scn->embree2geoms)[ray.geomID]; - hit->prim.mesh__ = geom_mesh; - hit->prim.inst__ = NULL; - hit->prim.prim_id = ray.primID; - hit->prim.geom_id = geom_mesh->name; - hit->prim.inst_id = S3D_INVALID_ID; - hit->prim.scene_prim_id = /* Compute the "scene space" primitive id */ - hit->prim.prim_id /* Mesh space */ - + geom_mesh->scene_prim_id_offset; /* Scene space */ - - } else { /* The hit shape is instantiated */ - /* Retrieve the hit instance */ - struct geometry* geom_inst; - struct geometry* geom_mesh; - ASSERT((unsigned)ray.instID < darray_geom_size_get(&scn->embree2geoms)); - geom_inst = darray_geom_data_get(&scn->embree2geoms)[ray.instID]; - geom_mesh = scene_get_mesh(geom_inst->data.instance->scene, ray.geomID); - hit->prim.mesh__ = geom_mesh; - hit->prim.inst__ = geom_inst; - hit->prim.prim_id = ray.primID; - hit->prim.geom_id = geom_mesh->name; - hit->prim.inst_id = geom_inst->name; - hit->prim.scene_prim_id = /* Compute the "scene space" */ - hit->prim.prim_id /* Mesh space */ - + geom_mesh->scene_prim_id_offset /* Inst space */ - + geom_inst->scene_prim_id_offset; /* Scene space */ - - flip_surface = geom_inst->flip_surface; - ASSERT(hit->prim.inst__); - ASSERT(((struct geometry*)hit->prim.inst__)->type == GEOM_INSTANCE); - } - ASSERT(hit->prim.mesh__); - ASSERT(((struct geometry*)hit->prim.mesh__)->type == GEOM_MESH); - - /* Flip geometric normal with respect to the flip surface flag */ - flip_surface ^= ((struct geometry*)hit->prim.mesh__)->flip_surface; - if(flip_surface) f3_minus(hit->normal, hit->normal); - } - return RES_OK; + hit_setup(scn, &ray, hit); + return RES_OK; } res_T diff --git a/src/s3d_shape.c b/src/s3d_shape.c @@ -201,6 +201,18 @@ s3d_shape_flip_surface(struct s3d_shape* shape) } res_T +s3d_shape_set_hit_filter_function + (struct s3d_shape* shape, + s3d_hit_filter_function_T func, + void* data) +{ + if(!shape || !func) return RES_BAD_ARG; + shape->filter = func; + shape->filter_data = data; + return RES_OK; +} + +res_T s3d_instance_set_position (struct s3d_shape* shape, const float position[3]) { diff --git a/src/s3d_shape_c.h b/src/s3d_shape_c.h @@ -52,8 +52,11 @@ struct s3d_shape { char is_enabled; enum geometry_type type; + s3d_hit_filter_function_T filter; + void* filter_data; + union { - struct instance* instance; + struct instance* instance; struct mesh* mesh; } data; diff --git a/src/test_s3d_shape.c b/src/test_s3d_shape.c @@ -37,6 +37,17 @@ #include <rsys/float3.h> #include <rsys/math.h> +static int +filter_none + (const struct s3d_hit* hit, + const float org[3], + const float dir[3], + void* data) +{ + (void)hit, (void)org, (void)dir, (void)data; + return 0; +} + int main(int argc, char** argv) { @@ -288,6 +299,11 @@ main(int argc, char** argv) CHECK(s3d_shape_flip_surface(shape), RES_OK); CHECK(s3d_shape_flip_surface(shape), RES_OK); + CHECK(s3d_shape_set_hit_filter_function(NULL, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_shape_set_hit_filter_function(shape, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_shape_set_hit_filter_function(NULL, filter_none, NULL), RES_BAD_ARG); + CHECK(s3d_shape_set_hit_filter_function(shape, filter_none, NULL), RES_OK); + CHECK(s3d_scene_attach_shape(scn, shape), RES_OK); CHECK(s3d_scene_instantiate(scn, &inst), RES_OK); diff --git a/src/test_s3d_trace_ray.c b/src/test_s3d_trace_ray.c @@ -37,6 +37,8 @@ #include <rsys/image.h> #include <rsys/float3.h> +#include <string.h> + #define IMG_WIDTH 640 #define IMG_HEIGHT 480 @@ -81,6 +83,21 @@ camera_ray f3_set(org, cam->pos); } +static int +filter_func + (const struct s3d_hit* hit, + const float pos[3], + const float dir[3], + void* data) +{ + NCHECK(hit, NULL); + NCHECK(pos, NULL); + NCHECK(dir, NULL); + CHECK((uintptr_t)data, 0xDECAFBAD); + CHECK(S3D_HIT_NONE(hit), 0); + return hit->prim.prim_id % 2 == 0; +} + int main(int argc, char** argv) { @@ -111,10 +128,22 @@ main(int argc, char** argv) unsigned tall_block_id; unsigned short_block_id; size_t i; + char filter = 0; + char* img_name = NULL; mem_init_proxy_allocator(&allocator, &mem_default_allocator); if(argc > 1) { + if(!strcmp(argv[1], "filter")) { + filter = 1; + } else { + img_name = argv[1]; + } + } + if(!img_name && argc > 2) { + img_name = argv[2]; + } + if(img_name) { img = MEM_ALLOC(&allocator, 3 * IMG_WIDTH * IMG_HEIGHT); NCHECK(img, NULL); } @@ -253,6 +282,10 @@ main(int argc, char** argv) CHECK(s3d_mesh_copy(walls, walls_copy), RES_OK); CHECK(s3d_shape_ref_put(walls), RES_OK); CHECK(s3d_shape_get_id(walls_copy, &walls_id), RES_OK); + if(filter) { + CHECK(s3d_shape_set_hit_filter_function + (walls_copy, filter_func, (void*)0xDECAFBAD), RES_OK); + } CHECK(s3d_scene_clear(scn), RES_OK); CHECK(s3d_scene_attach_shape(scn, walls_copy), RES_OK); @@ -351,8 +384,8 @@ main(int argc, char** argv) } CHECK(s3d_scene_end_session(scn2), RES_OK); - if(argc > 1) { - CHECK(image_ppm_write(argv[1], IMG_WIDTH, IMG_HEIGHT, 3, img), RES_OK); + if(img_name) { + CHECK(image_ppm_write(img_name, IMG_WIDTH, IMG_HEIGHT, 3, img), RES_OK); } if(img)