diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index b71a705eecb..0e7e81a2383 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -2289,26 +2289,12 @@ bool CustomData_merge(const CustomData *source, return changed; } -static bool attribute_stored_in_bmesh_flag(const StringRef name) -{ - return ELEM(name, - "position", - ".hide_vert", - ".hide_edge", - ".hide_poly", - ".select_vert", - ".select_edge", - ".select_poly", - "material_index", - "sharp_edge"); -} - CustomData CustomData_shallow_copy_remove_non_bmesh_attributes(const CustomData *src, const eCustomDataMask mask) { Vector dst_layers; for (const CustomDataLayer &layer : Span{src->layers, src->totlayer}) { - if (attribute_stored_in_bmesh_flag(layer.name)) { + if (BM_attribute_stored_in_bmesh_builtin(layer.name)) { continue; } if (!(mask & CD_TYPE_AS_MASK(layer.type))) { diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index f4c8ca10c42..35f59a2f143 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -133,6 +133,20 @@ static char bm_face_flag_to_mflag(const BMFace *f) return ((hflag & BM_ELEM_SMOOTH) ? ME_SMOOTH : 0); } +bool BM_attribute_stored_in_bmesh_builtin(const StringRef name) +{ + return ELEM(name, + "position", + ".hide_vert", + ".hide_edge", + ".hide_poly", + ".select_vert", + ".select_edge", + ".select_poly", + "material_index", + "sharp_edge"); +} + /* Static function for alloc (duplicate in modifiers_bmesh.c) */ static BMFace *bm_face_create_from_mpoly(BMesh &bm, Span loops, diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.h b/source/blender/bmesh/intern/bmesh_mesh_convert.h index bae689b3af2..ef968594998 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.h +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.h @@ -9,6 +9,16 @@ #include "bmesh.h" +#ifdef __cplusplus +# include "BLI_string_ref.hh" + +/** + * \return Whether attributes with the given name are stored in special flags or fields in BMesh + * rather than in the regular custom data blocks. + */ +bool BM_attribute_stored_in_bmesh_builtin(const blender::StringRef name); +#endif + #ifdef __cplusplus extern "C" { #endif diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index e1032cb996c..986974f2e20 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -28,6 +28,8 @@ #include "BKE_pointcache.h" #include "BKE_scene.h" +#include "BLI_index_range.hh" + #include "DEG_depsgraph.h" #include "WM_api.h" @@ -42,6 +44,8 @@ #include "bmesh.h" #include "bmesh_tools.h" +using blender::IndexRange; + void SCULPT_dynamic_topology_triangulate(BMesh *bm) { if (bm->totloop != bm->totface * 3) { @@ -281,8 +285,8 @@ static int dyntopo_warning_popup(bContext *C, wmOperatorType *ot, enum eDynTopoW uiLayout *layout = UI_popup_menu_layout(pup); if (flag & (DYNTOPO_WARN_VDATA | DYNTOPO_WARN_EDATA | DYNTOPO_WARN_LDATA)) { - const char *msg_error = TIP_("Vertex Data Detected!"); - const char *msg = TIP_("Dyntopo will not preserve vertex colors, UVs, or other customdata"); + const char *msg_error = TIP_("Attribute Data Detected"); + const char *msg = TIP_("Dyntopo will not preserve colors, UVs, or other attributes"); uiItemL(layout, msg_error, ICON_INFO); uiItemL(layout, msg, ICON_NONE); uiItemS(layout); @@ -305,6 +309,39 @@ static int dyntopo_warning_popup(bContext *C, wmOperatorType *ot, enum eDynTopoW return OPERATOR_INTERFACE; } +static bool dyntopo_supports_customdata_layers(const blender::Span layers, + int totelem) +{ + for (const CustomDataLayer &layer : layers) { + if (CD_TYPE_AS_MASK(layer.type) & CD_MASK_PROP_ALL) { + if (layer.name[0] == '\0') { + return false; + } + + if (STREQ(layer.name, ".sculpt_face_sets") && totelem > 0) { + int *fsets = static_cast(layer.data); + int fset = fsets[0]; + + /* Check if only one face set exists. */ + for (int i : IndexRange(totelem)) { + if (fsets[i] != fset) { + return false; + } + } + + return true; + } + + /* Some data is stored as generic attributes on #Mesh but in flags or field on #BMesh. */ + return BM_attribute_stored_in_bmesh_builtin(layer.name); + } + /* Some layers just encode #Mesh topology or are handled as special cases for dyntopo. */ + return ELEM(layer.type, CD_MEDGE, CD_MFACE, CD_MLOOP, CD_MPOLY, CD_PAINT_MASK, CD_ORIGINDEX); + } + + return true; +} + enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob) { Mesh *me = static_cast(ob->data); @@ -315,18 +352,17 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob) BLI_assert(ss->bm == nullptr); UNUSED_VARS_NDEBUG(ss); - for (int i = 0; i < CD_NUMTYPES; i++) { - if (!ELEM(i, CD_MEDGE, CD_MFACE, CD_MLOOP, CD_MPOLY, CD_PAINT_MASK, CD_ORIGINDEX)) { - if (CustomData_has_layer(&me->vdata, i)) { - flag |= DYNTOPO_WARN_VDATA; - } - if (CustomData_has_layer(&me->edata, i)) { - flag |= DYNTOPO_WARN_EDATA; - } - if (CustomData_has_layer(&me->ldata, i)) { - flag |= DYNTOPO_WARN_LDATA; - } - } + if (!dyntopo_supports_customdata_layers({me->vdata.layers, me->vdata.totlayer}, me->totvert)) { + flag |= DYNTOPO_WARN_VDATA; + } + if (!dyntopo_supports_customdata_layers({me->edata.layers, me->edata.totlayer}, me->totedge)) { + flag |= DYNTOPO_WARN_EDATA; + } + if (!dyntopo_supports_customdata_layers({me->pdata.layers, me->pdata.totlayer}, me->totpoly)) { + flag |= DYNTOPO_WARN_LDATA; + } + if (!dyntopo_supports_customdata_layers({me->ldata.layers, me->ldata.totlayer}, me->totloop)) { + flag |= DYNTOPO_WARN_LDATA; } {