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 18b104deef0d188c79cd6a8347fe02a85d9c94a1
parent fb5162a8e4bfaeb01ca196d32348a20c4a5bd326
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Mon, 22 Jan 2018 15:10:40 +0100

Add and test the sphere hit filter function

Diffstat:
Mcmake/CMakeLists.txt | 5++++-
Msrc/s3d.h | 15+++++++++++++++
Msrc/s3d_c.h | 6++++++
Msrc/s3d_geometry.c | 73++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/s3d_mesh.h | 8+-------
Msrc/s3d_scene_view.c | 46++++++++++++++++++++++++++++++----------------
Msrc/s3d_scene_view_c.h | 5+++++
Msrc/s3d_shape.c | 20++++++++++++++++++++
Msrc/s3d_sphere.h | 3++-
Msrc/test_s3d_sphere_instance.c | 51+++++++++++++++++++++++++++++++++++++++++----------
Msrc/test_s3d_trace_ray_sphere.c | 17++++++++++++-----
11 files changed, 178 insertions(+), 71 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -150,11 +150,14 @@ if(NOT NO_TEST) new_test(test_s3d_shape) new_test(test_s3d_sphere) new_test(test_s3d_sphere_box) - new_test(test_s3d_sphere_instance) new_test(test_s3d_primitive) new_test(test_s3d_trace_ray_instance) new_test(test_s3d_trace_ray_sphere) + build_test(test_s3d_sphere_instance) + register_test(test_s3d_sphere_instance_legacy test_s3d_sphere_instance) + register_test(test_s3d_sphere_instance_filter test_s3d_sphere_instance filter) + 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) diff --git a/src/s3d.h b/src/s3d.h @@ -462,6 +462,21 @@ s3d_sphere_setup const float position[3], const float radius); +/* Define an 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_sphere_set_hit_filter_function + (struct s3d_shape* shape, + s3d_hit_filter_function_T func, + void* filter_data); + +S3D_API res_T +s3d_sphere_get_hit_filter_data + (struct s3d_shape* shape, + void** data); + /******************************************************************************* * Mesh API - Manage a triangular meshes ******************************************************************************/ diff --git a/src/s3d_c.h b/src/s3d_c.h @@ -38,6 +38,12 @@ #include <rsys/rsys.h> +/* Filter function and its associated user defined data */ +struct hit_filter { + s3d_hit_filter_function_T func; + void* data; +}; + static FINLINE res_T rtc_error_to_res_T(const enum RTCError err) { diff --git a/src/s3d_geometry.c b/src/s3d_geometry.c @@ -34,6 +34,7 @@ #include "s3d_geometry.h" #include "s3d_instance.h" #include "s3d_mesh.h" +#include "s3d_scene_view_c.h" #include "s3d_sphere.h" #include <rsys/mem_allocator.h> @@ -41,6 +42,44 @@ /******************************************************************************* * Helper functions ******************************************************************************/ +static FINLINE void +sphere_ray_setup + (RTCRay& ray, + const float tfar, + struct geometry* geom) +{ + const RTCRay r = ray; + float cos_theta; + ASSERT(tfar >= 0 && geom); + + ray.tfar = tfar; + ray.geomID = geom->irtc; + ray.primID = 0; + f3_mulf(ray.Ng, ray.dir, tfar); + f3_add(ray.Ng, ray.Ng, ray.org); + f3_sub(ray.Ng, ray.Ng, geom->data.sphere->pos); + + /* Compute the parametric coordinate */ + f3_normalize(ray.Ng, ray.Ng); + cos_theta = ray.Ng[2]; + ray.v = (1.f - cos_theta) * 0.5f; + if(absf(cos_theta) == 1) { + ray.u = 0; + } else if(eq_epsf(ray.Ng[0], 0.f, 1.e-6f)) { + ray.u = ray.Ng[1] > 0 ? 0.25f : 0.75f; + } else { + double phi = atan2f(ray.Ng[1], ray.Ng[0]); /* phi in [-PI, PI] */ + if(phi < 0) phi = 2*PI + phi; /* phi in [0, 2PI] */ + ray.u = (float)(phi / (2*PI)); + } + + /* Filter the intersection if required */ + if(geom->data.sphere->filter.func) { + rtc_hit_filter_wrapper(&geom->data.sphere->filter, ray); + if(ray.geomID == RTC_INVALID_GEOMETRY_ID) ray = r; /* Restore the ray */ + } +} + static void geometry_release(ref_T* ref) { @@ -138,11 +177,11 @@ void geometry_rtc_sphere_intersect(void* data, RTCRay& ray, size_t item) { float v[3]; - float cos_theta; float A, B, C, D, Q, rcpA, t0, t1; struct geometry* geom = (struct geometry*)data; struct sphere sphere; ASSERT(geom && item == 0 && geom->type == GEOM_SPHERE); + (void)item; sphere = *geom->data.sphere; f3_sub(v, ray.org, sphere.pos); @@ -157,36 +196,8 @@ geometry_rtc_sphere_intersect(void* data, RTCRay& ray, size_t item) t0 = 0.5f * rcpA * (-B - Q); t1 = 0.5f * rcpA * (-B + Q); - if(ray.tnear < t0 && t0 < ray.tfar) { - ray.tfar = t0; - ray.geomID = geom->irtc; - ray.primID = (unsigned)item; - f3_mulf(ray.Ng, ray.dir, t0); - f3_add(ray.Ng, ray.Ng, ray.org); - f3_sub(ray.Ng, ray.Ng, sphere.pos); - } - if(ray.tnear < t1 && t1 < ray.tfar) { - ray.tfar = t1; - ray.geomID = geom->irtc; - ray.primID = (unsigned) item; - f3_mulf(ray.Ng, ray.dir, t1); - f3_add(ray.Ng, ray.Ng, ray.org); - f3_sub(ray.Ng, ray.Ng, sphere.pos); - } - - /* Compute the parametric coordinate */ - f3_normalize(ray.Ng, ray.Ng); - cos_theta = ray.Ng[2]; - ray.v = (1.f - cos_theta) * 0.5f; - if(absf(cos_theta) == 1) { - ray.u = 0; - } else if(eq_epsf(ray.Ng[0], 0.f, 1.e-6f)) { - ray.u = ray.Ng[1] > 0 ? 0.25f : 0.75f; - } else { - double phi = atan2f(ray.Ng[1], ray.Ng[0]); /* phi in [-PI, PI] */ - if(phi < 0) phi = 2*PI + phi; /* phi in [0, 2PI] */ - ray.u = (float)(phi / (2*PI)); - } + if(ray.tnear < t0 && t0 < ray.tfar) sphere_ray_setup(ray, t0, geom); + if(ray.tnear < t1 && t1 < ray.tfar) sphere_ray_setup(ray, t1, geom); } void diff --git a/src/s3d_mesh.h b/src/s3d_mesh.h @@ -33,7 +33,7 @@ #ifndef S3D_MESH_H #define S3D_MESH_H -#include "s3d.h" +#include "s3d_c.h" #include "s3d_buffer.h" #include "s3d_geometry.h" @@ -52,12 +52,6 @@ #define BUFFER_DARRAY darray_float #include "s3d_buffer.h" -/* Filter function and its associated user defined data */ -struct hit_filter { - s3d_hit_filter_function_T func; - void* data; -}; - struct mesh { /* Triangular mesh */ struct index_buffer* indices; struct vertex_buffer* attribs[S3D_ATTRIBS_COUNT__]; diff --git a/src/s3d_scene_view.c b/src/s3d_scene_view.c @@ -210,21 +210,6 @@ hit_setup(struct s3d_scene_view* scnview, const RTCRay* ray, struct s3d_hit* hit 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* ray_ex = static_cast<struct ray_extended*>(&ray); - - hit_setup(ray_ex->scnview, &ray, &hit); - if(filter->func(&hit, ray_ex->ws_org, ray_ex->ws_dir, ray_ex->data, filter->data)) { - /* Discard the intersection */ - ray.geomID = RTC_INVALID_GEOMETRY_ID; - } -} - static res_T embree_geometry_register (struct s3d_scene_view* scnview, @@ -326,7 +311,8 @@ embree_geometry_setup_filter_function if(!geom->data.mesh->filter.func) { rtcSetIntersectionFilterFunction(scnview->rtc_scn, geom->irtc, NULL); } else { - rtcSetIntersectionFilterFunction(scnview->rtc_scn, geom->irtc, filter_wrapper); + rtcSetIntersectionFilterFunction + (scnview->rtc_scn, geom->irtc, rtc_hit_filter_wrapper); rtcSetUserData(scnview->rtc_scn, geom->irtc, &geom->data.mesh->filter); } } @@ -570,6 +556,19 @@ scene_view_register_sphere geom->embree_outdated_mask |= EMBREE_USER_GEOMETRY; } + /* Update the filter function. Actually, filter functions are supported for + * built-in geometries only. For user defined geometries one has to + * explicitly call the filter function in the user defined intersection + * function. */ + if(geom->data.sphere->filter.func != shape->data.sphere->filter.func + || geom->data.sphere->filter.data != shape->data.sphere->filter.data) { + geom->data.sphere->filter = shape->data.sphere->filter; + /* The user defined geometries do not support filter function => the + * EMBREE_FILTER_FUNCTION flag is thus invalid for them. Enable the + * EMBREE_USER_GEOMETRY flag instead to notify its update. */ + geom->embree_outdated_mask |= EMBREE_USER_GEOMETRY; + } + if(geom->is_enabled != shape->is_enabled) { geom->is_enabled = shape->is_enabled; geom->embree_outdated_mask |= EMBREE_ENABLE; @@ -1575,3 +1574,18 @@ scene_view_destroy(struct s3d_scene_view* scnview) MEM_RM(scnview->scn->dev->allocator, scnview); } +/* Wrapper between an Embree and a Star-3D filter function */ +void +rtc_hit_filter_wrapper(void* user_ptr, RTCRay& ray) +{ + struct s3d_hit hit; + struct hit_filter* filter = (struct hit_filter*)user_ptr; + struct ray_extended* ray_ex = static_cast<struct ray_extended*>(&ray); + + hit_setup(ray_ex->scnview, &ray, &hit); + if(filter->func(&hit, ray_ex->ws_org, ray_ex->ws_dir, ray_ex->data, filter->data)) { + /* Discard the intersection */ + ray.geomID = RTC_INVALID_GEOMETRY_ID; + } +} + diff --git a/src/s3d_scene_view_c.h b/src/s3d_scene_view_c.h @@ -114,6 +114,11 @@ struct s3d_scene_view { }; extern LOCAL_SYM void +rtc_hit_filter_wrapper + (void* user_ptr, /* struct hit_filter* */ + RTCRay& ray); + +extern LOCAL_SYM void scene_view_destroy (struct s3d_scene_view* scnview); diff --git a/src/s3d_shape.c b/src/s3d_shape.c @@ -309,6 +309,26 @@ s3d_sphere_setup } res_T +s3d_sphere_set_hit_filter_function + (struct s3d_shape* shape, + s3d_hit_filter_function_T func, + void* data) +{ + if(!shape || shape->type != GEOM_SPHERE) return RES_BAD_ARG; + shape->data.sphere->filter.func = func; + shape->data.sphere->filter.data = data; + return RES_OK; +} + +res_T +s3d_sphere_get_hit_filter_data(struct s3d_shape* shape, void** data) +{ + if(!shape || !data || shape->type != GEOM_SPHERE) return RES_BAD_ARG; + *data = shape->data.sphere->filter.data; + return RES_OK; +} + +res_T s3d_mesh_setup_indexed_vertices (struct s3d_shape* shape, const unsigned ntris, diff --git a/src/s3d_sphere.h b/src/s3d_sphere.h @@ -33,7 +33,7 @@ #ifndef S3D_SPHERE_H #define S3D_SPHERE_H -#include "s3d.h" +#include "s3d_c.h" #include <rsys/ref_count.h> #include <rsys/float3.h> @@ -42,6 +42,7 @@ struct sphere { float pos[3]; float radius; struct s3d_device* dev; + struct hit_filter filter; ref_T ref; }; diff --git a/src/test_s3d_sphere_instance.c b/src/test_s3d_sphere_instance.c @@ -38,6 +38,25 @@ #include <rsys/float2.h> #include <rsys/float3.h> +#include <string.h> + +static int +filter_front_face + (const struct s3d_hit* hit, + const float pos[3], + const float dir[3], + void* ray_data, + void* filter_data) +{ + CHK(hit != NULL); + CHK(pos != NULL); + CHK(dir != NULL); + CHK(filter_data == NULL); + CHK(ray_data == NULL); + CHK(S3D_HIT_NONE(hit) == 0); + return f3_dot(hit->normal, dir) < 0; +} + static void test_sampling (struct s3d_scene_view* view, @@ -126,12 +145,10 @@ test_ray_tracing(struct s3d_scene_view* view) ((uint8_t*)img.pixels)[ipix+2] = 0; } else { float normal[3] = {0.f, 0.f, 0.f}; - float dot; f3_normalize(normal, hit.normal); - dot = absf(f3_dot(normal, dir)); - ((uint8_t*)img.pixels)[ipix+0] = (uint8_t)(dot*255.f); - ((uint8_t*)img.pixels)[ipix+1] = (uint8_t)(dot*255.f); - ((uint8_t*)img.pixels)[ipix+2] = (uint8_t)(dot*255.f); + ((uint8_t*)img.pixels)[ipix+0] = (uint8_t)(fabs(normal[0])*255.f); + ((uint8_t*)img.pixels)[ipix+1] = (uint8_t)(fabs(normal[1])*255.f); + ((uint8_t*)img.pixels)[ipix+2] = (uint8_t)(fabs(normal[2])*255.f); } } } @@ -146,6 +163,7 @@ main(int argc, char** argv) { struct mem_allocator allocator; struct s3d_device* dev; + struct s3d_shape* sphere; struct s3d_shape* sphere0; struct s3d_shape* sphere1; struct s3d_scene* scn; @@ -154,17 +172,21 @@ main(int argc, char** argv) unsigned inst0_id; unsigned inst1_id; float center[3]; + char filter = 0; (void)argc, (void)argv; + if(argc > 1 && !strcmp(argv[1], "filter")) { + filter = 1; + } + CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); CHK(s3d_device_create(NULL, &allocator, 0, &dev) == RES_OK); CHK(s3d_scene_create(dev, &scn) == RES_OK); - CHK(s3d_shape_create_sphere(dev, &sphere0) == RES_OK); - CHK(s3d_sphere_setup(sphere0, f3_splat(center, 0), 2) == RES_OK); - CHK(s3d_shape_get_id(sphere0, &geom_id) == RES_OK); - CHK(s3d_scene_attach_shape(scn, sphere0) == RES_OK); - CHK(s3d_shape_ref_put(sphere0) == RES_OK); + CHK(s3d_shape_create_sphere(dev, &sphere) == RES_OK); + CHK(s3d_sphere_setup(sphere, f3_splat(center, 0), 2) == RES_OK); + CHK(s3d_shape_get_id(sphere, &geom_id) == RES_OK); + CHK(s3d_scene_attach_shape(scn, sphere) == RES_OK); CHK(s3d_scene_instantiate(scn, &sphere0) == RES_OK); CHK(s3d_scene_instantiate(scn, &sphere1) == RES_OK); @@ -178,6 +200,13 @@ main(int argc, char** argv) CHK(s3d_instance_set_position(sphere0, f3(center,-1.5, 0, 0)) == RES_OK); CHK(s3d_instance_set_position(sphere1, f3(center, 1.5, 0, 0)) == RES_OK); + if(filter) { + CHK(s3d_sphere_set_hit_filter_function + (NULL, filter_front_face, NULL) == RES_BAD_ARG); + CHK(s3d_sphere_set_hit_filter_function + (sphere, filter_front_face, NULL) == RES_OK); + } + CHK(s3d_scene_view_create (scn, S3D_TRACE|S3D_GET_PRIMITIVE|S3D_SAMPLE, &view) == RES_OK); @@ -186,6 +215,7 @@ main(int argc, char** argv) CHK(s3d_device_ref_put(dev) == RES_OK); CHK(s3d_scene_ref_put(scn) == RES_OK); + CHK(s3d_shape_ref_put(sphere) == RES_OK); CHK(s3d_shape_ref_put(sphere0) == RES_OK); CHK(s3d_shape_ref_put(sphere1) == RES_OK); CHK(s3d_scene_view_ref_put(view) == RES_OK); @@ -195,3 +225,4 @@ main(int argc, char** argv) CHK(mem_allocated_size() == 0); return 0; } + diff --git a/src/test_s3d_trace_ray_sphere.c b/src/test_s3d_trace_ray_sphere.c @@ -43,6 +43,7 @@ main(int argc, char** argv) struct mem_allocator allocator; struct image img; struct camera cam; + struct s3d_hit hit; struct s3d_device* dev; struct s3d_scene* scn; struct s3d_shape* shape; @@ -53,6 +54,10 @@ main(int argc, char** argv) float pos[3] = {0, 0, 0}; float tgt[3] = {0, 0, 0}; float up[3] = {0, 1, 0}; + const float range[2] = {0, FLT_MAX}; + float org[3]; + float dir[3]; + float proj_ratio; size_t x, y; int hit_something = 0; @@ -70,6 +75,12 @@ main(int argc, char** argv) CHK(s3d_scene_attach_shape(scn, shape) == RES_OK); CHK(s3d_scene_view_create(scn, S3D_TRACE, &view) == RES_OK); + f3(org, 1.0, 1.0, -4); + f3(dir, 0.0, 0.0, 1); + CHK(s3d_scene_view_trace_ray(view, org, dir, range, NULL, &hit) == RES_OK); + CHK(!S3D_HIT_NONE(&hit)); + CHK(eq_epsf(hit.distance, 2, 1.e-6f)); + f3(pos, 0, 0, -10); f3(tgt, 0, 0, 0); f3(up, 0, 1, 0); @@ -81,11 +92,7 @@ main(int argc, char** argv) pixel[1] = (float)y / (float)img_sz[1]; FOR_EACH(x, 0, img_sz[0]) { const size_t ipix = (y*img_sz[0] + x)*3/*RGB*/; - struct s3d_hit hit; - const float range[2] = {0, FLT_MAX}; - float org[3]; - float dir[3]; - + pixel[0] = (float)x/(float)img_sz[0]; camera_ray(&cam, pixel, org, dir); CHK(s3d_scene_view_trace_ray(view, org, dir, range, NULL, &hit) == RES_OK);