commit 0b57cc86614e82b4ae0bcfd01e99b792fe36e16c
parent 892da0223168ea7c6c9071cfda30616782d1b80b
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Mon, 14 Oct 2019 15:02:23 +0200
Add callbacks to manage app data for triangles.
Diffstat:
6 files changed, 408 insertions(+), 12 deletions(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -132,6 +132,7 @@ if(NOT NO_TEST)
new_test(test_senc_sample_enclosure)
new_test(test_senc_scene)
new_test(test_senc_undefined_medium)
+ new_test(test_senc_undefined_medium_attr)
target_link_libraries(test_senc_sample_enclosure StarSP)
target_link_libraries(test_senc_many_enclosures Star3DUT)
diff --git a/src/senc.h b/src/senc.h
@@ -172,19 +172,62 @@ senc_scene_reserve
* It can be used to define the 2 sides of a triangle at different times.
* When deduplicating triangles, the first occurence is kept (with its original
* global_id). Users can provide their own global ids for triangles; these ids
- * are not used by the library but are returned as-is by some API calls. */
+ * are not used by the library but are returned as-is by some API calls.
+ * The callbacks devoted to attributes management allow the client app to store
+ * its own data. Add and merge events can fail and stop the add_geometry. */
SENC_API res_T
+senc_scene_add_geometry_attr
+ (struct senc_scene* scene,
+ /* Number of added triangles */
+ const unsigned triangles_count,
+ /* User function that provides vertices ids for added triangles */
+ void(*indices)(const unsigned itri, unsigned ids[3], void* context),
+ /* User function that provides media ids for added triangles */
+ void(*media) /* Can be NULL <=> SENC_UNDEFINED_MEDIUM medium used */
+ (const unsigned itri, unsigned med[2], void* context),
+ void(*global_id) /* Can be NULL <=> use triangle rank */
+ (const unsigned itri, unsigned* gid, void* context),
+ /* Number of added vertices */
+ const unsigned vertices_count,
+ /* User function that provides coordinates for added vertices */
+ 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. */
+ 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_IDth 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. */
+ res_T(*merge_triangle) /* Can be NULL */
+ (const unsigned global_id, const unsigned itri, const int reversed_triangle,
+ void* context),
+ void* context);
+
+SENC_API INLINE res_T
senc_scene_add_geometry
(struct senc_scene* scene,
+ /* Number of added triangles */
const unsigned triangles_count,
+ /* User function that provides vertices ids for added triangles */
void(*indices)(const unsigned itri, unsigned ids[3], void* context),
+ /* User function that provides media ids for added triangles */
void(*media) /* Can be NULL <=> SENC_UNDEFINED_MEDIUM medium used */
(const unsigned itri, unsigned med[2], void* context),
+ /* User function that provides a global id for added triangles. */
void(*global_id) /* Can be NULL <=> use triangle rank */
(const unsigned itri, unsigned* gid, void* context),
+ /* Number of added vertices */
const unsigned vertices_count,
+ /* User function that provides coordinates for added vertices */
void(*position)(const unsigned ivert, double pos[3], void* context),
- void* context);
+ void* context)
+{
+ return senc_scene_add_geometry_attr(scene, triangles_count, indices,
+ media, global_id, vertices_count, position, NULL, NULL, context);
+}
/* Returns a descriptor of the scene that holds the analysis' result. */
SENC_API res_T
@@ -196,7 +239,7 @@ senc_scene_analyze
SENC_API res_T
senc_scene_get_convention
(const struct senc_scene* scene,
- int* convention);
+ int* convention);
/* Returns the number of triangles in the scene. */
SENC_API res_T
diff --git a/src/senc_scene.c b/src/senc_scene.c
@@ -127,14 +127,16 @@ error:
}
res_T
-senc_scene_add_geometry
+senc_scene_add_geometry_attr
(struct senc_scene* scn,
const unsigned ntris,
- void(*indices)(const unsigned itri, unsigned ids[3], void* ctx),
- void(*media)(const unsigned itri, unsigned med[2], void* ctx),
- void(*global_id)(const unsigned itri, unsigned* gid, void* context),
+ void(*indices)(const unsigned, unsigned*, void*),
+ void(*media)(const unsigned, unsigned*, void*),
+ void(*global_id)(const unsigned, unsigned*, void*),
const unsigned nverts,
- void(*position)(const unsigned ivert, double pos[3], void* ctx),
+ 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*),
void* ctx)
{
struct darray_vrtx_id unique_vertice_ids;
@@ -275,9 +277,6 @@ senc_scene_add_geometry
goto error;
} else {
/* Legit duplicate */
- 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);
range_adjust_ptr = darray_triangle_in_data_get(&scn->triangles_in) + *p_trg;
/* Replace possible undefined media */
FOR_EACH(j, 0, 2) {
@@ -287,6 +286,14 @@ senc_scene_add_geometry
scn->sides_with_defined_medium_count++;
}
}
+ 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);
+ }
}
} else {
/* New triangle */
@@ -299,6 +306,9 @@ senc_scene_add_geometry
if(tmp.medium[j] != SENC_UNDEFINED_MEDIUM)
scn->sides_with_defined_medium_count++;
}
+ if(add_triangle) {
+ OK(add_triangle(tmp.global_id, i, ctx));
+ }
++actual_nutris;
}
if(range_adjust_ptr) {
diff --git a/src/test_senc_undefined_medium.c b/src/test_senc_undefined_medium.c
@@ -42,7 +42,7 @@ test(int convention)
conv_front = (convention & SENC_CONVENTION_NORMAL_FRONT) != 0;
conv_in = (convention & SENC_CONVENTION_NORMAL_INSIDE) != 0;
- /* Create the box with reversed triangles */
+ /* Create a box with reversed triangles */
FOR_EACH(i, 0, sizeof(rev_box_indices) / sizeof(*rev_box_indices)) {
switch (i % 3) {
case 0: rev_box_indices[i] = box_indices[i]; break;
diff --git a/src/test_senc_undefined_medium_attr.c b/src/test_senc_undefined_medium_attr.c
@@ -0,0 +1,341 @@
+/* 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 "senc_s3d_wrapper.h"
+#include "test_senc_utils.h"
+
+#include <rsys/dynamic_array_uint.h>
+#include <rsys/double3.h>
+
+#include <star/s3d.h>
+
+/* Manage interface properties */
+struct merge_ctx {
+ struct darray_uint global_interf_data;
+ const unsigned* current_add_interf_data;
+};
+
+res_T add_trg
+ (const unsigned global_id,
+ const unsigned itri,
+ void* context)
+{
+ res_T res = RES_OK;
+ struct context* ctx = context;
+ struct merge_ctx* merge_ctx;
+ unsigned interf;
+ ASSERT(ctx);
+ merge_ctx = ctx->custom;
+ /* Get interface information from ctx */
+ interf = merge_ctx->current_add_interf_data[itri];
+ /* Keep data */
+ res = darray_uint_resize(&merge_ctx->global_interf_data, global_id + 1);
+ if(res != RES_OK) return res;
+ darray_uint_data_get(&merge_ctx->global_interf_data)[global_id] = interf;
+ return res;
+}
+
+res_T merge_trg
+ (const unsigned global_id,
+ const unsigned itri,
+ const int reversed_triangle,
+ void* context)
+{
+ struct context* ctx = context;
+ struct merge_ctx* merge_ctx;
+ unsigned interf;
+ ASSERT(ctx); (void)reversed_triangle;
+ merge_ctx = ctx->custom;
+ /* Get interface information from ctx */
+ interf = merge_ctx->current_add_interf_data[itri];
+ if(darray_uint_data_get(&merge_ctx->global_interf_data)[global_id] != interf)
+ /* Previous interface id is different: no possible merge */
+ return RES_BAD_ARG;
+ /* Same data: no merge needed */
+ return RES_OK;
+}
+
+static void
+test(int convention)
+{
+ struct mem_allocator allocator;
+ struct senc_descriptor* desc = NULL;
+ struct senc_device* dev = NULL;
+ struct senc_scene* scn = NULL;
+ struct senc_enclosure* enclosure;
+ struct senc_enclosure_header header;
+ unsigned medium, expected_external_medium, expected_internal_medium;
+ unsigned gid;
+ struct context ctx;
+ unsigned i, t, ecount, vcount, tcount, scount;
+ int is_front, is_in;
+ unsigned media[12], interface_ids[12] = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 };
+ unsigned rev_box_indices[sizeof(box_indices) / sizeof(*box_indices)];
+ int conv_front, conv_in;
+ struct merge_ctx merge_ctx;
+
+ darray_uint_init(&allocator, &merge_ctx.global_interf_data);
+ merge_ctx.current_add_interf_data = interface_ids;
+
+ conv_front = (convention & SENC_CONVENTION_NORMAL_FRONT) != 0;
+ conv_in = (convention & SENC_CONVENTION_NORMAL_INSIDE) != 0;
+
+ /* Create a box with reversed triangles */
+ FOR_EACH(i, 0, sizeof(rev_box_indices) / sizeof(*rev_box_indices)) {
+ switch (i % 3) {
+ case 0: rev_box_indices[i] = box_indices[i]; break;
+ case 1: rev_box_indices[i] = box_indices[i + 1]; break;
+ case 2: rev_box_indices[i] = box_indices[i - 1]; break;
+ }
+ }
+ 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, convention, &scn));
+ is_front = (convention & SENC_CONVENTION_NORMAL_FRONT) != 0;
+ is_in = (convention & SENC_CONVENTION_NORMAL_INSIDE) != 0;
+
+ /* 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 = &merge_ctx;
+
+ /* Can add the same triangles again defined/undefined media in any order */
+
+ /* Add geometry with no media information on both sides */
+ for (i = 0; i < sizeof(media) / sizeof(*media); i++)
+ media[i] = SENC_UNDEFINED_MEDIUM;
+ OK(senc_scene_add_geometry_attr(scn, ntriangles, get_indices, NULL,
+ NULL, nvertices, get_position, add_trg, merge_trg, &ctx));
+ OK(senc_scene_get_unique_sides_without_medium_count(scn, &scount));
+ CHK(scount == 2 * ntriangles);
+
+ /* If merge fails, add geometry fails */
+ interface_ids[0] = 6;
+ BA(senc_scene_add_geometry_attr(scn, ntriangles, get_indices, NULL,
+ NULL, nvertices, get_position, add_trg, merge_trg, &ctx));
+ interface_ids[0] = 0;
+
+ /* Add geometry with no media information on the front sides */
+ OK(senc_scene_add_geometry_attr(scn, ntriangles, get_indices, get_media,
+ NULL, nvertices, get_position, add_trg, merge_trg, &ctx));
+ OK(senc_scene_get_unique_sides_without_medium_count(scn, &scount));
+ CHK(scount == ntriangles);
+
+ /* Analyze with undefined media on the front sides */
+ OK(senc_scene_analyze(scn, &desc));
+ OK(senc_descriptor_get_enclosure_count(desc, &ecount));
+ CHK(ecount == 2);
+
+ FOR_EACH(i, 0, ecount) {
+ struct senc_enclosure* ee;
+ struct senc_enclosure_header hh;
+ unsigned cc;
+ OK(senc_descriptor_get_enclosure(desc, i, &enclosure));
+ OK(senc_enclosure_get_header(enclosure, &header));
+
+ CHK(header.enclosure_id == i);
+ CHK(header.enclosed_media_count == 1);
+
+ OK(senc_enclosure_get_medium(enclosure, 0, &medium));
+ /* Geometrical normals point outside the cube in input triangles:
+ * if convention is front, front medium (undef) is outside,
+ * that is medium 0's enclosure is infinite */
+ expected_external_medium = conv_front ? SENC_UNDEFINED_MEDIUM : 1;
+ expected_internal_medium = conv_front ? 1 :SENC_UNDEFINED_MEDIUM;
+
+ CHK(medium == (header.is_infinite
+ ? expected_external_medium : expected_internal_medium));
+ CHK(header.triangle_count == ntriangles);
+ CHK(header.unique_triangle_count == ntriangles);
+ CHK(header.vertices_count == nvertices);
+ CHK(header.is_infinite == (i == 0));
+
+ OK(senc_descriptor_get_enclosure_count_by_medium(desc, medium, &cc));
+ CHK(cc == 1);
+ OK(senc_descriptor_get_enclosure_by_medium(desc, medium, 0, &ee));
+ OK(senc_enclosure_get_header(ee, &hh));
+ CHK(header.enclosure_id == hh.enclosure_id);
+ OK(senc_enclosure_ref_put(ee));
+
+ FOR_EACH(t, 0, header.triangle_count) {
+ unsigned ind[3];
+ OK(senc_enclosure_get_triangle_global_id(enclosure, t, &gid));
+ CHK(gid == t);
+ OK(senc_enclosure_get_triangle(enclosure, t, ind));
+ }
+ OK(senc_enclosure_ref_put(enclosure));
+ }
+ OK(senc_descriptor_ref_put(desc));
+
+ /* Same geometry, front media are defined for odd triangles */
+ for (i = 0; i < sizeof(media) / sizeof(*media); i++)
+ media[i] = (i % 2) ? 0 : SENC_UNDEFINED_MEDIUM;
+ OK(senc_scene_add_geometry_attr(scn, ntriangles, get_indices, get_media,
+ NULL, nvertices, get_position, add_trg, merge_trg, &ctx));
+ OK(senc_scene_get_unique_sides_without_medium_count(scn, &scount));
+ CHK(scount == ntriangles / 2);
+
+ /* Analyze with undefined media */
+ OK(senc_scene_analyze(scn, &desc));
+ OK(senc_descriptor_ref_put(desc));
+
+ /* Get the deduplicated geometry without (successful) analyze */
+ OK(senc_scene_get_unique_vertices_count(scn, &vcount));
+ CHK(vcount == nvertices);
+ OK(senc_scene_get_unique_triangles_count(scn, &tcount));
+ CHK(tcount == ntriangles);
+ FOR_EACH(i, 0, tcount) {
+ int j;
+ unsigned med[2], ids[3];
+ OK(senc_scene_get_unique_triangle(scn, i, ids));
+ OK(senc_scene_get_unique_triangle_media(scn, i, med));
+ CHK(med[0] == ((i % 2) ? 0 : SENC_UNDEFINED_MEDIUM) && med[1] == 1);
+ FOR_EACH(j, 0, 3) {
+ double pos[3];
+ CHK(ids[j] < vcount);
+ OK(senc_scene_get_unique_vertex(scn, ids[j], pos));
+ }
+ }
+
+ /* Same information again, using a reversed box */
+ ctx.indices = rev_box_indices;
+ SWAP(const unsigned*, ctx.front_media, ctx.back_media);
+ OK(senc_scene_add_geometry_attr(scn, ntriangles, get_indices, get_media,
+ NULL, nvertices, get_position, add_trg, merge_trg, &ctx));
+ OK(senc_scene_get_unique_sides_without_medium_count(scn, &scount));
+ CHK(scount == ntriangles / 2);
+
+ /* Analyze with undefined media */
+ OK(senc_scene_analyze(scn, &desc));
+ OK(senc_descriptor_ref_put(desc));
+
+ /* Define media for remaining triangles, using reversed box */
+ for (i = 0; i < sizeof(media) / sizeof(*media); i++)
+ media[i] = (i % 2) ? SENC_UNDEFINED_MEDIUM : 0;
+ OK(senc_scene_add_geometry_attr(scn, ntriangles, get_indices, get_media,
+ NULL, nvertices, get_position, add_trg, merge_trg, &ctx));
+ OK(senc_scene_get_unique_sides_without_medium_count(scn, &scount));
+ CHK(scount == 0);
+
+ /* Get the deduplicated geometry without (successful) analyze */
+ OK(senc_scene_get_unique_vertices_count(scn, &vcount));
+ CHK(vcount == nvertices);
+ OK(senc_scene_get_unique_triangles_count(scn, &tcount));
+ CHK(tcount == ntriangles);
+ FOR_EACH(i, 0, tcount) {
+ int j;
+ unsigned med[2], ids[3];
+ OK(senc_scene_get_unique_triangle(scn, i, ids));
+ OK(senc_scene_get_unique_triangle_media(scn, i, med));
+ CHK(med[0] == 0 && med[1] == 1);
+ FOR_EACH(j, 0, 3) {
+ double pos[3];
+ CHK(ids[j] < vcount);
+ OK(senc_scene_get_unique_vertex(scn, ids[j], pos));
+ }
+ }
+
+ /* Analyze with all media defined */
+ OK(senc_scene_analyze(scn, &desc));
+ OK(senc_descriptor_ref_put(desc));
+
+ /* Define media for all triangles, nothing new here */
+ for (i = 0; i < sizeof(media) / sizeof(*media); i++)
+ media[i] = 0;
+ OK(senc_scene_add_geometry_attr(scn, ntriangles, get_indices, get_media,
+ NULL, nvertices, get_position, add_trg, merge_trg, &ctx));
+ OK(senc_scene_get_unique_sides_without_medium_count(scn, &scount));
+ CHK(scount == 0);
+
+ /* Define incoherent media for some triangles */
+ for (i = 0; i < sizeof(media) / sizeof(*media); i++)
+ media[i] = (i % 2);
+ BA(senc_scene_add_geometry_attr(scn, ntriangles, get_indices, get_media,
+ NULL, nvertices, get_position, add_trg, merge_trg, &ctx));
+ OK(senc_scene_get_unique_sides_without_medium_count(scn, &scount));
+ CHK(scount == 0);
+
+ /* Scene is still OK and can be analyzed */
+ OK(senc_scene_analyze(scn, &desc));
+
+ OK(senc_descriptor_get_global_triangles_count(desc, &tcount));
+ CHK(tcount == sizeof(media) / sizeof(*media));
+
+ OK(senc_descriptor_get_enclosure_count(desc, &ecount));
+ CHK(ecount == 2);
+
+ FOR_EACH(i, 0, ecount) {
+ struct senc_enclosure* ee;
+ struct senc_enclosure_header hh;
+ unsigned cc;
+ OK(senc_descriptor_get_enclosure(desc, i, &enclosure));
+ OK(senc_enclosure_get_header(enclosure, &header));
+
+ CHK(header.enclosure_id == i);
+ CHK(header.enclosed_media_count == 1);
+ OK(senc_enclosure_get_medium(enclosure, 0, &medium));
+ /* Geometrical normals point outside the cube in input triangles:
+ * if convention is front, front medium (0) is outside,
+ * that is medium 0's enclosure is infinite */
+ CHK(is_front == ((medium == 0) == header.is_infinite));
+ CHK(header.triangle_count == ntriangles);
+ CHK(header.unique_triangle_count == ntriangles);
+ CHK(header.vertices_count == nvertices);
+ CHK(header.is_infinite == (i == 0));
+
+ OK(senc_descriptor_get_enclosure_count_by_medium(desc, medium, &cc));
+ CHK(cc == 1);
+ OK(senc_descriptor_get_enclosure_by_medium(desc, medium, 0, &ee));
+ OK(senc_enclosure_get_header(ee, &hh));
+ CHK(header.enclosure_id == hh.enclosure_id);
+ OK(senc_enclosure_ref_put(ee));
+
+ FOR_EACH(t, 0, header.triangle_count) {
+ OK(senc_enclosure_get_triangle_global_id(enclosure, t, &gid));
+ CHK(gid == t);
+ }
+ OK(senc_enclosure_ref_put(enclosure));
+ }
+
+ SENC(scene_ref_put(scn));
+ SENC(device_ref_put(dev));
+ SENC(descriptor_ref_put(desc));
+ darray_uint_release(&merge_ctx.global_interf_data);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+}
+
+int
+main(int argc, char** argv)
+{
+ (void) argc, (void) argv;
+ test(SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_INSIDE);
+ test(SENC_CONVENTION_NORMAL_BACK | SENC_CONVENTION_NORMAL_INSIDE);
+ test(SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_OUTSIDE);
+ test(SENC_CONVENTION_NORMAL_BACK | SENC_CONVENTION_NORMAL_OUTSIDE);
+ return 0;
+}
diff --git a/src/test_senc_utils.h b/src/test_senc_utils.h
@@ -82,6 +82,7 @@ struct context {
const unsigned* front_media;
const unsigned* back_media;
const unsigned* global_ids;
+ void* custom;
double offset[3];
double scale;
char reverse_vrtx, reverse_med;