commit 95987dd2f27fb613bd9ca9cb40f473e7179705e0
parent 9d8d2ff7ee24f94b92e507cacdaa719d19ea5888
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Mon, 27 Jun 2016 15:40:59 +0200
Implement and test the s2d_scene_sample function
Implement the s2d_primitive API
Diffstat:
7 files changed, 503 insertions(+), 12 deletions(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -59,6 +59,7 @@ set(S2D_FILES_SRC
s2d_device.c
s2d_geometry.c
s2d_line_segments.c
+ s2d_primitive.c
s2d_scene.c
s2d_shape.c)
set(S2D_FILES_INC_API s2d.h)
@@ -126,6 +127,7 @@ if(NOT NO_TEST)
endfunction()
new_test(test_s2d_device)
+ new_test(test_s2d_sample)
new_test(test_s2d_scene)
new_test(test_s2d_shape)
new_test(test_s2d_trace_ray)
diff --git a/src/s2d_line_segments.c b/src/s2d_line_segments.c
@@ -35,6 +35,26 @@
/*******************************************************************************
* Helper functions
******************************************************************************/
+static FINLINE float
+line_compute_segment_length
+ (struct line_segments* lines,
+ const size_t isegment)
+{
+ const uint32_t* ids;
+ const float* pos;
+ const float* v0;
+ const float* v1;
+ const size_t id = isegment*2/*#ids per segment*/;
+ float tmp[2];
+ ASSERT(lines && isegment < line_segments_get_nsegments(lines));
+
+ ids = line_segments_get_ids(lines);
+ pos = line_segments_get_pos(lines);
+ v0 = pos + ids[id+0]*2/*#coords*/;
+ v1 = pos + ids[id+1]*2/*#coords*/;
+ return f2_len(f2_sub(tmp, v1, v0));
+}
+
static void
line_setup_indices
(struct line_segments* lines,
@@ -209,6 +229,7 @@ line_release(ref_T* ref)
lines = CONTAINER_OF(ref, struct line_segments, ref);
line_segments_clear(lines);
dev = lines->dev;
+ darray_float_release(&lines->cdf);
MEM_RM(dev->allocator, lines);
S2D(device_ref_put(dev));
}
@@ -232,6 +253,7 @@ line_segments_create(struct s2d_device* dev, struct line_segments** out_lines)
ref_init(&lines->ref);
S2D(device_ref_get(dev));
lines->dev = dev;
+ darray_float_init(dev->allocator, &lines->cdf);
exit:
*out_lines = lines;
@@ -275,6 +297,7 @@ line_segments_clear(struct line_segments* lines)
}
lines->resize_mask = 0;
lines->update_mask = 0;
+ darray_float_clear(&lines->cdf);
}
size_t
@@ -326,28 +349,47 @@ line_segments_get_attr
return darray_float_data_get(&lines->attribs[usage]->data);
}
+res_T
+line_segments_compute_cdf(struct line_segments* lines)
+{
+ size_t iseg, nsegs;
+ float length = 0.f;
+ res_T res = RES_OK;
+ ASSERT(lines);
+
+ darray_float_clear(&lines->cdf);
+
+ nsegs = line_segments_get_nsegments(lines);
+ if(!nsegs) goto exit;
+
+ res = darray_float_resize(&lines->cdf, nsegs);
+ if(res != RES_OK) goto error;
+
+ FOR_EACH(iseg, 0, nsegs) {
+ length += line_compute_segment_length(lines, iseg);
+ darray_float_data_get(&lines->cdf)[iseg] = length;
+ }
+
+exit:
+ return res;
+error:
+ darray_float_clear(&lines->cdf);
+ goto exit;
+}
+
float
line_segments_compute_length(struct line_segments* lines)
{
- const uint32_t* ids;
- const float* pos;
size_t iseg, nsegs;
- float tmp[2];
float length = 0.f;
ASSERT(lines);
nsegs = line_segments_get_nsegments(lines);
if(!nsegs) return 0.f;
- ids = line_segments_get_ids(lines);
- pos = line_segments_get_pos(lines);
+ FOR_EACH(iseg, 0, nsegs)
+ length += line_compute_segment_length(lines, iseg);
- FOR_EACH(iseg, 0, nsegs) {
- const size_t id = iseg * 2/*#ids per segment*/;
- const float* v0 = pos + ids[id+0]*2/*#coords*/;
- const float* v1 = pos + ids[id+1]*2/*#coords*/;
- length += f2_len(f2_sub(tmp, v0, v1));
- }
return length;
}
diff --git a/src/s2d_line_segments.h b/src/s2d_line_segments.h
@@ -62,6 +62,7 @@ struct line_segments { /* Segmented contour */
struct index_buffer* indices;
struct vertex_buffer* attribs[S2D_ATTRIBS_COUNT__];
enum s2d_type attribs_type[S2D_ATTRIBS_COUNT__];
+ struct darray_float cdf;
struct hit_filter filter;
int resize_mask; /* Combination of buffer_type */
@@ -108,6 +109,11 @@ line_segments_get_attr
(struct line_segments* lines,
const enum s2d_attrib_usage usage);
+/* Compute line segments Cumulative Distribution Function */
+extern LOCAL_SYM res_T
+line_segments_compute_cdf
+ (struct line_segments* line);
+
extern LOCAL_SYM float
line_segments_compute_length
(struct line_segments* lines);
diff --git a/src/s2d_primitive.c b/src/s2d_primitive.c
@@ -0,0 +1,162 @@
+/* Copyright (C) |Meso|Star> 2016 (contact@meso-star.com)
+ *
+ * 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 "s2d.h"
+#include "s2d_geometry.h"
+#include "s2d_line_segments.h"
+
+#include <rsys/float2.h>
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+res_T
+s2d_primitive_get_attrib
+ (const struct s2d_primitive* prim,
+ const enum s2d_attrib_usage usage,
+ const float s,
+ struct s2d_attrib* attrib)
+{
+ const uint32_t* ids;
+ struct geometry* geom = NULL;
+ res_T res = RES_OK;
+
+ if(!prim
+ || !attrib
+ || (usage != S2D_GEOMETRY_NORMAL && (unsigned)usage >= S2D_ATTRIBS_COUNT__)) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(prim->geom_id == S2D_INVALID_ID
+ || prim->prim_id == S2D_INVALID_ID
+ || prim->mesh__ == NULL) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ /* Unnormalized barycentric coordinates */
+ if(s < 0.f || s >= 1.f) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ geom = (struct geometry*)prim->mesh__;
+
+ /* The line segments haven't tge required attrib */
+ if(usage != S2D_GEOMETRY_NORMAL && !geom->lines->attribs[usage]) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ /* Out of bound primitive index */
+ if(prim->prim_id >= line_segments_get_nsegments(geom->lines)) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ids = line_segments_get_ids(geom->lines) + prim->prim_id * 2/*#segment ids*/;
+ attrib->usage = usage;
+
+ if(usage == S2D_POSITION || usage == S2D_GEOMETRY_NORMAL) {
+ const float* v0;
+ const float* v1;
+ const float* pos;
+
+ attrib->type = S2D_FLOAT2;
+ pos = line_segments_get_pos(geom->lines);
+ v0 = pos + ids[0] * 2;
+ v1 = pos + ids[1] * 2;
+
+ if(usage == S2D_POSITION) {
+ float tmp[2];
+ f2_mulf(attrib->value, v1, s);
+ f2_add(attrib->value, attrib->value, f2_mulf(tmp, v0, 1.f-s));
+ } else { ASSERT(usage == S2D_GEOMETRY_NORMAL);
+ const float dx = v1[0] - v0[0];
+ const float dy = v1[1] - v0[1];
+ /* Build the segment normal with respect to edge orientation.
+ * Default is clock wise */
+ if(geom->flip_contour) {
+ attrib->value[0] = -dy;
+ attrib->value[1] = dx;
+ } else {
+ attrib->value[0] = dy;
+ attrib->value[1] = -dx;
+ }
+ }
+ } else {
+ FATAL("Unimplemented attribute getter.\n");
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+s2d_primitive_sample
+ (const struct s2d_primitive* prim,
+ const float u,
+ float* s)
+{
+ if(!prim || S2D_PRIMITIVE_EQ(prim, &S2D_PRIMITIVE_NULL) || !s)
+ return RES_BAD_ARG;
+
+ if(u < 0.f || u >= 1.f)
+ return RES_BAD_ARG;
+
+ /* Only line segments primitives are currently supported. So the "u"
+ * canonical variable is directly the parametric coordinate of the sample
+ * onto the segment */
+ *s = u;
+ return RES_OK;
+}
+
+res_T
+s2d_primitive_compute_length(const s2d_primitive* prim, float* length)
+{
+ const uint32_t* ids;
+ const float* pos;
+ const float* v0, *v1;
+ float tmp[2];
+ struct geometry* geom;
+
+ if(!prim || !length || S2D_PRIMITIVE_EQ(prim, &S2D_PRIMITIVE_NULL))
+ return RES_BAD_ARG;
+
+ geom = (struct geometry*)prim->mesh__;
+ pos = line_segments_get_pos(geom->lines);
+ ids = line_segments_get_ids(geom->lines) + prim->prim_id * 2/*#segment ids*/;
+ v0 = pos + ids[0] * 2/*#coords*/;
+ v1 = pos + ids[1] * 2/*#coords*/;
+ *length = f2_len(f2_sub(tmp, v1, v0));
+ return RES_OK;
+}
+
diff --git a/src/s2d_scene.c b/src/s2d_scene.c
@@ -38,6 +38,8 @@
#include <rsys/float3.h>
#include <rsys/mem_allocator.h>
+#include <algorithm>
+
struct ray_extended : public RTCRay {
struct s2d_scene* scene;
void* data; /* User defined data */
@@ -369,6 +371,60 @@ scene_detach_shape
return RES_OK;
}
+static res_T
+scene_compute_cdf(struct s2d_scene* scn)
+{
+ struct list_node* node;
+ struct s2d_shape* shape;
+ struct geometry** pgeom;
+ struct geometry* geom;
+ size_t len;
+ float length = 0.f;
+ res_T res = RES_OK;
+ ASSERT(scn);
+
+ darray_fltui_clear(&scn->cdf);
+
+ LIST_FOR_EACH(node, &scn->shapes) {
+
+ shape = CONTAINER_OF(node, struct s2d_shape, scene_attachment);
+ pgeom = htable_geom_find(&scn->cached_geoms, &shape);
+ ASSERT(pgeom != NULL);
+ geom = *pgeom;
+
+ if(!geom->is_enabled) continue;
+
+ res = line_segments_compute_cdf(geom->lines);
+ if(res != RES_OK) goto error;
+ len = darray_float_size_get(&geom->lines->cdf);
+
+ if(len) { /* The geometry has valid segments */
+ struct fltui fltui;
+ length += darray_float_cdata_get(&geom->lines->cdf)[len - 1];
+ fltui.ui = geom->irtc;
+ fltui.flt = length;
+ res = darray_fltui_push_back(&scn->cdf, &fltui);
+ if(res != RES_OK) goto error;
+ }
+ }
+
+exit:
+ return res;
+error:
+ darray_fltui_clear(&scn->cdf);
+ goto exit;
+}
+
+static FINLINE bool
+operator < (const struct fltui& it, const float val)
+{
+ /* This operator is used by the std::lower_bound algorithm that returns an
+ * iterator to the first element that is not less than val while one expect
+ * an iterator on the first element that is not less *or equal* than val.
+ * That's why we use <= rather than < */
+ return it.flt <= val;
+}
+
static void
scene_compute_aabb(struct s2d_scene* scn)
{
@@ -419,7 +475,8 @@ scene_sync
}
if((session_mask & S2D_SAMPLE) != 0) {
- FATAL("The S2D_SAMPLE session is not implemented yet!\n");
+ res = scene_compute_cdf(scn);
+ if(res != RES_OK) goto error;
}
if((session_mask & S2D_GET_PRIMITIVE) != 0) {
FATAL("The S2D_GET_PRIMITIVE session is not implemented yet!\n");
@@ -450,6 +507,7 @@ scene_release(ref_T* ref)
if(scn->rtc_scn) rtcDeleteScene(scn->rtc_scn);
htable_geom_release(&scn->cached_geoms);
darray_geom_release(&scn->embree2geoms);
+ darray_fltui_release(&scn->cdf);
MEM_RM(dev->allocator, scn);
S2D(device_ref_put(dev));
}
@@ -484,6 +542,7 @@ s2d_scene_create(struct s2d_device* dev, struct s2d_scene** out_scn)
list_init(&scn->shapes);
htable_geom_init(dev->allocator, &scn->cached_geoms);
darray_geom_init(dev->allocator, &scn->embree2geoms);
+ darray_fltui_init(dev->allocator, &scn->cdf);
ref_init(&scn->ref);
S2D(device_ref_get(dev));
scn->dev = dev;
@@ -678,3 +737,77 @@ s2d_scene_trace_ray
return RES_OK;
}
+res_T
+s2d_scene_sample
+ (struct s2d_scene* scn,
+ const float u,
+ const float v,
+ struct s2d_primitive* primitive,
+ float* s)
+{
+ struct geometry* geom;
+ const struct fltui* fltui_begin, *fltui_end, *fltui_found;
+ const float* flt_begin, *flt_end, *flt_found;
+ size_t igeom;
+ float f;
+ res_T res = RES_OK;
+
+ if(!scn || !primitive || !s) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(u < 0.f || u >= 1.f || v < 0.f || v >= 1.f) {
+ log_error(scn->dev,
+ "%s: The submitted numbers are not canonical, i.e. they ar not in [0, 1[.\n",
+ __FUNCTION__);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ if((scn->session_mask & S2D_SAMPLE) == 0) {
+ log_error(scn->dev,
+ "%s: A S2D_SAMPLE session should be active onto the submitted scene.\n",
+ __FUNCTION__);
+ res = RES_BAD_OP;
+ goto error;
+ }
+
+ /* Find the sampled geometry */
+ if(darray_fltui_size_get(&scn->cdf) == 0) { /* No geometry to sample */
+ *primitive = S2D_PRIMITIVE_NULL;
+ goto exit;
+ } else if(darray_fltui_size_get(&scn->cdf) == 1) {
+ igeom = darray_fltui_cdata_get(&scn->cdf)[0].ui;
+ f = u * darray_fltui_cdata_get(&scn->cdf)[0].flt;/* Map u to the CDF bounds */
+ } else {
+ fltui_begin = darray_fltui_cdata_get(&scn->cdf);
+ fltui_end = fltui_begin + darray_fltui_size_get(&scn->cdf);
+ f = u * fltui_end[-1].flt; /* Map u to the CDF bounds */
+ fltui_found = std::lower_bound(fltui_begin, fltui_end, f);
+ ASSERT(fltui_found != fltui_end);
+ igeom = fltui_found->ui;
+
+ if(fltui_found != fltui_begin) /* Transform u to the geometry CDF bounds */
+ f -= fltui_found[-1].flt;
+ }
+ geom = darray_geom_data_get(&scn->embree2geoms)[igeom];
+ ASSERT(geom);
+
+ /* Find the sampled segment */
+ flt_begin = darray_float_cdata_get(&geom->lines->cdf);
+ flt_end = flt_begin + darray_float_size_get(&geom->lines->cdf);
+ flt_found = std::lower_bound(flt_begin, flt_end, f);
+ ASSERT(flt_found != flt_end);
+
+ primitive->mesh__ = geom;
+ primitive->geom_id = geom->name;
+ primitive->prim_id = (unsigned)(flt_found - flt_begin);
+ primitive->scene_prim_id = 0/*TODO*/;
+ S2D(primitive_sample(primitive, v, s));
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
diff --git a/src/s2d_scene_c.h b/src/s2d_scene_c.h
@@ -59,10 +59,17 @@ geom_ptr_init__(struct mem_allocator* alloc, struct geometry** geom)
#define HTABLE_KEY struct s2d_shape*
#include <rsys/hash_table.h>
+/* Generate the darray_fltui dynamic array */
+struct fltui { float flt; unsigned ui; };
+#define DARRAY_NAME fltui
+#define DARRAY_DATA struct fltui
+#include <rsys/dynamic_array.h>
+
struct s2d_scene {
struct list_node shapes; /* List of attached shapes */
struct htable_geom cached_geoms; /* Cached shape geometries */
struct darray_geom embree2geoms; /* Shape geometries indexed by embree id */
+ struct darray_fltui cdf; /* Unormalized CDF */
float lower[2], upper[2]; /* AABB of the scene */
diff --git a/src/test_s2d_sample.c b/src/test_s2d_sample.c
@@ -0,0 +1,139 @@
+/* Copyright (C) |Meso|Star> 2016 (contact@meso-star.com)
+ *
+ * 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 "s2d.h"
+#include "test_s2d_utils.h"
+
+#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)
+{
+ struct mem_allocator allocator;
+ struct s2d_vertex_data vdata;
+ struct s2d_device* dev;
+ struct s2d_scene* scn;
+ struct s2d_shape* shape;
+ struct s2d_primitive prim;
+ float s;
+ unsigned box_id;
+ int i;
+ (void)argc, (void)argv;
+
+ mem_init_proxy_allocator(&allocator, &mem_default_allocator);
+
+ CHECK(s2d_device_create(NULL, &allocator, 1, &dev), RES_OK);
+ CHECK(s2d_scene_create(dev, &scn), RES_OK);
+ CHECK(s2d_shape_create_line_segments(dev, &shape), RES_OK);
+
+ CHECK(s2d_shape_get_id(shape, &box_id), RES_OK);
+ CHECK(s2d_scene_attach_shape(scn, shape), RES_OK);
+ CHECK(s2d_scene_begin_session(scn, S2D_SAMPLE), RES_OK);
+
+ CHECK(s2d_scene_sample(NULL, 1, 1, NULL, NULL), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(scn, 1, 1, NULL, NULL), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(NULL, 0, 1, NULL, NULL), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(scn, 0, 1, NULL, NULL), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(NULL, 1, 0, NULL, NULL), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(scn, 1, 0, NULL, NULL), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(NULL, 0, 0, NULL, NULL), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(scn, 0, 0, NULL, NULL), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(NULL, 1, 1, &prim, NULL), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(scn, 1, 1, &prim, NULL), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(NULL, 0, 1, &prim, NULL), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(scn, 0, 1, &prim, NULL), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(NULL, 1, 0, &prim, NULL), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(scn, 1, 0, &prim, NULL), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(NULL, 0, 0, &prim, NULL), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(scn, 0, 0, &prim, NULL), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(NULL, 1, 1, NULL, &s), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(scn, 1, 1, NULL, &s), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(NULL, 0, 1, NULL,&s), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(scn, 0, 1, NULL, &s), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(NULL, 1, 0, NULL, &s), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(scn, 1, 0, NULL, &s), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(NULL, 0, 0, NULL, &s), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(scn, 0, 0, NULL, &s), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(NULL, 1, 1, &prim, &s), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(scn, 1, 1, &prim, &s), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(NULL, 0, 1, &prim, &s), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(scn, 0, 1, &prim, &s), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(NULL, 1, 0, &prim, &s), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(scn, 1, 0, &prim, &s), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(NULL, 0, 0, &prim, &s), RES_BAD_ARG);
+ CHECK(s2d_scene_sample(scn, 0, 0, &prim, &s), RES_OK);
+ CHECK(S2D_PRIMITIVE_EQ(&prim, &S2D_PRIMITIVE_NULL), 1);
+
+ vdata.usage = S2D_POSITION;
+ vdata.type = S2D_FLOAT2;
+ vdata.get = box_get_position;
+ CHECK(s2d_line_segments_setup_indexed_vertices
+ (shape, box_nsegs, box_get_ids, box_nverts, &vdata, 1, (void*)&box_desc),
+ RES_OK);
+
+ CHECK(s2d_scene_sample(scn, 0, 0, &prim, &s), RES_OK);
+ CHECK(S2D_PRIMITIVE_EQ(&prim, &S2D_PRIMITIVE_NULL), 1);
+ CHECK(s2d_scene_end_session(scn), RES_OK);
+
+ CHECK(s2d_scene_begin_session(scn, S2D_SAMPLE), RES_OK);
+ CHECK(s2d_scene_sample(scn, 0, 0, &prim, &s), RES_OK);
+ CHECK(S2D_PRIMITIVE_EQ(&prim, &S2D_PRIMITIVE_NULL), 0);
+
+ CHECK(prim.prim_id < 4, 1);
+ CHECK(prim.geom_id, box_id);
+ CHECK(s, 0);
+
+ FOR_EACH(i, 0, NSAMPS) {
+ const float u = rand_canonic();
+ const float v = rand_canonic();
+ CHECK(s2d_scene_sample(scn, u, v, &prim, &s), RES_OK);
+ CHECK(S2D_PRIMITIVE_EQ(&prim, &S2D_PRIMITIVE_NULL), 0);
+ CHECK(prim.prim_id < 4, 1);
+ CHECK(prim.geom_id, box_id);
+ }
+
+ CHECK(s2d_scene_end_session(scn), RES_OK);
+
+ CHECK(s2d_device_ref_put(dev), RES_OK);
+ CHECK(s2d_scene_ref_put(scn), RES_OK);
+ CHECK(s2d_shape_ref_put(shape), RES_OK);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHECK(mem_allocated_size(), 0);
+ return 0;
+}
+