star-3d

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

s3d_scene_view.c (49330B)


      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 #include "s3d.h"
     19 #include "s3d_device_c.h"
     20 #include "s3d_scene_c.h"
     21 #include "s3d_scene_view_c.h"
     22 #include "s3d_shape_c.h"
     23 
     24 #include <rsys/algorithm.h>
     25 #include <rsys/float3.h>
     26 #include <rsys/float33.h>
     27 #include <rsys/mem_allocator.h>
     28 
     29 /*******************************************************************************
     30  * Helper functions
     31  ******************************************************************************/
     32 static FINLINE int
     33 aabb_is_degenerated(const float lower[3], const float upper[3])
     34 {
     35   ASSERT(lower && upper);
     36   return lower[0] > upper[0] || lower[1] > upper[1] || lower[2] > upper[2];
     37 }
     38 
     39 static INLINE int
     40 cmp_float(const void* a, const void* b)
     41 {
     42   const float key = *(const float*)a;
     43   const float val = *(const float*)b;
     44   if(key < val) return -1;
     45   if(key > val) return +1;
     46   return 0;
     47 }
     48 
     49 static INLINE int
     50 cmp_float_to_fltui(const void* a, const void* b)
     51 {
     52   const float key = *(const float*)a;
     53   const struct fltui* fltui = (const struct fltui*)b;
     54   if(key < fltui->flt) return -1;
     55   if(key > fltui->flt) return +1;
     56   return 0;
     57 }
     58 
     59 static INLINE int
     60 cmp_size_t_to_nprims_cdf(const void* a, const void* b)
     61 {
     62   const size_t key = *(const size_t*)a;
     63   const struct nprims_cdf* nprims_cdf = (const struct nprims_cdf*)b;
     64   if(key < nprims_cdf->nprims-1) return -1;
     65   if(key > nprims_cdf->nprims-1) return +1;
     66   return 0;
     67 }
     68 
     69 static INLINE void
     70 scene_view_destroy_geometry(struct s3d_scene_view* scnview, struct geometry* geom)
     71 {
     72   ASSERT(geom);
     73   if(geom->rtc) {
     74     if(geom->rtc_id != RTC_INVALID_GEOMETRY_ID) {
     75       rtcDetachGeometry(scnview->rtc_scn, geom->rtc_id);
     76       geom->rtc_id = RTC_INVALID_GEOMETRY_ID;
     77     }
     78     rtcReleaseGeometry(geom->rtc);
     79     geom->rtc = NULL;
     80     scnview->rtc_scn_update = 1; /* Notify the scene upd */
     81   }
     82   geometry_ref_put(geom);
     83 }
     84 
     85 static void
     86 on_shape_detach
     87   (const struct s3d_scene* scn,
     88    const struct s3d_shape* shape,
     89    void* data)
     90 {
     91   struct geometry** pgeom;
     92   struct geometry* geom;
     93   struct s3d_scene_view* scnview = (struct s3d_scene_view*)data;
     94   unsigned shape_id;
     95   ASSERT(scn && shape && data);
     96   (void)scn;
     97 
     98   S3D(shape_get_id(shape, &shape_id));
     99   pgeom = htable_geom_find(&scnview->cached_geoms, &shape_id);
    100 
    101   /* The scnview did not register a geometry for this shape. Ignore the signal */
    102   if(!pgeom) return;
    103 
    104   geom = *pgeom;
    105   if(scnview->mask == 0) {
    106     /* The scnview is NOT in use. Directly rm the cached geometry and notify
    107      * that the scene AABB must be reevaluated */
    108     size_t n; (void)n;
    109     scene_view_destroy_geometry(scnview, geom);
    110     n = htable_geom_erase(&scnview->cached_geoms, &shape_id);
    111     ASSERT(n == 1);
    112     scnview->aabb_update = 1;
    113   } else {
    114     /* The scnview is in use. Delay the deletion of the cached geometry */
    115     res_T res = darray_uint_push_back(&scnview->detached_shapes, &shape_id);
    116     if(res != RES_OK) FATAL("Insufficient memory.\n");
    117   }
    118 }
    119 
    120 static INLINE enum RTCBuildQuality
    121 accel_struct_quality_to_rtc_build_quality
    122   (enum s3d_accel_struct_quality quality)
    123 {
    124   enum RTCBuildQuality rtc_quality = RTC_BUILD_QUALITY_MEDIUM;
    125   switch(quality) {
    126     case S3D_ACCEL_STRUCT_QUALITY_LOW:
    127       rtc_quality = RTC_BUILD_QUALITY_LOW;
    128       break;
    129     case S3D_ACCEL_STRUCT_QUALITY_MEDIUM:
    130       rtc_quality = RTC_BUILD_QUALITY_MEDIUM;
    131       break;
    132     case S3D_ACCEL_STRUCT_QUALITY_HIGH:
    133       rtc_quality = RTC_BUILD_QUALITY_HIGH;
    134       break;
    135     default: FATAL("Unreachable code\n"); break;
    136   }
    137   return rtc_quality;
    138 }
    139 
    140 static INLINE int
    141 accel_struct_mask_to_rtc_scene_flags(const int mask)
    142 {
    143   int rtc_scene_flags = 0;
    144   if(mask & S3D_ACCEL_STRUCT_FLAG_ROBUST)
    145     rtc_scene_flags |= RTC_SCENE_FLAG_ROBUST;
    146   if(mask & S3D_ACCEL_STRUCT_FLAG_DYNAMIC)
    147     rtc_scene_flags |= RTC_SCENE_FLAG_DYNAMIC;
    148   if(mask & S3D_ACCEL_STRUCT_FLAG_COMPACT)
    149     rtc_scene_flags |= RTC_SCENE_FLAG_COMPACT;
    150   return rtc_scene_flags;
    151 }
    152 
    153 static res_T
    154 embree_geometry_register
    155   (struct s3d_scene_view* scnview,
    156    struct geometry* geom,
    157    const struct s3d_accel_struct_conf* accel_struct_conf)
    158 {
    159   enum RTCBuildQuality rtc_build_quality = RTC_BUILD_QUALITY_MEDIUM;
    160   ASSERT(scnview && geom && accel_struct_conf);
    161 
    162   rtc_build_quality = accel_struct_quality_to_rtc_build_quality
    163     (accel_struct_conf->quality);
    164 
    165   /* Create the Embree geometry if it is not valid */
    166   if(geom->rtc != NULL) {
    167     switch(geom->type) {
    168       case GEOM_MESH:
    169         if(geom->rtc_build_quality != rtc_build_quality) {
    170           /* Update the build quality of the geometry */
    171           rtcSetGeometryBuildQuality(geom->rtc, rtc_build_quality);
    172           rtcCommitGeometry(geom->rtc);
    173           geom->rtc_build_quality = rtc_build_quality;
    174           scnview->rtc_scn_update = 1;
    175         }
    176         break;
    177       case GEOM_INSTANCE:
    178         /* If the geometry is an instance one have to update it if the
    179          * instantiated geometry was updated. Currently, we have no simple way to
    180          * know if the geometry was upd or not so we simply force the update. */
    181         rtcCommitGeometry(geom->rtc);
    182         scnview->rtc_scn_update = 1;
    183         break;
    184       case GEOM_SPHERE: /* Do nothing */ break;
    185       default: FATAL("Unreachable code\n"); break;
    186     }
    187   } else {
    188     switch(geom->type) {
    189       case GEOM_MESH:
    190         geom->rtc = rtcNewGeometry
    191           (scnview->scn->dev->rtc, RTC_GEOMETRY_TYPE_TRIANGLE);
    192         break;
    193       case GEOM_INSTANCE:
    194         geom->rtc = rtcNewGeometry
    195           (scnview->scn->dev->rtc, RTC_GEOMETRY_TYPE_INSTANCE);
    196         rtcSetGeometryInstancedScene(geom->rtc, geom->data.instance->scnview->rtc_scn);
    197         break;
    198       case GEOM_SPHERE:
    199         geom->rtc = rtcNewGeometry
    200           (scnview->scn->dev->rtc, RTC_GEOMETRY_TYPE_USER);
    201         /* Setup geometry callbacks. Note that the "occluded" is not set since
    202          * rtcOccluded is not used */
    203         rtcSetGeometryUserPrimitiveCount(geom->rtc, 1);
    204         rtcSetGeometryBoundsFunction(geom->rtc, geometry_rtc_sphere_bounds, NULL);
    205         rtcSetGeometryIntersectFunction(geom->rtc, geometry_rtc_sphere_intersect);
    206         break;
    207       default: FATAL("Unreachable code\n"); break;
    208     }
    209     if(geom->rtc == NULL)
    210       return RES_UNKNOWN_ERR;
    211 
    212     if(geom->type == GEOM_MESH) {
    213       /* Set the build quality of the geometry */
    214       rtcSetGeometryBuildQuality(geom->rtc, rtc_build_quality);
    215       geom->rtc_build_quality = rtc_build_quality;
    216     }
    217 
    218     /* Set the Star-3D representation of the geometry to the Embree geometry */
    219     rtcSetGeometryUserData(geom->rtc, geom);
    220 
    221     /* Attach the Embree geometry to the Embree scene of the scene view */
    222     geom->rtc_id = rtcAttachGeometry(scnview->rtc_scn, geom->rtc);
    223 
    224     scnview->rtc_scn_update = 1;
    225   }
    226   return RES_OK;
    227 }
    228 
    229 static INLINE res_T
    230 embree_geometry_setup_positions
    231   (struct s3d_scene_view* scnview, struct geometry* geom)
    232 {
    233   RTCBuffer buf = NULL;
    234   float* verts;
    235   size_t nverts;
    236   res_T res = RES_OK;
    237   ASSERT(scnview && geom && geom->type == GEOM_MESH && geom->rtc);
    238 
    239   verts = mesh_get_pos(geom->data.mesh);
    240   nverts = mesh_get_nverts(geom->data.mesh);
    241 
    242   buf = rtcNewSharedBuffer
    243     (scnview->scn->dev->rtc, verts, sizeof(float[3])*nverts);
    244   if(!buf) {
    245     res = rtc_error_to_res_T(rtcGetDeviceError(scnview->scn->dev->rtc));
    246     goto error;
    247   }
    248 
    249   rtcSetGeometryBuffer(geom->rtc, RTC_BUFFER_TYPE_VERTEX, 0/*slot*/,
    250     RTC_FORMAT_FLOAT3, buf, 0/*offset*/, sizeof(float[3])/*stride*/, nverts);
    251   rtcUpdateGeometryBuffer(geom->rtc, RTC_BUFFER_TYPE_VERTEX, 0);
    252 
    253 exit:
    254   if(buf) rtcReleaseBuffer(buf);
    255   return res;
    256 error:
    257   goto exit;
    258 }
    259 
    260 static INLINE res_T
    261 embree_geometry_setup_indices
    262   (struct s3d_scene_view* scnview, struct geometry* geom)
    263 {
    264   RTCBuffer buf = NULL;
    265   size_t ntris;
    266   uint32_t* ids;
    267   res_T res = RES_OK;
    268   ASSERT(scnview && geom && geom->type == GEOM_MESH && geom->rtc);
    269 
    270   ids = mesh_get_ids(geom->data.mesh);
    271   ntris = mesh_get_ntris(geom->data.mesh);
    272 
    273   buf = rtcNewSharedBuffer
    274     (scnview->scn->dev->rtc, ids, sizeof(uint32_t[3])*ntris);
    275   if(!buf) {
    276     res = rtc_error_to_res_T(rtcGetDeviceError(scnview->scn->dev->rtc));
    277     goto error;
    278   }
    279 
    280   rtcSetGeometryBuffer(geom->rtc, RTC_BUFFER_TYPE_INDEX, 0/*slot*/,
    281     RTC_FORMAT_UINT3, buf, 0/*offset*/, sizeof(uint32_t[3])/*stride*/, ntris);
    282   rtcUpdateGeometryBuffer(geom->rtc, RTC_BUFFER_TYPE_INDEX, 0);
    283 
    284 exit:
    285   if(buf) rtcReleaseBuffer(buf);
    286   return res;
    287 error:
    288   goto exit;
    289 }
    290 
    291 static INLINE void
    292 embree_geometry_setup_enable_state
    293   (struct s3d_scene_view* scnview, struct geometry* geom)
    294 {
    295   ASSERT(scnview && geom);
    296   (void)scnview;
    297   if(geom->is_enabled) {
    298     rtcEnableGeometry(geom->rtc);
    299   } else {
    300     rtcDisableGeometry(geom->rtc);
    301   }
    302 }
    303 
    304 static INLINE void
    305 embree_geometry_setup_filter_function
    306   (struct s3d_scene_view* scnview, struct geometry* geom)
    307 {
    308   ASSERT(scnview && geom && geom->rtc &&geom->type == GEOM_MESH);
    309   (void)scnview;
    310 
    311   if(!geom->data.mesh->filter.func) {
    312     rtcSetGeometryIntersectFilterFunction(geom->rtc, NULL);
    313   } else {
    314     rtcSetGeometryIntersectFilterFunction(geom->rtc, rtc_hit_filter_wrapper);
    315   }
    316 }
    317 
    318 static INLINE void
    319 embree_geometry_setup_transform
    320   (struct s3d_scene_view* scnview, struct geometry* geom)
    321 {
    322   ASSERT(scnview && geom && geom->rtc != NULL);
    323   ASSERT(geom->type == GEOM_INSTANCE);
    324   (void)scnview;
    325   rtcSetGeometryTransform(geom->rtc, 0, RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR,
    326     geom->data.instance->transform);
    327 }
    328 
    329 static INLINE res_T
    330 scene_view_setup_embree
    331   (struct s3d_scene_view* scnview,
    332    const struct s3d_accel_struct_conf* accel_struct_conf)
    333 {
    334   struct htable_geom_iterator it, end;
    335   int rtc_outdated = 0;
    336   int rtc_scn_flags = 0;
    337   enum RTCBuildQuality rtc_scn_build_quality = 0;
    338   res_T res = RES_OK;
    339   ASSERT(scnview);
    340 
    341   rtc_scn_flags = accel_struct_mask_to_rtc_scene_flags
    342     (accel_struct_conf->mask);
    343   rtc_scn_build_quality = accel_struct_quality_to_rtc_build_quality
    344     (accel_struct_conf->quality);
    345 
    346   /* The rtc_scn could be already allocated since the scene views are cached */
    347   if(!scnview->rtc_scn) {
    348     scnview->rtc_scn = rtcNewScene(scnview->scn->dev->rtc);
    349     if(!scnview->rtc_scn) {
    350       res = rtc_error_to_res_T(rtcGetDeviceError(scnview->scn->dev->rtc));
    351       goto error;
    352     }
    353     rtcSetSceneFlags(scnview->rtc_scn, rtc_scn_flags);
    354     scnview->rtc_scn_flags = rtc_scn_flags;
    355     rtc_outdated = 1;
    356   }
    357 
    358   /* Check if the scene flags were updated */
    359   if(scnview->rtc_scn_flags != rtc_scn_flags) {
    360     rtcSetSceneFlags(scnview->rtc_scn, rtc_scn_flags);
    361     scnview->rtc_scn_flags = rtc_scn_flags;
    362     rtc_outdated = 1;
    363   }
    364 
    365   /* Check if the build quality was updated */
    366   if(scnview->rtc_scn_build_quality != rtc_scn_build_quality) {
    367     rtcSetSceneBuildQuality(scnview->rtc_scn, rtc_scn_build_quality);
    368     scnview->rtc_scn_build_quality = rtc_scn_build_quality;
    369     rtc_outdated = 1;
    370   }
    371 
    372   htable_geom_begin(&scnview->cached_geoms, &it);
    373   htable_geom_end(&scnview->cached_geoms, &end);
    374 
    375   while(!htable_geom_iterator_eq(&it, &end)) {
    376     struct geometry** pgeom = htable_geom_iterator_data_get(&it);
    377     struct geometry* geom = *pgeom;
    378 
    379     htable_geom_iterator_next(&it);
    380 
    381     /* Define whether or not the embree scene is outdated */
    382     if(geom->embree_outdated_mask) rtc_outdated = 1;
    383     if(geom->type == GEOM_INSTANCE && geom->data.instance->scnview->rtc_commit)
    384       rtc_outdated = 1;
    385 
    386     /* Register the embree geometry */
    387     res = embree_geometry_register(scnview, geom, accel_struct_conf);
    388     if(res != RES_OK) goto error;
    389 
    390     /* Flush the embree geometry states */
    391     if((geom->embree_outdated_mask & EMBREE_VERTICES) != 0) {
    392       res = embree_geometry_setup_positions(scnview, geom);
    393       if(res != RES_OK) goto error;
    394     }
    395     if((geom->embree_outdated_mask & EMBREE_INDICES) != 0) {
    396       embree_geometry_setup_indices(scnview, geom);
    397       if(res != RES_OK) goto error;
    398     }
    399     if((geom->embree_outdated_mask & EMBREE_ENABLE) != 0)
    400       embree_geometry_setup_enable_state(scnview, geom);
    401     if((geom->embree_outdated_mask & EMBREE_FILTER_FUNCTION) != 0)
    402       embree_geometry_setup_filter_function(scnview, geom);
    403     if((geom->embree_outdated_mask & EMBREE_TRANSFORM) != 0)
    404       embree_geometry_setup_transform(scnview, geom);
    405 
    406     /* Commit the updated geometry */
    407     if(geom->embree_outdated_mask)
    408       rtcCommitGeometry(geom->rtc);
    409 
    410     geom->embree_outdated_mask = 0;
    411   }
    412 
    413   rtc_outdated = rtc_outdated || scnview->rtc_scn_update;
    414 
    415   /* Commit the embree changes */
    416   if(rtc_outdated) {
    417     rtcCommitScene(scnview->rtc_scn);
    418     scnview->rtc_commit = 1; /* Notify that the RTC scene was committed */
    419     scnview->rtc_scn_update = 0;
    420   }
    421 
    422 exit:
    423   return res;
    424 error:
    425   if(scnview->rtc_scn) {
    426     rtcReleaseScene(scnview->rtc_scn);
    427     scnview->rtc_scn = NULL;
    428   }
    429   goto exit;
    430 }
    431 
    432 static res_T
    433 scene_view_register_mesh
    434   (struct s3d_scene_view* scnview,
    435    struct s3d_shape* shape)
    436 {
    437   struct geometry** pgeom = NULL;
    438   struct geometry* geom = NULL;
    439   size_t iattr;
    440   unsigned shape_id;
    441   int is_valid;
    442   res_T res = RES_OK;
    443   ASSERT(scnview && shape && shape->type == GEOM_MESH);
    444 
    445   is_valid = shape->data.mesh->indices && shape->data.mesh->attribs[S3D_POSITION];
    446 
    447   /* Retrieve the cached geometry */
    448   S3D(shape_get_id(shape, &shape_id));
    449   pgeom = htable_geom_find(&scnview->cached_geoms, &shape_id);
    450   if(pgeom) {
    451     geom = *pgeom;
    452     if(!is_valid) {
    453       scene_view_destroy_geometry(scnview, geom);
    454       htable_geom_erase(&scnview->cached_geoms, &shape_id);
    455     }
    456   } else if(is_valid) {
    457     res = geometry_create(scnview->scn->dev, &geom);
    458     if(res != RES_OK) goto error;
    459     res = mesh_create(scnview->scn->dev, &geom->data.mesh);
    460     if(res != RES_OK) goto error;
    461     geom->type = GEOM_MESH;
    462     res = htable_geom_set(&scnview->cached_geoms, &shape_id, &geom);
    463     if(res != RES_OK) goto error;
    464     geom->name = shape->id.index;
    465   }
    466 
    467   if(!is_valid) goto exit;
    468 
    469   /* Get a reference onto the shape mesh indices */
    470   if(geom->data.mesh->indices != shape->data.mesh->indices) {
    471     geom->embree_outdated_mask |= EMBREE_INDICES;
    472     if(geom->data.mesh->indices) { /* Release the previous index buffer */
    473       index_buffer_ref_put(geom->data.mesh->indices);
    474       geom->data.mesh->indices = NULL;
    475     }
    476     ASSERT(shape->data.mesh->indices);
    477     index_buffer_ref_get(shape->data.mesh->indices);
    478     geom->data.mesh->indices = shape->data.mesh->indices;
    479   }
    480 
    481   /* Get a reference onto the shape mesh attribs */
    482   FOR_EACH(iattr, 0, S3D_ATTRIBS_COUNT__) {
    483     if(geom->data.mesh->attribs[iattr] == shape->data.mesh->attribs[iattr])
    484       continue;
    485 
    486     geom->embree_outdated_mask |= EMBREE_VERTICES;
    487 
    488     if(geom->data.mesh->attribs[iattr]) { /* Release the previous buffer */
    489       vertex_buffer_ref_put(geom->data.mesh->attribs[iattr]);
    490       geom->data.mesh->attribs[iattr] = NULL;
    491     }
    492 
    493     /* This attrib does not exist anymore */
    494     if(!shape->data.mesh->attribs[iattr])
    495       continue;
    496 
    497     /* Get the new buffer */
    498     vertex_buffer_ref_get(shape->data.mesh->attribs[iattr]);
    499     geom->data.mesh->attribs[iattr] = shape->data.mesh->attribs[iattr];
    500     geom->data.mesh->attribs_type[iattr] = shape->data.mesh->attribs_type[iattr];
    501   }
    502 
    503   /*  Update the enable flag */
    504   if(geom->is_enabled != shape->is_enabled) {
    505     geom->is_enabled = shape->is_enabled;
    506     geom->embree_outdated_mask |= EMBREE_ENABLE;
    507   }
    508 
    509   /* Update the filter function */
    510   if(geom->data.mesh->filter.func != shape->data.mesh->filter.func
    511   || geom->data.mesh->filter.data != shape->data.mesh->filter.data) {
    512     geom->data.mesh->filter = shape->data.mesh->filter;
    513     geom->embree_outdated_mask |= EMBREE_FILTER_FUNCTION;
    514   }
    515 
    516   geom->flip_surface = shape->flip_surface;
    517 
    518   /* The geometry is updated => recompute the scene AABB */
    519   if(geom->embree_outdated_mask
    520   & (EMBREE_VERTICES|EMBREE_INDICES|EMBREE_ENABLE)) {
    521     scnview->aabb_update = 1;
    522   }
    523 
    524 exit:
    525   return res;
    526 error:
    527   goto exit;
    528 }
    529 
    530 static res_T
    531 scene_view_register_sphere
    532   (struct s3d_scene_view* scnview,
    533    struct s3d_shape* shape)
    534 {
    535   struct geometry** pgeom = NULL;
    536   struct geometry* geom = NULL;
    537   unsigned shape_id;
    538   int is_valid;
    539   res_T res = RES_OK;
    540   ASSERT(scnview && shape && shape->type == GEOM_SPHERE);
    541 
    542   is_valid = !sphere_is_degenerated(shape->data.sphere);
    543 
    544   /* Retrieve the cached geometry */
    545   S3D(shape_get_id(shape, &shape_id));
    546   pgeom = htable_geom_find(&scnview->cached_geoms, &shape_id);
    547   if(pgeom) {
    548     geom = *pgeom;
    549     if(!is_valid) {
    550       scene_view_destroy_geometry(scnview, geom);
    551       htable_geom_erase(&scnview->cached_geoms, &shape_id);
    552     }
    553   } else if(is_valid) {
    554     res = geometry_create(scnview->scn->dev, &geom);
    555     if(res != RES_OK) goto error;
    556     res = sphere_create(scnview->scn->dev, &geom->data.sphere);
    557     if(res != RES_OK) goto error;
    558     geom->type = GEOM_SPHERE;
    559     res = htable_geom_set(&scnview->cached_geoms, &shape_id, &geom);
    560     if(res != RES_OK) goto error;
    561     geom->name = shape->id.index;
    562     geom->embree_outdated_mask |= EMBREE_USER_GEOMETRY;
    563   }
    564 
    565   if(!is_valid) goto exit;
    566 
    567   /* Setup the sphere radius */
    568   if(geom->data.sphere->radius != shape->data.sphere->radius) {
    569     geom->data.sphere->radius = shape->data.sphere->radius;
    570     geom->embree_outdated_mask |= EMBREE_USER_GEOMETRY;
    571   }
    572 
    573   /* Setup the sphere position */
    574   if(!f3_eq(geom->data.sphere->pos, shape->data.sphere->pos)) {
    575     f3_set(geom->data.sphere->pos, shape->data.sphere->pos);
    576     geom->embree_outdated_mask |= EMBREE_USER_GEOMETRY;
    577   }
    578 
    579   if(geom->is_enabled != shape->is_enabled) {
    580     geom->is_enabled = shape->is_enabled;
    581     geom->embree_outdated_mask |= EMBREE_ENABLE;
    582   }
    583 
    584   /* The geometry is updated => recompute the scene AABB */
    585   if(geom->embree_outdated_mask & (EMBREE_USER_GEOMETRY|EMBREE_ENABLE)) {
    586     scnview->aabb_update = 1;
    587   }
    588 
    589   /* Update the filter function. Actually, filter functions are supported for
    590    * built-in geometries only. For user defined geometries one has to
    591    * explicitly call the filter function in the user defined  intersection
    592    * function.  */
    593   if(geom->data.sphere->filter.func != shape->data.sphere->filter.func
    594   || geom->data.sphere->filter.data != shape->data.sphere->filter.data) {
    595     geom->data.sphere->filter = shape->data.sphere->filter;
    596     /* The user defined geometries do not support filter function => the
    597      * EMBREE_FILTER_FUNCTION flag is thus invalid for them. Enable the
    598      * EMBREE_USER_GEOMETRY flag instead to notify its update. */
    599     geom->embree_outdated_mask |= EMBREE_USER_GEOMETRY;
    600   }
    601 
    602   geom->flip_surface = shape->flip_surface;
    603 
    604 exit:
    605   return res;
    606 error:
    607   goto exit;
    608 }
    609 
    610 static res_T
    611 scene_view_register_instance
    612   (struct s3d_scene_view* scnview,
    613    struct s3d_shape* shape,
    614    const int mask)
    615 {
    616   struct geometry** pgeom = NULL;
    617   struct geometry* geom = NULL;
    618   struct s3d_scene_view** pview = NULL;
    619   struct s3d_scene_view* view = NULL;
    620   unsigned shape_id;
    621   res_T res = RES_OK;
    622   ASSERT(scnview && shape && shape->type == GEOM_INSTANCE);
    623 
    624   /* The instance cannot contain instances, i.e. one instancing level is
    625    * supported */
    626   if(shape->data.instance->scene->instances_count != 0) {
    627     res = RES_BAD_ARG;
    628     goto error;
    629   }
    630 
    631   /* Recursively create scnview on the scene to instantiate if necessary */
    632   pview = htable_instview_find
    633     (&scnview->instviews, &shape->data.instance->scene);
    634   if(pview) {
    635     view = *pview;
    636   } else {
    637     res = s3d_scene_view_create
    638       (shape->data.instance->scene, mask, &view);
    639     if(res != RES_OK) goto error;
    640     res = htable_instview_set
    641       (&scnview->instviews, &shape->data.instance->scene, &view);
    642     if(res != RES_OK) goto error;
    643   }
    644 
    645   /* Create the scene instance of the geometry if necessary */
    646   S3D(shape_get_id(shape, &shape_id));
    647   pgeom = htable_geom_find(&scnview->cached_geoms, &shape_id);
    648   if(pgeom) {
    649     geom = *pgeom;
    650   } else {
    651     res = geometry_create(scnview->scn->dev, &geom);
    652     if(res != RES_OK) goto error;
    653     geom->type = GEOM_INSTANCE;
    654     res = instance_create(shape->data.instance->scene, &geom->data.instance);
    655     if(res != RES_OK) goto error;
    656     res = htable_geom_set(&scnview->cached_geoms, &shape_id, &geom);
    657     if(res != RES_OK) goto error;
    658     geom->name = shape->id.index;
    659 
    660     /* Force the commit of the newly created geometry by setting up its
    661      * outdated mask */
    662     geom->embree_outdated_mask |= EMBREE_TRANSFORM;
    663   }
    664   ASSERT(geom->data.instance->scene == shape->data.instance->scene);
    665   geom->data.instance->scnview = view;
    666 
    667   /* Update the Embree instance transformation if necessary */
    668   if(!f33_eq(shape->data.instance->transform, geom->data.instance->transform)
    669   || !f3_eq(shape->data.instance->transform+9, geom->data.instance->transform+9)) {
    670     geom->embree_outdated_mask |= EMBREE_TRANSFORM;
    671     f33_set(geom->data.instance->transform, shape->data.instance->transform);
    672     f3_set(geom->data.instance->transform+9, shape->data.instance->transform+9);
    673   }
    674 
    675   /* Update the enable flag */
    676   if(geom->is_enabled != shape->is_enabled) {
    677     geom->is_enabled = shape->is_enabled;
    678     geom->embree_outdated_mask |= EMBREE_ENABLE;
    679   }
    680 
    681   /* The instance transform was updated => recompute the scene AABB */
    682   if(geom->embree_outdated_mask & (EMBREE_TRANSFORM|EMBREE_ENABLE)) {
    683     scnview->aabb_update = 1;
    684   }
    685 
    686   geom->flip_surface = shape->flip_surface;
    687 
    688 exit:
    689   return res;
    690 error:
    691   goto exit;
    692 }
    693 
    694 static res_T
    695 scene_view_compute_cdf(struct s3d_scene_view* scnview)
    696 {
    697   struct htable_geom_iterator it, end;
    698   size_t len;
    699   float area = 0.f;
    700   res_T res = RES_OK;
    701   ASSERT(scnview);
    702   ASSERT(darray_fltui_size_get(&scnview->cdf) == 0);
    703 
    704   htable_geom_begin(&scnview->cached_geoms, &it);
    705   htable_geom_end(&scnview->cached_geoms, &end);
    706 
    707   while(!htable_geom_iterator_eq(&it, &end)) {
    708     const unsigned* shape_id = htable_geom_iterator_key_get(&it);
    709     struct geometry* geom = *htable_geom_iterator_data_get(&it);
    710     struct fltui fltui;
    711 
    712     htable_geom_iterator_next(&it);
    713 
    714     if(!geom->is_enabled) continue;
    715 
    716     switch(geom->type) {
    717       case GEOM_MESH:
    718         res = mesh_compute_cdf(geom->data.mesh);
    719         if(res != RES_OK) goto error;
    720         len = darray_float_size_get(&geom->data.mesh->cdf);
    721         if(len) {
    722           area += darray_float_cdata_get(&geom->data.mesh->cdf)[len - 1];
    723         }
    724         break;
    725       case GEOM_SPHERE:
    726         len = 1;
    727         area += sphere_compute_area(geom->data.sphere);
    728         break;
    729       case GEOM_INSTANCE:
    730         /* The instance CDF was computed during its scnview  synchronisation */
    731         len = darray_fltui_size_get(&geom->data.instance->scnview->cdf);
    732         if(len) {
    733           area += darray_fltui_cdata_get
    734             (&geom->data.instance->scnview->cdf)[len - 1].flt;
    735         }
    736         break;
    737       default: FATAL("Unreachable code\n"); break;
    738     }
    739     fltui.ui = *shape_id;
    740     fltui.flt = area;
    741     if(len) {
    742       res = darray_fltui_push_back(&scnview->cdf, &fltui);
    743       if(res != RES_OK) goto error;
    744     }
    745   }
    746 exit:
    747   return res;
    748 error:
    749   darray_fltui_clear(&scnview->cdf);
    750   goto exit;
    751 }
    752 
    753 static res_T
    754 scene_view_compute_nprims_cdf
    755   (struct s3d_scene_view* scnview,
    756    const char store_cdf)
    757 {
    758   struct htable_geom_iterator it, end;
    759   size_t len;
    760   unsigned nprims;
    761   res_T res = RES_OK;
    762   ASSERT(scnview);
    763   ASSERT(darray_nprims_cdf_size_get(&scnview->nprims_cdf) == 0);
    764 
    765   htable_geom_begin(&scnview->cached_geoms, &it);
    766   htable_geom_end(&scnview->cached_geoms, &end);
    767 
    768   nprims = 0;
    769   while(!htable_geom_iterator_eq(&it, &end)) {
    770     const unsigned* shape_id = htable_geom_iterator_key_get(&it);
    771     struct geometry* geom = *htable_geom_iterator_data_get(&it);
    772     struct nprims_cdf cdf;
    773 
    774     htable_geom_iterator_next(&it);
    775 
    776     if(!geom->is_enabled) continue;
    777 
    778     geom->scene_prim_id_offset = nprims;
    779     switch(geom->type) {
    780       case GEOM_MESH:
    781         len = mesh_get_ntris(geom->data.mesh);
    782         nprims += (unsigned)len;
    783         break;
    784       case GEOM_SPHERE:
    785         len = 1;
    786         nprims += 1;
    787         break;
    788       case GEOM_INSTANCE:
    789         /* The instance CDF was computed during its scnview synchronisation */
    790         len = darray_nprims_cdf_size_get
    791           (&geom->data.instance->scnview->nprims_cdf);
    792         if(len) {
    793           nprims += darray_nprims_cdf_cdata_get
    794             (&geom->data.instance->scnview->nprims_cdf)[len - 1].nprims;
    795         }
    796         break;
    797       default: FATAL("Unreachable code\n"); break;
    798     }
    799 
    800     cdf.nprims = nprims;
    801     cdf.ishape = *shape_id;
    802     if(store_cdf && len) {
    803       res = darray_nprims_cdf_push_back(&scnview->nprims_cdf, &cdf);
    804       if(res != RES_OK) goto error;
    805     }
    806   }
    807 exit:
    808   return res;
    809 error:
    810   darray_nprims_cdf_clear(&scnview->nprims_cdf);
    811   goto exit;
    812 }
    813 
    814 static void
    815 scene_view_compute_scene_aabb(struct s3d_scene_view* scnview)
    816 {
    817   struct htable_geom_iterator it, end;
    818   float lower[3], upper[3];
    819 
    820   ASSERT(scnview->lower[0] == FLT_MAX && scnview->upper[0] == -FLT_MAX);
    821   ASSERT(scnview->lower[1] == FLT_MAX && scnview->upper[1] == -FLT_MAX);
    822   ASSERT(scnview->lower[2] == FLT_MAX && scnview->upper[2] == -FLT_MAX);
    823 
    824   htable_geom_begin(&scnview->cached_geoms, &it);
    825   htable_geom_end(&scnview->cached_geoms, &end);
    826 
    827   while(!htable_geom_iterator_eq(&it, &end)) {
    828     struct instance* inst;
    829     struct geometry* geom = *htable_geom_iterator_data_get(&it);
    830 
    831     htable_geom_iterator_next(&it);
    832 
    833     if(!geom->is_enabled) continue;
    834 
    835     switch(geom->type) {
    836       case GEOM_MESH:
    837         mesh_compute_aabb(geom->data.mesh, lower, upper);
    838         break;
    839       case GEOM_SPHERE:
    840         sphere_compute_aabb(geom->data.sphere, lower, upper);
    841         break;
    842       case GEOM_INSTANCE:
    843         /* Note that the instance AABB was computed during its scnview
    844          * synchronisation. */
    845         inst = geom->data.instance;
    846         if(aabb_is_degenerated(inst->scnview->lower, inst->scnview->upper)) {
    847           /* Empty scene */
    848           f3_splat(lower, FLT_MAX);
    849           f3_splat(upper,-FLT_MAX);
    850         } else {
    851           /* Transform local scene AABB bounds in world space */
    852           f33_mulf3(lower, inst->transform, inst->scnview->lower);
    853           f33_mulf3(upper, inst->transform, inst->scnview->upper);
    854           f3_add(lower, inst->transform + 9, lower);
    855           f3_add(upper, inst->transform + 9, upper);
    856           /* Define the world space AABB of the transformed local scene AABB */
    857           if(lower[0] > upper[0]) SWAP(float, lower[0], upper[0]);
    858           if(lower[1] > upper[1]) SWAP(float, lower[1], upper[1]);
    859           if(lower[2] > upper[2]) SWAP(float, lower[2], upper[2]);
    860         }
    861         break;
    862       default: FATAL("Unreachable code\n"); break;
    863     }
    864     f3_min(scnview->lower, scnview->lower, lower);
    865     f3_max(scnview->upper, scnview->upper, upper);
    866   }
    867 }
    868 
    869 static float
    870 scene_view_compute_volume
    871   (struct s3d_scene_view* scnview,
    872    const char flip_surface)
    873 {
    874   struct htable_geom_iterator it, end;
    875   float volume;
    876 
    877   ASSERT(scnview);
    878   htable_geom_begin(&scnview->cached_geoms, &it);
    879   htable_geom_end(&scnview->cached_geoms, &end);
    880 
    881   volume = 0.f;
    882   while(!htable_geom_iterator_eq(&it, &end)) {
    883     struct geometry* geom = *htable_geom_iterator_data_get(&it);
    884     const char flip = geom->flip_surface ^ flip_surface;
    885 
    886     htable_geom_iterator_next(&it);
    887 
    888     if(!geom->is_enabled) continue;
    889 
    890     switch(geom->type) {
    891       case GEOM_MESH:
    892         volume += mesh_compute_volume(geom->data.mesh, flip);
    893         break;
    894       case GEOM_SPHERE:
    895         volume += flip
    896           ? -sphere_compute_volume(geom->data.sphere)
    897           :  sphere_compute_volume(geom->data.sphere);
    898         break;
    899       case GEOM_INSTANCE:
    900         volume += scene_view_compute_volume(geom->data.instance->scnview, flip);
    901         break;
    902       default: FATAL("Unreachable code\n"); break;
    903     }
    904   }
    905 
    906   if(volume < 0.f) {
    907     log_warning(scnview->scn->dev,
    908 "%s:\n"
    909 "\tthe volume is negative. The scene shapes might not represent closed 2D\n"
    910 "\tmanifold volumes, or their surface normal might not point inward the volume.\n",
    911       FUNC_NAME);
    912   }
    913 
    914   return volume;
    915 }
    916 
    917 static res_T
    918 scene_view_sync
    919   (struct s3d_scene_view* scnview,
    920    const int mask,
    921    const struct s3d_accel_struct_conf* accel_struct_conf)
    922 {
    923   struct htable_shape_iterator it, end;
    924   res_T res = RES_OK;
    925 
    926   ASSERT(scnview && accel_struct_conf);
    927 
    928   /* Commit the scene shape to the scnview */
    929   htable_shape_begin(&scnview->scn->shapes, &it);
    930   htable_shape_end(&scnview->scn->shapes, &end);
    931   while(!htable_shape_iterator_eq(&it, &end)) {
    932     struct s3d_shape** pshape = htable_shape_iterator_data_get(&it);
    933     struct s3d_shape* shape = *pshape;
    934 
    935     switch(shape->type) {
    936       case GEOM_INSTANCE:
    937         res = scene_view_register_instance(scnview, shape, mask);
    938         break;
    939       case GEOM_MESH:
    940         res = scene_view_register_mesh(scnview, shape);
    941         break;
    942       case GEOM_SPHERE:
    943         res = scene_view_register_sphere(scnview, shape);
    944         break;
    945       default: FATAL("Unreachable code\n"); break;
    946     }
    947     if(res != RES_OK) goto error;
    948     htable_shape_iterator_next(&it);
    949   }
    950 
    951   /* Setup the scene for the S3D_TRACE scnview */
    952   if((mask & S3D_TRACE) != 0) {
    953     res = scene_view_setup_embree(scnview, accel_struct_conf);
    954     if(res != RES_OK) goto error;
    955   }
    956   /* Setup the scene for the S3D_SAMPLE scnview */
    957   if((mask & S3D_SAMPLE) != 0) {
    958     res = scene_view_compute_cdf(scnview);
    959     if(res != RES_OK) goto error;
    960   }
    961 
    962   /* Compute the scene AABB if it is updated */
    963   if(scnview->aabb_update) {
    964     if((mask & S3D_TRACE) == 0) {
    965       /* Compute from scratch */
    966       f3_splat(scnview->lower, FLT_MAX);
    967       f3_splat(scnview->upper,-FLT_MAX);
    968       scene_view_compute_scene_aabb(scnview);
    969     } else {
    970       /* Retrieve the scene AABB from Embree since it was already computed to
    971        * build the acceleration data structure */
    972       struct RTCBounds bounds;
    973       rtcGetSceneBounds(scnview->rtc_scn, &bounds);
    974       scnview->lower[0] = bounds.lower_x;
    975       scnview->lower[1] = bounds.lower_y;
    976       scnview->lower[2] = bounds.lower_z;
    977       scnview->upper[0] = bounds.upper_x;
    978       scnview->upper[1] = bounds.upper_y;
    979       scnview->upper[2] = bounds.upper_z;
    980     }
    981     scnview->aabb_update = 0;
    982   }
    983 
    984   /* Setup the scene for the scene_primitive_id/S3D_GET_PRIMITIVE scnview */
    985   res = scene_view_compute_nprims_cdf(scnview, (mask & S3D_GET_PRIMITIVE)!=0);
    986   if(res != RES_OK) goto error;
    987 
    988   scnview->mask = mask;
    989 
    990 exit:
    991   return res;
    992 error:
    993   goto exit;
    994 }
    995 
    996 static res_T
    997 scene_view_create(struct s3d_scene* scn, struct s3d_scene_view** out_scnview)
    998 {
    999   struct s3d_scene_view* scnview = NULL;
   1000   res_T res = RES_OK;
   1001   ASSERT(scn && out_scnview);
   1002 
   1003   if(!is_list_empty(&scn->scnviews)) {
   1004     /* Retrieve an already allocated scnview */
   1005     scnview = CONTAINER_OF(list_head(&scn->scnviews), struct s3d_scene_view, node);
   1006     list_del(&scnview->node);
   1007     ref_get(&scnview->ref);
   1008   } else {
   1009     scnview = (struct s3d_scene_view*)MEM_CALLOC
   1010       (scn->dev->allocator, 1, sizeof(struct s3d_scene_view));
   1011     if(!scnview) {
   1012       res = RES_MEM_ERR;
   1013       goto error;
   1014     }
   1015     list_init(&scnview->node);
   1016     htable_geom_init(scn->dev->allocator, &scnview->cached_geoms);
   1017     darray_fltui_init(scn->dev->allocator, &scnview->cdf);
   1018     darray_nprims_cdf_init(scn->dev->allocator, &scnview->nprims_cdf);
   1019     htable_instview_init(scn->dev->allocator, &scnview->instviews);
   1020     darray_uint_init(scn->dev->allocator, &scnview->detached_shapes);
   1021     f3_splat(scnview->lower, FLT_MAX);
   1022     f3_splat(scnview->upper,-FLT_MAX);
   1023     ref_init(&scnview->ref);
   1024     scnview->rtc_scn_build_quality = RTC_BUILD_QUALITY_MEDIUM;
   1025 
   1026     CLBK_INIT(&scnview->on_shape_detach_cb);
   1027     CLBK_SETUP(&scnview->on_shape_detach_cb, on_shape_detach, scnview);
   1028     SIG_CONNECT_CLBK(&scn->sig_shape_detach, &scnview->on_shape_detach_cb);
   1029   }
   1030   S3D(scene_ref_get(scn));
   1031   scnview->scn = scn;
   1032 exit:
   1033   *out_scnview = scnview;
   1034   return res;
   1035 error:
   1036   if(scnview) {
   1037     S3D(scene_view_ref_put(scnview));
   1038     scnview = NULL;
   1039   }
   1040   goto exit;
   1041 }
   1042 
   1043 static void
   1044 scene_view_release(ref_T* ref)
   1045 {
   1046   struct htable_instview_iterator it_view, end_view;
   1047   struct htable_geom_iterator it_geom, end_geom;
   1048   struct s3d_scene_view* scnview = CONTAINER_OF(ref, struct s3d_scene_view, ref);
   1049   size_t i, n;
   1050   ASSERT(ref);
   1051 
   1052   /* Release the scnview of the instances */
   1053   htable_instview_begin(&scnview->instviews, &it_view);
   1054   htable_instview_end(&scnview->instviews, &end_view);
   1055   while(!htable_instview_iterator_eq(&it_view, &end_view)) {
   1056     struct s3d_scene_view* view = *htable_instview_iterator_data_get(&it_view);
   1057     htable_instview_iterator_next(&it_view);
   1058     S3D(scene_view_ref_put(view));
   1059   }
   1060   htable_instview_clear(&scnview->instviews);
   1061 
   1062   /* Reset the scnview of the cached instance. Note that this step is actually
   1063    * not necessary but this reset is "appreciated" in debug */
   1064   htable_geom_begin(&scnview->cached_geoms, &it_geom);
   1065   htable_geom_end(&scnview->cached_geoms, &end_geom);
   1066   while(!htable_geom_iterator_eq(&it_geom, &end_geom)) {
   1067     struct geometry* geom = *htable_geom_iterator_data_get(&it_geom);
   1068     htable_geom_iterator_next(&it_geom);
   1069     if(geom->type != GEOM_INSTANCE) continue;
   1070     geom->data.instance->scnview = NULL;
   1071   }
   1072 
   1073   /* Remove the geometry of the shapes detached while the scnview was active */
   1074   n = darray_uint_size_get(&scnview->detached_shapes);
   1075   FOR_EACH(i, 0, n) {
   1076     const unsigned shape_id = darray_uint_cdata_get(&scnview->detached_shapes)[i];
   1077     struct geometry** pgeom = htable_geom_find(&scnview->cached_geoms, &shape_id);
   1078     struct geometry* geom = NULL;
   1079     size_t tmp; (void)tmp;
   1080 
   1081     /* It is possible that there is no geometry in the cache associated with the
   1082      * registered shape Id. This is the sign that this geometry  has already
   1083      * been removed from the cache, and that its shape has been detached several
   1084      * times from the scene while the scene view was active.
   1085      *
   1086      * Indeed, each detachment is simply saved in the "detached shapes" list,
   1087      * without checking that the shape has already been saved as being to be
   1088      * detached. In other words, only the first occurrence of the shape in the
   1089      * said list is valid, the others must be ignored. */
   1090     if(pgeom == NULL) { ASSERT(n > 0); continue; }
   1091 
   1092     geom = *pgeom;
   1093     scene_view_destroy_geometry(scnview, geom);
   1094     tmp = htable_geom_erase(&scnview->cached_geoms, &shape_id);
   1095     ASSERT(tmp == 1);
   1096     scnview->aabb_update = 1; /* The scene AABB must be reevaluated */
   1097   }
   1098   darray_uint_clear(&scnview->detached_shapes);
   1099 
   1100   /* Clear the scnview data structures excepted the cache of geometries and the
   1101    * scene AABB that will be used to speed up the future scnview creation */
   1102   darray_fltui_clear(&scnview->cdf);
   1103   darray_nprims_cdf_clear(&scnview->nprims_cdf);
   1104   scnview->mask = 0;
   1105   scnview->rtc_commit = 0;
   1106 
   1107   /* Do not physically release the memory space of the scnview. Add it to the
   1108    * available scnviews pool of the scene */
   1109   list_add(&scnview->scn->scnviews, &scnview->node);
   1110   S3D(scene_ref_put(scnview->scn));
   1111 }
   1112 
   1113 /*******************************************************************************
   1114  * Exported functions
   1115  ******************************************************************************/
   1116 res_T
   1117 s3d_scene_view_create
   1118   (struct s3d_scene* scn,
   1119    const int mask,
   1120    struct s3d_scene_view** out_scnview)
   1121 {
   1122   return s3d_scene_view_create2
   1123     (scn, mask, &S3D_ACCEL_STRUCT_CONF_DEFAULT, out_scnview);
   1124 }
   1125 
   1126 res_T
   1127 s3d_scene_view_create2
   1128   (struct s3d_scene* scn,
   1129    const int mask,
   1130    const struct s3d_accel_struct_conf* cfg,
   1131    struct s3d_scene_view** out_scnview)
   1132 {
   1133   struct s3d_scene_view* scnview = NULL;
   1134   const struct s3d_accel_struct_conf* accel_struct_conf = cfg;
   1135   res_T res = RES_OK;
   1136 
   1137   if(!scn || !out_scnview) {
   1138     res = RES_BAD_ARG;
   1139     goto error;
   1140   }
   1141 
   1142   if(!accel_struct_conf && (mask & S3D_TRACE)) {
   1143     accel_struct_conf = &S3D_ACCEL_STRUCT_CONF_DEFAULT;
   1144   }
   1145 
   1146   res = scene_view_create(scn, &scnview);
   1147   if(res != RES_OK) goto error;
   1148 
   1149   res = scene_view_sync(scnview, mask, accel_struct_conf);
   1150   if(res != RES_OK) goto error;
   1151 
   1152 exit:
   1153   if(out_scnview) *out_scnview = scnview;
   1154   return res;
   1155 error:
   1156   if(scnview) {
   1157     S3D(scene_view_ref_put(scnview));
   1158     scnview = NULL;
   1159   }
   1160   goto exit;
   1161 }
   1162 
   1163 res_T
   1164 s3d_scene_view_ref_get(struct s3d_scene_view* scnview)
   1165 {
   1166   if(!scnview) return RES_BAD_ARG;
   1167   ref_get(&scnview->ref);
   1168   return RES_OK;
   1169 }
   1170 
   1171 res_T
   1172 s3d_scene_view_ref_put(struct s3d_scene_view* scnview)
   1173 {
   1174   if(!scnview) return RES_BAD_ARG;
   1175   ref_put(&scnview->ref, scene_view_release);
   1176   return RES_OK;
   1177 }
   1178 
   1179 res_T
   1180 s3d_scene_view_get_mask(struct s3d_scene_view* scnview, int* mask)
   1181 {
   1182   if(!scnview || !mask) return RES_BAD_ARG;
   1183   *mask = scnview->mask;
   1184   return RES_OK;
   1185 }
   1186 
   1187 res_T
   1188 s3d_scene_view_sample
   1189   (struct s3d_scene_view* scnview,
   1190    const float u,
   1191    const float v,
   1192    const float w,
   1193    struct s3d_primitive* primitive, /* sampled primitive */
   1194    float st[2])
   1195 {
   1196   struct geometry** pgeom;
   1197   struct geometry* geom;
   1198   size_t sz;
   1199   size_t i;
   1200   const struct fltui* fltui, *fltui_found;
   1201   const float* flt, *flt_found;
   1202   unsigned ishape;
   1203   float f;
   1204   res_T res = RES_OK;
   1205 
   1206   if(!scnview || !primitive || !st) {
   1207     res = RES_BAD_ARG;
   1208     goto error;
   1209   }
   1210   /* Expecting canonic numbers */
   1211   if(u < 0.f || u >= 1.f || v < 0.f || v >= 1.f || w < 0.f || w >= 1.f) {
   1212     log_error(scnview->scn->dev,
   1213       "%s: the submitted numbers are not canonical, i.e. they are not in [0, 1[.\n",
   1214       FUNC_NAME);
   1215     res = RES_BAD_ARG;
   1216     goto error;
   1217   }
   1218   if((scnview->mask & S3D_SAMPLE) == 0) {
   1219     log_error(scnview->scn->dev,
   1220       "%s: no active S3D_SAMPLE scnview on the submitted scene.\n",
   1221       FUNC_NAME);
   1222     res = RES_BAD_OP;
   1223     goto error;
   1224   }
   1225 
   1226   /* Find the sampled geometry */
   1227   if(darray_fltui_size_get(&scnview->cdf) == 0) {
   1228     /* No geometry to sample */
   1229     *primitive = S3D_PRIMITIVE_NULL;
   1230     goto exit;
   1231   } else if(darray_fltui_size_get(&scnview->cdf) == 1) {
   1232     ishape = darray_fltui_cdata_get(&scnview->cdf)[0].ui;
   1233     /* Map u to the CDF bounds */
   1234     f = u * darray_fltui_cdata_get(&scnview->cdf)[0].flt;
   1235   } else {
   1236     fltui = darray_fltui_cdata_get(&scnview->cdf);
   1237     sz = darray_fltui_size_get(&scnview->cdf);
   1238     f = u * fltui[sz-1].flt; /* Map u to [0, SceneArea[ */
   1239     fltui_found = search_lower_bound
   1240       (&f, fltui, sz, sizeof(*fltui), cmp_float_to_fltui);
   1241     ASSERT(fltui_found);
   1242 
   1243     /* search_lower_bound returns the first entry that is not less than `f'.
   1244      * The following code discards entries that are also `equal' to `f' */
   1245     i = (size_t)(fltui_found - fltui);
   1246     while(fltui[i].flt == f && i < sz) ++i;
   1247     ASSERT(i < sz);
   1248 
   1249     fltui_found = fltui + i;
   1250     ishape = fltui_found->ui;
   1251 
   1252     /* Map f to [0, <Shape|Instance>Area[ */
   1253     if(i) f -= fltui_found[-1].flt;
   1254   }
   1255   pgeom = htable_geom_find(&scnview->cached_geoms, &ishape);
   1256   ASSERT(pgeom);
   1257   geom = *pgeom;
   1258 
   1259   if(geom->type == GEOM_MESH || geom->type == GEOM_SPHERE) {
   1260     primitive->inst__ = NULL;
   1261     primitive->inst_id = S3D_INVALID_ID;
   1262     primitive->scene_prim_id = 0;
   1263   } else {
   1264     /* Find the sampled instantiated geometry */
   1265     ASSERT(geom->type == GEOM_INSTANCE);
   1266     primitive->inst__ = geom;
   1267     primitive->inst_id = geom->name;
   1268     primitive->scene_prim_id = geom->scene_prim_id_offset;
   1269     if(darray_fltui_size_get(&geom->data.instance->scnview->cdf) == 1) {
   1270       ishape = darray_fltui_cdata_get(&geom->data.instance->scnview->cdf)[0].ui;
   1271     } else {
   1272       fltui = darray_fltui_cdata_get(&geom->data.instance->scnview->cdf);
   1273       sz = darray_fltui_size_get(&geom->data.instance->scnview->cdf);
   1274       fltui_found = search_lower_bound
   1275         (&f, fltui, sz, sizeof(*fltui), cmp_float_to_fltui);
   1276       ASSERT(fltui_found);
   1277 
   1278       /* search_lower_bound returns the first entry that is not less than `f'.
   1279        * The following code discards entries that are also `equal' to `f' */
   1280       i = (size_t)(fltui_found - fltui);
   1281       while(fltui[i].flt == f && i < sz) ++i;
   1282       ASSERT(i < sz);
   1283 
   1284       fltui_found = fltui + i;
   1285       ishape = fltui_found->ui;
   1286 
   1287       /* Map `f' to [0, ShapeArea[ */
   1288       if(i) f -= fltui_found[-1].flt;
   1289     }
   1290     pgeom = htable_geom_find(&geom->data.instance->scnview->cached_geoms, &ishape);
   1291     ASSERT(pgeom);
   1292     geom = *pgeom;
   1293   }
   1294 
   1295   /* Find the sampled primitive */
   1296   primitive->shape__ = geom;
   1297   primitive->geom_id = geom->name;
   1298   primitive->scene_prim_id += geom->scene_prim_id_offset;
   1299   if(geom->type == GEOM_SPHERE) {
   1300     primitive->prim_id = 0;
   1301   } else if(geom->type == GEOM_MESH) {
   1302     flt = darray_float_cdata_get(&geom->data.mesh->cdf);
   1303     sz = darray_float_size_get(&geom->data.mesh->cdf);
   1304     flt_found = search_lower_bound(&f, flt, sz, sizeof(*flt), cmp_float);
   1305     ASSERT(flt_found != NULL);
   1306     i = (size_t)(flt_found - flt);
   1307 
   1308     /* search_lower_bound returns the first entry that is not less than `f'.
   1309      * The following code discards entries that are also `equal' to `f' */
   1310     while(flt[i] == f && i < sz) ++i;
   1311     ASSERT(i < sz);
   1312 
   1313     primitive->prim_id = (unsigned)i;
   1314 
   1315   } else {
   1316     FATAL("Unreachable code\n");
   1317   }
   1318   primitive->scene_prim_id += primitive->prim_id;
   1319   S3D(primitive_sample(primitive, v, w, st));
   1320 
   1321 exit:
   1322   return res;
   1323 error:
   1324   goto exit;
   1325 }
   1326 
   1327 res_T
   1328 s3d_scene_view_get_primitive
   1329   (struct s3d_scene_view* scnview,
   1330    const unsigned iprim,
   1331    struct s3d_primitive* prim)
   1332 {
   1333   struct geometry** pgeom;
   1334   struct geometry* geom;
   1335   const struct nprims_cdf* begin, *found;
   1336   size_t sz;
   1337   size_t nprims;
   1338   unsigned ishape;
   1339   size_t i;
   1340   res_T res = RES_OK;
   1341 
   1342   if(!scnview || !prim) {
   1343     res = RES_BAD_ARG;
   1344     goto error;
   1345   }
   1346   if((scnview->mask & S3D_GET_PRIMITIVE) == 0) {
   1347     log_error(scnview->scn->dev,
   1348       "%s: no active S3D_GET_PRIMITIVE scnview on the submitted scene.\n",
   1349       FUNC_NAME);
   1350     res = RES_BAD_OP;
   1351     goto error;
   1352   }
   1353   S3D(scene_view_primitives_count(scnview, &nprims));
   1354   if(iprim  >= nprims) {
   1355     log_error(scnview->scn->dev,
   1356       "%s: the primitive index %u exceeds the number of scene primitives %u.\n",
   1357       FUNC_NAME, iprim, (unsigned)nprims);
   1358     res = RES_BAD_ARG;
   1359     goto error;
   1360   }
   1361 
   1362   i = iprim;
   1363   if(darray_nprims_cdf_size_get(&scnview->nprims_cdf) == 1) {
   1364     ishape = darray_nprims_cdf_cdata_get(&scnview->nprims_cdf)[0].ishape;
   1365   } else {
   1366     begin = darray_nprims_cdf_cdata_get(&scnview->nprims_cdf);
   1367     sz = darray_nprims_cdf_size_get(&scnview->nprims_cdf);
   1368     found = search_lower_bound
   1369       (&i, begin, sz, sizeof(*begin), cmp_size_t_to_nprims_cdf);
   1370     ASSERT(found != NULL);
   1371     ishape = found->ishape;
   1372     if(found != begin) {
   1373       ASSERT(i >= found[-1].nprims);
   1374       i -= found[-1].nprims;
   1375     }
   1376   }
   1377   pgeom = htable_geom_find(&scnview->cached_geoms, &ishape);
   1378   ASSERT(pgeom);
   1379   geom = *pgeom;
   1380 
   1381   if(geom->type == GEOM_MESH || geom->type == GEOM_SPHERE) {
   1382     prim->inst__ = NULL;
   1383     prim->inst_id = S3D_INVALID_ID;
   1384     prim->scene_prim_id = 0;
   1385   } else {
   1386     ASSERT(geom->type == GEOM_INSTANCE);
   1387     prim->inst__ = geom;
   1388     prim->inst_id = geom->name;
   1389     prim->scene_prim_id = geom->scene_prim_id_offset;
   1390     if(darray_nprims_cdf_size_get(&geom->data.instance->scnview->nprims_cdf)==1) {
   1391       ishape = darray_nprims_cdf_cdata_get
   1392         (&geom->data.instance->scnview->nprims_cdf)[0].ishape;
   1393     } else {
   1394       begin = darray_nprims_cdf_cdata_get
   1395         (&geom->data.instance->scnview->nprims_cdf);
   1396       sz = darray_nprims_cdf_size_get
   1397         (&geom->data.instance->scnview->nprims_cdf);
   1398       found = search_lower_bound
   1399         (&i, begin, sz, sizeof(*begin), cmp_size_t_to_nprims_cdf);
   1400       ASSERT(found != NULL);
   1401       ishape = found->ishape;
   1402       if(found != begin) {
   1403         ASSERT(i >= found[-1].nprims);
   1404         i -= found[-1].nprims;
   1405       }
   1406     }
   1407     pgeom = htable_geom_find(&geom->data.instance->scnview->cached_geoms, &ishape);
   1408     ASSERT(pgeom);
   1409     geom = *pgeom;
   1410   }
   1411   ASSERT(geom->type == GEOM_MESH || geom->type == GEOM_SPHERE);
   1412   ASSERT(geom->type != GEOM_MESH || i < mesh_get_ntris(geom->data.mesh));
   1413   prim->shape__ = geom;
   1414   prim->geom_id = geom->name;
   1415   prim->prim_id = (unsigned)i;
   1416   prim->scene_prim_id += geom->scene_prim_id_offset;
   1417   prim->scene_prim_id += prim->prim_id;
   1418 
   1419 exit:
   1420   return res;
   1421 error:
   1422   goto exit;
   1423 }
   1424 
   1425 res_T
   1426 s3d_scene_view_primitives_count(struct s3d_scene_view* scnview, size_t* prims_count)
   1427 {
   1428   res_T res = RES_OK;
   1429 
   1430   if(!scnview || !prims_count) {
   1431     res = RES_BAD_ARG;
   1432     goto error;
   1433   }
   1434   if((scnview->mask & S3D_GET_PRIMITIVE) != 0) {
   1435     const size_t len = darray_nprims_cdf_size_get(&scnview->nprims_cdf);
   1436     if(!len) {
   1437       *prims_count = 0;
   1438     } else {
   1439       *prims_count = darray_nprims_cdf_cdata_get
   1440         (&scnview->nprims_cdf)[len - 1].nprims;
   1441     }
   1442   } else {
   1443     struct htable_geom_iterator it, end;
   1444     size_t inst_count;
   1445 
   1446     htable_geom_begin(&scnview->cached_geoms, &it);
   1447     htable_geom_end(&scnview->cached_geoms, &end);
   1448     *prims_count = 0;
   1449     while(!htable_geom_iterator_eq(&it, &end)) {
   1450       struct geometry** pgeom = htable_geom_iterator_data_get(&it);
   1451       struct geometry* geom = *pgeom;
   1452       htable_geom_iterator_next(&it);
   1453 
   1454       if(!geom->is_enabled) continue;
   1455 
   1456       switch(geom->type) {
   1457         case GEOM_MESH:
   1458           *prims_count += mesh_get_ntris(geom->data.mesh);
   1459           break;
   1460         case GEOM_SPHERE:
   1461           *prims_count += 1;
   1462           break;
   1463         case GEOM_INSTANCE:
   1464           S3D(scene_view_primitives_count(geom->data.instance->scnview, &inst_count));
   1465           *prims_count += inst_count;
   1466           break;
   1467         default: FATAL("Unreachable code\n"); break;
   1468       }
   1469     }
   1470   }
   1471 exit:
   1472   return res;
   1473 error:
   1474   goto exit;
   1475 }
   1476 
   1477 res_T
   1478 s3d_scene_view_compute_area(struct s3d_scene_view* scnview, float* out_area)
   1479 {
   1480   float area;
   1481   res_T res = RES_OK;
   1482 
   1483   if(!scnview || !out_area) {
   1484     res = RES_BAD_ARG;
   1485     goto error;
   1486   }
   1487   if((scnview->mask & S3D_SAMPLE) != 0) {
   1488     /* Retrieve the overall scene area from the scene cumulative distribution
   1489      * function */
   1490     size_t len = darray_fltui_size_get(&scnview->cdf);
   1491     if(!len) {
   1492       area = 0.f;
   1493     } else {
   1494       area = darray_fltui_cdata_get(&scnview->cdf)[len - 1].flt;
   1495     }
   1496   } else {
   1497     struct htable_geom_iterator it, end;
   1498     float inst_area;
   1499 
   1500     htable_geom_begin(&scnview->cached_geoms, &it);
   1501     htable_geom_end(&scnview->cached_geoms, &end);
   1502 
   1503     area = 0.f;
   1504     while(!htable_geom_iterator_eq(&it, &end)) {
   1505       struct geometry** pgeom = htable_geom_iterator_data_get(&it);
   1506       struct geometry* geom = *pgeom;
   1507 
   1508       htable_geom_iterator_next(&it);
   1509 
   1510       if(!geom->is_enabled) continue;
   1511 
   1512       switch(geom->type) {
   1513         case GEOM_MESH:
   1514           area += mesh_compute_area(geom->data.mesh);
   1515           break;
   1516         case GEOM_SPHERE:
   1517           area += sphere_compute_area(geom->data.sphere);
   1518           break;
   1519         case GEOM_INSTANCE:
   1520           /* TODO take into account the instance scale factor */
   1521           S3D(scene_view_compute_area(geom->data.instance->scnview, &inst_area));
   1522           area += inst_area;
   1523           break;
   1524         default: FATAL("Unreachable code\n"); break;
   1525       }
   1526     }
   1527   }
   1528 
   1529 exit:
   1530   if(out_area) *out_area = area;
   1531   return res;
   1532 error:
   1533   area = -1.f;
   1534   goto exit;
   1535 }
   1536 
   1537 res_T
   1538 s3d_scene_view_compute_volume(struct s3d_scene_view* scnview, float* out_volume)
   1539 {
   1540   if(!scnview || !out_volume) return RES_BAD_ARG;
   1541   *out_volume = scene_view_compute_volume(scnview, 0/*No initial flip_surface*/);
   1542   return RES_OK;
   1543 }
   1544 
   1545 res_T
   1546 s3d_scene_view_get_aabb(struct s3d_scene_view* scnview, float lower[3], float upper[3])
   1547 {
   1548   if(!scnview || !lower || !upper) return RES_BAD_ARG;
   1549   f3_set(lower, scnview->lower);
   1550   f3_set(upper, scnview->upper);
   1551   return RES_OK;
   1552 }
   1553 
   1554 /*******************************************************************************
   1555  * Local functions
   1556  ******************************************************************************/
   1557 void
   1558 scene_view_destroy(struct s3d_scene_view* scnview)
   1559 {
   1560   struct htable_geom_iterator it, end;
   1561   ASSERT(scnview && !is_list_empty(&scnview->node)/*Not in use*/);
   1562   ASSERT(scnview->mask == 0);
   1563 
   1564   /* Delete the cached geometries */
   1565   htable_geom_begin(&scnview->cached_geoms, &it);
   1566   htable_geom_end(&scnview->cached_geoms, &end);
   1567   while(!htable_geom_iterator_eq(&it, &end)) {
   1568     struct geometry** pgeom = htable_geom_iterator_data_get(&it);
   1569     struct geometry* geom = *pgeom;
   1570     scene_view_destroy_geometry(scnview, geom);
   1571     htable_geom_iterator_next(&it);
   1572   }
   1573 
   1574   /* Delete the back-end scene */
   1575   if(scnview->rtc_scn) rtcReleaseScene(scnview->rtc_scn);
   1576 
   1577   /* Release internal data structure */
   1578   htable_geom_release(&scnview->cached_geoms);
   1579   darray_fltui_release(&scnview->cdf);
   1580   darray_nprims_cdf_release(&scnview->nprims_cdf);
   1581   htable_instview_release(&scnview->instviews);
   1582   darray_uint_release(&scnview->detached_shapes);
   1583 
   1584   /* Remove the scnview from its pool */
   1585   list_del(&scnview->node);
   1586   CLBK_DISCONNECT(&scnview->on_shape_detach_cb);
   1587 
   1588   /* Free the scnview memory space */
   1589   MEM_RM(scnview->scn->dev->allocator, scnview);
   1590 }
   1591