senc3d_scene_analyze.c (90701B)
1 /* Copyright (C) 2018-2020, 2023, 2024 |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 #define _POSIX_C_SOURCE 200112L 17 18 #include "senc3d.h" 19 #include "senc3d_device_c.h" 20 #include "senc3d_scene_c.h" 21 #include "senc3d_scene_analyze_c.h" 22 #include "senc3d_internal_types.h" 23 24 #include <rsys/rsys.h> 25 #include <rsys/float3.h> 26 #include <rsys/double33.h> 27 #include <rsys/mem_allocator.h> 28 #include <rsys/hash_table.h> 29 #include <rsys/dynamic_array.h> 30 #include <rsys/dynamic_array_uint.h> 31 #include <rsys/dynamic_array_uchar.h> 32 #include <rsys/clock_time.h> 33 #include <rsys/str.h> 34 35 #include <star/s3d.h> 36 37 #include <omp.h> 38 #include <math.h> 39 #include <limits.h> 40 #include <stdlib.h> 41 42 #define CC_DESCRIPTOR_NULL__ {\ 43 0, 0, {{DBL_MAX,-DBL_MAX}, {DBL_MAX,-DBL_MAX}, {DBL_MAX,-DBL_MAX}}, NULL,\ 44 0, INT_MAX, VRTX_NULL__, 0,\ 45 CC_ID_NONE, CC_GROUP_ROOT_NONE, ENCLOSURE_NULL__,\ 46 { TRG_NULL__, 0}\ 47 } 48 #ifdef COMPILER_GCC 49 #pragma GCC diagnostic push 50 #pragma GCC diagnostic ignored "-Wmissing-field-initializers" 51 #endif 52 const struct cc_descriptor CC_DESCRIPTOR_NULL = CC_DESCRIPTOR_NULL__; 53 #ifdef COMPILER_GCC 54 #pragma GCC diagnostic pop 55 #endif 56 57 #define DARRAY_NAME component_id 58 #define DARRAY_DATA component_id_t 59 #include <rsys/dynamic_array.h> 60 61 #define HTABLE_NAME component_id 62 #define HTABLE_KEY component_id_t 63 #define HTABLE_DATA char 64 #include <rsys/hash_table.h> 65 66 /* A threshold for dot products: 67 * If ray.normal < threshold we suspect accuracy could be a problem */ 68 #define DOT_THRESHOLD 0.0001f 69 70 enum ctx_type { 71 CTX0, 72 CTX1, 73 CTX2 74 }; 75 76 struct filter_ctx { 77 enum ctx_type type; 78 struct senc3d_scene* scn; 79 struct s3d_scene_view* view; 80 component_id_t origin_component; 81 struct darray_triangle_comp* triangles_comp; 82 struct darray_ptr_component_descriptor* components; 83 /* Tmp data used across filter calls */ 84 double current_6volume; 85 int cpt; 86 float s; 87 /* Result of CTX1 hit */ 88 component_id_t hit_component; 89 float hit_dir[3], hit_dist; 90 struct s3d_primitive hit_prim; 91 }; 92 93 #define HTABLE_NAME overlap 94 #define HTABLE_KEY trg_id_t 95 #define HTABLE_DATA char 96 #include <rsys/hash_table.h> 97 98 /****************************************************************************** 99 * Helper function 100 *****************************************************************************/ 101 static INLINE int 102 neighbour_cmp(const void* w1, const void* w2) 103 { 104 const double a1 = ((struct neighbour_info*)w1)->angle; 105 const double a2 = ((struct neighbour_info*)w2)->angle; 106 return (a1 > a2) - (a1 < a2); 107 } 108 109 /* Returns 1 if cc2 is inside cc1, 0 otherwise */ 110 static FINLINE int 111 is_component_inside 112 (struct cc_descriptor* cc1, 113 struct cc_descriptor* cc2, 114 struct filter_ctx* ctx) 115 { 116 int i; 117 side_id_t side; 118 const struct triangle_in* trg = NULL; 119 double pt[3] = { 0, 0, 0 }; 120 float org[3], dir[3] = { 0, 0, 1 }, rg[2] = { 0, FLT_MAX }; 121 struct filter_ctx ctx2; 122 struct s3d_hit hit = S3D_HIT_NULL; 123 const struct triangle_comp* 124 trg_comp = darray_triangle_comp_cdata_get(ctx->triangles_comp); 125 const struct triangle_in* 126 triangles = darray_triangle_in_cdata_get(&ctx->scn->triangles_in); 127 const union double3* vertices = darray_position_cdata_get(&ctx->scn->vertices); 128 ASSERT(cc1 && cc2 && ctx); 129 /* Volume must be compatible */ 130 if(fabs(cc2->_6volume) >= fabs(cc1->_6volume)) 131 return 0; 132 /* Bbox must be compatible */ 133 for(i = 0; i < 3; i++) { 134 if(cc2->bbox[i][0] < cc1->bbox[i][0] || cc2->bbox[i][1] > cc1->bbox[i][1]) 135 return 0; 136 } 137 /* Check if component cc2 is inside component cc1. 138 * We already know that bbox and volume allow cc2 to fit inside component 139 * ccc1, but it is not enough. 140 * The method is to cast a ray from cc2 and count the number of times it 141 * crosses component cc1; as components must not overlap, testing from a 142 * single point is OK, as long as the point is not on cc1 boundary (it is on 143 * cc2 boundary, though). */ 144 for(side = cc2->side_range.first; side <= cc2->side_range.last; side++) { 145 /* Find a triangle on cc2 boundary that is not on cc1 boundary (it exists, 146 * as the 2 components cannot share all their triangles) */ 147 trg_id_t t = TRGSIDE_2_TRG(side); 148 const component_id_t* candidate_comp = trg_comp[t].component; 149 if(candidate_comp[SENC3D_FRONT] != cc2->cc_id 150 && candidate_comp[SENC3D_BACK] != cc2->cc_id) 151 continue; 152 if(candidate_comp[SENC3D_FRONT] == cc1->cc_id 153 || candidate_comp[SENC3D_BACK] == cc1->cc_id) 154 continue; 155 /* This triangle is OK */ 156 trg = triangles + t; 157 break; 158 } 159 ASSERT(trg != NULL); 160 /* Any point on trg can do the trick: use the barycenter */ 161 FOR_EACH(i, 0, 3) { 162 vrtx_id_t v = trg->vertice_id[i]; 163 ASSERT(v < darray_position_size_get(&ctx->scn->vertices)); 164 d3_add(pt, pt, vertices[v].vec); 165 } 166 d3_divd(pt, pt, 3); 167 f3_set_d3(org, pt); 168 /* Trace a ray and count intersections with component c */ 169 ctx2.type = CTX2; 170 ctx2.triangles_comp = ctx->triangles_comp; 171 ctx2.cpt = 0; 172 ctx2.origin_component = cc1->cc_id; 173 S3D(scene_view_trace_ray(ctx->view, org, dir, rg, &ctx2, &hit)); 174 /* cc2 is not inside cc1 if cpt is even */ 175 if(ctx2.cpt % 2 == 0) return 0; 176 return 1; 177 } 178 179 static side_id_t 180 get_side_not_in_connex_component 181 (const side_id_t last_side, 182 const struct trgside* trgsides, 183 const uchar* processed, 184 side_id_t* first_side_not_in_component, 185 const medium_id_t medium) 186 { 187 ASSERT(trgsides && processed && first_side_not_in_component); 188 { 189 side_id_t i = *first_side_not_in_component; 190 while(i <= last_side 191 && (trgsides[i].medium != medium 192 || (processed[TRGSIDE_2_TRG(i)] & TRGSIDE_2_SIDEFLAG(i)))) 193 ++i; 194 195 *first_side_not_in_component = i + 1; 196 if(i > last_side) return SIDE_NULL__; 197 return i; 198 } 199 } 200 201 /* Here unsigned are required by s3d API */ 202 static void 203 get_scn_indices 204 (const unsigned itri, 205 unsigned ids[3], 206 void* ctx) 207 { 208 int i; 209 const struct senc3d_scene* scene = ctx; 210 const struct triangle_in* trg = 211 darray_triangle_in_cdata_get(&scene->triangles_in) + itri; 212 FOR_EACH(i, 0, 3) { 213 ASSERT(trg->vertice_id[i] < scene->nverts); 214 ASSERT(trg->vertice_id[i] <= UINT_MAX); 215 ids[i] = (unsigned)trg->vertice_id[i]; /* Back to s3d API type */ 216 } 217 } 218 219 /* Here unsigned are required by s3d API */ 220 static void 221 get_scn_position 222 (const unsigned ivert, 223 float pos[3], 224 void* ctx) 225 { 226 const struct senc3d_scene* scene = ctx; 227 const union double3* pt = 228 darray_position_cdata_get(&scene->vertices) + ivert; 229 f3_set_d3(pos, pt->vec); 230 } 231 232 static int 233 self_hit_filter 234 (const struct s3d_hit* hit, 235 const float ray_org[3], 236 const float ray_dir[3], 237 const float ray_range[2], 238 void* ray_data, 239 void* filter_data) 240 { 241 struct filter_ctx* ctx = ray_data; 242 243 (void)ray_org; (void)ray_range; (void)filter_data; 244 ASSERT(ctx); 245 ASSERT(hit->uv[0] == CLAMP(hit->uv[0], 0, 1)); 246 ASSERT(hit->uv[1] == CLAMP(hit->uv[1], 0, 1)); 247 248 switch (ctx->type) { 249 default: FATAL("Invalid"); 250 251 case CTX2: { 252 /* The filter is used to count the hits on some component along an 253 * infinite ray */ 254 const struct triangle_comp* trg_comp; 255 const component_id_t* hit_comp; 256 ASSERT(hit->prim.prim_id 257 < darray_triangle_comp_size_get(ctx->triangles_comp)); 258 trg_comp = darray_triangle_comp_cdata_get(ctx->triangles_comp); 259 hit_comp = trg_comp[hit->prim.prim_id].component; 260 if(hit_comp[SENC3D_FRONT] == ctx->origin_component 261 || hit_comp[SENC3D_BACK] == ctx->origin_component) 262 { 263 ctx->cpt++; 264 } 265 return 1; /* Reject to continue counting */ 266 } 267 268 case CTX0: { 269 /* This filter is called from a closest point query from a point belonging 270 * to origin_component. The returned hit is used to determine the search 271 * radius for CTX1 main computation. */ 272 const struct triangle_comp* 273 trg_comp = darray_triangle_comp_cdata_get(ctx->triangles_comp); 274 const component_id_t* 275 hit_comp = trg_comp[hit->prim.prim_id].component; 276 const component_id_t oc = ctx->origin_component; 277 vrtx_id_t other_id; 278 struct cc_descriptor* const* comp_descriptors 279 = darray_ptr_component_descriptor_cdata_get(ctx->components); 280 size_t compsz = darray_ptr_component_descriptor_size_get(ctx->components); 281 const union double3* 282 vertices = darray_position_cdata_get(&ctx->scn->vertices); 283 const double org_z = vertices[comp_descriptors[oc]->max_z_vrtx_id].pos.z; 284 float s = 0, hit_normal[3], rdir[3]; 285 enum senc3d_side hit_side; 286 const int log_components = 287 ctx->scn->convention & SENC3D_LOG_COMPONENTS_INFORMATION; 288 289 ASSERT(hit->prim.prim_id 290 < darray_triangle_comp_size_get(ctx->triangles_comp)); 291 ASSERT(hit_comp[SENC3D_FRONT] < compsz); 292 ASSERT(hit_comp[SENC3D_BACK] < compsz); 293 (void)compsz; /* Avoid "unused variable" warning */ 294 295 /* Hit acceptance must be coherent at CTX0 and FTCX1 stages: 296 * the search radius as found at stage CTX0 must include the hit that 297 * stage CTX1 will select using an infinite radius */ 298 if(hit_comp[SENC3D_FRONT] == oc || hit_comp[SENC3D_BACK] == oc) { 299 return 1; /* Self hit, reject */ 300 } 301 if(hit->distance == 0) { 302 /* origin component is in contact with some other components 303 * We will need further exploration to know if they should be considered 304 * Accepting hit at distance 0 => radius is definitively 0 */ 305 int n; 306 307 /* If same component, process only once */ 308 FOR_EACH(n, 0, (hit_comp[SENC3D_FRONT] == hit_comp[SENC3D_BACK] ? 1 : 2)) { 309 const enum senc3d_side sides[2] = { SENC3D_FRONT, SENC3D_BACK }; 310 component_id_t c = hit_comp[sides[n]]; 311 ASSERT(c < darray_ptr_component_descriptor_size_get(ctx->components)); 312 if(comp_descriptors[c]->is_outer_border) { 313 /* The inner component we are trying to link can only be linked to 314 * an outer component if it is inside */ 315 if(!is_component_inside(comp_descriptors[c], 316 comp_descriptors[oc], ctx)) 317 { 318 continue; 319 } 320 321 if(log_components) { 322 #pragma omp critical 323 printf("Component #%u: decreasing search radius " 324 "(R=%g, n=%g,%g,%g, components: %u, %u)\n", 325 oc, hit->distance, SPLIT3(hit->normal), 326 hit_comp[SENC3D_FRONT], hit_comp[SENC3D_BACK]); 327 } 328 return 0; 329 } else { 330 /* c is an inner component */ 331 vrtx_id_t c_z_id; 332 /* The inner component we are trying to link can only be linked to 333 * another inner component if (at least partly) above and not inside */ 334 c_z_id = comp_descriptors[c]->max_z_vrtx_id; 335 ASSERT(c_z_id < darray_position_size_get(&ctx->scn->vertices)); 336 ASSERT(vertices[c_z_id].pos.z >= org_z); 337 if(vertices[c_z_id].pos.z == org_z) { 338 continue; /* Not above */ 339 } 340 if(is_component_inside(comp_descriptors[c], 341 comp_descriptors[oc], ctx)) 342 { 343 continue; /* Inside */ 344 } 345 if(log_components) { 346 #pragma omp critical 347 printf("Component #%u: decreasing search radius " 348 "(R=%g, n=%g,%g,%g, components: %u, %u)\n", 349 oc, hit->distance, SPLIT3(hit->normal), 350 hit_comp[SENC3D_FRONT], hit_comp[SENC3D_BACK]); 351 } 352 return 0; 353 } 354 } 355 return 1; 356 } 357 358 ASSERT(hit->distance > 0); 359 if(hit_comp[SENC3D_FRONT] == hit_comp[SENC3D_BACK]) { 360 /* Hit component is known, check if above */ 361 other_id = comp_descriptors[hit_comp[SENC3D_FRONT]]->max_z_vrtx_id; 362 ASSERT(other_id < darray_position_size_get(&ctx->scn->vertices)); 363 if(vertices[other_id].pos.z <= org_z) { 364 return 1; 365 } 366 if(log_components) { 367 #pragma omp critical 368 printf("Component #%u: decreasing search radius " 369 "(R=%g, n=%g,%g,%g, components: %u, %u)\n", 370 oc, hit->distance, SPLIT3(hit->normal), 371 hit_comp[SENC3D_FRONT], hit_comp[SENC3D_BACK]); 372 } 373 return 0; 374 } 375 376 /* Compute hit side */ 377 /* For s to be comparable, vectors must be normalized */ 378 f3_normalize(hit_normal, hit->normal); 379 f3_normalize(rdir, ray_dir); 380 s = f3_dot(rdir, hit_normal); /* Can be NaN for tiny distances */ 381 if(isnan(s)) { 382 /* Try to fix it */ 383 f3_divf(rdir, ray_dir, hit->distance); 384 f3_normalize(rdir, rdir); 385 s = f3_dot(rdir, hit_normal); 386 ASSERT(!isnan(s)); 387 } 388 389 if(fabsf(s) < DOT_THRESHOLD) { 390 /* We cannot know for sure which side to consider */ 391 vrtx_id_t i1 = comp_descriptors[hit_comp[SENC3D_FRONT]]->max_z_vrtx_id; 392 vrtx_id_t i2 = comp_descriptors[hit_comp[SENC3D_BACK]]->max_z_vrtx_id; 393 double possible_z = MMIN(vertices[i1].pos.z, vertices[i2].pos.z); 394 if(possible_z > org_z) { 395 /* Both components are above origin component => keep */ 396 if(log_components) { 397 #pragma omp critical 398 printf("Component #%u: decreasing search radius " 399 "(R=%g, n=%g,%g,%g, components: %u, %u)\n", 400 oc, hit->distance, SPLIT3(hit->normal), 401 hit_comp[SENC3D_FRONT], hit_comp[SENC3D_BACK]); 402 } 403 return 0; 404 } 405 /* Cannot be sure => the safest choice is to reject */ 406 return 1; 407 } 408 /* Determine which side was hit */ 409 hit_side = 410 ((s < 0) /* Facing geometrical normal of hit */ 411 == ((ctx->scn->convention & SENC3D_CONVENTION_NORMAL_FRONT) != 0)) 412 /* Warning: following Embree 2 convention for geometrical normals, 413 * the Star3D hit normal is left-handed while star-enclosures-3d uses 414 * right-handed convention */ 415 ? SENC3D_BACK : SENC3D_FRONT; 416 other_id = comp_descriptors[hit_comp[hit_side]]->max_z_vrtx_id; 417 ASSERT(other_id < darray_position_size_get(&ctx->scn->vertices)); 418 if(vertices[other_id].pos.z <= org_z) { 419 return 1; 420 } 421 if(log_components) { 422 #pragma omp critical 423 printf("Component #%u: decreasing search radius " 424 "(R=%g, n=%g,%g,%g, components: %u, %u)\n", 425 oc, hit->distance, SPLIT3(hit->normal), 426 hit_comp[SENC3D_FRONT], hit_comp[SENC3D_BACK]); 427 } 428 return 0; 429 } 430 431 case CTX1: { 432 /* This filter is called from a closest point query from a point belonging 433 * to origin_component. The returned hit is used to determine a component 434 * to which origin_component is linked. At a later stage the algorithm 435 * process linked components to determine their relative inclusions. 436 * 437 * This filter is called with a search distance that has been ajusted in 438 * CTX0 filter. This distance must be left unchanged to ensure visiting 439 * all the surfaces at the determined distance: allways reject hits to 440 * avoid decreasing search distance. 441 * 442 * For each hit, the filter computes if the hit is on a component above 443 * origin_component (that is with >= Z). 444 * If the hit is distant (dist>0), we just keep the hit as a valid candidate, 445 * but things get more tricky when dist==0 (ray_org is a vertex where some 446 * other components can be in contact with origin_component). 447 * In this case, one of the other components can include the origin_component 448 * (greater volume needed), or they can be disjoint, with (at least) ray_org 449 * as a common vertex (they can also partially intersect, but this is invalid 450 * and remains undetected by star enclosures). */ 451 struct cc_descriptor* const* comp_descriptors 452 = darray_ptr_component_descriptor_cdata_get(ctx->components); 453 const struct triangle_comp* 454 trg_comp = darray_triangle_comp_cdata_get(ctx->triangles_comp); 455 const component_id_t* 456 hit_comp = trg_comp[hit->prim.prim_id].component; 457 const union double3* 458 vertices = darray_position_cdata_get(&ctx->scn->vertices); 459 enum senc3d_side hit_side; 460 float s = 0, hit_normal[3], rdir[3]; 461 const int log_components = 462 ctx->scn->convention & SENC3D_LOG_COMPONENTS_INFORMATION; 463 const component_id_t oc = ctx->origin_component; 464 /* Links must be upward to avoid creating loops 465 * Instead of comparing hit.z VS origin.z, compare max_Z of components 466 * Here we keep max_z of the origin component that will be used for these 467 * comparisons */ 468 const double org_z = vertices[comp_descriptors[oc]->max_z_vrtx_id].pos.z; 469 vrtx_id_t other_id; 470 471 ASSERT(hit->prim.prim_id 472 < darray_triangle_comp_size_get(ctx->triangles_comp)); 473 474 if(log_components) { 475 #pragma omp critical 476 printf("Component #%u: investigating hit " 477 "(d=%g, n=%g,%g,%g, components: %u, %u)\n", 478 oc, hit->distance, SPLIT3(hit->normal), 479 hit_comp[SENC3D_FRONT], hit_comp[SENC3D_BACK]); 480 } 481 482 /* Has CTX1 filter do not reduce search radius, hits can be processed 483 * that are at farther distance than the current best candidate: we need 484 * to reject them here */ 485 if(hit->distance > ctx->hit_dist) { 486 /* No improvement */ 487 if(log_components) { 488 #pragma omp critical 489 printf("Component #%u: further away (%g): reject\n", 490 oc, ctx->hit_dist); 491 } 492 return 1; 493 } 494 495 /* Hit acceptance must be coherent at CTX0 and FTCX1 stages: 496 * the search radius as found at stage CTX0 must include the hit that 497 * stage CTX1 will select using an infinite radius */ 498 if(hit_comp[SENC3D_FRONT] == oc || hit_comp[SENC3D_BACK] == oc) { 499 /* Self hit */ 500 if(log_components) { 501 #pragma omp critical 502 printf("Component #%u: self hit: reject\n", oc); 503 } 504 return 1; 505 } 506 507 if(hit->distance == 0) { 508 /* origin_component is in contact with some other components 509 * We will need further exploration to know if they should be considered */ 510 int n; 511 512 /* If same component, process only once */ 513 FOR_EACH(n, 0, (hit_comp[SENC3D_FRONT] == hit_comp[SENC3D_BACK] ? 1 : 2)) { 514 const enum senc3d_side sides[2] = { SENC3D_FRONT, SENC3D_BACK }; 515 component_id_t c = hit_comp[sides[n]]; 516 ASSERT(c < darray_ptr_component_descriptor_size_get(ctx->components)); 517 if(c == ctx->hit_component) { 518 /* Cannot change ctx->hit_component */ 519 if(log_components) { 520 #pragma omp critical 521 printf("Component #%u: hit component #%u and already linked to it:" 522 " reject\n", 523 oc, c); 524 } 525 continue; 526 } 527 if(comp_descriptors[c]->is_outer_border) { 528 double v; 529 /* The inner component we are trying to link can only be linked to 530 * an outer component if it is inside */ 531 if(log_components) { 532 #pragma omp critical 533 printf("Component #%u: hit outer component #%u\n", oc, c); 534 } 535 if(!is_component_inside(comp_descriptors[c], 536 comp_descriptors[oc], ctx)) 537 { 538 if(log_components) { 539 #pragma omp critical 540 printf("Component #%u: not inside: reject\n", oc); 541 } 542 continue; 543 } 544 v = fabs(comp_descriptors[c]->_6volume); 545 /* If already linked to an inner component, prefer an outer one 546 * regardless of their respective volumes. 547 * If origin_component is inside several outer components, the one 548 * we are looking for is the smallest one (to manage outer component 549 * inside another outer component). */ 550 if((ctx->hit_component != COMPONENT_NULL__ 551 && !comp_descriptors[ctx->hit_component]->is_outer_border) 552 || v < ctx->current_6volume) { 553 ctx->hit_component = c; 554 ctx->current_6volume = v; 555 ctx->hit_dist = 0; 556 ctx->hit_prim = hit->prim; 557 if(log_components) { 558 if(v < ctx->current_6volume) { 559 #pragma omp critical 560 printf("Component #%u: currently the smaller one: " 561 "keep component #%u\n", 562 oc, ctx->hit_component); 563 } else { 564 #pragma omp critical 565 printf("Component #%u: change from inner to outer: " 566 "keep component #%u\n", 567 oc, ctx->hit_component); 568 } 569 } 570 } else { 571 if(log_components) { 572 #pragma omp critical 573 printf("Component #%u: not the smaller one: reject\n", oc); 574 } 575 continue; 576 } 577 } else { 578 /* c is an inner component */ 579 vrtx_id_t c_z_id; 580 double v; 581 /* If we've already found a valid outer component, inner components 582 * should not be considered anymore */ 583 if(log_components) { 584 #pragma omp critical 585 printf("Component #%u: hit inner component #%u\n", oc, c); 586 } 587 if(ctx->hit_component != COMPONENT_NULL__ 588 && comp_descriptors[ctx->hit_component]->is_outer_border) 589 { 590 if(log_components) { 591 #pragma omp critical 592 printf("Component #%u: already in an outer component: reject\n", 593 oc); 594 } 595 continue; 596 } 597 /* The inner component we are trying to link can only be linked to 598 * another inner component if (at least partly) above it and not 599 * inside */ 600 c_z_id = comp_descriptors[c]->max_z_vrtx_id; 601 ASSERT(c_z_id < darray_position_size_get(&ctx->scn->vertices)); 602 ASSERT(vertices[c_z_id].pos.z >= org_z); 603 if(vertices[c_z_id].pos.z == org_z) { 604 if(log_components) { 605 #pragma omp critical 606 printf("Component #%u: not (even in part) above: reject\n", oc); 607 } 608 continue; /* Not above */ 609 } 610 if(is_component_inside(comp_descriptors[c], 611 comp_descriptors[oc], ctx)) 612 { 613 if(log_components) { 614 #pragma omp critical 615 printf("Component #%u: not outside: reject\n", oc); 616 } 617 continue; /* Inside */ 618 } 619 v = fabs(comp_descriptors[c]->_6volume); 620 /* If origin_component is facing several inner components, the one 621 * we are looking for is the largest one (to manage inner component 622 * inside another inner component) */ 623 if(ctx->current_6volume == DBL_MAX || v > ctx->current_6volume) { 624 ctx->hit_component = c; 625 ctx->current_6volume = v; 626 ctx->hit_dist = 0; 627 ctx->hit_prim = hit->prim; 628 if(log_components) { 629 #pragma omp critical 630 printf("Component #%u: currently the bigger one: " 631 "keep component #%u\n", 632 oc, ctx->hit_component); 633 } 634 } else { 635 if(log_components) { 636 #pragma omp critical 637 printf("Component #%u: not the bigger one: reject\n", oc); 638 } 639 continue; 640 } 641 } 642 } 643 return 1; 644 } 645 646 ASSERT(hit->distance > 0); 647 if(hit_comp[SENC3D_FRONT] == hit_comp[SENC3D_BACK]) { 648 /* Easy case and hit component is known */ 649 other_id = comp_descriptors[hit_comp[SENC3D_FRONT]]->max_z_vrtx_id; 650 ASSERT(other_id < darray_position_size_get(&ctx->scn->vertices)); 651 if(vertices[other_id].pos.z <= org_z) { 652 if(log_components) { 653 #pragma omp critical 654 printf("Component #%u: 2 sides, not above: reject\n", oc); 655 } 656 return 1; 657 } 658 ctx->hit_component = hit_comp[SENC3D_FRONT]; 659 ctx->s = 1; 660 f3_set(ctx->hit_dir, ray_dir); 661 ctx->hit_dist = hit->distance; 662 ctx->hit_prim = hit->prim; 663 if(log_components) { 664 #pragma omp critical 665 printf("Component #%u: 2 sides with same component: " 666 "keep component #%u\n", 667 oc, ctx->hit_component); 668 } 669 return 1; 670 } 671 672 /* Compute hit side */ 673 /* For s to be comparable, vectors must be normalized */ 674 f3_normalize(hit_normal, hit->normal); 675 f3_normalize(rdir, ray_dir); 676 s = f3_dot(rdir, hit_normal); /* Can be NaN for tiny distances */ 677 if(isnan(s)) { 678 /* Try to fix it */ 679 f3_divf(rdir, ray_dir, hit->distance); 680 f3_normalize(rdir, rdir); 681 s = f3_dot(rdir, hit_normal); 682 ASSERT(!isnan(s)); 683 if(log_components) { 684 #pragma omp critical 685 printf("Component #%u: had to fix s (was NaN)\n", oc); 686 } 687 } 688 689 if(ctx->hit_dist == hit->distance && fabsf(ctx->s) >= fabsf(s)) { 690 /* Same distance with no s improvement: keep the previous hit */ 691 if(log_components) { 692 #pragma omp critical 693 printf("Component #%u: not improving s (%g VS %g): reject\n", 694 oc, s, ctx->s); 695 } 696 return 1; 697 } 698 699 if(fabsf(s) < DOT_THRESHOLD) { 700 /* We cannot know for sure which side to consider */ 701 vrtx_id_t i1 = comp_descriptors[hit_comp[SENC3D_FRONT]]->max_z_vrtx_id; 702 vrtx_id_t i2 = comp_descriptors[hit_comp[SENC3D_BACK]]->max_z_vrtx_id; 703 double possible_z = MMAX(vertices[i1].pos.z, vertices[i2].pos.z); 704 if(possible_z <= org_z) { 705 /* None of the components are above origin component => reject */ 706 if(log_components) { 707 #pragma omp critical 708 printf("Component #%u: none of the components above: reject\n", 709 oc); 710 } 711 return 1; 712 } 713 ctx->hit_component = COMPONENT_NULL__; 714 ctx->s = s; 715 f3_set(ctx->hit_dir, ray_dir); 716 ctx->hit_dist = hit->distance; 717 ctx->hit_prim = hit->prim; 718 if(log_components) { 719 #pragma omp critical 720 printf("Component #%u: tiny s (%g): " 721 "keep but don't know the component\n", 722 oc, s); 723 } 724 return 1; 725 } 726 /* Determine which side was hit */ 727 hit_side = 728 ((s < 0) /* Facing geometrical normal of hit */ 729 == ((ctx->scn->convention & SENC3D_CONVENTION_NORMAL_FRONT) != 0)) 730 /* Warning: following Embree 2 convention for geometrical normals, 731 * the Star3D hit normal is left-handed while star-enclosures-3d uses 732 * right-handed convention */ 733 ? SENC3D_BACK : SENC3D_FRONT; 734 other_id = comp_descriptors[hit_comp[hit_side]]->max_z_vrtx_id; 735 ASSERT(other_id < darray_position_size_get(&ctx->scn->vertices)); 736 if(vertices[other_id].pos.z <= org_z) { 737 if(log_components) { 738 #pragma omp critical 739 printf("Component #%u: standard s, not (even in part) above: reject\n", 740 oc); 741 } 742 return 1; 743 } 744 ctx->hit_component = hit_comp[hit_side]; 745 ctx->s = s; 746 f3_set(ctx->hit_dir, ray_dir); 747 ctx->hit_dist = hit->distance; 748 ctx->hit_prim = hit->prim; 749 if(log_components) { 750 #pragma omp critical 751 printf("Component #%u: standard s, (%g): keep component #%u\n", 752 oc, s, ctx->hit_component); 753 } 754 return 1; 755 } 756 } 757 } 758 759 static void 760 extract_connex_components 761 (struct senc3d_scene* scn, 762 struct trgside* trgsides, 763 struct darray_ptr_component_descriptor* connex_components, 764 const struct darray_triangle_tmp* triangles_tmp_array, 765 struct darray_triangle_comp* triangles_comp_array, 766 struct s3d_scene_view** s3d_view, 767 ATOMIC* component_count, 768 /* Shared error status. 769 * We accept to overwrite an error with a different error */ 770 res_T* p_res) 771 { 772 /* This function is called from an omp parallel block and executed 773 * concurrently. */ 774 struct mem_allocator* alloc; 775 int64_t m_idx; /* OpenMP requires a signed type for the for loop variable */ 776 struct darray_side_id stack; 777 const union double3* positions; 778 const struct triangle_tmp* triangles_tmp; 779 struct triangle_comp* triangles_comp; 780 /* An array to flag sides when processed */ 781 uchar* processed; 782 /* An array to store the component being processed */ 783 struct darray_side_id current_component; 784 /* A bool array to store media of the component being processed */ 785 uchar* current_media = NULL; 786 size_t sz, ii; 787 788 ASSERT(scn && trgsides && connex_components && triangles_tmp_array 789 && triangles_comp_array && s3d_view && component_count && p_res); 790 alloc = scn->dev->allocator; 791 positions = darray_position_cdata_get(&scn->vertices); 792 triangles_tmp = darray_triangle_tmp_cdata_get(triangles_tmp_array); 793 triangles_comp = darray_triangle_comp_data_get(triangles_comp_array); 794 darray_side_id_init(alloc, &stack); 795 darray_side_id_init(alloc, ¤t_component); 796 processed = MEM_CALLOC(alloc, scn->ntris, sizeof(uchar)); 797 if(!processed) { 798 *p_res = RES_MEM_ERR; 799 return; 800 } 801 802 #ifndef NDEBUG 803 #pragma omp single 804 { 805 trg_id_t t_; 806 int s; 807 ASSERT(darray_ptr_component_descriptor_size_get(connex_components) == 0); 808 FOR_EACH(t_, 0, scn->ntris) { 809 const struct triangle_in* trg_in = 810 darray_triangle_in_cdata_get(&scn->triangles_in) + t_; 811 const struct side_range* media_use 812 = darray_side_range_cdata_get(&scn->media_use); 813 FOR_EACH(s, 0, 2) { 814 const side_id_t side = TRGIDxSIDE_2_TRGSIDE(t_, (enum senc3d_side)s); 815 medium_id_t medium = trg_in->medium[s]; 816 m_idx = medium_id_2_medium_idx(medium); 817 ASSERT(media_use[m_idx].first <= side && side 818 <= media_use[m_idx].last); 819 } 820 } 821 } /* Implicit barrier here */ 822 #endif 823 824 /* We loop on sides to build connex components. */ 825 ASSERT(darray_side_range_size_get(&scn->media_use) <= INT64_MAX); 826 #pragma omp for schedule(dynamic) nowait 827 /* Process all media, including unspecified */ 828 for(m_idx = 0; m_idx < (int64_t)darray_side_range_size_get(&scn->media_use); 829 m_idx++) 830 { 831 const medium_id_t medium = medium_idx_2_medium_id(m_idx); 832 /* media_use 0 is for SENC3D_UNSPECIFIED_MEDIUM, n+1 is for n */ 833 const struct side_range* media_use = 834 darray_side_range_cdata_get(&scn->media_use) + m_idx; 835 /* Any not-already-used side is used as a starting point */ 836 side_id_t first_side_not_in_component = media_use->first; 837 double max_nz; 838 side_id_t max_nz_side_id; 839 const side_id_t last_side = media_use->last; 840 int component_canceled = 0, max_z_is_2sided = 0, fst_nz = 1; 841 res_T tmp_res = RES_OK; 842 ATOMIC id; 843 844 if(*p_res != RES_OK) continue; 845 if(first_side_not_in_component == SIDE_NULL__) 846 continue; /* Unused medium */ 847 ASSERT(first_side_not_in_component < 2 * scn->ntris); 848 ASSERT(darray_side_id_size_get(&stack) == 0); 849 ASSERT(darray_side_id_size_get(¤t_component) == 0); 850 for(;;) { 851 /* Process all components for this medium 852 * Here we start from a side of the currently processed medium that is 853 * not member of any component yet; by exploring neighbourhood the 854 * process can harvest sides with different media */ 855 side_id_t crt_side_id = get_side_not_in_connex_component 856 (last_side, trgsides, processed, &first_side_not_in_component, medium); 857 side_id_t cc_start_side_id = crt_side_id; 858 side_id_t cc_last_side_id = crt_side_id; 859 vrtx_id_t max_z_vrtx_id = VRTX_NULL__; 860 struct cc_descriptor *cc; 861 unsigned media_count; 862 double max_z = -DBL_MAX; 863 component_canceled = 0; 864 ASSERT(crt_side_id == SIDE_NULL__ || crt_side_id < 2 * scn->ntris); 865 darray_side_id_clear(¤t_component); 866 867 if(*p_res != RES_OK) break; 868 if(crt_side_id == SIDE_NULL__) 869 break; /* start_side_id=SIDE_NULL__ => component done! */ 870 871 #ifndef NDEBUG 872 { 873 trg_id_t tid = TRGSIDE_2_TRG(crt_side_id); 874 enum senc3d_side s = TRGSIDE_2_SIDE(crt_side_id); 875 medium_id_t side_med 876 = darray_triangle_in_data_get(&scn->triangles_in)[tid].medium[s]; 877 ASSERT(side_med == medium); 878 } 879 #endif 880 881 /* Reuse array if possible, or create a new one */ 882 if(current_media) { 883 /* current_media 0 is for SENC3D_UNSPECIFIED_MEDIUM, n+1 is for n */ 884 memset(current_media, 0, darray_side_range_size_get(&scn->media_use)); 885 } else { 886 current_media = MEM_CALLOC(alloc, 887 darray_side_range_size_get(&scn->media_use), sizeof(*current_media)); 888 if(!current_media) { 889 *p_res = RES_MEM_ERR; 890 continue; 891 } 892 } 893 current_media[m_idx] = 1; 894 media_count = 1; 895 for(;;) { /* Process all sides of this component */ 896 int i; 897 enum side_flag crt_side_flag = TRGSIDE_2_SIDEFLAG(crt_side_id); 898 struct trgside* crt_side = trgsides + crt_side_id; 899 const trg_id_t crt_trg_id = TRGSIDE_2_TRG(crt_side_id); 900 uchar* trg_used = processed + crt_trg_id; 901 const struct triangle_tmp* const trg_tmp = triangles_tmp + crt_trg_id; 902 ASSERT(crt_trg_id < scn->ntris); 903 904 if(*p_res != RES_OK) break; 905 906 /* Record max_z information 907 * Keep track of the appropriate vertex of the component in order to 908 * start upwards closest point query at the component grouping step of 909 * the algorithm. The most appropriate vertex is (the) one with the 910 * greater Z coordinate. */ 911 if(max_z < trg_tmp->max_z) { 912 /* New best vertex */ 913 const struct triangle_in* trg_in = 914 darray_triangle_in_cdata_get(&scn->triangles_in) + crt_trg_id; 915 max_z = trg_tmp->max_z; 916 /* Select a vertex with z == max_z */ 917 FOR_EACH(i, 0, 3) { 918 if(max_z == positions[trg_in->vertice_id[i]].pos.z) { 919 max_z_vrtx_id = trg_in->vertice_id[i]; 920 break; 921 } 922 } 923 ASSERT(i < 3); /* Found one */ 924 } 925 926 /* Record crt_side both as component and triangle level */ 927 if((*trg_used & crt_side_flag) == 0) { 928 OK2(darray_side_id_push_back(¤t_component, &crt_side_id)); 929 *trg_used = *trg_used | (uchar)crt_side_flag; 930 } 931 932 /* Store neighbour's sides in a waiting stack */ 933 FOR_EACH(i, 0, 3) { 934 side_id_t neighbour_id = crt_side->facing_side_id[i]; 935 trg_id_t nbour_trg_id = TRGSIDE_2_TRG(neighbour_id); 936 enum side_flag nbour_side_id = TRGSIDE_2_SIDEFLAG(neighbour_id); 937 uchar* nbour_used = processed + nbour_trg_id; 938 const struct trgside* neighbour = trgsides + neighbour_id; 939 medium_id_t nbour_med_idx = medium_id_2_medium_idx(neighbour->medium); 940 if((int64_t)nbour_med_idx < m_idx 941 || (*nbour_used & SIDE_CANCELED_FLAG(nbour_side_id))) 942 { 943 /* 1) Not the same medium. 944 * Neighbour's medium idx is less than current medium: the whole 945 * component is to be processed by another thread (possibly the one 946 * associated with neighbour's medium). 947 * 2) Neighbour was canceled: no need to replay the component 948 * again as it will eventually rediscover the side with low medium 949 * id and recancel all the work in progress */ 950 component_canceled = 1; 951 darray_side_id_clear(&stack); 952 /* Don't cancel used flags as all these sides will get us back to 953 * (at least) the neighbour side we have just discovered, that will 954 * cancel them again and again */ 955 sz = darray_side_id_size_get(¤t_component); 956 FOR_EACH(ii, 0, sz) { 957 side_id_t used_side 958 = darray_side_id_cdata_get(¤t_component)[ii]; 959 trg_id_t used_trg_id = TRGSIDE_2_TRG(used_side); 960 enum side_flag used_side_flag = TRGSIDE_2_SIDEFLAG(used_side); 961 uchar* used = processed + used_trg_id; 962 ASSERT(*used & (uchar)used_side_flag); 963 /* Set the used flag for sides in cancelled component as leading 964 * to further cancellations */ 965 *used |= SIDE_CANCELED_FLAG(used_side_flag); 966 } 967 goto canceled; 968 } 969 if(*nbour_used & nbour_side_id) continue; /* Already processed */ 970 /* Mark neighbour as processed and stack it */ 971 *nbour_used |= (uchar)nbour_side_id; 972 OK2(darray_side_id_push_back(&stack, &neighbour_id)); 973 OK2(darray_side_id_push_back(¤t_component, &neighbour_id)); 974 if(!current_media[nbour_med_idx]) { 975 current_media[nbour_med_idx] = 1; 976 media_count++; 977 } 978 } 979 sz = darray_side_id_size_get(&stack); 980 if(sz == 0) break; /* Empty stack => component is done! */ 981 crt_side_id = darray_side_id_cdata_get(&stack)[sz - 1]; 982 darray_side_id_pop_back(&stack); 983 cc_start_side_id = MMIN(cc_start_side_id, crt_side_id); 984 cc_last_side_id = MMAX(cc_last_side_id, crt_side_id); 985 } 986 canceled: 987 if(component_canceled) continue; 988 989 /* Register the new component and get it initialized */ 990 cc = MEM_ALLOC(alloc, sizeof(struct cc_descriptor)); 991 if(!cc) *p_res = RES_MEM_ERR; 992 if(*p_res != RES_OK) break; 993 994 ASSERT(max_z_vrtx_id != VRTX_NULL__); 995 cc_descriptor_init(alloc, cc); 996 id = ATOMIC_INCR(component_count) - 1; 997 ASSERT(id <= COMPONENT_MAX__); 998 cc->cc_id = (component_id_t)id; 999 sz = darray_side_id_size_get(¤t_component); 1000 ASSERT(sz > 0 && sz <= SIDE_MAX__); 1001 cc->side_count = (side_id_t)sz; 1002 cc->side_range.first = cc_start_side_id; 1003 cc->side_range.last = cc_last_side_id; 1004 cc->max_z_vrtx_id = max_z_vrtx_id; 1005 /* Tranfer ownership of the array to component */ 1006 ASSERT(!cc->media && current_media); 1007 cc->media = current_media; 1008 cc->media_count = media_count; 1009 current_media = NULL; 1010 1011 /* Write component membership in the global structure 1012 * No need for sync here as an unique thread writes a given side */ 1013 {STATIC_ASSERT(sizeof(cc->cc_id) >= 4, Cannot_write_IDs_sync_free);} 1014 ASSERT(IS_ALIGNED(triangles_comp->component, sizeof(cc->cc_id))); 1015 FOR_EACH(ii, 0, sz) { 1016 const side_id_t s_id = darray_side_id_cdata_get(¤t_component)[ii]; 1017 trg_id_t t_id = TRGSIDE_2_TRG(s_id); 1018 enum senc3d_side side = TRGSIDE_2_SIDE(s_id); 1019 component_id_t* cmp = triangles_comp[t_id].component; 1020 ASSERT(cmp[side] == COMPONENT_NULL__); 1021 ASSERT(medium_id_2_medium_idx(trgsides[s_id].medium) 1022 >= medium_id_2_medium_idx(medium)); 1023 cmp[side] = cc->cc_id; 1024 } 1025 /* Compute component's bbox, area and volume, and record information on 1026 * the max_z side of the component to help find out if the component is 1027 * inner or outer */ 1028 fst_nz = 1; 1029 max_nz = 0; 1030 max_nz_side_id = SIDE_NULL__; 1031 FOR_EACH(ii, 0, sz) { 1032 const side_id_t s_id = darray_side_id_cdata_get(¤t_component)[ii]; 1033 trg_id_t t_id = TRGSIDE_2_TRG(s_id); 1034 enum senc3d_side side = TRGSIDE_2_SIDE(s_id); 1035 struct triangle_comp* trg_comp = triangles_comp + t_id; 1036 const struct triangle_tmp* const trg_tmp = triangles_tmp + t_id; 1037 const struct triangle_in* trg_in = 1038 darray_triangle_in_cdata_get(&scn->triangles_in) + t_id; 1039 const union double3* vertices = 1040 darray_position_cdata_get(&scn->vertices); 1041 double edge0[3], edge1[3], normal[3], norm; 1042 const double* v0 = vertices[trg_in->vertice_id[0]].vec; 1043 const double* v1 = vertices[trg_in->vertice_id[1]].vec; 1044 const double* v2 = vertices[trg_in->vertice_id[2]].vec; 1045 int n, is_2sided = (trg_comp->component[SENC3D_FRONT] 1046 == trg_comp->component[SENC3D_BACK]); 1047 1048 /* Compute component's bbox, area and volume */ 1049 for(n = 0; n < 3; n++) { 1050 cc->bbox[n][0] = MMIN(cc->bbox[n][0], v0[n]); 1051 cc->bbox[n][0] = MMIN(cc->bbox[n][0], v1[n]); 1052 cc->bbox[n][0] = MMIN(cc->bbox[n][0], v2[n]); 1053 cc->bbox[n][1] = MMAX(cc->bbox[n][1], v0[n]); 1054 cc->bbox[n][1] = MMAX(cc->bbox[n][1], v1[n]); 1055 cc->bbox[n][1] = MMAX(cc->bbox[n][1], v2[n]); 1056 } 1057 d3_sub(edge0, v1, v0); 1058 d3_sub(edge1, v2, v0); 1059 d3_cross(normal, edge0, edge1); 1060 norm = d3_normalize(normal, normal); 1061 ASSERT(norm); 1062 cc->_2area += norm; 1063 1064 if(!is_2sided) { 1065 /* Build a tetrahedron whose base is the triangle and whose apex is 1066 * the coordinate system's origin. If the 2 sides of the triangle 1067 * are part of the component, the contribution of the triangle 1068 * must be 0. To achieve this, we just skip both sides */ 1069 double _2base = norm; /* 2 * base area */ 1070 double h = -d3_dot(normal, v0); /* Height of the tetrahedron */ 1071 if((trg_comp->component[SENC3D_FRONT] == cc->cc_id) 1072 == (scn->convention & SENC3D_CONVENTION_NORMAL_FRONT)) 1073 cc->_6volume += (h * _2base); 1074 else 1075 cc->_6volume -= (h * _2base); 1076 } 1077 1078 ASSERT(trg_comp->component[side] != COMPONENT_NULL__); (void)side; 1079 if(trg_tmp->max_z == max_z) { 1080 int i; 1081 /* Candidate to define max_nz (triangle using max_z_vrtx) */ 1082 FOR_EACH(i, 0, 3) { 1083 if(cc->max_z_vrtx_id == trg_in->vertice_id[i]) { 1084 if(fst_nz || fabs(normal[2]) > fabs(max_nz)) { 1085 max_nz_side_id = s_id; 1086 max_nz = normal[2]; 1087 max_z_is_2sided = is_2sided; 1088 fst_nz = 0; 1089 } 1090 break; 1091 } 1092 } 1093 } 1094 } 1095 ASSERT(!fst_nz); 1096 /* Determine if this component can be an inner part inside another 1097 * component (substracting a volume) as only these components will need 1098 * to search for their possible outer component when grouping 1099 * components to create enclosures. 1100 * The inner/outer property comes from the normal orientation of the 1101 * triangle on top of the component, this triangle being the one whose 1102 * |Nz| is maximal. If this triangle has its 2 sides in the component, 1103 * the component is inner */ 1104 if(max_nz == 0 || max_z_is_2sided) cc->is_outer_border = 0; 1105 else { 1106 ASSERT(max_nz_side_id != SIDE_NULL__); 1107 if(TRGSIDE_IS_FRONT(max_nz_side_id) 1108 == ((scn->convention & SENC3D_CONVENTION_NORMAL_FRONT) != 0)) { 1109 /* Geom normal points towards the component */ 1110 cc->is_outer_border = (max_nz < 0); 1111 } else { 1112 /* Geom normal points away from the component */ 1113 cc->is_outer_border = (max_nz > 0); 1114 } 1115 } 1116 1117 /* Need to synchronize connex_components growth as this global structure 1118 * is accessed by multipe threads */ 1119 #pragma omp critical 1120 { 1121 struct cc_descriptor** components; 1122 sz = darray_ptr_component_descriptor_size_get(connex_components); 1123 if(sz <= cc->cc_id) { 1124 tmp_res = darray_ptr_component_descriptor_resize(connex_components, 1125 1 + cc->cc_id); 1126 if(tmp_res != RES_OK) *p_res = tmp_res; 1127 } 1128 if(*p_res == RES_OK) { 1129 /* Don't set the pointer before resize as this can lead to move data */ 1130 components = 1131 darray_ptr_component_descriptor_data_get(connex_components); 1132 ASSERT(components[cc->cc_id] == NULL); 1133 components[cc->cc_id] = cc; 1134 } 1135 } 1136 } 1137 tmp_error: 1138 if(tmp_res != RES_OK) *p_res = tmp_res; 1139 } /* No barrier here */ 1140 1141 MEM_RM(alloc, processed); 1142 MEM_RM(alloc, current_media); 1143 darray_side_id_release(¤t_component); 1144 darray_side_id_release(&stack); 1145 1146 /* The first thread here creates the s3d view */ 1147 #pragma omp single nowait 1148 if(*p_res == RES_OK) { 1149 res_T res = RES_OK; 1150 struct s3d_device* s3d = NULL; 1151 struct s3d_scene* s3d_scn = NULL; 1152 struct s3d_shape* s3d_shp = NULL; 1153 struct s3d_vertex_data attribs; 1154 1155 attribs.type = S3D_FLOAT3; 1156 attribs.usage = S3D_POSITION; 1157 attribs.get = get_scn_position; 1158 1159 /* Put geometry in a 3D view */ 1160 OK(s3d_device_create(scn->dev->logger, alloc, 0, &s3d)); 1161 OK(s3d_scene_create(s3d, &s3d_scn)); 1162 OK(s3d_shape_create_mesh(s3d, &s3d_shp)); 1163 1164 /* Back to s3d API type for ntris and nverts */ 1165 ASSERT(scn->ntris <= UINT_MAX); 1166 ASSERT(scn->nverts <= UINT_MAX); 1167 OK(s3d_mesh_setup_indexed_vertices(s3d_shp, 1168 (unsigned)scn->ntris, get_scn_indices, 1169 (unsigned)scn->nverts, &attribs, 1, scn)); 1170 OK(s3d_mesh_set_hit_filter_function(s3d_shp, self_hit_filter, NULL)); 1171 OK(s3d_scene_attach_shape(s3d_scn, s3d_shp)); 1172 OK(s3d_scene_view_create(s3d_scn, S3D_TRACE, s3d_view)); 1173 error: 1174 if(res != RES_OK) *p_res = res; 1175 if(s3d) S3D(device_ref_put(s3d)); 1176 if(s3d_scn) S3D(scene_ref_put(s3d_scn)); 1177 if(s3d_shp) S3D(shape_ref_put(s3d_shp)); 1178 } 1179 1180 #ifndef NDEBUG 1181 /* Need to wait for all threads done to be able to check stuff */ 1182 #pragma omp barrier 1183 if(*p_res != RES_OK) return; 1184 #pragma omp single 1185 { 1186 trg_id_t t_; 1187 component_id_t c; 1188 ASSERT(ATOMIC_GET(component_count) == 1189 (int)darray_ptr_component_descriptor_size_get(connex_components)); 1190 FOR_EACH(t_, 0, scn->ntris) { 1191 struct triangle_comp* trg_comp = 1192 darray_triangle_comp_data_get(triangles_comp_array) + t_; 1193 ASSERT(trg_comp->component[SENC3D_FRONT] != COMPONENT_NULL__); 1194 ASSERT(trg_comp->component[SENC3D_BACK] != COMPONENT_NULL__); 1195 } 1196 FOR_EACH(c, 0, (component_id_t)ATOMIC_GET(component_count)) { 1197 struct cc_descriptor** components = 1198 darray_ptr_component_descriptor_data_get(connex_components); 1199 ASSERT(components[c] != NULL && components[c]->cc_id == c); 1200 } 1201 } /* Implicit barrier here */ 1202 #endif 1203 } 1204 1205 #ifndef NDEBUG 1206 static int 1207 compare_components 1208 (const void* ptr1, const void* ptr2) 1209 { 1210 const struct cc_descriptor* const* cc1 = ptr1; 1211 const struct cc_descriptor* const* cc2 = ptr2; 1212 ASSERT((*cc1)->side_range.first != (*cc2)->side_range.first); 1213 return (int)(*cc1)->side_range.first - (int)(*cc2)->side_range.first; 1214 } 1215 1216 static res_T 1217 reorder_components 1218 (struct senc3d_scene* scn, 1219 struct darray_ptr_component_descriptor* connex_components, 1220 struct darray_triangle_comp* triangles_comp_array) 1221 { 1222 struct cc_descriptor** cc_descriptors; 1223 struct triangle_comp* triangles_comp; 1224 component_id_t c; 1225 component_id_t* new_ids = NULL; 1226 size_t t, cc_count; 1227 res_T res = RES_OK; 1228 1229 ASSERT(connex_components && triangles_comp_array); 1230 1231 cc_count = darray_ptr_component_descriptor_size_get(connex_components); 1232 ASSERT(cc_count <= COMPONENT_MAX__); 1233 cc_descriptors = darray_ptr_component_descriptor_data_get(connex_components); 1234 1235 /* Single thread code: can allocate here */ 1236 new_ids = MEM_ALLOC(scn->dev->allocator, sizeof(*new_ids) * cc_count); 1237 if(!new_ids) { 1238 res = RES_MEM_ERR; 1239 goto error; 1240 } 1241 1242 FOR_EACH(c, 0, cc_count) ASSERT(cc_descriptors[c]->cc_id == c); 1243 /* Sort components by first side */ 1244 qsort(cc_descriptors, cc_count, sizeof(*cc_descriptors), compare_components); 1245 /* Make conversion table */ 1246 FOR_EACH(c, 0, cc_count) { 1247 component_id_t rank = cc_descriptors[c]->cc_id; 1248 new_ids[rank] = c; 1249 } 1250 triangles_comp = darray_triangle_comp_data_get(triangles_comp_array); 1251 FOR_EACH(c, 0, cc_count) { 1252 ASSERT(new_ids[cc_descriptors[c]->cc_id] == c); 1253 /* Update the enclosure ID in cc_descriptor */ 1254 cc_descriptors[c]->cc_id = c; 1255 } 1256 FOR_EACH(t, 0, scn->ntris) { 1257 struct triangle_comp* comp = triangles_comp + t; 1258 component_id_t fc = comp->component[SENC3D_FRONT]; 1259 component_id_t bc = comp->component[SENC3D_BACK]; 1260 ASSERT(new_ids[fc] < cc_count); 1261 comp->component[SENC3D_FRONT] = new_ids[fc]; 1262 ASSERT(new_ids[bc] < cc_count); 1263 comp->component[SENC3D_BACK] = new_ids[bc]; 1264 } 1265 1266 error: 1267 if(new_ids) MEM_RM(scn->dev->allocator, new_ids); 1268 return res; 1269 } 1270 #endif 1271 1272 static void 1273 group_connex_components 1274 (struct senc3d_scene* scn, 1275 struct darray_triangle_comp* triangles_comp, 1276 struct darray_ptr_component_descriptor* connex_components, 1277 struct s3d_scene_view* s3d_view, 1278 ATOMIC* next_enclosure_id, 1279 /* Shared error status. 1280 * We accept to overwrite an error with a different error */ 1281 res_T* res) 1282 { 1283 /* This function is called from an omp parallel block and executed 1284 * concurrently. */ 1285 struct cc_descriptor** descriptors; 1286 const union double3* positions; 1287 size_t tmp; 1288 component_id_t cc_count; 1289 int64_t ccc; 1290 struct filter_ctx ctx0, ctx1; 1291 float lower[3], upper[3]; 1292 const int log_components = scn->convention & SENC3D_LOG_COMPONENTS_INFORMATION; 1293 const int dump_components = scn->convention & SENC3D_DUMP_COMPONENTS_STL; 1294 1295 ASSERT(scn && triangles_comp && connex_components 1296 && s3d_view && next_enclosure_id && res); 1297 ASSERT(scn->analyze.enclosures_count == 1); 1298 1299 #ifndef NDEBUG 1300 #pragma omp single 1301 { 1302 /* Ensure reproducible cc_ids for debugging purpose */ 1303 *res = reorder_components(scn, connex_components, triangles_comp); 1304 } 1305 if(*res != RES_OK) return; 1306 #endif 1307 descriptors = darray_ptr_component_descriptor_data_get(connex_components); 1308 tmp = darray_ptr_component_descriptor_size_get(connex_components); 1309 ASSERT(tmp <= COMPONENT_MAX__); 1310 cc_count = (component_id_t)tmp; 1311 positions = darray_position_cdata_get(&scn->vertices); 1312 1313 #pragma omp single 1314 if(dump_components) { 1315 /* Do it now before any other problem can occur (fingers crossed). 1316 * Do it sequential and not optimized as it is debug code. 1317 * Don't throw errors, just skip to the next component. */ 1318 static unsigned scene_cpt = 0; 1319 struct str name; 1320 res_T tmp_res; 1321 const struct triangle_comp* tc = 1322 darray_triangle_comp_cdata_get(triangles_comp); 1323 const struct triangle_in* tin = darray_triangle_in_cdata_get(&scn->triangles_in); 1324 const int output_normal_in = 1325 (scn->convention & SENC3D_CONVENTION_NORMAL_INSIDE) != 0; 1326 str_init(scn->dev->allocator, &name); 1327 printf("Dumping components for scene #%u\n", scene_cpt); 1328 for(ccc = 0; ccc < (int64_t)cc_count; ccc++) { 1329 FILE* f; 1330 trg_id_t t; 1331 component_id_t c = (component_id_t)ccc; 1332 tmp_res = str_printf(&name, "scn_%u_comp_%u.stl", scene_cpt, c); 1333 if(tmp_res != RES_OK) continue; 1334 f = fopen(str_cget(&name), "w"); 1335 if(!f) continue; 1336 fprintf(f, "solid %s\n", str_cget(&name)); 1337 for(t = 0; t < scn->ntris; t++) { 1338 const component_id_t cf_id = tc[t].component[SENC3D_FRONT]; 1339 const component_id_t cb_id = tc[t].component[SENC3D_BACK]; 1340 if(cf_id == c || cb_id == c) { 1341 const vrtx_id_t* vertice_id = tin[t].vertice_id; 1342 double n[3], e1[3], e2[3]; 1343 const int input_normal_in = (cf_id == c); 1344 const int revert_triangle = (input_normal_in != output_normal_in); 1345 const vrtx_id_t i0 = vertice_id[0]; 1346 const vrtx_id_t i1 = vertice_id[revert_triangle ? 2 : 1]; 1347 const vrtx_id_t i2 = vertice_id[revert_triangle ? 1 : 2]; 1348 /* This triangle is in component #c */ 1349 if(cf_id == cb_id) { /* Both sides in component */ 1350 /* Could add some log */ 1351 } 1352 d3_sub(e1, positions[i1].vec, positions[i0].vec); 1353 d3_sub(e2, positions[i2].vec, positions[i0].vec); 1354 d3_normalize(n, d3_cross(n, e1, e2)); 1355 fprintf(f, " facet normal %16g %16g %16g\n", SPLIT3(n)); 1356 fprintf(f, " outer loop\n"); 1357 fprintf(f, " vertex %16g %16g %16g\n", SPLIT3(positions[i0].vec)); 1358 fprintf(f, " vertex %16g %16g %16g\n", SPLIT3(positions[i1].vec)); 1359 fprintf(f, " vertex %16g %16g %16g\n", SPLIT3(positions[i2].vec)); 1360 fprintf(f, " endloop\n"); 1361 fprintf(f, " endfacet\n"); 1362 } 1363 } 1364 printf("Dumped component #%u in file %s\n", c, str_cget(&name)); 1365 fprintf(f, "endsolid %s\n", str_cget(&name)); 1366 fclose(f); 1367 } 1368 str_release(&name); 1369 scene_cpt++; 1370 } 1371 1372 ctx0.type = CTX0; 1373 ctx0.scn = scn; 1374 ctx0.view = s3d_view; 1375 ctx0.triangles_comp = triangles_comp; 1376 ctx0.components = connex_components; 1377 ctx1.type = CTX1; 1378 ctx1.scn = scn; 1379 ctx1.view = s3d_view; 1380 ctx1.triangles_comp = triangles_comp; 1381 ctx1.components = connex_components; 1382 *res = s3d_scene_view_get_aabb(s3d_view, lower, upper); 1383 if(*res != RES_OK) goto end; 1384 #pragma omp for schedule(dynamic) 1385 for(ccc = 0; ccc < (int64_t)cc_count; ccc++) { 1386 res_T tmp_res = RES_OK; 1387 component_id_t c = (component_id_t)ccc; 1388 struct s3d_hit hit = S3D_HIT_NULL; 1389 float origin[3], rrr[3], r; 1390 struct cc_descriptor* const cc = descriptors[c]; 1391 const double* max_vrtx; 1392 int i; 1393 1394 if(*res != RES_OK) continue; 1395 ASSERT(cc->cc_id == c); 1396 ASSERT(cc->cc_group_root == CC_GROUP_ID_NONE); 1397 ASSERT(cc->max_z_vrtx_id < scn->nverts); 1398 1399 if(cc->is_outer_border) { 1400 ATOMIC id; 1401 /* No need to create a link from this CC: inner CC are doing the job */ 1402 cc->cc_group_root = cc->cc_id; /* New group with self as root */ 1403 id = ATOMIC_INCR(next_enclosure_id) - 1; 1404 ASSERT(id <= ENCLOSURE_MAX__); 1405 cc->enclosure_id = (enclosure_id_t)id; 1406 if(log_components) { 1407 #pragma omp critical 1408 printf("Component #%u: is outer, not processed\n", c); 1409 } 1410 continue; 1411 } 1412 1413 /* First step is to determine the distance of the closest upward geometry */ 1414 max_vrtx = positions[cc->max_z_vrtx_id].vec; 1415 f3_set_d3(origin, max_vrtx); 1416 /* Limit search radius. Only upwards moves (+Z) will be considered. 1417 * Use a radius allowing to reach the closest top vertex of scene's AABB */ 1418 FOR_EACH(i, 0, 2) { 1419 ASSERT(lower[i] <= origin[i] && origin[i] <= upper[i]); 1420 rrr[i] = MMIN(origin[i] - lower[i], upper[i] - origin[i]); 1421 } 1422 rrr[2] = upper[2] - origin[2]; 1423 r = f3_len(rrr) * (1 + FLT_EPSILON) + FLT_EPSILON; /* Ensure r > 0 */ 1424 ctx0.origin_component = cc->cc_id; 1425 ctx0.current_6volume = DBL_MAX; 1426 ctx0.hit_dist = FLT_MAX; 1427 ctx0.hit_component = COMPONENT_NULL__; 1428 tmp_res = s3d_scene_view_closest_point(s3d_view, origin, r, &ctx0, &hit); 1429 if(tmp_res != RES_OK) { 1430 *res = tmp_res; 1431 continue; 1432 } 1433 /* If no hit, the component is facing an infinite medium */ 1434 if(S3D_HIT_NONE(&hit)) { 1435 cc->cc_group_root = CC_GROUP_ROOT_INFINITE; 1436 cc->enclosure_id = 0; 1437 if(log_components) { 1438 #pragma omp critical 1439 printf("Component #%u: is part of enclosure #0\n", c); 1440 } 1441 continue; 1442 } 1443 1444 /* Second step is to determine which component faces component #c */ 1445 ctx1.origin_component = cc->cc_id; 1446 ctx1.current_6volume = DBL_MAX; 1447 ctx1.hit_dist = FLT_MAX; 1448 ctx1.hit_component = COMPONENT_NULL__; 1449 /* New search radius is hit.distance + some margin to cope with numerical 1450 * issues (and r==0 is an error) */ 1451 r = hit.distance * (1 + FLT_EPSILON) + FLT_EPSILON; 1452 if(log_components) { 1453 #pragma omp critical 1454 printf("Component #%u: starting search for components (R=%g) from %g %g %g\n", 1455 c, r, SPLIT3(origin)); 1456 } 1457 /* Cast a sphere to find links between connex components */ 1458 tmp_res = s3d_scene_view_closest_point(s3d_view, origin, r, &ctx1, &hit); 1459 if(tmp_res != RES_OK) { 1460 *res = tmp_res; 1461 continue; 1462 } 1463 /* As CTX1 filter rejects any hit, do not rely on hit but use result as 1464 * stored in ctx1 */ 1465 /* If no hit is accepted, the component is facing an infinite medium */ 1466 if(ctx1.hit_dist == FLT_MAX) { 1467 cc->cc_group_root = CC_GROUP_ROOT_INFINITE; 1468 cc->enclosure_id = 0; 1469 if(log_components) { 1470 #pragma omp critical 1471 printf("Component #%u: is part of enclosure #0\n", c); 1472 } 1473 continue; 1474 } 1475 else if(ctx1.hit_component == COMPONENT_NULL__) { 1476 /* The selected triangle was nearly parallel to the line of sight: 1477 * FRONT/BACK discrimination was not reliable enough and should be done 1478 * differently. */ 1479 /* Could try something; now just report a failure */ 1480 log_err(scn->dev, LIB_NAME": %s: %d: search failed\n", FUNC_NAME, 1481 __LINE__ ); 1482 *res = RES_BAD_OP; 1483 continue; 1484 } else { 1485 /* If hit, group this component */ 1486 cc->cc_group_root = ctx1.hit_component; 1487 ASSERT(cc->cc_group_root < cc_count); 1488 if(log_components) { 1489 #pragma omp critical 1490 printf("Component #%u: linked to component #%u\n", c, cc->cc_group_root); 1491 } 1492 } 1493 } 1494 /* Implicit barrier here */ 1495 if(*res != RES_OK) goto end; 1496 1497 /* One thread post-processes links to group connex components */ 1498 #pragma omp single 1499 { 1500 res_T tmp_res = RES_OK; 1501 size_t ec = (size_t)ATOMIC_GET(next_enclosure_id); 1502 ASSERT(ec <= ENCLOSURE_MAX__); 1503 scn->analyze.enclosures_count = (enclosure_id_t)ec; 1504 tmp_res = darray_enclosure_resize(&scn->analyze.enclosures, 1505 scn->analyze.enclosures_count); 1506 if(tmp_res != RES_OK) { 1507 *res = tmp_res; 1508 } else { 1509 struct enclosure_data* enclosures 1510 = darray_enclosure_data_get(&scn->analyze.enclosures); 1511 FOR_EACH(ccc, 0, (int64_t)cc_count) { 1512 component_id_t c = (component_id_t)ccc; 1513 struct cc_descriptor* const cc = descriptors[c]; 1514 const struct cc_descriptor* other_desc = cc; 1515 struct enclosure_data* enc; 1516 #ifndef NDEBUG 1517 component_id_t cc_cpt = 0; 1518 #endif 1519 1520 while(other_desc->enclosure_id == CC_GROUP_ID_NONE) { 1521 ASSERT(other_desc->cc_group_root < cc_count); 1522 other_desc = descriptors[other_desc->cc_group_root]; 1523 /* Cannot have more components in cc than cc_count! */ 1524 ASSERT(++cc_cpt <= cc_count); 1525 } 1526 ASSERT(other_desc->cc_group_root != CC_GROUP_ROOT_NONE); 1527 ASSERT(other_desc->enclosure_id != CC_GROUP_ID_NONE); 1528 cc->cc_group_root = other_desc->cc_group_root; 1529 cc->enclosure_id = other_desc->enclosure_id; 1530 enc = enclosures + cc->enclosure_id; 1531 ++enc->cc_count; 1532 /* Linked list of componnents */ 1533 enc->first_component = cc->cc_id; 1534 enc->side_range.first = MMIN(enc->side_range.first, cc->side_range.first); 1535 enc->side_range.last = MMAX(enc->side_range.last, cc->side_range.last); 1536 enc->side_count += cc->side_count; 1537 tmp_res = bool_array_of_media_merge(&enc->tmp_enclosed_media, cc->media, 1538 cc->media_count, darray_side_range_size_get(&scn->media_use)); 1539 if(tmp_res != RES_OK) { 1540 *res = tmp_res; 1541 break; 1542 } 1543 } 1544 } 1545 } 1546 /* Implicit barrier here */ 1547 end: 1548 return; 1549 } 1550 1551 static void 1552 collect_and_link_neighbours 1553 (struct senc3d_scene* scn, 1554 struct trgside* trgsides, 1555 struct darray_triangle_tmp* triangles_tmp_array, 1556 struct darray_frontier_edge* frontiers, 1557 struct htable_overlap* overlaps, 1558 /* Shared error status. 1559 * We accept to overwrite an error with a different error */ 1560 res_T* res) 1561 { 1562 /* This function is called from an omp parallel block and executed 1563 * concurrently. */ 1564 const struct triangle_in* triangles_in; 1565 struct triangle_tmp* triangles_tmp; 1566 const union double3* vertices; 1567 const int thread_count = omp_get_num_threads(); 1568 const int rank = omp_get_thread_num(); 1569 const int front = ((scn->convention & SENC3D_CONVENTION_NORMAL_FRONT) != 0); 1570 /* Htable used to give an id to edges */ 1571 struct htable_edge_id edge_ids; 1572 /* Array to keep neighbourhood of edges 1573 * Resize/Push operations on neighbourhood_by_edge are valid in the 1574 * openmp block because a given neighbourhood is only processed 1575 * by a single thread */ 1576 struct darray_neighbourhood neighbourhood_by_edge; 1577 edge_id_t edge_count; 1578 edge_id_t nbedges_guess; 1579 edge_id_t e; 1580 trg_id_t t; 1581 size_t sz; 1582 res_T tmp_res; 1583 1584 ASSERT(scn && trgsides && triangles_tmp_array && frontiers && res); 1585 ASSERT((size_t)scn->nverts + (size_t)scn->ntris + 2 <= EDGE_MAX__); 1586 1587 htable_edge_id_init(scn->dev->allocator, &edge_ids); 1588 darray_neighbourhood_init(scn->dev->allocator, &neighbourhood_by_edge); 1589 1590 triangles_in = darray_triangle_in_cdata_get(&scn->triangles_in); 1591 triangles_tmp = darray_triangle_tmp_data_get(triangles_tmp_array); 1592 vertices = darray_position_cdata_get(&scn->vertices); 1593 1594 ASSERT(scn->ntris == darray_triangle_tmp_size_get(triangles_tmp_array)); 1595 1596 /* Make some room for edges. */ 1597 nbedges_guess = 4 + (thread_count == 1 1598 ? (edge_id_t)(scn->nverts + scn->ntris) 1599 : (edge_id_t)((scn->nverts + scn->ntris) / (0.75 * thread_count))); 1600 OK2(darray_neighbourhood_reserve(&neighbourhood_by_edge, nbedges_guess)); 1601 OK2(htable_edge_id_reserve(&edge_ids, nbedges_guess)); 1602 1603 /* Loop on triangles to register edges. 1604 * All threads considering all the edges and processing some */ 1605 FOR_EACH(t, 0, scn->ntris) { 1606 struct trg_edge edge; 1607 uchar ee; 1608 FOR_EACH(ee, 0, 3) { 1609 edge_id_t* p_id; 1610 size_t n_sz; 1611 struct edge_neighbourhood* neighbourhood; 1612 struct neighbour_info* info; 1613 const vrtx_id_t v0 = triangles_in[t].vertice_id[ee]; 1614 const vrtx_id_t v1 = triangles_in[t].vertice_id[(ee + 1) % 3]; 1615 /* Process only "my" edges! */ 1616 const int64_t h = 1617 /* v0,v1 and v1,v0 must give the same hash!!! */ 1618 v0 + v1 + (int64_t)MMIN(v0, v1); 1619 if(h % thread_count != rank) continue; 1620 /* Create edge. */ 1621 set_edge(v0, v1, &edge, &triangles_tmp[t].reversed_edge[ee]); 1622 /* Find edge id; create it if not already done. */ 1623 p_id = htable_edge_id_find(&edge_ids, &edge); 1624 if(p_id) { 1625 neighbourhood = 1626 darray_neighbourhood_data_get(&neighbourhood_by_edge) + *p_id; 1627 ASSERT(neighbourhood->edge.vrtx0 == edge.vrtx0 1628 && neighbourhood->edge.vrtx1 == edge.vrtx1); 1629 } else { 1630 /* Create id */ 1631 edge_id_t id; 1632 sz = htable_edge_id_size_get(&edge_ids); 1633 ASSERT(sz <= EDGE_MAX__); 1634 id = (edge_id_t)sz; 1635 ASSERT(htable_edge_id_size_get(&edge_ids) 1636 == darray_neighbourhood_size_get(&neighbourhood_by_edge)); 1637 OK2(htable_edge_id_set(&edge_ids, &edge, &id)); 1638 OK2(darray_neighbourhood_resize(&neighbourhood_by_edge, 1 + sz)); 1639 neighbourhood = darray_neighbourhood_data_get(&neighbourhood_by_edge) + sz; 1640 /* Add neighbour info to a newly created edge's neighbour list */ 1641 neighbourhood->edge = edge; 1642 ASSERT(darray_neighbour_size_get(&neighbourhood->neighbours) == 0); 1643 /* Just a guess: few edges will have less than 2 neighbours */ 1644 OK2(darray_neighbour_reserve(&neighbourhood->neighbours, 2)); 1645 } 1646 /* Add neighbour info to neighbourhood */ 1647 n_sz = darray_neighbour_size_get(&neighbourhood->neighbours); 1648 OK2(darray_neighbour_resize(&neighbourhood->neighbours, 1 + n_sz)); 1649 info = darray_neighbour_data_get(&neighbourhood->neighbours) + n_sz; 1650 info->trg_id = t; 1651 info->common_edge_rank = ee; 1652 } 1653 } /* No barrier here. */ 1654 1655 /* Loop on collected edges. 1656 * For each edge sort triangle sides by rotation angle 1657 * and connect neighbours. */ 1658 sz = darray_neighbourhood_size_get(&neighbourhood_by_edge); 1659 ASSERT(sz <= EDGE_MAX__); 1660 edge_count = (edge_id_t)sz; 1661 FOR_EACH(e, 0, edge_count) { 1662 double edge[3], common_edge[3], basis[9], norm, max_z, maxz_edge, a; 1663 vrtx_id_t v0, v1, v2; 1664 struct edge_neighbourhood* neighbourhood 1665 = darray_neighbourhood_data_get(&neighbourhood_by_edge) + e; 1666 struct darray_neighbour* neighbour_list = &neighbourhood->neighbours; 1667 side_id_t i, neighbour_count; 1668 sz = darray_neighbour_size_get(neighbour_list); 1669 ASSERT(sz > 0 && sz <= SIDE_MAX__); 1670 neighbour_count = (side_id_t)sz; 1671 ASSERT(neighbour_count); 1672 v0 = neighbourhood->edge.vrtx0; 1673 v1 = neighbourhood->edge.vrtx1; 1674 d3_sub(common_edge, vertices[v1].vec, vertices[v0].vec); 1675 maxz_edge = MMAX(vertices[v0].pos.z, vertices[v1].pos.z); 1676 norm = d3_normalize(common_edge, common_edge); 1677 ASSERT(norm); (void)norm; 1678 d33_basis(basis, common_edge); 1679 d33_inverse(basis, basis); 1680 FOR_EACH(i, 0, neighbour_count) { 1681 struct neighbour_info* neighbour_info 1682 = darray_neighbour_data_get(neighbour_list) + i; 1683 const trg_id_t crt_id = neighbour_info->trg_id; 1684 const uchar crt_edge = neighbour_info->common_edge_rank; 1685 const struct triangle_in* trg_in = triangles_in + crt_id; 1686 struct triangle_tmp* neighbour = triangles_tmp + crt_id; 1687 union double3 n; /* Geometrical normal to neighbour triangle */ 1688 const int is_reversed = neighbour->reversed_edge[crt_edge]; 1689 v2 = trg_in->vertice_id[(crt_edge + 2) % 3]; 1690 max_z = MMAX(vertices[v2].pos.z, maxz_edge); 1691 ASSERT(neighbour->max_z <= max_z); 1692 neighbour->max_z = max_z; 1693 /* Compute rotation angle around common edge */ 1694 d3_sub(edge, vertices[v2].vec, vertices[v0].vec); 1695 d33_muld3(edge, basis, edge); 1696 ASSERT(d3_len(edge) != 0 && (edge[0] != 0 || edge[1] != 0)); 1697 neighbour_info->angle = atan2(edge[1], edge[0]); /* in ]-pi + pi]*/ 1698 if(is_reversed) 1699 d3(n.vec, +edge[1], -edge[0], 0); 1700 else 1701 d3(n.vec, -edge[1], +edge[0], 0); 1702 1703 /* Normal orientation calculation. */ 1704 if(neighbour_info->angle > 3 * PI / 4) { 1705 ASSERT(n.pos.y); 1706 neighbour_info->normal_toward_next_neighbour = (n.pos.y < 0); 1707 } else if(neighbour_info->angle > PI / 4) { 1708 ASSERT(n.pos.x); 1709 neighbour_info->normal_toward_next_neighbour = (n.pos.x < 0); 1710 } else if(neighbour_info->angle > -PI / 4) { 1711 ASSERT(n.pos.y); 1712 neighbour_info->normal_toward_next_neighbour = (n.pos.y > 0); 1713 } else if(neighbour_info->angle > -3 * PI / 4) { 1714 ASSERT(n.pos.x); 1715 neighbour_info->normal_toward_next_neighbour = (n.pos.x > 0); 1716 } else { 1717 ASSERT(n.pos.y); 1718 neighbour_info->normal_toward_next_neighbour = (n.pos.y < 0); 1719 } 1720 } 1721 /* Sort triangles by rotation angle */ 1722 qsort(darray_neighbour_data_get(neighbour_list), neighbour_count, 1723 sizeof(struct neighbour_info), neighbour_cmp); 1724 /* Link sides. 1725 * Create cycles of sides by neighbourhood around common edge. */ 1726 a = -DBL_MAX; 1727 FOR_EACH(i, 0, neighbour_count) { 1728 /* Neighbourhood info for current pair of triangles */ 1729 const struct neighbour_info* current 1730 = darray_neighbour_cdata_get(neighbour_list) + i; 1731 const struct neighbour_info* ccw_neighbour 1732 = darray_neighbour_cdata_get(neighbour_list) + (i + 1) % neighbour_count; 1733 /* Rank of the edge of interest in triangles */ 1734 const uchar crt_edge = current->common_edge_rank; 1735 /* Here ccw refers to the rotation around the common edge 1736 * and has nothing to do with vertices order in triangle definition 1737 * nor Front/Back side convention */ 1738 const uchar ccw_edge = ccw_neighbour->common_edge_rank; 1739 /* User id of current triangles */ 1740 const trg_id_t crt_id = current->trg_id; 1741 const trg_id_t ccw_id = ccw_neighbour->trg_id; 1742 /* Facing sides of triangles */ 1743 const enum senc3d_side crt_side 1744 = (current->normal_toward_next_neighbour == front) 1745 ? SENC3D_FRONT : SENC3D_BACK; 1746 const enum senc3d_side ccw_side 1747 = (ccw_neighbour->normal_toward_next_neighbour == front) 1748 ? SENC3D_BACK : SENC3D_FRONT; 1749 /* Index of sides in trgsides */ 1750 const side_id_t crt_side_idx = TRGIDxSIDE_2_TRGSIDE(crt_id, crt_side); 1751 const side_id_t ccw_side_idx = TRGIDxSIDE_2_TRGSIDE(ccw_id, ccw_side); 1752 /* Side ptrs */ 1753 struct trgside* const p_crt_side = trgsides + crt_side_idx; 1754 struct trgside* const p_ccw_side = trgsides + ccw_side_idx; 1755 /* Check that angle is a discriminant property */ 1756 ASSERT(a <= current->angle); /* Is sorted */ 1757 if(a == current->angle) { 1758 /* Two consecutive triangles with same angle! Store them */ 1759 const struct neighbour_info* previous; 1760 trg_id_t prev_id; 1761 previous = darray_neighbour_cdata_get(neighbour_list) + i - 1; 1762 prev_id = previous->trg_id; 1763 #pragma omp critical 1764 { 1765 char one = 1; 1766 tmp_res = htable_overlap_set(overlaps, &crt_id, &one); 1767 if(tmp_res == RES_OK) 1768 tmp_res = htable_overlap_set(overlaps, &prev_id, &one); 1769 } 1770 if(tmp_res != RES_OK) goto tmp_error; 1771 } 1772 a = current->angle; 1773 /* Link sides */ 1774 ASSERT(p_crt_side->facing_side_id[crt_edge] == SIDE_NULL__); 1775 ASSERT(p_ccw_side->facing_side_id[ccw_edge] == SIDE_NULL__); 1776 p_crt_side->facing_side_id[crt_edge] = ccw_side_idx; 1777 p_ccw_side->facing_side_id[ccw_edge] = crt_side_idx; 1778 /* Record media */ 1779 ASSERT(p_crt_side->medium == MEDIUM_NULL__ 1780 || p_crt_side->medium == triangles_in[crt_id].medium[crt_side]); 1781 ASSERT(p_ccw_side->medium == MEDIUM_NULL__ 1782 || p_ccw_side->medium == triangles_in[ccw_id].medium[ccw_side]); 1783 p_crt_side->medium = triangles_in[crt_id].medium[crt_side]; 1784 p_ccw_side->medium = triangles_in[ccw_id].medium[ccw_side]; 1785 ASSERT(p_crt_side->medium == SENC3D_UNSPECIFIED_MEDIUM 1786 || p_crt_side->medium < darray_side_range_size_get(&scn->media_use) - 1); 1787 ASSERT(p_ccw_side->medium == SENC3D_UNSPECIFIED_MEDIUM 1788 || p_ccw_side->medium < darray_side_range_size_get(&scn->media_use) - 1); 1789 /* Detect triangles that could surround a hole: 1790 * - single triangle on (one of) its edge 1791 * - different media on its sides */ 1792 if(neighbour_count == 1 1793 && p_crt_side->medium != p_ccw_side->medium) 1794 #pragma omp critical 1795 { 1796 struct frontier_edge frontier_edge; 1797 frontier_edge.trg = crt_id; 1798 frontier_edge.vrtx0 = v0; 1799 frontier_edge.vrtx1 = v1; 1800 darray_frontier_edge_push_back(frontiers, &frontier_edge); 1801 } 1802 } 1803 } 1804 1805 tmp_error: 1806 if(tmp_res != RES_OK) *res = tmp_res; 1807 /* Threads are allowed to return whitout sync. */ 1808 htable_edge_id_release(&edge_ids); 1809 darray_neighbourhood_release(&neighbourhood_by_edge); 1810 } 1811 1812 static int 1813 compare_enclosures 1814 (const void* ptr1, const void* ptr2) 1815 { 1816 const struct enclosure_data* e1 = ptr1; 1817 const struct enclosure_data* e2 = ptr2; 1818 ASSERT(e1->side_range.first != e2->side_range.first); 1819 return (int)e1->side_range.first - (int)e2->side_range.first; 1820 } 1821 1822 static res_T 1823 reorder_enclosures 1824 (struct senc3d_scene* scn, 1825 const struct darray_ptr_component_descriptor* connex_components) 1826 { 1827 struct enclosure_data* enclosures; 1828 struct cc_descriptor* const* cc_descriptors; 1829 enclosure_id_t* new_ids; 1830 enclosure_id_t e; 1831 size_t c, cc_count; 1832 res_T res = RES_OK; 1833 1834 /* Single thread code: can allocate here */ 1835 new_ids = MEM_ALLOC(scn->dev->allocator, 1836 sizeof(*new_ids) * scn->analyze.enclosures_count); 1837 if(!new_ids) { 1838 res = RES_MEM_ERR; 1839 goto error; 1840 } 1841 cc_count = darray_ptr_component_descriptor_size_get(connex_components); 1842 ASSERT(cc_count <= COMPONENT_MAX__); 1843 cc_descriptors = darray_ptr_component_descriptor_cdata_get(connex_components); 1844 enclosures = darray_enclosure_data_get(&scn->analyze.enclosures); 1845 /* Store initial enclosure order */ 1846 FOR_EACH(e, 0, scn->analyze.enclosures_count) 1847 enclosures[e].header.enclosure_id = e; 1848 /* Sort enclosures by first side while keeping enclosure 0 1849 * at rank 0 (its a convention) */ 1850 qsort(enclosures + 1, scn->analyze.enclosures_count - 1, sizeof(*enclosures), 1851 compare_enclosures); 1852 /* Make conversion table */ 1853 FOR_EACH(e, 0, scn->analyze.enclosures_count) { 1854 enclosure_id_t rank = enclosures[e].header.enclosure_id; 1855 new_ids[rank] = e; 1856 } 1857 FOR_EACH(e, 0, scn->analyze.enclosures_count) { 1858 ASSERT(new_ids[enclosures[e].header.enclosure_id] == e); 1859 enclosures[e].header.enclosure_id = e; 1860 } 1861 FOR_EACH(c, 0, cc_count) { 1862 /* Update the enclosure ID in cc_descriptor */ 1863 enclosure_id_t new_id = new_ids[cc_descriptors[c]->enclosure_id]; 1864 ASSERT(new_id < scn->analyze.enclosures_count); 1865 cc_descriptors[c]->enclosure_id = new_id; 1866 } 1867 error: 1868 if(new_ids) MEM_RM(scn->dev->allocator, new_ids); 1869 return res; 1870 } 1871 1872 static void 1873 build_result 1874 (struct senc3d_scene* scn, 1875 const struct darray_ptr_component_descriptor* connex_components, 1876 const struct darray_triangle_comp* triangles_comp_array, 1877 struct darray_frontier_edge* frontiers, 1878 /* Shared error status. 1879 * We accept to overwrite an error with a different error */ 1880 res_T* res) 1881 { 1882 /* This function is called from an omp parallel block and executed 1883 * concurrently. */ 1884 struct mem_allocator* alloc; 1885 struct cc_descriptor* const* cc_descriptors; 1886 struct enclosure_data* enclosures; 1887 const struct triangle_in* triangles_in; 1888 struct triangle_enc* triangles_enc; 1889 const struct triangle_comp* triangles_comp; 1890 struct htable_vrtx_id vtable; 1891 int output_normal_in, normals_front, normals_back; 1892 size_t cc_count; 1893 int64_t tt; 1894 int64_t ee; 1895 1896 ASSERT(scn && connex_components && triangles_comp_array && frontiers && res); 1897 1898 alloc = scn->dev->allocator; 1899 output_normal_in = (scn->convention & SENC3D_CONVENTION_NORMAL_INSIDE) != 0; 1900 normals_front = (scn->convention & SENC3D_CONVENTION_NORMAL_FRONT) != 0; 1901 normals_back = (scn->convention & SENC3D_CONVENTION_NORMAL_BACK) != 0; 1902 ASSERT(normals_back != normals_front); 1903 ASSERT(output_normal_in 1904 != ((scn->convention & SENC3D_CONVENTION_NORMAL_OUTSIDE) != 0)); 1905 cc_count = darray_ptr_component_descriptor_size_get(connex_components); 1906 ASSERT(cc_count <= COMPONENT_MAX__); 1907 cc_descriptors = darray_ptr_component_descriptor_cdata_get(connex_components); 1908 enclosures = darray_enclosure_data_get(&scn->analyze.enclosures); 1909 triangles_in = darray_triangle_in_cdata_get(&scn->triangles_in); 1910 triangles_comp = darray_triangle_comp_cdata_get(triangles_comp_array); 1911 1912 #pragma omp single 1913 { 1914 size_t c; 1915 enclosure_id_t e; 1916 res_T tmp_res = 1917 darray_triangle_enc_resize(&scn->analyze.triangles_enc, scn->ntris); 1918 if(tmp_res != RES_OK) 1919 *res = tmp_res; 1920 /* Sort enclosures by first side to ensure reproducible results */ 1921 else *res = reorder_enclosures(scn, connex_components); 1922 if(*res != RES_OK) goto single_err; 1923 /* Compute area and volume */ 1924 FOR_EACH(c, 0, cc_count) { 1925 struct enclosure_data* enc; 1926 ASSERT(cc_descriptors[c]->enclosure_id < scn->analyze.enclosures_count); 1927 enc = enclosures + cc_descriptors[c]->enclosure_id; 1928 /* Sum up areas and volumes of components int enclosures */ 1929 enc->header.area += cc_descriptors[c]->_2area; 1930 enc->header.volume += cc_descriptors[c]->_6volume; 1931 } 1932 FOR_EACH(e, 0, scn->analyze.enclosures_count) { 1933 struct enclosure_data* enc = enclosures + e; 1934 enc->header.area /= 2; 1935 enc->header.volume /= 6; 1936 } 1937 single_err: (void)0; 1938 }/* Implicit barrier here. */ 1939 if(*res != RES_OK) goto exit; 1940 triangles_enc = darray_triangle_enc_data_get(&scn->analyze.triangles_enc); 1941 1942 /* Build global enclosure information */ 1943 #pragma omp for 1944 for(tt = 0; tt < (int64_t)scn->ntris; tt++) { 1945 trg_id_t t = (trg_id_t)tt; 1946 const component_id_t cf_id = triangles_comp[t].component[SENC3D_FRONT]; 1947 const component_id_t cb_id = triangles_comp[t].component[SENC3D_BACK]; 1948 const struct cc_descriptor* cf = cc_descriptors[cf_id]; 1949 const struct cc_descriptor* cb = cc_descriptors[cb_id]; 1950 const enclosure_id_t ef_id = cf->enclosure_id; 1951 const enclosure_id_t eb_id = cb->enclosure_id; 1952 ASSERT(triangles_enc[t].enclosure[SENC3D_FRONT] == ENCLOSURE_NULL__); 1953 triangles_enc[t].enclosure[SENC3D_FRONT] = ef_id; 1954 ASSERT(triangles_enc[t].enclosure[SENC3D_BACK] == ENCLOSURE_NULL__); 1955 triangles_enc[t].enclosure[SENC3D_BACK] = eb_id; 1956 } 1957 /* Implicit barrier here */ 1958 1959 /* Resize/push operations on enclosure's fields are valid in the 1960 * openmp block as a given enclosure is processed by a single thread */ 1961 htable_vrtx_id_init(alloc, &vtable); 1962 1963 ASSERT(scn->analyze.enclosures_count <= ENCLOSURE_MAX__); 1964 #pragma omp for schedule(dynamic) nowait 1965 for(ee = 0; ee < (int64_t)scn->analyze.enclosures_count; ee++) { 1966 const enclosure_id_t e = (enclosure_id_t)ee; 1967 struct enclosure_data* enc = enclosures + ee; 1968 trg_id_t fst_idx = 0; 1969 trg_id_t sgd_idx = enc->side_count; 1970 trg_id_t t; 1971 medium_id_t m; 1972 res_T tmp_res = RES_OK; 1973 ASSERT(enc->first_component < cc_count); 1974 ASSERT(cc_descriptors[enc->first_component]->cc_id 1975 == enc->first_component); 1976 1977 if(*res != RES_OK) continue; 1978 ASSERT(e <= ENCLOSURE_MAX__); 1979 enc->header.enclosure_id = (unsigned)ee; /* Back to API type */ 1980 ASSERT(cc_descriptors[enc->first_component]->enclosure_id 1981 == enc->header.enclosure_id); 1982 enc->header.is_infinite = (e == 0); 1983 1984 ASSERT(enc->header.enclosed_media_count 1985 < darray_side_range_size_get(&scn->media_use)); 1986 OK2(bool_array_of_media_to_darray_media(&enc->enclosed_media, 1987 &enc->tmp_enclosed_media, darray_side_range_size_get(&scn->media_use))); 1988 ASSERT(darray_media_size_get(&enc->enclosed_media) <= MEDIUM_MAX__); 1989 enc->header.enclosed_media_count 1990 = (medium_id_t)darray_media_size_get(&enc->enclosed_media); 1991 darray_uchar_purge(&enc->tmp_enclosed_media); 1992 1993 /* Add this enclosure in relevant by-medium lists */ 1994 FOR_EACH(m, 0, enc->header.enclosed_media_count) { 1995 medium_id_t medium = darray_media_cdata_get(&enc->enclosed_media)[m]; 1996 size_t m_idx = medium_id_2_medium_idx(medium); 1997 struct darray_enc_id* enc_ids_array_by_medium; 1998 ASSERT(medium == SENC3D_UNSPECIFIED_MEDIUM 1999 || medium < darray_side_range_size_get(&scn->media_use) - 1); 2000 ASSERT(darray_enc_ids_array_size_get(&scn->analyze.enc_ids_array_by_medium) 2001 == darray_side_range_size_get(&scn->media_use)); 2002 enc_ids_array_by_medium = 2003 darray_enc_ids_array_data_get(&scn->analyze.enc_ids_array_by_medium) 2004 + m_idx; 2005 #pragma omp critical 2006 { 2007 tmp_res = darray_enc_id_push_back(enc_ids_array_by_medium, &e); 2008 } 2009 if(tmp_res != RES_OK) { 2010 *res = tmp_res; 2011 break; 2012 } 2013 } 2014 if(*res != RES_OK) continue; 2015 2016 /* Build side and vertex lists. */ 2017 OK2(darray_sides_enc_resize(&enc->sides, enc->side_count)); 2018 /* Size is just a hint */ 2019 OK2(darray_vrtx_id_reserve(&enc->vertices, 2020 (size_t)(enc->side_count * 0.6))); 2021 /* New vertex numbering scheme local to the enclosure */ 2022 htable_vrtx_id_clear(&vtable); 2023 ASSERT(scn->ntris == darray_triangle_in_size_get(&scn->triangles_in)); 2024 /* Put at the end the back-faces of triangles that also have their 2025 * front-face in the list. */ 2026 for(t = TRGSIDE_2_TRG(enc->side_range.first); 2027 t <= TRGSIDE_2_TRG(enc->side_range.last); 2028 t++) 2029 { 2030 const struct triangle_in* trg_in = triangles_in + t; 2031 struct side_enc* side_enc; 2032 vrtx_id_t vertice_id[3]; 2033 int i; 2034 if(triangles_enc[t].enclosure[SENC3D_FRONT] != e 2035 && triangles_enc[t].enclosure[SENC3D_BACK] != e) 2036 continue; 2037 ++enc->header.unique_primitives_count; 2038 2039 FOR_EACH(i, 0, 3) { 2040 vrtx_id_t* p_id = htable_vrtx_id_find(&vtable, trg_in->vertice_id + i); 2041 if(p_id) { 2042 vertice_id[i] = *p_id; /* Known vertex */ 2043 } else { 2044 /* Create new association */ 2045 size_t tmp = htable_vrtx_id_size_get(&vtable); 2046 ASSERT(tmp == darray_vrtx_id_size_get(&enc->vertices)); 2047 ASSERT(tmp < VRTX_MAX__); 2048 vertice_id[i] = (vrtx_id_t)tmp; 2049 OK2(htable_vrtx_id_set(&vtable, trg_in->vertice_id + i, 2050 vertice_id + i)); 2051 OK2(darray_vrtx_id_push_back(&enc->vertices, trg_in->vertice_id + i)); 2052 ++enc->header.vertices_count; 2053 } 2054 } 2055 ASSERT(triangles_enc[t].enclosure[SENC3D_FRONT] == e 2056 || triangles_enc[t].enclosure[SENC3D_BACK] == e); 2057 if(triangles_enc[t].enclosure[SENC3D_FRONT] == e) { 2058 /* Front side of the original triangle is member of the enclosure */ 2059 int input_normal_in = normals_front; 2060 int revert_triangle = (input_normal_in != output_normal_in); 2061 ++enc->header.primitives_count; 2062 side_enc = darray_sides_enc_data_get(&enc->sides) + fst_idx++; 2063 FOR_EACH(i, 0, 3) { 2064 int ii = revert_triangle ? 2 - i : i; 2065 side_enc->vertice_id[i] = vertice_id[ii]; 2066 } 2067 side_enc->side_id = TRGIDxSIDE_2_TRGSIDE(t, SENC3D_FRONT); 2068 } 2069 if(triangles_enc[t].enclosure[SENC3D_BACK] == e) { 2070 /* Back side of the original triangle is member of the enclosure */ 2071 int input_normal_in = normals_back; 2072 int revert_triangle = (input_normal_in != output_normal_in); 2073 ++enc->header.primitives_count; 2074 /* If both sides are in the enclosure, put the second side at the end */ 2075 side_enc = darray_sides_enc_data_get(&enc->sides) + 2076 ((triangles_enc[t].enclosure[SENC3D_FRONT] == e) ? --sgd_idx : fst_idx++); 2077 FOR_EACH(i, 0, 3) { 2078 int ii = revert_triangle ? 2 - i : i; 2079 side_enc->vertice_id[i] = vertice_id[ii]; 2080 } 2081 side_enc->side_id = TRGIDxSIDE_2_TRGSIDE(t, SENC3D_BACK); 2082 } 2083 if(fst_idx == sgd_idx) break; 2084 } 2085 continue; 2086 tmp_error: 2087 ASSERT(tmp_res != RES_OK); 2088 *res = tmp_res; 2089 } /* No barrier here */ 2090 htable_vrtx_id_release(&vtable); 2091 /* The first thread here copies frontiers into descriptor */ 2092 #pragma omp single nowait 2093 darray_frontier_edge_copy_and_clear(&scn->analyze.frontiers, frontiers); 2094 /* No barrier here */ 2095 exit: 2096 return; 2097 } 2098 2099 /****************************************************************************** 2100 * Exported functions 2101 *****************************************************************************/ 2102 res_T 2103 scene_analyze 2104 (struct senc3d_scene* scn) 2105 { 2106 /* By triangle tmp data */ 2107 struct darray_triangle_tmp triangles_tmp; 2108 char triangles_tmp_initialized = 0; 2109 /* Array of connex components. 2110 * They are refered to by arrays of ids. */ 2111 struct darray_ptr_component_descriptor connex_components; 2112 char connex_components_initialized = 0; 2113 /* Array of frontiers edges */ 2114 struct darray_frontier_edge frontiers; 2115 char frontiers_initialized = 0; 2116 /* Htable used to store overlapping segments */ 2117 struct htable_overlap overlaps; 2118 char overlaps_initialized = 0; 2119 /* Store by-triangle components */ 2120 struct darray_triangle_comp triangles_comp; 2121 char triangles_comp_initialized = 0; 2122 /* Array of triangle sides. */ 2123 struct trgside* trgsides = NULL; 2124 struct s3d_scene_view* s3d_view = NULL; 2125 /* Atomic counters to share beetwen threads */ 2126 ATOMIC component_count = 0; 2127 ATOMIC next_enclosure_id = 1; 2128 res_T res = RES_OK; 2129 res_T res2 = RES_OK; 2130 2131 if(!scn) return RES_BAD_ARG; 2132 2133 if(!scn->ntris) goto exit; 2134 2135 darray_triangle_tmp_init(scn->dev->allocator, &triangles_tmp); 2136 triangles_tmp_initialized = 1; 2137 darray_frontier_edge_init(scn->dev->allocator, &frontiers); 2138 frontiers_initialized = 1; 2139 htable_overlap_init(scn->dev->allocator, &overlaps); 2140 overlaps_initialized = 1; 2141 2142 OK(darray_triangle_tmp_resize(&triangles_tmp, scn->ntris)); 2143 trgsides 2144 = MEM_CALLOC(scn->dev->allocator, 2 * scn->ntris, sizeof(struct trgside)); 2145 if(!trgsides) { 2146 res = RES_MEM_ERR; 2147 goto error; 2148 } 2149 #ifndef NDEBUG 2150 else { 2151 /* Initialise trgsides to allow assert code */ 2152 size_t i; 2153 FOR_EACH(i, 0, 2 * scn->ntris) 2154 init_trgside(scn->dev->allocator, trgsides + i); 2155 } 2156 #endif 2157 2158 /* The end of the analyze is multithreaded */ 2159 ASSERT(scn->dev->nthreads > 0); 2160 #pragma omp parallel num_threads(scn->dev->nthreads) 2161 { 2162 /* Step 1: build neighbourhoods */ 2163 collect_and_link_neighbours(scn, trgsides, &triangles_tmp, &frontiers, 2164 &overlaps, &res); 2165 /* No barrier at the end of step 1: data used in step 1 cannot be 2166 * released / data produced by step 1 cannot be used 2167 * until next sync point */ 2168 2169 /* The first thread here allocates some data. 2170 * Barrier needed at the end to ensure data created before any use. */ 2171 #pragma omp single 2172 { 2173 res_T tmp_res = RES_OK; 2174 darray_ptr_component_descriptor_init(scn->dev->allocator, 2175 &connex_components); 2176 connex_components_initialized = 1; 2177 /* Just a hint; to limit contention */ 2178 OK2(darray_ptr_component_descriptor_reserve(&connex_components, 2179 2 * darray_side_range_size_get(&scn->media_use))); 2180 darray_triangle_comp_init(scn->dev->allocator, &triangles_comp); 2181 triangles_comp_initialized = 1; 2182 OK2(darray_triangle_comp_resize(&triangles_comp, scn->ntris)); 2183 tmp_error: 2184 if(tmp_res != RES_OK) res2 = tmp_res; 2185 } 2186 /* Implicit barrier here: constraints on step 1 data are now met */ 2187 2188 #pragma omp single 2189 { 2190 /* Save all the overlapping segments in a darray */ 2191 res_T tmp_res = RES_OK; 2192 struct htable_overlap_iterator it, end; 2193 ASSERT(overlaps_initialized); 2194 htable_overlap_begin(&overlaps, &it); 2195 htable_overlap_end(&overlaps, &end); 2196 tmp_res = darray_trg_id_reserve(&scn->analyze.overlapping_ids, 2197 htable_overlap_size_get(&overlaps)); 2198 if(tmp_res != RES_OK) goto tmp_error2; 2199 while(!htable_overlap_iterator_eq(&it, &end)) { 2200 tmp_res = darray_trg_id_push_back(&scn->analyze.overlapping_ids, 2201 htable_overlap_iterator_key_get(&it)); 2202 if(tmp_res != RES_OK) goto tmp_error2; 2203 htable_overlap_iterator_next(&it); 2204 } 2205 /* Sort overlapping triangle ids */ 2206 qsort(darray_trg_id_data_get(&scn->analyze.overlapping_ids), 2207 darray_trg_id_size_get(&scn->analyze.overlapping_ids), 2208 sizeof(*darray_trg_id_cdata_get(&scn->analyze.overlapping_ids)), 2209 cmp_trg_id); 2210 htable_overlap_release(&overlaps); 2211 overlaps_initialized = 0; 2212 tmp_error2: 2213 if(tmp_res != RES_OK) res2 = tmp_res; 2214 } 2215 2216 if(darray_trg_id_size_get(&scn->analyze.overlapping_ids)) { 2217 /* Stop analysis here as the model is ill-formed */ 2218 goto end_; 2219 } 2220 2221 if(res != RES_OK || res2 != RES_OK) { 2222 #pragma omp single nowait 2223 { 2224 if(res != RES_OK) { 2225 log_err(scn->dev, 2226 LIB_NAME":%s: could not build neighbourhoods from scene\n", 2227 FUNC_NAME); 2228 } else { 2229 res = res2; 2230 } 2231 } 2232 goto error_; 2233 } 2234 2235 /* Step 2: extract triangle connex components */ 2236 extract_connex_components(scn, trgsides, &connex_components, 2237 &triangles_tmp, &triangles_comp, &s3d_view, &component_count, &res); 2238 /* No barrier at the end of step 2: data used in step 2 cannot be 2239 * released / data produced by step 2 cannot be used 2240 * until next sync point */ 2241 2242 #pragma omp barrier 2243 /* Constraints on step 2 data are now met */ 2244 2245 if(res != RES_OK) { 2246 #pragma omp single nowait 2247 { 2248 log_err(scn->dev, 2249 LIB_NAME":%s: could not extract connex components from scene\n", 2250 FUNC_NAME); 2251 } /* No barrier here */ 2252 goto error_; 2253 } 2254 2255 /* One thread releases some data before going to step 3, 2256 * the others go to step 3 without sync */ 2257 #pragma omp single nowait 2258 { 2259 darray_triangle_tmp_release(&triangles_tmp); 2260 triangles_tmp_initialized = 0; 2261 } /* No barrier here */ 2262 2263 /* Step 3: group components */ 2264 group_connex_components(scn, &triangles_comp, &connex_components, s3d_view, 2265 &next_enclosure_id, &res); 2266 /* Barrier at the end of step 3: data used in step 3 can be released / 2267 * data produced by step 3 can be used */ 2268 2269 if(res != RES_OK) { 2270 #pragma omp single nowait 2271 { 2272 log_err(scn->dev, 2273 LIB_NAME":%s: could not group connex components from scene\n", 2274 FUNC_NAME); 2275 } 2276 goto error_; 2277 } 2278 2279 /* One thread releases some data and allocate other data before going to 2280 * step 4, the others waiting for alloced data */ 2281 #pragma omp single 2282 { 2283 if(s3d_view) S3D(scene_view_ref_put(s3d_view)); 2284 s3d_view = NULL; 2285 } /* Implicit barrier here */ 2286 if(res != RES_OK) goto error_; 2287 2288 /* Step 4: Build result */ 2289 build_result(scn, &connex_components, &triangles_comp, &frontiers, &res); 2290 /* No barrier at the end of step 4: data used in step 4 cannot be 2291 * released / data produced by step 4 cannot be used 2292 * until next sync point */ 2293 2294 #pragma omp barrier 2295 /* Constraints on step 4 data are now met */ 2296 2297 if(res != RES_OK) { 2298 #pragma omp single nowait 2299 { 2300 log_err(scn->dev, 2301 LIB_NAME":%s: could not build result\n", FUNC_NAME); 2302 } 2303 goto error_; 2304 } 2305 2306 /* Some threads release data */ 2307 #pragma omp sections nowait 2308 { 2309 #pragma omp section 2310 { 2311 custom_darray_ptr_component_descriptor_release(&connex_components); 2312 connex_components_initialized = 0; 2313 } 2314 #pragma omp section 2315 { 2316 darray_triangle_comp_release(&triangles_comp); 2317 triangles_comp_initialized = 0; 2318 } 2319 } /* No barrier here */ 2320 2321 end_: 2322 error_: 2323 ; 2324 } /* Implicit barrier here */ 2325 2326 if(res != RES_OK) goto error; 2327 exit: 2328 if(connex_components_initialized) 2329 custom_darray_ptr_component_descriptor_release(&connex_components); 2330 if(s3d_view) S3D(scene_view_ref_put(s3d_view)); 2331 if(triangles_tmp_initialized) darray_triangle_tmp_release(&triangles_tmp); 2332 if(triangles_comp_initialized) darray_triangle_comp_release(&triangles_comp); 2333 if(frontiers_initialized) darray_frontier_edge_release(&frontiers); 2334 if(overlaps_initialized) htable_overlap_release(&overlaps); 2335 if(trgsides) MEM_RM(scn->dev->allocator, trgsides); 2336 2337 return res; 2338 error: 2339 goto exit; 2340 }