solstice

Compute collected power and efficiencies of a solar plant
git clone git://git.meso-star.com/solstice.git
Log | Files | Refs | README | LICENSE

solstice_material.c (22158B)


      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 "solstice.h"
     18 #include "solstice_c.h"
     19 
     20 #include <rsys/double33.h>
     21 #include <rsys/image.h>
     22 #include <solstice/ssol.h>
     23 
     24 struct dielectric_param {
     25   struct ssol_image* normal_map;
     26 };
     27 
     28 struct matte_param {
     29   struct ssol_data reflectivity;
     30   struct ssol_image* normal_map;
     31 };
     32 
     33 struct mirror_param {
     34   struct ssol_data reflectivity;
     35   struct ssol_data roughness;
     36   struct ssol_image* normal_map;
     37 };
     38 
     39 struct thin_dielectric_param {
     40   struct ssol_image* normal_map;
     41 };
     42 
     43 /*******************************************************************************
     44  * Helper functions
     45  ******************************************************************************/
     46 static void
     47 perturb_normal
     48   (const struct ssol_surface_fragment* frag,
     49    const struct ssol_image* normal_map,
     50    double normal[3])
     51 {
     52   double basis[9];
     53   double N[3];
     54   ASSERT(frag && normal_map && normal);
     55 
     56   SSOL(image_sample(normal_map, SSOL_FILTER_LINEAR, SSOL_ADDRESS_CLAMP,
     57     SSOL_ADDRESS_CLAMP, frag->uv, N));
     58 
     59   d3_set(basis+0, frag->dPdu);
     60   d3_set(basis+3, frag->dPdv);
     61   d3_set(basis+6, frag->Ns);
     62   d3_normalize(basis + 0, basis + 0);
     63   d3_normalize(basis + 3, basis + 3);
     64 
     65   d3_subd(N, d3_muld(N, N, 2), 1);
     66   d33_muld3(N, basis, N);
     67   d3_normalize(normal, N);
     68 }
     69 
     70 static enum ssol_microfacet_distribution
     71 solparser_to_ssol_ufacet_distrib
     72   (const enum solparser_microfacet_distribution distrib)
     73 {
     74   enum ssol_microfacet_distribution ufacet_distrib;
     75   switch(distrib) {
     76     case SOLPARSER_MICROFACET_BECKMANN:
     77       ufacet_distrib = SSOL_MICROFACET_BECKMANN;
     78       break;
     79     case SOLPARSER_MICROFACET_PILLBOX:
     80       ufacet_distrib = SSOL_MICROFACET_PILLBOX;
     81       break;
     82     default: FATAL("Unreachable code.\n"); break;
     83   }
     84   return ufacet_distrib;
     85 }
     86 
     87 static void
     88 mtl_get_normal
     89   (struct ssol_device* dev,
     90    struct ssol_param_buffer* buf,
     91    const double wavelength,
     92    const struct ssol_surface_fragment* frag,
     93    double* val)
     94 {
     95   (void)dev, (void)buf, (void)wavelength;
     96   d3_set(val, frag->Ns);
     97 }
     98 
     99 static void
    100 dielectric_get_normal
    101   (struct ssol_device* dev,
    102    struct ssol_param_buffer* buf,
    103    const double wavelength,
    104    const struct ssol_surface_fragment* frag,
    105    double* val)
    106 {
    107   const struct dielectric_param* param = ssol_param_buffer_get(buf);
    108   (void)dev, (void)buf, (void)wavelength;
    109   perturb_normal(frag, param->normal_map, val);
    110 }
    111 
    112 static void
    113 dielectric_param_release(void* mem)
    114 {
    115   struct dielectric_param* param = mem;
    116   ASSERT(param);
    117   if(param->normal_map) SSOL(image_ref_put(param->normal_map));
    118 }
    119 
    120 static void
    121 matte_get_reflectivity
    122   (struct ssol_device* dev,
    123    struct ssol_param_buffer* buf,
    124    const double wavelength,
    125    const struct ssol_surface_fragment* frag,
    126    double* val)
    127 {
    128   const struct matte_param* param = ssol_param_buffer_get(buf);
    129   (void)dev, (void)wavelength, (void)frag;
    130   *val = ssol_data_get_value(&param->reflectivity, wavelength);
    131 }
    132 
    133 static void
    134 matte_get_normal
    135   (struct ssol_device* dev,
    136    struct ssol_param_buffer* buf,
    137    const double wavelength,
    138    const struct ssol_surface_fragment* frag,
    139    double* val)
    140 {
    141   const struct matte_param* param = ssol_param_buffer_get(buf);
    142   (void)dev, (void)wavelength;
    143   perturb_normal(frag, param->normal_map, val);
    144 }
    145 
    146 static void
    147 matte_param_release(void* mem)
    148 {
    149   struct matte_param* param = mem;
    150   ASSERT(param);
    151   if(param->normal_map) SSOL(image_ref_put(param->normal_map));
    152   ssol_data_clear(&param->reflectivity);
    153 }
    154 
    155 static void
    156 mirror_get_reflectivity
    157   (struct ssol_device* dev,
    158    struct ssol_param_buffer* buf,
    159    const double wavelength,
    160    const struct ssol_surface_fragment* frag,
    161    double* val)
    162 {
    163   const struct mirror_param* param = ssol_param_buffer_get(buf);
    164   (void)dev, (void)wavelength, (void)frag;
    165   *val = ssol_data_get_value(&param->reflectivity, wavelength);
    166 }
    167 
    168 static void
    169 mirror_get_roughness
    170   (struct ssol_device* dev,
    171    struct ssol_param_buffer* buf,
    172    const double wavelength,
    173    const struct ssol_surface_fragment* frag,
    174    double* val)
    175 {
    176   const struct mirror_param* param = ssol_param_buffer_get(buf);
    177   (void)dev, (void)wavelength, (void)frag;
    178   *val = ssol_data_get_value(&param->roughness, wavelength);
    179 }
    180 
    181 static void
    182 mirror_get_normal
    183   (struct ssol_device* dev,
    184    struct ssol_param_buffer* buf,
    185    const double wavelength,
    186    const struct ssol_surface_fragment* frag,
    187    double* val)
    188 {
    189   const struct mirror_param* param = ssol_param_buffer_get(buf);
    190   (void)dev, (void)wavelength;
    191   perturb_normal(frag, param->normal_map, val);
    192 }
    193 
    194 static void
    195 mirror_param_release(void* mem)
    196 {
    197   struct mirror_param* param = mem;
    198   ASSERT(param);
    199   if(param->normal_map) SSOL(image_ref_put(param->normal_map));
    200   ssol_data_clear(&param->reflectivity);
    201   ssol_data_clear(&param->roughness);
    202 }
    203 
    204 static void
    205 thin_dielectric_get_normal
    206   (struct ssol_device* dev,
    207    struct ssol_param_buffer* buf,
    208    const double wavelength,
    209    const struct ssol_surface_fragment* frag,
    210    double* val)
    211 {
    212   const struct thin_dielectric_param* param = ssol_param_buffer_get(buf);
    213   (void)dev, (void)buf, (void)wavelength;
    214   perturb_normal(frag, param->normal_map, val);
    215 }
    216 
    217 static void
    218 thin_dielectric_param_release(void* mem)
    219 {
    220   struct thin_dielectric_param* param = mem;
    221   ASSERT(param);
    222   if(param->normal_map) SSOL(image_ref_put(param->normal_map));
    223 }
    224 
    225 static res_T
    226 load_image
    227   (struct solstice* solstice,
    228    const char* filename,
    229    struct ssol_image** out_ssol_img)
    230 {
    231   struct ssol_image* ssol_img = NULL;
    232   struct ssol_image_layout layout;
    233   struct image img;
    234   char* mem;
    235   size_t x, y;
    236   res_T res = RES_OK;
    237   ASSERT(solstice && filename && out_ssol_img);
    238 
    239   image_init(solstice->allocator, &img);
    240 
    241   res = image_read_ppm(&img, filename);
    242   if(res != RES_OK) {
    243     fprintf(stderr, "Could not load the PPM image `%s'.\n", filename);
    244     goto error;
    245   }
    246 
    247   res = ssol_image_create(solstice->ssol, &ssol_img);
    248   if(res != RES_OK) {
    249     fprintf(stderr, "Could not create the Solstice Solver image.\n");
    250     goto error;
    251   }
    252 
    253   res = ssol_image_setup(ssol_img, img.width, img.height, SSOL_PIXEL_DOUBLE3);
    254   if(res != RES_OK) {
    255     fprintf(stderr, "Could not setup the Solstice Solver image.\n");
    256     goto error;
    257   }
    258 
    259   SSOL(image_get_layout(ssol_img, &layout));
    260   SSOL(image_map(ssol_img, &mem));
    261 
    262   FOR_EACH(y, 0, layout.height) {
    263     char* dst_row = mem + layout.offset + y * layout.row_pitch;
    264     const char* src_row = img.pixels + y * img.pitch;
    265 
    266     FOR_EACH(x, 0, layout.width) {
    267       char* dst = dst_row + x*ssol_sizeof_pixel_format(layout.pixel_format);
    268       const char* src = src_row + x*sizeof_image_format(img.format);
    269       switch(img.format) {
    270         case IMAGE_RGB8:
    271           ((double*)dst)[0] = ((double)((uint8_t*)src)[0] + 0.5) / UINT8_MAX;
    272           ((double*)dst)[1] = ((double)((uint8_t*)src)[1] + 0.5) / UINT8_MAX;
    273           ((double*)dst)[2] = ((double)((uint8_t*)src)[2] + 0.5) / UINT8_MAX;
    274           break;
    275         case IMAGE_RGB16:
    276           ((double*)dst)[0] = ((double)((uint16_t*)src)[0] + 0.5) / UINT16_MAX;
    277           ((double*)dst)[1] = ((double)((uint16_t*)src)[1] + 0.5) / UINT16_MAX;
    278           ((double*)dst)[2] = ((double)((uint16_t*)src)[2] + 0.5) / UINT16_MAX;
    279           break;
    280         default: FATAL("Unreachable code.\n"); break;
    281       }
    282     }
    283   }
    284 
    285 exit:
    286   image_release(&img);
    287   *out_ssol_img = ssol_img;
    288   return res;
    289 error:
    290   if(ssol_img) SSOL(image_ref_put(ssol_img));
    291   goto exit;
    292 }
    293 
    294 static res_T
    295 create_material_dielectric
    296   (struct solstice* solstice,
    297    const struct solparser_material_dielectric* dielectric,
    298    struct ssol_material** out_mtl)
    299 {
    300   const struct solparser_medium* medium_i;
    301   const struct solparser_medium* medium_t;
    302   struct ssol_medium ssol_medium_i = SSOL_MEDIUM_VACUUM;
    303   struct ssol_medium ssol_medium_t = SSOL_MEDIUM_VACUUM;
    304   struct ssol_dielectric_shader shader = SSOL_DIELECTRIC_SHADER_NULL;
    305   struct ssol_image* img = NULL;
    306   struct ssol_material* mtl = NULL;
    307   struct ssol_param_buffer* pbuf = NULL;
    308   res_T res = RES_OK;
    309   ASSERT(solstice && dielectric && out_mtl);
    310 
    311   res = ssol_material_create_dielectric(solstice->ssol, &mtl);
    312   if(res != RES_OK) {
    313     fprintf(stderr,
    314       "Could not allocate the Solstice Solver dielectric material.\n");
    315     goto error;
    316   }
    317 
    318   medium_i = solparser_get_medium(solstice->parser, dielectric->medium_i);
    319   medium_t = solparser_get_medium(solstice->parser, dielectric->medium_t);
    320 
    321   if(!SOLPARSER_ID_IS_VALID(dielectric->normal_map)) {
    322     shader.normal = mtl_get_normal;
    323   } else {
    324     const struct solparser_image* image;
    325     struct dielectric_param* param = NULL;
    326 
    327     image = solparser_get_image(solstice->parser, dielectric->normal_map);
    328     res = load_image(solstice, str_cget(&image->filename), &img);
    329     if(res != RES_OK) goto error;
    330 
    331     res = ssol_param_buffer_create
    332       (solstice->ssol, sizeof(struct dielectric_param), &pbuf);
    333     if(res != RES_OK) {
    334       fprintf(stderr, "Could not create the Solstice Solver parameter buffer.\n");
    335       goto error;
    336     }
    337 
    338     param = ssol_param_buffer_allocate(pbuf, sizeof(struct dielectric_param),
    339       ALIGNOF(struct dielectric_param), dielectric_param_release);
    340     if(!param) {
    341       fprintf(stderr, "Could not allocate the dielectric parameter.\n");
    342       res = RES_MEM_ERR;
    343       goto error;
    344     }
    345     param->normal_map = img;
    346     shader.normal = dielectric_get_normal;
    347     SSOL(material_set_param_buffer(mtl, pbuf));
    348   }
    349 
    350   #define SET_SSOL_DATA(Medium, Name) {                                        \
    351     res = mtl_to_ssol_data(solstice, &Medium->Name, &ssol_## Medium.Name);     \
    352     if(res != RES_OK) goto error;                                              \
    353   } (void)0
    354   SET_SSOL_DATA(medium_i, refractive_index);
    355   SET_SSOL_DATA(medium_t, refractive_index);
    356   SET_SSOL_DATA(medium_i, extinction);
    357   SET_SSOL_DATA(medium_t, extinction);
    358   #undef SET_SSOL_DATA
    359   SSOL(dielectric_setup(mtl, &shader, &ssol_medium_i, &ssol_medium_t));
    360 
    361 exit:
    362   ssol_medium_clear(&ssol_medium_i);
    363   ssol_medium_clear(&ssol_medium_t);
    364   if(pbuf) SSOL(param_buffer_ref_put(pbuf));
    365   *out_mtl = mtl;
    366   return res;
    367 error:
    368   if(mtl) SSOL(material_ref_put(mtl)), mtl = NULL;
    369   if(img) SSOL(image_ref_put(img));
    370   goto exit;
    371 }
    372 
    373 static res_T
    374 create_material_matte
    375   (struct solstice* solstice,
    376    const struct solparser_material_matte* matte,
    377    struct ssol_material** out_mtl)
    378 {
    379   struct ssol_matte_shader shader = SSOL_MATTE_SHADER_NULL;
    380   struct ssol_image* img = NULL;
    381   struct ssol_material* mtl = NULL;
    382   struct ssol_param_buffer* pbuf = NULL;
    383   struct matte_param* param;
    384   res_T res = RES_OK;
    385   ASSERT(solstice && matte && out_mtl);
    386 
    387   res = ssol_material_create_matte(solstice->ssol, &mtl);
    388   if(res != RES_OK) {
    389     fprintf(stderr, "Could not create the Solstice Solver matte material.\n");
    390     goto error;
    391   }
    392 
    393   res = ssol_param_buffer_create
    394     (solstice->ssol, sizeof(struct matte_param), &pbuf);
    395   if(res != RES_OK) {
    396     fprintf(stderr, "Could not create the Solstice Solver parameter buffer.\n");
    397     goto error;
    398   }
    399 
    400   param = ssol_param_buffer_allocate(pbuf, sizeof(struct matte_param),
    401     ALIGNOF(struct matte_param), matte_param_release);
    402   if(!param) {
    403     fprintf(stderr, "Could not allocate the matte parameter.\n");
    404     res = RES_MEM_ERR;
    405     goto error;
    406   }
    407   memset(param, 0, sizeof(struct matte_param));
    408 
    409   res = mtl_to_ssol_data(solstice, &matte->reflectivity, &param->reflectivity);
    410   if(res != RES_OK) goto error;
    411 
    412   if(!SOLPARSER_ID_IS_VALID(matte->normal_map)) {
    413     shader.normal = mtl_get_normal;
    414   } else {
    415     const struct solparser_image* image;
    416     image = solparser_get_image(solstice->parser, matte->normal_map);
    417     res = load_image(solstice, str_cget(&image->filename), &img);
    418     if(res != RES_OK) goto error;
    419     param->normal_map = img;
    420     shader.normal = matte_get_normal;
    421   }
    422 
    423   shader.reflectivity = matte_get_reflectivity;
    424   SSOL(matte_setup(mtl, &shader));
    425   SSOL(material_set_param_buffer(mtl, pbuf));
    426 
    427 exit:
    428   if(pbuf) SSOL(param_buffer_ref_put(pbuf));
    429   *out_mtl = mtl;
    430   return res;
    431 error:
    432   if(mtl) SSOL(material_ref_put(mtl)), mtl = NULL;
    433   if(img) SSOL(image_ref_put(img));
    434   goto exit;
    435 }
    436 
    437 static res_T
    438 create_material_mirror
    439   (struct solstice* solstice,
    440    const struct solparser_material_mirror* mirror,
    441    struct ssol_material** out_mtl)
    442 {
    443   struct ssol_image* img = NULL;
    444   struct ssol_mirror_shader shader = SSOL_MIRROR_SHADER_NULL;
    445   struct ssol_material* mtl = NULL;
    446   struct ssol_param_buffer* pbuf = NULL;
    447   struct mirror_param* param;
    448   enum ssol_microfacet_distribution ufacet_distrib;
    449   res_T res = RES_OK;
    450   ASSERT(solstice && mirror && out_mtl);
    451 
    452   res = ssol_material_create_mirror(solstice->ssol, &mtl);
    453   if(res != RES_OK) {
    454     fprintf(stderr, "Could not create the Solstice Solver mirror material.\n");
    455     goto error;
    456   }
    457 
    458   res = ssol_param_buffer_create
    459     (solstice->ssol, sizeof(struct mirror_param), &pbuf);
    460   if(res != RES_OK) {
    461     fprintf(stderr, "Could not create the Solstice Solver parameter buffer.\n");
    462     goto error;
    463   }
    464 
    465   param = ssol_param_buffer_allocate(pbuf, sizeof(struct mirror_param),
    466     ALIGNOF(struct mirror_param), mirror_param_release);
    467   if(!param) {
    468     fprintf(stderr, "Could not allocate the mirror parameters.\n");
    469     res = RES_MEM_ERR;
    470     goto error;
    471   }
    472   memset(param, 0, sizeof(struct mirror_param));
    473 
    474   res = mtl_to_ssol_data(solstice, &mirror->reflectivity, &param->reflectivity);
    475   if(res != RES_OK) goto error;
    476 
    477   switch(mirror->ufacet_distrib) {
    478     case SOLPARSER_MICROFACET_BECKMANN:
    479       /* For the beckmann distribution, convert the slope error to the
    480        * corresponding beckmann rougness by multiplying it by sqrt(2) */
    481       res = scaled_mtl_to_ssol_data
    482         (solstice, &mirror->slope_error, sqrt(2), &param->roughness);
    483       break;
    484     case SOLPARSER_MICROFACET_PILLBOX:
    485       /* Direct correspondance between the solver roughness parameter and the
    486        * provided slope error */
    487       res = mtl_to_ssol_data(solstice, &mirror->slope_error, &param->roughness);
    488       break;
    489     default: FATAL("Unreachable code.\n"); break;
    490   }
    491 
    492   if(!SOLPARSER_ID_IS_VALID(mirror->normal_map)) {
    493     shader.normal = mtl_get_normal;
    494   } else {
    495     const struct solparser_image* image;
    496     image = solparser_get_image(solstice->parser, mirror->normal_map);
    497     res = load_image(solstice, str_cget(&image->filename), &img);
    498     if(res != RES_OK) goto error;
    499     param->normal_map = img;
    500     shader.normal = mirror_get_normal;
    501   }
    502 
    503   shader.reflectivity = mirror_get_reflectivity;
    504   shader.roughness = mirror_get_roughness;
    505 
    506   ufacet_distrib = solparser_to_ssol_ufacet_distrib(mirror->ufacet_distrib);
    507   SSOL(mirror_setup(mtl, &shader, ufacet_distrib));
    508   SSOL(material_set_param_buffer(mtl, pbuf));
    509 
    510 exit:
    511   if(pbuf) SSOL(param_buffer_ref_put(pbuf));
    512   *out_mtl = mtl;
    513   return res;
    514 error:
    515   if(mtl) SSOL(material_ref_put(mtl)), mtl = NULL;
    516   if(img) SSOL(image_ref_put(img));
    517   goto exit;
    518 }
    519 
    520 static res_T
    521 create_material_thin_dielectric
    522   (struct solstice* solstice,
    523    const struct solparser_material_thin_dielectric* thin,
    524    struct ssol_material** out_mtl)
    525 {
    526   struct ssol_image* img = NULL;
    527   struct ssol_thin_dielectric_shader shader = SSOL_THIN_DIELECTRIC_SHADER_NULL;
    528   const struct solparser_medium* medium_i = NULL;
    529   const struct solparser_medium* medium_t = NULL;
    530   struct ssol_medium ssol_medium_i = SSOL_MEDIUM_VACUUM;
    531   struct ssol_medium ssol_medium_t = SSOL_MEDIUM_VACUUM;
    532   struct ssol_material* mtl = NULL;
    533   struct ssol_param_buffer* pbuf = NULL;
    534   res_T res = RES_OK;
    535   ASSERT(solstice && thin && out_mtl);
    536 
    537   res = ssol_material_create_thin_dielectric(solstice->ssol, &mtl);
    538   if(res != RES_OK) {
    539     fprintf(stderr,
    540       "Could not allocate the Solstice Solver thin dielectric material.\n");
    541     goto error;
    542   }
    543 
    544   medium_i = solparser_get_medium(solstice->parser, thin->medium_i);
    545   medium_t = solparser_get_medium(solstice->parser, thin->medium_t);
    546 
    547   if(!SOLPARSER_ID_IS_VALID(thin->normal_map)) {
    548     shader.normal = mtl_get_normal;
    549   } else {
    550     const struct solparser_image* image;
    551     struct dielectric_param* param = NULL;
    552 
    553     image = solparser_get_image(solstice->parser, thin->normal_map);
    554     res = load_image(solstice, str_cget(&image->filename), &img);
    555     if(res != RES_OK) goto error;
    556 
    557     res = ssol_param_buffer_create
    558       (solstice->ssol, sizeof(struct thin_dielectric_param), &pbuf);
    559     if(res != RES_OK) {
    560       fprintf(stderr, "Could not create the Solsitce Solver parameter buffer.\n");
    561       goto error;
    562     }
    563 
    564     param = ssol_param_buffer_allocate(pbuf,
    565       sizeof(struct thin_dielectric_param),
    566       ALIGNOF(struct thin_dielectric_param),
    567       thin_dielectric_param_release);
    568     if(!param) {
    569       fprintf(stderr, "Could not allocate the thin dielectric parameter.\n");
    570       res = RES_MEM_ERR;
    571       goto error;
    572     }
    573     param->normal_map = img;
    574     shader.normal = thin_dielectric_get_normal;
    575     SSOL(material_set_param_buffer(mtl, pbuf));
    576   }
    577 
    578   #define SET_SSOL_DATA(Medium, Name) {                                        \
    579     res = mtl_to_ssol_data(solstice, &Medium->Name, &ssol_## Medium.Name);     \
    580     if(res != RES_OK) goto error;                                              \
    581   } (void)0
    582   SET_SSOL_DATA(medium_i, refractive_index);
    583   SET_SSOL_DATA(medium_t, refractive_index);
    584   SET_SSOL_DATA(medium_i, extinction);
    585   SET_SSOL_DATA(medium_t, extinction);
    586   #undef SET_SSOL_DATA
    587   SSOL(thin_dielectric_setup
    588     (mtl, &shader, &ssol_medium_i, &ssol_medium_t, thin->thickness));
    589 
    590 exit:
    591   ssol_medium_clear(&ssol_medium_i);
    592   ssol_medium_clear(&ssol_medium_t);
    593   if(pbuf) SSOL(param_buffer_ref_put(pbuf));
    594   *out_mtl = mtl;
    595   return res;
    596 error:
    597   if(mtl) SSOL(material_ref_put(mtl)), mtl = NULL;
    598   if(img) SSOL(image_ref_put(img));
    599   goto exit;
    600 }
    601 
    602 /*******************************************************************************
    603  * Local functions
    604  ******************************************************************************/
    605 res_T
    606 scaled_mtl_to_ssol_data
    607   (struct solstice* solstice,
    608    const struct solparser_mtl_data* mtl_data,
    609    const double scale_factor,
    610    struct ssol_data* data)
    611 {
    612   struct ssol_spectrum* spectrum = NULL;
    613   res_T res = RES_OK;
    614   ASSERT(mtl_data && data);
    615 
    616   ssol_data_clear(data);
    617   switch(mtl_data->type) {
    618     case SOLPARSER_MTL_DATA_REAL:
    619       ssol_data_set_real(data, mtl_data->value.real*scale_factor);
    620       break;
    621     case SOLPARSER_MTL_DATA_SPECTRUM:
    622       res = solstice_create_scaled_ssol_spectrum
    623         (solstice, mtl_data->value.spectrum, scale_factor, &spectrum);
    624       if(res != RES_OK) goto error;
    625       ssol_data_set_spectrum(data, spectrum);
    626       break;
    627     default: FATAL("Unreachable code.\n"); break;
    628   }
    629 
    630 exit:
    631   if(spectrum) SSOL(spectrum_ref_put(spectrum));
    632   return res;
    633 error:
    634   ssol_data_clear(data);
    635   goto exit;
    636 }
    637 
    638 extern LOCAL_SYM res_T
    639 mtl_to_ssol_data
    640   (struct solstice* solstice,
    641    const struct solparser_mtl_data* mtl_data,
    642    struct ssol_data* data)
    643 {
    644   return scaled_mtl_to_ssol_data(solstice, mtl_data, 1.0, data);
    645 }
    646 
    647 res_T
    648 solstice_create_ssol_material
    649   (struct solstice* solstice,
    650    const struct solparser_material_id mtl_id,
    651    struct ssol_material** out_ssol_mtl)
    652 {
    653   const struct solparser_material* mtl;
    654   struct ssol_material* ssol_mtl = NULL;
    655   struct ssol_material** pssol_mtl = NULL;
    656   res_T res = RES_OK;
    657   ASSERT(solstice);
    658 
    659   mtl = solparser_get_material(solstice->parser, mtl_id);
    660   ASSERT(mtl);
    661 
    662   if(mtl->type == SOLPARSER_MATERIAL_VIRTUAL) {
    663     /* Use the global solstice virtual material */
    664     ssol_mtl = solstice->mtl_virtual;
    665   } else {
    666     pssol_mtl = htable_material_find(&solstice->materials, &mtl_id.i);
    667     if(pssol_mtl) {
    668       ssol_mtl = *pssol_mtl;
    669     } else {
    670       const struct solparser_material_dielectric* dielectric;
    671       const struct solparser_material_matte* matte;
    672       const struct solparser_material_mirror* mirror;
    673       const struct solparser_material_thin_dielectric* thin_dielectric;
    674 
    675       switch(mtl->type) {
    676         case SOLPARSER_MATERIAL_DIELECTRIC:
    677           dielectric = solparser_get_material_dielectric
    678             (solstice->parser, mtl->data.dielectric);
    679           res = create_material_dielectric(solstice, dielectric, &ssol_mtl);
    680           break;
    681         case SOLPARSER_MATERIAL_MATTE:
    682           matte = solparser_get_material_matte
    683             (solstice->parser, mtl->data.matte);
    684           res = create_material_matte(solstice, matte, &ssol_mtl);
    685           break;
    686         case SOLPARSER_MATERIAL_MIRROR:
    687           mirror = solparser_get_material_mirror
    688             (solstice->parser, mtl->data.mirror);
    689           res = create_material_mirror(solstice, mirror, &ssol_mtl);
    690           break;
    691         case SOLPARSER_MATERIAL_THIN_DIELECTRIC:
    692           thin_dielectric = solparser_get_material_thin_dielectric
    693             (solstice->parser, mtl->data.thin_dielectric);
    694           res = create_material_thin_dielectric
    695             (solstice, thin_dielectric, &ssol_mtl);
    696           break;
    697         default: FATAL("Unreachable code.\n"); break;
    698       }
    699       if(res != RES_OK) goto error;
    700 
    701       /* Cache the created material for future use. */
    702       res = htable_material_set(&solstice->materials, &mtl_id.i, &ssol_mtl);
    703       if(res != RES_OK) {
    704         fprintf(stderr, "Could not register the material into solstice.\n");
    705         goto error;
    706       }
    707     }
    708   }
    709 
    710   /* Get an additional reference onto the material in order to give to the
    711    * caller an ownership onto the returned material. */
    712   SSOL(material_ref_get(ssol_mtl));
    713 
    714 exit:
    715   *out_ssol_mtl = ssol_mtl;
    716   return res;
    717 error:
    718   if(ssol_mtl) SSOL(material_ref_put(ssol_mtl));
    719   goto exit;
    720 }
    721