Fix #137751: Dyntopo undo can crash when drawing attributes

When performing an undo in Sculpt Mode with Dyntopo enabled, it is
possible that the active color attribute exists in the original mesh but
not on the BMesh used by Dyntopo. When attempting to extract this color
data, this can then lead to a crash.

To prevent this, use the BMesh as the source of truth for existence of
generic attributes when mode inside Sculpt Mode when Dyntopo is enabled.

Pull Request: https://projects.blender.org/blender/blender/pulls/137828
This commit is contained in:
Sean Kim
2025-04-22 22:24:10 +02:00
committed by Sean Kim
parent 7002f16992
commit d8bab1a65e

View File

@@ -21,6 +21,8 @@
#include "BLI_math_matrix.hh"
#include "bmesh_class.hh"
#include "DRW_pbvh.hh"
#include "DRW_render.hh"
@@ -123,6 +125,28 @@ static Vector<SculptBatch> sculpt_batches_get_ex(const Object *ob,
return result_batches;
}
static const CustomData *get_cdata(const BMesh &bm, const bke::AttrDomain domain)
{
switch (domain) {
case bke::AttrDomain::Point:
return &bm.vdata;
case bke::AttrDomain::Corner:
return &bm.ldata;
case bke::AttrDomain::Face:
return &bm.pdata;
default:
return nullptr;
}
}
static bool bmesh_attribute_exists(const BMesh &bm,
const bke::AttributeMetaData &meta_data,
const StringRef name)
{
const CustomData *cdata = get_cdata(bm, meta_data.domain);
return cdata && CustomData_get_offset_named(cdata, meta_data.data_type, name) != -1;
}
Vector<SculptBatch> sculpt_batches_get(const Object *ob, SculptBatchFeature features)
{
Vector<pbvh::AttributeRequest, 16> attrs;
@@ -138,19 +162,30 @@ Vector<SculptBatch> sculpt_batches_get(const Object *ob, SculptBatchFeature feat
const Mesh *mesh = BKE_object_get_original_mesh(ob);
const bke::AttributeAccessor attributes = mesh->attributes();
const SculptSession &ss = *ob->sculpt;
/* If Dyntopo is enabled, the source of truth for an attribute existing or not is the BMesh, not
* the Mesh. */
if (features & SCULPT_BATCH_VERTEX_COLOR) {
if (const char *name = mesh->active_color_attribute) {
if (const std::optional<bke::AttributeMetaData> meta_data = attributes.lookup_meta_data(
name))
{
attrs.append(pbvh::GenericRequest{name, meta_data->data_type, meta_data->domain});
if (ss.bm) {
if (bmesh_attribute_exists(*ss.bm, *meta_data, name)) {
attrs.append(pbvh::GenericRequest{name, meta_data->data_type, meta_data->domain});
}
}
else {
attrs.append(pbvh::GenericRequest{name, meta_data->data_type, meta_data->domain});
}
}
}
}
if (features & SCULPT_BATCH_UV) {
if (const char *name = CustomData_get_active_layer_name(&mesh->corner_data, CD_PROP_FLOAT2)) {
const CustomData *corner_data = ss.bm ? &ss.bm->ldata : &mesh->corner_data;
if (const char *name = CustomData_get_active_layer_name(corner_data, CD_PROP_FLOAT2)) {
attrs.append(pbvh::GenericRequest{name, CD_PROP_FLOAT2, bke::AttrDomain::Corner});
}
}