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