Refactor: Sculpt: Make sculpt undo node definition private

This allows more flexibility about how we store undo data. Potentially
we wouldn't need to store everything segmented per-node if we didn't
expose the `undo::Node` struct.
Part of #118145.

Pull Request: https://projects.blender.org/blender/blender/pulls/127116
This commit is contained in:
Hans Goudey
2024-09-04 17:16:41 +02:00
committed by Hans Goudey
parent e5988cf912
commit 1fe7feb4ec
7 changed files with 260 additions and 110 deletions

View File

@@ -929,6 +929,17 @@ const float *BM_log_find_original_vert_co(BMLog *log, BMVert *v)
return lv == nullptr ? nullptr : lv->co;
}
const float *BM_log_find_original_vert_mask(BMLog *log, BMVert *v)
{
BMLogEntry *entry = log->current_entry;
const BMLogVert *lv;
uint v_id = bm_log_vert_id_get(log, v);
void *key = POINTER_FROM_UINT(v_id);
lv = static_cast<const BMLogVert *>(BLI_ghash_lookup(entry->modified_verts, key));
return lv == nullptr ? nullptr : &lv->mask;
}
const float *BM_log_original_vert_co(BMLog *log, BMVert *v)
{
BMLogEntry *entry = log->current_entry;

View File

@@ -189,6 +189,15 @@ void BM_log_before_all_removed(BMesh *bm, BMLog *log);
*/
const float *BM_log_find_original_vert_co(BMLog *log, BMVert *v);
/**
* Search the log for the original vertex mask.
*
* Does not modify the log or the vertex.
*
* \return the pointer or nullptr if the vertex isn't found.
*/
const float *BM_log_find_original_vert_mask(BMLog *log, BMVert *v);
/**
* Get the logged coordinates of a vertex.
*

View File

@@ -1180,9 +1180,11 @@ static void restore_mask_from_undo_step(Object &object)
bke::SpanAttributeWriter<float> mask = attributes.lookup_or_add_for_write_span<float>(
".sculpt_mask", bke::AttrDomain::Point);
node_mask.foreach_index(GrainSize(1), [&](const int i) {
if (const undo::Node *unode = undo::get_node(&nodes[i], undo::Type::Mask)) {
if (const std::optional<Span<float>> orig_data = orig_mask_data_lookup_mesh(object,
nodes[i]))
{
const Span<int> verts = bke::pbvh::node_unique_verts(nodes[i]);
array_utils::scatter(unode->mask.as_span(), verts, mask.span);
array_utils::scatter(*orig_data, verts, mask.span);
BKE_pbvh_node_mark_update_mask(nodes[i]);
}
});
@@ -1193,13 +1195,12 @@ static void restore_mask_from_undo_step(Object &object)
MutableSpan<bke::pbvh::BMeshNode> nodes = ss.pbvh->nodes<bke::pbvh::BMeshNode>();
const int offset = CustomData_get_offset_named(&ss.bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
if (offset != -1) {
node_mask.foreach_index([&](const int i) {
if (undo::get_node(&nodes[i], undo::Type::Mask)) {
for (BMVert *vert : BKE_pbvh_bmesh_node_unique_verts(&nodes[i])) {
const float orig_mask = BM_log_original_mask(ss.bm_log, vert);
BM_ELEM_CD_SET_FLOAT(vert, offset, orig_mask);
node_mask.foreach_index(GrainSize(1), [&](const int i) {
for (BMVert *vert : BKE_pbvh_bmesh_node_unique_verts(&nodes[i])) {
if (const float *orig_mask = BM_log_find_original_vert_mask(ss.bm_log, vert)) {
BM_ELEM_CD_SET_FLOAT(vert, offset, *orig_mask);
BKE_pbvh_node_mark_update_mask(nodes[i]);
}
BKE_pbvh_node_mark_update_mask(nodes[i]);
}
});
}
@@ -1212,13 +1213,15 @@ static void restore_mask_from_undo_step(Object &object)
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
const Span<CCGElem *> grids = subdiv_ccg.grids;
node_mask.foreach_index(GrainSize(1), [&](const int i) {
if (const undo::Node *unode = undo::get_node(&nodes[i], undo::Type::Mask)) {
if (const std::optional<Span<float>> orig_data = orig_mask_data_lookup_grids(object,
nodes[i]))
{
int index = 0;
for (const int grid : unode->grids) {
for (const int grid : bke::pbvh::node_grid_indices(nodes[i])) {
CCGElem *elem = grids[grid];
for (const int i : IndexRange(key.grid_area)) {
if (grid_hidden.is_empty() || !grid_hidden[grid][i]) {
CCG_elem_offset_mask(key, elem, i) = unode->mask[index];
CCG_elem_offset_mask(key, elem, i) = (*orig_data)[index];
}
index++;
}
@@ -1245,7 +1248,9 @@ static void restore_color_from_undo_step(Object &object)
const GroupedSpan<int> vert_to_face_map = ss.vert_to_face_map;
bke::GSpanAttributeWriter color_attribute = color::active_color_attribute_for_write(mesh);
node_mask.foreach_index(GrainSize(1), [&](const int i) {
if (const undo::Node *unode = undo::get_node(&nodes[i], undo::Type::Color)) {
if (const std::optional<Span<float4>> orig_data = orig_color_data_lookup_mesh(object,
nodes[i]))
{
const Span<int> verts = bke::pbvh::node_unique_verts(nodes[i]);
for (const int i : verts.index_range()) {
color::color_vert_set(faces,
@@ -1253,7 +1258,7 @@ static void restore_color_from_undo_step(Object &object)
vert_to_face_map,
color_attribute.domain,
verts[i],
unode->col[i],
(*orig_data)[i],
color_attribute.span);
}
}
@@ -1269,13 +1274,18 @@ static void restore_face_set_from_undo_step(Object &object)
switch (ss.pbvh->type()) {
case bke::pbvh::Type::Mesh: {
const Mesh &mesh = *static_cast<const Mesh *>(object.data);
const Span<int> tri_faces = mesh.corner_tri_faces();
MutableSpan<bke::pbvh::MeshNode> nodes = ss.pbvh->nodes<bke::pbvh::MeshNode>();
bke::SpanAttributeWriter<int> attribute = face_set::ensure_face_sets_mesh(object);
threading::EnumerableThreadSpecific<Vector<int>> all_tls;
node_mask.foreach_index(GrainSize(1), [&](const int i) {
if (const undo::Node *unode = undo::get_node(&nodes[i], undo::Type::FaceSet)) {
const Span<int> faces = unode->face_indices;
const Span<int> face_sets = unode->face_sets;
blender::array_utils::scatter(face_sets, faces, attribute.span);
Vector<int> &tls = all_tls.local();
if (const std::optional<Span<int>> orig_data = orig_face_set_data_lookup_mesh(object,
nodes[i]))
{
const Span<int> faces = bke::pbvh::node_face_indices_calc_mesh(tri_faces, nodes[i], tls);
scatter_data_mesh(*orig_data, faces, attribute.span);
BKE_pbvh_node_mark_update_face_sets(nodes[i]);
}
});
@@ -1283,13 +1293,18 @@ static void restore_face_set_from_undo_step(Object &object)
break;
}
case bke::pbvh::Type::Grids: {
const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
MutableSpan<bke::pbvh::GridsNode> nodes = ss.pbvh->nodes<bke::pbvh::GridsNode>();
bke::SpanAttributeWriter<int> attribute = face_set::ensure_face_sets_mesh(object);
threading::EnumerableThreadSpecific<Vector<int>> all_tls;
node_mask.foreach_index(GrainSize(1), [&](const int i) {
if (const undo::Node *unode = undo::get_node(&nodes[i], undo::Type::FaceSet)) {
const Span<int> faces = unode->face_indices;
const Span<int> face_sets = unode->face_sets;
blender::array_utils::scatter(face_sets, faces, attribute.span);
Vector<int> &tls = all_tls.local();
if (const std::optional<Span<int>> orig_data = orig_face_set_data_lookup_grids(object,
nodes[i]))
{
const Span<int> faces = bke::pbvh::node_face_indices_calc_grids(
subdiv_ccg, nodes[i], tls);
scatter_data_mesh(*orig_data, faces, attribute.span);
BKE_pbvh_node_mark_update_face_sets(nodes[i]);
}
});
@@ -1325,9 +1340,11 @@ void restore_position_from_undo_step(const Depsgraph &depsgraph, Object &object)
threading::parallel_for(node_mask.index_range(), 1, [&](const IndexRange range) {
LocalData &tls = all_tls.local();
node_mask.slice(range).foreach_index([&](const int i) {
if (const undo::Node *unode = undo::get_node(&nodes[i], undo::Type::Position)) {
if (const std::optional<OrigPositionData> orig_data = orig_position_data_lookup_mesh(
object, nodes[i]))
{
const Span<int> verts = bke::pbvh::node_unique_verts(nodes[i]);
const Span<float3> undo_positions = unode->position.as_span().take_front(verts.size());
const Span<float3> undo_positions = orig_data->positions;
if (need_translations) {
tls.translations.resize(verts.size());
translations_from_new_positions(
@@ -1388,13 +1405,15 @@ void restore_position_from_undo_step(const Depsgraph &depsgraph, Object &object)
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
const Span<CCGElem *> grids = subdiv_ccg.grids;
node_mask.foreach_index(GrainSize(1), [&](const int i) {
if (const undo::Node *unode = undo::get_node(&nodes[i], undo::Type::Position)) {
if (const std::optional<OrigPositionData> orig_data = orig_position_data_lookup_grids(
object, nodes[i]))
{
int index = 0;
for (const int grid : unode->grids) {
for (const int grid : bke::pbvh::node_grid_indices(nodes[i])) {
CCGElem *elem = grids[grid];
for (const int i : IndexRange(key.grid_area)) {
if (grid_hidden.is_empty() || !grid_hidden[grid][i]) {
CCG_elem_offset_co(key, elem, i) = unode->position[index];
CCG_elem_offset_co(key, elem, i) = orig_data->positions[index];
}
index++;
}
@@ -1693,9 +1712,11 @@ static void calc_area_normal_and_center_node_mesh(const Object &object,
const Span<int> verts = bke::pbvh::node_unique_verts(node);
if (ss.cache && !ss.cache->accum) {
if (const undo::Node *unode = undo::get_node(&node, undo::Type::Position)) {
const Span<float3> orig_positions = unode->position.as_span().take_front(verts.size());
const Span<float3> orig_normals = unode->normal.as_span().take_front(verts.size());
if (const std::optional<OrigPositionData> orig_data = orig_position_data_get_mesh(object,
node))
{
const Span<float3> orig_positions = orig_data->positions;
const Span<float3> orig_normals = orig_data->normals;
tls.distances.reinitialize(verts.size());
const MutableSpan<float> distances_sq = tls.distances;
@@ -1780,9 +1801,11 @@ static void calc_area_normal_and_center_node_grids(const Object &object,
const Span<int> grids = bke::pbvh::node_grid_indices(node);
if (ss.cache && !ss.cache->accum) {
if (const undo::Node *unode = undo::get_node(&node, undo::Type::Position)) {
const Span<float3> orig_positions = unode->position.as_span();
const Span<float3> orig_normals = unode->normal.as_span();
if (const std::optional<OrigPositionData> orig_data = orig_position_data_get_grids(object,
node))
{
const Span<float3> orig_positions = orig_data->positions;
const Span<float3> orig_normals = orig_data->normals;
tls.distances.reinitialize(orig_positions.size());
const MutableSpan<float> distances_sq = tls.distances;
@@ -1877,13 +1900,9 @@ static void calc_area_normal_and_center_node_bmesh(const Object &object,
const float normal_radius_sq = normal_radius * normal_radius;
const float normal_radius_inv = math::rcp(normal_radius);
const undo::Node *unode = nullptr;
bool use_original = false;
if (ss.cache && !ss.cache->accum) {
unode = undo::get_node(&node, undo::Type::Position);
if (unode) {
use_original = undo::get_bmesh_log_entry() != nullptr;
}
use_original = undo::get_bmesh_log_entry() != nullptr;
}
/* When the mesh is edited we can't rely on original coords
@@ -2904,6 +2923,7 @@ static void sculpt_pbvh_update_pixels(const Depsgraph &depsgraph,
* \{ */
struct SculptRaycastData {
const Object *object;
SculptSession *ss;
const float *ray_start;
const float *ray_normal;
@@ -2927,6 +2947,7 @@ struct SculptRaycastData {
};
struct SculptFindNearestToRayData {
const Object *object;
SculptSession *ss;
const float *ray_start, *ray_normal;
bool hit;
@@ -4761,18 +4782,30 @@ static void sculpt_raycast_cb(blender::bke::pbvh::Node &node, SculptRaycastData
if (BKE_pbvh_node_get_tmin(&node) >= *tmin) {
return;
}
bool use_origco = false;
Span<float3> origco;
if (srd.original && srd.ss->cache) {
if (srd.ss->pbvh->type() == bke::pbvh::Type::BMesh) {
use_origco = true;
}
else {
/* Intersect with coordinates from before we started stroke. */
if (const undo::Node *unode = undo::get_node(&node, undo::Type::Position)) {
switch (srd.ss->pbvh->type()) {
case bke::pbvh::Type::Mesh:
if (const std::optional<OrigPositionData> orig_data = orig_position_data_lookup_mesh(
*srd.object, static_cast<const bke::pbvh::MeshNode &>(node)))
{
use_origco = true;
origco = orig_data->positions;
}
break;
case bke::pbvh::Type::Grids:
if (const std::optional<OrigPositionData> orig_data = orig_position_data_lookup_grids(
*srd.object, static_cast<const bke::pbvh::GridsNode &>(node)))
{
use_origco = true;
origco = orig_data->positions;
}
break;
case bke::pbvh::Type::BMesh:
use_origco = true;
origco = unode->position.as_span();
}
break;
}
}
@@ -4808,18 +4841,31 @@ static void sculpt_find_nearest_to_ray_cb(blender::bke::pbvh::Node &node,
if (BKE_pbvh_node_get_tmin(&node) >= *tmin) {
return;
}
bool use_origco = false;
Span<float3> origco;
if (srd.original && srd.ss->cache) {
if (srd.ss->pbvh->type() == bke::pbvh::Type::BMesh) {
use_origco = true;
}
else {
/* Intersect with coordinates from before we started stroke. */
if (const undo::Node *unode = undo::get_node(&node, undo::Type::Position)) {
switch (srd.ss->pbvh->type()) {
case bke::pbvh::Type::Mesh:
if (const std::optional<OrigPositionData> orig_data = orig_position_data_lookup_mesh(
*srd.object, static_cast<const bke::pbvh::MeshNode &>(node)))
{
use_origco = true;
origco = orig_data->positions;
}
break;
case bke::pbvh::Type::Grids:
if (const std::optional<OrigPositionData> orig_data = orig_position_data_lookup_grids(
*srd.object, static_cast<const bke::pbvh::GridsNode &>(node)))
{
use_origco = true;
origco = orig_data->positions;
}
break;
case bke::pbvh::Type::BMesh:
use_origco = true;
origco = unode->position.as_span();
}
break;
}
}
@@ -4922,6 +4968,7 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
SculptRaycastData srd{};
srd.original = original;
srd.object = &ob;
srd.ss = ob.sculpt;
srd.hit = false;
if (ss.pbvh->type() == bke::pbvh::Type::Mesh) {
@@ -5097,6 +5144,7 @@ bool SCULPT_stroke_get_location_ex(bContext *C,
bool hit = false;
{
SculptRaycastData srd;
srd.object = &ob;
srd.ss = ob.sculpt;
srd.ray_start = ray_start;
srd.ray_normal = ray_normal;
@@ -5139,6 +5187,7 @@ bool SCULPT_stroke_get_location_ex(bContext *C,
SculptFindNearestToRayData srd{};
srd.original = original;
srd.object = &ob;
srd.ss = ob.sculpt;
srd.hit = false;
if (ss.pbvh->type() == bke::pbvh::Type::Mesh) {
@@ -6208,6 +6257,7 @@ bool SCULPT_vertex_is_occluded(const Object &object, const float3 &position, boo
SculptRaycastData srd = {nullptr};
srd.original = original;
srd.object = &object;
srd.ss = &ss;
srd.hit = false;
srd.ray_start = ray_start;

View File

@@ -625,8 +625,8 @@ void calc_vert_factors(const Depsgraph &depsgraph,
{
Span<float3> orig_normals;
if (cache.settings.flags & (BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL)) {
if (const undo::Node *unode = undo::get_node(&node, undo::Type::Position)) {
orig_normals = unode->normal.as_span();
if (std::optional<OrigPositionData> orig_data = orig_position_data_lookup_mesh(object, node)) {
orig_normals = orig_data->normals;
}
}
@@ -672,8 +672,9 @@ void calc_grids_factors(const Depsgraph &depsgraph,
Span<float3> orig_normals;
if (cache.settings.flags & (BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL)) {
if (const undo::Node *unode = undo::get_node(&node, undo::Type::Position)) {
orig_normals = unode->normal.as_span();
if (std::optional<OrigPositionData> orig_data = orig_position_data_lookup_grids(object, node))
{
orig_normals = orig_data->normals;
}
}

View File

@@ -718,16 +718,45 @@ struct OrigPositionData {
* Retrieve positions from the latest undo state. This is often used for modal actions that depend
* on the initial state of the geometry from before the start of the action.
*/
OrigPositionData orig_position_data_get_mesh(const Object &object,
const bke::pbvh::MeshNode &node);
OrigPositionData orig_position_data_get_grids(const Object &object,
const bke::pbvh::GridsNode &node);
std::optional<OrigPositionData> orig_position_data_lookup_mesh(const Object &object,
const bke::pbvh::MeshNode &node);
inline OrigPositionData orig_position_data_get_mesh(const Object &object,
const bke::pbvh::MeshNode &node)
{
return *orig_position_data_lookup_mesh(object, node);
}
std::optional<OrigPositionData> orig_position_data_lookup_grids(const Object &object,
const bke::pbvh::GridsNode &node);
inline OrigPositionData orig_position_data_get_grids(const Object &object,
const bke::pbvh::GridsNode &node)
{
return *orig_position_data_lookup_grids(object, node);
}
void orig_position_data_gather_bmesh(const BMLog &bm_log,
const Set<BMVert *, 0> &verts,
MutableSpan<float3> positions,
MutableSpan<float3> normals);
Span<float4> orig_color_data_get_mesh(const Object &object, const bke::pbvh::MeshNode &node);
std::optional<Span<float4>> orig_color_data_lookup_mesh(const Object &object,
const bke::pbvh::MeshNode &node);
inline Span<float4> orig_color_data_get_mesh(const Object &object, const bke::pbvh::MeshNode &node)
{
return *orig_color_data_lookup_mesh(object, node);
}
std::optional<Span<int>> orig_face_set_data_lookup_mesh(const Object &object,
const bke::pbvh::MeshNode &node);
std::optional<Span<int>> orig_face_set_data_lookup_grids(const Object &object,
const bke::pbvh::GridsNode &node);
std::optional<Span<float>> orig_mask_data_lookup_mesh(const Object &object,
const bke::pbvh::MeshNode &node);
std::optional<Span<float>> orig_mask_data_lookup_grids(const Object &object,
const bke::pbvh::GridsNode &node);
}

View File

@@ -129,6 +129,37 @@ namespace blender::ed::sculpt_paint::undo {
#define NO_ACTIVE_LAYER bke::AttrDomain::Auto
struct Node {
Array<float3, 0> position;
Array<float3, 0> orig_position;
Array<float3, 0> normal;
Array<float4, 0> col;
Array<float, 0> mask;
Array<float4, 0> loop_col;
/* Mesh. */
Array<int, 0> vert_indices;
int unique_verts_num;
Array<int, 0> corner_indices;
BitVector<0> vert_hidden;
BitVector<0> face_hidden;
/* Multires. */
/** Indices of grids in the pbvh::Tree node. */
Array<int, 0> grids;
BitGroupVector<0> grid_hidden;
/* Sculpt Face Sets */
Array<int, 0> face_sets;
Vector<int> face_indices;
};
struct SculptAttrRef {
bke::AttrDomain domain;
eCustomDataType type;
@@ -1129,7 +1160,13 @@ static void free_step_data(StepData &step_data)
step_data.~StepData();
}
const Node *get_node(const bke::pbvh::Node *node, const Type type)
/**
* Retrieve the undo data of a given type for the active undo step. For example, this is used to
* access "original" data from before the current stroke.
*
* This is only possible when building an undo step, in between #push_begin and #push_end.
*/
static const Node *get_node(const bke::pbvh::Node *node, const Type type)
{
StepData *step_data = get_step_data();
if (!step_data) {
@@ -2124,19 +2161,25 @@ void push_multires_mesh_end(bContext *C, const char *str)
namespace blender::ed::sculpt_paint {
OrigPositionData orig_position_data_get_mesh(const Object & /*object*/,
const bke::pbvh::MeshNode &node)
std::optional<OrigPositionData> orig_position_data_lookup_mesh(const Object & /*object*/,
const bke::pbvh::MeshNode &node)
{
const undo::Node *unode = undo::get_node(&node, undo::Type::Position);
return {unode->position.as_span().take_front(unode->unique_verts_num),
unode->normal.as_span().take_front(unode->unique_verts_num)};
if (!unode) {
return std::nullopt;
}
return OrigPositionData{unode->position.as_span().take_front(unode->unique_verts_num),
unode->normal.as_span().take_front(unode->unique_verts_num)};
}
OrigPositionData orig_position_data_get_grids(const Object & /*object*/,
const bke::pbvh::GridsNode &node)
std::optional<OrigPositionData> orig_position_data_lookup_grids(const Object & /*object*/,
const bke::pbvh::GridsNode &node)
{
const undo::Node *unode = undo::get_node(&node, undo::Type::Position);
return {unode->position.as_span(), unode->normal.as_span()};
if (!unode) {
return std::nullopt;
}
return OrigPositionData{unode->position.as_span(), unode->normal.as_span()};
}
void orig_position_data_gather_bmesh(const BMLog &bm_log,
@@ -2159,10 +2202,54 @@ void orig_position_data_gather_bmesh(const BMLog &bm_log,
}
}
Span<float4> orig_color_data_get_mesh(const Object & /*object*/, const bke::pbvh::MeshNode &node)
std::optional<Span<float4>> orig_color_data_lookup_mesh(const Object & /*object*/,
const bke::pbvh::MeshNode &node)
{
const undo::Node *unode = undo::get_node(&node, undo::Type::Color);
if (!unode) {
return std::nullopt;
}
return unode->col.as_span();
}
std::optional<Span<int>> orig_face_set_data_lookup_mesh(const Object & /*object*/,
const bke::pbvh::MeshNode &node)
{
const undo::Node *unode = undo::get_node(&node, undo::Type::FaceSet);
if (!unode) {
return std::nullopt;
}
return unode->face_sets.as_span();
}
std::optional<Span<int>> orig_face_set_data_lookup_grids(const Object & /*object*/,
const bke::pbvh::GridsNode &node)
{
const undo::Node *unode = undo::get_node(&node, undo::Type::FaceSet);
if (!unode) {
return std::nullopt;
}
return unode->face_sets.as_span();
}
std::optional<Span<float>> orig_mask_data_lookup_mesh(const Object & /*object*/,
const bke::pbvh::MeshNode &node)
{
const undo::Node *unode = undo::get_node(&node, undo::Type::Mask);
if (!unode) {
return std::nullopt;
}
return unode->mask.as_span();
}
std::optional<Span<float>> orig_mask_data_lookup_grids(const Object & /*object*/,
const bke::pbvh::GridsNode &node)
{
const undo::Node *unode = undo::get_node(&node, undo::Type::Mask);
if (!unode) {
return std::nullopt;
}
return unode->mask.as_span();
}
} // namespace blender::ed::sculpt_paint

View File

@@ -45,37 +45,6 @@ enum class Type : int8_t {
Color,
};
struct Node {
Array<float3, 0> position;
Array<float3, 0> orig_position;
Array<float3, 0> normal;
Array<float4, 0> col;
Array<float, 0> mask;
Array<float4, 0> loop_col;
/* Mesh. */
Array<int, 0> vert_indices;
int unique_verts_num;
Array<int, 0> corner_indices;
BitVector<0> vert_hidden;
BitVector<0> face_hidden;
/* Multires. */
/** Indices of grids in the pbvh::Tree node. */
Array<int, 0> grids;
BitGroupVector<0> grid_hidden;
/* Sculpt Face Sets */
Array<int, 0> face_sets;
Vector<int> face_indices;
};
/* Storage of geometry for the undo node.
* Is used as a storage for either original or modified geometry. */
struct NodeGeometry {
@@ -95,6 +64,8 @@ struct NodeGeometry {
int faces_num;
};
struct Node;
struct StepData {
/**
* The type of data stored in this undo step. For historical reasons this is often set when the
@@ -177,14 +148,6 @@ void push_nodes(const Depsgraph &depsgraph,
const IndexMask &node_mask,
undo::Type type);
/**
* Retrieve the undo data of a given type for the active undo step. For example, this is used to
* access "original" data from before the current stroke.
*
* This is only possible when building an undo step, in between #push_begin and #push_end.
*/
const undo::Node *get_node(const bke::pbvh::Node *node, undo::Type type);
/**
* Pushes an undo step using the operator name. This is necessary for
* redo panels to work; operators that do not support that may use