Sculpt dyntopo:
* Got automasking to work with dyntopo properly. - AutomaskingCache->factor has been replaced with the new temp layer API (which works for all PBVH modes). - AutomaskingCache->factor is, however, only initialized for topology and face set boundary modes (if DynTopo is enabled) since it's probably better to calculate the rest dynamically in that case. * Fixed stats bug
This commit is contained in:
parent
ca4ac36c59
commit
17d4c7abb1
|
@ -391,6 +391,9 @@ typedef enum {
|
|||
PBVH_Collapse = 2,
|
||||
PBVH_Cleanup = 4, // dissolve verts surrounded by either 3 or 4 triangles then triangulate
|
||||
} PBVHTopologyUpdateMode;
|
||||
|
||||
typedef float (*DyntopoMaskCB)(SculptVertRef vertex, void *userdata);
|
||||
|
||||
bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
|
||||
PBVHTopologyUpdateMode mode,
|
||||
const float center[3],
|
||||
|
@ -399,7 +402,9 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
|
|||
const bool use_frontface,
|
||||
const bool use_projected,
|
||||
int symaxis,
|
||||
bool updatePBVH);
|
||||
bool updatePBVH,
|
||||
DyntopoMaskCB mask_cb,
|
||||
void *mask_cb_data);
|
||||
|
||||
bool BKE_pbvh_bmesh_update_topology_nodes(PBVH *pbvh,
|
||||
bool (*searchcb)(PBVHNode *node, void *data),
|
||||
|
@ -412,7 +417,9 @@ bool BKE_pbvh_bmesh_update_topology_nodes(PBVH *pbvh,
|
|||
const bool use_frontface,
|
||||
const bool use_projected,
|
||||
int sym_axis,
|
||||
bool updatePBVH);
|
||||
bool updatePBVH,
|
||||
DyntopoMaskCB mask_cb,
|
||||
void *mask_cb_data);
|
||||
/* Node Access */
|
||||
|
||||
void BKE_pbvh_node_mark_update(PBVHNode *node);
|
||||
|
|
|
@ -1349,6 +1349,8 @@ typedef struct {
|
|||
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;
|
||||
|
@ -1359,6 +1361,21 @@ typedef struct {
|
|||
float totedge;
|
||||
} EdgeQueueContext;
|
||||
|
||||
BLI_INLINE float maskcb_get(EdgeQueueContext *eq_ctx, BMEdge *e)
|
||||
{
|
||||
if (eq_ctx->mask_cb) {
|
||||
SculptVertRef sv1 = {(intptr_t)e->v1};
|
||||
SculptVertRef sv2 = {(intptr_t)e->v2};
|
||||
|
||||
float w1 = eq_ctx->mask_cb(sv1, eq_ctx->mask_cb_data);
|
||||
float w2 = eq_ctx->mask_cb(sv2, eq_ctx->mask_cb_data);
|
||||
|
||||
return (w1 + w2) * 0.5f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
BLI_INLINE float calc_weighted_edge_split(EdgeQueueContext *eq_ctx, BMVert *v1, BMVert *v2)
|
||||
{
|
||||
#ifdef FANCY_EDGE_WEIGHTS
|
||||
|
@ -1480,10 +1497,6 @@ static float dist_to_tri_sphere_simple(
|
|||
|
||||
static bool edge_queue_tri_in_sphere(const EdgeQueue *q, BMFace *f)
|
||||
{
|
||||
float c[3];
|
||||
float v1[3], v2[3], v3[3], co[3];
|
||||
const float mul = 1.0f;
|
||||
|
||||
BMLoop *l = f->l_first;
|
||||
|
||||
/* Check if triangle intersects the sphere */
|
||||
|
@ -1551,29 +1564,14 @@ static bool edge_queue_vert_in_circle(const EdgeQueue *q, BMVert *v)
|
|||
return len_squared_v3v3(q->center_proj, c) <= q->radius_squared;
|
||||
}
|
||||
|
||||
/* Return true if the vertex mask is less than 1.0, false otherwise */
|
||||
static bool check_mask(EdgeQueueContext *eq_ctx, BMVert *v)
|
||||
{
|
||||
return DYNTOPO_MASK(eq_ctx->cd_dyn_vert, v) < 1.0f;
|
||||
}
|
||||
|
||||
static void edge_queue_insert(EdgeQueueContext *eq_ctx, BMEdge *e, float priority)
|
||||
{
|
||||
void **elems = eq_ctx->q->elems;
|
||||
BLI_array_declare(elems);
|
||||
BLI_array_len_set(elems, eq_ctx->q->totelems);
|
||||
|
||||
/* Don't let topology update affect fully masked vertices. This used to
|
||||
* have a 50% mask cutoff, with the reasoning that you can't do a 50%
|
||||
* topology update. But this gives an ugly border in the mesh. The mask
|
||||
* should already make the brush move the vertices only 50%, which means
|
||||
* that topology updates will also happen less frequent, that should be
|
||||
* enough. */
|
||||
if (((eq_ctx->cd_vert_mask_offset == -1) ||
|
||||
(check_mask(eq_ctx, e->v1) || check_mask(eq_ctx, e->v2))) &&
|
||||
!(BM_elem_flag_test_bool(e->v1, BM_ELEM_HIDDEN) ||
|
||||
BM_elem_flag_test_bool(e->v2, BM_ELEM_HIDDEN))) {
|
||||
|
||||
if (eq_ctx->cd_vert_mask_offset == -1 ||
|
||||
!((e->v1->head.hflag | e->v2->head.hflag) & BM_ELEM_HIDDEN)) {
|
||||
float dis = len_v3v3(e->v1->co, e->v2->co);
|
||||
eq_ctx->avg_elen += dis;
|
||||
eq_ctx->max_elen = MAX2(eq_ctx->max_elen, dis);
|
||||
|
@ -1604,7 +1602,9 @@ static void long_edge_queue_edge_add(EdgeQueueContext *eq_ctx, BMEdge *e)
|
|||
if (EDGE_QUEUE_TEST(e) == false)
|
||||
#endif
|
||||
{
|
||||
const float len_sq = BM_edge_calc_length_squared(e);
|
||||
const float w = maskcb_get(eq_ctx, 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);
|
||||
}
|
||||
|
@ -1652,6 +1652,10 @@ static void long_edge_queue_edge_add_recursive(EdgeQueueContext *eq_ctx,
|
|||
BMLoop *l_adjacent[2] = {l_iter->next, l_iter->prev};
|
||||
for (int i = 0; i < (int)ARRAY_SIZE(l_adjacent); i++) {
|
||||
float len_sq_other = BM_edge_calc_length_squared(l_adjacent[i]->e);
|
||||
float w = maskcb_get(eq_ctx, l_adjacent[i]->e);
|
||||
|
||||
len_sq_other *= w * w;
|
||||
|
||||
if (len_sq_other > max_ff(len_sq_cmp, limit_len_sq)) {
|
||||
// edge_queue_insert(eq_ctx, l_adjacent[i]->e, -len_sq_other);
|
||||
long_edge_queue_edge_add_recursive(eq_ctx,
|
||||
|
@ -1696,7 +1700,11 @@ static void long_edge_queue_face_add(EdgeQueueContext *eq_ctx, BMFace *f, bool i
|
|||
BMLoop *l_iter = l_first;
|
||||
do {
|
||||
#ifdef USE_EDGEQUEUE_EVEN_SUBDIV
|
||||
const float len_sq = BM_edge_calc_length_squared(l_iter->e);
|
||||
float len_sq = BM_edge_calc_length_squared(l_iter->e);
|
||||
float w = maskcb_get(eq_ctx, l_iter->e);
|
||||
|
||||
len_sq *= w * w;
|
||||
|
||||
if (len_sq > eq_ctx->q->limit_len_squared) {
|
||||
long_edge_queue_edge_add_recursive(eq_ctx,
|
||||
l_iter->radial_next,
|
||||
|
@ -1829,12 +1837,16 @@ static void long_edge_queue_edge_add_recursive_2(EdgeQueueThreadData *tdata,
|
|||
do {
|
||||
BMLoop *l_adjacent[2] = {l_iter->next, l_iter->prev};
|
||||
for (int i = 0; i < (int)ARRAY_SIZE(l_adjacent); i++) {
|
||||
BMEdge *e = l_adjacent[i]->e;
|
||||
|
||||
float len_sq_other = calc_weighted_edge_split(
|
||||
tdata->eq_ctx, l_adjacent[i]->e->v1, l_adjacent[i]->e->v2);
|
||||
|
||||
float w = maskcb_get(tdata->eq_ctx, e);
|
||||
|
||||
len_sq_other *= w * w;
|
||||
|
||||
if (len_sq_other > max_ff(len_sq_cmp, limit_len_sq)) {
|
||||
// edge_queue_insert(eq_ctx, l_adjacent[i]->e, -len_sq_other);
|
||||
long_edge_queue_edge_add_recursive_2(tdata,
|
||||
l_adjacent[i]->radial_next,
|
||||
l_adjacent[i],
|
||||
|
@ -1886,7 +1898,11 @@ void long_edge_queue_task_cb(void *__restrict userdata,
|
|||
surface_smooth_v_safe(l_iter->v);
|
||||
|
||||
#ifdef USE_EDGEQUEUE_EVEN_SUBDIV
|
||||
const float len_sq = BM_edge_calc_length_squared(l_iter->e);
|
||||
float w = maskcb_get(eq_ctx, l_iter->e);
|
||||
float len_sq = BM_edge_calc_length_squared(l_iter->e);
|
||||
|
||||
len_sq *= w * w;
|
||||
|
||||
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);
|
||||
|
@ -1903,9 +1919,9 @@ void long_edge_queue_task_cb(void *__restrict userdata,
|
|||
TGSET_ITER_END
|
||||
}
|
||||
|
||||
void short_edge_queue_task_cb(void *__restrict userdata,
|
||||
const int n,
|
||||
const TaskParallelTLS *__restrict tls)
|
||||
static void short_edge_queue_task_cb(void *__restrict userdata,
|
||||
const int n,
|
||||
const TaskParallelTLS *__restrict tls)
|
||||
{
|
||||
EdgeQueueThreadData *tdata = ((EdgeQueueThreadData *)userdata) + n;
|
||||
PBVHNode *node = tdata->node;
|
||||
|
@ -1937,8 +1953,16 @@ void short_edge_queue_task_cb(void *__restrict userdata,
|
|||
BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
|
||||
BMLoop *l_iter = l_first;
|
||||
do {
|
||||
float w = maskcb_get(eq_ctx, l_iter->e);
|
||||
|
||||
if (w == 0.0f) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef USE_EDGEQUEUE_EVEN_SUBDIV
|
||||
const float len_sq = calc_weighted_edge_collapse(eq_ctx, l_iter->e->v1, l_iter->e->v2);
|
||||
float len_sq = calc_weighted_edge_collapse(eq_ctx, l_iter->e->v1, l_iter->e->v2);
|
||||
len_sq /= w * w;
|
||||
|
||||
if (len_sq < eq_ctx->q->limit_len_squared) {
|
||||
short_edge_queue_edge_add_recursive_2(
|
||||
tdata, l_iter->radial_next, l_iter, len_sq, eq_ctx->q->limit_len, 0);
|
||||
|
@ -2047,7 +2071,13 @@ static void long_edge_queue_create(EdgeQueueContext *eq_ctx,
|
|||
for (int j = 0; j < td->totedge; j++) {
|
||||
BMEdge *e = edges[j];
|
||||
e->head.hflag &= ~BM_ELEM_TAG;
|
||||
edge_queue_insert(eq_ctx, e, -calc_weighted_edge_split(eq_ctx, e->v1, e->v2));
|
||||
|
||||
float w = -calc_weighted_edge_split(eq_ctx, e->v1, e->v2);
|
||||
float w2 = maskcb_get(eq_ctx, e);
|
||||
|
||||
w *= w2 * w2;
|
||||
|
||||
edge_queue_insert(eq_ctx, e, w);
|
||||
}
|
||||
|
||||
if (td->edges) {
|
||||
|
@ -2145,8 +2175,18 @@ static void short_edge_queue_create(EdgeQueueContext *eq_ctx,
|
|||
BMEdge **edges = td->edges;
|
||||
for (int j = 0; j < td->totedge; j++) {
|
||||
BMEdge *e = edges[j];
|
||||
float w = calc_weighted_edge_collapse(eq_ctx, e->v1, e->v2);
|
||||
float w2 = maskcb_get(eq_ctx, e);
|
||||
|
||||
if (w2 > 0.0f) {
|
||||
w /= w2 * w2;
|
||||
}
|
||||
else {
|
||||
w = 100000.0f;
|
||||
}
|
||||
|
||||
e->head.hflag &= ~BM_ELEM_TAG;
|
||||
edge_queue_insert(eq_ctx, e, calc_weighted_edge_collapse(eq_ctx, e->v1, e->v2));
|
||||
edge_queue_insert(eq_ctx, e, w);
|
||||
}
|
||||
|
||||
if (td->edges) {
|
||||
|
@ -2213,7 +2253,7 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx,
|
|||
|
||||
float mask_v_new = 0.5f * (mask_v1 + mask_v2);
|
||||
|
||||
BM_ELEM_CD_SET_FLOAT(v_new, eq_ctx->cd_vert_mask_offset, mask_v_new);
|
||||
// BM_ELEM_CD_SET_FLOAT(v_new, eq_ctx->cd_vert_mask_offset, mask_v_new);
|
||||
}
|
||||
|
||||
/* For each face, add two new triangles and delete the original */
|
||||
|
@ -2462,9 +2502,6 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
|
|||
{
|
||||
BMVert *v_del, *v_conn;
|
||||
|
||||
MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v1);
|
||||
MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v2);
|
||||
|
||||
// customdata interpolation
|
||||
if (BM_elem_flag_test(e, BM_ELEM_SEAM)) {
|
||||
for (int step = 0; step < 2; step++) {
|
||||
|
@ -2768,8 +2805,6 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
|
|||
void BKE_pbvh_bmesh_update_origvert(
|
||||
PBVH *pbvh, BMVert *v, float **r_co, float **r_no, float **r_color, bool log_undo)
|
||||
{
|
||||
float *co = NULL, *no = NULL;
|
||||
|
||||
MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
|
||||
|
||||
if (log_undo) {
|
||||
|
@ -3038,9 +3073,6 @@ bool BKE_pbvh_bmesh_node_raycast_detail(PBVH *pbvh,
|
|||
return false;
|
||||
}
|
||||
|
||||
bool hit = false;
|
||||
BMFace *f_hit = NULL;
|
||||
|
||||
BKE_pbvh_bmesh_check_tris(pbvh, node);
|
||||
for (int i = 0; i < node->tribuf->tottri; i++) {
|
||||
PBVHTri *tri = node->tribuf->tris + i;
|
||||
|
@ -3199,7 +3231,7 @@ void pbvh_bmesh_normals_update(PBVHNode **nodes, int totnode)
|
|||
#endif
|
||||
}
|
||||
|
||||
void pbvh_bmesh_normals_update_old(PBVHNode **nodes, int totnode)
|
||||
static void pbvh_bmesh_normals_update_old(PBVHNode **nodes, int totnode)
|
||||
{
|
||||
for (int n = 0; n < totnode; n++) {
|
||||
PBVHNode *node = nodes[n];
|
||||
|
@ -3552,9 +3584,11 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh,
|
|||
MEM_freeN(nodeinfo);
|
||||
}
|
||||
|
||||
/*
|
||||
static double last_update_time[128] = {
|
||||
0,
|
||||
};
|
||||
*/
|
||||
|
||||
bool BKE_pbvh_bmesh_update_topology_nodes(PBVH *pbvh,
|
||||
bool (*searchcb)(PBVHNode *node, void *data),
|
||||
|
@ -3567,7 +3601,9 @@ bool BKE_pbvh_bmesh_update_topology_nodes(PBVH *pbvh,
|
|||
const bool use_frontface,
|
||||
const bool use_projected,
|
||||
int sym_axis,
|
||||
bool updatePBVH)
|
||||
bool updatePBVH,
|
||||
DyntopoMaskCB mask_cb,
|
||||
void *mask_cb_data)
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
|
@ -3594,7 +3630,10 @@ bool BKE_pbvh_bmesh_update_topology_nodes(PBVH *pbvh,
|
|||
use_frontface,
|
||||
use_projected,
|
||||
sym_axis,
|
||||
updatePBVH);
|
||||
updatePBVH,
|
||||
mask_cb,
|
||||
mask_cb_data);
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
|
@ -3785,7 +3824,9 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
|
|||
const bool use_frontface,
|
||||
const bool use_projected,
|
||||
int sym_axis,
|
||||
bool updatePBVH)
|
||||
bool updatePBVH,
|
||||
DyntopoMaskCB mask_cb,
|
||||
void *mask_cb_data)
|
||||
{
|
||||
/*
|
||||
if (sym_axis >= 0 &&
|
||||
|
@ -3816,6 +3857,8 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
|
|||
NULL,
|
||||
NULL,
|
||||
pbvh->bm,
|
||||
mask_cb,
|
||||
mask_cb_data,
|
||||
|
||||
cd_dyn_vert,
|
||||
cd_vert_mask_offset,
|
||||
|
@ -3900,6 +3943,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
|
|||
int max_steps = (int)((float)DYNTOPO_MAX_ITER * ratio);
|
||||
|
||||
modified |= pbvh_bmesh_subdivide_long_edges(&eq_ctx, pbvh, &edge_loops, max_steps);
|
||||
|
||||
if (q.elems) {
|
||||
MEM_freeN(q.elems);
|
||||
}
|
||||
|
@ -4146,10 +4190,6 @@ void BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node)
|
|||
INIT_MINMAX(min, max);
|
||||
|
||||
TGSET_ITER (f, node->bm_faces) {
|
||||
BMVert *v1 = f->l_first->v;
|
||||
BMVert *v2 = f->l_first->next->v;
|
||||
BMVert *v3 = f->l_first->prev->v;
|
||||
|
||||
PBVHTri tri = {0};
|
||||
|
||||
BMLoop *l = f->l_first;
|
||||
|
@ -4185,7 +4225,7 @@ void BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node)
|
|||
}
|
||||
TGSET_ITER_END
|
||||
|
||||
bm->elem_index_dirty | BM_VERT;
|
||||
bm->elem_index_dirty |= BM_VERT;
|
||||
|
||||
node->tribuf->tris = tris;
|
||||
node->tribuf->tottri = BLI_array_len(tris);
|
||||
|
|
|
@ -6652,6 +6652,67 @@ int SCULPT_get_symmetry_pass(const SculptSession *ss)
|
|||
|
||||
return symidx;
|
||||
}
|
||||
|
||||
typedef struct DynTopoAutomaskState {
|
||||
AutomaskingCache *cache;
|
||||
SculptSession *ss;
|
||||
} DynTopoAutomaskState;
|
||||
|
||||
ATTR_NO_OPT static float sculpt_topology_automasking_cb(SculptVertRef vertex, void *vdata)
|
||||
{
|
||||
DynTopoAutomaskState *state = (DynTopoAutomaskState *)vdata;
|
||||
float mask = SCULPT_automasking_factor_get(state->cache, state->ss, vertex);
|
||||
float mask2 = 1.0f - SCULPT_vertex_mask_get(state->ss, vertex);
|
||||
|
||||
return mask * mask2;
|
||||
}
|
||||
|
||||
ATTR_NO_OPT static float sculpt_topology_automasking_mask_cb(SculptVertRef vertex, void *vdata)
|
||||
{
|
||||
DynTopoAutomaskState *state = (DynTopoAutomaskState *)vdata;
|
||||
return 1.0f - SCULPT_vertex_mask_get(state->ss, vertex);
|
||||
}
|
||||
|
||||
bool SCULPT_dyntopo_automasking_init(const SculptSession *ss,
|
||||
const Brush *br,
|
||||
const Sculpt *sd,
|
||||
DyntopoMaskCB *r_mask_cb,
|
||||
void **r_mask_cb_data)
|
||||
{
|
||||
if (!SCULPT_is_automasking_enabled(sd, ss, br)) {
|
||||
if (CustomData_has_layer(&ss->bm->vdata, CD_PAINT_MASK)) {
|
||||
DynTopoAutomaskState *state = MEM_callocN(sizeof(DynTopoAutomaskState),
|
||||
"DynTopoAutomaskState");
|
||||
state->cache = ss->cache->automasking;
|
||||
state->ss = (SculptSession *)ss;
|
||||
|
||||
*r_mask_cb_data = (void *)state;
|
||||
*r_mask_cb = sculpt_topology_automasking_mask_cb;
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
*r_mask_cb = NULL;
|
||||
*r_mask_cb_data = NULL;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DynTopoAutomaskState *state = MEM_callocN(sizeof(DynTopoAutomaskState), "DynTopoAutomaskState");
|
||||
state->cache = ss->cache->automasking;
|
||||
state->ss = (SculptSession *)ss;
|
||||
|
||||
*r_mask_cb_data = (void *)state;
|
||||
*r_mask_cb = sculpt_topology_automasking_cb;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SCULPT_dyntopo_automasking_end(void *mask_data)
|
||||
{
|
||||
MEM_SAFE_FREE(mask_data);
|
||||
}
|
||||
|
||||
/* Note: we do the topology update before any brush actions to avoid
|
||||
* issues with the proxies. The size of the proxy can't change, so
|
||||
* topology must be updated first. */
|
||||
|
@ -6714,6 +6775,10 @@ static void sculpt_topology_update(Sculpt *sd,
|
|||
int symidx = SCULPT_get_symmetry_pass(ss);
|
||||
|
||||
bool modified;
|
||||
void *mask_cb_data;
|
||||
DyntopoMaskCB mask_cb;
|
||||
|
||||
SCULPT_dyntopo_automasking_init(ss, brush, sd, &mask_cb, &mask_cb_data);
|
||||
|
||||
/* do nodes under the brush cursor */
|
||||
modified = BKE_pbvh_bmesh_update_topology_nodes(
|
||||
|
@ -6728,7 +6793,11 @@ static void sculpt_topology_update(Sculpt *sd,
|
|||
(brush->flag & BRUSH_FRONTFACE) != 0,
|
||||
(brush->falloff_shape != PAINT_FALLOFF_SHAPE_SPHERE),
|
||||
symidx,
|
||||
DYNTOPO_HAS_DYNAMIC_SPLIT(brush->sculpt_tool));
|
||||
DYNTOPO_HAS_DYNAMIC_SPLIT(brush->sculpt_tool),
|
||||
mask_cb,
|
||||
mask_cb_data);
|
||||
|
||||
SCULPT_dyntopo_automasking_end(mask_cb_data);
|
||||
|
||||
/* Update average stroke position. */
|
||||
copy_v3_v3(location, ss->cache->true_location);
|
||||
|
@ -7355,8 +7424,8 @@ void SCULPT_flush_stroke_deform(Sculpt *sd, Object *ob, bool is_proxy_used)
|
|||
MEM_SAFE_FREE(nodes);
|
||||
|
||||
/* Modifiers could depend on mesh normals, so we should update them.
|
||||
* Note, then if sculpting happens on locked key, normals should be re-calculate after applying
|
||||
* coords from key-block on base mesh. */
|
||||
* Note, then if sculpting happens on locked key, normals should be re-calculate after
|
||||
* applying coords from key-block on base mesh. */
|
||||
BKE_mesh_calc_normals(me);
|
||||
}
|
||||
else if (ss->shapekey_active) {
|
||||
|
@ -7953,8 +8022,8 @@ static float sculpt_brush_dynamic_size_get(Brush *brush, StrokeCache *cache, flo
|
|||
}
|
||||
}
|
||||
|
||||
/* In these brushes the grab delta is calculated always from the initial stroke location, which is
|
||||
* generally used to create grab deformations. */
|
||||
/* In these brushes the grab delta is calculated always from the initial stroke location, which
|
||||
* is generally used to create grab deformations. */
|
||||
static bool sculpt_needs_delta_from_anchored_origin(Brush *brush)
|
||||
{
|
||||
if (ELEM(brush->sculpt_tool,
|
||||
|
@ -8167,9 +8236,9 @@ static void sculpt_update_cache_paint_variants(StrokeCache *cache, const Brush *
|
|||
1.0f - cache->pressure :
|
||||
cache->pressure;
|
||||
|
||||
/* This makes wet mix more sensible in higher values, which allows to create brushes that have
|
||||
* a wider pressure range were they only blend colors without applying too much of the brush
|
||||
* color. */
|
||||
/* This makes wet mix more sensible in higher values, which allows to create brushes that
|
||||
* have a wider pressure range were they only blend colors without applying too much of the
|
||||
* brush color. */
|
||||
cache->paint_brush.wet_mix = 1.0f - pow2f(1.0f - cache->paint_brush.wet_mix);
|
||||
}
|
||||
|
||||
|
@ -8440,8 +8509,9 @@ float SCULPT_raycast_init(ViewContext *vc,
|
|||
return dist;
|
||||
}
|
||||
|
||||
/* Gets the normal, location and active vertex location of the geometry under the cursor. This also
|
||||
* updates the active vertex and cursor related data of the SculptSession using the mouse position
|
||||
/* Gets the normal, location and active vertex location of the geometry under the cursor. This
|
||||
* also updates the active vertex and cursor related data of the SculptSession using the mouse
|
||||
* position
|
||||
*/
|
||||
bool SCULPT_cursor_geometry_info_update(bContext *C,
|
||||
SculptCursorGeometryInfo *out,
|
||||
|
@ -9446,10 +9516,10 @@ static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene,
|
|||
/* Here we can detect geometry that was just added to Sculpt Mode as it has the
|
||||
* SCULPT_FACE_SET_NONE assigned, so we can create a new Face Set for it. */
|
||||
/* In sculpt mode all geometry that is assigned to SCULPT_FACE_SET_NONE is considered as not
|
||||
* initialized, which is used is some operators that modify the mesh topology to perform certain
|
||||
* actions in the new polys. After these operations are finished, all polys should have a valid
|
||||
* face set ID assigned (different from SCULPT_FACE_SET_NONE) to manage their visibility
|
||||
* correctly. */
|
||||
* initialized, which is used is some operators that modify the mesh topology to perform
|
||||
* certain actions in the new polys. After these operations are finished, all polys should have
|
||||
* a valid face set ID assigned (different from SCULPT_FACE_SET_NONE) to manage their
|
||||
* visibility correctly. */
|
||||
/* TODO(pablodp606): Based on this we can improve the UX in future tools for creating new
|
||||
* objects, like moving the transform pivot position to the new area or masking existing
|
||||
* geometry. */
|
||||
|
@ -10124,7 +10194,8 @@ void SCULPT_connected_components_ensure(Object *ob)
|
|||
|
||||
SCULPT_vertex_random_access_ensure(ss);
|
||||
|
||||
/* Topology IDs already initialized. They only need to be recalculated when the PBVH is rebuild.
|
||||
/* Topology IDs already initialized. They only need to be recalculated when the PBVH is
|
||||
* rebuild.
|
||||
*/
|
||||
if (ss->vertex_info.connected_component) {
|
||||
return;
|
||||
|
@ -10201,7 +10272,8 @@ void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist)
|
|||
SculptSession *ss = ob->sculpt;
|
||||
const int totvert = SCULPT_vertex_count_get(ss);
|
||||
|
||||
/* Fake neighbors were already initialized with the same distance, so no need to be recalculated.
|
||||
/* Fake neighbors were already initialized with the same distance, so no need to be
|
||||
* recalculated.
|
||||
*/
|
||||
if (ss->fake_neighbors.fake_neighbor_index &&
|
||||
ss->fake_neighbors.current_max_distance == max_dist) {
|
||||
|
@ -10491,8 +10563,8 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven
|
|||
|
||||
SCULPT_vertex_random_access_ensure(ss);
|
||||
|
||||
/* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move,
|
||||
* so it needs to be updated here. */
|
||||
/* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse
|
||||
* move, so it needs to be updated here. */
|
||||
SculptCursorGeometryInfo sgi;
|
||||
float mouse[2];
|
||||
mouse[0] = event->mval[0];
|
||||
|
|
|
@ -85,9 +85,11 @@ bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd,
|
|||
|
||||
bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br)
|
||||
{
|
||||
/*
|
||||
if (br && SCULPT_stroke_is_dynamic_topology(ss, br)) {
|
||||
return false;
|
||||
}
|
||||
}*/
|
||||
|
||||
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_TOPOLOGY)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -118,6 +120,18 @@ static int sculpt_automasking_mode_effective_bits(const Sculpt *sculpt, const Br
|
|||
return sculpt->automasking_flags;
|
||||
}
|
||||
|
||||
static float sculpt_concavity_factor(AutomaskingCache *automasking, float fac)
|
||||
{
|
||||
if (automasking->settings.flags & BRUSH_AUTOMASKING_INVERT_CONCAVITY) {
|
||||
fac = 1.0 - fac;
|
||||
}
|
||||
|
||||
fac = pow(fac * 1.5f, (0.5f + automasking->settings.concave_factor) * 8.0);
|
||||
CLAMP(fac, 0.0f, 1.0f);
|
||||
|
||||
return fac;
|
||||
}
|
||||
|
||||
static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush *brush)
|
||||
{
|
||||
|
||||
|
@ -134,9 +148,9 @@ static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush
|
|||
return false;
|
||||
}
|
||||
|
||||
float SCULPT_automasking_factor_get(AutomaskingCache *automasking,
|
||||
SculptSession *ss,
|
||||
SculptVertRef vert)
|
||||
ATTR_NO_OPT float SCULPT_automasking_factor_get(AutomaskingCache *automasking,
|
||||
SculptSession *ss,
|
||||
SculptVertRef vert)
|
||||
{
|
||||
float mask = 1.0f;
|
||||
bool do_concave;
|
||||
|
@ -151,25 +165,20 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking,
|
|||
/* If the cache is initialized with valid info, use the cache. This is used when the
|
||||
* automasking information can't be computed in real time per vertex and needs to be
|
||||
* initialized for the whole mesh when the stroke starts. */
|
||||
if (automasking->factor) {
|
||||
mask = automasking->factor[BKE_pbvh_vertex_index_to_table(ss->pbvh, vert)];
|
||||
if (automasking->factorlayer) {
|
||||
mask = *(float *)SCULPT_temp_cdata_get(vert, automasking->factorlayer);
|
||||
}
|
||||
|
||||
// don't used cached automasking factors for facesets or concave in
|
||||
// dyntopo
|
||||
if (automasking->factorlayer && !ss->bm) {
|
||||
return mask;
|
||||
}
|
||||
|
||||
if (do_concave) {
|
||||
if (!automasking->factor) {
|
||||
mask = SCULPT_calc_concavity(ss, vert);
|
||||
}
|
||||
float fac = SCULPT_calc_concavity(ss, vert);
|
||||
|
||||
if (automasking->settings.flags & BRUSH_AUTOMASKING_INVERT_CONCAVITY) {
|
||||
mask = 1.0 - mask;
|
||||
}
|
||||
|
||||
mask = pow(mask*1.5f, (0.5f + automasking->settings.concave_factor) * 8.0);
|
||||
CLAMP(mask, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
if (automasking->factor) {
|
||||
return mask;
|
||||
mask *= sculpt_concavity_factor(automasking, fac);
|
||||
}
|
||||
|
||||
if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) {
|
||||
|
@ -199,7 +208,10 @@ void SCULPT_automasking_cache_free(AutomaskingCache *automasking)
|
|||
return;
|
||||
}
|
||||
|
||||
MEM_SAFE_FREE(automasking->factor);
|
||||
if (automasking->factorlayer) {
|
||||
MEM_SAFE_FREE(automasking->factorlayer);
|
||||
}
|
||||
|
||||
MEM_SAFE_FREE(automasking);
|
||||
}
|
||||
|
||||
|
@ -217,7 +229,7 @@ static bool sculpt_automasking_is_constrained_by_radius(Brush *br)
|
|||
}
|
||||
|
||||
typedef struct AutomaskFloodFillData {
|
||||
float *automask_factor;
|
||||
SculptCustomLayer *factorlayer;
|
||||
float radius;
|
||||
bool use_radius;
|
||||
float location[3];
|
||||
|
@ -232,26 +244,32 @@ static bool automask_floodfill_cb(SculptSession *ss,
|
|||
{
|
||||
AutomaskFloodFillData *data = userdata;
|
||||
|
||||
data->automask_factor[BKE_pbvh_vertex_index_to_table(ss->pbvh, to_vref)] = 1.0f;
|
||||
data->automask_factor[BKE_pbvh_vertex_index_to_table(ss->pbvh, from_vref)] = 1.0f;
|
||||
*(float *)SCULPT_temp_cdata_get(to_vref, data->factorlayer) = 1.0f;
|
||||
*(float *)SCULPT_temp_cdata_get(from_vref, data->factorlayer) = 1.0f;
|
||||
|
||||
return (!data->use_radius ||
|
||||
SCULPT_is_vertex_inside_brush_radius_symm(
|
||||
SCULPT_vertex_co_get(ss, to_vref), data->location, data->radius, data->symm));
|
||||
}
|
||||
|
||||
static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *automask_factor)
|
||||
static void SCULPT_topology_automasking_init(Sculpt *sd,
|
||||
Object *ob,
|
||||
SculptCustomLayer *factorlayer)
|
||||
{
|
||||
SculptSession *ss = ob->sculpt;
|
||||
Brush *brush = BKE_paint_brush(&sd->paint);
|
||||
|
||||
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
|
||||
BLI_assert(!"Topology masking: pmap missing");
|
||||
return NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
const int totvert = SCULPT_vertex_count_get(ss);
|
||||
for (int i = 0; i < totvert; i++) {
|
||||
automask_factor[i] = 0.0f;
|
||||
SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
|
||||
|
||||
float *fac = SCULPT_temp_cdata_get(vertex, factorlayer);
|
||||
*fac = 0.0f;
|
||||
}
|
||||
|
||||
/* Flood fill automask to connected vertices. Limited to vertices inside
|
||||
|
@ -262,7 +280,7 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au
|
|||
SCULPT_floodfill_add_active(sd, ob, ss, &flood, radius);
|
||||
|
||||
AutomaskFloodFillData fdata = {
|
||||
.automask_factor = automask_factor,
|
||||
.factorlayer = factorlayer,
|
||||
.radius = radius,
|
||||
.use_radius = ss->cache && sculpt_automasking_is_constrained_by_radius(brush),
|
||||
.symm = SCULPT_mesh_symmetry_xyz_get(ob),
|
||||
|
@ -270,22 +288,22 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au
|
|||
copy_v3_v3(fdata.location, SCULPT_active_vertex_co_get(ss));
|
||||
SCULPT_floodfill_execute(ss, &flood, automask_floodfill_cb, &fdata);
|
||||
SCULPT_floodfill_free(&flood);
|
||||
|
||||
return automask_factor;
|
||||
}
|
||||
|
||||
static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *automask_factor)
|
||||
static void sculpt_face_sets_automasking_init(Sculpt *sd,
|
||||
Object *ob,
|
||||
SculptCustomLayer *factorlayer)
|
||||
{
|
||||
SculptSession *ss = ob->sculpt;
|
||||
Brush *brush = BKE_paint_brush(&sd->paint);
|
||||
|
||||
if (!SCULPT_is_automasking_enabled(sd, ss, brush)) {
|
||||
return NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
|
||||
BLI_assert(!"Face Sets automasking: pmap missing");
|
||||
return NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
int tot_vert = SCULPT_vertex_count_get(ss);
|
||||
|
@ -294,25 +312,25 @@ static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *a
|
|||
SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
|
||||
|
||||
if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) {
|
||||
automask_factor[i] *= 0.0f;
|
||||
*(float *)SCULPT_temp_cdata_get(vertex, factorlayer) = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return automask_factor;
|
||||
return;
|
||||
}
|
||||
|
||||
#define EDGE_DISTANCE_INF -1
|
||||
|
||||
float *SCULPT_boundary_automasking_init(Object *ob,
|
||||
eBoundaryAutomaskMode mode,
|
||||
int propagation_steps,
|
||||
float *automask_factor)
|
||||
void SCULPT_boundary_automasking_init(Object *ob,
|
||||
eBoundaryAutomaskMode mode,
|
||||
int propagation_steps,
|
||||
SculptCustomLayer *factorlayer)
|
||||
{
|
||||
SculptSession *ss = ob->sculpt;
|
||||
|
||||
if (!ss->pmap) {
|
||||
BLI_assert(!"Boundary Edges masking: pmap missing");
|
||||
return NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
const int totvert = SCULPT_vertex_count_get(ss);
|
||||
|
@ -354,16 +372,18 @@ float *SCULPT_boundary_automasking_init(Object *ob,
|
|||
}
|
||||
|
||||
for (int i = 0; i < totvert; i++) {
|
||||
SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
|
||||
|
||||
if (edge_distance[i] == EDGE_DISTANCE_INF) {
|
||||
continue;
|
||||
}
|
||||
const float p = 1.0f - ((float)edge_distance[i] / (float)propagation_steps);
|
||||
const float edge_boundary_automask = pow2f(p);
|
||||
automask_factor[i] *= (1.0f - edge_boundary_automask);
|
||||
|
||||
*(float *)SCULPT_temp_cdata_get(vertex, factorlayer) *= (1.0f - edge_boundary_automask);
|
||||
}
|
||||
|
||||
MEM_SAFE_FREE(edge_distance);
|
||||
return automask_factor;
|
||||
}
|
||||
|
||||
static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automasking,
|
||||
|
@ -410,7 +430,10 @@ float SCULPT_calc_concavity(SculptSession *ss, SculptVertRef vref)
|
|||
return 1.0 - f;
|
||||
}
|
||||
|
||||
static void SCULPT_concavity_automasking_init(Object *ob, Brush *brush, float *factor)
|
||||
static void SCULPT_concavity_automasking_init(Object *ob,
|
||||
Brush *brush,
|
||||
AutomaskingCache *automasking,
|
||||
SculptCustomLayer *factorlayer)
|
||||
{
|
||||
SculptSession *ss = ob->sculpt;
|
||||
|
||||
|
@ -423,13 +446,14 @@ static void SCULPT_concavity_automasking_init(Object *ob, Brush *brush, float *f
|
|||
for (int i = 0; i < totvert; i++) {
|
||||
SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
|
||||
float f = SCULPT_calc_concavity(ss, vref);
|
||||
f = sculpt_concavity_factor(automasking, f);
|
||||
|
||||
factor[i] *= f;
|
||||
*(float *)SCULPT_temp_cdata_get(vref, factorlayer) *= f;
|
||||
}
|
||||
// BKE_pbvh_vertex_iter_begin
|
||||
}
|
||||
|
||||
AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object *ob)
|
||||
ATTR_NO_OPT AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object *ob)
|
||||
{
|
||||
SculptSession *ss = ob->sculpt;
|
||||
const int totvert = SCULPT_vertex_count_get(ss);
|
||||
|
@ -446,9 +470,29 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object
|
|||
return automasking;
|
||||
}
|
||||
|
||||
automasking->factor = MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor");
|
||||
SCULPT_vertex_random_access_ensure(ss);
|
||||
SCULPT_face_random_access_ensure(ss);
|
||||
|
||||
automasking->factorlayer = MEM_callocN(sizeof(*automasking->factorlayer),
|
||||
"automasking->factorlayer");
|
||||
|
||||
if (!SCULPT_temp_customlayer_get(ss,
|
||||
ATTR_DOMAIN_POINT,
|
||||
CD_PROP_FLOAT,
|
||||
"__sculpt_mask_factor",
|
||||
automasking->factorlayer)) {
|
||||
// failed
|
||||
MEM_freeN(automasking->factorlayer);
|
||||
return automasking;
|
||||
}
|
||||
|
||||
// automasking->factorlayer = SCULPT_temp_customlayer_ensure()
|
||||
// automasking->factor = MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor");
|
||||
for (int i = 0; i < totvert; i++) {
|
||||
automasking->factor[i] = 1.0f;
|
||||
SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
|
||||
float *f = SCULPT_temp_cdata_get(vertex, automasking->factorlayer);
|
||||
|
||||
*f = 1.0f;
|
||||
}
|
||||
|
||||
const int boundary_propagation_steps = brush ?
|
||||
|
@ -457,26 +501,35 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object
|
|||
|
||||
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_TOPOLOGY)) {
|
||||
SCULPT_vertex_random_access_ensure(ss);
|
||||
SCULPT_topology_automasking_init(sd, ob, automasking->factor);
|
||||
SCULPT_topology_automasking_init(sd, ob, automasking->factorlayer);
|
||||
}
|
||||
|
||||
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
|
||||
SCULPT_vertex_random_access_ensure(ss);
|
||||
SCULPT_boundary_automasking_init(ob,
|
||||
AUTOMASK_INIT_BOUNDARY_FACE_SETS,
|
||||
boundary_propagation_steps,
|
||||
automasking->factorlayer);
|
||||
}
|
||||
|
||||
// for dyntopo, only topology and fset boundary area initialized here
|
||||
if (ss->bm) {
|
||||
return automasking;
|
||||
}
|
||||
|
||||
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_FACE_SETS)) {
|
||||
SCULPT_vertex_random_access_ensure(ss);
|
||||
sculpt_face_sets_automasking_init(sd, ob, automasking->factor);
|
||||
sculpt_face_sets_automasking_init(sd, ob, automasking->factorlayer);
|
||||
}
|
||||
|
||||
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) {
|
||||
SCULPT_vertex_random_access_ensure(ss);
|
||||
SCULPT_boundary_automasking_init(
|
||||
ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps, automasking->factor);
|
||||
}
|
||||
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
|
||||
SCULPT_vertex_random_access_ensure(ss);
|
||||
SCULPT_boundary_automasking_init(
|
||||
ob, AUTOMASK_INIT_BOUNDARY_FACE_SETS, boundary_propagation_steps, automasking->factor);
|
||||
ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps, automasking->factorlayer);
|
||||
}
|
||||
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_CONCAVITY)) {
|
||||
SCULPT_vertex_random_access_ensure(ss);
|
||||
SCULPT_concavity_automasking_init(ob, brush, automasking->factor);
|
||||
SCULPT_concavity_automasking_init(ob, brush, automasking, automasking->factorlayer);
|
||||
}
|
||||
|
||||
return automasking;
|
||||
|
|
|
@ -116,13 +116,30 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op))
|
|||
SCULPT_undo_push_begin(ob, "Dynamic topology flood fill");
|
||||
SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_COORDS);
|
||||
|
||||
while (BKE_pbvh_bmesh_update_topology(
|
||||
ss->pbvh, PBVH_Collapse | PBVH_Subdivide, center, NULL, size, false, false, -1, false)) {
|
||||
DyntopoMaskCB mask_cb;
|
||||
void *mask_cb_data;
|
||||
|
||||
SCULPT_dyntopo_automasking_init(ss, NULL, sd, &mask_cb, &mask_cb_data);
|
||||
|
||||
while (BKE_pbvh_bmesh_update_topology(ss->pbvh,
|
||||
PBVH_Collapse | PBVH_Subdivide,
|
||||
center,
|
||||
NULL,
|
||||
size,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
mask_cb,
|
||||
mask_cb_data)) {
|
||||
|
||||
for (int i = 0; i < totnodes; i++) {
|
||||
BKE_pbvh_node_mark_topology_update(nodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
SCULPT_dyntopo_automasking_end(mask_cb_data);
|
||||
|
||||
MEM_SAFE_FREE(nodes);
|
||||
SCULPT_undo_push_end();
|
||||
|
||||
|
@ -228,7 +245,13 @@ static void sample_detail_dyntopo(bContext *C, ViewContext *vc, ARegion *region,
|
|||
srd.edge_length = 0.0f;
|
||||
isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal);
|
||||
|
||||
BKE_pbvh_raycast(ob->sculpt->pbvh, sculpt_raycast_detail_cb, &srd, ray_start, ray_normal, false, srd.ss->stroke_id);
|
||||
BKE_pbvh_raycast(ob->sculpt->pbvh,
|
||||
sculpt_raycast_detail_cb,
|
||||
&srd,
|
||||
ray_start,
|
||||
ray_normal,
|
||||
false,
|
||||
srd.ss->stroke_id);
|
||||
|
||||
if (srd.hit && srd.edge_length > 0.0f) {
|
||||
/* Convert edge length to world space detail resolution. */
|
||||
|
|
|
@ -48,6 +48,14 @@ struct bContext;
|
|||
|
||||
enum ePaintSymmetryFlags;
|
||||
|
||||
typedef struct SculptCustomLayer {
|
||||
bool is_cdlayer; // false for multires data
|
||||
void *data; // only valid for multires and face
|
||||
int elemsize;
|
||||
int cd_offset; // for bmesh
|
||||
CustomDataLayer *layer; // not for multires
|
||||
} SculptCustomLayer;
|
||||
|
||||
/*
|
||||
maximum symmetry passes returned by SCULPT_get_symmetry_pass.
|
||||
enough for about ~30 radial symmetry passes, which seems like plenty
|
||||
|
@ -402,10 +410,11 @@ typedef enum eBoundaryAutomaskMode {
|
|||
AUTOMASK_INIT_BOUNDARY_EDGES = 1,
|
||||
AUTOMASK_INIT_BOUNDARY_FACE_SETS = 2,
|
||||
} eBoundaryAutomaskMode;
|
||||
float *SCULPT_boundary_automasking_init(Object *ob,
|
||||
eBoundaryAutomaskMode mode,
|
||||
int propagation_steps,
|
||||
float *automask_factor);
|
||||
|
||||
void SCULPT_boundary_automasking_init(Object *ob,
|
||||
eBoundaryAutomaskMode mode,
|
||||
int propagation_steps,
|
||||
SculptCustomLayer *factorlayer);
|
||||
|
||||
/* Geodesic distances. */
|
||||
|
||||
|
@ -751,14 +760,6 @@ struct SculptRakeData {
|
|||
float follow_co[3];
|
||||
};
|
||||
|
||||
typedef struct SculptCustomLayer {
|
||||
bool is_cdlayer; // false for multires data
|
||||
void *data; // only valid for multires and face
|
||||
int elemsize;
|
||||
int cd_offset; // for bmesh
|
||||
CustomDataLayer *layer; // not for multires
|
||||
} SculptCustomLayer;
|
||||
|
||||
/* Single struct used by all BLI_task threaded callbacks, let's avoid adding 10's of those... */
|
||||
typedef struct SculptThreadedTaskData {
|
||||
struct bContext *C;
|
||||
|
@ -988,7 +989,8 @@ typedef struct AutomaskingCache {
|
|||
AutomaskingSettings settings;
|
||||
/* Precomputed auto-mask factor indexed by vertex, owned by the auto-masking system and
|
||||
* initialized in #SCULPT_automasking_cache_init when needed. */
|
||||
float *factor;
|
||||
// float *factor;
|
||||
SculptCustomLayer *factorlayer;
|
||||
} AutomaskingCache;
|
||||
|
||||
typedef struct StrokeCache {
|
||||
|
@ -1533,6 +1535,7 @@ int SCULPT_get_symmetry_pass(const SculptSession *ss);
|
|||
void SCULPT_on_sculptsession_bmesh_free(SculptSession *ss);
|
||||
void SCULPT_reorder_bmesh(SculptSession *ss);
|
||||
|
||||
// TODO: support faces
|
||||
static inline void *SCULPT_temp_cdata_get(SculptVertRef vertex, SculptCustomLayer *scl)
|
||||
{
|
||||
if (scl->data) {
|
||||
|
@ -1552,7 +1555,9 @@ always create all of your attributes together with SCULPT_temp_customlayer_ensur
|
|||
then initialize their SculptCustomLayer's with SCULPT_temp_customlayer_get
|
||||
afterwards. Otherwise customdata offsets will be wrong (for PBVH_BMESH).
|
||||
|
||||
return true on success. if false layer was not created.
|
||||
return true on success. if false, layer was not created.
|
||||
|
||||
Access per element data with SCULPT_temp_cdata_get.
|
||||
*/
|
||||
bool SCULPT_temp_customlayer_ensure(SculptSession *ss,
|
||||
AttributeDomain domain,
|
||||
|
@ -1560,3 +1565,10 @@ bool SCULPT_temp_customlayer_ensure(SculptSession *ss,
|
|||
char *name);
|
||||
bool SCULPT_temp_customlayer_get(
|
||||
SculptSession *ss, AttributeDomain domain, int proptype, char *name, SculptCustomLayer *scl);
|
||||
|
||||
bool SCULPT_dyntopo_automasking_init(const SculptSession *ss,
|
||||
const Brush *br,
|
||||
const Sculpt *sd,
|
||||
DyntopoMaskCB *r_mask_cb,
|
||||
void **r_mask_cb_data);
|
||||
void SCULPT_dyntopo_automasking_end(void *mask_data);
|
||||
|
|
|
@ -382,8 +382,10 @@ static void stats_object_sculpt(const Object *ob, SceneStats *stats)
|
|||
stats->totfacesculpt = ss->totfaces;
|
||||
break;
|
||||
case PBVH_BMESH:
|
||||
stats->totvertsculpt = ob->sculpt->bm->totvert;
|
||||
stats->tottri = ob->sculpt->bm->totface;
|
||||
if (ob->sculpt->bm) {
|
||||
stats->totvertsculpt = ob->sculpt->bm->totvert;
|
||||
stats->tottri = ob->sculpt->bm->totface;
|
||||
}
|
||||
break;
|
||||
case PBVH_GRIDS:
|
||||
stats->totvertsculpt = BKE_pbvh_get_grid_num_vertices(ss->pbvh);
|
||||
|
@ -443,15 +445,7 @@ static void stats_update(Depsgraph *depsgraph,
|
|||
FOREACH_OBJECT_END;
|
||||
}
|
||||
else if (ob && (ob->mode & OB_MODE_SCULPT)) {
|
||||
/* Sculpt Mode. */
|
||||
if (stats_is_object_dynamic_topology_sculpt(ob)) {
|
||||
/* Dynamic topology. Do not count all vertices,
|
||||
* dynamic topology stats are initialized later as part of sculpt stats. */
|
||||
}
|
||||
else {
|
||||
/* When dynamic topology is not enabled both sculpt stats and scene stats are collected. */
|
||||
stats_object_sculpt(ob, stats);
|
||||
}
|
||||
stats_object_sculpt(ob, stats);
|
||||
}
|
||||
else {
|
||||
/* Objects. */
|
||||
|
|
Loading…
Reference in New Issue