[Bf-blender-cvs] [73529fb1bb8] temp_bmesh_multires: Sculpy dyntopo: fixed various topology bugs

Joseph Eagar noreply at git.blender.org
Mon Aug 30 01:06:57 CEST 2021


Commit: 73529fb1bb88de178e051921ab849233c7b2d699
Author: Joseph Eagar
Date:   Sun Aug 29 16:05:26 2021 -0700
Branches: temp_bmesh_multires
https://developer.blender.org/rB73529fb1bb88de178e051921ab849233c7b2d699

Sculpy dyntopo: fixed various topology bugs

* Fixed crash in dyntopo collapse.  The
  loops around vertex iterator dyntopo uses
  doesn't actually work on non-manifold meshes,
  or meshes with invalid normals, this was not
  being checked in pbvh_bmesh_collapse_edge.
* Rotate tool now works with dyntopo.

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

M	source/blender/blenkernel/intern/dyntopo.c
M	source/blender/bmesh/intern/bmesh_log.c
M	source/blender/editors/sculpt_paint/sculpt.c
M	source/blender/editors/sculpt_paint/sculpt_smooth.c
M	source/blender/makesdna/DNA_brush_enums.h

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

diff --git a/source/blender/blenkernel/intern/dyntopo.c b/source/blender/blenkernel/intern/dyntopo.c
index 938d39d0dbc..51e1ef183c9 100644
--- a/source/blender/blenkernel/intern/dyntopo.c
+++ b/source/blender/blenkernel/intern/dyntopo.c
@@ -109,8 +109,7 @@ static void pbvh_bmesh_verify(PBVH *pbvh);
  * Take care since 'break' won't works as expected within these macros!
  */
 
-#define BM_DISK_EDGE(e, v) \
-   &(((&e->v1_disk_link)[v == e->v2])))
+#define BM_DISK_EDGE(e, v) (&((&(e)->v1_disk_link)[(v) == (e)->v2]))
 
 #define BM_LOOPS_OF_VERT_ITER_BEGIN(l_iter_radial_, v_) \
   { \
@@ -161,6 +160,275 @@ static void pbvh_split_edges(
 void bm_log_message(const char *fmt, ...);
 void pbvh_bmesh_check_nodes_simple(PBVH *pbvh);
 
+//#define CHECKMESH
+//#define TEST_INVALID_NORMALS
+
+#ifndef CHECKMESH
+#  define fix_mesh(bm)
+#  define validate_vert(bm, v, autofix, check_manifold) true
+#  define validate_edge(bm, e, autofix, check_manifold) true
+#  define validate_face(bm, f, autofix, check_manifold) true
+#  define validate_vert_faces(bm, v, autofix, check_manifold) true
+#  define check_face_is_manifold(bm, f) true
+#else
+
+#  define CHECKMESH_ATTR ATTR_NO_OPT
+
+CHECKMESH_ATTR static void _debugprint(const char *fmt, ...)
+{
+  va_list args;
+  va_start(args, fmt);
+  vprintf(fmt, args);
+  va_end(args);
+}
+
+void bmesh_disk_edge_append(BMEdge *e, BMVert *v);
+void bmesh_radial_loop_append(BMEdge *e, BMLoop *l);
+
+CHECKMESH_ATTR static bool check_face_is_manifold(BMesh *bm, BMFace *f)
+{
+  BMLoop *l = f->l_first;
+
+  do {
+    if (l->radial_next != l && l->radial_next->radial_next != l) {
+      _debugprint("non-manifold edge in loop\n");
+      return false;
+    }
+
+#  ifdef TEST_INVALID_NORMALS
+    if (l != l->radial_next && l->v == l->radial_next->v) {
+      _debugprint("invalid normals\n");
+      return false;
+    }
+#  endif
+  } while ((l = l->next) != f->l_first);
+
+  return true;
+}
+
+CHECKMESH_ATTR
+static void fix_mesh(BMesh *bm)
+{
+  BMIter iter;
+  BMVert *v;
+  BMEdge *e;
+  BMFace *f;
+
+  _debugprint("fixing mesh. . .\n");
+
+  BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+    v->e = NULL;
+  }
+
+  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+    e->v1_disk_link.next = e->v1_disk_link.prev = NULL;
+    e->v2_disk_link.next = e->v2_disk_link.prev = NULL;
+  }
+
+  // rebuild disk cycles
+  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+    e->l = NULL;
+
+    bmesh_disk_edge_append(e, e->v1);
+    bmesh_disk_edge_append(e, e->v2);
+  }
+
+  BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+    BMLoop *l = f->l_first;
+
+    do {
+      l->e = BM_edge_exists(l->v, l->next->v);
+
+      bmesh_radial_loop_append(l->e, l);
+    } while ((l = l->next) != f->l_first);
+  }
+  _debugprint("done fixing mesh.\n");
+}
+
+CHECKMESH_ATTR
+static bool validate_vert(BMesh *bm, BMVert *v, bool autofix, bool check_manifold)
+{
+  if (v->head.htype != BM_VERT) {
+    _debugprint("bad vertex\n");
+    return false;
+  }
+
+  BMEdge *e = v->e;
+  int i = 0;
+
+  if (!e) {
+    return true;
+  }
+
+  do {
+    if (e->v1 != v && e->v2 != v) {
+      _debugprint("edge does not contain v\n");
+      goto error;
+    }
+
+    if (e->l) {
+      int j = 0;
+
+      BMLoop *l = e->l;
+      do {
+        if (l->e->v1 != v && l->e->v2 != v) {
+          _debugprint("loop's edges doesn't contain v\n");
+          goto error;
+        }
+
+        if (l->v != v && l->next->v != v) {
+          _debugprint("loop and loop->next don't contain v\n");
+          goto error;
+        }
+
+        j++;
+        if (j > 1000) {
+          _debugprint("corrupted radial cycle\n");
+          goto error;
+        }
+
+        if (check_manifold) {
+          check_face_is_manifold(bm, l->f);
+        }
+      } while ((l = l->radial_next) != e->l);
+    }
+    if (i > 10000) {
+      _debugprint("corrupted disk cycle\n");
+      goto error;
+    }
+
+    e = BM_DISK_EDGE_NEXT(e, v);
+    i++;
+  } while (e != v->e);
+
+  return true;
+
+error:
+
+  if (autofix) {
+    fix_mesh(bm);
+  }
+
+  return false;
+}
+
+CHECKMESH_ATTR
+static bool validate_edge(BMesh *bm, BMEdge *e, bool autofix, bool check_manifold)
+{
+  if (e->head.htype != BM_EDGE) {
+    _debugprint("corrupted edge!\n");
+    return false;
+  }
+
+  bool ret = validate_vert(bm, e->v1, false, check_manifold) &&
+             validate_vert(bm, e->v2, false, check_manifold);
+
+  if (!ret && autofix) {
+    fix_mesh(bm);
+  }
+
+  return ret;
+}
+
+CHECKMESH_ATTR
+static bool validate_face(BMesh *bm, BMFace *f, bool autofix, bool check_manifold)
+{
+  if (f->head.htype != BM_FACE) {
+    _debugprint("corrupted edge!\n");
+    return false;
+  }
+
+  BMLoop **ls = NULL;
+  BLI_array_staticdeclare(ls, 32);
+
+  BMLoop *l = f->l_first;
+  int i = 0;
+  do {
+    i++;
+
+    if (i > 100000) {
+      _debugprint("Very corrupted face!\n");
+      goto error;
+    }
+
+    if (!validate_edge(bm, l->e, false, check_manifold)) {
+      goto error;
+    }
+
+    BLI_array_append(ls, l);
+  } while ((l = l->next) != f->l_first);
+
+  for (int i = 0; i < BLI_array_len(ls); i++) {
+    BMLoop *l1 = ls[i];
+    for (int j = 0; j < BLI_array_len(ls); j++) {
+      BMLoop *l2 = ls[j];
+
+      if (i != j && l1->v == l2->v) {
+        _debugprint("duplicate verts in face!\n");
+        goto error;
+      }
+
+      if (BM_edge_exists(l->v, l->next->v) != l->e) {
+        _debugprint("loop has wrong edge!\n");
+        goto error;
+      }
+    }
+  }
+
+  BLI_array_free(ls);
+  return true;
+
+error:
+  BLI_array_free(ls);
+
+  if (autofix) {
+    fix_mesh(bm);
+  }
+
+  return false;
+}
+
+CHECKMESH_ATTR bool validate_vert_faces(BMesh *bm, BMVert *v, int autofix, bool check_manifold)
+{
+  if (!validate_vert(bm, v, autofix, check_manifold)) {
+    return false;
+  }
+
+  if (!v->e) {
+    return true;
+  }
+
+  BMEdge *e = v->e;
+  do {
+    BMLoop *l = e->l;
+
+    if (!l) {
+      continue;
+    }
+
+    do {
+      if (!validate_edge(bm, l->e, false, false)) {
+        goto error;
+      }
+
+      if (!validate_face(bm, l->f, false, check_manifold)) {
+        goto error;
+      }
+    } while ((l = l->radial_next) != e->l);
+  } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e);
+
+  return true;
+
+error:
+
+  if (autofix) {
+    fix_mesh(bm);
+  }
+
+  return false;
+}
+#endif
+
 static BMEdge *bmesh_edge_create_log(PBVH *pbvh, BMVert *v1, BMVert *v2, BMEdge *e_example)
 {
   BMEdge *e = BM_edge_exists(v1, v2);
@@ -1643,6 +1911,8 @@ static bool check_face_is_tri(PBVH *pbvh, BMFace *f)
 
   BMLoop *l = f->l_first;
   do {
+    validate_vert(pbvh->bm, l->v, true, true);
+
     if (l->e->head.index == -1) {
       l->e->head.index = 0;
     }
@@ -1719,6 +1989,7 @@ static bool check_face_is_tri(PBVH *pbvh, BMFace *f)
       }
     } while ((l = l->next) != f2->l_first);
 
+    validate_face(pbvh->bm, f2, false, true);
     BKE_pbvh_bmesh_add_face(pbvh, f2, true, true);
   }
 
@@ -1753,6 +2024,13 @@ static bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v)
   BMFace **fs = NULL;
   BLI_array_staticdeclare(fs, 32);
 
+  validate_vert(pbvh->bm, v, true, true);
+
+  if (v->head.htype != BM_VERT) {
+    printf("non-vert %p fed to %s\n", v, __func__);
+    return false;
+  }
+
   BMIter iter;
   BMFace *f;
   BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) {
@@ -2235,6 +2513,7 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx,
     e_tri[1] = bmesh_edge_create_log(pbvh, v_tri[1], v_tri[2], NULL);
 
     f_new = pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f_adj, false, true);
+
     long_edge_queue_face_add(eq_ctx, f_new, true);
 
     pbvh_bmesh_copy_facedata(pbvh, bm, f_new, f_adj);
@@ -2389,9 +2668,12 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
 {
   BMVert *v_del, *v_conn;
 
+  validate_edge(pbvh->bm, e, true, true);
+
   check_vert_fan_are_tris(pbvh, e->v1);
   check_vert_fan_are_tris(pbvh, e->v2);
 
+  validate_edge(pbvh->bm, e, true, true);
   // pbvh_bmesh_check_nodes_simple(pbvh);
 
   bm_log_message("  == collapse == ");
@@ -2434,6 +2716,8 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
     v_conn = v1;
   }
 
+  validate_vert_faces(pbvh->bm, v_conn, false, true);
+
   int ni_conn = BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_vert_node_offset);
   const float v_ws[2] = {0.5f, 0.5f};
   const void *v_blocks[2] = {v_del->head.data, v_conn->head.data};
@@ -2443,6 +2727,7 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
   /* Remove the merge vertex from the PBVH */
   pbvh_bmesh_vert_remove(pbvh, v_del);
 
+  validate_vert_faces(pbvh->bm, v_conn, false, true);
   // pbvh_bmesh_check_nodes_simple(pbvh);
 
   /* Remove all faces adjacent to the edge */
@@ -2466,14 +2751,20 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
     BM_face_kill(pbvh->bm, f_adj);
   }
 
+  validate_vert_faces(pbvh->bm, v_conn, false, false);
+
   // pbvh_bmesh_check_nodes_simple(pbvh);
 
   /* Kill the edge */
   BLI_assert(BM_edge_is_wire(e));
 
+  validate_edge(pbvh->bm, e, true, true);
+
   BM_log_edge_removed(pbvh->bm_log, e);
   BM_edge_kill(pbvh->bm, e);
 
+  validate_vert_faces(pbvh->bm, v_conn, false, false);
+
   /* For all remaining faces of v_del, create a new face that is the
    * same except it uses v_conn instead of v_del */
   /* NOTE: this could be done with BM_vert_splice(), but that
@@ -2548,6 +2839,8 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
     //*/
   }
 
+  validate_vert_faces(pbvh->bm, v_conn, false, false);
+
   // pbvh_bmesh_check_nodes_simple(pbvh);
 
 #if 1
@@ -2603,8 +2896,41 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
       BMVert *old_tri[3] = {v_del, l->next->v, l->prev->v};
       BMVert *v_tri[3] = {v_conn, l->next->v, l->prev->v};
 
-      MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->next->v);
-      MDynTopoVert *mv3 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->prev->v);
+      if (v_conn == l->next->v || v_conn == l->prev->v) {
+        // this can happen on non-manifold meshes
+        continue;
+      }
+
+#  ifdef CHECKMESH
+
+      int m = 0;
+#    define LTEST1(l, bit) \
+      if ((l) != (l)->radial_next && (l) == (l)->radial_next->radial_next && \
+          (l)->v == (l)->radial_next->v) { \
+        m |= 1 << bit; \
+      }
+
+ 

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list