Fix #133267: Sculpt "Persistent Base" doesn't work for mesh and multires
These geometry types don't work properly with attributes currently, so the persistent base isn't really "persistent" and doesn't last after exiting sculpt mode, but it's still useful for it to work within a sculpt session. Enable that by adding temporary array storage in `SculptSession`. During the sculpt refactor project I mistakenly assumed that this didn't work well enough that anyone would use it. Pull Request: https://projects.blender.org/blender/blender/pulls/133410
This commit is contained in:
@@ -446,6 +446,12 @@ struct SculptSession : blender::NonCopyable, blender::NonMovable {
|
||||
/* Boundary Brush Preview */
|
||||
std::unique_ptr<SculptBoundaryPreview> boundary_preview;
|
||||
|
||||
/* "Persistent" positions and normals for multires. (For mesh the
|
||||
* ".sculpt_persistent_co" attribute is used, etc.). */
|
||||
blender::Array<blender::float3> sculpt_persistent_co;
|
||||
blender::Array<blender::float3> sculpt_persistent_no;
|
||||
blender::Array<float> sculpt_persistent_disp;
|
||||
|
||||
SculptVertexInfo vertex_info = {};
|
||||
SculptFakeNeighbors fake_neighbors = {};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ namespace blender::ed::sculpt_paint {
|
||||
inline namespace layer_cc {
|
||||
|
||||
struct LocalData {
|
||||
Vector<float3> persistent_positions;
|
||||
Vector<float3> persistent_normals;
|
||||
Vector<float3> positions;
|
||||
Vector<float> factors;
|
||||
Vector<float> 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<float3> persistent_base_positions,
|
||||
const Span<float3> persistent_base_normals,
|
||||
bke::pbvh::GridsNode &node,
|
||||
LocalData &tls,
|
||||
MutableSpan<float> 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<float> masks = tls.masks;
|
||||
|
||||
const MutableSpan<float> 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<float3> 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<float3> 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<float3> 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<float3> positions = subdiv_ccg.positions;
|
||||
if (ss.cache->layer_displacement_factor.is_empty()) {
|
||||
ss.cache->layer_displacement_factor = Array<float>(positions.size(), 0.0f);
|
||||
|
||||
const Span<float3> persistent_position = ss.sculpt_persistent_co;
|
||||
const Span<float3> persistent_normal = ss.sculpt_persistent_no;
|
||||
|
||||
bool use_persistent_base = false;
|
||||
MutableSpan<float> 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<float>(positions.size(), 0.0f);
|
||||
}
|
||||
use_persistent_base = true;
|
||||
displacement = ss.sculpt_persistent_disp;
|
||||
}
|
||||
}
|
||||
const MutableSpan<float> displacement = ss.cache->layer_displacement_factor;
|
||||
|
||||
if (displacement.is_empty()) {
|
||||
if (ss.cache->layer_displacement_factor.is_empty()) {
|
||||
ss.cache->layer_displacement_factor = Array<float>(positions.size(), 0.0f);
|
||||
}
|
||||
displacement = ss.cache->layer_displacement_factor;
|
||||
}
|
||||
|
||||
MutableSpan<bke::pbvh::GridsNode> nodes = pbvh.nodes<bke::pbvh::GridsNode>();
|
||||
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;
|
||||
|
||||
@@ -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<float3> init_positions;
|
||||
Span<float3> 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<int> verts = calc_visible_vert_indices_grids(
|
||||
key, grid_hidden, nodes[i].grids(), vert_indices);
|
||||
|
||||
@@ -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<Mesh *>(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<Mesh *>(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<float3>("position");
|
||||
if (positions.sharing_info && positions.varray.is_span()) {
|
||||
attributes.add<float3>(".sculpt_persistent_co",
|
||||
bke::AttrDomain::Point,
|
||||
bke::AttributeInitShared(positions.varray.get_internal_span().data(),
|
||||
*positions.sharing_info));
|
||||
}
|
||||
else {
|
||||
attributes.add<float3>(".sculpt_persistent_co",
|
||||
bke::AttrDomain::Point,
|
||||
bke::AttributeInitVArray(positions.varray));
|
||||
}
|
||||
const bke::AttributeReader positions = attributes.lookup<float3>("position");
|
||||
if (positions.sharing_info && positions.varray.is_span()) {
|
||||
attributes.add<float3>(
|
||||
".sculpt_persistent_co",
|
||||
bke::AttrDomain::Point,
|
||||
bke::AttributeInitShared(positions.varray.get_internal_span().data(),
|
||||
*positions.sharing_info));
|
||||
}
|
||||
else {
|
||||
attributes.add<float3>(".sculpt_persistent_co",
|
||||
bke::AttrDomain::Point,
|
||||
bke::AttributeInitVArray(positions.varray));
|
||||
}
|
||||
|
||||
const Span<float3> vert_normals = bke::pbvh::vert_normals_eval(*depsgraph, ob);
|
||||
attributes.add<float3>(".sculpt_persistent_no",
|
||||
bke::AttrDomain::Point,
|
||||
bke::AttributeInitVArray(VArray<float3>::ForSpan(vert_normals)));
|
||||
const Span<float3> vert_normals = bke::pbvh::vert_normals_eval(*depsgraph, ob);
|
||||
attributes.add<float3>(".sculpt_persistent_no",
|
||||
bke::AttrDomain::Point,
|
||||
bke::AttributeInitVArray(VArray<float3>::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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user