/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2007 Blender Foundation. All rights reserved. */ /** \file * \ingroup bmesh * * BM construction functions. */ #include "MEM_guardedalloc.h" #include "BLI_alloca.h" #include "BLI_math.h" #include "BLI_sort_utils.h" #include "BKE_customdata.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "bmesh.h" #include "intern/bmesh_private.h" bool BM_verts_from_edges(BMVert **vert_arr, BMEdge **edge_arr, const int len) { int i, i_prev = len - 1; for (i = 0; i < len; i++) { vert_arr[i] = BM_edge_share_vert(edge_arr[i_prev], edge_arr[i]); if (vert_arr[i] == NULL) { return false; } i_prev = i; } return true; } 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; } 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 *bm_src, BMesh *bm_dst, const BMLoop *l_src, BMLoop *l_dst, eCustomDataMask mask_exclude); 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); } void BM_face_copy_shared(BMesh *bm, BMFace *f, BMLoopFilterFunc filter_fn, void *user_data) { BMLoop *l_first; BMLoop *l_iter; #ifdef DEBUG l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { BLI_assert(BM_ELEM_API_FLAG_TEST(l_iter, _FLAG_OVERLAP) == 0); } while ((l_iter = l_iter->next) != l_first); #endif l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { BMLoop *l_other = l_iter->radial_next; if (l_other && l_other != l_iter) { BMLoop *l_src[2]; BMLoop *l_dst[2] = {l_iter, l_iter->next}; uint j; if (l_other->v == l_iter->v) { l_src[0] = l_other; l_src[1] = l_other->next; } else { l_src[0] = l_other->next; l_src[1] = l_other; } for (j = 0; j < 2; j++) { BLI_assert(l_dst[j]->v == l_src[j]->v); if (BM_ELEM_API_FLAG_TEST(l_dst[j], _FLAG_OVERLAP) == 0) { if ((filter_fn == NULL) || filter_fn(l_src[j], user_data)) { bm_loop_attrs_copy(bm, bm, l_src[j], l_dst[j], 0x0); BM_ELEM_API_FLAG_ENABLE(l_dst[j], _FLAG_OVERLAP); } } } } } while ((l_iter = l_iter->next) != l_first); l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { BM_ELEM_API_FLAG_DISABLE(l_iter, _FLAG_OVERLAP); } while ((l_iter = l_iter->next) != l_first); } /** * Given an array of edges, * order them using the winding defined by \a v1 & \a v2 * into \a edges_sort & \a verts_sort. * * All arrays must be \a len long. */ static bool bm_edges_sort_winding(BMVert *v1, BMVert *v2, BMEdge **edges, const int len, BMEdge **edges_sort, BMVert **verts_sort) { BMEdge *e_iter, *e_first; BMVert *v_iter; int i; /* 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); } /* find first edge */ i = 0; v_iter = v1; e_iter = e_first = v1->e; do { 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; } 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; } BM_ELEM_API_FLAG_DISABLE(e_iter, _FLAG_MF); edges_sort[i] = e_iter; BM_ELEM_API_FLAG_DISABLE(v_iter, _FLAG_MV); verts_sort[i] = v_iter; i += 1; /* 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; } e_first = e_iter; } } while ((e_iter = bmesh_disk_edge_next(e_iter, v_iter)) != e_first); if (i == len) { return true; } error: for (i = 0; i < len; i++) { 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); } return false; } 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); BLI_assert(len && v1 && v2 && edges && bm); 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; } 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); uint winding[2] = {0, 0}; int i, i_prev = len - 1; BMVert *v_winding[2] = {vert_arr[i_prev], vert_arr[0]}; BLI_assert(len > 2); for (i = 0; i < len; i++) { if (create_edges) { edge_arr[i] = BM_edge_create(bm, vert_arr[i_prev], vert_arr[i], NULL, BM_CREATE_NO_DOUBLE); } else { edge_arr[i] = BM_edge_exists(vert_arr[i_prev], vert_arr[i]); if (edge_arr[i] == NULL) { return NULL; } } if (calc_winding) { /* the edge may exist already and be attached to a face * in this case we can find the best winding to use for the new face */ if (edge_arr[i]->l) { BMVert *test_v1, *test_v2; /* we want to use the reverse winding to the existing order */ BM_edge_ordered_verts(edge_arr[i], &test_v2, &test_v1); winding[(vert_arr[i_prev] == test_v2)]++; BLI_assert(ELEM(vert_arr[i_prev], test_v2, test_v1)); } } i_prev = i; } /* --- */ if (calc_winding) { if (winding[0] < winding[1]) { winding[0] = 1; winding[1] = 0; } else { winding[0] = 0; winding[1] = 1; } } else { winding[0] = 0; winding[1] = 1; } /* --- */ /* create the face */ return BM_face_create_ngon( bm, v_winding[winding[0]], v_winding[winding[1]], edge_arr, len, f_example, create_flag); } void BM_verts_sort_radial_plane(BMVert **vert_arr, int len) { struct SortIntByFloat *vang = BLI_array_alloca(vang, len); BMVert **vert_arr_map = BLI_array_alloca(vert_arr_map, len); float nor[3], cent[3]; int index_tangent = 0; BM_verts_calc_normal_from_cloud_ex(vert_arr, len, nor, cent, &index_tangent); const float *far = vert_arr[index_tangent]->co; /* Now calculate every points angle around the normal (signed). */ for (int i = 0; i < len; i++) { vang[i].sort_value = angle_signed_on_axis_v3v3v3_v3(far, cent, vert_arr[i]->co, nor); vang[i].data = i; vert_arr_map[i] = vert_arr[i]; } /* sort by angle and magic! - we have our ngon */ qsort(vang, len, sizeof(*vang), BLI_sortutil_cmp_float); /* --- */ for (int i = 0; i < len; i++) { vert_arr[i] = vert_arr_map[vang[i].data]; } } /*************************************************************/ static void bm_vert_attrs_copy( BMesh *bm_src, BMesh *bm_dst, const BMVert *v_src, BMVert *v_dst, eCustomDataMask mask_exclude) { if ((bm_src == bm_dst) && (v_src == v_dst)) { BLI_assert_msg(0, "BMVert: source and target match"); return; } if ((mask_exclude & CD_MASK_NORMAL) == 0) { copy_v3_v3(v_dst->no, v_src->no); } CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->vdata, v_dst->head.data, mask_exclude); CustomData_bmesh_copy_data_exclude_by_type( &bm_src->vdata, &bm_dst->vdata, v_src->head.data, &v_dst->head.data, mask_exclude); } static void bm_edge_attrs_copy( BMesh *bm_src, BMesh *bm_dst, const BMEdge *e_src, BMEdge *e_dst, eCustomDataMask mask_exclude) { if ((bm_src == bm_dst) && (e_src == e_dst)) { BLI_assert_msg(0, "BMEdge: source and target match"); return; } CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->edata, e_dst->head.data, mask_exclude); CustomData_bmesh_copy_data_exclude_by_type( &bm_src->edata, &bm_dst->edata, e_src->head.data, &e_dst->head.data, mask_exclude); } static void bm_loop_attrs_copy( BMesh *bm_src, BMesh *bm_dst, const BMLoop *l_src, BMLoop *l_dst, eCustomDataMask mask_exclude) { if ((bm_src == bm_dst) && (l_src == l_dst)) { BLI_assert_msg(0, "BMLoop: source and target match"); return; } CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->ldata, l_dst->head.data, mask_exclude); CustomData_bmesh_copy_data_exclude_by_type( &bm_src->ldata, &bm_dst->ldata, l_src->head.data, &l_dst->head.data, mask_exclude); } static void bm_face_attrs_copy( BMesh *bm_src, BMesh *bm_dst, const BMFace *f_src, BMFace *f_dst, eCustomDataMask mask_exclude) { if ((bm_src == bm_dst) && (f_src == f_dst)) { BLI_assert_msg(0, "BMFace: source and target match"); return; } if ((mask_exclude & CD_MASK_NORMAL) == 0) { copy_v3_v3(f_dst->no, f_src->no); } CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->pdata, f_dst->head.data, mask_exclude); CustomData_bmesh_copy_data_exclude_by_type( &bm_src->pdata, &bm_dst->pdata, f_src->head.data, &f_dst->head.data, mask_exclude); f_dst->mat_nr = f_src->mat_nr; } 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 uint64_t cd_mask_exclude) { /* TODO: Special handling for hide flags? */ /* TODO: swap src/dst args, everywhere else in bmesh does other way round. */ const BMHeader *ele_src = ele_src_v; BMHeader *ele_dst = ele_dst_v; BLI_assert(ele_src->htype == ele_dst->htype); BLI_assert(ele_src != ele_dst); if ((hflag_mask & BM_ELEM_SELECT) == 0) { /* First we copy select */ if (BM_elem_flag_test((BMElem *)ele_src, BM_ELEM_SELECT)) { BM_elem_select_set(bm_dst, (BMElem *)ele_dst, true); } } /* Now we copy flags */ if (hflag_mask == 0) { ele_dst->hflag = ele_src->hflag; } else if (hflag_mask == 0xff) { /* pass */ } else { ele_dst->hflag = ((ele_dst->hflag & hflag_mask) | (ele_src->hflag & ~hflag_mask)); } /* Copy specific attributes */ switch (ele_dst->htype) { case BM_VERT: bm_vert_attrs_copy( bm_src, bm_dst, (const BMVert *)ele_src, (BMVert *)ele_dst, cd_mask_exclude); break; case BM_EDGE: bm_edge_attrs_copy( bm_src, bm_dst, (const BMEdge *)ele_src, (BMEdge *)ele_dst, cd_mask_exclude); break; case BM_LOOP: bm_loop_attrs_copy( bm_src, bm_dst, (const BMLoop *)ele_src, (BMLoop *)ele_dst, cd_mask_exclude); break; case BM_FACE: bm_face_attrs_copy( bm_src, bm_dst, (const BMFace *)ele_src, (BMFace *)ele_dst, cd_mask_exclude); break; default: BLI_assert(0); break; } } void BM_elem_attrs_copy(BMesh *bm_src, BMesh *bm_dst, const void *ele_src, void *ele_dst) { /* BMESH_TODO, default 'use_flags' to false */ BM_elem_attrs_copy_ex(bm_src, bm_dst, ele_src, ele_dst, BM_ELEM_SELECT, 0x0); } void BM_elem_select_copy(BMesh *bm_dst, void *ele_dst_v, const void *ele_src_v) { BMHeader *ele_dst = ele_dst_v; const BMHeader *ele_src = ele_src_v; BLI_assert(ele_src->htype == ele_dst->htype); if ((ele_src->hflag & BM_ELEM_SELECT) != (ele_dst->hflag & BM_ELEM_SELECT)) { BM_elem_select_set(bm_dst, (BMElem *)ele_dst, (ele_src->hflag & BM_ELEM_SELECT) != 0); } } /* 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) { BMLoop **loops = BLI_array_alloca(loops, f->len); BMVert **verts = BLI_array_alloca(verts, f->len); BMEdge **edges = BLI_array_alloca(edges, f->len); BMFace *f_new; BMLoop *l_iter, *l_first; int j; j = 0; l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { loops[j] = l_iter; verts[j] = vtable[BM_elem_index_get(l_iter->v)]; edges[j] = etable[BM_elem_index_get(l_iter->e)]; j++; } while ((l_iter = l_iter->next) != l_first); f_new = BM_face_create(bm_new, verts, edges, f->len, NULL, BM_CREATE_SKIP_CD); if (UNLIKELY(f_new == NULL)) { return NULL; } /* use totface in case adding some faces fails */ BM_elem_index_set(f_new, (bm_new->totface - 1)); /* set_inline */ BM_elem_attrs_copy_ex(bm_old, bm_new, f, f_new, 0xff, 0x0); f_new->head.hflag = f->head.hflag; /* low level! don't do this for normal api use */ j = 0; l_iter = l_first = BM_FACE_FIRST_LOOP(f_new); do { BM_elem_attrs_copy(bm_old, bm_new, loops[j], l_iter); j++; } while ((l_iter = l_iter->next) != l_first); return f_new; } void BM_mesh_copy_init_customdata_from_mesh_array(BMesh *bm_dst, const Mesh *me_src_array[], const int me_src_array_len, const BMAllocTemplate *allocsize) { if (allocsize == NULL) { allocsize = &bm_mesh_allocsize_default; } for (int i = 0; i < me_src_array_len; i++) { const Mesh *me_src = me_src_array[i]; CustomData mesh_vdata = CustomData_shallow_copy_remove_non_bmesh_attributes( &me_src->vdata, CD_MASK_BMESH.vmask); CustomData mesh_edata = CustomData_shallow_copy_remove_non_bmesh_attributes( &me_src->edata, CD_MASK_BMESH.emask); CustomData mesh_pdata = CustomData_shallow_copy_remove_non_bmesh_attributes( &me_src->pdata, CD_MASK_BMESH.lmask); CustomData mesh_ldata = CustomData_shallow_copy_remove_non_bmesh_attributes( &me_src->ldata, CD_MASK_BMESH.pmask); if (i == 0) { CustomData_copy(&mesh_vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0); CustomData_copy(&mesh_edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0); CustomData_copy(&mesh_pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0); CustomData_copy(&mesh_ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0); } else { CustomData_merge(&mesh_vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0); CustomData_merge(&mesh_edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0); CustomData_merge(&mesh_pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0); CustomData_merge(&mesh_ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0); } MEM_SAFE_FREE(mesh_vdata.layers); MEM_SAFE_FREE(mesh_edata.layers); MEM_SAFE_FREE(mesh_pdata.layers); MEM_SAFE_FREE(mesh_ldata.layers); } CustomData_bmesh_init_pool(&bm_dst->vdata, allocsize->totvert, BM_VERT); CustomData_bmesh_init_pool(&bm_dst->edata, allocsize->totedge, BM_EDGE); CustomData_bmesh_init_pool(&bm_dst->ldata, allocsize->totloop, BM_LOOP); CustomData_bmesh_init_pool(&bm_dst->pdata, allocsize->totface, BM_FACE); } void BM_mesh_copy_init_customdata_from_mesh(BMesh *bm_dst, const Mesh *me_src, const BMAllocTemplate *allocsize) { BM_mesh_copy_init_customdata_from_mesh_array(bm_dst, &me_src, 1, allocsize); } void BM_mesh_copy_init_customdata(BMesh *bm_dst, BMesh *bm_src, const BMAllocTemplate *allocsize) { if (allocsize == NULL) { allocsize = &bm_mesh_allocsize_default; } CustomData_copy(&bm_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0); CustomData_copy(&bm_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0); CustomData_copy(&bm_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0); CustomData_copy(&bm_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0); CustomData_bmesh_init_pool(&bm_dst->vdata, allocsize->totvert, BM_VERT); CustomData_bmesh_init_pool(&bm_dst->edata, allocsize->totedge, BM_EDGE); CustomData_bmesh_init_pool(&bm_dst->ldata, allocsize->totloop, BM_LOOP); CustomData_bmesh_init_pool(&bm_dst->pdata, allocsize->totface, BM_FACE); } void BM_mesh_copy_init_customdata_all_layers(BMesh *bm_dst, BMesh *bm_src, const char htype, const BMAllocTemplate *allocsize) { if (allocsize == NULL) { allocsize = &bm_mesh_allocsize_default; } const char htypes[4] = {BM_VERT, BM_EDGE, BM_LOOP, BM_FACE}; BLI_assert(((&bm_dst->vdata + 1) == &bm_dst->edata) && ((&bm_dst->vdata + 2) == &bm_dst->ldata) && ((&bm_dst->vdata + 3) == &bm_dst->pdata)); BLI_assert(((&allocsize->totvert + 1) == &allocsize->totedge) && ((&allocsize->totvert + 2) == &allocsize->totloop) && ((&allocsize->totvert + 3) == &allocsize->totface)); for (int i = 0; i < 4; i++) { if (!(htypes[i] & htype)) { continue; } CustomData *dst = &bm_dst->vdata + i; CustomData *src = &bm_src->vdata + i; const int size = *(&allocsize->totvert + i); for (int l = 0; l < src->totlayer; l++) { CustomData_add_layer_named( dst, src->layers[l].type, CD_SET_DEFAULT, NULL, 0, src->layers[l].name); } CustomData_bmesh_init_pool(dst, size, htypes[i]); } } BMesh *BM_mesh_copy(BMesh *bm_old) { BMesh *bm_new; BMVert *v, *v_new, **vtable = NULL; BMEdge *e, *e_new, **etable = NULL; BMFace *f, *f_new, **ftable = NULL; BMElem **eletable; BMEditSelection *ese; BMIter iter; int i; const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_BM(bm_old); /* allocate a bmesh */ bm_new = BM_mesh_create(&allocsize, &((struct BMeshCreateParams){ .use_toolflags = bm_old->use_toolflags, })); BM_mesh_copy_init_customdata(bm_new, bm_old, &allocsize); vtable = MEM_mallocN(sizeof(BMVert *) * bm_old->totvert, "BM_mesh_copy vtable"); etable = MEM_mallocN(sizeof(BMEdge *) * bm_old->totedge, "BM_mesh_copy etable"); ftable = MEM_mallocN(sizeof(BMFace *) * bm_old->totface, "BM_mesh_copy ftable"); BM_ITER_MESH_INDEX (v, &iter, bm_old, BM_VERTS_OF_MESH, i) { /* copy between meshes so can't use 'example' argument */ v_new = BM_vert_create(bm_new, v->co, NULL, BM_CREATE_SKIP_CD); BM_elem_attrs_copy_ex(bm_old, bm_new, v, v_new, 0xff, 0x0); v_new->head.hflag = v->head.hflag; /* low level! don't do this for normal api use */ vtable[i] = v_new; BM_elem_index_set(v, i); /* set_inline */ BM_elem_index_set(v_new, i); /* set_inline */ } bm_old->elem_index_dirty &= ~BM_VERT; bm_new->elem_index_dirty &= ~BM_VERT; /* safety check */ BLI_assert(i == bm_old->totvert); BM_ITER_MESH_INDEX (e, &iter, bm_old, BM_EDGES_OF_MESH, i) { e_new = BM_edge_create(bm_new, vtable[BM_elem_index_get(e->v1)], vtable[BM_elem_index_get(e->v2)], e, BM_CREATE_SKIP_CD); BM_elem_attrs_copy_ex(bm_old, bm_new, e, e_new, 0xff, 0x0); e_new->head.hflag = e->head.hflag; /* low level! don't do this for normal api use */ etable[i] = e_new; BM_elem_index_set(e, i); /* set_inline */ BM_elem_index_set(e_new, i); /* set_inline */ } bm_old->elem_index_dirty &= ~BM_EDGE; bm_new->elem_index_dirty &= ~BM_EDGE; /* safety check */ BLI_assert(i == bm_old->totedge); BM_ITER_MESH_INDEX (f, &iter, bm_old, BM_FACES_OF_MESH, i) { BM_elem_index_set(f, i); /* set_inline */ f_new = bm_mesh_copy_new_face(bm_new, bm_old, vtable, etable, f); ftable[i] = f_new; if (f == bm_old->act_face) { bm_new->act_face = f_new; } } bm_old->elem_index_dirty &= ~BM_FACE; bm_new->elem_index_dirty &= ~BM_FACE; /* low level! don't do this for normal api use */ bm_new->totvertsel = bm_old->totvertsel; bm_new->totedgesel = bm_old->totedgesel; bm_new->totfacesel = bm_old->totfacesel; /* safety check */ BLI_assert(i == bm_old->totface); /* copy over edit selection history */ for (ese = bm_old->selected.first; ese; ese = ese->next) { BMElem *ele = NULL; switch (ese->htype) { case BM_VERT: eletable = (BMElem **)vtable; break; case BM_EDGE: eletable = (BMElem **)etable; break; case BM_FACE: eletable = (BMElem **)ftable; break; default: eletable = NULL; break; } if (eletable) { ele = eletable[BM_elem_index_get(ese->ele)]; if (ele) { BM_select_history_store(bm_new, ele); } } } MEM_freeN(etable); MEM_freeN(vtable); MEM_freeN(ftable); /* Copy various settings. */ bm_new->shapenr = bm_old->shapenr; bm_new->selectmode = bm_old->selectmode; return bm_new; } char BM_edge_flag_from_mflag(const short mflag) { return (((mflag & ME_SEAM) ? BM_ELEM_SEAM : 0) | ((mflag & ME_EDGEDRAW) ? BM_ELEM_DRAW : 0) | ((mflag & ME_SHARP) == 0 ? BM_ELEM_SMOOTH : 0)); } char BM_face_flag_from_mflag(const char mflag) { return ((mflag & ME_SMOOTH) ? BM_ELEM_SMOOTH : 0); } short BM_edge_flag_to_mflag(BMEdge *e) { const char hflag = e->head.hflag; return (((hflag & BM_ELEM_SEAM) ? ME_SEAM : 0) | ((hflag & BM_ELEM_DRAW) ? ME_EDGEDRAW : 0) | ((hflag & BM_ELEM_SMOOTH) == 0 ? ME_SHARP : 0) | (BM_edge_is_wire(e) ? ME_LOOSEEDGE : 0) | /* not typical */ ME_EDGERENDER); } char BM_face_flag_to_mflag(BMFace *f) { const char hflag = f->head.hflag; return ((hflag & BM_ELEM_SMOOTH) ? ME_SMOOTH : 0); }