star-phor

Radiative transfer solver for photoreactors.
git clone https://www.edstar.cnrs.fr/git/star-phor.git
Log | Files | Refs | README | LICENSE

commit 4e881934e3b6593e2ff6219bc34133f78548dfbb
parent afaa3d054ff2f714dc6af62bb5f57041e3635e21
Author: Eduardo Fontana Lazzari <edufonlaz@gmail.com>
Date:   Tue,  6 Jan 2026 09:29:36 +0100

Introduce new abstractions for path tracing

Up until this point, path tracing abstractions were mostly inherited
from previous computation steps as well as from the supporting libraries
used during path tracing. As a result, these abstractions were often
only partially aligned with the physical concepts manipulated by the
algorithm.

In several cases, different elements were describing the same underlying
entity from distinct perspectives, such as s3d primitives and sphor
interfaces, which both refer to the same scene element but describe its
geometry in the former case and its physical properties in the latter.
This overlap caused lower-level abstractions to leak into the path
tracing algorithm. In other cases, the available abstractions were
simply not expressive enough to fully capture the physical concepts they
were intended to represent.

These restrictions motivate the introduction of new abstractions in
star-phor, especially in the realization function. These abstractions,
introduced by this commit, address these issues by either encapsulating
related lower-level structures or complementing existing ones with
missing semantic information. The newly introduced abstractions are ray,
intersection and primitive.

As a consequence of this refactor, the realization function now relies
on fewer local variables, and function interfaces are simplified,
requiring fewer arguments while conveying more explicit meaning.

This commit also introduces a set of getter functions to access the
physical properties associated with a given (sphor) primitive. These
functions provide ad hoc error handling adpted to the star-phor context
by abstracting direct calls to star-phor-input functions. In particular,
some conditions that are acceptable in star-phor-input are considered
errors in star-phor, and this distinction is now handled consistently at
this abstraction layer.

Diffstat:
Msrc/sphor_compute_mvrea.c | 456+++++++++++++++++++++++++++----------------------------------------------------
Msrc/sphor_config.c | 6+++---
Msrc/sphor_interface.c | 349+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sphor_interface.h | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sphor_ran_brdf.c | 16++++++----------
Msrc/sphor_ran_brdf.h | 2+-
Msrc/sphor_ran_source.c | 70+++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/sphor_ran_source.h | 5++---
8 files changed, 653 insertions(+), 343 deletions(-)

diff --git a/src/sphor_compute_mvrea.c b/src/sphor_compute_mvrea.c @@ -279,6 +279,7 @@ setup_MVREA_per_volume_component_accums accum_key.component_id = j_proprad; res = htable_accum2id_set(accum2id, &accum_key, &accum_id); if (RES_OK != res){ goto error; } + } } } @@ -360,8 +361,7 @@ error: static res_T volume_compute_total_ka (struct sphor* sphor, - struct interface interface, - size_t side_id, + const struct sphin_volume* volume, /* May be NULL => out_ka = 0 */ double wavelength, double* out_ka) { @@ -369,29 +369,27 @@ volume_compute_total_ka size_t j = 0; double ka = 0; struct sphin_prop_rad* prop_rad = NULL; - struct sphin_volume* volume = NULL; res_T res = RES_OK; ASSERT(NULL != sphor); ASSERT(NULL != out_ka); + (void)sphor; + /* No volume defined in the interface side */ - if (INVALID_ID == interface.volumes[side_id]) { + if (NULL == volume) { ka = 0.; goto exit; } - res = sphin_config_get_volume - (sphor->config, interface.volumes[side_id], &volume); - if (RES_OK != res){ goto error; } - /* Get ka corresponding to the current wavelength */ - res = sphin_volume_get_prop_rad_count(volume, &prop_rad_count); + res = sphin_volume_get_prop_rad_count + ((struct sphin_volume*)volume, &prop_rad_count); if (RES_OK != res){ goto error; } FOR_EACH(j, 0, prop_rad_count) { double ka_i = 0; - res = sphin_volume_get_prop_rad(volume, j, &prop_rad); + res = sphin_volume_get_prop_rad((struct sphin_volume*)volume, j, &prop_rad); if (RES_OK != res){ goto error; } res = prop_rad_compute_ka(prop_rad, wavelength, &ka_i); @@ -407,152 +405,53 @@ error: goto exit; } -static res_T -sample_abs_free_path_exp - (struct sphor* sphor, - struct ssp_rng* rng, - struct interface interface, - size_t side_id, - double ka, - double* s) -{ - - ASSERT(NULL != sphor); - ASSERT(NULL != s); - - (void)sphor; - - /* Get the absorption coefficient of the current volume to compute an - * absorption free length. If the intersected interface does not correspond - * to any volume in the config, the absorption coefficient is supposed to be - * zero and the absorption free length is set to FLT_MAX */ - if (INVALID_ID != interface.volumes[side_id]) { - /* Sample free path length according to volume absorption coefficent */ - *s = ssp_ran_exp(rng, ka);} - else { - *s = FLT_MAX; - } - - return RES_OK; -} - -static res_T -interface_side_get_refractive_index - (struct sphor* sphor, - struct interface* interface, - enum sphin_side side_id, - double wavelength, - double* n_real, - double* n_imag) +static struct sphin_volume* +volume_get_from_primitive + (struct sphor* sphor, + const struct primitive* primitive) { struct sphin_volume* volume = NULL; - struct sphin_refractive_index* refr_ind = NULL; - struct sphin_spectral_property* spec_prop = NULL; - struct sphin_spectral_property_descriptor spec_prop_desc - = SPHIN_SPECTRAL_PROPERTY_DESCRIPTOR_NULL; - res_T res = RES_OK; + size_t volume_id = INVALID_ID; ASSERT(NULL != sphor); - ASSERT(NULL != interface); - ASSERT(NULL != n_real); - ASSERT(NULL != n_imag); - - /* If the interface side doesnt has a volume defined, the refractive index - * receives the default value of 1 to the real part and 0 to the imaginary - * part */ - if (INVALID_ID == interface->volumes[side_id]) { - *n_real = 1; - *n_imag = 0; - goto exit; - } - - res = sphin_config_get_volume - (sphor->config, interface->volumes[side_id], &volume); - if (RES_OK != res){ goto error; } + ASSERT(NULL != primitive); - res = sphin_volume_get_refractive_index(volume, &refr_ind); - if (RES_OK != res){ goto error; } + volume_id = primitive->interface->volumes[primitive->side]; - /* If the volume is null, the refractive index - * receives the default value of 1 to the real part and 0 to the imaginary - * part */ - if (NULL == refr_ind) { - *n_real = 1; - *n_imag = 0; - goto exit; - } - - /* Real part of the refractive index */ - res = sphin_refractive_index_get_n_real(refr_ind, &spec_prop); - if (RES_OK != res){ goto error; } - - if (NULL != spec_prop) { - res = sphin_spectral_property_get_desc(spec_prop, &spec_prop_desc); - if (RES_OK != res){ goto error; } - - res = sphin_spectral_property_interpolate_at_wavelength - (spec_prop, wavelength, SPHIN_INTERPOLATION_LINEAR, n_real); - if (RES_OK != res){ goto error; } + if(INVALID_ID != volume_id) { + SPHIN(config_get_volume(sphor->config, volume_id, &volume)); } - else { *n_real = 1; } - /* Imag part of the refractive index */ - res = sphin_refractive_index_get_n_imag(refr_ind, &spec_prop); - if (RES_OK != res){ goto error; } - - if (NULL != spec_prop) { - res = sphin_spectral_property_get_desc(spec_prop, &spec_prop_desc); - if (RES_OK != res){ goto error; } - - res = sphin_spectral_property_interpolate_at_wavelength - (spec_prop, wavelength, SPHIN_INTERPOLATION_LINEAR, n_imag); - if (RES_OK != res){ goto error; } - } - else { *n_imag = 0; } -exit: - return res; -error: - goto exit; + return volume; } static res_T interface_sample_ray_interaction_type_brdf (struct sphor* sphor, struct ssp_rng* rng, - struct interface* interface, - enum sphin_side side_id, - struct s3d_hit* hit, - double dir[3], - double wavelength, + const struct ray* ray, + const struct intersection* intersection, enum ray_interface_interaction_type* interaction_type) { enum sphin_brdf_type brdf_type = SPHIN_BRDF_NONE__; - struct sphin_surface* surface = NULL; struct sphin_brdf* brdf = NULL; + struct primitive* primitive = NULL; double reflectivity = 0; double s = 0; /* random number */ - size_t i = 0; res_T res = RES_OK; ASSERT(NULL != sphor); ASSERT(NULL != rng); - ASSERT(NULL != hit); - ASSERT(NULL != interface); + ASSERT(NULL != ray); + ASSERT(NULL != intersection); ASSERT(NULL != interaction_type); - (void)hit; - (void)dir; - (void)wavelength; + (void)ray; - FOR_EACH(i, 0, (size_t)interface->surface_count[(size_t)side_id]) { - res = sphin_config_get_surface - (sphor->config, interface->surfaces[(size_t)side_id][i], &surface); - if (RES_OK != res){ goto error; } - res = sphin_surface_get_brdf(surface, &brdf); - if (RES_OK != res){ goto error; } - if (NULL != brdf){ break; } - } + primitive = (struct primitive*)&intersection->position.primitive; + res = primitive_get_brdf(sphor, primitive, &brdf); + if (RES_OK != res){ goto error; } if (NULL != brdf) { res = RES_BAD_ARG; goto error; } res = sphin_brdf_get_type(brdf, &brdf_type); @@ -591,48 +490,47 @@ static res_T sample_interface_ray_interaction_from_brdf (struct sphor* sphor, struct ssp_rng* rng, - struct interface* interface, - enum sphin_side side_id, - struct s3d_hit* hit, - double wavelength, + struct ray* ray, + struct intersection* intersection, double dir[3], enum ray_interface_interaction_type* interaction_type) { - struct sphin_surface* surface = NULL; struct sphin_brdf* brdf = NULL; - size_t i = 0; - double sample[3] = {0}; + struct primitive* primitive = NULL; res_T res = RES_OK; - FOR_EACH(i, 0, (size_t)interface->surface_count[(size_t)side_id]) { - res = sphin_config_get_surface - (sphor->config, interface->surfaces[(size_t)side_id][i], &surface); - if (RES_OK != res){ goto error; } - res = sphin_surface_get_brdf(surface, &brdf); - if (RES_OK != res){ goto error; } - if (NULL != brdf){ break; } - } + ASSERT(NULL != sphor); + ASSERT(NULL != rng); + ASSERT(NULL != ray); + ASSERT(NULL != intersection); + + primitive = &intersection->position.primitive; + + res = primitive_get_brdf(sphor, primitive, &brdf); + if (RES_OK != res){ goto error; } + if (NULL != brdf) { res = RES_BAD_ARG; goto error; } res = interface_sample_ray_interaction_type_brdf - (sphor, rng, interface, side_id, hit, dir, wavelength, interaction_type); + (sphor, rng, ray, intersection, interaction_type); if (RES_OK != res){ goto error; } - switch (*interaction_type) { - case RAY_INTERFACE_INTERACTION_ABSORPTION: - break; - case RAY_INTERFACE_INTERACTION_REFLECTION: - res = ran_brdf_reflection_direction(brdf, rng, &hit->prim, dir,sample); - d3_set(dir, sample); - break; - case RAY_INTERFACE_INTERACTION_TRANSMISSION: - break; - case RAY_INTERFACE_INTERACTION_NONE__: - res = RES_BAD_ARG; - goto error; - default: - FATAL("Unreachable code\n"); - break; - } + switch (*interaction_type) { + case RAY_INTERFACE_INTERACTION_ABSORPTION: + break; + case RAY_INTERFACE_INTERACTION_REFLECTION: + res = ran_brdf_reflection_direction + (brdf, rng, primitive, ray->direction, dir); + break; + case RAY_INTERFACE_INTERACTION_TRANSMISSION: + d3_set(dir, ray->direction); + break; + case RAY_INTERFACE_INTERACTION_NONE__: + res = RES_BAD_ARG; + goto error; + default: + FATAL("Unreachable code\n"); + break; + } exit: return res; @@ -642,14 +540,12 @@ error: static res_T sample_interface_ray_interaction_from_refractive_index -(struct sphor* sphor, - struct ssp_rng* rng, - struct interface* interface, - enum sphin_side side_id, - struct s3d_hit* hit, - double wavelength, - double dir[3], - enum ray_interface_interaction_type* interaction_type) + (struct sphor* sphor, + struct ssp_rng* rng, + struct ray* ray, + struct intersection* intersection, + double dir[3], + enum ray_interface_interaction_type* interaction_type) { double invert_normal = 1.; double normal[3] = {0}; @@ -658,37 +554,30 @@ sample_interface_ray_interaction_from_refractive_index double pdf = 0; double sample[3] = {0}; int flag = 0; - size_t side_id_back = 0; - struct s3d_attrib attrib; + struct primitive* primitive = &intersection->position.primitive; struct ssf_bsdf* bsdf = NULL; res_T res = RES_OK; ASSERT(NULL != sphor); ASSERT(NULL != rng); - ASSERT(NULL != hit); - ASSERT(NULL != interface); + ASSERT(NULL != ray); + ASSERT(NULL != intersection); ASSERT(NULL != dir); ASSERT(NULL != interaction_type); /* Get refractive index of the medium in the incident side */ - res = interface_side_get_refractive_index - (sphor, interface, side_id, wavelength, &n_real_i, &n_imag_i); + res = primitive_get_in_refraction_index + (sphor, primitive, ray->wavelength, &n_real_i, &n_imag_i); if (RES_OK != res){ goto error; } /* Get refractive index of the medium in the transmitted side */ - side_id_back = SPHIN_SIDE_FRONT^side_id; - res = interface_side_get_refractive_index - (sphor, interface, side_id_back, wavelength, &n_real_t, &n_imag_t); + res = primitive_get_out_refraction_index + (sphor, primitive, ray->wavelength, &n_real_t, &n_imag_t); if (RES_OK != res){ goto error; } - /* Get surface normal in the intersection point and ensure its compatible with - * the ssf API */ - res = s3d_primitive_get_attrib - (&hit->prim, S3D_GEOMETRY_NORMAL, hit->uv, &attrib); - if (RES_OK != res){ goto error; } - d3_set_f3(normal, attrib.value); + d3_set(normal, primitive->normal); /* Ensure normal and incoming direction point to the same hemisphere */ - invert_normal = (d3_dot(normal, dir) > 0 ) ?1. : -1.; + invert_normal = (d3_dot(normal, ray->direction) > 0 ) ?1. : -1.; d3_normalize(normal, d3_muld(normal, normal, invert_normal)); /* Sample an interaction type and a new direction using ssf */ @@ -699,7 +588,7 @@ sample_interface_ray_interaction_from_refractive_index res = ssf_specular_dielectric_dielectric_interface_setup (bsdf, n_real_i, n_real_t); if (RES_OK != res){ goto error; } - ssf_bsdf_sample(bsdf, rng, dir, normal, sample, &flag, &pdf); + ssf_bsdf_sample(bsdf, rng, ray->direction, normal, sample, &flag, &pdf); if (flag && SSF_TRANSMISSION) { *interaction_type = RAY_INTERFACE_INTERACTION_TRANSMISSION; } @@ -707,6 +596,8 @@ sample_interface_ray_interaction_from_refractive_index *interaction_type = RAY_INTERFACE_INTERACTION_REFLECTION; } else { res = RES_BAD_ARG; } + d3_set(dir, sample); + exit: if (NULL != bsdf) { SSF(bsdf_ref_put(bsdf)); } return res; @@ -718,40 +609,33 @@ static res_T sample_interface_ray_interaction (struct sphor* sphor, struct ssp_rng* rng, - struct interface* interface, - enum sphin_side side_id, - struct s3d_hit* hit, - double wavelength, + struct ray* ray, + struct intersection* intersection, double dir[3], enum ray_interface_interaction_type* interaction_type) { - struct sphin_surface* surface = NULL; struct sphin_brdf* brdf = NULL; - size_t i = 0; + struct primitive* primitive = NULL; res_T res = RES_OK; ASSERT(NULL != sphor); ASSERT(NULL != rng); - ASSERT(NULL != hit); - ASSERT(NULL != interface); + ASSERT(NULL != ray); + ASSERT(NULL != intersection); + + primitive = &intersection->position.primitive; /* Verify if one of the surfaces defined in the intersected interface * side has a BRDF defined */ - FOR_EACH(i, 0, (size_t)interface->surface_count[(size_t)side_id]) { - res = sphin_config_get_surface - (sphor->config, interface->surfaces[(size_t)side_id][i], &surface); - if (RES_OK != res){ goto error; } - res = sphin_surface_get_brdf(surface, &brdf); - if (RES_OK != res){ goto error; } - if (NULL != brdf){ break; } - } + res = primitive_get_brdf(sphor, primitive, &brdf); + if (RES_OK != res){ goto error; } if(NULL != brdf) { /* If the interface side is a sphin_surface with a BRDF declared, * verify if a reflection takes place based on the reflectivity of the * surface */ res = sample_interface_ray_interaction_from_brdf - (sphor, rng, interface, side_id, hit, wavelength, dir, interaction_type); + (sphor, rng, ray, intersection, dir, interaction_type); if (RES_OK != res){ goto error; } } @@ -760,7 +644,7 @@ sample_interface_ray_interaction * index of each side of the interface to construct the BRDF */ else{ res = sample_interface_ray_interaction_from_refractive_index - (sphor, rng, interface, side_id, hit, wavelength, dir, interaction_type); + (sphor, rng, ray, intersection, dir, interaction_type); if (RES_OK != res){ goto error; } } exit: @@ -770,52 +654,6 @@ error: } static res_T -trace_ray - (struct sphor* sphor, - double pos[3], - double dir[3], - struct interface* interface, - struct s3d_primitive* prim_org, - enum sphin_side* side_id, - struct s3d_hit *out_hit) -{ - /* Position and direction using when calling s3d functions */ - float r[3] = {0}; /* Position */ - float w[3] = {0}; /* Direction */ - /* Allowed range for rays during path sampling */ - float range[2] = {0.f, FLT_MAX}; - struct s3d_hit hit = S3D_HIT_NULL; - size_t triangle_id; - res_T res = RES_OK; - - f3_set_d3(r, pos); - f3_set_d3(w, dir); - res = s3d_scene_view_trace_ray - (sphor->scene_view, r, w, range, prim_org, &hit); - if (RES_OK != res){ goto error; } - - if (S3D_HIT_NONE(&hit)) { - goto exit; - } - /* Retrieve the intercepted triangle and the corresponding interface in the - * sphor structure */ - triangle_id = hit.prim.prim_id; - *interface = darray_interface_data_get(&sphor->interfaces)[triangle_id]; - /* Update current primitive */ - *prim_org = hit.prim; - /* Retrieve the side of the intercepted interface corresponding to the - * income direction of the hit */ - res = interface_get_side_id(dir, &hit, side_id); - if (RES_OK != res){ goto error; } - -exit: - *out_hit = hit; - return res; -error: - goto exit; -} - -static res_T MVREA_write_outputs (struct sphor* sphor, const struct darray_accum* accums, @@ -848,20 +686,16 @@ compute_MVREA_realization struct darray_accum* accums) { double dir[3] = {0}; /* Current direction in path sampling */ - double pos[3] = {0}; /* Current position in path sampling */ double response_function = 0; - double wavelength = 0; double ka = 0; double s = 0; /* Random number */ enum ray_interface_interaction_type ray_interface_interaction_type = RAY_INTERFACE_INTERACTION_NONE__; - enum sphin_side side_id = SPHIN_SIDE_NONE__; - struct interface interface = INTERFACE_NULL; - struct s3d_hit hit = S3D_HIT_NULL; - struct s3d_primitive prim = S3D_PRIMITIVE_NULL; - struct s3d_attrib attrib; + struct intersection intersection = INTERSECTION_NULL; + struct ray ray = RAY_DEFAULT; struct source_view* source_view = NULL; struct sphin_sensor_volume* sensor_volume = NULL; + struct sphin_sensor_surface* sensor_surface = NULL; struct sphin_volume* volume = NULL; res_T res = RES_OK; @@ -871,54 +705,60 @@ compute_MVREA_realization ASSERT(NULL != accums); /* Sample a random position from a source in the whole scene */ - res = sample_source_position(sphor, rng, &source_view, &prim, pos); + res = sample_source_position + (sphor, rng, &source_view, &ray.origin_on_prim, ray.origin); if (RES_OK != res){ goto error; } /* Sample a direction according to the source direction distribution */ - res = source_surface_sample_direction(sphor, rng, source_view, &prim, dir); + res = source_surface_sample_direction + (sphor, rng, &ray.origin_on_prim, ray.direction); if (RES_OK != res){ goto error; } /* Sample a wavelength according to the source emission spectrum */ - res = source_sample_wavelength(sphor, rng, source_view, &wavelength); + res = source_sample_wavelength + (sphor, rng, source_view, &ray.wavelength); + if (RES_OK != res){ goto error; } + + volume = volume_get_from_primitive(sphor, &ray.origin_on_prim.primitive); + + /* Retrieve the ka of the present volume */ + res = volume_compute_total_ka(sphor, volume, ray.wavelength, &ka); if (RES_OK != res){ goto error; } /* While no absorption takes place and an interface is found */ for(;;) { + /* Sample the free path length to absorption from an exponential + * distribution with rate parameter ka */ + s = ssp_ran_exp(rng, ka); + /* Trace a ray in the scene from the sampled primitive in the sampled * direction and retrive the hit distance */ - res = trace_ray(sphor, pos, dir, &interface, &prim, &side_id, &hit); + res = trace_ray(sphor, &ray, &intersection); if (RES_OK != res){ goto error; } /* If there is no intersection in the sampled direction from the sampled * position, the photon is lost and it counts zero in the weight. */ - if (S3D_HIT_NONE(&hit)) { break; } - - /* Retrieve the ka of the present volume */ - res = volume_compute_total_ka(sphor, interface, side_id, wavelength, &ka); - if (RES_OK != res){ goto error; } - - /* Sample the free path length to absorption from an exponential - * distribution with rate parameter ka */ - res = sample_abs_free_path_exp - (sphor, rng, interface, side_id, ka, &s); - if (RES_OK != res){ goto error; } + if (INTERSECTION_NONE(&intersection)) { break; } /* If the distance between the ray origin and the intersection is bigger * than the absorption free path length, an absorption takes place (check * if the volume is a sensor and return the weight accordingly) */ - if (s < hit.distance) { - res = sphin_config_get_volume - (sphor->config, interface.volumes[side_id], &volume); - if (RES_OK != res){ goto error; } + if (s < intersection.distance) { /* Check if the volume is a sensor */ res = sphin_volume_get_sensor(volume, &sensor_volume); if (RES_OK != res){ goto error; } - /* If the volume is a sensor, compute the weight using the response - * function */ - if (NULL != sensor_volume) { + if (NULL == sensor_volume) { + /* If the volume is not a sensor, the absorbed photon does not count in + * the weight. The weight is implictly 0 */ + } else { + /* If the volume is a sensor, compute the weight using the response + * function */ + + /* TODO res = update_mc_weights(...); */ struct accum_key accum_key = ACCUM_KEY_NULL; size_t prop_rad_count = 0; + size_t volume_id = SIZE_MAX; size_t i_proprad = 0; double vol = 0; double tot_pow = 0; @@ -936,6 +776,9 @@ compute_MVREA_realization mc_weight = tot_pow / vol; + volume_id = intersection.position.primitive.interface->volumes + [intersection.position.primitive.side]; + /* Update total accumulator for all volumes in the scene*/ accum_key.sensor_type = SPHOR_SENSOR_VOLUME; accum_key.sensor_id = ALL_SENSORS; @@ -944,7 +787,7 @@ compute_MVREA_realization /* Update total accumulator in the volume */ accum_key.sensor_type = SPHOR_SENSOR_VOLUME; - accum_key.sensor_id = interface.volumes[side_id]; + accum_key.sensor_id = volume_id; accum_key.component_id = ALL_COMPONENTS; update_accum(accum2id, &accum_key, mc_weight, accums); @@ -955,7 +798,7 @@ compute_MVREA_realization struct sphin_prop_rad* prop_rad = NULL; res = sphin_volume_get_prop_rad(volume, i_proprad, &prop_rad); if (RES_OK != res){ goto error; } - res = prop_rad_compute_ka(prop_rad, wavelength, &ka_i); + res = prop_rad_compute_ka(prop_rad, ray.wavelength, &ka_i); if (RES_OK != res){ goto error; } accum_key.component_id = i_proprad; @@ -963,45 +806,57 @@ compute_MVREA_realization update_accum(accum2id, &accum_key, mc_weight, accums); } } - /* If the volume is not a sensor, the absorbed photon does not count in - * the weight. The weight is implictly 0 */ + + /* Absorption => stop the path */ break; } /* Else, the distance between the ray origin and the intersection is smaller * than the absorption free path length. The photon intersects a primitive * in the scene view. Sample the type of interaction that the photon has - * with the interface */ + * with the interface as well as the new direction sampled */ res = sample_interface_ray_interaction - (sphor, - rng, - &interface, - side_id, - &hit, - wavelength, - dir, - &ray_interface_interaction_type); + (sphor, rng, &ray, &intersection, dir, &ray_interface_interaction_type); if (RES_OK != res){ goto error; } int stop = 0; switch (ray_interface_interaction_type) { /* An absorption took place, the weight is zero and the path stops */ case RAY_INTERFACE_INTERACTION_ABSORPTION: - /* TODO: verify if surface is sensor and update its accum */ + /* TODO: verify if surface is sensor and update its accum + res = primitive_get_sensor_surface(sphor, &intersection.position.primitive, &sensor_surface); + if (RES_OK != res){ goto error; } */ + /* If the volume is a sensor, compute the weight using the response + * function */ + if (NULL != sensor_surface) + { + /* res = update_mc_weights(...= */ + } stop = 1; break; + /* Else, if a reflection takes place, sample a new direction according to * the photon arriving direction, geometry normak and surface brdf * properties */ case RAY_INTERFACE_INTERACTION_REFLECTION: break; + /* If the ray traverses the interface, it simply continue in the same * direction as before */ case RAY_INTERFACE_INTERACTION_TRANSMISSION: - /* The position coordinates are the same, but on the other side of the - * interface */ - side_id = SPHIN_SIDE_FRONT ^ side_id; /* Cross the interface */ + /* Continue the path on the other side of the primitive.*/ + intersection.position.primitive.side = + !intersection.position.primitive.side; + + /* Update the current medium */ + volume = volume_get_from_primitive + (sphor, &intersection.position.primitive); + + /* Retrieve the ka of the present volume */ + res = volume_compute_total_ka(sphor, volume, ray.wavelength, &ka); + if (RES_OK != res){ goto error; } break; + /* Handle the possible exceptions */ case RAY_INTERFACE_INTERACTION_NONE__: res = RES_BAD_ARG; @@ -1012,10 +867,9 @@ compute_MVREA_realization } if (stop) { break; } - /* The hit position is the new position in path tracing */ - res = s3d_primitive_get_attrib (&hit.prim, S3D_POSITION, hit.uv, &attrib); + /* Update ray with the intersection information */ + res = ray_update(&ray, &intersection, dir); if (RES_OK != res){ goto error; } - d3_set_f3(pos, attrib.value); } exit: @@ -1125,10 +979,10 @@ sphor_compute_MVREA exit: /* Free the proxy generator and each one of the thread generators */ - if (rng_proxy) ssp_rng_proxy_ref_put(rng_proxy); - if (rngs) { + if (NULL != rng_proxy) ssp_rng_proxy_ref_put(rng_proxy); + if (NULL != rngs) { FOR_EACH(i, 0, nthreads) { - if(rngs[i]) ssp_rng_ref_put(rngs[i]); + if(NULL != rngs[i]) ssp_rng_ref_put(rngs[i]); } mem_rm(rngs); } diff --git a/src/sphor_config.c b/src/sphor_config.c @@ -521,7 +521,7 @@ hit_filter_function const float pos[3], const float dir[3], const float range[2], - void* primitive, + void* primitive_id, void* interface) { (void)pos; @@ -529,9 +529,9 @@ hit_filter_function (void)range; (void)interface; - struct s3d_primitive* prim = primitive; + size_t* prim_id = primitive_id; - if(hit->prim.prim_id == prim->prim_id) { return 1; } + if(hit->prim.prim_id == *prim_id) { return 1; } return 0; } diff --git a/src/sphor_interface.c b/src/sphor_interface.c @@ -23,8 +23,13 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "sphor_interface.h" +#include "sphor_c.h" + #include <rsys/double3.h> +#include <rsys/double2.h> +#include <rsys/float3.h> +#include <rsys/float2.h> #include <star/s3d.h> #include <star/sphin.h> @@ -36,6 +41,350 @@ /******************************************************************************* * Local functions ******************************************************************************/ + +/******************************************************************************* + * Interface surface getters + ******************************************************************************/ +res_T +primitive_get_brdf + (struct sphor* sphor, + const struct primitive* primitive, + struct sphin_brdf** out_brdf) +{ + char* surface_name = NULL; + size_t i = 0; + const struct interface* interface = NULL; + struct sphin_brdf* brdf = NULL; + struct sphin_brdf* found_brdf = NULL; + struct sphin_surface* surface = NULL; + res_T res = RES_OK; + + ASSERT(NULL != sphor); + ASSERT(NULL != primitive); + ASSERT(NULL != out_brdf); + + interface = primitive->interface; + + FOR_EACH(i, 0, (size_t)interface->surface_count[(size_t)primitive->side]) { + res = sphin_config_get_surface + (sphor->config, interface->surfaces[(size_t)primitive->side][i], &surface); + if (RES_OK != res){ goto error; } + res = sphin_surface_get_brdf(surface, &brdf); + if (RES_OK != res){ goto error; } + if (NULL != brdf){ + if (NULL == found_brdf) { + sphin_surface_get_name(surface, &surface_name); + found_brdf = brdf; /* Keep first non-NULL */ + } else { + /* Found second non-NULL BRDF - this is an error/conflict */ + char* last_surface_name = NULL; + sphin_surface_get_name(surface, &last_surface_name); + ERROR(sphor, + "Multiple BRDFs defined for same interface side. " + "Conflicting surfaces: '%s' and '%s'.\n", + surface_name, last_surface_name); + res = RES_BAD_ARG; + goto error; + } + } + } + +exit: + *out_brdf = brdf; + return res; +error: + found_brdf = NULL; + goto exit; +} + +res_T +primitive_get_source + (struct sphor* sphor, + const struct primitive* primitive, + struct sphin_source_surface** out_source) +{ + char* surface_name = NULL; + size_t i = 0; + const struct interface* interface = NULL; + struct sphin_source_surface* source = NULL; + struct sphin_source_surface* found_source = NULL; + struct sphin_surface* surface = NULL; + res_T res = RES_OK; + + ASSERT(NULL != sphor); + ASSERT(NULL != primitive); + ASSERT(NULL != out_source); + + interface = primitive->interface; + + FOR_EACH(i, 0, (size_t)interface->surface_count[(size_t)primitive->side]) { + res = sphin_config_get_surface + (sphor->config, interface->surfaces[(size_t)primitive->side][i], &surface); + if (RES_OK != res){ goto error; } + res = sphin_surface_get_source(surface, &source); + if (RES_OK != res){ goto error; } + if (NULL != source){ + if (NULL == found_source) { + sphin_surface_get_name(surface, &surface_name); + found_source = source; /* Keep first non-NULL */ + } else { + /* Found second non-NULL sensor - this is an error/conflict */ + char* last_surface_name = NULL; + sphin_surface_get_name(surface, &last_surface_name); + ERROR(sphor, + "Multiple surface sources defined for same interface side. " + "Conflicting surfaces: '%s' and '%s'.\n", + surface_name, last_surface_name); + res = RES_BAD_ARG; + goto error; + } + } + } + +exit: + *out_source = source; + return res; +error: + found_source = NULL; + goto exit; +} + +res_T +primitive_get_sensor_surface + (struct sphor* sphor, + const struct primitive* primitive, + struct sphin_sensor_surface** out_sensor) +{ + char* surface_name = NULL; + size_t i = 0; + const struct interface* interface = NULL; + struct sphin_sensor_surface* sensor = NULL; + struct sphin_sensor_surface* found_sensor = NULL; + struct sphin_surface* surface = NULL; + res_T res = RES_OK; + + ASSERT(NULL != sphor); + ASSERT(NULL != primitive); + ASSERT(NULL != out_sensor); + + interface = primitive->interface; + + FOR_EACH(i, 0, (size_t)interface->surface_count[(size_t)primitive->side]) { + res = sphin_config_get_surface + (sphor->config, interface->surfaces[(size_t)primitive->side][i], &surface); + if (RES_OK != res){ goto error; } + res = sphin_surface_get_sensor(surface, &sensor); + if (RES_OK != res){ goto error; } + if (NULL != sensor){ + if (NULL == found_sensor) { + sphin_surface_get_name(surface, &surface_name); + found_sensor = sensor; /* Keep first non-NULL */ + } else { + /* Found second non-NULL sensor - this is an error/conflict */ + char* last_surface_name = NULL; + sphin_surface_get_name(surface, &last_surface_name); + ERROR(sphor, + "Multiple sensors defined for same interface side. " + "Conflicting surfaces: '%s' and '%s'.\n", + surface_name, last_surface_name); + res = RES_BAD_ARG; + goto error; + } + } + } + +exit: + *out_sensor = sensor; + return res; +error: + found_sensor = NULL; + goto exit; +} + +/******************************************************************************* + * Interface volume getters + ******************************************************************************/ +res_T +primitive_get_in_refraction_index + (struct sphor* sphor, + struct primitive* primitive, + double wavelength, + double* n_real, + double* n_imag) +{ + struct sphin_volume* volume = NULL; + struct sphin_refractive_index* refr_ind = NULL; + struct sphin_spectral_property* spec_prop = NULL; + struct sphin_spectral_property_descriptor spec_prop_desc + = SPHIN_SPECTRAL_PROPERTY_DESCRIPTOR_NULL; + res_T res = RES_OK; + + ASSERT(NULL != sphor); + ASSERT(NULL != primitive); + ASSERT(NULL != n_real); + ASSERT(NULL != n_imag); + + /* If the interface side doesnt has a volume defined, the refractive index + * receives the default value of 1 to the real part and 0 to the imaginary + * part */ + if (INVALID_ID == primitive->interface->volumes[primitive->side]) { + *n_real = 1; + *n_imag = 0; + goto exit; + } + + res = sphin_config_get_volume + (sphor->config, primitive->interface->volumes[primitive->side], &volume); + if (RES_OK != res){ goto error; } + + res = sphin_volume_get_refractive_index(volume, &refr_ind); + if (RES_OK != res){ goto error; } + + /* If the volume is null, the refractive index + * receives the default value of 1 to the real part and 0 to the imaginary + * part */ + if (NULL == refr_ind) { + *n_real = 1; + *n_imag = 0; + goto exit; + } + + /* Real part of the refractive index */ + res = sphin_refractive_index_get_n_real(refr_ind, &spec_prop); + if (RES_OK != res){ goto error; } + + if (NULL != spec_prop) { + res = sphin_spectral_property_get_desc(spec_prop, &spec_prop_desc); + if (RES_OK != res){ goto error; } + + res = sphin_spectral_property_interpolate_at_wavelength + (spec_prop, wavelength, SPHIN_INTERPOLATION_LINEAR, n_real); + if (RES_OK != res){ goto error; } + } + else { *n_real = 1; } + + /* Imag part of the refractive index */ + res = sphin_refractive_index_get_n_imag(refr_ind, &spec_prop); + if (RES_OK != res){ goto error; } + + if (NULL != spec_prop) { + res = sphin_spectral_property_get_desc(spec_prop, &spec_prop_desc); + if (RES_OK != res){ goto error; } + + res = sphin_spectral_property_interpolate_at_wavelength + (spec_prop, wavelength, SPHIN_INTERPOLATION_LINEAR, n_imag); + if (RES_OK != res){ goto error; } + } + else { *n_imag = 0; } +exit: + return res; +error: + goto exit; +} + +res_T +primitive_get_out_refraction_index + (struct sphor* sphor, + struct primitive* primitive, + double wavelength, + double* n_real, + double* n_imag) +{ + /* out_primitive is the primitive corresponding to the other side of the + * primitive in the functions' args */ + struct primitive out_primitive = PRIMITIVE_NULL; + res_T res = RES_OK; + + ASSERT(NULL != sphor); + ASSERT(NULL != primitive); + ASSERT(NULL != n_real); + ASSERT(NULL != n_imag); + + /* The primitive corresponding to the out side contains the exact same + * interface, but other side_id */ + out_primitive.interface = primitive->interface; + out_primitive.side = primitive->side^SPHIN_SIDE_FRONT; + + res = primitive_get_in_refraction_index + (sphor, &out_primitive, wavelength, n_real, n_imag); + return res; +} + +res_T +trace_ray + (const struct sphor* sphor, + const struct ray* ray, + struct intersection* intersection) +{ + /* Position, direction and range when calling s3d functions */ + float pos[3] = {0}; /* Position */ + float dir[3] = {0}; /* Direction */ + float rge[2] = {0}; /* Range */ + + double normal[3] = {0}; + enum sphin_side side_id = SPHIN_SIDE_NONE__; + size_t prim_id = INVALID_ID; + size_t triangle_id; + const struct interface* interface = NULL; + struct s3d_hit hit = S3D_HIT_NULL; + res_T res = RES_OK; + + f3_set_d3(pos, ray->origin); + f3_set_d3(dir, ray->direction); + f2_set_d2(rge, ray->range); + + prim_id = ray->origin_on_prim.primitive.prim_id; + + /* Trace ray using s3d */ + res = s3d_scene_view_trace_ray + (sphor->scene_view, pos, dir, rge, &prim_id, &hit); + if (RES_OK != res){ goto error; } + + /* Construct sphor structure using sphor */ + if (S3D_HIT_NONE(&hit)) { + *intersection = INTERSECTION_NULL; + goto exit; + } + + triangle_id = hit.prim.prim_id; + interface = &darray_interface_cdata_get(&sphor->interfaces)[triangle_id]; + d3_normalize(normal, d3_set_f3(normal, hit.normal)); + + side_id = + (d3_dot(ray->direction, normal) < 0) ? SPHIN_SIDE_BACK : SPHIN_SIDE_FRONT; + + intersection->distance = hit.distance; + d2_set_f2(intersection->position.uv, hit.uv); + d3_set(intersection->position.primitive.normal, normal); + intersection->position.primitive.interface = interface; + intersection->position.primitive.prim_id = triangle_id; + intersection->position.primitive.side = side_id; + +exit: + return res; +error: + goto exit; +} + +res_T +ray_update + (struct ray* ray, + const struct intersection* intersection, + const double dir[3]) +{ + double displacement[3] = {0}; + res_T res = RES_OK; + + ASSERT(NULL != intersection); + ASSERT(NULL != ray); + + ray->origin_on_prim = intersection->position; + d3_muld(displacement, ray->direction, intersection->distance); + d3_add(ray->origin, ray->origin, displacement); + d3_set(ray->direction, dir); + return res; +} + res_T interface_get_side_id (const double dir[3], /* Direction of arrival of the hit into the geometry */ diff --git a/src/sphor_interface.h b/src/sphor_interface.h @@ -57,6 +57,98 @@ static const struct interface INTERFACE_NULL = INTERFACE_NULL__; #define DARRAY_DATA struct interface #include <rsys/dynamic_array.h> +struct primitive { + const struct interface* interface; + double normal[3]; /* Assumes normal is the same in the entire primitive */ + size_t prim_id; + enum sphin_side side; +}; +#define PRIMITIVE_NULL__ {NULL, {0,0,0}, INVALID_ID, SPHIN_SIDE_NONE__} +static const struct primitive PRIMITIVE_NULL = PRIMITIVE_NULL__; + +struct primitive_pos { + struct primitive primitive; + double uv[2]; /* Local coordinate */ +}; +#define PRIMITIVE_POS_NULL__ {PRIMITIVE_NULL__, {0,0}} +static const struct primitive_pos PRIMITIVE_POS_NULL = PRIMITIVE_POS_NULL__; + +struct intersection { + struct primitive_pos position; + double distance; +}; +#define INTERSECTION_NULL__ {PRIMITIVE_POS_NULL__, DBL_MAX} +static const struct intersection INTERSECTION_NULL = INTERSECTION_NULL__; + +/* Syntactic sugar */ +#define INTERSECTION_NONE(Intersection) ((Intersection)->distance >= DBL_MAX) + +struct ray { + double origin[3]; + double direction[3]; + double wavelength; + double range[2]; + + /* Origin of the ray when located on a primitive. + * Used to avoid self intersection */ + struct primitive_pos origin_on_prim; +}; +#define RAY_DEFAULT__ { \ + {0,0,0}, \ + {0,0,0}, \ + DBL_MAX, \ + {0, DBL_MAX}, \ + PRIMITIVE_POS_NULL__ \ +} +static const struct ray RAY_DEFAULT = RAY_DEFAULT__; + +extern LOCAL_SYM res_T +primitive_get_source + (struct sphor* sphor, + const struct primitive* primitive, + struct sphin_source_surface** out_source); + +extern LOCAL_SYM res_T +primitive_get_sensor_surface + (struct sphor* sphor, + const struct primitive* primitive, + struct sphin_sensor_surface** out_sensor); + +extern LOCAL_SYM res_T +primitive_get_brdf + (struct sphor* sphor, + const struct primitive* primitive, + struct sphin_brdf** out_brdf); + +extern LOCAL_SYM res_T +primitive_get_in_refraction_index + (struct sphor* sphor, + struct primitive* primitive, + double wavelength, + double* n_real, + double* n_imag); + +extern LOCAL_SYM res_T +primitive_get_out_refraction_index + (struct sphor* sphor, + struct primitive* primitive, + double wavelength, + double* n_real, + double* n_imag); + +extern LOCAL_SYM res_T +trace_ray + (const struct sphor* sphor, + const struct ray* ray, + struct intersection* intersection); + +extern LOCAL_SYM res_T +ray_update + (struct ray* ray, + const struct intersection* intersection, + const double dir[3]); + +/* TODO this should disappear */ extern LOCAL_SYM res_T interface_get_side_id (const double dir[3], diff --git a/src/sphor_ran_brdf.c b/src/sphor_ran_brdf.c @@ -22,8 +22,9 @@ * 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 "sphor_sources.h" +#include "sphor_interface.h" #include "sphor_ran_brdf.h" +#include "sphor_sources.h" #include <star/sphin.h> #include <star/ssp.h> @@ -41,30 +42,25 @@ res_T ran_brdf_reflection_direction (struct sphin_brdf* brdf, struct ssp_rng* rng, - struct s3d_primitive* prim, + struct primitive* primitive, double initial_dir[3], double final_dir[3]) { - struct s3d_attrib attrib; enum sphin_brdf_type brdf_type = SPHIN_BRDF_NONE__; - float st[2] = {0}; double normal[3] = {0}; double new_dir[3] = {0}; double dot = 0; - double invert_normal = 1.f; + double invert_normal = 1.; res_T res = RES_OK; - res = s3d_primitive_get_attrib - (prim, S3D_GEOMETRY_NORMAL, st, &attrib); - if (RES_OK != res){ goto error; } + d3_set(normal, primitive->normal); - d3_set_f3(normal, attrib.value); /* Ensure the geometry normal points into the same hemisphere as the incoming * direction. If the dot product between the normal and the incoming (initial) * direction is positive, then the normal points in the same direction as the * incoming ray, which is incorrect for reflection. Invert the normal in that * case. */ - invert_normal = (d3_dot(normal, initial_dir) > 0 ) ?-1.f : 1.f; + invert_normal = (d3_dot(normal, initial_dir) > 0 ) ?-1. : 1.; res = sphin_brdf_get_type(brdf, &brdf_type); if (RES_OK != res){ goto error; } diff --git a/src/sphor_ran_brdf.h b/src/sphor_ran_brdf.h @@ -35,7 +35,7 @@ extern LOCAL_SYM res_T ran_brdf_reflection_direction (struct sphin_brdf* brdf, struct ssp_rng* rng, - struct s3d_primitive* prim, + struct primitive* primitive, double initial_dir[3], double final_dir[3]); diff --git a/src/sphor_ran_source.c b/src/sphor_ran_source.c @@ -43,25 +43,17 @@ res_T source_surface_sample_direction (struct sphor* sphor, struct ssp_rng* rng, - struct source_view* source_view, - struct s3d_primitive* prim, + struct primitive_pos* prim_pos, double dir[3]) { - struct s3d_attrib attrib; struct sphin_source_surface* source = NULL; - struct sphin_surface* surface = NULL; struct sphin_source_surface_direction_distribution src_dir_dist = SPHIN_SOURCE_SURFACE_DIRECTION_DISTRIBUTION_NULL; double normal[3] = {0}; double sample[3] = {0}; - float st[2] = {0}; res_T res = RES_OK; - res = sphin_config_get_surface - (sphor->config, source_view->sphin_id, &surface); - if (RES_OK != res){ goto error; } - - res = sphin_surface_get_source(surface, &source); + res = primitive_get_source(sphor, &prim_pos->primitive, &source); if (RES_OK != res){ goto error; } if (NULL == source){ res = RES_BAD_ARG; goto error; } @@ -69,12 +61,7 @@ source_surface_sample_direction res = sphin_source_surface_get_direction_distribution(source, &src_dir_dist); if (RES_OK != res){ goto error; } - res = s3d_primitive_get_attrib - (prim, S3D_GEOMETRY_NORMAL, st, &attrib); - if (RES_OK != res){ goto error; } - - d3_minus(normal, d3_set_f3(normal, attrib.value)); - d3_normalize(normal, normal); + d3_set(normal, prim_pos->primitive.normal); switch (src_dir_dist.type) { case SPHIN_SOURCE_DIRECTION_COLLIM: @@ -122,15 +109,18 @@ res_T sample_source_position (struct sphor* sphor, struct ssp_rng* rng, - struct source_view** source, - struct s3d_primitive* scn_primitive, + struct source_view** source_view, + struct primitive_pos* position, double pos[3]) { float st[2] = {0}; size_t scn_prim_id; struct source_view* source_to_sample = NULL; struct s3d_primitive src_prim = S3D_PRIMITIVE_NULL; - struct s3d_attrib attrib; + struct s3d_primitive scn_prim = S3D_PRIMITIVE_NULL; + struct s3d_attrib src_normal; + struct s3d_attrib scn_normal; + struct s3d_attrib scn_pos; res_T res = RES_OK; ASSERT(NULL != sphor); @@ -143,17 +133,47 @@ sample_source_position (sphor, rng, source_to_sample->view, &src_prim, st); if (RES_OK != res) { goto error; } - res = s3d_primitive_get_attrib(&src_prim, S3D_POSITION, st, &attrib); - if (RES_OK != res){ goto error; } - d3_set_f3(pos, attrib.value); - scn_prim_id = darray_size_t_data_get (&source_to_sample->src2scn)[src_prim.prim_id]; + res = s3d_scene_view_get_primitive - (sphor->scene_view, (unsigned)scn_prim_id, scn_primitive); + (sphor->scene_view, (unsigned)scn_prim_id, &scn_prim); + if (RES_OK != res){ goto error; } + + res = s3d_primitive_get_attrib(&src_prim, S3D_GEOMETRY_NORMAL, st, &src_normal); + if (RES_OK != res){ goto error; } + res = s3d_primitive_get_attrib(&scn_prim, S3D_GEOMETRY_NORMAL, st, &scn_normal); + if (RES_OK != res){ goto error; } + + f3_normalize(src_normal.value, src_normal.value); + f3_normalize(scn_normal.value, scn_normal.value); + + /* Flip the normal in Star-Phor convention, i.e., right-hand rule */ + f3_minus(src_normal.value, src_normal.value); + f3_minus(scn_normal.value, scn_normal.value); + + position->uv[0] = st[0]; + position->uv[1] = st[1]; + position->primitive.prim_id = scn_prim_id; + position->primitive.interface = + darray_interface_data_get(&sphor->interfaces) + scn_prim_id; + position->primitive.normal[0] = scn_normal.value[0]; + position->primitive.normal[1] = scn_normal.value[1]; + position->primitive.normal[2] = scn_normal.value[2]; + + if(f3_dot(src_normal.value, scn_normal.value) > 0) { + /* The scene normal points in the same direction of the source normal, that + * points on the emission direction, i.e., */ + position->primitive.side = SPHIN_SIDE_FRONT; + } else { + position->primitive.side = SPHIN_SIDE_BACK; + } + + res = s3d_primitive_get_attrib(&scn_prim, S3D_POSITION, st, &scn_pos); if (RES_OK != res){ goto error; } - *source = source_to_sample; + d3_set_f3(pos, scn_pos.value); + *source_view = source_to_sample; exit: return res; error: diff --git a/src/sphor_ran_source.h b/src/sphor_ran_source.h @@ -35,8 +35,7 @@ extern LOCAL_SYM res_T source_surface_sample_direction (struct sphor* sphor, struct ssp_rng* rng, - struct source_view* source_view, - struct s3d_primitive* prim, + struct primitive_pos* prim_pos, double dir[3]); extern LOCAL_SYM res_T @@ -61,7 +60,7 @@ sample_source_position (struct sphor* sphor, struct ssp_rng* rng, struct source_view** source_view, - struct s3d_primitive* scn_primitive, + struct primitive_pos* position, double pos[3]); #endif /* SPHOR_RAN_SOURCES_H */