[Bf-blender-cvs] [ae1c1cd8c0] master: Refactor Mesh split_faces() code to use loop normal spaces.

Bastien Montagne noreply at git.blender.org
Wed Feb 22 09:57:43 CET 2017


Commit: ae1c1cd8c0da0c4b86efc171d5a6e34fbfb50d67
Author: Bastien Montagne
Date:   Wed Feb 22 09:40:46 2017 +0100
Branches: master
https://developer.blender.org/rBae1c1cd8c0da0c4b86efc171d5a6e34fbfb50d67

Refactor Mesh split_faces() code to use loop normal spaces.

Finding which loop should share its vertex with which others is not easy
with regular Mesh data (mostly due to lack of advanced topology info, as
opposed with BMesh case).

Custom loop normals computing already does that - and can return 'loop
normal spaces', which among other things contain definitions of 'smooth
fans' of loops around vertices.

Using those makes it easy to find vertices (and then edges) that needs
splitting.

This commit also adds support of non-autosmooth meshes, where we want to
split out flat faces from smooth ones.

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

M	source/blender/blenkernel/BKE_mesh.h
M	source/blender/blenkernel/intern/mesh.c
M	source/blender/blenkernel/intern/mesh_evaluate.c

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

diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index d41878825b..e42692622f 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -131,7 +131,6 @@ bool BKE_mesh_uv_cdlayer_rename(struct Mesh *me, const char *old_name, const cha
 
 float (*BKE_mesh_vertexCos_get(const struct Mesh *me, int *r_numVerts))[3];
 
-void BKE_mesh_calc_normals_split(struct Mesh *mesh);
 void BKE_mesh_split_faces(struct Mesh *mesh);
 
 struct Mesh *BKE_mesh_new_from_object(struct Main *bmain, struct Scene *sce, struct Object *ob,
@@ -228,6 +227,9 @@ void BKE_lnor_space_custom_normal_to_data(MLoopNorSpace *lnor_space, const float
 
 bool BKE_mesh_has_custom_loop_normals(struct Mesh *me);
 
+void BKE_mesh_calc_normals_split(struct Mesh *mesh);
+void BKE_mesh_calc_normals_split_ex(struct Mesh *mesh, struct MLoopNorSpaceArray *r_lnors_spacearr);
+
 void BKE_mesh_normals_loop_split(
         const struct MVert *mverts, const int numVerts, struct MEdge *medges, const int numEdges,
         struct MLoop *mloops, float (*r_loopnors)[3], const int numLoops,
diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index 73fcd7615f..7a27c43e28 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -39,7 +39,9 @@
 
 #include "BLI_utildefines.h"
 #include "BLI_math.h"
+#include "BLI_linklist.h"
 #include "BLI_listbase.h"
+#include "BLI_memarena.h"
 #include "BLI_edgehash.h"
 #include "BLI_string.h"
 
@@ -2053,7 +2055,7 @@ void BKE_mesh_mselect_active_set(Mesh *me, int index, int type)
 	           (me->mselect[me->totselect - 1].type  == type));
 }
 
-void BKE_mesh_calc_normals_split(Mesh *mesh)
+void BKE_mesh_calc_normals_split_ex(Mesh *mesh, MLoopNorSpaceArray *r_lnors_spacearr)
 {
 	float (*r_loopnors)[3];
 	float (*polynors)[3];
@@ -2088,251 +2090,249 @@ void BKE_mesh_calc_normals_split(Mesh *mesh)
 	BKE_mesh_normals_loop_split(
 	        mesh->mvert, mesh->totvert, mesh->medge, mesh->totedge,
 	        mesh->mloop, r_loopnors, mesh->totloop, mesh->mpoly, (const float (*)[3])polynors, mesh->totpoly,
-	        (mesh->flag & ME_AUTOSMOOTH) != 0, mesh->smoothresh, NULL, clnors, NULL);
+	        (mesh->flag & ME_AUTOSMOOTH) != 0, mesh->smoothresh, r_lnors_spacearr, clnors, NULL);
 
 	if (free_polynors) {
 		MEM_freeN(polynors);
 	}
 }
 
-/* Split faces helper functions. */
-
-enum {
-	/* Vertex is adjacent to some loop which normal is different,
-	 * hence split of this vertex is required.
-	 */
-	SPLIT_VERT_NEED_SPLIT = (1 << 0),
-	/* Original vertex was already re-used by split logic. */
-	SPLIT_VERT_REUSED     = (1 << 1),
-};
-enum {
-	/* Edge is adjacent to any of vertex tagged for split.
-	 */
-	SPLIT_EDGE_NEED_SPLIT = (1 << 0),
-	/* Original edge was already re-used by split logic. */
-	SPLIT_EDGE_REUSED     = (1 << 1),
-};
-
-/* Tag vertices which normals are not equal to any adjacent loop
- * and hence split on that vertex is required.
- *
- * Returns truth if any of vertex needs to be split.
- */
-static bool split_faces_tag_verts(const Mesh *mesh, uchar *vert_flags)
+void BKE_mesh_calc_normals_split(Mesh *mesh)
 {
-	const int num_polys = mesh->totpoly;
-	const MVert *mvert = mesh->mvert;
-	const MLoop *mloop = mesh->mloop;
-	const MPoly *mpoly = mesh->mpoly;
-	float (*lnors)[3] = CustomData_get_layer(&mesh->ldata, CD_NORMAL);
-	bool has_split_verts = false;
-	for (int poly = 0; poly < num_polys; poly++) {
-		const MPoly *mp = &mpoly[poly];
-		for (int loop = 0; loop < mp->totloop; loop++) {
-			const MLoop *ml = &mloop[mp->loopstart + loop];
-			const MVert *mv = &mvert[ml->v];
-			float vn[3];
-			normal_short_to_float_v3(vn, mv->no);
-			if (len_squared_v3v3(vn, lnors[mp->loopstart + loop]) > FLT_EPSILON) {
-				vert_flags[ml->v] |= SPLIT_VERT_NEED_SPLIT;
-				has_split_verts = true;
-			}
-		}
-	}
-	return has_split_verts;
+	BKE_mesh_calc_normals_split_ex(mesh, NULL);
 }
 
-/* Count number of new vertices to be added.
- *
- * Note that one of the loop where split is required will re-use
- * it's vertex in order to avoid creation of loose vertices.
- */
-static int split_faces_count_new_verts(const Mesh *mesh, uchar *vert_flags)
-{
-	const int num_polys = mesh->totpoly;
-	const MLoop *mloop = mesh->mloop;
-	const MPoly *mpoly = mesh->mpoly;
-	int num_new_verts = 0;
-	for (int poly = 0; poly < num_polys; poly++) {
-		const MPoly *mp = &mpoly[poly];
-		for (int loop = 0; loop < mp->totloop; loop++) {
-			const MLoop *ml = &mloop[mp->loopstart + loop];
-			if (vert_flags[ml->v] & SPLIT_VERT_NEED_SPLIT) {
-				if (vert_flags[ml->v] & SPLIT_VERT_REUSED) {
-					++num_new_verts;
+/* Split faces helper functions. */
+
+typedef struct SplitFaceNewVert {
+	struct SplitFaceNewVert *next;
+	int new_index;
+	int orig_index;
+	float *vnor;
+} SplitFaceNewVert;
+
+typedef struct SplitFaceNewEdge {
+	struct SplitFaceNewEdge *next;
+	int new_index;
+	int orig_index;
+	int v1;
+	int v2;
+} SplitFaceNewEdge;
+
+/* Detect needed new vertices, and update accordingly loops' vertex indices.
+ * WARNING! Leaves mesh in invalid state. */
+static int split_faces_prepare_new_verts(
+        const Mesh *mesh, MLoopNorSpaceArray *lnors_spacearr, SplitFaceNewVert **new_verts, MemArena *memarena,
+        bool *r_need_vnors_recalc)
+{
+	/* Note: if lnors_spacearr is NULL, ther is no autosmooth handling, and we only split out flat polys. */
+	const int num_loops = mesh->totloop;
+	int num_verts = mesh->totvert;
+	MVert *mvert = mesh->mvert;
+	MLoop *mloop = mesh->mloop;
+
+	BLI_bitmap *verts_used = BLI_BITMAP_NEW(num_verts, __func__);
+
+	if (lnors_spacearr) {
+		BLI_bitmap *done_loops = BLI_BITMAP_NEW(num_loops, __func__);
+
+		MLoop *ml = mloop;
+		MLoopNorSpace **lnor_space = lnors_spacearr->lspacearr;
+		for (int loop_idx = 0; loop_idx < num_loops; loop_idx++, ml++, lnor_space++) {
+			if (!BLI_BITMAP_TEST(done_loops, loop_idx)) {
+				const int vert_idx = ml->v;
+				const bool vert_used = BLI_BITMAP_TEST_BOOL(verts_used, vert_idx);
+				/* If vert is already used by another smooth fan, we need a new vert for this one. */
+				const int new_vert_idx = vert_used ? num_verts++ : vert_idx;
+
+				if ((*lnor_space)->loops) {
+					for (LinkNode *lnode = (*lnor_space)->loops; lnode; lnode = lnode->next) {
+						const int ml_fan_idx = GET_INT_FROM_POINTER(lnode->link);
+						BLI_BITMAP_ENABLE(done_loops, ml_fan_idx);
+						if (vert_used) {
+							mloop[ml_fan_idx].v = new_vert_idx;
+						}
+					}
 				}
 				else {
-					vert_flags[ml->v] |= SPLIT_VERT_REUSED;
+					/* Single loop in this fan... */
+					BLI_BITMAP_ENABLE(done_loops, loop_idx);
+					if (vert_used) {
+						ml->v = new_vert_idx;
+					}
 				}
-			}
-		}
-	}
-	return num_new_verts;
-}
 
-/* Tag edges which are adjacent to at least one vertex tagged for split. */
-static void split_faces_tag_edges(Mesh *mesh,
-                                  const uchar *vert_flags,
-                                  uchar *edge_flags)
-{
-	const int num_polys = mesh->totpoly;
-	const MLoop *mloop = mesh->mloop;
-	const MPoly *mpoly = mesh->mpoly;
-	for (int poly = 0; poly < num_polys; poly++) {
-		const MPoly *mp = &mpoly[poly];
-		int loop_prev = mp->totloop - 1;
-		for (int loop = 0; loop < mp->totloop; loop++) {
-			const int poly_loop_prev = mp->loopstart + loop_prev;
-			const MLoop *ml = &mloop[mp->loopstart + loop];
-			const MLoop *ml_prev = &mloop[poly_loop_prev];
-			const int mv_flag = vert_flags[ml->v];
-			const int mv_prev_flag = vert_flags[ml_prev->v];
-			bool need_split = false;
-			if (mv_flag & SPLIT_VERT_NEED_SPLIT) {
-				if (mv_prev_flag & SPLIT_VERT_NEED_SPLIT) {
-					/* Create new edge between twp split vertices. */
-					need_split = true;
+				if (!vert_used) {
+					BLI_BITMAP_ENABLE(verts_used, vert_idx);
+					/* We need to update that vertex's normal here, we won't go over it again. */
+					/* This is important! *DO NOT* set vnor to final computed lnor, vnor should always be defined to
+					 * 'automatic normal' value computed from its polys, not some custom normal.
+					 * Fortunately, that's the loop normal space's 'lnor' reference vector. ;) */
+					normal_float_to_short_v3(mvert[vert_idx].no, (*lnor_space)->vec_lnor);
 				}
 				else {
-					/* Create new edge from existing vertex to a split one. */
-					need_split = true;
+					/* Add new vert to list. */
+					SplitFaceNewVert *new_vert = BLI_memarena_alloc(memarena, sizeof(*new_vert));
+					new_vert->orig_index = vert_idx;
+					new_vert->new_index = new_vert_idx;
+					new_vert->vnor = (*lnor_space)->vec_lnor;  /* See note above. */
+					new_vert->next = *new_verts;
+					*new_verts = new_vert;
 				}
 			}
-			else if (mv_prev_flag & SPLIT_VERT_NEED_SPLIT) {
-				/* Create new edge from split vertex to existing one. */
-				need_split = true;
+		}
+
+		MEM_freeN(done_loops);
+	}
+	else {
+		/* No loop normal spaces available, we only split out flat polys. */
+		const int num_polys = mesh->totpoly;
+		const MPoly *mpoly = mesh->mpoly;
+
+		/* We do that in two loops, to keep original edges/verts to smooth polys preferencially. */
+		const MPoly *mp = mpoly;
+		for (int i = 0; i < num_polys; i++, mp++) {
+			if (mp->flag & ME_SMOOTH) {
+				const MLoop *ml = &mloop[mp->loopstart];
+				for (int j = 0; j < mp->totloop; j++, ml++) {
+					/* Just mark the vertex as used/reserved, that way neighbor flat polys, if any,
+					 * will have to create their own. */
+					BLI_BITMAP_ENABLE(verts_used, ml->v);
+				}
 			}
-			if (need_split) {
-				edge_flags[ml_prev->e] |= SPLIT_EDGE_NEED_SPLIT;
+		}
+
+		mp = mpoly;
+		for (int i = 0; i < num_polys; i++, mp++) {
+			if (!(mp->flag & ME_SMOOTH)) {
+				MLoop *ml = &mloop[mp->loopstart];
+				for (int j = 0; j < mp->totloop; j++, ml++) {
+					const int vert_idx = ml->v;
+
+					if (BLI_BITMAP_TEST(verts_used, vert_idx)) {
+						/* Add new vert to list. */
+						const int new_vert_idx = num_verts++;
+						ml->v = new_vert_idx;
+
+						SplitFaceNewVert *new_vert = BLI_memarena_alloc(memarena, sizeof(*new_vert));
+						new_vert->orig_index = vert_idx;
+						new_vert->new_index = new_vert_idx;
+						new_vert->vnor = NULL;  /* See note belo

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list