rngrd_properties.c (14211B)
1 /* Copyright (C) 2022, 2023, 2025 Centre National de la Recherche Scientifique 2 * Copyright (C) 2022, 2023, 2025 Institut Pierre-Simon Laplace 3 * Copyright (C) 2022, 2023, 2025 Institut de Physique du Globe de Paris 4 * Copyright (C) 2022, 2023, 2025 |Méso|Star> (contact@meso-star.com) 5 * Copyright (C) 2022, 2023, 2025 Observatoire de Paris 6 * Copyright (C) 2022, 2023, 2025 Université de Reims Champagne-Ardenne 7 * Copyright (C) 2022, 2023, 2025 Université de Versaille Saint-Quentin 8 * Copyright (C) 2022, 2023, 2025 Université Paul Sabatier 9 * 10 * This program is free software: you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation, either version 3 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 22 23 #include "rngrd.h" 24 #include "rngrd_c.h" 25 #include "rngrd_log.h" 26 27 #include <mrumtl.h> 28 #include <rad-net/rnsl.h> 29 30 #include <star/sbuf.h> 31 #include <star/ssf.h> 32 33 #include <rsys/cstr.h> 34 35 /******************************************************************************* 36 * Helper functions 37 ******************************************************************************/ 38 static INLINE res_T 39 check_create_bsdf_args 40 (const struct rngrd* ground, 41 const struct rngrd_create_bsdf_args* args) 42 { 43 double sum_bcoords; 44 ASSERT(ground); 45 46 if(!args) return RES_BAD_ARG; 47 48 /* Invalid primitive */ 49 if(args->prim.prim_id >= ground->ntriangles) 50 return RES_BAD_ARG; 51 52 /* Invalid barycentric_coords */ 53 sum_bcoords = 54 args->barycentric_coords[0] 55 + args->barycentric_coords[1] 56 + args->barycentric_coords[2]; 57 if(!eq_eps(sum_bcoords, 1, 1.e-6)) 58 return RES_BAD_ARG; 59 60 /* Invalid random number */ 61 if(args->r < 0 || args->r >= 1) 62 return RES_BAD_ARG; 63 64 /* Invalid wavelength */ 65 if(args->wavelength < 0) 66 return RES_BAD_ARG; 67 68 return RES_OK; 69 } 70 71 static INLINE res_T 72 check_get_temperature_args 73 (const struct rngrd* ground, 74 const struct rngrd_get_temperature_args* args) 75 { 76 double sum_bcoords; 77 ASSERT(ground); 78 79 if(!args) return RES_BAD_ARG; 80 81 /* Invalid primitive */ 82 if(args->prim.prim_id >= ground->ntriangles) 83 return RES_BAD_ARG; 84 85 /* Invalid barycentric_coords */ 86 sum_bcoords = 87 args->barycentric_coords[0] 88 + args->barycentric_coords[1] 89 + args->barycentric_coords[2]; 90 if(!eq_eps(sum_bcoords, 1, 1.e-6)) 91 return RES_BAD_ARG; 92 93 return RES_OK; 94 } 95 96 static res_T 97 check_sbuf_desc 98 (const struct rngrd* ground, 99 const struct sbuf_desc* desc, 100 const struct rngrd_create_args* args) 101 { 102 ASSERT(ground && desc && args); 103 104 if(desc->size != ground->ntriangles) { 105 log_err(ground, 106 "%s: no sufficient surface properties regarding the mesh %s\n", 107 args->props_filename, args->smsh_filename); 108 return RES_BAD_ARG; 109 } 110 111 if(desc->szitem != 8 || desc->alitem != 8 || desc->pitch != 8) { 112 log_err(ground, "%s: unexpected layout of properties\n", 113 args->props_filename); 114 return RES_BAD_ARG; 115 } 116 117 return RES_OK; 118 } 119 120 static res_T 121 create_bsdf_diffuse 122 (struct rngrd* ground, 123 const struct mrumtl_brdf_lambertian* lambertian, 124 struct ssf_bsdf** out_bsdf) 125 { 126 struct ssf_bsdf* bsdf = NULL; 127 struct mem_allocator* allocator = NULL; 128 res_T res = RES_OK; 129 ASSERT(ground && lambertian && out_bsdf); 130 131 allocator = ground->allocator; 132 133 res = ssf_bsdf_create(allocator, &ssf_lambertian_reflection, &bsdf); 134 if(res != RES_OK) goto error; 135 res = ssf_lambertian_reflection_setup(bsdf, lambertian->reflectivity); 136 if(res != RES_OK) goto error; 137 138 exit: 139 *out_bsdf = bsdf; 140 return res; 141 error: 142 if(bsdf) { SSF(bsdf_ref_put(bsdf)); bsdf = NULL; } 143 goto exit; 144 } 145 146 static res_T 147 create_bsdf_specular 148 (struct rngrd* ground, 149 const struct mrumtl_brdf_specular* specular, 150 struct ssf_bsdf** out_bsdf) 151 { 152 struct ssf_bsdf* bsdf = NULL; 153 struct ssf_fresnel* fresnel = NULL; 154 struct mem_allocator* allocator = NULL; 155 res_T res = RES_OK; 156 ASSERT(ground && specular && out_bsdf); 157 158 allocator = ground->allocator; 159 160 res = ssf_bsdf_create(allocator, &ssf_specular_reflection, &bsdf); 161 if(res != RES_OK) goto error; 162 res = ssf_fresnel_create(allocator, &ssf_fresnel_constant, &fresnel); 163 if(res != RES_OK) goto error; 164 res = ssf_fresnel_constant_setup(fresnel, specular->reflectivity); 165 if(res != RES_OK) goto error; 166 res = ssf_specular_reflection_setup(bsdf, fresnel); 167 if(res != RES_OK) goto error; 168 169 exit: 170 if(fresnel) SSF(fresnel_ref_put(fresnel)); 171 *out_bsdf = bsdf; 172 return res; 173 error: 174 if(bsdf) { SSF(bsdf_ref_put(bsdf)); bsdf = NULL; } 175 goto exit; 176 } 177 178 static INLINE res_T 179 create_bsdf 180 (struct rngrd* ground, 181 const struct mrumtl_brdf* brdf, 182 struct ssf_bsdf** out_bsdf) 183 { 184 struct mrumtl_brdf_lambertian lambertian; 185 struct mrumtl_brdf_specular specular; 186 struct ssf_bsdf* bsdf = NULL; 187 res_T res = RES_OK; 188 ASSERT(ground && brdf && out_bsdf); 189 190 switch(mrumtl_brdf_get_type(brdf)) { 191 case MRUMTL_BRDF_LAMBERTIAN: 192 MRUMTL(brdf_get_lambertian(brdf, &lambertian)); 193 res = create_bsdf_diffuse(ground, &lambertian, &bsdf); 194 if(res != RES_OK) goto error; 195 break; 196 case MRUMTL_BRDF_SPECULAR: 197 MRUMTL(brdf_get_specular(brdf, &specular)); 198 res = create_bsdf_specular(ground, &specular, &bsdf); 199 if(res != RES_OK) goto error; 200 break; 201 default: FATAL("Unreachable code.\n"); break; 202 } 203 204 exit: 205 *out_bsdf = bsdf; 206 return res; 207 error: 208 if(bsdf) { SSF(bsdf_ref_put(bsdf)); bsdf = NULL; } 209 goto exit; 210 } 211 212 static res_T 213 mtl_create_bsdf_list 214 (struct rngrd* ground, 215 const char* filename, 216 struct mtl* mtl) 217 { 218 size_t nbsdfs = 0; 219 size_t ibsdf = 0; 220 res_T res = RES_OK; 221 ASSERT(ground && filename && mtl); 222 223 /* Reserve memory space for the list of BSDFs */ 224 nbsdfs = mrumtl_get_brdfs_count(mtl->mrumtl); 225 res = darray_bsdf_resize(&mtl->bsdf_lst, nbsdfs); 226 if(res != RES_OK) goto error; 227 228 FOR_EACH(ibsdf, 0, nbsdfs) { 229 const struct mrumtl_brdf* brdf = mrumtl_get_brdf(mtl->mrumtl, ibsdf); 230 struct ssf_bsdf** bsdf = darray_bsdf_data_get(&mtl->bsdf_lst) + ibsdf; 231 232 res = create_bsdf(ground, brdf, bsdf); 233 if(res != RES_OK) goto error; 234 } 235 236 exit: 237 return res; 238 239 error: 240 log_err(ground, "%s: error creating the list of BSDFs -- %s\n", 241 filename, res_to_cstr(res)); 242 darray_bsdf_clear(&mtl->bsdf_lst); 243 goto exit; 244 } 245 246 static res_T 247 load_mtl(struct rngrd* ground, const char* filename, struct mrumtl** out_mtl) 248 { 249 struct mrumtl_create_args args = MRUMTL_CREATE_ARGS_DEFAULT; 250 struct mrumtl* mtl = NULL; 251 res_T res = RES_OK; 252 ASSERT(ground && filename && out_mtl); 253 254 args.verbose = ground->verbose; 255 args.logger = ground->logger; 256 args.allocator = ground->allocator; 257 res = mrumtl_create(&args, &mtl); 258 if(res != RES_OK) { 259 log_err(ground, "%s: could not create the MruMtl data structure\n", filename); 260 goto error; 261 } 262 263 res = mrumtl_load(mtl, filename); 264 if(res != RES_OK) goto error; 265 266 exit: 267 *out_mtl = mtl; 268 return res; 269 error: 270 if(mtl) { MRUMTL(ref_put(mtl)); mtl = NULL; } 271 goto exit; 272 } 273 274 static res_T 275 load_mtllst(struct rngrd* ground, const struct rngrd_create_args* args) 276 { 277 struct rnsl_create_args rnsl_args = RNSL_CREATE_ARGS_DEFAULT; 278 struct rnsl* rnsl = NULL; 279 size_t imtl, nmtls; 280 res_T res = RES_OK; 281 282 /* Create loader of material paths */ 283 rnsl_args.logger = ground->logger; 284 rnsl_args.allocator = ground->allocator; 285 rnsl_args.verbose = ground->verbose; 286 res = rnsl_create(&rnsl_args, &rnsl); 287 if(res != RES_OK) { 288 log_err(ground, "Failed to create loader for material list `%s' -- %s\n", 289 args->mtllst_filename, res_to_cstr(res)); 290 goto error; 291 } 292 293 /* Load the list of material paths */ 294 res = rnsl_load(rnsl, args->mtllst_filename); 295 if(res != RES_OK) goto error; 296 297 /* Reserve memory space for the list of materials */ 298 nmtls = rnsl_get_strings_count(rnsl); 299 res = darray_mtl_resize(&ground->mtls, nmtls); 300 if(res != RES_OK) { 301 log_err(ground, "%s: could not allocate the list of %lu materials -- %s\n", 302 args->mtllst_filename, nmtls, res_to_cstr(res)); 303 goto error; 304 } 305 306 /* Load the list of materials and create their associated BSDFs */ 307 FOR_EACH(imtl, 0, nmtls) { 308 struct mtl* mtl = darray_mtl_data_get(&ground->mtls)+imtl; 309 const char* filename = rnsl_get_string(rnsl, imtl); 310 311 res = load_mtl(ground, filename, &mtl->mrumtl); 312 if(res != RES_OK) goto error; 313 res = mtl_create_bsdf_list(ground, filename, mtl); 314 if(res != RES_OK) goto error; 315 } 316 317 exit: 318 if(rnsl) RNSL(ref_put(rnsl)); 319 return res; 320 error: 321 darray_mtl_clear(&ground->mtls); 322 goto exit; 323 } 324 325 /******************************************************************************* 326 * Exported functions 327 ******************************************************************************/ 328 res_T 329 rngrd_create_bsdf 330 (struct rngrd* ground, 331 const struct rngrd_create_bsdf_args* args, 332 struct ssf_bsdf** out_bsdf) 333 { 334 const struct ALIGN(8) { uint32_t mtl_id; float temperature; }* item = NULL; 335 struct mtl* mtl = NULL; 336 struct ssf_bsdf* bsdf = NULL; 337 size_t ibsdf; 338 struct sbuf_desc desc; 339 res_T res = RES_OK; 340 341 if(!ground || !out_bsdf) { res = RES_BAD_ARG; goto error; } 342 res = check_create_bsdf_args(ground, args); 343 if(res != RES_OK) goto error; 344 345 /* Retrieve the material id of the primitive to consider */ 346 res = sbuf_get_desc(ground->props, &desc); 347 if(res != RES_OK) goto error; 348 item = sbuf_desc_at(&desc, args->prim.prim_id); 349 350 /* Retrieve the spectrally varying material of the primitive */ 351 ASSERT(item->mtl_id < darray_mtl_size_get(&ground->mtls)); 352 mtl = darray_mtl_data_get(&ground->mtls)+item->mtl_id; 353 354 /* Get the BSDF based on the input wavelength */ 355 res = mrumtl_fetch_brdf(mtl->mrumtl, args->wavelength, args->r, &ibsdf); 356 if(res != RES_OK) goto error; 357 bsdf = darray_bsdf_data_get(&mtl->bsdf_lst)[ibsdf]; 358 ASSERT(bsdf); 359 SSF(bsdf_ref_get(bsdf)); 360 361 exit: 362 if(out_bsdf) *out_bsdf = bsdf; 363 return res; 364 365 error: 366 log_err(ground, "%s: error creating the BRDF for the primitive %lu " 367 "at the wavelength %g -- %s\n", 368 FUNC_NAME, (unsigned long)args->prim.prim_id, args->wavelength, 369 res_to_cstr(res)); 370 if(bsdf) { 371 SSF(bsdf_ref_put(bsdf)); 372 bsdf = NULL; 373 } 374 goto exit; 375 } 376 377 res_T 378 rngrd_get_temperature 379 (const struct rngrd* ground, 380 const struct rngrd_get_temperature_args* args, 381 double* temperature) 382 { 383 const struct ALIGN(8) { uint32_t mtl_id; float temperature; }* item = NULL; 384 struct sbuf_desc desc; 385 res_T res = RES_OK; 386 387 if(!ground || !temperature) { res = RES_BAD_ARG; goto error; } 388 res = check_get_temperature_args(ground, args); 389 if(res != RES_OK) goto error; 390 391 /* Retrieve the ground temperature */ 392 res = sbuf_get_desc(ground->props, &desc); 393 if(res != RES_OK) goto error; 394 item = sbuf_desc_at(&desc, args->prim.prim_id); 395 ASSERT(item->temperature >= 0); 396 397 *temperature = item->temperature; 398 399 exit: 400 return res; 401 error: 402 goto exit; 403 } 404 405 res_T 406 rngrd_get_temperature_range 407 (const struct rngrd* ground, 408 double T_range[2]) 409 { 410 struct sbuf_desc desc; 411 size_t i; 412 res_T res = RES_OK; 413 414 if(!ground || !T_range) { res = RES_BAD_ARG; goto error; } 415 416 res = sbuf_get_desc(ground->props, &desc); 417 if(res != RES_OK) goto error; 418 419 T_range[0] = +DBL_MAX; 420 T_range[1] = -DBL_MAX; 421 FOR_EACH(i, 0, desc.size) { 422 const struct ALIGN(8) { uint32_t mtl_id; float temperature; }* item; 423 item = sbuf_desc_at(&desc, i); 424 ASSERT(item->temperature >= 0); 425 426 T_range[0] = MMIN(T_range[0], item->temperature); 427 T_range[1] = MMAX(T_range[1], item->temperature); 428 } 429 430 exit: 431 return res; 432 error: 433 goto exit; 434 } 435 436 /******************************************************************************* 437 * Local function 438 ******************************************************************************/ 439 res_T 440 setup_properties(struct rngrd* ground, const struct rngrd_create_args* args) 441 { 442 struct sbuf_create_args sbuf_args = SBUF_CREATE_ARGS_DEFAULT; 443 struct sbuf_desc sbuf_desc = SBUF_DESC_NULL; 444 res_T res = RES_OK; 445 ASSERT(ground && args); 446 447 res = load_mtllst(ground, args); 448 if(res != RES_OK) goto error; 449 450 /* Create the Star-Buffer loader */ 451 sbuf_args.logger = ground->logger; 452 sbuf_args.allocator = ground->allocator; 453 sbuf_args.verbose = ground->verbose; 454 res = sbuf_create(&sbuf_args, &ground->props); 455 if(res != RES_OK) goto error; 456 457 /* Load and retrieve properties */ 458 res = sbuf_load(ground->props, args->props_filename); 459 if(res != RES_OK) goto error; 460 res = sbuf_get_desc(ground->props, &sbuf_desc); 461 if(res != RES_OK) goto error; 462 res = check_sbuf_desc(ground, &sbuf_desc, args); 463 if(res != RES_OK) goto error; 464 465 exit: 466 return res; 467 error: 468 if(ground->props) { SBUF(ref_put(ground->props)); ground->props = NULL; } 469 darray_mtl_clear(&ground->mtls); 470 goto exit; 471 } 472 473 res_T 474 check_properties(const struct rngrd* ground) 475 { 476 struct sbuf_desc sbuf_desc = SBUF_DESC_NULL; 477 size_t i; 478 res_T res = RES_OK; 479 ASSERT(ground); 480 481 /* The layout of sbuf_desc has already been checked when creating rngrd */ 482 res = sbuf_get_desc(ground->props, &sbuf_desc); 483 if(res != RES_OK) goto error; 484 485 FOR_EACH(i, 0, sbuf_desc.size) { 486 const struct ALIGN(8) { uint32_t mtl_id; float temperature; }* item = NULL; 487 item = sbuf_desc_at(&sbuf_desc, i); 488 489 ASSERT(IS_ALIGNED(item, sbuf_desc.alitem)); 490 ASSERT(sizeof(*item) == sbuf_desc.szitem); 491 492 if(item->mtl_id >= darray_mtl_size_get(&ground->mtls)) { 493 log_err(ground, 494 "%s: triangle %lu: invalid material id `%u'. It must be in [0, %lu[\n", 495 str_cget(&ground->name), (unsigned long)i, item->mtl_id, 496 (unsigned long)darray_mtl_size_get(&ground->mtls)); 497 res = RES_BAD_ARG; 498 goto error; 499 } 500 501 if(item->temperature < 0) { 502 log_err(ground, "%s: triangle %lu: invalid temperature `%g'\n", 503 str_cget(&ground->name), i, item->temperature); 504 res = RES_BAD_ARG; 505 goto error; 506 } 507 } 508 509 exit: 510 return res; 511 error: 512 goto exit; 513 }