star-phor

Radiative transfer solver for photoreactors.
git clone https://www.edstar.cnrs.fr/git/star-phor.git
Log | Files | Refs | README | LICENSE

sphor_config.c (20224B)


      1 /* Copyright (C) 2024-2026 Centre National de la Recherche Scientifique
      2  * Copyright (C) 2024-2026 Clermont Auvergne INP
      3  * Copyright (C) 2024-2026 INSA Lyon
      4  * Copyright (C) 2024-2026 Institut Mines Télécom Albi-Carmaux
      5  * Copyright (C) 2024-2026 Institut National Polytechnique de Toulouse
      6  * Copyright (C) 2024-2026 |Méso|Star> (contact@meso-star.com)
      7  * Copyright (C) 2024-2026 PhotonLyX (info@photonlyx.com)
      8  * Copyright (C) 2024-2026 Université de Lorraine
      9  * Copyright (C) 2024-2026 Université Paul Sabatier
     10  * Copyright (C) 2024-2026 Université Toulouse - Jean Jaurès
     11  *
     12  * This program is free software: you can redistribute it and/or modify
     13  * it under the terms of the GNU General Public License as published by
     14  * the Free Software Foundation, either version 3 of the License, or
     15  * (at your option) any later version.
     16  *
     17  * This program is distributed in the hope that it will be useful,
     18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     20  * GNU General Public License for more details.
     21  *
     22  * You should have received a copy of the GNU General Public License
     23  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     24 
     25 #include "sphor.h"
     26 #include "sphor_c.h"
     27 #include "sphor_config.h"
     28 #include "sphor_interface.h"
     29 #include "sphor_sources.h"
     30 
     31 #include <rsys/double3.h>
     32 #include <rsys/rsys.h>
     33 #include <star/sphin.h>
     34 #include <star/s3d.h>
     35 #include <star/suniq.h>
     36 
     37 struct s3d_scene_view;
     38 struct suniq;
     39 
     40 const double UV_EPS = 1e-6;
     41 
     42 /*******************************************************************************
     43  * Helper functions
     44  ******************************************************************************/
     45 /* Compare the vector "a" to the vector "b". It returns an integer indicating
     46  * the result of the comparison, as follows:
     47  *  - 0, if the "a" and "b" are equal;
     48  *  - a negative value if "a" is less than "b";
     49  *  - a positive value if "a" is greater than "b". */
     50 static INLINE int
     51 vec3_cmp
     52   (const double a[3],
     53    const double b[3])
     54 {
     55   int i;
     56   ASSERT(NULL != a);
     57   ASSERT(NULL != b);
     58 
     59   FOR_EACH(i, 0, 3) {
     60          if (a[i] < b[i]) { return -1; }
     61     else if (a[i] > b[i]) { return +1; }
     62   }
     63   return 0;
     64 }
     65 
     66 static int
     67 are_triangles_reversed
     68   (const struct suniq_triangle* tri0,
     69    const struct suniq_triangle* tri1)
     70 {
     71   int i = 0;
     72 
     73   ASSERT(NULL != tri0);
     74   ASSERT(NULL != tri1);
     75 
     76   FOR_EACH(i, 0, 3) {
     77     if (0 == vec3_cmp(tri0->vertices[0], tri1->vertices[i])) { break; }
     78   }
     79   ASSERT(i<3);
     80   /* Check the order of the next vertex:
     81    * If tri0->v1 matches the next vertex in tri1 (cyclically),
     82    * the triangles have the same orientation (not reversed).
     83    * Otherwise, the vertex order has been reversed. */
     84   if (0 == vec3_cmp(tri0->vertices[1], tri1->vertices[(i+1)%3])) {
     85     return 0; /* Same orientation */
     86   } else {
     87     ASSERT(0 == vec3_cmp(tri0->vertices[1], tri1->vertices[(i+2)%3]));
     88     return 1; /* Reversed orientation */
     89   }
     90 }
     91 
     92 static res_T
     93 register_interface
     94   (struct sphor* sphor,
     95    const size_t id,
     96    struct interface** interface)
     97 {
     98   res_T res = RES_OK;
     99 
    100   ASSERT(NULL != sphor);
    101   ASSERT(NULL != interface);
    102   ASSERT(id <= darray_interface_size_get(&sphor->interfaces));
    103 
    104   if (id == darray_interface_size_get(&sphor->interfaces)) {
    105     res = darray_interface_push_back(&sphor->interfaces, &INTERFACE_NULL);
    106     if (RES_OK != res) { return RES_BAD_ARG; }
    107   }
    108   *interface = darray_interface_data_get(&sphor->interfaces) + id;
    109 
    110   return res;
    111 }
    112 
    113 /*******************************************************************************
    114  * Volume setup
    115  ******************************************************************************/
    116 static res_T
    117 setup_interface_volume_geometry_triangle
    118   (struct sphor* sphor,
    119    struct suniq* suniq,
    120    const struct sphin_geometry_descriptor* geom_desc,
    121    const size_t ivolume,
    122    const size_t itri)
    123 {
    124   size_t itri_suniq = 0; /* Triangle index used internally by suniq */
    125   struct suniq_triangle tri = SUNIQ_TRIANGLE_NULL;
    126   struct suniq_triangle tri_suniq = SUNIQ_TRIANGLE_NULL;
    127   struct interface* interface = NULL;
    128   enum sphin_side side = SPHIN_SIDE_NONE__;
    129   int flip = 0;
    130   res_T res = RES_OK;
    131 
    132   ASSERT(NULL != sphor);
    133   ASSERT(NULL != suniq);
    134   ASSERT(NULL != geom_desc);
    135 
    136   setup_triangle(geom_desc, itri, &tri);
    137 
    138   res = suniq_register_triangle(suniq, &tri, &itri_suniq);
    139   if (RES_OK != res) { goto error; }
    140 
    141   /* Retrieve the registered triangle and its properties */
    142   res = suniq_get_triangle(suniq, itri_suniq, &tri_suniq);
    143   if (RES_OK != res) { goto error; }
    144 
    145   res = register_interface(sphor, itri_suniq, &interface);
    146   if (RES_OK != res) { goto error; }
    147 
    148   /* Determine the side to append the surface based on whether the geometry is
    149    * flipped and knowing the input side using the xor (^) operator.
    150    * The value of the is_back variable is given by the truth table:
    151    *
    152    *               +-----------+
    153    *               |   flip    |
    154    *               +-----+-----+
    155    *               |  0  |  1  |
    156    *  +------+-----+-----+-----+
    157    *  | side |  0  |  0  |  1  |
    158    *  |  ==  |-----+-----+-----+
    159    *  | back |  1  |  1  |  0  |
    160    *  +------+-----+-----+-----+   */
    161 
    162   flip = are_triangles_reversed(&tri, &tri_suniq);
    163 
    164   side = (size_t) flip ^ geom_desc->side;
    165 
    166   if (INVALID_ID != interface->volumes[side]) {
    167     ERROR(sphor,
    168          "Redefinition of volume side in triangle from file '%s'.\n",
    169           geom_desc->filename);
    170     res = RES_BAD_ARG;
    171     goto error;
    172   }
    173   interface->volumes[side] = ivolume;
    174 
    175 exit:
    176   return res;
    177 error:
    178   goto exit;
    179 }
    180 
    181 static res_T
    182 setup_interfaces_volume_geometry
    183   (struct sphor* sphor,
    184    struct suniq* suniq,
    185    const size_t ivolume,
    186    const size_t igeom)
    187 {
    188   struct sphin_geometry* geom = NULL;
    189   struct sphin_geometry_descriptor geom_desc = SPHIN_GEOMETRY_DESCRIPTOR_NULL;
    190   struct sphin_volume* volume = NULL;
    191   size_t itri = 0;
    192   size_t n = 0;
    193   res_T res = RES_OK;
    194 
    195   ASSERT(NULL != sphor);
    196   ASSERT(NULL != suniq);
    197   ASSERT(RES_OK == sphin_config_get_volume_count(sphor->config, &n));
    198   ASSERT(ivolume < n);
    199 
    200   res = sphin_config_get_volume(sphor->config, ivolume, &volume);
    201   if (RES_OK != res) { goto error; }
    202 
    203   ASSERT(RES_OK == sphin_volume_get_geometry_count(volume, &n));
    204   ASSERT(igeom < n);
    205 
    206   res = sphin_volume_get_geometry(volume, igeom, &geom);
    207   if (RES_OK != res) { goto error; }
    208 
    209   res = sphin_geometry_get_desc(geom, &geom_desc);
    210   if (RES_OK != res) { goto error; }
    211 
    212   n = geom_desc.mesh.triangle_count;
    213   FOR_EACH(itri, 0, n) {
    214     res = setup_interface_volume_geometry_triangle
    215       (sphor, suniq, &geom_desc, ivolume, itri);
    216     if (RES_OK != res) { goto error; }
    217   }
    218 
    219 exit:
    220   return res;
    221 error:
    222   goto exit;
    223 }
    224 
    225 static res_T
    226 setup_interfaces_volume
    227   (struct sphor* sphor,
    228    struct suniq* suniq,
    229    const size_t ivolume)
    230 {
    231   struct sphin_volume* volume = NULL;
    232   size_t n = 0;
    233   size_t igeom = 0;
    234   res_T res = RES_OK;
    235 
    236   ASSERT(NULL != sphor);
    237   ASSERT(NULL != suniq);
    238   ASSERT(RES_OK == sphin_config_get_volume_count(sphor->config, &n));
    239   ASSERT(ivolume < n);
    240 
    241   res = sphin_config_get_volume(sphor->config, ivolume, &volume);
    242   if (RES_OK != res) { goto error; }
    243 
    244   res = sphin_volume_get_geometry_count(volume, &n);
    245   if (RES_OK != res) { goto error; }
    246 
    247   FOR_EACH(igeom, 0, n) {
    248     res = setup_interfaces_volume_geometry
    249       (sphor, suniq, ivolume, igeom);
    250     if (RES_OK != res) { goto error; }
    251   }
    252 exit:
    253   return res;
    254 error:
    255   goto exit;
    256 }
    257 
    258 /*******************************************************************************
    259  * Surface setup
    260  ******************************************************************************/
    261 static res_T
    262 setup_interface_surface_geometry_triangle
    263   (struct sphor* sphor,
    264    struct suniq* suniq,
    265    const struct sphin_geometry_descriptor* geom_desc,
    266    const size_t isurface,
    267    const size_t itri)
    268 {
    269   size_t itri_suniq = 0; /* Triangle index used internally by suniq */
    270   struct suniq_triangle tri = SUNIQ_TRIANGLE_NULL;
    271   struct suniq_triangle tri_suniq = SUNIQ_TRIANGLE_NULL;
    272   struct interface* interface = NULL;
    273   int flip = 0;
    274   short surf_count = 0;
    275   enum sphin_side side = SPHIN_SIDE_NONE__;
    276   res_T res = RES_OK;
    277 
    278   ASSERT(NULL != sphor);
    279   ASSERT(NULL != suniq);
    280   ASSERT(NULL != geom_desc);
    281 
    282   setup_triangle(geom_desc, itri, &tri);
    283 
    284   res = suniq_register_triangle(suniq, &tri, &itri_suniq);
    285   if (RES_OK != res) { goto error; }
    286 
    287   /* Retrieve the registered triangle and its properties */
    288   res = suniq_get_triangle(suniq, itri_suniq, &tri_suniq);
    289   if (RES_OK != res) { goto error; }
    290 
    291   res = register_interface(sphor, itri_suniq, &interface);
    292   if (RES_OK != res) { goto error; }
    293 
    294   /* Determine the side to append the surface based on whether the geometry is
    295    * flipped and knowing the input side using the xor (^) operator.
    296    * The value of the is_back variable is given by the truth table:
    297    *
    298    *               +-----------+
    299    *               |   flip    |
    300    *               +-----+-----+
    301    *               |  0  |  1  |
    302    *  +------+-----+-----+-----+
    303    *  | side |  0  |  0  |  1  |
    304    *  |  ==  |-----+-----+-----+
    305    *  | back |  1  |  1  |  0  |
    306    *  +------+-----+-----+-----+   */
    307 
    308   flip = are_triangles_reversed(&tri, &tri_suniq);
    309 
    310   side = (size_t)flip ^ geom_desc->side;
    311 
    312   surf_count = interface->surface_count[side];
    313 
    314   if (MAX_SURFACE_COUNT <= surf_count) {
    315     ERROR(sphor,
    316           "Too many surfaces on the same side of interface "
    317           "defined by triangle from file '%s'.  "
    318           "Maximum allowed: %d\n",
    319           geom_desc->filename, MAX_SURFACE_COUNT);
    320     res = RES_MEM_ERR;
    321     goto error;
    322   }
    323   interface->surfaces[side][surf_count] = isurface;
    324   interface->surface_count[side] += 1;
    325 
    326 exit:
    327   return res;
    328 error:
    329   goto exit;
    330 }
    331 
    332 static res_T
    333 setup_interfaces_surface_geometry
    334   (struct sphor* sphor,
    335    struct suniq* suniq,
    336    const size_t isurface,
    337    const size_t igeom)
    338 {
    339   struct sphin_geometry* geom = NULL;
    340   struct sphin_geometry_descriptor geom_desc = SPHIN_GEOMETRY_DESCRIPTOR_NULL;
    341   struct sphin_surface* surface = NULL;
    342   size_t itri = 0;
    343   size_t n = 0;
    344   res_T res = RES_OK;
    345 
    346   ASSERT(NULL != sphor);
    347   ASSERT(NULL != suniq);
    348   ASSERT(RES_OK == sphin_config_get_surface_count(sphor->config, &n));
    349   ASSERT(isurface < n);
    350 
    351   res = sphin_config_get_surface(sphor->config, isurface, &surface);
    352   if (RES_OK != res) { goto error; }
    353 
    354   ASSERT(RES_OK == sphin_surface_get_geometry_count(surface, &n));
    355   ASSERT(igeom < n);
    356 
    357   res = sphin_surface_get_geometry(surface, igeom, &geom);
    358   if (RES_OK != res) { goto error; }
    359 
    360   res = sphin_geometry_get_desc(geom, &geom_desc);
    361   if (RES_OK != res) { goto error; }
    362 
    363   n = geom_desc.mesh.triangle_count;
    364   FOR_EACH(itri, 0, n) {
    365     res = setup_interface_surface_geometry_triangle
    366       (sphor, suniq, &geom_desc, isurface, itri);
    367     if (RES_OK != res) { goto error; }
    368   }
    369 
    370 exit:
    371   return res;
    372 error:
    373   goto exit;
    374 }
    375 
    376 static res_T
    377 setup_interfaces_surface
    378   (struct sphor* sphor,
    379    struct suniq* suniq,
    380    const size_t isurface)
    381 {
    382   struct sphin_surface* surface = NULL;
    383   size_t n = 0;
    384   size_t igeom = 0;
    385   res_T res = RES_OK;
    386 
    387   ASSERT(NULL != sphor);
    388   ASSERT(NULL != suniq);
    389   ASSERT(RES_OK == sphin_config_get_surface_count(sphor->config, &n));
    390   ASSERT(isurface < n);
    391 
    392   res = sphin_config_get_surface(sphor->config, isurface, &surface);
    393   if (RES_OK != res) { goto error; }
    394 
    395   res = sphin_surface_get_geometry_count(surface, &n);
    396   if (RES_OK != res) { goto error; }
    397 
    398   FOR_EACH(igeom, 0, n) {
    399     res = setup_interfaces_surface_geometry
    400       (sphor, suniq, isurface, igeom);
    401     if (RES_OK != res) { goto error; }
    402   }
    403 exit:
    404   return res;
    405 error:
    406   goto exit;
    407 }
    408 
    409 static res_T
    410 setup_interfaces
    411   (struct sphor* sphor,
    412    struct suniq* suniq)
    413 {
    414   size_t id = 0;
    415   size_t volume_count = 0;
    416   size_t surface_count = 0;
    417   res_T res = RES_OK;
    418 
    419   ASSERT(NULL != sphor);
    420   ASSERT(NULL != suniq);
    421   /* Assumes that config is already loaded */
    422   ASSERT(NULL != sphor->config);
    423 
    424   res = sphin_config_get_volume_count(sphor->config, &volume_count);
    425   if (RES_OK != res) { goto error; }
    426   res = sphin_config_get_surface_count(sphor->config, &surface_count);
    427   if (RES_OK != res) { goto error; }
    428 
    429   FOR_EACH(id, 0, volume_count) {
    430     res = setup_interfaces_volume(sphor, suniq, id);
    431     if (RES_OK != res) { goto error; }
    432   }
    433 
    434   FOR_EACH(id, 0, surface_count) {
    435     res = setup_interfaces_surface(sphor, suniq, id);
    436     if (RES_OK != res) { goto error; }
    437   }
    438 
    439 exit:
    440   return res;
    441 
    442 error:
    443   goto exit;
    444 }
    445 
    446 static void
    447 get_triangle_indices
    448   (const unsigned tri_id,
    449    unsigned indices[3],
    450    void* data)
    451 {
    452   struct suniq_desc* desc = data;
    453   size_t ids[3];
    454 
    455   ASSERT(NULL != data);
    456   ASSERT(NULL != indices);
    457 
    458   SUNIQ(desc_get_triangle_indices(desc, tri_id, ids));
    459 
    460   indices[0] = (unsigned)ids[0];
    461   indices[1] = (unsigned)ids[1];
    462   indices[2] = (unsigned)ids[2];
    463 }
    464 
    465 static void
    466 get_vertex_coords
    467   (const unsigned vertex_id,
    468    float coords[3],
    469    void* data)
    470 {
    471   struct suniq_desc* desc = data;
    472   double pos[3];
    473 
    474   ASSERT(NULL != data);
    475   ASSERT(NULL != coords);
    476 
    477   SUNIQ(desc_get_vertex(desc, vertex_id, pos));
    478 
    479   coords[0] = (float)pos[0];
    480   coords[1] = (float)pos[1];
    481   coords[2] = (float)pos[2];
    482 }
    483 
    484 static res_T
    485 load_config
    486   (struct sphor* sphor,
    487    const struct sphor_create_args* args)
    488 {
    489   struct sphin* sphin = NULL;
    490   struct sphin_config* config = NULL;
    491   struct sphin_create_args sphin_args = SPHIN_CREATE_ARGS_DEFAULT;
    492   res_T res = RES_OK;
    493 
    494   ASSERT(NULL != sphor);
    495   ASSERT(NULL != args);
    496 
    497   /* Create sphin struct */
    498   sphin_args.verbose = sphor->verbose;
    499   sphin_args.logger = sphor->logger;
    500   sphin_args.allocator = sphor->allocator;
    501 
    502   res = sphin_create(&sphin_args, &sphin);
    503   if (RES_OK != res) { goto error; }
    504 
    505   /* Load config */
    506   if (NULL != args->input_filename) {
    507     res = sphin_load(sphin, args->input_filename, &config);
    508   }
    509   else {
    510     res = sphin_load_stream(sphin, stdin, "stdin", &config);
    511   }
    512   if (RES_OK != res) { goto error; }
    513 
    514 exit:
    515   if (NULL != sphin) { SPHIN(ref_put(sphin)); }
    516   sphor->config = config;
    517   return res;
    518 error:
    519   if (NULL != config) {
    520     SPHIN(config_ref_put(config));
    521     config = NULL;
    522   }
    523   goto exit;
    524 }
    525 
    526 /* Function attached to the scene view that determines whether a hit is valid.
    527  * Returns 0 if the hit is valid, 1 otherwise. */
    528 static int
    529 hit_filter_function
    530   (const struct s3d_hit* hit,
    531    const float pos[3],
    532    const float dir[3],
    533    const float range[2],
    534    void* primitive_id,
    535    void* interface)
    536 {
    537   double u = 0;
    538   double v = 0;
    539 
    540   (void)pos;
    541   (void)dir;
    542   (void)interface;
    543 
    544   size_t* prim_id = primitive_id;
    545 
    546   /* Discard hits that do not respect the range — this may happen due to
    547    * numerical precision errors in Embree. */
    548   if(hit->distance <= range[0] || hit->distance >= range[1])
    549     return 1;
    550 
    551   /* Discard auto-intersection */
    552   if (hit->prim.prim_id == *prim_id) { return 1; }
    553 
    554   /* Discard intersections of photons trapped in the edges of two primitives */
    555   u = hit->uv[0];
    556   v = hit->uv[1];
    557   if (hit->distance < UV_EPS
    558   && (u < UV_EPS || v < UV_EPS || (u + v) > 1.0 - UV_EPS)) {
    559     return 1;
    560   }
    561 
    562   return 0;
    563 }
    564 
    565 /*******************************************************************************
    566  * Local functions
    567  ******************************************************************************/
    568 res_T
    569 setup_config
    570   (struct sphor* sphor,
    571    const struct sphor_create_args* args)
    572 {
    573   struct suniq* suniq = NULL;
    574 
    575   res_T res = RES_OK;
    576 
    577   ASSERT(NULL != sphor);
    578   ASSERT(NULL != args);
    579 
    580   res = suniq_create(sphor->allocator, &suniq);
    581   if (RES_OK != res) { goto error; }
    582 
    583   /* Parse input file */
    584   res = load_config(sphor, args);
    585   if (RES_OK != res) { goto error; }
    586 
    587   /* Attach physical properties to primitives */
    588   res = setup_interfaces(sphor, suniq);
    589   if (RES_OK != res) { goto error; }
    590 
    591   /* Setup the scene views for each source to accelerate the sampling of a
    592    * position in a given source */
    593   res = setup_source_views(sphor, args, suniq);
    594   if (RES_OK != res) { goto error; }
    595 
    596   /* Compute distribution used to important sample a source with respect to
    597    * their power */
    598   res = setup_source_distrib_power(sphor, args);
    599   if (RES_OK != res) { goto error; }
    600 
    601   /* Build accelerating structure for geometrical data */
    602   res = setup_geometry_accel_struct
    603     (sphor, suniq, (S3D_TRACE | S3D_GET_PRIMITIVE), &sphor->scene_view);
    604   if (RES_OK != res) { goto error; }
    605 
    606 exit:
    607   if (NULL != suniq) { SUNIQ(ref_put(suniq)); }
    608   return res;
    609 error:
    610   goto exit;
    611 }
    612 
    613 void
    614 setup_triangle
    615   (const struct sphin_geometry_descriptor* geom_desc,
    616    const size_t itri,
    617    struct suniq_triangle* tri)
    618 {
    619   const double* coords[3] = {NULL, NULL, NULL};
    620   size_t ids[3] = {0, 0, 0};
    621 
    622   ASSERT(NULL != geom_desc);
    623   ASSERT(NULL != tri);
    624   ASSERT(itri < geom_desc->mesh.triangle_count);
    625 
    626   /* Get the indices of the triangle vertices. They are stored in a one
    627    * dimensional array. So, to retrieve the vertex indices of the i^th triangle,
    628    * one has to multiply i by 3, i.e. the number of vertex per triangle :
    629    *
    630    *           0   1   2   3   4   5   6   7   8
    631    *         +---+---+---+---+---+---+---+---+---+-----
    632    * indices |id0|id1|id2|id0|id1|id2|id0|id1|id2| ...
    633    *         +---+---+---+---+---+---+---+---+---+-----
    634    *          \__tri 0__/ \__tri 1__/ \__tri 2__/  */
    635   ids[0] = geom_desc->mesh.indices[itri*3/*#indices per triangle*/+0];
    636   ids[1] = geom_desc->mesh.indices[itri*3/*#indices per triangle*/+1];
    637   ids[2] = geom_desc->mesh.indices[itri*3/*#indices per triangle*/+2];
    638 
    639   /* Get coords for the triangle corresponding to the vertices retrieved. Like
    640    * the indices, the coords are stored in an one dimensional array.
    641    *
    642    *           0   1   2   3   4   5   6   7   8
    643    *         +---+---+---+---+---+---+---+---+---+-----
    644    * coords  | x | y | z | x | y | z | x | y | z | ...
    645    *         +---+---+---+---+---+---+---+---+---+-----
    646    *          \__vert0__/ \__vert1__/ \__vert2__/  */
    647   coords[0] = geom_desc->mesh.coords + ids[0]*3/*#coords per vertex*/;
    648   coords[1] = geom_desc->mesh.coords + ids[1]*3/*#coords per vertex*/;
    649   coords[2] = geom_desc->mesh.coords + ids[2]*3/*#coords per vertex*/;
    650 
    651   /* Set the retrieved coords to the triangle struct */
    652   d3_set(tri->vertices[0], coords[0]);
    653   d3_set(tri->vertices[1], coords[1]);
    654   d3_set(tri->vertices[2], coords[2]);
    655 }
    656 
    657 res_T
    658 setup_geometry_accel_struct
    659   (struct sphor* sphor,
    660    struct suniq* suniq,
    661    const int view_mask, /* Combination of s3d_scene_view flags */
    662    struct s3d_scene_view** out_view)
    663 {
    664   struct suniq_desc desc = SUNIQ_DESC_NULL;
    665   struct s3d_vertex_data vertex_data;
    666   struct s3d_device* device = NULL;
    667   struct s3d_scene* scene = NULL;
    668   struct s3d_scene_view* view = NULL;
    669   struct s3d_shape* shape = NULL;
    670   res_T res = RES_OK;
    671 
    672   ASSERT(NULL != sphor);
    673   ASSERT(NULL != suniq);
    674   ASSERT(NULL != out_view);
    675   ASSERT(0 != view_mask);
    676 
    677   res = suniq_get_desc(suniq, &desc);
    678   if (RES_OK != res) { goto error; }
    679 
    680   if (0 == desc.ntriangles) {
    681     ERROR(sphor,
    682       "Could not start simulation: "
    683       "surfaces and volumes should have a geometry\n");
    684     res = RES_BAD_ARG;
    685     goto error;
    686   }
    687 
    688   res = s3d_device_create
    689     (sphor->logger, sphor->allocator, sphor->verbose, &device);
    690   if (RES_OK != res) { goto error; }
    691 
    692   res = s3d_shape_create_mesh(device, &shape);
    693   if (RES_OK != res) { goto error; }
    694 
    695   vertex_data.usage = S3D_POSITION;
    696   vertex_data.type = S3D_FLOAT3;
    697   vertex_data.get = get_vertex_coords;
    698   res = s3d_mesh_setup_indexed_vertices
    699     (shape, (unsigned)desc.ntriangles, get_triangle_indices,
    700      (unsigned)desc.nvertices, &vertex_data, 1, &desc);
    701   if (RES_OK != res) { goto error; }
    702 
    703   res = s3d_scene_create(device, &scene);
    704   if (RES_OK != res) { goto error; }
    705 
    706   res = s3d_mesh_set_hit_filter_function(shape, hit_filter_function, NULL);
    707   if (RES_OK != res) { goto error; }
    708 
    709   res = s3d_scene_attach_shape(scene, shape);
    710   if (RES_OK != res) { goto error; }
    711 
    712   res = s3d_scene_view_create(scene, view_mask, &view);
    713   if (RES_OK != res) { goto error; }
    714 
    715 exit:
    716   if (NULL != device) { S3D(device_ref_put(device)); }
    717   if (NULL != shape) { S3D(shape_ref_put(shape)); }
    718   if (NULL != scene) { S3D(scene_ref_put(scene)); }
    719   *out_view = view;
    720   return res;
    721 error:
    722   if (NULL != view) {
    723     S3D(scene_view_ref_put(view));
    724     view = NULL;
    725   }
    726   goto exit;
    727 }