mrumtl

Describe materials that vary spectrally
git clone git://git.meso-star.fr/mrumtl.git
Log | Files | Refs | README | LICENSE

mrumtl.c (18838B)


      1 /* Copyright (C) 2020-2023 |Méso|Star> (contact@meso-star.com)
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 3 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     15 
     16 #define _POSIX_C_SOURCE 200112L /* strtok_r support */
     17 
     18 #include "mrumtl.h"
     19 #include "mrumtl_c.h"
     20 #include "mrumtl_log.h"
     21 
     22 #include <rsys/algorithm.h>
     23 #include <rsys/cstr.h>
     24 #include <rsys/text_reader.h>
     25 
     26 #include <string.h>
     27 
     28 /*******************************************************************************
     29  * Helper functions
     30  ******************************************************************************/
     31 static INLINE res_T
     32 check_mrumtl_create_args(const struct mrumtl_create_args* args)
     33 {
     34   /* Nothing to check. Only return RES_BAD_ARG if args is NULL */
     35   return args ? RES_OK : RES_BAD_ARG;
     36 }
     37 
     38 static res_T
     39 parse_brdf_lambertian
     40   (struct mrumtl* mrumtl,
     41    struct txtrdr* txtrdr,
     42    char** tk_ctx,
     43    struct brdf_lambertian* brdf)
     44 {
     45   char* tk = NULL;
     46   res_T res = RES_OK;
     47   ASSERT(mrumtl && txtrdr && tk_ctx && brdf);
     48 
     49   tk = strtok_r(NULL, " \t", tk_ctx);
     50   if(!tk) {
     51     log_err(mrumtl, "%s:%lu: missing lambertian reflectivity.\n",
     52       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr));
     53     res = RES_BAD_ARG;
     54     goto error;
     55   }
     56 
     57   res = cstr_to_double(tk, &brdf->reflectivity);
     58   if(res != RES_OK) {
     59     log_err(mrumtl, "%s:%lu: invalid lambertian reflectivity `%s'.\n",
     60       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk);
     61     res = RES_BAD_ARG;
     62     goto error;
     63   }
     64 
     65   if(brdf->reflectivity < 0 || brdf->reflectivity > 1) {
     66     log_err(mrumtl, "%s:%lu: the lambertian reflectivity must be in [0, 1] "
     67       "while the submitted value is %g.\n",
     68       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
     69       brdf->reflectivity);
     70     res = RES_BAD_ARG;
     71     goto error;
     72   }
     73 
     74   tk = strtok_r(NULL, " \t", tk_ctx);
     75   if(tk) {
     76     log_warn(mrumtl, "%s:%lu: unexpected text `%s'.\n",
     77       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk);
     78   }
     79 
     80 exit:
     81   return res;
     82 error:
     83   goto exit;
     84 }
     85 
     86 static res_T
     87 parse_brdf_specular
     88   (struct mrumtl* mrumtl,
     89    struct txtrdr* txtrdr,
     90    char** tk_ctx,
     91    struct brdf_specular* brdf)
     92 {
     93   char* tk = NULL;
     94   res_T res = RES_OK;
     95   ASSERT(mrumtl && txtrdr && tk_ctx && brdf);
     96 
     97   tk = strtok_r(NULL, " \t", tk_ctx);
     98   if(!tk) {
     99     log_err(mrumtl, "%s:%lu: missing specular reflectivity.\n",
    100       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr));
    101     res = RES_BAD_ARG;
    102     goto error;
    103   }
    104 
    105   res = cstr_to_double(tk, &brdf->reflectivity);
    106   if(res != RES_OK) {
    107     log_err(mrumtl, "%s:%lu: invalid specular reflectivity `%s'.\n",
    108       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk);
    109     res = RES_BAD_ARG;
    110     goto error;
    111   }
    112 
    113   if(brdf->reflectivity < 0 || brdf->reflectivity > 1) {
    114     log_err(mrumtl, "%s:%lu: the specular reflectivity must be in [0, 1] "
    115       "while the submitted value is %g.\n",
    116       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    117       brdf->reflectivity);
    118     res = RES_BAD_ARG;
    119     goto error;
    120   }
    121 
    122   tk = strtok_r(NULL, " \t", tk_ctx);
    123   if(tk) {
    124     log_warn(mrumtl, "%s:%lu: unexpected text `%s'.\n",
    125       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk);
    126   }
    127 
    128 exit:
    129   return res;
    130 error:
    131   goto exit;
    132 }
    133 
    134 static res_T
    135 parse_brdf
    136   (struct mrumtl* mrumtl,
    137    struct txtrdr* txtrdr,
    138    char** tk_ctx,
    139    struct mrumtl_brdf* brdf)
    140 {
    141   char* tk = NULL;
    142   res_T res = RES_OK;
    143   ASSERT(mrumtl && txtrdr && tk_ctx && brdf);
    144 
    145   tk = strtok_r(NULL, " \t", tk_ctx);
    146   if(!tk) {
    147     log_err(mrumtl, "%s:%lu: missing BRDF type.\n",
    148       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr));
    149     res = RES_BAD_ARG;
    150     goto error;
    151   }
    152 
    153   if(!strcmp(tk, "lambertian")) {
    154     brdf->type = MRUMTL_BRDF_LAMBERTIAN;
    155     res = parse_brdf_lambertian(mrumtl, txtrdr, tk_ctx, &brdf->param.lambertian);
    156     if(res != RES_OK) goto error;
    157 
    158   } else if(!strcmp(tk, "specular")) {
    159     brdf->type = MRUMTL_BRDF_SPECULAR;
    160     res = parse_brdf_specular(mrumtl, txtrdr, tk_ctx, &brdf->param.specular);
    161     if(res != RES_OK) goto error;
    162 
    163   } else {
    164     log_err(mrumtl, "%s:%lu: invalid BRDF type `%s'.\n",
    165       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk);
    166     res = RES_BAD_ARG;
    167     goto error;
    168   }
    169 
    170 exit:
    171   return res;
    172 error:
    173   goto exit;
    174 }
    175 
    176 static res_T
    177 parse_wlen_brdf
    178   (struct mrumtl* mrumtl,
    179    struct txtrdr* txtrdr,
    180    struct mrumtl_brdf* brdf)
    181 {
    182   char* tk = NULL;
    183   char* tk_ctx = NULL;
    184   double wlen = 0;
    185   res_T res = RES_OK;
    186   ASSERT(mrumtl && txtrdr && brdf);
    187 
    188   res = txtrdr_read_line(txtrdr);
    189   if(res != RES_OK) {
    190     log_err(mrumtl, "%s: could not read the line `%lu' -- %s.\n",
    191       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    192       res_to_cstr(res));
    193     goto error;
    194   }
    195 
    196   if(!txtrdr_get_cline(txtrdr)) {
    197     const size_t nexpect = darray_brdf_size_get(&mrumtl->brdfs);
    198     const size_t nparsed = (size_t)
    199       (brdf - darray_brdf_cdata_get(&mrumtl->brdfs));
    200     log_err(mrumtl,
    201       "%s:%lu: missing BRDF. "
    202       "Expecting %lu BRDF%swhile %lu %s parsed.\n",
    203       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    204       (unsigned long)nexpect, nexpect == 1 ? " " : "s ",
    205       (unsigned long)nparsed, nparsed > 1 ? "were" : "was");
    206     res = RES_BAD_ARG;
    207     goto error;
    208   }
    209 
    210   tk = strtok_r(txtrdr_get_line(txtrdr), " \t", &tk_ctx);
    211   ASSERT(tk);
    212 
    213   res = cstr_to_double(tk, &wlen);
    214   if(res == RES_OK && wlen < 0) res = RES_BAD_ARG;
    215   if(res != RES_OK) {
    216     log_err(mrumtl, "%s:%lu: invalid wavelength `%s'.\n",
    217       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk);
    218     goto error;
    219   }
    220   brdf->wlen[0] = wlen;
    221   brdf->wlen[1] = wlen;
    222 
    223   res = parse_brdf(mrumtl, txtrdr, &tk_ctx, brdf);
    224   if(res != RES_OK) goto error;
    225 
    226 exit:
    227   return res;
    228 error:
    229   goto exit;
    230 }
    231 
    232 static res_T
    233 parse_band_brdf
    234   (struct mrumtl* mrumtl,
    235    struct txtrdr* txtrdr,
    236    struct mrumtl_brdf* brdf)
    237 {
    238   char* tk = NULL;
    239   char* tk_ctx = NULL;
    240   res_T res = RES_OK;
    241   ASSERT(mrumtl && txtrdr && brdf);
    242 
    243   res = txtrdr_read_line(txtrdr);
    244   if(res != RES_OK) {
    245     log_err(mrumtl, "%s: could not read the line `%lu' -- %s.\n",
    246       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    247       res_to_cstr(res));
    248     goto error;
    249   }
    250 
    251   if(!txtrdr_get_cline(txtrdr)) {
    252     const size_t nexpect = darray_brdf_size_get(&mrumtl->brdfs);
    253     const size_t nparsed = (size_t)
    254       (brdf - darray_brdf_cdata_get(&mrumtl->brdfs));
    255     log_err(mrumtl,
    256       "%s:%lu: missing BRDF. "
    257       "Expecting %lu BRDF%swhile %lu %s parsed.\n",
    258       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    259       (unsigned long)nexpect, nexpect == 1 ? " " : "s ",
    260       (unsigned long)nparsed, nparsed > 1 ? "were" : "was");
    261     res = RES_BAD_ARG;
    262     goto error;
    263   }
    264 
    265   tk = strtok_r(txtrdr_get_line(txtrdr), " \t", &tk_ctx);
    266   ASSERT(tk);
    267 
    268   res = cstr_to_double(tk, &brdf->wlen[0]);
    269   if(res == RES_OK && brdf->wlen[0] < 0) res = RES_BAD_ARG;
    270   if(res != RES_OK) {
    271     log_err(mrumtl, "%s:%lu: invalid band min boundary `%s'.\n",
    272       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk);
    273     goto error;
    274   }
    275 
    276   tk = strtok_r(NULL, " \t", &tk_ctx);
    277   if(!tk) {
    278     log_err(mrumtl, "%s:%lu: missing band max boundary.\n",
    279       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr));
    280     res = RES_BAD_ARG;
    281     goto error;
    282   }
    283 
    284   res = cstr_to_double(tk, &brdf->wlen[1]);
    285   if(res == RES_OK && brdf->wlen[1] < 0) res = RES_BAD_ARG;
    286   if(res != RES_OK) {
    287     log_err(mrumtl, "%s:%lu: invalid band max boundary `%s'.\n",
    288       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk);
    289     goto error;
    290   }
    291 
    292   if(brdf->wlen[0] > brdf->wlen[1]) {
    293     log_err(mrumtl, "%s:%lu: band boundaries are degenerated -- [%g, %g].\n",
    294       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    295       brdf->wlen[0], brdf->wlen[1]);
    296     res = RES_BAD_ARG;
    297     goto error;
    298   }
    299 
    300   res = parse_brdf(mrumtl, txtrdr, &tk_ctx, brdf);
    301   if(res != RES_OK) goto error;
    302 
    303 exit:
    304   return res;
    305 error:
    306   goto exit;
    307 }
    308 
    309 static res_T
    310 parse_per_wlen_brdf(struct mrumtl* mrumtl, struct txtrdr* txtrdr, char** tk_ctx)
    311 {
    312   char* tk = NULL;
    313   unsigned long count = 0;
    314   unsigned long i;
    315   res_T res = RES_OK;
    316   ASSERT(mrumtl && txtrdr && tk_ctx);
    317 
    318   tk = strtok_r(NULL, " \t", tk_ctx);
    319   if(!tk) {
    320     log_err(mrumtl, "%s:%lu: the number of wavelengths is missing.\n",
    321       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr));
    322     res = RES_BAD_ARG;
    323     goto error;
    324   }
    325 
    326   res = cstr_to_ulong(tk, &count);
    327   if(res != RES_OK) {
    328     log_err(mrumtl, "%s:%lu: invalid number of wavelengths `%s'.\n",
    329       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk);
    330     res = RES_BAD_ARG;
    331     goto error;
    332   }
    333 
    334   tk = strtok_r(NULL, " \t", tk_ctx);
    335   if(tk) {
    336     log_warn(mrumtl, "%s:%lu: unexpected text `%s'.\n",
    337       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk);
    338   }
    339 
    340   res = darray_brdf_resize(&mrumtl->brdfs, count);
    341   if(res != RES_OK) {
    342     log_err(mrumtl,
    343       "%s:%lu: could not allocate the list of BRDFs -- %s.\n",
    344       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    345       res_to_cstr(res));
    346     goto error;
    347   }
    348 
    349   FOR_EACH(i, 0, count) {
    350     struct mrumtl_brdf* brdf = NULL;
    351 
    352     brdf = darray_brdf_data_get(&mrumtl->brdfs) + i;
    353 
    354     res = parse_wlen_brdf(mrumtl, txtrdr, brdf);
    355     if(res != RES_OK) goto error;
    356     ASSERT(brdf->wlen[0] == brdf->wlen[1]);
    357 
    358     if(i > 0 && brdf[0].wlen[0] <= brdf[-1].wlen[0]) {
    359       log_err(mrumtl,
    360         "%s:%lu: the BRDFs must be sorted un ascending order of wavelengths "
    361         "(brdf %lu at %g nm; brdf %lu at %g nm).\n",
    362         txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    363         (unsigned long)(i-1), brdf[-1].wlen[0],
    364         (unsigned long)(i),   brdf[ 0].wlen[0]);
    365       res = RES_BAD_ARG;
    366       goto error;
    367     }
    368   }
    369 
    370 exit:
    371   return res;
    372 error:
    373   darray_brdf_clear(&mrumtl->brdfs);
    374   goto exit;
    375 }
    376 
    377 static res_T
    378 parse_per_band_brdf(struct mrumtl* mrumtl, struct txtrdr* txtrdr, char** tk_ctx)
    379 {
    380   char* tk = NULL;
    381   unsigned long count = 0;
    382   unsigned long i = 0;
    383   res_T res = RES_OK;
    384   ASSERT(mrumtl && txtrdr && tk_ctx);
    385 
    386   tk = strtok_r(NULL, " \t", tk_ctx);
    387   if(!tk) {
    388     log_err(mrumtl, "%s:%lu: the number of bands is missing.\n",
    389       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr));
    390     res = RES_BAD_ARG;
    391     goto error;
    392   }
    393 
    394   res = cstr_to_ulong(tk, &count);
    395   if(res != RES_OK) {
    396     log_err(mrumtl, "%s:%lu: invalid number of bands `%s'.\n",
    397       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk);
    398     res = RES_BAD_ARG;
    399     goto error;
    400   }
    401 
    402   tk = strtok_r(NULL, " \t", tk_ctx);
    403   if(tk) {
    404     log_warn(mrumtl, "%s:%lu: unexpected text `%s'.\n",
    405       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk);
    406   }
    407 
    408   res = darray_brdf_resize(&mrumtl->brdfs, count);
    409   if(res != RES_OK) {
    410     log_err(mrumtl,
    411       "%s:%lu: could not allocate the list of BRDFs -- %s.\n",
    412       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    413       res_to_cstr(res));
    414     goto error;
    415   }
    416 
    417   FOR_EACH(i, 0, count) {
    418     struct mrumtl_brdf* brdf = NULL;
    419 
    420     brdf = darray_brdf_data_get(&mrumtl->brdfs) + i;
    421 
    422     res = parse_band_brdf(mrumtl, txtrdr, brdf);
    423     if(res != RES_OK) goto error;
    424 
    425     if(i > 0) {
    426       if(brdf[0].wlen[0] <  brdf[-1].wlen[1]
    427       || brdf[0].wlen[0] == brdf[-1].wlen[0]) {
    428         log_err(mrumtl,
    429           "%s:%lu: the BRDFs must be sorted in ascending order "
    430           "of bands and must not overlap (BRDF %lu in [%g, %g] nm; "
    431           "BRDF %lu in [%g %g] nm)\n",
    432           txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    433           (unsigned long)(i-1), brdf[-1].wlen[0], brdf[-1].wlen[1],
    434           (unsigned long)(i),   brdf[ 0].wlen[0], brdf[ 0].wlen[1]);
    435         res = RES_BAD_ARG;
    436         goto error;
    437       }
    438     }
    439   }
    440 
    441 exit:
    442   return res;
    443 error:
    444   darray_brdf_clear(&mrumtl->brdfs);
    445   goto exit;
    446 }
    447 
    448 static INLINE void
    449 clear_mrumtl(struct mrumtl* mrumtl)
    450 {
    451   ASSERT(mrumtl);
    452   str_clear(&mrumtl->name);
    453   darray_brdf_clear(&mrumtl->brdfs);
    454 }
    455 
    456 static res_T
    457 load_stream
    458   (struct mrumtl* mrumtl,
    459    FILE* stream,
    460    const char* stream_name)
    461 {
    462   char* tk = NULL;
    463   char* tk_ctx = NULL;
    464   struct txtrdr* txtrdr = NULL;
    465   res_T res = RES_OK;
    466   ASSERT(mrumtl && stream && stream_name);
    467 
    468   clear_mrumtl(mrumtl);
    469 
    470   res = txtrdr_stream(mrumtl->allocator, stream, stream_name, '#', &txtrdr);
    471   if(res != RES_OK) {
    472     log_err(mrumtl, "Could not create the text reader to parse `%s' -- %s.\n",
    473       stream_name, res_to_cstr(res));
    474     goto error;
    475   }
    476 
    477   res = txtrdr_read_line(txtrdr);
    478   if(res != RES_OK) {
    479     log_err(mrumtl, "%s: could not read the line `%lu' -- %s.\n",
    480       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    481       res_to_cstr(res));
    482     goto error;
    483   }
    484 
    485   res = str_set(&mrumtl->name, stream_name);
    486   if(res != RES_OK) {
    487     log_err(mrumtl, "Could not copy the stream name `%s' -- %s.\n",
    488       stream_name, res_to_cstr(res));
    489     goto error;
    490   }
    491 
    492   if(!txtrdr_get_cline(txtrdr)) goto exit; /* No line to parse */
    493 
    494   tk = strtok_r(txtrdr_get_line(txtrdr), " \t", &tk_ctx);
    495   ASSERT(tk);
    496 
    497   if(!strcmp(tk, "wavelengths")) {
    498     res = parse_per_wlen_brdf(mrumtl, txtrdr, &tk_ctx);
    499     if(res != RES_BAD_ARG) goto error;
    500   } else if(!strcmp(tk, "bands")) {
    501     res = parse_per_band_brdf(mrumtl, txtrdr, &tk_ctx);
    502     if(res != RES_BAD_ARG) goto error;
    503   } else {
    504     log_err(mrumtl, "%s:%lu: invalid directive `%s'.\n",
    505       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk);
    506     res = RES_BAD_ARG;
    507     goto error;
    508   }
    509 
    510 exit:
    511   if(txtrdr) txtrdr_ref_put(txtrdr);
    512   return res;
    513 error:
    514   goto exit;
    515 }
    516 
    517 static void
    518 release_mrumtl(ref_T* ref)
    519 {
    520   struct mrumtl* mrumtl;
    521   ASSERT(ref);
    522   mrumtl = CONTAINER_OF(ref, struct mrumtl, ref);
    523 
    524   darray_brdf_release(&mrumtl->brdfs);
    525   str_release(&mrumtl->name);
    526 
    527   /* First release the programs that uses the libs array */
    528   if(mrumtl->logger == &mrumtl->logger__) logger_release(&mrumtl->logger__);
    529   MEM_RM(mrumtl->allocator, mrumtl);
    530 }
    531 
    532 /*******************************************************************************
    533  * Exported functions
    534  ******************************************************************************/
    535 res_T
    536 mrumtl_create
    537   (const struct mrumtl_create_args* args,
    538    struct mrumtl** out_mrumtl)
    539 {
    540   struct mrumtl* mrumtl = NULL;
    541   struct mem_allocator* allocator = NULL;
    542   res_T res = RES_OK;
    543 
    544   if(!out_mrumtl) { res = RES_BAD_ARG;  goto error; }
    545   res = check_mrumtl_create_args(args);
    546   if(res != RES_OK) goto error;
    547 
    548   allocator = args->allocator ? args->allocator : &mem_default_allocator;
    549   mrumtl = MEM_CALLOC(allocator, 1, sizeof(*mrumtl));
    550   if(!mrumtl) {
    551     if(args->verbose) {
    552       #define ERR_STR "Could not allocate the MruMtl handler.\n"
    553       if(args->logger) {
    554         logger_print(args->logger, LOG_ERROR, ERR_STR);
    555       } else {
    556         fprintf(stderr, MSG_ERROR_PREFIX ERR_STR);
    557       }
    558       #undef ERR_STR
    559     }
    560     res = RES_MEM_ERR;
    561     goto error;
    562   }
    563   ref_init(&mrumtl->ref);
    564   mrumtl->allocator = allocator;
    565   mrumtl->verbose = args->verbose;
    566   darray_brdf_init(mrumtl->allocator, &mrumtl->brdfs);
    567   str_init(mrumtl->allocator, &mrumtl->name);
    568 
    569   if(args->logger) {
    570     mrumtl->logger = args->logger;
    571   } else {
    572     res = setup_log_default(mrumtl);
    573     if(res != RES_OK) {
    574       if(args->verbose) {
    575         fprintf(stderr, MSG_ERROR_PREFIX
    576           "Could not setup the MruMtl logger.\n");
    577       }
    578       goto error;
    579     }
    580     mrumtl->logger = &mrumtl->logger__;
    581   }
    582 
    583 exit:
    584   if(out_mrumtl) *out_mrumtl = mrumtl;
    585   return res;
    586 error:
    587   if(mrumtl) {
    588     MRUMTL(ref_put(mrumtl));
    589     mrumtl = NULL;
    590   }
    591   goto exit;
    592 }
    593 
    594 res_T
    595 mrumtl_ref_get(struct mrumtl* mrumtl)
    596 {
    597   if(!mrumtl) return RES_BAD_ARG;
    598   ref_get(&mrumtl->ref);
    599   return RES_OK;
    600 }
    601 
    602 res_T
    603 mrumtl_ref_put(struct mrumtl* mrumtl)
    604 {
    605   if(!mrumtl) return RES_BAD_ARG;
    606   ref_put(&mrumtl->ref, release_mrumtl);
    607   return RES_OK;
    608 }
    609 
    610 res_T
    611 mrumtl_load(struct mrumtl* mrumtl, const char* filename)
    612 {
    613   FILE* fp = NULL;
    614   res_T res = RES_OK;
    615 
    616   if(!mrumtl || !filename) {
    617     res = RES_BAD_ARG;
    618     goto error;
    619   }
    620 
    621   fp = fopen(filename, "r");
    622   if(!fp) {
    623     log_err(mrumtl, "%s: error opening file `%s' -- %s.\n",
    624       FUNC_NAME, filename, strerror(errno));
    625     res = RES_IO_ERR;
    626     goto error;
    627   }
    628 
    629   res = load_stream(mrumtl, fp, filename);
    630   if(res != RES_OK) goto error;
    631 
    632 exit:
    633   if(fp) fclose(fp);
    634   return res;
    635 error:
    636   goto exit;
    637 }
    638 
    639 res_T
    640 mrumtl_load_stream
    641   (struct mrumtl* mrumtl,
    642    FILE* stream,
    643    const char* stream_name)
    644 {
    645   res_T res = RES_OK;
    646 
    647   if(!mrumtl || !stream) {
    648     res = RES_BAD_ARG;
    649     goto error;
    650   }
    651 
    652   res = load_stream(mrumtl, stream, stream_name ? stream_name : "<stream>");
    653   if(res != RES_OK) goto error;
    654 
    655 exit:
    656   return res;
    657 error:
    658   goto exit;
    659 }
    660 
    661 size_t
    662 mrumtl_get_brdfs_count(const struct mrumtl* mrumtl)
    663 {
    664   ASSERT(mrumtl);
    665   return darray_brdf_size_get(&mrumtl->brdfs);
    666 }
    667 
    668 const struct mrumtl_brdf*
    669 mrumtl_get_brdf(const struct mrumtl* mrumtl, const size_t ibrdf)
    670 {
    671   ASSERT(mrumtl && ibrdf < mrumtl_get_brdfs_count(mrumtl));
    672   return darray_brdf_cdata_get(&mrumtl->brdfs) + ibrdf;
    673 }
    674 
    675 enum mrumtl_brdf_type
    676 mrumtl_brdf_get_type(const struct mrumtl_brdf* brdf)
    677 {
    678   ASSERT(brdf);
    679   return brdf->type;
    680 }
    681 
    682 res_T
    683 mrumtl_brdf_get_lambertian
    684   (const struct mrumtl_brdf* brdf,
    685    struct mrumtl_brdf_lambertian* lambertian)
    686 {
    687   if(!brdf || !lambertian) return RES_BAD_ARG;
    688   if(brdf->type != MRUMTL_BRDF_LAMBERTIAN) return RES_BAD_ARG;
    689   lambertian->wavelengths[0] = brdf->wlen[0];
    690   lambertian->wavelengths[1] = brdf->wlen[1];
    691   lambertian->reflectivity = brdf->param.lambertian.reflectivity;
    692   return RES_OK;
    693 } 
    694 
    695 res_T
    696 mrumtl_brdf_get_specular
    697   (const struct mrumtl_brdf* brdf,
    698    struct mrumtl_brdf_specular* specular)
    699 {
    700   if(!brdf || !specular) return RES_BAD_ARG;
    701   if(brdf->type != MRUMTL_BRDF_SPECULAR) return RES_BAD_ARG;
    702   specular->wavelengths[0] = brdf->wlen[0];
    703   specular->wavelengths[1] = brdf->wlen[1];
    704   specular->reflectivity = brdf->param.specular.reflectivity;
    705   return RES_OK;
    706 }
    707