From de1f2c41fa8ce284ecd445803002fff944e2fd92 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 18 Aug 2021 21:43:59 -0700 Subject: [PATCH] * BM_mesh_remap can now reorder loops * Wrote yet another BKE_pbvh_reorder_bmesh function --- source/blender/blenkernel/intern/pbvh_bmesh.c | 399 +++++++++++++++++- source/blender/bmesh/intern/bmesh_log.c | 2 +- source/blender/bmesh/intern/bmesh_mesh.c | 93 +++- source/blender/bmesh/intern/bmesh_mesh.h | 6 +- source/blender/editors/mesh/editmesh_tools.c | 2 +- source/blender/python/bmesh/bmesh_py_types.c | 2 +- 6 files changed, 496 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c index cead721ffef..42d353eb0da 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.c +++ b/source/blender/blenkernel/intern/pbvh_bmesh.c @@ -48,8 +48,10 @@ Topology rake: #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_rand.h" +#include "BLI_sort_utils.h" #include "BLI_task.h" #include "BLI_utildefines.h" + #include "PIL_time.h" #include "atomic_ops.h" @@ -1339,6 +1341,87 @@ void BKE_pbvh_recalc_bmesh_boundary(PBVH *pbvh) } } +void BKE_pbvh_update_all_boundary_bmesh(PBVH *pbvh) +{ + // update boundary flags in a hopefully faster manner then v->e iteration + // XXX or not, it's slower + + BMIter iter; + BMEdge *e; + BMVert *v; + + const int cd_fset = CustomData_get_offset(&pbvh->bm->pdata, CD_SCULPT_FACE_SETS); + + BM_ITER_MESH (v, &iter, pbvh->bm, BM_VERTS_OF_MESH) { + MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v); + mv->flag &= ~(DYNVERT_BOUNDARY | DYNVERT_FSET_BOUNDARY | DYNVERT_NEED_BOUNDARY | + DYNVERT_VERT_FSET_HIDDEN); + + if (cd_fset >= 0 && v->e && v->e->l) { + mv->flag |= DYNVERT_VERT_FSET_HIDDEN; + } + } + + BM_ITER_MESH (e, &iter, pbvh->bm, BM_EDGES_OF_MESH) { + if (!e->l || e->l == e->l->radial_next) { + MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, e->v1); + MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, e->v2); + + mv1->flag |= DYNVERT_BOUNDARY; + mv2->flag |= DYNVERT_BOUNDARY; + } + + if (!e->l) { + continue; + } + + if (cd_fset < 0) { + MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, e->v1); + MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, e->v2); + + BMLoop *l = e->l; + + do { + if (l->f->len > 3) { + mv1->flag |= DYNVERT_NEED_TRIANGULATE; + mv2->flag |= DYNVERT_NEED_TRIANGULATE; + break; + } + + l = l->radial_next; + } while (l != e->l); + continue; + } + + MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, e->v1); + MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, e->v2); + + BMLoop *l = e->l; + int lastfset = BM_ELEM_CD_GET_INT(l->f, cd_fset); + do { + int fset = BM_ELEM_CD_GET_INT(l->f, cd_fset); + + if (l->f->len > 3) { + mv1->flag |= DYNVERT_NEED_TRIANGULATE; + mv2->flag |= DYNVERT_NEED_TRIANGULATE; + } + + if (fset > 0) { + MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->v); + mv->flag &= ~DYNVERT_VERT_FSET_HIDDEN; + } + + if (lastfset != fset) { + mv1->flag |= DYNVERT_FSET_BOUNDARY; + mv2->flag |= DYNVERT_FSET_BOUNDARY; + } + + lastfset = fset; + + l = l->radial_next; + } while (l != e->l); + } +} /* Build a PBVH from a BMesh */ void BKE_pbvh_build_bmesh(PBVH *pbvh, BMesh *bm, @@ -1373,6 +1456,8 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, BMIter iter; BMVert *v; + // BKE_pbvh_update_all_boundary_bmesh(pbvh); + int cd_vcol_offset = CustomData_get_offset(&bm->vdata, CD_PROP_COLOR); BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { @@ -2558,6 +2643,316 @@ static void scan_edge_split(BMesh *bm, BMEdge **edges, int totedge) BLI_array_free(fmap); } +#define MAX_RE_CHILD 3 +typedef struct ReVertNode { + int totvert, totchild; + struct ReVertNode *parent; + struct ReVertNode *children[MAX_RE_CHILD]; + BMVert *verts[]; +} ReVertNode; + +ATTR_NO_OPT BMesh *BKE_pbvh_reorder_bmesh(PBVH *pbvh) +{ + /*try to compute size of verts per node*/ + int vsize = sizeof(BMVert); + vsize += pbvh->bm->vdata.totsize; + + // perhaps aim for l2 cache? + const int limit = 1024; + int leaf_limit = MAX2(limit / vsize, 4); + + const int cd_vcol = CustomData_get_offset(&pbvh->bm->vdata, CD_PROP_COLOR); + + BLI_mempool *pool = BLI_mempool_create(sizeof(ReVertNode) + sizeof(void *) * vsize, 0, 8192, 0); + ReVertNode **vnodemap = MEM_calloc_arrayN(pbvh->bm->totvert, sizeof(void *), "vnodemap"); + + printf("leaf_limit: %d\n", leaf_limit); + + BMIter iter; + BMVert *v; + const char flag = BM_ELEM_TAG_ALT; + int i = 0; + + BM_ITER_MESH (v, &iter, pbvh->bm, BM_VERTS_OF_MESH) { + BM_elem_flag_disable(v, flag); + v->head.index = i++; + } + + BMVert **stack = NULL; + BLI_array_declare(stack); + + BM_ITER_MESH (v, &iter, pbvh->bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, flag)) { + continue; + } + + ReVertNode *node = BLI_mempool_calloc(pool); + + BLI_array_clear(stack); + BLI_array_append(stack, v); + + BM_elem_flag_enable(v, flag); + + vnodemap[v->head.index] = node; + node->verts[node->totvert++] = v; + + while (BLI_array_len(stack) > 0) { + BMVert *v2 = BLI_array_pop(stack); + BMEdge *e; + + if (node->totvert >= leaf_limit) { + break; + } + + if (!v2->e) { + continue; + } + + int len = node->totvert; + + e = v2->e; + do { + BMVert *v3 = BM_edge_other_vert(e, v2); + + if (!BM_elem_flag_test(v3, flag) && len < leaf_limit) { + BM_elem_flag_enable(v3, flag); + vnodemap[v3->head.index] = node; + node->verts[node->totvert++] = v3; + + len++; + + BLI_array_append(stack, v3); + } + + e = e->v1 == v2 ? e->v1_disk_link.next : e->v2_disk_link.next; + } while (e != v2->e); + } + } + + const int steps = 4; + ReVertNode **roots = NULL; + BLI_array_declare(roots); + + for (int step = 0; step < steps; step++) { + const bool last_step = step == steps - 1; + + BM_ITER_MESH_INDEX (v, &iter, pbvh->bm, BM_VERTS_OF_MESH, i) { + BMEdge *e = v->e; + + if (!e) { + continue; + } + + ReVertNode *node = vnodemap[v->head.index]; + if (node->parent) { + continue; + } + + ReVertNode *other_node = NULL; + + ReVertNode *parent = BLI_mempool_calloc(pool); + parent->children[0] = node; + parent->totchild = 1; + + do { + BMVert *v2 = BM_edge_other_vert(e, v); + + ReVertNode *node2 = vnodemap[v2->head.index]; + + bool ok = node != node2 && !node2->parent; + for (int j = 0; j < parent->totchild; j++) { + if (parent->children[j] == node2) { + ok = false; + break; + } + } + + if (ok) { + parent->children[parent->totchild++] = node2; + break; + } + + e = e->v1 == v ? e->v1_disk_link.next : e->v2_disk_link.next; + } while (e != v->e); + + if (parent->totchild == 1) { + BLI_mempool_free(pool, parent); + continue; + } + + if (last_step) { + BLI_array_append(roots, parent); + } + + for (int j = 0; j < parent->totchild; j++) { + parent->children[j]->parent = parent; + } + } + + BM_ITER_MESH_INDEX (v, &iter, pbvh->bm, BM_VERTS_OF_MESH, i) { + while (vnodemap[i]->parent) { + vnodemap[i] = vnodemap[i]->parent; + } + } + } + + BLI_mempool_iter loopiter; + BLI_mempool_iternew(pbvh->bm->lpool, &loopiter); + BMLoop *l = BLI_mempool_iterstep(&loopiter); + BMEdge *e; + BMFace *f; + + for (i = 0; l; l = BLI_mempool_iterstep(&loopiter), i++) { + l->head.hflag &= ~flag; + } + BM_ITER_MESH (e, &iter, pbvh->bm, BM_EDGES_OF_MESH) { + e->head.hflag &= ~flag; + } + + BM_ITER_MESH (f, &iter, pbvh->bm, BM_FACES_OF_MESH) { + f->head.hflag &= ~flag; + } + + int totroot = BLI_array_len(roots); + ReVertNode **nstack = NULL; + BLI_array_declare(nstack); + int vorder = 0, eorder = 0, lorder = 0, forder = 0; + + for (i = 0; i < totroot; i++) { + BLI_array_clear(nstack); + + ReVertNode *node = roots[i]; + BLI_array_append(nstack, node); + + while (BLI_array_len(nstack) > 0) { + ReVertNode *node2 = BLI_array_pop(nstack); + + if (node2->totchild == 0) { + for (int j = 0; j < node2->totvert; j++) { + v = node2->verts[j]; + +#if 0 + if (cd_vcol >= 0) { + MPropCol *col = BM_ELEM_CD_GET_VOID_P(node2->verts[j], cd_vcol); + + float r = 0.0f, g = 0.0f, b = 0.0f; + + unsigned int p = (unsigned int)node2->parent; + p = p % 65535; + + unsigned int p2 = (unsigned int)node2->parent; + p2 = p2 % 65535; + + r = ((float)vorder) * 0.01; + g = ((float)p2) / 65535.0f; + b = ((float)p) / 65535.0f; + + r = cosf(r * 17.2343) * 0.5 + 0.5; + g = cosf(g * 11.2343) * 0.5 + 0.5; + b = cosf(b * 19.2343) * 0.5 + 0.5; + + col->color[0] = r; + col->color[1] = g; + col->color[2] = b; + col->color[3] = 1.0f; + } +#endif + v->head.index = vorder++; + + BMEdge *e = v->e; + if (!e) { + continue; + } + + do { + if (!(e->head.hflag & flag)) { + e->head.hflag |= flag; + e->head.index = eorder++; + } + + if (e->l) { + BMLoop *l = e->l; + + do { + if (!(l->head.hflag & flag)) { + l->head.hflag |= flag; + l->head.index = lorder++; + } + + if (!(l->f->head.hflag & flag)) { + l->f->head.hflag |= flag; + l->f->head.index = forder++; + } + + l = l->radial_next; + } while (l != e->l); + } + e = e->v1 == v ? e->v1_disk_link.next : e->v2_disk_link.next; + } while (e != v->e); + } + } + else { + for (int j = 0; j < node2->totchild; j++) { + BLI_array_append(nstack, node2->children[j]); + } + } + } + } + + uint *vidx, *eidx, *lidx, *fidx; + + vidx = MEM_malloc_arrayN(pbvh->bm->totvert, sizeof(*vidx), "vorder"); + eidx = MEM_malloc_arrayN(pbvh->bm->totedge, sizeof(*eidx), "eorder"); + lidx = MEM_malloc_arrayN(pbvh->bm->totloop, sizeof(*lidx), "lorder"); + fidx = MEM_malloc_arrayN(pbvh->bm->totface, sizeof(*fidx), "forder"); + + printf("v %d %d\n", vorder, pbvh->bm->totvert); + printf("e %d %d\n", eorder, pbvh->bm->totedge); + printf("l %d %d\n", lorder, pbvh->bm->totloop); + printf("f %d %d\n", forder, pbvh->bm->totface); + + BM_ITER_MESH_INDEX (v, &iter, pbvh->bm, BM_VERTS_OF_MESH, i) { + vidx[i] = (uint)v->head.index; + } + + BM_ITER_MESH_INDEX (e, &iter, pbvh->bm, BM_EDGES_OF_MESH, i) { + eidx[i] = (uint)e->head.index; + } + BM_ITER_MESH_INDEX (f, &iter, pbvh->bm, BM_FACES_OF_MESH, i) { + fidx[i] = (uint)f->head.index; + } + + BLI_mempool_iternew(pbvh->bm->lpool, &loopiter); + l = BLI_mempool_iterstep(&loopiter); + + for (i = 0; l; l = BLI_mempool_iterstep(&loopiter), i++) { + // handle orphaned loops + if (!(l->head.hflag & flag)) { + printf("warning in %s: orphaned loop!\n", __func__); + l->head.index = lorder++; + } + + lidx[i] = (uint)l->head.index; + } + + BM_mesh_remap(pbvh->bm, vidx, eidx, fidx, lidx); + + printf("roots: %d\n", BLI_array_len(roots)); + + MEM_SAFE_FREE(vidx); + MEM_SAFE_FREE(eidx); + MEM_SAFE_FREE(lidx); + MEM_SAFE_FREE(fidx); + + MEM_SAFE_FREE(nstack); + MEM_SAFE_FREE(roots); + BLI_mempool_destroy(pool); + MEM_SAFE_FREE(stack); + MEM_SAFE_FREE(vnodemap); + + return pbvh->bm; +} + BMesh *BKE_pbvh_reorder_bmesh2(PBVH *pbvh) { if (BKE_pbvh_type(pbvh) != PBVH_BMESH || pbvh->totnode == 0) { @@ -2826,7 +3221,7 @@ static int sort_edges(const void *va, const void *vb) return (ni1 + ni2) - (ni3 + ni4); } -BMesh *BKE_pbvh_reorder_bmesh(PBVH *pbvh) +BMesh *BKE_pbvh_reorder_bmesh1(PBVH *pbvh) { BMesh *bm = pbvh->bm; @@ -2920,7 +3315,7 @@ BMesh *BKE_pbvh_reorder_bmesh(PBVH *pbvh) faces[i].elem->head.index = faces[i].index; } - BM_mesh_remap(bm, vs, es, fs); + BM_mesh_remap(bm, vs, es, fs, NULL); // create new mappings BMVert **mapvs = MEM_malloc_arrayN(bm->totvert, sizeof(BMVert *), __func__); diff --git a/source/blender/bmesh/intern/bmesh_log.c b/source/blender/bmesh/intern/bmesh_log.c index 83964abfd3a..435e555d06e 100644 --- a/source/blender/bmesh/intern/bmesh_log.c +++ b/source/blender/bmesh/intern/bmesh_log.c @@ -1200,7 +1200,7 @@ void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log) } BLI_ghash_free(id_to_idx, NULL, NULL); - BM_mesh_remap(bm, varr, NULL, farr); + BM_mesh_remap(bm, varr, NULL, farr, NULL); MEM_freeN(varr); MEM_freeN(farr); diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c index 06ff1b1df55..cc4112783cf 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.c +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -72,7 +72,7 @@ static void bm_mempool_init_ex(const BMAllocTemplate *allocsize, } if (r_lpool) { *r_lpool = BLI_mempool_create( - loop_size, allocsize->totloop, bm_mesh_chunksize_default.totloop, BLI_MEMPOOL_NOP); + loop_size, allocsize->totloop, bm_mesh_chunksize_default.totloop, BLI_MEMPOOL_ALLOW_ITER); } if (r_fpool) { *r_fpool = BLI_mempool_create( @@ -875,7 +875,11 @@ int BM_mesh_elem_count(BMesh *bm, const char htype) * \warning Be careful if you keep pointers to affected BM elements, * or arrays, when using this func! */ -void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const uint *face_idx) +void BM_mesh_remap(BMesh *bm, + const uint *vert_idx, + const uint *edge_idx, + const uint *face_idx, + const uint *loop_idx) { /* Mapping old to new pointers. */ GHash *vptr_map = NULL, *eptr_map = NULL, *fptr_map = NULL; @@ -944,6 +948,70 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const } } + GHash *lptr_map = NULL; + + /* Remap Loops */ + if (loop_idx) { + BMLoop **ltable = MEM_malloc_arrayN(bm->totloop, sizeof(*ltable), "ltable"); + + BMLoop *ed; + BLI_mempool_iter liter; + BLI_mempool_iternew(bm->lpool, &liter); + BMLoop *l = (BMLoop *)BLI_mempool_iterstep(&liter); + + int i = 0; + for (; l; l = (BMLoop *)BLI_mempool_iterstep(&liter), i++) { + l->head.index = i; + ltable[i] = l; + } + + BMLoop **loops_pool, *loops_copy, **edl; + int totloop = bm->totloop; + const uint *new_idx; + /* Special case: Python uses custom data layers to hold PyObject references. + * These have to be kept in place, else the PyObjects we point to, won't point back to us. */ + const int cd_loop_pyptr = CustomData_get_offset(&bm->ldata, CD_BM_ELEM_PYPTR); + + /* Init the old-to-new vert pointers mapping */ + lptr_map = BLI_ghash_ptr_new_ex("BM_mesh_remap loop pointers mapping", bm->totloop); + + /* Make a copy of all vertices. */ + loops_pool = ltable; + loops_copy = MEM_mallocN(sizeof(BMLoop) * totloop, "BM_mesh_remap loops copy"); + + void **pyptrs = (cd_loop_pyptr != -1) ? MEM_mallocN(sizeof(void *) * totloop, __func__) : NULL; + for (i = totloop, ed = loops_copy + totloop - 1, edl = loops_pool + totloop - 1; i--; + ed--, edl--) { + *ed = **edl; + if (cd_loop_pyptr != -1) { + void **pyptr = BM_ELEM_CD_GET_VOID_P(((BMElem *)ed), cd_loop_pyptr); + pyptrs[i] = *pyptr; + } + } + + /* Copy back verts to their new place, and update old2new pointers mapping. */ + new_idx = loop_idx + totloop - 1; + ed = loops_copy + totloop - 1; + edl = loops_pool + totloop - 1; /* old, org pointer */ + for (i = totloop; i--; new_idx--, ed--, edl--) { + BMLoop *new_edl = loops_pool[*new_idx]; + *new_edl = *ed; + BLI_ghash_insert(lptr_map, *edl, new_edl); +#if 0 + printf( + "mapping loop from %d to %d (%p/%p to %p)\n", i, *new_idx, *edl, loops_pool[i], new_edl); +#endif + if (cd_loop_pyptr != -1) { + void **pyptr = BM_ELEM_CD_GET_VOID_P(((BMElem *)new_edl), cd_loop_pyptr); + *pyptr = pyptrs[*new_idx]; + } + } + + MEM_SAFE_FREE(ltable); + MEM_SAFE_FREE(loops_copy); + MEM_SAFE_FREE(pyptrs); + } + /* Remap Edges */ if (edge_idx) { BMEdge **edges_pool, *edges_copy, **edp; @@ -976,6 +1044,11 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const for (i = totedge; i--; new_idx--, ed--, edp--) { BMEdge *new_edp = edges_pool[*new_idx]; *new_edp = *ed; + + if (new_edp->l && lptr_map) { + new_edp->l = BLI_ghash_lookup(lptr_map, (BMLoop *)new_edp->l); + } + BLI_ghash_insert(eptr_map, *edp, new_edp); #if 0 printf( @@ -1028,6 +1101,22 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const BMFace *new_fap = faces_pool[*new_idx]; *new_fap = *fa; BLI_ghash_insert(fptr_map, *fap, new_fap); + + if (lptr_map) { + new_fap->l_first = BLI_ghash_lookup(lptr_map, (void *)new_fap->l_first); + + BMLoop *l = new_fap->l_first; + + do { + l->next = BLI_ghash_lookup(lptr_map, (void *)l->next); + l->prev = BLI_ghash_lookup(lptr_map, (void *)l->prev); + l->radial_next = BLI_ghash_lookup(lptr_map, (void *)l->radial_next); + l->radial_prev = BLI_ghash_lookup(lptr_map, (void *)l->radial_prev); + + l = l->next; + } while (l != new_fap->l_first); + } + if (cd_poly_pyptr != -1) { void **pyptr = BM_ELEM_CD_GET_VOID_P(((BMElem *)new_fap), cd_poly_pyptr); *pyptr = pyptrs[*new_idx]; diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index d9176a7f5fb..7fdb860c163 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -99,7 +99,11 @@ BMFace *BM_face_at_index_find_or_table(BMesh *bm, const int index); int BM_mesh_elem_count(BMesh *bm, const char htype); -void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const uint *face_idx); +void BM_mesh_remap(BMesh *bm, + const uint *vert_idx, + const uint *edge_idx, + const uint *face_idx, + const uint *loop_idx); void BM_mesh_rebuild(BMesh *bm, const struct BMeshCreateParams *params, diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index a3faebb869f..073eb576ecd 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -7105,7 +7105,7 @@ static void sort_bmelem_flag(bContext *C, } } - BM_mesh_remap(em->bm, map[0], map[1], map[2]); + BM_mesh_remap(em->bm, map[0], map[1], map[2], NULL); DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index 581ec92a8bd..3e94f184781 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -2764,7 +2764,7 @@ static PyObject *bpy_bmelemseq_sort(BPy_BMElemSeq *self, PyObject *args, PyObjec return NULL; } - BM_mesh_remap(bm, vert_idx, edge_idx, face_idx); + BM_mesh_remap(bm, vert_idx, edge_idx, face_idx, NULL); PyMem_FREE(elem_map_idx); PyMem_FREE(elem_idx);