Sculpt: Reimplement face sets visibility operator

Iterate over faces instead of vertices, and multithread the logic over
all PBVH nodes. The result is about 3 times faster in my tests;
noticeably snappier for large meshes. Add a new sculpt undo type for
face hide storage (as opposed to vertex storage), and use that only
when the data actually changed, which should reduce memory usage too.
Internally some code is shared with the other visibility operators,
since this operation always affects base mesh faces, most isn't.

Similar to 4e66769ec0
This commit is contained in:
Hans Goudey
2023-12-06 17:30:11 -05:00
parent f6ef00a6f4
commit 251069ae5b
8 changed files with 265 additions and 172 deletions

View File

@@ -384,6 +384,10 @@ int BKE_pbvh_node_num_unique_verts(const PBVH &pbvh, const PBVHNode &node);
blender::Span<int> BKE_pbvh_node_get_vert_indices(const PBVHNode *node);
blender::Span<int> BKE_pbvh_node_get_unique_vert_indices(const PBVHNode *node);
void BKE_pbvh_node_get_loops(const PBVHNode *node, const int **r_loop_indices);
void BKE_pbvh_node_calc_face_indices(const PBVH &pbvh,
const PBVHNode &node,
blender::Vector<int> &faces);
blender::Vector<int> BKE_pbvh_node_calc_face_indices(const PBVH &pbvh, const PBVHNode &node);
/* Get number of faces in the mesh; for PBVH_GRIDS the

View File

@@ -1811,9 +1811,8 @@ blender::Span<int> BKE_pbvh_node_get_unique_vert_indices(const PBVHNode *node)
return node->vert_indices.as_span().take_front(node->uniq_verts);
}
blender::Vector<int> BKE_pbvh_node_calc_face_indices(const PBVH &pbvh, const PBVHNode &node)
void BKE_pbvh_node_calc_face_indices(const PBVH &pbvh, const PBVHNode &node, Vector<int> &faces)
{
Vector<int> faces;
switch (pbvh.header.type) {
case PBVH_FACES: {
const Span<int> looptri_faces = pbvh.looptri_faces;
@@ -1843,7 +1842,12 @@ blender::Vector<int> BKE_pbvh_node_calc_face_indices(const PBVH &pbvh, const PBV
BLI_assert_unreachable();
break;
}
}
blender::Vector<int> BKE_pbvh_node_calc_face_indices(const PBVH &pbvh, const PBVHNode &node)
{
Vector<int> faces;
BKE_pbvh_node_calc_face_indices(pbvh, node, faces);
return faces;
}

View File

@@ -53,6 +53,21 @@
namespace blender::ed::sculpt_paint::hide {
void tag_update_visibility(const bContext &C)
{
ARegion *region = CTX_wm_region(&C);
ED_region_tag_redraw(region);
Object *ob = CTX_data_active_object(&C);
WM_event_add_notifier(&C, NC_OBJECT | ND_DRAW, ob);
DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
const RegionView3D *rv3d = CTX_wm_region_view3d(&C);
if (!BKE_sculptsession_use_pbvh_draw(ob, rv3d)) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
}
enum class VisAction {
Hide = 0,
Show = 1,
@@ -87,7 +102,7 @@ static bool is_effected(const VisArea area,
return ((inside && area == VisArea::Inside) || (!inside && area == VisArea::Outside));
}
static void vert_show_all(Object &object, const Span<PBVHNode *> nodes)
void mesh_show_all(Object &object, const Span<PBVHNode *> nodes)
{
Mesh &mesh = *static_cast<Mesh *>(object.data);
bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
@@ -110,9 +125,7 @@ static void vert_show_all(Object &object, const Span<PBVHNode *> nodes)
BKE_mesh_flush_hidden_from_verts(&mesh);
}
static bool vert_hide_is_changed(const Span<int> verts,
const Span<bool> orig_hide,
const Span<bool> new_hide)
bool hide_is_changed(const Span<int> verts, const Span<bool> orig_hide, const Span<bool> new_hide)
{
for (const int i : verts.index_range()) {
if (orig_hide[verts[i]] != new_hide[i]) {
@@ -141,7 +154,7 @@ static void vert_hide_update(Object &object,
new_hide.reinitialize(verts.size());
array_utils::gather(hide_vert.span.as_span(), verts, new_hide.as_mutable_span());
calc_hide(verts, new_hide);
if (!vert_hide_is_changed(verts, hide_vert.span, new_hide)) {
if (!hide_is_changed(verts, hide_vert.span, new_hide)) {
continue;
}
@@ -196,14 +209,14 @@ static void partialvis_update_mesh(Object &object,
});
break;
case VisAction::Show:
vert_show_all(object, nodes);
mesh_show_all(object, nodes);
break;
}
break;
case VisArea::Masked: {
const VArraySpan<float> mask = *attributes.lookup<float>(".sculpt_mask", ATTR_DOMAIN_POINT);
if (action == VisAction::Show && mask.is_empty()) {
vert_show_all(object, nodes);
mesh_show_all(object, nodes);
}
else {
vert_hide_update(object, nodes, [&](const Span<int> verts, MutableSpan<bool> hide) {
@@ -219,12 +232,13 @@ static void partialvis_update_mesh(Object &object,
}
}
static void grids_show_all(Depsgraph &depsgraph, Object &object, const Span<PBVHNode *> nodes)
void grids_show_all(Depsgraph &depsgraph, Object &object, const Span<PBVHNode *> nodes)
{
Mesh &mesh = *static_cast<Mesh *>(object.data);
PBVH &pbvh = *object.sculpt->pbvh;
SubdivCCG &subdiv_ccg = *object.sculpt->subdiv_ccg;
const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
bool any_changed = false;
if (!grid_hidden.is_empty()) {
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (PBVHNode *node : nodes.slice(range)) {
@@ -233,12 +247,16 @@ static void grids_show_all(Depsgraph &depsgraph, Object &object, const Span<PBVH
return bits::any_bit_set(grid_hidden[i]);
}))
{
any_changed = true;
SCULPT_undo_push_node(&object, node, SCULPT_UNDO_HIDDEN);
BKE_pbvh_node_mark_rebuild_draw(node);
}
}
});
}
if (!any_changed) {
return;
}
for (PBVHNode *node : nodes) {
BKE_pbvh_node_fully_hidden_set(node, false);
}
@@ -498,7 +516,6 @@ static Vector<PBVHNode *> get_pbvh_nodes(PBVH *pbvh,
static int hide_show_exec(bContext *C, wmOperator *op)
{
ARegion *region = CTX_wm_region(C);
Object *ob = CTX_data_active_object(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
@@ -546,14 +563,7 @@ static int hide_show_exec(bContext *C, wmOperator *op)
SCULPT_undo_push_end(ob);
SCULPT_topology_islands_invalidate(ob->sculpt);
RegionView3D *rv3d = CTX_wm_region_view3d(C);
if (!BKE_sculptsession_use_pbvh_draw(ob, rv3d)) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
ED_region_tag_redraw(region);
tag_update_visibility(*C);
return OPERATOR_FINISHED;
}
@@ -683,7 +693,6 @@ static void invert_visibility_bmesh(Object &object, const Span<PBVHNode *> nodes
static int visibility_invert_exec(bContext *C, wmOperator *op)
{
ARegion &region = *CTX_wm_region(C);
Object &object = *CTX_data_active_object(C);
Depsgraph &depsgraph = *CTX_data_ensure_evaluated_depsgraph(C);
@@ -707,14 +716,7 @@ static int visibility_invert_exec(bContext *C, wmOperator *op)
SCULPT_undo_push_end(&object);
SCULPT_topology_islands_invalidate(object.sculpt);
RegionView3D *rv3d = CTX_wm_region_view3d(C);
if (!BKE_sculptsession_use_pbvh_draw(&object, rv3d)) {
DEG_id_tag_update(&object.id, ID_RECALC_GEOMETRY);
}
DEG_id_tag_update(&object.id, ID_RECALC_SHADING);
ED_region_tag_redraw(&region);
tag_update_visibility(*C);
return OPERATOR_FINISHED;
}

View File

@@ -455,6 +455,11 @@ enum BrushStrokeMode {
/* paint_hide.cc */
namespace blender::ed::sculpt_paint::hide {
bool hide_is_changed(Span<int> verts, Span<bool> orig_hide, Span<bool> new_hide);
void mesh_show_all(Object &object, const Span<PBVHNode *> nodes);
void grids_show_all(Depsgraph &depsgraph, Object &object, Span<PBVHNode *> nodes);
void tag_update_visibility(const bContext &C);
void PAINT_OT_hide_show(wmOperatorType *ot);
void PAINT_OT_visibility_invert(wmOperatorType *ot);
} // namespace blender::ed::sculpt_paint::hide

View File

@@ -420,46 +420,6 @@ bool SCULPT_vertex_visible_get(const SculptSession *ss, PBVHVertRef vertex)
return true;
}
void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible)
{
BLI_assert(ss->face_sets != nullptr);
BLI_assert(ss->hide_poly != nullptr);
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
case PBVH_GRIDS:
for (int i = 0; i < ss->totfaces; i++) {
if (ss->face_sets[i] != face_set) {
continue;
}
ss->hide_poly[i] = !visible;
}
break;
case PBVH_BMESH:
break;
}
}
void SCULPT_face_visibility_all_set(SculptSession *ss, bool visible)
{
SCULPT_topology_islands_invalidate(ss);
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
case PBVH_GRIDS:
blender::MutableSpan(ss->hide_poly, ss->totfaces).fill(!visible);
break;
case PBVH_BMESH: {
BMIter iter;
BMFace *f;
BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) {
BM_elem_flag_set(f, BM_ELEM_HIDDEN, !visible);
}
break;
}
}
}
bool SCULPT_vertex_any_face_visible_get(SculptSession *ss, PBVHVertRef vertex)
{
switch (BKE_pbvh_type(ss->pbvh)) {

View File

@@ -13,7 +13,9 @@
#include "MEM_guardedalloc.h"
#include "BLI_array.hh"
#include "BLI_array_utils.hh"
#include "BLI_bit_vector.hh"
#include "BLI_enumerable_thread_specific.hh"
#include "BLI_function_ref.hh"
#include "BLI_hash.h"
#include "BLI_math_matrix.h"
@@ -51,6 +53,7 @@
#include "ED_sculpt.hh"
#include "paint_intern.hh"
#include "sculpt_intern.hh"
#include "RNA_access.hh"
@@ -747,15 +750,6 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op)
SCULPT_undo_push_end(ob);
/* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */
SCULPT_visibility_sync_all_from_faces(ob);
for (PBVHNode *node : nodes) {
BKE_pbvh_node_mark_update_visibility(node);
}
BKE_pbvh_update_visibility(ss->pbvh);
SCULPT_tag_update_overlays(C);
return OPERATOR_FINISHED;
@@ -834,127 +828,157 @@ enum eSculptFaceGroupVisibilityModes {
SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE = 2,
};
static void face_hide_update(Object &object,
const Span<PBVHNode *> nodes,
const FunctionRef<void(Span<int>, MutableSpan<bool>)> calc_hide)
{
PBVH &pbvh = *object.sculpt->pbvh;
Mesh &mesh = *static_cast<Mesh *>(object.data);
bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
bke::SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_span<bool>(
".hide_poly", ATTR_DOMAIN_FACE);
struct TLS {
Vector<int> face_indices;
Vector<bool> new_hide;
};
bool any_changed = false;
threading::EnumerableThreadSpecific<TLS> all_tls;
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
TLS &tls = all_tls.local();
for (PBVHNode *node : nodes.slice(range)) {
tls.face_indices.clear();
BKE_pbvh_node_calc_face_indices(pbvh, *node, tls.face_indices);
const Span<int> faces = tls.face_indices;
tls.new_hide.reinitialize(faces.size());
MutableSpan<bool> new_hide = tls.new_hide;
array_utils::gather(hide_poly.span.as_span(), faces, new_hide);
calc_hide(faces, new_hide);
if (!hide::hide_is_changed(faces, hide_poly.span, new_hide)) {
continue;
}
any_changed = true;
SCULPT_undo_push_node(&object, node, SCULPT_UNDO_FACE_HIDDEN);
array_utils::scatter(new_hide.as_span(), faces, hide_poly.span);
BKE_pbvh_node_mark_update_visibility(node);
}
});
hide_poly.finish();
if (any_changed) {
SCULPT_visibility_sync_all_from_faces(&object);
}
}
static void show_all(Depsgraph &depsgraph, Object &object, const Span<PBVHNode *> nodes)
{
switch (BKE_pbvh_type(object.sculpt->pbvh)) {
case PBVH_FACES:
hide::mesh_show_all(object, nodes);
break;
case PBVH_GRIDS:
hide::grids_show_all(depsgraph, object, nodes);
break;
case PBVH_BMESH:
BLI_assert_unreachable();
break;
}
}
static int sculpt_face_set_change_visibility_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object &object = *CTX_data_active_object(C);
SculptSession *ss = object.sculpt;
Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
Mesh *mesh = BKE_object_get_original_mesh(ob);
Mesh *mesh = BKE_object_get_original_mesh(&object);
BKE_sculpt_update_object_for_edit(&depsgraph, &object, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false);
/* Not supported for dyntopo. */
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
/* Not supported for dyntopo. There is no active face. */
return OPERATOR_CANCELLED;
}
const int mode = RNA_enum_get(op->ptr, "mode");
const int tot_vert = SCULPT_vertex_count_get(ss);
const int active_face_set = SCULPT_active_face_set_get(ss);
PBVH *pbvh = ob->sculpt->pbvh;
SCULPT_undo_push_begin(&object, op);
PBVH *pbvh = object.sculpt->pbvh;
Vector<PBVHNode *> nodes = bke::pbvh::search_gather(pbvh, {});
if (nodes.is_empty()) {
return OPERATOR_CANCELLED;
}
const int active_face_set = SCULPT_active_face_set_get(ss);
ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh);
SCULPT_undo_push_begin(ob, op);
for (PBVHNode *node : nodes) {
SCULPT_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN);
}
const bke::AttributeAccessor attributes = mesh->attributes();
const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", ATTR_DOMAIN_FACE);
const VArraySpan<int> face_sets = *attributes.lookup<int>(".sculpt_face_set", ATTR_DOMAIN_FACE);
switch (mode) {
case SCULPT_FACE_SET_VISIBILITY_TOGGLE: {
bool hidden_vertex = false;
/* This can fail with regular meshes with non-manifold geometry as the visibility state can't
* be synced from face sets to non-manifold vertices. */
if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
for (int i = 0; i < tot_vert; i++) {
PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
if (!SCULPT_vertex_visible_get(ss, vertex)) {
hidden_vertex = true;
break;
}
}
}
if (ss->hide_poly) {
for (int i = 0; i < ss->totfaces; i++) {
if (ss->hide_poly[i]) {
hidden_vertex = true;
break;
}
}
}
if (hidden_vertex) {
SCULPT_face_visibility_all_set(ss, true);
if (hide_poly.contains(true) || face_sets.is_empty()) {
show_all(depsgraph, object, nodes);
}
else {
if (ss->face_sets) {
SCULPT_face_visibility_all_set(ss, false);
SCULPT_face_set_visibility_set(ss, active_face_set, true);
}
else {
SCULPT_face_visibility_all_set(ss, true);
}
face_hide_update(object, nodes, [&](const Span<int> faces, MutableSpan<bool> hide) {
for (const int i : hide.index_range()) {
hide[i] = face_sets[faces[i]] == active_face_set;
}
});
}
break;
}
case SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE:
ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh);
if (ss->face_sets) {
SCULPT_face_visibility_all_set(ss, false);
SCULPT_face_set_visibility_set(ss, active_face_set, true);
if (face_sets.is_empty()) {
show_all(depsgraph, object, nodes);
}
else {
SCULPT_face_set_visibility_set(ss, active_face_set, true);
face_hide_update(object, nodes, [&](const Span<int> faces, MutableSpan<bool> hide) {
for (const int i : hide.index_range()) {
if (face_sets[faces[i]] == active_face_set) {
hide[i] = false;
}
}
});
}
break;
case SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE:
ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh);
if (ss->face_sets) {
SCULPT_face_set_visibility_set(ss, active_face_set, false);
if (face_sets.is_empty()) {
face_hide_update(object, nodes, [&](const Span<int> /*faces*/, MutableSpan<bool> hide) {
hide.fill(true);
});
}
else {
SCULPT_face_visibility_all_set(ss, false);
face_hide_update(object, nodes, [&](const Span<int> faces, MutableSpan<bool> hide) {
for (const int i : hide.index_range()) {
if (face_sets[faces[i]] == active_face_set) {
hide[i] = true;
}
}
});
}
break;
}
/* For modes that use the cursor active vertex, update the rotation origin for viewport
* navigation.
*/
* navigation. */
if (ELEM(mode, SCULPT_FACE_SET_VISIBILITY_TOGGLE, SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE)) {
UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
float location[3];
copy_v3_v3(location, SCULPT_active_vertex_co_get(ss));
mul_m4_v3(ob->object_to_world, location);
mul_m4_v3(object.object_to_world, location);
copy_v3_v3(ups->average_stroke_accum, location);
ups->average_stroke_counter = 1;
ups->last_stroke_valid = true;
}
/* Sync face sets visibility and vertex visibility. */
SCULPT_visibility_sync_all_from_faces(ob);
SCULPT_undo_push_end(ob);
for (PBVHNode *node : nodes) {
BKE_pbvh_node_mark_update_visibility(node);
}
SCULPT_undo_push_end(&object);
BKE_pbvh_update_visibility(ss->pbvh);
ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh);
SCULPT_tag_update_overlays(C);
SCULPT_topology_islands_invalidate(object.sculpt);
hide::tag_update_visibility(*C);
return OPERATOR_FINISHED;
}

View File

@@ -141,6 +141,7 @@ enum eBoundaryAutomaskMode {
enum SculptUndoType {
SCULPT_UNDO_COORDS,
SCULPT_UNDO_HIDDEN,
SCULPT_UNDO_FACE_HIDDEN,
SCULPT_UNDO_MASK,
SCULPT_UNDO_DYNTOPO_BEGIN,
SCULPT_UNDO_DYNTOPO_END,
@@ -195,6 +196,7 @@ struct SculptUndoNode {
blender::Array<int> loop_index;
blender::BitVector<> vert_hidden;
blender::BitVector<> face_hidden;
/* multires */
int maxgrid; /* same for grid */
@@ -979,9 +981,6 @@ bool SCULPT_vertex_is_boundary(const SculptSession *ss, PBVHVertRef vertex);
bool SCULPT_vertex_visible_get(const SculptSession *ss, PBVHVertRef vertex);
bool SCULPT_vertex_all_faces_visible_get(const SculptSession *ss, PBVHVertRef vertex);
bool SCULPT_vertex_any_face_visible_get(SculptSession *ss, PBVHVertRef vertex);
void SCULPT_face_visibility_all_set(SculptSession *ss, bool visible);
void SCULPT_visibility_sync_all_from_faces(Object *ob);
/** \} */
@@ -999,8 +998,6 @@ bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, PBVHVertRef vertex);
int SCULPT_face_set_next_available_get(SculptSession *ss);
void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible);
/** \} */
/* -------------------------------------------------------------------- */

View File

@@ -181,6 +181,7 @@ static char *undo_type_to_str(int type)
_(SCULPT_UNDO_DYNTOPO_SYMMETRIZE)
_(SCULPT_UNDO_FACE_SETS)
_(SCULPT_UNDO_HIDDEN)
_(SCULPT_UNDO_FACE_HIDDEN)
_(SCULPT_UNDO_MASK)
_(SCULPT_UNDO_COLOR)
default:
@@ -281,10 +282,11 @@ void sculpt_undo_print_nodes(Object *ob, void *active)
struct PartialUpdateData {
PBVH *pbvh;
bool changed_position;
bool changed_hide;
bool changed_hide_vert;
bool changed_mask;
Span<bool> modified_grids;
Span<bool> modified_hidden_verts;
Span<bool> modified_hidden_faces;
Span<bool> modified_mask_verts;
Span<bool> modified_color_verts;
Span<bool> modified_face_set_faces;
@@ -322,14 +324,29 @@ static void update_modified_node_mesh(PBVHNode *node, void *userdata)
}
}
Vector<int> faces;
if (!data->modified_face_set_faces.is_empty()) {
for (const int face : BKE_pbvh_node_calc_face_indices(*data->pbvh, *node)) {
if (faces.is_empty()) {
faces = BKE_pbvh_node_calc_face_indices(*data->pbvh, *node);
}
for (const int face : faces) {
if (data->modified_face_set_faces[face]) {
BKE_pbvh_node_mark_update_face_sets(node);
break;
}
}
}
if (!data->modified_hidden_faces.is_empty()) {
if (faces.is_empty()) {
faces = BKE_pbvh_node_calc_face_indices(*data->pbvh, *node);
}
for (const int face : faces) {
if (data->modified_hidden_faces[face]) {
BKE_pbvh_node_mark_update_visibility(node);
break;
}
}
}
}
static void update_modified_node_grids(PBVHNode *node, void *userdata)
@@ -346,18 +363,34 @@ static void update_modified_node_grids(PBVHNode *node, void *userdata)
if (data->changed_mask) {
BKE_pbvh_node_mark_update_mask(node);
}
if (data->changed_hide) {
if (data->changed_hide_vert) {
BKE_pbvh_node_mark_update_visibility(node);
}
}
Vector<int> faces;
if (!data->modified_face_set_faces.is_empty()) {
for (const int face : BKE_pbvh_node_calc_face_indices(*data->pbvh, *node)) {
if (faces.is_empty()) {
faces = BKE_pbvh_node_calc_face_indices(*data->pbvh, *node);
}
for (const int face : faces) {
if (data->modified_face_set_faces[face]) {
BKE_pbvh_node_mark_update_face_sets(node);
break;
}
}
}
if (!data->modified_hidden_faces.is_empty()) {
if (faces.is_empty()) {
faces = BKE_pbvh_node_calc_face_indices(*data->pbvh, *node);
}
for (const int face : faces) {
if (data->modified_hidden_faces[face]) {
BKE_pbvh_node_mark_update_visibility(node);
break;
}
}
}
}
static bool test_swap_v3_v3(float a[3], float b[3])
@@ -534,6 +567,34 @@ static bool sculpt_undo_restore_hidden(Object *ob,
return true;
}
static bool sculpt_undo_restore_hidden_face(Object &object,
SculptUndoNode &unode,
MutableSpan<bool> modified_faces)
{
using namespace blender;
SculptSession *ss = object.sculpt;
Mesh &mesh = *static_cast<Mesh *>(object.data);
bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
bke::SpanAttributeWriter hide_poly = attributes.lookup_or_add_for_write_span<bool>(
".hide_poly", ATTR_DOMAIN_FACE);
const Span<int> face_indices = unode.face_indices;
bool modified = false;
for (const int i : face_indices.index_range()) {
const int face = face_indices[i];
if (unode.face_hidden[i].test() != hide_poly.span[face]) {
unode.face_hidden[i].set(!unode.face_hidden[i].test());
hide_poly.span[face] = !hide_poly.span[face];
modified_faces[face] = true;
modified = true;
}
}
hide_poly.finish();
ss->hide_poly = BKE_sculpt_hide_poly_ensure(&mesh);
return modified;
}
static bool sculpt_undo_restore_color(Object *ob,
SculptUndoNode *unode,
MutableSpan<bool> modified_vertices)
@@ -894,7 +955,8 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
bool changed_all_geometry = false;
bool changed_position = false;
bool changed_hide = false;
bool changed_hide_vert = false;
bool changed_hide_face = false;
bool changed_mask = false;
bool changed_face_sets = false;
bool changed_color = false;
@@ -903,6 +965,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
* track of other updates. In order to tell the corresponding PBVH nodes to update, keep track
* of which elements were updated for specific layers. */
Vector<bool> modified_verts_hide;
Vector<bool> modified_faces_hide;
Vector<bool> modified_verts_mask;
Vector<bool> modified_verts_color;
Vector<bool> modified_faces_face_set;
@@ -936,7 +999,13 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
case SCULPT_UNDO_HIDDEN:
modified_verts_hide.resize(ss->totvert, false);
if (sculpt_undo_restore_hidden(ob, unode, modified_verts_hide)) {
changed_hide = true;
changed_hide_vert = true;
}
break;
case SCULPT_UNDO_FACE_HIDDEN:
modified_faces_hide.resize(ss->totfaces, false);
if (sculpt_undo_restore_hidden_face(*ob, *unode, modified_faces_hide)) {
changed_hide_face = true;
}
break;
case SCULPT_UNDO_MASK:
@@ -987,7 +1056,8 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
if (!changed_position && !changed_hide && !changed_mask && !changed_face_sets && !changed_color)
if (!changed_position && !changed_hide_vert && !changed_hide_face && !changed_mask &&
!changed_face_sets && !changed_color)
{
return;
}
@@ -997,11 +1067,12 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
* the nodes get recreated, though in that case it could do all. */
PartialUpdateData data{};
data.changed_position = changed_position;
data.changed_hide = changed_hide;
data.changed_hide_vert = changed_hide_vert;
data.changed_mask = changed_mask;
data.pbvh = ss->pbvh;
data.modified_grids = modified_grids;
data.modified_hidden_verts = modified_verts_hide;
data.modified_hidden_faces = modified_faces_hide;
data.modified_mask_verts = modified_verts_mask;
data.modified_color_verts = modified_verts_color;
data.modified_face_set_faces = modified_faces_face_set;
@@ -1018,8 +1089,11 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
if (changed_mask) {
BKE_pbvh_update_mask(ss->pbvh);
}
if (changed_hide) {
if (changed_hide_face) {
SCULPT_visibility_sync_all_from_faces(ob);
BKE_pbvh_update_visibility(ss->pbvh);
}
if (changed_hide_vert) {
if (ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_GRIDS)) {
Mesh &mesh = *static_cast<Mesh *>(ob->data);
BKE_pbvh_sync_visibility_from_verts(ss->pbvh, &mesh);
@@ -1028,7 +1102,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
}
if (BKE_sculpt_multires_active(scene, ob)) {
if (changed_hide) {
if (changed_hide_vert) {
multires_mark_as_modified(depsgraph, ob, MULTIRES_HIDDEN_MODIFIED);
}
else if (changed_position) {
@@ -1159,7 +1233,7 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt
unode->totvert = totvert;
bool need_loops = type == SCULPT_UNDO_COLOR;
const bool need_faces = type == SCULPT_UNDO_FACE_SETS;
const bool need_faces = ELEM(type, SCULPT_UNDO_FACE_SETS, SCULPT_UNDO_FACE_HIDDEN);
if (need_loops) {
int totloop;
@@ -1199,6 +1273,11 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt
break;
}
case SCULPT_UNDO_FACE_HIDDEN: {
unode->face_hidden.resize(unode->face_indices.size());
usculpt->undo_size += BLI_BITMAP_SIZE(unode->face_indices.size());
break;
}
case SCULPT_UNDO_MASK: {
unode->mask.reinitialize(allvert);
usculpt->undo_size += unode->mask.as_span().size_in_bytes();
@@ -1278,13 +1357,12 @@ static void sculpt_undo_store_coords(Object *ob, SculptUndoNode *unode)
static void sculpt_undo_store_hidden(Object *ob, SculptUndoNode *unode)
{
using namespace blender;
using namespace blender::bke;
if (!unode->grids.is_empty()) {
/* Already stored during allocation. */
}
const Mesh &mesh = *static_cast<const Mesh *>(ob->data);
const AttributeAccessor attributes = mesh.attributes();
const bke::AttributeAccessor attributes = mesh.attributes();
const VArraySpan<bool> hide_vert = *attributes.lookup<bool>(".hide_vert", ATTR_DOMAIN_POINT);
if (hide_vert.is_empty()) {
return;
@@ -1296,6 +1374,21 @@ static void sculpt_undo_store_hidden(Object *ob, SculptUndoNode *unode)
unode->vert_hidden[i].set(hide_vert[verts[i]]);
}
static void sculpt_undo_store_face_hidden(Object &object, SculptUndoNode &unode)
{
using namespace blender;
const Mesh &mesh = *static_cast<const Mesh *>(object.data);
const bke::AttributeAccessor attributes = mesh.attributes();
const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", ATTR_DOMAIN_FACE);
if (hide_poly.is_empty()) {
unode.face_hidden.fill(false);
return;
}
const Span<int> faces = unode.face_indices;
for (const int i : faces.index_range())
unode.face_hidden[i].set(hide_poly[faces[i]]);
}
static void sculpt_undo_store_mask(Object *ob, SculptUndoNode *unode)
{
SculptSession *ss = ob->sculpt;
@@ -1403,6 +1496,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt
BKE_pbvh_vertex_iter_end;
break;
case SCULPT_UNDO_FACE_HIDDEN:
case SCULPT_UNDO_HIDDEN: {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) {
BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset);
@@ -1489,6 +1583,9 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType
case SCULPT_UNDO_HIDDEN:
sculpt_undo_store_hidden(ob, unode);
break;
case SCULPT_UNDO_FACE_HIDDEN:
sculpt_undo_store_face_hidden(*ob, *unode);
break;
case SCULPT_UNDO_MASK:
if (pbvh_has_mask(ss->pbvh)) {
sculpt_undo_store_mask(ob, unode);