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_trace_ray.c (9704B)


      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_c.h"
     18 #include "s3d_device_c.h"
     19 #include "s3d_instance.h"
     20 #include "s3d_geometry.h"
     21 #include "s3d_mesh.h"
     22 #include "s3d_sphere.h"
     23 #include "s3d_scene_view_c.h"
     24 
     25 #include <rsys/float33.h>
     26 #include <limits.h>
     27 
     28 struct intersect_context {
     29   struct RTCRayQueryContext rtc;
     30   struct s3d_scene_view* scnview;
     31   void* data; /* Per ray user defined data */
     32   float ws_org[3]; /* World space ray origin */
     33   float ws_dir[3]; /* World space ray direction */
     34   float ws_range[3]; /* World space ray range */
     35 };
     36 
     37 /*******************************************************************************
     38  * Helper functions
     39  ******************************************************************************/
     40 static INLINE void
     41 hit_setup
     42   (struct s3d_scene_view* scnview,
     43    const struct RTCRayHit* ray_hit,
     44    struct s3d_hit* hit)
     45 {
     46   float w;
     47   char flip_surface = 0;
     48 
     49   ASSERT(scnview && hit && ray_hit);
     50 
     51   if(ray_hit->hit.geomID == RTC_INVALID_GEOMETRY_ID) { /* No hit */
     52     *hit = S3D_HIT_NULL;
     53     return;
     54   }
     55 
     56   hit->normal[0] = ray_hit->hit.Ng_x;
     57   hit->normal[1] = ray_hit->hit.Ng_y;
     58   hit->normal[2] = ray_hit->hit.Ng_z;
     59   hit->distance = ray_hit->ray.tfar;
     60 
     61   if(ray_hit->hit.instID[0] == RTC_INVALID_GEOMETRY_ID) {
     62     struct geometry* geom_shape;
     63     geom_shape = scene_view_geometry_from_embree_id(scnview, ray_hit->hit.geomID);
     64     hit->prim.shape__ = geom_shape;
     65     hit->prim.inst__ = NULL;
     66     hit->prim.prim_id = ray_hit->hit.primID;
     67     hit->prim.geom_id = geom_shape->name;
     68     hit->prim.inst_id = S3D_INVALID_ID;
     69     hit->prim.scene_prim_id = /* Compute the "scene space" primitive id */
     70       hit->prim.prim_id /* Mesh space */
     71     + geom_shape->scene_prim_id_offset; /* Scene space */
     72 
     73   } else { /* The hit shape is instantiated */
     74     /* Retrieve the hit instance */
     75     struct geometry* geom_inst;
     76     struct geometry* geom_shape;
     77     float transform[9];
     78     geom_inst = scene_view_geometry_from_embree_id
     79       (scnview, ray_hit->hit.instID[0]);
     80     geom_shape = scene_view_geometry_from_embree_id
     81       (geom_inst->data.instance->scnview, ray_hit->hit.geomID);
     82     hit->prim.shape__ = geom_shape;
     83     hit->prim.inst__ = geom_inst;
     84     hit->prim.prim_id = ray_hit->hit.primID;
     85     hit->prim.geom_id = geom_shape->name;
     86     hit->prim.inst_id = geom_inst->name;
     87     hit->prim.scene_prim_id = /* Compute the "scene space" primitive id */
     88       hit->prim.prim_id /* Shape space */
     89       + geom_shape->scene_prim_id_offset /* Inst space */
     90       + geom_inst->scene_prim_id_offset; /* Scene space */
     91 
     92     flip_surface = geom_inst->flip_surface;
     93     ASSERT(hit->prim.inst__);
     94     ASSERT(((struct geometry*)hit->prim.inst__)->type == GEOM_INSTANCE);
     95 
     96     /* Transform the normal in world space */
     97     f33_invtrans(transform, geom_inst->data.instance->transform);
     98     f33_mulf3(hit->normal, transform, hit->normal);
     99   }
    100   ASSERT(hit->prim.shape__);
    101   ASSERT(((struct geometry*)hit->prim.shape__)->type == GEOM_MESH
    102        ||((struct geometry*)hit->prim.shape__)->type == GEOM_SPHERE);
    103 
    104   /* Handle Embree returning uv out of range */
    105   hit->uv[0] = CLAMP(ray_hit->hit.u, 0, 1);
    106   hit->uv[1] = CLAMP(ray_hit->hit.v, 0, 1);
    107 
    108   if(((struct geometry*)hit->prim.shape__)->type == GEOM_MESH) {
    109     w = 1.f - hit->uv[0] - hit->uv[1];
    110     if(w < 0.f) { /* Handle precision error */
    111       if(hit->uv[0] > hit->uv[1]) hit->uv[0] += w;
    112       else hit->uv[1] += w;
    113       w = 0.f;
    114     }
    115 
    116     /* Embree stores on the u and v ray parameters the barycentric coordinates of
    117      * the hit with respect to the second and third triangle vertices,
    118      * respectively. The following code computes the barycentric coordinates of
    119      * the hit for the first and second triangle vertices */
    120     hit->uv[1] = hit->uv[0];
    121     hit->uv[0] = w;
    122 
    123     /* In Embree3 the normal orientation is flipped wrt to Star-3D convention */
    124     #if RTC_VERSION_MAJOR >= 3
    125     f3_minus(hit->normal, hit->normal);
    126     #endif
    127   }
    128 
    129   /* Flip geometric normal with respect to the flip surface flag */
    130   flip_surface ^= ((struct geometry*)hit->prim.shape__)->flip_surface;
    131   if(flip_surface) f3_minus(hit->normal, hit->normal);
    132 }
    133 
    134 /*******************************************************************************
    135  * Exported functions
    136  ******************************************************************************/
    137 res_T
    138 s3d_scene_view_trace_ray
    139   (struct s3d_scene_view* scnview,
    140    const float org[3],
    141    const float dir[3],
    142    const float range[2],
    143    void* ray_data,
    144    struct s3d_hit* hit)
    145 {
    146   struct RTCRayHit ray_hit;
    147   struct RTCIntersectArguments intersect_args;
    148   struct intersect_context intersect_ctx;
    149   size_t i;
    150 
    151   if(!scnview || !org || !dir || !range || !hit)
    152     return RES_BAD_ARG;
    153   if(!f3_is_normalized(dir)) {
    154     log_error(scnview->scn->dev,
    155       "%s: unnormalized ray direction {%g, %g, %g}.\n",
    156       FUNC_NAME, SPLIT3(dir));
    157     return RES_BAD_ARG;
    158   }
    159   if(range[0] < 0) {
    160     log_error(scnview->scn->dev,
    161       "%s: invalid ray range [%g, %g] - it must be in [0, INF).\n",
    162       FUNC_NAME, range[0], range[1]);
    163     return RES_BAD_ARG;
    164   }
    165   if((scnview->mask & S3D_TRACE) == 0) {
    166     log_error(scnview->scn->dev,
    167       "%s: the S3D_TRACE flag is not active onto the submitted scene view.\n",
    168       FUNC_NAME);
    169     return RES_BAD_OP;
    170   }
    171   if(range[0] > range[1]) { /* Degenerated range <=> disabled ray */
    172     *hit = S3D_HIT_NULL;
    173     return RES_OK;
    174   }
    175 
    176   /* Initialise the ray */
    177   ray_hit.ray.org_x = org[0];
    178   ray_hit.ray.org_y = org[1];
    179   ray_hit.ray.org_z = org[2];
    180   ray_hit.ray.dir_x = dir[0];
    181   ray_hit.ray.dir_y = dir[1];
    182   ray_hit.ray.dir_z = dir[2];
    183   ray_hit.ray.tnear = range[0];
    184   ray_hit.ray.tfar = range[1];
    185   ray_hit.ray.time = FLT_MAX; /* Invalid fields */
    186   ray_hit.ray.mask = UINT_MAX; /* Invalid fields */
    187   ray_hit.ray.id = UINT_MAX; /* Invalid fields */
    188   ray_hit.ray.flags = UINT_MAX; /* Invalid fields */
    189 
    190   /* Initialise the hit */
    191   ray_hit.hit.geomID = RTC_INVALID_GEOMETRY_ID;
    192   FOR_EACH(i, 0, RTC_MAX_INSTANCE_LEVEL_COUNT) {
    193     ray_hit.hit.instID[i] = RTC_INVALID_GEOMETRY_ID;
    194   }
    195 
    196   /* Initialise the intersect context */
    197   rtcInitIntersectArguments(&intersect_args);
    198   intersect_args.context = &intersect_ctx.rtc;
    199   rtcInitRayQueryContext(&intersect_ctx.rtc);
    200   intersect_ctx.ws_org[0] = org[0];
    201   intersect_ctx.ws_org[1] = org[1];
    202   intersect_ctx.ws_org[2] = org[2];
    203   intersect_ctx.ws_dir[0] = dir[0];
    204   intersect_ctx.ws_dir[1] = dir[1];
    205   intersect_ctx.ws_dir[2] = dir[2];
    206   intersect_ctx.ws_range[0] = range[0];
    207   intersect_ctx.ws_range[1] = range[1];
    208   intersect_ctx.scnview = scnview;
    209   intersect_ctx.data = ray_data;
    210 
    211   /* Here we go! */
    212   rtcIntersect1(scnview->rtc_scn, &ray_hit, &intersect_args);
    213 
    214   hit_setup(scnview, &ray_hit, hit);
    215   return RES_OK;
    216 }
    217 
    218 res_T
    219 s3d_scene_view_trace_rays
    220   (struct s3d_scene_view* scnview,
    221    const size_t nrays,
    222    const int mask,
    223    const float* origins,
    224    const float* directions,
    225    const float* ranges,
    226    void* rays_data,
    227    const size_t sizeof_ray_data,
    228    struct s3d_hit* hits)
    229 {
    230   size_t iray;
    231   size_t iorg, idir, irange, idata;
    232   size_t org_step, dir_step, range_step, data_step;
    233   res_T res = RES_OK;
    234 
    235   if(!scnview) return RES_BAD_ARG;
    236   if(!nrays) return RES_OK;
    237 
    238   org_step = mask & S3D_RAYS_SINGLE_ORIGIN ? 0 : 3;
    239   dir_step = mask & S3D_RAYS_SINGLE_DIRECTION ? 0 : 3;
    240   range_step = mask & S3D_RAYS_SINGLE_RANGE ? 0 : 2;
    241   data_step = (mask & S3D_RAYS_SINGLE_DATA) || !rays_data ? 0 : sizeof_ray_data;
    242   iorg = idir = irange = idata = 0;
    243 
    244   FOR_EACH(iray, 0, nrays) {
    245     res = s3d_scene_view_trace_ray(scnview, origins+iorg, directions+idir,
    246       ranges+irange, (char*)rays_data+idata, hits+iray);
    247     if(UNLIKELY(res != RES_OK)) break;
    248     iorg += org_step;
    249     idir += dir_step;
    250     irange += range_step;
    251     idata += data_step;
    252   }
    253   return res;
    254 }
    255 
    256 /*******************************************************************************
    257  * Local functions
    258  ******************************************************************************/
    259 /* Wrapper between an Embree and a Star-3D filter function */
    260 void
    261 rtc_hit_filter_wrapper(const struct RTCFilterFunctionNArguments* args)
    262 {
    263   struct s3d_hit hit;
    264   struct RTCRayHit ray_hit;
    265   struct intersect_context* ctx;
    266   struct geometry* geom;
    267   struct hit_filter* filter;
    268   int is_hit_filtered = 0;
    269   ASSERT(args && args->N == 1 && args->context && args->valid[0] != 0);
    270 
    271   rtc_rayN_get_ray(args->ray, args->N, 0, &ray_hit.ray);
    272   rtc_hitN_get_hit(args->hit, args->N, 0, &ray_hit.hit);
    273 
    274   ctx = CONTAINER_OF(args->context, struct intersect_context, rtc);
    275 
    276   geom = args->geometryUserPtr;
    277   switch(geom->type) {
    278     case GEOM_MESH:
    279       filter = &geom->data.mesh->filter;
    280       break;
    281     case GEOM_SPHERE:
    282       filter = &geom->data.sphere->filter;
    283       break;
    284     default: FATAL("Unreachable code\n"); break;
    285   }
    286   ASSERT(filter->func);
    287 
    288   hit_setup(ctx->scnview, &ray_hit, &hit);
    289   is_hit_filtered = filter->func
    290     (&hit, ctx->ws_org, ctx->ws_dir, ctx->ws_range, ctx->data, filter->data);
    291   if(is_hit_filtered) {
    292     args->valid[0] = 0;
    293   }
    294 }