solstice-solver

Solver library of the solstice app
git clone git://git.meso-star.com/solstice-solver.git
Log | Files | Refs | README | LICENSE

commit 61e48b579498c3f048e501097af849847f799889
parent 135e3ce32d4f72f490520b23edebbef1f56dda6c
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Tue, 20 Sep 2016 18:50:31 +0200

Add atmosphere models in API

First step is to allow uniform atmospheres, described by an absorbtion
spectra.

Diffstat:
Mcmake/CMakeLists.txt | 3+++
Msrc/ssol.h | 35++++++++++++++++++++++++++++++++++-
Asrc/ssol_atmosphere.c | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ssol_atmosphere_c.h | 44++++++++++++++++++++++++++++++++++++++++++++
Msrc/ssol_scene.c | 38++++++++++++++++++++++++++++++++++----
Msrc/ssol_scene_c.h | 1+
Msrc/ssol_solver.c | 16++++++++++++++++
Msrc/ssol_solver_c.h | 1+
Msrc/ssol_spectrum.c | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ssol_spectrum_c.h | 12++++++++++++
Asrc/test_ssol_atmosphere.c | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_ssol_scene.c | 21+++++++++++++++++++++
12 files changed, 447 insertions(+), 5 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -51,6 +51,7 @@ set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) set(SSOL_FILES_SRC + ssol_atmosphere.c ssol_brdf.c ssol_brdf_composite.c ssol_brdf_reflection.c @@ -71,6 +72,7 @@ set(SSOL_FILES_INC_API ssol.h) set(SSOL_FILES_INC + ssol_atmosphere_c.h ssol_brdf.h ssol_brdf_composite.h ssol_brdf_reflection.h @@ -142,6 +144,7 @@ if(NOT NO_TEST) register_test(${_name} ${_name}) endfunction() + new_test(test_ssol_atmosphere) new_test(test_ssol_device) new_test(test_ssol_image) new_test(test_ssol_material) diff --git a/src/ssol.h b/src/ssol.h @@ -46,13 +46,13 @@ struct mem_allocator; struct ssp_rng; /* Opaque Solstice solver types */ +struct ssol_atmosphere; struct ssol_device; struct ssol_image; struct ssol_material; struct ssol_object; struct ssol_instance; struct ssol_scene; -struct ssol_quadric; struct ssol_shape; struct ssol_spectrum; struct ssol_sun; @@ -307,6 +307,16 @@ ssol_scene_detach_sun (struct ssol_scene* scn, struct ssol_sun* sun); +SSOL_API res_T +ssol_scene_attach_atmosphere + (struct ssol_scene* scn, + struct ssol_atmosphere* atm); + +SSOL_API res_T +ssol_scene_detach_atmosphere + (struct ssol_scene* scn, + struct ssol_atmosphere* atm); + /******************************************************************************* * Shape API - Define a geometry that can be generated from a quadric equation * or from a triangular mesh. @@ -523,6 +533,29 @@ ssol_sun_set_buie_param const double param); /* In ]0, 1[ */ /******************************************************************************* + * Atmosphere API - Describe an atmosphere model. + ******************************************************************************/ +/* The atmosphere describes absorbtion along the light paths */ +SSOL_API res_T +ssol_atmosphere_create_uniform + (struct ssol_device* dev, + struct ssol_atmosphere** atmosphere); + +SSOL_API res_T +ssol_atmosphere_ref_get + (struct ssol_atmosphere* atmosphere); + +SSOL_API res_T +ssol_atmosphere_ref_put + (struct ssol_atmosphere* atmosphere); + +/* List of per wavelength power of the sun */ +SSOL_API res_T +ssol_atmosphere_set_uniform_absorbtion + (struct ssol_atmosphere* atmosphere, + struct ssol_spectrum* spectrum); + +/******************************************************************************* * Miscellaneous functions ******************************************************************************/ SSOL_API res_T diff --git a/src/ssol_atmosphere.c b/src/ssol_atmosphere.c @@ -0,0 +1,120 @@ +/* Copyright (C) CNRS 2016 +* +* 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 "ssol.h" +#include "ssol_atmosphere_c.h" +#include "ssol_device_c.h" + +#include <rsys/rsys.h> +#include <rsys/mem_allocator.h> +#include <rsys/ref_count.h> + +/******************************************************************************* +* Helper functions +******************************************************************************/ +static void +atmosphere_release(ref_T* ref) +{ + struct ssol_device* dev; + struct ssol_atmosphere* atmosphere = CONTAINER_OF(ref, struct ssol_atmosphere, ref); + ASSERT(ref); + dev = atmosphere->dev; + ASSERT(dev && dev->allocator); + switch (atmosphere->type) { + case ATMOS_UNIFORM: + if (atmosphere->data.uniform.spectrum) + SSOL(spectrum_ref_put(atmosphere->data.uniform.spectrum)); + break; + default: FATAL("Unreachable code\n"); break; + } + MEM_RM(dev->allocator, atmosphere); + SSOL(device_ref_put(dev)); +} + + +/******************************************************************************* +* Exported ssol_atmosphere functions +******************************************************************************/ +res_T +ssol_atmosphere_create_uniform + (struct ssol_device* dev, + struct ssol_atmosphere** out_atmosphere) +{ + struct ssol_atmosphere* atmosphere = NULL; + res_T res = RES_OK; + if (!dev || !out_atmosphere) { + return RES_BAD_ARG; + } + + atmosphere = (struct ssol_atmosphere*)MEM_CALLOC + (dev->allocator, 1, sizeof(struct ssol_atmosphere)); + if (!atmosphere) { + res = RES_MEM_ERR; + goto error; + } + + SSOL(device_ref_get(dev)); + atmosphere->dev = dev; + atmosphere->type = ATMOS_UNIFORM; + ref_init(&atmosphere->ref); + +exit: + if (out_atmosphere) *out_atmosphere = atmosphere; + return res; +error: + if (atmosphere) { + SSOL(atmosphere_ref_put(atmosphere)); + atmosphere = NULL; + } + goto exit; +} + +res_T +ssol_atmosphere_ref_get + (struct ssol_atmosphere* atmosphere) +{ + if (!atmosphere) + return RES_BAD_ARG; + ref_get(&atmosphere->ref); + return RES_OK; +} + +res_T +ssol_atmosphere_ref_put + (struct ssol_atmosphere* atmosphere) +{ + if (!atmosphere) + return RES_BAD_ARG; + ref_put(&atmosphere->ref, atmosphere_release); + return RES_OK; +} + +res_T +ssol_atmosphere_set_uniform_absorbtion + (struct ssol_atmosphere* atmosphere, + struct ssol_spectrum* spectrum) +{ + struct atm_uniform* uni; + if (!atmosphere || !spectrum || atmosphere->type != ATMOS_UNIFORM) + return RES_BAD_ARG; + uni = &atmosphere->data.uniform; + if (spectrum == uni->spectrum) /* no change */ + return RES_OK; + if (uni->spectrum) + SSOL(spectrum_ref_put(uni->spectrum)); + SSOL(spectrum_ref_get(spectrum)); + uni->spectrum = spectrum; + return RES_OK; +} +\ No newline at end of file diff --git a/src/ssol_atmosphere_c.h b/src/ssol_atmosphere_c.h @@ -0,0 +1,44 @@ +/* Copyright (C) CNRS 2016 +* +* 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 SSOL_ATMOSPHERE_C_H +#define SSOL_ATMOSPHERE_C_H + +#include <rsys/ref_count.h> +#include <rsys/list.h> + +struct ssol_scene; + +enum atmosphere_type { + ATMOS_UNIFORM, + ATMOS_TYPES_COUNT__ +}; + +struct atm_uniform { + struct ssol_spectrum* spectrum; +}; + +struct ssol_atmosphere { + enum atmosphere_type type; + struct ssol_scene* scene_attachment; + union { + struct atm_uniform uniform; + } data; + + struct ssol_device* dev; + ref_T ref; +}; + +#endif /* SSOL_ATMOSPHERE_C_H */ diff --git a/src/ssol_scene.c b/src/ssol_scene.c @@ -15,6 +15,7 @@ #include "ssol.h" #include "ssol_c.h" +#include "ssol_atmosphere_c.h" #include "ssol_scene_c.h" #include "ssol_solver_c.h" #include "ssol_sun_c.h" @@ -43,7 +44,8 @@ scene_release(ref_T* ref) SSOL(scene_clear(scene)); if(scene->scn_rt) S3D(scene_ref_put(scene->scn_rt)); if(scene->scn_samp) S3D(scene_ref_put(scene->scn_samp)); - if(scene->sun) SSOL(sun_ref_put(scene->sun)); + if (scene->sun) SSOL(sun_ref_put(scene->sun)); + if (scene->atmosphere) SSOL(atmosphere_ref_put(scene->atmosphere)); htable_instance_release(&scene->instances_rt); htable_instance_release(&scene->instances_samp); MEM_RM(dev->allocator, scene); @@ -51,7 +53,7 @@ scene_release(ref_T* ref) } /******************************************************************************* - * Exported ssol_image functions + * Exported ssol_scene functions ******************************************************************************/ res_T ssol_scene_create @@ -214,6 +216,34 @@ ssol_scene_detach_sun(struct ssol_scene* scene, struct ssol_sun* sun) return RES_OK; } + +res_T +ssol_scene_attach_atmosphere(struct ssol_scene* scene, struct ssol_atmosphere* atm) +{ + if (!scene || !atm + || atm->scene_attachment /* Should detach this atmosphere first from its own scene */ + || scene->atmosphere) /* Should detach previous atm first */ + return RES_BAD_ARG; + + SSOL(atmosphere_ref_get(atm)); + scene->atmosphere = atm; + atm->scene_attachment = scene; + return RES_OK; +} + +res_T +ssol_scene_detach_atmosphere(struct ssol_scene* scene, struct ssol_atmosphere* atm) +{ + if (!scene || !atm || !scene->atmosphere || atm->scene_attachment != scene) + return RES_BAD_ARG; + + ASSERT(atm == scene->atmosphere); + atm->scene_attachment = NULL; + scene->atmosphere = NULL; + SSOL(atmosphere_ref_put(atm)); + return RES_OK; +} + /******************************************************************************* * Local functions ******************************************************************************/ @@ -276,7 +306,6 @@ hit_filter_function struct realisation* rs = realisation; struct segment* seg; struct segment* prev; - double dist; (void) filter_data, (void) org, (void) dir; ASSERT(rs); @@ -323,7 +352,7 @@ hit_filter_function d33_muld3(dir_local, tr, seg->dir); /* recompute hit */ int valid = punched_shape_intersect_local(shape, org_local, dir_local, - hit->distance, seg->hit_pos_local, seg->hit_normal, &dist); + hit->distance, seg->hit_pos_local, seg->hit_normal, &seg->hit_distance); if (!valid) return 1; /* transform point to world */ d33_muld3(seg->hit_pos, transform, seg->hit_pos_local); @@ -337,6 +366,7 @@ hit_filter_function d3_set_f3(seg->hit_normal, hit->normal); /* use raytraced distance to fill hit_pos */ d3_add(seg->hit_pos, seg->org, d3_muld(seg->hit_pos, seg->dir, hit->distance)); + seg->hit_distance = hit->distance; break; } default: FATAL("Unreachable code.\n"); break; diff --git a/src/ssol_scene_c.h b/src/ssol_scene_c.h @@ -41,6 +41,7 @@ struct ssol_scene { struct s3d_scene* scn_rt; struct s3d_scene* scn_samp; struct ssol_sun* sun; + struct ssol_atmosphere* atmosphere; struct ssol_device* dev; ref_T ref; diff --git a/src/ssol_solver.c b/src/ssol_solver.c @@ -15,6 +15,7 @@ #include "ssol.h" #include "ssol_c.h" +#include "ssol_atmosphere_c.h" #include "ssol_solver_c.h" #include "ssol_device_c.h" #include "ssol_scene_c.h" @@ -207,6 +208,7 @@ static void check_segment(const struct segment* seg) { check_fst_segment(seg); + ASSERT_NAN(&seg->hit_distance, 1); ASSERT(seg->self_instance); NCHECK(seg->self_front, 99); /* hit filter is supposed to work properly */ @@ -264,6 +266,7 @@ reset_segment(struct segment* seg) #ifndef NDEBUG d3_splat(seg->dir, NAN); seg->hit = S3D_HIT_NULL; + seg->hit_distance = NAN; seg->hit_front = 99; seg->hit_instance = NULL; seg->hit_material = NULL; @@ -598,6 +601,19 @@ propagate(struct realisation* rs) return; } + if (rs->data.scene->atmosphere) { + double ka; + const struct ssol_spectrum* spectrum; + switch (rs->data.scene->atmosphere->type) { + case ATMOS_UNIFORM: + spectrum = rs->data.scene->atmosphere->data.uniform.spectrum; + CHECK(spectrum_interpolate(spectrum, rs->freq, &ka), RES_OK); + break; + default: FATAL("Unreachable code\n"); break; + } + seg->weight *= exp(-ka * seg->hit_distance); + } + /* fill fragment and loop */ check_segment(seg); surface_fragment_setup(&rs->data.fragment, seg->hit_pos, seg->dir, diff --git a/src/ssol_solver_c.h b/src/ssol_solver_c.h @@ -67,6 +67,7 @@ struct segment { double hit_pos[3]; double hit_pos_local[3]; /* in local coordinate, only set on punched shapes */ double hit_normal[3]; /* possibly reversed to face the incoming dir */ + double hit_distance; float tmin; /* used to reject duplicate hits in raytracing */ char hit_front; /* is the ending point of the segment on the front face? */ char self_front; /* was the starting point of the segment on the front face? */ diff --git a/src/ssol_spectrum.c b/src/ssol_spectrum.c @@ -21,6 +21,7 @@ #include <rsys/mem_allocator.h> #include <rsys/ref_count.h> #include <rsys/math.h> +#include <rsys/algorithm.h> /******************************************************************************* * Helper functions @@ -39,6 +40,91 @@ spectrum_release(ref_T* ref) SSOL(device_ref_put(dev)); } +static char +spectrum_includes_point + (const struct ssol_spectrum* spectrum, + const double wavelenght) +{ + const double* data; + size_t sz; + ASSERT(spectrum && spectrum->frequencies.data && spectrum->intensities.data); + sz = spectrum->frequencies.size; + ASSERT(sz && sz == spectrum->intensities.size); + data = spectrum->frequencies.data; + return data[0] <= wavelenght && wavelenght <= data[sz - 1]; +} + +int +eq_d(const void* key, const void* base) +{ + double k = *(double*) key; + double b = *(double*) base; + if (k > b) return +1; + if (k < b) return -1; + return 0; +} + +/******************************************************************************* +* Local ssol_spectrum functions +******************************************************************************/ +res_T +spectrum_includes + (const struct ssol_spectrum* reference, + const struct ssol_spectrum* tested, + char* include) +{ + const double* test_data; + size_t test_sz; + if(!reference || !tested || !include) { + return RES_BAD_ARG; + } + + test_sz = tested->frequencies.size; + if(!reference->frequencies.size || !test_sz) { + return RES_BAD_ARG; + } + + test_data = tested->frequencies.data; + *include = spectrum_includes_point(reference, test_data[0]) + && spectrum_includes_point(reference, test_data[test_sz - 1]); + + return RES_OK; +} + +res_T +spectrum_interpolate + (const struct ssol_spectrum* spectrum, + const double wavelenght, + double* intensity) +{ + double* next; + double* freqs; + double* ints; + double slope; + size_t idx_next, sz; + if (!spectrum + || !intensity + || !spectrum_includes_point(spectrum, wavelenght)) + { + return RES_BAD_ARG; + } + + sz = spectrum->frequencies.size; + freqs = spectrum->frequencies.data; + ints = spectrum->intensities.data; + next = search_lower_bound(&wavelenght, freqs, sz, sizeof(double), &eq_d); + ASSERT(next); /* cause spectrum_includes_point */ + idx_next = next - freqs; + ASSERT(idx_next); /* cause spectrum_includes_point */ + ASSERT(ints[idx_next] >= ints[idx_next - 1]); + ASSERT(freqs[idx_next] >= freqs[idx_next - 1]); + + slope = (ints[idx_next] - ints[idx_next - 1]) / (freqs[idx_next] - freqs[idx_next - 1]); + *intensity = ints[idx_next - 1] + (wavelenght - freqs[idx_next - 1]) * slope; + ASSERT(*intensity > 0); + return RES_OK; +} + /******************************************************************************* * Exported ssol_spectrum functions ******************************************************************************/ diff --git a/src/ssol_spectrum_c.h b/src/ssol_spectrum_c.h @@ -26,4 +26,16 @@ struct ssol_spectrum { ref_T ref; }; +extern LOCAL_SYM res_T +spectrum_includes + (const struct ssol_spectrum* reference, + const struct ssol_spectrum* tested, + char* include); + +extern LOCAL_SYM res_T +spectrum_interpolate + (const struct ssol_spectrum* spectrum, + const double wavelenght, + double* intensity); + #endif /* SSOL_SPECTRUM_C_H */ diff --git a/src/test_ssol_atmosphere.c b/src/test_ssol_atmosphere.c @@ -0,0 +1,74 @@ +/* Copyright (C) CNRS 2016 +* +* 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 "ssol.h" +#include "test_ssol_utils.h" + +#include <rsys/logger.h> + +int +main(int argc, char** argv) +{ + struct logger logger; + struct mem_allocator allocator; + struct ssol_device* dev; + struct ssol_spectrum* spectrum; + struct ssol_spectrum* spectrum2; + struct ssol_atmosphere* atm; + (void) argc, (void) argv; + + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + + CHECK(logger_init(&allocator, &logger), RES_OK); + logger_set_stream(&logger, LOG_OUTPUT, log_stream, NULL); + logger_set_stream(&logger, LOG_ERROR, log_stream, NULL); + logger_set_stream(&logger, LOG_WARNING, log_stream, NULL); + + CHECK(ssol_device_create + (&logger, &allocator, SSOL_NTHREADS_DEFAULT, 0, &dev), RES_OK); + + CHECK(ssol_spectrum_create(dev, &spectrum), RES_OK); + CHECK(ssol_spectrum_create(dev, &spectrum2), RES_OK); + + CHECK(ssol_atmosphere_create_uniform(NULL, &atm), RES_BAD_ARG); + CHECK(ssol_atmosphere_create_uniform(dev, NULL), RES_BAD_ARG); + CHECK(ssol_atmosphere_create_uniform(dev, &atm), RES_OK); + + CHECK(ssol_atmosphere_ref_get(NULL), RES_BAD_ARG); + CHECK(ssol_atmosphere_ref_get(atm), RES_OK); + + CHECK(ssol_atmosphere_ref_put(NULL), RES_BAD_ARG); + CHECK(ssol_atmosphere_ref_put(atm), RES_OK); + + CHECK(ssol_atmosphere_set_uniform_absorbtion(NULL, spectrum), RES_BAD_ARG); + CHECK(ssol_atmosphere_set_uniform_absorbtion(atm, NULL), RES_BAD_ARG); + CHECK(ssol_atmosphere_set_uniform_absorbtion(atm, spectrum), RES_OK); + CHECK(ssol_atmosphere_set_uniform_absorbtion(atm, spectrum2), RES_OK); + CHECK(ssol_atmosphere_set_uniform_absorbtion(atm, spectrum2), RES_OK); + + CHECK(ssol_atmosphere_ref_put(atm), RES_OK); + + CHECK(ssol_spectrum_ref_put(spectrum), RES_OK); + CHECK(ssol_spectrum_ref_put(spectrum2), RES_OK); + CHECK(ssol_device_ref_put(dev), RES_OK); + + logger_release(&logger); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHECK(mem_allocated_size(), 0); + + return 0; +} diff --git a/src/test_ssol_scene.c b/src/test_ssol_scene.c @@ -31,6 +31,10 @@ main(int argc, char** argv) struct ssol_sun* sun; struct ssol_sun* sun2; struct ssol_scene* scene; + struct ssol_spectrum* spectrum; + struct ssol_atmosphere* atm; + double wavelengths[3] = { 10, 20, 30 }; + double data[3] = { 1, 2.1, 1.5 }; double transform[12]; (void) argc, (void) argv; @@ -82,6 +86,21 @@ main(int argc, char** argv) CHECK(ssol_scene_detach_sun(scene, sun), RES_OK); CHECK(ssol_scene_detach_sun(scene, sun), RES_BAD_ARG); + CHECK(ssol_spectrum_create(dev, &spectrum), RES_OK); + CHECK(ssol_spectrum_setup(spectrum, wavelengths, data, 3), RES_OK); + CHECK(ssol_atmosphere_create_uniform(dev, &atm), RES_OK); + CHECK(ssol_atmosphere_set_uniform_absorbtion(atm, spectrum), RES_OK); + + CHECK(ssol_scene_attach_atmosphere(NULL, atm), RES_BAD_ARG); + CHECK(ssol_scene_attach_atmosphere(scene, NULL), RES_BAD_ARG); + CHECK(ssol_scene_attach_atmosphere(scene, atm), RES_OK); + CHECK(ssol_scene_attach_atmosphere(scene, atm), RES_BAD_ARG); + + CHECK(ssol_scene_detach_atmosphere(NULL, atm), RES_BAD_ARG); + CHECK(ssol_scene_detach_atmosphere(scene, NULL), RES_BAD_ARG); + CHECK(ssol_scene_detach_atmosphere(scene, atm), RES_OK); + CHECK(ssol_scene_detach_atmosphere(scene, atm), RES_BAD_ARG); + CHECK(ssol_sun_create_directional(dev, &sun2), RES_OK); CHECK(ssol_scene_detach_sun(scene, sun2), RES_BAD_ARG); CHECK(ssol_sun_ref_put(sun2), RES_OK); @@ -92,6 +111,8 @@ main(int argc, char** argv) CHECK(ssol_object_ref_put(object), RES_OK); CHECK(ssol_shape_ref_put(shape), RES_OK); CHECK(ssol_sun_ref_put(sun), RES_OK); + CHECK(ssol_spectrum_ref_put(spectrum), RES_OK); + CHECK(ssol_atmosphere_ref_put(atm), RES_OK); CHECK(ssol_material_ref_put(material), RES_OK); CHECK(ssol_device_ref_put(dev), RES_OK);