commit 3f500ee497b80ed0005fe68e27c9b171c0b80842
parent fb1f3fe52b7d9f0711bc83d0e312b96fabbf4de1
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Wed, 12 Oct 2016 08:52:59 +0200
Major solver refactoring
The compilation works but the tests failed.
Flatten the solver code to exhibit global and per realisation data
structures. Simplify the radiative random walk by removing the deferred
segment intersector; the whole radiative random walk is now performed in
a purely forward manner.
Diffstat:
10 files changed, 687 insertions(+), 307 deletions(-)
diff --git a/src/ssol.h b/src/ssol.h
@@ -58,7 +58,7 @@ struct ssol_spectrum;
struct ssol_sun;
struct ssol_estimator;
-enum ssol_face_flag {
+enum ssol_side_flag {
SSOL_FRONT = BIT(0),
SSOL_BACK = BIT(1)
};
diff --git a/src/ssol_c.h b/src/ssol_c.h
@@ -27,11 +27,6 @@
#define SSOL_TO_S3D_NORMAL S3D_ATTRIB_0
#define SSOL_TO_S3D_TEXCOORD S3D_ATTRIB_1
-/* hack until the needed API comes from a merge */
-#define FRONT_FLAG 0
-#define RECEIVER_ID_MASK 0x7FFFFFFF
-#define BACK_FLAG 0x80000000
-
#ifndef NDEBUG
#define ASSERT_NAN(x, sz) { \
int i__; \
@@ -42,7 +37,17 @@
#define ASSERT_NAN(x, sz)
#endif
-#define NON_BOOL 99
+struct ray_data {
+ struct ssol_scene* scn;
+ struct s3d_primitive prim_from;
+ struct ssol_instance* inst_from;
+ enum ssol_side_flag side_from;
+
+ /* Output data */
+ double N[3];
+ double dst;
+};
+
static FINLINE enum s3d_attrib_usage
ssol_to_s3d_attrib_usage(const enum ssol_attrib_usage usage)
diff --git a/src/ssol_ranst_sun_wl.c b/src/ssol_ranst_sun_wl.c
@@ -144,9 +144,7 @@ ranst_sun_wl_ref_put(struct ranst_sun_wl* ran)
}
double
-ranst_sun_wl_get
- (const struct ranst_sun_wl* ran,
- struct ssp_rng* rng)
+ranst_sun_wl_get(const struct ranst_sun_wl* ran, struct ssp_rng* rng)
{
ASSERT(ran);
return ran->get(ran, rng);
diff --git a/src/ssol_scene.c b/src/ssol_scene.c
@@ -129,7 +129,7 @@ ssol_scene_attach_instance
/* Register the instance against the scene */
S3D(shape_get_id(instance->shape_rt, &id));
pinst = htable_instance_find(&scene->instances_rt, &id);
- if (pinst) {
+ if(pinst) {
/* already attached */
ASSERT(*pinst == instance); /* cannot be attached to another instance! */
return RES_OK;
@@ -202,14 +202,15 @@ 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) {
+ if(sun->scene_attachment || scene->sun) {
/* already attached: must be linked together */
- if (sun->scene_attachment != scene || scene->sun != sun)
+ if(sun->scene_attachment != scene || scene->sun != sun) {
/* if not detach first! */
return RES_BAD_ARG;
- else
+ } else {
/* nothing to change */
return RES_OK;
+ }
}
/* no previous attachment */
SSOL(sun_ref_get(sun));
@@ -235,16 +236,17 @@ ssol_scene_detach_sun(struct ssol_scene* scene, struct ssol_sun* sun)
res_T
ssol_scene_attach_atmosphere(struct ssol_scene* scene, struct ssol_atmosphere* atm)
{
- if (!scene || !atm)
+ if(!scene || !atm)
return RES_BAD_ARG;
- if (atm->scene_attachment || scene->atmosphere) {
+ if(atm->scene_attachment || scene->atmosphere) {
/* already attached: must be linked together */
- if (atm->scene_attachment != scene || scene->atmosphere != atm)
+ if(atm->scene_attachment != scene || scene->atmosphere != atm) {
/* if not detach first! */
return RES_BAD_ARG;
- else
+ } else {
/* nothing to change */
return RES_OK;
+ }
}
/* no previous attachment */
SSOL(atmosphere_ref_get(atm));
@@ -256,7 +258,7 @@ ssol_scene_attach_atmosphere(struct ssol_scene* scene, struct ssol_atmosphere* a
res_T
ssol_scene_detach_atmosphere(struct ssol_scene* scene, struct ssol_atmosphere* atm)
{
- if (!scene || !atm || !scene->atmosphere || atm->scene_attachment != scene)
+ if(!scene || !atm || !scene->atmosphere || atm->scene_attachment != scene)
return RES_BAD_ARG;
ASSERT(atm == scene->atmosphere);
@@ -270,15 +272,18 @@ ssol_scene_detach_atmosphere(struct ssol_scene* scene, struct ssol_atmosphere* a
* Local functions
******************************************************************************/
res_T
-scene_setup_s3d_sampling_scene
+scene_create_s3d_views
(struct ssol_scene* scn,
- char* has_sampled,
- char* has_receiver)
+ struct s3d_scene_view** out_view_rt,
+ struct s3d_scene_view** out_view_samp)
{
struct htable_instance_iterator it, end;
+ struct s3d_scene_view* view_rt = NULL;
+ struct s3d_scene_view* view_samp = NULL;
+ int has_sampled = 0;
+ int has_receiver = 0;
res_T res = RES_OK;
- char hs = 0, hr = 0;
- ASSERT(scn);
+ ASSERT(scn && out_view_rt && out_view_samp);
S3D(scene_clear(scn->scn_samp));
htable_instance_clear(&scn->instances_samp);
@@ -292,37 +297,59 @@ scene_setup_s3d_sampling_scene
htable_instance_iterator_next(&it);
if(inst->receiver_mask) {
- hr = 1;
+ has_receiver = 1;
}
if(!inst->sample) continue;
+
/* FIXME: should not sample virtual (material) instance
as material is used to compute output dir */
- hs = 1;
+ has_sampled = 1;
/* Attach the instantiated s3d sampling shape to the s3d sampling scene */
res = s3d_scene_attach_shape(scn->scn_samp, inst->shape_samp);
- if (res != RES_OK) goto error;
+ 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));
res = htable_instance_set(&scn->instances_samp, &id, &inst);
- if (res != RES_OK) goto error;
+ if(res != RES_OK) goto error;
/* Do not get a reference onto the instance since it was already referenced
* by the scene on its attachment */
}
+ if(!has_sampled) {
+ log_error(scn->dev, "No solstice instance to sample.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(!has_receiver) {
+ log_warning(scn->dev, "No receiver is defined.\n");
+ }
+
+ res = s3d_scene_view_create(scn->scn_rt, S3D_TRACE, &view_rt);
+ if(res != RES_OK) goto error;
+ res = s3d_scene_view_create(scn->scn_samp, S3D_SAMPLE, &view_samp);
+ if(res != RES_OK) goto error;
+
exit:
- if (has_sampled) *has_sampled = hs;
- if (has_receiver) *has_receiver = hr;
+ *out_view_rt = view_rt;
+ *out_view_samp = view_samp;
return res;
error:
S3D(scene_clear(scn->scn_samp));
htable_instance_clear(&scn->instances_samp);
- has_sampled = NULL;
- has_receiver = NULL;
+ if(view_rt) {
+ S3D(scene_view_ref_put(view_rt));
+ view_rt = NULL;
+ }
+ if(view_samp) {
+ S3D(scene_view_ref_put(view_samp));
+ view_samp = NULL;
+ }
goto exit;
}
@@ -331,126 +358,74 @@ 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 posf[3],
+ const float dirf[3],
+ void* ray_data,
+ void* filter_data)
{
struct ssol_instance* inst;
- const struct shaded_shape* shaded_shape;
- const struct ssol_shape* shape;
- struct realisation* rs = realisation;
- struct segment* seg;
+ struct ssol_material* mtl;
+ struct ray_data* rdata = ray_data;
+ const struct shaded_shape* sshape;
+ enum ssol_side_flag hit_side;
+ double pos[3], dir[3], N[3], dst = FLT_MAX;
size_t id;
- int is_receiver = 0;
- uint32_t inst_id;
- int32_t receiver_id;
-
- (void) filter_data, (void) org, (void) dir;
- ASSERT(rs);
- seg = current_segment(rs);
- ASSERT(seg);
-
- /* these components have been set */
- ASSERT_NAN(seg->dir, 3);
- ASSERT_NAN(seg->org, 3);
- ASSERT(seg->self_instance);
- ASSERT(seg->self_front != NON_BOOL);
-
- inst = *htable_instance_find(&rs->data.scene->instances_rt, &hit->prim.inst_id);
- id = *htable_shaded_shape_find
- (&inst->object->shaded_shapes_rt, &hit->prim.geom_id);
- shaded_shape = darray_shaded_shape_cdata_get(&inst->object->shaded_shapes)+id;
- shape = shaded_shape->shape;
- seg->on_punched = (shape->type == SHAPE_PUNCHED);
- switch (shape->type) {
- case SHAPE_MESH: {
- d3_set_f3(seg->hit_normal, hit->normal);
- /* use raytraced distance to fill hit_pos */
- d3_add(seg->hit_pos, seg->org, d3_muld(seg->hit_pos, seg->dir, hit->distance));
- seg->hit_distance = hit->distance;
- break;
- }
- case SHAPE_PUNCHED: {
- /* hits on quadrics must be recomputed more accurately */
- double org_local[3], hit_pos_local[3], dir_local[3];
- double R[9]; /* Rotation matrix */
- double T[3]; /* Translation vector */
- double R_invtrans[9]; /* Inverse transpose rotation matrix */
- double T_inv[3]; /* Inverse of the translation vector */
- int valid;
-
- if(d33_is_identity(shape->quadric.transform)) {
- d33_set(R, inst->transform);
- d3_set (T, inst->transform+9);
+ (void)filter_data;
+ ASSERT(hit && posf && dirf);
+
+ /* No ray data => nothing to filter */
+ if(!ray_data) return 0;
+
+ /* Retrieve the intersected instance and shaded shape */
+ inst = *htable_instance_find(&rdata->scn->instances_rt, &hit->prim.inst_id);
+ id = *htable_shaded_shape_find(&inst->object->shaded_shapes_rt, &hit->prim.geom_id);
+ sshape = darray_shaded_shape_cdata_get(&inst->object->shaded_shapes)+id;
+
+ /* Discard self intersection */
+ switch(sshape->shape->type) {
+ case SHAPE_MESH:
+ if(S3D_PRIMITIVE_EQ(&hit->prim, &rdata->prim_from)) {
+ /* Discard self intersection for mesh, i.e. when the intersected
+ * primitive is the primitive from which the ray starts */
+ return 1;
} else {
- d33_muld33(R, shape->quadric.transform, inst->transform);
- d33_muld3 (T, shape->quadric.transform, inst->transform+9);
+ /* No self intersection. Define which side of the primitive is hit.
+ * Note that incoming direction points inward the primitive */
+ hit_side = f3_dot(hit->normal, dirf) < 0 ? SSOL_FRONT : SSOL_BACK;
}
- d33_invtrans(R_invtrans, R);
- d3_minus(T_inv, T);
-
- /* get org in local coordinate */
- d3_set(org_local, seg->org);
- d3_add(org_local, org_local, T_inv);
- d3_muld33(org_local, org_local, R_invtrans);
-
- /* get dir in local */
- d3_muld33(dir_local, seg->dir, R_invtrans);
- /* recompute hit */
- valid = punched_shape_intersect_local(shape, org_local, dir_local,
- hit->distance, hit_pos_local, seg->hit_normal, &seg->hit_distance);
- if (!valid) return 1;
- /* transform point to world */
- d33_muld3(seg->hit_pos, R, hit_pos_local);
- d3_add(seg->hit_pos, seg->hit_pos, T);
- /* transform normal to world */
- d33_muld3(seg->hit_normal, R_invtrans, seg->hit_normal);
break;
- }
+ 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);
+ if(dst >= FLT_MAX) {
+ /* No projection is found => the ray does not intersect the quadric */
+ return 1;
+ } else if(inst == rdata->inst_from) {
+ /* If the intersected instance is the one from which the ray starts,
+ * ensure that the ray does not intersect the opposite side of the
+ * quadric */
+ hit_side = d3_dot(dir, N) < 0 ? SSOL_FRONT : SSOL_BACK;
+ if(hit_side != rdata->side_from) {
+ return 1;
+ }
+ }
default: FATAL("Unreachable code.\n"); break;
}
- d3_normalize(seg->hit_normal, seg->hit_normal);
- /* Discard self intersection */
- seg->hit_front = d3_dot(seg->hit_normal, seg->dir) < 0;
- if (seg->self_instance == inst && seg->self_front != seg->hit_front) {
+ /* Discard virtual material that are not receivers */
+ mtl = hit_side == SSOL_FRONT ? sshape->mtl_front : sshape->mtl_back;
+ if(!(inst->receiver_mask & (int)hit_side) && mtl->type == MATERIAL_VIRTUAL)
return 1;
- }
-
- SSOL(instance_get_id(inst, &inst_id));
- ASSERT(inst_id < INT32_MAX);
- if(seg->hit_front) {
- seg->hit_material = shaded_shape->mtl_front;
- is_receiver = inst->receiver_mask & SSOL_FRONT;
- receiver_id = (int32_t)inst_id;
- } else {
- d3_muld(seg->hit_normal, seg->hit_normal, -1);
- seg->hit_material = shaded_shape->mtl_back;
- is_receiver = inst->receiver_mask & SSOL_BACK;
- receiver_id = -(int32_t)inst_id;
- }
-
- /* Check if the hit surface is a receiver that registers hit data
- * impacts on receivers before sampled surfaces are not registered */
- if (!seg->sun_segment && is_receiver) {
- struct receiver_record candidate;
- f3_set_d3(candidate.dir, seg->dir);
- candidate.hit_distance = (float)seg->hit_distance;
- f3_set_d3(candidate.hit_normal, seg->hit_normal);
- f3_set_d3(candidate.hit_pos, seg->hit_pos);
- candidate.instance = inst;
- candidate.receiver_id = receiver_id;
- f2_set(candidate.uv, seg->hit.uv);
- darray_receiver_record_push_back(
- &rs->data.receiver_record_candidates, &candidate);
- }
- if(seg->hit_material->type == MATERIAL_VIRTUAL) {
- return 1; /* Discard virtual material */
+ /* Save the nearest intersected quadric point */
+ if(sshape->shape->type == SHAPE_PUNCHED && rdata->dst >= dst) {
+ d3_set(rdata->N, N);
+ rdata->dst = dst;
}
- seg->hit_instance = inst;
return 0;
}
diff --git a/src/ssol_scene_c.h b/src/ssol_scene_c.h
@@ -50,14 +50,8 @@ struct ssol_scene {
ref_T ref;
};
-/* Fill the Star-3D sampling scene with the Star-3D shape of the instances */
-extern LOCAL_SYM res_T
-scene_setup_s3d_sampling_scene
- (struct ssol_scene* scn,
- char* has_sampled,
- char* has_receiver);
-
-/* TODO comment */
+/* Create the Star-3D views of the RT and sampling scenes. Return an error if
+ * the sampling scene is empty. */
extern LOCAL_SYM res_T
scene_create_s3d_views
(struct ssol_scene* scn,
diff --git a/src/ssol_shape.c b/src/ssol_shape.c
@@ -470,7 +470,7 @@ quadric_setup_s3d_shape_rt
* profile of the quadric */
static res_T
quadric_setup_s3d_shape_samp
- (const struct ssol_quadric* quadric,
+ (const struct ssol_quadric* quadric,
const struct darray_double* coords,
const struct darray_size_t* ids,
struct s3d_shape* shape)
@@ -610,37 +610,44 @@ quadric_solve_second
return 0;
}
-static void
+static FINLINE void
quadric_plane_gradient_local(double grad[3])
{
+ ASSERT(grad);
grad[0] = 0;
grad[1] = 0;
grad[2] = 1;
}
-static void
+static FINLINE void
quadric_parabol_gradient_local
(const struct ssol_quadric_parabol* quad,
const double pt[3],
double grad[3])
{
- grad[0] = -pt[0];
- grad[1] = -pt[1];
- grad[2] = 2 * quad->focal;
+ double tmp[3];
+ ASSERT(quad && pt && grad);
+ tmp[0] = -pt[0];
+ tmp[1] = -pt[1];
+ tmp[2] = 2 * quad->focal;
+ d3_set(grad, tmp);
}
-static void
+static FINLINE void
quadric_parabolic_cylinder_gradient_local
(const struct ssol_quadric_parabolic_cylinder* quad,
const double pt[3],
double grad[3])
{
- grad[0] = 0;
- grad[1] = -pt[1];
- grad[2] = 2 * quad->focal;
+ double tmp[3];
+ ASSERT(quad && pt && grad);
+ tmp[0] = 0;
+ tmp[1] = -pt[1];
+ tmp[2] = 2 * quad->focal;
+ d3_set(grad, tmp);
}
-static int
+static FINLINE int
quadric_plane_intersect_local
(const double org[3],
const double dir[3],
@@ -652,14 +659,20 @@ quadric_plane_intersect_local
const double a = 0;
const double b = dir[2];
const double c = org[2];
- int sol = quadric_solve_second(a, b, c, 0, dist);
+ double tmp[3];
+ double dst;
+ int sol = quadric_solve_second(a, b, c, 0, &dst);
+
if(!sol) return 0;
- d3_add(pt, org, d3_muld(pt, dir, *dist));
+ d3_add(tmp, org, d3_muld(tmp, dir, dst));
+
+ d3_set(pt, tmp);
quadric_plane_gradient_local(grad);
+ *dist = dst;
return 1;
}
-static int
+static FINLINE int
quadric_parabol_intersect_local
(const struct ssol_quadric_parabol* quad,
const double org[3],
@@ -681,7 +694,7 @@ quadric_parabol_intersect_local
return 1;
}
-static int
+static FINLINE int
quadric_parabolic_cylinder_intersect_local
(const struct ssol_quadric_parabolic_cylinder* quad,
const double org[3],
@@ -702,24 +715,7 @@ quadric_parabolic_cylinder_intersect_local
return 1;
}
-static void
-shape_release(ref_T* ref)
-{
- struct ssol_device* dev;
- struct ssol_shape* shape = CONTAINER_OF(ref, struct ssol_shape, ref);
- ASSERT(ref);
- dev = shape->dev;
- ASSERT(dev && dev->allocator);
- if(shape->shape_rt) S3D(shape_ref_put(shape->shape_rt));
- if(shape->shape_samp) S3D(shape_ref_put(shape->shape_samp));
- MEM_RM(dev->allocator, shape);
- SSOL(device_ref_put(dev));
-}
-
-/*******************************************************************************
- * Local functions
- ******************************************************************************/
-void
+static FINLINE void
punched_shape_set_z_local(const struct ssol_shape* shape, double pt[3])
{
ASSERT(shape && pt);
@@ -744,7 +740,7 @@ punched_shape_set_z_local(const struct ssol_shape* shape, double pt[3])
}
}
-void
+static FINLINE void
punched_shape_set_normal_local
(const struct ssol_shape* shape,
const double pt[3],
@@ -753,57 +749,170 @@ punched_shape_set_normal_local
ASSERT(shape && pt);
ASSERT(shape->type == SHAPE_PUNCHED);
switch (shape->quadric.type) {
- case SSOL_QUADRIC_PLANE: {
- quadric_plane_gradient_local(normal);
- break;
- }
- case SSOL_QUADRIC_PARABOLIC_CYLINDER: {
- const struct ssol_quadric_parabolic_cylinder* quad
- = &shape->quadric.data.parabolic_cylinder;
- quadric_parabolic_cylinder_gradient_local(quad, pt, normal);
- break;
- }
- case SSOL_QUADRIC_PARABOL: {
- const struct ssol_quadric_parabol* quad = &shape->quadric.data.parabol;
- quadric_parabol_gradient_local(quad, pt, normal);
- break;
- }
- default: FATAL("Unreachable code\n"); break;
+ case SSOL_QUADRIC_PLANE:
+ quadric_plane_gradient_local(normal);
+ break;
+ case SSOL_QUADRIC_PARABOLIC_CYLINDER:
+ quadric_parabolic_cylinder_gradient_local
+ (&shape->quadric.data.parabolic_cylinder, pt, normal);
+ break;
+ case SSOL_QUADRIC_PARABOL: {
+ quadric_parabol_gradient_local
+ (&shape->quadric.data.parabol, pt, normal);
+ break;
+ }
+ default: FATAL("Unreachable code\n"); break;
}
}
-int
+static FINLINE int
punched_shape_intersect_local
(const struct ssol_shape* shape,
const double org[3],
const double dir[3],
const double hint,
double pt[3],
- double normal[3],
+ double N[3],
double* dist)
{
- ASSERT(shape && org && dir && hint >= 0 && pt && normal && dist);
+ int hit;
+ ASSERT(shape && org && dir && hint >= 0 && pt && N && dist);
ASSERT(shape->type == SHAPE_PUNCHED);
ASSERT(dir[0] || dir[1] || dir[2]);
- /* hits on quadrics must be recomputed more accurately */
+
+ /* Hits on quadrics must be recomputed more accurately */
switch (shape->quadric.type) {
- case SSOL_QUADRIC_PLANE: {
- return quadric_plane_intersect_local(org, dir, pt, normal, dist);
- }
- case SSOL_QUADRIC_PARABOLIC_CYLINDER: {
- const struct ssol_quadric_parabolic_cylinder* quad
- = &shape->quadric.data.parabolic_cylinder;
- return quadric_parabolic_cylinder_intersect_local(
- quad, org, dir, hint, pt, normal, dist);
+ case SSOL_QUADRIC_PLANE:
+ hit = quadric_plane_intersect_local(org, dir, pt, N, dist);
+ break;
+ case SSOL_QUADRIC_PARABOLIC_CYLINDER:
+ hit = quadric_parabolic_cylinder_intersect_local
+ (&shape->quadric.data.parabolic_cylinder, 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);
+ break;
+ default: FATAL("Unreachable code\n"); break;
}
- case SSOL_QUADRIC_PARABOL: {
- const struct ssol_quadric_parabol* quad = &shape->quadric.data.parabol;
- return quadric_parabol_intersect_local(
- quad, org, dir, hint, pt, normal, dist);
+ return hit;
+}
+
+static void
+shape_release(ref_T* ref)
+{
+ struct ssol_device* dev;
+ struct ssol_shape* shape = CONTAINER_OF(ref, struct ssol_shape, ref);
+ ASSERT(ref);
+ dev = shape->dev;
+ ASSERT(dev && dev->allocator);
+ if(shape->shape_rt) S3D(shape_ref_put(shape->shape_rt));
+ if(shape->shape_samp) S3D(shape_ref_put(shape->shape_samp));
+ MEM_RM(dev->allocator, shape);
+ SSOL(device_ref_put(dev));
+}
+
+/*******************************************************************************
+ * Local functions
+ ******************************************************************************/
+void
+punched_shape_project_point
+ (struct ssol_shape* shape,
+ const double transform[12], /* Shape to world space transformation */
+ const double pos[3], /* World space position near of the quadric */
+ 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 */
+ double R_invtrans[9]; /* Inverse transpose of R */
+ double T[3]; /* Quadric to world translation vector */
+ double T_inv[3]; /* Inverse of T */
+ double pos_local[3];
+ double N_local[3];
+ ASSERT(shape && transform && pos && pos_quadric && N_quadric);
+ ASSERT(shape->type == SHAPE_PUNCHED);
+
+ /* Compute world<->quadric space transformations */
+ if(d33_is_identity(shape->quadric.transform)) {
+ d33_set(R, transform);
+ d3_set (T, transform+9);
+ } else {
+ d33_muld33(R, shape->quadric.transform, transform);
+ d33_muld3 (T, shape->quadric.transform, transform+9);
}
- default: FATAL("Unreachable code\n"); break;
+ d33_invtrans(R_invtrans, R);
+ 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);
+
+ /* Project pos_local onto the quadric and compute its associated normal */
+ punched_shape_set_z_local(shape, pos_local);
+ punched_shape_set_normal_local(shape, pos_local, N_local);
+
+ /* 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);
+}
+
+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 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 */
+ double R_invtrans[9]; /* Inverse transpose of R */
+ double T[3]; /* Quadric to world translation vector */
+ double T_inv[3]; /* Inverse of T */
+ double dir_local[3];
+ double pos_local[3];
+ double N_local[3];
+ double dst; /* Hit distance */
+ int valid;
+ ASSERT(shape && transform && pos && pos_quadric && N_quadric);
+ ASSERT(shape->type == SHAPE_PUNCHED);
+
+ /* Compute world<->quadric space transformations */
+ if(d33_is_identity(shape->quadric.transform)) {
+ d33_set(R, transform);
+ d3_set (T, transform+9);
+ } else {
+ d33_muld33(R, shape->quadric.transform, transform);
+ d33_muld3 (T, shape->quadric.transform, transform+9);
}
- return 0;
+ d33_invtrans(R_invtrans, R);
+ 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);
+
+ /* 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);
+ 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;
}
/*******************************************************************************
diff --git a/src/ssol_shape_c.h b/src/ssol_shape_c.h
@@ -37,32 +37,26 @@ struct ssol_shape {
ref_T ref;
};
-/* Compute the z value from x,y according to the punched face quadric's
- * equation */
+/* Project pos onto the punched surface and retrieve its associated normal */
extern LOCAL_SYM void
-punched_shape_set_z_local
- (const struct ssol_shape* shape,
- double pt[3]);
-
-/* set the normal to a punched shape at pt */
-extern LOCAL_SYM void
-punched_shape_set_normal_local
- (const struct ssol_shape* shape,
- const double pt[3],
- double normal[3]);
-
-/* Search for an exact ray intersection on a punched shape
- * hint is an estimate of the distance (can be from raytracing)
- * Return 1 on success */
-extern LOCAL_SYM int
-punched_shape_intersect_local
- (const struct ssol_shape* shape,
- const double org[3],
- const double dir[3],
- const double hint,
- double pt[3],
- double normal[3],
- double* dist);
+punched_shape_project_point
+ (struct ssol_shape* shape,
+ const double transform[12], /* Shape to world space transformation */
+ const double pos[3], /* World space position near of the quadric */
+ double pos_quadric[3], /* World space position onto the quadric */
+ double N_quadric[3]); /* World space normal onto the quadric */
+
+/* Return the hit distance of the ray wrt the punched surface. >= FLT_MAX if
+ * the ray does not intersect the quadric */
+extern LOCAL_SYM double
+punched_shape_trace_ray
+ (struct ssol_shape* shape,
+ const double transform[12], /* Shape to world space transformation */
+ 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 */
#endif /* SSOL_SHAPE_C_H */
diff --git a/src/ssol_solver.c b/src/ssol_solver.c
@@ -13,6 +13,8 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+#define _POSIX_C_SOURCE 200112L /* nextafterf support */
+
#include "ssol.h"
#include "ssol_c.h"
#include "ssol_atmosphere_c.h"
@@ -42,7 +44,6 @@
/*******************************************************************************
* Helper functions
******************************************************************************/
-
static FINLINE void
solstice_trace_ray(struct realisation* rs)
{
@@ -56,7 +57,7 @@ solstice_trace_ray(struct realisation* rs)
/* the filter function recomputes intersections on quadrics and sets seg */
}
-static int
+static INLINE int
cmp_candidates(const void* _c1, const void* _c2)
{
const struct receiver_record* c1 = _c1;
@@ -102,67 +103,7 @@ check_scene(const struct ssol_scene* scene, const char* caller)
/*******************************************************************************
* Local functions
******************************************************************************/
-res_T
-set_sun_distributions(struct solver_data* data)
-{
- struct ssol_spectrum* spectrum;
- struct ssol_device* dev;
- const struct ssol_sun* sun;
- const double* wavelengths;
- const double* intensities;
- res_T res = RES_OK;
- size_t sz;
- if (!data) return RES_BAD_ARG;
-
- ASSERT(data->scene);
- sun = data->scene->sun;
- ASSERT(sun);
- dev = data->scene->dev;
- ASSERT(dev && dev->allocator);
- /* first set the spectrum distribution */
- res = ranst_sun_wl_create(dev->allocator, &data->sun_wl_ran);
- if (res != RES_OK) goto error;
- spectrum = sun->spectrum;
- wavelengths = darray_double_cdata_get(&spectrum->wavelengths);
- intensities = darray_double_cdata_get(&spectrum->intensities);
- sz = darray_double_size_get(&spectrum->wavelengths);
- res = ranst_sun_wl_setup(
- data->sun_wl_ran, wavelengths, intensities, sz);
- if (res != RES_OK) goto error;
- /* then the direction distribution */
- res = ranst_sun_dir_create(dev->allocator, &data->sun_dir_ran);
- if (res != RES_OK) goto error;
- switch (sun->type) {
- case SUN_DIRECTIONAL:
- res = ranst_sun_dir_dirac_setup(data->sun_dir_ran, sun->direction);
- break;
- case SUN_PILLBOX:
- res = ranst_sun_dir_pillbox_setup
- (data->sun_dir_ran, sun->data.pillbox.aperture, sun->direction);
- break;
- case SUN_BUIE:
- res = ranst_sun_dir_buie_setup
- (data->sun_dir_ran, sun->data.csr.ratio, sun->direction);
- break;
- default:
- res = RES_OK;
- FATAL("Unreachable code\n");
- }
-
-exit:
- return res;
-error:
- if (data->sun_wl_ran) {
- CHECK(ranst_sun_wl_ref_put(data->sun_wl_ran), RES_OK);
- data->sun_wl_ran = NULL;
- }
- if (data->sun_dir_ran) {
- CHECK(ranst_sun_dir_ref_put(data->sun_dir_ran), RES_OK);
- data->sun_dir_ran = NULL;
- }
- goto exit;
-}
-
+#if 0
static void
release_solver_data(struct solver_data* data)
{
@@ -180,7 +121,10 @@ release_solver_data(struct solver_data* data)
}
res_T
-set_views(struct solver_data* data)
+create_s3d_views
+ (struct s3d_scene* scn,
+ struct s3d_scene_view** view_rt,
+ struct s3d_scene_)
{
res_T res = RES_OK;
char has_sampled, has_receiver;
@@ -214,7 +158,9 @@ error:
release_solver_data(data);
goto exit;
}
+#endif
+#if 0
struct segment*
previous_segment(struct realisation* rs)
{
@@ -452,10 +398,12 @@ release_realisation(struct realisation* rs)
release_solver_data(&rs->data);
darray_segment_release(&rs->segments);
}
+#endif
/* partial setting of rs->start
* front_exposed, cos_sun will be set later
* material is set to NULL and will be set later */
+#if 0
static void
sample_starting_point(struct realisation* rs)
{
@@ -543,7 +491,60 @@ sample_starting_point(struct realisation* rs)
/* will be defined later, depending on wich side sees the sun */
start->material = NULL;
}
+#endif
+#if 0
+static void
+sample_starting_point
+ (struct s3d_scene_view* view,
+ struct ssp_rng* rng,
+ double pos[3])
+ double pos[3],
+ double N[3])
+{
+ struct s3d_attrib attrib;
+ struct s3d_primitive prim;
+ struct ssol_instance* inst;
+ struct shaded_shape* shape;
+ size_t id;
+ float uv[2];
+ float r1, r2, r3;
+ ASSERT(rng);
+
+ /* Sample a point into the scene view */
+ r1 = ssp_rng_canonical_float(rng);
+ r2 = ssp_rng_canonical_float(rng);
+ r3 = ssp_rng_canonical_float(rng);
+ S3D(scene_view_sample(view, r1, r2, r3, &prim, uv));
+
+ /* Retrieve the position the sampled point */
+ S3D(primitive_get_attrib(&prim, S3D_POSITION, uv, &attrib));
+ d3_set_f3(pos, attrib.value);
+
+ /* Retrieve the sampled shaded shape */
+ inst = htable_instance_find(scn->instances_samp, prim.inst_id);
+ id = htable_shaded_shape_find(inst->object->shaded_shapes_samp, prim.geom_id);
+ shape = darray_shaded_shape_cdata_get(inst->object->shaded_shapes)[id].shape;
+
+ switch(shape->type) {
+ case SHAPE_MESH:
+ /* Simply fetch the normal of the sampled point. Returned normal is in
+ * world space */
+ S3D(primitive_get_attrib(&prim, S3D_GEOMETRY_NORMAL, uv, &attrib));
+ d3_normalize(N, d3_set_f3(N, attrib.value));
+ break;
+ case SHAPE_PUNCHED:
+ /* Project the sampeld point onto the quadric and compute its associated
+ * normal. */
+ /* FIXME Is it really necessary since this point is only used to trace a
+ * ray toward the sun? */
+ shape_project_point_onto_quadric(shape, inst->transform, pos, pos, N);
+ break;
+ }
+}
+#endif
+
+#if 0
static void
sample_input_sundir(struct realisation* rs)
{
@@ -557,7 +558,9 @@ sample_wavelength(struct realisation* rs)
ASSERT(rs);
rs->wavelength = ranst_sun_wl_get(rs->data.sun_wl_ran, rs->data.rng);
}
+#endif
+#if 0
/* check if the sampled point as described in rs->start receives sun light
* return 1 if positive
* if positive, fills sun_segment */
@@ -769,10 +772,12 @@ propagate(struct realisation* rs)
surface_fragment_setup(&rs->data.fragment, seg->hit_pos, seg->dir,
seg->hit_normal, &seg->hit.prim, seg->hit.uv);
}
+#endif
/*******************************************************************************
* Exported function
******************************************************************************/
+#if 0
res_T
ssol_solve
(struct ssol_scene* scene,
@@ -843,3 +848,232 @@ error:
/* TODO: release data */
goto exit;
}
+#endif
+
+res_T
+ssol_solve
+ (struct ssol_scene* scn,
+ struct ssp_rng* rng,
+ const size_t nrealisations,
+ FILE* output,
+ struct ssol_estimator* estimator)
+{
+ 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 ssf_bsdf* bsdf = NULL;
+ float sampled_area;
+ size_t i;
+ res_T res = RES_OK;
+
+ if(!scn || !rng || !nrealisations || !output || !estimator) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ 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);
+ if(res != RES_OK) goto error;
+ res = sun_create_distributions(scn->sun, &ran_sun_dir, &ran_sun_wl);
+ if(res != RES_OK) goto error;
+ S3D(scene_view_compute_area(view_samp, &sampled_area));
+
+ /* Create per thread data structures. TODO create as many of these structures
+ * as there are threads */
+ res = ssf_bsdf_create(scn->dev->allocator, &bsdf);
+ if(res != RES_OK) goto error;
+
+ FOR_EACH(i, 0, nrealisations) {
+ struct s3d_attrib attr;
+ struct s3d_hit hit;
+ struct s3d_primitive prim;
+ struct surface_fragment frag;
+ struct ssol_instance* inst;
+ const struct shaded_shape* sshape;
+ struct ray_data ray_data;
+ double pos[3], dir[3], N[3], tmp[3], weight, cos_dir_N, wl;
+ float posf[3], dirf[3], uv[2];
+ float range[2] = { 0, FLT_MAX };
+ size_t id;
+ int depth = 0;
+
+ /* Sample a point into the scene view */
+ S3D(scene_view_sample(view_samp,
+ ssp_rng_canonical_float(rng),
+ ssp_rng_canonical_float(rng),
+ ssp_rng_canonical_float(rng),
+ &prim, uv));
+
+ /* Retrieve the position of the sampled point */
+ S3D(primitive_get_attrib(&prim, S3D_POSITION, uv, &attr));
+ f3_set(posf, attr.value);
+
+ /* Retrieve the sampled instance and shaded shape */
+ inst = *htable_instance_find(&scn->instances_samp, &prim.inst_id);
+ id = *htable_shaded_shape_find(&inst->object->shaded_shapes_samp, &prim.geom_id);
+ sshape = darray_shaded_shape_cdata_get(&inst->object->shaded_shapes)+id;
+
+ /* Fetch the current position and its associated normal */
+ switch(sshape->shape->type) {
+ case SHAPE_MESH:
+ S3D(primitive_get_attrib(&prim, S3D_GEOMETRY_NORMAL, uv, &attr));
+ f3_normalize(attr.value, attr.value);
+ d3_set_f3(N, attr.value);
+ break;
+ case SHAPE_PUNCHED:
+ d3_set_f3(pos, posf);
+ punched_shape_project_point(sshape->shape, inst->transform, pos, pos, N);
+ break;
+ default: FATAL("Unreachable code"); break;
+ }
+
+ /* Sample a sun direction */
+ ranst_sun_dir_get(ran_sun_dir, rng, dir);
+ cos_dir_N = d3_dot(N, dir);
+
+ /* Initialise the ray data to avoid self intersection */
+ ray_data.scn = scn;
+ ray_data.prim_from = prim;
+ ray_data.inst_from = inst;
+ ray_data.side_from = cos_dir_N < 0 ? SSOL_FRONT : SSOL_BACK;
+
+ /* Trace a ray toward the sun to check if the sampled point is occluded */
+ f3_minus(dirf, f3_set_d3(dirf, dir));
+ ray_data.dst = FLT_MAX;
+ S3D(scene_view_trace_ray(view_rt, posf, dirf, range, &ray_data, &hit));
+ if(!S3D_HIT_NONE(&hit)) { /* First ray is occluded */
+ estimator->shadow.weight += weight;
+ estimator->shadow.sqr_weight += weight*weight;
+ continue;
+ }
+
+ /* Sample a wavelength */
+ wl = ranst_sun_wl_get(ran_sun_wl, rng);
+
+ /* Initialise the integration weight */
+ weight = scn->sun->dni * sampled_area * fabs(cos_dir_N);
+
+ for(;;) {
+ struct ssol_material* mtl;
+ double pdf;
+ uint32_t inst_id;
+ int32_t receiver_id;
+ int is_receiver;
+
+ if(cos_dir_N < 0) { /* Front face */
+ mtl = sshape->mtl_front;
+ is_receiver = inst->receiver_mask & SSOL_FRONT;
+ SSOL(instance_get_id(inst, &inst_id));
+ receiver_id = (int32_t)inst_id;
+
+ } else { /* Back face */
+ mtl = sshape->mtl_back;
+ is_receiver = inst->receiver_mask & SSOL_BACK;
+ SSOL(instance_get_id(inst, &inst_id));
+ receiver_id = -(int32_t)inst_id;
+ d3_minus(N, N);
+ }
+
+ if(is_receiver) {
+ struct ssol_receiver_data out;
+ size_t n;
+ out.realization_id = i;
+ out.date = 0; /* TODO */
+ out.segment_id = (uint32_t)depth;
+ out.receiver_id = receiver_id;
+ out.wavelength = (float)wl;
+ f3_set_d3(out.pos, pos);
+ f3_set_d3(out.in_dir, dir);
+ f3_set_d3(out.normal, N);
+ f2_set(out.uv, uv);
+ out.weight = weight;
+ n = fwrite(&out, sizeof(out), 1, output);
+ if(n != sizeof(out)) {
+ res = RES_IO_ERR;
+ goto error;
+ }
+ }
+
+ if(mtl->type == MATERIAL_VIRTUAL) {
+ range[0] = nextafterf(hit.distance, FLT_MAX);
+ range[1] = FLT_MAX;
+ } else {
+ /* TODO ensure that if `prim' was sampled, then the surface fragment
+ * setup remains valid in *all* situations */
+ surface_fragment_setup(&frag, pos, dir, N, &prim, uv);
+
+ /* Shade the surface fragment */
+ res = material_shade(mtl, &frag, wl, bsdf);
+ if(res != RES_OK) goto error;
+
+ /* By convention, Star-SF assumes that incoming and reflected
+ * directions point outward the surface => negate incoming dir */
+ d3_minus(dir, dir);
+
+ /* Sample the BSDF to find the next direction to trace */
+ weight *= ssf_bsdf_sample(bsdf, rng, dir, frag.Ns, dir, &pdf);
+
+ range[0] = 0;
+ range[1] = FLT_MAX;
+ }
+
+ /* Trace the next ray */
+ ray_data.dst = FLT_MAX;
+ f3_set_d3(dirf, dir);
+ f3_set_d3(posf, pos);
+ S3D(scene_view_trace_ray(view_rt, posf, dirf, range, &ray_data, &hit));
+ if(S3D_HIT_NONE(&hit)) break;
+
+ ++depth;
+
+ /* Retrieve the hit instance and shaded shape */
+ inst = *htable_instance_find(&scn->instances_rt, &prim.inst_id);
+ id = *htable_shaded_shape_find(&inst->object->shaded_shapes_rt, &prim.geom_id);
+ sshape = darray_shaded_shape_cdata_get(&inst->object->shaded_shapes)+id;
+
+ /* Fetch the current position and its associated normal */
+ switch(sshape->shape->type) {
+ case SHAPE_MESH:
+ f3_normalize(hit.normal, hit.normal);
+ d3_set_f3(N, hit.normal);
+ f3_mulf(dirf, dirf, hit.distance);
+ f3_add(posf, posf, dirf);
+ d3_set_f3(pos, posf);
+ break;
+ case SHAPE_PUNCHED:
+ d3_normalize(N, ray_data.N);
+ d3_muld(tmp, dir, ray_data.dst);
+ d3_add(pos, pos, tmp);
+ break;
+ default: FATAL("Unreachable code"); break;
+ }
+ cos_dir_N = d3_dot(dir, N);
+
+ /* Setup the ray data to avoid self intersection */
+ ray_data.prim_from = hit.prim;
+ ray_data.inst_from = inst;
+ ray_data.side_from = cos_dir_N < 0 ? SSOL_FRONT : SSOL_BACK;
+ }
+
+ if(depth == 0) {
+ estimator->missing.weight += weight;
+ estimator->missing.sqr_weight += weight*weight;
+ }
+
+ }
+
+exit:
+ if(view_rt) S3D(scene_view_ref_put(view_rt));
+ if(view_samp) S3D(scene_view_ref_put(view_samp));
+ 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(bsdf) SSF(bsdf_ref_put(bsdf));
+ return res;
+error:
+ goto exit;
+}
+
diff --git a/src/ssol_sun.c b/src/ssol_sun.c
@@ -14,8 +14,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "ssol.h"
-#include "ssol_sun_c.h"
#include "ssol_device_c.h"
+#include "ssol_sun_c.h"
+#include "ssol_ranst_sun_dir.h"
+#include "ssol_ranst_sun_wl.h"
+#include "ssol_spectrum_c.h"
#include <rsys/rsys.h>
#include <rsys/mem_allocator.h>
@@ -176,3 +179,61 @@ ssol_sun_set_buie_param
return RES_OK;
}
+/*******************************************************************************
+ * Local function
+ ******************************************************************************/
+res_T
+sun_create_distributions
+ (struct ssol_sun* sun,
+ struct ranst_sun_dir** out_ran_dir,
+ struct ranst_sun_wl** out_ran_wl)
+{
+ struct ranst_sun_dir* ran_dir = NULL;
+ struct ranst_sun_wl* ran_wl = NULL;
+ res_T res = RES_OK;
+ ASSERT(sun && out_ran_dir && out_ran_wl);
+
+ /* Create and setup the spectrum distribution */
+ res = ranst_sun_wl_create(sun->dev->allocator, &ran_wl);
+ if(res != RES_OK) goto error;
+ res = ranst_sun_wl_setup(ran_wl,
+ darray_double_cdata_get(&sun->spectrum->wavelengths),
+ darray_double_cdata_get(&sun->spectrum->intensities),
+ darray_double_size_get(&sun->spectrum->wavelengths));
+ if(res != RES_OK) goto error;
+
+ /* Create and setup the the direction distribution */
+ res = ranst_sun_dir_create(sun->dev->allocator, &ran_dir);
+ if(res != RES_OK) goto error;
+ switch(sun->type) {
+ case SUN_DIRECTIONAL:
+ res = ranst_sun_dir_dirac_setup(ran_dir, sun->direction);
+ break;
+ case SUN_PILLBOX:
+ res = ranst_sun_dir_pillbox_setup
+ (ran_dir, sun->data.pillbox.aperture, sun->direction);
+ break;
+ case SUN_BUIE:
+ res = ranst_sun_dir_buie_setup
+ (ran_dir, sun->data.csr.ratio, sun->direction);
+ break;
+ default: FATAL("Unreachable code\n"); break;
+ }
+
+exit:
+ *out_ran_dir = ran_dir;
+ *out_ran_wl = ran_wl;
+ return res;
+error:
+ if(ran_wl) {
+ CHECK(ranst_sun_wl_ref_put(ran_wl), RES_OK);
+ ran_wl = NULL;
+ }
+ if(ran_dir) {
+ CHECK(ranst_sun_dir_ref_put(ran_dir), RES_OK);
+ ran_dir = NULL;
+ }
+ goto exit;
+}
+
+
diff --git a/src/ssol_sun_c.h b/src/ssol_sun_c.h
@@ -19,6 +19,10 @@
#include <rsys/ref_count.h>
#include <rsys/list.h>
+/* Forward declaration */
+struct ranst_sun_dir;
+struct ranst_sun_wl;
+
enum sun_type {
SUN_DIRECTIONAL,
SUN_PILLBOX,
@@ -26,7 +30,7 @@ enum sun_type {
SUN_TYPES_COUNT__
};
-struct pillbox {
+struct pillbox {
double aperture;
};
@@ -49,4 +53,10 @@ struct ssol_sun {
ref_T ref;
};
+extern LOCAL_SYM res_T
+sun_create_distributions
+ (struct ssol_sun* sun,
+ struct ranst_sun_dir** out_ran_dir,
+ struct ranst_sun_wl** out_ran_wl);
+
#endif /* SSOL_SUN_C_H */