Cleanup: Sculpt: Use references for SculptSession variables

This commit is contained in:
Hans Goudey 2024-06-20 10:23:05 -04:00
parent 2079507ca5
commit 385cc4fb3b
7 changed files with 208 additions and 210 deletions

@ -453,8 +453,8 @@ void multires_force_sculpt_rebuild(Object *object)
return;
}
SculptSession *ss = object->sculpt;
bke::pbvh::free(ss->pbvh);
SculptSession &ss = *object->sculpt;
bke::pbvh::free(ss.pbvh);
}
void multires_force_external_reload(Object *object)

@ -1399,16 +1399,16 @@ void BKE_sculptsession_free_vwpaint_data(SculptSession *ss)
*/
static void sculptsession_bm_to_me_update_data_only(Object *ob, bool reorder)
{
SculptSession *ss = ob->sculpt;
SculptSession &ss = *ob->sculpt;
if (ss->bm) {
if (ss.bm) {
if (ob->data) {
if (reorder) {
BM_log_mesh_elems_reorder(ss->bm, ss->bm_log);
BM_log_mesh_elems_reorder(ss.bm, ss.bm_log);
}
BMeshToMeshParams params{};
params.calc_object_remap = false;
BM_mesh_bm_to_me(nullptr, ss->bm, static_cast<Mesh *>(ob->data), &params);
BM_mesh_bm_to_me(nullptr, ss.bm, static_cast<Mesh *>(ob->data), &params);
}
}
}
@ -1624,13 +1624,13 @@ static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob)
*/
static void sculpt_update_persistent_base(Object *ob)
{
SculptSession *ss = ob->sculpt;
SculptSession &ss = *ob->sculpt;
ss->attrs.persistent_co = BKE_sculpt_attribute_get(
ss.attrs.persistent_co = BKE_sculpt_attribute_get(
ob, AttrDomain::Point, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_co));
ss->attrs.persistent_no = BKE_sculpt_attribute_get(
ss.attrs.persistent_no = BKE_sculpt_attribute_get(
ob, AttrDomain::Point, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_no));
ss->attrs.persistent_disp = BKE_sculpt_attribute_get(
ss.attrs.persistent_disp = BKE_sculpt_attribute_get(
ob, AttrDomain::Point, CD_PROP_FLOAT, SCULPT_ATTRIBUTE_NAME(persistent_disp));
}
@ -1641,7 +1641,7 @@ static void sculpt_update_object(Depsgraph *depsgraph,
{
Scene *scene = DEG_get_input_scene(depsgraph);
Sculpt *sd = scene->toolsettings->sculpt;
SculptSession *ss = ob->sculpt;
SculptSession &ss = *ob->sculpt;
Mesh *mesh_orig = BKE_object_get_original_mesh(ob);
Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval);
MultiresModifierData *mmd = sculpt_multires_modifier_get(scene, ob, true);
@ -1655,93 +1655,93 @@ static void sculpt_update_object(Depsgraph *depsgraph,
return;
}
ss->depsgraph = depsgraph;
ss.depsgraph = depsgraph;
ss->deform_modifiers_active = sculpt_modifiers_active(scene, sd, ob);
ss.deform_modifiers_active = sculpt_modifiers_active(scene, sd, ob);
ss->building_vp_handle = false;
ss.building_vp_handle = false;
ss->scene = scene;
ss.scene = scene;
ss->shapekey_active = (mmd == nullptr) ? BKE_keyblock_from_object(ob) : nullptr;
ss.shapekey_active = (mmd == nullptr) ? BKE_keyblock_from_object(ob) : nullptr;
/* NOTE: Weight pPaint require mesh info for loop lookup, but it never uses multires code path,
* so no extra checks is needed here. */
if (mmd) {
ss->multires.active = true;
ss->multires.modifier = mmd;
ss->multires.level = mmd->sculptlvl;
ss->totvert = mesh_eval->verts_num;
ss->faces_num = mesh_eval->faces_num;
ss->totfaces = mesh_orig->faces_num;
ss.multires.active = true;
ss.multires.modifier = mmd;
ss.multires.level = mmd->sculptlvl;
ss.totvert = mesh_eval->verts_num;
ss.faces_num = mesh_eval->faces_num;
ss.totfaces = mesh_orig->faces_num;
/* These are assigned to the base mesh in Multires. This is needed because Face Sets operators
* and tools use the Face Sets data from the base mesh when Multires is active. */
ss->vert_positions = mesh_orig->vert_positions_for_write();
ss->faces = mesh_orig->faces();
ss->corner_verts = mesh_orig->corner_verts();
ss.vert_positions = mesh_orig->vert_positions_for_write();
ss.faces = mesh_orig->faces();
ss.corner_verts = mesh_orig->corner_verts();
}
else {
ss->totvert = mesh_orig->verts_num;
ss->faces_num = mesh_orig->faces_num;
ss->totfaces = mesh_orig->faces_num;
ss->vert_positions = mesh_orig->vert_positions_for_write();
ss->faces = mesh_orig->faces();
ss->corner_verts = mesh_orig->corner_verts();
ss->multires.active = false;
ss->multires.modifier = nullptr;
ss->multires.level = 0;
ss.totvert = mesh_orig->verts_num;
ss.faces_num = mesh_orig->faces_num;
ss.totfaces = mesh_orig->faces_num;
ss.vert_positions = mesh_orig->vert_positions_for_write();
ss.faces = mesh_orig->faces();
ss.corner_verts = mesh_orig->corner_verts();
ss.multires.active = false;
ss.multires.modifier = nullptr;
ss.multires.level = 0;
CustomDataLayer *layer;
AttrDomain domain;
if (BKE_pbvh_get_color_layer(mesh_orig, &layer, &domain)) {
if (layer->type == CD_PROP_COLOR) {
ss->vcol = static_cast<MPropCol *>(layer->data);
ss.vcol = static_cast<MPropCol *>(layer->data);
}
else {
ss->mcol = static_cast<MLoopCol *>(layer->data);
ss.mcol = static_cast<MLoopCol *>(layer->data);
}
ss->vcol_domain = domain;
ss->vcol_type = static_cast<eCustomDataType>(layer->type);
ss.vcol_domain = domain;
ss.vcol_type = static_cast<eCustomDataType>(layer->type);
}
else {
ss->vcol = nullptr;
ss->mcol = nullptr;
ss.vcol = nullptr;
ss.mcol = nullptr;
ss->vcol_type = (eCustomDataType)-1;
ss->vcol_domain = AttrDomain::Point;
ss.vcol_type = (eCustomDataType)-1;
ss.vcol_domain = AttrDomain::Point;
}
}
/* Sculpt Face Sets. */
if (use_face_sets) {
ss->face_sets = static_cast<const int *>(
ss.face_sets = static_cast<const int *>(
CustomData_get_layer_named(&mesh_orig->face_data, CD_PROP_INT32, ".sculpt_face_set"));
}
else {
ss->face_sets = nullptr;
ss.face_sets = nullptr;
}
ss->hide_poly = (bool *)CustomData_get_layer_named(
ss.hide_poly = (bool *)CustomData_get_layer_named(
&mesh_orig->face_data, CD_PROP_BOOL, ".hide_poly");
ss->subdiv_ccg = mesh_eval->runtime->subdiv_ccg.get();
ss.subdiv_ccg = mesh_eval->runtime->subdiv_ccg.get();
PBVH *pbvh = BKE_sculpt_object_pbvh_ensure(depsgraph, ob);
BLI_assert(pbvh == ss->pbvh.get());
BLI_assert(pbvh == ss.pbvh.get());
UNUSED_VARS_NDEBUG(pbvh);
BKE_pbvh_subdiv_cgg_set(*ss->pbvh, ss->subdiv_ccg);
BKE_pbvh_subdiv_cgg_set(*ss.pbvh, ss.subdiv_ccg);
sculpt_attribute_update_refs(ob, BKE_pbvh_type(*ss->pbvh));
sculpt_attribute_update_refs(ob, BKE_pbvh_type(*ss.pbvh));
sculpt_update_persistent_base(ob);
if (ob->type == OB_MESH) {
ss->vert_to_face_map = mesh_orig->vert_to_face_map();
ss.vert_to_face_map = mesh_orig->vert_to_face_map();
}
if (ss->deform_modifiers_active) {
if (ss.deform_modifiers_active) {
/* Painting doesn't need crazyspace, use already evaluated mesh coordinates if possible. */
bool used_me_eval = false;
@ -1755,67 +1755,67 @@ static void sculpt_update_object(Depsgraph *depsgraph,
me_eval_deform->corners_num == mesh_eval->corners_num &&
me_eval_deform->verts_num == mesh_eval->verts_num)
{
BKE_sculptsession_free_deformMats(ss);
BKE_sculptsession_free_deformMats(&ss);
BLI_assert(me_eval_deform->verts_num == mesh_orig->verts_num);
ss->deform_cos = mesh_eval->vert_positions();
BKE_pbvh_vert_coords_apply(*ss->pbvh, ss->deform_cos);
ss.deform_cos = mesh_eval->vert_positions();
BKE_pbvh_vert_coords_apply(*ss.pbvh, ss.deform_cos);
used_me_eval = true;
}
}
if (ss->orig_cos.is_empty() && !used_me_eval) {
BKE_sculptsession_free_deformMats(ss);
if (ss.orig_cos.is_empty() && !used_me_eval) {
BKE_sculptsession_free_deformMats(&ss);
ss->orig_cos = (ss->shapekey_active) ?
Span(static_cast<const float3 *>(ss->shapekey_active->data),
mesh_orig->verts_num) :
mesh_orig->vert_positions();
ss.orig_cos = (ss.shapekey_active) ?
Span(static_cast<const float3 *>(ss.shapekey_active->data),
mesh_orig->verts_num) :
mesh_orig->vert_positions();
BKE_crazyspace_build_sculpt(depsgraph, scene, ob, ss->deform_imats, ss->deform_cos);
BKE_pbvh_vert_coords_apply(*ss->pbvh, ss->deform_cos);
BKE_crazyspace_build_sculpt(depsgraph, scene, ob, ss.deform_imats, ss.deform_cos);
BKE_pbvh_vert_coords_apply(*ss.pbvh, ss.deform_cos);
for (blender::float3x3 &matrix : ss->deform_imats) {
for (blender::float3x3 &matrix : ss.deform_imats) {
matrix = blender::math::invert(matrix);
}
}
}
else {
BKE_sculptsession_free_deformMats(ss);
BKE_sculptsession_free_deformMats(&ss);
}
if (ss->shapekey_active != nullptr && ss->deform_cos.is_empty()) {
ss->deform_cos = Span(static_cast<const float3 *>(ss->shapekey_active->data),
mesh_orig->verts_num);
if (ss.shapekey_active != nullptr && ss.deform_cos.is_empty()) {
ss.deform_cos = Span(static_cast<const float3 *>(ss.shapekey_active->data),
mesh_orig->verts_num);
}
/* 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()) {
const Span key_data(static_cast<const float3 *>(ss->shapekey_active->data),
if (ss.shapekey_active) {
bool pbvh_deformed = BKE_pbvh_is_deformed(*ss.pbvh);
if (!pbvh_deformed || ss.deform_cos.is_empty()) {
const Span key_data(static_cast<const float3 *>(ss.shapekey_active->data),
mesh_orig->verts_num);
if (key_data.data() != nullptr) {
if (!pbvh_deformed) {
/* apply shape keys coordinates to PBVH */
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;
if (ss.deform_cos.is_empty()) {
ss.deform_cos = key_data;
}
}
}
}
if (is_paint_tool) {
if (ss->vcol_domain == AttrDomain::Corner) {
if (ss.vcol_domain == AttrDomain::Corner) {
/* Ensure pbvh nodes have loop indices; the sculpt undo system
* needs them for color attributes.
*/
BKE_pbvh_ensure_node_loops(*ss->pbvh);
BKE_pbvh_ensure_node_loops(*ss.pbvh);
}
/*
@ -1824,14 +1824,14 @@ static void sculpt_update_object(Depsgraph *depsgraph,
* The relevant changes are stored/encoded in the paint canvas key.
* These include the active uv map, and resolutions.
*/
if (U.experimental.use_sculpt_texture_paint && ss->pbvh) {
if (U.experimental.use_sculpt_texture_paint && ss.pbvh) {
char *paint_canvas_key = BKE_paint_canvas_key_get(&scene->toolsettings->paint_mode, ob);
if (ss->last_paint_canvas_key == nullptr ||
!STREQ(paint_canvas_key, ss->last_paint_canvas_key))
if (ss.last_paint_canvas_key == nullptr ||
!STREQ(paint_canvas_key, ss.last_paint_canvas_key))
{
MEM_SAFE_FREE(ss->last_paint_canvas_key);
ss->last_paint_canvas_key = paint_canvas_key;
BKE_pbvh_mark_rebuild_pixels(*ss->pbvh);
MEM_SAFE_FREE(ss.last_paint_canvas_key);
ss.last_paint_canvas_key = paint_canvas_key;
BKE_pbvh_mark_rebuild_pixels(*ss.pbvh);
}
else {
MEM_freeN(paint_canvas_key);
@ -2257,14 +2257,14 @@ int BKE_sculptsession_vertex_count(const SculptSession *ss)
*/
static CustomData *sculpt_get_cdata(Object *ob, AttrDomain domain)
{
SculptSession *ss = ob->sculpt;
SculptSession &ss = *ob->sculpt;
if (ss->bm) {
if (ss.bm) {
switch (domain) {
case AttrDomain::Point:
return &ss->bm->vdata;
return &ss.bm->vdata;
case AttrDomain::Face:
return &ss->bm->pdata;
return &ss.bm->pdata;
default:
BLI_assert_unreachable();
return nullptr;
@ -2276,7 +2276,7 @@ static CustomData *sculpt_get_cdata(Object *ob, AttrDomain domain)
switch (domain) {
case AttrDomain::Point:
/* Cannot get vertex domain for multires grids. */
if (ss->pbvh && BKE_pbvh_type(*ss->pbvh) == PBVH_GRIDS) {
if (ss.pbvh && BKE_pbvh_type(*ss.pbvh) == PBVH_GRIDS) {
return nullptr;
}
@ -2292,14 +2292,14 @@ static CustomData *sculpt_get_cdata(Object *ob, AttrDomain domain)
static int sculpt_attr_elem_count_get(Object *ob, AttrDomain domain)
{
SculptSession *ss = ob->sculpt;
const SculptSession &ss = *ob->sculpt;
switch (domain) {
case AttrDomain::Point:
return BKE_sculptsession_vertex_count(ss);
return BKE_sculptsession_vertex_count(&ss);
break;
case AttrDomain::Face:
return ss->totfaces;
return ss.totfaces;
break;
default:
BLI_assert_unreachable();

@ -73,7 +73,7 @@ void triangulate(BMesh *bm)
void enable_ex(Main &bmain, Depsgraph &depsgraph, Object &ob)
{
SculptSession *ss = ob.sculpt;
SculptSession &ss = *ob.sculpt;
Mesh *mesh = static_cast<Mesh *>(ob.data);
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
@ -85,28 +85,28 @@ void enable_ex(Main &bmain, Depsgraph &depsgraph, Object &ob)
/* Create triangles-only BMesh. */
BMeshCreateParams create_params{};
create_params.use_toolflags = false;
ss->bm = BM_mesh_create(&allocsize, &create_params);
ss.bm = BM_mesh_create(&allocsize, &create_params);
BMeshFromMeshParams convert_params{};
convert_params.calc_face_normal = true;
convert_params.calc_vert_normal = true;
convert_params.use_shapekey = true;
convert_params.active_shapekey = ob.shapenr;
BM_mesh_bm_from_me(ss->bm, mesh, &convert_params);
triangulate(ss->bm);
BM_mesh_bm_from_me(ss.bm, mesh, &convert_params);
triangulate(ss.bm);
BM_data_layer_ensure_named(ss->bm, &ss->bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
BM_data_layer_ensure_named(ss.bm, &ss.bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
/* Make sure the data for existing faces are initialized. */
if (mesh->faces_num != ss->bm->totface) {
BM_mesh_normals_update(ss->bm);
if (mesh->faces_num != ss.bm->totface) {
BM_mesh_normals_update(ss.bm);
}
/* Enable dynamic topology. */
mesh->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY;
/* Enable logging for undo/redo. */
ss->bm_log = BM_log_create(ss->bm);
ss.bm_log = BM_log_create(ss.bm);
/* Update dependency graph, so modifiers that depend on dyntopo being enabled
* are re-evaluated and the PBVH is re-created. */

@ -321,7 +321,7 @@ static void flip_for_symmetry_pass(GestureData &gesture_data, const ePaintSymmet
static Vector<PBVHNode *> update_affected_nodes_by_line_plane(GestureData &gesture_data)
{
SculptSession *ss = gesture_data.ss;
SculptSession &ss = *gesture_data.ss;
float clip_planes[3][4];
copy_v4_v4(clip_planes[0], gesture_data.line.plane);
copy_v4_v4(clip_planes[1], gesture_data.line.side_plane[0]);
@ -331,14 +331,14 @@ static Vector<PBVHNode *> update_affected_nodes_by_line_plane(GestureData &gestu
frustum.planes = clip_planes;
frustum.num_planes = gesture_data.line.use_side_planes ? 3 : 1;
return gesture_data.nodes = bke::pbvh::search_gather(*ss->pbvh, [&](PBVHNode &node) {
return gesture_data.nodes = bke::pbvh::search_gather(*ss.pbvh, [&](PBVHNode &node) {
return BKE_pbvh_node_frustum_contain_AABB(&node, &frustum);
});
}
static void update_affected_nodes_by_clip_planes(GestureData &gesture_data)
{
SculptSession *ss = gesture_data.ss;
SculptSession &ss = *gesture_data.ss;
float clip_planes[4][4];
copy_m4_m4(clip_planes, gesture_data.clip_planes);
negate_m4(clip_planes);
@ -347,7 +347,7 @@ static void update_affected_nodes_by_clip_planes(GestureData &gesture_data)
frustum.planes = clip_planes;
frustum.num_planes = 4;
gesture_data.nodes = bke::pbvh::search_gather(*ss->pbvh, [&](PBVHNode &node) {
gesture_data.nodes = bke::pbvh::search_gather(*ss.pbvh, [&](PBVHNode &node) {
switch (gesture_data.selection_type) {
case SelectionType::Inside:
return BKE_pbvh_node_frustum_contain_AABB(&node, &frustum);

@ -85,9 +85,9 @@ static void gesture_apply_for_symmetry_pass(bContext & /*C*/, gesture::GestureDa
static void gesture_end(bContext &C, gesture::GestureData &gesture_data)
{
SculptSession *ss = gesture_data.ss;
SculptSession &ss = *gesture_data.ss;
const Sculpt &sd = *CTX_data_tool_settings(&C)->sculpt;
if (ss->deform_modifiers_active || ss->shapekey_active) {
if (ss.deform_modifiers_active || ss.shapekey_active) {
SCULPT_flush_stroke_deform(sd, *gesture_data.vc.obact, true);
}

@ -438,10 +438,10 @@ static bool test_swap_v3_v3(float3 &a, float3 &b)
}
static bool restore_deformed(
const SculptSession *ss, Node &unode, int uindex, int oindex, float3 &coord)
const SculptSession &ss, Node &unode, int uindex, int oindex, float3 &coord)
{
if (test_swap_v3_v3(coord, unode.orig_position[uindex])) {
copy_v3_v3(unode.position[uindex], ss->deform_cos[oindex]);
copy_v3_v3(unode.position[uindex], ss.deform_cos[oindex]);
return true;
}
return false;
@ -454,13 +454,13 @@ static bool restore_coords(bContext *C,
Node &unode,
MutableSpan<bool> modified_verts)
{
SculptSession *ss = object.sculpt;
SubdivCCG *subdiv_ccg = ss->subdiv_ccg;
SculptSession &ss = *object.sculpt;
SubdivCCG *subdiv_ccg = ss.subdiv_ccg;
if (unode.mesh_verts_num) {
/* Regular mesh restore. */
if (ss->shapekey_active && ss->shapekey_active->name != step_data.active_shape_key_name) {
if (ss.shapekey_active && ss.shapekey_active->name != step_data.active_shape_key_name) {
/* Shape key has been changed before calling undo operator. */
Key *key = BKE_key_from_object(&object);
@ -481,14 +481,14 @@ static bool restore_coords(bContext *C,
/* No need for float comparison here (memory is exactly equal or not). */
const Span<int> index = unode.vert_indices.as_span().take_front(unode.unique_verts_num);
MutableSpan<float3> positions = ss->vert_positions;
MutableSpan<float3> positions = ss.vert_positions;
if (ss->shapekey_active) {
float(*vertCos)[3] = BKE_keyblock_convert_to_vertcos(&object, ss->shapekey_active);
MutableSpan key_positions(reinterpret_cast<float3 *>(vertCos), ss->shapekey_active->totelem);
if (ss.shapekey_active) {
float(*vertCos)[3] = BKE_keyblock_convert_to_vertcos(&object, ss.shapekey_active);
MutableSpan key_positions(reinterpret_cast<float3 *>(vertCos), ss.shapekey_active->totelem);
if (!unode.orig_position.is_empty()) {
if (ss->deform_modifiers_active) {
if (ss.deform_modifiers_active) {
for (const int i : index.index_range()) {
restore_deformed(ss, unode, i, index[i], key_positions[index[i]]);
}
@ -506,17 +506,17 @@ static bool restore_coords(bContext *C,
}
/* Propagate new coords to keyblock. */
SCULPT_vertcos_to_key(object, ss->shapekey_active, key_positions);
SCULPT_vertcos_to_key(object, ss.shapekey_active, key_positions);
/* PBVH uses its own vertex array, so coords should be */
/* propagated to PBVH here. */
BKE_pbvh_vert_coords_apply(*ss->pbvh, key_positions);
BKE_pbvh_vert_coords_apply(*ss.pbvh, key_positions);
MEM_freeN(vertCos);
}
else {
if (!unode.orig_position.is_empty()) {
if (ss->deform_modifiers_active) {
if (ss.deform_modifiers_active) {
for (const int i : index.index_range()) {
restore_deformed(ss, unode, i, index[i], positions[index[i]]);
modified_verts[index[i]] = true;
@ -559,8 +559,8 @@ static bool restore_coords(bContext *C,
static bool restore_hidden(Object &object, Node &unode, MutableSpan<bool> modified_vertices)
{
SculptSession *ss = object.sculpt;
SubdivCCG *subdiv_ccg = ss->subdiv_ccg;
SculptSession &ss = *object.sculpt;
SubdivCCG *subdiv_ccg = ss.subdiv_ccg;
if (unode.mesh_verts_num) {
Mesh &mesh = *static_cast<Mesh *>(object.data);
@ -628,7 +628,7 @@ static bool restore_hidden_face(Object &object, Node &unode, MutableSpan<bool> m
static bool restore_color(Object &object, Node &unode, MutableSpan<bool> modified_vertices)
{
const Mesh &mesh = *static_cast<const Mesh *>(object.data);
SculptSession *ss = object.sculpt;
SculptSession &ss = *object.sculpt;
bool modified = false;
@ -636,12 +636,12 @@ static bool restore_color(Object &object, Node &unode, MutableSpan<bool> modifie
* vertex colors for original data lookup. */
if (!unode.col.is_empty() && unode.loop_col.is_empty()) {
BKE_pbvh_swap_colors(
*ss->pbvh, unode.vert_indices.as_span().take_front(unode.unique_verts_num), unode.col);
*ss.pbvh, unode.vert_indices.as_span().take_front(unode.unique_verts_num), unode.col);
modified = true;
}
if (!unode.loop_col.is_empty() && unode.mesh_corners_num == mesh.corners_num) {
BKE_pbvh_swap_colors(*ss->pbvh, unode.corner_indices, unode.loop_col);
BKE_pbvh_swap_colors(*ss.pbvh, unode.corner_indices, unode.loop_col);
modified = true;
}
@ -655,8 +655,8 @@ static bool restore_color(Object &object, Node &unode, MutableSpan<bool> modifie
static bool restore_mask(Object &object, Node &unode, MutableSpan<bool> modified_vertices)
{
Mesh *mesh = BKE_object_get_original_mesh(&object);
SculptSession *ss = object.sculpt;
SubdivCCG *subdiv_ccg = ss->subdiv_ccg;
SculptSession &ss = *object.sculpt;
SubdivCCG *subdiv_ccg = ss.subdiv_ccg;
if (unode.mesh_verts_num) {
bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
@ -715,19 +715,19 @@ static bool restore_face_sets(Object &object,
return modified;
}
static void bmesh_restore_generic(Node &unode, Object &object, SculptSession *ss)
static void bmesh_restore_generic(Node &unode, Object &object, SculptSession &ss)
{
if (unode.applied) {
BM_log_undo(ss->bm, ss->bm_log);
BM_log_undo(ss.bm, ss.bm_log);
unode.applied = false;
}
else {
BM_log_redo(ss->bm, ss->bm_log);
BM_log_redo(ss.bm, ss.bm_log);
unode.applied = true;
}
if (unode.type == Type::Mask) {
Vector<PBVHNode *> nodes = bke::pbvh::search_gather(*ss->pbvh, {});
Vector<PBVHNode *> nodes = bke::pbvh::search_gather(*ss.pbvh, {});
for (PBVHNode *node : nodes) {
BKE_pbvh_node_mark_redraw(node);
}
@ -740,7 +740,7 @@ static void bmesh_restore_generic(Node &unode, Object &object, SculptSession *ss
/* Create empty sculpt BMesh and enable logging. */
static void bmesh_enable(Object &object, Node &unode)
{
SculptSession *ss = object.sculpt;
SculptSession &ss = *object.sculpt;
Mesh *mesh = static_cast<Mesh *>(object.data);
SCULPT_pbvh_clear(object);
@ -749,16 +749,16 @@ static void bmesh_enable(Object &object, Node &unode)
BMeshCreateParams bmesh_create_params{};
bmesh_create_params.use_toolflags = false;
ss->bm = BM_mesh_create(&bm_mesh_allocsize_default, &bmesh_create_params);
BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
ss.bm = BM_mesh_create(&bm_mesh_allocsize_default, &bmesh_create_params);
BM_data_layer_add_named(ss.bm, &ss.bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
mesh->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY;
/* Restore the BMLog using saved entries. */
ss->bm_log = BM_log_from_existing_entries_create(ss->bm, unode.bm_entry);
ss.bm_log = BM_log_from_existing_entries_create(ss.bm, unode.bm_entry);
}
static void bmesh_restore_begin(bContext *C, Node &unode, Object &object, SculptSession *ss)
static void bmesh_restore_begin(bContext *C, Node &unode, Object &object, SculptSession &ss)
{
if (unode.applied) {
dyntopo::disable(C, &unode);
@ -768,19 +768,19 @@ static void bmesh_restore_begin(bContext *C, Node &unode, Object &object, Sculpt
bmesh_enable(object, unode);
/* Restore the mesh from the first log entry. */
BM_log_redo(ss->bm, ss->bm_log);
BM_log_redo(ss.bm, ss.bm_log);
unode.applied = true;
}
}
static void bmesh_restore_end(bContext *C, Node &unode, Object &object, SculptSession *ss)
static void bmesh_restore_end(bContext *C, Node &unode, Object &object, SculptSession &ss)
{
if (unode.applied) {
bmesh_enable(object, unode);
/* Restore the mesh from the last log entry. */
BM_log_undo(ss->bm, ss->bm_log);
BM_log_undo(ss.bm, ss.bm_log);
unode.applied = false;
}
@ -869,7 +869,7 @@ static void restore_geometry(Node &unode, Object &object)
*
* Returns true if this was a dynamic-topology undo step, otherwise
* returns false to indicate the non-dyntopo code should run. */
static int bmesh_restore(bContext *C, Node &unode, Object &object, SculptSession *ss)
static int bmesh_restore(bContext *C, Node &unode, Object &object, SculptSession &ss)
{
switch (unode.type) {
case Type::DyntopoBegin:
@ -880,7 +880,7 @@ static int bmesh_restore(bContext *C, Node &unode, Object &object, SculptSession
bmesh_restore_end(C, unode, object, ss);
return true;
default:
if (ss->bm_log) {
if (ss.bm_log) {
bmesh_restore_generic(unode, object, ss);
return true;
}
@ -904,12 +904,12 @@ static int bmesh_restore(bContext *C, Node &unode, Object &object, SculptSession
* Note that the dependency graph is ensured to be evaluated prior to the undo step is decoded,
* so if the object's modifier stack references other object it is all fine. */
static void refine_subdiv(Depsgraph *depsgraph,
SculptSession *ss,
SculptSession &ss,
Object &object,
bke::subdiv::Subdiv *subdiv)
{
Array<float3> deformed_verts = BKE_multires_create_deformed_base_mesh_vert_coords(
depsgraph, &object, ss->multires.modifier);
depsgraph, &object, ss.multires.modifier);
bke::subdiv::eval_refine_from_mesh(subdiv,
static_cast<const Mesh *>(object.data),
@ -927,12 +927,12 @@ static void restore_list(bContext *C, Depsgraph *depsgraph, StepData &step_data)
return;
}
Mesh &mesh = *static_cast<Mesh *>(object.data);
SculptSession *ss = object.sculpt;
SubdivCCG *subdiv_ccg = ss->subdiv_ccg;
SculptSession &ss = *object.sculpt;
SubdivCCG *subdiv_ccg = ss.subdiv_ccg;
/* Restore pivot. */
ss->pivot_pos = step_data.pivot_pos;
ss->pivot_rot = step_data.pivot_rot;
ss.pivot_pos = step_data.pivot_pos;
ss.pivot_rot = step_data.pivot_rot;
bool clear_automask_cache = false;
for (const std::unique_ptr<Node> &unode : step_data.nodes) {
@ -942,7 +942,7 @@ static void restore_list(bContext *C, Depsgraph *depsgraph, StepData &step_data)
}
if (clear_automask_cache) {
ss->last_automasking_settings_hash = 0;
ss.last_automasking_settings_hash = 0;
}
if (!step_data.nodes.is_empty()) {
@ -982,7 +982,7 @@ static void restore_list(bContext *C, Depsgraph *depsgraph, StepData &step_data)
for (std::unique_ptr<Node> &unode : step_data.nodes) {
/* Check if undo data matches current data well enough to continue. */
if (unode->mesh_verts_num) {
if (ss->totvert != unode->mesh_verts_num) {
if (ss.totvert != unode->mesh_verts_num) {
continue;
}
}
@ -998,37 +998,37 @@ static void restore_list(bContext *C, Depsgraph *depsgraph, StepData &step_data)
switch (unode->type) {
case Type::Position:
modified_verts_position.resize(ss->totvert, false);
modified_verts_position.resize(ss.totvert, false);
if (restore_coords(C, object, depsgraph, step_data, *unode, modified_verts_position)) {
changed_position = true;
}
break;
case Type::HideVert:
modified_verts_hide.resize(ss->totvert, false);
modified_verts_hide.resize(ss.totvert, false);
if (restore_hidden(object, *unode, modified_verts_hide)) {
changed_hide_vert = true;
}
break;
case Type::HideFace:
modified_faces_hide.resize(ss->totfaces, false);
modified_faces_hide.resize(ss.totfaces, false);
if (restore_hidden_face(object, *unode, modified_faces_hide)) {
changed_hide_face = true;
}
break;
case Type::Mask:
modified_verts_mask.resize(ss->totvert, false);
modified_verts_mask.resize(ss.totvert, false);
if (restore_mask(object, *unode, modified_verts_mask)) {
changed_mask = true;
}
break;
case Type::FaceSet:
modified_faces_face_set.resize(ss->totfaces, false);
modified_faces_face_set.resize(ss.totfaces, false);
if (restore_face_sets(object, *unode, modified_faces_face_set)) {
changed_face_sets = true;
}
break;
case Type::Color:
modified_verts_color.resize(ss->totvert, false);
modified_verts_color.resize(ss.totvert, false);
if (restore_color(object, *unode, modified_verts_color)) {
changed_color = true;
}
@ -1073,7 +1073,7 @@ static void restore_list(bContext *C, Depsgraph *depsgraph, StepData &step_data)
data.changed_position = changed_position;
data.changed_hide_vert = changed_hide_vert;
data.changed_mask = changed_mask;
data.pbvh = ss->pbvh.get();
data.pbvh = ss.pbvh.get();
data.modified_grids = modified_grids;
data.modified_position_verts = modified_verts_position;
data.modified_hidden_verts = modified_verts_hide;
@ -1083,30 +1083,30 @@ static void restore_list(bContext *C, Depsgraph *depsgraph, StepData &step_data)
data.modified_face_set_faces = modified_faces_face_set;
if (use_multires_undo) {
bke::pbvh::search_callback(
*ss->pbvh, {}, [&](PBVHNode &node) { update_modified_node_grids(node, data); });
*ss.pbvh, {}, [&](PBVHNode &node) { update_modified_node_grids(node, data); });
}
else {
bke::pbvh::search_callback(
*ss->pbvh, {}, [&](PBVHNode &node) { update_modified_node_mesh(mesh, node, data); });
*ss.pbvh, {}, [&](PBVHNode &node) { update_modified_node_mesh(mesh, node, data); });
}
if (changed_position) {
bke::pbvh::update_bounds(*ss->pbvh);
bke::pbvh::store_bounds_orig(*ss->pbvh);
bke::pbvh::update_bounds(*ss.pbvh);
bke::pbvh::store_bounds_orig(*ss.pbvh);
}
if (changed_mask) {
bke::pbvh::update_mask(*ss->pbvh);
bke::pbvh::update_mask(*ss.pbvh);
}
if (changed_hide_face) {
hide::sync_all_from_faces(object);
bke::pbvh::update_visibility(*ss->pbvh);
bke::pbvh::update_visibility(*ss.pbvh);
}
if (changed_hide_vert) {
if (ELEM(BKE_pbvh_type(*ss->pbvh), PBVH_FACES, PBVH_GRIDS)) {
if (ELEM(BKE_pbvh_type(*ss.pbvh), PBVH_FACES, PBVH_GRIDS)) {
Mesh &mesh = *static_cast<Mesh *>(object.data);
BKE_pbvh_sync_visibility_from_verts(*ss->pbvh, &mesh);
BKE_pbvh_sync_visibility_from_verts(*ss.pbvh, &mesh);
}
bke::pbvh::update_visibility(*ss->pbvh);
bke::pbvh::update_visibility(*ss.pbvh);
}
if (BKE_sculpt_multires_active(scene, &object)) {
@ -1119,14 +1119,14 @@ static void restore_list(bContext *C, Depsgraph *depsgraph, StepData &step_data)
}
const bool tag_update = ID_REAL_USERS(object.data) > 1 ||
!BKE_sculptsession_use_pbvh_draw(&object, rv3d) || ss->shapekey_active ||
ss->deform_modifiers_active;
!BKE_sculptsession_use_pbvh_draw(&object, rv3d) || ss.shapekey_active ||
ss.deform_modifiers_active;
if (tag_update) {
Mesh *mesh = static_cast<Mesh *>(object.data);
if (changed_position) {
mesh->tag_positions_changed();
BKE_sculptsession_free_deformMats(ss);
BKE_sculptsession_free_deformMats(&ss);
}
DEG_id_tag_update(&object.id, ID_RECALC_GEOMETRY);
}
@ -1155,12 +1155,12 @@ Node *get_node(const PBVHNode *node, const Type type)
return step_data->undo_nodes_by_pbvh_node.lookup_default({node, type}, nullptr);
}
static size_t alloc_and_store_hidden(const SculptSession *ss, const PBVHNode &node, Node *unode)
static size_t alloc_and_store_hidden(const SculptSession &ss, const PBVHNode &node, Node *unode)
{
if (!ss->subdiv_ccg) {
if (!ss.subdiv_ccg) {
return 0;
}
const BitGroupVector<> grid_hidden = ss->subdiv_ccg->grid_hidden;
const BitGroupVector<> grid_hidden = ss.subdiv_ccg->grid_hidden;
if (grid_hidden.is_empty()) {
return 0;
}
@ -1208,7 +1208,7 @@ static Node *find_or_alloc_node_type(const Type type)
static Node *alloc_node(const Object &object, const PBVHNode *node, const Type type)
{
StepData *step_data = get_step_data();
const SculptSession *ss = object.sculpt;
const SculptSession &ss = *object.sculpt;
Node *unode = alloc_node_type(type);
step_data->undo_nodes_by_pbvh_node.add({node, type}, unode);
@ -1216,9 +1216,9 @@ static Node *alloc_node(const Object &object, const PBVHNode *node, const Type t
const Mesh &mesh = *static_cast<Mesh *>(object.data);
int verts_num;
if (BKE_pbvh_type(*ss->pbvh) == PBVH_GRIDS) {
unode->mesh_grids_num = ss->subdiv_ccg->grids.size();
unode->grid_size = ss->subdiv_ccg->grid_size;
if (BKE_pbvh_type(*ss.pbvh) == PBVH_GRIDS) {
unode->mesh_grids_num = ss.subdiv_ccg->grids.size();
unode->grid_size = ss.subdiv_ccg->grid_size;
unode->grids = bke::pbvh::node_grid_indices(*node);
step_data->undo_size += unode->grids.as_span().size_in_bytes();
@ -1227,7 +1227,7 @@ static Node *alloc_node(const Object &object, const PBVHNode *node, const Type t
verts_num = unode->grids.size() * grid_area;
}
else {
unode->mesh_verts_num = ss->totvert;
unode->mesh_verts_num = ss.totvert;
unode->vert_indices = bke::pbvh::node_verts(*node);
unode->unique_verts_num = bke::pbvh::node_unique_verts(*node).size();
@ -1248,11 +1248,11 @@ static Node *alloc_node(const Object &object, const PBVHNode *node, const Type t
}
if (need_faces) {
if (BKE_pbvh_type(*ss->pbvh) == PBVH_FACES) {
if (BKE_pbvh_type(*ss.pbvh) == PBVH_FACES) {
bke::pbvh::node_face_indices_calc_mesh(mesh.corner_tri_faces(), *node, unode->face_indices);
}
else {
bke::pbvh::node_face_indices_calc_grids(*ss->pbvh, *node, unode->face_indices);
bke::pbvh::node_face_indices_calc_grids(*ss.pbvh, *node, unode->face_indices);
}
step_data->undo_size += unode->face_indices.as_span().size_in_bytes();
}
@ -1268,7 +1268,7 @@ static Node *alloc_node(const Object &object, const PBVHNode *node, const Type t
break;
}
case Type::HideVert: {
if (BKE_pbvh_type(*ss->pbvh) == PBVH_GRIDS) {
if (BKE_pbvh_type(*ss.pbvh) == PBVH_GRIDS) {
step_data->undo_size += alloc_and_store_hidden(ss, *node, unode);
}
else {
@ -1295,7 +1295,7 @@ static Node *alloc_node(const Object &object, const PBVHNode *node, const Type t
step_data->undo_size += unode->col.as_span().size_in_bytes();
/* Allocate loop colors separately too. */
if (ss->vcol_domain == bke::AttrDomain::Corner) {
if (ss.vcol_domain == bke::AttrDomain::Corner) {
unode->loop_col.reinitialize(unode->corner_indices.size());
unode->undo_size += unode->loop_col.as_span().size_in_bytes();
}
@ -1318,7 +1318,7 @@ static Node *alloc_node(const Object &object, const PBVHNode *node, const Type t
}
}
if (ss->deform_modifiers_active) {
if (ss.deform_modifiers_active) {
unode->orig_position.reinitialize(unode->vert_indices.size());
step_data->undo_size += unode->orig_position.as_span().size_in_bytes();
}
@ -1328,10 +1328,10 @@ static Node *alloc_node(const Object &object, const PBVHNode *node, const Type t
static void store_coords(const Object &object, Node *unode)
{
SculptSession *ss = object.sculpt;
SculptSession &ss = *object.sculpt;
if (!unode->grids.is_empty()) {
const SubdivCCG &subdiv_ccg = *ss->subdiv_ccg;
const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
const Span<CCGElem *> grids = subdiv_ccg.grids;
{
@ -1356,14 +1356,14 @@ static void store_coords(const Object &object, Node *unode)
}
}
else {
array_utils::gather(BKE_pbvh_get_vert_positions(*ss->pbvh).as_span(),
array_utils::gather(BKE_pbvh_get_vert_positions(*ss.pbvh).as_span(),
unode->vert_indices.as_span(),
unode->position.as_mutable_span());
array_utils::gather(BKE_pbvh_get_vert_normals(*ss->pbvh),
array_utils::gather(BKE_pbvh_get_vert_normals(*ss.pbvh),
unode->vert_indices.as_span(),
unode->normal.as_mutable_span());
if (ss->deform_modifiers_active) {
array_utils::gather(ss->orig_cos.as_span(),
if (ss.deform_modifiers_active) {
array_utils::gather(ss.orig_cos.as_span(),
unode->vert_indices.as_span(),
unode->orig_position.as_mutable_span());
}
@ -1407,10 +1407,10 @@ static void store_face_hidden(const Object &object, Node &unode)
static void store_mask(const Object &object, Node *unode)
{
const SculptSession *ss = object.sculpt;
const SculptSession &ss = *object.sculpt;
if (!unode->grids.is_empty()) {
const SubdivCCG &subdiv_ccg = *ss->subdiv_ccg;
const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
if (key.has_mask) {
const Span<CCGElem *> grids = subdiv_ccg.grids;
@ -1442,19 +1442,19 @@ static void store_mask(const Object &object, Node *unode)
static void store_color(const Object &object, Node *unode)
{
const Mesh &mesh = *static_cast<const Mesh *>(object.data);
SculptSession *ss = object.sculpt;
SculptSession &ss = *object.sculpt;
BLI_assert(BKE_pbvh_type(*ss->pbvh) == PBVH_FACES);
BLI_assert(BKE_pbvh_type(*ss.pbvh) == PBVH_FACES);
/* NOTE: even with loop colors we still store (derived)
* vertex colors for original data lookup. */
BKE_pbvh_store_colors_vertex(*ss->pbvh,
BKE_pbvh_store_colors_vertex(*ss.pbvh,
mesh.vert_to_face_map(),
unode->vert_indices.as_span().take_front(unode->unique_verts_num),
unode->col);
if (!unode->loop_col.is_empty() && !unode->corner_indices.is_empty()) {
BKE_pbvh_store_colors(*ss->pbvh, unode->corner_indices, unode->loop_col);
BKE_pbvh_store_colors(*ss.pbvh, unode->corner_indices, unode->loop_col);
}
}
@ -1492,7 +1492,7 @@ static void store_face_sets(const Mesh &mesh, Node &unode)
static Node *bmesh_push(const Object &object, const PBVHNode *node, Type type)
{
StepData *step_data = get_step_data();
const SculptSession *ss = object.sculpt;
const SculptSession &ss = *object.sculpt;
Node *unode = step_data->nodes.is_empty() ? nullptr : step_data->nodes.first().get();
@ -1504,8 +1504,8 @@ static Node *bmesh_push(const Object &object, const PBVHNode *node, Type type)
unode->applied = true;
if (type == Type::DyntopoEnd) {
unode->bm_entry = BM_log_entry_add(ss->bm_log);
BM_log_before_all_removed(ss->bm, ss->bm_log);
unode->bm_entry = BM_log_entry_add(ss.bm_log);
BM_log_before_all_removed(ss.bm, ss.bm_log);
}
else if (type == Type::DyntopoBegin) {
/* Store a copy of the mesh's current vertices, loops, and
@ -1516,17 +1516,17 @@ static Node *bmesh_push(const Object &object, const PBVHNode *node, Type type)
NodeGeometry *geometry = &unode->geometry_bmesh_enter;
store_geometry_data(geometry, object);
unode->bm_entry = BM_log_entry_add(ss->bm_log);
BM_log_all_added(ss->bm, ss->bm_log);
unode->bm_entry = BM_log_entry_add(ss.bm_log);
BM_log_all_added(ss.bm, ss.bm_log);
}
else {
unode->bm_entry = BM_log_entry_add(ss->bm_log);
unode->bm_entry = BM_log_entry_add(ss.bm_log);
}
}
if (node) {
const int cd_vert_mask_offset = CustomData_get_offset_named(
&ss->bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
&ss.bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
/* The vertices and node aren't changed, though pointers to them are stored in the log. */
PBVHNode *node_mut = const_cast<PBVHNode *>(node);
@ -1537,24 +1537,24 @@ static Node *bmesh_push(const Object &object, const PBVHNode *node, Type type)
/* Before any vertex values get modified, ensure their
* original positions are logged. */
for (BMVert *vert : BKE_pbvh_bmesh_node_unique_verts(node_mut)) {
BM_log_vert_before_modified(ss->bm_log, vert, cd_vert_mask_offset);
BM_log_vert_before_modified(ss.bm_log, vert, cd_vert_mask_offset);
}
for (BMVert *vert : BKE_pbvh_bmesh_node_other_verts(node_mut)) {
BM_log_vert_before_modified(ss->bm_log, vert, cd_vert_mask_offset);
BM_log_vert_before_modified(ss.bm_log, vert, cd_vert_mask_offset);
}
break;
case Type::HideFace:
case Type::HideVert: {
for (BMVert *vert : BKE_pbvh_bmesh_node_unique_verts(node_mut)) {
BM_log_vert_before_modified(ss->bm_log, vert, cd_vert_mask_offset);
BM_log_vert_before_modified(ss.bm_log, vert, cd_vert_mask_offset);
}
for (BMVert *vert : BKE_pbvh_bmesh_node_other_verts(node_mut)) {
BM_log_vert_before_modified(ss->bm_log, vert, cd_vert_mask_offset);
BM_log_vert_before_modified(ss.bm_log, vert, cd_vert_mask_offset);
}
for (BMFace *f : BKE_pbvh_bmesh_node_faces(node_mut)) {
BM_log_face_modified(ss->bm_log, f);
BM_log_face_modified(ss.bm_log, f);
}
break;
}
@ -1574,7 +1574,7 @@ static Node *bmesh_push(const Object &object, const PBVHNode *node, Type type)
Node *push_node(const Object &object, const PBVHNode *node, Type type)
{
SculptSession *ss = object.sculpt;
SculptSession &ss = *object.sculpt;
Node *unode;
@ -1583,10 +1583,10 @@ Node *push_node(const Object &object, const PBVHNode *node, Type type)
/* List is manipulated by multiple threads, so we lock. */
std::scoped_lock lock(step_data->nodes_mutex);
ss->needs_flush_to_id = 1;
ss.needs_flush_to_id = 1;
threading::isolate_task([&]() {
if (ss->bm || ELEM(type, Type::DyntopoBegin, Type::DyntopoEnd)) {
if (ss.bm || ELEM(type, Type::DyntopoBegin, Type::DyntopoEnd)) {
/* Dynamic topology stores only one undo node per stroke,
* regardless of the number of PBVH nodes modified. */
unode = bmesh_push(object, node, type);
@ -2027,7 +2027,7 @@ static bool use_multires_mesh(bContext *C)
static void push_all_grids(Object *object)
{
SculptSession *ss = object->sculpt;
SculptSession &ss = *object->sculpt;
/* It is possible that undo push is done from an object state where there is no PBVH. This
* happens, for example, when an operation which tagged for geometry update was performed prior
@ -2037,11 +2037,11 @@ static void push_all_grids(Object *object)
* ensure PBVH for the new base geometry, which will have same coordinates as if we create PBVH
* here.
*/
if (ss->pbvh == nullptr) {
if (ss.pbvh == nullptr) {
return;
}
Vector<PBVHNode *> nodes = bke::pbvh::search_gather(*ss->pbvh, {});
Vector<PBVHNode *> nodes = bke::pbvh::search_gather(*ss.pbvh, {});
for (PBVHNode *node : nodes) {
push_node(*object, node, Type::Position);
}

@ -362,9 +362,7 @@ static bool stats_is_object_dynamic_topology_sculpt(const Object *ob)
static void stats_object_sculpt(const Object *ob, SceneStats *stats)
{
SculptSession *ss = ob->sculpt;
if (ss == nullptr || ss->pbvh == nullptr) {
return;
}