solstice-solver

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

commit 8f47cde2d21c759488d960edc6e9082616ce3675
parent c85dbd31863334f945faeeeb536fe5f0fbb62ced
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Tue, 21 Feb 2017 17:31:11 +0100

BugFix: shadow rays used the wrong side to pick materials on obstacles.

A new test (solver6) is added to demonstrate the now-fixed bug.
Test solver3N is removed, as it was working only due to this bug. No
idea on the result we should get in this test: cannot assert anything.

Diffstat:
Mcmake/CMakeLists.txt | 2+-
Msrc/ssol.h | 3++-
Msrc/ssol_c.h | 5+++--
Msrc/ssol_scene.c | 14+++++++++-----
Msrc/ssol_solver.c | 2++
Msrc/test_ssol_materials.h | 4++--
Msrc/test_ssol_solver1.c | 2++
Dsrc/test_ssol_solver3N.c | 261-------------------------------------------------------------------------------
Asrc/test_ssol_solver6.c | 239+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 260 insertions(+), 272 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -155,9 +155,9 @@ if(NOT NO_TEST) new_test(test_ssol_solver2) new_test(test_ssol_solver2b) new_test(test_ssol_solver3) - new_test(test_ssol_solver3N) new_test(test_ssol_solver4) new_test(test_ssol_solver5) + new_test(test_ssol_solver6) new_test(test_ssol_sun) endif() diff --git a/src/ssol.h b/src/ssol.h @@ -62,7 +62,8 @@ struct ssol_estimator; enum ssol_side_flag { SSOL_FRONT = BIT(0), - SSOL_BACK = BIT(1) + SSOL_BACK = BIT(1), + SSOL_INVALID_SIDE = BIT(2) }; enum ssol_clipping_op { diff --git a/src/ssol_c.h b/src/ssol_c.h @@ -35,7 +35,8 @@ struct ray_data { struct s3d_primitive prim_from; /* Primitive from which the ray starts */ const struct ssol_instance* inst_from; /* Instance from which the ray starts */ enum ssol_side_flag side_from; /* Primitive side from which the ray starts */ - int discard_virtual_materials; /* Define if virtual materials are not RT */ + short discard_virtual_materials; /* Define if virtual materials are not RT */ + short reversed_ray; /* Define if the ray direction is reversed */ float range_min; /* Output data */ @@ -44,7 +45,7 @@ struct ray_data { }; static const struct ray_data RAY_DATA_NULL = { - NULL, S3D_PRIMITIVE_NULL__, NULL, 0, 0, 0, {0, 0, 0}, 0 + NULL, S3D_PRIMITIVE_NULL__, NULL, SSOL_INVALID_SIDE, 0, 0, 0, {0, 0, 0}, 0 }; diff --git a/src/ssol_scene.c b/src/ssol_scene.c @@ -387,7 +387,7 @@ hit_filter_function struct ssol_material* mtl; struct ray_data* rdata = ray_data; const struct shaded_shape* sshape; - enum ssol_side_flag hit_side = 3; + enum ssol_side_flag hit_side = SSOL_INVALID_SIDE; double pos[3], dir[3], N[3], dst = FLT_MAX; size_t id; (void)filter_data; @@ -425,8 +425,10 @@ hit_filter_function if(dst >= FLT_MAX) { /* No projection is found => the ray does not intersect the quadric */ return 1; - } else if(inst == rdata->inst_from) { - + } + else if (inst != rdata->inst_from) { + hit_side = d3_dot(dir, N) < 0 ? SSOL_FRONT : SSOL_BACK; + } else { if(hit->distance <= rdata->range_min) { /* Handle RT numerical imprecision, the hit is below the lower bound * of the ray range. */ @@ -435,7 +437,6 @@ hit_filter_function /* If the intersected instance is the one from which the ray starts, * ensure that the ray does not intersect the opposite side of the * quadric */ - hit_side = d3_dot(dir, N) < 0 ? SSOL_FRONT : SSOL_BACK; if(hit_side != rdata->side_from) { return 1; } @@ -444,7 +445,10 @@ hit_filter_function break; default: FATAL("Unreachable code.\n"); break; } - + ASSERT(hit_side == SSOL_BACK || hit_side == SSOL_FRONT); + if (rdata->reversed_ray) { + hit_side = (hit_side == SSOL_FRONT) ? SSOL_BACK : SSOL_FRONT; + } mtl = hit_side == SSOL_FRONT ? sshape->mtl_front : sshape->mtl_back; if(mtl->type == MATERIAL_VIRTUAL) { /* Discard all virtual materials */ diff --git a/src/ssol_solver.c b/src/ssol_solver.c @@ -150,6 +150,7 @@ point_init ray_data.inst_from = pt->inst; ray_data.side_from = pt->side; ray_data.discard_virtual_materials = 1; /* Do not intersect virtual mtl */ + ray_data.reversed_ray = 1; /* The ray direction is reversed */ ray_data.dst = FLT_MAX; /* Trace a ray toward the sun to check if the sampled point is occluded */ @@ -524,6 +525,7 @@ ssol_solve ray_data.inst_from = pt.inst; ray_data.side_from = pt.side; ray_data.discard_virtual_materials = 0; + ray_data.reversed_ray = 0; ray_data.range_min = range[0]; ray_data.dst = FLT_MAX; S3D(scene_view_trace_ray(view_rt, org, dir, range, &ray_data, &hit)); diff --git a/src/test_ssol_materials.h b/src/test_ssol_materials.h @@ -18,8 +18,6 @@ #include <rsys/rsys.h> -#define REFLECTIVITY 0.87 - struct ssol_device; static INLINE void @@ -56,6 +54,7 @@ get_shader_reflectivity *val = 1; } +#ifdef REFLECTIVITY static INLINE void get_shader_reflectivity_2 (struct ssol_device* dev, @@ -72,6 +71,7 @@ get_shader_reflectivity_2 (void) P, (void) Ng, (void) Ns, (void) uv, (void) w; *val = REFLECTIVITY; } +#endif static INLINE void get_shader_roughness diff --git a/src/test_ssol_solver1.c b/src/test_ssol_solver1.c @@ -15,6 +15,8 @@ #include "ssol.h" #include "test_ssol_utils.h" + +#define REFLECTIVITY 0.87 #include "test_ssol_materials.h" #define HALF_X 1 diff --git a/src/test_ssol_solver3N.c b/src/test_ssol_solver3N.c @@ -1,261 +0,0 @@ -/* 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 "ssol.h" -#include "test_ssol_utils.h" -#include "test_ssol_materials.h" - -#define PLANE_NAME SQUARE -#define HALF_X 250 -#define HALF_Y 250 -#include "test_ssol_rect_geometry.h" - -#define POLYGON_NAME POLY -#define HALF_X 1 -#define HALF_Y 1 -#include "test_ssol_rect2D_geometry.h" - -#include <rsys/logger.h> -#include <rsys/double33.h> - -#include <star/s3d.h> -#include <star/ssp.h> - -/******************************************************************************* - * Helper functions - ******************************************************************************/ -struct common { - struct ssol_scene* scene; - struct ssol_object* m_object; - double target[3]; - double sun_dir[3]; -}; - -static void -set_1(struct common* common, const double pos[3]) -{ - struct ssol_instance* heliostat; - double transform[12]; /* 3x4 column major matrix */ - double out_dir[3], axis[3], c, a; - - ASSERT(common); - ASSERT(d3_is_normalized(common->sun_dir)); - - /* compute rotation axis and angle */ - d3_set(out_dir, common->target); - d3_sub(out_dir, pos, out_dir); - d3_normalize(out_dir, out_dir); - d3_cross(axis, out_dir, common->sun_dir); - /* FIXME: manage the colinear case */ - NCHECK(d3_normalize(axis, axis), 0); - c = d3_dot(out_dir, common->sun_dir); - a = acos(c) / 2; - - /* setup transform */ - d33_rotation_axis_angle(transform, axis, a); - d3_set(transform + 9, pos); - - /* create instance and attach it */ - SSOL(object_instantiate(common->m_object, &heliostat)); - CHECK(ssol_instance_set_transform(heliostat, transform), RES_OK); - - SSOL(scene_attach_instance(common->scene, heliostat)); - SSOL(instance_ref_put(heliostat)); -} - -static void -get_wlen(const size_t i, double* wlen, double* data, void* ctx) -{ - double wavelengths[3] = { 1, 2, 3 }; - double intensities[3] = { 1, 0.8, 1 }; - CHECK(i < 3, 1); - (void)ctx; - *wlen = wavelengths[i]; - *data = intensities[i]; -} - -/******************************************************************************* - * Test main program - ******************************************************************************/ -int -main(int argc, char** argv) -{ - struct common common; - struct logger logger; - struct mem_allocator allocator; - struct ssol_device* dev; - struct ssp_rng* rng; - struct ssol_scene* scene; - struct ssol_shape* square; - struct ssol_vertex_data attribs[1] = { SSOL_VERTEX_DATA_NULL__ }; - struct ssol_shape* quad_square; - struct ssol_carving carving = SSOL_CARVING_NULL; - struct ssol_quadric quadric = SSOL_QUADRIC_DEFAULT; - struct ssol_punched_surface punched = SSOL_PUNCHED_SURFACE_NULL; - struct ssol_material* m_mtl; - struct ssol_material* v_mtl; - struct ssol_mirror_shader shader = SSOL_MIRROR_SHADER_NULL; - struct ssol_object* m_object; - struct ssol_object* t_object; - struct ssol_instance* target; - struct ssol_sun* sun; - struct ssol_spectrum* spectrum; - struct ssol_estimator* estimator; - struct ssol_estimator_status status; - double sun_dir[3]; - double target_pos[3]; - double transform[12]; /* 3x4 column major matrix */ - size_t count; - FILE* tmp; - double m, std; - int i, j, k; - uint32_t r_id; - (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(ssp_rng_create(&allocator, &ssp_rng_threefry, &rng), RES_OK); - CHECK(ssol_spectrum_create(dev, &spectrum), RES_OK); - CHECK(ssol_spectrum_setup(spectrum, get_wlen, 3, NULL), RES_OK); - CHECK(ssol_sun_create_directional(dev, &sun), RES_OK); - d3(sun_dir, 1, 0, -1); - d3_normalize(sun_dir, sun_dir); - CHECK(ssol_sun_set_direction(sun, sun_dir), RES_OK); - CHECK(ssol_sun_set_spectrum(sun, spectrum), RES_OK); - CHECK(ssol_sun_set_dni(sun, 1000), RES_OK); - CHECK(ssol_scene_create(dev, &scene), RES_OK); - CHECK(ssol_scene_attach_sun(scene, sun), RES_OK); - - /* create scene content */ - - d3(target_pos, 0, 0, 1000); - - CHECK(ssol_shape_create_mesh(dev, &square), RES_OK); - attribs[0].usage = SSOL_POSITION; - attribs[0].get = get_position; - CHECK(ssol_mesh_setup(square, SQUARE_NTRIS__, get_ids, - SQUARE_NVERTS__, attribs, 1, (void*) &SQUARE_DESC__), RES_OK); - - CHECK(ssol_shape_create_punched_surface(dev, &quad_square), RES_OK); - carving.get = get_polygon_vertices; - carving.operation = SSOL_AND; - carving.nb_vertices = POLY_NVERTS__; - carving.context = &POLY_EDGES__; - quadric.type = SSOL_QUADRIC_PLANE; - punched.nb_carvings = 1; - punched.quadric = &quadric; - punched.carvings = &carving; - CHECK(ssol_punched_surface_setup(quad_square, &punched), RES_OK); - - CHECK(ssol_material_create_mirror(dev, &m_mtl), RES_OK); - shader.normal = get_shader_normal; - shader.reflectivity = get_shader_reflectivity; - shader.roughness = get_shader_roughness; - CHECK(ssol_mirror_set_shader(m_mtl, &shader), RES_OK); - CHECK(ssol_material_create_virtual(dev, &v_mtl), RES_OK); - - CHECK(ssol_object_create(dev, &m_object), RES_OK); - CHECK(ssol_object_add_shaded_shape(m_object, quad_square, m_mtl, v_mtl), RES_OK); - - common.scene = scene; - d3_set(common.sun_dir, sun_dir); - common.m_object = m_object; - d3_set(common.target, target_pos); - - /* with too big numbers here, the program will crash... */ -#define NX 40 -#define NY 40 -#define NZ 1 - for (k = 0; k < NZ; k++) { - double pos[3]; - pos[2] = -k; - for (i = 0; i < NX; i++) { - pos[0] = -NX + 2. * i; - for (j = 0; j < NY; j++) { - pos[1] = -NX + 2. * j; - set_1(&common, pos); - } - } - } - - d33_rotation_pitch(transform, PI); /* flip faces: invert normal */ - d3_set(transform + 9, target_pos); - - CHECK(ssol_object_create(dev, &t_object), RES_OK); - CHECK(ssol_object_add_shaded_shape(t_object, square, v_mtl, v_mtl), RES_OK); - CHECK(ssol_object_instantiate(t_object, &target), RES_OK); - CHECK(ssol_instance_set_transform(target, transform), RES_OK); - CHECK(ssol_instance_set_receiver(target, SSOL_FRONT), RES_OK); - CHECK(ssol_instance_sample(target, 0), RES_OK); - CHECK(ssol_scene_attach_instance(scene, target), RES_OK); - - NCHECK(tmp = tmpfile(), 0); -#define N__ 10000 -#define GET_STATUS ssol_estimator_get_status -#define GET_RCV_STATUS ssol_estimator_get_receiver_status - CHECK(ssol_solve(scene, rng, N__, tmp, &estimator), RES_OK); - CHECK(ssol_instance_get_id(target, &r_id), RES_OK); - CHECK(ssol_estimator_get_count(estimator, &count), RES_OK); - CHECK(count, N__); - CHECK(pp_sum(tmp, (int32_t)r_id, count, &m, &std), RES_OK); - CHECK(fclose(tmp), 0); - logger_print(&logger, LOG_OUTPUT, "\nIr = %g +/- %g\n", m, std); -#define DNI_cos (1000 * cos(PI / 8)) - CHECK(eq_eps(m, 4 * NX * NY * NZ * DNI_cos, 4 * NX * NY * NZ * DNI_cos * 2e-1), 1); - CHECK(GET_STATUS(estimator, SSOL_STATUS_SHADOW, &status), RES_OK); - logger_print(&logger, LOG_OUTPUT, "Shadows = %g +/- %g", status.irradiance.E, status.irradiance.SE); - CHECK(eq_eps(status.irradiance.E, 0, 1e-4), 1); - CHECK(GET_STATUS(estimator, SSOL_STATUS_MISSING, &status), RES_OK); - logger_print(&logger, LOG_OUTPUT, "Missing = %g +/- %g", status.irradiance.E, status.irradiance.SE); - CHECK(eq_eps(status.irradiance.E, 0, 1e-4), 1); - CHECK(status.Nf, 0); - CHECK(GET_RCV_STATUS(estimator, target, SSOL_FRONT, &status), RES_OK); - logger_print(&logger, LOG_OUTPUT, "Ir(target) = %g +/- %g", status.irradiance.E, status.irradiance.SE); - CHECK(eq_eps(status.irradiance.E, m, 1e-6), 1); - CHECK(eq_eps(status.irradiance.SE, std, 1e-3), 1); -#undef GET_STATUS -#undef GET_RCV_STATUS - - /* Free data */ - CHECK(ssol_instance_ref_put(target), RES_OK); - CHECK(ssol_object_ref_put(m_object), RES_OK); - CHECK(ssol_object_ref_put(t_object), RES_OK); - CHECK(ssol_shape_ref_put(square), RES_OK); - CHECK(ssol_shape_ref_put(quad_square), RES_OK); - CHECK(ssol_material_ref_put(m_mtl), RES_OK); - CHECK(ssol_material_ref_put(v_mtl), RES_OK); - CHECK(ssol_estimator_ref_put(estimator), RES_OK); - CHECK(ssol_device_ref_put(dev), RES_OK); - CHECK(ssol_scene_ref_put(scene), RES_OK); - CHECK(ssp_rng_ref_put(rng), RES_OK); - CHECK(ssol_spectrum_ref_put(spectrum), RES_OK); - CHECK(ssol_sun_ref_put(sun), 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_solver6.c b/src/test_ssol_solver6.c @@ -0,0 +1,239 @@ +/* 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 "ssol.h" +#include "test_ssol_utils.h" + +#define REFLECTIVITY 0 +#include "test_ssol_materials.h" + +#define PLANE_NAME SQUARE +#define HALF_X 5 +#define HALF_Y 5 +#include "test_ssol_rect_geometry.h" + +#define POLYGON_NAME POLY +#define HALF_X 5 +#define HALF_Y 5 +#include "test_ssol_rect2D_geometry.h" + +#include <rsys/logger.h> +#include <rsys/double33.h> + +#include <star/s3d.h> +#include <star/ssp.h> + +static void +get_wlen(const size_t i, double* wlen, double* data, void* ctx) +{ + double wavelengths[3] = { 1, 2, 3 }; + double intensities[3] = { 1, 0.8, 1 }; + CHECK(i < 3, 1); + (void)ctx; + *wlen = wavelengths[i]; + *data = intensities[i]; +} + +int +main(int argc, char** argv) +{ + struct logger logger; + struct mem_allocator allocator; + struct ssol_device* dev; + struct ssp_rng* rng; + struct ssol_scene* scene; + struct ssol_shape* square; + struct ssol_vertex_data attribs[1] = { SSOL_VERTEX_DATA_NULL__ }; + struct ssol_shape* quad_square; + struct ssol_carving carving = SSOL_CARVING_NULL; + struct ssol_quadric quadric = SSOL_QUADRIC_DEFAULT; + struct ssol_punched_surface punched = SSOL_PUNCHED_SURFACE_NULL; + struct ssol_material* m_mtl; + struct ssol_material* v_mtl; + struct ssol_material* bck_mtl; + struct ssol_mirror_shader m_shader = SSOL_MIRROR_SHADER_NULL; + struct ssol_matte_shader bck_shader = SSOL_MATTE_SHADER_NULL; + struct ssol_sun* sun; + struct ssol_spectrum* spectrum; + struct ssol_estimator* estimator; + struct ssol_estimator_status status; + double dir[3]; + double transform[12]; /* 3x4 column major matrix */ + FILE* tmp; + + (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, 1, 0, &dev), RES_OK); + + CHECK(ssp_rng_create(&allocator, &ssp_rng_threefry, &rng), RES_OK); + CHECK(ssol_spectrum_create(dev, &spectrum), RES_OK); + CHECK(ssol_spectrum_setup(spectrum, get_wlen, 3, NULL), RES_OK); + CHECK(ssol_sun_create_directional(dev, &sun), RES_OK); + CHECK(ssol_sun_set_direction(sun, d3(dir, 0, 0, -1)), RES_OK); + CHECK(ssol_sun_set_spectrum(sun, spectrum), RES_OK); + CHECK(ssol_sun_set_dni(sun, 1000), RES_OK); + CHECK(ssol_scene_create(dev, &scene), RES_OK); + CHECK(ssol_scene_attach_sun(scene, sun), RES_OK); + + /* Create scene content */ + + CHECK(ssol_shape_create_mesh(dev, &square), RES_OK); + attribs[0].usage = SSOL_POSITION; + attribs[0].get = get_position; + CHECK(ssol_mesh_setup(square, SQUARE_NTRIS__, get_ids, + SQUARE_NVERTS__, attribs, 1, (void*) &SQUARE_DESC__), RES_OK); + + CHECK(ssol_shape_create_punched_surface(dev, &quad_square), RES_OK); + carving.get = get_polygon_vertices; + carving.operation = SSOL_AND; + carving.nb_vertices = POLY_NVERTS__; + carving.context = &POLY_EDGES__; + quadric.type = SSOL_QUADRIC_PLANE; + punched.nb_carvings = 1; + punched.quadric = &quadric; + punched.carvings = &carving; + CHECK(ssol_punched_surface_setup(quad_square, &punched), RES_OK); + + CHECK(ssol_material_create_mirror(dev, &m_mtl), RES_OK); + m_shader.normal = get_shader_normal; + m_shader.reflectivity = get_shader_reflectivity; + m_shader.roughness = get_shader_roughness; + CHECK(ssol_mirror_set_shader(m_mtl, &m_shader), RES_OK); + CHECK(ssol_material_create_matte(dev, &bck_mtl), RES_OK); + bck_shader.normal = get_shader_normal; + bck_shader.reflectivity = get_shader_reflectivity_2; + CHECK(ssol_matte_set_shader(bck_mtl, &bck_shader), RES_OK); + CHECK(ssol_material_create_virtual(dev, &v_mtl), RES_OK); + + /* 1st reflector */ + struct ssol_object *m_object1; + struct ssol_instance* heliostat1; + CHECK(ssol_object_create(dev, &m_object1), RES_OK); + CHECK(ssol_object_add_shaded_shape(m_object1, quad_square, m_mtl, m_mtl), RES_OK); + CHECK(ssol_object_instantiate(m_object1, &heliostat1), RES_OK); + CHECK(ssol_scene_attach_instance(scene, heliostat1), RES_OK); + d33_rotation_pitch(transform, PI); /* flip faces: invert normal */ + d3_splat(transform + 9, 0); + transform[9] = -25; + CHECK(ssol_instance_set_transform(heliostat1, transform), RES_OK); + + /* 2nd reflector */ + struct ssol_object *m_object2; + struct ssol_instance* heliostat2; + CHECK(ssol_object_create(dev, &m_object2), RES_OK); + CHECK(ssol_object_add_shaded_shape(m_object2, quad_square, m_mtl, m_mtl), RES_OK); + CHECK(ssol_object_instantiate(m_object2, &heliostat2), RES_OK); + CHECK(ssol_scene_attach_instance(scene, heliostat2), RES_OK); + d33_rotation_pitch(transform, PI); /* flip faces: invert normal */ + d3_splat(transform + 9, 0); + transform[9] = +25; + CHECK(ssol_instance_set_transform(heliostat2, transform), RES_OK); + + /* 1st target */ + struct ssol_object* t_object1; + struct ssol_instance* target1; + CHECK(ssol_object_create(dev, &t_object1), RES_OK); + CHECK(ssol_object_add_shaded_shape(t_object1, square, bck_mtl, v_mtl), RES_OK); + CHECK(ssol_object_instantiate(t_object1, &target1), RES_OK); + CHECK(ssol_instance_set_transform(target1, transform), RES_OK); + CHECK(ssol_instance_set_receiver(target1, SSOL_FRONT), RES_OK); + CHECK(ssol_instance_sample(target1, 0), RES_OK); + CHECK(ssol_scene_attach_instance(scene, target1), RES_OK); + d33_rotation_pitch(transform, PI); /* flip faces: invert normal */ + d3_splat(transform + 9, 0); + transform[9] = -25; + transform[11] = 10; + CHECK(ssol_instance_set_transform(target1, transform), RES_OK); + + /* 2nd target */ + struct ssol_object* t_object2; + struct ssol_instance* target2; + CHECK(ssol_object_create(dev, &t_object2), RES_OK); + CHECK(ssol_object_add_shaded_shape(t_object2, square, bck_mtl, v_mtl), RES_OK); + CHECK(ssol_object_instantiate(t_object2, &target2), RES_OK); + CHECK(ssol_instance_set_transform(target2, transform), RES_OK); + CHECK(ssol_instance_set_receiver(target2, SSOL_BACK), RES_OK); + CHECK(ssol_instance_sample(target2, 0), RES_OK); + CHECK(ssol_scene_attach_instance(scene, target2), RES_OK); + d33_set_identity(transform); + d3_splat(transform + 9, 0); + transform[9] = +25; + transform[11] = 10; + CHECK(ssol_instance_set_transform(target2, transform), RES_OK); + + NCHECK(tmp = tmpfile(), 0); +#define N__ 10000 +#define GET_STATUS ssol_estimator_get_status +#define GET_RCV_STATUS ssol_estimator_get_receiver_status + CHECK(ssol_solve(scene, rng, N__, tmp, &estimator), RES_OK); + CHECK(fclose(tmp), 0); + + CHECK(GET_STATUS(estimator, SSOL_STATUS_SHADOW, &status), RES_OK); + logger_print(&logger, LOG_OUTPUT, "Shadows = %g +/- %g", status.irradiance.E, status.irradiance.SE); + CHECK(eq_eps(status.irradiance.E, 100000, 2 * 100000/sqrt(N__)), 1); + CHECK(status.Nf, 0); + + CHECK(GET_STATUS(estimator, SSOL_STATUS_MISSING, &status), RES_OK); + logger_print(&logger, LOG_OUTPUT, "Missing = %g +/- %g", status.irradiance.E, status.irradiance.SE); + CHECK(eq_eps(status.irradiance.E, 0, 0), 1); + + CHECK(GET_RCV_STATUS(estimator, target1, SSOL_FRONT, &status), RES_OK); + logger_print(&logger, LOG_OUTPUT, "Ir(target1) = %g +/- %g", status.irradiance.E, status.irradiance.SE); + CHECK(eq_eps(status.irradiance.E, 100000, 2 * 100000 / sqrt(N__)), 1); + + CHECK(GET_RCV_STATUS(estimator, target2, SSOL_BACK, &status), RES_OK); + logger_print(&logger, LOG_OUTPUT, "Ir(target2) = %g +/- %g", status.irradiance.E, status.irradiance.SE); + CHECK(eq_eps(status.irradiance.E, 0, 1), 1); + +#undef GET_STATUS +#undef GET_RCV_STATUS + + /* Free data */ + CHECK(ssol_instance_ref_put(heliostat1), RES_OK); + CHECK(ssol_instance_ref_put(target1), RES_OK); + CHECK(ssol_object_ref_put(m_object1), RES_OK); + CHECK(ssol_object_ref_put(t_object1), RES_OK); + CHECK(ssol_instance_ref_put(heliostat2), RES_OK); + CHECK(ssol_instance_ref_put(target2), RES_OK); + CHECK(ssol_object_ref_put(m_object2), RES_OK); + CHECK(ssol_object_ref_put(t_object2), RES_OK); + CHECK(ssol_shape_ref_put(square), RES_OK); + CHECK(ssol_shape_ref_put(quad_square), RES_OK); + CHECK(ssol_material_ref_put(m_mtl), RES_OK); + CHECK(ssol_material_ref_put(bck_mtl), RES_OK); + CHECK(ssol_material_ref_put(v_mtl), RES_OK); + CHECK(ssol_device_ref_put(dev), RES_OK); + CHECK(ssol_estimator_ref_put(estimator), RES_OK); + CHECK(ssol_scene_ref_put(scene), RES_OK); + CHECK(ssp_rng_ref_put(rng), RES_OK); + CHECK(ssol_spectrum_ref_put(spectrum), RES_OK); + CHECK(ssol_sun_ref_put(sun), RES_OK); + + logger_release(&logger); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHECK(mem_allocated_size(), 0); + + return 0; +}