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 b5f75e6b4f96443ea32c2a14d1b643ea10e88fa2
parent f7e2a36b1a1e5816b0935b2db4efc90bff532f65
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed, 17 Jan 2018 15:55:48 +0100

Add the sphere shape

It is still not tested. On sphere intersection, the parametric
coordinate of the hit is not set. Finally, spheric pimitive are not
handled by the s3d_primtive_get_attrib function.

Diffstat:
Mcmake/CMakeLists.txt | 6++++--
Msrc/s3d.h | 28+++++++++++++++++++++-------
Msrc/s3d_geometry.c | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/s3d_geometry.h | 23++++++++++++++++++++++-
Msrc/s3d_mesh.h | 6+++---
Msrc/s3d_primitive.c | 120+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/s3d_scene_view.c | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/s3d_shape.c | 59++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/s3d_shape_c.h | 2++
Asrc/s3d_sphere.c | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/s3d_sphere.h | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
11 files changed, 589 insertions(+), 92 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' 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,27 @@ s3d_triangle_get_vertex_attrib struct s3d_attrib* attrib); /******************************************************************************* + * Sphere API - Manage a spherical shape + ******************************************************************************/ +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); + +/******************************************************************************* * 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 +511,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_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_sphere.h" #include <rsys/mem_allocator.h> @@ -55,6 +56,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 +118,90 @@ 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); + + 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.u = 0.0f; /* TODO */ + ray.v = 0.0f; /* TODO */ + 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.u = 0.0f; /* TODO */ + ray.v = 0.0f; /* TODO */ + 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); + } +} + +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,6 +30,9 @@ * 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_MESH_H +#define S3D_MESH_H + #include "s3d.h" #include "s3d_buffer.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 diff --git a/src/s3d_primitive.c b/src/s3d_primitive.c @@ -31,9 +31,11 @@ * 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> @@ -46,7 +48,7 @@ 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->shape__ != NULL && (prim->inst_id != S3D_INVALID_ID || prim->inst__ == NULL); } @@ -61,7 +63,7 @@ s3d_primitive_get_attrib struct s3d_attrib* attrib) { const uint32_t* ids; - struct geometry* geom_mesh = NULL; + struct geometry* geom_shape = NULL; const float* transform = NULL; char flip_surface = 0; float w; @@ -77,32 +79,36 @@ s3d_primitive_get_attrib return RES_BAD_ARG; if(prim->inst__ == NULL) { - geom_mesh = (struct geometry*)prim->mesh__; - flip_surface = geom_mesh->flip_surface; + 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_mesh = (struct geometry*)prim->mesh__; + geom_shape = (struct geometry*)prim->shape__; transform = geom_inst->data.instance->transform; - ASSERT(geom_mesh); - flip_surface = geom_inst->flip_surface ^ geom_mesh->flip_surface; + 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) { /* TODO */ + log_error(geom_shape->dev, "%s: unsupported sphere primitive.\n", FUNC_NAME); + return RES_BAD_ARG; } - 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_shape->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_shape->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_shape->data.mesh) + prim->prim_id * 3/*#triangle ids*/; attrib->usage = usage; if(usage == S3D_POSITION || usage == S3D_GEOMETRY_NORMAL) { @@ -110,7 +116,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_shape->data.mesh); v0 = pos + ids[0] * 3; v1 = pos + ids[1] * 3; v2 = pos + ids[2] * 3; @@ -144,10 +150,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_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); + attr = mesh_get_attr(geom_shape->data.mesh, usage); v0 = attr + ids[0] * dim; v1 = attr + ids[1] * dim; v2 = attr + ids[2] * dim; @@ -176,9 +182,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 +199,7 @@ s3d_primitive_sample const float v, float st[2]) { + struct geometry* geom_shape; double sqrt_u; if(!check_primitive(prim) || !st) @@ -199,35 +209,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] = (float)(u * 2*PI); + 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 +287,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 +297,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 +311,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 @@ -158,35 +158,35 @@ hit_setup(struct s3d_scene_view* scnview, const RTCRay* ray, struct s3d_hit* hit 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,11 +197,12 @@ 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; + flip_surface ^= ((struct geometry*)hit->prim.shape__)->flip_surface; if(flip_surface) f3_minus(hit->normal, hit->normal); } @@ -245,6 +246,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) @@ -383,6 +395,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 +529,58 @@ 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; + } + + /* 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; + } + + 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 +686,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 +745,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 +797,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 +855,10 @@ 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); case GEOM_INSTANCE: volume += scene_view_compute_volume(geom->data.instance->scnview, flip); break; @@ -819,6 +902,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 +1255,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; @@ -1198,16 +1284,21 @@ s3d_scene_view_sample } 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 +1360,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; @@ -1300,7 +1391,7 @@ s3d_scene_view_get_primitive } ASSERT(geom->type == GEOM_MESH); ASSERT(i < mesh_get_ntris(geom->data.mesh)); - prim->mesh__ = geom; + prim->shape__ = geom; prim->geom_id = geom->name; prim->prim_id = (unsigned)i; prim->scene_prim_id += geom->scene_prim_id_offset; @@ -1347,6 +1438,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 +1495,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)); 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,28 @@ 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; } 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,102 @@ +/* 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.h" + +#include <rsys/ref_count.h> +#include <rsys/float3.h> + +struct sphere { + float pos[3]; + float radius; + struct s3d_device* dev; + 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 */