diff options
-rw-r--r-- | source/blender/blenkernel/BKE_pbvh.h | 3 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/brush.c | 12 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/dyntopo.c | 546 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/paint.c | 7 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/pbvh_bmesh.c | 30 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_mempool.h | 11 | ||||
-rw-r--r-- | source/blender/blenlib/intern/BLI_mempool.c | 138 | ||||
-rw-r--r-- | source/blender/bmesh/intern/bmesh_log.c | 6 | ||||
-rw-r--r-- | source/blender/bmesh/intern/bmesh_mesh.c | 456 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 80 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_dyntopo.c | 13 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_face_set.c | 165 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_intern.h | 1 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_smooth.c | 85 |
14 files changed, 1307 insertions, 246 deletions
diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 76adab9168b..afed28d8120 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -33,6 +33,9 @@ extern "C" { #endif +// experimental feature to detect quad diagonals and mark (but not dissolve) them +//#define SCULPT_DIAGONAL_EDGE_MARKS + typedef struct SculptVertRef { intptr_t i; } SculptVertRef; diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index b0870e0558e..956b69d608f 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -1789,6 +1789,8 @@ void BKE_brush_sculpt_reset(Brush *br) break; case SCULPT_TOOL_SMOOTH: br->flag &= ~BRUSH_SPACE_ATTEN; + br->flag2 |= BRUSH_SMOOTH_PRESERVE_FACE_SETS | BRUSH_SMOOTH_USE_AREA_WEIGHT; + br->spacing = 5; br->alpha = 0.7f; br->surface_smooth_shape_preservation = 0.5f; @@ -1801,9 +1803,10 @@ void BKE_brush_sculpt_reset(Brush *br) br->alpha = 1.0f; br->rake_factor = 1.0f; br->dyntopo.inherit = DYNTOPO_INHERIT_BITMASK & - ~(DYNTOPO_INHERIT_ALL | DYNTOPO_LOCAL_COLLAPSE | DYNTOPO_INHERIT_DETAIL_RANGE); + ~(DYNTOPO_INHERIT_ALL | DYNTOPO_LOCAL_COLLAPSE | + DYNTOPO_INHERIT_DETAIL_RANGE); br->dyntopo.flag |= DYNTOPO_LOCAL_COLLAPSE; - br->dyntopo.detail_range = 0.5f; + br->dyntopo.detail_range = 0.4f; break; case SCULPT_TOOL_THUMB: br->size = 75; @@ -1980,13 +1983,14 @@ void BKE_brush_sculpt_reset(Brush *br) // don't use DYNTOPO_INHERIT_BITMASK, we want to include // future bits - br->flag |= BRUSH_SMOOTH_PRESERVE_FACE_SETS | BRUSH_SMOOTH_USE_AREA_WEIGHT | - BRUSH_CURVATURE_RAKE; + br->flag2 |= BRUSH_SMOOTH_PRESERVE_FACE_SETS | BRUSH_SMOOTH_USE_AREA_WEIGHT | + BRUSH_CURVATURE_RAKE; br->dyntopo.inherit = 0x7FFFFFFF & ~(DYNTOPO_INHERIT_ALL | DYNTOPO_SUBDIVIDE | DYNTOPO_COLLAPSE); br->dyntopo.flag |= DYNTOPO_COLLAPSE | DYNTOPO_SUBDIVIDE; br->autosmooth_factor = 0.05; br->topology_rake_factor = 0.35; + br->topology_rake_projection = 0.975; break; case SCULPT_TOOL_VCOL_BOUNDARY: diff --git a/source/blender/blenkernel/intern/dyntopo.c b/source/blender/blenkernel/intern/dyntopo.c index 766a1b95b21..d2324788561 100644 --- a/source/blender/blenkernel/intern/dyntopo.c +++ b/source/blender/blenkernel/intern/dyntopo.c @@ -61,16 +61,18 @@ * (avoids performing subdivisions too far away). */ #define EVEN_GENERATION_SCALE 1.1f -// recursion depth to start applying front face test +/* recursion depth to start applying front face test */ #define DEPTH_START_LIMIT 5 -//#define FANCY_EDGE_WEIGHTS -#define SKINNY_EDGE_FIX +//#define FANCY_EDGE_WEIGHTS <= too slow +//#define SKINNY_EDGE_FIX -// slightly relax geometry by this factor along surface tangents -// to improve convergence of remesher +/* slightly relax geometry by this factor along surface tangents + to improve convergence of remesher */ #define DYNTOPO_SAFE_SMOOTH_FAC 0.05f +#define DYNTOPO_SAFE_SMOOTH_SUBD_ONLY_FAC 0.75f + #ifdef USE_EDGEQUEUE_EVEN_SUBDIV # include "BKE_global.h" #endif @@ -157,8 +159,12 @@ struct EdgeQueueContext; static bool check_face_is_tri(PBVH *pbvh, BMFace *f); static bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v); -static void pbvh_split_edges( - PBVH *pbvh, BMesh *bm, BMEdge **edges, int totedge, bool ignore_isolated_edges); +static void pbvh_split_edges(struct EdgeQueueContext *eq_ctx, + PBVH *pbvh, + BMesh *bm, + BMEdge **edges, + int totedge, + bool ignore_isolated_edges); void bm_log_message(const char *fmt, ...); void pbvh_bmesh_check_nodes_simple(PBVH *pbvh); static void edge_queue_create_local(struct EdgeQueueContext *eq_ctx, @@ -458,7 +464,7 @@ static BMEdge *bmesh_edge_create_log(PBVH *pbvh, BMVert *v1, BMVert *v2, BMEdge return e; } -BLI_INLINE void surface_smooth_v_safe(PBVH *pbvh, BMVert *v) +BLI_INLINE void surface_smooth_v_safe(PBVH *pbvh, BMVert *v, float fac) { float co[3]; float tan[3]; @@ -516,9 +522,9 @@ BLI_INLINE void surface_smooth_v_safe(PBVH *pbvh, BMVert *v) float x = v->co[0], y = v->co[1], z = v->co[2]; // conflicts here should be pretty rare. - atomic_cas_float(&v->co[0], x, x + co[0] * DYNTOPO_SAFE_SMOOTH_FAC); - atomic_cas_float(&v->co[1], y, y + co[1] * DYNTOPO_SAFE_SMOOTH_FAC); - atomic_cas_float(&v->co[2], z, z + co[2] * DYNTOPO_SAFE_SMOOTH_FAC); + atomic_cas_float(&v->co[0], x, x + co[0] * fac); + atomic_cas_float(&v->co[1], y, y + co[1] * fac); + atomic_cas_float(&v->co[2], z, z + co[2] * fac); } static void pbvh_kill_vert(PBVH *pbvh, BMVert *v) @@ -1229,6 +1235,7 @@ typedef struct EdgeQueueContext { int val34_verts_tot; int val34_verts_size; bool local_mode; + float surface_smooth_fac; } EdgeQueueContext; static void edge_queue_insert_val34_vert(EdgeQueueContext *eq_ctx, BMVert *v) @@ -1279,9 +1286,15 @@ BLI_INLINE float calc_weighted_edge_split(EdgeQueueContext *eq_ctx, BMVert *v1, { #ifdef FANCY_EDGE_WEIGHTS float l = len_squared_v3v3(v1->co, v2->co); - float val = (float)BM_vert_edge_count(v1) + (float)BM_vert_edge_count(v2); - val = MAX2(val * 0.5 - 6.0f, 1.0f); - val = powf(val, 0.5); + // float val = (float)BM_vert_edge_count(v1) + (float)BM_vert_edge_count(v2); + MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(eq_ctx->cd_dyn_vert, v1); + MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(eq_ctx->cd_dyn_vert, v2); + float val = (float)(mv1->valence + mv2->valence) * 0.5f; + + val -= 6.0f; + val = MAX2(val, 1.0f); + + // val = powf(val, 0.5); l *= val; return l; @@ -1301,14 +1314,16 @@ BLI_INLINE float calc_weighted_edge_collapse(EdgeQueueContext *eq_ctx, BMVert *v { #ifdef FANCY_EDGE_WEIGHTS float l = len_squared_v3v3(v1->co, v2->co); - float val = (float)BM_vert_edge_count(v1) + (float)BM_vert_edge_count(v2); - val = MAX2(val * 0.5 - 6.0f, 1.0f); - val = powf(val, 0.5); - l /= val; + // float val = (float)BM_vert_edge_count(v1) + (float)BM_vert_edge_count(v2); + MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(eq_ctx->cd_dyn_vert, v1); + MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(eq_ctx->cd_dyn_vert, v2); + float val = (float)(mv1->valence + mv2->valence) * 0.5f; + + val -= 6.0f; + val = MAX2(val, 1.0f); - // if (BM_vert_edge_count(v1) == 4 || BM_vert_edge_count(v2) == 4) { - // l *= 0.25f; - //} + // val = powf(val, 0.5); + l *= val; return l; #else @@ -1548,11 +1563,6 @@ static void long_edge_queue_edge_add_recursive(EdgeQueueContext *eq_ctx, edge_queue_insert(eq_ctx, l_edge->e, -len_sq, eq_ctx->q->limit_len); } - /* temp support previous behavior! */ - if (UNLIKELY(G.debug_value == 1234)) { - return; - } - if ((l_edge->radial_next != l_edge)) { const float len_sq_cmp = len_sq * EVEN_EDGELEN_THRESHOLD; @@ -1678,11 +1688,6 @@ static void short_edge_queue_edge_add_recursive_2(EdgeQueueThreadData *tdata, edge_thread_data_insert(tdata, l_edge->e); - /* temp support previous behavior! */ - if (UNLIKELY(G.debug_value == 1234)) { - return; - } - if ((l_edge->radial_next != l_edge)) { const float len_sq_cmp = len_sq * EVEN_EDGELEN_THRESHOLD; @@ -1716,7 +1721,8 @@ static void long_edge_queue_edge_add_recursive_2(EdgeQueueThreadData *tdata, BMLoop *l_end, const float len_sq, float limit_len, - int depth) + int depth, + bool insert) { BLI_assert(len_sq > square_f(limit_len)); @@ -1732,11 +1738,8 @@ static void long_edge_queue_edge_add_recursive_2(EdgeQueueThreadData *tdata, } #endif - edge_thread_data_insert(tdata, l_edge->e); - - /* temp support previous behavior! */ - if (UNLIKELY(G.debug_value == 1234)) { - return; + if (insert) { + edge_thread_data_insert(tdata, l_edge->e); } if ((l_edge->radial_next != l_edge)) { @@ -1758,14 +1761,24 @@ static void long_edge_queue_edge_add_recursive_2(EdgeQueueThreadData *tdata, len_sq_other *= w * w; - if (len_sq_other > max_ff(len_sq_cmp, limit_len_sq)) { - long_edge_queue_edge_add_recursive_2(tdata, - l_adjacent[i]->radial_next, - l_adjacent[i], - len_sq_other, - limit_len, - depth + 1); + bool insert_ok = len_sq_other > max_ff(len_sq_cmp, limit_len_sq); +#ifdef EVEN_NO_TEST_DEPTH_LIMIT + if (!insert_ok && depth >= EVEN_NO_TEST_DEPTH_LIMIT) { + continue; + } +#else + if (!insert_ok) { + continue; } +#endif + + long_edge_queue_edge_add_recursive_2(tdata, + l_adjacent[i]->radial_next, + l_adjacent[i], + len_sq_other, + limit_len, + depth + 1, + insert_ok); } } while ((l_iter = l_iter->radial_next) != l_end); } @@ -1825,8 +1838,8 @@ static void long_edge_queue_task_cb(void *__restrict userdata, // try to improve convergence by applying a small amount of smoothing to topology, // but tangentially to surface. - if (BLI_rng_get_float(rng) > 0.75) { - surface_smooth_v_safe(tdata->pbvh, l_iter->v); + if (BLI_rng_get_float(rng) > 0.5) { + surface_smooth_v_safe(tdata->pbvh, l_iter->v, eq_ctx->surface_smooth_fac); } #ifdef USE_EDGEQUEUE_EVEN_SUBDIV @@ -1837,7 +1850,7 @@ static void long_edge_queue_task_cb(void *__restrict userdata, if (len_sq > eq_ctx->q->limit_len_squared) { long_edge_queue_edge_add_recursive_2( - tdata, l_iter->radial_next, l_iter, len_sq, eq_ctx->q->limit_len, 0); + tdata, l_iter->radial_next, l_iter, len_sq, eq_ctx->q->limit_len, 0, true); } #else const float len_sq = BM_edge_calc_length_squared(l_iter->e); @@ -2222,10 +2235,6 @@ static void long_edge_queue_create(EdgeQueueContext *eq_ctx, 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; @@ -2243,13 +2252,6 @@ static void long_edge_queue_create(EdgeQueueContext *eq_ctx, 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); @@ -2375,10 +2377,6 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, if (mv->flag & DYNVERT_NEED_VALENCE) { BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, (SculptVertRef){.i = (intptr_t)v}); } - - if (mv->valence == 3 || mv->valence == 4) { - edge_queue_insert_val34_vert(eq_ctx, v); - } } } } @@ -2709,8 +2707,6 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx, 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"); @@ -2965,7 +2961,7 @@ static bool pbvh_bmesh_subdivide_long_edges( #endif #ifdef USE_NEW_SPLIT - pbvh_split_edges(pbvh, pbvh->bm, edges, BLI_array_len(edges), has_cleanup); + pbvh_split_edges(eq_ctx, pbvh, pbvh->bm, edges, BLI_array_len(edges), has_cleanup); BLI_array_free(edges); #endif @@ -3159,7 +3155,6 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh, // pbvh_bmesh_check_nodes_simple(pbvh); -#if 1 BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_del) { BMFace *existing_face; @@ -3187,11 +3182,11 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh, * skip adding this face and mark the existing one for * deletion as well. Prevents extraneous "flaps" from being * created. */ -# if 0 +#if 0 if (UNLIKELY(existing_face = BM_face_exists(v_tri, 3))) -# else +#else if (UNLIKELY(existing_face = bm_face_exists_tri_from_loop_vert(l->next, v_conn))) -# endif +#endif { bool ok2 = true; @@ -3217,14 +3212,14 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh, continue; } -# ifdef CHECKMESH +#ifdef CHECKMESH int m = 0; -# define LTEST1(l, bit) \ - if ((l) != (l)->radial_next && (l) == (l)->radial_next->radial_next && \ - (l)->v == (l)->radial_next->v) { \ - m |= 1 << bit; \ - } +# define LTEST1(l, bit) \ + if ((l) != (l)->radial_next && (l) == (l)->radial_next->radial_next && \ + (l)->v == (l)->radial_next->v) { \ + m |= 1 << bit; \ + } if (l->f->len != 3) { printf("error in %s!!\n", __func__); @@ -3243,7 +3238,7 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh, // SWAP(BMVert *, v_tri[0], v_tri[2]); // SWAP(BMVert *, old_tri[0], old_tri[2]); } -# endif +#endif MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v_tri[1]); MDynTopoVert *mv3 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v_tri[2]); @@ -3293,7 +3288,7 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh, CustomData_bmesh_copy_data( &pbvh->bm->ldata, &pbvh->bm->ldata, l->prev->head.data, &l2->prev->head.data); -# if 0 +#if 0 BMLoop *l3 = f2->l_first; do { if (l3->v == f2->l_first->v && l3->f != f2 && l3->f != l->f) { @@ -3302,11 +3297,10 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh, break; } } while ((l3 = l3->radial_next) != f2->l_first); -# endif +#endif } } BM_LOOPS_OF_VERT_ITER_END; -#endif // pbvh_bmesh_check_nodes_simple(pbvh); @@ -3612,6 +3606,8 @@ cleanup_valence_3_4(EdgeQueueContext *ectx, GSet *vset = BLI_gset_ptr_new("vset"); const int cd_vert_node = pbvh->cd_vert_node_offset; + int updateflag = DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_VALENCE; + 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); @@ -3626,6 +3622,8 @@ cleanup_valence_3_4(EdgeQueueContext *ectx, MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v); + mv->flag &= ~DYNVERT_VALENCE_TEMP; + validate_vert(pbvh->bm, v, false, true); check_vert_fan_are_tris(pbvh, v); validate_vert(pbvh->bm, v, true, true); @@ -3664,6 +3662,17 @@ cleanup_valence_3_4(EdgeQueueContext *ectx, for (int j = 0; j < val; j++) { ls[i++] = l->v == v ? l->next : l; + MDynTopoVert *mv_l; + + if (l->v == v) { + mv_l = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->next->v); + } + else { + mv_l = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->v); + } + + mv_l->flag |= updateflag; + l = l->prev->radial_next; if (l->v != v) { @@ -3789,13 +3798,17 @@ cleanup_valence_3_4(EdgeQueueContext *ectx, validate_vert(pbvh->bm, v, false, false); - 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]}); + MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, vs[0]); + MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, vs[1]); + MDynTopoVert *mv3 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, vs[2]); + + mv1->flag |= updateflag; + mv2->flag |= updateflag; + mv3->flag |= updateflag; BMFace *f1 = NULL; bool ok1 = vs[0] != vs[1] && vs[1] != vs[2] && vs[0] != vs[2]; - // ok1 = ok1 && !BM_face_exists(vs, 3); + ok1 = ok1 && !BM_face_exists(vs, 3); if (ok1) { f1 = pbvh_bmesh_face_create(pbvh, n, vs, NULL, l->f, true, false); @@ -3810,16 +3823,23 @@ cleanup_valence_3_4(EdgeQueueContext *ectx, BMFace *f2 = NULL; - bool ok2 = val == 4 && vs[0] != vs[2] && vs[2] != vs[3] && vs[0] != vs[3]; - // ok2 = ok2 && !BM_face_exists(vs, 3); - if (ok2) { + if (val == 4) { 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]}); + bool ok2 = val == 4 && vs[0] != vs[2] && vs[2] != vs[3] && vs[0] != vs[3]; + ok2 = ok2 && !BM_face_exists(vs, 3); + + if (ok2) { + MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, vs[0]); + MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, vs[1]); + MDynTopoVert *mv3 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, vs[2]); + + mv1->flag |= updateflag; + mv2->flag |= updateflag; + mv3->flag |= updateflag; BMFace *example = NULL; if (v->e && v->e->l) { @@ -3874,6 +3894,128 @@ cleanup_valence_3_4(EdgeQueueContext *ectx, return modified; } +//#define DEFRAGMENT_MEMORY +bool BM_defragment_vertex(BMesh *bm, + BMVert *v, + RNG *rand, + void (*on_vert_swap)(BMVert *a, BMVert *b, void *userdata), + void *userdata); + +typedef struct SwapData { + PBVH *pbvh; +} SwapData; + +static void on_vert_swap(BMVert *v1, BMVert *v2, void *userdata) +{ + SwapData *sdata = (SwapData *)userdata; + PBVH *pbvh = sdata->pbvh; + BMesh *bm = pbvh->bm; + + int ni1 = BM_ELEM_CD_GET_INT(v1, pbvh->cd_vert_node_offset); + int ni2 = BM_ELEM_CD_GET_INT(v2, pbvh->cd_vert_node_offset); + + // check we don't have an orphan vert + PBVHNode *node1 = v1->e && v1->e->l && ni1 >= 0 ? pbvh->nodes + ni1 : NULL; + PBVHNode *node2 = v2->e && v2->e->l && ni2 >= 0 ? pbvh->nodes + ni2 : NULL; + + if ((node1 && !(node1->flag & PBVH_Leaf)) || (node2 && !(node2->flag & PBVH_Leaf))) { + printf("node error! %s\n", __func__); + } + + int updateflag = PBVH_UpdateOtherVerts | PBVH_UpdateNormals | PBVH_UpdateTris | PBVH_UpdateTris; + updateflag |= PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw | + PBVH_RebuildDrawBuffers | PBVH_UpdateVisibility; + updateflag |= PBVH_UpdateDrawBuffers; + + if (node1) { + node1->flag |= updateflag; + BLI_table_gset_remove(node1->bm_unique_verts, v1, NULL); + BLI_table_gset_insert(node1->bm_unique_verts, v2); + } + + if (node2) { + node2->flag |= updateflag; + BLI_table_gset_remove(node2->bm_unique_verts, v2, NULL); + BLI_table_gset_insert(node2->bm_unique_verts, v1); + } + + if (!node1 || !node2) { + // eek! + printf("swap pbvh error! %s %d %d\n", __func__, ni1, ni2); + return; + } +} + +static unsigned int rseed = 0; +static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, + PBVH *pbvh, + const float center[3], + const float view_normal[3], + float radius, + bool use_frontface, + bool use_projected) +{ + EdgeQueue q; + + bool modified = false; + + 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; + BMVert *v; + + if (!(node->flag & PBVH_Leaf) || !(node->flag & PBVH_UpdateTopology)) { + continue; + } + + 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; + } + + BM_log_entry_add_ex(pbvh->bm, pbvh->bm_log, true); + + pbvh_bmesh_check_nodes(pbvh); + + // untag val34 verts + for (int i = 0; i < eq_ctx->val34_verts_tot; i++) { + BMVert *v = eq_ctx->val34_verts[i]; + + if (!v || v->head.htype != BM_VERT || !v->head.data) { + printf("%s error\n", __func__); + continue; + } + MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v); + + mv->flag &= ~DYNVERT_VALENCE_TEMP; + } + + modified |= cleanup_valence_3_4( + eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected); + pbvh_bmesh_check_nodes(pbvh); + + return modified; +} + /* Collapse short edges, subdivide long edges */ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, PBVHTopologyUpdateMode mode, @@ -3887,15 +4029,6 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, DyntopoMaskCB mask_cb, void *mask_cb_data) { - /* - if (sym_axis >= 0 && - PIL_check_seconds_timer() - last_update_time[sym_axis] < DYNTOPO_RUN_INTERVAL) { - return false; - } - - if (sym_axis >= 0) { - last_update_time[sym_axis] = PIL_check_seconds_timer(); - }*/ /* 2 is enough for edge faces - manifold edge */ BLI_buffer_declare_static(BMLoop *, edge_loops, BLI_BUFFER_NOP, 2); @@ -3943,29 +4076,65 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, printf("v: %.2f e: %.2f l: %.2f f: %.2f\n", fvmem, femem, flmem, ffmem); } + + float safe_smooth; + + if ((mode & PBVH_Subdivide) && (!(mode & PBVH_Collapse) || (mode & PBVH_LocalCollapse))) { + safe_smooth = DYNTOPO_SAFE_SMOOTH_SUBD_ONLY_FAC; + } + else { + safe_smooth = DYNTOPO_SAFE_SMOOTH_FAC; + } + #endif - 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, + /* + + +typedef struct EdgeQueueContext { + EdgeQueue *q; + BLI_mempool *pool; + BMesh *bm; + DyntopoMaskCB mask_cb; + void *mask_cb_data; + int cd_dyn_vert; + int cd_vert_mask_offset; + int cd_vert_node_offset; + int cd_face_node_offset; + float avg_elen; + float max_elen; + float min_elen; + float totedge; + BMVert **val34_verts; + int val34_verts_tot; + int val34_verts_size; + bool local_mode; + float surface_smooth_fac; +} EdgeQueueContext; +*/ + EdgeQueueContext eq_ctx = {.q = NULL, + .pool = NULL, + .bm = pbvh->bm, + .mask_cb = mask_cb, + .mask_cb_data = mask_cb_data, + + .cd_dyn_vert = cd_dyn_vert, + .cd_vert_mask_offset = cd_vert_mask_offset, + .cd_vert_node_offset = cd_vert_node_offset, + .cd_face_node_offset = cd_face_node_offset, .avg_elen = 0.0f, .max_elen = -1e17, .min_elen = 1e17, .totedge = 0.0f, - NULL, - 0, - 0, - false}; + .val34_verts = NULL, + .val34_verts_tot = 0, + .val34_verts_size = 0, + .local_mode = false, + .surface_smooth_fac = safe_smooth}; int tempflag = 1 << 15; #if 1 + if (mode & PBVH_Collapse) { BM_log_entry_add_ex(pbvh->bm, pbvh->bm_log, true); @@ -4002,6 +4171,8 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, ratio = MIN2(ratio, 5.0f); } } +# else + ratio = 1.0f; # endif float brusharea = radius / (pbvh->bm_min_edge_len * 0.5f + pbvh->bm_max_edge_len * 0.5f); @@ -4022,6 +4193,12 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, BLI_mempool_destroy(queue_pool); } + // if no collapse, run cleanup here to avoid degenerate geometry + if ((mode & PBVH_Cleanup) && !(mode & PBVH_Collapse)) { + modified |= do_cleanup_3_4( + &eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected); + } + if (mode & PBVH_Subdivide) { BM_log_entry_add_ex(pbvh->bm, pbvh->bm_log, true); @@ -4040,8 +4217,9 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, use_projected, mode & PBVH_LocalSubdivide); -# if 0 /// def SKINNY_EDGE_FIX - // prevent remesher thrashing by throttling edge splitting in pathological case of skinny edges +# ifdef SKINNY_EDGE_FIX + // prevent remesher thrashing by throttling edge splitting in pathological case of skinny + // edges float avg_elen = eq_ctx.avg_elen; if (eq_ctx.totedge > 0.0f) { avg_elen /= eq_ctx.totedge; @@ -4069,7 +4247,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, brusharea = brusharea * brusharea * M_PI; int max_steps = (int)((float)DYNTOPO_MAX_ITER * ratio); - max_steps = (int)(brusharea * ratio * 1.0f); + max_steps = (int)(brusharea * ratio * 2.0f); printf("brusharea: %.2f, ratio: %.2f\n", brusharea, ratio); printf("subdivide max_steps %d\n", max_steps); @@ -4088,63 +4266,48 @@ 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; + // if have collapse, run cleanup for nicer geometry more compatible with topology rake + if ((mode & PBVH_Cleanup) && (mode & PBVH_Collapse)) { + modified |= do_cleanup_3_4( + &eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected); + } - eq_ctx.q = &q; - edge_queue_init(&eq_ctx, use_projected, use_frontface, center, view_normal, radius); + int dcount = 0; - for (int n = 0; n < pbvh->totnode; n++) { - PBVHNode *node = pbvh->nodes + n; +//#define DEFRAGMENT_MEMORY +#ifdef DEFRAGMENT_MEMORY + RNG *rng = BLI_rng_new(rseed++); + SwapData swapdata = {.pbvh = pbvh}; + for (int n = 0; n < pbvh->totnode; n++) { + PBVHNode *node = pbvh->nodes + n; - if (!(node->flag & PBVH_Leaf) || !(node->flag & PBVH_UpdateTopology)) { - continue; - } + 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; - } + BMVert *v; - if (use_frontface && dot_v3v3(v->no, view_normal) < 0.0f) { - continue; - } + float radius_sqr = radius * radius; - MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v); + TGSET_ITER (v, node->bm_unique_verts) { + if (len_squared_v3v3(v->co, center) > radius_sqr) { + continue; + } - if (mv->flag & DYNVERT_NEED_VALENCE) { - BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, (SculptVertRef){.i = (intptr_t)v}); - } + if (BM_defragment_vertex(pbvh->bm, v, rng, on_vert_swap, &swapdata)) { + modified = true; + dcount++; + } - if (mv->valence < 5) { - edge_queue_insert_val34_vert(&eq_ctx, v); - } + if (dcount > 100) { + // break; } - TGSET_ITER_END; } + 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) { - BM_log_entry_add_ex(pbvh->bm, pbvh->bm_log, true); - - pbvh_bmesh_check_nodes(pbvh); - - modified |= cleanup_valence_3_4( - &eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected); - pbvh_bmesh_check_nodes(pbvh); - } + BLI_rng_free(rng); +#endif if (modified) { @@ -4202,7 +4365,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, PBVHNode *node = pbvh->nodes + i; if (node->flag & PBVH_Leaf) { - BKE_pbvh_bmesh_check_tris(pbvh, node); + // BKE_pbvh_bmesh_check_tris(pbvh, node); } } @@ -4325,14 +4488,58 @@ static const int splitmap[43][16] = { {6, -1, 3, -1, 5, -1, 1, -1}, // 42 }; -static void pbvh_split_edges( - PBVH *pbvh, BMesh *bm, BMEdge **edges, int totedge, bool ignore_isolated_edges) +static void pbvh_split_edges(EdgeQueueContext *eq_ctx, + PBVH *pbvh, + BMesh *bm, + BMEdge **edges1, + int totedge, + bool ignore_isolated_edges) { + BMEdge **edges = edges1; BMFace **faces = NULL; BLI_array_staticdeclare(faces, 512); bm_log_message(" == split edges == "); +//# define EXPAND_SPLIT_REGION +# ifdef EXPAND_SPLIT_REGION + BLI_array_declare(edges); + + edges = MEM_callocN(sizeof(void *) * totedge, "edges copy"); + memcpy(edges, edges1, sizeof(void *) * totedge); + + BLI_array_len_set(edges, totedge); + + GSet *visit = BLI_gset_ptr_new("visit"); + for (int i = 0; i < totedge; i++) { + BLI_gset_add(visit, edges[i]); + } + + for (int i = 0; i < totedge; i++) { + BMEdge *e = edges[i]; + BMLoop *l = e->l; + + if (!l) { + continue; + } + + do { + BMLoop *l2 = l->f->l_first; + do { + if (!BLI_gset_haskey(visit, l2->e)) { + // BLI_gset() + BLI_gset_add(visit, l2->e); + l2->e->head.hflag |= SPLIT_TAG; + BLI_array_append(edges, l2->e); + } + } while ((l2 = l2->next) != l->f->l_first); + } while ((l = l->radial_next) != e->l); + } + + BLI_gset_free(visit, NULL); + totedge = BLI_array_len(edges); +# endif + const int node_updateflag = PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateNormals | PBVH_UpdateOtherVerts | PBVH_UpdateCurvatureDir | PBVH_UpdateTriAreas | PBVH_UpdateDrawBuffers | @@ -4510,7 +4717,9 @@ static void pbvh_split_edges( mv->flag |= DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT; mv->stroke_id = pbvh->stroke_id; -# if 1 + mv->flag |= DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY; + mv->flag &= ~DYNVERT_VALENCE_TEMP; + BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); int ni = BM_ELEM_CD_GET_INT(v1, pbvh->cd_vert_node_offset); @@ -4578,10 +4787,6 @@ static void pbvh_split_edges( BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); printf("eek!"); } - -# else - BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); -# endif } bm_log_message(" == split edges (triangulate) == "); @@ -4721,5 +4926,10 @@ static void pbvh_split_edges( } BLI_array_free(faces); +# ifdef EXPAND_SPLIT_REGION + if (edges != edges1) { + BLI_array_free(edges); + } +# endif } #endif diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 078c087a1b5..7d9e3fc4c74 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -1948,9 +1948,12 @@ void BKE_sculpt_toolsettings_data_ensure(struct Scene *scene) sd->detail_size = 12; } + if (!sd->detail_range || !sd->dyntopo_spacing) { + sd->flags |= SCULPT_DYNTOPO_CLEANUP; // should really do this in do_versions_290.c + } + if (!sd->detail_range) { sd->detail_range = 0.4f; - sd->flags |= SCULPT_DYNTOPO_CLEANUP; // should really do this in do_versions_290.c } if (!sd->detail_percent) { @@ -1958,7 +1961,7 @@ void BKE_sculpt_toolsettings_data_ensure(struct Scene *scene) } if (!sd->dyntopo_spacing) { - sd->dyntopo_spacing = 25; + sd->dyntopo_spacing = 35; } if (sd->constant_detail == 0.0f) { diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c index cb580ff4606..11f9ae7a616 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.c +++ b/source/blender/blenkernel/intern/pbvh_bmesh.c @@ -2193,9 +2193,21 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) continue; } +#ifdef SCULPT_DIAGONAL_EDGE_MARKS + int ecount = 0; +#endif + // clear edgeflag for building edge indices later BMLoop *l = f->l_first; do { +#ifdef SCULPT_DIAGONAL_EDGE_MARKS + BMEdge *e2 = l->v->e; + do { + if (e2->head.hflag & BM_ELEM_DRAW) { + ecount++; + } + } while ((e2 = BM_DISK_EDGE_NEXT(e2, l->v)) != l->v->e); +#endif l->e->head.hflag &= ~edgeflag; } while ((l = l->next) != f->l_first); @@ -2235,8 +2247,13 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) BMLoop *l2 = loops[loops_idx[i][(j + 1) % 3]]; void **val = NULL; + BMEdge *e = BM_edge_exists(l->v, l2->v); - if (BM_edge_exists(l->v, l2->v)) { +# ifdef SCULPT_DIAGONAL_EDGE_MARKS + if (e && (e->head.hflag & BM_ELEM_DRAW)) { +# else + if (e) { +# endif tri->eflag |= 1 << j; mat_tri->eflag |= 1 << j; } @@ -3609,20 +3626,27 @@ BMesh *BKE_pbvh_reorder_bmesh(PBVH *pbvh) v = node2->verts[j]; #if 0 + const int cd_vcol = CustomData_get_offset(&pbvh->bm->vdata, CD_PROP_COLOR); + if (cd_vcol >= 0) { MPropCol *col = BM_ELEM_CD_GET_VOID_P(node2->verts[j], cd_vcol); float r = 0.0f, g = 0.0f, b = 0.0f; + ReVertNode *parent = node2->parent; + for (int j = 0; parent->parent && j < 2; j++) { + parent = parent->parent; + } + unsigned int p = (unsigned int)node2->parent; p = p % 65535; - unsigned int p2 = (unsigned int)node2->parent; + unsigned int p2 = (unsigned int)parent; p2 = p2 % 65535; r = ((float)vorder) * 0.01; g = ((float)p2) / 65535.0f; - b = ((float)p) / 65535.0f; + b = ((float)p2) / 65535.0f; r = cosf(r * 17.2343) * 0.5 + 0.5; g = cosf(g * 11.2343) * 0.5 + 0.5; diff --git a/source/blender/blenlib/BLI_mempool.h b/source/blender/blenlib/BLI_mempool.h index b8e1a22b875..4093b9b5fe0 100644 --- a/source/blender/blenlib/BLI_mempool.h +++ b/source/blender/blenlib/BLI_mempool.h @@ -87,6 +87,10 @@ enum { * order of allocation when no chunks have been freed. */ BLI_MEMPOOL_ALLOW_ITER = (1 << 0), + + /* allow random access, implies BLI_MEMPOOL_ALLOW_ITER since we + need the freewords to detect free state of elements*/ + BLI_MEMPOOL_RANDOM_ACCESS = (1 << 1) | (1 << 0) }; void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter) ATTR_NONNULL(); @@ -105,6 +109,13 @@ BLI_mempool *BLI_mempool_create_for_tasks(const unsigned int esize, int *r_esize, int flag); +// memory coherence stuff +int BLI_mempool_find_elems_fuzzy( + BLI_mempool *pool, int idx, int range, void **r_elems, int r_elems_size); + +int BLI_mempool_get_size(BLI_mempool *pool); +int BLI_mempool_find_real_index(BLI_mempool *pool, void *ptr); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c index 65ebf38debf..a8e7a1e0ec2 100644 --- a/source/blender/blenlib/intern/BLI_mempool.c +++ b/source/blender/blenlib/intern/BLI_mempool.c @@ -124,6 +124,12 @@ struct BLI_mempool { * this is needed for iteration so we can loop over chunks in the order added. */ BLI_mempool_chunk *chunk_tail; + /* only used if BLI_MEMPOOL_RANDOM_ACCESS is true*/ + BLI_mempool_chunk **chunktable; + + /* only used if BLI_MEMPOOL_RANDOM_ACCESS is true*/ + int totchunk; + /** Element size in bytes. */ uint esize; /** Chunk size in bytes. */ @@ -168,6 +174,33 @@ static uint power_of_2_max_u(uint x) } #endif +static void mempool_update_chunktable(BLI_mempool *pool) +{ + if (!(pool->flag & BLI_MEMPOOL_RANDOM_ACCESS)) { + return; + } + + pool->totchunk = 0; + BLI_mempool_chunk *chunk = pool->chunks; + + while (chunk) { + pool->totchunk++; + chunk = chunk->next; + } + + MEM_SAFE_FREE(pool->chunktable); + pool->chunktable = (BLI_mempool_chunk **)MEM_mallocN( + sizeof(pool->chunktable) * (size_t)pool->totchunk, "mempool chunktable"); + + int i = 0; + chunk = pool->chunks; + + while (chunk) { + pool->chunktable[i++] = chunk; + chunk = chunk->next; + } +} + BLI_INLINE BLI_mempool_chunk *mempool_chunk_find(BLI_mempool_chunk *head, uint index) { while (index-- && head) { @@ -209,6 +242,26 @@ static BLI_freenode *mempool_chunk_add(BLI_mempool *pool, BLI_freenode *curnode = CHUNK_DATA(mpchunk); uint j; + if (pool->flag & BLI_MEMPOOL_RANDOM_ACCESS) { + if (!pool->chunktable || + MEM_allocN_len(pool->chunktable) / sizeof(void *) <= (size_t)pool->totchunk) { + void *old = pool->chunktable; + + size_t size = (size_t)pool->totchunk + 2ULL; + size += size >> 1ULL; + + pool->chunktable = MEM_mallocN(sizeof(void *) * size, "mempool chunktable"); + + if (old) { + memcpy(pool->chunktable, old, sizeof(void *) * (size_t)pool->totchunk); + } + + MEM_SAFE_FREE(old); + } + + pool->chunktable[pool->totchunk++] = mpchunk; + } + /* append */ if (pool->chunk_tail) { pool->chunk_tail->next = mpchunk; @@ -406,6 +459,9 @@ BLI_mempool *BLI_mempool_create(uint esize, uint totelem, uint pchunk, uint flag /* allocate the pool structure */ pool = MEM_mallocN(sizeof(BLI_mempool), "memory pool"); + pool->totchunk = 0; + pool->chunktable = NULL; + /* set the elem size */ if (esize < (int)MEMPOOL_ELEM_SIZE_MIN) { esize = (int)MEMPOOL_ELEM_SIZE_MIN; @@ -502,6 +558,84 @@ void *BLI_mempool_calloc(BLI_mempool *pool) return retval; } +int BLI_mempool_find_real_index(BLI_mempool *pool, void *ptr) +{ + BLI_mempool_chunk *chunk = pool->chunks; + uintptr_t uptr = ptr; + uintptr_t cptr; + int chunki = 0; + + while (chunk) { + cptr = (uintptr_t)chunk; + + if (uptr >= cptr && uptr < cptr + pool->csize) { + break; + } + + chunk = chunk->next; + chunki++; + } + + if (!chunk) { + return -1; // failed + } + + return chunki * (int)pool->pchunk + ((int)(uptr - cptr)) / (int)pool->esize; +} + +/*finds an element in pool that's roughly at idx, idx*/ +int BLI_mempool_find_elems_fuzzy( + BLI_mempool *pool, int idx, int range, void **r_elems, int r_elems_size) +{ + int istart = idx - range, iend = idx + range; + istart = MAX2(istart, 0); + + int totelem = 0; + + for (int i = istart; i < iend; i++) { + int chunki = i / (int)pool->pchunk; + if (chunki >= (int)pool->totchunk) { + break; + } + + int idx2 = i % (int)pool->pchunk; + + BLI_mempool_chunk *chunk = pool->chunktable[chunki]; + char *data = (char *)CHUNK_DATA(chunk); + void *ptr = data + idx2 * (int)pool->esize; + + BLI_asan_unpoison(ptr, pool->esize); + + BLI_freenode *fnode = (BLI_freenode *)ptr; + if (fnode->freeword == FREEWORD) { + BLI_asan_poison(ptr, pool->esize); + continue; + } + + r_elems[totelem++] = ptr; + + if (totelem == r_elems_size) { + break; + } + } + + return totelem; +} + +int BLI_mempool_get_size(BLI_mempool *pool) +{ + BLI_mempool_chunk *chunk = pool->chunks; + int ret = 0; + + while (chunk) { + chunk = chunk->next; + + ret += pool->pchunk; + } + + return ret; +} + /** * Free an element from the mempool. * @@ -563,6 +697,8 @@ void BLI_mempool_free(BLI_mempool *pool, void *addr) first->next = NULL; pool->chunk_tail = first; + mempool_update_chunktable(pool); + #ifdef USE_TOTALLOC pool->totalloc = pool->pchunk; #endif @@ -962,6 +1098,8 @@ void BLI_mempool_destroy(BLI_mempool *pool) { mempool_chunk_free_all(pool->chunks, pool); + MEM_SAFE_FREE(pool->chunktable); + #ifdef WITH_MEM_VALGRIND VALGRIND_DESTROY_MEMPOOL(pool); #endif diff --git a/source/blender/bmesh/intern/bmesh_log.c b/source/blender/bmesh/intern/bmesh_log.c index a0478010ceb..0458ca5f386 100644 --- a/source/blender/bmesh/intern/bmesh_log.c +++ b/source/blender/bmesh/intern/bmesh_log.c @@ -1764,11 +1764,15 @@ static void log_idmap_save(BMesh *bm, BMLog *log, BMLogEntry *entry) int cd_loop_off = cd_id_offs[2]; int *lmap = idmap->maps[2]; + bool reported = false; + BM_ITER_MESH_INDEX (elem, &iter, bm, iters[i], j) { int id = BM_ELEM_CD_GET_INT(elem, cd_off); - if ((BMElem *)BM_ELEM_FROM_ID(bm, id) != elem) { + if (!reported && (BMElem *)BM_ELEM_FROM_ID(bm, id) != elem) { printf("IDMap error for elem type %d\n", elem->head.htype); + printf(" further errors suppressed\n"); + reported = true; } map[j] = id; diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c index 9169c8fe57d..8220f741fda 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.c +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -26,8 +26,10 @@ #include "DNA_scene_types.h" #include "BLI_alloca.h" +#include "BLI_array.h" #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_rand.h" #include "BLI_utildefines.h" #include "BKE_customdata.h" @@ -140,6 +142,23 @@ void BM_mesh_elem_toolflags_clear(BMesh *bm) } } +// int cdmap[8] = {0, 1, -1, -1, 2, -1, -1, -1, 3}; + +static void bm_swap_cd_data(int htype, BMesh *bm, CustomData *cd, void *a, void *b) +{ + int tot = cd->totsize; + // int cd_id = bm->idmap.cd_id_off[htype]; + + char *sa = (char *)a; + char *sb = (char *)b; + + for (int i = 0; i < tot; i++, sa++, sb++) { + char tmp = *sa; + *sa = *sb; + *sb = tmp; + } +} + /** * \brief BMesh Make Mesh * @@ -881,12 +900,6 @@ int BM_mesh_elem_count(BMesh *bm, const char htype) } } -static void swap_block(void *tmp, void *a, void *b, int size) -{ - memcpy(tmp, b, size); - memcpy(b, a, size); - memcpy(a, tmp, size); -} /** * Remaps the vertices, edges and/or faces of the bmesh as indicated by vert/edge/face_idx arrays * (xxx_idx[org_index] = new_index). @@ -923,25 +936,18 @@ void BM_mesh_remap(BMesh *bm, bm, (vert_idx ? BM_VERT : 0) | (edge_idx ? BM_EDGE : 0) | (face_idx ? BM_FACE : 0)); CustomData *cdatas[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; - void *swap_temps[4]; - for (int i = 0; i < 4; i++) { - if (cdatas[i]->totsize) { - swap_temps[i] = MEM_mallocN(cdatas[i]->totsize, "cdata temp"); - } - else { - swap_temps[i] = NULL; - } - } +#define DO_SWAP(ci, cdata, v, vp) *(v) = *(vp); -#define DO_SWAP(ci, cdata, v, vp) \ - void *cdold = v->head.data; \ +// NOT WORKING +/* unswaps customdata blocks*/ +#define DO_SWAP2(ci, cdata, v, vp) \ + void *cdold = (v)->head.data; \ void *cdnew = (vp)->head.data; \ - *v = *(vp); \ - /* swap customdata blocks*/ \ + *(v) = *(vp); \ if (cdold) { \ - v->head.data = cdold; \ - swap_block(swap_temps[ci], cdold, cdnew, bm->cdata.totsize); \ + (v)->head.data = cdold; \ + memcpy(cdold, cdnew, bm->cdata.totsize); \ } /* Remap Verts */ @@ -981,11 +987,12 @@ void BM_mesh_remap(BMesh *bm, DO_SWAP(0, vdata, new_vep, ve); + BLI_ghash_insert(vptr_map, *vep, new_vep); #if 0 printf( "mapping vert from %d to %d (%p/%p to %p)\n", i, *new_idx, *vep, verts_pool[i], new_vep); #endif - BLI_ghash_insert(vptr_map, *vep, new_vep); + if (cd_vert_pyptr != -1) { void **pyptr = BM_ELEM_CD_GET_VOID_P(((BMElem *)new_vep), cd_vert_pyptr); *pyptr = pyptrs[*new_idx]; @@ -1373,10 +1380,6 @@ void BM_mesh_remap(BMesh *bm, } } } - - for (int i = 0; i < 4; i++) { - MEM_SAFE_FREE(swap_temps[i]); - } } /** @@ -1719,4 +1722,405 @@ void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm, } } +void bm_swap_ids(BMesh *bm, BMElem *e1, BMElem *e2) +{ + int cd_id = bm->idmap.cd_id_off[e1->head.htype]; + + if (cd_id < 0) { + return; + } + + int id1 = BM_ELEM_CD_GET_INT(e1, cd_id); + int id2 = BM_ELEM_CD_GET_INT(e2, cd_id); + + if (bm->idmap.map) { + SWAP(BMElem *, bm->idmap.map[id1], bm->idmap.map[id2]); + } + else if (bm->idmap.ghash) { + void **val1, **val2; + + BLI_ghash_ensure_p(bm->idmap.ghash, POINTER_FROM_INT(id1), &val1); + BLI_ghash_ensure_p(bm->idmap.ghash, POINTER_FROM_INT(id2), &val2); + + *val1 = (void *)e2; + *val2 = (void *)e1; + } +} +static void bm_swap_elements_post(BMesh *bm, CustomData *cdata, BMElem *e1, BMElem *e2) +{ + // unswap customdata pointers + SWAP(void *, e1->head.data, e2->head.data); + + // swap contents of customdata instead + bm_swap_cd_data(e1->head.htype, bm, cdata, e1->head.data, e2->head.data); + + // unswap index + SWAP(int, e1->head.index, e2->head.index); + + bm_swap_ids(bm, e1, e2); +} + +void BM_swap_verts(BMesh *bm, BMVert *v1, BMVert *v2) +{ + if (v1 == v2) { + return; + } + + BMLoop **ls1 = NULL; + BLI_array_staticdeclare(ls1, 64); + BMLoop **ls2 = NULL; + BLI_array_staticdeclare(ls2, 64); + + BMEdge **es1 = NULL; + int *sides1 = NULL; + + BLI_array_staticdeclare(es1, 32); + BLI_array_staticdeclare(sides1, 32); + + BMEdge **es2 = NULL; + int *sides2 = NULL; + + BLI_array_staticdeclare(es2, 32); + BLI_array_staticdeclare(sides2, 32); + + for (int i = 0; i < 2; i++) { + BMVert *v = i ? v2 : v1; + BMVert *v_2 = i ? v1 : v2; + + BMEdge *e = v->e, *starte = e; + + if (!e) { + continue; + } + + // int count = 0; + + do { + // if (count++ > 10000) { + // printf("error!\n"); + // break; + // } + + int side = 0; + if (e->v1 == v) { + side |= 1; + } + + if (e->v2 == v) { + side |= 2; + } + + if (i) { + BLI_array_append(es2, e); + BLI_array_append(sides2, side); + } + else { + BLI_array_append(es1, e); + BLI_array_append(sides1, side); + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != starte); + } + + for (int i = 0; i < 2; i++) { + BMVert *v = i ? v2 : v1; + BMVert *v_2 = i ? v1 : v2; + + BMEdge **es = i ? es2 : es1; + BMLoop **ls = i ? ls2 : ls1; + int elen = i ? BLI_array_len(es2) : BLI_array_len(es1); + int *sides = i ? sides2 : sides1; + + for (int j = 0; j < elen; j++) { + BMEdge *e = es[j]; + int side = sides[j]; + + // if (side == 3) { + // printf("edge had duplicate verts!\n"); + //} + + if (side & 1) { + e->v1 = v_2; + } + + if (side & 2) { + e->v2 = v_2; + } + +#if 1 + BMLoop *l = e->l; + if (l) { + + do { + BMLoop *l2 = l; + + do { + if (l2->v == v) { + if (i) { + BLI_array_append(ls2, l2); + } + else { + BLI_array_append(ls1, l2); + } + } + } while ((l2 = l2->next) != l); + } while ((l = l->radial_next) != e->l); + } +#endif + // e = enext; + } // while (e != starte); + } + + for (int i = 0; i < 2; i++) { + BMVert *v = i ? v2 : v1; + BMVert *v_2 = i ? v1 : v2; + + BMLoop **ls = i ? ls2 : ls1; + + int llen = i ? BLI_array_len(ls2) : BLI_array_len(ls1); + + for (int j = 0; j < llen; j++) { + ls[j]->v = v_2; + } + } + + BLI_array_free(ls1); + BLI_array_free(ls2); + // BMVert tmp = *v1; + //*v1 = *v2; + //*v2 = tmp; + + SWAP(BMVert, (*v1), (*v2)); + // swap contents of customdata, don't swap pointers + bm_swap_elements_post(bm, &bm->vdata, (BMElem *)v1, (BMElem *)v2); + + bm->elem_table_dirty |= BM_VERT; + bm->elem_index_dirty |= BM_VERT; + + BLI_array_free(es1); + BLI_array_free(sides1); + BLI_array_free(es2); + BLI_array_free(sides2); +} + +void BM_swap_edges(BMesh *bm, BMEdge *e1, BMEdge *e2) +{ + for (int i = 0; i < 2; i++) { + BMEdge *e = i ? e2 : e1; + BMEdge *e_2 = i ? e1 : e2; + + for (int j = 0; j < 2; j++) { + BMVert *v = j ? e->v2 : e->v1; + + if (v->e == e) { + v->e = e_2; + } + } + + BMLoop *l = e->l; + if (l) { + do { + l->e = e_2; + } while ((l = l->radial_next) != e->l); + } + } + + SWAP(BMEdge, *e1, *e2); + // swap contents of customdata, don't swap pointers + bm_swap_elements_post(bm, &bm->edata, (BMElem *)e1, (BMElem *)e2); +} + +void BM_swap_loops(BMesh *bm, BMLoop *l1, BMLoop *l2) +{ + for (int i = 0; i < 2; i++) { + BMLoop *l = i ? l2 : l1; + BMLoop *l_2 = i ? l1 : l2; + + l->prev->next = l2; + l->next->prev = l2; + + if (l != l->radial_next) { + l->radial_next->radial_prev = l2; + l->radial_prev->radial_next = l2; + } + + if (l == l->e->l) { + l->e->l = l2; + } + + if (l == l->f->l_first) { + l->f->l_first = l2; + } + } + + // swap contents of customdata, don't swap pointers + SWAP(BMLoop, *l1, *l2); + // swap contents of customdata, don't swap pointers + bm_swap_elements_post(bm, &bm->ldata, (BMElem *)l1, (BMElem *)l2); +} + +// memory coherence defragmentation + +#ifndef ABSLL +# define ABSLL(a) ((a) < 0LL ? -(a) : (a)) +#endif + +#define DEFRAG_FLAG BM_ELEM_TAG_ALT + +bool BM_defragment_vertex(BMesh *bm, + BMVert *v, + RNG *rand, + void (*on_vert_swap)(BMVert *a, BMVert *b, void *userdata), + void *userdata) +{ + BMEdge *e = v->e; + + int cd_vcol = CustomData_get_offset(&bm->vdata, CD_PROP_COLOR); + +#if 0 + if (cd_vcol >= 0) { + float *color = BM_ELEM_CD_GET_VOID_P(v, cd_vcol); + int idx = BLI_mempool_find_real_index(bm->vpool, (void *)v); + int size = BLI_mempool_get_size(bm->vpool); + + float f = (float)idx / (float)size / 2.0f; + + color[0] = color[1] = color[2] = f; + color[3] = 1.0f; + } +#endif + + return false; + + // return false; + + // BM_mesh_elem_table_ensure(bm, BM_VERT|BM_EDGE|BM_FACE); + if (!e) { + return false; + } + + bool bad = false; + int limit = 128; + + int vlimit = sizeof(BMVert *) * limit; + int elimit = sizeof(BMEdge *) * limit; + int llimit = sizeof(BMLoop *) * limit; + int flimit = sizeof(BMFace *) * limit; + + intptr_t iv = (intptr_t)v; + + BMEdge *laste = NULL; + do { + BMVert *v2 = BM_edge_other_vert(e, v); + intptr_t iv2 = (intptr_t)v2; + intptr_t ie = (intptr_t)e; + + v2->head.hflag &= DEFRAG_FLAG; + e->head.hflag &= ~DEFRAG_FLAG; + + if (ABSLL(iv2 - iv) > vlimit) { + bad = true; + break; + } + + if (laste) { + intptr_t ilaste = (intptr_t)laste; + if (ABSLL(ilaste - ie) > elimit) { + bad = true; + break; + } + } + + BMLoop *l = e->l; + if (l) { + do { + intptr_t il = (intptr_t)l; + intptr_t ilnext = (intptr_t)l->next; + + if (ABSLL(il - ilnext) > llimit) { + bad = true; + break; + } + + BMLoop *l2 = l->f->l_first; + do { + l2->head.hflag &= ~DEFRAG_FLAG; + } while ((l2 = l2->next) != l->f->l_first); + + l2->f->head.hflag &= ~DEFRAG_FLAG; + + l = l->radial_next; + } while (l != e->l); + } + laste = e; + } while (!bad && (e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + float prob = 1.0; + + if (!bad || BLI_rng_get_float(rand) > prob) { + return false; + } + + // find sort candidates + // BLI_mempool_find_elems_fuzzy + + int vidx = BLI_mempool_find_real_index(bm->vpool, (void *)v); + const int count = 5; + BMVert **elems = BLI_array_alloca(elems, count); + + do { + BMVert *v2 = BM_edge_other_vert(e, v); + int totelem = BLI_mempool_find_elems_fuzzy(bm->vpool, vidx, 4, (void **)elems, count); + + for (int i = 0; i < totelem; i++) { + if (elems[i] == v2 || elems[i] == v) { + continue; + } + + elems[i]->head.hflag &= ~DEFRAG_FLAG; + } + + bool ok = false; + + for (int i = 0; i < totelem; i++) { + if (elems[i] == v2 || elems[i] == v || (elems[i]->head.hflag & DEFRAG_FLAG)) { + continue; + } + + if (elems[i]->head.htype != BM_VERT) { + printf("ERROR!\n"); + } + // found one + v2->head.hflag |= DEFRAG_FLAG; + elems[i]->head.hflag |= DEFRAG_FLAG; + + on_vert_swap(v2, elems[i], userdata); + BM_swap_verts(bm, v2, elems[i]); + + BMIter iter; + BMEdge *et; + int f = 0; +#if 0 + BM_ITER_ELEM (et, &iter, v2, BM_EDGES_OF_VERT) { + printf("an edge %d\n", f++); + } + + f = 0; + BM_ITER_ELEM (et, &iter, v, BM_EDGES_OF_VERT) { + printf("an 1edge %d\n", f++); + } +#endif + + // BM_swap_verts(bm, v2, elems[i]); + + ok = true; + break; + } + + if (ok) { + break; + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + return true; +} /** \} */ diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index e0cb863aa97..349fce48396 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -1409,30 +1409,22 @@ static void sculpt_vertex_neighbors_get_bmesh(const SculptSession *ss, return; } + BMEdge *e2 = NULL; + do { BMVert *v2; - BMEdge *e2; - - if (v == e->v1) { - v2 = e->v2; - e2 = e->v1_disk_link.next; - } - else { - v2 = e->v1; - e2 = e->v2_disk_link.next; - } + e2 = BM_DISK_EDGE_NEXT(e, v); + v2 = v == e->v1 ? e->v2 : e->v1; MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v2); - if (!(mv->flag & DYNVERT_VERT_FSET_HIDDEN)) { + if (!(mv->flag & DYNVERT_VERT_FSET_HIDDEN)) { // && (e->head.hflag & BM_ELEM_DRAW)) { sculpt_vertex_neighbor_add_nocheck(iter, BKE_pbvh_make_vref((intptr_t)v2), BKE_pbvh_make_eref((intptr_t)e), BM_elem_index_get(v2)); } - - e = e2; - } while (e != v->e); + } while ((e = e2) != v->e); if (ss->fake_neighbors.use_fake_neighbors) { int index = BM_elem_index_get(v); @@ -4005,6 +3997,11 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, const bool have_bmesh = BKE_pbvh_type(ss->pbvh) == PBVH_BMESH; + const bool weighted = ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; + if (weighted || ss->cache->brush->boundary_smooth_factor > 0.0f) { + BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]); + } + PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { @@ -4092,6 +4089,41 @@ static void bmesh_topology_rake( SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_COLOR, "_rake_temp"); int cd_temp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_COLOR, "_rake_temp"); +#ifdef SCULPT_DIAGONAL_EDGE_MARKS + // reset edge flags, single threaded + for (int i = 0; i < totnode; i++) { + PBVHNode *node = nodes[i]; + PBVHVertexIter vd; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, brush->falloff_shape); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + + BMVert *v = vd.bm_vert; + BMEdge *e = v->e; + + if (!e) { + continue; + } + + do { + e->head.hflag |= BM_ELEM_DRAW; + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + } + BKE_pbvh_vertex_iter_end; + } +#endif + + 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 (brush->flag2 & BRUSH_TOPOLOGY_RAKE_IGNORE_BRUSH_FALLOFF) { local_brush = *brush; brush = &local_brush; @@ -9960,6 +9992,9 @@ static int sculpt_spatial_sort_exec(bContext *C, wmOperator *op) ss->active_vertex_index.i = 0; ss->active_face_index.i = 0; + BKE_pbvh_free(ss->pbvh); + ss->pbvh = NULL; + /* Finish undo. */ SCULPT_undo_push_end(); @@ -9971,7 +10006,8 @@ static int sculpt_spatial_sort_exec(bContext *C, wmOperator *op) } /* Redraw. */ - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, ND_DATA | NC_OBJECT | ND_DRAW, ob); return OPERATOR_FINISHED; } @@ -11552,6 +11588,7 @@ int SCULPT_vertex_valence_get(const struct SculptSession *ss, SculptVertRef vert int tot = 0; int mval = -1; +#if 0 if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { BMVert *v = (BMVert *)vertex.i; MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v); @@ -11562,10 +11599,17 @@ int SCULPT_vertex_valence_get(const struct SculptSession *ss, SculptVertRef vert mval = mv->valence; -#ifdef NDEBUG - // return mval; -#endif +# ifdef NDEBUG + return mval; +# endif } +#else + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + BMVert *v = (BMVert *)vertex.i; + + return BM_vert_edge_count(v); + } +#endif SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { tot++; diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c index c985d7b9e9d..31919b82a82 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c @@ -85,8 +85,11 @@ BMesh *SCULPT_dyntopo_empty_bmesh() { + const BMAllocTemplate allocsize = { + .totvert = 2048 * 16, .totface = 2048 * 16, .totloop = 4196 * 16, .totedge = 2048 * 16}; + BMesh *bm = BM_mesh_create( - &bm_mesh_allocsize_default, + &allocsize, &((struct BMeshCreateParams){.use_toolflags = false, .use_unique_ids = true, .use_id_elem_mask = BM_VERT | BM_EDGE | BM_FACE, @@ -760,7 +763,8 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene { SculptSession *ss = ob->sculpt; Mesh *me = ob->data; - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(me); + const BMAllocTemplate allocsize = { + .totvert = 2048 * 16, .totface = 2048 * 16, .totloop = 4196 * 16, .totedge = 2048 * 16}; SCULPT_pbvh_clear(ob); @@ -833,6 +837,11 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene SCULPT_dyntopo_node_layers_update_offsets(ss); int i = 0; + BMEdge *e; + + BM_ITER_MESH (e, &iter, ss->bm, BM_EDGES_OF_MESH) { + e->head.hflag |= BM_ELEM_DRAW; + } BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v); diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index 87f14a2a108..72ab985418d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -77,8 +77,6 @@ static int sculpt_face_material_get(SculptSession *ss, SculptFaceRef face) { - int ret = 0; - switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_BMESH: { BMFace *f = (BMFace *)face.i; @@ -94,8 +92,6 @@ static int sculpt_face_material_get(SculptSession *ss, SculptFaceRef face) int SCULPT_face_set_get(SculptSession *ss, SculptFaceRef face) { - int ret = 0; - switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_BMESH: { BMFace *f = (BMFace *)face.i; @@ -268,6 +264,30 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, const int active_fset = abs(ss->cache->paint_face_set); MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss); + const float test_limit = 0.05f; + int cd_mask = -1; + + if (ss->bm) { + cd_mask = CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK); + } + + /*check if we need to sample the current face set*/ + + bool set_active_faceset = ss->cache->automasking && + (brush->automasking_flags & BRUSH_AUTOMASKING_FACE_SETS); + set_active_faceset = set_active_faceset && ss->cache->invert; + set_active_faceset = set_active_faceset && ss->cache->automasking->settings.initial_face_set == + ss->cache->automasking->settings.current_face_set; + + int automasking_fset_flag = 0; + + if (set_active_faceset) { + // temporarily clear faceset flag + automasking_fset_flag = ss->cache->automasking ? ss->cache->automasking->settings.flags & + BRUSH_AUTOMASKING_FACE_SETS : + 0; + ss->cache->automasking->settings.flags &= ~BRUSH_AUTOMASKING_FACE_SETS; + } BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { @@ -291,8 +311,60 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.vertex, thread_id); - if (fade > 0.05f && ss->face_sets[vert_map->indices[j]] > 0) { - ss->face_sets[vert_map->indices[j]] = abs(ss->cache->paint_face_set); + if (fade > test_limit && ss->face_sets[vert_map->indices[j]] > 0) { + bool ok = true; + + int fset = abs(ss->face_sets[vert_map->indices[j]]); + + // XXX kind of hackish, tries to sample faces that are within + // 8 pixels of the center of the brush, and using a crude linear + // scale at that - joeedh + if (set_active_faceset && + fset != abs(ss->cache->automasking->settings.initial_face_set)) { + + float radius = ss->cache->radius; + float pixels = 8; // TODO: multiply with DPI + radius = pixels * (radius / (float)ss->cache->dyntopo_pixel_radius); + + if (sqrtf(test.dist) < radius) { + ss->cache->automasking->settings.initial_face_set = abs(fset); + set_active_faceset = false; + ss->cache->automasking->settings.flags |= BRUSH_AUTOMASKING_FACE_SETS; + } + else { + ok = false; + } + } + + MLoop *ml = &ss->mloop[p->loopstart]; + + for (int i = 0; i < p->totloop; i++, ml++) { + MVert *v = &ss->mvert[ml->v]; + float fno[3]; + + normal_short_to_float_v3(fno, v->no); + float mask = ss->vmask ? ss->vmask[ml->v] : 0.0f; + + const float fade2 = bstrength * + SCULPT_brush_strength_factor(ss, + brush, + v->co, + sqrtf(test.dist), + v->no, + fno, + mask, + (SculptVertRef){.i = ml->v}, + thread_id); + + if (fade2 < test_limit) { + ok = false; + break; + } + } + + if (ok) { + ss->face_sets[vert_map->indices[j]] = abs(ss->cache->paint_face_set); + } } } } @@ -318,15 +390,60 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); - if (fade > 0.05f && fset > 0) { + if (fade > test_limit && fset > 0) { BMLoop *l = f->l_first; + bool ok = true; + + // XXX kind of hackish, tries to sample faces that are within + // 8 pixels of the center of the brush, and using a crude linear + // scale at that - joeedh + if (set_active_faceset && + abs(fset) != abs(ss->cache->automasking->settings.initial_face_set)) { + + float radius = ss->cache->radius; + float pixels = 8; // TODO: multiple with DPI + radius = pixels * (radius / (float)ss->cache->dyntopo_pixel_radius); + + if (sqrtf(test.dist) < radius) { + ss->cache->automasking->settings.initial_face_set = abs(fset); + set_active_faceset = false; + ss->cache->automasking->settings.flags |= BRUSH_AUTOMASKING_FACE_SETS; + } + else { + ok = false; + } + } + do { + short sno[3]; + float mask = cd_mask >= 0 ? BM_ELEM_CD_GET_FLOAT(l->v, cd_mask) : 0.0f; + + normal_float_to_short_v3(sno, l->v->no); + + const float fade2 = bstrength * SCULPT_brush_strength_factor( + ss, + brush, + l->v->co, + sqrtf(test.dist), + sno, + l->v->no, + mask, + (SculptVertRef){.i = (intptr_t)l->v}, + thread_id); + + if (fade2 < test_limit) { + ok = false; + break; + } + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, l->v); mv->flag |= DYNVERT_NEED_BOUNDARY; } while ((l = l->next) != f->l_first); - BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, active_fset); + if (ok) { + BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, active_fset); + } } } } @@ -353,6 +470,11 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, } } BKE_pbvh_vertex_iter_end; + + // restore automasking flag + if (set_active_faceset) { + ss->cache->automasking->settings.flags |= automasking_fset_flag; + } } static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, @@ -419,8 +541,27 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in .nodes = nodes, }; + bool threaded = true; + + /*for ctrl invert mode we have to set the automasking initial_face_set + to the first non-current faceset that is found*/ + if (SCULPT_stroke_is_first_brush_step(ss->cache)) { + if (ss->cache->invert && ss->cache->automasking && + (brush->automasking_flags & BRUSH_AUTOMASKING_FACE_SETS)) { + ss->cache->automasking->settings.current_face_set = + ss->cache->automasking->settings.initial_face_set; + } + } + + if (ss->cache->invert && !ss->cache->alt_smooth && ss->cache->automasking && + ss->cache->automasking->settings.initial_face_set == + ss->cache->automasking->settings.current_face_set) { + threaded = false; + } + + // ctrl-click is single threaded since the tasks will set the initial face set TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BKE_pbvh_parallel_range_settings(&settings, threaded, totnode); if (ss->cache->alt_smooth) { SCULPT_boundary_info_ensure(ob); for (int i = 0; i < 4; i++) { @@ -554,10 +695,6 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) } if (mode == SCULPT_FACE_SET_SELECTION) { - Mesh *mesh = ob->data; - BMesh *bm; - BMFace *f; - BMIter iter; const int totface = ss->totfaces; for (int i = 0; i < totface; i++) { @@ -861,8 +998,6 @@ static void sculpt_face_sets_init_loop(Object *ob, const int mode) } case PBVH_FACES: case PBVH_GRIDS: { - int f_i = fref.i; - if (fmaps) { fmap = fmaps[i] + 2; } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 119887db551..8adedbd58e5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -1027,6 +1027,7 @@ typedef struct AutomaskingSettings { /* Flags from eAutomasking_flag. */ int flags; int initial_face_set; + int current_face_set; // used by faceset draw tool float concave_factor; } AutomaskingSettings; diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index ee49ba416c8..e29f222ac35 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -29,7 +29,9 @@ #include "BLI_compiler_attrs.h" #include "BLI_hash.h" #include "BLI_math.h" +#include "BLI_rand.h" #include "BLI_task.h" +#include "BLI_threads.h" #include "DNA_brush_types.h" #include "DNA_mesh_types.h" @@ -61,6 +63,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "atomic_ops.h" #include "bmesh.h" #ifdef PROXY_ADVANCED /* clang-format off */ @@ -102,7 +105,7 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, } const bool weighted = (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) && !is_boundary; - float *areas; + float *areas = NULL; SculptCornerType ctype = SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP; if (check_fsets) { @@ -146,8 +149,19 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, from verts*/ SculptBoundaryType final_boundary = 0; + if (ni.has_edge) { final_boundary = SCULPT_edge_is_boundary(ss, ni.edge, bflag); + +#ifdef SCULPT_DIAGONAL_EDGE_MARKS + if (ss->bm) { + BMEdge *e = (BMEdge *)ni.edge.i; + if (!(e->head.hflag & BM_ELEM_DRAW)) { + neighbor_count--; + continue; + } + } +#endif } else { final_boundary = is_boundary & SCULPT_vertex_is_boundary(ss, ni.vertex, bflag); @@ -162,7 +176,8 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, */ - bool slide = slide_fset > 0.0f && is_boundary == SCULPT_BOUNDARY_FACE_SET; + bool slide = (slide_fset > 0.0f && is_boundary == SCULPT_BOUNDARY_FACE_SET) || + bound_smooth > 0.0f; slide = slide && !final_boundary; if (slide) { @@ -189,7 +204,7 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, ok = true; } - if (do_diffuse && bound_scl) { + if (do_diffuse && bound_scl && !is_boundary) { /* simple boundary inflator using an ad-hoc diffusion-based pseudo-geodesic field @@ -429,6 +444,16 @@ static void vec_transform(float r_dir2[3], float no[3], int bits) } } +volatile int blehrand = 0; +static int blehrand_get() +{ + int i = blehrand; + i = (i * 124325 + 231423322) & 524287; + + blehrand = i; + return i; +} + /* For bmesh: Average surrounding verts based on an orthogonality measure. * Naturally converges to a quad-like structure. */ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, @@ -453,6 +478,18 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float dir[3]; float dir3[3] = {0.0f, 0.0f, 0.0f}; + const bool weighted = (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT); + float *areas; + + if (weighted) { + SculptVertRef vertex = {.i = (intptr_t)v}; + + 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); + } + copy_v3_v3(dir, col); if (dot_v3v3(dir, dir) == 0.0f) { @@ -471,12 +508,23 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, BMIter eiter; BMEdge *e; bool had_bound = false; + int area_i = 0; - BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { + BM_ITER_ELEM_INDEX (e, &eiter, v, BM_EDGES_OF_VERT, area_i) { BMVert *v_other = (e->v1 == v) ? e->v2 : e->v1; float dir2[3]; float *col2 = BM_ELEM_CD_GET_VOID_P(v_other, cd_temp); + + float bucketw = 1.0f; // col2[3] < col[3] ? 2.0f : 1.0f; + // bucketw /= 0.00001f + len_v3v3(e->v1->co, e->v2->co); + // if (weighted) { + // bucketw = 1.0 / (0.000001 + areas[area_i]); + //} + // if (e == v->e) { + // bucketw *= 2.0; + //} + MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(cd_dyn_vert, v_other); // bool bound = (mv2->flag & // (DYNVERT_BOUNDARY)); // | DYNVERT_FSET_BOUNDARY | DYNVERT_SHARP_BOUNDARY)); @@ -506,7 +554,7 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, } } - closest_vec_to_perp(dir, dir2, v->no, buckets, 1.0f); // col2[3]); + closest_vec_to_perp(dir, dir2, v->no, buckets, bucketw); // col2[3]); madd_v3_v3fl(dir3, dir2, dirw); totdir3 += dirw; @@ -525,8 +573,31 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, /* fac is a measure of how orthogonal or parallel the edge is * relative to the direction. */ float fac = dot_v3v3(vec, dir); +#ifdef SCULPT_DIAGONAL_EDGE_MARKS + float th = fabsf(saacos(fac)) / M_PI + 0.5f; + th -= floorf(th); + + const float limit = 0.045; + + if (fabsf(th - 0.25) < limit || fabsf(th - 0.75) < limit) { + BMEdge enew = *e, eold = *e; + + enew.head.hflag &= ~BM_ELEM_DRAW; + // enew.head.hflag |= BM_ELEM_SEAM; // XXX debug + + atomic_cas_int64((intptr_t *)(&e->head.index), + *(intptr_t *)(&eold.head.index), + *(intptr_t *)(&enew.head.index)); + } +#endif + fac = fac * fac - 0.5f; fac *= fac; + + if (weighted) { + fac *= areas[area_i]; + } + madd_v3_v3fl(avg_co, v_other->co, fac); tot_co += fac; } @@ -574,9 +645,9 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, } } - negate_v3(col); + // negate_v3(col); vec_transform(col, v->no, bi); - negate_v3(col); + // negate_v3(col); } } |