Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/bmesh/intern/bmesh_mesh.c')
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.c448
1 files changed, 444 insertions, 4 deletions
diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c
index 442cd9275ec..a454fb7e948 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.c
+++ b/source/blender/bmesh/intern/bmesh_mesh.c
@@ -30,6 +30,7 @@
#include "DNA_listBase.h"
#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
#include "BLI_linklist_stack.h"
#include "BLI_listbase.h"
@@ -260,6 +261,11 @@ void BM_mesh_data_free(BMesh *bm)
BLI_freelistN(&bm->selected);
+ if (bm->lnor_spacearr) {
+ BKE_lnor_spacearr_free(bm->lnor_spacearr);
+ MEM_freeN(bm->lnor_spacearr);
+ }
+
BMO_error_clear(bm);
}
@@ -313,6 +319,9 @@ void BM_mesh_free(BMesh *bm)
* Helpers for #BM_mesh_normals_update and #BM_verts_calc_normal_vcos
*/
+/* We use that existing internal API flag, assuming no other tool using it would run concurrently to clnors editing. */
+#define BM_LNORSPACE_UPDATE _FLAG_MF
+
typedef struct BMEdgesCalcVectorsData {
/* Read-only data. */
const float (*vcos)[3];
@@ -638,7 +647,8 @@ bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr)
* Will use first clnors_data array, and fallback to cd_loop_clnors_offset (use NULL and -1 to not use clnors). */
static void bm_mesh_loops_calc_normals(
BMesh *bm, const float (*vcos)[3], const float (*fnos)[3], float (*r_lnos)[3],
- MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset)
+ MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2],
+ const int cd_loop_clnors_offset, const bool do_rebuild)
{
BMIter fiter;
BMFace *f_curr;
@@ -694,6 +704,11 @@ static void bm_mesh_loops_calc_normals(
l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
do {
+ if (do_rebuild && !BM_ELEM_API_FLAG_TEST(l_curr, BM_LNORSPACE_UPDATE) &&
+ !(bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL))
+ {
+ continue;
+ }
/* A smooth edge, we have to check for cyclic smooth fan case.
* If we find a new, never-processed cyclic smooth fan, we can do it now using that loop/edge as
* 'entry point', otherwise we can skip it. */
@@ -894,7 +909,10 @@ static void bm_mesh_loops_calc_normals(
clnors_avg[0] /= clnors_nbr;
clnors_avg[1] /= clnors_nbr;
/* Fix/update all clnors of this fan with computed average value. */
- printf("Invalid clnors in this fan!\n");
+
+ /* Prints continuously when merge custom normals, so commenting. */
+ /* printf("Invalid clnors in this fan!\n"); */
+
while ((clnor = BLI_SMALLSTACK_POP(clnors))) {
//print_v2("org clnor", clnor);
clnor[0] = (short)clnors_avg[0];
@@ -1009,7 +1027,8 @@ void BM_mesh_loop_normals_update(
void BM_loops_calc_normal_vcos(
BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*fnos)[3],
const bool use_split_normals, const float split_angle, float (*r_lnos)[3],
- MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset)
+ MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2],
+ const int cd_loop_clnors_offset, const bool do_rebuild)
{
const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
@@ -1019,7 +1038,8 @@ void BM_loops_calc_normal_vcos(
bm_mesh_edges_sharp_tag(bm, vnos, fnos, r_lnos, has_clnors ? (float)M_PI : split_angle, false);
/* Finish computing lnos by accumulating face normals in each fan of faces defined by sharp edges. */
- bm_mesh_loops_calc_normals(bm, vcos, fnos, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset);
+ bm_mesh_loops_calc_normals(
+ bm, vcos, fnos, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset, do_rebuild);
}
else {
BLI_assert(!r_lnors_spacearr);
@@ -1041,6 +1061,422 @@ void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle)
bm_mesh_edges_sharp_tag(bm, NULL, NULL, NULL, split_angle, true);
}
+void BM_lnorspacearr_store(BMesh *bm, float(*r_lnors)[3])
+{
+ BLI_assert(bm->lnor_spacearr != NULL);
+
+ if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
+ BM_data_layer_add(bm, &bm->ldata, CD_CUSTOMLOOPNORMAL);
+ }
+
+ int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+
+ BM_loops_calc_normal_vcos(
+ bm, NULL, NULL, NULL, true, M_PI, r_lnors, bm->lnor_spacearr, NULL, cd_loop_clnors_offset, false);
+ bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
+}
+
+#define CLEAR_SPACEARRAY_THRESHOLD(x) ((x) / 2)
+
+void BM_lnorspace_invalidate(BMesh *bm, const bool do_invalidate_all)
+{
+ if (bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
+ return;
+ }
+ if (do_invalidate_all || bm->totvertsel > CLEAR_SPACEARRAY_THRESHOLD(bm->totvert)) {
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
+ return;
+ }
+ if (bm->lnor_spacearr == NULL) {
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
+ return;
+ }
+
+ BMVert *v;
+ BMLoop *l;
+ BMIter viter, liter;
+ /* Note: we could use temp tag of BMItem for that, but probably better not use it in such a low-level func?
+ * --mont29 */
+ BLI_bitmap *done_verts = BLI_BITMAP_NEW(bm->totvert, __func__);
+
+ BM_mesh_elem_index_ensure(bm, BM_VERT);
+
+ /* When we affect a given vertex, we may affect following smooth fans:
+ * - all smooth fans of said vertex;
+ * - all smooth fans of all immediate loop-neighbors vertices;
+ * This can be simplified as 'all loops of selected vertices and their immediate neighbors'
+ * need to be tagged for update.
+ */
+ BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
+ BM_ITER_ELEM(l, &liter, v, BM_LOOPS_OF_VERT) {
+ BM_ELEM_API_FLAG_ENABLE(l, BM_LNORSPACE_UPDATE);
+
+ /* Note that we only handle unselected neighbor vertices here, main loop will take care of
+ * selected ones. */
+ if ((!BM_elem_flag_test(l->prev->v, BM_ELEM_SELECT)) &&
+ !BLI_BITMAP_TEST(done_verts, BM_elem_index_get(l->prev->v)))
+ {
+
+ BMLoop *l_prev;
+ BMIter liter_prev;
+ BM_ITER_ELEM(l_prev, &liter_prev, l->prev->v, BM_LOOPS_OF_VERT) {
+ BM_ELEM_API_FLAG_ENABLE(l_prev, BM_LNORSPACE_UPDATE);
+ }
+ BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(l_prev->v));
+ }
+
+ if ((!BM_elem_flag_test(l->next->v, BM_ELEM_SELECT)) &&
+ !BLI_BITMAP_TEST(done_verts, BM_elem_index_get(l->next->v)))
+ {
+
+ BMLoop *l_next;
+ BMIter liter_next;
+ BM_ITER_ELEM(l_next, &liter_next, l->next->v, BM_LOOPS_OF_VERT) {
+ BM_ELEM_API_FLAG_ENABLE(l_next, BM_LNORSPACE_UPDATE);
+ }
+ BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(l_next->v));
+ }
+ }
+
+ BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(v));
+ }
+ }
+
+ MEM_freeN(done_verts);
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY;
+}
+
+void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor)
+{
+ BLI_assert(bm->lnor_spacearr != NULL);
+
+ if (!(bm->spacearr_dirty & (BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL))) {
+ return;
+ }
+ BMFace *f;
+ BMLoop *l;
+ BMIter fiter, liter;
+
+ float(*r_lnors)[3] = MEM_callocN(sizeof(*r_lnors) * bm->totloop, __func__);
+ float(*oldnors)[3] = preserve_clnor ? MEM_mallocN(sizeof(*oldnors) * bm->totloop, __func__) : NULL;
+
+ int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ if (preserve_clnor) {
+ BLI_assert(bm->lnor_spacearr->lspacearr != NULL);
+
+ BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM(l, &liter, f, BM_LOOPS_OF_FACE) {
+ if (BM_ELEM_API_FLAG_TEST(l, BM_LNORSPACE_UPDATE) || bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
+ short(*clnor)[2] = BM_ELEM_CD_GET_VOID_P(l, cd_loop_clnors_offset);
+ int l_index = BM_elem_index_get(l);
+
+ BKE_lnor_space_custom_data_to_normal(
+ bm->lnor_spacearr->lspacearr[l_index], *clnor,
+ oldnors[l_index]);
+ }
+ }
+ }
+ }
+
+ if (bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
+ BKE_lnor_spacearr_clear(bm->lnor_spacearr);
+ }
+ BM_loops_calc_normal_vcos(
+ bm, NULL, NULL, NULL, true, M_PI, r_lnors, bm->lnor_spacearr, NULL, cd_loop_clnors_offset, true);
+ MEM_freeN(r_lnors);
+
+ BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM(l, &liter, f, BM_LOOPS_OF_FACE) {
+ if (BM_ELEM_API_FLAG_TEST(l, BM_LNORSPACE_UPDATE) || bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
+ if (preserve_clnor) {
+ short(*clnor)[2] = BM_ELEM_CD_GET_VOID_P(l, cd_loop_clnors_offset);
+ int l_index = BM_elem_index_get(l);
+ BKE_lnor_space_custom_normal_to_data(
+ bm->lnor_spacearr->lspacearr[l_index], oldnors[l_index],
+ *clnor);
+ }
+ BM_ELEM_API_FLAG_DISABLE(l, BM_LNORSPACE_UPDATE);
+ }
+ }
+ }
+
+ MEM_SAFE_FREE(oldnors);
+ bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
+
+#ifndef NDEBUG
+ BM_lnorspace_err(bm);
+#endif
+}
+
+void BM_lnorspace_update(BMesh *bm)
+{
+ if (bm->lnor_spacearr == NULL) {
+ bm->lnor_spacearr = MEM_callocN(sizeof(*bm->lnor_spacearr), __func__);
+ }
+ if (bm->lnor_spacearr->lspacearr == NULL) {
+ float(*lnors)[3] = MEM_callocN(sizeof(*lnors) * bm->totloop, __func__);
+
+ BM_lnorspacearr_store(bm, lnors);
+
+ MEM_freeN(lnors);
+ }
+ else if (bm->spacearr_dirty & (BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL)) {
+ BM_lnorspace_rebuild(bm, false);
+ }
+}
+
+void BM_normals_loops_edges_tag(BMesh *bm, const bool do_edges)
+{
+ BMFace *f;
+ BMEdge *e;
+ BMIter fiter, eiter;
+ BMLoop *l_curr, *l_first;
+
+ if (do_edges) {
+ int index_edge;
+ BM_ITER_MESH_INDEX(e, &eiter, bm, BM_EDGES_OF_MESH, index_edge) {
+ BMLoop *l_a, *l_b;
+
+ BM_elem_index_set(e, index_edge); /* set_inline */
+ BM_elem_flag_disable(e, BM_ELEM_TAG);
+ if (BM_edge_loop_pair(e, &l_a, &l_b)) {
+ if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) && l_a->v != l_b->v) {
+ BM_elem_flag_enable(e, BM_ELEM_TAG);
+ }
+ }
+ }
+ bm->elem_index_dirty &= ~BM_EDGE;
+ }
+
+ int index_face, index_loop = 0;
+ BM_ITER_MESH_INDEX(f, &fiter, bm, BM_FACES_OF_MESH, index_face) {
+ BM_elem_index_set(f, index_face); /* set_inline */
+ l_curr = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ BM_elem_index_set(l_curr, index_loop++); /* set_inline */
+ BM_elem_flag_disable(l_curr, BM_ELEM_TAG);
+ } while ((l_curr = l_curr->next) != l_first);
+ }
+ bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP);
+}
+
+/**
+* Auxillary function only used by rebuild to detect if any spaces were not marked as invalid.
+* Reports error if any of the lnor spaces change after rebuilding, meaning that all the possible
+* lnor spaces to be rebuilt were not correctly marked.
+*/
+#ifndef NDEBUG
+void BM_lnorspace_err(BMesh *bm)
+{
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
+ bool clear = true;
+
+ MLoopNorSpaceArray *temp = MEM_callocN(sizeof(*temp), __func__);
+ temp->lspacearr = NULL;
+
+ BKE_lnor_spacearr_init(temp, bm->totloop, MLNOR_SPACEARR_BMLOOP_PTR);
+
+ int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+ float(*lnors)[3] = MEM_callocN(sizeof(*lnors) * bm->totloop, __func__);
+ BM_loops_calc_normal_vcos(bm, NULL, NULL, NULL, true, M_PI, lnors, temp, NULL, cd_loop_clnors_offset, true);
+
+ for (int i = 0; i < bm->totloop; i++) {
+ int j = 0;
+ j += compare_ff(temp->lspacearr[i]->ref_alpha, bm->lnor_spacearr->lspacearr[i]->ref_alpha, 1e-4f);
+ j += compare_ff(temp->lspacearr[i]->ref_beta, bm->lnor_spacearr->lspacearr[i]->ref_beta, 1e-4f);
+ j += compare_v3v3(temp->lspacearr[i]->vec_lnor, bm->lnor_spacearr->lspacearr[i]->vec_lnor, 1e-4f);
+ j += compare_v3v3(temp->lspacearr[i]->vec_ortho, bm->lnor_spacearr->lspacearr[i]->vec_ortho, 1e-4f);
+ j += compare_v3v3(temp->lspacearr[i]->vec_ref, bm->lnor_spacearr->lspacearr[i]->vec_ref, 1e-4f);
+
+ if (j != 5) {
+ clear = false;
+ break;
+ }
+ }
+ BKE_lnor_spacearr_free(temp);
+ MEM_freeN(temp);
+ MEM_freeN(lnors);
+ BLI_assert(clear);
+
+ bm->spacearr_dirty &= ~BM_SPACEARR_DIRTY_ALL;
+}
+#endif
+
+static void bm_loop_normal_mark_indiv_do_loop(
+ BMLoop *l, BLI_bitmap *loops, MLoopNorSpaceArray *lnor_spacearr, int *totloopsel)
+{
+ if (l != NULL) {
+ const int l_idx = BM_elem_index_get(l);
+
+ if (!BLI_BITMAP_TEST(loops, BM_elem_index_get(l))) {
+ /* If vert and face selected share a loop, mark it for editing. */
+ BLI_BITMAP_ENABLE(loops, l_idx);
+ (*totloopsel)++;
+
+ /* Mark all loops in same loop normal space (aka smooth fan). */
+ if ((lnor_spacearr->lspacearr[l_idx]->flags & MLNOR_SPACE_IS_SINGLE) == 0) {
+ for (LinkNode *node = lnor_spacearr->lspacearr[l_idx]->loops; node; node = node->next) {
+ const int lfan_idx = BM_elem_index_get((BMLoop *)node->link);
+ if (!BLI_BITMAP_TEST(loops, lfan_idx)) {
+ BLI_BITMAP_ENABLE(loops, lfan_idx);
+ (*totloopsel)++;
+ }
+ }
+ }
+ }
+ }
+}
+
+/* Mark the individual clnors to be edited, if multiple selection methods are used. */
+static int bm_loop_normal_mark_indiv(BMesh *bm, BLI_bitmap *loops)
+{
+ BMEditSelection *ese, *ese_prev;
+ int totloopsel = 0;
+
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ BLI_assert(bm->lnor_spacearr != NULL);
+ BLI_assert(bm->lnor_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
+
+ /* Goes from last selected to the first selected element. */
+ for (ese = bm->selected.last; ese; ese = ese->prev) {
+ if (ese->htype == BM_FACE) {
+ ese_prev = ese;
+ /* If current face is selected, then any verts to be edited must have been selected before it. */
+ while ((ese_prev = ese_prev->prev)) {
+ if (ese_prev->htype == BM_VERT) {
+ bm_loop_normal_mark_indiv_do_loop(
+ BM_face_vert_share_loop((BMFace *)ese->ele, (BMVert *)ese_prev->ele),
+ loops, bm->lnor_spacearr, &totloopsel);
+ }
+ else if (ese_prev->htype == BM_EDGE) {
+ bm_loop_normal_mark_indiv_do_loop(
+ BM_face_vert_share_loop((BMFace *)ese->ele, ((BMEdge *)ese_prev->ele)->v1),
+ loops, bm->lnor_spacearr, &totloopsel);
+
+ bm_loop_normal_mark_indiv_do_loop(
+ BM_face_vert_share_loop((BMFace *)ese->ele, ((BMEdge *)ese_prev->ele)->v2),
+ loops, bm->lnor_spacearr, &totloopsel);
+ }
+ }
+ }
+ }
+
+ return totloopsel;
+}
+
+static void loop_normal_editdata_init(BMesh *bm, BMLoopNorEditData *lnor_ed, BMVert *v, BMLoop *l, const int offset)
+{
+ BLI_assert(bm->lnor_spacearr != NULL);
+ BLI_assert(bm->lnor_spacearr->lspacearr != NULL);
+
+ const int l_index = BM_elem_index_get(l);
+ short *clnors_data = BM_ELEM_CD_GET_VOID_P(l, offset);
+
+ lnor_ed->loop_index = l_index;
+ lnor_ed->loop = l;
+
+ float custom_normal[3];
+ BKE_lnor_space_custom_data_to_normal(bm->lnor_spacearr->lspacearr[l_index], clnors_data, custom_normal);
+
+ lnor_ed->clnors_data = clnors_data;
+ copy_v3_v3(lnor_ed->nloc, custom_normal);
+ copy_v3_v3(lnor_ed->niloc, custom_normal);
+
+ lnor_ed->loc = v->co;
+}
+
+BMLoopNorEditDataArray *BM_loop_normal_editdata_array_init(BMesh *bm)
+{
+ BMLoop *l;
+ BMVert *v;
+ BMIter liter, viter;
+
+ bool verts = (bm->selectmode & SCE_SELECT_VERTEX) != 0;
+ bool edges = (bm->selectmode & SCE_SELECT_EDGE) != 0;
+ bool faces = (bm->selectmode & SCE_SELECT_FACE) != 0;
+ int totloopsel = 0;
+
+ BLI_assert(bm->spacearr_dirty == 0);
+
+ BMLoopNorEditDataArray *lnors_ed_arr = MEM_mallocN(
+ sizeof(*lnors_ed_arr), __func__);
+ lnors_ed_arr->lidx_to_lnor_editdata = MEM_callocN(
+ sizeof(*lnors_ed_arr->lidx_to_lnor_editdata) * bm->totloop, __func__);
+
+ if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
+ BM_data_layer_add(bm, &bm->ldata, CD_CUSTOMLOOPNORMAL);
+ }
+ const int cd_custom_normal_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ BLI_bitmap *loops = BLI_BITMAP_NEW(bm->totloop, __func__);
+ if (faces && (verts || edges)) {
+ /* More than one selection mode, check for individual normals to edit. */
+ totloopsel = bm_loop_normal_mark_indiv(bm, loops);
+ }
+
+ if (totloopsel) {
+ BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata = MEM_mallocN(sizeof(*lnor_ed) * totloopsel, __func__);
+
+ BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) {
+ BM_ITER_ELEM(l, &liter, v, BM_LOOPS_OF_VERT) {
+ if (BLI_BITMAP_TEST(loops, BM_elem_index_get(l))) {
+ loop_normal_editdata_init(bm, lnor_ed, v, l, cd_custom_normal_offset);
+ lnors_ed_arr->lidx_to_lnor_editdata[BM_elem_index_get(l)] = lnor_ed;
+ lnor_ed++;
+ }
+ }
+ }
+ lnors_ed_arr->totloop = totloopsel;
+ }
+ else { /* If multiple selection modes are inactive OR no such loop is found, fall back to editing all loops. */
+ totloopsel = BM_total_loop_select(bm);
+ BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata = MEM_mallocN(sizeof(*lnor_ed) * totloopsel, __func__);
+
+ BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
+ BM_ITER_ELEM(l, &liter, v, BM_LOOPS_OF_VERT) {
+ loop_normal_editdata_init(bm, lnor_ed, v, l, cd_custom_normal_offset);
+ lnors_ed_arr->lidx_to_lnor_editdata[BM_elem_index_get(l)] = lnor_ed;
+ lnor_ed++;
+ }
+ }
+ }
+ lnors_ed_arr->totloop = totloopsel;
+ }
+
+ MEM_freeN(loops);
+ lnors_ed_arr->cd_custom_normal_offset = cd_custom_normal_offset;
+ return lnors_ed_arr;
+}
+
+void BM_loop_normal_editdata_array_free(BMLoopNorEditDataArray *lnors_ed_arr)
+{
+ MEM_SAFE_FREE(lnors_ed_arr->lnor_editdata);
+ MEM_SAFE_FREE(lnors_ed_arr->lidx_to_lnor_editdata);
+ MEM_freeN(lnors_ed_arr);
+}
+
+int BM_total_loop_select(BMesh *bm)
+{
+ int r_sel = 0;
+ BMVert *v;
+ BMIter viter;
+
+ BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
+ r_sel += BM_vert_face_count(v);
+ }
+ }
+ return r_sel;
+}
+
/**
* \brief BMesh Begin Edit
*
@@ -1089,6 +1525,7 @@ void bmesh_edit_end(BMesh *bm, BMOpTypeFlag type_flag)
/* compute normals, clear temp flags and flush selections */
if (type_flag & BMO_OPTYPE_FLAG_NORMALS_CALC) {
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
BM_mesh_normals_update(bm);
}
@@ -1105,6 +1542,9 @@ void bmesh_edit_end(BMesh *bm, BMOpTypeFlag type_flag)
if ((type_flag & BMO_OPTYPE_FLAG_SELECT_VALIDATE) == 0) {
bm->selected = select_history;
}
+ if (type_flag & BMO_OPTYPE_FLAG_INVALIDATE_CLNOR_ALL) {
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
+ }
}
void BM_mesh_elem_index_ensure_ex(BMesh *bm, const char htype, int elem_offset[4])