commit feb29598bd7077192484e7e4da76955c16e59f06
parent a3ac5a3a7fe8378facbb497a53d9918889ddb0ba
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Mon, 29 Feb 2016 14:17:21 +0100
Refactor the geometry definition of the tests
Diffstat:
3 files changed, 304 insertions(+), 279 deletions(-)
diff --git a/src/test_sschiff_estimator_cylinder.c b/src/test_sschiff_estimator_cylinder.c
@@ -47,11 +47,6 @@ struct result {
double avg_proj_area_SE;
};
-struct geometry {
- float* vertices;
- unsigned* indices;
-};
-
struct sampler_context {
struct geometry geometry;
double aspect_ratio;
@@ -59,12 +54,6 @@ struct sampler_context {
double sigma;
};
-struct cylinder {
- struct geometry* geometry;
- float radius;
- float height;
-};
-
static void
get_material_property
(void* mtl,
@@ -78,63 +67,13 @@ get_material_property
props->relative_real_refractive_index = 1.01;
}
-static void
-get_indices(const unsigned itri, unsigned ids[3], void* ctx)
-{
- struct cylinder* cylinder = ctx;
- const size_t i = itri * 3;
-
- CHECK(sa_size(cylinder->geometry->indices) % 3, 0);
- CHECK(itri < sa_size(cylinder->geometry->indices) / 3, 1);
- ids[0] = cylinder->geometry->indices[i + 0];
- ids[1] = cylinder->geometry->indices[i + 1];
- ids[2] = cylinder->geometry->indices[i + 2];
-}
-
-static void
-get_position(const unsigned ivert, float vertex[3], void* ctx)
-{
- struct cylinder* cylinder = ctx;
- const size_t i = ivert * 3;
-
- CHECK(sa_size(cylinder->geometry->vertices) % 3, 0);
- CHECK(ivert < sa_size(cylinder->geometry->vertices) / 3, 1);
- vertex[0] = cylinder->geometry->vertices[i + 0] * cylinder->radius;
- vertex[1] = cylinder->geometry->vertices[i + 1] * cylinder->radius;
- vertex[2] = cylinder->geometry->vertices[i + 2] * cylinder->height;
-}
-
-static FINLINE void
-dump_geometry(struct geometry* geom)
-{
- size_t i;
- NCHECK(geom, NULL);
- CHECK(sa_size(geom->vertices)%3, 0);
- CHECK(sa_size(geom->indices)%3, 0);
-
- FOR_EACH(i, 0, sa_size(geom->vertices)/3) {
- printf("v %f %f %f\n",
- geom->vertices[i*3+0],
- geom->vertices[i*3+1],
- geom->vertices[i*3+2]);
- }
- FOR_EACH(i, 0, sa_size(geom->indices)/3) {
- printf("f %d %d %d\n",
- geom->indices[i*3+0] + 1,
- geom->indices[i*3+1] + 1,
- geom->indices[i*3+2] + 1);
- }
-}
-
static res_T
sample_cylinder
(struct ssp_rng* rng, struct s3d_shape* shape, void* sampler_context)
{
- struct s3d_vertex_data attrib;
struct sampler_context* sampler_ctx = sampler_context;
struct cylinder cylinder;
double sample;
- size_t nverts, nprims;
(void)rng, (void)sampler_context;
sample = ssp_ran_lognormal(rng, log(sampler_ctx->mean_radius), log(sampler_ctx->sigma));
@@ -142,82 +81,7 @@ sample_cylinder
cylinder.radius = (float)(sample / pow(3.0 / (2.0*sampler_ctx->aspect_ratio), 1.0/3.0));
cylinder.height = (float)(2.f * cylinder.radius / sampler_ctx->aspect_ratio);
- attrib.usage = S3D_POSITION;
- attrib.type = S3D_FLOAT3;
- attrib.get = get_position;
-
- nverts = sa_size(cylinder.geometry->vertices) / 3/*#coords*/;
- nprims = sa_size(cylinder.geometry->indices) / 3/*#indices per prim*/;
-
- return s3d_mesh_setup_indexed_vertices(shape, (unsigned)nprims,
- get_indices, (unsigned)nverts, &attrib, 1, &cylinder);
-}
-
-static void
-cylinder_init(struct geometry* geometry, const unsigned nsteps)
-{
- const double step = 2*PI / (double)nsteps;
- unsigned istep;
-
- NCHECK(geometry, NULL);
- NCHECK(nsteps, 0);
-
- geometry->vertices = NULL;
- geometry->indices = NULL;
-
- /* Generate the vertex coordinates */
- FOR_EACH(istep, 0, nsteps) {
- const float theta = (float)(istep * step);
- const float x = (float)cos(theta);
- const float y = (float)sin(theta);
- f3(sa_add(geometry->vertices, 3), x, y, -1.f);
- f3(sa_add(geometry->vertices, 3), x, y, 0.f);
- }
-
- /* "Polar" vertices */
- f3(sa_add(geometry->vertices, 3), 0.f, 0.f, -1.f);
- f3(sa_add(geometry->vertices, 3), 0.f, 0.f, 0.f);
-
- /* Contour primitives */
- FOR_EACH(istep, 0, nsteps) {
- const unsigned id = istep * 2;
- unsigned* iprim;
-
- iprim = sa_add(geometry->indices, 3);
- iprim[0] = (id + 0);
- iprim[1] = (id + 1);
- iprim[2] = (id + 2) % (nsteps*2);
-
- iprim = sa_add(geometry->indices, 3);
- iprim[0] = (id + 2) % (nsteps*2);
- iprim[1] = (id + 1);
- iprim[2] = (id + 3) % (nsteps*2);
- }
-
- /* Cap primitives */
- FOR_EACH(istep, 0, nsteps) {
- const unsigned id = istep * 2;
- unsigned* iprim;
-
- iprim = sa_add(geometry->indices, 3);
- iprim[0] = (nsteps * 2);
- iprim[1] = (id + 0);
- iprim[2] = (id + 2) % (nsteps*2);
-
- iprim = sa_add(geometry->indices, 3);
- iprim[0] = (nsteps * 2) + 1;
- iprim[1] = (id + 3) % (nsteps*2);
- iprim[2] = (id + 1);
- }
-}
-
-static void
-cylinder_release(struct geometry* geometry)
-{
- sa_release(geometry->vertices);
- sa_release(geometry->indices);
- geometry->vertices = NULL;
- geometry->indices = NULL;
+ return cylinder_setup_s3d_shape(&cylinder, shape);
}
static INLINE void
@@ -554,7 +418,7 @@ main(int argc, char** argv)
};
const size_t nx = sizeof(x)/sizeof(double);
const size_t nscatt_angles = 1000;
- const size_t ngeoms = 1000;
+ const size_t ngeoms = 100;
const size_t ndirs = 100;
size_t i;
(void)argc, (void)argv;
@@ -565,7 +429,7 @@ main(int argc, char** argv)
CHECK(sschiff_device_create
(NULL, &allocator, SSCHIFF_NTHREADS_DEFAULT, 1, NULL, &dev), RES_OK);
- cylinder_init(&sampler_ctx.geometry, 64);
+ geometry_init_cylinder(&sampler_ctx.geometry, 64);
FOR_EACH(i, 0, nx) {
const double wavelength = 0.6; /* In micron */
@@ -635,7 +499,7 @@ main(int argc, char** argv)
CHECK(sschiff_device_ref_put(dev), RES_OK);
CHECK(ssp_rng_ref_put(rng), RES_OK);
- cylinder_release(&sampler_ctx.geometry);
+ geometry_release(&sampler_ctx.geometry);
check_memory_allocator(&allocator);
mem_shutdown_proxy_allocator(&allocator);
CHECK(mem_allocated_size(), 0);
diff --git a/src/test_sschiff_estimator_sphere.c b/src/test_sschiff_estimator_sphere.c
@@ -43,11 +43,6 @@ struct result {
double extinction_cross_section;
};
-struct geometry {
- float* vertices; /* List of float[3] */
- unsigned* indices; /* List of unsigned[3] */
-};
-
struct sampler_context {
struct geometry geometry;
double wavelength; /* In micron */
@@ -58,11 +53,6 @@ struct sampler_context {
double sigma;
};
-struct sphere {
- struct geometry* geometry;
- float radius; /* In micron */
-};
-
static void
get_material_property
(void* mtl,
@@ -82,148 +72,22 @@ get_material_property
ctx->relative_imaginary_refractive_index;
}
-static void
-get_indices(const unsigned itri, unsigned ids[3], void* ctx)
-{
- struct sphere* sphere = ctx;
- const size_t i = itri * 3;
-
- CHECK(sa_size(sphere->geometry->indices) % 3, 0);
- CHECK(itri < sa_size(sphere->geometry->indices) / 3, 1);
- ids[0] = sphere->geometry->indices[i + 0];
- ids[1] = sphere->geometry->indices[i + 1];
- ids[2] = sphere->geometry->indices[i + 2];
-}
-
-static INLINE void
-get_position(const unsigned ivert, float vertex[3], void* ctx)
-{
- struct sphere* sphere = ctx;
- const size_t i = ivert * 3;
-
- CHECK(sa_size(sphere->geometry->vertices) % 3, 0);
- CHECK(ivert < sa_size(sphere->geometry->vertices) / 3, 1);
- vertex[0] = sphere->geometry->vertices[i + 0] * sphere->radius;
- vertex[1] = sphere->geometry->vertices[i + 1] * sphere->radius;
- vertex[2] = sphere->geometry->vertices[i + 2] * sphere->radius;
-}
-
static res_T
sample_sphere
(struct ssp_rng* rng,
struct s3d_shape* shape,
void* sampler_context)
{
- struct s3d_vertex_data attrib;
struct sampler_context* sampler_ctx = sampler_context;
struct sphere sphere;
- size_t nverts, nprims;
NCHECK(rng, NULL);
- NCHECK(shape, NULL);
sphere.geometry = &sampler_ctx->geometry;
sphere.radius = (float)ssp_ran_lognormal
(rng, log(sampler_ctx->mean_radius), log(sampler_ctx->sigma));
- attrib.usage = S3D_POSITION;
- attrib.type = S3D_FLOAT3;
- attrib.get = get_position;
-
- nverts = sa_size(sphere.geometry->vertices) / 3/*#coords*/;
- nprims = sa_size(sphere.geometry->indices) / 3/*#indices per prim*/;
-
- return s3d_mesh_setup_indexed_vertices(shape, (unsigned)nprims,
- get_indices, (unsigned)nverts, &attrib, 1, &sphere);
-}
-
-static void
-sphere_init(struct geometry* sphere, const unsigned ntheta)
-{
- const unsigned nphi = (unsigned)(((double)ntheta + 0.5) / 2.0);
- const double step_theta = 2*PI / (double)ntheta;
- const double step_phi = PI / (double)nphi;
- double* cos_theta = NULL;
- double* sin_theta = NULL;
- double* cos_phi = NULL;
- double* sin_phi = NULL;
- unsigned itheta, iphi;
-
- NCHECK(sphere, NULL);
- NCHECK(ntheta, 0);
-
- sphere->vertices = NULL;
- sphere->indices = NULL;
-
- /* Precompute the cosine/sinus of the theta/phi angles */
- FOR_EACH(itheta, 0, ntheta) {
- const double theta = -PI + (double)itheta * step_theta;
- sa_push(cos_theta, cos(theta));
- sa_push(sin_theta, sin(theta));
- }
- FOR_EACH(iphi, 1, nphi) {
- const double phi = -PI/2 + (double)iphi * step_phi;
- sa_push(cos_phi, cos(phi));
- sa_push(sin_phi, sin(phi));
- }
- /* Compute the contour vertices */
- FOR_EACH(itheta, 0, ntheta) {
- FOR_EACH(iphi, 0, nphi-1) {
- sa_push(sphere->vertices, (float)(cos_theta[itheta]*cos_phi[iphi]));
- sa_push(sphere->vertices, (float)(sin_theta[itheta]*cos_phi[iphi]));
- sa_push(sphere->vertices, (float)sin_phi[iphi]);
- }
- }
- /* Compute the polar vertices */
- f3(sa_add(sphere->vertices, 3), 0.f, 0.f,-1.f);
- f3(sa_add(sphere->vertices, 3), 0.f, 0.f, 1.f);
-
- /* Define the indices of the contour primitives */
- FOR_EACH(itheta, 0, ntheta) {
- const unsigned itheta0 = itheta * (nphi - 1);
- const unsigned itheta1 = ((itheta + 1) % ntheta) * (nphi - 1);
- FOR_EACH(iphi, 0, nphi-2) {
- const unsigned iphi0 = iphi + 0;
- const unsigned iphi1 = iphi + 1;
-
- sa_push(sphere->indices, itheta0 + iphi0);
- sa_push(sphere->indices, itheta0 + iphi1);
- sa_push(sphere->indices, itheta1 + iphi0);
-
- sa_push(sphere->indices, itheta1 + iphi0);
- sa_push(sphere->indices, itheta0 + iphi1);
- sa_push(sphere->indices, itheta1 + iphi1);
- }
- }
-
- /* Define the indices of the polar primitives */
- FOR_EACH(itheta, 0, ntheta) {
- const unsigned itheta0 = itheta * (nphi - 1);
- const unsigned itheta1 = ((itheta + 1) % ntheta) * (nphi - 1);
-
- sa_push(sphere->indices, ntheta * (nphi - 1));
- sa_push(sphere->indices, itheta0);
- sa_push(sphere->indices, itheta1);
-
- sa_push(sphere->indices, ntheta * (nphi - 1) + 1);
- sa_push(sphere->indices, itheta1 + (nphi - 2));
- sa_push(sphere->indices, itheta0 + (nphi - 2));
- }
-
- /* Release the intermediary data structure */
- sa_release(cos_theta);
- sa_release(sin_theta);
- sa_release(cos_phi);
- sa_release(sin_phi);
-}
-
-static void
-sphere_release(struct geometry* sphere)
-{
- sa_release(sphere->vertices);
- sa_release(sphere->indices);
- sphere->vertices = NULL;
- sphere->indices = NULL;
+ return sphere_setup_s3d_shape(&sphere, shape);
}
static void
@@ -347,7 +211,7 @@ main(int argc, char** argv)
CHECK(sschiff_device_create
(NULL, &allocator, SSCHIFF_NTHREADS_DEFAULT, 1, NULL, &dev), RES_OK);
- sphere_init(&sampler_ctx.geometry, 64);
+ geometry_init_sphere(&sampler_ctx.geometry, 64);
sampler_ctx.wavelength = wlen;
sampler_ctx.relative_real_refractive_index = 1.1;
@@ -485,7 +349,7 @@ main(int argc, char** argv)
CHECK(sschiff_device_ref_put(dev), RES_OK);
CHECK(ssp_rng_ref_put(rng), RES_OK);
- sphere_release(&sampler_ctx.geometry);
+ geometry_release(&sampler_ctx.geometry);
check_memory_allocator(&allocator);
mem_shutdown_proxy_allocator(&allocator);
CHECK(mem_allocated_size(), 0);
diff --git a/src/test_sschiff_utils.h b/src/test_sschiff_utils.h
@@ -29,8 +29,305 @@
#ifndef TEST_SSCHIFF_UTILS_H
#define TEST_SSCHIFF_UTILS_H
+#include <rsys/float3.h>
+#include <rsys/stretchy_array.h>
#include <rsys/mem_allocator.h>
+#include <star/s3d.h>
+
+/*******************************************************************************
+ * Helper geometry data structure
+ ******************************************************************************/
+struct geometry {
+ float* vertices; /* List of float[3] */
+ unsigned* indices; /* List of unsigned[3] */
+};
+
+static INLINE void
+geometry_init_sphere(struct geometry* sphere, const unsigned ntheta)
+{
+ const unsigned nphi = (unsigned)(((double)ntheta + 0.5) / 2.0);
+ const double step_theta = 2*PI / (double)ntheta;
+ const double step_phi = PI / (double)nphi;
+ double* cos_theta = NULL;
+ double* sin_theta = NULL;
+ double* cos_phi = NULL;
+ double* sin_phi = NULL;
+ unsigned itheta, iphi;
+
+ NCHECK(sphere, NULL);
+ NCHECK(ntheta, 0);
+
+ sphere->vertices = NULL;
+ sphere->indices = NULL;
+
+ /* Precompute the cosine/sinus of the theta/phi angles */
+ FOR_EACH(itheta, 0, ntheta) {
+ const double theta = -PI + (double)itheta * step_theta;
+ sa_push(cos_theta, cos(theta));
+ sa_push(sin_theta, sin(theta));
+ }
+ FOR_EACH(iphi, 1, nphi) {
+ const double phi = -PI/2 + (double)iphi * step_phi;
+ sa_push(cos_phi, cos(phi));
+ sa_push(sin_phi, sin(phi));
+ }
+ /* Compute the contour vertices */
+ FOR_EACH(itheta, 0, ntheta) {
+ FOR_EACH(iphi, 0, nphi-1) {
+ sa_push(sphere->vertices, (float)(cos_theta[itheta]*cos_phi[iphi]));
+ sa_push(sphere->vertices, (float)(sin_theta[itheta]*cos_phi[iphi]));
+ sa_push(sphere->vertices, (float)sin_phi[iphi]);
+ }
+ }
+ /* Compute the polar vertices */
+ f3(sa_add(sphere->vertices, 3), 0.f, 0.f,-1.f);
+ f3(sa_add(sphere->vertices, 3), 0.f, 0.f, 1.f);
+
+ /* Define the indices of the contour primitives */
+ FOR_EACH(itheta, 0, ntheta) {
+ const unsigned itheta0 = itheta * (nphi - 1);
+ const unsigned itheta1 = ((itheta + 1) % ntheta) * (nphi - 1);
+ FOR_EACH(iphi, 0, nphi-2) {
+ const unsigned iphi0 = iphi + 0;
+ const unsigned iphi1 = iphi + 1;
+
+ sa_push(sphere->indices, itheta0 + iphi0);
+ sa_push(sphere->indices, itheta0 + iphi1);
+ sa_push(sphere->indices, itheta1 + iphi0);
+
+ sa_push(sphere->indices, itheta1 + iphi0);
+ sa_push(sphere->indices, itheta0 + iphi1);
+ sa_push(sphere->indices, itheta1 + iphi1);
+ }
+ }
+
+ /* Define the indices of the polar primitives */
+ FOR_EACH(itheta, 0, ntheta) {
+ const unsigned itheta0 = itheta * (nphi - 1);
+ const unsigned itheta1 = ((itheta + 1) % ntheta) * (nphi - 1);
+
+ sa_push(sphere->indices, ntheta * (nphi - 1));
+ sa_push(sphere->indices, itheta0);
+ sa_push(sphere->indices, itheta1);
+
+ sa_push(sphere->indices, ntheta * (nphi - 1) + 1);
+ sa_push(sphere->indices, itheta1 + (nphi - 2));
+ sa_push(sphere->indices, itheta0 + (nphi - 2));
+ }
+
+ /* Release the intermediary data structure */
+ sa_release(cos_theta);
+ sa_release(sin_theta);
+ sa_release(cos_phi);
+ sa_release(sin_phi);
+}
+
+static INLINE void
+geometry_init_cylinder(struct geometry* geometry, const unsigned nsteps)
+{
+ const double step = 2*PI / (double)nsteps;
+ unsigned istep;
+
+ NCHECK(geometry, NULL);
+ NCHECK(nsteps, 0);
+
+ geometry->vertices = NULL;
+ geometry->indices = NULL;
+
+ /* Generate the vertex coordinates */
+ FOR_EACH(istep, 0, nsteps) {
+ const float theta = (float)(istep * step);
+ const float x = (float)cos(theta);
+ const float y = (float)sin(theta);
+ f3(sa_add(geometry->vertices, 3), x, y, -1.f);
+ f3(sa_add(geometry->vertices, 3), x, y, 0.f);
+ }
+
+ /* "Polar" vertices */
+ f3(sa_add(geometry->vertices, 3), 0.f, 0.f, -1.f);
+ f3(sa_add(geometry->vertices, 3), 0.f, 0.f, 0.f);
+
+ /* Contour primitives */
+ FOR_EACH(istep, 0, nsteps) {
+ const unsigned id = istep * 2;
+ unsigned* iprim;
+
+ iprim = sa_add(geometry->indices, 3);
+ iprim[0] = (id + 0);
+ iprim[1] = (id + 1);
+ iprim[2] = (id + 2) % (nsteps*2);
+
+ iprim = sa_add(geometry->indices, 3);
+ iprim[0] = (id + 2) % (nsteps*2);
+ iprim[1] = (id + 1);
+ iprim[2] = (id + 3) % (nsteps*2);
+ }
+
+ /* Cap primitives */
+ FOR_EACH(istep, 0, nsteps) {
+ const unsigned id = istep * 2;
+ unsigned* iprim;
+
+ iprim = sa_add(geometry->indices, 3);
+ iprim[0] = (nsteps * 2);
+ iprim[1] = (id + 0);
+ iprim[2] = (id + 2) % (nsteps*2);
+
+ iprim = sa_add(geometry->indices, 3);
+ iprim[0] = (nsteps * 2) + 1;
+ iprim[1] = (id + 3) % (nsteps*2);
+ iprim[2] = (id + 1);
+ }
+
+}
+
+static INLINE void
+geometry_release(struct geometry* geometry)
+{
+ NCHECK(geometry, NULL);
+ sa_release(geometry->vertices);
+ sa_release(geometry->indices);
+ geometry->vertices = NULL;
+ geometry->indices = NULL;
+}
+
+static INLINE void
+geometry_dump(struct geometry* geom, FILE* file)
+{
+ size_t i;
+ NCHECK(geom, NULL);
+ NCHECK(file, NULL);
+
+ CHECK(sa_size(geom->vertices)%3, 0); /* Ensure 3D position */
+ CHECK(sa_size(geom->indices)%3, 0); /* Ensure triangular primitives */
+
+ FOR_EACH(i, 0, sa_size(geom->vertices)/3) {
+ fprintf(file, "v %f %f %f\n",
+ geom->vertices[i*3+0],
+ geom->vertices[i*3+1],
+ geom->vertices[i*3+2]);
+ }
+ FOR_EACH(i, 0, sa_size(geom->indices)/3) {
+ fprintf(file, "f %d %d %d\n",
+ geom->indices[i*3+0] + 1,
+ geom->indices[i*3+1] + 1,
+ geom->indices[i*3+2] + 1);
+ }
+}
+
+/*******************************************************************************
+ * Cylinder shape
+ ******************************************************************************/
+struct cylinder {
+ struct geometry* geometry;
+ float radius;
+ float height;
+};
+
+static INLINE void
+cylinder_get_indices(const unsigned itri, unsigned ids[3], void* ctx)
+{
+ struct cylinder* cylinder = ctx;
+ const size_t i = itri * 3;
+
+ CHECK(sa_size(cylinder->geometry->indices) % 3, 0);
+ CHECK(itri < sa_size(cylinder->geometry->indices) / 3, 1);
+ ids[0] = cylinder->geometry->indices[i + 0];
+ ids[1] = cylinder->geometry->indices[i + 1];
+ ids[2] = cylinder->geometry->indices[i + 2];
+}
+
+static INLINE void
+cylinder_get_position(const unsigned ivert, float vertex[3], void* ctx)
+{
+ struct cylinder* cylinder = ctx;
+ const size_t i = ivert * 3;
+
+ CHECK(sa_size(cylinder->geometry->vertices) % 3, 0);
+ CHECK(ivert < sa_size(cylinder->geometry->vertices) / 3, 1);
+ vertex[0] = cylinder->geometry->vertices[i + 0] * cylinder->radius;
+ vertex[1] = cylinder->geometry->vertices[i + 1] * cylinder->radius;
+ vertex[2] = cylinder->geometry->vertices[i + 2] * cylinder->height;
+}
+
+static INLINE res_T
+cylinder_setup_s3d_shape(struct cylinder* cylinder, struct s3d_shape* shape)
+{
+ struct s3d_vertex_data attrib;
+ size_t nverts, nprims;
+
+ NCHECK(cylinder, NULL);
+ NCHECK(shape, NULL);
+
+ attrib.usage = S3D_POSITION;
+ attrib.type = S3D_FLOAT3;
+ attrib.get = cylinder_get_position;
+
+ nverts = sa_size(cylinder->geometry->vertices) / 3/*#coords*/;
+ nprims = sa_size(cylinder->geometry->indices) / 3/*#indices per prim*/;
+
+ return s3d_mesh_setup_indexed_vertices(shape, (unsigned)nprims,
+ cylinder_get_indices, (unsigned)nverts, &attrib, 1, cylinder);
+}
+
+/*******************************************************************************
+ * Spherical shape
+ ******************************************************************************/
+struct sphere {
+ struct geometry* geometry;
+ float radius;
+};
+
+static INLINE void
+sphere_get_indices(const unsigned itri, unsigned ids[3], void* ctx)
+{
+ struct sphere* sphere = ctx;
+ const size_t i = itri * 3;
+
+ CHECK(sa_size(sphere->geometry->indices) % 3, 0);
+ CHECK(itri < sa_size(sphere->geometry->indices) / 3, 1);
+ ids[0] = sphere->geometry->indices[i + 0];
+ ids[1] = sphere->geometry->indices[i + 1];
+ ids[2] = sphere->geometry->indices[i + 2];
+}
+
+static INLINE void
+sphere_get_position(const unsigned ivert, float vertex[3], void* ctx)
+{
+ struct sphere* sphere = ctx;
+ const size_t i = ivert * 3;
+
+ CHECK(sa_size(sphere->geometry->vertices) % 3, 0);
+ CHECK(ivert < sa_size(sphere->geometry->vertices) / 3, 1);
+ vertex[0] = sphere->geometry->vertices[i + 0] * sphere->radius;
+ vertex[1] = sphere->geometry->vertices[i + 1] * sphere->radius;
+ vertex[2] = sphere->geometry->vertices[i + 2] * sphere->radius;
+}
+
+static INLINE res_T
+sphere_setup_s3d_shape(struct sphere* sphere, struct s3d_shape* shape)
+{
+ struct s3d_vertex_data attrib;
+ size_t nverts, nprims;
+
+ NCHECK(sphere, NULL);
+ NCHECK(shape, NULL);
+
+ attrib.usage = S3D_POSITION;
+ attrib.type = S3D_FLOAT3;
+ attrib.get = sphere_get_position;
+
+ nverts = sa_size(sphere->geometry->vertices) / 3/*#coords*/;
+ nprims = sa_size(sphere->geometry->indices) / 3/*#indices per prim*/;
+
+ return s3d_mesh_setup_indexed_vertices(shape, (unsigned)nprims,
+ sphere_get_indices, (unsigned)nverts, &attrib, 1, sphere);
+}
+
+/*******************************************************************************
+ * Miscellaneous functions
+ ******************************************************************************/
static void
check_memory_allocator(struct mem_allocator* allocator)
{