star-3d

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

test_s3d_trace_ray.c (18136B)


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