sphor_interface.c (20364B)
1 /* Copyright (C) 2024-2026 Centre National de la Recherche Scientifique 2 * Copyright (C) 2024-2026 Clermont Auvergne INP 3 * Copyright (C) 2024-2026 INSA Lyon 4 * Copyright (C) 2024-2026 Institut Mines Télécom Albi-Carmaux 5 * Copyright (C) 2024-2026 Institut National Polytechnique de Toulouse 6 * Copyright (C) 2024-2026 |Méso|Star> (contact@meso-star.com) 7 * Copyright (C) 2024-2026 PhotonLyX (info@photonlyx.com) 8 * Copyright (C) 2024-2026 Université de Lorraine 9 * Copyright (C) 2024-2026 Université Paul Sabatier 10 * Copyright (C) 2024-2026 Université Toulouse - Jean Jaurès 11 * 12 * This program is free software: you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License as published by 14 * the Free Software Foundation, either version 3 of the License, or 15 * (at your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License for more details. 21 * 22 * You should have received a copy of the GNU General Public License 23 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 24 25 #include "sphor_interface.h" 26 #include "sphor_sources.h" 27 #include "sphor_c.h" 28 29 #include <rsys/double3.h> 30 #include <rsys/double2.h> 31 #include <rsys/float3.h> 32 #include <rsys/float2.h> 33 #include <rsys/rsys.h> 34 #include <star/s3d.h> 35 #include <star/sphin.h> 36 #include <star/ssf.h> 37 38 struct sphin_brdf; 39 struct sphin_sensor_surface; 40 struct sphin_source_surface; 41 42 /******************************************************************************* 43 * Local functions 44 ******************************************************************************/ 45 46 /******************************************************************************* 47 * Interface surface getters 48 ******************************************************************************/ 49 res_T 50 primitive_get_brdf 51 (struct sphor* sphor, 52 const struct primitive* primitive, 53 struct sphin_brdf** out_brdf) 54 { 55 char* surface_name = NULL; 56 size_t i = 0; 57 const struct interface* interface = NULL; 58 struct sphin_brdf* brdf = NULL; 59 struct sphin_brdf* found_brdf = NULL; 60 struct sphin_surface* surface = NULL; 61 res_T res = RES_OK; 62 63 ASSERT(NULL != sphor); 64 ASSERT(NULL != primitive); 65 ASSERT(NULL != out_brdf); 66 67 interface = primitive->interface; 68 69 FOR_EACH(i, 0, (size_t)interface->surface_count[(size_t)primitive->side]) { 70 res = sphin_config_get_surface 71 (sphor->config, 72 interface->surfaces[(size_t)primitive->side][i], 73 &surface); 74 if (RES_OK != res) { goto error; } 75 76 res = sphin_surface_get_brdf(surface, &brdf); 77 if (RES_OK != res) { goto error; } 78 if (NULL != brdf){ 79 if (NULL == found_brdf) { 80 sphin_surface_get_name(surface, &surface_name); 81 found_brdf = brdf; /* Keep first non-NULL */ 82 } else { 83 /* Found second non-NULL BRDF - this is an error/conflict */ 84 char* last_surface_name = NULL; 85 sphin_surface_get_name(surface, &last_surface_name); 86 ERROR(sphor, 87 "Multiple BRDFs defined for same interface side. " 88 "Conflicting surfaces: '%s' and '%s'.\n", 89 surface_name, last_surface_name); 90 res = RES_BAD_ARG; 91 goto error; 92 } 93 } 94 } 95 96 exit: 97 *out_brdf = brdf; 98 return res; 99 error: 100 found_brdf = NULL; 101 goto exit; 102 } 103 104 res_T 105 primitive_get_btdf 106 (struct sphor* sphor, 107 const struct primitive* primitive, 108 struct sphin_btdf** out_btdf) 109 { 110 char* surface_name = NULL; 111 size_t i = 0; 112 const struct interface* interface = NULL; 113 struct sphin_btdf* btdf = NULL; 114 struct sphin_btdf* found_btdf = NULL; 115 struct sphin_surface* surface = NULL; 116 res_T res = RES_OK; 117 118 ASSERT(NULL != sphor); 119 ASSERT(NULL != primitive); 120 ASSERT(NULL != out_btdf); 121 122 interface = primitive->interface; 123 124 FOR_EACH(i, 0, (size_t)interface->surface_count[(size_t)primitive->side]) { 125 res = sphin_config_get_surface 126 (sphor->config, 127 interface->surfaces[(size_t)primitive->side][i], 128 &surface); 129 if (RES_OK != res) { goto error; } 130 131 res = sphin_surface_get_btdf(surface, &btdf); 132 if (RES_OK != res) { goto error; } 133 if (NULL != btdf){ 134 if (NULL == found_btdf) { 135 sphin_surface_get_name(surface, &surface_name); 136 found_btdf = btdf; /* Keep first non-NULL */ 137 } else { 138 /* Found second non-NULL BTDF - this is an error/conflict */ 139 char* last_surface_name = NULL; 140 sphin_surface_get_name(surface, &last_surface_name); 141 ERROR(sphor, 142 "Multiple BTDFs defined for same interface side. " 143 "Conflicting surfaces: '%s' and '%s'.\n", 144 surface_name, last_surface_name); 145 res = RES_BAD_ARG; 146 goto error; 147 } 148 } 149 } 150 151 exit: 152 *out_btdf = btdf; 153 return res; 154 error: 155 found_btdf = NULL; 156 goto exit; 157 } 158 159 res_T 160 primitive_get_source 161 (struct sphor* sphor, 162 const struct primitive* primitive, 163 struct sphin_source_surface** out_source) 164 { 165 char* surface_name = NULL; 166 size_t i = 0; 167 const struct interface* interface = NULL; 168 struct sphin_source_surface* source = NULL; 169 struct sphin_source_surface* found_source = NULL; 170 struct sphin_surface* surface = NULL; 171 res_T res = RES_OK; 172 173 ASSERT(NULL != sphor); 174 ASSERT(NULL != primitive); 175 ASSERT(NULL != out_source); 176 177 interface = primitive->interface; 178 179 FOR_EACH(i, 0, (size_t)interface->surface_count[(size_t)primitive->side]) { 180 res = sphin_config_get_surface 181 (sphor->config, 182 interface->surfaces[(size_t)primitive->side][i], 183 &surface); 184 if (RES_OK != res) { goto error; } 185 res = sphin_surface_get_source(surface, &source); 186 if (RES_OK != res) { goto error; } 187 if (NULL != source){ 188 if (NULL == found_source) { 189 sphin_surface_get_name(surface, &surface_name); 190 found_source = source; /* Keep first non-NULL */ 191 } else { 192 /* Found second non-NULL sensor - this is an error/conflict */ 193 char* last_surface_name = NULL; 194 sphin_surface_get_name(surface, &last_surface_name); 195 ERROR(sphor, 196 "Multiple surface sources defined for same interface side. " 197 "Conflicting surfaces: '%s' and '%s'.\n", 198 surface_name, last_surface_name); 199 res = RES_BAD_ARG; 200 goto error; 201 } 202 } 203 } 204 205 exit: 206 *out_source = source; 207 return res; 208 error: 209 found_source = NULL; 210 goto exit; 211 } 212 213 res_T 214 primitive_get_sensor_surface 215 (struct sphor* sphor, 216 const struct primitive* primitive, 217 struct sphin_sensor_surface** out_sensor) 218 { 219 char* surface_name = NULL; 220 size_t i = 0; 221 const struct interface* interface = NULL; 222 struct sphin_sensor_surface* sensor = NULL; 223 struct sphin_sensor_surface* found_sensor = NULL; 224 struct sphin_surface* surface = NULL; 225 res_T res = RES_OK; 226 227 ASSERT(NULL != sphor); 228 ASSERT(NULL != primitive); 229 ASSERT(NULL != out_sensor); 230 231 interface = primitive->interface; 232 233 FOR_EACH(i, 0, (size_t)interface->surface_count[(size_t)primitive->side]) { 234 res = sphin_config_get_surface 235 (sphor->config, 236 interface->surfaces[(size_t)primitive->side][i], 237 &surface); 238 if (RES_OK != res) { goto error; } 239 res = sphin_surface_get_sensor(surface, &sensor); 240 if (RES_OK != res) { goto error; } 241 if (NULL != sensor){ 242 if (NULL == found_sensor) { 243 sphin_surface_get_name(surface, &surface_name); 244 found_sensor = sensor; /* Keep first non-NULL */ 245 } else { 246 /* Found second non-NULL sensor - this is an error/conflict */ 247 char* last_surface_name = NULL; 248 sphin_surface_get_name(surface, &last_surface_name); 249 ERROR(sphor, 250 "Multiple sensors defined for same interface side. " 251 "Conflicting surfaces: '%s' and '%s'.\n", 252 surface_name, last_surface_name); 253 res = RES_BAD_ARG; 254 goto error; 255 } 256 } 257 } 258 259 exit: 260 *out_sensor = sensor; 261 return res; 262 error: 263 found_sensor = NULL; 264 goto exit; 265 } 266 267 /******************************************************************************* 268 * Interface volume getters 269 ******************************************************************************/ 270 res_T 271 primitive_get_in_refraction_index 272 (struct sphor* sphor, 273 struct primitive* primitive, 274 double wavelength, 275 double* n_real, 276 double* n_imag) 277 { 278 struct sphin_volume* volume = NULL; 279 struct sphin_refractive_index* refr_ind = NULL; 280 struct sphin_spectral_property* spec_prop = NULL; 281 struct sphin_spectral_property_descriptor spec_prop_desc 282 = SPHIN_SPECTRAL_PROPERTY_DESCRIPTOR_NULL; 283 res_T res = RES_OK; 284 285 ASSERT(NULL != sphor); 286 ASSERT(NULL != primitive); 287 ASSERT(NULL != n_real); 288 ASSERT(NULL != n_imag); 289 290 /* If the interface side doesnt has a volume defined, the refractive index 291 * receives the default value of 1 to the real part and 0 to the imaginary 292 * part */ 293 if (INVALID_ID == primitive->interface->volumes[primitive->side]) { 294 *n_real = 1; 295 *n_imag = 0; 296 goto exit; 297 } 298 299 res = sphin_config_get_volume 300 (sphor->config, primitive->interface->volumes[primitive->side], &volume); 301 if (RES_OK != res) { goto error; } 302 303 res = sphin_volume_get_refractive_index(volume, &refr_ind); 304 if (RES_OK != res) { goto error; } 305 306 /* If the volume is null, the refractive index 307 * receives the default value of 1 to the real part and 0 to the imaginary 308 * part */ 309 if (NULL == refr_ind) { 310 *n_real = 1; 311 *n_imag = 0; 312 goto exit; 313 } 314 315 /* Real part of the refractive index */ 316 res = sphin_refractive_index_get_n_real(refr_ind, &spec_prop); 317 if (RES_OK != res) { goto error; } 318 319 if (NULL != spec_prop) { 320 res = sphin_spectral_property_get_desc(spec_prop, &spec_prop_desc); 321 if (RES_OK != res) { goto error; } 322 323 res = sphin_spectral_property_interpolate_at_wavelength 324 (spec_prop, wavelength, SPHIN_INTERPOLATION_LINEAR, n_real); 325 if (RES_OK != res) { goto error; } 326 } 327 else { *n_real = 1; } 328 329 /* Imag part of the refractive index */ 330 res = sphin_refractive_index_get_n_imag(refr_ind, &spec_prop); 331 if (RES_OK != res) { goto error; } 332 333 if (NULL != spec_prop) { 334 res = sphin_spectral_property_get_desc(spec_prop, &spec_prop_desc); 335 if (RES_OK != res) { goto error; } 336 337 res = sphin_spectral_property_interpolate_at_wavelength 338 (spec_prop, wavelength, SPHIN_INTERPOLATION_LINEAR, n_imag); 339 if (RES_OK != res) { goto error; } 340 } 341 else { *n_imag = 0; } 342 exit: 343 return res; 344 error: 345 goto exit; 346 } 347 348 res_T 349 primitive_get_out_refraction_index 350 (struct sphor* sphor, 351 struct primitive* primitive, 352 double wavelength, 353 double* n_real, 354 double* n_imag) 355 { 356 /* out_primitive is the primitive corresponding to the other side of the 357 * primitive in the functions' args */ 358 struct primitive out_primitive = PRIMITIVE_NULL; 359 res_T res = RES_OK; 360 361 ASSERT(NULL != sphor); 362 ASSERT(NULL != primitive); 363 ASSERT(NULL != n_real); 364 ASSERT(NULL != n_imag); 365 366 /* The primitive corresponding to the out side contains the exact same 367 * interface, but other side_id */ 368 out_primitive.interface = primitive->interface; 369 out_primitive.side = primitive->side^SPHIN_SIDE_FRONT; 370 371 res = primitive_get_in_refraction_index 372 (sphor, &out_primitive, wavelength, n_real, n_imag); 373 return res; 374 } 375 376 res_T 377 primitive_get_reflectivity 378 (struct sphor* sphor, 379 struct primitive* primitive, 380 double wavelength, 381 double cos_theta, 382 double* out_reflectivity) 383 { 384 double eta_i = 0, k_i = 0; 385 double eta_t = 0, k_t = 0; 386 double refr_wl = 0; 387 double reflectivity = 0; 388 enum sphin_brdf_reflectivity_type reflectivity_type = 389 SPHIN_BRDF_REFLECTIVITY_NONE__; 390 struct sphin_brdf* brdf = NULL; 391 struct sphin_spectral_property* reflectivity_spectrum = NULL; 392 struct ssf_fresnel* fresnel = NULL; 393 res_T res = RES_OK; 394 395 ASSERT(NULL != sphor); 396 ASSERT(NULL != primitive); 397 ASSERT(NULL != out_reflectivity); 398 399 res = primitive_get_brdf(sphor, primitive, &brdf); 400 if (RES_OK != res) { goto error; } 401 402 if (NULL == brdf) { 403 res = RES_BAD_ARG; 404 goto error; 405 } 406 407 res = sphin_brdf_get_reflectivity_type(brdf, &reflectivity_type); 408 if (RES_OK != res) { goto error; } 409 410 switch (reflectivity_type) { 411 case SPHIN_BRDF_REFLECTIVITY_TABULATED: 412 413 res = sphin_brdf_get_reflectivity_value(brdf, &reflectivity_spectrum); 414 if (RES_OK != res) { goto error; } 415 416 res = sphin_spectral_property_interpolate_at_wavelength 417 (reflectivity_spectrum, wavelength, 418 SPHIN_INTERPOLATION_LINEAR, &refr_wl); 419 if (RES_OK != res) { goto error; } 420 421 res = ssf_fresnel_create 422 (sphor->allocator, &ssf_fresnel_constant, &fresnel); 423 if (RES_OK != res) { goto error; } 424 425 res = ssf_fresnel_constant_setup(fresnel, refr_wl); 426 if (RES_OK != res) { goto error; } 427 428 break; 429 430 case SPHIN_BRDF_REFLECTIVITY_FRESNEL_DIELECTRIC: 431 432 /* Get refractive index of the medium in the incident side */ 433 res = primitive_get_in_refraction_index 434 (sphor, primitive, wavelength, &eta_i, &k_i); 435 if (RES_OK != res) { goto error; } 436 437 /* Get refractive index of the medium in the transmitted side */ 438 res = primitive_get_out_refraction_index 439 (sphor, primitive, wavelength, &eta_t, &k_t); 440 if (RES_OK != res) { goto error; } 441 442 res = ssf_fresnel_create 443 (sphor->allocator, &ssf_fresnel_dielectric_dielectric, &fresnel); 444 if (RES_OK != res) { goto error; } 445 446 res = ssf_fresnel_dielectric_dielectric_setup(fresnel, eta_i, eta_t); 447 if (RES_OK != res) { goto error; } 448 449 break; 450 451 case SPHIN_BRDF_REFLECTIVITY_FRESNEL_DIELECTRIC_CONDUCTOR: 452 453 /* Get refractive index of the medium in the incident side */ 454 res = primitive_get_in_refraction_index 455 (sphor, primitive, wavelength, &eta_i, &k_i); 456 if (RES_OK != res) { goto error; } 457 458 /* Get refractive index of the medium in the transmitted side */ 459 res = primitive_get_out_refraction_index 460 (sphor, primitive, wavelength, &eta_t, &k_t); 461 if (RES_OK != res) { goto error; } 462 463 res = ssf_fresnel_create 464 (sphor->allocator, &ssf_fresnel_dielectric_conductor, &fresnel); 465 if (RES_OK != res) { goto error; } 466 467 res = ssf_fresnel_dielectric_conductor_setup(fresnel, eta_i, eta_t, k_t); 468 if (RES_OK != res) { goto error; } 469 470 break; 471 472 case SPHIN_BRDF_REFLECTIVITY_NONE__: 473 res = RES_BAD_ARG; 474 goto error; 475 476 default: 477 FATAL("Unreachable code\n"); 478 break; 479 } 480 481 reflectivity = ssf_fresnel_eval(fresnel, cos_theta); 482 if (reflectivity > 1 || reflectivity < 0) { 483 res = RES_BAD_ARG; 484 goto error; 485 } 486 487 exit: 488 *out_reflectivity = reflectivity; 489 if (NULL != fresnel) { 490 ssf_fresnel_ref_put(fresnel); 491 } 492 return res; 493 error: 494 goto exit; 495 } 496 497 res_T 498 primitive_get_transmissivity 499 (struct sphor* sphor, 500 struct primitive* primitive, 501 double wavelength, 502 double cos_theta, 503 double* out_transmissivity) 504 { 505 double eta_i = 0, k_i = 0; 506 double eta_t = 0, k_t = 0; 507 double transm_wl = 0; 508 double transmissivity = 0; 509 enum sphin_btdf_transmissivity_type transmissivity_type = 510 SPHIN_BTDF_TRANSMISSIVITY_NONE__; 511 struct sphin_btdf* btdf = NULL; 512 struct sphin_spectral_property* transmissivity_spectrum = NULL; 513 struct ssf_fresnel* fresnel = NULL; 514 res_T res = RES_OK; 515 516 ASSERT(NULL != sphor); 517 ASSERT(NULL != primitive); 518 ASSERT(NULL != out_transmissivity); 519 520 res = primitive_get_btdf(sphor, primitive, &btdf); 521 if (RES_OK != res) { goto error; } 522 523 if (NULL == btdf) { 524 res = RES_BAD_ARG; 525 goto error; 526 } 527 528 res = sphin_btdf_get_transmissivity_type(btdf, &transmissivity_type); 529 if (RES_OK != res) { goto error; } 530 531 switch (transmissivity_type) { 532 case SPHIN_BTDF_TRANSMISSIVITY_TABULATED: 533 534 res = sphin_btdf_get_transmissivity_value(btdf, &transmissivity_spectrum); 535 if (RES_OK != res) { goto error; } 536 537 res = sphin_spectral_property_interpolate_at_wavelength 538 (transmissivity_spectrum, wavelength, 539 SPHIN_INTERPOLATION_LINEAR, &transm_wl); 540 if (RES_OK != res) { goto error; } 541 542 res = ssf_fresnel_create 543 (sphor->allocator, &ssf_fresnel_constant, &fresnel); 544 if (RES_OK != res) { goto error; } 545 546 res = ssf_fresnel_constant_setup(fresnel, 1. - transm_wl); 547 if (RES_OK != res) { goto error; } 548 549 break; 550 551 case SPHIN_BTDF_TRANSMISSIVITY_FRESNEL_DIELECTRIC: 552 553 /* Get refractive index of the medium in the incident side */ 554 res = primitive_get_in_refraction_index 555 (sphor, primitive, wavelength, &eta_i, &k_i); 556 if (RES_OK != res) { goto error; } 557 558 /* Get refractive index of the medium in the transmitted side */ 559 res = primitive_get_out_refraction_index 560 (sphor, primitive, wavelength, &eta_t, &k_t); 561 if (RES_OK != res) { goto error; } 562 563 res = ssf_fresnel_create 564 (sphor->allocator, &ssf_fresnel_dielectric_dielectric, &fresnel); 565 if (RES_OK != res) { goto error; } 566 567 res = ssf_fresnel_dielectric_dielectric_setup(fresnel, eta_i, eta_t); 568 if (RES_OK != res) { goto error; } 569 570 break; 571 572 case SPHIN_BRDF_REFLECTIVITY_FRESNEL_DIELECTRIC_CONDUCTOR: 573 574 /* Get refractive index of the medium in the incident side */ 575 res = primitive_get_in_refraction_index 576 (sphor, primitive, wavelength, &eta_i, &k_i); 577 if (RES_OK != res) { goto error; } 578 579 /* Get refractive index of the medium in the transmitted side */ 580 res = primitive_get_out_refraction_index 581 (sphor, primitive, wavelength, &eta_t, &k_t); 582 if (RES_OK != res) { goto error; } 583 584 res = ssf_fresnel_create 585 (sphor->allocator, &ssf_fresnel_dielectric_conductor, &fresnel); 586 if (RES_OK != res) { goto error; } 587 588 res = ssf_fresnel_dielectric_conductor_setup(fresnel, eta_i, eta_t, k_t); 589 if (RES_OK != res) { goto error; } 590 591 break; 592 593 case SPHIN_BRDF_REFLECTIVITY_NONE__: 594 res = RES_BAD_ARG; 595 goto error; 596 597 default: 598 FATAL("Unreachable code\n"); 599 break; 600 } 601 602 transmissivity = 1 - ssf_fresnel_eval(fresnel, cos_theta); 603 if (transmissivity > 1 || transmissivity < 0) { 604 res = RES_BAD_ARG; 605 goto error; 606 } 607 608 exit: 609 *out_transmissivity = transmissivity; 610 if (NULL != fresnel) { 611 ssf_fresnel_ref_put(fresnel); 612 } 613 return res; 614 error: 615 goto exit; 616 } 617 618 res_T 619 trace_ray 620 (const struct sphor* sphor, 621 const struct ray* ray, 622 struct intersection* intersection) 623 { 624 /* Position, direction and range when calling s3d functions */ 625 float pos[3] = {0}; /* Position */ 626 float dir[3] = {0}; /* Direction */ 627 float rge[2] = {0}; /* Range */ 628 629 double normal[3] = {0}; 630 enum sphin_side side_id = SPHIN_SIDE_NONE__; 631 size_t prim_id = INVALID_ID; 632 size_t triangle_id; 633 const struct interface* interface = NULL; 634 struct s3d_hit hit = S3D_HIT_NULL; 635 res_T res = RES_OK; 636 637 f3_set_d3(pos, ray->origin); 638 f3_set_d3(dir, ray->direction); 639 f2_set_d2(rge, ray->range); 640 641 prim_id = ray->origin_on_prim.primitive.prim_id; 642 643 /* Trace ray using s3d */ 644 res = s3d_scene_view_trace_ray 645 (sphor->scene_view, pos, dir, rge, &prim_id, &hit); 646 if (RES_OK != res) { goto error; } 647 648 /* Construct sphor structure using sphor */ 649 if (S3D_HIT_NONE(&hit)) { 650 *intersection = INTERSECTION_NULL; 651 goto exit; 652 } 653 654 triangle_id = hit.prim.prim_id; 655 interface = &darray_interface_cdata_get(&sphor->interfaces)[triangle_id]; 656 d3_normalize(normal, d3_set_f3(normal, hit.normal)); 657 658 side_id = 659 (d3_dot(ray->direction, normal) < 0) ? SPHIN_SIDE_BACK : SPHIN_SIDE_FRONT; 660 661 intersection->distance = hit.distance; 662 d2_set_f2(intersection->position.uv, hit.uv); 663 d3_set(intersection->position.primitive.normal, normal); 664 intersection->position.primitive.interface = interface; 665 intersection->position.primitive.prim_id = triangle_id; 666 intersection->position.primitive.side = side_id; 667 668 exit: 669 return res; 670 error: 671 goto exit; 672 } 673 674 res_T 675 ray_update 676 (struct ray* ray, 677 const struct intersection* intersection, 678 const double dir[3]) 679 { 680 double displacement[3] = {0}; 681 res_T res = RES_OK; 682 683 ASSERT(NULL != intersection); 684 ASSERT(NULL != ray); 685 686 ray->origin_on_prim = intersection->position; 687 d3_muld(displacement, ray->direction, intersection->distance); 688 d3_add(ray->origin, ray->origin, displacement); 689 d3_set(ray->direction, dir); 690 return res; 691 }