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 478a65339f787d29f0942f25e842cf349dc75882
parent 6173ec407affbda1fde1c2f1637835816e9105db
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Fri,  5 Oct 2018 12:19:50 +0200

Merge branch 'release_0.2'

Diffstat:
MREADME.md | 67++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mcmake/CMakeLists.txt | 20++++++++++++++++----
Msrc/senc.h | 126++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Msrc/senc_descriptor.c | 84++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Msrc/senc_descriptor_c.h | 21++++++++++++++++-----
Msrc/senc_device.c | 4++++
Msrc/senc_device_c.h | 5+----
Msrc/senc_enclosure.c | 62+++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Msrc/senc_enclosure_c.h | 2+-
Msrc/senc_enclosure_data.h | 142++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/senc_internal_types.h | 95++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/senc_s3d_wrapper.h | 16++++++++--------
Msrc/senc_scene.c | 130++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/senc_scene_analyze.c | 1824++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/senc_scene_analyze_c.h | 200++++++++++++++++++++-----------------------------------------------------------
Msrc/senc_scene_c.h | 45+++++++++++++++++++++++++++++++++++----------
Msrc/test_senc_cube_behind_cube.c | 51+++++++++++++++++++++++++++++++++++++++++++--------
Msrc/test_senc_cube_in_cube.c | 46++++++++++++++++++++++++++++++++++++----------
Msrc/test_senc_cube_on_cube.c | 22++++++++++++++--------
Msrc/test_senc_descriptor.c | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/test_senc_device.c | 2+-
Msrc/test_senc_enclosure.c | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Asrc/test_senc_inconsistant_cube.c | 156+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc_many_enclosures.c | 172+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_senc_many_triangles.c | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Msrc/test_senc_sample_enclosure.c | 35++++++++++++++++++-----------------
Msrc/test_senc_scene.c | 192++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/test_senc_utils.h | 196++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
28 files changed, 2680 insertions(+), 1392 deletions(-)

diff --git a/README.md b/README.md @@ -1,29 +1,54 @@ -# StarEnclosures +StarEnclosures +============== -The purpose of this library is to extract enclosures from raw geometry. An -enclosure is a set of triangles enclosing a given volume. The library manages -vertices and triangles duplicates, easing the scene definition process. It also -checks some coherency properties, most noticeably the enclosed medium unicity. +Overview +-------- -## How to build +The purpose of Star-enclosures is to extract enclosures from raw +geometry. An enclosure is a set of triangles enclosing a given volume. +The enclosure notion extends to open enclosures for which there is no +inside or outside. For every detected enclosure, the library provides +the set of involved triangle sides as well as the set of involved media +and a few metrics (number of triangles, ...). + +The library allows geometry to be added by subsets of triangles and +manages vertices and triangles duplicates as long as the duplicate +information is coherent, easing the scene definition process. + +Also the convention regarding FRONT/BACK sides for input triangles as +well as the convention regarding normals' orientation for output +triangles can be set. + +How to build +------------ StarEnclosures relies on the [CMake](http://www.cmake.org) and the -[RCMake](https://gitlab.com/vaplv/rcmake/) package to build. It also depends on -the [RSys](https://gitlab.com/vaplv/rsys/) and -[Star-3D](https://gitlab.com/meso-star/star-3d/) and -[Star-SP](https://gitlab.com/meso-star/star-sp/) libraries, the later being -necessary only when building tests. +[RCMake](https://gitlab.com/vaplv/rcmake/) package to build. It also +depends on the [RSys](https://gitlab.com/vaplv/rsys/) and +[Star-3D](https://gitlab.com/meso-star/star-3d/) libraries. Additionaly, +two more libraries are needed to build tests: +[Star-SP](https://gitlab.com/meso-star/star-sp/) and +[Star-3DUT](https://gitlab.com/meso-star/star-3dut/). + +First ensure that CMake and a C compiler that implements the OpenMP 1.2 +are installed on your system. Then install the RCMake package as well as +all the aforementioned prerequisites. Finally generate the project from +the `cmake/CMakeLists.txt` file by appending to the `CMAKE_PREFIX_PATH` +variable the install directories of its dependencies. + +Release notes +------------- -First ensure that CMake and a C compiler are installed on your system. Then -install the RCMake package as well as all the aforementioned prerequisites. -Finally generate the project from the `cmake/CMakeLists.txt` file by appending -to the `CMAKE_PREFIX_PATH` variable the install directories of its -dependencies. +### Version 0.2 -## License +- Add the support of enclosures with multiple media. +- Allow to set the FRONT/BACK convention for input triangles. +- Allow to set the normal convention for output triangles. -StarEnclosures is Copyright (C) |Meso|Star> 2018 (<contact@meso-star.com>). -It is free software released under the GPLv3+ license. You are welcome to -redistribute it under certain conditions; refer to the COPYING files for -details. +License +------- +StarEnclosures is Copyright (C) |Meso|Star&gt; 2018 +(<contact@meso-star.com>). It is free software released under the GPLv3+ +license. You are welcome to redistribute it under certain conditions; +refer to the COPYING files for details. diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -26,6 +26,7 @@ option(NO_TEST "Do not build tests" OFF) find_package(RCMake 0.4 REQUIRED) find_package(Star3D 0.5 REQUIRED) find_package(RSys 0.6.1 REQUIRED) +find_package(OpenMP 2.0 REQUIRED) if(NOT NO_TEST) find_package(StarSP 0.7 REQUIRED) @@ -36,11 +37,17 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${RCMAKE_SOURCE_DIR}) include(rcmake) include(rcmake_runtime) +if(NO_TEST) +rcmake_append_runtime_dirs(_runtime_dirs RSys Star3D) +else() +rcmake_append_runtime_dirs(_runtime_dirs RSys StarSP Star3DUT Star3D) +endif() + ################################################################################ # Configure and define targets ################################################################################ set(VERSION_MAJOR 0) -set(VERSION_MINOR 1) +set(VERSION_MINOR 2) set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) @@ -81,14 +88,16 @@ target_link_libraries(senc RSys Star3D) set_target_properties(senc PROPERTIES DEFINE_SYMBOL SENC_SHARED_BUILD VERSION ${VERSION} + COMPILE_FLAGS ${OpenMP_C_FLAGS} SOVERSION ${VERSION_MAJOR}) rcmake_copy_runtime_libraries(senc) if(CMAKE_COMPILER_IS_GNUCC) - set_target_properties(senc PROPERTIES LINK_FLAGS "-lm") + set_target_properties(senc PROPERTIES LINK_FLAGS "${OpenMP_C_FLAGS}") + target_link_libraries(senc m) endif() -rcmake_setup_devel(senc StarEnc ${VERSION} senc_version.h) +rcmake_setup_devel(senc StarEnc ${VERSION} star/senc_version.h) ################################################################################ # Add tests @@ -117,11 +126,14 @@ if(NOT NO_TEST) new_test(test_senc_descriptor) new_test(test_senc_device) new_test(test_senc_enclosure) + new_test(test_senc_inconsistant_cube) + new_test(test_senc_many_enclosures) new_test(test_senc_many_triangles) new_test(test_senc_sample_enclosure) new_test(test_senc_scene) target_link_libraries(test_senc_sample_enclosure StarSP) + target_link_libraries(test_senc_many_enclosures Star3DUT) target_link_libraries(test_senc_many_triangles Star3DUT) rcmake_copy_runtime_libraries(test_senc_sample_enclosure) rcmake_copy_runtime_libraries(test_senc_many_triangles) @@ -134,5 +146,5 @@ install(TARGETS senc ARCHIVE DESTINATION bin LIBRARY DESTINATION lib RUNTIME DESTINATION bin) -install(FILES ${SENC_FILES_INC_API} DESTINATION include/) +install(FILES ${SENC_FILES_INC_API} DESTINATION include/star) install(FILES ${SENC_FILES_DOC} DESTINATION share/doc/star-enc) diff --git a/src/senc.h b/src/senc.h @@ -57,7 +57,7 @@ struct senc_scene; struct senc_enclosure; /* Enclosure header type */ -struct enclosure_header { +struct senc_enclosure_header { /* The ID of the enclosure; 0, 1, ... */ unsigned enclosure_id; /* Number of triangles; a triangle can be accounted for twice, once by side */ @@ -66,24 +66,69 @@ struct enclosure_header { unsigned unique_triangle_count; /* Number of vertices */ unsigned vertices_count; - /* The medium inside the enclosure */ - unsigned enclosed_medium; + /* The number of media inside the enclosure */ + unsigned enclosed_media_count; /* Is the enclosure open/infinite? * Only the outermost enclosure is infinite. */ char is_infinite; }; +/* We consider the geometrical normal Ng to a triangle V0 V1 V2 + * that verifies "(V0, V0V1, V0V2, Ng) is a direct system" + * (right-handed system). + * + * The user can set the convention used to determine which side of + * a triangle is to be considered front/back by using the flags : + * SENC_CONVENTION_NORMAL_FRONT => Ng points toward the front side, + * SENC_CONVENTION_NORMAL_BACK => Ng points toward the back side. + * + * Additionaly the user can set the convention used to output enclosures + * so that Ng points toward the enclosure or on the opposite direction + * (for a closed enclosure Ng points toward the inside or toward the outside) + * by using the flags : + * SENC_CONVENTION_NORMAL_INSIDE => Ng points toward the enclosure, + * SENC_CONVENTION_NORMAL_OUTSIDE => Ng points to the opposite of the enclosure. + * + * Note that normals in output data can be opposite to normals in input data + * (vertices are then given in reverse order). + * + * Also note that both sides of a triangle can be part of the same enclosure; + * in this case the 2 sides will be given with opposite Ng (meaning they + * will be given with opposite vertices order). + */ +enum senc_convention { + /* + * Convention regarding FRONT/BACK sides in input data + */ + + /* Geometrical normals point toward the front side */ + SENC_CONVENTION_NORMAL_FRONT = BIT(0), + /* Geometrical normals point toward the back side */ + SENC_CONVENTION_NORMAL_BACK = BIT(1), + + /* + * Convention regarding geometrical normals in output data + */ + + /* Geometrical normals point toward the enclosure */ + SENC_CONVENTION_NORMAL_INSIDE = BIT(2), + /* Geometrical normals point to the opposite of the enclosure */ + SENC_CONVENTION_NORMAL_OUTSIDE = BIT(3) +}; + BEGIN_DECLS /******************************************************************************* * StarEnclosures device. It is an handle toward the StarEnc library. * It manages the lib resources. + * If provided, the allocator has to be suitable for parallel high frequency + * allocations. As a consequence, a rsys proxy allocator should be avoided. ******************************************************************************/ SENC_API res_T senc_device_create (struct logger* logger, /* May be NULL <=> use default logger */ struct mem_allocator* allocator, /* May be NULL <=> use default allocator */ - const unsigned nthreads_hint, /* UNUSED */ + const unsigned nthreads_hint, const int verbose, struct senc_device** device); @@ -102,19 +147,23 @@ senc_device_ref_put SENC_API res_T senc_scene_create (struct senc_device* device, - const unsigned media_count, + const enum senc_convention convention, struct senc_scene** scene); /* 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) and are deduplicated. */ + * medium on both sides (or an error will be reported) and are deduplicated. + * When deduplicating triangles, the first occurence is kept (with it original + * global_id). */ SENC_API res_T senc_scene_add_geometry (struct senc_scene* scene, const unsigned triangles_count, void(*indices)(const unsigned itri, unsigned ids[3], void* context), void(*media)(const unsigned itri, unsigned med[2], void* context), + void(*global_id) /* May be NULL <=> use triangle rank */ + (const unsigned itri, unsigned* gid, void* context), const unsigned vertices_count, void(*position)(const unsigned ivert, double pos[3], void* context), void* context); @@ -125,6 +174,31 @@ senc_scene_analyze struct senc_descriptor** descriptor); SENC_API res_T +senc_scene_get_convention + (const struct senc_scene* scene, + enum senc_convention* convention); + +SENC_API res_T +senc_scene_get_triangles_count + (const struct senc_scene* scene, + unsigned* count); + +SENC_API res_T +senc_scene_get_unique_triangles_count + (const struct senc_scene* scene, + unsigned* count); + +SENC_API res_T +senc_scene_get_vertices_count + (const struct senc_scene* scene, + unsigned* count); + +SENC_API res_T +senc_scene_get_unique_vertices_count + (const struct senc_scene* scene, + unsigned* count); + +SENC_API res_T senc_scene_ref_get (struct senc_scene* scene); @@ -136,17 +210,35 @@ senc_scene_ref_put * StarEnclosures descriptor. It is an handle toward an analyze result. ******************************************************************************/ SENC_API res_T +senc_descriptor_get_max_medium + (const struct senc_descriptor* descriptor, + unsigned* max_medium_id); + +SENC_API res_T senc_descriptor_get_enclosure_count (const struct senc_descriptor* descriptor, unsigned* count); SENC_API res_T +senc_descriptor_get_enclosure_count_by_medium + (const struct senc_descriptor* descriptor, + const unsigned imed, /* Must be in [0 max_medium_id] */ + unsigned* count); + +SENC_API res_T senc_descriptor_get_enclosure (struct senc_descriptor* descriptor, const unsigned idx, struct senc_enclosure** enclosure); SENC_API res_T +senc_descriptor_get_enclosure_by_medium + (struct senc_descriptor* descriptor, + const unsigned imed, + const unsigned idx, + struct senc_enclosure** enclosure); + +SENC_API res_T senc_descriptor_get_global_triangles_count (const struct senc_descriptor* descriptor, unsigned* count); /* Number of unique triangles. */ @@ -181,6 +273,12 @@ senc_descriptor_get_global_triangle_enclosures unsigned enclosures[2]); SENC_API res_T +senc_descriptor_get_global_triangle_global_id + (const struct senc_descriptor* descriptor, + const unsigned itri, + unsigned* gid); + +SENC_API res_T senc_descriptor_ref_get (struct senc_descriptor* descriptor); @@ -196,13 +294,13 @@ senc_descriptor_ref_put * case the 2 occurences of the triangle have reversed vertices order and * unique_triangle_count and triangle_count differ. * By-index API accesses of triangles (or properties) visit unique triangles - * for indexes in the [0 unique_triangle_count[ and back-faces of the + * for indexes in the [0 unique_triangle_count[ range and back-faces of the * doubly-listed triangles in the [unique_triangle_count triangle_count[ range. ******************************************************************************/ SENC_API res_T senc_enclosure_get_header (const struct senc_enclosure* enclosure, - const struct enclosure_header** header); + struct senc_enclosure_header* header); SENC_API res_T senc_enclosure_get_triangle @@ -223,6 +321,18 @@ senc_enclosure_get_triangle_media unsigned medium[2]); SENC_API res_T +senc_enclosure_get_triangle_global_id + (const struct senc_enclosure* enclosure, + const unsigned itri, + unsigned* gid); + +SENC_API res_T +senc_enclosure_get_medium + (const struct senc_enclosure* enclosure, + const unsigned imed, + unsigned* medium); + +SENC_API res_T senc_enclosure_ref_get (struct senc_enclosure* enclosure); diff --git a/src/senc_descriptor.c b/src/senc_descriptor.c @@ -36,6 +36,7 @@ descriptor_release(ref_T * ref) scn = desc->scene; darray_triangle_enc_release(&desc->triangles_enc); darray_enclosure_release(&desc->enclosures); + darray_enc_ids_array_release(&desc->enc_ids_array_by_medium); MEM_RM(scn->dev->allocator, desc); SENC(scene_ref_put(scn)); @@ -48,21 +49,29 @@ struct senc_descriptor* descriptor_create(struct senc_scene* scn) { struct senc_descriptor* desc; + res_T res = RES_OK; ASSERT(scn); desc = MEM_CALLOC(scn->dev->allocator, 1, sizeof(struct senc_descriptor)); if(desc) { desc->scene = scn; SENC(scene_ref_get(desc->scene)); + ref_init(&desc->ref); darray_triangle_enc_init(scn->dev->allocator, &desc->triangles_enc); - /* Enclosure 0 is always defined for infinite */ darray_enclosure_init(scn->dev->allocator, &desc->enclosures); - darray_enclosure_resize(&desc->enclosures, 1); + darray_enc_ids_array_init(scn->dev->allocator, + &desc->enc_ids_array_by_medium); + OK(darray_enc_ids_array_resize(&desc->enc_ids_array_by_medium, scn->nmeds)); + /* Enclosure 0 is always defined for infinite */ + OK(darray_enclosure_resize(&desc->enclosures, 1)); desc->enclosures_count = 1; desc->triangle_count = scn->nutris; desc->vertices_count = scn->nuverts; - ref_init(&desc->ref); } +end: return desc; +error: + if(desc) SENC(descriptor_ref_put(desc)); + goto end; } struct mem_allocator* @@ -76,6 +85,16 @@ struct mem_allocator* * Exported functions ******************************************************************************/ res_T +senc_descriptor_get_max_medium + (const struct senc_descriptor* desc, unsigned* max_medium_id) +{ + if(!desc || !max_medium_id) return RES_BAD_ARG; + ASSERT(desc->scene->nmeds < UINT_MAX); /* API type */ + *max_medium_id = (unsigned)desc->scene->nmeds - 1; + return RES_OK; +} + +res_T senc_descriptor_get_enclosure_count (const struct senc_descriptor* desc, unsigned* count) { @@ -83,11 +102,31 @@ senc_descriptor_get_enclosure_count if(!desc || !count) return RES_BAD_ARG; tmp = darray_enclosure_size_get(&desc->enclosures); ASSERT(tmp < UINT_MAX); /* API type */ + ASSERT(desc->enclosures_count == tmp); *count = (unsigned)tmp; return RES_OK; } res_T +senc_descriptor_get_enclosure_count_by_medium + (const struct senc_descriptor* desc, + const unsigned imed, + unsigned* count) +{ + size_t tmp; + const struct darray_enc_id* enc_ids; + if(!desc || !count || imed >= desc->scene->nmeds) + return RES_BAD_ARG; + ASSERT(darray_enc_ids_array_size_get(&desc->enc_ids_array_by_medium) + == desc->scene->nmeds); + enc_ids = darray_enc_ids_array_cdata_get(&desc->enc_ids_array_by_medium) + imed; + tmp = darray_enc_id_size_get(enc_ids); + ASSERT(tmp < UINT_MAX); /* API type */ + *count = (unsigned)tmp; + return RES_OK; +} + +FINLINE res_T senc_descriptor_get_enclosure (struct senc_descriptor* desc, const unsigned idx, @@ -96,14 +135,30 @@ senc_descriptor_get_enclosure struct senc_enclosure* enc; if(!desc || idx >= darray_enclosure_size_get(&desc->enclosures) || !out_enc) return RES_BAD_ARG; - enc = - enclosure_create(desc, darray_enclosure_data_get(&desc->enclosures) + idx); + enc = enclosure_create(desc, idx); if(!enc) return RES_MEM_ERR; *out_enc = enc; return RES_OK; } res_T +senc_descriptor_get_enclosure_by_medium + (struct senc_descriptor* desc, + const unsigned imed, + const unsigned idx, + struct senc_enclosure** out_enc) +{ + const struct darray_enc_id* enc_ids; + unsigned index; + if(!desc || imed >= desc->scene->nmeds || !out_enc) return RES_BAD_ARG; + enc_ids = + darray_enc_ids_array_cdata_get(&desc->enc_ids_array_by_medium) + imed; + if(idx >= darray_enc_id_size_get(enc_ids)) return RES_BAD_ARG; + index = darray_enc_id_cdata_get(enc_ids)[idx]; + return senc_descriptor_get_enclosure(desc, index, out_enc); +} + +res_T senc_descriptor_get_global_triangles_count (const struct senc_descriptor* desc, unsigned* count) @@ -175,7 +230,9 @@ senc_descriptor_get_global_triangle_media return RES_BAD_ARG; trg = darray_triangle_in_cdata_get(&desc->scene->triangles_in) + itri; FOR_EACH(i, 0, 2) { +#if (UINT_MAX < MEDIUM_MAX__) ASSERT(trg->medium[i] < UINT_MAX); +#endif media[i] = (unsigned)trg->medium[i]; /* Back to API type */ } return RES_OK; @@ -194,13 +251,30 @@ senc_descriptor_get_global_triangle_enclosures return RES_BAD_ARG; trg = darray_triangle_enc_cdata_get(&desc->triangles_enc) + itri; FOR_EACH(i, 0, 2) { +#if (UINT_MAX < ENCLOSURE_MAX__) ASSERT(trg->enclosure[i] < UINT_MAX); +#endif enclosures[i] = (unsigned)trg->enclosure[i]; /* Back to API type */ } return RES_OK; } res_T +senc_descriptor_get_global_triangle_global_id + (const struct senc_descriptor* desc, + const unsigned itri, + unsigned* gid) +{ + const struct triangle_in* trg; + if(!gid || !desc + || itri >= darray_triangle_in_size_get(&desc->scene->triangles_in)) + return RES_BAD_ARG; + trg = darray_triangle_in_cdata_get(&desc->scene->triangles_in) + itri; + *gid = trg->global_id; + return RES_OK; +} + +res_T senc_descriptor_ref_get(struct senc_descriptor* desc) { if(!desc) return RES_BAD_ARG; diff --git a/src/senc_descriptor_c.h b/src/senc_descriptor_c.h @@ -31,7 +31,6 @@ struct triangle_comp { component_id_t component[2]; }; -#ifndef NDEBUG static void triangle_comp_init(struct mem_allocator* alloc, struct triangle_comp* trg) { int i; @@ -39,15 +38,14 @@ triangle_comp_init(struct mem_allocator* alloc, struct triangle_comp* trg) { ASSERT(trg); FOR_EACH(i, 0, 2) trg->component[i] = COMPONENT_NULL__; } -#define DARRAY_FUNCTOR_INIT triangle_comp_init -#endif #define DARRAY_NAME triangle_comp #define DARRAY_DATA struct triangle_comp +#define DARRAY_FUNCTOR_INIT triangle_comp_init #include <rsys/dynamic_array.h> struct triangle_enc { - /* The connex component in which each side is. */ + /* The enclosure in which each side is. */ enclosure_id_t enclosure[2]; }; @@ -55,7 +53,7 @@ struct triangle_enc { static void triangle_enc_init(struct mem_allocator* alloc, struct triangle_enc* trg) { int i; - (void) alloc; + (void)alloc; ASSERT(trg); FOR_EACH(i, 0, 2) trg->enclosure[i] = ENCLOSURE_NULL__; } @@ -74,6 +72,18 @@ triangle_enc_init(struct mem_allocator* alloc, struct triangle_enc* trg) { #define DARRAY_FUNCTOR_COPY_AND_RELEASE enclosure_data_copy_and_release #include <rsys/dynamic_array.h> +#define DARRAY_NAME enc_id +#define DARRAY_DATA enclosure_id_t +#include <rsys/dynamic_array.h> + +#define DARRAY_NAME enc_ids_array +#define DARRAY_DATA struct darray_enc_id +#define DARRAY_FUNCTOR_INIT darray_enc_id_init +#define DARRAY_FUNCTOR_COPY darray_enc_id_copy +#define DARRAY_FUNCTOR_RELEASE darray_enc_id_release +#define DARRAY_FUNCTOR_COPY_AND_RELEASE darray_enc_id_copy_and_release +#include <rsys/dynamic_array.h> + struct senc_descriptor { struct senc_scene* scene; enclosure_id_t enclosures_count; @@ -81,6 +91,7 @@ struct senc_descriptor { struct darray_triangle_enc triangles_enc; /* Store enclosures */ struct darray_enclosure enclosures; + struct darray_enc_ids_array enc_ids_array_by_medium; trg_id_t triangle_count; vrtx_id_t vertices_count; diff --git a/src/senc_device.c b/src/senc_device.c @@ -19,6 +19,8 @@ #include <rsys/logger.h> #include <rsys/mem_allocator.h> +#include <omp.h> + /******************************************************************************* * Helper functions ******************************************************************************/ @@ -104,6 +106,8 @@ senc_device_create dev->logger = log; dev->allocator = allocator; dev->verbose = verbose; + /* Cannot use int args for MMIN here as default is -1 */ + dev->nthreads = (int)MMIN(nthreads_hint, (unsigned)omp_get_num_procs()); ref_init(&dev->ref); exit: diff --git a/src/senc_device_c.h b/src/senc_device_c.h @@ -19,10 +19,6 @@ #include <rsys/free_list.h> #include <rsys/ref_count.h> -#define OK(expr)\ - res = expr;\ - if(res != RES_OK) goto error; - struct name { FITEM; }; #define FITEM_TYPE name #include <rsys/free_list.h> @@ -31,6 +27,7 @@ struct senc_device { struct logger* logger; struct mem_allocator* allocator; int verbose; + int nthreads; ref_T ref; }; diff --git a/src/senc_enclosure.c b/src/senc_enclosure.c @@ -45,13 +45,15 @@ enclosure_release(ref_T * ref) struct senc_enclosure* enclosure_create (struct senc_descriptor* desc, - const struct enclosure_data* data) + const unsigned idx) { struct senc_enclosure* enc; - ASSERT(desc); + ASSERT(desc && idx < darray_enclosure_size_get(&desc->enclosures)); enc = MEM_CALLOC(descriptor_get_allocator(desc), 1, sizeof(struct senc_enclosure)); if(enc) { + const struct enclosure_data* data + = darray_enclosure_data_get(&desc->enclosures) + idx; SENC(descriptor_ref_get(desc)); enc->desc = desc; enc->data = data; @@ -66,10 +68,10 @@ enclosure_create res_T senc_enclosure_get_header (const struct senc_enclosure* enclosure, - const struct enclosure_header** header) + struct senc_enclosure_header* header) { if(!enclosure || !header) return RES_BAD_ARG; - *header = &enclosure->data->header; + *header = enclosure->data->header; return RES_OK; } @@ -100,15 +102,19 @@ senc_enclosure_get_vertex const unsigned ivert, double coord[3]) { - const union double3* positions; if(!enclosure || !coord - || ivert >= enclosure->data->header.vertices_count) + || ivert >= enclosure->data->header.vertices_count) { return RES_BAD_ARG; - ASSERT(darray_position_size_get(&enclosure->data->vertices) - == enclosure->data->header.vertices_count); - positions = darray_position_cdata_get(&enclosure->data->vertices); - d3_set(coord, positions[ivert].vec); - return RES_OK; + } else { + const vrtx_id_t idx + = darray_vrtx_id_cdata_get(&enclosure->data->vertices)[ivert]; + const union double3* positions + = darray_position_cdata_get(&enclosure->desc->scene->vertices); + ASSERT(darray_vrtx_id_size_get(&enclosure->data->vertices) + == enclosure->data->header.vertices_count); + d3_set(coord, positions[idx].vec); + return RES_OK; + } } res_T @@ -126,13 +132,47 @@ senc_enclosure_get_triangle_media == enclosure->data->header.triangle_count); triangle = darray_triangle_in_cdata_get(&enclosure->data->sides) + itri; FOR_EACH(i, 0, 2) { +#if (UINT_MAX < MEDIUM_MAX__) ASSERT(triangle->medium[i] < UINT_MAX); +#endif medium[i] = (unsigned)triangle->medium[i]; /* Back to API type */ } return RES_OK; } res_T +senc_enclosure_get_triangle_global_id + (const struct senc_enclosure* enclosure, + const unsigned itri, + unsigned* gid) +{ + const struct triangle_in* triangle; + if(!enclosure || !gid + || itri >= enclosure->data->header.triangle_count) + return RES_BAD_ARG; + ASSERT(darray_triangle_in_size_get(&enclosure->data->sides) + == enclosure->data->header.triangle_count); + triangle = darray_triangle_in_cdata_get(&enclosure->data->sides) + itri; + *gid = triangle->global_id; + return RES_OK; +} + +res_T +senc_enclosure_get_medium + (const struct senc_enclosure* enclosure, + const unsigned imed, + unsigned* medium) +{ + if(!enclosure || !medium + || imed >= enclosure->data->header.enclosed_media_count) + return RES_BAD_ARG; + ASSERT(enclosure->data->header.enclosed_media_count + == darray_media_size_get(&enclosure->data->enclosed_media)); + *medium = darray_media_cdata_get(&enclosure->data->enclosed_media)[imed]; + return RES_OK; +} + +res_T senc_enclosure_ref_get(struct senc_enclosure* enc) { if(!enc) return RES_BAD_ARG; diff --git a/src/senc_enclosure_c.h b/src/senc_enclosure_c.h @@ -32,6 +32,6 @@ struct senc_enclosure { struct senc_enclosure* enclosure_create (struct senc_descriptor* desc, - const struct enclosure_data* data); + const unsigned idx); #endif /* SENC_ENCLOSURE_C_H */ diff --git a/src/senc_enclosure_data.h b/src/senc_enclosure_data.h @@ -18,68 +18,178 @@ #include <rsys/rsys.h> #include <rsys/ref_count.h> +#include <rsys/hash_table.h> +#include <rsys/dynamic_array.h> + +/* unsigned char array with init to zero */ +static FINLINE void +zero_init_uchar + (struct mem_allocator* alloc, unsigned char* data) +{ + ASSERT(data); (void) alloc; + *data = 0; +} +#define DARRAY_FUNCTOR_INIT zero_init_uchar +#include <rsys/dynamic_array_uchar.h> #include "senc.h" #include "senc_scene_c.h" +#include "senc_internal_types.h" + +#include <limits.h> static void -init_header(struct enclosure_header* header) +init_header(struct senc_enclosure_header* header) { ASSERT(header); header->enclosure_id = ENCLOSURE_NULL__; header->triangle_count = 0; header->unique_triangle_count = 0; header->vertices_count = 0; - header->enclosed_medium = MEDIUM_NULL__; + header->enclosed_media_count = 0; header->is_infinite = CHAR_MAX; } +#define DARRAY_NAME media +#define DARRAY_DATA medium_id_t +#include <rsys/dynamic_array.h> + +static FINLINE res_T +bool_array_of_media_merge + (struct darray_uchar* dst, + const unsigned char* src, + const medium_id_t sz) +{ + res_T res = RES_OK; + medium_id_t i; + unsigned char* data_dst; + + ASSERT(src && dst); + + OK(darray_uchar_resize(dst, sz)); + data_dst = darray_uchar_data_get(dst); + ASSERT(sz <= MEDIUM_MAX__); + if(res != RES_OK) goto error; + FOR_EACH(i, 0, sz) { + if(!src[i]) continue; + data_dst[i] = 1; + } +end: + return res; +error: + goto end; +} + +static FINLINE res_T +bool_array_of_media_to_darray_media + (struct darray_media* dst, + struct darray_uchar* src) +{ + res_T res = RES_OK; + medium_id_t i; + size_t sz; + const unsigned char* data; + + ASSERT(src && dst); + + data = darray_uchar_cdata_get(src); + sz = darray_uchar_size_get(src); + ASSERT(sz <= MEDIUM_MAX__); + darray_media_clear(dst); + if(res != RES_OK) goto error; + FOR_EACH(i, 0, (medium_id_t) sz) { + if(!data[i]) continue; + res = darray_media_push_back(dst, &i); + if(res != RES_OK) goto error; + } +end: + return res; +error: + goto end; +} + struct enclosure_data { - struct enclosure_header header; + struct senc_enclosure_header header; /* Same triangle can appear twice if both sides */ struct darray_triangle_in sides; - struct darray_position vertices; + /* Index of vertices in scene's unique vertices */ + struct darray_vrtx_id vertices; + /* List of the enclosed media */ + struct darray_uchar tmp_enclosed_media; + struct darray_media enclosed_media; + /* Number of components involved in this enclosure */ + component_id_t cc_count; + /* Linked list of the components */ + component_id_t first_component; + /* Range of triangles member of the enclosure */ + struct side_range side_range; + /* Counts */ + side_id_t side_count; }; static FINLINE void enclosure_data_init(struct mem_allocator* alloc, struct enclosure_data* enc) { ASSERT(enc); init_header(&enc->header); + enc->cc_count = 0; + enc->first_component = COMPONENT_NULL__; + enc->side_range.first = SIDE_NULL__; + enc->side_range.last = 0; + enc->side_count = 0; darray_triangle_in_init(alloc, &enc->sides); - darray_position_init(alloc, &enc->vertices); + darray_vrtx_id_init(alloc, &enc->vertices); + darray_uchar_init(alloc, &enc->tmp_enclosed_media); + darray_media_init(alloc, &enc->enclosed_media); } static FINLINE res_T enclosure_data_copy -(struct enclosure_data* dst, - const struct enclosure_data* src) + (struct enclosure_data* dst, + const struct enclosure_data* src) { res_T res = RES_OK; ASSERT(src && dst); dst->header = src->header; - res = darray_triangle_in_copy(&dst->sides, &src->sides); - if(res != RES_OK) return res; - return darray_position_copy(&dst->vertices, &src->vertices); + dst->cc_count = src->cc_count; + dst->first_component = src->first_component; + dst->side_range = src->side_range; + dst->side_count = src->side_count; + OK(darray_triangle_in_copy(&dst->sides, &src->sides)); + OK(darray_vrtx_id_copy(&dst->vertices, &src->vertices)); + OK(darray_uchar_copy(&dst->tmp_enclosed_media, &src->tmp_enclosed_media)); + OK(darray_media_copy(&dst->enclosed_media, &src->enclosed_media)); +error: + return res; } static FINLINE void enclosure_data_release(struct enclosure_data* n) { ASSERT(n); darray_triangle_in_release(&n->sides); - darray_position_release(&n->vertices); + darray_vrtx_id_release(&n->vertices); + darray_uchar_release(&n->tmp_enclosed_media); + darray_media_release(&n->enclosed_media); } static FINLINE res_T enclosure_data_copy_and_release -(struct enclosure_data* dst, - struct enclosure_data* src) + (struct enclosure_data* dst, + struct enclosure_data* src) { res_T res = RES_OK; ASSERT(src && dst); dst->header = src->header; - res = darray_triangle_in_copy_and_release(&dst->sides, &src->sides); - if(res != RES_OK) return res; - return darray_position_copy_and_release(&dst->vertices, &src->vertices); + dst->cc_count = src->cc_count; + dst->first_component = src->first_component; + dst->side_range = src->side_range; + dst->side_count = src->side_count; + OK(darray_triangle_in_copy_and_release(&dst->sides, &src->sides)); + OK(darray_vrtx_id_copy_and_release(&dst->vertices, &src->vertices)); + OK(darray_uchar_copy_and_release(&dst->tmp_enclosed_media, + &src->tmp_enclosed_media)); + OK(darray_media_copy_and_release(&dst->enclosed_media, &src->enclosed_media)); +error: + return res; } #endif /* SENC_ENCLOSURE_DATA_H */ diff --git a/src/senc_internal_types.h b/src/senc_internal_types.h @@ -20,6 +20,29 @@ #include <stdint.h> +/* Utility macros */ +#ifdef NDEBUG +#define OK2(Expr)\ + if((tmp_res = (Expr)) != RES_OK) goto tmp_error; + +#define OK(Expr)\ + if((res = (Expr)) != RES_OK) goto error; +#else +#define OK2(Expr)\ + if((tmp_res = (Expr)) != RES_OK) {\ + fprintf(stderr, "%s: error code set to %d at line %d\n", FUNC_NAME,\ + tmp_res, __LINE__);\ + goto tmp_error;\ + } + +#define OK(Expr)\ + if((res = (Expr)) != RES_OK) {\ + fprintf(stderr, "%s: error code set to %d at line %d\n", FUNC_NAME,\ + res, __LINE__);\ + goto error;\ + } +#endif + /* Side IDs are uint32_t */ typedef uint32_t side_id_t; #define SIDE_MAX__ (UINT32_MAX-1) @@ -44,21 +67,73 @@ typedef vrtx_id_t edge_id_t; #define EDGE_MAX__ VRTX_MAX__ #define EDGE_NULL__ VRTX_NULL__ -/* Medium IDs are internally uint16_t */ +/* Medium IDs are internally uint32_t */ /* Should nnot be larger than unsigned, as the API uses it. */ -typedef uint16_t medium_id_t; -#define MEDIUM_MAX__ (UINT16_MAX-1) -#define MEDIUM_NULL__ UINT16_MAX +typedef uint32_t medium_id_t; +#define MEDIUM_MAX__ INT32_MAX +#define MEDIUM_NULL__ UINT32_MAX -/* Enclosure IDs are internally uint16_t */ +/* Enclosure IDs are internally uint32_t */ /* Cannot be larger than unsigned, as the API uses it. */ -typedef uint16_t enclosure_id_t; -#define ENCLOSURE_MAX__ (UINT16_MAX-1) -#define ENCLOSURE_NULL__ UINT16_MAX +typedef uint32_t enclosure_id_t; +#define ENCLOSURE_MAX__ UINT32_MAX +#define ENCLOSURE_NULL__ UINT32_MAX /* Component IDs use the same type than enclosure IDs */ typedef enclosure_id_t component_id_t; -#define COMPONENT_MAX__ ENCLOSURE_MAX__ -#define COMPONENT_NULL__ ENCLOSURE_NULL__ +#define COMPONENT_MAX__ (UINT32_MAX - 2) /* To allow special values */ +#define COMPONENT_NULL__ UINT32_MAX +/* Special values */ +#define CC_GROUP_ROOT_NONE UINT32_MAX +#define CC_GROUP_ROOT_INFINITE (UINT32_MAX - 1) +#define CC_GROUP_ID_NONE UINT32_MAX +#define CC_ID_NONE UINT32_MAX + +/* This one is used as flag */ +enum side_flag { + FLAG_FRONT = BIT(0), + FLAG_BACK = BIT(1) +}; + +/* This one is used as an index to arrays */ +enum side_id { + SIDE_FRONT = 0, + SIDE_BACK = 1 +}; + +/* Utility macros */ +static FINLINE trg_id_t +TRGSIDE_2_TRG(side_id_t s) { + ASSERT(((size_t)s >> 1) <= TRG_MAX__); + return s >> 1; +} + +static FINLINE int +TRGSIDE_IS_FRONT(side_id_t s) { + return (s & 1) == 0; +} + +static FINLINE enum side_id +TRGSIDE_2_SIDE(side_id_t s) { + return (s & 1) ? SIDE_BACK : SIDE_FRONT; +} + +static FINLINE enum side_flag +TRGSIDE_2_SIDEFLAG(side_id_t s) { + return (s & 1) ? FLAG_BACK : FLAG_FRONT; +} + +static FINLINE side_id_t +TRGIDxSIDE_2_TRGSIDE(trg_id_t t, enum side_id i) { + ASSERT((((size_t)t << 1) | (i == SIDE_BACK)) < SIDE_MAX__); + ASSERT(i == SIDE_FRONT || i == SIDE_BACK); + return (side_id_t)((t << 1) | (i == SIDE_BACK)); +} + +static FINLINE side_id_t +TRGSIDE_OPPOSITE(side_id_t s) { + return TRGIDxSIDE_2_TRGSIDE(TRGSIDE_2_TRG(s), + TRGSIDE_IS_FRONT(s) ? SIDE_BACK : SIDE_FRONT); +} #endif /* SENC_INTERNAL_TYPES_H */ diff --git a/src/senc_s3d_wrapper.h b/src/senc_s3d_wrapper.h @@ -21,7 +21,7 @@ #include <rsys/rsys.h> #include <rsys/float3.h> -void FINLINE +FINLINE void senc_descriptor_get_global_indices__ (const unsigned itri, unsigned indices[3], @@ -31,10 +31,10 @@ senc_descriptor_get_global_indices__ res_T r; ASSERT(indices && ctx); r = senc_descriptor_get_global_triangle(descriptor, itri, indices); - ASSERT(r == RES_OK); + ASSERT(r == RES_OK); (void)r; } -void FINLINE +static FINLINE void senc_descriptor_get_global_vertices__ (const unsigned ivert, float coord[3], @@ -45,11 +45,11 @@ senc_descriptor_get_global_vertices__ res_T r; ASSERT(coord && ctx); r = senc_descriptor_get_global_vertex(descriptor, ivert, tmp); - ASSERT(r == RES_OK); + ASSERT(r == RES_OK); (void)r; f3_set_d3(coord, tmp); } -void FINLINE +FINLINE void senc_enclosure_get_triangle__ (const unsigned itri, unsigned indices[3], @@ -59,10 +59,10 @@ senc_enclosure_get_triangle__ res_T r; ASSERT(indices && ctx); r = senc_enclosure_get_triangle(enclosure, itri, indices); - ASSERT(r == RES_OK); + ASSERT(r == RES_OK); (void)r; } -void FINLINE +static FINLINE void senc_enclosure_get_vertex__ (const unsigned ivert, float coord[3], @@ -73,7 +73,7 @@ senc_enclosure_get_vertex__ res_T r; ASSERT(coord && ctx); r = senc_enclosure_get_vertex(enclosure, ivert, tmp); - ASSERT(r == RES_OK); + ASSERT(r == RES_OK); (void)r; f3_set_d3(coord, tmp); } diff --git a/src/senc_scene.c b/src/senc_scene.c @@ -37,6 +37,7 @@ scene_release(ref_T * ref) darray_position_release(&scn->vertices); htable_vrtx_release(&scn->unique_vertices); htable_trg_release(&scn->unique_triangles); + darray_side_range_release(&scn->media_use); MEM_RM(dev->allocator, scn); SENC(device_ref_put(dev)); } @@ -47,13 +48,16 @@ scene_release(ref_T * ref) res_T senc_scene_create (struct senc_device* dev, - const unsigned nmeds, + const enum senc_convention conv, struct senc_scene** out_scn) { struct senc_scene* scn = NULL; res_T res = RES_OK; - if(!dev || !out_scn || !nmeds || nmeds > MEDIUM_MAX__) + if(!dev || !out_scn + /* Convention must be set both regarding FRONT/BACK and INSIDE/OUTSIDE */ + || !(conv & (SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_BACK)) + || !(conv & (SENC_CONVENTION_NORMAL_INSIDE | SENC_CONVENTION_NORMAL_OUTSIDE))) return RES_BAD_ARG; scn = MEM_CALLOC(dev->allocator, 1, sizeof(struct senc_scene)); @@ -65,16 +69,18 @@ senc_scene_create ref_init(&scn->ref); SENC(device_ref_get(dev)); scn->dev = dev; + scn->convention = conv; scn->ngeoms = 0; scn->ntris = 0; scn->nutris = 0; - scn->nmeds = (medium_id_t)nmeds; + scn->nmeds = 0; scn->nverts = 0; scn->nuverts = 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); htable_trg_init(dev->allocator, &scn->unique_triangles); + darray_side_range_init(dev->allocator, &scn->media_use); exit: if(scn) *out_scn = scn; @@ -93,6 +99,7 @@ senc_scene_add_geometry 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), const unsigned nverts, void(*position)(const unsigned ivert, double pos[3], void* ctx), void* ctx) @@ -132,17 +139,20 @@ 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 */ unique_v = scn->nuverts + actual_nuverts; - res = darray_position_push_back(&scn->vertices, &tmp); - if(res != RES_OK) goto error; + OK(darray_position_push_back(&scn->vertices, &tmp)); ASSERT(unique_v == htable_vrtx_size_get(&scn->unique_vertices)); OK(htable_vrtx_set(&scn->unique_vertices, &tmp, &unique_v)); ++actual_nuverts; } - /* The unique ID for vertex v is u */ + /* The unique ID for vertex i is unique_v */ ASSERT(i == darray_vrtx_id_size_get(&unique_vertice_ids)); OK(darray_vrtx_id_push_back(&unique_vertice_ids, &unique_v)); ++actual_nverts; @@ -155,8 +165,12 @@ senc_scene_add_geometry union vrtx_id3 trg_key; struct triangle_in tmp; trg_id_t* p_trg; - trg_id_t tr; char reversed; + if(global_id) { + global_id(i, &tmp.global_id, ctx); + } else { + tmp.global_id = (unsigned)(scn->ntris + i); + } indices(i, ind, ctx); /* API: indices needs an unsigned */ FOR_EACH(j, 0, 3) { if(ind[j] >= nverts) { @@ -173,7 +187,7 @@ senc_scene_add_geometry const union double3* positions = darray_position_cdata_get(&scn->vertices); log_err(scn->dev, "%s: triangle %lu is degenerate.\n", - FUNC_NAME, (unsigned long)i); + FUNC_NAME, (unsigned long)tmp.global_id); log_err(scn->dev, " (%g %g %g)\n (%g %g %g)\n (%g %g %g)\n", SPLIT3(positions[trg[i].vertice_id[0]].vec), SPLIT3(positions[trg[i].vertice_id[1]].vec), @@ -183,17 +197,11 @@ senc_scene_add_geometry } /* Get media */ media(i, med, ctx); /* API: media needs an unsigned */ - ASSERT(scn->nmeds <= MEDIUM_MAX__); FOR_EACH(j, 0, 2) { if(med[j] >= scn->nmeds) { - log_err(scn->dev, - "%s: triangle %lu %s side references invalid medium: %lu.\n", - FUNC_NAME, - (unsigned long)i, - (j ? "back" : "front"), - (unsigned long)med[j]); - res = RES_BAD_ARG; - goto error; + ASSERT(med[j] <= MEDIUM_MAX__); + scn->nmeds = med[j] + 1; + darray_side_range_resize(&scn->media_use, scn->nmeds); } tmp.medium[j] = (medium_id_t)med[j]; } @@ -213,35 +221,52 @@ senc_scene_add_geometry /* 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 with incoherent media.\n", - FUNC_NAME, (unsigned long)*p_trg); - log_err(scn->dev, "Triangle %lu:\n (%g %g %g)\n (%g %g %g)\n (%g %g %g)\n", - (unsigned long)*p_trg, + 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", - umed[ureversed? 1 : 0], umed[ureversed ? 0 : 1], - med[reversed ? 1 : 0], med[reversed ? 0 : 1]); + (unsigned long)umed[ureversed? 1 : 0], + (unsigned long)umed[ureversed ? 0 : 1], + (unsigned long)med[reversed ? 1 : 0], + (unsigned long)med[reversed ? 0 : 1]); res = RES_BAD_ARG; 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); if(!same) { FOR_EACH(j, 0, 2) { - tmp.medium[j] = (medium_id_t) med[1-j]; + tmp.medium[j] = (medium_id_t)med[1-j]; } } } - } - else { + } 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)); + FOR_EACH(j, 0, 2) { + 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)); + ASSERT(media_use->first < 2 * (scn->nutris + actual_nutris + 1)); + media_use->last = MMAX(media_use->last, TRGIDxSIDE_2_TRGSIDE(u, j)); + ASSERT(media_use->last < 2 * (scn->nutris + actual_nutris + 1)); + ASSERT(media_use->first <= media_use->last); + } ++actual_nutris; } - tr = scn->nutris + i; ++actual_ntris; } @@ -261,6 +286,57 @@ error: } res_T +senc_scene_get_convention + (const struct senc_scene* scn, + enum senc_convention* convention) +{ + if(!scn || !convention) return RES_BAD_ARG; + *convention = scn->convention; + return RES_OK; + +} + +res_T +senc_scene_get_triangles_count + (const struct senc_scene* scn, + unsigned* count) +{ + if(!scn || !count) return RES_BAD_ARG; + *count = scn->ntris; + return RES_OK; +} + +res_T +senc_scene_get_unique_triangles_count + (const struct senc_scene* scn, + unsigned* count) +{ + if(!scn || !count) return RES_BAD_ARG; + *count = scn->nutris; + return RES_OK; +} + +res_T +senc_scene_get_vertices_count + (const struct senc_scene* scn, + unsigned* count) +{ + if(!scn || !count) return RES_BAD_ARG; + *count = scn->nverts; + return RES_OK; +} + +res_T +senc_scene_get_unique_vertices_count + (const struct senc_scene* scn, + unsigned* count) +{ + if(!scn || !count) return RES_BAD_ARG; + *count = scn->nuverts; + return RES_OK; +} + +res_T senc_scene_ref_get(struct senc_scene* scn) { if(!scn) return RES_BAD_ARG; diff --git a/src/senc_scene_analyze.c b/src/senc_scene_analyze.c @@ -26,82 +26,36 @@ #include <rsys/mem_allocator.h> #include <rsys/hash_table.h> #include <rsys/dynamic_array.h> -#include <rsys/dynamic_array_uint.h> +#include <rsys/dynamic_array_uchar.h> #include <star/s3d.h> +#include <omp.h> #include <limits.h> #include <stdlib.h> #define CC_DESCRIPTOR_NULL__ {\ - {0,0,-DBL_MAX}, -1, SIDE_NULL__, VRTX_NULL__, 0, MEDIUM_NULL__,\ - CC_ID_NONE, CC_GROUP_ROOT_NONE, CC_GROUP_ID_NONE\ + CHAR_MAX, VRTX_NULL__, 0,\ + CC_ID_NONE, CC_GROUP_ROOT_NONE, ENCLOSURE_NULL__,\ + { TRG_NULL__, 0},\ + NULL\ } +#ifdef COMPILER_GCC + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif const struct cc_descriptor CC_DESCRIPTOR_NULL = CC_DESCRIPTOR_NULL__; +#ifdef COMPILER_GCC + #pragma GCC diagnostic pop +#endif #define DARRAY_NAME component_id #define DARRAY_DATA component_id_t #include <rsys/dynamic_array.h> -#define DARRAY_NAME side_id -#define DARRAY_DATA side_id_t -#include <rsys/dynamic_array.h> - -#define HTABLE_NAME vrtx_id -#define HTABLE_KEY vrtx_id_t -#define HTABLE_DATA vrtx_id_t -#include <rsys/hash_table.h> - /******************************************************************************* * Helper function ******************************************************************************/ -static void -dumplist - (const struct trgside* trgsides, - const struct darray_side_id* side_ids, - const enum list_id list_id) -{ - side_id_t i; - size_t tmp; - (void)list_id; - ASSERT(trgsides && side_ids); - printf("\n"); - tmp = darray_side_id_size_get(side_ids); - ASSERT(tmp <= SIDE_MAX__); - FOR_EACH(i, 0, (side_id_t)tmp) { - const side_id_t id = darray_side_id_cdata_get(side_ids)[i]; - const struct trgside* const side = trgsides + id; - printf("Side %lu (%lu %lu %lu)\n", - (unsigned long)id, - (unsigned long)side->facing_side_id[0], - (unsigned long)side->facing_side_id[1], - (unsigned long)side->facing_side_id[2]); - ASSERT(side->list_id & list_id); - } -} - -static int -find_side_in_list - (const struct trgside* trgsides, - const struct darray_side_id* side_ids, - const side_id_t side_id, - const enum list_id list_id) -{ - side_id_t i; - size_t tmp; - (void)list_id; - (void)trgsides; - ASSERT(trgsides && side_ids); - tmp = darray_side_id_size_get(side_ids); - ASSERT(tmp <= SIDE_MAX__); - FOR_EACH(i, 0, (side_id_t)tmp) { - const side_id_t id = darray_side_id_cdata_get(side_ids)[i]; - ASSERT(trgsides[id].list_id & list_id); - if(id == side_id) return 1; - } - return 0; -} - static INLINE int neighbour_cmp(const void* w1, const void* w2) { @@ -110,345 +64,29 @@ neighbour_cmp(const void* w1, const void* w2) return (a1 > a2) - (a1 < a2); } -static void -find_component_Zmax - (struct senc_scene* scn, - struct darray_triangle_tmp* triangles_tmp_array, - struct cc_descriptor* cc) -{ - trg_id_t trid; - double edge0[3], edge1[3], normal[3], norm, side_nz; - struct triangle_in* triangles_in; - struct triangle_tmp* triangles_tmp; - const union double3* vertices; - const char* side_membership; - ASSERT(scn && triangles_tmp_array && cc); - - vertices = darray_position_cdata_get(&scn->vertices); - triangles_in = darray_triangle_in_data_get(&scn->triangles_in); - triangles_tmp = darray_triangle_tmp_data_get(triangles_tmp_array); - side_membership = darray_char_cdata_get(&cc->side_membership); - - /* Build the sorted list of side ids */ - ASSERT(darray_triangle_tmp_size_get(triangles_tmp_array) - == darray_triangle_in_size_get(&scn->triangles_in)); - ASSERT(scn->nutris - == darray_triangle_in_size_get(&scn->triangles_in)); - FOR_EACH(trid, 0, scn->nutris) { - const char member = side_membership[trid]; - struct triangle_in* const trg_in = triangles_in + trid; - struct triangle_tmp* const trg_tmp = triangles_tmp + trid; - enum side_id side; - int change = 0; - - if(!member) continue; - - /* Keep track of the appropriate vertex/side of the connex component - * in order to cast a ray at the component grouping step of the algorithm. - * The most appropriate vertex is (the) one with the greater Z coordinate. - * If more than one vertex/side has the same Z, we want the side that most - * faces Z (that is the one with the greater nz). - * This is mandatory to select the correct side when both sides of a triangle - * are candidate. */ - if(cc->max_vrtx[2] > trg_tmp->max_z) - continue; - - d3_sub(edge0, vertices[trg_in->vertice_id[1]].vec, - vertices[trg_in->vertice_id[0]].vec); - d3_sub(edge1, vertices[trg_in->vertice_id[2]].vec, - vertices[trg_in->vertice_id[0]].vec); - d3_cross(normal, edge0, edge1); - norm = d3_normalize(normal, normal); - ASSERT(norm); - - if((member & FLAG_FRONT) && (member & FLAG_BACK)) { - /* Select the side with nz>0 */ - side_nz = fabs(normal[2]); - side = (normal[2] > 0) ? SIDE_FRONT : SIDE_BACK; - } else if(member & FLAG_FRONT) { - side_nz = normal[2]; - side = SIDE_FRONT; - } else { - ASSERT(member & FLAG_BACK); - side_nz = -normal[2]; - side = SIDE_BACK; - } - - if(cc->max_vrtx[2] < trg_tmp->max_z) { - change = 1; /* Try first to improve z */ - } - else if(cc->max_z_nz <= 0 && fabs(cc->max_z_nz) < fabs(side_nz)) { - change = 1; /* If nz <= 0, the more negative the better */ - } - else if(cc->max_z_nz > 0 && cc->max_z_nz < side_nz) { - change = 1; /* If nz > 0, the more positive the better */ - } - if(change) { - cc->max_z_nz = side_nz; - cc->max_z_side_id = TRGIDxSIDE_2_TRGSIDE(trid, side); - ASSERT(trg_tmp->max_z_vrtx_id < 3); - ASSERT(trg_in->vertice_id[trg_tmp->max_z_vrtx_id] < scn->nverts); - cc->max_z_vrtx_id = trg_in->vertice_id[trg_tmp->max_z_vrtx_id]; - ASSERT(trg_tmp->max_z == vertices[cc->max_z_vrtx_id].pos.z); - d3_set(cc->max_vrtx, vertices[cc->max_z_vrtx_id].vec); - } - } -} - -static FINLINE void -add_side_to_stack - (struct senc_scene* scn, - struct darray_side_id* stack, - struct trgside* trgsides, - const side_id_t side_id) -{ - (void)scn; - ASSERT(scn && trgsides && stack - && side_id < SIDE_MAX__ && side_id < 2 * scn->nutris); - ASSERT(!find_side_in_list(trgsides, stack, side_id, FLAG_WAITING_STACK)); - darray_side_id_push_back(stack, &side_id); - trgsides[side_id].list_id = FLAG_WAITING_STACK; -} - -static FINLINE void -add_side_to_medium_list - (struct senc_scene* scn, - struct darray_side_id* side_ids_by_medium, - struct trgside* trgsides, - const side_id_t side_id) -{ - (void)scn; - ASSERT(scn && side_ids_by_medium && trgsides - && side_id < 2 * scn->nutris); - if(trgsides[side_id].list_id == FLAG_LIST_BY_MEDIUM) { - ASSERT(find_side_in_list(trgsides, side_ids_by_medium, side_id, - FLAG_LIST_BY_MEDIUM)); - return; - } - ASSERT(!find_side_in_list(trgsides, side_ids_by_medium, side_id, - FLAG_LIST_BY_MEDIUM)); - darray_side_id_push_back(side_ids_by_medium, &side_id); - trgsides[side_id].list_id = FLAG_LIST_BY_MEDIUM; -} - static side_id_t -get_side_from_medium_list_not_in_connex_component - (struct senc_scene* scn, - struct trgside* trgsides, - side_id_t* first_side_by_medium_not_in_component, - const struct darray_side_id* side_ids_by_medium) -{ - side_id_t i, sz; - size_t tmp; - const side_id_t* ids; - (void)scn; - ASSERT(scn && trgsides && first_side_by_medium_not_in_component - && side_ids_by_medium); - tmp = darray_side_id_size_get(side_ids_by_medium); - ids = darray_side_id_cdata_get(side_ids_by_medium); - ASSERT(tmp <= SIDE_MAX__); - sz = (side_id_t)tmp; - i = *first_side_by_medium_not_in_component; - while(i < sz && trgsides[ids[i]].list_id != FLAG_LIST_BY_MEDIUM) ++i; - - *first_side_by_medium_not_in_component = i+1; - if(i == sz) return SIDE_NULL__; - ASSERT(trgsides[ids[i]].list_id == FLAG_LIST_BY_MEDIUM); - return ids[i]; -} - -static FINLINE side_id_t -get_side_from_stack - (struct trgside* trgsides, - struct darray_side_id* stack) -{ - side_id_t id; - size_t sz; - (void)trgsides; - ASSERT(trgsides && stack); - sz = darray_side_id_size_get(stack); - id = darray_side_id_cdata_get(stack)[sz - 1]; - ASSERT(trgsides[id].list_id == FLAG_WAITING_STACK); - darray_side_id_pop_back(stack); - return id; -} - -static res_T -extract_connex_components - (struct senc_descriptor* desc, - struct trgside* trgsides, - struct darray_cc_descriptor* connex_components, - struct darray_triangle_tmp* triangles_tmp_array, - struct darray_triangle_comp* triangles_comp, - struct darray_side_id* side_ids_by_medium) +get_side_not_in_connex_component + (const side_id_t last_side, + const struct trgside* trgsides, + const unsigned char* processed, + side_id_t* first_side_not_in_component, + const medium_id_t medium) { - res_T res = RES_OK; - medium_id_t m; - size_t tmp; - struct senc_scene* scn; - struct mem_allocator* alloc; - struct darray_side_id stack; - /* Arrays of cc_descriptor ids, organized by medium. */ - struct darray_component_id* component_ids_by_medium = NULL; - - ASSERT(trgsides && desc && connex_components && triangles_tmp_array - && side_ids_by_medium); - - alloc = descriptor_get_allocator(desc); - scn = desc->scene; - - /* Init data structures */ - darray_side_id_init(alloc, &stack); - component_ids_by_medium - = MEM_ALLOC(alloc, scn->nmeds * sizeof(struct darray_uint)); - if(!component_ids_by_medium) { - res = RES_MEM_ERR; - goto error; - } - FOR_EACH(m, 0, scn->nmeds) { - struct darray_component_id* const cc = component_ids_by_medium + m; - darray_component_id_init(alloc, cc); - } - - /* For each medium extract connex components */ - FOR_EACH(m, 0, scn->nmeds) { - struct darray_component_id* const cc_ids_by_medium - = component_ids_by_medium + m; - struct cc_descriptor current_cc; - /* Id of the first trgside not already in a connex component */ - side_id_t first_side_by_medium_not_in_component = 0; - /* Init current component */ - cc_descriptor_init(alloc, &current_cc); - - for(;;) { - /* Any not-already-used side by medium is used as a starting point */ - const side_id_t start_side_id = - get_side_from_medium_list_not_in_connex_component(scn, trgsides, - &first_side_by_medium_not_in_component, side_ids_by_medium + m); - side_id_t crt_side_id = start_side_id; - char* side_membership; - - ASSERT(start_side_id == SIDE_NULL__ || start_side_id < 2 * scn->nutris); - if(start_side_id == SIDE_NULL__) - break; /* start_side_id=SIDE_NULL__ => medium done! */ - ASSERT(trgsides[start_side_id].list_id == FLAG_LIST_BY_MEDIUM); - - /* Reset CC data for a new CC */ - tmp = darray_cc_descriptor_size_get(connex_components); - ASSERT(tmp <= COMPONENT_MAX__); - cc_descriptor_clear(&current_cc); - /* 1 char per triangle (2 sides) */ - darray_char_resize(&current_cc.side_membership, scn->nutris); - side_membership = darray_char_data_get(&current_cc.side_membership); - memset(side_membership, 0, scn->nutris * sizeof(char)); - current_cc.cc_id = (component_id_t)tmp; - current_cc.medium = m; - - for(;;) { - int i; - /* Pop waiting stack to feed crt_side */ - struct trgside* crt_side = trgsides + crt_side_id; - const trg_id_t crt_trg_id = TRGSIDE_2_TRG(crt_side_id); - struct triangle_comp* trg_comp = - darray_triangle_comp_data_get(triangles_comp) + crt_trg_id; - ASSERT(crt_trg_id < scn->nutris); - ASSERT(trgsides[crt_side_id].medium == m); - - /* Record crt_side both as component and triangle level */ - current_cc.side_count++; - trgsides[crt_side_id].list_id = FLAG_LIST_COMPONENT; - if(TRGSIDE_IS_FRONT(crt_side_id)) { - ASSERT(trg_comp->component[SIDE_FRONT] == USHRT_MAX); - trg_comp->component[SIDE_FRONT] = current_cc.cc_id; - ASSERT(!(side_membership[crt_trg_id] & FLAG_FRONT)); - side_membership[crt_trg_id] |= FLAG_FRONT; - } else { - ASSERT(trg_comp->component[SIDE_BACK] == USHRT_MAX); - trg_comp->component[SIDE_BACK] = current_cc.cc_id; - ASSERT(!(side_membership[crt_trg_id] & FLAG_BACK)); - side_membership[crt_trg_id] |= FLAG_BACK; - } -#ifndef NDEBUG - crt_side->member_of_cc = current_cc.cc_id; -#endif - /* Store neighbour sides in a waiting stack */ - FOR_EACH(i, 0, 3) { - side_id_t neighbour_id = crt_side->facing_side_id[i]; - struct trgside* neighbour = trgsides + neighbour_id; - if(neighbour->medium != crt_side->medium) { - /* Found medium discontinuity! - * Model topology is broken. */ - const struct triangle_in* triangles_in - = darray_triangle_in_cdata_get(&scn->triangles_in); - const union double3* positions - = darray_position_cdata_get(&scn->vertices); - log_err(scn->dev, - "Medium mismatch found between triangle %lu %s" - " side and triangle %u %s side.\n", - (unsigned long)crt_trg_id, - TRGSIDE_IS_FRONT(crt_side_id) ? "front": "back", - TRGSIDE_2_TRG(neighbour_id), - TRGSIDE_IS_FRONT(neighbour_id) ? "front" : "back"); - log_err(scn->dev, - "Triangle %lu:\n (%g %g %g)\n (%g %g %g)\n (%g %g %g)\n", - (unsigned long)crt_trg_id, - SPLIT3(positions[triangles_in[crt_trg_id].vertice_id[0]].vec), - SPLIT3(positions[triangles_in[crt_trg_id].vertice_id[1]].vec), - SPLIT3(positions[triangles_in[crt_trg_id].vertice_id[2]].vec)); - log_err(scn->dev, - "Triangle %lu:\n (%g %g %g)\n (%g %g %g)\n (%g %g %g)\n", - (unsigned long)neighbour_id, - SPLIT3(positions[triangles_in[neighbour_id].vertice_id[0]].vec), - SPLIT3(positions[triangles_in[neighbour_id].vertice_id[1]].vec), - SPLIT3(positions[triangles_in[neighbour_id].vertice_id[2]].vec)); - log_err(desc->scene->dev, "Media: %lu VS %lu\n", - neighbour->medium, crt_side->medium); - res = RES_BAD_ARG; - cc_descriptor_release(&current_cc); - goto error; - } - if(neighbour->list_id == FLAG_LIST_COMPONENT) { - /* Already processed */ -#ifndef NDEBUG - ASSERT(neighbour->member_of_cc == current_cc.cc_id); -#endif - continue; - } - if(neighbour->list_id == FLAG_WAITING_STACK) { - continue; /* Already processed */ - } - add_side_to_stack(scn, &stack, trgsides, neighbour_id); - } - if(darray_side_id_size_get(&stack) == 0) - break; /* Empty stack => connex component is done! */ - crt_side_id = get_side_from_stack(trgsides, &stack); - } - /* Keep track of this new connex component */ - darray_component_id_push_back(cc_ids_by_medium, &current_cc.cc_id); - find_component_Zmax(scn, triangles_tmp_array, &current_cc); - darray_cc_descriptor_push_back(connex_components, &current_cc); - } - cc_descriptor_release(&current_cc); - } - -exit: - if(component_ids_by_medium) { - FOR_EACH(m, 0, scn->nmeds) { - struct darray_component_id* cc = component_ids_by_medium + m; - darray_component_id_release(cc); - } - MEM_RM(scn->dev->allocator, component_ids_by_medium); + ASSERT(trgsides && processed && first_side_not_in_component); + { + side_id_t i = *first_side_not_in_component; + while (i <= last_side + && (trgsides[i].medium != medium + || (processed[TRGSIDE_2_TRG(i)] & TRGSIDE_2_SIDEFLAG(i)))) + ++i; + + *first_side_not_in_component = i + 1; + if(i > last_side) return SIDE_NULL__; + return i; } - darray_side_id_release(&stack); - ASSERT(desc->triangle_count - == darray_triangle_comp_size_get(triangles_comp)); - /* triangles_enc is still unused: no size to assert */ - return res; -error: - goto exit; } -void +static void get_scn_indices(const unsigned itri, unsigned ids[3], void* ctx) { int i; const struct senc_scene* scene = ctx; @@ -460,7 +98,7 @@ get_scn_indices(const unsigned itri, unsigned ids[3], void* ctx) { } } -void +static void get_scn_position(const unsigned ivert, float pos[3], void* ctx) { const struct senc_scene* scene = ctx; const union double3* pt = @@ -495,391 +133,682 @@ self_hit_filter return (hit_component == *origin_component); } -static res_T -group_connex_components +static void +extract_connex_components (struct senc_descriptor* desc, struct trgside* trgsides, - struct darray_triangle_comp* triangles_comp, - struct darray_cc_descriptor* connex_components) + struct darray_ptr_component_descriptor* connex_components, + const struct darray_triangle_tmp* triangles_tmp_array, + struct darray_triangle_comp* triangles_comp_array, + struct s3d_scene_view** s3d_view, + ATOMIC* component_count, + /* Shared error status. + * We accept to overwritte an error with a different error */ + res_T* p_res) { - res_T res = RES_OK; + /* This function is called from an omp parallel block and executed + * concurrently. */ + const struct senc_scene* scn; struct mem_allocator* alloc; - struct cc_descriptor* descriptors; - struct s3d_device* s3d = NULL; - struct s3d_scene* s3d_scn = NULL; - struct s3d_shape* s3d_shp = NULL; - struct s3d_scene_view* s3d_view = NULL; - struct s3d_vertex_data attribs; - size_t tmp; - component_id_t cc_count, c; - medium_id_t infinite_medium = MEDIUM_NULL__; - side_id_t infinite_medium_first_side = SIDE_MAX__; - char infinite_medium_is_known = 0; - (void)trgsides; + int64_t mm; + struct darray_side_id stack; + struct darray_side_id ids_of_sides_around_max_z_vertex; + const union double3* positions; + const struct triangle_tmp* triangles_tmp; + struct triangle_comp* triangles_comp; + /* An array to flag sides when processed */ + unsigned char* processed; + /* An array to store the component being processed */ + struct darray_side_id current_component; + /* A bool array to store media of the component being processed */ + unsigned char* current_media = NULL; + size_t sz, ii; + ASSERT(trgsides && desc && connex_components && triangles_tmp_array + && triangles_comp_array && s3d_view && component_count && p_res); alloc = descriptor_get_allocator(desc); - descriptors = darray_cc_descriptor_data_get(connex_components); - tmp = darray_cc_descriptor_size_get(connex_components); - ASSERT(tmp <= COMPONENT_MAX__); - cc_count = (component_id_t)tmp; + scn = desc->scene; + positions = darray_position_cdata_get(&scn->vertices); + triangles_tmp = darray_triangle_tmp_cdata_get(triangles_tmp_array); + triangles_comp = darray_triangle_comp_data_get(triangles_comp_array); + darray_side_id_init(alloc, &stack); + darray_side_id_init(alloc, &ids_of_sides_around_max_z_vertex); + darray_side_id_init(alloc, &current_component); + processed = MEM_CALLOC(alloc, scn->nutris, sizeof(unsigned char)); + if(!processed) { + *p_res = RES_MEM_ERR; + return; + } + +#ifndef NDEBUG + #pragma omp single + { + trg_id_t t_; + ASSERT(darray_ptr_component_descriptor_size_get(connex_components) == 0); + FOR_EACH(t_, 0, scn->nutris) { + const struct triangle_in* trg_in = + darray_triangle_in_cdata_get(&scn->triangles_in) + t_; + const struct side_range* media_use + = darray_side_range_cdata_get(&scn->media_use); + FOR_EACH(mm, 0, 2) { + const side_id_t side = TRGIDxSIDE_2_TRGSIDE(t_, mm); + const medium_id_t medium = trg_in->medium[mm]; + ASSERT(media_use[medium].first <= side && side + <= media_use[medium].last); + } + } + } /* Implicit barrier here */ +#endif - if(!cc_count) return RES_OK; /* No component to group */ + /* We loop on sides to build connex components. */ + #pragma omp for schedule(dynamic) nowait + for(mm = 0; mm < (int64_t)scn->nmeds; mm++) { /* Process all media */ + const medium_id_t m = (medium_id_t)mm; + /* Any not-already-used side is used as a starting point */ + const struct side_range* media_use = + darray_side_range_cdata_get(&scn->media_use) + m; + side_id_t first_side_not_in_component = media_use->first; + double max_z_nz; + const side_id_t last_side = media_use->last; + int component_canceled = 0; + res_T tmp_res = RES_OK; + ATOMIC id; + + if(*p_res != RES_OK) continue; + if(first_side_not_in_component == SIDE_NULL__) + continue; /* Unused medium */ + ASSERT(first_side_not_in_component < 2 * scn->nutris); + ASSERT(darray_side_id_size_get(&stack) == 0); + ASSERT(darray_side_id_size_get(&current_component) == 0); + for(;;) { /* Process all components for this medium */ + const side_id_t start_side_id = get_side_not_in_connex_component + (last_side, trgsides, processed, &first_side_not_in_component, m); + side_id_t crt_side_id = start_side_id; + side_id_t last_side_id = start_side_id; + vrtx_id_t max_z_vrtx_id = VRTX_NULL__; + struct cc_descriptor *cc; + double max_z = -DBL_MAX; + + ASSERT(start_side_id == SIDE_NULL__ || start_side_id < 2 * scn->nutris); + darray_side_id_clear(&current_component); + + if(*p_res != RES_OK) break; + if(start_side_id == SIDE_NULL__) + break; /* start_side_id=SIDE_NULL__ => component done! */ + +#ifndef NDEBUG + { + trg_id_t tid = TRGSIDE_2_TRG(start_side_id); + enum side_id s = TRGSIDE_2_SIDE(start_side_id); + medium_id_t side_med + = darray_triangle_in_data_get(&desc->scene->triangles_in)[tid].medium[s]; + ASSERT(side_med == m); + } +#endif + + /* Reuse array if possible, or create a new one */ + if(current_media) { + memset(current_media, 0, scn->nmeds); + } else { + current_media = MEM_CALLOC(alloc, scn->nmeds, sizeof(unsigned char)); + if(!current_media) { + *p_res = RES_MEM_ERR; + continue; + } + } + current_media[m] = 1; + for(;;) { /* Process all sides of this component */ + int i; + enum side_flag crt_side_flag = TRGSIDE_2_SIDEFLAG(crt_side_id); + struct trgside* crt_side = trgsides + crt_side_id; + const trg_id_t crt_trg_id = TRGSIDE_2_TRG(crt_side_id); + const struct triangle_in* trg_in = + darray_triangle_in_cdata_get(&scn->triangles_in) + crt_trg_id; + unsigned char* trg_used = processed + crt_trg_id; + const struct triangle_tmp* const trg_tmp = triangles_tmp + crt_trg_id; + ASSERT(crt_trg_id < scn->nutris); + + if(*p_res != RES_OK) break; + + /* Record Zmax information + * Keep track of the appropriate vertex of the component in order + * to cast a ray at the component grouping step of the algorithm. + * The most appropriate vertex is (the) one with the greater Z + * coordinate. */ + if(max_z < trg_tmp->max_z) { + /* New best vertex */ + max_z = trg_tmp->max_z; + /* New vertex: reset list of sides */ + darray_side_id_clear(&ids_of_sides_around_max_z_vertex); + + /* Select a vertex with z == max_z */ + FOR_EACH(i, 0, 3) { + if(max_z == positions[trg_in->vertice_id[i]].pos.z) { + max_z_vrtx_id = trg_in->vertice_id[i]; + break; + } + } + ASSERT(i < 3); /* Found one */ + /* List of sides using the vertex */ + OK2(darray_side_id_push_back(&ids_of_sides_around_max_z_vertex, + &crt_side_id)); + } else if(max_z == trg_tmp->max_z) { + /* Does this triangle use the currently selected max_z vertex? */ + FOR_EACH(i, 0, 3) { + if(max_z_vrtx_id == trg_in->vertice_id[i]) { + /* List of sides using the vertex */ + OK2(darray_side_id_push_back(&ids_of_sides_around_max_z_vertex, + &crt_side_id)); + break; + } + } + } + + /* Record crt_side both as component and triangle level */ + if((*trg_used & crt_side_flag) == 0) { + OK2(darray_side_id_push_back(&current_component, &crt_side_id)); + *trg_used |= (unsigned char)crt_side_flag; + } + + /* Store neighbour' sides in a waiting stack */ + FOR_EACH(i, 0, 3) { + side_id_t neighbour_id = crt_side->facing_side_id[i]; + trg_id_t nbour_trg_id = TRGSIDE_2_TRG(neighbour_id); + enum side_flag nbour_side_id = TRGSIDE_2_SIDEFLAG(neighbour_id); + unsigned char* nbour_used = processed + nbour_trg_id; + const struct trgside* neighbour = trgsides + neighbour_id; + if(*nbour_used & nbour_side_id) continue; /* Already processed */ + if(neighbour->medium < m) { + /* Not the same medium. + * Neighbour's medium id is less than current medium: the whole + * component is to be processed by another thread (possibly the one + * associated with neighbour's medium). */ + component_canceled = 1; + goto canceled; + } + /* Mark neighbour as processed and stack it */ + *nbour_used |= (unsigned char)nbour_side_id; + OK2(darray_side_id_push_back(&stack, &neighbour_id)); + OK2(darray_side_id_push_back(&current_component, &neighbour_id)); + current_media[neighbour->medium] = 1; + } + sz = darray_side_id_size_get(&stack); + if(sz == 0) break; /* Empty stack => component is done! */ + crt_side_id = darray_side_id_cdata_get(&stack)[sz - 1]; + darray_side_id_pop_back(&stack); + last_side_id = MMAX(last_side_id, crt_side_id); + } + canceled: + if(component_canceled) continue; + + /* Register the new component and get it initialized */ + cc = MEM_ALLOC(alloc, sizeof(struct cc_descriptor)); + if(!cc) *p_res = RES_MEM_ERR; + if(*p_res != RES_OK) break; + + ASSERT(m == trgsides[start_side_id].medium); + ASSERT(max_z_vrtx_id != VRTX_NULL__); + cc_descriptor_init(alloc, cc); + id = ATOMIC_INCR(component_count) - 1; + ASSERT(id <= COMPONENT_MAX__); + cc->cc_id = (component_id_t)id; + sz = darray_side_id_size_get(&current_component); + ASSERT(sz <= SIDE_MAX__); + cc->side_count = (side_id_t)sz; + cc->side_range.first = start_side_id; + cc->side_range.last = last_side_id; + cc->max_z_vrtx_id = max_z_vrtx_id; + /* Tranfer ownership of the array to component */ + ASSERT(!cc->media && current_media); + cc->media = current_media; + current_media = NULL; + + /* Write component membership in the global structure + * No need for sync here as an unique thread write a given side */ + STATIC_ASSERT(sizeof(cc->cc_id) >= 4, Cannot_write_IDs_sync_free); + ASSERT(IS_ALIGNED(triangles_comp->component, sizeof(cc->cc_id))); + FOR_EACH(ii, 0, sz) { + const side_id_t s = darray_side_id_cdata_get(&current_component)[ii]; + triangles_comp[TRGSIDE_2_TRG(s)].component[TRGSIDE_2_SIDE(s)] = cc->cc_id; + } + + /* Compute the normal at the max_z vertex. */ + max_z_nz = 0; + sz = darray_side_id_size_get(&ids_of_sides_around_max_z_vertex); + FOR_EACH(ii, 0, sz) { + const side_id_t side_id = + darray_side_id_cdata_get(&ids_of_sides_around_max_z_vertex)[ii]; + const trg_id_t trg_id = TRGSIDE_2_TRG(side_id); + const struct triangle_in* trg_in = + darray_triangle_in_cdata_get(&scn->triangles_in) + trg_id; + const struct triangle_comp* trg_comp = triangles_comp + trg_id; + const union double3* vertices = + darray_position_cdata_get(&scn->vertices); + double edge0[3], edge1[3], normal[3], norm; + + /* To garanty that triangles with 2 sides in the component total to 0 + * regardless of numeric accuracy, we need to prevent them to + * contribute (remember than x + y - y == 0 can be false). */ + ASSERT(trg_comp->component[SIDE_FRONT] == cc->cc_id + || trg_comp->component[SIDE_BACK] == cc->cc_id); + if(trg_comp->component[SIDE_FRONT] == trg_comp->component[SIDE_BACK]) + continue; + + d3_sub(edge0, vertices[trg_in->vertice_id[1]].vec, + vertices[trg_in->vertice_id[0]].vec); + d3_sub(edge1, vertices[trg_in->vertice_id[2]].vec, + vertices[trg_in->vertice_id[0]].vec); + d3_cross(normal, edge0, edge1); + norm = d3_normalize(normal, normal); + ASSERT(norm); (void)norm; + + /* Orient the geometrical normal according to the convention */ + if(TRGSIDE_IS_FRONT(side_id) + == ((scn->convention & SENC_CONVENTION_NORMAL_FRONT) != 0)) { + max_z_nz += normal[2]; + } else { + max_z_nz -= normal[2]; + } + } + cc->is_outer_border = (max_z_nz < 0); + + /* Need to synchronize connex_components growth as this global structure + * is accessed by multipe threads */ + #pragma omp critical + { + struct cc_descriptor** components; + sz = darray_ptr_component_descriptor_size_get(connex_components); + if(sz <= cc->cc_id) { + tmp_res = darray_ptr_component_descriptor_resize(connex_components, + 1 + cc->cc_id); + if(tmp_res != RES_OK) *p_res = tmp_res; + } + if(*p_res == RES_OK) { + /* Don't set the pointer before resize as this can lead to move data */ + components = + darray_ptr_component_descriptor_data_get(connex_components); + ASSERT(components[cc->cc_id] == NULL); + components[cc->cc_id] = cc; + } + } + } + tmp_error: + if(tmp_res != RES_OK) *p_res = tmp_res; + } /* No barrier here */ + + MEM_RM(alloc, processed); + MEM_RM(alloc, current_media); + darray_side_id_release(&current_component); + darray_side_id_release(&stack); + darray_side_id_release(&ids_of_sides_around_max_z_vertex); + + /* The first thread here creates the s3d view */ + #pragma omp single nowait + if(*p_res == RES_OK) { + res_T res = RES_OK; + struct s3d_device* s3d = NULL; + struct s3d_scene* s3d_scn = NULL; + struct s3d_shape* s3d_shp = NULL; + struct s3d_vertex_data attribs; + + attribs.type = S3D_FLOAT3; + attribs.usage = S3D_POSITION; + attribs.get = get_scn_position; + + /* Put geometry in a 3D view */ + OK(s3d_device_create(desc->scene->dev->logger, alloc, 0, &s3d)); + OK(s3d_scene_create(s3d, &s3d_scn)); + OK(s3d_shape_create_mesh(s3d, &s3d_shp)); + + /* Back to API type for ntris and nverts */ + ASSERT(desc->scene->nutris < UINT_MAX); + 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)); + s3d_mesh_set_hit_filter_function(s3d_shp, self_hit_filter, + triangles_comp_array); + OK(s3d_scene_attach_shape(s3d_scn, s3d_shp)); + OK(s3d_scene_view_create(s3d_scn, S3D_TRACE, s3d_view)); + error: + if(res != RES_OK) *p_res = res; + if(s3d) S3D(device_ref_put(s3d)); + if(s3d_scn) S3D(scene_ref_put(s3d_scn)); + if(s3d_shp) S3D(shape_ref_put(s3d_shp)); + } + +#ifndef NDEBUG + /* Need to wait for all threads done to be able to check stuff */ + #pragma omp barrier + if(*p_res != RES_OK) return; + #pragma omp single + { + trg_id_t t_; + component_id_t c; + ASSERT(ATOMIC_GET(component_count) == + (int)darray_ptr_component_descriptor_size_get(connex_components)); + FOR_EACH(t_, 0, scn->nutris) { + struct triangle_comp* trg_comp = + darray_triangle_comp_data_get(triangles_comp_array) + t_; + ASSERT(trg_comp->component[SIDE_FRONT] != COMPONENT_NULL__); + ASSERT(trg_comp->component[SIDE_BACK] != COMPONENT_NULL__); + } + FOR_EACH(c, 0, ATOMIC_GET(component_count)) { + struct cc_descriptor** components = + darray_ptr_component_descriptor_data_get(connex_components); + ASSERT(components[c] != NULL && components[c]->cc_id == c); + } + ASSERT(desc->triangle_count == scn->nutris); + } /* Implicit barrier here */ +#endif +} - attribs.type = S3D_FLOAT3; - attribs.usage = S3D_POSITION; - attribs.get = get_scn_position; +static void +group_connex_components + (struct senc_descriptor* desc, + struct trgside* trgsides, + struct darray_triangle_comp* triangles_comp, + struct darray_ptr_component_descriptor* connex_components, + struct s3d_scene_view* s3d_view, + ATOMIC* next_enclosure_id, + /* Shared error status. + * We accept to overwritte an error with a different error */ + res_T* res) +{ + /* This function is called from an omp parallel block and executed + * concurrently. */ + struct cc_descriptor** descriptors; + const union double3* positions; + size_t tmp; + component_id_t cc_count; + int64_t ccc; - /* Put geometry in a 3D view */ - OK(s3d_device_create(desc->scene->dev->logger, alloc, 0, &s3d)); - OK(s3d_scene_create(s3d, &s3d_scn)); - OK(s3d_shape_create_mesh(s3d, &s3d_shp)); + (void)trgsides; + ASSERT(desc && trgsides && triangles_comp && connex_components + && s3d_view && next_enclosure_id && res); + ASSERT(desc->enclosures_count == 1); - /* Back to API type for ntris and nverts */ - ASSERT(desc->scene->nutris < UINT_MAX); - 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)); - s3d_mesh_set_hit_filter_function(s3d_shp, self_hit_filter, triangles_comp); - OK(s3d_scene_attach_shape(s3d_scn, s3d_shp)); - OK(s3d_scene_view_create(s3d_scn, S3D_TRACE, &s3d_view)); + descriptors = darray_ptr_component_descriptor_data_get(connex_components); + tmp = darray_ptr_component_descriptor_size_get(connex_components); + ASSERT(tmp <= COMPONENT_MAX__); + cc_count = (component_id_t)tmp; + positions = darray_position_cdata_get(&desc->scene->vertices); /* Cast rays to find links between connex components */ - FOR_EACH(c, 0, cc_count) { + #pragma omp for + for(ccc = 0; ccc < (int64_t)cc_count; ccc++) { + res_T tmp_res = RES_OK; + component_id_t c = (component_id_t)ccc; struct s3d_hit hit = S3D_HIT_NULL; float origin[3]; const float dir[3] = { 0, 0, 1 }; const float range[2] = { 0, FLT_MAX }; - struct cc_descriptor* const cc = descriptors + c; - const struct triangle_comp* origin_trg = - darray_triangle_comp_cdata_get(triangles_comp) + cc->max_z_vrtx_id; - component_id_t self_hit_component - = origin_trg->component[1 - TRGSIDE_2_SIDE(cc->max_z_side_id)]; + struct cc_descriptor* const cc = descriptors[c]; + component_id_t self_hit_component = cc->cc_id; + const double* max_vrtx; + if(*res != RES_OK) continue; + ASSERT(cc->cc_id == c); ASSERT(cc->cc_group_root == CC_GROUP_ID_NONE); + ASSERT(cc->max_z_vrtx_id < desc->scene->nverts); - if(cc->max_z_nz < 0) { + max_vrtx = positions[cc->max_z_vrtx_id].vec; + if(cc->is_outer_border) { + ATOMIC id; /* Don't need to cast a ray */ - cc->cc_group_root = c; /* New group with self as root */ - ASSERT(desc->enclosures_count < USHRT_MAX); - cc->enclosure_id = desc->enclosures_count++; + cc->cc_group_root = cc->cc_id; /* New group with self as root */ + id = ATOMIC_INCR(next_enclosure_id) - 1; + ASSERT(id <= ENCLOSURE_MAX__); + cc->enclosure_id = (enclosure_id_t)id; continue; } - ASSERT(cc->max_z_nz != 0 - /* The only situation with nz==0 we can think of is this one: */ - || (trgsides[cc->max_z_side_id].medium - == trgsides[TRGSIDE_OPPOSITE(cc->max_z_side_id)].medium - && (trgsides[cc->max_z_side_id].facing_side_id[0] - == TRGSIDE_OPPOSITE(cc->max_z_side_id) - || trgsides[cc->max_z_side_id].facing_side_id[1] - == TRGSIDE_OPPOSITE(cc->max_z_side_id) - || trgsides[cc->max_z_side_id].facing_side_id[2] - == TRGSIDE_OPPOSITE(cc->max_z_side_id)))); - f3_set_d3(origin, cc->max_vrtx); + f3_set_d3(origin, max_vrtx); /* Self-hit data: self hit if hit this component "on the other side" */ - OK(s3d_scene_view_trace_ray(s3d_view, origin, dir, range, - &self_hit_component, &hit)); + tmp_res = s3d_scene_view_trace_ray(s3d_view, origin, dir, range, + &self_hit_component, &hit); + if(tmp_res != RES_OK) { + *res = tmp_res; + continue; + } /* If no hit, the component is facing an infinite medium */ if(S3D_HIT_NONE(&hit)) { cc->cc_group_root = CC_GROUP_ROOT_INFINITE; cc->enclosure_id = 0; - if(!infinite_medium_is_known) { - infinite_medium_is_known = 1; - infinite_medium = cc->medium; - infinite_medium_first_side = cc->max_z_side_id; - continue; - } - if(infinite_medium != cc->medium) { - /* Medium mismatch! Model topology is broken. */ - const trg_id_t t1 = TRGSIDE_2_TRG(infinite_medium_first_side); - const trg_id_t t2 = TRGSIDE_2_TRG(cc->max_z_side_id); - const struct triangle_in* triangles_in - = darray_triangle_in_cdata_get(&desc->scene->triangles_in); - const union double3* positions - = darray_position_cdata_get(&desc->scene->vertices); - log_err(desc->scene->dev, - "Medium mismatch found between triangle %lu %s side and triangle" - " %lu %s side, both facing infinite.\n", - t1, - TRGSIDE_IS_FRONT(infinite_medium_first_side) ? "front" : "back", - TRGSIDE_2_TRG(cc->max_z_side_id), - TRGSIDE_IS_FRONT(cc->max_z_side_id) ? "front" : "back"); - log_err(desc->scene->dev, - "Triangle %lu:\n (%g %g %g)\n (%g %g %g)\n (%g %g %g)\n", - (unsigned long)t1, - SPLIT3(positions[triangles_in[t1].vertice_id[0]].vec), - SPLIT3(positions[triangles_in[t1].vertice_id[1]].vec), - SPLIT3(positions[triangles_in[t1].vertice_id[2]].vec)); - log_err(desc->scene->dev, - "Triangle %lu:\n (%g %g %g)\n (%g %g %g)\n (%g %g %g)\n", - (unsigned long)t2, - SPLIT3(positions[triangles_in[t2].vertice_id[0]].vec), - SPLIT3(positions[triangles_in[t2].vertice_id[1]].vec), - SPLIT3(positions[triangles_in[t2].vertice_id[2]].vec)); - log_err(desc->scene->dev, "Media: %lu VS %lu\n", - infinite_medium, cc->medium); - res = RES_BAD_ARG; - goto error; - } - /* Same medium as previous members of the group: OK */ - continue; } else { /* If hit, group this component */ const trg_id_t hit_trg_id = (trg_id_t)hit.prim.prim_id; - const struct triangle_in* hit_trg_in = - darray_triangle_in_cdata_get(&desc->scene->triangles_in) + hit_trg_id; const struct triangle_comp* hit_trg_comp = darray_triangle_comp_cdata_get(triangles_comp) + hit_trg_id; enum side_id hit_side = (hit.normal[2] > 0) ? SIDE_FRONT : SIDE_BACK; - const side_id_t hit_side_id = TRGIDxSIDE_2_TRGSIDE(hit_trg_id, hit_side); ASSERT(hit_trg_id < desc->scene->nutris); /* Not really the root until following links */ cc->cc_group_root = hit_trg_comp->component[hit_side]; -#ifndef NDEBUG - { - const struct cc_descriptor* hit_desc; - ASSERT(cc->cc_group_root - < darray_cc_descriptor_size_get(connex_components)); - hit_desc = darray_cc_descriptor_cdata_get(connex_components) - + cc->cc_group_root; - ASSERT(hit_desc->medium == hit_trg_in->medium[hit_side]); - ASSERT(darray_char_cdata_get(&hit_desc->side_membership)[hit_trg_id] - & TRGSIDE_IS_FRONT(hit_side) ? FLAG_FRONT : FLAG_BACK); - } -#endif - if(hit_trg_in->medium[hit_side] != cc->medium) { - /* Medium mismatch! - * Model topology is broken. */ - const trg_id_t t1 = TRGSIDE_2_TRG(hit_side_id); - const trg_id_t t2 = TRGSIDE_2_TRG(hit_side); - const struct triangle_in* triangles_in - = darray_triangle_in_cdata_get(&desc->scene->triangles_in); - const union double3* positions - = darray_position_cdata_get(&desc->scene->vertices); - log_err(desc->scene->dev, - "Medium mismatch found between triangle %lu %s side and triangle" - " %u %s side facing each other.\n", - (unsigned long)t1, - TRGSIDE_IS_FRONT(hit_side) ? "front" : "back", - (unsigned long)t2, - TRGSIDE_IS_FRONT(cc->max_z_side_id) ? "front" : "back"); - log_err(desc->scene->dev, - "Triangle %lu:\n (%g %g %g)\n (%g %g %g)\n (%g %g %g)\n", - (unsigned long)t1, - SPLIT3(positions[triangles_in[t1].vertice_id[0]].vec), - SPLIT3(positions[triangles_in[t1].vertice_id[1]].vec), - SPLIT3(positions[triangles_in[t1].vertice_id[2]].vec)); - log_err(desc->scene->dev, - "Triangle %lu:\n (%g %g %g)\n (%g %g %g)\n (%g %g %g)\n", - (unsigned long)t2, - SPLIT3(positions[triangles_in[t2].vertice_id[0]].vec), - SPLIT3(positions[triangles_in[t2].vertice_id[1]].vec), - SPLIT3(positions[triangles_in[t2].vertice_id[2]].vec)); - log_err(desc->scene->dev, "Media: %lu VS %lu\n", - hit_trg_in->medium[hit_side], cc->medium); - res = RES_BAD_ARG; - goto error; - } + ASSERT(cc->cc_group_root < cc_count); } } - - /* Post-process links to group connex components */ - printf("\n"); - FOR_EACH(c, 0, cc_count) { - struct cc_descriptor* const cc = descriptors + c; - const struct cc_descriptor* other_desc = cc; - - while(other_desc->enclosure_id == CC_GROUP_ID_NONE) { - other_desc = darray_cc_descriptor_cdata_get(connex_components) - + other_desc->cc_group_root; + /* Implicit barrier here */ + ASSERT(ATOMIC_GET(next_enclosure_id) < ENCLOSURE_MAX__); + if(*res != RES_OK) return; + + /* One thread post-processes links to group connex components */ + #pragma omp single + { + res_T tmp_res = RES_OK; + desc->enclosures_count = (enclosure_id_t)ATOMIC_GET(next_enclosure_id); + tmp_res = darray_enclosure_resize(&desc->enclosures, desc->enclosures_count); + if(tmp_res != RES_OK) { + *res = tmp_res; + } else { + struct enclosure_data* enclosures + = darray_enclosure_data_get(&desc->enclosures); + FOR_EACH(ccc, 0, cc_count) { + component_id_t c = (component_id_t)ccc; + struct cc_descriptor* const cc = descriptors[c]; + const struct cc_descriptor* other_desc = cc; + struct enclosure_data* enc; + + while(other_desc->enclosure_id == CC_GROUP_ID_NONE) { + other_desc = *(darray_ptr_component_descriptor_cdata_get(connex_components) + + other_desc->cc_group_root); + } + ASSERT(other_desc->cc_group_root != CC_GROUP_ROOT_NONE); + ASSERT(other_desc->enclosure_id != CC_GROUP_ID_NONE); + cc->cc_group_root = other_desc->cc_group_root; + cc->enclosure_id = other_desc->enclosure_id; + enc = enclosures + cc->enclosure_id; + ++enc->cc_count; + /* Linked list of componnents */ + enc->first_component = cc->cc_id; + enc->side_range.first = MMIN(enc->side_range.first, cc->side_range.first); + enc->side_range.last = MMAX(enc->side_range.last, cc->side_range.last); + enc->side_count += cc->side_count; + tmp_res = bool_array_of_media_merge(&enc->tmp_enclosed_media, cc->media, + desc->scene->nmeds); + if(tmp_res != RES_OK) { + *res = tmp_res; + break; + } + } } - ASSERT(other_desc->cc_group_root != CC_GROUP_ROOT_NONE); - ASSERT(other_desc->enclosure_id != CC_GROUP_ID_NONE); - cc->cc_group_root = other_desc->cc_group_root; - cc->enclosure_id = other_desc->enclosure_id; } - -exit: - /* Local Star3D stuff is no longer useful */ - if(s3d) S3D(device_ref_put(s3d)); - if(s3d_scn) S3D(scene_ref_put(s3d_scn)); - if(s3d_shp) S3D(shape_ref_put(s3d_shp)); - if(s3d_view) S3D(scene_view_ref_put(s3d_view)); - return res; -error: - goto exit; + /* Implicit barrier here */ } -static res_T -scan_edges +static void +collect_and_link_neighbours (struct senc_scene* scn, - struct darray_neighbourhood* neighbourhood_by_edge, - struct darray_triangle_tmp* triangles_tmp_array) + struct trgside* trgsides, + struct darray_triangle_tmp* triangles_tmp_array, + /* Shared error status. + * We accept to overwritte an error with a different error */ + res_T* res) { - trg_id_t t; - struct triangle_in *triangles_in; + /* This function is called from an omp parallel block and executed + * concurrently. */ + const struct triangle_in *triangles_in; struct triangle_tmp *triangles_tmp; - edge_id_t max_nbedges; + const union double3* vertices; + const int thread_count = omp_get_num_threads(); + const int rank = omp_get_thread_num(); /* Htable used to give an id to edges */ struct htable_edge_id edge_ids; - res_T res = RES_OK; + /* Array to keep neighbourhood of edges + * Resize/Push operations on neighbourhood_by_edge are valid in the + * openmp block because a given neighbourhood is only processed + * by a single thread */ + struct darray_neighbourhood neighbourhood_by_edge; + edge_id_t edge_count; + edge_id_t nbedges_guess; + edge_id_t e; + trg_id_t t; + size_t sz; + res_T tmp_res; - ASSERT(scn && neighbourhood_by_edge && triangles_tmp_array); + ASSERT(scn && trgsides && triangles_tmp_array && res); ASSERT((size_t)scn->nuverts + (size_t)scn->nutris + 2 <= EDGE_MAX__); - /* Make some room for edges. */ - max_nbedges = (edge_id_t)(scn->nuverts + scn->nutris + 2); htable_edge_id_init(scn->dev->allocator, &edge_ids); - OK(htable_edge_id_reserve(&edge_ids, max_nbedges)); - OK(darray_neighbourhood_reserve(neighbourhood_by_edge, max_nbedges)); - OK(darray_triangle_tmp_resize(triangles_tmp_array, scn->nutris)); + darray_neighbourhood_init(scn->dev->allocator, &neighbourhood_by_edge); - /* Loop on triangles to register edges. */ - triangles_in = darray_triangle_in_data_get(&scn->triangles_in); + triangles_in = darray_triangle_in_cdata_get(&scn->triangles_in); triangles_tmp = darray_triangle_tmp_data_get(triangles_tmp_array); + vertices = darray_position_cdata_get(&scn->vertices); + + ASSERT(scn->nutris == darray_triangle_tmp_size_get(triangles_tmp_array)); + + /* Make some room for edges. */ + nbedges_guess = 4 + (thread_count == 1 + ? (edge_id_t)(scn->nuverts + scn->nutris) + : (edge_id_t)((scn->nuverts + scn->nutris) / (0.75 * thread_count))); + OK2(darray_neighbourhood_reserve(&neighbourhood_by_edge, nbedges_guess)); + OK2(htable_edge_id_reserve(&edge_ids, nbedges_guess)); + + /* Loop on triangles to register edges. + * All threads considering all the edges and processing some */ FOR_EACH(t, 0, scn->nutris) { - struct darray_neighbour* neighbour_list; - struct neighbour_info neighbour; - struct edge_neighbourhood neighbourhood; - char e; - FOR_EACH(e, 0, 3) { + struct trg_edge edge; + unsigned char ee; + FOR_EACH(ee, 0, 3) { edge_id_t* p_id; - edge_id_t id; - + size_t n_sz; + struct edge_neighbourhood* neighbourhood; + struct neighbour_info* info; + 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 = + /* v0,v1 and v1,v0 must give the same hash!!! */ + v0 + v1 + (int64_t)MMIN(v0, v1); + if(h % thread_count != rank) continue; /* Create edge. */ - set_edge(triangles_in[t].vertice_id[e], - triangles_in[t].vertice_id[(e + 1) % 3], - &neighbourhood.edge, &triangles_tmp[t].reversed_edge[e]); + set_edge(v0, v1, &edge, &triangles_tmp[t].reversed_edge[ee]); /* Find edge id; create it if not already done. */ - p_id = htable_edge_id_find(&edge_ids, &neighbourhood.edge); - + p_id = htable_edge_id_find(&edge_ids, &edge); if(p_id) { - id = *p_id; + neighbourhood = + darray_neighbourhood_data_get(&neighbourhood_by_edge) + *p_id; + ASSERT(neighbourhood->edge.vrtx0 == edge.vrtx0 + && neighbourhood->edge.vrtx1 == edge.vrtx1); } else { /* Create id */ - size_t tmp; - tmp = htable_edge_id_size_get(&edge_ids); - ASSERT(tmp <= EDGE_MAX__); - id = (edge_id_t)tmp; - OK(htable_edge_id_set(&edge_ids, &neighbourhood.edge, &id)); - darray_neighbour_init(scn->dev->allocator, &neighbourhood.neighbours); - OK(darray_neighbourhood_push_back(neighbourhood_by_edge, - &neighbourhood)); + edge_id_t id; + sz = htable_edge_id_size_get(&edge_ids); + ASSERT(sz <= EDGE_MAX__); + id = (edge_id_t)sz; + ASSERT(htable_edge_id_size_get(&edge_ids) + == darray_neighbourhood_size_get(&neighbourhood_by_edge)); + OK2(htable_edge_id_set(&edge_ids, &edge, &id)); + OK2(darray_neighbourhood_resize(&neighbourhood_by_edge, 1 + sz)); + neighbourhood = darray_neighbourhood_data_get(&neighbourhood_by_edge) + sz; + /* Add neighbour info to a newly created edge's neighbour list */ + neighbourhood->edge = edge; + ASSERT(darray_neighbour_size_get(&neighbourhood->neighbours) == 0); + /* Just a guess: few edges will have less than 2 neighbours */ + OK2(darray_neighbour_reserve(&neighbourhood->neighbours, 2)); } - /* Add neighbour info to edge's neighbour list */ - neighbour_list - = &darray_neighbourhood_data_get(neighbourhood_by_edge)[id].neighbours; - neighbour.trg_id = t; - neighbour.edge_rank = e; - darray_neighbour_push_back(neighbour_list, &neighbour); + /* Add neighbour info to neighbourhood */ + n_sz = darray_neighbour_size_get(&neighbourhood->neighbours); + OK2(darray_neighbour_resize(&neighbourhood->neighbours, 1 + n_sz)); + info = darray_neighbour_data_get(&neighbourhood->neighbours) + n_sz; + info->trg_id = t; + info->common_edge_rank = ee; } - } - -exit: - htable_edge_id_release(&edge_ids); - return res; -error: - goto exit; -} + } /* No barrier here. */ -static res_T -link_neighbours - (struct senc_scene* scn, - struct trgside* trgsides, - struct darray_neighbourhood* neighbourhood_by_edge, - struct darray_triangle_tmp* triangles_tmp_array, - struct darray_side_id* side_ids_by_medium) -{ - edge_id_t e, edge_count; - struct triangle_in *triangles_in; - struct triangle_tmp *triangles_tmp; - const union double3* vertices; - size_t tmp; - res_T res = RES_OK; - - ASSERT(scn && neighbourhood_by_edge && triangles_tmp_array - && side_ids_by_medium); - - /* Loop on edges. + /* Loop on collected edges. * For each edge sort triangle sides by rotation angle * and connect neighbours. */ - triangles_in = darray_triangle_in_data_get(&scn->triangles_in); - triangles_tmp = darray_triangle_tmp_data_get(triangles_tmp_array); - vertices = darray_position_cdata_get(&scn->vertices); - tmp = darray_neighbourhood_size_get(neighbourhood_by_edge); - ASSERT(tmp <= EDGE_MAX__); - edge_count = (edge_id_t)tmp; - + sz = darray_neighbourhood_size_get(&neighbourhood_by_edge); + ASSERT(sz <= EDGE_MAX__); + edge_count = (edge_id_t)sz; FOR_EACH(e, 0, edge_count) { - double edge[3], common_edge[3], basis[9], norm, mz, mz_edge; + double edge[3], common_edge[3], basis[9], norm, max_z, maxz_edge; vrtx_id_t v0, v1, v2; struct edge_neighbourhood* neighbourhood - = darray_neighbourhood_data_get(neighbourhood_by_edge) + e; + = darray_neighbourhood_data_get(&neighbourhood_by_edge) + e; struct darray_neighbour* neighbour_list = &neighbourhood->neighbours; side_id_t i, neighbour_count; - char mz_vid, mz_vid_edge; - tmp = darray_neighbour_size_get(neighbour_list); - ASSERT(tmp <= SIDE_MAX__); - neighbour_count = (side_id_t)tmp; + sz = darray_neighbour_size_get(neighbour_list); + ASSERT(sz <= SIDE_MAX__); + neighbour_count = (side_id_t)sz; ASSERT(neighbour_count); v0 = neighbourhood->edge.vrtx0; v1 = neighbourhood->edge.vrtx1; d3_sub(common_edge, vertices[v1].vec, vertices[v0].vec); - if(vertices[v0].pos.z > vertices[v1].pos.z) { - mz_edge = vertices[v0].pos.z; - mz_vid_edge = 0; - } else { - mz_edge = vertices[v1].pos.z; - mz_vid_edge = 1; - } + maxz_edge = MMAX(vertices[v0].pos.z, vertices[v1].pos.z); norm = d3_normalize(common_edge, common_edge); - ASSERT(norm); + ASSERT(norm); (void)norm; d33_basis(basis, common_edge); d33_inverse(basis, basis); FOR_EACH(i, 0, neighbour_count) { struct neighbour_info* neighbour_info = darray_neighbour_data_get(neighbour_list) + i; - struct triangle_in *trg_in = triangles_in + neighbour_info->trg_id; - struct triangle_tmp *neighbour = triangles_tmp + neighbour_info->trg_id; - char actual_vid; - v2 = trg_in->vertice_id[(neighbour_info->edge_rank + 2) % 3]; - if(vertices[v2].pos.z > mz_edge) { - mz = vertices[v2].pos.z; - mz_vid = 2; - } else { - mz = mz_edge; - mz_vid = mz_vid_edge; - } - /* Compute the actual vertex id - * as vertices are not in the v0 v1 v2 order in the actual triangle */ - if (mz_vid == 2) { - actual_vid = (2 + neighbour_info->edge_rank) % 3; - } - else { - int is_r = neighbour->reversed_edge[neighbour_info->edge_rank]; - ASSERT(mz_vid == 0 || mz_vid == 1); - actual_vid = ((is_r ? 1 - mz_vid : mz_vid) + neighbour_info->edge_rank) % 3; - } - - ASSERT(neighbour->max_z <= mz); - neighbour->max_z = mz; - ASSERT(0 <= actual_vid && actual_vid <= 2); - neighbour->max_z_vrtx_id = actual_vid; + const trg_id_t crt_id = neighbour_info->trg_id; + const unsigned char crt_edge = neighbour_info->common_edge_rank; + const struct triangle_in* trg_in = triangles_in + crt_id; + struct triangle_tmp* neighbour = triangles_tmp + crt_id; + union double3 n; /* Geometrical normal to neighbour triangle */ + const int is_reversed = neighbour->reversed_edge[crt_edge]; + v2 = trg_in->vertice_id[(crt_edge + 2) % 3]; + max_z = MMAX(vertices[v2].pos.z, maxz_edge); + ASSERT(neighbour->max_z <= max_z); + neighbour->max_z = max_z; /* Compute rotation angle around common edge */ d3_sub(edge, vertices[v2].vec, vertices[v0].vec); d33_muld3(edge, basis, edge); ASSERT(d3_len(edge) && (edge[0] || edge[1])); neighbour_info->angle = atan2(edge[1], edge[0]); + if(is_reversed) + d3(n.vec, +edge[1], -edge[0], 0); + else + d3(n.vec, -edge[1], +edge[0], 0); if(neighbour_info->angle < 0) neighbour_info->angle += 2 * PI; - /* Due to catastrophic cancelation, -eps+2pi translates to 2pi */ - ASSERT(0 <= neighbour_info->angle && neighbour_info->angle <= 2 * PI); + + /* Normal orientation calculation. */ + if(neighbour_info->angle <= PI / 4) { + ASSERT(n.pos.y); + neighbour_info->normal_toward_next_neighbour = (n.pos.y > 0); + } else if (neighbour_info->angle <= 3 * PI / 4) { + ASSERT(n.pos.x); + neighbour_info->normal_toward_next_neighbour = (n.pos.x < 0); + } else if (neighbour_info->angle <= 5 * PI / 4) { + ASSERT(n.pos.y); + neighbour_info->normal_toward_next_neighbour = (n.pos.y < 0); + } else if (neighbour_info->angle <= 7 * PI / 4) { + ASSERT(n.pos.x); + neighbour_info->normal_toward_next_neighbour = (n.pos.x > 0); + } else { + ASSERT(n.pos.y); + neighbour_info->normal_toward_next_neighbour = (n.pos.y > 0); + } } /* Sort triangles by rotation angle */ qsort(darray_neighbour_data_get(neighbour_list), neighbour_count, sizeof(struct neighbour_info), neighbour_cmp); - /* Link sides. + /* Link sides. * Create cycles of sides by neighbourhood around common edge. */ FOR_EACH(i, 0, neighbour_count) { /* Neighbourhood info for current pair of triangles */ @@ -888,16 +817,21 @@ link_neighbours const struct neighbour_info* ccw_neighbour = darray_neighbour_cdata_get(neighbour_list) + (i + 1) % neighbour_count; /* Rank of the edge of interest in triangles */ - const char crt_edge = current->edge_rank; - const char ccw_edge = ccw_neighbour->edge_rank; + const unsigned char crt_edge = current->common_edge_rank; + /* Here ccw refers to the rotation around the common edge + * and has nothing to do with vertices order in triangle definition + * nor Front/Back side convention */ + const unsigned char ccw_edge = ccw_neighbour->common_edge_rank; /* User id of current triangles */ const trg_id_t crt_id = current->trg_id; const trg_id_t ccw_id = ccw_neighbour->trg_id; /* Facing sides of triangles */ + const int front = ((scn->convention & SENC_CONVENTION_NORMAL_FRONT) != 0); const enum side_id crt_side - = triangles_tmp[crt_id].reversed_edge[crt_edge] ? SIDE_BACK : SIDE_FRONT; + = current->normal_toward_next_neighbour == front ? SIDE_FRONT : SIDE_BACK; const enum side_id ccw_side - = triangles_tmp[ccw_id].reversed_edge[ccw_edge] ? SIDE_FRONT : SIDE_BACK; + = ccw_neighbour->normal_toward_next_neighbour == front ? + SIDE_BACK : SIDE_FRONT; /* Index of sides in trgsides */ const side_id_t crt_side_idx = TRGIDxSIDE_2_TRGSIDE(crt_id, crt_side); const side_id_t ccw_side_idx = TRGIDxSIDE_2_TRGSIDE(ccw_id, ccw_side); @@ -907,168 +841,219 @@ link_neighbours /* Link sides */ p_crt_side->facing_side_id[crt_edge] = ccw_side_idx; p_ccw_side->facing_side_id[ccw_edge] = crt_side_idx; - /* Record sides by medium */ + + /* Record media */ p_crt_side->medium = triangles_in[crt_id].medium[crt_side]; p_ccw_side->medium = triangles_in[ccw_id].medium[ccw_side]; ASSERT(p_crt_side->medium < scn->nmeds); ASSERT(p_ccw_side->medium < scn->nmeds); - add_side_to_medium_list(scn, side_ids_by_medium + p_crt_side->medium, - trgsides, crt_side_idx); - add_side_to_medium_list(scn, side_ids_by_medium + p_ccw_side->medium, - trgsides, ccw_side_idx); } } - return res; +tmp_error: + if(tmp_res != RES_OK) *res = tmp_res; + /* Threads are allowed to return whitout sync. */ + htable_edge_id_release(&edge_ids); + darray_neighbourhood_release(&neighbourhood_by_edge); } -static res_T +static void build_result (struct senc_descriptor* desc, - struct darray_cc_descriptor* connex_components) + const struct darray_ptr_component_descriptor* connex_components, + const struct darray_triangle_comp* triangles_comp_array, + /* Shared error status. + * We accept to overwritte an error with a different error */ + res_T* res) { - res_T res = RES_OK; + /* This function is called from an omp parallel block and executed + * concurrently. */ struct mem_allocator* alloc; - const struct cc_descriptor* cc_descriptors; + struct cc_descriptor* const* cc_descriptors; struct enclosure_data* enclosures; - char* side_membership = NULL; const struct triangle_in* triangles_in; struct triangle_enc* triangles_enc; - const union double3* positions; + const struct triangle_comp* triangles_comp; struct htable_vrtx_id vtable; - size_t tmp; - component_id_t cc_count, c; - enclosure_id_t e; - side_id_t* side_counts; + struct senc_scene* scn; + int output_normal_in, normals_front, normals_back; + int64_t tt; + int64_t ee; - ASSERT(desc && connex_components); + ASSERT(desc && connex_components && triangles_comp_array); alloc = descriptor_get_allocator(desc); - tmp = darray_cc_descriptor_size_get(connex_components); - ASSERT(tmp < COMPONENT_MAX__); - cc_count = (component_id_t)tmp; - cc_descriptors = darray_cc_descriptor_cdata_get(connex_components); - darray_enclosure_resize(&desc->enclosures, desc->enclosures_count); + scn = desc->scene; + output_normal_in = (scn->convention & SENC_CONVENTION_NORMAL_INSIDE) != 0; + normals_front = (scn->convention & SENC_CONVENTION_NORMAL_FRONT) != 0; + normals_back = (scn->convention & SENC_CONVENTION_NORMAL_BACK) != 0; + ASSERT(normals_back != normals_front); + ASSERT(output_normal_in != ((scn->convention & SENC_CONVENTION_NORMAL_OUTSIDE) != 0)); + ASSERT(darray_ptr_component_descriptor_size_get(connex_components) + <= COMPONENT_MAX__); + cc_descriptors = darray_ptr_component_descriptor_cdata_get(connex_components); enclosures = darray_enclosure_data_get(&desc->enclosures); - triangles_in = darray_triangle_in_cdata_get(&desc->scene->triangles_in); - positions = darray_position_cdata_get(&desc->scene->vertices); - /* Set some enclosure data */ - side_counts = MEM_CALLOC(alloc, desc->enclosures_count, sizeof(side_id_t)); - if(!side_counts) { - res = RES_MEM_ERR; - goto error; - } - FOR_EACH(e, 0, desc->enclosures_count) { - struct enclosure_data* enc = enclosures + e; - ASSERT(e < UINT_MAX); - enc->header.enclosure_id = (unsigned)e; /* Back to API type */ - enc->header.is_infinite = (e == 0); - } - /* Process components to feed enclosures */ - FOR_EACH(c, 0, cc_count) { - const struct cc_descriptor* cc = cc_descriptors + c; - const enclosure_id_t e_id = cc->enclosure_id; - struct enclosure_data* enc = enclosures + e_id; - ASSERT(enc->header.enclosed_medium == MEDIUM_NULL__ /* Unset */ - || enc->header.enclosed_medium == cc->medium); /* Same medium */ - ASSERT(enc->header.enclosed_medium < UINT_MAX); - enc->header.enclosed_medium = (unsigned)cc->medium; /* Back to API type */ - side_counts[e_id] += cc->side_count; - } - side_membership - = MEM_ALLOC(alloc, desc->scene->nutris * sizeof(*side_membership)); - if(!side_membership) { - res = RES_MEM_ERR; - goto error; - } - res = darray_triangle_enc_resize(&desc->triangles_enc, desc->scene->nutris); - if(res != RES_OK) goto error; + triangles_in = darray_triangle_in_cdata_get(&scn->triangles_in); + triangles_comp = darray_triangle_comp_cdata_get(triangles_comp_array); + + #pragma omp single + { + res_T tmp_res = + darray_triangle_enc_resize(&desc->triangles_enc, scn->nutris); + if(tmp_res != RES_OK) *res = tmp_res; + }/* Implicit barrier here. */ + if(*res != RES_OK) return; triangles_enc = darray_triangle_enc_data_get(&desc->triangles_enc); + + /* Build global enclosure information */ + #pragma omp for + for(tt = 0; tt < (int64_t) scn->nutris; tt++) { + trg_id_t t = (trg_id_t)tt; + const component_id_t cf_id = triangles_comp[t].component[SIDE_FRONT]; + const component_id_t cb_id = triangles_comp[t].component[SIDE_BACK]; + const struct cc_descriptor* cf = cc_descriptors[cf_id]; + const struct cc_descriptor* cb = cc_descriptors[cb_id]; + const enclosure_id_t ef_id = cf->enclosure_id; + const enclosure_id_t eb_id = cb->enclosure_id; + ASSERT(triangles_enc[t].enclosure[SIDE_FRONT] == ENCLOSURE_NULL__); + triangles_enc[t].enclosure[SIDE_FRONT] = ef_id; + ASSERT(triangles_enc[t].enclosure[SIDE_BACK] == ENCLOSURE_NULL__); + triangles_enc[t].enclosure[SIDE_BACK] = eb_id; + } + /* Implicit barrier here */ + + /* Resize/push operations on enclosure's fields are valid in the + * openmp block as a given enclosure is processed by a single thread */ htable_vrtx_id_init(alloc, &vtable); - FOR_EACH(e, 0, desc->enclosures_count) { + + ASSERT(desc->enclosures_count <= ENCLOSURE_MAX__); + #pragma omp for schedule(dynamic) nowait + for(ee = 0; ee < (int64_t)desc->enclosures_count; ee++) { + const enclosure_id_t e = (enclosure_id_t)ee; struct enclosure_data* enc = enclosures + e; + trg_id_t fst_idx = 0; + trg_id_t sgd_idx = enc->side_count; trg_id_t t; - memset(side_membership, 0, desc->scene->nutris * sizeof(*side_membership)); - /* Process all CC enclosures_count times to limit memory footprint. */ - FOR_EACH(c, 0, cc_count) { - const struct cc_descriptor* cc = cc_descriptors + c; - const char* cc_membership - = darray_char_cdata_get(&cc->side_membership); - if(cc->enclosure_id != e) continue; - FOR_EACH(t, 0, desc->scene->nutris) side_membership[t] |= cc_membership[t]; + medium_id_t m; + res_T tmp_res = RES_OK; + ASSERT(enc->first_component + < darray_ptr_component_descriptor_size_get(connex_components)); + ASSERT(cc_descriptors[enc->first_component]->cc_id + == enc->first_component); + + if(*res != RES_OK) continue; + ASSERT(e <= UINT_MAX); + enc->header.enclosure_id = (unsigned)e; /* Back to API type */ + ASSERT(cc_descriptors[enc->first_component]->enclosure_id + == enc->header.enclosure_id); + enc->header.is_infinite = (e == 0); + + ASSERT(darray_uchar_size_get(&enc->tmp_enclosed_media) <= UINT_MAX); + ASSERT(enc->header.enclosed_media_count <= scn->nmeds); + OK2(bool_array_of_media_to_darray_media + (&enc->enclosed_media, &enc->tmp_enclosed_media)); + enc->header.enclosed_media_count + = (unsigned)darray_media_size_get(&enc->enclosed_media); + darray_uchar_purge(&enc->tmp_enclosed_media); + + /* Add this enclosure in relevant by-medium lists */ + FOR_EACH(m, 0, enc->header.enclosed_media_count) { + medium_id_t medium = darray_media_data_get(&enc->enclosed_media)[m]; + struct darray_enc_id* enc_ids_by_medium = + darray_enc_ids_array_data_get(&desc->enc_ids_array_by_medium) + medium; + #pragma omp critical + { + tmp_res = darray_enc_id_push_back(enc_ids_by_medium, &e); + } + if(tmp_res != RES_OK) { + *res = tmp_res; + break; + } } - /* Translate membership into a side and vertex lists. */ - OK(darray_triangle_in_reserve(&enc->sides, side_counts[e])); - OK(darray_position_reserve(&enc->vertices, side_counts[e] / 2)); + if(*res != RES_OK) continue; + + /* Build side and vertex lists. */ + OK2(darray_triangle_in_resize(&enc->sides, enc->side_count)); + /* Size is just a int */ + OK2(darray_vrtx_id_reserve(&enc->vertices, + (size_t)(enc->side_count * 0.6))); /* New vertex numbering scheme local to the enclosure */ htable_vrtx_id_clear(&vtable); - ASSERT(desc->scene->nutris - == darray_triangle_in_size_get(&desc->scene->triangles_in)); - /* Proceed in 2 steps; the second step puts at the end the back-faces - * of triangles that also have front-face in the list. */ - FOR_EACH(t, 0, desc->scene->nutris) { + ASSERT(scn->nutris == darray_triangle_in_size_get(&scn->triangles_in)); + /* Put at the end the back-faces of triangles that also have their + * front-face in the list. */ + for(t = TRGSIDE_2_TRG(enc->side_range.first); + t <= TRGSIDE_2_TRG(enc->side_range.last); + t++) + { const struct triangle_in* trg_in = triangles_in + t; - struct triangle_in trg; + struct triangle_in* trg; + unsigned vertice_id[3]; int i; - if(!side_membership[t]) continue; + if(triangles_enc[t].enclosure[SIDE_FRONT] != e + && triangles_enc[t].enclosure[SIDE_BACK] != e) + continue; ++enc->header.unique_triangle_count; + FOR_EACH(i, 0, 3) { - vrtx_id_t* id = htable_vrtx_id_find(&vtable, trg_in->vertice_id + i); - if(id) { - trg.vertice_id[i] = *id; /* Known vertex */ + vrtx_id_t* p_id = htable_vrtx_id_find(&vtable, trg_in->vertice_id + i); + if(p_id) { + vertice_id[i] = *p_id; /* Known vertex */ } else { /* Create new association */ - tmp = htable_vrtx_id_size_get(&vtable); - ASSERT(tmp == darray_position_size_get(&enc->vertices)); + size_t tmp = htable_vrtx_id_size_get(&vtable); + ASSERT(tmp == darray_vrtx_id_size_get(&enc->vertices)); ASSERT(tmp < VRTX_MAX__); - trg.vertice_id[i] = (vrtx_id_t)tmp; - OK(htable_vrtx_id_set(&vtable, trg_in->vertice_id + i, - trg.vertice_id + i)); - OK(darray_position_push_back(&enc->vertices, - positions + trg_in->vertice_id[i])); + vertice_id[i] = (vrtx_id_t)tmp; + OK2(htable_vrtx_id_set(&vtable, trg_in->vertice_id + i, + vertice_id + i)); + OK2(darray_vrtx_id_push_back(&enc->vertices, trg_in->vertice_id + i)); ++enc->header.vertices_count; } } - FOR_EACH(i, 0, 2) trg.medium[i] = trg_in->medium[i]; - if(side_membership[t] & FLAG_FRONT) { - ++enc->header.triangle_count; - OK(darray_triangle_in_push_back(&enc->sides, &trg)); - ASSERT(triangles_enc[t].enclosure[SIDE_FRONT] == ENCLOSURE_NULL__); - triangles_enc[t].enclosure[SIDE_FRONT] = e; - } else if(side_membership[t] & FLAG_BACK) { + ASSERT(triangles_enc[t].enclosure[SIDE_FRONT] == e + || triangles_enc[t].enclosure[SIDE_BACK] == e); + if(triangles_enc[t].enclosure[SIDE_FRONT] == e) { + /* Front side of the original triangle is member of the enclosure */ + int input_normal_in = normals_front; + int revert_triangle = (input_normal_in != output_normal_in); ++enc->header.triangle_count; - triangle_in_flip(&trg); - OK(darray_triangle_in_push_back(&enc->sides, &trg)); - ASSERT(triangles_enc[t].enclosure[SIDE_BACK] == ENCLOSURE_NULL__); - triangles_enc[t].enclosure[SIDE_BACK] = e; + trg = darray_triangle_in_data_get(&enc->sides) + fst_idx++; + FOR_EACH(i, 0, 2) { + int ii = revert_triangle ? 1 - i : i; + trg->medium[i] = trg_in->medium[ii]; + } + trg->global_id = trg_in->global_id; + FOR_EACH(i, 0, 3) { + int ii = revert_triangle ? 2 - i : i; + trg->vertice_id[i] = vertice_id[ii]; + } } - } - FOR_EACH(t, 0, desc->scene->nutris) { - const struct triangle_in* trg_in = triangles_in + t; - struct triangle_in trg; - int i; - if(!((side_membership[t] & FLAG_FRONT) && (side_membership[t] & FLAG_BACK))) - continue; - ++enc->header.triangle_count; - FOR_EACH(i, 0, 3) { - vrtx_id_t* id = htable_vrtx_id_find(&vtable, trg_in->vertice_id + i); - ASSERT(id); - trg.vertice_id[2-i] = *id; /* Known vertex */ + if(triangles_enc[t].enclosure[SIDE_BACK] == e) { + /* Back side of the original triangle is member of the enclosure */ + int input_normal_in = normals_back; + int revert_triangle = (input_normal_in != output_normal_in); + ++enc->header.triangle_count; + /* If both sides are in the enclosure, put the second side at the end */ + trg = darray_triangle_in_data_get(&enc->sides) + + ((triangles_enc[t].enclosure[SIDE_FRONT] == e) ? --sgd_idx : fst_idx++); + FOR_EACH(i, 0, 2) { + int ii = revert_triangle ? 1 - i : i; + trg->medium[i] = trg_in->medium[ii]; + } + trg->global_id = trg_in->global_id; + FOR_EACH(i, 0, 3) { + int ii = revert_triangle ? 2 - i : i; + trg->vertice_id[i] = vertice_id[ii]; + } } - FOR_EACH(i, 0, 2) trg.medium[1-i] = trg_in->medium[i]; - OK(darray_triangle_in_push_back(&enc->sides, &trg)); - ASSERT(triangles_enc[t].enclosure[SIDE_BACK] == ENCLOSURE_NULL__); - triangles_enc[t].enclosure[SIDE_BACK] = e; + if(fst_idx == sgd_idx) break; } - ASSERT(darray_triangle_in_size_get(&enc->sides) == side_counts[e]); - } - -exit: + continue; + tmp_error: + ASSERT(tmp_res != RES_OK); + *res = tmp_res; + } /* No barrier here */ htable_vrtx_id_release(&vtable); - if(side_membership) MEM_RM(alloc, side_membership); - if(side_counts) MEM_RM(alloc, side_counts); - return res; -error: - goto exit; } /******************************************************************************* @@ -1077,26 +1062,25 @@ error: res_T senc_scene_analyze(struct senc_scene* scn, struct senc_descriptor** out_desc) { - res_T res = RES_OK; - unsigned m; struct senc_descriptor* desc = NULL; /* By triangle tmp data */ struct darray_triangle_tmp triangles_tmp; char triangles_tmp_initialized = 0; - /* Arrays of trgside ids, organized by medium */ - struct darray_side_id* side_ids_by_medium = NULL; /* Array of connex components. * They are refered to by arrays of ids. */ - struct darray_cc_descriptor connex_components; + struct darray_ptr_component_descriptor connex_components; char connex_components_initialized = 0; - /* Triangle neighbourhood by edge. */ - struct darray_neighbourhood neighbourhood_by_edge; - char neighbourhood_by_edge_initialized = 0; /* Store by-triangle components */ struct darray_triangle_comp triangles_comp; char triangles_comp_initialized = 0; /* Array of triangle sides. */ struct trgside* trgsides = NULL; + struct s3d_scene_view* s3d_view = NULL; + /* Atomic counters to share beetwen threads */ + ATOMIC component_count = 0; + ATOMIC next_enclosure_id = 1; + res_T res = RES_OK; + res_T res2 = RES_OK; if(!scn || !out_desc) return RES_BAD_ARG; @@ -1110,26 +1094,8 @@ senc_scene_analyze(struct senc_scene* scn, struct senc_descriptor** out_desc) darray_triangle_tmp_init(scn->dev->allocator, &triangles_tmp); triangles_tmp_initialized = 1; - darray_neighbourhood_init(scn->dev->allocator, &neighbourhood_by_edge); - neighbourhood_by_edge_initialized = 1; - - /* Step 1: list edges and connect triangles to edges */ - res = scan_edges(scn, &neighbourhood_by_edge, &triangles_tmp); - if(res != RES_OK) { - log_err(scn->dev, "%s: could not scan edges.\n", FUNC_NAME); - goto error; - } - side_ids_by_medium - = MEM_ALLOC(scn->dev->allocator, scn->nmeds * sizeof(struct darray_uint)); - if(!side_ids_by_medium) { - res = RES_MEM_ERR; - goto error; - } - FOR_EACH(m, 0, scn->nmeds) { - struct darray_side_id* const s = side_ids_by_medium + m; - darray_side_id_init(scn->dev->allocator, s); - } + OK(darray_triangle_tmp_resize(&triangles_tmp, scn->nutris)); trgsides = MEM_CALLOC(scn->dev->allocator, 2 * scn->nutris, sizeof(struct trgside)); if(!trgsides) { @@ -1137,77 +1103,143 @@ senc_scene_analyze(struct senc_scene* scn, struct senc_descriptor** out_desc) goto error; } - /* Step 2: link triangles by neighbourhood */ - res = link_neighbours(scn, trgsides, &neighbourhood_by_edge, - &triangles_tmp, side_ids_by_medium); - if(res != RES_OK) { - log_err(scn->dev, "%s: could not link neighbours.\n", FUNC_NAME); - goto error; - } + /* The end of the analyze is multithreaded */ + ASSERT(scn->dev->nthreads > 0); + #pragma omp parallel num_threads(scn->dev->nthreads) + { + /* Step 1: build neighbourhoods */ + collect_and_link_neighbours(scn, trgsides, &triangles_tmp, &res); + /* No barrier at the end of step 1: data used in step 1 cannot be + * released / data produced by step 1 cannot be used + * until next sync point */ + + /* The first thread here allocates some data. + * Barrier needed at the end to ensure data created before any use. */ + #pragma omp single + { + res_T tmp_res = RES_OK; + darray_ptr_component_descriptor_init(scn->dev->allocator, + &connex_components); + connex_components_initialized = 1; + /* Just a hint; to limit contention */ + OK2(darray_ptr_component_descriptor_reserve(&connex_components, + 2 * scn->nmeds)); + darray_triangle_comp_init(scn->dev->allocator, &triangles_comp); + triangles_comp_initialized = 1; + OK2(darray_triangle_comp_resize(&triangles_comp, scn->nutris)); + tmp_error: + if(tmp_res != RES_OK) res2 = tmp_res; + } + /* Implicit barrier here: constraints on step 1 data are now met */ - darray_neighbourhood_release(&neighbourhood_by_edge); - neighbourhood_by_edge_initialized = 0; - darray_cc_descriptor_init(scn->dev->allocator, &connex_components); - connex_components_initialized = 1; - darray_triangle_comp_init(scn->dev->allocator, &triangles_comp); - triangles_comp_initialized = 1; - OK(darray_triangle_comp_resize(&triangles_comp, scn->nutris)); - - /* Step 3: extract triangle connex components */ - res = extract_connex_components(desc, trgsides, &connex_components, - &triangles_tmp, &triangles_comp, side_ids_by_medium); - if(res != RES_OK) { - log_err(scn->dev, - "%s: could not extract connex components from scene.\n", FUNC_NAME); - goto error; - } + if(res != RES_OK || res2 != RES_OK) { + #pragma omp single nowait + { + if(res != RES_OK) { + log_err(scn->dev, + "%s: could not build neighbourhoods from scene.\n", FUNC_NAME); + } else { + res = res2; + } + } + goto error_; + } + + /* Step 2: extract triangle connex components */ + extract_connex_components(desc, trgsides, &connex_components, + &triangles_tmp, &triangles_comp, &s3d_view, &component_count, &res); + /* No barrier at the end of step 2: data used in step 2 cannot be + * released / data produced by step 2 cannot be used + * until next sync point */ - darray_triangle_tmp_release(&triangles_tmp); - triangles_tmp_initialized = 0; - if(side_ids_by_medium) { - FOR_EACH(m, 0, scn->nmeds) { - struct darray_side_id* cc = side_ids_by_medium + m; - darray_side_id_release(cc); + #pragma omp barrier + /* Constraints on step 2 data are now met */ + + if(res != RES_OK) { + #pragma omp single nowait + { + log_err(scn->dev, + "%s: could not extract connex components from scene.\n", FUNC_NAME); + } /* No barrier here */ + goto error_; } - MEM_RM(scn->dev->allocator, side_ids_by_medium); - } - side_ids_by_medium = NULL; - - /* Step 4: group components */ - res = group_connex_components(desc, trgsides, &triangles_comp, - &connex_components); - if(res != RES_OK) { - log_err(scn->dev, - "%s: could not group connex components from scene.\n", FUNC_NAME); - goto error; - } - darray_triangle_comp_release(&triangles_comp); - triangles_comp_initialized = 0; + /* One thread releases some data before going to step 3, + * the others go to step 3 without sync */ + #pragma omp single nowait + { + darray_triangle_tmp_release(&triangles_tmp); + triangles_tmp_initialized = 0; + } /* No barrier here */ + + /* Step 3: group components */ + group_connex_components(desc, trgsides, &triangles_comp, + &connex_components, s3d_view, &next_enclosure_id, &res); + /* Barrier at the end of step 3: data used in step 3 can be released / + * data produced by step 3 can be used */ + + if(res != RES_OK) { + #pragma omp single nowait + { + log_err(scn->dev, + "%s: could not group connex components from scene.\n", FUNC_NAME); + } + goto error_; + } - /* Build result. */ - res = build_result(desc, &connex_components); - if(res != RES_OK) { - log_err(scn->dev, "%s: could not build result.\n", FUNC_NAME); - goto error; - } + /* One thread releases some data before going to step 4, + * the others go to step 4 without sync */ + #pragma omp single nowait + { + if(s3d_view) S3D(scene_view_ref_put(s3d_view)); + s3d_view = NULL; + } /* No barrier here */ + + /* Step 4: Build result */ + build_result(desc, &connex_components, &triangles_comp, &res); + /* No barrier at the end of step 4: data used in step 4 cannot be + * released / data produced by step 4 cannot be used + * until next sync point */ + + #pragma omp barrier + /* Constraints on step 4 data are now met */ + + if(res != RES_OK) { + #pragma omp single nowait + { + log_err(scn->dev, "%s: could not build result.\n", FUNC_NAME); + } + goto error_; + } + + /* Some threads release data */ + #pragma omp sections nowait + { + #pragma omp section + { + custom_darray_ptr_component_descriptor_release(&connex_components); + connex_components_initialized = 0; + } + #pragma omp section + { + darray_triangle_comp_release(&triangles_comp); + triangles_comp_initialized = 0; + } + } /* No barrier here */ +error_: + ; + } /* Implicit barrier here */ + if(res != RES_OK) goto error; exit: if(connex_components_initialized) - darray_cc_descriptor_release(&connex_components); - if(neighbourhood_by_edge_initialized) - darray_neighbourhood_release(&neighbourhood_by_edge); + custom_darray_ptr_component_descriptor_release(&connex_components); + if(s3d_view) S3D(scene_view_ref_put(s3d_view)); if(triangles_tmp_initialized) darray_triangle_tmp_release(&triangles_tmp); if(triangles_comp_initialized) darray_triangle_comp_release(&triangles_comp); - if(side_ids_by_medium) { - FOR_EACH(m, 0, scn->nmeds) { - struct darray_side_id* cc = side_ids_by_medium + m; - darray_side_id_release(cc); - } - MEM_RM(scn->dev->allocator, side_ids_by_medium); - } if(trgsides) MEM_RM(scn->dev->allocator, trgsides); if(desc) *out_desc = desc; + return res; error: if(desc) SENC(descriptor_ref_put(desc)); diff --git a/src/senc_scene_analyze_c.h b/src/senc_scene_analyze_c.h @@ -20,31 +20,9 @@ #include "senc_internal_types.h" #include <rsys/mem_allocator.h> -#include <rsys/dynamic_array_char.h> #include <rsys/hash_table.h> #include <rsys/double3.h> - -/* This one is used as an index to arrays */ -enum side_id { - SIDE_FRONT = 0, - SIDE_BACK = 1 -}; - -/* This one is used as flag */ -enum side_flag { - FLAG_FRONT = BIT(0), - FLAG_BACK = BIT(1) -}; - -enum list_id { - FLAG_NO_LIST = 0, - FLAG_LIST_BY_MEDIUM = BIT(1), - FLAG_LIST_COMPONENT = BIT(2), - FLAG_WAITING_STACK = BIT(3), - FLAG_ANY_LIST = 0xFF -}; - /* Triangle edge struct and basic functions */ struct trg_edge { vrtx_id_t vrtx0, vrtx1; @@ -63,10 +41,10 @@ set_edge (const vrtx_id_t vrtx0, const vrtx_id_t vrtx1, struct trg_edge* edge, - char* reversed) + unsigned char* reversed) { ASSERT(edge && reversed && vrtx0 != vrtx1); - ASSERT(*reversed == CHAR_MAX); /* Should not be already set. */ + ASSERT(*reversed == UCHAR_MAX); /* Should not be already set. */ if(vrtx0 < vrtx1) { edge->vrtx0 = vrtx0; edge->vrtx1 = vrtx1; @@ -93,159 +71,83 @@ struct trgside { /* Id of this trgside's medium */ medium_id_t medium; /* The list containing the trgside; made of enum list_id flags */ - char list_id; + unsigned char list_id; /* Implicit information that we don't need to store: * - triangle_id * - side * This is due to the memory layout of the elt darray: * front(trg_0), back(trg_0), front(trg_1), back(trg_1), ... */ - -#ifndef NDEBUG - component_id_t member_of_cc; -#endif }; -static FINLINE trg_id_t -TRGSIDE_2_TRG(side_id_t s) { - ASSERT(((size_t)s >> 1) <= TRG_MAX__); - return s >> 1; } - -static FINLINE int -TRGSIDE_IS_FRONT(side_id_t s) { - return (s & 1) == 0; -} - -static FINLINE enum side_id -TRGSIDE_2_SIDE(side_id_t s) { - return (s & 1) ? SIDE_BACK : SIDE_FRONT; -} - -static FINLINE side_id_t -TRGIDxSIDE_2_TRGSIDE(trg_id_t t, enum side_id i) { - ASSERT((((size_t)t << 1) | (i == SIDE_BACK)) < SIDE_MAX__); - ASSERT(i == SIDE_FRONT || i == SIDE_BACK); - return (side_id_t)((t << 1) | (i == SIDE_BACK)); -} - -static FINLINE side_id_t -TRGSIDE_OPPOSITE(side_id_t s) { - return TRGIDxSIDE_2_TRGSIDE(TRGSIDE_2_TRG(s), - TRGSIDE_IS_FRONT(s) ? SIDE_BACK : SIDE_FRONT); -} +#define DARRAY_NAME side_id +#define DARRAY_DATA side_id_t +#include <rsys/dynamic_array.h> /* Descriptors for connex component. * Define lists of trg sides starting from a given head. * Also keeps the maximum z info of the component - * along with the associated triangle id, - * a star3D id */ -#define CC_ID_NONE USHRT_MAX -#define CC_GROUP_ROOT_NONE USHRT_MAX -#define CC_GROUP_ROOT_INFINITE (USHRT_MAX-1) -#define CC_GROUP_ID_NONE USHRT_MAX + * and the list of media found */ struct cc_descriptor { - double max_vrtx[3]; - double max_z_nz; - side_id_t max_z_side_id; - vrtx_id_t max_z_vrtx_id; + /* Does this component is an outer border of an enclosure or an inner one? */ + char is_outer_border; + vrtx_id_t max_z_vrtx_id; /* id of the vrtx with max z value */ side_id_t side_count; - medium_id_t medium; + /* Used when grouping components to form enclosures */ component_id_t cc_id; component_id_t cc_group_root; enclosure_id_t enclosure_id; - /* TODO: use only 1 bit per side (now 1 char per triangle) */ - struct darray_char side_membership; + /* Range of sides member of this component */ + struct side_range side_range; + /* Media used by this component */ + unsigned char* media; }; extern const struct cc_descriptor CC_DESCRIPTOR_NULL; static FINLINE void -cc_descriptor_init(struct mem_allocator* alloc, struct cc_descriptor* data) +cc_descriptor_init + (struct mem_allocator* alloc, + struct cc_descriptor* data) { ASSERT(data); + (void)alloc; *data = CC_DESCRIPTOR_NULL; - darray_char_init(alloc, &data->side_membership); } static FINLINE void -cc_descriptor_release(struct cc_descriptor* data) +ptr_component_descriptor_init + (struct mem_allocator* alloc, + struct cc_descriptor** data) { + (void)alloc; ASSERT(data); - darray_char_release(&data->side_membership); + *data = NULL; } -static FINLINE void -cc_descriptor_clear(struct cc_descriptor* data) -{ - d3_set(data->max_vrtx, CC_DESCRIPTOR_NULL.max_vrtx); - data->max_z_nz = CC_DESCRIPTOR_NULL.max_z_nz; - data->max_z_side_id = CC_DESCRIPTOR_NULL.max_z_side_id; - data->max_z_vrtx_id = CC_DESCRIPTOR_NULL.max_z_vrtx_id; - data->side_count = CC_DESCRIPTOR_NULL.side_count; - data->medium = CC_DESCRIPTOR_NULL.medium; - data->cc_id = CC_DESCRIPTOR_NULL.cc_id; - data->cc_group_root = CC_DESCRIPTOR_NULL.cc_group_root; - data->enclosure_id = CC_DESCRIPTOR_NULL.enclosure_id; - darray_char_clear(&data->side_membership); -} +#define DARRAY_NAME ptr_component_descriptor +#define DARRAY_DATA struct cc_descriptor* +#define DARRAY_FUNCTOR_INIT ptr_component_descriptor_init +#include <rsys/dynamic_array.h> +/* Need allocator to free array elts: cannot rely on standard + * darray release stuff */ static FINLINE void -cc_descriptor_purge(struct cc_descriptor* data) -{ - d3_set(data->max_vrtx, CC_DESCRIPTOR_NULL.max_vrtx); - data->max_z_nz = CC_DESCRIPTOR_NULL.max_z_nz; - data->max_z_side_id = CC_DESCRIPTOR_NULL.max_z_side_id; - data->max_z_vrtx_id = CC_DESCRIPTOR_NULL.max_z_vrtx_id; - data->side_count = CC_DESCRIPTOR_NULL.side_count; - data->medium = CC_DESCRIPTOR_NULL.medium; - data->cc_id = CC_DESCRIPTOR_NULL.cc_id; - data->cc_group_root = CC_DESCRIPTOR_NULL.cc_group_root; - data->enclosure_id = CC_DESCRIPTOR_NULL.enclosure_id; - darray_char_purge(&data->side_membership); -} - -static FINLINE res_T -cc_descriptor_copy(struct cc_descriptor* dst, const struct cc_descriptor* src) +custom_darray_ptr_component_descriptor_release + (struct darray_ptr_component_descriptor* array) { - ASSERT(dst && src); - d3_set(dst->max_vrtx, src->max_vrtx); - dst->max_z_nz = src->max_z_nz; - dst->max_z_side_id = src->max_z_side_id; - dst->max_z_vrtx_id = src->max_z_vrtx_id; - dst->side_count = src->side_count; - dst->medium = src->medium; - dst->cc_id = src->cc_id; - dst->cc_group_root = src->cc_group_root; - dst->enclosure_id = src->enclosure_id; - return darray_char_copy(&dst->side_membership, &src->side_membership); -} - -static FINLINE res_T -cc_descriptor_copy_and_release - (struct cc_descriptor* dst, - struct cc_descriptor* src) -{ - ASSERT(dst && src); - d3_set(dst->max_vrtx, src->max_vrtx); - dst->max_z_nz = src->max_z_nz; - dst->max_z_side_id = src->max_z_side_id; - dst->max_z_vrtx_id = src->max_z_vrtx_id; - dst->side_count = src->side_count; - dst->medium = src->medium; - dst->cc_id = src->cc_id; - dst->cc_group_root = src->cc_group_root; - dst->enclosure_id = src->enclosure_id; - return darray_char_copy_and_release(&dst->side_membership, - &src->side_membership); + size_t c, cc_count; + struct cc_descriptor** components; + if(!array) return; + cc_count = darray_ptr_component_descriptor_size_get(array); + components = darray_ptr_component_descriptor_data_get(array); + FOR_EACH(c, 0, cc_count) { + if(!components[c]) continue; + MEM_RM(array->allocator, components[c]->media); + MEM_RM(array->allocator, components[c]); + } + darray_ptr_component_descriptor_release(array); } -#define DARRAY_NAME cc_descriptor -#define DARRAY_DATA struct cc_descriptor -#define DARRAY_FUNCTOR_INIT cc_descriptor_init -#define DARRAY_FUNCTOR_RELEASE cc_descriptor_release -#define DARRAY_FUNCTOR_COPY cc_descriptor_copy -#define DARRAY_FUNCTOR_COPY_AND_RELEASE cc_descriptor_copy_and_release -#include <rsys/dynamic_array.h> - /* Triangle information. * Depending on lifespan, information is kept in different places: * - triangle_in for user provided information (kept in scene) @@ -257,9 +159,7 @@ cc_descriptor_copy_and_release struct triangle_tmp { /* Are the edges of the triangle defined in the same order than * the edges they are linked to? */ - char reversed_edge[3]; - /* tmp data used to find the +Z-most vertex of components */ - char max_z_vrtx_id; + unsigned char reversed_edge[3]; double max_z; }; @@ -267,10 +167,9 @@ struct triangle_tmp { static FINLINE void triangle_tmp_init(struct mem_allocator* alloc, struct triangle_tmp* trg) { int i; - (void) alloc; + (void)alloc; ASSERT(trg); - FOR_EACH(i, 0, 3) trg->reversed_edge[i] = CHAR_MAX; - trg->max_z_vrtx_id = CHAR_MAX; + FOR_EACH(i, 0, 3) trg->reversed_edge[i] = UCHAR_MAX; trg->max_z = -DBL_MAX; } #define DARRAY_FUNCTOR_INIT triangle_tmp_init @@ -287,10 +186,13 @@ triangle_tmp_init(struct mem_allocator* alloc, struct triangle_tmp* trg) { #include <rsys/hash_table.h> struct neighbour_info { + double angle; trg_id_t trg_id; /* Rank of the edge in the triangle (in [0 2]) */ - char edge_rank; - double angle; + unsigned char common_edge_rank; + /* Does the geometrical normal point towards the next neighbour + * (if not, it points towards the previous one)? */ + unsigned char normal_toward_next_neighbour; }; #define DARRAY_NAME neighbour #define DARRAY_DATA struct neighbour_info diff --git a/src/senc_scene_c.h b/src/senc_scene_c.h @@ -24,6 +24,11 @@ struct mem_allocator; +#define HTABLE_NAME vrtx_id +#define HTABLE_KEY vrtx_id_t +#define HTABLE_DATA vrtx_id_t +#include <rsys/hash_table.h> + union double3 { struct { double x, y, z; @@ -43,16 +48,19 @@ struct triangle_in { vrtx_id_t vertice_id[3]; /* Ids of this triangle's media */ medium_id_t medium[2]; + /* Triangle index in user world (that is regardless of deduplication). */ + unsigned global_id; }; #ifndef NDEBUG static FINLINE void triangle_in_init(struct mem_allocator* alloc, struct triangle_in* trg) { int i; - (void) alloc; + (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__; + trg->global_id = 0; } #define DARRAY_FUNCTOR_INIT triangle_in_init #endif @@ -74,7 +82,7 @@ triangle_in_flip(struct triangle_in* trg) { trg->medium[1] = m; } -FINLINE int +static FINLINE int vrtx_eq(const union double3* v1, const union double3* v2) { ASSERT(v1 && v2); @@ -100,7 +108,7 @@ union vrtx_id3 { vrtx_id_t vec[3]; }; -FINLINE char /* Return 1 if reversed */ +static FINLINE char /* Return 1 if reversed */ trg_make_key(union vrtx_id3* k, const vrtx_id_t t[3]) { ASSERT(t); @@ -135,21 +143,18 @@ trg_make_key(union vrtx_id3* k, const vrtx_id_t t[3]) k->vec[1] = t[0]; k->vec[2] = t[1]; return 0; - } - else { + } else { k->vec[1] = t[1]; k->vec[2] = t[0]; return 1; } - } - else { + } else { k->vec[0] = t[1]; if(t[0] < t[2]) { k->vec[1] = t[0]; k->vec[2] = t[2]; return 1; - } - else { + } else { k->vec[1] = t[2]; k->vec[2] = t[0]; return 0; @@ -157,7 +162,7 @@ trg_make_key(union vrtx_id3* k, const vrtx_id_t t[3]) } } -FINLINE int +static FINLINE int trg_key_eq(const union vrtx_id3* k1, const union vrtx_id3* k2) { ASSERT(k1 && k2); @@ -174,7 +179,26 @@ trg_key_eq(const union vrtx_id3* k1, const union vrtx_id3* k2) #define HTABLE_KEY_FUNCTOR_EQ trg_key_eq #include <rsys/hash_table.h> +struct side_range { + side_id_t first, last; +}; +static FINLINE void +side_range_init(struct mem_allocator* alloc, struct side_range* data) +{ + ASSERT(data); + (void)alloc; + data->first = SIDE_NULL__; + data->last = 0; +} +#define DARRAY_NAME side_range +#define DARRAY_DATA struct side_range +#define DARRAY_FUNCTOR_INIT side_range_init +#include <rsys/dynamic_array.h> + struct senc_scene { + /* Front / Back sides convention */ + enum senc_convention convention; + /* Triangle information as given by user; no duplicates here */ struct darray_triangle_in triangles_in; @@ -196,6 +220,7 @@ struct senc_scene { trg_id_t ntris, nutris; /* Trg count, unique trg count */ vrtx_id_t nverts, nuverts; /* Vrtx count, unique vrtx count */ medium_id_t nmeds; + struct darray_side_range media_use; ref_T ref; struct senc_device* dev; diff --git a/src/test_senc_cube_behind_cube.c b/src/test_senc_cube_behind_cube.c @@ -26,6 +26,7 @@ main(int argc, char** argv) struct senc_device* dev = NULL; struct senc_scene* scn = NULL; struct context ctx; + unsigned i, ecount, maxm; (void)argc, (void)argv; CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); @@ -33,7 +34,8 @@ main(int argc, char** argv) (NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev) == RES_OK); /* Create the scene */ - CHK(senc_scene_create(dev, 2, &scn) == RES_OK); + CHK(senc_scene_create(dev, + SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_INSIDE, &scn) == RES_OK); ctx.positions = box_vertices; ctx.indices = box_indices; @@ -45,8 +47,8 @@ main(int argc, char** argv) ctx.back_media = medium1; /* First cube */ - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); /* +Z from the first cube, * big enough to prevent rays from the first cube to miss this one */ @@ -54,24 +56,57 @@ main(int argc, char** argv) ctx.scale = 5; /* Second cube */ - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); CHK(senc_scene_analyze(scn, &desc) == RES_OK); + CHK(senc_descriptor_get_enclosure_count(desc, &ecount) == RES_OK); + CHK(ecount == 3); + + FOR_EACH(i, 0, ecount) { + struct senc_enclosure* enclosure; + struct senc_enclosure_header header; + CHK(senc_descriptor_get_enclosure(desc, i, &enclosure) == RES_OK); + CHK(senc_enclosure_get_header(enclosure, &header) == RES_OK); + ASSERT(header.enclosed_media_count == 1); + CHK(senc_enclosure_ref_put(enclosure) == RES_OK); + } + + CHK(senc_descriptor_get_max_medium(desc, &maxm) == RES_OK); + CHK(maxm == 1); + check_desc(desc); + /* Even further in +Z, even bigger */ d3(ctx.offset, -3, -3, 30); ctx.scale = 7; + /* Front/back media have been exchanged: external enclosure shows 2 media */ ctx.front_media = medium1; ctx.back_media = medium0; /* Third cube */ - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); if(desc) CHK(senc_descriptor_ref_put(desc) == RES_OK); desc = NULL; - CHK(senc_scene_analyze(scn, &desc) == RES_BAD_ARG); + CHK(senc_scene_analyze(scn, &desc) == RES_OK); + + CHK(senc_descriptor_get_enclosure_count(desc, &ecount) == RES_OK); + CHK(ecount == 4); + + FOR_EACH(i, 0, ecount) { + struct senc_enclosure* enclosure; + struct senc_enclosure_header header; + CHK(senc_descriptor_get_enclosure(desc, i, &enclosure) == RES_OK); + CHK(senc_enclosure_get_header(enclosure, &header) == RES_OK); + ASSERT(header.enclosed_media_count == (header.is_infinite ? 2u : 1u)); + CHK(senc_enclosure_ref_put(enclosure) == RES_OK); + } + + CHK(senc_descriptor_get_max_medium(desc, &maxm) == RES_OK); + CHK(maxm == 1); + check_desc(desc); CHK(senc_scene_ref_put(scn) == RES_OK); CHK(senc_device_ref_put(dev) == RES_OK); diff --git a/src/test_senc_cube_in_cube.c b/src/test_senc_cube_in_cube.c @@ -26,6 +26,7 @@ main(int argc, char** argv) struct senc_device* dev = NULL; struct senc_scene* scn = NULL; struct context ctx; + unsigned i, ecount, maxm; (void)argc, (void)argv; CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); @@ -33,7 +34,8 @@ main(int argc, char** argv) (NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev) == RES_OK); /* Create the scene */ - CHK(senc_scene_create(dev, 2, &scn) == RES_OK); + CHK(senc_scene_create(dev, + SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_INSIDE, &scn) == RES_OK); ctx.positions = box_vertices; ctx.indices = box_indices; @@ -47,8 +49,8 @@ main(int argc, char** argv) ctx.back_media = medium1; /* First cube */ - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); d3(ctx.offset, -1, -1, -1); ctx.scale = 3; @@ -57,27 +59,51 @@ main(int argc, char** argv) ctx.reverse_vrtx = 1; /* Second cube */ - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); CHK(senc_scene_analyze(scn, &desc) == RES_OK); + CHK(senc_descriptor_get_enclosure_count(desc, &ecount) == RES_OK); + CHK(ecount == 3); + + FOR_EACH(i, 0, ecount) { + struct senc_enclosure* enclosure; + struct senc_enclosure_header header; + CHK(senc_descriptor_get_enclosure(desc, i, &enclosure) == RES_OK); + CHK(senc_enclosure_get_header(enclosure, &header) == RES_OK); + ASSERT(header.enclosed_media_count == 1); + CHK(senc_enclosure_ref_put(enclosure) == RES_OK); + } + + CHK(senc_descriptor_get_max_medium(desc, &maxm) == RES_OK); + CHK(maxm == 1); + check_desc(desc); + d3(ctx.offset, -4, -4, -4); ctx.scale = 10; ctx.reverse_vrtx = 1; ctx.reverse_med = 1; /* Biggest cube exterior is medium 1 */ ctx.front_media = medium1; - /* Biggest cube interior is medium 0 */ - ctx.back_media = medium0; /* mismatch with cube 2 */ + /* Biggest cube interior is medium 0 + * interior/exterior media have been exchanged: external enclosure shows 2 media */ + ctx.back_media = medium0; /* Third cube */ - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); if(desc) CHK(senc_descriptor_ref_put(desc) == RES_OK); desc = NULL; - CHK(senc_scene_analyze(scn, &desc) == RES_BAD_ARG); + CHK(senc_scene_analyze(scn, &desc) == RES_OK); + + CHK(senc_descriptor_get_enclosure_count(desc, &ecount) == RES_OK); + CHK(ecount == 4); + + CHK(senc_descriptor_get_max_medium(desc, &maxm) == RES_OK); + CHK(maxm == 1); + check_desc(desc); CHK(senc_scene_ref_put(scn) == RES_OK); CHK(senc_device_ref_put(dev) == RES_OK); diff --git a/src/test_senc_cube_on_cube.c b/src/test_senc_cube_on_cube.c @@ -13,11 +13,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#define _POSIX_C_SOURCE 200112L /* snprintf */ + #include "senc.h" #include "test_senc_utils.h" #include <rsys/double3.h> +#include <stdio.h> + /* Z ^ @@ -53,9 +57,10 @@ main(int argc, char** argv) (NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev) == RES_OK); /* Create the scene */ - CHK(senc_scene_create(dev, 3, &scn) == RES_OK); + CHK(senc_scene_create(dev, + SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_INSIDE, &scn) == RES_OK); - ctx.positions = box_vertices; + ctx.positions = cube_vertices; /* Need true cubes for cubes #1 and #2 */ ctx.indices = box_indices; ctx.scale = 1; ctx.reverse_vrtx = 0; @@ -68,8 +73,8 @@ main(int argc, char** argv) ctx.back_media = medium0; /* Add cube #1 */ - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, + NULL, nvertices, get_position, &ctx) == RES_OK); d3(ctx.offset, 1, 1, 1); ctx.scale = 1; @@ -80,9 +85,10 @@ main(int argc, char** argv) ctx.back_media = medium0; /* Add cube #2 (has a duplicate face with cube #1) */ - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, + NULL, nvertices, get_position, &ctx) == RES_OK); + ctx.positions = box_vertices; /* Can use distorded cube for cube #3 */ d3(ctx.offset, 0, 0, 0); ctx.scale = 4; ctx.reverse_vrtx = 1; @@ -93,8 +99,8 @@ main(int argc, char** argv) ctx.back_media = medium1; /* Add cube #3 */ - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, + NULL, nvertices, get_position, &ctx) == RES_OK); CHK(senc_scene_analyze(scn, &desc) == RES_OK); diff --git a/src/test_senc_descriptor.c b/src/test_senc_descriptor.c @@ -19,8 +19,6 @@ #include <rsys/float3.h> #include <rsys/double3.h> -#include <star/s3d.h> - int main(int argc, char** argv) { @@ -30,18 +28,19 @@ main(int argc, char** argv) struct senc_descriptor* desc = NULL; struct senc_enclosure* enc = NULL; struct context ctx; - unsigned count; + unsigned count, maxm; unsigned indices[3]; double coord[3]; unsigned media[2]; unsigned enclosures[2]; - (void) argc, (void) argv; + (void)argc, (void)argv; CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); CHK(senc_device_create(NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev) == RES_OK); - CHK(senc_scene_create(dev, 2, &scn) == RES_OK); + CHK(senc_scene_create(dev, + SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_INSIDE, &scn) == RES_OK); /* A 3D cube */ ctx.positions = box_vertices; @@ -53,8 +52,8 @@ main(int argc, char** argv) ctx.front_media = medium0; ctx.back_media = medium1; - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); CHK(senc_scene_analyze(scn, &desc) == RES_OK); @@ -63,6 +62,13 @@ main(int argc, char** argv) CHK(senc_descriptor_ref_put(NULL) == RES_BAD_ARG); CHK(senc_descriptor_ref_put(desc) == RES_OK); + CHK(senc_descriptor_get_max_medium(NULL, &maxm) == RES_BAD_ARG); + CHK(senc_descriptor_get_max_medium(desc, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_max_medium(NULL, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_max_medium(desc, &maxm) == RES_OK); + + CHK(maxm == 1); + CHK(senc_descriptor_get_enclosure_count(NULL, &count) == RES_BAD_ARG); CHK(senc_descriptor_get_enclosure_count(desc, NULL) == RES_BAD_ARG); CHK(senc_descriptor_get_enclosure_count(NULL, NULL) == RES_BAD_ARG); @@ -70,27 +76,57 @@ main(int argc, char** argv) CHK(count == 2); + CHK(senc_descriptor_get_enclosure_count_by_medium(NULL, 0, &count) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_count_by_medium(desc, 9, &count) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_count_by_medium(desc, 0, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_count_by_medium(NULL, 9, &count) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_count_by_medium(NULL, 0, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_count_by_medium(desc, 9, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_count_by_medium(NULL, 9, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_count_by_medium(desc, 0, &count) == RES_OK); + + CHK(count == 1); + CHK(senc_descriptor_get_enclosure(NULL, 0, &enc) == RES_BAD_ARG); - CHK(senc_descriptor_get_enclosure(desc, count, &enc) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure(desc, 9, &enc) == RES_BAD_ARG); CHK(senc_descriptor_get_enclosure(desc, 0, NULL) == RES_BAD_ARG); - CHK(senc_descriptor_get_enclosure(NULL, count, &enc) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure(NULL, 9, &enc) == RES_BAD_ARG); CHK(senc_descriptor_get_enclosure(NULL, 0, NULL) == RES_BAD_ARG); - CHK(senc_descriptor_get_enclosure(desc, count, NULL) == RES_BAD_ARG); - CHK(senc_descriptor_get_enclosure(NULL, count, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure(desc, 9, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure(NULL, 9, NULL) == RES_BAD_ARG); CHK(senc_descriptor_get_enclosure(desc, 0, &enc) == RES_OK); + CHK(senc_enclosure_ref_put(enc) == RES_OK); + + CHK(senc_descriptor_get_enclosure_by_medium(desc, 0, 0, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_by_medium(desc, 0, 9, &enc) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_by_medium(desc, 0, 9, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_by_medium(desc, 9, 0, &enc) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_by_medium(desc, 9, 0, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_by_medium(desc, 9, 9, &enc) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_by_medium(desc, 9, 9, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_by_medium(NULL, 0, 0, &enc) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_by_medium(NULL, 0, 0, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_by_medium(NULL, 0, 9, &enc) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_by_medium(NULL, 0, 9, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_by_medium(NULL, 9, 0, &enc) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_by_medium(NULL, 9, 0, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_by_medium(NULL, 9, 9, &enc) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_by_medium(NULL, 9, 9, NULL) == RES_BAD_ARG); + CHK(senc_descriptor_get_enclosure_by_medium(desc, 0, 0, &enc) == RES_OK); + CHK(senc_descriptor_get_global_vertices_count(NULL, &count) == RES_BAD_ARG); CHK(senc_descriptor_get_global_vertices_count(desc, NULL) == RES_BAD_ARG); CHK(senc_descriptor_get_global_vertices_count(desc, &count) == RES_OK); - CHK(count == box_nvertices); + CHK(count == nvertices); CHK(senc_descriptor_get_global_triangles_count(NULL, &count) == RES_BAD_ARG); CHK(senc_descriptor_get_global_triangles_count(desc, NULL) == RES_BAD_ARG); CHK(senc_descriptor_get_global_triangles_count(desc, &count) == RES_OK); - CHK(count == box_ntriangles); + CHK(count == ntriangles); CHK(senc_descriptor_get_global_triangle(NULL, 0, indices) == RES_BAD_ARG); - CHK(senc_descriptor_get_global_triangle(NULL, box_ntriangles, indices) + CHK(senc_descriptor_get_global_triangle(NULL, ntriangles, indices) == RES_BAD_ARG); CHK(senc_descriptor_get_global_triangle(desc, 0, NULL) == RES_BAD_ARG); CHK(senc_descriptor_get_global_triangle(desc, 0, indices) == RES_OK); @@ -99,7 +135,7 @@ main(int argc, char** argv) && indices[2] == box_indices[2]); CHK(senc_descriptor_get_global_vertex(NULL, 0, coord) == RES_BAD_ARG); - CHK(senc_descriptor_get_global_vertex(NULL, box_nvertices, coord) + CHK(senc_descriptor_get_global_vertex(NULL, nvertices, coord) == RES_BAD_ARG); CHK(senc_descriptor_get_global_vertex(desc, 0, NULL) == RES_BAD_ARG); CHK(senc_descriptor_get_global_vertex(desc, 0, coord) == RES_OK); @@ -109,7 +145,7 @@ main(int argc, char** argv) CHK(senc_descriptor_get_global_triangle_media(NULL, 0, media) == RES_BAD_ARG); - CHK(senc_descriptor_get_global_triangle_media(NULL, box_nvertices, media) + CHK(senc_descriptor_get_global_triangle_media(NULL, nvertices, media) == RES_BAD_ARG); CHK(senc_descriptor_get_global_triangle_media(desc, 0, NULL) == RES_BAD_ARG); @@ -120,7 +156,7 @@ main(int argc, char** argv) CHK(senc_descriptor_get_global_triangle_enclosures( NULL, 0, enclosures) == RES_BAD_ARG); CHK(senc_descriptor_get_global_triangle_enclosures( - NULL, box_nvertices, enclosures) == RES_BAD_ARG); + NULL, nvertices, enclosures) == RES_BAD_ARG); CHK(senc_descriptor_get_global_triangle_enclosures(desc, 0, NULL) == RES_BAD_ARG); CHK(senc_descriptor_get_global_triangle_enclosures(desc, 0, enclosures) @@ -131,25 +167,25 @@ main(int argc, char** argv) /* Add valid duplicate geometry */ CHK(senc_descriptor_ref_put(desc) == RES_OK); desc = NULL; - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, + NULL, nvertices, get_position, &ctx) == RES_OK); CHK(senc_scene_analyze(scn, &desc) == RES_OK); CHK(senc_descriptor_get_global_vertices_count(desc, &count) == RES_OK); /* Duplicate vertices have been replaced */ - CHK(count == box_nvertices); + CHK(count == nvertices); CHK(senc_descriptor_get_global_triangles_count(desc, &count) == RES_OK); /* Duplicate triangles have been replaced */ - CHK(count == box_ntriangles); + CHK(count == ntriangles); /* Add invalid duplicate geometry */ CHK(senc_descriptor_ref_put(desc) == RES_OK); desc = NULL; ctx.front_media = medium1; ctx.back_media = medium0; - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_BAD_ARG); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_BAD_ARG); CHK(senc_scene_ref_put(scn) == RES_OK); CHK(senc_device_ref_put(dev) == RES_OK); diff --git a/src/test_senc_device.c b/src/test_senc_device.c @@ -22,7 +22,7 @@ static INLINE void log_stream(const char* msg, void* ctx) { ASSERT(msg); - (void) msg, (void) ctx; + (void)msg, (void)ctx; printf("%s\n", msg); } diff --git a/src/test_senc_enclosure.c b/src/test_senc_enclosure.c @@ -17,13 +17,12 @@ #include "senc_s3d_wrapper.h" #include "test_senc_utils.h" -#include <rsys/float3.h> #include <rsys/double3.h> #include <star/s3d.h> -int -main(int argc, char** argv) +static void +test(enum senc_convention convention) { struct mem_allocator allocator; struct senc_descriptor* desc = NULL; @@ -31,20 +30,30 @@ main(int argc, char** argv) struct senc_scene* scn = NULL; struct senc_enclosure* enclosures[2] = { NULL, NULL }; struct senc_enclosure* enclosure; - const struct enclosure_header* header; + struct senc_enclosure_header header; struct s3d_device* s3d = NULL; struct s3d_scene* s3d_scn = NULL; struct s3d_shape* s3d_shp = NULL; struct s3d_vertex_data s3d_attribs; + unsigned indices[2][3]; + unsigned medium, media[2]; + unsigned gid; + double vrtx[3]; struct context ctx; - unsigned i, n, count; - (void) argc, (void) argv; + unsigned i, n, t, ecount; + enum senc_convention conv; + int is_front, is_in; CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); CHK(senc_device_create(NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev) == RES_OK); - CHK(senc_scene_create(dev, 2, &scn) == RES_OK); + CHK(senc_scene_create(dev, convention, &scn) == RES_OK); + + CHK(senc_scene_get_convention(scn, &conv) == RES_OK); + CHK(conv == convention); + is_front = (conv & SENC_CONVENTION_NORMAL_FRONT) != 0; + is_in = (conv & SENC_CONVENTION_NORMAL_INSIDE) != 0; s3d_attribs.type = S3D_FLOAT3; s3d_attribs.usage = S3D_POSITION; @@ -55,7 +64,8 @@ main(int argc, char** argv) CHK(s3d_scene_create(s3d, &s3d_scn) == RES_OK); /* A 3D cube. - * 2 enclosures (inside, outside) sharing the same triangles, but opposite sides */ + * 2 enclosures (inside, outside) sharing the same triangles, + * but opposite sides */ ctx.positions = box_vertices; ctx.indices = box_indices; ctx.scale = 1; @@ -65,15 +75,82 @@ main(int argc, char** argv) ctx.front_media = medium0; ctx.back_media = medium1; - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, + NULL, nvertices, get_position, &ctx) == RES_OK); CHK(senc_scene_analyze(scn, &desc) == RES_OK); - CHK(senc_descriptor_get_enclosure_count(desc, &count) == RES_OK); - CHK(count == 2); + CHK(senc_descriptor_get_enclosure_count(desc, &ecount) == RES_OK); + CHK(ecount == 2); - FOR_EACH(i, 0, count) { + CHK(senc_descriptor_get_enclosure(desc, 0, &enclosure) == RES_OK); + CHK(senc_enclosure_ref_get(NULL) == RES_BAD_ARG); + CHK(senc_enclosure_ref_get(enclosure) == RES_OK); + CHK(senc_enclosure_ref_put(NULL) == RES_BAD_ARG); + CHK(senc_enclosure_ref_put(enclosure) == RES_OK); + + CHK(senc_enclosure_get_triangle(NULL, 0, indices[0]) == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle(enclosure, ntriangles, indices[0]) + == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle(enclosure, 0, NULL) == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle(NULL, ntriangles, indices[0]) + == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle(NULL, 0, NULL) == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle(enclosure, ntriangles, NULL) + == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle(NULL, ntriangles, NULL) == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle(enclosure, 0, indices[0]) == RES_OK); + + CHK(senc_enclosure_get_vertex(NULL, 0, vrtx) == RES_BAD_ARG); + CHK(senc_enclosure_get_vertex(enclosure, nvertices, vrtx) + == RES_BAD_ARG); + CHK(senc_enclosure_get_vertex(enclosure, 0, NULL) == RES_BAD_ARG); + CHK(senc_enclosure_get_vertex(NULL, nvertices, vrtx) == RES_BAD_ARG); + CHK(senc_enclosure_get_vertex(NULL, 0, NULL) == RES_BAD_ARG); + CHK(senc_enclosure_get_vertex(enclosure, nvertices, NULL) + == RES_BAD_ARG); + CHK(senc_enclosure_get_vertex(NULL, nvertices, NULL) == RES_BAD_ARG); + CHK(senc_enclosure_get_vertex(enclosure, 0, vrtx) == RES_OK); + + CHK(senc_enclosure_get_triangle_media(NULL, 0, media) == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle_media(enclosure, ntriangles, media) + == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle_media(enclosure, 0, NULL) == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle_media(NULL, ntriangles, media) + == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle_media(NULL, 0, NULL) == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle_media(enclosure, ntriangles, NULL) + == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle_media(NULL, ntriangles, NULL) + == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle_media(enclosure, 0, media) == RES_OK); + + CHK(senc_enclosure_get_triangle_global_id(NULL, 0, &gid) == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle_global_id(enclosure, ntriangles, &gid) + == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle_global_id(enclosure, 0, NULL) + == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle_global_id(NULL, ntriangles, &gid) + == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle_global_id(NULL, 0, NULL) == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle_global_id(enclosure, ntriangles, NULL) + == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle_global_id(NULL, ntriangles, NULL) + == RES_BAD_ARG); + CHK(senc_enclosure_get_triangle_global_id(enclosure, 0, &gid) == RES_OK); + + CHK(senc_enclosure_get_medium(NULL, 0, &medium) == RES_BAD_ARG); + CHK(senc_enclosure_get_medium(enclosure, 2, &medium) == RES_BAD_ARG); + CHK(senc_enclosure_get_medium(enclosure, 0, NULL) == RES_BAD_ARG); + CHK(senc_enclosure_get_medium(NULL, 2, &medium) == RES_BAD_ARG); + CHK(senc_enclosure_get_medium(NULL, 0, NULL) == RES_BAD_ARG); + CHK(senc_enclosure_get_medium(enclosure, 2, NULL) == RES_BAD_ARG); + CHK(senc_enclosure_get_medium(NULL, 2, NULL) == RES_BAD_ARG); + CHK(senc_enclosure_get_medium(enclosure, 0, &medium) == RES_OK); + + CHK(senc_enclosure_ref_put(enclosure) == RES_OK); + + FOR_EACH(i, 0, ecount) { CHK(senc_descriptor_get_enclosure(desc, i, &enclosure) == RES_OK); CHK(senc_enclosure_get_header(NULL, &header) == RES_BAD_ARG); @@ -81,27 +158,43 @@ main(int argc, char** argv) CHK(senc_enclosure_get_header(NULL, NULL) == RES_BAD_ARG); CHK(senc_enclosure_get_header(enclosure, &header) == RES_OK); - CHK(header->enclosure_id == i); - CHK(header->enclosed_medium == (i == 0 ? 0U : 1U)); - CHK(header->triangle_count == box_ntriangles); - CHK(header->unique_triangle_count == box_ntriangles); - CHK(header->vertices_count == box_nvertices); - CHK(header->is_infinite == (i == 0)); + CHK(header.enclosure_id == i); + CHK(header.enclosed_media_count == 1); + CHK(senc_enclosure_get_medium(enclosure, 0, &medium) == RES_OK); + /* 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) { + CHK(senc_enclosure_get_triangle_global_id(enclosure, t, &gid) == RES_OK); + CHK(gid == t); + } CHK(senc_enclosure_ref_put(enclosure) == RES_OK); } FOR_EACH(i, 0, 2) CHK(senc_descriptor_get_enclosure(desc, i, enclosures + i) == RES_OK); - FOR_EACH(n, 0, box_ntriangles) { - unsigned indices[2][3]; + FOR_EACH(n, 0, ntriangles) { + int same, reversed; /* Read same triangles in both enclosures */ FOR_EACH(i, 0, 2) CHK(senc_enclosure_get_triangle(enclosures[i], n, indices[i]) == RES_OK); - /* Same triangles, opposite sides */ - CHK(indices[0][0] == indices[1][0]); - CHK(indices[0][1] == indices[1][2]); - CHK(indices[0][2] == indices[1][1]); + /* Same triangles and opposite sides for the 2 enclosures */ + FOR_EACH(i, 0, 3) CHK(indices[0][i] == indices[1][2 - i]); + /* Enclosure 0 is outside (and contains medium 0 if convention is front). + * Geometrical normals in output data point in the same direction that those + * of input triangles for enclosure 0 iff convention is inside. + * The opposite holds for enclosure 1. */ + cmp_trg(n, enclosures[0], box_indices + 3 * n, box_vertices, &same, &reversed); + CHK((same && !reversed) == is_in); + cmp_trg(n, enclosures[1], box_indices + 3 * n, box_vertices, &same, &reversed); + CHK(same && reversed == is_in); } FOR_EACH(i, 0, 2) CHK(senc_enclosure_ref_put(enclosures[i]) == RES_OK); @@ -112,7 +205,8 @@ main(int argc, char** argv) /* Same 3D cube, but with a hole (incomplete). * 1 single enclosure including both sides of triangles */ - CHK(senc_scene_create(dev, 2, &scn) == RES_OK); + CHK(senc_scene_create(dev, + SENC_CONVENTION_NORMAL_BACK | SENC_CONVENTION_NORMAL_INSIDE, &scn) == RES_OK); ctx.positions = box_vertices; ctx.indices = box_indices; @@ -121,13 +215,13 @@ main(int argc, char** argv) ctx.front_media = medium0; ctx.back_media = medium0; - CHK(senc_scene_add_geometry(scn, box_ntriangles - 1, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles - 1, get_indices, get_media, + NULL, nvertices, get_position, &ctx) == RES_OK); CHK(senc_scene_analyze(scn, &desc) == RES_OK); - CHK(senc_descriptor_get_enclosure_count(desc, &count) == RES_OK); - CHK(count == 1); + CHK(senc_descriptor_get_enclosure_count(desc, &ecount) == RES_OK); + CHK(ecount == 1); dump_enclosure(desc, 0, "test_enclosure_hole.obj"); @@ -138,29 +232,26 @@ main(int argc, char** argv) CHK(senc_enclosure_get_header(NULL, NULL) == RES_BAD_ARG); CHK(senc_enclosure_get_header(enclosure, &header) == RES_OK); - CHK(header->enclosure_id == 0); - CHK(header->enclosed_medium == 0); - CHK(header->triangle_count == 2 * header->unique_triangle_count); - CHK(header->unique_triangle_count == box_ntriangles - 1); - CHK(header->vertices_count == box_nvertices); - CHK(header->is_infinite == 1); - - FOR_EACH(n, 0, header->unique_triangle_count) { - unsigned indices[2][3]; - /* Read 2 consecutive triangles in the enclosure */ - FOR_EACH(i, 0, 2) /* Triangle n VS 2n */ - CHK(senc_enclosure_get_triangle(enclosure, - n + i * header->unique_triangle_count, indices[i]) == RES_OK); - /* Same triangles, opposite sides */ - CHK(indices[0][0] == indices[1][2]); - CHK(indices[0][1] == indices[1][1]); - CHK(indices[0][2] == indices[1][0]); + CHK(header.enclosure_id == 0); + CHK(header.enclosed_media_count == 1); + CHK(header.triangle_count == 2 * header.unique_triangle_count); + CHK(header.unique_triangle_count == ntriangles - 1); + CHK(header.vertices_count == nvertices); + CHK(header.is_infinite == 1); + + FOR_EACH(t, 0, header.unique_triangle_count) { + /* The first unique_triangle_count triangles of an enclosure + * are unique triangles */ + CHK(senc_enclosure_get_triangle_global_id(enclosure, t, &gid) == RES_OK); + CHK(gid == t); + } + FOR_EACH(n, 0, header.unique_triangle_count) { /* Put geometry in a 3D view */ CHK(s3d_shape_create_mesh(s3d, &s3d_shp) == RES_OK); - CHK(s3d_mesh_setup_indexed_vertices(s3d_shp, header->triangle_count, - senc_enclosure_get_triangle__, header->vertices_count, &s3d_attribs, + CHK(s3d_mesh_setup_indexed_vertices(s3d_shp, header.triangle_count, + senc_enclosure_get_triangle__, header.vertices_count, &s3d_attribs, 1, enclosure) == RES_OK); @@ -179,6 +270,15 @@ main(int argc, char** argv) 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_inconsistant_cube.c b/src/test_senc_inconsistant_cube.c @@ -0,0 +1,156 @@ +/* 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> + +/* The following array lists the indices toward the 3D vertices of each +* triangle. +* ,6---,7 ,6----7 +* ,' | ,'/| ,' | \ | Y Z +* 2----3' / | 2', | \ | | ,' +* |', | / ,5 | ',4---,5 0----X +* | ',|/,' | ,' | ,' +* 0----1' 0----1' +* Front, right Back, left and +* and Top faces bottom faces */ +/* Triangle #1 rotation order is not consistant with other triangles */ +static unsigned +inconsistant_box_indices[12/*#triangles*/ * 3/*#indices per triangle*/] = { + 0, 1, 2, 1, 2, 3, /* Front face */ + 0, 4, 2, 2, 4, 6, /* Left face*/ + 4, 5, 6, 6, 5, 7, /* Back face */ + 3, 7, 1, 1, 7, 5, /* Right face */ + 2, 6, 3, 3, 6, 7, /* Top face */ + 0, 1, 4, 4, 1, 5 /* Bottom face */ +}; +static const unsigned inconsistant_box_ntriangles += sizeof(inconsistant_box_indices) / (3 * sizeof(*inconsistant_box_indices)); + +/* Media definitions reflect triangle #1 inconsistancy */ +static const unsigned +inconsistant_medium_front[12] = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static const unsigned +inconsistant_medium_back[12] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + +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; + enum senc_convention conv; + int conv_front, conv_in; + struct context ctx; + unsigned i, e, ecount; + + CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); + CHK(senc_device_create(NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev) + == RES_OK); + + CHK(senc_scene_create(dev, convention, &scn) == RES_OK); + + /* A 3D cube. + * 2 enclosures (inside, outside) sharing the same triangles, + * but opposite sides. + * What differs in this test is that triangle #0 vertices are given + * in the opposite rotation order. */ + ctx.positions = box_vertices; + ctx.indices = inconsistant_box_indices; + ctx.scale = 1; + ctx.reverse_vrtx = 0; + ctx.reverse_med = 0; + d3(ctx.offset, 0, 0, 0); + ctx.front_media = inconsistant_medium_front; + ctx.back_media = inconsistant_medium_back; + + CHK(senc_scene_add_geometry(scn, inconsistant_box_ntriangles, get_indices, + get_media, NULL, nvertices, get_position, &ctx) == RES_OK); + + CHK(senc_scene_analyze(scn, &desc) == RES_OK); + + CHK(senc_descriptor_get_enclosure_count(desc, &ecount) == RES_OK); + CHK(ecount == 2); + + CHK(senc_scene_get_convention(scn, &conv) == RES_OK); + CHK(conv == convention); + conv_front = (conv & SENC_CONVENTION_NORMAL_FRONT) != 0; + conv_in = (conv & SENC_CONVENTION_NORMAL_INSIDE) != 0; + + FOR_EACH(e, 0, ecount) { + unsigned medium, expected_external_medium, expected_medium; + char name[128]; + int front_inside; + int expected_side; + CHK(senc_descriptor_get_enclosure(desc, e, &enclosure) == RES_OK); + CHK(senc_enclosure_get_header(enclosure, &header) == RES_OK); + CHK(header.enclosed_media_count == 1); + CHK(senc_enclosure_get_medium(enclosure, 0, &medium) == RES_OK); + /* If NORMAL_FRONT the external enclosure's medium should be 0, + * 1 if NORMAL_BACK */ + expected_external_medium = conv_front ? 0 : 1; + expected_medium = (header.is_infinite ? + expected_external_medium : !expected_external_medium); + CHK(medium == expected_medium); + /* Common media, for non input triangles */ + front_inside = (conv_front == conv_in); + expected_side = front_inside ? 0 : 1; + + sprintf(name, "test_inconsistant_cube_%s_%s_%u.obj", + conv_front ? "front" : "back", conv_in ? "in" : "out", e); + dump_enclosure(desc, e, name); + + FOR_EACH(i, 0, header.triangle_count) { + int same, reversed, fst_reversed; + unsigned med[2]; + fst_reversed = (e == 0) == conv_in; + CHK(senc_enclosure_get_triangle_media(enclosure, i, med) == RES_OK); + CHK(med[expected_side] == medium); + cmp_trg(i, enclosure, + inconsistant_box_indices + (3 * i), box_vertices, + &same, &reversed); + /* Should be made of the same triangles */ + CHK(same); + CHK(i ? reversed != fst_reversed : reversed == fst_reversed); + } + 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_many_enclosures.c b/src/test_senc_many_enclosures.c @@ -0,0 +1,172 @@ +/* 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" + +#include <star/s3dut.h> +#include <rsys/double3.h> +#include <rsys/clock_time.h> + +#include <limits.h> + +struct s3dut_context { + struct s3dut_mesh_data data; + struct context ctx; +}; + +static void +get_s3dut_indices(const unsigned itri, unsigned ids[3], void* context) +{ + struct s3dut_context* ctx = context; + ASSERT(ids && ctx); + ASSERT(itri < ctx->data.nprimitives); + ASSERT(ctx->data.indices[itri * 3 + 0] < UINT_MAX + && ctx->data.indices[itri * 3 + 1] < UINT_MAX + && ctx->data.indices[itri * 3 + 2] < UINT_MAX); + ids[0] = (unsigned)ctx->data.indices[itri * 3 + 0]; + ids[ctx->ctx.reverse_vrtx ? 2 : 1] = (unsigned)ctx->data.indices[itri * 3 + 1]; + ids[ctx->ctx.reverse_vrtx ? 1 : 2] = (unsigned)ctx->data.indices[itri * 3 + 2]; +} + +static void +get_s3dut_position(const unsigned ivert, double pos[3], void* context) +{ + struct s3dut_context* ctx = context; + ASSERT(pos && ctx); + ASSERT(ivert < ctx->data.nvertices); + pos[0] + = ctx->data.positions[ivert * 3 + 0] * ctx->ctx.scale + ctx->ctx.offset[0]; + pos[1] + = ctx->data.positions[ivert * 3 + 1] * ctx->ctx.scale + ctx->ctx.offset[1]; + pos[2] + = ctx->data.positions[ivert * 3 + 2] * ctx->ctx.scale + ctx->ctx.offset[2]; +} + +static void +get_s3dut_media(const unsigned itri, unsigned medium[2], void* context) +{ + struct s3dut_context* ctx = context; + (void)itri; + ASSERT(medium && ctx); + medium[ctx->ctx.reverse_med ? 1 : 0] = *ctx->ctx.front_media; + medium[ctx->ctx.reverse_med ? 0 : 1] = *ctx->ctx.back_media; +} + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct senc_descriptor* desc = NULL; + struct senc_device* dev = NULL; + struct senc_scene* scn = NULL; + struct s3dut_mesh* cyl = NULL; + struct s3dut_context ctx; + unsigned m_in, m_out; + unsigned count; + unsigned cyl_trg_count, cyl_vrtx_count, e; + int i, j, k; + char dump[64]; + struct time t0, t1; + (void)argc, (void)argv; + + CHK(mem_init_regular_allocator(&allocator) == RES_OK); + CHK(senc_device_create(NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev) + == RES_OK); + +#define NB_CYL_1 64 + /* 64^3 = 262144 cylinders */ +#define NB_CYL (NB_CYL_1 * NB_CYL_1 * NB_CYL_1) + /* Create the scene */ + CHK(senc_scene_create(dev, + SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_INSIDE, &scn) == RES_OK); + + ctx.ctx.positions = NULL; + ctx.ctx.indices = NULL; + ctx.ctx.reverse_vrtx = 0; + ctx.ctx.reverse_med = 0; + ctx.ctx.front_media = &m_in; + ctx.ctx.back_media = &m_out; + /* A 20 triangles 12 vertices cylinder template */ + S3DUT(create_cylinder(&allocator, 1, 1, 5, 1, &cyl)); + S3DUT(mesh_get_data(cyl, &ctx.data)); + ASSERT(ctx.data.nprimitives < UINT_MAX); + ASSERT(ctx.data.nvertices < UINT_MAX); + cyl_trg_count = (unsigned)ctx.data.nprimitives; + cyl_vrtx_count = (unsigned)ctx.data.nvertices; + FOR_EACH(i, 0, NB_CYL_1) { + double center_x = 2 * (1 + NB_CYL_1) * (i - NB_CYL_1 / 2); + FOR_EACH(j, 0, NB_CYL_1) { + double misalignment = 0.01; + FOR_EACH(k, 0, NB_CYL_1) { + double center_y = 2 * (1 + NB_CYL_1) * (j - NB_CYL_1 / 2); + m_in = (unsigned)k; + m_out = (unsigned)(k + 1); + ctx.ctx.scale = k + 1; + /* Mitigate Embree issue #181 + * We cannot keep perfect alignment of cylinders + * or some hits are missed */ + misalignment *= -1; + d3(ctx.ctx.offset, center_x + misalignment, center_y + misalignment, 0); + CHK(senc_scene_add_geometry(scn, cyl_trg_count, get_s3dut_indices, + get_s3dut_media, NULL, cyl_vrtx_count, get_s3dut_position, &ctx) + == RES_OK); + } + } + } + S3DUT(mesh_ref_put(cyl)); + + time_current(&t0); + CHK(senc_scene_analyze(scn, &desc) == RES_OK); + time_sub(&t0, time_current(&t1), &t0); + time_dump(&t0, TIME_MSEC | TIME_SEC | TIME_MIN, NULL, dump, sizeof(dump)); + printf("Scene analyzed in: %s\n", dump); + + /* dump_global(desc, "test_many_enclosures.obj"); */ + + CHK(senc_descriptor_get_global_vertices_count(desc, &count) == RES_OK); + CHK(count == NB_CYL * cyl_vrtx_count); + CHK(senc_descriptor_get_global_triangles_count(desc, &count) == RES_OK); + CHK(count == NB_CYL * cyl_trg_count); + + CHK(senc_descriptor_get_enclosure_count(desc, &count) == RES_OK); + CHK(count == 1 + NB_CYL); + + FOR_EACH(e, 0, count) { + struct senc_enclosure* enclosure; + struct senc_enclosure_header header; + unsigned m; + CHK(senc_descriptor_get_enclosure(desc, e, &enclosure) == RES_OK); + CHK(senc_enclosure_get_header(enclosure, &header) == RES_OK); + CHK(header.enclosed_media_count == 1); + CHK(senc_enclosure_get_medium(enclosure, 0, &m) == RES_OK); + CHK(header.triangle_count == + (header.is_infinite /* Outermost enclosure: NB_CYL_1*NB_CYL_1 cylinders */ + ? NB_CYL_1 * NB_CYL_1 * cyl_trg_count + : (m == 0 + ? cyl_trg_count /* Innermost enclosures: 1 cylinder */ + : 2 * cyl_trg_count))); /* Other enclosures: 2 cylinders */ + CHK(senc_enclosure_ref_put(enclosure) == RES_OK); + } + + CHK(senc_scene_ref_put(scn) == RES_OK); + CHK(senc_device_ref_put(dev) == RES_OK); + CHK(senc_descriptor_ref_put(desc) == RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_regular_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_senc_many_triangles.c b/src/test_senc_many_triangles.c @@ -17,8 +17,10 @@ #include "test_senc_utils.h" #include <star/s3dut.h> - #include <rsys/double3.h> +#include <rsys/clock_time.h> + +#include <limits.h> struct s3dut_context { struct s3dut_mesh_data data; @@ -34,9 +36,9 @@ get_s3dut_indices(const unsigned itri, unsigned ids[3], void* context) ASSERT(ctx->data.indices[itri * 3 + 0] < UINT_MAX && ctx->data.indices[itri * 3 + 1] < UINT_MAX && ctx->data.indices[itri * 3 + 2] < UINT_MAX); - ids[0] = (unsigned) ctx->data.indices[itri * 3 + 0]; - ids[ctx->ctx.reverse_vrtx ? 2 : 1] = (unsigned) ctx->data.indices[itri * 3 + 1]; - ids[ctx->ctx.reverse_vrtx ? 1 : 2] = (unsigned) ctx->data.indices[itri * 3 + 2]; + ids[0] = (unsigned)ctx->data.indices[itri * 3 + 0]; + ids[ctx->ctx.reverse_vrtx ? 2 : 1] = (unsigned)ctx->data.indices[itri * 3 + 1]; + ids[ctx->ctx.reverse_vrtx ? 1 : 2] = (unsigned)ctx->data.indices[itri * 3 + 2]; } static void @@ -45,19 +47,22 @@ get_s3dut_position(const unsigned ivert, double pos[3], void* context) struct s3dut_context* ctx = context; ASSERT(pos && ctx); ASSERT(ivert < ctx->data.nvertices); - pos[0] = ctx->data.positions[ivert * 3 + 0] * ctx->ctx.scale + ctx->ctx.offset[0]; - pos[1] = ctx->data.positions[ivert * 3 + 1] * ctx->ctx.scale + ctx->ctx.offset[1]; - pos[2] = ctx->data.positions[ivert * 3 + 2] * ctx->ctx.scale + ctx->ctx.offset[2]; + pos[0] + = ctx->data.positions[ivert * 3 + 0] * ctx->ctx.scale + ctx->ctx.offset[0]; + pos[1] + = ctx->data.positions[ivert * 3 + 1] * ctx->ctx.scale + ctx->ctx.offset[1]; + pos[2] + = ctx->data.positions[ivert * 3 + 2] * ctx->ctx.scale + ctx->ctx.offset[2]; } static void get_s3dut_media(const unsigned itri, unsigned medium[2], void* context) { struct s3dut_context* ctx = context; - (void) itri; + (void)itri; ASSERT(medium && ctx); - medium[ctx->ctx.reverse_med ? 1 : 0] = ctx->ctx.front_media[0]; - medium[ctx->ctx.reverse_med ? 0 : 1] = ctx->ctx.back_media[1]; + medium[ctx->ctx.reverse_med ? 1 : 0] = *ctx->ctx.front_media; + medium[ctx->ctx.reverse_med ? 0 : 1] = *ctx->ctx.back_media; } int @@ -69,52 +74,76 @@ main(int argc, char** argv) struct senc_scene* scn = NULL; struct s3dut_mesh* cyl = NULL; struct s3dut_context ctx; + unsigned m0 = 0, m1; unsigned count; - unsigned cyl_trg_count, cyl_vrtx_count; - int i; - (void) argc, (void) argv; + unsigned cyl_trg_count, cyl_vrtx_count, i; + char dump[64]; + struct time t0, t1; + (void)argc, (void)argv; - CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); - CHK(senc_device_create - (NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev) == RES_OK); + CHK(mem_init_regular_allocator(&allocator) == RES_OK); + CHK(senc_device_create (NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev) + == RES_OK); +#define NB_CYL 4 /* Create the scene */ - CHK(senc_scene_create(dev, 2, &scn) == RES_OK); + CHK(senc_scene_create(dev, + SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_INSIDE, &scn) == RES_OK); ctx.ctx.positions = NULL; ctx.ctx.indices = NULL; ctx.ctx.scale = 1; ctx.ctx.reverse_vrtx = 0; ctx.ctx.reverse_med = 0; - ctx.ctx.front_media = medium0; - ctx.ctx.back_media = medium1; - /* Create a cylinder (320 K trg, 160 K vrtx). */ - CHK(s3dut_create_cylinder(&allocator, 1, 2, 400, 400, &cyl) == RES_OK); + ctx.ctx.back_media = &m0; + ctx.ctx.front_media = &m1; + /* A 2,562,560 triangles 1,281,282 vertices cylinder template */ + S3DUT(create_cylinder(&allocator, 1, 2, 1280, 1000, &cyl)); S3DUT(mesh_get_data(cyl, &ctx.data)); ASSERT(ctx.data.nprimitives < UINT_MAX); ASSERT(ctx.data.nvertices < UINT_MAX); cyl_trg_count = (unsigned)ctx.data.nprimitives; cyl_vrtx_count = (unsigned)ctx.data.nvertices; -#define NB_CYL 10 FOR_EACH(i, 0, NB_CYL) { + m1 = i; d3(ctx.ctx.offset, 0, 0, i * 10); CHK(senc_scene_add_geometry(scn, cyl_trg_count, get_s3dut_indices, - get_s3dut_media, cyl_vrtx_count, get_s3dut_position, &ctx) == RES_OK); + get_s3dut_media, NULL, cyl_vrtx_count, get_s3dut_position, &ctx) + == RES_OK); } S3DUT(mesh_ref_put(cyl)); + + time_current(&t0); CHK(senc_scene_analyze(scn, &desc) == RES_OK); + time_sub(&t0, time_current(&t1), &t0); + time_dump(&t0, TIME_MSEC | TIME_SEC | TIME_MIN, NULL, dump, sizeof(dump)); + printf("Scene analyzed in: %s\n", dump); + + /* dump_global(desc, "test_many_triangles.obj"); */ CHK(senc_descriptor_get_global_vertices_count(desc, &count) == RES_OK); CHK(count == NB_CYL * cyl_vrtx_count); CHK(senc_descriptor_get_global_triangles_count(desc, &count) == RES_OK); CHK(count == NB_CYL * cyl_trg_count); + CHK(senc_descriptor_get_enclosure_count(desc, &count) == RES_OK); + CHK(count == 1 + NB_CYL); + FOR_EACH(i, 0, count) { + struct senc_enclosure* enclosure; + struct senc_enclosure_header header; + CHK(senc_descriptor_get_enclosure(desc, i, &enclosure) == RES_OK); + CHK(senc_enclosure_get_header(enclosure, &header) == RES_OK); + CHK(header.triangle_count == + i ? cyl_trg_count : NB_CYL * cyl_trg_count); + CHK(senc_enclosure_ref_put(enclosure) == RES_OK); + } + CHK(senc_scene_ref_put(scn) == RES_OK); CHK(senc_device_ref_put(dev) == RES_OK); CHK(senc_descriptor_ref_put(desc) == RES_OK); check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); + mem_shutdown_regular_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_senc_sample_enclosure.c b/src/test_senc_sample_enclosure.c @@ -31,7 +31,7 @@ main(int argc, char** argv) struct senc_device* dev = NULL; struct senc_scene* scn = NULL; struct senc_enclosure* enclosure = NULL; - const struct enclosure_header* header = NULL; + struct senc_enclosure_header header; struct s3d_device* s3d = NULL; struct s3d_scene* s3d_scn = NULL; struct s3d_scene_view* s3d_view = NULL; @@ -42,26 +42,27 @@ main(int argc, char** argv) struct context ctx; int i; float st[2]; - (void) argc, (void) argv; + (void)argc, (void)argv; CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); CHK(senc_device_create(NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev) == RES_OK); - CHK(senc_scene_create(dev, 2, &scn) == RES_OK); + CHK(senc_scene_create(dev, + SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_INSIDE, &scn) == RES_OK); vrtx_get.type = S3D_FLOAT3; vrtx_get.usage = S3D_POSITION; vrtx_get.get = senc_enclosure_get_vertex__; - CHK(s3d_device_create(NULL, &allocator, 0, &s3d) == RES_OK); + S3D(device_create(NULL, &allocator, 0, &s3d)); - CHK(s3d_scene_create(s3d, &s3d_scn) == RES_OK); + S3D(scene_create(s3d, &s3d_scn)); /* A 3D cube, but with a hole (incomplete). * 1 single enclosure including both sides of triangles */ - ctx.positions = box_vertices; + ctx.positions = cube_vertices; /* Need a true cube */ ctx.indices = box_indices; ctx.scale = 1; ctx.reverse_vrtx = 0; @@ -70,8 +71,8 @@ main(int argc, char** argv) ctx.front_media = medium0; ctx.back_media = medium0; - CHK(senc_scene_add_geometry(scn, box_ntriangles - 1, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles - 1, get_indices, + get_media, NULL, nvertices, get_position, &ctx) == RES_OK); CHK(senc_scene_analyze(scn, &desc) == RES_OK); @@ -79,14 +80,14 @@ main(int argc, char** argv) CHK(senc_enclosure_get_header(enclosure, &header) == RES_OK); /* Put enclosure in a 3D view... */ - CHK(s3d_shape_create_mesh(s3d, &s3d_shp) == RES_OK); - CHK(s3d_mesh_setup_indexed_vertices(s3d_shp, header->triangle_count, - senc_enclosure_get_triangle__, header->vertices_count, &vrtx_get, 1, enclosure) - == RES_OK); + S3D(shape_create_mesh(s3d, &s3d_shp)); + S3D(mesh_setup_indexed_vertices(s3d_shp, header.triangle_count, + senc_enclosure_get_triangle__, header.vertices_count, &vrtx_get, 1, + enclosure)); - CHK(s3d_scene_attach_shape(s3d_scn, s3d_shp) == RES_OK); + S3D(scene_attach_shape(s3d_scn, s3d_shp)); - CHK(s3d_scene_view_create(s3d_scn, S3D_SAMPLE, &s3d_view) == RES_OK); + S3D(scene_view_create(s3d_scn, S3D_SAMPLE, &s3d_view)); /* ... and sample it. */ CHK(ssp_rng_create(&allocator, &ssp_rng_threefry, &rng) == RES_OK); @@ -104,19 +105,19 @@ main(int argc, char** argv) if(eq_eps(attrib.value[n], 0, FLT_EPSILON) || eq_eps(attrib.value[n], 1, FLT_EPSILON)) c++; - ASSERT(c == 1); + CHK(c == 1); S3D(primitive_get_attrib(&prim, S3D_GEOMETRY_NORMAL, st, &attrib)); c = 0; FOR_EACH(n, 0, 3) if(eq_eps(attrib.value[n], -1, FLT_EPSILON) || eq_eps(attrib.value[n], 1, FLT_EPSILON)) c++; - ASSERT(c == 1); + CHK(c == 1); c = 0; FOR_EACH(n, 0, 3) if(eq_eps(attrib.value[n], 0, FLT_EPSILON)) c++; - ASSERT(c == 2); + CHK(c == 2); } SENC(enclosure_ref_put(enclosure)); diff --git a/src/test_senc_scene.c b/src/test_senc_scene.c @@ -19,8 +19,6 @@ #include <rsys/float3.h> #include <rsys/double3.h> -#include <star/s3d.h> - int main(int argc, char** argv) { @@ -28,24 +26,64 @@ main(int argc, char** argv) struct senc_device* dev = NULL; struct senc_scene* scn = NULL; struct senc_descriptor* desc = NULL; + struct senc_enclosure* enc = NULL; + struct senc_enclosure_header header; struct context ctx; + unsigned medfront[2], medback[2]; + unsigned count, i, maxm; + enum senc_convention convention; (void)argc, (void)argv; CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); CHK(senc_device_create(NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev) == RES_OK); - CHK(senc_scene_create(NULL, 2, &scn) == RES_BAD_ARG); + CHK(senc_scene_create(NULL, + SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_INSIDE, &scn) == RES_BAD_ARG); CHK(senc_scene_create(dev, 0, &scn) == RES_BAD_ARG); - CHK(senc_scene_create(dev, 2, NULL) == RES_BAD_ARG); + CHK(senc_scene_create(dev, + SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_INSIDE, NULL) == RES_BAD_ARG); CHK(senc_scene_create(NULL, 0, &scn) == RES_BAD_ARG); - CHK(senc_scene_create(NULL, 2, NULL) == RES_BAD_ARG); + CHK(senc_scene_create(NULL, + SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_INSIDE, NULL) == RES_BAD_ARG); CHK(senc_scene_create(dev, 0, NULL) == RES_BAD_ARG); CHK(senc_scene_create(NULL, 0, NULL) == RES_BAD_ARG); - /* It is valid to have unused media */ - CHK(senc_scene_create(dev, 4, &scn) == RES_OK); - - /* A 3D cube */ + CHK(senc_scene_create(dev, + SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_INSIDE, &scn) == RES_OK); + + CHK(senc_scene_get_convention(NULL, &convention) == RES_BAD_ARG); + CHK(senc_scene_get_convention(scn, NULL) == RES_BAD_ARG); + CHK(senc_scene_get_convention(NULL, NULL) == RES_BAD_ARG); + CHK(senc_scene_get_convention(scn, &convention) == RES_OK); + CHK(convention == (SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_INSIDE)); + + CHK(senc_scene_get_triangles_count(NULL, &count) == RES_BAD_ARG); + CHK(senc_scene_get_triangles_count(scn, NULL) == RES_BAD_ARG); + CHK(senc_scene_get_triangles_count(NULL, NULL) == RES_BAD_ARG); + CHK(senc_scene_get_triangles_count(scn, &count) == RES_OK); + CHK(count == 0); + + CHK(senc_scene_get_unique_triangles_count(NULL, &count) == RES_BAD_ARG); + CHK(senc_scene_get_unique_triangles_count(scn, NULL) == RES_BAD_ARG); + CHK(senc_scene_get_unique_triangles_count(NULL, NULL) == RES_BAD_ARG); + CHK(senc_scene_get_unique_triangles_count(scn, &count) == RES_OK); + CHK(count == 0); + + CHK(senc_scene_get_vertices_count(NULL, &count) == RES_BAD_ARG); + CHK(senc_scene_get_vertices_count(scn, NULL) == RES_BAD_ARG); + CHK(senc_scene_get_vertices_count(NULL, NULL) == RES_BAD_ARG); + CHK(senc_scene_get_vertices_count(scn, &count) == RES_OK); + CHK(count == 0); + + CHK(senc_scene_get_unique_vertices_count(NULL, &count) == RES_BAD_ARG); + CHK(senc_scene_get_unique_vertices_count(scn, NULL) == RES_BAD_ARG); + CHK(senc_scene_get_unique_vertices_count(NULL, NULL) == RES_BAD_ARG); + CHK(senc_scene_get_unique_vertices_count(scn, &count) == RES_OK); + CHK(count == 0); + + /* A 3D cube. + * With this geometry front is inside with NORMAL_BACK convention, + * outside with NORMAL_FRONT convention */ ctx.positions = box_vertices; ctx.indices = box_indices; ctx.scale = 1; @@ -54,21 +92,31 @@ main(int argc, char** argv) d3(ctx.offset, 0, 0, 0); ctx.front_media = medium0; ctx.back_media = medium1; - - CHK(senc_scene_add_geometry(NULL, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_BAD_ARG); - CHK(senc_scene_add_geometry(scn, 0, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_BAD_ARG); - CHK(senc_scene_add_geometry(scn, box_ntriangles, NULL, get_media, - box_nvertices, get_position, &ctx) == RES_BAD_ARG); - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, NULL, - box_nvertices, get_position, &ctx) == RES_BAD_ARG); - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - 0, get_position, &ctx) == RES_BAD_ARG); - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, NULL, &ctx) == RES_BAD_ARG); - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_OK); + ctx.global_ids = gid_face; + + CHK(senc_scene_add_geometry(NULL, ntriangles, get_indices, get_media, + get_global_id, nvertices, get_position, &ctx) == RES_BAD_ARG); + CHK(senc_scene_add_geometry(scn, 0, get_indices, get_media, get_global_id, + nvertices, get_position, &ctx) == RES_BAD_ARG); + CHK(senc_scene_add_geometry(scn, ntriangles, NULL, get_media, + get_global_id, nvertices, get_position, &ctx) == RES_BAD_ARG); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL, + get_global_id, nvertices, get_position, &ctx) == RES_BAD_ARG); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, + get_global_id, 0, get_position, &ctx) == RES_BAD_ARG); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, + get_global_id, nvertices, NULL, &ctx) == RES_BAD_ARG); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, + get_global_id, nvertices, get_position, &ctx) == RES_OK); + + CHK(senc_scene_get_triangles_count(scn, &count) == RES_OK); + CHK(count == ntriangles); + CHK(senc_scene_get_unique_triangles_count(scn, &count) == RES_OK); + CHK(count == ntriangles); + CHK(senc_scene_get_vertices_count(scn, &count) == RES_OK); + CHK(count == nvertices); + CHK(senc_scene_get_unique_vertices_count(scn, &count) == RES_OK); + CHK(count == nvertices); CHK(senc_scene_analyze(NULL, NULL) == RES_BAD_ARG); CHK(senc_scene_analyze(scn, NULL) == RES_BAD_ARG); @@ -81,43 +129,105 @@ main(int argc, char** argv) CHK(senc_scene_ref_put(NULL) == RES_BAD_ARG); CHK(senc_scene_ref_put(scn) == RES_OK); - /* Invalid medium ID */ - ctx.back_media = medium1_12; - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_BAD_ARG); - ctx.back_media = medium1; + CHK(senc_scene_ref_put(scn) == RES_OK); + CHK(senc_descriptor_ref_put(desc) == RES_OK); + CHK(senc_scene_create(dev, + SENC_CONVENTION_NORMAL_BACK | SENC_CONVENTION_NORMAL_INSIDE, &scn) == RES_OK); + CHK(senc_scene_get_convention(scn, &convention) == RES_OK); + CHK(convention == (SENC_CONVENTION_NORMAL_BACK | SENC_CONVENTION_NORMAL_INSIDE)); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, + get_global_id, nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_analyze(scn, &desc) == RES_OK); + /* Check that medium 0 is inside */ + CHK(senc_descriptor_get_enclosure_by_medium(desc, 0, 0, &enc) == RES_OK); + CHK(senc_enclosure_get_header(enc, &header) == RES_OK); + CHK(!header.is_infinite); + CHK(senc_enclosure_ref_put(enc) == RES_OK); + + FOR_EACH(i, 0, ntriangles) { + unsigned gid; + CHK(senc_descriptor_get_global_triangle_global_id(desc, i, &gid) == RES_OK); + /* gid has been set to gid_face. */ + CHK(gid == gid_face[i]); + } + CHK(senc_descriptor_get_global_triangle_media(desc, 0, medback) == RES_OK); + + ctx.front_media = medium1_3; + CHK(senc_scene_ref_put(scn) == RES_OK); + CHK(senc_descriptor_ref_put(desc) == RES_OK); + CHK(senc_scene_create(dev, + SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_INSIDE, &scn) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, + get_global_id, nvertices, get_position, &ctx) == RES_OK); + /* Medium mismatch between neighbour segments, but OK */ + CHK(senc_scene_analyze(scn, &desc) == RES_OK); + + CHK(senc_descriptor_get_max_medium(desc, &maxm) == RES_OK); + CHK(maxm == 3); + CHK(senc_descriptor_get_enclosure_count_by_medium(desc, 0, &count) == RES_OK); + CHK(count == 0); /* Medium 0 unused */ + CHK(senc_descriptor_get_enclosure_count_by_medium(desc, 1, &count) == RES_OK); + CHK(count == 2); /* Medium 1 used twice */ + CHK(senc_descriptor_get_enclosure_count_by_medium(desc, 2, &count) == RES_OK); + CHK(count == 0); /* Medium 2 unused */ + CHK(senc_descriptor_get_enclosure_count_by_medium(desc, 3, &count) == RES_OK); + CHK(count == 1); /* Medium 3 used */ + check_desc(desc); + + ctx.front_media = medium0; + CHK(senc_scene_ref_put(scn) == RES_OK); + CHK(senc_descriptor_ref_put(desc) == RES_OK); + CHK(senc_scene_create(dev, + SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_INSIDE, &scn) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_analyze(scn, &desc) == RES_OK); + /* Check that medium 0 is outside */ + CHK(senc_descriptor_get_enclosure_by_medium(desc, 0, 0, &enc) == RES_OK); + CHK(senc_enclosure_get_header(enc, &header) == RES_OK); + CHK(header.is_infinite); + CHK(senc_enclosure_ref_put(enc) == RES_OK); + + FOR_EACH(i, 0, ntriangles) { + unsigned gid; + CHK(senc_descriptor_get_global_triangle_global_id(desc, i, &gid) == RES_OK); + /* Default gid: triangle rank. */ + CHK(gid == i); + } + CHK(senc_descriptor_get_global_triangle_media(desc, 0, medfront) == RES_OK); + FOR_EACH(i, 0, 2) CHK(medback[i] == medfront[i]); /* Invalid vertex ID */ - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices - 1, get_position, &ctx) == RES_BAD_ARG); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, NULL, + nvertices - 1, get_position, &ctx) == RES_BAD_ARG); /* Incoherent medium on a duplicate triangle */ ctx.back_media = medium1_3; - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_BAD_ARG); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_BAD_ARG); /* It is OK dd geometry after a failed add */ ctx.back_media = medium1; - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); /* Coherent medium on duplicate triangle */ ctx.back_media = medium1; - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); /* Coherent medium on duplicate triangle V2 */ ctx.reverse_med = 1; ctx.front_media = medium1; ctx.back_media = medium0; - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); /* Coherent medium on duplicate triangle V3 */ ctx.reverse_med = 0; ctx.reverse_vrtx = 1; - CHK(senc_scene_add_geometry(scn, box_ntriangles, get_indices, get_media, - box_nvertices, get_position, &ctx) == RES_OK); + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); CHK(senc_scene_ref_put(scn) == RES_OK); CHK(senc_device_ref_put(dev) == RES_OK); diff --git a/src/test_senc_utils.h b/src/test_senc_utils.h @@ -16,13 +16,28 @@ #ifndef TEST_UTILS_H #define TEST_UTILS_H +#include <rsys/rsys.h> #include <rsys/mem_allocator.h> +#include <rsys/double3.h> + #include <stdio.h> /******************************************************************************* * Geometry ******************************************************************************/ +/* Distorded cube */ static const double box_vertices[8/*#vertices*/*3/*#coords per vertex*/] = { + 0.1, 0.0, 0.0, + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 1.0, 1.0, 0.0, + 0.0, 0.0, 1.1, + 1.0, 0.0, 1.0, + 0.0, 1.0, 1.0, + 1.0, 1.1, 1.0 +}; +/* Need a true cube for some tests */ +static const double cube_vertices[8/*#vertices*/ * 3/*#coords per vertex*/] = { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, @@ -32,19 +47,22 @@ static const double box_vertices[8/*#vertices*/*3/*#coords per vertex*/] = { 0.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; -static const unsigned box_nvertices = sizeof(box_vertices) / sizeof(double[3]); +static const unsigned nvertices = sizeof(box_vertices) / sizeof(double[3]); +STATIC_ASSERT(sizeof(box_vertices) == sizeof(cube_vertices), + The_2_geometries_must_have_the_same_number_of_vertices); /* The following array lists the indices toward the 3D vertices of each * triangle. - * ,6---,7 ,6----7 - * ,' | ,'/| ,' | \ | Y Z - * 2----3' / | 2', | \ | | ,' - * |', | / ,5 | ',4---,5 0----X - * | ',|/,' | ,' | ,' - * 0----1' 0----1' - * Front, right Back, left and + * ,2---,3 ,2----3 + * ,' | ,'/| ,'/| \ | + * 6----7' / | 6' / | \ | Y + * |', | / ,1 | / ,0---,1 | + * | ',|/,' |/,' | ,' o--X + * 4----5' 4----5' / + * Front, right Back, left and Z * and Top faces bottom faces */ -static const unsigned box_indices[12/*#triangles*/*3/*#indices per triangle*/] = { +static const unsigned +box_indices[12/*#triangles*/*3/*#indices per triangle*/] = { 0, 2, 1, 1, 2, 3, /* Front face */ 0, 4, 2, 2, 4, 6, /* Left face*/ 4, 5, 6, 6, 5, 7, /* Back face */ @@ -52,13 +70,15 @@ static const unsigned box_indices[12/*#triangles*/*3/*#indices per triangle*/] = 2, 6, 3, 3, 6, 7, /* Top face */ 0, 1, 4, 4, 1, 5 /* Bottom face */ }; -static const unsigned box_ntriangles = sizeof(box_indices) / (3 * sizeof(*box_indices)); +static const unsigned +ntriangles = sizeof(box_indices) / (3 * sizeof(*box_indices)); struct context { const double* positions; const unsigned* indices; const unsigned* front_media; const unsigned* back_media; + const unsigned* global_ids; double offset[3]; double scale; char reverse_vrtx, reverse_med; @@ -68,11 +88,12 @@ static const unsigned medium0[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static const unsigned medium1[12] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; static const unsigned medium2[12] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; static const unsigned medium1_3[12] = { 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1 }; -static const unsigned medium1_12[12] = { 1, 1, 1, 1, 1, 1, 1, 1, 12, 1, 1, 1 }; static const unsigned medium1_back0[12] = { 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1 }; static const unsigned medium1_front0[12] = { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; -static void +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) { struct context* ctx = context; @@ -82,7 +103,7 @@ get_indices(const unsigned itri, unsigned ids[3], void* context) ids[ctx->reverse_vrtx ? 1 : 2] = ctx->indices[itri * 3 + 2]; } -static void +static INLINE void get_position(const unsigned ivert, double pos[3], void* context) { struct context* ctx = context; @@ -92,7 +113,7 @@ get_position(const unsigned ivert, double pos[3], void* context) pos[2] = ctx->positions[ivert * 3 + 2] * ctx->scale + ctx->offset[2]; } -static void +static INLINE void get_media(const unsigned itri, unsigned medium[2], void* context) { struct context* ctx = context; @@ -101,39 +122,54 @@ get_media(const unsigned itri, unsigned medium[2], void* context) medium[ctx->reverse_med ? 0 : 1] = ctx->back_media[itri]; } +static INLINE void +get_global_id(const unsigned itri, unsigned* gid, void* context) +{ + struct context* ctx = context; + ASSERT(gid && context); + *gid = ctx->global_ids[itri]; +} + /******************************************************************************* * Miscellaneous ******************************************************************************/ static INLINE void -dump_mesh - (FILE* stream, - const double* pos, - const size_t npos, - const size_t* ids, - const size_t nids) +dump_global + (struct senc_descriptor* desc, + const char* name) { - size_t i; - CHK(pos != NULL && npos != 0); - CHK(ids != NULL && nids != 0); - FOR_EACH(i, 0, npos) { - fprintf(stream, "v %g %g %g\n", SPLIT3(pos+i*3)); + FILE* stream; + unsigned triangles_count, vertices_count, i; + + ASSERT(desc && name); + + CHK(senc_descriptor_get_global_vertices_count(desc, &vertices_count) == RES_OK); + CHK(senc_descriptor_get_global_triangles_count(desc, &triangles_count) == RES_OK); + + stream = fopen(name, "w"); + CHK(stream); + FOR_EACH(i, 0, vertices_count) { + double tmp[3]; + CHK(senc_descriptor_get_global_vertex(desc, i, tmp) == RES_OK); + fprintf(stream, "v %g %g %g\n", SPLIT3(tmp)); } - FOR_EACH(i, 0, nids) { - fprintf(stream, "f %lu %lu %lu\n", - (unsigned long)(ids[i*3+0] + 1), - (unsigned long)(ids[i*3+1] + 1), - (unsigned long)(ids[i*3+2] + 1)); + FOR_EACH(i, 0, triangles_count) { + unsigned indices[3]; + CHK(senc_descriptor_get_global_triangle(desc, i, indices) == RES_OK); + fprintf(stream, "f %lu %lu %lu\n", (unsigned long)(1 + indices[0]), + (unsigned long)(1 + indices[1]), (unsigned long)(1 + indices[2])); } + fclose(stream); } -static void +static INLINE void dump_enclosure (struct senc_descriptor* desc, const unsigned enc, const char* name) { struct senc_enclosure* enclosure; - const struct enclosure_header* header; + struct senc_enclosure_header header; FILE* stream; unsigned count, i; @@ -146,16 +182,16 @@ dump_enclosure stream = fopen(name, "w"); CHK(stream); - FOR_EACH(i, 0, header->vertices_count) { + FOR_EACH(i, 0, header.vertices_count) { double tmp[3]; CHK(senc_enclosure_get_vertex(enclosure, i, tmp) == RES_OK); fprintf(stream, "v %g %g %g\n", SPLIT3(tmp)); } - FOR_EACH(i, 0, header->triangle_count) { + FOR_EACH(i, 0, header.triangle_count) { unsigned indices[3]; CHK(senc_enclosure_get_triangle(enclosure, i, indices) == RES_OK); - fprintf(stream, "f %lu %lu %lu\n", - 1+indices[0], 1+indices[1], 1+indices[2]); + fprintf(stream, "f %lu %lu %lu\n", (unsigned long)(1+indices[0]), + (unsigned long)(1+indices[1]), (unsigned long)(1+indices[2])); } CHK(senc_enclosure_ref_put(enclosure) == RES_OK); fclose(stream); @@ -172,5 +208,93 @@ check_memory_allocator(struct mem_allocator* allocator) } } +/******************************************************************************* + * Check functions + ******************************************************************************/ +static INLINE void check_desc(struct senc_descriptor* desc) +{ + unsigned maxm, ecount, i; + size_t e_cpt = 0; + CHK(senc_descriptor_get_max_medium(desc, &maxm) == RES_OK); + CHK(senc_descriptor_get_enclosure_count(desc, &ecount) == RES_OK); + for(i = 0; i <= maxm; i++) { + unsigned j, ecount_bym; + unsigned found = 0; + CHK(senc_descriptor_get_enclosure_count_by_medium(desc, i, &ecount_bym) == RES_OK); + /* Can be 0 if media numbering is not compact */ + FOR_EACH(j, 0, ecount_bym) { + struct senc_enclosure* enc; + struct senc_enclosure_header h; + unsigned k; + int f = 0; + CHK(senc_descriptor_get_enclosure_by_medium(desc, i, j, &enc) == RES_OK); + CHK(senc_enclosure_get_header(enc, &h) == RES_OK); + ASSERT(h.enclosed_media_count); + FOR_EACH(k, 0, h.enclosed_media_count) { + unsigned m; + CHK(senc_enclosure_get_medium(enc, k, &m) == RES_OK); + found += (m == i); + f += (m == i); + } + ASSERT(f == 1); /* Single reference expected */ + CHK(senc_enclosure_ref_put(enc) == RES_OK); + } + ASSERT(found == ecount_bym); /* All the enclosures enclose medim i */ + e_cpt += ecount_bym; + } + ASSERT(e_cpt >= ecount); /* Every enc has been visited at least once */ +} + +/* Compare the itri-th triangle of enclosure with a triangle described by trg2 & vertices2 */ +static INLINE void +cmp_trg + (const unsigned itri, + const struct senc_enclosure* enclosure, + const unsigned trg2[3], + const double* vertices2, + int* trg_eq, + int* trg_reversed) +{ + unsigned trg1[3]; + double t1[3][3]; + double t2[3][3]; + unsigned trg1_eq[3] = { 3, 3, 3 }; + unsigned i, j, fst_vrtx = 3; + + ASSERT(enclosure && trg2 && vertices2 && trg_eq && trg_reversed); + + CHK(senc_enclosure_get_triangle(enclosure, itri, trg1) == RES_OK); + FOR_EACH(i, 0, 3) { + CHK(senc_enclosure_get_vertex(enclosure, trg1[i], t1[i]) == RES_OK); + d3_set(t2[i], vertices2 + (3 * trg2[i])); + } + FOR_EACH(i, 0, 3) { + FOR_EACH(j, 0, 3) { + if (d3_eq(t1[i], t2[j])) { + trg1_eq[i] = j; + if (i == 0) fst_vrtx = j; + break; + } + } + } + FOR_EACH(i, 0, 3) { + if (trg1_eq[i] == 3) { + *trg_eq = 0; + return; + } + if (trg1_eq[i] == trg1_eq[(i + 1) % 3] + || trg1_eq[i] == trg1_eq[(i + 2) % 3]) { + *trg_eq = 0; + return; + } + } + /* Same 3 vertices */ + ASSERT(fst_vrtx != 3); + *trg_eq = 1; + + *trg_reversed = (trg1_eq[1] != (fst_vrtx + 1) % 3); + ASSERT(*trg_reversed != (trg1_eq[1] != (fst_vrtx + 2) % 3)); +} + #endif /* TEST_UTILS_H */