diff --git a/source/blender/blenkernel/BKE_paint.hh b/source/blender/blenkernel/BKE_paint.hh index 6cdc37d0c0e..59eb9111732 100644 --- a/source/blender/blenkernel/BKE_paint.hh +++ b/source/blender/blenkernel/BKE_paint.hh @@ -446,6 +446,12 @@ struct SculptSession : blender::NonCopyable, blender::NonMovable { /* Boundary Brush Preview */ std::unique_ptr boundary_preview; + /* "Persistent" positions and normals for multires. (For mesh the + * ".sculpt_persistent_co" attribute is used, etc.). */ + blender::Array sculpt_persistent_co; + blender::Array sculpt_persistent_no; + blender::Array sculpt_persistent_disp; + SculptVertexInfo vertex_info = {}; SculptFakeNeighbors fake_neighbors = {}; diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 681a1d212f7..beed1cf1e4e 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2096,6 +2096,10 @@ void BKE_sculptsession_free_pbvh(Object &object) ss->fake_neighbors.fake_neighbor_index = {}; ss->topology_island_cache.reset(); + ss->sculpt_persistent_co = {}; + ss->sculpt_persistent_no = {}; + ss->sculpt_persistent_disp = {}; + ss->clear_active_vert(false); } diff --git a/source/blender/editors/sculpt_paint/brushes/layer.cc b/source/blender/editors/sculpt_paint/brushes/layer.cc index e0fc328f718..84315694fc2 100644 --- a/source/blender/editors/sculpt_paint/brushes/layer.cc +++ b/source/blender/editors/sculpt_paint/brushes/layer.cc @@ -32,6 +32,8 @@ namespace blender::ed::sculpt_paint { inline namespace layer_cc { struct LocalData { + Vector persistent_positions; + Vector persistent_normals; Vector positions; Vector factors; Vector distances; @@ -218,6 +220,9 @@ static void calc_grids(const Depsgraph &depsgraph, const Sculpt &sd, const Brush &brush, Object &object, + const bool use_persistent_base, + const Span persistent_base_positions, + const Span persistent_base_normals, bke::pbvh::GridsNode &node, LocalData &tls, MutableSpan layer_displacement_factor) @@ -250,33 +255,64 @@ static void calc_grids(const Depsgraph &depsgraph, calc_brush_texture_factors(ss, brush, positions, factors); + if (subdiv_ccg.masks.is_empty()) { + tls.masks.clear(); + } + else { + tls.masks.resize(positions.size()); + gather_data_grids(subdiv_ccg, subdiv_ccg.masks.as_span(), grids, tls.masks.as_mutable_span()); + } + const MutableSpan masks = tls.masks; + const MutableSpan displacement_factors = gather_data_grids( subdiv_ccg, layer_displacement_factor.as_span(), grids, tls.displacement_factors); - offset_displacement_factors(displacement_factors, tls.factors, cache.bstrength); - if (!subdiv_ccg.masks.is_empty()) { - tls.masks.resize(positions.size()); - mask::gather_mask_grids(subdiv_ccg, grids, tls.masks); + if (use_persistent_base) { + if (cache.invert) { + reset_displacement_factors(displacement_factors, tls.factors, cache.bstrength); + } + else { + offset_displacement_factors(displacement_factors, tls.factors, cache.bstrength); + } + clamp_displacement_factors(displacement_factors, masks); + + scatter_data_grids( + subdiv_ccg, displacement_factors.as_span(), grids, layer_displacement_factor); + + tls.translations.resize(positions.size()); + const MutableSpan translations = tls.translations; + calc_translations( + gather_data_grids(subdiv_ccg, persistent_base_positions, grids, tls.persistent_positions), + gather_data_grids(subdiv_ccg, persistent_base_normals, grids, tls.persistent_normals), + positions, + displacement_factors, + tls.factors, + brush.height, + translations); + + clip_and_lock_translations(sd, ss, positions, translations); + apply_translations(translations, grids, subdiv_ccg); } else { - tls.masks.clear(); + offset_displacement_factors(displacement_factors, tls.factors, cache.bstrength); + clamp_displacement_factors(displacement_factors, masks); + + scatter_data_grids( + subdiv_ccg, displacement_factors.as_span(), grids, layer_displacement_factor); + + tls.translations.resize(positions.size()); + const MutableSpan translations = tls.translations; + calc_translations(orig_data.positions, + orig_data.normals, + positions, + displacement_factors, + tls.factors, + brush.height, + translations); + + clip_and_lock_translations(sd, ss, positions, translations); + apply_translations(translations, grids, subdiv_ccg); } - clamp_displacement_factors(displacement_factors, tls.masks); - - scatter_data_grids(subdiv_ccg, displacement_factors.as_span(), grids, layer_displacement_factor); - - tls.translations.resize(positions.size()); - const MutableSpan translations = tls.translations; - calc_translations(orig_data.positions, - orig_data.normals, - positions, - displacement_factors, - tls.factors, - brush.height, - translations); - - clip_and_lock_translations(sd, ss, positions, translations); - apply_translations(translations, grids, subdiv_ccg); } static void calc_bmesh(const Depsgraph &depsgraph, @@ -414,14 +450,42 @@ void do_layer_brush(const Depsgraph &depsgraph, case bke::pbvh::Type::Grids: { SubdivCCG &subdiv_ccg = *object.sculpt->subdiv_ccg; MutableSpan positions = subdiv_ccg.positions; - if (ss.cache->layer_displacement_factor.is_empty()) { - ss.cache->layer_displacement_factor = Array(positions.size(), 0.0f); + + const Span persistent_position = ss.sculpt_persistent_co; + const Span persistent_normal = ss.sculpt_persistent_no; + + bool use_persistent_base = false; + MutableSpan displacement; + if (brush.flag & BRUSH_PERSISTENT) { + if (!persistent_position.is_empty() && !persistent_normal.is_empty()) { + if (ss.sculpt_persistent_disp.is_empty()) { + ss.sculpt_persistent_disp = Array(positions.size(), 0.0f); + } + use_persistent_base = true; + displacement = ss.sculpt_persistent_disp; + } } - const MutableSpan displacement = ss.cache->layer_displacement_factor; + + if (displacement.is_empty()) { + if (ss.cache->layer_displacement_factor.is_empty()) { + ss.cache->layer_displacement_factor = Array(positions.size(), 0.0f); + } + displacement = ss.cache->layer_displacement_factor; + } + MutableSpan nodes = pbvh.nodes(); node_mask.foreach_index(GrainSize(1), [&](const int i) { LocalData &tls = all_tls.local(); - calc_grids(depsgraph, sd, brush, object, nodes[i], tls, displacement); + calc_grids(depsgraph, + sd, + brush, + object, + use_persistent_base, + persistent_position, + persistent_normal, + nodes[i], + tls, + displacement); bke::pbvh::update_node_bounds_grids(subdiv_ccg.grid_area, positions, nodes[i]); }); break; diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.cc b/source/blender/editors/sculpt_paint/sculpt_cloth.cc index a0f06da95d7..42164989f5d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.cc @@ -571,6 +571,18 @@ void ensure_nodes_constraints(const Sculpt &sd, const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg; const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg); const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden; + + Span init_positions; + Span persistent_position; + if (brush != nullptr && brush->flag & BRUSH_PERSISTENT) { + persistent_position = ss.sculpt_persistent_co; + } + if (persistent_position.is_empty()) { + init_positions = cloth_sim.init_pos; + } + else { + init_positions = persistent_position; + } uninitialized_nodes.foreach_index([&](const int i) { const Span verts = calc_visible_vert_indices_grids( key, grid_hidden, nodes[i].grids(), vert_indices); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 753c41c68b0..f2eb2205f67 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -97,36 +97,47 @@ static int set_persistent_base_exec(bContext *C, wmOperator * /*op*/) return OPERATOR_CANCELLED; } - /* Only mesh geometry supports attributes properly. */ - if (bke::object::pbvh_get(ob)->type() != bke::pbvh::Type::Mesh) { - return OPERATOR_CANCELLED; - } - BKE_sculpt_update_object_for_edit(depsgraph, &ob, false); - Mesh &mesh = *static_cast(ob.data); - bke::MutableAttributeAccessor attributes = mesh.attributes_for_write(); - attributes.remove(".sculpt_persistent_co"); - attributes.remove(".sculpt_persistent_no"); - attributes.remove(".sculpt_persistent_disp"); + switch (bke::object::pbvh_get(ob)->type()) { + case bke::pbvh::Type::Mesh: { + Mesh &mesh = *static_cast(ob.data); + bke::MutableAttributeAccessor attributes = mesh.attributes_for_write(); + attributes.remove(".sculpt_persistent_co"); + attributes.remove(".sculpt_persistent_no"); + attributes.remove(".sculpt_persistent_disp"); - const bke::AttributeReader positions = attributes.lookup("position"); - if (positions.sharing_info && positions.varray.is_span()) { - attributes.add(".sculpt_persistent_co", - bke::AttrDomain::Point, - bke::AttributeInitShared(positions.varray.get_internal_span().data(), - *positions.sharing_info)); - } - else { - attributes.add(".sculpt_persistent_co", - bke::AttrDomain::Point, - bke::AttributeInitVArray(positions.varray)); - } + const bke::AttributeReader positions = attributes.lookup("position"); + if (positions.sharing_info && positions.varray.is_span()) { + attributes.add( + ".sculpt_persistent_co", + bke::AttrDomain::Point, + bke::AttributeInitShared(positions.varray.get_internal_span().data(), + *positions.sharing_info)); + } + else { + attributes.add(".sculpt_persistent_co", + bke::AttrDomain::Point, + bke::AttributeInitVArray(positions.varray)); + } - const Span vert_normals = bke::pbvh::vert_normals_eval(*depsgraph, ob); - attributes.add(".sculpt_persistent_no", - bke::AttrDomain::Point, - bke::AttributeInitVArray(VArray::ForSpan(vert_normals))); + const Span vert_normals = bke::pbvh::vert_normals_eval(*depsgraph, ob); + attributes.add(".sculpt_persistent_no", + bke::AttrDomain::Point, + bke::AttributeInitVArray(VArray::ForSpan(vert_normals))); + break; + } + case bke::pbvh::Type::Grids: { + const SubdivCCG &subdiv_ccg = *ss->subdiv_ccg; + ss->sculpt_persistent_co = subdiv_ccg.positions; + ss->sculpt_persistent_no = subdiv_ccg.normals; + ss->sculpt_persistent_disp = {}; + break; + } + case bke::pbvh::Type::BMesh: { + return OPERATOR_CANCELLED; + } + } return OPERATOR_FINISHED; }