#include "MEM_guardedalloc.h" #include "BKE_utildefines.h" #include "bmesh.h" #include "mesh_intern.h" #include "bmesh_private.h" #include "BLI_math.h" #include "BLI_array.h" #include #include #include #define FACE_MARK 1 #define FACE_ORIG 2 #define FACE_NEW 4 #define EDGE_MARK 1 #define VERT_MARK 1 static int check_hole_in_region(BMesh *bm, BMFace *f) { BMWalker regwalker; BMIter liter2; BMLoop *l2, *l3; BMFace *f2; /*checks if there are any unmarked boundary edges in the face region*/ BMW_Init(®walker, bm, BMW_ISLAND, FACE_MARK, 0); f2 = BMW_Begin(®walker, f); for (; f2; f2=BMW_Step(®walker)) { l2 = BMIter_New(&liter2, bm, BM_LOOPS_OF_FACE, f2); for (; l2; l2=BMIter_Step(&liter2)) { l3 = bmesh_radial_nextloop(l2); if (BMO_TestFlag(bm, l3->f, FACE_MARK) != BMO_TestFlag(bm, l2->f, FACE_MARK)) { if (!BMO_TestFlag(bm, l2->e, EDGE_MARK)) { return 0; } } } } BMW_End(®walker); return 1; } void dissolvefaces_exec(BMesh *bm, BMOperator *op) { BMOIter oiter; BMIter liter, liter2, liter3; BMLoop *l, *l2, *l3; BMFace *f, *f2, *nf = NULL; BLI_array_declare(region); BLI_array_declare(regions); BMLoop ***regions = NULL; BMLoop **region = NULL; BMWalker regwalker; int i, j, fcopied; BMO_Flag_Buffer(bm, op, "faces", FACE_MARK, BM_FACE); /*collect regions*/ f = BMO_IterNew(&oiter, bm, op, "faces", BM_FACE); for (; f; f=BMO_IterStep(&oiter)) { if (!BMO_TestFlag(bm, f, FACE_MARK)) continue; BLI_array_empty(region); region = NULL; /*forces different allocation*/ /*yay, walk!*/ BMW_Init(®walker, bm, BMW_ISLAND, FACE_MARK, 0); f2 = BMW_Begin(®walker, f); for (; f2; f2=BMW_Step(®walker)) { l2 = BMIter_New(&liter2, bm, BM_LOOPS_OF_FACE, f2); for (; l2; l2=BMIter_Step(&liter2)) { l3 = BMIter_New(&liter3, bm, BM_LOOPS_OF_LOOP, l2); for (; l3; l3=BMIter_Step(&liter3)) { if (!BMO_TestFlag(bm, l3->f, FACE_MARK)) { BLI_array_growone(region); region[BLI_array_count(region)-1] = l2; break; } } if (bmesh_radial_nextloop(l2) == l2) { BLI_array_growone(region); region[BLI_array_count(region)-1] = l2; } } } BMW_End(®walker); BMW_Init(®walker, bm, BMW_ISLAND, FACE_MARK, 0); f2 = BMW_Begin(®walker, f); for (; f2; f2=BMW_Step(®walker)) { BMO_ClearFlag(bm, f2, FACE_MARK); BMO_SetFlag(bm, f2, FACE_ORIG); } BMW_End(®walker); if (BMO_HasError(bm)) { BMO_ClearStack(bm); BMO_RaiseError(bm, op, BMERR_DISSOLVEFACES_FAILED, NULL); goto cleanup; } BLI_array_growone(region); BLI_array_growone(regions); regions[BLI_array_count(regions)-1] = region; region[BLI_array_count(region)-1] = NULL; } for (i=0; ie; } if (!region[0]) { BMO_RaiseError(bm, op, BMERR_DISSOLVEFACES_FAILED, "Could not find boundary of dissolve region"); goto cleanup; } if (region[0]->e->v1 == region[0]->v) f= BM_Make_Ngon(bm, region[0]->e->v1, region[0]->e->v2, edges, j, 1); else f= BM_Make_Ngon(bm, region[0]->e->v2, region[0]->e->v1, edges, j, 1); BLI_array_free(edges); if (!f) { BMO_RaiseError(bm, op, BMERR_DISSOLVEFACES_FAILED, "Could not create merged face"); goto cleanup; } /*if making the new face failed (e.g. overlapping test) unmark the original faces for deletion.*/ BMO_ClearFlag(bm, f, FACE_ORIG); BMO_SetFlag(bm, f, FACE_NEW); fcopied = 0; l=BMIter_New(&liter, bm, BM_LOOPS_OF_FACE, f); for (; l; l=BMIter_Step(&liter)) { /*ensure winding is identical*/ l2 = BMIter_New(&liter2, bm, BM_LOOPS_OF_LOOP, l); for (; l2; l2=BMIter_Step(&liter2)) { if (BMO_TestFlag(bm, l2->f, FACE_ORIG)) { if (l2->v != l->v) bmesh_loop_reverse(bm, l2->f); break; } } /*copy over attributes*/ l2 = BMIter_New(&liter2, bm, BM_LOOPS_OF_LOOP, l); for (; l2; l2=BMIter_Step(&liter2)) { if (BMO_TestFlag(bm, l2->f, FACE_ORIG)) { if (!fcopied) { BM_Copy_Attributes(bm, bm, l2->f, f); fcopied = 1; } BM_Copy_Attributes(bm, bm, l2, l); break; } } } } BMO_CallOpf(bm, "del geom=%ff context=%d", FACE_ORIG, DEL_FACES); if (BMO_HasError(bm)) goto cleanup; BMO_Flag_To_Slot(bm, op, "regionout", FACE_NEW, BM_FACE); cleanup: /*free/cleanup*/ for (i=0; iv1, VERT_MARK); BMO_SetFlag(bm, e->v2, VERT_MARK); BM_Join_Faces(bm, e->loop->f, ((BMLoop*)e->loop->radial.next->data)->f, e); } } BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { if (BMO_TestFlag(bm, v, VERT_MARK) && BM_Vert_EdgeCount(v) == 2) { BLI_array_growone(verts); verts[BLI_array_count(verts)-1] = v; } } /*clean up extreneous 2-valence vertices*/ for (i=0; iedge, verts[i], 1.0); } BLI_array_free(verts); //BMO_InitOpf(bm, &fop, "dissolvefaces faces=%ff", FACE_MARK); //BMO_Exec_Op(bm, &fop); //BMO_CopySlot(op, &fop, "regionout", "regionout"); //BMO_Finish_Op(bm, &fop); } void dissolveedges_exec(BMesh *bm, BMOperator *op) { BMOperator fop; BMOIter oiter; BMIter iter; BMVert *v; BMEdge *e; BMFace *f; int i; BMO_ITER(e, &oiter, bm, op, "edges", BM_EDGE) { if (BM_Edge_FaceCount(e) == 2) { BM_Join_Faces(bm, e->loop->f, ((BMLoop*)e->loop->radial.next->data)->f, e); } } } static int test_extra_verts(BMesh *bm, BMVert *v) { BMIter iter, liter, iter2, iter3; BMFace *f, *f2; BMLoop *l; BMEdge *e; int found; /*test faces around verts for verts that would be wronly killed by dissolve faces.*/ f = BMIter_New(&iter, bm, BM_FACES_OF_VERT, v); for (; f; f=BMIter_Step(&iter)) { l=BMIter_New(&liter, bm, BM_LOOPS_OF_FACE, f); for (; l; l=BMIter_Step(&liter)) { if (!BMO_TestFlag(bm, l->v, VERT_MARK)) { /*if an edge around a vert is a boundary edge, then dissolve faces won't destroy it. also if it forms a boundary with one of the face regions*/ found = 0; e = BMIter_New(&iter2, bm, BM_EDGES_OF_VERT, l->v); for (; e; e=BMIter_Step(&iter2)) { if (BM_Edge_FaceCount(e)==1) found = 1; f2 = BMIter_New(&iter3, bm, BM_FACES_OF_EDGE, e); for (; f2; f2=BMIter_Step(&iter3)) { if (!BMO_TestFlag(bm, f2, FACE_MARK)) { found = 1; break; } } if (found) break; } if (!found) return 0; } } } return 1; } void dissolveverts_exec(BMesh *bm, BMOperator *op) { BMOpSlot *vinput; BMIter iter, fiter; BMVert *v; BMFace *f; int i; vinput = BMO_GetSlot(op, "verts"); BMO_Flag_Buffer(bm, op, "verts", VERT_MARK, BM_VERT); for (v=BMIter_New(&iter, bm, BM_VERTS_OF_MESH, NULL); v; v=BMIter_Step(&iter)) { if (BMO_TestFlag(bm, v, VERT_MARK)) { /*check if it's a two-valence vert*/ if (BM_Vert_EdgeCount(v) == 2) { /*collapse the vert*/ BM_Collapse_Vert(bm, v->edge, v, 0.5f); continue; } f=BMIter_New(&fiter, bm, BM_FACES_OF_VERT, v); for (; f; f=BMIter_Step(&fiter)) { BMO_SetFlag(bm, f, FACE_ORIG); BMO_SetFlag(bm, f, FACE_MARK); } /*check if our additions to the input to face dissolve will destroy nonmarked vertices.*/ if (!test_extra_verts(bm, v)) { f=BMIter_New(&fiter, bm, BM_FACES_OF_VERT, v); for (; f; f=BMIter_Step(&fiter)) { if (BMO_TestFlag(bm, f, FACE_ORIG)) { BMO_ClearFlag(bm, f, FACE_MARK); BMO_ClearFlag(bm, f, FACE_ORIG); } } } else { f=BMIter_New(&fiter, bm, BM_FACES_OF_VERT, v); for (; f; f=BMIter_Step(&fiter)) { BMO_ClearFlag(bm, f, FACE_ORIG); } } } } BMO_CallOpf(bm, "dissolvefaces faces=%ff", FACE_MARK); if (BMO_HasError(bm)) { char *msg; BMO_GetError(bm, &msg, NULL); BMO_ClearStack(bm); BMO_RaiseError(bm, op, BMERR_DISSOLVEVERTS_FAILED,msg); } /*clean up any remaining*/ for (v=BMIter_New(&iter, bm, BM_VERTS_OF_MESH, NULL); v; v=BMIter_Step(&iter)) { if (BMO_TestFlag(bm, v, VERT_MARK)) { if (!BM_Dissolve_Vert(bm, v)) { BMO_RaiseError(bm, op, BMERR_DISSOLVEVERTS_FAILED, NULL); return; } } } } /*this code is for cleaning up two-edged faces, it shall become it's own function one day.*/ #if 0 /*clean up two-edged faces*/ /*basic idea is to keep joining 2-edged faces until their gone. this however relies on joining two 2-edged faces together to work, which doesn't.*/ found3 = 1; while (found3) { found3 = 0; for (f=BMIter_New(&iter, bm, BM_FACES_OF_MESH, NULL); f; f=BMIter_Step(&iter)){ if (!BM_Validate_Face(bm, f, stderr)) { printf("error.\n"); } if (f->len == 2) { //this design relies on join faces working //with two-edged faces properly. //commenting this line disables the //outermost loop. //found3 = 1; found2 = 0; l = BMIter_New(&liter, bm, BM_LOOPS_OF_FACE, f); fe = l->e; for (; l; l=BMIter_Step(&liter)) { f2 = BMIter_New(&fiter, bm, BM_FACES_OF_EDGE, l->e); for (; f2; f2=BMIter_Step(&fiter)) { if (f2 != f) { BM_Join_Faces(bm, f, f2, l->e); found2 = 1; break; } } if (found2) break; } if (!found2) { bmesh_kf(bm, f); bmesh_ke(bm, fe); } } /*else if (f->len == 3) { BMEdge *ed[3]; BMVert *vt[3]; BMLoop *lp[3]; int i=0; //check for duplicate edges l = BMIter_New(&liter, bm, BM_LOOPS_OF_FACE, f); for (; l; l=BMIter_Step(&liter)) { ed[i] = l->e; lp[i] = l; vt[i++] = l->v; } if (vt[0] == vt[1] || vt[0] == vt[2]) { i += 1; } }*/ } } if (oldlen == len) break; oldlen = len; #endif