commit 10498e115555814aaa80443e4d41fd6060205094
parent 1848419c9fd6d58ffc53f1920c828be60eee6ef8
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Thu, 10 Oct 2019 14:13:17 +0200
Make possible to add geometry with some side's media undefined
Allows to create geometry on-the-fly, in multiple rounds
Diffstat:
7 files changed, 257 insertions(+), 42 deletions(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -24,8 +24,8 @@ option(NO_TEST "Do not build tests" OFF)
# Check dependencies
################################################################################
find_package(RCMake 0.4 REQUIRED)
-find_package(Star3D 0.5 REQUIRED)
-find_package(RSys 0.6.1 REQUIRED)
+find_package(Star3D 0.6 REQUIRED)
+find_package(RSys 0.8.1 REQUIRED)
find_package(OpenMP 2.0 REQUIRED)
if(NOT NO_TEST)
@@ -131,6 +131,7 @@ if(NOT NO_TEST)
new_test(test_senc_many_triangles)
new_test(test_senc_sample_enclosure)
new_test(test_senc_scene)
+ new_test(test_senc_undefined_medium)
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
@@ -40,6 +40,9 @@
* as CPU cores */
#define SENC_NTHREADS_DEFAULT (~0u)
+/* A constant to specify an undefined medium */
+#define SENC_UNDEFINED_MEDIUM UINT_MAX
+
/* Forward declaration of external opaque data types */
struct logger;
struct mem_allocator;
@@ -163,6 +166,8 @@ senc_scene_reserve
* 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) and are deduplicated.
+ * 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.
* 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. */
@@ -178,7 +183,8 @@ senc_scene_add_geometry
void(*position)(const unsigned ivert, double pos[3], void* context),
void* context);
-/* Returns a descriptor of the scene that holds the analysis' result. */
+/* Returns a descriptor of the scene that holds the analysis' result.
+ * Its an error that some triangle side still has undefined medium. */
SENC_API res_T
senc_scene_analyze
(struct senc_scene* scene,
diff --git a/src/senc_scene.c b/src/senc_scene.c
@@ -42,6 +42,15 @@ scene_release(ref_T * ref)
SENC(device_ref_put(dev));
}
+static INLINE int
+compatible_medium
+ (const medium_id_t m1,
+ const medium_id_t m2)
+{
+ if(m1 == SENC_UNDEFINED_MEDIUM || m2 == SENC_UNDEFINED_MEDIUM) return 1;
+ return (m1 == m2);
+}
+
/*******************************************************************************
* Exported functions
******************************************************************************/
@@ -76,6 +85,7 @@ senc_scene_create
scn->nmeds = 0;
scn->nverts = 0;
scn->nuverts = 0;
+ scn->sides_with_defined_medium_count = 0;
darray_triangle_in_init(dev->allocator, &scn->triangles_in);
darray_position_init(dev->allocator, &scn->vertices);
htable_vrtx_init(dev->allocator, &scn->unique_vertices);
@@ -169,8 +179,8 @@ senc_scene_add_geometry
} else {
/* New vertex */
unique_v = scn->nuverts + actual_nuverts;
- OK(darray_position_push_back(&scn->vertices, &tmp));
ASSERT(unique_v == htable_vrtx_size_get(&scn->unique_vertices));
+ OK(darray_position_push_back(&scn->vertices, &tmp));
OK(htable_vrtx_set(&scn->unique_vertices, &tmp, &unique_v));
++actual_nuverts;
}
@@ -185,7 +195,7 @@ senc_scene_add_geometry
unsigned med[2];
unsigned ind[3];
union vrtx_id3 trg_key;
- struct triangle_in tmp;
+ struct triangle_in tmp, *range_adjust_ptr = NULL;
trg_id_t* p_trg;
char reversed;
if(global_id) {
@@ -193,7 +203,7 @@ senc_scene_add_geometry
} else {
tmp.global_id = (unsigned)(scn->ntris + i);
}
- indices(i, ind, ctx); /* API: indices needs an unsigned */
+ indices(i, ind, ctx); /* API: indices need unsigneds */
FOR_EACH(j, 0, 3) {
if(ind[j] >= nverts) {
res = RES_BAD_ARG;
@@ -220,8 +230,8 @@ senc_scene_add_geometry
/* Get media */
media(i, med, ctx); /* API: media needs an unsigned */
FOR_EACH(j, 0, 2) {
- if(med[j] >= scn->nmeds) {
- ASSERT(med[j] <= MEDIUM_MAX__);
+ if(med[j] != SENC_UNDEFINED_MEDIUM && med[j] >= scn->nmeds) {
+ ASSERT(med[j] < MEDIUM_MAX__);
scn->nmeds = med[j] + 1;
darray_side_range_resize(&scn->media_use, scn->nmeds);
}
@@ -237,9 +247,11 @@ senc_scene_add_geometry
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(umed[0] != (same ? med[0] : med[1])
- || umed[1] != (same ? med[1] : med[0])) {
+ 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);
@@ -256,8 +268,8 @@ senc_scene_add_geometry
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)med[reversed ? 1 : 0],
- (unsigned long)med[reversed ? 0 : 1]);
+ (unsigned long)tmp.medium[reversed ? 1 : 0],
+ (unsigned long)tmp.medium[reversed ? 0 : 1]);
res = RES_BAD_ARG;
goto error;
} else {
@@ -265,29 +277,45 @@ senc_scene_add_geometry
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);
- if(!same) {
- FOR_EACH(j, 0, 2) {
- tmp.medium[j] = (medium_id_t)med[1-j];
+ 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 {
/* New triangle */
trg_id_t u = scn->nutris + actual_nutris;
- struct side_range* media_use;
ASSERT(u == htable_trg_size_get(&scn->unique_triangles));
OK(htable_trg_set(&scn->unique_triangles, &trg_key, &u));
OK(darray_triangle_in_push_back(&scn->triangles_in, &tmp));
+ range_adjust_ptr = darray_triangle_in_data_get(&scn->triangles_in) + u;
FOR_EACH(j, 0, 2) {
+ if(tmp.medium[j] != SENC_UNDEFINED_MEDIUM)
+ scn->sides_with_defined_medium_count++;
+ }
+ ++actual_nutris;
+ }
+ if(range_adjust_ptr) {
+ ptrdiff_t u = range_adjust_ptr - trg;
+ ASSERT(u < scn->nutris + actual_nutris && u < TRG_MAX__);
+ FOR_EACH(j, 0, 2) {
+ struct side_range* media_use;
+ if(tmp.medium[j] == SENC_UNDEFINED_MEDIUM) continue;
ASSERT(tmp.medium[j] < scn->nmeds);
media_use = darray_side_range_data_get(&scn->media_use) + tmp.medium[j];
- media_use->first = MMIN(media_use->first, TRGIDxSIDE_2_TRGSIDE(u, j));
+ media_use->first =
+ MMIN(media_use->first, TRGIDxSIDE_2_TRGSIDE((trg_id_t)u, j));
ASSERT(media_use->first < 2 * (scn->nutris + actual_nutris + 1));
- media_use->last = MMAX(media_use->last, TRGIDxSIDE_2_TRGSIDE(u, j));
+ media_use->last =
+ MMAX(media_use->last, TRGIDxSIDE_2_TRGSIDE((trg_id_t)u, j));
ASSERT(media_use->last < 2 * (scn->nutris + actual_nutris + 1));
ASSERT(media_use->first <= media_use->last);
}
- ++actual_nutris;
}
++actual_ntris;
}
diff --git a/src/senc_scene_analyze.c b/src/senc_scene_analyze.c
@@ -108,11 +108,11 @@ get_scn_position(const unsigned ivert, float pos[3], void* ctx) {
static int
self_hit_filter
-(const struct s3d_hit* hit,
- const float ray_org[3],
- const float ray_dir[3],
- void* ray_data,
- void* filter_data)
+ (const struct s3d_hit* hit,
+ const float ray_org[3],
+ const float ray_dir[3],
+ void* ray_data,
+ void* filter_data)
{
const struct darray_triangle_comp* triangles_comp = filter_data;
const component_id_t* origin_component = ray_data;
@@ -148,7 +148,7 @@ extract_connex_components
{
/* This function is called from an omp parallel block and executed
* concurrently. */
- const struct senc_scene* scn;
+ struct senc_scene* scn;
struct mem_allocator* alloc;
int64_t mm;
struct darray_side_id stack;
@@ -502,7 +502,7 @@ extract_connex_components
ASSERT(desc->scene->nuverts < UINT_MAX);
OK(s3d_mesh_setup_indexed_vertices(s3d_shp,
(unsigned)desc->scene->nutris, get_scn_indices,
- (unsigned)desc->scene->nuverts, &attribs, 1, desc->scene));
+ (unsigned)desc->scene->nuverts, &attribs, 1, scn));
s3d_mesh_set_hit_filter_function(s3d_shp, self_hit_filter,
triangles_comp_array);
OK(s3d_scene_attach_shape(s3d_scn, s3d_shp));
@@ -692,8 +692,8 @@ collect_and_link_neighbours
{
/* This function is called from an omp parallel block and executed
* concurrently. */
- const struct triangle_in *triangles_in;
- struct triangle_tmp *triangles_tmp;
+ const struct triangle_in* triangles_in;
+ struct triangle_tmp* triangles_tmp;
const union double3* vertices;
const int thread_count = omp_get_num_threads();
const int rank = omp_get_thread_num();
@@ -743,7 +743,7 @@ collect_and_link_neighbours
const vrtx_id_t v0 = triangles_in[t].vertice_id[ee];
const vrtx_id_t v1 = triangles_in[t].vertice_id[(ee + 1) % 3];
/* Process only "my" edges! */
- const int64_t h =
+ const int64_t h =
/* v0,v1 and v1,v0 must give the same hash!!! */
v0 + v1 + (int64_t)MMIN(v0, v1);
if(h % thread_count != rank) continue;
@@ -1148,7 +1148,9 @@ build_result
* Exported functions
******************************************************************************/
res_T
-senc_scene_analyze(struct senc_scene* scn, struct senc_descriptor** out_desc)
+senc_scene_analyze
+ (struct senc_scene* scn,
+ struct senc_descriptor** out_desc)
{
struct senc_descriptor* desc = NULL;
/* By triangle tmp data */
@@ -1177,6 +1179,12 @@ senc_scene_analyze(struct senc_scene* scn, struct senc_descriptor** out_desc)
if(!scn || !out_desc) return RES_BAD_ARG;
+ if(scn->sides_with_defined_medium_count < 2 * scn->nutris) {
+ log_err(scn->dev,
+ "%s: not all triangles have defined media on both sides.\n", FUNC_NAME);
+ return RES_BAD_ARG;
+ }
+
desc = descriptor_create(scn);
if(!desc) {
res = RES_MEM_ERR;
diff --git a/src/senc_scene_c.h b/src/senc_scene_c.h
@@ -52,21 +52,19 @@ struct triangle_in {
unsigned global_id;
};
-#ifndef NDEBUG
static FINLINE void
triangle_in_init(struct mem_allocator* alloc, struct triangle_in* trg) {
int i;
(void)alloc;
ASSERT(trg);
FOR_EACH(i, 0, 3) trg->vertice_id[i] = VRTX_NULL__;
- FOR_EACH(i, 0, 2) trg->medium[i] = MEDIUM_NULL__;
+ FOR_EACH(i, 0, 2) trg->medium[i] = SENC_UNDEFINED_MEDIUM;
trg->global_id = 0;
}
-#define DARRAY_FUNCTOR_INIT triangle_in_init
-#endif
#define DARRAY_NAME triangle_in
#define DARRAY_DATA struct triangle_in
+#define DARRAY_FUNCTOR_INIT triangle_in_init
#include <rsys/dynamic_array.h>
static FINLINE void
@@ -221,6 +219,7 @@ struct senc_scene {
vrtx_id_t nverts, nuverts; /* Vrtx count, unique vrtx count */
medium_id_t nmeds;
struct darray_side_range media_use;
+ side_id_t sides_with_defined_medium_count;
ref_T ref;
struct senc_device* dev;
diff --git a/src/test_senc_undefined_medium.c b/src/test_senc_undefined_medium.c
@@ -0,0 +1,170 @@
+/* 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/double3.h>
+
+#include <star/s3d.h>
+
+static void
+test(enum senc_convention 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;
+ unsigned gid;
+ struct context ctx;
+ unsigned i, t, ecount, tcount;
+ int is_front, is_in;
+ unsigned media[12];
+ unsigned rev_box_indices[sizeof(box_indices) / sizeof(*box_indices)];
+
+ /* Create the 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;
+
+ /* Can add the same triangles again defined/undefined media in any order */
+
+ /* Add geometry with no media information */
+ for (i = 0; i < sizeof(media) / sizeof(*media); i++)
+ media[i] = SENC_UNDEFINED_MEDIUM;
+ OK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media,
+ NULL, nvertices, get_position, &ctx));
+
+ /* Cannot run analyze with undefined media */
+ BA(senc_scene_analyze(scn, &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(scn, ntriangles, get_indices, get_media,
+ NULL, nvertices, get_position, &ctx));
+
+ /* Cannot run analyze with undefined media */
+ BA(senc_scene_analyze(scn, &desc));
+
+ /* 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(scn, ntriangles, get_indices, get_media,
+ NULL, nvertices, get_position, &ctx));
+
+ /* Cannot run analyze with undefined media */
+ BA(senc_scene_analyze(scn, &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(scn, ntriangles, get_indices, get_media,
+ NULL, nvertices, get_position, &ctx));
+
+ /* Can run analyze */
+ 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(scn, ntriangles, get_indices, get_media,
+ NULL, nvertices, get_position, &ctx));
+
+ /* Define incoherent media for some triangles */
+ for (i = 0; i < sizeof(media) / sizeof(*media); i++)
+ media[i] = (i % 2);
+ BA(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media,
+ NULL, nvertices, get_position, &ctx));
+
+ /* 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) {
+ 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));
+
+ 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));
+
+ 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
@@ -22,6 +22,9 @@
#include <stdio.h>
+#define OK(Expr) CHK((Expr) == RES_OK)
+#define BA(Expr) CHK((Expr) == RES_BAD_ARG)
+
/*******************************************************************************
* Geometry
******************************************************************************/
@@ -94,9 +97,9 @@ static const unsigned medium1_front0[12] = { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
static const unsigned gid_face[12] = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 };
static INLINE void
-get_indices(const unsigned itri, unsigned ids[3], void* context)
+get_indices(const unsigned itri, unsigned ids[3], const void* context)
{
- struct context* ctx = context;
+ const struct context* ctx = context;
ASSERT(ids && ctx);
ids[0] = ctx->indices[itri * 3 + 0];
ids[ctx->reverse_vrtx ? 2 : 1] = ctx->indices[itri * 3 + 1];
@@ -104,9 +107,9 @@ get_indices(const unsigned itri, unsigned ids[3], void* context)
}
static INLINE void
-get_position(const unsigned ivert, double pos[3], void* context)
+get_position(const unsigned ivert, double pos[3], const void* context)
{
- struct context* ctx = context;
+ const struct context* ctx = context;
ASSERT(pos && ctx);
pos[0] = ctx->positions[ivert * 3 + 0] * ctx->scale + ctx->offset[0];
pos[1] = ctx->positions[ivert * 3 + 1] * ctx->scale + ctx->offset[1];
@@ -114,18 +117,18 @@ get_position(const unsigned ivert, double pos[3], void* context)
}
static INLINE void
-get_media(const unsigned itri, unsigned medium[2], void* context)
+get_media(const unsigned itri, unsigned medium[2], const void* context)
{
- struct context* ctx = context;
+ const struct context* ctx = context;
ASSERT(medium && ctx);
medium[ctx->reverse_med ? 1 : 0] = ctx->front_media[itri];
medium[ctx->reverse_med ? 0 : 1] = ctx->back_media[itri];
}
static INLINE void
-get_global_id(const unsigned itri, unsigned* gid, void* context)
+get_global_id(const unsigned itri, unsigned* gid, const void* context)
{
- struct context* ctx = context;
+ const struct context* ctx = context;
ASSERT(gid && context);
*gid = ctx->global_ids[itri];
}