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 }