solstice

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

commit bc27763de2137ee5ad2e83e8affe98a66ca10f5d
parent 3519ceab269d0cb68477b7cbda120100faf1394d
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Tue, 18 Apr 2017 14:21:13 +0200

Merge branch 'feature_normal_map' into develop

Diffstat:
Mcmake/parser/CMakeLists.txt | 3+++
Mdoc/input | 11+++++++++--
Msrc/parser/solparser.c | 12++++++++++++
Msrc/parser/solparser.h | 38+++++++++++++++++++++++---------------
Msrc/parser/solparser_c.h | 54++++++++++++++++++++++++++++++++++++++----------------
Asrc/parser/solparser_image.c | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/parser/solparser_image.h | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/parser/solparser_material.c | 45+++++++++++++++++++++++++++++++--------------
Msrc/parser/solparser_material.h | 45+++++++++++++++++++++++++++++++++++++++++++++
Msrc/parser/test_solparser8.c | 2+-
Asrc/parser/test_solparser_normal_map.c | 335+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/parser/yaml/test_ko_0.yaml | 21++++++++++++++++++++-
Msrc/parser/yaml/test_ok_2.yaml | 13+++++++++++++
Msrc/solstice_draw.c | 43++++++++++++++++++++++++++-----------------
Msrc/solstice_material.c | 335++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/solstice_object.c | 18++++++++++++++++--
16 files changed, 1041 insertions(+), 103 deletions(-)

diff --git a/cmake/parser/CMakeLists.txt b/cmake/parser/CMakeLists.txt @@ -32,6 +32,7 @@ include_directories( set(SOLPARSER_FILES_SRC solparser.c solparser_entity.c + solparser_image.c solparser_geometry.c solparser_material.c solparser_pivot.c @@ -42,6 +43,7 @@ set(SOLPARSER_FILES_INC solparser_c.h solparser_entity.h solparser_geometry.h + solparser_image.h solparser_material.h solparser_pivot.h solparser_shape.h @@ -103,6 +105,7 @@ if(NOT NO_TEST) new_test(test_solparser6) new_test(test_solparser7) new_test(test_solparser8) + new_test(test_solparser_normal_map) rcmake_copy_runtime_libraries(test_solparser) endif() diff --git a/doc/input b/doc/input @@ -225,15 +225,18 @@ dielectric: medium_i: <dielectric-medium> medium_t: <dielectric-medium> +[ <normal-map> ] <matte> ::= matte: reflectivity: REAL # in [0, 1] +[ <normal-map> ] <mirror> ::= mirror: reflectivity: REAL # in [0, 1] roughness: REAL # in [0, 1] +[ <normal-map> ] <virtual> ::= virtual: EMPTY-STRING @@ -243,10 +246,14 @@ thickness: REAL # in [0, INF) medium_i: <dielectric-medium> medium_t: <dielectric-medium> +[ <normal-map> ] <dielectric-medium> ::= - refractive_index: REAL # in ]0, INF) - absorptivity: REAL # in [0, INF) + refractive_index: REAL # in ]0, INF) + absorptivity: REAL # in [0, INF) + +<normal-map> ::= + normal_map: { path: PATH } #---------------------------------------- <entity> ::= diff --git a/src/parser/solparser.c b/src/parser/solparser.c @@ -186,6 +186,7 @@ parser_clear(struct solparser* parser) /* Materials */ htable_yaml2sols_clear(&parser->yaml2mtls); + darray_image_clear(&parser->images); darray_material_clear(&parser->mtls); darray_material2_clear(&parser->mtls2); darray_medium_clear(&parser->mediums); @@ -240,6 +241,7 @@ parser_release(ref_T* ref) /* Materials */ htable_yaml2sols_release(&parser->yaml2mtls); + darray_image_release(&parser->images); darray_material_release(&parser->mtls); darray_material2_release(&parser->mtls2); darray_medium_release(&parser->mediums); @@ -568,6 +570,7 @@ solparser_create /* Materials */ htable_yaml2sols_init(mem_allocator, &parser->yaml2mtls); + darray_image_init(mem_allocator, &parser->images); darray_material_init(mem_allocator, &parser->mtls); darray_material2_init(mem_allocator, &parser->mtls2); darray_medium_init(mem_allocator, &parser->mediums); @@ -882,6 +885,15 @@ solparser_get_entity return darray_entity_cdata_get(&parser->entities) + entity.i; } +const struct solparser_image* +solparser_get_image + (const struct solparser* parser, + const struct solparser_image_id image) +{ + ASSERT(parser && image.i < darray_image_size_get(&parser->images)); + return darray_image_cdata_get(&parser->images) + image.i; +} + const struct solparser_geometry* solparser_get_geometry (const struct solparser* parser, diff --git a/src/parser/solparser.h b/src/parser/solparser.h @@ -18,10 +18,13 @@ #include "solparser_entity.h" #include <rsys/rsys.h> +#include <stddef.h> struct mem_allocator; struct solparser; +#define SOLPARSER_ID_IS_VALID(Id) ((Id).i != SIZE_MAX) + struct solparser_entity_iterator { struct htable_str2sols_iterator it__; /* Internal data */ }; @@ -87,16 +90,16 @@ solparser_get_entity (const struct solparser* parser, const struct solparser_entity_id entity); +extern LOCAL_SYM const struct solparser_image* +solparser_get_image + (const struct solparser* parser, + const struct solparser_image_id image); + extern LOCAL_SYM const struct solparser_geometry* solparser_get_geometry (const struct solparser* parser, const struct solparser_geometry_id geom); -extern LOCAL_SYM const struct solparser_medium* -solparser_get_medium - (const struct solparser* parser, - const struct solparser_medium_id medium); - extern LOCAL_SYM const struct solparser_material* solparser_get_material (const struct solparser* parser, @@ -127,21 +130,16 @@ solparser_get_material_thin_dielectric (const struct solparser* parser, const struct solparser_material_thin_dielectric_id thin_dielectric); +extern LOCAL_SYM const struct solparser_medium* +solparser_get_medium + (const struct solparser* parser, + const struct solparser_medium_id medium); + extern LOCAL_SYM const struct solparser_object* solparser_get_object (const struct solparser* parser, const struct solparser_object_id obj); -extern LOCAL_SYM const struct solparser_x_pivot* -solparser_get_x_pivot - (const struct solparser* parser, - const struct solparser_pivot_id x_pivot); - -extern LOCAL_SYM const struct solparser_zx_pivot* -solparser_get_zx_pivot - (const struct solparser* parser, - const struct solparser_pivot_id zx_pivot); - extern LOCAL_SYM const struct solparser_shape* solparser_get_shape (const struct solparser* parser, @@ -196,6 +194,16 @@ extern LOCAL_SYM const struct solparser_sun* solparser_get_sun (const struct solparser* parser); +extern LOCAL_SYM const struct solparser_x_pivot* +solparser_get_x_pivot + (const struct solparser* parser, + const struct solparser_pivot_id x_pivot); + +extern LOCAL_SYM const struct solparser_zx_pivot* +solparser_get_zx_pivot + (const struct solparser* parser, + const struct solparser_pivot_id zx_pivot); + /******************************************************************************* * Entity interator ******************************************************************************/ diff --git a/src/parser/solparser_c.h b/src/parser/solparser_c.h @@ -18,6 +18,7 @@ #include "solparser.h" #include "solparser_entity.h" +#include "solparser_image.h" #include "solparser_material.h" #include "solparser_pivot.h" #include "solparser_shape.h" @@ -48,21 +49,25 @@ struct target_alias { /* Declare the array of dielectric materials */ #define DARRAY_NAME dielectric #define DARRAY_DATA struct solparser_material_dielectric +#define DARRAY_FUNCTOR_INIT solparser_material_dielectric_init #include <rsys/dynamic_array.h> /* Declare the array of matte materials */ #define DARRAY_NAME matte #define DARRAY_DATA struct solparser_material_matte +#define DARRAY_FUNCTOR_INIT solparser_material_matte_init #include <rsys/dynamic_array.h> /* Declare the array of mirror materials */ #define DARRAY_NAME mirror #define DARRAY_DATA struct solparser_material_mirror +#define DARRAY_FUNCTOR_INIT solparser_material_mirror_init #include <rsys/dynamic_array.h> /* Declare the array of thin_dielectric materials */ #define DARRAY_NAME thin_dielectric #define DARRAY_DATA struct solparser_material_thin_dielectric +#define DARRAY_FUNCTOR_INIT solparser_material_thin_dielectric_init #include <rsys/dynamic_array.h> /* Declare the array of materials */ @@ -90,6 +95,15 @@ struct target_alias { #define DARRAY_DATA struct solparser_shape_cylinder #include <rsys/dynamic_array.h> +/* Declare the array of images */ +#define DARRAY_NAME image +#define DARRAY_DATA struct solparser_image +#define DARRAY_FUNCTOR_INIT solparser_image_init +#define DARRAY_FUNCTOR_RELEASE solparser_image_release +#define DARRAY_FUNCTOR_COPY solparser_image_copy +#define DARRAY_FUNCTOR_COPY_AND_RELEASE solparser_image_copy_and_release +#include <rsys/dynamic_array.h> + /* Declare the array of imported geometries */ #define DARRAY_NAME impgeom #define DARRAY_DATA struct solparser_shape_imported_geometry @@ -192,6 +206,7 @@ struct solparser { /* Materia data */ struct htable_yaml2sols yaml2mtls; /* Cache of materials */ + struct darray_image images; struct darray_material mtls; struct darray_material2 mtls2; /* Double sided materials */ struct darray_medium mediums; @@ -328,13 +343,6 @@ parse_transform * Main parsing functions ******************************************************************************/ extern LOCAL_SYM res_T -parse_material - (struct solparser* parser, - yaml_document_t* doc, - yaml_node_t* mtl, - struct solparser_material_double_sided_id* out_imtl2); - -extern LOCAL_SYM res_T parse_entity (struct solparser* parser, yaml_document_t* doc, @@ -343,6 +351,13 @@ parse_entity struct solparser_entity_id* out_isolent); extern LOCAL_SYM res_T +parse_image + (struct solparser* parser, + yaml_document_t* doc, + const yaml_node_t* image, + struct solparser_image_id* out_img); + +extern LOCAL_SYM res_T parse_focals_description (struct solparser* parser, yaml_document_t* doc, @@ -357,18 +372,18 @@ parse_geometry struct solparser_geometry_id* out_isolgeom); extern LOCAL_SYM res_T -parse_x_pivot +parse_material (struct solparser* parser, yaml_document_t* doc, - const yaml_node_t* x_pivot, - struct solparser_pivot_id* out_isolpivot); + yaml_node_t* mtl, + struct solparser_material_double_sided_id* out_imtl2); extern LOCAL_SYM res_T -parse_zx_pivot +parse_spectrum (struct solparser* parser, yaml_document_t* doc, - const yaml_node_t* zx_pivot, - struct solparser_pivot_id* out_isolpivot); + const yaml_node_t* spectrum, + struct darray_spectrum_data* data); extern LOCAL_SYM res_T parse_sun @@ -378,10 +393,17 @@ parse_sun struct solparser_sun** out_solsun); extern LOCAL_SYM res_T -parse_spectrum +parse_x_pivot (struct solparser* parser, yaml_document_t* doc, - const yaml_node_t* spectrum, - struct darray_spectrum_data* data); + const yaml_node_t* x_pivot, + struct solparser_pivot_id* out_isolpivot); + +extern LOCAL_SYM res_T +parse_zx_pivot + (struct solparser* parser, + yaml_document_t* doc, + const yaml_node_t* zx_pivot, + struct solparser_pivot_id* out_isolpivot); #endif /* SOLPARSER_C_H */ diff --git a/src/parser/solparser_image.c b/src/parser/solparser_image.c @@ -0,0 +1,107 @@ +/* Copyright (C) CNRS 2016-2017 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "solparser_c.h" + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +parse_image + (struct solparser* parser, + yaml_document_t* doc, + const yaml_node_t* img, + struct solparser_image_id* out_iimg) +{ + enum { PATH }; + struct solparser_image* solimg = NULL; + size_t isolimg = SIZE_MAX; + intptr_t i, n; + int mask = 0; /* Register the parsed attributes */ + res_T res = RES_OK; + ASSERT(parser && doc && img &&out_iimg); + + if(img->type != YAML_MAPPING_NODE) { + log_err(parser, img, "expect a mapping of image parameters.\n"); + res = RES_BAD_ARG; + goto error; + } + + /* Allocate an image */ + isolimg = darray_image_size_get(&parser->images); + res = darray_image_resize(&parser->images, isolimg + 1); + if(res != RES_OK) { + log_err(parser, img, "could not allocate the image.\n"); + goto error; + } + solimg = darray_image_data_get(&parser->images) + isolimg; + + n = img->data.mapping.pairs.top - img->data.mapping.pairs.start; + FOR_EACH(i, 0, n) { + yaml_node_t* key; + yaml_node_t* val; + + key = yaml_document_get_node(doc, img->data.mapping.pairs.start[i].key); + val = yaml_document_get_node(doc, img->data.mapping.pairs.start[i].value); + if(key->type != YAML_SCALAR_NODE) { + log_err(parser, key, "expect image parameters.\n"); + goto error; + } + #define SETUP_MASK(Flag, Name) { \ + if(mask & BIT(Flag)) { \ + log_err(parser, key, \ + "the image parameter `"Name"' is already defined.\n"); \ + res = RES_BAD_ARG; \ + goto error; \ + } \ + mask |= BIT(Flag); \ + } (void)0 + if(!strcmp((char*)key->data.scalar.value, "path")) { + SETUP_MASK(PATH, "path"); + res = parse_string(parser, val, &solimg->filename); + } else { + log_err(parser, key, "unknown image parameter `%s'.\n", + key->data.scalar.value); + res = RES_BAD_ARG; + goto error; + } + if(res != RES_OK) { + log_node(parser, key); + goto error; + } + #undef SETUP_MASK + } + + #define CHECK_PARAM(Flag, Name) \ + if(!(mask & BIT(Flag))) { \ + log_err(parser, img, \ + "the image parameter `"Name"' is missing.\n"); \ + res = RES_BAD_ARG; \ + goto error; \ + } (void)0 + CHECK_PARAM(PATH, "path"); + #undef CHECK_PARAM + +exit: + out_iimg->i = isolimg; + return res; +error: + if(solimg) { + darray_image_pop_back(&parser->images); + isolimg = SIZE_MAX; + } + goto exit; +} + diff --git a/src/parser/solparser_image.h b/src/parser/solparser_image.h @@ -0,0 +1,62 @@ +/* Copyright (C) CNRS 2016-2017 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef SOLPARSER_IMAGE_H +#define SOLPARSER_IMAGE_H + +#include <rsys/str.h> + +struct solparser_image { + struct str filename; +}; + +struct solparser_image_id { size_t i; }; + +static INLINE void +solparser_image_init + (struct mem_allocator* allocator, + struct solparser_image* img) +{ + ASSERT(img); + str_init(allocator, &img->filename); +} + +static INLINE void +solparser_image_release(struct solparser_image* img) +{ + ASSERT(img); + str_release(&img->filename); +} + +static INLINE res_T +solparser_image_copy + (struct solparser_image* dst, + const struct solparser_image* src) +{ + ASSERT(dst && src); + return str_copy(&dst->filename, &src->filename); +} + +static INLINE res_T +solparser_image_copy_and_release + (struct solparser_image* dst, + struct solparser_image* src) +{ + ASSERT(dst && src); + return str_copy_and_release(&dst->filename, &src->filename); +} + +#endif /* SOLPARSER_IMAGE_H */ + diff --git a/src/parser/solparser_material.c b/src/parser/solparser_material.c @@ -120,7 +120,7 @@ parse_material_dielectric const yaml_node_t* dielec, struct solparser_material_dielectric_id* out_imtl) { - enum { MEDIUM_I, MEDIUM_T }; + enum { MEDIUM_I, MEDIUM_T, NORMAL_MAP }; struct solparser_material_dielectric* mtl = NULL; size_t imtl = SIZE_MAX; int mask = 0; /* Register the parsed attributes */ @@ -166,7 +166,10 @@ parse_material_dielectric } \ mask |= BIT(Flag); \ } (void)0 - if(!strcmp((char*)key->data.scalar.value, "medium_i")) { + if(!strcmp((char*)key->data.scalar.value, "normal_map")) { + SETUP_MASK(NORMAL_MAP, "normal_map"); + res = parse_image(parser, doc, val, &mtl->normal_map); + } else if(!strcmp((char*)key->data.scalar.value, "medium_i")) { SETUP_MASK(MEDIUM_I, "medium_i"); res = parse_medium(parser, doc, val, &mtl->medium_i); } else if(!strcmp((char*)key->data.scalar.value, "medium_t")) { @@ -214,7 +217,7 @@ parse_material_matte const yaml_node_t* matte, struct solparser_material_matte_id* out_imtl) { - enum { REFLECTIVITY }; + enum { NORMAL_MAP, REFLECTIVITY }; struct solparser_material_matte* mtl = NULL; size_t imtl = SIZE_MAX; intptr_t i, n; @@ -250,13 +253,20 @@ parse_material_matte goto error; } - if(!strcmp((char*)key->data.scalar.value, "reflectivity")) { - if(mask & BIT(REFLECTIVITY)) { - log_err(parser, key, "the matte reflectivity is already defined.\n"); - res = RES_BAD_ARG; - goto error; - } - mask |= BIT(REFLECTIVITY); + #define SETUP_MASK(Flag, Name) { \ + if(mask & BIT(Flag)) { \ + log_err(parser, key, \ + "the "Name" of the matte material is already defined.\n"); \ + res = RES_BAD_ARG; \ + goto error; \ + } \ + mask |= BIT(Flag); \ + } (void)0 + if(!strcmp((char*)key->data.scalar.value, "normal_map")) { + SETUP_MASK(NORMAL_MAP, "normal_map"); + res = parse_image(parser, doc, val, &mtl->normal_map); + } else if(!strcmp((char*)key->data.scalar.value, "reflectivity")) { + SETUP_MASK(REFLECTIVITY, "reflectivity"); res = parse_real(parser, val, 0, 1, &mtl->reflectivity); } else { log_err(parser, key, "unknown matte parameter `%s'.\n", @@ -268,6 +278,7 @@ parse_material_matte log_node(parser, key); goto error; } + #undef SETUP_MASK } if(!(mask & BIT(REFLECTIVITY))) { @@ -294,7 +305,7 @@ parse_material_mirror const yaml_node_t* mirror, struct solparser_material_mirror_id* out_imtl) { - enum { REFLECTIVITY, ROUGHNESS }; + enum { NORMAL_MAP, REFLECTIVITY, ROUGHNESS }; struct solparser_material_mirror* mtl = NULL; size_t imtl = SIZE_MAX; int mask = 0; /* Register the parsed attributes */ @@ -340,7 +351,10 @@ parse_material_mirror } \ mask |= BIT(Flag); \ } (void)0 - if(!strcmp((char*)key->data.scalar.value, "reflectivity")) { + if(!strcmp((char*)key->data.scalar.value, "normal_map")) { + SETUP_MASK(NORMAL_MAP, "normal_map"); + res = parse_image(parser, doc, val, &mtl->normal_map); + } else if(!strcmp((char*)key->data.scalar.value, "reflectivity")) { SETUP_MASK(REFLECTIVITY, "reflectivity"); res = parse_real(parser, val, 0, 1, &mtl->reflectivity); } else if(!strcmp((char*)key->data.scalar.value, "roughness")) { @@ -387,7 +401,7 @@ parse_material_thin_dielectric yaml_node_t* thin, struct solparser_material_thin_dielectric_id* out_imtl) { - enum { MEDIUM_I, MEDIUM_T, THICKNESS }; + enum { MEDIUM_I, MEDIUM_T, NORMAL_MAP, THICKNESS }; struct solparser_material_thin_dielectric* mtl = NULL; size_t imtl = SIZE_MAX; int mask = 0; /* Register the parsed attributes */ @@ -434,7 +448,10 @@ parse_material_thin_dielectric } \ mask |= BIT(Flag); \ } (void)0 - if(!strcmp((char*)key->data.scalar.value, "medium_i")) { + if(!strcmp((char*)key->data.scalar.value, "normal_map")) { + SETUP_MASK(NORMAL_MAP, "normal_map"); + res = parse_image(parser, doc, val, &mtl->normal_map); + } else if(!strcmp((char*)key->data.scalar.value, "medium_i")) { SETUP_MASK(MEDIUM_I, "medium_i"); res = parse_medium(parser, doc, val, &mtl->medium_i); } else if(!strcmp((char*)key->data.scalar.value, "medium_t")) { diff --git a/src/parser/solparser_material.h b/src/parser/solparser_material.h @@ -16,6 +16,7 @@ #ifndef SOLPARSER_MATERIAL_H #define SOLPARSER_MATERIAL_H +#include "solparser_image.h" #include <stddef.h> enum solparser_material_type { @@ -36,31 +37,75 @@ struct solparser_medium_id { size_t i; }; struct solparser_material_dielectric { struct solparser_medium_id medium_i; /* Medium the material "looks at" */ struct solparser_medium_id medium_t; /* Opposite medium */ + struct solparser_image_id normal_map; }; struct solparser_material_dielectric_id { size_t i; }; +static INLINE void +solparser_material_dielectric_init + (struct mem_allocator* allocator, + struct solparser_material_dielectric* dielectric) +{ + ASSERT(dielectric); + (void)allocator; + dielectric->normal_map.i = SIZE_MAX; +} + struct solparser_material_matte { double reflectivity; /* In [0, 1] */ + struct solparser_image_id normal_map; }; struct solparser_material_matte_id { size_t i; }; +static INLINE void +solparser_material_matte_init + (struct mem_allocator* allocator, + struct solparser_material_matte* matte) +{ + ASSERT(matte); + (void)allocator; + matte->normal_map.i = SIZE_MAX; +} + struct solparser_material_mirror { double roughness; /* In [0, 1] */ double reflectivity; /* In [0, 1] */ + struct solparser_image_id normal_map; }; struct solparser_material_mirror_id { size_t i; }; +static INLINE void +solparser_material_mirror_init + (struct mem_allocator* allocator, + struct solparser_material_mirror* mirror) +{ + ASSERT(mirror); + (void)allocator; + mirror->normal_map.i = SIZE_MAX; +} + struct solparser_material_thin_dielectric { struct solparser_medium_id medium_i; /* Outside medium */ struct solparser_medium_id medium_t; /* Medium of the slab */ + struct solparser_image_id normal_map; double thickness; }; struct solparser_material_thin_dielectric_id { size_t i; }; +static INLINE void +solparser_material_thin_dielectric_init + (struct mem_allocator* allocator, + struct solparser_material_thin_dielectric* thin) +{ + ASSERT(thin); + (void)allocator; + thin->normal_map.i = SIZE_MAX; +} + struct solparser_material { enum solparser_material_type type; union { diff --git a/src/parser/test_solparser8.c b/src/parser/test_solparser8.c @@ -37,7 +37,7 @@ main(int argc, char** argv) (void)argc, (void)argv; CHECK(mem_init_proxy_allocator(&allocator, &mem_default_allocator), RES_OK); - solparser_create(&allocator, &parser); + CHECK(solparser_create(&allocator, &parser), RES_OK); stream = tmpfile(); NCHECK(stream, NULL); diff --git a/src/parser/test_solparser_normal_map.c b/src/parser/test_solparser_normal_map.c @@ -0,0 +1,335 @@ +/* Copyright (C) CNRS 2016-2017 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "solparser.h" +#include "test_solstice_utils.h" + +static void +test_dielectric(struct solparser* parser) +{ + struct solparser_entity_iterator it, end; + struct solparser_entity_id entity_id; + struct solparser_object_id obj_id; + const struct solparser_entity* entity; + const struct solparser_image* img; + const struct solparser_geometry* geom; + const struct solparser_material_double_sided* mtl2; + const struct solparser_material* mtl; + const struct solparser_material_dielectric* dielectric; + const struct solparser_object* obj; + const struct solparser_shape* shape; + FILE* stream; + + stream = tmpfile(); + NCHECK(stream, NULL); + + fprintf(stream, "- sun: {dni: 1, spectrum: [{wavelength: 1, data: 1} ]}\n"); + fprintf(stream, "\n"); + fprintf(stream, "- material: &glass\n"); + fprintf(stream, " front:\n"); + fprintf(stream, " dielectric:\n"); + fprintf(stream, " medium_i: &out {refractive_index: 1, absorptivity: 0}\n"); + fprintf(stream, " medium_t: &in {refractive_index: 1.5, absorptivity: 20}\n"); + fprintf(stream, " normal_map: {path: my_normal_map}\n"); + fprintf(stream, " back: {dielectric: {medium_i: *in, medium_t: *out}}\n"); + fprintf(stream, "\n"); + fprintf(stream, "- entity:\n"); + fprintf(stream, " name: foo\n"); + fprintf(stream, " primary: 1\n"); + fprintf(stream, " geometry:\n"); + fprintf(stream, " - cylinder: { radius: 2, height: 2 }\n"); + fprintf(stream, " material: *glass\n"); + rewind(stream); + + CHECK(solparser_setup(parser, NULL, stream), RES_OK); + CHECK(solparser_load(parser), RES_OK); + + solparser_entity_iterator_begin(parser, &it); + solparser_entity_iterator_end(parser, &end); + CHECK(solparser_entity_iterator_eq(&it, &end), 0); + + entity_id = solparser_entity_iterator_get(&it); + entity = solparser_get_entity(parser, entity_id); + + CHECK(strcmp("foo", str_cget(&entity->name)), 0); + CHECK(solparser_entity_get_children_count(entity), 0); + CHECK(entity->type, SOLPARSER_ENTITY_GEOMETRY); + CHECK(entity->primary, 1); + geom = solparser_get_geometry(parser, entity->data.geometry); + CHECK(solparser_geometry_get_objects_count(geom), 1); + obj_id = solparser_geometry_get_object(geom, 0); + obj = solparser_get_object(parser, obj_id); + shape = solparser_get_shape(parser, obj->shape); + CHECK(shape->type, SOLPARSER_SHAPE_CYLINDER); + mtl2 = solparser_get_material_double_sided(parser, obj->mtl2); + NCHECK(mtl2->front.i, mtl2->back.i); + + mtl = solparser_get_material(parser, mtl2->front); + CHECK(mtl->type, SOLPARSER_MATERIAL_DIELECTRIC); + dielectric = solparser_get_material_dielectric(parser, mtl->data.dielectric); + CHECK(SOLPARSER_ID_IS_VALID(dielectric->normal_map), 1); + img = solparser_get_image(parser, dielectric->normal_map); + CHECK(strcmp(str_cget(&img->filename), "my_normal_map"), 0); + + mtl = solparser_get_material(parser, mtl2->back); + CHECK(mtl->type, SOLPARSER_MATERIAL_DIELECTRIC); + dielectric = solparser_get_material_dielectric(parser, mtl->data.dielectric); + CHECK(SOLPARSER_ID_IS_VALID(dielectric->normal_map), 0); + + solparser_entity_iterator_next(&it); + CHECK(solparser_entity_iterator_eq(&it, &end), 1); + + CHECK(solparser_load(parser), RES_BAD_OP); + fclose(stream); +} + +static void +test_matte(struct solparser* parser) +{ + struct solparser_entity_iterator it, end; + struct solparser_entity_id entity_id; + struct solparser_object_id obj_id; + const struct solparser_entity* entity; + const struct solparser_image* img; + const struct solparser_geometry* geom; + const struct solparser_material_double_sided* mtl2; + const struct solparser_material* mtl; + const struct solparser_material_matte* matte; + const struct solparser_object* obj; + const struct solparser_shape* shape; + FILE* stream; + + stream = tmpfile(); + NCHECK(stream, NULL); + + fprintf(stream, "- sun: { dni: 1, spectrum: [{wavelength: 1, data: 1} ] }\n"); + fprintf(stream, "- entity:\n"); + fprintf(stream, " name: test\n"); + fprintf(stream, " primary: 0\n"); + fprintf(stream, " geometry:\n"); + fprintf(stream, " - sphere: { radius: 1 }\n"); + fprintf(stream, " material:\n"); + fprintf(stream, " matte:\n"); + fprintf(stream, " reflectivity: 0.123\n"); + fprintf(stream, " normal_map: { path: \"path to normal map\" }\n"); + rewind(stream); + + CHECK(solparser_setup(parser, NULL, stream), RES_OK); + CHECK(solparser_load(parser), RES_OK); + + solparser_entity_iterator_begin(parser, &it); + solparser_entity_iterator_end(parser, &end); + CHECK(solparser_entity_iterator_eq(&it, &end), 0); + + entity_id = solparser_entity_iterator_get(&it); + entity = solparser_get_entity(parser, entity_id); + + CHECK(strcmp("test", str_cget(&entity->name)), 0); + CHECK(solparser_entity_get_children_count(entity), 0); + CHECK(entity->primary, 0); + CHECK(entity->type, SOLPARSER_ENTITY_GEOMETRY); + geom = solparser_get_geometry(parser, entity->data.geometry); + CHECK(solparser_geometry_get_objects_count(geom), 1); + obj_id = solparser_geometry_get_object(geom, 0); + obj = solparser_get_object(parser, obj_id); + shape = solparser_get_shape(parser, obj->shape); + CHECK(shape->type, SOLPARSER_SHAPE_SPHERE); + mtl2 = solparser_get_material_double_sided(parser, obj->mtl2); + CHECK(mtl2->front.i, mtl2->back.i); + + mtl = solparser_get_material(parser, mtl2->front); + CHECK(mtl->type, SOLPARSER_MATERIAL_MATTE); + matte = solparser_get_material_matte(parser, mtl->data.matte); + CHECK(matte->reflectivity, 0.123); + CHECK(SOLPARSER_ID_IS_VALID(matte->normal_map), 1); + img = solparser_get_image(parser, matte->normal_map); + CHECK(strcmp(str_cget(&img->filename), "path to normal map"), 0); + + solparser_entity_iterator_next(&it); + CHECK(solparser_entity_iterator_eq(&it, &end), 1); + + CHECK(solparser_load(parser), RES_BAD_OP); + fclose(stream); +} + +static void +test_mirror(struct solparser* parser) +{ + struct solparser_entity_iterator it, end; + struct solparser_entity_id entity_id; + struct solparser_object_id obj_id; + const struct solparser_entity* entity; + const struct solparser_image* img; + const struct solparser_geometry* geom; + const struct solparser_material_double_sided* mtl2; + const struct solparser_material* mtl; + const struct solparser_material_mirror* mirror; + const struct solparser_object* obj; + const struct solparser_shape* shape; + FILE* stream; + + stream = tmpfile(); + NCHECK(stream, NULL); + + fprintf(stream, "- sun: { dni: 1, spectrum: [{wavelength: 1, data: 1} ] }\n"); + fprintf(stream, "- entity: \n"); + fprintf(stream, " name: my entity\n"); + fprintf(stream, " primary: 0\n"); + fprintf(stream, " geometry: \n"); + fprintf(stream, " - cuboid: { size: [1, 1, 1] }\n"); + fprintf(stream, " material: \n"); + fprintf(stream, " mirror:\n"); + fprintf(stream, " reflectivity: 1\n"); + fprintf(stream, " roughness: 0.1\n"); + fprintf(stream, " normal_map: { path: Normal map } \n"); + rewind(stream); + + CHECK(solparser_setup(parser, NULL, stream), RES_OK); + CHECK(solparser_load(parser), RES_OK); + + solparser_entity_iterator_begin(parser, &it); + solparser_entity_iterator_end(parser, &end); + CHECK(solparser_entity_iterator_eq(&it, &end), 0); + + entity_id = solparser_entity_iterator_get(&it); + entity = solparser_get_entity(parser, entity_id); + + CHECK(strcmp("my entity", str_cget(&entity->name)), 0); + CHECK(solparser_entity_get_children_count(entity), 0); + CHECK(entity->primary, 0); + CHECK(entity->type, SOLPARSER_ENTITY_GEOMETRY); + geom = solparser_get_geometry(parser, entity->data.geometry); + CHECK(solparser_geometry_get_objects_count(geom), 1); + obj_id = solparser_geometry_get_object(geom, 0); + obj = solparser_get_object(parser, obj_id); + shape = solparser_get_shape(parser, obj->shape); + CHECK(shape->type, SOLPARSER_SHAPE_CUBOID); + mtl2 = solparser_get_material_double_sided(parser, obj->mtl2); + CHECK(mtl2->front.i, mtl2->back.i); + + mtl = solparser_get_material(parser, mtl2->front); + CHECK(mtl->type, SOLPARSER_MATERIAL_MIRROR); + mirror = solparser_get_material_mirror(parser, mtl->data.mirror); + CHECK(mirror->reflectivity, 1); + CHECK(mirror->roughness, 0.1); + CHECK(SOLPARSER_ID_IS_VALID(mirror->normal_map), 1); + img = solparser_get_image(parser, mirror->normal_map); + CHECK(strcmp(str_cget(&img->filename), "Normal map"), 0); + + solparser_entity_iterator_next(&it); + CHECK(solparser_entity_iterator_eq(&it, &end), 1); + + CHECK(solparser_load(parser), RES_BAD_OP); + fclose(stream); +} + +static void +test_thin_dielectric(struct solparser* parser) +{ + struct solparser_entity_iterator it, end; + struct solparser_entity_id entity_id; + struct solparser_object_id obj_id; + const struct solparser_entity* entity; + const struct solparser_image* img; + const struct solparser_geometry* geom; + const struct solparser_material_double_sided* mtl2; + const struct solparser_material* mtl; + const struct solparser_material_thin_dielectric* thin; + const struct solparser_object* obj; + const struct solparser_shape* shape; + FILE* stream; + + stream = tmpfile(); + NCHECK(stream, NULL); + fprintf(stream, "- sun: { dni: 1, spectrum: [{wavelength: 1, data: 1} ] }\n"); + fprintf(stream, "- entity:\n"); + fprintf(stream, " name: test\n"); + fprintf(stream, " primary: 0\n"); + fprintf(stream, " geometry:\n"); + fprintf(stream, " - sphere: { radius: 1 }\n"); + fprintf(stream, " material:\n"); + fprintf(stream, " thin_dielectric:\n"); + fprintf(stream, " thickness: 0.1\n"); + fprintf(stream, " medium_i:\n"); + fprintf(stream, " refractive_index: 1\n"); + fprintf(stream, " absorptivity: 0\n"); + fprintf(stream, " medium_t:\n"); + fprintf(stream, " refractive_index: 1.5\n"); + fprintf(stream, " absorptivity: 20\n"); + fprintf(stream, " normal_map: { path: Bump }\n"); + rewind(stream); + + CHECK(solparser_setup(parser, NULL, stream), RES_OK); + CHECK(solparser_load(parser), RES_OK); + + solparser_entity_iterator_begin(parser, &it); + solparser_entity_iterator_end(parser, &end); + CHECK(solparser_entity_iterator_eq(&it, &end), 0); + + entity_id = solparser_entity_iterator_get(&it); + entity = solparser_get_entity(parser, entity_id); + + CHECK(strcmp("test", str_cget(&entity->name)), 0); + CHECK(solparser_entity_get_children_count(entity), 0); + CHECK(entity->primary, 0); + CHECK(entity->type, SOLPARSER_ENTITY_GEOMETRY); + geom = solparser_get_geometry(parser, entity->data.geometry); + CHECK(solparser_geometry_get_objects_count(geom), 1); + obj_id = solparser_geometry_get_object(geom, 0); + obj = solparser_get_object(parser, obj_id); + shape = solparser_get_shape(parser, obj->shape); + CHECK(shape->type, SOLPARSER_SHAPE_SPHERE); + mtl2 = solparser_get_material_double_sided(parser, obj->mtl2); + CHECK(mtl2->front.i, mtl2->back.i); + + mtl = solparser_get_material(parser, mtl2->front); + CHECK(mtl->type, SOLPARSER_MATERIAL_THIN_DIELECTRIC); + thin = solparser_get_material_thin_dielectric(parser, mtl->data.thin_dielectric); + CHECK(thin->thickness, 0.1); + NCHECK(thin->medium_i.i, thin->medium_t.i); + CHECK(SOLPARSER_ID_IS_VALID(thin->normal_map), 1); + img = solparser_get_image(parser, thin->normal_map); + CHECK(strcmp(str_cget(&img->filename), "Bump"), 0); + + solparser_entity_iterator_next(&it); + CHECK(solparser_entity_iterator_eq(&it, &end), 1); + + CHECK(solparser_load(parser), RES_BAD_OP); + fclose(stream); +} + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct solparser* parser; + (void)argc, (void)argv; + + CHECK(mem_init_proxy_allocator(&allocator, &mem_default_allocator), RES_OK); + CHECK(solparser_create(&allocator, &parser), RES_OK); + + test_dielectric(parser); + test_matte(parser); + test_mirror(parser); + test_thin_dielectric(parser); + + solparser_ref_put(parser); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHECK(mem_allocated_size(), 0); + return 0; +} + diff --git a/src/parser/yaml/test_ko_0.yaml b/src/parser/yaml/test_ko_0.yaml @@ -204,6 +204,7 @@ # <matte> ::= # matte: # reflectivity: REAL # in [0, 1] +# [ normal_map: { path: PATH } ] # # missing matte parameters @@ -224,7 +225,25 @@ # 2x reflectivity - material: { matte: { reflectivity: 1, reflectivity: 1 } } --- - +# bad normal map +- material: + matte: { reflectivity: 1, normal_map: map } +--- +# bad normal map +- material: + matte: { reflectivity: 1, normal_map: { path: } } +--- +# Missing reflectivity +- material: + matte: { normal_map: { path: test } } +--- +# 2x normal map +- material: + matte: + reflectivity: 1 + normal_map: { path: test } + normal_map: { path: test2} +# # # <thin-dielectric> ::= # thin_dielectric: diff --git a/src/parser/yaml/test_ok_2.yaml b/src/parser/yaml/test_ok_2.yaml @@ -39,3 +39,16 @@ geometry: - material: { matte: { reflectivity: 1 } } stl: { path: "path/path/path" } +--- +- sun: { dni: 1, spectrum: [{wavelength: 1, data: 1}] } +- material: &lambertian + matte: + reflectivity: 0.123 + normal_map: { path: "path/to my normal map" } +- entity: + name: hop + primary: 1 + geometry: + - material: *lambertian + stl: { path: "path/path/path" } + diff --git a/src/solstice_draw.c b/src/solstice_draw.c @@ -23,27 +23,32 @@ /******************************************************************************* * Helper function ******************************************************************************/ -/* Assume that the pixel format of the src is DOUBLE3 in gray scale while the - * pixel format of dst is UBYTE */ +/* Assume that the pixel format of the src is DOUBLE3 and dst is UBYTE3 */ static void -tone_map(const double* src, unsigned char* dst, const size_t count) +tone_map(const double* src, uint8_t* dst, const size_t count) { size_t i; ASSERT(src && dst && count); FOR_EACH(i, 0, count) { - double val; - val = pow(src[i*3/*#channels*/], 1/SCREEN_GAMMA);/* Gamma correction */ - val = CLAMP(val, 0, 1); - dst[i] = (unsigned char)((val * 255) + 0.5/*round*/); + double val[3]; + val[0] = pow(src[i*3/*#channels*/+0], 1/SCREEN_GAMMA);/* Gamma correction */ + val[1] = pow(src[i*3/*#channels*/+1], 1/SCREEN_GAMMA);/* Gamma correction */ + val[2] = pow(src[i*3/*#channels*/+2], 1/SCREEN_GAMMA);/* Gamma correction */ + val[0] = CLAMP(val[0], 0, 1); + val[1] = CLAMP(val[1], 0, 1); + val[2] = CLAMP(val[2], 0, 1); + dst[i*3/*#channels*/ + 0] = (uint8_t)((val[0]*255) + 0.5/*round*/); + dst[i*3/*#channels*/ + 1] = (uint8_t)((val[1]*255) + 0.5/*round*/); + dst[i*3/*#channels*/ + 2] = (uint8_t)((val[2]*255) + 0.5/*round*/); } } static void -tone_map_image(const struct ssol_image* img, unsigned char* dst) +tone_map_image(const struct ssol_image* img, uint8_t* dst) { struct ssol_image_layout layout; size_t irow = 0; - void* mem; + char* mem; ASSERT(img && dst); SSOL(image_get_layout(img, &layout)); @@ -52,7 +57,7 @@ tone_map_image(const struct ssol_image* img, unsigned char* dst) SSOL(image_map(img, &mem)); FOR_EACH(irow, 0, layout.height) { const void* src_row = ((char*)mem) + layout.offset + irow * layout.row_pitch; - unsigned char* dst_row = dst + irow * layout.width; + uint8_t* dst_row = dst + irow * layout.width * 3/*#channels*/; tone_map(src_row, dst_row, layout.width); } } @@ -64,13 +69,17 @@ res_T solstice_draw(struct solstice* solstice) { struct ssol_image_layout layout; - unsigned char* ubytes = NULL; + struct image img; + size_t pitch; res_T res = RES_OK; ASSERT(solstice); SSOL(image_get_layout(solstice->framebuffer, &layout)); - ubytes = MEM_ALLOC(solstice->allocator, layout.width*layout.height); - if(!ubytes) { + + pitch = layout.width * sizeof_image_format(IMAGE_RGB8); + image_init(solstice->allocator, &img); + res = image_setup(&img, layout.width, layout.height, pitch, IMAGE_RGB8, NULL); + if(res != RES_OK) { fprintf(stderr, "Could not allocate the 8-bits image buffer.\n"); res = RES_MEM_ERR; goto error; @@ -92,9 +101,9 @@ solstice_draw(struct solstice* solstice) goto error; } - tone_map_image(solstice->framebuffer, ubytes); - res = image_ppm_write_stream(solstice->output, (int)layout.width, - (int)layout.height, 1, ubytes); + tone_map_image(solstice->framebuffer, (uint8_t*)img.pixels); + + res = image_write_ppm_stream(&img, 0, solstice->output); if(res != RES_OK) { fprintf(stderr, "Could not write the rendered image to the output stream.\n"); @@ -102,7 +111,7 @@ solstice_draw(struct solstice* solstice) } exit: - if(ubytes) MEM_RM(solstice->allocator, ubytes); + image_release(&img); return res; error: goto exit; diff --git a/src/solstice_material.c b/src/solstice_material.c @@ -16,36 +16,87 @@ #include "solstice.h" #include "solstice_c.h" +#include <rsys/double33.h> +#include <rsys/image.h> #include <solstice/ssol.h> +struct dielectric_param { + struct ssol_image* normal_map; +}; + struct matte_param { double reflectivity; + struct ssol_image* normal_map; }; struct mirror_param { double reflectivity; double roughness; + struct ssol_image* normal_map; +}; + +struct thin_dielectric_param { + struct ssol_image* normal_map; }; /******************************************************************************* * Helper functions ******************************************************************************/ static void +perturb_normal + (const struct ssol_surface_fragment* frag, + const struct ssol_image* normal_map, + double normal[3]) +{ + double basis[9]; + double N[3]; + ASSERT(frag && normal_map && normal); + + SSOL(image_sample(normal_map, SSOL_FILTER_LINEAR, SSOL_ADDRESS_CLAMP, + SSOL_ADDRESS_CLAMP, frag->uv, N)); + + d3_set(basis+0, frag->dPdu); + d3_set(basis+3, frag->dPdv); + d3_set(basis+6, frag->Ng); + d3_normalize(basis + 0, basis + 0); + d3_normalize(basis + 3, basis + 3); + + d3_subd(N, d3_muld(N, N, 2), 1); + d33_muld3(N, basis, N); + d3_normalize(normal, N); +} + +static void mtl_get_normal (struct ssol_device* dev, struct ssol_param_buffer* buf, const double wavelength, - const double P[3], - const double Ng[3], - const double Ns[3], - const double uv[2], - const double w[3], + const struct ssol_surface_fragment* frag, double* val) { - (void)dev, (void)buf, (void)wavelength, (void)P, (void)Ng, (void)uv, (void)w; - val[0] = Ns[0]; - val[1] = Ns[1]; - val[2] = Ns[2]; + (void)dev, (void)buf, (void)wavelength; + d3_set(val, frag->Ns); +} + +static void +dielectric_get_normal + (struct ssol_device* dev, + struct ssol_param_buffer* buf, + const double wavelength, + const struct ssol_surface_fragment* frag, + double* val) +{ + const struct dielectric_param* param = ssol_param_buffer_get(buf); + (void)dev, (void)buf, (void)wavelength; + perturb_normal(frag, param->normal_map, val); +} + +static void +dielectric_param_release(void* mem) +{ + struct dielectric_param* param = mem; + ASSERT(param); + if(param->normal_map) SSOL(image_ref_put(param->normal_map)); } static void @@ -53,32 +104,45 @@ matte_get_reflectivity (struct ssol_device* dev, struct ssol_param_buffer* buf, const double wavelength, - const double P[3], - const double Ng[3], - const double Ns[3], - const double uv[2], - const double w[3], + const struct ssol_surface_fragment* frag, double* val) { const struct matte_param* param = ssol_param_buffer_get(buf); - (void)dev, (void)wavelength, (void)P, (void)Ng, (void)Ns, (void)uv, (void)w; + (void)dev, (void)wavelength, (void)frag; *val = param->reflectivity; } static void +matte_get_normal + (struct ssol_device* dev, + struct ssol_param_buffer* buf, + const double wavelength, + const struct ssol_surface_fragment* frag, + double* val) +{ + const struct matte_param* param = ssol_param_buffer_get(buf); + (void)dev, (void)wavelength; + perturb_normal(frag, param->normal_map, val); +} + +static void +matte_param_release(void* mem) +{ + struct matte_param* param = mem; + ASSERT(param); + if(param->normal_map) SSOL(image_ref_put(param->normal_map)); +} + +static void mirror_get_reflectivity (struct ssol_device* dev, struct ssol_param_buffer* buf, const double wavelength, - const double P[3], - const double Ng[3], - const double Ns[3], - const double uv[2], - const double w[3], + const struct ssol_surface_fragment* frag, double* val) { const struct mirror_param* param = ssol_param_buffer_get(buf); - (void)dev, (void)wavelength, (void)P, (void)Ng, (void)Ns, (void)uv, (void)w; + (void)dev, (void)wavelength, (void)frag; *val = param->reflectivity; } @@ -87,18 +151,125 @@ mirror_get_roughness (struct ssol_device* dev, struct ssol_param_buffer* buf, const double wavelength, - const double P[3], - const double Ng[3], - const double Ns[3], - const double uv[2], - const double w[3], + const struct ssol_surface_fragment* frag, double* val) { const struct mirror_param* param = ssol_param_buffer_get(buf); - (void)dev, (void)wavelength, (void)P, (void)Ng, (void)Ns, (void)uv, (void)w; + (void)dev, (void)wavelength, (void)frag; *val = param->roughness; } +static void +mirror_get_normal + (struct ssol_device* dev, + struct ssol_param_buffer* buf, + const double wavelength, + const struct ssol_surface_fragment* frag, + double* val) +{ + const struct mirror_param* param = ssol_param_buffer_get(buf); + (void)dev, (void)wavelength; + perturb_normal(frag, param->normal_map, val); +} + +static void +mirror_param_release(void* mem) +{ + struct mirror_param* param = mem; + ASSERT(param); + if(param->normal_map) SSOL(image_ref_put(param->normal_map)); +} + +static void +thin_dielectric_get_normal + (struct ssol_device* dev, + struct ssol_param_buffer* buf, + const double wavelength, + const struct ssol_surface_fragment* frag, + double* val) +{ + const struct thin_dielectric_param* param = ssol_param_buffer_get(buf); + (void)dev, (void)buf, (void)wavelength; + perturb_normal(frag, param->normal_map, val); +} + +static void +thin_dielectric_param_release(void* mem) +{ + struct thin_dielectric_param* param = mem; + ASSERT(param); + if(param->normal_map) SSOL(image_ref_put(param->normal_map)); +} + +static res_T +load_image + (struct solstice* solstice, + const char* filename, + struct ssol_image** out_ssol_img) +{ + struct ssol_image* ssol_img = NULL; + struct ssol_image_layout layout; + struct image img; + char* mem; + size_t x, y; + res_T res = RES_OK; + ASSERT(solstice && filename && out_ssol_img); + + image_init(solstice->allocator, &img); + + res = image_read_ppm(&img, filename); + if(res != RES_OK) { + fprintf(stderr, "Could not load the PPM image `%s'.\n", filename); + goto error; + } + + res = ssol_image_create(solstice->ssol, &ssol_img); + if(res != RES_OK) { + fprintf(stderr, "Could not create the Solstice Solver image.\n"); + goto error; + } + + res = ssol_image_setup(ssol_img, img.width, img.height, SSOL_PIXEL_DOUBLE3); + if(res != RES_OK) { + fprintf(stderr, "Could not setup the Solstice Solver image.\n"); + goto error; + } + + SSOL(image_get_layout(ssol_img, &layout)); + SSOL(image_map(ssol_img, &mem)); + + FOR_EACH(y, 0, layout.height) { + char* dst_row = mem + layout.offset + y * layout.row_pitch; + const char* src_row = img.pixels + y * img.pitch; + + FOR_EACH(x, 0, layout.width) { + char* dst = dst_row + x*ssol_sizeof_pixel_format(layout.pixel_format); + const char* src = src_row + x*sizeof_image_format(img.format); + switch(img.format) { + case IMAGE_RGB8: + ((double*)dst)[0] = ((double)((uint8_t*)src)[0] + 0.5) / UINT8_MAX; + ((double*)dst)[1] = ((double)((uint8_t*)src)[1] + 0.5) / UINT8_MAX; + ((double*)dst)[2] = ((double)((uint8_t*)src)[2] + 0.5) / UINT8_MAX; + break; + case IMAGE_RGB16: + ((double*)dst)[0] = ((double)((uint16_t*)src)[0] + 0.5) / UINT16_MAX; + ((double*)dst)[1] = ((double)((uint16_t*)src)[1] + 0.5) / UINT16_MAX; + ((double*)dst)[2] = ((double)((uint16_t*)src)[2] + 0.5) / UINT16_MAX; + break; + default: FATAL("Unreachable code.\n"); break; + } + } + } + +exit: + image_release(&img); + *out_ssol_img = ssol_img; + return res; +error: + if(ssol_img) SSOL(image_ref_put(ssol_img)); + goto exit; +} + static res_T create_material_dielectric (struct solstice* solstice, @@ -110,7 +281,9 @@ create_material_dielectric struct ssol_medium ssol_medium_i; struct ssol_medium ssol_medium_t; struct ssol_dielectric_shader shader = SSOL_DIELECTRIC_SHADER_NULL; + struct ssol_image* img = NULL; struct ssol_material* mtl = NULL; + struct ssol_param_buffer* pbuf = NULL; res_T res = RES_OK; ASSERT(solstice && dielectric && out_mtl); @@ -123,7 +296,36 @@ create_material_dielectric medium_i = solparser_get_medium(solstice->parser, dielectric->medium_i); medium_t = solparser_get_medium(solstice->parser, dielectric->medium_t); - shader.normal = mtl_get_normal; + + if(!SOLPARSER_ID_IS_VALID(dielectric->normal_map)) { + shader.normal = mtl_get_normal; + } else { + const struct solparser_image* image; + struct dielectric_param* param = NULL; + + image = solparser_get_image(solstice->parser, dielectric->normal_map); + res = load_image(solstice, str_cget(&image->filename), &img); + if(res != RES_OK) goto error; + + res = ssol_param_buffer_create + (solstice->ssol, sizeof(struct dielectric_param), &pbuf); + if(res != RES_OK) { + fprintf(stderr, "Could not create the Solstice Solver parameter buffer.\n"); + goto error; + } + + param = ssol_param_buffer_allocate(pbuf, sizeof(struct dielectric_param), + ALIGNOF(struct dielectric_param), dielectric_param_release); + if(!param) { + fprintf(stderr, "Could not allocate the dielectric parameter.\n"); + res = RES_MEM_ERR; + goto error; + } + param->normal_map = img; + shader.normal = dielectric_get_normal; + SSOL(material_set_param_buffer(mtl, pbuf)); + } + ssol_medium_i.refractive_index = medium_i->refractive_index; ssol_medium_i.absorptivity = medium_i->absorptivity; ssol_medium_t.refractive_index = medium_t->refractive_index; @@ -131,10 +333,12 @@ create_material_dielectric SSOL(dielectric_setup(mtl, &shader, &ssol_medium_i, &ssol_medium_t)); exit: + if(pbuf) SSOL(param_buffer_ref_put(pbuf)); *out_mtl = mtl; return res; error: if(mtl) SSOL(material_ref_put(mtl)), mtl = NULL; + if(img) SSOL(image_ref_put(img)); goto exit; } @@ -145,6 +349,7 @@ create_material_matte struct ssol_material** out_mtl) { struct ssol_matte_shader shader = SSOL_MATTE_SHADER_NULL; + struct ssol_image* img = NULL; struct ssol_material* mtl = NULL; struct ssol_param_buffer* pbuf = NULL; struct matte_param* param; @@ -164,16 +369,27 @@ create_material_matte goto error; } - param = ssol_param_buffer_allocate - (pbuf, sizeof(struct matte_param), ALIGNOF(struct matte_param)); + param = ssol_param_buffer_allocate(pbuf, sizeof(struct matte_param), + ALIGNOF(struct matte_param), matte_param_release); if(!param) { fprintf(stderr, "Could not allocate the matte parameter.\n"); res = RES_MEM_ERR; goto error; } + memset(param, 0, sizeof(struct matte_param)); + param->reflectivity = matte->reflectivity; + if(!SOLPARSER_ID_IS_VALID(matte->normal_map)) { + shader.normal = mtl_get_normal; + } else { + const struct solparser_image* image; + image = solparser_get_image(solstice->parser, matte->normal_map); + res = load_image(solstice, str_cget(&image->filename), &img); + if(res != RES_OK) goto error; + param->normal_map = img; + shader.normal = matte_get_normal; + } - shader.normal = mtl_get_normal; shader.reflectivity = matte_get_reflectivity; SSOL(matte_setup(mtl, &shader)); SSOL(material_set_param_buffer(mtl, pbuf)); @@ -184,6 +400,7 @@ exit: return res; error: if(mtl) SSOL(material_ref_put(mtl)), mtl = NULL; + if(img) SSOL(image_ref_put(img)); goto exit; } @@ -193,6 +410,7 @@ create_material_mirror const struct solparser_material_mirror* mirror, struct ssol_material** out_mtl) { + struct ssol_image* img = NULL; struct ssol_mirror_shader shader = SSOL_MIRROR_SHADER_NULL; struct ssol_material* mtl = NULL; struct ssol_param_buffer* pbuf = NULL; @@ -213,17 +431,28 @@ create_material_mirror goto error; } - param = ssol_param_buffer_allocate - (pbuf, sizeof(struct mirror_param), ALIGNOF(struct mirror_param)); + param = ssol_param_buffer_allocate(pbuf, sizeof(struct mirror_param), + ALIGNOF(struct mirror_param), mirror_param_release); if(!param) { fprintf(stderr, "Could not allocate the mirror parameters.\n"); res = RES_MEM_ERR; goto error; } + memset(param, 0, sizeof(struct mirror_param)); param->reflectivity = mirror->reflectivity; param->roughness = mirror->roughness; - shader.normal = mtl_get_normal; + if(!SOLPARSER_ID_IS_VALID(mirror->normal_map)) { + shader.normal = mtl_get_normal; + } else { + const struct solparser_image* image; + image = solparser_get_image(solstice->parser, mirror->normal_map); + res = load_image(solstice, str_cget(&image->filename), &img); + if(res != RES_OK) goto error; + param->normal_map = img; + shader.normal = mirror_get_normal; + } + shader.reflectivity = mirror_get_reflectivity; shader.roughness = mirror_get_roughness; SSOL(mirror_setup(mtl, &shader)); @@ -235,6 +464,7 @@ exit: return res; error: if(mtl) SSOL(material_ref_put(mtl)), mtl = NULL; + if(img) SSOL(image_ref_put(img)); goto exit; } @@ -244,12 +474,14 @@ create_material_thin_dielectric const struct solparser_material_thin_dielectric* thin, struct ssol_material** out_mtl) { + struct ssol_image* img = NULL; struct ssol_thin_dielectric_shader shader = SSOL_THIN_DIELECTRIC_SHADER_NULL; const struct solparser_medium* medium_i; const struct solparser_medium* medium_t; struct ssol_medium ssol_medium_i; struct ssol_medium ssol_medium_t; struct ssol_material* mtl = NULL; + struct ssol_param_buffer* pbuf = NULL; res_T res = RES_OK; ASSERT(solstice && thin && out_mtl); @@ -260,9 +492,40 @@ create_material_thin_dielectric goto error; } - shader.normal = mtl_get_normal; medium_i = solparser_get_medium(solstice->parser, thin->medium_i); medium_t = solparser_get_medium(solstice->parser, thin->medium_t); + + if(!SOLPARSER_ID_IS_VALID(thin->normal_map)) { + shader.normal = mtl_get_normal; + } else { + const struct solparser_image* image; + struct dielectric_param* param = NULL; + + image = solparser_get_image(solstice->parser, thin->normal_map); + res = load_image(solstice, str_cget(&image->filename), &img); + if(res != RES_OK) goto error; + + res = ssol_param_buffer_create + (solstice->ssol, sizeof(struct thin_dielectric_param), &pbuf); + if(res != RES_OK) { + fprintf(stderr, "Could not create the Solsitce Solver parameter buffer.\n"); + goto error; + } + + param = ssol_param_buffer_allocate(pbuf, + sizeof(struct thin_dielectric_param), + ALIGNOF(struct thin_dielectric_param), + thin_dielectric_param_release); + if(!param) { + fprintf(stderr, "Could not allocate the thin dielectric parameter.\n"); + res = RES_MEM_ERR; + goto error; + } + param->normal_map = img; + shader.normal = thin_dielectric_get_normal; + SSOL(material_set_param_buffer(mtl, pbuf)); + } + ssol_medium_i.refractive_index = medium_i->refractive_index; ssol_medium_t.refractive_index = medium_t->refractive_index; ssol_medium_i.absorptivity = medium_i->absorptivity; @@ -271,10 +534,12 @@ create_material_thin_dielectric (mtl, &shader, &ssol_medium_i, &ssol_medium_t, thin->thickness)); exit: + if(pbuf) SSOL(param_buffer_ref_put(pbuf)); *out_mtl = mtl; return res; error: if(mtl) SSOL(material_ref_put(mtl)), mtl = NULL; + if(img) SSOL(image_ref_put(img)); goto exit; } diff --git a/src/solstice_object.c b/src/solstice_object.c @@ -500,11 +500,17 @@ create_shaded_shape res_T res = RES_OK; ASSERT(solstice && ssol_front && ssol_back && ssol_shape); + *ssol_front = NULL; + *ssol_back = NULL; + *ssol_shape = NULL; + obj = solparser_get_object(solstice->parser, obj_id); mtl2 = solparser_get_material_double_sided(solstice->parser, obj->mtl2); - solstice_create_ssol_material(solstice, mtl2->front, ssol_front); - solstice_create_ssol_material(solstice, mtl2->back, ssol_back); + res = solstice_create_ssol_material(solstice, mtl2->front, ssol_front); + if(res != RES_OK) goto error; + res = solstice_create_ssol_material(solstice, mtl2->back, ssol_back); + if(res != RES_OK) goto error; /* Define the shape transformation */ rotation[0] = MDEG2RAD(obj->rotation[0]); @@ -547,7 +553,15 @@ create_shaded_shape break; default: FATAL("Unreachable code.\n"); break; } + if(res != RES_OK) goto error; + +exit: return res; +error: + if(*ssol_front) SSOL(material_ref_put(*ssol_front)); + if(*ssol_back) SSOL(material_ref_put(*ssol_back)); + if(*ssol_shape) SSOL(shape_ref_put(*ssol_shape)); + goto exit; } /*******************************************************************************