When these features aren't used, there is no sense in storing the

corresponding data layers and using their values for computations.
Avoiding that should increase performance in many operations that
would otherwise have to read, write, or propagate these values.
It also means decreased memory usage-- not just for sculpt mode
but for any mesh that was in sculpt mode. Previously the mask, face set,
and hide status layers were *always* allocated by sculpt mode.

Here are a few basic tests when masking and face sets are not used:

| Test | Before | After |
| Subsurf Modifier | 148 ms | 126 ms |
| Sculpt Overlay Extraction | 24 ms every redraw | 0 ms |
| Memory usage | 252 MB | 236 MB |

I wouldn't expect any difference when they are used though.

The code changes are mostly just making sculpt features safe for when
the layers aren't stored, and some changes to the conversion to and
from the hide layers. Use of the ".hide_poly" attribute replaces testing
whether face sets are negative in many places.

Differential Revision: https://developer.blender.org/D15937
This commit is contained in:
Hans Goudey 2022-09-12 12:48:35 -05:00
parent 3d3c34f345
commit b5f7af31d6
Notes: blender-bot 2023-02-14 08:06:38 +01:00
Referenced by commit 5bad311f4c, Fix: Multires crash after recent face set refactor
Referenced by issue #103724, Regression: Disappearing mesh with cloth brush fresh multires modifier
Referenced by issue #102940, Regression: Blender 3.4.0 Release Candidate crash on 'Mask by Color'
Referenced by issue #101864, Regression: Crash when lasso masking in sculpt mode
Referenced by issue #101722, Regression: Missing default face set for primitive meshes
Referenced by issue #101348, Regression: Smooth Brush leaves artifacts when face sets are hidden.
Referenced by issue #101203, Regression: Issues with undoing the visibility of face sets in sculpt mode
Referenced by issue #101031, Regression: Sculpt: Crash when running Initialize Face Sets operators
Referenced by issue #101027, Regression: Sculpt: Many operations not respecting the visibility anymore (probably everything that uses `BKE_pbvh_vertex_iter_begin`)
22 changed files with 283 additions and 211 deletions

View File

@ -213,9 +213,7 @@ bool BKE_paint_always_hide_test(struct Object *ob);
/**
* Returns non-zero if any of the face's vertices are hidden, zero otherwise.
*/
bool paint_is_face_hidden(const struct MLoopTri *lt,
const bool *hide_vert,
const struct MLoop *mloop);
bool paint_is_face_hidden(const struct MLoopTri *lt, const bool *hide_poly);
/**
* Returns non-zero if any of the corners of the grid
* face whose inner corner is at (x, y) are hidden, zero otherwise.
@ -689,7 +687,7 @@ void BKE_sculpt_update_object_for_edit(struct Depsgraph *depsgraph,
bool need_pmap,
bool need_mask,
bool is_paint_tool);
void BKE_sculpt_update_object_before_eval(const struct Scene *scene, struct Object *ob_eval);
void BKE_sculpt_update_object_before_eval(struct Object *ob_eval);
void BKE_sculpt_update_object_after_eval(struct Depsgraph *depsgraph, struct Object *ob_eval);
/**
@ -698,6 +696,7 @@ void BKE_sculpt_update_object_after_eval(struct Depsgraph *depsgraph, struct Obj
*/
struct MultiresModifierData *BKE_sculpt_multires_active(const struct Scene *scene,
struct Object *ob);
int *BKE_sculpt_face_sets_ensure(struct Mesh *mesh);
int BKE_sculpt_mask_layers_ensure(struct Object *ob, struct MultiresModifierData *mmd);
void BKE_sculpt_toolsettings_data_ensure(struct Scene *scene);
@ -719,18 +718,17 @@ void BKE_sculpt_sync_face_sets_visibility_to_grids(struct Mesh *mesh,
struct SubdivCCG *subdiv_ccg);
/**
* Ensures that a Face Set data-layers exists. If it does not, it creates one respecting the
* visibility stored in the vertices of the mesh. If it does, it copies the visibility from the
* mesh to the Face Sets. */
void BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(struct Mesh *mesh);
* If a face set layer exists, initialize its visiblity (sign) from the mesh's hidden values.
*/
void BKE_sculpt_face_sets_update_from_base_mesh_visibility(struct Mesh *mesh);
/**
* Ensures we do have expected mesh data in original mesh for the sculpt mode.
* Makes sculpt data consistent with other data on the mesh.
*
* \note IDs are expected to be original ones here, and calling code should ensure it updates its
* depsgraph properly after calling this function if it needs up-to-date evaluated data.
*/
void BKE_sculpt_ensure_orig_mesh_data(struct Scene *scene, struct Object *object);
void BKE_sculpt_ensure_orig_mesh_data(struct Object *object);
/**
* Test if PBVH can be used directly for drawing, which is faster than

View File

@ -674,6 +674,8 @@ const float (*BKE_pbvh_get_vert_normals(const PBVH *pbvh))[3];
const bool *BKE_pbvh_get_vert_hide(const PBVH *pbvh);
bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh);
const bool *BKE_pbvh_get_poly_hide(const PBVH *pbvh);
PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(PBVHNode *node);
void BKE_pbvh_node_color_buffer_free(PBVH *pbvh);
bool BKE_pbvh_get_color_layer(const struct Mesh *me,

View File

@ -1720,7 +1720,7 @@ void makeDerivedMesh(struct Depsgraph *depsgraph,
BKE_object_free_derived_caches(ob);
if (DEG_is_active(depsgraph)) {
BKE_sculpt_update_object_before_eval(scene, ob);
BKE_sculpt_update_object_before_eval(ob);
}
/* NOTE: Access the `edit_mesh` after freeing the derived caches, so that `ob->data` is restored

View File

@ -1247,13 +1247,12 @@ void BKE_paint_blend_read_lib(BlendLibReader *reader, Scene *sce, Paint *p)
}
}
bool paint_is_face_hidden(const MLoopTri *lt, const bool *hide_vert, const MLoop *mloop)
bool paint_is_face_hidden(const MLoopTri *lt, const bool *hide_poly)
{
if (!hide_vert) {
if (!hide_poly) {
return false;
}
return ((hide_vert[mloop[lt->tri[0]].v]) || (hide_vert[mloop[lt->tri[1]].v]) ||
(hide_vert[mloop[lt->tri[2]].v]));
return hide_poly[lt->poly];
}
bool paint_is_grid_face_hidden(const uint *grid_hidden, int gridsize, int x, int y)
@ -1632,15 +1631,8 @@ static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob)
return false;
}
/**
* \param need_mask: So that the evaluated mesh that is returned has mask data.
*/
static void sculpt_update_object(Depsgraph *depsgraph,
Object *ob,
Object *ob_eval,
bool need_pmap,
bool need_mask,
bool is_paint_tool)
static void sculpt_update_object(
Depsgraph *depsgraph, Object *ob, Object *ob_eval, bool need_pmap, bool is_paint_tool)
{
Scene *scene = DEG_get_input_scene(depsgraph);
Sculpt *sd = scene->toolsettings->sculpt;
@ -1662,15 +1654,6 @@ static void sculpt_update_object(Depsgraph *depsgraph,
ss->scene = scene;
if (need_mask) {
if (mmd == nullptr) {
BLI_assert(CustomData_has_layer(&me->vdata, CD_PAINT_MASK));
}
else {
BLI_assert(CustomData_has_layer(&me->ldata, CD_GRID_PAINT_MASK));
}
}
ss->shapekey_active = (mmd == nullptr) ? BKE_keyblock_from_object(ob) : nullptr;
/* NOTE: Weight pPaint require mesh info for loop lookup, but it never uses multires code path,
@ -1726,7 +1709,6 @@ static void sculpt_update_object(Depsgraph *depsgraph,
/* Sculpt Face Sets. */
if (use_face_sets) {
BLI_assert(CustomData_has_layer(&me->pdata, CD_SCULPT_FACE_SETS));
ss->face_sets = static_cast<int *>(CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS));
}
else {
@ -1859,24 +1841,7 @@ static void sculpt_update_object(Depsgraph *depsgraph,
}
}
static void sculpt_face_sets_ensure(Mesh *mesh)
{
if (CustomData_has_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)) {
return;
}
int *new_face_sets = static_cast<int *>(CustomData_add_layer(
&mesh->pdata, CD_SCULPT_FACE_SETS, CD_CONSTRUCT, nullptr, mesh->totpoly));
/* Initialize the new Face Set data-layer with a default valid visible ID and set the default
* color to render it white. */
for (int i = 0; i < mesh->totpoly; i++) {
new_face_sets[i] = 1;
}
mesh->face_sets_color_default = 1;
}
void BKE_sculpt_update_object_before_eval(const Scene *scene, Object *ob_eval)
void BKE_sculpt_update_object_before_eval(Object *ob_eval)
{
/* Update before mesh evaluation in the dependency graph. */
SculptSession *ss = ob_eval->sculpt;
@ -1906,16 +1871,6 @@ void BKE_sculpt_update_object_before_eval(const Scene *scene, Object *ob_eval)
MEM_freeN(nodes);
}
}
if (ss) {
Object *ob_orig = DEG_get_original_object(ob_eval);
Mesh *mesh = BKE_object_get_original_mesh(ob_orig);
MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob_orig);
/* Ensure attribute layout is still correct. */
sculpt_face_sets_ensure(mesh);
BKE_sculpt_mask_layers_ensure(ob_orig, mmd);
}
}
void BKE_sculpt_update_object_after_eval(Depsgraph *depsgraph, Object *ob_eval)
@ -1924,7 +1879,7 @@ void BKE_sculpt_update_object_after_eval(Depsgraph *depsgraph, Object *ob_eval)
* other data when modifiers change the mesh. */
Object *ob_orig = DEG_get_original_object(ob_eval);
sculpt_update_object(depsgraph, ob_orig, ob_eval, false, false, false);
sculpt_update_object(depsgraph, ob_orig, ob_eval, false, false);
}
void BKE_sculpt_color_layer_create_if_needed(Object *object)
@ -1962,13 +1917,53 @@ void BKE_sculpt_color_layer_create_if_needed(Object *object)
}
void BKE_sculpt_update_object_for_edit(
Depsgraph *depsgraph, Object *ob_orig, bool need_pmap, bool need_mask, bool is_paint_tool)
Depsgraph *depsgraph, Object *ob_orig, bool need_pmap, bool /*need_mask*/, bool is_paint_tool)
{
BLI_assert(ob_orig == DEG_get_original_object(ob_orig));
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob_orig);
sculpt_update_object(depsgraph, ob_orig, ob_eval, need_pmap, need_mask, is_paint_tool);
sculpt_update_object(depsgraph, ob_orig, ob_eval, need_pmap, is_paint_tool);
}
int *BKE_sculpt_face_sets_ensure(Mesh *mesh)
{
using namespace blender;
using namespace blender::bke;
if (CustomData_has_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)) {
return static_cast<int *>(CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS));
}
const AttributeAccessor attributes = mesh->attributes_for_write();
const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
".hide_poly", ATTR_DOMAIN_FACE, false);
MutableSpan<int> face_sets = {
static_cast<int *>(CustomData_add_layer(
&mesh->pdata, CD_SCULPT_FACE_SETS, CD_CONSTRUCT, nullptr, mesh->totpoly)),
mesh->totpoly};
/* Initialize the new face sets with a default valid visible ID and set the default
* color to render it white. */
if (hide_poly.is_single() && !hide_poly.get_internal_single()) {
face_sets.fill(1);
}
else {
const int face_sets_default_visible_id = 1;
const int face_sets_default_hidden_id = -(face_sets_default_visible_id + 1);
const VArraySpan<bool> hide_poly_span{hide_poly};
for (const int i : face_sets.index_range()) {
/* Assign a new hidden ID to hidden faces. This way we get at initial split in two Face Sets
* between hidden and visible faces based on the previous mesh visibly from other mode that
* can be useful in some cases. */
face_sets[i] = hide_poly_span[i] ? face_sets_default_hidden_id :
face_sets_default_visible_id;
}
}
mesh->face_sets_color_default = 1;
return face_sets.data();
}
int BKE_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd)
@ -2092,67 +2087,48 @@ static bool check_sculpt_object_deformed(Object *object, const bool for_construc
return deformed;
}
void BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(Mesh *mesh)
{
const int face_sets_default_visible_id = 1;
const int face_sets_default_hidden_id = -(face_sets_default_visible_id + 1);
bool initialize_new_face_sets = false;
if (CustomData_has_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)) {
/* Make everything visible. */
int *current_face_sets = static_cast<int *>(
CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS));
for (int i = 0; i < mesh->totpoly; i++) {
current_face_sets[i] = abs(current_face_sets[i]);
}
}
else {
initialize_new_face_sets = true;
int *new_face_sets = static_cast<int *>(CustomData_add_layer(
&mesh->pdata, CD_SCULPT_FACE_SETS, CD_CONSTRUCT, nullptr, mesh->totpoly));
/* Initialize the new Face Set data-layer with a default valid visible ID and set the default
* color to render it white. */
for (int i = 0; i < mesh->totpoly; i++) {
new_face_sets[i] = face_sets_default_visible_id;
}
mesh->face_sets_color_default = face_sets_default_visible_id;
}
int *face_sets = static_cast<int *>(CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS));
const bool *hide_poly = (const bool *)CustomData_get_layer_named(
&mesh->pdata, CD_PROP_BOOL, ".hide_poly");
for (int i = 0; i < mesh->totpoly; i++) {
if (!(hide_poly && hide_poly[i])) {
continue;
}
if (initialize_new_face_sets) {
/* When initializing a new Face Set data-layer, assign a new hidden Face Set ID to hidden
* vertices. This way, we get at initial split in two Face Sets between hidden and
* visible vertices based on the previous mesh visibly from other mode that can be
* useful in some cases. */
face_sets[i] = face_sets_default_hidden_id;
}
else {
/* Otherwise, set the already existing Face Set ID to hidden. */
face_sets[i] = -abs(face_sets[i]);
}
}
}
void BKE_sculpt_sync_face_sets_visibility_to_base_mesh(Mesh *mesh)
void BKE_sculpt_face_sets_update_from_base_mesh_visibility(Mesh *mesh)
{
using namespace blender;
using namespace blender::bke;
const int *face_sets = static_cast<const int *>(
CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS));
if (!face_sets) {
if (!CustomData_has_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)) {
return;
}
const AttributeAccessor attributes = mesh->attributes();
const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
".hide_poly", ATTR_DOMAIN_FACE, false);
if (hide_poly.is_single() && !hide_poly.get_internal_single()) {
return;
}
MutableSpan<int> face_sets{
static_cast<int *>(CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)), mesh->totpoly};
for (const int i : hide_poly.index_range()) {
face_sets[i] = hide_poly[i] ? -std::abs(face_sets[i]) : std::abs(face_sets[i]);
}
}
static void set_hide_poly_from_face_sets(Mesh &mesh)
{
using namespace blender;
using namespace blender::bke;
if (!CustomData_has_layer(&mesh.pdata, CD_SCULPT_FACE_SETS)) {
return;
}
const Span<int> face_sets{
static_cast<const int *>(CustomData_get_layer(&mesh.pdata, CD_SCULPT_FACE_SETS)),
mesh.totpoly};
MutableAttributeAccessor attributes = mesh.attributes_for_write();
if (std::all_of(
face_sets.begin(), face_sets.end(), [&](const int value) { return value > 0; })) {
attributes.remove(".hide_poly");
return;
}
MutableAttributeAccessor attributes = mesh->attributes_for_write();
SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_only_span<bool>(
".hide_poly", ATTR_DOMAIN_FACE);
if (!hide_poly) {
@ -2162,7 +2138,11 @@ void BKE_sculpt_sync_face_sets_visibility_to_base_mesh(Mesh *mesh)
hide_poly.span[i] = face_sets[i] < 0;
}
hide_poly.finish();
}
void BKE_sculpt_sync_face_sets_visibility_to_base_mesh(Mesh *mesh)
{
set_hide_poly_from_face_sets(*mesh);
BKE_mesh_flush_hidden_from_polys(mesh);
}
@ -2200,41 +2180,29 @@ void BKE_sculpt_sync_face_sets_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv
void BKE_sculpt_sync_face_set_visibility(Mesh *mesh, SubdivCCG *subdiv_ccg)
{
BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(mesh);
BKE_sculpt_face_sets_update_from_base_mesh_visibility(mesh);
BKE_sculpt_sync_face_sets_visibility_to_base_mesh(mesh);
BKE_sculpt_sync_face_sets_visibility_to_grids(mesh, subdiv_ccg);
}
void BKE_sculpt_ensure_orig_mesh_data(Scene *scene, Object *object)
void BKE_sculpt_ensure_orig_mesh_data(Object *object)
{
Mesh *mesh = BKE_mesh_from_object(object);
MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, object);
BLI_assert(object->mode == OB_MODE_SCULPT);
/* Copy the current mesh visibility to the Face Sets. */
BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(mesh);
if (object->sculpt != nullptr) {
/* If a sculpt session is active, ensure we have its face-set data properly up-to-date. */
object->sculpt->face_sets = static_cast<int *>(
CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS));
/* NOTE: In theory we could add that on the fly when required by sculpt code.
* But this then requires proper update of depsgraph etc. For now we play safe, optimization is
* always possible later if it's worth it. */
BKE_sculpt_mask_layers_ensure(object, mmd);
}
BKE_sculpt_face_sets_update_from_base_mesh_visibility(mesh);
/* Tessfaces aren't used and will become invalid. */
BKE_mesh_tessface_clear(mesh);
/* We always need to flush updates from depsgraph here, since at the very least
* `BKE_sculpt_face_sets_ensure_from_base_mesh_visibility()` will have updated some data layer of
* `BKE_sculpt_face_sets_update_from_base_mesh_visibility()` will have updated some data layer of
* the mesh.
*
* All known potential sources of updates:
* - Addition of, or changes to, the `CD_SCULPT_FACE_SETS` data layer
* (`BKE_sculpt_face_sets_ensure_from_base_mesh_visibility`).
* (`BKE_sculpt_face_sets_update_from_base_mesh_visibility`).
* - Addition of a `CD_PAINT_MASK` data layer (`BKE_sculpt_mask_layers_ensure`).
* - Object has any active modifier (modifier stack can be different in Sculpt mode).
* - Multires:

View File

@ -285,7 +285,7 @@ static void build_mesh_leaf_node(PBVH *pbvh, PBVHNode *node)
}
if (has_visible == false) {
if (!paint_is_face_hidden(lt, pbvh->hide_vert, pbvh->mloop)) {
if (!paint_is_face_hidden(lt, pbvh->hide_poly)) {
has_visible = true;
}
}
@ -552,6 +552,7 @@ void BKE_pbvh_build_mesh(PBVH *pbvh,
pbvh->mesh = mesh;
pbvh->header.type = PBVH_FACES;
pbvh->mpoly = mpoly;
pbvh->hide_poly = (bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".hide_poly");
pbvh->material_indices = (const int *)CustomData_get_layer_named(
&mesh->pdata, CD_PROP_INT32, "material_index");
pbvh->mloop = mloop;
@ -1313,11 +1314,7 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata,
}
case PBVH_FACES:
node->draw_buffers = GPU_pbvh_mesh_buffers_build(
pbvh->mesh,
pbvh->looptri,
CustomData_get_layer(pbvh->pdata, CD_SCULPT_FACE_SETS),
node->prim_indices,
node->totprim);
pbvh->mesh, pbvh->looptri, node->prim_indices, node->totprim);
break;
case PBVH_BMESH:
node->draw_buffers = GPU_pbvh_bmesh_buffers_build(pbvh->flags &
@ -2293,7 +2290,7 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh,
const MLoopTri *lt = &pbvh->looptri[faces[i]];
const int *face_verts = node->face_vert_indices[i];
if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_vert, mloop)) {
if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_poly)) {
continue;
}
@ -2602,7 +2599,7 @@ static bool pbvh_faces_node_nearest_to_ray(PBVH *pbvh,
const MLoopTri *lt = &pbvh->looptri[faces[i]];
const int *face_verts = node->face_vert_indices[i];
if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_vert, mloop)) {
if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_poly)) {
continue;
}
@ -3219,6 +3216,12 @@ const bool *BKE_pbvh_get_vert_hide(const PBVH *pbvh)
return pbvh->hide_vert;
}
const bool *BKE_pbvh_get_poly_hide(const PBVH *pbvh)
{
BLI_assert(pbvh->header.type == PBVH_FACES);
return pbvh->hide_poly;
}
bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh)
{
BLI_assert(pbvh->header.type == PBVH_FACES);

View File

@ -156,6 +156,7 @@ struct PBVH {
bool *hide_vert;
struct MVert *verts;
const struct MPoly *mpoly;
bool *hide_poly;
/** Material indices. Only valid for polygon meshes. */
const int *material_indices;
const struct MLoop *mloop;

View File

@ -186,7 +186,7 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op)
}
if (ob->mode == OB_MODE_SCULPT) {
BKE_sculpt_ensure_orig_mesh_data(CTX_data_scene(C), ob);
BKE_sculpt_ensure_orig_mesh_data(ob);
ED_sculpt_undo_geometry_end(ob);
}
@ -912,7 +912,7 @@ static void quadriflow_start_job(void *customdata, short *stop, short *do_update
}
if (ob->mode == OB_MODE_SCULPT) {
BKE_sculpt_ensure_orig_mesh_data(qj->scene, ob);
BKE_sculpt_ensure_orig_mesh_data(ob);
ED_sculpt_undo_geometry_end(ob);
}

View File

@ -134,6 +134,7 @@ static void mask_flood_fill_task_cb(void *__restrict userdata,
static int mask_flood_fill_exec(bContext *C, wmOperator *op)
{
const Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
PaintMaskFloodMode mode;
@ -146,6 +147,9 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op)
mode = RNA_enum_get(op->ptr, "mode");
value = RNA_float_get(op->ptr, "value");
MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
BKE_sculpt_mask_layers_ensure(ob, mmd);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
pbvh = ob->sculpt->pbvh;
multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
@ -774,6 +778,8 @@ static void sculpt_gesture_init_face_set_properties(SculptGestureContext *sgcont
struct Mesh *mesh = BKE_mesh_from_object(sgcontext->vc.obact);
sgcontext->operation = MEM_callocN(sizeof(SculptGestureFaceSetOperation), "Face Set Operation");
sgcontext->ss->face_sets = BKE_sculpt_face_sets_ensure(mesh);
SculptGestureFaceSetOperation *face_set_operation = (SculptGestureFaceSetOperation *)
sgcontext->operation;
@ -817,7 +823,7 @@ static void mask_gesture_apply_task_cb(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin (sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (sculpt_gesture_is_vertex_effected(sgcontext, &vd)) {
float prevmask = *vd.mask;
float prevmask = vd.mask ? *vd.mask : 0.0f;
if (!any_masked) {
any_masked = true;
@ -863,6 +869,10 @@ static void sculpt_gesture_init_mask_properties(SculptGestureContext *sgcontext,
SculptGestureMaskOperation *mask_operation = (SculptGestureMaskOperation *)sgcontext->operation;
Object *object = sgcontext->vc.obact;
MultiresModifierData *mmd = BKE_sculpt_multires_active(sgcontext->vc.scene, object);
BKE_sculpt_mask_layers_ensure(sgcontext->vc.obact, mmd);
mask_operation->op.sculpt_gesture_begin = sculpt_gesture_mask_begin;
mask_operation->op.sculpt_gesture_apply_for_symmetry_pass =
sculpt_gesture_mask_apply_for_symmetry_pass;

View File

@ -253,11 +253,11 @@ float SCULPT_vertex_mask_get(SculptSession *ss, PBVHVertRef vertex)
float *mask;
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
return ss->vmask[vertex.i];
return ss->vmask ? ss->vmask[vertex.i] : 0.0f;
case PBVH_BMESH:
v = (BMVert *)vertex.i;
mask = BM_ELEM_CD_GET_VOID_P(v, CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK));
return *mask;
return mask ? *mask : 0.0f;
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
const int grid_index = vertex.i / key->grid_area;
@ -329,7 +329,7 @@ int SCULPT_active_face_set_get(SculptSession *ss)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
return ss->face_sets[ss->active_face_index];
return ss->face_sets ? ss->face_sets[ss->active_face_index] : SCULPT_FACE_SET_NONE;
case PBVH_GRIDS: {
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg,
ss->active_grid_index);
@ -383,6 +383,7 @@ bool SCULPT_vertex_visible_get(SculptSession *ss, PBVHVertRef vertex)
void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible)
{
BLI_assert(ss->face_sets != NULL);
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
case PBVH_GRIDS:
@ -405,6 +406,7 @@ void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visibl
void SCULPT_face_sets_visibility_invert(SculptSession *ss)
{
BLI_assert(ss->face_sets != NULL);
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
case PBVH_GRIDS:
@ -422,6 +424,9 @@ void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible)
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
case PBVH_GRIDS:
if (!ss->face_sets) {
return;
}
for (int i = 0; i < ss->totfaces; i++) {
/* This can run on geometry without a face set assigned, so its ID sign can't be changed to
@ -446,11 +451,15 @@ void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible)
bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, PBVHVertRef vertex)
{
const bool *hide_poly = BKE_pbvh_get_poly_hide(ss->pbvh);
if (!hide_poly) {
return true;
}
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
MeshElemMap *vert_map = &ss->pmap[vertex.i];
for (int j = 0; j < ss->pmap[vertex.i].count; j++) {
if (ss->face_sets[vert_map->indices[j]] > 0) {
if (!hide_poly[vert_map->indices[j]]) {
return true;
}
}
@ -466,11 +475,15 @@ bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, PBVHVertRef verte
bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, PBVHVertRef vertex)
{
const bool *hide_poly = BKE_pbvh_get_poly_hide(ss->pbvh);
if (!hide_poly) {
return true;
}
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
MeshElemMap *vert_map = &ss->pmap[vertex.i];
for (int j = 0; j < ss->pmap[vertex.i].count; j++) {
if (ss->face_sets[vert_map->indices[j]] < 0) {
if (hide_poly[vert_map->indices[j]]) {
return false;
}
}
@ -482,7 +495,7 @@ bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, PBVHVertRe
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
const int grid_index = vertex.i / key->grid_area;
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index);
return ss->face_sets[face_index] > 0;
return !hide_poly[face_index];
}
}
return true;
@ -492,6 +505,7 @@ void SCULPT_vertex_face_set_set(SculptSession *ss, PBVHVertRef vertex, int face_
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
BLI_assert(ss->face_sets != NULL);
MeshElemMap *vert_map = &ss->pmap[vertex.i];
for (int j = 0; j < ss->pmap[vertex.i].count; j++) {
if (ss->face_sets[vert_map->indices[j]] > 0) {
@ -502,6 +516,7 @@ void SCULPT_vertex_face_set_set(SculptSession *ss, PBVHVertRef vertex, int face_
case PBVH_BMESH:
break;
case PBVH_GRIDS: {
BLI_assert(ss->face_sets != NULL);
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
const int grid_index = vertex.i / key->grid_area;
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index);
@ -517,6 +532,9 @@ int SCULPT_vertex_face_set_get(SculptSession *ss, PBVHVertRef vertex)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
if (!ss->face_sets) {
return SCULPT_FACE_SET_NONE;
}
MeshElemMap *vert_map = &ss->pmap[vertex.i];
int face_set = 0;
for (int i = 0; i < ss->pmap[vertex.i].count; i++) {
@ -529,6 +547,9 @@ int SCULPT_vertex_face_set_get(SculptSession *ss, PBVHVertRef vertex)
case PBVH_BMESH:
return 0;
case PBVH_GRIDS: {
if (!ss->face_sets) {
return SCULPT_FACE_SET_NONE;
}
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
const int grid_index = vertex.i / key->grid_area;
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index);
@ -542,6 +563,9 @@ bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
if (!ss->face_sets) {
return face_set == SCULPT_FACE_SET_NONE;
}
MeshElemMap *vert_map = &ss->pmap[vertex.i];
for (int i = 0; i < ss->pmap[vertex.i].count; i++) {
if (ss->face_sets[vert_map->indices[i]] == face_set) {
@ -553,6 +577,9 @@ bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_
case PBVH_BMESH:
return true;
case PBVH_GRIDS: {
if (!ss->face_sets) {
return face_set == SCULPT_FACE_SET_NONE;
}
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
const int grid_index = vertex.i / key->grid_area;
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index);
@ -599,6 +626,9 @@ static void UNUSED_FUNCTION(sculpt_visibility_sync_vertex_to_face_sets)(SculptSe
void SCULPT_visibility_sync_all_vertex_to_face_sets(SculptSession *ss)
{
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
if (ss->face_sets == NULL) {
return;
}
for (int i = 0; i < ss->totfaces; i++) {
const MPoly *poly = &ss->mpoly[i];
bool poly_visible = true;
@ -620,6 +650,9 @@ void SCULPT_visibility_sync_all_vertex_to_face_sets(SculptSession *ss)
static bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, int index)
{
if (!ss->face_sets) {
return true;
}
MeshElemMap *vert_map = &ss->pmap[index];
int face_set = -1;
for (int i = 0; i < ss->pmap[index].count; i++) {
@ -676,6 +709,9 @@ bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, PBVHVertRef vertex)
case PBVH_BMESH:
return true;
case PBVH_GRIDS: {
if (!ss->face_sets) {
return true;
}
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
const int grid_index = vertex.i / key->grid_area;
const int vertex_index = vertex.i - grid_index * key->grid_area;
@ -703,6 +739,9 @@ int SCULPT_face_set_next_available_get(SculptSession *ss)
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
case PBVH_GRIDS: {
if (!ss->face_sets) {
return 0;
}
int next_face_set = 0;
for (int i = 0; i < ss->totfaces; i++) {
if (abs(ss->face_sets[i]) > next_face_set) {
@ -792,9 +831,10 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss,
iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY;
iter->neighbors = iter->neighbors_fixed;
iter->neighbor_indices = iter->neighbor_indices_fixed;
const bool *hide_poly = BKE_pbvh_get_vert_hide(ss->pbvh);
for (int i = 0; i < ss->pmap[vertex.i].count; i++) {
if (ss->face_sets[vert_map->indices[i]] < 0) {
if (hide_poly && hide_poly[vert_map->indices[i]]) {
/* Skip connectivity from hidden faces. */
continue;
}
@ -3302,6 +3342,15 @@ static void do_brush_action(Sculpt *sd,
BKE_pbvh_ensure_node_loops(ss->pbvh);
}
if (SCULPT_tool_is_mask(brush->sculpt_tool)) {
MultiresModifierData *mmd = BKE_sculpt_multires_active(ss->scene, ob);
BKE_sculpt_mask_layers_ensure(ob, mmd);
}
if (SCULPT_tool_is_face_sets(brush->sculpt_tool)) {
Mesh *mesh = BKE_object_get_original_mesh(ob);
ss->face_sets = BKE_sculpt_face_sets_ensure(mesh);
}
/* Build a list of all nodes that are potentially within the brush's area of influence */
if (SCULPT_tool_needs_all_pbvh_nodes(brush)) {

View File

@ -215,13 +215,7 @@ static void SCULPT_dynamic_topology_disable_ex(
BKE_sculptsession_bm_to_me(ob, true);
/* Reset Face Sets as they are no longer valid. */
if (!CustomData_has_layer(&me->pdata, CD_SCULPT_FACE_SETS)) {
CustomData_add_layer(&me->pdata, CD_SCULPT_FACE_SETS, CD_SET_DEFAULT, NULL, me->totpoly);
}
ss->face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS);
for (int i = 0; i < me->totpoly; i++) {
ss->face_sets[i] = 1;
}
CustomData_free_layers(&me->pdata, CD_SCULPT_FACE_SETS, me->totpoly);
me->face_sets_color_default = 1;
/* Sync the visibility to vertices manually as the pmap is still not initialized. */

View File

@ -17,6 +17,7 @@
#include "DNA_brush_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "BKE_brush.h"
@ -1390,9 +1391,15 @@ static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_c
/* Face Sets are always stored as they are needed for snapping. */
expand_cache->initial_face_sets = MEM_malloc_arrayN(totface, sizeof(int), "initial face set");
expand_cache->original_face_sets = MEM_malloc_arrayN(totface, sizeof(int), "original face set");
for (int i = 0; i < totface; i++) {
expand_cache->initial_face_sets[i] = ss->face_sets[i];
expand_cache->original_face_sets[i] = ss->face_sets[i];
if (ss->face_sets) {
for (int i = 0; i < totface; i++) {
expand_cache->initial_face_sets[i] = ss->face_sets[i];
expand_cache->original_face_sets[i] = ss->face_sets[i];
}
}
else {
memset(expand_cache->initial_face_sets, SCULPT_FACE_SET_NONE, sizeof(int) * totface);
memset(expand_cache->original_face_sets, SCULPT_FACE_SET_NONE, sizeof(int) * totface);
}
if (expand_cache->target == SCULPT_EXPAND_TARGET_MASK) {
@ -2118,6 +2125,16 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even
return OPERATOR_CANCELLED;
}
if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS) {
Mesh *mesh = ob->data;
ss->face_sets = BKE_sculpt_face_sets_ensure(mesh);
}
if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_MASK) {
MultiresModifierData *mmd = BKE_sculpt_multires_active(ss->scene, ob);
BKE_sculpt_mask_layers_ensure(ob, mmd);
}
/* Face Set operations are not supported in dyntopo. */
if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS &&
BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {

View File

@ -303,6 +303,9 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
Mesh *mesh = ob->data;
ss->face_sets = BKE_sculpt_face_sets_ensure(mesh);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED, false);
const int tot_vert = SCULPT_vertex_count_get(ss);
@ -349,7 +352,6 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
}
if (all_visible) {
Mesh *mesh = ob->data;
mesh->face_sets_color_default = next_face_set;
BKE_pbvh_face_sets_color_set(
ss->pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default);
@ -373,7 +375,6 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
}
if (mode == SCULPT_FACE_SET_SELECTION) {
Mesh *mesh = ob->data;
BMesh *bm;
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
bm = BM_mesh_create(&allocsize,
@ -850,6 +851,10 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
if (!pbvh_has_face_sets(ss->pbvh)) {
return OPERATOR_CANCELLED;
}
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
const int tot_vert = SCULPT_vertex_count_get(ss);
@ -1000,6 +1005,10 @@ static int sculpt_face_sets_randomize_colors_exec(bContext *C, wmOperator *UNUSE
return OPERATOR_CANCELLED;
}
if (!pbvh_has_face_sets(ss->pbvh)) {
return OPERATOR_CANCELLED;
}
PBVH *pbvh = ob->sculpt->pbvh;
PBVHNode **nodes;
int totnode;
@ -1154,7 +1163,9 @@ static void sculpt_face_set_shrink(Object *ob,
static bool check_single_face_set(SculptSession *ss, int *face_sets, const bool check_visible_only)
{
if (face_sets == NULL) {
return true;
}
int first_face_set = SCULPT_FACE_SET_NONE;
if (check_visible_only) {
for (int f = 0; f < ss->totfaces; f++) {

View File

@ -14,6 +14,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "BKE_brush.h"
#include "BKE_context.h"
@ -174,11 +175,15 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
const Scene *scene = CTX_data_scene(C);
PBVHNode **nodes;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
int totnode;
int filter_type = RNA_enum_get(op->ptr, "filter_type");
MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
BKE_sculpt_mask_layers_ensure(ob, mmd);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
SculptSession *ss = ob->sculpt;

View File

@ -170,6 +170,8 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
}
}
const bool *hide_poly = BKE_pbvh_get_poly_hide(ss->pbvh);
/* Add edges adjacent to an initial vertex to the queue. */
for (int i = 0; i < totedge; i++) {
const int v1 = edges[i].v1;
@ -199,7 +201,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
if (ss->epmap[e].count != 0) {
for (int poly_map_index = 0; poly_map_index < ss->epmap[e].count; poly_map_index++) {
const int poly = ss->epmap[e].indices[poly_map_index];
if (ss->face_sets[poly] <= 0) {
if (hide_poly && hide_poly[poly]) {
continue;
}
const MPoly *mpoly = &polys[poly];

View File

@ -1837,6 +1837,16 @@ BLI_INLINE bool SCULPT_tool_is_paint(int tool)
return ELEM(tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR);
}
BLI_INLINE bool SCULPT_tool_is_mask(int tool)
{
return ELEM(tool, SCULPT_TOOL_MASK);
}
BLI_INLINE bool SCULPT_tool_is_face_sets(int tool)
{
return ELEM(tool, SCULPT_TOOL_DRAW_FACE_SETS);
}
#ifdef __cplusplus
}
#endif

View File

@ -391,7 +391,7 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent
if (create_face_set) {
ss->filter_cache->prev_face_set = MEM_callocN(sizeof(float) * ss->totfaces, "prev face mask");
for (int i = 0; i < ss->totfaces; i++) {
ss->filter_cache->prev_face_set[i] = ss->face_sets[i];
ss->filter_cache->prev_face_set[i] = ss->face_sets ? ss->face_sets[i] : 0;
}
ss->filter_cache->new_face_set = SCULPT_face_set_next_available_get(ss);
}

View File

@ -300,28 +300,30 @@ static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene,
ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
ob->sculpt->mode_type = OB_MODE_SCULPT;
BKE_sculpt_ensure_orig_mesh_data(scene, ob);
BKE_sculpt_ensure_orig_mesh_data(ob);
BKE_scene_graph_evaluated_ensure(depsgraph, bmain);
/* This function expects a fully evaluated depsgraph. */
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
/* 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. */
/* 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. */
SculptSession *ss = ob->sculpt;
const int new_face_set = SCULPT_face_set_next_available_get(ss);
for (int i = 0; i < ss->totfaces; i++) {
if (ss->face_sets[i] == SCULPT_FACE_SET_NONE) {
ss->face_sets[i] = new_face_set;
if (ss->face_sets) {
/* 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. */
/* 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. */
const int new_face_set = SCULPT_face_set_next_available_get(ss);
for (int i = 0; i < ss->totfaces; i++) {
if (ss->face_sets[i] == SCULPT_FACE_SET_NONE) {
ss->face_sets[i] = new_face_set;
}
}
}
}

View File

@ -476,7 +476,8 @@ static bool sculpt_undo_restore_face_sets(bContext *C, SculptUndoNode *unode)
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = BKE_view_layer_active_object_get(view_layer);
Mesh *me = BKE_object_get_original_mesh(ob);
int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS);
int *face_sets = CustomData_add_layer(
&me->pdata, CD_SCULPT_FACE_SETS, CD_CONSTRUCT, NULL, me->totpoly);
for (int i = 0; i < me->totpoly; i++) {
face_sets[i] = unode->face_sets[i];
}
@ -1354,8 +1355,13 @@ static SculptUndoNode *sculpt_undo_face_sets_push(Object *ob, SculptUndoType typ
unode->face_sets = MEM_callocN(me->totpoly * sizeof(int), "sculpt face sets");
const int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS);
for (int i = 0; i < me->totpoly; i++) {
unode->face_sets[i] = face_sets[i];
if (face_sets) {
for (int i = 0; i < me->totpoly; i++) {
unode->face_sets[i] = face_sets[i];
}
}
else {
memset(unode->face_sets, SCULPT_FACE_SET_NONE, sizeof(int) * me->totpoly);
}
BLI_addtail(&usculpt->nodes, unode);
@ -1513,7 +1519,9 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType
sculpt_undo_store_hidden(ob, unode);
break;
case SCULPT_UNDO_MASK:
sculpt_undo_store_mask(ob, unode);
if (pbvh_has_mask(ss->pbvh)) {
sculpt_undo_store_mask(ob, unode);
}
break;
case SCULPT_UNDO_COLOR:
sculpt_undo_store_color(ob, unode);

View File

@ -49,7 +49,6 @@ typedef struct GPU_PBVH_Buffers GPU_PBVH_Buffers;
*/
GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const struct Mesh *mesh,
const struct MLoopTri *looptri,
const int *sculpt_face_sets,
const int *face_indices,
int face_indices_len);

View File

@ -210,13 +210,9 @@ static void gpu_pbvh_batch_init(GPU_PBVH_Buffers *buffers, GPUPrimType prim)
/** \name Mesh PBVH
* \{ */
static bool gpu_pbvh_is_looptri_visible(const MLoopTri *lt,
const bool *hide_vert,
const MLoop *mloop,
const int *sculpt_face_sets)
static bool gpu_pbvh_is_looptri_visible(const MLoopTri *lt, const bool *hide_poly)
{
return (!paint_is_face_hidden(lt, hide_vert, mloop) && sculpt_face_sets &&
sculpt_face_sets[lt->poly] > SCULPT_FACE_SET_NONE);
return !paint_is_face_hidden(lt, hide_poly);
}
void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
@ -233,8 +229,8 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
GPUAttrRef vcol_refs[MAX_GPU_ATTR];
GPUAttrRef cd_uvs[MAX_GPU_ATTR];
const bool *hide_vert = (const bool *)CustomData_get_layer_named(
&mesh->vdata, CD_PROP_BOOL, ".hide_vert");
const bool *hide_poly = (const bool *)CustomData_get_layer_named(
&mesh->pdata, CD_PROP_BOOL, ".hide_poly");
const int *material_indices = (const int *)CustomData_get_layer_named(
&mesh->pdata, CD_PROP_INT32, "material_index");
@ -315,7 +311,7 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
for (uint i = 0; i < buffers->face_indices_len; i++) {
const MLoopTri *lt = &buffers->looptri[buffers->face_indices[i]];
if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, buffers->mloop, sculpt_face_sets)) {
if (!gpu_pbvh_is_looptri_visible(lt, hide_poly)) {
continue;
}
@ -355,7 +351,7 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
buffers->mloop[lt->tri[2]].v,
};
if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, buffers->mloop, sculpt_face_sets)) {
if (!gpu_pbvh_is_looptri_visible(lt, hide_poly)) {
continue;
}
@ -395,7 +391,7 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
buffers->mloop[lt->tri[2]].v,
};
if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, buffers->mloop, sculpt_face_sets)) {
if (!gpu_pbvh_is_looptri_visible(lt, hide_poly)) {
continue;
}
@ -459,7 +455,6 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const Mesh *mesh,
const MLoopTri *looptri,
const int *sculpt_face_sets,
const int *face_indices,
const int face_indices_len)
{
@ -472,8 +467,8 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const Mesh *mesh,
buffers = MEM_callocN(sizeof(GPU_PBVH_Buffers), "GPU_Buffers");
const bool *hide_vert = (bool *)CustomData_get_layer_named(
&mesh->vdata, CD_PROP_BOOL, ".hide_vert");
const bool *hide_poly = (bool *)CustomData_get_layer_named(
&mesh->pdata, CD_PROP_BOOL, ".hide_poly");
/* smooth or flat for all */
buffers->smooth = polys[looptri[face_indices[0]].poly].flag & ME_SMOOTH;
@ -483,7 +478,7 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const Mesh *mesh,
/* Count the number of visible triangles */
for (i = 0, tottri = 0; i < face_indices_len; i++) {
const MLoopTri *lt = &looptri[face_indices[i]];
if (gpu_pbvh_is_looptri_visible(lt, hide_vert, loops, sculpt_face_sets)) {
if (gpu_pbvh_is_looptri_visible(lt, hide_poly)) {
int r_edges[3];
BKE_mesh_looptri_get_real_edges(mesh, lt, r_edges);
for (int j = 0; j < 3; j++) {
@ -516,7 +511,7 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const Mesh *mesh,
const MLoopTri *lt = &looptri[face_indices[i]];
/* Skip hidden faces */
if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, loops, sculpt_face_sets)) {
if (!gpu_pbvh_is_looptri_visible(lt, hide_poly)) {
continue;
}

View File

@ -136,9 +136,7 @@ eAttrDomain BKE_id_attribute_domain(const struct ID *UNUSED(id),
/* -------------------------------------------------------------------- */
/** \name Stubs of BKE_paint.h
* \{ */
bool paint_is_face_hidden(const struct MLoopTri *UNUSED(lt),
const bool *UNUSED(hide_vert),
const struct MLoop *UNUSED(mloop))
bool paint_is_face_hidden(const struct MLoopTri *UNUSED(lt), const bool *UNUSED(hide_poly))
{
BLI_assert_unreachable();
return false;

View File

@ -500,7 +500,7 @@ void rna_Object_data_update(Main *bmain, Scene *scene, PointerRNA *ptr)
Object *object = (Object *)ptr->data;
if (object->mode == OB_MODE_SCULPT) {
BKE_sculpt_ensure_orig_mesh_data(scene, object);
BKE_sculpt_ensure_orig_mesh_data(object);
}
rna_Object_internal_update_data_dependency(bmain, scene, ptr);