sculpt-dev: Cleanup dyntopo.cc

* Split into dyntopo.cc, dyntopo_collapse.cc and dyntopo_intern.hh
* Removed various unused functions.
This commit is contained in:
Joseph Eagar 2023-01-27 17:27:44 -08:00
parent 390b6492e7
commit 4e76cc86b0
6 changed files with 1746 additions and 2001 deletions

View File

@ -132,6 +132,7 @@ set(SRC
intern/displist.cc
intern/dynamicpaint.c
intern/dyntopo.cc
intern/dyntopo_collapse.cc
intern/editlattice.c
intern/editmesh.cc
intern/editmesh_bvh.c
@ -514,6 +515,7 @@ set(SRC
intern/CCGSubSurf.h
intern/CCGSubSurf_inline.h
intern/CCGSubSurf_intern.h
intern/dyntopo_intern.hh
intern/attribute_access_intern.hh
intern/data_transfer_intern.h
intern/lib_intern.h

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,904 @@
#include "MEM_guardedalloc.h"
#include "DNA_customdata_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "BLI_array.hh"
#include "BLI_asan.h"
#include "BLI_buffer.h"
#include "BLI_index_range.hh"
#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_task.hh"
#include "BLI_vector.hh"
#include "BLI_alloca.h"
#include "BLI_compiler_attrs.h"
#include "BLI_compiler_compat.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "PIL_time.h"
#include "atomic_ops.h"
#include "BKE_customdata.h"
#include "BKE_dyntopo.h"
#include "BKE_paint.h"
#include "BKE_pbvh.h"
#include "bmesh.h"
#include "bmesh_log.h"
#include "dyntopo_intern.hh"
#include "pbvh_intern.h"
#include <stdio.h>
using blender::float2;
using blender::float3;
using blender::float4;
using blender::IndexRange;
using blender::Map;
using blender::Set;
using blender::Vector;
namespace blender::dyntopo {
// copied from decimate modifier code
inline bool bm_edge_collapse_is_degenerate_topology(BMEdge *e_first)
{
/* simply check that there is no overlap between faces and edges of each vert,
* (excluding the 2 faces attached to 'e' and 'e' its self) */
BMEdge *e_iter;
/* clear flags on both disks */
e_iter = e_first;
do {
if (!bm_edge_is_manifold_or_boundary(e_iter->l)) {
return true;
}
bm_edge_tag_disable(e_iter);
} while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, e_first->v1)) != e_first);
e_iter = e_first;
do {
if (!bm_edge_is_manifold_or_boundary(e_iter->l)) {
return true;
}
bm_edge_tag_disable(e_iter);
} while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, e_first->v2)) != e_first);
/* now enable one side... */
e_iter = e_first;
do {
bm_edge_tag_enable(e_iter);
} while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, e_first->v1)) != e_first);
/* ... except for the edge we will collapse, we know that's shared,
* disable this to avoid false positive. We could be smart and never enable these
* face/edge tags in the first place but easier to do this */
// bm_edge_tag_disable(e_first);
/* do inline... */
{
#if 0
BMIter iter;
BMIter liter;
BMLoop *l;
BMVert *v;
BM_ITER_ELEM (l, &liter, e_first, BM_LOOPS_OF_EDGE) {
BM_elem_flag_disable(l->f, BM_ELEM_TAG);
BM_ITER_ELEM (v, &iter, l->f, BM_VERTS_OF_FACE) {
BM_elem_flag_disable(v, BM_ELEM_TAG);
}
}
#else
/* we know each face is a triangle, no looping/iterators needed here */
BMLoop *l_radial;
BMLoop *l_face;
l_radial = e_first->l;
l_face = l_radial;
BLI_assert(l_face->f->len == 3);
BM_elem_flag_disable(l_face->f, BM_ELEM_TAG);
BM_elem_flag_disable((l_face = l_radial)->v, BM_ELEM_TAG);
BM_elem_flag_disable((l_face = l_face->next)->v, BM_ELEM_TAG);
BM_elem_flag_disable((l_face->next)->v, BM_ELEM_TAG);
l_face = l_radial->radial_next;
if (l_radial != l_face) {
BLI_assert(l_face->f->len == 3);
BM_elem_flag_disable(l_face->f, BM_ELEM_TAG);
BM_elem_flag_disable((l_face = l_radial->radial_next)->v, BM_ELEM_TAG);
BM_elem_flag_disable((l_face = l_face->next)->v, BM_ELEM_TAG);
BM_elem_flag_disable((l_face->next)->v, BM_ELEM_TAG);
}
#endif
}
/* and check for overlap */
e_iter = e_first;
do {
if (bm_edge_tag_test(e_iter)) {
return true;
}
} while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, e_first->v2)) != e_first);
return false;
}
typedef struct TraceData {
PBVH *pbvh;
SmallHash visit;
blender::Set<void *> visit2;
BMEdge *e;
} TraceData;
ATTR_NO_OPT void col_on_vert_kill(BMesh *bm, BMVert *v, void *userdata)
{
TraceData *data = (TraceData *)userdata;
PBVH *pbvh = data->pbvh;
if (BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset) != DYNTOPO_NODE_NONE) {
// printf("vert pbvh remove!\n");
blender::dyntopo::pbvh_bmesh_vert_remove(pbvh, v);
}
if (!BLI_smallhash_haskey(&data->visit, (uintptr_t)v)) {
// printf("vert kill!\n");
BM_log_vert_pre(pbvh->bm_log, v);
BLI_smallhash_insert(&data->visit, (uintptr_t)v, nullptr);
#ifdef USE_NEW_IDMAP
BM_idmap_release(pbvh->bm_idmap, reinterpret_cast<BMElem *>(v), true);
#endif
}
}
ATTR_NO_OPT void col_on_edge_kill(BMesh *bm, BMEdge *e, void *userdata)
{
TraceData *data = (TraceData *)userdata;
PBVH *pbvh = data->pbvh;
if (!BLI_smallhash_haskey(&data->visit, (uintptr_t)e)) {
// printf("edge kill!\n");
BM_log_edge_pre(pbvh->bm_log, e);
BLI_smallhash_insert(&data->visit, (uintptr_t)e, nullptr);
#ifdef USE_NEW_IDMAP
BM_idmap_release(pbvh->bm_idmap, reinterpret_cast<BMElem *>(e), true);
#endif
}
}
ATTR_NO_OPT void col_on_face_kill(BMesh *bm, BMFace *f, void *userdata)
{
TraceData *data = (TraceData *)userdata;
PBVH *pbvh = data->pbvh;
if (BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset) != DYNTOPO_NODE_NONE) {
pbvh_bmesh_face_remove(pbvh, f, false, false, false);
}
if (!BLI_smallhash_haskey(&data->visit, (uintptr_t)f)) {
BM_log_face_pre(pbvh->bm_log, f);
BLI_smallhash_insert(&data->visit, (uintptr_t)f, nullptr);
#ifdef USE_NEW_IDMAP
BM_idmap_release(pbvh->bm_idmap, reinterpret_cast<BMElem *>(f), true);
#endif
}
}
ATTR_NO_OPT static void collapse_restore_id(BMIdMap *idmap, BMElem *elem)
{
int id = BM_idmap_get_id(idmap, elem);
if (id < 0 || id >= idmap->map_size || idmap->map[id]) {
BM_idmap_alloc(idmap, elem);
}
else {
BM_idmap_assign(idmap, elem, id);
}
}
ATTR_NO_OPT void col_on_vert_add(BMesh *bm, BMVert *v, void *userdata)
{
TraceData *data = (TraceData *)userdata;
PBVH *pbvh = data->pbvh;
if (!data->visit2.add(static_cast<void *>(v))) {
// return;
}
pbvh_boundary_update_bmesh(pbvh, v);
MSculptVert *mv = (MSculptVert *)BM_ELEM_CD_GET_VOID_P(v, data->pbvh->cd_sculpt_vert);
mv->flag |= SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT;
collapse_restore_id(pbvh->bm_idmap, (BMElem *)v);
BM_log_vert_post(pbvh->bm_log, v);
}
ATTR_NO_OPT void col_on_edge_add(BMesh *bm, BMEdge *e, void *userdata)
{
TraceData *data = (TraceData *)userdata;
PBVH *pbvh = data->pbvh;
if (!data->visit2.add(static_cast<void *>(e))) {
// return;
}
collapse_restore_id(pbvh->bm_idmap, (BMElem *)e);
BM_log_edge_post(pbvh->bm_log, e);
}
ATTR_NO_OPT void col_on_face_add(BMesh *bm, BMFace *f, void *userdata)
{
TraceData *data = (TraceData *)userdata;
PBVH *pbvh = data->pbvh;
if (!data->visit2.add(static_cast<void *>(f))) {
// return;
}
if (bm_elem_is_free((BMElem *)f, BM_FACE)) {
printf("%s: error, f was freed!\n", __func__);
return;
}
if (BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset) != DYNTOPO_NODE_NONE) {
pbvh_bmesh_face_remove(pbvh, f, false, false, false);
}
collapse_restore_id(pbvh->bm_idmap, (BMElem *)f);
BM_log_face_post(pbvh->bm_log, f);
BKE_pbvh_bmesh_add_face(pbvh, f, false, false);
}
/* Faces *outside* the ring region are tagged with facetag, used to detect
* border edges.
*/
ATTR_NO_OPT static void vert_ring_do_tag(BMVert *v, int tag, int facetag, int depth)
{
BMEdge *e = v->e;
do {
BMVert *v2 = BM_edge_other_vert(e, v);
if (depth > 0) {
vert_ring_do_tag(v2, tag, facetag, depth - 1);
}
e->head.hflag |= tag;
v2->head.hflag |= tag;
if (!e->l) {
continue;
}
BMLoop *l = e->l;
do {
l->f->head.hflag |= tag;
BMLoop *l2 = l;
do {
l2->v->head.hflag |= tag;
l2->e->head.hflag |= tag;
l2->f->head.hflag |= tag;
/*set up face tags for faces outside this region*/
BMLoop *l3 = l2->radial_next;
do {
l3->f->head.hflag |= facetag;
} while ((l3 = l3->radial_next) != l2);
} while ((l2 = l2->next) != l);
} while ((l = l->radial_next) != e->l);
} while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e);
}
ATTR_NO_OPT static void vert_ring_untag_inner_faces(BMVert *v, int tag, int facetag, int depth)
{
if (!v->e) {
return;
}
BMEdge *e = v->e;
/* untag faces inside this region with facetag */
do {
BMLoop *l = e->l;
if (depth > 0) {
BMVert *v2 = BM_edge_other_vert(e, v);
vert_ring_untag_inner_faces(v2, tag, facetag, depth - 1);
}
if (!l) {
continue;
}
do {
l->f->head.hflag &= ~facetag;
} while ((l = l->radial_next) != e->l);
} while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e);
}
ATTR_NO_OPT void vert_ring_do_apply(BMVert *v,
void (*callback)(BMElem *elem, void *userdata),
void *userdata,
int tag,
int facetag,
int depth)
{
BMEdge *e = v->e;
callback((BMElem *)v, userdata);
v->head.hflag &= ~tag;
e = v->e;
do {
BMVert *v2 = BM_edge_other_vert(e, v);
if (depth > 0) {
vert_ring_do_apply(v2, callback, userdata, tag, facetag, depth - 1);
}
if (v2->head.hflag & tag) {
v2->head.hflag &= ~tag;
callback((BMElem *)v2, userdata);
}
if (e->head.hflag & tag) {
e->head.hflag &= ~tag;
callback((BMElem *)e, userdata);
}
if (!e->l) {
continue;
}
BMLoop *l = e->l;
do {
BMLoop *l2 = l;
do {
if (l2->v->head.hflag & tag) {
l2->v->head.hflag &= ~tag;
callback((BMElem *)l2->v, userdata);
}
if (l2->e->head.hflag & tag) {
l2->e->head.hflag &= ~tag;
callback((BMElem *)l2->e, userdata);
}
if (l2->f->head.hflag & tag) {
l2->f->head.hflag &= ~tag;
callback((BMElem *)l2->f, userdata);
}
} while ((l2 = l2->next) != l);
} while ((l = l->radial_next) != e->l);
} while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e);
}
const int COLLAPSE_TAG = BM_ELEM_INTERNAL_TAG;
const int COLLAPSE_FACE_TAG = BM_ELEM_TAG_ALT;
ATTR_NO_OPT static void vert_ring_do(BMVert *v,
void (*callback)(BMElem *elem, void *userdata),
void *userdata,
int tag,
int facetag,
int depth)
{
if (!v->e) {
v->head.hflag &= ~tag;
callback((BMElem *)v, userdata);
return;
}
vert_ring_do_tag(v, tag, facetag, depth);
vert_ring_untag_inner_faces(v, tag, facetag, depth);
vert_ring_do_apply(v, callback, userdata, tag, facetag, depth);
}
static void edge_ring_do(BMEdge *e,
void (*callback)(BMElem *elem, void *userdata),
void *userdata,
int tag,
int facetag,
int depth)
{
vert_ring_do_tag(e->v1, tag, facetag, depth);
vert_ring_do_tag(e->v2, tag, facetag, depth);
vert_ring_untag_inner_faces(e->v1, tag, facetag, depth);
vert_ring_untag_inner_faces(e->v2, tag, facetag, depth);
vert_ring_do_apply(e->v1, callback, userdata, tag, facetag, depth);
vert_ring_do_apply(e->v2, callback, userdata, tag, facetag, depth);
return;
for (int i = 0; i < 2; i++) {
BMVert *v2 = i ? e->v2 : e->v1;
BMEdge *e2 = v2->e;
do {
e2->head.hflag |= tag;
e2->v1->head.hflag |= tag;
e2->v2->head.hflag |= tag;
if (!e2->l) {
continue;
}
BMLoop *l = e2->l;
do {
BMLoop *l2 = l;
do {
l2->v->head.hflag |= tag;
l2->e->head.hflag |= tag;
l2->f->head.hflag |= tag;
} while ((l2 = l2->next) != l);
} while ((l = l->radial_next) != e2->l);
} while ((e2 = BM_DISK_EDGE_NEXT(e2, v2)) != v2->e);
}
for (int i = 0; i < 2; i++) {
BMVert *v2 = i ? e->v2 : e->v1;
BMEdge *e2 = v2->e;
if (v2->head.hflag & tag) {
v2->head.hflag &= ~tag;
callback((BMElem *)v2, userdata);
}
do {
if (e2->head.hflag & tag) {
e2->head.hflag &= ~tag;
callback((BMElem *)e2, userdata);
}
if (!e2->l) {
continue;
}
BMLoop *l = e2->l;
do {
BMLoop *l2 = l;
do {
if (l2->v->head.hflag & tag) {
callback((BMElem *)l2->v, userdata);
l2->v->head.hflag &= ~tag;
}
if (l2->e->head.hflag & tag) {
callback((BMElem *)l2->e, userdata);
l2->e->head.hflag &= ~tag;
}
if (l2->f->head.hflag & tag) {
callback((BMElem *)l2->f, userdata);
l2->f->head.hflag &= ~tag;
}
} while ((l2 = l2->next) != l);
} while ((l = l->radial_next) != e2->l);
} while ((e2 = BM_DISK_EDGE_NEXT(e2, v2)) != v2->e);
}
}
/*
* This function is rather complicated. It has to
* snap UVs, log geometry and free ids.
*/
ATTR_NO_OPT BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh,
BMEdge *e,
BMVert *v1,
BMVert *v2,
GHash *deleted_verts,
BLI_Buffer *deleted_faces,
EdgeQueueContext *eq_ctx)
{
bm_logstack_push();
BMVert *v_del, *v_conn;
if (pbvh->dyntopo_stop) {
bm_logstack_pop();
return nullptr;
}
pbvh_check_vert_boundary(pbvh, v1);
pbvh_check_vert_boundary(pbvh, v2);
TraceData tdata;
BLI_smallhash_init(&tdata.visit);
tdata.pbvh = pbvh;
tdata.e = e;
const int mupdateflag = SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT;
// updateflag |= SCULPTVERT_NEED_TRIANGULATE; // to check for non-manifold flaps
validate_edge(pbvh, pbvh->header.bm, e, true, true);
check_vert_fan_are_tris(pbvh, e->v1);
check_vert_fan_are_tris(pbvh, e->v2);
MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v1);
MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v2);
int boundflag1 = BM_ELEM_CD_GET_INT(v1, pbvh->cd_boundary_flag);
int boundflag2 = BM_ELEM_CD_GET_INT(v2, pbvh->cd_boundary_flag);
/* one of the two vertices may be masked, select the correct one for deletion */
if (!(boundflag1 & SCULPTVERT_ALL_CORNER) || DYNTOPO_MASK(eq_ctx->cd_vert_mask_offset, v1) <
DYNTOPO_MASK(eq_ctx->cd_vert_mask_offset, v2)) {
v_del = v1;
v_conn = v2;
}
else {
v_del = v2;
v_conn = v1;
SWAP(MSculptVert *, mv1, mv2);
SWAP(int, boundflag1, boundflag2);
}
if ((boundflag1 & SCULPTVERT_ALL_CORNER) ||
(boundflag1 & SCULPTVERT_ALL_BOUNDARY) != (boundflag2 & SCULPTVERT_ALL_BOUNDARY)) {
bm_logstack_pop();
return nullptr;
}
int uvidx = pbvh->header.bm->ldata.typemap[CD_PROP_FLOAT2];
CustomDataLayer *uv_layer = nullptr;
int totuv = 0;
if (uvidx >= 0) {
uv_layer = pbvh->header.bm->ldata.layers + uvidx;
totuv = 0;
while (uvidx < pbvh->header.bm->ldata.totlayer &&
pbvh->header.bm->ldata.layers[uvidx].type == CD_PROP_FLOAT2) {
uvidx++;
totuv++;
}
}
/*have to check edge flags directly, vertex flag test above isn't specific enough and
can sometimes let bad edges through*/
if ((boundflag1 & SCULPT_BOUNDARY_SHARP) && (e->head.hflag & BM_ELEM_SMOOTH)) {
bm_logstack_pop();
return nullptr;
}
if ((boundflag1 & SCULPT_BOUNDARY_SEAM) && !(e->head.hflag & BM_ELEM_SEAM)) {
bm_logstack_pop();
return nullptr;
}
bool snap = !(boundflag2 & SCULPTVERT_ALL_CORNER);
/* snap customdata */
if (snap) {
int ni_conn = BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_vert_node_offset);
const float v_ws[2] = {0.5f, 0.5f};
const void *v_blocks[2] = {v_del->head.data, v_conn->head.data};
CustomData_bmesh_interp(
&pbvh->header.bm->vdata, v_blocks, v_ws, nullptr, 2, v_conn->head.data);
BM_ELEM_CD_SET_INT(v_conn, pbvh->cd_vert_node_offset, ni_conn);
}
// deal with UVs
if (e->l) {
BMLoop *l = e->l;
for (int step = 0; step < 2; step++) {
BMVert *v = step ? e->v2 : e->v1;
BMEdge *e2 = v->e;
if (!e2) {
continue;
}
do {
BMLoop *l2 = e2->l;
if (!l2) {
continue;
}
do {
BMLoop *l3 = l2->v != v ? l2->next : l2;
/* store visit bits for each uv layer in l3->head.index */
l3->head.index = 0;
} while ((l2 = l2->radial_next) != e2->l);
} while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e);
}
float(*uv)[2] = BLI_array_alloca(uv, 4 * totuv);
do {
const void *ls2[2] = {l->head.data, l->next->head.data};
float ws2[2] = {0.5f, 0.5f};
if (!snap) {
const int axis = l->v == v_del ? 0 : 1;
ws2[axis] = 0.0f;
ws2[axis ^ 1] = 1.0f;
}
for (int step = 0; uv_layer && step < 2; step++) {
BMLoop *l1 = step ? l : l->next;
for (int k = 0; k < totuv; k++) {
float *luv = (float *)BM_ELEM_CD_GET_VOID_P(l1, uv_layer[k].offset);
copy_v2_v2(uv[k * 2 + step], luv);
}
}
CustomData_bmesh_interp(&pbvh->header.bm->ldata, ls2, ws2, nullptr, 2, l->head.data);
CustomData_bmesh_copy_data(
&pbvh->header.bm->ldata, &pbvh->header.bm->ldata, l->head.data, &l->next->head.data);
for (int step = 0; totuv >= 0 && step < 2; step++) {
BMVert *v = step ? l->next->v : l->v;
BMLoop *l1 = step ? l->next : l;
BMEdge *e2 = v->e;
do {
BMLoop *l2 = e2->l;
if (!l2) {
continue;
}
do {
BMLoop *l3 = l2->v != v ? l2->next : l2;
if (!l3 || l3 == l1 || l3 == l || l3 == l->next) {
continue;
}
for (int k = 0; k < totuv; k++) {
const int flag = 1 << k;
if (l3->head.index & flag) {
continue;
}
const int cd_uv = uv_layer[k].offset;
float *luv1 = (float *)BM_ELEM_CD_GET_VOID_P(l1, cd_uv);
float *luv2 = (float *)BM_ELEM_CD_GET_VOID_P(l3, cd_uv);
float dx = luv2[0] - uv[k * 2 + step][0];
float dy = luv2[1] - uv[k * 2 + step][1];
float delta = dx * dx + dy * dy;
if (delta < 0.001) {
l3->head.index |= flag;
copy_v2_v2(luv2, luv1);
}
}
} while ((l2 = l2->radial_next) != e2->l);
} while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e);
}
} while ((l = l->radial_next) != e->l);
}
validate_vert_faces(pbvh, pbvh->header.bm, v_conn, false, true);
BMEdge *e2;
const int tag = COLLAPSE_TAG;
const int facetag = COLLAPSE_FACE_TAG;
const int log_rings = 1;
// edge_ring_do(e, collapse_ring_callback_pre, &tdata, tag, facetag, log_rings - 1);
blender::dyntopo::pbvh_bmesh_vert_remove(pbvh, v_del);
BM_log_edge_pre(pbvh->bm_log, e);
BLI_smallhash_reinsert(&tdata.visit, (uintptr_t)e, nullptr);
#ifdef USE_NEW_IDMAP
BM_idmap_release(pbvh->bm_idmap, (BMElem *)e, true);
#endif
BM_log_vert_removed(pbvh->bm_log, v_del, pbvh->cd_vert_mask_offset);
BLI_smallhash_reinsert(&tdata.visit, (uintptr_t)v_del, nullptr);
#ifdef USE_NEW_IDMAP
BM_idmap_release(pbvh->bm_idmap, (BMElem *)v_del, true);
#endif
// edge_ring_do(e, collapse_ring_callback_pre2, &tdata, tag, facetag, log_rings - 1);
if (deleted_verts) {
BLI_ghash_insert(deleted_verts, (void *)v_del, nullptr);
}
pbvh_bmesh_check_nodes(pbvh);
validate_vert_faces(pbvh, pbvh->header.bm, v_conn, false, true);
BMTracer tracer;
BM_empty_tracer(&tracer, &tdata);
tracer.on_vert_kill = col_on_vert_kill;
tracer.on_edge_kill = col_on_edge_kill;
tracer.on_face_kill = col_on_face_kill;
tracer.on_vert_create = col_on_vert_add;
tracer.on_edge_create = col_on_edge_add;
tracer.on_face_create = col_on_face_add;
if (!snap) {
float co[3];
copy_v3_v3(co, v_conn->co);
// full non-manifold collapse
BM_edge_collapse(pbvh->header.bm, e, v_del, true, true, true, true, &tracer);
copy_v3_v3(v_conn->co, co);
}
else {
float co[3];
add_v3_v3v3(co, v_del->co, v_conn->co);
mul_v3_fl(co, 0.5f);
// full non-manifold collapse
BM_edge_collapse(pbvh->header.bm, e, v_del, true, true, true, true, &tracer);
copy_v3_v3(v_conn->co, co);
}
if (!v_conn->e) {
printf("%s: pbvh error, v_conn->e was null\n", __func__);
return v_conn;
}
validate_vert_faces(pbvh, pbvh->header.bm, v_conn, false, true);
e2 = v_conn->e;
do {
BMLoop *l = e2->l;
if (!l) {
continue;
}
do {
BMLoop *l2 = l->f->l_first;
do {
pbvh_boundary_update_bmesh(pbvh, l2->v);
MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l2->v);
mv->flag |= mupdateflag;
} while ((l2 = l2->next) != l->f->l_first);
} while ((l = l->radial_next) != e2->l);
} while ((e2 = BM_DISK_EDGE_NEXT(e2, v_conn)) != v_conn->e);
pbvh_bmesh_check_nodes(pbvh);
if (!v_conn) {
bm_logstack_pop();
return nullptr;
}
MSculptVert *mv_conn = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v_conn);
pbvh_boundary_update_bmesh(pbvh, v_conn);
MV_ADD_FLAG(mv_conn, mupdateflag);
#if 0
e2 = v_conn->e;
BMEdge *enext;
do {
if (!e2) {
break;
}
enext = BM_DISK_EDGE_NEXT(e2, v_conn);
// kill wire edge
if (!e2->l) {
BM_log_edge_pre(pbvh->bm_log, e2);
BM_idmap_release(pbvh->bm_idmap, (BMElem *)e2, true);
BM_edge_kill(pbvh->header.bm, e2);
}
} while (v_conn->e && (e2 = enext) != v_conn->e);
#endif
if (0) {
BMElem *elem = nullptr;
SmallHashIter siter;
void **val = BLI_smallhash_iternew_p(&tdata.visit, &siter, (uintptr_t *)&elem);
for (; val; val = BLI_smallhash_iternext_p(&siter, (uintptr_t *)&elem)) {
if (bm_elem_is_free(elem, BM_EDGE) && bm_elem_is_free(elem, BM_VERT) &&
bm_elem_is_free(elem, BM_FACE)) {
continue;
}
switch (elem->head.htype) {
case BM_VERT:
if (!BM_log_has_vert_post(pbvh->bm_log, (BMVert *)elem)) {
BM_log_vert_added(pbvh->bm_log, (BMVert *)elem, -1);
}
break;
case BM_EDGE:
if (!BM_log_has_edge_post(pbvh->bm_log, (BMEdge *)elem)) {
BM_log_edge_added(pbvh->bm_log, (BMEdge *)elem);
}
break;
case BM_FACE:
if (!BM_log_has_face_post(pbvh->bm_log, (BMFace *)elem)) {
BM_log_face_added(pbvh->bm_log, (BMFace *)elem);
}
break;
}
}
}
MSculptVert *mv3 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v_conn);
pbvh_boundary_update_bmesh(pbvh, v_conn);
MV_ADD_FLAG(mv3, mupdateflag);
if (!v_conn->e) {
// delete isolated vertex
if (BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_vert_node_offset) != DYNTOPO_NODE_NONE) {
blender::dyntopo::pbvh_bmesh_vert_remove(pbvh, v_conn);
}
// if (!BLI_smallhash_lookup(&tdata.visit, (intptr_t)v_conn)) {
BM_log_vert_removed(pbvh->bm_log, v_conn, 0);
//}
#ifdef USE_NEW_IDMAP
BM_idmap_release(pbvh->bm_idmap, (BMElem *)v_conn, true);
#endif
BM_vert_kill(pbvh->header.bm, v_conn);
bm_logstack_pop();
return nullptr;
}
if (BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_vert_node_offset) == DYNTOPO_NODE_NONE) {
printf("%s: error: failed to remove vert from pbvh?\n", __func__);
}
#if 0
e = v_conn->e;
if (e) {
do {
enext = BM_DISK_EDGE_NEXT(e, v_conn);
BMVert *v2 = BM_edge_other_vert(e, v_conn);
MSculptVert *mv4 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v2);
} while ((e = enext) != v_conn->e);
}
#endif
if (v_conn) {
check_for_fins(pbvh, v_conn);
}
validate_vert_faces(pbvh, pbvh->header.bm, v_conn, false, true);
bm_logstack_pop();
PBVH_CHECK_NAN(v_conn->co);
return v_conn;
}
} // namespace blender::dyntopo

View File

@ -0,0 +1,714 @@
//#define DYNTOPO_VALIDATE_LOG
#include "BKE_paint.h"
#include "BKE_pbvh.h"
#include "bmesh.h"
#include "pbvh_intern.h"
struct MinMaxHeap;
struct GHash;
struct BLI_Buffer;
extern "C" {
void bmesh_disk_edge_append(BMEdge *e, BMVert *v);
void bmesh_radial_loop_append(BMEdge *e, BMLoop *l);
void bm_kill_only_edge(BMesh *bm, BMEdge *e);
void bm_kill_only_loop(BMesh *bm, BMLoop *l);
void bm_kill_only_face(BMesh *bm, BMFace *f);
}
namespace blender::dyntopo {
static int elem_sizes[] = {-1,
(int)sizeof(BMVert),
(int)sizeof(BMEdge),
0,
(int)sizeof(BMLoop),
-1,
-1,
-1,
(int)sizeof(BMFace)};
inline bool bm_elem_is_free(BMElem *elem, int htype)
{
BLI_asan_unpoison(elem, elem_sizes[htype]);
bool ret = elem->head.htype != htype;
if (ret) {
BLI_asan_poison(elem, elem_sizes[htype]);
}
return ret;
}
#ifdef DYNTOPO_VALIDATE_LOG
# define VALIDATE_LOG(log) BM_log_validate_cur(log)
#else
# define VALIDATE_LOG(log)
#endif
//#define DYNTOPO_REPORT
//#define WITH_ADAPTIVE_CURVATURE
//#define DYNTOPO_NO_THREADING
#define SCULPTVERT_VALENCE_TEMP SCULPTVERT_SPLIT_TEMP
#define SCULPTVERT_SMOOTH_BOUNDARY \
(SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SHARP | \
SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV)
#define SCULPTVERT_ALL_BOUNDARY \
(SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SHARP | \
SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV)
#define SCULPTVERT_SMOOTH_CORNER \
(SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP | SCULPT_CORNER_SEAM | \
SCULPT_CORNER_UV)
#define SCULPTVERT_ALL_CORNER \
(SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP | SCULPT_CORNER_SEAM | \
SCULPT_CORNER_UV)
#define DYNTOPO_MAX_ITER 4096
#define DYNTOPO_USE_HEAP
#define DYNTOPO_USE_MINMAX_HEAP
#ifndef DYNTOPO_USE_HEAP
/* don't add edges into the queue multiple times */
# define USE_EDGEQUEUE_TAG
#endif
/* Avoid skinny faces */
#define USE_EDGEQUEUE_EVEN_SUBDIV
/* How much longer we need to be to consider for subdividing
* (avoids subdividing faces which are only *slightly* skinny) */
#define EVEN_EDGELEN_THRESHOLD 1.2f
/* How much the limit increases per recursion
* (avoids performing subdivisions too far away). */
#define EVEN_GENERATION_SCALE 1.1f
/* recursion depth to start applying front face test */
#define DEPTH_START_LIMIT 5
//#define FANCY_EDGE_WEIGHTS <= too slow
//#define SKINNY_EDGE_FIX
/* slightly relax geometry by this factor along surface tangents
to improve convergence of remesher */
#define DYNTOPO_SAFE_SMOOTH_FAC 0.05f
#define DYNTOPO_SAFE_SMOOTH_SUBD_ONLY_FAC 0.075f
#ifdef USE_EDGEQUEUE_EVEN_SUBDIV
# include "BKE_global.h"
#endif
/* Support for only operating on front-faces */
#define USE_EDGEQUEUE_FRONTFACE
/**
* Ensure we don't have dirty tags for the edge queue, and that they are left cleared.
* (slow, even for debug mode, so leave disabled for now).
*/
#if defined(USE_EDGEQUEUE_TAG) && 0
# if !defined(NDEBUG)
# define USE_EDGEQUEUE_TAG_VERIFY
# endif
#endif
// #define USE_VERIFY
#define DYNTOPO_MASK(cd_mask_offset, v) BM_ELEM_CD_GET_FLOAT(v, cd_mask_offset)
#ifdef USE_VERIFY
static void pbvh_bmesh_verify(PBVH *pbvh);
#endif
/* -------------------------------------------------------------------- */
/** \name BMesh Utility API
*
* Use some local functions which assume triangles.
* \{ */
/**
* Typically using BM_LOOPS_OF_VERT and BM_FACES_OF_VERT iterators are fine,
* however this is an area where performance matters so do it in-line.
*
* Take care since 'break' won't works as expected within these macros!
*/
#define BM_DISK_EDGE(e, v) (&((&(e)->v1_disk_link)[(v) == (e)->v2]))
#define BM_LOOPS_OF_VERT_ITER_BEGIN(l_iter_radial_, v_) \
{ \
struct { \
BMVert *v; \
BMEdge *e_iter, *e_first; \
BMLoop *l_iter_radial; \
} _iter; \
_iter.v = v_; \
if (_iter.v->e) { \
_iter.e_iter = _iter.e_first = _iter.v->e; \
do { \
if (_iter.e_iter->l) { \
_iter.l_iter_radial = _iter.e_iter->l; \
do { \
if (_iter.l_iter_radial->v == _iter.v) { \
l_iter_radial_ = _iter.l_iter_radial;
#define BM_LOOPS_OF_VERT_ITER_END \
} \
} \
while ((_iter.l_iter_radial = _iter.l_iter_radial->radial_next) != _iter.e_iter->l) \
; \
} \
} \
while ((_iter.e_iter = BM_DISK_EDGE_NEXT(_iter.e_iter, _iter.v)) != _iter.e_first) \
; \
} \
} \
((void)0)
#define BM_FACES_OF_VERT_ITER_BEGIN(f_iter_, v_) \
{ \
BMLoop *l_iter_radial_; \
BM_LOOPS_OF_VERT_ITER_BEGIN (l_iter_radial_, v_) { \
f_iter_ = l_iter_radial_->f;
#define BM_FACES_OF_VERT_ITER_END \
} \
BM_LOOPS_OF_VERT_ITER_END; \
} \
((void)0)
struct EdgeQueue;
typedef struct EdgeQueueContext {
BLI_mempool *pool;
BMesh *bm;
DyntopoMaskCB mask_cb;
void *mask_cb_data;
int cd_sculpt_vert;
int cd_vert_mask_offset;
int cd_vert_node_offset;
int cd_face_node_offset;
float avg_elen;
float max_elen;
float min_elen;
float totedge;
bool local_mode;
float surface_smooth_fac;
struct MinMaxHeap *heap_mm;
int max_heap_mm;
// TableGSet *used_verts;
BMVert **used_verts;
int used_verts_size;
int tot_used_verts;
float view_normal[3];
bool use_view_normal;
float limit_min, limit_max, limit_mid;
const float *center;
float center_proj[3]; /* for when we use projected coords. */
float radius_squared;
float limit_len_min;
float limit_len_max;
float limit_len_min_sqr;
float limit_len_max_sqr;
bool (*edge_queue_tri_in_range)(const struct EdgeQueueContext *q, BMVert *vs[3], float no[3]);
bool (*edge_queue_vert_in_range)(const struct EdgeQueueContext *q, BMVert *v);
PBVHTopologyUpdateMode mode;
} EdgeQueueContext;
bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root);
bool check_face_is_tri(PBVH *pbvh, BMFace *f);
bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v);
ATTR_NO_OPT BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh,
BMEdge *e,
BMVert *v1,
BMVert *v2,
struct GHash *deleted_verts,
struct BLI_Buffer *deleted_faces,
struct EdgeQueueContext *eq_ctx);
extern "C" void bm_log_message(const char *fmt, ...);
void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v);
inline bool bm_edge_tag_test(BMEdge *e)
{
/* is the edge or one of its faces tagged? */
return (BM_elem_flag_test(e->v1, BM_ELEM_TAG) || BM_elem_flag_test(e->v2, BM_ELEM_TAG) ||
(e->l &&
(BM_elem_flag_test(e->l->f, BM_ELEM_TAG) ||
(e->l != e->l->radial_next && BM_elem_flag_test(e->l->radial_next->f, BM_ELEM_TAG)))));
}
inline void bm_edge_tag_disable(BMEdge *e)
{
BM_elem_flag_disable(e->v1, BM_ELEM_TAG);
BM_elem_flag_disable(e->v2, BM_ELEM_TAG);
if (e->l) {
BM_elem_flag_disable(e->l->f, BM_ELEM_TAG);
if (e->l != e->l->radial_next) {
BM_elem_flag_disable(e->l->radial_next->f, BM_ELEM_TAG);
}
}
}
/* takes the edges loop */
BLI_INLINE int bm_edge_is_manifold_or_boundary(BMLoop *l)
{
#if 0
/* less optimized version of check below */
return (BM_edge_is_manifold(l->e) || BM_edge_is_boundary(l->e);
#else
/* if the edge is a boundary it points to its self, else this must be a manifold */
return LIKELY(l) && LIKELY(l->radial_next->radial_next == l);
#endif
}
inline void bm_edge_tag_enable(BMEdge *e)
{
BM_elem_flag_enable(e->v1, BM_ELEM_TAG);
BM_elem_flag_enable(e->v2, BM_ELEM_TAG);
if (e->l) {
BM_elem_flag_enable(e->l->f, BM_ELEM_TAG);
if (e->l != e->l->radial_next) {
BM_elem_flag_enable(e->l->radial_next->f, BM_ELEM_TAG);
}
}
}
void pbvh_bmesh_face_remove(
PBVH *pbvh, BMFace *f, bool log_face, bool check_verts, bool ensure_ownership_transfer);
bool check_for_fins(PBVH *pbvh, BMVert *v);
} // namespace blender::dyntopo
extern "C" {
bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
PBVHTopologyUpdateMode mode,
const float center[3],
const float view_normal[3],
float radius,
const bool use_frontface,
const bool use_projected,
int sym_axis,
bool updatePBVH,
DyntopoMaskCB mask_cb,
void *mask_cb_data,
int custom_max_steps,
bool disable_surface_relax,
bool is_snake_hook);
void BKE_pbvh_bmesh_remove_face(PBVH *pbvh, BMFace *f, bool log_face);
void BKE_pbvh_bmesh_remove_edge(PBVH *pbvh, BMEdge *e, bool log_edge);
void BKE_pbvh_bmesh_remove_vertex(PBVH *pbvh, BMVert *v, bool log_vert);
void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool force_tree_walk);
}
/*************************** Topology update **************************/
/**** Debugging Tools ********/
inline void fix_mesh(PBVH *pbvh, BMesh *bm)
{
BMIter iter;
BMVert *v;
BMEdge *e;
BMFace *f;
printf("fixing mesh. . .\n");
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
v->e = nullptr;
MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v);
MV_ADD_FLAG(mv,
SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_TRIANGULATE);
}
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
e->v1_disk_link.next = e->v1_disk_link.prev = nullptr;
e->v2_disk_link.next = e->v2_disk_link.prev = nullptr;
e->l = nullptr;
if (e->v1 == e->v2) {
bm_kill_only_edge(bm, e);
}
}
// rebuild disk cycles
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
if (BM_edge_exists(e->v1, e->v2)) {
printf("duplicate edge %p!\n", e);
bm_kill_only_edge(bm, e);
continue;
}
bmesh_disk_edge_append(e, e->v1);
bmesh_disk_edge_append(e, e->v2);
}
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
BMLoop *l = f->l_first;
do {
if (f->len < 3) {
break;
}
if (l->next->v == l->v) {
BMLoop *l_del = l->next;
l->next = l_del->next;
l_del->next->prev = l;
f->len--;
if (f->l_first == l_del) {
f->l_first = l;
}
bm_kill_only_loop(bm, l_del);
if (f->len < 3) {
break;
}
}
} while ((l = l->next) != f->l_first);
if (f->len < 3) {
int ni = BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset);
if (ni >= 0 && ni < pbvh->totnode && (pbvh->nodes[ni].flag & PBVH_Leaf)) {
BLI_table_gset_remove(pbvh->nodes[ni].bm_faces, f, nullptr);
}
bm_kill_only_face(bm, f);
continue;
}
do {
l->e = BM_edge_exists(l->v, l->next->v);
if (!l->e) {
l->e = BM_edge_create(bm, l->v, l->next->v, nullptr, BM_CREATE_NOP);
}
bmesh_radial_loop_append(l->e, l);
} while ((l = l->next) != f->l_first);
}
bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE;
bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE;
printf("done fixing mesh.\n");
}
//#define CHECKMESH
//#define TEST_INVALID_NORMALS
#ifndef CHECKMESH
# define validate_vert(pbvh, bm, v, autofix, check_manifold) true
# define validate_edge(pbvh, bm, e, autofix, check_manifold) true
# define validate_face(pbvh, bm, f, autofix, check_manifold) true
# define validate_vert_faces(pbvh, bm, v, autofix, check_manifold) true
# define check_face_is_manifold(pbvh, bm, f) true
#else
# define CHECKMESH_ATTR ATTR_NO_OPT
CHECKMESH_ATTR static void _debugprint(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
CHECKMESH_ATTR static bool check_face_is_manifold(PBVH *pbvh, BMesh *bm, BMFace *f)
{
BMLoop *l = f->l_first;
do {
if (l->radial_next != l && l->radial_next->radial_next != l) {
//_debugprint("non-manifold edge in loop\n");
BMVert *v1 = l->e->v1, *v2 = l->e->v2;
for (int i = 0; i < 2; i++) {
BMVert *v = i ? v2 : v1;
BMEdge *e = v->e;
if (!e) {
continue;
}
int i = 0;
do {
if (!e) {
break;
}
bool same = e->v1 == v1 && e->v2 == v2;
same = same || (e->v1 == v2 && e->v2 == v1);
if (same && e != l->e) {
// printf("duplicate edges in face!\n");
}
if (i++ > 5000) {
printf("infinite loop in edge disk cycle! v: %p, e: %p\n", v, e);
break;
}
} while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e);
}
l->e->head.hflag |= BM_ELEM_SELECT;
l->f->head.hflag |= BM_ELEM_SELECT;
l->v->head.hflag |= BM_ELEM_SELECT;
// pbvh->dyntopo_stop = true;
return false;
}
# ifdef TEST_INVALID_NORMALS
if (l != l->radial_next && l->v == l->radial_next->v) {
_debugprint("invalid normals\n");
return false;
}
# endif
} while ((l = l->next) != f->l_first);
return true;
}
CHECKMESH_ATTR
static bool validate_vert(PBVH *pbvh, BMesh *bm, BMVert *v, bool autofix, bool check_manifold)
{
if (v->head.htype != BM_VERT) {
_debugprint("bad vertex\n");
return false;
}
BMEdge *e = v->e;
int i = 0;
if (!e) {
return true;
}
do {
if (e->v1 != v && e->v2 != v) {
_debugprint("edge does not contain v\n");
goto error;
}
if (e->l) {
int j = 0;
BMLoop *l = e->l;
do {
if (l->e->v1 != v && l->e->v2 != v) {
_debugprint("loop's edges doesn't contain v\n");
goto error;
}
if (l->v != v && l->next->v != v) {
_debugprint("loop and loop->next don't contain v\n");
goto error;
}
j++;
if (j > 1000) {
_debugprint("corrupted radial cycle\n");
goto error;
}
if (check_manifold) {
check_face_is_manifold(pbvh, bm, l->f);
}
} while ((l = l->radial_next) != e->l);
}
if (i > 10000) {
_debugprint("corrupted disk cycle\n");
goto error;
}
e = BM_DISK_EDGE_NEXT(e, v);
i++;
} while (e != v->e);
return true;
error:
if (autofix) {
fix_mesh(pbvh, bm);
}
return false;
}
CHECKMESH_ATTR
static bool validate_edge(PBVH *pbvh, BMesh *bm, BMEdge *e, bool autofix, bool check_manifold)
{
if (e->head.htype != BM_EDGE) {
_debugprint("corrupted edge!\n");
return false;
}
bool ret = validate_vert(pbvh, bm, e->v1, false, check_manifold) &&
validate_vert(pbvh, bm, e->v2, false, check_manifold);
if (!ret && autofix) {
fix_mesh(pbvh, bm);
}
return ret;
}
CHECKMESH_ATTR bool face_verts_are_same(PBVH *pbvh, BMesh *bm, BMFace *f1, BMFace *f2)
{
BMLoop *l1 = f1->l_first;
BMLoop *l2 = f2->l_first;
int count1 = 0;
do {
count1++;
} while ((l1 = l1->next) != f1->l_first);
do {
bool ok = false;
do {
if (l2->v == l1->v) {
ok = true;
break;
}
} while ((l2 = l2->next) != f2->l_first);
if (!ok) {
return false;
}
} while ((l1 = l1->next) != f1->l_first);
return true;
}
CHECKMESH_ATTR
static bool validate_face(PBVH *pbvh, BMesh *bm, BMFace *f, bool autofix, bool check_manifold)
{
if (f->head.htype != BM_FACE) {
_debugprint("corrupted edge!\n");
return false;
}
BMLoop **ls = nullptr;
BLI_array_staticdeclare(ls, 32);
BMLoop *l = f->l_first;
int i = 0;
do {
i++;
if (i > 100000) {
_debugprint("Very corrupted face!\n");
goto error;
}
if (!validate_edge(pbvh, bm, l->e, false, check_manifold)) {
goto error;
}
BMLoop *l2 = l->radial_next;
do {
if (l2->f != f && face_verts_are_same(pbvh, bm, l2->f, f)) {
_debugprint("Duplicate faces!\n");
goto error;
}
} while ((l2 = l2->radial_next) != l);
BLI_array_append(ls, l);
} while ((l = l->next) != f->l_first);
for (int i = 0; i < BLI_array_len(ls); i++) {
BMLoop *l1 = ls[i];
for (int j = 0; j < BLI_array_len(ls); j++) {
BMLoop *l2 = ls[j];
if (i != j && l1->v == l2->v) {
_debugprint("duplicate verts in face!\n");
goto error;
}
if (BM_edge_exists(l->v, l->next->v) != l->e) {
_debugprint("loop has wrong edge!\n");
goto error;
}
}
}
BLI_array_free(ls);
return true;
error:
BLI_array_free(ls);
if (autofix) {
fix_mesh(pbvh, bm);
}
return false;
}
CHECKMESH_ATTR bool validate_vert_faces(
PBVH *pbvh, BMesh *bm, BMVert *v, int autofix, bool check_manifold)
{
if (!validate_vert(pbvh, bm, v, autofix, check_manifold)) {
return false;
}
if (!v->e) {
return true;
}
BMEdge *e = v->e;
do {
BMLoop *l = e->l;
if (!l) {
continue;
}
do {
if (!validate_edge(pbvh, bm, l->e, false, false)) {
goto error;
}
if (!validate_face(pbvh, bm, l->f, false, check_manifold)) {
goto error;
}
} while ((l = l->radial_next) != e->l);
} while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e);
return true;
error:
if (autofix) {
fix_mesh(pbvh, bm);
}
return false;
}
#endif

View File

@ -12,8 +12,8 @@
#include "BKE_paint.h" /* for SCULPT_BOUNDARY_NEEDS_UPDATE */
#include "BKE_pbvh.h"
#include "bmesh.h"
#include "../../bmesh/intern/bmesh_idmap.h"
#include "bmesh.h"
#define PBVH_STACK_FIXED_DEPTH 100
@ -429,12 +429,10 @@ void bke_pbvh_update_vert_boundary(int cd_sculpt_vert,
int cd_face_node_offset,
int cd_vcol_offset,
int cd_boundary_flags,
BMVert * v,
BMVert *v,
int bound_symmetry,
const struct CustomData *ldata,
const int totuv);
void pbvh_boundary_update_bmesh(PBVH *pbvh, struct BMVert *v);
bool pbvh_boundary_needs_update_bmesh(PBVH *pbvh, struct BMVert *v);
BLI_INLINE bool pbvh_check_vert_boundary(PBVH *pbvh, struct BMVert *v)
{
@ -467,6 +465,24 @@ void pbvh_pixels_free(PBVH *pbvh);
void pbvh_pixels_free_brush_test(PBVHNode *node);
void pbvh_free_draw_buffers(PBVH *pbvh, PBVHNode *node);
BLI_INLINE bool pbvh_boundary_needs_update_bmesh(PBVH *pbvh, BMVert *v)
{
int *flags = (int *)BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_boundary_flag);
return *flags & SCULPT_BOUNDARY_NEEDS_UPDATE;
}
BLI_INLINE void pbvh_boundary_update_bmesh(PBVH *pbvh, BMVert *v)
{
if (pbvh->cd_boundary_flag == -1) {
printf("%s: error!\n", __func__);
return;
}
int *flags = (int *)BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_boundary_flag);
*flags |= SCULPT_BOUNDARY_NEEDS_UPDATE;
}
#ifdef __cplusplus
}
#endif

View File

@ -8,7 +8,7 @@
# define WITH_BM_ID_FREELIST
#endif
//#define USE_NEW_IDMAP
#define USE_NEW_IDMAP
#ifdef __cplusplus
# include "BLI_map.hh"