s3d_scene_view.c (49330B)
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_device_c.h" 20 #include "s3d_scene_c.h" 21 #include "s3d_scene_view_c.h" 22 #include "s3d_shape_c.h" 23 24 #include <rsys/algorithm.h> 25 #include <rsys/float3.h> 26 #include <rsys/float33.h> 27 #include <rsys/mem_allocator.h> 28 29 /******************************************************************************* 30 * Helper functions 31 ******************************************************************************/ 32 static FINLINE int 33 aabb_is_degenerated(const float lower[3], const float upper[3]) 34 { 35 ASSERT(lower && upper); 36 return lower[0] > upper[0] || lower[1] > upper[1] || lower[2] > upper[2]; 37 } 38 39 static INLINE int 40 cmp_float(const void* a, const void* b) 41 { 42 const float key = *(const float*)a; 43 const float val = *(const float*)b; 44 if(key < val) return -1; 45 if(key > val) return +1; 46 return 0; 47 } 48 49 static INLINE int 50 cmp_float_to_fltui(const void* a, const void* b) 51 { 52 const float key = *(const float*)a; 53 const struct fltui* fltui = (const struct fltui*)b; 54 if(key < fltui->flt) return -1; 55 if(key > fltui->flt) return +1; 56 return 0; 57 } 58 59 static INLINE int 60 cmp_size_t_to_nprims_cdf(const void* a, const void* b) 61 { 62 const size_t key = *(const size_t*)a; 63 const struct nprims_cdf* nprims_cdf = (const struct nprims_cdf*)b; 64 if(key < nprims_cdf->nprims-1) return -1; 65 if(key > nprims_cdf->nprims-1) return +1; 66 return 0; 67 } 68 69 static INLINE void 70 scene_view_destroy_geometry(struct s3d_scene_view* scnview, struct geometry* geom) 71 { 72 ASSERT(geom); 73 if(geom->rtc) { 74 if(geom->rtc_id != RTC_INVALID_GEOMETRY_ID) { 75 rtcDetachGeometry(scnview->rtc_scn, geom->rtc_id); 76 geom->rtc_id = RTC_INVALID_GEOMETRY_ID; 77 } 78 rtcReleaseGeometry(geom->rtc); 79 geom->rtc = NULL; 80 scnview->rtc_scn_update = 1; /* Notify the scene upd */ 81 } 82 geometry_ref_put(geom); 83 } 84 85 static void 86 on_shape_detach 87 (const struct s3d_scene* scn, 88 const struct s3d_shape* shape, 89 void* data) 90 { 91 struct geometry** pgeom; 92 struct geometry* geom; 93 struct s3d_scene_view* scnview = (struct s3d_scene_view*)data; 94 unsigned shape_id; 95 ASSERT(scn && shape && data); 96 (void)scn; 97 98 S3D(shape_get_id(shape, &shape_id)); 99 pgeom = htable_geom_find(&scnview->cached_geoms, &shape_id); 100 101 /* The scnview did not register a geometry for this shape. Ignore the signal */ 102 if(!pgeom) return; 103 104 geom = *pgeom; 105 if(scnview->mask == 0) { 106 /* The scnview is NOT in use. Directly rm the cached geometry and notify 107 * that the scene AABB must be reevaluated */ 108 size_t n; (void)n; 109 scene_view_destroy_geometry(scnview, geom); 110 n = htable_geom_erase(&scnview->cached_geoms, &shape_id); 111 ASSERT(n == 1); 112 scnview->aabb_update = 1; 113 } else { 114 /* The scnview is in use. Delay the deletion of the cached geometry */ 115 res_T res = darray_uint_push_back(&scnview->detached_shapes, &shape_id); 116 if(res != RES_OK) FATAL("Insufficient memory.\n"); 117 } 118 } 119 120 static INLINE enum RTCBuildQuality 121 accel_struct_quality_to_rtc_build_quality 122 (enum s3d_accel_struct_quality quality) 123 { 124 enum RTCBuildQuality rtc_quality = RTC_BUILD_QUALITY_MEDIUM; 125 switch(quality) { 126 case S3D_ACCEL_STRUCT_QUALITY_LOW: 127 rtc_quality = RTC_BUILD_QUALITY_LOW; 128 break; 129 case S3D_ACCEL_STRUCT_QUALITY_MEDIUM: 130 rtc_quality = RTC_BUILD_QUALITY_MEDIUM; 131 break; 132 case S3D_ACCEL_STRUCT_QUALITY_HIGH: 133 rtc_quality = RTC_BUILD_QUALITY_HIGH; 134 break; 135 default: FATAL("Unreachable code\n"); break; 136 } 137 return rtc_quality; 138 } 139 140 static INLINE int 141 accel_struct_mask_to_rtc_scene_flags(const int mask) 142 { 143 int rtc_scene_flags = 0; 144 if(mask & S3D_ACCEL_STRUCT_FLAG_ROBUST) 145 rtc_scene_flags |= RTC_SCENE_FLAG_ROBUST; 146 if(mask & S3D_ACCEL_STRUCT_FLAG_DYNAMIC) 147 rtc_scene_flags |= RTC_SCENE_FLAG_DYNAMIC; 148 if(mask & S3D_ACCEL_STRUCT_FLAG_COMPACT) 149 rtc_scene_flags |= RTC_SCENE_FLAG_COMPACT; 150 return rtc_scene_flags; 151 } 152 153 static res_T 154 embree_geometry_register 155 (struct s3d_scene_view* scnview, 156 struct geometry* geom, 157 const struct s3d_accel_struct_conf* accel_struct_conf) 158 { 159 enum RTCBuildQuality rtc_build_quality = RTC_BUILD_QUALITY_MEDIUM; 160 ASSERT(scnview && geom && accel_struct_conf); 161 162 rtc_build_quality = accel_struct_quality_to_rtc_build_quality 163 (accel_struct_conf->quality); 164 165 /* Create the Embree geometry if it is not valid */ 166 if(geom->rtc != NULL) { 167 switch(geom->type) { 168 case GEOM_MESH: 169 if(geom->rtc_build_quality != rtc_build_quality) { 170 /* Update the build quality of the geometry */ 171 rtcSetGeometryBuildQuality(geom->rtc, rtc_build_quality); 172 rtcCommitGeometry(geom->rtc); 173 geom->rtc_build_quality = rtc_build_quality; 174 scnview->rtc_scn_update = 1; 175 } 176 break; 177 case GEOM_INSTANCE: 178 /* If the geometry is an instance one have to update it if the 179 * instantiated geometry was updated. Currently, we have no simple way to 180 * know if the geometry was upd or not so we simply force the update. */ 181 rtcCommitGeometry(geom->rtc); 182 scnview->rtc_scn_update = 1; 183 break; 184 case GEOM_SPHERE: /* Do nothing */ break; 185 default: FATAL("Unreachable code\n"); break; 186 } 187 } else { 188 switch(geom->type) { 189 case GEOM_MESH: 190 geom->rtc = rtcNewGeometry 191 (scnview->scn->dev->rtc, RTC_GEOMETRY_TYPE_TRIANGLE); 192 break; 193 case GEOM_INSTANCE: 194 geom->rtc = rtcNewGeometry 195 (scnview->scn->dev->rtc, RTC_GEOMETRY_TYPE_INSTANCE); 196 rtcSetGeometryInstancedScene(geom->rtc, geom->data.instance->scnview->rtc_scn); 197 break; 198 case GEOM_SPHERE: 199 geom->rtc = rtcNewGeometry 200 (scnview->scn->dev->rtc, RTC_GEOMETRY_TYPE_USER); 201 /* Setup geometry callbacks. Note that the "occluded" is not set since 202 * rtcOccluded is not used */ 203 rtcSetGeometryUserPrimitiveCount(geom->rtc, 1); 204 rtcSetGeometryBoundsFunction(geom->rtc, geometry_rtc_sphere_bounds, NULL); 205 rtcSetGeometryIntersectFunction(geom->rtc, geometry_rtc_sphere_intersect); 206 break; 207 default: FATAL("Unreachable code\n"); break; 208 } 209 if(geom->rtc == NULL) 210 return RES_UNKNOWN_ERR; 211 212 if(geom->type == GEOM_MESH) { 213 /* Set the build quality of the geometry */ 214 rtcSetGeometryBuildQuality(geom->rtc, rtc_build_quality); 215 geom->rtc_build_quality = rtc_build_quality; 216 } 217 218 /* Set the Star-3D representation of the geometry to the Embree geometry */ 219 rtcSetGeometryUserData(geom->rtc, geom); 220 221 /* Attach the Embree geometry to the Embree scene of the scene view */ 222 geom->rtc_id = rtcAttachGeometry(scnview->rtc_scn, geom->rtc); 223 224 scnview->rtc_scn_update = 1; 225 } 226 return RES_OK; 227 } 228 229 static INLINE res_T 230 embree_geometry_setup_positions 231 (struct s3d_scene_view* scnview, struct geometry* geom) 232 { 233 RTCBuffer buf = NULL; 234 float* verts; 235 size_t nverts; 236 res_T res = RES_OK; 237 ASSERT(scnview && geom && geom->type == GEOM_MESH && geom->rtc); 238 239 verts = mesh_get_pos(geom->data.mesh); 240 nverts = mesh_get_nverts(geom->data.mesh); 241 242 buf = rtcNewSharedBuffer 243 (scnview->scn->dev->rtc, verts, sizeof(float[3])*nverts); 244 if(!buf) { 245 res = rtc_error_to_res_T(rtcGetDeviceError(scnview->scn->dev->rtc)); 246 goto error; 247 } 248 249 rtcSetGeometryBuffer(geom->rtc, RTC_BUFFER_TYPE_VERTEX, 0/*slot*/, 250 RTC_FORMAT_FLOAT3, buf, 0/*offset*/, sizeof(float[3])/*stride*/, nverts); 251 rtcUpdateGeometryBuffer(geom->rtc, RTC_BUFFER_TYPE_VERTEX, 0); 252 253 exit: 254 if(buf) rtcReleaseBuffer(buf); 255 return res; 256 error: 257 goto exit; 258 } 259 260 static INLINE res_T 261 embree_geometry_setup_indices 262 (struct s3d_scene_view* scnview, struct geometry* geom) 263 { 264 RTCBuffer buf = NULL; 265 size_t ntris; 266 uint32_t* ids; 267 res_T res = RES_OK; 268 ASSERT(scnview && geom && geom->type == GEOM_MESH && geom->rtc); 269 270 ids = mesh_get_ids(geom->data.mesh); 271 ntris = mesh_get_ntris(geom->data.mesh); 272 273 buf = rtcNewSharedBuffer 274 (scnview->scn->dev->rtc, ids, sizeof(uint32_t[3])*ntris); 275 if(!buf) { 276 res = rtc_error_to_res_T(rtcGetDeviceError(scnview->scn->dev->rtc)); 277 goto error; 278 } 279 280 rtcSetGeometryBuffer(geom->rtc, RTC_BUFFER_TYPE_INDEX, 0/*slot*/, 281 RTC_FORMAT_UINT3, buf, 0/*offset*/, sizeof(uint32_t[3])/*stride*/, ntris); 282 rtcUpdateGeometryBuffer(geom->rtc, RTC_BUFFER_TYPE_INDEX, 0); 283 284 exit: 285 if(buf) rtcReleaseBuffer(buf); 286 return res; 287 error: 288 goto exit; 289 } 290 291 static INLINE void 292 embree_geometry_setup_enable_state 293 (struct s3d_scene_view* scnview, struct geometry* geom) 294 { 295 ASSERT(scnview && geom); 296 (void)scnview; 297 if(geom->is_enabled) { 298 rtcEnableGeometry(geom->rtc); 299 } else { 300 rtcDisableGeometry(geom->rtc); 301 } 302 } 303 304 static INLINE void 305 embree_geometry_setup_filter_function 306 (struct s3d_scene_view* scnview, struct geometry* geom) 307 { 308 ASSERT(scnview && geom && geom->rtc &&geom->type == GEOM_MESH); 309 (void)scnview; 310 311 if(!geom->data.mesh->filter.func) { 312 rtcSetGeometryIntersectFilterFunction(geom->rtc, NULL); 313 } else { 314 rtcSetGeometryIntersectFilterFunction(geom->rtc, rtc_hit_filter_wrapper); 315 } 316 } 317 318 static INLINE void 319 embree_geometry_setup_transform 320 (struct s3d_scene_view* scnview, struct geometry* geom) 321 { 322 ASSERT(scnview && geom && geom->rtc != NULL); 323 ASSERT(geom->type == GEOM_INSTANCE); 324 (void)scnview; 325 rtcSetGeometryTransform(geom->rtc, 0, RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR, 326 geom->data.instance->transform); 327 } 328 329 static INLINE res_T 330 scene_view_setup_embree 331 (struct s3d_scene_view* scnview, 332 const struct s3d_accel_struct_conf* accel_struct_conf) 333 { 334 struct htable_geom_iterator it, end; 335 int rtc_outdated = 0; 336 int rtc_scn_flags = 0; 337 enum RTCBuildQuality rtc_scn_build_quality = 0; 338 res_T res = RES_OK; 339 ASSERT(scnview); 340 341 rtc_scn_flags = accel_struct_mask_to_rtc_scene_flags 342 (accel_struct_conf->mask); 343 rtc_scn_build_quality = accel_struct_quality_to_rtc_build_quality 344 (accel_struct_conf->quality); 345 346 /* The rtc_scn could be already allocated since the scene views are cached */ 347 if(!scnview->rtc_scn) { 348 scnview->rtc_scn = rtcNewScene(scnview->scn->dev->rtc); 349 if(!scnview->rtc_scn) { 350 res = rtc_error_to_res_T(rtcGetDeviceError(scnview->scn->dev->rtc)); 351 goto error; 352 } 353 rtcSetSceneFlags(scnview->rtc_scn, rtc_scn_flags); 354 scnview->rtc_scn_flags = rtc_scn_flags; 355 rtc_outdated = 1; 356 } 357 358 /* Check if the scene flags were updated */ 359 if(scnview->rtc_scn_flags != rtc_scn_flags) { 360 rtcSetSceneFlags(scnview->rtc_scn, rtc_scn_flags); 361 scnview->rtc_scn_flags = rtc_scn_flags; 362 rtc_outdated = 1; 363 } 364 365 /* Check if the build quality was updated */ 366 if(scnview->rtc_scn_build_quality != rtc_scn_build_quality) { 367 rtcSetSceneBuildQuality(scnview->rtc_scn, rtc_scn_build_quality); 368 scnview->rtc_scn_build_quality = rtc_scn_build_quality; 369 rtc_outdated = 1; 370 } 371 372 htable_geom_begin(&scnview->cached_geoms, &it); 373 htable_geom_end(&scnview->cached_geoms, &end); 374 375 while(!htable_geom_iterator_eq(&it, &end)) { 376 struct geometry** pgeom = htable_geom_iterator_data_get(&it); 377 struct geometry* geom = *pgeom; 378 379 htable_geom_iterator_next(&it); 380 381 /* Define whether or not the embree scene is outdated */ 382 if(geom->embree_outdated_mask) rtc_outdated = 1; 383 if(geom->type == GEOM_INSTANCE && geom->data.instance->scnview->rtc_commit) 384 rtc_outdated = 1; 385 386 /* Register the embree geometry */ 387 res = embree_geometry_register(scnview, geom, accel_struct_conf); 388 if(res != RES_OK) goto error; 389 390 /* Flush the embree geometry states */ 391 if((geom->embree_outdated_mask & EMBREE_VERTICES) != 0) { 392 res = embree_geometry_setup_positions(scnview, geom); 393 if(res != RES_OK) goto error; 394 } 395 if((geom->embree_outdated_mask & EMBREE_INDICES) != 0) { 396 embree_geometry_setup_indices(scnview, geom); 397 if(res != RES_OK) goto error; 398 } 399 if((geom->embree_outdated_mask & EMBREE_ENABLE) != 0) 400 embree_geometry_setup_enable_state(scnview, geom); 401 if((geom->embree_outdated_mask & EMBREE_FILTER_FUNCTION) != 0) 402 embree_geometry_setup_filter_function(scnview, geom); 403 if((geom->embree_outdated_mask & EMBREE_TRANSFORM) != 0) 404 embree_geometry_setup_transform(scnview, geom); 405 406 /* Commit the updated geometry */ 407 if(geom->embree_outdated_mask) 408 rtcCommitGeometry(geom->rtc); 409 410 geom->embree_outdated_mask = 0; 411 } 412 413 rtc_outdated = rtc_outdated || scnview->rtc_scn_update; 414 415 /* Commit the embree changes */ 416 if(rtc_outdated) { 417 rtcCommitScene(scnview->rtc_scn); 418 scnview->rtc_commit = 1; /* Notify that the RTC scene was committed */ 419 scnview->rtc_scn_update = 0; 420 } 421 422 exit: 423 return res; 424 error: 425 if(scnview->rtc_scn) { 426 rtcReleaseScene(scnview->rtc_scn); 427 scnview->rtc_scn = NULL; 428 } 429 goto exit; 430 } 431 432 static res_T 433 scene_view_register_mesh 434 (struct s3d_scene_view* scnview, 435 struct s3d_shape* shape) 436 { 437 struct geometry** pgeom = NULL; 438 struct geometry* geom = NULL; 439 size_t iattr; 440 unsigned shape_id; 441 int is_valid; 442 res_T res = RES_OK; 443 ASSERT(scnview && shape && shape->type == GEOM_MESH); 444 445 is_valid = shape->data.mesh->indices && shape->data.mesh->attribs[S3D_POSITION]; 446 447 /* Retrieve the cached geometry */ 448 S3D(shape_get_id(shape, &shape_id)); 449 pgeom = htable_geom_find(&scnview->cached_geoms, &shape_id); 450 if(pgeom) { 451 geom = *pgeom; 452 if(!is_valid) { 453 scene_view_destroy_geometry(scnview, geom); 454 htable_geom_erase(&scnview->cached_geoms, &shape_id); 455 } 456 } else if(is_valid) { 457 res = geometry_create(scnview->scn->dev, &geom); 458 if(res != RES_OK) goto error; 459 res = mesh_create(scnview->scn->dev, &geom->data.mesh); 460 if(res != RES_OK) goto error; 461 geom->type = GEOM_MESH; 462 res = htable_geom_set(&scnview->cached_geoms, &shape_id, &geom); 463 if(res != RES_OK) goto error; 464 geom->name = shape->id.index; 465 } 466 467 if(!is_valid) goto exit; 468 469 /* Get a reference onto the shape mesh indices */ 470 if(geom->data.mesh->indices != shape->data.mesh->indices) { 471 geom->embree_outdated_mask |= EMBREE_INDICES; 472 if(geom->data.mesh->indices) { /* Release the previous index buffer */ 473 index_buffer_ref_put(geom->data.mesh->indices); 474 geom->data.mesh->indices = NULL; 475 } 476 ASSERT(shape->data.mesh->indices); 477 index_buffer_ref_get(shape->data.mesh->indices); 478 geom->data.mesh->indices = shape->data.mesh->indices; 479 } 480 481 /* Get a reference onto the shape mesh attribs */ 482 FOR_EACH(iattr, 0, S3D_ATTRIBS_COUNT__) { 483 if(geom->data.mesh->attribs[iattr] == shape->data.mesh->attribs[iattr]) 484 continue; 485 486 geom->embree_outdated_mask |= EMBREE_VERTICES; 487 488 if(geom->data.mesh->attribs[iattr]) { /* Release the previous buffer */ 489 vertex_buffer_ref_put(geom->data.mesh->attribs[iattr]); 490 geom->data.mesh->attribs[iattr] = NULL; 491 } 492 493 /* This attrib does not exist anymore */ 494 if(!shape->data.mesh->attribs[iattr]) 495 continue; 496 497 /* Get the new buffer */ 498 vertex_buffer_ref_get(shape->data.mesh->attribs[iattr]); 499 geom->data.mesh->attribs[iattr] = shape->data.mesh->attribs[iattr]; 500 geom->data.mesh->attribs_type[iattr] = shape->data.mesh->attribs_type[iattr]; 501 } 502 503 /* Update the enable flag */ 504 if(geom->is_enabled != shape->is_enabled) { 505 geom->is_enabled = shape->is_enabled; 506 geom->embree_outdated_mask |= EMBREE_ENABLE; 507 } 508 509 /* Update the filter function */ 510 if(geom->data.mesh->filter.func != shape->data.mesh->filter.func 511 || geom->data.mesh->filter.data != shape->data.mesh->filter.data) { 512 geom->data.mesh->filter = shape->data.mesh->filter; 513 geom->embree_outdated_mask |= EMBREE_FILTER_FUNCTION; 514 } 515 516 geom->flip_surface = shape->flip_surface; 517 518 /* The geometry is updated => recompute the scene AABB */ 519 if(geom->embree_outdated_mask 520 & (EMBREE_VERTICES|EMBREE_INDICES|EMBREE_ENABLE)) { 521 scnview->aabb_update = 1; 522 } 523 524 exit: 525 return res; 526 error: 527 goto exit; 528 } 529 530 static res_T 531 scene_view_register_sphere 532 (struct s3d_scene_view* scnview, 533 struct s3d_shape* shape) 534 { 535 struct geometry** pgeom = NULL; 536 struct geometry* geom = NULL; 537 unsigned shape_id; 538 int is_valid; 539 res_T res = RES_OK; 540 ASSERT(scnview && shape && shape->type == GEOM_SPHERE); 541 542 is_valid = !sphere_is_degenerated(shape->data.sphere); 543 544 /* Retrieve the cached geometry */ 545 S3D(shape_get_id(shape, &shape_id)); 546 pgeom = htable_geom_find(&scnview->cached_geoms, &shape_id); 547 if(pgeom) { 548 geom = *pgeom; 549 if(!is_valid) { 550 scene_view_destroy_geometry(scnview, geom); 551 htable_geom_erase(&scnview->cached_geoms, &shape_id); 552 } 553 } else if(is_valid) { 554 res = geometry_create(scnview->scn->dev, &geom); 555 if(res != RES_OK) goto error; 556 res = sphere_create(scnview->scn->dev, &geom->data.sphere); 557 if(res != RES_OK) goto error; 558 geom->type = GEOM_SPHERE; 559 res = htable_geom_set(&scnview->cached_geoms, &shape_id, &geom); 560 if(res != RES_OK) goto error; 561 geom->name = shape->id.index; 562 geom->embree_outdated_mask |= EMBREE_USER_GEOMETRY; 563 } 564 565 if(!is_valid) goto exit; 566 567 /* Setup the sphere radius */ 568 if(geom->data.sphere->radius != shape->data.sphere->radius) { 569 geom->data.sphere->radius = shape->data.sphere->radius; 570 geom->embree_outdated_mask |= EMBREE_USER_GEOMETRY; 571 } 572 573 /* Setup the sphere position */ 574 if(!f3_eq(geom->data.sphere->pos, shape->data.sphere->pos)) { 575 f3_set(geom->data.sphere->pos, shape->data.sphere->pos); 576 geom->embree_outdated_mask |= EMBREE_USER_GEOMETRY; 577 } 578 579 if(geom->is_enabled != shape->is_enabled) { 580 geom->is_enabled = shape->is_enabled; 581 geom->embree_outdated_mask |= EMBREE_ENABLE; 582 } 583 584 /* The geometry is updated => recompute the scene AABB */ 585 if(geom->embree_outdated_mask & (EMBREE_USER_GEOMETRY|EMBREE_ENABLE)) { 586 scnview->aabb_update = 1; 587 } 588 589 /* Update the filter function. Actually, filter functions are supported for 590 * built-in geometries only. For user defined geometries one has to 591 * explicitly call the filter function in the user defined intersection 592 * function. */ 593 if(geom->data.sphere->filter.func != shape->data.sphere->filter.func 594 || geom->data.sphere->filter.data != shape->data.sphere->filter.data) { 595 geom->data.sphere->filter = shape->data.sphere->filter; 596 /* The user defined geometries do not support filter function => the 597 * EMBREE_FILTER_FUNCTION flag is thus invalid for them. Enable the 598 * EMBREE_USER_GEOMETRY flag instead to notify its update. */ 599 geom->embree_outdated_mask |= EMBREE_USER_GEOMETRY; 600 } 601 602 geom->flip_surface = shape->flip_surface; 603 604 exit: 605 return res; 606 error: 607 goto exit; 608 } 609 610 static res_T 611 scene_view_register_instance 612 (struct s3d_scene_view* scnview, 613 struct s3d_shape* shape, 614 const int mask) 615 { 616 struct geometry** pgeom = NULL; 617 struct geometry* geom = NULL; 618 struct s3d_scene_view** pview = NULL; 619 struct s3d_scene_view* view = NULL; 620 unsigned shape_id; 621 res_T res = RES_OK; 622 ASSERT(scnview && shape && shape->type == GEOM_INSTANCE); 623 624 /* The instance cannot contain instances, i.e. one instancing level is 625 * supported */ 626 if(shape->data.instance->scene->instances_count != 0) { 627 res = RES_BAD_ARG; 628 goto error; 629 } 630 631 /* Recursively create scnview on the scene to instantiate if necessary */ 632 pview = htable_instview_find 633 (&scnview->instviews, &shape->data.instance->scene); 634 if(pview) { 635 view = *pview; 636 } else { 637 res = s3d_scene_view_create 638 (shape->data.instance->scene, mask, &view); 639 if(res != RES_OK) goto error; 640 res = htable_instview_set 641 (&scnview->instviews, &shape->data.instance->scene, &view); 642 if(res != RES_OK) goto error; 643 } 644 645 /* Create the scene instance of the geometry if necessary */ 646 S3D(shape_get_id(shape, &shape_id)); 647 pgeom = htable_geom_find(&scnview->cached_geoms, &shape_id); 648 if(pgeom) { 649 geom = *pgeom; 650 } else { 651 res = geometry_create(scnview->scn->dev, &geom); 652 if(res != RES_OK) goto error; 653 geom->type = GEOM_INSTANCE; 654 res = instance_create(shape->data.instance->scene, &geom->data.instance); 655 if(res != RES_OK) goto error; 656 res = htable_geom_set(&scnview->cached_geoms, &shape_id, &geom); 657 if(res != RES_OK) goto error; 658 geom->name = shape->id.index; 659 660 /* Force the commit of the newly created geometry by setting up its 661 * outdated mask */ 662 geom->embree_outdated_mask |= EMBREE_TRANSFORM; 663 } 664 ASSERT(geom->data.instance->scene == shape->data.instance->scene); 665 geom->data.instance->scnview = view; 666 667 /* Update the Embree instance transformation if necessary */ 668 if(!f33_eq(shape->data.instance->transform, geom->data.instance->transform) 669 || !f3_eq(shape->data.instance->transform+9, geom->data.instance->transform+9)) { 670 geom->embree_outdated_mask |= EMBREE_TRANSFORM; 671 f33_set(geom->data.instance->transform, shape->data.instance->transform); 672 f3_set(geom->data.instance->transform+9, shape->data.instance->transform+9); 673 } 674 675 /* Update the enable flag */ 676 if(geom->is_enabled != shape->is_enabled) { 677 geom->is_enabled = shape->is_enabled; 678 geom->embree_outdated_mask |= EMBREE_ENABLE; 679 } 680 681 /* The instance transform was updated => recompute the scene AABB */ 682 if(geom->embree_outdated_mask & (EMBREE_TRANSFORM|EMBREE_ENABLE)) { 683 scnview->aabb_update = 1; 684 } 685 686 geom->flip_surface = shape->flip_surface; 687 688 exit: 689 return res; 690 error: 691 goto exit; 692 } 693 694 static res_T 695 scene_view_compute_cdf(struct s3d_scene_view* scnview) 696 { 697 struct htable_geom_iterator it, end; 698 size_t len; 699 float area = 0.f; 700 res_T res = RES_OK; 701 ASSERT(scnview); 702 ASSERT(darray_fltui_size_get(&scnview->cdf) == 0); 703 704 htable_geom_begin(&scnview->cached_geoms, &it); 705 htable_geom_end(&scnview->cached_geoms, &end); 706 707 while(!htable_geom_iterator_eq(&it, &end)) { 708 const unsigned* shape_id = htable_geom_iterator_key_get(&it); 709 struct geometry* geom = *htable_geom_iterator_data_get(&it); 710 struct fltui fltui; 711 712 htable_geom_iterator_next(&it); 713 714 if(!geom->is_enabled) continue; 715 716 switch(geom->type) { 717 case GEOM_MESH: 718 res = mesh_compute_cdf(geom->data.mesh); 719 if(res != RES_OK) goto error; 720 len = darray_float_size_get(&geom->data.mesh->cdf); 721 if(len) { 722 area += darray_float_cdata_get(&geom->data.mesh->cdf)[len - 1]; 723 } 724 break; 725 case GEOM_SPHERE: 726 len = 1; 727 area += sphere_compute_area(geom->data.sphere); 728 break; 729 case GEOM_INSTANCE: 730 /* The instance CDF was computed during its scnview synchronisation */ 731 len = darray_fltui_size_get(&geom->data.instance->scnview->cdf); 732 if(len) { 733 area += darray_fltui_cdata_get 734 (&geom->data.instance->scnview->cdf)[len - 1].flt; 735 } 736 break; 737 default: FATAL("Unreachable code\n"); break; 738 } 739 fltui.ui = *shape_id; 740 fltui.flt = area; 741 if(len) { 742 res = darray_fltui_push_back(&scnview->cdf, &fltui); 743 if(res != RES_OK) goto error; 744 } 745 } 746 exit: 747 return res; 748 error: 749 darray_fltui_clear(&scnview->cdf); 750 goto exit; 751 } 752 753 static res_T 754 scene_view_compute_nprims_cdf 755 (struct s3d_scene_view* scnview, 756 const char store_cdf) 757 { 758 struct htable_geom_iterator it, end; 759 size_t len; 760 unsigned nprims; 761 res_T res = RES_OK; 762 ASSERT(scnview); 763 ASSERT(darray_nprims_cdf_size_get(&scnview->nprims_cdf) == 0); 764 765 htable_geom_begin(&scnview->cached_geoms, &it); 766 htable_geom_end(&scnview->cached_geoms, &end); 767 768 nprims = 0; 769 while(!htable_geom_iterator_eq(&it, &end)) { 770 const unsigned* shape_id = htable_geom_iterator_key_get(&it); 771 struct geometry* geom = *htable_geom_iterator_data_get(&it); 772 struct nprims_cdf cdf; 773 774 htable_geom_iterator_next(&it); 775 776 if(!geom->is_enabled) continue; 777 778 geom->scene_prim_id_offset = nprims; 779 switch(geom->type) { 780 case GEOM_MESH: 781 len = mesh_get_ntris(geom->data.mesh); 782 nprims += (unsigned)len; 783 break; 784 case GEOM_SPHERE: 785 len = 1; 786 nprims += 1; 787 break; 788 case GEOM_INSTANCE: 789 /* The instance CDF was computed during its scnview synchronisation */ 790 len = darray_nprims_cdf_size_get 791 (&geom->data.instance->scnview->nprims_cdf); 792 if(len) { 793 nprims += darray_nprims_cdf_cdata_get 794 (&geom->data.instance->scnview->nprims_cdf)[len - 1].nprims; 795 } 796 break; 797 default: FATAL("Unreachable code\n"); break; 798 } 799 800 cdf.nprims = nprims; 801 cdf.ishape = *shape_id; 802 if(store_cdf && len) { 803 res = darray_nprims_cdf_push_back(&scnview->nprims_cdf, &cdf); 804 if(res != RES_OK) goto error; 805 } 806 } 807 exit: 808 return res; 809 error: 810 darray_nprims_cdf_clear(&scnview->nprims_cdf); 811 goto exit; 812 } 813 814 static void 815 scene_view_compute_scene_aabb(struct s3d_scene_view* scnview) 816 { 817 struct htable_geom_iterator it, end; 818 float lower[3], upper[3]; 819 820 ASSERT(scnview->lower[0] == FLT_MAX && scnview->upper[0] == -FLT_MAX); 821 ASSERT(scnview->lower[1] == FLT_MAX && scnview->upper[1] == -FLT_MAX); 822 ASSERT(scnview->lower[2] == FLT_MAX && scnview->upper[2] == -FLT_MAX); 823 824 htable_geom_begin(&scnview->cached_geoms, &it); 825 htable_geom_end(&scnview->cached_geoms, &end); 826 827 while(!htable_geom_iterator_eq(&it, &end)) { 828 struct instance* inst; 829 struct geometry* geom = *htable_geom_iterator_data_get(&it); 830 831 htable_geom_iterator_next(&it); 832 833 if(!geom->is_enabled) continue; 834 835 switch(geom->type) { 836 case GEOM_MESH: 837 mesh_compute_aabb(geom->data.mesh, lower, upper); 838 break; 839 case GEOM_SPHERE: 840 sphere_compute_aabb(geom->data.sphere, lower, upper); 841 break; 842 case GEOM_INSTANCE: 843 /* Note that the instance AABB was computed during its scnview 844 * synchronisation. */ 845 inst = geom->data.instance; 846 if(aabb_is_degenerated(inst->scnview->lower, inst->scnview->upper)) { 847 /* Empty scene */ 848 f3_splat(lower, FLT_MAX); 849 f3_splat(upper,-FLT_MAX); 850 } else { 851 /* Transform local scene AABB bounds in world space */ 852 f33_mulf3(lower, inst->transform, inst->scnview->lower); 853 f33_mulf3(upper, inst->transform, inst->scnview->upper); 854 f3_add(lower, inst->transform + 9, lower); 855 f3_add(upper, inst->transform + 9, upper); 856 /* Define the world space AABB of the transformed local scene AABB */ 857 if(lower[0] > upper[0]) SWAP(float, lower[0], upper[0]); 858 if(lower[1] > upper[1]) SWAP(float, lower[1], upper[1]); 859 if(lower[2] > upper[2]) SWAP(float, lower[2], upper[2]); 860 } 861 break; 862 default: FATAL("Unreachable code\n"); break; 863 } 864 f3_min(scnview->lower, scnview->lower, lower); 865 f3_max(scnview->upper, scnview->upper, upper); 866 } 867 } 868 869 static float 870 scene_view_compute_volume 871 (struct s3d_scene_view* scnview, 872 const char flip_surface) 873 { 874 struct htable_geom_iterator it, end; 875 float volume; 876 877 ASSERT(scnview); 878 htable_geom_begin(&scnview->cached_geoms, &it); 879 htable_geom_end(&scnview->cached_geoms, &end); 880 881 volume = 0.f; 882 while(!htable_geom_iterator_eq(&it, &end)) { 883 struct geometry* geom = *htable_geom_iterator_data_get(&it); 884 const char flip = geom->flip_surface ^ flip_surface; 885 886 htable_geom_iterator_next(&it); 887 888 if(!geom->is_enabled) continue; 889 890 switch(geom->type) { 891 case GEOM_MESH: 892 volume += mesh_compute_volume(geom->data.mesh, flip); 893 break; 894 case GEOM_SPHERE: 895 volume += flip 896 ? -sphere_compute_volume(geom->data.sphere) 897 : sphere_compute_volume(geom->data.sphere); 898 break; 899 case GEOM_INSTANCE: 900 volume += scene_view_compute_volume(geom->data.instance->scnview, flip); 901 break; 902 default: FATAL("Unreachable code\n"); break; 903 } 904 } 905 906 if(volume < 0.f) { 907 log_warning(scnview->scn->dev, 908 "%s:\n" 909 "\tthe volume is negative. The scene shapes might not represent closed 2D\n" 910 "\tmanifold volumes, or their surface normal might not point inward the volume.\n", 911 FUNC_NAME); 912 } 913 914 return volume; 915 } 916 917 static res_T 918 scene_view_sync 919 (struct s3d_scene_view* scnview, 920 const int mask, 921 const struct s3d_accel_struct_conf* accel_struct_conf) 922 { 923 struct htable_shape_iterator it, end; 924 res_T res = RES_OK; 925 926 ASSERT(scnview && accel_struct_conf); 927 928 /* Commit the scene shape to the scnview */ 929 htable_shape_begin(&scnview->scn->shapes, &it); 930 htable_shape_end(&scnview->scn->shapes, &end); 931 while(!htable_shape_iterator_eq(&it, &end)) { 932 struct s3d_shape** pshape = htable_shape_iterator_data_get(&it); 933 struct s3d_shape* shape = *pshape; 934 935 switch(shape->type) { 936 case GEOM_INSTANCE: 937 res = scene_view_register_instance(scnview, shape, mask); 938 break; 939 case GEOM_MESH: 940 res = scene_view_register_mesh(scnview, shape); 941 break; 942 case GEOM_SPHERE: 943 res = scene_view_register_sphere(scnview, shape); 944 break; 945 default: FATAL("Unreachable code\n"); break; 946 } 947 if(res != RES_OK) goto error; 948 htable_shape_iterator_next(&it); 949 } 950 951 /* Setup the scene for the S3D_TRACE scnview */ 952 if((mask & S3D_TRACE) != 0) { 953 res = scene_view_setup_embree(scnview, accel_struct_conf); 954 if(res != RES_OK) goto error; 955 } 956 /* Setup the scene for the S3D_SAMPLE scnview */ 957 if((mask & S3D_SAMPLE) != 0) { 958 res = scene_view_compute_cdf(scnview); 959 if(res != RES_OK) goto error; 960 } 961 962 /* Compute the scene AABB if it is updated */ 963 if(scnview->aabb_update) { 964 if((mask & S3D_TRACE) == 0) { 965 /* Compute from scratch */ 966 f3_splat(scnview->lower, FLT_MAX); 967 f3_splat(scnview->upper,-FLT_MAX); 968 scene_view_compute_scene_aabb(scnview); 969 } else { 970 /* Retrieve the scene AABB from Embree since it was already computed to 971 * build the acceleration data structure */ 972 struct RTCBounds bounds; 973 rtcGetSceneBounds(scnview->rtc_scn, &bounds); 974 scnview->lower[0] = bounds.lower_x; 975 scnview->lower[1] = bounds.lower_y; 976 scnview->lower[2] = bounds.lower_z; 977 scnview->upper[0] = bounds.upper_x; 978 scnview->upper[1] = bounds.upper_y; 979 scnview->upper[2] = bounds.upper_z; 980 } 981 scnview->aabb_update = 0; 982 } 983 984 /* Setup the scene for the scene_primitive_id/S3D_GET_PRIMITIVE scnview */ 985 res = scene_view_compute_nprims_cdf(scnview, (mask & S3D_GET_PRIMITIVE)!=0); 986 if(res != RES_OK) goto error; 987 988 scnview->mask = mask; 989 990 exit: 991 return res; 992 error: 993 goto exit; 994 } 995 996 static res_T 997 scene_view_create(struct s3d_scene* scn, struct s3d_scene_view** out_scnview) 998 { 999 struct s3d_scene_view* scnview = NULL; 1000 res_T res = RES_OK; 1001 ASSERT(scn && out_scnview); 1002 1003 if(!is_list_empty(&scn->scnviews)) { 1004 /* Retrieve an already allocated scnview */ 1005 scnview = CONTAINER_OF(list_head(&scn->scnviews), struct s3d_scene_view, node); 1006 list_del(&scnview->node); 1007 ref_get(&scnview->ref); 1008 } else { 1009 scnview = (struct s3d_scene_view*)MEM_CALLOC 1010 (scn->dev->allocator, 1, sizeof(struct s3d_scene_view)); 1011 if(!scnview) { 1012 res = RES_MEM_ERR; 1013 goto error; 1014 } 1015 list_init(&scnview->node); 1016 htable_geom_init(scn->dev->allocator, &scnview->cached_geoms); 1017 darray_fltui_init(scn->dev->allocator, &scnview->cdf); 1018 darray_nprims_cdf_init(scn->dev->allocator, &scnview->nprims_cdf); 1019 htable_instview_init(scn->dev->allocator, &scnview->instviews); 1020 darray_uint_init(scn->dev->allocator, &scnview->detached_shapes); 1021 f3_splat(scnview->lower, FLT_MAX); 1022 f3_splat(scnview->upper,-FLT_MAX); 1023 ref_init(&scnview->ref); 1024 scnview->rtc_scn_build_quality = RTC_BUILD_QUALITY_MEDIUM; 1025 1026 CLBK_INIT(&scnview->on_shape_detach_cb); 1027 CLBK_SETUP(&scnview->on_shape_detach_cb, on_shape_detach, scnview); 1028 SIG_CONNECT_CLBK(&scn->sig_shape_detach, &scnview->on_shape_detach_cb); 1029 } 1030 S3D(scene_ref_get(scn)); 1031 scnview->scn = scn; 1032 exit: 1033 *out_scnview = scnview; 1034 return res; 1035 error: 1036 if(scnview) { 1037 S3D(scene_view_ref_put(scnview)); 1038 scnview = NULL; 1039 } 1040 goto exit; 1041 } 1042 1043 static void 1044 scene_view_release(ref_T* ref) 1045 { 1046 struct htable_instview_iterator it_view, end_view; 1047 struct htable_geom_iterator it_geom, end_geom; 1048 struct s3d_scene_view* scnview = CONTAINER_OF(ref, struct s3d_scene_view, ref); 1049 size_t i, n; 1050 ASSERT(ref); 1051 1052 /* Release the scnview of the instances */ 1053 htable_instview_begin(&scnview->instviews, &it_view); 1054 htable_instview_end(&scnview->instviews, &end_view); 1055 while(!htable_instview_iterator_eq(&it_view, &end_view)) { 1056 struct s3d_scene_view* view = *htable_instview_iterator_data_get(&it_view); 1057 htable_instview_iterator_next(&it_view); 1058 S3D(scene_view_ref_put(view)); 1059 } 1060 htable_instview_clear(&scnview->instviews); 1061 1062 /* Reset the scnview of the cached instance. Note that this step is actually 1063 * not necessary but this reset is "appreciated" in debug */ 1064 htable_geom_begin(&scnview->cached_geoms, &it_geom); 1065 htable_geom_end(&scnview->cached_geoms, &end_geom); 1066 while(!htable_geom_iterator_eq(&it_geom, &end_geom)) { 1067 struct geometry* geom = *htable_geom_iterator_data_get(&it_geom); 1068 htable_geom_iterator_next(&it_geom); 1069 if(geom->type != GEOM_INSTANCE) continue; 1070 geom->data.instance->scnview = NULL; 1071 } 1072 1073 /* Remove the geometry of the shapes detached while the scnview was active */ 1074 n = darray_uint_size_get(&scnview->detached_shapes); 1075 FOR_EACH(i, 0, n) { 1076 const unsigned shape_id = darray_uint_cdata_get(&scnview->detached_shapes)[i]; 1077 struct geometry** pgeom = htable_geom_find(&scnview->cached_geoms, &shape_id); 1078 struct geometry* geom = NULL; 1079 size_t tmp; (void)tmp; 1080 1081 /* It is possible that there is no geometry in the cache associated with the 1082 * registered shape Id. This is the sign that this geometry has already 1083 * been removed from the cache, and that its shape has been detached several 1084 * times from the scene while the scene view was active. 1085 * 1086 * Indeed, each detachment is simply saved in the "detached shapes" list, 1087 * without checking that the shape has already been saved as being to be 1088 * detached. In other words, only the first occurrence of the shape in the 1089 * said list is valid, the others must be ignored. */ 1090 if(pgeom == NULL) { ASSERT(n > 0); continue; } 1091 1092 geom = *pgeom; 1093 scene_view_destroy_geometry(scnview, geom); 1094 tmp = htable_geom_erase(&scnview->cached_geoms, &shape_id); 1095 ASSERT(tmp == 1); 1096 scnview->aabb_update = 1; /* The scene AABB must be reevaluated */ 1097 } 1098 darray_uint_clear(&scnview->detached_shapes); 1099 1100 /* Clear the scnview data structures excepted the cache of geometries and the 1101 * scene AABB that will be used to speed up the future scnview creation */ 1102 darray_fltui_clear(&scnview->cdf); 1103 darray_nprims_cdf_clear(&scnview->nprims_cdf); 1104 scnview->mask = 0; 1105 scnview->rtc_commit = 0; 1106 1107 /* Do not physically release the memory space of the scnview. Add it to the 1108 * available scnviews pool of the scene */ 1109 list_add(&scnview->scn->scnviews, &scnview->node); 1110 S3D(scene_ref_put(scnview->scn)); 1111 } 1112 1113 /******************************************************************************* 1114 * Exported functions 1115 ******************************************************************************/ 1116 res_T 1117 s3d_scene_view_create 1118 (struct s3d_scene* scn, 1119 const int mask, 1120 struct s3d_scene_view** out_scnview) 1121 { 1122 return s3d_scene_view_create2 1123 (scn, mask, &S3D_ACCEL_STRUCT_CONF_DEFAULT, out_scnview); 1124 } 1125 1126 res_T 1127 s3d_scene_view_create2 1128 (struct s3d_scene* scn, 1129 const int mask, 1130 const struct s3d_accel_struct_conf* cfg, 1131 struct s3d_scene_view** out_scnview) 1132 { 1133 struct s3d_scene_view* scnview = NULL; 1134 const struct s3d_accel_struct_conf* accel_struct_conf = cfg; 1135 res_T res = RES_OK; 1136 1137 if(!scn || !out_scnview) { 1138 res = RES_BAD_ARG; 1139 goto error; 1140 } 1141 1142 if(!accel_struct_conf && (mask & S3D_TRACE)) { 1143 accel_struct_conf = &S3D_ACCEL_STRUCT_CONF_DEFAULT; 1144 } 1145 1146 res = scene_view_create(scn, &scnview); 1147 if(res != RES_OK) goto error; 1148 1149 res = scene_view_sync(scnview, mask, accel_struct_conf); 1150 if(res != RES_OK) goto error; 1151 1152 exit: 1153 if(out_scnview) *out_scnview = scnview; 1154 return res; 1155 error: 1156 if(scnview) { 1157 S3D(scene_view_ref_put(scnview)); 1158 scnview = NULL; 1159 } 1160 goto exit; 1161 } 1162 1163 res_T 1164 s3d_scene_view_ref_get(struct s3d_scene_view* scnview) 1165 { 1166 if(!scnview) return RES_BAD_ARG; 1167 ref_get(&scnview->ref); 1168 return RES_OK; 1169 } 1170 1171 res_T 1172 s3d_scene_view_ref_put(struct s3d_scene_view* scnview) 1173 { 1174 if(!scnview) return RES_BAD_ARG; 1175 ref_put(&scnview->ref, scene_view_release); 1176 return RES_OK; 1177 } 1178 1179 res_T 1180 s3d_scene_view_get_mask(struct s3d_scene_view* scnview, int* mask) 1181 { 1182 if(!scnview || !mask) return RES_BAD_ARG; 1183 *mask = scnview->mask; 1184 return RES_OK; 1185 } 1186 1187 res_T 1188 s3d_scene_view_sample 1189 (struct s3d_scene_view* scnview, 1190 const float u, 1191 const float v, 1192 const float w, 1193 struct s3d_primitive* primitive, /* sampled primitive */ 1194 float st[2]) 1195 { 1196 struct geometry** pgeom; 1197 struct geometry* geom; 1198 size_t sz; 1199 size_t i; 1200 const struct fltui* fltui, *fltui_found; 1201 const float* flt, *flt_found; 1202 unsigned ishape; 1203 float f; 1204 res_T res = RES_OK; 1205 1206 if(!scnview || !primitive || !st) { 1207 res = RES_BAD_ARG; 1208 goto error; 1209 } 1210 /* Expecting canonic numbers */ 1211 if(u < 0.f || u >= 1.f || v < 0.f || v >= 1.f || w < 0.f || w >= 1.f) { 1212 log_error(scnview->scn->dev, 1213 "%s: the submitted numbers are not canonical, i.e. they are not in [0, 1[.\n", 1214 FUNC_NAME); 1215 res = RES_BAD_ARG; 1216 goto error; 1217 } 1218 if((scnview->mask & S3D_SAMPLE) == 0) { 1219 log_error(scnview->scn->dev, 1220 "%s: no active S3D_SAMPLE scnview on the submitted scene.\n", 1221 FUNC_NAME); 1222 res = RES_BAD_OP; 1223 goto error; 1224 } 1225 1226 /* Find the sampled geometry */ 1227 if(darray_fltui_size_get(&scnview->cdf) == 0) { 1228 /* No geometry to sample */ 1229 *primitive = S3D_PRIMITIVE_NULL; 1230 goto exit; 1231 } else if(darray_fltui_size_get(&scnview->cdf) == 1) { 1232 ishape = darray_fltui_cdata_get(&scnview->cdf)[0].ui; 1233 /* Map u to the CDF bounds */ 1234 f = u * darray_fltui_cdata_get(&scnview->cdf)[0].flt; 1235 } else { 1236 fltui = darray_fltui_cdata_get(&scnview->cdf); 1237 sz = darray_fltui_size_get(&scnview->cdf); 1238 f = u * fltui[sz-1].flt; /* Map u to [0, SceneArea[ */ 1239 fltui_found = search_lower_bound 1240 (&f, fltui, sz, sizeof(*fltui), cmp_float_to_fltui); 1241 ASSERT(fltui_found); 1242 1243 /* search_lower_bound returns the first entry that is not less than `f'. 1244 * The following code discards entries that are also `equal' to `f' */ 1245 i = (size_t)(fltui_found - fltui); 1246 while(fltui[i].flt == f && i < sz) ++i; 1247 ASSERT(i < sz); 1248 1249 fltui_found = fltui + i; 1250 ishape = fltui_found->ui; 1251 1252 /* Map f to [0, <Shape|Instance>Area[ */ 1253 if(i) f -= fltui_found[-1].flt; 1254 } 1255 pgeom = htable_geom_find(&scnview->cached_geoms, &ishape); 1256 ASSERT(pgeom); 1257 geom = *pgeom; 1258 1259 if(geom->type == GEOM_MESH || geom->type == GEOM_SPHERE) { 1260 primitive->inst__ = NULL; 1261 primitive->inst_id = S3D_INVALID_ID; 1262 primitive->scene_prim_id = 0; 1263 } else { 1264 /* Find the sampled instantiated geometry */ 1265 ASSERT(geom->type == GEOM_INSTANCE); 1266 primitive->inst__ = geom; 1267 primitive->inst_id = geom->name; 1268 primitive->scene_prim_id = geom->scene_prim_id_offset; 1269 if(darray_fltui_size_get(&geom->data.instance->scnview->cdf) == 1) { 1270 ishape = darray_fltui_cdata_get(&geom->data.instance->scnview->cdf)[0].ui; 1271 } else { 1272 fltui = darray_fltui_cdata_get(&geom->data.instance->scnview->cdf); 1273 sz = darray_fltui_size_get(&geom->data.instance->scnview->cdf); 1274 fltui_found = search_lower_bound 1275 (&f, fltui, sz, sizeof(*fltui), cmp_float_to_fltui); 1276 ASSERT(fltui_found); 1277 1278 /* search_lower_bound returns the first entry that is not less than `f'. 1279 * The following code discards entries that are also `equal' to `f' */ 1280 i = (size_t)(fltui_found - fltui); 1281 while(fltui[i].flt == f && i < sz) ++i; 1282 ASSERT(i < sz); 1283 1284 fltui_found = fltui + i; 1285 ishape = fltui_found->ui; 1286 1287 /* Map `f' to [0, ShapeArea[ */ 1288 if(i) f -= fltui_found[-1].flt; 1289 } 1290 pgeom = htable_geom_find(&geom->data.instance->scnview->cached_geoms, &ishape); 1291 ASSERT(pgeom); 1292 geom = *pgeom; 1293 } 1294 1295 /* Find the sampled primitive */ 1296 primitive->shape__ = geom; 1297 primitive->geom_id = geom->name; 1298 primitive->scene_prim_id += geom->scene_prim_id_offset; 1299 if(geom->type == GEOM_SPHERE) { 1300 primitive->prim_id = 0; 1301 } else if(geom->type == GEOM_MESH) { 1302 flt = darray_float_cdata_get(&geom->data.mesh->cdf); 1303 sz = darray_float_size_get(&geom->data.mesh->cdf); 1304 flt_found = search_lower_bound(&f, flt, sz, sizeof(*flt), cmp_float); 1305 ASSERT(flt_found != NULL); 1306 i = (size_t)(flt_found - flt); 1307 1308 /* search_lower_bound returns the first entry that is not less than `f'. 1309 * The following code discards entries that are also `equal' to `f' */ 1310 while(flt[i] == f && i < sz) ++i; 1311 ASSERT(i < sz); 1312 1313 primitive->prim_id = (unsigned)i; 1314 1315 } else { 1316 FATAL("Unreachable code\n"); 1317 } 1318 primitive->scene_prim_id += primitive->prim_id; 1319 S3D(primitive_sample(primitive, v, w, st)); 1320 1321 exit: 1322 return res; 1323 error: 1324 goto exit; 1325 } 1326 1327 res_T 1328 s3d_scene_view_get_primitive 1329 (struct s3d_scene_view* scnview, 1330 const unsigned iprim, 1331 struct s3d_primitive* prim) 1332 { 1333 struct geometry** pgeom; 1334 struct geometry* geom; 1335 const struct nprims_cdf* begin, *found; 1336 size_t sz; 1337 size_t nprims; 1338 unsigned ishape; 1339 size_t i; 1340 res_T res = RES_OK; 1341 1342 if(!scnview || !prim) { 1343 res = RES_BAD_ARG; 1344 goto error; 1345 } 1346 if((scnview->mask & S3D_GET_PRIMITIVE) == 0) { 1347 log_error(scnview->scn->dev, 1348 "%s: no active S3D_GET_PRIMITIVE scnview on the submitted scene.\n", 1349 FUNC_NAME); 1350 res = RES_BAD_OP; 1351 goto error; 1352 } 1353 S3D(scene_view_primitives_count(scnview, &nprims)); 1354 if(iprim >= nprims) { 1355 log_error(scnview->scn->dev, 1356 "%s: the primitive index %u exceeds the number of scene primitives %u.\n", 1357 FUNC_NAME, iprim, (unsigned)nprims); 1358 res = RES_BAD_ARG; 1359 goto error; 1360 } 1361 1362 i = iprim; 1363 if(darray_nprims_cdf_size_get(&scnview->nprims_cdf) == 1) { 1364 ishape = darray_nprims_cdf_cdata_get(&scnview->nprims_cdf)[0].ishape; 1365 } else { 1366 begin = darray_nprims_cdf_cdata_get(&scnview->nprims_cdf); 1367 sz = darray_nprims_cdf_size_get(&scnview->nprims_cdf); 1368 found = search_lower_bound 1369 (&i, begin, sz, sizeof(*begin), cmp_size_t_to_nprims_cdf); 1370 ASSERT(found != NULL); 1371 ishape = found->ishape; 1372 if(found != begin) { 1373 ASSERT(i >= found[-1].nprims); 1374 i -= found[-1].nprims; 1375 } 1376 } 1377 pgeom = htable_geom_find(&scnview->cached_geoms, &ishape); 1378 ASSERT(pgeom); 1379 geom = *pgeom; 1380 1381 if(geom->type == GEOM_MESH || geom->type == GEOM_SPHERE) { 1382 prim->inst__ = NULL; 1383 prim->inst_id = S3D_INVALID_ID; 1384 prim->scene_prim_id = 0; 1385 } else { 1386 ASSERT(geom->type == GEOM_INSTANCE); 1387 prim->inst__ = geom; 1388 prim->inst_id = geom->name; 1389 prim->scene_prim_id = geom->scene_prim_id_offset; 1390 if(darray_nprims_cdf_size_get(&geom->data.instance->scnview->nprims_cdf)==1) { 1391 ishape = darray_nprims_cdf_cdata_get 1392 (&geom->data.instance->scnview->nprims_cdf)[0].ishape; 1393 } else { 1394 begin = darray_nprims_cdf_cdata_get 1395 (&geom->data.instance->scnview->nprims_cdf); 1396 sz = darray_nprims_cdf_size_get 1397 (&geom->data.instance->scnview->nprims_cdf); 1398 found = search_lower_bound 1399 (&i, begin, sz, sizeof(*begin), cmp_size_t_to_nprims_cdf); 1400 ASSERT(found != NULL); 1401 ishape = found->ishape; 1402 if(found != begin) { 1403 ASSERT(i >= found[-1].nprims); 1404 i -= found[-1].nprims; 1405 } 1406 } 1407 pgeom = htable_geom_find(&geom->data.instance->scnview->cached_geoms, &ishape); 1408 ASSERT(pgeom); 1409 geom = *pgeom; 1410 } 1411 ASSERT(geom->type == GEOM_MESH || geom->type == GEOM_SPHERE); 1412 ASSERT(geom->type != GEOM_MESH || i < mesh_get_ntris(geom->data.mesh)); 1413 prim->shape__ = geom; 1414 prim->geom_id = geom->name; 1415 prim->prim_id = (unsigned)i; 1416 prim->scene_prim_id += geom->scene_prim_id_offset; 1417 prim->scene_prim_id += prim->prim_id; 1418 1419 exit: 1420 return res; 1421 error: 1422 goto exit; 1423 } 1424 1425 res_T 1426 s3d_scene_view_primitives_count(struct s3d_scene_view* scnview, size_t* prims_count) 1427 { 1428 res_T res = RES_OK; 1429 1430 if(!scnview || !prims_count) { 1431 res = RES_BAD_ARG; 1432 goto error; 1433 } 1434 if((scnview->mask & S3D_GET_PRIMITIVE) != 0) { 1435 const size_t len = darray_nprims_cdf_size_get(&scnview->nprims_cdf); 1436 if(!len) { 1437 *prims_count = 0; 1438 } else { 1439 *prims_count = darray_nprims_cdf_cdata_get 1440 (&scnview->nprims_cdf)[len - 1].nprims; 1441 } 1442 } else { 1443 struct htable_geom_iterator it, end; 1444 size_t inst_count; 1445 1446 htable_geom_begin(&scnview->cached_geoms, &it); 1447 htable_geom_end(&scnview->cached_geoms, &end); 1448 *prims_count = 0; 1449 while(!htable_geom_iterator_eq(&it, &end)) { 1450 struct geometry** pgeom = htable_geom_iterator_data_get(&it); 1451 struct geometry* geom = *pgeom; 1452 htable_geom_iterator_next(&it); 1453 1454 if(!geom->is_enabled) continue; 1455 1456 switch(geom->type) { 1457 case GEOM_MESH: 1458 *prims_count += mesh_get_ntris(geom->data.mesh); 1459 break; 1460 case GEOM_SPHERE: 1461 *prims_count += 1; 1462 break; 1463 case GEOM_INSTANCE: 1464 S3D(scene_view_primitives_count(geom->data.instance->scnview, &inst_count)); 1465 *prims_count += inst_count; 1466 break; 1467 default: FATAL("Unreachable code\n"); break; 1468 } 1469 } 1470 } 1471 exit: 1472 return res; 1473 error: 1474 goto exit; 1475 } 1476 1477 res_T 1478 s3d_scene_view_compute_area(struct s3d_scene_view* scnview, float* out_area) 1479 { 1480 float area; 1481 res_T res = RES_OK; 1482 1483 if(!scnview || !out_area) { 1484 res = RES_BAD_ARG; 1485 goto error; 1486 } 1487 if((scnview->mask & S3D_SAMPLE) != 0) { 1488 /* Retrieve the overall scene area from the scene cumulative distribution 1489 * function */ 1490 size_t len = darray_fltui_size_get(&scnview->cdf); 1491 if(!len) { 1492 area = 0.f; 1493 } else { 1494 area = darray_fltui_cdata_get(&scnview->cdf)[len - 1].flt; 1495 } 1496 } else { 1497 struct htable_geom_iterator it, end; 1498 float inst_area; 1499 1500 htable_geom_begin(&scnview->cached_geoms, &it); 1501 htable_geom_end(&scnview->cached_geoms, &end); 1502 1503 area = 0.f; 1504 while(!htable_geom_iterator_eq(&it, &end)) { 1505 struct geometry** pgeom = htable_geom_iterator_data_get(&it); 1506 struct geometry* geom = *pgeom; 1507 1508 htable_geom_iterator_next(&it); 1509 1510 if(!geom->is_enabled) continue; 1511 1512 switch(geom->type) { 1513 case GEOM_MESH: 1514 area += mesh_compute_area(geom->data.mesh); 1515 break; 1516 case GEOM_SPHERE: 1517 area += sphere_compute_area(geom->data.sphere); 1518 break; 1519 case GEOM_INSTANCE: 1520 /* TODO take into account the instance scale factor */ 1521 S3D(scene_view_compute_area(geom->data.instance->scnview, &inst_area)); 1522 area += inst_area; 1523 break; 1524 default: FATAL("Unreachable code\n"); break; 1525 } 1526 } 1527 } 1528 1529 exit: 1530 if(out_area) *out_area = area; 1531 return res; 1532 error: 1533 area = -1.f; 1534 goto exit; 1535 } 1536 1537 res_T 1538 s3d_scene_view_compute_volume(struct s3d_scene_view* scnview, float* out_volume) 1539 { 1540 if(!scnview || !out_volume) return RES_BAD_ARG; 1541 *out_volume = scene_view_compute_volume(scnview, 0/*No initial flip_surface*/); 1542 return RES_OK; 1543 } 1544 1545 res_T 1546 s3d_scene_view_get_aabb(struct s3d_scene_view* scnview, float lower[3], float upper[3]) 1547 { 1548 if(!scnview || !lower || !upper) return RES_BAD_ARG; 1549 f3_set(lower, scnview->lower); 1550 f3_set(upper, scnview->upper); 1551 return RES_OK; 1552 } 1553 1554 /******************************************************************************* 1555 * Local functions 1556 ******************************************************************************/ 1557 void 1558 scene_view_destroy(struct s3d_scene_view* scnview) 1559 { 1560 struct htable_geom_iterator it, end; 1561 ASSERT(scnview && !is_list_empty(&scnview->node)/*Not in use*/); 1562 ASSERT(scnview->mask == 0); 1563 1564 /* Delete the cached geometries */ 1565 htable_geom_begin(&scnview->cached_geoms, &it); 1566 htable_geom_end(&scnview->cached_geoms, &end); 1567 while(!htable_geom_iterator_eq(&it, &end)) { 1568 struct geometry** pgeom = htable_geom_iterator_data_get(&it); 1569 struct geometry* geom = *pgeom; 1570 scene_view_destroy_geometry(scnview, geom); 1571 htable_geom_iterator_next(&it); 1572 } 1573 1574 /* Delete the back-end scene */ 1575 if(scnview->rtc_scn) rtcReleaseScene(scnview->rtc_scn); 1576 1577 /* Release internal data structure */ 1578 htable_geom_release(&scnview->cached_geoms); 1579 darray_fltui_release(&scnview->cdf); 1580 darray_nprims_cdf_release(&scnview->nprims_cdf); 1581 htable_instview_release(&scnview->instviews); 1582 darray_uint_release(&scnview->detached_shapes); 1583 1584 /* Remove the scnview from its pool */ 1585 list_del(&scnview->node); 1586 CLBK_DISCONNECT(&scnview->on_shape_detach_cb); 1587 1588 /* Free the scnview memory space */ 1589 MEM_RM(scnview->scn->dev->allocator, scnview); 1590 } 1591