htrdr

Solving radiative transfer in heterogeneous media
git clone git://git.meso-star.fr/htrdr.git
Log | Files | Refs | README | LICENSE

htrdr_materials.c (13178B)


      1 /* Copyright (C) 2018-2019, 2022-2025 Centre National de la Recherche Scientifique
      2  * Copyright (C) 2020-2022 Institut Mines Télécom Albi-Carmaux
      3  * Copyright (C) 2022-2025 Institut Pierre-Simon Laplace
      4  * Copyright (C) 2022-2025 Institut de Physique du Globe de Paris
      5  * Copyright (C) 2018-2025 |Méso|Star> (contact@meso-star.com)
      6  * Copyright (C) 2022-2025 Observatoire de Paris
      7  * Copyright (C) 2022-2025 Université de Reims Champagne-Ardenne
      8  * Copyright (C) 2022-2025 Université de Versaille Saint-Quentin
      9  * Copyright (C) 2018-2019, 2022-2025 Université Paul Sabatier
     10  *
     11  * This program is free software: you can redistribute it and/or modify
     12  * it under the terms of the GNU General Public License as published by
     13  * the Free Software Foundation, either version 3 of the License, or
     14  * (at your option) any later version.
     15  *
     16  * This program is distributed in the hope that it will be useful,
     17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     19  * GNU General Public License for more details.
     20  *
     21  * You should have received a copy of the GNU General Public License
     22  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     23 
     24 #define _POSIX_C_SOURCE 200112L /* strtok_r and wordexp support */
     25 
     26 #include "core/htrdr.h"
     27 #include "core/htrdr_log.h"
     28 #include "core/htrdr_materials.h"
     29 
     30 #include <mrumtl.h>
     31 #include <star/ssf.h>
     32 #include <star/ssp.h>
     33 
     34 #include <rsys/cstr.h>
     35 #include <rsys/double3.h>
     36 #include <rsys/hash_table.h>
     37 #include <rsys/mem_allocator.h>
     38 #include <rsys/ref_count.h>
     39 #include <rsys/str.h>
     40 #include <rsys/text_reader.h>
     41 
     42 #include <string.h>
     43 #include <wordexp.h>
     44 
     45 struct mtl {
     46   struct mrumtl* mrumtl;
     47   double temperature; /* In Kelvin */
     48 };
     49 static const struct mtl MTL_NULL = {NULL, -1};
     50 
     51 /* Generate the hash table that maps a material name to its data */
     52 #define HTABLE_NAME name2mtl
     53 #define HTABLE_DATA struct mtl
     54 #define HTABLE_KEY struct str
     55 #define HTABLE_KEY_FUNCTOR_INIT str_init
     56 #define HTABLE_KEY_FUNCTOR_RELEASE str_release
     57 #define HTABLE_KEY_FUNCTOR_COPY str_copy
     58 #define HTABLE_KEY_FUNCTOR_COPY_AND_RELEASE str_copy_and_release
     59 #define HTABLE_KEY_FUNCTOR_HASH str_hash
     60 #define HTABLE_KEY_FUNCTOR_EQ str_eq
     61 #include <rsys/hash_table.h>
     62 
     63 struct htrdr_materials {
     64   struct htable_name2mtl name2mtl;
     65   struct htrdr* htrdr;
     66   ref_T ref;
     67 };
     68 
     69 /*******************************************************************************
     70  * Local functions
     71  ******************************************************************************/
     72 static res_T
     73 parse_material
     74   (struct htrdr_materials* mats,
     75    struct txtrdr* txtrdr,
     76    struct str* str) /* Scratch string */
     77 {
     78   struct mrumtl_create_args mrumtl_args = MRUMTL_CREATE_ARGS_DEFAULT;
     79   wordexp_t wexp;
     80   char* tk = NULL;
     81   char* tk_ctx = NULL;
     82   struct mtl mtl = MTL_NULL;
     83   int err = 0;
     84   int wexp_is_allocated = 0;
     85   res_T res = RES_OK;
     86   ASSERT(mats && txtrdr);
     87 
     88   tk = strtok_r(txtrdr_get_line(txtrdr), " \t", &tk_ctx);
     89   ASSERT(tk);
     90 
     91   res = str_set(str, tk);
     92   if(res != RES_OK) {
     93     htrdr_log_err(mats->htrdr,
     94       "%s:%lu: could not copy the material name `%s' -- %s.\n",
     95       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk,
     96       res_to_cstr(res));
     97     goto error;
     98   }
     99 
    100   tk = strtok_r(NULL, "", &tk_ctx);
    101   if(!tk) {
    102     htrdr_log_err(mats->htrdr,
    103       "%s:%lu: missing the MruMtl file for the material `%s'.\n",
    104       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    105       str_cget(str));
    106     res = RES_BAD_ARG;
    107     goto error;
    108   }
    109 
    110   err = wordexp(tk, &wexp, 0);
    111   if(err) {
    112     htrdr_log_err(mats->htrdr,
    113       "%s:%lu: error in word expension of the mrumtl path.\n",
    114       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr));
    115     res = RES_BAD_ARG;
    116     goto error;
    117   }
    118   wexp_is_allocated = 1;
    119 
    120   if(wexp.we_wordc < 1) {
    121     htrdr_log_err(mats->htrdr,
    122       "%s:%lu: missing the MruMtl file for the material `%s'.\n",
    123       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    124       str_cget(str));
    125     res = RES_BAD_ARG;
    126     goto error;
    127   }
    128 
    129   /*  Parse the mrumtl file if any */
    130   if(strcmp(wexp.we_wordv[0], "none")) {
    131     mrumtl_args.logger = htrdr_get_logger(mats->htrdr);
    132     mrumtl_args.allocator = htrdr_get_allocator(mats->htrdr);
    133     mrumtl_args.verbose = htrdr_get_verbosity_level(mats->htrdr);
    134     res = mrumtl_create(&mrumtl_args, &mtl.mrumtl);
    135     if(res != RES_OK) {
    136       htrdr_log_err(mats->htrdr,
    137         "%s:%lu: error creating the MruMtl loader for the material `%s'-- %s.\n",
    138         txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    139         str_cget(str), res_to_cstr(res));
    140       goto error;
    141     }
    142 
    143     res = mrumtl_load(mtl.mrumtl, wexp.we_wordv[0]);
    144     if(res != RES_OK) goto error;
    145   }
    146 
    147   if(wexp.we_wordc < 2) {
    148     if(mtl.mrumtl) {
    149       htrdr_log_err(mats->htrdr,
    150         "%s:%lu: missing temperature for the material `%s'.\n",
    151         txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    152         str_cget(str));
    153       res = RES_BAD_ARG;
    154       goto error;
    155     }
    156   } else {
    157     /* Parse the temperature */
    158     res = cstr_to_double(wexp.we_wordv[1], &mtl.temperature);
    159     if(res != RES_OK) {
    160       htrdr_log_err(mats->htrdr,
    161         "%s:%lu: error parsing the temperature `%s' for the material `%s' "
    162         "-- %s.\n",
    163         txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    164         wexp.we_wordv[1], str_cget(str), res_to_cstr(res));
    165       goto error;
    166     }
    167   }
    168 
    169   /* Register the material */
    170   res = htable_name2mtl_set(&mats->name2mtl, str, &mtl);
    171   if(res != RES_OK) {
    172     htrdr_log_err(mats->htrdr,
    173       "%s:%lu: could not register the material `%s' -- %s.\n",
    174       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    175       str_cget(str), res_to_cstr(res));
    176     goto error;
    177   }
    178 
    179   if(wexp.we_wordc > 2) {
    180     htrdr_log_warn(mats->htrdr, "%s:%lu: unexpected text `%s'.\n",
    181       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    182       wexp.we_wordv[2]);
    183   }
    184 
    185 exit:
    186   if(wexp_is_allocated) wordfree(&wexp);
    187   return res;
    188 error:
    189   if(mtl.mrumtl) MRUMTL(ref_put(mtl.mrumtl));
    190   goto exit;
    191 }
    192 
    193 static res_T
    194 parse_materials_list
    195   (struct htrdr_materials* mats,
    196    const char* filename,
    197    const char* func_name)
    198 {
    199   struct txtrdr* txtrdr = NULL;
    200   struct str str;
    201   res_T res = RES_OK;
    202   ASSERT(mats && filename && func_name);
    203 
    204   str_init(htrdr_get_allocator(mats->htrdr), &str);
    205 
    206   res = txtrdr_file(htrdr_get_allocator(mats->htrdr), filename, '#', &txtrdr);
    207   if(res != RES_OK) {
    208     htrdr_log_err(mats->htrdr,
    209       "%s: could not create the text reader for the material file `%s' -- %s.\n",
    210       func_name, filename, res_to_cstr(res));
    211     goto error;
    212   }
    213 
    214   for(;;) {
    215     res = txtrdr_read_line(txtrdr);
    216     if(res != RES_OK) {
    217       htrdr_log_err(mats->htrdr,
    218         "%s: error reading a line in the material file `%s' -- %s.\n",
    219         func_name, filename, res_to_cstr(res));
    220       goto error;
    221     }
    222 
    223     if(!txtrdr_get_cline(txtrdr)) break;
    224 
    225     res = parse_material(mats, txtrdr, &str);
    226     if(res != RES_OK) goto error;
    227   }
    228 
    229 exit:
    230   str_release(&str);
    231   if(txtrdr) txtrdr_ref_put(txtrdr);
    232   return res;
    233 error:
    234   goto exit;
    235 }
    236 
    237 static void
    238 materials_release(ref_T* ref)
    239 {
    240   struct htable_name2mtl_iterator it, it_end;
    241   struct htrdr_materials* mats;
    242   struct htrdr* htrdr;
    243   ASSERT(ref);
    244   mats = CONTAINER_OF(ref, struct htrdr_materials, ref);
    245 
    246   htable_name2mtl_begin(&mats->name2mtl, &it);
    247   htable_name2mtl_end(&mats->name2mtl, &it_end);
    248   while(!htable_name2mtl_iterator_eq(&it, &it_end)) {
    249     struct mtl* mtl = htable_name2mtl_iterator_data_get(&it);
    250     /* The mrumtl can be NULL for semi transparent materials */
    251     if(mtl->mrumtl) MRUMTL(ref_put(mtl->mrumtl));
    252     htable_name2mtl_iterator_next(&it);
    253   }
    254   htable_name2mtl_release(&mats->name2mtl);
    255   htrdr = mats->htrdr;
    256   MEM_RM(htrdr_get_allocator(htrdr), mats);
    257   htrdr_ref_put(htrdr);
    258 }
    259 
    260 static res_T
    261 create_bsdf_diffuse
    262   (struct htrdr* htrdr,
    263    const struct mrumtl_brdf* brdf,
    264    const size_t ithread,
    265    struct ssf_bsdf** out_bsdf)
    266 {
    267   struct mrumtl_brdf_lambertian lambert;
    268   struct ssf_bsdf* bsdf = NULL;
    269   res_T res = RES_OK;
    270   ASSERT(htrdr && brdf && out_bsdf);
    271   ASSERT(mrumtl_brdf_get_type(brdf) == MRUMTL_BRDF_LAMBERTIAN);
    272 
    273   res = ssf_bsdf_create(htrdr_get_thread_allocator(htrdr, ithread),
    274     &ssf_lambertian_reflection, &bsdf);
    275   if(res != RES_OK) goto error;
    276 
    277   MRUMTL(brdf_get_lambertian(brdf, &lambert));
    278   res = ssf_lambertian_reflection_setup(bsdf, lambert.reflectivity);
    279   if(res != RES_OK) goto error;
    280 
    281 exit:
    282   *out_bsdf = bsdf;
    283   return res;
    284 error:
    285    if(bsdf) { SSF(bsdf_ref_put(bsdf)); bsdf = NULL; }
    286   goto exit;
    287 }
    288 
    289 static res_T
    290 create_bsdf_specular
    291   (struct htrdr* htrdr,
    292    const struct mrumtl_brdf* brdf,
    293    const size_t ithread,
    294    struct ssf_bsdf** out_bsdf)
    295 {
    296   struct mrumtl_brdf_specular spec;
    297   struct ssf_bsdf* bsdf = NULL;
    298   struct ssf_fresnel* fresnel = NULL;
    299   struct mem_allocator* allocator = NULL;
    300   res_T res = RES_OK;
    301   ASSERT(htrdr && brdf && out_bsdf);
    302   ASSERT(mrumtl_brdf_get_type(brdf) == MRUMTL_BRDF_SPECULAR);
    303 
    304   allocator = htrdr_get_thread_allocator(htrdr, ithread);
    305 
    306   res = ssf_bsdf_create(allocator, &ssf_specular_reflection, &bsdf);
    307   if(res != RES_OK) goto error;
    308 
    309   res = ssf_fresnel_create(allocator, &ssf_fresnel_constant, &fresnel);
    310   if(res != RES_OK) goto error;
    311 
    312   MRUMTL(brdf_get_specular(brdf, &spec));
    313   res =  ssf_fresnel_constant_setup(fresnel, spec.reflectivity);
    314   if(res != RES_OK) goto error;
    315 
    316   res = ssf_specular_reflection_setup(bsdf, fresnel);
    317   if(res != RES_OK) goto error;
    318 
    319 exit:
    320   if(fresnel) SSF(fresnel_ref_put(fresnel));
    321   *out_bsdf = bsdf;
    322   return res;
    323 error:
    324   if(bsdf) { SSF(bsdf_ref_put(bsdf)); bsdf = NULL; }
    325   goto exit;
    326 }
    327 
    328 /*******************************************************************************
    329  * Local symbol
    330  ******************************************************************************/
    331 res_T
    332 htrdr_materials_create
    333   (struct htrdr* htrdr,
    334    const char* filename,
    335    struct htrdr_materials** out_mtl)
    336 {
    337   struct htrdr_materials* mats = NULL;
    338   res_T res = RES_OK;
    339   ASSERT(htrdr && filename && out_mtl);
    340 
    341   mats = MEM_CALLOC(htrdr_get_allocator(htrdr), 1, sizeof(*mats));
    342   if(!mats) {
    343     res = RES_MEM_ERR;
    344     htrdr_log_err(htrdr,
    345       "%s: could not allocate the mats data structure -- %s.\n",
    346       FUNC_NAME, res_to_cstr(res));
    347     goto error;
    348   }
    349   ref_init(&mats->ref);
    350   htrdr_ref_get(htrdr);
    351   mats->htrdr = htrdr;
    352   htable_name2mtl_init(htrdr_get_allocator(htrdr), &mats->name2mtl);
    353 
    354   res = parse_materials_list(mats, filename, FUNC_NAME);
    355   if(res != RES_OK) goto error;
    356 
    357 exit:
    358   if(out_mtl) *out_mtl = mats;
    359   return res;
    360 error:
    361   if(mats) {
    362     htrdr_materials_ref_put(mats);
    363     mats = NULL;
    364   }
    365   goto exit;
    366 }
    367 
    368 void
    369 htrdr_materials_ref_get(struct htrdr_materials* mats)
    370 {
    371   ASSERT(mats);
    372   ref_get(&mats->ref);
    373 }
    374 
    375 void
    376 htrdr_materials_ref_put(struct htrdr_materials* mats)
    377 {
    378   ASSERT(mats);
    379   ref_put(&mats->ref, materials_release);
    380 }
    381 
    382 int
    383 htrdr_materials_find_mtl
    384   (struct htrdr_materials* mats,
    385    const char* name,
    386    struct htrdr_mtl* htrdr_mtl)
    387 {
    388   struct str str;
    389   struct htable_name2mtl_iterator it, it_end;
    390   int found = 0;
    391   ASSERT(mats && name && htrdr_mtl);
    392 
    393   str_init(htrdr_get_allocator(mats->htrdr), &str);
    394   CHK(str_set(&str, name) == RES_OK);
    395 
    396   htable_name2mtl_find_iterator(&mats->name2mtl, &str, &it);
    397   htable_name2mtl_end(&mats->name2mtl, &it_end);
    398   if(htable_name2mtl_iterator_eq(&it, &it_end)) { /* No material found */
    399     *htrdr_mtl = HTRDR_MTL_NULL;
    400     found = 0;
    401   } else {
    402     struct mtl* mtl = htable_name2mtl_iterator_data_get(&it);
    403     ASSERT(mtl != NULL);
    404     htrdr_mtl->name = str_cget(htable_name2mtl_iterator_key_get(&it));
    405     htrdr_mtl->mrumtl = mtl->mrumtl;
    406     htrdr_mtl->temperature = mtl->temperature;
    407     found = 1;
    408   }
    409   str_release(&str);
    410 
    411   return found;
    412 }
    413 
    414 res_T
    415 htrdr_mtl_create_bsdf
    416   (struct htrdr* htrdr,
    417    const struct htrdr_mtl* mtl,
    418    const size_t ithread,
    419    const double wavelength,
    420    struct ssp_rng* rng,
    421    struct ssf_bsdf** out_bsdf)
    422 {
    423   struct ssf_bsdf* bsdf = NULL;
    424   const struct mrumtl_brdf* brdf = NULL;
    425   size_t ibrdf;
    426   double r;
    427   res_T res = RES_OK;
    428   ASSERT(htrdr && mtl && wavelength && rng && out_bsdf);
    429 
    430   r = ssp_rng_canonical(rng);
    431 
    432   res = mrumtl_fetch_brdf(mtl->mrumtl, wavelength, r, &ibrdf);
    433   if(res != RES_OK) {
    434     htrdr_log_err(htrdr,
    435       "%s: error retrieving the MruMtl BRDF for the wavelength %g.\n",
    436       FUNC_NAME, wavelength);
    437     res = RES_BAD_ARG;
    438     goto error;
    439   }
    440 
    441   brdf = mrumtl_get_brdf(mtl->mrumtl, ibrdf);
    442   switch(mrumtl_brdf_get_type(brdf)) {
    443     case MRUMTL_BRDF_LAMBERTIAN:
    444       res = create_bsdf_diffuse(htrdr, brdf, ithread, &bsdf);
    445       break;
    446     case MRUMTL_BRDF_SPECULAR:
    447       res = create_bsdf_specular(htrdr, brdf, ithread, &bsdf);
    448       break;
    449     default: FATAL("Unreachable code.\n");  break;
    450   }
    451   if(res != RES_OK) {
    452     htrdr_log_err(htrdr, "%s: could not create the BSDF -- %s.\n",
    453       FUNC_NAME, res_to_cstr(res));
    454     goto error;
    455   }
    456 
    457 exit:
    458   *out_bsdf = bsdf;
    459   return res;
    460 error:
    461   if(bsdf) { SSF(bsdf_ref_put(bsdf)); bsdf = NULL; }
    462   goto exit;
    463 }
    464