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:
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)