star-3d

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

s3d_scene_view.c (48634B)


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