Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r--source/blender/blenkernel/BKE_pbvh.h6
-rw-r--r--source/blender/blenkernel/intern/pbvh_bmesh.c308
2 files changed, 270 insertions, 44 deletions
diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h
index 53e41c1dec8..3d12483dac0 100644
--- a/source/blender/blenkernel/BKE_pbvh.h
+++ b/source/blender/blenkernel/BKE_pbvh.h
@@ -140,9 +140,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 14a2ee0c3e7..e84ec9902de 100644
--- a/source/blender/blenkernel/intern/pbvh_bmesh.c
+++ b/source/blender/blenkernel/intern/pbvh_bmesh.c
@@ -29,6 +29,7 @@
#include "BLI_ghash.h"
#include "BLI_heap.h"
#include "BLI_math.h"
+#include "BLI_listbase.h"
#include "BKE_ccg.h"
#include "BKE_DerivedMesh.h"
@@ -684,6 +685,154 @@ static void short_edge_queue_create(EdgeQueueContext *eq_ctx,
}
}
+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], eq_ctx->cd_vert_node_offset, eq_ctx->cd_face_node_offset);
+ }
+ else {
+ v_tri[j] = NULL;
+ }
+ }
+
+ /* Remove the face */
+ pbvh_bmesh_face_remove(bvh, f_del, eq_ctx->cd_vert_node_offset, eq_ctx->cd_face_node_offset);
+ 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]);
+ }
+ }
+}
+
+
+/* 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;
+ BLI_heap_insert(eq_ctx->q->heap, dist_sq, pair);
+ }
+
+ /* 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])
@@ -915,48 +1064,7 @@ static void pbvh_bmesh_collapse_edge(PBVH *bvh, BMEdge *e,
/* 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], eq_ctx->cd_vert_node_offset, eq_ctx->cd_face_node_offset);
- }
- else {
- v_tri[j] = NULL;
- }
- }
-
- /* Remove the face */
- pbvh_bmesh_face_remove(bvh, f_del, eq_ctx->cd_vert_node_offset, eq_ctx->cd_face_node_offset);
- 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
@@ -1071,6 +1179,110 @@ bool pbvh_bmesh_node_raycast(PBVHNode *node, const float ray_start[3],
return hit;
}
+/* 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);
+ e_prev = l_first->prev->e;
+ if (l_iter == NULL)
+ return false;
+
+ do {
+ /* boundary, we're not interested! */
+ if (UNLIKELY(l_iter == NULL)) {
+ return false;
+ }
+ BLI_buffer_append(buf, BMVert *, BM_edge_other_vert(e_prev, v));
+ } while (((l_iter = BM_vert_step_fan_loop(l_iter, &e_prev)) != l_first));
+ return true;
+}
+
+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);
+
+ 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;
+ }
+
+ if (!bm_vert_ordered_fan_walk(v1, &edge_verts_v1))
+ continue;
+ if (!bm_vert_ordered_fan_walk(v2, &edge_verts_v2))
+ continue;
+
+ /* this should NOT happen, but have a guard here to prevent crashing for now */
+ if (edge_verts_v1.count < 3 || edge_verts_v2.count < 3) {
+ continue;
+ }
+
+ /* Note, maybe this should be done after deletion of the vertices? */
+#if 0
+ if (edge_verts_v2.count > edge_verts_v1.count) {
+ pbvh_bridge_loops(bvh, &edge_verts_v1, &edge_verts_v2, deleted_verts);
+ }
+ else {
+ pbvh_bridge_loops(bvh, &edge_verts_v2, &edge_verts_v1, deleted_verts);
+ }
+#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))) {
+ pbvh_bmesh_delete_vert_face(bvh, v1, l->f, deleted_verts, eq_ctx);
+ }
+ while ((l = BM_vert_find_first_loop(v2))) {
+ pbvh_bmesh_delete_vert_face(bvh, v2, l->f, deleted_verts, eq_ctx);
+ }
+
+ if (BM_ELEM_CD_GET_INT(v1, eq_ctx->cd_vert_node_offset) != DYNTOPO_NODE_NONE)
+ pbvh_bmesh_vert_remove(bvh, v1, eq_ctx->cd_vert_node_offset, eq_ctx->cd_face_node_offset);
+ if (BM_ELEM_CD_GET_INT(v2, eq_ctx->cd_vert_node_offset) != DYNTOPO_NODE_NONE)
+ pbvh_bmesh_vert_remove(bvh, v2, eq_ctx->cd_vert_node_offset, eq_ctx->cd_face_node_offset);
+
+ 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);
+ }
+ BLI_buffer_free(&edge_verts_v1);
+ BLI_buffer_free(&edge_verts_v2);
+
+ 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],
@@ -1236,6 +1448,18 @@ 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);
+ }
+
if (mode & PBVH_Subdivide) {
EdgeQueue q;
BLI_mempool *queue_pool = BLI_mempool_create(sizeof(BMVert *[2]), 0, 128, BLI_MEMPOOL_NOP);