[Bf-blender-cvs] [25c5928b2b6] master: Refactor 'split faces' mesh code.

Bastien Montagne noreply at git.blender.org
Wed Aug 30 16:53:17 CEST 2017


Commit: 25c5928b2b6a71a426b123a81b605e2fbe5e297e
Author: Bastien Montagne
Date:   Wed Aug 30 16:42:53 2017 +0200
Branches: master
https://developer.blender.org/rB25c5928b2b6a71a426b123a81b605e2fbe5e297e

Refactor 'split faces' mesh code.

Previous version was trying to do a quick and simple process in the case
we were only considering smooth/flat status of faces.

Thing is, even then, the algorithm was not actually working in all
possible situations, e.g. two smooth faces having a single vertex in
common, but no common edges, would not have split that vertex, leading
to incorrect shading etc.

So now, tweaked slightly our split normals code to be able to generate
lnor spaces even when autosmooth is disabled, and we always go that way
when splitting faces.

Using smooth fans from clnor spaces is not only the only way to get 100%
correct results, it also makes face split code simpler.

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

M	source/blender/blenkernel/intern/mesh.c

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

diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index b89541173e1..2e725cbfbfd 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -2059,6 +2059,12 @@ void BKE_mesh_mselect_active_set(Mesh *me, int index, int type)
 	           (me->mselect[me->totselect - 1].type  == type));
 }
 
+/**
+ * Compute 'split' (aka loop, or per face corner's) normals.
+ *
+ * \param r_lnors_spacearr Allows to get computed loop normal space array. That data, among other things,
+ *                         contains 'smooth fan' info, useful e.g. to split geometry along sharp edges...
+ */
 void BKE_mesh_calc_normals_split_ex(Mesh *mesh, MLoopNorSpaceArray *r_lnors_spacearr)
 {
 	float (*r_loopnors)[3];
@@ -2094,7 +2100,10 @@ void BKE_mesh_calc_normals_split_ex(Mesh *mesh, MLoopNorSpaceArray *r_lnors_spac
 	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, r_lnors_spacearr, clnors, NULL);
+	        /* Note that we enforce computing clnors when the clnor space array is requested by caller here.
+	         * However, we obviously only use the autosmooth angle threshold only in case autosmooth is enabled. */
+	        r_lnors_spacearr != NULL, (mesh->flag & ME_AUTOSMOOTH) != 0 ? mesh->smoothresh : (float)M_PI,
+	        r_lnors_spacearr, clnors, NULL);
 
 	if (free_polynors) {
 		MEM_freeN(polynors);
@@ -2126,118 +2135,70 @@ typedef struct 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)
+        const Mesh *mesh, MLoopNorSpaceArray *lnors_spacearr, SplitFaceNewVert **new_verts, MemArena *memarena)
 {
-	/* Note: if lnors_spacearr is NULL, ther is no autosmooth handling, and we only split out flat polys. */
+	/* This is now mandatory, trying to do the job in simple way without that data is doomed to fail, even when only
+	 * dealing with smooth/flat faces one can find cases that no simple algorithm can handle properly. */
+	BLI_assert(lnors_spacearr != NULL);
+
 	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__);
+	BLI_bitmap *done_loops = BLI_BITMAP_NEW(num_loops, __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;
-
-				BLI_assert(*lnor_space);
-
-				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 {
-					/* Single loop in this fan... */
-					BLI_BITMAP_ENABLE(done_loops, loop_idx);
+	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;
+
+			BLI_assert(*lnor_space);
+
+			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) {
-						ml->v = new_vert_idx;
+						mloop[ml_fan_idx].v = new_vert_idx;
 					}
 				}
-
-				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 {
-					/* 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;
-				}
 			}
-		}
-
-		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);
+			else {
+				/* Single loop in this fan... */
+				BLI_BITMAP_ENABLE(done_loops, loop_idx);
+				if (vert_used) {
+					ml->v = new_vert_idx;
 				}
 			}
-		}
 
-		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 below about normals. */
-						new_vert->next = *new_verts;
-						*new_verts = new_vert;
-					}
-					else {
-						BLI_BITMAP_ENABLE(verts_used, vert_idx);
-					}
-				}
-				/* Note: there is no way to get new normals for smooth vertices here (and we don't have direct access
-				 * to poly normals either for flat ones), so we'll have to recompute all vnors at the end... */
-				*r_need_vnors_recalc = 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 {
+				/* 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;
 			}
 		}
 	}
 
+	MEM_freeN(done_loops);
 	MEM_freeN(verts_used);
 
 	return num_verts - mesh->totvert;
@@ -2356,27 +2317,17 @@ void BKE_mesh_split_faces(Mesh *mesh, bool free_loop_normals)
 	}
 	BKE_mesh_tessface_clear(mesh);
 
-	MLoopNorSpaceArray *lnors_spacearr = NULL;
-	MemArena *memarena;
-	bool need_vnors_recalc = false;
-
-	if (mesh->flag & ME_AUTOSMOOTH) {
-		lnors_spacearr = MEM_callocN(sizeof(*lnors_spacearr), __func__);
-		/* Compute loop normals and loop normal spaces (a.k.a. smooth fans of faces around vertices). */
-		BKE_mesh_calc_normals_split_ex(mesh, lnors_spacearr);
-		/* Stealing memarena from loop normals space array. */
-		memarena = lnors_spacearr->mem;
-	}
-	else {
-		/* We still have to split out flat faces... */
-		memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
-	}
+	MLoopNorSpaceArray lnors_spacearr = {NULL};
+	/* Compute loop normals and loop normal spaces (a.k.a. smooth fans of faces around vertices). */
+	BKE_mesh_calc_normals_split_ex(mesh, &lnors_spacearr);
+	/* Stealing memarena from loop normals space array. */
+	MemArena *memarena = lnors_spacearr.mem;
 
 	SplitFaceNewVert *new_verts = NULL;
 	SplitFaceNewEdge *new_edges = NULL;
 
 	/* Detect loop normal spaces (a.k.a. smooth fans) that will need a new vert. */
-	const int num_new_verts = split_faces_prepare_new_verts(mesh, lnors_spacearr, &new_verts, memarena, &need_vnors_recalc);
+	const int num_new_verts = split_faces_prepare_new_verts(mesh, &lnors_spacearr, &new_verts, memarena);
 
 	if (num_new_verts > 0) {
 		/* Reminder: beyond this point, there is no way out, mesh is in invalid state (due to early-reassignment of
@@ -2388,9 +2339,9 @@ void BKE_mesh_split_faces(Mesh *mesh, bool free_loop_normals)
 
 		/* Reallocate all vert and edge related data. */
 		mesh->totvert += num_new_verts;
-		mesh->totedge += num_new_edges;
 		CustomData_realloc(&mesh->vdata, mesh->totvert);
 		if (do_edges) {
+			mesh->totedge += num_new_edges;
 			CustomData_realloc(&mesh->edata, mesh->totedge);
 		}
 		/* Update pointers to a newly allocated memory. */
@@ -2410,18 +2361,9 @@

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list