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:
Joseph Eagar 2021-06-24 23:50:49 -07:00
parent ca4ac36c59
commit 17d4c7abb1
7 changed files with 353 additions and 152 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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. */

View File

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

View File

@ -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. */