From f1c0249f34c4171ec311b5b9882e36fed5889259 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 31 Aug 2022 09:09:01 -0500 Subject: Mesh: Move material indices to a generic attribute This patch moves material indices from the mesh `MPoly` struct to a generic integer attribute. The builtin material index was already exposed in geometry nodes, but this makes it a "proper" attribute accessible with Python and visible in the "Attributes" panel. The goals of the refactor are code simplification and memory and performance improvements, mainly because the attribute doesn't have to be stored and processed if there are no materials. However, until 4.0, material indices will still be read and written in the old format, meaning there may be a temporary increase in memory usage. Further notes: * Completely removing the `MPoly.mat_nr` after 4.0 may require changes to DNA or introducing a new `MPoly` type. * Geometry nodes regression tests didn't look at material indices, so the change reveals a bug in the realize instances node that I fixed. * Access to material indices from the RNA `MeshPolygon` type is slower with this patch. The `material_index` attribute can be used instead. * Cycles is changed to read from the attribute instead. * BMesh isn't changed in this patch. Theoretically it could be though, to save 2 bytes per face when less than two materials are used. * Eventually we could use a 16 bit integer attribute type instead. Ref T95967 Differential Revision: https://developer.blender.org/D15675 --- source/blender/bmesh/intern/bmesh_mesh_convert.cc | 54 +++++++++++++++++------ 1 file changed, 41 insertions(+), 13 deletions(-) (limited to 'source/blender/bmesh/intern/bmesh_mesh_convert.cc') diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index 4e0e27cd051..47ad5080451 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -363,6 +363,8 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar &me->edata, CD_PROP_BOOL, ".hide_edge"); const bool *hide_poly = (const bool *)CustomData_get_layer_named( &me->pdata, CD_PROP_BOOL, ".hide_poly"); + const int *material_indices = (const int *)CustomData_get_layer_named( + &me->pdata, CD_PROP_INT32, "material_index"); Span mvert{me->mvert, me->totvert}; Array vtable(me->totvert); @@ -484,7 +486,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar BM_face_select_set(bm, f, true); } - f->mat_nr = mpoly[i].mat_nr; + f->mat_nr = material_indices == NULL ? 0 : material_indices[i]; if (i == me->act_face) { bm->act_face = f; } @@ -923,16 +925,16 @@ BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e) } } -template -static void write_elem_flag_to_attribute(blender::bke::MutableAttributeAccessor &attributes, - const StringRef attribute_name, - const eAttrDomain domain, - const bool do_write, - const GetFn &get_fn) +template +static void write_fn_to_attribute(blender::bke::MutableAttributeAccessor attributes, + const StringRef attribute_name, + const eAttrDomain domain, + const bool do_write, + const GetFn &get_fn) { using namespace blender; if (do_write) { - bke::SpanAttributeWriter attribute = attributes.lookup_or_add_for_write_only_span( + bke::SpanAttributeWriter attribute = attributes.lookup_or_add_for_write_only_span( attribute_name, domain); threading::parallel_for(attribute.span.index_range(), 4096, [&](IndexRange range) { for (const int i : range) { @@ -966,15 +968,15 @@ static void convert_bmesh_hide_flags_to_mesh_attributes(BMesh &bm, bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(mesh); BM_mesh_elem_table_ensure(&bm, BM_VERT | BM_EDGE | BM_FACE); - write_elem_flag_to_attribute( + write_fn_to_attribute( attributes, ".hide_vert", ATTR_DOMAIN_POINT, need_hide_vert, [&](const int i) { return BM_elem_flag_test(BM_vert_at_index(&bm, i), BM_ELEM_HIDDEN); }); - write_elem_flag_to_attribute( + write_fn_to_attribute( attributes, ".hide_edge", ATTR_DOMAIN_EDGE, need_hide_edge, [&](const int i) { return BM_elem_flag_test(BM_edge_at_index(&bm, i), BM_ELEM_HIDDEN); }); - write_elem_flag_to_attribute( + write_fn_to_attribute( attributes, ".hide_poly", ATTR_DOMAIN_FACE, need_hide_poly, [&](const int i) { return BM_elem_flag_test(BM_face_at_index(&bm, i), BM_ELEM_HIDDEN); }); @@ -1039,6 +1041,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh bool need_hide_vert = false; bool need_hide_edge = false; bool need_hide_poly = false; + bool need_material_index = false; /* Clear normals on the mesh completely, since the original vertex and polygon count might be * different than the BMesh's. */ @@ -1111,7 +1114,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh BMLoop *l_iter, *l_first; mpoly->loopstart = j; mpoly->totloop = f->len; - mpoly->mat_nr = f->mat_nr; + if (f->mat_nr != 0) { + need_material_index = true; + } mpoly->flag = BM_face_flag_to_mflag(f); if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { need_hide_poly = true; @@ -1144,6 +1149,16 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh BM_CHECK_ELEMENT(f); } + if (need_material_index) { + BM_mesh_elem_table_ensure(bm, BM_FACE); + write_fn_to_attribute( + blender::bke::mesh_attributes_for_write(*me), + "material_index", + ATTR_DOMAIN_FACE, + true, + [&](const int i) { return static_cast(BM_face_at_index(bm, i)->mat_nr); }); + } + /* Patch hook indices and vertex parents. */ if (params->calc_object_remap && (ototvert > 0)) { BLI_assert(bmain != nullptr); @@ -1307,6 +1322,7 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * bool need_hide_vert = false; bool need_hide_edge = false; bool need_hide_poly = false; + bool need_material_index = false; /* Clear normals on the mesh completely, since the original vertex and polygon count might be * different than the BMesh's. */ @@ -1385,7 +1401,9 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * } mp->loopstart = j; - mp->mat_nr = efa->mat_nr; + if (efa->mat_nr != 0) { + need_material_index = true; + } l_iter = l_first = BM_FACE_FIRST_LOOP(efa); do { @@ -1403,6 +1421,16 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * } bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP); + if (need_material_index) { + BM_mesh_elem_table_ensure(bm, BM_FACE); + write_fn_to_attribute( + blender::bke::mesh_attributes_for_write(*me), + "material_index", + ATTR_DOMAIN_FACE, + true, + [&](const int i) { return static_cast(BM_face_at_index(bm, i)->mat_nr); }); + } + convert_bmesh_hide_flags_to_mesh_attributes( *bm, need_hide_vert, need_hide_edge, need_hide_poly, *me); -- cgit v1.2.3