commit 8361b263b75d1408559f86a988b147ed54ae4fc2
parent 73f24da03d4ed25c8c2bd64ff920e67680a58c10
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Fri, 21 Jun 2024 12:04:55 +0200
API update for custom sampling of conductive paths
The sampled path no longer returns the hit corresponding to its arrival
limit position. It returns only its position and the geometric primitive
on which it lies. The hit is then defined on the solver side when the
custom sampling procedure returns.
This is both easier for the caller and less error-prone since, in
addition to the intersected primitive, its normal and the parametric
coordinate of the intersection also had to be defined in coherence with
the solver. This meant that the caller had to take into account the
order of the vertices in his geometry in relation to the order of the
vertices on the solver's side, which in reality he knows nothing about.
Now, this data is managed by the solver. The only constraint is that the
position of the path is actually on the primitive it is supposed to have
reached.
In return, the solver performs an additional "nearest point" query to
match the position and path primitive to the corresponding intersection.
But this query doesn't take much time, as the search radius can be
limited to the delta of the solid, for example. And if no results can be
found in the nearest neighborhood, this means that the sampled path
returned is invalid, and this is flagged as an error.
Diffstat:
5 files changed, 84 insertions(+), 38 deletions(-)
diff --git a/src/sdis.h b/src/sdis.h
@@ -225,8 +225,8 @@ struct sdis_path {
struct sdis_rwalk_vertex vtx; /* Current position and time */
/* Surface intersection. When defined, the path is on a border */
- struct s2d_hit hit_2d;
- struct s3d_hit hit_3d;
+ struct s2d_primitive prim_2d;
+ struct s3d_primitive prim_3d;
double elapsed_time; /* Time elapsed along the path */
double weight; /* Monte Carlo weight update along the path */
@@ -236,8 +236,8 @@ struct sdis_path {
};
#define SDIS_PATH_NULL__ { \
SDIS_RWALK_VERTEX_NULL__, \
- S2D_HIT_NULL__, \
- S3D_HIT_NULL__, \
+ S2D_PRIMITIVE_NULL__, \
+ S3D_PRIMITIVE_NULL__, \
0, /* Elapsed time */ \
0, /* MC weight */ \
0 /* At limit */ \
diff --git a/src/sdis_Xd_begin.h b/src/sdis_Xd_begin.h
@@ -65,6 +65,7 @@
#define SXD_GET_PRIMITIVE CONCAT(CONCAT(S, DIM), D_GET_PRIMITIVE)
#define SXD_SAMPLE CONCAT(CONCAT(S, DIM), D_SAMPLE)
#define SXD_TRACE CONCAT(CONCAT(S, DIM), D_TRACE)
+#define SXD_PRIMITIVE_NULL CONCAT(CONCAT(S, DIM), D_PRIMITIVE_NULL)
#define SXD_PRIMITIVE_EQ CONCAT(CONCAT(S, DIM), D_PRIMITIVE_EQ)
/* Vector macros generic to SDIS_XD_DIMENSION */
diff --git a/src/sdis_Xd_end.h b/src/sdis_Xd_end.h
@@ -34,6 +34,8 @@
#undef SXD_GET_PRIMITIVE
#undef SXD_SAMPLE
#undef SXD_TRACE
+#undef SXD_PRIMITIVE_NULL
+#undef SXD_PRIMITIVE_EQ
#undef dX
#undef fX
diff --git a/src/sdis_heat_path_conductive_custom_Xd.h b/src/sdis_heat_path_conductive_custom_Xd.h
@@ -30,14 +30,14 @@ XD(check_sampled_path)
(struct sdis_scene* scn,
const struct sdis_path* path)
{
- const struct sXd(hit)* hit = NULL;
+ int null_prim = 0;
res_T res = RES_OK;
ASSERT(scn && path);
- hit = &path->XD(hit);
+ null_prim = SXD_PRIMITIVE_EQ(&path->XD(prim), &SXD_PRIMITIVE_NULL);
/* Check end of path */
- if(!path->at_limit && SXD_HIT_NONE(hit)) {
+ if(!path->at_limit && null_prim) {
log_err(scn->dev,
"%s: the sampled path should have reached a limit condition or a boundary"
" -- pos=("FORMAT_VECX")\n",
@@ -46,36 +46,75 @@ XD(check_sampled_path)
goto error;
}
- if(!SXD_HIT_NONE(&path->XD(hit))) {
- struct sXd(attrib) attr;
+ if(!null_prim) {
struct sXd(primitive) prim;
- const unsigned iprim = hit->prim.prim_id;
- float N[3] = {0,0,0};
+ const unsigned iprim = path->XD(prim).prim_id;
/* Check intersected primitive */
res = sXd(scene_view_get_primitive)(scn->sXd(view), iprim, &prim);
- if(res != RES_OK) {
+ if(res != RES_OK || !SXD_PRIMITIVE_EQ(&path->XD(prim), &prim)) {
log_err(scn->dev,
"%s: invalid intersected primitive on sampled path -- %s\n",
FUNC_NAME, res_to_cstr(res));
goto error;
}
+ }
- /* Check normal */
-#if DIM == 2
- SXD(primitive_get_attrib(&prim, SXD_GEOMETRY_NORMAL, hit->u, &attr));
-#else
- SXD(primitive_get_attrib(&prim, SXD_GEOMETRY_NORMAL, hit->uv, &attr));
-#endif
- fX(normalize)(attr.value, attr.value);
- fX(normalize)(N, hit->normal);
- if(!fX(eq)(attr.value, N)) {
- log_err(scn->dev,
- "%s: invalid normal on the intersected primitive\n",
- FUNC_NAME);
- res = RES_BAD_ARG;
- goto error;
- }
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static int
+XD(keep_only_one_primitive)
+ (const struct sXd(hit)* hit,
+ const float org[DIM],
+ const float dir[DIM],
+ const float range[2],
+ void* query_data,
+ void* filter_data)
+{
+ const struct sXd(primitive)* prim = query_data;
+ (void)org, (void)dir, (void)range, (void)filter_data;
+ return !SXD_PRIMITIVE_EQ(prim, &hit->prim);
+}
+
+static res_T
+XD(get_path_hit)
+ (struct sdis_scene* scn,
+ struct sdis_path* path,
+ const struct sdis_medium* mdm,
+ struct sXd(hit)* hit)
+{
+ struct hit_filter_data filter_data = HIT_FILTER_DATA_NULL;
+ float query_radius = 0;
+ float query_pos[DIM] = {0};
+ double delta = 0;
+ res_T res = RES_OK;
+ ASSERT(scn && path && hit);
+
+ filter_data.XD(custom_filter) = XD(keep_only_one_primitive);
+ filter_data.custom_filter_data = &path->XD(prim);
+
+ /* Search for the hit corresponding to the path position on a primitive. Search
+ * at a maximum distance from the delta of the medium, as this hit should be
+ * very close to the submitted position, since it should represent the same
+ * point. */
+ delta = solid_get_delta(mdm, &path->vtx);
+ fX_set_dX(query_pos, path->vtx.P);
+ query_radius = (float)delta;
+ SXD(scene_view_closest_point(scn->sXd(view), query_pos, query_radius,
+ &filter_data, hit));
+ ASSERT(SXD_PRIMITIVE_EQ(&hit->prim, &path->XD(prim)));
+
+ if(SXD_HIT_NONE(hit)) {
+ log_warn(scn->dev,
+ "%s: the position returned by custom sampling of the conductive path "
+ "is too far from the primitive it should be on -- search distance=%g\n",
+ FUNC_NAME, delta);
+ res = RES_BAD_OP;
+ goto error;
}
exit:
@@ -98,7 +137,11 @@ XD(conductive_path_custom)
{
struct sdis_path path = SDIS_PATH_NULL;
res_T res = RES_OK;
+
+ /* Check pre-conditions */
ASSERT(scn && rwalk && rng && T);
+ ASSERT(sdis_medium_get_type(mdm) == SDIS_SOLID);
+ ASSERT(mdm->shader.solid.sample_path);
/* Sample a conductive path */
path.vtx = rwalk->vtx;
@@ -112,11 +155,13 @@ XD(conductive_path_custom)
}
res = XD(check_sampled_path)(scn, &path);
+ if(res!= RES_OK) goto error;
+
+ res = XD(get_path_hit)(scn, &path, mdm, &rwalk->XD(hit));
if(res != RES_OK) goto error;
/* Update random walk position and time from sampled path */
rwalk->vtx = path.vtx;
- rwalk->XD(hit) = path.XD(hit);
rwalk->elapsed_time += path.elapsed_time;
/* The path reached a boundary */
diff --git a/src/test_sdis_custom_solid_path_sampling.c b/src/test_sdis_custom_solid_path_sampling.c
@@ -195,32 +195,30 @@ struct custom_solid {
};
static void
-setup_solver_hit
+setup_solver_primitive
(struct sdis_scene* scn,
const struct shape* shape,
const struct s3d_hit* user_hit,
- struct s3d_hit* solver_hit)
+ struct s3d_primitive* prim)
{
struct sdis_primkey key = SDIS_PRIMKEY_NULL;
const double *v0, *v1, *v2;
float v0f[3], v1f[3], v2f[3];
struct s3d_attrib attr0, attr1, attr2;
- *solver_hit = *user_hit;
-
v0 = shape->pos + shape->ids[user_hit->prim.prim_id*3+0]*3;
v1 = shape->pos + shape->ids[user_hit->prim.prim_id*3+1]*3;
v2 = shape->pos + shape->ids[user_hit->prim.prim_id*3+2]*3;
sdis_primkey_setup(&key, v0, v1, v2);
- OK(sdis_scene_get_s3d_primitive(scn, &key, &solver_hit->prim));
+ OK(sdis_scene_get_s3d_primitive(scn, &key, prim));
/* Check that the primitive on the solver side is the same as that on the
* user side. On the solver side, vertices are stored in simple precision in
* Star-3D view. We therefore need to take care of this conversion to check
* that the vertices are the same */
- OK(s3d_triangle_get_vertex_attrib(&solver_hit->prim, 0, S3D_POSITION, &attr0));
- OK(s3d_triangle_get_vertex_attrib(&solver_hit->prim, 1, S3D_POSITION, &attr1));
- OK(s3d_triangle_get_vertex_attrib(&solver_hit->prim, 2, S3D_POSITION, &attr2));
+ OK(s3d_triangle_get_vertex_attrib(prim, 0, S3D_POSITION, &attr0));
+ OK(s3d_triangle_get_vertex_attrib(prim, 1, S3D_POSITION, &attr1));
+ OK(s3d_triangle_get_vertex_attrib(prim, 2, S3D_POSITION, &attr2));
f3_set_d3(v0f, v0);
f3_set_d3(v1f, v1);
f3_set_d3(v2f, v2);
@@ -294,11 +292,11 @@ sample_steady_diffusive_path
d3_set(path->vtx.P, pos);
path->weight = 0;
path->at_limit = 0;
- path->hit_2d = S2D_HIT_NULL;
+ path->prim_2d = S2D_PRIMITIVE_NULL;
path->elapsed_time = 0;
path->weight = 0;
path->at_limit = 0;
- setup_solver_hit(scn, solid->shape, &hit, &path->hit_3d);
+ setup_solver_primitive(scn, solid->shape, &hit, &path->prim_3d);
return RES_OK;
}