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