star-3d

Surface structuring for efficient 3D geometric queries
git clone git://git.meso-star.fr/star-3d.git
Log | Files | Refs | README | LICENSE

test_s3d_trace_ray.c (18109B)


      1 /* Copyright (C) 2015-2023 |Méso|Star> (contact@meso-star.com)
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 3 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     15 
     16 #define _POSIX_C_SOURCE 200112L /* exp2f, fabsf */
     17 
     18 #include "s3d.h"
     19 #include "test_s3d_camera.h"
     20 #include "test_s3d_cbox.h"
     21 #include "test_s3d_utils.h"
     22 
     23 #include <rsys/float2.h>
     24 #include <rsys/float3.h>
     25 #include <rsys/image.h>
     26 
     27 #include <string.h>
     28 
     29 #define IMG_WIDTH 640
     30 #define IMG_HEIGHT 480
     31 
     32 struct ray_data {
     33   float ray_org[3];
     34   float ray_dir[3];
     35   float ray_range[2];
     36 };
     37 
     38 static int
     39 filter_func
     40   (const struct s3d_hit* hit,
     41    const float pos[3],
     42    const float dir[3],
     43    const float range[2],
     44    void* ray_data,
     45    void* filter_data)
     46 {
     47   struct ray_data* data = ray_data;
     48   CHK(hit != NULL);
     49   CHK(pos != NULL);
     50   CHK(dir != NULL);
     51   CHK(range != NULL);
     52   CHK(ray_data != NULL);
     53   CHK((uintptr_t)filter_data == 0xDECAFBAD);
     54   CHK(S3D_HIT_NONE(hit) == 0);
     55   CHK(f3_eq(pos, data->ray_org));
     56   CHK(f3_eq(dir, data->ray_dir));
     57   CHK(f2_eq(range, data->ray_range));
     58   return hit->prim.prim_id % 2 == 0;
     59 }
     60 
     61 static void
     62 triangle_get_ids(const unsigned itri, unsigned ids[3], void* ctx)
     63 {
     64   (void)ctx;
     65   CHK(itri == 0);
     66   CHK(ids);
     67   ids[0] = 0;
     68   ids[1] = 1;
     69   ids[2] = 2;
     70 }
     71 
     72 static void
     73 triangle_get_pos(const unsigned ivert, float pos[3], void* ctx)
     74 {
     75   float* vertices = ctx;
     76   CHK(ctx);
     77   CHK(ivert < 3);
     78   CHK(pos);
     79   switch (ivert) { /* Setup a random triangle */
     80   case 0: f3_set(pos, vertices + 0); break;
     81   case 1: f3_set(pos, vertices + 3); break;
     82   case 2: f3_set(pos, vertices + 6); break;
     83   default: FATAL("Unreachable code\n"); break;
     84   }
     85 }
     86 
     87 int
     88 main(int argc, char** argv)
     89 {
     90   struct mem_allocator allocator;
     91   struct image img;
     92   struct s3d_device* dev;
     93   struct s3d_hit hit;
     94   struct s3d_scene* scn;
     95   struct s3d_scene* scn2;
     96   struct s3d_scene_view* scnview;
     97   struct s3d_shape* inst;
     98   struct s3d_shape* walls;
     99   struct s3d_shape* walls_copy;
    100   struct s3d_shape* tall_block;
    101   struct s3d_shape* short_block;
    102   struct s3d_vertex_data attribs;
    103   struct s3d_primitive prims[30];
    104   struct camera cam;
    105   struct cbox_desc desc;
    106   unsigned ntris, nverts;
    107   size_t nprims;
    108   size_t ix, iy;
    109   float transform[12];
    110   float vec[3];
    111   float lower[3], upper[3];
    112   float pos[3], tgt[3], up[3];
    113   float org[3] = { 0.f, 0.f, 0.f };
    114   float dir[3] = { 0.f, 1.f, 0.f };
    115   float range[2] = { 0.f, FLT_MAX };
    116   unsigned inst_id;
    117   unsigned walls_id;
    118   unsigned tall_block_id;
    119   unsigned short_block_id;
    120   size_t a, i;
    121   char filter = 0;
    122 
    123   mem_init_proxy_allocator(&allocator, &mem_default_allocator);
    124 
    125   if(argc > 1 && !strcmp(argv[1], "filter")) {
    126     filter = 1;
    127   }
    128 
    129   image_init(&allocator, &img);
    130   CHK(image_setup
    131     (&img, IMG_WIDTH, IMG_HEIGHT, IMG_WIDTH*3, IMAGE_RGB8, NULL) == RES_OK);
    132 
    133   CHK(s3d_device_create(NULL, &allocator, 0, &dev) == RES_OK);
    134   CHK(s3d_scene_create(dev, &scn) == RES_OK);
    135 
    136   /* Trace ray in empty scene */
    137   CHK(s3d_scene_view_create(scn, S3D_TRACE, &scnview) == RES_OK);
    138   CHK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit) == RES_OK);
    139   CHK(S3D_HIT_NONE(&hit));
    140   CHK(s3d_scene_view_ref_put(scnview) == RES_OK);
    141 
    142   attribs.usage = S3D_POSITION;
    143   attribs.type = S3D_FLOAT3;
    144   attribs.get = cbox_get_position;
    145 
    146   ntris = cbox_walls_ntris;
    147   nverts = cbox_walls_nverts;
    148   desc.vertices = cbox_walls;
    149   desc.indices = cbox_walls_ids;
    150   CHK(s3d_shape_create_mesh(dev, &walls) == RES_OK);
    151   CHK(s3d_mesh_setup_indexed_vertices
    152     (walls, ntris, cbox_get_ids, nverts, &attribs, 1, &desc) == RES_OK);
    153   CHK(s3d_scene_attach_shape(scn, walls) == RES_OK);
    154   CHK(s3d_shape_ref_put(walls) == RES_OK);
    155 
    156   CHK(s3d_scene_view_create(scn, S3D_TRACE, &scnview) == RES_OK);
    157   CHK(s3d_scene_view_trace_ray(NULL, NULL, NULL, NULL, NULL, NULL) == RES_BAD_ARG);
    158   CHK(s3d_scene_view_trace_ray(scnview, NULL, NULL, NULL, NULL, NULL) == RES_BAD_ARG);
    159   CHK(s3d_scene_view_trace_ray(NULL, org, NULL, NULL, NULL, NULL) == RES_BAD_ARG);
    160   CHK(s3d_scene_view_trace_ray(scnview, org, NULL, NULL, NULL, NULL) == RES_BAD_ARG);
    161   CHK(s3d_scene_view_trace_ray(NULL, NULL, dir, NULL, NULL, NULL) == RES_BAD_ARG);
    162   CHK(s3d_scene_view_trace_ray(scnview, NULL, dir, NULL, NULL, NULL) == RES_BAD_ARG);
    163   CHK(s3d_scene_view_trace_ray(NULL, org, dir, NULL, NULL, NULL) == RES_BAD_ARG);
    164   CHK(s3d_scene_view_trace_ray(scnview, org, dir, NULL, NULL, NULL) == RES_BAD_ARG);
    165   CHK(s3d_scene_view_trace_ray(NULL, NULL, NULL, range, NULL, NULL) == RES_BAD_ARG);
    166   CHK(s3d_scene_view_trace_ray(scnview, NULL, NULL, range, NULL, NULL) == RES_BAD_ARG);
    167   CHK(s3d_scene_view_trace_ray(NULL, org, NULL, range, NULL, NULL) == RES_BAD_ARG);
    168   CHK(s3d_scene_view_trace_ray(scnview, org, NULL, range, NULL, NULL) == RES_BAD_ARG);
    169   CHK(s3d_scene_view_trace_ray(NULL, NULL, dir, range, NULL, NULL) == RES_BAD_ARG);
    170   CHK(s3d_scene_view_trace_ray(scnview, NULL, dir, range, NULL, NULL) == RES_BAD_ARG);
    171   CHK(s3d_scene_view_trace_ray(NULL, org, dir, range, NULL, NULL) == RES_BAD_ARG);
    172   CHK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, NULL) == RES_BAD_ARG);
    173   CHK(s3d_scene_view_trace_ray(NULL, NULL, NULL, NULL, NULL, &hit) == RES_BAD_ARG);
    174   CHK(s3d_scene_view_trace_ray(scnview, NULL, NULL, NULL, NULL, &hit) == RES_BAD_ARG);
    175   CHK(s3d_scene_view_trace_ray(NULL, org, NULL, NULL, NULL, &hit) == RES_BAD_ARG);
    176   CHK(s3d_scene_view_trace_ray(scnview, org, NULL, NULL, NULL, &hit) == RES_BAD_ARG);
    177   CHK(s3d_scene_view_trace_ray(NULL, NULL, dir, NULL, NULL, &hit) == RES_BAD_ARG);
    178   CHK(s3d_scene_view_trace_ray(scnview, NULL, dir, NULL, NULL, &hit) == RES_BAD_ARG);
    179   CHK(s3d_scene_view_trace_ray(NULL, org, dir, NULL, NULL, &hit) == RES_BAD_ARG);
    180   CHK(s3d_scene_view_trace_ray(scnview, org, dir, NULL, NULL, &hit) == RES_BAD_ARG);
    181   CHK(s3d_scene_view_trace_ray(NULL, NULL, NULL, range, NULL, &hit) == RES_BAD_ARG);
    182   CHK(s3d_scene_view_trace_ray(scnview, NULL, NULL, range, NULL, &hit) == RES_BAD_ARG);
    183   CHK(s3d_scene_view_trace_ray(NULL, org, NULL, range, NULL, &hit) == RES_BAD_ARG);
    184   CHK(s3d_scene_view_trace_ray(scnview, org, NULL, range, NULL, &hit) == RES_BAD_ARG);
    185   CHK(s3d_scene_view_trace_ray(NULL, NULL, dir, range, NULL, &hit) == RES_BAD_ARG);
    186   CHK(s3d_scene_view_trace_ray(scnview, NULL, dir, range, NULL, &hit) == RES_BAD_ARG);
    187   CHK(s3d_scene_view_trace_ray(NULL, org, dir, range, NULL, &hit) == RES_BAD_ARG);
    188   CHK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit) == RES_OK);
    189   CHK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit) == RES_OK);
    190   f3(dir, 1.f, 1.f, 1.f);
    191   CHK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit) == RES_BAD_ARG);
    192   CHK(s3d_scene_view_ref_put(scnview) == RES_OK);
    193 
    194   f3(dir, 0.f, 1.f, 0.f);
    195   CHK(s3d_scene_clear(scn) == RES_OK);
    196 
    197   /* Update the inst with the CBox tall block mesh */
    198   ntris = cbox_block_ntris;
    199   nverts = cbox_block_nverts;
    200   desc.vertices = cbox_short_block;
    201   desc.indices = cbox_block_ids;
    202   CHK(s3d_shape_create_mesh(dev, &tall_block) == RES_OK);
    203   CHK(s3d_shape_get_id(tall_block, &tall_block_id) == RES_OK);
    204   CHK(s3d_mesh_setup_indexed_vertices
    205     (tall_block, ntris, cbox_get_ids, nverts, &attribs, 1, &desc) == RES_OK);
    206   CHK(s3d_scene_attach_shape(scn, tall_block) == RES_OK);
    207 
    208   /* Update the inst vertices */
    209   desc.vertices = cbox_tall_block;
    210   CHK(s3d_mesh_setup_indexed_vertices
    211     (tall_block, ntris, S3D_KEEP, nverts, &attribs, 1, &desc) == RES_OK);
    212 
    213   /* Create a the CBox short block inst */
    214   desc.vertices = cbox_short_block;
    215   CHK(s3d_shape_create_mesh(dev, &short_block) == RES_OK);
    216   CHK(s3d_shape_get_id(short_block, &short_block_id) == RES_OK);
    217   CHK(s3d_mesh_setup_indexed_vertices
    218     (short_block, ntris, cbox_get_ids, nverts, &attribs, 1, &desc) == RES_OK);
    219   CHK(s3d_scene_attach_shape(scn, short_block) == RES_OK);
    220 
    221   /* Instantiate the scene */
    222   CHK(s3d_scene_instantiate(scn, &inst) == RES_OK);
    223   CHK(s3d_scene_view_create(scn, S3D_SAMPLE, &scnview) == RES_OK);
    224   CHK(s3d_scene_view_primitives_count(scnview, &nprims) == RES_OK);
    225   CHK(nprims == 20);
    226   CHK(s3d_scene_view_ref_put(scnview) == RES_OK);
    227   CHK(s3d_shape_get_id(inst, &inst_id) == RES_OK);
    228 
    229   /* Create the CBox walls */
    230   desc.indices = cbox_walls_ids;
    231   desc.vertices = cbox_walls;
    232   nverts = cbox_walls_nverts;
    233   ntris = cbox_walls_ntris;
    234   CHK(s3d_shape_create_mesh(dev, &walls) == RES_OK);
    235   CHK(s3d_shape_get_id(walls, &walls_id) == RES_OK);
    236   CHK(s3d_mesh_setup_indexed_vertices
    237     (walls, ntris, cbox_get_ids, nverts, &attribs, 1, &desc) == RES_OK);
    238   CHK(s3d_scene_attach_shape(scn, walls) == RES_OK);
    239 
    240   /* Check that the ids are all different */
    241   CHK(walls_id != short_block_id);
    242   CHK(walls_id != tall_block_id);
    243   CHK(short_block_id != tall_block_id);
    244 
    245   /* Attach the CBox instance to a scene */
    246   CHK(s3d_scene_create(dev, &scn2) == RES_OK);
    247   f3(org, -100.f, 0.f, -2.f);
    248   CHK(s3d_scene_attach_shape(scn2, inst) == RES_OK);
    249   CHK(s3d_instance_set_position(inst, org) == RES_OK);
    250 
    251   CHK(s3d_shape_enable(inst, 0) == RES_OK);
    252   CHK(s3d_scene_view_create(scn2, S3D_TRACE|S3D_SAMPLE, &scnview) == RES_OK);
    253   CHK(s3d_scene_view_ref_put(scnview) == RES_OK);
    254 
    255   CHK(s3d_shape_enable(inst, 1) == RES_OK);
    256   CHK(s3d_scene_view_create(scn2, S3D_TRACE, &scnview) == RES_OK);
    257   CHK(s3d_scene_view_ref_put(scnview) == RES_OK);
    258 
    259   CHK(s3d_shape_create_mesh(dev, &walls_copy) == RES_OK);
    260   CHK(s3d_mesh_copy(walls, walls_copy) == RES_OK);
    261   CHK(s3d_shape_ref_put(walls) == RES_OK);
    262   CHK(s3d_shape_get_id(walls_copy, &walls_id) == RES_OK);
    263   if(filter) {
    264     CHK(s3d_mesh_set_hit_filter_function
    265       (walls_copy, filter_func, (void*)(uintptr_t)0xDECAFBAD) == RES_OK);
    266   }
    267 
    268   CHK(s3d_scene_clear(scn) == RES_OK);
    269   CHK(s3d_scene_attach_shape(scn, walls_copy) == RES_OK);
    270   CHK(s3d_scene_attach_shape(scn, short_block) == RES_OK);
    271   CHK(s3d_scene_attach_shape(scn, tall_block) == RES_OK);
    272 
    273   CHK(s3d_scene_view_create(scn2, S3D_TRACE|S3D_GET_PRIMITIVE, &scnview) == RES_OK);
    274   CHK(s3d_scene_view_primitives_count(scnview, &nprims) == RES_OK);
    275   CHK(nprims == 30);
    276 
    277   CHK(s3d_scene_view_get_aabb(scnview, lower, upper) == RES_OK);
    278   CHK(eq_epsf(lower[0], -100.f, 1.e-6f) == 1);
    279   CHK(eq_epsf(lower[1], 0.f, 1.e-6f) == 1);
    280   CHK(eq_epsf(lower[2], -2.f, 1.e-6f) == 1);
    281   CHK(eq_epsf(upper[0], 452.f, 1.e-6f) == 1);
    282   CHK(eq_epsf(upper[1], 559.f, 1.e-6f) == 1);
    283   CHK(eq_epsf(upper[2], 546.f, 1.e-6f) == 1);
    284 
    285   FOR_EACH(i, 0, nprims) {
    286     size_t j;
    287     CHK(s3d_scene_view_get_primitive(scnview, (unsigned)i, prims + i) == RES_OK);
    288     CHK(S3D_PRIMITIVE_EQ(prims + i, &S3D_PRIMITIVE_NULL) == 0);
    289     FOR_EACH(j, 0, i) {
    290       CHK(S3D_PRIMITIVE_EQ(prims + i, prims + j) == 0);
    291     }
    292 
    293     CHK(s3d_primitive_get_transform(prims + i, transform) == RES_OK);
    294     CHK(f3_eq(transform + 0, f3(vec, 1.f, 0.f, 0.f)) == 1);
    295     CHK(f3_eq(transform + 3, f3(vec, 0.f, 1.f, 0.f)) == 1);
    296     CHK(f3_eq(transform + 6, f3(vec, 0.f, 0.f, 1.f)) == 1);
    297     CHK(f3_eq(transform + 9, f3(vec, -100.f, 0.f, -2.f)) == 1);
    298   }
    299 
    300   f3(pos, 178.f, -1000.f, 273.f);
    301   f3(tgt, 178.f, 0.f, 273.f);
    302   f3(up, 0.f, 0.f, 1.f);
    303   camera_init(&cam, pos, tgt, up, (float)PI*0.25f,
    304     (float)IMG_WIDTH/(float)IMG_HEIGHT);
    305   FOR_EACH(iy, 0, IMG_HEIGHT) {
    306     float pixel[2];
    307 
    308     pixel[1] = (float)iy/(float)IMG_HEIGHT;
    309     FOR_EACH(ix, 0, IMG_WIDTH) {
    310       struct ray_data ray_data;
    311       const size_t ipix = (iy*IMG_WIDTH + ix) * 3/*RGB*/;
    312 
    313       pixel[0] = (float)ix/(float)IMG_WIDTH;
    314       camera_ray(&cam, pixel, org, dir);
    315 
    316       f3_set(ray_data.ray_org, org);
    317       f3_set(ray_data.ray_dir, dir);
    318       f2_set(ray_data.ray_range, range);
    319       CHK(s3d_scene_view_trace_ray
    320         (scnview, org, dir, range, &ray_data, &hit) == RES_OK);
    321 
    322       if(S3D_HIT_NONE(&hit)) {
    323         ((uint8_t*)img.pixels)[ipix+0] = 0;
    324         ((uint8_t*)img.pixels)[ipix+1] = 0;
    325         ((uint8_t*)img.pixels)[ipix+2] = 0;
    326       } else {
    327         float N[3], len, dot, col[3] = { 1.f, 1.f, 1.f };
    328         struct s3d_attrib attr;
    329 
    330         CHK(hit.prim.inst_id == inst_id);
    331         CHK(hit.prim.geom_id == walls_id
    332          || hit.prim.geom_id == tall_block_id
    333          || hit.prim.geom_id == short_block_id);
    334         CHK(hit.prim.geom_id < 10);
    335 
    336         CHK(s3d_primitive_get_transform(&hit.prim, transform) == RES_OK);
    337         CHK(f3_eq(transform + 0, f3(vec, 1.f, 0.f, 0.f)) == 1);
    338         CHK(f3_eq(transform + 3, f3(vec, 0.f, 1.f, 0.f)) == 1);
    339         CHK(f3_eq(transform + 6, f3(vec, 0.f, 0.f, 1.f)) == 1);
    340         CHK(f3_eq(transform + 9, f3(vec, -100.f, 0.f, -2.f)) == 1);
    341 
    342         CHK(s3d_primitive_get_attrib
    343           (&hit.prim, S3D_POSITION, hit.uv, &attr) == RES_OK);
    344         CHK(attr.type == S3D_FLOAT3);
    345         CHK(attr.usage == S3D_POSITION);
    346         f3_add(pos, f3_mulf(pos, dir, hit.distance), org);
    347         CHK(f3_eq_eps
    348           (pos, attr.value, 1.e-3f/*Sic O_o!! Really bad precision!*/));
    349 
    350         len = f3_normalize(N, hit.normal);
    351         CHK(len != 0);
    352 
    353         CHK(s3d_primitive_get_attrib
    354           (&hit.prim, S3D_GEOMETRY_NORMAL, hit.uv, &attr) == RES_OK);
    355         CHK(attr.type == S3D_FLOAT3);
    356         CHK(attr.usage == S3D_GEOMETRY_NORMAL);
    357         f3_normalize(attr.value, attr.value);
    358         CHK(f3_eq_eps(attr.value, N, 1.e-6f) == 1);
    359 
    360         CHK(hit.prim.scene_prim_id >= hit.prim.prim_id);
    361         CHK(hit.prim.scene_prim_id < 30);
    362 
    363         if(hit.prim.geom_id == walls_id) {
    364           if(hit.prim.prim_id == 4 || hit.prim.prim_id == 5) {
    365             col[0] = 1.f, col[1] = 0.f, col[2] = 0.f;
    366           } else if(hit.prim.prim_id == 6 || hit.prim.prim_id == 7) {
    367             col[0] = 0.f, col[1] = 1.f, col[2] = 0.f;
    368           }
    369         }
    370 
    371         dot = f3_dot(N, dir);
    372         if(dot < 0.f)
    373           dot = f3_dot(f3_minus(N, N), dir);
    374 
    375         ((uint8_t*)img.pixels)[ipix+0] = (unsigned char)(dot * col[0] * 255.f);
    376         ((uint8_t*)img.pixels)[ipix+1] = (unsigned char)(dot * col[1] * 255.f);
    377         ((uint8_t*)img.pixels)[ipix+2] = (unsigned char)(dot * col[2] * 255.f);
    378       }
    379     }
    380   }
    381   CHK(s3d_scene_view_ref_put(scnview) == RES_OK);
    382 
    383   CHK(image_write_ppm_stream(&img, 0, stdout) == RES_OK);
    384   image_release(&img);
    385 
    386   CHK(s3d_shape_ref_put(inst) == RES_OK);
    387   CHK(s3d_shape_ref_put(short_block) == RES_OK);
    388   CHK(s3d_shape_ref_put(tall_block) == RES_OK);
    389   CHK(s3d_shape_ref_put(walls_copy) == RES_OK);
    390   CHK(s3d_scene_ref_put(scn) == RES_OK);
    391   CHK(s3d_scene_ref_put(scn2) == RES_OK);
    392 
    393   /* Check accuracy on a configuration whose analytic distance is known */
    394   FOR_EACH(a, 0, 16) {
    395     const float amplitude = exp2f((float)a);
    396     const float eps = 5e-6f * amplitude;
    397     float vertices[9];
    398     struct s3d_vertex_data vdata = S3D_VERTEX_DATA_NULL;
    399     struct s3d_scene_view* view = NULL;
    400     struct s3d_shape* msh = NULL;
    401     FOR_EACH(i, 0, 1000) {
    402       float A[3], B[3], C[3], AB[3], AC[3], N[3];
    403       int j, n;
    404 
    405       /* Randomly generate a triangle ABC */
    406       FOR_EACH(n, 0, 3)
    407         A[n] = (rand_canonic() - 0.5f) * amplitude;
    408       do {
    409         FOR_EACH(n, 0, 3) B[n] = (rand_canonic() - 0.5f) * amplitude;
    410       } while (f3_eq_eps(A, B, eps));
    411       do {
    412         FOR_EACH(n, 0, 3) C[n] = (rand_canonic() - 0.5f) * amplitude;
    413       } while (f3_eq_eps(A, C, eps) || f3_eq_eps(B, C, eps));
    414 
    415       f3_sub(AB, B, A);
    416       f3_sub(AC, C, A);
    417       f3_cross(N, AB, AC);
    418       f3_normalize(N, N);
    419 
    420       f3_set(vertices + 0, A);
    421       f3_set(vertices + 3, B);
    422       f3_set(vertices + 6, C);
    423 
    424       CHK(s3d_scene_create(dev, &scn) == RES_OK);
    425       CHK(s3d_shape_create_mesh(dev, &msh) == RES_OK);
    426       CHK(s3d_scene_attach_shape(scn, msh) == RES_OK);
    427 
    428       vdata.usage = S3D_POSITION;
    429       vdata.type = S3D_FLOAT3;
    430       vdata.get = triangle_get_pos;
    431       CHK(s3d_mesh_setup_indexed_vertices
    432       (msh, 1, triangle_get_ids, 3, &vdata, 1, vertices) == RES_OK);
    433 
    434       CHK(s3d_scene_view_create(scn, S3D_TRACE, &view) == RES_OK);
    435 
    436       FOR_EACH(j, 0, 1000) {
    437         float proj[3]; /* Projection of pos on the line */
    438         float tmp[3];
    439         float u, v, w, h;
    440 
    441         /* Randomly generate a pos not on the triangle
    442          * with know position wrt the problem: pos = A + u.AB + v.AC + k.N */
    443         u = 3 * rand_canonic() - 1;
    444         v = 3 * rand_canonic() - 1;
    445         w = 1 - u - v;
    446         h = (2 * rand_canonic() - 1) * amplitude;
    447         f3_add(proj, A, f3_add(proj, f3_mulf(proj, AB, u), f3_mulf(tmp, AC, v)));
    448         f3_add(pos, proj, f3_mulf(pos, N, h));
    449 
    450         /* Raytrace from pos towards proj */
    451         f3_mulf(dir, N, (h > 0 ? -1.f : 1.f));
    452         f3_normalize(dir, dir);
    453         CHK(s3d_scene_view_trace_ray(view, pos, dir, range, NULL, &hit)
    454           == RES_OK);
    455 
    456         /* Check result */
    457         if(u < 0 || v < 0 || w < 0) {
    458           if(!S3D_HIT_NONE(&hit))
    459             CHK(u >= -FLT_EPSILON && v >= -FLT_EPSILON && w >= -FLT_EPSILON);
    460         } else {
    461           if(S3D_HIT_NONE(&hit))
    462             CHK(u <= FLT_EPSILON || v <= FLT_EPSILON || w <= FLT_EPSILON);
    463         }
    464         if(!S3D_HIT_NONE(&hit)) {
    465           struct s3d_attrib attr;
    466           float d;
    467           CHK(eq_epsf(hit.distance, fabsf(h), eps));
    468           CHK(s3d_primitive_get_attrib(&hit.prim, S3D_POSITION, hit.uv, &attr)
    469             == RES_OK);
    470           /* Intersection-point's position is less accurate than hit distance */
    471           d = f3_len(f3_sub(tmp, attr.value, proj));
    472           CHK(d <= 10 * eps);
    473         }
    474       }
    475 
    476       CHK(s3d_shape_ref_put(msh) == RES_OK);
    477       CHK(s3d_scene_view_ref_put(view) == RES_OK);
    478       CHK(s3d_scene_ref_put(scn) == RES_OK);
    479     }
    480   }
    481 
    482   CHK(s3d_device_ref_put(dev) == RES_OK);
    483 
    484   check_memory_allocator(&allocator);
    485   mem_shutdown_proxy_allocator(&allocator);
    486   CHK(mem_allocated_size() == 0);
    487 
    488   return 0;
    489 }
    490