solstice-solver

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

commit 1f84b7e24e79d5e5e884e1ed00fcd6ab6c0ecf45
parent 123b067eb1017904ecd82183760d6adc2d3da083
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Fri,  9 Sep 2016 18:14:20 +0200

Work in progress on solver; new test2 still failing

Diffstat:
Mcmake/CMakeLists.txt | 1+
Msrc/ssol.h | 3+++
Msrc/ssol_c.h | 12++++++++++++
Msrc/ssol_scene.c | 98++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/ssol_shape.c | 1+
Msrc/ssol_solver.c | 373+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/ssol_solver_c.h | 29++++++++++++++++-------------
Asrc/test_ssol_solver2.c | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 498 insertions(+), 196 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -149,6 +149,7 @@ if(NOT NO_TEST) new_test(test_ssol_shape) new_test(test_ssol_spectrum) new_test(test_ssol_solver1) + new_test(test_ssol_solver2) new_test(test_ssol_sun) endif(NOT NO_TEST) diff --git a/src/ssol.h b/src/ssol.h @@ -424,11 +424,14 @@ ssol_instance_set_receiver const char* name_front, /* May be NULL <=> Front faces are no more receivers */ const char* name_back); /* May be NULL <=> back faces are no more receivers */ +/* TODO: per face mask */ SSOL_API res_T ssol_instance_set_target_mask (struct ssol_instance* instance, const uint32_t mask); +/* TODO: add a dont_sample flag */ + SSOL_API res_T ssol_instance_is_attached (struct ssol_instance* instance, diff --git a/src/ssol_c.h b/src/ssol_c.h @@ -42,5 +42,17 @@ hit_filter_function void* realisation, void* filter_data); +#include <math.h> + +#ifndef NAN +#define NAN (INF * 0.0) +#endif +#define ISNAN(x) (!((x) == (x))) +#ifndef NDEBUG +#define ASSERT_NAN(x, sz) {int i; for (i = 0; i < (sz); i++)ASSERT(!ISNAN((x)[i]));} +#else +#define ASSERT_NAN(x, sz) +#endif + #endif /* SSOL_C_H */ diff --git a/src/ssol_scene.c b/src/ssol_scene.c @@ -27,7 +27,7 @@ #include <rsys/list.h> #include <rsys/mem_allocator.h> #include <rsys/rsys.h> -#include <rsys/double3.h> +#include <rsys/double33.h> /******************************************************************************* * Helper functions @@ -267,56 +267,92 @@ error: ******************************************************************************/ int hit_filter_function - (const struct s3d_hit* hit, - const float org[3], - const float dir[3], - void* realisation, - void* filter_data) +(const struct s3d_hit* hit, + const float org[3], + const float dir[3], + void* realisation, + void* filter_data) { - struct ssol_instance* inst; + const struct ssol_instance* inst; + const struct ssol_shape* shape; const struct str* receiver_name; struct realisation* rs = realisation; - struct ssol_material* mtl; struct segment* seg; struct segment* prev; - int front_face = 0; + double dist; (void) filter_data, (void) org, (void) dir; ASSERT(rs); - prev = previous_segment(rs); seg = current_segment(rs); + prev = previous_segment(rs); ASSERT(seg); - /* TODO: need to detect self intersect at the instance level, - * using front/back face to avoid false self intersect events */ - if(prev && S3D_PRIMITIVE_EQ(&hit->prim, &prev->hit.prim)) - return 1; /* Discard self intersection */ + /* these components have been set */ + ASSERT_NAN(seg->dir, 3); + ASSERT_NAN(seg->org, 3); + ASSERT(seg->self_instance); + NCHECK(seg->self_front, 99); + /* Discard self intersection; using raytracing normal */ inst = *htable_instance_find(&rs->data.scene->instances_rt, &hit->prim.inst_id); - - if (inst->object->shape->type == SHAPE_PUNCHED) { + + int hit_front = f3_dot(hit->normal, dir) < 0; + if (seg->self_instance == inst && seg->self_front != hit_front) { + return 1; + } + + shape = inst->object->shape; + seg->on_punched = (shape->type == SHAPE_PUNCHED); + switch (shape->type) { + case SHAPE_PUNCHED: { /* hits on quadrics must be recomputed more accurately */ - /* FIXME: use world2local and local2world transform */ - int valid = punched_shape_intersect_local(inst->object->shape, seg->org, - seg->dir, hit->distance, seg->hit_pos, seg->hit_normal, &seg->dist); + double org_local[3], dir_local[3]; + const double* transform = inst->transform; + double tr[9]; + d33_inverse(tr, transform); + + /* get org in local coordinate */ + if (prev && prev->on_punched) { + d3_set(org_local, prev->hit_pos_local); + } + else { + d3_set(org_local, seg->org); + d3_sub(org_local, org_local, transform + 9); + d33_muld3(org_local, tr, org_local); + } + + /* get dir in local */ + d33_muld3(dir_local, tr, seg->dir); + /* recompute hit */ + int valid = punched_shape_intersect_local(shape, org_local, dir_local, + hit->distance, seg->hit_pos_local, seg->hit_normal, &dist); if (!valid) return 1; + /* transform point to world */ + d33_muld3(seg->hit_pos, transform, seg->hit_pos_local); + d3_add(seg->hit_pos, transform + 9, seg->hit_pos); + /* transform normal to world */ + d33_invtrans(tr, transform); + d33_muld3(seg->hit_normal, tr, seg->hit_normal); + ASSERT(d3_dot(seg->hit_normal, d3_set_f3(tr, hit->normal)) > 0); + break; } - else { - double* from = prev ? prev->hit_pos : rs->start.pos; - ASSERT(inst->object->shape->type == SHAPE_MESH); + case SHAPE_MESH: { d3_set_f3(seg->hit_normal, hit->normal); - seg->dist = hit->distance; /* use raytraced distance to fill hit_pos */ - d3_add(seg->hit_pos, from, d3_muld(seg->hit_pos, seg->dir, hit->distance)); + d3_add(seg->hit_pos, seg->org, d3_muld(seg->hit_pos, seg->dir, hit->distance)); + break; + } + default: FATAL("Unreachable code.\n"); break; } - front_face = d3_dot(seg->hit_normal, seg->dir) < 0; + seg->hit_front = d3_dot(seg->hit_normal, seg->dir) < 0; + ASSERT(hit_front == seg->hit_front); - if(front_face) { - mtl = inst->object->mtl_front; + if(seg->hit_front) { + seg->hit_material = inst->object->mtl_front; receiver_name = &inst->receiver_front; } else { - mtl = inst->object->mtl_back; + seg->hit_material = inst->object->mtl_back; receiver_name = &inst->receiver_back; } @@ -335,13 +371,13 @@ hit_filter_function } /* register success mask */ - if(front_face) { + if(seg->hit_front) { rs->success_mask |= inst->target_mask; } - if(mtl->type == MATERIAL_VIRTUAL) { + if(seg->hit_material->type == MATERIAL_VIRTUAL) { return 1; /* Discard virtual material */ } - rs->data.instance = inst; + seg->hit_instance = inst; return 0; } diff --git a/src/ssol_shape.c b/src/ssol_shape.c @@ -763,6 +763,7 @@ punched_shape_intersect_local { ASSERT(shape && org && dir && hint >= 0 && pt && normal && dist); ASSERT(shape->type == SHAPE_PUNCHED); + ASSERT(dir[0] || dir[1] || dir[2]); /* hits on quadrics must be recomputed more accurately */ switch (shape->quadric.type) { case SSOL_QUADRIC_PLANE: { diff --git a/src/ssol_solver.c b/src/ssol_solver.c @@ -44,13 +44,17 @@ static const char* END_TEXT[] = END_TEXT__; static FINLINE void solstice_trace_ray(struct realisation* rs) { - float org[3], dir[3]; + float org[3], dir[3], range[2] = { 0, FLT_MAX }; struct segment* seg = current_segment(rs); f3_set_d3(org, seg->org); f3_set_d3(dir, seg->dir); + fprintf(rs->data.out_stream, + "trace (%g:%g:%g) (%g:%g:%g)\n", + SPLIT3(seg->org), + SPLIT3(seg->dir)); S3D(scene_view_trace_ray - (rs->data.view_rt, org, dir, seg->range, rs, &seg->hit)); + (rs->data.view_rt, org, dir, range, rs, &seg->hit)); /* the filter function recomputes intersections on quadrics and sets seg */ } @@ -167,77 +171,163 @@ previous_segment(struct realisation* rs) { size_t idx; ASSERT(rs); - if (!rs->s_idx) return NULL; + if (rs->s_idx == 0) return NULL; idx = rs->s_idx - 1; ASSERT(idx < darray_segment_size_get(&rs->segments)); return darray_segment_data_get(&rs->segments) + idx; } struct segment* -sun_segment(struct realisation* rs) +current_segment(struct realisation* rs) { struct segment* seg; ASSERT(rs); - seg = darray_segment_data_get(&rs->segments); + ASSERT(rs->s_idx < darray_segment_size_get(&rs->segments)); + seg = darray_segment_data_get(&rs->segments) + rs->s_idx; ASSERT(seg); return seg; } -struct segment* -current_segment(struct realisation* rs) +static void +check_fst_segment(const struct segment* seg) { - struct segment* seg; - ASSERT(rs); - ASSERT(rs->s_idx < darray_segment_size_get(&rs->segments)); - seg = darray_segment_data_get(&rs->segments) + rs->s_idx; ASSERT(seg); - return seg; + ASSERT_NAN(seg->dir, 3); + /* hit is not checked and can be used only for debugging purpose */ + NCHECK(seg->hit_front, 99); + ASSERT(seg->hit_instance); + ASSERT(seg->hit_material); + ASSERT_NAN(seg->hit_normal, 3); + ASSERT_NAN(seg->hit_pos, 3); + if (seg->on_punched) ASSERT_NAN(seg->hit_pos_local, 3); + NCHECK(seg->on_punched, 99); + ASSERT_NAN(seg->org, 3); + ASSERT(seg->weight > 0); +} + +static void +check_segment(const struct segment* seg) +{ + check_fst_segment(seg); + ASSERT(seg->self_instance); + NCHECK(seg->self_front, 99); + /* hit filter is supposed to work properly */ + ASSERT(seg->self_instance != seg->hit_instance + || seg->self_front != seg->hit_front); } res_T -next_segment(struct realisation* rs) +setup_next_segment(struct realisation* rs) { res_T res = RES_OK; + const struct segment* prev; + const struct solver_data* data; + struct segment* seg; ASSERT(rs); - ++rs->s_idx; - if (rs->s_idx >= darray_segment_size_get(&rs->segments)) { + + if (++rs->s_idx >= darray_segment_size_get(&rs->segments)) { res = darray_segment_resize(&rs->segments, rs->s_idx + 1); if (res != RES_OK) return res; } - reset_segment(current_segment(rs)); - return RES_OK; + prev = previous_segment(rs); + seg = current_segment(rs); + data = &rs->data; + ASSERT(seg && prev && data); + + if(rs->s_idx == 1) + check_fst_segment(prev); + else + check_segment(prev); + reset_segment(seg); + seg->self_instance = prev->hit_instance; + seg->self_front = prev->hit_front; + d3_set(seg->org, prev->hit_pos); + + res = material_shade(prev->hit_material, &data->fragment, rs->freq, data->brdfs); + if (res != RES_OK) { + rs->end = TERM_ERR; + return res; + } + + seg->weight = prev->weight + * brdf_composite_sample( + data->brdfs, data->rng, prev->dir, data->fragment.Ns, seg->dir); + return res; } void reset_segment(struct segment* seg) { ASSERT(seg); - seg->range[0] = 0; - seg->range[1] = FLT_MAX; +#ifndef NDEBUG + d3_splat(seg->dir, NAN); + seg->hit = S3D_HIT_NULL; + seg->hit_front = 99; + seg->hit_instance = NULL; + seg->hit_material = NULL; + d3_splat(seg->hit_normal, NAN); + d3_splat(seg->hit_pos, NAN); + d3_splat(seg->hit_pos_local, NAN); + seg->on_punched = 99; + d3_splat(seg->org, NAN); + seg->self_instance = NULL; + seg->self_front = 99; + seg->weight = NAN; +#else seg->hit = S3D_HIT_NULL; +#endif } static void reset_starting_point(struct starting_point* start) { ASSERT(start); - start->primitive = S3D_PRIMITIVE_NULL; +#ifndef NDEBUG + start->cos_sun = NAN; + start->front_exposed = 99; + start->instance = NULL; + start->material = NULL; + d3_splat(start->normal, NAN); + start->on_punched = 99; + d3_splat(start->pos, NAN); + d3_splat(start->pos_local, NAN); + start->sampl_primitive = S3D_PRIMITIVE_NULL; + d3_splat(start->sundir, NAN); + start->uv[0] = start->uv[1] = NAN; +#else + start->sampl_primitive = S3D_PRIMITIVE_NULL; +#endif +} + +static void +check_starting_point(const struct starting_point* start) +{ + ASSERT(start); + ASSERT(start->cos_sun > 0); /* normal is flipped facing in_dir */ + NCHECK(start->front_exposed, 99); + ASSERT(start->instance); + ASSERT(start->material); + ASSERT_NAN(start->normal, 3); + NCHECK(start->on_punched, 99); + ASSERT_NAN(start->pos, 3); + if(start->on_punched) ASSERT_NAN(start->pos_local, 3); + ASSERT(!S3D_PRIMITIVE_EQ(&start->sampl_primitive, &S3D_PRIMITIVE_NULL)); + ASSERT_NAN(start->sundir, 3); + ASSERT_NAN(start->uv, 2); } static void reset_realisation(size_t cpt, struct realisation* rs) { rs->s_idx = 0; - rs->s_idx = 0; rs->end = TERM_NONE; rs->mode = MODE_STD; rs->rs_id = cpt; rs->success_mask = 0; reset_starting_point(&rs->start); brdf_composite_clear(rs->data.brdfs); - rs->data.instance = NULL; - /* reset sun segment (always used) */ - reset_segment(sun_segment(rs)); + /* reset first segment (always used) */ + reset_segment(current_segment(rs)); } static res_T @@ -284,22 +374,6 @@ release_realisation(struct realisation* rs) darray_segment_release(&rs->segments); } -static INLINE struct ssol_material* -get_material_from_hit - (struct ssol_scene* scene, - const double dir[3], - const struct s3d_hit* hit) -{ - struct ssol_instance* inst; - float dirf[3]; - int front_face; - ASSERT(scene && hit); - inst = *htable_instance_find(&scene->instances_rt, &hit->prim.inst_id); - f3_set_d3(dirf, dir); - front_face = f3_dot(dirf, hit->normal) < 0.f; - return front_face ? inst->object->mtl_front : inst->object->mtl_back; -} - /* partial setting of rs->start * front_exposed, cos_sun will be set later * material is set to NULL and will be set later @@ -308,51 +382,66 @@ static void sample_point_on_primary_mirror(struct realisation* rs) { struct s3d_attrib attrib; - struct ssol_object* object; + struct ssol_shape* shape; float r1, r2, r3; struct solver_data* data; struct s3d_primitive sampl_prim; + struct starting_point* start; + ASSERT(rs); data = &rs->data; ASSERT(data->rng && data->view_samp && data->scene); + start = &rs->start; /* sample a point on a primary mirror's carving */ r1 = ssp_rng_canonical_float(data->rng); r2 = ssp_rng_canonical_float(data->rng); r3 = ssp_rng_canonical_float(data->rng); - S3D(scene_view_sample(data->view_samp, r1, r2, r3, &sampl_prim, rs->start.uv)); - S3D(primitive_get_attrib(&sampl_prim, S3D_POSITION, rs->start.uv, &attrib)); + S3D(scene_view_sample(data->view_samp, r1, r2, r3, &sampl_prim, start->uv)); + S3D(primitive_get_attrib(&sampl_prim, S3D_POSITION, start->uv, &attrib)); CHECK(attrib.type, S3D_FLOAT3); - d3_set_f3(rs->start.pos, attrib.value); + d3_set_f3(start->pos, attrib.value); /* find the solstice shape and project the sampled point on the mirror */ - rs->start.instance = *htable_instance_find + start->instance = *htable_instance_find (&data->scene->instances_samp, &sampl_prim.inst_id); - object = rs->start.instance->object; - switch (object->shape->type) { - case SHAPE_MESH: - /* no projection needed */ - /* set normal */ - S3D(primitive_get_attrib(&sampl_prim, S3D_GEOMETRY_NORMAL, rs->start.uv, &attrib)); - CHECK(attrib.type, S3D_FLOAT3); - d3_set_f3(rs->start.normal, attrib.value); - d3_normalize(rs->start.normal, rs->start.normal); - /* to avoid self intersect */ - rs->start.primitive = sampl_prim; /* FIXME: cannot use a sampling primitive! */ - break; - case SHAPE_PUNCHED: - /* project the sampled point on the quadric */ - punched_shape_set_z_local(object->shape, rs->start.pos); - /* compute exact normal */ - punched_shape_set_normal_local(object->shape, rs->start.pos, rs->start.normal); - /* cannot self intersect as the sampled mesh is not raytraced */ - rs->start.primitive = S3D_PRIMITIVE_NULL; - break; - default: FATAL("Unreachable code.\n"); break; + start->sampl_primitive = sampl_prim; + shape = start->instance->object->shape; + start->on_punched = (shape->type == SHAPE_PUNCHED); + switch (shape->type) { + case SHAPE_MESH: { + /* no projection needed */ + /* set normal */ + S3D(primitive_get_attrib(&sampl_prim, S3D_GEOMETRY_NORMAL, start->uv, &attrib)); + CHECK(attrib.type, S3D_FLOAT3); + d3_set_f3(start->normal, attrib.value); + d3_normalize(start->normal, start->normal); + break; + } + case SHAPE_PUNCHED: { + const double* transform = start->instance->transform; + double tr[9]; + /* project the sampled point on the quadric */ + d33_inverse(tr, transform); + d3_set(start->pos_local, start->pos); + d3_sub(start->pos_local, start->pos_local, transform + 9); + d33_muld3(start->pos_local, tr, start->pos_local); + punched_shape_set_z_local(shape, start->pos_local); + /* transform point to world */ + d33_muld3(start->pos, transform, start->pos_local); + d3_add(start->pos, transform + 9, start->pos); + /* compute exact normal */ + punched_shape_set_normal_local(shape, start->pos_local, start->normal); + /* transform normal to world */ + d33_invtrans(tr, transform); + d33_muld3(start->normal, tr, start->normal); + break; + } + default: FATAL("Unreachable code.\n"); break; } /* TODO: transform everything to world coordinate */ /* will be defined later, depending on wich side sees the sun */ - rs->start.material = NULL; + start->material = NULL; } static void @@ -376,48 +465,77 @@ sample_wavelength(struct realisation* rs) static int receive_sunlight(struct realisation* rs) { - struct segment* seg = sun_segment(rs); + struct segment* seg; const struct str* receiver_name = NULL; - const struct ssol_sun* sun = rs->data.scene->sun; + const struct ssol_sun* sun; + struct starting_point* start; - ASSERT(d3_is_normalized(rs->start.sundir)); - ASSERT(d3_is_normalized(rs->start.normal)); - /* search for occlusions from starting point */ - d3_set(seg->dir, rs->start.sundir); + ASSERT(rs && rs->s_idx == 0); + seg = current_segment(rs); + sun = rs->data.scene->sun; + start = &rs->start; + ASSERT(d3_is_normalized(start->sundir)); + ASSERT(d3_is_normalized(start->normal)); + + /* find which material/face is exposed to sun */ + start->cos_sun = d3_dot(start->normal, start->sundir); + start->front_exposed = start->cos_sun < 0; + if (start->front_exposed) { + start->cos_sun *= -1; + start->material = start->instance->object->mtl_front; + } + else { + start->material = start->instance->object->mtl_back; + d3_muld(start->normal, start->normal, -1); + } + /* start must now be complete */ + check_starting_point(start); + + /* start filling seg from starting point */ + /* seg is set to cast a ray from the sampled point to the sun */ + d3_set(seg->dir, start->sundir); d3_muld(seg->dir, seg->dir, -1); - d3_set(seg->org, rs->start.pos); - seg->hit.prim = rs->start.primitive; - /* range as already been set */ - ASSERT(current_segment(rs) == sun_segment(rs)); + d3_set(seg->org, start->pos); + seg->self_instance = start->instance; + seg->self_front = start->front_exposed; + + /* search for occlusions from starting point */ + ASSERT(rs->s_idx == 0); /* sun segment */ solstice_trace_ray(rs); if (!S3D_HIT_NONE(&seg->hit)) { rs->end = TERM_SHADOW; return 0; } - /* find which material/face is exposed to sun */ - rs->start.cos_sun = d3_dot(rs->start.normal, rs->start.sundir); - rs->start.front_exposed = rs->start.cos_sun < 0; - if(rs->start.front_exposed) { - rs->start.cos_sun *= -1; - rs->start.material = rs->start.instance->object->mtl_front; - } - else { - rs->start.material = rs->start.instance->object->mtl_back; - d3_muld(rs->start.normal, rs->start.normal, -1); - } + /* fill segment to allow standard propagation + * pretend the ray was cast in the opposite direction */ + d3_set(seg->dir, start->sundir); + d3_sub(seg->org, seg->org, seg->dir); + /* hit_front will be set from the next impact (if any) */ + /* hit_instance will be set from the next impact (if any) */ + seg->hit_material = start->material; + d3_set(seg->hit_normal, start->normal); + d3_set(seg->hit_pos, start->pos); + d3_set(seg->hit_pos_local, start->pos_local); + seg->on_punched = start->on_punched; + seg->hit_instance = seg->self_instance; + seg->self_instance = NULL; + seg->hit_front = seg->self_front; + seg->self_front = 99; seg->weight = sun->dni * rs->start.cos_sun; + ASSERT(seg->weight > 0); /* fill fragment from starting point */ /* FIXME: is fragment->Ns orientation correct when has_normal && back_face??? */ - surface_fragment_setup(&rs->data.fragment, seg->org, rs->start.sundir, - rs->start.normal, &rs->start.primitive, rs->start.uv); + surface_fragment_setup(&rs->data.fragment, seg->hit_pos, seg->dir, + /* FIXME: must provide a raytracing prim, not a sampling one! */ + seg->hit_normal, &start->sampl_primitive, start->uv); /* if the sampled instance is a receiver, register the sampled point */ - if(rs->start.front_exposed) { - receiver_name = &rs->start.instance->receiver_front; + if(start->front_exposed) { + receiver_name = &start->instance->receiver_front; } else { - receiver_name = &rs->start.instance->receiver_back; + receiver_name = &start->instance->receiver_back; } if(!str_is_empty(receiver_name)) { /* normal orientation has already been checked */ @@ -430,56 +548,15 @@ receive_sunlight(struct realisation* rs) seg->weight, SPLIT3(seg->hit_pos), SPLIT3(seg->dir), - SPLIT2(rs->start.uv)); + SPLIT2(start->uv)); } /* register success mask (normal orientation has already been checked) */ - rs->success_mask |= rs->start.instance->target_mask; - - /* restore self intersect information for further visibility test - * (previous call to trace_ray overwrote prim) */ - seg->hit.prim = rs->start.primitive; - + rs->success_mask |= start->instance->target_mask; + return 1; } -static res_T -set_output_pos_and_dir(struct realisation* rs) -{ - struct ssol_material* material; - struct segment* seg = current_segment(rs); - struct segment* prev = previous_segment(rs); - struct ssol_scene* scene = rs->data.scene; - double* dir; - int fst_segment; - res_T res = RES_OK; - - /* next_segment should have been called */ - ASSERT(prev); - - fst_segment = (prev == sun_segment(rs)); - - if (fst_segment) { - d3_set(seg->org, rs->start.pos); - material = rs->start.material; - } else { - d3_set(seg->org, prev->hit_pos); - material = get_material_from_hit(scene, prev->dir, &prev->hit); - } - CHECK(material->type, MATERIAL_MIRROR); - res = material_shade(material, &rs->data.fragment, rs->freq, rs->data.brdfs); - if (res != RES_OK) { - rs->end = TERM_ERR; - return res; - } - - dir = fst_segment ? rs->start.sundir : prev->dir; - seg->weight = prev->weight - * brdf_composite_sample - (rs->data.brdfs, rs->data.rng, dir, rs->data.fragment.Ns, seg->dir); - return res; -} - static void propagate(struct realisation* rs) { @@ -491,12 +568,9 @@ propagate(struct realisation* rs) rs->end = TERM_MISSING; return; } - /* should not stop on a virtual surface */ - ASSERT(get_material_from_hit(rs->data.scene, seg->dir, &seg->hit)->type - != MATERIAL_VIRTUAL); - /* fill fragment from hit and loop */ - d3_add(seg->hit_pos, seg->org, d3_muld(seg->hit_pos, seg->dir, seg->dist)); + /* fill fragment and loop */ + check_segment(seg); surface_fragment_setup(&rs->data.fragment, seg->hit_pos, seg->dir, seg->hit_normal, &seg->hit.prim, seg->hit.uv); } @@ -532,24 +606,19 @@ ssol_solve if (receive_sunlight(&rs)) { /* start propagating from mirror */ do { - if (RES_OK != next_segment(&rs)) { + if (RES_OK != setup_next_segment(&rs)) { rs.end = TERM_ERR; } else { - /* set next segment and propagate */ - set_output_pos_and_dir(&rs); propagate(&rs); } } while (rs.end == TERM_NONE); } /* propagation ended */ - if (rs.success_mask) { - fprintf(output, "Realization %u succeeded: 0x%0x\n", - (unsigned)r, rs.success_mask); - } else { - fprintf(output, "Realization %u failed: %s\n", - (unsigned)r, END_TEXT[rs.end]); - } + fprintf(output, "Realization %u success mask: 0x%0x\n", + (unsigned)r, rs.success_mask); + fprintf(output, "Realization %u end: %s\n\n", + (unsigned)r, END_TEXT[rs.end]); } exit: diff --git a/src/ssol_solver_c.h b/src/ssol_solver_c.h @@ -57,25 +57,32 @@ enum realisation_mode { }; struct segment { + const struct ssol_instance* hit_instance; + const struct ssol_instance* self_instance; + const struct ssol_material* hit_material; double weight; - float range[2]; struct s3d_hit hit; double org[3], dir[4]; double hit_pos[3]; + double hit_pos_local[3]; /* in local coordinate, only set on punched shapes */ double hit_normal[3]; - double dist; + char hit_front; + char self_front; + char on_punched; /* is the hit on a punched shape? */ }; struct starting_point { - struct ssol_instance* instance; - struct ssol_material* material; - struct s3d_primitive primitive; + const struct ssol_instance* instance; + const struct ssol_material* material; + struct s3d_primitive sampl_primitive; double sundir[3]; double pos[3]; - double normal[3]; + double pos_local[3]; /* in local coordinate, only set on quadrics */ + double normal[3]; /* oriented to face the sun*/ double cos_sun; float uv[2]; - int front_exposed; + char front_exposed; + char on_punched; /* is the point on a punched shape? */ }; #include <rsys/dynamic_array.h> @@ -96,7 +103,6 @@ struct solver_data { struct ssp_ranst_piecewise_linear* sun_spectrum_ran; /* Tmp data used for propagation */ struct brdf_composite* brdfs; - struct ssol_instance* instance; struct surface_fragment fragment; }; @@ -113,7 +119,7 @@ struct realisation { }; #define SOLVER_DATA_NULL__ \ - {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, SURFACE_FRAGMENT_NULL__} + {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } static const struct solver_data SOLVER_DATA_NULL = SOLVER_DATA_NULL__; extern LOCAL_SYM res_T @@ -126,13 +132,10 @@ extern LOCAL_SYM struct segment* previous_segment(struct realisation* rs); extern LOCAL_SYM struct segment* -sun_segment(struct realisation* rs); - -extern LOCAL_SYM struct segment* current_segment(struct realisation* rs); extern LOCAL_SYM res_T -next_segment(struct realisation* rs); +setup_next_segment(struct realisation* rs); extern LOCAL_SYM void reset_segment(struct segment* seg); diff --git a/src/test_ssol_solver2.c b/src/test_ssol_solver2.c @@ -0,0 +1,177 @@ +/* Copyright (C) CNRS 2016 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "ssol.h" +#include "test_ssol_utils.h" +#include "test_ssol_geometries.h" +#include "test_ssol_materials.h" +#include "test_ssol_postprocess.h" + +#include "ssol_solver_c.h" + +#include <rsys/logger.h> +#include <rsys/double33.h> + +#include <star/s3d.h> +#include <star/ssp.h> + +/******************************************************************************* + * Test main program + ******************************************************************************/ +int +main(int argc, char** argv) +{ + struct logger logger; + struct mem_allocator allocator; + struct ssol_device* dev; + struct ssp_rng* rng; + struct ssol_scene* scene; + struct ssol_shape* square; + struct ssol_vertex_data attribs[1]; + struct ssol_shape* quad_square; + struct ssol_carving carving; + struct ssol_quadric quadric; + struct ssol_punched_surface punched; + struct ssol_material* m_mtl; + struct ssol_material* v_mtl; + struct ssol_mirror_shader shader; + struct ssol_object* m_object; + struct ssol_object* s_object; + struct ssol_object* t_object; + struct ssol_instance* heliostat; + struct ssol_instance* secondary; + struct ssol_instance* target; + struct ssol_sun* sun; + struct ssol_spectrum* spectrum; + double dir[3]; + double frequencies[3] = { 1, 2, 3 }; + double intensities[3] = { 1, 0.8, 1 }; + double transform1[12]; /* 3x4 column major matrix */ + double transform2[12]; /* 3x4 column major matrix */ + double polygon[] = { -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0 }; + const size_t npolygon_verts = sizeof(polygon) / sizeof(double[2]); + + (void) argc, (void) argv; + + d33_splat(transform1, 0); + d3_splat(transform1 + 9, 0); + d33_rotation_pitch(transform1, PI); /* flip faces: invert normal */ + transform1[9] = 2; /* +2 offset along X axis */ + transform1[11] = 2; /* +2 offset along Z axis */ + + d33_set_identity(transform2); + d3_splat(transform2 + 9, 0); + transform2[9] = 4; /* +4 offset along X axis */ + + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + + CHECK(logger_init(&allocator, &logger), RES_OK); + logger_set_stream(&logger, LOG_OUTPUT, log_stream, NULL); + logger_set_stream(&logger, LOG_ERROR, log_stream, NULL); + logger_set_stream(&logger, LOG_WARNING, log_stream, NULL); + + CHECK(ssol_device_create + (&logger, &allocator, SSOL_NTHREADS_DEFAULT, 0, &dev), RES_OK); + + CHECK(ssp_rng_create(&allocator, &ssp_rng_threefry, &rng), RES_OK); + CHECK(ssol_spectrum_create(dev, &spectrum), RES_OK); + CHECK(ssol_spectrum_setup(spectrum, frequencies, intensities, 3), 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); + + CHECK(ssol_solve(NULL, rng, 10, stdout), RES_BAD_ARG); + CHECK(ssol_solve(scene, NULL, 10, stdout), RES_BAD_ARG); + CHECK(ssol_solve(scene, rng, 0, stdout), RES_BAD_ARG); + CHECK(ssol_solve(scene, rng, 10, NULL), RES_BAD_ARG); + CHECK(ssol_solve(scene, rng, 10, stdout), RES_BAD_ARG); /* no geometry */ + + /* create scene content */ + + CHECK(ssol_shape_create_mesh(dev, &square), RES_OK); + attribs[0].usage = SSOL_POSITION; + attribs[0].get = get_position; + CHECK(ssol_mesh_setup(square, square_walls_ntris, get_ids, + square_walls_nverts, attribs, 1, (void*)&square_walls_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 = npolygon_verts; + carving.context = &polygon; + quadric.type = SSOL_QUADRIC_PLANE; + punched.nb_carvings = 1; + punched.quadric = &quadric; + punched.carvings = &carving; + CHECK(ssol_punched_surface_setup(quad_square, &punched), RES_OK); + + CHECK(ssol_material_create_mirror(dev, &m_mtl), RES_OK); + shader.normal = get_shader_normal; + shader.reflectivity = get_shader_reflectivity; + shader.roughness = get_shader_roughness; + CHECK(ssol_mirror_set_shader(m_mtl, &shader), RES_OK); + CHECK(ssol_material_create_virtual(dev, &v_mtl), RES_OK); + + CHECK(ssol_object_create(dev, square, m_mtl, m_mtl, &m_object), RES_OK); + CHECK(ssol_object_instantiate(m_object, &heliostat), RES_OK); + CHECK(ssol_instance_set_receiver(heliostat, "miroir", NULL), RES_OK); + CHECK(ssol_instance_set_target_mask(heliostat, 0x1), RES_OK); + CHECK(ssol_scene_attach_instance(scene, heliostat), RES_OK); + + CHECK(ssol_object_create(dev, quad_square, m_mtl, m_mtl, &s_object), RES_OK); + CHECK(ssol_object_instantiate(s_object, &secondary), RES_OK); + CHECK(ssol_instance_set_receiver(secondary, "secondaire", NULL), RES_OK); + CHECK(ssol_instance_set_transform(secondary, transform1), RES_OK); + CHECK(ssol_instance_set_target_mask(secondary, 0x2), RES_OK); + CHECK(ssol_scene_attach_instance(scene, secondary), RES_OK); + + CHECK(ssol_object_create(dev, square, v_mtl, v_mtl, &t_object), 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, "cible", NULL), RES_OK); + CHECK(ssol_instance_set_target_mask(target, 0x4), RES_OK); + CHECK(ssol_scene_attach_instance(scene, target), RES_OK); + + CHECK(ssol_solve(scene, rng, 20, stdout), RES_OK); + + /* 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_object), RES_OK); + CHECK(ssol_object_ref_put(s_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_device_ref_put(dev), RES_OK); + CHECK(ssol_scene_ref_put(scene), RES_OK); + CHECK(ssp_rng_ref_put(rng), RES_OK); + CHECK(ssol_spectrum_ref_put(spectrum), RES_OK); + CHECK(ssol_sun_ref_put(sun), RES_OK); + + logger_release(&logger); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHECK(mem_allocated_size(), 0); + + return 0; +}