diff --git a/source/blender/blenkernel/BKE_paint.hh b/source/blender/blenkernel/BKE_paint.hh index b05529da27f..ab7e9a5f80a 100644 --- a/source/blender/blenkernel/BKE_paint.hh +++ b/source/blender/blenkernel/BKE_paint.hh @@ -18,6 +18,7 @@ #include "BLI_offset_indices.hh" #include "BLI_ordered_edge.hh" #include "BLI_set.hh" +#include "BLI_shared_cache.hh" #include "BLI_utility_mixins.hh" #include "DNA_brush_enums.h" @@ -470,6 +471,13 @@ struct SculptSession : blender::NonCopyable, blender::NonMovable { /* Crazy-space deformation matrices. */ blender::Array deform_imats; + /** + * Normals corresponding to the #deform_cos evaluated/deform positions. Stored as a #SharedCache + * for consistency with mesh caches in #MeshRuntime::vert_normals_cache. + */ + blender::SharedCache> vert_normals_deform; + blender::SharedCache> face_normals_deform; + /* Pool for texture evaluations. */ ImagePool *tex_pool = nullptr; diff --git a/source/blender/blenkernel/BKE_pbvh_api.hh b/source/blender/blenkernel/BKE_pbvh_api.hh index 0d660fbd434..5846fb0526f 100644 --- a/source/blender/blenkernel/BKE_pbvh_api.hh +++ b/source/blender/blenkernel/BKE_pbvh_api.hh @@ -183,23 +183,9 @@ class Tree { /* Memory backing for Node.prim_indices. */ Array prim_indices_; - /** Local array used when not sculpting base mesh positions directly. */ - Array vert_positions_deformed_; - /** Local array used when not sculpting base mesh positions directly. */ - Array vert_normals_deformed_; - /** Local array used when not sculpting base mesh positions directly. */ - Array face_normals_deformed_; - - MutableSpan vert_positions_; - Span vert_normals_; - Span face_normals_; - /* Grid Data */ SubdivCCG *subdiv_ccg_ = nullptr; - /* flag are verts/faces deformed */ - bool deformed_ = false; - float planes_[6][4]; int num_planes_; @@ -261,7 +247,6 @@ namespace blender::bke::pbvh { * Do a full rebuild with on Mesh data structure. */ std::unique_ptr build_mesh(Mesh *mesh); -void update_mesh_pointers(Tree &pbvh, Mesh *mesh); /** * Do a full rebuild with on Grids data structure. */ @@ -515,7 +500,6 @@ void BKE_pbvh_subdiv_cgg_set(blender::bke::pbvh::Tree &pbvh, SubdivCCG *subdiv_c void BKE_pbvh_vert_coords_apply(blender::bke::pbvh::Tree &pbvh, blender::Span vert_positions); -bool BKE_pbvh_is_deformed(const blender::bke::pbvh::Tree &pbvh); void BKE_pbvh_node_get_bm_orco_data(blender::bke::pbvh::Node *node, int (**r_orco_tris)[3], @@ -530,7 +514,7 @@ namespace blender::bke::pbvh { * topology-changing operations. If there are no deform modifiers, this returns the original mesh's * vertex positions. */ -Span vert_positions_eval(const Depsgraph &depsgraph, const Object &object); +Span vert_positions_eval(const Depsgraph &depsgraph, const Object &object_orig); Span vert_positions_eval_from_eval(const Object &object_eval); /** @@ -539,13 +523,13 @@ Span vert_positions_eval_from_eval(const Object &object_eval); * they are used for drawing and we don't run a full dependency graph update whenever they are * changed. */ -MutableSpan vert_positions_eval_for_write(const Depsgraph &depsgraph, Object &object); +MutableSpan vert_positions_eval_for_write(const Depsgraph &depsgraph, Object &object_orig); /** * Return the vertex normals corresponding the the positions from #vert_positions_eval. This may be * a reference to the normals cache on the original mesh. */ -Span vert_normals_eval(const Depsgraph &depsgraph, const Object &object); +Span vert_normals_eval(const Depsgraph &depsgraph, const Object &object_orig); Span vert_normals_eval_from_eval(const Object &object_eval); } // namespace blender::bke::pbvh diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 0dee816f51c..c58b90b4ebe 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2052,16 +2052,12 @@ static void sculpt_update_object(Depsgraph *depsgraph, /* if pbvh is deformed, key block is already applied to it */ if (ss.shapekey_active) { - bool pbvh_deformed = BKE_pbvh_is_deformed(*ss.pbvh); - if (!pbvh_deformed || ss.deform_cos.is_empty()) { + if (ss.deform_cos.is_empty()) { const Span key_data(static_cast(ss.shapekey_active->data), mesh_orig->verts_num); if (key_data.data() != nullptr) { - if (!pbvh_deformed) { - /* apply shape keys coordinates to pbvh::Tree */ - BKE_pbvh_vert_coords_apply(*ss.pbvh, key_data); - } + BKE_pbvh_vert_coords_apply(*ss.pbvh, key_data); if (ss.deform_cos.is_empty()) { ss.deform_cos = key_data; } @@ -2390,18 +2386,6 @@ blender::bke::pbvh::Tree *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Ob } if (ob->sculpt->pbvh) { - /* NOTE: It is possible that pointers to grids or other geometry data changed. Need to update - * those pointers. */ - const pbvh::Type pbvh_type = ob->sculpt->pbvh->type(); - switch (pbvh_type) { - case pbvh::Type::Mesh: - pbvh::update_mesh_pointers(*ob->sculpt->pbvh, BKE_object_get_original_mesh(ob)); - break; - case pbvh::Type::Grids: - case pbvh::Type::BMesh: - break; - } - return ob->sculpt->pbvh.get(); } diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 2c31c196a2a..2b72df8fcad 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -35,6 +35,7 @@ #include "BKE_ccg.hh" #include "BKE_mesh.hh" #include "BKE_mesh_mapping.hh" +#include "BKE_object.hh" #include "BKE_paint.hh" #include "BKE_pbvh_api.hh" #include "BKE_subdiv_ccg.hh" @@ -341,23 +342,9 @@ static void build_nodes_recursive_mesh(const Span corner_verts, nodes); } -void update_mesh_pointers(Tree &pbvh, Mesh *mesh) -{ - BLI_assert(pbvh.type() == Type::Mesh); - if (!pbvh.deformed_) { - /* Deformed data not matching the original mesh are owned directly by the - * Tree, and are set separately by #BKE_pbvh_vert_coords_apply. */ - pbvh.vert_positions_ = mesh->vert_positions_for_write(); - pbvh.vert_normals_ = mesh->vert_normals(); - pbvh.face_normals_ = mesh->face_normals(); - } -} - std::unique_ptr build_mesh(Mesh *mesh) { std::unique_ptr pbvh = std::make_unique(Type::Mesh); - update_mesh_pointers(*pbvh, mesh); - MutableSpan vert_positions = mesh->vert_positions_for_write(); const Span corner_verts = mesh->corner_verts(); const Span corner_tris = mesh->corner_tris(); @@ -846,6 +833,79 @@ static bool update_search(Node *node, const int flag) return true; } +/** + * Logic used to test whether to use the evaluated mesh for positions. + * \todo A deeper test of equality of topology array pointers would be better. This is kept for now + * to avoid changing logic during a refactor. + */ +static bool mesh_topology_count_matches(const Mesh &a, const Mesh &b) +{ + return a.faces_num == b.faces_num && a.corners_num == b.corners_num && + a.verts_num == b.verts_num; +} + +static const SharedCache> &vert_normals_cache_eval(const Object &object_orig, + const Object &object_eval) +{ + const SculptSession &ss = *object_orig.sculpt; + const Mesh &mesh_orig = *static_cast(object_orig.data); + BLI_assert(object_orig.sculpt->pbvh->type() == Type::Mesh); + if (object_orig.mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) { + if (const Mesh *mesh_eval = BKE_object_get_evaluated_mesh_no_subsurf(&object_eval)) { + if (mesh_topology_count_matches(*mesh_eval, mesh_orig)) { + return mesh_eval->runtime->vert_normals_cache; + } + } + if (const Mesh *mesh_eval = BKE_object_get_mesh_deform_eval(&object_eval)) { + return mesh_eval->runtime->vert_normals_cache; + } + } + + if (!ss.deform_cos.is_empty()) { + BLI_assert(ss.deform_cos.size() == mesh_orig.verts_num); + return ss.vert_normals_deform; + } + + return mesh_orig.runtime->vert_normals_cache; +} +static SharedCache> &vert_normals_cache_eval_for_write(Object &object_orig, + Object &object_eval) +{ + return const_cast> &>( + vert_normals_cache_eval(object_orig, object_eval)); +} + +static const SharedCache> &face_normals_cache_eval(const Object &object_orig, + const Object &object_eval) +{ + const SculptSession &ss = *object_orig.sculpt; + const Mesh &mesh_orig = *static_cast(object_orig.data); + BLI_assert(object_orig.sculpt->pbvh->type() == Type::Mesh); + if (object_orig.mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) { + if (const Mesh *mesh_eval = BKE_object_get_evaluated_mesh_no_subsurf(&object_eval)) { + if (mesh_topology_count_matches(*mesh_eval, mesh_orig)) { + return mesh_eval->runtime->face_normals_cache; + } + } + if (const Mesh *mesh_eval = BKE_object_get_mesh_deform_eval(&object_eval)) { + return mesh_eval->runtime->face_normals_cache; + } + } + + if (!ss.deform_cos.is_empty()) { + BLI_assert(ss.deform_cos.size() == mesh_orig.verts_num); + return ss.face_normals_deform; + } + + return mesh_orig.runtime->face_normals_cache; +} +static SharedCache> &face_normals_cache_eval_for_write(Object &object_orig, + Object &object_eval) +{ + return const_cast> &>( + face_normals_cache_eval(object_orig, object_eval)); +} + static void normals_calc_faces(const Span positions, const OffsetIndices faces, const Span corner_verts, @@ -940,13 +1000,17 @@ static void update_normals_mesh(Object &object_orig, Object &object_eval, Span(object_orig.data); - Tree &pbvh = *object_orig.sculpt->pbvh; const Span positions = bke::pbvh::vert_positions_eval_from_eval(object_eval); const OffsetIndices faces = mesh.faces(); const Span corner_verts = mesh.corner_verts(); const Span tri_faces = mesh.corner_tri_faces(); const GroupedSpan vert_to_face_map = mesh.vert_to_face_map(); + SharedCache> &vert_normals_cache = vert_normals_cache_eval_for_write(object_orig, + object_eval); + SharedCache> &face_normals_cache = face_normals_cache_eval_for_write(object_orig, + object_eval); + VectorSet boundary_faces; for (const Node *node : nodes) { for (const int vert : node->vert_indices_.as_span().drop_front(node->unique_verts_num_)) { @@ -954,35 +1018,21 @@ static void update_normals_mesh(Object &object_orig, Object &object_eval, Spanface_normals_cache.is_dirty()) { - mesh.face_normals(); - } - if (mesh.runtime->vert_normals_cache.is_dirty()) { - mesh.vert_normals(); - } - } - VectorSet boundary_verts; + threading::parallel_invoke( [&]() { - if (pbvh.deformed_) { - calc_node_face_normals( - positions, faces, corner_verts, tri_faces, nodes, pbvh.face_normals_deformed_); - calc_boundary_face_normals( - positions, faces, corner_verts, boundary_faces, pbvh.face_normals_deformed_); + if (face_normals_cache.is_dirty()) { + face_normals_cache.ensure([&](Vector &r_data) { + r_data.resize(faces.size()); + bke::mesh::normals_calc_faces(positions, faces, corner_verts, r_data); + }); } else { - mesh.runtime->face_normals_cache.update([&](Vector &r_data) { + face_normals_cache.update([&](Vector &r_data) { calc_node_face_normals(positions, faces, corner_verts, tri_faces, nodes, r_data); calc_boundary_face_normals(positions, faces, corner_verts, boundary_faces, r_data); }); - /* #SharedCache::update() reallocates cached vectors if they were shared initially. */ - pbvh.face_normals_ = mesh.runtime->face_normals_cache.data(); } }, [&]() { @@ -992,19 +1042,20 @@ static void update_normals_mesh(Object &object_orig, Object &object_eval, Span face_normals = face_normals_cache.data(); - if (pbvh.deformed_) { - calc_node_vert_normals( - vert_to_face_map, pbvh.face_normals_, nodes, pbvh.vert_normals_deformed_); - calc_boundary_vert_normals( - vert_to_face_map, pbvh.face_normals_, boundary_verts, pbvh.vert_normals_deformed_); + if (vert_normals_cache.is_dirty()) { + vert_normals_cache.ensure([&](Vector &r_data) { + r_data.resize(positions.size()); + mesh::normals_calc_verts( + positions, faces, corner_verts, vert_to_face_map, face_normals, r_data); + }); } else { - mesh.runtime->vert_normals_cache.update([&](Vector &r_data) { - calc_node_vert_normals(vert_to_face_map, pbvh.face_normals_, nodes, r_data); - calc_boundary_vert_normals(vert_to_face_map, pbvh.face_normals_, boundary_verts, r_data); + vert_normals_cache.update([&](Vector &r_data) { + calc_node_vert_normals(vert_to_face_map, face_normals, nodes, r_data); + calc_boundary_vert_normals(vert_to_face_map, face_normals, boundary_verts, r_data); }); - pbvh.vert_normals_ = mesh.runtime->vert_normals_cache.data(); } for (Node *node : nodes) { @@ -2473,7 +2524,8 @@ static blender::draw::pbvh::PBVH_GPU_Args pbvh_draw_args_init(const Object &obje args.corner_edges = mesh_eval.corner_edges(); args.corner_tris = mesh_eval.corner_tris(); args.vert_normals = blender::bke::pbvh::vert_normals_eval_from_eval(object_eval); - args.face_normals = pbvh.face_normals_; + args.face_normals = + blender::bke::pbvh::face_normals_cache_eval(object_orig, object_eval).data(); args.hide_poly = *mesh_orig.attributes().lookup(".hide_poly", blender::bke::AttrDomain::Face); @@ -2668,46 +2720,11 @@ void BKE_pbvh_vert_coords_apply(blender::bke::pbvh::Tree &pbvh, const blender::Span vert_positions) { using namespace blender::bke::pbvh; - - if (!pbvh.deformed_) { - if (!pbvh.vert_positions_.is_empty()) { - /* When the Tree is deformed, it creates a separate vertex position array - * that it owns directly. Conceptually these copies often aren't and often adds extra - * indirection, but: - * - Sculpting shape keys, the deformations are flushed back to the keys as a separate step. - * - Sculpting on a deformed mesh, deformations are also flushed to original positions - * separately. - * - The Tree currently always assumes we want to change positions, and - * has no way to avoid calculating normals if it's only used for painting, for example. */ - pbvh.vert_positions_deformed_ = pbvh.vert_positions_.as_span(); - pbvh.vert_positions_ = pbvh.vert_positions_deformed_; - - pbvh.vert_normals_deformed_ = pbvh.vert_normals_; - pbvh.vert_normals_ = pbvh.vert_normals_deformed_; - - pbvh.face_normals_deformed_ = pbvh.face_normals_; - pbvh.face_normals_ = pbvh.face_normals_deformed_; - - pbvh.deformed_ = true; - } + for (Node &node : pbvh.nodes_) { + BKE_pbvh_node_mark_positions_update(node); } - - if (!pbvh.vert_positions_.is_empty()) { - blender::MutableSpan positions = pbvh.vert_positions_; - positions.copy_from(vert_positions); - - for (Node &node : pbvh.nodes_) { - BKE_pbvh_node_mark_positions_update(node); - } - - update_bounds_mesh(vert_positions, pbvh); - store_bounds_orig(pbvh); - } -} - -bool BKE_pbvh_is_deformed(const blender::bke::pbvh::Tree &pbvh) -{ - return pbvh.deformed_; + update_bounds_mesh(vert_positions, pbvh); + store_bounds_orig(pbvh); } namespace blender::bke::pbvh { @@ -2728,38 +2745,87 @@ void get_frustum_planes(const Tree &pbvh, PBVHFrustumPlanes *planes) } } -Span vert_positions_eval(const Depsgraph & /*depsgraph*/, const Object &object) +static Span vert_positions_eval(const Object &object_orig, const Object &object_eval) { - BLI_assert(object.sculpt->pbvh->type() == Type::Mesh); - return object.sculpt->pbvh->vert_positions_; + const SculptSession &ss = *object_orig.sculpt; + const Mesh &mesh_orig = *static_cast(object_orig.data); + BLI_assert(object_orig.sculpt->pbvh->type() == Type::Mesh); + if (object_orig.mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) { + if (const Mesh *mesh_eval = BKE_object_get_evaluated_mesh_no_subsurf(&object_eval)) { + if (mesh_topology_count_matches(*mesh_eval, mesh_orig)) { + return mesh_eval->vert_positions(); + } + } + if (const Mesh *mesh_eval = BKE_object_get_mesh_deform_eval(&object_eval)) { + return mesh_eval->vert_positions(); + } + } + + if (!ss.deform_cos.is_empty()) { + BLI_assert(ss.deform_cos.size() == mesh_orig.verts_num); + return ss.deform_cos; + } + + return mesh_orig.vert_positions(); +} +static MutableSpan vert_positions_eval_for_write(Object &object_orig, Object &object_eval) +{ + SculptSession &ss = *object_orig.sculpt; + Mesh &mesh_orig = *static_cast(object_orig.data); + BLI_assert(object_orig.sculpt->pbvh->type() == Type::Mesh); + if (object_orig.mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) { + if (const Mesh *mesh_eval = BKE_object_get_evaluated_mesh_no_subsurf(&object_eval)) { + if (mesh_topology_count_matches(*mesh_eval, mesh_orig)) { + Mesh *mesh_eval_mut = const_cast(mesh_eval); + return mesh_eval_mut->vert_positions_for_write(); + } + } + if (const Mesh *mesh_eval = BKE_object_get_mesh_deform_eval(&object_eval)) { + Mesh *mesh_eval_mut = const_cast(mesh_eval); + return mesh_eval_mut->vert_positions_for_write(); + } + } + + if (!ss.deform_cos.is_empty()) { + BLI_assert(ss.deform_cos.size() == mesh_orig.verts_num); + return ss.deform_cos; + } + + return mesh_orig.vert_positions_for_write(); +} + +Span vert_positions_eval(const Depsgraph &depsgraph, const Object &object_orig) +{ + const Object &object_eval = *DEG_get_evaluated_object(&depsgraph, + &const_cast(object_orig)); + return vert_positions_eval(object_orig, object_eval); } Span vert_positions_eval_from_eval(const Object &object_eval) { BLI_assert(!DEG_is_original_object(&object_eval)); - Object &object_orig = *DEG_get_original_object(&const_cast(object_eval)); - BLI_assert(object_orig.sculpt->pbvh->type() == Type::Mesh); - return object_orig.sculpt->pbvh->vert_positions_; + const Object &object_orig = *DEG_get_original_object(&const_cast(object_eval)); + return vert_positions_eval(object_orig, object_eval); } -MutableSpan vert_positions_eval_for_write(const Depsgraph & /*depsgraph*/, Object &object) +MutableSpan vert_positions_eval_for_write(const Depsgraph &depsgraph, Object &object_orig) { - BLI_assert(object.sculpt->pbvh->type() == Type::Mesh); - return object.sculpt->pbvh->vert_positions_; + Object &object_eval = *DEG_get_evaluated_object(&depsgraph, &object_orig); + return vert_positions_eval_for_write(object_orig, object_eval); } -Span vert_normals_eval(const Depsgraph & /*depsgraph*/, const Object &object) +Span vert_normals_eval(const Depsgraph &depsgraph, const Object &object_orig) { - BLI_assert(object.sculpt->pbvh->type() == Type::Mesh); - return object.sculpt->pbvh->vert_normals_; + const Object &object_eval = *DEG_get_evaluated_object(&depsgraph, + &const_cast(object_orig)); + return vert_normals_cache_eval(object_orig, object_eval).data(); } Span vert_normals_eval_from_eval(const Object &object_eval) { BLI_assert(!DEG_is_original_object(&object_eval)); Object &object_orig = *DEG_get_original_object(&const_cast(object_eval)); - BLI_assert(object_orig.sculpt->pbvh->type() == Type::Mesh); - return object_orig.sculpt->pbvh->vert_normals_; + return vert_normals_cache_eval(object_orig, object_eval).data(); } } // namespace blender::bke::pbvh diff --git a/source/blender/editors/sculpt_paint/mesh_brush_common.hh b/source/blender/editors/sculpt_paint/mesh_brush_common.hh index 2d1d191221b..02d1fbe4244 100644 --- a/source/blender/editors/sculpt_paint/mesh_brush_common.hh +++ b/source/blender/editors/sculpt_paint/mesh_brush_common.hh @@ -430,17 +430,6 @@ void update_shape_keys(Object &object, Span translations, Span positions_orig); -/** - * Currently the pbvh::Tree owns its own copy of deformed positions that needs to be updated to - * stay in sync with brush deformations. - * \todo This should be removed one the pbvh::Tree no longer stores this copy of deformed - * positions. - */ -void apply_translations_to_pbvh(const Depsgraph &depsgraph, - Object &object, - Span verts, - Span translations); - /** * Write the new translated positions to the original mesh, taking into account inverse * deformation from modifiers, axis locking, and clipping. Flush the deformation to shape keys as diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 7c3ec366be5..67602b4bda2 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -7209,21 +7209,6 @@ void update_shape_keys(Object &object, } } -void apply_translations_to_pbvh(const Depsgraph &depsgraph, - Object &object, - const Span verts, - const Span translations) -{ - if (!BKE_pbvh_is_deformed(*object.sculpt->pbvh)) { - return; - } - MutableSpan pbvh_positions = bke::pbvh::vert_positions_eval_for_write(depsgraph, object); - for (const int i : verts.index_range()) { - const int vert = verts[i]; - pbvh_positions[vert] += translations[i]; - } -} - void write_translations(const Depsgraph &depsgraph, const Sculpt &sd, Object &object, @@ -7236,7 +7221,11 @@ void write_translations(const Depsgraph &depsgraph, clip_and_lock_translations(sd, ss, positions_eval, verts, translations); - apply_translations_to_pbvh(depsgraph, object, verts, translations); + MutableSpan positions_eval_mut = bke::pbvh::vert_positions_eval_for_write(depsgraph, + object); + if (positions_eval_mut.data() != positions_orig.data()) { + apply_translations(translations, verts, positions_eval_mut); + } if (!ss.deform_imats.is_empty()) { apply_crazyspace_to_translations(ss.deform_imats, verts, translations);