star-uvm

Spatial structuring of unstructured volumetric meshes
git clone git://git.meso-star.fr/star-uvm.git
Log | Files | Refs | README | LICENSE

suvm_volume.c (21975B)


      1 /* Copyright (C) 2020-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 #include "suvm.h"
     17 #include "suvm_c.h"
     18 #include "suvm_device.h"
     19 #include "suvm_volume.h"
     20 
     21 #include <rsys/cstr.h>
     22 #include <rsys/dynamic_array_double.h>
     23 #include <rsys/dynamic_array_size_t.h>
     24 #include <rsys/ref_count.h>
     25 
     26 /* Generate the dynamic array of RTCBuildPrimitive */
     27 #define DARRAY_NAME rtc_prim
     28 #define DARRAY_DATA struct RTCBuildPrimitive
     29 #define DARRAY_ALIGNMENT ALIGNOF(struct RTCBuildPrimitive)
     30 #include <rsys/dynamic_array.h>
     31 
     32 /*******************************************************************************
     33  * Helper functions
     34  ******************************************************************************/
     35 static INLINE int
     36 check_tetrahedral_mesh_args(const struct suvm_tetrahedral_mesh_args* args)
     37 {
     38   return args
     39       && args->ntetrahedra
     40       && args->nvertices
     41       && args->get_indices
     42       && args->get_position;
     43 }
     44 
     45 static INLINE void
     46 buffer_release(struct buffer* buf)
     47 {
     48   ASSERT(buf);
     49   if(buf->mem) MEM_RM(buf->allocator, buf->mem);
     50   *buf = BUFFER_NULL;
     51 }
     52 
     53 static res_T
     54 buffer_init_from_data
     55   (struct mem_allocator* mem_allocator, /* May be NULL */
     56    struct buffer* buf,
     57    const struct suvm_data* data,
     58    const size_t ndata,
     59    void* context)
     60 {
     61   struct mem_allocator* allocator = NULL;
     62   size_t idata;
     63   res_T res = RES_OK;
     64   ASSERT(buf && data && data->get && ndata);
     65 
     66   allocator = mem_allocator ? mem_allocator : &mem_default_allocator;
     67 
     68   *buf = BUFFER_NULL;
     69   if(!data->size || !IS_POW2(data->alignment)) {
     70     res = RES_BAD_ARG;
     71     goto error;
     72   }
     73   buf->elmt_size = data->size;
     74   buf->elmt_alignment = data->alignment;
     75   buf->elmt_stride = ALIGN_SIZE(data->size, data->alignment);
     76   buf->size = ndata;
     77   buf->allocator = allocator;
     78 
     79   /* Allocate the required memory */
     80   buf->mem = MEM_ALLOC_ALIGNED
     81     (buf->allocator, buf->elmt_stride*ndata, buf->elmt_alignment);
     82   if(!buf->mem) {
     83     res = RES_MEM_ERR;
     84     goto error;
     85   }
     86 
     87   /* Fill the buffer with the submitted data */
     88   FOR_EACH(idata, 0, ndata) {
     89     char* elmt = (char*)buf->mem + idata*buf->elmt_stride;
     90     data->get(idata, elmt, context);
     91 
     92     /* Clean up padding bytes to initialise them regarding their possible
     93      * hashing by the suvm_volume_compute_hash function */
     94     if(buf->elmt_stride != buf->elmt_size) {
     95       memset(elmt + buf->elmt_size, 0, buf->elmt_stride - buf->elmt_size);
     96     }
     97   }
     98 
     99 exit:
    100   return res;
    101 error:
    102   buffer_release(buf);
    103   goto exit;
    104 }
    105 
    106 static res_T
    107 setup_tetrahedral_mesh_indices
    108   (struct suvm_volume* vol, const struct suvm_tetrahedral_mesh_args* args)
    109 {
    110   size_t itetra;
    111   res_T res = RES_OK;
    112   ASSERT(vol && args);
    113 
    114   res = darray_u32_resize
    115     (&vol->indices, args->ntetrahedra*4/*#vertices per tetra*/);
    116   if(res != RES_OK) goto error;
    117 
    118   /* Locally copy the indices */
    119   FOR_EACH(itetra, 0, args->ntetrahedra) {
    120     size_t tetra[4];
    121     uint32_t* tetra_u32 = darray_u32_data_get(&vol->indices)+itetra*4;
    122     args->get_indices(itetra, tetra, args->context);
    123     ASSERT(tetra[0] < UINT32_MAX);
    124     ASSERT(tetra[1] < UINT32_MAX);
    125     ASSERT(tetra[2] < UINT32_MAX);
    126     ASSERT(tetra[3] < UINT32_MAX);
    127     tetra_u32[0] = (uint32_t)tetra[0];
    128     tetra_u32[1] = (uint32_t)tetra[1];
    129     tetra_u32[2] = (uint32_t)tetra[2];
    130     tetra_u32[3] = (uint32_t)tetra[3];
    131   }
    132 
    133 exit:
    134   return res;
    135 error:
    136   darray_u32_purge(&vol->indices);
    137   goto exit;
    138 }
    139 
    140 static res_T
    141 setup_tetrahedral_mesh_position
    142   (struct suvm_volume* vol, const struct suvm_tetrahedral_mesh_args* args)
    143 {
    144   size_t ivert;
    145   res_T res = RES_OK;
    146   ASSERT(vol && args);
    147 
    148   res = darray_float_resize
    149     (&vol->positions, args->nvertices*3/*#coords per vertex*/);
    150   if(res != RES_OK) goto error;
    151 
    152   /* Locally copy the positions */
    153   FOR_EACH(ivert, 0, args->nvertices) {
    154     double vertd[3];
    155     float* vertf = darray_float_data_get(&vol->positions)+ivert*3;
    156     args->get_position(ivert, vertd, args->context);
    157     vertf[0] = (float)vertd[0];
    158     vertf[1] = (float)vertd[1];
    159     vertf[2] = (float)vertd[2];
    160   }
    161 
    162 exit:
    163   return res;
    164 error:
    165   darray_float_purge(&vol->positions);
    166   goto exit;
    167 }
    168 
    169 static res_T
    170 setup_tetrahedral_mesh
    171   (struct suvm_volume* vol, const struct suvm_tetrahedral_mesh_args* args)
    172 {
    173   res_T res = RES_OK;
    174   ASSERT(vol && args);
    175 
    176   res = setup_tetrahedral_mesh_indices(vol, args);
    177   if(res != RES_OK) goto error;
    178   res = setup_tetrahedral_mesh_position(vol, args);
    179   if(res != RES_OK) goto error;
    180 
    181    /* Store the per tetrahedron data */
    182   if(args->tetrahedron_data.get) {
    183     res = buffer_init_from_data(vol->dev->allocator, &vol->prim_data,
    184       &args->tetrahedron_data, args->ntetrahedra, args->context);
    185     if(res != RES_OK) goto error;
    186     vol->has_prim_data = 1;
    187   }
    188 
    189   /* Store the per vertex data */
    190   if(args->vertex_data.get) {
    191     res = buffer_init_from_data(vol->dev->allocator, &vol->vert_data,
    192       &args->vertex_data, args->nvertices, args->context);
    193     if(res != RES_OK) goto error;
    194     vol->has_vert_data = 1;
    195   }
    196 
    197 exit:
    198   return res;
    199 error:
    200   darray_u32_purge(&vol->indices);
    201   darray_float_purge(&vol->positions);
    202   darray_float_purge(&vol->normals);
    203   buffer_release(&vol->prim_data);
    204   buffer_release(&vol->vert_data);
    205   goto exit;
    206 }
    207 
    208 static INLINE void*
    209 rtc_node_inner_create
    210   (RTCThreadLocalAllocator allocator, unsigned int nchildren, void* ctx)
    211 {
    212   struct node_inner* inner = NULL;
    213   ASSERT(nchildren == 2);
    214   (void)ctx, (void)nchildren;
    215   inner = rtcThreadLocalAlloc(allocator, sizeof(*inner), 16);
    216   if(!inner) return NULL;
    217   inner->node.type = NODE_INNER;
    218   return &inner->node;
    219 }
    220 
    221 static INLINE void
    222 rtc_node_inner_set_children
    223   (void* ptr, void* children[2], unsigned nchildren, void* ctx)
    224 {
    225   struct node_inner* inner = CONTAINER_OF(ptr, struct node_inner, node);
    226   struct node* node = ptr;
    227   ASSERT(node && node->type == NODE_INNER && children && nchildren == 2);
    228   (void)ctx, (void)nchildren, (void)node;
    229   inner->children[0] = children[0];
    230   inner->children[1] = children[1];
    231 }
    232 
    233 static INLINE void
    234 rtc_node_inner_set_bounds
    235   (void* ptr,
    236    const struct RTCBounds* bounds[2],
    237    unsigned nchildren,
    238    void* ctx)
    239 {
    240   struct node_inner* inner = CONTAINER_OF(ptr, struct node_inner, node);
    241   struct node* node = ptr;
    242   ASSERT(node && node->type == NODE_INNER && bounds && nchildren == 2);
    243   (void)ctx, (void)nchildren, (void)node;
    244 
    245   /* Setup the AABB of the 1st child */
    246   inner->low[0][0] = bounds[0]->lower_x;
    247   inner->low[0][1] = bounds[0]->lower_y;
    248   inner->low[0][2] = bounds[0]->lower_z;
    249   inner->upp[0][0] = bounds[0]->upper_x;
    250   inner->upp[0][1] = bounds[0]->upper_y;
    251   inner->upp[0][2] = bounds[0]->upper_z;
    252 
    253   /* Setup the AABB of the 2nd child */
    254   inner->low[1][0] = bounds[1]->lower_x;
    255   inner->low[1][1] = bounds[1]->lower_y;
    256   inner->low[1][2] = bounds[1]->lower_z;
    257   inner->upp[1][0] = bounds[1]->upper_x;
    258   inner->upp[1][1] = bounds[1]->upper_y;
    259   inner->upp[1][2] = bounds[1]->upper_z;
    260 }
    261 
    262 static INLINE void*
    263 rtc_node_leaf_create
    264   (RTCThreadLocalAllocator allocator,
    265    const struct RTCBuildPrimitive* prim,
    266    size_t nprims,
    267    void* ctx)
    268 {
    269   struct node_leaf* leaf = NULL;
    270   ASSERT(prim && nprims == 1);
    271   (void)ctx, (void)nprims;
    272 
    273   leaf = rtcThreadLocalAlloc(allocator, sizeof(*leaf), 16);
    274   if(!leaf) return NULL;
    275 
    276   leaf->low[0] = prim->lower_x;
    277   leaf->low[1] = prim->lower_y;
    278   leaf->low[2] = prim->lower_z;
    279   leaf->upp[0] = prim->upper_x;
    280   leaf->upp[1] = prim->upper_y;
    281   leaf->upp[2] = prim->upper_z;
    282   leaf->geom_id = prim->geomID;
    283   leaf->prim_id = prim->primID;
    284   leaf->node.type = NODE_LEAF;
    285   return &leaf->node;
    286 }
    287 
    288 static res_T
    289 build_bvh(struct suvm_volume* vol)
    290 {
    291   struct darray_rtc_prim rtc_prims;
    292   struct RTCBuildArguments args;
    293   size_t iprim, nprims;
    294   int rtc_prims_is_init = 0;
    295   res_T res = RES_OK;
    296   ASSERT(vol);
    297 
    298   nprims = volume_get_primitives_count(vol);
    299 
    300   /* Create the BVH */
    301   vol->bvh = rtcNewBVH(vol->dev->rtc);
    302   if(!vol->bvh) {
    303     const enum RTCError rtc_err = rtcGetDeviceError(vol->dev->rtc);
    304     log_err(vol->dev, "Could not create the BVH -- %s.\n",
    305       rtc_error_string(rtc_err));
    306     res = rtc_error_to_res_T(rtc_err);
    307     goto error;
    308   }
    309 
    310   /* Allocate the array of geometric primitives */
    311   darray_rtc_prim_init(vol->dev->allocator, &rtc_prims);
    312   rtc_prims_is_init = 1;
    313   res = darray_rtc_prim_resize(&rtc_prims, nprims);
    314   if(res != RES_OK) goto error;
    315 
    316   /* Setup the primitive array */
    317   FOR_EACH(iprim, 0, nprims) {
    318     const uint32_t* ids = darray_u32_cdata_get(&vol->indices) + iprim*4;
    319     const float* verts[4];
    320     struct RTCBuildPrimitive* prim = darray_rtc_prim_data_get(&rtc_prims)+iprim;
    321     double low[3], upp[3];
    322 
    323     ASSERT(ids[0] < volume_get_vertices_count(vol));
    324     ASSERT(ids[1] < volume_get_vertices_count(vol));
    325     ASSERT(ids[2] < volume_get_vertices_count(vol));
    326     ASSERT(ids[3] < volume_get_vertices_count(vol));
    327 
    328     /* Fetch the tetrahedron vertices */
    329     verts[0] = darray_float_cdata_get(&vol->positions) + ids[0]*3/*#coords*/;
    330     verts[1] = darray_float_cdata_get(&vol->positions) + ids[1]*3/*#coords*/;
    331     verts[2] = darray_float_cdata_get(&vol->positions) + ids[2]*3/*#coords*/;
    332     verts[3] = darray_float_cdata_get(&vol->positions) + ids[3]*3/*#coords*/;
    333 
    334     /* Compute the tetrahedron AABB */
    335     low[0] = MMIN(MMIN(verts[0][0],verts[1][0]), MMIN(verts[2][0],verts[3][0]));
    336     low[1] = MMIN(MMIN(verts[0][1],verts[1][1]), MMIN(verts[2][1],verts[3][1]));
    337     low[2] = MMIN(MMIN(verts[0][2],verts[1][2]), MMIN(verts[2][2],verts[3][2]));
    338     upp[0] = MMAX(MMAX(verts[0][0],verts[1][0]), MMAX(verts[2][0],verts[3][0]));
    339     upp[1] = MMAX(MMAX(verts[0][1],verts[1][1]), MMAX(verts[2][1],verts[3][1]));
    340     upp[2] = MMAX(MMAX(verts[0][2],verts[1][2]), MMAX(verts[2][2],verts[3][2]));
    341 
    342     /* Setup the build primitive */
    343     prim->lower_x = (float)low[0];
    344     prim->lower_y = (float)low[1];
    345     prim->lower_z = (float)low[2];
    346     prim->upper_x = (float)upp[0];
    347     prim->upper_y = (float)upp[1];
    348     prim->upper_z = (float)upp[2];
    349     prim->geomID = 0;
    350     prim->primID = (unsigned int)iprim;
    351   }
    352 
    353   /* Setup the build arguments */
    354   args = rtcDefaultBuildArguments();
    355   args.byteSize = sizeof(args);
    356   args.buildQuality = RTC_BUILD_QUALITY_MEDIUM;
    357   args.buildFlags = RTC_BUILD_FLAG_NONE;
    358   args.maxBranchingFactor = 2;
    359   args.maxDepth = 1024;
    360   args.sahBlockSize = 1;
    361   args.minLeafSize = 1;
    362   args.maxLeafSize = 1;
    363   args.traversalCost = 1.f;
    364   args.intersectionCost = 10.f;
    365   args.bvh = vol->bvh;
    366   args.primitives = darray_rtc_prim_data_get(&rtc_prims);
    367   args.primitiveCount = nprims;
    368   args.primitiveArrayCapacity = darray_rtc_prim_capacity(&rtc_prims);
    369   args.createNode = rtc_node_inner_create;
    370   args.setNodeChildren = rtc_node_inner_set_children;
    371   args.setNodeBounds = rtc_node_inner_set_bounds;
    372   args.createLeaf = rtc_node_leaf_create;
    373   args.splitPrimitive = NULL;
    374   args.buildProgress = NULL;
    375   args.userPtr = vol;
    376 
    377   /* Build the BVH */
    378   vol->bvh_root = rtcBuildBVH(&args);
    379   if(!vol->bvh_root) {
    380     const enum RTCError rtc_err = rtcGetDeviceError(vol->dev->rtc);
    381     log_err(vol->dev, "Error building the BVH -- %s.\n",
    382       rtc_error_string(rtc_err));
    383     res = rtc_error_to_res_T(rtc_err);
    384     goto error;
    385   }
    386 
    387 exit:
    388   if(rtc_prims_is_init) darray_rtc_prim_release(&rtc_prims);
    389   return res;
    390 error:
    391   if(vol->bvh) {
    392     rtcReleaseBVH(vol->bvh);
    393     vol->bvh = NULL;
    394   }
    395   goto exit;
    396 }
    397 
    398 static res_T
    399 setup_tetrahedra_normals
    400   (struct suvm_volume* vol,
    401    const int precompute_normals)
    402 {
    403   size_t itetra, ntetra;
    404   int fixup = 0;
    405   res_T res = RES_OK;
    406   ASSERT(vol);
    407 
    408   ntetra = volume_get_primitives_count(vol);
    409 
    410   if(precompute_normals) {
    411     res = darray_float_resize(&vol->normals,
    412       ntetra * 4/*#facets per tetrahedron*/ * 3/*#coords per normal*/);
    413     if(res != RES_OK) goto error;
    414   }
    415 
    416   FOR_EACH(itetra, 0, ntetra) {
    417     uint32_t* ids = NULL;
    418     const float* vert0 = NULL;
    419     const float* vert3 = NULL;
    420     float normal0[3];
    421     float v0[3];
    422     int flip = 0;
    423 
    424     /* Fetch tetrahedron indices */
    425     ids = darray_u32_data_get(&vol->indices) + itetra*4;
    426     ASSERT(ids[0] < volume_get_vertices_count(vol));
    427     ASSERT(ids[1] < volume_get_vertices_count(vol));
    428     ASSERT(ids[2] < volume_get_vertices_count(vol));
    429     ASSERT(ids[3] < volume_get_vertices_count(vol));
    430 
    431     /* Fetch the tetrahedron vertices */
    432     vert0 = darray_float_cdata_get(&vol->positions) + ids[0]*3/*#coords*/;
    433     vert3 = darray_float_cdata_get(&vol->positions) + ids[3]*3/*#coords*/;
    434 
    435     /* Compute the normal of the 1st facet */
    436     volume_primitive_compute_facet_normal(vol, itetra, 0, normal0);
    437 
    438     /* Check that the normal of the 1st facet looks the fourth vertex */
    439     if(f3_dot(normal0, f3_sub(v0, vert3, vert0)) < 0) {
    440       fixup = flip = 1;
    441       /* Revert tetrahedron orientation */
    442       SWAP(uint32_t, ids[1], ids[2]);
    443     }
    444 
    445     /* Precompute and store the facet the normals */
    446     if(precompute_normals) {
    447       float* n0 = darray_float_data_get(&vol->normals) + (itetra*4 + 0)*3;
    448       float* n1 = darray_float_data_get(&vol->normals) + (itetra*4 + 1)*3;
    449       float* n2 = darray_float_data_get(&vol->normals) + (itetra*4 + 2)*3;
    450       float* n3 = darray_float_data_get(&vol->normals) + (itetra*4 + 3)*3;
    451       if(!flip) {
    452         /* Copy the already computed normal of the facet 0 */
    453         n0[0] = normal0[0];
    454         n0[1] = normal0[1];
    455         n0[2] = normal0[2];
    456       } else {
    457         /* We could store the negated normal of the facet 0 but we recompute it
    458          * from scratch to ensure strict equivalence if it is not stored; since
    459          * tetrahedron vertices were flipped the negated normal and the
    460          * recomputed one are not strictly equals */
    461         volume_primitive_compute_facet_normal(vol, itetra, 0, n0);
    462       }
    463       volume_primitive_compute_facet_normal(vol, itetra, 1, n1);
    464       volume_primitive_compute_facet_normal(vol, itetra, 2, n2);
    465       volume_primitive_compute_facet_normal(vol, itetra, 3, n3);
    466     }
    467 
    468 #ifndef NDEBUG
    469     {
    470       const float* verts[4];
    471       float normals[4][3];
    472       float pt[3], v1[3];
    473 
    474       verts[0] = vert0;
    475       verts[1] = darray_float_cdata_get(&vol->positions) + ids[1]*3/*#coords*/;
    476       verts[2] = darray_float_cdata_get(&vol->positions) + ids[2]*3/*#coords*/;
    477       verts[3] = vert3;
    478 
    479       /* Compute the position at the center of the tetrahedron */
    480       pt[0] = (verts[0][0] + verts[1][0] + verts[2][0] + verts[3][0]) * 0.25f;
    481       pt[1] = (verts[0][1] + verts[1][1] + verts[2][1] + verts[3][1]) * 0.25f;
    482       pt[2] = (verts[0][2] + verts[1][2] + verts[2][2] + verts[3][2]) * 0.25f;
    483 
    484       /* Fetch the tetrahedron normals */
    485       volume_primitive_get_facet_normal(vol, itetra, 0, normals[0]);
    486       volume_primitive_get_facet_normal(vol, itetra, 1, normals[1]);
    487       volume_primitive_get_facet_normal(vol, itetra, 2, normals[2]);
    488       volume_primitive_get_facet_normal(vol, itetra, 3, normals[3]);
    489 
    490       /* Check normals orientation */
    491       f3_sub(v0, pt, verts[0]);
    492       f3_sub(v1, pt, verts[3]);
    493       ASSERT(f3_dot(normals[0], v0) >= 0);
    494       ASSERT(f3_dot(normals[1], v1) >= 0);
    495       ASSERT(f3_dot(normals[2], v1) >= 0);
    496       ASSERT(f3_dot(normals[3], v1) >= 0);
    497     }
    498 #endif
    499   }
    500 
    501   if(fixup) {
    502     log_warn(vol->dev, "Tetrahedra were not correctly oriented regarding the "
    503       "Star-UVM convention.\n");
    504   }
    505 
    506 exit:
    507   return res;
    508 error:
    509   darray_float_purge(&vol->normals);
    510   goto exit;
    511 }
    512 
    513 static void
    514 volume_release(ref_T* ref)
    515 {
    516   struct suvm_volume* vol = NULL;
    517   struct suvm_device* dev = NULL;
    518   ASSERT(ref);
    519   vol = CONTAINER_OF(ref, struct suvm_volume, ref);
    520   dev = vol->dev;
    521   darray_u32_release(&vol->indices);
    522   darray_float_release(&vol->positions);
    523   darray_float_release(&vol->normals);
    524   buffer_release(&vol->prim_data);
    525   buffer_release(&vol->vert_data);
    526   if(vol->bvh) rtcReleaseBVH(vol->bvh);
    527   MEM_RM(dev->allocator, vol);
    528   SUVM(device_ref_put(dev));
    529 }
    530 
    531 /*******************************************************************************
    532  * Exported functions
    533  ******************************************************************************/
    534 res_T
    535 suvm_tetrahedral_mesh_create
    536   (struct suvm_device* dev,
    537    const struct suvm_tetrahedral_mesh_args* args,
    538    struct suvm_volume** out_vol)
    539 {
    540   struct suvm_volume* vol = NULL;
    541   res_T res = RES_OK;
    542 
    543   if(!dev || !check_tetrahedral_mesh_args(args) || !out_vol) {
    544     res = RES_BAD_ARG;
    545     goto error;
    546   }
    547 
    548   vol = MEM_CALLOC(dev->allocator, 1, sizeof(*vol));
    549   if(!vol) {
    550     res = RES_MEM_ERR;
    551     goto error;
    552   }
    553   ref_init(&vol->ref);
    554   SUVM(device_ref_get(dev));
    555   vol->dev = dev;
    556   darray_u32_init(dev->allocator, &vol->indices);
    557   darray_float_init(dev->allocator, &vol->positions);
    558   darray_float_init(dev->allocator, &vol->normals);
    559 
    560   /* Locally copy the volumetric mesh data */
    561   res = setup_tetrahedral_mesh(vol, args);
    562   if(res != RES_OK) goto error;
    563 
    564   /* Ensure that the vertices of the tetrahedra are well ordered regarding the
    565    * normal convention and store them if required */
    566   res = setup_tetrahedra_normals(vol, args->precompute_normals);
    567   if(res != RES_OK) goto error;
    568 
    569   /* Build the BVH of the volumetric mesh */
    570   res = build_bvh(vol);
    571   if(res != RES_OK) goto error;
    572 
    573   /* Setup the volume AABB */
    574   if(vol->bvh_root->type == NODE_LEAF) {
    575     const struct node_leaf* leaf = NULL;
    576     leaf = CONTAINER_OF(vol->bvh_root, struct node_leaf, node);
    577     vol->low[0] = leaf->low[0];
    578     vol->low[1] = leaf->low[1];
    579     vol->low[2] = leaf->low[2];
    580     vol->upp[0] = leaf->upp[0];
    581     vol->upp[1] = leaf->upp[1];
    582     vol->upp[2] = leaf->upp[2];
    583   } else {
    584     const struct node_inner* inner = NULL;
    585     inner = CONTAINER_OF(vol->bvh_root, struct node_inner, node);
    586     vol->low[0] = MMIN(inner->low[0][0], inner->low[1][0]);
    587     vol->low[1] = MMIN(inner->low[0][1], inner->low[1][1]);
    588     vol->low[2] = MMIN(inner->low[0][2], inner->low[1][2]);
    589     vol->upp[0] = MMAX(inner->upp[0][0], inner->upp[1][0]);
    590     vol->upp[1] = MMAX(inner->upp[0][1], inner->upp[1][1]);
    591     vol->upp[2] = MMAX(inner->upp[0][2], inner->upp[1][2]);
    592   }
    593 
    594 exit:
    595   if(out_vol) *out_vol = vol;
    596   return res;
    597 error:
    598   if(vol) { SUVM(volume_ref_put(vol)); vol = NULL; }
    599   goto exit;
    600 }
    601 
    602 res_T
    603 suvm_volume_ref_get(struct suvm_volume* vol)
    604 {
    605   if(!vol) return RES_BAD_ARG;
    606   ref_get(&vol->ref);
    607   return RES_OK;
    608 }
    609 
    610 res_T
    611 suvm_volume_ref_put(struct suvm_volume* vol)
    612 {
    613   if(!vol) return RES_BAD_ARG;
    614   ref_put(&vol->ref, volume_release);
    615   return RES_OK;
    616 }
    617 
    618 res_T
    619 suvm_volume_get_aabb
    620   (const struct suvm_volume* volume,
    621    double lower[3],
    622    double upper[3])
    623 {
    624   if(!volume || !lower || !upper) return RES_BAD_ARG;
    625   lower[0] = volume->low[0];
    626   lower[1] = volume->low[1];
    627   lower[2] = volume->low[2];
    628   upper[0] = volume->upp[0];
    629   upper[1] = volume->upp[1];
    630   upper[2] = volume->upp[2];
    631   return RES_OK;
    632 }
    633 
    634 res_T
    635 suvm_volume_get_primitives_count(const struct suvm_volume* vol, size_t* nprims)
    636 {
    637   if(!vol || !nprims) return RES_BAD_ARG;
    638   *nprims = volume_get_primitives_count(vol);
    639   return RES_OK;
    640 }
    641 
    642 res_T
    643 suvm_volume_get_primitive
    644   (const struct suvm_volume* vol,
    645    const size_t iprim,
    646    struct suvm_primitive* prim)
    647 {
    648   if(!vol|| !prim || iprim >= volume_get_primitives_count(vol))
    649     return RES_BAD_ARG;
    650   volume_primitive_setup(vol, iprim, prim);
    651   return RES_OK;
    652 }
    653 
    654 res_T
    655 suvm_volume_compute_hash
    656   (const struct suvm_volume* vol,
    657    const int cpnt_mask,
    658    hash256_T hash)
    659 {
    660   struct sha256_ctx ctx;
    661   res_T res = RES_OK;
    662 
    663   if(!vol || !hash) {
    664     res = RES_BAD_ARG;
    665     goto error;
    666   }
    667 
    668   sha256_ctx_init(&ctx);
    669 
    670   if(cpnt_mask & SUVM_POSITIONS) {
    671     const float* pos = darray_float_cdata_get(&vol->positions);
    672     const size_t n = darray_float_size_get(&vol->positions);
    673     sha256_ctx_update(&ctx, (const char*)pos, sizeof(*pos)*n);
    674   }
    675   if(cpnt_mask & SUVM_INDICES) {
    676     const uint32_t* ids = darray_u32_cdata_get(&vol->indices);
    677     const size_t n = darray_u32_size_get(&vol->indices);
    678     sha256_ctx_update(&ctx, (const char*)ids, sizeof(*ids)*n);
    679   }
    680   if(cpnt_mask & SUVM_PRIMITIVE_DATA) {
    681     const size_t sz = vol->prim_data.size * vol->prim_data.elmt_stride;
    682     sha256_ctx_update(&ctx, vol->prim_data.mem, sz);
    683   }
    684   if(cpnt_mask & SUVM_VERTEX_DATA) {
    685     const size_t sz = vol->vert_data.size * vol->vert_data.elmt_stride;
    686     sha256_ctx_update(&ctx, vol->vert_data.mem, sz);
    687   }
    688 
    689   sha256_ctx_finalize(&ctx, hash);
    690 
    691 exit:
    692   return res;
    693 error:
    694   goto exit;
    695 }
    696 
    697 res_T
    698 suvm_volume_get_mesh_desc
    699   (const struct suvm_volume* vol,
    700    struct suvm_mesh_desc* desc)
    701 {
    702   res_T res = RES_OK;
    703 
    704   if(!vol || !desc) {
    705     res = RES_BAD_ARG;
    706     goto error;
    707   }
    708 
    709   desc->positions = darray_float_cdata_get(&vol->positions);
    710   desc->indices = darray_u32_cdata_get(&vol->indices);
    711   desc->dvertex = 3;
    712   desc->dprimitive = 4;
    713   desc->nvertices = darray_float_size_get(&vol->positions) / desc->dvertex;
    714   desc->nprimitives = darray_u32_size_get(&vol->indices) / desc->dprimitive;
    715 
    716 exit:
    717   return res;
    718 error:
    719   goto exit;
    720 }
    721 
    722 res_T
    723 suvm_mesh_desc_compute_hash(const struct suvm_mesh_desc* desc, hash256_T hash)
    724 {
    725   struct sha256_ctx ctx;
    726 
    727   if(!desc || !hash) return RES_BAD_ARG;
    728 
    729   #define HASH(Var, Nb) \
    730     sha256_ctx_update(&ctx, (const char*)(Var), sizeof(*Var)*(Nb));
    731 
    732   sha256_ctx_init(&ctx);
    733   HASH(desc->positions, desc->nvertices*desc->dvertex);
    734   HASH(desc->indices, desc->nprimitives*desc->dprimitive);
    735   HASH(&desc->nvertices, 1);
    736   HASH(&desc->nprimitives, 1);
    737   HASH(&desc->dvertex, 1);
    738   HASH(&desc->dprimitive, 1);
    739   sha256_ctx_finalize(&ctx, hash);
    740 
    741   #undef HASH
    742 
    743   return RES_OK;
    744 }