Sculpt dyntopo: Dynamic field-propegated topology rake
I might write a paper on this. Topology rake now locally updates a vector field, which it uses to smooth the input and constrain to mesh (including face set) boundaries. This can make an enormous difference for things like smoothing. Note that this is different from the existing 'curvature rake' mode, which also builds a field and which is fed into the input of this new one. The only oddity is that the field is stored in a CD_PROP_COLOR since we don't have a CD_PROP_FLOAT4, and this shows up in the UI (not sure if I'm messing up the CD_TEMPORARY flags or if the UI doesn't check for them).
This commit is contained in:
parent
73529fb1bb
commit
baa24243a5
|
@ -567,10 +567,13 @@ def brush_settings(layout, context, brush, popover=False):
|
|||
slider=True,
|
||||
)
|
||||
|
||||
box.prop(brush, "boundary_smooth_factor");
|
||||
box.prop(brush, "boundary_smooth_factor")
|
||||
box.prop(brush, "use_weighted_smooth")
|
||||
box.prop(brush, "preserve_faceset_boundary")
|
||||
|
||||
if brush.preserve_faceset_boundary:
|
||||
box.prop(brush, "autosmooth_fset_slide")
|
||||
|
||||
box.prop(brush, "use_custom_auto_smooth_spacing", text="Custom Spacing")
|
||||
if brush.use_custom_auto_smooth_spacing:
|
||||
UnifiedPaintPanel.prop_unified(
|
||||
|
@ -619,7 +622,7 @@ def brush_settings(layout, context, brush, popover=False):
|
|||
|
||||
if brush.use_custom_topology_rake_spacing:
|
||||
box.prop(brush, "topology_rake_spacing", text="Spacing")
|
||||
box.prop(brush, "topology_rake_projection");
|
||||
box.prop(brush, "topology_rake_projection")
|
||||
|
||||
box.prop(brush, "topology_rake_radius_factor", slider=True)
|
||||
box.prop(brush, "use_curvature_rake")
|
||||
|
@ -820,6 +823,8 @@ def brush_settings(layout, context, brush, popover=False):
|
|||
|
||||
col.prop(brush, "use_weighted_smooth")
|
||||
col.prop(brush, "preserve_faceset_boundary")
|
||||
if brush.preserve_faceset_boundary:
|
||||
col.prop(brush, "autosmooth_fset_slide")
|
||||
|
||||
col.prop(brush, "smooth_deform_type")
|
||||
|
||||
|
|
|
@ -790,7 +790,6 @@ class VIEW3D_PT_sculpt_dyntopo_advanced(Panel, View3DPaintPanel):
|
|||
|
||||
col.prop_enum(brush.dyntopo, "inherit", value="ALL", text="Use All Defaults", icon="LOCKED" if inherit_all else "UNLOCKED")
|
||||
|
||||
|
||||
def do_prop(key):
|
||||
row = col.row()
|
||||
if key.upper() in brush.dyntopo.inherit:
|
||||
|
@ -814,6 +813,8 @@ class VIEW3D_PT_sculpt_dyntopo_advanced(Panel, View3DPaintPanel):
|
|||
do_prop("collapse")
|
||||
do_prop("cleanup")
|
||||
do_prop("spacing")
|
||||
do_prop("local_subdivide")
|
||||
do_prop("local_collapse")
|
||||
do_prop("detail_size")
|
||||
do_prop("detail_range")
|
||||
do_prop("detail_percent")
|
||||
|
|
|
@ -424,9 +424,11 @@ struct BMesh *BKE_pbvh_get_bmesh(PBVH *pbvh);
|
|||
void BKE_pbvh_bmesh_detail_size_set(PBVH *pbvh, float detail_size, float detail_range);
|
||||
|
||||
typedef enum {
|
||||
PBVH_Subdivide = 1,
|
||||
PBVH_Collapse = 2,
|
||||
PBVH_Cleanup = 4, // dissolve verts surrounded by either 3 or 4 triangles then triangulate
|
||||
PBVH_Subdivide = 1 << 0,
|
||||
PBVH_Collapse = 1 << 1,
|
||||
PBVH_Cleanup = 1 << 2, // dissolve verts surrounded by either 3 or 4 triangles then triangulate
|
||||
PBVH_LocalSubdivide = 1 << 3,
|
||||
PBVH_LocalCollapse = 1 << 4
|
||||
} PBVHTopologyUpdateMode;
|
||||
|
||||
typedef float (*DyntopoMaskCB)(SculptVertRef vertex, void *userdata);
|
||||
|
|
|
@ -1800,8 +1800,10 @@ void BKE_brush_sculpt_reset(Brush *br)
|
|||
case SCULPT_TOOL_SNAKE_HOOK:
|
||||
br->alpha = 1.0f;
|
||||
br->rake_factor = 1.0f;
|
||||
br->dyntopo.inherit = DYNTOPO_INHERIT_BITMASK & ~(DYNTOPO_INHERIT_ALL | DYNTOPO_COLLAPSE);
|
||||
br->dyntopo.flag |= DYNTOPO_COLLAPSE;
|
||||
br->dyntopo.inherit = DYNTOPO_INHERIT_BITMASK &
|
||||
~(DYNTOPO_INHERIT_ALL | DYNTOPO_LOCAL_COLLAPSE | DYNTOPO_INHERIT_DETAIL_RANGE);
|
||||
br->dyntopo.flag |= DYNTOPO_LOCAL_COLLAPSE;
|
||||
br->dyntopo.detail_range = 0.5f;
|
||||
break;
|
||||
case SCULPT_TOOL_THUMB:
|
||||
br->size = 75;
|
||||
|
@ -2701,6 +2703,24 @@ void BKE_brush_get_dyntopo(Brush *brush, Sculpt *sd, DynTopoSettings *out)
|
|||
}
|
||||
}
|
||||
|
||||
if (inherit & DYNTOPO_LOCAL_COLLAPSE) {
|
||||
if (sd->flags & SCULPT_DYNTOPO_LOCAL_COLLAPSE) {
|
||||
out->flag |= DYNTOPO_LOCAL_COLLAPSE;
|
||||
}
|
||||
else {
|
||||
out->flag &= ~DYNTOPO_LOCAL_COLLAPSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (inherit & DYNTOPO_LOCAL_SUBDIVIDE) {
|
||||
if (sd->flags & SCULPT_DYNTOPO_LOCAL_SUBDIVIDE) {
|
||||
out->flag |= DYNTOPO_LOCAL_SUBDIVIDE;
|
||||
}
|
||||
else {
|
||||
out->flag &= ~DYNTOPO_LOCAL_SUBDIVIDE;
|
||||
}
|
||||
}
|
||||
|
||||
if (inherit & DYNTOPO_COLLAPSE) {
|
||||
if (sd->flags & SCULPT_DYNTOPO_COLLAPSE) {
|
||||
out->flag |= DYNTOPO_COLLAPSE;
|
||||
|
|
|
@ -153,12 +153,22 @@ static void pbvh_bmesh_verify(PBVH *pbvh);
|
|||
} \
|
||||
((void)0)
|
||||
|
||||
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);
|
||||
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,
|
||||
PBVH *pbvh,
|
||||
const float center[3],
|
||||
const float view_normal[3],
|
||||
float radius,
|
||||
const bool use_frontface,
|
||||
const bool use_projected,
|
||||
bool is_collapse);
|
||||
|
||||
//#define CHECKMESH
|
||||
//#define TEST_INVALID_NORMALS
|
||||
|
@ -1173,6 +1183,11 @@ static void pbvh_bmesh_edge_loops(BLI_Buffer *buf, BMEdge *e)
|
|||
|
||||
struct EdgeQueue;
|
||||
|
||||
typedef struct EdgePair {
|
||||
BMVert *v1, *v2;
|
||||
float limit_len_squared;
|
||||
} EdgePair;
|
||||
|
||||
typedef struct EdgeQueue {
|
||||
HeapSimple *heap;
|
||||
|
||||
|
@ -1196,7 +1211,7 @@ typedef struct EdgeQueue {
|
|||
#endif
|
||||
} EdgeQueue;
|
||||
|
||||
typedef struct {
|
||||
typedef struct EdgeQueueContext {
|
||||
EdgeQueue *q;
|
||||
BLI_mempool *pool;
|
||||
BMesh *bm;
|
||||
|
@ -1213,6 +1228,7 @@ typedef struct {
|
|||
BMVert **val34_verts;
|
||||
int val34_verts_tot;
|
||||
int val34_verts_size;
|
||||
bool local_mode;
|
||||
} EdgeQueueContext;
|
||||
|
||||
static void edge_queue_insert_val34_vert(EdgeQueueContext *eq_ctx, BMVert *v)
|
||||
|
@ -1420,6 +1436,7 @@ typedef struct EdgeQueueThreadData {
|
|||
EdgeQueueContext *eq_ctx;
|
||||
int totedge;
|
||||
int size;
|
||||
bool is_collapse;
|
||||
} EdgeQueueThreadData;
|
||||
|
||||
static void edge_thread_data_insert(EdgeQueueThreadData *tdata, BMEdge *e)
|
||||
|
@ -1434,7 +1451,14 @@ static void edge_thread_data_insert(EdgeQueueThreadData *tdata, BMEdge *e)
|
|||
}
|
||||
}
|
||||
|
||||
e->head.hflag |= BM_ELEM_TAG;
|
||||
BMElem elem;
|
||||
memcpy(&elem, (BMElem *)e, sizeof(BMElem));
|
||||
|
||||
elem.head.hflag = e->head.hflag | BM_ELEM_TAG;
|
||||
int64_t iold = *((int64_t *)&e->head.index);
|
||||
int64_t inew = *((int64_t *)&elem.head.index);
|
||||
|
||||
atomic_cas_int64((int64_t *)&e->head.index, iold, inew);
|
||||
|
||||
tdata->edges[tdata->totedge] = e;
|
||||
tdata->totedge++;
|
||||
|
@ -1449,7 +1473,7 @@ static bool edge_queue_vert_in_circle(const EdgeQueue *q, BMVert *v)
|
|||
return len_squared_v3v3(q->center_proj, c) <= q->radius_squared;
|
||||
}
|
||||
|
||||
static void edge_queue_insert(EdgeQueueContext *eq_ctx, BMEdge *e, float priority)
|
||||
static void edge_queue_insert(EdgeQueueContext *eq_ctx, BMEdge *e, float priority, float limit)
|
||||
{
|
||||
void **elems = eq_ctx->q->elems;
|
||||
BLI_array_declare(elems);
|
||||
|
@ -1463,9 +1487,12 @@ static void edge_queue_insert(EdgeQueueContext *eq_ctx, BMEdge *e, float priorit
|
|||
eq_ctx->min_elen = MIN2(eq_ctx->min_elen, dis);
|
||||
eq_ctx->totedge += 1.0f;
|
||||
|
||||
BMVert **pair = BLI_mempool_alloc(eq_ctx->pool);
|
||||
pair[0] = e->v1;
|
||||
pair[1] = e->v2;
|
||||
EdgePair *pair = BLI_mempool_alloc(eq_ctx->pool);
|
||||
|
||||
pair->v1 = e->v1;
|
||||
pair->v2 = e->v2;
|
||||
pair->limit_len_squared = limit * limit;
|
||||
|
||||
#ifdef DYNTOPO_USE_HEAP
|
||||
BLI_heapsimple_insert(eq_ctx->q->heap, priority, pair);
|
||||
#endif
|
||||
|
@ -1491,7 +1518,7 @@ static void long_edge_queue_edge_add(EdgeQueueContext *eq_ctx, BMEdge *e)
|
|||
const float len_sq = BM_edge_calc_length_squared(e) * w * w;
|
||||
|
||||
if (len_sq > eq_ctx->q->limit_len_squared) {
|
||||
edge_queue_insert(eq_ctx, e, -len_sq);
|
||||
edge_queue_insert(eq_ctx, e, -len_sq, eq_ctx->q->limit_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1518,7 +1545,7 @@ static void long_edge_queue_edge_add_recursive(EdgeQueueContext *eq_ctx,
|
|||
if (EDGE_QUEUE_TEST(l_edge->e) == false)
|
||||
# endif
|
||||
{
|
||||
edge_queue_insert(eq_ctx, l_edge->e, -len_sq);
|
||||
edge_queue_insert(eq_ctx, l_edge->e, -len_sq, eq_ctx->q->limit_len);
|
||||
}
|
||||
|
||||
/* temp support previous behavior! */
|
||||
|
@ -1564,7 +1591,7 @@ static void short_edge_queue_edge_add(EdgeQueueContext *eq_ctx, BMEdge *e)
|
|||
{
|
||||
const float len_sq = calc_weighted_edge_collapse(eq_ctx, e->v1, e->v2);
|
||||
if (len_sq < eq_ctx->q->limit_len_squared) {
|
||||
edge_queue_insert(eq_ctx, e, len_sq);
|
||||
edge_queue_insert(eq_ctx, e, len_sq, eq_ctx->q->limit_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1869,7 +1896,6 @@ static void short_edge_queue_task_cb(void *__restrict userdata,
|
|||
continue;
|
||||
}
|
||||
|
||||
#ifdef USE_EDGEQUEUE_EVEN_SUBDIV
|
||||
float len_sq = calc_weighted_edge_collapse(eq_ctx, l_iter->e->v1, l_iter->e->v2);
|
||||
len_sq /= w * w;
|
||||
|
||||
|
@ -1877,18 +1903,43 @@ static void short_edge_queue_task_cb(void *__restrict userdata,
|
|||
short_edge_queue_edge_add_recursive_2(
|
||||
tdata, l_iter->radial_next, l_iter, len_sq, eq_ctx->q->limit_len, 0);
|
||||
}
|
||||
#else
|
||||
const float len_sq = calc_weighted_edge_split(eq_ctx, l_iter->e->v1, l_iter->e->v2);
|
||||
if (len_sq > eq_ctx->q->limit_len_squared) {
|
||||
edge_thread_data_insert(tdata, l_iter->e);
|
||||
}
|
||||
#endif
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
}
|
||||
TGSET_ITER_END
|
||||
}
|
||||
|
||||
static void short_edge_queue_task_cb_local(void *__restrict userdata,
|
||||
const int n,
|
||||
const TaskParallelTLS *__restrict tls)
|
||||
{
|
||||
EdgeQueueThreadData *tdata = ((EdgeQueueThreadData *)userdata) + n;
|
||||
PBVHNode *node = tdata->node;
|
||||
EdgeQueueContext *eq_ctx = tdata->eq_ctx;
|
||||
|
||||
BMFace *f;
|
||||
|
||||
TGSET_ITER (f, node->bm_faces) {
|
||||
#ifdef USE_EDGEQUEUE_FRONTFACE
|
||||
if (eq_ctx->q->use_view_normal) {
|
||||
if (dot_v3v3(f->no, eq_ctx->q->view_normal) < 0.0f) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (eq_ctx->q->edge_queue_tri_in_range(eq_ctx->q, f)) {
|
||||
BMLoop *l = f->l_first;
|
||||
|
||||
do {
|
||||
edge_thread_data_insert(tdata, l->e);
|
||||
|
||||
} while ((l = l->next) != f->l_first);
|
||||
}
|
||||
}
|
||||
TGSET_ITER_END
|
||||
}
|
||||
|
||||
static bool check_face_is_tri(PBVH *pbvh, BMFace *f)
|
||||
{
|
||||
bool origlen = f->len;
|
||||
|
@ -2098,13 +2149,22 @@ static void long_edge_queue_create(EdgeQueueContext *eq_ctx,
|
|||
const float view_normal[3],
|
||||
float radius,
|
||||
const bool use_frontface,
|
||||
const bool use_projected)
|
||||
const bool use_projected,
|
||||
const bool local_mode)
|
||||
{
|
||||
if (local_mode) {
|
||||
edge_queue_create_local(
|
||||
eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected, false);
|
||||
return;
|
||||
}
|
||||
|
||||
eq_ctx->q->heap = BLI_heapsimple_new();
|
||||
eq_ctx->q->elems = NULL;
|
||||
eq_ctx->q->totelems = 0;
|
||||
eq_ctx->q->radius_squared = radius * radius;
|
||||
eq_ctx->q->limit_len_squared = pbvh->bm_max_edge_len * pbvh->bm_max_edge_len;
|
||||
eq_ctx->local_mode = local_mode;
|
||||
|
||||
#ifdef USE_EDGEQUEUE_EVEN_SUBDIV
|
||||
eq_ctx->q->limit_len = pbvh->bm_max_edge_len;
|
||||
#endif
|
||||
|
@ -2198,7 +2258,7 @@ static void long_edge_queue_create(EdgeQueueContext *eq_ctx,
|
|||
|
||||
w *= w2 * w2;
|
||||
|
||||
edge_queue_insert(eq_ctx, e, w);
|
||||
edge_queue_insert(eq_ctx, e, w, eq_ctx->q->limit_len);
|
||||
}
|
||||
|
||||
MEM_SAFE_FREE(td->edges);
|
||||
|
@ -2207,6 +2267,255 @@ static void long_edge_queue_create(EdgeQueueContext *eq_ctx,
|
|||
BLI_array_free(tdata);
|
||||
}
|
||||
|
||||
static void edge_queue_create_local(EdgeQueueContext *eq_ctx,
|
||||
PBVH *pbvh,
|
||||
const float center[3],
|
||||
const float view_normal[3],
|
||||
float radius,
|
||||
const bool use_frontface,
|
||||
const bool use_projected,
|
||||
bool is_collapse)
|
||||
{
|
||||
eq_ctx->q->heap = BLI_heapsimple_new();
|
||||
eq_ctx->q->elems = NULL;
|
||||
eq_ctx->q->totelems = 0;
|
||||
eq_ctx->q->center = center;
|
||||
eq_ctx->q->radius_squared = radius * radius;
|
||||
eq_ctx->q->limit_len_squared = pbvh->bm_min_edge_len * pbvh->bm_min_edge_len;
|
||||
#ifdef USE_EDGEQUEUE_EVEN_SUBDIV
|
||||
eq_ctx->q->limit_len = pbvh->bm_min_edge_len;
|
||||
#endif
|
||||
|
||||
eq_ctx->q->view_normal = view_normal;
|
||||
|
||||
#ifdef USE_EDGEQUEUE_FRONTFACE
|
||||
eq_ctx->q->use_view_normal = use_frontface;
|
||||
#else
|
||||
UNUSED_VARS(use_frontface);
|
||||
#endif
|
||||
|
||||
edge_queue_init(eq_ctx, use_projected, use_frontface, center, view_normal, radius);
|
||||
|
||||
EdgeQueueThreadData *tdata = NULL;
|
||||
BLI_array_declare(tdata);
|
||||
|
||||
for (int n = 0; n < pbvh->totnode; n++) {
|
||||
PBVHNode *node = &pbvh->nodes[n];
|
||||
EdgeQueueThreadData td;
|
||||
|
||||
if ((node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology) &&
|
||||
!(node->flag & PBVH_FullyHidden)) {
|
||||
memset(&td, 0, sizeof(td));
|
||||
td.pbvh = pbvh;
|
||||
td.node = node;
|
||||
td.is_collapse = is_collapse;
|
||||
td.eq_ctx = eq_ctx;
|
||||
|
||||
BLI_array_append(tdata, td);
|
||||
}
|
||||
}
|
||||
|
||||
int count = BLI_array_len(tdata);
|
||||
|
||||
TaskParallelSettings settings;
|
||||
|
||||
BLI_parallel_range_settings_defaults(&settings);
|
||||
BLI_task_parallel_range(0, count, tdata, short_edge_queue_task_cb_local, &settings);
|
||||
|
||||
const int cd_dyn_vert = pbvh->cd_dyn_vert;
|
||||
BMEdge **edges = NULL;
|
||||
float *lens = NULL;
|
||||
BLI_array_declare(edges);
|
||||
BLI_array_declare(lens);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
EdgeQueueThreadData *td = tdata + i;
|
||||
|
||||
BMEdge **edges2 = td->edges;
|
||||
for (int j = 0; j < td->totedge; j++) {
|
||||
edges2[j]->head.hflag &= ~BM_ELEM_TAG;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
EdgeQueueThreadData *td = tdata + i;
|
||||
|
||||
BMEdge **edges2 = td->edges;
|
||||
for (int j = 0; j < td->totedge; j++) {
|
||||
BMEdge *e = edges2[j];
|
||||
|
||||
e->v1->head.hflag &= ~BM_ELEM_TAG;
|
||||
e->v2->head.hflag &= ~BM_ELEM_TAG;
|
||||
|
||||
if (!(e->head.hflag & BM_ELEM_TAG)) {
|
||||
BLI_array_append(edges, e);
|
||||
e->head.hflag |= BM_ELEM_TAG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
EdgeQueueThreadData *td = tdata + i;
|
||||
MEM_SAFE_FREE(td->edges);
|
||||
MEM_SAFE_FREE(td->val34_verts);
|
||||
}
|
||||
|
||||
for (int i = 0; i < BLI_array_len(edges); i++) {
|
||||
BMEdge *e = edges[i];
|
||||
float len = len_v3v3(e->v1->co, e->v2->co);
|
||||
|
||||
for (int j = 0; j < 2; j++) {
|
||||
BMVert *v = j ? e->v2 : e->v1;
|
||||
|
||||
if (!is_collapse) {
|
||||
if (!(v->head.hflag & BM_ELEM_TAG)) {
|
||||
v->head.hflag |= BM_ELEM_TAG;
|
||||
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 == 3 || mv->valence == 4) {
|
||||
edge_queue_insert_val34_vert(eq_ctx, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
e->head.index = i;
|
||||
BLI_array_append(lens, len);
|
||||
}
|
||||
|
||||
// make sure tags around border edges are unmarked
|
||||
for (int i = 0; i < BLI_array_len(edges); i++) {
|
||||
BMEdge *e = edges[i];
|
||||
|
||||
for (int j = 0; j < 2; j++) {
|
||||
BMVert *v1 = j ? e->v2 : e->v1;
|
||||
BMEdge *e1 = v1->e;
|
||||
|
||||
do {
|
||||
e1->head.hflag &= ~BM_ELEM_TAG;
|
||||
|
||||
e1 = BM_DISK_EDGE_NEXT(e1, v1);
|
||||
} while (e1 != v1->e);
|
||||
}
|
||||
}
|
||||
|
||||
// re-tag edge list
|
||||
for (int i = 0; i < BLI_array_len(edges); i++) {
|
||||
edges[i]->head.hflag |= BM_ELEM_TAG;
|
||||
}
|
||||
|
||||
int totstep = is_collapse ? 3 : 3;
|
||||
|
||||
// blur lengths
|
||||
for (int step = 0; step < totstep; step++) {
|
||||
for (int i = 0; i < BLI_array_len(edges); i++) {
|
||||
BMEdge *e = edges[i];
|
||||
|
||||
float len = lens[i];
|
||||
float totlen = 0.0f;
|
||||
|
||||
for (int j = 0; j < 2; j++) {
|
||||
BMVert *v1 = j ? e->v2 : e->v1;
|
||||
BMEdge *e1 = v1->e;
|
||||
|
||||
do {
|
||||
if (e1->head.hflag & BM_ELEM_TAG) {
|
||||
len += lens[e1->head.index];
|
||||
totlen += 1.0f;
|
||||
}
|
||||
|
||||
e1 = BM_DISK_EDGE_NEXT(e1, v1);
|
||||
} while (e1 != v1->e);
|
||||
}
|
||||
|
||||
if (totlen != 0.0f) {
|
||||
len /= totlen;
|
||||
lens[i] += (len - lens[i]) * 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pbvh->bm->elem_index_dirty |= BM_EDGE;
|
||||
float sign = is_collapse ? 1.0f : -1.0f;
|
||||
|
||||
const float detail_range = pbvh->bm_min_edge_len == 0.0f ?
|
||||
0.0f :
|
||||
pbvh->bm_max_edge_len / pbvh->bm_min_edge_len;
|
||||
|
||||
for (int i = 0; i < BLI_array_len(edges); i++) {
|
||||
BMEdge *e = edges[i];
|
||||
MDynTopoVert *mv1, *mv2;
|
||||
|
||||
e->head.hflag &= ~BM_ELEM_TAG;
|
||||
|
||||
mv1 = BKE_PBVH_DYNVERT(cd_dyn_vert, e->v1);
|
||||
mv2 = BKE_PBVH_DYNVERT(cd_dyn_vert, e->v2);
|
||||
|
||||
pbvh_check_vert_boundary(pbvh, e->v1);
|
||||
pbvh_check_vert_boundary(pbvh, e->v2);
|
||||
|
||||
if ((mv1->flag & DYNVERT_ALL_CORNER) || (mv2->flag & DYNVERT_ALL_CORNER)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((mv1->flag & DYNVERT_ALL_BOUNDARY) != (mv2->flag & DYNVERT_ALL_BOUNDARY)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float limit = lens[i];
|
||||
|
||||
// limit *= detail_range;
|
||||
if (is_collapse) {
|
||||
limit *= pbvh->bm_detail_range;
|
||||
}
|
||||
else {
|
||||
limit *= 1.0 + pbvh->bm_detail_range;
|
||||
}
|
||||
|
||||
eq_ctx->q->limit_len = limit;
|
||||
eq_ctx->q->limit_len_squared = limit * limit;
|
||||
|
||||
if (sign * (len_v3v3(e->v2->co, e->v1->co) - limit) >= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float w;
|
||||
if (is_collapse) {
|
||||
w = calc_weighted_edge_collapse(eq_ctx, e->v1, e->v2);
|
||||
}
|
||||
else {
|
||||
w = calc_weighted_edge_split(eq_ctx, e->v1, e->v2);
|
||||
}
|
||||
|
||||
float w2 = maskcb_get(eq_ctx, e);
|
||||
|
||||
if (w2 > 0.0f) {
|
||||
if (is_collapse) {
|
||||
w /= w2 * w2;
|
||||
}
|
||||
else {
|
||||
w *= w2 * w2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
w = 100000.0f;
|
||||
}
|
||||
|
||||
if (!is_collapse) {
|
||||
w = -w;
|
||||
}
|
||||
|
||||
edge_queue_insert(eq_ctx, e, w, limit);
|
||||
}
|
||||
|
||||
BLI_array_free(edges);
|
||||
BLI_array_free(lens);
|
||||
BLI_array_free(tdata);
|
||||
}
|
||||
|
||||
/* Create a priority queue containing vertex pairs connected by a
|
||||
* short edge as defined by PBVH.bm_min_edge_len.
|
||||
*
|
||||
|
@ -2222,17 +2531,23 @@ static void short_edge_queue_create(EdgeQueueContext *eq_ctx,
|
|||
const float view_normal[3],
|
||||
float radius,
|
||||
const bool use_frontface,
|
||||
const bool use_projected)
|
||||
const bool use_projected,
|
||||
bool local_mode)
|
||||
{
|
||||
if (local_mode) {
|
||||
edge_queue_create_local(
|
||||
eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected, true);
|
||||
return;
|
||||
}
|
||||
|
||||
eq_ctx->local_mode = false;
|
||||
eq_ctx->q->heap = BLI_heapsimple_new();
|
||||
eq_ctx->q->elems = NULL;
|
||||
eq_ctx->q->totelems = 0;
|
||||
eq_ctx->q->center = center;
|
||||
eq_ctx->q->radius_squared = radius * radius;
|
||||
eq_ctx->q->limit_len_squared = pbvh->bm_min_edge_len * pbvh->bm_min_edge_len;
|
||||
#ifdef USE_EDGEQUEUE_EVEN_SUBDIV
|
||||
eq_ctx->q->limit_len = pbvh->bm_min_edge_len;
|
||||
#endif
|
||||
eq_ctx->q->limit_len_squared = pbvh->bm_min_edge_len * pbvh->bm_min_edge_len;
|
||||
|
||||
eq_ctx->q->view_normal = view_normal;
|
||||
|
||||
|
@ -2316,7 +2631,7 @@ static void short_edge_queue_create(EdgeQueueContext *eq_ctx,
|
|||
}
|
||||
|
||||
e->head.hflag &= ~BM_ELEM_TAG;
|
||||
edge_queue_insert(eq_ctx, e, w);
|
||||
edge_queue_insert(eq_ctx, e, w, eq_ctx->q->limit_len);
|
||||
}
|
||||
|
||||
if (td->edges) {
|
||||
|
@ -2586,9 +2901,9 @@ static bool pbvh_bmesh_subdivide_long_edges(
|
|||
eq_ctx->q->elems[ri] = eq_ctx->q->elems[eq_ctx->q->totelems - 1];
|
||||
eq_ctx->q->totelems--;
|
||||
#else
|
||||
BMVert **pair = BLI_heapsimple_pop_min(eq_ctx->q->heap);
|
||||
EdgePair *pair = BLI_heapsimple_pop_min(eq_ctx->q->heap);
|
||||
#endif
|
||||
BMVert *v1 = pair[0], *v2 = pair[1];
|
||||
BMVert *v1 = pair->v1, *v2 = pair->v2;
|
||||
BMEdge *e;
|
||||
|
||||
BLI_mempool_free(eq_ctx->pool, pair);
|
||||
|
@ -2610,7 +2925,8 @@ static bool pbvh_bmesh_subdivide_long_edges(
|
|||
continue;
|
||||
}
|
||||
#else
|
||||
// BLI_assert(calc_weighted_edge_split(eq_ctx, v1->co, v2->co) > eq_ctx->q->limit_len_squared);
|
||||
// BLI_assert(calc_weighted_edge_split(eq_ctx, v1->co, v2->co) >
|
||||
// eq_ctx->q->limit_len_squared);
|
||||
#endif
|
||||
|
||||
/* Check that the edge's vertices are still in the PBVH. It's
|
||||
|
@ -2977,6 +3293,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
|
||||
BMLoop *l3 = f2->l_first;
|
||||
do {
|
||||
if (l3->v == f2->l_first->v && l3->f != f2 && l3->f != l->f) {
|
||||
|
@ -2985,6 +3302,7 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
|
|||
break;
|
||||
}
|
||||
} while ((l3 = l3->radial_next) != f2->l_first);
|
||||
# endif
|
||||
}
|
||||
}
|
||||
BM_LOOPS_OF_VERT_ITER_END;
|
||||
|
@ -3193,9 +3511,11 @@ static bool pbvh_bmesh_collapse_short_edges(EdgeQueueContext *eq_ctx,
|
|||
eq_ctx->q->elems[ri] = eq_ctx->q->elems[eq_ctx->q->totelems - 1];
|
||||
eq_ctx->q->totelems--;
|
||||
#else
|
||||
BMVert **pair = BLI_heapsimple_pop_min(eq_ctx->q->heap);
|
||||
EdgePair *pair = BLI_heapsimple_pop_min(eq_ctx->q->heap);
|
||||
#endif
|
||||
BMVert *v1 = pair[0], *v2 = pair[1];
|
||||
BMVert *v1 = pair->v1, *v2 = pair->v2;
|
||||
float limit_len_squared = pair->limit_len_squared;
|
||||
|
||||
BLI_mempool_free(eq_ctx->pool, pair);
|
||||
pair = NULL;
|
||||
|
||||
|
@ -3220,7 +3540,7 @@ static bool pbvh_bmesh_collapse_short_edges(EdgeQueueContext *eq_ctx,
|
|||
EDGE_QUEUE_DISABLE(e);
|
||||
#endif
|
||||
|
||||
if (calc_weighted_edge_collapse(eq_ctx, v1, v2) >= min_len_squared) {
|
||||
if (calc_weighted_edge_collapse(eq_ctx, v1, v2) >= limit_len_squared) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -3235,6 +3555,7 @@ static bool pbvh_bmesh_collapse_short_edges(EdgeQueueContext *eq_ctx,
|
|||
|
||||
any_collapsed = true;
|
||||
|
||||
eq_ctx->q->limit_len_squared = limit_len_squared;
|
||||
pbvh_bmesh_collapse_edge(pbvh, e, v1, v2, deleted_verts, deleted_faces, eq_ctx);
|
||||
|
||||
#ifdef TEST_COLLAPSE
|
||||
|
@ -3639,7 +3960,8 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
|
|||
.totedge = 0.0f,
|
||||
NULL,
|
||||
0,
|
||||
0};
|
||||
0,
|
||||
false};
|
||||
|
||||
int tempflag = 1 << 15;
|
||||
|
||||
|
@ -3648,16 +3970,23 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
|
|||
BM_log_entry_add_ex(pbvh->bm, pbvh->bm_log, true);
|
||||
|
||||
EdgeQueue q;
|
||||
BLI_mempool *queue_pool = BLI_mempool_create(sizeof(BMVert *) * 2, 0, 128, BLI_MEMPOOL_NOP);
|
||||
BLI_mempool *queue_pool = BLI_mempool_create(sizeof(EdgePair), 0, 128, BLI_MEMPOOL_NOP);
|
||||
|
||||
eq_ctx.q = &q;
|
||||
eq_ctx.pool = queue_pool;
|
||||
|
||||
short_edge_queue_create(
|
||||
&eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected);
|
||||
short_edge_queue_create(&eq_ctx,
|
||||
pbvh,
|
||||
center,
|
||||
view_normal,
|
||||
radius,
|
||||
use_frontface,
|
||||
use_projected,
|
||||
mode & PBVH_LocalCollapse);
|
||||
|
||||
# ifdef SKINNY_EDGE_FIX
|
||||
// prevent remesher thrashing by throttling edge splitting in pathological case of skinny edges
|
||||
// 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;
|
||||
|
@ -3697,13 +4026,19 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
|
|||
BM_log_entry_add_ex(pbvh->bm, pbvh->bm_log, true);
|
||||
|
||||
EdgeQueue q;
|
||||
BLI_mempool *queue_pool = BLI_mempool_create(sizeof(BMVert *) * 2, 0, 128, BLI_MEMPOOL_NOP);
|
||||
BLI_mempool *queue_pool = BLI_mempool_create(sizeof(EdgePair), 0, 128, BLI_MEMPOOL_NOP);
|
||||
|
||||
eq_ctx.q = &q;
|
||||
eq_ctx.pool = queue_pool;
|
||||
|
||||
long_edge_queue_create(
|
||||
&eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected);
|
||||
long_edge_queue_create(&eq_ctx,
|
||||
pbvh,
|
||||
center,
|
||||
view_normal,
|
||||
radius,
|
||||
use_frontface,
|
||||
use_projected,
|
||||
mode & PBVH_LocalSubdivide);
|
||||
|
||||
# if 0 /// def SKINNY_EDGE_FIX
|
||||
// prevent remesher thrashing by throttling edge splitting in pathological case of skinny edges
|
||||
|
@ -3725,15 +4060,19 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
|
|||
# else
|
||||
ratio = 1.0f;
|
||||
# endif
|
||||
float edgelen = eq_ctx.local_mode && eq_ctx.totedge ?
|
||||
eq_ctx.avg_elen / eq_ctx.totedge :
|
||||
(pbvh->bm_min_edge_len * 0.5f + pbvh->bm_max_edge_len * 0.5f);
|
||||
|
||||
float brusharea = radius / (pbvh->bm_min_edge_len * 0.5f + pbvh->bm_max_edge_len * 0.5f);
|
||||
float brusharea = radius / edgelen;
|
||||
//(pbvh->bm_min_edge_len * 0.5f + pbvh->bm_max_edge_len * 0.5f);
|
||||
brusharea = brusharea * brusharea * M_PI;
|
||||
|
||||
int max_steps = (int)((float)DYNTOPO_MAX_ITER * ratio);
|
||||
max_steps = (int)(brusharea * ratio * 1.0f);
|
||||
|
||||
printf("brusharea: %.2f, ratio: %.2f\n", brusharea, ratio);
|
||||
printf("max_steps %d\n", max_steps);
|
||||
printf("subdivide max_steps %d\n", max_steps);
|
||||
|
||||
pbvh_bmesh_check_nodes(pbvh);
|
||||
modified |= pbvh_bmesh_subdivide_long_edges(
|
||||
|
@ -4029,8 +4368,18 @@ static void pbvh_split_edges(
|
|||
}
|
||||
|
||||
do {
|
||||
if (!l) {
|
||||
printf("error 1 %s\n", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
BMLoop *l2 = l->f->l_first;
|
||||
do {
|
||||
if (!l2->e) {
|
||||
printf("error2 %s\n", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
l2->e->head.hflag &= ~SPLIT_TAG;
|
||||
l2->v->head.hflag &= ~SPLIT_TAG;
|
||||
|
||||
|
|
|
@ -3226,6 +3226,7 @@ void BKE_pbvh_bmesh_detail_size_set(PBVH *pbvh, float detail_size, float detail_
|
|||
{
|
||||
pbvh->bm_max_edge_len = detail_size;
|
||||
pbvh->bm_min_edge_len = pbvh->bm_max_edge_len * detail_range;
|
||||
pbvh->bm_detail_range = detail_range;
|
||||
}
|
||||
|
||||
void BKE_pbvh_node_mark_topology_update(PBVHNode *node)
|
||||
|
|
|
@ -191,6 +191,7 @@ struct PBVH {
|
|||
BMesh *bm;
|
||||
float bm_max_edge_len;
|
||||
float bm_min_edge_len;
|
||||
float bm_detail_range;
|
||||
|
||||
int cd_dyn_vert;
|
||||
int cd_vert_node_offset;
|
||||
|
|
|
@ -4020,9 +4020,9 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata,
|
|||
BMVert *v = (BMVert *)vd.vertex.i;
|
||||
MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v);
|
||||
|
||||
if (mv->flag & (DYNVERT_BOUNDARY | DYNVERT_FSET_BOUNDARY)) {
|
||||
continue;
|
||||
}
|
||||
// if (mv->flag & (DYNVERT_BOUNDARY | DYNVERT_FSET_BOUNDARY)) {
|
||||
// continue;
|
||||
//}
|
||||
}
|
||||
|
||||
float direction2[3];
|
||||
|
@ -4041,6 +4041,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata,
|
|||
copy_v3_v3(direction2, direction);
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (SCULPT_vertex_is_boundary(
|
||||
ss, vd.vertex, SCULPT_BOUNDARY_SHARP | SCULPT_BOUNDARY_MESH | check_fsets)) {
|
||||
continue;
|
||||
|
@ -4054,9 +4055,16 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata,
|
|||
if (mv->flag & (DYNVERT_CORNER | DYNVERT_SHARP_CORNER)) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
SCULPT_bmesh_four_neighbor_average(
|
||||
avg, direction2, vd.bm_vert, data->rake_projection, check_fsets);
|
||||
SCULPT_bmesh_four_neighbor_average(ss,
|
||||
avg,
|
||||
direction2,
|
||||
vd.bm_vert,
|
||||
data->rake_projection,
|
||||
check_fsets,
|
||||
data->cd_temp,
|
||||
data->cd_dyn_vert);
|
||||
|
||||
sub_v3_v3v3(val, avg, vd.co);
|
||||
|
||||
|
@ -4074,11 +4082,16 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata,
|
|||
static void bmesh_topology_rake(
|
||||
Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength)
|
||||
{
|
||||
SculptSession *ss = ob->sculpt;
|
||||
Brush *brush = BKE_paint_brush(&sd->paint);
|
||||
const float strength = clamp_f(bstrength, 0.0f, 1.0f);
|
||||
|
||||
Brush local_brush;
|
||||
|
||||
// vector4, nto color
|
||||
SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_COLOR, "_rake_temp");
|
||||
int cd_temp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_COLOR, "_rake_temp");
|
||||
|
||||
if (brush->flag2 & BRUSH_TOPOLOGY_RAKE_IGNORE_BRUSH_FALLOFF) {
|
||||
local_brush = *brush;
|
||||
brush = &local_brush;
|
||||
|
@ -4103,6 +4116,8 @@ static void bmesh_topology_rake(
|
|||
.brush = brush,
|
||||
.nodes = nodes,
|
||||
.strength = factor,
|
||||
.cd_temp = cd_temp,
|
||||
.cd_dyn_vert = ss->cd_dyn_vert,
|
||||
.rake_projection = brush->topology_rake_projection};
|
||||
TaskParallelSettings settings;
|
||||
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
|
||||
|
@ -7241,10 +7256,16 @@ static void sculpt_topology_update(Sculpt *sd,
|
|||
if (brush->cached_dyntopo.flag & DYNTOPO_SUBDIVIDE) {
|
||||
mode |= PBVH_Subdivide;
|
||||
}
|
||||
else if (brush->cached_dyntopo.flag & DYNTOPO_LOCAL_SUBDIVIDE) {
|
||||
mode |= PBVH_LocalSubdivide | PBVH_Subdivide;
|
||||
}
|
||||
|
||||
if (brush->cached_dyntopo.flag & DYNTOPO_COLLAPSE) {
|
||||
mode |= PBVH_Collapse;
|
||||
}
|
||||
else if (brush->cached_dyntopo.flag & DYNTOPO_LOCAL_COLLAPSE) {
|
||||
mode |= PBVH_LocalCollapse | PBVH_Collapse;
|
||||
}
|
||||
}
|
||||
|
||||
if (brush->cached_dyntopo.flag & DYNTOPO_CLEANUP) {
|
||||
|
|
|
@ -641,8 +641,14 @@ void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
|
|||
void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
|
||||
|
||||
/* Topology rake */
|
||||
void SCULPT_bmesh_four_neighbor_average(
|
||||
float avg[3], float direction[3], struct BMVert *v, float projection, bool check_fsets);
|
||||
void SCULPT_bmesh_four_neighbor_average(SculptSession *ss,
|
||||
float avg[3],
|
||||
float direction[3],
|
||||
struct BMVert *v,
|
||||
float projection,
|
||||
bool check_fsets,
|
||||
int cd_temp,
|
||||
int cd_dyn_vert);
|
||||
|
||||
/* Smoothing api */
|
||||
void SCULPT_neighbor_coords_average(
|
||||
|
@ -914,7 +920,7 @@ typedef struct SculptThreadedTaskData {
|
|||
|
||||
// Layer brush
|
||||
int cd_pers_co, cd_pers_no, cd_pers_disp;
|
||||
int cd_layer_disp;
|
||||
int cd_layer_disp, cd_temp, cd_dyn_vert;
|
||||
|
||||
float smooth_projection;
|
||||
float rake_projection;
|
||||
|
|
|
@ -84,12 +84,15 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss,
|
|||
|
||||
int bflag = SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_SHARP;
|
||||
float bound_smooth = powf(ss->cache->brush->boundary_smooth_factor, BOUNDARY_SMOOTH_EXP);
|
||||
float slide_fset = ss->cache->brush->autosmooth_fset_slide;
|
||||
|
||||
slide_fset = MAX2(slide_fset, bound_smooth);
|
||||
|
||||
if (check_fsets) {
|
||||
bflag |= SCULPT_BOUNDARY_FACE_SET;
|
||||
}
|
||||
|
||||
const bool is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bflag);
|
||||
const SculptBoundaryType is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bflag);
|
||||
|
||||
const float *co = SCULPT_vertex_co_get(ss, vertex);
|
||||
float no[3];
|
||||
|
@ -141,41 +144,50 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss,
|
|||
|
||||
/*use the new edge api if edges are available, if not estimate boundary
|
||||
from verts*/
|
||||
if (is_boundary || ni.has_edge) {
|
||||
bool is_boundary2;
|
||||
|
||||
if (ni.has_edge) {
|
||||
is_boundary2 = SCULPT_edge_is_boundary(ss, ni.edge, bflag);
|
||||
}
|
||||
else {
|
||||
is_boundary2 = SCULPT_vertex_is_boundary(ss, ni.vertex, bflag);
|
||||
}
|
||||
SculptBoundaryType final_boundary = 0;
|
||||
if (ni.has_edge) {
|
||||
final_boundary = SCULPT_edge_is_boundary(ss, ni.edge, bflag);
|
||||
}
|
||||
else {
|
||||
final_boundary = is_boundary & SCULPT_vertex_is_boundary(ss, ni.vertex, bflag);
|
||||
}
|
||||
|
||||
do_diffuse = bound_scl != NULL;
|
||||
|
||||
if (is_boundary) {
|
||||
/* Boundary vertices use only other boundary vertices.
|
||||
|
||||
This if statement needs to be refactored a bit, it's confusing.
|
||||
|
||||
*/
|
||||
if (is_boundary2 || !is_boundary) {
|
||||
copy_v3_v3(tmp, SCULPT_vertex_co_get(ss, ni.vertex));
|
||||
ok = true;
|
||||
do_diffuse = !is_boundary;
|
||||
}
|
||||
else if (bound_scl) {
|
||||
|
||||
bool slide = slide_fset > 0.0f && is_boundary == SCULPT_BOUNDARY_FACE_SET;
|
||||
slide = slide && !final_boundary;
|
||||
|
||||
if (slide) {
|
||||
// project non-boundary offset onto boundary normal
|
||||
float t[3];
|
||||
|
||||
w *= bound_smooth;
|
||||
w *= slide_fset;
|
||||
|
||||
sub_v3_v3v3(t, SCULPT_vertex_co_get(ss, ni.vertex), co);
|
||||
madd_v3_v3v3fl(tmp, co, no, dot_v3v3(t, no));
|
||||
ok = true;
|
||||
|
||||
do_diffuse = true;
|
||||
}
|
||||
else if (final_boundary) {
|
||||
copy_v3_v3(tmp, SCULPT_vertex_co_get(ss, ni.vertex));
|
||||
ok = true;
|
||||
do_diffuse = false;
|
||||
}
|
||||
else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(tmp, SCULPT_vertex_co_get(ss, ni.vertex));
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (do_diffuse && bound_scl) {
|
||||
/*
|
||||
|
@ -270,7 +282,7 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss,
|
|||
}
|
||||
|
||||
if (SCULPT_vertex_is_corner(ss, vertex, ctype)) {
|
||||
interp_v3_v3v3(result, result, co, 1.0f - bound_smooth);
|
||||
interp_v3_v3v3(result, result, co, 1.0f - slide_fset);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,24 +376,146 @@ void SCULPT_neighbor_coords_average_interior_velocity(SculptSession *ss,
|
|||
add_v3_v3(result, co);
|
||||
}
|
||||
}
|
||||
|
||||
static int closest_vec_to_perp(float dir[3], float r_dir2[3], float no[3], float *buckets, float w)
|
||||
{
|
||||
int bits = 0;
|
||||
|
||||
if (dot_v3v3(r_dir2, dir) < 0.0f) {
|
||||
negate_v3(r_dir2);
|
||||
bits |= 1;
|
||||
}
|
||||
|
||||
float dir4[3];
|
||||
cross_v3_v3v3(dir4, r_dir2, no);
|
||||
normalize_v3(dir4);
|
||||
|
||||
if (dot_v3v3(dir4, dir) < 0.0f) {
|
||||
negate_v3(dir4);
|
||||
bits |= 2;
|
||||
}
|
||||
|
||||
if (dot_v3v3(dir4, dir) > dot_v3v3(r_dir2, dir)) {
|
||||
copy_v3_v3(r_dir2, dir4);
|
||||
bits |= 4;
|
||||
}
|
||||
|
||||
buckets[bits] += w;
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
static void vec_transform(float r_dir2[3], float no[3], int bits)
|
||||
{
|
||||
if (bits & 4) {
|
||||
float dir4[3];
|
||||
|
||||
copy_v3_v3(dir4, r_dir2);
|
||||
|
||||
if (bits & 2) {
|
||||
negate_v3(dir4);
|
||||
}
|
||||
|
||||
float dir5[3];
|
||||
|
||||
cross_v3_v3v3(dir5, no, dir4);
|
||||
normalize_v3(dir5);
|
||||
|
||||
copy_v3_v3(r_dir2, dir5);
|
||||
}
|
||||
|
||||
if (bits & 1) {
|
||||
negate_v3(r_dir2);
|
||||
}
|
||||
}
|
||||
|
||||
/* For bmesh: Average surrounding verts based on an orthogonality measure.
|
||||
* Naturally converges to a quad-like structure. */
|
||||
void SCULPT_bmesh_four_neighbor_average(
|
||||
float avg[3], float direction[3], BMVert *v, float projection, bool check_fsets)
|
||||
void SCULPT_bmesh_four_neighbor_average(SculptSession *ss,
|
||||
float avg[3],
|
||||
float direction[3],
|
||||
BMVert *v,
|
||||
float projection,
|
||||
bool check_fsets,
|
||||
int cd_temp,
|
||||
int cd_dyn_vert)
|
||||
{
|
||||
|
||||
float avg_co[3] = {0.0f, 0.0f, 0.0f};
|
||||
float tot_co = 0.0f;
|
||||
float buckets[8] = {0};
|
||||
|
||||
// zero_v3(direction);
|
||||
|
||||
MDynTopoVert *mv = BKE_PBVH_DYNVERT(cd_dyn_vert, v);
|
||||
|
||||
float *col = BM_ELEM_CD_GET_VOID_P(v, cd_temp);
|
||||
float dir[3];
|
||||
float dir3[3] = {0.0f, 0.0f, 0.0f};
|
||||
|
||||
copy_v3_v3(dir, col);
|
||||
|
||||
if (dot_v3v3(dir, dir) == 0.0f) {
|
||||
copy_v3_v3(dir, direction);
|
||||
}
|
||||
else {
|
||||
closest_vec_to_perp(dir, direction, v->no, buckets, 1.0f); // col[3]);
|
||||
}
|
||||
|
||||
float totdir3 = 0.0f;
|
||||
|
||||
const float selfw = (float)mv->valence * 0.25f;
|
||||
madd_v3_v3fl(dir3, direction, selfw);
|
||||
totdir3 += selfw;
|
||||
|
||||
BMIter eiter;
|
||||
BMEdge *e;
|
||||
bool had_bound = false;
|
||||
|
||||
BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
|
||||
if (BM_edge_is_boundary(e)) {
|
||||
copy_v3_v3(avg, v->co);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(cd_dyn_vert, v_other);
|
||||
// bool bound = (mv2->flag &
|
||||
// (DYNVERT_BOUNDARY)); // | DYNVERT_FSET_BOUNDARY | DYNVERT_SHARP_BOUNDARY));
|
||||
// bool bound2 = (mv2->flag &
|
||||
// (DYNVERT_BOUNDARY | DYNVERT_FSET_BOUNDARY | DYNVERT_SHARP_BOUNDARY));
|
||||
|
||||
SculptBoundaryType bflag = SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_MESH |
|
||||
SCULPT_BOUNDARY_SHARP | SCULPT_BOUNDARY_SEAM;
|
||||
|
||||
int bound = SCULPT_edge_is_boundary(ss, (SculptEdgeRef){.i = (intptr_t)e}, bflag);
|
||||
float dirw = 1.0f;
|
||||
|
||||
if (bound) {
|
||||
had_bound = true;
|
||||
|
||||
sub_v3_v3v3(dir2, v_other->co, v->co);
|
||||
madd_v3_v3fl(dir2, v->no, -dot_v3v3(v->no, dir2));
|
||||
normalize_v3(dir2);
|
||||
dirw = 100000.0f;
|
||||
}
|
||||
else {
|
||||
dirw = col2[3];
|
||||
|
||||
copy_v3_v3(dir2, col2);
|
||||
if (dot_v3v3(dir2, dir2) == 0.0f) {
|
||||
copy_v3_v3(dir2, dir);
|
||||
}
|
||||
}
|
||||
|
||||
closest_vec_to_perp(dir, dir2, v->no, buckets, 1.0f); // col2[3]);
|
||||
|
||||
madd_v3_v3fl(dir3, dir2, dirw);
|
||||
totdir3 += dirw;
|
||||
|
||||
if (had_bound) {
|
||||
tot_co = 0.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
float vec[3];
|
||||
sub_v3_v3v3(vec, v_other->co, v->co);
|
||||
|
||||
|
@ -390,7 +524,7 @@ void SCULPT_bmesh_four_neighbor_average(
|
|||
|
||||
/* fac is a measure of how orthogonal or parallel the edge is
|
||||
* relative to the direction. */
|
||||
float fac = dot_v3v3(vec, direction);
|
||||
float fac = dot_v3v3(vec, dir);
|
||||
fac = fac * fac - 0.5f;
|
||||
fac *= fac;
|
||||
madd_v3_v3fl(avg_co, v_other->co, fac);
|
||||
|
@ -409,7 +543,40 @@ void SCULPT_bmesh_four_neighbor_average(
|
|||
add_v3_v3(avg, v->co);
|
||||
}
|
||||
else {
|
||||
zero_v3(avg);
|
||||
// zero_v3(avg);
|
||||
copy_v3_v3(avg, v->co);
|
||||
}
|
||||
|
||||
if (totdir3 > 0.0f) {
|
||||
float outdir = totdir3 / (float)mv->valence;
|
||||
|
||||
// mul_v3_fl(dir3, 1.0 / totdir3);
|
||||
normalize_v3(dir3);
|
||||
if (had_bound) {
|
||||
copy_v3_v3(col, dir3);
|
||||
col[3] = 1000.0f;
|
||||
}
|
||||
else {
|
||||
|
||||
mul_v3_fl(col, col[3]);
|
||||
madd_v3_v3fl(col, dir3, outdir);
|
||||
|
||||
col[3] = (col[3] + outdir) * 0.4;
|
||||
normalize_v3(col);
|
||||
}
|
||||
|
||||
float maxb = 0.0f;
|
||||
int bi = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (buckets[i] > maxb) {
|
||||
maxb = buckets[i];
|
||||
bi = i;
|
||||
}
|
||||
}
|
||||
|
||||
negate_v3(col);
|
||||
vec_transform(col, v->no, bi);
|
||||
negate_v3(col);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -796,6 +963,8 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
|
|||
|
||||
bool modified = false;
|
||||
const float bound_smooth = powf(ss->cache->brush->boundary_smooth_factor, BOUNDARY_SMOOTH_EXP);
|
||||
const float slide_fset = ss->cache->brush->autosmooth_fset_slide;
|
||||
|
||||
SculptCustomLayer *bound_scl = data->scl2;
|
||||
|
||||
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
|
||||
|
@ -821,8 +990,8 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
|
|||
else {
|
||||
float avg[3], val[3];
|
||||
|
||||
if (bound_smooth == 0.0f && SCULPT_vertex_is_corner(ss, vd.vertex, ctype)) {
|
||||
continue;
|
||||
if (SCULPT_vertex_is_corner(ss, vd.vertex, ctype) & ~SCULPT_CORNER_FACE_SET) {
|
||||
// continue;
|
||||
}
|
||||
|
||||
SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex, projection, bound_scl);
|
||||
|
|
|
@ -416,7 +416,10 @@ typedef enum eBrushFlags2 {
|
|||
BRUSH_SMOOTH_USE_AREA_WEIGHT = (1 << 13),
|
||||
|
||||
/*preserve face set boundaries*/
|
||||
BRUSH_SMOOTH_PRESERVE_FACE_SETS = (1 << 14)
|
||||
BRUSH_SMOOTH_PRESERVE_FACE_SETS = (1 << 14),
|
||||
|
||||
/*topology rake in dynamic mode*/
|
||||
BRUSH_DYNAMIC_RAKE = (1<<15),
|
||||
} eBrushFlags2;
|
||||
|
||||
typedef enum {
|
||||
|
@ -626,7 +629,9 @@ enum {
|
|||
DYNTOPO_SUBDIVIDE = 1 << 0,
|
||||
DYNTOPO_COLLAPSE = 1 << 1,
|
||||
DYNTOPO_DISABLED = 1 << 2,
|
||||
DYNTOPO_CLEANUP = 1 << 3
|
||||
DYNTOPO_CLEANUP = 1 << 3,
|
||||
DYNTOPO_LOCAL_COLLAPSE = 1 << 4,
|
||||
DYNTOPO_LOCAL_SUBDIVIDE = 1 << 5
|
||||
};
|
||||
|
||||
// dyntopo override flags, copies all flags from dyntopo flags
|
||||
|
|
|
@ -283,13 +283,14 @@ typedef struct Brush {
|
|||
char gpencil_sculpt_tool;
|
||||
/** Active grease pencil weight tool. */
|
||||
char gpencil_weight_tool;
|
||||
char _pad1[2];
|
||||
char _pad1[6];
|
||||
|
||||
float autosmooth_factor;
|
||||
float autosmooth_radius_factor;
|
||||
float autosmooth_projection;
|
||||
int autosmooth_spacing; // spacing for BRUSH_CUSTOM_AUTOSMOOTH_SPACING
|
||||
float boundary_smooth_factor;
|
||||
float autosmooth_fset_slide;
|
||||
|
||||
float tilt_strength_factor;
|
||||
|
||||
|
|
|
@ -2242,6 +2242,8 @@ typedef enum eSculptFlags {
|
|||
|
||||
// hides facesets/masks and forces indexed mode to save GPU bandwidth
|
||||
SCULPT_FAST_DRAW = (1 << 20),
|
||||
SCULPT_DYNTOPO_LOCAL_SUBDIVIDE = (1 << 21),
|
||||
SCULPT_DYNTOPO_LOCAL_COLLAPSE = (1 << 22)
|
||||
} eSculptFlags;
|
||||
|
||||
/** #ImagePaintSettings.mode */
|
||||
|
|
|
@ -356,6 +356,8 @@ static EnumPropertyItem rna_enum_brush_dyntopo_flag[] = {
|
|||
{DYNTOPO_SUBDIVIDE, "SUBDIVIDE", ICON_NONE, "Subdivide", ""},
|
||||
{DYNTOPO_COLLAPSE, "COLLAPSE", ICON_NONE, "Collapse", ""},
|
||||
{DYNTOPO_DISABLED, "DISABLED", ICON_NONE, "Disable", ""},
|
||||
{DYNTOPO_LOCAL_COLLAPSE, "LOCAL_COLLAPSE", ICON_NONE, "Local Collapse", ""},
|
||||
{DYNTOPO_LOCAL_SUBDIVIDE, "LOCAL_SUBDIVIDE", ICON_NONE, "Local Subdivide", ""},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
|
@ -363,6 +365,8 @@ static EnumPropertyItem rna_enum_brush_dyntopo_inherit[] = {
|
|||
{DYNTOPO_SUBDIVIDE, "SUBDIVIDE", ICON_NONE, "Subdivide", ""},
|
||||
{DYNTOPO_COLLAPSE, "COLLAPSE", ICON_NONE, "Collapse", ""},
|
||||
{DYNTOPO_DISABLED, "DISABLED", ICON_NONE, "Disable", ""},
|
||||
{DYNTOPO_LOCAL_COLLAPSE, "LOCAL_COLLAPSE", ICON_NONE, "Local Collapse", ""},
|
||||
{DYNTOPO_LOCAL_SUBDIVIDE, "LOCAL_SUBDIVIDE", ICON_NONE, "Local Subdivide", ""},
|
||||
{DYNTOPO_INHERIT_ALL, "ALL", ICON_NONE, "All", "Inherit All"},
|
||||
{DYNTOPO_INHERIT_DETAIL_RANGE, "DETAIL_RANGE", ICON_NONE, "All", ""},
|
||||
{DYNTOPO_INHERIT_DETAIL_PERCENT, "DETAIL_PERCENT", ICON_NONE, "Percent", ""},
|
||||
|
@ -1248,6 +1252,26 @@ static void rna_def_dyntopo_settings(BlenderRNA *brna)
|
|||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update");
|
||||
|
||||
prop = RNA_def_property(srna, "local_collapse", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", DYNTOPO_LOCAL_COLLAPSE);
|
||||
RNA_def_property_ui_icon(prop, ICON_NONE, 0);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Local Collapse",
|
||||
"When collapse is disabled, collapse anyway based on local edge lengths under brush");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update");
|
||||
|
||||
prop = RNA_def_property(srna, "local_subdivide", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", DYNTOPO_LOCAL_SUBDIVIDE);
|
||||
RNA_def_property_ui_icon(prop, ICON_NONE, 0);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Local Subdivide",
|
||||
"When subdivide is disabled, subdivide anyway based on local edge lengths under brush");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update");
|
||||
|
||||
prop = RNA_def_property(srna, "mode", PROP_ENUM, 0);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "mode");
|
||||
RNA_def_property_enum_items(prop, rna_enum_brush_dyntopo_mode);
|
||||
|
@ -3107,6 +3131,15 @@ static void rna_def_brush(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(prop, "Boundary Smoothing", "How much to smooth sharp boundaries ");
|
||||
RNA_def_property_update(prop, 0, "rna_Brush_update");
|
||||
|
||||
prop = RNA_def_property(srna, "autosmooth_fset_slide", PROP_FLOAT, PROP_FACTOR);
|
||||
RNA_def_property_float_sdna(prop, NULL, "autosmooth_fset_slide");
|
||||
RNA_def_property_float_default(prop, 0);
|
||||
RNA_def_property_range(prop, -2.0f, 2.0f);
|
||||
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Face Set Slide", "Slide face set boundaries instead of sharpening them");
|
||||
RNA_def_property_update(prop, 0, "rna_Brush_update");
|
||||
|
||||
prop = RNA_def_property(srna, "vcol_boundary_exponent", PROP_FLOAT, PROP_FACTOR);
|
||||
RNA_def_property_float_sdna(prop, NULL, "vcol_boundary_exponent");
|
||||
RNA_def_property_float_default(prop, 0);
|
||||
|
|
Loading…
Reference in New Issue