solstice-solver

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

commit fb9bc1a8fca117c98d92d61d4fe86b8ffa02fb86
parent bf386fec2bd9fe2c42621cc7a8470d917050989f
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Mon, 27 Mar 2017 15:45:41 +0200

Merge branch 'release_0.1'

Diffstat:
Mcmake/CMakeLists.txt | 30++++++++++++++++++++++++------
Msrc/ssol.h | 342++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/ssol_atmosphere.c | 19++++---------------
Msrc/ssol_atmosphere_c.h | 3+--
Msrc/ssol_c.h | 4++--
Msrc/ssol_device.c | 1+
Msrc/ssol_device_c.h | 2+-
Msrc/ssol_draw.c | 72+++++++++++++++++++++++++-----------------------------------------------
Asrc/ssol_draw.h | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ssol_draw_draft.c | 162+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ssol_draw_pt.c | 360+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ssol_estimator.c | 212+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/ssol_estimator_c.h | 549++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/ssol_instance.c | 82++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/ssol_instance_c.h | 4+++-
Msrc/ssol_material.c | 326++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/ssol_material_c.h | 37++++++++++++++++++++++++++++++-------
Asrc/ssol_mc_receiver.c | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ssol_object.c | 29++++++++++++++++++++++++++++-
Msrc/ssol_object_c.h | 7+++++++
Msrc/ssol_scene.c | 141+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Msrc/ssol_scene_c.h | 6+++---
Msrc/ssol_shape.c | 504++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/ssol_shape_c.h | 35++++++++++++++++++++++++++++++++++-
Msrc/ssol_solver.c | 898+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/test_ssol_atmosphere.c | 12+-----------
Msrc/test_ssol_by_receiver_integration.c | 59++++++++++++++++++++++++++---------------------------------
Asrc/test_ssol_circ2D_geometry.h | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_ssol_cube_geometry.h | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_ssol_device.c | 8++++++++
Msrc/test_ssol_draw.c | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/test_ssol_image.c | 13+------------
Msrc/test_ssol_instance.c | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/test_ssol_material.c | 249++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Msrc/test_ssol_materials.h | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_ssol_object.c | 11+----------
Msrc/test_ssol_rect2D_geometry.h | 38+++++++++++++++++++++++++++++---------
Msrc/test_ssol_rect_geometry.h | 36++++++++++++++++++++++++++++--------
Msrc/test_ssol_scene.c | 125++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/test_ssol_shape.c | 101++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Msrc/test_ssol_solver1.c | 383+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/test_ssol_solver2.c | 64+++++++++++++++++++++++++---------------------------------------
Msrc/test_ssol_solver2b.c | 59++++++++++++++++++++++++-----------------------------------
Msrc/test_ssol_solver3.c | 53+++++++++++++++++++++--------------------------------
Msrc/test_ssol_solver4.c | 63+++++++++++++++++++++++++++------------------------------------
Msrc/test_ssol_solver5.c | 54++++++++++++++++++++++--------------------------------
Msrc/test_ssol_solver6.c | 78++++++++++++++++++++++++++++++++----------------------------------------------
Asrc/test_ssol_solver7.c | 245+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_ssol_solver8.c | 187+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_ssol_solver9.c | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_ssol_spectrum.c | 12+-----------
Msrc/test_ssol_sun.c | 11+----------
Msrc/test_ssol_utils.h | 8--------
53 files changed, 5258 insertions(+), 1268 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -27,7 +27,7 @@ find_package(RCMake 0.2.3 REQUIRED) find_package(RSys 0.4 REQUIRED) find_package(Star3D 0.4 REQUIRED) find_package(StarCPR REQUIRED) -find_package(StarSF 0.0 REQUIRED) +find_package(StarSF 0.1 REQUIRED) find_package(StarSP 0.4 REQUIRED) find_package(OpenMP 1.2 REQUIRED) @@ -57,9 +57,12 @@ set(SSOL_FILES_SRC ssol_camera.c ssol_device.c ssol_draw.c + ssol_draw_pt.c + ssol_draw_draft.c ssol_estimator.c ssol_image.c ssol_material.c + ssol_mc_receiver.c ssol_object.c ssol_instance.c ssol_param_buffer.c @@ -79,6 +82,7 @@ set(SSOL_FILES_INC ssol_c.h ssol_camera.h ssol_device_c.h + ssol_draw.h ssol_estimator_c.h ssol_image_c.h ssol_material_c.h @@ -123,15 +127,22 @@ rcmake_setup_devel(solstice-solver SolSolver ${VERSION} solstice/ssol_version.h) # Add tests ################################################################################ if(NOT NO_TEST) - function(new_test _name) - add_executable(${_name} - ${SSOL_SOURCE_DIR}/${_name}.c) + function(build_test _name) + add_executable(${_name} ${SSOL_SOURCE_DIR}/${_name}.c) target_link_libraries(${_name} solstice-solver solstice-test RSys Star3D StarSP) - add_test(${_name} ${_name}) + endfunction() + + function(register_test _name) + add_test(${_name} ${ARGN}) rcmake_set_test_runtime_dirs(${_name} _runtime_dirs) endfunction() + function(new_test _name) + build_test(${_name}) + register_test(${_name} ${_name}) + endfunction() + add_library(solstice-test STATIC ${SSOL_SOURCE_DIR}/test_ssol_geometries.h ${SSOL_SOURCE_DIR}/test_ssol_materials.h @@ -142,7 +153,6 @@ if(NOT NO_TEST) new_test(test_ssol_by_receiver_integration) new_test(test_ssol_camera) new_test(test_ssol_device) - new_test(test_ssol_draw) new_test(test_ssol_image) new_test(test_ssol_material) new_test(test_ssol_object) @@ -158,7 +168,15 @@ if(NOT NO_TEST) new_test(test_ssol_solver4) new_test(test_ssol_solver5) new_test(test_ssol_solver6) + new_test(test_ssol_solver7) + new_test(test_ssol_solver8) + new_test(test_ssol_solver9) new_test(test_ssol_sun) + + build_test(test_ssol_draw) + register_test(test_ssol_draw_draft test_ssol_draw draft) + register_test(test_ssol_draw_pt test_ssol_draw pt) + endif() ################################################################################ diff --git a/src/ssol.h b/src/ssol.h @@ -66,6 +66,21 @@ enum ssol_side_flag { SSOL_INVALID_SIDE = BIT(2) }; +enum ssol_path_type { + SSOL_PATH_MISSING, /* The path misses the receivers */ + SSOL_PATH_SHADOW, /* The path is occluded before the sampled geometry */ + SSOL_PATH_SUCCESS /* The path contributes to at least one receiver */ +}; + +enum ssol_material_type { + SSOL_MATERIAL_DIELECTRIC, + SSOL_MATERIAL_MATTE, + SSOL_MATERIAL_MIRROR, + SSOL_MATERIAL_THIN_DIELECTRIC, + SSOL_MATERIAL_VIRTUAL, + SSOL_MATERIAL_TYPES_COUNT__ +}; + enum ssol_clipping_op { SSOL_AND, SSOL_SUB, @@ -85,6 +100,7 @@ enum ssol_parametrization_type { enum ssol_quadric_type { SSOL_QUADRIC_PLANE, SSOL_QUADRIC_PARABOL, + SSOL_QUADRIC_HYPERBOL, SSOL_QUADRIC_PARABOLIC_CYLINDER, SSOL_QUADRIC_TYPE_COUNT__ }; @@ -149,6 +165,15 @@ struct ssol_quadric_parabol { static const struct ssol_quadric_parabol SSOL_QUADRIC_PARABOL_NULL = SSOL_QUADRIC_PARABOL_NULL__; +struct ssol_quadric_hyperbol { + /* Define (x^2 + y^2) / a^2 - (z - 1/2)^2 / b^2 + 1 = 0 + * with a^2 = f - f^2; b = f -1/2; f = real_focal / (img_focal + real_focal) */ + double img_focal, real_focal; +}; +#define SSOL_QUADRIC_HYPERBOL_NULL__ { -1.0 , -1.0 } +static const struct ssol_quadric_hyperbol SSOL_QUADRIC_HYPERBOL_NULL = +SSOL_QUADRIC_HYPERBOL_NULL__; + struct ssol_quadric_parabolic_cylinder { double focal; /* Define y^2 - 4 focal z = 0 */ }; @@ -161,18 +186,24 @@ struct ssol_quadric { union { struct ssol_quadric_plane plane; struct ssol_quadric_parabol parabol; + struct ssol_quadric_hyperbol hyperbol; struct ssol_quadric_parabolic_cylinder parabolic_cylinder; } data; /* 3x4 column major transformation of the quadric in object space */ double transform[12]; + + /* Hint on the how to discretised */ + size_t slices_count_hint; }; #define SSOL_QUADRIC_DEFAULT__ { \ SSOL_QUADRIC_PLANE, \ {SSOL_QUADRIC_PLANE_DEFAULT__}, \ - {1,0,0, 0,1,0, 0,0,1, 0,0,0} \ + {1,0,0, 0,1,0, 0,0,1, 0,0,0}, \ + SIZE_MAX /* <=> Use default discretisation */ \ } + static const struct ssol_quadric SSOL_QUADRIC_DEFAULT = SSOL_QUADRIC_DEFAULT__; /* Define the contour of a 2D polygon as well as the clipping operation to @@ -196,6 +227,13 @@ struct ssol_punched_surface { static const struct ssol_punched_surface SSOL_PUNCHED_SURFACE_NULL = SSOL_PUNCHED_SURFACE_NULL__; +struct ssol_medium { + double absorptivity; + double refractive_index; +}; +#define SSOL_MEDIUM_VACUUM__ { 0, 1 } +static const struct ssol_medium SSOL_MEDIUM_VACUUM = SSOL_MEDIUM_VACUUM__; + typedef void (*ssol_shader_getter_T) (struct ssol_device* dev, @@ -208,6 +246,14 @@ typedef void const double w[3], /* Incoming direction. Point toward the surface */ double* val); /* Returned value */ +/* Dielectric material shader */ +struct ssol_dielectric_shader { + ssol_shader_getter_T normal; +}; +#define SSOL_DIELECTRIC_SHADER_NULL__ { NULL } +static const struct ssol_dielectric_shader SSOL_DIELECTRIC_SHADER_NULL = + SSOL_DIELECTRIC_SHADER_NULL__; + /* Mirror material shader */ struct ssol_mirror_shader { ssol_shader_getter_T normal; @@ -227,6 +273,14 @@ struct ssol_matte_shader { static const struct ssol_matte_shader SSOL_MATTE_SHADER_NULL = SSOL_MATTE_SHADER_NULL__; +/* Thin dielectric shader */ +struct ssol_thin_dielectric_shader { + ssol_shader_getter_T normal; +}; +#define SSOL_THIN_DIELECTRIC_SHADER_NULL__ { NULL } +static const struct ssol_thin_dielectric_shader +SSOL_THIN_DIELECTRIC_SHADER_NULL = SSOL_THIN_DIELECTRIC_SHADER_NULL__; + /* The type of data produced on receiver hits as ssol_solve() write them on its * FILE* argument */ struct ssol_receiver_data { @@ -248,28 +302,106 @@ struct ssol_receiver_data { /* TODO Add the geometry and primitive identifier */ }; +struct ssol_instantiated_shaded_shape { + struct ssol_shape* shape; + struct ssol_material* mtl_front; + struct ssol_material* mtl_back; + + /* Internal data */ + double R__[9]; + double T__[3]; + double R_invtrans__[9]; +}; + +#define SSOL_INSTANTIATED_SHADED_SHAPE_NULL__ { 0 } +static const struct ssol_instantiated_shaded_shape +SSOL_INSTANTIATED_SHADED_SHAPE_NULL = SSOL_INSTANTIATED_SHADED_SHAPE_NULL__; + +struct ssol_path_tracker { + /* Control the length of the path segment starting/ending from/to the + * infinite. A value less than zero means for default value */ + double sun_ray_length; + double infinite_ray_length; +}; + +#define SSOL_PATH_TRACKER_DEFAULT__ {-1, -1} +static const struct ssol_path_tracker SSOL_PATH_TRACKER_DEFAULT = + SSOL_PATH_TRACKER_DEFAULT__; + +struct ssol_path { + /* Internal data */ + const void* path__; +}; + +struct ssol_path_vertex { + double pos[3]; /* Position */ + double weight; /* Monte-Carlo weight */ +}; + struct ssol_mc_result { double E; /* Expectation */ double V; /* Variance */ double SE; /* Standard error, i.e. sqrt(Expectation / N) */ }; +#define SSOL_MC_RESULT_NULL__ {0, 0, 0} +static const struct ssol_mc_result SSOL_MC_RESULT_NULL = SSOL_MC_RESULT_NULL__; -/* result for MC simulations */ -struct ssol_estimator_status { - struct ssol_mc_result irradiance; - struct ssol_mc_result absorptivity_loss; - struct ssol_mc_result reflectivity_loss; - struct ssol_mc_result cos_loss; - size_t N; /* Samples count */ - size_t Nf; /* Failed samples count */ +struct ssol_mc_global { + struct ssol_mc_result cos_loss; /* In W */ + struct ssol_mc_result shadowed; /* In W */ + struct ssol_mc_result missing; /* In W */ }; - -/* the always-ON indicators (MC computations) */ -enum ssol_status_type { - SSOL_STATUS_SHADOW, - SSOL_STATUS_MISSING, - SSOL_STATUS_TYPES_COUNT__ +#define SSOL_MC_GLOBAL_NULL__ { \ + SSOL_MC_RESULT_NULL__, \ + SSOL_MC_RESULT_NULL__, \ + SSOL_MC_RESULT_NULL__ \ +} +static const struct ssol_mc_global SSOL_MC_GLOBAL_NULL = SSOL_MC_GLOBAL_NULL__; + +struct ssol_mc_receiver { + struct ssol_mc_result integrated_irradiance; /* In W */ + struct ssol_mc_result absorptivity_loss; /* In W */ + struct ssol_mc_result reflectivity_loss; /* In W */ + struct ssol_mc_result cos_loss; /* In W TODO remove this */ + + /* Internal data */ + size_t N__; + void* mc__; + const struct ssol_instance* instance__; +}; +#define SSOL_MC_RECEIVER_NULL__ { \ + SSOL_MC_RESULT_NULL__, \ + SSOL_MC_RESULT_NULL__, \ + SSOL_MC_RESULT_NULL__, \ + SSOL_MC_RESULT_NULL__, \ + 0, NULL, NULL \ +} +static const struct ssol_mc_receiver SSOL_MC_RECEIVER_NULL = + SSOL_MC_RECEIVER_NULL__; + +struct ssol_mc_shape { + /* Internal data */ + size_t N__; + void* mc__; + const struct ssol_shape* shape__; +}; +#define SSOL_MC_SHAPE_NULL__ { 0, NULL, NULL } +static const struct ssol_mc_shape SSOL_MC_SHAPE_NULL = SSOL_MC_SHAPE_NULL__; + +struct ssol_mc_primitive { + struct ssol_mc_result integrated_irradiance; /* In W */ + struct ssol_mc_result absorptivity_loss; /* In W */ + struct ssol_mc_result reflectivity_loss; /* In W */ + struct ssol_mc_result cos_loss; /* In W TODO remove this */ }; +#define SSOL_MC_PRIMITIVE_NULL__ { \ + SSOL_MC_RESULT_NULL__, \ + SSOL_MC_RESULT_NULL__, \ + SSOL_MC_RESULT_NULL__, \ + SSOL_MC_RESULT_NULL__ \ +} +static const struct ssol_mc_primitive SSOL_MC_PRIMITIVE_NULL = + SSOL_MC_PRIMITIVE_NULL__; typedef res_T (*ssol_write_pixels_T) @@ -427,6 +559,12 @@ ssol_scene_detach_instance (struct ssol_scene* scn, struct ssol_instance* instance); +SSOL_API res_T +ssol_scene_compute_aabb + (const struct ssol_scene* scn, + float lower[3], + float upper[3]); + /* Detach all the instances from the scene and release the reference that the * scene takes onto them. * Also detach the attached sun if any. */ @@ -454,6 +592,12 @@ ssol_scene_detach_atmosphere (struct ssol_scene* scn, struct ssol_atmosphere* atm); +SSOL_API res_T +ssol_scene_for_each_instance + (struct ssol_scene* scn, + res_T (*func)(struct ssol_instance* instance, void* ctx), + void* ctx); + /******************************************************************************* * Shape API - Define a geometry that can be generated from a quadric equation * or from a triangular mesh. @@ -476,6 +620,29 @@ SSOL_API res_T ssol_shape_ref_put (struct ssol_shape* shape); +SSOL_API res_T +ssol_shape_get_vertices_count + (const struct ssol_shape* shape, + unsigned* nverts); + +SSOL_API res_T +ssol_shape_get_vertex_attrib + (const struct ssol_shape* shape, + const unsigned ivert, + const enum ssol_attrib_usage usage, + double value[]); + +SSOL_API res_T +ssol_shape_get_triangles_count + (const struct ssol_shape* shape, + unsigned* ntris); + +SSOL_API res_T +ssol_shape_get_triangle_indices + (const struct ssol_shape* shape, + const unsigned itri, + unsigned ids[3]); + /* Define a punched surface in local space, i.e. no translation & no orientation */ SSOL_API res_T ssol_punched_surface_setup @@ -500,6 +667,11 @@ ssol_mesh_setup * (e.g.: refractive index) properties of a geometry. ******************************************************************************/ SSOL_API res_T +ssol_material_create_dielectric + (struct ssol_device* dev, + struct ssol_material** mtl); + +SSOL_API res_T ssol_material_create_mirror (struct ssol_device* dev, struct ssol_material** mtl); @@ -515,6 +687,16 @@ ssol_material_create_virtual struct ssol_material** mtl); SSOL_API res_T +ssol_material_create_thin_dielectric + (struct ssol_device* dev, + struct ssol_material** mtl); + +SSOL_API res_T +ssol_material_get_type + (const struct ssol_material* mtl, + enum ssol_material_type* type); + +SSOL_API res_T ssol_material_ref_get (struct ssol_material* mtl); @@ -528,15 +710,30 @@ ssol_material_set_param_buffer struct ssol_param_buffer* buf); SSOL_API res_T -ssol_mirror_set_shader +ssol_dielectric_setup + (struct ssol_material* mtl, + const struct ssol_dielectric_shader* shader, + const struct ssol_medium* outside_medium, + const struct ssol_medium* inside_medium); + +SSOL_API res_T +ssol_mirror_setup (struct ssol_material* mtl, const struct ssol_mirror_shader* shader); SSOL_API res_T -ssol_matte_set_shader +ssol_matte_setup (struct ssol_material* mtl, const struct ssol_matte_shader* shader); +SSOL_API res_T +ssol_thin_dielectric_setup + (struct ssol_material* mtl, + const struct ssol_thin_dielectric_shader* shader, + const struct ssol_medium* outside_medium, + const struct ssol_medium* slab_medium, + const double thickness); + /******************************************************************************* * Object API - Opaque abstraction of a geometry with its associated properties. ******************************************************************************/ @@ -565,6 +762,12 @@ SSOL_API res_T ssol_object_clear (struct ssol_object* object); +/* Retrieve the area of the object */ +SSOL_API res_T +ssol_object_get_area + (const struct ssol_object* object, + double* area); + /******************************************************************************* * Object Instance API - Clone of an object with a set of per instance data as * world transformation, material parameters, etc. Note that the object @@ -593,7 +796,8 @@ ssol_instance_set_transform SSOL_API res_T ssol_instance_set_receiver (struct ssol_instance* instance, - const int mask); /* Combination of ssol_side_flag */ + const int mask, /* Combination of ssol_side_flag */ + const int per_primitive); /* Enable the per primitive integration */ /* Define whether or not the instance is sampled or not. By default an instance * is sampled. */ @@ -608,6 +812,30 @@ ssol_instance_get_id (const struct ssol_instance* instance, uint32_t* id); +/* Retrieve the area of the instance */ +SSOL_API res_T +ssol_instance_get_area + (const struct ssol_instance* instance, + double* area); + +SSOL_API res_T +ssol_instance_get_shaded_shapes_count + (const struct ssol_instance* instance, + size_t* nshaded_shapes); + +SSOL_API res_T +ssol_instance_get_shaded_shape + (const struct ssol_instance* instance, + const size_t ishaded_shape, + struct ssol_instantiated_shaded_shape* shaded_shape_instance); + +SSOL_API res_T +ssol_instantiated_shaded_shape_get_vertex_attrib + (const struct ssol_instantiated_shaded_shape* sshape, + const unsigned ivert, + const enum ssol_attrib_usage usage, + double value[]); + /******************************************************************************* * Param buffer API ******************************************************************************/ @@ -767,17 +995,17 @@ ssol_estimator_ref_put (struct ssol_estimator* estimator); SSOL_API res_T -ssol_estimator_get_status +ssol_estimator_get_mc_global (const struct ssol_estimator* estimator, - const enum ssol_status_type type, - struct ssol_estimator_status* status); + struct ssol_mc_global* mc_global); SSOL_API res_T -ssol_estimator_get_receiver_status +ssol_estimator_get_mc_sampled_x_receiver (struct ssol_estimator* estimator, - const struct ssol_instance* instance, + const struct ssol_instance* prim_instance, + const struct ssol_instance* recv_instance, const enum ssol_side_flag side, - struct ssol_estimator_status* status); + struct ssol_mc_receiver* rcv); SSOL_API res_T ssol_estimator_get_count @@ -789,15 +1017,63 @@ ssol_estimator_get_failed_count (const struct ssol_estimator* estimator, size_t* count); +/* Retrieve the overall area of the sampled instances */ SSOL_API res_T ssol_estimator_get_sampled_area (const struct ssol_estimator* estimator, double* area); +/******************************************************************************* + * Tracked paths + ******************************************************************************/ SSOL_API res_T -ssol_estimator_get_primary_area +ssol_estimator_get_tracked_paths_count (const struct ssol_estimator* estimator, - double* area); + size_t* npaths); + +SSOL_API res_T +ssol_estimator_get_tracked_path + (const struct ssol_estimator* estimator, + const size_t ipath, + struct ssol_path* path); + +SSOL_API res_T +ssol_path_get_vertices_count + (const struct ssol_path* path, + size_t* nvertices); + +SSOL_API res_T +ssol_path_get_vertex + (const struct ssol_path* path, + const size_t ivertex, + struct ssol_path_vertex* vertex); + +SSOL_API res_T +ssol_path_get_type + (const struct ssol_path* path, + enum ssol_path_type* type); + +/******************************************************************************* + * Per receiver MC estimations + ******************************************************************************/ +SSOL_API res_T +ssol_estimator_get_mc_receiver + (struct ssol_estimator* estimator, + const struct ssol_instance* instance, + const enum ssol_side_flag side, + struct ssol_mc_receiver* rcv); + +SSOL_API res_T +ssol_mc_receiver_get_mc_shape + (struct ssol_mc_receiver* rcv, + const struct ssol_shape* shape, + struct ssol_mc_shape* mc); + +SSOL_API res_T +ssol_mc_shape_get_mc_primitive + (struct ssol_mc_shape* shape, + const unsigned i, /* In [0, ssol_shape_get_triangles_count[ */ + struct ssol_mc_primitive* prim); /******************************************************************************* * Miscellaneous functions @@ -807,15 +1083,27 @@ ssol_solve (struct ssol_scene* scn, struct ssp_rng* rng, const size_t realisations_count, + const struct ssol_path_tracker* tracker, /* NULL<=>Do not record the paths */ FILE* output, /* May be NULL <=> does not ouput ssol_receiver_data */ struct ssol_estimator** estimator); SSOL_API res_T -ssol_draw +ssol_draw_draft + (struct ssol_scene* scn, + struct ssol_camera* cam, + const size_t width, /* #pixels in X */ + const size_t height, /* #pixels in Y */ + const size_t spp, /* #samples per pixel */ + ssol_write_pixels_T writer, + void* writer_data); + +SSOL_API res_T +ssol_draw_pt (struct ssol_scene* scn, struct ssol_camera* cam, const size_t width, /* #pixels in X */ const size_t height, /* #pixels in Y */ + const size_t spp, ssol_write_pixels_T writer, void* writer_data); diff --git a/src/ssol_atmosphere.c b/src/ssol_atmosphere.c @@ -123,24 +123,13 @@ ssol_atmosphere_set_uniform_absorption * Local functions ******************************************************************************/ double -compute_atmosphere_transmissivity +atmosphere_uniform_get_absorption (const struct ssol_atmosphere* atmosphere, - const double distance, const double wavelength) { - double ka; const struct ssol_spectrum* spectrum; - if (!atmosphere) - return 1; - - ASSERT(distance >= 0 && wavelength >= 0); - switch (atmosphere->type) { - case ATMOS_UNIFORM: - spectrum = atmosphere->data.uniform.spectrum; - ka = spectrum_interpolate(spectrum, wavelength); - break; - default: FATAL("Unreachable code\n"); break; - } - return exp(-ka * distance); + ASSERT(atmosphere && atmosphere->type == ATMOS_UNIFORM && wavelength >= 0); + spectrum = atmosphere->data.uniform.spectrum; + return spectrum_interpolate(spectrum, wavelength); } diff --git a/src/ssol_atmosphere_c.h b/src/ssol_atmosphere_c.h @@ -42,9 +42,8 @@ struct ssol_atmosphere { }; extern LOCAL_SYM double -compute_atmosphere_transmissivity +atmosphere_uniform_get_absorption (const struct ssol_atmosphere* atmosphere, - const double distance, const double wavelength); #endif /* SSOL_ATMOSPHERE_C_H */ diff --git a/src/ssol_c.h b/src/ssol_c.h @@ -41,11 +41,11 @@ struct ray_data { /* Output data */ double N[3]; /* Normal of the nearest punched surface point */ - double dst; /* Hit distance of the nearest punced surface point */ + double dst; /* Hit distance of the nearest punched surface point */ }; static const struct ray_data RAY_DATA_NULL = { - NULL, S3D_PRIMITIVE_NULL__, NULL, SSOL_INVALID_SIDE, 0, 0, 0, {0, 0, 0}, 0 + NULL, S3D_PRIMITIVE_NULL__, NULL, SSOL_INVALID_SIDE, 0, 0, 0, {0,0,0}, FLT_MAX }; diff --git a/src/ssol_device.c b/src/ssol_device.c @@ -19,6 +19,7 @@ #include <rsys/logger.h> #include <rsys/mem_allocator.h> +#include <star/s3d.h> #include <star/scpr.h> #include <omp.h> diff --git a/src/ssol_device_c.h b/src/ssol_device_c.h @@ -19,7 +19,6 @@ #include <rsys/dynamic_array.h> #include <rsys/free_list.h> #include <rsys/ref_count.h> -#include <star/s3d.h> #define DARRAY_NAME byte #define DARRAY_DATA char @@ -35,6 +34,7 @@ #include <rsys/dynamic_array.h> struct scpr_mesh; +struct s3d_device; struct ssol_device { struct logger* logger; diff --git a/src/ssol_draw.c b/src/ssol_draw.c @@ -14,16 +14,15 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "ssol.h" -#include "ssol_c.h" -#include "ssol_camera.h" #include "ssol_device_c.h" +#include "ssol_draw.h" #include "ssol_scene_c.h" -#include <rsys/double3.h> -#include <rsys/math.h> #include <star/s3d.h> #include <omp.h> +#include <star/ssf.h> +#include <star/ssp.h> #define TILE_SIZE 32 /* definition in X & Y of a tile */ STATIC_ASSERT(IS_POW2(TILE_SIZE), TILE_SIZE_must_be_a_power_of_2); @@ -43,41 +42,23 @@ morton2D_decode(const uint32_t u32) } static void -Li(struct ssol_scene* scn, - struct s3d_scene_view* view, - const float org[3], - const float dir[3], - double val[3]) -{ - const float range[2] = {0, FLT_MAX}; - struct ray_data ray_data = RAY_DATA_NULL; - struct s3d_hit hit; - - ray_data.scn = scn; - ray_data.discard_virtual_materials = 1; - S3D(scene_view_trace_ray(view, org, dir, range, &ray_data, &hit)); - if(S3D_HIT_NONE(&hit)) { - d3_splat(val, 0); - } else { - float N[3]={0}; - f3_normalize(N, hit.normal); - d3_splat(val, fabs(f3_dot(N, dir))); - } -} - -static void draw_tile (struct ssol_scene* scn, struct s3d_scene_view* view, const struct ssol_camera* cam, + const int ithread, + const size_t spp, const size_t origin[2], /* Tile origin */ const size_t size[2], /* Tile definition */ const float pix_sz[2], /* Normalized size of a pixel in the image plane */ - double* pixels) + double* pixels, + pixel_shader_T shader, + void* shader_data) { size_t npixels; size_t mcode; /* Morton code of the tile pixel */ - ASSERT(scn && view && cam && origin && size && pix_sz && pixels); + ASSERT(scn && view && cam && spp && origin && size && pix_sz && pixels); + ASSERT(shader); /* Adjust the #pixels to process them wrt a morton order */ npixels = round_up_pow2(MMAX(size[0], size[1])); @@ -85,7 +66,6 @@ draw_tile FOR_EACH(mcode, 0, npixels) { size_t ipix[2]; - float org[3], dir[3], samp[2]; double* pixel; ipix[0] = morton2D_decode((uint32_t)(mcode>>0)); @@ -94,29 +74,27 @@ draw_tile if(ipix[1] >= size[1]) continue; pixel = pixels + (ipix[1]*size[0] + ipix[0])*3/*#channels*/; - ipix[0] = ipix[0] + origin[0]; ipix[1] = ipix[1] + origin[1]; - samp[0] = ((float)ipix[0] + 0.5f) * pix_sz[0]; - samp[1] = ((float)ipix[1] + 0.5f) * pix_sz[1]; - - camera_ray(cam, samp, org, dir); - Li(scn, view, org, dir, pixel); + shader(scn, cam, view, ithread, ipix, pix_sz, spp, pixel, shader_data); } } /******************************************************************************* - * Exported function + * Local function ******************************************************************************/ res_T -ssol_draw +draw (struct ssol_scene* scn, - struct ssol_camera* cam, + const struct ssol_camera* cam, const size_t width, const size_t height, + const size_t spp, /* #samples per pixel */ ssol_write_pixels_T writer, - void* data) + void* writer_data, + pixel_shader_T pixel_shader, + void* pixel_shader_data) { struct s3d_scene_view* view = NULL; struct darray_byte* tiles = NULL; @@ -126,10 +104,8 @@ ssol_draw size_t i; ATOMIC res = RES_OK; - if(!scn || !cam || !width || !height || !writer) { - res = RES_BAD_ARG; - goto error; - } + if(!scn || !cam || !width || !height || !spp || !writer || !pixel_shader) + return RES_BAD_ARG; tiles = darray_tile_data_get(&scn->dev->tiles); ASSERT(darray_tile_size_get(&scn->dev->tiles) == scn->dev->nthreads); @@ -154,7 +130,7 @@ ssol_draw for(mcode=0; mcode<(int64_t)ntiles; ++mcode) { size_t tile_org[2]; size_t tile_sz[2]; - int ithread = omp_get_thread_num(); + const int ithread = omp_get_thread_num(); double* pixels; res_T res_local; @@ -172,9 +148,10 @@ ssol_draw pixels = (double*)darray_byte_data_get(tiles+ithread); - draw_tile(scn, view, cam, tile_org, tile_sz, pix_sz, pixels); + draw_tile(scn, view, cam, ithread, spp, tile_org, tile_sz, pix_sz, pixels, + pixel_shader, pixel_shader_data); - res_local = writer(data, tile_org, tile_sz, SSOL_PIXEL_DOUBLE3, pixels); + res_local = writer(writer_data, tile_org, tile_sz, SSOL_PIXEL_DOUBLE3, pixels); if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; @@ -188,3 +165,4 @@ error: goto exit; } + diff --git a/src/ssol_draw.h b/src/ssol_draw.h @@ -0,0 +1,52 @@ +/* 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/>. */ + +#ifndef SSOL_DRAW_H +#define SSOL_DRAW_H + +#include "ssol.h" +#include <rsys/rsys.h> + +/* Forward declarations */ +struct s3d_scene_view; +struct ssf_bsdf; +struct ssp_rng; + +typedef void +(*pixel_shader_T) + (struct ssol_scene* scn, + const struct ssol_camera* cam, + struct s3d_scene_view* view, + const int ithread, /* Id of the thread invoking the function */ + const size_t pix_coords[2], /* Image space pixel coordinates */ + const float pix_sz[2], /* Normalized pixel size */ + const size_t nsamples, /* #samples per pixel */ + double pixel[3], /* Output pixel */ + void* ctx); /* User defined data */ + +extern LOCAL_SYM res_T +draw + (struct ssol_scene* scn, + const struct ssol_camera* cam, + const size_t width, + const size_t height, + const size_t spp, + ssol_write_pixels_T writer, + void* writer_data, + pixel_shader_T pixel_shader, + void* pixel_shader_data); + +#endif /* SSOL_DRAW_H */ + diff --git a/src/ssol_draw_draft.c b/src/ssol_draw_draft.c @@ -0,0 +1,162 @@ +/* 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_c.h" +#include "ssol_camera.h" +#include "ssol_device_c.h" +#include "ssol_draw.h" +#include "ssol_object_c.h" +#include "ssol_scene_c.h" +#include "ssol_shape_c.h" + +#include <rsys/double3.h> +#include <rsys/dynamic_array_float.h> +#include <rsys/float3.h> + +#include <star/ssp.h> + +#include <float.h> + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static void +Li + (struct ssol_scene* scn, + struct s3d_scene_view* view, + const float org[3], + const float dir[3], + double val[3]) +{ + const float range[2] = {0, FLT_MAX}; + struct ray_data ray_data = RAY_DATA_NULL; + struct s3d_hit hit; + ASSERT(scn && view && org && dir && val); + + ray_data.scn = scn; + ray_data.discard_virtual_materials = 1; + S3D(scene_view_trace_ray(view, org, dir, range, &ray_data, &hit)); + if(S3D_HIT_NONE(&hit)) { + d3_splat(val, 0); + } else { + struct ssol_instance* inst; + const struct shaded_shape* sshape; + size_t isshape; + float N[3]={0}; + + /* Retrieve the hit shaded shape */ + inst = *htable_instance_find(&scn->instances_rt, &hit.prim.inst_id); + isshape = *htable_shaded_shape_find + (&inst->object->shaded_shapes_rt, &hit.prim.geom_id); + sshape = darray_shaded_shape_cdata_get + (&inst->object->shaded_shapes) + isshape; + + /* Retrieve and normalized the hit normal */ + switch(sshape->shape->type) { + case SHAPE_MESH: f3_normalize(N, hit.normal); break; + case SHAPE_PUNCHED: f3_normalize(N, f3_set_d3(N, ray_data.N)); break; + default: FATAL("Unreachable code"); break; + } + ASSERT(f3_is_normalized(N)); + d3_splat(val, fabs(f3_dot(N, dir))); + } +} + +static void +draw_pixel + (struct ssol_scene* scn, + const struct ssol_camera* cam, + struct s3d_scene_view* view, + const int ithread, + const size_t pix_coords[2], /* Image space pixel coordinates */ + const float pix_sz[2], /* Normalized pixel size */ + const size_t nsamples, + double pixel[3], + void* ctx) +{ + struct darray_float* samples = ctx; + float samp[2]; + float ray_org[3], ray_dir[3]; + double sum[3] = {0, 0, 0}; + size_t i; + ASSERT(scn && cam && view && pix_coords && pix_sz && nsamples && pixel && ctx); + (void)ithread; + + FOR_EACH(i, 0, nsamples) { + double weight[3]; + const float* r = darray_float_cdata_get(samples) + i*2; + + /* Generate a sample into the pixel */ + samp[0] = ((float)pix_coords[0] + r[0]) * pix_sz[0]; + samp[1] = ((float)pix_coords[1] + r[1]) * pix_sz[1]; + + /* Generate a ray starting from the pinhole camera and passing through the + * pixel sample */ + camera_ray(cam, samp, ray_org, ray_dir); + + /* Compute the radiance arriving through the sampled camera ray */ + Li(scn, view, ray_org, ray_dir, weight); + d3_add(sum, sum, weight); + } + d3_divd(pixel, sum, (double)nsamples); +} + + +/******************************************************************************* + * Exported function + ******************************************************************************/ +res_T +ssol_draw_draft + (struct ssol_scene* scn, + struct ssol_camera* cam, + const size_t width, + const size_t height, + const size_t spp, + ssol_write_pixels_T writer, + void* data) +{ + struct darray_float samples; + struct ssp_rng* rng = NULL; + size_t i; + res_T res = RES_OK; + + if(!scn || !spp) return RES_BAD_ARG; + + darray_float_init(scn->dev->allocator, &samples); + res = darray_float_reserve(&samples, spp * 2/*#dimensions*/); + if(res != RES_OK) goto error; + + res = ssp_rng_create(scn->dev->allocator, &ssp_rng_threefry, &rng); + if(res != RES_OK) goto error; + + /* Generate the pixel samples */ + FOR_EACH(i, 0, spp) { + const float x = ssp_rng_canonical_float(rng); + const float y = ssp_rng_canonical_float(rng); + darray_float_push_back(&samples, &x); + darray_float_push_back(&samples, &y); + } + + res = draw(scn, cam, width, height, spp, writer, data, draw_pixel, &samples); + if(res != RES_OK) goto error; + +exit: + darray_float_release(&samples); + if(rng) SSP(rng_ref_put(rng)); + return res; +error: + goto exit; +} + diff --git a/src/ssol_draw_pt.c b/src/ssol_draw_pt.c @@ -0,0 +1,360 @@ +/* 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_c.h" +#include "ssol_camera.h" +#include "ssol_device_c.h" +#include "ssol_draw.h" +#include "ssol_material_c.h" +#include "ssol_object_c.h" +#include "ssol_scene_c.h" +#include "ssol_shape_c.h" +#include "ssol_sun_c.h" + +#include <rsys/double2.h> +#include <rsys/double3.h> +#include <rsys/float3.h> + +#include <star/s3d.h> +#include <star/ssf.h> +#include <star/ssp.h> + +/******************************************************************************* + * Per thread draw_pt context + ******************************************************************************/ +struct thread_context { + struct ssp_rng* rng; + struct ssf_bsdf* bsdf; +}; + +static void +thread_context_release(struct thread_context* ctx) +{ + ASSERT(ctx); + if(ctx->rng) SSP(rng_ref_put(ctx->rng)); + if(ctx->bsdf) SSF(bsdf_ref_put(ctx->bsdf)); +} + +static res_T +thread_context_init + (struct mem_allocator* allocator, + struct thread_context* ctx) +{ + res_T res = RES_OK; + ASSERT(ctx); + memset(ctx, 0, sizeof(ctx[0])); + res = ssf_bsdf_create(allocator, &ctx->bsdf); + if(res != RES_OK) goto error; +exit: + return res; +error: + thread_context_release(ctx); + goto exit; +} + +static void +thread_context_setup + (struct thread_context* ctx, + struct ssp_rng* rng) +{ + ASSERT(ctx && rng); + if(ctx->rng) SSP(rng_ref_put(ctx->rng)); + SSP(rng_ref_get(rng)); + ctx->rng = rng; +} + +/* Declare the container of the per thread contexts */ +#define DARRAY_NAME thread_context +#define DARRAY_DATA struct thread_context +#define DARRAY_FUNCTOR_INIT thread_context_init +#define DARRAY_FUNCTOR_RELEASE thread_context_release +#include <rsys/dynamic_array.h> + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE double +sun_lighting + (struct ssol_sun* sun, + struct s3d_scene_view* view, + struct ray_data* ray_data, + struct ssf_bsdf* bsdf, + const double wo[3], + const double N[3], + const float ray_org[3]) +{ + struct s3d_hit hit; + const float ray_range[2] = {0, FLT_MAX}; + double wi[3]; + float ray_dir[3]; + double cos_wi_N; + double R; + ASSERT(sun && view && ray_data && bsdf && wo && N && ray_org); + ASSERT(d3_dot(wo, N) >= 0); /* Assume that wo point outward the surface */ + + /* Ensure that the incoming direction point outward the surface */ + d3_minus(wi, sun->direction); + + /* The point look backward the sun */ + if(d3_dot(wi, N) < 0) return 0.0; + + R = ssf_bsdf_eval(bsdf, wo, N, wi); + if(R <= 0) return 0.0; + + cos_wi_N = d3_dot(wi, N); + + f3_set_d3(ray_dir, wi); + S3D(scene_view_trace_ray(view, ray_org, ray_dir, ray_range, ray_data, &hit)); + if(S3D_HIT_NONE(&hit)) return R * cos_wi_N; + return 0; +} + +static res_T +Li(struct ssol_scene* scn, + struct thread_context* ctx, + struct s3d_scene_view* view, + const float org[3], + const float dir[3], + double val[3]) +{ + struct ssol_medium medium; + struct s3d_hit hit; + struct ray_data ray_data = RAY_DATA_NULL; + struct ssol_instance* inst; + struct ssol_material* mtl; + const struct shaded_shape* sshape; + struct surface_fragment frag; + size_t isshape; + double throughput = 1.0; + double wi[3], o[3], uv[3]; + double wo[3]; + double N[3]; + double L = 0; + double R; + double pdf; + const float ray_range[2] = {0, FLT_MAX}; + float ray_org[3]; + float ray_dir[3]; + enum ssol_side_flag side; + int russian_roulette = 0; + int type; + res_T res = RES_OK; + ASSERT(scn && view && org && dir && val); + + ray_data.scn = scn; + ray_data.discard_virtual_materials = 1; + + f3_set(ray_org, org); + f3_set(ray_dir, dir); + + /* Assume that the path starts from vacuum */ + medium = SSOL_MEDIUM_VACUUM; + + for(;;) { + S3D(scene_view_trace_ray + (view, ray_org, ray_dir, ray_range, &ray_data, &hit)); + + if(medium.absorptivity > 0) { + throughput *= exp(-medium.absorptivity * hit.distance); + } + + if(S3D_HIT_NONE(&hit)) { /* Background lighting */ + if(ray_dir[2] > 0) L += throughput * 1.e-1; + break; + } + + /* Retrieve the hit shaded shape */ + inst = *htable_instance_find(&scn->instances_rt, &hit.prim.inst_id); + isshape = *htable_shaded_shape_find + (&inst->object->shaded_shapes_rt, &hit.prim.geom_id); + sshape = darray_shaded_shape_cdata_get(&inst->object->shaded_shapes)+isshape; + + d3_set_f3(o, ray_org); + d3_set_f3(wo, ray_dir); + d2_set_f2(uv, hit.uv); + d3_normalize(wo, wo); + + /* Retrieve and normalized the hit normal */ + switch(sshape->shape->type) { + case SHAPE_MESH: d3_normalize(N, d3_set_f3(N, hit.normal)); break; + case SHAPE_PUNCHED: d3_normalize(N, ray_data.N); break; + default: FATAL("Unreachable code"); break; + } + + if(d3_dot(N, wo) < 0) { + mtl = sshape->mtl_front; + side = SSOL_FRONT; + } else { + mtl = sshape->mtl_back; + side = SSOL_BACK; + d3_minus(N, N); + } + + surface_fragment_setup(&frag, o, wo, N, &hit.prim, hit.uv); + SSF(bsdf_clear(ctx->bsdf)); + res = material_shade_rendering + (mtl, &frag, 1/*TODO wavelength*/, &medium, ctx->bsdf); + if(res != RES_OK) goto error; + + /* Update the ray */ + ray_data.prim_from = hit.prim; + ray_data.inst_from = inst; + ray_data.side_from = side; + f3_mulf(ray_dir, ray_dir, hit.distance); + f3_add(ray_org, ray_org, ray_dir); + + d3_minus(wo, wo); + if(scn->sun) { + L += throughput * sun_lighting + (scn->sun, view, &ray_data, ctx->bsdf, wo, N, ray_org); + } + + R = ssf_bsdf_sample(ctx->bsdf, ctx->rng, wo, frag.Ns, wi, &type, &pdf); + ASSERT(0 <= R && R <= 1); + f3_set_d3(ray_dir, wi); + if(type & SSF_TRANSMISSION) material_get_next_medium(mtl, &medium, &medium); + + if(!russian_roulette) { + throughput *= fabs(d3_dot(wi, N)) * R; + } else { + if(ssp_rng_canonical(ctx->rng) >= R) break; + throughput *= d3_dot(wi, N); + } + + if(throughput <= 0) break; + + if(!russian_roulette) { + russian_roulette = throughput < 0.1; + } + } + d3_splat(val, L); + +exit: + return res; +error: + d3(val, 1, 1, 0); + goto exit; +} + +static void +draw_pixel + (struct ssol_scene* scn, + const struct ssol_camera* cam, + struct s3d_scene_view* view, + const int ithread, + const size_t pix_coords[2], /* Image space pixel coordinates */ + const float pix_sz[2], /* Normalized pixel size */ + const size_t nsamples, + double pixel[3], + void* data) +{ + struct darray_thread_context* thread_ctxs = data; + struct thread_context* ctx; + double sum[3] = {0, 0, 0}; + size_t isample; + res_T res = RES_OK; + ASSERT(scn && cam && pix_coords && pix_sz && nsamples && pixel && data); + ASSERT((size_t)ithread < darray_thread_context_size_get(thread_ctxs)); + + ctx = darray_thread_context_data_get(thread_ctxs) + ithread; + + FOR_EACH(isample, 0, nsamples) { + const int MAX_NFAILURES = 10; + double weight[3]; + float samp[2]; /* Pixel sample */ + float ray_org[3], ray_dir[3]; + int nfailures = 0; + + /* Generate a sample into the pixel */ + samp[0] = ((float)pix_coords[0]+ssp_rng_canonical_float(ctx->rng))*pix_sz[0]; + samp[1] = ((float)pix_coords[1]+ssp_rng_canonical_float(ctx->rng))*pix_sz[1]; + + do { + /* Generate a ray starting from the pinhole camera and passing through the + * pixel sample */ + camera_ray(cam, samp, ray_org, ray_dir); + + /* Compute the radiance arriving through the sampled camera ray */ + res = Li(scn, ctx, view, ray_org, ray_dir, weight); + } while(res == RES_BAD_OP && ++nfailures < MAX_NFAILURES); + if(res != RES_OK) goto error; + + d3_add(sum, sum, weight); + } + + d3_divd(pixel, sum, (double)nsamples); +exit: + return; +error: + log_error(scn->dev, "Path tracing integrator error.\n"); + d3(pixel, 1, 1, 0); + goto exit; +} + +/******************************************************************************* + * Exported function + ******************************************************************************/ +res_T +ssol_draw_pt + (struct ssol_scene* scn, + struct ssol_camera* cam, + const size_t width, + const size_t height, + const size_t spp, + ssol_write_pixels_T writer, + void* data) +{ + struct darray_thread_context thread_ctxs; + struct ssp_rng_proxy* rng_proxy = NULL; + size_t i; + res_T res = RES_OK; + + if(!scn) + return RES_BAD_ARG; + + darray_thread_context_init(scn->dev->allocator, &thread_ctxs); + + /* Create a RNG proxy */ + res = ssp_rng_proxy_create + (scn->dev->allocator, &ssp_rng_threefry, scn->dev->nthreads, &rng_proxy); + if(res != RES_OK) goto error; + + /* Create the thread contexts */ + res = darray_thread_context_resize(&thread_ctxs, scn->dev->nthreads); + if(res != RES_OK) goto error; + FOR_EACH(i, 0, scn->dev->nthreads) { + struct thread_context* ctx; + struct ssp_rng* rng; + + ctx = darray_thread_context_data_get(&thread_ctxs)+i; + + res = ssp_rng_proxy_create_rng(rng_proxy, i, &rng); + if(res != RES_OK) goto error; + + thread_context_setup(ctx, rng); + SSP(rng_ref_put(rng)); + } + + /* Invoke the draw process */ + res = draw(scn, cam, width, height, spp, writer, data, draw_pixel, &thread_ctxs); + if(res != RES_OK) goto error; + +exit: + darray_thread_context_release(&thread_ctxs); + if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); + return (res_T)res; +error: + goto exit; +} diff --git a/src/ssol_estimator.c b/src/ssol_estimator.c @@ -20,9 +20,10 @@ #include "ssol_device_c.h" #include "ssol_instance_c.h" -#include <rsys/rsys.h> +#include <rsys/double3.h> #include <rsys/mem_allocator.h> #include <rsys/ref_count.h> +#include <rsys/rsys.h> #include <math.h> @@ -30,31 +31,42 @@ * Helper functions ******************************************************************************/ static res_T -create_per_receiver_mc_data +create_mc_receivers (struct ssol_estimator* estimator, struct ssol_scene* scene) { struct htable_instance_iterator it, end; + struct mc_receiver mc_rcv_null; + struct mc_sampled mc_samp_null; res_T res = RES_OK; ASSERT(scene && estimator); htable_instance_begin(&scene->instances_rt, &it); htable_instance_end(&scene->instances_rt, &end); + mc_receiver_init(estimator->dev->allocator, &mc_rcv_null); + mc_sampled_init(estimator->dev->allocator, &mc_samp_null); + while(!htable_instance_iterator_eq(&it, &end)) { const struct ssol_instance* inst = *htable_instance_iterator_data_get(&it); htable_instance_iterator_next(&it); - if(!inst->receiver_mask) continue; - - res = htable_receiver_set - (&estimator->global_receivers, &inst, &MC_RECV_DATA_NULL); - if(res != RES_OK) goto error; + if(inst->receiver_mask) { + res = htable_receiver_set(&estimator->mc_receivers, &inst, &mc_rcv_null); + if(res != RES_OK) goto error; + } + if(inst->sample) { + res = htable_sampled_set(&estimator->mc_sampled, &inst, &mc_samp_null); + if(res != RES_OK) goto error; + } } exit: + mc_receiver_release(&mc_rcv_null); + mc_sampled_release(&mc_samp_null); return res; error: - htable_receiver_clear(&estimator->global_receivers); + htable_receiver_clear(&estimator->mc_receivers); + htable_sampled_clear(&estimator->mc_sampled); goto exit; } @@ -66,7 +78,9 @@ estimator_release(ref_T* ref) CONTAINER_OF(ref, struct ssol_estimator, ref); ASSERT(ref); dev = estimator->dev; - htable_receiver_release(&estimator->global_receivers); + htable_receiver_release(&estimator->mc_receivers); + htable_sampled_release(&estimator->mc_sampled); + darray_path_release(&estimator->paths); ASSERT(dev && dev->allocator); MEM_RM(dev->allocator, estimator); SSOL(device_ref_put(dev)); @@ -76,89 +90,90 @@ estimator_release(ref_T* ref) * Exported function ******************************************************************************/ res_T -ssol_estimator_ref_get -(struct ssol_estimator* estimator) +ssol_estimator_ref_get(struct ssol_estimator* estimator) { - if (!estimator) return RES_BAD_ARG; + if(!estimator) return RES_BAD_ARG; ref_get(&estimator->ref); return RES_OK; } res_T -ssol_estimator_ref_put -(struct ssol_estimator* estimator) +ssol_estimator_ref_put(struct ssol_estimator* estimator) { - if (!estimator) return RES_BAD_ARG; + if(!estimator) return RES_BAD_ARG; ref_put(&estimator->ref, estimator_release); return RES_OK; } res_T -ssol_estimator_get_status +ssol_estimator_get_mc_global (const struct ssol_estimator* estimator, - const enum ssol_status_type type, - struct ssol_estimator_status* status) + struct ssol_mc_global* global) { - const struct mc_data* data; - if (!estimator || type >= SSOL_STATUS_TYPES_COUNT__ || !status) - return RES_BAD_ARG; - - switch (type) { - case SSOL_STATUS_SHADOW: data = &estimator->shadow; break; - case SSOL_STATUS_MISSING: data = &estimator->missing; break; - default: FATAL("Unreachable code.\n"); break; - } - status->N = estimator->realisation_count; - status->Nf = estimator->failed_count; - status->irradiance.E = data->weight / (double)status->N; - status->irradiance.V - = data->sqr_weight / (double)status->N - - status->irradiance.E * status->irradiance.E; - status->irradiance.SE - = (status->irradiance.V > 0) - ? sqrt(status->irradiance.V / (double)status->N) : 0; - status->absorptivity_loss.E = 0; - status->absorptivity_loss.V = 0; - status->absorptivity_loss.SE = 0; - status->reflectivity_loss.E = 0; - status->reflectivity_loss.V = 0; - status->reflectivity_loss.SE = 0; - status->cos_loss.E = 0; - status->cos_loss.V = 0; - status->cos_loss.SE = 0; + if(!estimator || !global) return RES_BAD_ARG; + #define SETUP_MC_RESULT(Name) { \ + const double N = (double)estimator->realisation_count; \ + const struct mc_data* data = &estimator->Name; \ + global->Name.E = data->weight / N; \ + global->Name.V = data->sqr_weight / N - global->Name.E*global->Name.E; \ + global->Name.SE = global->Name.V > 0 ? sqrt(global->Name.V / N) : 0; \ + } (void)0 + SETUP_MC_RESULT(cos_loss); + SETUP_MC_RESULT(shadowed); + SETUP_MC_RESULT(missing); + #undef SETUP_MC_RESULT return RES_OK; } res_T -ssol_estimator_get_receiver_status +ssol_estimator_get_mc_sampled_x_receiver (struct ssol_estimator* estimator, - const struct ssol_instance* instance, + const struct ssol_instance* samp_instance, + const struct ssol_instance* recv_instance, const enum ssol_side_flag side, - struct ssol_estimator_status* status) + struct ssol_mc_receiver* rcv) { - const struct mc_per_receiver_1side_data* data = NULL; - if (!estimator || !instance || !status - || (side != SSOL_BACK && side != SSOL_FRONT)) + struct mc_sampled* mc_samp = NULL; + struct mc_receiver* mc_rcv = NULL; + struct mc_receiver_1side* mc_rcv1 = NULL; + + if(!estimator || !samp_instance || !recv_instance || !rcv + || (side != SSOL_BACK && side != SSOL_FRONT) + || !samp_instance->sample + || !(recv_instance->receiver_mask & (int)side)) return RES_BAD_ARG; - /* Check if a receiver is defined for this instance/side */ - data = estimator_get_receiver_data - (&estimator->global_receivers, instance, side); - if(data == NULL) return RES_BAD_ARG; + memset(rcv, 0, sizeof(rcv[0])); - status->N = estimator->realisation_count; - status->Nf = estimator->failed_count; - #define SETUP_MC_STATUS(Name) { \ - const double N = (double)estimator->realisation_count; \ - status->Name.E = data->Name.weight / N; \ - status->Name.V = data->Name.sqr_weight/N - status->Name.E*status->Name.E; \ - status->Name.SE = status->Name.V > 0 ? sqrt(status->Name.V / N) : 0; \ + + mc_samp = htable_sampled_find(&estimator->mc_sampled, &samp_instance); + if(!mc_samp || !mc_samp->nb_samples) { + /* The sampled instance has no MC estimation */ + return RES_BAD_ARG; + } + + mc_rcv = htable_receiver_find(&mc_samp->mc_rcvs, &recv_instance); + if(!mc_rcv) { + /* No radiative path starting from the sampled instance reaches the receiver + * instance. */ + return RES_OK; + } + + mc_rcv1 = side == SSOL_FRONT ? &mc_rcv->front : &mc_rcv->back; + #define SETUP_MC_RESULT(Name) { \ + const double N = (double)mc_samp->nb_samples; \ + const struct mc_data* data = &mc_rcv1->Name; \ + rcv->Name.E = data->weight / N; \ + rcv->Name.V = data->sqr_weight / N - rcv->Name.E*rcv->Name.E; \ + rcv->Name.SE = rcv->Name.V > 0 ? sqrt(rcv->Name.V / N) : 0; \ } (void)0 - SETUP_MC_STATUS(irradiance); - SETUP_MC_STATUS(absorptivity_loss); - SETUP_MC_STATUS(reflectivity_loss); - SETUP_MC_STATUS(cos_loss); - #undef SETUP_MC_STATUS + SETUP_MC_RESULT(integrated_irradiance); + SETUP_MC_RESULT(absorptivity_loss); + SETUP_MC_RESULT(reflectivity_loss); + SETUP_MC_RESULT(cos_loss); + #undef SETUP_MC_RESULT + rcv->mc__ = mc_rcv1; + rcv->N__ = mc_samp->nb_samples; return RES_OK; } @@ -182,21 +197,64 @@ ssol_estimator_get_failed_count res_T ssol_estimator_get_sampled_area - (const struct ssol_estimator* estimator, - double* area) + (const struct ssol_estimator* estimator, double* area) { - if (!estimator || !area) return RES_BAD_ARG; + if(!estimator || !area) return RES_BAD_ARG; *area = estimator->sampled_area; return RES_OK; } res_T -ssol_estimator_get_primary_area +ssol_estimator_get_tracked_paths_count + (const struct ssol_estimator* estimator, size_t* npaths) +{ + if(!estimator || !npaths) return RES_BAD_ARG; + *npaths = darray_path_size_get(&estimator->paths); + return RES_OK; +} + +res_T +ssol_estimator_get_tracked_path (const struct ssol_estimator* estimator, - double* area) + const size_t ipath, + struct ssol_path* path) +{ + if(!estimator || ipath >= darray_path_size_get(&estimator->paths) || !path) + return RES_BAD_ARG; + path->path__ = darray_path_cdata_get(&estimator->paths) + ipath; + return RES_OK; +} + +res_T +ssol_path_get_vertices_count(const struct ssol_path* path, size_t* nvertices) +{ + const struct path* p; + if(!path || !nvertices) return RES_BAD_ARG; + p = path->path__; + *nvertices = darray_path_vertex_size_get(&p->vertices); + return RES_OK; +} + +res_T +ssol_path_get_vertex + (const struct ssol_path* path, + const size_t ivertex, + struct ssol_path_vertex* vertex) +{ + const struct path* p; + if(!path || !vertex) return RES_BAD_ARG; + p = path->path__; + if(ivertex >= darray_path_vertex_size_get(&p->vertices)) return RES_BAD_ARG; + *vertex = darray_path_vertex_cdata_get(&p->vertices)[ivertex]; + return RES_OK; +} + +res_T +ssol_path_get_type(const struct ssol_path* path, enum ssol_path_type* type) { - if (!estimator || !area) return RES_BAD_ARG; - *area = estimator->primary_area; + ASSERT(path && type); + if(!path || !type) return RES_BAD_ARG; + *type = ((struct path*)path->path__)->type; return RES_OK; } @@ -223,12 +281,14 @@ estimator_create goto error; } - htable_receiver_init(dev->allocator, &estimator->global_receivers); + htable_receiver_init(dev->allocator, &estimator->mc_receivers); + htable_sampled_init(dev->allocator, &estimator->mc_sampled); + darray_path_init(dev->allocator, &estimator->paths); SSOL(device_ref_get(dev)); estimator->dev = dev; ref_init(&estimator->ref); - res = create_per_receiver_mc_data(estimator, scene); + res = create_mc_receivers(estimator, scene); if(res != RES_OK) goto error; exit: diff --git a/src/ssol_estimator_c.h b/src/ssol_estimator_c.h @@ -1,93 +1,473 @@ /* 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/>. */ + * + * 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_ESTIMATOR_C_H #define SSOL_ESTIMATOR_C_H +#include "ssol_device_c.h" +#include "ssol_instance_c.h" +#include "ssol_shape_c.h" + #include <rsys/ref_count.h> #include <rsys/hash_table.h> +/* Forward declaration */ struct mem_allocator; - -static FINLINE int -side_idx(const enum ssol_side_flag side) -{ - ASSERT(side == SSOL_FRONT || side == SSOL_BACK); - return side == SSOL_FRONT ? 0 : 1; -} +struct ssol_instance; /* Monte carlo data */ struct mc_data { double weight; double sqr_weight; }; - #define MC_DATA_NULL__ { 0, 0 } static const struct mc_data MC_DATA_NULL = MC_DATA_NULL__; -struct mc_per_receiver_1side_data { - struct mc_data irradiance; - struct mc_data absorptivity_loss; - struct mc_data reflectivity_loss; - struct mc_data cos_loss; +#define MC_RECEIVER_DATA \ + struct mc_data integrated_irradiance; /* In W */ \ + struct mc_data absorptivity_loss; /* In W */ \ + struct mc_data reflectivity_loss; /* In W */ \ + struct mc_data cos_loss; /* In W */ + +#define MC_RECEIVER_DATA_NULL__ \ + MC_DATA_NULL__, \ + MC_DATA_NULL__, \ + MC_DATA_NULL__, \ + MC_DATA_NULL__ + +/******************************************************************************* + * One sided per shape MC data + ******************************************************************************/ +struct mc_primitive_1side { + MC_RECEIVER_DATA }; -#define MC_RECV_1SIDE_DATA_NULL__ {\ - MC_DATA_NULL__, MC_DATA_NULL__, MC_DATA_NULL__, MC_DATA_NULL__ } +#define MC_PRIMITIVE_1SIDE_NULL__ { MC_RECEIVER_DATA_NULL__ } +static const struct mc_primitive_1side MC_PRIMITIVE_1SIDE_NULL = + MC_PRIMITIVE_1SIDE_NULL__; + +/* Map an unsigned to a struct mc_primitive_1side */ +#define HTABLE_NAME prim2mc +#define HTABLE_KEY unsigned +#define HTABLE_DATA struct mc_primitive_1side +#include <rsys/hash_table.h> + +struct mc_shape_1side { + struct htable_prim2mc prim2mc; +}; + +static INLINE void +mc_shape_1side_init + (struct mem_allocator* allocator, struct mc_shape_1side* mc) +{ + ASSERT(mc); + htable_prim2mc_init(allocator, &mc->prim2mc); +} + +static INLINE void +mc_shape_1side_release(struct mc_shape_1side* mc) +{ + ASSERT(mc); + htable_prim2mc_release(&mc->prim2mc); +} + +static INLINE res_T +mc_shape_1side_copy + (struct mc_shape_1side* dst, const struct mc_shape_1side* src) +{ + ASSERT(dst && src); + return htable_prim2mc_copy(&dst->prim2mc, &src->prim2mc); +} + +static INLINE res_T +mc_shape_1side_copy_and_release + (struct mc_shape_1side* dst, struct mc_shape_1side* src) +{ + ASSERT(dst && src); + return htable_prim2mc_copy_and_release(&dst->prim2mc, &src->prim2mc); +} + +static INLINE res_T +mc_shape_1side_get_mc_primitive + (struct mc_shape_1side* mc_shape1, + const unsigned iprim, + struct mc_primitive_1side** out_mc_prim1) +{ + struct mc_primitive_1side* mc_prim1 = NULL; + res_T res = RES_OK; + ASSERT(mc_shape1 && out_mc_prim1); + + mc_prim1 = htable_prim2mc_find(&mc_shape1->prim2mc, &iprim); + if(!mc_prim1) { + res = htable_prim2mc_set(&mc_shape1->prim2mc, &iprim, &MC_PRIMITIVE_1SIDE_NULL); + if(res != RES_OK) goto error; + + mc_prim1 = htable_prim2mc_find(&mc_shape1->prim2mc, &iprim); + } + +exit: + *out_mc_prim1 = mc_prim1; + return res; +error: + goto exit; +} -static const struct mc_per_receiver_1side_data -MC_RECV_1SIDE_DATA_NULL = MC_RECV_1SIDE_DATA_NULL__; +/******************************************************************************* + * One sided per receiver MC data + ******************************************************************************/ +/* Map a ssol shape to a struct mc_shape_1side */ +#define HTABLE_NAME shape2mc +#define HTABLE_KEY const struct ssol_shape* +#define HTABLE_DATA struct mc_shape_1side +#define HTABLE_DATA_FUNCTOR_INIT mc_shape_1side_init +#define HTABLE_DATA_FUNCTOR_RELEASE mc_shape_1side_release +#define HTABLE_DATA_FUNCTOR_COPY mc_shape_1side_copy +#define HTABLE_DATA_FUNCTOR_COPY_AND_RELEASE mc_shape_1side_copy_and_release +#include <rsys/hash_table.h> -struct mc_per_receiver_data { - struct mc_per_receiver_1side_data front; - struct mc_per_receiver_1side_data back; +struct mc_receiver_1side { + MC_RECEIVER_DATA + struct htable_shape2mc shape2mc; }; -#define MC_RECV_DATA_NULL__ { MC_RECV_1SIDE_DATA_NULL__, MC_RECV_1SIDE_DATA_NULL__ } +static INLINE void +mc_receiver_1side_init + (struct mem_allocator* allocator, struct mc_receiver_1side* mc) +{ + ASSERT(mc); + mc->integrated_irradiance = MC_DATA_NULL; + mc->absorptivity_loss = MC_DATA_NULL; + mc->reflectivity_loss = MC_DATA_NULL; + mc->cos_loss = MC_DATA_NULL; + htable_shape2mc_init(allocator, &mc->shape2mc); +} + +static INLINE void +mc_receiver_1side_release(struct mc_receiver_1side* mc) +{ + ASSERT(mc); + htable_shape2mc_release(&mc->shape2mc); +} + +static INLINE res_T +mc_receiver_1side_copy + (struct mc_receiver_1side* dst, const struct mc_receiver_1side* src) +{ + ASSERT(dst && src); + dst->integrated_irradiance = src->integrated_irradiance; + dst->absorptivity_loss = src->absorptivity_loss; + dst->reflectivity_loss = src->reflectivity_loss; + dst->cos_loss = src->cos_loss; + return htable_shape2mc_copy(&dst->shape2mc, &src->shape2mc); +} + +static INLINE res_T +mc_receiver_1side_copy_and_release + (struct mc_receiver_1side* dst, struct mc_receiver_1side* src) +{ + ASSERT(dst && src); + dst->integrated_irradiance = src->integrated_irradiance; + dst->absorptivity_loss = src->absorptivity_loss; + dst->reflectivity_loss = src->reflectivity_loss; + dst->cos_loss = src->cos_loss; + return htable_shape2mc_copy_and_release(&dst->shape2mc, &src->shape2mc); +} + +static INLINE res_T +mc_receiver_1side_get_mc_shape + (struct mc_receiver_1side* mc_rcv, + const struct ssol_shape* shape, + struct mc_shape_1side** out_mc_shape1) +{ + struct mc_shape_1side* mc_shape1 = NULL; + struct mc_shape_1side mc_shape1_null; + res_T res = RES_OK; + ASSERT(mc_rcv && shape && out_mc_shape1); + + mc_shape_1side_init(shape->dev->allocator, &mc_shape1_null); -static const struct mc_per_receiver_data -MC_RECV_DATA_NULL = MC_RECV_DATA_NULL__; + mc_shape1 = htable_shape2mc_find(&mc_rcv->shape2mc, &shape); + if(!mc_shape1) { + res = htable_shape2mc_set(&mc_rcv->shape2mc, &shape, &mc_shape1_null); + if(res != RES_OK) goto error; + + mc_shape1 = htable_shape2mc_find(&mc_rcv->shape2mc, &shape); + } + +exit: + mc_shape_1side_release(&mc_shape1_null); + *out_mc_shape1 = mc_shape1; + return res; +error: + goto exit; +} + +/******************************************************************************* + * Double sided per receiver MC data + ******************************************************************************/ +struct mc_receiver { + struct mc_receiver_1side front; + struct mc_receiver_1side back; +}; + +static INLINE void +mc_receiver_init(struct mem_allocator* allocator, struct mc_receiver* mc) +{ + ASSERT(mc); + mc_receiver_1side_init(allocator, &mc->front); + mc_receiver_1side_init(allocator, &mc->back); +} static INLINE void -init_mc_per_recv_data - (struct mem_allocator* alloc, - struct mc_per_receiver_data* data) +mc_receiver_release(struct mc_receiver* mc) +{ + ASSERT(mc); + mc_receiver_1side_release(&mc->front); + mc_receiver_1side_release(&mc->back); +} + +static INLINE res_T +mc_receiver_copy(struct mc_receiver* dst, const struct mc_receiver* src) { - (void)alloc; - ASSERT(data); - *data = MC_RECV_DATA_NULL; + res_T res = RES_OK; + ASSERT(dst && src); + res = mc_receiver_1side_copy(&dst->front, &src->front); + if(res != RES_OK) return res; + res = mc_receiver_1side_copy(&dst->back, &src->back); + if(res != RES_OK) return res; + return RES_OK; +} + +static INLINE res_T +mc_receiver_copy_and_release + (struct mc_receiver* dst, struct mc_receiver* src) +{ + res_T res = RES_OK; + ASSERT(dst && src); + res = mc_receiver_1side_copy_and_release(&dst->front, &src->front); + if(res != RES_OK) return res; + res = mc_receiver_1side_copy_and_release(&dst->back, &src->back); + if(res != RES_OK) return res; + return RES_OK; } /* Define the htable_receiver data structure */ -struct ssol_instance; #define HTABLE_NAME receiver #define HTABLE_KEY const struct ssol_instance* -#define HTABLE_DATA struct mc_per_receiver_data -#define HTABLE_FUNCTOR_INIT init_mc_per_recv_data +#define HTABLE_DATA struct mc_receiver +#define HTABLE_DATA_FUNCTOR_INIT mc_receiver_init +#define HTABLE_DATA_FUNCTOR_RELEASE mc_receiver_release +#define HTABLE_DATA_FUNCTOR_COPY mc_receiver_copy +#define HTABLE_DATA_FUNCTOR_COPY_AND_RELEASE mc_receiver_copy_and_release #include <rsys/hash_table.h> +/******************************************************************************* + * Per sampled instance MC data + ******************************************************************************/ +struct mc_sampled { + /* Global data for this entity */ + struct mc_data cos_loss; + struct mc_data shadowed; + double area; + double sun_cos; + size_t nb_samples; + + /* By-receptor data for this entity */ + struct htable_receiver mc_rcvs; +}; + +static INLINE void +mc_sampled_init + (struct mem_allocator* allocator, + struct mc_sampled* samp) +{ + ASSERT(samp); + samp->cos_loss = MC_DATA_NULL; + samp->shadowed = MC_DATA_NULL; + samp->area = 0; + samp->sun_cos = 0; + samp->nb_samples = 0; + htable_receiver_init(allocator, &samp->mc_rcvs); +} + +static INLINE void +mc_sampled_release(struct mc_sampled* samp) +{ + ASSERT(samp); + htable_receiver_release(&samp->mc_rcvs); +} + +static INLINE res_T +mc_sampled_copy(struct mc_sampled* dst, const struct mc_sampled* src) +{ + ASSERT(dst && src); + dst->cos_loss = src->cos_loss; + dst->shadowed = src->shadowed; + dst->area = src->area; + dst->sun_cos = src->sun_cos; + dst->nb_samples = src->nb_samples; + return htable_receiver_copy(&dst->mc_rcvs, &src->mc_rcvs); +} + +static INLINE res_T +mc_sampled_copy_and_release(struct mc_sampled* dst, struct mc_sampled* src) +{ + ASSERT(dst && src); + dst->cos_loss = src->cos_loss; + dst->shadowed = src->shadowed; + dst->area = src->area; + dst->sun_cos = src->sun_cos; + dst->nb_samples = src->nb_samples; + return htable_receiver_copy_and_release(&dst->mc_rcvs, &src->mc_rcvs); +} + +static INLINE res_T +mc_sampled_get_mc_receiver_1side + (struct mc_sampled* mc_samp, + const struct ssol_instance* inst, + const enum ssol_side_flag side, + struct mc_receiver_1side** out_mc_rcv1) +{ + struct mc_receiver* mc_rcv = NULL; + struct mc_receiver_1side* mc_rcv1 = NULL; + struct mc_receiver mc_rcv_null; + res_T res = RES_OK; + ASSERT(mc_samp && inst); + ASSERT(inst->receiver_mask & (int)side); + + mc_receiver_init(inst->dev->allocator, &mc_rcv_null); + + mc_rcv = htable_receiver_find(&mc_samp->mc_rcvs, &inst); + if(!mc_rcv) { + res = htable_receiver_set(&mc_samp->mc_rcvs, &inst, &mc_rcv_null); + if(res != RES_OK) goto error; + mc_rcv = htable_receiver_find(&mc_samp->mc_rcvs, &inst); + } + mc_rcv1 = side == SSOL_FRONT ? &mc_rcv->front : &mc_rcv->back; + +exit: + mc_receiver_release(&mc_rcv_null); + *out_mc_rcv1 = mc_rcv1; + return res; +error: + mc_rcv1 = NULL; + goto exit; +} + +/* Define the htable_primary data structure */ +#define HTABLE_NAME sampled +#define HTABLE_KEY const struct ssol_instance* +#define HTABLE_DATA struct mc_sampled +#define HTABLE_DATA_FUNCTOR_INIT mc_sampled_init +#define HTABLE_DATA_FUNCTOR_RELEASE mc_sampled_release +#define HTABLE_DATA_FUNCTOR_COPY mc_sampled_copy +#define HTABLE_DATA_FUNCTOR_COPY_AND_RELEASE mc_sampled_copy_and_release +#include <rsys/hash_table.h> + +/******************************************************************************* + * Radiative path + ******************************************************************************/ +#define DARRAY_NAME path_vertex +#define DARRAY_DATA struct ssol_path_vertex +#include <rsys/dynamic_array.h> + +struct path { + enum ssol_path_type type; + struct darray_path_vertex vertices; +}; + +static INLINE void +path_init(struct mem_allocator* allocator, struct path* path) +{ + ASSERT(path); + path->type = SSOL_PATH_MISSING; + darray_path_vertex_init(allocator, &path->vertices); +} + +static INLINE void +path_release(struct path* path) +{ + ASSERT(path); + darray_path_vertex_release(&path->vertices); +} + +static INLINE res_T +path_copy(struct path* dst, const struct path* src) +{ + ASSERT(dst && src); + dst->type = src->type; + return darray_path_vertex_copy(&dst->vertices, &src->vertices); +} + +static INLINE res_T +path_copy_and_release(struct path* dst, struct path* src) +{ + ASSERT(dst && src); + dst->type = src->type; + return darray_path_vertex_copy_and_release(&dst->vertices, &src->vertices); +} + +static INLINE res_T +path_copy_and_clear(struct path* dst, struct path* src) +{ + ASSERT(dst && src); + dst->type = src->type; + return darray_path_vertex_copy_and_clear(&dst->vertices, &src->vertices); +} + +static INLINE res_T +path_add_vertex(struct path* path, const double pos[3], const double weight) +{ + struct ssol_path_vertex vertex; + ASSERT(path && pos && weight >= 0); + vertex.pos[0] = pos[0]; + vertex.pos[1] = pos[1]; + vertex.pos[2] = pos[2]; + vertex.weight = weight; + return darray_path_vertex_push_back(&path->vertices, &vertex); +} + +#define DARRAY_NAME path +#define DARRAY_DATA struct path +#define DARRAY_FUNCTOR_INIT path_init +#define DARRAY_FUNCTOR_RELEASE path_release +#define DARRAY_FUNCTOR_COPY path_copy +#define DARRAY_FUNCTOR_COPY_AND_RELEASE path_copy_and_release +#include <rsys/dynamic_array.h> + +/******************************************************************************* + * Estimator data structure + ******************************************************************************/ struct ssol_estimator { size_t realisation_count; size_t failed_count; - /* the implicit MC computations */ - struct mc_data shadow; + + /* Implicit MC computations */ + struct mc_data shadowed; struct mc_data missing; - /* 1 global MC per receiver */ - struct htable_receiver global_receivers; - /* areas */ - double sampled_area, primary_area; + struct mc_data cos_loss; /* TODO compute it */ + + struct htable_receiver mc_receivers; /* Per receiver MC */ + struct htable_sampled mc_sampled; /* Per sampled instance MC */ + + struct darray_path paths; /* Tracked paths */ + + /* Overall area of the sampled instances. Actually this is not the area that + * is effectively sampled since an instance may be sampled through a proxy + * geometry */ + double sampled_area; struct ssol_device* dev; ref_T ref; @@ -99,18 +479,65 @@ estimator_create struct ssol_scene* scene, struct ssol_estimator** estimator); -static FINLINE struct mc_per_receiver_1side_data* -estimator_get_receiver_data +static FINLINE res_T +get_mc_receiver_1side (struct htable_receiver* receivers, - const struct ssol_instance* instance, - const enum ssol_side_flag side) + const struct ssol_instance* inst, + const enum ssol_side_flag side, + struct mc_receiver_1side** out_mc_rcv1) +{ + struct mc_receiver* mc_rcv = NULL; + struct mc_receiver_1side* mc_rcv1 = NULL; + struct mc_receiver mc_rcv_null; + res_T res = RES_OK; + ASSERT(receivers && inst && out_mc_rcv1); + ASSERT(inst->receiver_mask & (int)side); + + mc_receiver_init(inst->dev->allocator, &mc_rcv_null); + + mc_rcv = htable_receiver_find(receivers, &inst); + if(!mc_rcv) { + res = htable_receiver_set(receivers, &inst, &mc_rcv_null); + if(res != RES_OK) goto error; + mc_rcv = htable_receiver_find(receivers, &inst); + } + + mc_rcv1 = side == SSOL_FRONT ? &mc_rcv->front : &mc_rcv->back; +exit: + mc_receiver_release(&mc_rcv_null); + *out_mc_rcv1 = mc_rcv1; + return res; +error: + goto exit; +} + +static FINLINE res_T +get_mc_sampled + (struct htable_sampled* sampled, + const struct ssol_instance* inst, + struct mc_sampled** out_mc_samp) { - struct mc_per_receiver_data* data; - ASSERT(receivers && instance); - if(!(instance->receiver_mask & (int)side)) return NULL; - data = htable_receiver_find(receivers, &instance); - if(!data) return NULL; - return side == SSOL_FRONT ? &data->front : &data->back; + struct mc_sampled* mc_samp = NULL; + struct mc_sampled mc_samp_null; + res_T res = RES_OK; + ASSERT(sampled && inst && out_mc_samp); + + mc_sampled_init(inst->dev->allocator, &mc_samp_null); + + mc_samp = htable_sampled_find(sampled, &inst); + if(!mc_samp) { + res = htable_sampled_set(sampled, &inst, &mc_samp_null); + if(res != RES_OK) goto error; + mc_samp = htable_sampled_find(sampled, &inst); + } + +exit: + mc_sampled_release(&mc_samp_null); + *out_mc_samp = mc_samp; + return res; +error: + goto exit; } #endif /* SSOL_ESTIMATOR_C_H */ + diff --git a/src/ssol_instance.c b/src/ssol_instance.c @@ -14,6 +14,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "ssol.h" +#include "ssol_c.h" #include "ssol_object_c.h" #include "ssol_shape_c.h" #include "ssol_instance_c.h" @@ -84,10 +85,12 @@ ssol_object_instantiate /* Create the Star-3D instance to ray-trace */ res = s3d_scene_instantiate(object->scn_rt, &instance->shape_rt); if(res != RES_OK) goto error; + instance->shape_rt_area = object->scn_rt_area; /* Create the Star-3D instance to sample */ res = s3d_scene_instantiate(object->scn_samp, &instance->shape_samp); if(res != RES_OK) goto error; + instance->shape_samp_area = object->scn_samp_area; exit: if(out_instance) *out_instance = instance; @@ -152,10 +155,14 @@ error: } res_T -ssol_instance_set_receiver(struct ssol_instance* instance, const int mask) +ssol_instance_set_receiver + (struct ssol_instance* instance, + const int mask, + const int per_primitive) { if(!instance) return RES_BAD_ARG; instance->receiver_mask = mask; + instance->receiver_per_primitive = per_primitive; return RES_OK; } @@ -181,3 +188,76 @@ ssol_instance_get_id(const struct ssol_instance* instance, uint32_t* id) return RES_OK; } +res_T +ssol_instance_get_area + (const struct ssol_instance* instance, + double* area) +{ + if (!instance || !area) return RES_BAD_ARG;; + /* the area of the 3D surface */ + *area = instance->shape_rt_area; + return RES_OK; +} + +res_T +ssol_instance_get_shaded_shapes_count + (const struct ssol_instance* instance, size_t* count) +{ + if(!instance || !count) return RES_BAD_ARG; + *count = darray_shaded_shape_size_get(&instance->object->shaded_shapes); + return RES_OK; +} + +res_T +ssol_instance_get_shaded_shape + (const struct ssol_instance* instance, + const size_t ishape, + struct ssol_instantiated_shaded_shape* sshape) +{ + const struct shaded_shape* shaded_shape; + + if(!instance || !sshape) return RES_BAD_ARG; + if(ishape >= darray_shaded_shape_size_get(&instance->object->shaded_shapes)) + return RES_BAD_ARG; + + shaded_shape = darray_shaded_shape_cdata_get + (&instance->object->shaded_shapes) + ishape; + sshape->shape = shaded_shape->shape; + sshape->mtl_front = shaded_shape->mtl_front; + sshape->mtl_back = shaded_shape->mtl_back; + + d33_set(sshape->R__, instance->transform); + d3_set(sshape->T__, instance->transform+9); + d33_invtrans(sshape->R_invtrans__, sshape->R__); + return RES_OK; +} + +res_T +ssol_instantiated_shaded_shape_get_vertex_attrib + (const struct ssol_instantiated_shaded_shape* sshape, + const unsigned ivert, + const enum ssol_attrib_usage usage, + double value[]) +{ + res_T res = RES_OK; + + if(!sshape || (unsigned)usage >= SSOL_ATTRIBS_COUNT__ || !value) + return RES_BAD_ARG; + + res = shape_fetched_raw_vertex_attrib(sshape->shape, ivert, usage, value); + if(res != RES_OK) return res; + + /* Transform the fetched attrib */ + switch(usage) { + case SSOL_NORMAL: + d33_muld3(value, sshape->R_invtrans__, value); + break; + case SSOL_POSITION: + d33_muld3(value, sshape->R__, value); + d3_add(value, sshape->T__, value); + break; + default: /* Do nothing */ break; + } + return RES_OK; +} + diff --git a/src/ssol_instance_c.h b/src/ssol_instance_c.h @@ -24,8 +24,10 @@ struct ssol_instance { struct ssol_object* object; /* Instantiated object */ struct s3d_shape* shape_rt; /* Instantiated Star-3D shape to ray-trace */ struct s3d_shape* shape_samp; /* Instantiated Star-3D shape to sample */ + double shape_rt_area, shape_samp_area; double transform[12]; /* Column major 4x3 affine transformation */ - int receiver_mask; /* Combination of ssol_face_flag */ + int receiver_mask; /* Combination of ssol_side_flag */ + int receiver_per_primitive; /* Enable the per primitive receiver */ int sample; /* Define whether or not the instance should be sampled */ struct fid id; /* Unique identifier */ diff --git a/src/ssol_material.c b/src/ssol_material.c @@ -34,6 +34,63 @@ * Helper functions ******************************************************************************/ static res_T +dielectric_shade + (const struct ssol_material* mtl, + const struct surface_fragment* fragment, + const double wavelength, /* In nanometer */ + const struct ssol_medium* medium, + struct ssf_bsdf* bsdf) +{ + struct ssf_bxdf* brdf = NULL; + struct ssf_bxdf* btdf = NULL; + struct ssf_fresnel* fresnel = NULL; + const struct ssol_dielectric_shader* shader; + double eta_i, eta_t; + double N[3]; + res_T res = RES_OK; + ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_DIELECTRIC); + ASSERT(medium && bsdf); + + shader = &mtl->data.dielectric; + + /* Fetch material attribs */ + shader->normal(mtl->dev, mtl->buf, wavelength, fragment->pos, fragment->Ng, + fragment->Ns, fragment->uv, fragment->dir, N); + + if(!MEDIA_EQ(medium, &mtl->out_medium)) { + log_error(mtl->dev, "Inconsistent medium description.\n"); + res = RES_BAD_OP; + goto error; + } + + eta_i = mtl->out_medium.refractive_index; + eta_t = mtl->in_medium.refractive_index; + + #define CALL(Func) { res = Func; if(res != RES_OK) goto error; } (void)0 + /* Setup the reflective part */ + CALL(ssf_fresnel_create + (mtl->dev->allocator, &ssf_fresnel_dielectric_dielectric, &fresnel)); + CALL(ssf_fresnel_dielectric_dielectric_setup(fresnel, eta_i, eta_t)); + CALL(ssf_bxdf_create(mtl->dev->allocator, &ssf_specular_reflection, &brdf)); + CALL(ssf_specular_reflection_setup(brdf, fresnel)); + /* Setup the transmissive part */ + CALL(ssf_bxdf_create(mtl->dev->allocator, &ssf_specular_transmission, &btdf)); + CALL(ssf_specular_transmission_setup(btdf, eta_i, eta_t)); + /* Setup the scattering function */ + CALL(ssf_bsdf_add(bsdf, brdf, 0.5)); + CALL(ssf_bsdf_add(bsdf, btdf, 0.5)); + #undef CALL + +exit: + if(brdf) SSF(bxdf_ref_put(brdf)); + if(btdf) SSF(bxdf_ref_put(btdf)); + if(fresnel) SSF(fresnel_ref_put(fresnel)); + return res; +error: + goto exit; +} + +static res_T matte_shade (const struct ssol_material* mtl, const struct surface_fragment* fragment, @@ -45,7 +102,7 @@ matte_shade double normal[3]; double reflectivity; res_T res; - ASSERT(mtl && fragment && mtl->type == MATERIAL_MATTE); + ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_MATTE); ASSERT(bsdf); shader = &mtl->data.matte; @@ -78,6 +135,7 @@ mirror_shade (const struct ssol_material* mtl, const struct surface_fragment* fragment, const double wavelength, /* In nanometer */ + const int rendering, struct ssf_bsdf* bsdf) { struct ssf_bxdf* brdf = NULL; @@ -88,7 +146,7 @@ mirror_shade double roughness; double reflectivity; res_T res; - ASSERT(mtl && fragment && mtl->type == MATERIAL_MIRROR); + ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_MIRROR); ASSERT(bsdf); shader = &mtl->data.mirror; @@ -120,8 +178,16 @@ mirror_shade res = ssf_beckmann_distribution_setup(distrib, roughness); if(res != RES_OK) goto error; - res = ssf_bxdf_create - (mtl->dev->allocator, &ssf_microfacet2_reflection, &brdf); + /* Microfacet2 is not well suited for rendering since it cannot be + * evaluated and consequently it returns an invalid result for direct + * lighting. */ + if(rendering) { + res = ssf_bxdf_create + (mtl->dev->allocator, &ssf_microfacet_reflection, &brdf); + } else { + res = ssf_bxdf_create + (mtl->dev->allocator, &ssf_microfacet2_reflection, &brdf); + } if(res != RES_OK) goto error; res = ssf_microfacet_reflection_setup(brdf, fresnel, distrib); if(res != RES_OK) goto error; @@ -140,17 +206,90 @@ error: goto exit; } -static void -material_release(ref_T* ref) +static res_T +thin_dielectric_shade + (const struct ssol_material* mtl, + const struct surface_fragment* fragment, + const double wavelength, /* In nanometer */ + struct ssf_bsdf* bsdf) { - struct ssol_device* dev; - struct ssol_material* material = CONTAINER_OF(ref, struct ssol_material, ref); - ASSERT(ref); - dev = material->dev; - if(material->buf) SSOL(param_buffer_ref_put(material->buf)); - ASSERT(dev && dev->allocator); - MEM_RM(dev->allocator, material); - SSOL(device_ref_put(dev)); + struct ssf_bxdf* bxdf = NULL; + const struct ssol_thin_dielectric_shader* shader; + double N[3]; + double thickness; + double absorptivity; + double eta_i; + double eta_t; + res_T res = RES_OK; + ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_THIN_DIELECTRIC); + ASSERT(bsdf); + + shader = &mtl->data.thin_dielectric.shader; + + /* Fetch material attribs */ + shader->normal(mtl->dev, mtl->buf, wavelength, fragment->pos, + fragment->Ng, fragment->Ns, fragment->uv, fragment->dir, N); + eta_i = mtl->out_medium.refractive_index; + eta_t = mtl->data.thin_dielectric.slab_medium.refractive_index; + absorptivity = mtl->data.thin_dielectric.slab_medium.absorptivity; + thickness = mtl->data.thin_dielectric.thickness; + + /* Setup the BxDF */ + res = ssf_bxdf_create + (mtl->dev->allocator, &ssf_thin_specular_dielectric, &bxdf); + if(res != RES_OK) goto error; + res = ssf_thin_specular_dielectric_setup + (bxdf, absorptivity, eta_i, eta_t, thickness); + if(res != RES_OK) goto error; + + /* Setup the BSDF */ + res = ssf_bsdf_add(bsdf, bxdf, 1.0); + if(res != RES_OK) goto error; + +exit: + if(bxdf) SSF(bxdf_ref_put(bxdf)); + return res; +error: + goto exit; +} + +static INLINE res_T +shade + (const struct ssol_material* mtl, + const struct surface_fragment* fragment, + const double wavelength, /* In nanometer */ + const int rendering, /* Is material used for rendering */ + const struct ssol_medium* medium, + struct ssf_bsdf* bsdf) +{ + res_T res = RES_OK; + ASSERT(mtl); + + /* Specific material shading */ + switch(mtl->type) { + case SSOL_MATERIAL_DIELECTRIC: + res = dielectric_shade + (mtl, fragment, wavelength, medium, bsdf); + break; + case SSOL_MATERIAL_MATTE: + res = matte_shade(mtl, fragment, wavelength, bsdf); + break; + case SSOL_MATERIAL_MIRROR: + res = mirror_shade(mtl, fragment, wavelength, rendering, bsdf); + break; + case SSOL_MATERIAL_THIN_DIELECTRIC: + res = thin_dielectric_shade(mtl, fragment, wavelength, bsdf); + break; + case SSOL_MATERIAL_VIRTUAL: /* Nothing to shade */ break; + default: FATAL("Unreachable code\n"); break; + } + return res; +} + +static INLINE int +check_shader_dielectric(const struct ssol_dielectric_shader* shader) +{ + return shader && shader->normal; } static INLINE int @@ -170,6 +309,33 @@ check_shader_matte(const struct ssol_matte_shader* shader) && shader->reflectivity; } +static INLINE int +check_shader_thin_differential(const struct ssol_thin_dielectric_shader* shader) +{ + return shader && shader->normal; +} + +static INLINE int +check_medium(const struct ssol_medium* medium) +{ + return medium + && medium->refractive_index > 0 + && medium->absorptivity >= 0; +} + +static void +material_release(ref_T* ref) +{ + struct ssol_device* dev; + struct ssol_material* material = CONTAINER_OF(ref, struct ssol_material, ref); + ASSERT(ref); + dev = material->dev; + if(material->buf) SSOL(param_buffer_ref_put(material->buf)); + ASSERT(dev && dev->allocator); + MEM_RM(dev->allocator, material); + SSOL(device_ref_put(dev)); +} + /******************************************************************************* * Local functions ******************************************************************************/ @@ -177,13 +343,13 @@ static res_T ssol_material_create (struct ssol_device* dev, struct ssol_material** out_material, - enum material_type type) + enum ssol_material_type type) { struct ssol_material* material = NULL; res_T res = RES_OK; if(!dev || !out_material - || type >= MATERIAL_TYPES_COUNT__) { + || type >= SSOL_MATERIAL_TYPES_COUNT__) { return RES_BAD_ARG; } @@ -198,6 +364,8 @@ ssol_material_create material->dev = dev; ref_init(&material->ref); material->type = type; + material->in_medium = SSOL_MEDIUM_VACUUM; + material->out_medium = SSOL_MEDIUM_VACUUM; exit: if (out_material) *out_material = material; @@ -218,7 +386,7 @@ ssol_material_ref_get(struct ssol_material* material) { if (!material) return RES_BAD_ARG; - ASSERT(material->type < MATERIAL_TYPES_COUNT__); + ASSERT(material->type < SSOL_MATERIAL_TYPES_COUNT__); ref_get(&material->ref); return RES_OK; } @@ -228,12 +396,21 @@ ssol_material_ref_put(struct ssol_material* material) { if (!material) return RES_BAD_ARG; - ASSERT(material->type < MATERIAL_TYPES_COUNT__); + ASSERT(material->type < SSOL_MATERIAL_TYPES_COUNT__); ref_put(&material->ref, material_release); return RES_OK; } res_T +ssol_material_get_type + (const struct ssol_material* mtl, enum ssol_material_type* type) +{ + if(!mtl || !type) return RES_BAD_ARG; + *type = mtl->type; + return RES_OK; +} + +res_T ssol_material_set_param_buffer (struct ssol_material* mtl, struct ssol_param_buffer* buf) { @@ -244,25 +421,58 @@ ssol_material_set_param_buffer } res_T +ssol_material_create_dielectric + (struct ssol_device* dev, struct ssol_material** out_material) +{ + return ssol_material_create(dev, out_material, SSOL_MATERIAL_DIELECTRIC); +} + +res_T ssol_material_create_mirror (struct ssol_device* dev, struct ssol_material** out_material) { - return ssol_material_create(dev, out_material, MATERIAL_MIRROR); + return ssol_material_create(dev, out_material, SSOL_MATERIAL_MIRROR); } res_T ssol_material_create_matte (struct ssol_device* dev, struct ssol_material** out_material) { - return ssol_material_create(dev, out_material, MATERIAL_MATTE); + return ssol_material_create(dev, out_material, SSOL_MATERIAL_MATTE); } res_T -ssol_mirror_set_shader +ssol_material_create_thin_dielectric + (struct ssol_device* dev, struct ssol_material** out_material) +{ + return ssol_material_create(dev, out_material, SSOL_MATERIAL_THIN_DIELECTRIC); +} + +res_T +ssol_dielectric_setup + (struct ssol_material* material, + const struct ssol_dielectric_shader* shader, + const struct ssol_medium* outside_medium, + const struct ssol_medium* inside_medium) +{ + if(!material + || material->type != SSOL_MATERIAL_DIELECTRIC + || !check_shader_dielectric(shader) + || !check_medium(outside_medium) + || !check_medium(inside_medium)) + return RES_BAD_ARG; + material->data.dielectric = *shader; + material->out_medium = *outside_medium; + material->in_medium = *inside_medium; + return RES_OK; +} + +res_T +ssol_mirror_setup (struct ssol_material* material, const struct ssol_mirror_shader* shader) { if(!material - || material->type != MATERIAL_MIRROR + || material->type != SSOL_MATERIAL_MIRROR || !check_shader_mirror(shader)) return RES_BAD_ARG; material->data.mirror = *shader; @@ -270,11 +480,11 @@ ssol_mirror_set_shader } res_T -ssol_matte_set_shader +ssol_matte_setup (struct ssol_material* material, const struct ssol_matte_shader* shader) { if(!material - || material->type != MATERIAL_MATTE + || material->type != SSOL_MATERIAL_MATTE || !check_shader_matte(shader)) return RES_BAD_ARG; material->data.matte = *shader; @@ -282,10 +492,33 @@ ssol_matte_set_shader } res_T +ssol_thin_dielectric_setup + (struct ssol_material* material, + const struct ssol_thin_dielectric_shader* shader, + const struct ssol_medium* outside_medium, + const struct ssol_medium* slab_medium, + const double thickness) +{ + if(!material + || material->type != SSOL_MATERIAL_THIN_DIELECTRIC + || !check_shader_thin_differential(shader) + || !check_medium(outside_medium) + || !check_medium(slab_medium) + || thickness < 0) + return RES_BAD_ARG; + material->data.thin_dielectric.shader = *shader; + material->data.thin_dielectric.slab_medium = *slab_medium; + material->data.thin_dielectric.thickness = thickness; + material->out_medium = *outside_medium; + material->in_medium = *outside_medium; + return RES_OK; +} + +res_T ssol_material_create_virtual (struct ssol_device* dev, struct ssol_material** out_material) { - return ssol_material_create(dev, out_material, MATERIAL_VIRTUAL); + return ssol_material_create(dev, out_material, SSOL_MATERIAL_VIRTUAL); } /******************************************************************************* @@ -365,22 +598,47 @@ material_shade (const struct ssol_material* mtl, const struct surface_fragment* fragment, const double wavelength, /* In nanometer */ + const struct ssol_medium* medium, struct ssf_bsdf* bsdf) { - res_T res = RES_OK; - ASSERT(mtl); + return shade(mtl, fragment, wavelength, 0, medium, bsdf); +} - /* Specific material shading */ +res_T +material_shade_rendering + (const struct ssol_material* mtl, + const struct surface_fragment* fragment, + const double wavelength, /* In nanometer */ + const struct ssol_medium* medium, + struct ssf_bsdf* bsdf) +{ + return shade(mtl, fragment, wavelength, 1, medium, bsdf); +} + +res_T +material_get_next_medium + (const struct ssol_material* mtl, + const struct ssol_medium* medium, + struct ssol_medium* next_medium) +{ + ASSERT(mtl && medium && next_medium); switch(mtl->type) { - case MATERIAL_MATTE: - res = matte_shade(mtl, fragment, wavelength, bsdf); + /* The material is an interface between 2 media */ + case SSOL_MATERIAL_DIELECTRIC: + if(MEDIA_EQ(&mtl->out_medium, medium)) { + *next_medium = mtl->in_medium; + } else { + *next_medium = mtl->out_medium; + } break; - case MATERIAL_MIRROR: - res = mirror_shade(mtl, fragment, wavelength, bsdf); + /* The material is not an interface between 2 media */ + case SSOL_MATERIAL_MATTE: + case SSOL_MATERIAL_MIRROR: + case SSOL_MATERIAL_THIN_DIELECTRIC: + *next_medium = *medium; break; - case MATERIAL_VIRTUAL: /* Nothing to shade */ break; default: FATAL("Unreachable code\n"); break; } - return res; + return RES_OK; } diff --git a/src/ssol_material_c.h b/src/ssol_material_c.h @@ -23,6 +23,10 @@ struct s3d_primitive; struct ssf_bsdf; struct ssol_device; +#define MEDIA_EQ(A, B) \ + ( ((A)->refractive_index == (B)->refractive_index) \ + && ((A)->absorptivity == (B)->absorptivity)) + struct surface_fragment { double dir[3]; /* World space incoming direction */ double pos[3]; /* World space position */ @@ -34,21 +38,25 @@ struct surface_fragment { static const struct surface_fragment SURFACE_FRAGMENT_NULL = SURFACE_FRAGMENT_NULL__; -enum material_type { - MATERIAL_MATTE, - MATERIAL_MIRROR, - MATERIAL_VIRTUAL, - MATERIAL_TYPES_COUNT__ +struct thin_dielectric { + struct ssol_thin_dielectric_shader shader; + struct ssol_medium slab_medium; + double thickness; }; struct ssol_material { - enum material_type type; + enum ssol_material_type type; union { + struct ssol_dielectric_shader dielectric; struct ssol_matte_shader matte; struct ssol_mirror_shader mirror; + struct thin_dielectric thin_dielectric; } data; + struct ssol_medium out_medium; + struct ssol_medium in_medium; + struct ssol_param_buffer* buf; struct ssol_device* dev; ref_T ref; @@ -68,7 +76,22 @@ material_shade (const struct ssol_material* mtl, const struct surface_fragment* fragment, const double wavelength, /* In nanometer */ + const struct ssol_medium* medium, /* Current medium */ struct ssf_bsdf* bsdf); /* Bidirectional Scattering Distribution Function */ -#endif /* SSOL_MATERIAL_C_H */ +/* Material shading for rendering purposes */ +extern LOCAL_SYM res_T +material_shade_rendering + (const struct ssol_material* mtl, + const struct surface_fragment* fragment, + const double wavelength, /* In nanometer */ + const struct ssol_medium* medium, + struct ssf_bsdf* bsdf); /* Bidirectional Scattering Distribution Function */ + +extern LOCAL_SYM res_T +material_get_next_medium + (const struct ssol_material* mtl, + const struct ssol_medium* medium, /* Current mediu */ + struct ssol_medium* next_medium); +#endif /* SSOL_MATERIAL_C_H */ diff --git a/src/ssol_mc_receiver.c b/src/ssol_mc_receiver.c @@ -0,0 +1,133 @@ +/* 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 "ssol_estimator_c.h" +#include "ssol_object_c.h" + +#ifdef COMPILER_CL + #pragma warning(push) + #pragma warning(disable:4706) /* Assignment within a condition */ +#endif + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +ssol_estimator_get_mc_receiver + (struct ssol_estimator* estimator, + const struct ssol_instance* instance, + const enum ssol_side_flag side, + struct ssol_mc_receiver* rcv) +{ + struct mc_receiver* mc_rcv = NULL; + struct mc_receiver_1side* mc_rcv1 = NULL; + + if(!estimator || !instance || !rcv + || !(instance->receiver_mask & (int)side)) + return RES_BAD_ARG; + + memset(rcv, 0, sizeof(rcv[0])); + + mc_rcv = htable_receiver_find(&estimator->mc_receivers, &instance); + if(!mc_rcv) { + /* The receiver has no MC estimation */ + return RES_OK; + } + + mc_rcv1 = side == SSOL_FRONT ? &mc_rcv->front : &mc_rcv->back; + #define SETUP_MC_RESULT(Name) { \ + const double N = (double)estimator->realisation_count; \ + const struct mc_data* data = &mc_rcv1->Name; \ + rcv->Name.E = data->weight / N; \ + rcv->Name.V = data->sqr_weight/N - rcv->Name.E*rcv->Name.E; \ + rcv->Name.SE = rcv->Name.V > 0 ? sqrt(rcv->Name.V / N) : 0; \ + } (void)0 + SETUP_MC_RESULT(integrated_irradiance); + SETUP_MC_RESULT(absorptivity_loss); + SETUP_MC_RESULT(reflectivity_loss); + SETUP_MC_RESULT(cos_loss); + #undef SETUP_MC_RESULT + rcv->mc__ = mc_rcv1; + rcv->N__ = estimator->realisation_count; + rcv->instance__ = instance; + return RES_OK; +} + +res_T +ssol_mc_receiver_get_mc_shape + (struct ssol_mc_receiver* rcv, + const struct ssol_shape* shape, + struct ssol_mc_shape* mc) +{ + struct mc_receiver_1side* mc_rcv1; + + if(!rcv || !shape || !mc) return RES_BAD_ARG; + if(!object_has_shape(rcv->instance__->object, shape)) return RES_BAD_ARG; + mc_rcv1 = rcv->mc__; + mc->N__ = rcv->N__; + mc->mc__ = htable_shape2mc_find(&mc_rcv1->shape2mc, &shape); + mc->shape__ = shape; + return RES_OK; +} + +res_T +ssol_mc_shape_get_mc_primitive + (struct ssol_mc_shape* shape, + const unsigned i, + struct ssol_mc_primitive* prim) +{ + struct mc_shape_1side* mc_shape1; + struct mc_primitive_1side* mc_prim1; + unsigned ntris; + + if(!shape || !prim) return RES_BAD_ARG; + + SSOL(shape_get_triangles_count(shape->shape__, &ntris)); + if(i >= ntris) return RES_BAD_ARG; + + mc_shape1 = shape->mc__; + if(!mc_shape1 || !(mc_prim1 = htable_prim2mc_find(&mc_shape1->prim2mc, &i))) { + #define SETUP_MC_RESULT(Name) { \ + prim->Name.E = 0; \ + prim->Name.V = 0; \ + prim->Name.SE = 0; \ + } (void)0 + SETUP_MC_RESULT(integrated_irradiance); + SETUP_MC_RESULT(absorptivity_loss); + SETUP_MC_RESULT(reflectivity_loss); + SETUP_MC_RESULT(cos_loss); + #undef SETUP_MC_RESULT + } else { + #define SETUP_MC_RESULT(Name) { \ + const double N = (double)shape->N__; \ + const struct mc_data* data = &mc_prim1->Name; \ + prim->Name.E = data->weight / N; \ + prim->Name.V = data->sqr_weight/N - prim->Name.E*prim->Name.E; \ + prim->Name.SE = prim->Name.V > 0 ? sqrt(prim->Name.V / N) : 0; \ + } (void)0 + SETUP_MC_RESULT(integrated_irradiance); + SETUP_MC_RESULT(absorptivity_loss); + SETUP_MC_RESULT(reflectivity_loss); + SETUP_MC_RESULT(cos_loss); + #undef SETUP_MC_RESULT + } + return RES_OK; +} + +#ifdef COMPILER_CL + #pragma warning(pop) +#endif + diff --git a/src/ssol_object.c b/src/ssol_object.c @@ -18,6 +18,8 @@ #include "ssol_object_c.h" #include "ssol_shape_c.h" +#include <star/s3d.h> + #include <rsys/ref_count.h> #include <rsys/rsys.h> #include <rsys/mem_allocator.h> @@ -111,7 +113,7 @@ ssol_object_add_shaded_shape struct ssol_material* front, struct ssol_material* back) { - enum { + enum { ATTACH_S3D_RT, ATTACH_S3D_SAMP, REGISTER_RT, REGISTER_SAMP, REGISTER_SHAPE }; struct shaded_shape* shaded_shape; @@ -137,11 +139,13 @@ ssol_object_add_shaded_shape res = s3d_scene_attach_shape(object->scn_rt, shape->shape_rt); if(res != RES_OK) goto error; mask |= BIT(ATTACH_S3D_RT); + object->scn_rt_area += shape->shape_rt_area; /* Add the shape samp to the sampling scene of the object */ res = s3d_scene_attach_shape(object->scn_samp, shape->shape_samp); if(res != RES_OK) goto error; mask |= BIT(ATTACH_S3D_SAMP); + object->scn_samp_area += shape->shape_samp_area; /* Ask for a shaded shape identifier */ i = darray_shaded_shape_size_get(&object->shaded_shapes); @@ -208,9 +212,32 @@ ssol_object_clear(struct ssol_object* obj) htable_shaded_shape_clear(&obj->shaded_shapes_rt); htable_shaded_shape_clear(&obj->shaded_shapes_samp); + obj->scn_rt_area = 0; + S3D(scene_clear(obj->scn_rt)); S3D(scene_clear(obj->scn_samp)); return RES_OK; } +res_T +ssol_object_get_area(const struct ssol_object* object, double* area) +{ + if(!object || !area) return RES_BAD_ARG;; + /* the area of the 3D surface */ + *area = object->scn_rt_area; + return RES_OK; +} + +/******************************************************************************* + * Local function + ******************************************************************************/ +int +object_has_shape(struct ssol_object* obj, const struct ssol_shape* shape) +{ + unsigned id; + ASSERT(obj && shape); + S3D(shape_get_id(shape->shape_rt, &id)); + return htable_shaded_shape_find(&obj->shaded_shapes_rt, &id) != NULL; +} + diff --git a/src/ssol_object_c.h b/src/ssol_object_c.h @@ -47,9 +47,16 @@ struct ssol_object { struct s3d_scene* scn_rt; /* RT scene to instantiate */ struct s3d_scene* scn_samp; /* Sampling scene to instantiate */ + double scn_rt_area, scn_samp_area; struct ssol_device* dev; ref_T ref; }; +extern LOCAL_SYM int +object_has_shape + (struct ssol_object* obj, + const struct ssol_shape* shape); + #endif /* SSOL_OBJECT_C_H */ + diff --git a/src/ssol_scene.c b/src/ssol_scene.c @@ -45,7 +45,6 @@ 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->scn_prim) S3D(scene_ref_put(scene->scn_prim)); if(scene->sun) SSOL(sun_ref_put(scene->sun)); if(scene->atmosphere) SSOL(atmosphere_ref_put(scene->atmosphere)); htable_instance_release(&scene->instances_rt); @@ -84,8 +83,6 @@ ssol_scene_create if(res != RES_OK) goto error; res = s3d_scene_create(dev->s3d, &scene->scn_samp); if(res != RES_OK) goto error; - res = s3d_scene_create(dev->s3d, &scene->scn_prim); - if(res != RES_OK) goto error; exit: if(out_scene) *out_scene = scene; @@ -118,15 +115,18 @@ res_T ssol_scene_attach_instance (struct ssol_scene* scene, struct ssol_instance* instance) { + enum { ATTACH_S3D, SET_INSTANCE_RT }; unsigned id; struct ssol_instance** pinst; + int mask = 0; res_T res; if(!scene || !instance) return RES_BAD_ARG; /* Attach the instantiated s3d shape to ray-trace to the RT scene */ res = s3d_scene_attach_shape(scene->scn_rt, instance->shape_rt); - if(res != RES_OK) return res; + if(res != RES_OK) goto error; + mask |= BIT(ATTACH_S3D); /* Register the instance against the scene */ S3D(shape_get_id(instance->shape_rt, &id)); @@ -134,15 +134,26 @@ ssol_scene_attach_instance if(pinst) { /* already attached */ ASSERT(*pinst == instance); /* cannot be attached to another instance! */ - return RES_OK; + goto exit; } + res = htable_instance_set(&scene->instances_rt, &id, &instance); - if(res != RES_OK) { + if(res != RES_OK) goto error; + mask |= BIT(SET_INSTANCE_RT); + + SSOL(instance_ref_get(instance)); + +exit: + return res; +error: + if(mask & BIT(ATTACH_S3D)) { S3D(scene_detach_shape(scene->scn_rt, instance->shape_rt)); - return res; } - SSOL(instance_ref_get(instance)); - return RES_OK; + if(mask & BIT(SET_INSTANCE_RT)) { + const size_t n = htable_instance_erase(&scene->instances_rt, &id); + ASSERT(n == 1); (void)n; + } + goto exit; } res_T @@ -177,6 +188,30 @@ ssol_scene_detach_instance } res_T +ssol_scene_compute_aabb + (const struct ssol_scene* scene, float lower[3], float upper[3]) +{ + struct s3d_scene_view* view = NULL; + res_T res = RES_OK; + + if(!scene || !lower || !upper) { + res = RES_BAD_ARG; + goto error; + } + + res = s3d_scene_view_create(scene->scn_rt, S3D_GET_PRIMITIVE, &view); + if(res != RES_OK) goto error; + res = s3d_scene_view_get_aabb(view, lower, upper); + if(res != RES_OK) goto error; + +exit: + if(view) S3D(scene_view_ref_put(view)); + return res; +error: + goto exit; +} + +res_T ssol_scene_clear(struct ssol_scene* scene) { struct htable_instance_iterator it, it_end; @@ -195,10 +230,8 @@ ssol_scene_clear(struct ssol_scene* scene) htable_instance_clear(&scene->instances_samp); S3D(scene_clear(scene->scn_rt)); S3D(scene_clear(scene->scn_samp)); - S3D(scene_clear(scene->scn_prim)); - if (scene->sun) ssol_scene_detach_sun(scene, scene->sun); - if (scene->atmosphere) - ssol_scene_detach_atmosphere(scene, scene->atmosphere); + if(scene->sun) SSOL(scene_detach_sun(scene, scene->sun)); + if(scene->atmosphere) SSOL(scene_detach_atmosphere(scene, scene->atmosphere)); return RES_OK; } @@ -208,13 +241,11 @@ ssol_scene_attach_sun(struct ssol_scene* scene, struct ssol_sun* sun) if(!scene || ! sun) return RES_BAD_ARG; if(sun->scene_attachment || scene->sun) { - /* already attached: must be linked together */ + /* Already attached: must be linked together */ if(sun->scene_attachment != scene || scene->sun != sun) { - /* if not detach first! */ - return RES_BAD_ARG; + return RES_BAD_ARG; /* If not detach first! */ } else { - /* nothing to change */ - return RES_OK; + return RES_OK; /* Nothing to change */ } } /* no previous attachment */ @@ -273,6 +304,36 @@ ssol_scene_detach_atmosphere(struct ssol_scene* scene, struct ssol_atmosphere* a return RES_OK; } +res_T +ssol_scene_for_each_instance + (struct ssol_scene* scn, + res_T (*func)(struct ssol_instance* instance, void* ctx), + void* ctx) +{ + struct htable_instance_iterator it, end; + res_T res = RES_OK; + + if(!scn || !func) { + res = RES_BAD_ARG; + goto error; + } + + htable_instance_begin(&scn->instances_rt, &it); + htable_instance_end(&scn->instances_rt, &end); + while(!htable_instance_iterator_eq(&it, &end)) { + struct ssol_instance* inst = *htable_instance_iterator_data_get(&it); + htable_instance_iterator_next(&it); + + res = func(inst, ctx); + if(res != RES_OK) goto error; + } + +exit: + return res; +error: + goto exit; +} + /******************************************************************************* * Local functions ******************************************************************************/ @@ -281,25 +342,27 @@ scene_create_s3d_views (struct ssol_scene* scn, struct s3d_scene_view** out_view_rt, struct s3d_scene_view** out_view_samp, - struct s3d_scene_view** out_view_prim) + double* out_sampled_area, + double* out_sampled_area_proxy) { struct htable_instance_iterator it, end; struct s3d_scene_view* view_rt = NULL; struct s3d_scene_view* view_samp = NULL; - struct s3d_scene_view* view_prim = NULL; + double sampled_area = 0; + double sampled_area_proxy = 0; int has_sampled = 0; int has_receiver = 0; res_T res = RES_OK; ASSERT(scn && out_view_rt && out_view_samp); + ASSERT(out_sampled_area && out_sampled_area_proxy); S3D(scene_clear(scn->scn_samp)); - S3D(scene_clear(scn->scn_prim)); htable_instance_clear(&scn->instances_samp); htable_instance_begin(&scn->instances_rt, &it); htable_instance_end(&scn->instances_rt, &end); - while (!htable_instance_iterator_eq(&it, &end)) { + while(!htable_instance_iterator_eq(&it, &end)) { struct ssol_instance* inst = *htable_instance_iterator_data_get(&it); unsigned id; htable_instance_iterator_next(&it); @@ -310,6 +373,9 @@ scene_create_s3d_views if(!inst->sample) continue; + sampled_area += inst->shape_rt_area; + sampled_area_proxy += inst->shape_samp_area; + /* Note that geometries with virtual material can be sampled without risk * since the solver avoid to shade them and simply pursue the primary ray */ has_sampled = 1; @@ -318,10 +384,6 @@ scene_create_s3d_views res = s3d_scene_attach_shape(scn->scn_samp, inst->shape_samp); if(res != RES_OK) goto error; - /* Attach the instantiated s3d raytraced shape to the s3d primary scene */ - res = s3d_scene_attach_shape(scn->scn_prim, inst->shape_rt); - if(res != RES_OK) goto error; - /* Register the instantiated s3d sampling shape */ S3D(shape_get_id(inst->shape_samp, &id)); ASSERT(!htable_instance_find(&scn->instances_samp, &id)); @@ -346,13 +408,12 @@ scene_create_s3d_views if(res != RES_OK) goto error; res = s3d_scene_view_create(scn->scn_samp, S3D_SAMPLE, &view_samp); if(res != RES_OK) goto error; - res = s3d_scene_view_create(scn->scn_prim, S3D_SAMPLE, &view_prim); - if (res != RES_OK) goto error; exit: *out_view_rt = view_rt; *out_view_samp = view_samp; - *out_view_prim = view_prim; + *out_sampled_area = sampled_area; + *out_sampled_area_proxy = sampled_area_proxy; return res; error: S3D(scene_clear(scn->scn_samp)); @@ -365,10 +426,6 @@ error: S3D(scene_view_ref_put(view_samp)); view_samp = NULL; } - if (view_prim) { - S3D(scene_view_ref_put(view_prim)); - view_prim = NULL; - } goto exit; } @@ -378,7 +435,7 @@ error: int hit_filter_function (const struct s3d_hit* hit, - const float posf[3], + const float orgf[3], const float dirf[3], void* ray_data, void* filter_data) @@ -388,10 +445,10 @@ hit_filter_function struct ray_data* rdata = ray_data; const struct shaded_shape* sshape; enum ssol_side_flag hit_side = SSOL_INVALID_SIDE; - double pos[3], dir[3], N[3], dst = FLT_MAX; + double org[3], dir[3], N[3], dst = FLT_MAX; size_t id; (void)filter_data; - ASSERT(hit && posf && dirf); + ASSERT(hit && orgf && dirf); /* No ray data => nothing to filter */ if(!ray_data) return 0; @@ -420,23 +477,23 @@ hit_filter_function case SHAPE_PUNCHED: /* Project the hit position into the punched shape */ d3_set_f3(dir, dirf); - d3_set_f3(pos, posf); - dst = punched_shape_trace_ray(sshape->shape, inst->transform, pos, dir, - hit->distance, pos, N); + d3_set_f3(org, orgf); + dst = punched_shape_trace_ray(sshape->shape, inst->transform, org, dir, + hit->distance, N); if(dst >= FLT_MAX) { /* No projection is found => the ray does not intersect the quadric */ return 1; } if((float)dst <= rdata->range_min) { /* Handle RT numerical imprecision, the hit is below the lower bound - * of the ray range. */ + * of the ray range. */ return 1; } hit_side = d3_dot(dir, N) < 0 ? SSOL_FRONT : SSOL_BACK; if(inst == rdata->inst_from && hit_side != rdata->side_from) { /* The intersected instance is the one from which the ray starts, * ensure that the ray does not intersect the opposite side of the - * quadric + * quadric * * Note that reversed_ray is intentionally not considered here! */ return 1; @@ -449,7 +506,7 @@ hit_filter_function 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) { + if(mtl->type == SSOL_MATERIAL_VIRTUAL) { /* Discard all virtual materials */ if(rdata->discard_virtual_materials) return 1; /* Discard virtual material that are not receivers */ diff --git a/src/ssol_scene_c.h b/src/ssol_scene_c.h @@ -22,7 +22,7 @@ struct ssol_instance; - /* Define the htable_instance data structure */ +/* Define the htable_instance data structure */ #define HTABLE_NAME instance #define HTABLE_KEY unsigned /* S3D object instance identifier */ #define HTABLE_DATA struct ssol_instance* @@ -42,7 +42,6 @@ struct ssol_scene { struct s3d_scene* scn_rt; /* S3D scene to ray trace */ struct s3d_scene* scn_samp; /* S3D scene to sample */ - struct s3d_scene* scn_prim; /* S3D scene of primary objects */ struct ssol_sun* sun; /* Sun of the scene */ struct ssol_atmosphere* atmosphere; /* Atmosphere of the scene */ @@ -58,7 +57,8 @@ scene_create_s3d_views (struct ssol_scene* scn, struct s3d_scene_view** view_rt, struct s3d_scene_view** view_samp, - struct s3d_scene_view** out_view_prim); + double* sampled_area, /* Area of the instance set as "samplable" */ + double* sampled_area_proxy); /* Area of the sampled geometries */ #endif /* SSOL_SCENE_C_H */ diff --git a/src/ssol_shape.c b/src/ssol_shape.c @@ -44,7 +44,7 @@ struct mesh_context { struct quadric_mesh_context { const double* coords; const size_t* ids; - double focal; /* Use by parabol and parabolic cylinder quadrics */ + const union priv_quadric_data* quadric; const double* transform; /* 3x4 column major matrix */ }; @@ -64,6 +64,12 @@ check_parabol(const struct ssol_quadric_parabol* parabol) } static INLINE int +check_hyperbol(const struct ssol_quadric_hyperbol* hyperbol) +{ + return hyperbol && hyperbol->img_focal > 0 && hyperbol->real_focal > 0; +} + +static INLINE int check_parabolic_cylinder (const struct ssol_quadric_parabolic_cylinder* parabolic_cylinder) { @@ -80,6 +86,8 @@ check_quadric(const struct ssol_quadric* quadric) return check_plane(&quadric->data.plane); case SSOL_QUADRIC_PARABOL: return check_parabol(&quadric->data.parabol); + case SSOL_QUADRIC_HYPERBOL: + return check_hyperbol(&quadric->data.hyperbol); case SSOL_QUADRIC_PARABOLIC_CYLINDER: return check_parabolic_cylinder(&quadric->data.parabolic_cylinder); default: return 0; @@ -183,6 +191,33 @@ quadric_mesh_plane_get_pos(const unsigned ivert, float pos[3], void* ctx) f3_set_d3(pos, p); } +static FINLINE double +hyperbol_z + (const double p[2], + const struct priv_hyperbol_data* hyperbol) +{ + const double z0 = hyperbol->g_2 + hyperbol->abs_b; + const double r2 = p[0] * p[0] + p[1] * p[1]; + return hyperbol->abs_b * sqrt(1 + r2 * hyperbol->_1_a2) + hyperbol->g_2 - z0; +} + +static FINLINE double +parabol_z + (const double p[2], + const struct priv_parabol_data* parabol) +{ + const double r2 = p[0] * p[0] + p[1] * p[1]; + return r2 * parabol->_1_4f; +} + +static FINLINE double +parabolic_cylinder_z + (const double p[2], + const struct priv_pcylinder_data* pcyl) +{ + return (p[1] * p[1]) * pcyl->_1_4f; +} + static void quadric_mesh_parabol_get_pos(const unsigned ivert, float pos[3], void* ctx) { @@ -192,7 +227,25 @@ quadric_mesh_parabol_get_pos(const unsigned ivert, float pos[3], void* ctx) ASSERT(pos && ctx); p[0] = msh->coords[i+0]; p[1] = msh->coords[i+1]; - p[2] = (p[0]*p[0] + p[1]*p[1]) / (4.0*msh->focal); + p[2] = parabol_z(p, &msh->quadric->parabol); + + /* Transform the position in object space */ + d33_muld3(p, msh->transform, p); + d3_add(p, p, msh->transform+9); + + f3_set_d3(pos, p); +} + +static void +quadric_mesh_hyperbol_get_pos(const unsigned ivert, float pos[3], void* ctx) +{ + const size_t i = ivert * 2/*#coords per vertex*/; + const struct quadric_mesh_context* msh = ctx; + double p[3]; /* Temporary quadric space position */ + ASSERT(pos && ctx); + p[0] = msh->coords[i+0]; + p[1] = msh->coords[i+1]; + p[2] = hyperbol_z(p, &msh->quadric->hyperbol); /* Transform the position in object space */ d33_muld3(p, msh->transform, p); @@ -211,7 +264,7 @@ quadric_mesh_parabolic_cylinder_get_pos ASSERT(pos && ctx); p[0] = msh->coords[i+0]; p[1] = msh->coords[i+1]; - p[2] = ((p[1]*p[1]) / (4.0*msh->focal)); + p[2] = parabolic_cylinder_z(p, &msh->quadric->pcylinder); /* Transform the position in object space */ d33_muld3(p, msh->transform, p); @@ -415,21 +468,60 @@ error: goto exit; } +static double +mesh_compute_area + (const unsigned ntris, + void (*get_indices)(const unsigned itri, unsigned ids[3], void* data), + const unsigned nverts, + void (*get_position)(const unsigned ivert, float position[3], void* data), + void* ctx) +{ + unsigned itri; + double area = 0; + (void)nverts; + + FOR_EACH(itri, 0, ntris) { + float v0[3], v1[3], v2[3]; + double E0[3], E1[3], N[3]; + double V0[3], V1[3], V2[3]; + unsigned IDS[3]; + + get_indices(itri, IDS, ctx); + ASSERT(IDS[0] < nverts); + ASSERT(IDS[1] < nverts); + ASSERT(IDS[2] < nverts); + + get_position(IDS[0], v0, ctx); + get_position(IDS[1], v1, ctx); + get_position(IDS[2], v2, ctx); + d3_set_f3(V0, v0); + d3_set_f3(V1, v1); + d3_set_f3(V2, v2); + d3_sub(E0, V1, V0); + d3_sub(E1, V2, V0); + + area += d3_len(d3_cross(N, E0, E1)); + } + return area * 0.5; +} + /* Setup the Star-3D shape of the quadric to ray-trace, i.e. the clipped 2D * profile of the quadric whose vertices are displaced with respect to the * quadric equation */ static res_T quadric_setup_s3d_shape_rt - (const struct ssol_quadric* quadric, + (const struct ssol_shape* shape, const struct darray_double* coords, const struct darray_size_t* ids, - struct s3d_shape* shape) + struct s3d_shape* s3dshape, + double* rt_area) { struct quadric_mesh_context ctx; struct s3d_vertex_data vdata; unsigned nverts; unsigned ntris; - ASSERT(quadric && coords && ids && shape); + res_T res; + ASSERT(shape && coords && ids && s3dshape && rt_area); ASSERT(darray_double_size_get(coords)%2 == 0); ASSERT(darray_size_t_size_get(ids)%3 == 0); ASSERT(darray_double_size_get(coords)/2 <= UINT_MAX); @@ -439,17 +531,20 @@ quadric_setup_s3d_shape_rt ntris = (unsigned)darray_size_t_size_get(ids) / 3/*#ids per triangle*/; ctx.coords = darray_double_cdata_get(coords); ctx.ids = darray_size_t_cdata_get(ids); - ctx.transform = quadric->transform; + ctx.transform = shape->quadric.transform; vdata.usage = S3D_POSITION; vdata.type = S3D_FLOAT3; - switch(quadric->type) { + vdata.get = NULL; + ctx.quadric = &shape->priv_quadric; + switch (shape->quadric.type) { case SSOL_QUADRIC_PARABOL: - ctx.focal = quadric->data.parabol.focal; vdata.get = quadric_mesh_parabol_get_pos; break; + case SSOL_QUADRIC_HYPERBOL: + vdata.get = quadric_mesh_hyperbol_get_pos; + break; case SSOL_QUADRIC_PARABOLIC_CYLINDER: - ctx.focal = quadric->data.parabolic_cylinder.focal; vdata.get = quadric_mesh_parabolic_cylinder_get_pos; break; case SSOL_QUADRIC_PLANE: @@ -458,8 +553,14 @@ quadric_setup_s3d_shape_rt default: FATAL("Unreachable code.\n"); break; } - return s3d_mesh_setup_indexed_vertices - (shape, ntris, quadric_mesh_get_ids, nverts, &vdata, 1, &ctx); + res = s3d_mesh_setup_indexed_vertices + (s3dshape, ntris, quadric_mesh_get_ids, nverts, &vdata, 1, &ctx); + if(res != RES_OK) return res; + + ASSERT(vdata.get); + *rt_area = mesh_compute_area + (ntris, quadric_mesh_get_ids, nverts, vdata.get, &ctx); + return RES_OK; } /* Setup the Star-3D shape of the quadric to sample, i.e. the clipped 2D @@ -469,12 +570,14 @@ quadric_setup_s3d_shape_samp (const struct ssol_quadric* quadric, const struct darray_double* coords, const struct darray_size_t* ids, - struct s3d_shape* shape) + struct s3d_shape* shape, + double *samp_area) { struct quadric_mesh_context ctx; struct s3d_vertex_data vdata; unsigned nverts; unsigned ntris; + res_T res; ASSERT(coords && ids && shape); ASSERT(darray_double_size_get(coords)%2 == 0); ASSERT(darray_size_t_size_get(ids)%3 == 0); @@ -490,8 +593,12 @@ quadric_setup_s3d_shape_samp vdata.usage = S3D_POSITION; vdata.type = S3D_FLOAT3; vdata.get = quadric_mesh_plane_get_pos; - return s3d_mesh_setup_indexed_vertices + res = s3d_mesh_setup_indexed_vertices (shape, ntris, quadric_mesh_get_ids, nverts, &vdata, 1, &ctx); + if(res != RES_OK) return res; + *samp_area = mesh_compute_area + (ntris, quadric_mesh_get_ids, nverts, quadric_mesh_plane_get_pos, &ctx); + return RES_OK; } static res_T @@ -584,30 +691,41 @@ quadric_plane_gradient_local(double grad[3]) static FINLINE void quadric_parabol_gradient_local - (const struct ssol_quadric_parabol* quad, + (const struct priv_parabol_data* quad, const double pt[3], double grad[3]) { - double tmp[3]; ASSERT(quad && pt && grad); - tmp[0] = -pt[0]; - tmp[1] = -pt[1]; - tmp[2] = 2 * quad->focal; - d3_set(grad, tmp); + grad[0] = -pt[0]; + grad[1] = -pt[1]; + grad[2] = 2 * quad->focal; +} + +static FINLINE void +quadric_hyperbol_gradient_local + (const struct priv_hyperbol_data* quad, + const double pt[3], + double grad[3]) +{ + ASSERT(quad && pt && grad); + { + const double z0 = quad->g_2 + quad->abs_b; + grad[0] = pt[0]; + grad[1] = pt[1]; + grad[2] = -(pt[2] + z0 - quad->g_2) * quad->_a2_b2; + } } static FINLINE void quadric_parabolic_cylinder_gradient_local - (const struct ssol_quadric_parabolic_cylinder* quad, + (const struct priv_pcylinder_data* quad, const double pt[3], double grad[3]) { - double tmp[3]; ASSERT(quad && pt && grad); - tmp[0] = 0; - tmp[1] = -pt[1]; - tmp[2] = 2 * quad->focal; - d3_set(grad, tmp); + grad[0] = 0; + grad[1] = -pt[1]; + grad[2] = 2 * quad->focal; } static FINLINE int @@ -615,7 +733,7 @@ quadric_plane_intersect_local (const double org[3], const double dir[3], const double hint, - double pt[3], + double hit_pt[3], double grad[3], double* dist) { @@ -623,14 +741,11 @@ quadric_plane_intersect_local const double a = 0; const double b = dir[2]; const double c = org[2]; - double tmp[3]; double dst; int sol = quadric_solve_second(a, b, c, hint, &dst); if(!sol) return 0; - d3_add(tmp, org, d3_muld(tmp, dir, dst)); - - d3_set(pt, tmp); + d3_add(hit_pt, org, d3_muld(hit_pt, dir, dst)); quadric_plane_gradient_local(grad); *dist = dst; return 1; @@ -638,11 +753,11 @@ quadric_plane_intersect_local static FINLINE int quadric_parabol_intersect_local - (const struct ssol_quadric_parabol* quad, + (const struct priv_parabol_data* quad, const double org[3], const double dir[3], const double hint, - double pt[3], + double hit_pt[3], double grad[3], double* dist) /* in/out: */ { @@ -653,23 +768,50 @@ quadric_parabol_intersect_local 2 * org[0] * dir[0] + 2 * org[1] * dir[1] - 4 * quad->focal * dir[2]; const double c = org[0] * org[0] + org[1] * org[1] - 4 * quad->focal * org[2]; const int sol = quadric_solve_second(a, b, c, hint, &dst); - double tmp[3]; if(!sol) return 0; - d3_add(tmp, org, d3_muld(tmp, dir, dst)); - quadric_parabol_gradient_local(quad, tmp, grad); - d3_set(pt, tmp); + d3_add(hit_pt, org, d3_muld(hit_pt, dir, dst)); + quadric_parabol_gradient_local(quad, hit_pt, grad); + *dist = dst; + return 1; +} + +static FINLINE int +quadric_hyperbol_intersect_local + (const struct priv_hyperbol_data* quad, + const double org[3], + const double dir[3], + const double hint, + double hit_pt[3], + double grad[3], + double* dist) +{ + double dst; + const double b2 = quad->abs_b * quad->abs_b; + const double b2_a2 = b2 * quad->_1_a2; + const double z0 = quad->g_2 + quad->abs_b; + const double a = + b2_a2 * (dir[0] * dir[0] + dir[1] * dir[1]) - dir[2] * dir[2]; + const double b = + 2 * (b2_a2 * (org[0] * dir[0] + org[1] * dir[1]) - (org[2] + z0 - quad->g_2) * dir[2]); + const double c = b2_a2 * (org[0] * org[0] + org[1] * org[1]) + b2 + - (org[2] + z0 - quad->g_2) * (org[2] + z0 - quad->g_2); + const int sol = quadric_solve_second(a, b, c, hint, &dst); + + if(!sol) return 0; + d3_add(hit_pt, org, d3_muld(hit_pt, dir, dst)); + quadric_hyperbol_gradient_local(quad, hit_pt, grad); *dist = dst; return 1; } static FINLINE int quadric_parabolic_cylinder_intersect_local - (const struct ssol_quadric_parabolic_cylinder* quad, + (const struct priv_pcylinder_data* quad, const double org[3], const double dir[3], const double hint, - double pt[3], + double hit_pt[3], double grad[3], double* dist) { @@ -678,9 +820,10 @@ quadric_parabolic_cylinder_intersect_local const double b = 2 * org[1] * dir[1] - 4 * quad->focal * dir[2]; const double c = org[1] * org[1] - 4 * quad->focal * org[2]; const int sol = quadric_solve_second(a, b, c, hint, dist); + if(!sol) return 0; - d3_add(pt, org, d3_muld(pt, dir, *dist)); - quadric_parabolic_cylinder_gradient_local(quad, pt, grad); + d3_add(hit_pt, org, d3_muld(hit_pt, dir, *dist)); + quadric_parabolic_cylinder_gradient_local(quad, hit_pt, grad); return 1; } @@ -690,21 +833,18 @@ punched_shape_set_z_local(const struct ssol_shape* shape, double pt[3]) ASSERT(shape && pt); ASSERT(shape->type == SHAPE_PUNCHED); switch (shape->quadric.type) { - case SSOL_QUADRIC_PLANE: { + case SSOL_QUADRIC_PLANE: pt[2] = 0; break; - } - case SSOL_QUADRIC_PARABOLIC_CYLINDER: { - const struct ssol_quadric_parabolic_cylinder* quad - = &shape->quadric.data.parabolic_cylinder; - pt[2] = (pt[1] * pt[1]) / (4.0 * quad->focal); + case SSOL_QUADRIC_PARABOLIC_CYLINDER: + pt[2] = parabolic_cylinder_z(pt, &shape->priv_quadric.pcylinder); break; - } - case SSOL_QUADRIC_PARABOL: { - const struct ssol_quadric_parabol* quad = &shape->quadric.data.parabol; - pt[2] = (pt[0] * pt[0] + pt[1] * pt[1]) / (4.0 * quad->focal); + case SSOL_QUADRIC_PARABOL: + pt[2] = parabol_z(pt, &shape->priv_quadric.parabol); + break; + case SSOL_QUADRIC_HYPERBOL: + pt[2] = hyperbol_z(pt, &shape->priv_quadric.hyperbol); break; - } default: FATAL("Unreachable code\n"); break; } } @@ -723,11 +863,15 @@ punched_shape_set_normal_local break; case SSOL_QUADRIC_PARABOLIC_CYLINDER: quadric_parabolic_cylinder_gradient_local - (&shape->quadric.data.parabolic_cylinder, pt, normal); + (&shape->priv_quadric.pcylinder, pt, normal); break; case SSOL_QUADRIC_PARABOL: { quadric_parabol_gradient_local - (&shape->quadric.data.parabol, pt, normal); + (&shape->priv_quadric.parabol, pt, normal); + break; + case SSOL_QUADRIC_HYPERBOL: + quadric_hyperbol_gradient_local + (&shape->priv_quadric.hyperbol, pt, normal); break; } default: FATAL("Unreachable code\n"); break; @@ -756,11 +900,15 @@ punched_shape_intersect_local break; case SSOL_QUADRIC_PARABOLIC_CYLINDER: hit = quadric_parabolic_cylinder_intersect_local - (&shape->quadric.data.parabolic_cylinder, org, dir, hint, pt, N, dist); + (&shape->priv_quadric.pcylinder, org, dir, hint, pt, N, dist); break; case SSOL_QUADRIC_PARABOL: hit = quadric_parabol_intersect_local - (&shape->quadric.data.parabol, org, dir, hint, pt, N, dist); + (&shape->priv_quadric.parabol, org, dir, hint, pt, N, dist); + break; + case SSOL_QUADRIC_HYPERBOL: + hit = quadric_hyperbol_intersect_local + (&shape->priv_quadric.hyperbol, org, dir, hint, pt, N, dist); break; default: FATAL("Unreachable code\n"); break; } @@ -781,6 +929,106 @@ shape_release(ref_T* ref) SSOL(device_ref_put(dev)); } +/* Return the parabol discretisation parameter */ +static FINLINE void +priv_parabol_data_setup + (struct priv_parabol_data* data, + const struct ssol_quadric_parabol* parabol) +{ + ASSERT(data && parabol); + data->focal = parabol->focal; + data->_1_4f = 1 / (4.0 * parabol->focal); +} + +static FINLINE void +priv_hyperbol_data_setup + (struct priv_hyperbol_data* data, + const struct ssol_quadric_hyperbol* hyperbol) +{ + double g, f, a2; + ASSERT(data && hyperbol); + + /* Re-dimensionalize */ + g = hyperbol->real_focal + hyperbol->img_focal; + f = hyperbol->real_focal / g; + a2 = g * g * (f - f * f); + + data->g_2 = g * 0.5; + data->abs_b = g * fabs(f - 0.5); + data->_a2_b2 = a2 / (data->abs_b * data->abs_b); + data->_1_a2 = 1 / a2; +} + +static FINLINE void +priv_parabolic_cylinder_data_setup + (struct priv_pcylinder_data* data, + const struct ssol_quadric_parabolic_cylinder* parabolic_cylinder) +{ + ASSERT(data && parabolic_cylinder); + data->focal = parabolic_cylinder->focal; + data->_1_4f = 1 / (4.0 * parabolic_cylinder->focal); +} + +static INLINE void +priv_quadric_data_setup + (union priv_quadric_data* priv_data, + const struct ssol_quadric* quadric) +{ + ASSERT(priv_data && quadric); + switch(quadric->type) { + case SSOL_QUADRIC_PLANE: /* Do nothing */ break; + case SSOL_QUADRIC_PARABOL: + priv_parabol_data_setup + (&priv_data->parabol, &quadric->data.parabol); + break; + case SSOL_QUADRIC_HYPERBOL: + priv_hyperbol_data_setup + (&priv_data->hyperbol, &quadric->data.hyperbol); + break; + case SSOL_QUADRIC_PARABOLIC_CYLINDER: + priv_parabolic_cylinder_data_setup + (&priv_data->pcylinder, &quadric->data.parabolic_cylinder); + break; + default: FATAL("Unreachable code\n"); break; + } +} + +static INLINE size_t +priv_quadric_data_compute_slices_count + (const enum ssol_quadric_type type, + const union priv_quadric_data* priv_data, + const double lower[3], + const double upper[3]) +{ + size_t nslices; + double max_z; + ASSERT(priv_data && lower && upper); + + switch(type) { + case SSOL_QUADRIC_PLANE: nslices = 1; break; + case SSOL_QUADRIC_PARABOL: + max_z = MMAX + (parabol_z(lower, &priv_data->parabol), + parabol_z(upper, &priv_data->parabol)); + nslices = MMIN(50, (size_t)(3 + sqrt(max_z) * 6)); + break; + case SSOL_QUADRIC_HYPERBOL: + max_z = MMAX + (hyperbol_z(lower, &priv_data->hyperbol), + hyperbol_z(upper, &priv_data->hyperbol)); + nslices = MMIN(50, (size_t)(3 + sqrt(max_z) * 6)); + break; + case SSOL_QUADRIC_PARABOLIC_CYLINDER: + max_z = MMAX + (parabolic_cylinder_z(lower, &priv_data->pcylinder), + parabolic_cylinder_z(upper, &priv_data->pcylinder)); + nslices = MMIN(50, (size_t)(3 + sqrt(max_z) * 6)); + break; + default: FATAL("Unreachable code\n"); break; + } + return nslices; +} + /******************************************************************************* * Local functions ******************************************************************************/ @@ -829,10 +1077,9 @@ double punched_shape_trace_ray (struct ssol_shape* shape, const double transform[12], /* Shape to world space transformation */ - const double pos[3], /* World space position near of the quadric */ - const double dir[3], /* World space projection direction */ + const double org[3], /* World space position near of the ray origin */ + const double dir[3], /* World space ray direction */ const double hint_dst, /* Hint on the hit distance */ - double pos_quadric[3], /* World space position onto the quadric */ double N_quadric[3]) /* World space normal onto the quadric */ { double R[9]; /* Quadric to world rotation matrix */ @@ -840,11 +1087,12 @@ punched_shape_trace_ray double T[3]; /* Quadric to world translation vector */ double T_inv[3]; /* Inverse of T */ double dir_local[3]; - double pos_local[3]; + double org_local[3]; + double hit_local[3]; double N_local[3]; double dst; /* Hit distance */ int valid; - ASSERT(shape && transform && pos && pos_quadric && N_quadric); + ASSERT(shape && transform && org && N_quadric); ASSERT(shape->type == SHAPE_PUNCHED); /* Compute world<->quadric space transformations */ @@ -855,27 +1103,52 @@ punched_shape_trace_ray d3_minus(T_inv, T); /* Transform pos in quadric space */ - d3_add(pos_local, pos, T_inv); - d3_muld33(pos_local, pos_local, R_invtrans); + d3_add(org_local, org, T_inv); + d3_muld33(org_local, org_local, R_invtrans); /* Transform dir in quadric space */ d3_muld33(dir_local, dir, R_invtrans); /* Project pos_local onto the quadric and compute its associated normal */ valid = punched_shape_intersect_local - (shape, pos_local, dir_local, hint_dst, pos_local, N_local, &dst); + (shape, org_local, dir_local, hint_dst, hit_local, N_local, &dst); if(!valid) return INF; - /* Transform the local position in world space */ - d33_muld3(pos_quadric, R, pos_local); - d3_add(pos_quadric, pos_quadric, T); - /* Transform the quadric normal in world space */ d33_muld3(N_quadric, R_invtrans, N_local); d3_normalize(N_quadric, N_quadric); return dst; } +res_T +shape_fetched_raw_vertex_attrib + (const struct ssol_shape* shape, + const unsigned ivert, + const enum ssol_attrib_usage usage, + double value[3]) +{ + struct s3d_attrib s3d_attr; + enum s3d_attrib_usage s3d_usage; + res_T res = RES_OK; + + ASSERT(shape && value); + s3d_usage = ssol_to_s3d_attrib_usage(usage); + + res = s3d_mesh_get_vertex_attrib + (shape->shape_rt, ivert, s3d_usage, &s3d_attr); + if(res != RES_OK) return res; + + d3_splat(value, 1); + switch(s3d_attr.type) { + case S3D_FLOAT3: value[2] = (double)s3d_attr.value[2]; + case S3D_FLOAT2: value[1] = (double)s3d_attr.value[1]; + case S3D_FLOAT: value[0] = (double)s3d_attr.value[0]; + break; + default: FATAL("Unexpected vertex attrib type\n"); break; + } + return RES_OK; +} + /******************************************************************************* * Exported ssol_shape functions ******************************************************************************/ @@ -912,6 +1185,57 @@ ssol_shape_ref_put(struct ssol_shape* shape) } res_T +ssol_shape_get_vertices_count + (const struct ssol_shape* shape, unsigned* nverts) +{ + if(!shape || !nverts) return RES_BAD_ARG; + return s3d_mesh_get_vertices_count(shape->shape_rt, nverts); +} + +res_T +ssol_shape_get_vertex_attrib + (const struct ssol_shape* shape, + const unsigned ivert, + const enum ssol_attrib_usage usage, + double value[]) +{ + res_T res = RES_OK; + if(!shape || (unsigned)usage >= SSOL_ATTRIBS_COUNT__ || !value) + return RES_BAD_ARG; + + res = shape_fetched_raw_vertex_attrib(shape, ivert, usage, value); + if(res != RES_OK) return res; + + /* Transform the fetch attrib */ + if(shape->type == SHAPE_PUNCHED) { + if(usage == SSOL_POSITION) { + d33_muld3(value, shape->quadric.transform, value); + d3_add(value, shape->quadric.transform + 9, value); + } else if(usage == SSOL_NORMAL) { + double R_invtrans[9]; + d33_invtrans(R_invtrans, shape->quadric.transform); + d33_muld3(value, R_invtrans, value); + } + } + return RES_OK; +} + +res_T +ssol_shape_get_triangles_count(const struct ssol_shape* shape, unsigned* ntris) +{ + if(!shape || !ntris) return RES_BAD_ARG; + return s3d_mesh_get_triangles_count(shape->shape_rt, ntris); +} + +res_T +ssol_shape_get_triangle_indices + (const struct ssol_shape* shape, const unsigned itri, unsigned ids[3]) +{ + if(!shape || !ids) return RES_BAD_ARG; + return s3d_mesh_get_triangle_indices(shape->shape_rt, itri, ids); +} + +res_T ssol_punched_surface_setup (struct ssol_shape* shape, const struct ssol_punched_surface* psurf) @@ -944,30 +1268,15 @@ ssol_punched_surface_setup goto error; } + /* Setup internal data */ + priv_quadric_data_setup(&shape->priv_quadric, psurf->quadric); + /* Define the #slices of the discretized quadric */ - switch (psurf->quadric->type) { - case SSOL_QUADRIC_PLANE: - nslices = 1; - break; - case SSOL_QUADRIC_PARABOL: { - double z[2]; - z[0] = (lower[0] * lower[0] + lower[1] * lower[1]) - / (4.0 * psurf->quadric->data.parabol.focal); - z[1] = (upper[0] * upper[0] + upper[1] * upper[1]) - / (4.0 * psurf->quadric->data.parabol.focal); - nslices = MMIN(50, (size_t)(3 + sqrt(MMAX(z[0], z[1])) * 6)); - break; - } - case SSOL_QUADRIC_PARABOLIC_CYLINDER: { - double z[2]; - z[0] = (lower[1] * lower[1]) / - (4.0 * psurf->quadric->data.parabolic_cylinder.focal); - z[1] = (upper[1] * upper[1]) / - (4.0 * psurf->quadric->data.parabolic_cylinder.focal); - nslices = MMIN(50, (size_t)(3 + sqrt(MMAX(z[0], z[1])) * 6)); - break; - } - default: FATAL("Unreachable code\n"); break; + if(psurf->quadric->slices_count_hint != SIZE_MAX) { + nslices = psurf->quadric->slices_count_hint; + } else { + nslices = priv_quadric_data_compute_slices_count + (shape->quadric.type, &shape->priv_quadric, lower, upper); } res = build_triangulated_plane(&coords, &ids, lower, upper, nslices); @@ -979,11 +1288,12 @@ ssol_punched_surface_setup /* Setup the Star-3D shape to ray-trace */ res = quadric_setup_s3d_shape_rt - (psurf->quadric, &coords, &ids, shape->shape_rt); + (shape, &coords, &ids, shape->shape_rt, &shape->shape_rt_area); if(res != RES_OK) goto error; /* Setup the Star-3D shape to sample */ - res = quadric_setup_s3d_shape_samp(psurf->quadric, &coords, &ids, shape->shape_samp); + res = quadric_setup_s3d_shape_samp + (psurf->quadric, &coords, &ids, shape->shape_samp, &shape->shape_samp_area); if(res != RES_OK) goto error; exit: @@ -1005,6 +1315,7 @@ ssol_mesh_setup void* data) { struct s3d_vertex_data attrs[SSOL_ATTRIBS_COUNT__]; + void (*get_position)(const unsigned ivert, float position[3], void* data) = NULL; res_T res = RES_OK; unsigned i; @@ -1030,6 +1341,8 @@ ssol_mesh_setup case SSOL_POSITION: attrs[i].usage = SSOL_TO_S3D_POSITION; attrs[i].type = S3D_FLOAT3; + ASSERT(!get_position); + get_position = attrs[i].get; break; case SSOL_NORMAL: attrs[i].usage = SSOL_TO_S3D_NORMAL; @@ -1042,13 +1355,18 @@ ssol_mesh_setup default: FATAL("Unreachable code.\n"); break; } } + ASSERT(get_position); + res = s3d_mesh_setup_indexed_vertices (shape->shape_rt, ntris, get_indices, nverts, attrs, nattribs, data); if(res != RES_OK) goto error; + shape->shape_rt_area = + mesh_compute_area(ntris, get_indices, nverts, get_position, data); /* The Star-3D shape to sample is the same of the one to ray-traced */ res = s3d_mesh_copy(shape->shape_rt, shape->shape_samp); if(res != RES_OK) goto error; + shape->shape_samp_area = shape->shape_rt_area; exit: return res; diff --git a/src/ssol_shape_c.h b/src/ssol_shape_c.h @@ -26,12 +26,37 @@ enum shape_type { SHAPE_TYPES_COUNT__ }; +struct priv_parabol_data { + double focal; + double _1_4f; +}; + +struct priv_hyperbol_data { + double g_2; + double _a2_b2; + double _1_a2; + double abs_b; +}; + +struct priv_pcylinder_data { + double focal; + double _1_4f; +}; + +union priv_quadric_data { + struct priv_hyperbol_data hyperbol; + struct priv_parabol_data parabol; + struct priv_pcylinder_data pcylinder; +}; + struct ssol_shape { enum shape_type type; struct s3d_shape* shape_rt; /* Star-3D shape to ray-trace */ struct s3d_shape* shape_samp; /* Star-3D shape to sample */ + union priv_quadric_data priv_quadric; struct ssol_quadric quadric; + double shape_rt_area, shape_samp_area; struct ssol_device* dev; ref_T ref; @@ -55,8 +80,16 @@ punched_shape_trace_ray const double org[3], /* Ray origin in world space */ const double dir[3], /* Ray direction in world space */ const double hint_dst, /* Hint on the hit distance */ - double pos_quadric[3], /* World space position onto the quadric */ double N_quadric[3]); /* World space normal onto the quadric */ +/* Fetch vertex attrib without any post treatment, i.e. the position and the + * normal are not transformed */ +extern LOCAL_SYM res_T +shape_fetched_raw_vertex_attrib + (const struct ssol_shape* shape, + const unsigned ivert, + const enum ssol_attrib_usage usage, + double value[]); + #endif /* SSOL_SHAPE_C_H */ diff --git a/src/ssol_solver.c b/src/ssol_solver.c @@ -44,9 +44,117 @@ #include <limits.h> #include <omp.h> +/******************************************************************************* + * Thread context + ******************************************************************************/ +struct thread_context { + struct ssp_rng* rng; + struct ssf_bsdf* bsdf; + struct mc_data shadowed; + struct mc_data missing; + struct mc_data cos_loss; + struct htable_receiver mc_rcvs; + struct htable_sampled mc_samps; + struct darray_path paths; /* paths */ +}; + +static void +thread_context_release(struct thread_context* ctx) +{ + ASSERT(ctx); + if(ctx->rng) SSP(rng_ref_put(ctx->rng)); + if(ctx->bsdf) SSF(bsdf_ref_put(ctx->bsdf)); + htable_receiver_release(&ctx->mc_rcvs); + htable_sampled_release(&ctx->mc_samps); + darray_path_release(&ctx->paths); +} + +static res_T +thread_context_init(struct mem_allocator* allocator, struct thread_context* ctx) +{ + res_T res = RES_OK; + ASSERT(ctx); + + memset(ctx, 0, sizeof(ctx[0])); + htable_receiver_init(allocator, &ctx->mc_rcvs); + htable_sampled_init(allocator, &ctx->mc_samps); + darray_path_init(allocator, &ctx->paths); + + res = ssf_bsdf_create(allocator, &ctx->bsdf); + if(res != RES_OK) goto error; + +exit: + return res; +error: + thread_context_release(ctx); + goto exit; +} + +/* Define a copy functor only for consistency since this function will not be + * used */ +static res_T +thread_context_copy + (struct thread_context* dst, const struct thread_context* src) +{ + res_T res = RES_OK; + ASSERT(dst && src); + dst->rng = src->rng; + dst->bsdf = src->bsdf; + dst->shadowed = src->shadowed; + dst->missing = src->missing; + dst->cos_loss = src->cos_loss; + res = htable_receiver_copy(&dst->mc_rcvs, &src->mc_rcvs); + if(res != RES_OK) return res; + res = htable_sampled_copy(&dst->mc_samps, &src->mc_samps); + if(res != RES_OK) return res; + res = darray_path_copy(&dst->paths, &src->paths); + if(res != RES_OK) return res; + return RES_OK; +} + +static void +thread_context_clear(struct thread_context* ctx) +{ + ASSERT(ctx); + if(ctx->rng) SSP(rng_ref_put(ctx->rng)); + htable_receiver_clear(&ctx->mc_rcvs); + htable_sampled_clear(&ctx->mc_samps); + darray_path_clear(&ctx->paths); +} + +static res_T +thread_context_setup + (struct thread_context* ctx, + struct ssp_rng_proxy* rng_proxy, + const size_t ithread) +{ + res_T res = RES_OK; + ASSERT(rng_proxy && ctx); + thread_context_clear(ctx); + res = ssp_rng_proxy_create_rng(rng_proxy, ithread, &ctx->rng); + if(res != RES_OK) goto error; +exit: + return res; +error: + thread_context_clear(ctx); + goto exit; +} + +/* Declare the container of the per thread contexts */ +#define DARRAY_NAME thread_ctx +#define DARRAY_DATA struct thread_context +#define DARRAY_FUNCTOR_INIT thread_context_init +#define DARRAY_FUNCTOR_RELEASE thread_context_release +#define DARRAY_FUNCTOR_COPY thread_context_copy +#include <rsys/dynamic_array.h> + +/******************************************************************************* + * Random walk point + ******************************************************************************/ struct point { const struct ssol_instance* inst; const struct shaded_shape* sshape; + struct mc_sampled* mc_samp; struct s3d_primitive prim; double N[3]; double pos[3]; @@ -63,6 +171,7 @@ struct point { #define POINT_NULL__ { \ NULL, /* Instance */ \ NULL, /* Shaded shape */ \ + NULL, /* Primary data */ \ S3D_PRIMITIVE_NULL__, /* Primitive */ \ {0, 0, 0}, /* Normal */ \ {0, 0, 0}, /* Position */ \ @@ -74,27 +183,27 @@ struct point { } static const struct point POINT_NULL = POINT_NULL__; -/******************************************************************************* - * Helper functions - ******************************************************************************/ -/* Return 1 if the returned point is lit by the sun and 0 otherwise */ -static int +static res_T point_init (struct point* pt, + const double sampled_area_proxy, struct ssol_scene* scn, - const double sampled_area, + struct htable_sampled* sampled, struct s3d_scene_view* view_samp, struct s3d_scene_view* view_rt, struct ranst_sun_dir* ran_sun_dir, struct ranst_sun_wl* ran_sun_wl, - struct ssp_rng* rng) + struct ssp_rng* rng, + int* is_lit) { struct s3d_attrib attr; struct s3d_hit hit; struct ray_data ray_data = RAY_DATA_NULL; float dir[3], pos[3], range[2] = { 0, FLT_MAX }; - double cos_sun; size_t id; + res_T res = RES_OK; + ASSERT(pt && scn && sampled && view_samp && view_rt); + ASSERT(ran_sun_dir && ran_sun_wl && rng && is_lit); /* Sample a point into the scene view */ S3D(scene_view_sample @@ -113,15 +222,6 @@ point_init f3_normalize(attr.value, attr.value); d3_set_f3(pt->N, attr.value); - /* Sample a sun direction */ - ranst_sun_dir_get(ran_sun_dir, rng, pt->dir); - - /* Initialise the Monte Carlo weight */ - cos_sun = fabs(d3_dot(pt->N, pt->dir)); - pt->weight = scn->sun->dni * sampled_area * cos_sun; - pt->cos_loss = scn->sun->dni * sampled_area * (1 - cos_sun); - pt->absorptivity_loss = pt->reflectivity_loss = 0; - /* Retrieve the sampled instance and shaded shape */ pt->inst = *htable_instance_find(&scn->instances_samp, &pt->prim.inst_id); id = *htable_shaded_shape_find @@ -129,12 +229,36 @@ point_init pt->sshape = darray_shaded_shape_cdata_get (&pt->inst->object->shaded_shapes) + id; - /* For punched surface, retrieve the sampled position and normal onto the - * quadric surface */ - if(pt->sshape->shape->type == SHAPE_PUNCHED) { + /* Sample a sun direction */ + ranst_sun_dir_get(ran_sun_dir, rng, pt->dir); + + /* Initialise the Monte Carlo weight */ + if(pt->sshape->shape->type != SHAPE_PUNCHED) { + double surface_sun_cos = fabs(d3_dot(pt->N, pt->dir)); + pt->weight = scn->sun->dni * sampled_area_proxy * surface_sun_cos; + pt->cos_loss = scn->sun->dni * sampled_area_proxy * (1 - surface_sun_cos); + } else { + double proxy_sun_cos = fabs(d3_dot(pt->N, pt->dir)); + double cos_ratio, surface_proxy_cos, surface_sun_cos, tmp_n[3]; + /* For punched surface, retrieve the sampled position and normal onto the + * quadric surface */ punched_shape_project_point - (pt->sshape->shape, pt->inst->transform, pt->pos, pt->pos, pt->N); + (pt->sshape->shape, pt->inst->transform, pt->pos, pt->pos, tmp_n); + surface_proxy_cos = d3_dot(pt->N, tmp_n); + surface_sun_cos = d3_dot(tmp_n, pt->dir); + cos_ratio = fabs(surface_sun_cos / surface_proxy_cos); + d3_set(pt->N, tmp_n); + pt->weight = scn->sun->dni * sampled_area_proxy * cos_ratio; + pt->cos_loss = scn->sun->dni * sampled_area_proxy * (1 - proxy_sun_cos); } + pt->absorptivity_loss = pt->reflectivity_loss = 0; + + /* Store sampled entity related weights */ + res = get_mc_sampled(sampled, pt->inst, &pt->mc_samp); + if(res != RES_OK) goto error; + pt->mc_samp->cos_loss.weight += pt->cos_loss; + pt->mc_samp->cos_loss.sqr_weight += pt->cos_loss * pt->cos_loss; + pt->mc_samp->nb_samples++; /* Define the primitive side on which the point lies */ if(d3_dot(pt->N, pt->dir) < 0) { @@ -157,11 +281,15 @@ point_init f3_minus(dir, f3_set_d3(dir, pt->dir)); f3_set_d3(pos, pt->pos); S3D(scene_view_trace_ray(view_rt, pos, dir, range, &ray_data, &hit)); - if(!S3D_HIT_NONE(&hit)) return 0; + *is_lit = S3D_HIT_NONE(&hit); + if(*is_lit) { + pt->wl = ranst_sun_wl_get(ran_sun_wl, rng); /* Sample a wavelength */ + } - /* Sample a wavelength */ - pt->wl = ranst_sun_wl_get(ran_sun_wl, rng); - return 1; +exit: + return res; +error: + goto exit; } static FINLINE void @@ -220,11 +348,19 @@ point_get_material(const struct point* pt) static FINLINE res_T point_shade - (struct point* pt, struct ssf_bsdf* bsdf, struct ssp_rng* rng, double dir[3]) + (struct point* pt, + struct ssf_bsdf* bsdf, + struct ssol_medium* medium, + struct ssp_rng* rng, + double dir[3]) { + struct ssol_material* mtl; struct surface_fragment frag; - double wi[3], pdf, r; + double r = 1; + double wi[3], pdf; + int type; res_T res; + ASSERT(pt && bsdf && medium && rng && dir); /* TODO ensure that if `prim' was sampled, then the surface fragment setup * remains valid in *all* situations, i.e. even though the point primitive @@ -240,19 +376,25 @@ point_shade surface_fragment_setup(&frag, pt->pos, pt->dir, pt->N, &pt->prim, pt->uv); /* Shade the surface fragment */ + mtl = point_get_material(pt); SSF(bsdf_clear(bsdf)); - res = material_shade(point_get_material(pt), &frag, pt->wl, bsdf); + res = material_shade(mtl, &frag, pt->wl, medium, bsdf); if(res != RES_OK) return res; /* By convention, Star-SF assumes that incoming and reflected * directions point outward the surface => negate incoming dir */ d3_minus(wi, pt->dir); - r = ssf_bsdf_sample(bsdf, rng, wi, frag.Ns, dir, &pdf); - ASSERT(0 <= r && r <= 1); + if(d3_dot(wi, frag.Ns) <= 0) { + r = 0; + } else { + r = ssf_bsdf_sample(bsdf, rng, wi, frag.Ns, dir, &type, &pdf); + ASSERT(0 <= r && r <= 1); + } pt->reflectivity_loss += (1 - r) * pt->weight; pt->weight *= r; + if(type & SSF_TRANSMISSION) material_get_next_medium(mtl, medium, medium); return RES_OK; } @@ -296,32 +438,35 @@ point_dump return n != 1 ? RES_IO_ERR : RES_OK; } +/******************************************************************************* + * Helper functions + ******************************************************************************/ static FINLINE res_T check_scene(const struct ssol_scene* scene, const char* caller) { ASSERT(scene && caller); - if (!scene->sun) { + if(!scene->sun) { log_error(scene->dev, "%s: no sun attached.\n", caller); return RES_BAD_ARG; } - if (!scene->sun->spectrum) { + if(!scene->sun->spectrum) { log_error(scene->dev, "%s: sun's spectrum undefined.\n", caller); return RES_BAD_ARG; } - if (scene->sun->dni <= 0) { + if(scene->sun->dni <= 0) { log_error(scene->dev, "%s: sun's DNI undefined.\n", caller); return RES_BAD_ARG; } - if (scene->atmosphere) { + if(scene->atmosphere) { int i; ASSERT(scene->atmosphere->type == ATMOS_UNIFORM); i = spectrum_includes (scene->atmosphere->data.uniform.spectrum, scene->sun->spectrum); - if (!i) { + if(!i) { log_error(scene->dev, "%s: sun/atmosphere spectra mismatch.\n", caller); return RES_BAD_ARG; } @@ -329,145 +474,289 @@ check_scene(const struct ssol_scene* scene, const char* caller) return RES_OK; } -/******************************************************************************* - * Exported functions - ******************************************************************************/ -res_T -ssol_solve - (struct ssol_scene* scn, - struct ssp_rng* rng_state, - const size_t realisations_count, - FILE* output, - struct ssol_estimator** out_estimator) +/* Compute an empirical length of the path segment coming from/going to the + * infinite, wrt the scene bounding box */ +static INLINE double +compute_infinite_path_segment_extend(struct s3d_scene_view* view) { - struct htable_receiver_iterator it, end; - struct s3d_scene_view* view_rt = NULL; - struct s3d_scene_view* view_samp = NULL; - struct s3d_scene_view* view_prim = NULL; - struct ranst_sun_dir* ran_sun_dir = NULL; - struct ranst_sun_wl* ran_sun_wl = NULL; - struct ssf_bsdf** bsdfs = NULL; - struct ssp_rng** rngs = NULL; - struct ssp_rng_proxy* rng_proxy = NULL; - struct mc_data* mc_shadows = NULL; - struct mc_data* mc_missings = NULL; - struct htable_receiver* mc_rcvs = NULL; - struct ssol_estimator* estimator = NULL; - float area; - int nthreads = 0; - int nrealisations = 0; - int i = 0; - ATOMIC res = RES_OK; - ASSERT(nrealisations <= INT_MAX); + float lower[3], upper[3], size[3]; + ASSERT(view); + S3D(scene_view_get_aabb(view, lower, upper)); + f3_sub(size, upper, lower); + return MMAX(size[0], MMAX(size[1], size[2])) * 0.75; +} - if(!scn || !rng_state || !realisations_count || !out_estimator) { - res = RES_BAD_ARG; - goto error; +static INLINE res_T +path_register_and_clear + (struct darray_path* paths, + struct path* path) +{ + struct path* dst_path; + size_t ipath; + res_T res = RES_OK; + ASSERT(paths && path); + + ipath = darray_path_size_get(paths); + res = darray_path_resize(paths, ipath + 1); + if(res != RES_OK) return res; + + dst_path = darray_path_data_get(paths) + ipath; + return path_copy_and_clear(dst_path, path); +} + +static res_T +accum_mc_receivers_1side + (struct mc_receiver_1side* dst, + struct mc_receiver_1side* src) +{ + struct htable_shape2mc_iterator it_shape, end_shape; + res_T res = RES_OK; + ASSERT(dst && src); + + #define ACCUM_WEIGHT(Name) { \ + dst->Name.weight += src->Name.weight; \ + dst->Name.sqr_weight += src->Name.sqr_weight; \ + } (void)0 + ACCUM_WEIGHT(integrated_irradiance); + ACCUM_WEIGHT(absorptivity_loss); + ACCUM_WEIGHT(reflectivity_loss); + ACCUM_WEIGHT(cos_loss); + #undef ACCUM_WEIGHT + + /* Merge the per shape MC */ + htable_shape2mc_begin(&src->shape2mc, &it_shape); + htable_shape2mc_end(&src->shape2mc, &end_shape); + while(!htable_shape2mc_iterator_eq(&it_shape, &end_shape)) { + struct htable_prim2mc_iterator it_prim, end_prim; + const struct ssol_shape* shape = *htable_shape2mc_iterator_key_get(&it_shape); + struct mc_shape_1side* mc_shape1_src; + struct mc_shape_1side* mc_shape1_dst; + + mc_shape1_src = htable_shape2mc_iterator_data_get(&it_shape); + + res = mc_receiver_1side_get_mc_shape(dst, shape, &mc_shape1_dst); + if(res != RES_OK) goto error; + + /* Merge the per primitive MC */ + htable_prim2mc_begin(&mc_shape1_src->prim2mc, &it_prim); + htable_prim2mc_end(&mc_shape1_src->prim2mc, &end_prim); + while(!htable_prim2mc_iterator_eq(&it_prim, &end_prim)) { + const unsigned iprim = *htable_prim2mc_iterator_key_get(&it_prim); + struct mc_primitive_1side* mc_prim1_src; + struct mc_primitive_1side* mc_prim1_dst; + + mc_prim1_src = htable_prim2mc_iterator_data_get(&it_prim); + + res = mc_shape_1side_get_mc_primitive(mc_shape1_dst, iprim, &mc_prim1_dst); + if(res != RES_OK) goto error; + + #define ACCUM_WEIGHT(Name) { \ + mc_prim1_dst->Name.weight += mc_prim1_src->Name.weight; \ + mc_prim1_dst->Name.sqr_weight += mc_prim1_src->Name.sqr_weight; \ + } (void)0 + ACCUM_WEIGHT(integrated_irradiance); + ACCUM_WEIGHT(absorptivity_loss); + ACCUM_WEIGHT(reflectivity_loss); + ACCUM_WEIGHT(cos_loss); + #undef ACCUM_WEIGHT + + htable_prim2mc_iterator_next(&it_prim); + } + htable_shape2mc_iterator_next(&it_shape); } - /* CL compiler supports OpenMP parallel loop whose indices are signed. The - * following line ensures that the unsigned number of realisations does not - * overflow the realisation index. */ - if(realisations_count > INT_MAX) { - res = RES_BAD_ARG; - goto error; +exit: + return res; +error: + goto exit; +} + +static res_T +accum_mc_sampled(struct mc_sampled* dst, struct mc_sampled* src) +{ + struct htable_receiver_iterator it, end; + struct mc_receiver mc_rcv_null; + res_T res = RES_OK; + ASSERT(dst && src); + + mc_receiver_init(NULL, &mc_rcv_null); + + #define ACCUM_WEIGHT(Name) { \ + dst->Name.weight += src->Name.weight; \ + dst->Name.sqr_weight += src->Name.sqr_weight; \ + } (void)0 + ACCUM_WEIGHT(cos_loss); + ACCUM_WEIGHT(shadowed); + #undef ACCUM_WEIGHT + + dst->nb_samples += src->nb_samples; + + /* dst->by_receiver += src->by_receiver; */ + htable_receiver_begin(&src->mc_rcvs, &it); + htable_receiver_end(&src->mc_rcvs, &end); + while(!htable_receiver_iterator_eq(&it, &end)) { + struct mc_receiver* src_mc_rcv = htable_receiver_iterator_data_get(&it); + const struct ssol_instance* inst = *htable_receiver_iterator_key_get(&it); + struct mc_receiver* dst_mc_rcv = htable_receiver_find(&dst->mc_rcvs, &inst); + htable_receiver_iterator_next(&it); + + if(!dst_mc_rcv) { + res = htable_receiver_set(&dst->mc_rcvs, &inst, &mc_rcv_null); + if(res != RES_OK) goto error; + dst_mc_rcv = htable_receiver_find(&dst->mc_rcvs, &inst); + } + + if(inst->receiver_mask & (int)SSOL_FRONT) { + res = accum_mc_receivers_1side(&dst_mc_rcv->front, &src_mc_rcv->front); + if(res != RES_OK) goto error; + } + if(inst->receiver_mask & (int)SSOL_BACK) { + res = accum_mc_receivers_1side(&dst_mc_rcv->back, &src_mc_rcv->back); + if(res != RES_OK) goto error; + } } - nrealisations = (int)realisations_count; - nthreads = (int) scn->dev->nthreads; +exit: + mc_receiver_release(&mc_rcv_null); + return res; +error: + goto exit; +} - res = check_scene(scn, FUNC_NAME); - if(res != RES_OK) goto error; +static res_T +update_mc + (const struct point* pt, + const size_t irealisation, + const size_t ibounce, + struct htable_receiver* mc_rcvs, + FILE* output) +{ + struct mc_receiver_1side* mc_rcv1 = NULL; + struct mc_receiver_1side* mc_samp_x_rcv1 = NULL; + res_T res = RES_OK; + ASSERT(pt && mc_rcvs && point_is_receiver(pt)); - /* Create data structures shared by all threads */ - res = scene_create_s3d_views(scn, &view_rt, &view_samp, &view_prim); - if(res != RES_OK) goto error; - res = sun_create_distributions(scn->sun, &ran_sun_dir, &ran_sun_wl); + res = point_dump(pt, irealisation, ibounce, output); if(res != RES_OK) goto error; - /* Create the estimator */ - res = estimator_create(scn->dev, scn, &estimator); - if (res != RES_OK) goto error; - S3D(scene_view_compute_area(view_samp, &area)); - estimator->sampled_area = area; - S3D(scene_view_compute_area(view_prim, &area)); - estimator->primary_area = area; - - /* Create a RNG proxy from the submitted RNG state */ - res = ssp_rng_proxy_create_from_rng - (scn->dev->allocator, rng_state, scn->dev->nthreads, &rng_proxy); + /* Per receiver MC accumulation */ + res = get_mc_receiver_1side(mc_rcvs, pt->inst, pt->side, &mc_rcv1); if(res != RES_OK) goto error; - /* Create per thread data structures */ - #define CREATE(Data) { \ - ASSERT(!(Data)); \ - if(!sa_add((Data), scn->dev->nthreads)) { \ - res = RES_BAD_ARG; \ - goto error; \ - } \ - memset((Data), 0, sizeof((Data)[0])*scn->dev->nthreads); \ + #define ACCUM_WEIGHT(Name, W) { \ + mc_rcv1->Name.weight += (W); \ + mc_rcv1->Name.sqr_weight += (W)*(W); \ } (void)0 - CREATE(rngs); - CREATE(bsdfs); - CREATE(mc_shadows); - CREATE(mc_missings); - CREATE(mc_rcvs); - #undef CREATE - - /* Setup per thread data structures */ - FOR_EACH(i, 0, nthreads) { - res = ssf_bsdf_create(scn->dev->allocator, bsdfs+i); - if(res != RES_OK) goto error; - res = ssp_rng_proxy_create_rng(rng_proxy, (size_t)i, rngs + i); + ACCUM_WEIGHT(integrated_irradiance, pt->weight); + ACCUM_WEIGHT(absorptivity_loss, pt->absorptivity_loss); + ACCUM_WEIGHT(reflectivity_loss, pt->reflectivity_loss); + ACCUM_WEIGHT(cos_loss, pt->cos_loss); + #undef ACCUM_WEIGHT + + /* Per-sampled/receiver MC accumulation */ + res = mc_sampled_get_mc_receiver_1side + (pt->mc_samp, pt->inst, pt->side, &mc_samp_x_rcv1); + if(res != RES_OK) goto error; + #define ACCUM_WEIGHT(Name, W) { \ + mc_samp_x_rcv1->Name.weight += (W); \ + mc_samp_x_rcv1->Name.sqr_weight += (W)*(W); \ + } (void)0 + ACCUM_WEIGHT(integrated_irradiance, pt->weight); + ACCUM_WEIGHT(absorptivity_loss, pt->absorptivity_loss); + ACCUM_WEIGHT(reflectivity_loss, pt->reflectivity_loss); + ACCUM_WEIGHT(cos_loss, pt->cos_loss); + #undef ACCUM_WEIGHT + + /* Per primitive receiver MC accumulation */ + if(pt->inst->receiver_per_primitive) { + struct mc_shape_1side* mc_shape1; + struct mc_primitive_1side* mc_prim1; + + res = mc_receiver_1side_get_mc_shape(mc_rcv1, pt->sshape->shape, &mc_shape1); if(res != RES_OK) goto error; - htable_receiver_init(scn->dev->allocator, mc_rcvs + i); - res = htable_receiver_copy(mc_rcvs + i, &estimator->global_receivers); + + res = mc_shape_1side_get_mc_primitive(mc_shape1, pt->prim.prim_id, &mc_prim1); if(res != RES_OK) goto error; - htable_receiver_begin(mc_rcvs + i, &it); - htable_receiver_end(mc_rcvs + i, &end); - while (!htable_receiver_iterator_eq(&it, &end)) { - struct mc_per_receiver_data* estimator_data = - htable_receiver_iterator_data_get(&it); - *estimator_data = MC_RECV_DATA_NULL; - htable_receiver_iterator_next(&it); - } + + #define ACCUM_WEIGHT(Name, W) { \ + mc_prim1->Name.weight += (W); \ + mc_prim1->Name.sqr_weight += (W)*(W); \ + } (void)0 + ACCUM_WEIGHT(integrated_irradiance, pt->weight); + ACCUM_WEIGHT(absorptivity_loss, pt->absorptivity_loss); + ACCUM_WEIGHT(reflectivity_loss, pt->reflectivity_loss); + ACCUM_WEIGHT(cos_loss, pt->cos_loss); + #undef ACCUM_WEIGHT } - #pragma omp parallel for schedule(static) - for(i = 0; i < nrealisations; ++i) { - struct s3d_hit hit = S3D_HIT_NULL; - struct point pt; - struct ssp_rng* rng; - struct ssf_bsdf* bsdf; - struct mc_data* shadow; - struct mc_data* missing; - struct htable_receiver* receiver; - float org[3], dir[3], range[2] = { 0, FLT_MAX }; - const int ithread = omp_get_thread_num(); - size_t depth = 0; - int is_lit; - int hit_a_receiver = 0; +exit: + return res; +error: + goto exit; +} - if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurs */ +static res_T +trace_radiative_path + (const size_t path_id, /* Unique id of the radiative path */ + const double sampled_area_proxy, /* Overall area of the sampled geometries */ + struct thread_context* thread_ctx, + struct ssol_scene* scn, + struct s3d_scene_view* view_samp, + struct s3d_scene_view* view_rt, + struct ranst_sun_dir* ran_sun_dir, + struct ranst_sun_wl* ran_sun_wl, + const struct ssol_path_tracker* tracker, /* May be NULL */ + FILE* output) /* May be NULL */ +{ + struct ssol_medium medium = SSOL_MEDIUM_VACUUM; + struct path path; + struct s3d_hit hit = S3D_HIT_NULL; + struct point pt; + float org[3], dir[3], range[2] = { 0, FLT_MAX }; + size_t depth = 0; + int is_lit = 0; + res_T res = RES_OK; + ASSERT(thread_ctx && scn && view_samp && view_rt && ran_sun_dir && ran_sun_wl); + + if(tracker) path_init(scn->dev->allocator, &path); + + /* Find a new starting point of the radiative random walk */ + res = point_init(&pt, sampled_area_proxy, scn, &thread_ctx->mc_samps, + view_samp, view_rt, ran_sun_dir, ran_sun_wl, thread_ctx->rng, &is_lit); + if(res != RES_OK) goto error; - /* Fetch per thread data */ - rng = rngs[ithread]; - bsdf = bsdfs[ithread]; - shadow = mc_shadows + ithread; - missing = mc_missings + ithread; - receiver = mc_rcvs + ithread; - - /* Find a new starting point of the radiative random walk */ - is_lit = point_init(&pt, scn, estimator->sampled_area, view_samp, view_rt, - ran_sun_dir, ran_sun_wl, rng); - - if(!is_lit) { /* The starting point is not lit */ - shadow->weight += pt.weight; - shadow->sqr_weight += pt.weight * pt.weight; - continue; + if(scn->atmosphere) { + /* Assume that the path starts from an uniform atmosphere */ + medium.absorptivity = atmosphere_uniform_get_absorption + (scn->atmosphere, pt.wl); + } + + if(tracker) { + /* Add the first point of the starting segment */ + if(tracker->sun_ray_length > 0) { + double pos[3], wi[3]; + d3_minus(wi, pt.dir); + d3_muld(wi, wi, tracker->sun_ray_length); + d3_add(pos, pt.pos, wi); + res = path_add_vertex(&path, pos, scn->sun->dni); + if(res != RES_OK) goto error; } - /* Setup the ray as if it starts from the current point position in order to handle - * the points that start from a virtual material */ + /* Register the init position onto the sampled geometry */ + res = path_add_vertex(&path, pt.pos, pt.weight); + if(res != RES_OK) goto error; + } + + if(!is_lit) { /* The starting point is not lit */ + pt.mc_samp->shadowed.weight += pt.weight; + pt.mc_samp->shadowed.sqr_weight += pt.weight; + thread_ctx->shadowed.weight += pt.weight; + thread_ctx->shadowed.sqr_weight += pt.weight * pt.weight; + if(tracker) path.type = SSOL_PATH_SHADOW; + } else { + int hit_a_receiver = 0; + + /* Setup the ray as if it starts from the current point position in order + * to handle the points that start from a virtual material */ f3_set_d3(org, pt.pos); f3_set_d3(dir, pt.dir); hit.distance = 0; @@ -477,27 +766,13 @@ ssol_solve struct ssol_material* mtl; if(point_is_receiver(&pt)) { - const res_T res_local = point_dump(&pt, (size_t)i, depth, output); - struct mc_per_receiver_1side_data* data = NULL; - if(res_local != RES_OK) { - ATOMIC_SET(&res, res_local); - break; - } - data = estimator_get_receiver_data(receiver, pt.inst, pt.side); - ASSERT(data); - data->irradiance.weight += pt.weight; - data->irradiance.sqr_weight += pt.weight*pt.weight; - data->absorptivity_loss.weight += pt.absorptivity_loss; - data->absorptivity_loss.sqr_weight += pt.absorptivity_loss * pt.absorptivity_loss; - data->reflectivity_loss.weight += pt.reflectivity_loss; - data->reflectivity_loss.sqr_weight += pt.reflectivity_loss * pt.reflectivity_loss; - data->cos_loss.weight += pt.cos_loss; - data->cos_loss.sqr_weight += pt.cos_loss * pt.cos_loss; hit_a_receiver = 1; + res = update_mc(&pt, path_id, depth, &thread_ctx->mc_rcvs, output); + if(res != RES_OK) goto error; } mtl = point_get_material(&pt); - if(mtl->type == MATERIAL_VIRTUAL) { + if(mtl->type == SSOL_MATERIAL_VIRTUAL) { /* Note that for Virtual materials, the ray parameters 'org' & 'dir' * are not updated to ensure that it pursues its traversal without any * accuracy issue */ @@ -506,12 +781,11 @@ ssol_solve } else { /* Modulate the point weight wrt to its scattering functions and * generate an outgoing direction */ - res_T res_local = point_shade(&pt, bsdf, rng, pt.dir); - if(res_local != RES_OK) { - ATOMIC_SET(&res, res_local); - break; - } - if (pt.weight == 0) break; + res = point_shade(&pt, thread_ctx->bsdf, &medium, thread_ctx->rng, pt.dir); + if(res != RES_OK) goto error; + + /* Stop the radiative random walk */ + if(pt.weight == 0) break; /* Setup new ray parameters */ f2(range, 0, FLT_MAX); @@ -529,14 +803,23 @@ ssol_solve ray_data.range_min = range[0]; ray_data.dst = FLT_MAX; S3D(scene_view_trace_ray(view_rt, org, dir, range, &ray_data, &hit)); - if(S3D_HIT_NONE(&hit)) break; + if(S3D_HIT_NONE(&hit)) { + /* Add the point of the last path segment going to the infinite */ + if(tracker && tracker->infinite_ray_length > 0) { + double pos[3], wi[3]; + d3_set_f3(wi, dir); + d3_add(pos, pt.pos, d3_muld(wi, wi, tracker->infinite_ray_length)); + res = path_add_vertex(&path, pos, pt.weight); + if(res != RES_OK) goto error; + } + break; + } - depth += mtl->type != MATERIAL_VIRTUAL; + depth += mtl->type != SSOL_MATERIAL_VIRTUAL; - /* Take into account the atmosphere attenuation along the new ray */ - if(scn->atmosphere) { - double transmissivity = - compute_atmosphere_transmissivity(scn->atmosphere, hit.distance, pt.wl); + /* Take into account the medium attenuation */ + if(medium.absorptivity > 0 && hit.distance > 0) { + const double transmissivity = exp(-medium.absorptivity * hit.distance); ASSERT(0 < transmissivity && transmissivity <= 1); pt.absorptivity_loss += (1 - transmissivity) * pt.weight; pt.weight *= transmissivity; @@ -544,70 +827,227 @@ ssol_solve /* Update the point */ point_update_from_hit(&pt, scn, org, dir, &hit, &ray_data); - } + if(tracker) { + res = path_add_vertex(&path, pt.pos, pt.weight); + if(res != RES_OK) goto error; + } + } if(!hit_a_receiver) { - missing->weight += pt.weight; - missing->sqr_weight += pt.weight*pt.weight; + thread_ctx->missing.weight += pt.weight; + thread_ctx->missing.sqr_weight += pt.weight*pt.weight; + } + if(tracker) { + path.type = hit_a_receiver ? SSOL_PATH_SUCCESS : SSOL_PATH_MISSING; } } - /* Merge per thread estimations */ + if(tracker) { + res = path_register_and_clear(&thread_ctx->paths, &path); + if(res != RES_OK) goto error; + } + +exit: + if(tracker) path_release(&path); + return res; +error: + goto exit; +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +ssol_solve + (struct ssol_scene* scn, + struct ssp_rng* rng_state, + const size_t realisations_count, + const struct ssol_path_tracker* path_tracker, + FILE* output, + struct ssol_estimator** out_estimator) +{ + struct htable_receiver_iterator r_it, r_end; + struct htable_sampled_iterator s_it, s_end; + struct s3d_scene_view* view_rt = NULL; + struct s3d_scene_view* view_samp = NULL; + struct ranst_sun_dir* ran_sun_dir = NULL; + struct ranst_sun_wl* ran_sun_wl = NULL; + struct darray_thread_ctx thread_ctxs; + struct ssol_estimator* estimator = NULL; + struct ssol_path_tracker tracker; + struct ssp_rng_proxy* rng_proxy = NULL; + double sampled_area; + double sampled_area_proxy; + int nthreads = 0; + int64_t nrealisations = 0; + int i = 0; + ATOMIC res = RES_OK; + ASSERT(nrealisations <= INT_MAX); + + if(!scn || !rng_state || !realisations_count || !out_estimator) + return RES_BAD_ARG; + + darray_thread_ctx_init(scn->dev->allocator, &thread_ctxs); + + /* CL compiler supports OpenMP parallel loop whose indices are signed. The + * following line ensures that the unsigned number of realisations does not + * overflow the realisation index. */ + if(realisations_count > INT64_MAX) { + res = RES_BAD_ARG; + goto error; + } + nrealisations = (int)realisations_count; + nthreads = (int) scn->dev->nthreads; + + res = check_scene(scn, FUNC_NAME); + if(res != RES_OK) goto error; + + /* Create data structures shared by all threads */ + res = scene_create_s3d_views(scn, &view_rt, &view_samp, &sampled_area, + &sampled_area_proxy); + if(res != RES_OK) goto error; + res = sun_create_distributions(scn->sun, &ran_sun_dir, &ran_sun_wl); + if(res != RES_OK) goto error; + + /* Create a RNG proxy from the submitted RNG state */ + res = ssp_rng_proxy_create_from_rng + (scn->dev->allocator, rng_state, scn->dev->nthreads, &rng_proxy); + if(res != RES_OK) goto error; + + /* Create the estimator */ + res = estimator_create(scn->dev, scn, &estimator); + if (res != RES_OK) goto error; + + /* Create per thread data structures */ + res = darray_thread_ctx_resize(&thread_ctxs, scn->dev->nthreads); + if(res != RES_OK) goto error; FOR_EACH(i, 0, nthreads) { - estimator->shadow.weight += mc_shadows[i].weight; - estimator->shadow.sqr_weight += mc_shadows[i].sqr_weight; - estimator->missing.weight += mc_missings[i].weight; - estimator->missing.sqr_weight += mc_missings[i].sqr_weight; + struct thread_context* ctx = darray_thread_ctx_data_get(&thread_ctxs)+i; + res = thread_context_setup(ctx, rng_proxy, (size_t)i); + if(res != RES_OK) goto error; } - /* Merge per thread estimations */ - htable_receiver_begin(&estimator->global_receivers, &it); - htable_receiver_end(&estimator->global_receivers, &end); - while (!htable_receiver_iterator_eq(&it, &end)) { - struct mc_per_receiver_data* estimator_data - = htable_receiver_iterator_data_get(&it); - const struct ssol_instance* key = *htable_receiver_iterator_key_get(&it); - htable_receiver_iterator_next(&it); + /* Setup the path tracker */ + if(path_tracker) { + tracker = *path_tracker; + if(tracker.sun_ray_length < 0 || tracker.infinite_ray_length < 0) { + const double extend = compute_infinite_path_segment_extend(view_rt); + if(tracker.sun_ray_length < 0) tracker.sun_ray_length = extend; + if(tracker.infinite_ray_length < 0) tracker.infinite_ray_length = extend; + } + path_tracker = &tracker; + } + + /* Launch the parallel MC estimation */ + #pragma omp parallel for schedule(static) + for(i = 0; i < nrealisations; ++i) { + struct thread_context* thread_ctx; + const int ithread = omp_get_thread_num(); + res_T res_local; + + if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurs */ + + /* Fetch per thread data */ + thread_ctx = darray_thread_ctx_data_get(&thread_ctxs) + ithread; + + /* Execute a MC experiment */ + res_local = trace_radiative_path((size_t)i, sampled_area_proxy, thread_ctx, + scn, view_samp, view_rt, ran_sun_dir, ran_sun_wl, path_tracker, output); + if(res_local != RES_OK) { + ATOMIC_SET(&res, res_local); + continue; + } + } + + /* Merge per thread global MC estimations */ + FOR_EACH(i, 0, nthreads) { + const struct thread_context* thread_ctx; + thread_ctx = darray_thread_ctx_cdata_get(&thread_ctxs)+i; + #define ACCUM_WEIGHT(Name) { \ + estimator->Name.weight += thread_ctx->Name.weight; \ + estimator->Name.sqr_weight += thread_ctx->Name.sqr_weight; \ + } (void)0 + ACCUM_WEIGHT(shadowed); + ACCUM_WEIGHT(missing); + ACCUM_WEIGHT(cos_loss); + #undef ACCUM_WEIGHT + } + + /* Merge per thread receiver MC estimations */ + htable_receiver_begin(&estimator->mc_receivers, &r_it); + htable_receiver_end(&estimator->mc_receivers, &r_end); + while(!htable_receiver_iterator_eq(&r_it, &r_end)) { + struct mc_receiver* mc_rcv = htable_receiver_iterator_data_get(&r_it); + const struct ssol_instance* inst = *htable_receiver_iterator_key_get(&r_it); + htable_receiver_iterator_next(&r_it); + FOR_EACH(i, 0, nthreads) { - /* Sum both sides, even if no receiver is defined to avoid tests */ - struct mc_per_receiver_data* thread_data - = htable_receiver_find(mc_rcvs + i, &key); - #define ACCUM_WEIGHT(Name) { \ - estimator_data->front.Name.weight += thread_data->front.Name.weight; \ - estimator_data->back.Name.weight += thread_data->back.Name.weight; \ - estimator_data->front.Name.sqr_weight += thread_data->front.Name.sqr_weight;\ - estimator_data->back.Name.sqr_weight += thread_data->back.Name.sqr_weight;\ - } (void)0 - ACCUM_WEIGHT(irradiance); - ACCUM_WEIGHT(absorptivity_loss); - ACCUM_WEIGHT(reflectivity_loss); - ACCUM_WEIGHT(cos_loss); - #undef ACCUM_WEIGHT + struct thread_context* thread_ctx; + struct mc_receiver* mc_rcv_thread; + + thread_ctx = darray_thread_ctx_data_get(&thread_ctxs) + i; + mc_rcv_thread = htable_receiver_find(&thread_ctx->mc_rcvs, &inst); + if(!mc_rcv_thread) continue; /* Receiver was not visited in this thread */ + + if(inst->receiver_mask & (int)SSOL_FRONT) { + res = accum_mc_receivers_1side(&mc_rcv->front, &mc_rcv_thread->front); + if(res != RES_OK) goto error; + } + if(inst->receiver_mask & (int)SSOL_BACK) { + res = accum_mc_receivers_1side(&mc_rcv->back, &mc_rcv_thread->back); + if(res != RES_OK) goto error; + } + } + } + + /* Merge per thread sampled instance MC estimations */ + htable_sampled_begin(&estimator->mc_sampled, &s_it); + htable_sampled_end(&estimator->mc_sampled, &s_end); + while(!htable_sampled_iterator_eq(&s_it, &s_end)) { + struct mc_sampled* mc_samp = htable_sampled_iterator_data_get(&s_it); + const struct ssol_instance* inst = *htable_sampled_iterator_key_get(&s_it); + htable_sampled_iterator_next(&s_it); + + FOR_EACH(i, 0, nthreads) { + struct thread_context* thread_ctx; + struct mc_sampled* mc_samp_thread; + + thread_ctx = darray_thread_ctx_data_get(&thread_ctxs) + i; + mc_samp_thread = htable_sampled_find(&thread_ctx->mc_samps, &inst); + if(!mc_samp_thread) continue; /* Instance was not sampled in this thread */ + + res = accum_mc_sampled(mc_samp, mc_samp_thread); + if(res != RES_OK) goto error; } } - estimator->realisation_count += realisations_count; + + /* Merge per thread tracked paths */ + if(path_tracker) { + FOR_EACH(i, 0, nthreads) { + struct thread_context* thread_ctx; + size_t ipath, npaths; + + thread_ctx = darray_thread_ctx_data_get(&thread_ctxs) + i; + npaths = darray_path_size_get(&thread_ctx->paths); + FOR_EACH(ipath, 0, npaths) { + struct path* path; + path = darray_path_data_get(&thread_ctx->paths) + ipath; + res = path_register_and_clear(&estimator->paths, path); + if(res != RES_OK) goto error; + } + } + } + + estimator->realisation_count = realisations_count; + estimator->sampled_area = sampled_area; exit: + darray_thread_ctx_release(&thread_ctxs); if(view_rt) S3D(scene_view_ref_put(view_rt)); if(view_samp) S3D(scene_view_ref_put(view_samp)); - if(view_prim) S3D(scene_view_ref_put(view_prim)); if(ran_sun_dir) ranst_sun_dir_ref_put(ran_sun_dir); if(ran_sun_wl) ranst_sun_wl_ref_put(ran_sun_wl); if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); - if(bsdfs) { - FOR_EACH(i, 0, nthreads) if(bsdfs[i]) SSF(bsdf_ref_put(bsdfs[i])); - sa_release(bsdfs); - } - if(rngs) { - FOR_EACH(i, 0, nthreads) if(rngs[i]) SSP(rng_ref_put(rngs[i])); - sa_release(rngs); - } - if(mc_rcvs) { - FOR_EACH(i, 0, nthreads) htable_receiver_release(mc_rcvs + i); - sa_release(mc_rcvs); - } - sa_release(mc_shadows); - sa_release(mc_missings); if(out_estimator) *out_estimator = estimator; return (res_T)res; error: diff --git a/src/test_ssol_atmosphere.c b/src/test_ssol_atmosphere.c @@ -16,12 +16,9 @@ #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; @@ -31,13 +28,8 @@ main(int argc, char** 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); + (NULL, &allocator, SSOL_NTHREADS_DEFAULT, 0, &dev), RES_OK); CHECK(ssol_spectrum_create(dev, &spectrum), RES_OK); CHECK(ssol_spectrum_create(dev, &spectrum2), RES_OK); @@ -64,8 +56,6 @@ main(int argc, char** argv) 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); diff --git a/src/test_ssol_by_receiver_integration.c b/src/test_ssol_by_receiver_integration.c @@ -27,7 +27,6 @@ #define HALF_Y 10 #include "test_ssol_rect2D_geometry.h" -#include <rsys/logger.h> #include <rsys/double33.h> #include <star/s3d.h> @@ -47,7 +46,6 @@ get_wlen(const size_t i, double* wlen, double* data, void* ctx) int main(int argc, char** argv) { - struct logger logger; struct mem_allocator allocator; struct ssol_device* dev; struct ssp_rng* rng; @@ -68,7 +66,7 @@ main(int argc, char** argv) struct ssol_sun* sun; struct ssol_spectrum* spectrum; struct ssol_estimator *estimator1, *estimator2; - struct ssol_estimator_status status; + struct ssol_mc_receiver mc_rcv; double dir[3]; double transform[12]; /* 3x4 column major matrix */ @@ -80,13 +78,8 @@ main(int argc, char** 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); + (NULL, &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); @@ -121,7 +114,7 @@ main(int argc, char** argv) 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_mirror_setup(m_mtl, &shader), RES_OK); CHECK(ssol_material_create_virtual(dev, &v_mtl), RES_OK); CHECK(ssol_object_create(dev, &m_object), RES_OK); @@ -133,33 +126,35 @@ main(int argc, char** argv) 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_set_receiver(target, SSOL_FRONT, 0), RES_OK); CHECK(ssol_instance_sample(target, 0), RES_OK); CHECK(ssol_scene_attach_instance(scene, target), RES_OK); #define N__ 10000 #define S_DNI_cos (4 * 1000 * cos(PI / 4)) -#define GET_RCV_STATUS ssol_estimator_get_receiver_status - CHECK(ssol_solve(scene, rng, N__, NULL, &estimator1), RES_OK); - CHECK(GET_RCV_STATUS(estimator1, target, SSOL_FRONT, &status), RES_OK); - logger_print(&logger, LOG_OUTPUT, "Ir(target) = %g +/- %g", - status.irradiance.E, status.irradiance.SE); - CHECK(ssol_instance_set_receiver(heliostat, SSOL_FRONT), RES_OK); - CHECK(eq_eps(status.irradiance.E, S_DNI_cos, S_DNI_cos * 2e-1), 1); - CHECK(ssol_solve(scene, rng, 8 * N__, NULL, &estimator2), RES_OK); - CHECK(GET_RCV_STATUS(estimator2, 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, S_DNI_cos, S_DNI_cos * 5e-2), 1); +#define GET_MC_RCV ssol_estimator_get_mc_receiver +#define GET_MC_SAMP_X_RCV ssol_estimator_get_mc_sampled_x_receiver + CHECK(ssol_solve(scene, rng, N__, 0, NULL, &estimator1), RES_OK); + CHECK(GET_MC_RCV(estimator1, target, SSOL_FRONT, &mc_rcv), RES_OK); + printf("Ir(target) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(ssol_instance_set_receiver(heliostat, SSOL_FRONT, 0), RES_OK); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, S_DNI_cos, S_DNI_cos * 2e-1), 1); + CHECK(ssol_solve(scene, rng, 8 * N__, 0, NULL, &estimator2), RES_OK); + CHECK(GET_MC_RCV(estimator2, target, SSOL_FRONT, &mc_rcv), RES_OK); + printf("Ir(target) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, S_DNI_cos, S_DNI_cos * 5e-2), 1); CHECK(ssol_estimator_ref_put(estimator1), RES_OK); - CHECK(ssol_solve(scene, rng, 3 * N__, NULL, &estimator1), RES_OK); - CHECK(GET_RCV_STATUS(estimator1, 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, S_DNI_cos, S_DNI_cos * 1e-1), 1); -#undef N__ -#undef S_DNI_cos -#undef GET_RCV_STATUS + CHECK(ssol_solve(scene, rng, 3 * N__, 0, NULL, &estimator1), RES_OK); + CHECK(GET_MC_RCV(estimator1, target, SSOL_FRONT, &mc_rcv), RES_OK); + printf("Ir(target) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, S_DNI_cos, S_DNI_cos * 1e-1), 1); + CHECK(GET_MC_SAMP_X_RCV(estimator1, heliostat, target, SSOL_FRONT, &mc_rcv), RES_OK); + printf("Ir(heliostat=>target) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, S_DNI_cos, S_DNI_cos * 1e-1), 1); /* Free data */ CHECK(ssol_instance_ref_put(heliostat), RES_OK); @@ -178,8 +173,6 @@ main(int argc, char** argv) 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); diff --git a/src/test_ssol_circ2D_geometry.h b/src/test_ssol_circ2D_geometry.h @@ -0,0 +1,54 @@ +/* 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 "test_ssol_geometries.h" +#include <rsys/math.h> + + +/******************************************************************************* +* Circle polygon +******************************************************************************/ +#if !defined(RADIUS) +#error "Missing the RADIUS macro defining the circle radius" +#endif +#if !defined(NVERTS) +#define NVERTS 36 +#endif +#if !defined(CIRCLE_NAME) +#error "Missing the CIRCLE_NAME macro defining the circle name" +#endif + +#define EDGES__ CONCAT(CIRCLE_NAME, _EDGES__) +#define INIT_FUNC__ CONCAT(init_, CIRCLE_NAME) + +/* should be const but scpr expects non-const data */ +static double EDGES__ [2*NVERTS]; + +static void INIT_FUNC__() { + int n; + /* radius that give the same area than a circle */ + double r = sqrt(2 * RADIUS * RADIUS * PI / (sin(2 * PI / NVERTS) * NVERTS)); + for (n = 0; n < NVERTS; n++) { + EDGES__[2 * n] = r * cos((double)-n * 2 * PI / (double)NVERTS); + EDGES__[2 * n + 1] = r * sin((double)-n * 2 * PI / (double) NVERTS); + } +} + +#undef EDGES__ +#undef INIT_FUNC__ + +#undef RADIUS +#undef NVERTS +#undef CIRCLE_NAME diff --git a/src/test_ssol_cube_geometry.h b/src/test_ssol_cube_geometry.h @@ -0,0 +1,107 @@ +/* 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 "test_ssol_geometries.h" + +/******************************************************************************* +* Rectangle polygon +******************************************************************************/ +#if !defined(HALF_X) && !(defined(X_MIN) && defined(X_MAX)) +#error "Missing the HALF_X or X_MIN and X_MAX macros defining the cube size" +#endif +#if !defined(HALF_Y) && !(defined(Y_MIN) && defined(Y_MAX)) +#error "Missing the HALF_Y or Y_MIN and Y_MAX macros defining the cube size" +#endif +#if !defined(HALF_Z) && !(defined(Z_MIN) && defined(Z_MAX)) +#error "Missing the HALF_Z or Z_MIN and Z_MAX macros defining the cube size" +#endif +#if !defined(CUBE_NAME) +#error "Missing the CUBE_NAME macro defining the rectangle name" +#endif + +#define EDGES__ CONCAT(CUBE_NAME, _EDGES__) +#define CUBE_NVERTS__ CONCAT(CUBE_NAME, _NVERTS__) + +#if !defined(X_MIN) +#define X_MIN (float)(-(HALF_X)) +#endif + +#if !defined(X_MAX) +#define X_MAX (float)(HALF_X) +#endif + +#if !defined(Y_MIN) +#define Y_MIN (float)(-(HALF_Y)) +#endif + +#if !defined(Y_MAX) +#define Y_MAX (float)(HALF_Y) +#endif + +#if !defined(Z_MIN) +#define Z_MIN (float)(-(HALF_Z)) +#endif + +#if !defined(Z_MAX) +#define Z_MAX (float)(HALF_Z) +#endif + +static const float EDGES__ [] = { + X_MIN, Y_MIN, Z_MIN, + X_MIN, Y_MIN, Z_MAX, + X_MIN, Y_MAX, Z_MIN, + X_MIN, Y_MAX, Z_MAX, + X_MAX, Y_MIN, Z_MIN, + X_MAX, Y_MIN, Z_MAX, + X_MAX, Y_MAX, Z_MIN, + X_MAX, Y_MAX, Z_MAX +}; + +const unsigned CUBE_NVERTS__ = sizeof(EDGES__) / sizeof(float[3]); + +const unsigned TRG_IDS__ [] = { + 0, 6, 4, + 0, 2, 6, + 0, 3, 2, + 0, 1, 3, + 2, 7, 6, + 2, 3, 7, + 4, 6, 7, + 4, 7, 5, + 0, 4, 5, + 0, 5, 1, + 1, 5, 7, + 1, 7, 3 +}; +const unsigned CUBE_NTRIS__ = sizeof(TRG_IDS__) / sizeof(unsigned[3]); + +static const struct desc CUBE_DESC__ = { EDGES__, TRG_IDS__ }; + +#undef EDGES__ +#undef TRG_IDS__ +#undef CUBE_DESC__ +#undef CUBE_NVERTS__ +#undef CUBE_NTRIS__ + +#undef HALF_X +#undef HALF_Y +#undef HALF_Z +#undef X_MIN +#undef X_MAX +#undef Y_MIN +#undef Y_MAX +#undef Z_MIN +#undef Z_MAX +#undef CUBE_NAME diff --git a/src/test_ssol_device.c b/src/test_ssol_device.c @@ -18,6 +18,14 @@ #include <rsys/logger.h> +static INLINE void +log_stream(const char* msg, void* ctx) +{ + ASSERT(msg); + (void) msg, (void) ctx; + printf("%s\n", msg); +} + int main(int argc, char** argv) { diff --git a/src/test_ssol_draw.c b/src/test_ssol_draw.c @@ -18,11 +18,15 @@ #include "test_ssol_geometries.h" #include "test_ssol_materials.h" +#include <rsys/double3.h> +#include <rsys/float3.h> #include <rsys/image.h> #include <rsys/math.h> -#define WIDTH 800 -#define HEIGHT 600 +#include <string.h> + +#define WIDTH 256 +#define HEIGHT 256 #define PITCH (WIDTH*sizeof(unsigned char[3])) #define PROJ_RATIO ((double)WIDTH/(double)HEIGHT) @@ -113,15 +117,16 @@ setup_cornell_box(struct ssol_device* dev, struct ssol_scene* scn) struct ssol_object* obj; struct ssol_instance* inst; struct ssol_material* mtl; - struct ssol_mirror_shader shader = SSOL_MIRROR_SHADER_NULL; + struct ssol_matte_shader shader = SSOL_MATTE_SHADER_NULL; struct ssol_vertex_data vdata; struct desc desc; + float lower[3], upper[3]; + float tmp[3]; shader.normal = get_shader_normal; shader.reflectivity = get_shader_reflectivity; - shader.roughness = get_shader_roughness; - CHECK(ssol_material_create_mirror(dev, &mtl), RES_OK); - CHECK(ssol_mirror_set_shader(mtl, &shader), RES_OK); + CHECK(ssol_material_create_matte(dev, &mtl), RES_OK); + CHECK(ssol_matte_setup(mtl, &shader), RES_OK); vdata.usage = SSOL_POSITION; vdata.get = get_position; @@ -164,6 +169,10 @@ setup_cornell_box(struct ssol_device* dev, struct ssol_scene* scn) CHECK(ssol_object_ref_put(obj), RES_OK); CHECK(ssol_material_ref_put(mtl), RES_OK); + + CHECK(ssol_scene_compute_aabb(scn, lower, upper), RES_OK); + CHECK(f3_eq_eps(lower, f3(tmp, 0, 0, 0), 1.e-6f), 1); + CHECK(f3_eq_eps(upper, f3(tmp, 552.f, 559.f, 548.f), 1.e-6f), 1); } int @@ -173,12 +182,36 @@ main(int argc, char** argv) struct ssol_device* dev; struct ssol_camera* cam; struct ssol_scene* scn; + struct ssol_sun* sun; unsigned char* pixels = NULL; const double pos[3] = {278.0, -1000.0, 273.0}; const double tgt[3] = {278.0, 0.0, 273.0}; const double up[3] = {0.0, 0.0, 1.0}; + double dir[3]; + res_T (*draw_func) + (struct ssol_scene* scn, + struct ssol_camera* cam, + const size_t width, + const size_t height, + const size_t spp, + ssol_write_pixels_T writer, + void* data); (void)argc, (void)argv; + if(argc <= 1) { + fprintf(stderr, "Usage: %s <draft|pt>\n", argv[0]); + return -1; + } + + if(!strcmp(argv[1], "draft")) { + draw_func = ssol_draw_draft; + } else if(!strcmp(argv[1], "pt")) { + draw_func = ssol_draw_pt; + } else { + fprintf(stderr, "Usage: %s <draft|pt>\n", argv[0]); + return -1; + } + CHECK(mem_init_proxy_allocator(&allocator, &mem_default_allocator), RES_OK); CHECK(ssol_device_create @@ -193,41 +226,80 @@ main(int argc, char** argv) CHECK(ssol_camera_set_fov(cam, PI/4.0), RES_OK); CHECK(ssol_camera_look_at(cam, pos, tgt, up), RES_OK); + d3(dir, 1, 1, -1); + d3_normalize(dir, dir); + CHECK(ssol_sun_create_directional(dev, &sun), RES_OK); + CHECK(ssol_sun_set_direction(sun, dir), RES_OK); + CHECK(ssol_sun_set_dni(sun, 1000), RES_OK); + CHECK(ssol_scene_attach_sun(scn, sun), RES_OK); + pixels = MEM_CALLOC(&allocator, HEIGHT, PITCH); NCHECK(pixels, NULL); - CHECK(ssol_draw(NULL, NULL, 0, 0, NULL, pixels), RES_BAD_ARG); - CHECK(ssol_draw(scn, NULL, 0, 0, NULL, pixels), RES_BAD_ARG); - CHECK(ssol_draw(NULL, cam, 0, 0, NULL, pixels), RES_BAD_ARG); - CHECK(ssol_draw(scn, cam, 0, 0, NULL, pixels), RES_BAD_ARG); - CHECK(ssol_draw(NULL, NULL, WIDTH, 0, NULL, pixels), RES_BAD_ARG); - CHECK(ssol_draw(scn, NULL, WIDTH, 0, NULL, pixels), RES_BAD_ARG); - CHECK(ssol_draw(NULL, cam, WIDTH, 0, NULL, pixels), RES_BAD_ARG); - CHECK(ssol_draw(scn, cam, WIDTH, 0, NULL, pixels), RES_BAD_ARG); - CHECK(ssol_draw(NULL, NULL, 0, HEIGHT, NULL, pixels), RES_BAD_ARG); - CHECK(ssol_draw(scn, NULL, 0, HEIGHT, NULL, pixels), RES_BAD_ARG); - CHECK(ssol_draw(NULL, cam, 0, HEIGHT, NULL, pixels), RES_BAD_ARG); - CHECK(ssol_draw(scn, cam, 0, HEIGHT, NULL, pixels), RES_BAD_ARG); - CHECK(ssol_draw(NULL, NULL, WIDTH, HEIGHT, NULL, pixels), RES_BAD_ARG); - CHECK(ssol_draw(scn, NULL, WIDTH, HEIGHT, NULL, pixels), RES_BAD_ARG); - CHECK(ssol_draw(NULL, cam, WIDTH, WIDTH, NULL, pixels), RES_BAD_ARG); - CHECK(ssol_draw(scn, cam, WIDTH, HEIGHT, NULL, pixels), RES_BAD_ARG); - CHECK(ssol_draw(NULL, NULL, 0, 0, write_RGB8, pixels), RES_BAD_ARG); - CHECK(ssol_draw(scn, NULL, 0, 0, write_RGB8, pixels), RES_BAD_ARG); - CHECK(ssol_draw(NULL, cam, 0, 0, write_RGB8, pixels), RES_BAD_ARG); - CHECK(ssol_draw(scn, cam, 0, 0, write_RGB8, pixels), RES_BAD_ARG); - CHECK(ssol_draw(NULL, NULL, WIDTH, 0, write_RGB8, pixels), RES_BAD_ARG); - CHECK(ssol_draw(scn, NULL, WIDTH, 0, write_RGB8, pixels), RES_BAD_ARG); - CHECK(ssol_draw(NULL, cam, WIDTH, 0, write_RGB8, pixels), RES_BAD_ARG); - CHECK(ssol_draw(scn, cam, WIDTH, 0, write_RGB8, pixels), RES_BAD_ARG); - CHECK(ssol_draw(NULL, NULL, 0, HEIGHT, write_RGB8, pixels), RES_BAD_ARG); - CHECK(ssol_draw(scn, NULL, 0, HEIGHT, write_RGB8, pixels), RES_BAD_ARG); - CHECK(ssol_draw(NULL, cam, 0, HEIGHT, write_RGB8, pixels), RES_BAD_ARG); - CHECK(ssol_draw(scn, cam, 0, HEIGHT, write_RGB8, pixels), RES_BAD_ARG); - CHECK(ssol_draw(NULL, NULL, WIDTH, HEIGHT, write_RGB8, pixels), RES_BAD_ARG); - CHECK(ssol_draw(scn, NULL, WIDTH, HEIGHT, write_RGB8, pixels), RES_BAD_ARG); - CHECK(ssol_draw(NULL, cam, WIDTH, WIDTH, write_RGB8, pixels), RES_BAD_ARG); - CHECK(ssol_draw(scn, cam, WIDTH, HEIGHT, write_RGB8, pixels), RES_OK); + CHECK(draw_func(NULL, NULL, 0, 0, 0, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, NULL, 0, 0, 0, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, cam, 0, 0, 0, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, cam, 0, 0, 0, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, NULL, WIDTH, 0, 0, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, NULL, WIDTH, 0, 0, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, cam, WIDTH, 0, 0, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, cam, WIDTH, 0, 0, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, NULL, 0, HEIGHT, 0, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, NULL, 0, HEIGHT, 0, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, cam, 0, HEIGHT, 0, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, cam, 0, HEIGHT, 0, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, NULL, WIDTH, HEIGHT, 0, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, NULL, WIDTH, HEIGHT, 0, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, cam, WIDTH, WIDTH, 0, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, cam, WIDTH, HEIGHT, 0, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, NULL, 0, 0, 0, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, NULL, 0, 0, 0, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, cam, 0, 0, 0, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, cam, 0, 0, 0, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, NULL, WIDTH, 0, 0, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, NULL, WIDTH, 0, 0, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, cam, WIDTH, 0, 0, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, cam, WIDTH, 0, 0, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, NULL, 0, HEIGHT, 0, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, NULL, 0, HEIGHT, 0, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, cam, 0, HEIGHT, 0, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, cam, 0, HEIGHT, 0, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, NULL, WIDTH, HEIGHT, 0, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, NULL, WIDTH, HEIGHT, 0, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, cam, WIDTH, WIDTH, 0, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, cam, WIDTH, HEIGHT, 0, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, NULL, 0, 0, 4, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, NULL, 0, 0, 4, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, cam, 0, 0, 4, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, cam, 0, 0, 4, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, NULL, WIDTH, 0, 4, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, NULL, WIDTH, 0, 4, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, cam, WIDTH, 0, 4, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, cam, WIDTH, 0, 4, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, NULL, 0, HEIGHT, 4, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, NULL, 0, HEIGHT, 4, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, cam, 0, HEIGHT, 4, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, cam, 0, HEIGHT, 4, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, NULL, WIDTH, HEIGHT, 4, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, NULL, WIDTH, HEIGHT, 4, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, cam, WIDTH, WIDTH, 4, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, cam, WIDTH, HEIGHT, 4, NULL, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, NULL, 0, 0, 4, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, NULL, 0, 0, 4, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, cam, 0, 0, 4, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, cam, 0, 0, 4, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, NULL, WIDTH, 0, 4, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, NULL, WIDTH, 0, 4, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, cam, WIDTH, 0, 4, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, cam, WIDTH, 0, 4, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, NULL, 0, HEIGHT, 4, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, NULL, 0, HEIGHT, 4, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, cam, 0, HEIGHT, 4, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, cam, 0, HEIGHT, 4, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, NULL, WIDTH, HEIGHT, 4, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, NULL, WIDTH, HEIGHT, 4, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(NULL, cam, WIDTH, WIDTH, 4, write_RGB8, pixels), RES_BAD_ARG); + CHECK(draw_func(scn, cam, WIDTH, HEIGHT, 4, write_RGB8, pixels), RES_OK); CHECK(image_ppm_write_stream(stdout, WIDTH, HEIGHT, 3, pixels), RES_OK); @@ -235,6 +307,7 @@ main(int argc, char** argv) CHECK(ssol_device_ref_put(dev), RES_OK); CHECK(ssol_camera_ref_put(cam), RES_OK); CHECK(ssol_scene_ref_put(scn), RES_OK); + CHECK(ssol_sun_ref_put(sun), RES_OK); check_memory_allocator(&allocator); mem_shutdown_proxy_allocator(&allocator); diff --git a/src/test_ssol_image.c b/src/test_ssol_image.c @@ -16,13 +16,10 @@ #include "ssol.h" #include "test_ssol_utils.h" -#include <rsys/logger.h> - int main(int argc, char** argv) { double block[8/*#rows*/*3/*#channels*/*8/*#column*/]; - struct logger logger; struct mem_allocator allocator; struct ssol_device* dev; struct ssol_image* img; @@ -35,13 +32,8 @@ main(int argc, char** 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); + (NULL, &allocator, SSOL_NTHREADS_DEFAULT, 0, &dev), RES_OK); CHECK(ssol_image_create(dev, &img), RES_OK); @@ -139,11 +131,8 @@ main(int argc, char** argv) } CHECK(ssol_image_ref_put(img), 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); diff --git a/src/test_ssol_instance.c b/src/test_ssol_instance.c @@ -15,13 +15,16 @@ #include "ssol.h" #include "test_ssol_utils.h" +#include <rsys/double33.h> -#include <rsys/logger.h> +#define PLANE_NAME SQUARE +#define HALF_X 1 +#define HALF_Y 1 +#include "test_ssol_rect_geometry.h" int main(int argc, char** argv) { - struct logger logger; struct mem_allocator allocator; struct ssol_device* dev; struct ssol_shape* shape; @@ -29,23 +32,28 @@ main(int argc, char** argv) struct ssol_object* object; struct ssol_instance* instance; struct ssol_instance* instance1; - double transform[12]; + struct ssol_vertex_data attrib = SSOL_VERTEX_DATA_NULL; + struct ssol_instantiated_shaded_shape sshape; + double transform[12] = {1, 0, 0, 0, 1, 0, 0, 0, 1, 10, 0, 0}; + double val[3]; + size_t n; + unsigned i, count; uint32_t id, id1; (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); + (NULL, &allocator, SSOL_NTHREADS_DEFAULT, 0, &dev), RES_OK); CHECK(ssol_material_create_virtual(dev, &material), RES_OK); - CHECK(ssol_shape_create_punched_surface(dev, &shape), RES_OK); + attrib.usage = SSOL_POSITION; + attrib.get = get_position; + CHECK(ssol_shape_create_mesh(dev, &shape), RES_OK); + CHECK(ssol_mesh_setup(shape, SQUARE_NTRIS__, get_ids, SQUARE_NVERTS__, + &attrib, 1, (void*)&SQUARE_DESC__), RES_OK); + CHECK(ssol_object_create(dev, &object), RES_OK); CHECK(ssol_object_add_shaded_shape(object, shape, material, material), RES_OK); @@ -64,31 +72,89 @@ main(int argc, char** argv) CHECK(ssol_instance_ref_put(NULL), RES_BAD_ARG); CHECK(ssol_instance_ref_put(instance), RES_OK); - CHECK(ssol_instance_ref_put(instance1), RES_OK); CHECK(ssol_instance_set_transform(NULL, transform), RES_BAD_ARG); CHECK(ssol_instance_set_transform(instance, NULL), RES_BAD_ARG); CHECK(ssol_instance_set_transform(instance, transform), RES_OK); CHECK(ssol_instance_set_transform(instance, transform), RES_OK); - #define SET_RECEIVER ssol_instance_set_receiver - CHECK(ssol_instance_set_receiver(NULL, 0), RES_BAD_ARG); - CHECK(ssol_instance_set_receiver(instance, 0), RES_OK); - #undef SET_RECEIVER + CHECK(ssol_instance_set_receiver(NULL, 0, 0), RES_BAD_ARG); + CHECK(ssol_instance_set_receiver(instance, 0, 0), RES_OK); CHECK(ssol_instance_sample(NULL, 0), RES_BAD_ARG); CHECK(ssol_instance_sample(instance, 0), RES_OK); CHECK(ssol_instance_sample(instance, 1), RES_OK); - CHECK(ssol_instance_ref_put(instance), RES_OK); + CHECK(ssol_instance_get_shaded_shapes_count(NULL, NULL), RES_BAD_ARG); + CHECK(ssol_instance_get_shaded_shapes_count(instance, NULL), RES_BAD_ARG); + CHECK(ssol_instance_get_shaded_shapes_count(NULL, &n), RES_BAD_ARG); + CHECK(ssol_instance_get_shaded_shapes_count(instance, &n), RES_OK); + CHECK(n, 1); + + CHECK(ssol_instance_get_shaded_shape(NULL, n, NULL), RES_BAD_ARG); + CHECK(ssol_instance_get_shaded_shape(instance, n, NULL), RES_BAD_ARG); + CHECK(ssol_instance_get_shaded_shape(NULL, 0, NULL), RES_BAD_ARG); + CHECK(ssol_instance_get_shaded_shape(instance, 0, NULL), RES_BAD_ARG); + CHECK(ssol_instance_get_shaded_shape(NULL, n, &sshape), RES_BAD_ARG); + CHECK(ssol_instance_get_shaded_shape(instance, n, &sshape), RES_BAD_ARG); + CHECK(ssol_instance_get_shaded_shape(NULL, 0, &sshape), RES_BAD_ARG); + CHECK(ssol_instance_get_shaded_shape(instance, 0, &sshape), RES_OK); + + CHECK(sshape.shape, shape); + CHECK(sshape.mtl_front, material); + CHECK(sshape.mtl_back, material); + + CHECK(ssol_shape_get_vertices_count(sshape.shape, &count), RES_OK); + + #define GET_ATTR ssol_instantiated_shaded_shape_get_vertex_attrib + CHECK(GET_ATTR(NULL, count, (unsigned)-1, NULL), RES_BAD_ARG); + CHECK(GET_ATTR(&sshape, count, (unsigned)-1, NULL), RES_BAD_ARG); + CHECK(GET_ATTR(NULL, 0, (unsigned)-1, NULL), RES_BAD_ARG); + CHECK(GET_ATTR(&sshape, 0, (unsigned)-1, NULL), RES_BAD_ARG); + CHECK(GET_ATTR(NULL, count, SSOL_POSITION, NULL), RES_BAD_ARG); + CHECK(GET_ATTR(&sshape, count, SSOL_POSITION, NULL), RES_BAD_ARG); + CHECK(GET_ATTR(NULL, 0, SSOL_POSITION, NULL), RES_BAD_ARG); + CHECK(GET_ATTR(&sshape, 0, SSOL_POSITION, NULL), RES_BAD_ARG); + CHECK(GET_ATTR(NULL, count, (unsigned)-1, val), RES_BAD_ARG); + CHECK(GET_ATTR(&sshape, count, (unsigned)-1, val), RES_BAD_ARG); + CHECK(GET_ATTR(NULL, 0, (unsigned)-1, val), RES_BAD_ARG); + CHECK(GET_ATTR(&sshape, 0, (unsigned)-1, val), RES_BAD_ARG); + CHECK(GET_ATTR(NULL, count, SSOL_POSITION, val), RES_BAD_ARG); + CHECK(GET_ATTR(&sshape, count, SSOL_POSITION, val), RES_BAD_ARG); + CHECK(GET_ATTR(NULL, 0, SSOL_POSITION, val), RES_BAD_ARG); + FOR_EACH(i, 0, count) { + float valf[3]; + double val2[3]; + + CHECK(GET_ATTR(&sshape, i, SSOL_POSITION, val), RES_OK); + get_position(i, valf, (void*)&SQUARE_DESC__); + d3_set_f3(val2, valf); + d33_muld3(val2, transform, val2); + d3_add(val2, transform+9, val2); + CHECK(eq_eps(val[0], val2[0], 1.e-6), 1); + CHECK(eq_eps(val[1], val2[1], 1.e-6), 1); + CHECK(eq_eps(val[2], val2[2], 1.e-6), 1); + } + CHECK(ssol_instance_get_shaded_shape(instance1, 0, &sshape), RES_OK); + FOR_EACH(i, 0, count) { + float valf[3]; + + CHECK(GET_ATTR(&sshape, i, SSOL_POSITION, val), RES_OK); + get_position(i, valf, (void*)&SQUARE_DESC__); + CHECK((float)val[0], valf[0]); + CHECK((float)val[1], valf[1]); + CHECK((float)val[2], valf[2]); + } + #undef GET_ATTR + + CHECK(ssol_instance_ref_put(instance), RES_OK); + CHECK(ssol_instance_ref_put(instance1), RES_OK); CHECK(ssol_object_ref_put(object), RES_OK); CHECK(ssol_shape_ref_put(shape), RES_OK); CHECK(ssol_material_ref_put(material), 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); diff --git a/src/test_ssol_material.c b/src/test_ssol_material.c @@ -17,34 +17,25 @@ #include "test_ssol_utils.h" #include "test_ssol_materials.h" -#include <rsys/logger.h> - -int -main(int argc, char** argv) +static void +test_mirror(struct ssol_device* dev) { - struct logger logger; - struct mem_allocator allocator; - struct ssol_device* dev; - struct ssol_material* material; struct ssol_mirror_shader mirror = SSOL_MIRROR_SHADER_NULL; - struct ssol_matte_shader matte = SSOL_MATTE_SHADER_NULL; struct ssol_param_buffer* pbuf = NULL; - (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); + struct ssol_material* material; + enum ssol_material_type type; + CHECK(ssol_material_create_mirror(NULL, NULL), RES_BAD_ARG); CHECK(ssol_material_create_mirror(NULL, &material), RES_BAD_ARG); CHECK(ssol_material_create_mirror(dev, NULL), RES_BAD_ARG); CHECK(ssol_material_create_mirror(dev, &material), RES_OK); + CHECK(ssol_material_get_type(NULL, NULL), RES_BAD_ARG); + CHECK(ssol_material_get_type(material, NULL), RES_BAD_ARG); + CHECK(ssol_material_get_type(NULL, &type), RES_BAD_ARG); + CHECK(ssol_material_get_type(material, &type), RES_OK); + CHECK(type, SSOL_MATERIAL_MIRROR); + CHECK(ssol_material_ref_get(NULL), RES_BAD_ARG); CHECK(ssol_material_ref_get(material), RES_OK); @@ -57,10 +48,10 @@ main(int argc, char** argv) mirror.reflectivity = get_shader_reflectivity; mirror.roughness = get_shader_roughness; - CHECK(ssol_mirror_set_shader(NULL, &mirror), RES_BAD_ARG); - CHECK(ssol_mirror_set_shader(material, NULL), RES_BAD_ARG); - CHECK(ssol_mirror_set_shader(material, &mirror), RES_OK); - CHECK(ssol_mirror_set_shader(material, &mirror), RES_OK); + CHECK(ssol_mirror_setup(NULL, &mirror), RES_BAD_ARG); + CHECK(ssol_mirror_setup(material, NULL), RES_BAD_ARG); + CHECK(ssol_mirror_setup(material, &mirror), RES_OK); + CHECK(ssol_mirror_setup(material, &mirror), RES_OK); CHECK(ssol_material_set_param_buffer(NULL, NULL), RES_BAD_ARG); CHECK(ssol_material_set_param_buffer(material, NULL), RES_BAD_ARG); @@ -68,48 +59,223 @@ main(int argc, char** argv) CHECK(ssol_material_set_param_buffer(material, pbuf), RES_OK); mirror.normal = NULL; - CHECK(ssol_mirror_set_shader(material, &mirror), RES_BAD_ARG); + CHECK(ssol_mirror_setup(material, &mirror), RES_BAD_ARG); mirror.normal = get_shader_normal; mirror.reflectivity = NULL; - CHECK(ssol_mirror_set_shader(material, &mirror), RES_BAD_ARG); + CHECK(ssol_mirror_setup(material, &mirror), RES_BAD_ARG); mirror.reflectivity = get_shader_reflectivity; mirror.roughness = NULL; - CHECK(ssol_mirror_set_shader(material, &mirror), RES_BAD_ARG); + CHECK(ssol_mirror_setup(material, &mirror), RES_BAD_ARG); mirror.roughness = get_shader_roughness; CHECK(ssol_material_ref_put(material), RES_OK); - - CHECK(ssol_material_create_virtual(NULL, &material), RES_BAD_ARG); - CHECK(ssol_material_create_virtual(dev, NULL), RES_BAD_ARG); - CHECK(ssol_material_create_virtual(dev, &material), RES_OK); - - CHECK(ssol_material_ref_put(material), RES_OK); CHECK(ssol_param_buffer_ref_put(pbuf), RES_OK); +} + +static void +test_matte(struct ssol_device* dev) +{ + struct ssol_matte_shader matte = SSOL_MATTE_SHADER_NULL; + struct ssol_material* material; + enum ssol_material_type type; CHECK(ssol_material_create_matte(NULL, NULL), RES_BAD_ARG); CHECK(ssol_material_create_matte(dev, NULL), RES_BAD_ARG); CHECK(ssol_material_create_matte(NULL, &material), RES_BAD_ARG); CHECK(ssol_material_create_matte(dev, &material), RES_OK); + CHECK(ssol_material_get_type(material, &type), RES_OK); + CHECK(type, SSOL_MATERIAL_MATTE); + matte.normal = get_shader_normal; matte.reflectivity = get_shader_reflectivity; - CHECK(ssol_matte_set_shader(NULL, NULL), RES_BAD_ARG); - CHECK(ssol_matte_set_shader(material, NULL), RES_BAD_ARG); - CHECK(ssol_matte_set_shader(NULL, &matte), RES_BAD_ARG); - CHECK(ssol_matte_set_shader(material, &matte), RES_OK); + CHECK(ssol_matte_setup(NULL, NULL), RES_BAD_ARG); + CHECK(ssol_matte_setup(material, NULL), RES_BAD_ARG); + CHECK(ssol_matte_setup(NULL, &matte), RES_BAD_ARG); + CHECK(ssol_matte_setup(material, &matte), RES_OK); matte.normal = NULL; - CHECK(ssol_matte_set_shader(material, &matte), RES_BAD_ARG); + CHECK(ssol_matte_setup(material, &matte), RES_BAD_ARG); matte.normal = get_shader_normal; matte.reflectivity = NULL; - CHECK(ssol_matte_set_shader(material, &matte), RES_BAD_ARG); + CHECK(ssol_matte_setup(material, &matte), RES_BAD_ARG); CHECK(ssol_material_ref_put(material), RES_OK); - CHECK(ssol_device_ref_put(dev), RES_OK); +} + +static void +test_thin_dielectric(struct ssol_device* dev) +{ + struct ssol_thin_dielectric_shader shader = + SSOL_THIN_DIELECTRIC_SHADER_NULL; + struct ssol_material* mtl; + struct ssol_medium mdm0 = SSOL_MEDIUM_VACUUM; + struct ssol_medium mdm1 = SSOL_MEDIUM_VACUUM; + enum ssol_material_type type; + + CHECK(ssol_material_create_thin_dielectric(NULL, NULL), RES_BAD_ARG); + CHECK(ssol_material_create_thin_dielectric(dev, NULL), RES_BAD_ARG); + CHECK(ssol_material_create_thin_dielectric(NULL, &mtl), RES_BAD_ARG); + CHECK(ssol_material_create_thin_dielectric(dev, &mtl), RES_OK); + + CHECK(ssol_material_get_type(mtl, &type), RES_OK); + CHECK(type, SSOL_MATERIAL_THIN_DIELECTRIC); + + shader.normal = get_shader_normal; + + CHECK(ssol_thin_dielectric_setup(NULL, NULL, NULL, NULL, -1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(mtl, NULL, NULL, NULL, -1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(NULL, &shader, NULL, NULL, -1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(mtl, &shader, NULL, NULL, -1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(NULL, NULL, &mdm0, NULL, -1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(mtl, NULL, &mdm0, NULL, -1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(NULL, &shader, &mdm0, NULL, -1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(mtl, &shader, &mdm0, NULL, -1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(NULL, NULL, NULL, NULL, 1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(mtl, NULL, NULL, NULL, 1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(NULL, &shader, NULL, NULL, 1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(mtl, &shader, NULL, NULL, 1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(NULL, NULL, &mdm0, NULL, 1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(mtl, NULL, &mdm0, NULL, 1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(NULL, &shader, &mdm0, NULL, 1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(mtl, &shader, &mdm0, NULL, 1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(NULL, NULL, NULL, &mdm1, -1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(mtl, NULL, NULL, &mdm1, -1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(NULL, &shader, NULL, &mdm1, -1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(mtl, &shader, NULL, &mdm1, -1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(NULL, NULL, &mdm0, &mdm1, -1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(mtl, NULL, &mdm0, &mdm1, -1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(NULL, &shader, &mdm0, &mdm1, -1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(mtl, &shader, &mdm0, &mdm1, -1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(NULL, NULL, NULL, &mdm1, 1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(mtl, NULL, NULL, &mdm1, 1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(NULL, &shader, NULL, &mdm1, 1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(mtl, &shader, NULL, &mdm1, 1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(NULL, NULL, &mdm0, &mdm1, 1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(mtl, NULL, &mdm0, &mdm1, 1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(NULL, &shader, &mdm0, &mdm1, 1), RES_BAD_ARG); + CHECK(ssol_thin_dielectric_setup(mtl, &shader, &mdm0, &mdm1, 1), RES_OK); + + shader.normal = NULL; + CHECK(ssol_thin_dielectric_setup(mtl, &shader, &mdm0, &mdm1, 1), RES_BAD_ARG); + shader.normal = get_shader_normal; + + mdm0.absorptivity = -1; + CHECK(ssol_thin_dielectric_setup(mtl, &shader, &mdm0, &mdm1, 1), RES_BAD_ARG); + mdm0.absorptivity = SSOL_MEDIUM_VACUUM.absorptivity; + + mdm0.refractive_index = 0; + CHECK(ssol_thin_dielectric_setup(mtl, &shader, &mdm0, &mdm1, 1), RES_BAD_ARG); + mdm0.refractive_index = SSOL_MEDIUM_VACUUM.refractive_index; + + mdm1.absorptivity = -1; + CHECK(ssol_thin_dielectric_setup(mtl, &shader, &mdm0, &mdm1, 1), RES_BAD_ARG); + mdm1.absorptivity = SSOL_MEDIUM_VACUUM.absorptivity; + + mdm1.refractive_index = 0; + CHECK(ssol_thin_dielectric_setup(mtl, &shader, &mdm0, &mdm1, 1), RES_BAD_ARG); + mdm1.refractive_index = SSOL_MEDIUM_VACUUM.refractive_index; + + CHECK(ssol_material_ref_put(mtl), RES_OK); +} + +static void +test_dielectric(struct ssol_device* dev) +{ + struct ssol_dielectric_shader dielectric = SSOL_DIELECTRIC_SHADER_NULL; + struct ssol_material* material; + struct ssol_medium mdm0 = SSOL_MEDIUM_VACUUM; + struct ssol_medium mdm1 = SSOL_MEDIUM_VACUUM; + enum ssol_material_type type; + + CHECK(ssol_material_create_dielectric(NULL, NULL), RES_BAD_ARG); + CHECK(ssol_material_create_dielectric(dev, NULL), RES_BAD_ARG); + CHECK(ssol_material_create_dielectric(NULL, &material), RES_BAD_ARG); + CHECK(ssol_material_create_dielectric(dev, &material), RES_OK); + + CHECK(ssol_material_get_type(material, &type), RES_OK); + CHECK(type, SSOL_MATERIAL_DIELECTRIC); + + dielectric.normal = get_shader_normal; + + CHECK(ssol_dielectric_setup(NULL, NULL, NULL, NULL), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, NULL, NULL), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, NULL, NULL), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, NULL, NULL), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, NULL, &mdm0, NULL), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, &mdm0, NULL), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, &mdm0, NULL), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, &mdm0, NULL), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, NULL, NULL, &mdm1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, NULL, &mdm1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, NULL, &mdm1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, NULL, &mdm1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, NULL, &mdm0, &mdm1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, &mdm0, &mdm1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, &mdm0, &mdm1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, &mdm0, &mdm1), RES_OK); + + dielectric.normal = NULL; + CHECK(ssol_dielectric_setup(NULL, &dielectric, &mdm0, &mdm1), RES_BAD_ARG); + dielectric.normal = get_shader_normal; + + mdm0.refractive_index = 0; + CHECK(ssol_dielectric_setup(NULL, &dielectric, &mdm0, &mdm1), RES_BAD_ARG); + mdm0.refractive_index = SSOL_MEDIUM_VACUUM.refractive_index; + + mdm1.refractive_index = 0; + CHECK(ssol_dielectric_setup(NULL, &dielectric, &mdm0, &mdm1), RES_BAD_ARG); + mdm1.refractive_index = SSOL_MEDIUM_VACUUM.refractive_index; - logger_release(&logger); + mdm0.absorptivity = -1; + CHECK(ssol_dielectric_setup(NULL, &dielectric, &mdm0, &mdm1), RES_BAD_ARG); + mdm0.absorptivity = SSOL_MEDIUM_VACUUM.refractive_index; + + mdm1.absorptivity = -1; + CHECK(ssol_dielectric_setup(NULL, &dielectric, &mdm0, &mdm1), RES_BAD_ARG); + mdm1.refractive_index = SSOL_MEDIUM_VACUUM.refractive_index; + + CHECK(ssol_material_ref_put(material), RES_OK); +} + +static void +test_virtual(struct ssol_device* dev) +{ + struct ssol_material* material; + enum ssol_material_type type; + + CHECK(ssol_material_create_virtual(NULL, NULL), RES_BAD_ARG); + CHECK(ssol_material_create_virtual(NULL, &material), RES_BAD_ARG); + CHECK(ssol_material_create_virtual(dev, NULL), RES_BAD_ARG); + CHECK(ssol_material_create_virtual(dev, &material), RES_OK); + + CHECK(ssol_material_get_type(material, &type), RES_OK); + CHECK(type, SSOL_MATERIAL_VIRTUAL); + + CHECK(ssol_material_ref_put(material), RES_OK); +} + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct ssol_device* dev; + (void) argc, (void) argv; + + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + + CHECK(ssol_device_create + (NULL, &allocator, SSOL_NTHREADS_DEFAULT, 0, &dev), RES_OK); + + test_mirror(dev); + test_matte(dev); + test_thin_dielectric(dev); + test_dielectric(dev); + test_virtual(dev); + + CHECK(ssol_device_ref_put(dev), RES_OK); check_memory_allocator(&allocator); mem_shutdown_proxy_allocator(&allocator); @@ -117,3 +283,4 @@ main(int argc, char** argv) return 0; } + diff --git a/src/test_ssol_materials.h b/src/test_ssol_materials.h @@ -90,4 +90,55 @@ get_shader_roughness *val = 0; } +static INLINE void +get_shader_absorption + (struct ssol_device* dev, + struct ssol_param_buffer* buf, + const double wavelength, + const double P[3], + const double Ng[3], + const double Ns[3], + const double uv[2], + const double w[3], + double* val) +{ + (void)dev, (void)buf, (void)wavelength; + (void)P, (void)Ng, (void)Ns, (void)uv, (void) w; + *val = 0; +} + +static INLINE void +get_shader_thickness + (struct ssol_device* dev, + struct ssol_param_buffer* buf, + const double wavelength, + const double P[3], + const double Ng[3], + const double Ns[3], + const double uv[2], + const double w[3], + double* val) +{ + (void)dev, (void)buf, (void)wavelength; + (void)P, (void)Ng, (void)Ns, (void)uv, (void) w; + *val = 1; +} + +static INLINE void +get_shader_refractive_index + (struct ssol_device* dev, + struct ssol_param_buffer* buf, + const double wavelength, + const double P[3], + const double Ng[3], + const double Ns[3], + const double uv[2], + const double w[3], + double* val) +{ + (void)dev, (void)buf, (void)wavelength; + (void)P, (void)Ng, (void)Ns, (void)uv, (void) w; + *val = 1.5; +} + #endif /* TEST_SSOL_MATERIALS_H */ diff --git a/src/test_ssol_object.c b/src/test_ssol_object.c @@ -16,12 +16,9 @@ #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_shape* shape; @@ -33,13 +30,8 @@ main(int argc, char** 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); + (NULL, &allocator, SSOL_NTHREADS_DEFAULT, 0, &dev), RES_OK); CHECK(ssol_material_create_virtual(dev, &mtl), RES_OK); CHECK(ssol_material_create_virtual(dev, &mtl2), RES_OK); CHECK(ssol_shape_create_punched_surface(dev, &shape), RES_OK); @@ -88,7 +80,6 @@ main(int argc, char** argv) CHECK(ssol_material_ref_put(mtl2), 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); diff --git a/src/test_ssol_rect2D_geometry.h b/src/test_ssol_rect2D_geometry.h @@ -18,25 +18,41 @@ /******************************************************************************* * Rectangle polygon ******************************************************************************/ -#if !defined(HALF_X) -#error "Missing the HALF_X macro defining the rectangle size" +#if !defined(HALF_X) && !(defined(X_MIN) && defined(X_MAX)) +#error "Missing the HALF_X or X_MIN and X_MAX macros defining the rectangle size" #endif -#if !defined(HALF_Y) -#error "Missing the HALF_Y macro defining the rectangle size" +#if !defined(HALF_Y) && !(defined(Y_MIN) && defined(Y_MAX)) +#error "Missing the HALF_Y or Y_MIN and Y_MAX macros defining the rectangle size" #endif #if !defined(POLYGON_NAME) -#error "Missing the POLYGON_NAME macro defining the polygon name" +#error "Missing the POLYGON_NAME macro defining the rectangle name" #endif #define EDGES__ CONCAT(POLYGON_NAME, _EDGES__) #define POLY_NVERTS__ CONCAT(POLYGON_NAME, _NVERTS__) +#if !defined(X_MIN) +#define X_MIN (float)(-(HALF_X)) +#endif + +#if !defined(X_MAX) +#define X_MAX (float)(HALF_X) +#endif + +#if !defined(Y_MIN) +#define Y_MIN (float)(-(HALF_Y)) +#endif + +#if !defined(Y_MAX) +#define Y_MAX (float)(HALF_Y) +#endif + /* should be const but scpr expects non-const data */ static double EDGES__ [] = { - -HALF_X, -HALF_Y, - -HALF_X, HALF_Y, - HALF_X, HALF_Y, - HALF_X, -HALF_Y, + X_MIN, Y_MIN, + X_MIN, Y_MAX, + X_MAX, Y_MAX, + X_MAX, Y_MIN }; const unsigned POLY_NVERTS__ = sizeof(EDGES__) / sizeof(double[2]); @@ -46,4 +62,8 @@ const unsigned POLY_NVERTS__ = sizeof(EDGES__) / sizeof(double[2]); #undef HALF_X #undef HALF_Y +#undef X_MIN +#undef X_MAX +#undef Y_MIN +#undef Y_MAX #undef POLYGON_NAME diff --git a/src/test_ssol_rect_geometry.h b/src/test_ssol_rect_geometry.h @@ -18,11 +18,11 @@ /******************************************************************************* * Rectangle plane ******************************************************************************/ -#if !defined(HALF_X) -#error "Missing the HALF_X macro defining the rectangle size" +#if !defined(HALF_X) && !(defined(X_MIN) && defined(X_MAX)) +#error "Missing the HALF_X or X_MIN and X_MAX macros defining the rectangle size" #endif -#if !defined(HALF_Y) -#error "Missing the HALF_Y macro defining the rectangle size" +#if !defined(HALF_Y) && !(defined(Y_MIN) && defined(Y_MAX)) +#error "Missing the HALF_Y or Y_MIN and Y_MAX macros defining the rectangle size" #endif #if !defined(PLANE_NAME) #error "Missing the DARRAY_NAME macro defining the rectangle name" @@ -34,11 +34,27 @@ #define RECT_NVERTS__ CONCAT(PLANE_NAME, _NVERTS__) #define RECT_NTRIS__ CONCAT(PLANE_NAME, _NTRIS__) +#if !defined(X_MIN) +#define X_MIN (float)(-(HALF_X)) +#endif + +#if !defined(X_MAX) +#define X_MAX (float)(HALF_X) +#endif + +#if !defined(Y_MIN) +#define Y_MIN (float)(-(HALF_Y)) +#endif + +#if !defined(Y_MAX) +#define Y_MAX (float)(HALF_Y) +#endif + static const float EDGES__ [] = { - (float) -HALF_X, (float) -HALF_Y, 0.f, - (float) HALF_X, (float) -HALF_Y, 0.f, - (float) HALF_X, (float) HALF_Y, 0.f, - (float) -HALF_X, (float) HALF_Y, 0.f + X_MIN, Y_MIN, 0.f, + X_MAX, Y_MIN, 0.f, + X_MAX, Y_MAX, 0.f, + X_MIN, Y_MAX, 0.f }; const unsigned RECT_NVERTS__ = sizeof(EDGES__) / sizeof(float[3]); @@ -56,4 +72,8 @@ static const struct desc RECT_DESC__ = { EDGES__, TRG_IDS__ }; #undef HALF_X #undef HALF_Y +#undef X_MIN +#undef X_MAX +#undef Y_MIN +#undef Y_MAX #undef PLANE_NAME diff --git a/src/test_ssol_scene.c b/src/test_ssol_scene.c @@ -15,8 +15,35 @@ #include "ssol.h" #include "test_ssol_utils.h" +#include "test_ssol_geometries.h" -#include <rsys/logger.h> +#include <rsys/float3.h> + +struct scene_ctx { + struct ssol_instance* instance; + struct ssol_instance* instance2; + int instance_found; + int instance2_found; +}; + +static res_T +instance_func(struct ssol_instance* inst, void* context) +{ + struct scene_ctx* ctx = context; + NCHECK(inst, NULL); + if(!ctx) return RES_BAD_ARG; + + if(inst == ctx->instance) { + CHECK(ctx->instance_found, 0); + ctx->instance_found = 1; + } else if(inst == ctx->instance2) { + CHECK(ctx->instance2_found, 0); + ctx->instance2_found = 1; + } else { + FATAL("Unreachable code.\n"); + } + return RES_OK; +} static void get_wlen(const size_t i, double* wlen, double* data, void* ctx) @@ -32,13 +59,30 @@ get_wlen(const size_t i, double* wlen, double* data, void* ctx) int main(int argc, char** argv) { - struct logger logger; + const float tall_block[] = { + 423.f, 247.f, 0.f, + 265.f, 296.f, 0.f, + 314.f, 456.f, 0.f, + 472.f, 406.f, 0.f, + 423.f, 247.f, 330.f, + 265.f, 296.f, 330.f, + 314.f, 456.f, 330.f, + 472.f, 406.f, 330.f + }; + const unsigned block_ids[] = { + 4, 5, 6, 6, 7, 4, + 1, 2, 6, 6, 5, 1, + 0, 3, 7, 7, 4, 0, + 2, 3, 7, 7, 6, 2, + 0, 1, 5, 5, 4, 0 + }; struct mem_allocator allocator; struct ssol_device* dev; struct ssol_shape* shape; struct ssol_material* material; struct ssol_object* object; struct ssol_instance* instance; + struct ssol_instance* instance2; struct ssol_sun* sun; struct ssol_sun* sun2; struct ssol_scene* scene; @@ -46,18 +90,17 @@ main(int argc, char** argv) struct ssol_spectrum* spectrum; struct ssol_atmosphere* atm; struct ssol_atmosphere* atm2; + struct ssol_vertex_data vdata; + struct scene_ctx ctx; + struct desc desc; double transform[12]; + float lower[3], upper[3], tmp[3]; (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); + (NULL, &allocator, SSOL_NTHREADS_DEFAULT, 0, &dev), RES_OK); CHECK(ssol_material_create_virtual(dev, &material), RES_OK); @@ -65,6 +108,7 @@ main(int argc, char** argv) CHECK(ssol_object_create(dev, &object), RES_OK); CHECK(ssol_object_add_shaded_shape(object, shape, material, material), RES_OK); CHECK(ssol_object_instantiate(object, &instance), RES_OK); + CHECK(ssol_object_instantiate(object, &instance2), RES_OK); CHECK(ssol_instance_set_transform(instance, transform), RES_OK); CHECK(ssol_sun_create_directional(dev, &sun), RES_OK); CHECK(ssol_sun_create_directional(dev, &sun2), RES_OK); @@ -85,11 +129,28 @@ main(int argc, char** argv) CHECK(ssol_scene_attach_instance(scene, NULL), RES_BAD_ARG); CHECK(ssol_scene_attach_instance(scene, instance), RES_OK); CHECK(ssol_scene_attach_instance(scene, instance), RES_OK); + CHECK(ssol_scene_attach_instance(scene, instance2), RES_OK); + + ctx.instance = instance; + ctx.instance2 = instance2; + ctx.instance_found = 0; + ctx.instance2_found = 0; + CHECK(ssol_scene_for_each_instance(NULL, NULL, NULL), RES_BAD_ARG); + CHECK(ssol_scene_for_each_instance(scene, NULL, NULL), RES_BAD_ARG); + CHECK(ssol_scene_for_each_instance(NULL, instance_func, NULL), RES_BAD_ARG); + CHECK(ssol_scene_for_each_instance(scene, instance_func, NULL), RES_BAD_ARG); + CHECK(ssol_scene_for_each_instance(NULL, NULL, &ctx), RES_BAD_ARG); + CHECK(ssol_scene_for_each_instance(scene, NULL, &ctx), RES_BAD_ARG); + CHECK(ssol_scene_for_each_instance(NULL, instance_func, &ctx), RES_BAD_ARG); + CHECK(ssol_scene_for_each_instance(scene, instance_func, &ctx), RES_OK); + CHECK(ctx.instance_found, 1); + CHECK(ctx.instance2_found, 1); CHECK(ssol_scene_detach_instance(NULL, instance), RES_BAD_ARG); CHECK(ssol_scene_detach_instance(scene, NULL), RES_BAD_ARG); CHECK(ssol_scene_detach_instance(scene, instance), RES_OK); CHECK(ssol_scene_detach_instance(scene, instance), RES_BAD_ARG); + CHECK(ssol_scene_detach_instance(scene, instance2), RES_OK); CHECK(ssol_scene_attach_instance(scene, instance), RES_OK); CHECK(ssol_scene_attach_instance(scene2, instance), RES_OK); @@ -154,8 +215,54 @@ main(int argc, char** argv) CHECK(ssol_scene_ref_put(scene), RES_OK); CHECK(ssol_scene_ref_put(scene2), RES_OK); + CHECK(ssol_shape_ref_put(shape), RES_OK); + CHECK(ssol_object_ref_put(object), RES_OK); + CHECK(ssol_instance_ref_put(instance), RES_OK); + + CHECK(ssol_scene_create(dev, &scene), RES_OK); + CHECK(ssol_shape_create_mesh(dev, &shape), RES_OK); + CHECK(ssol_object_create(dev, &object), RES_OK); + + vdata.usage = SSOL_POSITION; + vdata.get = get_position; + desc.vertices = tall_block;; + desc.indices = block_ids; + + CHECK(ssol_mesh_setup(shape, 10, get_ids, 8, &vdata, 1, &desc), RES_OK); + CHECK(ssol_object_add_shaded_shape(object, shape, material, material), RES_OK); + CHECK(ssol_object_instantiate(object, &instance), RES_OK); + + CHECK(ssol_scene_compute_aabb(NULL, NULL, NULL), RES_BAD_ARG); + CHECK(ssol_scene_compute_aabb(scene, NULL, NULL), RES_BAD_ARG); + CHECK(ssol_scene_compute_aabb(NULL, lower, NULL), RES_BAD_ARG); + CHECK(ssol_scene_compute_aabb(scene, lower, NULL), RES_BAD_ARG); + CHECK(ssol_scene_compute_aabb(NULL, NULL, upper), RES_BAD_ARG); + CHECK(ssol_scene_compute_aabb(scene, NULL, upper), RES_BAD_ARG); + CHECK(ssol_scene_compute_aabb(NULL, lower, upper), RES_BAD_ARG); + CHECK(ssol_scene_compute_aabb(scene, lower, upper), RES_OK); + + /* Empty scene */ + CHECK(lower[0] > upper[0], 1); + CHECK(lower[1] > upper[1], 1); + CHECK(lower[2] > upper[2], 1); + + CHECK(ssol_scene_attach_instance(scene, instance), RES_OK); + CHECK(ssol_scene_compute_aabb(scene, lower, upper), RES_OK); + CHECK(f3_eq_eps(lower, f3(tmp, 265.f, 247.f, 0.f), 1.e-6f), 1); + CHECK(f3_eq_eps(upper, f3(tmp, 472.f, 456.f, 330.f), 1.e-6f), 1); + + CHECK(ssol_scene_clear(scene), RES_OK); + CHECK(ssol_scene_compute_aabb(scene, lower, upper), RES_OK); + + /* Empty scene */ + CHECK(lower[0] > upper[0], 1); + CHECK(lower[1] > upper[1], 1); + CHECK(lower[2] > upper[2], 1); + + CHECK(ssol_scene_ref_put(scene), RES_OK); CHECK(ssol_instance_ref_put(instance), RES_OK); + CHECK(ssol_instance_ref_put(instance2), RES_OK); CHECK(ssol_object_ref_put(object), RES_OK); CHECK(ssol_shape_ref_put(shape), RES_OK); CHECK(ssol_sun_ref_put(sun), RES_OK); @@ -166,8 +273,6 @@ main(int argc, char** argv) 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); diff --git a/src/test_ssol_shape.c b/src/test_ssol_shape.c @@ -21,19 +21,14 @@ #define HALF_Y 1 #include "test_ssol_rect_geometry.h" -#include <rsys/logger.h> - -/******************************************************************************* - * Test main program - ******************************************************************************/ int main(int argc, char** argv) { - struct logger logger; struct mem_allocator allocator; struct ssol_device* dev; struct ssol_shape* shape; - struct ssol_vertex_data attribs[3] = { SSOL_VERTEX_DATA_NULL__, SSOL_VERTEX_DATA_NULL__, SSOL_VERTEX_DATA_NULL__ }; + struct ssol_vertex_data attribs[3] = + {SSOL_VERTEX_DATA_NULL__, SSOL_VERTEX_DATA_NULL__, SSOL_VERTEX_DATA_NULL__}; struct ssol_punched_surface punched_surface = SSOL_PUNCHED_SURFACE_NULL; struct ssol_carving carving = SSOL_CARVING_NULL; struct ssol_quadric quadric = SSOL_QUADRIC_DEFAULT; @@ -41,17 +36,15 @@ main(int argc, char** argv) -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 0.f, -2.f }; const size_t npolygon_verts = sizeof(polygon)/sizeof(double[2]); + double val[3]; + unsigned ids[3]; + unsigned i, n; (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); + (NULL, &allocator, SSOL_NTHREADS_DEFAULT, 0, &dev), RES_OK); CHECK(ssol_shape_create_mesh(NULL, NULL), RES_BAD_ARG); CHECK(ssol_shape_create_mesh(dev, NULL), RES_BAD_ARG); @@ -86,6 +79,72 @@ main(int argc, char** argv) CHECK(ssol_mesh_setup(shape, SQUARE_NTRIS__, get_ids, SQUARE_NVERTS__, attribs, 3, (void*)&SQUARE_DESC__), RES_OK); + CHECK(ssol_shape_get_vertices_count(NULL, NULL), RES_BAD_ARG); + CHECK(ssol_shape_get_vertices_count(shape, NULL), RES_BAD_ARG); + CHECK(ssol_shape_get_vertices_count(NULL, &n), RES_BAD_ARG); + CHECK(ssol_shape_get_vertices_count(shape, &n), RES_OK); + CHECK(n, SQUARE_NVERTS__); + + CHECK(ssol_shape_get_vertex_attrib(NULL, n, (unsigned)-1, NULL), RES_BAD_ARG); + CHECK(ssol_shape_get_vertex_attrib(shape, n, (unsigned)-1, NULL), RES_BAD_ARG); + CHECK(ssol_shape_get_vertex_attrib(NULL, 0, (unsigned)-1, NULL), RES_BAD_ARG); + CHECK(ssol_shape_get_vertex_attrib(shape, 0, (unsigned)-1, NULL), RES_BAD_ARG); + CHECK(ssol_shape_get_vertex_attrib(NULL, n, SSOL_POSITION, NULL), RES_BAD_ARG); + CHECK(ssol_shape_get_vertex_attrib(shape, n, SSOL_POSITION, NULL), RES_BAD_ARG); + CHECK(ssol_shape_get_vertex_attrib(NULL, 0, SSOL_POSITION, NULL), RES_BAD_ARG); + CHECK(ssol_shape_get_vertex_attrib(shape, 0, SSOL_POSITION, NULL), RES_BAD_ARG); + CHECK(ssol_shape_get_vertex_attrib(NULL, n, (unsigned)-1, val), RES_BAD_ARG); + CHECK(ssol_shape_get_vertex_attrib(shape, n, (unsigned)-1, val), RES_BAD_ARG); + CHECK(ssol_shape_get_vertex_attrib(NULL, 0, (unsigned)-1, val), RES_BAD_ARG); + CHECK(ssol_shape_get_vertex_attrib(shape, 0, (unsigned)-1,val), RES_BAD_ARG); + CHECK(ssol_shape_get_vertex_attrib(NULL, n, SSOL_POSITION, val), RES_BAD_ARG); + CHECK(ssol_shape_get_vertex_attrib(shape, n, SSOL_POSITION, val), RES_BAD_ARG); + CHECK(ssol_shape_get_vertex_attrib(NULL, 0, SSOL_POSITION, val), RES_BAD_ARG); + + FOR_EACH(i, 0, n) { + float valf[3]; + + CHECK(ssol_shape_get_vertex_attrib(shape, i, SSOL_POSITION, val), RES_OK); + get_position(i, valf, (void*)&SQUARE_DESC__); + CHECK((float)val[0], valf[0]); + CHECK((float)val[1], valf[1]); + CHECK((float)val[2], valf[2]); + + CHECK(ssol_shape_get_vertex_attrib(shape, i, SSOL_NORMAL, val), RES_OK); + get_normal(i, valf, (void*)&SQUARE_DESC__); + CHECK((float)val[0], valf[0]); + CHECK((float)val[1], valf[1]); + CHECK((float)val[2], valf[2]); + + CHECK(ssol_shape_get_vertex_attrib(shape, i, SSOL_TEXCOORD, val), RES_OK); + get_uv(i, valf, (void*)&SQUARE_DESC__); + CHECK((float)val[0], valf[0]); + CHECK((float)val[1], valf[1]); + } + + CHECK(ssol_shape_get_triangles_count(NULL, NULL), RES_BAD_ARG); + CHECK(ssol_shape_get_triangles_count(shape, NULL), RES_BAD_ARG); + CHECK(ssol_shape_get_triangles_count(NULL, &n), RES_BAD_ARG); + CHECK(ssol_shape_get_triangles_count(shape, &n), RES_OK); + CHECK(n, SQUARE_NTRIS__); + + CHECK(ssol_shape_get_triangle_indices(NULL, n, NULL), RES_BAD_ARG); + CHECK(ssol_shape_get_triangle_indices(shape, n, NULL), RES_BAD_ARG); + CHECK(ssol_shape_get_triangle_indices(NULL, 0, NULL), RES_BAD_ARG); + CHECK(ssol_shape_get_triangle_indices(shape, 0, NULL), RES_BAD_ARG); + CHECK(ssol_shape_get_triangle_indices(NULL, n, ids), RES_BAD_ARG); + CHECK(ssol_shape_get_triangle_indices(shape, n, ids), RES_BAD_ARG); + CHECK(ssol_shape_get_triangle_indices(NULL, 0, ids), RES_BAD_ARG); + + FOR_EACH(i, 0, n) { + unsigned ids2[3]; + CHECK(ssol_shape_get_triangle_indices(shape, i, ids), RES_OK); + get_ids(i, ids2, (void*)&SQUARE_DESC__); + CHECK(ids[0], ids2[0]); + CHECK(ids[1], ids2[1]); + CHECK(ids[2], ids2[2]); + } + CHECK(ssol_shape_ref_put(shape), RES_OK); CHECK(ssol_shape_create_punched_surface(NULL, NULL), RES_BAD_ARG); @@ -141,6 +200,19 @@ main(int argc, char** argv) CHECK(ssol_punched_surface_setup(shape, &punched_surface), RES_BAD_ARG); quadric.data.parabol.focal = 1; + quadric.type = SSOL_QUADRIC_HYPERBOL; + quadric.data.hyperbol.real_focal = 1; + quadric.data.hyperbol.img_focal = 1; + CHECK(ssol_punched_surface_setup(shape, &punched_surface), RES_OK); + + quadric.data.hyperbol.real_focal = 0; + CHECK(ssol_punched_surface_setup(shape, &punched_surface), RES_BAD_ARG); + quadric.data.hyperbol.real_focal = 1; + + quadric.data.hyperbol.img_focal = 0; + CHECK(ssol_punched_surface_setup(shape, &punched_surface), RES_BAD_ARG); + quadric.data.hyperbol.img_focal = 1; + quadric.type = SSOL_QUADRIC_PARABOLIC_CYLINDER; quadric.data.parabolic_cylinder.focal = 1; CHECK(ssol_punched_surface_setup(shape, &punched_surface), RES_OK); @@ -150,11 +222,8 @@ main(int argc, char** argv) quadric.data.parabolic_cylinder.focal = 1; CHECK(ssol_shape_ref_put(shape), 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); diff --git a/src/test_ssol_solver1.c b/src/test_ssol_solver1.c @@ -24,7 +24,6 @@ #define PLANE_NAME SQUARE #include "test_ssol_rect_geometry.h" -#include <rsys/logger.h> #include <rsys/double33.h> #include <star/s3d.h> @@ -49,11 +48,11 @@ int main(int argc, char** argv) { struct spectrum_desc desc = {0}; - struct logger logger; struct mem_allocator allocator; struct ssol_device* dev; struct ssp_rng* rng; struct ssol_scene* scene; + struct ssol_shape* dummy; struct ssol_shape* square; struct ssol_vertex_data attribs[1] = { SSOL_VERTEX_DATA_NULL__ }; struct ssol_material *m_mtl, *m_mtl2; @@ -70,7 +69,12 @@ main(int argc, char** argv) struct ssol_spectrum* abs; struct ssol_atmosphere* atm; struct ssol_estimator* estimator; - struct ssol_estimator_status status; + struct ssol_mc_global mc_global; + struct ssol_mc_receiver mc_rcv; + struct ssol_mc_shape mc_shape; + struct ssol_mc_primitive mc_prim; + struct ssol_path path; + struct ssol_path_vertex vertex; double dir[3]; double wavelengths[3] = { 1, 2, 3 }; double intensities[3] = { 1, 0.8, 1 }; @@ -80,11 +84,12 @@ main(int argc, char** argv) double transform1[12]; /* 3x4 column major matrix */ double transform2[12]; /* 3x4 column major matrix */ double dbl; - size_t count, fcount; + size_t i, count, fcount; FILE* tmp = NULL; double m, std; double a_m, a_std; uint32_t r_id; + unsigned ntris; (void) argc, (void) argv; @@ -100,13 +105,8 @@ main(int argc, char** 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); + (NULL, &allocator, SSOL_NTHREADS_DEFAULT, 0, &dev), RES_OK); CHECK(ssp_rng_create(&allocator, &ssp_rng_threefry, &rng), RES_OK); @@ -122,18 +122,17 @@ main(int argc, char** argv) CHECK(ssol_scene_create(dev, &scene), RES_OK); CHECK(ssol_scene_attach_sun(scene, sun), RES_OK); - CHECK(ssol_solve(NULL, rng, 10, NULL, &estimator), RES_BAD_ARG); - CHECK(ssol_solve(scene, NULL, 10, NULL, &estimator), RES_BAD_ARG); - CHECK(ssol_solve(scene, rng, 0, NULL, &estimator), RES_BAD_ARG); - CHECK(ssol_solve(scene, rng, 10, NULL, &estimator), RES_BAD_ARG); - CHECK(ssol_solve(scene, rng, 10, NULL, NULL), RES_BAD_ARG); + CHECK(ssol_solve(NULL, rng, 10, 0, NULL, &estimator), RES_BAD_ARG); + CHECK(ssol_solve(scene, NULL, 10, 0, NULL, &estimator), RES_BAD_ARG); + CHECK(ssol_solve(scene, rng, 0, 0, NULL, &estimator), RES_BAD_ARG); + CHECK(ssol_solve(scene, rng, 10, 0, NULL, &estimator), RES_BAD_ARG); + CHECK(ssol_solve(scene, rng, 10, 0, NULL, NULL), RES_BAD_ARG); /* No geometry */ - CHECK(ssol_solve(scene, rng, 10, NULL, &estimator), RES_BAD_ARG); - + CHECK(ssol_solve(scene, rng, 10, 0, NULL, &estimator), RES_BAD_ARG); /* Create scene content */ - + CHECK(ssol_shape_create_mesh(dev, &dummy), RES_OK); CHECK(ssol_shape_create_mesh(dev, &square), RES_OK); attribs[0].usage = SSOL_POSITION; attribs[0].get = get_position; @@ -144,17 +143,17 @@ main(int argc, char** argv) 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_mirror_setup(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, square, m_mtl, m_mtl), RES_OK); CHECK(ssol_object_instantiate(m_object, &heliostat), RES_OK); - CHECK(ssol_instance_set_receiver(heliostat, SSOL_FRONT), RES_OK); + CHECK(ssol_instance_set_receiver(heliostat, SSOL_FRONT, 0), RES_OK); CHECK(ssol_scene_attach_instance(scene, heliostat), RES_OK); CHECK(ssol_object_instantiate(m_object, &secondary), RES_OK); - CHECK(ssol_instance_set_receiver(secondary, SSOL_FRONT), RES_OK); + CHECK(ssol_instance_set_receiver(secondary, SSOL_FRONT, 0), RES_OK); CHECK(ssol_instance_set_transform(secondary, transform1), RES_OK); CHECK(ssol_scene_attach_instance(scene, secondary), RES_OK); @@ -162,10 +161,49 @@ main(int argc, char** argv) 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, transform2), RES_OK); - CHECK(ssol_instance_set_receiver(target, SSOL_FRONT), RES_OK); + CHECK(ssol_instance_set_receiver(target, SSOL_FRONT, 0), RES_OK); CHECK(ssol_scene_attach_instance(scene, target), RES_OK); - CHECK(ssol_solve(scene, rng, 1, NULL, &estimator), RES_OK); + CHECK(ssol_solve + (scene, rng, 1, &SSOL_PATH_TRACKER_DEFAULT, NULL, &estimator), RES_OK); + + CHECK(ssol_estimator_get_tracked_paths_count(NULL, NULL), RES_BAD_ARG); + CHECK(ssol_estimator_get_tracked_paths_count(estimator, NULL), RES_BAD_ARG); + CHECK(ssol_estimator_get_tracked_paths_count(NULL, &count), RES_BAD_ARG); + CHECK(ssol_estimator_get_tracked_paths_count(estimator, &count), RES_OK); + CHECK(count, 1); + + CHECK(ssol_estimator_get_tracked_path(NULL, count, NULL), RES_BAD_ARG); + CHECK(ssol_estimator_get_tracked_path(estimator, count, NULL), RES_BAD_ARG); + CHECK(ssol_estimator_get_tracked_path(NULL, 0, NULL), RES_BAD_ARG); + CHECK(ssol_estimator_get_tracked_path(estimator, 0, NULL), RES_BAD_ARG); + CHECK(ssol_estimator_get_tracked_path(NULL, count, &path), RES_BAD_ARG); + CHECK(ssol_estimator_get_tracked_path(estimator, count, &path), RES_BAD_ARG); + CHECK(ssol_estimator_get_tracked_path(NULL, 0, &path), RES_BAD_ARG); + CHECK(ssol_estimator_get_tracked_path(estimator, 0, &path), RES_OK); + + CHECK(ssol_path_get_vertices_count(NULL, NULL), RES_BAD_ARG); + CHECK(ssol_path_get_vertices_count(&path, NULL), RES_BAD_ARG); + CHECK(ssol_path_get_vertices_count(NULL, &count), RES_BAD_ARG); + CHECK(ssol_path_get_vertices_count(&path, &count), RES_OK); + NCHECK(count, 0); + + CHECK(ssol_path_get_vertex(NULL, count, NULL), RES_BAD_ARG); + CHECK(ssol_path_get_vertex(&path, count, NULL), RES_BAD_ARG); + CHECK(ssol_path_get_vertex(NULL, 0, NULL), RES_BAD_ARG); + CHECK(ssol_path_get_vertex(&path, 0, NULL), RES_BAD_ARG); + CHECK(ssol_path_get_vertex(NULL, count, &vertex), RES_BAD_ARG); + CHECK(ssol_path_get_vertex(&path, count, &vertex), RES_BAD_ARG); + CHECK(ssol_path_get_vertex(NULL, 0, &vertex), RES_BAD_ARG); + FOR_EACH(i, 0, count) { + CHECK(ssol_path_get_vertex(&path, i, &vertex), RES_OK); + } + + CHECK(ssol_estimator_get_sampled_area(NULL, NULL), RES_BAD_ARG); + CHECK(ssol_estimator_get_sampled_area(estimator, NULL), RES_BAD_ARG); + CHECK(ssol_estimator_get_sampled_area(NULL, &dbl), RES_BAD_ARG); + CHECK(ssol_estimator_get_sampled_area(estimator, &dbl), RES_OK); + CHECK(eq_eps(dbl, 12, 1.e-6), 1); CHECK(ssol_estimator_get_count(NULL, NULL), RES_BAD_ARG); CHECK(ssol_estimator_get_count(estimator, NULL), RES_BAD_ARG); @@ -179,12 +217,9 @@ main(int argc, char** argv) CHECK(ssol_estimator_get_failed_count(estimator, &fcount), RES_OK); CHECK(fcount, 0); - #define GET_STATUS ssol_estimator_get_status - CHECK(GET_STATUS(NULL, SSOL_STATUS_MISSING, &status), RES_BAD_ARG); - CHECK(GET_STATUS(estimator, SSOL_STATUS_TYPES_COUNT__, &status), RES_BAD_ARG); - CHECK(GET_STATUS(estimator, SSOL_STATUS_MISSING, NULL), RES_BAD_ARG); - CHECK(GET_STATUS(estimator, SSOL_STATUS_MISSING, &status), RES_OK); - #undef GET_STATUS + CHECK(ssol_estimator_get_mc_global(NULL, &mc_global), RES_BAD_ARG); + CHECK(ssol_estimator_get_mc_global(estimator, NULL), RES_BAD_ARG); + CHECK(ssol_estimator_get_mc_global(estimator, &mc_global), RES_OK); CHECK(ssol_estimator_ref_get(NULL), RES_BAD_ARG); CHECK(ssol_estimator_ref_get(estimator), RES_OK); @@ -196,7 +231,7 @@ main(int argc, char** argv) CHECK(ssol_instance_sample(target, 0), RES_OK); CHECK(ssol_instance_sample(secondary, 0), RES_OK); CHECK(ssol_instance_sample(heliostat, 0), RES_OK); - CHECK(ssol_solve(scene, rng, 10, NULL, &estimator), RES_BAD_ARG); + CHECK(ssol_solve(scene, rng, 10, 0, NULL, &estimator), RES_BAD_ARG); CHECK(ssol_instance_sample(target, 1), RES_OK); CHECK(ssol_instance_sample(secondary, 1), RES_OK); @@ -204,7 +239,7 @@ main(int argc, char** argv) /* No attached sun */ CHECK(ssol_scene_detach_sun(scene, sun), RES_OK); - CHECK(ssol_solve(scene, rng, 10, NULL, &estimator), RES_BAD_ARG); + CHECK(ssol_solve(scene, rng, 10, 0, NULL, &estimator), RES_BAD_ARG); CHECK(ssol_sun_ref_put(sun), RES_OK); /* Sun with no spectrum */ @@ -212,7 +247,7 @@ main(int argc, char** argv) CHECK(ssol_sun_set_direction(sun, d3(dir, 1, 0, -1)), RES_OK); CHECK(ssol_sun_set_dni(sun, 1000), RES_OK); CHECK(ssol_scene_attach_sun(scene, sun), RES_OK); - CHECK(ssol_solve(scene, rng, 10, NULL, &estimator), RES_BAD_ARG); + CHECK(ssol_solve(scene, rng, 10, 0, NULL, &estimator), RES_BAD_ARG); CHECK(ssol_scene_detach_sun(scene, sun), RES_OK); CHECK(ssol_sun_ref_put(sun), RES_OK); @@ -221,20 +256,20 @@ main(int argc, char** argv) CHECK(ssol_sun_set_direction(sun, d3(dir, 1, 0, -1)), RES_OK); CHECK(ssol_sun_set_spectrum(sun, spectrum), RES_OK); CHECK(ssol_scene_attach_sun(scene, sun), RES_OK); - CHECK(ssol_solve(scene, rng, 10, NULL, &estimator), RES_BAD_ARG); + CHECK(ssol_solve(scene, rng, 10, 0, NULL, &estimator), RES_BAD_ARG); CHECK(ssol_sun_set_dni(sun, 1000), RES_OK); /* No receiver in scene */ - CHECK(ssol_instance_set_receiver(heliostat, 0), RES_OK); - CHECK(ssol_instance_set_receiver(secondary, 0), RES_OK); - CHECK(ssol_instance_set_receiver(target, 0), RES_OK); - CHECK(ssol_solve(scene, rng, 10, NULL, &estimator), RES_OK); - CHECK(ssol_instance_set_receiver(heliostat, SSOL_FRONT), RES_OK); - CHECK(ssol_instance_set_receiver(secondary, SSOL_FRONT), RES_OK); - CHECK(ssol_instance_set_receiver(target, SSOL_FRONT), RES_OK); + CHECK(ssol_instance_set_receiver(heliostat, 0, 0), RES_OK); + CHECK(ssol_instance_set_receiver(secondary, 0, 0), RES_OK); + CHECK(ssol_instance_set_receiver(target, 0, 0), RES_OK); + CHECK(ssol_solve(scene, rng, 10, 0, NULL, &estimator), RES_OK); + CHECK(ssol_instance_set_receiver(heliostat, SSOL_FRONT, 0), RES_OK); + CHECK(ssol_instance_set_receiver(secondary, SSOL_FRONT, 0), RES_OK); + CHECK(ssol_instance_set_receiver(target, SSOL_FRONT, 0), RES_OK); CHECK(ssol_estimator_ref_put(estimator), RES_OK); - /* Spectra mismatch */ + /* Spectra mismatch */ desc.wavelengths = mismatch; desc.wavelengths = ka; desc.count = 2; @@ -243,7 +278,7 @@ main(int argc, char** argv) CHECK(ssol_atmosphere_create_uniform(dev, &atm), RES_OK); CHECK(ssol_atmosphere_set_uniform_absorption(atm, abs), RES_OK); CHECK(ssol_scene_attach_atmosphere(scene, atm), RES_OK); - CHECK(ssol_solve(scene, rng, 10, NULL, &estimator), RES_BAD_ARG); + CHECK(ssol_solve(scene, rng, 10, 0, NULL, &estimator), RES_BAD_ARG); CHECK(ssol_scene_detach_atmosphere(scene, atm), RES_OK); CHECK(ssol_spectrum_ref_put(abs), RES_OK); CHECK(ssol_atmosphere_ref_put(atm), RES_OK); @@ -251,9 +286,9 @@ main(int argc, char** argv) /* Can sample any geometry; variance is high */ 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); +#define GET_MC_RCV ssol_estimator_get_mc_receiver +#define GET_MC_GLOBAL ssol_estimator_get_mc_global + CHECK(ssol_solve(scene, rng, N__, 0, 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__); @@ -261,47 +296,40 @@ main(int argc, char** argv) CHECK(fclose(tmp), 0); CHECK(ssol_estimator_get_failed_count(estimator, &fcount), RES_OK); CHECK(fcount, 0); - logger_print(&logger, LOG_OUTPUT, "\nIr = %g +/- %g", m, std); + printf("Ir = %g +/- %g; ", m, std); #define COS cos(PI / 4) -#define DNI 1000 +#define DNI 1000 #define DNI_cos (DNI * COS) CHECK(eq_eps(m, 4 * DNI_cos, MMAX(4 * DNI_cos * 1e-2, 2*std)), 1); #define SQR(x) ((x)*(x)) dbl = sqrt((SQR(12 * DNI_cos) / 3 - SQR(4 * DNI_cos)) / (double)count); CHECK(eq_eps(std, dbl, dbl*1e-2), 1); /* Target was sampled but shadowed by secondary */ - 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, m, 2 * dbl), 1); - CHECK(status.N, count); - CHECK(status.Nf, fcount); - 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, m, 2*status.irradiance.SE), 1); - CHECK(status.N, count); - CHECK(status.Nf, fcount); - CHECK(GET_RCV_STATUS(NULL, NULL, SSOL_BACK, NULL), RES_BAD_ARG); - CHECK(GET_RCV_STATUS(estimator, NULL, SSOL_BACK, NULL), RES_BAD_ARG); - CHECK(GET_RCV_STATUS(NULL, target, SSOL_BACK, NULL), RES_BAD_ARG); - CHECK(GET_RCV_STATUS(estimator, target, SSOL_BACK, NULL), RES_BAD_ARG); - CHECK(GET_RCV_STATUS(NULL, NULL, SSOL_BACK, &status), RES_BAD_ARG); - CHECK(GET_RCV_STATUS(estimator, NULL, SSOL_BACK, &status), RES_BAD_ARG); - CHECK(GET_RCV_STATUS(NULL, target, SSOL_BACK, &status), RES_BAD_ARG); - CHECK(GET_RCV_STATUS(estimator, target, SSOL_BACK, &status), RES_BAD_ARG); - CHECK(GET_RCV_STATUS(NULL, NULL, SSOL_FRONT, NULL), RES_BAD_ARG); - CHECK(GET_RCV_STATUS(estimator, NULL, SSOL_FRONT, NULL), RES_BAD_ARG); - CHECK(GET_RCV_STATUS(NULL, target, SSOL_FRONT, NULL), RES_BAD_ARG); - CHECK(GET_RCV_STATUS(estimator, target, SSOL_FRONT, NULL), RES_BAD_ARG); - CHECK(GET_RCV_STATUS(NULL, NULL, SSOL_FRONT, &status), RES_BAD_ARG); - CHECK(GET_RCV_STATUS(estimator, NULL, SSOL_FRONT, &status), RES_BAD_ARG); - CHECK(GET_RCV_STATUS(NULL, target, SSOL_FRONT, &status), RES_BAD_ARG); - 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-8), 1); - CHECK(eq_eps(status.irradiance.SE, std, 1e-4), 1); + CHECK(ssol_estimator_get_mc_global(estimator, &mc_global), RES_OK); + printf("Shadows = %g +/- %g; ", mc_global.shadowed.E, mc_global.shadowed.SE); + printf("Missing = %g +/- %g; ", mc_global.missing.E, mc_global.missing.SE); + CHECK(eq_eps(mc_global.shadowed.E, m, 2 * dbl), 1); + CHECK(eq_eps(mc_global.missing.E, m, 2*mc_global.missing.SE), 1); + CHECK(GET_MC_RCV(NULL, NULL, SSOL_BACK, NULL), RES_BAD_ARG); + CHECK(GET_MC_RCV(estimator, NULL, SSOL_BACK, NULL), RES_BAD_ARG); + CHECK(GET_MC_RCV(NULL, target, SSOL_BACK, NULL), RES_BAD_ARG); + CHECK(GET_MC_RCV(estimator, target, SSOL_BACK, NULL), RES_BAD_ARG); + CHECK(GET_MC_RCV(NULL, NULL, SSOL_BACK, &mc_rcv), RES_BAD_ARG); + CHECK(GET_MC_RCV(estimator, NULL, SSOL_BACK, &mc_rcv), RES_BAD_ARG); + CHECK(GET_MC_RCV(NULL, target, SSOL_BACK, &mc_rcv), RES_BAD_ARG); + CHECK(GET_MC_RCV(estimator, target, SSOL_BACK, &mc_rcv), RES_BAD_ARG); + CHECK(GET_MC_RCV(NULL, NULL, SSOL_FRONT, NULL), RES_BAD_ARG); + CHECK(GET_MC_RCV(estimator, NULL, SSOL_FRONT, NULL), RES_BAD_ARG); + CHECK(GET_MC_RCV(NULL, target, SSOL_FRONT, NULL), RES_BAD_ARG); + CHECK(GET_MC_RCV(estimator, target, SSOL_FRONT, NULL), RES_BAD_ARG); + CHECK(GET_MC_RCV(NULL, NULL, SSOL_FRONT, &mc_rcv), RES_BAD_ARG); + CHECK(GET_MC_RCV(estimator, NULL, SSOL_FRONT, &mc_rcv), RES_BAD_ARG); + CHECK(GET_MC_RCV(NULL, target, SSOL_FRONT, &mc_rcv), RES_BAD_ARG); + CHECK(GET_MC_RCV(estimator, target, SSOL_FRONT, &mc_rcv), RES_OK); + printf("Ir(target) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, m, 1e-8), 1); + CHECK(eq_eps(mc_rcv.integrated_irradiance.SE, std, 1e-4), 1); CHECK(ssol_estimator_ref_put(estimator), RES_OK); /* Sample primary mirror only; variance is low */ @@ -309,27 +337,24 @@ main(int argc, char** argv) CHECK(ssol_instance_sample(secondary, 0), RES_OK); NCHECK(tmp = tmpfile(), 0); - CHECK(ssol_solve(scene, rng, N__, tmp, &estimator), RES_OK); + CHECK(ssol_solve(scene, rng, N__, 0, tmp, &estimator), 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", m, std); + printf("Ir = %g +/- %g; ", m, std); CHECK(eq_eps(m, 4 * DNI_cos, MMAX(4 * DNI_cos * 1e-2, std)), 1); CHECK(eq_eps(std, 0, 1e-4), 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(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-8), 1); - CHECK(eq_eps(status.irradiance.SE, std, 1e-4), 1); + CHECK(ssol_estimator_get_mc_global(estimator, &mc_global), RES_OK); + printf("Shadows = %g +/- %g; ", mc_global.shadowed.E, mc_global.shadowed.SE); + printf("Missing = %g +/- %g; ", mc_global.missing.E, mc_global.missing.SE); + CHECK(eq_eps(mc_global.shadowed.E, 0, 1e-4), 1); + CHECK(eq_eps(mc_global.missing.E, 0, 1e-4), 1); + CHECK(GET_MC_RCV(estimator, target, SSOL_FRONT, &mc_rcv), RES_OK); + printf("Ir(target) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, m, 1e-8), 1); + CHECK(eq_eps(mc_rcv.integrated_irradiance.SE, std, 1e-4), 1); CHECK(ssol_estimator_ref_put(estimator), RES_OK); /* Check atmosphere model; with no absorption result is unchanged */ @@ -343,30 +368,27 @@ main(int argc, char** argv) CHECK(ssol_scene_attach_atmosphere(scene, atm), RES_OK); NCHECK(tmp = tmpfile(), 0); - CHECK(ssol_solve(scene, rng, N__, tmp, &estimator), RES_OK); + CHECK(ssol_solve(scene, rng, N__, 0, tmp, &estimator), 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", m, std); + printf("Ir = %g +/- %g; ", m, std); CHECK(eq_eps(m, 4 * DNI_cos, MMAX(4 * DNI_cos * 1e-2, std)), 1); CHECK(eq_eps(std, 0, 1e-4), 1); CHECK(ssol_scene_detach_atmosphere(scene, atm), RES_OK); CHECK(ssol_spectrum_ref_put(abs), RES_OK); CHECK(ssol_atmosphere_ref_put(atm), RES_OK); - 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(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-8), 1); - CHECK(eq_eps(status.irradiance.SE, std, 1e-4), 1); + CHECK(ssol_estimator_get_mc_global(estimator, &mc_global), RES_OK); + printf("Shadows = %g +/- %g; ", mc_global.shadowed.E, mc_global.shadowed.SE); + printf("Missing = %g +/- %g; ", mc_global.missing.E, mc_global.missing.SE); + CHECK(eq_eps(mc_global.shadowed.E, 0, 1e-4), 1); + CHECK(eq_eps(mc_global.missing.E, 0, 1e-4), 1); + CHECK(GET_MC_RCV(estimator, target, SSOL_FRONT, &mc_rcv), RES_OK); + printf("Ir(target) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, m, 1e-8), 1); + CHECK(eq_eps(mc_rcv.integrated_irradiance.SE, std, 1e-4), 1); CHECK(ssol_estimator_ref_put(estimator), RES_OK); /* Check atmosphere model and imperfect mirror: there are losses */ @@ -376,12 +398,12 @@ main(int argc, char** argv) shader.normal = get_shader_normal; shader.reflectivity = get_shader_reflectivity_2; shader.roughness = get_shader_roughness; - CHECK(ssol_mirror_set_shader(m_mtl2, &shader), RES_OK); + CHECK(ssol_mirror_setup(m_mtl2, &shader), RES_OK); CHECK(ssol_object_create(dev, &m_object2), RES_OK); CHECK(ssol_object_add_shaded_shape(m_object2, square, m_mtl2, m_mtl2), RES_OK); CHECK(ssol_object_instantiate(m_object2, &heliostat2), RES_OK); - CHECK(ssol_instance_set_receiver(heliostat2, SSOL_FRONT), RES_OK); + CHECK(ssol_instance_set_receiver(heliostat2, SSOL_FRONT, 0), RES_OK); CHECK(ssol_scene_attach_instance(scene, heliostat2), RES_OK); #define KA 0.03 @@ -391,52 +413,91 @@ main(int argc, char** argv) CHECK(ssol_atmosphere_create_uniform(dev, &atm), RES_OK); CHECK(ssol_atmosphere_set_uniform_absorption(atm, abs), RES_OK); CHECK(ssol_scene_attach_atmosphere(scene, atm), RES_OK); + CHECK(ssol_instance_set_receiver(target, SSOL_FRONT, 1), RES_OK); NCHECK(tmp = tmpfile(), 0); - CHECK(ssol_solve(scene, rng, N__, tmp, &estimator), RES_OK); + CHECK(ssol_solve(scene, rng, N__, 0, tmp, &estimator), RES_OK); CHECK(ssol_estimator_get_count(estimator, &count), RES_OK); CHECK(count, N__); CHECK(pp_sum(tmp, (int32_t)r_id, count, &a_m, &a_std), RES_OK); CHECK(fclose(tmp), 0); - logger_print(&logger, LOG_OUTPUT, "\nIr = %g +/- %g", a_m, a_std); + printf("Ir = %g +/- %g; ", a_m, a_std); #define K (exp(-KA * 4 * sqrt(2))) - CHECK(eq_eps(a_m, REFLECTIVITY * 4 * K * DNI_cos, + CHECK(eq_eps(a_m, REFLECTIVITY * 4 * K * DNI_cos, MMAX(4 * K * DNI_cos * 1e-1, a_std)), 1); CHECK(eq_eps(a_std, 0, 1e-4), 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(GET_RCV_STATUS(estimator, target, SSOL_FRONT, &status), RES_OK); - logger_print(&logger, LOG_OUTPUT, - "Ir(target) = %g +/- %g (%.2g %%)", - status.irradiance.E, status.irradiance.SE, 100 * status.irradiance.E / m); - logger_print(&logger, LOG_OUTPUT, - "Atmospheric Loss(target) = %g +/- %g (%.2g %%)", - status.absorptivity_loss.E, status.absorptivity_loss.SE, - 100 * status.absorptivity_loss.E / m); - logger_print(&logger, LOG_OUTPUT, - "Reflectivity Loss(target) = %g +/- %g (%.2g %%)", - status.reflectivity_loss.E, status.reflectivity_loss.SE, - 100 * status.reflectivity_loss.E / m); - logger_print(&logger, LOG_OUTPUT, - "Cos Loss(target) = %g +/- %g (%.2g %%)", - status.cos_loss.E, status.cos_loss.SE, 100 * status.cos_loss.E / m); - CHECK(eq_eps(status.irradiance.E, a_m, 1e-8), 1); - CHECK(eq_eps(status.irradiance.SE, a_std, 1e-4), 1); - CHECK(eq_eps(status.irradiance.E + status.absorptivity_loss.E - + status.reflectivity_loss.E, m, 1e-8), 1); - CHECK(eq_eps(status.irradiance.E + status.absorptivity_loss.E - + status.reflectivity_loss.E + status.cos_loss.E, 4 * DNI, 1e-8), 1); - CHECK(eq_eps(status.cos_loss.E / (4 * DNI), 1 - COS, 1e-8), 1); + CHECK(ssol_estimator_get_mc_global(estimator, &mc_global), RES_OK); + printf("Shadows = %g +/- %g; ", mc_global.shadowed.E, mc_global.shadowed.SE); + printf("Missing = %g +/- %g\n", mc_global.missing.E, mc_global.missing.SE); + CHECK(eq_eps(mc_global.shadowed.E, 0, 1e-4), 1); + CHECK(eq_eps(mc_global.missing.E, 0, 1e-4), 1); + CHECK(GET_MC_RCV(estimator, target, SSOL_FRONT, &mc_rcv), RES_OK); + printf + ("\tIr(target) = %g +/- %g (%.2g %%)\n", + mc_rcv.integrated_irradiance.E, + mc_rcv.integrated_irradiance.SE, + 100 * mc_rcv.integrated_irradiance.E / m); + printf + ("\tAtmospheric Loss(target) = %g +/- %g (%.2g %%)\n", + mc_rcv.absorptivity_loss.E, + mc_rcv.absorptivity_loss.SE, + 100 * mc_rcv.absorptivity_loss.E / m); + printf + ("\tReflectivity Loss(target) = %g +/- %g (%.2g %%)\n", + mc_rcv.reflectivity_loss.E, + mc_rcv.reflectivity_loss.SE, + 100 * mc_rcv.reflectivity_loss.E / m); + printf + ("\tCos Loss(target) = %g +/- %g (%.2g %%)\n", + mc_rcv.cos_loss.E, + mc_rcv.cos_loss.SE, + 100 * mc_rcv.cos_loss.E / m); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, a_m, 1e-8), 1); + CHECK(eq_eps(mc_rcv.integrated_irradiance.SE, a_std, 1e-4), 1); + CHECK(eq_eps + ( mc_rcv.integrated_irradiance.E + + mc_rcv.absorptivity_loss.E + + mc_rcv.reflectivity_loss.E, m, 1e-8), 1); + CHECK(eq_eps + ( mc_rcv.integrated_irradiance.E + + mc_rcv.absorptivity_loss.E + + mc_rcv.reflectivity_loss.E + + mc_rcv.cos_loss.E, 4 * DNI, 1e-8), 1); + CHECK(eq_eps(mc_rcv.cos_loss.E / (4 * DNI), 1 - COS, 1e-8), 1); + + CHECK(ssol_mc_receiver_get_mc_shape(NULL, NULL, NULL), RES_BAD_ARG); + CHECK(ssol_mc_receiver_get_mc_shape(&mc_rcv, NULL, NULL), RES_BAD_ARG); + CHECK(ssol_mc_receiver_get_mc_shape(NULL, square, NULL), RES_BAD_ARG); + CHECK(ssol_mc_receiver_get_mc_shape(&mc_rcv, square, NULL), RES_BAD_ARG); + CHECK(ssol_mc_receiver_get_mc_shape(NULL, NULL, &mc_shape), RES_BAD_ARG); + CHECK(ssol_mc_receiver_get_mc_shape(&mc_rcv, NULL, &mc_shape), RES_BAD_ARG); + CHECK(ssol_mc_receiver_get_mc_shape(NULL, square, &mc_shape), RES_BAD_ARG); + CHECK(ssol_mc_receiver_get_mc_shape(&mc_rcv, dummy, &mc_shape), RES_BAD_ARG); + CHECK(ssol_mc_receiver_get_mc_shape(&mc_rcv, square, &mc_shape), RES_OK); + + CHECK(ssol_shape_get_triangles_count(square, &ntris), RES_OK); + NCHECK(ntris, 0); + + CHECK(ssol_mc_shape_get_mc_primitive(NULL, ntris, NULL), RES_BAD_ARG); + CHECK(ssol_mc_shape_get_mc_primitive(&mc_shape, ntris, NULL), RES_BAD_ARG); + CHECK(ssol_mc_shape_get_mc_primitive(NULL, 0, NULL), RES_BAD_ARG); + CHECK(ssol_mc_shape_get_mc_primitive(&mc_shape, 0, NULL), RES_BAD_ARG); + CHECK(ssol_mc_shape_get_mc_primitive(NULL, ntris, &mc_prim), RES_BAD_ARG); + CHECK(ssol_mc_shape_get_mc_primitive(&mc_shape, ntris, &mc_prim), RES_BAD_ARG); + CHECK(ssol_mc_shape_get_mc_primitive(NULL, 0, &mc_prim), RES_BAD_ARG); + + dbl = 0; + FOR_EACH(i, 0, ntris) { + CHECK(ssol_mc_shape_get_mc_primitive(&mc_shape, (unsigned)i, &mc_prim), RES_OK); + dbl += mc_prim.integrated_irradiance.E; + } + + CHECK(eq_eps(dbl, a_m, 1.e-6), 1); CHECK(ssol_estimator_ref_put(estimator), RES_OK); CHECK(ssol_scene_detach_instance(scene, heliostat2), RES_OK); CHECK(ssol_scene_attach_instance(scene, heliostat), RES_OK); + CHECK(ssol_instance_set_receiver(target, SSOL_FRONT, 0), RES_OK); /* Check a monochromatic sun */ desc.wavelengths = &mono; @@ -455,30 +516,25 @@ main(int argc, char** argv) desc.count = 2; CHECK(ssol_spectrum_setup(abs, get_wlen, 2, &desc), RES_OK); NCHECK(tmp = tmpfile(), 0); - CHECK(ssol_solve(scene, rng, N__, tmp, &estimator), RES_OK); + CHECK(ssol_solve(scene, rng, N__, 0, tmp, &estimator), 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", m, std); + printf("Ir = %g +/- %g; ", m, std); #define K2 (exp(-0.121 * 4 * sqrt(2))) CHECK(eq_eps(m, 4 * K2 * DNI_cos, MMAX(4 * K2 * DNI_cos * 1e-4, std)), 1); CHECK(eq_eps(std, 0, 1e-4), 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(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-8), 1); - CHECK(eq_eps(status.irradiance.SE, std, 1e-4), 1); -#undef GET_STATUS -#undef GET_RCV_STATUS + CHECK(ssol_estimator_get_mc_global(estimator, &mc_global), RES_OK); + printf("Shadows = %g +/- %g; ", mc_global.shadowed.E, mc_global.shadowed.SE); + printf("Missing = %g +/- %g; ", mc_global.missing.E, mc_global.missing.SE); + CHECK(eq_eps(mc_global.shadowed.E, 0, 1e-4), 1); + CHECK(eq_eps(mc_global.missing.E, 0, 1e-4), 1); + CHECK(GET_MC_RCV(estimator, target, SSOL_FRONT, &mc_rcv), RES_OK); + printf("Ir(target) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, m, 1e-8), 1); + CHECK(eq_eps(mc_rcv.integrated_irradiance.SE, std, 1e-4), 1); /* Free data */ CHECK(ssol_instance_ref_put(heliostat2), RES_OK); @@ -489,20 +545,19 @@ status.irradiance.E, status.irradiance.SE); 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(dummy), RES_OK); CHECK(ssol_shape_ref_put(square), RES_OK); CHECK(ssol_material_ref_put(m_mtl), RES_OK); CHECK(ssol_material_ref_put(v_mtl), 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(abs), RES_OK); CHECK(ssol_atmosphere_ref_put(atm), RES_OK); CHECK(ssol_estimator_ref_put(estimator), RES_OK); CHECK(ssol_spectrum_ref_put(spectrum), RES_OK); CHECK(ssol_sun_ref_put(sun), RES_OK); CHECK(ssol_sun_ref_put(sun_mono), RES_OK); - - logger_release(&logger); + CHECK(ssp_rng_ref_put(rng), RES_OK); check_memory_allocator(&allocator); mem_shutdown_proxy_allocator(&allocator); diff --git a/src/test_ssol_solver2.c b/src/test_ssol_solver2.c @@ -32,7 +32,6 @@ #define HALF_Y 1 #include "test_ssol_rect2D_geometry.h" -#include <rsys/logger.h> #include <rsys/double33.h> #include <star/s3d.h> @@ -52,7 +51,6 @@ get_wlen(const size_t i, double* wlen, double* data, void* ctx) int main(int argc, char** argv) { - struct logger logger; struct mem_allocator allocator; struct ssol_device* dev; struct ssp_rng* rng; @@ -77,7 +75,8 @@ main(int argc, char** argv) struct ssol_sun* sun; struct ssol_spectrum* spectrum; struct ssol_estimator* estimator; - struct ssol_estimator_status status; + struct ssol_mc_global mc_global; + struct ssol_mc_receiver mc_rcv; double dir[3]; double transform1[12]; /* 3x4 column major matrix */ double transform2[12]; /* 3x4 column major matrix */ @@ -104,13 +103,8 @@ main(int argc, char** 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); + (NULL, &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); @@ -151,15 +145,15 @@ main(int argc, char** argv) 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_mirror_setup(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, rect, m_mtl, m_mtl), RES_OK); CHECK(ssol_object_instantiate(m_object, &heliostat1), RES_OK); CHECK(ssol_object_instantiate(m_object, &heliostat2), RES_OK); - CHECK(ssol_instance_set_receiver(heliostat1, SSOL_FRONT), RES_OK); - CHECK(ssol_instance_set_receiver(heliostat2, SSOL_FRONT), RES_OK); + CHECK(ssol_instance_set_receiver(heliostat1, SSOL_FRONT, 0), RES_OK); + CHECK(ssol_instance_set_receiver(heliostat2, SSOL_FRONT, 0), RES_OK); transform3[9] = -0.5; /* -0.5 offset along X axis */ CHECK(ssol_instance_set_transform(heliostat1, transform3), RES_OK); transform3[9] = +0.5; /* +0.5 offset along X axis */ @@ -170,7 +164,7 @@ main(int argc, char** argv) CHECK(ssol_object_create(dev, &s_object), RES_OK); CHECK(ssol_object_add_shaded_shape(s_object, quad_square, m_mtl, m_mtl), RES_OK); CHECK(ssol_object_instantiate(s_object, &secondary), RES_OK); - CHECK(ssol_instance_set_receiver(secondary, SSOL_FRONT), RES_OK); + CHECK(ssol_instance_set_receiver(secondary, SSOL_FRONT, 0), RES_OK); CHECK(ssol_instance_set_transform(secondary, transform1), RES_OK); CHECK(ssol_instance_sample(secondary, 0), RES_OK); CHECK(ssol_scene_attach_instance(scene, secondary), RES_OK); @@ -179,44 +173,38 @@ main(int argc, char** argv) 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, transform2), RES_OK); - CHECK(ssol_instance_set_receiver(target, SSOL_FRONT), RES_OK); + CHECK(ssol_instance_set_receiver(target, SSOL_FRONT, 0), 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); +#define GET_MC_RCV ssol_estimator_get_mc_receiver + CHECK(ssol_solve(scene, rng, N__, 0, 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); + printf("Ir = %g +/- %g\n", m, std); #define DNI_cos (1000 * cos(PI / 4)) CHECK(eq_eps(m, 4 * DNI_cos, 4 * DNI_cos * 1e-4), 1); #define SQR(x) ((x)*(x)) CHECK(eq_eps(std, 0, 1e-4), 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(GET_RCV_STATUS(estimator, heliostat1, SSOL_BACK, &status), RES_BAD_ARG); - CHECK(GET_RCV_STATUS(estimator, heliostat1, SSOL_FRONT, &status), RES_OK); - logger_print(&logger, LOG_OUTPUT, "Ir(heliostat1) = %g +/- %g", status.irradiance.E, status.irradiance.SE); - CHECK(GET_RCV_STATUS(estimator, heliostat2, SSOL_FRONT, &status), RES_OK); - logger_print(&logger, LOG_OUTPUT, "Ir(heliostat2) = %g +/- %g", status.irradiance.E, status.irradiance.SE); - CHECK(GET_RCV_STATUS(estimator, secondary, SSOL_FRONT, &status), RES_OK); - logger_print(&logger, LOG_OUTPUT, "Ir(secondary) = %g +/- %g", status.irradiance.E, status.irradiance.SE); - 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-8), 1); - CHECK(eq_eps(status.irradiance.SE, std, 1e-4), 1); -#undef GET_STATUS -#undef GET_RCV_STATUS + CHECK(ssol_estimator_get_mc_global(estimator, &mc_global), RES_OK); + printf("Shadows = %g +/- %g\n", mc_global.shadowed.E, mc_global.shadowed.SE); + printf("Missing = %g +/- %g", mc_global.missing.E, mc_global.missing.SE); + CHECK(eq_eps(mc_global.shadowed.E, 0, 1e-4), 1); + CHECK(eq_eps(mc_global.missing.E, 0, 1e-4), 1); + CHECK(GET_MC_RCV(estimator, heliostat1, SSOL_BACK, &mc_rcv), RES_BAD_ARG); + CHECK(GET_MC_RCV(estimator, secondary, SSOL_FRONT, &mc_rcv), RES_OK); + printf("Ir(secondary) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(GET_MC_RCV(estimator, target, SSOL_FRONT, &mc_rcv), RES_OK); + printf("Ir(target) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, m, 1e-8), 1); + CHECK(eq_eps(mc_rcv.integrated_irradiance.SE, std, 1e-4), 1); /* Free data */ CHECK(ssol_instance_ref_put(heliostat1), RES_OK); @@ -238,8 +226,6 @@ main(int argc, char** argv) 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); diff --git a/src/test_ssol_solver2b.c b/src/test_ssol_solver2b.c @@ -32,7 +32,6 @@ #define HALF_Y 1 #include "test_ssol_rect2D_geometry.h" -#include <rsys/logger.h> #include <rsys/double33.h> #include <star/s3d.h> @@ -52,7 +51,6 @@ get_wlen(const size_t i, double* wlen, double* data, void* ctx) int main(int argc, char** argv) { - struct logger logger; struct mem_allocator allocator; struct ssol_device* dev; struct ssp_rng* rng; @@ -77,7 +75,8 @@ main(int argc, char** argv) struct ssol_sun* sun; struct ssol_spectrum* spectrum; struct ssol_estimator* estimator; - struct ssol_estimator_status status; + struct ssol_mc_global mc_global; + struct ssol_mc_receiver mc_rcv; double dir[3]; double transform1[12]; /* 3x4 column major matrix */ double transform2[12]; /* 3x4 column major matrix */ @@ -86,7 +85,6 @@ main(int argc, char** argv) FILE* tmp; double m, std; uint32_t r_id; - (void) argc, (void) argv; d33_splat(transform1, 0); @@ -104,13 +102,8 @@ main(int argc, char** 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); + (NULL, &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); @@ -122,7 +115,7 @@ main(int argc, char** argv) CHECK(ssol_scene_create(dev, &scene), RES_OK); CHECK(ssol_scene_attach_sun(scene, sun), RES_OK); - /* create scene content */ + /* Create scene content */ CHECK(ssol_shape_create_mesh(dev, &rect), RES_OK); attribs[0].usage = SSOL_POSITION; @@ -156,15 +149,15 @@ main(int argc, char** argv) 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_mirror_setup(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_rect, m_mtl, m_mtl), RES_OK); CHECK(ssol_object_instantiate(m_object, &heliostat1), RES_OK); CHECK(ssol_object_instantiate(m_object, &heliostat2), RES_OK); - CHECK(ssol_instance_set_receiver(heliostat1, SSOL_FRONT), RES_OK); - CHECK(ssol_instance_set_receiver(heliostat2, SSOL_FRONT), RES_OK); + CHECK(ssol_instance_set_receiver(heliostat1, SSOL_FRONT, 0), RES_OK); + CHECK(ssol_instance_set_receiver(heliostat2, SSOL_FRONT, 0), RES_OK); transform3[9] = -0.5; /* -0.5 offset along X axis */ CHECK(ssol_instance_set_transform(heliostat1, transform3), RES_OK); transform3[9] = +0.5; /* +0.5 offset along X axis */ @@ -175,7 +168,7 @@ main(int argc, char** argv) CHECK(ssol_object_create(dev, &s_object), RES_OK); CHECK(ssol_object_add_shaded_shape(s_object, quad_square, m_mtl, m_mtl), RES_OK); CHECK(ssol_object_instantiate(s_object, &secondary), RES_OK); - CHECK(ssol_instance_set_receiver(secondary, SSOL_FRONT), RES_OK); + CHECK(ssol_instance_set_receiver(secondary, SSOL_FRONT, 0), RES_OK); CHECK(ssol_instance_set_transform(secondary, transform1), RES_OK); CHECK(ssol_instance_sample(secondary, 0), RES_OK); CHECK(ssol_scene_attach_instance(scene, secondary), RES_OK); @@ -184,39 +177,37 @@ main(int argc, char** argv) CHECK(ssol_object_add_shaded_shape(t_object, rect, v_mtl, v_mtl), RES_OK); CHECK(ssol_object_instantiate(t_object, &target), RES_OK); CHECK(ssol_instance_set_transform(target, transform2), RES_OK); - CHECK(ssol_instance_set_receiver(target, SSOL_FRONT), RES_OK); + CHECK(ssol_instance_set_receiver(target, SSOL_FRONT, 0), 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__ 50000 -#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_solve(scene, rng, N__, 0, 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); + printf("Ir = %g +/- %g\n", m, std); #define DNI_cos (1000 * cos(PI / 4)) CHECK(eq_eps(m, 2 * DNI_cos, MMAX(2 * DNI_cos * 1e-2, std)), 1); #define SQR(x) ((x)*(x)) CHECK(eq_eps(std, sqrt((SQR(4 * DNI_cos) / 2 - SQR(2 * DNI_cos)) / (double)count), 1e-3), 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-8), 1); - CHECK(eq_eps(status.irradiance.SE, std, 1e-4), 1); -#undef GET_STATUS -#undef GET_RCV_STATUS + CHECK(ssol_estimator_get_mc_global(estimator, &mc_global), RES_OK); + printf("Shadows = %g +/- %g\n", mc_global.shadowed.E, mc_global.shadowed.SE); + printf("Missing = %g +/- %g\n", mc_global.missing.E, mc_global.missing.SE); + CHECK(eq_eps(mc_global.shadowed.E, 0, 1e-4), 1); + CHECK(eq_eps(mc_global.missing.E, 0, 1e-4), 1); + CHECK(ssol_estimator_get_mc_receiver + (estimator, target, SSOL_FRONT, &mc_rcv), RES_OK); + printf("Ir(target) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, m, 1e-8), 1); + CHECK(eq_eps(mc_rcv.integrated_irradiance.SE, std, 1e-4), 1); + CHECK(ssol_estimator_get_failed_count(estimator, &count), RES_OK); + CHECK(count, 0); /* Free data */ CHECK(ssol_instance_ref_put(heliostat1), RES_OK); @@ -238,8 +229,6 @@ main(int argc, char** argv) 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); diff --git a/src/test_ssol_solver3.c b/src/test_ssol_solver3.c @@ -27,7 +27,6 @@ #define HALF_Y 10 #include "test_ssol_rect2D_geometry.h" -#include <rsys/logger.h> #include <rsys/double33.h> #include <star/s3d.h> @@ -47,7 +46,6 @@ get_wlen(const size_t i, double* wlen, double* data, void* ctx) int main(int argc, char** argv) { - struct logger logger; struct mem_allocator allocator; struct ssol_device* dev; struct ssp_rng* rng; @@ -68,14 +66,14 @@ main(int argc, char** argv) struct ssol_sun* sun; struct ssol_spectrum* spectrum; struct ssol_estimator* estimator; - struct ssol_estimator_status status; + struct ssol_mc_global mc_global; + struct ssol_mc_receiver mc_rcv; double dir[3]; double transform[12]; /* 3x4 column major matrix */ size_t count; FILE* tmp; double m, std; uint32_t r_id; - (void) argc, (void) argv; d3_splat(transform + 9, 0); @@ -84,13 +82,8 @@ main(int argc, char** 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); + (NULL, &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); @@ -125,52 +118,50 @@ main(int argc, char** argv) 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_mirror_setup(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, m_mtl), RES_OK); CHECK(ssol_object_instantiate(m_object, &heliostat), RES_OK); - CHECK(ssol_instance_set_receiver(heliostat, SSOL_FRONT), RES_OK); + CHECK(ssol_instance_set_receiver(heliostat, SSOL_FRONT, 0), RES_OK); CHECK(ssol_scene_attach_instance(scene, heliostat), RES_OK); 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_set_receiver(target, SSOL_FRONT, 0), 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__ 20000 -#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_solve(scene, rng, N__, 0, 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); + printf("Ir = %g +/- %g\n", m, std); #define DNI_cos (1000 * cos(PI / 4)) CHECK(eq_eps(m, 4 * DNI_cos, 4 * DNI_cos * 2e-1), 1); #define SQR(x) ((x)*(x)) CHECK(eq_eps(std, sqrt((SQR(400*DNI_cos) / 100 - SQR(4*DNI_cos)) / (double)count), 20), 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-8), 1); - CHECK(eq_eps(status.irradiance.SE, std, 1e-4), 1); -#undef GET_STATUS -#undef GET_RCV_STATUS + CHECK(ssol_estimator_get_mc_global(estimator, &mc_global), RES_OK); + printf("Shadows = %g +/- %g\n", mc_global.shadowed.E, mc_global.shadowed.SE); + printf("Missing = %g +/- %g\n", mc_global.missing.E, mc_global.missing.SE); + CHECK(eq_eps(mc_global.shadowed.E, 0, 1e-4), 1); + CHECK(eq_eps(mc_global.missing.E, 0, 1e-4), 1); + CHECK(ssol_estimator_get_mc_receiver + (estimator, target, SSOL_FRONT, &mc_rcv), RES_OK); + printf("Ir(target) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, m, 1e-8), 1); + CHECK(eq_eps(mc_rcv.integrated_irradiance.SE, std, 1e-4), 1); + CHECK(ssol_estimator_get_failed_count(estimator, &count), RES_OK); + CHECK(count, 0); /* Free data */ CHECK(ssol_instance_ref_put(heliostat), RES_OK); @@ -188,8 +179,6 @@ main(int argc, char** argv) 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); diff --git a/src/test_ssol_solver4.c b/src/test_ssol_solver4.c @@ -27,7 +27,6 @@ #define HALF_Y 10 #include "test_ssol_rect2D_geometry.h" -#include <rsys/logger.h> #include <rsys/double33.h> #include <star/s3d.h> @@ -47,7 +46,6 @@ get_wlen(const size_t i, double* wlen, double* data, void* ctx) int main(int argc, char** argv) { - struct logger logger; struct mem_allocator allocator; struct ssol_device* dev; struct ssp_rng* rng; @@ -69,7 +67,8 @@ main(int argc, char** argv) struct ssol_sun* sun; struct ssol_spectrum* spectrum; struct ssol_estimator* estimator; - struct ssol_estimator_status status; + struct ssol_mc_global mc_global; + struct ssol_mc_receiver mc_rcv; double dir[3]; double transform[12]; /* 3x4 column major matrix */ size_t count; @@ -85,13 +84,8 @@ main(int argc, char** 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); + (NULL, &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); @@ -103,7 +97,7 @@ main(int argc, char** argv) CHECK(ssol_scene_create(dev, &scene), RES_OK); CHECK(ssol_scene_attach_sun(scene, sun), RES_OK); - /* create scene content */ + /* Create scene content */ CHECK(ssol_shape_create_mesh(dev, &square), RES_OK); attribs[0].usage = SSOL_POSITION; @@ -127,7 +121,7 @@ main(int argc, char** argv) 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_mirror_setup(m_mtl, &shader), RES_OK); CHECK(ssol_material_create_virtual(dev, &v_mtl), RES_OK); CHECK(ssol_object_create(dev, &m_object), RES_OK); @@ -139,21 +133,20 @@ main(int argc, char** argv) CHECK(ssol_object_add_shaded_shape(t_object, square, v_mtl, v_mtl), RES_OK); CHECK(ssol_object_instantiate(t_object, &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_set_receiver(target1, SSOL_FRONT, 0), RES_OK); CHECK(ssol_instance_sample(target1, 0), RES_OK); CHECK(ssol_scene_attach_instance(scene, target1), RES_OK); CHECK(ssol_object_instantiate(t_object, &target2), RES_OK); transform[11] += 1.e-4; CHECK(ssol_instance_set_transform(target2, transform), RES_OK); - CHECK(ssol_instance_set_receiver(target2, SSOL_FRONT), RES_OK); + CHECK(ssol_instance_set_receiver(target2, SSOL_FRONT, 0), RES_OK); CHECK(ssol_instance_sample(target2, 0), RES_OK); CHECK(ssol_scene_attach_instance(scene, target2), 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); +#define GET_MC_RCV ssol_estimator_get_mc_receiver + CHECK(ssol_solve(scene, rng, N__, 0, tmp, &estimator), RES_OK); CHECK(ssol_instance_get_id(target1, &r_id1), RES_OK); CHECK(ssol_instance_get_id(target2, &r_id2), RES_OK); CHECK(ssol_estimator_get_count(estimator, &count), RES_OK); @@ -161,29 +154,29 @@ main(int argc, char** argv) CHECK(pp_sum(tmp, (int32_t)r_id1, count, &m1, &std1), RES_OK); CHECK(pp_sum(tmp, (int32_t)r_id2, count, &m2, &std2), RES_OK); CHECK(fclose(tmp), 0); - logger_print(&logger, LOG_OUTPUT, "\nIr = %g +/- %g\n", m1, std1); + printf("Ir = %g +/- %g\n", m1, std1); #define DNI_cos (1000 * cos(0)) CHECK(eq_eps(m1, 400 * DNI_cos, 400 * DNI_cos * 1e-4), 1); CHECK(eq_eps(std1, 0, 1), 1); CHECK(m1, m2); CHECK(std1, std2); - 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, 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, m1, 1e-8), 1); - CHECK(eq_eps(status.irradiance.SE, std1, 1e-4), 1); - CHECK(GET_RCV_STATUS(estimator, target2, SSOL_FRONT, &status), RES_OK); - logger_print(&logger, LOG_OUTPUT, "Ir(target2) = %g +/- %g", status.irradiance.E, status.irradiance.SE); - CHECK(eq_eps(status.irradiance.E, m2, 1e-8), 1); - CHECK(eq_eps(status.irradiance.SE, std2, 1e-4), 1); -#undef GET_STATUS -#undef GET_RCV_STATUS + CHECK(ssol_estimator_get_mc_global(estimator, &mc_global), RES_OK); + printf("Shadows = %g +/- %g\n", mc_global.shadowed.E, mc_global.shadowed.SE); + printf("Missing = %g +/- %g\n", mc_global.missing.E, mc_global.missing.SE); + CHECK(eq_eps(mc_global.shadowed.E, 0, 1e-4), 1); + CHECK(eq_eps(mc_global.missing.E, 0, 1e-4), 1); + CHECK(GET_MC_RCV(estimator, target1, SSOL_FRONT, &mc_rcv), RES_OK); + printf("Ir(target1) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, m1, 1e-8), 1); + CHECK(eq_eps(mc_rcv.integrated_irradiance.SE, std1, 1e-4), 1); + CHECK(GET_MC_RCV(estimator, target2, SSOL_FRONT, &mc_rcv), RES_OK); + printf("Ir(target2) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, m2, 1e-8), 1); + CHECK(eq_eps(mc_rcv.integrated_irradiance.SE, std2, 1e-4), 1); + CHECK(ssol_estimator_get_failed_count(estimator, &count), RES_OK); + CHECK(count, 0); /* Free data */ CHECK(ssol_instance_ref_put(heliostat), RES_OK); @@ -202,8 +195,6 @@ main(int argc, char** argv) 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); diff --git a/src/test_ssol_solver5.c b/src/test_ssol_solver5.c @@ -27,7 +27,6 @@ #define HALF_Y 10 #include "test_ssol_rect2D_geometry.h" -#include <rsys/logger.h> #include <rsys/double33.h> #include <star/s3d.h> @@ -47,7 +46,6 @@ get_wlen(const size_t i, double* wlen, double* data, void* ctx) int main(int argc, char** argv) { - struct logger logger; struct mem_allocator allocator; struct ssol_device* dev; struct ssp_rng* rng; @@ -68,15 +66,16 @@ main(int argc, char** argv) struct ssol_sun* sun; struct ssol_spectrum* spectrum; struct ssol_estimator* estimator; - struct ssol_estimator_status status; + struct ssol_mc_global mc_global; + struct ssol_mc_receiver mc_rcv; double dir[3]; double transform[12]; /* 3x4 column major matrix */ size_t count; FILE* tmp; double m, std; uint32_t r_id; - (void) argc, (void) argv; + #define FOCAL 10 d3_splat(transform + 9, 0); d33_rotation_pitch(transform, PI); /* flip faces: invert normal */ @@ -84,13 +83,8 @@ main(int argc, char** 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); + (NULL, &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); @@ -102,7 +96,7 @@ main(int argc, char** argv) CHECK(ssol_scene_create(dev, &scene), RES_OK); CHECK(ssol_scene_attach_sun(scene, sun), RES_OK); - /* create scene content */ + /* Create scene content */ CHECK(ssol_shape_create_mesh(dev, &rect), RES_OK); attribs[0].usage = SSOL_POSITION; @@ -126,7 +120,7 @@ main(int argc, char** argv) 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_mirror_setup(m_mtl, &shader), RES_OK); CHECK(ssol_material_create_virtual(dev, &v_mtl), RES_OK); CHECK(ssol_object_create(dev, &m_object), RES_OK); @@ -138,37 +132,35 @@ main(int argc, char** argv) CHECK(ssol_object_add_shaded_shape(t_object, rect, 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_set_receiver(target, SSOL_FRONT, 0), 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_solve(scene, rng, N__, 0, 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); + printf("Ir = %g +/- %g\n", m, std); #define DNI_cos (1000 * cos(0)) CHECK(eq_eps(m, 400 * DNI_cos, 20), 1); CHECK(eq_eps(std, 0, 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-8), 1); - CHECK(eq_eps(status.irradiance.SE, std, 1e-4), 1); -#undef GET_STATUS -#undef GET_RCV_STATUS + CHECK(ssol_estimator_get_mc_global(estimator, &mc_global), RES_OK); + printf("Shadows = %g +/- %g\n", mc_global.shadowed.E, mc_global.shadowed.SE); + printf("Missing = %g +/- %g\n", mc_global.missing.E, mc_global.missing.SE); + CHECK(eq_eps(mc_global.shadowed.E, 0, 1e-4), 1); + CHECK(eq_eps(mc_global.missing.E, 0, 1e-4), 1); + CHECK(ssol_estimator_get_mc_receiver + (estimator, target, SSOL_FRONT, &mc_rcv), RES_OK); + printf("Ir(target) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, m, 1e-8), 1); + CHECK(eq_eps(mc_rcv.integrated_irradiance.SE, std, 1e-4), 1); + CHECK(ssol_estimator_get_failed_count(estimator, &count), RES_OK); + CHECK(count, 0); /* Free data */ CHECK(ssol_instance_ref_put(heliostat), RES_OK); @@ -186,8 +178,6 @@ main(int argc, char** argv) 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); diff --git a/src/test_ssol_solver6.c b/src/test_ssol_solver6.c @@ -29,7 +29,6 @@ #define HALF_Y 5 #include "test_ssol_rect2D_geometry.h" -#include <rsys/logger.h> #include <rsys/double33.h> #include <star/s3d.h> @@ -49,10 +48,17 @@ get_wlen(const size_t i, double* wlen, double* data, void* ctx) int main(int argc, char** argv) { - struct logger logger; struct mem_allocator allocator; struct ssol_device* dev; struct ssp_rng* rng; + struct ssol_object *m_object1; + struct ssol_object *m_object2; + struct ssol_object* t_object1; + struct ssol_object* t_object2; + struct ssol_instance* heliostat1; + struct ssol_instance* heliostat2; + struct ssol_instance* target1; + struct ssol_instance* target2; struct ssol_scene* scene; struct ssol_shape* square; struct ssol_vertex_data attribs[1] = { SSOL_VERTEX_DATA_NULL__ }; @@ -68,7 +74,8 @@ main(int argc, char** argv) struct ssol_sun* sun; struct ssol_spectrum* spectrum; struct ssol_estimator* estimator; - struct ssol_estimator_status status; + struct ssol_mc_global mc_global; + struct ssol_mc_receiver mc_rcv; double dir[3]; double transform[12]; /* 3x4 column major matrix */ FILE* tmp; @@ -77,13 +84,8 @@ main(int argc, char** 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); + (NULL, &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); @@ -118,16 +120,14 @@ main(int argc, char** argv) 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_mirror_setup(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_matte_setup(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); @@ -138,8 +138,6 @@ main(int argc, char** argv) 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); @@ -150,13 +148,11 @@ main(int argc, char** argv) 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_set_receiver(target1, SSOL_FRONT, 0), 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 */ @@ -166,13 +162,11 @@ main(int argc, char** argv) 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_set_receiver(target2, SSOL_BACK, 0), RES_OK); CHECK(ssol_instance_sample(target2, 0), RES_OK); CHECK(ssol_scene_attach_instance(scene, target2), RES_OK); d33_set_identity(transform); @@ -183,30 +177,24 @@ main(int argc, char** argv) 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); +#define GET_MC_RCV ssol_estimator_get_mc_receiver + CHECK(ssol_solve(scene, rng, N__, 0, 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 + CHECK(ssol_estimator_get_mc_global(estimator, &mc_global), RES_OK); + printf("Shadows = %g +/- %g\n", mc_global.shadowed.E, mc_global.shadowed.SE); + printf("Missing = %g +/- %g\n", mc_global.missing.E, mc_global.missing.SE); + CHECK(eq_eps(mc_global.shadowed.E, 100000, 2 * 100000/sqrt(N__)), 1); + CHECK(eq_eps(mc_global.missing.E, 0, 0), 1); + + CHECK(GET_MC_RCV(estimator, target1, SSOL_FRONT, &mc_rcv), RES_OK); + printf("Ir(target1) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, 100000, 2*100000/sqrt(N__)), 1); + + CHECK(GET_MC_RCV(estimator, target2, SSOL_BACK, &mc_rcv), RES_OK); + printf("Ir(target2) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, 0, 1), 1); /* Free data */ CHECK(ssol_instance_ref_put(heliostat1), RES_OK); @@ -229,8 +217,6 @@ main(int argc, char** argv) 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); diff --git a/src/test_ssol_solver7.c b/src/test_ssol_solver7.c @@ -0,0 +1,245 @@ +/* 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 <rsys/mem_allocator.h> +#include <rsys/image.h> + +#define SCREEN_GAMMA 2.2 +#define WIDTH 640 +#define HEIGHT 480 +#define PROJ_RATIO (double)WIDTH/(double)HEIGHT + +#define REFLECTIVITY 0 +#include "test_ssol_materials.h" + +#define TARGET_SZ 2 +#define PLANE_NAME TARGET +#define HALF_X (TARGET_SZ/2) +#define HALF_Y (TARGET_SZ/2) +STATIC_ASSERT((HALF_X * 2 == TARGET_SZ), ONLY_ENVEN_VALUES_FOR_SZ); +#include "test_ssol_rect_geometry.h" + +#define HELIOSTAT_SZ 4 +#define POLYGON_NAME HELIOSTAT +#define HALF_X (HELIOSTAT_SZ/2) +#define HALF_Y (HELIOSTAT_SZ/2) +STATIC_ASSERT(HALF_X * 2 == HELIOSTAT_SZ, ONLY_ENVEN_VALUES_FOR_SZ); +#include "test_ssol_rect2D_geometry.h" + +#define HYPERBOL_SZ 24 +#define POLYGON_NAME HYPERBOL +#define HALF_X (HYPERBOL_SZ/2) +#define HALF_Y (HYPERBOL_SZ/2) +STATIC_ASSERT(HALF_X * 2 == HYPERBOL_SZ, ONLY_ENVEN_VALUES_FOR_SZ); +#include "test_ssol_rect2D_geometry.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 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_carving carving1 = SSOL_CARVING_NULL; + struct ssol_carving carving2 = SSOL_CARVING_NULL; + struct ssol_material *m_mtl, *bck_mtl, *v_mtl; + struct ssol_mirror_shader m_shader = SSOL_MIRROR_SHADER_NULL; + struct ssol_matte_shader bck_shader = SSOL_MATTE_SHADER_NULL; + struct ssol_object* t_object; + struct ssol_instance* target; + struct ssol_sun* sun; + struct ssol_spectrum* spectrum; + struct ssol_estimator* estimator; + struct ssol_mc_global mc_global; + struct ssol_mc_receiver mc_rcv; + double dir[3]; + double transform[12]; /* 3x4 column major matrix */ + FILE* tmp; + /* primary is a parabol */ + struct ssol_quadric quadric1 = SSOL_QUADRIC_DEFAULT; + struct ssol_punched_surface punched1 = SSOL_PUNCHED_SURFACE_NULL; + struct ssol_object* m_object1; + struct ssol_shape* quad_square1; + struct ssol_instance* heliostat; + /* secondary is an hyperbol */ + struct ssol_quadric quadric2 = SSOL_QUADRIC_DEFAULT; + struct ssol_punched_surface punched2 = SSOL_PUNCHED_SURFACE_NULL; + struct ssol_object* m_object2; + struct ssol_shape* quad_square2; + struct ssol_instance* secondary; + (void) argc, (void) argv; + +#define H 10 + + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + + CHECK(ssol_device_create + (NULL, &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, 1, 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, TARGET_NTRIS__, get_ids, + TARGET_NVERTS__, attribs, 1, (void*) &TARGET_DESC__), 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_setup(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_setup(bck_mtl, &bck_shader), RES_OK); + CHECK(ssol_material_create_virtual(dev, &v_mtl), RES_OK); + + carving1.get = get_polygon_vertices; + carving1.operation = SSOL_AND; + carving1.nb_vertices = HELIOSTAT_NVERTS__; + carving1.context = &HELIOSTAT_EDGES__; + + CHECK(ssol_shape_create_punched_surface(dev, &quad_square1), RES_OK); + quadric1.type = SSOL_QUADRIC_PARABOL; + quadric1.data.parabol.focal = 10 * sqrt(2) * H; + punched1.nb_carvings = 1; + punched1.quadric = &quadric1; + punched1.carvings = &carving1; + CHECK(ssol_punched_surface_setup(quad_square1, &punched1), RES_OK); + CHECK(ssol_object_create(dev, &m_object1), RES_OK); + CHECK(ssol_object_add_shaded_shape(m_object1, quad_square1, m_mtl, m_mtl), RES_OK); + CHECK(ssol_object_instantiate(m_object1, &heliostat), RES_OK); + CHECK(ssol_scene_attach_instance(scene, heliostat), RES_OK); + d33_rotation_yaw(transform, -0.25 * PI); + d3_splat(transform + 9, 0); + transform[9] = 10 * H; /* target the img focal point of the hyperbol */ + CHECK(ssol_instance_set_transform(heliostat, transform), RES_OK); + + carving2.get = get_polygon_vertices; + carving2.operation = SSOL_AND; + carving2.nb_vertices = HYPERBOL_NVERTS__; + carving2.context = &HYPERBOL_EDGES__; + + CHECK(ssol_shape_create_punched_surface(dev, &quad_square2), RES_OK); + quadric2.type = SSOL_QUADRIC_HYPERBOL; + quadric2.data.hyperbol.real_focal = 9 * H; + quadric2.data.hyperbol.img_focal = 1 * H; + punched2.nb_carvings = 1; + punched2.quadric = &quadric2; + punched2.carvings = &carving2; + CHECK(ssol_punched_surface_setup(quad_square2, &punched2), RES_OK); + CHECK(ssol_object_create(dev, &m_object2), RES_OK); + CHECK(ssol_object_add_shaded_shape(m_object2, quad_square2, m_mtl, v_mtl), RES_OK); + CHECK(ssol_object_instantiate(m_object2, &secondary), RES_OK); + CHECK(ssol_scene_attach_instance(scene, secondary), RES_OK); + d33_set_identity(transform); + d3_splat(transform + 9, 0); + transform[11] = 9 * H; + CHECK(ssol_instance_set_transform(secondary, transform), RES_OK); + CHECK(ssol_instance_sample(secondary, 0), RES_OK); + + CHECK(ssol_object_create(dev, &t_object), RES_OK); + CHECK(ssol_object_add_shaded_shape(t_object, square, bck_mtl, v_mtl), RES_OK); + CHECK(ssol_object_instantiate(t_object, &target), RES_OK); + CHECK(ssol_instance_set_receiver(target, SSOL_FRONT, 0), RES_OK); + CHECK(ssol_instance_sample(target, 0), RES_OK); + CHECK(ssol_scene_attach_instance(scene, target), RES_OK); + d33_set_identity(transform); + d3_splat(transform + 9, 0); + CHECK(ssol_instance_set_transform(target, transform), RES_OK); + +#define N__ 10000 +#define DNI_cos (1000 * cos(0)) +#define TOTAL (HELIOSTAT_SZ * HELIOSTAT_SZ * DNI_cos) +#define GET_MC_RCV ssol_estimator_get_mc_receiver + + NCHECK(tmp = tmpfile(), 0); + CHECK(ssol_solve(scene, rng, N__, 0, tmp, &estimator), RES_OK); + CHECK(fclose(tmp), 0); + + printf("Total = %g\n", TOTAL); + CHECK(ssol_estimator_get_mc_global(estimator, &mc_global), RES_OK); + printf("Shadows = %g +/- %g\n", + mc_global.shadowed.E, mc_global.shadowed.SE); + CHECK(eq_eps(mc_global.shadowed.E, 0, 1e-4), 1); + printf("Missing = %g +/- %g\n", + mc_global.missing.E, mc_global.missing.SE); + CHECK(eq_eps(mc_global.missing.E, 0, 1e-4), 1); + + CHECK(GET_MC_RCV(estimator, target, SSOL_FRONT, &mc_rcv), RES_OK); + printf("Ir(target1) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, TOTAL, TOTAL * 1e-4), 1); + CHECK(eq_eps(mc_rcv.integrated_irradiance.SE, 0, 1e-4), 1); + + /* Free data */ + CHECK(ssol_instance_ref_put(heliostat), RES_OK); + CHECK(ssol_instance_ref_put(secondary), RES_OK); + CHECK(ssol_instance_ref_put(target), RES_OK); + CHECK(ssol_object_ref_put(m_object1), RES_OK); + CHECK(ssol_object_ref_put(m_object2), 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_square1), RES_OK); + CHECK(ssol_shape_ref_put(quad_square2), 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_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); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHECK(mem_allocated_size(), 0); + + return 0; +} + diff --git a/src/test_ssol_solver8.c b/src/test_ssol_solver8.c @@ -0,0 +1,187 @@ +/* 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 TARGET +#define HALF_X 10 +#define HALF_Y 10 +#include "test_ssol_rect_geometry.h" + +#define X_SZ 10 +#define Y_SZ 4 +#define POLYGON_NAME POLY +#define HALF_X (X_SZ / 2) +STATIC_ASSERT((HALF_X * 2 == X_SZ), ONLY_ENVEN_VALUES_FOR_X_SZ); +#define Y_MIN 0 +#define Y_MAX Y_SZ +#include "test_ssol_rect2D_geometry.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 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* heliostat; + struct ssol_instance* target; + struct ssol_sun* sun; + struct ssol_spectrum* spectrum; + struct ssol_estimator* estimator; + struct ssol_mc_global mc_global; + struct ssol_mc_receiver mc_rcv; + double dir[3]; + double transform[12]; /* 3x4 column major matrix */ + size_t count; + FILE* tmp; + uint32_t r_id; + + (void) argc, (void) argv; + d3_splat(transform + 9, 0); + d33_rotation_pitch(transform, PI); /* flip faces: invert normal */ + transform[11] = 4; /* set it just above the parabolic cylinder */ + + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + + CHECK(ssol_device_create + (NULL, &allocator, SSOL_NTHREADS_DEFAULT, 0, &dev), RES_OK); + +#define DNI 1000 + 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, 1, -1)), RES_OK); + CHECK(ssol_sun_set_spectrum(sun, spectrum), RES_OK); + CHECK(ssol_sun_set_dni(sun, DNI), 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, TARGET_NTRIS__, get_ids, + TARGET_NVERTS__, attribs, 1, (void*) &TARGET_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_PARABOLIC_CYLINDER; + quadric.data.parabol.focal = 1; + 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_setup(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, m_mtl), RES_OK); + CHECK(ssol_object_instantiate(m_object, &heliostat), RES_OK); + CHECK(ssol_scene_attach_instance(scene, heliostat), RES_OK); + + 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, 0), 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__ 100000 +#define GET_MC_RCV ssol_estimator_get_mc_receiver + CHECK(ssol_solve(scene, rng, N__, 0, 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(fclose(tmp), 0); + CHECK(ssol_estimator_get_failed_count(estimator, &count), RES_OK); + CHECK(count, 0); +#define S (sqrt(2) * X_SZ * Y_SZ) + CHECK(ssol_estimator_get_mc_global(estimator, &mc_global), RES_OK); + printf("Cos = %g +/- %g\n", mc_global.cos_loss.E, mc_global.cos_loss.SE); + printf("Shadows = %g +/- %g\n", mc_global.shadowed.E, mc_global.shadowed.SE); + printf("Missing = %g +/- %g\n", mc_global.missing.E, mc_global.missing.SE); + CHECK(eq_eps(mc_global.cos_loss.E, 0, 1e-4), 1); + CHECK(eq_eps(mc_global.shadowed.E, 0, 1e-4), 1); + CHECK(eq_eps(mc_global.missing.E, 0, 1e-4), 1); + CHECK(GET_MC_RCV(estimator, target, SSOL_FRONT, &mc_rcv), RES_OK); + printf("Ir(target1) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, S * DNI, + 2 * mc_rcv.integrated_irradiance.SE), 1); + + /* Free data */ + CHECK(ssol_instance_ref_put(heliostat), RES_OK); + 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); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHECK(mem_allocated_size(), 0); + + return 0; +} diff --git a/src/test_ssol_solver9.c b/src/test_ssol_solver9.c @@ -0,0 +1,184 @@ +/* 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" + +#include <rsys/math.h> + +#define TGT_X 6 +#define TGT_Y 10 +#define PLANE_NAME TARGET +#define HALF_X (TGT_X / 2) +#define HALF_Y (TGT_Y / 2) +STATIC_ASSERT((HALF_X * 2 == TGT_X), ONLY_ENVEN_VALUES_FOR_TGT_X); +STATIC_ASSERT((HALF_Y * 2 == TGT_Y), ONLY_ENVEN_VALUES_FOR_TGT_Y); +#include "test_ssol_rect_geometry.h" + +#define SZ MMAX(TGT_X, TGT_Y) +#define CUBE_NAME CUBE +#define HALF_X (SZ / 2) +#define HALF_Y (SZ / 2) +#define HALF_Z (SZ / 2) +STATIC_ASSERT((HALF_X * 2 == SZ), ONLY_ENVEN_VALUES_FOR_SZ); +#include "test_ssol_cube_geometry.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 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* cube; + 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* heliostat; + struct ssol_instance* target; + struct ssol_sun* sun; + struct ssol_spectrum* spectrum; + struct ssol_estimator* estimator; + struct ssol_mc_global mc_global; + struct ssol_mc_receiver mc_rcv; + double dir[3]; + double transform[12]; /* 3x4 column major matrix */ + size_t count; + FILE* tmp; + uint32_t r_id; + + (void) argc, (void) argv; + d3_splat(transform + 9, 0); + d33_rotation_pitch(transform, PI); /* flip faces: invert normal */ + transform[11] = 6; /* set it above the cube */ + + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + + CHECK(ssol_device_create + (NULL, &allocator, SSOL_NTHREADS_DEFAULT, 0, &dev), RES_OK); + +#define DNI 1000 + 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, DNI), 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, TARGET_NTRIS__, get_ids, + TARGET_NVERTS__, attribs, 1, (void*) &TARGET_DESC__), RES_OK); + + CHECK(ssol_shape_create_mesh(dev, &cube), RES_OK); + CHECK(ssol_mesh_setup(cube, CUBE_NTRIS__, get_ids, + CUBE_NVERTS__, attribs, 1, (void*) &CUBE_DESC__), 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_setup(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, cube, m_mtl, m_mtl), RES_OK); + CHECK(ssol_object_instantiate(m_object, &heliostat), RES_OK); + CHECK(ssol_scene_attach_instance(scene, heliostat), RES_OK); + + 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, 0), 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__ 100000 +#define GET_MC_RCV ssol_estimator_get_mc_receiver + CHECK(ssol_solve(scene, rng, N__, 0, 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(fclose(tmp), 0); + CHECK(ssol_estimator_get_failed_count(estimator, &count), RES_OK); + CHECK(count, 0); +#define DNI_TGT_S (DNI * TGT_X * TGT_Y) +#define DNI_S (DNI * SZ * SZ) + CHECK(ssol_estimator_get_mc_global(estimator, &mc_global), RES_OK); + printf("Cos = %g +/- %g\n", mc_global.cos_loss.E, mc_global.cos_loss.SE); + printf("Shadows = %g +/- %g\n", mc_global.shadowed.E, mc_global.shadowed.SE); + printf("Missing = %g +/- %g\n", mc_global.missing.E, mc_global.missing.SE); + /* CHECK(eq_eps(mc_global.cos_loss.E, 4 * DNI_S, DNI_S * 1e-4), 1); */ + CHECK(eq_eps(mc_global.shadowed.E, DNI_S, 3 * mc_global.shadowed.SE), 1); + CHECK(eq_eps(mc_global.missing.E, + MMAX(DNI_S, DNI_TGT_S) - MMIN(DNI_S, DNI_TGT_S), + 3 * mc_global.missing.SE), 1); + CHECK(GET_MC_RCV(estimator, target, SSOL_FRONT, &mc_rcv), RES_OK); + printf("Ir(target1) = %g +/- %g\n", + mc_rcv.integrated_irradiance.E, mc_rcv.integrated_irradiance.SE); + CHECK(eq_eps(mc_rcv.integrated_irradiance.E, MMIN(DNI_S, DNI_TGT_S), + 3 * mc_rcv.integrated_irradiance.SE), 1); + + /* Free data */ + CHECK(ssol_instance_ref_put(heliostat), RES_OK); + 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(cube), 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); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHECK(mem_allocated_size(), 0); + + return 0; +} diff --git a/src/test_ssol_spectrum.c b/src/test_ssol_spectrum.c @@ -16,8 +16,6 @@ #include "ssol.h" #include "test_ssol_utils.h" -#include <rsys/logger.h> - struct spectrum_desc { double wavelengths[3]; double data[3]; @@ -41,7 +39,6 @@ get_wlen(const size_t i, double* wlen, double* data, void* ctx) int main(int argc, char** argv) { - struct logger logger; struct mem_allocator allocator; struct ssol_device* dev; struct ssol_spectrum* spectrum; @@ -50,13 +47,8 @@ main(int argc, char** 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); + (NULL, &allocator, SSOL_NTHREADS_DEFAULT, 0, &dev), RES_OK); CHECK(ssol_spectrum_create(NULL, &spectrum), RES_BAD_ARG); CHECK(ssol_spectrum_create(dev, NULL), RES_BAD_ARG); @@ -90,8 +82,6 @@ main(int argc, char** argv) 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); diff --git a/src/test_ssol_sun.c b/src/test_ssol_sun.c @@ -16,13 +16,11 @@ #include "ssol.h" #include "test_ssol_utils.h" -#include <rsys/logger.h> #include <rsys/double3.h> int main(int argc, char** argv) { - struct logger logger; struct mem_allocator allocator; struct ssol_device* dev; struct ssol_spectrum* spectrum; @@ -36,13 +34,8 @@ main(int argc, char** 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); + (NULL, &allocator, SSOL_NTHREADS_DEFAULT, 0, &dev), RES_OK); CHECK(ssol_spectrum_create(dev, &spectrum), RES_OK); CHECK(ssol_spectrum_create(dev, &spectrum2), RES_OK); @@ -198,8 +191,6 @@ main(int argc, char** argv) 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); diff --git a/src/test_ssol_utils.h b/src/test_ssol_utils.h @@ -20,14 +20,6 @@ #include <stdio.h> static INLINE void -log_stream(const char* msg, void* ctx) -{ - ASSERT(msg); - (void) msg, (void) ctx; - printf("%s\n", msg); -} - -static INLINE void check_memory_allocator(struct mem_allocator* allocator) { if(MEM_ALLOCATED_SIZE(allocator)) {