diff options
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r-- | source/blender/blenkernel/BKE_pbvh.h | 6 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/pbvh_bmesh.c | 913 |
2 files changed, 875 insertions, 44 deletions
diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index c8c693fc342..81e7ce1cdbe 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -141,9 +141,11 @@ struct BMesh *BKE_pbvh_get_bmesh(PBVH *pbvh); void BKE_pbvh_bmesh_detail_size_set(PBVH *pbvh, float detail_size); typedef enum { - PBVH_Subdivide = 1, - PBVH_Collapse = 2, + PBVH_Subdivide = (1 << 0), + PBVH_Collapse = (1 << 1), + PBVH_TopologyGenus = (1 << 2), } PBVHTopologyUpdateMode; + bool BKE_pbvh_bmesh_update_topology(PBVH *bvh, PBVHTopologyUpdateMode mode, const float center[3], float radius); diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c index 9e18d54614b..e0cf9eeeae1 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.c +++ b/source/blender/blenkernel/intern/pbvh_bmesh.c @@ -25,10 +25,13 @@ #include "MEM_guardedalloc.h" #include "BLI_utildefines.h" +#include "BLI_alloca.h" +#include "BLI_array.h" #include "BLI_buffer.h" #include "BLI_ghash.h" #include "BLI_heap.h" #include "BLI_math.h" +#include "BLI_listbase.h" #include "BKE_ccg.h" #include "BKE_DerivedMesh.h" @@ -41,6 +44,9 @@ #include <assert.h> +/* removes skinny areas after making holes (is rather ugly without this) */ +#define USE_HOLE_VOLUME_CLEAN + /****************************** Building ******************************/ /* Update node data after splitting */ @@ -627,6 +633,80 @@ static void long_edge_queue_face_add(EdgeQueueContext *eq_ctx, } } +#ifdef USE_HOLE_VOLUME_CLEAN + +#define SHARP_THRESHOLD 0.5f + +/** + * 1.0 == sharp, 0.0 == flat + */ +static float bm_edge_calc_sharpness(BMEdge *e) +{ + if (BM_edge_is_manifold(e)) { + float axis[3], ang; + sub_v3_v3v3(axis, e->v1->co, e->v2->co); + ang = angle_on_axis_v3v3v3_v3( + e->l->prev->v->co, + e->v1->co, + e->l->radial_next->prev->v->co, + axis); + return 1.0f - (ang / M_PI); + } + else { + return 1.0f; + } +} + +/** + * volume weighted by sharpness + */ +static float bm_edge_calc_volume_weighted(BMEdge *e) +{ + BMLoop *l_a, *l_b; + + if (BM_edge_loop_pair(e, &l_a, &l_b)) { + BMVert *v1_alt = l_a->prev->v; + BMVert *v2_alt = l_b->prev->v; + float sharp; + + sharp = bm_edge_calc_sharpness(e); + + if (sharp > SHARP_THRESHOLD) { + /* remap from the threshold back to 0-1 */ + sharp = (sharp - SHARP_THRESHOLD) / (1.0 - SHARP_THRESHOLD); + return volume_tetrahedron_v3(e->v1->co, e->v2->co, v1_alt->co, v2_alt->co) / (FLT_EPSILON + (sharp)); + } + } + + return FLT_MAX; +} + +static void tetrahedron_edge_queue_edge_add(EdgeQueueContext *eq_ctx, + BMEdge *e) +{ + const float volume = bm_edge_calc_volume_weighted(e); + if (volume != FLT_MAX) { + edge_queue_insert(eq_ctx, e, volume); + } +} + +static void tetrahedron_edge_queue_face_add(EdgeQueueContext *eq_ctx, + BMFace *f) +{ + if (edge_queue_tri_in_sphere(eq_ctx->q, f)) { + BMLoop *l_iter; + BMLoop *l_first; + + /* Check each edge of the face */ + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + tetrahedron_edge_queue_edge_add(eq_ctx, l_iter->e); + } while ((l_iter = l_iter->next) != l_first); + } +} + +#endif /* USE_HOLE_VOLUME_CLEAN */ + static void short_edge_queue_face_add(EdgeQueueContext *eq_ctx, BMFace *f) { @@ -722,6 +802,224 @@ static void short_edge_queue_create(EdgeQueueContext *eq_ctx, } } +#ifdef USE_HOLE_VOLUME_CLEAN +static void tetrahedron_edge_queue_create( + EdgeQueueContext *eq_ctx, + PBVH *bvh, const float center[3], + float radius) +{ + int n; + + eq_ctx->q->heap = BLI_heap_new(); + eq_ctx->q->center = center; + eq_ctx->q->radius_squared = radius * radius; + eq_ctx->q->limit_len_squared = bvh->bm_min_edge_len * bvh->bm_min_edge_len; + + for (n = 0; n < bvh->totnode; n++) { + PBVHNode *node = &bvh->nodes[n]; + + /* Check leaf nodes marked for topology update */ + if ((node->flag & PBVH_Leaf) && + (node->flag & PBVH_UpdateTopology) && + !(node->flag & PBVH_FullyHidden)) + { + GSetIterator gs_iter; + + /* Check each face */ + GSET_ITER (gs_iter, node->bm_faces) { + BMFace *f = BLI_gsetIterator_getKey(&gs_iter); + + tetrahedron_edge_queue_face_add(eq_ctx, f); + } + } + } +} +#endif /* USE_HOLE_VOLUME_CLEAN */ + +static void pbvh_bmesh_delete_vert_face(PBVH *bvh, BMVert *v, BMFace *f_del, GSet *deleted_verts, EdgeQueueContext *eq_ctx) +{ + BMLoop *l_iter; + BMVert *v_tri[3]; + BMEdge *e_tri[3]; + int j; + + /* Get vertices and edges of face */ + BLI_assert(f_del->len == 3); + l_iter = BM_FACE_FIRST_LOOP(f_del); + v_tri[0] = l_iter->v; e_tri[0] = l_iter->e; l_iter = l_iter->next; + v_tri[1] = l_iter->v; e_tri[1] = l_iter->e; l_iter = l_iter->next; + v_tri[2] = l_iter->v; e_tri[2] = l_iter->e; + + /* Check if any of the face's vertices are now unused, if so + * remove them from the PBVH */ + for (j = 0; j < 3; j++) { + if (v_tri[j] != v && BM_vert_face_count(v_tri[j]) == 1) { + BLI_gset_insert(deleted_verts, v_tri[j]); + pbvh_bmesh_vert_remove(bvh, v_tri[j]); + } + else { + v_tri[j] = NULL; + } + } + + /* Remove the face */ + pbvh_bmesh_face_remove(bvh, f_del); + BM_face_kill(bvh->bm, f_del); + + /* Check if any of the face's edges are now unused by any + * face, if so delete them */ + for (j = 0; j < 3; j++) { + if (BM_edge_is_wire(e_tri[j])) + BM_edge_kill(bvh->bm, e_tri[j]); + } + + /* Delete unused vertices */ + for (j = 0; j < 3; j++) { + if (v_tri[j]) { + BM_log_vert_removed(bvh->bm_log, v_tri[j], eq_ctx->cd_vert_mask_offset); + BM_vert_kill(bvh->bm, v_tri[j]); + } + } +} + + +/** + * Same as #pbvh_bmesh_delete_vert_face but keeps verts + */ +static void pbvh_bmesh_delete_edge_face(PBVH *bvh, BMFace *f_del) +{ + BMLoop *l_iter; + BMEdge *e_tri[3]; + int j; + + /* Get vertices and edges of face */ + BLI_assert(f_del->len == 3); + l_iter = BM_FACE_FIRST_LOOP(f_del); + e_tri[0] = l_iter->e; l_iter = l_iter->next; + e_tri[1] = l_iter->e; l_iter = l_iter->next; + e_tri[2] = l_iter->e; + + /* Remove the face */ + pbvh_bmesh_face_remove(bvh, f_del); + BM_face_kill(bvh->bm, f_del); + + /* Check if any of the face's edges are now unused by any + * face, if so delete them */ + for (j = 0; j < 3; j++) { + if (BM_edge_is_wire(e_tri[j])) + BM_edge_kill(bvh->bm, e_tri[j]); + } +} + +/* Create a priority queue containing vertex pairs not connected by an + * edge, but close enough as defined by PBVH.bm_min_edge_len. + * + * Only nodes marked for topology update are checked, and in those + * nodes only edges used by a face intersecting the (center, radius) + * sphere are checked. + * + * The highest priority (lowest number) is given to the pair of vertices with + * the shortest distance. + */ +static void close_vert_queue_create(EdgeQueueContext *eq_ctx, + PBVH *bvh, const float center[3], + float radius) +{ + int n; + Heap *distheap = BLI_heap_new(); + BMVert *vprev, *vcur; + int num_close_verts; + /* list of verts for faster traversing */ + ListBase vlist = {0}; + LinkData *curnode; + LinkData *prevnode; + + eq_ctx->q->heap = BLI_heap_new(); + eq_ctx->q->center = center; + eq_ctx->q->radius_squared = radius * radius; + eq_ctx->q->limit_len_squared = bvh->bm_min_edge_len * bvh->bm_min_edge_len; + + for (n = 0; n < bvh->totnode; n++) { + PBVHNode *node = &bvh->nodes[n]; + + /* Check leaf nodes marked for topology update */ + if ((node->flag & PBVH_Leaf) && + (node->flag & PBVH_UpdateTopology) && + !(node->flag & PBVH_FullyHidden)) + { + GSetIterator gs_iter; + + /* Insert vertices in a distance heap */ + GSET_ITER (gs_iter, node->bm_unique_verts) { + BMVert *v = BLI_gsetIterator_getKey(&gs_iter); + float dist_to_center_sq = len_squared_v3v3(eq_ctx->q->center, v->co); + + if (dist_to_center_sq <= eq_ctx->q->radius_squared && check_mask(eq_ctx, v) && !BM_vert_is_boundary(v)) + BLI_heap_insert(distheap, dist_to_center_sq, v); + } + } + } + + num_close_verts = BLI_heap_size(distheap); + + for (n = 0; n < num_close_verts; n++) { + LinkData *d = MEM_callocN(sizeof(*d), "Node"); + d->data = BLI_heap_popmin(distheap); + BLI_addtail(&vlist, d); + } + + BLI_heap_free(distheap, NULL); + + curnode = vlist.first; + + /* iterate the list and make pairs of vertices that are closer than the squared limit distance + * and not on part of the same face. The logic here is borrowed from our remove doubles operator */ + while (curnode) { + vprev = (BMVert *)curnode->data; + + prevnode = curnode; + curnode = curnode->next; + + while (curnode) { + vcur = (BMVert *)curnode->data; + + /* vertices belonging to the same face are not eligible for merging */ + if (!BM_edge_exists(vprev, vcur)) { + float dist_sq = len_squared_v3v3(vcur->co, vprev->co); + + if (dist_sq < eq_ctx->q->limit_len_squared) + { + BMVert **pair = BLI_mempool_alloc(eq_ctx->pool); + pair[0] = vprev; + pair[1] = vcur; + +#if 0 + BLI_heap_insert(eq_ctx->q->heap, + min_ff(len_squared_v3v3(eq_ctx->q->center, vcur->co), + len_squared_v3v3(eq_ctx->q->center, vprev->co)), + pair); +#else + BLI_heap_insert(eq_ctx->q->heap, dist_sq, pair); +#endif + } + + /* the two vertices in the pair are obliterated as part of the genus topology change, get a new + * vertex for processing. If the test does not pass then a new vertex must be used as + * previous because it is guaranteed that next vertices will not pass the test */ + BLI_remlink(&vlist, curnode); + MEM_freeN(curnode); + curnode = prevnode->next; + break; + } + + curnode = curnode->next; + } + } + + BLI_freelistN(&vlist); +} + + /*************************** Topology update **************************/ static void bm_edges_from_tri(BMesh *bm, BMVert *v_tri[3], BMEdge *e_tri[3]) @@ -951,48 +1249,7 @@ static void pbvh_bmesh_collapse_edge( /* Delete the tagged faces */ for (i = 0; i < deleted_faces->count; i++) { BMFace *f_del = BLI_buffer_at(deleted_faces, BMFace *, i); - BMLoop *l_iter; - BMVert *v_tri[3]; - BMEdge *e_tri[3]; - int j; - - /* Get vertices and edges of face */ - BLI_assert(f_del->len == 3); - l_iter = BM_FACE_FIRST_LOOP(f_del); - v_tri[0] = l_iter->v; e_tri[0] = l_iter->e; l_iter = l_iter->next; - v_tri[1] = l_iter->v; e_tri[1] = l_iter->e; l_iter = l_iter->next; - v_tri[2] = l_iter->v; e_tri[2] = l_iter->e; - - /* Check if any of the face's vertices are now unused, if so - * remove them from the PBVH */ - for (j = 0; j < 3; j++) { - if (v_tri[j] != v_del && BM_vert_face_count(v_tri[j]) == 1) { - BLI_gset_insert(deleted_verts, v_tri[j]); - pbvh_bmesh_vert_remove(bvh, v_tri[j]); - } - else { - v_tri[j] = NULL; - } - } - - /* Remove the face */ - pbvh_bmesh_face_remove(bvh, f_del); - BM_face_kill(bvh->bm, f_del); - - /* Check if any of the face's edges are now unused by any - * face, if so delete them */ - for (j = 0; j < 3; j++) { - if (BM_edge_is_wire(e_tri[j])) - BM_edge_kill(bvh->bm, e_tri[j]); - } - - /* Delete unused vertices */ - for (j = 0; j < 3; j++) { - if (v_tri[j]) { - BM_log_vert_removed(bvh->bm_log, v_tri[j], eq_ctx->cd_vert_mask_offset); - BM_vert_kill(bvh->bm, v_tri[j]); - } - } + pbvh_bmesh_delete_vert_face(bvh, v_del, f_del, deleted_verts, eq_ctx); } /* Move v_conn to the midpoint of v_conn and v_del (if v_conn still exists, it @@ -1065,6 +1322,95 @@ static bool pbvh_bmesh_collapse_short_edges( return any_collapsed; } +#ifdef USE_HOLE_VOLUME_CLEAN + +static float len_to_tetrahedron_volume(float f) +{ + return (f * f * f) / 6.0f; +} + +static bool pbvh_bmesh_collapse_small_tetrahedrons( + EdgeQueueContext *eq_ctx, + PBVH *bvh, + BLI_Buffer *deleted_faces) +{ + /* length as a tetrahedron volume, x1.5x to remove more... gives a bit nicer results */ + float min_volume = len_to_tetrahedron_volume(bvh->bm_min_edge_len) * 1.5f; + GSet *deleted_verts; + bool any_collapsed = false; + + deleted_verts = BLI_gset_ptr_new("deleted_verts"); + + while (!BLI_heap_is_empty(eq_ctx->q->heap)) { + BMLoop *l_a, *l_b; + BMVert **pair = BLI_heap_popmin(eq_ctx->q->heap); + BMVert *v1 = pair[0], *v2 = pair[1]; + BMEdge *e; + + /* values on either side of the edge */ + BMLoop *l_adj; + BMVert *v1_alt; + BMVert *v2_alt; + BMEdge *e_alt; + + BLI_mempool_free(eq_ctx->pool, pair); + pair = NULL; + + /* Check the verts still exist */ + if (BLI_gset_haskey(deleted_verts, v1) || + BLI_gset_haskey(deleted_verts, v2)) + { + continue; + } + + /* Check that the edge still exists */ + if (!(e = BM_edge_exists(v1, v2))) { + continue; + } + + if (!BM_edge_loop_pair(e, &l_a, &l_b)) { + continue; + } + + v1_alt = l_a->prev->v; + v2_alt = l_b->prev->v; + + if (bm_edge_calc_volume_weighted(e) > min_volume) { + continue; + } + + /* Check that the edge's vertices are still in the PBVH. It's + * possible that an edge collapse has deleted adjacent faces + * and the node has been split, thus leaving wire edges and + * associated vertices. */ + if ((BM_ELEM_CD_GET_INT(e->v1, eq_ctx->cd_vert_node_offset) == DYNTOPO_NODE_NONE) || + (BM_ELEM_CD_GET_INT(e->v2, eq_ctx->cd_vert_node_offset) == DYNTOPO_NODE_NONE)) + { + continue; + } + + any_collapsed = true; + + /* Remove all faces adjacent to the edge, we _KNOW_ there are 2! */ + while ((l_adj = e->l)) { + BMFace *f_adj = l_adj->f; + pbvh_bmesh_face_remove(bvh, f_adj); + BM_face_kill(bvh->bm, f_adj); + } + + e_alt = BM_edge_create(bvh->bm, v1_alt, v2_alt, NULL, BM_CREATE_NO_DOUBLE); + + pbvh_bmesh_collapse_edge(bvh, e_alt, v1_alt, v2_alt, + deleted_verts, + deleted_faces, eq_ctx); + } + + BLI_gset_free(deleted_verts, NULL); + + return any_collapsed; +} +#endif /* USE_HOLE_VOLUME_CLEAN */ + /************************* Called from pbvh.c *************************/ bool pbvh_bmesh_node_raycast(PBVHNode *node, const float ray_start[3], @@ -1107,6 +1453,462 @@ bool pbvh_bmesh_node_raycast(PBVHNode *node, const float ray_start[3], return hit; } +/* remove sides of the bridge when there are adjacent holes, + * so 2 holes form a larger hole (typically what you want) */ +// #define USE_BRIDGE_MERGE_HOLES + +/* disallow bridges sharing vertices */ +#define USE_BRIDGE_STRICT + +#define USE_BRIDGE_CLEAN + +/* radial walk the vert edges */ +static bool bm_vert_ordered_fan_walk(BMVert *v, BLI_Buffer *buf) +{ + BMLoop *l_iter, *l_first; + BMEdge *e_prev; + BLI_assert(buf->count == 0); + + l_iter = l_first = BM_vert_find_first_loop(v); + if (l_iter == NULL) + return false; + + e_prev = l_first->prev->e; + do { + BMVert *v_other; + /* boundary, we're not interested! */ + if (UNLIKELY(l_iter == NULL)) { + return false; + } + v_other = BM_edge_other_vert(e_prev, v); + BLI_buffer_append(buf, BMVert *, v_other); + +#ifdef USE_BRIDGE_MERGE_HOLES + /* ensure all connected faces have tags disabled */ + { + BMLoop *l_first_radial = l_iter->next->radial_next; + BMLoop *l_iter_radial = l_first_radial->radial_next; + if (l_first_radial != l_iter_radial) { + do { + BM_elem_flag_disable(l_iter_radial->prev->v, BM_ELEM_TAG); + } while ((l_iter_radial = l_iter_radial->radial_next) != l_first_radial); + } + BM_elem_flag_disable(v_other, BM_ELEM_TAG); + } +#endif + + } while (((l_iter = BM_vert_step_fan_loop(l_iter, &e_prev)) != l_first)); + return true; +} + +#ifdef USE_BRIDGE_MERGE_HOLES +static void pbvh_bmesh_delete_edge_face_tagged_radial( + PBVH *bvh, BMLoop *l_rim, EdgeQueueContext *eq_ctx) +{ + /* will only be a single face in most cases (manifold edge) + * but l_rim is attached to the central vertex, so don't touch it */ + BMLoop *l_iter = l_rim->radial_next; + BMLoop *l_next; + if (l_rim != l_iter) { + do { + /* vert on the triangle, not attached to this current edge-loop + * if its tagged we know both bridge-loops share this face and it should be removed */ + BMVert *v_other = l_iter->prev->v; + l_next = l_iter->radial_next; + + if (BM_elem_flag_test(v_other, BM_ELEM_TAG)) { + BLI_assert(!BM_vert_in_face(l_iter->f, l_rim->prev->v)); + pbvh_bmesh_delete_edge_face(bvh, l_iter->f, eq_ctx); + printf("Removing border\n"); + } + } while ((l_iter = l_next) != l_rim); + } +} +#endif /* USE_BRIDGE_MERGE_HOLES */ + +#if defined(USE_BRIDGE_MERGE_HOLES) || defined(USE_BRIDGE_STRICT) +static void bm_earray_tag( + BMElemF **earr, int earr_num, + bool tag) +{ + int i; + + if (tag) { + for (i = 0; i < earr_num; i++) { + BM_elem_flag_enable(earr[i], BM_ELEM_TAG); + } + } + else { + for (i = 0; i < earr_num; i++) { + BM_elem_flag_disable(earr[i], BM_ELEM_TAG); + } + } +} +#endif + +#ifdef USE_BRIDGE_STRICT +static bool bm_earray_tag_check_any( + BMElemF **earr, int earr_num) +{ + int i; + + for (i = 0; i < earr_num; i++) { + if (BM_elem_flag_test(earr[i], BM_ELEM_TAG)) { + return true; + } + } + return false; +} +#endif + +static void bm_varray_center( + const BMVert **varr, const int varr_num, + float r_center[3]) +{ + int i; + + zero_v3(r_center); + for (i = 0; i < varr_num; i++) { + add_v3_v3(r_center, varr[i]->co); + } + mul_v3_fl(r_center, 1.0f / (float)varr_num); +} + +static void bm_varray_normal( + const BMVert **varr, int varr_num, + float r_normal[3]) +{ + const BMVert *v_prev = varr[varr_num - 1]; + const BMVert *v_curr = varr[0]; + int i; + + zero_v3(r_normal); + + /* Newell's Method */ + for (i = 0; i < varr_num; v_prev = v_curr, v_curr = varr[++i]) { + add_newell_cross_v3_v3v3(r_normal, v_prev->co, v_curr->co); + } + + normalize_v3(r_normal); +} + +static void bm_varray_dirs_2d( + const BMVert **varr, int varr_num, + float axis_mat[3][3], const float center[3], + float (*r_dirs)[2]) +{ + float center_2d[2]; + int i; + + mul_v2_m3v3(center_2d, axis_mat, center); + + for (i = 0; i < varr_num; i++) { + mul_v2_m3v3(r_dirs[i], axis_mat, varr[i]->co); + sub_v2_v2(r_dirs[i], center_2d); + normalize_v2(r_dirs[i]); + } +} + +static void pbvh_bridge_loops( + PBVH *bvh, + BMVert **eloop_a, int eloop_a_len, + BMVert **eloop_b, int eloop_b_len) +{ + float (*eloop_a_dirs)[2] = BLI_array_alloca(eloop_a_dirs, eloop_a_len); + float (*eloop_b_dirs)[2] = BLI_array_alloca(eloop_b_dirs, eloop_b_len); + float eloop_a_cent[3], eloop_a_normal[3]; + float eloop_b_cent[3], eloop_b_normal[3]; + float axis[3]; + float axis_mat[3][3]; + int a_offset = 0; + int b_offset = 0; + int a_step_base; + int b_step_base; + + BLI_assert(eloop_a_len >= eloop_b_len); + + bm_varray_center((const BMVert **)eloop_a, eloop_a_len, eloop_a_cent); + bm_varray_center((const BMVert **)eloop_b, eloop_b_len, eloop_b_cent); + + bm_varray_normal((const BMVert **)eloop_a, eloop_a_len, eloop_a_normal); + bm_varray_normal((const BMVert **)eloop_b, eloop_b_len, eloop_b_normal); + + if (dot_v3v3(eloop_a_normal, eloop_b_normal) < 0.0f) { + BLI_array_reverse(eloop_b, eloop_b_len); + negate_v3(eloop_b_normal); + } + + add_v3_v3v3(axis, eloop_a_normal, eloop_b_normal); + normalize_v3(axis); + + axis_dominant_v3_to_m3(axis_mat, axis); + + bm_varray_dirs_2d((const BMVert **)eloop_a, eloop_a_len, axis_mat, eloop_a_cent, eloop_a_dirs); + bm_varray_dirs_2d((const BMVert **)eloop_b, eloop_b_len, axis_mat, eloop_b_cent, eloop_b_dirs); + + /* find closest angle on the smaller loop */ + { + float t_best; + int i; + + a_offset = 0; + + t_best = -1.0f; + for (i = 0; i < eloop_b_len; i++) { + float t = dot_v2v2(eloop_a_dirs[a_offset], eloop_b_dirs[i]); + if (t > t_best) { + t_best = t; + b_offset = i; + } + } + } + + a_step_base = 0; + b_step_base = 0; + + do { + BMFace *f_new; + BMVert *v_tri[3]; + + /* Step (false == a, true == b) */ + bool step_side; + + const int a_curr = ((a_step_base + a_offset)) % eloop_a_len; + const int b_curr = ((b_step_base + b_offset)) % eloop_b_len; + const int a_next = ((a_step_base + a_offset) + 1) % eloop_a_len; + const int b_next = ((b_step_base + b_offset) + 1) % eloop_b_len; + + if ((a_step_base != eloop_a_len) && (b_step_base == eloop_b_len)) { + step_side = false; + } + else if ((a_step_base == eloop_a_len) && (b_step_base != eloop_b_len)) { + step_side = true; + } + else { + if (dot_v2v2(eloop_a_dirs[a_curr], eloop_b_dirs[b_next]) < + dot_v2v2(eloop_a_dirs[a_next], eloop_b_dirs[b_curr])) + { + step_side = false; + } + else { + step_side = true; + } + } + + if (step_side == false) { + v_tri[0] = eloop_b[b_curr]; + v_tri[1] = eloop_a[a_curr]; + v_tri[2] = eloop_a[a_next]; + a_step_base += 1; + } + else { + v_tri[0] = eloop_a[a_curr]; + v_tri[2] = eloop_b[b_curr]; + v_tri[1] = eloop_b[b_next]; + b_step_base += 1; + } + BLI_assert(a_step_base <= eloop_a_len); + BLI_assert(b_step_base <= eloop_b_len); + + /* possible loops share verts */ + if (!ELEM(v_tri[0], v_tri[1], v_tri[2]) && + !BM_face_exists(v_tri, 3, NULL)) + { + BMEdge *e_tri[3]; + int i; + BLI_assert(!ELEM(v_tri[1], v_tri[2], v_tri[0]) && + !ELEM(v_tri[2], v_tri[0], v_tri[1])); + + bm_edges_from_tri(bvh->bm, v_tri, e_tri); + + /* (v_tri[1] -> v_tri[2]), needs to have a face else we ignore */ + if (e_tri[1]->l) { + BMLoop *l_rim = e_tri[1]->l; + BMFace *f_rim = l_rim->f; + PBVHNode *n = pbvh_bmesh_node_lookup(bvh, f_rim); + const int ni = n - bvh->nodes; + + /* winding should be correct in most cases (when verts face away from eachother at least)... + * Even so, better base it off adjacent face to keep normals contiguous. */ + if (l_rim->v == v_tri[1]) { + SWAP(BMVert *, v_tri[1], v_tri[2]); + SWAP(BMEdge *, e_tri[0], e_tri[2]); + } + + f_new = pbvh_bmesh_face_create(bvh, ni, v_tri, e_tri, f_rim); + (void)f_new; + + for (i = 0; i < 3; i++) { + if (!BLI_gset_haskey(n->bm_unique_verts, v_tri[i]) && + !BLI_gset_haskey(n->bm_other_verts, v_tri[i])) + { + BLI_gset_insert(n->bm_other_verts, v_tri[i]); + } + } + } + else { + printf("Ignoring boundary\n"); + } + } + } while ((a_step_base != eloop_a_len) || + (b_step_base != eloop_b_len)); +} + +static void pbvh_bmesh_collapse_close_verts(EdgeQueueContext *eq_ctx, + PBVH *bvh) +{ + int counter = 0; + BLI_buffer_declare_static(BMVert *, edge_verts_v1, BLI_BUFFER_NOP, 32); + BLI_buffer_declare_static(BMVert *, edge_verts_v2, BLI_BUFFER_NOP, 32); +#ifdef USE_BRIDGE_CLEAN + BLI_buffer_declare_static(BMFace *, face_clean, BLI_BUFFER_NOP, 32); +#endif + GSet *deleted_verts = BLI_gset_ptr_new_ex("deleted_verts", BLI_heap_size(eq_ctx->q->heap)); + + while (!BLI_heap_is_empty(eq_ctx->q->heap)) { + BMLoop *l; + BMVert **pair = BLI_heap_popmin(eq_ctx->q->heap); + BMVert *v1, *v2; + + v1 = pair[0]; + v2 = pair[1]; + + edge_verts_v1.count = 0; + edge_verts_v2.count = 0; + + counter++; + + /* check if an edge exists with those two vertices already. + * It is possible if an adjacent vertex pair is joined that + * the two vertices already share an edge. Joining the edge rings + * would then be impossible */ + if (BLI_gset_haskey(deleted_verts, v1) || + BLI_gset_haskey(deleted_verts, v2) || + BM_edge_exists(v1, v2)) + /* no need to check 'BM_vert_is_boundary', walking edges below will do. */ + { + continue; + } + + /* Regarding the check for '< 3' verts, this should NOT happen, + * but have a guard here to prevent crashing for now */ + if (!bm_vert_ordered_fan_walk(v1, &edge_verts_v1) || (edge_verts_v1.count < 3)) + continue; + if (!bm_vert_ordered_fan_walk(v2, &edge_verts_v2) || (edge_verts_v2.count < 3)) + continue; + +#ifdef USE_BRIDGE_STRICT + bm_earray_tag(edge_verts_v1.data, edge_verts_v1.count, false); + bm_earray_tag(edge_verts_v2.data, edge_verts_v2.count, true); + if (bm_earray_tag_check_any(edge_verts_v1.data, edge_verts_v1.count)) { + printf("Bridges share verts!\n"); + continue; + } +#endif + +#ifdef USE_BRIDGE_MERGE_HOLES + bm_earray_tag(edge_verts_v1.data, edge_verts_v1.count, true); + bm_earray_tag(edge_verts_v2.data, edge_verts_v2.count, true); + + /* ensure we don't remove radial faces here */ + BM_elem_flag_disable(v1, BM_ELEM_TAG); + BM_elem_flag_disable(v2, BM_ELEM_TAG); +#endif + + /* Remove the faces (would use 'BM_FACES_OF_VERT' except we can't look on data we remove) */ + while ((l = BM_vert_find_first_loop(v1))) { +#ifdef USE_BRIDGE_MERGE_HOLES + pbvh_bmesh_delete_edge_face_tagged_radial(bvh, l->next, eq_ctx); +#endif + BLI_assert(BM_ELEM_CD_GET_INT(l->f, eq_ctx->cd_face_node_offset) != DYNTOPO_NODE_NONE); + pbvh_bmesh_delete_edge_face(bvh, l->f); + } + while ((l = BM_vert_find_first_loop(v2))) { +#ifdef USE_BRIDGE_MERGE_HOLES + pbvh_bmesh_delete_edge_face_tagged_radial(bvh, l->next, eq_ctx); +#endif + BLI_assert(BM_ELEM_CD_GET_INT(l->f, eq_ctx->cd_face_node_offset) != DYNTOPO_NODE_NONE); + pbvh_bmesh_delete_edge_face(bvh, l->f); + } + + /* Note, maybe this should be done after deletion of the vertices? */ + if (edge_verts_v2.count < edge_verts_v1.count) { + pbvh_bridge_loops( + bvh, + edge_verts_v1.data, edge_verts_v1.count, + edge_verts_v2.data, edge_verts_v2.count); + } + else { + pbvh_bridge_loops( + bvh, + edge_verts_v2.data, edge_verts_v2.count, + edge_verts_v1.data, edge_verts_v1.count); + } + + if (BM_ELEM_CD_GET_INT(v1, eq_ctx->cd_vert_node_offset) != DYNTOPO_NODE_NONE) + pbvh_bmesh_vert_remove(bvh, v1); + if (BM_ELEM_CD_GET_INT(v2, eq_ctx->cd_vert_node_offset) != DYNTOPO_NODE_NONE) + pbvh_bmesh_vert_remove(bvh, v2); + + BM_log_vert_removed(bvh->bm_log, v1, eq_ctx->cd_vert_mask_offset); + BM_vert_kill(bvh->bm, v1); + BLI_gset_insert(deleted_verts, v1); + BM_log_vert_removed(bvh->bm_log, v2, eq_ctx->cd_vert_mask_offset); + BM_vert_kill(bvh->bm, v2); + BLI_gset_insert(deleted_verts, v2); + + +#ifdef USE_BRIDGE_CLEAN + { + BLI_Buffer *bufs[2] = {&edge_verts_v1, &edge_verts_v2}; + int i, j; + + + for (j = 0; j < 2; j++) { + BMVert **varr = BLI_buffer_array(bufs[j], BMVert *); + for (i = 0; i < bufs[j]->count; i++) { + BMVert *v = varr[i]; + + if (!BLI_gset_haskey(deleted_verts, v)) { + BMIter bm_iter; + BMLoop *l; + + face_clean.count = 0; + BM_ITER_ELEM (l, &bm_iter, v, BM_LOOPS_OF_VERT) { + if ((BM_edge_is_boundary(l->prev->e) || + BM_edge_is_boundary(l->e) || + BM_edge_is_boundary(l->next->e))) + { + BLI_buffer_append(&face_clean, BMFace *, l->f); + } + } + + if (face_clean.count != 0) { + BMFace **farr = BLI_buffer_array(&face_clean, BMFace *); + int k; + for (k = 0; k < face_clean.count; k++) { + pbvh_bmesh_delete_vert_face(bvh, NULL, farr[k], deleted_verts, eq_ctx); + printf("Cleaning bad face!\n"); + } + } + } + } + } + } +#endif + + } + BLI_buffer_free(&edge_verts_v1); + BLI_buffer_free(&edge_verts_v2); + +#ifdef USE_BRIDGE_CLEAN + BLI_buffer_free(&face_clean); +#endif + + BLI_gset_free(deleted_verts, NULL); +} + + bool BKE_pbvh_bmesh_node_raycast_detail( PBVHNode *node, const float ray_start[3], const float ray_normal[3], @@ -1272,6 +2074,33 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *bvh, PBVHTopologyUpdateMode mode, BLI_mempool_destroy(queue_pool); } + if (mode & PBVH_TopologyGenus) { + EdgeQueue q; + BLI_mempool *queue_pool = BLI_mempool_create(sizeof(BMVert *[2]), + 128, 128, 0); + EdgeQueueContext eq_ctx = {&q, queue_pool, bvh->bm, cd_vert_mask_offset, cd_vert_node_offset, cd_face_node_offset}; + + close_vert_queue_create(&eq_ctx, bvh, center, radius); + pbvh_bmesh_collapse_close_verts(&eq_ctx, bvh); + BLI_heap_free(q.heap, NULL); + BLI_mempool_destroy(queue_pool); + } + + /* remove low volume areas (test!) */ +#ifdef USE_HOLE_VOLUME_CLEAN + if (mode & PBVH_TopologyGenus) { + EdgeQueue q; + BLI_mempool *queue_pool = BLI_mempool_create(sizeof(BMEdge *), + 128, 128, 0); + EdgeQueueContext eq_ctx = {&q, queue_pool, bvh->bm, cd_vert_mask_offset, cd_vert_node_offset, cd_face_node_offset}; + + tetrahedron_edge_queue_create(&eq_ctx, bvh, center, radius); + pbvh_bmesh_collapse_small_tetrahedrons(&eq_ctx, bvh, &deleted_faces); + BLI_heap_free(q.heap, NULL); + BLI_mempool_destroy(queue_pool); + } +#endif + if (mode & PBVH_Subdivide) { EdgeQueue q; BLI_mempool *queue_pool = BLI_mempool_create(sizeof(BMVert *[2]), 0, 128, BLI_MEMPOOL_NOP); |