rngrd

Describe a surface and its physical properties
git clone git://git.meso-star.fr/rngrd.git
Log | Files | Refs | README | LICENSE

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 }