star-3d

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

s3d_scene_view_trace_ray.c (9731B)


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