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:
parent
9b8c82e2ed
commit
bde54e127e
|
@ -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")
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue