mrumtl

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

commit 6a2029b2107860c5e056809aefbce01e214d8f15
parent f18e65484f065e94fc3a51debaeb184054927e1d
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Mon,  2 Mar 2020 12:15:53 +0100

Implement the BRDF API

Diffstat:
Msrc/mrumtl.c | 232+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/mrumtl.h | 23+++++++++++++++++++++--
2 files changed, 224 insertions(+), 31 deletions(-)

diff --git a/src/mrumtl.c b/src/mrumtl.c @@ -17,6 +17,7 @@ #include "mrumtl.h" +#include <rsys/algorithm.h> #include <rsys/cstr.h> #include <rsys/dynamic_array.h> #include <rsys/logger.h> @@ -45,7 +46,7 @@ struct mrumtl_brdf { union { struct brdf_lambertian lambertian; struct brdf_specular specular; - } data; + } value; }; struct brdf_wlen { @@ -54,8 +55,8 @@ struct brdf_wlen { }; struct brdf_band { - double wlen_min; /* In nanometers */ - double wlen_max; /* In nanometers */ + double wlen_min; /* Inclusive bound in nanometers */ + double wlen_max; /* Exclusive bound In nanometers */ struct mrumtl_brdf brdf; }; @@ -81,9 +82,9 @@ struct mrumtl { ref_T ref; }; -#define MSG_INFO_PREFIX "MRUMTL:\x1b[1m\x1b[32minfo\x1b[0m: " -#define MSG_ERROR_PREFIX "MRUMTL:\x1b[1m\x1b[31merror\x1b[0m: " -#define MSG_WARNING_PREFIX "MRUMTL:\x1b[1m\x1b[33mwarning\x1b[0m: " +#define MSG_INFO_PREFIX "MruMtl:\x1b[1m\x1b[32minfo\x1b[0m: " +#define MSG_ERROR_PREFIX "MruMtl:\x1b[1m\x1b[31merror\x1b[0m: " +#define MSG_WARNING_PREFIX "MruMtl:\x1b[1m\x1b[33mwarning\x1b[0m: " /******************************************************************************* * Helper functions @@ -264,11 +265,11 @@ parse_brdf } if(!strcmp(tk, "lambertian")) { - res = parse_brdf_lambertian(mrumtl, txtrdr, tk_ctx, &brdf->data.lambertian); + res = parse_brdf_lambertian(mrumtl, txtrdr, tk_ctx, &brdf->value.lambertian); if(res != RES_OK) goto error; brdf->type = MRUMTL_BRDF_LAMBERTIAN; } else if(!strcmp(tk, "specular")) { - res = parse_brdf_specular(mrumtl, txtrdr, tk_ctx, &brdf->data.specular); + res = parse_brdf_specular(mrumtl, txtrdr, tk_ctx, &brdf->value.specular); if(res != RES_OK) goto error; brdf->type = MRUMTL_BRDF_SPECULAR; } else { @@ -331,6 +332,7 @@ parse_wlen_brdf if(tk) { log_err(mrumtl, "%s:%lu: unexpected directive `%s'.\n", txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); + res = RES_BAD_ARG; goto error; } @@ -557,13 +559,42 @@ error: goto exit; } +static INLINE void +clear_mrumtl(struct mrumtl* mrumtl) +{ + ASSERT(mrumtl); + darray_brdf_band_clear(&mrumtl->brdf_bands); + darray_brdf_wlen_clear(&mrumtl->brdf_wlens); + mrumtl->brdf_list_type = BRDF_LIST_NONE__; +} + static res_T -parse_data(struct mrumtl* mrumtl, struct txtrdr* txtrdr) +load_stream(struct mrumtl* mrumtl, FILE* stream, const char* stream_name) { char* tk = NULL; char* tk_ctx = NULL; + struct txtrdr* txtrdr = NULL; res_T res = RES_OK; - ASSERT(mrumtl && txtrdr); + ASSERT(mrumtl && stream && stream_name); + + clear_mrumtl(mrumtl); + + res = txtrdr_stream(mrumtl->allocator, stream, stream_name, '#', &txtrdr); + if(res != RES_OK) { + log_err(mrumtl, "Could not create the text reader to parse `%s' -- %s.\n", + stream_name, res_to_cstr(res)); + goto error; + } + + res = txtrdr_read_line(txtrdr); + if(res != RES_OK) { + log_err(mrumtl, "%s: could not read the line `%lu' -- %s.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), + res_to_cstr(res)); + goto error; + } + + if(!txtrdr_get_cline(txtrdr)) goto exit; /* No line to parse */ tk = strtok_r(txtrdr_get_line(txtrdr), " \t", &tk_ctx); ASSERT(tk); @@ -582,42 +613,134 @@ parse_data(struct mrumtl* mrumtl, struct txtrdr* txtrdr) } exit: + if(txtrdr) txtrdr_ref_put(txtrdr); return res; error: goto exit; } -static res_T -load_stream(struct mrumtl* mrumtl, FILE* stream, const char* stream_name) +static FINLINE int +cmp_band(const void* key, const void* elmt) { - struct txtrdr* txtrdr = NULL; + const double* wlen = key; + const struct brdf_band* brdf_band = elmt; + ASSERT(key && elmt); + + if(*wlen < brdf_band->wlen_min) { + return -1; + } else if(*wlen >= brdf_band->wlen_max) { + return 1; + } else { + /* The key is included in the bound */ + return 0; + } +} + +static INLINE res_T +fetch_brdf_band + (const struct mrumtl* mrumtl, + const double wavelength, + const struct mrumtl_brdf** out_brdf) +{ + const struct brdf_band* brdf_band = NULL; + const struct brdf_band* brdf_bands = NULL; + size_t nbrdf_bands = 0; res_T res = RES_OK; - ASSERT(mrumtl && stream && stream_name); + ASSERT(mrumtl && out_brdf); - res = txtrdr_stream(mrumtl->allocator, stream, stream_name, '#', &txtrdr); - if(res != RES_OK) { - log_err(mrumtl, "Could not create the text reader to parse `%s' -- %s.\n", - stream_name, res_to_cstr(res)); + brdf_bands = darray_brdf_band_cdata_get(&mrumtl->brdf_bands); + nbrdf_bands = darray_brdf_band_size_get(&mrumtl->brdf_bands); + + if(!nbrdf_bands) { + res = RES_BAD_ARG; goto error; } - for(;;) { - res = txtrdr_read_line(txtrdr); - if(res != RES_OK) { - log_err(mrumtl, "%s: could not read the line `%lu' -- %s.\n", - txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), - res_to_cstr(res)); - goto error; + brdf_band = search_lower_bound + (&wavelength, brdf_bands, nbrdf_bands, sizeof(*brdf_bands), cmp_band); + + if(!brdf_band) { + /* All BRDFs are included in a band whose upper boundary are lower than or + * equal to `wavelength'. `Clamp' the fetch query to the last BRDF */ + brdf_band = &brdf_bands[nbrdf_bands-1]; + ASSERT(brdf_band->wlen_max <= wavelength); + } else { + if(brdf_band != brdf_bands) { + /* The found BRDF is not the first one so the submitted wavelength is + * forcely included in the band that it overlaps */ + ASSERT(brdf_band->wlen_min <= wavelength); } + ASSERT(wavelength < brdf_band->wlen_max); + } - if(!txtrdr_get_cline(txtrdr)) break; /* No more parsed line */ + *out_brdf = &brdf_band->brdf; - res = parse_data(mrumtl, txtrdr); - if(res != RES_OK) goto error; +exit: + return res; +error: + goto exit; +} + +static FINLINE int +cmp_wlen(const void* key, const void* elmt) +{ + const double* wlen = key; + const struct brdf_wlen* brdf_wlen = elmt; + ASSERT(key && elmt); + + if(*wlen < brdf_wlen->wlen) { + return -1; + } else if(*wlen > brdf_wlen->wlen) { + return 1; + } else { + return 0; } +} + +static INLINE res_T +fetch_brdf_wlen + (const struct mrumtl* mrumtl, + const double wavelength, + const struct mrumtl_brdf** out_brdf) +{ + const struct brdf_wlen* brdf_wlen = NULL; + const struct brdf_wlen* brdf_wlens = NULL; + size_t nbrdf_wlens = 0; + res_T res = RES_OK; + ASSERT(mrumtl && out_brdf); + + brdf_wlens = darray_brdf_wlen_cdata_get(&mrumtl->brdf_wlens); + nbrdf_wlens = darray_brdf_wlen_size_get(&mrumtl->brdf_wlens); + + if(!nbrdf_wlens) { + res = RES_BAD_ARG; + goto error; + } + + brdf_wlen = search_lower_bound + (&wavelength, brdf_wlens, nbrdf_wlens, sizeof(*brdf_wlens), cmp_wlen); + + if(!brdf_wlen) { + /* All BRDFs are defined for a wavelength less than the submitted + * `wavelength'. */ + brdf_wlen = &brdf_wlens[nbrdf_wlens-1]; + ASSERT(brdf_wlen->wlen <= wavelength); + } else if(brdf_wlen != brdf_wlens) { + /* The found BRDF is not the first one => the submitted wavelength lies in + * [brdf_wlen[-1].wlen, brdf_wlen[0].wlen[. Select the right BRDF wrt the + * distance from the submitted `wavelengths' and the wavelength of the + * found BRDF and the one of the previous BRDF. */ + const double dst0 = wavelength - brdf_wlen[-1].wlen; + const double dst1 = brdf_wlen[0].wlen - wavelength; + ASSERT(dst0 > 0 && dst1 >= 0); + if(dst0 < dst1) { + brdf_wlen = brdf_wlen - 1; + } + } + + *out_brdf = &brdf_wlen->brdf; exit: - if(txtrdr) txtrdr_ref_put(txtrdr); return res; error: goto exit; @@ -768,3 +891,54 @@ error: goto exit; } +res_T +mrumtl_fetch_brdf + (const struct mrumtl* mrumtl, + const double wavelength, /* In nanometers */ + const struct mrumtl_brdf** out_brdf) +{ + res_T res = RES_OK; + + if(!mrumtl || !out_brdf) { + res = RES_BAD_ARG; + goto error; + } + + switch(mrumtl->brdf_list_type) { + case BRDF_LIST_BAND: + res = fetch_brdf_band(mrumtl, wavelength, out_brdf); + break; + case BRDF_LIST_WLEN: + res = fetch_brdf_wlen(mrumtl, wavelength, out_brdf); + break; + default: FATAL("Unreachable code.\n"); break; + } + if(res != RES_OK) goto error; + +exit: + return res; +error: + goto exit; +} + +enum mrumtl_brdf_type +mrumtl_brdf_get_type(const struct mrumtl_brdf* brdf) +{ + ASSERT(brdf); + return brdf->type; +} + +double +mrumtl_brdf_lambertian_get_reflectivity(const struct mrumtl_brdf* brdf) +{ + ASSERT(brdf && mrumtl_brdf_get_type(brdf) == MRUMTL_BRDF_LAMBERTIAN); + return brdf->value.lambertian.reflectivity; +} + +double +mrumtl_brdf_specular_get_reflectivity(const struct mrumtl_brdf* brdf) +{ + ASSERT(brdf && mrumtl_brdf_get_type(brdf) == MRUMTL_BRDF_SPECULAR); + return brdf->value.specular.reflectivity; +} + diff --git a/src/mrumtl.h b/src/mrumtl.h @@ -48,6 +48,7 @@ struct mem_allocator; /* Forward declaration of opaque data types */ struct mrumtl; +struct mrumtl_brdf; BEGIN_DECLS @@ -69,17 +70,35 @@ MRUMTL_API res_T mrumtl_ref_put (struct mrumtl* mrumtl); -MRUMTL_API res_T +MRUMTL_API res_T /* Clean up the previously loaded material */ mrumtl_load (struct mrumtl* mrumtl, const char* path); -MRUMTL_API res_T +MRUMTL_API res_T /* Clean up the previously created material */ mrumtl_load_stream (struct mrumtl* mrumtl, FILE* stream, const char* stream_name); /* May be NULL */ +MRUMTL_API res_T +mrumtl_fetch_brdf + (const struct mrumtl* mrumtl, + const double wavelength, /* In nanometers */ + const struct mrumtl_brdf** brdf); + +MRUMTL_API enum mrumtl_brdf_type +mrumtl_brdf_get_type + (const struct mrumtl_brdf* brdf); + +MRUMTL_API double +mrumtl_brdf_lambertian_get_reflectivity + (const struct mrumtl_brdf* brdf); + +MRUMTL_API double +mrumtl_brdf_specular_get_reflectivity + (const struct mrumtl_brdf* brdf); + END_DECLS #endif /* MRUMTL_H */