diff options
Diffstat (limited to 'source/blender/bmesh')
95 files changed, 8495 insertions, 3019 deletions
diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index a43e835f022..257768b0ac8 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -25,9 +25,9 @@ set(INC . - ../blenfont ../blenkernel ../blenlib + ../blentranslation ../makesdna ../../../intern/guardedalloc ../../../extern/rangetree @@ -43,6 +43,7 @@ set(SRC operators/bmo_bisect_plane.c operators/bmo_bridge.c operators/bmo_connect.c + operators/bmo_connect_concave.c operators/bmo_connect_nonplanar.c operators/bmo_connect_pair.c operators/bmo_create.c @@ -60,6 +61,8 @@ set(SRC operators/bmo_mesh_conv.c operators/bmo_mirror.c operators/bmo_normals.c + operators/bmo_offset_edgeloops.c + operators/bmo_planar_faces.c operators/bmo_poke.c operators/bmo_primitive.c operators/bmo_removedoubles.c @@ -74,6 +77,8 @@ set(SRC operators/bmo_utils.c operators/bmo_wireframe.c + intern/bmesh_callback_generic.c + intern/bmesh_callback_generic.h intern/bmesh_construct.c intern/bmesh_construct.h intern/bmesh_core.c @@ -140,6 +145,8 @@ set(SRC tools/bmesh_intersect.h tools/bmesh_path.c tools/bmesh_path.h + tools/bmesh_region_match.c + tools/bmesh_region_match.h tools/bmesh_triangulate.c tools/bmesh_triangulate.h tools/bmesh_wireframe.c diff --git a/source/blender/bmesh/SConscript b/source/blender/bmesh/SConscript index de839f7f6a6..c53974be1a7 100644 --- a/source/blender/bmesh/SConscript +++ b/source/blender/bmesh/SConscript @@ -35,8 +35,8 @@ sources += env.Glob('tools/*.c') incs = [ './', - '../blenfont', '../blenlib', + '../blentranslation', '../makesdna', '../blenkernel', '#/intern/guardedalloc', diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h index 8b5250b7c1e..78c814af86e 100644 --- a/source/blender/bmesh/bmesh.h +++ b/source/blender/bmesh/bmesh.h @@ -28,8 +28,7 @@ * * \addtogroup bmesh BMesh * - * \brief BMesh is a non-manifold boundary representation designed to replace the current, limited EditMesh structure, - * solving many of the design limitations and maintenance issues of EditMesh. + * \brief BMesh is a non-manifold boundary representation designed to support advanced editing operations. * * * \section bm_structure The Structure @@ -58,10 +57,14 @@ * * \subsection bm_loop The Loop * - * Loops define the boundary loop of a face. Each loop logically corresponds to an edge, - * which is defined by the loop and next loop's vertices. + * Loops can be thought of as a *face-corner*, since faces don't reference verts or edges directly. + * Each loop connects the face to one of its corner vertices, + * and also references an edge which connects this loop's vertex to the next loop's vertex. * - * Loops store several handy pointers: + * Loops allow faces to access their verts and edges, + * while edges and faces store their loops, allowing access in the opposite direction too. + * + * Loop pointers: * * - BMLoop#v - pointer to the vertex associated with this loop. * - BMLoop#e - pointer to the edge associated with this loop, @@ -78,11 +81,11 @@ * * \subsection bm_edges_and_verts Edges and Vertices * - * Edges and Vertices in BMesh are much like their counterparts in EditMesh, - * except for some members private to the BMesh api. + * Edges and Vertices in BMesh are primitive structures. * - * \note There can be more than one edge between two vertices in bmesh, - * though the rest of blender (e.g. DerivedMesh, CDDM, CCGSubSurf, etc) does not support this. + * \note There can be more than one edge between two vertices in BMesh, + * though the rest of Blender (e.g. DerivedMesh, CDDM, CCGSubSurf, etc) does not support this. + * So it should only occur temporarily during editing operations. * * * \subsection bm_queries Queries @@ -107,7 +110,8 @@ * \subsection bm_iter_api Iterator API * * Most topological queries in BMesh go through an iterator API (see Queries above). - * These are defined in bmesh_iterators.h. If you can, please use the #BM_ITER macro in bmesh_iterators.h + * These are defined in bmesh_iterators.h. + * If you can, please use the #BM_ITER_MESH, #BM_ITER_ELEM macros in bmesh_iterators.h * * * \subsection bm_walker_api Walker API @@ -161,7 +165,7 @@ * - integer - #BMO_OP_SLOT_INT * - boolean - #BMO_OP_SLOT_BOOL * - float - #BMO_OP_SLOT_FLT - * - pointer - #BMO_OP_SLOT_PNT + * - pointer - #BMO_OP_SLOT_PTR * - matrix - #BMO_OP_SLOT_MAT * - vector - #BMO_OP_SLOT_VEC * - buffer - #BMO_OP_SLOT_ELEMENT_BUF - a list of verts/edges/faces. @@ -199,18 +203,6 @@ * * There may be a better place for this section, but adding here for now. * - * \subsection bm_todo_tools Tools - * - * Probably most of these will be bmesh operators. - * - * - make ngons flat. - * - solidify (precise mode), keeps even wall thickness, re-creates outlines of offset faces with plane-plane - * intersections. - * - split vert (we already have in our API, just no tool). - * - flip selected region (invert all faces about the plane defined by the selected region outline) - * - interactive dissolve (like the knife tool but draw over edges to dissolve) - * - * * \subsection bm_todo_optimize Optimizations * * - skip normal calc when its not needed (when calling chain of operators & for modifiers, flag as dirty) @@ -218,11 +210,6 @@ * - ability to call BMO's with option not to create return data (will save some time) * - binary diff UNDO, currently this uses huge amount of ram when all shapes are stored for each undo step for eg. * - use two different iterator types for BMO map/buffer types. - * - * - * \subsection bm_todo_tools_enhance Tool Enhancements - * - * - vert slide UV correction (like we have for edge slide) */ #ifdef __cplusplus @@ -243,6 +230,7 @@ extern "C" { #include "intern/bmesh_error.h" #include "intern/bmesh_core.h" +#include "intern/bmesh_callback_generic.h" #include "intern/bmesh_construct.h" #include "intern/bmesh_delete.h" #include "intern/bmesh_edgeloop.h" diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h index 39359b97a4e..57107ed4e37 100644 --- a/source/blender/bmesh/bmesh_class.h +++ b/source/blender/bmesh/bmesh_class.h @@ -256,27 +256,54 @@ enum { #define BM_ALL (BM_VERT | BM_EDGE | BM_LOOP | BM_FACE) #define BM_ALL_NOLOOP (BM_VERT | BM_EDGE | BM_FACE) +/* args for _Generic */ +#define _BM_GENERIC_TYPE_ELEM_NONCONST \ + void *, BMVert *, BMEdge *, BMLoop *, BMFace *, \ + BMElem *, BMElemF *, BMHeader * + +#define _BM_GENERIC_TYPE_ELEM_CONST \ + const void *, const BMVert *, const BMEdge *, const BMLoop *, const BMFace *, \ + const BMElem *, const BMElemF *, const BMHeader *, \ + void * const, BMVert * const, BMEdge * const, BMLoop * const, BMFace * const, \ + BMElem * const, BMElemF * const, BMHeader * const + +#define BM_CHECK_TYPE_ELEM_CONST(ele) \ + CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPES_CONST) + +#define BM_CHECK_TYPE_ELEM_NONCONST(ele) \ + CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_ELEM_NONCONST) + +#define BM_CHECK_TYPE_ELEM(ele) \ + CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_ELEM_NONCONST, _BM_GENERIC_TYPE_ELEM_CONST) + +#define BM_CHECK_TYPE_ELEM_ASSIGN(ele) \ + (BM_CHECK_TYPE_ELEM(ele)), ele + /* BMHeader->hflag (char) */ enum { BM_ELEM_SELECT = (1 << 0), BM_ELEM_HIDDEN = (1 << 1), BM_ELEM_SEAM = (1 << 2), - BM_ELEM_SMOOTH = (1 << 3), /* used for faces and edges, note from the user POV, - * this is a sharp edge when disabled */ - - BM_ELEM_TAG = (1 << 4), /* internal flag, used for ensuring correct normals - * during multires interpolation, and any other time - * when temp tagging is handy. - * always assume dirty & clear before use. */ + /** + * used for faces and edges, note from the user POV, + * this is a sharp edge when disabled */ + BM_ELEM_SMOOTH = (1 << 3), + /** + * internal flag, used for ensuring correct normals + * during multires interpolation, and any other time + * when temp tagging is handy. + * always assume dirty & clear before use. */ + BM_ELEM_TAG = (1 << 4), BM_ELEM_DRAW = (1 << 5), /* edge display */ /* spare tag, assumed dirty, use define in each function to name based on use */ // _BM_ELEM_TAG_ALT = (1 << 6), // UNUSED - - BM_ELEM_INTERNAL_TAG = (1 << 7) /* for low level internal API tagging, - * since tools may want to tag verts and - * not have functions clobber them */ + /** + * for low level internal API tagging, + * since tools may want to tag verts and + * not have functions clobber them */ + BM_ELEM_INTERNAL_TAG = (1 << 7), }; struct BPy_BMGeneric; @@ -291,8 +318,17 @@ typedef bool (*BMElemFilterFunc)(BMElem *, void *user_data); #define BM_ELEM_CD_GET_INT(ele, offset) \ (assert(offset != -1), *((int *)((char *)(ele)->head.data + (offset)))) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#define BM_ELEM_CD_GET_VOID_P(ele, offset) \ + (assert(offset != -1), \ + _Generic(ele, \ + GENERIC_TYPE_ANY( POINTER_OFFSET((ele)->head.data, offset), _BM_GENERIC_TYPE_ELEM_NONCONST), \ + GENERIC_TYPE_ANY((const void *)POINTER_OFFSET((ele)->head.data, offset), _BM_GENERIC_TYPE_ELEM_CONST)) \ + ) +#else #define BM_ELEM_CD_GET_VOID_P(ele, offset) \ (assert(offset != -1), (void *)((char *)(ele)->head.data + (offset))) +#endif #define BM_ELEM_CD_SET_FLOAT(ele, offset, f) { CHECK_TYPE_NONCONST(ele); \ assert(offset != -1); *((float *)((char *)(ele)->head.data + (offset))) = (f); } (void)0 diff --git a/source/blender/bmesh/bmesh_tools.h b/source/blender/bmesh/bmesh_tools.h index baffeb774b6..f7f767f91bf 100644 --- a/source/blender/bmesh/bmesh_tools.h +++ b/source/blender/bmesh/bmesh_tools.h @@ -41,6 +41,7 @@ extern "C" { #include "tools/bmesh_edgenet.h" #include "tools/bmesh_edgesplit.h" #include "tools/bmesh_path.h" +#include "tools/bmesh_region_match.h" #include "tools/bmesh_triangulate.h" #ifdef __cplusplus diff --git a/source/blender/bmesh/intern/bmesh_callback_generic.c b/source/blender/bmesh/intern/bmesh_callback_generic.c new file mode 100644 index 00000000000..913255bfb33 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_callback_generic.c @@ -0,0 +1,60 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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 2 + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_callback_generic.c + * \ingroup bmesh + * + * BM element callback functions. + */ + +#include "BLI_utildefines.h" + +#include "bmesh.h" + +#include "intern/bmesh_callback_generic.h" + +bool BM_elem_cb_check_hflag_ex(BMElem *ele, void *user_data) +{ + const unsigned int hflag_pair = GET_INT_FROM_POINTER(user_data); + const char hflag_p = (hflag_pair & 0xff); + const char hflag_n = (hflag_pair >> 8); + + return ((BM_elem_flag_test(ele, hflag_p) != 0) && + (BM_elem_flag_test(ele, hflag_n) == 0)); +} + +bool BM_elem_cb_check_hflag_enabled(BMElem *ele, void *user_data) +{ + const char hflag = GET_INT_FROM_POINTER(user_data); + + return (BM_elem_flag_test(ele, hflag) != 0); +} + +bool BM_elem_cb_check_hflag_disabled(BMElem *ele, void *user_data) +{ + const char hflag = GET_INT_FROM_POINTER(user_data); + + return (BM_elem_flag_test(ele, hflag) == 0); +} + +bool BM_elem_cb_check_elem_not_equal(BMElem *ele, void *user_data) +{ + return (ele != user_data); +} diff --git a/source/blender/bmesh/intern/bmesh_callback_generic.h b/source/blender/bmesh/intern/bmesh_callback_generic.h new file mode 100644 index 00000000000..3cae01d417f --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_callback_generic.h @@ -0,0 +1,45 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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 2 + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_CALLBACK_GENERIC_H__ +#define __BMESH_CALLBACK_GENERIC_H__ + +/** \file blender/bmesh/intern/bmesh_callback_generic.h + * \ingroup bmesh + */ + +bool BM_elem_cb_check_hflag_enabled(BMElem *, void *user_data); +bool BM_elem_cb_check_hflag_disabled(BMElem *, void *user_data); +bool BM_elem_cb_check_hflag_ex(BMElem *, void *user_data); +bool BM_elem_cb_check_elem_not_equal(BMElem *ele, void *user_data); + +#define BM_elem_cb_check_hflag_ex_simple(type, hflag_p, hflag_n) \ + (bool (*)(type, void *))BM_elem_cb_check_hflag_ex, \ + SET_UINT_IN_POINTER(((hflag_p) | (hflag_n << 8))) + +#define BM_elem_cb_check_hflag_enabled_simple(type, hflag_p) \ + (bool (*)(type, void *))BM_elem_cb_check_hflag_enabled, \ + SET_UINT_IN_POINTER((hflag_p)) + +#define BM_elem_cb_check_hflag_disabled_simple(type, hflag_n) \ + (bool (*)(type, void *))BM_elem_cb_check_hflag_disabled, \ + SET_UINT_IN_POINTER(hflag_n) + +#endif /* __BMESH_CALLBACK_GENERIC_H__ */ diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index e0348fea636..7664108f348 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -46,9 +46,41 @@ #define SELECT 1 +/** + * Fill in an edge array from a vertex array (connected polygon loop). + * + * \returns false if any edges aren't found . + */ +bool BM_edges_from_verts(BMEdge **edge_arr, BMVert **vert_arr, const int len) +{ + int i, i_prev = len - 1; + for (i = 0; i < len; i++) { + edge_arr[i_prev] = BM_edge_exists(vert_arr[i_prev], vert_arr[i]); + if (edge_arr[i_prev] == NULL) { + return false; + } + i_prev = i; + } + return true; +} + +/** + * Fill in an edge array from a vertex array (connected polygon loop). + * Creating edges as-needed. + */ +void BM_edges_from_verts_ensure(BMesh *bm, BMEdge **edge_arr, BMVert **vert_arr, const int len) +{ + int i, i_prev = len - 1; + for (i = 0; i < len; i++) { + edge_arr[i_prev] = BM_edge_create(bm, vert_arr[i_prev], vert_arr[i], NULL, BM_CREATE_NO_DOUBLE); + i_prev = i; + } +} + /* prototypes */ -static void bm_loop_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, - const BMLoop *source_loop, BMLoop *target_loop); +static void bm_loop_attrs_copy( + BMesh *source_mesh, BMesh *target_mesh, + const BMLoop *source_loop, BMLoop *target_loop); /** * \brief Make Quad/Triangle @@ -64,9 +96,10 @@ static void bm_loop_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, * of the vertices in the vertex array. */ -BMFace *BM_face_create_quad_tri(BMesh *bm, - BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4, - const BMFace *f_example, const eBMCreateFlag create_flag) +BMFace *BM_face_create_quad_tri( + BMesh *bm, + BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4, + const BMFace *f_example, const eBMCreateFlag create_flag) { BMVert *vtar[4] = {v1, v2, v3, v4}; return BM_face_create_verts(bm, vtar, v4 ? 4 : 3, f_example, create_flag, true); @@ -81,8 +114,9 @@ BMFace *BM_face_create_quad_tri(BMesh *bm, * this is done since the face may not be completely surrounded by faces, * this way: a quad with 2 connected quads on either side will still get all 4 loops updated */ -void BM_face_copy_shared(BMesh *bm, BMFace *f, - BMElemFilterFunc filter_fn, void *user_data) +void BM_face_copy_shared( + BMesh *bm, BMFace *f, + BMElemFilterFunc filter_fn, void *user_data) { BMLoop *l_first; BMLoop *l_iter; @@ -132,155 +166,113 @@ void BM_face_copy_shared(BMesh *bm, BMFace *f, } /** - * \brief Make NGon + * Given an array of edges, + * order them using the winding defined by \a v1 & \a v2 + * into \a edges_sort & \a verts_sort. * - * Makes an ngon from an unordered list of edges. \a v1 and \a v2 - * must be the verts defining edges[0], - * and define the winding of the new face. - * - * \a edges are not required to be ordered, simply to to form - * a single closed loop as a whole. - * - * \note While this function will work fine when the edges - * are already sorted, if the edges are always going to be sorted, - * #BM_face_create should be considered over this function as it - * avoids some unnecessary work. + * All arrays must be \a len long. */ -BMFace *BM_face_create_ngon(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, const int len, - const BMFace *f_example, const eBMCreateFlag create_flag) +static bool bm_edges_sort_winding( + BMVert *v1, BMVert *v2, + BMEdge **edges, const int len, + BMEdge **edges_sort, BMVert **verts_sort) { - BMEdge **edges_sort = BLI_array_alloca(edges_sort, len); - BMVert **verts_sort = BLI_array_alloca(verts_sort, len + 1); - int esort_index = 0; - int vsort_index = 0; - - BMFace *f = NULL; - BMEdge *e; - BMVert *v, *ev1, *ev2; + BMEdge *e_iter, *e_first; + BMVert *v_iter; int i; - bool is_v1_found, is_reverse; - - /* this code is hideous, yeek. I'll have to think about ways of - * cleaning it up. basically, it now combines the old BM_face_create_ngon - * _and_ the old bmesh_mf functions, so its kindof smashed together - * - joeedh */ - - BLI_assert(len && v1 && v2 && edges && bm); - - /* put edges in correct order */ + /* all flags _must_ be cleared on exit! */ for (i = 0; i < len; i++) { BM_ELEM_API_FLAG_ENABLE(edges[i], _FLAG_MF); + BM_ELEM_API_FLAG_ENABLE(edges[i]->v1, _FLAG_MV); + BM_ELEM_API_FLAG_ENABLE(edges[i]->v2, _FLAG_MV); } - ev1 = edges[0]->v1; - ev2 = edges[0]->v2; - - BLI_assert(ELEM(v1, ev1, ev2) && ELEM(v2, ev1, ev2)); - - if (v1 == ev2) { - /* Swapping here improves performance and consistency of face - * structure in the special case that the edges are already in - * the correct order and winding */ - SWAP(BMVert *, ev1, ev2); - } - - verts_sort[vsort_index++] = ev1; - v = ev2; - e = edges[0]; + /* find first edge */ + i = 0; + v_iter = v1; + e_iter = e_first = v1->e; do { - BMEdge *e2 = e; - - /* vertex array is (len + 1) */ - if (UNLIKELY(vsort_index > len)) { - goto err; /* vertex in loop twice */ + if (BM_ELEM_API_FLAG_TEST(e_iter, _FLAG_MF) && + (BM_edge_other_vert(e_iter, v_iter) == v2)) + { + i = 1; + break; } + } while ((e_iter = bmesh_disk_edge_next(e_iter, v_iter)) != e_first); + if (i == 0) { + goto error; + } - verts_sort[vsort_index++] = v; - edges_sort[esort_index++] = e; - - /* we only flag the verts to check if they are in the face more than once */ - BM_ELEM_API_FLAG_ENABLE(v, _FLAG_MV); - - do { - e2 = bmesh_disk_edge_next(e2, v); - if (e2 != e && BM_ELEM_API_FLAG_TEST(e2, _FLAG_MF)) { - v = BM_edge_other_vert(e2, v); - break; + i = 0; + do { + /* entering loop will always succeed */ + if (BM_ELEM_API_FLAG_TEST(e_iter, _FLAG_MF)) { + if (UNLIKELY(BM_ELEM_API_FLAG_TEST(v_iter, _FLAG_MV) == false)) { + /* vert is in loop multiple times */ + goto error; } - } while (e2 != e); - if (UNLIKELY(e2 == e)) { - goto err; /* the edges do not form a closed loop */ - } + BM_ELEM_API_FLAG_DISABLE(e_iter, _FLAG_MF); + edges_sort[i] = e_iter; - e = e2; - } while (e != edges[0]); + BM_ELEM_API_FLAG_DISABLE(v_iter, _FLAG_MV); + verts_sort[i] = v_iter; - if (UNLIKELY(esort_index != len)) { - goto err; /* we didn't use all edges in forming the boundary loop */ - } + i += 1; - /* ok, edges are in correct order, now ensure they are going - * in the correct direction */ - is_v1_found = is_reverse = false; - for (i = 0; i < len; i++) { - if (BM_vert_in_edge(edges_sort[i], v1)) { - /* see if v1 and v2 are in the same edge */ - if (BM_vert_in_edge(edges_sort[i], v2)) { - /* if v1 is shared by the *next* edge, then the winding - * is incorrect */ - if (BM_vert_in_edge(edges_sort[(i + 1) % len], v1)) { - is_reverse = true; - break; + /* walk onto the next vertex */ + v_iter = BM_edge_other_vert(e_iter, v_iter); + if (i == len) { + if (UNLIKELY(v_iter != verts_sort[0])) { + goto error; } + break; } - is_v1_found = true; - } - - if ((is_v1_found == false) && BM_vert_in_edge(edges_sort[i], v2)) { - is_reverse = true; - break; + e_first = e_iter; } - } + } while ((e_iter = bmesh_disk_edge_next(e_iter, v_iter)) != e_first); - if (is_reverse) { - for (i = 0; i < len / 2; i++) { - v = verts_sort[i]; - verts_sort[i] = verts_sort[len - i - 1]; - verts_sort[len - i - 1] = v; - } + if (i == len) { + return true; } +error: for (i = 0; i < len; i++) { - edges_sort[i] = BM_edge_exists(verts_sort[i], verts_sort[(i + 1) % len]); - if (UNLIKELY(edges_sort[i] == NULL)) { - goto err; - } - - /* check if vert is in face more than once. if the flag is disabled. we've already visited */ - if (UNLIKELY(!BM_ELEM_API_FLAG_TEST(verts_sort[i], _FLAG_MV))) { - goto err; - } - BM_ELEM_API_FLAG_DISABLE(verts_sort[i], _FLAG_MV); + BM_ELEM_API_FLAG_DISABLE(edges[i], _FLAG_MF); + BM_ELEM_API_FLAG_DISABLE(edges[i]->v1, _FLAG_MV); + BM_ELEM_API_FLAG_DISABLE(edges[i]->v2, _FLAG_MV); } - f = BM_face_create(bm, verts_sort, edges_sort, len, f_example, create_flag); + return false; +} - /* clean up flags */ - for (i = 0; i < len; i++) { - BM_ELEM_API_FLAG_DISABLE(edges_sort[i], _FLAG_MF); - } +/** + * \brief Make NGon + * + * Makes an ngon from an unordered list of edges. + * Verts \a v1 and \a v2 define the winding of the new face. + * + * \a edges are not required to be ordered, simply to to form + * a single closed loop as a whole. + * + * \note While this function will work fine when the edges + * are already sorted, if the edges are always going to be sorted, + * #BM_face_create should be considered over this function as it + * avoids some unnecessary work. + */ +BMFace *BM_face_create_ngon( + BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, const int len, + const BMFace *f_example, const eBMCreateFlag create_flag) +{ + BMEdge **edges_sort = BLI_array_alloca(edges_sort, len); + BMVert **verts_sort = BLI_array_alloca(verts_sort, len); - return f; + BLI_assert(len && v1 && v2 && edges && bm); -err: - for (i = 0; i < len; i++) { - BM_ELEM_API_FLAG_DISABLE(edges[i], _FLAG_MF); - } - for (i = 0; i < vsort_index; i++) { - BM_ELEM_API_FLAG_DISABLE(verts_sort[i], _FLAG_MV); + if (bm_edges_sort_winding(v1, v2, edges, len, edges_sort, verts_sort)) { + return BM_face_create(bm, verts_sort, edges_sort, len, f_example, create_flag); } return NULL; @@ -294,9 +286,10 @@ err: * - Optionally create edges between vertices. * - Uses verts so no need to find edges (handy when you only have verts) */ -BMFace *BM_face_create_ngon_verts(BMesh *bm, BMVert **vert_arr, const int len, - const BMFace *f_example, const eBMCreateFlag create_flag, - const bool calc_winding, const bool create_edges) +BMFace *BM_face_create_ngon_verts( + BMesh *bm, BMVert **vert_arr, const int len, + const BMFace *f_example, const eBMCreateFlag create_flag, + const bool calc_winding, const bool create_edges) { BMEdge **edge_arr = BLI_array_alloca(edge_arr, len); unsigned int winding[2] = {0, 0}; @@ -375,8 +368,9 @@ BMFace *BM_face_create_ngon_verts(BMesh *bm, BMVert **vert_arr, const int len, * * \note Since this is a vcloud there is no direction. */ -BMFace *BM_face_create_ngon_vcloud(BMesh *bm, BMVert **vert_arr, int len, - const BMFace *f_example, const eBMCreateFlag create_flag) +BMFace *BM_face_create_ngon_vcloud( + BMesh *bm, BMVert **vert_arr, int len, + const BMFace *f_example, const eBMCreateFlag create_flag) { struct SortIntByFloat *vang = BLI_array_alloca(vang, len); BMVert **vert_arr_map = BLI_array_alloca(vert_arr_map, len); @@ -497,8 +491,9 @@ BMFace *BM_face_create_ngon_vcloud(BMesh *bm, BMVert **vert_arr, int len, /*************************************************************/ -static void bm_vert_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, - const BMVert *source_vertex, BMVert *target_vertex) +static void bm_vert_attrs_copy( + BMesh *source_mesh, BMesh *target_mesh, + const BMVert *source_vertex, BMVert *target_vertex) { if ((source_mesh == target_mesh) && (source_vertex == target_vertex)) { BLI_assert(!"BMVert: source and targer match"); @@ -510,8 +505,9 @@ static void bm_vert_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, source_vertex->head.data, &target_vertex->head.data); } -static void bm_edge_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, - const BMEdge *source_edge, BMEdge *target_edge) +static void bm_edge_attrs_copy( + BMesh *source_mesh, BMesh *target_mesh, + const BMEdge *source_edge, BMEdge *target_edge) { if ((source_mesh == target_mesh) && (source_edge == target_edge)) { BLI_assert(!"BMEdge: source and targer match"); @@ -522,8 +518,9 @@ static void bm_edge_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, source_edge->head.data, &target_edge->head.data); } -static void bm_loop_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, - const BMLoop *source_loop, BMLoop *target_loop) +static void bm_loop_attrs_copy( + BMesh *source_mesh, BMesh *target_mesh, + const BMLoop *source_loop, BMLoop *target_loop) { if ((source_mesh == target_mesh) && (source_loop == target_loop)) { BLI_assert(!"BMLoop: source and targer match"); @@ -534,8 +531,9 @@ static void bm_loop_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, source_loop->head.data, &target_loop->head.data); } -static void bm_face_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, - const BMFace *source_face, BMFace *target_face) +static void bm_face_attrs_copy( + BMesh *source_mesh, BMesh *target_mesh, + const BMFace *source_face, BMFace *target_face) { if ((source_mesh == target_mesh) && (source_face == target_face)) { BLI_assert(!"BMFace: source and targer match"); @@ -555,8 +553,9 @@ static void bm_face_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, * Copies attributes, e.g. customdata, header flags, etc, from one element * to another of the same type. */ -void BM_elem_attrs_copy_ex(BMesh *bm_src, BMesh *bm_dst, const void *ele_src_v, void *ele_dst_v, - const char hflag_mask) +void BM_elem_attrs_copy_ex( + BMesh *bm_src, BMesh *bm_dst, const void *ele_src_v, void *ele_dst_v, + const char hflag_mask) { const BMHeader *ele_src = ele_src_v; BMHeader *ele_dst = ele_dst_v; @@ -621,9 +620,10 @@ void BM_elem_select_copy(BMesh *bm_dst, BMesh *UNUSED(bm_src), void *ele_dst_v, } /* helper function for 'BM_mesh_copy' */ -static BMFace *bm_mesh_copy_new_face(BMesh *bm_new, BMesh *bm_old, - BMVert **vtable, BMEdge **etable, - BMFace *f) +static BMFace *bm_mesh_copy_new_face( + BMesh *bm_new, BMesh *bm_old, + BMVert **vtable, BMEdge **etable, + BMFace *f) { BMLoop **loops = BLI_array_alloca(loops, f->len); BMVert **verts = BLI_array_alloca(verts, f->len); diff --git a/source/blender/bmesh/intern/bmesh_construct.h b/source/blender/bmesh/intern/bmesh_construct.h index 12d3a4bd474..29503679547 100644 --- a/source/blender/bmesh/intern/bmesh_construct.h +++ b/source/blender/bmesh/intern/bmesh_construct.h @@ -29,23 +29,32 @@ struct BMAllocTemplate; -BMFace *BM_face_create_quad_tri(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4, - const BMFace *f_example, const eBMCreateFlag create_flag); +bool BM_edges_from_verts(BMEdge **edge_arr, BMVert **vert_arr, const int len); +void BM_edges_from_verts_ensure(BMesh *bm, BMEdge **edge_arr, BMVert **vert_arr, const int len); -void BM_face_copy_shared(BMesh *bm, BMFace *f, - BMElemFilterFunc filter_fn, void *user_data); +BMFace *BM_face_create_quad_tri( + BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4, + const BMFace *f_example, const eBMCreateFlag create_flag); -BMFace *BM_face_create_ngon(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, const int len, - const BMFace *f_example, const eBMCreateFlag create_flag); -BMFace *BM_face_create_ngon_verts(BMesh *bm, BMVert **vert_arr, const int len, - const BMFace *f_example, const eBMCreateFlag create_flag, - const bool calc_winding, const bool create_edges); +void BM_face_copy_shared( + BMesh *bm, BMFace *f, + BMElemFilterFunc filter_fn, void *user_data); -BMFace *BM_face_create_ngon_vcloud(BMesh *bm, BMVert **vert_arr, int len, - const BMFace *f_example, const eBMCreateFlag create_flag); +BMFace *BM_face_create_ngon( + BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, const int len, + const BMFace *f_example, const eBMCreateFlag create_flag); +BMFace *BM_face_create_ngon_verts( + BMesh *bm, BMVert **vert_arr, const int len, + const BMFace *f_example, const eBMCreateFlag create_flag, + const bool calc_winding, const bool create_edges); -void BM_elem_attrs_copy_ex(BMesh *bm_src, BMesh *bm_dst, const void *ele_src_v, void *ele_dst_v, - const char hflag_mask); +BMFace *BM_face_create_ngon_vcloud( + BMesh *bm, BMVert **vert_arr, int len, + const BMFace *f_example, const eBMCreateFlag create_flag); + +void BM_elem_attrs_copy_ex( + BMesh *bm_src, BMesh *bm_dst, const void *ele_src_v, void *ele_dst_v, + const char hflag_mask); void BM_elem_attrs_copy(BMesh *bm_src, BMesh *bm_dst, const void *ele_src_v, void *ele_dst_v); void BM_elem_select_copy(BMesh *bm_dst, BMesh *bm_src, void *ele_dst_v, const void *ele_src_v); diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index eb7b9f78ef4..e67aa1da340 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -31,10 +31,10 @@ #include "BLI_math_vector.h" #include "BLI_array.h" #include "BLI_alloca.h" -#include "BLI_smallhash.h" +#include "BLI_linklist_stack.h" #include "BLI_stackdefines.h" -#include "BLF_translation.h" +#include "BLT_translation.h" #include "BKE_DerivedMesh.h" @@ -57,8 +57,9 @@ /** * \brief Main function for creating a new vertex. */ -BMVert *BM_vert_create(BMesh *bm, const float co[3], - const BMVert *v_example, const eBMCreateFlag create_flag) +BMVert *BM_vert_create( + BMesh *bm, const float co[3], + const BMVert *v_example, const eBMCreateFlag create_flag) { BMVert *v = BLI_mempool_alloc(bm->vpool); @@ -88,7 +89,7 @@ BMVert *BM_vert_create(BMesh *bm, const float co[3], else { zero_v3(v->co); } - zero_v3(v->no); + /* 'v->no' set below */ v->e = NULL; /* --- done --- */ @@ -107,6 +108,7 @@ BMVert *BM_vert_create(BMesh *bm, const float co[3], if (v_example) { int *keyi; + /* handles 'v->no' too */ BM_elem_attrs_copy(bm, bm, v_example, v); /* exception: don't copy the original shapekey index */ @@ -117,6 +119,15 @@ BMVert *BM_vert_create(BMesh *bm, const float co[3], } else { CustomData_bmesh_set_default(&bm->vdata, &v->head.data); + zero_v3(v->no); + } + } + else { + if (v_example) { + copy_v3_v3(v->no, v_example->no); + } + else { + zero_v3(v->no); } } @@ -131,8 +142,9 @@ BMVert *BM_vert_create(BMesh *bm, const float co[3], * \note Duplicate edges are supported by the API however users should _never_ see them. * so unless you need a unique edge or know the edge won't exist, you should call with \a no_double = true */ -BMEdge *BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, - const BMEdge *e_example, const eBMCreateFlag create_flag) +BMEdge *BM_edge_create( + BMesh *bm, BMVert *v1, BMVert *v2, + const BMEdge *e_example, const eBMCreateFlag create_flag) { BMEdge *e; @@ -194,8 +206,9 @@ BMEdge *BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, return e; } -static BMLoop *bm_loop_create(BMesh *bm, BMVert *v, BMEdge *e, BMFace *f, - const BMLoop *l_example, const eBMCreateFlag create_flag) +static BMLoop *bm_loop_create( + BMesh *bm, BMVert *v, BMEdge *e, BMFace *f, + const BMLoop *l_example, const eBMCreateFlag create_flag) { BMLoop *l = NULL; @@ -213,8 +226,8 @@ static BMLoop *bm_loop_create(BMesh *bm, BMVert *v, BMEdge *e, BMFace *f, BM_elem_index_set(l, -1); /* set_ok_invalid */ #endif - l->head.hflag = 0; l->head.htype = BM_LOOP; + l->head.hflag = 0; l->head.api_flag = 0; l->v = v; @@ -244,8 +257,9 @@ static BMLoop *bm_loop_create(BMesh *bm, BMVert *v, BMEdge *e, BMFace *f, return l; } -static BMLoop *bm_face_boundary_add(BMesh *bm, BMFace *f, BMVert *startv, BMEdge *starte, - const eBMCreateFlag create_flag) +static BMLoop *bm_face_boundary_add( + BMesh *bm, BMFace *f, BMVert *startv, BMEdge *starte, + const eBMCreateFlag create_flag) { #ifdef USE_BMESH_HOLES BMLoopList *lst = BLI_mempool_calloc(bm->looplistpool); @@ -266,8 +280,9 @@ static BMLoop *bm_face_boundary_add(BMesh *bm, BMFace *f, BMVert *startv, BMEdge return l; } -BMFace *BM_face_copy(BMesh *bm_dst, BMesh *bm_src, BMFace *f, - const bool copy_verts, const bool copy_edges) +BMFace *BM_face_copy( + BMesh *bm_dst, BMesh *bm_src, BMFace *f, + const bool copy_verts, const bool copy_edges) { BMVert **verts = BLI_array_alloca(verts, f->len); BMEdge **edges = BLI_array_alloca(edges, f->len); @@ -362,7 +377,8 @@ BLI_INLINE BMFace *bm_face_create__internal(BMesh *bm) f->l_first = NULL; #endif f->len = 0; - zero_v3(f->no); + /* caller must initialize */ + // zero_v3(f->no); f->mat_nr = 0; /* --- done --- */ @@ -389,8 +405,9 @@ BLI_INLINE BMFace *bm_face_create__internal(BMesh *bm) * \param len Length of the face * \param create_flag Options for creating the face */ -BMFace *BM_face_create(BMesh *bm, BMVert **verts, BMEdge **edges, const int len, - const BMFace *f_example, const eBMCreateFlag create_flag) +BMFace *BM_face_create( + BMesh *bm, BMVert **verts, BMEdge **edges, const int len, + const BMFace *f_example, const eBMCreateFlag create_flag) { BMFace *f = NULL; BMLoop *l, *startl, *lastl; @@ -443,6 +460,15 @@ BMFace *BM_face_create(BMesh *bm, BMVert **verts, BMEdge **edges, const int len, } else { CustomData_bmesh_set_default(&bm->pdata, &f->head.data); + zero_v3(f->no); + } + } + else { + if (f_example) { + copy_v3_v3(f->no, f_example->no); + } + else { + zero_v3(f->no); } } @@ -454,25 +480,18 @@ BMFace *BM_face_create(BMesh *bm, BMVert **verts, BMEdge **edges, const int len, /** * Wrapper for #BM_face_create when you don't have an edge array */ -BMFace *BM_face_create_verts(BMesh *bm, BMVert **vert_arr, const int len, - const BMFace *f_example, const eBMCreateFlag create_flag, const bool create_edges) +BMFace *BM_face_create_verts( + BMesh *bm, BMVert **vert_arr, const int len, + const BMFace *f_example, const eBMCreateFlag create_flag, const bool create_edges) { BMEdge **edge_arr = BLI_array_alloca(edge_arr, len); - int i, i_prev = len - 1; if (create_edges) { - for (i = 0; i < len; i++) { - edge_arr[i_prev] = BM_edge_create(bm, vert_arr[i_prev], vert_arr[i], NULL, BM_CREATE_NO_DOUBLE); - i_prev = i; - } + BM_edges_from_verts_ensure(bm, edge_arr, vert_arr, len); } else { - for (i = 0; i < len; i++) { - edge_arr[i_prev] = BM_edge_exists(vert_arr[i_prev], vert_arr[i]); - if (edge_arr[i_prev] == NULL) { - return NULL; - } - i_prev = i; + if (BM_edges_from_verts(edge_arr, vert_arr, len) == false) { + return NULL; } } @@ -493,17 +512,17 @@ int bmesh_elem_check(void *element, const char htype) int err = 0; if (!element) - return 1; + return (1 << 0); if (head->htype != htype) - return 2; + return (1 << 1); switch (htype) { case BM_VERT: { BMVert *v = element; if (v->e && v->e->head.htype != BM_EDGE) { - err |= 4; + err |= (1 << 2); } break; } @@ -511,20 +530,20 @@ int bmesh_elem_check(void *element, const char htype) { BMEdge *e = element; if (e->l && e->l->head.htype != BM_LOOP) - err |= 8; + err |= (1 << 3); if (e->l && e->l->f->head.htype != BM_FACE) - err |= 16; + err |= (1 << 4); if (e->v1_disk_link.prev == NULL || e->v2_disk_link.prev == NULL || e->v1_disk_link.next == NULL || e->v2_disk_link.next == NULL) { - err |= 32; + err |= (1 << 5); } if (e->l && (e->l->radial_next == NULL || e->l->radial_prev == NULL)) - err |= 64; + err |= (1 << 6); if (e->l && e->l->f->len <= 0) - err |= 128; + err |= (1 << 7); break; } case BM_LOOP: @@ -533,14 +552,14 @@ int bmesh_elem_check(void *element, const char htype) int i; if (l->f->head.htype != BM_FACE) - err |= 256; + err |= (1 << 8); if (l->e->head.htype != BM_EDGE) - err |= 512; + err |= (1 << 9); if (l->v->head.htype != BM_VERT) - err |= 1024; + err |= (1 << 10); if (!BM_vert_in_edge(l->e, l->v)) { fprintf(stderr, "%s: fatal bmesh error (vert not in edge)! (bmesh internal error)\n", __func__); - err |= 2048; + err |= (1 << 11); } if (l->radial_next == NULL || l->radial_prev == NULL) @@ -1026,8 +1045,9 @@ static bool disk_is_flagged(BMVert *v, const char api_flag) return false; } - if (bmesh_radial_length(l) == 1) + if (BM_edge_is_boundary(l->e)) { return false; + } do { if (!BM_ELEM_API_FLAG_TEST(l->f, api_flag)) @@ -1111,7 +1131,7 @@ BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del) if (!d1 && !d2 && !BM_ELEM_API_FLAG_TEST(l_iter->e, _FLAG_JF)) { /* don't remove an edge it makes up the side of another face * else this will remove the face as well - campbell */ - if (BM_edge_face_count(l_iter->e) <= 2) { + if (!BM_edge_face_count_is_over(l_iter->e, 3)) { if (do_del) { BLI_array_append(deledges, l_iter->e); } @@ -1307,14 +1327,14 @@ static BMFace *bm_face_create__sfme(BMesh *bm, BMFace *f_example) * * \return A BMFace pointer */ -BMFace *bmesh_sfme(BMesh *bm, BMFace *f, BMLoop *l_v1, BMLoop *l_v2, - BMLoop **r_l, +BMFace *bmesh_sfme( + BMesh *bm, BMFace *f, BMLoop *l_v1, BMLoop *l_v2, + BMLoop **r_l, #ifdef USE_BMESH_HOLES - ListBase *holes, + ListBase *holes, #endif - BMEdge *e_example, - const bool no_double - ) + BMEdge *e_example, + const bool no_double) { #ifdef USE_BMESH_HOLES BMLoopList *lst, *lst2; @@ -1481,14 +1501,7 @@ BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **r_e) bmesh_disk_edge_remove(e_new, tv); bmesh_disk_edge_remove(e_new, v_new); - /* remove e from tv's disk cycle */ - bmesh_disk_edge_remove(e, tv); - - /* swap out tv for v_new in e */ - bmesh_edge_swapverts(e, tv, v_new); - - /* add e to v_new's disk cycle */ - bmesh_disk_edge_append(e, v_new); + bmesh_disk_vert_replace(e, v_new, tv); /* add e_new to v_new's disk cycle */ bmesh_disk_edge_append(e_new, v_new); @@ -1653,13 +1666,14 @@ BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **r_e) * faces with just 2 edges. It is up to the caller to decide what to do with * these faces. */ -BMEdge *bmesh_jekv(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, - const bool do_del, const bool check_edge_double) +BMEdge *bmesh_jekv( + BMesh *bm, BMEdge *e_kill, BMVert *v_kill, + const bool do_del, const bool check_edge_double) { BMEdge *e_old; BMVert *v_old, *tv; BMLoop *l_kill; - int len, radlen = 0, i; + int radlen = 0, i; bool halt = false; #ifndef NDEBUG bool edok; @@ -1670,10 +1684,8 @@ BMEdge *bmesh_jekv(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, if (BM_vert_in_edge(e_kill, v_kill) == 0) { return NULL; } - - len = bmesh_disk_count(v_kill); - if (len == 2) { + if (bmesh_disk_count_ex(v_kill, 3) == 2) { #ifndef NDEBUG int valence1, valence2; BMLoop *l; @@ -1700,12 +1712,8 @@ BMEdge *bmesh_jekv(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, e_splice = BM_edge_exists(tv, v_old); } - /* remove e_old from v_kill's disk cycle */ - bmesh_disk_edge_remove(e_old, v_kill); - /* relink e_old->v_kill to be e_old->tv */ - bmesh_edge_swapverts(e_old, v_kill, tv); - /* append e_old to tv's disk cycle */ - bmesh_disk_edge_append(e_old, tv); + bmesh_disk_vert_replace(e_old, tv, v_kill); + /* remove e_kill from tv's disk cycle */ bmesh_disk_edge_remove(e_kill, tv); @@ -1789,7 +1797,7 @@ BMEdge *bmesh_jekv(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, if (check_edge_double) { if (e_splice) { /* removes e_splice */ - BM_edge_splice(bm, e_splice, e_old); + BM_edge_splice(bm, e_old, e_splice); } } @@ -1963,27 +1971,38 @@ bool BM_vert_splice_check_double(BMVert *v_a, BMVert *v_b) BLI_assert(BM_edge_exists(v_a, v_b) == false); if (v_a->e && v_b->e) { - SmallHash visit; BMEdge *e, *e_first; - BLI_smallhash_init(&visit); +#define VERT_VISIT _FLAG_WALK + /* tag 'v_a' */ e = e_first = v_a->e; do { BMVert *v_other = BM_edge_other_vert(e, v_a); - BLI_smallhash_insert(&visit, (uintptr_t)v_other, NULL); + BLI_assert(!BM_ELEM_API_FLAG_TEST(v_other, VERT_VISIT)); + BM_ELEM_API_FLAG_ENABLE(v_other, VERT_VISIT); } while ((e = BM_DISK_EDGE_NEXT(e, v_a)) != e_first); + /* check 'v_b' connects to 'v_a' edges */ e = e_first = v_b->e; do { BMVert *v_other = BM_edge_other_vert(e, v_b); - if (BLI_smallhash_haskey(&visit, (uintptr_t)v_other)) { + if (BM_ELEM_API_FLAG_TEST(v_other, VERT_VISIT)) { is_double = true; break; } } while ((e = BM_DISK_EDGE_NEXT(e, v_b)) != e_first); - BLI_smallhash_release(&visit); + /* cleanup */ + e = e_first = v_a->e; + do { + BMVert *v_other = BM_edge_other_vert(e, v_a); + BLI_assert(BM_ELEM_API_FLAG_TEST(v_other, VERT_VISIT)); + BM_ELEM_API_FLAG_DISABLE(v_other, VERT_VISIT); + } while ((e = BM_DISK_EDGE_NEXT(e, v_a)) != e_first); + +#undef VERT_VISIT + } return is_double; @@ -1992,57 +2011,51 @@ bool BM_vert_splice_check_double(BMVert *v_a, BMVert *v_b) /** * \brief Splice Vert * - * Merges two verts into one (\a v into \a vtarget). + * Merges two verts into one + * (\a v_src into \a v_dst, removing \a v_src). * * \return Success * - * \warning This does't work for collapsing edges, + * \warning This doesn't work for collapsing edges, * where \a v and \a vtarget are connected by an edge * (assert checks for this case). */ -bool BM_vert_splice(BMesh *bm, BMVert *v, BMVert *v_target) +bool BM_vert_splice(BMesh *bm, BMVert *v_dst, BMVert *v_src) { BMEdge *e; /* verts already spliced */ - if (v == v_target) { + if (v_src == v_dst) { return false; } - BLI_assert(BM_vert_pair_share_face_check(v, v_target) == false); - - /* move all the edges from v's disk to vtarget's disk */ - while ((e = v->e)) { - - /* loop */ - BMLoop *l_first; - if ((l_first = e->l)) { - BMLoop *l_iter = l_first; - do { - if (l_iter->v == v) { - l_iter->v = v_target; - } - /* else if (l_iter->prev->v == v) {...} - * (this case will be handled by a different edge) */ - } while ((l_iter = l_iter->radial_next) != l_first); - } + BLI_assert(BM_vert_pair_share_face_check(v_src, v_dst) == false); - /* disk */ - bmesh_disk_edge_remove(e, v); - bmesh_edge_swapverts(e, v, v_target); - bmesh_disk_edge_append(e, v_target); + /* move all the edges from 'v_src' disk to 'v_dst' */ + while ((e = v_src->e)) { + bmesh_edge_vert_swap(e, v_dst, v_src); BLI_assert(e->v1 != e->v2); } - BM_CHECK_ELEMENT(v); - BM_CHECK_ELEMENT(v_target); + BM_CHECK_ELEMENT(v_src); + BM_CHECK_ELEMENT(v_dst); - /* v is unused now, and can be killed */ - BM_vert_kill(bm, v); + /* 'v_src' is unused now, and can be killed */ + BM_vert_kill(bm, v_src); return true; } + +/** \name BM_vert_separate, bmesh_vert_separate and friends + * \{ */ + +/* BM_edge_face_count(e) >= 1 */ +BLI_INLINE bool bm_edge_supports_separate(const BMEdge *e) +{ + return (e->l && e->l->radial_next != e->l); +} + /** * \brief Separate Vert * @@ -2054,167 +2067,252 @@ bool BM_vert_splice(BMesh *bm, BMVert *v, BMVert *v_target) * * \return Success */ -void bmesh_vert_separate(BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len, - const bool copy_select) +void bmesh_vert_separate( + BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len, + const bool copy_select) { - const int v_edgetot = BM_vert_face_count(v); - BMEdge **stack = BLI_array_alloca(stack, v_edgetot); - STACK_DECLARE(stack); + int v_edges_num = 0; - SmallHash visithash; - BMVert **verts = NULL; - BMIter eiter, liter; - BMLoop *l; - BMEdge *e; - int i, maxindex; - BMLoop *l_new; + /* Detailed notes on array use since this is stack memory, we have to be careful */ - BLI_smallhash_init_ex(&visithash, v_edgetot); + /* newly created vertices, only use when 'r_vout' is set + * (total size will be number of fans) */ + BLI_SMALLSTACK_DECLARE(verts_new, BMVert *); + /* fill with edges from the face-fan, clearing on completion + * (total size will be max fan edge count) */ + BLI_SMALLSTACK_DECLARE(edges, BMEdge *); + /* temp store edges to walk over when filling 'edges', + * (total size will be max radial edges of any edge) */ + BLI_SMALLSTACK_DECLARE(edges_search, BMEdge *); - STACK_INIT(stack, v_edgetot); + /* number of resulting verts, include self */ + int verts_num = 1; + /* track the total number of edges handled, so we know when we've found the last fan */ + int edges_found = 0; - maxindex = 0; - BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { - if (BLI_smallhash_haskey(&visithash, (uintptr_t)e)) { - continue; - } +#define EDGE_VISIT _FLAG_WALK + /* count and flag at once */ + if (v->e) { + BMEdge *e_first, *e_iter; + e_iter = e_first = v->e; + do { + v_edges_num += 1; + + BLI_assert(!BM_ELEM_API_FLAG_TEST(e_iter, EDGE_VISIT)); + BM_ELEM_API_FLAG_ENABLE(e_iter, EDGE_VISIT); + } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first); + } + + while (true) { /* Considering only edges and faces incident on vertex v, walk - * the edges & faces and assign an index to each connected set */ - BLI_smallhash_insert(&visithash, (uintptr_t)e, SET_INT_IN_POINTER(maxindex)); + * the edges & collect in the 'edges' list for splitting */ + + BMEdge *e = v->e; + BM_ELEM_API_FLAG_DISABLE(e, EDGE_VISIT); + do { + BLI_assert(!BM_ELEM_API_FLAG_TEST(e, EDGE_VISIT)); + BLI_SMALLSTACK_PUSH(edges, e); + edges_found += 1; + if (e->l) { BMLoop *l_iter, *l_first; l_iter = l_first = e->l; do { - l_new = (l_iter->v == v) ? l_iter->prev : l_iter->next; - BLI_assert(BM_vert_in_edge(l_new->e, v)); - if (!BLI_smallhash_haskey(&visithash, (uintptr_t)l_new->e)) { - BLI_smallhash_insert(&visithash, (uintptr_t)l_new->e, SET_INT_IN_POINTER(maxindex)); - STACK_PUSH(stack, l_new->e); + BMLoop *l_adjacent = (l_iter->v == v) ? l_iter->prev : l_iter->next; + BLI_assert(BM_vert_in_edge(l_adjacent->e, v)); + if (BM_ELEM_API_FLAG_TEST(l_adjacent->e, EDGE_VISIT)) { + BM_ELEM_API_FLAG_DISABLE(l_adjacent->e, EDGE_VISIT); + BLI_SMALLSTACK_PUSH(edges_search, l_adjacent->e); } } while ((l_iter = l_iter->radial_next) != l_first); } - } while ((e = STACK_POP(stack))); - - maxindex++; - } + } while ((e = BLI_SMALLSTACK_POP(edges_search))); - /* Make enough verts to split v for each group */ - if (r_vout != NULL) { - verts = MEM_callocN(sizeof(BMVert *) * maxindex, __func__); - } - else { - verts = BLI_array_alloca(verts, maxindex); - } + /* now we have all edges connected to 'v->e' */ - verts[0] = v; - for (i = 1; i < maxindex; i++) { - verts[i] = BM_vert_create(bm, v->co, v, BM_CREATE_NOP); - if (copy_select) { - BM_elem_select_copy(bm, bm, verts[i], v); - } - } + BLI_assert(edges_found <= v_edges_num); - /* Replace v with the new verts in each group */ -#if 0 - BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { - /* call first since its faster then a hash lookup */ - if (l->v != v) { - continue; - } - i = GET_INT_FROM_POINTER(BLI_ghash_lookup(visithash, l->e)); - if (i == 0) { - continue; + if (edges_found == v_edges_num) { + /* We're done! The remaining edges in 'edges' form the last fan, + * which can be left as is. + * if 'edges' were alloc'd it'd be freed here. */ + break; } + else { + BMVert *v_new; - /* Loops here should always refer to an edge that has v as an - * endpoint. For each appearance of this vert in a face, there - * will actually be two iterations: one for the loop heading - * towards vertex v, and another for the loop heading out from - * vertex v. Only need to swap the vertex on one of those times, - * on the outgoing loop. */ + v_new = BM_vert_create(bm, v->co, v, BM_CREATE_NOP); + if (copy_select) { + BM_elem_select_copy(bm, bm, v_new, v); + } - /* XXX - because this clobbers the iterator, this *whole* block is commented, see below */ - l->v = verts[i]; - } -#else - /* note: this is the same as the commented code above *except* that it doesn't break iterator - * by modifying data it loops over [#30632], this re-uses the 'stack' variable which is a bit - * bad practice but save alloc'ing a new array - note, the comment above is useful, keep it - * if you are tidying up code - campbell */ - STACK_INIT(stack, v_edgetot); - BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { - if (l->v == v) { - STACK_PUSH(stack, (BMEdge *)l); - } - } - while ((l = (BMLoop *)(STACK_POP(stack)))) { - if ((i = GET_INT_FROM_POINTER(BLI_smallhash_lookup(&visithash, (uintptr_t)l->e)))) { - l->v = verts[i]; - } - } -#endif + while ((e = BLI_SMALLSTACK_POP(edges))) { + bmesh_edge_vert_swap(e, v_new, v); + } - BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { - i = GET_INT_FROM_POINTER(BLI_smallhash_lookup(&visithash, (uintptr_t)e)); - if (i == 0) { - continue; + if (r_vout) { + BLI_SMALLSTACK_PUSH(verts_new, v_new); + } + verts_num += 1; } - - BLI_assert(e->v1 == v || e->v2 == v); - bmesh_disk_edge_remove(e, v); - bmesh_edge_swapverts(e, v, verts[i]); - bmesh_disk_edge_append(e, verts[i]); } - BLI_smallhash_release(&visithash); +#undef EDGE_VISIT - for (i = 0; i < maxindex; i++) { - BM_CHECK_ELEMENT(verts[i]); - } + /* flags are clean now, handle return values */ if (r_vout_len != NULL) { - *r_vout_len = maxindex; + *r_vout_len = verts_num; } if (r_vout != NULL) { + BMVert **verts; + + verts = MEM_mallocN(sizeof(BMVert *) * verts_num, __func__); *r_vout = verts; + + verts[0] = v; + BLI_SMALLSTACK_AS_TABLE(verts_new, &verts[1]); } } /** + * Utility function for #BM_vert_separate + * + * Takes a list of edges, which have been split from their original. + * + * Any edges which failed to split off in #bmesh_vert_separate will be merged back into the original edge. + * + * \param edges_separate + * A list-of-lists, each list is from a single original edge (the first edge is the original), + * Check for duplicates (not just with the first) but between all. + * This is O(n2) but radial edges are very rarely >2 and almost never >~10. + * + * \note typically its best to avoid creating the data in the first place, + * but inspecting all loops connectivity is quite involved. + * + * \note this function looks like it could become slow, + * but in common cases its only going to iterate a few times. + */ +static void bmesh_vert_separate__cleanup(BMesh *bm, LinkNode *edges_separate) +{ + do { + LinkNode *n_orig = edges_separate->link; + do { + BMEdge *e_orig = n_orig->link; + LinkNode *n_step = n_orig->next; + LinkNode *n_prev = n_orig; + do { + BMEdge *e = n_step->link; + BLI_assert(e != e_orig); + if ((e->v1 == e_orig->v1) && (e->v2 == e_orig->v2)) { + BM_edge_splice(bm, e_orig, e); + n_prev->next = n_step->next; + n_step = n_prev; + } + } while ((n_prev = n_step), + (n_step = n_step->next)); + + } while ((n_orig = n_orig->next) && n_orig->next); + } while ((edges_separate = edges_separate->next)); +} + +/** * High level function which wraps both #bmesh_vert_separate and #bmesh_edge_separate */ -void BM_vert_separate(BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len, - BMEdge **e_in, int e_in_len) +void BM_vert_separate( + BMesh *bm, BMVert *v, + BMEdge **e_in, int e_in_len, + const bool copy_select, + BMVert ***r_vout, int *r_vout_len) { + LinkNode *edges_separate = NULL; int i; for (i = 0; i < e_in_len; i++) { BMEdge *e = e_in[i]; - if (e->l && BM_vert_in_edge(e, v)) { - bmesh_edge_separate(bm, e, e->l, false); + if (bm_edge_supports_separate(e)) { + LinkNode *edges_orig = NULL; + do { + BMLoop *l_sep = e->l; + bmesh_edge_separate(bm, e, l_sep, copy_select); + BLI_linklist_prepend_alloca(&edges_orig, l_sep->e); + BLI_assert(e != l_sep->e); + } while (bm_edge_supports_separate(e)); + BLI_linklist_prepend_alloca(&edges_orig, e); + BLI_linklist_prepend_alloca(&edges_separate, edges_orig); } } - bmesh_vert_separate(bm, v, r_vout, r_vout_len, false); + bmesh_vert_separate(bm, v, r_vout, r_vout_len, copy_select); + + if (edges_separate) { + bmesh_vert_separate__cleanup(bm, edges_separate); + } } + +/** + * A version of #BM_vert_separate which takes a flag. + */ +void BM_vert_separate_hflag( + BMesh *bm, BMVert *v, + const char hflag, + const bool copy_select, + BMVert ***r_vout, int *r_vout_len) +{ + LinkNode *edges_separate = NULL; + BMEdge *e_iter, *e_first; + + e_iter = e_first = v->e; + do { + if (BM_elem_flag_test(e_iter, hflag)) { + BMEdge *e = e_iter; + if (bm_edge_supports_separate(e)) { + LinkNode *edges_orig = NULL; + do { + BMLoop *l_sep = e->l; + bmesh_edge_separate(bm, e, l_sep, copy_select); + /* trick to avoid looping over seperated edges */ + if (edges_separate == NULL && edges_orig == NULL) { + e_first = l_sep->e; + } + BLI_linklist_prepend_alloca(&edges_orig, l_sep->e); + BLI_assert(e != l_sep->e); + } while (bm_edge_supports_separate(e)); + BLI_linklist_prepend_alloca(&edges_orig, e); + BLI_linklist_prepend_alloca(&edges_separate, edges_orig); + } + } + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first); + + bmesh_vert_separate(bm, v, r_vout, r_vout_len, copy_select); + + if (edges_separate) { + bmesh_vert_separate__cleanup(bm, edges_separate); + } +} + +/** \} */ + + /** * \brief Splice Edge * * Splice two unique edges which share the same two vertices into one edge. + * (\a e_src into \a e_dst, removing e_src). * * \return Success * * \note Edges must already have the same vertices. */ -bool BM_edge_splice(BMesh *bm, BMEdge *e, BMEdge *e_target) +bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src) { BMLoop *l; - if (!BM_vert_in_edge(e, e_target->v1) || !BM_vert_in_edge(e, e_target->v2)) { + if (!BM_vert_in_edge(e_src, e_dst->v1) || !BM_vert_in_edge(e_src, e_dst->v2)) { /* not the same vertices can't splice */ /* the caller should really make sure this doesn't happen ever @@ -2224,21 +2322,21 @@ bool BM_edge_splice(BMesh *bm, BMEdge *e, BMEdge *e_target) return false; } - while (e->l) { - l = e->l; - BLI_assert(BM_vert_in_edge(e_target, l->v)); - BLI_assert(BM_vert_in_edge(e_target, l->next->v)); - bmesh_radial_loop_remove(l, e); - bmesh_radial_append(e_target, l); + while (e_src->l) { + l = e_src->l; + BLI_assert(BM_vert_in_edge(e_dst, l->v)); + BLI_assert(BM_vert_in_edge(e_dst, l->next->v)); + bmesh_radial_loop_remove(l, e_src); + bmesh_radial_append(e_dst, l); } - BLI_assert(bmesh_radial_length(e->l) == 0); + BLI_assert(bmesh_radial_length(e_src->l) == 0); - BM_CHECK_ELEMENT(e); - BM_CHECK_ELEMENT(e_target); + BM_CHECK_ELEMENT(e_src); + BM_CHECK_ELEMENT(e_dst); /* removes from disks too */ - BM_edge_kill(bm, e); + BM_edge_kill(bm, e_src); return true; } @@ -2254,8 +2352,9 @@ bool BM_edge_splice(BMesh *bm, BMEdge *e, BMEdge *e_target) * \note Does nothing if \a l_sep is already the only loop in the * edge radial. */ -void bmesh_edge_separate(BMesh *bm, BMEdge *e, BMLoop *l_sep, - const bool copy_select) +void bmesh_edge_separate( + BMesh *bm, BMEdge *e, BMLoop *l_sep, + const bool copy_select) { BMEdge *e_new; #ifndef NDEBUG @@ -2266,7 +2365,7 @@ void bmesh_edge_separate(BMesh *bm, BMEdge *e, BMLoop *l_sep, BLI_assert(e->l); if (BM_edge_is_boundary(e)) { - /* no cut required */ + BLI_assert(0); /* no cut required */ return; } @@ -2296,75 +2395,252 @@ void bmesh_edge_separate(BMesh *bm, BMEdge *e, BMLoop *l_sep, * Disconnects a face from its vertex fan at loop \a l_sep * * \return The newly created BMVert + * + * \note Will be a no-op and return original vertex if only two edges at that vertex. */ BMVert *bmesh_urmv_loop(BMesh *bm, BMLoop *l_sep) { - BMVert **vtar; - int len, i; BMVert *v_new = NULL; BMVert *v_sep = l_sep->v; + BMEdge *e_iter; + BMEdge *edges[2]; + int i; /* peel the face from the edge radials on both sides of the * loop vert, disconnecting the face from its fan */ - bmesh_edge_separate(bm, l_sep->e, l_sep, false); - bmesh_edge_separate(bm, l_sep->prev->e, l_sep->prev, false); + if (!BM_edge_is_boundary(l_sep->e)) + bmesh_edge_separate(bm, l_sep->e, l_sep, false); + if (!BM_edge_is_boundary(l_sep->prev->e)) + bmesh_edge_separate(bm, l_sep->prev->e, l_sep->prev, false); - if (bmesh_disk_count(v_sep) == 2) { - /* If there are still only two edges out of v_sep, then - * this whole URMV was just a no-op, so exit now. */ + /* do inline, below */ +#if 0 + if (BM_vert_edge_count_is_equal(v_sep, 2)) { return v_sep; } +#endif - /* Update the disk start, so that v->e points to an edge - * not touching the split loop. This is so that BM_vert_split - * will leave the original v_sep on some *other* fan (not the - * one-face fan that holds the unglue face). */ - while (v_sep->e == l_sep->e || v_sep->e == l_sep->prev->e) { - v_sep->e = bmesh_disk_edge_next(v_sep->e, v_sep); + /* Search for an edge unattached to this loop */ + e_iter = v_sep->e; + while (!ELEM(e_iter, l_sep->e, l_sep->prev->e)) { + e_iter = bmesh_disk_edge_next(e_iter, v_sep); + + /* We've come back around to the initial edge, all touch this loop. + * If there are still only two edges out of v_sep, + * then this whole URMV was just a no-op, so exit now. */ + if (e_iter == v_sep->e) { + BLI_assert(BM_vert_edge_count_is_equal(v_sep, 2)); + return v_sep; + } } - /* Split all fans connected to the vert, duplicating it for - * each fans. */ - bmesh_vert_separate(bm, v_sep, &vtar, &len, false); + v_sep->e = l_sep->e; - /* There should have been at least two fans cut apart here, - * otherwise the early exit would have kicked in. */ - BLI_assert(len >= 2); + v_new = BM_vert_create(bm, v_sep->co, v_sep, BM_CREATE_NOP); - v_new = l_sep->v; + edges[0] = l_sep->e; + edges[1] = l_sep->prev->e; - /* Desired result here is that a new vert should always be - * created for the unglue face. This is so we can glue any - * extras back into the original vert. */ - BLI_assert(v_new != v_sep); - BLI_assert(v_sep == vtar[0]); + for (i = 0; i < ARRAY_SIZE(edges); i++) { + BMEdge *e = edges[i]; + bmesh_edge_vert_swap(e, v_new, v_sep); + } - /* If there are more than two verts as a result, glue together - * all the verts except the one this URMV intended to create */ - if (len > 2) { - for (i = 0; i < len; i++) { - if (vtar[i] == v_new) { - break; + BLI_assert(v_sep != l_sep->v); + BLI_assert(v_sep->e != l_sep->v->e); + + BM_CHECK_ELEMENT(l_sep); + BM_CHECK_ELEMENT(v_sep); + BM_CHECK_ELEMENT(edges[0]); + BM_CHECK_ELEMENT(edges[1]); + BM_CHECK_ELEMENT(v_new); + + return v_new; +} + +/** + * A version of #bmesh_urmv_loop that disconnects multiple loops at once. + * + * Handles the task of finding fans boundaries. + */ +BMVert *bmesh_urmv_loop_multi( + BMesh *bm, BMLoop **larr, int larr_len) +{ + BMVert *v_sep = larr[0]->v; + BMVert *v_new; + int i; + bool is_mixed_any = false; + + BLI_SMALLSTACK_DECLARE(edges, BMEdge *); + +#define LOOP_VISIT _FLAG_WALK +#define EDGE_VISIT _FLAG_WALK + + for (i = 0; i < larr_len; i++) { + BMLoop *l_sep = larr[i]; + + /* all must be from the same vert! */ + BLI_assert(v_sep == l_sep->v); + + BLI_assert(!BM_ELEM_API_FLAG_TEST(l_sep, LOOP_VISIT)); + BM_ELEM_API_FLAG_ENABLE(l_sep, LOOP_VISIT); + + /* weak! but it makes it simpler to check for edges to split + * while doing a radial loop (where loops may be adjacent) */ + BM_ELEM_API_FLAG_ENABLE(l_sep->next, LOOP_VISIT); + BM_ELEM_API_FLAG_ENABLE(l_sep->prev, LOOP_VISIT); + } + + for (i = 0; i < larr_len; i++) { + BMLoop *l_sep = larr[i]; + + BMLoop *loop_pair[2] = {l_sep, l_sep->prev}; + int j; + for (j = 0; j < ARRAY_SIZE(loop_pair); j++) { + BMEdge *e = loop_pair[j]->e; + if (!BM_ELEM_API_FLAG_TEST(e, EDGE_VISIT)) { + BMLoop *l_iter, *l_first; + bool is_mixed = false; + + BM_ELEM_API_FLAG_ENABLE(e, EDGE_VISIT); + + l_iter = l_first = e->l; + do { + if (!BM_ELEM_API_FLAG_TEST(l_iter, LOOP_VISIT)) { + is_mixed = true; + is_mixed_any = true; + break; + } + } while ((l_iter = l_iter->radial_next) != l_first); + + if (is_mixed) { + /* ensure the first loop is one we don't own so we can do a quick check below + * on the edge's loop-flag to see if the edge is mixed or not. */ + e->l = l_iter; + } + BLI_SMALLSTACK_PUSH(edges, e); } } + } + + if (is_mixed_any == false) { + /* all loops in 'larr' are the soul owners of their edges. + * nothing to split away from, this is a no-op */ + v_new = v_sep; + } + else { + BMEdge *e; + + BLI_assert(!BLI_SMALLSTACK_IS_EMPTY(edges)); + + v_new = BM_vert_create(bm, v_sep->co, v_sep, BM_CREATE_NOP); + while ((e = BLI_SMALLSTACK_POP(edges))) { + BMLoop *l_iter, *l_first, *l_next; + BMEdge *e_new; + + /* disable so copied edge isn't left dirty (loop edges are cleared last too) */ + BM_ELEM_API_FLAG_DISABLE(e, EDGE_VISIT); + + if (!BM_ELEM_API_FLAG_TEST(e->l, LOOP_VISIT)) { + /* edge has some loops owned by us, some owned by other loops */ + BMVert *e_new_v_pair[2]; - if (i != len) { - /* Swap the single vert that was needed for the - * unglue into the last array slot */ - SWAP(BMVert *, vtar[i], vtar[len - 1]); + if (e->v1 == v_sep) { + e_new_v_pair[0] = v_new; + e_new_v_pair[1] = e->v2; + } + else { + BLI_assert(v_sep == e->v2); + e_new_v_pair[0] = e->v1; + e_new_v_pair[1] = v_new; + } - /* And then glue the rest back together */ - for (i = 1; i < len - 1; i++) { - BM_vert_splice(bm, vtar[i], vtar[0]); + e_new = BM_edge_create(bm, UNPACK2(e_new_v_pair), e, BM_CREATE_NOP); + + /* now moved all loops from 'larr' to this newly created edge */ + l_iter = l_first = e->l; + do { + l_next = l_iter->radial_next; + if (BM_ELEM_API_FLAG_TEST(l_iter, LOOP_VISIT)) { + bmesh_radial_loop_remove(l_iter, e); + bmesh_radial_append(e_new, l_iter); + l_iter->e = e_new; + } + } while ((l_iter = l_next) != l_first); + } + else { + /* we own the edge entirely, replace the vert */ + bmesh_disk_vert_replace(e, v_new, v_sep); } + + /* loop vert is handled last! */ } } - MEM_freeN(vtar); + for (i = 0; i < larr_len; i++) { + BMLoop *l_sep = larr[i]; + + l_sep->v = v_new; + + BLI_assert(BM_ELEM_API_FLAG_TEST(l_sep, LOOP_VISIT)); + BLI_assert(BM_ELEM_API_FLAG_TEST(l_sep->prev, LOOP_VISIT)); + BLI_assert(BM_ELEM_API_FLAG_TEST(l_sep->next, LOOP_VISIT)); + BM_ELEM_API_FLAG_DISABLE(l_sep, LOOP_VISIT); + BM_ELEM_API_FLAG_DISABLE(l_sep->prev, LOOP_VISIT); + BM_ELEM_API_FLAG_DISABLE(l_sep->next, LOOP_VISIT); + + + BM_ELEM_API_FLAG_DISABLE(l_sep->prev->e, EDGE_VISIT); + BM_ELEM_API_FLAG_DISABLE(l_sep->e, EDGE_VISIT); + } + +#undef LOOP_VISIT +#undef EDGE_VISIT + + return v_new; +} + +static void bmesh_edge_vert_swap__recursive(BMEdge *e, BMVert *v_dst, BMVert *v_src) +{ + BMLoop *l_iter, *l_first; + + BLI_assert(ELEM(v_src, e->v1, e->v2)); + bmesh_disk_vert_replace(e, v_dst, v_src); + + l_iter = l_first = e->l; + do { + if (l_iter->v == v_src) { + l_iter->v = v_dst; + if (BM_vert_in_edge(l_iter->prev->e, v_src)) { + bmesh_edge_vert_swap__recursive(l_iter->prev->e, v_dst, v_src); + } + } + else if (l_iter->next->v == v_src) { + l_iter->next->v = v_dst; + if (BM_vert_in_edge(l_iter->next->e, v_src)) { + bmesh_edge_vert_swap__recursive(l_iter->next->e, v_dst, v_src); + } + } + else { + BLI_assert(l_iter->prev->v != v_src); + } + } while ((l_iter = l_iter->radial_next) != l_first); +} +/** + * This function assumes l_sep is apart of a larger fan which has already been + * isolated by calling bmesh_edge_separate to segregate it radially. + */ +BMVert *bmesh_urmv_loop_region(BMesh *bm, BMLoop *l_sep) +{ + BMVert *v_new = BM_vert_create(bm, l_sep->v->co, l_sep->v, BM_CREATE_NOP); + /* passing either 'l_sep->e', 'l_sep->prev->e' will work */ + bmesh_edge_vert_swap__recursive(l_sep->e, v_new, l_sep->v); + BLI_assert(l_sep->v == v_new); return v_new; } + /** * \brief Unglue Region Make Vert (URMV) * diff --git a/source/blender/bmesh/intern/bmesh_core.h b/source/blender/bmesh/intern/bmesh_core.h index ab847fc82eb..2b100eb7b8d 100644 --- a/source/blender/bmesh/intern/bmesh_core.h +++ b/source/blender/bmesh/intern/bmesh_core.h @@ -27,8 +27,9 @@ * \ingroup bmesh */ -BMFace *BM_face_copy(BMesh *bm_dst, BMesh *bm_src, BMFace *f, - const bool copy_verts, const bool copy_edges); +BMFace *BM_face_copy( + BMesh *bm_dst, BMesh *bm_src, BMFace *f, + const bool copy_verts, const bool copy_edges); typedef enum eBMCreateFlag { BM_CREATE_NOP = 0, @@ -40,15 +41,19 @@ typedef enum eBMCreateFlag { BM_CREATE_SKIP_CD = (1 << 2), } eBMCreateFlag; -BMVert *BM_vert_create(BMesh *bm, const float co[3], - const BMVert *v_example, const eBMCreateFlag create_flag); -BMEdge *BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, - const BMEdge *e_example, const eBMCreateFlag create_flag); -BMFace *BM_face_create(BMesh *bm, BMVert **verts, BMEdge **edges, const int len, - const BMFace *f_example, const eBMCreateFlag create_flag); -BMFace *BM_face_create_verts(BMesh *bm, BMVert **verts, const int len, - const BMFace *f_example, const eBMCreateFlag create_flag, - const bool create_edges); +BMVert *BM_vert_create( + BMesh *bm, const float co[3], + const BMVert *v_example, const eBMCreateFlag create_flag); +BMEdge *BM_edge_create( + BMesh *bm, BMVert *v1, BMVert *v2, + const BMEdge *e_example, const eBMCreateFlag create_flag); +BMFace *BM_face_create( + BMesh *bm, BMVert **verts, BMEdge **edges, const int len, + const BMFace *f_example, const eBMCreateFlag create_flag); +BMFace *BM_face_create_verts( + BMesh *bm, BMVert **verts, const int len, + const BMFace *f_example, const eBMCreateFlag create_flag, + const bool create_edges); void BM_face_edges_kill(BMesh *bm, BMFace *f); void BM_face_verts_kill(BMesh *bm, BMFace *f); @@ -57,25 +62,32 @@ void BM_face_kill(BMesh *bm, BMFace *f); void BM_edge_kill(BMesh *bm, BMEdge *e); void BM_vert_kill(BMesh *bm, BMVert *v); -void bmesh_edge_separate(BMesh *bm, BMEdge *e, BMLoop *l_sep, - const bool copy_select); -bool BM_edge_splice(BMesh *bm, BMEdge *e, BMEdge *e_target); -bool BM_vert_splice(BMesh *bm, BMVert *v, BMVert *v_target); +void bmesh_edge_separate( + BMesh *bm, BMEdge *e, BMLoop *l_sep, + const bool copy_select); +bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src); +bool BM_vert_splice(BMesh *bm, BMVert *v_dst, BMVert *v_src); bool BM_vert_splice_check_double(BMVert *v_a, BMVert *v_b); -void bmesh_vert_separate(BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len, - const bool copy_select); +void bmesh_vert_separate( + BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len, + const bool copy_select); bool bmesh_loop_reverse(BMesh *bm, BMFace *f); BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del); -void BM_vert_separate(BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len, - BMEdge **e_in, int e_in_len); +void BM_vert_separate( + BMesh *bm, BMVert *v, BMEdge **e_in, int e_in_len, const bool copy_select, + BMVert ***r_vout, int *r_vout_len); +void BM_vert_separate_hflag( + BMesh *bm, BMVert *v, const char hflag, const bool copy_select, + BMVert ***r_vout, int *r_vout_len); /* EULER API - For modifying structure */ -BMFace *bmesh_sfme(BMesh *bm, BMFace *f, - BMLoop *l1, BMLoop *l2, - BMLoop **r_l, +BMFace *bmesh_sfme( + BMesh *bm, BMFace *f, + BMLoop *l1, BMLoop *l2, + BMLoop **r_l, #ifdef USE_BMESH_HOLES ListBase *holes, #endif @@ -84,11 +96,15 @@ BMFace *bmesh_sfme(BMesh *bm, BMFace *f, ); BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **r_e); -BMEdge *bmesh_jekv(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, - const bool do_del, const bool check_edge_splice); +BMEdge *bmesh_jekv( + BMesh *bm, BMEdge *e_kill, BMVert *v_kill, + const bool do_del, const bool check_edge_splice); BMFace *bmesh_jfke(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e); BMVert *bmesh_urmv(BMesh *bm, BMFace *f_sep, BMVert *v_sep); BMVert *bmesh_urmv_loop(BMesh *bm, BMLoop *l_sep); +BMVert *bmesh_urmv_loop_multi( + BMesh *bm, BMLoop **larr, int larr_len); +BMVert *bmesh_urmv_loop_region(BMesh *bm, BMLoop *l_sep); void bmesh_face_swap_data(BMFace *f_a, BMFace *f_b); diff --git a/source/blender/bmesh/intern/bmesh_edgeloop.c b/source/blender/bmesh/intern/bmesh_edgeloop.c index e83a1d5b00a..eaa070151a6 100644 --- a/source/blender/bmesh/intern/bmesh_edgeloop.c +++ b/source/blender/bmesh/intern/bmesh_edgeloop.c @@ -52,8 +52,9 @@ typedef struct BMEdgeLoopStore { /* -------------------------------------------------------------------- */ /* BM_mesh_edgeloops_find & Util Functions */ -static int bm_vert_other_tag(BMVert *v, BMVert *v_prev, - BMEdge **r_e) +static int bm_vert_other_tag( + BMVert *v, BMVert *v_prev, + BMEdge **r_e) { BMIter iter; BMEdge *e, *e_next = NULL; @@ -125,8 +126,9 @@ static bool bm_loop_build(BMEdgeLoopStore *el_store, BMVert *v_prev, BMVert *v, /** * \return listbase of listbases, each linking to a vertex. */ -int BM_mesh_edgeloops_find(BMesh *bm, ListBase *r_eloops, - bool (*test_fn)(BMEdge *, void *user_data), void *user_data) +int BM_mesh_edgeloops_find( + BMesh *bm, ListBase *r_eloops, + bool (*test_fn)(BMEdge *, void *user_data), void *user_data) { BMIter iter; BMEdge *e; @@ -183,8 +185,9 @@ struct VertStep { BMVert *v; }; -static void vs_add(BLI_mempool *vs_pool, ListBase *lb, - BMVert *v, BMEdge *e_prev, const int iter_tot) +static void vs_add( + BLI_mempool *vs_pool, ListBase *lb, + BMVert *v, BMEdge *e_prev, const int iter_tot) { struct VertStep *vs_new = BLI_mempool_alloc(vs_pool); vs_new->v = v; @@ -256,9 +259,10 @@ static bool bm_loop_path_build_step(BLI_mempool *vs_pool, ListBase *lb, const in return (BLI_listbase_is_empty(lb) == false); } -bool BM_mesh_edgeloops_find_path(BMesh *bm, ListBase *r_eloops, - bool (*test_fn)(BMEdge *, void *user_data), void *user_data, - BMVert *v_src, BMVert *v_dst) +bool BM_mesh_edgeloops_find_path( + BMesh *bm, ListBase *r_eloops, + bool (*test_fn)(BMEdge *, void *user_data), void *user_data, + BMVert *v_src, BMVert *v_dst) { BMIter iter; BMEdge *e; @@ -662,7 +666,7 @@ void BM_edgeloop_flip(BMesh *UNUSED(bm), BMEdgeLoopStore *el_store) void BM_edgeloop_expand(BMesh *UNUSED(bm), BMEdgeLoopStore *el_store, int el_store_len) { - /* first double until we are more then half as big */ + /* first double until we are more than half as big */ while ((el_store->len * 2) < el_store_len) { LinkData *node_curr = el_store->verts.first; while (node_curr) { diff --git a/source/blender/bmesh/intern/bmesh_edgeloop.h b/source/blender/bmesh/intern/bmesh_edgeloop.h index 527dba120e1..5df4ee5848e 100644 --- a/source/blender/bmesh/intern/bmesh_edgeloop.h +++ b/source/blender/bmesh/intern/bmesh_edgeloop.h @@ -32,17 +32,20 @@ struct ListBase; struct BMEdgeLoopStore; /* multiple edgeloops (ListBase) */ -int BM_mesh_edgeloops_find(BMesh *bm, struct ListBase *r_lb, - bool (*test_fn)(BMEdge *, void *user_data), void *user_data); -bool BM_mesh_edgeloops_find_path(BMesh *bm, ListBase *r_eloops, - bool (*test_fn)(BMEdge *, void *user_data), void *user_data, - BMVert *v_src, BMVert *v_dst); +int BM_mesh_edgeloops_find( + BMesh *bm, struct ListBase *r_lb, + bool (*test_fn)(BMEdge *, void *user_data), void *user_data); +bool BM_mesh_edgeloops_find_path( + BMesh *bm, ListBase *r_eloops, + bool (*test_fn)(BMEdge *, void *user_data), void *user_data, + BMVert *v_src, BMVert *v_dst); void BM_mesh_edgeloops_free(struct ListBase *eloops); void BM_mesh_edgeloops_calc_center(BMesh *bm, struct ListBase *eloops); void BM_mesh_edgeloops_calc_normal(BMesh *bm, struct ListBase *eloops); -void BM_mesh_edgeloops_calc_normal_aligned(BMesh *bm, struct ListBase *eloops, - const float no_align[3]); +void BM_mesh_edgeloops_calc_normal_aligned( + BMesh *bm, struct ListBase *eloops, + const float no_align[3]); void BM_mesh_edgeloops_calc_order(BMesh *UNUSED(bm), ListBase *eloops, const bool use_normals); @@ -59,8 +62,9 @@ const float *BM_edgeloop_center_get(struct BMEdgeLoopStore *el_store); void BM_edgeloop_edges_get(struct BMEdgeLoopStore *el_store, BMEdge **e_arr); void BM_edgeloop_calc_center(BMesh *bm, struct BMEdgeLoopStore *el_store); bool BM_edgeloop_calc_normal(BMesh *bm, struct BMEdgeLoopStore *el_store); -bool BM_edgeloop_calc_normal_aligned(BMesh *bm, struct BMEdgeLoopStore *el_store, - const float no_align[3]); +bool BM_edgeloop_calc_normal_aligned( + BMesh *bm, struct BMEdgeLoopStore *el_store, + const float no_align[3]); void BM_edgeloop_flip(BMesh *bm, struct BMEdgeLoopStore *el_store); void BM_edgeloop_expand(BMesh *bm, struct BMEdgeLoopStore *el_store, int el_store_len); diff --git a/source/blender/bmesh/intern/bmesh_inline.h b/source/blender/bmesh/intern/bmesh_inline.h index 96b2cd396a2..4b55060875b 100644 --- a/source/blender/bmesh/intern/bmesh_inline.h +++ b/source/blender/bmesh/intern/bmesh_inline.h @@ -39,11 +39,13 @@ #define BM_elem_flag_merge( ele_a, ele_b) _bm_elem_flag_merge (&(ele_a)->head, &(ele_b)->head) #define BM_elem_flag_merge_into(ele, ele_a, ele_b)_bm_elem_flag_merge_into (&(ele)->head, &(ele_a)->head, &(ele_b)->head) +ATTR_WARN_UNUSED_RESULT BLI_INLINE char _bm_elem_flag_test(const BMHeader *head, const char hflag) { return head->hflag & hflag; } +ATTR_WARN_UNUSED_RESULT BLI_INLINE bool _bm_elem_flag_test_bool(const BMHeader *head, const char hflag) { return (head->hflag & hflag) != 0; @@ -116,6 +118,7 @@ BLI_INLINE void _bm_elem_index_set(BMHeader *head, const int index) head->index = index; } +ATTR_WARN_UNUSED_RESULT BLI_INLINE int _bm_elem_index_get(const BMHeader *head) { return head->index; diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c index a32f28169f6..6e468bf44f2 100644 --- a/source/blender/bmesh/intern/bmesh_interp.c +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -40,39 +40,43 @@ #include "BKE_customdata.h" #include "BKE_multires.h" +#include "BLI_memarena.h" +#include "BLI_linklist.h" #include "bmesh.h" #include "intern/bmesh_private.h" /* edge and vertex share, currently theres no need to have different logic */ -static void bm_data_interp_from_elem(CustomData *data_layer, BMElem *ele1, BMElem *ele2, BMElem *ele_dst, const float fac) +static void bm_data_interp_from_elem( + CustomData *data_layer, const BMElem *ele_src_1, const BMElem *ele_src_2, + BMElem *ele_dst, const float fac) { - if (ele1->head.data && ele2->head.data) { + if (ele_src_1->head.data && ele_src_2->head.data) { /* first see if we can avoid interpolation */ if (fac <= 0.0f) { - if (ele1 == ele_dst) { + if (ele_src_1 == ele_dst) { /* do nothing */ } else { CustomData_bmesh_free_block_data(data_layer, ele_dst->head.data); - CustomData_bmesh_copy_data(data_layer, data_layer, ele1->head.data, &ele_dst->head.data); + CustomData_bmesh_copy_data(data_layer, data_layer, ele_src_1->head.data, &ele_dst->head.data); } } else if (fac >= 1.0f) { - if (ele2 == ele_dst) { + if (ele_src_2 == ele_dst) { /* do nothing */ } else { CustomData_bmesh_free_block_data(data_layer, ele_dst->head.data); - CustomData_bmesh_copy_data(data_layer, data_layer, ele2->head.data, &ele_dst->head.data); + CustomData_bmesh_copy_data(data_layer, data_layer, ele_src_2->head.data, &ele_dst->head.data); } } else { - void *src[2]; + const void *src[2]; float w[2]; - src[0] = ele1->head.data; - src[1] = ele2->head.data; + src[0] = ele_src_1->head.data; + src[1] = ele_src_2->head.data; w[0] = 1.0f - fac; w[1] = fac; CustomData_bmesh_interp(data_layer, src, w, NULL, 2, ele_dst->head.data); @@ -83,25 +87,25 @@ static void bm_data_interp_from_elem(CustomData *data_layer, BMElem *ele1, BMEle /** * \brief Data, Interp From Verts * - * Interpolates per-vertex data from two sources to a target. + * Interpolates per-vertex data from two sources to \a v_dst * * \note This is an exact match to #BM_data_interp_from_edges */ -void BM_data_interp_from_verts(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v, const float fac) +void BM_data_interp_from_verts(BMesh *bm, const BMVert *v_src_1, const BMVert *v_src_2, BMVert *v_dst, const float fac) { - bm_data_interp_from_elem(&bm->vdata, (BMElem *)v1, (BMElem *)v2, (BMElem *)v, fac); + bm_data_interp_from_elem(&bm->vdata, (const BMElem *)v_src_1, (const BMElem *)v_src_2, (BMElem *)v_dst, fac); } /** * \brief Data, Interp From Edges * - * Interpolates per-edge data from two sources to a target. + * Interpolates per-edge data from two sources to \a e_dst. * * \note This is an exact match to #BM_data_interp_from_verts */ -void BM_data_interp_from_edges(BMesh *bm, BMEdge *e1, BMEdge *e2, BMEdge *e, const float fac) +void BM_data_interp_from_edges(BMesh *bm, const BMEdge *e_src_1, const BMEdge *e_src_2, BMEdge *e_dst, const float fac) { - bm_data_interp_from_elem(&bm->edata, (BMElem *)e1, (BMElem *)e2, (BMElem *)e, fac); + bm_data_interp_from_elem(&bm->edata, (const BMElem *)e_src_1, (const BMElem *)e_src_2, (BMElem *)e_dst, fac); } /** @@ -118,26 +122,26 @@ static void UNUSED_FUNCTION(BM_Data_Vert_Average)(BMesh *UNUSED(bm), BMFace *UNU /** * \brief Data Face-Vert Edge Interp * - * Walks around the faces of an edge and interpolates the per-face-edge - * data between two sources to a target. + * Walks around the faces of \a e and interpolates + * the loop data between two sources. */ -void BM_data_interp_face_vert_edge(BMesh *bm, BMVert *v1, BMVert *UNUSED(v2), BMVert *v, BMEdge *e1, const float fac) +void BM_data_interp_face_vert_edge( + BMesh *bm, const BMVert *v_src_1, const BMVert *UNUSED(v_src_2), BMVert *v, BMEdge *e, const float fac) { - void *src[2]; float w[2]; BMLoop *l_v1 = NULL, *l_v = NULL, *l_v2 = NULL; BMLoop *l_iter = NULL; - if (!e1->l) { + if (!e->l) { return; } w[1] = 1.0f - fac; w[0] = fac; - l_iter = e1->l; + l_iter = e->l; do { - if (l_iter->v == v1) { + if (l_iter->v == v_src_1) { l_v1 = l_iter; l_v = l_v1->next; l_v2 = l_v->next; @@ -148,14 +152,17 @@ void BM_data_interp_face_vert_edge(BMesh *bm, BMVert *v1, BMVert *UNUSED(v2), BM l_v2 = l_iter->prev; } - if (!l_v1 || !l_v2) + if (!l_v1 || !l_v2) { return; - - src[0] = l_v1->head.data; - src[1] = l_v2->head.data; + } + else { + const void *src[2]; + src[0] = l_v1->head.data; + src[1] = l_v2->head.data; - CustomData_bmesh_interp(&bm->ldata, src, w, NULL, 2, l_v->head.data); - } while ((l_iter = l_iter->radial_next) != e1->l); + CustomData_bmesh_interp(&bm->ldata, src, w, NULL, 2, l_v->head.data); + } + } while ((l_iter = l_iter->radial_next) != e->l); } /** @@ -166,56 +173,57 @@ void BM_data_interp_face_vert_edge(BMesh *bm, BMVert *v1, BMVert *UNUSED(v2), BM * * \note Only handles loop customdata. multires is handled. */ -void BM_face_interp_from_face_ex(BMesh *bm, BMFace *target, BMFace *source, const bool do_vertex, - void **blocks_l, void **blocks_v, float (*cos_2d)[2], float axis_mat[3][3]) +void BM_face_interp_from_face_ex( + BMesh *bm, BMFace *f_dst, const BMFace *f_src, const bool do_vertex, + const void **blocks_l, const void **blocks_v, float (*cos_2d)[2], float axis_mat[3][3]) { BMLoop *l_iter; BMLoop *l_first; - float *w = BLI_array_alloca(w, source->len); + float *w = BLI_array_alloca(w, f_src->len); float co[2]; int i; - if (source != target) - BM_elem_attrs_copy(bm, bm, source, target); + if (f_src != f_dst) + BM_elem_attrs_copy(bm, bm, f_src, f_dst); /* interpolate */ i = 0; - l_iter = l_first = BM_FACE_FIRST_LOOP(target); + l_iter = l_first = BM_FACE_FIRST_LOOP(f_dst); do { mul_v2_m3v3(co, axis_mat, l_iter->v->co); - interp_weights_poly_v2(w, cos_2d, source->len, co); - CustomData_bmesh_interp(&bm->ldata, blocks_l, w, NULL, source->len, l_iter->head.data); + interp_weights_poly_v2(w, cos_2d, f_src->len, co); + CustomData_bmesh_interp(&bm->ldata, blocks_l, w, NULL, f_src->len, l_iter->head.data); if (do_vertex) { - CustomData_bmesh_interp(&bm->vdata, blocks_v, w, NULL, source->len, l_iter->v->head.data); + CustomData_bmesh_interp(&bm->vdata, blocks_v, w, NULL, f_src->len, l_iter->v->head.data); } } while (i++, (l_iter = l_iter->next) != l_first); } -void BM_face_interp_from_face(BMesh *bm, BMFace *target, BMFace *source, const bool do_vertex) +void BM_face_interp_from_face(BMesh *bm, BMFace *f_dst, const BMFace *f_src, const bool do_vertex) { BMLoop *l_iter; BMLoop *l_first; - void **blocks_l = BLI_array_alloca(blocks_l, source->len); - void **blocks_v = do_vertex ? BLI_array_alloca(blocks_v, source->len) : NULL; - float (*cos_2d)[2] = BLI_array_alloca(cos_2d, source->len); + const void **blocks_l = BLI_array_alloca(blocks_l, f_src->len); + const void **blocks_v = do_vertex ? BLI_array_alloca(blocks_v, f_src->len) : NULL; + float (*cos_2d)[2] = BLI_array_alloca(cos_2d, f_src->len); float axis_mat[3][3]; /* use normal to transform into 2d xy coords */ int i; /* convert the 3d coords into 2d for projection */ - BLI_assert(BM_face_is_normal_valid(source)); - axis_dominant_v3_to_m3(axis_mat, source->no); + BLI_assert(BM_face_is_normal_valid(f_src)); + axis_dominant_v3_to_m3(axis_mat, f_src->no); i = 0; - l_iter = l_first = BM_FACE_FIRST_LOOP(source); + l_iter = l_first = BM_FACE_FIRST_LOOP(f_src); do { mul_v2_m3v3(cos_2d[i], axis_mat, l_iter->v->co); blocks_l[i] = l_iter->head.data; if (do_vertex) blocks_v[i] = l_iter->v->head.data; } while (i++, (l_iter = l_iter->next) != l_first); - BM_face_interp_from_face_ex(bm, target, source, do_vertex, + BM_face_interp_from_face_ex(bm, f_dst, f_src, do_vertex, blocks_l, blocks_v, cos_2d, axis_mat); } @@ -233,8 +241,9 @@ void BM_face_interp_from_face(BMesh *bm, BMFace *target, BMFace *source, const b * y * </pre> */ -static int compute_mdisp_quad(BMLoop *l, float v1[3], float v2[3], float v3[3], float v4[3], - float e1[3], float e2[3]) +static int compute_mdisp_quad( + BMLoop *l, float v1[3], float v2[3], float v3[3], float v4[3], + float e1[3], float e2[3]) { float cent[3], n[3], p[3]; @@ -294,9 +303,10 @@ static float quad_coord(const float aa[3], const float bb[3], const float cc[3], return f1; } -static int quad_co(float *x, float *y, - const float v1[3], const float v2[3], const float v3[3], const float v4[3], - const float p[3], const float n[3]) +static int quad_co( + float *r_x, float *r_y, + const float v1[3], const float v2[3], const float v3[3], const float v4[3], + const float p[3], const float n[3]) { float projverts[5][3], n2[3]; float dprojverts[4][3], origin[3] = {0.0f, 0.0f, 0.0f}; @@ -332,14 +342,15 @@ static int quad_co(float *x, float *y, return 0; } - *y = quad_coord(dprojverts[1], dprojverts[0], dprojverts[2], dprojverts[3], 0, 1); - *x = quad_coord(dprojverts[2], dprojverts[1], dprojverts[3], dprojverts[0], 0, 1); + *r_y = quad_coord(dprojverts[1], dprojverts[0], dprojverts[2], dprojverts[3], 0, 1); + *r_x = quad_coord(dprojverts[2], dprojverts[1], dprojverts[3], dprojverts[0], 0, 1); return 1; } -static void mdisp_axis_from_quad(float v1[3], float v2[3], float UNUSED(v3[3]), float v4[3], - float axis_x[3], float axis_y[3]) +static void mdisp_axis_from_quad( + float v1[3], float v2[3], float UNUSED(v3[3]), float v4[3], + float axis_x[3], float axis_y[3]) { sub_v3_v3v3(axis_x, v4, v1); sub_v3_v3v3(axis_y, v2, v1); @@ -350,8 +361,9 @@ static void mdisp_axis_from_quad(float v1[3], float v2[3], float UNUSED(v3[3]), /* tl is loop to project onto, l is loop whose internal displacement, co, is being * projected. x and y are location in loop's mdisps grid of point co. */ -static bool mdisp_in_mdispquad(BMLoop *l, BMLoop *tl, float p[3], float *x, float *y, - int res, float axis_x[3], float axis_y[3]) +static bool mdisp_in_mdispquad( + BMLoop *l, BMLoop *tl, float p[3], float *x, float *y, + int res, float axis_x[3], float axis_y[3]) { float v1[3], v2[3], c[3], v3[3], v4[3], e1[3], e2[3]; float eps = FLT_EPSILON * 4000; @@ -384,8 +396,9 @@ static bool mdisp_in_mdispquad(BMLoop *l, BMLoop *tl, float p[3], float *x, floa return 1; } -static float bm_loop_flip_equotion(float mat[2][2], float b[2], const float target_axis_x[3], const float target_axis_y[3], - const float coord[3], int i, int j) +static float bm_loop_flip_equotion( + float mat[2][2], float b[2], const float target_axis_x[3], const float target_axis_y[3], + const float coord[3], int i, int j) { mat[0][0] = target_axis_x[i]; mat[0][1] = target_axis_y[i]; @@ -394,11 +407,12 @@ static float bm_loop_flip_equotion(float mat[2][2], float b[2], const float targ b[0] = coord[i]; b[1] = coord[j]; - return mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0]; + return cross_v2v2(mat[0], mat[1]); } -static void bm_loop_flip_disp(const float source_axis_x[3], const float source_axis_y[3], - const float target_axis_x[3], const float target_axis_y[3], float disp[3]) +static void bm_loop_flip_disp( + const float source_axis_x[3], const float source_axis_y[3], + const float target_axis_x[3], const float target_axis_y[3], float disp[3]) { float vx[3], vy[3], coord[3]; float n[3], vec[3]; @@ -425,7 +439,7 @@ static void bm_loop_flip_disp(const float source_axis_x[3], const float source_a disp[1] = (mat[0][0] * b[1] - b[0] * mat[1][0]) / d; } -static void bm_loop_interp_mdisps(BMesh *bm, BMLoop *l_dst, BMFace *f_src) +static void bm_loop_interp_mdisps(BMesh *bm, BMLoop *l_dst, const BMFace *f_src) { const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); MDisps *md_dst; @@ -445,8 +459,8 @@ static void bm_loop_interp_mdisps(BMesh *bm, BMLoop *l_dst, BMFace *f_src) /* if no disps data allocate a new grid, the size of the first grid in f_src. */ if (!md_dst->totdisp) { - MDisps *md_src = BM_ELEM_CD_GET_VOID_P(BM_FACE_FIRST_LOOP(f_src), cd_loop_mdisp_offset); - + const MDisps *md_src = BM_ELEM_CD_GET_VOID_P(BM_FACE_FIRST_LOOP(f_src), cd_loop_mdisp_offset); + md_dst->totdisp = md_src->totdisp; md_dst->level = md_src->level; if (md_dst->totdisp) { @@ -609,36 +623,37 @@ void BM_face_multires_bounds_smooth(BMesh *bm, BMFace *f) } /** - * project the multires grid in target onto source's set of multires grids + * project the multires grid in target onto f_src's set of multires grids */ -void BM_loop_interp_multires(BMesh *bm, BMLoop *target, BMFace *source) +void BM_loop_interp_multires(BMesh *bm, BMLoop *l_dst, const BMFace *f_src) { - bm_loop_interp_mdisps(bm, target, source); + bm_loop_interp_mdisps(bm, l_dst, f_src); } /** - * projects a single loop, target, onto source for customdata interpolation. multires is handled. + * projects a single loop, target, onto f_src for customdata interpolation. multires is handled. * if do_vertex is true, target's vert data will also get interpolated. */ -void BM_loop_interp_from_face(BMesh *bm, BMLoop *target, BMFace *source, - const bool do_vertex, const bool do_multires) +void BM_loop_interp_from_face( + BMesh *bm, BMLoop *l_dst, const BMFace *f_src, + const bool do_vertex, const bool do_multires) { BMLoop *l_iter; BMLoop *l_first; - void **vblocks = do_vertex ? BLI_array_alloca(vblocks, source->len) : NULL; - void **blocks = BLI_array_alloca(blocks, source->len); - float (*cos_2d)[2] = BLI_array_alloca(cos_2d, source->len); - float *w = BLI_array_alloca(w, source->len); + const void **vblocks = do_vertex ? BLI_array_alloca(vblocks, f_src->len) : NULL; + const void **blocks = BLI_array_alloca(blocks, f_src->len); + float (*cos_2d)[2] = BLI_array_alloca(cos_2d, f_src->len); + float *w = BLI_array_alloca(w, f_src->len); float axis_mat[3][3]; /* use normal to transform into 2d xy coords */ float co[2]; int i; /* convert the 3d coords into 2d for projection */ - BLI_assert(BM_face_is_normal_valid(source)); - axis_dominant_v3_to_m3(axis_mat, source->no); + BLI_assert(BM_face_is_normal_valid(f_src)); + axis_dominant_v3_to_m3(axis_mat, f_src->no); i = 0; - l_iter = l_first = BM_FACE_FIRST_LOOP(source); + l_iter = l_first = BM_FACE_FIRST_LOOP(f_src); do { mul_v2_m3v3(cos_2d[i], axis_mat, l_iter->v->co); blocks[i] = l_iter->head.data; @@ -648,48 +663,48 @@ void BM_loop_interp_from_face(BMesh *bm, BMLoop *target, BMFace *source, } } while (i++, (l_iter = l_iter->next) != l_first); - mul_v2_m3v3(co, axis_mat, target->v->co); + mul_v2_m3v3(co, axis_mat, l_dst->v->co); /* interpolate */ - interp_weights_poly_v2(w, cos_2d, source->len, co); - CustomData_bmesh_interp(&bm->ldata, blocks, w, NULL, source->len, target->head.data); + interp_weights_poly_v2(w, cos_2d, f_src->len, co); + CustomData_bmesh_interp(&bm->ldata, blocks, w, NULL, f_src->len, l_dst->head.data); if (do_vertex) { - CustomData_bmesh_interp(&bm->vdata, vblocks, w, NULL, source->len, target->v->head.data); + CustomData_bmesh_interp(&bm->vdata, vblocks, w, NULL, f_src->len, l_dst->v->head.data); } if (do_multires) { - bm_loop_interp_mdisps(bm, target, source); + bm_loop_interp_mdisps(bm, l_dst, f_src); } } -void BM_vert_interp_from_face(BMesh *bm, BMVert *v, BMFace *source) +void BM_vert_interp_from_face(BMesh *bm, BMVert *v_dst, const BMFace *f_src) { BMLoop *l_iter; BMLoop *l_first; - void **blocks = BLI_array_alloca(blocks, source->len); - float (*cos_2d)[2] = BLI_array_alloca(cos_2d, source->len); - float *w = BLI_array_alloca(w, source->len); + const void **blocks = BLI_array_alloca(blocks, f_src->len); + float (*cos_2d)[2] = BLI_array_alloca(cos_2d, f_src->len); + float *w = BLI_array_alloca(w, f_src->len); float axis_mat[3][3]; /* use normal to transform into 2d xy coords */ float co[2]; int i; /* convert the 3d coords into 2d for projection */ - BLI_assert(BM_face_is_normal_valid(source)); - axis_dominant_v3_to_m3(axis_mat, source->no); + BLI_assert(BM_face_is_normal_valid(f_src)); + axis_dominant_v3_to_m3(axis_mat, f_src->no); i = 0; - l_iter = l_first = BM_FACE_FIRST_LOOP(source); + l_iter = l_first = BM_FACE_FIRST_LOOP(f_src); do { mul_v2_m3v3(cos_2d[i], axis_mat, l_iter->v->co); blocks[i] = l_iter->v->head.data; } while (i++, (l_iter = l_iter->next) != l_first); - mul_v2_m3v3(co, axis_mat, v->co); + mul_v2_m3v3(co, axis_mat, v_dst->co); /* interpolate */ - interp_weights_poly_v2(w, cos_2d, source->len, co); - CustomData_bmesh_interp(&bm->vdata, blocks, w, NULL, source->len, v->head.data); + interp_weights_poly_v2(w, cos_2d, f_src->len, co); + CustomData_bmesh_interp(&bm->vdata, blocks, w, NULL, f_src->len, v_dst->head.data); } static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) @@ -812,6 +827,7 @@ void BM_data_layer_free(BMesh *bm, CustomData *data, int type) has_layer = CustomData_free_layer_active(data, type, 0); /* assert because its expensive to realloc - better not do if layer isnt present */ BLI_assert(has_layer != false); + UNUSED_VARS_NDEBUG(has_layer); update_data_blocks(bm, &olddata, data); if (olddata.layers) MEM_freeN(olddata.layers); @@ -831,7 +847,8 @@ void BM_data_layer_free_n(BMesh *bm, CustomData *data, int type, int n) has_layer = CustomData_free_layer(data, type, 0, CustomData_get_layer_index_n(data, type, n)); /* assert because its expensive to realloc - better not do if layer isnt present */ BLI_assert(has_layer != false); - + UNUSED_VARS_NDEBUG(has_layer); + update_data_blocks(bm, &olddata, data); if (olddata.layers) MEM_freeN(olddata.layers); } @@ -893,3 +910,249 @@ void BM_elem_float_data_set(CustomData *cd, void *element, int type, const float float *f = CustomData_bmesh_get(cd, ((BMHeader *)element)->data, type); if (f) *f = val; } + +/** \name Loop interpolation functions: BM_vert_loop_groups_data_layer_*** + * + * Handling loop custom-data such as UV's, while keeping contiguous fans is rather tedious. + * Especially when a verts loops can have multiple CustomData layers, + * and each layer can have multiple (different) contiguous fans. + * Said differently, a single vertices loops may span multiple UV islands. + * + * These functions snapshot vertices loops, storing each contiguous fan in its own group. + * The caller can manipulate the loops, then re-combine the CustomData values. + * + * While these functions don't explicitly handle multiple layers at once, + * the caller can simply store its own list. + * + * \note Currently they are averaged back together (weighted by loop angle) + * but we could copy add other methods to re-combine CustomData-Loop-Fans. + * + * \{ */ + +struct LoopWalkCtx { + /* same for all groups */ + int type; + int cd_layer_offset; + const float *loop_weights; + MemArena *arena; + + /* --- Per loop fan vars --- */ + + /* reference for this contiguous fan */ + const void *data_ref; + int data_len; + + /* accumulate 'LoopGroupCD.weight' to make unit length */ + float weight_accum; + + /* both arrays the size of the 'BM_vert_face_count(v)' + * each contiguous fan gets a slide of these arrays */ + void **data_array; + int *data_index_array; + float *weight_array; +}; + +/* Store vars to pass into 'CustomData_bmesh_interp' */ +struct LoopGroupCD { + /* direct customdata pointer array */ + void **data; + /* weights (aligned with 'data') */ + float *data_weights; + /* index-in-face */ + int *data_index; + /* number of loops in the fan */ + int data_len; +}; + +static void bm_loop_walk_add(struct LoopWalkCtx *lwc, BMLoop *l) +{ + const int i = BM_elem_index_get(l); + const float w = lwc->loop_weights[i]; + BM_elem_flag_enable(l, BM_ELEM_INTERNAL_TAG); + lwc->data_array[lwc->data_len] = BM_ELEM_CD_GET_VOID_P(l, lwc->cd_layer_offset); + lwc->data_index_array[lwc->data_len] = i; + lwc->weight_array[lwc->data_len] = w; + lwc->weight_accum += w; + + lwc->data_len += 1; +} + +/** + * called recursively, keep stack-usage minimal. + * + * \note called for fan matching so we're pretty much safe not to break the stack + */ +static void bm_loop_walk_data(struct LoopWalkCtx *lwc, BMLoop *l_walk) +{ + int i; + + BLI_assert(CustomData_data_equals(lwc->type, lwc->data_ref, BM_ELEM_CD_GET_VOID_P(l_walk, lwc->cd_layer_offset))); + BLI_assert(BM_elem_flag_test(l_walk, BM_ELEM_INTERNAL_TAG) == false); + + bm_loop_walk_add(lwc, l_walk); + + /* recurse around this loop-fan (in both directions) */ + for (i = 0; i < 2; i++) { + BMLoop *l_other = ((i == 0) ? l_walk : l_walk->prev)->radial_next; + if (l_other->radial_next != l_other) { + if (l_other->v != l_walk->v) { + l_other = l_other->next; + } + BLI_assert(l_other->v == l_walk->v); + if (!BM_elem_flag_test(l_other, BM_ELEM_INTERNAL_TAG)) { + if (CustomData_data_equals(lwc->type, lwc->data_ref, BM_ELEM_CD_GET_VOID_P(l_other, lwc->cd_layer_offset))) { + bm_loop_walk_data(lwc, l_other); + } + } + } + } +} + +LinkNode *BM_vert_loop_groups_data_layer_create( + BMesh *bm, BMVert *v, const int layer_n, const float *loop_weights, MemArena *arena) +{ + struct LoopWalkCtx lwc; + LinkNode *groups = NULL; + BMLoop *l; + BMIter liter; + int loop_num; + + + lwc.type = bm->ldata.layers[layer_n].type; + lwc.cd_layer_offset = bm->ldata.layers[layer_n].offset; + lwc.loop_weights = loop_weights; + lwc.arena = arena; + + loop_num = 0; + BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { + BM_elem_flag_disable(l, BM_ELEM_INTERNAL_TAG); + BM_elem_index_set(l, loop_num); /* set_dirty! */ + loop_num++; + } + bm->elem_index_dirty |= BM_LOOP; + + lwc.data_len = 0; + lwc.data_array = BLI_memarena_alloc(lwc.arena, sizeof(void *) * loop_num); + lwc.data_index_array = BLI_memarena_alloc(lwc.arena, sizeof(int) * loop_num); + lwc.weight_array = BLI_memarena_alloc(lwc.arena, sizeof(float) * loop_num); + + BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { + if (!BM_elem_flag_test(l, BM_ELEM_INTERNAL_TAG)) { + struct LoopGroupCD *lf = BLI_memarena_alloc(lwc.arena, sizeof(*lf)); + int len_prev = lwc.data_len; + + lwc.data_ref = BM_ELEM_CD_GET_VOID_P(l, lwc.cd_layer_offset); + + /* assign len-last */ + lf->data = &lwc.data_array[lwc.data_len]; + lf->data_index = &lwc.data_index_array[lwc.data_len]; + lf->data_weights = &lwc.weight_array[lwc.data_len]; + lwc.weight_accum = 0.0f; + + /* new group */ + bm_loop_walk_data(&lwc, l); + lf->data_len = lwc.data_len - len_prev; + + if (LIKELY(lwc.weight_accum != 0.0f)) { + mul_vn_fl(lf->data_weights, lf->data_len, 1.0f / lwc.weight_accum); + } + else { + copy_vn_fl(lf->data_weights, lf->data_len, 1.0f / (float)lf->data_len); + } + + BLI_linklist_prepend_arena(&groups, lf, lwc.arena); + } + } + + BLI_assert(lwc.data_len == loop_num); + + return groups; +} + +static void bm_vert_loop_groups_data_layer_merge__single( + BMesh *bm, void *lf_p, int layer_n, + void *data_tmp) +{ + struct LoopGroupCD *lf = lf_p; + const int type = bm->ldata.layers[layer_n].type; + int i; + const float *data_weights; + + data_weights = lf->data_weights; + + CustomData_bmesh_interp_n( + &bm->ldata, (const void **)lf->data, + data_weights, NULL, lf->data_len, data_tmp, layer_n); + + for (i = 0; i < lf->data_len; i++) { + CustomData_copy_elements(type, data_tmp, lf->data[i], 1); + } +} + +static void bm_vert_loop_groups_data_layer_merge_weights__single( + BMesh *bm, void *lf_p, const int layer_n, void *data_tmp, + const float *loop_weights) +{ + struct LoopGroupCD *lf = lf_p; + const int type = bm->ldata.layers[layer_n].type; + int i; + const float *data_weights; + + /* re-weight */ + float *temp_weights = BLI_array_alloca(temp_weights, lf->data_len); + float weight_accum = 0.0f; + + for (i = 0; i < lf->data_len; i++) { + float w = loop_weights[lf->data_index[i]] * lf->data_weights[i]; + temp_weights[i] = w; + weight_accum += w; + } + + if (LIKELY(weight_accum != 0.0f)) { + mul_vn_fl(temp_weights, lf->data_len, 1.0f / weight_accum); + data_weights = temp_weights; + } + else { + data_weights = lf->data_weights; + } + + CustomData_bmesh_interp_n( + &bm->ldata, (const void **)lf->data, + data_weights, NULL, lf->data_len, data_tmp, layer_n); + + for (i = 0; i < lf->data_len; i++) { + CustomData_copy_elements(type, data_tmp, lf->data[i], 1); + } +} + +/** + * Take existing custom data and merge each fan's data. + */ +void BM_vert_loop_groups_data_layer_merge(BMesh *bm, LinkNode *groups, const int layer_n) +{ + const int type = bm->ldata.layers[layer_n].type; + const int size = CustomData_sizeof(type); + void *data_tmp = alloca(size); + + do { + bm_vert_loop_groups_data_layer_merge__single(bm, groups->link, layer_n, data_tmp); + } while ((groups = groups->next)); +} + +/** + * A version of #BM_vert_loop_groups_data_layer_merge + * that takes an array of loop-weights (aligned with #BM_LOOPS_OF_VERT iterator) + */ +void BM_vert_loop_groups_data_layer_merge_weights( + BMesh *bm, LinkNode *groups, const int layer_n, const float *loop_weights) +{ + const int type = bm->ldata.layers[layer_n].type; + const int size = CustomData_sizeof(type); + void *data_tmp = alloca(size); + + do { + bm_vert_loop_groups_data_layer_merge_weights__single(bm, groups->link, layer_n, data_tmp, loop_weights); + } while ((groups = groups->next)); +} + +/** \} */ diff --git a/source/blender/bmesh/intern/bmesh_interp.h b/source/blender/bmesh/intern/bmesh_interp.h index c605ad31ae7..969e92f37db 100644 --- a/source/blender/bmesh/intern/bmesh_interp.h +++ b/source/blender/bmesh/intern/bmesh_interp.h @@ -27,12 +27,15 @@ * \ingroup bmesh */ -void BM_loop_interp_multires(BMesh *bm, BMLoop *target, BMFace *source); -void BM_vert_interp_from_face(BMesh *bm, BMVert *v, BMFace *source); +struct LinkNode; +struct MemArena; -void BM_data_interp_from_verts(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v, const float fac); -void BM_data_interp_from_edges(BMesh *bm, BMEdge *e1, BMEdge *e2, BMEdge *e, const float fac); -void BM_data_interp_face_vert_edge(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v, BMEdge *e1, const float fac); +void BM_loop_interp_multires(BMesh *bm, BMLoop *l_dst, const BMFace *f_src); +void BM_vert_interp_from_face(BMesh *bm, BMVert *v_dst, const BMFace *f_src); + +void BM_data_interp_from_verts(BMesh *bm, const BMVert *v_src_1, const BMVert *v_src_2, BMVert *v_dst, const float fac); +void BM_data_interp_from_edges(BMesh *bm, const BMEdge *e_src_1, const BMEdge *e_src_2, BMEdge *e_dst, const float fac); +void BM_data_interp_face_vert_edge(BMesh *bm, const BMVert *v_src_1, const BMVert *v_src_2, BMVert *v, BMEdge *e, const float fac); void BM_data_layer_add(BMesh *bm, CustomData *data, int type); void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char *name); void BM_data_layer_free(BMesh *bm, CustomData *data, int type); @@ -42,12 +45,26 @@ void BM_data_layer_copy(BMesh *bm, CustomData *data, int type, int src_n, int d float BM_elem_float_data_get(CustomData *cd, void *element, int type); void BM_elem_float_data_set(CustomData *cd, void *element, int type, const float val); -void BM_face_interp_from_face_ex(BMesh *bm, BMFace *target, BMFace *source, const bool do_vertex, - void **blocks, void **blocks_v, float (*cos_2d)[2], float axis_mat[3][3]); -void BM_face_interp_from_face(BMesh *bm, BMFace *target, BMFace *source, const bool do_vertex); -void BM_loop_interp_from_face(BMesh *bm, BMLoop *target, BMFace *source, - const bool do_vertex, const bool do_multires); +void BM_face_interp_from_face_ex( + BMesh *bm, BMFace *f_dst, const BMFace *f_src, const bool do_vertex, + const void **blocks, const void **blocks_v, + float (*cos_2d)[2], float axis_mat[3][3]); +void BM_face_interp_from_face( + BMesh *bm, BMFace *f_dst, const BMFace *f_src, + const bool do_vertex); +void BM_loop_interp_from_face( + BMesh *bm, BMLoop *l_dst, const BMFace *f_src, + const bool do_vertex, const bool do_multires); void BM_face_multires_bounds_smooth(BMesh *bm, BMFace *f); +struct LinkNode *BM_vert_loop_groups_data_layer_create( + BMesh *bm, BMVert *v, const int layer_n, + const float *loop_weights, struct MemArena *arena); +void BM_vert_loop_groups_data_layer_merge( + BMesh *bm, struct LinkNode *groups, const int layer_n); +void BM_vert_loop_groups_data_layer_merge_weights( + BMesh *bm, struct LinkNode *groups, const int layer_n, + const float *loop_weights); + #endif /* __BMESH_INTERP_H__ */ diff --git a/source/blender/bmesh/intern/bmesh_iterators.c b/source/blender/bmesh/intern/bmesh_iterators.c index 476878ad38c..0abf41709a0 100644 --- a/source/blender/bmesh/intern/bmesh_iterators.c +++ b/source/blender/bmesh/intern/bmesh_iterators.c @@ -56,7 +56,7 @@ const char bm_iter_itype_htype_map[BM_ITYPE_MAX] = { /** * Utility function. */ -int BM_iter_mesh_count(BMesh *bm, const char itype) +int BM_iter_mesh_count(const char itype, BMesh *bm) { int count; @@ -136,8 +136,9 @@ int BM_iter_as_array(BMesh *bm, const char itype, void *data, void **array, cons * * Sometimes its convenient to get the iterator as an array. */ -int BMO_iter_as_array(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, const char restrictmask, - void **array, const int len) +int BMO_iter_as_array( + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, const char restrictmask, + void **array, const int len) { int i = 0; @@ -169,9 +170,10 @@ int BMO_iter_as_array(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_nam * * Caller needs to free the array. */ -void *BM_iter_as_arrayN(BMesh *bm, const char itype, void *data, int *r_len, - /* optional args to avoid an alloc (normally stack array) */ - void **stack_array, int stack_array_size) +void *BM_iter_as_arrayN( + BMesh *bm, const char itype, void *data, int *r_len, + /* optional args to avoid an alloc (normally stack array) */ + void **stack_array, int stack_array_size) { BMIter iter; @@ -212,10 +214,11 @@ void *BM_iter_as_arrayN(BMesh *bm, const char itype, void *data, int *r_len, } } -void *BMO_iter_as_arrayN(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, const char restrictmask, - int *r_len, - /* optional args to avoid an alloc (normally stack array) */ - void **stack_array, int stack_array_size) +void *BMO_iter_as_arrayN( + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, const char restrictmask, + int *r_len, + /* optional args to avoid an alloc (normally stack array) */ + void **stack_array, int stack_array_size) { BMOIter iter; BMElem *ele; @@ -273,8 +276,9 @@ int BM_iter_elem_count_flag(const char itype, void *data, const char hflag, cons * * Counts how many flagged / unflagged items are found in this element. */ -int BMO_iter_elem_count_flag(BMesh *bm, const char itype, void *data, - const short oflag, const bool value) +int BMO_iter_elem_count_flag( + BMesh *bm, const char itype, void *data, + const short oflag, const bool value) { BMIter iter; BMElemF *ele; diff --git a/source/blender/bmesh/intern/bmesh_iterators.h b/source/blender/bmesh/intern/bmesh_iterators.h index 44be7072e71..c4b184ef8b8 100644 --- a/source/blender/bmesh/intern/bmesh_iterators.h +++ b/source/blender/bmesh/intern/bmesh_iterators.h @@ -84,30 +84,40 @@ typedef enum BMIterType { extern const char bm_iter_itype_htype_map[BM_ITYPE_MAX]; #define BM_ITER_MESH(ele, iter, bm, itype) \ - for (ele = BM_iter_new(iter, bm, itype, NULL); ele; ele = BM_iter_step(iter)) + for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_new(iter, bm, itype, NULL); \ + ele; \ + BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter)) #define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar) \ - for (ele = BM_iter_new(iter, bm, itype, NULL), indexvar = 0; ele; ele = BM_iter_step(iter), (indexvar)++) + for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_new(iter, bm, itype, NULL), indexvar = 0; \ + ele; \ + BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter), (indexvar)++) /* a version of BM_ITER_MESH which keeps the next item in storage * so we can delete the current item, see bug [#36923] */ #ifdef DEBUG # define BM_ITER_MESH_MUTABLE(ele, ele_next, iter, bm, itype) \ - for (ele = BM_iter_new(iter, bm, itype, NULL); \ - ele ? ((void)((iter)->count = BM_iter_mesh_count(bm, itype)), \ - (void)(ele_next = BM_iter_step(iter)), 1) : 0; \ - ele = ele_next) + for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_new(iter, bm, itype, NULL); \ + ele ? ((void)((iter)->count = BM_iter_mesh_count(itype, bm)), \ + (void)(ele_next = BM_iter_step(iter)), 1) : 0; \ + BM_CHECK_TYPE_ELEM_ASSIGN(ele) = ele_next) #else # define BM_ITER_MESH_MUTABLE(ele, ele_next, iter, bm, itype) \ - for (ele = BM_iter_new(iter, bm, itype, NULL); ele ? ((ele_next = BM_iter_step(iter)), 1) : 0; ele = ele_next) + for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_new(iter, bm, itype, NULL); \ + ele ? ((BM_CHECK_TYPE_ELEM_ASSIGN(ele_next) = BM_iter_step(iter)), 1) : 0; \ + ele = ele_next) #endif #define BM_ITER_ELEM(ele, iter, data, itype) \ - for (ele = BM_iter_new(iter, NULL, itype, data); ele; ele = BM_iter_step(iter)) + for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_new(iter, NULL, itype, data); \ + ele; \ + BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter)) #define BM_ITER_ELEM_INDEX(ele, iter, data, itype, indexvar) \ - for (ele = BM_iter_new(iter, NULL, itype, data), indexvar = 0; ele; ele = BM_iter_step(iter), (indexvar)++) + for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_new(iter, NULL, itype, data), indexvar = 0; \ + ele; \ + BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter), (indexvar)++) /* iterator type structs */ struct BMIter__elem_of_mesh { @@ -185,20 +195,22 @@ typedef struct BMIter { char itype; } BMIter; -int BM_iter_mesh_count(BMesh *bm, const char itype); void *BM_iter_at_index(BMesh *bm, const char itype, void *data, int index) ATTR_WARN_UNUSED_RESULT; int BM_iter_as_array(BMesh *bm, const char itype, void *data, void **array, const int len); -void *BM_iter_as_arrayN(BMesh *bm, const char itype, void *data, int *r_len, - void **stack_array, int stack_array_size) ATTR_WARN_UNUSED_RESULT; -int BMO_iter_as_array(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, const char restrictmask, - void **array, const int len); -void *BMO_iter_as_arrayN(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, const char restrictmask, - int *r_len, - /* optional args to avoid an alloc (normally stack array) */ - void **stack_array, int stack_array_size); - +void *BM_iter_as_arrayN( + BMesh *bm, const char itype, void *data, int *r_len, + void **stack_array, int stack_array_size) ATTR_WARN_UNUSED_RESULT; +int BMO_iter_as_array( + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, const char restrictmask, + void **array, const int len); +void *BMO_iter_as_arrayN( + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, const char restrictmask, + int *r_len, + /* optional args to avoid an alloc (normally stack array) */ + void **stack_array, int stack_array_size); int BM_iter_elem_count_flag(const char itype, void *data, const char hflag, const bool value); int BMO_iter_elem_count_flag(BMesh *bm, const char itype, void *data, const short oflag, const bool value); +int BM_iter_mesh_count(const char itype, BMesh *bm); int BM_iter_mesh_count_flag(const char itype, BMesh *bm, const char hflag, const bool value); /* private for bmesh_iterators_inline.c */ diff --git a/source/blender/bmesh/intern/bmesh_iterators_inline.h b/source/blender/bmesh/intern/bmesh_iterators_inline.h index d3e18b97acb..e68440021e6 100644 --- a/source/blender/bmesh/intern/bmesh_iterators_inline.h +++ b/source/blender/bmesh/intern/bmesh_iterators_inline.h @@ -37,6 +37,7 @@ * * Calls an iterators step function to return the next element. */ +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE void *BM_iter_step(BMIter *iter) { return iter->step(iter); @@ -50,6 +51,7 @@ BLI_INLINE void *BM_iter_step(BMIter *iter) * it with the appropriate function pointers based * upon its type. */ +ATTR_NONNULL(1) BLI_INLINE bool BM_iter_init(BMIter *iter, BMesh *bm, const char itype, void *data) { /* int argtype; */ @@ -169,6 +171,7 @@ BLI_INLINE bool BM_iter_init(BMIter *iter, BMesh *bm, const char itype, void *da * to return the first element of the iterator. * */ +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE void *BM_iter_new(BMIter *iter, BMesh *bm, const char itype, void *data) { if (LIKELY(BM_iter_init(iter, bm, itype, data))) { diff --git a/source/blender/bmesh/intern/bmesh_log.c b/source/blender/bmesh/intern/bmesh_log.c index 0bb1a892ffe..1f64f7b74cc 100644 --- a/source/blender/bmesh/intern/bmesh_log.c +++ b/source/blender/bmesh/intern/bmesh_log.c @@ -115,8 +115,8 @@ struct BMLog { typedef struct { float co[3]; short no[3]; - float mask; char hflag; + float mask; } BMLogVert; typedef struct { @@ -126,6 +126,10 @@ typedef struct { /************************* Get/set element IDs ************************/ +/* bypass actual hashing, the keys don't overlap */ +#define logkey_hash BLI_ghashutil_inthash_p_simple +#define logkey_cmp BLI_ghashutil_intcmp + /* Get the vertex's unique ID from the log */ static unsigned int bm_log_vert_id_get(BMLog *log, BMVert *v) { @@ -386,12 +390,12 @@ static BMLogEntry *bm_log_entry_create(void) { BMLogEntry *entry = MEM_callocN(sizeof(BMLogEntry), __func__); - entry->deleted_verts = BLI_ghash_ptr_new(__func__); - entry->deleted_faces = BLI_ghash_ptr_new(__func__); - entry->added_verts = BLI_ghash_ptr_new(__func__); - entry->added_faces = BLI_ghash_ptr_new(__func__); - entry->modified_verts = BLI_ghash_ptr_new(__func__); - entry->modified_faces = BLI_ghash_ptr_new(__func__); + entry->deleted_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + entry->deleted_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + entry->added_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + entry->added_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + entry->modified_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + entry->modified_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); entry->pool_verts = BLI_mempool_create(sizeof(BMLogVert), 0, 64, BLI_MEMPOOL_NOP); entry->pool_faces = BLI_mempool_create(sizeof(BMLogFace), 0, 64, BLI_MEMPOOL_NOP); @@ -423,9 +427,7 @@ static void bm_log_id_ghash_retake(RangeTreeUInt *unused_ids, GHash *id_ghash) void *key = BLI_ghashIterator_getKey(&gh_iter); unsigned int id = GET_UINT_FROM_POINTER(key); - if (range_tree_uint_has(unused_ids, id)) { - range_tree_uint_take(unused_ids, id); - } + range_tree_uint_retake(unused_ids, id); } } @@ -478,10 +480,11 @@ static void bm_log_id_ghash_release(BMLog *log, GHash *id_ghash) BMLog *BM_log_create(BMesh *bm) { BMLog *log = MEM_callocN(sizeof(*log), __func__); + const unsigned int reserve_num = (unsigned int)(bm->totvert + bm->totface); log->unused_ids = range_tree_uint_alloc(0, (unsigned)-1); - log->id_to_elem = BLI_ghash_ptr_new_ex(__func__, (unsigned int)(bm->totvert + bm->totface)); - log->elem_to_id = BLI_ghash_ptr_new_ex(__func__, (unsigned int)(bm->totvert + bm->totface)); + log->id_to_elem = BLI_ghash_new_ex(logkey_hash, logkey_cmp, __func__, reserve_num); + log->elem_to_id = BLI_ghash_ptr_new_ex(__func__, reserve_num); /* Assign IDs to all existing vertices and faces */ bm_log_assign_ids(bm, log); @@ -584,14 +587,14 @@ void BM_log_free(BMLog *log) /* Get the number of log entries */ int BM_log_length(const BMLog *log) { - return BLI_countlist(&log->entries); + return BLI_listbase_count(&log->entries); } /* Apply a consistent ordering to BMesh vertices */ void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log) { - void *varr; - void *farr; + unsigned int *varr; + unsigned int *farr; GHash *id_to_idx; @@ -599,41 +602,37 @@ void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log) BMVert *v; BMFace *f; - int i; + unsigned int i; /* Put all vertex IDs into an array */ - i = 0; varr = MEM_mallocN(sizeof(int) * (size_t)bm->totvert, __func__); - BM_ITER_MESH (v, &bm_iter, bm, BM_VERTS_OF_MESH) { - ((unsigned int *)varr)[i++] = bm_log_vert_id_get(log, v); + BM_ITER_MESH_INDEX (v, &bm_iter, bm, BM_VERTS_OF_MESH, i) { + varr[i] = bm_log_vert_id_get(log, v); } /* Put all face IDs into an array */ - i = 0; farr = MEM_mallocN(sizeof(int) * (size_t)bm->totface, __func__); - BM_ITER_MESH (f, &bm_iter, bm, BM_FACES_OF_MESH) { - ((unsigned int *)farr)[i++] = bm_log_face_id_get(log, f); + BM_ITER_MESH_INDEX (f, &bm_iter, bm, BM_FACES_OF_MESH, i) { + farr[i] = bm_log_face_id_get(log, f); } /* Create BMVert index remap array */ id_to_idx = bm_log_compress_ids_to_indices(varr, (unsigned int)bm->totvert); - i = 0; - BM_ITER_MESH (v, &bm_iter, bm, BM_VERTS_OF_MESH) { + BM_ITER_MESH_INDEX (v, &bm_iter, bm, BM_VERTS_OF_MESH, i) { const unsigned id = bm_log_vert_id_get(log, v); const void *key = SET_UINT_IN_POINTER(id); const void *val = BLI_ghash_lookup(id_to_idx, key); - ((unsigned int *)varr)[i++] = GET_UINT_FROM_POINTER(val); + varr[i] = GET_UINT_FROM_POINTER(val); } BLI_ghash_free(id_to_idx, NULL, NULL); /* Create BMFace index remap array */ id_to_idx = bm_log_compress_ids_to_indices(farr, (unsigned int)bm->totface); - i = 0; - BM_ITER_MESH (f, &bm_iter, bm, BM_FACES_OF_MESH) { + BM_ITER_MESH_INDEX (f, &bm_iter, bm, BM_FACES_OF_MESH, i) { const unsigned id = bm_log_face_id_get(log, f); const void *key = SET_UINT_IN_POINTER(id); const void *val = BLI_ghash_lookup(id_to_idx, key); - ((unsigned int *)farr)[i++] = GET_UINT_FROM_POINTER(val); + farr[i] = GET_UINT_FROM_POINTER(val); } BLI_ghash_free(id_to_idx, NULL, NULL); @@ -838,14 +837,15 @@ void BM_log_vert_before_modified(BMLog *log, BMVert *v, const int cd_vert_mask_o BMLogVert *lv; unsigned int v_id = bm_log_vert_id_get(log, v); void *key = SET_UINT_IN_POINTER(v_id); + void **val_p; /* Find or create the BMLogVert entry */ if ((lv = BLI_ghash_lookup(entry->added_verts, key))) { bm_log_vert_bmvert_copy(lv, v, cd_vert_mask_offset); } - else if (!BLI_ghash_haskey(entry->modified_verts, key)) { + else if (!BLI_ghash_ensure_p(entry->modified_verts, key, &val_p)) { lv = bm_log_vert_alloc(log, v, cd_vert_mask_offset); - BLI_ghash_insert(entry->modified_verts, key, lv); + *val_p = lv; } } @@ -989,6 +989,15 @@ void BM_log_all_added(BMesh *bm, BMLog *log) BMVert *v; BMFace *f; + /* avoid unnecessary resizing on initialization */ + if (BLI_ghash_size(log->current_entry->added_verts) == 0) { + BLI_ghash_reserve(log->current_entry->added_verts, (unsigned int)bm->totvert); + } + + if (BLI_ghash_size(log->current_entry->added_faces) == 0) { + BLI_ghash_reserve(log->current_entry->added_faces, (unsigned int)bm->totface); + } + /* Log all vertices as newly created */ BM_ITER_MESH (v, &bm_iter, bm, BM_VERTS_OF_MESH) { BM_log_vert_added(log, v, cd_vert_mask_offset); @@ -1073,6 +1082,24 @@ float BM_log_original_mask(BMLog *log, BMVert *v) return lv->mask; } +void BM_log_original_vert_data( + BMLog *log, BMVert *v, + const float **r_co, const short **r_no) +{ + BMLogEntry *entry = log->current_entry; + const BMLogVert *lv; + unsigned v_id = bm_log_vert_id_get(log, v); + void *key = SET_UINT_IN_POINTER(v_id); + + BLI_assert(entry); + + BLI_assert(BLI_ghash_haskey(entry->modified_verts, key)); + + lv = BLI_ghash_lookup(entry->modified_verts, key); + *r_co = lv->co; + *r_no = lv->no; +} + /************************ Debugging and Testing ***********************/ /* For internal use only (unit testing) */ diff --git a/source/blender/bmesh/intern/bmesh_log.h b/source/blender/bmesh/intern/bmesh_log.h index 2147b5c64b4..dd1772af068 100644 --- a/source/blender/bmesh/intern/bmesh_log.h +++ b/source/blender/bmesh/intern/bmesh_log.h @@ -96,6 +96,11 @@ const short *BM_log_original_vert_no(BMLog *log, BMVert *v); /* Get the logged mask of a vertex */ float BM_log_original_mask(BMLog *log, BMVert *v); +/* Get the logged data of a vertex (avoid multiple lookups) */ +void BM_log_original_vert_data( + BMLog *log, BMVert *v, + const float **r_co, const short **r_no); + /* For internal use only (unit testing) */ BMLogEntry *BM_log_current_entry(BMLog *log); struct RangeTreeUInt *BM_log_unused_ids(BMLog *log); diff --git a/source/blender/bmesh/intern/bmesh_marking.c b/source/blender/bmesh/intern/bmesh_marking.c index ee35d8cd1d2..17b6d1d99e7 100644 --- a/source/blender/bmesh/intern/bmesh_marking.c +++ b/source/blender/bmesh/intern/bmesh_marking.c @@ -41,6 +41,7 @@ #include "BLI_listbase.h" #include "bmesh.h" +#include "bmesh_structure.h" static void recount_totsels(BMesh *bm) { @@ -69,6 +70,69 @@ static void recount_totsels(BMesh *bm) } } +/** \name BMesh helper functions for selection flushing. + * \{ */ + +static bool bm_vert_is_edge_select_any_other(const BMVert *v, const BMEdge *e_first) +{ + const BMEdge *e_iter = e_first; + + /* start by stepping over the current edge */ + while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first) { + if (BM_elem_flag_test(e_iter, BM_ELEM_SELECT)) { + return true; + } + } + return false; +} + +#if 0 +static bool bm_vert_is_edge_select_any(const BMVert *v) +{ + if (v->e) { + const BMEdge *e_iter, *e_first; + e_iter = e_first = v->e; + do { + if (BM_elem_flag_test(e_iter, BM_ELEM_SELECT)) { + return true; + } + } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first); + } + return false; +} +#endif + +static bool bm_edge_is_face_select_any_other(BMLoop *l_first) +{ + const BMLoop *l_iter = l_first; + + /* start by stepping over the current face */ + while ((l_iter = l_iter->radial_next) != l_first) { + if (BM_elem_flag_test(l_iter->f, BM_ELEM_SELECT)) { + return true; + } + } + return false; +} + +#if 0 +static bool bm_edge_is_face_select_any(const BMEdge *e) +{ + if (e->l) { + const BMLoop *l_iter, *l_first; + l_iter = l_first = e->l; + do { + if (BM_elem_flag_test(l_iter->f, BM_ELEM_SELECT)) { + return true; + } + } while ((l_iter = l_iter->radial_next) != l_first); + } + return false; +} +#endif + +/** \} */ + /** * \brief Select Mode Clean * @@ -341,8 +405,8 @@ void BM_vert_select_set(BMesh *bm, BMVert *v, const bool select) if (select) { if (!BM_elem_flag_test(v, BM_ELEM_SELECT)) { - bm->totvertsel += 1; BM_elem_flag_enable(v, BM_ELEM_SELECT); + bm->totvertsel += 1; } } else { @@ -367,39 +431,27 @@ void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select) } if (select) { - if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) bm->totedgesel += 1; - - BM_elem_flag_enable(e, BM_ELEM_SELECT); + if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) { + BM_elem_flag_enable(e, BM_ELEM_SELECT); + bm->totedgesel += 1; + } BM_vert_select_set(bm, e->v1, true); BM_vert_select_set(bm, e->v2, true); } else { - if (BM_elem_flag_test(e, BM_ELEM_SELECT)) bm->totedgesel -= 1; - BM_elem_flag_disable(e, BM_ELEM_SELECT); + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { + BM_elem_flag_disable(e, BM_ELEM_SELECT); + bm->totedgesel -= 1; + } if ((bm->selectmode & SCE_SELECT_VERTEX) == 0) { - BMIter iter; - BMVert *verts[2] = {e->v1, e->v2}; - BMEdge *e2; int i; /* check if the vert is used by a selected edge */ for (i = 0; i < 2; i++) { - bool deselect = true; - - for (e2 = BM_iter_new(&iter, bm, BM_EDGES_OF_VERT, verts[i]); e2; e2 = BM_iter_step(&iter)) { - if (e2 == e) { - continue; - } - - if (BM_elem_flag_test(e2, BM_ELEM_SELECT)) { - deselect = false; - break; - } - } - - if (deselect) { - BM_vert_select_set(bm, verts[i], false); + BMVert *v = *((&e->v1) + i); + if (bm_vert_is_edge_select_any_other(v, e) == false) { + BM_vert_select_set(bm, v, false); } } } @@ -430,10 +482,10 @@ void BM_face_select_set(BMesh *bm, BMFace *f, const bool select) if (select) { if (!BM_elem_flag_test(f, BM_ELEM_SELECT)) { - bm->totfacesel++; + BM_elem_flag_enable(f, BM_ELEM_SELECT); + bm->totfacesel += 1; } - BM_elem_flag_enable(f, BM_ELEM_SELECT); l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { BM_vert_select_set(bm, l_iter->v, true); @@ -441,42 +493,80 @@ void BM_face_select_set(BMesh *bm, BMFace *f, const bool select) } while ((l_iter = l_iter->next) != l_first); } else { - BMIter liter; - BMLoop *l; - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) bm->totfacesel -= 1; - BM_elem_flag_disable(f, BM_ELEM_SELECT); + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + BM_elem_flag_disable(f, BM_ELEM_SELECT); + bm->totfacesel -= 1; + } /* flush down to edges */ - BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - BMIter fiter; - BMFace *f2; - BM_ITER_ELEM (f2, &fiter, l->e, BM_FACES_OF_EDGE) { - if (BM_elem_flag_test(f2, BM_ELEM_SELECT)) - break; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + /* vertex flushing is handled below */ + if (bm_edge_is_face_select_any_other(l_iter) == false) { + BM_edge_select_set_noflush(bm, l_iter->e, false); } + } while ((l_iter = l_iter->next) != l_first); - if (!f2) { - BM_edge_select_set(bm, l->e, false); + /* flush down to verts */ + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (bm_vert_is_edge_select_any_other(l_iter->v, l_iter->e) == false) { + BM_vert_select_set(bm, l_iter->v, false); } + } while ((l_iter = l_iter->next) != l_first); + } +} + +/** \name Non flushing versions element selection. + * \{ */ + +void BM_edge_select_set_noflush(BMesh *bm, BMEdge *e, const bool select) +{ + BLI_assert(e->head.htype == BM_EDGE); + + if (BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { + return; + } + + if (select) { + if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) { + BM_elem_flag_enable(e, BM_ELEM_SELECT); + bm->totedgesel += 1; + } + } + else { + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { + BM_elem_flag_disable(e, BM_ELEM_SELECT); + bm->totedgesel -= 1; } + } +} - /* flush down to verts */ - BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - BMIter eiter; - BMEdge *e; - BM_ITER_ELEM (e, &eiter, l->v, BM_EDGES_OF_VERT) { - if (BM_elem_flag_test(e, BM_ELEM_SELECT)) - break; - } +void BM_face_select_set_noflush(BMesh *bm, BMFace *f, const bool select) +{ + BLI_assert(f->head.htype == BM_FACE); - if (!e) { - BM_vert_select_set(bm, l->v, false); - } + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + return; + } + + if (select) { + if (!BM_elem_flag_test(f, BM_ELEM_SELECT)) { + BM_elem_flag_enable(f, BM_ELEM_SELECT); + bm->totfacesel += 1; + } + } + else { + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + BM_elem_flag_disable(f, BM_ELEM_SELECT); + bm->totfacesel -= 1; } } } +/** \} */ + /** * Select Mode Set * @@ -536,8 +626,9 @@ void BM_mesh_select_mode_set(BMesh *bm, int selectmode) /** * counts number of elements with flag enabled/disabled */ -static int bm_mesh_flag_count(BMesh *bm, const char htype, const char hflag, - const bool respecthide, const bool test_for_enabled) +static int bm_mesh_flag_count( + BMesh *bm, const char htype, const char hflag, + const bool respecthide, const bool test_for_enabled) { BMElem *ele; BMIter iter; @@ -823,6 +914,12 @@ void _bm_select_history_store_notest(BMesh *bm, BMHeader *ele) BLI_addtail(&(bm->selected), ese); } +void _bm_select_history_store_head_notest(BMesh *bm, BMHeader *ele) +{ + BMEditSelection *ese = bm_select_history_create(ele); + BLI_addhead(&(bm->selected), ese); +} + void _bm_select_history_store(BMesh *bm, BMHeader *ele) { if (!BM_select_history_check(bm, (BMElem *)ele)) { @@ -830,6 +927,12 @@ void _bm_select_history_store(BMesh *bm, BMHeader *ele) } } +void _bm_select_history_store_head(BMesh *bm, BMHeader *ele) +{ + if (!BM_select_history_check(bm, (BMElem *)ele)) { + BM_select_history_store_head_notest(bm, (BMElem *)ele); + } +} void _bm_select_history_store_after_notest(BMesh *bm, BMEditSelection *ese_ref, BMHeader *ele) { @@ -923,8 +1026,9 @@ GHash *BM_select_history_map_create(BMesh *bm) return map; } -void BM_mesh_elem_hflag_disable_test(BMesh *bm, const char htype, const char hflag, - const bool respecthide, const bool overwrite, const char hflag_test) +void BM_mesh_elem_hflag_disable_test( + BMesh *bm, const char htype, const char hflag, + const bool respecthide, const bool overwrite, const char hflag_test) { const char iter_types[3] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, @@ -994,8 +1098,9 @@ void BM_mesh_elem_hflag_disable_test(BMesh *bm, const char htype, const char hfl } } -void BM_mesh_elem_hflag_enable_test(BMesh *bm, const char htype, const char hflag, - const bool respecthide, const bool overwrite, const char hflag_test) +void BM_mesh_elem_hflag_enable_test( + BMesh *bm, const char htype, const char hflag, + const bool respecthide, const bool overwrite, const char hflag_test) { const char iter_types[3] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, @@ -1049,15 +1154,17 @@ void BM_mesh_elem_hflag_enable_test(BMesh *bm, const char htype, const char hfla } } -void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, - const bool respecthide) +void BM_mesh_elem_hflag_disable_all( + BMesh *bm, const char htype, const char hflag, + const bool respecthide) { /* call with 0 hflag_test */ BM_mesh_elem_hflag_disable_test(bm, htype, hflag, respecthide, false, 0); } -void BM_mesh_elem_hflag_enable_all(BMesh *bm, const char htype, const char hflag, - const bool respecthide) +void BM_mesh_elem_hflag_enable_all( + BMesh *bm, const char htype, const char hflag, + const bool respecthide) { /* call with 0 hflag_test */ BM_mesh_elem_hflag_enable_test(bm, htype, hflag, respecthide, false, 0); diff --git a/source/blender/bmesh/intern/bmesh_marking.h b/source/blender/bmesh/intern/bmesh_marking.h index 9e0c0923164..4730af66a74 100644 --- a/source/blender/bmesh/intern/bmesh_marking.h +++ b/source/blender/bmesh/intern/bmesh_marking.h @@ -43,15 +43,19 @@ void BM_face_hide_set(BMFace *f, const bool hide); /* Selection code */ void BM_elem_select_set(BMesh *bm, BMElem *ele, const bool select); -void BM_mesh_elem_hflag_enable_test(BMesh *bm, const char htype, const char hflag, - const bool respecthide, const bool overwrite, const char hflag_test); -void BM_mesh_elem_hflag_disable_test(BMesh *bm, const char htype, const char hflag, - const bool respecthide, const bool overwrite, const char hflag_test); - -void BM_mesh_elem_hflag_enable_all(BMesh *bm, const char htype, const char hflag, - const bool respecthide); -void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, - const bool respecthide); +void BM_mesh_elem_hflag_enable_test( + BMesh *bm, const char htype, const char hflag, + const bool respecthide, const bool overwrite, const char hflag_test); +void BM_mesh_elem_hflag_disable_test( + BMesh *bm, const char htype, const char hflag, + const bool respecthide, const bool overwrite, const char hflag_test); + +void BM_mesh_elem_hflag_enable_all( + BMesh *bm, const char htype, const char hflag, + const bool respecthide); +void BM_mesh_elem_hflag_disable_all( + BMesh *bm, const char htype, const char hflag, + const bool respecthide); /* individual element select functions, BM_elem_select_set is a shortcut for these * that automatically detects which one to use*/ @@ -59,6 +63,10 @@ void BM_vert_select_set(BMesh *bm, BMVert *v, const bool select); void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select); void BM_face_select_set(BMesh *bm, BMFace *f, const bool select); +/* lower level functions which don't do flushing */ +void BM_edge_select_set_noflush(BMesh *bm, BMEdge *e, const bool select); +void BM_face_select_set_noflush(BMesh *bm, BMFace *e, const bool select); + void BM_mesh_select_mode_clean_ex(BMesh *bm, const short selectmode); void BM_mesh_select_mode_clean(BMesh *bm); @@ -87,6 +95,8 @@ void BM_editselection_plane(BMEditSelection *ese, float r_plane[3]); #define BM_select_history_remove(bm, ele) _bm_select_history_remove(bm, &(ele)->head) #define BM_select_history_store_notest(bm, ele) _bm_select_history_store_notest(bm, &(ele)->head) #define BM_select_history_store(bm, ele) _bm_select_history_store(bm, &(ele)->head) +#define BM_select_history_store_head_notest(bm, ele) _bm_select_history_store_head_notest(bm, &(ele)->head) +#define BM_select_history_store_head(bm, ele) _bm_select_history_store_head(bm, &(ele)->head) #define BM_select_history_store_after_notest(bm, ese_ref, ele) _bm_select_history_store_after_notest(bm, ese_ref, &(ele)->head) #define BM_select_history_store_after(bm, ese, ese_ref) _bm_select_history_store_after(bm, ese_ref, &(ele)->head) @@ -94,6 +104,8 @@ bool _bm_select_history_check(BMesh *bm, const BMHeader *ele); bool _bm_select_history_remove(BMesh *bm, BMHeader *ele); void _bm_select_history_store_notest(BMesh *bm, BMHeader *ele); void _bm_select_history_store(BMesh *bm, BMHeader *ele); +void _bm_select_history_store_head_notest(BMesh *bm, BMHeader *ele); +void _bm_select_history_store_head(BMesh *bm, BMHeader *ele); void _bm_select_history_store_after(BMesh *bm, BMEditSelection *ese_ref, BMHeader *ele); void _bm_select_history_store_after_notest(BMesh *bm, BMEditSelection *ese_ref, BMHeader *ele); diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c index b16ea42304a..115330cb25a 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.c +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -34,10 +34,12 @@ #include "BLI_linklist_stack.h" #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_stack.h" #include "BLI_utildefines.h" #include "BKE_cdderivedmesh.h" #include "BKE_editmesh.h" +#include "BKE_mesh.h" #include "BKE_multires.h" #include "intern/bmesh_private.h" @@ -301,8 +303,9 @@ static void bm_mesh_edges_calc_vectors(BMesh *bm, float (*edgevec)[3], const flo bm->elem_index_dirty &= ~BM_EDGE; } -static void bm_mesh_verts_calc_normals(BMesh *bm, const float (*edgevec)[3], const float (*fnos)[3], - const float (*vcos)[3], float (*vnos)[3]) +static void bm_mesh_verts_calc_normals( + BMesh *bm, const float (*edgevec)[3], const float (*fnos)[3], + const float (*vcos)[3], float (*vnos)[3]) { BM_mesh_elem_index_ensure(bm, (vnos) ? (BM_EDGE | BM_VERT) : BM_EDGE); @@ -435,10 +438,12 @@ void BM_verts_calc_normal_vcos(BMesh *bm, const float (*fnos)[3], const float (* /** * Helpers for #BM_mesh_loop_normals_update and #BM_loops_calc_normals_vnos */ -static void bm_mesh_edges_sharp_tag(BMesh *bm, const float (*vnos)[3], const float (*fnos)[3], float split_angle, - float (*r_lnos)[3]) +static void bm_mesh_edges_sharp_tag( + BMesh *bm, const float (*vnos)[3], const float (*fnos)[3], float split_angle, + float (*r_lnos)[3]) { - BMIter eiter; + BMIter eiter, viter; + BMVert *v; BMEdge *e; int i; @@ -450,15 +455,18 @@ static void bm_mesh_edges_sharp_tag(BMesh *bm, const float (*vnos)[3], const flo { char htype = BM_LOOP; - if (vnos) { - htype |= BM_VERT; - } if (fnos) { htype |= BM_FACE; } BM_mesh_elem_index_ensure(bm, htype); } + /* Clear all vertices' tags (means they are all smooth for now). */ + BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) { + BM_elem_index_set(v, i); /* set_inline */ + BM_elem_flag_disable(v, BM_ELEM_TAG); + } + /* This first loop checks which edges are actually smooth, and pre-populate lnos with vnos (as if they were * all smooth). */ @@ -481,13 +489,13 @@ static void bm_mesh_edges_sharp_tag(BMesh *bm, const float (*vnos)[3], const flo * If the angle between both its polys' normals is below split_angle value, * and it is tagged as such, * and both its faces are smooth, - * and both its faces have compatible (non-flipped) normals, i.e. both loops on the same edge do not share - * the same vertex. + * and both its faces have compatible (non-flipped) normals, + * i.e. both loops on the same edge do not share the same vertex. */ if (is_angle_smooth && - BM_elem_flag_test_bool(e, BM_ELEM_SMOOTH) && - BM_elem_flag_test_bool(l_a->f, BM_ELEM_SMOOTH) && - BM_elem_flag_test_bool(l_b->f, BM_ELEM_SMOOTH) && + BM_elem_flag_test(e, BM_ELEM_SMOOTH) && + BM_elem_flag_test(l_a->f, BM_ELEM_SMOOTH) && + BM_elem_flag_test(l_b->f, BM_ELEM_SMOOTH) && l_a->v != l_b->v) { const float *no; @@ -499,20 +507,40 @@ static void bm_mesh_edges_sharp_tag(BMesh *bm, const float (*vnos)[3], const flo no = vnos ? vnos[BM_elem_index_get(l_b->v)] : l_b->v->no; copy_v3_v3(r_lnos[BM_elem_index_get(l_b)], no); } + else { + /* Sharp edge, tag its verts as such. */ + BM_elem_flag_enable(e->v1, BM_ELEM_TAG); + BM_elem_flag_enable(e->v2, BM_ELEM_TAG); + } + } + else { + /* Sharp edge, tag its verts as such. */ + BM_elem_flag_enable(e->v1, BM_ELEM_TAG); + BM_elem_flag_enable(e->v2, BM_ELEM_TAG); } } - bm->elem_index_dirty &= ~BM_EDGE; + bm->elem_index_dirty &= ~(BM_EDGE | BM_VERT); } -/* BMesh version of BKE_mesh_normals_loop_split() in mesh_evaluate.c */ -static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const float (*fnos)[3], float (*r_lnos)[3]) +/* BMesh version of BKE_mesh_normals_loop_split() in mesh_evaluate.c + * Will use first clnors_data array, and fallback to cd_loop_clnors_offset (use NULL and -1 to not use clnors). */ +static void bm_mesh_loops_calc_normals( + BMesh *bm, const float (*vcos)[3], const float (*fnos)[3], float (*r_lnos)[3], + MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset) { BMIter fiter; BMFace *f_curr; + const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1); + + MLoopNorSpaceArray _lnors_spacearr = {NULL}; /* Temp normal stack. */ BLI_SMALLSTACK_DECLARE(normal, float *); + /* Temp clnors stack. */ + BLI_SMALLSTACK_DECLARE(clnors, short *); + /* Temp edge vectors stack, only used when computing lnor spacearr. */ + BLI_Stack *edge_vectors = NULL; { char htype = BM_LOOP; @@ -525,6 +553,15 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const BM_mesh_elem_index_ensure(bm, htype); } + if (!r_lnors_spacearr && has_clnors) { + /* We need to compute lnor spacearr if some custom lnor data are given to us! */ + r_lnors_spacearr = &_lnors_spacearr; + } + if (r_lnors_spacearr) { + BKE_lnor_spacearr_init(r_lnors_spacearr, bm->totloop); + edge_vectors = BLI_stack_new(sizeof(float[3]), __func__); + } + /* We now know edges that can be smoothed (they are tagged), and edges that will be hard (they aren't). * Now, time to generate the normals. */ @@ -533,8 +570,10 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr); do { - if (BM_elem_flag_test_bool(l_curr->e, BM_ELEM_TAG)) { - /* A smooth edge. + if (BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) && + (!r_lnors_spacearr || BM_elem_flag_test(l_curr->v, BM_ELEM_TAG))) + { + /* A smooth edge, and we are not generating lnors_spacearr, or the related vertex is sharp. * We skip it because it is either: * - in the middle of a 'smooth fan' already computed (or that will be as soon as we hit * one of its ends, i.e. one of its two sharp edges), or... @@ -542,12 +581,45 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const * are just fine! */ } - else if (!BM_elem_flag_test_bool(l_curr->prev->e, BM_ELEM_TAG)) { + else if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) && + !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG)) + { /* Simple case (both edges around that vertex are sharp in related polygon), * this vertex just takes its poly normal. */ + const int l_curr_index = BM_elem_index_get(l_curr); const float *no = fnos ? fnos[BM_elem_index_get(f_curr)] : f_curr->no; - copy_v3_v3(r_lnos[BM_elem_index_get(l_curr)], no); + copy_v3_v3(r_lnos[l_curr_index], no); + + /* If needed, generate this (simple!) lnor space. */ + if (r_lnors_spacearr) { + float vec_curr[3], vec_prev[3]; + MLoopNorSpace *lnor_space = BKE_lnor_space_create(r_lnors_spacearr); + + { + const BMVert *v_pivot = l_curr->v; + const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co; + const BMVert *v_1 = BM_edge_other_vert(l_curr->e, v_pivot); + const float *co_1 = vcos ? vcos[BM_elem_index_get(v_1)] : v_1->co; + const BMVert *v_2 = BM_edge_other_vert(l_curr->prev->e, v_pivot); + const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co; + + sub_v3_v3v3(vec_curr, co_1, co_pivot); + normalize_v3(vec_curr); + sub_v3_v3v3(vec_prev, co_2, co_pivot); + normalize_v3(vec_prev); + } + + BKE_lnor_space_define(lnor_space, r_lnos[l_curr_index], vec_curr, vec_prev, NULL); + /* We know there is only one loop in this space, no need to create a linklist in this case... */ + BKE_lnor_space_add_loop(r_lnors_spacearr, lnor_space, l_curr_index, false); + + if (has_clnors) { + short (*clnor)[2] = clnors_data ? &clnors_data[l_curr_index] : + BM_ELEM_CD_GET_VOID_P(l_curr, cd_loop_clnors_offset); + BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor, r_lnos[l_curr_index]); + } + } } /* We *do not need* to check/tag loops as already computed! * Due to the fact a loop only links to one of its two edges, a same fan *will never be walked more than @@ -567,13 +639,26 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const */ BMVert *v_pivot = l_curr->v; BMEdge *e_next; + const BMEdge *e_org = l_curr->e; BMLoop *lfan_pivot, *lfan_pivot_next; + int lfan_pivot_index; float lnor[3] = {0.0f, 0.0f, 0.0f}; - float vec_curr[3], vec_next[3]; + float vec_curr[3], vec_next[3], vec_org[3]; + + /* We validate clnors data on the fly - cheapest way to do! */ + int clnors_avg[2] = {0, 0}; + short (*clnor_ref)[2] = NULL; + int clnors_nbr = 0; + bool clnors_invalid = false; const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co; + MLoopNorSpace *lnor_space = r_lnors_spacearr ? BKE_lnor_space_create(r_lnors_spacearr) : NULL; + + BLI_assert((edge_vectors == NULL) || BLI_stack_is_empty(edge_vectors)); + lfan_pivot = l_curr; + lfan_pivot_index = BM_elem_index_get(lfan_pivot); e_next = lfan_pivot->e; /* Current edge here, actually! */ /* Only need to compute previous edge's vector once, then we can just reuse old current one! */ @@ -581,8 +666,13 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot); const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co; - sub_v3_v3v3(vec_curr, co_2, co_pivot); - normalize_v3(vec_curr); + sub_v3_v3v3(vec_org, co_2, co_pivot); + normalize_v3(vec_org); + copy_v3_v3(vec_curr, vec_org); + + if (r_lnors_spacearr) { + BLI_stack_push(edge_vectors, vec_org); + } } while (true) { @@ -617,12 +707,38 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const const float *no = fnos ? fnos[BM_elem_index_get(f)] : f->no; /* Accumulate */ madd_v3_v3fl(lnor, no, fac); + + if (has_clnors) { + /* Accumulate all clnors, if they are not all equal we have to fix that! */ + short (*clnor)[2] = clnors_data ? &clnors_data[lfan_pivot_index] : + BM_ELEM_CD_GET_VOID_P(lfan_pivot, cd_loop_clnors_offset); + if (clnors_nbr) { + clnors_invalid |= ((*clnor_ref)[0] != (*clnor)[0] || (*clnor_ref)[1] != (*clnor)[1]); + } + else { + clnor_ref = clnor; + } + clnors_avg[0] += (*clnor)[0]; + clnors_avg[1] += (*clnor)[1]; + clnors_nbr++; + /* We store here a pointer to all custom lnors processed. */ + BLI_SMALLSTACK_PUSH(clnors, (short *)*clnor); + } } /* We store here a pointer to all loop-normals processed. */ - BLI_SMALLSTACK_PUSH(normal, (float *)r_lnos[BM_elem_index_get(lfan_pivot)]); + BLI_SMALLSTACK_PUSH(normal, (float *)r_lnos[lfan_pivot_index]); + + if (r_lnors_spacearr) { + /* Assign current lnor space to current 'vertex' loop. */ + BKE_lnor_space_add_loop(r_lnors_spacearr, lnor_space, lfan_pivot_index, true); + if (e_next != e_org) { + /* We store here all edges-normalized vectors processed. */ + BLI_stack_push(edge_vectors, vec_next); + } + } - if (!BM_elem_flag_test_bool(e_next, BM_ELEM_TAG)) { + if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) { /* Next edge is sharp, we have finished with this fan of faces around this vert! */ break; } @@ -631,23 +747,105 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const copy_v3_v3(vec_curr, vec_next); /* Next pivot loop to current one. */ lfan_pivot = lfan_pivot_next; + lfan_pivot_index = BM_elem_index_get(lfan_pivot); } - /* In case we get a zero normal here, just use vertex normal already set! */ - if (LIKELY(normalize_v3(lnor) != 0.0f)) { - /* Copy back the final computed normal into all related loop-normals. */ - float *nor; - while ((nor = BLI_SMALLSTACK_POP(normal))) { - copy_v3_v3(nor, lnor); + { + float lnor_len = normalize_v3(lnor); + + /* If we are generating lnor spacearr, we can now define the one for this fan. */ + if (r_lnors_spacearr) { + if (UNLIKELY(lnor_len == 0.0f)) { + /* Use vertex normal as fallback! */ + copy_v3_v3(lnor, r_lnos[lfan_pivot_index]); + lnor_len = 1.0f; + } + + BKE_lnor_space_define(lnor_space, lnor, vec_org, vec_next, edge_vectors); + + if (has_clnors) { + if (clnors_invalid) { + short *clnor; + + clnors_avg[0] /= clnors_nbr; + clnors_avg[1] /= clnors_nbr; + /* Fix/update all clnors of this fan with computed average value. */ + printf("Invalid clnors in this fan!\n"); + while ((clnor = BLI_SMALLSTACK_POP(clnors))) { + //print_v2("org clnor", clnor); + clnor[0] = (short)clnors_avg[0]; + clnor[1] = (short)clnors_avg[1]; + } + //print_v2("new clnors", clnors_avg); + } + else { + /* We still have to consume the stack! */ + while (BLI_SMALLSTACK_POP(clnors)); + } + BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor_ref, lnor); + } + } + + /* In case we get a zero normal here, just use vertex normal already set! */ + if (LIKELY(lnor_len != 0.0f)) { + /* Copy back the final computed normal into all related loop-normals. */ + float *nor; + + while ((nor = BLI_SMALLSTACK_POP(normal))) { + copy_v3_v3(nor, lnor); + } + } + else { + /* We still have to consume the stack! */ + while (BLI_SMALLSTACK_POP(normal)); } } - else { - /* We still have to clear the stack! */ - while (BLI_SMALLSTACK_POP(normal)); + + /* Tag related vertex as sharp, to avoid fanning around it again (in case it was a smooth one). */ + if (r_lnors_spacearr) { + BM_elem_flag_enable(l_curr->v, BM_ELEM_TAG); } } } while ((l_curr = l_curr->next) != l_first); } + + if (r_lnors_spacearr) { + BLI_stack_free(edge_vectors); + if (r_lnors_spacearr == &_lnors_spacearr) { + BKE_lnor_spacearr_free(r_lnors_spacearr); + } + } +} + +static void bm_mesh_loops_calc_normals_no_autosmooth( + BMesh *bm, const float (*vnos)[3], const float (*fnos)[3], float (*r_lnos)[3]) +{ + BMIter fiter; + BMFace *f_curr; + + { + char htype = BM_LOOP; + if (vnos) { + htype |= BM_VERT; + } + if (fnos) { + htype |= BM_FACE; + } + BM_mesh_elem_index_ensure(bm, htype); + } + + BM_ITER_MESH (f_curr, &fiter, bm, BM_FACES_OF_MESH) { + BMLoop *l_curr, *l_first; + const bool is_face_flat = !BM_elem_flag_test(f_curr, BM_ELEM_SMOOTH); + + l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr); + do { + const float *no = is_face_flat ? (fnos ? fnos[BM_elem_index_get(f_curr)] : f_curr->no) : + (vnos ? vnos[BM_elem_index_get(l_curr->v)] : l_curr->v->no); + copy_v3_v3(r_lnos[BM_elem_index_get(l_curr)], no); + + } while ((l_curr = l_curr->next) != l_first); + } } #if 0 /* Unused currently */ @@ -657,13 +855,24 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const * Updates the loop normals of a mesh. Assumes vertex and face normals are valid (else call BM_mesh_normals_update() * first)! */ -void BM_mesh_loop_normals_update(BMesh *bm, const float split_angle, float (*r_lnos)[3]) +void BM_mesh_loop_normals_update( + BMesh *bm, const bool use_split_normals, const float split_angle, float (*r_lnos)[3], + MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset) { - /* Tag smooth edges and set lnos from vnos when they might be completely smooth... */ - bm_mesh_edges_sharp_tag(bm, NULL, NULL, split_angle, r_lnos); + const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1); - /* Finish computing lnos by accumulating face normals in each fan of faces defined by sharp edges. */ - bm_mesh_loops_calc_normals(bm, NULL, NULL, r_lnos); + if (use_split_normals) { + /* Tag smooth edges and set lnos from vnos when they might be completely smooth... + * When using custom loop normals, disable the angle feature! */ + bm_mesh_edges_sharp_tag(bm, NULL, NULL, has_clnors ? (float)M_PI : split_angle, r_lnos); + + /* Finish computing lnos by accumulating face normals in each fan of faces defined by sharp edges. */ + bm_mesh_loops_calc_normals(bm, NULL, NULL, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset); + } + else { + BLI_assert(!r_lnors_spacearr); + bm_mesh_loops_calc_normals_no_autosmooth(bm, NULL, NULL, r_lnos); + } } #endif @@ -673,14 +882,25 @@ void BM_mesh_loop_normals_update(BMesh *bm, const float split_angle, float (*r_l * Compute split normals, i.e. vertex normals associated with each poly (hence 'loop normals'). * Useful to materialize sharp edges (or non-smooth faces) without actually modifying the geometry (splitting edges). */ -void BM_loops_calc_normal_vcos(BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*fnos)[3], - const float split_angle, float (*r_lnos)[3]) +void BM_loops_calc_normal_vcos( + BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*fnos)[3], + const bool use_split_normals, const float split_angle, float (*r_lnos)[3], + MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset) { - /* Tag smooth edges and set lnos from vnos when they might be completely smooth... */ - bm_mesh_edges_sharp_tag(bm, vnos, fnos, split_angle, r_lnos); + const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1); + + if (use_split_normals) { + /* Tag smooth edges and set lnos from vnos when they might be completely smooth... + * When using custom loop normals, disable the angle feature! */ + bm_mesh_edges_sharp_tag(bm, vnos, fnos, has_clnors ? (float)M_PI : split_angle, r_lnos); - /* Finish computing lnos by accumulating face normals in each fan of faces defined by sharp edges. */ - bm_mesh_loops_calc_normals(bm, vcos, fnos, r_lnos); + /* Finish computing lnos by accumulating face normals in each fan of faces defined by sharp edges. */ + bm_mesh_loops_calc_normals(bm, vcos, fnos, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset); + } + else { + BLI_assert(!r_lnors_spacearr); + bm_mesh_loops_calc_normals_no_autosmooth(bm, vnos, fnos, r_lnos); + } } static void UNUSED_FUNCTION(bm_mdisps_space_set)(Object *ob, BMesh *bm, int from, int to) @@ -913,8 +1133,9 @@ finally: * These functions ensure its correct and are called more often in debug mode. */ -void BM_mesh_elem_index_validate(BMesh *bm, const char *location, const char *func, - const char *msg_a, const char *msg_b) +void BM_mesh_elem_index_validate( + BMesh *bm, const char *location, const char *func, + const char *msg_a, const char *msg_b) { const char iter_types[3] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, @@ -1161,6 +1382,41 @@ BMFace *BM_face_at_index_find(BMesh *bm, const int index) return BLI_mempool_findelem(bm->fpool, index); } +/** + * Use lookup table when available, else use slower find functions. + * + * \note Try to use #BM_mesh_elem_table_ensure instead. + */ +BMVert *BM_vert_at_index_find_or_table(BMesh *bm, const int index) +{ + if ((bm->elem_table_dirty & BM_VERT) == 0) { + return (index < bm->totvert) ? bm->vtable[index] : NULL; + } + else { + return BM_vert_at_index_find(bm, index); + } +} + +BMEdge *BM_edge_at_index_find_or_table(BMesh *bm, const int index) +{ + if ((bm->elem_table_dirty & BM_EDGE) == 0) { + return (index < bm->totedge) ? bm->etable[index] : NULL; + } + else { + return BM_edge_at_index_find(bm, index); + } +} + +BMFace *BM_face_at_index_find_or_table(BMesh *bm, const int index) +{ + if ((bm->elem_table_dirty & BM_FACE) == 0) { + return (index < bm->totface) ? bm->ftable[index] : NULL; + } + else { + return BM_face_at_index_find(bm, index); + } +} + /** * Return the amount of element of type 'type' in a given bmesh. @@ -1182,6 +1438,24 @@ int BM_mesh_elem_count(BMesh *bm, const char htype) } /** + * Special case: Python uses custom-data layers to hold PyObject references. + * These have to be kept in-place, else the PyObject's we point to, wont point back to us. + * + * \note ``ele_src`` Is a duplicate, so we don't need to worry about getting in a feedback loop. + * + * \note If there are other customdata layers which need this functionality, it should be generalized. + * However #BM_mesh_remap is currently the only place where this is done. + */ +static void bm_mesh_remap_cd_update( + BMHeader *ele_dst, BMHeader *ele_src, + const int cd_elem_pyptr) +{ + void **pyptr_dst_p = BM_ELEM_CD_GET_VOID_P(((BMElem *)ele_dst), cd_elem_pyptr); + void **pyptr_src_p = BM_ELEM_CD_GET_VOID_P(((BMElem *)ele_src), cd_elem_pyptr); + *pyptr_dst_p = *pyptr_src_p; +} + +/** * Remaps the vertices, edges and/or faces of the bmesh as indicated by vert/edge/face_idx arrays * (xxx_idx[org_index] = new_index). * @@ -1221,6 +1495,7 @@ void BM_mesh_remap( BMVert **verts_pool, *verts_copy, **vep; int i, totvert = bm->totvert; const unsigned int *new_idx; + const int cd_vert_pyptr = CustomData_get_offset(&bm->vdata, CD_BM_ELEM_PYPTR); /* Init the old-to-new vert pointers mapping */ vptr_map = BLI_ghash_ptr_new_ex("BM_mesh_remap vert pointers mapping", bm->totvert); @@ -1241,7 +1516,10 @@ void BM_mesh_remap( BMVert *new_vep = verts_pool[*new_idx]; *new_vep = *ve; /* printf("mapping vert from %d to %d (%p/%p to %p)\n", i, *new_idx, *vep, verts_pool[i], new_vep);*/ - BLI_ghash_insert(vptr_map, (void *)*vep, (void *)new_vep); + BLI_ghash_insert(vptr_map, *vep, new_vep); + if (cd_vert_pyptr != -1) { + bm_mesh_remap_cd_update(&(*vep)->head, &new_vep->head, cd_vert_pyptr); + } } bm->elem_index_dirty |= BM_VERT; bm->elem_table_dirty |= BM_VERT; @@ -1254,6 +1532,7 @@ void BM_mesh_remap( BMEdge **edges_pool, *edges_copy, **edp; int i, totedge = bm->totedge; const unsigned int *new_idx; + const int cd_edge_pyptr = CustomData_get_offset(&bm->edata, CD_BM_ELEM_PYPTR); /* Init the old-to-new vert pointers mapping */ eptr_map = BLI_ghash_ptr_new_ex("BM_mesh_remap edge pointers mapping", bm->totedge); @@ -1272,8 +1551,11 @@ void BM_mesh_remap( for (i = totedge; i--; new_idx--, ed--, edp--) { BMEdge *new_edp = edges_pool[*new_idx]; *new_edp = *ed; - BLI_ghash_insert(eptr_map, (void *)*edp, (void *)new_edp); + BLI_ghash_insert(eptr_map, *edp, new_edp); /* printf("mapping edge from %d to %d (%p/%p to %p)\n", i, *new_idx, *edp, edges_pool[i], new_edp);*/ + if (cd_edge_pyptr != -1) { + bm_mesh_remap_cd_update(&(*edp)->head, &new_edp->head, cd_edge_pyptr); + } } bm->elem_index_dirty |= BM_EDGE; bm->elem_table_dirty |= BM_EDGE; @@ -1286,6 +1568,7 @@ void BM_mesh_remap( BMFace **faces_pool, *faces_copy, **fap; int i, totface = bm->totface; const unsigned int *new_idx; + const int cd_poly_pyptr = CustomData_get_offset(&bm->pdata, CD_BM_ELEM_PYPTR); /* Init the old-to-new vert pointers mapping */ fptr_map = BLI_ghash_ptr_new_ex("BM_mesh_remap face pointers mapping", bm->totface); @@ -1304,7 +1587,10 @@ void BM_mesh_remap( for (i = totface; i--; new_idx--, fa--, fap--) { BMFace *new_fap = faces_pool[*new_idx]; *new_fap = *fa; - BLI_ghash_insert(fptr_map, (void *)*fap, (void *)new_fap); + BLI_ghash_insert(fptr_map, *fap, new_fap); + if (cd_poly_pyptr != -1) { + bm_mesh_remap_cd_update(&(*fap)->head, &new_fap->head, cd_poly_pyptr); + } } bm->elem_index_dirty |= BM_FACE | BM_LOOP; @@ -1317,8 +1603,11 @@ void BM_mesh_remap( /* Verts' pointers, only edge pointers... */ if (eptr_map) { BM_ITER_MESH (ve, &iter, bm, BM_VERTS_OF_MESH) { -/* printf("Vert e: %p -> %p\n", ve->e, BLI_ghash_lookup(eptr_map, (const void *)ve->e));*/ - ve->e = BLI_ghash_lookup(eptr_map, (const void *)ve->e); +/* printf("Vert e: %p -> %p\n", ve->e, BLI_ghash_lookup(eptr_map, ve->e));*/ + if (ve->e) { + ve->e = BLI_ghash_lookup(eptr_map, ve->e); + BLI_assert(ve->e); + } } } @@ -1327,24 +1616,30 @@ void BM_mesh_remap( if (vptr_map || eptr_map) { BM_ITER_MESH (ed, &iter, bm, BM_EDGES_OF_MESH) { if (vptr_map) { -/* printf("Edge v1: %p -> %p\n", ed->v1, BLI_ghash_lookup(vptr_map, (const void *)ed->v1));*/ -/* printf("Edge v2: %p -> %p\n", ed->v2, BLI_ghash_lookup(vptr_map, (const void *)ed->v2));*/ - ed->v1 = BLI_ghash_lookup(vptr_map, (const void *)ed->v1); - ed->v2 = BLI_ghash_lookup(vptr_map, (const void *)ed->v2); +/* printf("Edge v1: %p -> %p\n", ed->v1, BLI_ghash_lookup(vptr_map, ed->v1));*/ +/* printf("Edge v2: %p -> %p\n", ed->v2, BLI_ghash_lookup(vptr_map, ed->v2));*/ + ed->v1 = BLI_ghash_lookup(vptr_map, ed->v1); + ed->v2 = BLI_ghash_lookup(vptr_map, ed->v2); + BLI_assert(ed->v1); + BLI_assert(ed->v2); } if (eptr_map) { /* printf("Edge v1_disk_link prev: %p -> %p\n", ed->v1_disk_link.prev,*/ -/* BLI_ghash_lookup(eptr_map, (const void *)ed->v1_disk_link.prev));*/ +/* BLI_ghash_lookup(eptr_map, ed->v1_disk_link.prev));*/ /* printf("Edge v1_disk_link next: %p -> %p\n", ed->v1_disk_link.next,*/ -/* BLI_ghash_lookup(eptr_map, (const void *)ed->v1_disk_link.next));*/ +/* BLI_ghash_lookup(eptr_map, ed->v1_disk_link.next));*/ /* printf("Edge v2_disk_link prev: %p -> %p\n", ed->v2_disk_link.prev,*/ -/* BLI_ghash_lookup(eptr_map, (const void *)ed->v2_disk_link.prev));*/ +/* BLI_ghash_lookup(eptr_map, ed->v2_disk_link.prev));*/ /* printf("Edge v2_disk_link next: %p -> %p\n", ed->v2_disk_link.next,*/ -/* BLI_ghash_lookup(eptr_map, (const void *)ed->v2_disk_link.next));*/ - ed->v1_disk_link.prev = BLI_ghash_lookup(eptr_map, (const void *)ed->v1_disk_link.prev); - ed->v1_disk_link.next = BLI_ghash_lookup(eptr_map, (const void *)ed->v1_disk_link.next); - ed->v2_disk_link.prev = BLI_ghash_lookup(eptr_map, (const void *)ed->v2_disk_link.prev); - ed->v2_disk_link.next = BLI_ghash_lookup(eptr_map, (const void *)ed->v2_disk_link.next); +/* BLI_ghash_lookup(eptr_map, ed->v2_disk_link.next));*/ + ed->v1_disk_link.prev = BLI_ghash_lookup(eptr_map, ed->v1_disk_link.prev); + ed->v1_disk_link.next = BLI_ghash_lookup(eptr_map, ed->v1_disk_link.next); + ed->v2_disk_link.prev = BLI_ghash_lookup(eptr_map, ed->v2_disk_link.prev); + ed->v2_disk_link.next = BLI_ghash_lookup(eptr_map, ed->v2_disk_link.next); + BLI_assert(ed->v1_disk_link.prev); + BLI_assert(ed->v1_disk_link.next); + BLI_assert(ed->v2_disk_link.prev); + BLI_assert(ed->v2_disk_link.next); } } } @@ -1353,16 +1648,19 @@ void BM_mesh_remap( BM_ITER_MESH (fa, &iter, bm, BM_FACES_OF_MESH) { BM_ITER_ELEM (lo, &iterl, fa, BM_LOOPS_OF_FACE) { if (vptr_map) { -/* printf("Loop v: %p -> %p\n", lo->v, BLI_ghash_lookup(vptr_map, (const void *)lo->v));*/ - lo->v = BLI_ghash_lookup(vptr_map, (const void *)lo->v); +/* printf("Loop v: %p -> %p\n", lo->v, BLI_ghash_lookup(vptr_map, lo->v));*/ + lo->v = BLI_ghash_lookup(vptr_map, lo->v); + BLI_assert(lo->v); } if (eptr_map) { -/* printf("Loop e: %p -> %p\n", lo->e, BLI_ghash_lookup(eptr_map, (const void *)lo->e));*/ - lo->e = BLI_ghash_lookup(eptr_map, (const void *)lo->e); +/* printf("Loop e: %p -> %p\n", lo->e, BLI_ghash_lookup(eptr_map, lo->e));*/ + lo->e = BLI_ghash_lookup(eptr_map, lo->e); + BLI_assert(lo->e); } if (fptr_map) { -/* printf("Loop f: %p -> %p\n", lo->f, BLI_ghash_lookup(fptr_map, (const void *)lo->f));*/ - lo->f = BLI_ghash_lookup(fptr_map, (const void *)lo->f); +/* printf("Loop f: %p -> %p\n", lo->f, BLI_ghash_lookup(fptr_map, lo->f));*/ + lo->f = BLI_ghash_lookup(fptr_map, lo->f); + BLI_assert(lo->f); } } } diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index 22e50502aee..b157237c7d0 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -28,6 +28,7 @@ */ struct BMAllocTemplate; +struct MLoopNorSpaceArray; void BM_mesh_elem_toolflags_ensure(BMesh *bm); void BM_mesh_elem_toolflags_clear(BMesh *bm); @@ -39,15 +40,18 @@ void BM_mesh_clear(BMesh *bm); void BM_mesh_normals_update(BMesh *bm); void BM_verts_calc_normal_vcos(BMesh *bm, const float (*fnos)[3], const float (*vcos)[3], float (*vnos)[3]); -void BM_loops_calc_normal_vcos(BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*pnos)[3], - const float split_angle, float (*r_lnos)[3]); +void BM_loops_calc_normal_vcos( + BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*pnos)[3], + const bool use_split_normals, const float split_angle, float (*r_lnos)[3], + struct MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset); void bmesh_edit_begin(BMesh *bm, const BMOpTypeFlag type_flag); void bmesh_edit_end(BMesh *bm, const BMOpTypeFlag type_flag); void BM_mesh_elem_index_ensure(BMesh *bm, const char hflag); -void BM_mesh_elem_index_validate(BMesh *bm, const char *location, const char *func, - const char *msg_a, const char *msg_b); +void BM_mesh_elem_index_validate( + BMesh *bm, const char *location, const char *func, + const char *msg_a, const char *msg_b); #ifndef NDEBUG bool BM_mesh_elem_table_check(BMesh *bm); @@ -65,6 +69,10 @@ BMVert *BM_vert_at_index_find(BMesh *bm, const int index); BMEdge *BM_edge_at_index_find(BMesh *bm, const int index); BMFace *BM_face_at_index_find(BMesh *bm, const int index); +BMVert *BM_vert_at_index_find_or_table(BMesh *bm, const int index); +BMEdge *BM_edge_at_index_find_or_table(BMesh *bm, const int index); +BMFace *BM_face_at_index_find_or_table(BMesh *bm, const int index); + // XXX int BM_mesh_elem_count(BMesh *bm, const char htype); diff --git a/source/blender/bmesh/intern/bmesh_mesh_conv.c b/source/blender/bmesh/intern/bmesh_mesh_conv.c index 05f3ff5b60b..24d70cefb2e 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_conv.c +++ b/source/blender/bmesh/intern/bmesh_mesh_conv.c @@ -200,8 +200,9 @@ char BM_mesh_cd_flag_from_bmesh(BMesh *bm) } /* Static function for alloc (duplicate in modifiers_bmesh.c) */ -static BMFace *bm_face_create_from_mpoly(MPoly *mp, MLoop *ml, - BMesh *bm, BMVert **vtable, BMEdge **etable) +static BMFace *bm_face_create_from_mpoly( + MPoly *mp, MLoop *ml, + BMesh *bm, BMVert **vtable, BMEdge **etable) { BMVert **verts = BLI_array_alloca(verts, mp->totloop); BMEdge **edges = BLI_array_alloca(edges, mp->totloop); @@ -221,8 +222,9 @@ static BMFace *bm_face_create_from_mpoly(MPoly *mp, MLoop *ml, * * \warning This function doesn't calculate face normals. */ -void BM_mesh_bm_from_me(BMesh *bm, Mesh *me, - const bool calc_face_normal, const bool set_key, int act_key_nr) +void BM_mesh_bm_from_me( + BMesh *bm, Mesh *me, + const bool calc_face_normal, const bool set_key, int act_key_nr) { MVert *mvert; MEdge *medge; @@ -788,7 +790,7 @@ void BM_mesh_bm_to_me(BMesh *bm, Mesh *me, bool do_tessface) { BMEditSelection *selected; - me->totselect = BLI_countlist(&(bm->selected)); + me->totselect = BLI_listbase_count(&(bm->selected)); if (me->mselect) MEM_freeN(me->mselect); @@ -850,15 +852,7 @@ void BM_mesh_bm_to_me(BMesh *bm, Mesh *me, bool do_tessface) * bmesh and the mesh are out of sync */ (oldverts != NULL)) /* not used here, but 'oldverts' is used later for applying 'ofs' */ { - bool act_is_basis = false; - - /* find if this key is a basis for any others */ - for (currkey = me->key->block.first; currkey; currkey = currkey->next) { - if (bm->shapenr - 1 == currkey->relative) { - act_is_basis = true; - break; - } - } + const bool act_is_basis = BKE_keyblock_is_basis(me->key, bm->shapenr - 1); /* active key is a base */ if (act_is_basis && (cd_shape_keyindex_offset != -1)) { diff --git a/source/blender/bmesh/intern/bmesh_mesh_conv.h b/source/blender/bmesh/intern/bmesh_mesh_conv.h index ab9d7a0ccf3..ce286f6c662 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_conv.h +++ b/source/blender/bmesh/intern/bmesh_mesh_conv.h @@ -39,8 +39,9 @@ void BM_mesh_cd_flag_ensure(BMesh *bm, struct Mesh *mesh, const char cd_flag); void BM_mesh_cd_flag_apply(BMesh *bm, const char cd_flag); char BM_mesh_cd_flag_from_bmesh(BMesh *bm); -void BM_mesh_bm_from_me(BMesh *bm, struct Mesh *me, - const bool calc_face_normal, const bool set_key, int act_key_nr); +void BM_mesh_bm_from_me( + BMesh *bm, struct Mesh *me, + const bool calc_face_normal, const bool set_key, int act_key_nr); void BM_mesh_bm_to_me(BMesh *bm, struct Mesh *me, bool do_tessface); #endif /* __BMESH_MESH_CONV_H__ */ diff --git a/source/blender/bmesh/intern/bmesh_mesh_validate.c b/source/blender/bmesh/intern/bmesh_mesh_validate.c index 3a7a4f85e99..478194735f3 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_validate.c +++ b/source/blender/bmesh/intern/bmesh_mesh_validate.c @@ -86,20 +86,19 @@ bool BM_mesh_validate(BMesh *bm) /* check edges */ BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) { - BMEdge *e_other; + void **val_p; if (e->v1 == e->v2) { ERRMSG("edge %d: duplicate index: %d", i, BM_elem_index_get(e->v1)); } - /* build edgehash at the same time */ - e_other = BLI_edgehash_lookup(edge_hash, BM_elem_index_get(e->v1), BM_elem_index_get(e->v2)); - if (e_other) { + if (BLI_edgehash_ensure_p(edge_hash, BM_elem_index_get(e->v1), BM_elem_index_get(e->v2), &val_p)) { + BMEdge *e_other = *val_p; ERRMSG("edge %d, %d: are duplicates", i, BM_elem_index_get(e_other)); } else { - BLI_edgehash_insert(edge_hash, BM_elem_index_get(e->v1), BM_elem_index_get(e->v2), e); + *val_p = e; } } @@ -196,7 +195,7 @@ bool BM_mesh_validate(BMesh *bm) ERRMSG("Finished - errors %d", errtot); - return true; + return (errtot == 0); } diff --git a/source/blender/bmesh/intern/bmesh_mods.c b/source/blender/bmesh/intern/bmesh_mods.c index 6e8591da0f3..13c43fabdb0 100644 --- a/source/blender/bmesh/intern/bmesh_mods.c +++ b/source/blender/bmesh/intern/bmesh_mods.c @@ -72,7 +72,8 @@ */ bool BM_vert_dissolve(BMesh *bm, BMVert *v) { - const int len = BM_vert_edge_count(v); + /* logic for 3 or more is identical */ + const int len = BM_vert_edge_count_ex(v, 3); if (len == 1) { BM_vert_kill(bm, v); /* will kill edges too */ @@ -97,7 +98,7 @@ bool BM_vert_dissolve(BMesh *bm, BMVert *v) return false; } } - else if (len == 2 && BM_vert_face_count(v) == 1) { + else if (len == 2 && BM_vert_face_count_is_equal(v, 1)) { /* boundary vertex on a face */ return (BM_vert_collapse_edge(bm, v->e, v, true, true) != NULL); } @@ -265,19 +266,21 @@ BMFace *BM_faces_join_pair(BMesh *bm, BMFace *f_a, BMFace *f_b, BMEdge *e, const * * \param bm The bmesh * \param f the original face - * \param v1, v2 vertices which define the split edge, must be different + * \param l_a, l_b Loops of this face, their vertices define + * the split edge to be created (must be differ and not can't be adjacent in the face). * \param r_l pointer which will receive the BMLoop for the split edge in the new face * \param example Edge used for attributes of splitting edge, if non-NULL - * \param nodouble Use an existing edge if found + * \param no_double: Use an existing edge if found * * \return Pointer to the newly created face representing one side of the split * if the split is successful (and the original original face will be the * other side). NULL if the split fails. */ -BMFace *BM_face_split(BMesh *bm, BMFace *f, - BMLoop *l_a, BMLoop *l_b, - BMLoop **r_l, BMEdge *example, - const bool no_double) +BMFace *BM_face_split( + BMesh *bm, BMFace *f, + BMLoop *l_a, BMLoop *l_b, + BMLoop **r_l, BMEdge *example, + const bool no_double) { const bool has_mdisp = CustomData_has_layer(&bm->ldata, CD_MDISPS); BMFace *f_new, *f_tmp; @@ -348,17 +351,18 @@ BMFace *BM_face_split(BMesh *bm, BMFace *f, * \param l_a, l_b vertices which define the split edge, must be different * \param cos Array of coordinates for intermediate points * \param n Length of \a cos (must be > 0) - * \param r_l pointer which will receive the BMLoop for the first split edge (from \a v1) in the new face + * \param r_l pointer which will receive the BMLoop for the first split edge (from \a l_a) in the new face * \param example Edge used for attributes of splitting edge, if non-NULL * * \return Pointer to the newly created face representing one side of the split * if the split is successful (and the original original face will be the * other side). NULL if the split fails. */ -BMFace *BM_face_split_n(BMesh *bm, BMFace *f, - BMLoop *l_a, BMLoop *l_b, - float cos[][3], int n, - BMLoop **r_l, BMEdge *example) +BMFace *BM_face_split_n( + BMesh *bm, BMFace *f, + BMLoop *l_a, BMLoop *l_b, + float cos[][3], int n, + BMLoop **r_l, BMEdge *example) { BMFace *f_new, *f_tmp; BMLoop *l_dummy; @@ -901,7 +905,9 @@ bool BM_face_split_edgenet( if (l_first == NULL) { mul_v2_m3v3(co, axis_mat, v->co); interp_weights_poly_v2(w, cos_2d, f->len, co); - CustomData_bmesh_interp(&bm->ldata, blocks, w, NULL, f->len, l_iter->head.data); + CustomData_bmesh_interp( + &bm->ldata, (const void **)blocks, + w, NULL, f->len, l_iter->head.data); l_first = l_iter; } else { @@ -987,8 +993,9 @@ bool BM_face_split_edgenet( * * \returns The New Edge */ -BMEdge *BM_vert_collapse_faces(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, float fac, - const bool do_del, const bool join_faces, const bool kill_degenerate_faces) +BMEdge *BM_vert_collapse_faces( + BMesh *bm, BMEdge *e_kill, BMVert *v_kill, float fac, + const bool do_del, const bool join_faces, const bool kill_degenerate_faces) { BMEdge *e_new = NULL; BMVert *tv = BM_edge_other_vert(e_kill, v_kill); @@ -1009,7 +1016,7 @@ BMEdge *BM_vert_collapse_faces(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, float l_iter = e_kill->l; do { if (l_iter->v == tv && l_iter->next->v == v_kill) { - void *src[2]; + const void *src[2]; BMLoop *tvloop = l_iter; BMLoop *kvloop = l_iter->next; @@ -1100,8 +1107,9 @@ BMEdge *BM_vert_collapse_faces(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, float * * \return The New Edge */ -BMEdge *BM_vert_collapse_edge(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, - const bool do_del, const bool kill_degenerate_faces) +BMEdge *BM_vert_collapse_edge( + BMesh *bm, BMEdge *e_kill, BMVert *v_kill, + const bool do_del, const bool kill_degenerate_faces) { /* nice example implementation but we want loops to have their customdata * accounted for */ @@ -1148,7 +1156,7 @@ BMEdge *BM_vert_collapse_edge(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, * </pre> * * \param e The edge to split. - * \param v One of the vertices in \a e and defines the the "from" end of the splitting operation, + * \param v One of the vertices in \a e and defines the "from" end of the splitting operation, * the new vertex will be \a fac of the way from \a v to the other end. * \param r_e The newly created edge. * \return The new vertex. @@ -1350,8 +1358,9 @@ bool BM_face_validate(BMFace *face, FILE *err) * * \note #BM_edge_rotate_check must have already run. */ -void BM_edge_calc_rotate(BMEdge *e, const bool ccw, - BMLoop **r_l1, BMLoop **r_l2) +void BM_edge_calc_rotate( + BMEdge *e, const bool ccw, + BMLoop **r_l1, BMLoop **r_l2) { BMVert *v1, *v2; BMFace *fa, *fb; @@ -1513,8 +1522,9 @@ bool BM_edge_rotate_check_degenerate(BMEdge *e, BMLoop *l1, BMLoop *l2) return true; } -bool BM_edge_rotate_check_beauty(BMEdge *e, - BMLoop *l1, BMLoop *l2) +bool BM_edge_rotate_check_beauty( + BMEdge *e, + BMLoop *l1, BMLoop *l2) { /* Stupid check for now: * Could compare angles of surrounding edges @@ -1640,3 +1650,9 @@ BMVert *BM_face_loop_separate(BMesh *bm, BMLoop *sl) { return bmesh_urmv_loop(bm, sl); } + +BMVert *BM_face_loop_separate_multi( + BMesh *bm, BMLoop **larr, int larr_len) +{ + return bmesh_urmv_loop_multi(bm, larr, larr_len); +} diff --git a/source/blender/bmesh/intern/bmesh_mods.h b/source/blender/bmesh/intern/bmesh_mods.h index 59aee323bba..1b826b1e0b2 100644 --- a/source/blender/bmesh/intern/bmesh_mods.h +++ b/source/blender/bmesh/intern/bmesh_mods.h @@ -35,24 +35,29 @@ bool BM_disk_dissolve(BMesh *bm, BMVert *v); BMFace *BM_faces_join_pair(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e, const bool do_del); -BMFace *BM_face_split(BMesh *bm, BMFace *f, - BMLoop *l_a, BMLoop *l_b, - BMLoop **r_l, - BMEdge *example, const bool no_double); - -BMFace *BM_face_split_n(BMesh *bm, BMFace *f, - BMLoop *l_a, BMLoop *l_b, - float cos[][3], int n, - BMLoop **r_l, BMEdge *example); - -bool BM_face_split_edgenet(BMesh *bm, BMFace *f, - BMEdge **edge_net, const int edge_net_len, - BMFace ***r_face_arr, int *r_face_arr_len); - -BMEdge *BM_vert_collapse_faces(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, float fac, - const bool do_del, const bool join_faces, const bool kill_degenerate_faces); -BMEdge *BM_vert_collapse_edge(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, - const bool do_del, const bool kill_degenerate_faces); +BMFace *BM_face_split( + BMesh *bm, BMFace *f, + BMLoop *l_a, BMLoop *l_b, + BMLoop **r_l, + BMEdge *example, const bool no_double); + +BMFace *BM_face_split_n( + BMesh *bm, BMFace *f, + BMLoop *l_a, BMLoop *l_b, + float cos[][3], int n, +BMLoop **r_l, BMEdge *example); + +bool BM_face_split_edgenet( + BMesh *bm, BMFace *f, + BMEdge **edge_net, const int edge_net_len, + BMFace ***r_face_arr, int *r_face_arr_len); + +BMEdge *BM_vert_collapse_faces( + BMesh *bm, BMEdge *e_kill, BMVert *v_kill, float fac, + const bool do_del, const bool join_faces, const bool kill_degenerate_faces); +BMEdge *BM_vert_collapse_edge( + BMesh *bm, BMEdge *e_kill, BMVert *v_kill, + const bool do_del, const bool kill_degenerate_faces); BMVert *BM_edge_split(BMesh *bm, BMEdge *e, BMVert *v, BMEdge **r_e, float percent); @@ -61,13 +66,16 @@ BMVert *BM_edge_split_n(BMesh *bm, BMEdge *e, int numcuts, BMVert **r_varr); bool BM_face_validate(BMFace *face, FILE *err); -void BM_edge_calc_rotate(BMEdge *e, const bool ccw, - BMLoop **r_l1, BMLoop **r_l2); +void BM_edge_calc_rotate( + BMEdge *e, const bool ccw, + BMLoop **r_l1, BMLoop **r_l2); bool BM_edge_rotate_check(BMEdge *e); -bool BM_edge_rotate_check_degenerate(BMEdge *e, - BMLoop *l1, BMLoop *l2); -bool BM_edge_rotate_check_beauty(BMEdge *e, - BMLoop *l1, BMLoop *l2); +bool BM_edge_rotate_check_degenerate( + BMEdge *e, + BMLoop *l1, BMLoop *l2); +bool BM_edge_rotate_check_beauty( + BMEdge *e, + BMLoop *l1, BMLoop *l2); BMEdge *BM_edge_rotate(BMesh *bm, BMEdge *e, const bool ccw, const short check_flag); /* flags for BM_edge_rotate */ @@ -81,5 +89,7 @@ enum { BMVert *BM_face_vert_separate(BMesh *bm, BMFace *sf, BMVert *sv); BMVert *BM_face_loop_separate(BMesh *bm, BMLoop *sl); +BMVert *BM_face_loop_separate_multi( + BMesh *bm, BMLoop **larr, int larr_len); #endif /* __BMESH_MODS_H__ */ diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index d8be55ce7c2..3e814948ade 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -104,6 +104,7 @@ static BMOpDefine bmo_smooth_vert_def = { "smooth_vert", /* slots_in */ {{"verts", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT}}, /* input vertices */ + {"factor", BMO_OP_SLOT_FLT}, /* smoothing factor */ {"mirror_clip_x", BMO_OP_SLOT_BOOL}, /* set vertices close to the x axis before the operation to 0 */ {"mirror_clip_y", BMO_OP_SLOT_BOOL}, /* set vertices close to the y axis before the operation to 0 */ {"mirror_clip_z", BMO_OP_SLOT_BOOL}, /* set vertices close to the z axis before the operation to 0 */ @@ -159,6 +160,28 @@ static BMOpDefine bmo_recalc_face_normals_def = { }; /* + * Planar Faces. + * + * Iteratively flatten faces. + */ +static BMOpDefine bmo_planar_faces_def = { + "planar_faces", + /* slots_in */ + {{"faces", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, /* input geometry. */ + {"iterations", BMO_OP_SLOT_INT}, + {"factor", BMO_OP_SLOT_FLT}, /* planar factor */ + {{'\0'}}, + }, + /* slots_out */ + {{"geom.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT | BM_EDGE | BM_FACE}}, /* output slot, computed boundary geometry. */ + {{'\0'}}, + }, + bmo_planar_faces_exec, + (BMO_OPTYPE_FLAG_SELECT_FLUSH | + BMO_OPTYPE_FLAG_SELECT_VALIDATE), +}; + +/* * Region Extend. * * used to implement the select more/less tools. @@ -172,8 +195,9 @@ static BMOpDefine bmo_region_extend_def = { "region_extend", /* slots_in */ {{"geom", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT | BM_EDGE | BM_FACE}}, /* input geometry */ - {"use_constrict", BMO_OP_SLOT_BOOL}, /* find boundary inside the regions, not outside. */ + {"use_contract", BMO_OP_SLOT_BOOL}, /* find boundary inside the regions, not outside. */ {"use_faces", BMO_OP_SLOT_BOOL}, /* extend from faces instead of edges */ + {"use_face_step", BMO_OP_SLOT_BOOL}, /* step over connected faces */ {{'\0'}}, }, /* slots_out */ @@ -357,6 +381,7 @@ static BMOpDefine bmo_collapse_def = { "collapse", /* slots_in */ {{"edges", BMO_OP_SLOT_ELEMENT_BUF, {BM_EDGE}}, /* input edges */ + {"uvs", BMO_OP_SLOT_BOOL}, /* also collapse UVs and such */ {{'\0'}}, }, {{{'\0'}}}, /* no output */ @@ -484,17 +509,19 @@ static BMOpDefine bmo_create_vert_def = { * Join Triangles. * * Tries to intelligently join triangles according - * to various settings and stuff. + * to angle threshold and delimiters. */ static BMOpDefine bmo_join_triangles_def = { "join_triangles", /* slots_in */ {{"faces", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, /* input geometry. */ + {"cmp_seam", BMO_OP_SLOT_BOOL}, {"cmp_sharp", BMO_OP_SLOT_BOOL}, {"cmp_uvs", BMO_OP_SLOT_BOOL}, {"cmp_vcols", BMO_OP_SLOT_BOOL}, {"cmp_materials", BMO_OP_SLOT_BOOL}, - {"limit", BMO_OP_SLOT_FLT}, + {"angle_face_threshold", BMO_OP_SLOT_FLT}, + {"angle_shape_threshold", BMO_OP_SLOT_FLT}, {{'\0'}}, }, /* slots_out */ @@ -931,6 +958,28 @@ static BMOpDefine bmo_connect_verts_def = { }; /* + * Connect Verts to form Convex Faces. + * + * Ensures all faces are convex **faces**. + */ +static BMOpDefine bmo_connect_verts_concave_def = { + "connect_verts_concave", + /* slots_in */ + {{"faces", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, + {{'\0'}}, + }, + /* slots_out */ + {{"edges.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_EDGE}}, + {"faces.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, + {{'\0'}}, + }, + bmo_connect_verts_concave_exec, + (BMO_OPTYPE_FLAG_UNTAN_MULTIRES | + BMO_OPTYPE_FLAG_NORMALS_CALC | + BMO_OPTYPE_FLAG_SELECT_FLUSH), +}; + +/* * Connect Verts Across non Planer Faces. * * Split faces by connecting edges along non planer **faces**. @@ -1675,8 +1724,10 @@ static BMOpDefine bmo_bevel_def = { {"offset_type", BMO_OP_SLOT_INT}, /* how to measure offset (enum) */ {"segments", BMO_OP_SLOT_INT}, /* number of segments in bevel */ {"profile", BMO_OP_SLOT_FLT}, /* profile shape, 0->1 (.5=>round) */ - {"vertex_only", BMO_OP_SLOT_BOOL}, /* only bevel vertices, not edges */ + {"vertex_only", BMO_OP_SLOT_BOOL}, /* only bevel vertices, not edges */ + {"clamp_overlap", BMO_OP_SLOT_BOOL}, /* do not allow beveled edges/vertices to overlap each other */ {"material", BMO_OP_SLOT_INT}, /* material for bevel faces, -1 means get from adjacent faces */ + {"loop_slide", BMO_OP_SLOT_BOOL}, /* prefer to slide along edges to having even widths */ {{'\0'}}, }, /* slots_out */ @@ -1816,6 +1867,27 @@ static BMOpDefine bmo_inset_region_def = { }; /* + * Edgeloop Offset. + * + * Creates edge loops based on simple edge-outset method. + */ +static BMOpDefine bmo_offset_edgeloops_def = { + "offset_edgeloops", + /* slots_in */ + {{"edges", BMO_OP_SLOT_ELEMENT_BUF, {BM_EDGE}}, /* input faces */ + {"use_cap_endpoint", BMO_OP_SLOT_BOOL}, + {{'\0'}}, + }, + /* slots_out */ + {{"edges.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_EDGE}}, /* output faces */ + {{'\0'}}, + }, + bmo_offset_edgeloops_exec, + (BMO_OPTYPE_FLAG_NORMALS_CALC | + BMO_OPTYPE_FLAG_SELECT_FLUSH), +}; + +/* * Wire Frame. * * Makes a wire-frame copy of faces. @@ -1947,6 +2019,7 @@ const BMOpDefine *bmo_opdefines[] = { &bmo_collapse_def, &bmo_collapse_uvs_def, &bmo_connect_verts_def, + &bmo_connect_verts_concave_def, &bmo_connect_verts_nonplanar_def, &bmo_connect_vert_pair_def, &bmo_contextual_create_def, @@ -1970,6 +2043,7 @@ const BMOpDefine *bmo_opdefines[] = { &bmo_duplicate_def, &bmo_holes_fill_def, &bmo_face_attribute_fill_def, + &bmo_offset_edgeloops_def, &bmo_edgeloop_fill_def, &bmo_edgenet_fill_def, &bmo_edgenet_prepare_def, @@ -1989,6 +2063,7 @@ const BMOpDefine *bmo_opdefines[] = { &bmo_pointmerge_facedata_def, &bmo_poke_def, &bmo_recalc_face_normals_def, + &bmo_planar_faces_def, &bmo_region_extend_def, &bmo_remove_doubles_def, &bmo_reverse_colors_def, diff --git a/source/blender/bmesh/intern/bmesh_operator_api.h b/source/blender/bmesh/intern/bmesh_operator_api.h index 287aafc8f9f..14e9bf81be7 100644 --- a/source/blender/bmesh/intern/bmesh_operator_api.h +++ b/source/blender/bmesh/intern/bmesh_operator_api.h @@ -273,9 +273,10 @@ BMOpSlot *BMO_slot_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *identif (op_dst)->slots_dst, slot_name_dst, \ (op_dst)->arena) -void _bmo_slot_copy(BMOpSlot slot_args_src[BMO_OP_MAX_SLOTS], const char *slot_name_src, - BMOpSlot slot_args_dst[BMO_OP_MAX_SLOTS], const char *slot_name_dst, - struct MemArena *arena_dst); +void _bmo_slot_copy( + BMOpSlot slot_args_src[BMO_OP_MAX_SLOTS], const char *slot_name_src, + BMOpSlot slot_args_dst[BMO_OP_MAX_SLOTS], const char *slot_name_dst, + struct MemArena *arena_dst); /* del "context" slot values, used for operator too */ enum { @@ -301,6 +302,8 @@ typedef enum { BMO_DELIM_NORMAL = 1 << 0, BMO_DELIM_MATERIAL = 1 << 1, BMO_DELIM_SEAM = 1 << 2, + BMO_DELIM_SHARP = 1 << 3, + BMO_DELIM_UV = 1 << 4, } BMO_Delimit; void BMO_op_flag_enable(BMesh *bm, BMOperator *op, const int op_flag); @@ -335,11 +338,12 @@ void BMO_slot_mat3_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_na void BMO_mesh_flag_disable_all(BMesh *bm, BMOperator *op, const char htype, const short oflag); -void BMO_mesh_selected_remap(BMesh *bm, - BMOpSlot *slot_vert_map, - BMOpSlot *slot_edge_map, - BMOpSlot *slot_face_map, - const bool check_select); +void BMO_mesh_selected_remap( + BMesh *bm, + BMOpSlot *slot_vert_map, + BMOpSlot *slot_edge_map, + BMOpSlot *slot_face_map, + const bool check_select); /* copies the values from another slot to the end of the output slot */ #define BMO_slot_buffer_append(op_src, slots_src, slot_name_src, \ @@ -347,53 +351,64 @@ void BMO_mesh_selected_remap(BMesh *bm, _bmo_slot_buffer_append((op_src)->slots_src, slot_name_src, \ (op_dst)->slots_dst, slot_name_dst, \ (op_dst)->arena) -void _bmo_slot_buffer_append(BMOpSlot slot_args_dst[BMO_OP_MAX_SLOTS], const char *slot_name_dst, - BMOpSlot slot_args_src[BMO_OP_MAX_SLOTS], const char *slot_name_src, - struct MemArena *arena_dst); +void _bmo_slot_buffer_append( + BMOpSlot slot_args_dst[BMO_OP_MAX_SLOTS], const char *slot_name_dst, + BMOpSlot slot_args_src[BMO_OP_MAX_SLOTS], const char *slot_name_src, + struct MemArena *arena_dst); /* puts every element of type 'type' (which is a bitmask) with tool * flag 'flag', into a slot. */ -void BMO_slot_buffer_from_enabled_flag(BMesh *bm, BMOperator *op, - BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char htype, const short oflag); +void BMO_slot_buffer_from_enabled_flag( + BMesh *bm, BMOperator *op, + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char htype, const short oflag); /* puts every element of type 'type' (which is a bitmask) without tool * flag 'flag', into a slot. */ -void BMO_slot_buffer_from_disabled_flag(BMesh *bm, BMOperator *op, - BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char htype, const short oflag); +void BMO_slot_buffer_from_disabled_flag( + BMesh *bm, BMOperator *op, + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char htype, const short oflag); /* tool-flags all elements inside an element slot array with flag flag. */ -void BMO_slot_buffer_flag_enable(BMesh *bm, - BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char htype, const short oflag); +void BMO_slot_buffer_flag_enable( + BMesh *bm, + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char htype, const short oflag); /* clears tool-flag flag from all elements inside a slot array. */ -void BMO_slot_buffer_flag_disable(BMesh *bm, - BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char htype, const short oflag); +void BMO_slot_buffer_flag_disable( + BMesh *bm, + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char htype, const short oflag); /* tool-flags all elements inside an element slot array with flag flag. */ -void BMO_slot_buffer_hflag_enable(BMesh *bm, - BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char htype, const char hflag, const bool do_flush); +void BMO_slot_buffer_hflag_enable( + BMesh *bm, + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char htype, const char hflag, const bool do_flush); /* clears tool-flag flag from all elements inside a slot array. */ -void BMO_slot_buffer_hflag_disable(BMesh *bm, - BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char htype, const char hflag, const bool do_flush); +void BMO_slot_buffer_hflag_disable( + BMesh *bm, + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char htype, const char hflag, const bool do_flush); /* puts every element of type 'type' (which is a bitmask) with header * flag 'flag', into a slot. note: ignores hidden elements * (e.g. elements with header flag BM_ELEM_HIDDEN set).*/ -void BMO_slot_buffer_from_enabled_hflag(BMesh *bm, BMOperator *op, - BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char htype, const char hflag); +void BMO_slot_buffer_from_enabled_hflag( + BMesh *bm, BMOperator *op, + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char htype, const char hflag); /* puts every element of type 'type' (which is a bitmask) without * header flag 'flag', into a slot. note: ignores hidden elements * (e.g. elements with header flag BM_ELEM_HIDDEN set).*/ -void BMO_slot_buffer_from_disabled_hflag(BMesh *bm, BMOperator *op, - BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char htype, const char hflag); +void BMO_slot_buffer_from_disabled_hflag( + BMesh *bm, BMOperator *op, + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char htype, const char hflag); + +void BMO_slot_buffer_from_array(BMOperator *op, BMOpSlot *slot, BMHeader **ele_buffer, int ele_buffer_len); void BMO_slot_buffer_from_single(BMOperator *op, BMOpSlot *slot, BMHeader *ele); void *BMO_slot_buffer_get_single(BMOpSlot *slot); @@ -403,19 +418,23 @@ void *BMO_slot_buffer_get_single(BMOpSlot *slot); int BMO_slot_buffer_count(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name); int BMO_slot_map_count(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name); -void BMO_slot_map_insert(BMOperator *op, BMOpSlot *slot, - const void *element, const void *data); +void BMO_slot_map_insert( + BMOperator *op, BMOpSlot *slot, + const void *element, const void *data); /* flags all elements in a mapping. note that the mapping must only have * bmesh elements in it.*/ -void BMO_slot_map_to_flag(BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], - const char *slot_name, const char hflag, const short oflag); +void BMO_slot_map_to_flag( + BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], + const char *slot_name, const char hflag, const short oflag); -void *BMO_slot_buffer_alloc(BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], - const char *slot_name, const int len); +void *BMO_slot_buffer_alloc( + BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], + const char *slot_name, const int len); -void BMO_slot_buffer_from_all(BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], - const char *slot_name, const char htype); +void BMO_slot_buffer_from_all( + BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], + const char *slot_name, const char htype); /** * This part of the API is used to iterate over element buffer or @@ -464,9 +483,10 @@ typedef struct BMOIter { void *BMO_slot_buffer_get_first(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name); -void *BMO_iter_new(BMOIter *iter, - BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char restrictmask); +void *BMO_iter_new( + BMOIter *iter, + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char restrictmask); void *BMO_iter_step(BMOIter *iter); void **BMO_iter_map_value_p(BMOIter *iter); @@ -477,10 +497,14 @@ int BMO_iter_map_value_int(BMOIter *iter); bool BMO_iter_map_value_bool(BMOIter *iter); #define BMO_ITER(ele, iter, slot_args, slot_name, restrict_flag) \ - for (ele = BMO_iter_new(iter, slot_args, slot_name, restrict_flag); ele; ele = BMO_iter_step(iter)) - -/******************* Inlined Functions********************/ -typedef void (*opexec)(BMesh *bm, BMOperator *op); + for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BMO_iter_new(iter, slot_args, slot_name, restrict_flag); \ + ele; \ + BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BMO_iter_step(iter)) + +#define BMO_ITER_INDEX(ele, iter, slot_args, slot_name, restrict_flag, i_) \ + for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BMO_iter_new(iter, slot_args, slot_name, restrict_flag), i_ = 0; \ + ele; \ + BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BMO_iter_step(iter), i_++) extern const int BMO_OPSLOT_TYPEINFO[BMO_OP_SLOT_TOTAL_TYPES]; diff --git a/source/blender/bmesh/intern/bmesh_operator_api_inline.h b/source/blender/bmesh/intern/bmesh_operator_api_inline.h index 2b78b775723..4f995e08b9c 100644 --- a/source/blender/bmesh/intern/bmesh_operator_api_inline.h +++ b/source/blender/bmesh/intern/bmesh_operator_api_inline.h @@ -38,55 +38,67 @@ * ghash or a mapping slot to do it. */ /* flags 15 and 16 (1 << 14 and 1 << 15) are reserved for bmesh api use */ +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2) BLI_INLINE short _bmo_elem_flag_test(BMesh *bm, BMFlagLayer *oflags, const short oflag) { return oflags[bm->stackdepth - 1].f & oflag; } +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2) BLI_INLINE bool _bmo_elem_flag_test_bool(BMesh *bm, BMFlagLayer *oflags, const short oflag) { return (oflags[bm->stackdepth - 1].f & oflag) != 0; } +ATTR_NONNULL(1, 2) BLI_INLINE void _bmo_elem_flag_enable(BMesh *bm, BMFlagLayer *oflags, const short oflag) { oflags[bm->stackdepth - 1].f |= oflag; } +ATTR_NONNULL(1, 2) BLI_INLINE void _bmo_elem_flag_disable(BMesh *bm, BMFlagLayer *oflags, const short oflag) { oflags[bm->stackdepth - 1].f &= (short)~oflag; } +ATTR_NONNULL(1, 2) BLI_INLINE void _bmo_elem_flag_set(BMesh *bm, BMFlagLayer *oflags, const short oflag, int val) { if (val) oflags[bm->stackdepth - 1].f |= oflag; else oflags[bm->stackdepth - 1].f &= (short)~oflag; } +ATTR_NONNULL(1, 2) BLI_INLINE void _bmo_elem_flag_toggle(BMesh *bm, BMFlagLayer *oflags, const short oflag) { oflags[bm->stackdepth - 1].f ^= oflag; } -BLI_INLINE void BMO_slot_map_int_insert(BMOperator *op, BMOpSlot *slot, - void *element, const int val) +ATTR_NONNULL(1, 2) +BLI_INLINE void BMO_slot_map_int_insert( + BMOperator *op, BMOpSlot *slot, + void *element, const int val) { union { void *ptr; int val; } t = {NULL}; BLI_assert(slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_INT); BMO_slot_map_insert(op, slot, element, ((t.val = val), t.ptr)); } -BLI_INLINE void BMO_slot_map_bool_insert(BMOperator *op, BMOpSlot *slot, - void *element, const bool val) +ATTR_NONNULL(1, 2) +BLI_INLINE void BMO_slot_map_bool_insert( + BMOperator *op, BMOpSlot *slot, + void *element, const bool val) { union { void *ptr; bool val; } t = {NULL}; BLI_assert(slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_BOOL); BMO_slot_map_insert(op, slot, element, ((t.val = val), t.ptr)); } -BLI_INLINE void BMO_slot_map_float_insert(BMOperator *op, BMOpSlot *slot, - void *element, const float val) +ATTR_NONNULL(1, 2) +BLI_INLINE void BMO_slot_map_float_insert( + BMOperator *op, BMOpSlot *slot, + void *element, const float val) { union { void *ptr; float val; } t = {NULL}; BLI_assert(slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_FLT); @@ -99,15 +111,19 @@ BLI_INLINE void BMO_slot_map_float_insert(BMOperator *op, BMOpSlot *slot, * do NOT use these for non-operator-api-allocated memory! instead * use BMO_slot_map_data_get and BMO_slot_map_insert, which copies the data. */ -BLI_INLINE void BMO_slot_map_ptr_insert(BMOperator *op, BMOpSlot *slot, - const void *element, void *val) +ATTR_NONNULL(1, 2) +BLI_INLINE void BMO_slot_map_ptr_insert( + BMOperator *op, BMOpSlot *slot, + const void *element, void *val) { BLI_assert(slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_INTERNAL); BMO_slot_map_insert(op, slot, element, val); } -BLI_INLINE void BMO_slot_map_elem_insert(BMOperator *op, BMOpSlot *slot, - const void *element, void *val) +ATTR_NONNULL(1, 2) +BLI_INLINE void BMO_slot_map_elem_insert( + BMOperator *op, BMOpSlot *slot, + const void *element, void *val) { BLI_assert(slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_ELEM); BMO_slot_map_insert(op, slot, element, val); @@ -115,25 +131,30 @@ BLI_INLINE void BMO_slot_map_elem_insert(BMOperator *op, BMOpSlot *slot, /* no values */ -BLI_INLINE void BMO_slot_map_empty_insert(BMOperator *op, BMOpSlot *slot, - const void *element) +ATTR_NONNULL(1, 2) +BLI_INLINE void BMO_slot_map_empty_insert( + BMOperator *op, BMOpSlot *slot, + const void *element) { BLI_assert(slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_EMPTY); BMO_slot_map_insert(op, slot, element, NULL); } +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE bool BMO_slot_map_contains(BMOpSlot *slot, const void *element) { BLI_assert(slot->slot_type == BMO_OP_SLOT_MAPPING); return BLI_ghash_haskey(slot->data.ghash, element); } +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE void **BMO_slot_map_data_get(BMOpSlot *slot, const void *element) { return BLI_ghash_lookup_p(slot->data.ghash, element); } +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE float BMO_slot_map_float_get(BMOpSlot *slot, const void *element) { void **data; @@ -148,6 +169,7 @@ BLI_INLINE float BMO_slot_map_float_get(BMOpSlot *slot, const void *element) } } +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE int BMO_slot_map_int_get(BMOpSlot *slot, const void *element) { void **data; @@ -162,6 +184,7 @@ BLI_INLINE int BMO_slot_map_int_get(BMOpSlot *slot, const void *element) } } +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE bool BMO_slot_map_bool_get(BMOpSlot *slot, const void *element) { void **data; @@ -176,6 +199,7 @@ BLI_INLINE bool BMO_slot_map_bool_get(BMOpSlot *slot, const void *element) } } +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE void *BMO_slot_map_ptr_get(BMOpSlot *slot, const void *element) { void **val = BMO_slot_map_data_get(slot, element); @@ -185,6 +209,7 @@ BLI_INLINE void *BMO_slot_map_ptr_get(BMOpSlot *slot, const void *element) return NULL; } +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE void *BMO_slot_map_elem_get(BMOpSlot *slot, const void *element) { void **val = (void **) BMO_slot_map_data_get(slot, element); diff --git a/source/blender/bmesh/intern/bmesh_operators.c b/source/blender/bmesh/intern/bmesh_operators.c index b041c010c22..e65f3e38900 100644 --- a/source/blender/bmesh/intern/bmesh_operators.c +++ b/source/blender/bmesh/intern/bmesh_operators.c @@ -35,7 +35,7 @@ #include "BLI_mempool.h" #include "BLI_listbase.h" -#include "BLF_translation.h" +#include "BLT_translation.h" #include "bmesh.h" #include "intern/bmesh_private.h" @@ -281,9 +281,10 @@ BMOpSlot *BMO_slot_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *identif * define used. * Copies data from one slot to another. */ -void _bmo_slot_copy(BMOpSlot slot_args_src[BMO_OP_MAX_SLOTS], const char *slot_name_src, - BMOpSlot slot_args_dst[BMO_OP_MAX_SLOTS], const char *slot_name_dst, - struct MemArena *arena_dst) +void _bmo_slot_copy( + BMOpSlot slot_args_src[BMO_OP_MAX_SLOTS], const char *slot_name_src, + BMOpSlot slot_args_dst[BMO_OP_MAX_SLOTS], const char *slot_name_dst, + struct MemArena *arena_dst) { BMOpSlot *slot_src = BMO_slot_get(slot_args_src, slot_name_src); BMOpSlot *slot_dst = BMO_slot_get(slot_args_dst, slot_name_dst); @@ -543,8 +544,9 @@ void BMO_slot_vec_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_nam * */ -static int bmo_mesh_flag_count(BMesh *bm, const char htype, const short oflag, - const bool test_for_enabled) +static int bmo_mesh_flag_count( + BMesh *bm, const char htype, const short oflag, + const bool test_for_enabled) { const char iter_types[3] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, @@ -602,11 +604,12 @@ void BMO_mesh_flag_disable_all(BMesh *bm, BMOperator *UNUSED(op), const char hty } } -void BMO_mesh_selected_remap(BMesh *bm, - BMOpSlot *slot_vert_map, - BMOpSlot *slot_edge_map, - BMOpSlot *slot_face_map, - const bool check_select) +void BMO_mesh_selected_remap( + BMesh *bm, + BMOpSlot *slot_vert_map, + BMOpSlot *slot_edge_map, + BMOpSlot *slot_face_map, + const bool check_select) { if (bm->selected.first) { BMEditSelection *ese, *ese_next; @@ -663,8 +666,9 @@ int BMO_slot_map_count(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_na /* inserts a key/value mapping into a mapping slot. note that it copies the * value, it doesn't store a reference to it. */ -void BMO_slot_map_insert(BMOperator *op, BMOpSlot *slot, - const void *element, const void *data) +void BMO_slot_map_insert( + BMOperator *op, BMOpSlot *slot, + const void *element, const void *data) { (void) op; /* Ignored in release builds. */ @@ -717,8 +721,9 @@ void *bmo_slot_buffer_grow(BMesh *bm, BMOperator *op, int slot_code, int totadd) } #endif -void BMO_slot_map_to_flag(BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char htype, const short oflag) +void BMO_slot_map_to_flag( + BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char htype, const short oflag) { GHashIterator gh_iter; BMOpSlot *slot = BMO_slot_get(slot_args, slot_name); @@ -759,8 +764,9 @@ void *BMO_slot_buffer_alloc(BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS] * * Copies all elements of a certain type into an operator slot. */ -void BMO_slot_buffer_from_all(BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], - const char *slot_name, const char htype) +void BMO_slot_buffer_from_all( + BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], + const char *slot_name, const char htype) { BMOpSlot *output = BMO_slot_get(slot_args, slot_name); int totelement = 0, i = 0; @@ -809,9 +815,10 @@ void BMO_slot_buffer_from_all(BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_ * Copies elements of a certain type, which have a certain header flag * enabled/disabled into a slot for an operator. */ -static void bmo_slot_buffer_from_hflag(BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char htype, const char hflag, - const bool test_for_enabled) +static void bmo_slot_buffer_from_hflag( + BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char htype, const char hflag, + const bool test_for_enabled) { BMOpSlot *output = BMO_slot_get(slot_args, slot_name); int totelement = 0, i = 0; @@ -872,16 +879,18 @@ static void bmo_slot_buffer_from_hflag(BMesh *bm, BMOperator *op, BMOpSlot slot_ } } -void BMO_slot_buffer_from_enabled_hflag(BMesh *bm, BMOperator *op, - BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char htype, const char hflag) +void BMO_slot_buffer_from_enabled_hflag( + BMesh *bm, BMOperator *op, + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char htype, const char hflag) { bmo_slot_buffer_from_hflag(bm, op, slot_args, slot_name, htype, hflag, true); } -void BMO_slot_buffer_from_disabled_hflag(BMesh *bm, BMOperator *op, - BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char htype, const char hflag) +void BMO_slot_buffer_from_disabled_hflag( + BMesh *bm, BMOperator *op, + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char htype, const char hflag) { bmo_slot_buffer_from_hflag(bm, op, slot_args, slot_name, htype, hflag, false); } @@ -900,6 +909,21 @@ void BMO_slot_buffer_from_single(BMOperator *op, BMOpSlot *slot, BMHeader *ele) *slot->data.buf = ele; } +void BMO_slot_buffer_from_array(BMOperator *op, BMOpSlot *slot, BMHeader **ele_buffer, int ele_buffer_len) +{ + BMO_ASSERT_SLOT_IN_OP(slot, op); + BLI_assert(slot->slot_type == BMO_OP_SLOT_ELEMENT_BUF); + BLI_assert(slot->len == 0 || slot->len == ele_buffer_len); + + if (slot->data.buf == NULL) { + slot->data.buf = BLI_memarena_alloc(op->arena, sizeof(*slot->data.buf) * ele_buffer_len); + } + + slot->len = ele_buffer_len; + memcpy(slot->data.buf, ele_buffer, ele_buffer_len * sizeof(*slot->data.buf)); +} + + void *BMO_slot_buffer_get_single(BMOpSlot *slot) { BLI_assert(slot->slot_type == BMO_OP_SLOT_ELEMENT_BUF); @@ -912,9 +936,10 @@ void *BMO_slot_buffer_get_single(BMOpSlot *slot) /** * Copies the values from another slot to the end of the output slot. */ -void _bmo_slot_buffer_append(BMOpSlot slot_args_dst[BMO_OP_MAX_SLOTS], const char *slot_name_dst, - BMOpSlot slot_args_src[BMO_OP_MAX_SLOTS], const char *slot_name_src, - struct MemArena *arena_dst) +void _bmo_slot_buffer_append( + BMOpSlot slot_args_dst[BMO_OP_MAX_SLOTS], const char *slot_name_dst, + BMOpSlot slot_args_src[BMO_OP_MAX_SLOTS], const char *slot_name_src, + struct MemArena *arena_dst) { BMOpSlot *slot_dst = BMO_slot_get(slot_args_dst, slot_name_dst); BMOpSlot *slot_src = BMO_slot_get(slot_args_src, slot_name_src); @@ -949,10 +974,11 @@ void _bmo_slot_buffer_append(BMOpSlot slot_args_dst[BMO_OP_MAX_SLOTS], const cha * Copies elements of a certain type, which have a certain flag set * into an output slot for an operator. */ -static void bmo_slot_buffer_from_flag(BMesh *bm, BMOperator *op, - BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char htype, const short oflag, - const bool test_for_enabled) +static void bmo_slot_buffer_from_flag( + BMesh *bm, BMOperator *op, + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char htype, const short oflag, + const bool test_for_enabled) { BMOpSlot *slot = BMO_slot_get(slot_args, slot_name); int totelement, i = 0; @@ -1011,16 +1037,18 @@ static void bmo_slot_buffer_from_flag(BMesh *bm, BMOperator *op, } } -void BMO_slot_buffer_from_enabled_flag(BMesh *bm, BMOperator *op, - BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char htype, const short oflag) +void BMO_slot_buffer_from_enabled_flag( + BMesh *bm, BMOperator *op, + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char htype, const short oflag) { bmo_slot_buffer_from_flag(bm, op, slot_args, slot_name, htype, oflag, true); } -void BMO_slot_buffer_from_disabled_flag(BMesh *bm, BMOperator *op, - BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char htype, const short oflag) +void BMO_slot_buffer_from_disabled_flag( + BMesh *bm, BMOperator *op, + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char htype, const short oflag) { bmo_slot_buffer_from_flag(bm, op, slot_args, slot_name, htype, oflag, false); } @@ -1031,9 +1059,10 @@ void BMO_slot_buffer_from_disabled_flag(BMesh *bm, BMOperator *op, * Header Flags elements in a slots buffer, automatically * using the selection API where appropriate. */ -void BMO_slot_buffer_hflag_enable(BMesh *bm, - BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char htype, const char hflag, const bool do_flush) +void BMO_slot_buffer_hflag_enable( + BMesh *bm, + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char htype, const char hflag, const bool do_flush) { BMOpSlot *slot = BMO_slot_get(slot_args, slot_name); BMElem **data = (BMElem **)slot->data.buf; @@ -1067,9 +1096,10 @@ void BMO_slot_buffer_hflag_enable(BMesh *bm, * Removes flags from elements in a slots buffer, automatically * using the selection API where appropriate. */ -void BMO_slot_buffer_hflag_disable(BMesh *bm, - BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char htype, const char hflag, const bool do_flush) +void BMO_slot_buffer_hflag_disable( + BMesh *bm, + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char htype, const char hflag, const bool do_flush) { BMOpSlot *slot = BMO_slot_get(slot_args, slot_name); BMElem **data = (BMElem **)slot->data.buf; @@ -1101,9 +1131,10 @@ void BMO_slot_buffer_hflag_disable(BMesh *bm, * * Flags elements in a slots buffer */ -void BMO_slot_buffer_flag_enable(BMesh *bm, - BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char htype, const short oflag) +void BMO_slot_buffer_flag_enable( + BMesh *bm, + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char htype, const short oflag) { BMOpSlot *slot = BMO_slot_get(slot_args, slot_name); BMHeader **data = slot->data.p; @@ -1125,9 +1156,10 @@ void BMO_slot_buffer_flag_enable(BMesh *bm, * * Removes flags from elements in a slots buffer */ -void BMO_slot_buffer_flag_disable(BMesh *bm, - BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char htype, const short oflag) +void BMO_slot_buffer_flag_disable( + BMesh *bm, + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char htype, const short oflag) { BMOpSlot *slot = BMO_slot_get(slot_args, slot_name); BMHeader **data = (BMHeader **)slot->data.buf; @@ -1379,9 +1411,10 @@ void *BMO_slot_buffer_get_first(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char * \param restrictmask restricts the iteration to certain element types * (e.g. combination of BM_VERT, BM_EDGE, BM_FACE), if iterating * over an element buffer (not a mapping). */ -void *BMO_iter_new(BMOIter *iter, - BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, - const char restrictmask) +void *BMO_iter_new( + BMOIter *iter, + BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, + const char restrictmask) { BMOpSlot *slot = BMO_slot_get(slot_args, slot_name); @@ -1636,6 +1669,7 @@ static int BMO_opcode_from_opname_check(const char *opname) * * **Element Buffer** (#BMO_OP_SLOT_ELEMENT_BUF) * - `e` - single element vert/edge/face (use with #BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE). + * - `eb` - elem buffer, take an array and a length. * - `av` - all verts * - `ae` - all edges * - `af` - all faces @@ -1756,12 +1790,23 @@ bool BMO_op_vinitf(BMesh *bm, BMOperator *op, const int flag, const char *_fmt, state = true; break; } - case 'e': /* single vert/edge/face */ + case 'e': { - BMHeader *ele = va_arg(vlist, void *); BMOpSlot *slot = BMO_slot_get(op->slots_in, slot_name); - BMO_slot_buffer_from_single(op, slot, ele); + if (NEXT_CHAR(fmt) == 'b') { + BMHeader **ele_buffer = va_arg(vlist, void *); + int ele_buffer_len = va_arg(vlist, int); + + BMO_slot_buffer_from_array(op, slot, ele_buffer, ele_buffer_len); + fmt++; + } + else { + /* single vert/edge/face */ + BMHeader *ele = va_arg(vlist, void *); + + BMO_slot_buffer_from_single(op, slot, ele); + } state = true; break; diff --git a/source/blender/bmesh/intern/bmesh_operators.h b/source/blender/bmesh/intern/bmesh_operators.h index b3d97947cab..d9961e589da 100644 --- a/source/blender/bmesh/intern/bmesh_operators.h +++ b/source/blender/bmesh/intern/bmesh_operators.h @@ -33,10 +33,10 @@ /*quad innervert values*/ enum { - SUBD_INNERVERT, - SUBD_PATH, - SUBD_FAN, - SUBD_STRAIGHT_CUT + SUBD_CORNER_INNERVERT, + SUBD_CORNER_PATH, + SUBD_CORNER_FAN, + SUBD_CORNER_STRAIGHT_CUT }; /* aligned with PROP_SMOOTH and friends */ @@ -46,6 +46,7 @@ enum { SUBD_FALLOFF_ROOT, SUBD_FALLOFF_SHARP, SUBD_FALLOFF_LIN, + SUBD_FALLOFF_INVSQUARE = 7, /* matching PROP_INVSQUARE */ }; enum { @@ -82,6 +83,7 @@ enum { SIMFACE_PERIMETER, SIMFACE_NORMAL, SIMFACE_COPLANAR, + SIMFACE_SMOOTH, #ifdef WITH_FREESTYLE SIMFACE_FREESTYLE #endif @@ -129,14 +131,15 @@ extern const BMOpDefine *bmo_opdefines[]; extern const int bmo_opdefines_total; /*------specific operator helper functions-------*/ -void BM_mesh_esubdivide(BMesh *bm, const char edge_hflag, - const float smooth, const short smooth_falloff, const bool use_smooth_even, - const float fractal, const float along_normal, - const int numcuts, - const int seltype, const int cornertype, - const short use_single_edge, const short use_grid_fill, - const short use_only_quads, - const int seed); +void BM_mesh_esubdivide( + BMesh *bm, const char edge_hflag, + const float smooth, const short smooth_falloff, const bool use_smooth_even, + const float fractal, const float along_normal, + const int numcuts, + const int seltype, const int cornertype, + const short use_single_edge, const short use_grid_fill, + const short use_only_quads, + const int seed); #include "intern/bmesh_operator_api_inline.h" diff --git a/source/blender/bmesh/intern/bmesh_operators_private.h b/source/blender/bmesh/intern/bmesh_operators_private.h index 9c1b7085835..5548ee7c361 100644 --- a/source/blender/bmesh/intern/bmesh_operators_private.h +++ b/source/blender/bmesh/intern/bmesh_operators_private.h @@ -41,6 +41,7 @@ void bmo_bridge_loops_exec(BMesh *bm, BMOperator *op); void bmo_collapse_exec(BMesh *bm, BMOperator *op); void bmo_collapse_uvs_exec(BMesh *bm, BMOperator *op); void bmo_connect_verts_exec(BMesh *bm, BMOperator *op); +void bmo_connect_verts_concave_exec(BMesh *bm, BMOperator *op); void bmo_connect_verts_nonplanar_exec(BMesh *bm, BMOperator *op); void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op); void bmo_contextual_create_exec(BMesh *bm, BMOperator *op); @@ -81,6 +82,8 @@ void bmo_pointmerge_exec(BMesh *bm, BMOperator *op); void bmo_pointmerge_facedata_exec(BMesh *bm, BMOperator *op); void bmo_recalc_face_normals_exec(BMesh *bm, BMOperator *op); void bmo_poke_exec(BMesh *bm, BMOperator *op); +void bmo_offset_edgeloops_exec(BMesh *bm, BMOperator *op); +void bmo_planar_faces_exec(BMesh *bm, BMOperator *op); void bmo_region_extend_exec(BMesh *bm, BMOperator *op); void bmo_remove_doubles_exec(BMesh *bm, BMOperator *op); void bmo_reverse_colors_exec(BMesh *bm, BMOperator *op); diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c index 9a1914b5596..b0eddf73960 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.c +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -37,6 +37,7 @@ #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_polyfill2d.h" +#include "BLI_polyfill2d_beautify.h" #include "bmesh.h" #include "bmesh_tools.h" @@ -98,11 +99,12 @@ static float bm_face_calc_poly_normal(const BMFace *f, float n[3]) /** * \brief COMPUTE POLY NORMAL (BMFace) * - * Same as #calc_poly_normal and #bm_face_calc_poly_normal + * Same as #bm_face_calc_poly_normal * but takes an array of vertex locations. */ -static float bm_face_calc_poly_normal_vertex_cos(BMFace *f, float r_no[3], - float const (*vertexCos)[3]) +static float bm_face_calc_poly_normal_vertex_cos( + BMFace *f, float r_no[3], + float const (*vertexCos)[3]) { BMLoop *l_first = BM_FACE_FIRST_LOOP(f); BMLoop *l_iter = l_first; @@ -126,8 +128,9 @@ static float bm_face_calc_poly_normal_vertex_cos(BMFace *f, float r_no[3], /** * \brief COMPUTE POLY CENTER (BMFace) */ -static void bm_face_calc_poly_center_mean_vertex_cos(BMFace *f, float r_cent[3], - float const (*vertexCos)[3]) +static void bm_face_calc_poly_center_mean_vertex_cos( + BMFace *f, float r_cent[3], + float const (*vertexCos)[3]) { BMLoop *l_first = BM_FACE_FIRST_LOOP(f); BMLoop *l_iter = l_first; @@ -213,9 +216,6 @@ float BM_face_calc_area(BMFace *f) if (f->len == 3) { area = area_tri_v3(verts[0], verts[1], verts[2]); } - else if (f->len == 4) { - area = area_quad_v3(verts[0], verts[1], verts[2], verts[3]); - } else { area = area_poly_v3((const float (*)[3])verts, f->len); } @@ -422,6 +422,43 @@ void BM_edge_normals_update(BMEdge *e) BM_vert_normal_update(e->v2); } +bool BM_vert_normal_update_ex(BMVert *v, const char hflag, float r_no[3]) +{ + /* TODO, we can normalize each edge only once, then compare with previous edge */ + + BMIter liter; + BMLoop *l; + int len = 0; + + zero_v3(r_no); + + BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { + if (BM_elem_flag_test(l->f, hflag)) { + float vec1[3], vec2[3], fac; + + /* Same calculation used in BM_mesh_normals_update */ + sub_v3_v3v3(vec1, l->v->co, l->prev->v->co); + sub_v3_v3v3(vec2, l->next->v->co, l->v->co); + normalize_v3(vec1); + normalize_v3(vec2); + + fac = saacos(-dot_v3v3(vec1, vec2)); + + madd_v3_v3fl(r_no, l->f->no, fac); + + len++; + } + } + + if (len) { + normalize_v3(r_no); + return true; + } + else { + return false; + } +} + /** * update a vert normal (but not the faces incident on it) */ @@ -431,12 +468,13 @@ void BM_vert_normal_update(BMVert *v) BMIter liter; BMLoop *l; - float vec1[3], vec2[3], fac; int len = 0; zero_v3(v->no); BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { + float vec1[3], vec2[3], fac; + /* Same calculation used in BM_mesh_normals_update */ sub_v3_v3v3(vec1, l->v->co, l->prev->v->co); sub_v3_v3v3(vec2, l->next->v->co, l->v->co); @@ -512,8 +550,9 @@ void BM_face_normal_update(BMFace *f) } /* exact same as 'BM_face_calc_normal' but accepts vertex coords */ -float BM_face_calc_normal_vcos(BMesh *bm, BMFace *f, float r_no[3], - float const (*vertexCos)[3]) +float BM_face_calc_normal_vcos( + BMesh *bm, BMFace *f, float r_no[3], + float const (*vertexCos)[3]) { BMLoop *l; @@ -571,8 +610,9 @@ float BM_face_calc_normal_subset(BMLoop *l_first, BMLoop *l_last, float r_no[3]) } /* exact same as 'BM_face_calc_normal' but accepts vertex coords */ -void BM_face_calc_center_mean_vcos(BMesh *bm, BMFace *f, float r_cent[3], - float const (*vertexCos)[3]) +void BM_face_calc_center_mean_vcos( + BMesh *bm, BMFace *f, float r_cent[3], + float const (*vertexCos)[3]) { /* must have valid index data */ BLI_assert((bm->elem_index_dirty & BM_VERT) == 0); @@ -666,49 +706,25 @@ static bool line_crosses_v2f(const float v1[2], const float v2[2], const float v */ bool BM_face_point_inside_test(BMFace *f, const float co[3]) { - int ax, ay; - float co2[2], cent[2] = {0.0f, 0.0f}, out[2] = {FLT_MAX * 0.5f, FLT_MAX * 0.5f}; + float axis_mat[3][3]; + float (*projverts)[2] = BLI_array_alloca(projverts, f->len); + + float co_2d[2]; BMLoop *l_iter; - BMLoop *l_first; - int crosses = 0; - float onepluseps = 1.0f + (float)FLT_EPSILON * 150.0f; + int i; if (is_zero_v3(f->no)) BM_face_normal_update(f); - - /* find best projection of face XY, XZ or YZ: barycentric weights of - * the 2d projected coords are the same and faster to compute - * - * this probably isn't all that accurate, but it has the advantage of - * being fast (especially compared to projecting into the face orientation) - */ - axis_dominant_v3(&ax, &ay, f->no); - - co2[0] = co[ax]; - co2[1] = co[ay]; - - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - cent[0] += l_iter->v->co[ax]; - cent[1] += l_iter->v->co[ay]; - } while ((l_iter = l_iter->next) != l_first); - - mul_v2_fl(cent, 1.0f / (float)f->len); - - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - float v1[2], v2[2]; - - v1[0] = (l_iter->prev->v->co[ax] - cent[0]) * onepluseps + cent[0]; - v1[1] = (l_iter->prev->v->co[ay] - cent[1]) * onepluseps + cent[1]; - - v2[0] = (l_iter->v->co[ax] - cent[0]) * onepluseps + cent[0]; - v2[1] = (l_iter->v->co[ay] - cent[1]) * onepluseps + cent[1]; - - crosses += line_crosses_v2f(v1, v2, co2, out) != 0; - } while ((l_iter = l_iter->next) != l_first); - - return crosses % 2 != 0; + + axis_dominant_v3_to_m3(axis_mat, f->no); + + mul_v2_m3v3(co_2d, axis_mat, co); + + for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f); i < f->len; i++, l_iter = l_iter->next) { + mul_v2_m3v3(projverts[i], axis_mat, l_iter->v->co); + } + + return isect_point_poly_v2(co_2d, (const float (*)[2])projverts, f->len, false); } /** @@ -728,20 +744,24 @@ bool BM_face_point_inside_test(BMFace *f, const float co[3]) * * \note use_tag tags new flags and edges. */ -void BM_face_triangulate(BMesh *bm, BMFace *f, - BMFace **r_faces_new, - int *r_faces_new_tot, - MemArena *sf_arena, - const int quad_method, - const int ngon_method, - const bool use_tag) +void BM_face_triangulate( + BMesh *bm, BMFace *f, + BMFace **r_faces_new, + int *r_faces_new_tot, + BMEdge **r_edges_new, + int *r_edges_new_tot, + const int quad_method, + const int ngon_method, + const bool use_tag, + MemArena *pf_arena, + + /* use for MOD_TRIANGULATE_NGON_BEAUTY only! */ + struct Heap *pf_heap, struct EdgeHash *pf_ehash) { BMLoop *l_iter, *l_first, *l_new; BMFace *f_new; - int orig_f_len = f->len; int nf_i = 0; - BMEdge **edge_array; - int edge_array_len; + int ne_i = 0; bool use_beauty = (ngon_method == MOD_TRIANGULATE_NGON_BEAUTY); BLI_assert(BM_face_is_normal_valid(f)); @@ -767,38 +787,39 @@ void BM_face_triangulate(BMesh *bm, BMFace *f, break; } case MOD_TRIANGULATE_QUAD_SHORTEDGE: - { - BMLoop *l_v3, *l_v4; - float d1, d2; - - l_v1 = l_first; - l_v2 = l_first->next->next; - l_v3 = l_first->next; - l_v4 = l_first->prev; - - d1 = len_squared_v3v3(l_v1->v->co, l_v2->v->co); - d2 = len_squared_v3v3(l_v3->v->co, l_v4->v->co); - - if (d2 < d1) { - l_v1 = l_v3; - l_v2 = l_v4; - } - break; - } case MOD_TRIANGULATE_QUAD_BEAUTY: default: { BMLoop *l_v3, *l_v4; - float cost; + bool split_24; l_v1 = l_first->next; l_v2 = l_first->next->next; l_v3 = l_first->prev; l_v4 = l_first; - cost = BM_verts_calc_rotate_beauty(l_v1->v, l_v2->v, l_v3->v, l_v4->v, 0, 0); + if (quad_method == MOD_TRIANGULATE_QUAD_SHORTEDGE) { + float d1, d2; + d1 = len_squared_v3v3(l_v4->v->co, l_v2->v->co); + d2 = len_squared_v3v3(l_v1->v->co, l_v3->v->co); + split_24 = ((d2 - d1) > 0.0f); + } + else { + /* first check if the quad is concave on either diagonal */ + const int flip_flag = is_quad_flip_v3(l_v1->v->co, l_v2->v->co, l_v3->v->co, l_v4->v->co); + if (UNLIKELY(flip_flag & (1 << 0))) { + split_24 = true; + } + else if (UNLIKELY(flip_flag & (1 << 1))) { + split_24 = false; + } + else { + split_24 = (BM_verts_calc_rotate_beauty(l_v1->v, l_v2->v, l_v3->v, l_v4->v, 0, 0) > 0.0f); + } + } - if (cost < 0.0f) { + /* named confusingly, l_v1 is in fact the second vertex */ + if (split_24) { l_v1 = l_v4; //l_v2 = l_v2; } @@ -821,6 +842,9 @@ void BM_face_triangulate(BMesh *bm, BMFace *f, if (r_faces_new) { r_faces_new[nf_i++] = f_new; } + if (r_edges_new) { + r_edges_new[ne_i++] = l_new->e; + } } else if (f->len > 4) { @@ -832,28 +856,31 @@ void BM_face_triangulate(BMesh *bm, BMFace *f, const int last_tri = f->len - 3; int i; - axis_dominant_v3_to_m3(axis_mat, f->no); + axis_dominant_v3_to_m3_negate(axis_mat, f->no); for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f); i < f->len; i++, l_iter = l_iter->next) { loops[i] = l_iter; mul_v2_m3v3(projverts[i], axis_mat, l_iter->v->co); } - BLI_polyfill_calc_arena((const float (*)[2])projverts, f->len, -1, tris, - sf_arena); + BLI_polyfill_calc_arena((const float (*)[2])projverts, f->len, 1, tris, + pf_arena); if (use_beauty) { - edge_array = BLI_array_alloca(edge_array, orig_f_len - 3); - edge_array_len = 0; + BLI_polyfill_beautify( + (const float (*)[2])projverts, f->len, tris, + pf_arena, pf_heap, pf_ehash); } + BLI_memarena_clear(pf_arena); + /* loop over calculated triangles and create new geometry */ for (i = 0; i < totfilltri; i++) { /* the order is reverse, otherwise the normal is flipped */ BMLoop *l_tri[3] = { - loops[tris[i][2]], + loops[tris[i][0]], loops[tris[i][1]], - loops[tris[i][0]]}; + loops[tris[i][2]]}; BMVert *v_tri[3] = { l_tri[0]->v, @@ -880,8 +907,7 @@ void BM_face_triangulate(BMesh *bm, BMFace *f, } } - /* we know any edge that we create and _isnt_ */ - if (use_beauty || use_tag) { + if (use_tag || r_edges_new) { /* new faces loops */ l_iter = l_first = l_new; do { @@ -891,14 +917,11 @@ void BM_face_triangulate(BMesh *bm, BMFace *f, bool is_new_edge = (l_iter == l_iter->radial_next); if (is_new_edge) { - if (use_beauty) { - edge_array[edge_array_len] = e; - edge_array_len++; - } - if (use_tag) { BM_elem_flag_enable(e, BM_ELEM_TAG); - + } + if (r_edges_new) { + r_edges_new[ne_i++] = e; } } /* note, never disable tag's */ @@ -906,83 +929,22 @@ void BM_face_triangulate(BMesh *bm, BMFace *f, } } - if ((!use_beauty) || (!r_faces_new)) { + { /* we can't delete the real face, because some of the callers expect it to remain valid. * so swap data and delete the last created tri */ bmesh_face_swap_data(f, f_new); BM_face_kill(bm, f_new); } - - if (use_beauty) { - BLI_assert(edge_array_len <= orig_f_len - 3); - - BM_mesh_beautify_fill(bm, edge_array, edge_array_len, 0, 0, 0, 0); - - if (r_faces_new) { - /* beautify deletes and creates new faces - * we need to re-populate the r_faces_new array - * with the new faces - */ - int i; - - -#define FACE_USED_TEST(f) (BM_elem_index_get(f) == -2) -#define FACE_USED_SET(f) BM_elem_index_set(f, -2) - - nf_i = 0; - for (i = 0; i < edge_array_len; i++) { - BMFace *f_a, *f_b; - BMEdge *e = edge_array[i]; -#ifndef NDEBUG - const bool ok = BM_edge_face_pair(e, &f_a, &f_b); - BLI_assert(ok); -#else - BM_edge_face_pair(e, &f_a, &f_b); -#endif - - if (FACE_USED_TEST(f_a) == false) { - FACE_USED_SET(f_a); /* set_dirty */ - - if (nf_i < edge_array_len) { - r_faces_new[nf_i++] = f_a; - } - else { - f_new = f_a; - break; - } - } - - if (FACE_USED_TEST(f_b) == false) { - FACE_USED_SET(f_b); /* set_dirty */ - - if (nf_i < edge_array_len) { - r_faces_new[nf_i++] = f_b; - } - else { - f_new = f_b; - break; - } - } - } - -#undef FACE_USED_TEST -#undef FACE_USED_SET - - /* nf_i doesn't include the last face */ - BLI_assert(nf_i <= orig_f_len - 3); - - /* we can't delete the real face, because some of the callers expect it to remain valid. - * so swap data and delete the last created tri */ - bmesh_face_swap_data(f, f_new); - BM_face_kill(bm, f_new); - } - } } bm->elem_index_dirty |= BM_FACE; if (r_faces_new_tot) { *r_faces_new_tot = nf_i; } + + if (r_edges_new_tot) { + *r_edges_new_tot = ne_i; + } } /** @@ -1299,7 +1261,7 @@ void BM_bmesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptr l_arr = BLI_memarena_alloc(arena, sizeof(*l_arr) * efa->len); projverts = BLI_memarena_alloc(arena, sizeof(*projverts) * efa->len); - axis_dominant_v3_to_m3(axis_mat, efa->no); + axis_dominant_v3_to_m3_negate(axis_mat, efa->no); j = 0; l_iter = l_first = BM_FACE_FIRST_LOOP(efa); @@ -1309,15 +1271,15 @@ void BM_bmesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptr j++; } while ((l_iter = l_iter->next) != l_first); - BLI_polyfill_calc_arena((const float (*)[2])projverts, efa->len, -1, tris, arena); + BLI_polyfill_calc_arena((const float (*)[2])projverts, efa->len, 1, tris, arena); for (j = 0; j < totfilltri; j++) { BMLoop **l_ptr = looptris[i++]; unsigned int *tri = tris[j]; - l_ptr[0] = l_arr[tri[2]]; + l_ptr[0] = l_arr[tri[0]]; l_ptr[1] = l_arr[tri[1]]; - l_ptr[2] = l_arr[tri[0]]; + l_ptr[2] = l_arr[tri[2]]; } BLI_memarena_clear(arena); diff --git a/source/blender/bmesh/intern/bmesh_polygon.h b/source/blender/bmesh/intern/bmesh_polygon.h index 91e649edb16..582b4248c7d 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.h +++ b/source/blender/bmesh/intern/bmesh_polygon.h @@ -27,40 +27,51 @@ * \ingroup bmesh */ +struct EdgeHash; +struct Heap; + #include "BLI_compiler_attrs.h" void BM_bmesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot); void BM_face_calc_tessellation(const BMFace *f, BMLoop **r_loops, unsigned int (*r_index)[3]); float BM_face_calc_normal(const BMFace *f, float r_no[3]) ATTR_NONNULL(); -float BM_face_calc_normal_vcos(BMesh *bm, BMFace *f, float r_no[3], - float const (*vertexCos)[3]) ATTR_NONNULL(); +float BM_face_calc_normal_vcos( + BMesh *bm, BMFace *f, float r_no[3], + float const (*vertexCos)[3]) ATTR_NONNULL(); float BM_face_calc_normal_subset(BMLoop *l_first, BMLoop *l_last, float r_no[3]) ATTR_NONNULL(); float BM_face_calc_area(BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); float BM_face_calc_perimeter(BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); void BM_face_calc_plane(BMFace *f, float r_plane[3]) ATTR_NONNULL(); void BM_face_calc_center_bounds(BMFace *f, float center[3]) ATTR_NONNULL(); void BM_face_calc_center_mean(BMFace *f, float center[3]) ATTR_NONNULL(); -void BM_face_calc_center_mean_vcos(BMesh *bm, BMFace *f, float r_cent[3], - float const (*vertexCos)[3]) ATTR_NONNULL(); +void BM_face_calc_center_mean_vcos( + BMesh *bm, BMFace *f, float r_cent[3], + float const (*vertexCos)[3]) ATTR_NONNULL(); void BM_face_calc_center_mean_weighted(BMFace *f, float center[3]) ATTR_NONNULL(); void BM_face_normal_update(BMFace *f) ATTR_NONNULL(); void BM_edge_normals_update(BMEdge *e) ATTR_NONNULL(); +bool BM_vert_normal_update_ex(BMVert *v, const char hflag, float r_no[3]); void BM_vert_normal_update(BMVert *v) ATTR_NONNULL(); void BM_vert_normal_update_all(BMVert *v) ATTR_NONNULL(); void BM_face_normal_flip(BMesh *bm, BMFace *f) ATTR_NONNULL(); bool BM_face_point_inside_test(BMFace *f, const float co[3]) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -void BM_face_triangulate(BMesh *bm, BMFace *f, - BMFace **r_faces_new, - int *r_faces_new_tot, - struct MemArena *sf_arena, - const int quad_method, const int ngon_method, - const bool use_tag) ATTR_NONNULL(1, 2); +void BM_face_triangulate( + BMesh *bm, BMFace *f, + BMFace **r_faces_new, + int *r_faces_new_tot, + BMEdge **r_edges_new, + int *r_edges_new_tot, + const int quad_method, const int ngon_method, + const bool use_tag, + struct MemArena *pf_arena, + struct Heap *pf_heap, struct EdgeHash *pf_ehash + ) ATTR_NONNULL(1, 2); void BM_face_splits_check_legal(BMesh *bm, BMFace *f, BMLoop *(*loops)[2], int len) ATTR_NONNULL(); void BM_face_splits_check_optimal(BMFace *f, BMLoop *(*loops)[2], int len) ATTR_NONNULL(); diff --git a/source/blender/bmesh/intern/bmesh_private.h b/source/blender/bmesh/intern/bmesh_private.h index 102a677943b..814015a2a74 100644 --- a/source/blender/bmesh/intern/bmesh_private.h +++ b/source/blender/bmesh/intern/bmesh_private.h @@ -54,6 +54,7 @@ int bmesh_elem_check(void *element, const char htype); #endif int bmesh_radial_length(const BMLoop *l); +int bmesh_disk_count_ex(const BMVert *v, const int count_max); int bmesh_disk_count(const BMVert *v); /** diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c index 685e5443583..1a8ea1e3a0d 100644 --- a/source/blender/bmesh/intern/bmesh_queries.c +++ b/source/blender/bmesh/intern/bmesh_queries.c @@ -38,6 +38,8 @@ #include "BLI_linklist.h" #include "BLI_stackdefines.h" +#include "BKE_customdata.h" + #include "bmesh.h" #include "intern/bmesh_private.h" @@ -193,7 +195,7 @@ bool BM_vert_pair_share_face_check( BMFace *f; BM_ITER_ELEM (f, &iter, v_a, BM_FACES_OF_VERT) { - if (BM_vert_in_face(f, v_b)) { + if (BM_vert_in_face(v_b, f)) { return true; } } @@ -202,6 +204,26 @@ bool BM_vert_pair_share_face_check( return false; } +bool BM_vert_pair_share_face_check_cb( + BMVert *v_a, BMVert *v_b, + bool (*test_fn)(BMFace *, void *user_data), void *user_data) +{ + if (v_a->e && v_b->e) { + BMIter iter; + BMFace *f; + + BM_ITER_ELEM (f, &iter, v_a, BM_FACES_OF_VERT) { + if (test_fn(f, user_data)) { + if (BM_vert_in_face(v_b, f)) { + return true; + } + } + } + } + + return false; +} + /** * Given 2 verts, find the smallest face they share and give back both loops. */ @@ -235,6 +257,36 @@ BMFace *BM_vert_pair_share_face_by_len( return f_cur; } +BMFace *BM_edge_pair_share_face_by_len( + BMEdge *e_a, BMEdge *e_b, + BMLoop **r_l_a, BMLoop **r_l_b, + const bool allow_adjacent) +{ + BMLoop *l_cur_a = NULL, *l_cur_b = NULL; + BMFace *f_cur = NULL; + + if (e_a->l && e_b->l) { + BMIter iter; + BMLoop *l_a, *l_b; + + BM_ITER_ELEM (l_a, &iter, e_a, BM_LOOPS_OF_EDGE) { + if ((f_cur == NULL) || (l_a->f->len < f_cur->len)) { + l_b = BM_face_edge_share_loop(l_a->f, e_b); + if (l_b && (allow_adjacent || !BM_loop_is_adjacent(l_a, l_b))) { + f_cur = l_a->f; + l_cur_a = l_a; + l_cur_b = l_b; + } + } + } + } + + *r_l_a = l_cur_a; + *r_l_b = l_cur_b; + + return f_cur; +} + static float bm_face_calc_split_dot(BMLoop *l_a, BMLoop *l_b) { float no[2][3]; @@ -250,6 +302,37 @@ static float bm_face_calc_split_dot(BMLoop *l_a, BMLoop *l_b) } /** + * Check if a point is inside the corner defined by a loop + * (within the 2 planes defined by the loops corner & face normal). + * + * \return signed, squared distance to the loops planes, less than 0.0 when outside. + */ +float BM_loop_point_side_of_loop_test(const BMLoop *l, const float co[3]) +{ + const float *axis = l->f->no; + return dist_signed_squared_to_corner_v3v3v3(co, l->prev->v->co, l->v->co, l->next->v->co, axis); +} + +/** + * Check if a point is inside the edge defined by a loop + * (within the plane defined by the loops edge & face normal). + * + * \return signed, squared distance to the edge plane, less than 0.0 when outside. + */ +float BM_loop_point_side_of_edge_test(const BMLoop *l, const float co[3]) +{ + const float *axis = l->f->no; + float dir[3]; + float plane[4]; + + sub_v3_v3v3(dir, l->next->v->co, l->v->co); + cross_v3_v3v3(plane, axis, dir); + + plane[3] = -dot_v3v3(plane, l->v->co); + return dist_signed_squared_to_plane_v3(co, plane); +} + +/** * Given 2 verts, find a face they share that has the lowest angle across these verts and give back both loops. * * This can be better then #BM_vert_pair_share_face_by_len because concave splits are ranked lowest. @@ -311,7 +394,7 @@ BMLoop *BM_vert_find_first_loop(BMVert *v) { BMEdge *e; - if (!v || !v->e) + if (!v->e) return NULL; e = bmesh_disk_faceedge_find_first(v->e, v); @@ -325,7 +408,7 @@ BMLoop *BM_vert_find_first_loop(BMVert *v) /** * Returns true if the vertex is used in a given face. */ -bool BM_vert_in_face(BMFace *f, BMVert *v) +bool BM_vert_in_face(BMVert *v, BMFace *f) { BMLoop *l_iter, *l_first; @@ -353,7 +436,7 @@ bool BM_vert_in_face(BMFace *f, BMVert *v) * Compares the number of vertices in an array * that appear in a given face */ -int BM_verts_in_face_count(BMFace *f, BMVert **varr, int len) +int BM_verts_in_face_count(BMVert **varr, int len, BMFace *f) { BMLoop *l_iter, *l_first; @@ -397,7 +480,7 @@ int BM_verts_in_face_count(BMFace *f, BMVert **varr, int len) /** * Return true if all verts are in the face. */ -bool BM_verts_in_face(BMFace *f, BMVert **varr, int len) +bool BM_verts_in_face(BMVert **varr, int len, BMFace *f) { BMLoop *l_iter, *l_first; @@ -448,12 +531,12 @@ bool BM_verts_in_face(BMFace *f, BMVert **varr, int len) } /** - * Returns whether or not a given edge is is part of a given face. + * Returns whether or not a given edge is part of a given face. */ -bool BM_edge_in_face(BMEdge *e, BMFace *f) +bool BM_edge_in_face(const BMEdge *e, const BMFace *f) { if (e->l) { - BMLoop *l_iter, *l_first; + const BMLoop *l_iter, *l_first; l_iter = l_first = e->l; do { @@ -613,7 +696,7 @@ BMEdge *BM_vert_other_disk_edge(BMVert *v, BMEdge *e_first) /** * Returns edge length */ -float BM_edge_calc_length(BMEdge *e) +float BM_edge_calc_length(const BMEdge *e) { return len_v3v3(e->v1->co, e->v2->co); } @@ -621,7 +704,7 @@ float BM_edge_calc_length(BMEdge *e) /** * Returns edge length squared (for comparisons) */ -float BM_edge_calc_length_squared(BMEdge *e) +float BM_edge_calc_length_squared(const BMEdge *e) { return len_squared_v3v3(e->v1->co, e->v2->co); } @@ -681,9 +764,9 @@ bool BM_edge_loop_pair(BMEdge *e, BMLoop **r_la, BMLoop **r_lb) /** * Fast alternative to ``(BM_vert_edge_count(v) == 2)`` */ -bool BM_vert_is_edge_pair(BMVert *v) +bool BM_vert_is_edge_pair(const BMVert *v) { - BMEdge *e = v->e; + const BMEdge *e = v->e; if (e) { const BMDiskLink *dl = bmesh_disk_edge_link_from_vert(e, v); return (dl->next == dl->prev); @@ -694,17 +777,22 @@ bool BM_vert_is_edge_pair(BMVert *v) /** * Returns the number of edges around this vertex. */ -int BM_vert_edge_count(BMVert *v) +int BM_vert_edge_count(const BMVert *v) { return bmesh_disk_count(v); } -int BM_vert_edge_count_nonwire(BMVert *v) +int BM_vert_edge_count_ex(const BMVert *v, const int count_max) +{ + return bmesh_disk_count_ex(v, count_max); +} + +int BM_vert_edge_count_nonwire(const BMVert *v) { int count = 0; BMIter eiter; BMEdge *edge; - BM_ITER_ELEM (edge, &eiter, v, BM_EDGES_OF_VERT) { + BM_ITER_ELEM (edge, &eiter, (BMVert *)v, BM_EDGES_OF_VERT) { if (edge->l) { count++; } @@ -714,18 +802,35 @@ int BM_vert_edge_count_nonwire(BMVert *v) /** * Returns the number of faces around this edge */ -int BM_edge_face_count(BMEdge *e) +int BM_edge_face_count(const BMEdge *e) { int count = 0; if (e->l) { - BMLoop *l_iter; - BMLoop *l_first; + BMLoop *l_iter, *l_first; l_iter = l_first = e->l; + do { + count++; + } while ((l_iter = l_iter->radial_next) != l_first); + } + + return count; +} + +int BM_edge_face_count_ex(const BMEdge *e, const int count_max) +{ + int count = 0; + + if (e->l) { + BMLoop *l_iter, *l_first; + l_iter = l_first = e->l; do { count++; + if (count == count_max) { + break; + } } while ((l_iter = l_iter->radial_next) != l_first); } @@ -736,11 +841,26 @@ int BM_edge_face_count(BMEdge *e) * Returns the number of faces around this vert * length matches #BM_LOOPS_OF_VERT iterator */ -int BM_vert_face_count(BMVert *v) +int BM_vert_face_count(const BMVert *v) { return bmesh_disk_facevert_count(v); } +int BM_vert_face_count_ex(const BMVert *v, int count_max) +{ + return bmesh_disk_facevert_count_ex(v, count_max); +} + +/** + * Return true if the vertex is connected to _any_ faces. + * + * same as ``BM_vert_face_count(v) != 0`` or ``BM_vert_find_first_loop(v) == NULL`` + */ +bool BM_vert_face_check(BMVert *v) +{ + return v->e && (bmesh_disk_faceedge_find_first(v->e, v) != NULL); +} + /** * Tests whether or not the vertex is part of a wire edge. * (ie: has no faces attached to it) @@ -773,9 +893,9 @@ bool BM_vert_is_wire(const BMVert *v) */ bool BM_vert_is_manifold(const BMVert *v) { - BMEdge *e, *e_old; - BMLoop *l; - int len, count, flag; + BMEdge *e_iter, *e_first, *e_prev; + BMLoop *l_iter, *l_first; + int loop_num = 0, loop_num_region = 0, boundary_num = 0; if (v->e == NULL) { /* loose vert */ @@ -783,50 +903,150 @@ bool BM_vert_is_manifold(const BMVert *v) } /* count edges while looking for non-manifold edges */ - len = 0; - e_old = e = v->e; + e_first = e_iter = v->e; + l_first = e_iter->l ? e_iter->l : NULL; do { /* loose edge or edge shared by more than two faces, * edges with 1 face user are OK, otherwise we could * use BM_edge_is_manifold() here */ - if (e->l == NULL || bmesh_radial_length(e->l) > 2) { + if (e_iter->l == NULL || (e_iter->l != e_iter->l->radial_next->radial_next)) { return false; } - len++; - } while ((e = bmesh_disk_edge_next(e, v)) != e_old); - - count = 1; - flag = 1; - e = NULL; - e_old = v->e; - l = e_old->l; - while (e != e_old) { - l = (l->v == v) ? l->prev : l->next; - e = l->e; - count++; /* count the edges */ - - if (flag && l->radial_next == l) { - /* we've hit the edge of an open mesh, reset once */ - flag = 0; - count = 1; - e_old = e; - e = NULL; - l = e_old->l; - } - else if (l->radial_next == l) { - /* break the loop */ - e = e_old; + + /* count radial loops */ + if (e_iter->l->v == v) { + loop_num += 1; + } + + if (!BM_edge_is_boundary(e_iter)) { + /* non boundary check opposite loop */ + if (e_iter->l->radial_next->v == v) { + loop_num += 1; + } + } + else { + /* start at the boundary */ + l_first = e_iter->l; + boundary_num += 1; + /* >2 boundaries cant be manifold */ + if (boundary_num == 3) { + return false; + } + } + } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first); + + e_first = l_first->e; + l_first = (l_first->v == v) ? l_first : l_first->next; + BLI_assert(l_first->v == v); + + l_iter = l_first; + e_prev = e_first; + + do { + loop_num_region += 1; + } while (((l_iter = BM_vert_step_fan_loop(l_iter, &e_prev)) != l_first) && (l_iter != NULL)); + + return (loop_num == loop_num_region); +} + +#define LOOP_VISIT _FLAG_WALK +#define EDGE_VISIT _FLAG_WALK + +static int bm_loop_region_count__recursive(BMEdge *e, BMVert *v) +{ + BMLoop *l_iter, *l_first; + int count = 0; + + BLI_assert(!BM_ELEM_API_FLAG_TEST(e, EDGE_VISIT)); + BM_ELEM_API_FLAG_ENABLE(e, EDGE_VISIT); + + l_iter = l_first = e->l; + do { + if (l_iter->v == v) { + BMEdge *e_other = l_iter->prev->e; + if (!BM_ELEM_API_FLAG_TEST(l_iter, LOOP_VISIT)) { + BM_ELEM_API_FLAG_ENABLE(l_iter, LOOP_VISIT); + count += 1; + } + if (!BM_ELEM_API_FLAG_TEST(e_other, EDGE_VISIT)) { + count += bm_loop_region_count__recursive(e_other, v); + } + } + else if (l_iter->next->v == v) { + BMEdge *e_other = l_iter->next->e; + if (!BM_ELEM_API_FLAG_TEST(l_iter->next, LOOP_VISIT)) { + BM_ELEM_API_FLAG_ENABLE(l_iter->next, LOOP_VISIT); + count += 1; + } + if (!BM_ELEM_API_FLAG_TEST(e_other, EDGE_VISIT)) { + count += bm_loop_region_count__recursive(e_other, v); + } } else { - l = l->radial_next; + BLI_assert(0); } - } + } while ((l_iter = l_iter->radial_next) != l_first); - if (count < len) { - /* vert shared by multiple regions */ - return false; + return count; +} + +static int bm_loop_region_count__clear(BMLoop *l) +{ + int count = 0; + BMEdge *e_iter, *e_first; + + /* clear flags */ + e_iter = e_first = l->e; + do { + BM_ELEM_API_FLAG_DISABLE(e_iter, EDGE_VISIT); + if (e_iter->l) { + BMLoop *l_iter, *l_first; + l_iter = l_first = e_iter->l; + do { + if (l_iter->v == l->v) { + BM_ELEM_API_FLAG_DISABLE(l_iter, LOOP_VISIT); + count += 1; + } + } while ((l_iter = l_iter->radial_next) != l_first); + } + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, l->v)) != e_first); + + return count; +} + +/** + * The number of loops connected to this loop (not including disconnected regions). + */ +int BM_loop_region_loops_count_ex(BMLoop *l, int *r_loop_total) +{ + const int count = bm_loop_region_count__recursive(l->e, l->v); + const int count_total = bm_loop_region_count__clear(l); + if (r_loop_total) { + *r_loop_total = count_total; } + return count; +} + +#undef LOOP_VISIT +#undef EDGE_VISIT +int BM_loop_region_loops_count(BMLoop *l) +{ + return BM_loop_region_loops_count_ex(l, NULL); +} + +/** + * A version of #BM_vert_is_manifold + * which only checks if we're connected to multiple isolated regions. + */ +bool BM_vert_is_manifold_region(const BMVert *v) +{ + BMLoop *l_first = BM_vert_find_first_loop((BMVert *)v); + if (l_first) { + int count, count_total; + count = BM_loop_region_loops_count_ex(l_first, &count_total); + return (count == count_total); + } return true; } @@ -851,6 +1071,53 @@ bool BM_edge_is_convex(const BMEdge *e) return true; } +/** + * \return true when loop customdata is contiguous. + */ +bool BM_edge_is_contiguous_loop_cd( + const BMEdge *e, + const int cd_loop_type, const int cd_loop_offset) +{ + BLI_assert(cd_loop_offset != -1); + + if (e->l && e->l->radial_next != e->l) { + const BMLoop *l_base_v1 = e->l; + const BMLoop *l_base_v2 = e->l->next; + const void *l_base_cd_v1 = BM_ELEM_CD_GET_VOID_P(l_base_v1, cd_loop_offset); + const void *l_base_cd_v2 = BM_ELEM_CD_GET_VOID_P(l_base_v2, cd_loop_offset); + const BMLoop *l_iter = e->l->radial_next; + do { + const BMLoop *l_iter_v1; + const BMLoop *l_iter_v2; + const void *l_iter_cd_v1; + const void *l_iter_cd_v2; + + if (l_iter->v == l_base_v1->v) { + l_iter_v1 = l_iter; + l_iter_v2 = l_iter->next; + } + else { + l_iter_v1 = l_iter->next; + l_iter_v2 = l_iter; + } + BLI_assert((l_iter_v1->v == l_base_v1->v) && + (l_iter_v2->v == l_base_v2->v)); + + l_iter_cd_v1 = BM_ELEM_CD_GET_VOID_P(l_iter_v1, cd_loop_offset); + l_iter_cd_v2 = BM_ELEM_CD_GET_VOID_P(l_iter_v2, cd_loop_offset); + + + if ((CustomData_data_equals(cd_loop_type, l_base_cd_v1, l_iter_cd_v1) == 0) || + (CustomData_data_equals(cd_loop_type, l_base_cd_v2, l_iter_cd_v2) == 0)) + { + return false; + } + + } while ((l_iter = l_iter->radial_next) != e->l); + } + return true; +} + bool BM_vert_is_boundary(const BMVert *v) { if (v->e) { @@ -1096,8 +1363,9 @@ BMLoop *BM_face_edge_share_loop(BMFace *f, BMEdge *e) * \note This is in fact quite a simple check, mainly include this function so the intent is more obvious. * We know these 2 verts will _always_ make up the loops edge */ -void BM_edge_ordered_verts_ex(const BMEdge *edge, BMVert **r_v1, BMVert **r_v2, - const BMLoop *edge_loop) +void BM_edge_ordered_verts_ex( + const BMEdge *edge, BMVert **r_v1, BMVert **r_v2, + const BMLoop *edge_loop) { BLI_assert(edge_loop->e == edge); (void)edge; /* quiet warning in release build */ @@ -1111,6 +1379,46 @@ void BM_edge_ordered_verts(const BMEdge *edge, BMVert **r_v1, BMVert **r_v2) } /** + * \return The previous loop, over \a eps_sq distance from \a l (or \a NULL if l_stop is reached). + */ +BMLoop *BM_loop_find_prev_nodouble(BMLoop *l, BMLoop *l_stop, const float eps_sq) +{ + BMLoop *l_step = l->prev; + + BLI_assert(!ELEM(l_stop, NULL, l)); + + while (UNLIKELY(len_squared_v3v3(l->v->co, l_step->v->co) < eps_sq)) { + l_step = l_step->prev; + BLI_assert(l_step != l); + if (UNLIKELY(l_step == l_stop)) { + return NULL; + } + } + + return l_step; +} + +/** + * \return The next loop, over \a eps_sq distance from \a l (or \a NULL if l_stop is reached). + */ +BMLoop *BM_loop_find_next_nodouble(BMLoop *l, BMLoop *l_stop, const float eps_sq) +{ + BMLoop *l_step = l->next; + + BLI_assert(!ELEM(l_stop, NULL, l)); + + while (UNLIKELY(len_squared_v3v3(l->v->co, l_step->v->co) < eps_sq)) { + l_step = l_step->next; + BLI_assert(l_step != l); + if (UNLIKELY(l_step == l_stop)) { + return NULL; + } + } + + return l_step; +} + +/** * Check if the loop is convex or concave * (depends on face normal) */ @@ -1132,7 +1440,7 @@ bool BM_loop_is_convex(const BMLoop *l) * * \return angle in radians */ -float BM_loop_calc_face_angle(BMLoop *l) +float BM_loop_calc_face_angle(const BMLoop *l) { return angle_v3v3v3(l->prev->v->co, l->v->co, @@ -1147,7 +1455,7 @@ float BM_loop_calc_face_angle(BMLoop *l) * \param l The loop to calculate the normal at * \param r_normal Resulting normal */ -void BM_loop_calc_face_normal(BMLoop *l, float r_normal[3]) +void BM_loop_calc_face_normal(const BMLoop *l, float r_normal[3]) { if (normal_tri_v3(r_normal, l->prev->v->co, @@ -1169,7 +1477,7 @@ void BM_loop_calc_face_normal(BMLoop *l, float r_normal[3]) * \param l The loop to calculate the direction at * \param r_dir Resulting direction */ -void BM_loop_calc_face_direction(BMLoop *l, float r_dir[3]) +void BM_loop_calc_face_direction(const BMLoop *l, float r_dir[3]) { float v_prev[3]; float v_next[3]; @@ -1193,7 +1501,7 @@ void BM_loop_calc_face_direction(BMLoop *l, float r_dir[3]) * \param l The loop to calculate the tangent at * \param r_tangent Resulting tangent */ -void BM_loop_calc_face_tangent(BMLoop *l, float r_tangent[3]) +void BM_loop_calc_face_tangent(const BMLoop *l, float r_tangent[3]) { float v_prev[3]; float v_next[3]; @@ -1305,7 +1613,7 @@ void BM_edge_calc_face_tangent(const BMEdge *e, const BMLoop *e_loop, float r_ta * * \returns the angle in radians */ -float BM_vert_calc_edge_angle(BMVert *v) +float BM_vert_calc_edge_angle_ex(const BMVert *v, const float fallback) { BMEdge *e1, *e2; @@ -1323,22 +1631,27 @@ float BM_vert_calc_edge_angle(BMVert *v) return (float)M_PI - angle_v3v3v3(v1->co, v->co, v2->co); } else { - return DEG2RADF(90.0f); + return fallback; } } +float BM_vert_calc_edge_angle(const BMVert *v) +{ + return BM_vert_calc_edge_angle_ex(v, DEG2RADF(90.0f)); +} + /** * \note this isn't optimal to run on an array of verts, * see 'solidify_add_thickness' for a function which runs on an array. */ -float BM_vert_calc_shell_factor(BMVert *v) +float BM_vert_calc_shell_factor(const BMVert *v) { BMIter iter; BMLoop *l; float accum_shell = 0.0f; float accum_angle = 0.0f; - BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + BM_ITER_ELEM (l, &iter, (BMVert *)v, BM_LOOPS_OF_VERT) { const float face_angle = BM_loop_calc_face_angle(l); accum_shell += shell_v3v3_normalized_to_dist(v->no, l->f->no) * face_angle; accum_angle += face_angle; @@ -1353,18 +1666,18 @@ float BM_vert_calc_shell_factor(BMVert *v) } /* alternate version of #BM_vert_calc_shell_factor which only * uses 'hflag' faces, but falls back to all if none found. */ -float BM_vert_calc_shell_factor_ex(BMVert *v, const char hflag) +float BM_vert_calc_shell_factor_ex(const BMVert *v, const float no[3], const char hflag) { BMIter iter; - BMLoop *l; + const BMLoop *l; float accum_shell = 0.0f; float accum_angle = 0.0f; int tot_sel = 0, tot = 0; - BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + BM_ITER_ELEM (l, &iter, (BMVert *)v, BM_LOOPS_OF_VERT) { if (BM_elem_flag_test(l->f, hflag)) { /* <-- main difference to BM_vert_calc_shell_factor! */ const float face_angle = BM_loop_calc_face_angle(l); - accum_shell += shell_v3v3_normalized_to_dist(v->no, l->f->no) * face_angle; + accum_shell += shell_v3v3_normalized_to_dist(no, l->f->no) * face_angle; accum_angle += face_angle; tot_sel++; } @@ -1390,15 +1703,15 @@ float BM_vert_calc_shell_factor_ex(BMVert *v, const char hflag) * \note quite an obscure function. * used in bmesh operators that have a relative scale options, */ -float BM_vert_calc_mean_tagged_edge_length(BMVert *v) +float BM_vert_calc_mean_tagged_edge_length(const BMVert *v) { BMIter iter; BMEdge *e; int tot; float length = 0.0f; - BM_ITER_ELEM_INDEX (e, &iter, v, BM_EDGES_OF_VERT, tot) { - BMVert *v_other = BM_edge_other_vert(e, v); + BM_ITER_ELEM_INDEX (e, &iter, (BMVert *)v, BM_EDGES_OF_VERT, tot) { + const BMVert *v_other = BM_edge_other_vert(e, v); if (BM_elem_flag_test(v_other, BM_ELEM_TAG)) { length += BM_edge_calc_length(e); } @@ -1544,85 +1857,59 @@ BMEdge *BM_edge_find_double(BMEdge *e) */ bool BM_face_exists(BMVert **varr, int len, BMFace **r_existface) { - BMVert *v_search = varr[0]; /* we can search any of the verts in the array */ - BMIter liter; - BMLoop *l_search; - - -#if 0 - BM_ITER_ELEM (f, &viter, v_search, BM_FACES_OF_VERT) { - if (f->len == len) { - if (BM_verts_in_face(f, varr, len)) { - if (r_existface) { - *r_existface = f; - } - return true; - } - } - } - - if (r_existface) { - *r_existface = NULL; - } - return false; - -#else - - /* faster to do the flagging once, and inline */ - bool is_init = false; - bool is_found = false; - int i; - - - BM_ITER_ELEM (l_search, &liter, v_search, BM_LOOPS_OF_VERT) { - if (l_search->f->len == len) { - if (is_init == false) { - is_init = true; - for (i = 0; i < len; i++) { - BLI_assert(!BM_ELEM_API_FLAG_TEST(varr[i], _FLAG_OVERLAP)); - BM_ELEM_API_FLAG_ENABLE(varr[i], _FLAG_OVERLAP); - } - } - - is_found = true; - - { - BMLoop *l_iter; + if (varr[0]->e) { + BMEdge *e_iter, *e_first; + e_iter = e_first = varr[0]->e; - /* skip ourselves */ - l_iter = l_search->next; + /* would normally use BM_LOOPS_OF_VERT, but this runs so often, + * its faster to iterate on the data directly */ + do { + if (e_iter->l) { + BMLoop *l_iter_radial, *l_first_radial; + l_iter_radial = l_first_radial = e_iter->l; do { - if (!BM_ELEM_API_FLAG_TEST(l_iter->v, _FLAG_OVERLAP)) { - is_found = false; - break; + if ((l_iter_radial->v == varr[0]) && + (l_iter_radial->f->len == len)) + { + /* the fist 2 verts match, now check the remaining (len - 2) faces do too + * winding isn't known, so check in both directions */ + int i_walk = 2; + + if (l_iter_radial->next->v == varr[1]) { + BMLoop *l_walk = l_iter_radial->next->next; + do { + if (l_walk->v != varr[i_walk]) { + break; + } + } while ((l_walk = l_walk->next), ++i_walk != len); + } + else if (l_iter_radial->prev->v == varr[1]) { + BMLoop *l_walk = l_iter_radial->prev->prev; + do { + if (l_walk->v != varr[i_walk]) { + break; + } + } while ((l_walk = l_walk->prev), ++i_walk != len); + } + + if (i_walk == len) { + if (r_existface) { + *r_existface = l_iter_radial->f; + } + return true; + } } - } while ((l_iter = l_iter->next) != l_search); - } + } while ((l_iter_radial = l_iter_radial->radial_next) != l_first_radial); - if (is_found) { - if (r_existface) { - *r_existface = l_search->f; - } - break; } - } - } - - if (is_found == false) { - if (r_existface) { - *r_existface = NULL; - } + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, varr[0])) != e_first); } - if (is_init == true) { - for (i = 0; i < len; i++) { - BM_ELEM_API_FLAG_DISABLE(varr[i], _FLAG_OVERLAP); - } + if (r_existface) { + *r_existface = NULL; } - - return is_found; -#endif + return false; } @@ -1717,8 +2004,8 @@ bool BM_face_exists_multi(BMVert **varr, BMEdge **earr, int len) if (/* non-boundary edge */ BM_elem_flag_test(e, BM_ELEM_INTERNAL_TAG) == false && /* ...using boundary verts */ - BM_elem_flag_test(e->v1, BM_ELEM_INTERNAL_TAG) == true && - BM_elem_flag_test(e->v2, BM_ELEM_INTERNAL_TAG) == true) + BM_elem_flag_test(e->v1, BM_ELEM_INTERNAL_TAG) && + BM_elem_flag_test(e->v2, BM_ELEM_INTERNAL_TAG)) { int tot_face_tag = 0; BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) { @@ -1777,7 +2064,7 @@ bool BM_face_exists_multi_edge(BMEdge **earr, int len) * * \note The face may contain other verts \b not in \a varr. * - * \note Its possible there are more then one overlapping faces, + * \note Its possible there are more than one overlapping faces, * in this case the first one found will be assigned to \a r_f_overlap. * * \param varr Array of unordered verts. @@ -1810,7 +2097,7 @@ bool BM_face_exists_overlap(BMVert **varr, const int len, BMFace **r_f_overlap) for (i = 0; i < len; i++) { BM_ITER_ELEM (f, &viter, varr[i], BM_FACES_OF_VERT) { if (BM_ELEM_API_FLAG_TEST(f, _FLAG_OVERLAP) == 0) { - if (len <= BM_verts_in_face_count(f, varr, len)) { + if (len <= BM_verts_in_face_count(varr, len, f)) { if (r_f_overlap) *r_f_overlap = f; @@ -2061,9 +2348,10 @@ float BM_mesh_calc_volume(BMesh *bm, bool is_signed) * (having both set is supported too). * \return The number of groups found. */ -int BM_mesh_calc_face_groups(BMesh *bm, int *r_groups_array, int (**r_group_index)[2], - BMElemFilterFunc filter_fn, void *user_data, - const char hflag_test, const char htype_step) +int BM_mesh_calc_face_groups( + BMesh *bm, int *r_groups_array, int (**r_group_index)[2], + BMElemFilterFunc filter_fn, void *user_data, + const char hflag_test, const char htype_step) { #ifdef DEBUG int group_index_len = 1; @@ -2128,6 +2416,7 @@ int BM_mesh_calc_face_groups(BMesh *bm, int *r_groups_array, int (**r_group_inde } BLI_assert(ok == true); + UNUSED_VARS_NDEBUG(ok); /* manage arrays */ if (group_index_len == group_curr) { @@ -2217,9 +2506,10 @@ int BM_mesh_calc_face_groups(BMesh *bm, int *r_groups_array, int (**r_group_inde * \note Unlike #BM_mesh_calc_face_groups there is no 'htype_step' argument, * since we always walk over verts. */ -int BM_mesh_calc_edge_groups(BMesh *bm, int *r_groups_array, int (**r_group_index)[2], - BMElemFilterFunc filter_fn, void *user_data, - const char hflag_test) +int BM_mesh_calc_edge_groups( + BMesh *bm, int *r_groups_array, int (**r_group_index)[2], + BMElemFilterFunc filter_fn, void *user_data, + const char hflag_test) { #ifdef DEBUG int group_index_len = 1; @@ -2244,7 +2534,7 @@ int BM_mesh_calc_edge_groups(BMesh *bm, int *r_groups_array, int (**r_group_inde BMEdge *e; int i; - STACK_INIT(group_array, bm->totface); + STACK_INIT(group_array, bm->totedge); /* init the array */ BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) { @@ -2282,6 +2572,7 @@ int BM_mesh_calc_edge_groups(BMesh *bm, int *r_groups_array, int (**r_group_inde } BLI_assert(ok == true); + UNUSED_VARS_NDEBUG(ok); /* manage arrays */ if (group_index_len == group_curr) { @@ -2347,6 +2638,9 @@ float bmesh_subd_falloff_calc(const int falloff, float val) break; case SUBD_FALLOFF_LIN: break; + case SUBD_FALLOFF_INVSQUARE: + val = val * (2.0f - val); + break; default: BLI_assert(0); break; diff --git a/source/blender/bmesh/intern/bmesh_queries.h b/source/blender/bmesh/intern/bmesh_queries.h index 0d47633dc73..2b18a5c8641 100644 --- a/source/blender/bmesh/intern/bmesh_queries.h +++ b/source/blender/bmesh/intern/bmesh_queries.h @@ -27,18 +27,18 @@ * \ingroup bmesh */ -bool BM_vert_in_face(BMFace *f, BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -int BM_verts_in_face_count(BMFace *f, BMVert **varr, int len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -bool BM_verts_in_face(BMFace *f, BMVert **varr, int len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +bool BM_vert_in_face(BMVert *v, BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +int BM_verts_in_face_count(BMVert **varr, int len, BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +bool BM_verts_in_face(BMVert **varr, int len, BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -bool BM_edge_in_face(BMEdge *e, BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +bool BM_edge_in_face(const BMEdge *e, const BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BLI_INLINE bool BM_edge_in_loop(const BMEdge *e, const BMLoop *l) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BLI_INLINE bool BM_vert_in_edge(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BLI_INLINE bool BM_verts_in_edge(const BMVert *v1, const BMVert *v2, const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -float BM_edge_calc_length(BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -float BM_edge_calc_length_squared(BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +float BM_edge_calc_length(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +float BM_edge_calc_length_squared(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); bool BM_edge_face_pair(BMEdge *e, BMFace **r_fa, BMFace **r_fb) ATTR_NONNULL(); bool BM_edge_loop_pair(BMEdge *e, BMLoop **r_la, BMLoop **r_lb) ATTR_NONNULL(); BLI_INLINE BMVert *BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); @@ -52,6 +52,9 @@ BMLoop *BM_vert_find_first_loop(BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL( bool BM_vert_pair_share_face_check( BMVert *v_a, BMVert *v_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +bool BM_vert_pair_share_face_check_cb( + BMVert *v_a, BMVert *v_b, + bool (*test_fn)(BMFace *f, void *user_data), void *user_data) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2, 3); BMFace *BM_vert_pair_share_face_by_len( BMVert *v_a, BMVert *v_b, BMLoop **r_l_a, BMLoop **r_l_b, @@ -61,30 +64,57 @@ BMFace *BM_vert_pair_share_face_by_angle( BMLoop **r_l_a, BMLoop **r_l_b, const bool allow_adjacent) ATTR_NONNULL(); -int BM_vert_edge_count_nonwire(BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -int BM_vert_edge_count(BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -int BM_edge_face_count(BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -int BM_vert_face_count(BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +BMFace *BM_edge_pair_share_face_by_len( + BMEdge *e_a, BMEdge *e_b, + BMLoop **r_l_a, BMLoop **r_l_b, + const bool allow_adjacent) ATTR_NONNULL(); + +int BM_vert_edge_count_nonwire(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +#define BM_vert_edge_count_is_equal(v, n) (BM_vert_edge_count_ex(v, (n) + 1) == n) +#define BM_vert_edge_count_is_over(v, n) (BM_vert_edge_count_ex(v, (n) + 1) == (n) + 1) +int BM_vert_edge_count_ex(const BMVert *v, const int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +int BM_vert_edge_count(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +#define BM_edge_face_count_is_equal(e, n) (BM_edge_face_count_ex(e, (n) + 1) == n) +#define BM_edge_face_count_is_over(e, n) (BM_edge_face_count_ex(e, (n) + 1) == (n) + 1) +int BM_edge_face_count_ex(const BMEdge *e, const int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +int BM_edge_face_count(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +#define BM_vert_face_count_is_equal(v, n) (BM_vert_face_count_ex(v, (n) + 1) == n) +#define BM_vert_face_count_is_over(v, n) (BM_vert_face_count_ex(v, (n) + 1) == (n) + 1) +int BM_vert_face_count_ex(const BMVert *v, int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +int BM_vert_face_count(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BMEdge *BM_vert_other_disk_edge(BMVert *v, BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -bool BM_vert_is_edge_pair(BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +bool BM_vert_is_edge_pair(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +bool BM_vert_face_check(BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); bool BM_vert_is_wire(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BLI_INLINE bool BM_edge_is_wire(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); bool BM_vert_is_manifold(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +bool BM_vert_is_manifold_region(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BLI_INLINE bool BM_edge_is_manifold(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); bool BM_vert_is_boundary(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BLI_INLINE bool BM_edge_is_boundary(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BLI_INLINE bool BM_edge_is_contiguous(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); bool BM_edge_is_convex(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +bool BM_edge_is_contiguous_loop_cd( + const BMEdge *e, + const int cd_loop_type, const int cd_loop_offset) + ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +int BM_loop_region_loops_count_ex(BMLoop *l, int *r_loop_total) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); +int BM_loop_region_loops_count(BMLoop *l) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); bool BM_loop_is_convex(const BMLoop *l) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BLI_INLINE bool BM_loop_is_adjacent(const BMLoop *l_a, const BMLoop *l_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +float BM_loop_point_side_of_loop_test(const BMLoop *l, const float co[3]) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +float BM_loop_point_side_of_edge_test(const BMLoop *l, const float co[3]) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); + +BMLoop *BM_loop_find_prev_nodouble(BMLoop *l, BMLoop *l_stop, const float eps_sq); +BMLoop *BM_loop_find_next_nodouble(BMLoop *l, BMLoop *l_stop, const float eps_sq); -float BM_loop_calc_face_angle(BMLoop *l) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -void BM_loop_calc_face_normal(BMLoop *l, float r_normal[3]) ATTR_NONNULL(); -void BM_loop_calc_face_direction(BMLoop *l, float r_normal[3]); -void BM_loop_calc_face_tangent(BMLoop *l, float r_tangent[3]); +float BM_loop_calc_face_angle(const BMLoop *l) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +void BM_loop_calc_face_normal(const BMLoop *l, float r_normal[3]) ATTR_NONNULL(); +void BM_loop_calc_face_direction(const BMLoop *l, float r_normal[3]); +void BM_loop_calc_face_tangent(const BMLoop *l, float r_tangent[3]); float BM_edge_calc_face_angle_ex(const BMEdge *e, const float fallback) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); float BM_edge_calc_face_angle(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); @@ -92,10 +122,11 @@ float BM_edge_calc_face_angle_signed_ex(const BMEdge *e, const float fallback) float BM_edge_calc_face_angle_signed(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); void BM_edge_calc_face_tangent(const BMEdge *e, const BMLoop *e_loop, float r_tangent[3]) ATTR_NONNULL(); -float BM_vert_calc_edge_angle(BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -float BM_vert_calc_shell_factor(BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -float BM_vert_calc_shell_factor_ex(BMVert *v, const char hflag) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -float BM_vert_calc_mean_tagged_edge_length(BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +float BM_vert_calc_edge_angle(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +float BM_vert_calc_edge_angle_ex(const BMVert *v, const float fallback) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +float BM_vert_calc_shell_factor(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +float BM_vert_calc_shell_factor_ex(const BMVert *v, const float no[3], const char hflag) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +float BM_vert_calc_mean_tagged_edge_length(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BMLoop *BM_face_find_shortest_loop(BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BMLoop *BM_face_find_longest_loop(BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); @@ -126,8 +157,9 @@ BMLoop *BM_face_vert_share_loop(BMFace *f, BMVert *v) ATTR_WARN_UNUSED_RESULT AT BMLoop *BM_face_edge_share_loop(BMFace *f, BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); void BM_edge_ordered_verts(const BMEdge *edge, BMVert **r_v1, BMVert **r_v2) ATTR_NONNULL(); -void BM_edge_ordered_verts_ex(const BMEdge *edge, BMVert **r_v1, BMVert **r_v2, - const BMLoop *edge_loop) ATTR_NONNULL(); +void BM_edge_ordered_verts_ex( + const BMEdge *edge, BMVert **r_v1, BMVert **r_v2, + const BMLoop *edge_loop) ATTR_NONNULL(); bool BM_vert_is_all_edge_flag_test(const BMVert *v, const char hflag, const bool respect_hide) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); bool BM_vert_is_all_face_flag_test(const BMVert *v, const char hflag, const bool respect_hide) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); @@ -141,12 +173,16 @@ bool BM_face_is_normal_valid(const BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNU float BM_mesh_calc_volume(BMesh *bm, bool is_signed) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -int BM_mesh_calc_face_groups(BMesh *bm, int *r_groups_array, int (**r_group_index)[2], - BMElemFilterFunc filter_fn, void *user_data, - const char hflag_test, const char htype_step) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2, 3); -int BM_mesh_calc_edge_groups(BMesh *bm, int *r_groups_array, int (**r_group_index)[2], - BMElemFilterFunc filter_fn, void *user_data, - const char hflag_test) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2, 3); +int BM_mesh_calc_face_groups( + BMesh *bm, int *r_groups_array, int (**r_group_index)[2], + BMElemFilterFunc filter_fn, void *user_data, + const char hflag_test, const char htype_step) + ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2, 3); +int BM_mesh_calc_edge_groups( + BMesh *bm, int *r_groups_array, int (**r_group_index)[2], + BMElemFilterFunc filter_fn, void *user_data, + const char hflag_test) + ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2, 3); /* not really any good place to put this */ float bmesh_subd_falloff_calc(const int falloff, float val) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/bmesh/intern/bmesh_queries_inline.h b/source/blender/bmesh/intern/bmesh_queries_inline.h index 6162af46837..430ba10fb42 100644 --- a/source/blender/bmesh/intern/bmesh_queries_inline.h +++ b/source/blender/bmesh/intern/bmesh_queries_inline.h @@ -30,14 +30,16 @@ * Returns whether or not a given vertex is * is part of a given edge. */ +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE bool BM_vert_in_edge(const BMEdge *e, const BMVert *v) { return (ELEM(v, e->v1, e->v2)); } /** - * Returns whether or not a given edge is is part of a given loop. + * Returns whether or not a given edge is part of a given loop. */ +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2) BLI_INLINE bool BM_edge_in_loop(const BMEdge *e, const BMLoop *l) { return (l->e == e || l->prev->e == e); @@ -47,6 +49,7 @@ BLI_INLINE bool BM_edge_in_loop(const BMEdge *e, const BMLoop *l) * Returns whether or not two vertices are in * a given edge */ +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2, 3) BLI_INLINE bool BM_verts_in_edge(const BMVert *v1, const BMVert *v2, const BMEdge *e) { return ((e->v1 == v1 && e->v2 == v2) || @@ -57,6 +60,7 @@ BLI_INLINE bool BM_verts_in_edge(const BMVert *v1, const BMVert *v2, const BMEdg * Given a edge and one of its vertices, returns * the other vertex. */ +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2) BLI_INLINE BMVert *BM_edge_other_vert(BMEdge *e, const BMVert *v) { if (e->v1 == v) { @@ -72,6 +76,7 @@ BLI_INLINE BMVert *BM_edge_other_vert(BMEdge *e, const BMVert *v) * Tests whether or not the edge is part of a wire. * (ie: has no faces attached to it) */ +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE bool BM_edge_is_wire(const BMEdge *e) { return (e->l == NULL); @@ -83,6 +88,7 @@ BLI_INLINE bool BM_edge_is_wire(const BMEdge *e) */ #if 1 /* fast path for checking manifold */ +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE bool BM_edge_is_manifold(const BMEdge *e) { const BMLoop *l = e->l; @@ -100,6 +106,7 @@ BLI_INLINE int BM_edge_is_manifold(BMEdge *e) * Tests that the edge is manifold and * that both its faces point the same way. */ +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE bool BM_edge_is_contiguous(const BMEdge *e) { const BMLoop *l = e->l; @@ -115,6 +122,7 @@ BLI_INLINE bool BM_edge_is_contiguous(const BMEdge *e) */ #if 1 /* fast path for checking boundary */ +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE bool BM_edge_is_boundary(const BMEdge *e) { const BMLoop *l = e->l; @@ -130,6 +138,7 @@ BLI_INLINE int BM_edge_is_boundary(BMEdge *e) /** * Tests whether one loop is next to another within the same face. */ +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2) BLI_INLINE bool BM_loop_is_adjacent(const BMLoop *l_a, const BMLoop *l_b) { BLI_assert(l_a->f == l_b->f); @@ -140,6 +149,7 @@ BLI_INLINE bool BM_loop_is_adjacent(const BMLoop *l_a, const BMLoop *l_b) /** * Check if we have a single wire edge user. */ +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE bool BM_vert_is_wire_endpoint(const BMVert *v) { const BMEdge *e = v->e; diff --git a/source/blender/bmesh/intern/bmesh_structure.c b/source/blender/bmesh/intern/bmesh_structure.c index 3e8002c0192..cb302139a4c 100644 --- a/source/blender/bmesh/intern/bmesh_structure.c +++ b/source/blender/bmesh/intern/bmesh_structure.c @@ -40,19 +40,56 @@ * MISC utility functions. */ -bool bmesh_edge_swapverts(BMEdge *e, BMVert *v_orig, BMVert *v_new) +void bmesh_disk_vert_swap(BMEdge *e, BMVert *v_dst, BMVert *v_src) { - if (e->v1 == v_orig) { - e->v1 = v_new; + if (e->v1 == v_src) { + e->v1 = v_dst; e->v1_disk_link.next = e->v1_disk_link.prev = NULL; - return true; } - else if (e->v2 == v_orig) { - e->v2 = v_new; + else if (e->v2 == v_src) { + e->v2 = v_dst; e->v2_disk_link.next = e->v2_disk_link.prev = NULL; - return true; } - return false; + else { + BLI_assert(0); + } +} + +/** + * Handles all connected data, use with care. + * + * Assumes caller has setup correct state before the swap is done. + */ +void bmesh_edge_vert_swap(BMEdge *e, BMVert *v_dst, BMVert *v_src) +{ + /* swap out loops */ + if (e->l) { + BMLoop *l_iter, *l_first; + l_iter = l_first = e->l; + do { + if (l_iter->v == v_src) { + l_iter->v = v_dst; + } + else if (l_iter->next->v == v_src) { + l_iter->next->v = v_dst; + } + else { + BLI_assert(l_iter->prev->v != v_src); + } + } while ((l_iter = l_iter->radial_next) != l_first); + } + + /* swap out edges */ + bmesh_disk_vert_replace(e, v_dst, v_src); +} + +void bmesh_disk_vert_replace(BMEdge *e, BMVert *v_dst, BMVert *v_src) +{ + BLI_assert(e->v1 == v_src || e->v2 == v_src); + bmesh_disk_edge_remove(e, v_src); /* remove e from tv's disk cycle */ + bmesh_disk_vert_swap(e, v_dst, v_src); /* swap out tv for v_new in e */ + bmesh_disk_edge_append(e, v_dst); /* add e to v_dst's disk cycle */ + BLI_assert(e->v1 != e->v2); } /** @@ -88,6 +125,7 @@ bool bmesh_edge_swapverts(BMEdge *e, BMVert *v_orig, BMVert *v_new) * the disk cycle has no problems dealing with non-manifold conditions involving faces. * * Functions relating to this cycle: + * - #bmesh_disk_vert_replace * - #bmesh_disk_edge_append * - #bmesh_disk_edge_remove * - #bmesh_disk_edge_next @@ -206,13 +244,29 @@ int bmesh_disk_count(const BMVert *v) return count; } +int bmesh_disk_count_ex(const BMVert *v, const int count_max) +{ + int count = 0; + if (v->e) { + BMEdge *e_first, *e_iter; + e_iter = e_first = v->e; + do { + count++; + if (count == count_max) { + break; + } + } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first); + } + return count; +} + bool bmesh_disk_validate(int len, BMEdge *e, BMVert *v) { BMEdge *e_iter; if (!BM_vert_in_edge(e, v)) return false; - if (bmesh_disk_count(v) != len || len == 0) + if (bmesh_disk_count_ex(v, len + 1) != len || len == 0) return false; e_iter = e; @@ -236,9 +290,9 @@ bool bmesh_disk_validate(int len, BMEdge *e, BMVert *v) int bmesh_disk_facevert_count(const BMVert *v) { /* is there an edge on this vert at all */ + int count = 0; if (v->e) { BMEdge *e_first, *e_iter; - int count = 0; /* first, loop around edge */ e_first = e_iter = v->e; @@ -247,11 +301,29 @@ int bmesh_disk_facevert_count(const BMVert *v) count += bmesh_radial_facevert_count(e_iter->l, v); } } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first); - return count; } - else { - return 0; + return count; +} + +int bmesh_disk_facevert_count_ex(const BMVert *v, const int count_max) +{ + /* is there an edge on this vert at all */ + int count = 0; + if (v->e) { + BMEdge *e_first, *e_iter; + + /* first, loop around edge */ + e_first = e_iter = v->e; + do { + if (e_iter->l) { + count += bmesh_radial_facevert_count_ex(e_iter->l, v, count_max - count); + if (count == count_max) { + break; + } + } + } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first); } + return count; } /** @@ -456,6 +528,23 @@ int bmesh_radial_facevert_count(const BMLoop *l, const BMVert *v) return count; } +int bmesh_radial_facevert_count_ex(const BMLoop *l, const BMVert *v, const int count_max) +{ + const BMLoop *l_iter; + int count = 0; + l_iter = l; + do { + if (l_iter->v == v) { + count++; + if (count == count_max) { + break; + } + } + } while ((l_iter = l_iter->radial_next) != l); + + return count; +} + /** * \brief RADIAL CHECK FACE VERT * diff --git a/source/blender/bmesh/intern/bmesh_structure.h b/source/blender/bmesh/intern/bmesh_structure.h index 29868194bbf..07f94796bb2 100644 --- a/source/blender/bmesh/intern/bmesh_structure.h +++ b/source/blender/bmesh/intern/bmesh_structure.h @@ -49,6 +49,7 @@ BLI_INLINE BMEdge *bmesh_disk_edge_next_safe(const BMEdge *e, const BMVert *v) A BLI_INLINE BMEdge *bmesh_disk_edge_prev_safe(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BLI_INLINE BMEdge *bmesh_disk_edge_next(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BLI_INLINE BMEdge *bmesh_disk_edge_prev(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +int bmesh_disk_facevert_count_ex(const BMVert *v, const int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); int bmesh_disk_facevert_count(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BMEdge *bmesh_disk_faceedge_find_first(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BMEdge *bmesh_disk_faceedge_find_next(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); @@ -60,6 +61,7 @@ void bmesh_radial_loop_remove(BMLoop *l, BMEdge *e) ATTR_NONNULL(1); * bmesh_radial_loop_next(BMLoop *l) / prev. * just use member access l->radial_next, l->radial_prev now */ +int bmesh_radial_facevert_count_ex(const BMLoop *l, const BMVert *v, const int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); int bmesh_radial_facevert_count(const BMLoop *l, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); bool bmesh_radial_facevert_check(const BMLoop *l, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BMLoop *bmesh_radial_faceloop_find_first(const BMLoop *l, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); @@ -68,7 +70,9 @@ BMLoop *bmesh_radial_faceloop_find_vert(const BMFace *f, const BMVert *v) ATTR_W bool bmesh_radial_validate(int radlen, BMLoop *l) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); /* EDGE UTILITIES */ -bool bmesh_edge_swapverts(BMEdge *e, BMVert *v_orig, BMVert *v_new) ATTR_NONNULL(); +void bmesh_disk_vert_swap(BMEdge *e, BMVert *v_dst, BMVert *v_src) ATTR_NONNULL(); +void bmesh_edge_vert_swap(BMEdge *e, BMVert *v_dst, BMVert *v_src) ATTR_NONNULL(); +void bmesh_disk_vert_replace(BMEdge *e, BMVert *v_dst, BMVert *v_src) ATTR_NONNULL(); BMEdge *bmesh_disk_edge_exists(const BMVert *v1, const BMVert *v2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); bool bmesh_disk_validate(int len, BMEdge *e, BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); diff --git a/source/blender/bmesh/intern/bmesh_structure_inline.h b/source/blender/bmesh/intern/bmesh_structure_inline.h index 5b7e890f5ea..64292194ae7 100644 --- a/source/blender/bmesh/intern/bmesh_structure_inline.h +++ b/source/blender/bmesh/intern/bmesh_structure_inline.h @@ -27,6 +27,7 @@ #ifndef __BMESH_STRUCTURE_INLINE_H__ #define __BMESH_STRUCTURE_INLINE_H__ +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2) BLI_INLINE BMDiskLink *bmesh_disk_edge_link_from_vert(const BMEdge *e, const BMVert *v) { BLI_assert(BM_vert_in_edge(e, v)); @@ -40,6 +41,7 @@ BLI_INLINE BMDiskLink *bmesh_disk_edge_link_from_vert(const BMEdge *e, const BMV * * \return Pointer to the next edge in the disk cycle for the vertex v. */ +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE BMEdge *bmesh_disk_edge_next_safe(const BMEdge *e, const BMVert *v) { if (v == e->v1) @@ -49,6 +51,7 @@ BLI_INLINE BMEdge *bmesh_disk_edge_next_safe(const BMEdge *e, const BMVert *v) return NULL; } +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE BMEdge *bmesh_disk_edge_prev_safe(const BMEdge *e, const BMVert *v) { if (v == e->v1) @@ -58,11 +61,13 @@ BLI_INLINE BMEdge *bmesh_disk_edge_prev_safe(const BMEdge *e, const BMVert *v) return NULL; } +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2) BLI_INLINE BMEdge *bmesh_disk_edge_next(const BMEdge *e, const BMVert *v) { return BM_DISK_EDGE_NEXT(e, v); } +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2) BLI_INLINE BMEdge *bmesh_disk_edge_prev(const BMEdge *e, const BMVert *v) { return BM_DISK_EDGE_PREV(e, v); diff --git a/source/blender/bmesh/intern/bmesh_walkers.c b/source/blender/bmesh/intern/bmesh_walkers.c index 6a5efbe70ac..d16eb572540 100644 --- a/source/blender/bmesh/intern/bmesh_walkers.c +++ b/source/blender/bmesh/intern/bmesh_walkers.c @@ -74,10 +74,11 @@ void *BMW_begin(BMWalker *walker, void *start) * a given type. The elements visited are filtered * by the bitmask 'searchmask'. */ -void BMW_init(BMWalker *walker, BMesh *bm, int type, - short mask_vert, short mask_edge, short mask_face, - BMWFlag flag, - int layer) +void BMW_init( + BMWalker *walker, BMesh *bm, int type, + short mask_vert, short mask_edge, short mask_face, + BMWFlag flag, + int layer) { memset(walker, 0, sizeof(BMWalker)); @@ -180,7 +181,7 @@ void *BMW_walk(BMWalker *walker) * \brief Current Walker State * * Returns the first state from the walker state - * worklist. This state is the the next in the + * worklist. This state is the next in the * worklist for processing. */ void *BMW_current_state(BMWalker *walker) diff --git a/source/blender/bmesh/intern/bmesh_walkers.h b/source/blender/bmesh/intern/bmesh_walkers.h index d551ea9fba9..f5a801a31c3 100644 --- a/source/blender/bmesh/intern/bmesh_walkers.h +++ b/source/blender/bmesh/intern/bmesh_walkers.h @@ -76,10 +76,11 @@ typedef struct BMWalker { /* initialize a walker. searchmask restricts some (not all) walkers to * elements with a specific tool flag set. flags is specific to each walker.*/ -void BMW_init(struct BMWalker *walker, BMesh *bm, int type, - short mask_vert, short mask_edge, short mask_face, - BMWFlag flag, - int layer); +void BMW_init( + struct BMWalker *walker, BMesh *bm, int type, + short mask_vert, short mask_edge, short mask_face, + BMWFlag flag, + int layer); void *BMW_begin(BMWalker *walker, void *start); void *BMW_step(struct BMWalker *walker); void BMW_end(struct BMWalker *walker); @@ -92,6 +93,11 @@ void BMW_state_remove(BMWalker *walker); void *BMW_walk(BMWalker *walker); void BMW_reset(BMWalker *walker); +#define BMW_ITER(ele, walker, data) \ + for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BMW_begin(walker, (BM_CHECK_TYPE_ELEM(data), data)); \ + ele; \ + BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BMW_step(walker)) + /* * example of usage, walking over an island of tool flagged faces: * @@ -108,8 +114,10 @@ void BMW_reset(BMWalker *walker); enum { BMW_VERT_SHELL, + BMW_LOOP_SHELL, + BMW_LOOP_SHELL_WIRE, BMW_FACE_SHELL, - BMW_LOOP, + BMW_EDGELOOP, BMW_FACELOOP, BMW_EDGERING, BMW_EDGEBOUNDARY, diff --git a/source/blender/bmesh/intern/bmesh_walkers_impl.c b/source/blender/bmesh/intern/bmesh_walkers_impl.c index 406dd412d6d..28c3debb93c 100644 --- a/source/blender/bmesh/intern/bmesh_walkers_impl.c +++ b/source/blender/bmesh/intern/bmesh_walkers_impl.c @@ -33,7 +33,6 @@ #include "BKE_customdata.h" #include "bmesh.h" -#include "intern/bmesh_private.h" #include "intern/bmesh_walkers_private.h" /* pop into stack memory (common operation) */ @@ -87,6 +86,30 @@ static bool bmw_mask_check_face(BMWalker *walker, BMFace *f) /** \} */ +/** \name BMesh Queries (modified to check walker flags) + * \{ */ + +/** + * Check for a wire edge, taking ignoring hidden. + */ +static bool bmw_edge_is_wire(const BMWalker *walker, const BMEdge *e) +{ + if (walker->flag & BMW_FLAG_TEST_HIDDEN) { + /* check if this is a wire edge, ignoring hidden faces */ + if (BM_edge_is_wire(e)) { + return true; + } + else { + return BM_edge_is_all_face_flag_test(e, BM_ELEM_HIDDEN, false); + } + } + else { + return BM_edge_is_wire(e); + } +} +/** \} */ + + /** \name Shell Walker * \{ * @@ -224,6 +247,291 @@ static void *bmw_VertShellWalker_step(BMWalker *walker) /** \} */ +/** \name LoopShell Walker + * \{ + * + * Starts at any element on the mesh and walks over the 'shell' it belongs + * to via visiting connected loops. + * + * \note this is mainly useful to loop over a shell delimited by edges. + */ +static void bmw_LoopShellWalker_visitLoop(BMWalker *walker, BMLoop *l) +{ + BMwLoopShellWalker *shellWalk = NULL; + + if (BLI_gset_haskey(walker->visit_set, l)) { + return; + } + + if (!bmw_mask_check_face(walker, l->f)) { + return; + } + + shellWalk = BMW_state_add(walker); + shellWalk->curloop = l; + BLI_gset_insert(walker->visit_set, l); +} + +static void bmw_LoopShellWalker_begin(BMWalker *walker, void *data) +{ + BMIter iter; + BMHeader *h = data; + + if (UNLIKELY(h == NULL)) { + return; + } + + switch (h->htype) { + case BM_LOOP: + { + /* starting the walk at a vert, add all the edges + * to the worklist */ + BMLoop *l = (BMLoop *)h; + bmw_LoopShellWalker_visitLoop(walker, l); + break; + } + + case BM_VERT: + { + BMVert *v = (BMVert *)h; + BMLoop *l; + BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + bmw_LoopShellWalker_visitLoop(walker, l); + } + break; + } + case BM_EDGE: + { + BMEdge *e = (BMEdge *)h; + BMLoop *l; + BM_ITER_ELEM (l, &iter, e, BM_LOOPS_OF_EDGE) { + bmw_LoopShellWalker_visitLoop(walker, l); + } + break; + } + case BM_FACE: + { + BMFace *f = (BMFace *)h; + BMLoop *l = BM_FACE_FIRST_LOOP(f); + /* walker will handle other loops within the face */ + bmw_LoopShellWalker_visitLoop(walker, l); + break; + } + default: + BLI_assert(0); + } +} + +static void *bmw_LoopShellWalker_yield(BMWalker *walker) +{ + BMwLoopShellWalker *shellWalk = BMW_current_state(walker); + return shellWalk->curloop; +} + +static void bmw_LoopShellWalker_step_impl(BMWalker *walker, BMLoop *l) +{ + BMEdge *e_edj_pair[2]; + int i; + + /* seems paranoid, but one caller also walks edges */ + BLI_assert(l->head.htype == BM_LOOP); + + bmw_LoopShellWalker_visitLoop(walker, l->next); + bmw_LoopShellWalker_visitLoop(walker, l->prev); + + e_edj_pair[0] = l->e; + e_edj_pair[1] = l->prev->e; + + for (i = 0; i < 2; i++) { + BMEdge *e = e_edj_pair[i]; + if (bmw_mask_check_edge(walker, e)) { + BMLoop *l_iter, *l_first; + + l_iter = l_first = e->l; + do { + BMLoop *l_radial = (l_iter->v == l->v) ? l_iter : l_iter->next; + BLI_assert(l_radial->v == l->v); + if (l != l_radial) { + bmw_LoopShellWalker_visitLoop(walker, l_radial); + } + } while ((l_iter = l_iter->radial_next) != l_first); + } + } +} + +static void *bmw_LoopShellWalker_step(BMWalker *walker) +{ + BMwLoopShellWalker *swalk, owalk; + BMLoop *l; + + BMW_state_remove_r(walker, &owalk); + swalk = &owalk; + + l = swalk->curloop; + bmw_LoopShellWalker_step_impl(walker, l); + + return l; +} + +/** \} */ + +/** \name LoopShell & 'Wire' Walker + * \{ + * + * Piggyback ontop of #BMwLoopShellWalker, but also walk over wire edges + * This isn't elegant but users expect it when selecting linked, + * so we can support delimiters _and_ walking over wire edges. + * + * Details: + * - can yield edges (as well as loops) + * - only step over wire edges. + * - verts and edges are stored in `visit_set_alt`. + */ + +static void bmw_LoopShellWalker_visitEdgeWire(BMWalker *walker, BMEdge *e) +{ + BMwLoopShellWireWalker *shellWalk = NULL; + + BLI_assert(bmw_edge_is_wire(walker, e)); + + if (BLI_gset_haskey(walker->visit_set_alt, e)) { + return; + } + + if (!bmw_mask_check_edge(walker, e)) { + return; + } + + shellWalk = BMW_state_add(walker); + shellWalk->curelem = (BMElem *)e; + BLI_gset_insert(walker->visit_set_alt, e); +} + +static void bmw_LoopShellWireWalker_visitVert(BMWalker *walker, BMVert *v, const BMEdge *e_from) +{ + BMEdge *e; + + BLI_assert(v->head.htype == BM_VERT); + + if (BLI_gset_haskey(walker->visit_set_alt, v)) { + return; + } + + if (!bmw_mask_check_vert(walker, v)) { + return; + } + + e = v->e; + do { + if (bmw_edge_is_wire(walker, e) && (e != e_from)) { + BMVert *v_other; + BMIter iter; + BMLoop *l; + + bmw_LoopShellWalker_visitEdgeWire(walker, e); + + /* check if we step onto a non-wire vertex */ + v_other = BM_edge_other_vert(e, v); + BM_ITER_ELEM (l, &iter, v_other, BM_LOOPS_OF_VERT) { + + bmw_LoopShellWalker_visitLoop(walker, l); + } + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + BLI_gset_insert(walker->visit_set_alt, v); +} + +static void bmw_LoopShellWireWalker_begin(BMWalker *walker, void *data) +{ + BMHeader *h = data; + + if (UNLIKELY(h == NULL)) { + return; + } + + bmw_LoopShellWalker_begin(walker, data); + + switch (h->htype) { + case BM_LOOP: + { + BMLoop *l = (BMLoop *)h; + bmw_LoopShellWireWalker_visitVert(walker, l->v, NULL); + break; + } + + case BM_VERT: + { + BMVert *v = (BMVert *)h; + if (v->e) { + bmw_LoopShellWireWalker_visitVert(walker, v, NULL); + } + break; + } + case BM_EDGE: + { + BMEdge *e = (BMEdge *)h; + if (bmw_mask_check_edge(walker, e)) { + bmw_LoopShellWireWalker_visitVert(walker, e->v1, NULL); + bmw_LoopShellWireWalker_visitVert(walker, e->v2, NULL); + } + else { + BMLoop *l_iter, *l_first; + + l_iter = l_first = e->l; + do { + bmw_LoopShellWalker_visitLoop(walker, l_iter); + bmw_LoopShellWalker_visitLoop(walker, l_iter->next); + } while ((l_iter = l_iter->radial_next) != l_first); + } + break; + } + case BM_FACE: + { + /* wire verts will be walked over */ + break; + } + default: + BLI_assert(0); + } +} + +static void *bmw_LoopShellWireWalker_yield(BMWalker *walker) +{ + BMwLoopShellWireWalker *shellWalk = BMW_current_state(walker); + return shellWalk->curelem; +} + +static void *bmw_LoopShellWireWalker_step(BMWalker *walker) +{ + BMwLoopShellWireWalker *swalk, owalk; + + BMW_state_remove_r(walker, &owalk); + swalk = &owalk; + + if (swalk->curelem->head.htype == BM_LOOP) { + BMLoop *l = (BMLoop *)swalk->curelem; + + bmw_LoopShellWalker_step_impl(walker, l); + + bmw_LoopShellWireWalker_visitVert(walker, l->v, NULL); + + return l; + } + else { + BMEdge *e = (BMEdge *)swalk->curelem; + + BLI_assert(e->head.htype == BM_EDGE); + + bmw_LoopShellWireWalker_visitVert(walker, e->v1, e); + bmw_LoopShellWireWalker_visitVert(walker, e->v2, e); + + return e; + } +} + +/** \} */ + /** \name FaceShell Walker * \{ @@ -544,9 +852,9 @@ static bool bm_edge_is_single(BMEdge *e) (BM_edge_is_boundary(e->l->next->e) || BM_edge_is_boundary(e->l->prev->e))); } -static void bmw_LoopWalker_begin(BMWalker *walker, void *data) +static void bmw_EdgeLoopWalker_begin(BMWalker *walker, void *data) { - BMwLoopWalker *lwalk = NULL, owalk, *owalk_pt; + BMwEdgeLoopWalker *lwalk = NULL, owalk, *owalk_pt; BMEdge *e = data; BMVert *v; const int vert_edge_count[2] = { @@ -594,7 +902,7 @@ static void bmw_LoopWalker_begin(BMWalker *walker, void *data) /* rewind */ while ((owalk_pt = BMW_current_state(walker))) { - owalk = *((BMwLoopWalker *)owalk_pt); + owalk = *((BMwEdgeLoopWalker *)owalk_pt); BMW_walk(walker); } @@ -607,16 +915,16 @@ static void bmw_LoopWalker_begin(BMWalker *walker, void *data) BLI_gset_insert(walker->visit_set, owalk.cur); } -static void *bmw_LoopWalker_yield(BMWalker *walker) +static void *bmw_EdgeLoopWalker_yield(BMWalker *walker) { - BMwLoopWalker *lwalk = BMW_current_state(walker); + BMwEdgeLoopWalker *lwalk = BMW_current_state(walker); return lwalk->cur; } -static void *bmw_LoopWalker_step(BMWalker *walker) +static void *bmw_EdgeLoopWalker_step(BMWalker *walker) { - BMwLoopWalker *lwalk, owalk; + BMwEdgeLoopWalker *lwalk, owalk; BMEdge *e, *nexte = NULL; BMLoop *l; BMVert *v; @@ -738,7 +1046,7 @@ static void *bmw_LoopWalker_step(BMWalker *walker) (owalk.is_single == false && vert_edge_tot > 2) || /* initial edge was a boundary, so is this edge and vertex is only apart of this face - * this lets us walk over the the boundary of an ngon which is handy */ + * this lets us walk over the boundary of an ngon which is handy */ (owalk.is_single == true && vert_edge_tot == 2 && BM_edge_is_boundary(e))) { /* find next boundary edge in the fan */ @@ -936,7 +1244,7 @@ static void *bmw_FaceLoopWalker_step(BMWalker *walker) * * Starts at a tool-flagged edge and walks over the edge ring * Conditions for starting and stepping the edge ring have been - * tuned in an attempt to match the edge rings built by EditMesh + * tuned to match behavior users expect (dating back to v2.4x). */ static void bmw_EdgeringWalker_begin(BMWalker *walker, void *data) { @@ -1180,17 +1488,16 @@ static void *bmw_UVEdgeWalker_yield(BMWalker *walker) static void *bmw_UVEdgeWalker_step(BMWalker *walker) { const int type = walker->bm->ldata.layers[walker->layer].type; + const int offset = walker->bm->ldata.layers[walker->layer].offset; + BMwUVEdgeWalker *lwalk, owalk; - BMLoop *l, *l2, *l3, *nl, *cl; - BMIter liter; - void *d1, *d2; - int i, j, rlen; + BMLoop *l; + int i; BMW_state_remove_r(walker, &owalk); lwalk = &owalk; l = lwalk->l; - nl = l->next; if (!bmw_mask_check_edge(walker, l->e)) { return l; @@ -1199,37 +1506,40 @@ static void *bmw_UVEdgeWalker_step(BMWalker *walker) /* go over loops around l->v and nl->v and see which ones share l and nl's * mloopuv's coordinates. in addition, push on l->next if necessary */ for (i = 0; i < 2; i++) { - cl = i ? nl : l; - BM_ITER_ELEM (l2, &liter, cl->v, BM_LOOPS_OF_VERT) { - d1 = CustomData_bmesh_get_layer_n(&walker->bm->ldata, - cl->head.data, walker->layer); - - rlen = BM_edge_face_count(l2->e); - for (j = 0; j < rlen; j++) { - if (BLI_gset_haskey(walker->visit_set, l2)) { + BMIter liter; + BMLoop *l_pivot, *l_radial; + + l_pivot = i ? l->next : l; + BM_ITER_ELEM (l_radial, &liter, l_pivot->v, BM_LOOPS_OF_VERT) { + BMLoop *l_radial_first = l_radial; + void *data_pivot = BM_ELEM_CD_GET_VOID_P(l_pivot, offset); + + do { + BMLoop *l_other; + void *data_other; + + if (BLI_gset_haskey(walker->visit_set, l_radial)) { continue; } - if (!bmw_mask_check_edge(walker, l2->e)) { - if (l2->v != cl->v) { + if (l_radial->v != l_pivot->v) { + if (!bmw_mask_check_edge(walker, l_radial->e)) { continue; } } - l3 = l2->v != cl->v ? l2->next : l2; - d2 = CustomData_bmesh_get_layer_n(&walker->bm->ldata, - l3->head.data, walker->layer); + l_other = (l_radial->v != l_pivot->v) ? l_radial->next : l_radial; + data_other = BM_ELEM_CD_GET_VOID_P(l_other, offset); - if (!CustomData_data_equals(type, d1, d2)) + if (!CustomData_data_equals(type, data_pivot, data_other)) continue; - + lwalk = BMW_state_add(walker); - BLI_gset_insert(walker->visit_set, l2); + BLI_gset_insert(walker->visit_set, l_radial); - lwalk->l = l2; + lwalk->l = l_radial; - l2 = l2->radial_next; - } + } while ((l_radial = l_radial->radial_next) != l_radial_first); } } @@ -1249,6 +1559,26 @@ static BMWalker bmw_VertShellWalker_Type = { BM_EDGE, /* valid restrict masks */ }; +static BMWalker bmw_LoopShellWalker_Type = { + BM_FACE | BM_LOOP | BM_EDGE | BM_VERT, + bmw_LoopShellWalker_begin, + bmw_LoopShellWalker_step, + bmw_LoopShellWalker_yield, + sizeof(BMwLoopShellWalker), + BMW_BREADTH_FIRST, + BM_EDGE, /* valid restrict masks */ +}; + +static BMWalker bmw_LoopShellWireWalker_Type = { + BM_FACE | BM_LOOP | BM_EDGE | BM_VERT, + bmw_LoopShellWireWalker_begin, + bmw_LoopShellWireWalker_step, + bmw_LoopShellWireWalker_yield, + sizeof(BMwLoopShellWireWalker), + BMW_BREADTH_FIRST, + BM_EDGE, /* valid restrict masks */ +}; + static BMWalker bmw_FaceShellWalker_Type = { BM_EDGE, bmw_FaceShellWalker_begin, @@ -1279,12 +1609,12 @@ static BMWalker bmw_IslandWalker_Type = { BM_EDGE | BM_FACE, /* valid restrict masks */ }; -static BMWalker bmw_LoopWalker_Type = { +static BMWalker bmw_EdgeLoopWalker_Type = { BM_EDGE, - bmw_LoopWalker_begin, - bmw_LoopWalker_step, - bmw_LoopWalker_yield, - sizeof(BMwLoopWalker), + bmw_EdgeLoopWalker_begin, + bmw_EdgeLoopWalker_step, + bmw_EdgeLoopWalker_yield, + sizeof(BMwEdgeLoopWalker), BMW_DEPTH_FIRST, 0, /* valid restrict masks */ /* could add flags here but so far none are used */ }; @@ -1341,8 +1671,10 @@ static BMWalker bmw_ConnectedVertexWalker_Type = { BMWalker *bm_walker_types[] = { &bmw_VertShellWalker_Type, /* BMW_VERT_SHELL */ + &bmw_LoopShellWalker_Type, /* BMW_LOOP_SHELL */ + &bmw_LoopShellWireWalker_Type, /* BMW_LOOP_SHELL_WIRE */ &bmw_FaceShellWalker_Type, /* BMW_FACE_SHELL */ - &bmw_LoopWalker_Type, /* BMW_LOOP */ + &bmw_EdgeLoopWalker_Type, /* BMW_EDGELOOP */ &bmw_FaceLoopWalker_Type, /* BMW_FACELOOP */ &bmw_EdgeringWalker_Type, /* BMW_EDGERING */ &bmw_EdgeboundaryWalker_Type, /* BMW_EDGEBOUNDARY */ diff --git a/source/blender/bmesh/intern/bmesh_walkers_private.h b/source/blender/bmesh/intern/bmesh_walkers_private.h index 82d1e760db7..66d812b45d0 100644 --- a/source/blender/bmesh/intern/bmesh_walkers_private.h +++ b/source/blender/bmesh/intern/bmesh_walkers_private.h @@ -45,6 +45,16 @@ typedef struct BMwShellWalker { BMEdge *curedge; } BMwShellWalker; +typedef struct BMwLoopShellWalker { + BMwGenericWalker header; + BMLoop *curloop; +} BMwLoopShellWalker; + +typedef struct BMwLoopShellWireWalker { + BMwGenericWalker header; + BMElem *curelem; +} BMwLoopShellWireWalker; + typedef struct BMwIslandboundWalker { BMwGenericWalker header; BMLoop *base; @@ -57,14 +67,14 @@ typedef struct BMwIslandWalker { BMFace *cur; } BMwIslandWalker; -typedef struct BMwLoopWalker { +typedef struct BMwEdgeLoopWalker { BMwGenericWalker header; BMEdge *cur, *start; BMVert *lastv, *startv; BMFace *f_hub; bool is_boundary; /* boundary looping changes behavior */ bool is_single; /* single means the edge verts are only connected to 1 face */ -} BMwLoopWalker; +} BMwEdgeLoopWalker; typedef struct BMwFaceLoopWalker { BMwGenericWalker header; diff --git a/source/blender/bmesh/operators/bmo_bevel.c b/source/blender/bmesh/operators/bmo_bevel.c index 213a0830e63..d5afb39d7b7 100644 --- a/source/blender/bmesh/operators/bmo_bevel.c +++ b/source/blender/bmesh/operators/bmo_bevel.c @@ -35,12 +35,14 @@ void bmo_bevel_exec(BMesh *bm, BMOperator *op) { - const float offset = BMO_slot_float_get(op->slots_in, "offset"); - const int offset_type = BMO_slot_int_get(op->slots_in, "offset_type"); - const int seg = BMO_slot_int_get(op->slots_in, "segments"); - const bool vonly = BMO_slot_bool_get(op->slots_in, "vertex_only"); - const float profile = BMO_slot_float_get(op->slots_in, "profile"); - const int material = BMO_slot_int_get(op->slots_in, "material"); + const float offset = BMO_slot_float_get(op->slots_in, "offset"); + const int offset_type = BMO_slot_int_get(op->slots_in, "offset_type"); + const int seg = BMO_slot_int_get(op->slots_in, "segments"); + const bool vonly = BMO_slot_bool_get(op->slots_in, "vertex_only"); + const float profile = BMO_slot_float_get(op->slots_in, "profile"); + const bool clamp_overlap = BMO_slot_bool_get(op->slots_in, "clamp_overlap"); + const int material = BMO_slot_int_get(op->slots_in, "material"); + const bool loop_slide = BMO_slot_bool_get(op->slots_in, "loop_slide"); if (offset > 0) { BMOIter siter; @@ -61,7 +63,7 @@ void bmo_bevel_exec(BMesh *bm, BMOperator *op) } } - BM_mesh_bevel(bm, offset, offset_type, seg, profile, vonly, false, false, NULL, -1, material); + BM_mesh_bevel(bm, offset, offset_type, seg, profile, vonly, false, clamp_overlap, NULL, -1, material, loop_slide); BMO_slot_buffer_from_enabled_hflag(bm, op, op->slots_out, "faces.out", BM_FACE, BM_ELEM_TAG); } diff --git a/source/blender/bmesh/operators/bmo_bridge.c b/source/blender/bmesh/operators/bmo_bridge.c index e4417477e76..b4570e03c83 100644 --- a/source/blender/bmesh/operators/bmo_bridge.c +++ b/source/blender/bmesh/operators/bmo_bridge.c @@ -87,8 +87,9 @@ static void bm_vert_loop_pair(BMesh *bm, BMVert *v1, BMVert *v2, BMLoop **l1, BM } /* el_b can have any offset */ -static float bm_edgeloop_offset_length(LinkData *el_a, LinkData *el_b, - LinkData *el_b_first, const float len_max) +static float bm_edgeloop_offset_length( + LinkData *el_a, LinkData *el_b, + LinkData *el_b_first, const float len_max) { float len = 0.0f; BLI_assert(el_a->prev == NULL); /* must be first */ @@ -137,10 +138,11 @@ static bool bm_edge_test_cb(BMEdge *e, void *bm_v) return BMO_elem_flag_test((BMesh *)bm_v, e, EDGE_MARK); } -static void bridge_loop_pair(BMesh *bm, - struct BMEdgeLoopStore *el_store_a, - struct BMEdgeLoopStore *el_store_b, - const bool use_merge, const float merge_factor, const int twist_offset) +static void bridge_loop_pair( + BMesh *bm, + struct BMEdgeLoopStore *el_store_a, + struct BMEdgeLoopStore *el_store_b, + const bool use_merge, const float merge_factor, const int twist_offset) { const float eps = 0.00001f; LinkData *el_a_first, *el_b_first; @@ -180,20 +182,42 @@ static void bridge_loop_pair(BMesh *bm, /* normalizing isn't strictly needed but without we may get very large values */ float no[3]; + float dir_a_orig[3], dir_b_orig[3]; float dir_a[3], dir_b[3]; + const float *test_a, *test_b; - sub_v3_v3v3(dir_a, + sub_v3_v3v3(dir_a_orig, ((BMVert *)(((LinkData *)lb_a->first)->data))->co, ((BMVert *)(((LinkData *)lb_a->last)->data))->co); - sub_v3_v3v3(dir_b, + sub_v3_v3v3(dir_b_orig, ((BMVert *)(((LinkData *)lb_b->first)->data))->co, ((BMVert *)(((LinkData *)lb_b->last)->data))->co); /* make the directions point out from the normals, 'no' is used as a temp var */ - cross_v3_v3v3(no, dir_a, el_dir); cross_v3_v3v3(dir_a, no, el_dir); - cross_v3_v3v3(no, dir_b, el_dir); cross_v3_v3v3(dir_b, no, el_dir); + cross_v3_v3v3(no, dir_a_orig, el_dir); cross_v3_v3v3(dir_a, no, el_dir); + cross_v3_v3v3(no, dir_b_orig, el_dir); cross_v3_v3v3(dir_b, no, el_dir); - if (dot_v3v3(dir_a, dir_b) < 0.0f) { + if (LIKELY(!is_zero_v3(dir_a) && !is_zero_v3(dir_b))) { + test_a = dir_a; + test_b = dir_b; + } + else { + /** + * This is a corner case: + * + * <pre> + * (loop a) (loop b) + * +--------+ +--------+ + * </pre> + * + * When loops are aligned to the direction between the loops values of 'dir_a/b' is degenerate, + * in this case compare the original directions (before they were corrected by 'el_dir'), see: T43013 + */ + test_a = dir_a_orig; + test_b = dir_b_orig; + } + + if (dot_v3v3(test_a, test_b) < 0.0f) { BM_edgeloop_flip(bm, el_store_b); } diff --git a/source/blender/bmesh/operators/bmo_connect_concave.c b/source/blender/bmesh/operators/bmo_connect_concave.c new file mode 100644 index 00000000000..107aead6994 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_connect_concave.c @@ -0,0 +1,219 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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 2 + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/operators/bmo_connect_concave.c + * \ingroup bmesh + * + * Connect vertices so all resulting faces are convex. + * + * Implementation: + * + * - triangulate all concave face (tagging convex verts), + * - rotate edges (beautify) so edges will connect nearby verts. + * - sort long edges (longest first), + * put any edges between 2 convex verts last since they often split convex regions. + * - merge the sorted edges as long as they don't create convex ngons. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_utildefines.h" +#include "BLI_alloca.h" +#include "BLI_memarena.h" +#include "BLI_heap.h" +#include "BLI_polyfill2d.h" +#include "BLI_polyfill2d_beautify.h" +#include "BLI_edgehash.h" + +#include "bmesh.h" + +#include "intern/bmesh_operators_private.h" /* own include */ + +#define EDGE_OUT (1 << 0) +#define FACE_OUT (1 << 1) + +static int bm_edge_length_cmp(const void *a_, const void *b_) +{ + const BMEdge *e_a = *(const void **)a_; + const BMEdge *e_b = *(const void **)b_; + + int e_a_concave = ((BM_elem_flag_test(e_a->v1, BM_ELEM_TAG)) && (BM_elem_flag_test(e_a->v2, BM_ELEM_TAG))); + int e_b_concave = ((BM_elem_flag_test(e_b->v1, BM_ELEM_TAG)) && (BM_elem_flag_test(e_b->v2, BM_ELEM_TAG))); + + /* merge edges between concave edges last since these + * are most likely to remain and be the main dividers */ + if (e_a_concave < e_b_concave) return -1; + else if (e_a_concave > e_b_concave) return 1; + else { + /* otherwise shortest edges last */ + const float e_a_len = BM_edge_calc_length_squared(e_a); + const float e_b_len = BM_edge_calc_length_squared(e_b); + if (e_a_len < e_b_len) return 1; + else if (e_a_len > e_b_len) return -1; + else return 0; + } +} + +static bool bm_face_split_by_concave( + BMesh *bm, BMFace *f_base, const float eps, + + MemArena *pf_arena, + struct Heap *pf_heap, struct EdgeHash *pf_ehash) +{ + const int f_base_len = f_base->len; + int faces_array_tot = f_base_len - 3; + int edges_array_tot = f_base_len - 3; + BMFace **faces_array = BLI_array_alloca(faces_array, faces_array_tot); + BMEdge **edges_array = BLI_array_alloca(edges_array, edges_array_tot); + const int quad_method = 0, ngon_method = 0; /* beauty */ + + float normal[3]; + BLI_assert(f_base->len > 3); + + copy_v3_v3(normal, f_base->no); + + BM_face_triangulate( + bm, f_base, + faces_array, &faces_array_tot, + edges_array, &edges_array_tot, + quad_method, ngon_method, false, + pf_arena, + pf_heap, pf_ehash); + + BLI_assert(edges_array_tot <= f_base_len - 3); + + if (faces_array_tot) { + int i; + for (i = 0; i < faces_array_tot; i++) { + BMFace *f = faces_array[i]; + BMO_elem_flag_enable(bm, f, FACE_OUT); + } + } + BMO_elem_flag_enable(bm, f_base, FACE_OUT); + + if (edges_array_tot) { + int i; + + qsort(edges_array, edges_array_tot, sizeof(*edges_array), bm_edge_length_cmp); + + for (i = 0; i < edges_array_tot; i++) { + BMLoop *l_pair[2]; + BMEdge *e = edges_array[i]; + BMO_elem_flag_enable(bm, e, EDGE_OUT); + + if (BM_edge_is_contiguous(e) && + BM_edge_loop_pair(e, &l_pair[0], &l_pair[1])) + { + bool ok = true; + int j; + for (j = 0; j < 2; j++) { + BMLoop *l = l_pair[j]; + + /* check that merging the edge (on this side) + * wouldn't result in a convex face-loop. + * + * This is the (l->next, l->prev) we would have once joined. + */ + float cross[3]; + cross_tri_v3( + cross, + l->v->co, + l->radial_next->next->next->v->co, + l->prev->v->co + ); + + if (dot_v3v3(cross, normal) <= eps) { + ok = false; + break; + } + } + + if (ok) { + BMFace *f_new, *f_pair[2] = {l_pair[0]->f, l_pair[1]->f}; + f_new = BM_faces_join(bm, f_pair, 2, true); + if (f_new) { + BMO_elem_flag_enable(bm, f_new, FACE_OUT); + } + } + } + } + } + + BLI_heap_clear(pf_heap, NULL); + BLI_edgehash_clear_ex(pf_ehash, NULL, BLI_POLYFILL_ALLOC_NGON_RESERVE); + + return true; +} + +static bool bm_face_convex_tag_verts(BMFace *f) +{ + bool is_concave = false; + if (f->len > 3) { + const BMLoop *l_iter, *l_first; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (BM_loop_is_convex(l_iter) == false) { + is_concave = true; + BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG); + } + else { + BM_elem_flag_disable(l_iter->v, BM_ELEM_TAG); + } + } while ((l_iter = l_iter->next) != l_first); + } + return is_concave; +} + +void bmo_connect_verts_concave_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMFace *f; + bool changed = false; + + MemArena *pf_arena; + Heap *pf_heap; + EdgeHash *pf_ehash; + + pf_arena = BLI_memarena_new(BLI_POLYFILL_ARENA_SIZE, __func__); + pf_heap = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE); + pf_ehash = BLI_edgehash_new_ex(__func__, BLI_POLYFILL_ALLOC_NGON_RESERVE); + + BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) { + if (f->len > 3 && bm_face_convex_tag_verts(f)) { + if (bm_face_split_by_concave( + bm, f, FLT_EPSILON, + pf_arena, pf_heap, pf_ehash)) + { + changed = true; + } + } + } + + if (changed) { + BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "edges.out", BM_EDGE, EDGE_OUT); + BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "faces.out", BM_FACE, FACE_OUT); + } + + BLI_memarena_free(pf_arena); + BLI_heap_free(pf_heap, NULL); + BLI_edgehash_free(pf_ehash, NULL); +} diff --git a/source/blender/bmesh/operators/bmo_connect_nonplanar.c b/source/blender/bmesh/operators/bmo_connect_nonplanar.c index 6859ce2060c..c9ce2c5f6b8 100644 --- a/source/blender/bmesh/operators/bmo_connect_nonplanar.c +++ b/source/blender/bmesh/operators/bmo_connect_nonplanar.c @@ -153,23 +153,11 @@ void bmo_connect_verts_nonplanar_exec(BMesh *bm, BMOperator *op) { BMOIter siter; BMFace *f; - int totface = 0, totloop = 0; + bool changed = false; BLI_LINKSTACK_DECLARE(fstack, BMFace *); const float angle_limit = BMO_slot_float_get(op->slots_in, "angle_limit"); - - BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) { - if (f->len > 3) { - totface += 1; - totloop += f->len; - } - } - - if (totface == 0) { - return; - } - BLI_LINKSTACK_INIT(fstack); BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) { @@ -188,11 +176,14 @@ void bmo_connect_verts_nonplanar_exec(BMesh *bm, BMOperator *op) BLI_LINKSTACK_PUSH(fstack, f_pair[j]); } } + changed = true; } } BLI_LINKSTACK_FREE(fstack); - BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "edges.out", BM_EDGE, EDGE_OUT); - BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "faces.out", BM_FACE, FACE_OUT); + if (changed) { + BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "edges.out", BM_EDGE, EDGE_OUT); + BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "faces.out", BM_FACE, FACE_OUT); + } } diff --git a/source/blender/bmesh/operators/bmo_connect_pair.c b/source/blender/bmesh/operators/bmo_connect_pair.c index a0acf6ed2c5..12af8902dc5 100644 --- a/source/blender/bmesh/operators/bmo_connect_pair.c +++ b/source/blender/bmesh/operators/bmo_connect_pair.c @@ -30,6 +30,7 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_heap.h" #include "bmesh.h" @@ -42,8 +43,14 @@ * Method for connecting across many faces. * * - use the line between both verts and their normal average to construct a matrix. - * - using the matrix, we can find all intersecting verts/edges and build connection data. - * - then walk the connected data and find the shortest path (as we do with other shortest-path functions). + * - using the matrix, we can find all intersecting verts/edges. + * - walk the connected data and find the shortest path. + * - store a heap of paths which are being scanned (#PathContext.states). + * - continuously search the shortest path in the heap. + * - never step over the same element twice (tag elements as #ELE_TOUCHED). + * this avoids going into an eternal loop of there are many possible branches (see T45582). + * - when running into a branch, create a new #PathLinkState state and add to the heap. + * - when the target is reached, finish - since none of the other paths can be shorter then the one just found. * - if the connection can't be found - fail. * - with the connection found, split all edges tagging verts (or tag verts that sit on the intersection). * - run the standard connect operator. @@ -56,15 +63,26 @@ /* typically hidden faces */ #define FACE_EXCLUDE 2 +/* any element we've walked over (only do it once!) */ +#define ELE_TOUCHED 4 + #define FACE_WALK_TEST(f) (CHECK_TYPE_INLINE(f, BMFace *), \ BMO_elem_flag_test(pc->bm_bmoflag, f, FACE_EXCLUDE) == 0) #define VERT_WALK_TEST(v) (CHECK_TYPE_INLINE(v, BMVert *), \ BMO_elem_flag_test(pc->bm_bmoflag, v, VERT_EXCLUDE) == 0) +#define ELE_TOUCH_TEST(e) \ + (CHECK_TYPE_ANY(e, BMVert *, BMEdge *, BMElem *, BMElemF *), \ + BMO_elem_flag_test(pc->bm_bmoflag, (BMElemF *)e, ELE_TOUCHED)) +#define ELE_TOUCH_MARK(e) \ + { CHECK_TYPE_ANY(e, BMVert *, BMEdge *, BMElem *, BMElemF *); \ + BMO_elem_flag_enable(pc->bm_bmoflag, (BMElemF *)e, ELE_TOUCHED); } ((void)0) + + // #define DEBUG_PRINT typedef struct PathContext { - ListBase state_lb; + Heap *states; float matrix[3][3]; float axis_sep; @@ -82,12 +100,10 @@ typedef struct PathContext { typedef struct PathLink { struct PathLink *next; BMElem *ele; /* edge or vert */ - BMElem *ele_from; /* edge or face we game from (not 'next->ele') */ + BMElem *ele_from; /* edge or face we came from (not 'next->ele') */ } PathLink; typedef struct PathLinkState { - struct PathLinkState *next, *prev; - /* chain of links */ struct PathLink *link_last; @@ -96,8 +112,65 @@ typedef struct PathLinkState { float co_prev[3]; } PathLinkState; -static int state_isect_co_pair(const PathContext *pc, - const float co_a[3], const float co_b[3]) +/** + \name Min Dist Dir Util + + * Simply getting the closest intersecting vert/edge is _not_ good enough. see T43792 + * we need to get the closest in both directions since the absolute closest may be a dead-end. + * + * Logic is simple: + * + * - first intersection, store the direction. + * - successive intersections will update the first distance if its aligned with the first hit. + * otherwise update the opposite distance. + * - caller stores best outcome in both directions. + * + * \{ */ + +typedef struct MinDistDir { + /* distance in both directions (FLT_MAX == uninitialized) */ + float dist_min[2]; + /* direction of the first intersection found */ + float dir[3]; +} MinDistDir; + +#define MIN_DIST_DIR_INIT {{FLT_MAX, FLT_MAX}} + +static int min_dist_dir_test(MinDistDir *mddir, const float dist_dir[3], const float dist_sq) +{ + + if (mddir->dist_min[0] == FLT_MAX) { + return 0; + } + else { + if (dot_v3v3(dist_dir, mddir->dir) > 0.0f) { + if (dist_sq < mddir->dist_min[0]) { + return 0; + } + } + else { + if (dist_sq < mddir->dist_min[1]) { + return 1; + } + } + } + + return -1; +} + +static void min_dist_dir_update(MinDistDir *dist, const float dist_dir[3]) +{ + if (dist->dist_min[0] == FLT_MAX) { + copy_v3_v3(dist->dir, dist_dir); + } +} + +/** \} */ + + +static int state_isect_co_pair( + const PathContext *pc, + const float co_a[3], const float co_b[3]) { const float diff_a = dot_m3_v3_row_x((float (*)[3])pc->matrix, co_a) - pc->axis_sep; const float diff_b = dot_m3_v3_row_x((float (*)[3])pc->matrix, co_b) - pc->axis_sep; @@ -113,15 +186,17 @@ static int state_isect_co_pair(const PathContext *pc, } } -static int state_isect_co_exact(const PathContext *pc, - const float co[3]) +static int state_isect_co_exact( + const PathContext *pc, + const float co[3]) { const float diff = dot_m3_v3_row_x((float (*)[3])pc->matrix, co) - pc->axis_sep; return (fabsf(diff) <= CONNECT_EPS); } -static float state_calc_co_pair_fac(const PathContext *pc, - const float co_a[3], const float co_b[3]) +static float state_calc_co_pair_fac( + const PathContext *pc, + const float co_a[3], const float co_b[3]) { float diff_a, diff_b, diff_tot; @@ -131,19 +206,21 @@ static float state_calc_co_pair_fac(const PathContext *pc, return (diff_tot > FLT_EPSILON) ? (diff_a / diff_tot) : 0.5f; } -static void state_calc_co_pair(const PathContext *pc, - const float co_a[3], const float co_b[3], - float r_co[3]) +static void state_calc_co_pair( + const PathContext *pc, + const float co_a[3], const float co_b[3], + float r_co[3]) { const float fac = state_calc_co_pair_fac(pc, co_a, co_b); interp_v3_v3v3(r_co, co_a, co_b, fac); } +#ifndef NDEBUG /** * Ideally we wouldn't need this and for most cases we don't. - * But when a face has vertices that are on the boundary more then once this becomes tricky. + * But when a face has vertices that are on the boundary more than once this becomes tricky. */ -static bool state_link_find(PathLinkState *state, BMElem *ele) +static bool state_link_find(const PathLinkState *state, BMElem *ele) { PathLink *link = state->link_last; BLI_assert(ELEM(ele->head.htype, BM_VERT, BM_EDGE, BM_FACE)); @@ -156,16 +233,21 @@ static bool state_link_find(PathLinkState *state, BMElem *ele) } return false; } +#endif -static void state_link_add(PathContext *pc, PathLinkState *state, - BMElem *ele, BMElem *ele_from) +static void state_link_add( + PathContext *pc, PathLinkState *state, + BMElem *ele, BMElem *ele_from) { PathLink *step_new = BLI_mempool_alloc(pc->link_pool); BLI_assert(ele != ele_from); BLI_assert(state_link_find(state, ele) == false); + /* never walk onto this again */ + ELE_TOUCH_MARK(ele); + #ifdef DEBUG_PRINT - printf("%s: adding to state %p:%d, %.4f - ", __func__, state, BLI_findindex(&pc->state_lb, state), state->dist); + printf("%s: adding to state %p, %.4f - ", __func__, state, state->dist); if (ele->head.htype == BM_VERT) { printf("vert %d, ", BM_elem_index_get(ele)); } @@ -217,12 +299,29 @@ static void state_link_add(PathContext *pc, PathLinkState *state, } static PathLinkState *state_dupe_add( - PathContext *pc, PathLinkState *state, const PathLinkState *state_orig) { state = MEM_mallocN(sizeof(*state), __func__); *state = *state_orig; - BLI_addhead(&pc->state_lb, state); + return state; +} + +static PathLinkState *state_link_add_test( + PathContext *pc, PathLinkState *state, const PathLinkState *state_orig, + BMElem *ele, BMElem *ele_from) +{ + const bool is_new = (state_orig->link_last != state->link_last); + if (is_new) { + state = state_dupe_add(state, state_orig); + } + + state_link_add(pc, state, ele, ele_from); + + /* after adding a link so we use the updated 'state->dist' */ + if (is_new) { + BLI_heap_insert(pc->states, state->dist, state); + } + return state; } @@ -230,23 +329,47 @@ static PathLinkState *state_dupe_add( static PathLinkState *state_step__face_edges( PathContext *pc, PathLinkState *state, const PathLinkState *state_orig, - BMLoop *l_iter, BMLoop *l_last) + BMLoop *l_iter, BMLoop *l_last, + MinDistDir *mddir) { + + BMLoop *l_iter_best[2] = {NULL, NULL}; + int i; + do { if (state_isect_co_pair(pc, l_iter->v->co, l_iter->next->v->co)) { - BMElem *ele_next = (BMElem *)l_iter->e; - BMElem *ele_next_from = (BMElem *)l_iter->f; + float dist_test; + float co_isect[3]; + float dist_dir[3]; + int index; - if (FACE_WALK_TEST((BMFace *)ele_next_from) && - (state_link_find(state, ele_next) == false)) - { - if (state_orig->link_last != state->link_last) { - state = state_dupe_add(pc, state, state_orig); + state_calc_co_pair(pc, l_iter->v->co, l_iter->next->v->co, co_isect); + + sub_v3_v3v3(dist_dir, co_isect, state_orig->co_prev); + dist_test = len_squared_v3(dist_dir); + if ((index = min_dist_dir_test(mddir, dist_dir, dist_test)) != -1) { + BMElem *ele_next = (BMElem *)l_iter->e; + BMElem *ele_next_from = (BMElem *)l_iter->f; + + if (FACE_WALK_TEST((BMFace *)ele_next_from) && + (ELE_TOUCH_TEST(ele_next) == false)) + { + min_dist_dir_update(mddir, dist_dir); + mddir->dist_min[index] = dist_test; + l_iter_best[index] = l_iter; } - state_link_add(pc, state, ele_next, ele_next_from); } } } while ((l_iter = l_iter->next) != l_last); + + for (i = 0; i < 2; i++) { + if ((l_iter = l_iter_best[i])) { + BMElem *ele_next = (BMElem *)l_iter->e; + BMElem *ele_next_from = (BMElem *)l_iter->f; + state = state_link_add_test(pc, state, state_orig, ele_next, ele_next_from); + } + } + return state; } @@ -254,23 +377,44 @@ static PathLinkState *state_step__face_edges( static PathLinkState *state_step__face_verts( PathContext *pc, PathLinkState *state, const PathLinkState *state_orig, - BMLoop *l_iter, BMLoop *l_last) + BMLoop *l_iter, BMLoop *l_last, + MinDistDir *mddir) { + BMLoop *l_iter_best[2] = {NULL, NULL}; + int i; + do { if (state_isect_co_exact(pc, l_iter->v->co)) { - BMElem *ele_next = (BMElem *)l_iter->v; - BMElem *ele_next_from = (BMElem *)l_iter->f; - - if (FACE_WALK_TEST((BMFace *)ele_next_from) && - state_link_find(state, ele_next) == false) - { - if (state_orig->link_last != state->link_last) { - state = state_dupe_add(pc, state, state_orig); + float dist_test; + const float *co_isect = l_iter->v->co; + float dist_dir[3]; + int index; + + sub_v3_v3v3(dist_dir, co_isect, state_orig->co_prev); + dist_test = len_squared_v3(dist_dir); + if ((index = min_dist_dir_test(mddir, dist_dir, dist_test)) != -1) { + BMElem *ele_next = (BMElem *)l_iter->v; + BMElem *ele_next_from = (BMElem *)l_iter->f; + + if (FACE_WALK_TEST((BMFace *)ele_next_from) && + (ELE_TOUCH_TEST(ele_next) == false)) + { + min_dist_dir_update(mddir, dist_dir); + mddir->dist_min[index] = dist_test; + l_iter_best[index] = l_iter; } - state_link_add(pc, state, ele_next, ele_next_from); } } } while ((l_iter = l_iter->next) != l_last); + + for (i = 0; i < 2; i++) { + if ((l_iter = l_iter_best[i])) { + BMElem *ele_next = (BMElem *)l_iter->v; + BMElem *ele_next_from = (BMElem *)l_iter->f; + state = state_link_add_test(pc, state, state_orig, ele_next, ele_next_from); + } + } + return state; } @@ -290,20 +434,12 @@ static bool state_step(PathContext *pc, PathLinkState *state) if ((l_start->f != ele_from) && FACE_WALK_TEST(l_start->f)) { + MinDistDir mddir = MIN_DIST_DIR_INIT; /* very similar to block below */ - if (BM_vert_in_face(l_start->f, pc->v_b)) { - if (state_orig.link_last != state->link_last) { - state = state_dupe_add(pc, state, &state_orig); - } - - state_link_add(pc, state, (BMElem *)pc->v_b, (BMElem *)l_start->f); - } - else { - state = state_step__face_edges(pc, state, &state_orig, - l_start->next, l_start); - state = state_step__face_verts(pc, state, &state_orig, - l_start->next->next, l_start); - } + state = state_step__face_edges(pc, state, &state_orig, + l_start->next, l_start, &mddir); + state = state_step__face_verts(pc, state, &state_orig, + l_start->next->next, l_start, &mddir); } } } @@ -319,24 +455,14 @@ static bool state_step(PathContext *pc, PathLinkState *state) if ((l_start->f != ele_from) && FACE_WALK_TEST(l_start->f)) { + MinDistDir mddir = MIN_DIST_DIR_INIT; /* very similar to block above */ - if (BM_vert_in_face(l_start->f, pc->v_b)) { - BMElem *ele_next = (BMElem *)pc->v_b; - BMElem *ele_next_from = (BMElem *)l_start->f; - - if (state_orig.link_last != state->link_last) { - state = state_dupe_add(pc, state, &state_orig); - } - state_link_add(pc, state, ele_next, ele_next_from); - } - else { - state = state_step__face_edges(pc, state, &state_orig, - l_start->next, l_start->prev); - if (l_start->f->len > 3) { - /* adjacent verts are handled in state_step__vert_edges */ - state = state_step__face_verts(pc, state, &state_orig, - l_start->next->next, l_start->prev); - } + state = state_step__face_edges(pc, state, &state_orig, + l_start->next, l_start->prev, &mddir); + if (l_start->f->len > 3) { + /* adjacent verts are handled in state_step__vert_edges */ + state = state_step__face_verts(pc, state, &state_orig, + l_start->next->next, l_start->prev, &mddir); } } } @@ -351,31 +477,16 @@ static bool state_step(PathContext *pc, PathLinkState *state) if (((BMElem *)e != ele_from) && VERT_WALK_TEST(v_other)) { - if (v_other == pc->v_b) { - BMElem *ele_next = (BMElem *)pc->v_b; + if (state_isect_co_exact(pc, v_other->co)) { + BMElem *ele_next = (BMElem *)v_other; BMElem *ele_next_from = (BMElem *)e; - - if (state_orig.link_last != state->link_last) { - state = state_dupe_add(pc, state, &state_orig); - } - state_link_add(pc, state, ele_next, ele_next_from); - } - else { - if (state_isect_co_exact(pc, v_other->co)) { - BMElem *ele_next = (BMElem *)v_other; - BMElem *ele_next_from = (BMElem *)e; - if (state_link_find(state, ele_next) == false) { - if (state_orig.link_last != state->link_last) { - state = state_dupe_add(pc, state, &state_orig); - } - state_link_add(pc, state, ele_next, ele_next_from); - } + if (ELE_TOUCH_TEST(ele_next) == false) { + state = state_link_add_test(pc, state, &state_orig, ele_next, ele_next_from); } } } } } - } else { BLI_assert(0); @@ -388,8 +499,7 @@ void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op) BMOpSlot *op_verts_slot = BMO_slot_get(op->slots_in, "verts"); PathContext pc; - bool found_all; - float found_dist_best = -1.0f; + PathLinkState state_best = {NULL}; if (op_verts_slot->len != 2) { /* fail! */ @@ -416,7 +526,7 @@ void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op) /* setup context */ { - BLI_listbase_clear(&pc.state_lb); + pc.states = BLI_heap_new(); pc.link_pool = BLI_mempool_create(sizeof(PathLink), 0, 512, BLI_MEMPOOL_NOP); } @@ -459,12 +569,22 @@ void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op) #endif /* get third axis */ + normalize_v3(basis_dir); + normalize_v3(basis_nor); cross_v3_v3v3(basis_tmp, basis_dir, basis_nor); + if (UNLIKELY(normalize_v3(basis_tmp) < FLT_EPSILON)) { + ortho_v3_v3(basis_nor, basis_dir); + normalize_v3(basis_nor); + cross_v3_v3v3(basis_tmp, basis_dir, basis_nor); + normalize_v3(basis_tmp); + } - normalize_v3_v3(pc.matrix[0], basis_tmp); - normalize_v3_v3(pc.matrix[1], basis_dir); - normalize_v3_v3(pc.matrix[2], basis_nor); - invert_m3(pc.matrix); + copy_v3_v3(pc.matrix[0], basis_tmp); + copy_v3_v3(pc.matrix[1], basis_dir); + copy_v3_v3(pc.matrix[2], basis_nor); + if (invert_m3(pc.matrix) == false) { + unit_m3(pc.matrix); + } pc.axis_sep = dot_m3_v3_row_x(pc.matrix, pc.v_a->co); } @@ -473,69 +593,61 @@ void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op) { PathLinkState *state; state = MEM_callocN(sizeof(*state), __func__); - BLI_addtail(&pc.state_lb, state); state_link_add(&pc, state, (BMElem *)pc.v_a, NULL); + BLI_heap_insert(pc.states, state->dist, state); } - found_all = false; - while (pc.state_lb.first) { - PathLinkState *state, *state_next; - found_all = true; - for (state = pc.state_lb.first; state; state = state_next) { - state_next = state->next; + while (!BLI_heap_is_empty(pc.states)) { + +#ifdef DEBUG_PRINT + printf("\n%s: stepping %d\n", __func__, BLI_heap_size(pc.states)); +#endif + + while (!BLI_heap_is_empty(pc.states)) { + PathLinkState *state = BLI_heap_popmin(pc.states); + + /* either we insert this into 'pc.states' or its freed */ + bool continue_search; + if (state->link_last->ele == (BMElem *)pc.v_b) { /* pass, wait until all are found */ #ifdef DEBUG_PRINT printf("%s: state %p loop found %.4f\n", __func__, state, state->dist); #endif - if ((found_dist_best == -1.0f) || (found_dist_best > state->dist)) { - found_dist_best = state->dist; - } + state_best = *state; + + /* we're done, exit all loops */ + BLI_heap_clear(pc.states, MEM_freeN); + continue_search = false; } else if (state_step(&pc, state)) { - if ((found_dist_best != -1.0f) && (found_dist_best <= state->dist)) { - BLI_remlink(&pc.state_lb, state); - MEM_freeN(state); - } - found_all = false; + continue_search = true; } else { /* didn't reach the end, remove it, * links are shared between states so just free the link_pool at the end */ - BLI_remlink(&pc.state_lb, state); - MEM_freeN(state); + +#ifdef DEBUG_PRINT + printf("%s: state %p removed\n", __func__, state); +#endif + continue_search = false; } - } - if (found_all) { -#ifdef DEBUG - for (state = pc.state_lb.first; state; state = state->next) { - BLI_assert(state->link_last->ele == (BMElem *)pc.v_b); + if (continue_search) { + BLI_heap_insert(pc.states, state->dist, state); + } + else { + MEM_freeN(state); } -#endif - break; } } - if (BLI_listbase_is_empty(&pc.state_lb)) { - found_all = false; - } - - if (found_all) { - PathLinkState *state, *state_best = NULL; + if (state_best.link_last) { PathLink *link; - float state_best_dist = FLT_MAX; /* find the best state */ - for (state = pc.state_lb.first; state; state = state->next) { - if ((state_best == NULL) || (state->dist < state_best_dist)) { - state_best = state; - state_best_dist = state_best->dist; - } - } - - link = state_best->link_last; + link = state_best.link_last; do { if (link->ele->head.htype == BM_EDGE) { BMEdge *e = (BMEdge *)link->ele; @@ -558,16 +670,15 @@ void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op) BMO_elem_flag_enable(bm, pc.v_b, VERT_OUT); BLI_mempool_destroy(pc.link_pool); - BLI_freelistN(&pc.state_lb); + + BLI_heap_free(pc.states, MEM_freeN); #if 1 - if (found_all) { - /* leave 'check_degenerate' off, if a user tries to cut with 2 verts, - * always connect even when resulting faces are degenerate [#39418] */ + if (state_best.link_last) { BMOperator op_sub; BMO_op_initf(bm, &op_sub, 0, - "connect_verts verts=%fv faces_exclude=%s", - VERT_OUT, op, "faces_exclude"); + "connect_verts verts=%fv faces_exclude=%s check_degenerate=%b", + VERT_OUT, op, "faces_exclude", true); BMO_op_exec(bm, &op_sub); BMO_slot_copy(&op_sub, slots_out, "edges.out", op, slots_out, "edges.out"); diff --git a/source/blender/bmesh/operators/bmo_create.c b/source/blender/bmesh/operators/bmo_create.c index dd814fa8bfb..1c054e89e39 100644 --- a/source/blender/bmesh/operators/bmo_create.c +++ b/source/blender/bmesh/operators/bmo_create.c @@ -39,7 +39,7 @@ /* This is what runs when pressing the F key * doing the best thing here isn't always easy create vs dissolve, its nice to support - * but it it _really_ gives issues we might have to not call dissolve. - campbell + * but it _really_ gives issues we might have to not call dissolve. - campbell */ void bmo_contextual_create_exec(BMesh *bm, BMOperator *op) { diff --git a/source/blender/bmesh/operators/bmo_dissolve.c b/source/blender/bmesh/operators/bmo_dissolve.c index 8cd9ee14bcb..ac0466a74d2 100644 --- a/source/blender/bmesh/operators/bmo_dissolve.c +++ b/source/blender/bmesh/operators/bmo_dissolve.c @@ -475,7 +475,7 @@ void bmo_dissolve_limit_exec(BMesh *bm, BMOperator *op) { BMOpSlot *einput = BMO_slot_get(op->slots_in, "edges"); BMOpSlot *vinput = BMO_slot_get(op->slots_in, "verts"); - const float angle_max = (float)M_PI / 2.0f; + const float angle_max = M_PI_2; const float angle_limit = min_ff(angle_max, BMO_slot_float_get(op->slots_in, "angle_limit")); const bool do_dissolve_boundaries = BMO_slot_bool_get(op->slots_in, "use_dissolve_boundaries"); const BMO_Delimit delimit = BMO_slot_int_get(op->slots_in, "delimit"); @@ -494,7 +494,7 @@ void bmo_dissolve_limit_exec(BMesh *bm, BMOperator *op) static void bm_mesh_edge_collapse_flagged(BMesh *bm, const int flag, const short oflag) { - BMO_op_callf(bm, flag, "collapse edges=%fe", oflag); + BMO_op_callf(bm, flag, "collapse edges=%fe uvs=%b", oflag, true); } void bmo_dissolve_degenerate_exec(BMesh *bm, BMOperator *op) diff --git a/source/blender/bmesh/operators/bmo_dupe.c b/source/blender/bmesh/operators/bmo_dupe.c index cd5592f08c9..33048e6c86e 100644 --- a/source/blender/bmesh/operators/bmo_dupe.c +++ b/source/blender/bmesh/operators/bmo_dupe.c @@ -44,9 +44,10 @@ * * Copy an existing vertex from one bmesh to another. */ -static BMVert *bmo_vert_copy(BMOperator *op, - BMOpSlot *slot_vertmap_out, - BMesh *bm_dst, BMesh *bm_src, BMVert *v_src, GHash *vhash) +static BMVert *bmo_vert_copy( + BMOperator *op, + BMOpSlot *slot_vertmap_out, + BMesh *bm_dst, BMesh *bm_src, BMVert *v_src, GHash *vhash) { BMVert *v_dst; @@ -72,12 +73,13 @@ static BMVert *bmo_vert_copy(BMOperator *op, * * Copy an existing edge from one bmesh to another. */ -static BMEdge *bmo_edge_copy(BMOperator *op, - BMOpSlot *slot_edgemap_out, - BMOpSlot *slot_boundarymap_out, - BMesh *bm_dst, BMesh *bm_src, - BMEdge *e_src, - GHash *vhash, GHash *ehash) +static BMEdge *bmo_edge_copy( + BMOperator *op, + BMOpSlot *slot_edgemap_out, + BMOpSlot *slot_boundarymap_out, + BMesh *bm_dst, BMesh *bm_src, + BMEdge *e_src, + GHash *vhash, GHash *ehash) { BMEdge *e_dst; BMVert *e_dst_v1, *e_dst_v2; @@ -109,7 +111,7 @@ static BMEdge *bmo_edge_copy(BMOperator *op, /* add to new/old edge map if necassary */ if (rlen < 2) { - /* not sure what non-manifold cases of greater then three + /* not sure what non-manifold cases of greater than three * radial should do. */ BMO_slot_map_elem_insert(op, slot_boundarymap_out, e_src, e_dst); } @@ -131,11 +133,12 @@ static BMEdge *bmo_edge_copy(BMOperator *op, * * Copy an existing face from one bmesh to another. */ -static BMFace *bmo_face_copy(BMOperator *op, - BMOpSlot *slot_facemap_out, - BMesh *bm_dst, BMesh *bm_src, - BMFace *f_src, - GHash *vhash, GHash *ehash) +static BMFace *bmo_face_copy( + BMOperator *op, + BMOpSlot *slot_facemap_out, + BMesh *bm_dst, BMesh *bm_src, + BMFace *f_src, + GHash *vhash, GHash *ehash) { BMFace *f_dst; BMVert **vtar = BLI_array_alloca(vtar, f_src->len); diff --git a/source/blender/bmesh/operators/bmo_extrude.c b/source/blender/bmesh/operators/bmo_extrude.c index 88b53b63abb..4449f223f35 100644 --- a/source/blender/bmesh/operators/bmo_extrude.c +++ b/source/blender/bmesh/operators/bmo_extrude.c @@ -39,6 +39,8 @@ #include "intern/bmesh_operators_private.h" /* own include */ +#define USE_EDGE_REGION_FLAGS + enum { EXT_INPUT = 1, EXT_KEEP = 2, @@ -135,7 +137,7 @@ void bmo_extrude_discrete_faces_exec(BMesh *bm, BMOperator *op) * This function won't crash if its not but won't work right either. * \a e_b is the new edge. * - * \note The edge this face comes from needs to be from the first and second verts fo the face. + * \note The edge this face comes from needs to be from the first and second verts to the face. * The caller must ensure this else we will copy from the wrong source. */ static void bm_extrude_copy_face_loop_attributes(BMesh *bm, BMFace *f) @@ -287,6 +289,39 @@ void bmo_extrude_vert_indiv_exec(BMesh *bm, BMOperator *op) BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "edges.out", BM_EDGE, EXT_KEEP); } +#ifdef USE_EDGE_REGION_FLAGS +/** + * When create an edge for an extruded face region + * check surrounding edge flags before creating a new edge. + */ +static bool bm_extrude_region_edge_flag(const BMVert *v, char r_e_hflag[2]) +{ + BMEdge *e_iter; + const char hflag_enable = BM_ELEM_SEAM; + const char hflag_disable = BM_ELEM_SMOOTH; + bool ok = false; + + r_e_hflag[0] = 0x0; + r_e_hflag[1] = 0xff; + + /* clear flags on both disks */ + e_iter = v->e; + do { + if (e_iter->l && !BM_edge_is_boundary(e_iter)) { + r_e_hflag[0] |= e_iter->head.hflag; + r_e_hflag[1] &= e_iter->head.hflag; + ok = true; + } + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != v->e); + + if (ok) { + r_e_hflag[0] &= hflag_enable; + r_e_hflag[1] = hflag_disable & ~r_e_hflag[1]; + } + return ok; +} +#endif /* USE_EDGE_REGION_FLAGS */ + void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) { BMOperator dupeop, delop; @@ -413,6 +448,9 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) slot_edges_exclude = BMO_slot_get(op->slots_in, "edges_exclude"); for (e = BMO_iter_new(&siter, dupeop.slots_out, "boundary_map.out", 0); e; e = BMO_iter_step(&siter)) { BMVert *f_verts[4]; +#ifdef USE_EDGE_REGION_FLAGS + BMEdge *f_edges[4]; +#endif /* this should always be wire, so this is mainly a speedup to avoid map lookup */ if (BM_edge_is_wire(e) && BMO_slot_map_contains(slot_edges_exclude, e)) { @@ -465,8 +503,38 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) f_verts[3] = e_new->v2; } - /* not sure what to do about example face, pass NULL for now */ +#ifdef USE_EDGE_REGION_FLAGS + /* handle new edges */ + f_edges[0] = e; + f_edges[2] = e_new; + + f_edges[1] = BM_edge_exists(f_verts[1], f_verts[2]); + if (f_edges[1] == NULL) { + char e_hflag[2]; + bool e_hflag_ok = bm_extrude_region_edge_flag(f_verts[2], e_hflag); + f_edges[1] = BM_edge_create(bm, f_verts[1], f_verts[2], NULL, BM_CREATE_NOP); + if (e_hflag_ok) { + BM_elem_flag_enable(f_edges[1], e_hflag[0]); + BM_elem_flag_disable(f_edges[1], e_hflag[1]); + } + } + + f_edges[3] = BM_edge_exists(f_verts[3], f_verts[0]); + if (f_edges[3] == NULL) { + char e_hflag[2]; + bool e_hflag_ok = bm_extrude_region_edge_flag(f_verts[3], e_hflag); + f_edges[3] = BM_edge_create(bm, f_verts[3], f_verts[0], NULL, BM_CREATE_NOP); + if (e_hflag_ok) { + BM_elem_flag_enable(f_edges[3], e_hflag[0]); + BM_elem_flag_disable(f_edges[3], e_hflag[1]); + } + } + + f = BM_face_create(bm, f_verts, f_edges, 4, NULL, BM_CREATE_NOP); +#else f = BM_face_create_verts(bm, f_verts, 4, NULL, BM_CREATE_NOP, true); +#endif + bm_extrude_copy_face_loop_attributes(bm, f); } @@ -608,10 +676,10 @@ static void calc_solidify_normals(BMesh *bm) } else { /* only one face attached to that edge */ - /* an edge without another attached- the weight on this is - * undefined, M_PI / 2 is 90d in radians and that seems good enough */ + /* an edge without another attached- the weight on this is undefined, + * M_PI_2 is 90d in radians and that seems good enough */ copy_v3_v3(edge_normal, f1->no); - mul_v3_fl(edge_normal, M_PI / 2); + mul_v3_fl(edge_normal, M_PI_2); } add_v3_v3(e->v1->no, edge_normal); diff --git a/source/blender/bmesh/operators/bmo_fill_attribute.c b/source/blender/bmesh/operators/bmo_fill_attribute.c index d9f50ac891c..85bca744d86 100644 --- a/source/blender/bmesh/operators/bmo_fill_attribute.c +++ b/source/blender/bmesh/operators/bmo_fill_attribute.c @@ -61,8 +61,9 @@ static bool bm_loop_is_face_untag(BMElem *ele, void *UNUSED(user_data)) /** * Copy all attributes from adjacent untagged faces. */ -static void bm_face_copy_shared_all(BMesh *bm, BMLoop *l, - const bool use_normals, const bool use_data) +static void bm_face_copy_shared_all( + BMesh *bm, BMLoop *l, + const bool use_normals, const bool use_data) { BMLoop *l_other = l->radial_next; BMFace *f = l->f, *f_other; @@ -90,8 +91,9 @@ static void bm_face_copy_shared_all(BMesh *bm, BMLoop *l, /** * Flood fill attributes. */ -static unsigned int bmesh_face_attribute_fill(BMesh *bm, - const bool use_normals, const bool use_data) +static unsigned int bmesh_face_attribute_fill( + BMesh *bm, + const bool use_normals, const bool use_data) { BLI_LINKSTACK_DECLARE(loop_queue_prev, BMLoop *); BLI_LINKSTACK_DECLARE(loop_queue_next, BMLoop *); diff --git a/source/blender/bmesh/operators/bmo_fill_grid.c b/source/blender/bmesh/operators/bmo_fill_grid.c index 40f6937245b..fd1e91a0b30 100644 --- a/source/blender/bmesh/operators/bmo_fill_grid.c +++ b/source/blender/bmesh/operators/bmo_fill_grid.c @@ -114,8 +114,9 @@ static void quad_verts_to_barycentric_tri( /** * Assign a loop pair from 2 verts (which _must_ share an edge) */ -static void bm_loop_pair_from_verts(BMVert *v_a, BMVert *v_b, - BMLoop *l_pair[2]) +static void bm_loop_pair_from_verts( + BMVert *v_a, BMVert *v_b, + BMLoop *l_pair[2]) { BMEdge *e = BM_edge_exists(v_a, v_b); if (e->l) { @@ -159,7 +160,7 @@ static void bm_loop_pair_test_copy(BMLoop *l_pair_a[2], BMLoop *l_pair_b[2]) */ static void bm_loop_interp_from_grid_boundary_4(BMesh *bm, BMLoop *l, BMLoop *l_bound[4], const float w[4]) { - void *l_cdata[4] = { + const void *l_cdata[4] = { l_bound[0]->head.data, l_bound[1]->head.data, l_bound[2]->head.data, @@ -170,8 +171,7 @@ static void bm_loop_interp_from_grid_boundary_4(BMesh *bm, BMLoop *l, BMLoop *l_ static void bm_loop_interp_from_grid_boundary_2(BMesh *bm, BMLoop *l, BMLoop *l_bound[2], const float t) { - - void *l_cdata[2] = { + const void *l_cdata[2] = { l_bound[0]->head.data, l_bound[1]->head.data}; @@ -186,8 +186,9 @@ static void bm_loop_interp_from_grid_boundary_2(BMesh *bm, BMLoop *l, BMLoop *l_ /** * Avoids calling #barycentric_weights_v2_quad often by caching weights into an array. */ -static void barycentric_weights_v2_grid_cache(const unsigned int xtot, const unsigned int ytot, - float (*weight_table)[4]) +static void barycentric_weights_v2_grid_cache( + const unsigned int xtot, const unsigned int ytot, + float (*weight_table)[4]) { float x_step = 1.0f / (float)(xtot - 1); float y_step = 1.0f / (float)(ytot - 1); @@ -217,9 +218,10 @@ static void barycentric_weights_v2_grid_cache(const unsigned int xtot, const uns * * \param v_grid 2d array of verts, all boundary verts must be set, we fill in the middle. */ -static void bm_grid_fill_array(BMesh *bm, BMVert **v_grid, const unsigned int xtot, unsigned const int ytot, - const short mat_nr, const bool use_smooth, - const bool use_flip, const bool use_interp_simple) +static void bm_grid_fill_array( + BMesh *bm, BMVert **v_grid, const unsigned int xtot, unsigned const int ytot, + const short mat_nr, const bool use_smooth, + const bool use_flip, const bool use_interp_simple) { const bool use_vert_interp = CustomData_has_interp(&bm->vdata); const bool use_loop_interp = CustomData_has_interp(&bm->ldata); @@ -346,7 +348,7 @@ static void bm_grid_fill_array(BMesh *bm, BMVert **v_grid, const unsigned int xt if (use_vert_interp) { const float *w = weight_table[XY(x, y)]; - void *v_cdata[4] = { + const void *v_cdata[4] = { v_grid[XY(x, 0)]->head.data, v_grid[XY(0, y)]->head.data, v_grid[XY(x, ytot - 1)]->head.data, @@ -486,10 +488,11 @@ static void bm_grid_fill_array(BMesh *bm, BMVert **v_grid, const unsigned int xt #undef XY } -static void bm_grid_fill(BMesh *bm, - struct BMEdgeLoopStore *estore_a, struct BMEdgeLoopStore *estore_b, - struct BMEdgeLoopStore *estore_rail_a, struct BMEdgeLoopStore *estore_rail_b, - const short mat_nr, const bool use_smooth, const bool use_interp_simple) +static void bm_grid_fill( + BMesh *bm, + struct BMEdgeLoopStore *estore_a, struct BMEdgeLoopStore *estore_b, + struct BMEdgeLoopStore *estore_rail_a, struct BMEdgeLoopStore *estore_rail_b, + const short mat_nr, const bool use_smooth, const bool use_interp_simple) { #define USE_FLIP_DETECT diff --git a/source/blender/bmesh/operators/bmo_hull.c b/source/blender/bmesh/operators/bmo_hull.c index 26a4dbe1e1d..2dfad5a1f47 100644 --- a/source/blender/bmesh/operators/bmo_hull.c +++ b/source/blender/bmesh/operators/bmo_hull.c @@ -67,8 +67,9 @@ typedef struct HullTriangle { /*************************** Hull Triangles ***************************/ -static void hull_add_triangle(BMesh *bm, GSet *hull_triangles, BLI_mempool *pool, - BMVert *v1, BMVert *v2, BMVert *v3) +static void hull_add_triangle( + BMesh *bm, GSet *hull_triangles, BLI_mempool *pool, + BMVert *v1, BMVert *v2, BMVert *v3) { HullTriangle *t; int i; @@ -189,8 +190,9 @@ static LinkData *final_edges_find_link(ListBase *adj, BMVert *v) return NULL; } -static int hull_final_edges_lookup(HullFinalEdges *final_edges, - BMVert *v1, BMVert *v2) +static int hull_final_edges_lookup( + HullFinalEdges *final_edges, + BMVert *v1, BMVert *v2) { ListBase *adj; @@ -259,8 +261,9 @@ static void hull_final_edges_free(HullFinalEdges *final_edges) /**************************** Final Output ****************************/ -static void hull_remove_overlapping(BMesh *bm, GSet *hull_triangles, - HullFinalEdges *final_edges) +static void hull_remove_overlapping( + BMesh *bm, GSet *hull_triangles, + HullFinalEdges *final_edges) { GSetIterator hull_iter; @@ -285,8 +288,8 @@ static void hull_remove_overlapping(BMesh *bm, GSet *hull_triangles, /* Note: can't change ghash while iterating, so mark * with 'skip' flag rather than deleting triangles */ - if (BM_vert_in_face(f, t->v[1]) && - BM_vert_in_face(f, t->v[2]) && f_on_hull) + if (BM_vert_in_face(t->v[1], f) && + BM_vert_in_face(t->v[2], f) && f_on_hull) { t->skip = true; BMO_elem_flag_disable(bm, f, HULL_FLAG_INTERIOR_ELE); @@ -296,8 +299,9 @@ static void hull_remove_overlapping(BMesh *bm, GSet *hull_triangles, } } -static void hull_mark_interior_elements(BMesh *bm, BMOperator *op, - HullFinalEdges *final_edges) +static void hull_mark_interior_elements( + BMesh *bm, BMOperator *op, + HullFinalEdges *final_edges) { BMEdge *e; BMFace *f; @@ -425,8 +429,9 @@ static int hull_input_vert_count(BMOperator *op) return count; } -static BMVert **hull_input_verts_copy(BMOperator *op, - const int num_input_verts) +static BMVert **hull_input_verts_copy( + BMOperator *op, + const int num_input_verts) { BMOIter oiter; BMVert *v; @@ -441,8 +446,9 @@ static BMVert **hull_input_verts_copy(BMOperator *op, return input_verts; } -static float (*hull_verts_for_bullet(BMVert **input_verts, - const int num_input_verts))[3] +static float (*hull_verts_for_bullet( + BMVert **input_verts, + const int num_input_verts))[3] { float (*coords)[3] = MEM_callocN(sizeof(*coords) * num_input_verts, __func__); int i; @@ -454,9 +460,10 @@ static float (*hull_verts_for_bullet(BMVert **input_verts, return coords; } -static BMVert **hull_verts_from_bullet(plConvexHull hull, - BMVert **input_verts, - const int num_input_verts) +static BMVert **hull_verts_from_bullet( + plConvexHull hull, + BMVert **input_verts, + const int num_input_verts) { const int num_verts = plConvexHullNumVertices(hull); BMVert **hull_verts = MEM_mallocN(sizeof(*hull_verts) * @@ -478,9 +485,10 @@ static BMVert **hull_verts_from_bullet(plConvexHull hull, return hull_verts; } -static void hull_from_bullet(BMesh *bm, BMOperator *op, - GSet *hull_triangles, - BLI_mempool *pool) +static void hull_from_bullet( + BMesh *bm, BMOperator *op, + GSet *hull_triangles, + BLI_mempool *pool) { int *fvi = NULL; BLI_array_declare(fvi); @@ -529,6 +537,9 @@ static void hull_from_bullet(BMesh *bm, BMOperator *op, } BLI_array_free(fvi); + + plConvexHullDelete(hull); + MEM_freeN(hull_verts); MEM_freeN(coords); MEM_freeN(input_verts); diff --git a/source/blender/bmesh/operators/bmo_inset.c b/source/blender/bmesh/operators/bmo_inset.c index b7914e84c50..118a19d3082 100644 --- a/source/blender/bmesh/operators/bmo_inset.c +++ b/source/blender/bmesh/operators/bmo_inset.c @@ -208,14 +208,11 @@ static void bm_loop_customdata_merge( */ const void *data_src; - CustomData_data_add( + CustomData_data_mix_value( type, BM_ELEM_CD_GET_VOID_P(l_a_inner_inset, offset), - BM_ELEM_CD_GET_VOID_P(l_b_inner_inset, offset)); - CustomData_data_multiply( - type, - BM_ELEM_CD_GET_VOID_P(l_a_inner_inset, offset), - 0.5f); + BM_ELEM_CD_GET_VOID_P(l_b_inner_inset, offset), + CDT_MIX_MIX, 0.5f); CustomData_data_copy_value( type, BM_ELEM_CD_GET_VOID_P(l_a_inner_inset, offset), @@ -390,8 +387,10 @@ static void bmo_face_inset_individual( if (use_interpolate) { - BM_face_interp_from_face_ex(bm, iface->f, iface->f, true, - iface->blocks_l, iface->blocks_v, iface->cos_2d, iface->axis_mat); + BM_face_interp_from_face_ex( + bm, iface->f, iface->f, true, + (const void **)iface->blocks_l, (const void **)iface->blocks_v, + iface->cos_2d, iface->axis_mat); /* build rim faces */ l_iter = l_first; @@ -658,9 +657,10 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op) es->l = es->e_old->l; /* must be a boundary */ } - /* run the separate arg */ - bmesh_edge_separate(bm, es->e_old, es->l, false); + if (!BM_edge_is_boundary(es->e_old)) { + bmesh_edge_separate(bm, es->e_old, es->l, false); + } /* calc edge-split info */ es->e_new = es->l->e; @@ -972,7 +972,7 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op) v_glue = v_split; } else { - BM_vert_splice(bm, v_split, v_glue); + BM_vert_splice(bm, v_glue, v_split); } } } @@ -993,8 +993,10 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op) for (i = 0; i < iface_array_len; i++) { if (iface_array[i]) { InterpFace *iface = iface_array[i]; - BM_face_interp_from_face_ex(bm, iface->f, iface->f, true, - iface->blocks_l, iface->blocks_v, iface->cos_2d, iface->axis_mat); + BM_face_interp_from_face_ex( + bm, iface->f, iface->f, true, + (const void **)iface->blocks_l, (const void **)iface->blocks_v, + iface->cos_2d, iface->axis_mat); } } } diff --git a/source/blender/bmesh/operators/bmo_join_triangles.c b/source/blender/bmesh/operators/bmo_join_triangles.c index 6562f26062f..3718f14276c 100644 --- a/source/blender/bmesh/operators/bmo_join_triangles.c +++ b/source/blender/bmesh/operators/bmo_join_triangles.c @@ -42,173 +42,276 @@ #include "intern/bmesh_operators_private.h" /* own include */ -#define FACE_OUT (1 << 0) - /* assumes edges are validated before reaching this poin */ -static float measure_facepair(const float v1[3], const float v2[3], - const float v3[3], const float v4[3], float limit) +static float quad_calc_error( + const float v1[3], const float v2[3], + const float v3[3], const float v4[3]) { /* gives a 'weight' to a pair of triangles that join an edge to decide how good a join they would make */ /* Note: this is more complicated than it needs to be and should be cleaned up.. */ - float n1[3], n2[3], measure = 0.0f, angle1, angle2, diff; - float edgeVec1[3], edgeVec2[3], edgeVec3[3], edgeVec4[3]; - float minarea, maxarea, areaA, areaB; - - /* First Test: Normal difference */ - normal_tri_v3(n1, v1, v2, v3); - normal_tri_v3(n2, v1, v3, v4); - angle1 = (compare_v3v3(n1, n2, FLT_EPSILON)) ? 0.0f : angle_normalized_v3v3(n1, n2); - - normal_tri_v3(n1, v2, v3, v4); - normal_tri_v3(n2, v4, v1, v2); - angle2 = (compare_v3v3(n1, n2, FLT_EPSILON)) ? 0.0f : angle_normalized_v3v3(n1, n2); - - measure += (angle1 + angle2) * 0.5f; - if (measure > limit) { - return measure; + float error = 0.0f; + + /* Normal difference */ + { + float n1[3], n2[3]; + float angle_a, angle_b; + float diff; + + normal_tri_v3(n1, v1, v2, v3); + normal_tri_v3(n2, v1, v3, v4); + angle_a = (compare_v3v3(n1, n2, FLT_EPSILON)) ? 0.0f : angle_normalized_v3v3(n1, n2); + + normal_tri_v3(n1, v2, v3, v4); + normal_tri_v3(n2, v4, v1, v2); + angle_b = (compare_v3v3(n1, n2, FLT_EPSILON)) ? 0.0f : angle_normalized_v3v3(n1, n2); + + diff = (angle_a + angle_b) / (float)(M_PI * 2); + + error += diff; } - /* Second test: Colinearity */ - sub_v3_v3v3(edgeVec1, v1, v2); - sub_v3_v3v3(edgeVec2, v2, v3); - sub_v3_v3v3(edgeVec3, v3, v4); - sub_v3_v3v3(edgeVec4, v4, v1); - - normalize_v3(edgeVec1); - normalize_v3(edgeVec2); - normalize_v3(edgeVec3); - normalize_v3(edgeVec4); - - /* a completely skinny face is 'pi' after halving */ - diff = 0.25f * (fabsf(angle_normalized_v3v3(edgeVec1, edgeVec2) - (float)M_PI_2) + - fabsf(angle_normalized_v3v3(edgeVec2, edgeVec3) - (float)M_PI_2) + - fabsf(angle_normalized_v3v3(edgeVec3, edgeVec4) - (float)M_PI_2) + - fabsf(angle_normalized_v3v3(edgeVec4, edgeVec1) - (float)M_PI_2)); - - if (!diff) { - return 0.0; + /* Colinearity */ + { + float edge_vecs[4][3]; + float diff; + + sub_v3_v3v3(edge_vecs[0], v1, v2); + sub_v3_v3v3(edge_vecs[1], v2, v3); + sub_v3_v3v3(edge_vecs[2], v3, v4); + sub_v3_v3v3(edge_vecs[3], v4, v1); + + normalize_v3(edge_vecs[0]); + normalize_v3(edge_vecs[1]); + normalize_v3(edge_vecs[2]); + normalize_v3(edge_vecs[3]); + + /* a completely skinny face is 'pi' after halving */ + diff = (fabsf(angle_normalized_v3v3(edge_vecs[0], edge_vecs[1]) - (float)M_PI_2) + + fabsf(angle_normalized_v3v3(edge_vecs[1], edge_vecs[2]) - (float)M_PI_2) + + fabsf(angle_normalized_v3v3(edge_vecs[2], edge_vecs[3]) - (float)M_PI_2) + + fabsf(angle_normalized_v3v3(edge_vecs[3], edge_vecs[0]) - (float)M_PI_2)) / (float)(M_PI * 2); + + error += diff; } - measure += diff; - if (measure > limit) { - return measure; + /* Concavity */ + { + float area_min, area_max, area_a, area_b; + float diff; + + area_a = area_tri_v3(v1, v2, v3) + area_tri_v3(v1, v3, v4); + area_b = area_tri_v3(v2, v3, v4) + area_tri_v3(v4, v1, v2); + + area_min = min_ff(area_a, area_b); + area_max = max_ff(area_a, area_b); + + diff = area_max ? (1.0f - (area_min / area_max)) : 1.0f; + + error += diff; } - /* Third test: Concavity */ - areaA = area_tri_v3(v1, v2, v3) + area_tri_v3(v1, v3, v4); - areaB = area_tri_v3(v2, v3, v4) + area_tri_v3(v4, v1, v2); + return error; +} + +static void bm_edge_to_quad_verts(const BMEdge *e, const BMVert *r_v_quad[4]) +{ + BLI_assert(e->l->f->len == 3 && e->l->radial_next->f->len == 3); + BLI_assert(BM_edge_is_manifold(e)); + r_v_quad[0] = e->l->v; + r_v_quad[1] = e->l->prev->v; + r_v_quad[2] = e->l->next->v; + r_v_quad[3] = e->l->radial_next->prev->v; +} + +/* cache customdata delimiters */ +struct DelimitData_CD { + int cd_type; + int cd_size; + int cd_offset; + int cd_offset_end; +}; + +struct DelimitData { + unsigned int do_seam : 1; + unsigned int do_sharp : 1; + unsigned int do_mat : 1; + unsigned int do_angle_face : 1; + unsigned int do_angle_shape : 1; - if (areaA <= areaB) minarea = areaA; - else minarea = areaB; + float angle_face; + float angle_face__cos; - if (areaA >= areaB) maxarea = areaA; - else maxarea = areaB; + float angle_shape; - if (!maxarea) measure += 1; - else measure += (1 - (minarea / maxarea)); + struct DelimitData_CD cdata[4]; + int cdata_len; +}; - return measure; +static bool bm_edge_is_contiguous_loop_cd_all( + const BMEdge *e, const struct DelimitData_CD *delimit_data) +{ + int cd_offset; + for (cd_offset = delimit_data->cd_offset; + cd_offset < delimit_data->cd_offset_end; + cd_offset += delimit_data->cd_size) + { + if (BM_edge_is_contiguous_loop_cd(e, delimit_data->cd_type, cd_offset) == false) { + return false; + } + } + + return true; } -#define T2QUV_LIMIT 0.005f -#define T2QCOL_LIMIT 3 +static bool bm_edge_delimit_cdata( + CustomData *ldata, CustomDataType type, + struct DelimitData_CD *r_delim_cd) +{ + const int layer_len = CustomData_number_of_layers(ldata, type); + r_delim_cd->cd_type = type; + r_delim_cd->cd_size = CustomData_sizeof(r_delim_cd->cd_type); + r_delim_cd->cd_offset = CustomData_get_n_offset(ldata, type, 0); + r_delim_cd->cd_offset_end = r_delim_cd->cd_size * layer_len; + return (r_delim_cd->cd_offset != -1); +} -static bool bm_edge_faces_cmp(BMesh *bm, BMEdge *e, const bool do_uv, const bool do_tf, const bool do_vcol) +static float bm_edge_is_delimit( + const BMEdge *e, + const struct DelimitData *delimit_data) { - /* first get loops */ - BMLoop *l[4]; - - l[0] = e->l; - l[2] = e->l->radial_next; - - /* match up loops on each side of an edge corresponding to each vert */ - if (l[0]->v == l[2]->v) { - l[1] = l[0]->next; - l[3] = l[1]->next; + BMFace *f_a = e->l->f, *f_b = e->l->radial_next->f; +#if 0 + const bool is_contig = BM_edge_is_contiguous(e); + float angle; +#endif + + if ((delimit_data->do_seam) && + (BM_elem_flag_test(e, BM_ELEM_SEAM))) + { + goto fail; } - else { - l[1] = l[0]->next; - l[3] = l[2]; - l[2] = l[3]->next; + if ((delimit_data->do_sharp) && + (BM_elem_flag_test(e, BM_ELEM_SMOOTH) == 0)) + { + goto fail; } - /* Test UV's */ - if (do_uv) { - const MLoopUV *luv[4] = { - CustomData_bmesh_get(&bm->ldata, l[0]->head.data, CD_MLOOPUV), - CustomData_bmesh_get(&bm->ldata, l[1]->head.data, CD_MLOOPUV), - CustomData_bmesh_get(&bm->ldata, l[2]->head.data, CD_MLOOPUV), - CustomData_bmesh_get(&bm->ldata, l[3]->head.data, CD_MLOOPUV), - }; - - /* do UV */ - if (luv[0] && (!compare_v2v2(luv[0]->uv, luv[2]->uv, T2QUV_LIMIT) || - !compare_v2v2(luv[1]->uv, luv[3]->uv, T2QUV_LIMIT))) - { - return false; + if ((delimit_data->do_mat) && + (f_a->mat_nr != f_b->mat_nr)) + { + goto fail; + } + + if (delimit_data->do_angle_face) { + if (dot_v3v3(f_a->no, f_b->no) < delimit_data->angle_face__cos) { + goto fail; } } - if (do_tf) { - const MTexPoly *tp[2] = { - CustomData_bmesh_get(&bm->pdata, l[0]->f->head.data, CD_MTEXPOLY), - CustomData_bmesh_get(&bm->pdata, l[1]->f->head.data, CD_MTEXPOLY), - }; + if (delimit_data->do_angle_shape) { + const BMVert *verts[4]; + bm_edge_to_quad_verts(e, verts); - if (tp[0] && (tp[0]->tpage != tp[1]->tpage)) { - return false; + /* if we're checking the shape at all, a flipped face is out of the question */ + if (is_quad_flip_v3(verts[0]->co, verts[1]->co, verts[2]->co, verts[3]->co)) { + goto fail; + } + else { + float edge_vecs[4][3]; + + sub_v3_v3v3(edge_vecs[0], verts[0]->co, verts[1]->co); + sub_v3_v3v3(edge_vecs[1], verts[1]->co, verts[2]->co); + sub_v3_v3v3(edge_vecs[2], verts[2]->co, verts[3]->co); + sub_v3_v3v3(edge_vecs[3], verts[3]->co, verts[0]->co); + + normalize_v3(edge_vecs[0]); + normalize_v3(edge_vecs[1]); + normalize_v3(edge_vecs[2]); + normalize_v3(edge_vecs[3]); + + if ((fabsf(angle_normalized_v3v3(edge_vecs[0], edge_vecs[1]) - (float)M_PI_2) > delimit_data->angle_shape) || + (fabsf(angle_normalized_v3v3(edge_vecs[1], edge_vecs[2]) - (float)M_PI_2) > delimit_data->angle_shape) || + (fabsf(angle_normalized_v3v3(edge_vecs[2], edge_vecs[3]) - (float)M_PI_2) > delimit_data->angle_shape) || + (fabsf(angle_normalized_v3v3(edge_vecs[3], edge_vecs[0]) - (float)M_PI_2) > delimit_data->angle_shape)) + { + goto fail; + } } } - /* Test Vertex Colors */ - if (do_vcol) { - const MLoopCol *lcol[4] = { - CustomData_bmesh_get(&bm->ldata, l[0]->head.data, CD_MLOOPCOL), - CustomData_bmesh_get(&bm->ldata, l[1]->head.data, CD_MLOOPCOL), - CustomData_bmesh_get(&bm->ldata, l[2]->head.data, CD_MLOOPCOL), - CustomData_bmesh_get(&bm->ldata, l[3]->head.data, CD_MLOOPCOL), - }; - - if (lcol[0]) { - if (!compare_rgb_uchar((unsigned char *)&lcol[0]->r, (unsigned char *)&lcol[2]->r, T2QCOL_LIMIT) || - !compare_rgb_uchar((unsigned char *)&lcol[1]->r, (unsigned char *)&lcol[3]->r, T2QCOL_LIMIT)) - { - return false; + if (delimit_data->cdata_len) { + int i; + for (i = 0; i < delimit_data->cdata_len; i++) { + if (!bm_edge_is_contiguous_loop_cd_all(e, &delimit_data->cdata[i])) { + goto fail; } } } + return false; + +fail: return true; } -#define EDGE_MARK 1 -#define EDGE_CHOSEN 2 - -#define FACE_MARK 1 -#define FACE_INPUT 2 - +#define EDGE_MARK (1 << 0) +#define FACE_OUT (1 << 0) +#define FACE_INPUT (1 << 2) void bmo_join_triangles_exec(BMesh *bm, BMOperator *op) { - const bool do_sharp = BMO_slot_bool_get(op->slots_in, "cmp_sharp"); - const bool do_uv = BMO_slot_bool_get(op->slots_in, "cmp_uvs"); - const bool do_tf = do_uv; /* texture face, make make its own option eventually */ - const bool do_vcol = BMO_slot_bool_get(op->slots_in, "cmp_vcols"); - const bool do_mat = BMO_slot_bool_get(op->slots_in, "cmp_materials"); - const float limit = BMO_slot_float_get(op->slots_in, "limit"); + float angle_face, angle_shape; BMIter iter; BMOIter siter; BMFace *f; - BMEdge *e, *e_next; + BMEdge *e; /* data: edge-to-join, sort_value: error weight */ struct SortPointerByFloat *jedges; unsigned i, totedge; unsigned int totedge_tag = 0; + struct DelimitData delimit_data = {0}; + + delimit_data.do_seam = BMO_slot_bool_get(op->slots_in, "cmp_seam"); + delimit_data.do_sharp = BMO_slot_bool_get(op->slots_in, "cmp_sharp"); + delimit_data.do_mat = BMO_slot_bool_get(op->slots_in, "cmp_materials"); + + angle_face = BMO_slot_float_get(op->slots_in, "angle_face_threshold"); + if (angle_face < DEG2RADF(180.0f)) { + delimit_data.angle_face = angle_face; + delimit_data.angle_face__cos = cosf(angle_face); + delimit_data.do_angle_face = true; + } + else { + delimit_data.do_angle_face = false; + } + + angle_shape = BMO_slot_float_get(op->slots_in, "angle_shape_threshold"); + if (angle_shape < DEG2RADF(180.0f)) { + delimit_data.angle_shape = angle_shape; + delimit_data.do_angle_shape = true; + } + else { + delimit_data.do_angle_shape = false; + } + + if (BMO_slot_bool_get(op->slots_in, "cmp_uvs") && + bm_edge_delimit_cdata(&bm->ldata, CD_MLOOPUV, &delimit_data.cdata[delimit_data.cdata_len])) + { + delimit_data.cdata_len += 1; + } + + delimit_data.cdata[delimit_data.cdata_len].cd_offset = -1; + if (BMO_slot_bool_get(op->slots_in, "cmp_vcols") && + bm_edge_delimit_cdata(&bm->ldata, CD_MLOOPCOL, &delimit_data.cdata[delimit_data.cdata_len])) + { + delimit_data.cdata_len += 1; + } + /* flag all edges of all input face */ BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) { if (f->len == 3) { @@ -220,10 +323,13 @@ void bmo_join_triangles_exec(BMesh *bm, BMOperator *op) BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { BMFace *f_a, *f_b; if (BM_edge_face_pair(e, &f_a, &f_b) && - (BMO_elem_flag_test(bm, f_a, FACE_INPUT) && BMO_elem_flag_test(bm, f_b, FACE_INPUT))) + (BMO_elem_flag_test(bm, f_a, FACE_INPUT) && + BMO_elem_flag_test(bm, f_b, FACE_INPUT))) { - BMO_elem_flag_enable(bm, e, EDGE_MARK); - totedge_tag++; + if (!bm_edge_is_delimit(e, &delimit_data)) { + BMO_elem_flag_enable(bm, e, EDGE_MARK); + totedge_tag++; + } } } @@ -236,36 +342,19 @@ void bmo_join_triangles_exec(BMesh *bm, BMOperator *op) i = 0; BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - BMVert *v1, *v2, *v3, *v4; - BMFace *f_a, *f_b; - float measure; + const BMVert *verts[4]; + float error; if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) continue; - f_a = e->l->f; - f_b = e->l->radial_next->f; + bm_edge_to_quad_verts(e, verts); - if (do_sharp && !BM_elem_flag_test(e, BM_ELEM_SMOOTH)) - continue; + error = quad_calc_error(verts[0]->co, verts[1]->co, verts[2]->co, verts[3]->co); - if (do_mat && f_a->mat_nr != f_b->mat_nr) - continue; - - if ((do_uv || do_tf || do_vcol) && (bm_edge_faces_cmp(bm, e, do_uv, do_tf, do_vcol) == false)) - continue; - - v1 = e->l->v; - v2 = e->l->prev->v; - v3 = e->l->next->v; - v4 = e->l->radial_next->prev->v; - - measure = measure_facepair(v1->co, v2->co, v3->co, v4->co, limit); - if (measure < limit) { - jedges[i].data = e; - jedges[i].sort_value = measure; - i++; - } + jedges[i].data = e; + jedges[i].sort_value = error; + i++; } totedge = i; @@ -279,27 +368,8 @@ void bmo_join_triangles_exec(BMesh *bm, BMOperator *op) f_b = e->l->radial_next->f; /* check if another edge already claimed this face */ - if ((BMO_elem_flag_test(bm, f_a, FACE_MARK) == false) || - (BMO_elem_flag_test(bm, f_b, FACE_MARK) == false)) - { - BMO_elem_flag_enable(bm, f_a, FACE_MARK); - BMO_elem_flag_enable(bm, f_b, FACE_MARK); - BMO_elem_flag_enable(bm, e, EDGE_CHOSEN); - } - } - - MEM_freeN(jedges); - - /* join best weighted */ - BM_ITER_MESH_MUTABLE (e, e_next, &iter, bm, BM_EDGES_OF_MESH) { - BMFace *f_new; - BMFace *f_a, *f_b; - - if (!BMO_elem_flag_test(bm, e, EDGE_CHOSEN)) - continue; - - BM_edge_face_pair(e, &f_a, &f_b); /* checked above */ - if ((f_a->len == 3 && f_b->len == 3)) { + if ((f_a->len == 3) && (f_b->len == 3)) { + BMFace *f_new; f_new = BM_faces_join_pair(bm, f_a, f_b, e, true); if (f_new) { BMO_elem_flag_enable(bm, f_new, FACE_OUT); @@ -307,39 +377,7 @@ void bmo_join_triangles_exec(BMesh *bm, BMOperator *op) } } - /* join 2-tri islands */ - BM_ITER_MESH_MUTABLE (e, e_next, &iter, bm, BM_EDGES_OF_MESH) { - if (BMO_elem_flag_test(bm, e, EDGE_MARK)) { - BMLoop *l_a, *l_b; - BMFace *f_a, *f_b; - - /* ok, this edge wasn't merged, check if it's - * in a 2-tri-pair island, and if so merge */ - l_a = e->l; - l_b = e->l->radial_next; - - f_a = l_a->f; - f_b = l_b->f; - - /* check the other 2 edges in both tris are untagged */ - if ((f_a->len == 3 && f_b->len == 3) && - (BMO_elem_flag_test(bm, l_a->next->e, EDGE_MARK) == false) && - (BMO_elem_flag_test(bm, l_a->prev->e, EDGE_MARK) == false) && - (BMO_elem_flag_test(bm, l_b->next->e, EDGE_MARK) == false) && - (BMO_elem_flag_test(bm, l_b->prev->e, EDGE_MARK) == false) && - /* check for faces that use same verts, this is supported but raises an error - * and cancels the operation when performed from editmode, since this is only - * two triangles we only need to compare a single vertex */ - (LIKELY(l_a->prev->v != l_b->prev->v))) - { - BMFace *f_new; - f_new = BM_faces_join_pair(bm, f_a, f_b, e, true); - if (f_new) { - BMO_elem_flag_enable(bm, f_new, FACE_OUT); - } - } - } - } + MEM_freeN(jedges); BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "faces.out", BM_FACE, FACE_OUT); } diff --git a/source/blender/bmesh/operators/bmo_normals.c b/source/blender/bmesh/operators/bmo_normals.c index d0f08871400..f62e445ca18 100644 --- a/source/blender/bmesh/operators/bmo_normals.c +++ b/source/blender/bmesh/operators/bmo_normals.c @@ -47,39 +47,79 @@ static bool bmo_recalc_normal_edge_filter_cb(BMElem *ele, void *UNUSED(user_data } /** - * Given an array of faces, recalculate their normals. - * this functions assumes all faces in the array are connected by edges. + * This uses a more comprehensive test to see if the furthest face from the center + * is pointing towards the center or not. * - * \param bm - * \param faces Array of connected faces. - * \param faces_len Length of \a faces - * \param oflag Flag to check before doing the actual face flipping. + * A simple test could just check the dot product of the faces-normal and the direction from the center, + * however this can fail for faces which make a sharp spike. eg: + * + * <pre> + * + + * |\ <- face + * + + + * \ \ + * \ \ + * \ +--------------+ + * \ | + * \ center -> + | + * \ | + * +------------+ + * </pre> + * + * In the example above, the a\ face can point towards the \a center + * which would end up flipping the normals inwards. + * + * To take these spikes into account, use the normals of the faces edges. */ -static void bmo_recalc_face_normals_array(BMesh *bm, BMFace **faces, const int faces_len, const short oflag) +#define USE_FACE_EDGE_NORMAL_TEST + +/** + * The center of the entire island is't necessarily well placed, + * + * This re-calculated a center relative to this face. + */ +#ifdef USE_FACE_EDGE_NORMAL_TEST +# define USE_FACE_LOCAL_CENTER_TEST +#endif + +/** + * \return a face index in \a faces and set \a r_is_flip if the face is flipped away from the center. + */ +static int recalc_face_normals_find_index(BMesh *bm, BMFace **faces, const int faces_len, bool *r_is_flip) { + float cent_area_accum = 0.0f; + float f_len_best_sq; + float cent[3], tvec[3]; - float (*faces_center)[3] = MEM_mallocN(sizeof(*faces_center) * faces_len, __func__); const float cent_fac = 1.0f / (float)faces_len; - int i, f_start_index; - const short oflag_flip = oflag | FACE_FLIP; - float f_len_best_sq; - BMFace *f; + float (*faces_center)[3] = MEM_mallocN(sizeof(*faces_center) * faces_len, __func__); + float *faces_area = MEM_mallocN(sizeof(*faces_area) * faces_len, __func__); + bool is_flip = false; + int f_start_index; + int i; - BLI_LINKSTACK_DECLARE(fstack, BMFace *); + UNUSED_VARS_NDEBUG(bm); zero_v3(cent); /* first calculate the center */ for (i = 0; i < faces_len; i++) { float *f_cent = faces_center[i]; + const float f_area = BM_face_calc_area(faces[i]); BM_face_calc_center_mean_weighted(faces[i], f_cent); - madd_v3_v3fl(cent, f_cent, cent_fac); + madd_v3_v3fl(cent, f_cent, cent_fac * f_area); + cent_area_accum += f_area; + faces_area[i] = f_area; BLI_assert(BMO_elem_flag_test(bm, faces[i], FACE_TEMP) == 0); BLI_assert(BM_face_is_normal_valid(faces[i])); } + if (cent_area_accum != 0.0f) { + mul_v3_fl(cent, 1.0f / cent_area_accum); + } + f_len_best_sq = -FLT_MAX; /* used in degenerate cases only */ f_start_index = 0; @@ -87,19 +127,104 @@ static void bmo_recalc_face_normals_array(BMesh *bm, BMFace **faces, const int f for (i = 0; i < faces_len; i++) { float f_len_test_sq; - if ((f_len_test_sq = len_squared_v3v3(faces_center[i], cent)) > f_len_best_sq) { - f_len_best_sq = f_len_test_sq; - f_start_index = i; + if (faces_area[i] > FLT_EPSILON) { + if ((f_len_test_sq = len_squared_v3v3(faces_center[i], cent)) > f_len_best_sq) { + f_len_best_sq = f_len_test_sq; + f_start_index = i; + } } } - /* make sure the starting face has the correct winding */ - sub_v3_v3v3(tvec, faces_center[f_start_index], cent); - if (dot_v3v3(tvec, faces[f_start_index]->no) < 0.0f) { - BMO_elem_flag_enable(bm, faces[f_start_index], FACE_FLIP); +#ifdef USE_FACE_EDGE_NORMAL_TEST + { + BMFace *f_test = faces[f_start_index]; + BMLoop *l_iter, *l_first; + float e_len_best_sq = -FLT_MAX; + BMLoop *l_other_best = NULL; + float no_edge[3]; + const float *no_best; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f_test); + do { + if (BM_edge_is_manifold(l_iter->e) && + bmo_recalc_normal_edge_filter_cb((BMElem *)l_iter->e, NULL)) + { + BMLoop *l_other = l_iter->radial_next; + + if (len_squared_v3v3(l_iter->v->co, l_iter->next->v->co) > FLT_EPSILON) { + float e_len_test_sq; + float e_cent[3]; + mid_v3_v3v3(e_cent, l_iter->v->co, l_iter->next->v->co); + e_len_test_sq = len_squared_v3v3(cent, e_cent); + if (e_len_test_sq > e_len_best_sq) { + l_other_best = l_other; + e_len_best_sq = e_len_test_sq; + } + } + } + } while ((l_iter = l_iter->next) != l_first); + + /* furthest edge on furthest face */ + if (l_other_best) { + float e_cent[3]; + +#ifdef USE_FACE_LOCAL_CENTER_TEST + { + float f_cent_other[3]; + BM_face_calc_center_mean_weighted(l_other_best->f, f_cent_other); + mid_v3_v3v3(cent, f_cent_other, faces_center[f_start_index]); + } +#endif + mid_v3_v3v3(e_cent, l_other_best->e->v1->co, l_other_best->e->v2->co); + sub_v3_v3v3(tvec, e_cent, cent); + + madd_v3_v3v3fl(no_edge, f_test->no, l_other_best->f->no, BM_edge_is_contiguous(l_other_best->e) ? 1 : -1); + no_best = no_edge; + } + else { + sub_v3_v3v3(tvec, faces_center[f_start_index], cent); + no_best = f_test->no; + } + + is_flip = (dot_v3v3(tvec, no_best) < 0.0f); } +#else + sub_v3_v3v3(tvec, faces_center[f_start_index], cent); + is_flip = (dot_v3v3(tvec, faces[f_start_index]->no) < 0.0f); +#endif + /* make sure the starting face has the correct winding */ MEM_freeN(faces_center); + MEM_freeN(faces_area); + + *r_is_flip = is_flip; + return f_start_index; +} + +/** + * Given an array of faces, recalculate their normals. + * this functions assumes all faces in the array are connected by edges. + * + * \param bm + * \param faces Array of connected faces. + * \param faces_len Length of \a faces + * \param oflag Flag to check before doing the actual face flipping. + */ +static void bmo_recalc_face_normals_array(BMesh *bm, BMFace **faces, const int faces_len, const short oflag) +{ + int i, f_start_index; + const short oflag_flip = oflag | FACE_FLIP; + bool is_flip; + + BMFace *f; + + BLI_LINKSTACK_DECLARE(fstack, BMFace *); + + f_start_index = recalc_face_normals_find_index(bm, faces, faces_len, &is_flip); + + if (is_flip) { + BMO_elem_flag_enable(bm, faces[f_start_index], FACE_FLIP); + } /* now that we've found our starting face, make all connected faces * have the same winding. this is done recursively, using a manual diff --git a/source/blender/bmesh/operators/bmo_offset_edgeloops.c b/source/blender/bmesh/operators/bmo_offset_edgeloops.c new file mode 100644 index 00000000000..a2f3f0bb519 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_offset_edgeloops.c @@ -0,0 +1,290 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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 2 + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/operators/bmo_offset_edgeloops.c + * \ingroup bmesh + * + * Simple edge offset functionality. + * + * \note Actual offset is done by edge-slide. + * (this only changes topology) + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_alloca.h" +#include "BLI_stackdefines.h" + +#include "BKE_customdata.h" + +#include "bmesh.h" + +#include "intern/bmesh_operators_private.h" /* own include */ + +#define USE_CAP_OPTION + +#define ELE_NEW (1 << 0) + +#ifdef USE_CAP_OPTION +#define ELE_VERT_ENDPOINT (1 << 1) +#endif + +/* set for debugging */ +#define OFFSET 0.0f + +static BMFace *bm_face_split_walk_back( + BMesh *bm, BMLoop *l_src, + BMLoop **r_l) +{ + float (*cos)[3]; + BMLoop *l_dst; + BMFace *f; + int num, i; + + for (l_dst = l_src->prev, num = 0; BM_elem_index_get(l_dst->prev->v) != -1; l_dst = l_dst->prev, num++) { + /* pass */ + } + + BLI_assert(num != 0); + + cos = BLI_array_alloca( + cos, num); + + for (l_dst = l_src->prev, i = 0; BM_elem_index_get(l_dst->prev->v) != -1; l_dst = l_dst->prev, i++) { + copy_v3_v3(cos[num - (i + 1)], l_dst->v->co); + } + + f = BM_face_split_n( bm, l_src->f, l_dst->prev, l_src->next, cos, num, r_l, NULL); + + return f; +} + +void bmo_offset_edgeloops_exec(BMesh *bm, BMOperator *op) +{ + const int edges_num = BMO_slot_buffer_count(op->slots_in, "edges"); + BMVert **verts; + STACK_DECLARE(verts); + int i; + +#ifdef USE_CAP_OPTION + bool use_cap_endpoint = BMO_slot_bool_get(op->slots_in, "use_cap_endpoint"); + int v_edges_max = 0; +#endif + + BMOIter oiter; + + /* only so we can detect new verts (index == -1) */ + BM_mesh_elem_index_ensure(bm, BM_VERT); + + BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); + + /* over alloc */ + verts = MEM_mallocN(sizeof(*verts) * (edges_num * 2), __func__); + + STACK_INIT(verts, (edges_num * 2)); + + { + BMEdge *e; + BMO_ITER (e, &oiter, op->slots_in, "edges", BM_EDGE) { + int j; + + BM_elem_flag_enable(e, BM_ELEM_TAG); + + for (j = 0; j < 2; j++) { + BMVert *v_edge = *(&(e->v1) + j); + if (!BM_elem_flag_test(v_edge, BM_ELEM_TAG)) { + BM_elem_flag_enable(v_edge, BM_ELEM_TAG); + STACK_PUSH(verts, v_edge); + } + } + } + } + + + /* -------------------------------------------------------------------- */ + /* Remove verts only used by tagged edges */ + + for (i = 0; i < STACK_SIZE(verts); i++) { + BMIter iter; + int flag = 0; + BMEdge *e; + + BM_ITER_ELEM (e, &iter, verts[i], BM_EDGES_OF_VERT) { + flag |= BM_elem_flag_test(e, BM_ELEM_TAG) ? 1 : 2; + if (flag == (1 | 2)) { + break; + } + } + + /* only boundary verts are interesting */ + if (flag != (1 | 2)) { + STACK_REMOVE(verts, i); + } + } + + /* possible but unlikely we have no mixed vertices */ + if (UNLIKELY(STACK_SIZE(verts) == 0)) { + MEM_freeN(verts); + return; + } + + /* main loop */ + for (i = 0; i < STACK_SIZE(verts); i++) { + int v_edges_num = 0; + int v_edges_num_untag = 0; + BMVert *v = verts[i]; + BMIter iter; + BMEdge *e; + + BM_ITER_ELEM (e, &iter, verts[i], BM_EDGES_OF_VERT) { + if (!BM_elem_flag_test(e, BM_ELEM_TAG)) { + BMVert *v_other; + BMIter liter; + BMLoop *l; + + BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) { + BM_elem_flag_enable(l->f, BM_ELEM_TAG); + } + + v_other = BM_edge_other_vert(e, v); + BM_edge_split(bm, e, v_other, NULL, 1.0f - OFFSET); + } + else { + v_edges_num_untag += 1; + } + + v_edges_num += 1; + } + +#ifdef USE_CAP_OPTION + if (v_edges_num_untag == 1) { + BMO_elem_flag_enable(bm, v, ELE_VERT_ENDPOINT); + } + + CLAMP_MIN(v_edges_max, v_edges_num); +#endif + + } + + + for (i = 0; i < STACK_SIZE(verts); i++) { + BMVert *v = verts[i]; + BMIter liter; + BMLoop *l; + + BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { + if (BM_elem_flag_test(l->f, BM_ELEM_TAG) && + (l->f->len != 3)) + { + BMFace *f_cmp = l->f; + if ((BM_elem_index_get(l->next->v) == -1) && + (BM_elem_index_get(l->prev->v) == -1)) + { +#ifdef USE_CAP_OPTION + if (use_cap_endpoint || (BMO_elem_flag_test(bm, v, ELE_VERT_ENDPOINT) == 0)) +#endif + { + BMLoop *l_new; + BM_face_split(bm, l->f, l->prev, l->next, &l_new, NULL, true); + BLI_assert(f_cmp == l->f); + BLI_assert(f_cmp != l_new->f); + UNUSED_VARS_NDEBUG(f_cmp); + BMO_elem_flag_enable(bm, l_new->e, ELE_NEW); + } + } + else if (l->f->len > 4) { + if (BM_elem_flag_test(l->e, BM_ELEM_TAG) != + BM_elem_flag_test(l->prev->e, BM_ELEM_TAG)) + { + if (BM_elem_index_get(l->next->v) == -1) { + if (BM_elem_index_get(l->prev->prev->v) == -1) { + BMLoop *l_new; + BM_face_split(bm, l->f, l->prev->prev, l->next, &l_new, NULL, true); + BLI_assert(f_cmp == l->f); + BLI_assert(f_cmp != l_new->f); + BMO_elem_flag_enable(bm, l_new->e, ELE_NEW); + BM_elem_flag_disable(l->f, BM_ELEM_TAG); + } + else { + /* walk backwards */ + BMLoop *l_new; + bm_face_split_walk_back(bm, l, &l_new); + do { + BMO_elem_flag_enable(bm, l_new->e, ELE_NEW); + l_new = l_new->next; + } while (BM_vert_is_edge_pair(l_new->v)); + BM_elem_flag_disable(l->f, BM_ELEM_TAG); + } + } + + /* Note: instead of duplicate code in alternate direction, + * we can be sure to hit the other vertex, so the code above runs. */ +#if 0 + else if (BM_elem_index_get(l->prev->v) == -1) { + if (BM_elem_index_get(l->next->next->v) == -1) { + } + } +#endif + } + } + } + } + } + +#ifdef USE_CAP_OPTION + if (use_cap_endpoint == false) { + BMVert **varr = BLI_array_alloca(varr, v_edges_max); + STACK_DECLARE(varr); + BMVert *v; + + for (i = 0; i < STACK_SIZE(verts); i++) { + BMIter iter; + BMEdge *e; + + v = verts[i]; + + STACK_INIT(varr, v_edges_max); + + BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) { + BMVert *v_other; + v_other = BM_edge_other_vert(e, v); + if (BM_elem_index_get(v_other) == -1) { + if (BM_vert_is_edge_pair(v_other)) { + /* defer bmesh_jekv to avoid looping over data we're removing */ + v_other->e = e; + STACK_PUSH(varr, v_other); + } + } + } + + while ((v = STACK_POP(varr))) { + bmesh_jekv(bm, v->e, v, true, false); + } + } + } +#endif + + MEM_freeN(verts); + + BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "edges.out", BM_EDGE, ELE_NEW); +} diff --git a/source/blender/bmesh/operators/bmo_planar_faces.c b/source/blender/bmesh/operators/bmo_planar_faces.c new file mode 100644 index 00000000000..2856d3d18a6 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_planar_faces.c @@ -0,0 +1,158 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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 2 + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/operators/bmo_planar_faces.c + * \ingroup bmesh + * + * Iteratively flatten 4+ sided faces. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_ghash.h" + +#include "bmesh.h" + +#include "intern/bmesh_operators_private.h" /* own include */ + +#define ELE_VERT_ADJUST (1 << 0) +#define ELE_FACE_ADJUST (1 << 1) + +struct VertAccum { + float co[3]; + int co_tot; +}; + +void bmo_planar_faces_exec(BMesh *bm, BMOperator *op) +{ + const float fac = BMO_slot_float_get(op->slots_in, "factor"); + const int iterations = BMO_slot_int_get(op->slots_in, "iterations"); + const int faces_num = BMO_slot_buffer_count(op->slots_in, "faces"); + + const float eps = 0.00001f; + const float eps_sq = SQUARE(eps); + + BMOIter oiter; + BMFace *f; + BLI_mempool *vert_accum_pool; + GHash *vaccum_map; + float (*faces_center)[3]; + int i, iter_step, shared_vert_num; + + faces_center = MEM_mallocN(sizeof(*faces_center) * faces_num, __func__); + + shared_vert_num = 0; + BMO_ITER_INDEX (f, &oiter, op->slots_in, "faces", BM_FACE, i) { + BMLoop *l_iter, *l_first; + + if (f->len == 3) { + continue; + } + + BM_face_calc_center_mean_weighted(f, faces_center[i]); + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (!BMO_elem_flag_test(bm, l_iter->v, ELE_VERT_ADJUST)) { + BMO_elem_flag_enable(bm, l_iter->v, ELE_VERT_ADJUST); + shared_vert_num += 1; + } + } while ((l_iter = l_iter->next) != l_first); + + BMO_elem_flag_enable(bm, f, ELE_FACE_ADJUST); + } + + vert_accum_pool = BLI_mempool_create(sizeof(struct VertAccum), 0, 512, BLI_MEMPOOL_NOP); + vaccum_map = BLI_ghash_ptr_new_ex(__func__, shared_vert_num); + + for (iter_step = 0; iter_step < iterations; iter_step++) { + GHashIterator gh_iter; + bool changed = false; + + BMO_ITER_INDEX (f, &oiter, op->slots_in, "faces", BM_FACE, i) { + BMLoop *l_iter, *l_first; + float plane[4]; + + if (!BMO_elem_flag_test(bm, f, ELE_FACE_ADJUST)) { + continue; + } + BMO_elem_flag_disable(bm, f, ELE_FACE_ADJUST); + + BLI_assert(f->len != 3); + + /* keep original face data (else we 'move' the face) */ +#if 0 + BM_face_normal_update(f); + BM_face_calc_center_mean_weighted(f, f_center); +#endif + + plane_from_point_normal_v3(plane, faces_center[i], f->no); + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + struct VertAccum *va; + void **va_p; + float co[3]; + + if (!BLI_ghash_ensure_p(vaccum_map, l_iter->v, &va_p)) { + *va_p = BLI_mempool_calloc(vert_accum_pool); + } + va = *va_p; + + closest_to_plane_normalized_v3(co, plane, l_iter->v->co); + va->co_tot += 1; + + interp_v3_v3v3(va->co, va->co, co, 1.0f / (float)va->co_tot); + } while ((l_iter = l_iter->next) != l_first); + } + + GHASH_ITER (gh_iter, vaccum_map) { + BMVert *v = BLI_ghashIterator_getKey(&gh_iter); + struct VertAccum *va = BLI_ghashIterator_getValue(&gh_iter); + BMIter iter; + + if (len_squared_v3v3(v->co, va->co) > eps_sq) { + BMO_elem_flag_enable(bm, v, ELE_VERT_ADJUST); + interp_v3_v3v3(v->co, v->co, va->co, fac); + changed = true; + } + + /* tag for re-calculation */ + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + if (f->len != 3) { + BMO_elem_flag_enable(bm, f, ELE_FACE_ADJUST); + } + } + } + + /* if nothing changed, break out early */ + if (changed == false) { + break; + } + + BLI_ghash_clear(vaccum_map, NULL, NULL); + BLI_mempool_clear(vert_accum_pool); + } + + MEM_freeN(faces_center); + BLI_ghash_free(vaccum_map, NULL, NULL); + BLI_mempool_destroy(vert_accum_pool); +} diff --git a/source/blender/bmesh/operators/bmo_primitive.c b/source/blender/bmesh/operators/bmo_primitive.c index e69dcca6342..2108a2c0589 100644 --- a/source/blender/bmesh/operators/bmo_primitive.c +++ b/source/blender/bmesh/operators/bmo_primitive.c @@ -662,78 +662,46 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op) void bmo_create_cube_exec(BMesh *bm, BMOperator *op) { - BMVert *v1, *v2, *v3, *v4, *v5, *v6, *v7, *v8; - float vec[3], mat[4][4], off = BMO_slot_float_get(op->slots_in, "size") / 2.0f; + BMVert *verts[8]; + float mat[4][4]; + float off = BMO_slot_float_get(op->slots_in, "size") / 2.0f; + int i, x, y, z; + const char faces[6][4] = { + {1, 3, 2, 0}, + {3, 7, 6, 2}, + {7, 5, 4, 6}, + {5, 1, 0, 4}, + {0, 2, 6, 4}, + {5, 7, 3, 1}, + }; BMO_slot_mat4_get(op->slots_in, "matrix", mat); if (!off) off = 0.5f; + i = 0; - vec[0] = -off; - vec[1] = -off; - vec[2] = -off; - mul_m4_v3(mat, vec); - v1 = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP); - BMO_elem_flag_enable(bm, v1, VERT_MARK); - - vec[0] = -off; - vec[1] = off; - vec[2] = -off; - mul_m4_v3(mat, vec); - v2 = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP); - BMO_elem_flag_enable(bm, v2, VERT_MARK); - - vec[0] = off; - vec[1] = off; - vec[2] = -off; - mul_m4_v3(mat, vec); - v3 = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP); - BMO_elem_flag_enable(bm, v3, VERT_MARK); - - vec[0] = off; - vec[1] = -off; - vec[2] = -off; - mul_m4_v3(mat, vec); - v4 = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP); - BMO_elem_flag_enable(bm, v4, VERT_MARK); - - vec[0] = -off; - vec[1] = -off; - vec[2] = off; - mul_m4_v3(mat, vec); - v5 = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP); - BMO_elem_flag_enable(bm, v5, VERT_MARK); - - vec[0] = -off; - vec[1] = off; - vec[2] = off; - mul_m4_v3(mat, vec); - v6 = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP); - BMO_elem_flag_enable(bm, v6, VERT_MARK); - - vec[0] = off; - vec[1] = off; - vec[2] = off; - mul_m4_v3(mat, vec); - v7 = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP); - BMO_elem_flag_enable(bm, v7, VERT_MARK); - - vec[0] = off; - vec[1] = -off; - vec[2] = off; - mul_m4_v3(mat, vec); - v8 = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP); - BMO_elem_flag_enable(bm, v8, VERT_MARK); - - /* the four sides */ - BM_face_create_quad_tri(bm, v5, v6, v2, v1, NULL, BM_CREATE_NOP); - BM_face_create_quad_tri(bm, v6, v7, v3, v2, NULL, BM_CREATE_NOP); - BM_face_create_quad_tri(bm, v7, v8, v4, v3, NULL, BM_CREATE_NOP); - BM_face_create_quad_tri(bm, v8, v5, v1, v4, NULL, BM_CREATE_NOP); - - /* top/bottom */ - BM_face_create_quad_tri(bm, v1, v2, v3, v4, NULL, BM_CREATE_NOP); - BM_face_create_quad_tri(bm, v8, v7, v6, v5, NULL, BM_CREATE_NOP); + for (x = -1; x < 2; x += 2) { + for (y = -1; y < 2; y += 2) { + for (z = -1; z < 2; z += 2) { + float vec[3] = {(float)x * off, (float)y * off, (float)z * off}; + mul_m4_v3(mat, vec); + verts[i] = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP); + BMO_elem_flag_enable(bm, verts[i], VERT_MARK); + i++; + } + } + } + + for (i = 0; i < ARRAY_SIZE(faces); i++) { + BMVert *quad[4] = { + verts[faces[i][0]], + verts[faces[i][1]], + verts[faces[i][2]], + verts[faces[i][3]], + }; + + BM_face_create_verts(bm, quad, 4, NULL, BM_CREATE_NOP, true); + } BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "verts.out", BM_VERT, VERT_MARK); } diff --git a/source/blender/bmesh/operators/bmo_removedoubles.c b/source/blender/bmesh/operators/bmo_removedoubles.c index 75f9feef413..42373ad0ef0 100644 --- a/source/blender/bmesh/operators/bmo_removedoubles.c +++ b/source/blender/bmesh/operators/bmo_removedoubles.c @@ -29,9 +29,9 @@ #include "MEM_guardedalloc.h" #include "BLI_math.h" -#include "BLI_array.h" #include "BLI_alloca.h" #include "BLI_stackdefines.h" +#include "BLI_stack.h" #include "BKE_customdata.h" @@ -198,7 +198,7 @@ void bmo_weld_verts_exec(BMesh *bm, BMOperator *op) BMO_elem_flag_enable(bm, e, EDGE_COL); } else if (!BM_edge_exists(v1, v2)) { - BM_edge_create(bm, v1, v2, e, BM_CREATE_NO_DOUBLE); + BM_edge_create(bm, v1, v2, e, BM_CREATE_NOP); } BMO_elem_flag_enable(bm, e, ELE_DEL); @@ -238,7 +238,7 @@ void bmo_weld_verts_exec(BMesh *bm, BMOperator *op) static int vergaverco(const void *e1, const void *e2) { - const BMVert *v1 = *(void **)e1, *v2 = *(void **)e2; + const BMVert *v1 = *(const void **)e1, *v2 = *(const void **)e2; float x1 = v1->co[0] + v1->co[1] + v1->co[2]; float x2 = v2->co[0] + v2->co[1] + v2->co[2]; @@ -261,7 +261,7 @@ void bmo_pointmerge_facedata_exec(BMesh *bm, BMOperator *op) BMOIter siter; BMIter iter; BMVert *v, *vert_snap; - BMLoop *l, *firstl = NULL; + BMLoop *l, *l_first = NULL; float fac; int i, tot; @@ -273,33 +273,35 @@ void bmo_pointmerge_facedata_exec(BMesh *bm, BMOperator *op) fac = 1.0f / tot; BM_ITER_ELEM (l, &iter, vert_snap, BM_LOOPS_OF_VERT) { - if (!firstl) { - firstl = l; + if (l_first == NULL) { + l_first = l; } for (i = 0; i < bm->ldata.totlayer; i++) { if (CustomData_layer_has_math(&bm->ldata, i)) { - int type = bm->ldata.layers[i].type; + const int type = bm->ldata.layers[i].type; + const int offset = bm->ldata.layers[i].offset; void *e1, *e2; - e1 = CustomData_bmesh_get_layer_n(&bm->ldata, firstl->head.data, i); - e2 = CustomData_bmesh_get_layer_n(&bm->ldata, l->head.data, i); + e1 = BM_ELEM_CD_GET_VOID_P(l_first, offset); + e2 = BM_ELEM_CD_GET_VOID_P(l, offset); CustomData_data_multiply(type, e2, fac); - if (l != firstl) + if (l != l_first) { CustomData_data_add(type, e1, e2); + } } } } BMO_ITER (v, &siter, op->slots_in, "verts", BM_VERT) { BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { - if (l == firstl) { + if (l == l_first) { continue; } - CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, firstl->head.data, &l->head.data); + CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, l_first->head.data, &l->head.data); } } } @@ -311,19 +313,20 @@ void bmo_average_vert_facedata_exec(BMesh *bm, BMOperator *op) BMVert *v; BMLoop *l /* , *firstl = NULL */; CDBlockBytes min, max; - void *block; - int i, type; + int i; for (i = 0; i < bm->ldata.totlayer; i++) { + const int type = bm->ldata.layers[i].type; + const int offset = bm->ldata.layers[i].offset; + if (!CustomData_layer_has_math(&bm->ldata, i)) continue; - type = bm->ldata.layers[i].type; CustomData_data_initminmax(type, &min, &max); BMO_ITER (v, &siter, op->slots_in, "verts", BM_VERT) { BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { - block = CustomData_bmesh_get_layer_n(&bm->ldata, l->head.data, i); + void *block = BM_ELEM_CD_GET_VOID_P(l, offset); CustomData_data_dominmax(type, block, &min, &max); } } @@ -334,7 +337,7 @@ void bmo_average_vert_facedata_exec(BMesh *bm, BMOperator *op) BMO_ITER (v, &siter, op->slots_in, "verts", BM_VERT) { BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { - block = CustomData_bmesh_get_layer_n(&bm->ldata, l->head.data, i); + void *block = BM_ELEM_CD_GET_VOID_P(l, offset); CustomData_data_copy_value(type, &min, block); } } @@ -375,13 +378,14 @@ void bmo_collapse_exec(BMesh *bm, BMOperator *op) BMOperator weldop; BMWalker walker; BMIter iter; - BMEdge *e, **edges = NULL; - BLI_array_declare(edges); - float min[3], max[3], center[3]; - unsigned int i, tot; + BMEdge *e; + BLI_Stack *edge_stack; BMOpSlot *slot_targetmap; - - BMO_op_callf(bm, op->flag, "collapse_uvs edges=%s", op, "edges"); + + if (BMO_slot_bool_get(op->slots_in, "uvs")) { + BMO_op_callf(bm, op->flag, "collapse_uvs edges=%s", op, "edges"); + } + BMO_op_init(bm, &weldop, op->flag, "weld_verts"); slot_targetmap = BMO_slot_get(weldop.slots_in, "targetmap"); @@ -392,18 +396,20 @@ void bmo_collapse_exec(BMesh *bm, BMOperator *op) BMW_FLAG_NOP, /* no need to use BMW_FLAG_TEST_HIDDEN, already marked data */ BMW_NIL_LAY); + edge_stack = BLI_stack_new(sizeof(BMEdge *), __func__); + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + float min[3], max[3], center[3]; BMVert *v_tar; if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) continue; - BLI_array_empty(edges); + BLI_assert(BLI_stack_is_empty(edge_stack)); INIT_MINMAX(min, max); - for (e = BMW_begin(&walker, e->v1), tot = 0; e; e = BMW_step(&walker), tot++) { - BLI_array_grow_one(edges); - edges[tot] = e; + for (e = BMW_begin(&walker, e->v1); e; e = BMW_step(&walker)) { + BLI_stack_push(edge_stack, &e); minmax_v3v3_v3(min, max, e->v1->co); minmax_v3v3_v3(min, max, e->v2->co); @@ -413,79 +419,90 @@ void bmo_collapse_exec(BMesh *bm, BMOperator *op) BM_elem_flag_disable(e->v2, BM_ELEM_TAG); } - mid_v3_v3v3(center, min, max); + if (!BLI_stack_is_empty(edge_stack)) { + + mid_v3_v3v3(center, min, max); - /* snap edges to a point. for initial testing purposes anyway */ - v_tar = edges[0]->v1; + /* snap edges to a point. for initial testing purposes anyway */ + e = *(BMEdge **)BLI_stack_peek(edge_stack); + v_tar = e->v1; - for (i = 0; i < tot; i++) { - unsigned int j; + while (!BLI_stack_is_empty(edge_stack)) { + unsigned int j; + BLI_stack_pop(edge_stack, &e); - for (j = 0; j < 2; j++) { - BMVert *v_src = *((&edges[i]->v1) + j); + for (j = 0; j < 2; j++) { + BMVert *v_src = *((&e->v1) + j); - copy_v3_v3(v_src->co, center); - if ((v_src != v_tar) && !BM_elem_flag_test(v_src, BM_ELEM_TAG)) { - BM_elem_flag_enable(v_src, BM_ELEM_TAG); - BMO_slot_map_elem_insert(&weldop, slot_targetmap, v_src, v_tar); + copy_v3_v3(v_src->co, center); + if ((v_src != v_tar) && !BM_elem_flag_test(v_src, BM_ELEM_TAG)) { + BM_elem_flag_enable(v_src, BM_ELEM_TAG); + BMO_slot_map_elem_insert(&weldop, slot_targetmap, v_src, v_tar); + } } } } } - + + BLI_stack_free(edge_stack); + BMO_op_exec(bm, &weldop); BMO_op_finish(bm, &weldop); BMW_end(&walker); - BLI_array_free(edges); } /* uv collapse function */ static void bmo_collapsecon_do_layer(BMesh *bm, const int layer, const short oflag) { + const int type = bm->ldata.layers[layer].type; + const int offset = bm->ldata.layers[layer].offset; BMIter iter, liter; BMFace *f; BMLoop *l, *l2; BMWalker walker; - void **blocks = NULL; - BLI_array_declare(blocks); + BLI_Stack *block_stack; CDBlockBytes min, max; - int i, tot, type = bm->ldata.layers[layer].type; BMW_init(&walker, bm, BMW_LOOPDATA_ISLAND, BMW_MASK_NOP, oflag, BMW_MASK_NOP, BMW_FLAG_NOP, /* no need to use BMW_FLAG_TEST_HIDDEN, already marked data */ layer); + block_stack = BLI_stack_new(sizeof(void *), __func__); + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { if (BMO_elem_flag_test(bm, l->e, oflag)) { /* walk */ - BLI_array_empty(blocks); + BLI_assert(BLI_stack_is_empty(block_stack)); CustomData_data_initminmax(type, &min, &max); - for (l2 = BMW_begin(&walker, l), tot = 0; l2; l2 = BMW_step(&walker), tot++) { - BLI_array_grow_one(blocks); - blocks[tot] = CustomData_bmesh_get_layer_n(&bm->ldata, l2->head.data, layer); - CustomData_data_dominmax(type, blocks[tot], &min, &max); + for (l2 = BMW_begin(&walker, l); l2; l2 = BMW_step(&walker)) { + void *block = BM_ELEM_CD_GET_VOID_P(l2, offset); + CustomData_data_dominmax(type, block, &min, &max); + BLI_stack_push(block_stack, &block); } - if (tot) { + if (!BLI_stack_is_empty(block_stack)) { CustomData_data_multiply(type, &min, 0.5f); CustomData_data_multiply(type, &max, 0.5f); CustomData_data_add(type, &min, &max); /* snap CD (uv, vcol) points to their centroid */ - for (i = 0; i < tot; i++) { - CustomData_data_copy_value(type, &min, blocks[i]); + while (!BLI_stack_is_empty(block_stack)) { + void *block; + BLI_stack_pop(block_stack, &block); + CustomData_data_copy_value(type, &min, block); } } } } } + BLI_stack_free(block_stack); + BMW_end(&walker); - BLI_array_free(blocks); } void bmo_collapse_uvs_exec(BMesh *bm, BMOperator *op) @@ -519,8 +536,9 @@ void bmo_collapse_uvs_exec(BMesh *bm, BMOperator *op) } -static void bmesh_find_doubles_common(BMesh *bm, BMOperator *op, - BMOperator *optarget, BMOpSlot *optarget_slot) +static void bmesh_find_doubles_common( + BMesh *bm, BMOperator *op, + BMOperator *optarget, BMOpSlot *optarget_slot) { BMVert **verts; int verts_len; @@ -529,7 +547,7 @@ static void bmesh_find_doubles_common(BMesh *bm, BMOperator *op, const float dist = BMO_slot_float_get(op->slots_in, "dist"); const float dist_sq = dist * dist; - const float dist3 = (M_SQRT3 + 0.00005f) * dist; /* Just above sqrt(3) */ + const float dist3 = ((float)M_SQRT3 + 0.00005f) * dist; /* Just above sqrt(3) */ /* Test whether keep_verts arg exists and is non-empty */ if (BMO_slot_exists(op->slots_in, "keep_verts")) { diff --git a/source/blender/bmesh/operators/bmo_similar.c b/source/blender/bmesh/operators/bmo_similar.c index f779828a00d..708d57a7a08 100644 --- a/source/blender/bmesh/operators/bmo_similar.c +++ b/source/blender/bmesh/operators/bmo_similar.c @@ -102,7 +102,6 @@ void bmo_similar_faces_exec(BMesh *bm, BMOperator *op) float angle = 0.0f; SimSel_FaceExt *f_ext = NULL; int *indices = NULL; - float t_no[3]; /* temporary normal */ const int type = BMO_slot_int_get(op->slots_in, "type"); const float thresh = BMO_slot_float_get(op->slots_in, "thresh"); const float thresh_radians = thresh * (float)M_PI; @@ -115,7 +114,7 @@ void bmo_similar_faces_exec(BMesh *bm, BMOperator *op) num_total = BM_mesh_elem_count(bm, BM_FACE); /* - * The first thing to do is to iterate through all the the selected items and mark them since + * The first thing to do is to iterate through all the selected items and mark them since * they will be in the selection anyway. * This will increase performance, (especially when the number of originally selected faces is high) * so the overall complexity will be less than $O(mn)$ where is the total number of selected faces, @@ -158,12 +157,8 @@ void bmo_similar_faces_exec(BMesh *bm, BMOperator *op) /* compute the center of the polygon */ BM_face_calc_center_mean(f_ext[i].f, f_ext[i].c); - /* normalize the polygon normal */ - copy_v3_v3(t_no, f_ext[i].f->no); - normalize_v3(t_no); - /* compute the plane distance */ - f_ext[i].d = dot_v3v3(t_no, f_ext[i].c); + f_ext[i].d = dot_v3v3(f_ext[i].f->no, f_ext[i].c); break; case SIMFACE_AREA: @@ -212,16 +207,23 @@ void bmo_similar_faces_exec(BMesh *bm, BMOperator *op) break; case SIMFACE_COPLANAR: + { + float sign = 1.0f; angle = angle_normalized_v3v3(fs->no, fm->no); /* angle -> 0 */ + /* allow for normal pointing in either direction (just check the plane) */ + if (angle > (float)M_PI * 0.5f) { + angle = (float)M_PI - angle; + sign = -1.0f; + } if (angle <= thresh_radians) { /* and dot product difference -> 0 */ - delta_fl = f_ext[i].d - f_ext[indices[idx]].d; + delta_fl = f_ext[i].d - (f_ext[indices[idx]].d * sign); if (bm_sel_similar_cmp_fl(delta_fl, thresh, compare)) { BMO_elem_flag_enable(bm, fm, FACE_MARK); cont = false; } } break; - + } case SIMFACE_AREA: delta_fl = f_ext[i].area - f_ext[indices[idx]].area; if (bm_sel_similar_cmp_fl(delta_fl, thresh, compare)) { @@ -245,6 +247,13 @@ void bmo_similar_faces_exec(BMesh *bm, BMOperator *op) cont = false; } break; + + case SIMFACE_SMOOTH: + if (BM_elem_flag_test(fm, BM_ELEM_SMOOTH) == BM_elem_flag_test(fs, BM_ELEM_SMOOTH)) { + BMO_elem_flag_enable(bm, fm, FACE_MARK); + cont = false; + } + break; #ifdef WITH_FREESTYLE case SIMFACE_FREESTYLE: if (CustomData_has_layer(&bm->pdata, CD_FREESTYLE_FACE)) { @@ -405,10 +414,10 @@ void bmo_similar_edges_exec(BMesh *bm, BMOperator *op) /* compute the angle between the two edges */ angle = angle_normalized_v3v3(e_ext[i].dir, e_ext[indices[idx]].dir); - if (angle > (float)(M_PI / 2.0)) /* use the smallest angle between the edges */ + if (angle > (float)M_PI_2) /* use the smallest angle between the edges */ angle = fabsf(angle - (float)M_PI); - if (angle / (float)(M_PI / 2.0) <= thresh) { + if (angle / (float)M_PI_2 <= thresh) { BMO_elem_flag_enable(bm, e, EDGE_MARK); cont = false; } diff --git a/source/blender/bmesh/operators/bmo_subdivide.c b/source/blender/bmesh/operators/bmo_subdivide.c index fc507cdbd21..45e3c8d193d 100644 --- a/source/blender/bmesh/operators/bmo_subdivide.c +++ b/source/blender/bmesh/operators/bmo_subdivide.c @@ -32,6 +32,7 @@ #include "BLI_rand.h" #include "BLI_array.h" #include "BLI_noise.h" +#include "BLI_stack.h" #include "BKE_customdata.h" @@ -79,8 +80,9 @@ static void bmo_subd_init_shape_info(BMesh *bm, SubDParams *params) } -typedef void (*subd_pattern_fill_fp)(BMesh *bm, BMFace *face, BMVert **verts, - const SubDParams *params); +typedef void (*subd_pattern_fill_fp)( + BMesh *bm, BMFace *face, BMVert **verts, + const SubDParams *params); /* * note: this is a pattern-based edge subdivider. @@ -162,10 +164,11 @@ static BMEdge *connect_smallest_face(BMesh *bm, BMVert *v_a, BMVert *v_b, BMFace return NULL; } /* calculates offset for co, based on fractal, sphere or smooth settings */ -static void alter_co(BMVert *v, BMEdge *UNUSED(origed), const SubDParams *params, float perc, - BMVert *vsta, BMVert *vend) +static void alter_co( + BMVert *v, BMEdge *UNUSED(e_orig), + const SubDParams *params, const float perc, + const BMVert *v_a, const BMVert *v_b) { - float tvec[3], fac; float *co = BM_ELEM_CD_GET_VOID_P(v, params->shape_info.cd_vert_shape_offset_tmp); int i; @@ -177,28 +180,26 @@ static void alter_co(BMVert *v, BMEdge *UNUSED(origed), const SubDParams *params } else if (params->use_smooth) { /* we calculate an offset vector vec1[], to be added to *co */ - float len, nor[3], nor1[3], nor2[3], val; + float dir[3], tvec[3]; + float fac, len, val; - sub_v3_v3v3(nor, vsta->co, vend->co); - len = 0.5f * normalize_v3(nor); - - copy_v3_v3(nor1, vsta->no); - copy_v3_v3(nor2, vend->no); + sub_v3_v3v3(dir, v_a->co, v_b->co); + len = (float)M_SQRT1_2 * normalize_v3(dir); /* cosine angle */ - fac = dot_v3v3(nor, nor1); - mul_v3_v3fl(tvec, nor1, fac); + fac = dot_v3v3(dir, v_a->no); + mul_v3_v3fl(tvec, v_a->no, fac); /* cosine angle */ - fac = -dot_v3v3(nor, nor2); - madd_v3_v3fl(tvec, nor2, fac); + fac = -dot_v3v3(dir, v_b->no); + madd_v3_v3fl(tvec, v_b->no, fac); /* falloff for multi subdivide */ val = fabsf(1.0f - 2.0f * fabsf(0.5f - perc)); val = bmesh_subd_falloff_calc(params->smooth_falloff, val); if (params->use_smooth_even) { - val *= BM_vert_calc_shell_factor(v); + val *= shell_v3v3_mid_normalized_to_dist(v_a->no, v_b->no); } mul_v3_fl(tvec, params->smooth * val * len); @@ -207,12 +208,13 @@ static void alter_co(BMVert *v, BMEdge *UNUSED(origed), const SubDParams *params } if (params->use_fractal) { - const float len = len_v3v3(vsta->co, vend->co); - float normal[3], co2[3], base1[3], base2[3]; + float normal[3], co2[3], base1[3], base2[3], tvec[3]; + const float len = len_v3v3(v_a->co, v_b->co); + float fac; fac = params->fractal * len; - mid_v3_v3v3(normal, vsta->no, vend->no); + mid_v3_v3v3(normal, v_a->no, v_b->no); ortho_basis_v3v3_v3(base1, base2, normal); add_v3_v3v3(co2, v->co, params->fractal_ofs); @@ -233,9 +235,12 @@ static void alter_co(BMVert *v, BMEdge *UNUSED(origed), const SubDParams *params * this by getting the normals and coords for each shape key and * re-calculate the smooth value for each but this is quite involved. * for now its ok to simply apply the difference IMHO - campbell */ - sub_v3_v3v3(tvec, v->co, co); if (params->shape_info.totlayer > 1) { + float tvec[3]; + + sub_v3_v3v3(tvec, v->co, co); + /* skip the last layer since its the temp */ i = params->shape_info.totlayer - 1; co = BM_ELEM_CD_GET_VOID_P(v, params->shape_info.cd_vert_shape_offset); @@ -249,19 +254,21 @@ static void alter_co(BMVert *v, BMEdge *UNUSED(origed), const SubDParams *params /* assumes in the edge is the correct interpolated vertices already */ /* percent defines the interpolation, rad and flag are for special options */ /* results in new vertex with correct coordinate, vertex normal and weight group info */ -static BMVert *bm_subdivide_edge_addvert(BMesh *bm, BMEdge *edge, BMEdge *oedge, - const SubDParams *params, float percent, - float percent2, - BMEdge **out, BMVert *vsta, BMVert *vend) +static BMVert *bm_subdivide_edge_addvert( + BMesh *bm, BMEdge *edge, BMEdge *e_orig, + const SubDParams *params, + const float factor_edge_split, const float factor_subd, + BMVert *v_a, BMVert *v_b, + BMEdge **r_edge) { - BMVert *ev; + BMVert *v_new; - ev = BM_edge_split(bm, edge, edge->v1, out, percent); + v_new = BM_edge_split(bm, edge, edge->v1, r_edge, factor_edge_split); - BMO_elem_flag_enable(bm, ev, ELE_INNER); + BMO_elem_flag_enable(bm, v_new, ELE_INNER); /* offset for smooth or sphere or fractal */ - alter_co(ev, oedge, params, percent2, vsta, vend); + alter_co(v_new, e_orig, params, factor_subd, v_a, v_b); #if 0 //BMESH_TODO /* clip if needed by mirror modifier */ @@ -278,35 +285,40 @@ static BMVert *bm_subdivide_edge_addvert(BMesh *bm, BMEdge *edge, BMEdge *oedge, } #endif - interp_v3_v3v3(ev->no, vsta->no, vend->no, percent2); - normalize_v3(ev->no); + interp_v3_v3v3(v_new->no, v_a->no, v_b->no, factor_subd); + normalize_v3(v_new->no); - return ev; + return v_new; } -static BMVert *subdivideedgenum(BMesh *bm, BMEdge *edge, BMEdge *oedge, - int curpoint, int totpoint, const SubDParams *params, - BMEdge **newe, BMVert *vsta, BMVert *vend) +static BMVert *subdivide_edge_num( + BMesh *bm, BMEdge *edge, BMEdge *e_orig, + int curpoint, int totpoint, const SubDParams *params, + BMVert *v_a, BMVert *v_b, + BMEdge **r_edge) { - BMVert *ev; - float percent, percent2 = 0.0f; + BMVert *v_new; + float factor_edge_split, factor_subd; if (BMO_elem_flag_test(bm, edge, EDGE_PERCENT) && totpoint == 1) { - percent = BMO_slot_map_float_get(params->slot_edge_percents, edge); + factor_edge_split = BMO_slot_map_float_get(params->slot_edge_percents, edge); + factor_subd = 0.0f; } else { - percent = 1.0f / (float)(totpoint + 1 - curpoint); - percent2 = (float)(curpoint + 1) / (float)(totpoint + 1); - + factor_edge_split = 1.0f / (float)(totpoint + 1 - curpoint); + factor_subd = (float)(curpoint + 1) / (float)(totpoint + 1); } - ev = bm_subdivide_edge_addvert(bm, edge, oedge, params, percent, - percent2, newe, vsta, vend); - return ev; + v_new = bm_subdivide_edge_addvert( + bm, edge, e_orig, params, + factor_edge_split, factor_subd, + v_a, v_b, r_edge); + return v_new; } -static void bm_subdivide_multicut(BMesh *bm, BMEdge *edge, const SubDParams *params, - BMVert *vsta, BMVert *vend) +static void bm_subdivide_multicut( + BMesh *bm, BMEdge *edge, const SubDParams *params, + BMVert *v_a, BMVert *v_b) { BMEdge *eed = edge, *e_new, e_tmp = *edge; BMVert *v, v1_tmp = *edge->v1, v2_tmp = *edge->v2, *v1 = edge->v1, *v2 = edge->v2; @@ -316,15 +328,11 @@ static void bm_subdivide_multicut(BMesh *bm, BMEdge *edge, const SubDParams *par e_tmp.v2 = &v2_tmp; for (i = 0; i < numcuts; i++) { - v = subdivideedgenum(bm, eed, &e_tmp, i, params->numcuts, params, &e_new, vsta, vend); - - BMO_elem_flag_enable(bm, v, SUBD_SPLIT); - BMO_elem_flag_enable(bm, eed, SUBD_SPLIT); - BMO_elem_flag_enable(bm, e_new, SUBD_SPLIT); + v = subdivide_edge_num(bm, eed, &e_tmp, i, params->numcuts, params, v_a, v_b, &e_new); - BMO_elem_flag_enable(bm, v, ELE_SPLIT); - BMO_elem_flag_enable(bm, eed, ELE_SPLIT); - BMO_elem_flag_enable(bm, e_new, SUBD_SPLIT); + BMO_elem_flag_enable(bm, v, SUBD_SPLIT | ELE_SPLIT); + BMO_elem_flag_enable(bm, eed, SUBD_SPLIT | ELE_SPLIT); + BMO_elem_flag_enable(bm, e_new, SUBD_SPLIT | ELE_SPLIT); BM_CHECK_ELEMENT(v); if (v->e) BM_CHECK_ELEMENT(v->e); @@ -437,7 +445,7 @@ static void quad_2edge_split_innervert(BMesh *bm, BMFace *UNUSED(face), BMVert * e = connect_smallest_face(bm, verts[i], verts[numcuts + (numcuts - i)], &f_new); e_tmp = *e; - v = bm_subdivide_edge_addvert(bm, e, &e_tmp, params, 0.5f, 0.5f, &e_new, e->v1, e->v2); + v = bm_subdivide_edge_addvert(bm, e, &e_tmp, params, 0.5f, 0.5f, e->v1, e->v2, &e_new); if (i != numcuts - 1) { connect_smallest_face(bm, v_last, v, &f_new); @@ -580,8 +588,7 @@ static void quad_4edge_subdivide(BMesh *bm, BMFace *UNUSED(face), BMVert **verts e_tmp = *e; for (a = 0; a < numcuts; a++) { - v = subdivideedgenum(bm, e, &e_tmp, a, numcuts, params, &e_new, - v1, v2); + v = subdivide_edge_num(bm, e, &e_tmp, a, numcuts, params, v1, v2, &e_new); BMESH_ASSERT(v != NULL); @@ -688,8 +695,7 @@ static void tri_3edge_subdivide(BMesh *bm, BMFace *UNUSED(face), BMVert **verts, e_tmp.v1 = &v1_tmp; e_tmp.v2 = &v2_tmp; for (j = 0; j < i; j++) { - v = subdivideedgenum(bm, e, &e_tmp, j, i, params, &e_new, - verts[a], verts[b]); + v = subdivide_edge_num(bm, e, &e_tmp, j, i, params, verts[a], verts[b], &e_new); lines[i + 1][j + 1] = v; BMO_elem_flag_enable(bm, e_new, ELE_INNER); @@ -766,8 +772,7 @@ void bmo_subdivide_edges_exec(BMesh *bm, BMOperator *op) BMOpSlot *einput; const SubDPattern *pat; SubDParams params; - SubDFaceData *facedata = NULL; - BLI_array_declare(facedata); + BLI_Stack *facedata; BMIter viter, fiter, liter; BMVert *v, **verts = NULL; BMEdge *edge; @@ -782,7 +787,7 @@ void bmo_subdivide_edges_exec(BMesh *bm, BMOperator *op) BLI_array_declare(verts); float smooth, fractal, along_normal; bool use_sphere, use_single_edge, use_grid_fill, use_only_quads; - int cornertype, seed, i, j, matched, a, b, numcuts, totesel, smooth_falloff; + int cornertype, seed, i, j, a, b, numcuts, totesel, smooth_falloff; BMO_slot_buffer_flag_enable(bm, op->slots_in, "edges", BM_EDGE, SUBD_SPLIT); @@ -802,13 +807,13 @@ void bmo_subdivide_edges_exec(BMesh *bm, BMOperator *op) patterns[1] = NULL; /* straight cut is patterns[1] == NULL */ switch (cornertype) { - case SUBD_PATH: + case SUBD_CORNER_PATH: patterns[1] = &quad_2edge_path; break; - case SUBD_INNERVERT: + case SUBD_CORNER_INNERVERT: patterns[1] = &quad_2edge_innervert; break; - case SUBD_FAN: + case SUBD_CORNER_FAN: patterns[1] = &quad_2edge_fan; break; } @@ -875,9 +880,12 @@ void bmo_subdivide_edges_exec(BMesh *bm, BMOperator *op) BM_EDGE, EDGE_PERCENT); + facedata = BLI_stack_new(sizeof(SubDFaceData), __func__); + BM_ITER_MESH (face, &fiter, bm, BM_FACES_OF_MESH) { BMEdge *e1 = NULL, *e2 = NULL; float vec1[3], vec2[3]; + bool matched = false; /* skip non-quads if requested */ if (use_only_quads && face->len != 4) @@ -891,8 +899,6 @@ void bmo_subdivide_edges_exec(BMesh *bm, BMOperator *op) BLI_array_grow_items(edges, face->len); BLI_array_grow_items(verts, face->len); - matched = 0; - totesel = 0; BM_ITER_ELEM_INDEX (l_new, &liter, face, BM_LOOPS_OF_FACE, i) { edges[i] = l_new->e; @@ -930,12 +936,13 @@ void bmo_subdivide_edges_exec(BMesh *bm, BMOperator *op) } } if (matched) { - BLI_array_grow_one(facedata); - b = BLI_array_count(facedata) - 1; - facedata[b].pat = pat; - facedata[b].start = verts[i]; - facedata[b].face = face; - facedata[b].totedgesel = totesel; + SubDFaceData *fd; + + fd = BLI_stack_push_r(facedata); + fd->pat = pat; + fd->start = verts[i]; + fd->face = face; + fd->totedgesel = totesel; BMO_elem_flag_enable(bm, face, SUBD_SPLIT); break; } @@ -966,15 +973,15 @@ void bmo_subdivide_edges_exec(BMesh *bm, BMOperator *op) } } if (matched) { - BLI_array_grow_one(facedata); - j = BLI_array_count(facedata) - 1; + SubDFaceData *fd; BMO_elem_flag_enable(bm, face, SUBD_SPLIT); - facedata[j].pat = pat; - facedata[j].start = verts[a]; - facedata[j].face = face; - facedata[j].totedgesel = totesel; + fd = BLI_stack_push_r(facedata); + fd->pat = pat; + fd->start = verts[a]; + fd->face = face; + fd->totedgesel = totesel; break; } } @@ -982,16 +989,16 @@ void bmo_subdivide_edges_exec(BMesh *bm, BMOperator *op) } if (!matched && totesel) { - BLI_array_grow_one(facedata); - j = BLI_array_count(facedata) - 1; + SubDFaceData *fd; BMO_elem_flag_enable(bm, face, SUBD_SPLIT); /* must initialize all members here */ - facedata[j].start = NULL; - facedata[j].pat = NULL; - facedata[j].totedgesel = totesel; - facedata[j].face = face; + fd = BLI_stack_push_r(facedata); + fd->start = NULL; + fd->pat = NULL; + fd->totedgesel = totesel; + fd->face = face; } } @@ -1009,16 +1016,17 @@ void bmo_subdivide_edges_exec(BMesh *bm, BMOperator *op) copy_v3_v3(v->co, co); } - i = 0; - for (i = 0; i < BLI_array_count(facedata); i++) { - face = facedata[i].face; + for (; !BLI_stack_is_empty(facedata); BLI_stack_discard(facedata)) { + SubDFaceData *fd = BLI_stack_peek(facedata); + + face = fd->face; /* figure out which pattern to use */ BLI_array_empty(verts); - pat = facedata[i].pat; + pat = fd->pat; - if (!pat && facedata[i].totedgesel == 2) { + if (!pat && fd->totedgesel == 2) { int vlen; /* ok, no pattern. we still may be able to do something */ @@ -1079,7 +1087,7 @@ void bmo_subdivide_edges_exec(BMesh *bm, BMOperator *op) BMIter other_fiter; BM_ITER_ELEM (other_loop, &other_fiter, loops[a]->v, BM_LOOPS_OF_VERT) { if (other_loop->f != face) { - if (BM_vert_in_face(other_loop->f, loops[b]->v)) { + if (BM_vert_in_face(loops[b]->v, other_loop->f)) { /* we assume that these verts are not making an edge in the face */ BLI_assert(other_loop->prev->v != loops[a]->v); BLI_assert(other_loop->next->v != loops[a]->v); @@ -1131,7 +1139,7 @@ void bmo_subdivide_edges_exec(BMesh *bm, BMOperator *op) a = 0; BM_ITER_ELEM_INDEX (l_new, &liter, face, BM_LOOPS_OF_FACE, j) { - if (l_new->v == facedata[i].start) { + if (l_new->v == fd->start) { a = j + 1; break; } @@ -1156,7 +1164,7 @@ void bmo_subdivide_edges_exec(BMesh *bm, BMOperator *op) BM_data_layer_free_n(bm, &bm->vdata, CD_SHAPEKEY, params.shape_info.tmpkey); - if (facedata) BLI_array_free(facedata); + BLI_stack_free(facedata); if (edges) BLI_array_free(edges); if (verts) BLI_array_free(verts); BLI_array_free(loops_split); @@ -1169,14 +1177,15 @@ void bmo_subdivide_edges_exec(BMesh *bm, BMOperator *op) } /* editmesh-emulating function */ -void BM_mesh_esubdivide(BMesh *bm, const char edge_hflag, - const float smooth, const short smooth_falloff, const bool use_smooth_even, - const float fractal, const float along_normal, - const int numcuts, - const int seltype, const int cornertype, - const short use_single_edge, const short use_grid_fill, - const short use_only_quads, - const int seed) +void BM_mesh_esubdivide( + BMesh *bm, const char edge_hflag, + const float smooth, const short smooth_falloff, const bool use_smooth_even, + const float fractal, const float along_normal, + const int numcuts, + const int seltype, const int cornertype, + const short use_single_edge, const short use_grid_fill, + const short use_only_quads, + const int seed) { BMOperator op; diff --git a/source/blender/bmesh/operators/bmo_subdivide_edgering.c b/source/blender/bmesh/operators/bmo_subdivide_edgering.c index c01ad10d716..0e619b4cece 100644 --- a/source/blender/bmesh/operators/bmo_subdivide_edgering.c +++ b/source/blender/bmesh/operators/bmo_subdivide_edgering.c @@ -80,8 +80,9 @@ static unsigned int bm_verts_tag_count(BMesh *bm) } #endif -static float bezier_handle_calc_length_v3(const float co_a[3], const float no_a[3], - const float co_b[3], const float no_b[3]) +static float bezier_handle_calc_length_v3( + const float co_a[3], const float no_a[3], + const float co_b[3], const float no_b[3]) { const float dot = dot_v3v3(no_a, no_b); /* gives closest approx at a circle with 2 parallel handles */ @@ -538,12 +539,13 @@ static void bm_edgering_pair_store_free( /* -------------------------------------------------------------------- */ /* Interpolation Function */ -static void bm_edgering_pair_interpolate(BMesh *bm, LoopPairStore *lpair, - struct BMEdgeLoopStore *el_store_a, - struct BMEdgeLoopStore *el_store_b, - ListBase *eloops_ring, - const int interp_mode, const int cuts, const float smooth, - const float *falloff_cache) +static void bm_edgering_pair_interpolate( + BMesh *bm, LoopPairStore *lpair, + struct BMEdgeLoopStore *el_store_a, + struct BMEdgeLoopStore *el_store_b, + ListBase *eloops_ring, + const int interp_mode, const int cuts, const float smooth, + const float *falloff_cache) { const int resolu = cuts + 2; const int dims = 3; @@ -878,9 +880,10 @@ static bool bm_edgering_pair_order_is_flipped(BMesh *UNUSED(bm), * Takes 2 edge loops that share edges, * sort their verts and rotates the list so the lined up. */ -static void bm_edgering_pair_order(BMesh *bm, - struct BMEdgeLoopStore *el_store_a, - struct BMEdgeLoopStore *el_store_b) +static void bm_edgering_pair_order( + BMesh *bm, + struct BMEdgeLoopStore *el_store_a, + struct BMEdgeLoopStore *el_store_b) { ListBase *lb_a = BM_edgeloop_verts_get(el_store_a); ListBase *lb_b = BM_edgeloop_verts_get(el_store_b); @@ -951,11 +954,12 @@ static void bm_edgering_pair_order(BMesh *bm, * * \note loops are _not_ aligned. */ -static void bm_edgering_pair_subdiv(BMesh *bm, - struct BMEdgeLoopStore *el_store_a, - struct BMEdgeLoopStore *el_store_b, - ListBase *eloops_ring, - const int cuts) +static void bm_edgering_pair_subdiv( + BMesh *bm, + struct BMEdgeLoopStore *el_store_a, + struct BMEdgeLoopStore *el_store_b, + ListBase *eloops_ring, + const int cuts) { ListBase *lb_a = BM_edgeloop_verts_get(el_store_a); // ListBase *lb_b = BM_edgeloop_verts_get(el_store_b); @@ -1043,11 +1047,12 @@ static void bm_edgering_pair_subdiv(BMesh *bm, bm_edgeloop_vert_tag(el_store_b, false); } -static void bm_edgering_pair_ringsubd(BMesh *bm, LoopPairStore *lpair, - struct BMEdgeLoopStore *el_store_a, - struct BMEdgeLoopStore *el_store_b, - const int interp_mode, const int cuts, const float smooth, - const float *falloff_cache) +static void bm_edgering_pair_ringsubd( + BMesh *bm, LoopPairStore *lpair, + struct BMEdgeLoopStore *el_store_a, + struct BMEdgeLoopStore *el_store_b, + const int interp_mode, const int cuts, const float smooth, + const float *falloff_cache) { ListBase eloops_ring = {NULL}; bm_edgering_pair_order(bm, el_store_a, el_store_b); diff --git a/source/blender/bmesh/operators/bmo_triangulate.c b/source/blender/bmesh/operators/bmo_triangulate.c index 986583cc21b..cb9ba5e361c 100644 --- a/source/blender/bmesh/operators/bmo_triangulate.c +++ b/source/blender/bmesh/operators/bmo_triangulate.c @@ -75,6 +75,7 @@ void bmo_triangle_fill_exec(BMesh *bm, BMOperator *op) GHash *sf_vert_map; float normal[3]; const int scanfill_flag = BLI_SCANFILL_CALC_HOLES | BLI_SCANFILL_CALC_POLYS | BLI_SCANFILL_CALC_LOOSE; + unsigned int nors_tot; bool calc_winding = false; sf_vert_map = BLI_ghash_ptr_new_ex(__func__, BMO_slot_buffer_count(op->slots_in, "edges")); @@ -103,6 +104,7 @@ void bmo_triangle_fill_exec(BMesh *bm, BMOperator *op) /* sf_edge = */ BLI_scanfill_edge_add(&sf_ctx, UNPACK2(sf_verts)); /* sf_edge->tmp.p = e; */ /* UNUSED */ } + nors_tot = BLI_ghash_size(sf_vert_map); BLI_ghash_free(sf_vert_map, NULL, NULL); @@ -111,7 +113,6 @@ void bmo_triangle_fill_exec(BMesh *bm, BMOperator *op) * Since we don't know winding, just accumulate */ ScanFillVert *sf_vert; struct SortNormal *nors; - const unsigned int nors_tot = BLI_ghash_size(sf_vert_map); unsigned int i; bool is_degenerate = true; @@ -182,7 +183,11 @@ void bmo_triangle_fill_exec(BMesh *bm, BMOperator *op) calc_winding = false; } - normalize_v3(normal); + /* in this case we almost certainly have degenerate geometry, + * better set a fallback value as a last resort */ + if (UNLIKELY(normalize_v3(normal) == 0.0f)) { + normal[2] = 1.0f; + } BLI_scanfill_calc_ex(&sf_ctx, scanfill_flag, normal); diff --git a/source/blender/bmesh/operators/bmo_utils.c b/source/blender/bmesh/operators/bmo_utils.c index d2d1c0854de..964d0b1dfc6 100644 --- a/source/blender/bmesh/operators/bmo_utils.c +++ b/source/blender/bmesh/operators/bmo_utils.c @@ -177,42 +177,112 @@ void bmo_rotate_edges_exec(BMesh *bm, BMOperator *op) #define SEL_FLAG 1 #define SEL_ORIG 2 -static void bmo_region_extend_extend(BMesh *bm, BMOperator *op, const bool use_faces) +static void bmo_face_flag_set_flush(BMesh *bm, BMFace *f, const short oflag, const bool value) +{ + BMLoop *l_iter; + BMLoop *l_first; + + BMO_elem_flag_set(bm, f, oflag, value); + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BMO_elem_flag_set(bm, l_iter->e, oflag, value); + BMO_elem_flag_set(bm, l_iter->v, oflag, value); + } while ((l_iter = l_iter->next) != l_first); +} + + +static void bmo_region_extend_expand( + BMesh *bm, BMOperator *op, + const bool use_faces, const bool use_faces_step) { - BMVert *v; - BMEdge *e; - BMIter eiter; BMOIter siter; if (!use_faces) { + BMVert *v; + BMO_ITER (v, &siter, op->slots_in, "geom", BM_VERT) { - BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { - if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) - if (!BMO_elem_flag_test(bm, e, SEL_ORIG)) + bool found = false; + + { + BMIter eiter; + BMEdge *e; + + BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { + if (!BMO_elem_flag_test(bm, e, SEL_ORIG) && !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { + found = true; break; + } + } } - if (e) { - BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { - if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { - BMO_elem_flag_enable(bm, e, SEL_FLAG); - BMO_elem_flag_enable(bm, BM_edge_other_vert(e, v), SEL_FLAG); + if (found) { + if (!use_faces_step) { + BMIter eiter; + BMEdge *e; + + BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { + if (!BMO_elem_flag_test(bm, e, SEL_FLAG) && !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { + BMO_elem_flag_enable(bm, e, SEL_FLAG); + BMO_elem_flag_enable(bm, BM_edge_other_vert(e, v), SEL_FLAG); + } + } + } + else { + BMIter fiter; + BMFace *f; + + BM_ITER_ELEM (f, &fiter, v, BM_FACES_OF_VERT) { + if (!BMO_elem_flag_test(bm, f, SEL_FLAG) && !BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + bmo_face_flag_set_flush(bm, f, SEL_FLAG, true); + } + } + + /* handle wire edges (when stepping over faces) */ + { + BMIter eiter; + BMEdge *e; + BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { + if (BM_edge_is_wire(e)) { + if (!BMO_elem_flag_test(bm, e, SEL_FLAG) && !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { + BMO_elem_flag_enable(bm, e, SEL_FLAG); + BMO_elem_flag_enable(bm, BM_edge_other_vert(e, v), SEL_FLAG); + } + } + } } } } } } else { - BMIter liter, fiter; - BMFace *f, *f2; - BMLoop *l; + BMFace *f; BMO_ITER (f, &siter, op->slots_in, "geom", BM_FACE) { + BMIter liter; + BMLoop *l; + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - BM_ITER_ELEM (f2, &fiter, l->e, BM_FACES_OF_EDGE) { - if (!BM_elem_flag_test(f2, BM_ELEM_HIDDEN)) { - if (!BMO_elem_flag_test(bm, f2, SEL_ORIG)) { - BMO_elem_flag_enable(bm, f2, SEL_FLAG); + if (!use_faces_step) { + BMIter fiter; + BMFace *f_other; + + BM_ITER_ELEM (f_other, &fiter, l->e, BM_FACES_OF_EDGE) { + if (!BMO_elem_flag_test(bm, f_other, SEL_ORIG | SEL_FLAG) && + !BM_elem_flag_test(f_other, BM_ELEM_HIDDEN)) + { + BMO_elem_flag_enable(bm, f_other, SEL_FLAG); + } + } + } + else { + BMIter fiter; + BMFace *f_other; + + BM_ITER_ELEM (f_other, &fiter, l->v, BM_FACES_OF_VERT) { + if (!BMO_elem_flag_test(bm, f_other, SEL_ORIG | SEL_FLAG) && + !BM_elem_flag_test(f_other, BM_ELEM_HIDDEN)) + { + BMO_elem_flag_enable(bm, f_other, SEL_FLAG); } } } @@ -221,43 +291,94 @@ static void bmo_region_extend_extend(BMesh *bm, BMOperator *op, const bool use_f } } -static void bmo_region_extend_constrict(BMesh *bm, BMOperator *op, const bool use_faces) +static void bmo_region_extend_contract( + BMesh *bm, BMOperator *op, + const bool use_faces, const bool use_faces_step) { - BMVert *v; - BMEdge *e; - BMIter eiter; BMOIter siter; if (!use_faces) { + BMVert *v; + BMO_ITER (v, &siter, op->slots_in, "geom", BM_VERT) { - BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { - if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) - if (!BMO_elem_flag_test(bm, e, SEL_ORIG)) + bool found = false; + + if (!use_faces_step) { + BMIter eiter; + BMEdge *e; + + BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { + if (!BMO_elem_flag_test(bm, e, SEL_ORIG)) { + found = true; break; + } + } } + else { + BMIter fiter; + BMFace *f; - if (e) { - BMO_elem_flag_enable(bm, v, SEL_FLAG); + BM_ITER_ELEM (f, &fiter, v, BM_FACES_OF_VERT) { + if (!BMO_elem_flag_test(bm, f, SEL_ORIG)) { + found = true; + break; + } + } - BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { - if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { - BMO_elem_flag_enable(bm, e, SEL_FLAG); + /* handle wire edges (when stepping over faces) */ + if (!found) { + BMIter eiter; + BMEdge *e; + + BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { + if (BM_edge_is_wire(e)) { + if (!BMO_elem_flag_test(bm, e, SEL_ORIG)) { + found = true; + break; + } + } } } + } + + if (found) { + BMIter eiter; + BMEdge *e; + + BMO_elem_flag_enable(bm, v, SEL_FLAG); + BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { + BMO_elem_flag_enable(bm, e, SEL_FLAG); + } } } } else { - BMIter liter, fiter; - BMFace *f, *f2; - BMLoop *l; + BMFace *f; BMO_ITER (f, &siter, op->slots_in, "geom", BM_FACE) { + BMIter liter; + BMLoop *l; + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - BM_ITER_ELEM (f2, &fiter, l->e, BM_FACES_OF_EDGE) { - if (!BM_elem_flag_test(f2, BM_ELEM_HIDDEN)) { - if (!BMO_elem_flag_test(bm, f2, SEL_ORIG)) { + + if (!use_faces_step) { + BMIter fiter; + BMFace *f_other; + + BM_ITER_ELEM (f_other, &fiter, l->e, BM_FACES_OF_EDGE) { + if (!BMO_elem_flag_test(bm, f_other, SEL_ORIG)) { + BMO_elem_flag_enable(bm, f, SEL_FLAG); + break; + } + } + } + else { + BMIter fiter; + BMFace *f_other; + + BM_ITER_ELEM (f_other, &fiter, l->v, BM_FACES_OF_VERT) { + if (!BMO_elem_flag_test(bm, f_other, SEL_ORIG)) { BMO_elem_flag_enable(bm, f, SEL_FLAG); break; } @@ -271,14 +392,17 @@ static void bmo_region_extend_constrict(BMesh *bm, BMOperator *op, const bool us void bmo_region_extend_exec(BMesh *bm, BMOperator *op) { const bool use_faces = BMO_slot_bool_get(op->slots_in, "use_faces"); - const bool constrict = BMO_slot_bool_get(op->slots_in, "use_constrict"); + const bool use_face_step = BMO_slot_bool_get(op->slots_in, "use_face_step"); + const bool constrict = BMO_slot_bool_get(op->slots_in, "use_contract"); BMO_slot_buffer_flag_enable(bm, op->slots_in, "geom", BM_ALL_NOLOOP, SEL_ORIG); - if (constrict) - bmo_region_extend_constrict(bm, op, use_faces); - else - bmo_region_extend_extend(bm, op, use_faces); + if (constrict) { + bmo_region_extend_contract(bm, op, use_faces, use_face_step); + } + else { + bmo_region_extend_expand(bm, op, use_faces, use_face_step); + } BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "geom.out", BM_ALL_NOLOOP, SEL_FLAG); } @@ -291,6 +415,7 @@ void bmo_smooth_vert_exec(BMesh *UNUSED(bm), BMOperator *op) BMEdge *e; float (*cos)[3] = MEM_mallocN(sizeof(*cos) * BMO_slot_buffer_count(op->slots_in, "verts"), __func__); float *co, *co2, clip_dist = BMO_slot_float_get(op->slots_in, "clip_dist"); + const float fac = BMO_slot_float_get(op->slots_in, "factor"); int i, j, clipx, clipy, clipz; int xaxis, yaxis, zaxis; @@ -322,7 +447,7 @@ void bmo_smooth_vert_exec(BMesh *UNUSED(bm), BMOperator *op) } mul_v3_fl(co, 1.0f / (float)j); - mid_v3_v3v3(co, co, v->co); + interp_v3_v3v3(co, v->co, co, fac); if (clipx && fabsf(v->co[0]) <= clip_dist) co[0] = 0.0f; diff --git a/source/blender/bmesh/operators/bmo_wireframe.c b/source/blender/bmesh/operators/bmo_wireframe.c index 62409fc3987..ac81dde93c0 100644 --- a/source/blender/bmesh/operators/bmo_wireframe.c +++ b/source/blender/bmesh/operators/bmo_wireframe.c @@ -26,8 +26,6 @@ * Creates a solid wireframe from connected faces. */ -#include "MEM_guardedalloc.h" - #include "DNA_material_types.h" #include "BLI_sys_types.h" diff --git a/source/blender/bmesh/tools/bmesh_beautify.c b/source/blender/bmesh/tools/bmesh_beautify.c index 6639e767e77..19fe492c670 100644 --- a/source/blender/bmesh/tools/bmesh_beautify.c +++ b/source/blender/bmesh/tools/bmesh_beautify.c @@ -37,6 +37,7 @@ #include "BLI_math.h" #include "BLI_heap.h" +#include "BLI_polyfill2d_beautify.h" #include "MEM_guardedalloc.h" @@ -96,7 +97,7 @@ static GSet *erot_gset_new(void) static void erot_state_ex(const BMEdge *e, int v_index[2], int f_index[2]) { - BLI_assert(BM_edge_is_manifold((BMEdge *)e)); + BLI_assert(BM_edge_is_manifold(e)); BLI_assert(BM_vert_in_edge(e, e->l->prev->v) == false); BLI_assert(BM_vert_in_edge(e, e->l->radial_next->prev->v) == false); @@ -125,22 +126,22 @@ static void erot_state_alternate(const BMEdge *e, EdRotState *e_state) /* -------------------------------------------------------------------- */ /* Calculate the improvement of rotating the edge */ -/** - * \return a negative value means the edge can be rotated. - */ static float bm_edge_calc_rotate_beauty__area( const float v1[3], const float v2[3], const float v3[3], const float v4[3]) { /* not a loop (only to be able to break out) */ do { float v1_xy[2], v2_xy[2], v3_xy[2], v4_xy[2]; - bool is_zero_a, is_zero_b; /* first get the 2d values */ { + const float eps = 1e-5; float no_a[3], no_b[3]; float no[3]; float axis_mat[3][3]; + float no_scale; + cross_tri_v3(no_a, v2, v3, v4); + cross_tri_v3(no_b, v2, v4, v1); // printf("%p %p %p %p - %p %p\n", v1, v2, v3, v4, e->l->f, e->l->radial_next->f); BLI_assert((ELEM(v1, v2, v3, v4) == false) && @@ -148,97 +149,40 @@ static float bm_edge_calc_rotate_beauty__area( (ELEM(v3, v1, v2, v4) == false) && (ELEM(v4, v1, v2, v3) == false)); - is_zero_a = (normal_tri_v3(no_a, v2, v3, v4) <= FLT_EPSILON); - is_zero_b = (normal_tri_v3(no_b, v2, v4, v1) <= FLT_EPSILON); - - if (LIKELY(is_zero_a == false && is_zero_b == false)) { - add_v3_v3v3(no, no_a, no_b); - if (UNLIKELY(normalize_v3(no) <= FLT_EPSILON)) { - break; - } - } - else if (is_zero_a == false) { - copy_v3_v3(no, no_a); - } - else if (is_zero_b == false) { - copy_v3_v3(no, no_b); - } - else { - /* both zero area, no useful normal can be calculated */ + add_v3_v3v3(no, no_a, no_b); + if (UNLIKELY((no_scale = normalize_v3(no)) <= FLT_EPSILON)) { break; } - // { float a = angle_normalized_v3v3(no_a, no_b); printf("~ %.7f\n", a); fflush(stdout);} - axis_dominant_v3_to_m3(axis_mat, no); mul_v2_m3v3(v1_xy, axis_mat, v1); mul_v2_m3v3(v2_xy, axis_mat, v2); mul_v2_m3v3(v3_xy, axis_mat, v3); mul_v2_m3v3(v4_xy, axis_mat, v4); - } - - // printf("%p %p %p %p - %p %p\n", v1, v2, v3, v4, e->l->f, e->l->radial_next->f); - - if (is_zero_a == false && is_zero_b == false) { - /* both tri's are valid, check we make a concave quad */ - if (!is_quad_convex_v2(v1_xy, v2_xy, v3_xy, v4_xy)) { - break; - } - } - else { - /* one of the tri's was degenerate, chech we're not rotating - * into a different degenerate shape or flipping the face */ - float area_a, area_b; - area_a = area_tri_signed_v2(v1_xy, v2_xy, v3_xy); - area_b = area_tri_signed_v2(v1_xy, v3_xy, v4_xy); - - if ((fabsf(area_a) <= FLT_EPSILON) || (fabsf(area_b) <= FLT_EPSILON)) { - /* one of the new rotations is degenerate */ - break; - } - - if ((area_a >= 0.0f) != (area_b >= 0.0f)) { - /* rotation would cause flipping */ + /** + * Check if input faces are already flipped. + * Logic for 'signum_i' addition is: + * + * Accept: + * - (1, 1) or (-1, -1): same side (common case). + * - (-1/1, 0): one degenerate, OK since we may rotate into a valid state. + * + * Ignore: + * - (-1, 1): opposite winding, ignore. + * - ( 0, 0): both degenerate, ignore. + * + * \note The cross product is divided by 'no_scale' + * so the rotation calculation is scale independent. + */ + if (!(signum_i_ex(cross_tri_v2(v2_xy, v3_xy, v4_xy) / no_scale, eps) + + signum_i_ex(cross_tri_v2(v2_xy, v4_xy, v1_xy) / no_scale, eps))) + { break; } } - { - /* testing rule: the area divided by the perimeter, - * check if (1-3) beats the existing (2-4) edge rotation */ - float area_a, area_b; - float prim_a, prim_b; - float fac_24, fac_13; - - float len_12, len_23, len_34, len_41, len_24, len_13; - - /* edges around the quad */ - len_12 = len_v2v2(v1_xy, v2_xy); - len_23 = len_v2v2(v2_xy, v3_xy); - len_34 = len_v2v2(v3_xy, v4_xy); - len_41 = len_v2v2(v4_xy, v1_xy); - /* edges crossing the quad interior */ - len_13 = len_v2v2(v1_xy, v3_xy); - len_24 = len_v2v2(v2_xy, v4_xy); - - /* edge (2-4), current state */ - area_a = area_tri_v2(v2_xy, v3_xy, v4_xy); - area_b = area_tri_v2(v2_xy, v4_xy, v1_xy); - prim_a = len_23 + len_34 + len_24; - prim_b = len_24 + len_41 + len_12; - fac_24 = (area_a / prim_a) + (area_b / prim_b); - - /* edge (1-3), new state */ - area_a = area_tri_v2(v1_xy, v2_xy, v3_xy); - area_b = area_tri_v2(v1_xy, v3_xy, v4_xy); - prim_a = len_12 + len_23 + len_13; - prim_b = len_34 + len_41 + len_13; - fac_13 = (area_a / prim_a) + (area_b / prim_b); - - /* negative number if (1-3) is an improved state */ - return fac_24 - fac_13; - } + return BLI_polyfill_beautify_quad_rotate_calc(v1_xy, v2_xy, v3_xy, v4_xy); } while (false); return FLT_MAX; @@ -272,6 +216,12 @@ static float bm_edge_calc_rotate_beauty__angle( return FLT_MAX; } +/** + * Assuming we have 2 triangles sharing an edge (2 - 4), + * check if the edge running from (1 - 3) gives better results. + * + * \return (negative number means the edge can be rotated, lager == better). + */ float BM_verts_calc_rotate_beauty( const BMVert *v1, const BMVert *v2, const BMVert *v3, const BMVert *v4, const short flag, const short method) @@ -324,11 +274,12 @@ BLI_INLINE bool edge_in_array(const BMEdge *e, const BMEdge **edge_array, const } /* recalc an edge in the heap (surrounding geometry has changed) */ -static void bm_edge_update_beauty_cost_single(BMEdge *e, Heap *eheap, HeapNode **eheap_table, GSet **edge_state_arr, - /* only for testing the edge is in the array */ - const BMEdge **edge_array, const int edge_array_len, +static void bm_edge_update_beauty_cost_single( + BMEdge *e, Heap *eheap, HeapNode **eheap_table, GSet **edge_state_arr, + /* only for testing the edge is in the array */ + const BMEdge **edge_array, const int edge_array_len, - const short flag, const short method) + const short flag, const short method) { if (edge_in_array(e, edge_array, edge_array_len)) { const int i = BM_elem_index_get(e); @@ -366,10 +317,11 @@ static void bm_edge_update_beauty_cost_single(BMEdge *e, Heap *eheap, HeapNode * } /* we have rotated an edge, tag other edges and clear this one */ -static void bm_edge_update_beauty_cost(BMEdge *e, Heap *eheap, HeapNode **eheap_table, GSet **edge_state_arr, - const BMEdge **edge_array, const int edge_array_len, - /* only for testing the edge is in the array */ - const short flag, const short method) +static void bm_edge_update_beauty_cost( + BMEdge *e, Heap *eheap, HeapNode **eheap_table, GSet **edge_state_arr, + const BMEdge **edge_array, const int edge_array_len, + /* only for testing the edge is in the array */ + const short flag, const short method) { int i; @@ -383,7 +335,7 @@ static void bm_edge_update_beauty_cost(BMEdge *e, Heap *eheap, HeapNode **eheap_ BLI_assert(e->l->f->len == 3 && e->l->radial_next->f->len == 3); - BLI_assert(BM_edge_face_count(e) == 2); + BLI_assert(BM_edge_face_count_is_equal(e, 2)); for (i = 0; i < 4; i++) { bm_edge_update_beauty_cost_single( @@ -400,9 +352,10 @@ static void bm_edge_update_beauty_cost(BMEdge *e, Heap *eheap, HeapNode **eheap_ /** * \note This function sets the edge indices to invalid values. */ -void BM_mesh_beautify_fill(BMesh *bm, BMEdge **edge_array, const int edge_array_len, - const short flag, const short method, - const short oflag_edge, const short oflag_face) +void BM_mesh_beautify_fill( + BMesh *bm, BMEdge **edge_array, const int edge_array_len, + const short flag, const short method, + const short oflag_edge, const short oflag_face) { Heap *eheap; /* edge heap */ HeapNode **eheap_table; /* edge index aligned table pointing to the eheap */ @@ -438,11 +391,11 @@ void BM_mesh_beautify_fill(BMesh *bm, BMEdge **edge_array, const int edge_array_ i = BM_elem_index_get(e); eheap_table[i] = NULL; - BLI_assert(BM_edge_face_count(e) == 2); + BLI_assert(BM_edge_face_count_is_equal(e, 2)); e = BM_edge_rotate(bm, e, false, BM_EDGEROT_CHECK_EXISTS); - BLI_assert(e == NULL || BM_edge_face_count(e) == 2); + BLI_assert(e == NULL || BM_edge_face_count_is_equal(e, 2)); if (LIKELY(e)) { GSet *e_state_set = edge_state_arr[i]; diff --git a/source/blender/bmesh/tools/bmesh_beautify.h b/source/blender/bmesh/tools/bmesh_beautify.h index 7cc17008b50..0d6aa23b81d 100644 --- a/source/blender/bmesh/tools/bmesh_beautify.h +++ b/source/blender/bmesh/tools/bmesh_beautify.h @@ -31,12 +31,14 @@ enum { VERT_RESTRICT_TAG = (1 << 0), }; -void BM_mesh_beautify_fill(BMesh *bm, BMEdge **edge_array, const int edge_array_len, - const short flag, const short method, - const short oflag_edge, const short oflag_face); +void BM_mesh_beautify_fill( + BMesh *bm, BMEdge **edge_array, const int edge_array_len, + const short flag, const short method, + const short oflag_edge, const short oflag_face); -float BM_verts_calc_rotate_beauty(const BMVert *v1, const BMVert *v2, - const BMVert *v3, const BMVert *v4, - const short flag, const short method); +float BM_verts_calc_rotate_beauty( + const BMVert *v1, const BMVert *v2, + const BMVert *v3, const BMVert *v4, + const short flag, const short method); #endif /* __BMESH_BEAUTIFY_H__ */ diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 4a9fb677257..5a7788c0b62 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -53,6 +53,10 @@ #define BEVEL_EPSILON 1e-6f #define BEVEL_EPSILON_SQ 1e-12f #define BEVEL_EPSILON_BIG 1e-4f +#define BEVEL_EPSILON_BIG_SQ 1e-8f +#define BEVEL_EPSILON_ANG DEG2RADF(2.0f) +#define BEVEL_SMALL_ANG DEG2RADF(10.0f) +#define BEVEL_MAX_ADJUST_PCT 10.0f /* happens far too often, uncomment for development */ // #define BEVEL_ASSERT_PROJECT @@ -183,11 +187,11 @@ typedef struct BevelParams { float pro_super_r; /* superellipse parameter for edge profile */ bool vertex_only; /* bevel vertices only */ bool use_weights; /* bevel amount affected by weights on edges or verts */ - bool preserve_widths; /* should bevel prefer widths over angles, if forced to choose? */ + bool loop_slide; /* should bevel prefer to slide along edges rather than keep widths spec? */ bool limit_offset; /* should offsets be limited by collisions? */ const struct MDeformVert *dvert; /* vertex group array, maybe set if vertex_only */ int vertex_group; /* vertex group index, maybe set if vertex_only */ - int mat_nr; /* if >= 0, material number for bevel; else material comes from adjacent faces */ + int mat_nr; /* if >= 0, material number for bevel; else material comes from adjacent faces */ } BevelParams; // #pragma GCC diagnostic ignored "-Wpadded" @@ -244,8 +248,9 @@ static void create_mesh_bmvert(BMesh *bm, VMesh *vm, int i, int j, int k, BMVert BM_elem_flag_disable(nv->v, BM_ELEM_TAG); } -static void copy_mesh_vert(VMesh *vm, int ito, int jto, int kto, - int ifrom, int jfrom, int kfrom) +static void copy_mesh_vert( + VMesh *vm, int ito, int jto, int kto, + int ifrom, int jfrom, int kfrom) { NewVert *nvto, *nvfrom; @@ -341,32 +346,64 @@ static EdgeHalf *next_bev(BevVert *bv, EdgeHalf *from_e) } /* Return a good representative face (for materials, etc.) for faces - * created around/near BoundVert v */ -static BMFace *boundvert_rep_face(BoundVert *v) -{ - BLI_assert(v->efirst != NULL && v->elast != NULL); - if (v->efirst->fnext == v->elast->fprev) - return v->efirst->fnext; - else if (v->efirst->fnext) - return v->efirst->fnext; - else - return v->elast->fprev; + * created around/near BoundVert v. + * Sometimes care about a second choice, if there is one. + * If r_fother parameter is non-NULL and there is another, different, + * possible frep, return the other one in that parameter. */ +static BMFace *boundvert_rep_face(BoundVert *v, BMFace **r_fother) +{ + BMFace *frep, *frep2; + + frep2 = NULL; + if (v->ebev) { + frep = v->ebev->fprev; + if (v->efirst->fprev != frep) + frep2 = v->efirst->fprev; + } + else { + frep = v->efirst->fprev; + if (frep) { + if (v->elast->fnext != frep) + frep2 = v->elast->fnext; + else if (v->efirst->fnext != frep) + frep2 = v->efirst->fnext; + else if (v->elast->fprev != frep) + frep2 = v->efirst->fprev; + } + else if (v->efirst->fnext) { + frep = v->efirst->fnext; + if (v->elast->fnext != frep) + frep2 = v->elast->fnext; + } + else if (v->elast->fprev) { + frep = v->elast->fprev; + } + } + if (r_fother) + *r_fother = frep2; + return frep; } /** * Make ngon from verts alone. * Make sure to properly copy face attributes and do custom data interpolation from * corresponding elements of face_arr, if that is non-NULL, else from facerep. + * If edge_arr is non-NULL, then for interpolation purposes only, the corresponding + * elements of vert_arr are snapped to any non-NULL edges in that array. * If mat_nr >= 0 then the material of the face is set to that. * * \note ALL face creation goes through this function, this is important to keep! */ -static BMFace *bev_create_ngon(BMesh *bm, BMVert **vert_arr, const int totv, - BMFace **face_arr, BMFace *facerep, int mat_nr, bool do_interp) +static BMFace *bev_create_ngon( + BMesh *bm, BMVert **vert_arr, const int totv, + BMFace **face_arr, BMFace *facerep, BMEdge **edge_arr, + int mat_nr, bool do_interp) { BMIter iter; BMLoop *l; BMFace *f, *interp_f; + BMEdge *bme; + float save_co[3]; int i; f = BM_face_create_verts(bm, vert_arr, totv, facerep, BM_CREATE_NOP, true); @@ -384,8 +421,19 @@ static BMFace *bev_create_ngon(BMesh *bm, BMVert **vert_arr, const int totv, else { interp_f = facerep; } - if (interp_f) + if (interp_f) { + bme = NULL; + if (edge_arr) + bme = edge_arr[i]; + if (bme) { + copy_v3_v3(save_co, l->v->co); + closest_to_line_segment_v3(l->v->co, save_co, bme->v1->co, bme->v2->co); + } BM_loop_interp_from_face(bm, l, interp_f, true, true); + if (bme) { + copy_v3_v3(l->v->co, save_co); + } + } i++; } } @@ -402,25 +450,32 @@ static BMFace *bev_create_ngon(BMesh *bm, BMVert **vert_arr, const int totv, return f; } -static BMFace *bev_create_quad_tri(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4, - BMFace *facerep, int mat_nr, bool do_interp) +static BMFace *bev_create_quad( + BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4, + BMFace *f1, BMFace *f2, BMFace *f3, BMFace *f4, + int mat_nr) { BMVert *varr[4] = {v1, v2, v3, v4}; - return bev_create_ngon(bm, varr, v4 ? 4 : 3, NULL, facerep, mat_nr, do_interp); + BMFace *farr[4] = {f1, f2, f3, f4}; + return bev_create_ngon(bm, varr, 4, farr, f1, NULL, mat_nr, true); } -static BMFace *bev_create_quad_tri_ex(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4, - BMFace *f1, BMFace *f2, BMFace *f3, BMFace *f4, int mat_nr) +static BMFace *bev_create_quad_ex( + BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4, + BMFace *f1, BMFace *f2, BMFace *f3, BMFace *f4, + BMEdge *e1, BMEdge *e2, BMEdge *e3, BMEdge *e4, + int mat_nr) { BMVert *varr[4] = {v1, v2, v3, v4}; BMFace *farr[4] = {f1, f2, f3, f4}; - return bev_create_ngon(bm, varr, v4 ? 4 : 3, farr, f1, mat_nr, true); + BMEdge *earr[4] = {e1, e2, e3, e4}; + return bev_create_ngon(bm, varr, 4, farr, f1, earr, mat_nr, true); } - /* Is Loop layer layer_index contiguous across shared vertex of l1 and l2? */ -static bool contig_ldata_across_loops(BMesh *bm, BMLoop *l1, BMLoop *l2, - int layer_index) +static bool contig_ldata_across_loops( + BMesh *bm, BMLoop *l1, BMLoop *l2, + int layer_index) { const int offset = bm->ldata.layers[layer_index].offset; const int type = bm->ldata.layers[layer_index].type; @@ -478,71 +533,79 @@ static bool contig_ldata_across_edge(BMesh *bm, BMEdge *e, BMFace *f1, BMFace *f return true; } -/* Like bev_create_quad_tri, but when verts straddle an old edge. - * e - * | - * v1+---|---+v4 - * | | | - * | | | - * v2+---|---+v3 - * | - * f1 | f2 - * - * Most CustomData for loops can be interpolated in their respective - * faces' loops, but for UVs and other 'has_math_cd' layers, only - * do this if the UVs are continuous across the edge e, otherwise pick - * one side (f1, arbitrarily), and interpolate them all on that side. - * For face data, use f1 (arbitrarily) as face representative. */ -static BMFace *bev_create_quad_straddle(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4, - BMFace *f1, BMFace *f2, int mat_nr, bool is_seam) -{ - BMFace *f, *facerep; - BMLoop *l; +/* Merge (using average) all the UV values for loops of v's faces. + * Caller should ensure that no seams are violated by doing this. */ +static void bev_merge_uvs(BMesh *bm, BMVert *v) +{ BMIter iter; + MLoopUV *luv; + BMLoop *l; + float uv[2]; + int n; + int num_of_uv_layers = CustomData_number_of_layers(&bm->ldata, CD_MLOOPUV); + int i; - f = bev_create_quad_tri(bm, v1, v2, v3, v4, f1, mat_nr, false); + for (i = 0; i < num_of_uv_layers; i++) { + int cd_loop_uv_offset = CustomData_get_n_offset(&bm->ldata, CD_MLOOPUV, i); - if (!f) - return NULL; + if (cd_loop_uv_offset == -1) + return; - BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) { - if (is_seam || l->v == v1 || l->v == v2) - facerep = f1; - else - facerep = f2; - if (facerep) - BM_loop_interp_from_face(bm, l, facerep, true, true); + n = 0; + zero_v2(uv); + BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + add_v2_v2(uv, luv->uv); + n++; + } + if (n > 1) { + mul_v2_fl(uv, 1.0f / (float)n); + BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + copy_v2_v2(luv->uv, uv); + } + } } - return f; } -/* Merge (using average) all the UV values for loops of v's faces. - * Caller should ensure that no seams are violated by doing this. */ -static void bev_merge_uvs(BMesh *bm, BMVert *v) +/* Merge (using average) the UV values for two specific loops of v: those for faces containing v, + * and part of faces that share edge bme */ +static void bev_merge_edge_uvs(BMesh *bm, BMEdge *bme, BMVert *v) { BMIter iter; MLoopUV *luv; - BMLoop *l; + BMLoop *l, *l1, *l2; float uv[2]; - int n; - int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + int num_of_uv_layers = CustomData_number_of_layers(&bm->ldata, CD_MLOOPUV); + int i; - if (cd_loop_uv_offset == -1) + l1 = NULL; + l2 = NULL; + BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + if (l->e == bme) + l1 = l; + else if (l->prev->e == bme) + l2 = l; + } + if (l1 == NULL || l2 == NULL) return; - n = 0; - zero_v2(uv); - BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + for (i = 0; i < num_of_uv_layers; i++) { + int cd_loop_uv_offset = CustomData_get_n_offset(&bm->ldata, CD_MLOOPUV, i); + + if (cd_loop_uv_offset == -1) + return; + + zero_v2(uv); + luv = BM_ELEM_CD_GET_VOID_P(l1, cd_loop_uv_offset); add_v2_v2(uv, luv->uv); - n++; - } - if (n > 1) { - mul_v2_fl(uv, 1.0f / (float)n); - BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - copy_v2_v2(luv->uv, uv); - } + luv = BM_ELEM_CD_GET_VOID_P(l2, cd_loop_uv_offset); + add_v2_v2(uv, luv->uv); + mul_v2_fl(uv, 0.5f); + luv = BM_ELEM_CD_GET_VOID_P(l1, cd_loop_uv_offset); + copy_v2_v2(luv->uv, uv); + luv = BM_ELEM_CD_GET_VOID_P(l2, cd_loop_uv_offset); + copy_v2_v2(luv->uv, uv); } } @@ -577,10 +640,42 @@ static bool is_outside_edge(EdgeHalf *e, const float co[3], BMVert **ret_closer_ } } +/* co should be approximately on the plane between e1 and e2, which share common vert v + * and common face f (which cannot be NULL). + * Is it between those edges, sweeping CCW? */ +static bool point_between_edges(float co[3], BMVert *v, BMFace *f, EdgeHalf *e1, EdgeHalf *e2) +{ + BMVert *v1, *v2; + float dir1[3], dir2[3], dirco[3], no[3]; + float ang11, ang1co; + + v1 = BM_edge_other_vert(e1->e, v); + v2 = BM_edge_other_vert(e2->e, v); + sub_v3_v3v3(dir1, v->co, v1->co); + sub_v3_v3v3(dir2, v->co, v2->co); + sub_v3_v3v3(dirco, v->co, co); + normalize_v3(dir1); + normalize_v3(dir2); + normalize_v3(dirco); + ang11 = angle_normalized_v3v3(dir1, dir2); + ang1co = angle_normalized_v3v3(dir1, dirco); + /* angles are in [0,pi]. need to compare cross product with normal to see if they are reflex */ + cross_v3_v3v3(no, dir1, dir2); + if (dot_v3v3(no, f->no) < 0.0f) + ang11 = (float)(M_PI * 2.0) - ang11; + cross_v3_v3v3(no, dir1, dirco); + if (dot_v3v3(no, f->no) < 0.0f) + ang1co = (float)(M_PI * 2.0) - ang1co; + return (ang11 - ang1co > -BEVEL_EPSILON_ANG); +} + /* * Calculate the meeting point between the offset edges for e1 and e2, putting answer in meetco. * e1 and e2 share vertex v and face f (may be NULL) and viewed from the normal side of * the bevel vertex, e1 precedes e2 in CCW order. + * Except: if edges_between is true, there are edges between e1 and e2 in CCW order so they + * don't share a common face. We want the meeting point to be on an existing face so it + * should be dropped onto one of the intermediate faces, if possible. * Offset edge is on right of both edges, where e1 enters v and e2 leave it. * When offsets are equal, the new point is on the edge bisector, with length offset/sin(angle/2), * but if the offsets are not equal (allowing for this, as bevel modifier has edge weights that may @@ -589,18 +684,29 @@ static bool is_outside_edge(EdgeHalf *e, const float co[3], BMVert **ret_closer_ * record the change in offset_l (or offset_r); later we can tell that a change has happened because * the offset will differ from its original value in offset_l_spec (or offset_r_spec). */ -static void offset_meet(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f, float meetco[3]) +static void offset_meet(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f, bool edges_between, float meetco[3]) { - float dir1[3], dir2[3], norm_v[3], norm_perp1[3], norm_perp2[3], - off1a[3], off1b[3], off2a[3], off2b[3], isect2[3], ang, d; + float dir1[3], dir2[3], dir1n[3], dir2p[3], norm_v[3], norm_v1[3], norm_v2[3], + norm_perp1[3], norm_perp2[3], off1a[3], off1b[3], off2a[3], off2b[3], + isect2[3], dropco[3], plane[4], ang, d; BMVert *closer_v; + EdgeHalf *e, *e1next, *e2prev; + BMFace *ff; + int isect_kind; /* get direction vectors for two offset lines */ sub_v3_v3v3(dir1, v->co, BM_edge_other_vert(e1->e, v)->co); sub_v3_v3v3(dir2, BM_edge_other_vert(e2->e, v)->co, v->co); + if (edges_between) { + e1next = e1->next; + e2prev = e2->prev; + sub_v3_v3v3(dir1n, BM_edge_other_vert(e1next->e, v)->co, v->co); + sub_v3_v3v3(dir2p, v->co, BM_edge_other_vert(e2prev->e, v)->co); + } + ang = angle_v3v3(dir1, dir2); - if (ang < BEVEL_EPSILON_BIG) { + if (ang < BEVEL_EPSILON_ANG) { /* special case: e1 and e2 are parallel; put offset point perp to both, from v. * need to find a suitable plane. * if offsets are different, we're out of luck: @@ -621,7 +727,7 @@ static void offset_meet(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f, float e2->offset_l = d; copy_v3_v3(meetco, off1a); } - else if (fabsf(ang - (float)M_PI) < BEVEL_EPSILON_BIG) { + else if (fabsf(ang - (float)M_PI) < BEVEL_EPSILON_ANG) { /* special case e1 and e2 are antiparallel, so bevel is into * a zero-area face. Just make the offset point on the * common line, at offset distance from v. */ @@ -635,17 +741,43 @@ static void offset_meet(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f, float else { /* Get normal to plane where meet point should be, * using cross product instead of f->no in case f is non-planar. + * Except: sometimes locally there can be a small angle + * between dir1 and dir2 that leads to a normal that is actually almost + * perpendicular to the face normal; in this case it looks wrong to use + * the local (cross-product) normal, so use the face normal if the angle + * between dir1 and dir2 is smallish. * If e1-v-e2 is a reflex angle (viewed from vertex normal side), need to flip. * Use f->no to figure out which side to look at angle from, as even if * f is non-planar, will be more accurate than vertex normal */ - cross_v3_v3v3(norm_v, dir2, dir1); - normalize_v3(norm_v); - if (dot_v3v3(norm_v, f ? f->no : v->no) < 0.0f) - negate_v3(norm_v); + if (f && ang < BEVEL_SMALL_ANG) { + copy_v3_v3(norm_v1, f->no); + copy_v3_v3(norm_v2, f->no); + } + else if (!edges_between) { + cross_v3_v3v3(norm_v1, dir2, dir1); + normalize_v3(norm_v1); + if (dot_v3v3(norm_v1, f ? f->no : v->no) < 0.0f) + negate_v3(norm_v1); + copy_v3_v3(norm_v2, norm_v1); + } + else { + /* separate faces; get face norms at corners for each separately */ + cross_v3_v3v3(norm_v1, dir1n, dir1); + normalize_v3(norm_v1); + f = e1->fnext; + if (dot_v3v3(norm_v1, f ? f->no : v->no) < 0.0f) + negate_v3(norm_v1); + cross_v3_v3v3(norm_v2, dir2, dir2p); + normalize_v3(norm_v2); + f = e2->fprev; + if (dot_v3v3(norm_v2, f ? f->no : v->no) < 0.0f) + negate_v3(norm_v2); + } + /* get vectors perp to each edge, perp to norm_v, and pointing into face */ - cross_v3_v3v3(norm_perp1, dir1, norm_v); - cross_v3_v3v3(norm_perp2, dir2, norm_v); + cross_v3_v3v3(norm_perp1, dir1, norm_v1); + cross_v3_v3v3(norm_perp2, dir2, norm_v2); normalize_v3(norm_perp1); normalize_v3(norm_perp2); @@ -657,11 +789,10 @@ static void offset_meet(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f, float madd_v3_v3fl(off2a, norm_perp2, e2->offset_l); add_v3_v3v3(off2b, off2a, dir2); - /* intersect the lines; by construction they should be on the same plane and not parallel */ - if (!isect_line_line_v3(off1a, off1b, off2a, off2b, meetco, isect2)) { -#ifdef BEVEL_ASSERT_PROJECT - BLI_assert(!"offset_meet failure"); -#endif + /* intersect the lines */ + isect_kind = isect_line_line_v3(off1a, off1b, off2a, off2b, meetco, isect2); + if (isect_kind == 0) { + /* lines are collinear: we already tested for this, but this used a different epsilon */ copy_v3_v3(meetco, off1a); /* just to do something */ d = dist_to_line_v3(meetco, v->co, BM_edge_other_vert(e2->e, v)->co); if (fabsf(d - e2->offset_l) > BEVEL_EPSILON) @@ -681,15 +812,38 @@ static void offset_meet(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f, float copy_v3_v3(meetco, closer_v->co); e1->offset_r = len_v3v3(meetco, v->co); } + if (edges_between && e1->offset_r > 0.0f && e2->offset_l > 0.0f) { + /* Try to drop meetco to a face between e1 and e2 */ + if (isect_kind == 2) { + /* lines didn't meet in 3d: get average of meetco and isect2 */ + mid_v3_v3v3(meetco, meetco, isect2); + } + for (e = e1; e != e2; e = e->next) { + ff = e->fnext; + if (!ff) + continue; + plane_from_point_normal_v3(plane, v->co, ff->no); + closest_to_plane_normalized_v3(dropco, plane, meetco); + if (point_between_edges(dropco, v, ff, e, e->next)) { + copy_v3_v3(meetco, dropco); + break; + } + } + e1->offset_r = dist_to_line_v3(meetco, v->co, BM_edge_other_vert(e1->e, v)->co); + e2->offset_l = dist_to_line_v3(meetco, v->co, BM_edge_other_vert(e2->e, v)->co); + } } } } +/* chosen so that 1/sin(BEVEL_GOOD_ANGLE) is about 4, giving that expansion factor to bevel width */ +#define BEVEL_GOOD_ANGLE 0.25f + /* Calculate the meeting point between e1 and e2 (one of which should have zero offsets), * where e1 precedes e2 in CCW order around their common vertex v (viewed from normal side). * If r_angle is provided, return the angle between e and emeet in *r_angle. * If the angle is 0, or it is 180 degrees or larger, there will be no meeting point; - * return false in that case, else true */ + * return false in that case, else true. */ static bool offset_meet_edge(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, float meetco[3], float *r_angle) { float dir1[3], dir2[3], fno[3], ang, sinang; @@ -701,7 +855,7 @@ static bool offset_meet_edge(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, float meetc /* find angle from dir1 to dir2 as viewed from vertex normal side */ ang = angle_normalized_v3v3(dir1, dir2); - if (ang < BEVEL_EPSILON) { + if (fabsf(ang) < BEVEL_GOOD_ANGLE) { if (r_angle) *r_angle = 0.0f; return false; @@ -712,10 +866,11 @@ static bool offset_meet_edge(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, float meetc if (r_angle) *r_angle = ang; - if (ang - (float)M_PI > BEVEL_EPSILON) + if (fabsf(ang - (float)M_PI) < BEVEL_GOOD_ANGLE) return false; sinang = sinf(ang); + copy_v3_v3(meetco, v->co); if (e1->offset_r == 0.0f) madd_v3_v3fl(meetco, dir1, e2->offset_l / sinang); @@ -724,6 +879,17 @@ static bool offset_meet_edge(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, float meetc return true; } +/* Return true if it will look good to put the meeting point where offset_on_edge_between + * would put it. This means that neither side sees a reflex angle */ +static bool good_offset_on_edge_between(EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid, BMVert *v) +{ + float ang; + float meet[3]; + + return offset_meet_edge(e1, emid, v, meet, &ang) && + offset_meet_edge(emid, e2, v, meet, &ang); +} + /* Calculate the best place for a meeting point for the offsets from edges e1 and e2 * on the in-between edge emid. Viewed from the vertex normal side, the CCW * order of these edges is e1, emid, e2. @@ -732,8 +898,9 @@ static bool offset_meet_edge(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, float meetc * already, prefer to keep the offset the same on this end. * Otherwise, pick a point between the two intersection points on emid that minimizes * the sum of squares of errors from desired offset. */ -static void offset_on_edge_between(BevelParams *bp, EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid, - BMVert *v, float meetco[3]) +static void offset_on_edge_between( + BevelParams *bp, EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid, + BMVert *v, float meetco[3]) { float d, ang1, ang2, sina1, sina2, lambda; float meet1[3], meet2[3]; @@ -782,81 +949,6 @@ static void offset_on_edge_between(BevelParams *bp, EdgeHalf *e1, EdgeHalf *e2, e2->offset_l = d; } -/* Calculate the best place for a meeting point for the offsets from edges e1 and e2 - * when there is an in-between edge emid, and we prefer to have a point that may not - * be on emid if that does a better job of keeping offsets at the user spec. - * Viewed from the vertex normal side, the CCW order of the edges is e1, emid, e2. - * The offset lines may not meet exactly: the lines may be angled so that they can't meet. - * In that case, pick the the offset_on_edge_between. */ -static void offset_in_two_planes(BevelParams *bp, EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid, - BMVert *v, float meetco[3]) -{ - float dir1[3], dir2[3], dirmid[3], norm_perp1[3], norm_perp2[3], - off1a[3], off1b[3], off2a[3], off2b[3], isect2[3], - f1no[3], f2no[3], ang, d; - int iret; - - /* get direction vectors for two offset lines */ - sub_v3_v3v3(dir1, v->co, BM_edge_other_vert(e1->e, v)->co); - sub_v3_v3v3(dir2, BM_edge_other_vert(e2->e, v)->co, v->co); - sub_v3_v3v3(dirmid, BM_edge_other_vert(emid->e, v)->co, v->co); - - /* get directions into offset planes */ - /* calculate face normals at corner in case faces are nonplanar */ - cross_v3_v3v3(f1no, dirmid, dir1); - cross_v3_v3v3(f2no, dirmid, dir2); - - /* if e1-v-emid or emid-v-e2 are reflex angles, need to flip corner normals */ - if (dot_v3v3(f1no, v->no) < 0.0f) - negate_v3(f1no); - if (dot_v3v3(f2no, v->no) < 0.0f) - negate_v3(f2no); - - /* get vectors perpendicular to e1 and e2, pointing into the proper faces */ - cross_v3_v3v3(norm_perp1, dir1, f1no); - normalize_v3(norm_perp1); - cross_v3_v3v3(norm_perp2, dir2, f2no); - normalize_v3(norm_perp2); - - /* get points that are offset distances from each line, then another point on each line */ - copy_v3_v3(off1a, v->co); - madd_v3_v3fl(off1a, norm_perp1, e1->offset_r); - sub_v3_v3v3(off1b, off1a, dir1); - copy_v3_v3(off2a, v->co); - madd_v3_v3fl(off2a, norm_perp2, e2->offset_l); - add_v3_v3v3(off2b, off2a, dir2); - - ang = angle_v3v3(dir1, dir2); - if (ang < BEVEL_EPSILON_BIG) { - /* lines are parallel; put intersection on emid */ - offset_on_edge_between(bp, e1, e2, emid, v, meetco); - } - else if (fabsf(ang - (float)M_PI) < BEVEL_EPSILON_BIG) { - slide_dist(e2, v, e2->offset_l, meetco); - d = dist_to_line_v3(meetco, v->co, BM_edge_other_vert(e1->e, v)->co); - if (fabsf(d - e1->offset_r) > BEVEL_EPSILON) - e1->offset_r = d; - } - else { - iret = isect_line_line_v3(off1a, off1b, off2a, off2b, meetco, isect2); - if (iret == 0) { - /* lines colinear: another test says they are parallel. so shouldn't happen */ - copy_v3_v3(meetco, off1a); - d = dist_to_line_v3(meetco, v->co, BM_edge_other_vert(e2->e, v)->co); - if (fabsf(d - e2->offset_l) > BEVEL_EPSILON) - e2->offset_l = d; - } - else if (iret == 2) { - /* lines are not coplanar and don't meet; meetco and isect2 are nearest to first and second lines */ - if (len_squared_v3v3(meetco, isect2) > 100.0f * BEVEL_EPSILON_SQ) { - /* offset lines don't meet so can't preserve widths */ - offset_on_edge_between(bp, e1, e2, emid, v, meetco); - } - } - /* else iret == 1 and the lines are coplanar so meetco has the intersection */ - } -} - /* Offset by e->offset in plane with normal plane_no, on left if left==true, * else on right. If no is NULL, choose an arbitrary plane different * from eh's direction. */ @@ -904,11 +996,11 @@ static void project_to_edge(BMEdge *e, const float co_a[3], const float co_b[3], /* If there is a bndv->ebev edge, find the mid control point if necessary. * It is the closest point on the beveled edge to the line segment between * bndv and bndv->next. */ -static void set_profile_params(BevelParams *bp, BoundVert *bndv) +static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv) { EdgeHalf *e; Profile *pro; - float co1[3], co2[3], co3[3], d1[3], d2[3], d3[3], l; + float co1[3], co2[3], co3[3], d1[3], d2[3], l; bool do_linear_interp; copy_v3_v3(co1, bndv->nv.co); @@ -942,18 +1034,37 @@ static void set_profile_params(BevelParams *bp, BoundVert *bndv) cross_v3_v3v3(pro->plane_no, d1, d2); l = normalize_v3(pro->plane_no); if (l <= BEVEL_EPSILON_BIG) { - /* co1 - midco -co2 are collinear - project plane that contains that line - * and is perpendicular to the plane containing it and the beveled edge */ - cross_v3_v3v3(d3, d1, pro->proj_dir); - normalize_v3(d3); - cross_v3_v3v3(pro->plane_no, d1, d3); - l = normalize_v3(pro->plane_no); - if (l <= BEVEL_EPSILON_BIG) { - /* whole profile is collinear with edge: just interpolate */ + /* co1 - midco -co2 are collinear. + * Should be case that beveled edge is coplanar with two boundary verts. + * If the profile is going to lead into unbeveled edges on each side + * (that is, both BoundVerts are "on-edge" points on non-beveled edges) + * then in order to get curve in multi-segment case, change projection plane + * to be that common plane, projection dir to be the plane normal, + * and mid to be the original vertex. + * Otherwise, we just want to linearly interpolate between co1 and co2. + */ + if (e->prev->is_bev || e->next->is_bev) { do_linear_interp = true; } - /* signal to weld that this is linear */ - pro->super_r = PRO_LINE_R; + else { + copy_v3_v3(pro->coa, co1); + copy_v3_v3(pro->midco, bv->v->co); + copy_v3_v3(pro->cob, co2); + sub_v3_v3v3(d1, pro->midco, co1); + normalize_v3(d1); + sub_v3_v3v3(d2, pro->midco, co2); + normalize_v3(d2); + cross_v3_v3v3(pro->plane_no, d1, d2); + l = normalize_v3(pro->plane_no); + if (l <= BEVEL_EPSILON_BIG) { + /* whole profile is collinear with edge: just interpolate */ + do_linear_interp = true; + } + else { + copy_v3_v3(pro->plane_co, bv->v->co); + copy_v3_v3(pro->proj_dir, pro->plane_no); + } + } } copy_v3_v3(pro->plane_co, co1); } @@ -1055,14 +1166,15 @@ static int bev_ccw_test(BMEdge *a, BMEdge *b, BMFace *f) * and B has the right side as columns - both extended into homogeneous coords. * So M = B*(Ainverse). Doing Ainverse by hand gives the code below. */ -static bool make_unit_square_map(const float va[3], const float vmid[3], const float vb[3], - float r_mat[4][4]) +static bool make_unit_square_map( + const float va[3], const float vmid[3], const float vb[3], + float r_mat[4][4]) { float vo[3], vd[3], vb_vmid[3], va_vmid[3], vddir[3]; sub_v3_v3v3(va_vmid, vmid, va); sub_v3_v3v3(vb_vmid, vmid, vb); - if (fabsf(angle_v3v3(va_vmid, vb_vmid) - (float)M_PI) > 100.0f * BEVEL_EPSILON) { + if (fabsf(angle_v3v3(va_vmid, vb_vmid) - (float)M_PI) > BEVEL_EPSILON_ANG) { sub_v3_v3v3(vo, va, vb_vmid); cross_v3_v3v3(vddir, vb_vmid, va_vmid); normalize_v3(vddir); @@ -1104,8 +1216,9 @@ static bool make_unit_square_map(const float va[3], const float vmid[3], const f * and 1/2{va+vb+vc-vd} * and Blender matrices have cols at m[i][*]. */ -static void make_unit_cube_map(const float va[3], const float vb[3], const float vc[3], - const float vd[3], float r_mat[4][4]) +static void make_unit_cube_map( + const float va[3], const float vb[3], const float vc[3], + const float vd[3], float r_mat[4][4]) { copy_v3_v3(r_mat[0], va); sub_v3_v3(r_mat[0], vb); @@ -1386,56 +1499,99 @@ static void set_bound_vert_seams(BevVert *bv) } while ((v = v->next) != bv->vmesh->boundstart); } -/* Make a circular list of BoundVerts for bv, each of which has the coordinates - * of a vertex on the the boundary of the beveled vertex bv->v. - * This may adjust some EdgeHalf widths, and there might have to be - * a subsequent pass to make the widths as consistent as possible. - * The first time through, construct will be true and we are making the BoundVerts - * and setting up the BoundVert and EdgeHalf pointers appropriately. - * For a width consistency path, we just recalculate the coordinates of the - * BoundVerts. If the other ends have been (re)built already, then we - * copy the offsets from there to match, else we use the ideal (user-specified) - * widths. - * Also, if construct, decide on the mesh pattern that will be used inside the boundary. - * Doesn't make the actual BMVerts */ -static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) +static int count_bound_vert_seams(BevVert *bv) +{ + int ans, i; + + if (!bv->any_seam) + return 0; + + ans = 0; + for (i = 0; i < bv->edgecount; i++) + if (bv->edges[i].is_seam) + ans++; + return ans; +} + +/* Is e between two planes where angle between is 180? */ +static bool eh_on_plane(EdgeHalf *e) +{ + float dot; + + if (e->fprev && e->fnext) { + dot = dot_v3v3(e->fprev->no, e->fnext->no); + if (fabsf(dot) <= BEVEL_EPSILON_BIG || + fabsf(dot - 1.0f) <= BEVEL_EPSILON_BIG) + { + return true; + } + } + return false; +} + +/* Calculate the profiles for all the BoundVerts of VMesh vm */ +static void calculate_vm_profiles(BevelParams *bp, BevVert *bv, VMesh *vm) { - MemArena *mem_arena = bp->mem_arena; - EdgeHalf *efirst, *e, *eother; BoundVert *v; - BevVert *bvother; - VMesh *vm; + + v = vm->boundstart; + do { + set_profile_params(bp, bv, v); + calculate_profile(bp, v); + } while ((v = v->next) != vm->boundstart); +} + +/* Implements build_boundary for vertex-only case */ +static void build_boundary_vertex_only(BevelParams *bp, BevVert *bv, bool construct) +{ + VMesh *vm = bv->vmesh; + EdgeHalf *efirst, *e; + BoundVert *v; float co[3]; - const float *no; - float lastd; - vm = bv->vmesh; + BLI_assert(bp->vertex_only); - if (bp->vertex_only) { - e = efirst = &bv->edges[0]; - } - else { - e = efirst = next_bev(bv, NULL); - do { - eother = find_other_end_edge_half(bp, e, &bvother); - if (eother && bvother->visited && bp->offset_type != BEVEL_AMT_PERCENT) { - /* try to keep bevel even by matching other end offsets */ - e->offset_l = eother->offset_r; - e->offset_r = eother->offset_l; - } - else { - /* reset to user spec */ - e->offset_l = e->offset_l_spec; - e->offset_r = e->offset_r_spec; - } - } while ((e = e->next) != efirst); - e = efirst; + e = efirst = &bv->edges[0]; + do { + slide_dist(e, bv->v, e->offset_l, co); + if (construct) { + v = add_new_bound_vert(bp->mem_arena, vm, co); + v->efirst = v->elast = e; + e->leftv = v; + } + else { + adjust_bound_vert(e->leftv, co); + } + } while ((e = e->next) != efirst); + + calculate_vm_profiles(bp, bv, vm); + + if (construct) { + set_bound_vert_seams(bv); + if (vm->count == 2) + vm->mesh_kind = M_NONE; + else if (bp->seg == 1) + vm->mesh_kind = M_POLY; + else + vm->mesh_kind = M_ADJ; } +} - BLI_assert(bv->edgecount >= 2); /* since bevel edges incident to 2 faces */ +/* Special case of build_boundary when a single edge is beveled. + * The 'width adjust' part of build_boundary has been done already, and + * efirst is the first beveled edge at vertex bv. */ +static void build_boundary_terminal_edge(BevelParams *bp, BevVert *bv, EdgeHalf *efirst, bool construct) +{ + MemArena *mem_arena = bp->mem_arena; + VMesh *vm = bv->vmesh; + BoundVert *v; + EdgeHalf *e; + const float *no; + float co[3], d; - if (bv->edgecount == 2 && bv->selcount == 1) { - /* special case: beveled edge meets non-beveled one at valence 2 vert */ + e = efirst; + if (bv->edgecount == 2) { + /* only 2 edges in, so terminate the edge with an artificial vertex on the unbeveled edge */ no = e->fprev ? e->fprev->no : (e->fnext ? e->fnext->no : NULL); offset_in_plane(e, no, true, co); if (construct) { @@ -1469,116 +1625,52 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) else { adjust_bound_vert(e->next->leftv, co); } - set_profile_params(bp, vm->boundstart); - calculate_profile(bp, vm->boundstart); - return; } - - lastd = bp->vertex_only ? bv->offset : e->offset_l; - do { - if (e->is_bev) { - /* handle only left side of beveled edge e here: next iteration should do right side */ - if (e->prev->is_bev) { - BLI_assert(e->prev != e); /* see: wire edge special case */ - offset_meet(e->prev, e, bv->v, e->fprev, co); - if (construct) { - v = add_new_bound_vert(mem_arena, vm, co); - v->efirst = e->prev; - v->elast = v->ebev = e; - e->leftv = v; - e->prev->rightv = v; - } - else { - v = e->leftv; - adjust_bound_vert(v, co); - } - } - else { - /* e->prev is not beveled */ - if (e->prev->prev->is_bev) { - BLI_assert(e->prev->prev != e); /* see: edgecount 2, selcount 1 case */ - /* find meet point between e->prev->prev and e and attach e->prev there */ - if (bp->preserve_widths) - offset_in_two_planes(bp, e->prev->prev, e, e->prev, bv->v, co); - else - offset_on_edge_between(bp, e->prev->prev, e, e->prev, bv->v, co); - if (construct) { - v = add_new_bound_vert(mem_arena, vm, co); - v->efirst = e->prev->prev; - v->elast = v->ebev = e; - e->leftv = v; - e->prev->leftv = v; - e->prev->prev->rightv = v; - } - else { - v = e->leftv; - adjust_bound_vert(v, co); - } - } - else { - /* neither e->prev nor e->prev->prev are beveled: make on-edge on e->prev */ - offset_meet(e->prev, e, bv->v, e->fprev, co); - if (construct) { - v = add_new_bound_vert(mem_arena, vm, co); - v->efirst = e->prev; - v->elast = v->ebev = e; - e->leftv = v; - e->prev->leftv = v; - } - else { - v = e->leftv; - adjust_bound_vert(v, co); - } - } - } - lastd = len_v3v3(bv->v->co, v->nv.co); + else { + /* More than 2 edges in. Put on-edge verts on all the other edges + * and join with the beveled edge to make a poly or adj mesh, + * Because e->prev has offset 0, offset_meet will put co on that edge. */ + /* TODO: should do something else if angle between e and e->prev > 180 */ + offset_meet(e->prev, e, bv->v, e->fprev, false, co); + if (construct) { + v = add_new_bound_vert(mem_arena, vm, co); + v->efirst = e->prev; + v->elast = v->ebev = e; + e->leftv = v; + e->prev->leftv = v; } else { - /* e is not beveled */ - if (e->next->is_bev) { - /* next iteration will place e between beveled previous and next edges */ - /* do nothing... */ - } - else if (e->prev->is_bev) { - /* on-edge meet between e->prev and e */ - offset_meet(e->prev, e, bv->v, e->fprev, co); - if (construct) { - v = add_new_bound_vert(mem_arena, vm, co); - v->efirst = e->prev; - v->elast = e; - e->leftv = v; - e->prev->rightv = v; - } - else { - adjust_bound_vert(e->leftv, co); - } + adjust_bound_vert(e->leftv, co); + } + e = e->next; + offset_meet(e->prev, e, bv->v, e->fprev, false, co); + if (construct) { + v = add_new_bound_vert(mem_arena, vm, co); + v->efirst = e->prev; + v->elast = e; + e->leftv = v; + e->prev->rightv = v; + } + else { + adjust_bound_vert(e->leftv, co); + } + /* For the edges not adjacent to the beveled edge, slide the bevel amount along. */ + d = efirst->offset_l_spec; + for (e = e->next; e->next != efirst; e = e->next) { + slide_dist(e, bv->v, d, co); + if (construct) { + v = add_new_bound_vert(mem_arena, vm, co); + v->efirst = v->elast = e; + e->leftv = v; } else { - /* None of e->prev, e, e->next are beveled. - * could either leave alone or add slide points to make - * one polygon around bv->v. For now, we choose latter. - * Could slide to make an even bevel plane but for now will - * just use last distance a meet point moved from bv->v. */ - slide_dist(e, bv->v, lastd, co); - if (construct) { - v = add_new_bound_vert(mem_arena, vm, co); - v->efirst = v->elast = e; - e->leftv = v; - } - else { - adjust_bound_vert(e->leftv, co); - } + adjust_bound_vert(e->leftv, co); } } - } while ((e = e->next) != efirst); - - v = vm->boundstart; - do { - set_profile_params(bp, v); - calculate_profile(bp, v); - } while ((v = v->next) != vm->boundstart); + } + calculate_vm_profiles(bp, bv, vm); - if (bv->selcount == 1 && bv->edgecount >= 3) { + if (bv->edgecount >= 3) { /* special case: snap profile to plane of adjacent two edges */ v = vm->boundstart; BLI_assert(v->ebev != NULL); @@ -1589,30 +1681,169 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) if (construct) { set_bound_vert_seams(bv); - BLI_assert(vm->count >= 2); - if (bp->vertex_only) { - if (vm->count == 2) - vm->mesh_kind = M_NONE; - else if (bp->seg > 1) - vm->mesh_kind = M_ADJ; - else - vm->mesh_kind = M_POLY; - } - else if (vm->count == 2 && bv->edgecount == 3) { + if (vm->count == 2 && bv->edgecount == 3) { vm->mesh_kind = M_NONE; } - else if (bv->selcount == 2) { - vm->mesh_kind = M_QUAD_STRIP; + else if (vm->count == 3) { + vm->mesh_kind = M_TRI_FAN; + } + else { + vm->mesh_kind = M_POLY; } - else if (efirst->seg == 1 || bv->selcount == 1) { - if (vm->count == 3 && bv->selcount == 1) { - vm->mesh_kind = M_TRI_FAN; + } +} + +/* Return a value that is v if v is within BEVEL_MAX_ADJUST_PCT of the spec (assumed positive), + * else clamp to make it at most that far away from spec */ +static float clamp_adjust(float v, float spec) +{ + float allowed_delta = spec * (BEVEL_MAX_ADJUST_PCT / 100.0f); + + if (v - spec > allowed_delta) + return spec + allowed_delta; + else if (spec - v > allowed_delta) + return spec - allowed_delta; + else + return v; +} + +/* Make a circular list of BoundVerts for bv, each of which has the coordinates + * of a vertex on the boundary of the beveled vertex bv->v. + * This may adjust some EdgeHalf widths, and there might have to be + * a subsequent pass to make the widths as consistent as possible. + * The first time through, construct will be true and we are making the BoundVerts + * and setting up the BoundVert and EdgeHalf pointers appropriately. + * For a width consistency path, we just recalculate the coordinates of the + * BoundVerts. If the other ends have been (re)built already, then we + * copy the offsets from there to match, else we use the ideal (user-specified) + * widths. + * Also, if construct, decide on the mesh pattern that will be used inside the boundary. + * Doesn't make the actual BMVerts */ +static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) +{ + MemArena *mem_arena = bp->mem_arena; + EdgeHalf *efirst, *e, *e2, *e3, *enip, *eip, *eother; + BoundVert *v; + BevVert *bvother; + VMesh *vm; + float co[3]; + int nip, nnip; + + /* Current bevel does nothing if only one edge into a vertex */ + if (bv->edgecount <= 1) + return; + + if (bp->vertex_only) { + build_boundary_vertex_only(bp, bv, construct); + return; + } + + vm = bv->vmesh; + + /* Find a beveled edge to be efirst. Then for each edge, try matching widths to other end. */ + e = efirst = next_bev(bv, NULL); + BLI_assert(e->is_bev); + do { + eother = find_other_end_edge_half(bp, e, &bvother); + if (eother && bvother->visited && bp->offset_type != BEVEL_AMT_PERCENT) { + /* try to keep bevel even by matching other end offsets */ + /* sometimes, adjustment can accumulate errors so use the bp->limit_offset to + * let user limit the adjustment to within a reasonable range around spec */ + if (bp->limit_offset) { + e->offset_l = clamp_adjust(eother->offset_r, e->offset_l_spec); + e->offset_r = clamp_adjust(eother->offset_l, e->offset_r_spec); } else { - vm->mesh_kind = M_POLY; + e->offset_l = eother->offset_r; + e->offset_r = eother->offset_l; } } else { + /* reset to user spec */ + e->offset_l = e->offset_l_spec; + e->offset_r = e->offset_r_spec; + } + } while ((e = e->next) != efirst); + + if (bv->selcount == 1) { + /* special case: only one beveled edge in */ + build_boundary_terminal_edge(bp, bv, efirst, construct); + return; + } + + /* Here: there is more than one beveled edge. + * We make BoundVerts to connect the sides of the beveled edges. + * Non-beveled edges in between will just join to the appropriate juncture point. */ + e = efirst; + do { + BLI_assert(e->is_bev); + /* Make the BoundVert for the right side of e; other side will be made + * when the beveled edge to the left of e is handled. + * Analyze edges until next beveled edge. + * They are either "in plane" (preceding and subsequent faces are coplanar) + * or not. The "non-in-plane" edges effect silhouette and we prefer to slide + * along one of those if possible. */ + nip = nnip = 0; /* counts of in-plane / not-in-plane */ + enip = eip = NULL; /* representatives of each */ + for (e2 = e->next; !e2->is_bev; e2 = e2->next) { + if (eh_on_plane(e2)) { + nip++; + eip = e2; + } + else { + nnip++; + enip = e2; + } + } + if (nip == 0 && nnip == 0) { + offset_meet(e, e2, bv->v, e->fnext, false, co); + } + else if (nnip > 0) { + if (bp->loop_slide && nnip == 1 && good_offset_on_edge_between(e, e2, enip, bv->v)) { + offset_on_edge_between(bp, e, e2, enip, bv->v, co); + } + else { + offset_meet(e, e2, bv->v, NULL, true, co); + } + } + else { + /* nip > 0 and nnip == 0 */ + if (bp->loop_slide && nip == 1 && good_offset_on_edge_between(e, e2, eip, bv->v)) { + offset_on_edge_between(bp, e, e2, eip, bv->v, co); + } + else { + offset_meet(e, e2, bv->v, e->fnext, true, co); + } + } + if (construct) { + v = add_new_bound_vert(mem_arena, vm, co); + v->efirst = e; + v->elast = e2; + v->ebev = e2; + e->rightv = v; + e2->leftv = v; + for (e3 = e->next; e3 != e2; e3 = e3->next) { + e3->leftv = e3->rightv = v; + } + } + else { + adjust_bound_vert(e->rightv, co); + } + e = e2; + } while (e != efirst); + + calculate_vm_profiles(bp, bv, vm); + + if (construct) { + set_bound_vert_seams(bv); + + if (vm->count == 2) { + vm->mesh_kind = M_NONE; + } + else if (efirst->seg == 1) { + vm->mesh_kind = M_POLY; + } + else { vm->mesh_kind = M_ADJ; } } @@ -1723,7 +1954,7 @@ static BoundVert *pipe_test(BevVert *bv) sub_v3_v3v3(dir3, BM_edge_other_vert(v3->ebev->e, bv->v)->co, bv->v->co); normalize_v3(dir1); normalize_v3(dir3); - if (angle_normalized_v3v3(dir1, dir3) < BEVEL_EPSILON_BIG) { + if (angle_normalized_v3v3(dir1, dir3) < BEVEL_EPSILON_ANG) { epipe = v1->ebev; break; } @@ -1736,7 +1967,7 @@ static BoundVert *pipe_test(BevVert *bv) /* check face planes: all should have normals perpendicular to epipe */ for (e = &bv->edges[0]; e != &bv->edges[bv->edgecount]; e++) { if (e->fnext) { - if (dot_v3v3(dir1, e->fnext->no) > BEVEL_EPSILON) + if (dot_v3v3(dir1, e->fnext->no) > BEVEL_EPSILON_BIG) return NULL; } } @@ -1836,9 +2067,10 @@ static void vmesh_center(VMesh *vm, float r_cent[3]) } } -static void avg4(float co[3], - const NewVert *v0, const NewVert *v1, - const NewVert *v2, const NewVert *v3) +static void avg4( + float co[3], + const NewVert *v0, const NewVert *v1, + const NewVert *v2, const NewVert *v3) { add_v3_v3v3(co, v0->co, v1->co); add_v3_v3(co, v2->co); @@ -2229,7 +2461,6 @@ static VMesh *make_cube_corner_adj_vmesh(BevelParams *bp) BoundVert *bndv; int i, j, k, ns2; float co[3], coc[3]; - float w; if (r == PRO_SQUARE_R) return make_cube_corner_straight(mem_arena, nseg); @@ -2262,10 +2493,8 @@ static VMesh *make_cube_corner_adj_vmesh(BevelParams *bp) bndv = bndv->next; } /* center vertex */ - w = (float)(1.0 / M_SQRT3); - co[0] = w; - co[1] = w; - co[2] = w; + copy_v3_fl(co, M_SQRT1_3); + if (nseg > 2) { if (r > 1.5f) mul_v3_fl(co, 1.4f); @@ -2492,6 +2721,63 @@ static VMesh *pipe_adj_vmesh(BevelParams *bp, BevVert *bv, BoundVert *vpipe) return vm; } +static void get_incident_edges(BMFace *f, BMVert *v, BMEdge **r_e1, BMEdge **r_e2) +{ + BMIter iter; + BMEdge *e; + + *r_e1 = NULL; + *r_e2 = NULL; + if (!f) + return; + BM_ITER_ELEM (e, &iter, f, BM_EDGES_OF_FACE) { + if (e->v1 == v || e->v2 == v) { + if (*r_e1 == NULL) + *r_e1 = e; + else if (*r_e2 == NULL) + *r_e2 = e; + } + } +} + +static BMEdge *find_closer_edge(float *co, BMEdge *e1, BMEdge *e2) +{ + float dsq1, dsq2; + + BLI_assert(e1 != NULL && e2 != NULL); + dsq1 = dist_squared_to_line_segment_v3(co, e1->v1->co, e1->v2->co); + dsq2 = dist_squared_to_line_segment_v3(co, e2->v1->co, e2->v2->co); + if (dsq1 < dsq2) + return e1; + else + return e2; +} + +/* Snap co to the closest edge of face f. Return the edge in *r_snap_e, + * the coordinates of snap point in r_ snap_co, + * and the distance squared to the snap point as function return */ +static float snap_face_dist_squared(float *co, BMFace *f, BMEdge **r_snap_e, float *r_snap_co) +{ + BMIter iter; + BMEdge *beste = NULL; + float d2, beste_d2; + BMEdge *e; + float closest[3]; + + beste_d2 = 1e20; + BM_ITER_ELEM(e, &iter, f, BM_EDGES_OF_FACE) { + closest_to_line_segment_v3(closest, co, e->v1->co, e->v2->co); + d2 = len_squared_v3v3(closest, co); + if (d2 < beste_d2) { + beste_d2 = d2; + beste = e; + copy_v3_v3(r_snap_co, closest); + } + } + *r_snap_e = beste; + return beste_d2; +} + /* * Given that the boundary is built and the boundary BMVerts have been made, * calculate the positions of the interior mesh points for the M_ADJ pattern, @@ -2502,7 +2788,9 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv) VMesh *vm1, *vm; BoundVert *v; BMVert *bmv1, *bmv2, *bmv3, *bmv4; - BMFace *f, *f2, *f23; + BMFace *f, *f2; + BMEdge *bme, *bme1, *bme2, *bme3; + EdgeHalf *e; BoundVert *vpipe; int mat_nr = bp->mat_nr; @@ -2540,8 +2828,14 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv) v = vm->boundstart; do { i = v->index; - f = boundvert_rep_face(v); - f2 = boundvert_rep_face(v->next); + f = boundvert_rep_face(v, NULL); + f2 = boundvert_rep_face(v->next, NULL); + if (bp->vertex_only) + e = v->efirst; + else + e = v->ebev; + BLI_assert(e != NULL); + bme = e->e; /* For odd ns, make polys with lower left corner at (i,j,k) for * j in [0, ns2-1], k in [0, ns2]. And then the center ngon. * For even ns, @@ -2553,11 +2847,54 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv) bmv3 = mesh_vert(vm, i, j + 1, k + 1)->v; bmv4 = mesh_vert(vm, i, j + 1, k)->v; BLI_assert(bmv1 && bmv2 && bmv3 && bmv4); - f23 = f; - if (odd && k == ns2 && f2 && !v->any_seam) - f23 = f2; - bev_create_quad_tri_ex(bm, bmv1, bmv2, bmv3, bmv4, - f, f23, f23, f, mat_nr); + if (bp->vertex_only) { + if (j < k) { + if (k == ns2 && j == ns2 - 1) { + bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2, + NULL, NULL, v->next->efirst->e, bme, mat_nr); + } + else { + bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2, mat_nr); + } + } + else if (j > k) { + bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2, mat_nr); + } + else { /* j == k */ + /* only one edge attached to v, since vertex_only */ + if (e->is_seam) { + bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2, + bme, NULL, bme, NULL, mat_nr); + } + else { + bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f, + bme, NULL, bme, NULL, mat_nr); + } + } + } + else { /* edge bevel */ + if (odd) { + if (k == ns2) { + if (e->is_seam) { + bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f, + NULL, bme, bme, NULL, mat_nr); + } + else { + bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f, f2, f2, f, mat_nr); + } + } + else { + bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f, mat_nr); + } + } + else { + bme1 = k == ns2 - 1 ? bme : NULL; + bme3 = j == ns2 - 1 ? v->prev->ebev->e : NULL; + bme2 = bme1 != NULL ? bme1 : bme3; + bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f, + NULL, bme1, bme2, bme3, mat_nr); + } + } } } } while ((v = v->next) != vm->boundstart); @@ -2576,66 +2913,147 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv) } } } while ((v = v->next) != vm->boundstart); - if (!bv->any_seam) - bev_merge_uvs(bm, mesh_vert(vm, 0, ns2, ns2)->v); + bmv1 = mesh_vert(vm, 0, ns2, ns2)->v; + if (bp->vertex_only || count_bound_vert_seams(bv) <= 1) + bev_merge_uvs(bm, bmv1); } /* center ngon */ if (odd) { + BMFace *frep; + BMEdge *frep_e1, *frep_e2, *frep_e; BMVert **vv = NULL; BMFace **vf = NULL; + BMEdge **ve = NULL; BLI_array_staticdeclare(vv, BM_DEFAULT_NGON_STACK_SIZE); BLI_array_staticdeclare(vf, BM_DEFAULT_NGON_STACK_SIZE); + BLI_array_staticdeclare(ve, BM_DEFAULT_NGON_STACK_SIZE); + if (bv->any_seam) { + frep = boundvert_rep_face(vm->boundstart, NULL); + get_incident_edges(frep, bv->v, &frep_e1, &frep_e2); + } + else { + frep = NULL; + frep_e1 = frep_e2 = NULL; + } v = vm->boundstart; do { i = v->index; BLI_array_append(vv, mesh_vert(vm, i, ns2, ns2)->v); - BLI_array_append(vf, v->any_seam ? f : boundvert_rep_face(v)); + if (frep) { + BLI_array_append(vf, frep); + frep_e = find_closer_edge(mesh_vert(vm, i, ns2, ns2)->v->co, frep_e1, frep_e2); + BLI_array_append(ve, v == vm->boundstart ? NULL : frep_e); + } + else { + BLI_array_append(vf, boundvert_rep_face(v, NULL)); + BLI_array_append(ve, NULL); + } } while ((v = v->next) != vm->boundstart); - f = boundvert_rep_face(vm->boundstart); - bev_create_ngon(bm, vv, BLI_array_count(vv), vf, f, mat_nr, true); + bev_create_ngon(bm, vv, BLI_array_count(vv), vf, frep, ve, mat_nr, true); BLI_array_free(vv); + BLI_array_free(vf); + BLI_array_free(ve); + } +} + +/* If we make a poly out of verts around bv, snapping to rep frep, will uv poly have zero area? + * The uv poly is made by snapping all outside-of-frep vertices to the closest edge in frep. + * Assume that this funciton is called when the only inside-of-frep vertex is vm->boundstart. + * The poly will have zero area if the distance of that first vertex to some edge e is zero, and all + * the other vertices snap to e or snap to an edge at a point that is essentially on e too. */ +static bool is_bad_uv_poly(BevVert *bv, BMFace *frep) +{ + BoundVert *v; + BMEdge *snape, *firste; + float co[3]; + VMesh *vm = bv->vmesh; + float d2; + + v = vm->boundstart; + d2 = snap_face_dist_squared(v->nv.v->co, frep, &firste, co); + if (d2 > BEVEL_EPSILON_BIG_SQ || firste == NULL) + return false; + + for (v = v->next; v != vm->boundstart; v = v->next) { + snap_face_dist_squared(v->nv.v->co, frep, &snape, co); + if (snape != firste) { + d2 = dist_to_line_v3(co, firste->v1->co, firste->v2->co); + if (d2 > BEVEL_EPSILON_BIG_SQ) + return false; + } } + return true; } static BMFace *bevel_build_poly(BevelParams *bp, BMesh *bm, BevVert *bv) { - BMFace *f; + BMFace *f, *frep, *frep2; int n, k; VMesh *vm = bv->vmesh; BoundVert *v; - BMFace *frep; + BMEdge *frep_e1, *frep_e2, *frep_e; BMVert **vv = NULL; BMFace **vf = NULL; + BMEdge **ve = NULL; BLI_array_staticdeclare(vv, BM_DEFAULT_NGON_STACK_SIZE); BLI_array_staticdeclare(vf, BM_DEFAULT_NGON_STACK_SIZE); + BLI_array_staticdeclare(ve, BM_DEFAULT_NGON_STACK_SIZE); - frep = boundvert_rep_face(vm->boundstart); + if (bv->any_seam) { + frep = boundvert_rep_face(vm->boundstart, &frep2); + if (frep2 && frep && is_bad_uv_poly(bv, frep)) { + frep = frep2; + } + get_incident_edges(frep, bv->v, &frep_e1, &frep_e2); + } + else { + frep = NULL; + frep_e1 = frep_e2 = NULL; + } v = vm->boundstart; n = 0; do { /* accumulate vertices for vertex ngon */ /* also accumulate faces in which uv interpolation is to happen for each */ BLI_array_append(vv, v->nv.v); - BLI_array_append(vf, bv->any_seam ? frep : boundvert_rep_face(v)); + if (frep) { + BLI_array_append(vf, frep); + frep_e = find_closer_edge(v->nv.v->co, frep_e1, frep_e2); + BLI_array_append(ve, n > 0 ? frep_e : NULL); + } + else { + BLI_array_append(vf, boundvert_rep_face(v, NULL)); + BLI_array_append(ve, NULL); + } n++; if (v->ebev && v->ebev->seg > 1) { for (k = 1; k < v->ebev->seg; k++) { BLI_array_append(vv, mesh_vert(vm, v->index, 0, k)->v); - BLI_array_append(vf, bv->any_seam ? frep : boundvert_rep_face(v)); + if (frep) { + BLI_array_append(vf, frep); + frep_e = find_closer_edge(mesh_vert(vm, v->index, 0, k)->v->co, frep_e1, frep_e2); + BLI_array_append(ve, k < v->ebev->seg / 2 ? NULL : frep_e); + } + else { + BLI_array_append(vf, boundvert_rep_face(v, NULL)); + BLI_array_append(ve, NULL); + } n++; } } } while ((v = v->next) != vm->boundstart); if (n > 2) { - f = bev_create_ngon(bm, vv, n, vf, boundvert_rep_face(v), bp->mat_nr, true); + f = bev_create_ngon(bm, vv, n, vf, frep, ve, bp->mat_nr, true); } else { f = NULL; } BLI_array_free(vv); + BLI_array_free(vf); + BLI_array_free(ve); return f; } @@ -2712,23 +3130,60 @@ static void bevel_build_quadstrip(BevelParams *bp, BMesh *bm, BevVert *bv) } } -/* Special case: there is no vmesh pattern because this has only two boundary verts, - * and there are no faces in the original mesh at the original vertex. - * Since there will be no rebuilt face to make the edge between the boundary verts, +/* Special case: vertex bevel with only two boundary verts. + * Want to make a curved edge if seg > 0. + * If there are no faces in the original mesh at the original vertex, + * there will be no rebuilt face to make the edge between the boundary verts, * we have to make it here. */ -static void bevel_build_one_wire(BMesh *bm, BevVert *bv) +static void bevel_vert_two_edges(BevelParams *bp, BMesh *bm, BevVert *bv) { VMesh *vm = bv->vmesh; BMVert *v1, *v2; BMEdge *e_eg; + Profile *pro; + float co[3]; + BoundVert *bndv; + int ns, k; - BLI_assert(vm->count == 2); + BLI_assert(vm->count == 2 && bp->vertex_only); v1 = mesh_vert(vm, 0, 0, 0)->v; v2 = mesh_vert(vm, 1, 0, 0)->v; - e_eg = bv->edges[0].e; - BLI_assert(v1 != NULL && v2 != NULL && e_eg != NULL); - BM_edge_create(bm, v1, v2, e_eg, BM_CREATE_NO_DOUBLE); + + ns = vm->seg; + if (ns > 1) { + /* Set up profile parameters */ + bndv = vm->boundstart; + pro = &bndv->profile; + pro->super_r = bp->pro_super_r; + copy_v3_v3(pro->coa, v1->co); + copy_v3_v3(pro->cob, v2->co); + copy_v3_v3(pro->midco, bv->v->co); + /* don't use projection */ + zero_v3(pro->plane_co); + zero_v3(pro->plane_no); + zero_v3(pro->proj_dir); + calculate_profile(bp, bndv); + for (k = 1; k < ns; k++) { + get_profile_point(bp, pro, k, ns, co); + copy_v3_v3(mesh_vert(vm, 0, 0, k)->co, co); + create_mesh_bmvert(bm, vm, 0, 0, k, bv->v); + } + copy_v3_v3(mesh_vert(vm, 0, 0, ns)->co, v2->co); + for (k = 1; k < ns; k++) + copy_mesh_vert(vm, 1, 0, ns - k, 0, 0, k); + } + + if (BM_vert_face_check(bv->v) == false) { + e_eg = bv->edges[0].e; + BLI_assert(e_eg != NULL); + for (k = 0; k < ns; k++) { + v1 = mesh_vert(vm, 0, 0, k)->v; + v2 = mesh_vert(vm, 0, 0, k + 1)->v; + BLI_assert(v1 != NULL && v2 != NULL); + BM_edge_create(bm, v1, v2, e_eg, BM_CREATE_NO_DOUBLE); + } + } } /* Given that the boundary is built, now make the actual BMVerts @@ -2814,8 +3269,8 @@ static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv) switch (vm->mesh_kind) { case M_NONE: - if (n == 2 && BM_vert_face_count(bv->v) == 0) - bevel_build_one_wire(bm, bv); + if (n == 2 && bp->vertex_only) + bevel_vert_two_edges(bp, bm, bv); break; case M_POLY: bevel_build_poly(bp, bm, bv); @@ -2898,8 +3353,9 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v) nwire++; /* If edge beveling, exclude wire edges from edges array. * Mark this edge as "chosen" so loop below won't choose it. */ - if (!bp->vertex_only) + if (!bp->vertex_only) { BM_BEVEL_EDGE_TAG_ENABLE(bme); + } } } if (!first_bme) @@ -2935,6 +3391,10 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v) } bv->offset *= weight; } + else if (bp->use_weights) { + weight = BM_elem_float_data_get(&bm->vdata, v, CD_BWEIGHT); + bv->offset *= weight; + } } BLI_ghash_insert(bp->vert_hash, v, bv); @@ -3077,6 +3537,18 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v) e->offset_r_spec *= weight; } } + else if (bp->vertex_only) { + /* Weight has already been applied to bv->offset, if present. + * Transfer to e->offset_[lr]_spec and treat percent as special case */ + if (bp->offset_type == BEVEL_AMT_PERCENT) { + v2 = BM_edge_other_vert(e->e, bv->v); + e->offset_l_spec = BM_edge_calc_length(e->e) * bv->offset / 100.0f; + } + else { + e->offset_l_spec = bv->offset; + } + e->offset_r_spec = e->offset_l_spec; + } else { e->offset_l_spec = e->offset_r_spec = 0.0f; } @@ -3155,7 +3627,7 @@ static bool bev_rebuild_polygon(BMesh *bm, BevelParams *bp, BMFace *f) BLI_array_append(vv_fix, bmv); } } - else if (vm->mesh_kind == M_ADJ && vm->seg > 1 && !e->is_bev && !eprev->is_bev) { + else if ((vm->mesh_kind == M_ADJ || bp->vertex_only) && vm->seg > 1 && !e->is_bev && !eprev->is_bev) { BLI_assert(v->prev == vend); i = vend->index; for (k = vm->seg - 1; k > 0; k--) { @@ -3178,7 +3650,7 @@ static bool bev_rebuild_polygon(BMesh *bm, BevelParams *bp, BMFace *f) } if (do_rebuild) { n = BLI_array_count(vv); - f_new = bev_create_ngon(bm, vv, n, NULL, f, -1, true); + f_new = bev_create_ngon(bm, vv, n, NULL, f, NULL, -1, true); for (k = 0; k < BLI_array_count(vv_fix); k++) { bev_merge_uvs(bm, vv_fix[k]); @@ -3376,18 +3848,20 @@ static void weld_cross_attrs_copy(BMesh *bm, BevVert *bv, VMesh *vm, int vmindex } } - /* * Build the polygons along the selected Edge */ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme) { BevVert *bv1, *bv2; - BMVert *bmv1, *bmv2, *bmv3, *bmv4, *bmv1i, *bmv2i, *bmv3i, *bmv4i; + BMVert *bmv1, *bmv2, *bmv3, *bmv4; VMesh *vm1, *vm2; EdgeHalf *e1, *e2; - BMEdge *bme1, *bme2; + BMEdge *bme1, *bme2, *center_bme; BMFace *f1, *f2, *f; + BMVert *verts[4]; + BMFace *faces[4]; + BMEdge *edges[4]; int k, nseg, i1, i2, odd, mid; int mat_nr = bp->mat_nr; @@ -3404,11 +3878,15 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme) BLI_assert(e1 && e2); - /* v4 v3 - * \ / - * e->v1 - e->v2 - * / \ - * v1 v2 + /* + * bme->v1 + * / | \ + * v1--|--v4 + * | | | + * | | | + * v2--|--v3 + * \ | / + * bme->v2 */ nseg = e1->seg; BLI_assert(nseg > 0 && nseg == e2->seg); @@ -3422,42 +3900,67 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme) f1 = e1->fprev; f2 = e1->fnext; + faces[0] = faces[1] = f1; + faces[2] = faces[3] = f2; i1 = e1->leftv->index; i2 = e2->leftv->index; vm1 = bv1->vmesh; vm2 = bv2->vmesh; - if (nseg == 1) { - bev_create_quad_straddle(bm, bmv1, bmv2, bmv3, bmv4, f1, f2, mat_nr, e1->is_seam); - } - else { - bmv1i = bmv1; - bmv2i = bmv2; - odd = nseg % 2; - mid = nseg / 2; - for (k = 1; k <= nseg; k++) { - bmv4i = mesh_vert(vm1, i1, 0, k)->v; - bmv3i = mesh_vert(vm2, i2, 0, nseg - k)->v; - if (odd && k == mid + 1) { - bev_create_quad_straddle(bm, bmv1i, bmv2i, bmv3i, bmv4i, f1, f2, mat_nr, e1->is_seam); + verts[0] = bmv1; + verts[1] = bmv2; + odd = nseg % 2; + mid = nseg / 2; + center_bme = NULL; + for (k = 1; k <= nseg; k++) { + verts[3] = mesh_vert(vm1, i1, 0, k)->v; + verts[2] = mesh_vert(vm2, i2, 0, nseg - k)->v; + if (odd && k == mid + 1) { + if (e1->is_seam) { + /* straddles a seam: choose to interpolate in f1 and snap right edge to bme */ + edges[0] = edges[1] = NULL; + edges[2] = edges[3] = bme; + bev_create_ngon(bm, verts, 4, NULL, f1, edges, mat_nr, true); } else { - f = (k <= mid) ? f1 : f2; - bev_create_quad_tri(bm, bmv1i, bmv2i, bmv3i, bmv4i, f, mat_nr, true); + /* straddles but not a seam: interpolate left half in f1, right half in f2 */ + bev_create_ngon(bm, verts, 4, faces, NULL, NULL, mat_nr, true); } - bmv1i = bmv4i; - bmv2i = bmv3i; } - if (!odd && !e1->is_seam) { - bev_merge_uvs(bm, mesh_vert(vm1, i1, 0, mid)->v); - bev_merge_uvs(bm, mesh_vert(vm2, i2, 0, mid)->v); + else if (!odd && k == mid) { + /* left poly that touches an even center line on right */ + edges[0] = edges[1] = NULL; + edges[2] = edges[3] = bme; + bev_create_ngon(bm, verts, 4, NULL, f1, edges, mat_nr, true); + center_bme = BM_edge_exists(verts[2], verts[3]); + BLI_assert(center_bme != NULL); + } + else if (!odd && k == mid + 1) { + /* right poly that touches an even center line on left */ + edges[0] = edges[1] = bme; + edges[2] = edges[3] = NULL; + bev_create_ngon(bm, verts, 4, NULL, f2, edges, mat_nr, true); + } + else { + /* doesn't cross or touch the center line, so interpolate in appropriate f1 or f2 */ + f = (k <= mid) ? f1 : f2; + bev_create_ngon(bm, verts, 4, NULL, f, NULL, mat_nr, true); } + verts[0] = verts[3]; + verts[1] = verts[2]; + } + if (!odd) { + if (!e1->is_seam) + bev_merge_edge_uvs(bm, center_bme, mesh_vert(vm1, i1, 0, mid)->v); + if (!e2->is_seam) + bev_merge_edge_uvs(bm, center_bme, mesh_vert(vm2, i2, 0, mid)->v); } /* Fix UVs along end edge joints. A nop unless other side built already. */ - if (!e1->is_seam && bv1->vmesh->mesh_kind == M_NONE) + /* TODO: if some seam, may want to do selective merge */ + if (!bv1->any_seam && bv1->vmesh->mesh_kind == M_NONE) bev_merge_end_uvs(bm, bv1, e1); - if (!e2->is_seam && bv2->vmesh->mesh_kind == M_NONE) + if (!bv2->any_seam && bv2->vmesh->mesh_kind == M_NONE) bev_merge_end_uvs(bm, bv2, e2); /* Copy edge data to first and last edge */ @@ -3529,7 +4032,7 @@ static float find_superellipse_chord_u(float u0, float d2goal, float r) * Return the u's in *r_params, which should point to an array of size n+1. */ static void find_even_superellipse_params(int n, float r, float *r_params) { - float d2low, d2high, d2, d2final, u; + float d2low, d2high, d2 = 0.0f, d2final, u; int i, j, n2; const int maxiters = 40; const float d2tol = 1e-6f; @@ -3679,10 +4182,12 @@ static float bevel_limit_offset(BMesh *bm, BevelParams *bp) * * \warning all tagged edges _must_ be manifold. */ -void BM_mesh_bevel(BMesh *bm, const float offset, const int offset_type, - const float segments, const float profile, - const bool vertex_only, const bool use_weights, const bool limit_offset, - const struct MDeformVert *dvert, const int vertex_group, const int mat) +void BM_mesh_bevel( + BMesh *bm, const float offset, const int offset_type, + const float segments, const float profile, + const bool vertex_only, const bool use_weights, const bool limit_offset, + const struct MDeformVert *dvert, const int vertex_group, const int mat, + const bool loop_slide) { BMIter iter; BMVert *v, *v_next; @@ -3696,7 +4201,7 @@ void BM_mesh_bevel(BMesh *bm, const float offset, const int offset_type, bp.pro_super_r = 4.0f * profile; /* convert to superellipse exponent */ bp.vertex_only = vertex_only; bp.use_weights = use_weights; - bp.preserve_widths = false; + bp.loop_slide = loop_slide; bp.limit_offset = limit_offset; bp.dvert = dvert; bp.vertex_group = vertex_group; diff --git a/source/blender/bmesh/tools/bmesh_bevel.h b/source/blender/bmesh/tools/bmesh_bevel.h index 52d8faa5401..386dc8a1fce 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.h +++ b/source/blender/bmesh/tools/bmesh_bevel.h @@ -29,9 +29,10 @@ struct MDeformVert; -void BM_mesh_bevel(BMesh *bm, const float offset, const int offset_type, const float segments, - const float profile, const bool vertex_only, const bool use_weights, - const bool limit_offset, const struct MDeformVert *dvert, const int vertex_group, - const int mat); +void BM_mesh_bevel( + BMesh *bm, const float offset, const int offset_type, const float segments, + const float profile, const bool vertex_only, const bool use_weights, + const bool limit_offset, const struct MDeformVert *dvert, const int vertex_group, + const int mat, const bool loop_slide); #endif /* __BMESH_BEVEL_H__ */ diff --git a/source/blender/bmesh/tools/bmesh_bisect_plane.c b/source/blender/bmesh/tools/bmesh_bisect_plane.c index ae9b882cea0..fbcf573acd9 100644 --- a/source/blender/bmesh/tools/bmesh_bisect_plane.c +++ b/source/blender/bmesh/tools/bmesh_bisect_plane.c @@ -53,7 +53,7 @@ /* -------------------------------------------------------------------- */ /* Math utils */ -static int plane_point_test_v3(const float plane[4], const float co[3], const float eps, float *r_depth) +static short plane_point_test_v3(const float plane[4], const float co[3], const float eps, float *r_depth) { const float f = plane_point_side_v3(plane, co); *r_depth = f; @@ -69,7 +69,8 @@ static int plane_point_test_v3(const float plane[4], const float co[3], const fl * later we may want to move this into some hash lookup * to a separate struct, but for now we can store in BMesh data */ -#define BM_VERT_DIR(v) ((v)->head.index) /* Direction -1/0/1 */ +#define BM_VERT_DIR(v) ((short *)(&(v)->head.index))[0] /* Direction -1/0/1 */ +#define BM_VERT_SKIP(v) ((short *)(&(v)->head.index))[1] /* Skip Vert 0/1 */ #define BM_VERT_DIST(v) ((v)->no[0]) /* Distance from the plane. */ #define BM_VERT_SORTVAL(v) ((v)->no[1]) /* Temp value for sorting. */ #define BM_VERT_LOOPINDEX(v) /* The verts index within a face (temp var) */ \ @@ -105,18 +106,19 @@ static int bm_vert_sortval_cb(const void *v_a_v, const void *v_b_v) if (val_a > val_b) return 1; else if (val_a < val_b) return -1; - return 0; + else return 0; } static void bm_face_bisect_verts(BMesh *bm, BMFace *f, const float plane[4], const short oflag_center) { - /* unlikely more then 2 verts are needed */ + /* unlikely more than 2 verts are needed */ const unsigned int f_len_orig = (unsigned int)f->len; BMVert **vert_split_arr = BLI_array_alloca(vert_split_arr, f_len_orig); STACK_DECLARE(vert_split_arr); BMLoop *l_iter, *l_first; bool use_dirs[3] = {false, false, false}; + bool is_inside = false; STACK_INIT(vert_split_arr, f_len_orig); @@ -129,6 +131,11 @@ static void bm_face_bisect_verts(BMesh *bm, BMFace *f, const float plane[4], con do { if (vert_is_center_test(l_iter->v)) { BLI_assert(BM_VERT_DIR(l_iter->v) == 0); + + /* if both are -1 or 1, or both are zero: + * don't flip 'inside' var while walking */ + BM_VERT_SKIP(l_iter->v) = (((BM_VERT_DIR(l_iter->prev->v) ^ BM_VERT_DIR(l_iter->next->v))) == 0); + STACK_PUSH(vert_split_arr, l_iter->v); } use_dirs[BM_VERT_DIR(l_iter->v) + 1] = true; @@ -230,15 +237,12 @@ static void bm_face_bisect_verts(BMesh *bm, BMFace *f, const float plane[4], con for (i = 0; i < STACK_SIZE(vert_split_arr) - 1; i++) { BMVert *v_a = vert_split_arr[i]; BMVert *v_b = vert_split_arr[i + 1]; - float co_mid[2]; - /* geometric test before doing face lookups, - * find if the split spans a filled region of the polygon. */ - mid_v2_v2v2(co_mid, - face_verts_proj_2d[BM_VERT_LOOPINDEX(v_a)], - face_verts_proj_2d[BM_VERT_LOOPINDEX(v_b)]); + if (!BM_VERT_SKIP(v_a)) { + is_inside = !is_inside; + } - if (isect_point_poly_v2(co_mid, (const float (*)[2])face_verts_proj_2d, f_len_orig, false)) { + if (is_inside) { BMLoop *l_a, *l_b; bool found = false; unsigned int j; @@ -254,7 +258,8 @@ static void bm_face_bisect_verts(BMesh *bm, BMFace *f, const float plane[4], con } } - BLI_assert(found == true); + /* ideally wont happen, but it can for self intersecting faces */ + // BLI_assert(found == true); /* in fact this simple test is good enough, * test if the loops are adjacent */ @@ -289,9 +294,10 @@ finally: * \param use_tag Only bisect tagged edges and faces. * \param oflag_center Operator flag, enabled for geometry on the axis (existing and created) */ -void BM_mesh_bisect_plane(BMesh *bm, float plane[4], - const bool use_snap_center, const bool use_tag, - const short oflag_center, const float eps) +void BM_mesh_bisect_plane( + BMesh *bm, const float plane[4], + const bool use_snap_center, const bool use_tag, + const short oflag_center, const float eps) { unsigned int einput_len; unsigned int i; diff --git a/source/blender/bmesh/tools/bmesh_bisect_plane.h b/source/blender/bmesh/tools/bmesh_bisect_plane.h index 15f902642c8..7f3a97c4c79 100644 --- a/source/blender/bmesh/tools/bmesh_bisect_plane.h +++ b/source/blender/bmesh/tools/bmesh_bisect_plane.h @@ -27,8 +27,9 @@ * \ingroup bmesh */ -void BM_mesh_bisect_plane(BMesh *bm, float plane[4], - const bool use_snap_center, const bool use_tag, - const short oflag_center, const float eps); +void BM_mesh_bisect_plane( + BMesh *bm, const float plane[4], + const bool use_snap_center, const bool use_tag, + const short oflag_center, const float eps); #endif /* __BMESH_BISECT_PLANE_H__ */ diff --git a/source/blender/bmesh/tools/bmesh_decimate.h b/source/blender/bmesh/tools/bmesh_decimate.h index a1b26990587..6415da9a0c2 100644 --- a/source/blender/bmesh/tools/bmesh_decimate.h +++ b/source/blender/bmesh/tools/bmesh_decimate.h @@ -27,21 +27,22 @@ * \ingroup bmesh */ -void BM_mesh_decimate_collapse(BMesh *bm, const float factor, float *vweights, const bool do_triangulate); +void BM_mesh_decimate_collapse( + BMesh *bm, const float factor, + float *vweights, float vweight_factor, + const bool do_triangulate); void BM_mesh_decimate_unsubdivide_ex(BMesh *bm, const int iterations, const bool tag_only); void BM_mesh_decimate_unsubdivide(BMesh *bm, const int iterations); -void BM_mesh_decimate_dissolve_ex(BMesh *bm, const float angle_limit, const bool do_dissolve_boundaries, - const BMO_Delimit delimit, - BMVert **vinput_arr, const int vinput_len, - BMEdge **einput_arr, const int einput_len, - const short oflag_out); -void BM_mesh_decimate_dissolve(BMesh *bm, const float angle_limit, const bool do_dissolve_boundaries, - const BMO_Delimit delimit); - -/* these weights are accumulated so too high values may reach 'inf' too quickly */ -#define BM_MESH_DECIM_WEIGHT_MAX 100000.0f -#define BM_MESH_DECIM_WEIGHT_EPS (1.0f / BM_MESH_DECIM_WEIGHT_MAX) +void BM_mesh_decimate_dissolve_ex( + BMesh *bm, const float angle_limit, const bool do_dissolve_boundaries, + BMO_Delimit delimit, + BMVert **vinput_arr, const int vinput_len, + BMEdge **einput_arr, const int einput_len, + const short oflag_out); +void BM_mesh_decimate_dissolve( + BMesh *bm, const float angle_limit, const bool do_dissolve_boundaries, + const BMO_Delimit delimit); #endif /* __BMESH_DECIMATE_H__ */ diff --git a/source/blender/bmesh/tools/bmesh_decimate_collapse.c b/source/blender/bmesh/tools/bmesh_decimate_collapse.c index ef1783cc693..43a34331fc2 100644 --- a/source/blender/bmesh/tools/bmesh_decimate_collapse.c +++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.c @@ -47,6 +47,12 @@ #define USE_TRIANGULATE #define USE_VERT_NORMAL_INTERP /* has the advantage that flipped faces don't mess up vertex normals */ +/* if the cost from #BLI_quadric_evaluate is 'noise', fallback to topology */ +#define USE_TOPOLOGY_FALLBACK +#ifdef USE_TOPOLOGY_FALLBACK +# define TOPOLOGY_FALLBACK_EPS FLT_EPSILON +#endif + /* these checks are for rare cases that we can't avoid since they are valid meshes still */ #define USE_SAFETY_CHECKS @@ -77,12 +83,15 @@ static void bm_decim_build_quadrics(BMesh *bm, Quadric *vquadrics) BMLoop *l_first; BMLoop *l_iter; - const float *co = BM_FACE_FIRST_LOOP(f)->v->co; - const float *no = f->no; - const float offset = -dot_v3v3(no, co); + float center[3]; + double plane_db[4]; Quadric q; - BLI_quadric_from_v3_dist(&q, no, offset); + BM_face_calc_center_mean(f, center); + copy_v3db_v3fl(plane_db, f->no); + plane_db[3] = -dot_v3db_v3fl(plane_db, center); + + BLI_quadric_from_plane(&q, plane_db); l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { @@ -94,14 +103,22 @@ static void bm_decim_build_quadrics(BMesh *bm, Quadric *vquadrics) BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { if (UNLIKELY(BM_edge_is_boundary(e))) { float edge_vector[3]; - float edge_cross[3]; + float edge_plane[3]; + double edge_plane_db[4]; sub_v3_v3v3(edge_vector, e->v2->co, e->v1->co); f = e->l->f; - cross_v3_v3v3(edge_cross, edge_vector, f->no); - if (normalize_v3(edge_cross) > FLT_EPSILON) { + cross_v3_v3v3(edge_plane, edge_vector, f->no); + copy_v3db_v3fl(edge_plane_db, edge_plane); + + if (normalize_v3_d(edge_plane_db) > (double)FLT_EPSILON) { Quadric q; - BLI_quadric_from_v3_dist(&q, edge_cross, -dot_v3v3(edge_cross, e->v1->co)); + float center[3]; + + mid_v3_v3v3(center, e->v1->co, e->v2->co); + + edge_plane_db[3] = -dot_v3db_v3fl(edge_plane_db, center); + BLI_quadric_from_plane(&q, edge_plane_db); BLI_quadric_mul(&q, BOUNDARY_PRESERVE_WEIGHT); BLI_quadric_add_qu_qu(&vquadrics[BM_elem_index_get(e->v1)], &q); @@ -112,18 +129,19 @@ static void bm_decim_build_quadrics(BMesh *bm, Quadric *vquadrics) } -static void bm_decim_calc_target_co(BMEdge *e, float optimize_co[3], - const Quadric *vquadrics) +static void bm_decim_calc_target_co( + BMEdge *e, float optimize_co[3], + const Quadric *vquadrics) { /* compute an edge contraction target for edge 'e' * this is computed by summing it's vertices quadrics and * optimizing the result. */ Quadric q; - BLI_quadric_add_qu_ququ(&q, - &vquadrics[BM_elem_index_get(e->v1)], - &vquadrics[BM_elem_index_get(e->v2)]); - + BLI_quadric_add_qu_ququ( + &q, + &vquadrics[BM_elem_index_get(e->v1)], + &vquadrics[BM_elem_index_get(e->v2)]); if (BLI_quadric_optimize(&q, optimize_co, OPTIMIZE_EPS)) { return; /* all is good */ @@ -162,13 +180,15 @@ static bool bm_edge_collapse_is_degenerate_flip(BMEdge *e, const float optimize_ cross_v3_v3v3(cross_exist, vec_other, vec_exist); cross_v3_v3v3(cross_optim, vec_other, vec_optim); - /* normalize isn't really needed, but ensures the value at a unit we can compare against */ - normalize_v3(cross_exist); - normalize_v3(cross_optim); + /* avoid normalize */ + if (dot_v3v3(cross_exist, cross_optim) <= + (len_squared_v3(cross_exist) + len_squared_v3(cross_optim)) * 0.01f) + { + return true; + } #else normal_tri_v3(cross_exist, v->co, co_prev, co_next); normal_tri_v3(cross_optim, optimize_co, co_prev, co_next); -#endif /* use a small value rather then zero so we don't flip a face in multiple steps * (first making it zero area, then flipping again) */ @@ -176,6 +196,8 @@ static bool bm_edge_collapse_is_degenerate_flip(BMEdge *e, const float optimize_ //printf("no flip\n"); return true; } +#endif + } } } @@ -183,9 +205,29 @@ static bool bm_edge_collapse_is_degenerate_flip(BMEdge *e, const float optimize_ return false; } -static void bm_decim_build_edge_cost_single(BMEdge *e, - const Quadric *vquadrics, const float *vweights, - Heap *eheap, HeapNode **eheap_table) +#ifdef USE_TOPOLOGY_FALLBACK +/** + * when the cost is so small that its not useful (flat surfaces), + * fallback to using a 'topology' cost. + * + * This avoids cases where a flat (or near flat) areas get very un-even geometry. + */ +static float bm_decim_build_edge_cost_single_squared__topology(BMEdge *e) +{ + return fabsf(dot_v3v3(e->v1->no, e->v2->no)) / min_ff(-len_squared_v3v3(e->v1->co, e->v2->co), -FLT_EPSILON); +} +static float bm_decim_build_edge_cost_single__topology(BMEdge *e) +{ + return fabsf(dot_v3v3(e->v1->no, e->v2->no)) / min_ff(-len_v3v3(e->v1->co, e->v2->co), -FLT_EPSILON); +} + +#endif /* USE_TOPOLOGY_FALLBACK */ + +static void bm_decim_build_edge_cost_single( + BMEdge *e, + const Quadric *vquadrics, + const float *vweights, const float vweight_factor, + Heap *eheap, HeapNode **eheap_table) { const Quadric *q1, *q2; float optimize_co[3]; @@ -202,8 +244,7 @@ static void bm_decim_build_edge_cost_single(BMEdge *e, } else { /* only collapse tri's */ - eheap_table[BM_elem_index_get(e)] = NULL; - return; + goto clear; } } else if (BM_edge_is_manifold(e)) { @@ -212,23 +253,11 @@ static void bm_decim_build_edge_cost_single(BMEdge *e, } else { /* only collapse tri's */ - eheap_table[BM_elem_index_get(e)] = NULL; - return; + goto clear; } } else { - eheap_table[BM_elem_index_get(e)] = NULL; - return; - } - - if (vweights) { - if ((vweights[BM_elem_index_get(e->v1)] >= BM_MESH_DECIM_WEIGHT_MAX) && - (vweights[BM_elem_index_get(e->v2)] >= BM_MESH_DECIM_WEIGHT_MAX)) - { - /* skip collapsing this edge */ - eheap_table[BM_elem_index_get(e)] = NULL; - return; - } + goto clear; } /* end sanity check */ @@ -238,36 +267,71 @@ static void bm_decim_build_edge_cost_single(BMEdge *e, q1 = &vquadrics[BM_elem_index_get(e->v1)]; q2 = &vquadrics[BM_elem_index_get(e->v2)]; - if (vweights == NULL) { - cost = (BLI_quadric_evaluate(q1, optimize_co) + - BLI_quadric_evaluate(q2, optimize_co)); - } - else { - /* add 1.0 so planar edges are still weighted against */ - cost = (((BLI_quadric_evaluate(q1, optimize_co) + 1.0f) * vweights[BM_elem_index_get(e->v1)]) + - ((BLI_quadric_evaluate(q2, optimize_co) + 1.0f) * vweights[BM_elem_index_get(e->v2)])); - } - // print("COST %.12f\n"); + cost = (BLI_quadric_evaluate(q1, optimize_co) + + BLI_quadric_evaluate(q2, optimize_co)); + /* note, 'cost' shouldn't be negative but happens sometimes with small values. * this can cause faces that make up a flat surface to over-collapse, see [#37121] */ cost = fabsf(cost); + +#ifdef USE_TOPOLOGY_FALLBACK + if (UNLIKELY(cost < TOPOLOGY_FALLBACK_EPS)) { + /* subtract existing cost to further differentiate edges from one another + * + * keep topology cost below 0.0 so their values don't interfere with quadric cost, + * (and they get handled first). + * */ + if (vweights == NULL) { + cost = bm_decim_build_edge_cost_single_squared__topology(e) - cost; + } + else { + /* with weights we need to use the real length so we can scale them properly */ + const float e_weight = (vweights[BM_elem_index_get(e->v1)] + + vweights[BM_elem_index_get(e->v2)]); + cost = bm_decim_build_edge_cost_single__topology(e) - cost; + /* note, this is rather arbitrary max weight is 2 here, + * allow for skipping edges 4x the length, based on weights */ + if (e_weight) { + cost *= 1.0f + (e_weight * vweight_factor); + } + + BLI_assert(cost <= 0.0f); + } + } + else +#endif + if (vweights) { + const float e_weight = 2.0f - (vweights[BM_elem_index_get(e->v1)] + + vweights[BM_elem_index_get(e->v2)]); + if (e_weight) { + cost += (BM_edge_calc_length(e) * ((e_weight * vweight_factor))); + } + } + eheap_table[BM_elem_index_get(e)] = BLI_heap_insert(eheap, cost, e); + return; + +clear: + eheap_table[BM_elem_index_get(e)] = NULL; } /* use this for degenerate cases - add back to the heap with an invalid cost, * this way it may be calculated again if surrounding geometry changes */ -static void bm_decim_invalid_edge_cost_single(BMEdge *e, - Heap *eheap, HeapNode **eheap_table) +static void bm_decim_invalid_edge_cost_single( + BMEdge *e, + Heap *eheap, HeapNode **eheap_table) { BLI_assert(eheap_table[BM_elem_index_get(e)] == NULL); eheap_table[BM_elem_index_get(e)] = BLI_heap_insert(eheap, COST_INVALID, e); } -static void bm_decim_build_edge_cost(BMesh *bm, - const Quadric *vquadrics, const float *vweights, - Heap *eheap, HeapNode **eheap_table) +static void bm_decim_build_edge_cost( + BMesh *bm, + const Quadric *vquadrics, + const float *vweights, const float vweight_factor, + Heap *eheap, HeapNode **eheap_table) { BMIter iter; BMEdge *e; @@ -275,7 +339,7 @@ static void bm_decim_build_edge_cost(BMesh *bm, BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) { eheap_table[i] = NULL; /* keep sanity check happy */ - bm_decim_build_edge_cost_single(e, vquadrics, vweights, eheap, eheap_table); + bm_decim_build_edge_cost_single(e, vquadrics, vweights, vweight_factor, eheap, eheap_table); } } @@ -440,10 +504,11 @@ static void bm_decim_triangulate_end(BMesh *bm) #ifdef USE_CUSTOMDATA /** - * \param v is the target to merge into. + * \param l: defines the vert to collapse into. */ -static void bm_edge_collapse_loop_customdata(BMesh *bm, BMLoop *l, BMVert *v_clear, BMVert *v_other, - const float customdata_fac) +static void bm_edge_collapse_loop_customdata( + BMesh *bm, BMLoop *l, BMVert *v_clear, BMVert *v_other, + const float customdata_fac) { /* disable seam check - the seam check would have to be done per layer, its not really that important */ //#define USE_SEAM @@ -452,8 +517,6 @@ static void bm_edge_collapse_loop_customdata(BMesh *bm, BMLoop *l, BMVert *v_cle const bool is_manifold = BM_edge_is_manifold(l->e); int side; - /* l defines the vert to collapse into */ - /* first find the loop of 'v_other' thats attached to the face of 'l' */ if (l->v == v_clear) { l_clear = l; @@ -519,13 +582,17 @@ static void bm_edge_collapse_loop_customdata(BMesh *bm, BMLoop *l, BMVert *v_cle if (CustomData_layer_has_math(&bm->ldata, i)) { const int offset = bm->ldata.layers[i].offset; const int type = bm->ldata.layers[i].type; - void *cd_src[2] = {(char *)src[0] + offset, - (char *)src[1] + offset}; - void *cd_iter = (char *)l_iter->head.data + offset; + const void *cd_src[2] = { + POINTER_OFFSET(src[0], offset), + POINTER_OFFSET(src[1], offset), + }; + void *cd_iter = POINTER_OFFSET(l_iter->head.data, offset); /* detect seams */ if (CustomData_data_equals(type, cd_src[0], cd_iter)) { - CustomData_bmesh_interp_n(&bm->ldata, cd_src, w, NULL, 2, l_iter->head.data, i); + CustomData_bmesh_interp_n( + &bm->ldata, cd_src, w, NULL, ARRAY_SIZE(cd_src), + POINTER_OFFSET(l_iter->head.data, offset), i); #ifdef USE_SEAM is_seam = false; #endif @@ -691,18 +758,19 @@ static bool bm_edge_collapse_is_degenerate_topology(BMEdge *e_first) * * Important - dont add vert/edge/face data on collapsing! * - * \param e_clear_other let caller know what edges we remove besides \a e_clear - * \param customdata_flag merge factor, scales from 0 - 1 ('v_clear' -> 'v_other') + * \param r_e_clear_other: Let caller know what edges we remove besides \a e_clear + * \param customdata_flag: Merge factor, scales from 0 - 1 ('v_clear' -> 'v_other') */ -static bool bm_edge_collapse(BMesh *bm, BMEdge *e_clear, BMVert *v_clear, int r_e_clear_other[2], +static bool bm_edge_collapse( + BMesh *bm, BMEdge *e_clear, BMVert *v_clear, int r_e_clear_other[2], #ifdef USE_CUSTOMDATA - const CD_UseFlag customdata_flag, - const float customdata_fac + const CD_UseFlag customdata_flag, + const float customdata_fac #else - const CD_UseFlag UNUSED(customdata_flag), - const float UNUSED(customdata_fac) + const CD_UseFlag UNUSED(customdata_flag), + const float UNUSED(customdata_fac) #endif - ) + ) { BMVert *v_other; @@ -719,6 +787,7 @@ static bool bm_edge_collapse(BMesh *bm, BMEdge *e_clear, BMVert *v_clear, int r_ BLI_assert(ok == true); BLI_assert(l_a->f->len == 3); BLI_assert(l_b->f->len == 3); + UNUSED_VARS_NDEBUG(ok); /* keep 'v_clear' 0th */ if (BM_vert_in_edge(l_a->prev->e, v_clear)) { @@ -777,12 +846,12 @@ static bool bm_edge_collapse(BMesh *bm, BMEdge *e_clear, BMVert *v_clear, int r_ BM_edge_kill(bm, e_clear); v_other->head.hflag |= v_clear->head.hflag; - BM_vert_splice(bm, v_clear, v_other); + BM_vert_splice(bm, v_other, v_clear); e_a_other[1]->head.hflag |= e_a_other[0]->head.hflag; e_b_other[1]->head.hflag |= e_b_other[0]->head.hflag; - BM_edge_splice(bm, e_a_other[0], e_a_other[1]); - BM_edge_splice(bm, e_b_other[0], e_b_other[1]); + BM_edge_splice(bm, e_a_other[1], e_a_other[0]); + BM_edge_splice(bm, e_b_other[1], e_b_other[0]); // BM_mesh_validate(bm); @@ -826,10 +895,10 @@ static bool bm_edge_collapse(BMesh *bm, BMEdge *e_clear, BMVert *v_clear, int r_ BM_edge_kill(bm, e_clear); v_other->head.hflag |= v_clear->head.hflag; - BM_vert_splice(bm, v_clear, v_other); + BM_vert_splice(bm, v_other, v_clear); e_a_other[1]->head.hflag |= e_a_other[0]->head.hflag; - BM_edge_splice(bm, e_a_other[0], e_a_other[1]); + BM_edge_splice(bm, e_a_other[1], e_a_other[0]); // BM_mesh_validate(bm); @@ -842,14 +911,17 @@ static bool bm_edge_collapse(BMesh *bm, BMEdge *e_clear, BMVert *v_clear, int r_ /* collapse e the edge, removing e->v2 */ -static void bm_decim_edge_collapse(BMesh *bm, BMEdge *e, - Quadric *vquadrics, float *vweights, - Heap *eheap, HeapNode **eheap_table, - const CD_UseFlag customdata_flag) +static void bm_decim_edge_collapse( + BMesh *bm, BMEdge *e, + Quadric *vquadrics, + float *vweights, const float vweight_factor, + Heap *eheap, HeapNode **eheap_table, + const CD_UseFlag customdata_flag) { int e_clear_other[2]; BMVert *v_other = e->v1; - int v_clear_index = BM_elem_index_get(e->v2); /* the vert is removed so only store the index */ + const int v_other_index = BM_elem_index_get(e->v1); + const int v_clear_index = BM_elem_index_get(e->v2); /* the vert is removed so only store the index */ float optimize_co[3]; float customdata_fac; @@ -892,7 +964,9 @@ static void bm_decim_edge_collapse(BMesh *bm, BMEdge *e, int i; if (vweights) { - vweights[BM_elem_index_get(v_other)] += vweights[v_clear_index]; + float v_other_weight = interpf(vweights[v_other_index], vweights[v_clear_index], customdata_fac); + CLAMP(v_other_weight, 0.0f, 1.0f); + vweights[v_other_index] = v_other_weight; } e = NULL; /* paranoid safety check */ @@ -909,7 +983,7 @@ static void bm_decim_edge_collapse(BMesh *bm, BMEdge *e, } /* update vertex quadric, add kept vertex from killed vertex */ - BLI_quadric_add_qu_qu(&vquadrics[BM_elem_index_get(v_other)], &vquadrics[v_clear_index]); + BLI_quadric_add_qu_qu(&vquadrics[v_other_index], &vquadrics[v_clear_index]); /* update connected normals */ @@ -930,7 +1004,7 @@ static void bm_decim_edge_collapse(BMesh *bm, BMEdge *e, e_iter = e_first = v_other->e; do { BLI_assert(BM_edge_find_double(e_iter) == NULL); - bm_decim_build_edge_cost_single(e_iter, vquadrics, vweights, eheap, eheap_table); + bm_decim_build_edge_cost_single(e_iter, vquadrics, vweights, vweight_factor, eheap, eheap_table); } while ((e_iter = bmesh_disk_edge_next(e_iter, v_other)) != e_first); } @@ -952,7 +1026,7 @@ static void bm_decim_edge_collapse(BMesh *bm, BMEdge *e, BLI_assert(BM_vert_in_edge(e_outer, l->v) == false); - bm_decim_build_edge_cost_single(e_outer, vquadrics, vweights, eheap, eheap_table); + bm_decim_build_edge_cost_single(e_outer, vquadrics, vweights, vweight_factor, eheap, eheap_table); } } } @@ -976,7 +1050,11 @@ static void bm_decim_edge_collapse(BMesh *bm, BMEdge *e, * \param vweights Optional array of vertex aligned weights [0 - 1], * a vertex group is the usual source for this. */ -void BM_mesh_decimate_collapse(BMesh *bm, const float factor, float *vweights, const bool do_triangulate) +void BM_mesh_decimate_collapse( + BMesh *bm, + const float factor, + float *vweights, float vweight_factor, + const bool do_triangulate) { Heap *eheap; /* edge heap */ HeapNode **eheap_table; /* edge index aligned table pointing to the eheap */ @@ -1004,7 +1082,7 @@ void BM_mesh_decimate_collapse(BMesh *bm, const float factor, float *vweights, c /* build initial edge collapse cost data */ bm_decim_build_quadrics(bm, vquadrics); - bm_decim_build_edge_cost(bm, vquadrics, vweights, eheap, eheap_table); + bm_decim_build_edge_cost(bm, vquadrics, vweights, vweight_factor, eheap, eheap_table); face_tot_target = bm->totface * factor; bm->elem_index_dirty |= BM_ALL; @@ -1026,13 +1104,11 @@ void BM_mesh_decimate_collapse(BMesh *bm, const float factor, float *vweights, c BMEdge *e = BLI_heap_popmin(eheap); BLI_assert(BM_elem_index_get(e) < tot_edge_orig); /* handy to detect corruptions elsewhere */ - // printf("COST %.10f\n", value); - /* under normal conditions wont be accessed again, * but NULL just incase so we don't use freed node */ eheap_table[BM_elem_index_get(e)] = NULL; - bm_decim_edge_collapse(bm, e, vquadrics, vweights, eheap, eheap_table, customdata_flag); + bm_decim_edge_collapse(bm, e, vquadrics, vweights, vweight_factor, eheap, eheap_table, customdata_flag); } diff --git a/source/blender/bmesh/tools/bmesh_decimate_dissolve.c b/source/blender/bmesh/tools/bmesh_decimate_dissolve.c index 096349e8e9c..a1460cec7d1 100644 --- a/source/blender/bmesh/tools/bmesh_decimate_dissolve.c +++ b/source/blender/bmesh/tools/bmesh_decimate_dissolve.c @@ -32,6 +32,8 @@ #include "BLI_math.h" #include "BLI_heap.h" +#include "BKE_customdata.h" + #include "bmesh.h" #include "bmesh_decimate.h" /* own include */ @@ -59,7 +61,32 @@ static float bm_vert_edge_face_angle(BMVert *v) #undef ANGLE_TO_UNIT } -static float bm_edge_calc_dissolve_error(const BMEdge *e, const BMO_Delimit delimit) +struct DelimitData { + int cd_loop_type; + int cd_loop_size; + int cd_loop_offset; + int cd_loop_offset_end; +}; + +static bool bm_edge_is_contiguous_loop_cd_all( + const BMEdge *e, const struct DelimitData *delimit_data) +{ + int cd_loop_offset; + for (cd_loop_offset = delimit_data->cd_loop_offset; + cd_loop_offset < delimit_data->cd_loop_offset_end; + cd_loop_offset += delimit_data->cd_loop_size) + { + if (BM_edge_is_contiguous_loop_cd(e, delimit_data->cd_loop_type, cd_loop_offset) == false) { + return false; + } + } + + return true; +} + +static float bm_edge_calc_dissolve_error( + const BMEdge *e, const BMO_Delimit delimit, + const struct DelimitData *delimit_data) { const bool is_contig = BM_edge_is_contiguous(e); float angle; @@ -74,6 +101,12 @@ static float bm_edge_calc_dissolve_error(const BMEdge *e, const BMO_Delimit deli goto fail; } + if ((delimit & BMO_DELIM_SHARP) && + (BM_elem_flag_test(e, BM_ELEM_SMOOTH) == 0)) + { + goto fail; + } + if ((delimit & BMO_DELIM_MATERIAL) && (e->l->f->mat_nr != e->l->radial_next->f->mat_nr)) { @@ -86,6 +119,12 @@ static float bm_edge_calc_dissolve_error(const BMEdge *e, const BMO_Delimit deli goto fail; } + if ((delimit & BMO_DELIM_UV) && + (bm_edge_is_contiguous_loop_cd_all(e, delimit_data) == 0)) + { + goto fail; + } + angle = BM_edge_calc_face_angle(e); if (is_contig == false) { angle = (float)M_PI - angle; @@ -98,17 +137,32 @@ fail: } -void BM_mesh_decimate_dissolve_ex(BMesh *bm, const float angle_limit, const bool do_dissolve_boundaries, - const BMO_Delimit delimit, - BMVert **vinput_arr, const int vinput_len, - BMEdge **einput_arr, const int einput_len, - const short oflag_out) +void BM_mesh_decimate_dissolve_ex( + BMesh *bm, const float angle_limit, const bool do_dissolve_boundaries, + BMO_Delimit delimit, + BMVert **vinput_arr, const int vinput_len, + BMEdge **einput_arr, const int einput_len, + const short oflag_out) { + struct DelimitData delimit_data = {0}; const int eheap_table_len = do_dissolve_boundaries ? einput_len : max_ii(einput_len, vinput_len); void *_heap_table = MEM_mallocN(sizeof(HeapNode *) * eheap_table_len, __func__); int i; + if (delimit & BMO_DELIM_UV) { + const int layer_len = CustomData_number_of_layers(&bm->ldata, CD_MLOOPUV); + if (layer_len == 0) { + delimit &= ~BMO_DELIM_UV; + } + else { + delimit_data.cd_loop_type = CD_MLOOPUV; + delimit_data.cd_loop_size = CustomData_sizeof(delimit_data.cd_loop_type); + delimit_data.cd_loop_offset = CustomData_get_n_offset(&bm->ldata, CD_MLOOPUV, 0); + delimit_data.cd_loop_offset_end = delimit_data.cd_loop_size * layer_len; + } + } + /* --- first edges --- */ if (1) { BMEdge **earray; @@ -133,7 +187,7 @@ void BM_mesh_decimate_dissolve_ex(BMesh *bm, const float angle_limit, const bool /* build heap */ for (i = 0; i < einput_len; i++) { BMEdge *e = einput_arr[i]; - const float cost = bm_edge_calc_dissolve_error(e, delimit); + const float cost = bm_edge_calc_dissolve_error(e, delimit, &delimit_data); eheap_table[i] = BLI_heap_insert(eheap, cost, e); BM_elem_index_set(e, i); /* set dirty */ } @@ -169,7 +223,7 @@ void BM_mesh_decimate_dissolve_ex(BMesh *bm, const float angle_limit, const bool do { const int j = BM_elem_index_get(l_iter->e); if (j != -1 && eheap_table[j]) { - const float cost = bm_edge_calc_dissolve_error(l_iter->e, delimit); + const float cost = bm_edge_calc_dissolve_error(l_iter->e, delimit, &delimit_data); BLI_heap_remove(eheap, eheap_table[j]); eheap_table[j] = BLI_heap_insert(eheap, cost, l_iter->e); } @@ -189,7 +243,7 @@ void BM_mesh_decimate_dissolve_ex(BMesh *bm, const float angle_limit, const bool /* prepare for cleanup */ BM_mesh_elem_index_ensure(bm, BM_VERT); vert_reverse_lookup = MEM_mallocN(sizeof(int) * bm->totvert, __func__); - fill_vn_i(vert_reverse_lookup, bm->totvert, -1); + copy_vn_i(vert_reverse_lookup, bm->totvert, -1); for (i = 0; i < vinput_len; i++) { BMVert *v = vinput_arr[i]; vert_reverse_lookup[BM_elem_index_get(v)] = i; @@ -316,8 +370,9 @@ void BM_mesh_decimate_dissolve_ex(BMesh *bm, const float angle_limit, const bool MEM_freeN(_heap_table); } -void BM_mesh_decimate_dissolve(BMesh *bm, const float angle_limit, const bool do_dissolve_boundaries, - const BMO_Delimit delimit) +void BM_mesh_decimate_dissolve( + BMesh *bm, const float angle_limit, const bool do_dissolve_boundaries, + const BMO_Delimit delimit) { int vinput_len; int einput_len; diff --git a/source/blender/bmesh/tools/bmesh_edgenet.c b/source/blender/bmesh/tools/bmesh_edgenet.c index 1328b81b746..2a1946df7ae 100644 --- a/source/blender/bmesh/tools/bmesh_edgenet.c +++ b/source/blender/bmesh/tools/bmesh_edgenet.c @@ -166,8 +166,8 @@ static BMFace *bm_edgenet_face_from_path( { BMFace *f; LinkNode *v_lnk; - unsigned int i; - unsigned int i_prev; + int i; + bool ok; BMVert **vert_arr = BLI_array_alloca(vert_arr, path_len); BMEdge **edge_arr = BLI_array_alloca(edge_arr, path_len); @@ -176,11 +176,9 @@ static BMFace *bm_edgenet_face_from_path( vert_arr[i] = v_lnk->link; } - i_prev = path_len - 1; - for (i = 0; i < path_len; i++) { - edge_arr[i_prev] = BM_edge_exists(vert_arr[i], vert_arr[i_prev]); - i_prev = i; - } + ok = BM_edges_from_verts(edge_arr, vert_arr, i); + BLI_assert(ok); + UNUSED_VARS_NDEBUG(ok); /* no need for this, we do overlap checks before allowing the path to be used */ #if 0 @@ -448,10 +446,10 @@ static LinkNode *bm_edgenet_path_calc_best( * * \param bm The mesh to operate on. * \param use_edge_tag Only fill tagged edges. - * \param face_oflag if nonzero, apply all new faces with this bmo flag. */ -void BM_mesh_edgenet(BMesh *bm, - const bool use_edge_tag, const bool use_new_face_tag) +void BM_mesh_edgenet( + BMesh *bm, + const bool use_edge_tag, const bool use_new_face_tag) { VertNetInfo *vnet_info = MEM_callocN(sizeof(*vnet_info) * (size_t)bm->totvert, __func__); BLI_mempool *edge_queue_pool = BLI_mempool_create(sizeof(LinkNode), 0, 512, BLI_MEMPOOL_NOP); diff --git a/source/blender/bmesh/tools/bmesh_edgenet.h b/source/blender/bmesh/tools/bmesh_edgenet.h index 327a7f5aa23..1ad5cadae7c 100644 --- a/source/blender/bmesh/tools/bmesh_edgenet.h +++ b/source/blender/bmesh/tools/bmesh_edgenet.h @@ -27,7 +27,8 @@ * \ingroup bmesh */ -void BM_mesh_edgenet(BMesh *bm, - const bool use_edge_tag, const bool use_new_face_tag); +void BM_mesh_edgenet( + BMesh *bm, + const bool use_edge_tag, const bool use_new_face_tag); #endif /* __BMESH_EDGENET_H__ */ diff --git a/source/blender/bmesh/tools/bmesh_edgesplit.c b/source/blender/bmesh/tools/bmesh_edgesplit.c index 947b77675d8..a59a5c43c82 100644 --- a/source/blender/bmesh/tools/bmesh_edgesplit.c +++ b/source/blender/bmesh/tools/bmesh_edgesplit.c @@ -35,70 +35,15 @@ #include "bmesh_edgesplit.h" /* own include */ - /** - * Remove the BM_ELEM_TAG flag for edges we cant split - * - * un-tag edges not connected to other tagged edges, - * unless they are on a boundary + * \param use_verts Use flagged verts instead of edges. + * \param tag_only Only split tagged edges. + * \param copy_select Copy selection history. */ -static void bm_edgesplit_validate_seams(BMesh *bm) -{ - BMIter iter; - BMEdge *e; - - unsigned char *vtouch; - - BM_mesh_elem_index_ensure(bm, BM_VERT); - - vtouch = MEM_callocN(sizeof(char) * bm->totvert, __func__); - - /* tag all boundary verts so as not to untag an edge which is inbetween only 2 faces [] */ - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - - /* unrelated to flag assignment in this function - since this is the - * only place we loop over all edges, disable tag */ - BM_elem_flag_disable(e, BM_ELEM_INTERNAL_TAG); - - if (e->l == NULL) { - BM_elem_flag_disable(e, BM_ELEM_TAG); - } - else if (BM_edge_is_boundary(e)) { - unsigned char *vt; - vt = &vtouch[BM_elem_index_get(e->v1)]; if (*vt < 2) (*vt)++; - vt = &vtouch[BM_elem_index_get(e->v2)]; if (*vt < 2) (*vt)++; - - /* while the boundary verts need to be tagged, - * the edge its self can't be split */ - BM_elem_flag_disable(e, BM_ELEM_TAG); - } - } - - /* single marked edges unconnected to any other marked edges - * are illegal, go through and unmark them */ - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(e, BM_ELEM_TAG)) { - /* lame, but we don't want the count to exceed 255, - * so just count to 2, its all we need */ - unsigned char *vt; - vt = &vtouch[BM_elem_index_get(e->v1)]; if (*vt < 2) (*vt)++; - vt = &vtouch[BM_elem_index_get(e->v2)]; if (*vt < 2) (*vt)++; - } - } - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(e, BM_ELEM_TAG)) { - if (vtouch[BM_elem_index_get(e->v1)] == 1 && - vtouch[BM_elem_index_get(e->v2)] == 1) - { - BM_elem_flag_disable(e, BM_ELEM_TAG); - } - } - } - - MEM_freeN(vtouch); -} - -void BM_mesh_edgesplit(BMesh *bm, const bool use_verts, const bool tag_only, const bool copy_select) +void BM_mesh_edgesplit( + BMesh *bm, + const bool use_verts, + const bool tag_only, const bool copy_select) { BMIter iter; BMEdge *e; @@ -142,43 +87,13 @@ void BM_mesh_edgesplit(BMesh *bm, const bool use_verts, const bool tag_only, con } } - bm_edgesplit_validate_seams(bm); - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { if (BM_elem_flag_test(e, BM_ELEM_TAG)) { - /* this flag gets copied so we can be sure duplicate edges get it too (important) */ - BM_elem_flag_enable(e, BM_ELEM_INTERNAL_TAG); - - /* keep splitting until each loop has its own edge */ - while (!BM_edge_is_boundary(e)) { - BMLoop *l_sep = e->l; - bmesh_edge_separate(bm, e, l_sep, copy_select); - BLI_assert(l_sep->e != e); - - if (use_ese) { - BMEditSelection *ese = BLI_ghash_lookup(ese_gh, e); - if (UNLIKELY(ese)) { - BM_select_history_store_after_notest(bm, ese, l_sep->e); - } - } - } - BM_elem_flag_enable(e->v1, BM_ELEM_TAG); BM_elem_flag_enable(e->v2, BM_ELEM_TAG); } } - if (use_verts) { - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(e->v1, BM_ELEM_TAG) == false) { - BM_elem_flag_disable(e->v1, BM_ELEM_TAG); - } - if (BM_elem_flag_test(e->v2, BM_ELEM_TAG) == false) { - BM_elem_flag_disable(e->v2, BM_ELEM_TAG); - } - } - } - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { if (BM_elem_flag_test(e, BM_ELEM_TAG)) { unsigned int i; @@ -191,7 +106,7 @@ void BM_mesh_edgesplit(BMesh *bm, const bool use_verts, const bool tag_only, con BMVert **vtar; int vtar_len; - bmesh_vert_separate(bm, v, &vtar, &vtar_len, copy_select); + BM_vert_separate_hflag(bm, v, BM_ELEM_TAG, copy_select, &vtar, &vtar_len); /* first value is always in 'v' */ if (vtar_len > 1) { @@ -208,13 +123,22 @@ void BM_mesh_edgesplit(BMesh *bm, const bool use_verts, const bool tag_only, con MEM_freeN(vtar); } else { - bmesh_vert_separate(bm, v, NULL, NULL, copy_select); + BM_vert_separate_hflag(bm, v, BM_ELEM_TAG, copy_select, NULL, NULL); } } } } } +#ifndef NDEBUG + /* ensure we don't have any double edges! */ + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_TAG)) { + BLI_assert(BM_edge_find_double(e) == NULL); + } + } +#endif + if (use_ese) { BLI_ghash_free(ese_gh, NULL, NULL); } diff --git a/source/blender/bmesh/tools/bmesh_edgesplit.h b/source/blender/bmesh/tools/bmesh_edgesplit.h index bd66f6a9e2f..26040077f43 100644 --- a/source/blender/bmesh/tools/bmesh_edgesplit.h +++ b/source/blender/bmesh/tools/bmesh_edgesplit.h @@ -27,6 +27,9 @@ * \ingroup bmesh */ -void BM_mesh_edgesplit(BMesh *bm, const bool use_verts, const bool tag_only, const bool copy_select); +void BM_mesh_edgesplit( + BMesh *bm, + const bool use_verts, + const bool tag_only, const bool copy_select); #endif /* __BMESH_EDGESPLIT_H__ */ diff --git a/source/blender/bmesh/tools/bmesh_intersect.c b/source/blender/bmesh/tools/bmesh_intersect.c index 4d87c3e3551..19cf2d29aff 100644 --- a/source/blender/bmesh/tools/bmesh_intersect.c +++ b/source/blender/bmesh/tools/bmesh_intersect.c @@ -45,7 +45,9 @@ #include "BLI_linklist_stack.h" #include "BLI_stackdefines.h" -#include "BLI_array.h" +#ifndef NDEBUG +# include "BLI_array_utils.h" +#endif #include "BLI_kdopbvh.h" @@ -541,7 +543,7 @@ static void bm_isect_tri_tri( if (((1 << i_b_e0) | (1 << i_b_e1)) & b_mask) continue; fac = line_point_factor_v3(fv_a[i_a]->co, fv_b[i_b_e0]->co, fv_b[i_b_e1]->co); - if ((fac > 0.0f - s->epsilon.eps) && (fac < 1.0 + s->epsilon.eps)) { + if ((fac > 0.0f - s->epsilon.eps) && (fac < 1.0f + s->epsilon.eps)) { float ix[3]; interp_v3_v3v3(ix, fv_b[i_b_e0]->co, fv_b[i_b_e1]->co, fac); if (len_squared_v3v3(ix, fv_a[i_a]->co) <= s->epsilon.eps2x_sq) { @@ -579,7 +581,7 @@ static void bm_isect_tri_tri( if (((1 << i_a_e0) | (1 << i_a_e1)) & a_mask) continue; fac = line_point_factor_v3(fv_b[i_b]->co, fv_a[i_a_e0]->co, fv_a[i_a_e1]->co); - if ((fac > 0.0 - s->epsilon.eps) && (fac < 1.0 + s->epsilon.eps)) { + if ((fac > 0.0f - s->epsilon.eps) && (fac < 1.0f + s->epsilon.eps)) { float ix[3]; interp_v3_v3v3(ix, fv_a[i_a_e0]->co, fv_a[i_a_e1]->co, fac); if (len_squared_v3v3(ix, fv_b[i_b]->co) <= s->epsilon.eps2x_sq) { @@ -802,7 +804,7 @@ bool BM_mesh_intersect( s.edgetri_cache = BLI_ghash_new(BLI_ghashutil_inthash_v4_p, BLI_ghashutil_inthash_v4_cmp, __func__); s.edge_verts = BLI_ghash_ptr_new(__func__); - s.face_edges = BLI_ghash_ptr_new(__func__); + s.face_edges = BLI_ghash_int_new(__func__); s.wire_edges = BLI_gset_ptr_new(__func__); s.vert_dissolve = NULL; @@ -857,7 +859,7 @@ bool BM_mesh_intersect( {UNPACK3(looptris[i][2]->v->co)}, }; - BLI_bvhtree_insert(tree_a, i, (float *)t_cos, 3); + BLI_bvhtree_insert(tree_a, i, (const float *)t_cos, 3); } } BLI_bvhtree_balance(tree_a); @@ -874,7 +876,7 @@ bool BM_mesh_intersect( {UNPACK3(looptris[i][2]->v->co)}, }; - BLI_bvhtree_insert(tree_b, i, (float *)t_cos, 3); + BLI_bvhtree_insert(tree_b, i, (const float *)t_cos, 3); } } BLI_bvhtree_balance(tree_b); @@ -883,7 +885,7 @@ bool BM_mesh_intersect( tree_b = tree_a; } - overlap = BLI_bvhtree_overlap(tree_b, tree_a, &tree_overlap_tot); + overlap = BLI_bvhtree_overlap(tree_b, tree_a, &tree_overlap_tot, NULL, NULL); if (overlap) { unsigned int i; @@ -998,13 +1000,13 @@ bool BM_mesh_intersect( if (BM_vert_in_edge(e, v_prev)) { v_prev = BM_edge_split(bm, e, v_prev, NULL, CLAMPIS(fac, 0.0f, 1.0f)); - BLI_assert( BM_vert_in_edge(e, v_end)); + BLI_assert(BM_vert_in_edge(e, v_end)); if (!BM_edge_exists(v_prev, vi) && !BM_vert_splice_check_double(v_prev, vi) && !BM_vert_pair_share_face_check(v_prev, vi)) { - BM_vert_splice(bm, v_prev, vi); + BM_vert_splice(bm, vi, v_prev); } else { copy_v3_v3(v_prev->co, vi->co); @@ -1015,6 +1017,7 @@ bool BM_mesh_intersect( } } } + UNUSED_VARS_NDEBUG(v_end); } } #endif @@ -1038,8 +1041,8 @@ bool BM_mesh_intersect( } } - splice_ls = MEM_mallocN((unsigned int)BLI_gset_size(s.wire_edges) * sizeof(*splice_ls), __func__); - STACK_INIT(splice_ls, (unsigned int)BLI_gset_size(s.wire_edges)); + splice_ls = MEM_mallocN(BLI_gset_size(s.wire_edges) * sizeof(*splice_ls), __func__); + STACK_INIT(splice_ls, BLI_gset_size(s.wire_edges)); for (node = s.vert_dissolve; node; node = node->next) { BMEdge *e_pair[2]; @@ -1226,7 +1229,7 @@ bool BM_mesh_intersect( if (!BM_edge_exists(UNPACK2(splice_ls[i])) && !BM_vert_splice_check_double(UNPACK2(splice_ls[i]))) { - BM_vert_splice(bm, UNPACK2(splice_ls[i])); + BM_vert_splice(bm, splice_ls[i][1], splice_ls[i][0]); } } } @@ -1265,10 +1268,8 @@ bool BM_mesh_intersect( face_edges_split(bm, f, e_ls_base); } } -#else - (void)totface_orig; #endif /* USE_NET */ - + (void)totface_orig; #ifdef USE_SEPARATE if (use_separate) { diff --git a/source/blender/bmesh/tools/bmesh_path.c b/source/blender/bmesh/tools/bmesh_path.c index 060d0dd969b..6633803414b 100644 --- a/source/blender/bmesh/tools/bmesh_path.c +++ b/source/blender/bmesh/tools/bmesh_path.c @@ -95,7 +95,7 @@ static void verttag_add_adjacent(Heap *heap, BMVert *v_a, BMVert **verts_prev, f LinkNode *BM_mesh_calc_path_vert( BMesh *bm, BMVert *v_src, BMVert *v_dst, const bool use_length, - void *user_data, bool (*test_fn)(BMVert *, void *user_data)) + bool (*test_fn)(BMVert *, void *user_data), void *user_data) { LinkNode *path = NULL; /* BM_ELEM_TAG flag is used to store visited edges */ @@ -126,7 +126,7 @@ LinkNode *BM_mesh_calc_path_vert( verts_prev = MEM_callocN(sizeof(*verts_prev) * totvert, __func__); cost = MEM_mallocN(sizeof(*cost) * totvert, __func__); - fill_vn_fl(cost, totvert, 1e20f); + copy_vn_fl(cost, totvert, 1e20f); /* * Arrays are now filled as follows: @@ -221,7 +221,7 @@ static void edgetag_add_adjacent(Heap *heap, BMEdge *e1, BMEdge **edges_prev, fl LinkNode *BM_mesh_calc_path_edge( BMesh *bm, BMEdge *e_src, BMEdge *e_dst, const bool use_length, - void *user_data, bool (*filter_fn)(BMEdge *, void *user_data)) + bool (*filter_fn)(BMEdge *, void *user_data), void *user_data) { LinkNode *path = NULL; /* BM_ELEM_TAG flag is used to store visited edges */ @@ -252,7 +252,7 @@ LinkNode *BM_mesh_calc_path_edge( edges_prev = MEM_callocN(sizeof(*edges_prev) * totedge, "SeamPathPrevious"); cost = MEM_mallocN(sizeof(*cost) * totedge, "SeamPathCost"); - fill_vn_fl(cost, totedge, 1e20f); + copy_vn_fl(cost, totedge, 1e20f); /* * Arrays are now filled as follows: @@ -347,7 +347,7 @@ static void facetag_add_adjacent(Heap *heap, BMFace *f_a, BMFace **faces_prev, f LinkNode *BM_mesh_calc_path_face( BMesh *bm, BMFace *f_src, BMFace *f_dst, const bool use_length, - void *user_data, bool (*test_fn)(BMFace *, void *user_data)) + bool (*test_fn)(BMFace *, void *user_data), void *user_data) { LinkNode *path = NULL; /* BM_ELEM_TAG flag is used to store visited edges */ @@ -378,7 +378,7 @@ LinkNode *BM_mesh_calc_path_face( faces_prev = MEM_callocN(sizeof(*faces_prev) * totface, __func__); cost = MEM_mallocN(sizeof(*cost) * totface, __func__); - fill_vn_fl(cost, totface, 1e20f); + copy_vn_fl(cost, totface, 1e20f); /* * Arrays are now filled as follows: diff --git a/source/blender/bmesh/tools/bmesh_path.h b/source/blender/bmesh/tools/bmesh_path.h index a13290b875e..c39e08e83ef 100644 --- a/source/blender/bmesh/tools/bmesh_path.h +++ b/source/blender/bmesh/tools/bmesh_path.h @@ -29,14 +29,17 @@ struct LinkNode *BM_mesh_calc_path_vert( BMesh *bm, BMVert *v_src, BMVert *v_dst, const bool use_length, - void *user_data, bool (*filter_fn)(BMVert *, void *)); + bool (*filter_fn)(BMVert *, void *), void *user_data) +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2, 3, 5); struct LinkNode *BM_mesh_calc_path_edge( BMesh *bm, BMEdge *e_src, BMEdge *e_dst, const bool use_length, - void *user_data, bool (*filter_fn)(BMEdge *, void *)); + bool (*filter_fn)(BMEdge *, void *), void *user_data) +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2, 3, 5); struct LinkNode *BM_mesh_calc_path_face( BMesh *bm, BMFace *f_src, BMFace *f_dst, const bool use_length, - void *user_data, bool (*test_fn)(BMFace *, void *)); + bool (*test_fn)(BMFace *, void *), void *user_data) +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2, 3, 5); #endif /* __BMESH_PATH_H__ */ diff --git a/source/blender/bmesh/tools/bmesh_region_match.c b/source/blender/bmesh/tools/bmesh_region_match.c new file mode 100644 index 00000000000..a6860a6614a --- /dev/null +++ b/source/blender/bmesh/tools/bmesh_region_match.c @@ -0,0 +1,1519 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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 2 + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/tools/bmesh_region_match.c + * \ingroup bmesh + * + * Given a contiguous region of faces, + * find multiple matching regions (based on topology) and return them. + * + * Implementation: + * + * - Given a face region, find its topological center. + * - Compare this with other vertices surrounding geometry with this ones. + * (reduce the search space by creating a connectivity ID per vertex + * and only run comprehensive tests on those). + * - All hashes must be order independent so matching topology can be identified. + * - The term UUID here doesn't mean each ID is initially unique. + * (uniqueness is improved by re-hashing with connected data). + */ + +#include <string.h> + +#include "MEM_guardedalloc.h" +#include "BLI_listbase.h" +#include "BLI_linklist.h" +#include "BLI_alloca.h" +#include "BLI_ghash.h" +#include "BLI_mempool.h" +#include "BLI_linklist_stack.h" + +#include "bmesh.h" + +#include "tools/bmesh_region_match.h" /* own incldue */ + +/* avoid re-creating ghash and pools for each search */ +#define USE_WALKER_REUSE + +/* do a first-pass id of all vertices, + * this avoids expensive checks on every item later on + * (works fine without, just slower) */ +#define USE_PIVOT_FASTMATCH + +/* otherwise use active element as pivot, for quick tests only */ +#define USE_PIVOT_SEARCH + +// #define DEBUG_TIME +// #define DEBUG_PRINT + +#ifdef DEBUG_TIME +# include "PIL_time.h" +# include "PIL_time_utildefines.h" +#endif + +#include "BLI_strict_flags.h" + + +/* -------------------------------------------------------------------- */ +/* UUID-Walk API */ + +/** \name Internal UUIDWalk API + * \{ */ + +#define PRIME_VERT_INIT 100003 + +typedef uintptr_t UUID_Int; + +typedef struct UUIDWalk { + + /* List of faces we can step onto (UUIDFaceStep's) */ + ListBase faces_step; + + /* Face & Vert UUID's */ + GHash *verts_uuid; + GHash *faces_uuid; + + /* memory pool for LinkNode's */ + BLI_mempool *link_pool; + + /* memory pool for LinkBase's */ + BLI_mempool *lbase_pool; + + /* memory pool for UUIDFaceStep's */ + BLI_mempool *step_pool; + BLI_mempool *step_pool_items; + + /* Optionaly use face-tag to isolate search */ + bool use_face_isolate; + + /* Increment for each pass added */ + UUID_Int pass; + + /* runtime vars, aviod re-creating each pass */ + struct { + GHash *verts_uuid; /* BMVert -> UUID */ + GSet *faces_step; /* BMFace */ + + GHash *faces_from_uuid; /* UUID -> UUIDFaceStepItem */ + + UUID_Int *rehash_store; + unsigned int rehash_store_len; + } cache; + +} UUIDWalk; + +/* stores a set of potential faces to step onto */ +typedef struct UUIDFaceStep { + struct UUIDFaceStep *next, *prev; + + /* unsorted 'BMFace' */ + LinkNode *faces; + + /* faces sorted into 'UUIDFaceStepItem' */ + ListBase items; +} UUIDFaceStep; + +/* store face-lists with same uuid */ +typedef struct UUIDFaceStepItem { + struct UUIDFaceStepItem *next, *prev; + uintptr_t uuid; + + LinkNode *list; + unsigned int list_len; +} UUIDFaceStepItem; + +BLI_INLINE bool bm_uuidwalk_face_test( + UUIDWalk *uuidwalk, BMFace *f) +{ + if (uuidwalk->use_face_isolate) { + return BM_elem_flag_test_bool(f, BM_ELEM_TAG); + } + else { + return true; + } +} + +BLI_INLINE bool bm_uuidwalk_vert_lookup( + UUIDWalk *uuidwalk, BMVert *v, UUID_Int *r_uuid) +{ + void **ret; + ret = BLI_ghash_lookup_p(uuidwalk->verts_uuid, v); + if (ret) { + *r_uuid = (UUID_Int)(*ret); + return true; + } + else { + return false; + } +} + +BLI_INLINE bool bm_uuidwalk_face_lookup( + UUIDWalk *uuidwalk, BMFace *f, UUID_Int *r_uuid) +{ + void **ret; + ret = BLI_ghash_lookup_p(uuidwalk->faces_uuid, f); + if (ret) { + *r_uuid = (UUID_Int)(*ret); + return true; + } + else { + return false; + } +} + +static unsigned int ghashutil_bmelem_indexhash(const void *key) +{ + const BMElem *ele = key; + return (unsigned int)BM_elem_index_get(ele); +} + +static bool ghashutil_bmelem_indexcmp(const void *a, const void *b) +{ + BLI_assert((a != b) == (BM_elem_index_get((BMElem *)a) != BM_elem_index_get((BMElem *)b))); + return (a != b); +} + +static GHash *ghash_bmelem_new_ex( + const char *info, + const unsigned int nentries_reserve) +{ + return BLI_ghash_new_ex(ghashutil_bmelem_indexhash, ghashutil_bmelem_indexcmp, info, nentries_reserve); +} + +static GSet *gset_bmelem_new_ex( + const char *info, + const unsigned int nentries_reserve) +{ + return BLI_gset_new_ex(ghashutil_bmelem_indexhash, ghashutil_bmelem_indexcmp, info, nentries_reserve); +} + + +static GHash *ghash_bmelem_new(const char *info) +{ + return ghash_bmelem_new_ex(info, 0); +} + +static GSet *gset_bmelem_new(const char *info) +{ + return gset_bmelem_new_ex(info, 0); +} + + +static void bm_uuidwalk_init( + UUIDWalk *uuidwalk, + const unsigned int faces_src_region_len, + const unsigned int verts_src_region_len) +{ + BLI_listbase_clear(&uuidwalk->faces_step); + + uuidwalk->verts_uuid = ghash_bmelem_new_ex(__func__, verts_src_region_len); + uuidwalk->faces_uuid = ghash_bmelem_new_ex(__func__, faces_src_region_len); + + uuidwalk->cache.verts_uuid = ghash_bmelem_new(__func__); + uuidwalk->cache.faces_step = gset_bmelem_new(__func__); + + /* works because 'int' ghash works for intptr_t too */ + uuidwalk->cache.faces_from_uuid = BLI_ghash_int_new(__func__); + + uuidwalk->cache.rehash_store = NULL; + uuidwalk->cache.rehash_store_len = 0; + + uuidwalk->use_face_isolate = false; + + /* smaller pool's for faster clearing */ + uuidwalk->link_pool = BLI_mempool_create(sizeof(LinkNode), 64, 64, BLI_MEMPOOL_NOP); + uuidwalk->step_pool = BLI_mempool_create(sizeof(UUIDFaceStep), 64, 64, BLI_MEMPOOL_NOP); + uuidwalk->step_pool_items = BLI_mempool_create(sizeof(UUIDFaceStepItem), 64, 64, BLI_MEMPOOL_NOP); + + uuidwalk->pass = 1; +} + +static void bm_uuidwalk_clear( + UUIDWalk *uuidwalk) +{ + BLI_listbase_clear(&uuidwalk->faces_step); + + BLI_ghash_clear(uuidwalk->verts_uuid, NULL, NULL); + BLI_ghash_clear(uuidwalk->faces_uuid, NULL, NULL); + + BLI_ghash_clear(uuidwalk->cache.verts_uuid, NULL, NULL); + BLI_gset_clear(uuidwalk->cache.faces_step, NULL); + BLI_ghash_clear(uuidwalk->cache.faces_from_uuid, NULL, NULL); + + /* keep rehash_store as-is, for reuse */ + + uuidwalk->use_face_isolate = false; + + BLI_mempool_clear(uuidwalk->link_pool); + BLI_mempool_clear(uuidwalk->step_pool); + BLI_mempool_clear(uuidwalk->step_pool_items); + + uuidwalk->pass = 1; +} + +static void bm_uuidwalk_free( + UUIDWalk *uuidwalk) +{ + /** + * Handled by pools + * + * - uuidwalk->faces_step + */ + + BLI_ghash_free(uuidwalk->verts_uuid, NULL, NULL); + BLI_ghash_free(uuidwalk->faces_uuid, NULL, NULL); + + /* cache */ + BLI_ghash_free(uuidwalk->cache.verts_uuid, NULL, NULL); + BLI_gset_free(uuidwalk->cache.faces_step, NULL); + BLI_ghash_free(uuidwalk->cache.faces_from_uuid, NULL, NULL); + MEM_SAFE_FREE(uuidwalk->cache.rehash_store); + + BLI_mempool_destroy(uuidwalk->link_pool); + BLI_mempool_destroy(uuidwalk->step_pool); + BLI_mempool_destroy(uuidwalk->step_pool_items); +} + +static UUID_Int bm_uuidwalk_calc_vert_uuid( + UUIDWalk *uuidwalk, BMVert *v) +{ +#define PRIME_VERT_SMALL 7 +#define PRIME_VERT_MID 43 +#define PRIME_VERT_LARGE 1031 + +#define PRIME_FACE_SMALL 13 +#define PRIME_FACE_MID 53 + + UUID_Int uuid; + + uuid = uuidwalk->pass * PRIME_VERT_LARGE; + + /* vert -> other */ + { + unsigned int tot = 0; + BMIter eiter; + BMEdge *e; + BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { + BMVert *v_other = BM_edge_other_vert(e, v); + UUID_Int uuid_other; + if (bm_uuidwalk_vert_lookup(uuidwalk, v_other, &uuid_other)) { + uuid ^= (uuid_other * PRIME_VERT_SMALL); + tot += 1; + } + } + uuid ^= (tot * PRIME_VERT_MID); + } + + /* faces */ + { + unsigned int tot = 0; + BMIter iter; + BMFace *f; + + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + UUID_Int uuid_other; + if (bm_uuidwalk_face_lookup(uuidwalk, f, &uuid_other)) { + uuid ^= (uuid_other * PRIME_FACE_SMALL); + tot += 1; + } + } + uuid ^= (tot * PRIME_FACE_MID); + } + + return uuid; + +#undef PRIME_VERT_SMALL +#undef PRIME_VERT_MID +#undef PRIME_VERT_LARGE + +#undef PRIME_FACE_SMALL +#undef PRIME_FACE_MID +} + +static UUID_Int bm_uuidwalk_calc_face_uuid( + UUIDWalk *uuidwalk, BMFace *f) +{ +#define PRIME_VERT_SMALL 11 + +#define PRIME_FACE_SMALL 17 +#define PRIME_FACE_LARGE 1013 + + UUID_Int uuid; + + uuid = uuidwalk->pass * (unsigned int)f->len * PRIME_FACE_LARGE; + + /* face-verts */ + { + BMLoop *l_iter, *l_first; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + UUID_Int uuid_other; + if (bm_uuidwalk_vert_lookup(uuidwalk, l_iter->v, &uuid_other)) { + uuid ^= (uuid_other * PRIME_VERT_SMALL); + } + } while ((l_iter = l_iter->next) != l_first); + } + + /* face-faces (connected by edge) */ + { + BMLoop *l_iter, *l_first; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (l_iter->radial_next != l_iter) { + BMLoop *l_iter_radial = l_iter->radial_next; + do { + UUID_Int uuid_other; + if (bm_uuidwalk_face_lookup(uuidwalk, l_iter_radial->f, &uuid_other)) { + uuid ^= (uuid_other * PRIME_FACE_SMALL); + } + } while ((l_iter_radial = l_iter_radial->radial_next) != l_iter); + } + } while ((l_iter = l_iter->next) != l_first); + } + + return uuid; + +#undef PRIME_VERT_SMALL + +#undef PRIME_FACE_SMALL +#undef PRIME_FACE_LARGE +} + +static void bm_uuidwalk_rehash_reserve( + UUIDWalk *uuidwalk, unsigned int rehash_store_len_new) +{ + if (UNLIKELY(rehash_store_len_new > uuidwalk->cache.rehash_store_len)) { + /* avoid re-allocs */ + rehash_store_len_new *= 2; + uuidwalk->cache.rehash_store = + MEM_reallocN(uuidwalk->cache.rehash_store, + rehash_store_len_new * sizeof(*uuidwalk->cache.rehash_store)); + uuidwalk->cache.rehash_store_len = rehash_store_len_new; + } +} + +/** + * Re-hash all elements, delay updating so as not to create feedback loop. + */ +static void bm_uuidwalk_rehash( + UUIDWalk *uuidwalk) +{ + GHashIterator gh_iter; + UUID_Int *uuid_store; + unsigned int i; + + unsigned int rehash_store_len_new = MAX2(BLI_ghash_size(uuidwalk->verts_uuid), + BLI_ghash_size(uuidwalk->faces_uuid)); + + bm_uuidwalk_rehash_reserve(uuidwalk, rehash_store_len_new); + uuid_store = uuidwalk->cache.rehash_store; + + /* verts */ + i = 0; + GHASH_ITER (gh_iter, uuidwalk->verts_uuid) { + BMVert *v = BLI_ghashIterator_getKey(&gh_iter); + uuid_store[i++] = bm_uuidwalk_calc_vert_uuid(uuidwalk, v); + } + i = 0; + GHASH_ITER (gh_iter, uuidwalk->verts_uuid) { + void **uuid_p = BLI_ghashIterator_getValue_p(&gh_iter); + *((UUID_Int *)uuid_p) = uuid_store[i++]; + } + + /* faces */ + i = 0; + GHASH_ITER (gh_iter, uuidwalk->faces_uuid) { + BMFace *f = BLI_ghashIterator_getKey(&gh_iter); + uuid_store[i++] = bm_uuidwalk_calc_face_uuid(uuidwalk, f); + } + i = 0; + GHASH_ITER (gh_iter, uuidwalk->faces_uuid) { + void **uuid_p = BLI_ghashIterator_getValue_p(&gh_iter); + *((UUID_Int *)uuid_p) = uuid_store[i++]; + } +} + +static void bm_uuidwalk_rehash_facelinks( + UUIDWalk *uuidwalk, + LinkNode *faces, const unsigned int faces_len, + const bool is_init) +{ + UUID_Int *uuid_store; + LinkNode *f_link; + unsigned int i; + + bm_uuidwalk_rehash_reserve(uuidwalk, faces_len); + uuid_store = uuidwalk->cache.rehash_store; + + i = 0; + for (f_link = faces; f_link; f_link = f_link->next) { + BMFace *f = f_link->link; + uuid_store[i++] = bm_uuidwalk_calc_face_uuid(uuidwalk, f); + } + + i = 0; + if (is_init) { + for (f_link = faces; f_link; f_link = f_link->next) { + BMFace *f = f_link->link; + BLI_ghash_insert(uuidwalk->faces_uuid, f, (void *)uuid_store[i++]); + } + } + else { + for (f_link = faces; f_link; f_link = f_link->next) { + BMFace *f = f_link->link; + void **uuid_p = BLI_ghash_lookup_p(uuidwalk->faces_uuid, f); + *((UUID_Int *)uuid_p) = uuid_store[i++]; + } + } +} + +static bool bm_vert_is_uuid_connect( + UUIDWalk *uuidwalk, BMVert *v) +{ + BMIter eiter; + BMEdge *e; + + BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { + BMVert *v_other = BM_edge_other_vert(e, v); + if (BLI_ghash_haskey(uuidwalk->verts_uuid, v_other)) { + return true; + } + } + return false; +} + +static void bm_uuidwalk_pass_add( + UUIDWalk *uuidwalk, LinkNode *faces_pass, const unsigned int faces_pass_len) +{ + GHashIterator gh_iter; + GHash *verts_uuid_pass; + GSet *faces_step_next; + LinkNode *f_link; + + UUIDFaceStep *fstep; + + BLI_assert(faces_pass_len == (unsigned int)BLI_linklist_count(faces_pass)); + + /* rehash faces now all their verts have been added */ + bm_uuidwalk_rehash_facelinks(uuidwalk, faces_pass, faces_pass_len, true); + + /* create verts_new */ + verts_uuid_pass = uuidwalk->cache.verts_uuid; + faces_step_next = uuidwalk->cache.faces_step; + + BLI_assert(BLI_ghash_size(verts_uuid_pass) == 0); + BLI_assert(BLI_gset_size(faces_step_next) == 0); + + /* Add the face_step data from connected faces, creating new passes */ + fstep = BLI_mempool_alloc(uuidwalk->step_pool); + BLI_addhead(&uuidwalk->faces_step, fstep); + fstep->faces = NULL; + BLI_listbase_clear(&fstep->items); + + for (f_link = faces_pass; f_link; f_link = f_link->next) { + BMFace *f = f_link->link; + BMLoop *l_iter, *l_first; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + /* fill verts_new */ + void **val_p; + if (!BLI_ghash_haskey(uuidwalk->verts_uuid, l_iter->v) && + !BLI_ghash_ensure_p(verts_uuid_pass, l_iter->v, &val_p) && + (bm_vert_is_uuid_connect(uuidwalk, l_iter->v) == true)) + { + const UUID_Int uuid = bm_uuidwalk_calc_vert_uuid(uuidwalk, l_iter->v); + *val_p = (void *)uuid; + } + + /* fill faces_step_next */ + if (l_iter->radial_next != l_iter) { + BMLoop *l_iter_radial = l_iter->radial_next; + do { + if (!BLI_ghash_haskey(uuidwalk->faces_uuid, l_iter_radial->f) && + !BLI_gset_haskey(faces_step_next, l_iter_radial->f) && + (bm_uuidwalk_face_test(uuidwalk, l_iter_radial->f))) + { + BLI_gset_insert(faces_step_next, l_iter_radial->f); + + /* add to fstep */ + BLI_linklist_prepend_pool(&fstep->faces, l_iter_radial->f, uuidwalk->link_pool); + } + } while ((l_iter_radial = l_iter_radial->radial_next) != l_iter); + } + } while ((l_iter = l_iter->next) != l_first); + } + + /* faces_uuid.update(verts_new) */ + GHASH_ITER (gh_iter, verts_uuid_pass) { + BMVert *v = BLI_ghashIterator_getKey(&gh_iter); + void *uuid_p = BLI_ghashIterator_getValue(&gh_iter); + BLI_ghash_insert(uuidwalk->verts_uuid, v, uuid_p); + } + + /* rehash faces now all their verts have been added */ + bm_uuidwalk_rehash_facelinks(uuidwalk, faces_pass, faces_pass_len, false); + + uuidwalk->pass += 1; + + BLI_ghash_clear(uuidwalk->cache.verts_uuid, NULL, NULL); + BLI_gset_clear(uuidwalk->cache.faces_step, NULL); +} + +static int bm_face_len_cmp(const void *v1, const void *v2) +{ + const BMFace *f1 = v1, *f2 = v2; + + if (f1->len > f2->len) return 1; + else if (f1->len < f2->len) return -1; + else return 0; +} + +static unsigned int bm_uuidwalk_init_from_edge( + UUIDWalk *uuidwalk, BMEdge *e) +{ + BMLoop *l_iter = e->l; + unsigned int f_arr_len = (unsigned int)BM_edge_face_count(e); + BMFace **f_arr = BLI_array_alloca(f_arr, f_arr_len); + unsigned int fstep_num = 0, i = 0; + + do { + BMFace *f = l_iter->f; + if (bm_uuidwalk_face_test(uuidwalk, f)) { + f_arr[i++] = f; + } + } while ((l_iter = l_iter->radial_next) != e->l); + BLI_assert(i <= f_arr_len); + f_arr_len = i; + + qsort(f_arr, f_arr_len, sizeof(*f_arr), bm_face_len_cmp); + + /* start us off! */ + { + const UUID_Int uuid = PRIME_VERT_INIT; + BLI_ghash_insert(uuidwalk->verts_uuid, e->v1, (void *)uuid); + BLI_ghash_insert(uuidwalk->verts_uuid, e->v2, (void *)uuid); + } + + /* turning an array into LinkNode's seems odd, + * but this is just for initialization, + * elsewhere using LinkNode's makes more sense */ + for (i = 0; i < f_arr_len; ) { + LinkNode *faces_pass = NULL; + const unsigned int i_init = i; + const int f_len = f_arr[i]->len; + + do { + BLI_linklist_prepend_pool(&faces_pass, f_arr[i++], uuidwalk->link_pool); + } while (i < f_arr_len && (f_len == f_arr[i]->len)); + + bm_uuidwalk_pass_add(uuidwalk, faces_pass, i - i_init); + BLI_linklist_free_pool(faces_pass, NULL, uuidwalk->link_pool); + fstep_num += 1; + } + + return fstep_num; +} + +#undef PRIME_VERT_INIT + +/** \} */ + + +/** \name Internal UUIDFaceStep API + * \{ */ + +static int facestep_sort(const void *a, const void *b) +{ + const UUIDFaceStepItem *fstep_a = a; + const UUIDFaceStepItem *fstep_b = b; + return (fstep_a->uuid > fstep_b->uuid) ? 1 : 0; +} + +/** + * Put faces in lists based on their uuid's, + * re-run for each pass since rehashing may differentiate face-groups. + */ +static bool bm_uuidwalk_facestep_begin( + UUIDWalk *uuidwalk, UUIDFaceStep *fstep) +{ + LinkNode *f_link, *f_link_next, **f_link_prev_p; + bool ok = false; + + BLI_assert(BLI_ghash_size(uuidwalk->cache.faces_from_uuid) == 0); + BLI_assert(BLI_listbase_is_empty(&fstep->items)); + + f_link_prev_p = &fstep->faces; + for (f_link = fstep->faces; f_link; f_link = f_link_next) { + BMFace *f = f_link->link; + f_link_next = f_link->next; + + /* possible another pass added this face already, free in that case */ + if (!BLI_ghash_haskey(uuidwalk->faces_uuid, f)) { + const UUID_Int uuid = bm_uuidwalk_calc_face_uuid(uuidwalk, f); + UUIDFaceStepItem *fstep_item; + void **val_p; + + ok = true; + + if (BLI_ghash_ensure_p(uuidwalk->cache.faces_from_uuid, (void *)uuid, &val_p)) { + fstep_item = *val_p; + } + else { + fstep_item = *val_p = BLI_mempool_alloc(uuidwalk->step_pool_items); + + /* add to start, so its handled on the next round of passes */ + BLI_addhead(&fstep->items, fstep_item); + fstep_item->uuid = uuid; + fstep_item->list = NULL; + fstep_item->list_len = 0; + } + + BLI_linklist_prepend_pool(&fstep_item->list, f, uuidwalk->link_pool); + fstep_item->list_len += 1; + + f_link_prev_p = &f_link->next; + } + else { + *f_link_prev_p = f_link->next; + BLI_mempool_free(uuidwalk->link_pool, f_link); + } + } + + BLI_ghash_clear(uuidwalk->cache.faces_from_uuid, NULL, NULL); + + BLI_listbase_sort(&fstep->items, facestep_sort); + + return ok; +} + +/** + * Cleans up temp data from #bm_uuidwalk_facestep_begin + */ +static void bm_uuidwalk_facestep_end( + UUIDWalk *uuidwalk, UUIDFaceStep *fstep) +{ + UUIDFaceStepItem *fstep_item; + + while ((fstep_item = BLI_pophead(&fstep->items))) { + BLI_mempool_free(uuidwalk->step_pool_items, fstep_item); + } +} + +static void bm_uuidwalk_facestep_free( + UUIDWalk *uuidwalk, UUIDFaceStep *fstep) +{ + LinkNode *f_link, *f_link_next; + + BLI_assert(BLI_listbase_is_empty(&fstep->items)); + + for (f_link = fstep->faces; f_link; f_link = f_link_next) { + f_link_next = f_link->next; + BLI_mempool_free(uuidwalk->link_pool, f_link); + } + + BLI_remlink(&uuidwalk->faces_step, fstep); + BLI_mempool_free(uuidwalk->step_pool, fstep); +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ +/* Main Loop to match up regions */ + +/** + * Given a face region and 2 candidate verts to begin mapping. + * return the matching region or NULL. + */ +static BMFace **bm_mesh_region_match_pair( +#ifdef USE_WALKER_REUSE + UUIDWalk *w_src, UUIDWalk *w_dst, +#endif + BMEdge *e_src, BMEdge *e_dst, + const unsigned int faces_src_region_len, + const unsigned int verts_src_region_len, + unsigned int *r_faces_result_len) +{ +#ifndef USE_WALKER_REUSE + UUIDWalk w_src_, w_dst_; + UUIDWalk *w_src = &w_src_, *w_dst = &w_dst_; +#endif + BMFace **faces_result = NULL; + bool found = false; + + BLI_assert(e_src != e_dst); + +#ifndef USE_WALKER_REUSE + bm_uuidwalk_init(w_src, faces_src_region_len, verts_src_region_len); + bm_uuidwalk_init(w_dst, faces_src_region_len, verts_src_region_len); +#endif + + w_src->use_face_isolate = true; + + /* setup the initial state */ + if (UNLIKELY(bm_uuidwalk_init_from_edge(w_src, e_src) != + bm_uuidwalk_init_from_edge(w_dst, e_dst))) + { + /* should never happen, if verts passed are compatible, but to be safe... */ + goto finally; + } + + bm_uuidwalk_rehash_reserve(w_src, MAX2(faces_src_region_len, verts_src_region_len)); + bm_uuidwalk_rehash_reserve(w_dst, MAX2(faces_src_region_len, verts_src_region_len)); + + while (true) { + bool ok = false; + + UUIDFaceStep *fstep_src = w_src->faces_step.first; + UUIDFaceStep *fstep_dst = w_dst->faces_step.first; + + BLI_assert(BLI_listbase_count(&w_src->faces_step) == BLI_listbase_count(&w_dst->faces_step)); + + while (fstep_src) { + + /* even if the destination has faces, + * it's not important, since the source doesn't, free and move-on. */ + if (fstep_src->faces == NULL) { + UUIDFaceStep *fstep_src_next = fstep_src->next; + UUIDFaceStep *fstep_dst_next = fstep_dst->next; + bm_uuidwalk_facestep_free(w_src, fstep_src); + bm_uuidwalk_facestep_free(w_dst, fstep_dst); + fstep_src = fstep_src_next; + fstep_dst = fstep_dst_next; + continue; + } + + if (bm_uuidwalk_facestep_begin(w_src, fstep_src) && + bm_uuidwalk_facestep_begin(w_dst, fstep_dst)) + { + /* Step over face-lists with matching UUID's + * both lists are sorted, so no need for lookups. + * The data is created on 'begin' and cleared on 'end' */ + UUIDFaceStepItem *fstep_item_src; + UUIDFaceStepItem *fstep_item_dst; + for (fstep_item_src = fstep_src->items.first, + fstep_item_dst = fstep_dst->items.first; + fstep_item_src && fstep_item_dst; + fstep_item_src = fstep_item_src->next, + fstep_item_dst = fstep_item_dst->next) + { + while ((fstep_item_dst != NULL) && + (fstep_item_dst->uuid < fstep_item_src->uuid)) + { + fstep_item_dst = fstep_item_dst->next; + } + + if ((fstep_item_dst == NULL) || + (fstep_item_src->uuid != fstep_item_dst->uuid) || + (fstep_item_src->list_len > fstep_item_dst->list_len)) + { + /* if the target walker has less than the source + * then the islands don't match, bail early */ + ok = false; + break; + } + + if (fstep_item_src->list_len == fstep_item_dst->list_len) { + /* found a match */ + bm_uuidwalk_pass_add(w_src, fstep_item_src->list, fstep_item_src->list_len); + bm_uuidwalk_pass_add(w_dst, fstep_item_dst->list, fstep_item_dst->list_len); + + BLI_linklist_free_pool(fstep_item_src->list, NULL, w_src->link_pool); + BLI_linklist_free_pool(fstep_item_dst->list, NULL, w_dst->link_pool); + + fstep_item_src->list = NULL; + fstep_item_src->list_len = 0; + + fstep_item_dst->list = NULL; + fstep_item_dst->list_len = 0; + + ok = true; + } + } + } + + bm_uuidwalk_facestep_end(w_src, fstep_src); + bm_uuidwalk_facestep_end(w_dst, fstep_dst); + + /* lock-step */ + fstep_src = fstep_src->next; + fstep_dst = fstep_dst->next; + } + + if (!ok) { + break; + } + + found = (BLI_ghash_size(w_dst->faces_uuid) == faces_src_region_len); + if (found) { + break; + } + + /* Expensive! but some cases fails without. + * (also faster in other cases since it can rule-out invalid regions) */ + bm_uuidwalk_rehash(w_src); + bm_uuidwalk_rehash(w_dst); + } + + if (found) { + GHashIterator gh_iter; + const unsigned int faces_result_len = BLI_ghash_size(w_dst->faces_uuid); + unsigned int i; + + faces_result = MEM_mallocN(sizeof(*faces_result) * (faces_result_len + 1), __func__); + GHASH_ITER_INDEX (gh_iter, w_dst->faces_uuid, i) { + BMFace *f = BLI_ghashIterator_getKey(&gh_iter); + faces_result[i] = f; + } + faces_result[faces_result_len] = NULL; + *r_faces_result_len = faces_result_len; + } + else { + *r_faces_result_len = 0; + } + +finally: + +#ifdef USE_WALKER_REUSE + bm_uuidwalk_clear(w_src); + bm_uuidwalk_clear(w_dst); +#else + bm_uuidwalk_free(w_src); + bm_uuidwalk_free(w_dst); +#endif + + return faces_result; +} + +/** + * Tag as visited, avoid re-use. + */ +static void bm_face_array_visit( + BMFace **faces, const unsigned int faces_len, + unsigned int *r_verts_len, + bool visit_faces) +{ + unsigned int verts_len = 0; + unsigned int i; + for (i = 0; i < faces_len; i++) { + BMFace *f = faces[i]; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (r_verts_len) { + if (!BM_elem_flag_test(l_iter->v, BM_ELEM_TAG)) { + verts_len += 1; + } + } + + BM_elem_flag_enable(l_iter->e, BM_ELEM_TAG); + BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG); + } while ((l_iter = l_iter->next) != l_first); + + if (visit_faces) { + BM_elem_flag_enable(f, BM_ELEM_TAG); + } + } + + if (r_verts_len) { + *r_verts_len = verts_len; + } +} + +#ifdef USE_PIVOT_SEARCH + +/** \name Internal UUIDWalk API + * \{ */ + +/* signed user id */ +typedef intptr_t SUID_Int; + +static bool bm_edge_is_region_boundary(BMEdge *e) +{ + if (e->l->radial_next != e->l) { + BMLoop *l_iter = e->l; + do { + if (!BM_elem_flag_test(l_iter->f, BM_ELEM_TAG)) { + return true; + } + } while ((l_iter = l_iter->radial_next) != e->l); + return false; + } + else { + /* boundary */ + return true; + } +} + +static void bm_face_region_pivot_edge_use_best( + GHash *gh, BMEdge *e_test, + BMEdge **r_e_pivot_best, + SUID_Int e_pivot_best_id[2]) +{ + SUID_Int e_pivot_test_id[2]; + + e_pivot_test_id[0] = (SUID_Int)BLI_ghash_lookup(gh, e_test->v1); + e_pivot_test_id[1] = (SUID_Int)BLI_ghash_lookup(gh, e_test->v2); + if (e_pivot_test_id[0] > e_pivot_test_id[1]) { + SWAP(SUID_Int, e_pivot_test_id[0], e_pivot_test_id[1]); + } + + if ((*r_e_pivot_best == NULL) || + ((e_pivot_best_id[0] != e_pivot_test_id[0]) ? + (e_pivot_best_id[0] < e_pivot_test_id[0]) : + (e_pivot_best_id[1] < e_pivot_test_id[1]))) + { + e_pivot_best_id[0] = e_pivot_test_id[0]; + e_pivot_best_id[1] = e_pivot_test_id[1]; + + /* both verts are from the same pass, record this! */ + *r_e_pivot_best = e_test; + } +} + +/* quick id from a boundary vertex */ +static SUID_Int bm_face_region_vert_boundary_id(BMVert *v) +{ +#define PRIME_VERT_SMALL_A 7 +#define PRIME_VERT_SMALL_B 13 +#define PRIME_VERT_MID_A 103 +#define PRIME_VERT_MID_B 131 + + int tot = 0; + BMIter iter; + BMLoop *l; + SUID_Int id = PRIME_VERT_MID_A; + + BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + const bool is_boundary_vert = (bm_edge_is_region_boundary(l->e) || bm_edge_is_region_boundary(l->prev->e)); + id ^= l->f->len * (is_boundary_vert ? PRIME_VERT_SMALL_A : PRIME_VERT_SMALL_B); + tot += 1; + } + + id ^= (tot * PRIME_VERT_MID_B); + + return id ? ABS(id) : 1; + +#undef PRIME_VERT_SMALL_A +#undef PRIME_VERT_SMALL_B +#undef PRIME_VERT_MID_A +#undef PRIME_VERT_MID_B +} + +/** + * Accumulate id's from a previous pass (swap sign each pass) + */ +static SUID_Int bm_face_region_vert_pass_id(GHash *gh, BMVert *v) +{ + BMIter eiter; + BMEdge *e; + SUID_Int tot = 0; + SUID_Int v_sum_face_len = 0; + SUID_Int v_sum_id = 0; + SUID_Int id; + SUID_Int id_min = INTPTR_MIN + 1; + +#define PRIME_VERT_MID_A 23 +#define PRIME_VERT_MID_B 31 + + BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { + if (BM_elem_flag_test(e, BM_ELEM_TAG)) { + BMVert *v_other = BM_edge_other_vert(e, v); + if (BM_elem_flag_test(v_other, BM_ELEM_TAG)) { + /* non-zero values aren't allowed... so no need to check haskey */ + SUID_Int v_other_id = (SUID_Int)BLI_ghash_lookup(gh, v_other); + if (v_other_id > 0) { + v_sum_id += v_other_id; + tot += 1; + + /* face-count */ + { + BMLoop *l_iter = e->l; + do { + if (BM_elem_flag_test(l_iter->f, BM_ELEM_TAG)) { + v_sum_face_len += l_iter->f->len; + } + } while ((l_iter = l_iter->radial_next) != e->l); + } + } + } + } + } + + id = (tot * PRIME_VERT_MID_A); + id ^= (v_sum_face_len * PRIME_VERT_MID_B); + id ^= v_sum_id; + + /* disallow 0 & min (since it can't be flipped) */ + id = (UNLIKELY(id == 0) ? 1 : UNLIKELY(id < id_min) ? id_min : id); + + return ABS(id); + +#undef PRIME_VERT_MID_A +#undef PRIME_VERT_MID_B +} + +/** + * Take a face region and find the inner-most vertex. + * also calculate the number of connections to the boundary, + * and the total number unique of verts used by this face region. + * + * This is only called once on the source region (no need to be highly optimized). + */ +static BMEdge *bm_face_region_pivot_edge_find( + BMFace **faces_region, unsigned int faces_region_len, + unsigned int verts_region_len, + unsigned int *r_depth) +{ + /* note, keep deterministic where possible (geometry order independent) + * this function assumed all visit faces & edges are tagged */ + + BLI_LINKSTACK_DECLARE(vert_queue_prev, BMVert *); + BLI_LINKSTACK_DECLARE(vert_queue_next, BMVert *); + + GHash *gh = BLI_ghash_ptr_new(__func__); + unsigned int i; + + BMEdge *e_pivot = NULL; + /* pick any non-boundary edge (not ideal) */ + BMEdge *e_pivot_fallback = NULL; + + SUID_Int pass = 0; + + /* total verts in 'gs' we have visited - aka - not v_init_none */ + unsigned int vert_queue_used = 0; + + BLI_LINKSTACK_INIT(vert_queue_prev); + BLI_LINKSTACK_INIT(vert_queue_next); + + /* face-verts */ + for (i = 0; i < faces_region_len; i++) { + BMFace *f = faces_region[i]; + + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BMEdge *e = l_iter->e; + if (bm_edge_is_region_boundary(e)) { + unsigned int j; + for (j = 0; j < 2; j++) { + void **val_p; + if (!BLI_ghash_ensure_p(gh, (&e->v1)[j], &val_p)) { + SUID_Int v_id = bm_face_region_vert_boundary_id((&e->v1)[j]); + *val_p = (void *)v_id; + BLI_LINKSTACK_PUSH(vert_queue_prev, (&e->v1)[j]); + vert_queue_used += 1; + } + } + } + else { + /* use incase (depth == 0), no interior verts */ + e_pivot_fallback = e; + } + } while ((l_iter = l_iter->next) != l_first); + } + + while (BLI_LINKSTACK_SIZE(vert_queue_prev)) { + BMVert *v; + while ((v = BLI_LINKSTACK_POP(vert_queue_prev))) { + BMIter eiter; + BMEdge *e; + BLI_assert(BLI_ghash_haskey(gh, v)); + BLI_assert((SUID_Int)BLI_ghash_lookup(gh, v) > 0); + BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { + if (BM_elem_flag_test(e, BM_ELEM_TAG)) { + BMVert *v_other = BM_edge_other_vert(e, v); + if (BM_elem_flag_test(v_other, BM_ELEM_TAG)) { + void **val_p; + if (!BLI_ghash_ensure_p(gh, v_other, &val_p)) { + /* add as negative, so we know not to read from them this pass */ + const SUID_Int v_id_other = -bm_face_region_vert_pass_id(gh, v_other); + *val_p = (void *)v_id_other; + BLI_LINKSTACK_PUSH(vert_queue_next, v_other); + vert_queue_used += 1; + } + } + } + } + } + + /* flip all the newly added hashes to positive */ + { + LinkNode *v_link; + for (v_link = vert_queue_next; v_link; v_link = v_link->next) { + SUID_Int *v_id_p = (SUID_Int *)BLI_ghash_lookup_p(gh, v_link->link); + *v_id_p = -(*v_id_p); + BLI_assert(*v_id_p > 0); + } + } + + BLI_LINKSTACK_SWAP(vert_queue_prev, vert_queue_next); + pass += 1; + + if (vert_queue_used == verts_region_len) { + break; + } + } + + if (BLI_LINKSTACK_SIZE(vert_queue_prev) >= 2) { + /* common case - we managed to find some interior verts */ + LinkNode *v_link; + BMEdge *e_pivot_best = NULL; + SUID_Int e_pivot_best_id[2] = {0, 0}; + + /* temp untag, so we can quickly know what other verts are in this last pass */ + for (v_link = vert_queue_prev; v_link; v_link = v_link->next) { + BMVert *v = v_link->link; + BM_elem_flag_disable(v, BM_ELEM_TAG); + } + + /* restore correct tagging */ + for (v_link = vert_queue_prev; v_link; v_link = v_link->next) { + BMIter eiter; + BMEdge *e_test; + + BMVert *v = v_link->link; + BM_elem_flag_enable(v, BM_ELEM_TAG); + + BM_ITER_ELEM (e_test, &eiter, v, BM_EDGES_OF_VERT) { + if (BM_elem_flag_test(e_test, BM_ELEM_TAG)) { + BMVert *v_other = BM_edge_other_vert(e_test, v); + if (BM_elem_flag_test(v_other, BM_ELEM_TAG) == false) { + bm_face_region_pivot_edge_use_best(gh, e_test, &e_pivot_best, e_pivot_best_id); + } + } + } + } + + e_pivot = e_pivot_best; + } + + if ((e_pivot == NULL) && BLI_LINKSTACK_SIZE(vert_queue_prev)) { + /* find the best single edge */ + BMEdge *e_pivot_best = NULL; + SUID_Int e_pivot_best_id[2] = {0, 0}; + + LinkNode *v_link; + + /* reduce a pass since we're having to step into a previous passes vert, + * and will be closer to the boundary */ + BLI_assert(pass != 0); + pass -= 1; + + for (v_link = vert_queue_prev; v_link; v_link = v_link->next) { + BMVert *v = v_link->link; + + BMIter eiter; + BMEdge *e_test; + BM_ITER_ELEM (e_test, &eiter, v, BM_EDGES_OF_VERT) { + if (BM_elem_flag_test(e_test, BM_ELEM_TAG)) { + BMVert *v_other = BM_edge_other_vert(e_test, v); + if (BM_elem_flag_test(v_other, BM_ELEM_TAG)) { + bm_face_region_pivot_edge_use_best(gh, e_test, &e_pivot_best, e_pivot_best_id); + } + } + } + } + + e_pivot = e_pivot_best; + } + + BLI_LINKSTACK_FREE(vert_queue_prev); + BLI_LINKSTACK_FREE(vert_queue_next); + + BLI_ghash_free(gh, NULL, NULL); + + if (e_pivot == NULL) { +#ifdef DEBUG_PRINT + printf("%s: using fallback edge!\n", __func__); +#endif + e_pivot = e_pivot_fallback; + pass = 0; + } + + *r_depth = (unsigned int)pass; + + return e_pivot; +} +/** \} */ + +#endif /* USE_PIVOT_SEARCH */ + + +/* -------------------------------------------------------------------- */ +/* Quick UUID pass - identify candidates */ + +#ifdef USE_PIVOT_FASTMATCH + +/** \name Fast Match + * \{ */ + +typedef uintptr_t UUIDFashMatch; + +static UUIDFashMatch bm_vert_fasthash_single(BMVert *v) +{ + BMIter eiter; + BMEdge *e; + UUIDFashMatch e_num = 0, f_num = 0, l_num = 0; + +#define PRIME_EDGE 7 +#define PRIME_FACE 31 +#define PRIME_LOOP 61 + + BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { + if (!BM_edge_is_wire(e)) { + BMLoop *l_iter = e->l; + e_num += 1; + do { + f_num += 1; + l_num += (unsigned int)l_iter->f->len; + } while ((l_iter = l_iter->radial_next) != e->l); + } + } + + return ((e_num * PRIME_EDGE) ^ + (f_num * PRIME_FACE) * + (l_num * PRIME_LOOP)); + +#undef PRIME_EDGE +#undef PRIME_FACE +#undef PRIME_LOOP +} + +static UUIDFashMatch *bm_vert_fasthash_create( + BMesh *bm, const unsigned int depth) +{ + UUIDFashMatch *id_prev; + UUIDFashMatch *id_curr; + unsigned int pass, i; + BMVert *v; + BMIter iter; + + id_prev = MEM_mallocN(sizeof(*id_prev) * (unsigned int)bm->totvert, __func__); + id_curr = MEM_mallocN(sizeof(*id_curr) * (unsigned int)bm->totvert, __func__); + + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + id_prev[i] = bm_vert_fasthash_single(v); + } + + for (pass = 0; pass < depth; pass++) { + BMEdge *e; + + memcpy(id_curr, id_prev, sizeof(*id_prev) * (unsigned int)bm->totvert); + + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + if (BM_edge_is_wire(e) == false) { + const int i1 = BM_elem_index_get(e->v1); + const int i2 = BM_elem_index_get(e->v2); + + id_curr[i1] += id_prev[i2]; + id_curr[i2] += id_prev[i1]; + } + } + } + MEM_freeN(id_prev); + + return id_curr; +} + +static void bm_vert_fasthash_edge_order( + UUIDFashMatch *fm, const BMEdge *e, UUIDFashMatch e_fm[2]) +{ + e_fm[0] = fm[BM_elem_index_get(e->v1)]; + e_fm[1] = fm[BM_elem_index_get(e->v2)]; + + if (e_fm[0] > e_fm[1]) { + SWAP(UUIDFashMatch, e_fm[0], e_fm[1]); + } +} + +static bool bm_vert_fasthash_edge_is_match( + UUIDFashMatch *fm, const BMEdge *e_a, const BMEdge *e_b) +{ + UUIDFashMatch e_a_fm[2]; + UUIDFashMatch e_b_fm[2]; + + bm_vert_fasthash_edge_order(fm, e_a, e_a_fm); + bm_vert_fasthash_edge_order(fm, e_b, e_b_fm); + + return ((e_a_fm[0] == e_b_fm[0]) && + (e_a_fm[1] == e_b_fm[1])); +} + +static void bm_vert_fasthash_destroy( + UUIDFashMatch *fm) +{ + MEM_freeN(fm); +} + +/** \} */ + +#endif /* USE_PIVOT_FASTMATCH */ + + +/** + * Take a face-region and return a list of matching face-regions. + * + * \param faces_region A single, contiguous face-region. + * \return A list of matching null-terminated face-region arrays. + */ +int BM_mesh_region_match( + BMesh *bm, + BMFace **faces_region, unsigned int faces_region_len, + ListBase *r_face_regions) +{ + BMEdge *e_src; + BMEdge *e_dst; + BMIter iter; + unsigned int verts_region_len = 0; + unsigned int faces_result_len = 0; + /* number of steps from e_src to a boundary vert */ + unsigned int depth; + + +#ifdef USE_WALKER_REUSE + UUIDWalk w_src, w_dst; +#endif + +#ifdef USE_PIVOT_FASTMATCH + UUIDFashMatch *fm; +#endif + +#ifdef DEBUG_PRINT + int search_num = 0; +#endif + +#ifdef DEBUG_TIME + TIMEIT_START(region_match); +#endif + + /* initialize visited verts */ + BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); + bm_face_array_visit(faces_region, faces_region_len, &verts_region_len, true); + + /* needed for 'ghashutil_bmelem_indexhash' */ + BM_mesh_elem_index_ensure(bm, BM_VERT | BM_FACE); + +#ifdef USE_PIVOT_SEARCH + e_src = bm_face_region_pivot_edge_find( + faces_region, faces_region_len, + verts_region_len, &depth); + + /* see which edge is added */ +#if 0 + BM_select_history_clear(bm); + if (e_src) { + BM_select_history_store(bm, e_src); + } +#endif + +#else + /* quick test only! */ + e_src = BM_mesh_active_edge_get(bm); +#endif + + if (e_src == NULL) { +#ifdef DEBUG_PRINT + printf("Couldn't find 'e_src'"); +#endif + return 0; + } + + BLI_listbase_clear(r_face_regions); + +#ifdef USE_PIVOT_FASTMATCH + if (depth > 0) { + fm = bm_vert_fasthash_create(bm, depth); + } + else { + fm = NULL; + } +#endif + +#ifdef USE_WALKER_REUSE + bm_uuidwalk_init(&w_src, faces_region_len, verts_region_len); + bm_uuidwalk_init(&w_dst, faces_region_len, verts_region_len); +#endif + + BM_ITER_MESH (e_dst, &iter, bm, BM_EDGES_OF_MESH) { + BMFace **faces_result; + unsigned int faces_result_len_out; + + if (BM_elem_flag_test(e_dst, BM_ELEM_TAG) || BM_edge_is_wire(e_dst)) { + continue; + } + +#ifdef USE_PIVOT_FASTMATCH + if (fm && !bm_vert_fasthash_edge_is_match(fm, e_src, e_dst)) { + continue; + } +#endif + +#ifdef DEBUG_PRINT + search_num += 1; +#endif + + faces_result = bm_mesh_region_match_pair( +#ifdef USE_WALKER_REUSE + &w_src, &w_dst, +#endif + e_src, e_dst, + faces_region_len, + verts_region_len, + &faces_result_len_out); + + /* tag verts as visited */ + if (faces_result) { + LinkData *link; + + bm_face_array_visit(faces_result, faces_result_len_out, NULL, false); + + link = BLI_genericNodeN(faces_result); + BLI_addtail(r_face_regions, link); + faces_result_len += 1; + } + } + +#ifdef USE_WALKER_REUSE + bm_uuidwalk_free(&w_src); + bm_uuidwalk_free(&w_dst); +#else + (void)bm_uuidwalk_clear; +#endif + +#ifdef USE_PIVOT_FASTMATCH + if (fm) { + bm_vert_fasthash_destroy(fm); + } +#endif + +#ifdef DEBUG_PRINT + printf("%s: search: %d, found %d\n", __func__, search_num, faces_result_len); +#endif + +#ifdef DEBUG_TIME + TIMEIT_END(region_match); +#endif + + return (int)faces_result_len; +} diff --git a/source/blender/bmesh/tools/bmesh_region_match.h b/source/blender/bmesh/tools/bmesh_region_match.h new file mode 100644 index 00000000000..edf8369b070 --- /dev/null +++ b/source/blender/bmesh/tools/bmesh_region_match.h @@ -0,0 +1,33 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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 2 + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_REGION_MATCH_H__ +#define __BMESH_REGION_MATCH_H__ + +/** \file blender/bmesh/tools/bmesh_region_match.h + * \ingroup bmesh + */ + +int BM_mesh_region_match( + BMesh *bm, + BMFace **faces_region, unsigned int faces_region_len, + ListBase *r_face_regions); + +#endif /* __BMESH_REGION_MATCH_H__ */ diff --git a/source/blender/bmesh/tools/bmesh_triangulate.c b/source/blender/bmesh/tools/bmesh_triangulate.c index 446c03a543f..6f2aaf28179 100644 --- a/source/blender/bmesh/tools/bmesh_triangulate.c +++ b/source/blender/bmesh/tools/bmesh_triangulate.c @@ -27,13 +27,19 @@ * */ +#include "DNA_modifier_types.h" /* for MOD_TRIANGULATE_NGON_BEAUTY only */ + #include "MEM_guardedalloc.h" #include "BLI_utildefines.h" #include "BLI_alloca.h" #include "BLI_memarena.h" -#include "BLI_listbase.h" -#include "BLI_scanfill.h" +#include "BLI_heap.h" +#include "BLI_edgehash.h" + +/* only for defines */ +#include "BLI_polyfill2d.h" +#include "BLI_polyfill2d_beautify.h" #include "bmesh.h" @@ -42,16 +48,27 @@ /** * a version of #BM_face_triangulate that maps to #BMOpSlot */ -static void bm_face_triangulate_mapping(BMesh *bm, BMFace *face, MemArena *sf_arena, - const int quad_method, const int ngon_method, - const bool use_tag, - BMOperator *op, BMOpSlot *slot_facemap_out) +static void bm_face_triangulate_mapping( + BMesh *bm, BMFace *face, + const int quad_method, const int ngon_method, + const bool use_tag, + BMOperator *op, BMOpSlot *slot_facemap_out, + + MemArena *pf_arena, + /* use for MOD_TRIANGULATE_NGON_BEAUTY only! */ + struct Heap *pf_heap, struct EdgeHash *pf_ehash) { int faces_array_tot = face->len - 3; BMFace **faces_array = BLI_array_alloca(faces_array, faces_array_tot); BLI_assert(face->len > 3); - BM_face_triangulate(bm, face, faces_array, &faces_array_tot, sf_arena, quad_method, ngon_method, use_tag); + BM_face_triangulate( + bm, face, + faces_array, &faces_array_tot, + NULL, NULL, + quad_method, ngon_method, use_tag, + pf_arena, + pf_heap, pf_ehash); if (faces_array_tot) { int i; @@ -63,22 +80,39 @@ static void bm_face_triangulate_mapping(BMesh *bm, BMFace *face, MemArena *sf_ar } -void BM_mesh_triangulate(BMesh *bm, const int quad_method, const int ngon_method, const bool tag_only, - BMOperator *op, BMOpSlot *slot_facemap_out) +void BM_mesh_triangulate( + BMesh *bm, const int quad_method, const int ngon_method, const bool tag_only, + BMOperator *op, BMOpSlot *slot_facemap_out) { BMIter iter; BMFace *face; - MemArena *sf_arena; + MemArena *pf_arena; + Heap *pf_heap; + EdgeHash *pf_ehash; - sf_arena = BLI_memarena_new(BLI_SCANFILL_ARENA_SIZE, __func__); + pf_arena = BLI_memarena_new(BLI_POLYFILL_ARENA_SIZE, __func__); + + if (ngon_method == MOD_TRIANGULATE_NGON_BEAUTY) { + pf_heap = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE); + pf_ehash = BLI_edgehash_new_ex(__func__, BLI_POLYFILL_ALLOC_NGON_RESERVE); + } + else { + pf_heap = NULL; + pf_ehash = NULL; + } if (slot_facemap_out) { /* same as below but call: bm_face_triangulate_mapping() */ BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) { if (face->len > 3) { if (tag_only == false || BM_elem_flag_test(face, BM_ELEM_TAG)) { - bm_face_triangulate_mapping(bm, face, sf_arena, quad_method, ngon_method, tag_only, - op, slot_facemap_out); + bm_face_triangulate_mapping( + bm, face, quad_method, + ngon_method, tag_only, + op, slot_facemap_out, + + pf_arena, + pf_heap, pf_ehash); } } } @@ -87,11 +121,22 @@ void BM_mesh_triangulate(BMesh *bm, const int quad_method, const int ngon_method BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) { if (face->len > 3) { if (tag_only == false || BM_elem_flag_test(face, BM_ELEM_TAG)) { - BM_face_triangulate(bm, face, NULL, NULL, sf_arena, quad_method, ngon_method, tag_only); + BM_face_triangulate( + bm, face, + NULL, NULL, + NULL, NULL, + quad_method, ngon_method, tag_only, + pf_arena, + pf_heap, pf_ehash); } } } } - BLI_memarena_free(sf_arena); + BLI_memarena_free(pf_arena); + + if (ngon_method == MOD_TRIANGULATE_NGON_BEAUTY) { + BLI_heap_free(pf_heap, NULL); + BLI_edgehash_free(pf_ehash, NULL); + } } diff --git a/source/blender/bmesh/tools/bmesh_triangulate.h b/source/blender/bmesh/tools/bmesh_triangulate.h index 550109ffef9..c6a5e04dfb2 100644 --- a/source/blender/bmesh/tools/bmesh_triangulate.h +++ b/source/blender/bmesh/tools/bmesh_triangulate.h @@ -30,7 +30,8 @@ #ifndef __BMESH_TRIANGULATE_H__ #define __BMESH_TRIANGULATE_H__ -void BM_mesh_triangulate(BMesh *bm, const int quad_method, const int ngon_method, const bool tag_only, - BMOperator *op, BMOpSlot *slot_facemap_out); +void BM_mesh_triangulate( + BMesh *bm, const int quad_method, const int ngon_method, const bool tag_only, + BMOperator *op, BMOpSlot *slot_facemap_out); #endif /* __BMESH_TRIANGULATE_H__ */ diff --git a/source/blender/bmesh/tools/bmesh_wireframe.c b/source/blender/bmesh/tools/bmesh_wireframe.c index 79fea3e5da1..e79ef52797b 100644 --- a/source/blender/bmesh/tools/bmesh_wireframe.c +++ b/source/blender/bmesh/tools/bmesh_wireframe.c @@ -55,8 +55,9 @@ static BMLoop *bm_edge_tag_faceloop(BMEdge *e) return NULL; } -static void bm_vert_boundary_tangent(BMVert *v, float r_no[3], float r_no_face[3], - BMVert **r_va_other, BMVert **r_vb_other) +static void bm_vert_boundary_tangent( + BMVert *v, float r_no[3], float r_no_face[3], + BMVert **r_va_other, BMVert **r_vb_other) { BMIter iter; BMEdge *e_iter; @@ -159,7 +160,7 @@ static bool bm_loop_is_radial_boundary(BMLoop *l_first) } /** - * \param def_nr -1 for no vertex groups. + * \param defgrp_index: Vertex group index, -1 for no vertex groups. * * \note All edge tags must be cleared. * \note Behavior matches MOD_solidify.c |