ssol_scene.c (16239B)
1 /* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) 2 * Copyright (C) 2016, 2018 CNRS 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 16 17 #include "ssol.h" 18 #include "ssol_atmosphere_c.h" 19 #include "ssol_c.h" 20 #include "ssol_device_c.h" 21 #include "ssol_instance_c.h" 22 #include "ssol_material_c.h" 23 #include "ssol_object_c.h" 24 #include "ssol_scene_c.h" 25 #include "ssol_shape_c.h" 26 #include "ssol_spectrum_c.h" 27 #include "ssol_sun_c.h" 28 29 #include <rsys/double3.h> 30 #include <rsys/float2.h> 31 #include <rsys/float3.h> 32 #include <rsys/list.h> 33 #include <rsys/mem_allocator.h> 34 #include <rsys/rsys.h> 35 36 /******************************************************************************* 37 * Helper functions 38 ******************************************************************************/ 39 static void 40 scene_release(ref_T* ref) 41 { 42 struct ssol_device* dev; 43 struct ssol_scene* scene = CONTAINER_OF(ref, struct ssol_scene, ref); 44 ASSERT(ref); 45 dev = scene->dev; 46 ASSERT(dev && dev->allocator); 47 SSOL(scene_clear(scene)); 48 if(scene->scn_rt) S3D(scene_ref_put(scene->scn_rt)); 49 if(scene->scn_samp) S3D(scene_ref_put(scene->scn_samp)); 50 if(scene->sun) SSOL(sun_ref_put(scene->sun)); 51 if(scene->atmosphere) SSOL(atmosphere_ref_put(scene->atmosphere)); 52 htable_instance_release(&scene->instances_rt); 53 htable_instance_release(&scene->instances_samp); 54 MEM_RM(dev->allocator, scene); 55 SSOL(device_ref_put(dev)); 56 } 57 58 /******************************************************************************* 59 * Exported ssol_scene functions 60 ******************************************************************************/ 61 res_T 62 ssol_scene_create 63 (struct ssol_device* dev, 64 struct ssol_scene** out_scene) 65 { 66 struct ssol_scene* scene = NULL; 67 res_T res = RES_OK; 68 if(!dev || !out_scene) { 69 return RES_BAD_ARG; 70 } 71 72 scene = (struct ssol_scene*)MEM_CALLOC 73 (dev->allocator, 1, sizeof(struct ssol_scene)); 74 if(!scene) { 75 res = RES_MEM_ERR; 76 goto error; 77 } 78 htable_instance_init(dev->allocator, &scene->instances_rt); 79 htable_instance_init(dev->allocator, &scene->instances_samp); 80 SSOL(device_ref_get(dev)); 81 scene->dev = dev; 82 ref_init(&scene->ref); 83 84 res = s3d_scene_create(dev->s3d, &scene->scn_rt); 85 if(res != RES_OK) goto error; 86 res = s3d_scene_create(dev->s3d, &scene->scn_samp); 87 if(res != RES_OK) goto error; 88 89 /* default air medium */ 90 ssol_medium_copy(&scene->air, &SSOL_MEDIUM_VACUUM); 91 92 exit: 93 if(out_scene) *out_scene = scene; 94 return res; 95 error: 96 if(scene) { 97 SSOL(scene_ref_put(scene)); 98 scene = NULL; 99 } 100 goto exit; 101 } 102 103 res_T 104 ssol_scene_ref_get(struct ssol_scene* scene) 105 { 106 if(!scene) return RES_BAD_ARG; 107 ref_get(&scene->ref); 108 return RES_OK; 109 } 110 111 res_T 112 ssol_scene_ref_put(struct ssol_scene* scene) 113 { 114 if(!scene) return RES_BAD_ARG; 115 ref_put(&scene->ref, scene_release); 116 return RES_OK; 117 } 118 119 res_T 120 ssol_scene_attach_instance 121 (struct ssol_scene* scene, struct ssol_instance* instance) 122 { 123 enum { ATTACH_S3D, SET_INSTANCE_RT }; 124 unsigned id; 125 struct ssol_instance** pinst; 126 int mask = 0; 127 res_T res; 128 129 if(!scene || !instance) return RES_BAD_ARG; 130 131 /* Attach the instantiated s3d shape to ray-trace to the RT scene */ 132 res = s3d_scene_attach_shape(scene->scn_rt, instance->shape_rt); 133 if(res != RES_OK) goto error; 134 mask |= BIT(ATTACH_S3D); 135 136 /* Register the instance against the scene */ 137 S3D(shape_get_id(instance->shape_rt, &id)); 138 pinst = htable_instance_find(&scene->instances_rt, &id); 139 if(pinst) { 140 /* already attached */ 141 ASSERT(*pinst == instance); /* cannot be attached to another instance! */ 142 goto exit; 143 } 144 145 res = htable_instance_set(&scene->instances_rt, &id, &instance); 146 if(res != RES_OK) goto error; 147 mask |= BIT(SET_INSTANCE_RT); 148 149 SSOL(instance_ref_get(instance)); 150 151 exit: 152 return res; 153 error: 154 if(mask & BIT(ATTACH_S3D)) { 155 S3D(scene_detach_shape(scene->scn_rt, instance->shape_rt)); 156 } 157 if(mask & BIT(SET_INSTANCE_RT)) { 158 const size_t n = htable_instance_erase(&scene->instances_rt, &id); 159 ASSERT(n == 1); (void)n; 160 } 161 goto exit; 162 } 163 164 res_T 165 ssol_scene_detach_instance 166 (struct ssol_scene* scene, 167 struct ssol_instance* instance) 168 { 169 struct ssol_instance** pinst; 170 struct ssol_instance* inst; 171 unsigned id; 172 size_t n; 173 (void)n, (void)inst; 174 175 if(!scene || !instance) return RES_BAD_ARG; 176 177 /* Retrieve the object instance identifier */ 178 S3D(shape_get_id(instance->shape_rt, &id)); 179 180 /* Check that the instance is effectively registered into the scene */ 181 pinst = htable_instance_find(&scene->instances_rt, &id); 182 if(!pinst) return RES_BAD_ARG; 183 inst = *pinst; 184 ASSERT(inst == instance); 185 186 /* Detach the object instance */ 187 n = htable_instance_erase(&scene->instances_rt, &id); 188 ASSERT(n == 1); 189 S3D(scene_detach_shape(scene->scn_rt, instance->shape_rt)); 190 SSOL(instance_ref_put(instance)); 191 192 return RES_OK; 193 } 194 195 res_T 196 ssol_scene_compute_aabb 197 (const struct ssol_scene* scene, float lower[3], float upper[3]) 198 { 199 struct s3d_scene_view* view = NULL; 200 res_T res = RES_OK; 201 202 if(!scene || !lower || !upper) { 203 res = RES_BAD_ARG; 204 goto error; 205 } 206 207 res = s3d_scene_view_create(scene->scn_rt, S3D_GET_PRIMITIVE, &view); 208 if(res != RES_OK) goto error; 209 res = s3d_scene_view_get_aabb(view, lower, upper); 210 if(res != RES_OK) goto error; 211 212 exit: 213 if(view) S3D(scene_view_ref_put(view)); 214 return res; 215 error: 216 goto exit; 217 } 218 219 res_T 220 ssol_scene_clear(struct ssol_scene* scene) 221 { 222 struct htable_instance_iterator it, it_end; 223 if(!scene) return RES_BAD_ARG; 224 225 htable_instance_begin(&scene->instances_rt, &it); 226 htable_instance_end(&scene->instances_rt, &it_end); 227 while(!htable_instance_iterator_eq(&it, &it_end)) { 228 struct ssol_instance* inst; 229 inst = *htable_instance_iterator_data_get(&it); 230 S3D(scene_detach_shape(scene->scn_rt, inst->shape_rt)); 231 SSOL(instance_ref_put(inst)); 232 htable_instance_iterator_next(&it); 233 } 234 htable_instance_clear(&scene->instances_rt); 235 htable_instance_clear(&scene->instances_samp); 236 S3D(scene_clear(scene->scn_rt)); 237 S3D(scene_clear(scene->scn_samp)); 238 ssol_medium_clear(&scene->air); 239 if(scene->sun) SSOL(scene_detach_sun(scene, scene->sun)); 240 if(scene->atmosphere) SSOL(scene_detach_atmosphere(scene, scene->atmosphere)); 241 return RES_OK; 242 } 243 244 res_T 245 ssol_scene_attach_sun(struct ssol_scene* scene, struct ssol_sun* sun) 246 { 247 if(!scene || ! sun) 248 return RES_BAD_ARG; 249 if(sun->scene_attachment || scene->sun) { 250 /* Already attached: must be linked together */ 251 if(sun->scene_attachment != scene || scene->sun != sun) { 252 return RES_BAD_ARG; /* If not detach first! */ 253 } else { 254 return RES_OK; /* Nothing to change */ 255 } 256 } 257 /* no previous attachment */ 258 SSOL(sun_ref_get(sun)); 259 scene->sun = sun; 260 sun->scene_attachment = scene; 261 return RES_OK; 262 } 263 264 res_T 265 ssol_scene_detach_sun(struct ssol_scene* scene, struct ssol_sun* sun) 266 { 267 if(!scene || !sun || !scene->sun || sun->scene_attachment != scene) 268 return RES_BAD_ARG; 269 270 ASSERT(sun == scene->sun); 271 sun->scene_attachment = NULL; 272 scene->sun = NULL; 273 SSOL(sun_ref_put(sun)); 274 return RES_OK; 275 } 276 277 278 res_T 279 ssol_scene_attach_atmosphere(struct ssol_scene* scene, struct ssol_atmosphere* atm) 280 { 281 if(!scene || !atm) 282 return RES_BAD_ARG; 283 if(atm->scene_attachment || scene->atmosphere) { 284 /* already attached: must be linked together */ 285 if(atm->scene_attachment != scene || scene->atmosphere != atm) { 286 /* if not detach first! */ 287 return RES_BAD_ARG; 288 } else { 289 /* nothing to change */ 290 return RES_OK; 291 } 292 } 293 /* no previous attachment */ 294 SSOL(atmosphere_ref_get(atm)); 295 scene->atmosphere = atm; 296 atm->scene_attachment = scene; 297 return RES_OK; 298 } 299 300 res_T 301 ssol_scene_detach_atmosphere(struct ssol_scene* scene, struct ssol_atmosphere* atm) 302 { 303 if(!scene || !atm || !scene->atmosphere || atm->scene_attachment != scene) 304 return RES_BAD_ARG; 305 306 ASSERT(atm == scene->atmosphere); 307 atm->scene_attachment = NULL; 308 scene->atmosphere = NULL; 309 SSOL(atmosphere_ref_put(atm)); 310 return RES_OK; 311 } 312 313 res_T 314 ssol_scene_for_each_instance 315 (struct ssol_scene* scn, 316 res_T (*func)(struct ssol_instance* instance, void* ctx), 317 void* ctx) 318 { 319 struct htable_instance_iterator it, end; 320 res_T res = RES_OK; 321 322 if(!scn || !func) { 323 res = RES_BAD_ARG; 324 goto error; 325 } 326 327 htable_instance_begin(&scn->instances_rt, &it); 328 htable_instance_end(&scn->instances_rt, &end); 329 while(!htable_instance_iterator_eq(&it, &end)) { 330 struct ssol_instance* inst = *htable_instance_iterator_data_get(&it); 331 htable_instance_iterator_next(&it); 332 333 res = func(inst, ctx); 334 if(res != RES_OK) goto error; 335 } 336 337 exit: 338 return res; 339 error: 340 goto exit; 341 } 342 343 /******************************************************************************* 344 * Local functions 345 ******************************************************************************/ 346 res_T 347 scene_create_s3d_views 348 (struct ssol_scene* scn, 349 struct s3d_scene_view** out_view_rt, 350 struct s3d_scene_view** out_view_samp) 351 { 352 struct htable_instance_iterator it, end; 353 struct s3d_scene_view* view_rt = NULL; 354 struct s3d_scene_view* view_samp = NULL; 355 double sampled_area = 0; 356 double sampled_area_proxy = 0; 357 int has_sampled = 0; 358 int has_receiver = 0; 359 res_T res = RES_OK; 360 ASSERT(scn && out_view_rt && out_view_samp); 361 362 S3D(scene_clear(scn->scn_samp)); 363 htable_instance_clear(&scn->instances_samp); 364 365 htable_instance_begin(&scn->instances_rt, &it); 366 htable_instance_end(&scn->instances_rt, &end); 367 368 while(!htable_instance_iterator_eq(&it, &end)) { 369 struct ssol_instance* inst = *htable_instance_iterator_data_get(&it); 370 unsigned id; 371 htable_instance_iterator_next(&it); 372 373 if(inst->receiver_mask) { 374 has_receiver = 1; 375 } 376 377 if(!inst->sample) continue; 378 379 sampled_area += inst->shape_rt_area; 380 sampled_area_proxy += inst->shape_samp_area; 381 382 /* Note that geometries with virtual material can be sampled without risk 383 * since the solver avoid to shade them and simply pursue the primary ray */ 384 has_sampled = 1; 385 386 /* Attach the instantiated s3d sampling shape to the s3d sampling scene */ 387 res = s3d_scene_attach_shape(scn->scn_samp, inst->shape_samp); 388 if(res != RES_OK) goto error; 389 390 /* Register the instantiated s3d sampling shape */ 391 S3D(shape_get_id(inst->shape_samp, &id)); 392 ASSERT(!htable_instance_find(&scn->instances_samp, &id)); 393 res = htable_instance_set(&scn->instances_samp, &id, &inst); 394 if(res != RES_OK) goto error; 395 396 /* Do not get a reference onto the instance since it was already referenced 397 * by the scene on its attachment */ 398 } 399 400 if(!has_sampled) { 401 log_error(scn->dev, "No solstice instance to sample.\n"); 402 res = RES_BAD_ARG; 403 goto error; 404 } 405 406 if(!has_receiver) { 407 log_warning(scn->dev, "No receiver is defined.\n"); 408 } 409 410 res = s3d_scene_view_create(scn->scn_rt, S3D_TRACE, &view_rt); 411 if(res != RES_OK) goto error; 412 res = s3d_scene_view_create(scn->scn_samp, S3D_SAMPLE, &view_samp); 413 if(res != RES_OK) goto error; 414 415 exit: 416 *out_view_rt = view_rt; 417 *out_view_samp = view_samp; 418 scn->sampled_area = sampled_area; 419 scn->sampled_area_proxy = sampled_area_proxy; 420 return res; 421 error: 422 S3D(scene_clear(scn->scn_samp)); 423 htable_instance_clear(&scn->instances_samp); 424 if(view_rt) { 425 S3D(scene_view_ref_put(view_rt)); 426 view_rt = NULL; 427 } 428 if(view_samp) { 429 S3D(scene_view_ref_put(view_samp)); 430 view_samp = NULL; 431 } 432 goto exit; 433 } 434 435 /******************************************************************************* 436 * Local miscellaneous functions 437 ******************************************************************************/ 438 int 439 hit_filter_function 440 (const struct s3d_hit* hit, 441 const float orgf[3], 442 const float dirf[3], 443 const float rangef[2], 444 void* ray_data, 445 void* filter_data) 446 { 447 struct ssol_instance* inst; 448 struct ssol_material* mtl; 449 struct ray_data* rdata = ray_data; 450 const struct shaded_shape* sshape; 451 enum ssol_side_flag hit_side = SSOL_INVALID_SIDE; 452 double org[3], dir[3], N[3], dst = FLT_MAX; 453 size_t id; 454 (void)filter_data, (void)rangef; 455 ASSERT(hit && orgf && dirf); 456 457 /* No ray data => nothing to filter */ 458 if(!ray_data) return 0; 459 /* Handle numerical imprecision */ 460 if(hit->distance < rangef[0]) return 1; 461 462 /* When searching for closest_point accept any point on the expected object */ 463 if(rdata->point_init_closest_point) { 464 int reject = (rdata->prim_from.geom_id != hit->prim.geom_id) 465 || (rdata->prim_from.inst_id != hit->prim.inst_id); 466 return reject; 467 } 468 469 /* Retrieve the intersected instance and shaded shape */ 470 inst = *htable_instance_find(&rdata->scn->instances_rt, &hit->prim.inst_id); 471 id = *htable_shaded_shape_find(&inst->object->shaded_shapes_rt, &hit->prim.geom_id); 472 sshape = darray_shaded_shape_cdata_get(&inst->object->shaded_shapes)+id; 473 474 /* Discard self intersection */ 475 switch(sshape->shape->type) { 476 case SHAPE_MESH: 477 if(hit->distance <= 1.e-6 /* FIXME hack */ 478 || hit->distance <= rangef[0] 479 || S3D_PRIMITIVE_EQ(&hit->prim, &rdata->prim_from)) { 480 /* Discard self intersection for mesh, i.e. when the intersected 481 * primitive is the primitive from which the ray starts */ 482 return 1; 483 } 484 /* No self intersection. Define which side of the primitive is hit. 485 * Note that incoming direction points inward the primitive */ 486 hit_side = f3_dot(hit->normal, dirf) < 0 ? SSOL_FRONT : SSOL_BACK; 487 break; 488 case SHAPE_PUNCHED: 489 /* Project the hit position into the punched shape */ 490 d3_set_f3(dir, dirf); 491 d3_set_f3(org, orgf); 492 dst = shape_trace_ray(sshape->shape, inst->transform, org, dir, 493 hit->distance, N, punched_shape_intersect_local); 494 if(dst >= FLT_MAX) { 495 /* No projection found => the ray does not intersect the quadric */ 496 return 1; 497 } 498 if((float)dst <= rangef[0]) { 499 /* Handle RT numerical imprecision, the hit is below the lower bound 500 * of the ray range. */ 501 return 1; 502 } 503 hit_side = d3_dot(dir, N) < 0 ? SSOL_FRONT : SSOL_BACK; 504 if(inst == rdata->inst_from 505 && sshape == rdata->sshape_from 506 && hit_side != rdata->side_from) { 507 /* The intersected instance is the one from which the ray starts, 508 * ensure that the ray does not intersect the opposite side of the 509 * quadric 510 * 511 * Note that reversed_ray is intentionally not considered here! */ 512 return 1; 513 } 514 break; 515 default: FATAL("Unreachable code.\n"); break; 516 } 517 ASSERT(hit_side == SSOL_BACK || hit_side == SSOL_FRONT); 518 if(rdata->reversed_ray) { 519 hit_side = (hit_side == SSOL_FRONT) ? SSOL_BACK : SSOL_FRONT; 520 } 521 mtl = hit_side == SSOL_FRONT ? sshape->mtl_front : sshape->mtl_back; 522 if(mtl->type == SSOL_MATERIAL_VIRTUAL) { 523 /* Discard all virtual materials */ 524 if(rdata->discard_virtual_materials) return 1; 525 /* Discard virtual material that are not receivers */ 526 if((inst->receiver_mask & (int)hit_side) == 0) return 1; 527 } 528 529 /* Save the nearest intersected quadric point */ 530 if(sshape->shape->type != SHAPE_MESH && rdata->dst >= dst) { 531 d3_set(rdata->N, N); 532 rdata->dst = dst; 533 } 534 535 return 0; 536 } 537 538 res_T 539 scene_check(const struct ssol_scene* scene, const char* caller) 540 { 541 ASSERT(scene && caller); 542 543 if(!scene->sun) { 544 log_error(scene->dev, "%s: no sun attached.\n", caller); 545 return RES_BAD_ARG; 546 } 547 548 if(!scene->sun->spectrum) { 549 log_error(scene->dev, "%s: sun's spectrum undefined.\n", caller); 550 return RES_BAD_ARG; 551 } 552 553 if(scene->sun->dni <= 0) { 554 log_error(scene->dev, "%s: sun's DNI undefined.\n", caller); 555 return RES_BAD_ARG; 556 } 557 return RES_OK; 558 } 559