[Bf-blender-cvs] [d3f578c] custom-normals-bmesh: Initial work to allow custom normals setting in BMesh, with quick bmesh py API call too.

Bastien Montagne noreply at git.blender.org
Tue Jun 7 22:05:16 CEST 2016


Commit: d3f578c7e772e65189bcc94ba42ee75e7c7a75a5
Author: Bastien Montagne
Date:   Tue Jun 7 21:55:28 2016 +0200
Branches: custom-normals-bmesh
https://developer.blender.org/rBd3f578c7e772e65189bcc94ba42ee75e7c7a75a5

Initial work to allow custom normals setting in BMesh, with quick bmesh py API call too.

Seems to be working OK from quick tests...

Some notes:
 - This is mimicking BKE_mesh_evaluate code. While this is OK for some cases,
   we'll likely want some more integrated and fine-grained ways to set/edit those
   normals in Edit mode once we create real editing tools for those (most likely based
   on some BMesh operators).
   This implies some kind of dynamic caching/update of clnors spaces though, which
   is not trivial. So kept for later, for now you have to set all custom normals at once.
 - Not sure where/how to expose this in py API, for now just added a func in bmesh.utils.

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

M	source/blender/blenkernel/BKE_mesh.h
M	source/blender/blenkernel/intern/mesh_evaluate.c
M	source/blender/bmesh/intern/bmesh_mesh.c
M	source/blender/bmesh/intern/bmesh_mesh.h
M	source/blender/python/bmesh/bmesh_py_utils.c

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

diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index d8d8690..3ec597f 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -194,6 +194,9 @@ void BKE_mesh_loop_tangents_ex(
 void BKE_mesh_loop_tangents(
         struct Mesh *mesh, const char *uvmap, float (*r_looptangents)[4], struct ReportList *reports);
 
+/* This threshold is a bit touchy (usual float precision issue), this value seems OK. */
+#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-6f)
+
 /**
  * References a contiguous loop-fan with normal offset vars.
  */
diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c
index 1c86fbc..2c448aa 100644
--- a/source/blender/blenkernel/intern/mesh_evaluate.c
+++ b/source/blender/blenkernel/intern/mesh_evaluate.c
@@ -433,9 +433,6 @@ MLoopNorSpace *BKE_lnor_space_create(MLoopNorSpaceArray *lnors_spacearr)
 	return BLI_memarena_calloc(lnors_spacearr->mem, sizeof(MLoopNorSpace));
 }
 
-/* This threshold is a bit touchy (usual float precision issue), this value seems OK. */
-#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-6f)
-
 /* Should only be called once.
  * Beware, this modifies ref_vec and other_vec in place!
  * In case no valid space can be generated, ref_alpha and ref_beta are set to zero (which means 'use auto lnors').
@@ -1370,6 +1367,7 @@ void BKE_mesh_normals_loop_split(
  * r_custom_loopnors is expected to have normalized normals, or zero ones, in which case they will be replaced
  * by default loop/vertex normal.
  */
+/* XXX Keep in sync with BMesh's bm_mesh_loops_normals_custom_set(). */
 static void mesh_normals_loop_custom_set(
         const MVert *mverts, const int numVerts, MEdge *medges, const int numEdges,
         MLoop *mloops, float (*r_custom_loopnors)[3], const int numLoops,
diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c
index ed1bd16..9147b22 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.c
+++ b/source/blender/bmesh/intern/bmesh_mesh.c
@@ -26,11 +26,14 @@
  * BM mesh level functions.
  */
 
+#include <limits.h>
+
 #include "MEM_guardedalloc.h"
 
 #include "DNA_listBase.h"
 #include "DNA_object_types.h"
 
+#include "BLI_bitmap.h"
 #include "BLI_linklist_stack.h"
 #include "BLI_listbase.h"
 #include "BLI_math.h"
@@ -39,6 +42,7 @@
 
 #include "BKE_cdderivedmesh.h"
 #include "BKE_editmesh.h"
+#include "BKE_global.h"
 #include "BKE_mesh.h"
 #include "BKE_multires.h"
 
@@ -903,6 +907,330 @@ void BM_loops_calc_normal_vcos(
 	}
 }
 
+/* BMesh version of mesh_normals_loop_custom_set() in mesh_evaluate.c, keep it in sync.
+ * 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_normals_custom_set(
+        BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*fnos)[3], float (*r_lnos)[3],
+        short (*r_clnors_data)[2], const int cd_loop_clnors_offset, const bool use_vertices)
+{
+	BMIter viter;
+	BMVert *v_curr;
+	BMIter fiter;
+	BMFace *f_curr;
+	BMIter liter;
+	BMLoop *l_curr;
+
+	MLoopNorSpaceArray lnors_spacearr = {NULL};
+	BLI_bitmap *done_loops = BLI_BITMAP_NEW((size_t)bm->totloop, __func__);
+	float (*lnos)[3] = MEM_callocN(sizeof(*lnos) * (size_t)bm->totloop, __func__);
+	/* In this case we always consider split nors as ON, and do not want to use angle to define smooth fans! */
+	const bool use_split_normals = true;
+	const float split_angle = (float)M_PI;
+	int i;
+
+	{
+		char htype = BM_LOOP;
+		if (vcos || use_vertices) {
+			htype |= BM_VERT;
+		}
+		if (fnos) {
+			htype |= BM_FACE;
+		}
+		BM_mesh_elem_index_ensure(bm, htype);
+	}
+
+	/* Compute current lnor spacearr. */
+	BM_loops_calc_normal_vcos(bm, vcos, vnos, fnos, use_split_normals, split_angle, lnos, &lnors_spacearr, NULL, -1);
+
+	/* Set all given zero vectors to their default value. */
+	if (use_vertices) {
+		BM_ITER_MESH_INDEX(v_curr, &viter, bm, BM_VERTS_OF_MESH, i) {
+			if (is_zero_v3(r_lnos[i])) {
+				copy_v3_v3(r_lnos[i], v_curr->no);
+			}
+		}
+	}
+	else {
+		BM_ITER_MESH(f_curr, &fiter, bm, BM_FACES_OF_MESH) {
+			BM_ITER_ELEM(l_curr, &liter, f_curr, BM_LOOPS_OF_FACE) {
+				i = BM_elem_index_get(l_curr);
+				if (is_zero_v3(r_lnos[i])) {
+					copy_v3_v3(r_lnos[i], lnos[i]);
+				}
+			}
+		}
+	}
+
+	/* Now, check each current smooth fan (one lnor space per smooth fan!), and if all its matching custom lnors
+	 * are not (enough) equal, add sharp edges as needed.
+	 * This way, next time we run BM_mesh_loop_normals_update(), we'll get lnor spacearr/smooth fans matching
+	 * given custom lnors.
+	 * Note this code *will never* unsharp edges!
+	 * And quite obviously, when we set custom normals per vertices, running this is absolutely useless.
+	 */
+	if (!use_vertices) {
+		BM_ITER_MESH(f_curr, &fiter, bm, BM_FACES_OF_MESH) {
+			BM_ITER_ELEM(l_curr, &liter, f_curr, BM_LOOPS_OF_FACE) {
+				i = BM_elem_index_get(l_curr);
+				if (!lnors_spacearr.lspacearr[i]) {
+					/* This should not happen in theory, but in some rare case (probably ugly geometry)
+					 * we can get some NULL loopspacearr at this point. :/
+					 * Maybe we should set those loops' edges as sharp?
+					 */
+					BLI_BITMAP_ENABLE(done_loops, i);
+					if (G.debug & G_DEBUG) {
+						printf("WARNING! Getting invalid NULL loop space for loop %d!\n", i);
+					}
+					continue;
+				}
+
+				if (!BLI_BITMAP_TEST(done_loops, i)) {
+					/* Notes:
+					 *     * In case of mono-loop smooth fan, loops is NULL, so everything is fine (we have nothing to do).
+					 *     * Loops in this linklist are ordered (in reversed order compared to how they were discovered by
+					 *       BKE_mesh_normals_loop_split(), but this is not a problem). Which means if we find a
+					 *       mismatching clnor, we know all remaining loops will have to be in a new, different smooth fan/
+					 *       lnor space.
+					 *     * In smooth fan case, we compare each clnor against a ref one, to avoid small differences adding
+					 *       up into a real big one in the end!
+					 */
+					LinkNode *loops = lnors_spacearr.lspacearr[i]->loops;
+					BMLoop *l_prev = NULL;
+					const float *org_no = NULL;
+
+					if (loops) {
+						if (BM_elem_index_get(l_curr) != GET_INT_FROM_POINTER(loops->link)) {
+							/* l_curr is not first loop of our loop fan, just skip until we find that one.
+							 * Simpler than to go searching for the first loop immediately, since getting a BMLoop
+							 * from its index is not trivial currently.*/
+							continue;
+						}
+
+						BLI_assert(loops->next);
+
+						BMLoop *l = l_curr;
+						BMEdge *e_next;  /* Used to handle fan loop direction... */
+
+						/* Set e_next to loop along fan in matching direction with loop space's fan. */
+						{
+							const int lidx_next = GET_INT_FROM_POINTER(loops->next->link);
+							if (BM_elem_index_get(l->radial_next) == lidx_next) {
+								e_next = ELEM(l->e, l->radial_next->e, l->radial_next->prev->e) ? l->prev->e : l->e;
+							}
+							else {
+								BLI_assert(BM_elem_index_get(l->radial_prev) == lidx_next);
+								e_next = ELEM(l->e, l->radial_prev->e, l->radial_prev->prev->e) ? l->prev->e : l->e;
+							}
+						}
+
+						while (loops) {
+							const int lidx = GET_INT_FROM_POINTER(loops->link);
+							const int nidx = lidx;
+							float *no = r_lnos[nidx];
+
+							BLI_assert(l != NULL);
+							BLI_assert(BM_elem_index_get(l) == lidx);
+
+							if (!org_no) {
+								org_no = no;
+							}
+							else if (dot_v3v3(org_no, no) < LNOR_SPACE_TRIGO_THRESHOLD) {
+								/* Current normal differs too much from org one, we have to tag the edge between
+								 * previous loop's face and current's one as sharp.
+								 * We know those two loops do not point to the same edge, since we do not allow reversed winding
+								 * in a same smooth fan.
+								 */
+								const BMLoop *lp = l->prev;
+
+								BLI_assert(((l_prev->e == lp->e) ? l_prev->e : l->e) == e_next);
+
+								BM_elem_flag_disable(e_next, BM_ELEM_SMOOTH);
+								org_no = no;
+							}
+
+							l_prev = l;
+							l = BM_vert_step_fan_loop(l, &e_next);
+							loops = loops->next;
+							BLI_BITMAP_ENABLE(done_loops, lidx);
+						}
+
+						/* We also have to check between last and first loops, otherwise we may miss some sharp edges here!
+						 * This is just a simplified version of above while loop.
+						 * See T45984. */
+						loops = lnors_spacearr.lspacearr[i]->loops;
+						if (org_no) {
+							const int lidx = GET_INT_FROM_POINTER(loops->link);
+							l = l_curr;
+							const int nidx = lidx;
+							float *no = r_lnos[nidx];
+
+							if (dot_v3v3(org_no, no) < LNOR_SPACE_TRIGO_THRESHOLD) {
+								const BMLoop *lp = l->prev;
+								BM_elem_flag_disable((l_prev->e == lp->e) ? l_prev->e : l->e, BM_ELEM_SMOOTH);
+							}
+						}
+					}
+
+					/* For single loops, where lnors_spacearr.lspacearr[i]->loops is NULL. */
+					BLI_BITMAP_ENABLE(done_loops, i);
+				}
+			}
+		}
+
+		/* And now, recompute our new auto lnors and lnor spacearr! */
+		BKE_lnor_spacearr_clear(&lnors_spacearr);
+		BM_loops_calc_normal_vcos(bm, vcos, vnos, fnos, use_split_normals, split_angle, lnos, &lnors_spacearr, NULL, -1);
+	}
+	else {
+		BLI_BITMAP_SET_ALL(done_loops, true, (size_t)bm->totloop);
+	}
+
+	/* And we just have to convert plain object-space custom normals to our lnor space-encoded ones. */
+	BM_ITER_MESH(f_curr, &fiter, bm, BM_FACES_OF_MESH) {
+		BM_ITER_ELEM(l_curr, &liter, f_curr, BM_LOOPS_OF_FACE) {
+			i = BM_elem_index_get(l_curr);
+
+			if (!lnors_spacearr.lspacearr[i]) {
+				BLI_BITMAP_DISABLE(done_loops, i);
+				if (G.debug & G_DEBUG) {
+					printf("WARNING! Still getting invalid NULL loop space in second loop for loop %d!\n", i);
+				}
+				continue;
+			}
+
+			if (BLI_BITMAP_TEST_BOOL(done_loops, i)) {
+				/* Note we accumulate and average all custom normals in current smooth fan, to avoid getting different
+				 * clnors data (tiny differences in plain custom normals can give rather huge differences in
+				 * computed 2D factors).
+				 */
+				LinkNode *loops = lnors_spacearr.lspacearr[i]->loops;
+				if (lo

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list