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 8e023fde7b0b4f0c3c22046d2c83c6e649396713
parent bb7813ddc4ec7b5e5bc58e7a092db4921b4d2159
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed, 17 Jul 2024 11:15:51 +0200

Merge branch 'release_0.7.2'

Diffstat:
MMakefile | 4++++
MREADME.md | 10+++++++++-
Mconfig.mk | 2+-
Msrc/senc3d.h | 7+++++++
Msrc/senc3d_scene.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/senc3d_scene_analyze.c | 313+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/test_senc3d_bad_grouping.c | 4----
Asrc/test_senc3d_bad_grouping2.c | 206+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_senc3d_bad_grouping3.c | 212+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 726 insertions(+), 84 deletions(-)

diff --git a/Makefile b/Makefile @@ -126,6 +126,8 @@ lint: ################################################################################ TEST_SRC =\ src/test_senc3d_bad_grouping.c\ + src/test_senc3d_bad_grouping2.c\ + src/test_senc3d_bad_grouping3.c\ src/test_senc3d_cube_behind_cube.c\ src/test_senc3d_cube_in_cube.c\ src/test_senc3d_cube_on_cube.c\ @@ -213,6 +215,8 @@ $(TEST_OBJ_S3DUT): config.mk senc3d-local.pc -c $(@:.o=.c) -o $@ test_senc3d_bad_grouping \ +test_senc3d_bad_grouping2 \ +test_senc3d_bad_grouping3 \ test_senc3d_cube_behind_cube \ test_senc3d_cube_in_cube \ test_senc3d_cube_on_cube \ diff --git a/README.md b/README.md @@ -37,9 +37,17 @@ Edit config.mk as needed, then run: ## Release notes +### Version 0.7.2 + +- Another correction in the code that groups connex components to create + enclosures. +- Add function `senc3d_scene_dump_enclosure_obj` which writes an Obj + file of a given enclosure geometry. + ### Version 0.7.1 -- Fixes a bug in the code that groups connex components to create enclosures. +- Fixes a bug in the code that groups connex components to create + enclosures. ### Version 0.7 diff --git a/config.mk b/config.mk @@ -1,4 +1,4 @@ -VERSION = 0.7.1 +VERSION = 0.7.2 PREFIX = /usr/local LIB_TYPE = SHARED diff --git a/src/senc3d.h b/src/senc3d.h @@ -333,6 +333,13 @@ senc3d_scene_get_overlapping_triangle const unsigned idx, unsigned* id); +/* Dump a given enclosure in an OBJ file */ +SENC3D_API res_T +senc3d_scene_dump_enclosure_obj + (struct senc3d_scene* scn, + const unsigned enc, + const char* filename); + SENC3D_API res_T senc3d_scene_ref_get (struct senc3d_scene* scene); diff --git a/src/senc3d_scene.c b/src/senc3d_scene.c @@ -319,6 +319,58 @@ senc3d_scene_get_vertex } res_T +senc3d_scene_dump_enclosure_obj + (struct senc3d_scene* scn, + const unsigned enc, + const char* name) +{ + struct senc3d_enclosure* enclosure = NULL; + struct senc3d_enclosure_header header; + FILE* stream = NULL; + unsigned count, i; + res_T res = RES_OK; + + if(!scn || !name) { + res = RES_BAD_ARG; + goto error; + } + + OK(senc3d_scene_get_enclosure_count(scn, &count)); + if(enc >= count) { + res = RES_BAD_ARG; + goto error; + } + + OK(senc3d_scene_get_enclosure(scn, enc, &enclosure)); + OK(senc3d_enclosure_get_header(enclosure, &header)); + + stream = fopen(name, "w"); + if(!stream) { + res = RES_BAD_ARG; + goto error; + } + + FOR_EACH(i, 0, header.vertices_count) { + double tmp[3]; + OK(senc3d_enclosure_get_vertex(enclosure, i, tmp)); + fprintf(stream, "v %g %g %g\n", SPLIT3(tmp)); + } + FOR_EACH(i, 0, header.primitives_count) { + unsigned indices[3]; + OK(senc3d_enclosure_get_triangle(enclosure, i, indices)); + fprintf(stream, "f %u %u %u\n", + 1+indices[0], 1+indices[1], 1+indices[2]); + } + +exit: + if(enclosure) SENC3D(enclosure_ref_put(enclosure)); + if(stream) fclose(stream); + return res; +error: + goto exit; +} + +res_T senc3d_scene_ref_get(struct senc3d_scene* scn) { if(!scn) return RES_BAD_ARG; diff --git a/src/senc3d_scene_analyze.c b/src/senc3d_scene_analyze.c @@ -67,12 +67,14 @@ const struct cc_descriptor CC_DESCRIPTOR_NULL = CC_DESCRIPTOR_NULL__; * If ray.normal < threshold we suspect accuracy could be a problem */ #define DOT_THRESHOLD 0.0001f -struct filter_ctx0 { - component_id_t origin_component; - struct darray_triangle_comp* triangles_comp; +enum ctx_type { + CTX0, + CTX1, + CTX2 }; -struct filter_ctx1 { +struct filter_ctx { + enum ctx_type type; struct senc3d_scene* scn; struct s3d_scene_view* view; component_id_t origin_component; @@ -80,34 +82,14 @@ struct filter_ctx1 { struct darray_ptr_component_descriptor* components; /* Tmp data used across filter calls */ double current_6volume; + int cpt; float s; - /* Result of hit */ + /* Result of CTX1 hit */ component_id_t hit_component; float hit_dir[3], hit_dist; struct s3d_primitive hit_prim; }; -struct filter_ctx2 { - struct darray_triangle_comp* triangles_comp; - int cpt; - component_id_t component; -}; - -enum fctx_type { - FCTX0, - FCTX1, - FCTX2 -}; - -struct filter_ctx { - enum fctx_type type; - union { - struct filter_ctx0 ctx0; - struct filter_ctx1 ctx1; - struct filter_ctx2 ctx2; - } c; -}; - #define HTABLE_NAME overlap #define HTABLE_KEY trg_id_t #define HTABLE_DATA char @@ -129,7 +111,7 @@ static FINLINE int is_component_inside (struct cc_descriptor* cc1, struct cc_descriptor* cc2, - struct filter_ctx1* ctx) + struct filter_ctx* ctx) { int i; side_id_t side; @@ -184,13 +166,13 @@ is_component_inside d3_divd(pt, pt, 3); f3_set_d3(org, pt); /* Trace a ray and count intersections with component c */ - ctx2.type = FCTX2; - ctx2.c.ctx2.triangles_comp = ctx->triangles_comp; - ctx2.c.ctx2.cpt = 0; - ctx2.c.ctx2.component = cc1->cc_id; + ctx2.type = CTX2; + ctx2.triangles_comp = ctx->triangles_comp; + ctx2.cpt = 0; + ctx2.origin_component = cc1->cc_id; S3D(scene_view_trace_ray(ctx->view, org, dir, rg, &ctx2, &hit)); /* cc2 is not inside cc1 if cpt is even */ - if(ctx2.c.ctx2.cpt % 2 == 0) return 0; + if(ctx2.cpt % 2 == 0) return 0; return 1; } @@ -256,68 +238,204 @@ self_hit_filter void* ray_data, void* filter_data) { - struct filter_ctx* fctx_ = ray_data; + struct filter_ctx* ctx = ray_data; (void)ray_org; (void)ray_range; (void)filter_data; - ASSERT(fctx_); + ASSERT(ctx); ASSERT(hit->uv[0] == CLAMP(hit->uv[0], 0, 1)); ASSERT(hit->uv[1] == CLAMP(hit->uv[1], 0, 1)); - switch (fctx_->type) { + switch (ctx->type) { default: FATAL("Invalid"); - case FCTX2: { + case CTX2: { /* The filter is used to count the hits on some component along an * infinite ray */ - struct filter_ctx2* ctx2 = &fctx_->c.ctx2; const struct triangle_comp* trg_comp; const component_id_t* hit_comp; ASSERT(hit->prim.prim_id - < darray_triangle_comp_size_get(ctx2->triangles_comp)); - trg_comp = darray_triangle_comp_cdata_get(ctx2->triangles_comp); + < darray_triangle_comp_size_get(ctx->triangles_comp)); + trg_comp = darray_triangle_comp_cdata_get(ctx->triangles_comp); hit_comp = trg_comp[hit->prim.prim_id].component; - if(hit_comp[SENC3D_FRONT] == ctx2->component - || hit_comp[SENC3D_BACK] == ctx2->component) + if(hit_comp[SENC3D_FRONT] == ctx->origin_component + || hit_comp[SENC3D_BACK] == ctx->origin_component) { - ctx2->cpt++; + ctx->cpt++; } return 1; /* Reject to continue counting */ } - case FCTX0: { + case CTX0: { /* This filter is called from a closest point query from a point belonging * to origin_component. The returned hit is used to determine the search - * radius for FCTX1 main computation. */ - struct filter_ctx0* ctx = &fctx_->c.ctx0; + * radius for CTX1 main computation. */ const struct triangle_comp* trg_comp = darray_triangle_comp_cdata_get(ctx->triangles_comp); const component_id_t* hit_comp = trg_comp[hit->prim.prim_id].component; const component_id_t oc = ctx->origin_component; + vrtx_id_t other_id; + struct cc_descriptor* const* comp_descriptors + = darray_ptr_component_descriptor_cdata_get(ctx->components); + size_t compsz = darray_ptr_component_descriptor_size_get(ctx->components); + const union double3* + vertices = darray_position_cdata_get(&ctx->scn->vertices); + const double org_z = vertices[comp_descriptors[oc]->max_z_vrtx_id].pos.z; + float s = 0, hit_normal[3], rdir[3]; + enum senc3d_side hit_side; + const int log_components = + ctx->scn->convention & SENC3D_LOG_COMPONENTS_INFORMATION; ASSERT(hit->prim.prim_id < darray_triangle_comp_size_get(ctx->triangles_comp)); + ASSERT(hit_comp[SENC3D_FRONT] < compsz); + ASSERT(hit_comp[SENC3D_BACK] < compsz); + (void)compsz; /* Avoid "unused variable" warning */ + /* Hit acceptance must be coherent at CTX0 and FTCX1 stages: + * the search radius as found at stage CTX0 must include the hit that + * stage CTX1 will select using an infinite radius */ if(hit_comp[SENC3D_FRONT] == oc || hit_comp[SENC3D_BACK] == oc) { - /* Self hit */ - return 1; /* Reject */ + return 1; /* Self hit, reject */ + } + if(hit->distance == 0) { + /* origin component is in contact with some other components + * We will need further exploration to know if they should be considered + * Accepting hit at distance 0 => radius is definitively 0 */ + int n; + + /* If same component, process only once */ + FOR_EACH(n, 0, (hit_comp[SENC3D_FRONT] == hit_comp[SENC3D_BACK] ? 1 : 2)) { + const enum senc3d_side sides[2] = { SENC3D_FRONT, SENC3D_BACK }; + component_id_t c = hit_comp[sides[n]]; + ASSERT(c < darray_ptr_component_descriptor_size_get(ctx->components)); + if(comp_descriptors[c]->is_outer_border) { + /* The inner component we are trying to link can only be linked to + * an outer component if it is inside */ + if(!is_component_inside(comp_descriptors[c], + comp_descriptors[oc], ctx)) + { + continue; + } + + if(log_components) { + #pragma omp critical + printf("Component #%u: decreasing search radius " + "(R=%g, n=%g,%g,%g, components: %u, %u)\n", + oc, hit->distance, SPLIT3(hit->normal), + hit_comp[SENC3D_FRONT], hit_comp[SENC3D_BACK]); + } + return 0; + } else { + /* c is an inner component */ + vrtx_id_t c_z_id; + /* The inner component we are trying to link can only be linked to + * another inner component if (at least partly) above and not inside */ + c_z_id = comp_descriptors[c]->max_z_vrtx_id; + ASSERT(c_z_id < darray_position_size_get(&ctx->scn->vertices)); + ASSERT(vertices[c_z_id].pos.z >= org_z); + if(vertices[c_z_id].pos.z == org_z) { + continue; /* Not above */ + } + if(is_component_inside(comp_descriptors[c], + comp_descriptors[oc], ctx)) + { + continue; /* Inside */ + } + if(log_components) { + #pragma omp critical + printf("Component #%u: decreasing search radius " + "(R=%g, n=%g,%g,%g, components: %u, %u)\n", + oc, hit->distance, SPLIT3(hit->normal), + hit_comp[SENC3D_FRONT], hit_comp[SENC3D_BACK]); + } + return 0; + } + } + return 1; + } + + ASSERT(hit->distance > 0); + if(hit_comp[SENC3D_FRONT] == hit_comp[SENC3D_BACK]) { + /* Hit component is known, check if above */ + other_id = comp_descriptors[hit_comp[SENC3D_FRONT]]->max_z_vrtx_id; + ASSERT(other_id < darray_position_size_get(&ctx->scn->vertices)); + if(vertices[other_id].pos.z <= org_z) { + return 1; + } + if(log_components) { + #pragma omp critical + printf("Component #%u: decreasing search radius " + "(R=%g, n=%g,%g,%g, components: %u, %u)\n", + oc, hit->distance, SPLIT3(hit->normal), + hit_comp[SENC3D_FRONT], hit_comp[SENC3D_BACK]); + } + return 0; } - if(hit->distance > 0 && ray_dir[2] <= 0) { - return 1; /* Not upward */ + /* Compute hit side */ + /* For s to be comparable, vectors must be normalized */ + f3_normalize(hit_normal, hit->normal); + f3_normalize(rdir, ray_dir); + s = f3_dot(rdir, hit_normal); /* Can be NaN for tiny distances */ + if(isnan(s)) { + /* Try to fix it */ + f3_divf(rdir, ray_dir, hit->distance); + f3_normalize(rdir, rdir); + s = f3_dot(rdir, hit_normal); + ASSERT(!isnan(s)); } - return 0; /* Keep*/ + if(fabsf(s) < DOT_THRESHOLD) { + /* We cannot know for sure which side to consider */ + vrtx_id_t i1 = comp_descriptors[hit_comp[SENC3D_FRONT]]->max_z_vrtx_id; + vrtx_id_t i2 = comp_descriptors[hit_comp[SENC3D_BACK]]->max_z_vrtx_id; + double possible_z = MMIN(vertices[i1].pos.z, vertices[i2].pos.z); + if(possible_z > org_z) { + /* Both components are above origin component => keep */ + if(log_components) { + #pragma omp critical + printf("Component #%u: decreasing search radius " + "(R=%g, n=%g,%g,%g, components: %u, %u)\n", + oc, hit->distance, SPLIT3(hit->normal), + hit_comp[SENC3D_FRONT], hit_comp[SENC3D_BACK]); + } + return 0; + } + /* Cannot be sure => the safest choice is to reject */ + return 1; + } + /* Determine which side was hit */ + hit_side = + ((s < 0) /* Facing geometrical normal of hit */ + == ((ctx->scn->convention & SENC3D_CONVENTION_NORMAL_FRONT) != 0)) + /* Warning: following Embree 2 convention for geometrical normals, + * the Star3D hit normal is left-handed while star-enclosures-3d uses + * right-handed convention */ + ? SENC3D_BACK : SENC3D_FRONT; + other_id = comp_descriptors[hit_comp[hit_side]]->max_z_vrtx_id; + ASSERT(other_id < darray_position_size_get(&ctx->scn->vertices)); + if(vertices[other_id].pos.z <= org_z) { + return 1; + } + if(log_components) { + #pragma omp critical + printf("Component #%u: decreasing search radius " + "(R=%g, n=%g,%g,%g, components: %u, %u)\n", + oc, hit->distance, SPLIT3(hit->normal), + hit_comp[SENC3D_FRONT], hit_comp[SENC3D_BACK]); + } + return 0; } - case FCTX1: { + case CTX1: { /* This filter is called from a closest point query from a point belonging * to origin_component. The returned hit is used to determine a component * to which origin_component is linked. At a later stage the algorithm * process linked components to determine their relative inclusions. * * This filter is called with a search distance that has been ajusted in - * FCTX0 filter. This distance must be left unchanged to ensure visiting + * CTX0 filter. This distance must be left unchanged to ensure visiting * all the surfaces at the determined distance: allways reject hits to * avoid decreasing search distance. * @@ -330,7 +448,6 @@ self_hit_filter * (greater volume needed), or they can be disjoint, with (at least) ray_org * as a common vertex (they can also partially intersect, but this is invalid * and remains undetected by star enclosures). */ - struct filter_ctx1* ctx = &fctx_->c.ctx1; struct cc_descriptor* const* comp_descriptors = darray_ptr_component_descriptor_cdata_get(ctx->components); const struct triangle_comp* @@ -355,24 +472,33 @@ self_hit_filter < darray_triangle_comp_size_get(ctx->triangles_comp)); if(log_components) { + #pragma omp critical printf("Component #%u: investigating hit " "(d=%g, n=%g,%g,%g, components: %u, %u)\n", oc, hit->distance, SPLIT3(hit->normal), hit_comp[SENC3D_FRONT], hit_comp[SENC3D_BACK]); } + /* Has CTX1 filter do not reduce search radius, hits can be processed + * that are at farther distance than the current best candidate: we need + * to reject them here */ if(hit->distance > ctx->hit_dist) { /* No improvement */ if(log_components) { + #pragma omp critical printf("Component #%u: further away (%g): reject\n", oc, ctx->hit_dist); } return 1; } + /* Hit acceptance must be coherent at CTX0 and FTCX1 stages: + * the search radius as found at stage CTX0 must include the hit that + * stage CTX1 will select using an infinite radius */ if(hit_comp[SENC3D_FRONT] == oc || hit_comp[SENC3D_BACK] == oc) { /* Self hit */ if(log_components) { + #pragma omp critical printf("Component #%u: self hit: reject\n", oc); } return 1; @@ -391,6 +517,7 @@ self_hit_filter if(c == ctx->hit_component) { /* Cannot change ctx->hit_component */ if(log_components) { + #pragma omp critical printf("Component #%u: hit component #%u and already linked to it:" " reject\n", oc, c); @@ -402,12 +529,14 @@ self_hit_filter /* The inner component we are trying to link can only be linked to * an outer component if it is inside */ if(log_components) { + #pragma omp critical printf("Component #%u: hit outer component #%u\n", oc, c); } if(!is_component_inside(comp_descriptors[c], comp_descriptors[oc], ctx)) { if(log_components) { + #pragma omp critical printf("Component #%u: not inside: reject\n", oc); } continue; @@ -419,7 +548,7 @@ self_hit_filter * we are looking for is the smallest one (to manage outer component * inside another outer component). */ if((ctx->hit_component != COMPONENT_NULL__ - && !comp_descriptors[ctx->hit_component]->is_outer_border ) + && !comp_descriptors[ctx->hit_component]->is_outer_border) || v < ctx->current_6volume) { ctx->hit_component = c; ctx->current_6volume = v; @@ -427,10 +556,12 @@ self_hit_filter ctx->hit_prim = hit->prim; if(log_components) { if(v < ctx->current_6volume) { + #pragma omp critical printf("Component #%u: currently the smaller one: " "keep component #%u\n", oc, ctx->hit_component); } else { + #pragma omp critical printf("Component #%u: change from inner to outer: " "keep component #%u\n", oc, ctx->hit_component); @@ -438,6 +569,7 @@ self_hit_filter } } else { if(log_components) { + #pragma omp critical printf("Component #%u: not the smaller one: reject\n", oc); } continue; @@ -449,12 +581,14 @@ self_hit_filter /* If we've already found a valid outer component, inner components * should not be considered anymore */ if(log_components) { + #pragma omp critical printf("Component #%u: hit inner component #%u\n", oc, c); } if(ctx->hit_component != COMPONENT_NULL__ - && comp_descriptors[ctx->hit_component]->is_outer_border ) + && comp_descriptors[ctx->hit_component]->is_outer_border) { if(log_components) { + #pragma omp critical printf("Component #%u: already in an outer component: reject\n", oc); } @@ -468,6 +602,7 @@ self_hit_filter ASSERT(vertices[c_z_id].pos.z >= org_z); if(vertices[c_z_id].pos.z == org_z) { if(log_components) { + #pragma omp critical printf("Component #%u: not (even in part) above: reject\n", oc); } continue; /* Not above */ @@ -476,6 +611,7 @@ self_hit_filter comp_descriptors[oc], ctx)) { if(log_components) { + #pragma omp critical printf("Component #%u: not outside: reject\n", oc); } continue; /* Inside */ @@ -490,12 +626,14 @@ self_hit_filter ctx->hit_dist = 0; ctx->hit_prim = hit->prim; if(log_components) { + #pragma omp critical printf("Component #%u: currently the bigger one: " "keep component #%u\n", oc, ctx->hit_component); } } else { if(log_components) { + #pragma omp critical printf("Component #%u: not the bigger one: reject\n", oc); } continue; @@ -512,8 +650,8 @@ self_hit_filter ASSERT(other_id < darray_position_size_get(&ctx->scn->vertices)); if(vertices[other_id].pos.z <= org_z) { if(log_components) { - printf("Component #%u: 2 sides, not (even in part) above: reject\n", - oc); + #pragma omp critical + printf("Component #%u: 2 sides, not above: reject\n", oc); } return 1; } @@ -523,6 +661,7 @@ self_hit_filter ctx->hit_dist = hit->distance; ctx->hit_prim = hit->prim; if(log_components) { + #pragma omp critical printf("Component #%u: 2 sides with same component: " "keep component #%u\n", oc, ctx->hit_component); @@ -531,7 +670,7 @@ self_hit_filter } /* Compute hit side */ - /* For s to be comparable, vectors must be normailzed */ + /* For s to be comparable, vectors must be normalized */ f3_normalize(hit_normal, hit->normal); f3_normalize(rdir, ray_dir); s = f3_dot(rdir, hit_normal); /* Can be NaN for tiny distances */ @@ -542,6 +681,7 @@ self_hit_filter s = f3_dot(rdir, hit_normal); ASSERT(!isnan(s)); if(log_components) { + #pragma omp critical printf("Component #%u: had to fix s (was NaN)\n", oc); } } @@ -549,6 +689,7 @@ self_hit_filter if(ctx->hit_dist == hit->distance && fabsf(ctx->s) >= fabsf(s)) { /* Same distance with no s improvement: keep the previous hit */ if(log_components) { + #pragma omp critical printf("Component #%u: not improving s (%g VS %g): reject\n", oc, s, ctx->s); } @@ -561,8 +702,10 @@ self_hit_filter vrtx_id_t i2 = comp_descriptors[hit_comp[SENC3D_BACK]]->max_z_vrtx_id; double possible_z = MMAX(vertices[i1].pos.z, vertices[i2].pos.z); if(possible_z <= org_z) { + /* None of the components are above origin component => reject */ if(log_components) { - printf("Component #%u: tiny s, not (even in part) above: reject\n", + #pragma omp critical + printf("Component #%u: none of the components above: reject\n", oc); } return 1; @@ -573,6 +716,7 @@ self_hit_filter ctx->hit_dist = hit->distance; ctx->hit_prim = hit->prim; if(log_components) { + #pragma omp critical printf("Component #%u: tiny s (%g): " "keep but don't know the component\n", oc, s); @@ -591,6 +735,7 @@ self_hit_filter ASSERT(other_id < darray_position_size_get(&ctx->scn->vertices)); if(vertices[other_id].pos.z <= org_z) { if(log_components) { + #pragma omp critical printf("Component #%u: standard s, not (even in part) above: reject\n", oc); } @@ -602,6 +747,7 @@ self_hit_filter ctx->hit_dist = hit->distance; ctx->hit_prim = hit->prim; if(log_components) { + #pragma omp critical printf("Component #%u: standard s, (%g): keep component #%u\n", oc, s, ctx->hit_component); } @@ -1151,7 +1297,7 @@ group_connex_components ASSERT(scn->analyze.enclosures_count == 1); #ifndef NDEBUG -#pragma omp single + #pragma omp single { /* Ensure reproducible cc_ids for debugging purpose */ *res = reorder_components(scn, connex_components, triangles_comp); @@ -1164,6 +1310,7 @@ group_connex_components cc_count = (component_id_t)tmp; positions = darray_position_cdata_get(&scn->vertices); + #pragma omp single if(dump_components) { /* Do it now before any other problem can occur (fingers crossed). * Do it sequential and not optimized as it is debug code. @@ -1222,14 +1369,16 @@ group_connex_components scene_cpt++; } - - ctx0.type = FCTX0; - ctx0.c.ctx0.triangles_comp = triangles_comp; - ctx1.type = FCTX1; - ctx1.c.ctx1.scn = scn; - ctx1.c.ctx1.view = s3d_view; - ctx1.c.ctx1.triangles_comp = triangles_comp; - ctx1.c.ctx1.components = connex_components; + ctx0.type = CTX0; + ctx0.scn = scn; + ctx0.view = s3d_view; + ctx0.triangles_comp = triangles_comp; + ctx0.components = connex_components; + ctx1.type = CTX1; + ctx1.scn = scn; + ctx1.view = s3d_view; + ctx1.triangles_comp = triangles_comp; + ctx1.components = connex_components; *res = s3d_scene_view_get_aabb(s3d_view, lower, upper); if(*res != RES_OK) goto end; #pragma omp for schedule(dynamic) @@ -1255,6 +1404,7 @@ group_connex_components ASSERT(id <= ENCLOSURE_MAX__); cc->enclosure_id = (enclosure_id_t)id; if(log_components) { + #pragma omp critical printf("Component #%u: is outer, not processed\n", c); } continue; @@ -1270,8 +1420,11 @@ group_connex_components rrr[i] = MMIN(origin[i] - lower[i], upper[i] - origin[i]); } rrr[2] = upper[2] - origin[2]; - r = f3_len(rrr) + FLT_EPSILON; /* Ensure r > 0 */ - ctx0.c.ctx0.origin_component = cc->cc_id; + r = f3_len(rrr) * (1 + FLT_EPSILON) + FLT_EPSILON; /* Ensure r > 0 */ + ctx0.origin_component = cc->cc_id; + ctx0.current_6volume = DBL_MAX; + ctx0.hit_dist = FLT_MAX; + ctx0.hit_component = COMPONENT_NULL__; tmp_res = s3d_scene_view_closest_point(s3d_view, origin, r, &ctx0, &hit); if(tmp_res != RES_OK) { *res = tmp_res; @@ -1282,20 +1435,22 @@ group_connex_components cc->cc_group_root = CC_GROUP_ROOT_INFINITE; cc->enclosure_id = 0; if(log_components) { + #pragma omp critical printf("Component #%u: is part of enclosure #0\n", c); } continue; } /* Second step is to determine which component faces component #c */ - ctx1.c.ctx1.origin_component = cc->cc_id; - ctx1.c.ctx1.current_6volume = DBL_MAX; - ctx1.c.ctx1.hit_dist = FLT_MAX; - ctx1.c.ctx1.hit_component = COMPONENT_NULL__; + ctx1.origin_component = cc->cc_id; + ctx1.current_6volume = DBL_MAX; + ctx1.hit_dist = FLT_MAX; + ctx1.hit_component = COMPONENT_NULL__; /* New search radius is hit.distance + some margin to cope with numerical * issues (and r==0 is an error) */ r = hit.distance * (1 + FLT_EPSILON) + FLT_EPSILON; if(log_components) { + #pragma omp critical printf("Component #%u: starting search for components (R=%g) from %g %g %g\n", c, r, SPLIT3(origin)); } @@ -1305,18 +1460,19 @@ group_connex_components *res = tmp_res; continue; } - /* As FCTX1 filter rejects any hit, do not rely on hit but use result as + /* As CTX1 filter rejects any hit, do not rely on hit but use result as * stored in ctx1 */ /* If no hit is accepted, the component is facing an infinite medium */ - if(ctx1.c.ctx1.hit_dist == FLT_MAX) { + if(ctx1.hit_dist == FLT_MAX) { cc->cc_group_root = CC_GROUP_ROOT_INFINITE; cc->enclosure_id = 0; if(log_components) { + #pragma omp critical printf("Component #%u: is part of enclosure #0\n", c); } continue; } - else if(ctx1.c.ctx1.hit_component == COMPONENT_NULL__) { + else if(ctx1.hit_component == COMPONENT_NULL__) { /* The selected triangle was nearly parallel to the line of sight: * FRONT/BACK discrimination was not reliable enough and should be done * differently. */ @@ -1327,9 +1483,10 @@ group_connex_components continue; } else { /* If hit, group this component */ - cc->cc_group_root = ctx1.c.ctx1.hit_component; + cc->cc_group_root = ctx1.hit_component; ASSERT(cc->cc_group_root < cc_count); if(log_components) { + #pragma omp critical printf("Component #%u: linked to component #%u\n", c, cc->cc_group_root); } } diff --git a/src/test_senc3d_bad_grouping.c b/src/test_senc3d_bad_grouping.c @@ -580,10 +580,6 @@ main(int argc, char** argv) OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); OK(senc3d_device_create(NULL, &allocator, SENC3D_NTHREADS_DEFAULT, 1, &dev)); - /* Create a scene with the cubes. - * The enclosures in the small cubes contain medium 0, the external enclosure - * contains medium 2, the enclosure between the small and big cubes - * contains medium 1. */ ctx.positions = bat_1_S_glazing_vertices; ctx.indices = bat_1_S_glazing_triangles; ctx.properties = bat_1_S_glazing_properties; diff --git a/src/test_senc3d_bad_grouping2.c b/src/test_senc3d_bad_grouping2.c @@ -0,0 +1,206 @@ +/* Copyright (C) 2018-2020, 2023, 2024 |Méso|Star> (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/>. */ + +/* This test has been created using the sg3_geometry_dump_as_C_code feature + * of star-geometry. It uses a star-cad scene that crashed. */ + +#include "senc3d.h" +#include "test_senc3d_utils.h" + +#include <rsys/double3.h> + +#include <stdio.h> + +/* + * +--------------------------+ + * | | + * | +--+ | + * | | | | + * | | | | + * | | | | + * | | | ++ | + * | | | ++ | + * | | | | + * | | | | + * | +--+ | + * | | + * +--------------------------+ +*/ + +static const unsigned bad2_vertices_count = 24; +static const double bad2_vertices[72] = +{ + 0, 10, 10, + 0, 0, 0, + 0, 0, 10, + 0, 10, 0, + 10, 10, 10, + 10, 0, 10, + 10, 0, 0, + 10, 10, 0, + 1, 9, 9, + 1, 1, 1, + 1, 1, 9, + 1, 9, 1, + 2, 9, 9, + 2, 1, 9, + 2, 1, 1, + 2, 9, 1, + 3, 5, 5, + 3, 4, 4, + 3, 4, 5, + 3, 5, 4, + 4, 5, 5, + 4, 4, 5, + 4, 4, 4, + 4, 5, 4, +}; +unsigned bad2_triangles_count = 36; +unsigned bad2_triangles[600] = +{ + 0, 1, 2, + 0, 3, 1, + 4, 5, 6, + 4, 6, 7, + 6, 2, 1, + 5, 2, 6, + 7, 3, 0, + 4, 7, 0, + 3, 6, 1, + 7, 6, 3, + 0, 2, 5, + 4, 0, 5, + 8, 9, 10, + 8, 11, 9, + 12, 13, 14, + 12, 14, 15, + 14, 10, 9, + 13, 10, 14, + 15, 11, 8, + 12, 15, 8, + 11, 14, 9, + 15, 14, 11, + 8, 10, 13, + 12, 8, 13, + 16, 17, 18, + 16, 19, 17, + 20, 21, 22, + 20, 22, 23, + 22, 18, 17, + 21, 18, 22, + 23, 19, 16, + 20, 23, 16, + 19, 22, 17, + 23, 22, 19, + 16, 18, 21, + 20, 16, 21 +}; +unsigned bad2_properties[108] = +{ + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM +}; + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct senc3d_device* dev = NULL; + struct senc3d_scene* scn = NULL; + struct context ctx = CONTEXT_NULL__; + unsigned count, e; + const double volumes[] = { -1000, 935, 64, 1}; + int volume_used[] = {0, 0, 0, 0}; + (void)argc, (void)argv; + + OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); + OK(senc3d_device_create(NULL, &allocator, SENC3D_NTHREADS_DEFAULT, 1, &dev)); + + ctx.positions = bad2_vertices; + ctx.indices = bad2_triangles; + ctx.properties = bad2_properties; + OK(senc3d_scene_create(dev, + SENC3D_CONVENTION_NORMAL_BACK | SENC3D_CONVENTION_NORMAL_OUTSIDE, + bad2_triangles_count, get_indices, get_media_from_properties, + bad2_vertices_count, get_position, &ctx, &scn)); + + OK(senc3d_scene_get_vertices_count(scn, &count)); + CHK(count == bad2_vertices_count); + + OK(senc3d_scene_get_triangles_count(scn, &count)); + CHK(count == bad2_triangles_count); + + OK(senc3d_scene_get_enclosure_count(scn, &count)); + CHK(count == 4); + FOR_EACH(e, 0, count) { + struct senc3d_enclosure* enclosure; + struct senc3d_enclosure_header header; + size_t i; + int found = 0; + OK(senc3d_scene_get_enclosure(scn, e, &enclosure)); + OK(senc3d_enclosure_get_header(enclosure, &header)); + OK(senc3d_enclosure_ref_put(enclosure)); + for(i = 0; i < sizeof(volumes)/sizeof(*volumes); i++) { + if(!volume_used[i] && fabs(volumes[i] - header.volume) < DBL_EPSILON) { + volume_used[i] = 1; + found = 1; + break; + } + } + if(!found) return 1; + } + + OK(senc3d_scene_ref_put(scn)); + OK(senc3d_device_ref_put(dev)); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_senc3d_bad_grouping3.c b/src/test_senc3d_bad_grouping3.c @@ -0,0 +1,212 @@ +/* Copyright (C) 2018-2020, 2023, 2024 |Méso|Star> (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/>. */ + +/* This test has been created using the sg3_geometry_dump_as_C_code feature + * of star-geometry. It uses a star-cad scene that crashed. */ + +#include "senc3d.h" +#include "test_senc3d_utils.h" + +#include <rsys/double3.h> + +#include <stdio.h> + +/* + * +-------------+ + * | | + * | + | + * | /|\ | + * | / | \ | + * | + | + | + * | | | | | + * | | | | | + * | +--+--+ | + * | | + * +-------------+ +*/ + +static const unsigned bad3_vertices_count = 24; +static const double bad3_vertices[72] = +{ + 0, 10, 10, + 0, 0, 0, + 0, 0, 10, + 0, 10, 0, + 10, 10, 10, + 10, 0, 10, + 10, 0, 0, + 10, 10, 0, + + 1, 5, 5, + 1, 1, 1, + 1, 1, 5, + 1, 5, 1, + 5, 5, 6, + 5, 1, 6, + 5, 1, 1, + 5, 5, 1, + + 5, 5, 6.12, /* Duplicate, use #12 instead */ + 5, 1, 1.14, /* Duplicate, use #14 instead */ + 5, 1, 6.13, /* Duplicate, use #13 instead */ + 5, 5, 1.15, /* Duplicate, use #15 instead */ + 9, 5, 5, + 9, 1, 5, + 9, 1, 1, + 9, 5, 1 +}; +unsigned bad3_triangles_count = 34; +unsigned bad3_triangles[582] = +{ + 0, 1, 2, + 0, 3, 1, + 4, 5, 6, + 4, 6, 7, + 6, 2, 1, + 5, 2, 6, + 7, 3, 0, + 4, 7, 0, + 3, 6, 1, + 7, 6, 3, + 0, 2, 5, + 4, 0, 5, + + 8, 9, 10, + 8, 11, 9, + 12, 13, 14, + 12, 14, 15, + 14, 10, 9, + 13, 10, 14, + 15, 11, 8, + 12, 15, 8, + 11, 14, 9, + 15, 14, 11, + 8, 10, 13, + 12, 8, 13, + + /* 12, 14, 13, */ /* Duplicate */ + /* 12, 15, 14, */ /* Duplicate */ + 20, 21, 22, + 20, 22, 23, + 22, 13, 14, + 21, 13, 22, + 23, 15, 12, + 20, 23, 12, + 15, 22, 14, + 23, 22, 15, + 12, 13, 21, + 20, 12, 21 +}; +unsigned bad3_properties[102] = +{ + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + /* SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, */ + /* SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, */ + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, + SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM +}; + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct senc3d_device* dev = NULL; + struct senc3d_scene* scn = NULL; + struct context ctx = CONTEXT_NULL__; + unsigned count, e; + const double volumes[] = { -1000, 856, 72, 72}; + int volume_used[] = {0, 0, 0, 0}; + (void)argc, (void)argv; + + OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); + OK(senc3d_device_create(NULL, &allocator, SENC3D_NTHREADS_DEFAULT, 1, &dev)); + + /* Create a scene with the cubes. + * The enclosures in the small cubes contain medium 0, the external enclosure + * contains medium 2, the enclosure between the small and big cubes + * contains medium 1. */ + ctx.positions = bad3_vertices; + ctx.indices = bad3_triangles; + ctx.properties = bad3_properties; + OK(senc3d_scene_create(dev, + SENC3D_CONVENTION_NORMAL_BACK | SENC3D_CONVENTION_NORMAL_OUTSIDE, + bad3_triangles_count, get_indices, get_media_from_properties, + bad3_vertices_count, get_position, &ctx, &scn)); + + OK(senc3d_scene_get_vertices_count(scn, &count)); + CHK(count == bad3_vertices_count); + + OK(senc3d_scene_get_triangles_count(scn, &count)); + CHK(count == bad3_triangles_count); + + OK(senc3d_scene_get_enclosure_count(scn, &count)); + CHK(count == 4); + FOR_EACH(e, 0, count) { + struct senc3d_enclosure* enclosure; + struct senc3d_enclosure_header header; + size_t i; + int found = 0; + OK(senc3d_scene_get_enclosure(scn, e, &enclosure)); + OK(senc3d_enclosure_get_header(enclosure, &header)); + OK(senc3d_enclosure_ref_put(enclosure)); + for(i = 0; i < sizeof(volumes)/sizeof(*volumes); i++) { + if(!volume_used[i] && fabs(volumes[i] - header.volume) < DBL_EPSILON) { + volume_used[i] = 1; + found = 1; + break; + } + } + if(!found) return 1; + } + + OK(senc3d_scene_ref_put(scn)); + OK(senc3d_device_ref_put(dev)); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; +}