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 783df142c3a5f0549ff313315cbfc6abc94b317b
parent f7e2a36b1a1e5816b0935b2db4efc90bff532f65
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Mon, 22 Jan 2018 15:16:35 +0100

Merge branch 'feature_sphere' into develop

Diffstat:
Mcmake/CMakeLists.txt | 18++++++++++++++----
Msrc/s3d.h | 45++++++++++++++++++++++++++++++++++++++-------
Msrc/s3d_c.h | 6++++++
Msrc/s3d_geometry.c | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/s3d_geometry.h | 23++++++++++++++++++++++-
Msrc/s3d_mesh.h | 14++++----------
Msrc/s3d_primitive.c | 264++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/s3d_scene_view.c | 244++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Msrc/s3d_scene_view_c.h | 5+++++
Msrc/s3d_shape.c | 79++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/s3d_shape_c.h | 2++
Asrc/s3d_sphere.c | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/s3d_sphere.h | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_s3d_primitive.c | 8--------
Asrc/test_s3d_sample_sphere.c | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_s3d_sampler.c | 8--------
Msrc/test_s3d_scene_view.c | 8--------
Asrc/test_s3d_sphere.c | 178+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_s3d_sphere_box.c | 147+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_s3d_sphere_instance.c | 228+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_s3d_trace_ray_sphere.c | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_s3d_utils.h | 8++++++++
22 files changed, 1695 insertions(+), 193 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -70,7 +70,8 @@ set(S3D_FILES_SRC s3d_primitive.c s3d_scene.c s3d_scene_view.c - s3d_shape.c) + s3d_shape.c + s3d_sphere.c) set(S3D_FILES_INC_API s3d.h) set(S3D_FILES_INC s3d.h @@ -82,7 +83,8 @@ set(S3D_FILES_INC s3d_mesh.h s3d_scene_c.h s3d_scene_view_c.h - s3d_shape_c.h) + s3d_shape_c.h + s3d_sphere.h) set(S3D_FILES_DOC COPYING.fr COPYING.en README.md) # Prepend each file in the `S3D_FILES_<SRC|INC>' list by `S3D_SOURCE_DIR' @@ -140,13 +142,21 @@ if(NOT NO_TEST) endfunction() new_test(test_s3d_device) - new_test(test_s3d_primitive) new_test(test_s3d_sampler) + new_test(test_s3d_sample_sphere) new_test(test_s3d_scene) new_test(test_s3d_scene_view) + new_test(test_s3d_seams) new_test(test_s3d_shape) + new_test(test_s3d_sphere) + new_test(test_s3d_sphere_box) + new_test(test_s3d_primitive) new_test(test_s3d_trace_ray_instance) - new_test(test_s3d_seams) + 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) diff --git a/src/s3d.h b/src/s3d.h @@ -101,7 +101,7 @@ struct s3d_primitive { unsigned inst_id; /* Instance identifier */ unsigned scene_prim_id; /* Identifier of the primitive in the scene */ /* Internal data. Should not be accessed */ - void* mesh__; + void* shape__; void* inst__; }; @@ -370,11 +370,6 @@ s3d_scene_view_get_aabb * Shape API - A shape defines a geometry that can be attached to a scene. ******************************************************************************/ S3D_API res_T -s3d_shape_create_mesh - (struct s3d_device* dev, - struct s3d_shape** shape); - -S3D_API res_T s3d_shape_ref_get (struct s3d_shape* shape); @@ -452,8 +447,44 @@ s3d_triangle_get_vertex_attrib struct s3d_attrib* attrib); /******************************************************************************* + * Sphere API - Manage a spherical shape. By default, the sphere normals point + * outward the sphere. One can use the s3d_shape_flip_surface function to + * revert them. + ******************************************************************************/ +S3D_API res_T +s3d_shape_create_sphere + (struct s3d_device* dev, + struct s3d_shape** sphere); + +S3D_API res_T +s3d_sphere_setup + (struct s3d_shape* shape, + 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 ******************************************************************************/ +S3D_API res_T +s3d_shape_create_mesh + (struct s3d_device* dev, + struct s3d_shape** shape); + /* Set/update the data of the indexed triangular meshes */ S3D_API res_T s3d_mesh_setup_indexed_vertices @@ -497,7 +528,7 @@ s3d_mesh_get_triangle_indices const unsigned itri, unsigned ids[3]); -/* Define a intersection filter function. The filter function is invoked at +/* 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. */ 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,12 +34,52 @@ #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> /******************************************************************************* * 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) { @@ -55,6 +95,9 @@ geometry_release(ref_T* ref) case GEOM_INSTANCE: if(geom->data.instance) instance_ref_put(geom->data.instance); break; + case GEOM_SPHERE: + if(geom->data.sphere) sphere_ref_put(geom->data.sphere); + break; default: FATAL("Unreachable code\n"); break; } MEM_RM(dev->allocator, geom); @@ -114,4 +157,73 @@ geometry_ref_put(struct geometry* geom) ref_put(&geom->ref, geometry_release); } +void +geometry_rtc_sphere_bounds(void* data, size_t item, RTCBounds& bounds) +{ + struct geometry* geom = (struct geometry*)data; + struct sphere sphere; + ASSERT(geom && item == 0 && geom->type == GEOM_SPHERE); + (void)item; + sphere = *geom->data.sphere; + bounds.lower_x = sphere.pos[0] - sphere.radius; + bounds.lower_y = sphere.pos[1] - sphere.radius; + bounds.lower_z = sphere.pos[2] - sphere.radius; + bounds.upper_x = sphere.pos[0] + sphere.radius; + bounds.upper_y = sphere.pos[1] + sphere.radius; + bounds.upper_z = sphere.pos[2] + sphere.radius; +} + +void +geometry_rtc_sphere_intersect(void* data, RTCRay& ray, size_t item) +{ + float v[3]; + 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); + A = f3_dot(ray.dir, ray.dir); + B = 2*f3_dot(v, ray.dir); + C = f3_dot(v, v) - sphere.radius*sphere.radius; + D = B*B - 4*A*C; + + if(D < 0.0f) return; + Q = sqrt(D); + rcpA = 1.f / A; + t0 = 0.5f * rcpA * (-B - Q); + t1 = 0.5f * rcpA * (-B + Q); + + 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 +geometry_rtc_sphere_occluded(void* data, RTCRay& ray, size_t item) +{ + float v[3]; + 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); + A = f3_dot(ray.dir, ray.dir); + B = 2*f3_dot(v, ray.dir); + C = f3_dot(v, v) - sphere.radius*sphere.radius; + D = B*B - 4*A*C; + + if(D < 0.0f) return; + Q = sqrt(D); + rcpA = 1.f / A; + t0 = 0.5f * rcpA * (-B - Q); + t1 = 0.5f * rcpA * (-B + Q); + + if(ray.tnear < t0 && t0 < ray.tfar) ray.geomID = 0; + if(ray.tnear < t1 && t1 < ray.tfar) ray.geomID = 0; +} diff --git a/src/s3d_geometry.h b/src/s3d_geometry.h @@ -40,6 +40,7 @@ enum geometry_type { GEOM_MESH, GEOM_INSTANCE, + GEOM_SPHERE, GEOM_TYPES_COUNT__, GEOM_NONE = GEOM_TYPES_COUNT__ }; @@ -49,7 +50,8 @@ enum embree_attrib { EMBREE_FILTER_FUNCTION = BIT(1), EMBREE_INDICES = BIT(2), EMBREE_TRANSFORM = BIT(4), - EMBREE_VERTICES = BIT(5) + EMBREE_VERTICES = BIT(5), + EMBREE_USER_GEOMETRY = BIT(6) }; /* Backend geometry */ @@ -67,6 +69,7 @@ struct geometry { union { struct instance* instance; struct mesh* mesh; + struct sphere* sphere; } data; struct s3d_device* dev; @@ -86,5 +89,23 @@ extern LOCAL_SYM void geometry_ref_put (struct geometry* geometry); +extern LOCAL_SYM void +geometry_rtc_sphere_bounds + (void* data, + size_t item, + RTCBounds& bounds); + +extern LOCAL_SYM void +geometry_rtc_sphere_intersect + (void* data, + RTCRay& ray, + size_t item); + +extern LOCAL_SYM void +geometry_rtc_sphere_occluded + (void* data, + RTCRay& ray, + size_t item); + #endif /* S3D_GEOMETRY_H */ diff --git a/src/s3d_mesh.h b/src/s3d_mesh.h @@ -30,7 +30,10 @@ * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ -#include "s3d.h" +#ifndef S3D_MESH_H +#define S3D_MESH_H + +#include "s3d_c.h" #include "s3d_buffer.h" #include "s3d_geometry.h" @@ -39,9 +42,6 @@ #include <rsys/dynamic_array_float.h> #include <rsys/ref_count.h> -#ifndef S3D_MESH_H -#define S3D_MESH_H - /* Generate the index buffer data type */ #define BUFFER_NAME index_buffer #define BUFFER_DARRAY darray_u32 @@ -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_primitive.c b/src/s3d_primitive.c @@ -31,78 +31,53 @@ * knowledge of the CeCILL license and that you accept its terms. */ #include "s3d_c.h" +#include "s3d_device_c.h" #include "s3d_instance.h" #include "s3d_mesh.h" #include "s3d_scene_c.h" +#include "s3d_sphere.h" #include <rsys/float33.h> /******************************************************************************* * Helper functions ******************************************************************************/ -static int -check_primitive(const struct s3d_primitive* prim) -{ - return prim - && prim->geom_id != S3D_INVALID_ID - && prim->prim_id != S3D_INVALID_ID - && prim->mesh__ != NULL - && (prim->inst_id != S3D_INVALID_ID || prim->inst__ == NULL); -} - -/******************************************************************************* - * Exported functions - ******************************************************************************/ -res_T -s3d_primitive_get_attrib - (const struct s3d_primitive* prim, +static res_T +mesh_get_primitive_attrib + (const struct geometry* geom, + const float* transform, /* Can be NULL => no transform */ + const char flip_surface, + const struct s3d_primitive* prim, const enum s3d_attrib_usage usage, const float uv[2], struct s3d_attrib* attrib) { const uint32_t* ids; - struct geometry* geom_mesh = NULL; - const float* transform = NULL; - char flip_surface = 0; float w; res_T res = RES_OK; - - if(!check_primitive(prim) || usage == S3D_ATTRIBS_COUNT__ || !uv || !attrib) - return RES_BAD_ARG; + ASSERT(geom && geom->type == GEOM_MESH && prim && prim->shape__ == geom); + ASSERT(uv && attrib); /* Unormalized barycentric coordinates */ w = CLAMP(1.f - uv[0] - uv[1], 0.f, 1.f); if(uv[0] < 0.f || uv[1] < 0.f || uv[0] > 1.f || uv[1] > 1.f - || !eq_epsf(w + uv[0] + uv[1], 1.f, 1.e-3f)) - return RES_BAD_ARG; - - if(prim->inst__ == NULL) { - geom_mesh = (struct geometry*)prim->mesh__; - flip_surface = geom_mesh->flip_surface; - } else { - const struct geometry* geom_inst = (const struct geometry*)prim->inst__; - ASSERT(geom_inst->type == GEOM_INSTANCE); - ASSERT(prim->inst_id == geom_inst->name); - geom_mesh = (struct geometry*)prim->mesh__; - transform = geom_inst->data.instance->transform; - ASSERT(geom_mesh); - flip_surface = geom_inst->flip_surface ^ geom_mesh->flip_surface; + || !eq_epsf(w + uv[0] + uv[1], 1.f, 1.e-3f)) { + res = RES_BAD_ARG; + goto error; } - ASSERT(prim->geom_id == geom_mesh->name); - ASSERT(geom_mesh->type == GEOM_MESH); /* The mesh haven't the required mesh attrib */ - if(usage != S3D_GEOMETRY_NORMAL && !geom_mesh->data.mesh->attribs[usage]) { + if(usage != S3D_GEOMETRY_NORMAL && !geom->data.mesh->attribs[usage]) { res = RES_BAD_ARG; goto error; } /* Out of bound primitive index */ - if(prim->prim_id >= mesh_get_ntris(geom_mesh->data.mesh)) { + if(prim->prim_id >= mesh_get_ntris(geom->data.mesh)) { res = RES_BAD_ARG; goto error; } - ids = mesh_get_ids(geom_mesh->data.mesh) + prim->prim_id * 3/*#triangle ids*/; + ids = mesh_get_ids(geom->data.mesh) + prim->prim_id * 3/*#triangle ids*/; attrib->usage = usage; if(usage == S3D_POSITION || usage == S3D_GEOMETRY_NORMAL) { @@ -110,7 +85,7 @@ s3d_primitive_get_attrib const float* pos; attrib->type = S3D_FLOAT3; /* Fetch data */ - pos = mesh_get_pos(geom_mesh->data.mesh); + pos = mesh_get_pos(geom->data.mesh); v0 = pos + ids[0] * 3; v1 = pos + ids[1] * 3; v2 = pos + ids[2] * 3; @@ -144,10 +119,10 @@ s3d_primitive_get_attrib const float* attr; const float* v0, *v1, *v2; unsigned i, dim; - attrib->type = geom_mesh->data.mesh->attribs_type[usage]; + attrib->type = geom->data.mesh->attribs_type[usage]; /* Fetch attrib data */ dim = s3d_type_get_dimension(attrib->type); - attr = mesh_get_attr(geom_mesh->data.mesh, usage); + attr = mesh_get_attr(geom->data.mesh, usage); v0 = attr + ids[0] * dim; v1 = attr + ids[1] * dim; v2 = attr + ids[2] * dim; @@ -163,6 +138,123 @@ error: goto exit; } +static res_T +sphere_get_attrib + (const struct geometry* geom, + const float* transform, /* Can be NULL => no transform */ + const char flip_surface, + const enum s3d_attrib_usage usage, + const float uv[2], + struct s3d_attrib* attrib) +{ + res_T res = RES_OK; + double phi, cos_theta, sin_theta; + float P[3]; + float N[3]; + ASSERT(geom && geom->type == GEOM_SPHERE); + ASSERT(uv && attrib); + + /* Only position and geometry normal are valid sphere attribs */ + if(usage != S3D_GEOMETRY_NORMAL && usage != S3D_POSITION) { + res = RES_BAD_ARG; + goto error; + } + + /* Compute the sampled position on the unit sphere that is actually equal to + * the normal at this position. */ + phi = uv[0] * 2*PI; + cos_theta = 1 - 2 * uv[1]; + sin_theta = 2 * sqrtf(uv[1] * (1 - uv[1])); + N[0] = (float)(cos(phi) * sin_theta); + N[1] = (float)(sin(phi) * sin_theta); + N[2] = (float)cos_theta; + + if(usage == S3D_GEOMETRY_NORMAL) { + if(flip_surface) f3_minus(N, N); + if(transform) { /* Transform the normal from local to world space */ + float invtrans[9]; + f33_invtrans(invtrans, transform); + f33_mulf3(attrib->value, invtrans, N); + } + f3_set(attrib->value, N); + } else { + ASSERT(usage == S3D_POSITION); + /* Compute the sampled position in local space */ + f3_mulf(P, N, geom->data.sphere->radius); + f3_add(P, P, geom->data.sphere->pos); + if(transform) { /* Transform the position from local to world space */ + f33_mulf3(P, transform, P); /* Affine */ + f3_add(P, P, transform + 9); /* Linear */ + } + f3_set(attrib->value, P); + } + +exit: + return res; +error: + goto exit; +} + +static int +check_primitive(const struct s3d_primitive* prim) +{ + return prim + && prim->geom_id != S3D_INVALID_ID + && prim->prim_id != S3D_INVALID_ID + && prim->shape__ != NULL + && (prim->inst_id != S3D_INVALID_ID || prim->inst__ == NULL); +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +s3d_primitive_get_attrib + (const struct s3d_primitive* prim, + const enum s3d_attrib_usage usage, + const float uv[2], + struct s3d_attrib* attrib) +{ + struct geometry* geom_shape = NULL; + const float* transform = NULL; + char flip_surface = 0; + res_T res = RES_OK; + + if(!check_primitive(prim) || usage == S3D_ATTRIBS_COUNT__ || !uv || !attrib) { + res = RES_BAD_ARG; + goto error; + } + + if(prim->inst__ == NULL) { + geom_shape = (struct geometry*)prim->shape__; + flip_surface = geom_shape->flip_surface; + } else { + const struct geometry* geom_inst = (const struct geometry*)prim->inst__; + ASSERT(geom_inst->type == GEOM_INSTANCE); + ASSERT(prim->inst_id == geom_inst->name); + geom_shape = (struct geometry*)prim->shape__; + transform = geom_inst->data.instance->transform; + ASSERT(geom_shape); + flip_surface = geom_inst->flip_surface ^ geom_shape->flip_surface; + } + ASSERT(prim->geom_id == geom_shape->name); + + if(geom_shape->type == GEOM_SPHERE) { + res = sphere_get_attrib + (geom_shape, transform, flip_surface, usage, uv, attrib); + } else { + ASSERT(geom_shape->type == GEOM_MESH); + res = mesh_get_primitive_attrib + (geom_shape, transform, flip_surface, prim, usage, uv, attrib); + } + if(res != RES_OK) goto error; + +exit: + return res; +error: + goto exit; +} + res_T s3d_primitive_has_attrib (const struct s3d_primitive* prim, @@ -176,9 +268,12 @@ s3d_primitive_has_attrib if(attr == S3D_GEOMETRY_NORMAL) { *has_attrib = 1; } else { - struct geometry* geom_mesh = (struct geometry*)prim->mesh__; - ASSERT(geom_mesh->type == GEOM_MESH); - *has_attrib = geom_mesh->data.mesh->attribs[attr] != NULL; + struct geometry* geom_shape = (struct geometry*)prim->shape__; + if(geom_shape->type == GEOM_MESH) { + *has_attrib = geom_shape->data.mesh->attribs[attr] != NULL; + } else { + *has_attrib = 0; + } } return RES_OK; } @@ -190,6 +285,7 @@ s3d_primitive_sample const float v, float st[2]) { + struct geometry* geom_shape; double sqrt_u; if(!check_primitive(prim) || !st) @@ -199,35 +295,51 @@ s3d_primitive_sample if(u < 0.f || u >= 1.f || v < 0.f || v >= 1.f) return RES_BAD_ARG; - /* Only triangular primitives are currently supported. So simply compute the - * barycentric coordinates of an uniform triangle sampling. */ - sqrt_u = sqrt(u); - st[0] = (float)(1.0 - sqrt_u); - st[1] = (float)(v * sqrt_u); + geom_shape = (struct geometry*)prim->shape__; + switch(geom_shape->type) { + case GEOM_MESH: + /* Triangular primitive */ + sqrt_u = sqrt(u); + st[0] = (float)(1.0 - sqrt_u); + st[1] = (float)(v * sqrt_u); + break; + case GEOM_SPHERE: + st[0] = u; + st[1] = v; + break; + default: FATAL("Unreachable code\n"); break; + } return RES_OK; } res_T s3d_primitive_compute_area(const struct s3d_primitive* prim, float* area) { - const uint32_t* ids; - const float* pos; - const float* v0, *v1, *v2; - float E0[3], E1[3], N[3]; struct geometry* geom; if(!check_primitive(prim) || !area) return RES_BAD_ARG; - geom = (struct geometry*)prim->mesh__; - pos = mesh_get_pos(geom->data.mesh); - ids = mesh_get_ids(geom->data.mesh) + prim->prim_id * 3/* #triangle ids */; - v0 = pos + ids[0] * 3/* #coords */; - v1 = pos + ids[1] * 3/* #coords */; - v2 = pos + ids[2] * 3/* #coords */; - f3_sub(E0, v1, v0); - f3_sub(E1, v2, v0); - *area = f3_len(f3_cross(N, E0, E1)) * 0.5f; + geom = (struct geometry*)prim->shape__; + if(geom->type == GEOM_SPHERE) { + *area = sphere_compute_area(geom->data.sphere); + } else if(geom->type == GEOM_MESH) { + const uint32_t* ids; + const float* pos; + const float* v0, *v1, *v2; + float E0[3], E1[3], N[3]; + + pos = mesh_get_pos(geom->data.mesh); + ids = mesh_get_ids(geom->data.mesh) + prim->prim_id * 3/* #triangle ids */; + v0 = pos + ids[0] * 3/* #coords */; + v1 = pos + ids[1] * 3/* #coords */; + v2 = pos + ids[2] * 3/* #coords */; + f3_sub(E0, v1, v0); + f3_sub(E1, v2, v0); + *area = f3_len(f3_cross(N, E0, E1)) * 0.5f; + } else { + FATAL("Unreachable code\n"); + } return RES_OK; } @@ -261,7 +373,7 @@ s3d_triangle_get_vertex_attrib const enum s3d_attrib_usage usage, struct s3d_attrib* attrib) { - struct geometry* geom_mesh = NULL; + struct geometry* geom_shape = NULL; const float* transform = NULL; const uint32_t* ids; @@ -271,9 +383,11 @@ s3d_triangle_get_vertex_attrib return RES_BAD_ARG; } - geom_mesh = (struct geometry*)prim->mesh__; - ASSERT(prim->geom_id == geom_mesh->name); - ASSERT(geom_mesh->type == GEOM_MESH); + geom_shape = (struct geometry*)prim->shape__; + ASSERT(prim->geom_id == geom_shape->name); + + if(geom_shape->type != GEOM_MESH) + return RES_BAD_ARG; if(prim->inst__ != NULL) { const struct geometry* geom_inst = (const struct geometry*)prim->inst__; @@ -283,30 +397,30 @@ s3d_triangle_get_vertex_attrib } /* The mesh haven't the required mesh attrib */ - if(!geom_mesh->data.mesh->attribs[usage]) { + if(!geom_shape->data.mesh->attribs[usage]) { return RES_BAD_ARG; } /* Out of bound primitive index */ - if(prim->prim_id >= mesh_get_ntris(geom_mesh->data.mesh)) { + if(prim->prim_id >= mesh_get_ntris(geom_shape->data.mesh)) { return RES_BAD_ARG; } - ids = mesh_get_ids(geom_mesh->data.mesh) + prim->prim_id * 3/*#triangle ids*/; + ids = mesh_get_ids(geom_shape->data.mesh) + prim->prim_id * 3/*#triangle ids*/; attrib->usage = usage; if(usage != S3D_POSITION) { const float* attr; unsigned i, dim; - attrib->type = geom_mesh->data.mesh->attribs_type[usage]; + attrib->type = geom_shape->data.mesh->attribs_type[usage]; /* Fetch attrib data */ dim = s3d_type_get_dimension(attrib->type); - attr = mesh_get_attr(geom_mesh->data.mesh, usage) + ids[ivertex] * dim; + attr = mesh_get_attr(geom_shape->data.mesh, usage) + ids[ivertex] * dim; FOR_EACH(i, 0, dim) attrib->value[i] = attr[i]; } else { const float* pos; attrib->type = S3D_FLOAT3; /* Fetch data */ - pos = mesh_get_pos(geom_mesh->data.mesh) + ids[ivertex] * 3; + pos = mesh_get_pos(geom_shape->data.mesh) + ids[ivertex] * 3; f3_set(attrib->value, pos); if(transform) { /* Transform the position from local to world space */ f33_mulf3(attrib->value, transform, attrib->value); /* Rotation */ diff --git a/src/s3d_scene_view.c b/src/s3d_scene_view.c @@ -140,53 +140,36 @@ hit_setup(struct s3d_scene_view* scnview, const RTCRay* ray, struct s3d_hit* hit 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; + struct geometry* geom_shape; ASSERT((unsigned)ray->geomID < darray_geom_size_get(&scnview->embree2geoms)); - geom_mesh = scene_view_geometry_from_embree_id(scnview, ray->geomID); - hit->prim.mesh__ = geom_mesh; + geom_shape = scene_view_geometry_from_embree_id(scnview, ray->geomID); + hit->prim.shape__ = geom_shape; hit->prim.inst__ = NULL; hit->prim.prim_id = ray->primID; - hit->prim.geom_id = geom_mesh->name; + hit->prim.geom_id = geom_shape->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 */ + + geom_shape->scene_prim_id_offset; /* Scene space */ } else { /* The hit shape is instantiated */ /* Retrieve the hit instance */ struct geometry* geom_inst; - struct geometry* geom_mesh; + struct geometry* geom_shape; float transform[9]; ASSERT((unsigned)ray->instID < darray_geom_size_get(&scnview->embree2geoms)); geom_inst = scene_view_geometry_from_embree_id(scnview, ray->instID); - geom_mesh = scene_view_geometry_from_embree_id + geom_shape = scene_view_geometry_from_embree_id (geom_inst->data.instance->scnview, ray->geomID); - hit->prim.mesh__ = geom_mesh; + hit->prim.shape__ = geom_shape; hit->prim.inst__ = geom_inst; hit->prim.prim_id = ray->primID; - hit->prim.geom_id = geom_mesh->name; + hit->prim.geom_id = geom_shape->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 */ + hit->prim.prim_id /* Shape space */ + + geom_shape->scene_prim_id_offset /* Inst space */ + geom_inst->scene_prim_id_offset; /* Scene space */ flip_surface = geom_inst->flip_surface; @@ -197,27 +180,34 @@ hit_setup(struct s3d_scene_view* scnview, const RTCRay* ray, struct s3d_hit* hit f33_invtrans(transform, geom_inst->data.instance->transform); f33_mulf3(hit->normal, transform, hit->normal); } - ASSERT(hit->prim.mesh__); - ASSERT(((struct geometry*)hit->prim.mesh__)->type == GEOM_MESH); + ASSERT(hit->prim.shape__); + ASSERT(((struct geometry*)hit->prim.shape__)->type == GEOM_MESH + ||((struct geometry*)hit->prim.shape__)->type == GEOM_SPHERE); - /* 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* ray_ex = static_cast<struct ray_extended*>(&ray); + hit->uv[0] = ray->u; + hit->uv[1] = ray->v; - 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; + if(((struct geometry*)hit->prim.shape__)->type == GEOM_MESH) { + 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; } + + /* Flip geometric normal with respect to the flip surface flag */ + flip_surface ^= ((struct geometry*)hit->prim.shape__)->flip_surface; + if(flip_surface) f3_minus(hit->normal, hit->normal); } static res_T @@ -225,7 +215,7 @@ embree_geometry_register (struct s3d_scene_view* scnview, struct geometry* geom) { - ASSERT(scnview && geom && (geom->type==GEOM_MESH || geom->type==GEOM_INSTANCE)); + ASSERT(scnview && geom); /* Create the Embree geometry if it is not valid */ if(geom->irtc != RTC_INVALID_GEOMETRY_ID) { @@ -245,6 +235,17 @@ embree_geometry_register geom->irtc = rtcNewInstance2 (scnview->rtc_scn, geom->data.instance->scnview->rtc_scn); break; + case GEOM_SPHERE: + geom->irtc = rtcNewUserGeometry3 + (scnview->rtc_scn, RTC_GEOMETRY_DYNAMIC, 1); + rtcSetUserData(scnview->rtc_scn, geom->irtc, geom); + rtcSetBoundsFunction + (scnview->rtc_scn, geom->irtc, geometry_rtc_sphere_bounds); + rtcSetIntersectFunction + (scnview->rtc_scn, geom->irtc, geometry_rtc_sphere_intersect); + rtcSetOccludedFunction + (scnview->rtc_scn, geom->irtc, geometry_rtc_sphere_occluded); + break; default: FATAL("Unreachable code\n"); break; } if(geom->irtc == RTC_INVALID_GEOMETRY_ID) @@ -310,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); } } @@ -337,9 +339,7 @@ scene_view_setup_embree(struct s3d_scene_view* scnview) RTC_SCENE_DYNAMIC | RTC_SCENE_INCOHERENT | RTC_SCENE_ROBUST; - const RTCAlgorithmFlags rtc_algorithm_mask = - RTC_INTERSECT1 - | RTC_INTERSECT4; + const RTCAlgorithmFlags rtc_algorithm_mask = RTC_INTERSECT1; int rtc_outdated = 0; res_T res = RES_OK; ASSERT(scnview); @@ -383,6 +383,8 @@ scene_view_setup_embree(struct s3d_scene_view* scnview) embree_geometry_setup_filter_function(scnview, geom); if((geom->embree_outdated_mask & EMBREE_TRANSFORM) != 0) embree_geometry_setup_transform(scnview, geom); + if((geom->embree_outdated_mask & EMBREE_USER_GEOMETRY) != 0) + rtcUpdate(scnview->rtc_scn, geom->irtc); geom->embree_outdated_mask = 0; } @@ -515,6 +517,72 @@ error: } static res_T +scene_view_register_sphere + (struct s3d_scene_view* scnview, + struct s3d_shape* shape) +{ + struct geometry** pgeom = NULL; + struct geometry* geom = NULL; + unsigned shape_id; + res_T res = RES_OK; + ASSERT(scnview && shape && shape->type == GEOM_SPHERE); + + /* Retrieve the cached geometry */ + S3D(shape_get_id(shape, &shape_id)); + pgeom = htable_geom_find(&scnview->cached_geoms, &shape_id); + if(pgeom) { + geom = *pgeom; + } else { + res = geometry_create(scnview->scn->dev, &geom); + if(res != RES_OK) goto error; + res = sphere_create(scnview->scn->dev, &geom->data.sphere); + if(res != RES_OK) goto error; + geom->type = GEOM_SPHERE; + res = htable_geom_set(&scnview->cached_geoms, &shape_id, &geom); + if(res != RES_OK) goto error; + geom->name = shape->id.index; + geom->embree_outdated_mask |= EMBREE_USER_GEOMETRY; + } + + /* Setup the sphere radius */ + if(geom->data.sphere->radius != shape->data.sphere->radius) { + geom->data.sphere->radius = shape->data.sphere->radius; + geom->embree_outdated_mask |= EMBREE_USER_GEOMETRY; + } + + /* Setup the sphere position */ + if(!f3_eq(geom->data.sphere->pos, shape->data.sphere->pos)) { + f3_set(geom->data.sphere->pos, shape->data.sphere->pos); + 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; + } + + geom->flip_surface = shape->flip_surface; + +exit: + return res; +error: + goto exit; +} + +static res_T scene_view_register_instance (struct s3d_scene_view* scnview, struct s3d_shape* shape, @@ -620,6 +688,10 @@ scene_view_compute_cdf(struct s3d_scene_view* scnview) area += darray_float_cdata_get(&geom->data.mesh->cdf)[len - 1]; } break; + case GEOM_SPHERE: + len = 1; + area += sphere_compute_area(geom->data.sphere); + break; case GEOM_INSTANCE: /* The instance CDF was computed during its scnview synchronisation */ len = darray_fltui_size_get(&geom->data.instance->scnview->cdf); @@ -675,6 +747,10 @@ scene_view_compute_nprims_cdf len = mesh_get_ntris(geom->data.mesh); nprims += (unsigned)len; break; + case GEOM_SPHERE: + len = 1; + nprims += 1; + break; case GEOM_INSTANCE: /* The instance CDF was computed during its scnview synchronisation */ len = darray_nprims_cdf_size_get @@ -723,7 +799,12 @@ scene_view_compute_scene_aabb(struct s3d_scene_view* scnview) if(!geom->is_enabled) continue; switch(geom->type) { - case GEOM_MESH: mesh_compute_aabb(geom->data.mesh, lower, upper); break; + case GEOM_MESH: + mesh_compute_aabb(geom->data.mesh, lower, upper); + break; + case GEOM_SPHERE: + sphere_compute_aabb(geom->data.sphere, lower, upper); + break; case GEOM_INSTANCE: /* Note that the instance AABB was computed during its scnview * synchronisation. */ @@ -776,6 +857,11 @@ scene_view_compute_volume case GEOM_MESH: volume += mesh_compute_volume(geom->data.mesh, flip); break; + case GEOM_SPHERE: + volume += flip + ? -sphere_compute_volume(geom->data.sphere) + : sphere_compute_volume(geom->data.sphere); + break; case GEOM_INSTANCE: volume += scene_view_compute_volume(geom->data.instance->scnview, flip); break; @@ -819,6 +905,9 @@ scene_view_sync case GEOM_MESH: res = scene_view_register_mesh(scnview, shape); break; + case GEOM_SPHERE: + res = scene_view_register_sphere(scnview, shape); + break; default: FATAL("Unreachable code\n"); break; } if(res != RES_OK) goto error; @@ -1169,7 +1258,7 @@ s3d_scene_view_sample ASSERT(pgeom); geom = *pgeom; - if(geom->type == GEOM_MESH) { + if(geom->type == GEOM_MESH || geom->type == GEOM_SPHERE) { primitive->inst__ = NULL; primitive->inst_id = S3D_INVALID_ID; primitive->scene_prim_id = 0; @@ -1196,18 +1285,22 @@ s3d_scene_view_sample ASSERT(pgeom); geom = *pgeom; } - ASSERT(geom->type == GEOM_MESH); - /* Find the sampled triangle */ - primitive->mesh__ = geom; + /* Find the sampled primitive */ + primitive->shape__ = geom; primitive->geom_id = geom->name; primitive->scene_prim_id += geom->scene_prim_id_offset; - flt_begin = darray_float_cdata_get(&geom->data.mesh->cdf); - flt_end = flt_begin + darray_float_size_get(&geom->data.mesh->cdf); - flt_found = std::lower_bound(flt_begin, flt_end, f); - ASSERT(flt_found != flt_end); - - primitive->prim_id = (unsigned)(flt_found - flt_begin); + if(geom->type == GEOM_SPHERE) { + primitive->prim_id = 0; + } else if(geom->type == GEOM_MESH) { + flt_begin = darray_float_cdata_get(&geom->data.mesh->cdf); + flt_end = flt_begin + darray_float_size_get(&geom->data.mesh->cdf); + flt_found = std::lower_bound(flt_begin, flt_end, f); + ASSERT(flt_found != flt_end); + primitive->prim_id = (unsigned)(flt_found - flt_begin); + } else { + FATAL("Unreachable code\n"); + } primitive->scene_prim_id += primitive->prim_id; S3D(primitive_sample(primitive, v, w, st)); @@ -1269,7 +1362,7 @@ s3d_scene_view_get_primitive ASSERT(pgeom); geom = *pgeom; - if(geom->type == GEOM_MESH) { + if(geom->type == GEOM_MESH || geom->type == GEOM_SPHERE) { prim->inst__ = NULL; prim->inst_id = S3D_INVALID_ID; prim->scene_prim_id = 0; @@ -1298,9 +1391,9 @@ s3d_scene_view_get_primitive ASSERT(pgeom); geom = *pgeom; } - ASSERT(geom->type == GEOM_MESH); - ASSERT(i < mesh_get_ntris(geom->data.mesh)); - prim->mesh__ = geom; + ASSERT(geom->type == GEOM_MESH || geom->type == GEOM_SPHERE); + ASSERT(geom->type != GEOM_MESH || i < mesh_get_ntris(geom->data.mesh)); + prim->shape__ = geom; prim->geom_id = geom->name; prim->prim_id = (unsigned)i; prim->scene_prim_id += geom->scene_prim_id_offset; @@ -1347,6 +1440,9 @@ s3d_scene_view_primitives_count(struct s3d_scene_view* scnview, size_t* prims_co case GEOM_MESH: *prims_count += mesh_get_ntris(geom->data.mesh); break; + case GEOM_SPHERE: + *prims_count += 1; + break; case GEOM_INSTANCE: S3D(scene_view_primitives_count(geom->data.instance->scnview, &inst_count)); *prims_count += inst_count; @@ -1401,6 +1497,9 @@ s3d_scene_view_compute_area(struct s3d_scene_view* scnview, float* out_area) case GEOM_MESH: area += mesh_compute_area(geom->data.mesh); break; + case GEOM_SPHERE: + area += sphere_compute_area(geom->data.sphere); + break; case GEOM_INSTANCE: /* TODO take into account the instance scale factor */ S3D(scene_view_compute_area(geom->data.instance->scnview, &inst_area)); @@ -1475,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 @@ -60,6 +60,9 @@ shape_release(ref_T* ref) case GEOM_INSTANCE: if(shape->data.instance) instance_ref_put(shape->data.instance); break; + case GEOM_SPHERE: + if(shape->data.sphere) sphere_ref_put(shape->data.sphere); + break; default: FATAL("Unreachable code \n"); break; } } @@ -122,13 +125,42 @@ s3d_shape_create_mesh } res = shape_create(dev, &shape); - if(res != RES_OK) - goto error; + if(res != RES_OK) goto error; shape->type = GEOM_MESH; res = mesh_create(dev, &shape->data.mesh); - if(res != RES_OK) + if(res != RES_OK) goto error; + +exit: + if(out_shape) + *out_shape = shape; + return res; +error: + if(shape) { + S3D(shape_ref_put(shape)); + shape = NULL; + } + goto exit; +} + +res_T +s3d_shape_create_sphere + (struct s3d_device* dev, struct s3d_shape** out_shape) +{ + struct s3d_shape* shape = NULL; + res_T res = RES_OK; + + if(!dev || !out_shape) { + res = RES_BAD_ARG; goto error; + } + + res = shape_create(dev, &shape); + if(res != RES_OK) goto error; + + shape->type = GEOM_SPHERE; + res = sphere_create(dev, &shape->data.sphere); + if(res != RES_OK) goto error; exit: if(out_shape) @@ -251,15 +283,48 @@ s3d_instance_transform { if(!shape || shape->type != GEOM_INSTANCE || !transform) return RES_BAD_ARG; - if(space == S3D_LOCAL_TRANSFORM) + if(space == S3D_LOCAL_TRANSFORM) { f33_mulf33(shape->data.instance->transform, transform, shape->data.instance->transform); - else if(space == S3D_WORLD_TRANSFORM) + } else if(space == S3D_WORLD_TRANSFORM) { f33_mulf33(shape->data.instance->transform, shape->data.instance->transform, transform); - else + } else { return RES_BAD_ARG; - + } + return RES_OK; +} + +res_T +s3d_sphere_setup + (struct s3d_shape* shape, + const float position[3], + const float radius) +{ + if(!shape || !position || radius <= 0) + return RES_BAD_ARG; + f3_set(shape->data.sphere->pos, position); + shape->data.sphere->radius = radius; + return RES_OK; +} + +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; } diff --git a/src/s3d_shape_c.h b/src/s3d_shape_c.h @@ -35,6 +35,7 @@ #include "s3d_mesh.h" #include "s3d_instance.h" +#include "s3d_sphere.h" #include <rsys/dynamic_array_u32.h> #include <rsys/dynamic_array_float.h> @@ -54,6 +55,7 @@ struct s3d_shape { union { struct instance* instance; struct mesh* mesh; + struct sphere* sphere; } data; struct s3d_device* dev; diff --git a/src/s3d_sphere.c b/src/s3d_sphere.c @@ -0,0 +1,96 @@ +/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com) + * + * This software is a computer program whose purpose is to describe a + * virtual 3D environment that can be ray-traced and sampled both robustly + * and efficiently. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#include "s3d_device_c.h" +#include "s3d_sphere.h" + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static void +sphere_release(ref_T* ref) +{ + struct sphere* sphere; + struct s3d_device* dev; + ASSERT(ref); + + sphere = CONTAINER_OF(ref, struct sphere, ref); + dev = sphere->dev; + MEM_RM(dev->allocator, sphere); + S3D(device_ref_put(dev)); +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +sphere_create(struct s3d_device* dev, struct sphere** out_sphere) +{ + struct sphere* sphere = NULL; + res_T res = RES_OK; + ASSERT(dev && out_sphere); + + sphere = (struct sphere*)MEM_CALLOC(dev->allocator, 1, sizeof(struct sphere)); + if(!sphere) { + res = RES_MEM_ERR; + goto error; + } + ref_init(&sphere->ref); + S3D(device_ref_get(dev)); + sphere->dev = dev; + sphere->radius = -1; + +exit: + *out_sphere = sphere; + return res; +error: + if(sphere) { + sphere_ref_put(sphere); + sphere = NULL; + } + goto exit; +} + +void +sphere_ref_get(struct sphere* sphere) +{ + ASSERT(sphere); + ref_get(&sphere->ref); +} + +void +sphere_ref_put(struct sphere* sphere) +{ + ASSERT(sphere); + ref_put(&sphere->ref, sphere_release); +} + diff --git a/src/s3d_sphere.h b/src/s3d_sphere.h @@ -0,0 +1,103 @@ +/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com) + * + * This software is a computer program whose purpose is to describe a + * virtual 3D environment that can be ray-traced and sampled both robustly + * and efficiently. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#ifndef S3D_SPHERE_H +#define S3D_SPHERE_H + +#include "s3d_c.h" + +#include <rsys/ref_count.h> +#include <rsys/float3.h> + +struct sphere { + float pos[3]; + float radius; + struct s3d_device* dev; + struct hit_filter filter; + ref_T ref; +}; + +extern LOCAL_SYM res_T +sphere_create + (struct s3d_device* dev, + struct sphere** sphere); + +extern LOCAL_SYM void +sphere_ref_get + (struct sphere* sphere); + +extern LOCAL_SYM void +sphere_ref_put + (struct sphere* sphere); + +static INLINE int +sphere_is_degenerated(const struct sphere* sphere) +{ + ASSERT(sphere); + return sphere->radius < 0; +} + +static INLINE void +sphere_compute_aabb + (const struct sphere* sphere, + float lower[3], + float upper[3]) +{ + ASSERT(sphere && lower && upper); + if(sphere_is_degenerated(sphere)) { + f3_splat(lower, FLT_MAX); + f3_splat(upper,-FLT_MAX); + } else { + f3_subf(lower, sphere->pos, sphere->radius); + f3_addf(upper, sphere->pos, sphere->radius); + } +} + +static INLINE float +sphere_compute_area(const struct sphere* sphere) +{ + float r; + ASSERT(sphere); + r = sphere->radius; + return (float)(4*PI*r*r); +} + +static INLINE float +sphere_compute_volume(const struct sphere* sphere) +{ + float r; + ASSERT(sphere); + r = sphere->radius; + return (float)(4*PI*r*r*r/3); +} + +#endif /* S3D_SPHERE_H */ diff --git a/src/test_s3d_primitive.c b/src/test_s3d_primitive.c @@ -49,14 +49,6 @@ static const unsigned plane_nverts = sizeof(plane_verts) / sizeof(float[3]); static const unsigned plane_ids[] = { 0, 1, 2, 2, 3, 0 }; static const unsigned plane_ntris = sizeof(plane_ids) / sizeof(unsigned[3]); -static float -rand_canonic(void) -{ - int r; - while((r = rand()) == RAND_MAX); - return (float)r / (float)RAND_MAX; -} - static void plane_get_ids(const unsigned itri, unsigned ids[3], void* data) { diff --git a/src/test_s3d_sample_sphere.c b/src/test_s3d_sample_sphere.c @@ -0,0 +1,143 @@ +/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com) + * + * This software is a computer program whose purpose is to describe a + * virtual 3D environment that can be ray-traced and sampled both robustly + * and efficiently. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#include "s3d.h" +#include "test_s3d_utils.h" + +#include <rsys/float2.h> +#include <rsys/float3.h> + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct s3d_attrib attr0; + struct s3d_attrib attr1; + struct s3d_primitive prim0; + struct s3d_primitive prim1; + struct s3d_device* dev; + struct s3d_shape* sphere0; + struct s3d_shape* sphere1; + struct s3d_scene* scn; + struct s3d_scene_view* view; + unsigned sphere0_id; + unsigned sphere1_id; + float center[3]; + float st0[2]; + float st1[2]; + int N = 10000; + int i; + float sum; + float E, V, SE; + (void)argc, (void)argv; + + 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_shape_create_sphere(dev, &sphere1) == RES_OK); + CHK(s3d_shape_get_id(sphere0, &sphere0_id) == RES_OK); + CHK(s3d_shape_get_id(sphere1, &sphere1_id) == RES_OK); + + CHK(s3d_sphere_setup(sphere0, f3(center,-1.5, 0, 0), 2) == RES_OK); + CHK(s3d_sphere_setup(sphere1, f3(center, 1.5, 0, 0), 2) == RES_OK); + CHK(s3d_scene_attach_shape(scn, sphere0) == RES_OK); + CHK(s3d_scene_attach_shape(scn, sphere1) == RES_OK); + + CHK(s3d_scene_view_create(scn, S3D_SAMPLE, &view) == RES_OK); + CHK(s3d_scene_view_sample(view, 0, 0, 0, &prim0, st0) == RES_OK); + CHK(prim0.prim_id == 0); + CHK(prim0.geom_id == sphere0_id || prim0.geom_id == sphere1_id); + CHK(prim0.inst_id == S3D_INVALID_ID); + + CHK(s3d_scene_view_sample(view, 0, 0, 0, &prim1, st1) == RES_OK); + CHK(S3D_PRIMITIVE_EQ(&prim0, &prim1)); + CHK(f2_eq(st0, st1)); + + CHK(s3d_primitive_get_attrib(&prim0, S3D_ATTRIB_0, st0, &attr0) == RES_BAD_ARG); + CHK(s3d_primitive_get_attrib(&prim0, S3D_ATTRIB_1, st0, &attr0) == RES_BAD_ARG); + CHK(s3d_primitive_get_attrib(&prim0, S3D_ATTRIB_2, st0, &attr0) == RES_BAD_ARG); + CHK(s3d_primitive_get_attrib(&prim0, S3D_ATTRIB_3, st0, &attr0) == RES_BAD_ARG); + CHK(s3d_primitive_get_attrib(&prim0, S3D_POSITION, st0, &attr0) == RES_OK); + CHK(s3d_primitive_get_attrib + (&prim0, S3D_GEOMETRY_NORMAL, st0, &attr1) == RES_OK); + + if(prim0.geom_id == sphere0_id) { + f3_sub(attr0.value, attr0.value, f3(center,-1.5, 0, 0)); + } else { + f3_sub(attr0.value, attr0.value, f3(center, 1.5, 0, 0)); + } + f3_mulf(attr1.value, attr1.value, 2.f); + CHK(f3_eq_eps(attr0.value, attr1.value, 1.e-3f)); + + /* Check that 50 percents of samples lie onto "sphere0" */ + sum = 0; + FOR_EACH(i, 0, N) { + const float u = rand_canonic(); + const float v = rand_canonic(); + const float w = rand_canonic(); + + CHK(s3d_scene_view_sample(view, u, v, w, &prim0, st0) == RES_OK); + if(prim0.geom_id == sphere0_id) { + sum += 1; + } + + CHK(s3d_primitive_get_attrib(&prim0, S3D_POSITION, st0, &attr0) == RES_OK); + CHK(s3d_primitive_get_attrib + (&prim0, S3D_GEOMETRY_NORMAL, st0, &attr1) == RES_OK); + + if(prim0.geom_id == sphere0_id) { + f3_sub(attr0.value, attr0.value, f3(center,-1.5, 0, 0)); + } else { + f3_sub(attr0.value, attr0.value, f3(center, 1.5, 0, 0)); + } + f3_mulf(attr1.value, attr1.value, 2.f); + CHK(f3_eq_eps(attr0.value, attr1.value, 1.e-3f)); + } + E = sum / (float)N; + V = sum / (float)N - E*E; + SE = (float)sqrt(V/(float)N); + CHK(eq_epsf(E, 0.5, SE)); + + CHK(s3d_device_ref_put(dev) == RES_OK); + CHK(s3d_scene_ref_put(scn) == 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); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; +} + diff --git a/src/test_s3d_sampler.c b/src/test_s3d_sampler.c @@ -38,14 +38,6 @@ #define NSAMPS 4096 -static float -rand_canonic(void) -{ - int r; - while((r = rand()) == RAND_MAX); - return (float)r / (float)RAND_MAX; -} - int main(int argc, char** argv) { diff --git a/src/test_s3d_scene_view.c b/src/test_s3d_scene_view.c @@ -99,14 +99,6 @@ static const unsigned plane_ntris = sizeof(plane_ids) / sizeof(unsigned[3]); /******************************************************************************* * helper function ******************************************************************************/ -static float -rand_canonic(void) -{ - int r; - while((r = rand()) == RAND_MAX); - return (float)r / (float)RAND_MAX; -} - static void get_ids(const unsigned itri, unsigned ids[3], void* data) { diff --git a/src/test_s3d_sphere.c b/src/test_s3d_sphere.c @@ -0,0 +1,178 @@ +/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com) + * + * This software is a computer program whose purpose is to describe a + * virtual 3D environment that can be ray-traced and sampled both robustly + * and efficiently. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#include "s3d.h" +#include "test_s3d_utils.h" + +#include <rsys/float2.h> +#include <rsys/float3.h> + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct s3d_primitive prim0; + struct s3d_primitive prim1; + struct s3d_hit hit; + struct s3d_device* dev; + struct s3d_scene* scn; + struct s3d_shape* sphere0; + struct s3d_shape* sphere1; + struct s3d_scene_view* view; + float center[3] = {0, 0, 0}; + float radius; + float org[3]; + float dir[3]; + float range[2]; + float N[3], P[3], tmp[3]; + float lower[3]; + float upper[3]; + float area; + float volume; + size_t nprims; + unsigned sphere0_id; + unsigned sphere1_id; + (void)argc, (void)argv; + + 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(NULL, NULL) == RES_BAD_ARG); + CHK(s3d_shape_create_sphere(dev, NULL) == RES_BAD_ARG); + CHK(s3d_shape_create_sphere(NULL, &sphere0) == RES_BAD_ARG); + CHK(s3d_shape_create_sphere(dev, &sphere0) == RES_OK); + + CHK(s3d_sphere_setup(NULL, NULL, -1) == RES_BAD_ARG); + CHK(s3d_sphere_setup(sphere0, NULL, -1) == RES_BAD_ARG); + CHK(s3d_sphere_setup(NULL, center, -1) == RES_BAD_ARG); + CHK(s3d_sphere_setup(sphere0, center, -1) == RES_BAD_ARG); + CHK(s3d_sphere_setup(NULL, NULL, 1) == RES_BAD_ARG); + CHK(s3d_sphere_setup(sphere0, NULL, 1) == RES_BAD_ARG); + CHK(s3d_sphere_setup(NULL, center, 1) == RES_BAD_ARG); + CHK(s3d_sphere_setup(sphere0, center, 1) == RES_OK); + CHK(s3d_sphere_setup(sphere0, center, 0) == RES_BAD_ARG); + CHK(s3d_shape_ref_put(sphere0) == RES_OK); + + CHK(s3d_shape_create_sphere(dev, &sphere0) == RES_OK); + CHK(s3d_scene_attach_shape(scn, sphere0) == RES_OK); + CHK(s3d_scene_view_create(scn, S3D_TRACE, &view) == RES_OK); + + f3(org, 0, 0, 100); + f3(dir, 0, 0, -1); + f2(range, 0, FLT_MAX); + + CHK(s3d_scene_view_trace_ray(view, org, dir, range, NULL, &hit) == RES_OK); + CHK(S3D_HIT_NONE(&hit)); + + radius = 2; + CHK(s3d_sphere_setup(sphere0, center, 2) == RES_OK); + CHK(s3d_scene_view_trace_ray(view, org, dir, range, NULL, &hit) == RES_OK); + CHK(S3D_HIT_NONE(&hit)); + CHK(s3d_scene_view_ref_put(view) == RES_OK); + CHK(s3d_scene_view_create(scn, S3D_TRACE, &view) == RES_OK); + CHK(s3d_scene_view_trace_ray(view, org, dir, range, NULL, &hit) == RES_OK); + CHK(!S3D_HIT_NONE(&hit)); + CHK(eq_epsf(hit.distance, 100 - radius, 1.e-3f)); + f3_normalize(N, hit.normal); + f3_add(P, org, f3_mulf(P, dir, hit.distance)); + CHK(f3_eq_eps(N, f3(tmp, 0, 0, 1), 1.e-3f)); + CHK(f3_eq_eps(P, f3(tmp, 0, 0, radius), 1.e-3f)); + + CHK(s3d_scene_view_compute_area(view, &area) == RES_OK); + CHK(eq_epsf(area, (float)(4*PI*radius*radius), 1.e-6f)); + CHK(s3d_scene_view_compute_volume(view, &volume) == RES_OK); + CHK(eq_epsf(volume, (float)(4.0/3.0*PI*radius*radius*radius), 1.e-6f)); + + CHK(s3d_shape_flip_surface(sphere0) == RES_OK); + CHK(s3d_scene_view_compute_area(view, &area) == RES_OK); + CHK(eq_epsf(area, (float)(4*PI*radius*radius), 1.e-6f)); + CHK(s3d_scene_view_compute_volume(view, &volume) == RES_OK); + CHK(eq_epsf(volume, (float)(4.0/3.0*PI*radius*radius*radius), 1.e-6f)); + + CHK(s3d_scene_view_ref_put(view) == RES_OK); + CHK(s3d_scene_view_create(scn, S3D_TRACE, &view) == RES_OK); + + CHK(s3d_scene_view_compute_area(view, &area) == RES_OK); + CHK(eq_epsf(area, (float)(4*PI*radius*radius), 1.e-6f)); + CHK(s3d_scene_view_compute_volume(view, &volume) == RES_OK); + CHK(eq_epsf(volume, (float)(-4.0/3.0*PI*radius*radius*radius), 1.e-6f)); + + CHK(s3d_shape_flip_surface(sphere0) == RES_OK); + CHK(s3d_scene_view_ref_put(view) == RES_OK); + + center[0] = 4; + CHK(s3d_shape_create_sphere(dev, &sphere1) == RES_OK); + CHK(s3d_sphere_setup(sphere1, center, radius) == RES_OK); + CHK(s3d_scene_attach_shape(scn, sphere1) == RES_OK); + CHK(s3d_scene_view_create(scn, S3D_GET_PRIMITIVE, &view) == RES_OK); + + CHK(s3d_scene_view_compute_area(view, &area) == RES_OK); + CHK(eq_epsf(area, (float)(2*4*PI*radius*radius), 1.e-6f)); + CHK(s3d_scene_view_compute_volume(view, &volume) == RES_OK); + CHK(eq_epsf(volume, (float)(2*4.0/3.0*PI*radius*radius*radius), 1.e-6f)); + + CHK(s3d_shape_get_id(sphere0, &sphere0_id) == RES_OK); + CHK(s3d_shape_get_id(sphere1, &sphere1_id) == RES_OK); + CHK(sphere0_id != sphere1_id); + + CHK(s3d_scene_view_primitives_count(view, &nprims) == RES_OK); + CHK(nprims == 2); + + CHK(s3d_scene_view_get_aabb(view, lower, upper) == RES_OK); + CHK(f3_eq_eps(lower, f3_splat(tmp, -2), 1.e-6f)); + CHK(f3_eq_eps(upper, f3(tmp, 6, 2, 2), 1.e-6f)); + + CHK(s3d_scene_view_get_primitive(view, 0, &prim0) == RES_OK); + CHK(s3d_scene_view_get_primitive(view, 1, &prim1) == RES_OK); + CHK(prim0.prim_id == 0); + CHK(prim1.prim_id == 0); + CHK(prim0.geom_id == sphere0_id || prim0.geom_id == sphere1_id); + CHK(prim1.geom_id == sphere0_id || prim1.geom_id == sphere1_id); + CHK(prim0.geom_id != prim1.geom_id); + CHK(prim0.inst_id == S3D_INVALID_ID); + CHK(prim1.inst_id == S3D_INVALID_ID); + CHK(prim0.scene_prim_id == 0); + CHK(prim1.scene_prim_id == 1); + + 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); + CHK(s3d_scene_ref_put(scn) == RES_OK); + CHK(s3d_device_ref_put(dev) == RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_s3d_sphere_box.c b/src/test_s3d_sphere_box.c @@ -0,0 +1,147 @@ +/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com) + * + * This software is a computer program whose purpose is to describe a + * virtual 3D environment that can be ray-traced and sampled both robustly + * and efficiently. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#include "s3d.h" +#include "test_s3d_camera.h" +#include "test_s3d_cbox.h" +#include "test_s3d_utils.h" + +#include <rsys/image.h> +#include <rsys/float3.h> + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct s3d_device* dev; + struct s3d_shape* box; + struct s3d_shape* sphere; + struct s3d_scene* scn; + struct s3d_scene_view* view; + struct s3d_vertex_data vdata; + struct cbox_desc desc; + struct image img; + struct camera cam; + const size_t img_sz[2] = {640, 480}; + float pos[3] = {0, 0, 0}; + float tgt[3] = {0, 0, 0}; + float up[3] = {0, 1, 0}; + float tmp[3]; + float lower[3]; + float upper[3]; + float proj_ratio; + size_t x, y; + float center[3]; + (void)argc, (void)argv; + + 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, &sphere) == RES_OK); + CHK(s3d_sphere_setup(sphere, f3(center, 150, 200, 90), 90) == RES_OK); + CHK(s3d_scene_attach_shape(scn, sphere) == RES_OK); + CHK(s3d_shape_ref_put(sphere) == RES_OK); + + CHK(s3d_shape_create_sphere(dev, &sphere) == RES_OK); + CHK(s3d_sphere_setup(sphere, f3(center, 400, 200, 90), 90) == RES_OK); + CHK(s3d_scene_attach_shape(scn, sphere) == RES_OK); + CHK(s3d_shape_ref_put(sphere) == RES_OK); + + desc.vertices = cbox_walls; + desc.indices = cbox_walls_ids; + vdata.usage = S3D_POSITION; + vdata.type = S3D_FLOAT3; + vdata.get = cbox_get_position; + CHK(s3d_shape_create_mesh(dev, &box) == RES_OK); + CHK(s3d_mesh_setup_indexed_vertices(box, cbox_walls_ntris, cbox_get_ids, + cbox_walls_nverts, &vdata, 1, &desc) == RES_OK); + CHK(s3d_scene_attach_shape(scn, box) == RES_OK); + CHK(s3d_shape_ref_put(box) == RES_OK); + + CHK(s3d_scene_view_create(scn, S3D_TRACE, &view) == RES_OK); + CHK(s3d_scene_view_get_aabb(view, lower, upper) == RES_OK); + CHK(f3_eq(lower, f3(tmp, 0, 0, 0))); + CHK(f3_eq(upper, f3(tmp, 552, 559, 548))); + + image_init(NULL, &img); + CHK(image_setup + (&img, img_sz[0], img_sz[1], img_sz[0]*3, IMAGE_RGB8, NULL) == RES_OK); + + f3(pos, 278.f, -1000.f, 273.f); + f3(tgt, 278.f, 0.f, 273.f); + f3(up, 0, 0, 1); + proj_ratio = (float)img_sz[0] / (float)img_sz[1]; + camera_init(&cam, pos, tgt, up, (float)PI*0.25f, proj_ratio); + + FOR_EACH(y, 0, img_sz[1]) { + float pixel[2]; + 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); + if(S3D_HIT_NONE(&hit)) { + ((uint8_t*)img.pixels)[ipix+0] = 0; + ((uint8_t*)img.pixels)[ipix+1] = 0; + ((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); + } + } + } + + /* Write image */ + CHK(image_write_ppm_stream(&img, 0, stdout) == RES_OK); + image_release(&img); + + CHK(s3d_device_ref_put(dev) == RES_OK); + CHK(s3d_scene_ref_put(scn) == RES_OK); + CHK(s3d_scene_view_ref_put(view) == RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_s3d_sphere_instance.c b/src/test_s3d_sphere_instance.c @@ -0,0 +1,228 @@ +/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com) + * + * This software is a computer program whose purpose is to describe a + * virtual 3D environment that can be ray-traced and sampled both robustly + * and efficiently. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#include "s3d.h" +#include "test_s3d_camera.h" +#include "test_s3d_utils.h" + +#include <rsys/image.h> +#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, + const unsigned geom_id, + const unsigned inst0_id) +{ + struct s3d_attrib attr0; + struct s3d_attrib attr1; + struct s3d_primitive prim; + int N = 10000; + int i; + float center[3]; + float sum; + float st[2]; + float E, V, SE; + + /* Check that 50 percents of samples lie onto the 1st instance */ + sum = 0; + FOR_EACH(i, 0, N) { + const float u = rand_canonic(); + const float v = rand_canonic(); + const float w = rand_canonic(); + + CHK(s3d_scene_view_sample(view, u, v, w, &prim, st) == RES_OK); + CHK(prim.geom_id == geom_id); + if(prim.inst_id == inst0_id) { + sum += 1; + } + + CHK(s3d_primitive_get_attrib(&prim, S3D_POSITION, st, &attr0) == RES_OK); + CHK(s3d_primitive_get_attrib + (&prim, S3D_GEOMETRY_NORMAL, st, &attr1) == RES_OK); + + if(prim.inst_id == inst0_id) { + f3_sub(attr0.value, attr0.value, f3(center,-1.5, 0, 0)); + } else { + f3_sub(attr0.value, attr0.value, f3(center, 1.5, 0, 0)); + } + f3_mulf(attr1.value, attr1.value, 2.f); + CHK(f3_eq_eps(attr0.value, attr1.value, 1.e-3f)); + } + E = sum / (float)N; + V = sum / (float)N - E*E; + SE = (float)sqrt(V/(float)N); + CHK(eq_epsf(E, 0.5, SE)); +} + +static void +test_ray_tracing(struct s3d_scene_view* view) +{ + struct image img; + struct camera cam; + const size_t img_sz[2] = {640, 480}; + float pos[3] = {0, 0, 0}; + float tgt[3] = {0, 0, 0}; + float up[3] = {0, 1, 0}; + float proj_ratio; + size_t x, y; + + image_init(NULL, &img); + CHK(image_setup + (&img, img_sz[0], img_sz[1], img_sz[0]*3, IMAGE_RGB8, NULL) == RES_OK); + + f3(pos, 0, 0, -10); + f3(tgt, 0, 0, 0); + f3(up, 0, 1, 0); + proj_ratio = (float)img_sz[0] / (float)img_sz[1]; + camera_init(&cam, pos, tgt, up, (float)PI*0.25f, proj_ratio); + + FOR_EACH(y, 0, img_sz[1]) { + float pixel[2]; + 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); + if(S3D_HIT_NONE(&hit)) { + ((uint8_t*)img.pixels)[ipix+0] = 0; + ((uint8_t*)img.pixels)[ipix+1] = 0; + ((uint8_t*)img.pixels)[ipix+2] = 0; + } else { + float normal[3] = {0.f, 0.f, 0.f}; + f3_normalize(normal, hit.normal); + ((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); + } + } + } + + /* Write image */ + CHK(image_write_ppm_stream(&img, 0, stdout) == RES_OK); + image_release(&img); +} + +int +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; + struct s3d_scene_view* view; + unsigned geom_id; + 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, &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); + CHK(s3d_shape_get_id(sphere0, &inst0_id) == RES_OK); + CHK(s3d_shape_get_id(sphere0, &inst1_id) == RES_OK); + CHK(s3d_scene_ref_put(scn) == RES_OK); + + CHK(s3d_scene_create(dev, &scn) == RES_OK); + CHK(s3d_scene_attach_shape(scn, sphere0) == RES_OK); + CHK(s3d_scene_attach_shape(scn, sphere1) == RES_OK); + 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); + + test_sampling(view, geom_id, inst0_id); + test_ray_tracing(view); + + 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); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + 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 @@ -0,0 +1,149 @@ +/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com) + * + * This software is a computer program whose purpose is to describe a + * virtual 3D environment that can be ray-traced and sampled both robustly + * and efficiently. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#include "s3d.h" +#include "test_s3d_camera.h" +#include "test_s3d_utils.h" + +#include <rsys/image.h> +#include <rsys/math.h> + +int +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; + struct s3d_scene_view* view; + const size_t img_sz[2] = {640, 480}; + const float radius = 2; + const float center[3] = {1.0, 1.0, 0}; + 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; + (void)argc, (void)argv; + + CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); + image_init(&allocator, &img); + CHK(image_setup + (&img, img_sz[0], img_sz[1], img_sz[0]*3, IMAGE_RGB8, NULL) == 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, &shape) == RES_OK); + CHK(s3d_sphere_setup(shape, center, radius) == RES_OK); + 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); + proj_ratio = (float)img_sz[0] / (float)img_sz[1]; + camera_init(&cam, pos, tgt, up, (float)PI*0.25f, proj_ratio); + + FOR_EACH(y, 0, img_sz[1]) { + float pixel[2]; + 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*/; + + 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); + if(S3D_HIT_NONE(&hit)) { + ((uint8_t*)img.pixels)[ipix+0] = 0; + ((uint8_t*)img.pixels)[ipix+1] = 0; + ((uint8_t*)img.pixels)[ipix+2] = 0; + } else { + struct s3d_attrib attr; + float normal[3] = {0.f, 0.f, 0.f}; + float tmp[3]; + float len; + float dot; + + f3_normalize(normal, hit.normal); + CHK(s3d_primitive_get_attrib + (&hit.prim, S3D_GEOMETRY_NORMAL, hit.uv, &attr) == RES_OK); + f3_normalize(attr.value, attr.value); + CHK(f3_eq_eps(normal, attr.value, 1.e-3f)); + + f3_add(pos, org, f3_mulf(pos, dir, hit.distance)); + CHK(s3d_primitive_get_attrib + (&hit.prim, S3D_POSITION, hit.uv, &attr) == RES_OK); + CHK(f3_eq_eps(pos, attr.value, 1.e-3f)); + + len = f3_len(f3_sub(pos, pos, center)); + CHK(eq_epsf(len, radius, 1.e-3f)); + CHK(f3_eq_eps(f3_mulf(tmp, normal, radius), pos, 1.e-3f)); + + 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); + hit_something = 1; + } + } + } + CHK(hit_something == 1); + + /* Write image */ + CHK(image_write_ppm_stream(&img, 0, stdout) == RES_OK); + image_release(&img); + + CHK(s3d_device_ref_put(dev) == RES_OK); + CHK(s3d_scene_ref_put(scn) == RES_OK); + CHK(s3d_shape_ref_put(shape) == RES_OK); + CHK(s3d_scene_view_ref_put(view) == RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; +} + diff --git a/src/test_s3d_utils.h b/src/test_s3d_utils.h @@ -36,6 +36,14 @@ #include <rsys/mem_allocator.h> #include <stdio.h> +static INLINE float +rand_canonic(void) +{ + int r; + while((r = rand()) == RAND_MAX); + return (float)r / (float)RAND_MAX; +} + static void check_memory_allocator(struct mem_allocator* allocator) {