[Bf-blender-cvs] [0b7f581] master: Split Normals I (2/5): Add basic BMesh support of split normals.

Bastien Montagne noreply at git.blender.org
Sun Apr 13 15:39:03 CEST 2014


Commit: 0b7f5813973c515b84cd7c18ef6d7d1e59374237
Author: Bastien Montagne
Date:   Sun Apr 13 12:25:02 2014 +0200
https://developer.blender.org/rB0b7f5813973c515b84cd7c18ef6d7d1e59374237

Split Normals I (2/5): Add basic BMesh support of split normals.

* Merely a re-implementation of core split algorithm for BMesh, taking advantage of topological data available.
* This code needs valid loop indices, so added BM_LOOP support to BM_mesh_elem_index_ensure() & co.

Reviewers: campbellbarton

Reviewed By: campbellbarton

CC: brecht

Differential Revision: https://developer.blender.org/D366

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

M	source/blender/blenkernel/intern/editderivedmesh.c
M	source/blender/bmesh/bmesh_class.h
M	source/blender/bmesh/intern/bmesh_core.c
M	source/blender/bmesh/intern/bmesh_mesh.c
M	source/blender/bmesh/intern/bmesh_mesh.h
M	source/blender/bmesh/intern/bmesh_mesh_conv.c
M	source/blender/bmesh/tools/bmesh_decimate_collapse.c
M	source/blender/bmesh/tools/bmesh_edgenet.c
M	source/blender/modifiers/intern/MOD_array.c
M	source/blender/python/bmesh/bmesh_py_types.c

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

diff --git a/source/blender/blenkernel/intern/editderivedmesh.c b/source/blender/blenkernel/intern/editderivedmesh.c
index 1525400..5862c53 100644
--- a/source/blender/blenkernel/intern/editderivedmesh.c
+++ b/source/blender/blenkernel/intern/editderivedmesh.c
@@ -173,7 +173,26 @@ static void emDM_calcNormals(DerivedMesh *dm)
 
 static void emDM_calcLoopNormals(DerivedMesh *dm, const float split_angle)
 {
-	/* Do nothing for now! */
+	EditDerivedBMesh *bmdm = (EditDerivedBMesh *)dm;
+	BMesh *bm = bmdm->em->bm;
+	const float (*vertexCos)[3], (*vertexNos)[3], (*polyNos)[3];
+	float (*loopNos)[3];
+
+	/* calculate loop normals from poly and vertex normals */
+	emDM_ensureVertNormals(bmdm);
+	dm->dirty &= ~DM_DIRTY_NORMALS;
+
+	vertexCos = bmdm->vertexCos;
+	vertexNos = bmdm->vertexNos;
+	polyNos = bmdm->polyNos;
+
+	loopNos = dm->getLoopDataArray(dm, CD_NORMAL);
+	if (!loopNos) {
+		DM_add_loop_layer(dm, CD_NORMAL, CD_CALLOC, NULL);
+		loopNos = dm->getLoopDataArray(dm, CD_NORMAL);
+	}
+
+	BM_loops_calc_normal_vcos(bm, vertexCos, vertexNos, polyNos, split_angle, loopNos);
 }
 
 static void emDM_recalcTessellation(DerivedMesh *UNUSED(dm))
@@ -1530,6 +1549,31 @@ static void *emDM_getTessFaceDataArray(DerivedMesh *dm, int type)
 		}
 	}
 
+	/* Special handling for CD_TESSLOOPNORMAL, we generate it on demand as well. */
+	if (type == CD_TESSLOOPNORMAL) {
+		const float (*lnors)[3] = dm->getLoopDataArray(dm, CD_NORMAL);
+
+		if (lnors) {
+			BMLoop *(*looptris)[3] = bmdm->em->looptris;
+			short (*tlnors)[4][3], (*tlnor)[4][3];
+			int index, i, j;
+
+			DM_add_tessface_layer(dm, type, CD_CALLOC, NULL);
+			index = CustomData_get_layer_index(&dm->faceData, type);
+			dm->faceData.layers[index].flag |= CD_FLAG_TEMPORARY;
+
+			tlnor = tlnors = DM_get_tessface_data_layer(dm, type);
+
+			BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+			for (i = 0; i < bmdm->em->tottri; i++, tlnor++, looptris++) {
+				for (j = 0; j < 3; j++) {
+					normal_float_to_short_v3((*tlnor)[j], lnors[BM_elem_index_get((*looptris)[j])]);
+				}
+			}
+		}
+	}
+
 	return datalayer;
 }
 
diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h
index 01e4c91..ea7505b 100644
--- a/source/blender/bmesh/bmesh_class.h
+++ b/source/blender/bmesh/bmesh_class.h
@@ -66,7 +66,7 @@ typedef struct BMHeader {
 	int index; /* notes:
 	            * - Use BM_elem_index_get/set macros for index
 	            * - Uninitialized to -1 so we can easily tell its not set.
-	            * - Used for edge/vert/face, check BMesh.elem_index_dirty for valid index values,
+	            * - Used for edge/vert/face/loop, check BMesh.elem_index_dirty for valid index values,
 	            *   this is abused by various tools which set it dirty.
 	            * - For loops this is used for sorting during tessellation. */
 
@@ -188,9 +188,8 @@ typedef struct BMesh {
 	int totvertsel, totedgesel, totfacesel;
 
 	/* flag index arrays as being dirty so we can check if they are clean and
-	 * avoid looping over the entire vert/edge/face array in those cases.
-	 * valid flags are - BM_VERT | BM_EDGE | BM_FACE.
-	 * BM_LOOP isn't handled so far. */
+	 * avoid looping over the entire vert/edge/face/loop array in those cases.
+	 * valid flags are - BM_VERT | BM_EDGE | BM_FACE | BM_LOOP. */
 	char elem_index_dirty;
 
 	/* flag array table as being dirty so we know when its safe to use it,
diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c
index bf19be1..a06921b 100644
--- a/source/blender/bmesh/intern/bmesh_core.c
+++ b/source/blender/bmesh/intern/bmesh_core.c
@@ -211,6 +211,8 @@ static BMLoop *bm_loop_create(BMesh *bm, BMVert *v, BMEdge *e, BMFace *f,
 	l->prev = NULL;
 	/* --- done --- */
 
+	/* may add to middle of the pool */
+	bm->elem_index_dirty |= BM_LOOP;
 
 	bm->totloop++;
 
diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c
index 23572d3..e8dbfa0 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.c
+++ b/source/blender/bmesh/intern/bmesh_mesh.c
@@ -31,6 +31,7 @@
 #include "DNA_listBase.h"
 #include "DNA_object_types.h"
 
+#include "BLI_linklist_stack.h"
 #include "BLI_listbase.h"
 #include "BLI_math.h"
 #include "BLI_utildefines.h"
@@ -431,6 +432,230 @@ void BM_verts_calc_normal_vcos(BMesh *bm, const float (*fnos)[3], const float (*
 	MEM_freeN(edgevec);
 }
 
+/**
+ * Helpers for #BM_mesh_loop_normals_update and #BM_loops_calc_normals_vnos
+ */
+static void bm_mesh_edges_sharp_tag(BMesh *bm, const float (*vnos)[3], const float (*fnos)[3], float split_angle,
+                                    float (*r_lnos)[3])
+{
+	BMIter eiter;
+	BMEdge *e;
+	int i;
+
+	const bool check_angle = (split_angle < (float)M_PI);
+
+	if (check_angle) {
+		split_angle = cosf(split_angle);
+	}
+
+	{
+		char hflag = BM_LOOP;
+		if (vnos)
+			hflag |= BM_VERT;
+		if (fnos)
+			hflag |= BM_FACE;
+		BM_mesh_elem_index_ensure(bm, hflag);
+	}
+
+	/* This first loop checks which edges are actually smooth, and pre-populate lnos with vnos (as if they were
+	 * all smooth).
+	 */
+	BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) {
+		BMLoop *l_a, *l_b;
+
+		BM_elem_index_set(e, i); /* set_inline */
+		BM_elem_flag_disable(e, BM_ELEM_TAG); /* Clear tag (means edge is sharp). */
+
+		/* An edge with only two loops, might be smooth... */
+		if (BM_edge_loop_pair(e, &l_a, &l_b)) {
+			bool is_angle_smooth = true;
+			if (check_angle) {
+				const float *no_a = fnos ? fnos[BM_elem_index_get(l_b->f)] : l_a->f->no;
+				const float *no_b = fnos ? fnos[BM_elem_index_get(l_b->f)] : l_b->f->no;
+				is_angle_smooth = (dot_v3v3(no_a, no_b) >= split_angle);
+			}
+
+			/* We only tag edges that are *really* smooth... */
+			if (is_angle_smooth &&
+			    BM_elem_flag_test_bool(e, BM_ELEM_SMOOTH) &&
+			    BM_elem_flag_test_bool(l_a->f, BM_ELEM_SMOOTH) &&
+			    BM_elem_flag_test_bool(l_b->f, BM_ELEM_SMOOTH))
+			{
+				const float *no;
+				BM_elem_flag_enable(e, BM_ELEM_TAG);
+
+				/* linked vertices might be fully smooth, copy their normals to loop ones. */
+				no = vnos ? vnos[BM_elem_index_get(l_a->v)] : l_a->v->no;
+				copy_v3_v3(r_lnos[BM_elem_index_get(l_a)], no);
+				no = vnos ? vnos[BM_elem_index_get(l_b->v)] : l_b->v->no;
+				copy_v3_v3(r_lnos[BM_elem_index_get(l_b)], no);
+			}
+		}
+	}
+
+	bm->elem_index_dirty &= ~BM_EDGE;
+}
+
+/* BMesh version of BKE_mesh_normals_loop_split() in mesh_evaluate.c */
+static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const float (*fnos)[3], float (*r_lnos)[3])
+{
+	BMIter fiter;
+	BMFace *f_curr;
+
+	/* Temp normal stack. */
+	BLI_SMALLSTACK_DECLARE(normal, float *);
+
+	{
+		char hflag = BM_LOOP;
+		if (vcos)
+			hflag |= BM_VERT;
+		if (fnos)
+			hflag |= BM_FACE;
+		BM_mesh_elem_index_ensure(bm, hflag);
+	}
+
+	/* We now know edges that can be smoothed (they are tagged), and edges that will be hard (they aren't).
+	 * Now, time to generate the normals.
+	 */
+	BM_ITER_MESH (f_curr, &fiter, bm, BM_FACES_OF_MESH) {
+		BMLoop *l_curr, *l_first;
+
+		l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
+		do {
+			if (BM_elem_flag_test_bool(l_curr->e, BM_ELEM_TAG)) {
+				/* A smooth edge.
+				 * We skip it because it is either:
+				 * - in the middle of a 'smooth fan' already computed (or that will be as soon as we hit
+				 *   one of its ends, i.e. one of its two sharp edges), or...
+				 * - the related vertex is a "full smooth" one, in which case pre-populated normals from vertex
+				 *   are just fine!
+				 */
+			}
+			else if (!BM_elem_flag_test_bool(l_curr->prev->e, BM_ELEM_TAG)) {
+				/* Simple case (both edges around that vertex are sharp in related polygon),
+				 * this vertex just takes its poly normal.
+				 */
+				const float *no = fnos ? fnos[BM_elem_index_get(f_curr)] : f_curr->no;
+				copy_v3_v3(r_lnos[BM_elem_index_get(l_curr)], no);
+			}
+			else {
+				/* We have to fan around current vertex, until we find the other non-smooth edge,
+				 * and accumulate face normals into the vertex!
+				 * Note in case this vertex has only one sharp edge, this is a waste because the normal is the same as
+				 * the vertex normal, but I do not see any easy way to detect that (would need to count number
+				 * of sharp edges per vertex, I doubt the additional memory usage would be worth it, especially as
+				 * it should not be a common case in real-life meshes anyway).
+				 */
+				BMVert *v_pivot = l_curr->v;
+				BMEdge *e_next;
+				BMLoop *lfan_pivot, *lfan_pivot_next;
+				float lnor[3] = {0.0f, 0.0f, 0.0f};
+				float vec_curr[3], vec_next[3];
+
+				const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co;
+
+				lfan_pivot = l_curr;
+				e_next = lfan_pivot->e;  /* Current edge here, actually! */
+
+				/* Only need to compute previous edge's vector once, then we can just reuse old current one! */
+				{
+					const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot);
+					const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
+
+					sub_v3_v3v3(vec_curr, co_2, co_pivot);
+					normalize_v3(vec_curr);
+				}
+
+				while (true) {
+					/* Much simpler than in sibling code with basic Mesh data! */
+					lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next);
+					BLI_assert(lfan_pivot_next->v == v_pivot);
+
+					/* Compute edge vector.
+					 * NOTE: We could pre-compute those into an array, in the first iteration, instead of computing them
+					 *       twice (or more) here. However, time gained is not worth memory and time lost,
+					 *       given the fact that this code should not be called that much in real-life meshes...
+					 */
+					{
+						const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot);
+						const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
+
+						sub_v3_v3v3(vec_next, co_2, co_pivot);
+						normalize_v3(vec_next);
+					}
+
+					{
+						/* Code similar to accumulate_vertex_normals_poly. */
+						/* Calculate angle between the two poly edges incident on this vertex. */
+						const BMFace *f = lfan_pivot->f;
+						const float fac = saacos(dot_v3v3(vec_next, vec_curr));
+						const float *no = fnos ? fnos[BM_elem_index_get(f)] : f->no;
+						/* Accumu

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list