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:
| M | src/mrumtl.c | | | 232 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- |
| M | src/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 */