From 251069ae5b71efcc85b5177de40cc8dd60d8913a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 6 Dec 2023 17:30:11 -0500 Subject: [PATCH] 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 4e66769ec09461384af48b0bfdafc28884f01d19 --- source/blender/blenkernel/BKE_pbvh_api.hh | 4 + source/blender/blenkernel/intern/pbvh.cc | 8 +- .../editors/sculpt_paint/paint_hide.cc | 54 ++--- .../editors/sculpt_paint/paint_intern.hh | 5 + source/blender/editors/sculpt_paint/sculpt.cc | 40 ---- .../editors/sculpt_paint/sculpt_face_set.cc | 194 ++++++++++-------- .../editors/sculpt_paint/sculpt_intern.hh | 7 +- .../editors/sculpt_paint/sculpt_undo.cc | 125 +++++++++-- 8 files changed, 265 insertions(+), 172 deletions(-) diff --git a/source/blender/blenkernel/BKE_pbvh_api.hh b/source/blender/blenkernel/BKE_pbvh_api.hh index beb7d928d7f..4a3dd67b83c 100644 --- a/source/blender/blenkernel/BKE_pbvh_api.hh +++ b/source/blender/blenkernel/BKE_pbvh_api.hh @@ -384,6 +384,10 @@ int BKE_pbvh_node_num_unique_verts(const PBVH &pbvh, const PBVHNode &node); blender::Span BKE_pbvh_node_get_vert_indices(const PBVHNode *node); blender::Span 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 &faces); blender::Vector BKE_pbvh_node_calc_face_indices(const PBVH &pbvh, const PBVHNode &node); /* Get number of faces in the mesh; for PBVH_GRIDS the diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 81529305344..7f6dd71f64b 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -1811,9 +1811,8 @@ blender::Span BKE_pbvh_node_get_unique_vert_indices(const PBVHNode *node) return node->vert_indices.as_span().take_front(node->uniq_verts); } -blender::Vector 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 &faces) { - Vector faces; switch (pbvh.header.type) { case PBVH_FACES: { const Span looptri_faces = pbvh.looptri_faces; @@ -1843,7 +1842,12 @@ blender::Vector BKE_pbvh_node_calc_face_indices(const PBVH &pbvh, const PBV BLI_assert_unreachable(); break; } +} +blender::Vector BKE_pbvh_node_calc_face_indices(const PBVH &pbvh, const PBVHNode &node) +{ + Vector faces; + BKE_pbvh_node_calc_face_indices(pbvh, node, faces); return faces; } diff --git a/source/blender/editors/sculpt_paint/paint_hide.cc b/source/blender/editors/sculpt_paint/paint_hide.cc index 9f0c7c251e7..141484360e2 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.cc +++ b/source/blender/editors/sculpt_paint/paint_hide.cc @@ -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 nodes) +void mesh_show_all(Object &object, const Span nodes) { Mesh &mesh = *static_cast(object.data); bke::MutableAttributeAccessor attributes = mesh.attributes_for_write(); @@ -110,9 +125,7 @@ static void vert_show_all(Object &object, const Span nodes) BKE_mesh_flush_hidden_from_verts(&mesh); } -static bool vert_hide_is_changed(const Span verts, - const Span orig_hide, - const Span new_hide) +bool hide_is_changed(const Span verts, const Span orig_hide, const Span 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 mask = *attributes.lookup(".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 verts, MutableSpan hide) { @@ -219,12 +232,13 @@ static void partialvis_update_mesh(Object &object, } } -static void grids_show_all(Depsgraph &depsgraph, Object &object, const Span nodes) +void grids_show_all(Depsgraph &depsgraph, Object &object, const Span nodes) { Mesh &mesh = *static_cast(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 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 nodes static int visibility_invert_exec(bContext *C, wmOperator *op) { - ARegion ®ion = *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(®ion); + tag_update_visibility(*C); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/sculpt_paint/paint_intern.hh b/source/blender/editors/sculpt_paint/paint_intern.hh index b297a49455d..905128fb8f3 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.hh +++ b/source/blender/editors/sculpt_paint/paint_intern.hh @@ -455,6 +455,11 @@ enum BrushStrokeMode { /* paint_hide.cc */ namespace blender::ed::sculpt_paint::hide { +bool hide_is_changed(Span verts, Span orig_hide, Span new_hide); +void mesh_show_all(Object &object, const Span nodes); +void grids_show_all(Depsgraph &depsgraph, Object &object, Span 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 diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 2d08ce5c17b..6ace76a0fcc 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -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)) { diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 3dd8a403e43..783e551c884 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -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 nodes, + const FunctionRef, MutableSpan)> calc_hide) +{ + PBVH &pbvh = *object.sculpt->pbvh; + Mesh &mesh = *static_cast(object.data); + bke::MutableAttributeAccessor attributes = mesh.attributes_for_write(); + bke::SpanAttributeWriter hide_poly = attributes.lookup_or_add_for_write_span( + ".hide_poly", ATTR_DOMAIN_FACE); + + struct TLS { + Vector face_indices; + Vector new_hide; + }; + + bool any_changed = false; + threading::EnumerableThreadSpecific 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 faces = tls.face_indices; + + tls.new_hide.reinitialize(faces.size()); + MutableSpan 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 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 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 hide_poly = *attributes.lookup(".hide_poly", ATTR_DOMAIN_FACE); + const VArraySpan face_sets = *attributes.lookup(".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 faces, MutableSpan 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 faces, MutableSpan 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 /*faces*/, MutableSpan hide) { + hide.fill(true); + }); } else { - SCULPT_face_visibility_all_set(ss, false); + face_hide_update(object, nodes, [&](const Span faces, MutableSpan 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; } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index a377f951094..84653c6490b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -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 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); - /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 961d30b8039..8b1ebbf3b4b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -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 modified_grids; Span modified_hidden_verts; + Span modified_hidden_faces; Span modified_mask_verts; Span modified_color_verts; Span modified_face_set_faces; @@ -322,14 +324,29 @@ static void update_modified_node_mesh(PBVHNode *node, void *userdata) } } + Vector 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 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 modified_faces) +{ + using namespace blender; + SculptSession *ss = object.sculpt; + Mesh &mesh = *static_cast(object.data); + bke::MutableAttributeAccessor attributes = mesh.attributes_for_write(); + bke::SpanAttributeWriter hide_poly = attributes.lookup_or_add_for_write_span( + ".hide_poly", ATTR_DOMAIN_FACE); + + const Span 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 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 modified_verts_hide; + Vector modified_faces_hide; Vector modified_verts_mask; Vector modified_verts_color; Vector 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(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(ob->data); - const AttributeAccessor attributes = mesh.attributes(); + const bke::AttributeAccessor attributes = mesh.attributes(); const VArraySpan hide_vert = *attributes.lookup(".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(object.data); + const bke::AttributeAccessor attributes = mesh.attributes(); + const VArraySpan hide_poly = *attributes.lookup(".hide_poly", ATTR_DOMAIN_FACE); + if (hide_poly.is_empty()) { + unode.face_hidden.fill(false); + return; + } + const Span 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);