* Fix bug with symmetrize creating non-manifold geometry.

* Fix bug in pbvh face create
This commit is contained in:
Joseph Eagar 2021-05-18 22:26:49 -07:00
parent daa4a33383
commit 71959181ad
6 changed files with 174 additions and 81 deletions

View File

@ -1060,7 +1060,7 @@ static void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v)
BM_FACES_OF_VERT_ITER_END;
}
static void pbvh_bmesh_face_remove(PBVH *pbvh, BMFace *f, bool log_face)
static void pbvh_bmesh_face_remove(PBVH *pbvh, BMFace *f, bool log_face, bool check_verts)
{
PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f);
@ -1072,32 +1072,30 @@ static void pbvh_bmesh_face_remove(PBVH *pbvh, BMFace *f, bool log_face)
/* Check if any of this face's vertices need to be removed
* from the node */
BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
BMLoop *l_iter = l_first;
do {
BMVert *v = l_iter->v;
if (pbvh_bmesh_node_vert_use_count_is_equal(pbvh, f_node, v, 1)) {
if (BLI_table_gset_haskey(f_node->bm_unique_verts, v)) {
/* Find a different node that uses 'v' */
PBVHNode *new_node;
if (check_verts) {
BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
BMLoop *l_iter = l_first;
do {
BMVert *v = l_iter->v;
if (pbvh_bmesh_node_vert_use_count_is_equal(pbvh, f_node, v, 1)) {
if (BLI_table_gset_haskey(f_node->bm_unique_verts, v)) {
/* Find a different node that uses 'v' */
PBVHNode *new_node;
new_node = pbvh_bmesh_vert_other_node_find(pbvh, v);
BLI_assert(new_node || BM_vert_face_count_is_equal(v, 1));
new_node = pbvh_bmesh_vert_other_node_find(pbvh, v);
BLI_assert(new_node || BM_vert_face_count_is_equal(v, 1));
if (new_node) {
pbvh_bmesh_vert_ownership_transfer(pbvh, new_node, v);
if (new_node) {
pbvh_bmesh_vert_ownership_transfer(pbvh, new_node, v);
}
}
else {
BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE);
BLI_table_gset_remove(f_node->bm_unique_verts, v, NULL);
/* Remove from other verts */
BLI_table_gset_remove(f_node->bm_other_verts, v, NULL);
}
}
else {
/* Remove from other verts */
BLI_table_gset_remove(f_node->bm_other_verts, v, NULL);
}
}
} while ((l_iter = l_iter->next) != l_first);
} while ((l_iter = l_iter->next) != l_first);
}
/* Remove face from node and top level */
BLI_table_gset_remove(f_node->bm_faces, f, NULL);
@ -1114,7 +1112,7 @@ static void pbvh_bmesh_face_remove(PBVH *pbvh, BMFace *f, bool log_face)
void BKE_pbvh_bmesh_remove_face(PBVH *pbvh, BMFace *f, bool log_face)
{
pbvh_bmesh_face_remove(pbvh, f, log_face);
pbvh_bmesh_face_remove(pbvh, f, log_face, true);
}
void BKE_pbvh_bmesh_remove_vertex(PBVH *pbvh, BMVert *v, bool log_vert)
@ -2660,7 +2658,7 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx,
&pbvh->bm->ldata, (const void **)lsrcs, lws, lws, 1, f_new->l_first->prev->head.data);
/* Delete original */
pbvh_bmesh_face_remove(pbvh, f_adj, true);
pbvh_bmesh_face_remove(pbvh, f_adj, true, true);
BM_face_kill(pbvh->bm, f_adj);
/* Ensure new vertex is in the node */
@ -2841,7 +2839,7 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
l = l->next;
} while (l != f_adj->l_first);
pbvh_bmesh_face_remove(pbvh, f_adj, true);
pbvh_bmesh_face_remove(pbvh, f_adj, true, true);
BM_face_kill(pbvh->bm, f_adj);
}
@ -3015,7 +3013,7 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
} while (l1 != f_del->l_first);
/* Remove the face */
pbvh_bmesh_face_remove(pbvh, f_del, true);
pbvh_bmesh_face_remove(pbvh, f_del, true, true);
BM_face_kill(pbvh->bm, f_del);
/* Check if any of the face's edges are now unused by any
@ -3939,8 +3937,6 @@ static bool cleanup_valence_3_4(PBVH *pbvh,
continue;
}
PBVHVertexIter vi;
GSetIterator gi;
BMVert *v;
TGSET_ITER (v, node->bm_unique_verts) {
@ -4022,7 +4018,7 @@ static bool cleanup_valence_3_4(PBVH *pbvh,
BLI_table_gset_remove(node2->bm_unique_verts, v, NULL);
BLI_table_gset_remove(node2->bm_other_verts, v, NULL);
pbvh_bmesh_face_remove(pbvh, f, true);
pbvh_bmesh_face_remove(pbvh, f, true, true);
}
}
@ -4037,6 +4033,8 @@ static bool cleanup_valence_3_4(PBVH *pbvh,
BMFace *f1 = NULL;
if (vs[0] != vs[1] && vs[1] != vs[2] && vs[0] != vs[2]) {
f1 = pbvh_bmesh_face_create(pbvh, n, vs, NULL, l->f, false, false);
normal_tri_v3(
f1->no, f1->l_first->v->co, f1->l_first->next->v->co, f1->l_first->prev->v->co);
}
if (val == 4 && vs[0] != vs[2] && vs[2] != vs[3] && vs[0] != vs[3]) {
@ -4052,6 +4050,8 @@ static bool cleanup_valence_3_4(PBVH *pbvh,
CustomData_bmesh_copy_data(
&pbvh->bm->ldata, &pbvh->bm->ldata, ls[2]->head.data, &f2->l_first->next->head.data);
normal_tri_v3(
f2->no, f2->l_first->v->co, f2->l_first->next->v->co, f2->l_first->prev->v->co);
BM_log_face_added(pbvh->bm_log, f2);
}

View File

@ -57,12 +57,6 @@
struct Mesh;
typedef struct BMLogEdge {
uint v1, v2;
void *data;
int flag;
} BMLogEdge;
struct BMLogEntry {
struct BMLogEntry *next, *prev;
@ -142,6 +136,7 @@ struct BMLog {
BMLogEntry *current_entry;
int cd_dyn_vert;
bool dead;
};
typedef struct {
@ -168,8 +163,8 @@ typedef struct {
static void full_copy_swap(BMesh *bm, BMLog *log, BMLogEntry *entry);
static void full_copy_load(BMesh *bm, BMLog *log, BMLogEntry *entry);
static void bm_log_entry_free(BMLogEntry *entry);
static bool bm_log_free_direct(BMLog *log, bool safe_mode);
static void *log_ghash_lookup(BMLog *log, GHash *gh, const void *key)
{
@ -608,10 +603,10 @@ static void bm_log_vert_values_swap(
}
}
ATTR_NO_OPT static void bm_log_face_values_swap(BMLog *log,
GHash *faces,
BMLogEntry *entry,
BMLogCallbacks *callbacks)
static void bm_log_face_values_swap(BMLog *log,
GHash *faces,
BMLogEntry *entry,
BMLogCallbacks *callbacks)
{
void *scratch = log->bm->pdata.pool ? BLI_mempool_alloc(log->bm->pdata.pool) : NULL;
@ -737,13 +732,18 @@ static BMLogEntry *bm_log_entry_create(void)
* Note: does not free the log entry itself */
static void bm_log_entry_free(BMLogEntry *entry)
{
if (entry->log) {
entry->log->refcount--;
BMLog *log = entry->log;
bool kill_log = false;
if (entry->log->refcount < 0) {
if (log) {
log->refcount--;
if (log->refcount < 0) {
fprintf(stderr, "BMLog refcount error\n");
entry->log->refcount = 0;
log->refcount = 0;
}
kill_log = !log->refcount;
}
if (entry->full_copy_mesh) {
@ -781,6 +781,10 @@ static void bm_log_entry_free(BMLogEntry *entry)
CustomData_free(&entry->edata, 0);
CustomData_free(&entry->ldata, 0);
CustomData_free(&entry->pdata, 0);
if (kill_log) {
bm_log_free_direct(log, true);
}
}
static void bm_log_id_ghash_retake(RangeTreeUInt *unused_ids, GHash *id_ghash)
@ -981,7 +985,7 @@ BMLog *BM_log_unfreeze(BMesh *bm, BMLogEntry *entry)
/* Free all the data in a BMLog including the log itself
* safe_mode means log->refcount will be checked, and if nonzero log will not be freed
*/
bool BM_log_free(BMLog *log, bool safe_mode)
static bool bm_log_free_direct(BMLog *log, bool safe_mode)
{
BMLogEntry *entry;
@ -998,6 +1002,8 @@ bool BM_log_free(BMLog *log, bool safe_mode)
return false;
}
log->dead = true;
BLI_rw_mutex_end(&log->lock);
if (log->unused_ids) {
@ -1018,11 +1024,24 @@ bool BM_log_free(BMLog *log, bool safe_mode)
entry->log = NULL;
}
MEM_freeN(log);
return true;
}
bool BM_log_free(BMLog *log, bool safe_mode)
{
if (log->dead) {
MEM_freeN(log);
return true;
}
if (bm_log_free_direct(log, safe_mode)) {
MEM_freeN(log);
return true;
}
return false;
}
/* Get the number of log entries */
int BM_log_length(const BMLog *log)
{
@ -1127,6 +1146,12 @@ BMLogEntry *BM_log_entry_add(BMesh *bm, BMLog *log)
BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_with_last)
{
if (log->dead) {
fprintf(stderr, "BMLog Error: log is dead\n");
fflush(stderr);
return NULL;
}
log->bm = bm;
/* WARNING: this is now handled by the UndoSystem: BKE_UNDOSYS_TYPE_SCULPT

View File

@ -28,6 +28,8 @@
#define ELE_OUT 1
#include "BLI_compiler_attrs.h"
void bmo_symmetrize_exec(BMesh *bm, BMOperator *op)
{
const float dist = BMO_slot_float_get(op->slots_in, "dist");
@ -96,6 +98,10 @@ void bmo_symmetrize_exec(BMesh *bm, BMOperator *op)
slot_targetmap = BMO_slot_get(op_weld.slots_in, "targetmap");
BMO_ITER (v, &siter, op_bisect.slots_out, "geom_cut.out", BM_VERT) {
if (!BM_vert_is_boundary(v)) {
continue;
}
BMVert *v_dupe = BMO_slot_map_elem_get(slot_vertmap, v);
BMO_slot_map_elem_insert(&op_weld, slot_targetmap, v_dupe, v);
}

View File

@ -872,7 +872,7 @@ int SCULPT_vertex_face_set_get(SculptSession *ss, SculptVertRef index)
}
}
return ret < 0 ? 0 : ret;
return ret;
}
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
@ -925,6 +925,54 @@ bool SCULPT_vertex_has_face_set(SculptSession *ss, SculptVertRef index, int face
return true;
}
/*
calcs visibility state based on face sets.
todo: also calc a face set boundary flag.
*/
void sculpt_vertex_faceset_update_bmesh(SculptSession *ss, SculptVertRef vert)
{
if (!ss->bm) {
return;
}
BMVert *v = (BMVert *)vert.i;
BMEdge *e = v->e;
bool ok = false;
const int cd_faceset_offset = ss->cd_faceset_offset;
if (!e) {
return;
}
do {
BMLoop *l = e->l;
if (l) {
do {
if (BM_ELEM_CD_GET_INT(l->f, cd_faceset_offset) > 0) {
ok = true;
break;
}
l = l->radial_next;
} while (l != e->l);
if (ok) {
break;
}
}
e = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next;
} while (e != v->e);
MDynTopoVert *mv = BM_ELEM_CD_GET_VOID_P(v, ss->cd_dyn_vert);
if (ok) {
mv->flag &= ~DYNVERT_VERT_FSET_HIDDEN;
}
else {
mv->flag |= DYNVERT_VERT_FSET_HIDDEN;
}
}
void SCULPT_visibility_sync_all_face_sets_to_vertices(Object *ob)
{
SculptSession *ss = ob->sculpt;
@ -956,6 +1004,8 @@ void SCULPT_visibility_sync_all_face_sets_to_vertices(Object *ob)
}
BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) {
MDynTopoVert *mv = BM_ELEM_CD_GET_VOID_P(v, ss->cd_dyn_vert);
BMIter iter2;
BMLoop *l;
@ -969,9 +1019,11 @@ void SCULPT_visibility_sync_all_face_sets_to_vertices(Object *ob)
}
if (!visible) {
mv->flag |= DYNVERT_VERT_FSET_HIDDEN;
BM_elem_flag_enable(v, BM_ELEM_HIDDEN);
}
else {
mv->flag &= ~DYNVERT_VERT_FSET_HIDDEN;
BM_elem_flag_disable(v, BM_ELEM_HIDDEN);
}
}
@ -1279,49 +1331,57 @@ static void sculpt_vertex_neighbors_get_bmesh(SculptSession *ss,
iter->neighbor_indices = iter->neighbor_indices_fixed;
iter->i = 0;
#if 1
// cache profiling revealed a hotspot here, don't use BM_ITER
BMEdge *e = v->e;
if (!v->e) {
return;
}
do {
if (v == e->v1) {
sculpt_vertex_neighbor_add_nocheck(
iter, BKE_pbvh_make_vref((intptr_t)e->v2), BM_elem_index_get(e->v2));
const bool have_facesets = ss->cd_faceset_offset >= 0;
const int cd_faceset_offset = ss->cd_faceset_offset;
e = e->v1_disk_link.next;
do {
BMVert *v2;
BMEdge *e2;
if (v == e->v1) {
v2 = e->v2;
e2 = e->v1_disk_link.next;
}
else {
sculpt_vertex_neighbor_add_nocheck(
iter, BKE_pbvh_make_vref((intptr_t)e->v1), BM_elem_index_get(e->v1));
v2 = e->v1;
e2 = e->v2_disk_link.next;
}
e = e->v2_disk_link.next;
// TODO: cache this in MDynTopoVert to avoid excessive DRAM fetches
BMLoop *l = e->l;
bool ok = false;
#if 0
if (l && have_facesets) {
do {
if (BM_ELEM_CD_GET_INT(l->f, cd_faceset_offset) > 0) {
ok = true;
break;
}
l = l->radial_next;
} while (l != e->l);
}
else {
ok = true;
}
#else
ok = true;
#endif
e = e2;
if (ok) {
sculpt_vertex_neighbor_add_nocheck(
iter, BKE_pbvh_make_vref((intptr_t)v2), BM_elem_index_get(v2));
}
} while (e != v->e);
#elif 0 // note that BM_EDGES_OF_VERT should be faster then BM_LOOPS_OF_VERT
BMEdge *e;
BM_ITER_ELEM (e, &liter, v, BM_EDGES_OF_VERT) {
BMVert *v_other = BM_edge_other_vert(e, v);
sculpt_vertex_neighbor_add(
iter, BKE_pbvh_make_vref((intptr_t)v_other), BM_elem_index_get(v_other));
}
#else
BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
const BMVert *adj_v[2] = {l->prev->v, l->next->v};
for (int i = 0; i < ARRAY_SIZE(adj_v); i++) {
const BMVert *v_other = adj_v[i];
if (v_other != (BMVert *)index.i) {
sculpt_vertex_neighbor_add(
iter, BKE_pbvh_make_vref((intptr_t)v_other), BM_elem_index_get(v_other));
}
}
}
#endif
}
static void sculpt_vertex_neighbors_get_faces(SculptSession *ss,

View File

@ -488,8 +488,10 @@ static void bmesh_undo_on_vert_change(BMVert *v, void *userdata, void *old_custo
BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, oldnode_i);
PBVHNode *node = BKE_pbvh_node_from_index(data->pbvh, oldnode_i);
BKE_pbvh_node_mark_update(node);
if (oldnode_i >= 0) {
PBVHNode *node = BKE_pbvh_node_from_index(data->pbvh, oldnode_i);
BKE_pbvh_node_mark_update(node);
}
}
static void bmesh_undo_on_face_change(BMFace *f, void *userdata, void *old_customdata)

View File

@ -543,7 +543,7 @@ typedef struct MDynTopoVert {
} MDynTopoVert;
/*MDynTopoVert->flag*/
enum { DYNVERT_BOUNDARY = (1 << 0) };
enum { DYNVERT_BOUNDARY = (1 << 0), DYNVERT_VERT_FSET_HIDDEN = (1 << 1) };
#ifdef __cplusplus
}