[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