diff options
author | Howard Trickey <howard.trickey@gmail.com> | 2021-02-28 02:51:48 +0300 |
---|---|---|
committer | Howard Trickey <howard.trickey@gmail.com> | 2021-02-28 02:51:48 +0300 |
commit | f3d60c68ef469a9a9de8d5dc4d7dbbd168950ceb (patch) | |
tree | 0fcb0b4581f0d7982826b2a29b46dc471e10e899 /source/blender/blenlib/intern/mesh_boolean.cc | |
parent | 92743cc895dd34bdaa165679bf9d06dfa3f8cb24 (diff) |
Fix T85948 Exact boolean crash with some nonplanar ngons.
Triangulating ngons could fail with the method that was being
used: projecting along the dominant normal axis and then using CDT.
It could fail if the ngon has self crossings or might be so after
the described projection.
Switched to using projection along the normal itself, and also to
using polyfill which produces some kind of triangulation no matter
what in such circumstances. This will also likely be faster if
there are a lot of ngons in the meshes, since the exact arithmetic
CDT was being used before, and now float arithmetic is used.
Diffstat (limited to 'source/blender/blenlib/intern/mesh_boolean.cc')
-rw-r--r-- | source/blender/blenlib/intern/mesh_boolean.cc | 127 |
1 files changed, 41 insertions, 86 deletions
diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index 37205ecef41..fcf5c5bfad3 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -38,6 +38,7 @@ # include "BLI_math_mpq.hh" # include "BLI_mesh_intersect.hh" # include "BLI_mpq3.hh" +# include "BLI_polyfill_2d.h" # include "BLI_set.hh" # include "BLI_span.hh" # include "BLI_stack.hh" @@ -2590,47 +2591,8 @@ static IMesh raycast_boolean(const IMesh &tm, } /** - * Which CDT output edge index is for an edge between output verts - * v1 and v2 (in either order)? - * \return -1 if none. - */ -static int find_cdt_edge(const CDT_result<mpq_class> &cdt_out, int v1, int v2) -{ - for (int e : cdt_out.edge.index_range()) { - const std::pair<int, int> &edge = cdt_out.edge[e]; - if ((edge.first == v1 && edge.second == v2) || (edge.first == v2 && edge.second == v1)) { - return e; - } - } - return -1; -} - -/** - * Return the original edge id for the CDT output edge e_out, given that - * the only input to CDT was face f. Pick the first, if there are several. - */ -static int orig_edge_for_cdt_edge(const CDT_result<mpq_class> &cdt_out, - int cdt_e_out, - const Face *f) -{ - for (int cdt_e_orig : cdt_out.edge_orig[cdt_e_out]) { - if (cdt_e_orig != NO_INDEX) { - BLI_assert(cdt_e_orig >= cdt_out.face_edge_offset); - int a = cdt_e_orig / cdt_out.face_edge_offset; - int b = cdt_e_orig % cdt_out.face_edge_offset; - /* It is the bth position within cdt input face a - 1. There is only one face, f. */ - BLI_assert(a == 1); - UNUSED_VARS_NDEBUG(a); - BLI_assert(b < f->size()); - return f->edge_orig[b]; - } - } - return NO_INDEX; -} - -/** * Tessellate face f into triangles and return an array of `const Face *` - * giving that triangulation. + * giving that triangulation. Intended to be used when f has > 4 vertices. * Care is taken so that the original edge index associated with * each edge in the output triangles either matches the original edge * for the (identical) edge of f, or else is -1. So diagonals added @@ -2638,62 +2600,55 @@ static int orig_edge_for_cdt_edge(const CDT_result<mpq_class> &cdt_out, */ static Array<Face *> triangulate_poly(Face *f, IMeshArena *arena) { + /* Similar to loop body in BM_mesh_calc_tesselation. */ int flen = f->size(); - CDT_input<mpq_class> cdt_in; - cdt_in.vert = Array<mpq2>(flen); - cdt_in.face = Array<Vector<int>>(1); - cdt_in.face[0].reserve(flen); - for (int i : f->index_range()) { - cdt_in.face[0].append(i); - } - /* Project poly along dominant axis of normal to get 2d coords. */ + BLI_assert(flen > 4); if (!f->plane_populated()) { f->populate_plane(false); } + /* Project along negative face normal so (x,y) can be used in 2d. */ const double3 &poly_normal = f->plane->norm; - int axis = double3::dominant_axis(poly_normal); - /* If project down y axis as opposed to x or z, the orientation - * of the polygon will be reversed. - * Yet another reversal happens if the poly normal in the dominant - * direction is opposite that of the positive dominant axis. */ - bool rev1 = (axis == 1); - bool rev2 = poly_normal[axis] < 0; - bool rev = rev1 ^ rev2; - for (int i = 0; i < flen; ++i) { - int ii = rev ? flen - i - 1 : i; - mpq2 &p2d = cdt_in.vert[ii]; - int k = 0; - for (int j = 0; j < 3; ++j) { - if (j != axis) { - p2d[k++] = (*f)[ii]->co_exact[j]; - } - } - } - CDT_result<mpq_class> cdt_out = delaunay_2d_calc(cdt_in, CDT_INSIDE); - int n_tris = cdt_out.face.size(); - Array<Face *> ans(n_tris); - for (int t = 0; t < n_tris; ++t) { - int i_v_out[3]; - const Vert *v[3]; + float no[3] = {float(poly_normal[0]), float(poly_normal[1]), float(poly_normal[2])}; + normalize_v3(no); + float axis_mat[3][3]; + float(*projverts)[2]; + unsigned int(*tris)[3]; + const int totfilltri = flen - 2; + /* Prepare projected vertices and array to receive triangles in tesselation. */ + tris = static_cast<unsigned int(*)[3]>(MEM_malloc_arrayN(totfilltri, sizeof(*tris), __func__)); + projverts = static_cast<float(*)[2]>(MEM_malloc_arrayN(flen, sizeof(*projverts), __func__)); + axis_dominant_v3_to_m3_negate(axis_mat, no); + for (int j = 0; j < flen; ++j) { + const double3 &dco = (*f)[j]->co; + float co[3] = {float(dco[0]), float(dco[1]), float(dco[2])}; + mul_v2_m3v3(projverts[j], axis_mat, co); + } + BLI_polyfill_calc(projverts, flen, 1, tris); + /* Put tesselation triangles into Face form. Record original edges where they exist. */ + Array<Face *> ans(totfilltri); + for (int t = 0; t < totfilltri; ++t) { + unsigned int *tri = tris[t]; int eo[3]; - for (int i = 0; i < 3; ++i) { - i_v_out[i] = cdt_out.face[t][i]; - v[i] = (*f)[cdt_out.vert_orig[i_v_out[i]][0]]; - } - for (int i = 0; i < 3; ++i) { - int e_out = find_cdt_edge(cdt_out, i_v_out[i], i_v_out[(i + 1) % 3]); - BLI_assert(e_out != -1); - eo[i] = orig_edge_for_cdt_edge(cdt_out, e_out, f); - } - if (rev) { - ans[t] = arena->add_face( - {v[0], v[2], v[1]}, f->orig, {eo[2], eo[1], eo[0]}, {false, false, false}); - } - else { + const Vert *v[3]; + for (int k = 0; k < 3; k++) { + BLI_assert(tri[k] < flen); + v[k] = (*f)[tri[k]]; + /* If tri edge goes between two successive indices in + * the original face, then it is an original edge. */ + if ((tri[k] + 1) % flen == tri[(k + 1) % 3]) { + eo[k] = f->edge_orig[tri[k]]; + } + else { + eo[k] = NO_INDEX; + } ans[t] = arena->add_face( {v[0], v[1], v[2]}, f->orig, {eo[0], eo[1], eo[2]}, {false, false, false}); } } + + MEM_freeN(tris); + MEM_freeN(projverts); + return ans; } |