[Bf-blender-cvs] [57cff46] master: BMesh Decimate: support ngons

Campbell Barton noreply at git.blender.org
Wed Jun 15 20:46:01 CEST 2016


Commit: 57cff46cec9599e5897de72f45ce735da79db0ff
Author: Campbell Barton
Date:   Thu Jun 16 04:27:48 2016 +1000
Branches: master
https://developer.blender.org/rB57cff46cec9599e5897de72f45ce735da79db0ff

BMesh Decimate: support ngons

===================================================================

M	source/blender/bmesh/tools/bmesh_decimate_collapse.c

===================================================================

diff --git a/source/blender/bmesh/tools/bmesh_decimate_collapse.c b/source/blender/bmesh/tools/bmesh_decimate_collapse.c
index 589b6d4..fe8b132 100644
--- a/source/blender/bmesh/tools/bmesh_decimate_collapse.c
+++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.c
@@ -34,6 +34,14 @@
 #include "BLI_math.h"
 #include "BLI_quadric.h"
 #include "BLI_heap.h"
+#include "BLI_linklist.h"
+#include "BLI_alloca.h"
+#include "BLI_memarena.h"
+#include "BLI_edgehash.h"
+#include "BLI_polyfill2d.h"
+#include "BLI_polyfill2d_beautify.h"
+#include "BLI_stackdefines.h"
+
 
 #include "BKE_customdata.h"
 
@@ -59,9 +67,6 @@
 #  define   TOPOLOGY_FALLBACK_EPS  1e-12f
 #endif
 
-/* these checks are for rare cases that we can't avoid since they are valid meshes still */
-#define USE_SAFETY_CHECKS
-
 #define BOUNDARY_PRESERVE_WEIGHT 100.0f
 #define OPTIMIZE_EPS 0.01f  /* FLT_EPSILON is too small, see [#33106] */
 #define COST_INVALID FLT_MAX
@@ -473,12 +478,58 @@ static int *bm_edge_symmetry_map(BMesh *bm, unsigned int symmetry_axis, float li
  *
  * \return true if any faces were triangulated.
  */
+static bool bm_face_triangulate(
+        BMesh *bm, BMFace *f_base, LinkNode **r_faces_double, int *r_edges_tri_tot,
 
-static bool bm_decim_triangulate_begin(BMesh *bm)
+        MemArena *pf_arena,
+        /* use for MOD_TRIANGULATE_NGON_BEAUTY only! */
+        struct Heap *pf_heap, struct EdgeHash *pf_ehash)
+{
+	const int f_base_len = f_base->len;
+	int faces_array_tot = f_base_len - 3;
+	int edges_array_tot = f_base_len - 3;
+	BMFace  **faces_array = BLI_array_alloca(faces_array, faces_array_tot);
+	BMEdge  **edges_array = BLI_array_alloca(edges_array, edges_array_tot);
+	const int quad_method = 0, ngon_method = 0;  /* beauty */
+
+	bool has_cut = false;
+
+	const int f_index = BM_elem_index_get(f_base);
+
+	BM_face_triangulate(
+	        bm, f_base,
+	        faces_array, &faces_array_tot,
+	        edges_array, &edges_array_tot,
+	        r_faces_double,
+	        quad_method, ngon_method, false,
+	        pf_arena,
+	        pf_heap, pf_ehash);
+
+	for (int i = 0; i < edges_array_tot; i++) {
+		BMLoop *l_iter, *l_first;
+		l_iter = l_first = edges_array[i]->l;
+		do {
+			BM_elem_index_set(l_iter, f_index);  /* set_dirty */
+			has_cut = true;
+		} while ((l_iter = l_iter->radial_next) != l_first);
+	}
+
+	for (int i = 0; i < faces_array_tot; i++) {
+		BM_face_normal_update(faces_array[i]);
+	}
+
+	*r_edges_tri_tot += edges_array_tot;
+
+	return has_cut;
+}
+
+
+static bool bm_decim_triangulate_begin(BMesh *bm, int *r_edges_tri_tot)
 {
 	BMIter iter;
 	BMFace *f;
-	// bool has_quad;  // could optimize this a little
+	bool has_quad;
+	bool has_ngon;
 	bool has_cut = false;
 
 	BLI_assert((bm->elem_index_dirty & BM_VERT) == 0);
@@ -493,98 +544,103 @@ static bool bm_decim_triangulate_begin(BMesh *bm)
 			BM_elem_index_set(l_iter, -1);  /* set_dirty */
 		} while ((l_iter = l_iter->next) != l_first);
 
-		// has_quad |= (f->len == 4)
+		has_quad |= (f->len > 3);
+		has_ngon |= (f->len > 4);
 	}
 
 	bm->elem_index_dirty |= BM_LOOP;
 
-	/* adding new faces as we loop over faces
-	 * is normally best avoided, however in this case its not so bad because any face touched twice
-	 * will already be triangulated*/
-	BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
-		if (f->len == 4) {
-			BMLoop *f_l[4];
-			BMLoop *l_a, *l_b;
+	{
+		MemArena *pf_arena;
+		Heap *pf_heap;
+		EdgeHash *pf_ehash;
 
-			{
-				BMLoop *l_iter = BM_FACE_FIRST_LOOP(f);
+		LinkNode *faces_double = NULL;
 
-				f_l[0] = l_iter; l_iter = l_iter->next;
-				f_l[1] = l_iter; l_iter = l_iter->next;
-				f_l[2] = l_iter; l_iter = l_iter->next;
-				f_l[3] = l_iter;
-			}
+		if (has_ngon) {
+			pf_arena = BLI_memarena_new(BLI_POLYFILL_ARENA_SIZE, __func__);
+			pf_heap = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE);
+			pf_ehash = BLI_edgehash_new_ex(__func__, BLI_POLYFILL_ALLOC_NGON_RESERVE);
+		}
+		else {
+			pf_arena = NULL;
+			pf_heap = NULL;
+			pf_ehash = NULL;
+		}
 
-			if (len_squared_v3v3(f_l[0]->v->co, f_l[2]->v->co) <
-			    len_squared_v3v3(f_l[1]->v->co, f_l[3]->v->co))
-			{
-				l_a = f_l[0];
-				l_b = f_l[2];
+		/* adding new faces as we loop over faces
+		 * is normally best avoided, however in this case its not so bad because any face touched twice
+		 * will already be triangulated*/
+		BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+			if (f->len > 3) {
+				has_cut |= bm_face_triangulate(
+				        bm, f, &faces_double,
+				        r_edges_tri_tot,
+
+				        pf_arena,
+				        pf_heap, pf_ehash);
 			}
-			else {
-				l_a = f_l[1];
-				l_b = f_l[3];
-			}
-
-#ifdef USE_SAFETY_CHECKS
-			if (BM_edge_exists(l_a->v, l_b->v) == NULL)
-#endif
-			{
-				BMFace *f_new;
-				BMLoop *l_new;
-
-				/* warning, NO_DOUBLE option here isn't handled as nice as it could be
-				 * - if there is a quad that has a free standing edge joining it along
-				 * where we want to split the face, there isnt a good way we can handle this.
-				 * currently that edge will get removed when joining the tris back into a quad. */
-				f_new = BM_face_split(bm, f, l_a, l_b, &l_new, NULL, false);
-
-				if (f_new) {
-					/* the value of this doesn't matter, only that the 2 loops match and have unique values */
-					const int f_index = BM_elem_index_get(f);
-
-					/* since we just split theres only ever 2 loops */
-					BLI_assert(BM_edge_is_manifold(l_new->e));
+		}
 
-					BM_elem_index_set(l_new, f_index);  /* set_dirty */
-					BM_elem_index_set(l_new->radial_next, f_index);  /* set_dirty */
+		while (faces_double) {
+			LinkNode *next = faces_double->next;
+			BM_face_kill(bm, faces_double->link);
+			MEM_freeN(faces_double);
+			faces_double = next;
+		}
 
-					BM_face_normal_update(f);
-					BM_face_normal_update(f_new);
+		BLI_memarena_free(pf_arena);
 
-					has_cut = true;
-				}
-			}
+		if (has_ngon) {
+			BLI_heap_free(pf_heap, NULL);
+			BLI_edgehash_free(pf_ehash, NULL);
 		}
-	}
 
-	BLI_assert((bm->elem_index_dirty & BM_VERT) == 0);
+		BLI_assert((bm->elem_index_dirty & BM_VERT) == 0);
 
-	if (has_cut) {
-		/* now triangulation is done we need to correct index values */
-		BM_mesh_elem_index_ensure(bm, BM_EDGE | BM_FACE);
+		if (has_cut) {
+			/* now triangulation is done we need to correct index values */
+			BM_mesh_elem_index_ensure(bm, BM_EDGE | BM_FACE);
+		}
 	}
 
 	return has_cut;
 }
 
-static void bm_decim_triangulate_end(BMesh *bm)
+
+static void bm_decim_triangulate_end(BMesh *bm, const int edges_tri_tot)
 {
 	/* decimation finished, now re-join */
 	BMIter iter;
-	BMEdge *e, *e_next;
+	BMEdge *e;
+
+	/* we need to collect before merging for ngons since the loops indices will be lost */
+	BMEdge **edges_tri = MEM_mallocN(MIN2(edges_tri_tot, bm->totedge) * sizeof(*edges_tri), __func__);
+	STACK_DECLARE(edges_tri);
 
 	/* boundary edges */
-	BM_ITER_MESH_MUTABLE (e, e_next, &iter, bm, BM_EDGES_OF_MESH) {
+	BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
 		BMLoop *l_a, *l_b;
 		if (BM_edge_loop_pair(e, &l_a, &l_b)) {
 			const int l_a_index = BM_elem_index_get(l_a);
 			if (l_a_index != -1) {
 				const int l_b_index = BM_elem_index_get(l_b);
 				if (l_a_index == l_b_index) {
-					if (LIKELY(l_a->f->len == 3 && l_b->f->len == 3)) {
-						if (l_a->v != l_b->v) {  /* if this is the case, faces have become flipped */
-							/* check we are not making a degenerate quad */
+					if (l_a->v != l_b->v) {  /* if this is the case, faces have become flipped */
+						/* check we are not making a degenerate quad */
+
+#define CAN_LOOP_MERGE(l) \
+	(BM_loop_is_manifold(l) && \
+	 ((l)->v != (l)->radial_next->v) && \
+	 (l_a_index == BM_elem_index_get(l)) && \
+	 (l_a_index == BM_elem_index_get((l)->radial_next)))
+
+						if ((l_a->f->len == 3 && l_b->f->len == 3) &&
+						    (!CAN_LOOP_MERGE(l_a->next)) &&
+						    (!CAN_LOOP_MERGE(l_a->prev)) &&
+						    (!CAN_LOOP_MERGE(l_b->next)) &&
+						    (!CAN_LOOP_MERGE(l_b->prev)))
+						{
 							BMVert *vquad[4] = {
 								e->v1,
 								BM_vert_in_edge(e, l_a->next->v) ? l_a->prev->v : l_a->next->v,
@@ -597,17 +653,32 @@ static void bm_decim_triangulate_end(BMesh *bm)
 							BLI_assert(ELEM(vquad[2], vquad[1], vquad[0], vquad[3]) == false);
 							BLI_assert(ELEM(vquad[3], vquad[1], vquad[2], vquad[0]) == false);
 
-							if (is_quad_convex_v3(vquad[0]->co, vquad[1]->co, vquad[2]->co, vquad[3]->co)) {
-								/* highly unlikely to fail, but prevents possible double-ups */
-								BMFace *f[2] = {l_a->f, l_b->f};
-								BM_faces_join(bm, f, 2, true);
+							if (!is_quad_convex_v3(vquad[0]->co, vquad[1]->co, vquad[2]->co, vquad[3]->co)) {
+								continue;
 							}
 						}
+#undef CAN_LOOP_MERGE
+
+						/* highly unlikely to fail, but prevents possible double-ups */
+						STACK_PUSH(edges_tri, e);
 					}
 				}
 			}
 		}
 	}
+
+	for (int i = 0; i < STACK_SIZE(edges_tri); i++) {
+		BMLoop *l_a, *l_b;
+		e = edges_tri[i];
+		if (BM_edge_loop_pair(e, &l_a, &l_b)) {
+			BMFace *f_array[2] = {l_a->f, l_b->f};
+			BM_faces_join(bm, f_array, 2, false);
+			if (e->l == NULL) {
+				BM_edge_kill(bm, e);
+			}
+		}
+	}
+	MEM_freeN(edges_tri);
 }
 
 #endif  /* USE_TRIANGULATE */
@@ -1220,7 +1291,6 @@ void BM_mesh_decimate_collapse(
 	Quadric *vquadrics;      /* vert index aligned quadrics */
 	int tot_edge_orig;
 	int face_tot_target;
-	bool use_triangulate;
 
 	CD_UseFlag customdata_flag = 0;
 
@@ -1230,8 +1300,11 @@ void BM_mesh_decimate_collapse(
 #endif
 
 #ifdef USE_TRIANGULATE
+	int edges_tri_tot = 0;
 	/* temp convert quads to triangles */
-	use_triangulate = bm_decim_triangulate_begin(bm);
+	bool use_triangulate = bm_decim_triangulate_begin(bm, &edges_tri_tot);
+#else
+	UNUSED_VARS(do_triangulate);
 #endif
 
 
@@ -1416,7 +1489,7 @@ invalidate:
 		/* its possible we only had triangles, skip this step in that case */
 		if (LIKELY(use_triangulate)) {
 			/* temp convert quads to triangles */
-			bm_decim_triangulate_end(bm);
+			bm_decim_triangulate_end(bm, edges_tri_tot);
 		}
 	}
 #endif




More information about the Bf-blender-cvs mailing list