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