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