commit f714362a43df38fc2418344e8153905dcbd9f44c
parent 3546aba57689b7d9fdc4f2083c8b52e50edcbe89
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Fri, 22 Nov 2019 17:26:54 +0100
Add involved medias as args to the merge callback.
Merge can now decide to continue add_geometry even if media are
incompatible. In this case the client app is supposed to record the
error and stop after the add_geometry. A common use of this could
be to record all the incompatibilities and stop afterwards.
Diffstat:
7 files changed, 271 insertions(+), 61 deletions(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -120,6 +120,7 @@ if(NOT NO_TEST)
register_test(${_name} ${_name})
endfunction()
+ new_test(test_senc_add_n_merge)
new_test(test_senc_cube_behind_cube)
new_test(test_senc_cube_in_cube)
new_test(test_senc_cube_on_cube)
diff --git a/src/senc.h b/src/senc.h
@@ -172,17 +172,23 @@ senc_scene_reserve
const unsigned media_count);
/* Add a new set of vertices and triangles to the scene.
- * Vertices can be duplicates and are deduplicated on the fly.
- * Triangles can be duplicates as long as they constantly define the same
- * medium on both sides (or an error will be reported, with the exception
- * of SENC_UNDEFINED_MEDIUM that causes no error) and are deduplicated.
+ * Vertices can be duplicates and are silently deduplicated on the fly.
+ * Triangles can be duplicates as long as media are compatible on both sides.
+ * Valid triangle duplicates are silently deduplicated, invalid duplicates
+ * trigger an error (add_geometry returns RES_BAD_ARG).
* The special value SENC_UNDEFINED_MEDIUM denotes an undefined medium.
* It can be used to define the 2 sides of a triangle at different times.
+ * Media on duplicate triangles are consider compatible if:
+ * - the merge_triangle callback is provided and returns RES_OK,
+ * - or media are identical or SENC_UNDEFINED_MEDIUM.
* When deduplicating triangles, the first occurence remains (with its
- * original index in user world, regardless of deduplication).
- * The add_triangle and merge_triangle callbacks can be used for attributes
- * management including triangle IDs; they allow the client app to store
- * its own data. They can also fail and stop the add_geometry call. */
+ * original index in user world, regardless of deduplication); the only
+ * situation where deduplication changes a previously recorded media is from
+ * SENC_UNDEFINED_MEDIUM to any defined medium.
+ * The add_triangle and merge_triangle callbacks can be used for attribute
+ * management (including triangle IDs) and to record media incompatibilities
+ * for a subsequent report; they allow the client app to store its own data.
+ * By returning an error, they can also stop the add_geometry call. */
SENC_API res_T
senc_scene_add_geometry
(struct senc_scene* scene,
@@ -199,16 +205,26 @@ senc_scene_add_geometry
void(*position)(const unsigned ivert, double pos[3], void* context),
/* Called for each new triangle so that the client app can manage its own
* triangle data/properties/attributes.
- * If return is not RES_OK, add_geometry stops and fails. */
+ * If return is not RES_OK, add_geometry stops immediately and returns
+ * whatever value add_triangle returned. */
res_T(*add_triangle) /* Can be NULL */
(const unsigned global_id, const unsigned itri, void* context),
/* Called if the IVERTth triangle of the current add_geometry is equal to
* the global_id_th global triangle so that the client app can try to merge
- * its own triangle data. The reversed_triangle arg indicates if the triangle
- * vertices' order is the same it was when the triangle was first added.
- * If return is not RES_OK, add_geometry stops and fails. */
+ * its own triangle data or record a possible media conflict.
+ * The reversed_triangle arg indicates if the triangle vertices' order is
+ * the same it was when the triangle was first added.
+ * triangle_media and merge_media contain the involved media.
+ * If those media are incompatible and merge_triangle returns RES_OK the
+ * process will continue with the next triangle and possibly end in success.
+ * If return is not RES_OK, add_geometry stops immediately and returns
+ * whatever value merge_triangle returned.
+ * If merge_triangle is NULL, a strict media compatibility is required for
+ * add_geometry to success: add_geometry would stop and return RES_BAD_ARG
+ * on the first occurence of duplicate triangle with incompatible media. */
res_T(*merge_triangle) /* Can be NULL */
(const unsigned global_id, const unsigned itri, const int reversed_triangle,
+ const unsigned triangle_media[2], const unsigned merge_media[2],
void* context),
void* context);
diff --git a/src/senc_scene.c b/src/senc_scene.c
@@ -80,7 +80,6 @@ senc_scene_create
SENC(device_ref_get(dev));
scn->dev = dev;
scn->convention = conv;
- scn->ngeoms = 0;
scn->ntris = 0;
scn->nutris = 0;
scn->next_medium_idx = 0;
@@ -135,7 +134,8 @@ senc_scene_add_geometry
const unsigned nverts,
void(*position)(const unsigned, double*, void* ctx),
res_T(*add_triangle)(const unsigned, const unsigned, void*),
- res_T(*merge_triangle)(const unsigned, const unsigned, const int, void*),
+ res_T(*merge_triangle)(const unsigned, const unsigned, const int,
+ const unsigned*, const unsigned*, void*),
void* ctx)
{
struct darray_vrtx_id unique_vertice_ids;
@@ -173,10 +173,6 @@ senc_scene_add_geometry
p_vrtx = htable_vrtx_find(&scn->unique_vertices, &tmp);
if(p_vrtx) {
/* Duplicate vertex */
- log_warn(scn->dev, "%s: vertex %lu is a duplicate of unique vertex %lu.\n",
- FUNC_NAME, (unsigned long)(scn->nverts + i), (unsigned long)*p_vrtx);
- log_warn(scn->dev, "%s: vertex %lu: (%g %g %g).\n",
- FUNC_NAME, (unsigned long)(scn->nverts + i), SPLIT3(tmp.vec));
unique_v = *p_vrtx;
} else {
/* New vertex */
@@ -240,55 +236,39 @@ senc_scene_add_geometry
reversed = trg_make_key(&trg_key, tmp.vertice_id);
p_trg = htable_trg_find(&scn->unique_triangles, &trg_key);
if(p_trg) {
+ /* Duplicate triangle. Need to check duplicate validity */
union vrtx_id3 utrg_key;
char ureversed = trg_make_key(&utrg_key, trg[*p_trg].vertice_id);
int same = (reversed == ureversed);
const medium_id_t* umed;
- /* Duplicate triangle. Need to check duplicate validity */
ASSERT(trg_key_eq(&trg_key, &utrg_key));
if(!same) SWAP(unsigned, tmp.medium[0], tmp.medium[1]);
umed = trg[*p_trg].medium;
- if(!compatible_medium(umed[0], tmp.medium[0])
- || !compatible_medium(umed[1], tmp.medium[1]))
- {
- /* Same triangles with different media: invalid! */
- const union double3* positions
- = darray_position_cdata_get(&scn->vertices);
- log_err(scn->dev, "%s: triangle %lu is a duplicate"
- " of triangle %lu with incoherent media.\n",
- FUNC_NAME, (unsigned long)tmp.global_id,
- (unsigned long)trg[*p_trg].global_id);
- log_err(scn->dev,
- "Triangle %lu:\n (%g %g %g)\n (%g %g %g)\n (%g %g %g)\n",
- (unsigned long)trg[*p_trg].global_id,
- SPLIT3(positions[trg[*p_trg].vertice_id[0]].vec),
- SPLIT3(positions[trg[*p_trg].vertice_id[1]].vec),
- SPLIT3(positions[trg[*p_trg].vertice_id[2]].vec));
- log_err(scn->dev, "Media: (%lu, %lu) VS (%lu, %lu)\n",
- (unsigned long)umed[ureversed? 1 : 0],
- (unsigned long)umed[ureversed ? 0 : 1],
- (unsigned long)tmp.medium[reversed ? 1 : 0],
- (unsigned long)tmp.medium[reversed ? 0 : 1]);
- res = RES_BAD_ARG;
- goto error;
- } else {
- /* Legit duplicate */
- range_adjust_ptr = darray_triangle_in_data_get(&scn->triangles_in) + *p_trg;
- /* Replace possible undefined media */
+ if(merge_triangle) {
+ /* Let the client app rule. */
+ unsigned smed[2], mmed[2];
FOR_EACH(j, 0, 2) {
- if(range_adjust_ptr->medium[j] == SENC_UNDEFINED_MEDIUM
- && tmp.medium[j] != SENC_UNDEFINED_MEDIUM) {
- range_adjust_ptr->medium[j] = tmp.medium[j];
- scn->sides_with_defined_medium_count++;
- }
+ smed[j] = (unsigned)umed[j];
+ mmed[j] = (unsigned)tmp.medium[j];
}
- if(merge_triangle) {
- OK(merge_triangle(trg[*p_trg].global_id, i, same, ctx));
- } else {
- log_warn(scn->dev,
- "%s: triangle %lu is a duplicate of triangle %lu.\n",
- FUNC_NAME, (unsigned long)tmp.global_id,
- (unsigned long)trg[*p_trg].global_id);
+ OK(merge_triangle(trg[*p_trg].global_id, i, same, smed, mmed, ctx));
+ /* If merge_triangle returns OK its OK even if media are incompatible. */
+ } else {
+ if(!compatible_medium(umed[0], tmp.medium[0])
+ || !compatible_medium(umed[1], tmp.medium[1]))
+ {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ }
+ /* Legit duplicate (or accepted by merge_triangle): replace undef media. */
+ range_adjust_ptr = darray_triangle_in_data_get(&scn->triangles_in) + *p_trg;
+ /* Replace possible undefined media */
+ FOR_EACH(j, 0, 2) {
+ if(range_adjust_ptr->medium[j] == SENC_UNDEFINED_MEDIUM
+ && tmp.medium[j] != SENC_UNDEFINED_MEDIUM) {
+ range_adjust_ptr->medium[j] = tmp.medium[j];
+ scn->sides_with_defined_medium_count++;
}
}
} else {
@@ -335,7 +315,6 @@ exit:
scn->ntris += actual_ntris;
ASSERT(scn->nuverts == htable_vrtx_size_get(&scn->unique_vertices));
ASSERT(scn->nutris == htable_trg_size_get(&scn->unique_triangles));
- ++scn->ngeoms;
return res;
error:
goto exit;
diff --git a/src/senc_scene_analyze.c b/src/senc_scene_analyze.c
@@ -177,7 +177,7 @@ extract_connex_components
/* If there are sides with undefined medium, its like another medium */
undefs = (scn->sides_with_defined_medium_count < 2 * scn->nutris) ? 1 : 0;
-#pragma omp single
+ #pragma omp single
{
if(undefs) {
/* Range is unknown, process each side */
diff --git a/src/senc_scene_c.h b/src/senc_scene_c.h
@@ -214,7 +214,6 @@ struct senc_scene {
struct htable_trg unique_triangles;
/* Keep sizes */
- unsigned ngeoms; /* Not used yet (just counted). */
trg_id_t ntris, nutris; /* Trg count, unique trg count */
vrtx_id_t nverts, nuverts; /* Vrtx count, unique vrtx count */
medium_id_t next_medium_idx;
diff --git a/src/test_senc_add_n_merge.c b/src/test_senc_add_n_merge.c
@@ -0,0 +1,205 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "senc.h"
+#include "test_senc_utils.h"
+
+/* Manage add_geometry behaviour */
+struct add_geom_ctx {
+ unsigned add_cpt, merge_cpt;
+ res_T add_res, merge_res;
+};
+
+static res_T
+add_trg
+ (const unsigned global_id,
+ const unsigned iseg,
+ void* context)
+{
+ struct context* ctx = context;
+ struct add_geom_ctx* add_geom_ctx;
+ ASSERT(ctx); (void)global_id; (void)iseg;
+ add_geom_ctx = ctx->custom;
+ if(add_geom_ctx->add_res == RES_OK) ++add_geom_ctx->add_cpt;
+ return add_geom_ctx->add_res;
+}
+
+static res_T
+merge_trg
+ (const unsigned global_id,
+ const unsigned iseg,
+ const int reversed_segment,
+ const unsigned triangle_media[2],
+ const unsigned merge_media[2],
+ void* context)
+{
+ struct context* ctx = context;
+ struct add_geom_ctx* add_geom_ctx;
+ ASSERT(ctx && triangle_media && merge_media);
+ (void)global_id; (void)iseg; (void)reversed_segment; (void)triangle_media; (void)merge_media;
+ add_geom_ctx = ctx->custom;
+ if(add_geom_ctx->merge_res == RES_OK) ++add_geom_ctx->merge_cpt;
+ return add_geom_ctx->merge_res;
+}
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct senc_device* dev = NULL;
+ struct senc_scene* scn = NULL;
+ struct context ctx;
+ unsigned i;
+ struct add_geom_ctx add_geom_ctx;
+ unsigned media[12];
+ const int conv = SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_INSIDE;
+ const unsigned media_count = sizeof(media) / sizeof(*media);
+ (void)argc; (void)argv;
+
+ OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator));
+ OK(senc_device_create(NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev));
+ OK(senc_scene_create(dev, conv, &scn));
+
+ /* A 3D cube.
+ * 2 enclosures (inside, outside) sharing the same triangles,
+ * but opposite sides */
+ ctx.positions = box_vertices;
+ ctx.indices = box_indices;
+ ctx.scale = 1;
+ ctx.reverse_vrtx = 0;
+ ctx.reverse_med = 0;
+ d3(ctx.offset, 0, 0, 0);
+ ctx.front_media = media;
+ ctx.back_media = medium1;
+ ctx.custom = &add_geom_ctx;
+
+ add_geom_ctx.add_cpt = add_geom_ctx.merge_cpt = 0;
+ add_geom_ctx.add_res = add_geom_ctx.merge_res = RES_OK;
+
+ /* Geometry with no media information on both sides */
+ for (i = 0; i < media_count; i++) media[i] = SENC_UNDEFINED_MEDIUM;
+ ctx.front_media = media;
+ ctx.back_media = media;
+
+ /* If add fails, add geometry fails the same way */
+ add_geom_ctx.add_res = RES_BAD_ARG;
+ BA(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL,
+ nvertices, get_position, add_trg, merge_trg, &ctx));
+ CHK(add_geom_ctx.add_cpt == 0);
+ add_geom_ctx.add_res = RES_MEM_ERR;
+ CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL,
+ nvertices, get_position, add_trg, merge_trg, &ctx) == RES_MEM_ERR);
+ CHK(add_geom_ctx.add_cpt == 0);
+
+ /* Successful add geometry with add callback */
+ add_geom_ctx.add_res = RES_OK;
+ OK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL,
+ nvertices, get_position, add_trg, merge_trg, &ctx));
+ CHK(add_geom_ctx.add_cpt == media_count);
+ CHK(add_geom_ctx.merge_cpt == 0);
+
+ /* Clear scene */
+ SENC(scene_ref_put(scn));
+ OK(senc_scene_create(dev, conv, &scn));
+
+ /* Successful add geometry without add callback */
+ add_geom_ctx.add_cpt = 0;
+ OK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL,
+ nvertices, get_position, NULL, merge_trg, &ctx));
+ CHK(add_geom_ctx.add_cpt == 0);
+ CHK(add_geom_ctx.merge_cpt == 0);
+
+ /* If merge fails, add geometry fails the same way */
+ add_geom_ctx.merge_res = RES_BAD_ARG;
+ BA(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL,
+ nvertices, get_position, add_trg, merge_trg, &ctx));
+ CHK(add_geom_ctx.merge_cpt == 0);
+ add_geom_ctx.merge_res = RES_MEM_ERR;
+ CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL,
+ nvertices, get_position, add_trg, merge_trg, &ctx) == RES_MEM_ERR);
+ CHK(add_geom_ctx.merge_cpt == 0);
+
+ /* Successful add geometry without merge callback */
+ OK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL,
+ nvertices, get_position, add_trg, NULL, &ctx));
+ CHK(add_geom_ctx.merge_cpt == 0);
+
+ /* Successful add geometry with merge callback */
+ add_geom_ctx.merge_res = RES_OK;
+ OK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL,
+ nvertices, get_position, add_trg, merge_trg, &ctx));
+ CHK(add_geom_ctx.merge_cpt == media_count);
+ add_geom_ctx.merge_cpt = 0;
+
+ /* Geometry with media information on both sides */
+ ctx.front_media = medium0;
+ ctx.back_media = medium1;
+
+ /* Clear scene */
+ SENC(scene_ref_put(scn));
+ OK(senc_scene_create(dev, conv, &scn));
+
+ /* Successful add geometry with add callback */
+ add_geom_ctx.add_res = RES_OK;
+ OK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL,
+ nvertices, get_position, add_trg, merge_trg, &ctx));
+ CHK(add_geom_ctx.add_cpt == media_count);
+ CHK(add_geom_ctx.merge_cpt == 0);
+ add_geom_ctx.add_cpt = 0;
+
+ /* Clear scene */
+ SENC(scene_ref_put(scn));
+ OK(senc_scene_create(dev, conv, &scn));
+
+ /* Successful add geometry with add callback */
+ add_geom_ctx.add_res = RES_OK;
+ OK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL,
+ nvertices, get_position, add_trg, merge_trg, &ctx));
+ CHK(add_geom_ctx.add_cpt == media_count);
+ CHK(add_geom_ctx.merge_cpt == 0);
+ add_geom_ctx.add_cpt = 0;
+
+ /* Successful add geometry with merge callback */
+ add_geom_ctx.merge_res = RES_OK;
+ OK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL,
+ nvertices, get_position, add_trg, merge_trg, &ctx));
+ CHK(add_geom_ctx.merge_cpt == media_count);
+ add_geom_ctx.merge_cpt = 0;
+
+ /* Geometry with incompatible media information on both sides */
+ ctx.front_media = medium1;
+ ctx.back_media = medium0;
+
+ /* Unsuccessful add geometry without merge callback */
+ OK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL,
+ nvertices, get_position, add_trg, NULL, &ctx));
+ CHK(add_geom_ctx.merge_cpt == 0);
+
+ /* Successful add geometry with merge callback */
+ add_geom_ctx.merge_res = RES_OK;
+ OK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL,
+ nvertices, get_position, add_trg, merge_trg, &ctx));
+ CHK(add_geom_ctx.merge_cpt == media_count);
+ add_geom_ctx.merge_cpt = 0;
+
+ SENC(scene_ref_put(scn));
+ SENC(device_ref_put(dev));
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+
+ return 0;
+}
diff --git a/src/test_senc_undefined_medium_attr.c b/src/test_senc_undefined_medium_attr.c
@@ -67,6 +67,8 @@ merge_trg
(const unsigned global_id,
const unsigned itri,
const int reversed_triangle,
+ const unsigned triangle_media[2],
+ const unsigned merge_media[2],
void* context)
{
res_T res = RES_OK;
@@ -75,7 +77,15 @@ merge_trg
int need_merge;
unsigned interf;
unsigned* interf_data;
+ int i, compat;
ASSERT(ctx); (void)reversed_triangle;
+ /* Check media compatibility */
+ compat = 1;
+ FOR_EACH(i, 0, 2)
+ compat &= (triangle_media[i] == SENC_UNDEFINED_MEDIUM
+ || merge_media[i] == SENC_UNDEFINED_MEDIUM
+ || triangle_media[i] == merge_media[i]);
+ if (!compat) return RES_BAD_ARG;
merge_ctx = ctx->custom;
res = darray_intface_id_resize(&merge_ctx->global_interf_data, global_id + 1);
if(res != RES_OK) return res;