[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