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