diff options
Diffstat (limited to 'source/blender/bmesh')
82 files changed, 5294 insertions, 1607 deletions
diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 80adb595ac9..6002b414779 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -114,6 +114,10 @@ set(SRC intern/bmesh_queries.c intern/bmesh_queries.h intern/bmesh_queries_inline.h + intern/bmesh_strands.c + intern/bmesh_strands.h + intern/bmesh_strands_conv.c + intern/bmesh_strands_conv.h intern/bmesh_structure.c intern/bmesh_structure.h intern/bmesh_structure_inline.h diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h index 87b1818fa5d..7ba700d17a2 100644 --- a/source/blender/bmesh/bmesh.h +++ b/source/blender/bmesh/bmesh.h @@ -256,6 +256,8 @@ extern "C" { #include "intern/bmesh_mesh_validate.h" #include "intern/bmesh_mods.h" #include "intern/bmesh_operators.h" +#include "intern/bmesh_strands.h" +#include "intern/bmesh_strands_conv.h" #include "intern/bmesh_polygon.h" #include "intern/bmesh_queries.h" #include "intern/bmesh_walkers.h" diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h index 120ff4997dc..058fec548a3 100644 --- a/source/blender/bmesh/bmesh_class.h +++ b/source/blender/bmesh/bmesh_class.h @@ -256,11 +256,36 @@ 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, void *, BMFace *, BMEdge *, BMVert *, BMLoop *, BMElem *, BMElemF *, BMHeader *) + CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_ELEM_NONCONST, _BM_GENERIC_TYPE_ELEM_CONST) +#ifndef __cplusplus #define BM_CHECK_TYPE_ELEM_ASSIGN(ele) \ - (BM_CHECK_TYPE_ELEM(ele), CHECK_TYPE_NONCONST(ele)), ele + (BM_CHECK_TYPE_ELEM(ele)), ele +#else +/* for C++: cast the lhs to a void*, + * because C++ does not allow implicit void* casting of the rhs + */ +#define BM_CHECK_TYPE_ELEM_ASSIGN(ele) \ + (BM_CHECK_TYPE_ELEM(ele), CHECK_TYPE_NONCONST(ele)), *(void**)(&(ele)) +#endif /* BMHeader->hflag (char) */ enum { @@ -297,8 +322,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/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 9e0807710fc..20a88b0e17c 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -31,7 +31,7 @@ #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" @@ -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; } } @@ -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,7 +2011,8 @@ 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 * @@ -2000,49 +2020,42 @@ bool BM_vert_splice_check_double(BMVert *v_a, BMVert *v_b) * 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)) { + BLI_assert(BM_vert_pair_share_face_check(v_src, v_dst) == false); - /* 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); - } - - /* 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))); + } while ((e = BLI_SMALLSTACK_POP(edges_search))); - maxindex++; - } - - /* 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; } @@ -2299,72 +2398,245 @@ void bmesh_edge_separate(BMesh *bm, BMEdge *e, BMLoop *l_sep, */ 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 (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 boundaris. + */ +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 aa1f511e8d7..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; 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 f745972293e..81155bc017e 100644 --- a/source/blender/bmesh/intern/bmesh_interp.c +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -241,8 +241,9 @@ void BM_face_interp_from_face(BMesh *bm, BMFace *f_dst, const BMFace *f_src, con * 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]; @@ -302,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}; @@ -340,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); @@ -358,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; @@ -392,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]; @@ -405,8 +410,9 @@ static float bm_loop_flip_equotion(float mat[2][2], float b[2], const float targ return mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0]; } -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]; @@ -453,8 +459,8 @@ static void bm_loop_interp_mdisps(BMesh *bm, BMLoop *l_dst, const 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) { @@ -905,6 +911,34 @@ void BM_elem_float_data_set(CustomData *cd, void *element, int type, const float if (f) *f = val; } +float BM_elem_float_data_named_get(CustomData *cd, void *element, int type, const char *name) +{ + const float *f = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name); + return f ? *f : 0.0f; +} + +void BM_elem_float_data_named_set(CustomData *cd, void *element, int type, const char *name, const float val) +{ + float *f = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name); + if (f) *f = val; +} + +void BM_elem_meshsample_data_named_get(CustomData *cd, void *element, int type, const char *name, MSurfaceSample *val) +{ + const MSurfaceSample *s = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name); + if (s) + memcpy(val, s, sizeof(MSurfaceSample)); + else + memset(val, 0, sizeof(MSurfaceSample)); +} + +void BM_elem_meshsample_data_named_set(CustomData *cd, void *element, int type, const char *name, const MSurfaceSample *val) +{ + MSurfaceSample *s = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name); + if (s) + memcpy(s, val, sizeof(MSurfaceSample)); +} + /** \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. @@ -1051,7 +1085,7 @@ LinkNode *BM_vert_loop_groups_data_layer_create( mul_vn_fl(lf->data_weights, lf->data_len, 1.0f / lwc.weight_accum); } else { - fill_vn_fl(lf->data_weights, lf->data_len, 1.0f / (float)lf->data_len); + copy_vn_fl(lf->data_weights, lf->data_len, 1.0f / (float)lf->data_len); } BLI_linklist_prepend_arena(&groups, lf, lwc.arena); diff --git a/source/blender/bmesh/intern/bmesh_interp.h b/source/blender/bmesh/intern/bmesh_interp.h index 969e92f37db..6168a655c93 100644 --- a/source/blender/bmesh/intern/bmesh_interp.h +++ b/source/blender/bmesh/intern/bmesh_interp.h @@ -29,6 +29,8 @@ struct LinkNode; struct MemArena; +struct MSurfaceSample; + 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); @@ -44,6 +46,10 @@ 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); +float BM_elem_float_data_named_get(CustomData *cd, void *element, int type, const char *name); +void BM_elem_float_data_named_set(CustomData *cd, void *element, int type, const char *name, const float val); +void BM_elem_meshsample_data_named_get(CustomData *cd, void *element, int type, const char *name, struct MSurfaceSample *val); +void BM_elem_meshsample_data_named_set(CustomData *cd, void *element, int type, const char *name, const struct MSurfaceSample *val); void BM_face_interp_from_face_ex( BMesh *bm, BMFace *f_dst, const BMFace *f_src, const bool do_vertex, diff --git a/source/blender/bmesh/intern/bmesh_iterators.c b/source/blender/bmesh/intern/bmesh_iterators.c index 4dc27d75a55..0abf41709a0 100644 --- a/source/blender/bmesh/intern/bmesh_iterators.c +++ b/source/blender/bmesh/intern/bmesh_iterators.c @@ -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 49e511bdcb5..c4b184ef8b8 100644 --- a/source/blender/bmesh/intern/bmesh_iterators.h +++ b/source/blender/bmesh/intern/bmesh_iterators.h @@ -197,14 +197,17 @@ typedef struct BMIter { 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); diff --git a/source/blender/bmesh/intern/bmesh_iterators_inline.h b/source/blender/bmesh/intern/bmesh_iterators_inline.h index d3e18b97acb..43b054d7845 100644 --- a/source/blender/bmesh/intern/bmesh_iterators_inline.h +++ b/source/blender/bmesh/intern/bmesh_iterators_inline.h @@ -29,6 +29,8 @@ #ifndef __BMESH_ITERATORS_INLINE_H__ #define __BMESH_ITERATORS_INLINE_H__ +#include "BLI_mempool.h" + /* inline here optimizes out the switch statement when called with * constant values (which is very common), nicer for loop-in-loop situations */ @@ -37,12 +39,12 @@ * * 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); } - /** * \brief Iterator Init * @@ -50,6 +52,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 +172,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 158c2aa4263..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); @@ -476,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); @@ -588,8 +593,8 @@ int BM_log_length(const BMLog *log) /* 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; @@ -597,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); @@ -836,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; } } @@ -987,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); @@ -1071,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 8aa64906019..17b6d1d99e7 100644 --- a/source/blender/bmesh/intern/bmesh_marking.c +++ b/source/blender/bmesh/intern/bmesh_marking.c @@ -73,9 +73,9 @@ static void recount_totsels(BMesh *bm) /** \name BMesh helper functions for selection flushing. * \{ */ -static bool bm_vert_is_edge_select_any_other(BMVert *v, BMEdge *e_first) +static bool bm_vert_is_edge_select_any_other(const BMVert *v, const BMEdge *e_first) { - BMEdge *e_iter = 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) { @@ -87,10 +87,10 @@ static bool bm_vert_is_edge_select_any_other(BMVert *v, BMEdge *e_first) } #if 0 -static bool bm_vert_is_edge_select_any(BMVert *v) +static bool bm_vert_is_edge_select_any(const BMVert *v) { if (v->e) { - BMEdge *e_iter, *e_first; + const BMEdge *e_iter, *e_first; e_iter = e_first = v->e; do { if (BM_elem_flag_test(e_iter, BM_ELEM_SELECT)) { @@ -104,7 +104,7 @@ static bool bm_vert_is_edge_select_any(BMVert *v) static bool bm_edge_is_face_select_any_other(BMLoop *l_first) { - BMLoop *l_iter = l_first; + const BMLoop *l_iter = l_first; /* start by stepping over the current face */ while ((l_iter = l_iter->radial_next) != l_first) { @@ -116,10 +116,10 @@ static bool bm_edge_is_face_select_any_other(BMLoop *l_first) } #if 0 -static bool bm_edge_is_face_select_any(BMEdge *e) +static bool bm_edge_is_face_select_any(const BMEdge *e) { if (e->l) { - BMLoop *l_iter, *l_first; + const BMLoop *l_iter, *l_first; l_iter = l_first = e->l; do { if (BM_elem_flag_test(l_iter->f, BM_ELEM_SELECT)) { @@ -626,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; @@ -913,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)) { @@ -920,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) { @@ -1013,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, @@ -1084,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, @@ -1139,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 15f972c6435..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*/ @@ -91,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) @@ -98,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 9a2869b64ef..2c4a98b0b7e 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.c +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -303,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); @@ -437,8 +438,9 @@ 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, viter; BMVert *v; @@ -1131,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, @@ -1379,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. @@ -1459,7 +1497,7 @@ 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); } bm->elem_index_dirty |= BM_VERT; bm->elem_table_dirty |= BM_VERT; @@ -1490,7 +1528,7 @@ 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);*/ } bm->elem_index_dirty |= BM_EDGE; @@ -1522,7 +1560,7 @@ 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); } bm->elem_index_dirty |= BM_FACE | BM_LOOP; diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index bac5da8347e..b157237c7d0 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -49,8 +49,9 @@ 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); @@ -68,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 3630bb78b8a..a0ef12c28db 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_conv.c +++ b/source/blender/bmesh/intern/bmesh_mesh_conv.c @@ -98,6 +98,9 @@ #include "bmesh.h" #include "intern/bmesh_private.h" /* for element checking */ +/* XXX stupid hack: linker otherwise strips bmesh_strands_conv.c because it is not used inside bmesh */ +void *__dummy_hack__ = &BM_strands_count_psys_keys; + /** * Currently this is only used for Python scripts * which may fail to keep matching UV/TexFace layers. @@ -200,8 +203,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 +225,20 @@ 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) +{ + BM_mesh_bm_from_me_ex(bm, me, CD_MASK_BMESH, calc_face_normal, set_key, act_key_nr); +} + +/** + * \brief Mesh -> BMesh + * + * \warning This function doesn't calculate face normals. + */ +void BM_mesh_bm_from_me_ex(BMesh *bm, Mesh *me, CustomDataMask mask, + const bool calc_face_normal, const bool set_key, int act_key_nr) { MVert *mvert; MEdge *medge; @@ -249,10 +265,10 @@ void BM_mesh_bm_from_me(BMesh *bm, Mesh *me, if (!me || !me->totvert) { if (me) { /*no verts? still copy customdata layout*/ - CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_ASSIGN, 0); - CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_ASSIGN, 0); - CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_ASSIGN, 0); - CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_ASSIGN, 0); + CustomData_copy(&me->vdata, &bm->vdata, mask, CD_ASSIGN, 0); + CustomData_copy(&me->edata, &bm->edata, mask, CD_ASSIGN, 0); + CustomData_copy(&me->ldata, &bm->ldata, mask, CD_ASSIGN, 0); + CustomData_copy(&me->pdata, &bm->pdata, mask, CD_ASSIGN, 0); CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT); CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE); @@ -264,10 +280,10 @@ void BM_mesh_bm_from_me(BMesh *bm, Mesh *me, vtable = MEM_mallocN(sizeof(void **) * me->totvert, "mesh to bmesh vtable"); - CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0); - CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0); - CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_CALLOC, 0); - CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&me->vdata, &bm->vdata, mask, CD_CALLOC, 0); + CustomData_copy(&me->edata, &bm->edata, mask, CD_CALLOC, 0); + CustomData_copy(&me->ldata, &bm->ldata, mask, CD_CALLOC, 0); + CustomData_copy(&me->pdata, &bm->pdata, mask, CD_CALLOC, 0); /* make sure uv layer names are consisten */ totuv = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); @@ -568,6 +584,11 @@ BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e) void BM_mesh_bm_to_me(BMesh *bm, Mesh *me, bool do_tessface) { + BM_mesh_bm_to_me_ex(bm, me, CD_MASK_MESH, do_tessface); +} + +void BM_mesh_bm_to_me_ex(BMesh *bm, Mesh *me, CustomDataMask mask, bool do_tessface) +{ MLoop *mloop; MPoly *mpoly; MVert *mvert, *oldverts; @@ -627,10 +648,10 @@ void BM_mesh_bm_to_me(BMesh *bm, Mesh *me, bool do_tessface) me->totface = 0; me->act_face = -1; - CustomData_copy(&bm->vdata, &me->vdata, CD_MASK_MESH, CD_CALLOC, me->totvert); - CustomData_copy(&bm->edata, &me->edata, CD_MASK_MESH, CD_CALLOC, me->totedge); - CustomData_copy(&bm->ldata, &me->ldata, CD_MASK_MESH, CD_CALLOC, me->totloop); - CustomData_copy(&bm->pdata, &me->pdata, CD_MASK_MESH, CD_CALLOC, me->totpoly); + CustomData_copy(&bm->vdata, &me->vdata, mask, CD_CALLOC, me->totvert); + CustomData_copy(&bm->edata, &me->edata, mask, CD_CALLOC, me->totedge); + CustomData_copy(&bm->ldata, &me->ldata, mask, CD_CALLOC, me->totloop); + CustomData_copy(&bm->pdata, &me->pdata, mask, CD_CALLOC, me->totpoly); CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, mvert, me->totvert); CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, me->totedge); diff --git a/source/blender/bmesh/intern/bmesh_mesh_conv.h b/source/blender/bmesh/intern/bmesh_mesh_conv.h index ab9d7a0ccf3..0d235a128ab 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_conv.h +++ b/source/blender/bmesh/intern/bmesh_mesh_conv.h @@ -32,15 +32,22 @@ * \ingroup bmesh */ +#include "BLI_sys_types.h" + struct Mesh; +typedef uint64_t CustomDataMask; void BM_mesh_cd_validate(BMesh *bm); 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, +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_ex(BMesh *bm, struct Mesh *me, CustomDataMask mask, 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); +void BM_mesh_bm_to_me_ex(BMesh *bm, struct Mesh *me, CustomDataMask mask, 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 7b3f64dc5cd..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); } @@ -269,16 +270,17 @@ BMFace *BM_faces_join_pair(BMesh *bm, BMFace *f_a, BMFace *f_b, BMEdge *e, const * 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; @@ -356,10 +358,11 @@ BMFace *BM_face_split(BMesh *bm, BMFace *f, * 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; @@ -990,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); @@ -1103,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 */ @@ -1353,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; @@ -1516,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 @@ -1643,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 d0679b9919a..ce988c6c6dd 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -359,6 +359,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 */ @@ -486,17 +487,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 */ diff --git a/source/blender/bmesh/intern/bmesh_operator_api.h b/source/blender/bmesh/intern/bmesh_operator_api.h index d966d882c67..3f7fb7b257d 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,62 @@ 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); @@ -405,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 @@ -466,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); @@ -483,9 +501,6 @@ bool BMO_iter_map_value_bool(BMOIter *iter); ele; \ BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BMO_iter_step(iter)) -/******************* Inlined Functions********************/ -typedef void (*opexec)(BMesh *bm, BMOperator *op); - extern const int BMO_OPSLOT_TYPEINFO[BMO_OP_SLOT_TOTAL_TYPES]; int BMO_opcode_from_opname(const char *opname); 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 ba154b04838..dda1f2fe30a 100644 --- a/source/blender/bmesh/intern/bmesh_operators.c +++ b/source/blender/bmesh/intern/bmesh_operators.c @@ -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); } @@ -927,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); @@ -964,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; @@ -1026,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); } @@ -1046,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; @@ -1082,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; @@ -1116,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; @@ -1140,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; @@ -1394,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); diff --git a/source/blender/bmesh/intern/bmesh_operators.h b/source/blender/bmesh/intern/bmesh_operators.h index f39fe29b596..d9961e589da 100644 --- a/source/blender/bmesh/intern/bmesh_operators.h +++ b/source/blender/bmesh/intern/bmesh_operators.h @@ -46,6 +46,7 @@ enum { SUBD_FALLOFF_ROOT, SUBD_FALLOFF_SHARP, SUBD_FALLOFF_LIN, + SUBD_FALLOFF_INVSQUARE = 7, /* matching PROP_INVSQUARE */ }; enum { @@ -130,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 979f7d2640a..8b9c25bf95c 100644 --- a/source/blender/bmesh/intern/bmesh_operators_private.h +++ b/source/blender/bmesh/intern/bmesh_operators_private.h @@ -54,6 +54,7 @@ void bmo_create_icosphere_exec(BMesh *bm, BMOperator *op); void bmo_create_monkey_exec(BMesh *bm, BMOperator *op); void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op); void bmo_create_vert_exec(BMesh *bm, BMOperator *op); +//void bmo_create_strand_exec(BMesh *bm, BMOperator *op); void bmo_delete_exec(BMesh *bm, BMOperator *op); void bmo_dissolve_edges_exec(BMesh *bm, BMOperator *op); void bmo_dissolve_faces_exec(BMesh *bm, BMOperator *op); diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c index bc06ba2c9b1..d2d31d6a562 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.c +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -102,8 +102,9 @@ static float bm_face_calc_poly_normal(const BMFace *f, float n[3]) * Same as #calc_poly_normal and #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; @@ -127,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; @@ -548,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; @@ -607,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); diff --git a/source/blender/bmesh/intern/bmesh_polygon.h b/source/blender/bmesh/intern/bmesh_polygon.h index 9980b59a298..582b4248c7d 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.h +++ b/source/blender/bmesh/intern/bmesh_polygon.h @@ -36,16 +36,18 @@ void BM_bmesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_loopt 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(); 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 c3be768aa09..182bd17c486 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" @@ -255,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]; @@ -501,10 +533,10 @@ bool BM_verts_in_face(BMVert **varr, int len, BMFace *f) /** * 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 { @@ -750,6 +782,11 @@ int BM_vert_edge_count(const BMVert *v) return bmesh_disk_count(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; @@ -770,13 +807,30 @@ 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); } @@ -792,6 +846,21 @@ 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) @@ -824,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 */ @@ -834,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 { - l = l->radial_next; + /* 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); - if (count < len) { - /* vert shared by multiple regions */ - return false; + 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 { + BLI_assert(0); + } + } while ((l_iter = l_iter->radial_next) != l_first); + + 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; } @@ -902,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) { @@ -1147,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 */ @@ -1183,7 +1400,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, @@ -1198,7 +1415,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, @@ -1220,7 +1437,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]; @@ -1244,7 +1461,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]; @@ -1356,7 +1573,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_ex(BMVert *v, const float fallback) +float BM_vert_calc_edge_angle_ex(const BMVert *v, const float fallback) { BMEdge *e1, *e2; @@ -1378,7 +1595,7 @@ float BM_vert_calc_edge_angle_ex(BMVert *v, const float fallback) } } -float BM_vert_calc_edge_angle(BMVert *v) +float BM_vert_calc_edge_angle(const BMVert *v) { return BM_vert_calc_edge_angle_ex(v, DEG2RADF(90.0f)); } @@ -1387,14 +1604,14 @@ float BM_vert_calc_edge_angle(BMVert *v) * \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; @@ -1409,15 +1626,15 @@ 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 float no[3], 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(no, l->f->no) * face_angle; @@ -1446,15 +1663,15 @@ float BM_vert_calc_shell_factor_ex(BMVert *v, const float no[3], const char hfla * \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); } @@ -1600,85 +1817,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(varr, len, f)) { - 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); - } - } + if (varr[0]->e) { + BMEdge *e_iter, *e_first; + e_iter = e_first = varr[0]->e; - is_found = true; - - { - BMLoop *l_iter; - - /* 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; } @@ -1773,8 +1964,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) { @@ -2117,9 +2308,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; @@ -2274,9 +2466,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; @@ -2301,7 +2494,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) { @@ -2405,6 +2598,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 4ee5588ba0b..f96d99fd452 100644 --- a/source/blender/bmesh/intern/bmesh_queries.h +++ b/source/blender/bmesh/intern/bmesh_queries.h @@ -31,7 +31,7 @@ bool BM_vert_in_face(BMVert *v, BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNU 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(); @@ -64,32 +64,54 @@ BMFace *BM_vert_pair_share_face_by_angle( BMLoop **r_l_a, BMLoop **r_l_b, const bool allow_adjacent) 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(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(); -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(); @@ -97,11 +119,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_edge_angle_ex(BMVert *v, const float fallback) 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 float no[3], 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(); @@ -132,8 +154,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(); @@ -147,12 +170,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 1ca56beb746..430ba10fb42 100644 --- a/source/blender/bmesh/intern/bmesh_queries_inline.h +++ b/source/blender/bmesh/intern/bmesh_queries_inline.h @@ -30,6 +30,7 @@ * 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)); @@ -38,6 +39,7 @@ BLI_INLINE bool BM_vert_in_edge(const BMEdge *e, const BMVert *v) /** * 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_strands.c b/source/blender/bmesh/intern/bmesh_strands.c new file mode 100644 index 00000000000..ae76c4761ad --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_strands.c @@ -0,0 +1,145 @@ +/* + * ***** 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): Lukas Toenne. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_strands.c + * \ingroup bmesh + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_math.h" +#include "BLI_mempool.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +/* + * STRANDS OF MESH CALLBACKS + */ + +void bmstranditer__strands_of_mesh_begin(struct BMIter__elem_of_mesh *iter) +{ + BLI_mempool_iternew(iter->pooliter.pool, &iter->pooliter); +} + +void *bmstranditer__strands_of_mesh_step(struct BMIter__elem_of_mesh *iter) +{ + BMVert *v; + + do { + v = BLI_mempool_iterstep(&iter->pooliter); + } while (v && !BM_strands_vert_is_root(v)); + + return v; +} + +/* + * VERTS OF STRAND CALLBACKS + */ + +/* BMIter__vert_of_strand is not included in the union in BMIter, just make sure it is big enough */ +BLI_STATIC_ASSERT(sizeof(BMIter__vert_of_strand) <= sizeof(BMIter), "BMIter must be at least as large as BMIter__vert_of_strand") + +void bmstranditer__verts_of_strand_begin(struct BMIter__vert_of_strand *iter) +{ + iter->e_next = iter->v_next->e; +} + +void *bmstranditer__verts_of_strand_step(struct BMIter__vert_of_strand *iter) +{ + BMVert *v_curr = iter->v_next; + + if (iter->e_next) { + BMEdge *e_first = iter->e_next; + + /* select the other vertex of the current edge */ + iter->v_next = (iter->v_next == iter->e_next->v1 ? iter->e_next->v2 : iter->e_next->v1); + + /* select the next edge of the current vertex */ + iter->e_next = bmesh_disk_edge_next(iter->e_next, iter->v_next); + if (iter->e_next == e_first) { + /* only one edge means the last segment, terminate */ + iter->e_next = NULL; + } + } + else + iter->v_next = NULL; /* last vertex, terminate */ + + return v_curr; +} + +/* ------------------------------------------------------------------------- */ + +int BM_strands_count(BMesh *bm) +{ + BMVert *v; + BMIter iter; + + int count = 0; + BM_ITER_STRANDS(v, &iter, bm, BM_STRANDS_OF_MESH) { + ++count; + } + + return count; +} + +int BM_strands_keys_count(BMVert *root) +{ + BMVert *v; + BMIter iter; + + int count = 0; + BM_ITER_STRANDS_ELEM(v, &iter, root, BM_VERTS_OF_STRAND) { + ++count; + } + + return count; +} + +/* ------------------------------------------------------------------------- */ + +/* Create a new strand */ +BMVert *BM_strands_create(BMesh *bm, int len, bool set_defaults) +{ + float co[3] = {0.0f, 0.0f, 0.0f}; + + BMVert *root, *v = NULL, *vprev; + int k; + + for (k = 0; k < len; ++k) { + vprev = v; + v = BM_vert_create(bm, co, NULL, set_defaults ? BM_CREATE_NOP : BM_CREATE_SKIP_CD); + + zero_v3(v->no); + + /* root */ + if (k == 0) { + root = v; + } + else { + /*BMEdge *e =*/ BM_edge_create(bm, vprev, v, NULL, set_defaults ? BM_CREATE_NOP : BM_CREATE_SKIP_CD); + } + } + + return root; +} diff --git a/source/blender/bmesh/intern/bmesh_strands.h b/source/blender/bmesh/intern/bmesh_strands.h new file mode 100644 index 00000000000..cd4267f0bb3 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_strands.h @@ -0,0 +1,215 @@ +/* + * ***** 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): Lukas Toenne. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_STRANDS_H__ +#define __BMESH_STRANDS_H__ + +/** \file blender/bmesh/intern/bmesh_strands.h + * \ingroup bmesh + */ + +#include "BLI_utildefines.h" + +#include "bmesh.h" +#include "bmesh_queries.h" +#include "bmesh_structure.h" + +/* True if v is the root of a strand */ +BLI_INLINE bool BM_strands_vert_is_root(BMVert *v) +{ + BMEdge *e_first = v->e; + BMEdge *e_next; + + if (!e_first) + return true; /* single vertex is both root and tip */ + e_next = bmesh_disk_edge_next(e_first, v); + + /* with a single edge, the vertex is either first or last of the curve; + * first vertex is defined as the root + */ + if (e_next == e_first) { + if (e_first->v1 == v) + return true; + } + return false; +} + +/* True if v is the tip of a strand */ +BLI_INLINE bool BM_strands_vert_is_tip(BMVert *v) +{ + BMEdge *e_first = v->e; + BMEdge *e_next; + + if (!e_first) + return true; /* single vertex is both root and tip */ + e_next = bmesh_disk_edge_next(e_first, v); + + /* with a single edge, the vertex is either first or last of the curve; + * last vertex is defined as the tip + */ + if (e_next == e_first) { + if (e_first->v2 == v) + return true; + } + return false; +} + +/* Next vertex on a strand */ +BLI_INLINE BMVert *BM_strands_vert_next(BMVert *v) +{ + BMEdge *e_first = v->e; + BMEdge *e_next; + + /* one of the edges leads to the previous vertex */ + if (e_first) { + if (e_first->v1 == v) + return e_first->v2; + + e_next = bmesh_disk_edge_next(e_first, v); + if (e_next->v1 == v) + return e_next->v2; + } + return NULL; +} + +/* Previous vertex on a strand */ +BLI_INLINE BMVert *BM_strands_vert_prev(BMVert *v) +{ + BMEdge *e_first = v->e; + BMEdge *e_next; + + /* one of the edges leads to the previous vertex */ + if (e_first) { + if (e_first->v2 == v) + return e_first->v1; + + e_next = bmesh_disk_edge_next(e_first, v); + if (e_next->v2 == v) + return e_next->v1; + } + return NULL; +} + +int BM_strands_count(BMesh *bm); +int BM_strands_keys_count(BMVert *root); + +/* Create a new strand */ +struct BMVert *BM_strands_create(struct BMesh *bm, int len, bool set_defaults); + +/* ==== Iterators ==== */ + +typedef enum BMStrandsIterType { + BM_STRANDS_OF_MESH, + BM_VERTS_OF_STRAND, +} BMStrandsIterType; + +#define BM_ITER_STRANDS(ele, iter, bm, itype) \ + for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, bm, itype, NULL); \ + ele; \ + BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter)) + +#define BM_ITER_STRANDS_INDEX(ele, iter, bm, itype, indexvar) \ + for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, bm, itype, NULL), indexvar = 0; \ + ele; \ + BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter), (indexvar)++) + +#define BM_ITER_STRANDS_ELEM(ele, iter, data, itype) \ + for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, NULL, itype, data); \ + ele; \ + BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter)) + +#define BM_ITER_STRANDS_ELEM_INDEX(ele, iter, data, itype, indexvar) \ + for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, NULL, itype, data), indexvar = 0; \ + ele; \ + BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter), (indexvar)++) + +typedef struct BMIter__vert_of_strand { + BMVert *v_next; + BMEdge *e_next; +} BMIter__vert_of_strand; + +void bmstranditer__strands_of_mesh_begin(struct BMIter__elem_of_mesh *iter); +void *bmstranditer__strands_of_mesh_step(struct BMIter__elem_of_mesh *iter); + +void bmstranditer__verts_of_strand_begin(struct BMIter__vert_of_strand *iter); +void *bmstranditer__verts_of_strand_step(struct BMIter__vert_of_strand *iter); + +BLI_INLINE bool BM_strand_iter_init(BMIter *iter, BMesh *bm, const char itype, void *data) +{ + /* int argtype; */ + iter->itype = itype; + + /* inlining optimizes out this switch when called with the defined type */ + switch ((BMStrandsIterType)itype) { + case BM_STRANDS_OF_MESH: + BLI_assert(bm != NULL); + BLI_assert(data == NULL); + iter->begin = (BMIter__begin_cb)bmstranditer__strands_of_mesh_begin; + iter->step = (BMIter__step_cb)bmstranditer__strands_of_mesh_step; + iter->data.elem_of_mesh.pooliter.pool = bm->vpool; + break; + case BM_VERTS_OF_STRAND: { + BMVert *root; + + BLI_assert(data != NULL); + BLI_assert(((BMElem *)data)->head.htype == BM_VERT); + root = (BMVert *)data; + BLI_assert(BM_strands_vert_is_root(root)); + iter->begin = (BMIter__begin_cb)bmstranditer__verts_of_strand_begin; + iter->step = (BMIter__step_cb)bmstranditer__verts_of_strand_step; + ((BMIter__vert_of_strand *)(&iter->data))->v_next = root; + break; + } + default: + /* fallback to regular bmesh iterator */ + return BM_iter_init(iter, bm, itype, data); + break; + } + + iter->begin(iter); + + return true; +} + +/** + * \brief Iterator New + * + * Takes a bmesh iterator structure and fills + * it with the appropriate function pointers based + * upon its type and then calls BMeshIter_step() + * to return the first element of the iterator. + * + */ +BLI_INLINE void *BM_strand_iter_new(BMIter *iter, BMesh *bm, const char itype, void *data) +{ + if (LIKELY(BM_strand_iter_init(iter, bm, itype, data))) { + return BM_iter_step(iter); + } + else { + return NULL; + } +} + +#define BM_strand_iter_new(iter, bm, itype, data) \ + (BM_ITER_CHECK_TYPE_DATA(data), BM_strand_iter_new(iter, bm, itype, data)) + +#endif /* __BMESH_STRANDS_H__ */ diff --git a/source/blender/bmesh/intern/bmesh_strands_conv.c b/source/blender/bmesh/intern/bmesh_strands_conv.c new file mode 100644 index 00000000000..50ea50c9299 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_strands_conv.c @@ -0,0 +1,1314 @@ +/* + * ***** 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): Lukas Toenne. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_strands_conv.c + * \ingroup bmesh + * + * BM mesh conversion functions. + */ + +#include "DNA_cache_library_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_particle_types.h" +#include "DNA_key_types.h" +#include "DNA_strands_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "BKE_cache_library.h" +#include "BKE_customdata.h" +#include "BKE_key.h" +#include "BKE_main.h" +#include "BKE_mesh_sample.h" +#include "BKE_strands.h" +#include "BKE_particle.h" + +#include "bmesh.h" +#include "intern/bmesh_private.h" /* for element checking */ + +const char *CD_HAIR_SEGMENT_LENGTH = "HAIR_SEGMENT_LENGTH"; +const char *CD_HAIR_MASS = "HAIR_MASS"; +const char *CD_HAIR_WEIGHT = "HAIR_WEIGHT"; +const char *CD_HAIR_ROOT_LOCATION = "HAIR_ROOT_LOCATION"; + +/* ------------------------------------------------------------------------- */ + +/** + * Currently this is only used for Python scripts + * which may fail to keep matching UV/TexFace layers. + * + * \note This should only perform any changes in exceptional cases, + * if we need this to be faster we could inline #BM_data_layer_add and only + * call #update_data_blocks once at the end. + */ +void BM_strands_cd_validate(BMesh *UNUSED(bm)) +{ +} + +void BM_strands_cd_flag_ensure(BMesh *bm, const char cd_flag) +{ + const char cd_flag_all = BM_strands_cd_flag_from_bmesh(bm) | cd_flag; + BM_strands_cd_flag_apply(bm, cd_flag_all); +} + +void BM_strands_cd_flag_apply(BMesh *bm, const char UNUSED(cd_flag)) +{ + /* CustomData_bmesh_init_pool() must run first */ + BLI_assert(bm->vdata.totlayer == 0 || bm->vdata.pool != NULL); + BLI_assert(bm->edata.totlayer == 0 || bm->edata.pool != NULL); + + if (CustomData_get_named_layer_index(&bm->vdata, CD_PROP_FLT, CD_HAIR_MASS) < 0) { + BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_FLT, CD_HAIR_MASS); + } + if (CustomData_get_named_layer_index(&bm->vdata, CD_PROP_FLT, CD_HAIR_WEIGHT) < 0) { + BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_FLT, CD_HAIR_WEIGHT); + } + if (CustomData_get_named_layer_index(&bm->vdata, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION) < 0) { + BM_data_layer_add_named(bm, &bm->vdata, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION); + } + if (CustomData_get_named_layer_index(&bm->vdata, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH) < 0) { + BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH); + } +} + +char BM_strands_cd_flag_from_bmesh(BMesh *UNUSED(bm)) +{ + char cd_flag = 0; + return cd_flag; +} + +/* ------------------------------------------------------------------------- */ +/* CacheLibrary */ + +static KeyBlock *bm_set_shapekey_from_strands_key(BMesh *bm, Strands *strands, Key *key, int act_key_nr) +{ + int totvert = strands->totverts; + KeyBlock *actkey, *block; + int i, j; + + if (!key) { + return NULL; + } + + if (act_key_nr != 0) + actkey = BLI_findlink(&key->block, act_key_nr - 1); + else + actkey = NULL; + + CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0); + + if (actkey && actkey->totelem == totvert) { + bm->shapenr = act_key_nr; + } + + for (i = 0, block = key->block.first; block; block = block->next, i++) { + CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY, + CD_ASSIGN, NULL, 0, block->name); + + j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); + bm->vdata.layers[j].uid = block->uid; + } + + return actkey; +} + +/* create vertex and edge data for BMesh based on strand data */ +static void bm_make_strands(BMesh *bm, Strands *strands, Key *key, struct DerivedMesh *UNUSED(emitter_dm), float mat[4][4], float (*keyco)[3], int cd_shape_keyindex_offset) +{ + KeyBlock *block; + StrandIterator it_strand; + + int vindex, eindex; + BMVert *v = NULL, *v_prev; + BMEdge *e; + + vindex = 0; + eindex = 0; + for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) { + StrandVertexIterator it_vert; + + for (BKE_strand_vertex_iter_init(&it_vert, &it_strand); BKE_strand_vertex_iter_valid(&it_vert); BKE_strand_vertex_iter_next(&it_vert)) { + float co[3]; + + copy_v3_v3(co, keyco ? keyco[vindex] : it_vert.vertex->co); + /* transform to duplicator local space */ + mul_m4_v3(mat, co); + + v_prev = v; + v = BM_vert_create(bm, co, NULL, BM_CREATE_SKIP_CD); + BM_elem_index_set(v, vindex); /* set_ok */ + + /* transfer flag */ +// v->head.hflag = BM_vert_flag_from_mflag(mvert->flag & ~SELECT); + + /* this is necessary for selection counts to work properly */ +// if (hkey->editflag & SELECT) { +// BM_vert_select_set(bm, v, true); +// } + +// normal_short_to_float_v3(v->no, mvert->no); + + /* Copy Custom Data */ +// CustomData_to_bmesh_block(&me->vdata, &bm->vdata, vindex, &v->head.data, true); + CustomData_bmesh_set_default(&bm->vdata, &v->head.data); + +// BM_elem_float_data_named_set(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_MASS, mass); + BM_elem_float_data_named_set(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_WEIGHT, it_vert.vertex->weight); + + /* root */ + BM_elem_meshsample_data_named_set(&bm->vdata, v, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &it_strand.curve->msurf); + + /* set shapekey data */ + if (key) { + int k; + + /* set shape key original index */ + if (cd_shape_keyindex_offset != -1) + BM_ELEM_CD_SET_INT(v, cd_shape_keyindex_offset, vindex); + + for (block = key->block.first, k = 0; block; block = block->next, k++) { + float *co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, k); + + if (co) { + mul_v3_m4v3(co, mat, ((float *)block->data) + 3 * vindex); + } + } + } + + vindex += 1; + + if (it_vert.index > 0) { + e = BM_edge_create(bm, v_prev, v, NULL, BM_CREATE_SKIP_CD); + BM_elem_index_set(e, eindex); /* set_ok; one less edge than vertices for each particle */ + + /* transfer flags */ +// e->head.hflag = BM_edge_flag_from_mflag(medge->flag & ~SELECT); + + /* this is necessary for selection counts to work properly */ +// if (medge->flag & SELECT) { +// BM_edge_select_set(bm, e, true); +// } + + /* Copy Custom Data */ +// CustomData_to_bmesh_block(&me->edata, &bm->edata, eindex, &e->head.data, true); + CustomData_bmesh_set_default(&bm->edata, &e->head.data); + + eindex += 1; + } + } + + } + + bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE); /* added in order, clear dirty flag */ +} + +/** + * \brief ParticleSystem -> BMesh + */ +void BM_strands_bm_from_strands(BMesh *bm, Strands *strands, float mat[4][4], Key *key, struct DerivedMesh *emitter_dm, + const bool set_key, int act_key_nr) +{ + KeyBlock *actkey; + float (*keyco)[3] = NULL; + int totvert, totedge; + + int cd_shape_keyindex_offset; + + /* free custom data */ + /* this isnt needed in most cases but do just incase */ + CustomData_free(&bm->vdata, bm->totvert); + CustomData_free(&bm->edata, bm->totedge); + CustomData_free(&bm->ldata, bm->totloop); + CustomData_free(&bm->pdata, bm->totface); + + totvert = strands->totverts; + totedge = strands->totverts - strands->totcurves; + + if (!strands || !totvert || !totedge) { + if (strands) { /*no verts? still copy customdata layout*/ + CustomData_bmesh_init_pool(&bm->vdata, totvert, BM_VERT); + CustomData_bmesh_init_pool(&bm->edata, totedge, BM_EDGE); + CustomData_bmesh_init_pool(&bm->ldata, 0, BM_LOOP); + CustomData_bmesh_init_pool(&bm->pdata, 0, BM_FACE); + } + return; /* sanity check */ + } + + actkey = bm_set_shapekey_from_strands_key(bm, strands, key, act_key_nr); + if (actkey) + keyco = actkey->data; + + CustomData_bmesh_init_pool(&bm->vdata, totvert, BM_VERT); + CustomData_bmesh_init_pool(&bm->edata, totedge, BM_EDGE); + + BM_strands_cd_flag_apply(bm, 0); + + cd_shape_keyindex_offset = key ? CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) : -1; + + bm_make_strands(bm, strands, key, emitter_dm, mat, set_key ? keyco : NULL, cd_shape_keyindex_offset); + +#if 0 /* TODO */ + if (me->mselect && me->totselect != 0) { + + BMVert **vert_array = MEM_mallocN(sizeof(BMVert *) * bm->totvert, "VSelConv"); + BMEdge **edge_array = MEM_mallocN(sizeof(BMEdge *) * bm->totedge, "ESelConv"); + BMFace **face_array = MEM_mallocN(sizeof(BMFace *) * bm->totface, "FSelConv"); + MSelect *msel; + +#pragma omp parallel sections if (bm->totvert + bm->totedge + bm->totface >= BM_OMP_LIMIT) + { +#pragma omp section + { BM_iter_as_array(bm, BM_VERTS_OF_MESH, NULL, (void **)vert_array, bm->totvert); } +#pragma omp section + { BM_iter_as_array(bm, BM_EDGES_OF_MESH, NULL, (void **)edge_array, bm->totedge); } +#pragma omp section + { BM_iter_as_array(bm, BM_FACES_OF_MESH, NULL, (void **)face_array, bm->totface); } + } + + for (i = 0, msel = me->mselect; i < me->totselect; i++, msel++) { + switch (msel->type) { + case ME_VSEL: + BM_select_history_store(bm, (BMElem *)vert_array[msel->index]); + break; + case ME_ESEL: + BM_select_history_store(bm, (BMElem *)edge_array[msel->index]); + break; + case ME_FSEL: + BM_select_history_store(bm, (BMElem *)face_array[msel->index]); + break; + } + } + + MEM_freeN(vert_array); + MEM_freeN(edge_array); + MEM_freeN(face_array); + } + else { + me->totselect = 0; + if (me->mselect) { + MEM_freeN(me->mselect); + me->mselect = NULL; + } + } +#endif +} + +/* ------------------------------------------------------------------------- */ + +#if 0 +/** + * \brief BMesh -> Mesh + */ +static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert) +{ + const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); + BMVert **vertMap = NULL; + BMVert *eve; + int i = 0; + BMIter iter; + + /* caller needs to ensure this */ + BLI_assert(ototvert > 0); + + vertMap = MEM_callocN(sizeof(*vertMap) * ototvert, "vertMap"); + if (cd_shape_keyindex_offset != -1) { + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); + if ((keyi != ORIGINDEX_NONE) && (keyi < ototvert)) { + vertMap[keyi] = eve; + } + } + } + else { + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + if (i < ototvert) { + vertMap[i] = eve; + } + else { + break; + } + } + } + + return vertMap; +} + +BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e) +{ + /* this is a cheap way to set the edge draw, its not precise and will + * pick the first 2 faces an edge uses. + * The dot comparison is a little arbitrary, but set so that a 5 subd + * IcoSphere won't vanish but subd 6 will (as with pre-bmesh blender) */ + + + if ( /* (med->flag & ME_EDGEDRAW) && */ /* assume to be true */ + (e->l && (e->l != e->l->radial_next)) && + (dot_v3v3(e->l->f->no, e->l->radial_next->f->no) > 0.9995f)) + { + med->flag &= ~ME_EDGEDRAW; + } + else { + med->flag |= ME_EDGEDRAW; + } +} +#endif + +static void bm_strands_make_strand(BMesh *bm, BMVert *root, Strands *UNUSED(strands), float imat[4][4], Key *UNUSED(key), + struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *UNUSED(emitter_bvhtree), + StrandIterator *it_strand) +{ + int numverts = BM_strands_keys_count(root); + + BMVert *v; + BMIter iter; + StrandVertexIterator it_vert; + + it_strand->curve->numverts = numverts; + /* init root matrix, fully constructed below for non-degenerate strands */ + unit_m3(it_strand->curve->root_matrix); + + BKE_strand_vertex_iter_init(&it_vert, it_strand); + BM_ITER_STRANDS_ELEM(v, &iter, root, BM_VERTS_OF_STRAND) { + BLI_assert(BKE_strand_vertex_iter_valid(&it_vert)); + + /* root */ + if (it_vert.index == 0) { + float loc[3], nor[3], tang[3]; + BM_elem_meshsample_data_named_get(&bm->vdata, v, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &it_strand->curve->msurf); + BKE_mesh_sample_eval(emitter_dm, &it_strand->curve->msurf, loc, nor, tang); + + /* construct root matrix */ + copy_v3_v3(it_strand->curve->root_matrix[2], nor); + copy_v3_v3(it_strand->curve->root_matrix[0], tang); + cross_v3_v3v3(it_strand->curve->root_matrix[1], it_strand->curve->root_matrix[2], it_strand->curve->root_matrix[0]); + + /* transform from edit space (duplicator local space) back to the original object space */ + mul_mat3_m4_v3(imat, it_strand->curve->root_matrix[0]); + mul_mat3_m4_v3(imat, it_strand->curve->root_matrix[1]); + mul_mat3_m4_v3(imat, it_strand->curve->root_matrix[2]); + } + + /* transform from edit space (duplicator local space) back to the original object space */ + mul_v3_m4v3(it_vert.vertex->co, imat, v->co); + it_vert.vertex->time = numverts > 0 ? (float)it_vert.index / (float)(numverts - 1) : 0.0f; + + if (it_vert.index == 0) { + /* weight 1.0 is used for pinning hair roots in particles */ + it_vert.vertex->weight = 1.0f; + } + else { + it_vert.vertex->weight = BM_elem_float_data_named_get(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_WEIGHT); + } + + BKE_strand_vertex_iter_next(&it_vert); + + BM_CHECK_ELEMENT(v); + } +} + +/** + * returns customdata shapekey index from a keyblock or -1 + * \note could split this out into a more generic function */ +static int bm_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey) +{ + int i; + int j = 0; + + for (i = 0; i < bm->vdata.totlayer; i++) { + if (bm->vdata.layers[i].type == CD_SHAPEKEY) { + if (currkey->uid == bm->vdata.layers[i].uid) { + return j; + } + j++; + } + } + return -1; +} + +/* go through and find any shapekey customdata layers + * that might not have corresponding KeyBlocks, and add them if + * necessary */ +static void bm_strands_add_missing_shapekeys(BMesh *bm, Key *key) +{ + KeyBlock *currkey; + int i; + + for (i = 0; i < bm->vdata.totlayer; i++) { + const CustomDataLayer *layer = &bm->vdata.layers[i]; + if (layer->type != CD_SHAPEKEY) + continue; + + for (currkey = key->block.first; currkey; currkey = currkey->next) { + if (currkey->uid == layer->uid) + break; + } + + if (!currkey) { + currkey = BKE_keyblock_add(key, layer->name); + currkey->uid = layer->uid; + } + } +} + +/* returns offset of the edit against the active shape, so other shapes can compensate accordingly to avoid deformation */ +static void bm_strands_get_basiskey_offset(BMesh *bm, Strands *strands, Key *key, int cd_shape_keyindex_offset, float (**r_offset)[3]) +{ + *r_offset = NULL; + + /* only need offsets for relative shape keys */ + if (key->type == KEY_RELATIVE) { + + KeyBlock *actkey = BLI_findlink(&key->block, bm->shapenr - 1); + /* unlikely, but the active key may not be valid if the bmesh and the mesh are out of sync */ + if (!actkey) + return; + + /* only if active key is a base */ + if (BKE_keyblock_is_basis(key, bm->shapenr - 1) && cd_shape_keyindex_offset >= 0) { + float (*fp)[3] = actkey->data; + float (*ofs)[3] = MEM_callocN(sizeof(float) * 3 * bm->totvert, "currkey->data"); + BMIter iter; + BMVert *eve; + StrandsVertex *svert; + int i; + + svert = strands->verts; + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); + + if (keyi != ORIGINDEX_NONE) { + sub_v3_v3v3(ofs[i], svert->co, fp[keyi]); + } + else { + /* if there are new vertices in the mesh, we can't propagate the offset + * because it will only work for the existing vertices and not the new + * ones, creating a mess when doing e.g. subdivide + translate */ + MEM_freeN(ofs); + ofs = NULL; + break; + } + + svert++; + } + + *r_offset = ofs; + } + } +} + +static float *bm_strands_apply_keyblock(BMesh *bm, Strands *strands, StrandsVertex *oldverts, float imat[4][4], Key *key, int cd_shape_keyindex_offset, + KeyBlock *kb, KeyBlock *actkb, float (*oldkey)[3], float (*offset)[3]) +{ + const bool apply_offset = (offset && (kb != actkb) && (bm->shapenr - 1 == kb->relative)); + const int shape_layer_index = bm_shape_layer_index_from_kb(bm, kb); + const int cd_shape_offset = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, shape_layer_index); + + float *newkey, *fp; + BMIter iter; + BMVert *eve; + StrandsVertex *svert; + int keyi; + float (*ofs_pt)[3] = offset; + + fp = newkey = MEM_callocN(key->elemsize * bm->totvert, "currkey->data"); + + svert = strands->verts; + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + + if (kb == actkb) { + copy_v3_v3(fp, eve->co); + + if (actkb != key->refkey) { /* important see bug [#30771] */ + if (cd_shape_keyindex_offset != -1) { + if (oldverts) { + keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); + if (keyi != ORIGINDEX_NONE && keyi < kb->totelem) { /* valid old vertex */ + copy_v3_v3(svert->co, oldverts[keyi].co); + } + } + } + } + } + else if (shape_layer_index != -1) { + /* in most cases this runs */ + copy_v3_v3(fp, BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset)); + } + else if ((oldkey != NULL) && + (cd_shape_keyindex_offset != -1) && + ((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) && + (keyi < kb->totelem)) + { + /* old method of reconstructing keys via vertice's original key indices, + * currently used if the new method above fails (which is theoretically + * possible in certain cases of undo) */ + copy_v3_v3(fp, oldkey[keyi]); + } + else { + /* fail! fill in with dummy value */ + copy_v3_v3(fp, svert->co); + } + + /* propagate edited basis offsets to other shapes */ + if (apply_offset) { + add_v3_v3(fp, *ofs_pt++); + } + + /* transform from edit space (duplicator local space) back to the original object space */ + mul_m4_v3(imat, fp); + + fp += 3; + svert++; + } + + return newkey; +} + +static void bm_strands_apply_shapekeys(BMesh *bm, Strands *strands, StrandsVertex *oldverts, float imat[4][4], Key *key) +{ + const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); + KeyBlock *actkb = BLI_findlink(&key->block, bm->shapenr - 1); + KeyBlock *kb; + float (*offset)[3] = NULL; + + bm_strands_add_missing_shapekeys(bm, key); + + if (oldverts) + bm_strands_get_basiskey_offset(bm, strands, key, cd_shape_keyindex_offset, &offset); + + for (kb = key->block.first; kb; kb = kb->next) { + float *newkey; + + newkey = bm_strands_apply_keyblock(bm, strands, oldverts, imat, key, cd_shape_keyindex_offset, kb, actkb, kb->data, offset); + + kb->totelem = bm->totvert; + if (kb->data) { + MEM_freeN(kb->data); + } + kb->data = newkey; + } + + if (offset) + MEM_freeN(offset); +} + +Strands *BM_strands_bm_to_strands(BMesh *bm, Strands *strands, float mat[4][4], Key *key, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree) +{ + Strands *oldstrands; + int ntotcurves; + float imat[4][4]; + + BMVert *root; + BMIter iter; + StrandIterator it_strand; + + ntotcurves = BM_strands_count(bm); + + /* lets save the old strands just in case we are actually working on + * a key ... we now do processing of the keys at the end */ + oldstrands = strands; + + invert_m4_m4(imat, mat); + + strands = BKE_strands_new(ntotcurves, bm->totvert); + +// strands->cd_flag = BM_strands_cd_flag_from_bmesh(bm); + + BKE_strand_iter_init(&it_strand, strands); + BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) { + BLI_assert(BKE_strand_iter_valid(&it_strand)); + + bm_strands_make_strand(bm, root, strands, imat, key, emitter_dm, emitter_bvhtree, &it_strand); + + BKE_strand_iter_next(&it_strand); + } + bm->elem_index_dirty &= ~BM_VERT; + + BKE_strands_ensure_normals(strands); + + +#if 0 // TODO + { + BMEditSelection *selected; + me->totselect = BLI_listbase_count(&(bm->selected)); + + if (me->mselect) MEM_freeN(me->mselect); + + me->mselect = MEM_callocN(sizeof(MSelect) * me->totselect, "Mesh selection history"); + + + for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) { + if (selected->htype == BM_VERT) { + me->mselect[i].type = ME_VSEL; + + } + else if (selected->htype == BM_EDGE) { + me->mselect[i].type = ME_ESEL; + + } + else if (selected->htype == BM_FACE) { + me->mselect[i].type = ME_FSEL; + } + + me->mselect[i].index = BM_elem_index_get(selected->ele); + } + } +#endif + + if (key) { + bm_strands_apply_shapekeys(bm, strands, oldstrands ? oldstrands->verts : NULL, imat, key); + } + + if (oldstrands) { + BKE_strands_free(oldstrands); + } + + return strands; +} + +/* ------------------------------------------------------------------------- */ +/* ParticleSystem */ + +int BM_strands_count_psys_keys(ParticleSystem *psys) +{ + ParticleData *pa; + int p; + int totkeys = 0; + + for (p = 0, pa = psys->particles; p < psys->totpart; ++p, ++pa) + totkeys += pa->totkey; + + return totkeys; +} + +#if 0 +static KeyBlock *bm_set_shapekey_from_psys(BMesh *bm, ParticleSystem *psys, int totvert, int act_key_nr) +{ + KeyBlock *actkey, *block; + int i, j; + + if (!psys->key) { + return NULL; + } + + if (act_key_nr != 0) + actkey = BLI_findlink(&psys->key->block, act_key_nr - 1); + else + actkey = NULL; + + CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0); + + /* check if we need to generate unique ids for the shapekeys. + * this also exists in the file reading code, but is here for + * a sanity check */ + if (!psys->key->uidgen) { + fprintf(stderr, + "%s had to generate shape key uid's in a situation we shouldn't need to! " + "(bmesh internal error)\n", + __func__); + + psys->key->uidgen = 1; + for (block = psys->key->block.first; block; block = block->next) { + block->uid = psys->key->uidgen++; + } + } + + if (actkey && actkey->totelem == totvert) { + bm->shapenr = act_key_nr; + } + + for (i = 0, block = psys->key->block.first; block; block = block->next, i++) { + CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY, + CD_ASSIGN, NULL, 0, block->name); + + j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); + bm->vdata.layers[j].uid = block->uid; + } + + return actkey; +} +#endif + +/* create vertex and edge data for BMesh based on particle hair keys */ +static void bm_make_particles(BMesh *bm, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm, float (*keyco)[3], int cd_shape_keyindex_offset) +{ +// KeyBlock *block; + ParticleData *pa; + HairKey *hkey; + int p, k; + + int vindex, eindex; + BMVert *v = NULL, *v_prev; + BMEdge *e; + + float hairmat[4][4]; + + /* XXX currently all particles and keys have the same mass, this may change */ + float mass = psys->part->mass; + + vindex = 0; + eindex = 0; + for (p = 0, pa = psys->particles; p < psys->totpart; ++p, ++pa) { + + /* hair keys are in a local "hair space", but edit data should be in object space */ + psys_mat_hair_to_object(ob, emitter_dm, psys->part->from, pa, hairmat); + + for (k = 0, hkey = pa->hair; k < pa->totkey; ++k, ++hkey) { + float co[3]; + + copy_v3_v3(co, keyco ? keyco[vindex] : hkey->co); + mul_m4_v3(hairmat, co); + + v_prev = v; + v = BM_vert_create(bm, co, NULL, BM_CREATE_SKIP_CD); + BM_elem_index_set(v, vindex); /* set_ok */ + + /* transfer flag */ +// v->head.hflag = BM_vert_flag_from_mflag(mvert->flag & ~SELECT); + + /* this is necessary for selection counts to work properly */ +// if (hkey->editflag & SELECT) { +// BM_vert_select_set(bm, v, true); +// } + +// normal_short_to_float_v3(v->no, mvert->no); + + /* Copy Custom Data */ +// CustomData_to_bmesh_block(&me->vdata, &bm->vdata, vindex, &v->head.data, true); + CustomData_bmesh_set_default(&bm->vdata, &v->head.data); + + BM_elem_float_data_named_set(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_MASS, mass); + BM_elem_float_data_named_set(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_WEIGHT, hkey->weight); + + /* root */ + if (k == 0) { + MSurfaceSample root_loc; + if (BKE_mesh_sample_from_particle(&root_loc, psys, emitter_dm, pa)) { + BM_elem_meshsample_data_named_set(&bm->vdata, v, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &root_loc); + } + } + +#if 0 + /* set shapekey data */ + if (psys->key) { + /* set shape key original index */ + if (cd_shape_keyindex_offset != -1) BM_ELEM_CD_SET_INT(v, cd_shape_keyindex_offset, vindex); + + for (block = psys->key->block.first, j = 0; block; block = block->next, j++) { + float *co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, j); + + if (co) { + copy_v3_v3(co, ((float *)block->data) + 3 * vindex); + } + } + } +#else + (void)cd_shape_keyindex_offset; +#endif + + vindex += 1; + + if (k > 0) { + e = BM_edge_create(bm, v_prev, v, NULL, BM_CREATE_SKIP_CD); + BM_elem_index_set(e, eindex); /* set_ok; one less edge than vertices for each particle */ + + /* transfer flags */ +// e->head.hflag = BM_edge_flag_from_mflag(medge->flag & ~SELECT); + + /* this is necessary for selection counts to work properly */ +// if (medge->flag & SELECT) { +// BM_edge_select_set(bm, e, true); +// } + + /* Copy Custom Data */ +// CustomData_to_bmesh_block(&me->edata, &bm->edata, eindex, &e->head.data, true); + CustomData_bmesh_set_default(&bm->edata, &e->head.data); + + eindex += 1; + } + + } /* hair keys */ + + } /* particles */ + + bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE); /* added in order, clear dirty flag */ +} + +/** + * \brief ParticleSystem -> BMesh + */ +void BM_strands_bm_from_psys(BMesh *bm, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm, + const bool set_key, int act_key_nr) +{ + // KeyBlock *actkey; + float (*keyco)[3] = NULL; + int totvert, totedge; + + int cd_shape_keyindex_offset; + + /* free custom data */ + /* this isnt needed in most cases but do just incase */ + CustomData_free(&bm->vdata, bm->totvert); + CustomData_free(&bm->edata, bm->totedge); + CustomData_free(&bm->ldata, bm->totloop); + CustomData_free(&bm->pdata, bm->totface); + + totvert = BM_strands_count_psys_keys(psys); + totedge = totvert - psys->totpart; + + if (!psys || !totvert || !totedge) { + if (psys) { /*no verts? still copy customdata layout*/ + CustomData_bmesh_init_pool(&bm->vdata, totvert, BM_VERT); + CustomData_bmesh_init_pool(&bm->edata, totedge, BM_EDGE); + CustomData_bmesh_init_pool(&bm->ldata, 0, BM_LOOP); + CustomData_bmesh_init_pool(&bm->pdata, 0, BM_FACE); + } + return; /* sanity check */ + } + +#if 0 + actkey = bm_set_shapekey_from_psys(bm, psys, totvert, act_key_nr); + if (actkey) + keyco = actkey->data; +#else + (void)act_key_nr; +#endif + + CustomData_bmesh_init_pool(&bm->vdata, totvert, BM_VERT); + CustomData_bmesh_init_pool(&bm->edata, totedge, BM_EDGE); + + BM_strands_cd_flag_apply(bm, /*psys->cd_flag*/0); + + cd_shape_keyindex_offset = /*psys->key ? CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) :*/ -1; + + bm_make_particles(bm, ob, psys, emitter_dm, set_key ? keyco : NULL, cd_shape_keyindex_offset); + + +#if 0 /* TODO */ + if (me->mselect && me->totselect != 0) { + + BMVert **vert_array = MEM_mallocN(sizeof(BMVert *) * bm->totvert, "VSelConv"); + BMEdge **edge_array = MEM_mallocN(sizeof(BMEdge *) * bm->totedge, "ESelConv"); + BMFace **face_array = MEM_mallocN(sizeof(BMFace *) * bm->totface, "FSelConv"); + MSelect *msel; + +#pragma omp parallel sections if (bm->totvert + bm->totedge + bm->totface >= BM_OMP_LIMIT) + { +#pragma omp section + { BM_iter_as_array(bm, BM_VERTS_OF_MESH, NULL, (void **)vert_array, bm->totvert); } +#pragma omp section + { BM_iter_as_array(bm, BM_EDGES_OF_MESH, NULL, (void **)edge_array, bm->totedge); } +#pragma omp section + { BM_iter_as_array(bm, BM_FACES_OF_MESH, NULL, (void **)face_array, bm->totface); } + } + + for (i = 0, msel = me->mselect; i < me->totselect; i++, msel++) { + switch (msel->type) { + case ME_VSEL: + BM_select_history_store(bm, (BMElem *)vert_array[msel->index]); + break; + case ME_ESEL: + BM_select_history_store(bm, (BMElem *)edge_array[msel->index]); + break; + case ME_FSEL: + BM_select_history_store(bm, (BMElem *)face_array[msel->index]); + break; + } + } + + MEM_freeN(vert_array); + MEM_freeN(edge_array); + MEM_freeN(face_array); + } + else { + me->totselect = 0; + if (me->mselect) { + MEM_freeN(me->mselect); + me->mselect = NULL; + } + } +#endif +} + +#if 0 +/** + * \brief BMesh -> Mesh + */ +static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert) +{ + const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); + BMVert **vertMap = NULL; + BMVert *eve; + int i = 0; + BMIter iter; + + /* caller needs to ensure this */ + BLI_assert(ototvert > 0); + + vertMap = MEM_callocN(sizeof(*vertMap) * ototvert, "vertMap"); + if (cd_shape_keyindex_offset != -1) { + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); + if ((keyi != ORIGINDEX_NONE) && (keyi < ototvert)) { + vertMap[keyi] = eve; + } + } + } + else { + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + if (i < ototvert) { + vertMap[i] = eve; + } + else { + break; + } + } + } + + return vertMap; +} + +/** + * returns customdata shapekey index from a keyblock or -1 + * \note could split this out into a more generic function */ +static int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey) +{ + int i; + int j = 0; + + for (i = 0; i < bm->vdata.totlayer; i++) { + if (bm->vdata.layers[i].type == CD_SHAPEKEY) { + if (currkey->uid == bm->vdata.layers[i].uid) { + return j; + } + j++; + } + } + return -1; +} + +BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e) +{ + /* this is a cheap way to set the edge draw, its not precise and will + * pick the first 2 faces an edge uses. + * The dot comparison is a little arbitrary, but set so that a 5 subd + * IcoSphere won't vanish but subd 6 will (as with pre-bmesh blender) */ + + + if ( /* (med->flag & ME_EDGEDRAW) && */ /* assume to be true */ + (e->l && (e->l != e->l->radial_next)) && + (dot_v3v3(e->l->f->no, e->l->radial_next->f->no) > 0.9995f)) + { + med->flag &= ~ME_EDGEDRAW; + } + else { + med->flag |= ME_EDGEDRAW; + } +} +#endif + +static void make_particle_hair(BMesh *bm, BMVert *root, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree, struct ParticleData *pa) +{ + int totkey = BM_strands_keys_count(root); + HairKey *hair; + + BMVert *v; + BMIter iter; + HairKey *hkey; + int k; + + float inv_hairmat[4][4]; + + pa->alive = PARS_ALIVE; + pa->flag = 0; + + pa->time = 0.0f; + pa->lifetime = 100.0f; + pa->dietime = 100.0f; + + pa->size = psys->part->size; + + // TODO define other particle stuff ... + + hair = MEM_callocN(totkey * sizeof(HairKey), "hair keys"); + + hkey = hair; + k = 0; + BM_ITER_STRANDS_ELEM(v, &iter, root, BM_VERTS_OF_STRAND) { + /* root */ + if (k == 0) { + MSurfaceSample root_loc; + BM_elem_meshsample_data_named_get(&bm->vdata, v, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &root_loc); + if (!BKE_mesh_sample_to_particle(&root_loc, psys, emitter_dm, emitter_bvhtree, pa)) { + pa->num = 0; + pa->num_dmcache = DMCACHE_NOTFOUND; + zero_v4(pa->fuv); + pa->foffset = 0.0f; + } + + /* edit data is in object space, hair keys must be converted back into "hair space" */ + psys_mat_hair_to_object(ob, emitter_dm, psys->part->from, pa, inv_hairmat); + invert_m4(inv_hairmat); + } + + mul_v3_m4v3(hkey->co, inv_hairmat, v->co); + mul_v3_m4v3(hkey->world_co, ob->obmat, v->co); + + hkey->time = totkey > 0 ? (float)k / (float)(totkey - 1) : 0.0f; + if (k == 0) { + /* weight 1.0 is used for pinning hair roots in particles */ + hkey->weight = 1.0f; + } + else { + hkey->weight = BM_elem_float_data_named_get(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_WEIGHT); + } + + ++hkey; + ++k; + + BM_CHECK_ELEMENT(v); + } + + if (pa->hair) + MEM_freeN(pa->hair); + + pa->hair = hair; + pa->totkey = totkey; +} + +void BM_strands_bm_to_psys(BMesh *bm, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree) +{ + ParticleData *particles, *oldparticles; + int ototpart, ntotpart; + + BMVert *root; + BMIter iter; + ParticleData *pa; + int p; + + ototpart = psys->totpart; + + ntotpart = BM_strands_count(bm); + + /* new particles block */ + if (bm->totvert == 0) particles = NULL; + else particles = MEM_callocN(ntotpart * sizeof(ParticleData), "particles"); + + /* lets save the old particles just in case we are actually working on + * a key ... we now do processing of the keys at the end */ + oldparticles = psys->particles; + + psys->totpart = ntotpart; + +// psys->cd_flag = BM_strands_cd_flag_from_bmesh(bm); + + pa = particles; + p = 0; + BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) { + + make_particle_hair(bm, root, ob, psys, emitter_dm, emitter_bvhtree, pa); + + ++pa; + ++p; + } + bm->elem_index_dirty &= ~BM_VERT; + + +#if 0 // TODO + { + BMEditSelection *selected; + me->totselect = BLI_listbase_count(&(bm->selected)); + + if (me->mselect) MEM_freeN(me->mselect); + + me->mselect = MEM_callocN(sizeof(MSelect) * me->totselect, "Mesh selection history"); + + + for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) { + if (selected->htype == BM_VERT) { + me->mselect[i].type = ME_VSEL; + + } + else if (selected->htype == BM_EDGE) { + me->mselect[i].type = ME_ESEL; + + } + else if (selected->htype == BM_FACE) { + me->mselect[i].type = ME_FSEL; + } + + me->mselect[i].index = BM_elem_index_get(selected->ele); + } + } +#endif + +#if 0 // TODO + /* see comment below, this logic is in twice */ + + if (me->key) { + const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); + + KeyBlock *currkey; + KeyBlock *actkey = BLI_findlink(&me->key->block, bm->shapenr - 1); + + float (*ofs)[3] = NULL; + + /* go through and find any shapekey customdata layers + * that might not have corresponding KeyBlocks, and add them if + * necessary */ + j = 0; + for (i = 0; i < bm->vdata.totlayer; i++) { + if (bm->vdata.layers[i].type != CD_SHAPEKEY) + continue; + + for (currkey = me->key->block.first; currkey; currkey = currkey->next) { + if (currkey->uid == bm->vdata.layers[i].uid) + break; + } + + if (!currkey) { + currkey = BKE_keyblock_add(me->key, bm->vdata.layers[i].name); + currkey->uid = bm->vdata.layers[i].uid; + } + + j++; + } + + + /* editing the base key should update others */ + if ((me->key->type == KEY_RELATIVE) && /* only need offsets for relative shape keys */ + (actkey != NULL) && /* unlikely, but the active key may not be valid if the + * bmesh and the mesh are out of sync */ + (oldverts != NULL)) /* not used here, but 'oldverts' is used later for applying 'ofs' */ + { + 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)) { + float (*fp)[3] = actkey->data; + + ofs = MEM_callocN(sizeof(float) * 3 * bm->totvert, "currkey->data"); + mvert = me->mvert; + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); + + if (keyi != ORIGINDEX_NONE) { + sub_v3_v3v3(ofs[i], mvert->co, fp[keyi]); + } + else { + /* if there are new vertices in the mesh, we can't propagate the offset + * because it will only work for the existing vertices and not the new + * ones, creating a mess when doing e.g. subdivide + translate */ + MEM_freeN(ofs); + ofs = NULL; + break; + } + + mvert++; + } + } + } + + for (currkey = me->key->block.first; currkey; currkey = currkey->next) { + const bool apply_offset = (ofs && (currkey != actkey) && (bm->shapenr - 1 == currkey->relative)); + int cd_shape_offset; + int keyi; + float (*ofs_pt)[3] = ofs; + float *newkey, (*oldkey)[3], *fp; + + j = bm_to_mesh_shape_layer_index_from_kb(bm, currkey); + cd_shape_offset = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, j); + + + fp = newkey = MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data"); + oldkey = currkey->data; + + mvert = me->mvert; + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + + if (currkey == actkey) { + copy_v3_v3(fp, eve->co); + + if (actkey != me->key->refkey) { /* important see bug [#30771] */ + if (cd_shape_keyindex_offset != -1) { + if (oldverts) { + keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); + if (keyi != ORIGINDEX_NONE && keyi < currkey->totelem) { /* valid old vertex */ + copy_v3_v3(mvert->co, oldverts[keyi].co); + } + } + } + } + } + else if (j != -1) { + /* in most cases this runs */ + copy_v3_v3(fp, BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset)); + } + else if ((oldkey != NULL) && + (cd_shape_keyindex_offset != -1) && + ((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) && + (keyi < currkey->totelem)) + { + /* old method of reconstructing keys via vertice's original key indices, + * currently used if the new method above fails (which is theoretically + * possible in certain cases of undo) */ + copy_v3_v3(fp, oldkey[keyi]); + } + else { + /* fail! fill in with dummy value */ + copy_v3_v3(fp, mvert->co); + } + + /* propagate edited basis offsets to other shapes */ + if (apply_offset) { + add_v3_v3(fp, *ofs_pt++); + } + + fp += 3; + mvert++; + } + + currkey->totelem = bm->totvert; + if (currkey->data) { + MEM_freeN(currkey->data); + } + currkey->data = newkey; + } + + if (ofs) MEM_freeN(ofs); + } +#else + psys->particles = particles; +#endif + + if (oldparticles) { + ParticleData *pa; + int p; + for (p = 0, pa = oldparticles; p < ototpart; ++p, ++pa) + if (pa->hair) + MEM_freeN(pa->hair); + MEM_freeN(oldparticles); + } +} diff --git a/source/blender/bmesh/intern/bmesh_strands_conv.h b/source/blender/bmesh/intern/bmesh_strands_conv.h new file mode 100644 index 00000000000..aa18b779b17 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_strands_conv.h @@ -0,0 +1,67 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2014 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_STRANDS_CONV_H__ +#define __BMESH_STRANDS_CONV_H__ + +/** \file blender/bmesh/intern/bmesh_strands_conv.h + * \ingroup bmesh + */ + +struct BMesh; +struct Mesh; +struct Object; +struct ParticleSystem; +struct DerivedMesh; +struct BVHTreeFromMesh; +struct Strands; +struct Key; + +extern const char *CD_HAIR_SEGMENT_LENGTH; +extern const char *CD_HAIR_MASS; +extern const char *CD_HAIR_WEIGHT; +extern const char *CD_HAIR_ROOT_LOCATION; + +void BM_strands_cd_validate(struct BMesh *bm); +void BM_strands_cd_flag_ensure(struct BMesh *bm, const char cd_flag); +void BM_strands_cd_flag_apply(struct BMesh *bm, const char cd_flag); +char BM_strands_cd_flag_from_bmesh(struct BMesh *bm); + +void BM_strands_bm_from_strands(struct BMesh *bm, struct Strands *strands, float mat[4][4], struct Key *key, struct DerivedMesh *emitter_dm, const bool set_key, int act_key_nr); +struct Strands *BM_strands_bm_to_strands(struct BMesh *bm, struct Strands *strands, float mat[4][4], struct Key *key, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree); + +int BM_strands_count_psys_keys(struct ParticleSystem *psys); +void BM_strands_bm_from_psys(struct BMesh *bm, struct Object *ob, struct ParticleSystem *psys, struct DerivedMesh *emitter_dm, + const bool set_key, int act_key_nr); +void BM_strands_bm_to_psys(struct BMesh *bm, struct Object *ob, struct ParticleSystem *psys, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree); + +#define BMALLOC_TEMPLATE_FROM_STRANDS(strands) { (CHECK_TYPE_INLINE(strands, Strands *), \ + strands->totverts), (strands->totverts - strands->totcurves), 0, 0 } +#define BMALLOC_TEMPLATE_FROM_PSYS(psys) { (CHECK_TYPE_INLINE(psys, ParticleSystem *), \ + BM_strands_count_psys_keys(psys)), (BM_strands_count_psys_keys(psys) - (psys)->totpart), 0, 0 } + +#endif /* __BMESH_STRANDS_CONV_H__ */ 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 20b56632099..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)); 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 b7bf80b0e3f..0fb70b2b733 100644 --- a/source/blender/bmesh/intern/bmesh_walkers_impl.c +++ b/source/blender/bmesh/intern/bmesh_walkers_impl.c @@ -223,6 +223,266 @@ 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; + } + + 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(BM_edge_is_wire(e)); + + if (BLI_gset_haskey(walker->visit_set_alt, 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; + } + + e = v->e; + do { + if (BM_edge_is_wire(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; + bmw_LoopShellWireWalker_visitVert(walker, v, NULL); + break; + } + case BM_EDGE: + { + BMEdge *e = (BMEdge *)h; + if (BM_edge_is_wire(e)) { + bmw_LoopShellWalker_visitEdgeWire(walker, e); + } + 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 * \{ @@ -543,9 +803,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] = { @@ -593,7 +853,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); } @@ -606,16 +866,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; @@ -1248,6 +1508,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, @@ -1278,12 +1558,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 */ }; @@ -1340,8 +1620,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_bridge.c b/source/blender/bmesh/operators/bmo_bridge.c index 6002dcf2c0d..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; diff --git a/source/blender/bmesh/operators/bmo_connect_concave.c b/source/blender/bmesh/operators/bmo_connect_concave.c index a00f65bd10f..107aead6994 100644 --- a/source/blender/bmesh/operators/bmo_connect_concave.c +++ b/source/blender/bmesh/operators/bmo_connect_concave.c @@ -79,8 +79,8 @@ static bool bm_face_split_by_concave( 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; + 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 */ diff --git a/source/blender/bmesh/operators/bmo_connect_pair.c b/source/blender/bmesh/operators/bmo_connect_pair.c index fbc128b375f..cf0e233fe6c 100644 --- a/source/blender/bmesh/operators/bmo_connect_pair.c +++ b/source/blender/bmesh/operators/bmo_connect_pair.c @@ -152,8 +152,9 @@ static void min_dist_dir_update(MinDistDir *dist, const float dist_dir[3]) /** \} */ -static int state_isect_co_pair(const PathContext *pc, - const float co_a[3], const float co_b[3]) +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; @@ -169,15 +170,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; @@ -187,9 +190,10 @@ 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); @@ -213,8 +217,9 @@ static bool state_link_find(const PathLinkState *state, BMElem *ele) return false; } -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); diff --git a/source/blender/bmesh/operators/bmo_dissolve.c b/source/blender/bmesh/operators/bmo_dissolve.c index ecb41363761..ac0466a74d2 100644 --- a/source/blender/bmesh/operators/bmo_dissolve.c +++ b/source/blender/bmesh/operators/bmo_dissolve.c @@ -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 a5a6480c187..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; @@ -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 aa92c3054cd..435b9e60949 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, @@ -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); } 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 dd954adcd55..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) { @@ -185,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); @@ -216,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); @@ -485,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 fb99c9777d0..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; @@ -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 27e140eb990..3d899bdef28 100644 --- a/source/blender/bmesh/operators/bmo_inset.c +++ b/source/blender/bmesh/operators/bmo_inset.c @@ -974,7 +974,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); } } } diff --git a/source/blender/bmesh/operators/bmo_join_triangles.c b/source/blender/bmesh/operators/bmo_join_triangles.c index 6562f26062f..841a68fbbe5 100644 --- a/source/blender/bmesh/operators/bmo_join_triangles.c +++ b/source/blender/bmesh/operators/bmo_join_triangles.c @@ -42,163 +42,230 @@ #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.0 - (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; +} - if (areaA <= areaB) minarea = areaA; - else minarea = areaB; +/* cache customdata delimiters */ +struct DelimitData_CD { + int cd_type; + int cd_size; + int cd_offset; + int cd_offset_end; +}; - if (areaA >= areaB) maxarea = areaA; - else maxarea = areaB; +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 (!maxarea) measure += 1; - else measure += (1 - (minarea / maxarea)); + float angle_face; + float angle_face__cos; - return measure; + float angle_shape; + + struct DelimitData_CD cdata[4]; + int cdata_len; +}; + +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 EDGE_CHOSEN (1 << 1) +#define FACE_OUT (1 << 0) +#define FACE_MARK (1 << 1) +#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; @@ -209,6 +276,44 @@ void bmo_join_triangles_exec(BMesh *bm, BMOperator *op) 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 +325,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 +344,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; - - if (do_sharp && !BM_elem_flag_test(e, BM_ELEM_SMOOTH)) - continue; - - 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; + bm_edge_to_quad_verts(e, verts); - v1 = e->l->v; - v2 = e->l->prev->v; - v3 = e->l->next->v; - v4 = e->l->radial_next->prev->v; + error = quad_calc_error(verts[0]->co, verts[1]->co, verts[2]->co, verts[3]->co); - 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,7 +370,7 @@ 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) || + 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); @@ -307,39 +398,5 @@ 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); - } - } - } - } - 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 0ef96553d67..f62e445ca18 100644 --- a/source/blender/bmesh/operators/bmo_normals.c +++ b/source/blender/bmesh/operators/bmo_normals.c @@ -71,7 +71,7 @@ static bool bmo_recalc_normal_edge_filter_cb(BMElem *ele, void *UNUSED(user_data * * To take these spikes into account, use the normals of the faces edges. */ - #define USE_FACE_EDGE_NORMAL_TEST +#define USE_FACE_EDGE_NORMAL_TEST /** * The center of the entire island is't necessarily well placed, diff --git a/source/blender/bmesh/operators/bmo_removedoubles.c b/source/blender/bmesh/operators/bmo_removedoubles.c index 871bee64c19..74bcbbaf463 100644 --- a/source/blender/bmesh/operators/bmo_removedoubles.c +++ b/source/blender/bmesh/operators/bmo_removedoubles.c @@ -380,8 +380,11 @@ void bmo_collapse_exec(BMesh *bm, BMOperator *op) float min[3], max[3], center[3]; unsigned int i, tot; 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"); @@ -519,8 +522,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; diff --git a/source/blender/bmesh/operators/bmo_subdivide.c b/source/blender/bmesh/operators/bmo_subdivide.c index 003c27671a5..e3304298647 100644 --- a/source/blender/bmesh/operators/bmo_subdivide.c +++ b/source/blender/bmesh/operators/bmo_subdivide.c @@ -80,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. @@ -163,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; @@ -178,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 = 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); @@ -208,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); @@ -234,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); @@ -250,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 */ @@ -279,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; @@ -317,7 +328,7 @@ 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); + v = subdivide_edge_num(bm, eed, &e_tmp, i, params->numcuts, params, v_a, v_b, &e_new); BMO_elem_flag_enable(bm, v, SUBD_SPLIT | ELE_SPLIT); BMO_elem_flag_enable(bm, eed, SUBD_SPLIT | ELE_SPLIT); @@ -434,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); @@ -577,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); @@ -685,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); @@ -1168,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_utils.c b/source/blender/bmesh/operators/bmo_utils.c index da1991a187d..964d0b1dfc6 100644 --- a/source/blender/bmesh/operators/bmo_utils.c +++ b/source/blender/bmesh/operators/bmo_utils.c @@ -208,7 +208,7 @@ static void bmo_region_extend_expand( BMEdge *e; BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { - if (!BMO_elem_flag_test(bm, e, SEL_ORIG)) { + if (!BMO_elem_flag_test(bm, e, SEL_ORIG) && !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { found = true; break; } @@ -221,7 +221,7 @@ static void bmo_region_extend_expand( BMEdge *e; BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { - if (!BMO_elem_flag_test(bm, e, SEL_FLAG)) { + 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); } @@ -232,7 +232,7 @@ static void bmo_region_extend_expand( BMFace *f; BM_ITER_ELEM (f, &fiter, v, BM_FACES_OF_VERT) { - if (!BMO_elem_flag_test(bm, f, SEL_FLAG)) { + 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); } } @@ -243,7 +243,7 @@ static void bmo_region_extend_expand( 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)) { + 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); } @@ -267,7 +267,9 @@ static void bmo_region_extend_expand( 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)) { + 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); } } @@ -277,7 +279,9 @@ static void bmo_region_extend_expand( 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)) { + 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); } } diff --git a/source/blender/bmesh/tools/bmesh_beautify.c b/source/blender/bmesh/tools/bmesh_beautify.c index 990a2108bd7..19fe492c670 100644 --- a/source/blender/bmesh/tools/bmesh_beautify.c +++ b/source/blender/bmesh/tools/bmesh_beautify.c @@ -274,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); @@ -316,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; @@ -333,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( @@ -389,11 +391,11 @@ void BM_mesh_beautify_fill( 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_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 2ef93618e2a..3348afa3bfa 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -57,6 +57,9 @@ /* happens far too often, uncomment for development */ // #define BEVEL_ASSERT_PROJECT +/* will likely remove the code enabled by this soon, when sure that it is not needed */ +// #define PRE_275_ALGORITHM + /* for testing */ // #pragma GCC diagnostic error "-Wpadded" @@ -187,7 +190,7 @@ typedef struct BevelParams { 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 +247,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; @@ -361,8 +365,9 @@ static BMFace *boundvert_rep_face(BoundVert *v) * * \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, int mat_nr, bool do_interp) { BMIter iter; BMLoop *l; @@ -402,15 +407,17 @@ 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_tri( + BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4, + BMFace *facerep, int mat_nr, bool do_interp) { BMVert *varr[4] = {v1, v2, v3, v4}; return bev_create_ngon(bm, varr, v4 ? 4 : 3, NULL, facerep, mat_nr, do_interp); } -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_tri_ex( + 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}; BMFace *farr[4] = {f1, f2, f3, f4}; @@ -419,8 +426,9 @@ static BMFace *bev_create_quad_tri_ex(BMesh *bm, BMVert *v1, BMVert *v2, BMVert /* 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,7 +486,9 @@ 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. +/** + * Like bev_create_quad_tri, but when verts straddle an old edge. + * <pre> * e * | * v1+---|---+v4 @@ -487,13 +497,16 @@ static bool contig_ldata_across_edge(BMesh *bm, BMEdge *e, BMFace *f1, BMFace *f * v2+---|---+v3 * | * f1 | f2 + * </pre> * * 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, + * 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; @@ -582,10 +595,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_BIG); +} + /* * 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 @@ -594,16 +639,27 @@ 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) { /* special case: e1 and e2 are parallel; put offset point perp to both, from v. @@ -643,14 +699,31 @@ static void offset_meet(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f, float * 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 (!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); @@ -662,11 +735,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 colinear: 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) @@ -686,15 +758,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.0 && e2->offset_l > 0.0) { + /* 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_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; @@ -706,7 +801,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; @@ -717,10 +812,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); @@ -729,6 +825,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. @@ -737,8 +844,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]; @@ -787,14 +895,16 @@ static void offset_on_edge_between(BevelParams *bp, EdgeHalf *e1, EdgeHalf *e2, e2->offset_l = d; } +#ifdef PRE_275_ALGORITHM /* 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 offset_on_edge_between. */ -static void offset_in_two_planes(BevelParams *bp, EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid, - BMVert *v, float meetco[3]) +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], @@ -861,6 +971,7 @@ static void offset_in_two_planes(BevelParams *bp, EdgeHalf *e1, EdgeHalf *e2, Ed /* else iret == 1 and the lines are coplanar so meetco has the intersection */ } } +#endif /* 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 @@ -909,11 +1020,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); @@ -947,18 +1058,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); } @@ -1060,8 +1190,9 @@ 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]; @@ -1109,8 +1240,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); @@ -1391,6 +1523,325 @@ static void set_bound_vert_seams(BevVert *bv) } while ((v = v->next) != bv->vmesh->boundstart); } +#ifndef PRE_275_ALGORITHM +/* 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 || + fabsf(dot - 1.0f) <= BEVEL_EPSILON) + { + 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) +{ + BoundVert *v; + + 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]; + + BLI_assert(bp->vertex_only); + + 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; + } +} + +/* 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; + + 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) { + v = add_new_bound_vert(mem_arena, vm, co); + v->efirst = v->elast = v->ebev = e; + e->leftv = v; + } + else { + adjust_bound_vert(e->leftv, co); + } + no = e->fnext ? e->fnext->no : (e->fprev ? e->fprev->no : NULL); + offset_in_plane(e, no, false, co); + if (construct) { + v = add_new_bound_vert(mem_arena, vm, co); + v->efirst = v->elast = e; + e->rightv = v; + } + else { + adjust_bound_vert(e->rightv, co); + } + /* make artifical extra point along unbeveled edge, and form triangle */ + slide_dist(e->next, bv->v, e->offset_l, co); + if (construct) { + v = add_new_bound_vert(mem_arena, vm, co); + v->efirst = v->elast = e->next; + e->next->leftv = e->next->rightv = v; + /* could use M_POLY too, but tri-fan looks nicer)*/ + vm->mesh_kind = M_TRI_FAN; + set_bound_vert_seams(bv); + } + else { + adjust_bound_vert(e->next->leftv, 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 { + 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); + } + d = len_v3v3(bv->v->co, co); + 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 { + adjust_bound_vert(e->leftv, co); + } + } + } + calculate_vm_profiles(bp, bv, vm); + + if (bv->edgecount >= 3) { + /* special case: snap profile to plane of adjacent two edges */ + v = vm->boundstart; + BLI_assert(v->ebev != NULL); + move_profile_plane(v, v->efirst, v->next->elast); + calculate_profile(bp, v); + } + + if (construct) { + set_bound_vert_seams(bv); + + if (vm->count == 2 && bv->edgecount == 3) { + vm->mesh_kind = M_NONE; + } + else if (vm->count == 3) { + vm->mesh_kind = M_TRI_FAN; + } + else { + vm->mesh_kind = M_POLY; + } + } +} + +/* 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 */ + 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 (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 (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, NULL, 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; + } + } +} +#endif + +#ifdef PRE_275_ALGORITHM +/* This code was used prior to just before the 2.75 Blender release. + * It treated multiple non-beveled edges between beveled ones differently */ + /* 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 @@ -1474,7 +1925,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) else { adjust_bound_vert(e->next->leftv, co); } - set_profile_params(bp, vm->boundstart); + set_profile_params(bp, bv, vm->boundstart); calculate_profile(bp, vm->boundstart); return; } @@ -1485,7 +1936,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) /* 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); + 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; @@ -1522,7 +1973,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) } 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); + 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; @@ -1546,7 +1997,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) } else if (e->prev->is_bev) { /* on-edge meet between e->prev and e */ - offset_meet(e->prev, e, bv->v, e->fprev, co); + 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; @@ -1580,7 +2031,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) v = vm->boundstart; do { - set_profile_params(bp, v); + set_profile_params(bp, bv, v); calculate_profile(bp, v); } while ((v = v->next) != vm->boundstart); @@ -1623,6 +2074,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) } } } +#endif /* Do a global pass to try to make offsets as even as possible. * Consider this graph: @@ -1842,9 +2294,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); @@ -2759,7 +3212,7 @@ static void bevel_vert_two_edges(BevelParams *bp, BMesh *bm, BevVert *bv) copy_mesh_vert(vm, 1, 0, ns - k, 0, 0, k); } - if (BM_vert_face_count(bv->v) == 0) { + 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++) { @@ -3736,10 +4189,11 @@ 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) { BMIter iter; BMVert *v, *v_next; diff --git a/source/blender/bmesh/tools/bmesh_bevel.h b/source/blender/bmesh/tools/bmesh_bevel.h index 52d8faa5401..b4bb6c56b7d 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); #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 e6e33c905da..fbcf573acd9 100644 --- a/source/blender/bmesh/tools/bmesh_bisect_plane.c +++ b/source/blender/bmesh/tools/bmesh_bisect_plane.c @@ -106,7 +106,7 @@ 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; } 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 811a144fc39..c3533245d9b 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) > 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; @@ -695,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; @@ -782,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); @@ -831,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); @@ -847,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; @@ -897,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 */ @@ -914,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 */ @@ -935,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); } @@ -957,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); } } } @@ -981,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 */ @@ -1009,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; @@ -1031,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 064dbd7405b..fc12bce8563 100644 --- a/source/blender/bmesh/tools/bmesh_intersect.c +++ b/source/blender/bmesh/tools/bmesh_intersect.c @@ -804,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; @@ -1006,7 +1006,7 @@ bool BM_mesh_intersect( !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); @@ -1040,8 +1040,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]; @@ -1228,7 +1228,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]); } } } @@ -1267,10 +1267,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 8ae3507a738..6633803414b 100644 --- a/source/blender/bmesh/tools/bmesh_path.c +++ b/source/blender/bmesh/tools/bmesh_path.c @@ -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: @@ -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: @@ -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_region_match.c b/source/blender/bmesh/tools/bmesh_region_match.c index bb7000e5534..72c3bc90599 100644 --- a/source/blender/bmesh/tools/bmesh_region_match.c +++ b/source/blender/bmesh/tools/bmesh_region_match.c @@ -190,14 +190,16 @@ static bool ghashutil_bmelem_indexcmp(const void *a, const void *b) return (a != b); } -static GHash *ghash_bmelem_new_ex(const char *info, - const unsigned int nentries_reserve) +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) +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); } @@ -419,8 +421,8 @@ static void bm_uuidwalk_rehash( UUID_Int *uuid_store; unsigned int i; - unsigned int rehash_store_len_new = (unsigned int)MAX2(BLI_ghash_size(uuidwalk->verts_uuid), - BLI_ghash_size(uuidwalk->faces_uuid)); + 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; @@ -534,12 +536,13 @@ static void bm_uuidwalk_pass_add( 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_haskey(verts_uuid_pass, 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); - BLI_ghash_insert(verts_uuid_pass, l_iter->v, (void *)uuid); + *val_p = (void *)uuid; } /* fill faces_step_next */ @@ -614,15 +617,16 @@ static unsigned int bm_uuidwalk_init_from_edge( /* 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; i++) { + 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); + bm_uuidwalk_pass_add(uuidwalk, faces_pass, i - i_init); BLI_linklist_free_pool(faces_pass, NULL, uuidwalk->link_pool); fstep_num += 1; } @@ -667,13 +671,15 @@ static bool bm_uuidwalk_facestep_begin( 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; - fstep_item = BLI_ghash_lookup(uuidwalk->cache.faces_from_uuid, (void *)uuid); - if (UNLIKELY(fstep_item == NULL)) { - fstep_item = BLI_mempool_alloc(uuidwalk->step_pool_items); - BLI_ghash_insert(uuidwalk->cache.faces_from_uuid, (void *)uuid, fstep_item); + 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); @@ -858,7 +864,7 @@ static BMFace **bm_mesh_region_match_pair( break; } - found = ((unsigned int)BLI_ghash_size(w_dst->faces_uuid) == faces_src_region_len); + found = (BLI_ghash_size(w_dst->faces_uuid) == faces_src_region_len); if (found) { break; } @@ -871,7 +877,7 @@ static BMFace **bm_mesh_region_match_pair( if (found) { GHashIterator gh_iter; - const unsigned int faces_result_len = (unsigned int)BLI_ghash_size(w_dst->faces_uuid); + 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__); @@ -1111,9 +1117,10 @@ static BMEdge *bm_face_region_pivot_edge_find( if (bm_edge_is_region_boundary(e)) { unsigned int j; for (j = 0; j < 2; j++) { - if (!BLI_ghash_haskey(gh, (&e->v1)[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]); - BLI_ghash_insert(gh, (&e->v1)[j], (void *)v_id); + *val_p = (void *)v_id; BLI_LINKSTACK_PUSH(vert_queue_prev, (&e->v1)[j]); vert_queue_used += 1; } @@ -1137,10 +1144,11 @@ static BMEdge *bm_face_region_pivot_edge_find( 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)) { - if (!BLI_ghash_haskey(gh, v_other)) { + 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); - BLI_ghash_insert(gh, v_other, (void *)v_id_other); + *val_p = (void *)v_id_other; BLI_LINKSTACK_PUSH(vert_queue_next, v_other); vert_queue_used += 1; } @@ -1451,7 +1459,7 @@ int BM_mesh_region_match( BMFace **faces_result; unsigned int faces_result_len_out; - if (BM_elem_flag_test(e_dst, BM_ELEM_TAG)) { + if (BM_elem_flag_test(e_dst, BM_ELEM_TAG) || BM_edge_is_wire(e_dst)) { continue; } diff --git a/source/blender/bmesh/tools/bmesh_triangulate.c b/source/blender/bmesh/tools/bmesh_triangulate.c index 404776a0769..6f2aaf28179 100644 --- a/source/blender/bmesh/tools/bmesh_triangulate.c +++ b/source/blender/bmesh/tools/bmesh_triangulate.c @@ -34,7 +34,6 @@ #include "BLI_utildefines.h" #include "BLI_alloca.h" #include "BLI_memarena.h" -#include "BLI_listbase.h" #include "BLI_heap.h" #include "BLI_edgehash.h" 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 |