Sculpt dyntopo: Smooth improvements and bug fixes

* Added an option to weight smooth by face areas
* Dyntopo now caches face areas in a CD_PROP_FLOAT layer
* Dyntopo also caches number of edges around verts inside of
  MDynTopoVert.  To avoid increasing the struct size flag was
  made a short.
* Cleanup mode (dissolves 3/4-valence verts) now piggybacks on
  subdivide code to build list of verts; this is much faster.
This commit is contained in:
Joseph Eagar 2021-08-23 21:06:10 -07:00
parent 9b8c82e2ed
commit bde54e127e
16 changed files with 899 additions and 287 deletions

View File

@ -567,6 +567,8 @@ def brush_settings(layout, context, brush, popover=False):
slider=True,
)
box.prop(brush, "use_weighted_smooth")
box.prop(brush, "use_custom_auto_smooth_spacing", text="Custom Spacing")
if brush.use_custom_auto_smooth_spacing:
UnifiedPaintPanel.prop_unified(
@ -812,7 +814,9 @@ def brush_settings(layout, context, brush, popover=False):
elif sculpt_tool == 'SMOOTH':
col = layout.column()
col.prop(brush, "use_weighted_smooth")
col.prop(brush, "smooth_deform_type")
if brush.smooth_deform_type == 'SURFACE':
col.prop(brush, "surface_smooth_shape_preservation")
col.prop(brush, "surface_smooth_current_vertex")

View File

@ -533,6 +533,7 @@ typedef struct SculptSession {
int cd_face_node_offset;
int cd_vcol_offset;
int cd_faceset_offset;
int cd_face_areas;
bool bm_smooth_shading;
/* Undo/redo log for dynamic topology sculpting */

View File

@ -209,7 +209,11 @@ typedef enum {
PBVH_Delete = 1 << 15,
PBVH_UpdateCurvatureDir = 1 << 16,
PBVH_UpdateTris = 1 << 17,
PBVH_RebuildNodeVerts = 1 << 18
PBVH_RebuildNodeVerts = 1 << 18,
/* tri areas are not guaranteed to be up to date, tools should
update all nodes on first step of brush*/
PBVH_UpdateTriAreas = 1 << 19
} PBVHNodeFlags;
typedef struct PBVHFrustumPlanes {
@ -263,11 +267,13 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh,
const int cd_vert_node_offset,
const int cd_face_node_offset,
const int cd_dyn_vert,
const int cd_face_areas,
bool fast_draw);
void BKE_pbvh_update_offsets(PBVH *pbvh,
const int cd_vert_node_offset,
const int cd_face_node_offset,
const int cd_dyn_vert);
const int cd_dyn_vert,
const int cd_face_areas);
void BKE_pbvh_free(PBVH *pbvh);
/** update original data, only data whose r_** parameters are passed in will be updated*/
@ -436,6 +442,18 @@ bool BKE_pbvh_bmesh_update_topology_nodes(PBVH *pbvh,
void *mask_cb_data);
/* Node Access */
void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node);
// updates boundaries and valences for whole mesh
void BKE_pbvh_bmesh_on_mesh_change(PBVH *pbvh);
bool BKE_pbvh_bmesh_check_valence(PBVH *pbvh, SculptVertRef vertex);
void BKE_pbvh_bmesh_update_valence(int cd_dyn_vert, SculptVertRef vertex);
void BKE_pbvh_bmesh_update_all_valence(PBVH *pbvh);
void BKE_pbvh_bmesh_flag_all_disk_sort(PBVH *pbvh);
bool BKE_pbvh_bmesh_mark_update_valence(PBVH *pbvh, SculptVertRef vertex);
void BKE_pbvh_node_mark_update_tri_area(PBVHNode *node);
void BKE_pbvh_update_all_tri_areas(PBVH *pbvh);
void BKE_pbvh_node_mark_update(PBVHNode *node);
void BKE_pbvh_node_mark_update_mask(PBVHNode *node);
void BKE_pbvh_node_mark_update_color(PBVHNode *node);
@ -751,6 +769,8 @@ void BKE_pbvh_update_vert_boundary(int cd_dyn_vert, int cd_faceset_offset, struc
PBVHNode *BKE_pbvh_get_node_leaf_safe(PBVH *pbvh, int i);
void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, SculptVertRef vertex, float *r_areas, int valence);
#if 0
typedef enum {
SCULPT_TEXTURE_UV = 1 << 0, // per-uv

View File

@ -32,6 +32,8 @@
#include <stdio.h>
#define DYNVERT_VALENCE_TEMP (1 << 14)
//#define USE_NEW_SPLIT
#define DYNVERT_ALL_BOUNDARY (DYNVERT_BOUNDARY | DYNVERT_FSET_BOUNDARY)
@ -162,6 +164,7 @@ BLI_INLINE void surface_smooth_v_safe(PBVH *pbvh, BMVert *v)
if (!e) {
return;
}
pbvh_check_vert_boundary(pbvh, v);
const int cd_dyn_vert = pbvh->cd_dyn_vert;
@ -172,7 +175,8 @@ BLI_INLINE void surface_smooth_v_safe(PBVH *pbvh, BMVert *v)
do {
BMVert *v2 = e->v1 == v ? e->v2 : e->v1;
pbvh_check_vert_boundary(pbvh, v2);
// can't check for boundary here, thread
// pbvh_check_vert_boundary(pbvh, v2);
MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(cd_dyn_vert, v2);
const bool bound2 = mv2->flag & DYNVERT_ALL_BOUNDARY;
@ -846,8 +850,40 @@ typedef struct {
float max_elen;
float min_elen;
float totedge;
BMVert **val34_verts;
int val34_verts_tot;
int val34_verts_size;
} EdgeQueueContext;
static void edge_queue_insert_val34_vert(EdgeQueueContext *eq_ctx, BMVert *v)
{
MDynTopoVert *mv = BKE_PBVH_DYNVERT(eq_ctx->cd_dyn_vert, v);
// prevent double adding
if (mv->flag & DYNVERT_VALENCE_TEMP) {
return;
}
mv->flag |= DYNVERT_VALENCE_TEMP;
eq_ctx->val34_verts_tot++;
if (eq_ctx->val34_verts_tot > eq_ctx->val34_verts_size) {
int size2 = 4 + eq_ctx->val34_verts_tot + (eq_ctx->val34_verts_tot >> 1);
if (eq_ctx->val34_verts) {
eq_ctx->val34_verts = MEM_reallocN(eq_ctx->val34_verts, sizeof(void *) * size2);
}
else {
eq_ctx->val34_verts = MEM_mallocN(sizeof(void *) * size2, "val34_verts");
}
eq_ctx->val34_verts_size = size2;
}
eq_ctx->val34_verts[eq_ctx->val34_verts_tot - 1] = v;
}
BLI_INLINE float maskcb_get(EdgeQueueContext *eq_ctx, BMEdge *e)
{
if (eq_ctx->mask_cb) {
@ -1019,6 +1055,8 @@ typedef struct EdgeQueueThreadData {
PBVH *pbvh;
PBVHNode *node;
BMEdge **edges;
BMVert **val34_verts;
int val34_verts_tot;
EdgeQueueContext *eq_ctx;
int totedge;
int size;
@ -1346,6 +1384,8 @@ static void long_edge_queue_edge_add_recursive_2(EdgeQueueThreadData *tdata,
}
}
static int _long_edge_queue_task_cb_seed = 0;
static void long_edge_queue_task_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
@ -1353,6 +1393,9 @@ static void long_edge_queue_task_cb(void *__restrict userdata,
EdgeQueueThreadData *tdata = ((EdgeQueueThreadData *)userdata) + n;
PBVHNode *node = tdata->node;
EdgeQueueContext *eq_ctx = tdata->eq_ctx;
RNG *rng = BLI_rng_new(_long_edge_queue_task_cb_seed++); // I don't care if seed becomes mangled
BMVert **val34 = NULL;
BLI_array_declare(val34);
BMFace *f;
const int cd_dyn_vert = tdata->pbvh->cd_dyn_vert;
@ -1381,9 +1424,23 @@ static void long_edge_queue_task_cb(void *__restrict userdata,
BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
BMLoop *l_iter = l_first;
do {
MDynTopoVert *mv = BKE_PBVH_DYNVERT(eq_ctx->cd_dyn_vert, l_iter->v);
/*
If valence is not up to date, just add it to the list;
long_edge_queue_create will check and de-duplicate this for us.
Can't update valence in a thread after all.
*/
if (mv->valence < 5 || (mv->flag & DYNVERT_NEED_VALENCE)) {
BLI_array_append(val34, l_iter->v);
}
// try to improve convergence by applying a small amount of smoothing to topology,
// but tangentially to surface.
surface_smooth_v_safe(tdata->pbvh, l_iter->v);
if (BLI_rng_get_float(rng) > 0.75) {
surface_smooth_v_safe(tdata->pbvh, l_iter->v);
}
#ifdef USE_EDGEQUEUE_EVEN_SUBDIV
float w = maskcb_get(eq_ctx, l_iter->e);
@ -1405,6 +1462,11 @@ static void long_edge_queue_task_cb(void *__restrict userdata,
}
}
TGSET_ITER_END
BLI_rng_free(rng);
tdata->val34_verts = val34;
tdata->val34_verts_tot = BLI_array_len(val34);
}
static void short_edge_queue_task_cb(void *__restrict userdata,
@ -1573,17 +1635,25 @@ static bool check_face_is_tri(PBVH *pbvh, BMFace *f)
static bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v)
{
BMFace **fs = NULL;
BLI_array_staticdeclare(fs, 32);
MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
if (!(mv->flag & DYNVERT_NEED_TRIANGULATE)) {
return true;
}
BMFace **fs = NULL;
BLI_array_staticdeclare(fs, 32);
BMIter iter;
BMFace *f;
BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) {
BMLoop *l = f->l_first;
do {
MDynTopoVert *mv_l = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->v);
mv_l->flag |= DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_VALENCE | DYNVERT_NEED_DISK_SORT;
} while ((l = l->next) != f->l_first);
BLI_array_append(fs, f);
}
@ -1598,6 +1668,34 @@ static bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v)
return false;
}
static void edge_queue_init(EdgeQueueContext *eq_ctx,
bool use_projected,
bool use_frontface,
const float center[3],
const float view_normal[3],
const float radius)
{
if (use_projected) {
eq_ctx->q->edge_queue_tri_in_range = edge_queue_tri_in_circle;
eq_ctx->q->edge_queue_vert_in_range = edge_queue_vert_in_circle;
project_plane_normalized_v3_v3v3(eq_ctx->q->center_proj, center, view_normal);
}
else {
eq_ctx->q->edge_queue_tri_in_range = edge_queue_tri_in_sphere;
eq_ctx->q->edge_queue_vert_in_range = edge_queue_vert_in_sphere;
}
eq_ctx->q->center = center;
eq_ctx->q->view_normal = view_normal;
eq_ctx->q->radius_squared = radius * radius;
#ifdef USE_EDGEQUEUE_FRONTFACE
eq_ctx->q->use_view_normal = use_frontface;
#else
UNUSED_VARS(use_frontface);
#endif
}
/* Create a priority queue containing vertex pairs connected by a long
* edge as defined by PBVH.bm_max_edge_len.
*
@ -1618,30 +1716,13 @@ static void long_edge_queue_create(EdgeQueueContext *eq_ctx,
eq_ctx->q->heap = BLI_heapsimple_new();
eq_ctx->q->elems = NULL;
eq_ctx->q->totelems = 0;
eq_ctx->q->center = center;
eq_ctx->q->radius_squared = radius * radius;
eq_ctx->q->limit_len_squared = pbvh->bm_max_edge_len * pbvh->bm_max_edge_len;
#ifdef USE_EDGEQUEUE_EVEN_SUBDIV
eq_ctx->q->limit_len = pbvh->bm_max_edge_len;
#endif
eq_ctx->q->view_normal = view_normal;
#ifdef USE_EDGEQUEUE_FRONTFACE
eq_ctx->q->use_view_normal = use_frontface;
#else
UNUSED_VARS(use_frontface);
#endif
if (use_projected) {
eq_ctx->q->edge_queue_tri_in_range = edge_queue_tri_in_circle;
eq_ctx->q->edge_queue_vert_in_range = edge_queue_vert_in_circle;
project_plane_normalized_v3_v3v3(eq_ctx->q->center_proj, center, view_normal);
}
else {
eq_ctx->q->edge_queue_tri_in_range = edge_queue_tri_in_sphere;
eq_ctx->q->edge_queue_vert_in_range = edge_queue_vert_in_sphere;
}
edge_queue_init(eq_ctx, use_projected, use_frontface, center, view_normal, radius);
#ifdef USE_EDGEQUEUE_TAG_VERIFY
pbvh_bmesh_edge_tag_verify(pbvh);
@ -1682,15 +1763,46 @@ static void long_edge_queue_create(EdgeQueueContext *eq_ctx,
BLI_parallel_range_settings_defaults(&settings);
BLI_task_parallel_range(0, count, tdata, long_edge_queue_task_cb, &settings);
const int cd_dyn_vert = pbvh->cd_dyn_vert;
for (int i = 0; i < count; i++) {
EdgeQueueThreadData *td = tdata + i;
for (int j = 0; j < td->val34_verts_tot; j++) {
BMVert *v = td->val34_verts[j];
MDynTopoVert *mv = BKE_PBVH_DYNVERT(cd_dyn_vert, v);
if (mv->flag & DYNVERT_NEED_VALENCE) {
BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, (SculptVertRef){.i = (intptr_t)v});
}
if (mv->valence < 5) {
edge_queue_insert_val34_vert(eq_ctx, v);
}
}
BMEdge **edges = td->edges;
for (int j = 0; j < td->totedge; j++) {
BMEdge *e = edges[j];
e->head.hflag &= ~BM_ELEM_TAG;
MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(cd_dyn_vert, e->v1);
MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(cd_dyn_vert, e->v2);
if (mv1->flag & DYNVERT_NEED_VALENCE) {
BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, (SculptVertRef){.i = (intptr_t)e->v1});
}
if (mv2->flag & DYNVERT_NEED_VALENCE) {
BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, (SculptVertRef){.i = (intptr_t)e->v2});
}
if (mv1->valence < 5) {
edge_queue_insert_val34_vert(eq_ctx, e->v1);
}
if (mv2->valence < 5) {
edge_queue_insert_val34_vert(eq_ctx, e->v2);
}
check_vert_fan_are_tris(pbvh, e->v1);
check_vert_fan_are_tris(pbvh, e->v2);
@ -1702,9 +1814,8 @@ static void long_edge_queue_create(EdgeQueueContext *eq_ctx,
edge_queue_insert(eq_ctx, e, w);
}
if (td->edges) {
MEM_freeN(td->edges);
}
MEM_SAFE_FREE(td->edges);
MEM_SAFE_FREE(td->val34_verts);
}
BLI_array_free(tdata);
}
@ -1744,15 +1855,7 @@ static void short_edge_queue_create(EdgeQueueContext *eq_ctx,
UNUSED_VARS(use_frontface);
#endif
if (use_projected) {
eq_ctx->q->edge_queue_tri_in_range = edge_queue_tri_in_circle;
eq_ctx->q->edge_queue_vert_in_range = edge_queue_vert_in_circle;
project_plane_normalized_v3_v3v3(eq_ctx->q->center_proj, center, view_normal);
}
else {
eq_ctx->q->edge_queue_tri_in_range = edge_queue_tri_in_sphere;
eq_ctx->q->edge_queue_vert_in_range = edge_queue_vert_in_sphere;
}
edge_queue_init(eq_ctx, use_projected, use_frontface, center, view_normal, radius);
EdgeQueueThreadData *tdata = NULL;
BLI_array_declare(tdata);
@ -1853,6 +1956,9 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx,
pbvh_check_vert_boundary(pbvh, e->v1);
pbvh_check_vert_boundary(pbvh, e->v2);
mv1->flag |= DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY;
mv2->flag |= DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY;
bool boundary = (mv1->flag & DYNVERT_ALL_BOUNDARY) && (mv2->flag & DYNVERT_ALL_BOUNDARY);
/* Get all faces adjacent to the edge */
@ -1877,6 +1983,8 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx,
e1->head.hflag = e2->head.hflag = eflag;
v_new->head.hflag = vflag;
MDynTopoVert *mv_new = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v_new);
/*TODO: is it worth interpolating edge customdata?*/
int ni_new = BM_ELEM_CD_GET_INT(v_new, pbvh->cd_vert_node_offset);
@ -1886,6 +1994,12 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx,
CustomData_bmesh_interp(
&pbvh->bm->vdata, (const void **)vsrcs, (float *)vws, NULL, 2, v_new->head.data);
// bke_pbvh_update_vert_boundary(pbvh->cd_dyn_vert, pbvh->cd_faceset_offset, v_new);
mv_new->flag |= DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY;
mv_new->flag &= ~DYNVERT_VALENCE_TEMP;
edge_queue_insert_val34_vert(eq_ctx, v_new);
int ni_new2 = BM_ELEM_CD_GET_INT(v_new, pbvh->cd_vert_node_offset);
if (ni_new2 != ni_new) {
// printf("error!\n");
@ -1913,6 +2027,14 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx,
v1 = l_adj->v;
v2 = l_adj->next->v;
MDynTopoVert *mv1b = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v1);
MDynTopoVert *mv2b = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v2);
MDynTopoVert *mv_opp = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v_opp);
mv1b->flag |= DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY;
mv2b->flag |= DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY;
mv_opp->flag |= DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY;
if (ni != node_index && i == 0) {
pbvh_bmesh_vert_ownership_transfer(pbvh, &pbvh->nodes[ni], v_new);
}
@ -1944,6 +2066,7 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx,
*/
/* Create two new faces */
v_tri[0] = v1;
v_tri[1] = v_new;
v_tri[2] = v_opp;
@ -2032,10 +2155,6 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx,
BM_edge_kill(pbvh->bm, e);
// pbvh_bmesh_check_nodes(pbvh);
MDynTopoVert *mv_new = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v_new);
bke_pbvh_update_vert_boundary(pbvh->cd_dyn_vert, pbvh->cd_faceset_offset, v_new);
mv_new->flag |= DYNVERT_NEED_DISK_SORT;
}
static bool pbvh_bmesh_subdivide_long_edges(EdgeQueueContext *eq_ctx,
@ -2211,6 +2330,9 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
eflag |= e2->head.hflag & ~BM_ELEM_HIDDEN;
}
MDynTopoVert *mv_l = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->v);
mv_l->flag |= DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_VALENCE;
l = l->next;
} while (l != f_adj->l_first);
@ -2391,6 +2513,10 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
l1->e = BM_edge_create(pbvh->bm, l1->v, l1->next->v, NULL, 0);
}
}
MDynTopoVert *mv_l = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->v);
mv_l->flag |= DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY;
l1 = l1->next;
} while (l1 != f_del->l_first);
@ -2444,9 +2570,7 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
BM_LOOPS_OF_VERT_ITER_END;
MDynTopoVert *mv_conn = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v_conn);
bke_pbvh_update_vert_boundary(pbvh->cd_dyn_vert, pbvh->cd_faceset_offset, v_conn);
mv_conn->flag |= DYNVERT_NEED_DISK_SORT;
mv_conn->flag |= DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY;
}
/* Delete v_del */
@ -2582,7 +2706,8 @@ __attribute__((optnone))
#endif
static bool
cleanup_valence_3_4(PBVH *pbvh,
cleanup_valence_3_4(EdgeQueueContext *ectx,
PBVH *pbvh,
const float center[3],
const float view_normal[3],
float radius,
@ -2595,128 +2720,108 @@ cleanup_valence_3_4(PBVH *pbvh,
float rsqr = radius2 * radius2;
GSet *vset = BLI_gset_ptr_new("vset");
const int cd_vert_node = pbvh->cd_vert_node_offset;
for (int n = 0; n < pbvh->totnode; n++) {
PBVHNode *node = pbvh->nodes + n;
for (int vi = 0; vi < ectx->val34_verts_tot; vi++) {
BMVert *v = ectx->val34_verts[vi];
const int n = BM_ELEM_CD_GET_INT(v, cd_vert_node);
/* Check leaf nodes marked for topology update */
bool ok = (node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology);
ok = ok && !(node->flag & (PBVH_FullyHidden | PBVH_Delete));
if (!ok) {
if (n == DYNTOPO_NODE_NONE) {
continue;
}
BMVert *v;
if (len_squared_v3v3(v->co, center) >= rsqr || !v->e) {
continue;
}
TGSET_ITER (v, node->bm_unique_verts) {
if (len_squared_v3v3(v->co, center) >= rsqr || !v->e) {
continue;
}
MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
check_vert_fan_are_tris(pbvh, v);
check_vert_fan_are_tris(pbvh, v);
BKE_pbvh_bmesh_check_valence(pbvh, (SculptVertRef){.i = (intptr_t)v});
const int val = BM_vert_edge_count(v);
if (val != 4 && val != 3) {
continue;
}
const int val = mv->valence;
if (val != 4 && val != 3) {
continue;
}
MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
pbvh_check_vert_boundary(pbvh, v);
pbvh_check_vert_boundary(pbvh, v);
if (mv->flag & DYNVERT_ALL_BOUNDARY) {
continue;
}
if (mv->flag & DYNVERT_ALL_BOUNDARY) {
continue;
}
BMIter iter;
BMLoop *l;
BMLoop *ls[4];
BMVert *vs[4];
BMIter iter;
BMLoop *l;
BMLoop *ls[4];
BMVert *vs[4];
l = v->e->l;
l = v->e->l;
if (!l) {
continue;
}
if (!l) {
continue;
}
if (l->v != v) {
l = l->next;
}
bool bad = false;
int i = 0;
for (int j = 0; j < val; j++) {
ls[i++] = l->v == v ? l->next : l;
l = l->prev->radial_next;
if (l->v != v) {
l = l->next;
}
bool bad = false;
int i = 0;
/*ignore non-manifold edges*/
for (int j = 0; j < val; j++) {
ls[i++] = l->v == v ? l->next : l;
if (l->radial_next == l || l->radial_next->radial_next != l) {
bad = true;
break;
}
l = l->prev->radial_next;
if (l->v != v) {
l = l->next;
for (int k = 0; k < j; k++) {
if (ls[k]->v == ls[j]->v) {
if (ls[j]->next->v != v) {
ls[j] = ls[j]->next;
}
else {
bad = true;
break;
}
}
/*ignore non-manifold edges*/
if (l->radial_next == l || l->radial_next->radial_next != l) {
// check for non-manifold edges
if (ls[k] != ls[k]->radial_next->radial_next) {
bad = true;
break;
}
for (int k = 0; k < j; k++) {
if (ls[k]->v == ls[j]->v) {
if (ls[j]->next->v != v) {
ls[j] = ls[j]->next;
}
else {
bad = true;
break;
}
}
// check for non-manifold edges
if (ls[k] != ls[k]->radial_next->radial_next) {
bad = true;
break;
}
if (ls[k]->f == ls[j]->f) {
bad = true;
break;
}
if (ls[k]->f == ls[j]->f) {
bad = true;
break;
}
}
}
if (bad) {
continue;
}
if (bad) {
continue;
}
int ni = BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset);
int ni = BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset);
if (ni >= 0 && BLI_table_gset_haskey(pbvh->nodes[ni].bm_other_verts, v)) {
printf("error! %d\n", (int)BLI_table_gset_haskey(pbvh->nodes[ni].bm_unique_verts, v));
BLI_table_gset_remove(pbvh->nodes[ni].bm_other_verts, v, NULL);
}
else if (ni < 0) {
printf("error!\n");
if (ni >= 0 && BLI_table_gset_haskey(pbvh->nodes[ni].bm_other_verts, v)) {
printf("error! %d\n", (int)BLI_table_gset_haskey(pbvh->nodes[ni].bm_unique_verts, v));
BLI_table_gset_remove(pbvh->nodes[ni].bm_other_verts, v, NULL);
}
else if (ni < 0) {
printf("error!\n");
// attempt to recover
BMFace *f;
BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) {
int ni2 = BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset);
if (ni2 != DYNTOPO_NODE_NONE) {
PBVHNode *node2 = pbvh->nodes + ni2;
BLI_table_gset_remove(node2->bm_unique_verts, v, NULL);
BLI_table_gset_remove(node2->bm_other_verts, v, NULL);
}
}
}
BM_log_vert_removed(pbvh->bm_log, v, pbvh->cd_vert_mask_offset);
pbvh_bmesh_vert_remove(pbvh, v);
// attempt to recover
BMFace *f;
BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) {
@ -2725,101 +2830,123 @@ cleanup_valence_3_4(PBVH *pbvh,
if (ni2 != DYNTOPO_NODE_NONE) {
PBVHNode *node2 = pbvh->nodes + ni2;
// 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, true, true);
BLI_table_gset_remove(node2->bm_unique_verts, v, NULL);
BLI_table_gset_remove(node2->bm_other_verts, v, NULL);
}
}
modified = true;
if (!v->e) {
printf("mesh error!\n");
continue;
}
l = v->e->l;
bool flipped = false;
if (val == 4) {
// check which quad diagonal to use to split quad
// try to preserve hard edges
float n1[3], n2[3], th1, th2;
normal_tri_v3(n1, ls[0]->v->co, ls[1]->v->co, ls[2]->v->co);
normal_tri_v3(n2, ls[0]->v->co, ls[2]->v->co, ls[3]->v->co);
th1 = dot_v3v3(n1, n2);
normal_tri_v3(n1, ls[1]->v->co, ls[2]->v->co, ls[3]->v->co);
normal_tri_v3(n2, ls[1]->v->co, ls[3]->v->co, ls[0]->v->co);
th2 = dot_v3v3(n1, n2);
if (th1 > th2) {
flipped = true;
BMLoop *ls2[4] = {ls[0], ls[1], ls[2], ls[3]};
for (int j = 0; j < 4; j++) {
ls[j] = ls2[(j + 1) % 4];
}
}
}
vs[0] = ls[0]->v;
vs[1] = ls[1]->v;
vs[2] = ls[2]->v;
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, true, false);
normal_tri_v3(
f1->no, f1->l_first->v->co, f1->l_first->next->v->co, f1->l_first->prev->v->co);
}
else {
// printf("eek1!\n");
}
if (val == 4 && vs[0] != vs[2] && vs[2] != vs[3] && vs[0] != vs[3]) {
vs[0] = ls[0]->v;
vs[1] = ls[2]->v;
vs[2] = ls[3]->v;
BMFace *example = NULL;
if (v->e && v->e->l) {
example = v->e->l->f;
}
BMFace *f2 = pbvh_bmesh_face_create(pbvh, n, vs, NULL, example, true, false);
CustomData_bmesh_swap_data_simple(
&pbvh->bm->ldata, &f2->l_first->prev->head.data, &ls[3]->head.data);
CustomData_bmesh_copy_data(
&pbvh->bm->ldata, &pbvh->bm->ldata, ls[0]->head.data, &f2->l_first->head.data);
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);
}
if (f1) {
CustomData_bmesh_swap_data_simple(
&pbvh->bm->ldata, &f1->l_first->head.data, &ls[0]->head.data);
CustomData_bmesh_swap_data_simple(
&pbvh->bm->ldata, &f1->l_first->next->head.data, &ls[1]->head.data);
CustomData_bmesh_swap_data_simple(
&pbvh->bm->ldata, &f1->l_first->prev->head.data, &ls[2]->head.data);
BM_log_face_added(pbvh->bm_log, f1);
}
BM_vert_kill(pbvh->bm, v);
}
TGSET_ITER_END
BM_log_vert_removed(pbvh->bm_log, v, pbvh->cd_vert_mask_offset);
pbvh_bmesh_vert_remove(pbvh, v);
BMFace *f;
BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) {
int ni2 = BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset);
if (ni2 != DYNTOPO_NODE_NONE) {
PBVHNode *node2 = pbvh->nodes + ni2;
// 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, true, true);
}
}
modified = true;
if (!v->e) {
printf("mesh error!\n");
continue;
}
l = v->e->l;
bool flipped = false;
if (val == 4) {
// check which quad diagonal to use to split quad
// try to preserve hard edges
float n1[3], n2[3], th1, th2;
normal_tri_v3(n1, ls[0]->v->co, ls[1]->v->co, ls[2]->v->co);
normal_tri_v3(n2, ls[0]->v->co, ls[2]->v->co, ls[3]->v->co);
th1 = dot_v3v3(n1, n2);
normal_tri_v3(n1, ls[1]->v->co, ls[2]->v->co, ls[3]->v->co);
normal_tri_v3(n2, ls[1]->v->co, ls[3]->v->co, ls[0]->v->co);
th2 = dot_v3v3(n1, n2);
if (th1 > th2) {
flipped = true;
BMLoop *ls2[4] = {ls[0], ls[1], ls[2], ls[3]};
for (int j = 0; j < 4; j++) {
ls[j] = ls2[(j + 1) % 4];
}
}
}
vs[0] = ls[0]->v;
vs[1] = ls[1]->v;
vs[2] = ls[2]->v;
BKE_pbvh_bmesh_mark_update_valence(pbvh, (SculptVertRef){.i = (intptr_t)vs[0]});
BKE_pbvh_bmesh_mark_update_valence(pbvh, (SculptVertRef){.i = (intptr_t)vs[1]});
BKE_pbvh_bmesh_mark_update_valence(pbvh, (SculptVertRef){.i = (intptr_t)vs[2]});
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, true, false);
normal_tri_v3(
f1->no, f1->l_first->v->co, f1->l_first->next->v->co, f1->l_first->prev->v->co);
}
else {
// printf("eek1!\n");
}
if (val == 4 && vs[0] != vs[2] && vs[2] != vs[3] && vs[0] != vs[3]) {
vs[0] = ls[0]->v;
vs[1] = ls[2]->v;
vs[2] = ls[3]->v;
BKE_pbvh_bmesh_mark_update_valence(pbvh, (SculptVertRef){.i = (intptr_t)vs[0]});
BKE_pbvh_bmesh_mark_update_valence(pbvh, (SculptVertRef){.i = (intptr_t)vs[1]});
BKE_pbvh_bmesh_mark_update_valence(pbvh, (SculptVertRef){.i = (intptr_t)vs[2]});
BMFace *example = NULL;
if (v->e && v->e->l) {
example = v->e->l->f;
}
BMFace *f2 = pbvh_bmesh_face_create(pbvh, n, vs, NULL, example, true, false);
CustomData_bmesh_swap_data_simple(
&pbvh->bm->ldata, &f2->l_first->prev->head.data, &ls[3]->head.data);
CustomData_bmesh_copy_data(
&pbvh->bm->ldata, &pbvh->bm->ldata, ls[0]->head.data, &f2->l_first->head.data);
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);
}
if (f1) {
CustomData_bmesh_swap_data_simple(
&pbvh->bm->ldata, &f1->l_first->head.data, &ls[0]->head.data);
CustomData_bmesh_swap_data_simple(
&pbvh->bm->ldata, &f1->l_first->next->head.data, &ls[1]->head.data);
CustomData_bmesh_swap_data_simple(
&pbvh->bm->ldata, &f1->l_first->prev->head.data, &ls[2]->head.data);
BM_log_face_added(pbvh->bm_log, f1);
}
BM_vert_kill(pbvh->bm, v);
}
BLI_gset_free(vset, NULL);
@ -2870,22 +2997,25 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
BLI_assert(len_squared_v3(view_normal) != 0.0f);
}
EdgeQueueContext eq_ctx = {
NULL,
NULL,
pbvh->bm,
mask_cb,
mask_cb_data,
EdgeQueueContext eq_ctx = {NULL,
NULL,
pbvh->bm,
mask_cb,
mask_cb_data,
cd_dyn_vert,
cd_vert_mask_offset,
cd_vert_node_offset,
cd_face_node_offset,
.avg_elen = 0.0f,
.max_elen = -1e17,
.min_elen = 1e17,
.totedge = 0.0f,
};
cd_dyn_vert,
cd_vert_mask_offset,
cd_vert_node_offset,
cd_face_node_offset,
.avg_elen = 0.0f,
.max_elen = -1e17,
.min_elen = 1e17,
.totedge = 0.0f,
NULL,
0,
0};
int tempflag = 1 << 15;
#if 1
if (mode & PBVH_Collapse) {
@ -2973,10 +3103,60 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
}
#endif
/* eq_ctx.val34_verts is build in long_edge_queue_create, if it's
disabled we have to build it manually
*/
if ((mode & PBVH_Cleanup) && !(mode & PBVH_Subdivide)) {
EdgeQueue q;
eq_ctx.q = &q;
edge_queue_init(&eq_ctx, use_projected, use_frontface, center, view_normal, radius);
for (int n = 0; n < pbvh->totnode; n++) {
PBVHNode *node = pbvh->nodes + n;
if (!(node->flag & PBVH_Leaf) || !(node->flag & PBVH_UpdateTopology)) {
continue;
}
BMVert *v;
TGSET_ITER (v, node->bm_unique_verts) {
if (!eq_ctx.q->edge_queue_vert_in_range(eq_ctx.q, v)) {
continue;
}
if (use_frontface && dot_v3v3(v->no, view_normal) < 0.0f) {
continue;
}
MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
if (mv->flag & DYNVERT_NEED_VALENCE) {
BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, (SculptVertRef){.i = (intptr_t)v});
}
if (mv->valence < 5) {
edge_queue_insert_val34_vert(&eq_ctx, v);
}
}
TGSET_ITER_END;
}
}
// untag val34 verts
for (int i = 0; i < eq_ctx.val34_verts_tot; i++) {
BMVert *v = eq_ctx.val34_verts[i];
MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
mv->flag &= ~DYNVERT_VALENCE_TEMP;
}
if (mode & PBVH_Cleanup) {
pbvh_bmesh_check_nodes(pbvh);
modified |= cleanup_valence_3_4(
pbvh, center, view_normal, radius, use_frontface, use_projected);
&eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected);
pbvh_bmesh_check_nodes(pbvh);
}
@ -3022,6 +3202,8 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
}
}
MEM_SAFE_FREE(eq_ctx.val34_verts);
BLI_buffer_free(&edge_loops);
BLI_buffer_free(&deleted_faces);
@ -3029,6 +3211,15 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
pbvh_bmesh_verify(pbvh);
#endif
// ensure triangulations are all up to date
for (int i = 0; i < pbvh->totnode; i++) {
PBVHNode *node = pbvh->nodes + i;
if (node->flag & PBVH_Leaf) {
BKE_pbvh_bmesh_check_tris(pbvh, node);
}
}
return modified;
}
@ -3187,6 +3378,9 @@ static void pbvh_split_edges(PBVH *pbvh, BMesh *bm, BMEdge **edges, int totedge)
do {
l2->e->head.hflag &= ~SPLIT_TAG;
l2->v->head.hflag &= ~SPLIT_TAG;
MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l2->v);
mv->flag |= DYNVERT_NEED_VALENCE;
} while ((l2 = l2->next) != l->f->l_first);
l->f->head.hflag &= ~SPLIT_TAG;
@ -3245,8 +3439,10 @@ static void pbvh_split_edges(PBVH *pbvh, BMesh *bm, BMEdge **edges, int totedge)
e->head.hflag &= ~SPLIT_TAG;
BMVert *newv = BM_edge_split(bm, e, e->v1, &newe, 0.5f);
MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, newv);
newv->head.hflag |= SPLIT_TAG;
mv->flag |= DYNVERT_NEED_VALENCE;
BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE);
BM_log_vert_added(pbvh->bm_log, newv, pbvh->cd_vert_mask_offset);

View File

@ -2139,6 +2139,7 @@ static PBVH *build_pbvh_for_dynamic_topology(Object *ob)
ob->sculpt->cd_vert_node_offset,
ob->sculpt->cd_face_node_offset,
ob->sculpt->cd_dyn_vert,
ob->sculpt->cd_face_areas,
ob->sculpt->fast_draw);
pbvh_show_mask_set(pbvh, ob->sculpt->show_mask);
pbvh_show_face_sets_set(pbvh, false);

View File

@ -3866,3 +3866,107 @@ PBVHNode *BKE_pbvh_get_node(PBVH *pbvh, int node)
{
return pbvh->nodes + node;
}
void BKE_pbvh_node_mark_update_tri_area(PBVHNode *node)
{
node->flag |= PBVH_UpdateTriAreas;
}
void BKE_pbvh_update_all_tri_areas(PBVH *pbvh)
{
for (int i = 0; i < pbvh->totnode; i++) {
PBVHNode *node = pbvh->nodes + i;
if (node->flag & PBVH_Leaf) {
node->flag |= PBVH_UpdateTriAreas;
#if 0
// ensure node triangulations are valid
// so we don't end up doing it inside brush threads
BKE_pbvh_bmesh_check_tris(pbvh, node);
#endif
}
}
}
void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node)
{
if (!(node->flag & PBVH_UpdateTriAreas) || !node->tribuf || !node->tribuf->tottri) {
return;
}
node->flag &= ~PBVH_UpdateTriAreas;
if (node->flag & PBVH_UpdateTris) {
BKE_pbvh_bmesh_check_tris(pbvh, node);
}
switch (BKE_pbvh_type(pbvh)) {
case PBVH_BMESH: {
BMFace *f;
const int cd_face_area = pbvh->cd_face_area;
TGSET_ITER (f, node->bm_faces) {
BM_ELEM_CD_SET_FLOAT(f, cd_face_area, 0.0f);
}
TGSET_ITER_END;
for (int i = 0; i < node->tribuf->tottri; i++) {
PBVHTri *tri = node->tribuf->tris + i;
BMVert *v1 = (BMVert *)(node->tribuf->verts[tri->v[0]].i);
BMVert *v2 = (BMVert *)(node->tribuf->verts[tri->v[1]].i);
BMVert *v3 = (BMVert *)(node->tribuf->verts[tri->v[2]].i);
BMFace *f = (BMFace *)tri->f.i;
float area = area_tri_v3(v1->co, v2->co, v3->co);
float farea = BM_ELEM_CD_GET_FLOAT(f, cd_face_area);
BM_ELEM_CD_SET_FLOAT(f, cd_face_area, farea + area);
}
break;
}
default:
break;
}
}
void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, SculptVertRef vertex, float *r_areas, int valence)
{
if (BKE_pbvh_type(pbvh) != PBVH_BMESH) {
// not supported
for (int i = 0; i < valence; i++) {
r_areas[i] = 1.0f;
}
return;
}
BMVert *v = (BMVert *)vertex.i;
BMEdge *e = v->e;
if (!e) {
for (int i = 0; i < valence; i++) {
r_areas[i] = 1.0f;
}
return;
}
const int cd_face_area = pbvh->cd_face_area;
int j = 0;
do {
float w = 0.0f;
if (!e->l) {
w = 0.0f;
}
else {
w += BM_ELEM_CD_GET_FLOAT(e->l->f, cd_face_area) * 0.5f;
w += BM_ELEM_CD_GET_FLOAT(e->l->radial_next->f, cd_face_area) * 0.5f;
}
r_areas[j++] = w;
e = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next;
} while (e != v->e);
}

View File

@ -105,6 +105,15 @@ void pbvh_bmesh_check_nodes(PBVH *pbvh)
if (BLI_table_gset_haskey(node->bm_other_verts, v)) {
printf("vert in node->bm_other_verts");
}
MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
BKE_pbvh_bmesh_check_valence(pbvh, (SculptVertRef){.i = (intptr_t)v});
if (BM_vert_edge_count(v) != mv->valence) {
printf("cached vertex valence mismatch; old: %d, should be: %d\n",
mv->valence,
BM_vert_edge_count(v));
}
}
for (int i = 0; i < pbvh->totnode; i++) {
@ -363,7 +372,6 @@ static void pbvh_bmesh_node_split(
if (n->tribuf || n->tri_buffers) {
BKE_pbvh_bmesh_free_tris(pbvh, n);
n->tribuf = NULL;
}
n->bm_faces = NULL;
@ -372,6 +380,7 @@ static void pbvh_bmesh_node_split(
n->layer_disp = NULL;
pbvh_free_all_draw_buffers(n);
n->flag &= ~PBVH_Leaf;
/* Recurse */
@ -1436,8 +1445,10 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh,
const int cd_vert_node_offset,
const int cd_face_node_offset,
const int cd_dyn_vert,
const int cd_face_areas,
bool fast_draw)
{
pbvh->cd_face_area = cd_face_areas;
pbvh->cd_vert_node_offset = cd_vert_node_offset;
pbvh->cd_face_node_offset = cd_face_node_offset;
pbvh->cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK);
@ -1472,6 +1483,7 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh,
mv->flag = DYNVERT_NEED_DISK_SORT;
bke_pbvh_update_vert_boundary(pbvh->cd_dyn_vert, pbvh->cd_faceset_offset, v);
BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, (SculptVertRef){(intptr_t)v});
copy_v3_v3(mv->origco, v->co);
copy_v3_v3(mv->origno, v->no);
@ -1583,6 +1595,8 @@ bool BKE_pbvh_bmesh_update_topology_nodes(PBVH *pbvh,
}
}
double start = PIL_check_seconds_timer();
modified = modified || BKE_pbvh_bmesh_update_topology(pbvh,
mode,
center,
@ -1595,6 +1609,10 @@ bool BKE_pbvh_bmesh_update_topology_nodes(PBVH *pbvh,
mask_cb,
mask_cb_data);
double end = PIL_check_seconds_timer();
printf("dyntopo time: %f\n", end - start);
return modified;
}
@ -1610,6 +1628,8 @@ static void pbvh_free_tribuf(PBVHTriBuf *tribuf)
tribuf->loops = NULL;
tribuf->edges = NULL;
tribuf->totloop = tribuf->tottri = tribuf->totedge = tribuf->totvert = 0;
tribuf->verts_size = 0;
tribuf->tris_size = 0;
tribuf->edges_size = 0;
@ -1742,12 +1762,7 @@ static bool pbvh_bmesh_split_tris(PBVH *pbvh, PBVHNode *node)
TGSET_ITER_END
if (node->tribuf) {
MEM_SAFE_FREE(node->tribuf->verts);
MEM_SAFE_FREE(node->tribuf->tris);
MEM_SAFE_FREE(node->tribuf->loops);
node->tribuf->tottri = 0;
node->tribuf->tris = NULL;
pbvh_free_tribuf(node->tribuf);
}
else {
node->tribuf = MEM_callocN(sizeof(*node->tribuf), "node->tribuf");
@ -1851,13 +1866,12 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node)
mat_vmaps[i] = NULL;
}
if (node->tribuf) {
pbvh_free_tribuf(node->tribuf);
}
else {
node->tribuf = MEM_callocN(sizeof(*node->tribuf), "node->tribuf");
if (node->tribuf || node->tri_buffers) {
BKE_pbvh_bmesh_free_tris(pbvh, node);
}
node->tribuf = MEM_callocN(sizeof(*node->tribuf), "node->tribuf");
BMLoop **loops = NULL;
uint(*loops_idx)[3] = NULL;
@ -2094,6 +2108,99 @@ static int pbvh_count_subtree_verts(PBVH *pbvh, PBVHNode *n)
return ret;
}
void BKE_pbvh_bmesh_flag_all_disk_sort(PBVH *pbvh)
{
BMVert *v;
BMIter iter;
BM_ITER_MESH (v, &iter, pbvh->bm, BM_VERTS_OF_MESH) {
MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
mv->flag |= DYNVERT_NEED_DISK_SORT;
}
}
void BKE_pbvh_bmesh_update_all_valence(PBVH *pbvh)
{
BMIter iter;
BMVert *v;
BM_ITER_MESH (v, &iter, pbvh->bm, BM_VERTS_OF_MESH) {
BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, (SculptVertRef){(intptr_t)v});
}
}
void BKE_pbvh_bmesh_on_mesh_change(PBVH *pbvh)
{
BMIter iter;
BMVert *v;
for (int i = 0; i < pbvh->totnode; i++) {
PBVHNode *node = pbvh->nodes + i;
if (node->flag & PBVH_Leaf) {
node->flag |= PBVH_UpdateTriAreas;
}
}
const int cd_dyn_vert = pbvh->cd_dyn_vert;
BM_ITER_MESH (v, &iter, pbvh->bm, BM_VERTS_OF_MESH) {
MDynTopoVert *mv = BKE_PBVH_DYNVERT(cd_dyn_vert, v);
mv->flag |= DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_TRIANGULATE;
BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, (SculptVertRef){.i = (intptr_t)v});
}
}
bool BKE_pbvh_bmesh_mark_update_valence(PBVH *pbvh, SculptVertRef vertex)
{
BMVert *v = (BMVert *)vertex.i;
MDynTopoVert *mv = BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_dyn_vert);
bool ret = mv->flag & DYNVERT_NEED_VALENCE;
mv->flag |= DYNVERT_NEED_VALENCE;
return ret;
}
bool BKE_pbvh_bmesh_check_valence(PBVH *pbvh, SculptVertRef vertex)
{
BMVert *v = (BMVert *)vertex.i;
MDynTopoVert *mv = BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_dyn_vert);
if (mv->flag & DYNVERT_NEED_VALENCE) {
BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, vertex);
return true;
}
return false;
}
void BKE_pbvh_bmesh_update_valence(int cd_dyn_vert, SculptVertRef vertex)
{
BMVert *v = (BMVert *)vertex.i;
BMEdge *e;
MDynTopoVert *mv = BM_ELEM_CD_GET_VOID_P(v, cd_dyn_vert);
mv->flag &= ~DYNVERT_NEED_VALENCE;
if (!v->e) {
mv->valence = 0;
return;
}
mv->valence = 0;
e = v->e;
do {
mv->valence++;
e = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next;
} while (e != v->e);
}
static void pbvh_bmesh_join_subnodes(PBVH *pbvh, PBVHNode *node, PBVHNode *parent)
{
if (!(node->flag & PBVH_Leaf)) {
@ -2481,10 +2588,12 @@ struct TableGSet *BKE_pbvh_bmesh_node_faces(PBVHNode *node)
void BKE_pbvh_update_offsets(PBVH *pbvh,
const int cd_vert_node_offset,
const int cd_face_node_offset,
const int cd_dyn_vert)
const int cd_dyn_vert,
const int cd_face_areas)
{
pbvh->cd_face_node_offset = cd_face_node_offset;
pbvh->cd_vert_node_offset = cd_vert_node_offset;
pbvh->cd_face_area = cd_face_areas;
pbvh->cd_vert_mask_offset = CustomData_get_offset(&pbvh->bm->vdata, CD_PAINT_MASK);
pbvh->cd_vcol_offset = CustomData_get_offset(&pbvh->bm->vdata, CD_PROP_COLOR);
pbvh->cd_dyn_vert = cd_dyn_vert;
@ -3907,6 +4016,31 @@ double pbvh_meshtest_smooth_test(MeshTest *m, PBVH *pbvh)
return average / average_tot;
}
/*
test results from blenderartists thread:
random, cluster, percent, data, data_perc, indices, ind_perc, mem (gb)
[1.22, 1.04, 14.42, 0.73, 67, 0.94, 29, 0],
[1.49, 1.46, 2.35, 1.10, 36, 1.17, 27, 0],
[1.29, 1.13, 14, 0.75, 71.54, 0.89, 45.08 , 0],
[1.58, 1.40, 12.3, 1.09, 44.7, 1.11, 42.42, 16],
[1.53, 1.36, 12.77, 1.08, 41.6, 1.07, 42.91, 0],
[1.56, 1.39, 12.47, 1.09, 42.65, 1.10, 42.15, 16],
[1.22, 1.06, 15.05, 0.75, 63.85, 0.82, 49.67, 32]
[random] average: 1.41 variange: 0.15 median: 1.49
[cluster] average: 1.26 variange: 0.17 median: 1.36
[cluster-percent] average: 11.91 variange: 4.02 median: 12.77
[data] average: 0.94 variange: 0.17 median: 1.08
[data-percent] average: 52.48 variange: 13.37 median: 44.70
[indices] average: 1.01 variange: 0.12 median: 1.07
[indices-percent] average: 39.75 variange: 7.82 median: 42.42
So looks like the biggest gain is from replacing pointers with indices
(which lessens total memory bandwidth). The pure data-oriented version
is a tad bit faster then the index-replacement one, but not by that much.
*/
void pbvh_bmesh_cache_test(CacheParams *params, BMesh **r_bm, PBVH **r_pbvh_out)
{
// build mesh
@ -4065,9 +4199,12 @@ void pbvh_bmesh_cache_test(CacheParams *params, BMesh **r_bm, PBVH **r_pbvh_out)
&bm->vdata, CD_PROP_INT32, "__dyntopo_vert_node");
int cd_face_node = CustomData_get_named_layer_index(
&bm->pdata, CD_PROP_INT32, "__dyntopo_face_node");
int cd_face_area = CustomData_get_named_layer_index(
&bm->pdata, CD_PROP_FLOAT, "__dyntopo_face_areas");
cd_vert_node = bm->vdata.layers[cd_vert_node].offset;
cd_face_node = bm->pdata.layers[cd_face_node].offset;
cd_face_area = bm->pdata.layers[cd_face_area].offset;
const int cd_fset = CustomData_get_offset(&bm->pdata, CD_SCULPT_FACE_SETS);
const int cd_dyn_vert = CustomData_get_offset(&bm->vdata, CD_DYNTOPO_VERT);
@ -4082,7 +4219,8 @@ void pbvh_bmesh_cache_test(CacheParams *params, BMesh **r_bm, PBVH **r_pbvh_out)
BM_mesh_elem_table_ensure(bm, BM_VERT | BM_FACE);
BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
BKE_pbvh_build_bmesh(pbvh, bm, false, bmlog, cd_vert_node, cd_face_node, cd_dyn_vert, false);
BKE_pbvh_build_bmesh(
pbvh, bm, false, bmlog, cd_vert_node, cd_face_node, cd_dyn_vert, cd_face_area, false);
int loop_size = sizeof(BMLoop) - sizeof(void *) * 4;

View File

@ -198,6 +198,7 @@ struct PBVH {
int cd_vert_mask_offset;
int cd_vcol_offset;
int cd_faceset_offset;
int cd_face_area;
float planes[6][4];
int num_planes;

View File

@ -1316,6 +1316,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext)
sgcontext->ss->cd_vert_node_offset,
sgcontext->ss->cd_face_node_offset,
sgcontext->ss->cd_dyn_vert,
sgcontext->ss->cd_face_areas,
sgcontext->ss->fast_draw);
}
else { // save result to mesh

View File

@ -9461,9 +9461,8 @@ static int sculpt_spatial_sort_exec(bContext *C, wmOperator *op)
SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_GEOMETRY);
BKE_pbvh_reorder_bmesh(ss->pbvh);
SCULT_dyntopo_flag_all_disk_sort(ss);
BKE_pbvh_recalc_bmesh_boundary(ss->pbvh);
BKE_pbvh_bmesh_on_mesh_change(ss->pbvh);
BM_log_full_mesh(ss->bm, ss->bm_log);
ss->active_vertex_index.i = 0;
@ -11059,12 +11058,36 @@ int SCULPT_vertex_valence_get(struct SculptSession *ss, SculptVertRef vertex)
{
SculptVertexNeighborIter ni;
int tot = 0;
int mval = -1;
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
BMVert *v = (BMVert *)vertex.i;
MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v);
if (mv->flag & DYNVERT_NEED_VALENCE) {
BKE_pbvh_bmesh_update_valence(ss->cd_dyn_vert, vertex);
}
mval = mv->valence;
#ifdef NDEBUG
// return mval;
#endif
}
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
tot++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
#ifdef NDEBUG
if (mval >= 0 && mval != tot) {
printf("Out of date vertex valence detected! old: %d, should be: %d\n", mval, tot);
}
#else
BLI_assert(mval < 0 || mval == tot);
#endif
return tot;
}

View File

@ -117,6 +117,30 @@ static float tri_voronoi_area(float p[3], float q[3], float r[3])
}
}
static float cotangent_tri_weight_v3_proj(const float n[3],
const float v1[3],
const float v2[3],
const float v3[3])
{
float a[3], b[3], c[3], c_len;
sub_v3_v3v3(a, v2, v1);
sub_v3_v3v3(b, v3, v1);
madd_v3_v3fl(a, n, -dot_v3v3(n, a));
madd_v3_v3fl(b, n, -dot_v3v3(n, b));
cross_v3_v3v3(c, a, b);
c_len = len_v3(c);
if (c_len > FLT_EPSILON) {
return dot_v3v3(a, b) / c_len;
}
return 0.0f;
}
void SCULPT_dyntopo_get_cotangents(SculptSession *ss,
SculptVertRef vertex,
float *r_ws,
@ -312,13 +336,7 @@ void SCULPT_get_cotangents(SculptSession *ss,
void SCULT_dyntopo_flag_all_disk_sort(SculptSession *ss)
{
BMVert *v;
BMIter iter;
BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) {
MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v);
mv->flag |= DYNVERT_NEED_DISK_SORT;
}
BKE_pbvh_bmesh_flag_all_disk_sort(ss->pbvh);
}
// returns true if edge disk list around vertex was sorted
@ -504,8 +522,11 @@ void SCULPT_dyntopo_node_layers_update_offsets(SculptSession *ss)
{
SCULPT_dyntopo_node_layers_add(ss);
if (ss->pbvh) {
BKE_pbvh_update_offsets(
ss->pbvh, ss->cd_vert_node_offset, ss->cd_face_node_offset, ss->cd_dyn_vert);
BKE_pbvh_update_offsets(ss->pbvh,
ss->cd_vert_node_offset,
ss->cd_face_node_offset,
ss->cd_dyn_vert,
ss->cd_face_areas);
}
if (ss->bm_log) {
BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert);
@ -542,6 +563,8 @@ int SCULPT_dyntopo_get_templayer(SculptSession *ss, int type, const char *name)
&ss->bm->vdata, type, li - CustomData_get_layer_index(&ss->bm->vdata, type));
}
char dyntopop_faces_areas_layer_id[] = "__dyntopo_face_areas";
void SCULPT_dyntopo_node_layers_add(SculptSession *ss)
{
int cd_node_layer_index, cd_face_node_layer_index;
@ -555,11 +578,11 @@ void SCULPT_dyntopo_node_layers_add(SculptSession *ss)
BM_data_layers_ensure(ss->bm, &ss->bm->vdata, vlayers, 3);
cd_face_node_layer_index = CustomData_get_named_layer_index(
&ss->bm->pdata, CD_PROP_INT32, dyntopop_node_idx_layer_id);
if (cd_face_node_layer_index == -1) {
BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT32, dyntopop_node_idx_layer_id);
}
BMCustomLayerReq flayers[] = {
{CD_PROP_INT32, dyntopop_node_idx_layer_id, CD_FLAG_TEMPORARY},
{CD_PROP_FLOAT, dyntopop_faces_areas_layer_id, CD_FLAG_TEMPORARY},
};
BM_data_layers_ensure(ss->bm, &ss->bm->pdata, flayers, 2);
// get indices again, as they might have changed after adding new layers
cd_node_layer_index = CustomData_get_named_layer_index(
@ -587,6 +610,10 @@ void SCULPT_dyntopo_node_layers_add(SculptSession *ss)
ss->bm->pdata.layers[cd_face_node_layer_index].flag |= CD_FLAG_TEMPORARY;
ss->cd_faceset_offset = CustomData_get_offset(&ss->bm->pdata, CD_SCULPT_FACE_SETS);
ss->cd_face_areas = CustomData_get_named_layer(
&ss->bm->pdata, CD_PROP_FLOAT, dyntopop_faces_areas_layer_id);
ss->cd_face_areas = ss->bm->pdata.layers[ss->cd_face_areas].offset;
}
/**
@ -796,9 +823,10 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene
BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) {
MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v);
mv->flag |= DYNVERT_NEED_DISK_SORT;
mv->flag |= DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_VALENCE;
BKE_pbvh_update_vert_boundary(ss->cd_dyn_vert, ss->cd_faceset_offset, v);
BKE_pbvh_bmesh_update_valence(ss->cd_dyn_vert, (SculptVertRef){.i = (intptr_t)v});
// persistent base
if (cd_pers_co >= 0) {

View File

@ -24,6 +24,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_alloca.h"
#include "BLI_blenlib.h"
#include "BLI_compiler_attrs.h"
#include "BLI_hash.h"
@ -76,7 +77,7 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss,
float projection)
{
float avg[3] = {0.0f, 0.0f, 0.0f};
int total = 0;
float total = 0.0f;
int neighbor_count = 0;
const bool is_boundary = SCULPT_vertex_is_boundary(ss, vertex);
const float *co = SCULPT_vertex_co_get(ss, vertex);
@ -86,13 +87,30 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss,
SCULPT_vertex_normal_get(ss, vertex, no);
}
const bool weighted = ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT;
float *areas;
if (weighted) {
int val = SCULPT_vertex_valence_get(ss, vertex);
areas = BLI_array_alloca(areas, val);
BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, val);
}
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
neighbor_count++;
float tmp[3];
float tmp[3], w;
bool ok = false;
if (weighted) {
w = areas[ni.i];
}
else {
w = 1.0f;
}
if (is_boundary) {
/* Boundary vertices use only other boundary vertices. */
if (SCULPT_vertex_is_boundary(ss, ni.vertex)) {
@ -114,13 +132,13 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss,
sub_v3_v3(tmp, co);
float fac = dot_v3v3(tmp, no);
madd_v3_v3fl(tmp, no, -fac * projection);
add_v3_v3(avg, tmp);
madd_v3_v3fl(avg, tmp, w);
}
else {
add_v3_v3(avg, tmp);
madd_v3_v3fl(avg, tmp, w);
}
total++;
total += w;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@ -131,7 +149,7 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss,
}
/* Avoid division by 0 when there are no neighbors. */
if (total == 0) {
if (total == 0.0f) {
copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex));
return;
}
@ -287,16 +305,34 @@ void SCULPT_neighbor_coords_average(SculptSession *ss,
{
float avg[3] = {0.0f, 0.0f, 0.0f};
float *co, no[3];
int total = 0;
float total = 0.0f;
if (projection > 0.0f) {
co = (float *)SCULPT_vertex_co_get(ss, vertex);
SCULPT_vertex_normal_get(ss, vertex, no);
}
const bool weighted = ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT;
float *areas;
if (weighted) {
int val = SCULPT_vertex_valence_get(ss, vertex);
areas = BLI_array_alloca(areas, val);
BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, val);
}
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
const float *co2 = SCULPT_vertex_co_get(ss, ni.vertex);
float w;
if (weighted) {
w = areas[ni.i];
}
else {
w = 1.0f;
}
if (projection > 0.0f) {
float tmp[3];
@ -305,16 +341,16 @@ void SCULPT_neighbor_coords_average(SculptSession *ss,
float fac = dot_v3v3(tmp, no);
madd_v3_v3fl(tmp, no, -fac * projection);
add_v3_v3(avg, tmp);
madd_v3_v3fl(avg, tmp, w);
}
else {
add_v3_v3(avg, co2);
madd_v3_v3fl(avg, co2, w);
}
total++;
total += w;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
if (total > 0) {
if (total > 0.0f) {
mul_v3_v3fl(result, avg, 1.0f / total);
if (projection > 0.0) {
@ -417,6 +453,11 @@ static void SCULPT_enhance_details_brush(Sculpt *sd,
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
if (SCULPT_stroke_is_first_brush_step(ss->cache) &&
(ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT)) {
BKE_pbvh_update_all_tri_areas(ss->pbvh);
}
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_info_ensure(ob);
@ -528,6 +569,7 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
}
#else
static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
@ -549,6 +591,13 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
const bool weighted = ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT;
if (weighted) {
BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]);
}
bool modified = false;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
@ -580,8 +629,14 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
modified = true;
}
BKE_pbvh_vertex_iter_end;
if (modified && weighted) {
BKE_pbvh_node_mark_update_tri_area(data->nodes[n]);
}
}
#endif
@ -721,6 +776,12 @@ void SCULPT_do_smooth_brush(
Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float projection)
{
SculptSession *ss = ob->sculpt;
if (SCULPT_stroke_is_first_brush_step(ss->cache) &&
(ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT)) {
BKE_pbvh_update_all_tri_areas(ss->pbvh);
}
if (ss->cache->bstrength <= 0.0f) {
/* Invert mode, intensify details. */
SCULPT_enhance_details_brush(sd, ob, nodes, totnode);
@ -801,6 +862,14 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
const bool weighted = ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT;
if (weighted) {
BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]);
}
bool modified = false;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
@ -832,8 +901,14 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex(
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
modified = true;
}
BKE_pbvh_vertex_iter_end;
if (modified && weighted) {
BKE_pbvh_node_mark_update_tri_area(data->nodes[n]);
}
}
static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex(
@ -876,6 +951,11 @@ void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in
Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt;
if (SCULPT_stroke_is_first_brush_step(ss->cache) &&
(ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT)) {
BKE_pbvh_update_all_tri_areas(ss->pbvh);
}
if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
BLI_assert(ss->cache->surface_smooth_laplacian_disp == NULL);
ss->cache->surface_smooth_laplacian_disp = MEM_callocN(

View File

@ -506,6 +506,11 @@ static void bmesh_undo_on_face_add(BMFace *f, void *userdata)
static void bmesh_undo_full_mesh(void *userdata)
{
BmeshUndoData *data = (BmeshUndoData *)userdata;
if (data->pbvh) {
BKE_pbvh_bmesh_update_all_valence(data->pbvh);
}
data->do_full_recalc = true;
}
@ -673,7 +678,10 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode, bool is_
SCULPT_dyntopo_node_layers_add(ss);
SCULPT_dyntopo_node_layers_update_offsets(ss);
SCULT_dyntopo_flag_all_disk_sort(ss);
if (ss->pbvh && ss->bm) {
SCULT_dyntopo_flag_all_disk_sort(ss);
}
if (!ss->bm_log) {
/* Restore the BMLog using saved entries. */

View File

@ -413,6 +413,7 @@ typedef enum eBrushFlags2 {
BRUSH_CUSTOM_AUTOSMOOTH_SPACING = (1 << 10),
BRUSH_CUSTOM_TOPOLOGY_RAKE_SPACING = (1 << 11),
BRUSH_TOPOLOGY_RAKE_IGNORE_BRUSH_FALLOFF = (1 << 12),
BRUSH_SMOOTH_USE_AREA_WEIGHT = (1 << 13)
} eBrushFlags2;
typedef enum {

View File

@ -526,7 +526,7 @@ typedef struct MRecast {
/** \} */
typedef struct MDynTopoVert {
int flag;
short flag, valence;
/**original coordinates*/
float origco[3], origno[3];
@ -549,7 +549,8 @@ enum {
DYNVERT_FSET_BOUNDARY = (1 << 2),
DYNVERT_NEED_BOUNDARY = (1 << 3),
DYNVERT_NEED_TRIANGULATE = (1 << 4),
DYNVERT_NEED_DISK_SORT = (1 << 5)
DYNVERT_NEED_DISK_SORT = (1 << 5),
DYNVERT_NEED_VALENCE = (1 << 6)
};

View File

@ -3289,6 +3289,11 @@ static void rna_def_brush(BlenderRNA *brna)
"Apply the maximum grab strength to the active vertex instead of the cursor location");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "use_weighted_smooth", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_SMOOTH_USE_AREA_WEIGHT);
RNA_def_property_ui_text(prop, "Weight By Area", "Weight by face area to get a smoother result");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "use_grab_silhouette", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_GRAB_SILHOUETTE);
RNA_def_property_ui_text(