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 }