#include "MEM_guardedalloc.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" #include "BLI_memarena.h" #include "BLI_array.h" #include "BLI_math.h" #include "BLI_array.h" #include "BLI_utildefines.h" #include "BLI_smallhash.h" #include "bmesh.h" #include "bmesh_operators_private.h" #define BEVEL_FLAG 1 #define BEVEL_DEL 2 #define FACE_NEW 4 #define EDGE_OLD 8 #define FACE_OLD 16 #define FACE_DONE 32 #define VERT_OLD 64 #define FACE_SPAN 128 #define FACE_HOLE 256 typedef struct LoopTag { BMVert *newv; } LoopTag; typedef struct EdgeTag { BMVert *newv1, *newv2; } EdgeTag; static void calc_corner_co(BMesh *bm, BMLoop *l, const float fac, float r_co[3]) { float no[3], l_vec_prev[3], l_vec_next[3], l_co_prev[3], l_co[3], l_co_next[3]; int is_concave; if (l->f->len > 2) { copy_v3_v3(l_co_prev, l->prev->v->co); copy_v3_v3(l_co, l->v->co); copy_v3_v3(l_co_next, l->next->v->co); /*calculate normal*/ sub_v3_v3v3(l_vec_prev, l_co_prev, l_co); sub_v3_v3v3(l_vec_next, l_co_next, l_co); cross_v3_v3v3(no, l_vec_prev, l_vec_next); is_concave= dot_v3v3(no, l->f->no) > 0.0f; } else { BMIter iter; BMLoop *l2; float up[3] = {0.0f, 0.0f, 1.0f}; copy_v3_v3(l_co_prev, l->prev->v->co); copy_v3_v3(l_co, l->v->co); BM_ITER(l2, &iter, bm, BM_LOOPS_OF_VERT, l->v) { if (l2->f != l->f) { copy_v3_v3(l_co_next, BM_OtherEdgeVert(l2->e, l2->next->v)->co); break; } } sub_v3_v3v3(l_vec_prev, l_co_prev, l_co); sub_v3_v3v3(l_vec_next, l_co_next, l_co); cross_v3_v3v3(no, l_vec_prev, l_vec_next); if (dot_v3v3(no, no) == 0.0f) { no[0] = no[1] = 0.0f; no[2] = -1.0f; } is_concave = dot_v3v3(no, up) < 0.0f; } if (1) { /*simple percentage method */ /* not strictly necessary, balance vectors * so the longer edge doesn't skew the result, * gives nicer, move even output */ float medium= (normalize_v3(l_vec_prev) + normalize_v3(l_vec_next)) / 2.0f; mul_v3_fl(l_vec_prev, medium); mul_v3_fl(l_vec_next, medium); /* done */ add_v3_v3(l_vec_prev, l_vec_next); mul_v3_fl(l_vec_prev, fac * 0.5); if (is_concave) negate_v3(l_vec_prev); add_v3_v3v3(r_co, l_vec_prev, l->v->co); } else { /* distance based */ float tvec[3]; float dist; normalize_v3(l_vec_prev); normalize_v3(l_vec_next); /* get the medium normalized direction */ add_v3_v3v3(tvec, l_vec_prev,l_vec_next); normalize_v3(tvec); /* for angle calculation */ negate_v3(l_vec_next); dist= shell_angle_to_dist(angle_normalized_v3v3(l_vec_prev, l_vec_next) * 0.5); mul_v3_fl(tvec, fac * dist); if (is_concave) negate_v3(tvec); add_v3_v3v3(r_co, tvec, l->v->co); } } #define ETAG_SET(e, v, nv) (v) == (e)->v1 ? (etags[BM_GetIndex((e))].newv1 = (nv)) : (etags[BM_GetIndex((e))].newv2 = (nv)) #define ETAG_GET(e, v) ((v) == (e)->v1 ? (etags[BM_GetIndex((e))].newv1) : (etags[BM_GetIndex((e))].newv2)) void bmesh_bevel_exec(BMesh *bm, BMOperator *op) { BMOIter siter; BMIter iter; BMEdge *e; BMVert *v; BMFace **faces = NULL, *f; LoopTag *tags=NULL, *tag; EdgeTag *etags = NULL; BMVert **verts = NULL; BMEdge **edges = NULL; BLI_array_declare(faces); BLI_array_declare(tags); BLI_array_declare(etags); BLI_array_declare(verts); BLI_array_declare(edges); SmallHash hash; float fac = BMO_Get_Float(op, "percent"); int i, li, has_elens, HasMDisps = CustomData_has_layer(&bm->ldata, CD_MDISPS); has_elens = CustomData_has_layer(&bm->edata, CD_PROP_FLT) && BMO_Get_Int(op, "uselengths"); if (has_elens) { li = BMO_Get_Int(op, "lengthlayer"); } BLI_smallhash_init(&hash); BMO_ITER(e, &siter, bm, op, "geom", BM_EDGE) { BMO_SetFlag(bm, e, BEVEL_FLAG|BEVEL_DEL); BMO_SetFlag(bm, e->v1, BEVEL_FLAG|BEVEL_DEL); BMO_SetFlag(bm, e->v2, BEVEL_FLAG|BEVEL_DEL); if (BM_Edge_FaceCount(e) < 2) { BMO_ClearFlag(bm, e, BEVEL_DEL); BMO_ClearFlag(bm, e->v1, BEVEL_DEL); BMO_ClearFlag(bm, e->v2, BEVEL_DEL); } #if 0 if (BM_Edge_FaceCount(e) == 0) { BMVert *verts[2] = {e->v1, e->v2}; BMEdge *edges[2] = {e, BM_Make_Edge(bm, e->v1, e->v2, e, 0)}; BMO_SetFlag(bm, edges[1], BEVEL_FLAG); BM_Make_Face(bm, verts, edges, 2, 0); } #endif } BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { BMO_SetFlag(bm, v, VERT_OLD); } #if 0 //a bit of cleaner code that, alas, doens't work. /*build edge tags*/ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { if (BMO_TestFlag(bm, e->v1, BEVEL_FLAG) || BMO_TestFlag(bm, e->v2, BEVEL_FLAG)) { BMIter liter; BMLoop *l; if (!BMO_TestFlag(bm, e, EDGE_OLD)) { BM_SetIndex(e, BLI_array_count(etags)); /* set_dirty! */ BLI_array_growone(etags); BMO_SetFlag(bm, e, EDGE_OLD); } BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) { BMLoop *l2; BMIter liter2; if (BMO_TestFlag(bm, l->f, BEVEL_FLAG)) continue; BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_FACE, l->f) { BM_SetIndex(l2, BLI_array_count(tags)); /* set_dirty! */ BLI_array_growone(tags); if (!BMO_TestFlag(bm, l2->e, EDGE_OLD)) { BM_SetIndex(l2->e, BLI_array_count(etags)); /* set_dirty! */ BLI_array_growone(etags); BMO_SetFlag(bm, l2->e, EDGE_OLD); } } BMO_SetFlag(bm, l->f, BEVEL_FLAG); BLI_array_append(faces, l->f); } } else { BM_SetIndex(e, -1); /* set_dirty! */ } } #endif /*create and assign looptag structures*/ BMO_ITER(e, &siter, bm, op, "geom", BM_EDGE) { BMLoop *l; BMIter liter; BMO_SetFlag(bm, e->v1, BEVEL_FLAG|BEVEL_DEL); BMO_SetFlag(bm, e->v2, BEVEL_FLAG|BEVEL_DEL); if (BM_Edge_FaceCount(e) < 2) { BMO_ClearFlag(bm, e, BEVEL_DEL); BMO_ClearFlag(bm, e->v1, BEVEL_DEL); BMO_ClearFlag(bm, e->v2, BEVEL_DEL); } if (!BLI_smallhash_haskey(&hash, (intptr_t)e)) { BLI_array_growone(etags); BM_SetIndex(e, BLI_array_count(etags)-1); /* set_dirty! */ BLI_smallhash_insert(&hash, (intptr_t)e, NULL); BMO_SetFlag(bm, e, EDGE_OLD); } /*find all faces surrounding e->v1 and, e->v2*/ for (i=0; i<2; i++) { BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, i?e->v2:e->v1) { BMLoop *l2; BMIter liter2; /*see if we've already processed this loop's face*/ if (BLI_smallhash_haskey(&hash, (intptr_t)l->f)) continue; /*create tags for all loops in l->f*/ BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_FACE, l->f) { BLI_array_growone(tags); BM_SetIndex(l2, BLI_array_count(tags)-1); /* set_loop */ if (!BLI_smallhash_haskey(&hash, (intptr_t)l2->e)) { BLI_array_growone(etags); BM_SetIndex(l2->e, BLI_array_count(etags)-1); /* set_dirty! */ BLI_smallhash_insert(&hash, (intptr_t)l2->e, NULL); BMO_SetFlag(bm, l2->e, EDGE_OLD); } } BLI_smallhash_insert(&hash, (intptr_t)l->f, NULL); BMO_SetFlag(bm, l->f, BEVEL_FLAG); BLI_array_append(faces, l->f); } } } bm->elem_index_dirty |= BM_EDGE; BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { BMIter eiter; if (!BMO_TestFlag(bm, v, BEVEL_FLAG)) continue; BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { if (!BMO_TestFlag(bm, e, BEVEL_FLAG) && !ETAG_GET(e, v)) { BMVert *v2; float co[3]; v2 = BM_OtherEdgeVert(e, v); sub_v3_v3v3(co, v2->co, v->co); if (has_elens) { float elen = *(float*)CustomData_bmesh_get_n(&bm->edata, e->head.data, CD_PROP_FLT, li); normalize_v3(co); mul_v3_fl(co, elen); } mul_v3_fl(co, fac); add_v3_v3(co, v->co); v2 = BM_Make_Vert(bm, co, v); ETAG_SET(e, v, v2); } } } for (i=0; ie, BEVEL_FLAG)) { if (BMO_TestFlag(bm, l->prev->e, BEVEL_FLAG)) { tag = tags + BM_GetIndex(l); calc_corner_co(bm, l, fac, co); tag->newv = BM_Make_Vert(bm, co, l->v); } else { tag = tags + BM_GetIndex(l); tag->newv = ETAG_GET(l->prev->e, l->v); if (!tag->newv) { sub_v3_v3v3(co, l->prev->v->co, l->v->co); if (has_elens) { float elen = *(float*)CustomData_bmesh_get_n(&bm->edata, l->prev->e->head.data, CD_PROP_FLT, li); normalize_v3(co); mul_v3_fl(co, elen); } mul_v3_fl(co, fac); add_v3_v3(co, l->v->co); tag->newv = BM_Make_Vert(bm, co, l->v); ETAG_SET(l->prev->e, l->v, tag->newv); } } } else if (BMO_TestFlag(bm, l->v, BEVEL_FLAG)) { tag = tags + BM_GetIndex(l); tag->newv = ETAG_GET(l->e, l->v); if (!tag->newv) { sub_v3_v3v3(co, l->next->v->co, l->v->co); if (has_elens) { float elen = *(float*)CustomData_bmesh_get_n(&bm->edata, l->e->head.data, CD_PROP_FLT, li); normalize_v3(co); mul_v3_fl(co, elen); } mul_v3_fl(co, fac); add_v3_v3(co, l->v->co); tag = tags + BM_GetIndex(l); tag->newv = BM_Make_Vert(bm, co, l->v); ETAG_SET(l->e, l->v, tag->newv); } } else { tag = tags + BM_GetIndex(l); tag->newv = l->v; BMO_ClearFlag(bm, l->v, BEVEL_DEL); } } } /*create new faces inset from original faces*/ for (i=0; inewv); if (!firstv) firstv = tag->newv; if (lastv) { e = BM_Make_Edge(bm, lastv, tag->newv, l->e, 1); BM_Copy_Attributes(bm, bm, l->prev->e, e); BLI_array_append(edges, e); } lastv=tag->newv; v2 = ETAG_GET(l->e, l->next->v); tag = tags + BM_GetIndex(l->next); if (!BMO_TestFlag(bm, l->e, BEVEL_FLAG) && v2 && v2 != tag->newv) { BLI_array_append(verts, v2); e = BM_Make_Edge(bm, lastv, v2, l->e, 1); BM_Copy_Attributes(bm, bm, l->e, e); BLI_array_append(edges, e); lastv = v2; } } e = BM_Make_Edge(bm, firstv, lastv, bm_firstfaceloop(faces[i])->e, 1); if (bm_firstfaceloop(faces[i])->prev->e != e) BM_Copy_Attributes(bm, bm, bm_firstfaceloop(faces[i])->prev->e, e); BLI_array_append(edges, e); f = BM_Make_Ngon(bm, verts[0], verts[1], edges, BLI_array_count(edges), 0); if (!f) { printf("%s: could not make face!\n", __func__); continue; } BMO_SetFlag(bm, f, FACE_NEW); } for (i=0; ie, BEVEL_FLAG)) continue; v1 = tags[BM_GetIndex(l)].newv; v2 = tags[BM_GetIndex(l->next)].newv; if (l->radial_next != l) { v3 = tags[BM_GetIndex(l->radial_next)].newv; if (l->radial_next->next->v == l->next->v) { v4 = v3; v3 = tags[BM_GetIndex(l->radial_next->next)].newv; } else { v4 = tags[BM_GetIndex(l->radial_next->next)].newv; } } else { /*the loop is on a boundary*/ v3 = l->next->v; v4 = l->v; for (j=0; j<2; j++) { BMIter eiter; BMVert *v = j ? v4 : v3; BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { if (!BM_Vert_In_Edge(e, v3) || !BM_Vert_In_Edge(e, v4)) continue; if (!BMO_TestFlag(bm, e, BEVEL_FLAG) && BMO_TestFlag(bm, e, EDGE_OLD)) { BMVert *vv; vv = ETAG_GET(e, v); if (!vv || BMO_TestFlag(bm, vv, BEVEL_FLAG)) continue; if (j) v1 = vv; else v2 = vv; break; } } } BMO_ClearFlag(bm, v3, BEVEL_DEL); BMO_ClearFlag(bm, v4, BEVEL_DEL); } if (v1 != v2 && v2 != v3 && v3 != v4) { BMIter liter2; BMLoop *l2, *l3; BMEdge *e1, *e2; float d1, d2, *d3; f = BM_Make_QuadTri(bm, v4, v3, v2, v1, l->f, 1); e1 = BM_Edge_Exist(v4, v3); e2 = BM_Edge_Exist(v2, v1); BM_Copy_Attributes(bm, bm, l->e, e1); BM_Copy_Attributes(bm, bm, l->e, e2); /*set edge lengths of cross edges as the average of the cross edges they're based on*/ if (has_elens) { #if 0 /* angle happens not to be used. why? - not sure it just isnt - campbell. leave this in incase we need to use it later */ float ang; #endif e1 = BM_Edge_Exist(v1, v4); e2 = BM_Edge_Exist(v2, v3); if (l->radial_next->v == l->v) { l2 = l->radial_next->prev; l3 = l->radial_next->next; } else { l2 = l->radial_next->next; l3 = l->radial_next->prev; } d3 = CustomData_bmesh_get_n(&bm->edata, e1->head.data, CD_PROP_FLT, li); d1 = *(float*)CustomData_bmesh_get_n(&bm->edata, l->prev->e->head.data, CD_PROP_FLT, li); d2 = *(float*)CustomData_bmesh_get_n(&bm->edata, l2->e->head.data, CD_PROP_FLT, li); #if 0 ang = angle_v3v3v3(l->prev->v->co, l->v->co, BM_OtherEdgeVert(l2->e, l->v)->co); #endif *d3 = (d1+d2)*0.5f; d3 = CustomData_bmesh_get_n(&bm->edata, e2->head.data, CD_PROP_FLT, li); d1 = *(float*)CustomData_bmesh_get_n(&bm->edata, l->next->e->head.data, CD_PROP_FLT, li); d2 = *(float*)CustomData_bmesh_get_n(&bm->edata, l3->e->head.data, CD_PROP_FLT, li); #if 0 ang = angle_v3v3v3(BM_OtherEdgeVert(l->next->e, l->next->v)->co, l->next->v->co, BM_OtherEdgeVert(l3->e, l->next->v)->co); #endif *d3 = (d1+d2)*0.5f; } if (!f) { fprintf(stderr, "%s: face index out of range! (bmesh internal error)\n", __func__); continue; } BMO_SetFlag(bm, f, FACE_NEW|FACE_SPAN); /*un-tag edges in f for deletion*/ BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_FACE, f) { BMO_ClearFlag(bm, l2->e, BEVEL_DEL); } } else { f = NULL; } } } /*fill in holes at vertices*/ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { BMIter eiter; BMVert *vv, *vstart=NULL, *lastv=NULL; SmallHash tmphash; int rad, insorig=0, err=0; BLI_smallhash_init(&tmphash); if (!BMO_TestFlag(bm, v, BEVEL_FLAG)) continue; BLI_array_empty(verts); BLI_array_empty(edges); BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { BMIter liter; BMVert *v1=NULL, *v2=NULL; BMLoop *l; if (BM_Edge_FaceCount(e) < 2) insorig = 1; if (BM_GetIndex(e) == -1) continue; rad = 0; BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) { if (!BMO_TestFlag(bm, l->f, FACE_OLD)) continue; rad++; if (l->v == v) tag = tags + BM_GetIndex(l); else tag = tags + BM_GetIndex(l->next); if (!v1) v1 = tag->newv; else if (!v2) v2 = tag->newv; } if (rad < 2) insorig = 1; if (!v1) v1 = ETAG_GET(e, v); if (!v2 || v1 == v2) v2 = ETAG_GET(e, v); if (v1) { if (!BLI_smallhash_haskey(&tmphash, (intptr_t)v1)) { BLI_array_append(verts, v1); BLI_smallhash_insert(&tmphash, (intptr_t)v1, NULL); } if (v2 && v1 != v2 && !BLI_smallhash_haskey(&tmphash, (intptr_t)v2)) { BLI_array_append(verts, v2); BLI_smallhash_insert(&tmphash, (intptr_t)v2, NULL); } } } if (!BLI_array_count(verts)) continue; if (insorig) { BLI_array_append(verts, v); BLI_smallhash_insert(&tmphash, (intptr_t)v, NULL); } /*find edges that exist between vertices in verts. this is basically a topological walk of the edges connecting them.*/ vstart = vstart ? vstart : verts[0]; vv = vstart; do { BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, vv) { BMVert *vv2 = BM_OtherEdgeVert(e, vv); if (vv2 != lastv && BLI_smallhash_haskey(&tmphash, (intptr_t)vv2)) { /*if we've go over the same vert twice, break out of outer loop*/ if (BLI_smallhash_lookup(&tmphash, (intptr_t)vv2) != NULL) { e = NULL; err = 1; break; } /*use self pointer as tag*/ BLI_smallhash_remove(&tmphash, (intptr_t)vv2); BLI_smallhash_insert(&tmphash, (intptr_t)vv2, vv2); lastv = vv; BLI_array_append(edges, e); vv = vv2; break; } } if (e == NULL) break; } while (vv != vstart); if (err) continue; /*there may not be a complete loop of edges, so start again and make final edge afterwards. in this case, the previous loop worked to find one of the two edges at the extremes.*/ if (vv != vstart) { /*undo previous tagging*/ for (i=0; i= 3) { BMFace *f; if (BM_Face_Exists(bm, verts, BLI_array_count(verts), &f)) continue; f = BM_Make_Ngon(bm, lastv, vstart, edges, BLI_array_count(edges), 0); if (!f) { fprintf(stderr, "%s: in bevel vert fill! (bmesh internal error)\n", __func__); } else { BMO_SetFlag(bm, f, FACE_NEW|FACE_HOLE); } } BLI_smallhash_release(&tmphash); } /*copy over customdata*/ for (i=0; inewv) continue; BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_VERT, tag->newv) { if (!BMO_TestFlag(bm, l2->f, FACE_NEW) || (l2->v != tag->newv && l2->v != l->v)) continue; if (tag->newv != l->v || HasMDisps) { BM_Copy_Attributes(bm, bm, l->f, l2->f); BM_loop_interp_from_face(bm, l2, l->f, 1, 1); } else { BM_Copy_Attributes(bm, bm, l->f, l2->f); BM_Copy_Attributes(bm, bm, l, l2); } if (HasMDisps) { BMLoop *l3; BMIter liter3; BM_ITER(l3, &liter3, bm, BM_LOOPS_OF_FACE, l2->f) { BM_loop_interp_multires(bm, l3, l->f); } } } } } /*handle vertices along boundary edges*/ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { if (BMO_TestFlag(bm, v, VERT_OLD) && BMO_TestFlag(bm, v, BEVEL_FLAG) && !BMO_TestFlag(bm, v, BEVEL_DEL)) { BMLoop *l; BMLoop *lorig=NULL; BMIter liter; BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) { // BMIter liter2; // BMLoop *l2 = l->v == v ? l : l->next, *l3; if (BMO_TestFlag(bm, l->f, FACE_OLD)) { lorig = l; break; } } if (!lorig) continue; BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) { BMLoop *l2 = l->v == v ? l : l->next; BM_Copy_Attributes(bm, bm, lorig->f, l2->f); BM_Copy_Attributes(bm, bm, lorig, l2); } } } #if 0 /*clean up any remaining 2-edged faces*/ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { if (f->len == 2) { BMFace *faces[2] = {f, bm_firstfaceloop(f)->radial_next->f}; if (faces[0] == faces[1]) BM_Kill_Face(bm, f); else BM_Join_Faces(bm, faces, 2); } } #endif BMO_CallOpf(bm, "del geom=%fv context=%i", BEVEL_DEL, DEL_VERTS); /*clean up any edges that might not get properly deleted*/ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { if (BMO_TestFlag(bm, e, EDGE_OLD) && !e->l) BMO_SetFlag(bm, e, BEVEL_DEL); } BMO_CallOpf(bm, "del geom=%fe context=%i", BEVEL_DEL, DEL_EDGES); BMO_CallOpf(bm, "del geom=%ff context=%i", BEVEL_DEL, DEL_FACES); BLI_smallhash_release(&hash); BLI_array_free(tags); BLI_array_free(etags); BLI_array_free(verts); BLI_array_free(edges); BLI_array_free(faces); BMO_Flag_To_Slot(bm, op, "face_spans", FACE_SPAN, BM_FACE); BMO_Flag_To_Slot(bm, op, "face_holes", FACE_HOLE, BM_FACE); }