Sculpt dyntopo

* Wrote a simple fix for drawing face sets
  in inverse (ctrl) mode with face set automasking
  on.

* Various fixes related to hard edges and smoothing.

* Started writing some code to defragment bmesh mempools.
  Need to figure out how to avoid triggering excessive
  PBVH node rebuilds.
This commit is contained in:
Joseph Eagar 2021-09-01 11:47:03 -07:00
parent baa24243a5
commit 8bfbbc467a
14 changed files with 1309 additions and 248 deletions

View File

@ -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;

View File

@ -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:

View File

@ -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;
// if (BM_vert_edge_count(v1) == 4 || BM_vert_edge_count(v2) == 4) {
// l *= 0.25f;
//}
val -= 6.0f;
val = MAX2(val, 1.0f);
// 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);
}
#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,
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
/*
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)) {
if (!(node->flag & PBVH_Leaf) || !(node->flag & PBVH_UpdateTopology)) {
continue;
}
BMVert *v;
float radius_sqr = radius * radius;
TGSET_ITER (v, node->bm_unique_verts) {
if (len_squared_v3v3(v->co, center) > radius_sqr) {
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);
}
if (BM_defragment_vertex(pbvh->bm, v, rng, on_vert_swap, &swapdata)) {
modified = true;
dcount++;
}
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

View File

@ -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) {

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;
}
/** \} */

View File

@ -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++;

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
}
}