star-enclosures-3d

Extract enclosures from 3D geometry
git clone git://git.meso-star.fr/star-enclosures-3d.git
Log | Files | Refs | README | LICENSE

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:
Mcmake/CMakeLists.txt | 5+++--
Msrc/senc.h | 8+++++++-
Msrc/senc_scene.c | 60++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/senc_scene_analyze.c | 30+++++++++++++++++++-----------
Msrc/senc_scene_c.h | 7+++----
Asrc/test_senc_undefined_medium.c | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_senc_utils.h | 19+++++++++++--------
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]; }