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:
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;
+}