star-2d

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

s2d_scene_view.c (36589B)


      1 /* Copyright (C) 2016-2021, 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 "s2d.h"
     17 #include "s2d_c.h"
     18 #include "s2d_device_c.h"
     19 #include "s2d_line_segments.h"
     20 #include "s2d_geometry.h"
     21 #include "s2d_scene_c.h"
     22 #include "s2d_scene_view_c.h"
     23 #include "s2d_shape_c.h"
     24 
     25 #include <rsys/algorithm.h>
     26 #include <rsys/float2.h>
     27 #include <rsys/float3.h>
     28 #include <rsys/mem_allocator.h>
     29 
     30 #include <limits.h>
     31 
     32 /* Number of floats added to the vertex position in order to ensure the Embree
     33  * vertex padding constraint */
     34 #define POSITION_PADDING 1
     35 
     36 struct intersect_context {
     37   struct RTCRayQueryContext rtc;
     38   struct s2d_scene_view* scnview;
     39   void* data; /* User defined data */
     40   float ws_org[2]; /* World space ray origin */
     41   float ws_dir[3]; /* World space ray direction */
     42   float ws_range[2]; /* World space ray range */
     43   float cos_dir_dir2d; /* Cosine between the 3D ws_dir and its 2D projection */
     44   int rt_3d; /* Define if the ray is traced in 3D */
     45 };
     46 
     47 /*******************************************************************************
     48  * Helper functions
     49  ******************************************************************************/
     50 static INLINE int
     51 cmp_float(const void* a, const void* b)
     52 {
     53   const float key = *(const float*)a;
     54   const float val = *(const float*)b;
     55   if(key < val) return -1;
     56   if(key > val) return +1;
     57   return 0;
     58 }
     59 
     60 static INLINE int
     61 cmp_float_to_fltui(const void* a, const void* b)
     62 {
     63   const float key = *(const float*)a;
     64   const struct fltui* fltui = (const struct fltui*)b;
     65   if(key < fltui->flt) return -1;
     66   if(key > fltui->flt) return +1;
     67   return 0;
     68 }
     69 
     70 static INLINE int
     71 cmp_size_t_to_nprims_cdf(const void* a, const void* b)
     72 {
     73   const size_t key = *(const size_t*)a;
     74   const struct nprims_cdf* nprims_cdf = (const struct nprims_cdf*)b;
     75   if(key < nprims_cdf->nprims-1) return -1;
     76   if(key > nprims_cdf->nprims-1) return +1;
     77   return 0;
     78 }
     79 
     80 static INLINE void
     81 scene_view_destroy_geometry(struct s2d_scene_view* scnview, struct geometry* geom)
     82 {
     83   ASSERT(geom);
     84   if(geom->rtc) {
     85     if(geom->rtc_id != RTC_INVALID_GEOMETRY_ID) {
     86       rtcDetachGeometry(scnview->rtc_scn, geom->rtc_id);
     87       geom->rtc_id = RTC_INVALID_GEOMETRY_ID;
     88     }
     89     rtcReleaseGeometry(geom->rtc);
     90     geom->rtc = NULL;
     91     scnview->rtc_scn_update = 1; /* Notify the scene upd */
     92   }
     93   geometry_ref_put(geom);
     94 }
     95 
     96 static void
     97 on_shape_detach
     98   (const struct s2d_scene* scn,
     99    const struct s2d_shape* shape,
    100    void* data)
    101 {
    102   struct geometry** pgeom;
    103   struct geometry* geom;
    104   struct s2d_scene_view* scnview = (struct s2d_scene_view*)data;
    105   unsigned shape_id;
    106   ASSERT(scn && shape && data);
    107   (void)scn;
    108 
    109   S2D(shape_get_id(shape, &shape_id));
    110   pgeom = htable_geom_find(&scnview->cached_geoms, &shape_id);
    111 
    112   /* The scnview did not register a geometry for this shape. Ignore the signal */
    113   if(!pgeom) return;
    114 
    115   geom = *pgeom;
    116   if(scnview->mask == 0) {
    117     /* The scnview is NOT in use. Directly rm the cached geometry */
    118     size_t n; (void)n;
    119     scene_view_destroy_geometry(scnview, geom);
    120     n = htable_geom_erase(&scnview->cached_geoms, &shape_id);
    121     ASSERT(n == 1);
    122   } else {
    123     /* The scnview is in use. Delay the deletion of the cached geometry */
    124     res_T res = darray_uint_push_back(&scnview->detached_shapes, &shape_id);
    125     if(res != RES_OK) FATAL("Insufficient memory.\n");
    126   }
    127 }
    128 
    129 static INLINE void
    130 hit_setup
    131   (struct s2d_scene_view* scnview,
    132    const struct RTCRayHit* ray_hit,
    133    struct s2d_hit* hit)
    134 {
    135   struct geometry* geom;
    136   ASSERT(scnview && hit && ray_hit);
    137 
    138   if(ray_hit->hit.geomID == RTC_INVALID_GEOMETRY_ID) { /* No hit */
    139     *hit = S2D_HIT_NULL;
    140     return;
    141   }
    142   ASSERT(eq_epsf(ray_hit->hit.Ng_z, 0.f, 1.e-6f));
    143   ASSERT((unsigned)(ray_hit->hit.instID[0]) == RTC_INVALID_GEOMETRY_ID);
    144 
    145   hit->normal[0] = ray_hit->hit.Ng_x;
    146   hit->normal[1] = ray_hit->hit.Ng_y;
    147   hit->distance = ray_hit->ray.tfar;
    148 
    149   /* In Embree3 the normal orientation is flipped wrt to Star-2D convention */
    150   #if RTC_VERSION_MAJOR >= 3
    151   f2_minus(hit->normal, hit->normal);
    152   #endif
    153 
    154   geom  = scene_view_geometry_from_embree_id(scnview, ray_hit->hit.geomID);
    155   hit->prim.mesh__ = geom;
    156   hit->prim.prim_id = ray_hit->hit.primID;
    157   hit->prim.geom_id = geom->name;
    158   hit->prim.scene_prim_id = hit->prim.prim_id + geom->scene_prim_id_offset;
    159 
    160   /* The Embree "v" parametric coordinate of the extruded quad corresponds to
    161    * the edge parametric coordinate (handle Embree returning v out of range) */
    162   hit->u = CLAMP(ray_hit->hit.v, 0, 1);
    163 
    164   if(geom->flip_contour) {
    165     f2_minus(hit->normal, hit->normal);
    166   }
    167 }
    168 
    169 static res_T
    170 embree_geometry_register
    171   (struct s2d_scene_view* scnview,
    172    struct geometry* geom)
    173 {
    174   ASSERT(scnview);
    175 
    176   /* Create the Embree geometry if it is not valid */
    177   if(geom->rtc == NULL) {
    178     geom->rtc = rtcNewGeometry(scnview->scn->dev->rtc, RTC_GEOMETRY_TYPE_QUAD);
    179     if(geom->rtc == NULL)
    180       return RES_UNKNOWN_ERR;
    181 
    182     /* Set the Star-2D representation of the geometry to the Embree geometry */
    183     rtcSetGeometryUserData(geom->rtc, geom);
    184 
    185     /* Attach the Embree geometry to the Embree scene of the scene view */
    186     geom->rtc_id = rtcAttachGeometry(scnview->rtc_scn, geom->rtc);
    187 
    188     scnview->rtc_scn_update = 1;
    189   }
    190   return RES_OK;
    191 }
    192 
    193 static res_T
    194 embree_geometry_setup_positions
    195   (struct s2d_scene_view* scnview, struct geometry* geom)
    196 {
    197   RTCBuffer buf = NULL;
    198   size_t nverts;
    199   size_t i;
    200   float* verts;
    201   float* rtc_verts;
    202   res_T res = RES_OK;
    203   ASSERT(scnview && geom && geom->rtc);
    204 
    205   nverts = line_segments_get_nverts(geom->lines);
    206   verts = line_segments_get_attr(geom->lines, S2D_POSITION);
    207 
    208   buf = rtcNewBuffer
    209     (scnview->scn->dev->rtc,
    210      nverts*2*sizeof(float[3]) + sizeof(float)*POSITION_PADDING);
    211   if(!buf) {
    212     res = rtc_error_to_res_T(rtcGetDeviceError(scnview->scn->dev->rtc));
    213     goto error;
    214   }
    215 
    216   /* Extrude segment vertices as quad whose Z is [-1, 1] */
    217   rtc_verts = rtcGetBufferData(buf);
    218   FOR_EACH(i, 0, nverts) {
    219     size_t ivert = i*2/*#coords per vertex*/;
    220     size_t rtc_ivert = i*3/*#coords*/ * 2/*#rtc vertices per line vertex*/;
    221 
    222     rtc_verts[rtc_ivert + 0] = verts[ivert + 0];
    223     rtc_verts[rtc_ivert + 1] = verts[ivert + 1];
    224     rtc_verts[rtc_ivert + 2] = 1.f;
    225 
    226     rtc_verts[rtc_ivert + 3] = verts[ivert + 0];
    227     rtc_verts[rtc_ivert + 4] = verts[ivert + 1];
    228     rtc_verts[rtc_ivert + 5] = -1.f;
    229   }
    230 
    231   rtcSetGeometryBuffer(geom->rtc, RTC_BUFFER_TYPE_VERTEX, 0/*slot*/,
    232     RTC_FORMAT_FLOAT3, buf, 0/*offset*/, sizeof(float[3])/*stride*/, nverts*2);
    233   rtcUpdateGeometryBuffer(geom->rtc, RTC_BUFFER_TYPE_VERTEX, 0/*slot*/);
    234 
    235 exit:
    236   if(buf) rtcReleaseBuffer(buf);
    237   return res;
    238 error:
    239   goto exit;
    240 }
    241 
    242 
    243 static INLINE res_T
    244 embree_geometry_setup_indices
    245   (struct s2d_scene_view* scnview, struct geometry* geom)
    246 {
    247   RTCBuffer buf = NULL;
    248   size_t nsegs;
    249   size_t i;
    250   uint32_t* ids;
    251   uint32_t* rtc_ids;
    252   res_T res = RES_OK;
    253   ASSERT(scnview && geom && geom->rtc);
    254 
    255   /* Define the index of the extruded line segments */
    256   nsegs = line_segments_get_nsegments(geom->lines);
    257   ids = line_segments_get_ids(geom->lines);
    258 
    259   buf = rtcNewBuffer(scnview->scn->dev->rtc, nsegs*sizeof(uint32_t[4]));
    260   if(!buf) {
    261     res = rtc_error_to_res_T(rtcGetDeviceError(scnview->scn->dev->rtc));
    262     goto error;
    263   }
    264 
    265   /* Define the index of the extruded line segments */
    266   rtc_ids = rtcGetBufferData(buf);
    267   FOR_EACH(i, 0, nsegs) {
    268     size_t id = i*2/*#ids per segment*/;
    269     size_t rtc_id = i*4/*#ids per quad*/;
    270 
    271     rtc_ids[rtc_id + 0] = ids[id + 0] * 2/*#rtc vertices per line vertex*/ + 1;
    272     rtc_ids[rtc_id + 1] = ids[id + 0] * 2/*#rtc vertices per line vertex*/ + 0;
    273     rtc_ids[rtc_id + 2] = ids[id + 1] * 2/*#rtc vertices per line vertex*/ + 0;
    274     rtc_ids[rtc_id + 3] = ids[id + 1] * 2/*#rtc vertices per line vertex*/ + 1;
    275   }
    276 
    277   rtcSetGeometryBuffer(geom->rtc,RTC_BUFFER_TYPE_INDEX, 0/*slot*/,
    278     RTC_FORMAT_UINT4, buf, 0/*offset*/, sizeof(uint32_t[4])/*stride*/, nsegs);
    279   rtcUpdateGeometryBuffer(geom->rtc, RTC_BUFFER_TYPE_INDEX, 0/*slot*/);
    280 
    281 exit:
    282   if(buf) rtcReleaseBuffer(buf);
    283   return res;
    284 error:
    285   goto exit;
    286 }
    287 
    288 static INLINE void
    289 embree_geometry_setup_enable_state
    290   (struct s2d_scene_view* scnview, struct geometry* geom)
    291 {
    292   ASSERT(scnview && geom && geom->rtc);
    293   (void)scnview;
    294   if(geom->is_enabled) {
    295     rtcEnableGeometry(geom->rtc);
    296   } else {
    297     rtcDisableGeometry(geom->rtc);
    298   }
    299 }
    300 
    301 static INLINE void
    302 embree_geometry_setup_filter_function
    303   (struct s2d_scene_view* scnview, struct geometry* geom)
    304 {
    305   ASSERT(scnview && geom && geom->rtc);
    306   (void)scnview;
    307   if(!geom->lines->filter.func) {
    308     rtcSetGeometryIntersectFilterFunction(geom->rtc, NULL);
    309   } else {
    310     rtcSetGeometryIntersectFilterFunction(geom->rtc, rtc_hit_filter_wrapper);
    311   }
    312 }
    313 
    314 static INLINE res_T
    315 scene_view_setup_embree(struct s2d_scene_view* scnview)
    316 {
    317   struct htable_geom_iterator it, end;
    318   int rtc_outdated = 0;
    319   res_T res = RES_OK;
    320   ASSERT(scnview);
    321 
    322   /* The rtc_scn could be already allocated since the scene views are cached */
    323   if(!scnview->rtc_scn) {
    324     scnview->rtc_scn = rtcNewScene(scnview->scn->dev->rtc);
    325     if(!scnview->rtc_scn) {
    326       res = rtc_error_to_res_T(rtcGetDeviceError(scnview->scn->dev->rtc));
    327       goto error;
    328     }
    329     rtcSetSceneFlags
    330       (scnview->rtc_scn, RTC_SCENE_FLAG_ROBUST | RTC_SCENE_FLAG_DYNAMIC);
    331     rtc_outdated = 1;
    332   }
    333 
    334   htable_geom_begin(&scnview->cached_geoms, &it);
    335   htable_geom_end(&scnview->cached_geoms, &end);
    336 
    337   while(!htable_geom_iterator_eq(&it, &end)) {
    338     struct geometry** pgeom = htable_geom_iterator_data_get(&it);
    339     struct geometry* geom = *pgeom;
    340 
    341     htable_geom_iterator_next(&it);
    342 
    343     /* Define whether or not the embree scene is outdated */
    344     if(geom->embree_outdated_mask) rtc_outdated = 1;
    345 
    346     /* Register the embree geometry */
    347     res = embree_geometry_register(scnview, geom);
    348     if(res != RES_OK) goto error;
    349 
    350     /* Flush the embree geometry states */
    351     if((geom->embree_outdated_mask & EMBREE_VERTICES) != 0) {
    352       res = embree_geometry_setup_positions(scnview, geom);
    353       if(res != RES_OK) goto error;
    354     }
    355     if((geom->embree_outdated_mask & EMBREE_INDICES) != 0) {
    356       res = embree_geometry_setup_indices(scnview, geom);
    357       if(res != RES_OK) goto error;
    358     }
    359     if((geom->embree_outdated_mask & EMBREE_ENABLE) != 0)
    360       embree_geometry_setup_enable_state(scnview, geom);
    361     if((geom->embree_outdated_mask & EMBREE_FILTER_FUNCTION) != 0)
    362       embree_geometry_setup_filter_function(scnview, geom);
    363 
    364     /* Commit the updated geometry */
    365     if(geom->embree_outdated_mask)
    366       rtcCommitGeometry(geom->rtc);
    367 
    368     geom->embree_outdated_mask = 0;
    369   }
    370 
    371   rtc_outdated = rtc_outdated || scnview->rtc_scn_update;
    372 
    373   /* Commit the embree changes */
    374   if(rtc_outdated) {
    375     rtcCommitScene(scnview->rtc_scn);
    376     scnview->rtc_commit = 1; /* Notify that the scene view was committed */
    377     scnview->rtc_scn_update = 0;
    378   }
    379 
    380 exit:
    381   return res;
    382 error:
    383   if(scnview->rtc_scn) {
    384     rtcReleaseScene(scnview->rtc_scn);
    385     scnview->rtc_scn = NULL;
    386   }
    387   goto exit;
    388 }
    389 
    390 static res_T
    391 scene_view_register_line_segments
    392   (struct s2d_scene_view* scnview,
    393    struct s2d_shape* shape)
    394 {
    395   struct geometry** pgeom = NULL;
    396   struct geometry* geom = NULL;
    397   size_t iattr;
    398   unsigned shape_id;
    399   int is_valid;
    400   res_T res = RES_OK;
    401   ASSERT(scnview && shape);
    402 
    403   is_valid = shape->lines->indices && shape->lines->attribs[S2D_POSITION];
    404 
    405   /* Retrieved the cached geometry */
    406   S2D(shape_get_id(shape, &shape_id));
    407   pgeom = htable_geom_find(&scnview->cached_geoms, &shape_id);
    408   if(pgeom) {
    409     geom = *pgeom;
    410     if(!is_valid) {
    411       scene_view_destroy_geometry(scnview, geom);
    412       htable_geom_erase(&scnview->cached_geoms, &shape_id);
    413     }
    414   } else if(is_valid) {
    415     res = geometry_create(scnview->scn->dev, &geom);
    416     if(res != RES_OK) goto error;
    417     res = line_segments_create(scnview->scn->dev, &geom->lines);
    418     if(res != RES_OK) goto error;
    419     res = htable_geom_set(&scnview->cached_geoms, &shape_id, &geom);
    420     if(res != RES_OK) goto error;
    421     geom->name = shape->id.index;
    422   }
    423 
    424   if(!is_valid) goto exit;
    425 
    426   /* Get a reference onto the shape lines segments indices */
    427   if(geom->lines->indices != shape->lines->indices) {
    428     geom->embree_outdated_mask |= EMBREE_INDICES;
    429     if(geom->lines->indices) { /* Release previous indices of the geometry */
    430       index_buffer_ref_put(geom->lines->indices);
    431       geom->lines->indices = NULL;
    432     }
    433     ASSERT(shape->lines->indices);
    434     index_buffer_ref_get(shape->lines->indices);
    435     geom->lines->indices = shape->lines->indices;
    436   }
    437 
    438   /* Get a reference onto the shape line segments attribs */
    439   FOR_EACH(iattr, 0, S2D_ATTRIBS_COUNT__) {
    440     if(geom->lines->attribs[iattr] == shape->lines->attribs[iattr])
    441       continue;
    442 
    443     geom->embree_outdated_mask |= EMBREE_VERTICES;
    444 
    445     if(geom->lines->attribs[iattr]) { /* Release the previous geometry attribs */
    446       vertex_buffer_ref_put(geom->lines->attribs[iattr]);
    447       geom->lines->attribs[iattr] = NULL;
    448     }
    449 
    450     if(!shape->lines->attribs[iattr]) continue;
    451 
    452     /* Get the new buffer */
    453     vertex_buffer_ref_get(shape->lines->attribs[iattr]);
    454     geom->lines->attribs[iattr] = shape->lines->attribs[iattr];
    455     geom->lines->attribs_type[iattr] = shape->lines->attribs_type[iattr];
    456   }
    457 
    458   /* Update the enable flag */
    459   if(geom->is_enabled != shape->is_enabled) {
    460     geom->is_enabled = shape->is_enabled;
    461     geom->embree_outdated_mask |= EMBREE_ENABLE;
    462   }
    463 
    464   /* Update the filter function */
    465   if(geom->lines->filter.func != shape->lines->filter.func
    466   || geom->lines->filter.data != shape->lines->filter.data) {
    467     geom->lines->filter = shape->lines->filter;
    468     geom->embree_outdated_mask |= EMBREE_FILTER_FUNCTION;
    469   }
    470 
    471   geom->flip_contour = shape->flip_contour;
    472 
    473 exit:
    474   return res;
    475 error:
    476   goto exit;
    477 }
    478 
    479 static res_T
    480 scene_view_compute_nprims_cdf
    481   (struct s2d_scene_view* scnview, const char store_cdf)
    482 {
    483   struct htable_geom_iterator it, end;
    484   size_t len;
    485   unsigned nprims;
    486   res_T res = RES_OK;
    487   ASSERT(scnview);
    488   ASSERT(darray_nprims_cdf_size_get(&scnview->nprims_cdf) == 0);
    489 
    490   htable_geom_begin(&scnview->cached_geoms, &it);
    491   htable_geom_end(&scnview->cached_geoms, &end);
    492 
    493   nprims = 0;
    494   while(!htable_geom_iterator_eq(&it, &end)) {
    495     const unsigned* shape_id = htable_geom_iterator_key_get(&it);
    496     struct geometry* geom = *htable_geom_iterator_data_get(&it);
    497     struct nprims_cdf cdf;
    498 
    499     htable_geom_iterator_next(&it);
    500 
    501     if(!geom->is_enabled) continue;
    502 
    503     geom->scene_prim_id_offset = nprims;
    504     len = line_segments_get_nsegments(geom->lines);
    505     nprims += (unsigned)len;
    506 
    507     cdf.nprims = nprims;
    508     cdf.ishape = *shape_id;
    509     if(store_cdf && len) {
    510       res = darray_nprims_cdf_push_back(&scnview->nprims_cdf, &cdf);
    511       if(res != RES_OK) goto error;
    512     }
    513   }
    514 
    515 exit:
    516   return res;
    517 error:
    518   darray_nprims_cdf_clear(&scnview->nprims_cdf);
    519   goto exit;
    520 }
    521 
    522 static void
    523 scene_view_compute_scene_aabb(struct s2d_scene_view* scnview)
    524 {
    525   struct htable_geom_iterator it, end;
    526   float lower[2], upper[2];
    527 
    528   ASSERT(scnview->lower[0] == FLT_MAX && scnview->upper[0] == -FLT_MAX);
    529   ASSERT(scnview->lower[1] == FLT_MAX && scnview->upper[1] == -FLT_MAX);
    530 
    531   htable_geom_begin(&scnview->cached_geoms, &it);
    532   htable_geom_end(&scnview->cached_geoms, &end);
    533 
    534   while(!htable_geom_iterator_eq(&it, &end)) {
    535     struct geometry* geom = *htable_geom_iterator_data_get(&it);
    536 
    537     htable_geom_iterator_next(&it);
    538 
    539     if(!geom->is_enabled) continue;
    540 
    541     line_segments_compute_aabb(geom->lines, lower, upper);
    542     f2_min(scnview->lower, scnview->lower, lower);
    543     f2_max(scnview->upper, scnview->upper, upper);
    544   }
    545 }
    546 
    547 static res_T
    548 scene_view_compute_cdf(struct s2d_scene_view* scnview)
    549 {
    550   struct htable_geom_iterator it, end;
    551   size_t len;
    552   float length = 0.f;
    553   res_T res = RES_OK;
    554   ASSERT(scnview);
    555   ASSERT(darray_fltui_size_get(&scnview->cdf) == 0);
    556 
    557   htable_geom_begin(&scnview->cached_geoms, &it);
    558   htable_geom_end(&scnview->cached_geoms, &end);
    559 
    560   while(!htable_geom_iterator_eq(&it, &end)) {
    561     const unsigned* shape_id = htable_geom_iterator_key_get(&it);
    562     struct geometry* geom = *htable_geom_iterator_data_get(&it);
    563     struct fltui fltui;
    564 
    565     htable_geom_iterator_next(&it);
    566 
    567     if(!geom->is_enabled) continue;
    568 
    569     res = line_segments_compute_cdf(geom->lines);
    570     if(res != RES_OK) goto error;
    571     len = darray_float_size_get(&geom->lines->cdf);
    572     if(len) {
    573       length += darray_float_cdata_get(&geom->lines->cdf)[len - 1];
    574 
    575       fltui.ui = *shape_id;
    576       fltui.flt = length;
    577       res = darray_fltui_push_back(&scnview->cdf, &fltui);
    578       if(res != RES_OK) goto error;
    579     }
    580   }
    581 exit:
    582   return res;
    583 error:
    584   darray_fltui_clear(&scnview->cdf);
    585   goto exit;
    586 }
    587 
    588 static res_T
    589 scene_view_sync
    590   (struct s2d_scene_view* scnview,
    591    const int mask)
    592 {
    593   struct htable_shape_iterator it, end;
    594   res_T res = RES_OK;
    595 
    596   ASSERT(scnview);
    597   ASSERT((mask & (S2D_TRACE|S2D_SAMPLE|S2D_GET_PRIMITIVE)) != 0);
    598 
    599   /* Commit the scene shape to the scnview */
    600   htable_shape_begin(&scnview->scn->shapes, &it);
    601   htable_shape_end(&scnview->scn->shapes, &end);
    602   while(!htable_shape_iterator_eq(&it, &end)) {
    603     struct s2d_shape** pshape = htable_shape_iterator_data_get(&it);
    604     struct s2d_shape* shape = *pshape;
    605 
    606     res = scene_view_register_line_segments(scnview, shape);
    607     if(res != RES_OK) goto error;
    608     htable_shape_iterator_next(&it);
    609   }
    610 
    611   scene_view_compute_scene_aabb(scnview);
    612 
    613   /* Setup the scene for the S2D_TRACE scnview */
    614   if((mask & S2D_TRACE) != 0) {
    615     res = scene_view_setup_embree(scnview);
    616     if(res != RES_OK) goto error;
    617   }
    618   /* Setup the scene for the S2D_SAMPLE scnview */
    619   if((mask & S2D_SAMPLE) != 0) {
    620     res = scene_view_compute_cdf(scnview);
    621     if(res != RES_OK) goto error;
    622   }
    623   /* Setup the scene for the scene_primitive_id/S3D_GET_PRIMITIVE scnview */
    624   res = scene_view_compute_nprims_cdf(scnview, (mask & S2D_GET_PRIMITIVE)!=0);
    625   if(res != RES_OK) goto error;
    626 
    627   scnview->mask = mask;
    628 
    629 exit:
    630   return res;
    631 error:
    632   goto exit;
    633 }
    634 
    635 static res_T
    636 scene_view_create(struct s2d_scene* scn, struct s2d_scene_view** out_scnview)
    637 {
    638   struct s2d_scene_view* scnview = NULL;
    639   res_T res = RES_OK;
    640   ASSERT(scn && out_scnview);
    641 
    642   if(!is_list_empty(&scn->scnviews)) {
    643     /* Retrieve an already allocated scnview */
    644     scnview = CONTAINER_OF(list_head(&scn->scnviews), struct s2d_scene_view, node);
    645     list_del(&scnview->node);
    646     ref_get(&scnview->ref);
    647   } else {
    648     scnview = (struct s2d_scene_view*)MEM_CALLOC
    649       (scn->dev->allocator, 1, sizeof(struct s2d_scene_view));
    650     if(!scnview) {
    651       res = RES_MEM_ERR;
    652       goto error;
    653     }
    654     list_init(&scnview->node);
    655     htable_geom_init(scn->dev->allocator, &scnview->cached_geoms);
    656     darray_fltui_init(scn->dev->allocator, &scnview->cdf);
    657     darray_nprims_cdf_init(scn->dev->allocator, &scnview->nprims_cdf);
    658     darray_uint_init(scn->dev->allocator, &scnview->detached_shapes);
    659     f2_splat(scnview->lower, FLT_MAX);
    660     f2_splat(scnview->upper,-FLT_MAX);
    661     ref_init(&scnview->ref);
    662 
    663     CLBK_INIT(&scnview->on_shape_detach_cb);
    664     CLBK_SETUP(&scnview->on_shape_detach_cb, on_shape_detach, scnview);
    665     SIG_CONNECT_CLBK(&scn->sig_shape_detach, &scnview->on_shape_detach_cb);
    666   }
    667   S2D(scene_ref_get(scn));
    668   scnview->scn = scn;
    669 exit:
    670   *out_scnview = scnview;
    671   return res;
    672 error:
    673   if(scnview) {
    674     S2D(scene_view_ref_put(scnview));
    675     scnview = NULL;
    676   }
    677   goto exit;
    678 }
    679 
    680 static void
    681 scene_view_release(ref_T* ref)
    682 {
    683   struct s2d_scene_view* scnview = CONTAINER_OF(ref, struct s2d_scene_view, ref);
    684   size_t i, n;
    685   ASSERT(ref);
    686 
    687   /* Remove the geometry of the shapes detached while the scnview was active */
    688   n = darray_uint_size_get(&scnview->detached_shapes);
    689   FOR_EACH(i, 0, n) {
    690     const unsigned shape_id = darray_uint_cdata_get(&scnview->detached_shapes)[i];
    691     struct geometry** pgeom = htable_geom_find(&scnview->cached_geoms, &shape_id);
    692     struct geometry* geom = *pgeom;
    693     size_t tmp; (void)tmp;
    694     scene_view_destroy_geometry(scnview, geom);
    695     tmp = htable_geom_erase(&scnview->cached_geoms, &shape_id);
    696     ASSERT(tmp == 1);
    697   }
    698   darray_uint_clear(&scnview->detached_shapes);
    699 
    700   /* Clear the scnview data structures excepted the cache of geometries that
    701    * will be used to speed up the future scnview creation */
    702   darray_fltui_clear(&scnview->cdf);
    703   darray_nprims_cdf_clear(&scnview->nprims_cdf);
    704   f2_splat(scnview->lower, FLT_MAX);
    705   f2_splat(scnview->upper,-FLT_MAX);
    706   scnview->mask = 0;
    707   scnview->rtc_commit = 0;
    708 
    709   /* Do not physically release the memory space of the scnview. Add it to the
    710    * available scnviews pool of the scene */
    711   list_add(&scnview->scn->scnviews, &scnview->node);
    712   S2D(scene_ref_put(scnview->scn));
    713 }
    714 
    715 static res_T
    716 scene_view_trace_ray
    717   (struct s2d_scene_view* scnview,
    718    const float org[2],
    719    const float dir[],
    720    const float range[2],
    721    const int rt_3d,
    722    void* ray_data,
    723    struct s2d_hit* hit)
    724 {
    725   struct RTCRayHit ray_hit;
    726   struct RTCIntersectArguments intersect_args;
    727   struct intersect_context intersect_ctx;
    728   float dot = 1;
    729   float dir2d[3] = {0.f, 0.f, 0.f};
    730   float range2d[2] = {FLT_MAX, -FLT_MAX};
    731   size_t i;
    732 
    733   if(!scnview || !org || !dir || !range || !hit)
    734     return RES_BAD_ARG;
    735   if(!rt_3d) {
    736     if(!f2_is_normalized(dir)) {
    737       log_error(scnview->scn->dev,
    738         "%s: unnormalized ray direction {%g, %g}.\n", FUNC_NAME, SPLIT2(dir));
    739       return RES_BAD_ARG;
    740     }
    741   } else {
    742     if(!f3_is_normalized(dir)) {
    743       log_error(scnview->scn->dev,
    744         "%s: unnormalized ray direction {%g, %g, %g}.\n", FUNC_NAME, SPLIT3(dir));
    745       return RES_BAD_ARG;
    746     }
    747     if(eq_epsf(dir[0], 0.f, 1.e-6f) && eq_epsf(dir[1], 0.f, 1.e-6f)) {
    748       *hit = S2D_HIT_NULL;
    749       return RES_OK;
    750     }
    751   }
    752   if(range[0] < 0) {
    753     log_error(scnview->scn->dev,
    754       "%s: invalid ray range [%g, %g] - it must be in [0, INF).\n",
    755       FUNC_NAME, range[0], range[1]);
    756     return RES_BAD_ARG;
    757   }
    758   if((scnview->mask & S2D_TRACE) == 0) {
    759     log_error(scnview->scn->dev,
    760       "%s: the S2D_TRACE flag is not active onto the submitted scene view.\n",
    761       FUNC_NAME);
    762     return RES_BAD_OP;
    763   }
    764   if(range[0] > range[1]) { /* Degenerated range <=> disabled ray */
    765     *hit = S2D_HIT_NULL;
    766     return RES_OK;
    767   }
    768 
    769   if(!rt_3d) {
    770     f2_set(dir2d, dir);
    771     f2_set(range2d, range);
    772   } else {
    773     f2_normalize(dir2d, dir);
    774     dot = f3_dot(dir2d, dir); /* Cosine between dir and dir_2d */
    775     range2d[0] = dot*range[0];
    776     range2d[1] = dot*range[1];
    777   }
    778 
    779   /* Initialise the ray */
    780   ray_hit.ray.org_x = org[0];
    781   ray_hit.ray.org_y = org[1];
    782   ray_hit.ray.org_z = 0.f;
    783   ray_hit.ray.dir_x = dir2d[0];
    784   ray_hit.ray.dir_y = dir2d[1];
    785   ray_hit.ray.dir_z = 0.f;
    786   ray_hit.ray.tnear = range2d[0];
    787   ray_hit.ray.tfar = range2d[1];
    788   ray_hit.ray.time = FLT_MAX; /* Invalid fields */
    789   ray_hit.ray.mask = UINT_MAX; /* Invalid fields */
    790   ray_hit.ray.id = UINT_MAX; /* Invalid fields */
    791   ray_hit.ray.flags = UINT_MAX; /* Invalid fields */
    792 
    793   /* Initialise the hit */
    794   ray_hit.hit.geomID = RTC_INVALID_GEOMETRY_ID;
    795   FOR_EACH(i, 0, RTC_MAX_INSTANCE_LEVEL_COUNT) {
    796     ray_hit.hit.instID[i] = RTC_INVALID_GEOMETRY_ID;
    797   }
    798 
    799   /* Initialise the intersect context */
    800   rtcInitIntersectArguments(&intersect_args);
    801   intersect_args.context = &intersect_ctx.rtc;
    802   rtcInitRayQueryContext(&intersect_ctx.rtc);
    803   intersect_ctx.ws_org[0] = org[0];
    804   intersect_ctx.ws_org[1] = org[1];
    805   intersect_ctx.ws_dir[0] = dir[0];
    806   intersect_ctx.ws_dir[1] = dir[1];
    807   intersect_ctx.ws_dir[2] = rt_3d ? dir[2] : 0.f;
    808   intersect_ctx.ws_range[0] = range[0];
    809   intersect_ctx.ws_range[1] = range[1];
    810   intersect_ctx.scnview = scnview;
    811   intersect_ctx.data = ray_data;
    812   intersect_ctx.rt_3d = rt_3d;
    813   intersect_ctx.cos_dir_dir2d = dot;
    814 
    815   /* Here we go */
    816   rtcIntersect1(scnview->rtc_scn, &ray_hit, &intersect_args);
    817 
    818   hit_setup(scnview, &ray_hit, hit);
    819 
    820   if(rt_3d && !S2D_HIT_NONE(hit)) hit->distance /= dot;
    821   return RES_OK;
    822 }
    823 
    824 /*******************************************************************************
    825  * Exported functions
    826  ******************************************************************************/
    827 res_T
    828 s2d_scene_view_create
    829   (struct s2d_scene* scn,
    830    const int mask,
    831    struct s2d_scene_view** out_scnview)
    832 {
    833   struct s2d_scene_view* scnview = NULL;
    834   res_T res = RES_OK;
    835 
    836   if(!scn || !out_scnview) {
    837     res = RES_BAD_ARG;
    838     goto error;
    839   }
    840 
    841   if(!(mask & S2D_TRACE)
    842   && !(mask & S2D_SAMPLE)
    843   && !(mask & S2D_GET_PRIMITIVE)) {
    844     log_error(scn->dev, "%s: no valid scene view mask is defined.\n", FUNC_NAME);
    845     res = RES_BAD_ARG;
    846     goto error;
    847   }
    848 
    849   res = scene_view_create(scn, &scnview);
    850   if(res != RES_OK) goto error;
    851 
    852   res = scene_view_sync(scnview, mask);
    853   if(res != RES_OK) goto error;
    854 
    855 exit:
    856   if(out_scnview) *out_scnview = scnview;
    857   return res;
    858 error:
    859   if(scnview) {
    860     S2D(scene_view_ref_put(scnview));
    861     scnview = NULL;
    862   }
    863   goto exit;
    864 }
    865 
    866 res_T
    867 s2d_scene_view_ref_get(struct s2d_scene_view* scnview)
    868 {
    869   if(!scnview) return RES_BAD_ARG;
    870   ref_get(&scnview->ref);
    871   return RES_OK;
    872 }
    873 
    874 res_T
    875 s2d_scene_view_ref_put(struct s2d_scene_view* scnview)
    876 {
    877   if(!scnview) return RES_BAD_ARG;
    878   ref_put(&scnview->ref, scene_view_release);
    879   return RES_OK;
    880 }
    881 
    882 res_T
    883 s2d_scene_view_get_mask(struct s2d_scene_view* scnview, int* mask)
    884 {
    885   if(!scnview || !mask) return RES_BAD_ARG;
    886   *mask = scnview->mask;
    887   return RES_OK;
    888 }
    889 
    890 res_T
    891 s2d_scene_view_trace_ray
    892   (struct s2d_scene_view* scnview,
    893    const float org[2],
    894    const float dir[2],
    895    const float range[2],
    896    void* ray_data,
    897    struct s2d_hit* hit)
    898 {
    899   return scene_view_trace_ray(scnview, org, dir, range, 0/*2D*/, ray_data, hit);
    900 }
    901 
    902 res_T
    903 s2d_scene_view_trace_ray_3d
    904   (struct s2d_scene_view* scnview,
    905    const float org[2],
    906    const float dir[3],
    907    const float range[2],
    908    void* ray_data,
    909    struct s2d_hit* hit)
    910 {
    911   return scene_view_trace_ray(scnview, org, dir, range, 1/*3D*/, ray_data, hit);
    912 }
    913 
    914 res_T
    915 s2d_scene_view_sample
    916   (struct s2d_scene_view* scnview,
    917    const float u, const float v,
    918    struct s2d_primitive* primitive,
    919    float* s)
    920 {
    921   struct geometry** pgeom;
    922   struct geometry* geom;
    923   size_t sz;
    924   size_t i;
    925   const struct fltui* fltui, *fltui_found;
    926   const float* flt, *flt_found;
    927   unsigned ishape;
    928   float f;
    929   res_T res = RES_OK;
    930 
    931   if(!scnview || !primitive || !s) {
    932     res = RES_BAD_ARG;
    933     goto error;
    934   }
    935 
    936   if(u < 0.f || u >= 1.f || v < 0.f || v >= 1.f) {
    937     log_error(scnview->scn->dev,
    938       "%s: The submitted numbers are not canonical, i.e. they ar not in [0, 1[.\n",
    939       FUNC_NAME);
    940     res = RES_BAD_ARG;
    941     goto error;
    942   }
    943   if((scnview->mask & S2D_SAMPLE) == 0) {
    944     log_error(scnview->scn->dev,
    945       "%s: the S2D_SAMPLE flag is not active onto the submitted scene view.\n",
    946       FUNC_NAME);
    947     res = RES_BAD_OP;
    948     goto error;
    949   }
    950 
    951   /* Find the sampled geometry */
    952   if(darray_fltui_size_get(&scnview->cdf) == 0) { /* No geometry to sample */
    953     *primitive = S2D_PRIMITIVE_NULL;
    954     goto exit;
    955   } else if(darray_fltui_size_get(&scnview->cdf) == 1) {
    956     ishape = darray_fltui_cdata_get(&scnview->cdf)[0].ui;
    957     /* Map u to the CDF bounds */
    958     f = u * darray_fltui_cdata_get(&scnview->cdf)[0].flt;
    959   } else {
    960     fltui = darray_fltui_cdata_get(&scnview->cdf);
    961     sz = darray_fltui_size_get(&scnview->cdf);
    962     f = u * fltui[sz-1].flt; /* Map u to [0, ScenePerimeter[ */
    963     fltui_found = search_lower_bound
    964       (&f, fltui, sz, sizeof(*fltui), cmp_float_to_fltui);
    965     ASSERT(fltui_found);
    966 
    967     /* search_lower_bound returns the first entry that is not less than `f'.
    968      * The following code discards entries that are also `equal' to `f' */
    969     i = (size_t)(fltui_found - fltui);
    970     while(fltui[i].flt == f && i < sz) ++i;
    971     ASSERT(i < sz);
    972 
    973     fltui_found = fltui + i;
    974     ishape = fltui_found->ui;
    975 
    976     /* Map f to [0, ShapePerimeter[ */
    977     if(i) f -= fltui[i-1].flt;
    978   }
    979   pgeom = htable_geom_find(&scnview->cached_geoms, &ishape);
    980   ASSERT(pgeom);
    981   geom = *pgeom;
    982 
    983   /* Find the sampled segment */
    984   flt = darray_float_cdata_get(&geom->lines->cdf);
    985   sz = darray_float_size_get(&geom->lines->cdf);
    986   flt_found = search_lower_bound(&f, flt, sz, sizeof(*flt), cmp_float);
    987   ASSERT(flt_found);
    988 
    989   /* search_lower_bound returns the first entry that is not less than `f'.
    990    * The following code discards entries that are also equal to `f' */
    991   i = (size_t)(flt_found - flt);
    992   while(flt[i] == f && i < sz) ++i;
    993   ASSERT(i < sz);
    994 
    995   primitive->mesh__ = geom;
    996   primitive->geom_id = geom->name;
    997   primitive->prim_id = (unsigned)i;
    998   primitive->scene_prim_id = primitive->prim_id + geom->scene_prim_id_offset;
    999   S2D(primitive_sample(primitive, v, s));
   1000 
   1001 exit:
   1002   return res;
   1003 error:
   1004   goto exit;
   1005 }
   1006 
   1007 res_T
   1008 s2d_scene_view_get_primitive
   1009   (struct s2d_scene_view* scnview,
   1010    const unsigned iprim,
   1011    struct s2d_primitive* prim)
   1012 {
   1013   struct geometry** pgeom;
   1014   struct geometry* geom;
   1015   const struct nprims_cdf* begin, *found;
   1016   size_t nprims;
   1017   size_t sz;
   1018   size_t i;
   1019   unsigned ishape;
   1020   res_T res = RES_OK;
   1021 
   1022   if(!scnview || !prim) {
   1023     res = RES_BAD_ARG;
   1024     goto error;
   1025   }
   1026   if((scnview->mask & S2D_GET_PRIMITIVE) == 0) {
   1027     log_error(scnview->scn->dev,
   1028       "%s: the S2D_GET_PRIMITIVE flag is not active onto the submitted scene view.\n",
   1029       FUNC_NAME);
   1030     res = RES_BAD_OP;
   1031     goto error;
   1032   }
   1033   S2D(scene_view_primitives_count(scnview, &nprims));
   1034   if(iprim >= nprims) {
   1035     log_error(scnview->scn->dev,
   1036       "%s: The primitive index %u exceeds the number of primitives %u.\n",
   1037       FUNC_NAME, iprim, (unsigned)nprims);
   1038     res = RES_BAD_ARG;
   1039     goto error;
   1040   }
   1041 
   1042   i = iprim;
   1043   if(darray_nprims_cdf_size_get(&scnview->nprims_cdf) == 1) {
   1044     ishape = darray_nprims_cdf_cdata_get(&scnview->nprims_cdf)[0].ishape;
   1045   } else {
   1046     begin = darray_nprims_cdf_cdata_get(&scnview->nprims_cdf);
   1047     sz = darray_nprims_cdf_size_get(&scnview->nprims_cdf);
   1048     found = search_lower_bound
   1049       (&i, begin, sz, sizeof(*begin), cmp_size_t_to_nprims_cdf);
   1050     ASSERT(found);
   1051     ishape = found->ishape;
   1052     if(found != begin) {
   1053       ASSERT(i >= found[-1].nprims);
   1054       i -= found[-1].nprims;
   1055     }
   1056   }
   1057   pgeom = htable_geom_find(&scnview->cached_geoms, &ishape);
   1058   ASSERT(pgeom);
   1059   geom = *pgeom;
   1060   ASSERT(i < line_segments_get_nsegments(geom->lines));
   1061   prim->mesh__ = geom;
   1062   prim->geom_id = geom->name;
   1063   prim->prim_id = (unsigned)i;
   1064   prim->scene_prim_id = geom->scene_prim_id_offset + prim->prim_id;
   1065 
   1066 exit:
   1067   return res;
   1068 error:
   1069   goto exit;
   1070 }
   1071 
   1072 res_T
   1073 s2d_scene_view_primitives_count
   1074   (struct s2d_scene_view* scnview, size_t* prims_count)
   1075 {
   1076   size_t nprims = 0;
   1077   res_T res = RES_OK;
   1078 
   1079   if(!scnview || !prims_count) {
   1080     res = RES_BAD_ARG;
   1081     goto error;
   1082   }
   1083   if((scnview->mask & S2D_GET_PRIMITIVE) != 0) {
   1084     const size_t len = darray_nprims_cdf_size_get(&scnview->nprims_cdf);
   1085     if(!len) {
   1086       nprims = 0;
   1087     } else {
   1088       nprims = darray_nprims_cdf_cdata_get(&scnview->nprims_cdf)[len-1].nprims;
   1089     }
   1090   } else {
   1091     struct htable_geom_iterator it, end;
   1092 
   1093     htable_geom_begin(&scnview->cached_geoms, &it);
   1094     htable_geom_end(&scnview->cached_geoms, &end);
   1095     nprims = 0;
   1096     while(!htable_geom_iterator_eq(&it, &end)) {
   1097       struct geometry** pgeom = htable_geom_iterator_data_get(&it);
   1098       struct geometry* geom = *pgeom;
   1099       htable_geom_iterator_next(&it);
   1100 
   1101       if(geom->is_enabled) {
   1102         nprims += line_segments_get_nsegments(geom->lines);
   1103       }
   1104     }
   1105   }
   1106 
   1107 exit:
   1108   if(prims_count) *prims_count = nprims;
   1109   return res;
   1110 error:
   1111   goto exit;
   1112 }
   1113 
   1114 res_T
   1115 s2d_scene_view_compute_contour_length
   1116   (struct s2d_scene_view* scnview, float* out_length)
   1117 {
   1118   float length = 0;
   1119   res_T res = RES_OK;
   1120 
   1121   if(!scnview || !out_length) {
   1122     res = RES_BAD_ARG;
   1123     goto error;
   1124   }
   1125 
   1126   if((scnview->mask & S2D_SAMPLE) != 0) {
   1127     /* Retrieve the overall scene contour length from the scene cumulative
   1128      * distribution function. Note that the CDF stores the cumulative contour
   1129      * length. The real contour length is thus the CDF upper bound */
   1130     size_t len = darray_fltui_size_get(&scnview->cdf);
   1131     if(!len) {
   1132       length = 0.f;
   1133     } else {
   1134       length = darray_fltui_cdata_get(&scnview->cdf)[len-1].flt;
   1135     }
   1136   } else {
   1137     struct htable_geom_iterator it, end;
   1138 
   1139     htable_geom_begin(&scnview->cached_geoms, &it);
   1140     htable_geom_end(&scnview->cached_geoms, &end);
   1141     length = 0.f;
   1142     while(!htable_geom_iterator_eq(&it, &end)) {
   1143       struct geometry** pgeom = htable_geom_iterator_data_get(&it);
   1144       struct geometry* geom = *pgeom;
   1145       htable_geom_iterator_next(&it);
   1146 
   1147       if(geom->is_enabled) {
   1148         length += line_segments_compute_length(geom->lines);
   1149       }
   1150     }
   1151   }
   1152 
   1153 exit:
   1154   if(out_length) *out_length = length;
   1155   return res;
   1156 error:
   1157   length = -1.f;
   1158   goto exit;
   1159 }
   1160 
   1161 res_T
   1162 s2d_scene_view_compute_area(struct s2d_scene_view* scnview, float* out_area)
   1163 {
   1164   struct htable_geom_iterator it, end;
   1165   float area = 0.f;
   1166   res_T res = RES_OK;
   1167 
   1168   if(!scnview || !out_area) {
   1169     res = RES_BAD_ARG;
   1170     goto error;
   1171   }
   1172 
   1173   htable_geom_begin(&scnview->cached_geoms, &it);
   1174   htable_geom_end(&scnview->cached_geoms, &end);
   1175   area = 0.f;
   1176   while(!htable_geom_iterator_eq(&it, &end)) {
   1177     struct geometry** pgeom = htable_geom_iterator_data_get(&it);
   1178     struct geometry* geom = *pgeom;
   1179     htable_geom_iterator_next(&it);
   1180 
   1181     if(geom->is_enabled) {
   1182       area += line_segments_compute_area(geom->lines, geom->flip_contour);
   1183     }
   1184   }
   1185 
   1186   if(area < 0.f) {
   1187     log_warning(scnview->scn->dev,
   1188 "%s:\n"
   1189 "\tthe area is negative. The scene shapes might not represent closed\n"
   1190 "\tpolygons, or the edge normals might not point inward the polygon.\n",
   1191       FUNC_NAME);
   1192   }
   1193 
   1194 exit:
   1195   if(out_area) *out_area = area;
   1196   return res;
   1197 error:
   1198   area = -1.f;
   1199   goto exit;
   1200 }
   1201 
   1202 res_T
   1203 s2d_scene_view_get_aabb
   1204   (struct s2d_scene_view* scnview, float lower[2], float upper[2])
   1205 {
   1206   if(!scnview || !lower || !upper) return RES_BAD_ARG;
   1207   f2_set(lower, scnview->lower);
   1208   f2_set(upper, scnview->upper);
   1209   return RES_OK;
   1210 }
   1211 
   1212 /*******************************************************************************
   1213  * Local functions
   1214  ******************************************************************************/
   1215 void
   1216 scene_view_destroy(struct s2d_scene_view* scnview)
   1217 {
   1218   struct htable_geom_iterator it, end;
   1219   ASSERT(scnview && !is_list_empty(&scnview->node)/*Not in use*/);
   1220   ASSERT(scnview->mask == 0);
   1221 
   1222   /* Delete the cached geometries */
   1223   htable_geom_begin(&scnview->cached_geoms, &it);
   1224   htable_geom_end(&scnview->cached_geoms, &end);
   1225   while(!htable_geom_iterator_eq(&it, &end)) {
   1226     struct geometry** pgeom = htable_geom_iterator_data_get(&it);
   1227     struct geometry* geom = *pgeom;
   1228     scene_view_destroy_geometry(scnview, geom);
   1229     htable_geom_iterator_next(&it);
   1230   }
   1231 
   1232   /* Delete the back-end scene */
   1233   if(scnview->rtc_scn) rtcReleaseScene(scnview->rtc_scn);
   1234 
   1235   /* Release internal data structure */
   1236   htable_geom_release(&scnview->cached_geoms);
   1237   darray_fltui_release(&scnview->cdf);
   1238   darray_nprims_cdf_release(&scnview->nprims_cdf);
   1239   darray_uint_release(&scnview->detached_shapes);
   1240 
   1241   /* Remove the scnview from its pool */
   1242   list_del(&scnview->node);
   1243   CLBK_DISCONNECT(&scnview->on_shape_detach_cb);
   1244 
   1245   /* Free the scnview memory space */
   1246   MEM_RM(scnview->scn->dev->allocator, scnview);
   1247 }
   1248 
   1249 void
   1250 rtc_hit_filter_wrapper(const struct RTCFilterFunctionNArguments* args)
   1251 {
   1252   struct s2d_hit hit;
   1253   struct RTCRayHit ray_hit;
   1254   struct intersect_context* ctx;
   1255   struct geometry* geom;
   1256   struct hit_filter* filter;
   1257   int is_hit_filtered = 0;
   1258   ASSERT(args && args->N == 1 && args->context && args->valid[0] != 0);
   1259 
   1260   rtc_rayN_get_ray(args->ray, args->N, 0, &ray_hit.ray);
   1261   rtc_hitN_get_hit(args->hit, args->N, 0, &ray_hit.hit);
   1262 
   1263   ctx = CONTAINER_OF(args->context, struct intersect_context, rtc);
   1264 
   1265   geom = args->geometryUserPtr;
   1266   filter = &geom->lines->filter;
   1267   ASSERT(filter->func);
   1268 
   1269   hit_setup(ctx->scnview, &ray_hit, &hit);
   1270   if(ctx->rt_3d) {
   1271     hit.distance /= ctx->cos_dir_dir2d;
   1272   }
   1273   is_hit_filtered = filter->func
   1274     (&hit, ctx->ws_org, ctx->ws_dir, ctx->ws_range, ctx->data, filter->data);
   1275   if(is_hit_filtered) {
   1276     args->valid[0] = 0;
   1277   }
   1278 }
   1279