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. @@ _______________________________________________ Bf-blender-cvs mailing list Bf-blender-cvs@blender.org List details, subscription details or unsubscribe: https://lists.blender.org/mailman/listinfo/bf-blender-cvs