From 91b52545989ce29c6cd350a99191accf23d3d9dd Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Sun, 19 Jun 2022 17:39:54 +0300 Subject: Fix T98874: new obj importer missing an option to import vertex groups The old Python OBJ importer had a (somewhat confusingly named) "Keep Vertex Order -> Poly Groups" option, that imported OBJ groups as "vertex groups" on the resulting mesh. All vertices of any face were assigned the vertex group, with a 1.0 weight. The new C++ importer did not have this option. It was trying to do something with vertex groups, but failing to actually achieve anything :) -- the vertex groups were created on the wrong object (later on overwritten by "nomain mesh to main mesh" operation); vertex weights were set to 1.0/vertex_count, and each vertex was only set to be in one group, even when it belongs to multiple faces from different groups. End result was that to the user, vertex groups were not visible/present at all (see T98874). This patch adds the import option (named "Vertex Groups"), which is off by default, and fixes the import code logic to actually do the right thing. Tested on file from T98874; vertex groups are imported just like with the Python importer. Reviewed By: Howard Trickey Differential Revision: https://developer.blender.org/D15200 --- .../io/wavefront_obj/importer/obj_import_mesh.cc | 39 +++++++++------------- 1 file changed, 16 insertions(+), 23 deletions(-) (limited to 'source/blender/io/wavefront_obj/importer/obj_import_mesh.cc') diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index acc35ad46e1..51318331c76 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -10,6 +10,7 @@ #include "BKE_attribute.h" #include "BKE_customdata.h" +#include "BKE_deform.h" #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_node_tree_update.h" @@ -47,7 +48,7 @@ Object *MeshFromGeometry::create_mesh(Main *bmain, obj->data = BKE_object_obdata_add_from_type(bmain, OB_MESH, ob_name.c_str()); create_vertices(mesh); - create_polys_loops(obj, mesh); + create_polys_loops(obj, mesh, import_params.import_vertex_groups); create_edges(mesh); create_uv_verts(mesh); create_normals(mesh); @@ -69,6 +70,9 @@ Object *MeshFromGeometry::create_mesh(Main *bmain, BKE_mesh_nomain_to_mesh(mesh, dst, obj, &CD_MASK_EVERYTHING, true); dst->flag |= autosmooth; + /* Note: vertex groups have to be created after final mesh is assigned to the object. */ + create_vertex_groups(obj); + return obj; } @@ -163,19 +167,13 @@ void MeshFromGeometry::create_vertices(Mesh *mesh) } } -void MeshFromGeometry::create_polys_loops(Object *obj, Mesh *mesh) +void MeshFromGeometry::create_polys_loops(Object *obj, Mesh *mesh, bool use_vertex_groups) { - /* Will not be used if vertex groups are not imported. */ mesh->dvert = nullptr; - float weight = 0.0f; const int64_t total_verts = mesh_geometry_.vertex_count_; - if (total_verts && mesh_geometry_.use_vertex_groups_) { + if (use_vertex_groups && total_verts && mesh_geometry_.has_vertex_groups_) { mesh->dvert = static_cast( CustomData_add_layer(&mesh->vdata, CD_MDEFORMVERT, CD_CALLOC, nullptr, total_verts)); - weight = 1.0f / total_verts; - } - else { - UNUSED_VARS(weight); } const int64_t tot_face_elems{mesh->totpoly}; @@ -208,28 +206,23 @@ void MeshFromGeometry::create_polys_loops(Object *obj, Mesh *mesh) tot_loop_idx++; mloop.v = curr_corner.vert_index; + /* Setup vertex group data, if needed. */ if (!mesh->dvert) { continue; } - /* Iterating over mloop results in finding the same vertex multiple times. - * Another way is to allocate memory for dvert while creating vertices and fill them here. - */ - MDeformVert &def_vert = mesh->dvert[mloop.v]; - if (!def_vert.dw) { - def_vert.dw = static_cast( - MEM_callocN(sizeof(MDeformWeight), "OBJ Import Deform Weight")); - } - /* Every vertex in a face is assigned the same deform group. */ - int group_idx = curr_face.vertex_group_index; - /* Deform group number (def_nr) must behave like an index into the names' list. */ - *(def_vert.dw) = {static_cast(group_idx), weight}; + const int group_index = curr_face.vertex_group_index; + MDeformWeight *dw = BKE_defvert_ensure_index(mesh->dvert + mloop.v, group_index); + dw->weight = 1.0f; } } +} - if (!mesh->dvert) { +void MeshFromGeometry::create_vertex_groups(Object *obj) +{ + Mesh *mesh = static_cast(obj->data); + if (mesh->dvert == nullptr) { return; } - /* Add deform group names. */ for (const std::string &name : mesh_geometry_.group_order_) { BKE_object_defgroup_add_name(obj, name.data()); } -- cgit v1.2.3 From 256cb68d33aec68f2c3ae20a48a115046ba5ad0e Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 21 Jun 2022 14:17:24 -0500 Subject: Cleanup: Remove unused argument --- source/blender/io/wavefront_obj/importer/obj_import_mesh.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'source/blender/io/wavefront_obj/importer/obj_import_mesh.cc') diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index 51318331c76..aa38a4d6715 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -48,7 +48,7 @@ Object *MeshFromGeometry::create_mesh(Main *bmain, obj->data = BKE_object_obdata_add_from_type(bmain, OB_MESH, ob_name.c_str()); create_vertices(mesh); - create_polys_loops(obj, mesh, import_params.import_vertex_groups); + create_polys_loops(mesh, import_params.import_vertex_groups); create_edges(mesh); create_uv_verts(mesh); create_normals(mesh); @@ -167,7 +167,7 @@ void MeshFromGeometry::create_vertices(Mesh *mesh) } } -void MeshFromGeometry::create_polys_loops(Object *obj, Mesh *mesh, bool use_vertex_groups) +void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) { mesh->dvert = nullptr; const int64_t total_verts = mesh_geometry_.vertex_count_; -- cgit v1.2.3 From 7b6b740ace1e56a8217fb44ed9fd3cf0c0a324f4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 27 Jun 2022 17:29:57 +1000 Subject: Cleanup: spelling in comments --- source/blender/io/wavefront_obj/importer/obj_import_mesh.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source/blender/io/wavefront_obj/importer/obj_import_mesh.cc') diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index aa38a4d6715..1e0b36ae4cb 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -70,7 +70,7 @@ Object *MeshFromGeometry::create_mesh(Main *bmain, BKE_mesh_nomain_to_mesh(mesh, dst, obj, &CD_MASK_EVERYTHING, true); dst->flag |= autosmooth; - /* Note: vertex groups have to be created after final mesh is assigned to the object. */ + /* NOTE: vertex groups have to be created after final mesh is assigned to the object. */ create_vertex_groups(obj); return obj; -- cgit v1.2.3 From 94323bb427480aaaeea52f6833173866fbacf2e1 Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Wed, 6 Jul 2022 13:29:59 +0300 Subject: IO: speed up import of large Alembic/USD/OBJ scenes by optimizing material assignment The importer parts that were doing assignment of materials to the imported objects/meshes were essentially having a quadratic complexity in terms of scene object count. For each material assigned to each object, they were scanning the whole scene, checking which other Objects use the same Mesh data, in order to resize their material arrays to match the size. Performance details (Windows, Ryzen 5950X): - Import OBJ Blender 3.0 splash scene (24k objects): 43.0s -> 32.9s - Import USD Disney Moana scene (260k objects): saves two hours (~7400s). Note that later on this crashes when trying to render the imported result; crashes in the same way/place both in master and this patch. Implementation details: The importers were doing "scan the world" basically twice for each object, for each material: once when creating a new material slot (assigns an empty material), and then again when assigning the material. However, all these importers (USD, Alembic, OBJ) always create one Object for one Mesh. So that whole quadratic complexity resulting from "scan the world for possible other users of this obdata" is completely not needed; it just never finds anything. So add a new dedicated function BKE_object_material_assign_single_obdata that skips the expensive part, but should only be used when the caller knows that the obdata has exactly one user (the passed object). Reviewed By: Bastien Montagne, Michael Kowalski Differential Revision: https://developer.blender.org/D15145 --- source/blender/io/wavefront_obj/importer/obj_import_mesh.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'source/blender/io/wavefront_obj/importer/obj_import_mesh.cc') diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index 1e0b36ae4cb..8cef0700b3b 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -306,8 +306,7 @@ void MeshFromGeometry::create_materials(Main *bmain, if (mat == nullptr) { continue; } - BKE_object_material_slot_add(bmain, obj); - BKE_object_material_assign(bmain, obj, mat, obj->totcol, BKE_MAT_ASSIGN_USERPREF); + BKE_object_material_assign_single_obdata(bmain, obj, mat, obj->totcol + 1); } } -- cgit v1.2.3 From fad857f47319166d3ff97029385b50059731a576 Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Sun, 10 Jul 2022 20:09:29 +0300 Subject: Fix T99532: New OBJ importer in some cases fails to import faces The importer code was written under incorrect assumption that vertex data (v, vn, vt commands etc.) are grouped by object, i.e. follow the o command, and that each object has its own vertex data commands. This is not the case -- all the vertex data in the whole OBJ file is "global", with no relation to any objects/groups; it's just that the faces belong to the object, and then they pull in any vertices they like. This patch fixes this incorrect assumption in the importer: - Vertex data is now properly global; no need to track some sort of "offsets" per object like it was doing before. - For each object, face definitions track the minimum & maximum vertex indices referenced by the object, and then all that vertex range is created in the final Blender object. Note: it might be (unusual, but possible) that an object does not reference a sequential range of vertices, e.g. just a single face with vertex indices 1, 10, 100 -- the resulting Blender mesh will have all the 100 vertices (some "loose" without belonging to a face). It should be possible to track the used vertices exactly (e.g. with a vector set), but I haven't done that for performance reasons. Reviewed By: Howard Trickey Differential Revision: https://developer.blender.org/D15410 --- .../io/wavefront_obj/importer/obj_import_mesh.cc | 57 ++++++++++++---------- 1 file changed, 31 insertions(+), 26 deletions(-) (limited to 'source/blender/io/wavefront_obj/importer/obj_import_mesh.cc') diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index 8cef0700b3b..01f05466b3a 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -31,13 +31,17 @@ Object *MeshFromGeometry::create_mesh(Main *bmain, Map &created_materials, const OBJImportParams &import_params) { + const int64_t tot_verts_object{mesh_geometry_.get_vertex_count()}; + if (tot_verts_object <= 0) { + /* Empty mesh */ + return nullptr; + } std::string ob_name{mesh_geometry_.geometry_name_}; if (ob_name.empty()) { ob_name = "Untitled"; } fixup_invalid_faces(); - const int64_t tot_verts_object{mesh_geometry_.vertex_count_}; /* Total explicitly imported edges, not the ones belonging the polygons to be created. */ const int64_t tot_edges{mesh_geometry_.edges_.size()}; const int64_t tot_face_elems{mesh_geometry_.face_elements_.size()}; @@ -153,9 +157,9 @@ void MeshFromGeometry::fixup_invalid_faces() void MeshFromGeometry::create_vertices(Mesh *mesh) { - const int tot_verts_object{mesh_geometry_.vertex_count_}; + const int tot_verts_object{mesh_geometry_.get_vertex_count()}; for (int i = 0; i < tot_verts_object; ++i) { - int vi = mesh_geometry_.vertex_start_ + i; + int vi = mesh_geometry_.vertex_index_min_ + i; if (vi < global_vertices_.vertices.size()) { copy_v3_v3(mesh->mvert[i].co, global_vertices_.vertices[vi]); } @@ -170,7 +174,7 @@ void MeshFromGeometry::create_vertices(Mesh *mesh) void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) { mesh->dvert = nullptr; - const int64_t total_verts = mesh_geometry_.vertex_count_; + const int64_t total_verts = mesh_geometry_.get_vertex_count(); if (use_vertex_groups && total_verts && mesh_geometry_.has_vertex_groups_) { mesh->dvert = static_cast( CustomData_add_layer(&mesh->vdata, CD_MDEFORMVERT, CD_CALLOC, nullptr, total_verts)); @@ -204,7 +208,7 @@ void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) const PolyCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx]; MLoop &mloop = mesh->mloop[tot_loop_idx]; tot_loop_idx++; - mloop.v = curr_corner.vert_index; + mloop.v = curr_corner.vert_index - mesh_geometry_.vertex_index_min_; /* Setup vertex group data, if needed. */ if (!mesh->dvert) { @@ -231,14 +235,14 @@ void MeshFromGeometry::create_vertex_groups(Object *obj) void MeshFromGeometry::create_edges(Mesh *mesh) { const int64_t tot_edges{mesh_geometry_.edges_.size()}; - const int64_t total_verts{mesh_geometry_.vertex_count_}; + const int64_t total_verts{mesh_geometry_.get_vertex_count()}; UNUSED_VARS_NDEBUG(total_verts); for (int i = 0; i < tot_edges; ++i) { const MEdge &src_edge = mesh_geometry_.edges_[i]; MEdge &dst_edge = mesh->medge[i]; - BLI_assert(src_edge.v1 < total_verts && src_edge.v2 < total_verts); - dst_edge.v1 = src_edge.v1; - dst_edge.v2 = src_edge.v2; + dst_edge.v1 = src_edge.v1 - mesh_geometry_.vertex_index_min_; + dst_edge.v2 = src_edge.v2 - mesh_geometry_.vertex_index_min_; + BLI_assert(dst_edge.v1 < total_verts && dst_edge.v2 < total_verts); dst_edge.flag = ME_LOOSEEDGE; } @@ -312,10 +316,8 @@ void MeshFromGeometry::create_materials(Main *bmain, void MeshFromGeometry::create_normals(Mesh *mesh) { - /* NOTE: Needs more clarity about what is expected in the viewport if the function works. */ - /* No normal data: nothing to do. */ - if (global_vertices_.vertex_normals.is_empty() || !mesh_geometry_.has_vertex_normals_) { + if (global_vertices_.vertex_normals.is_empty()) { return; } @@ -341,23 +343,26 @@ void MeshFromGeometry::create_normals(Mesh *mesh) void MeshFromGeometry::create_colors(Mesh *mesh) { - /* Nothing to do if we don't have vertex colors. */ - if (mesh_geometry_.vertex_color_count_ < 1) { - return; - } - if (mesh_geometry_.vertex_color_count_ != mesh_geometry_.vertex_count_) { - std::cerr << "Mismatching number of vertices (" << mesh_geometry_.vertex_count_ - << ") and colors (" << mesh_geometry_.vertex_color_count_ << ") on object '" - << mesh_geometry_.geometry_name_ << "', ignoring colors." << std::endl; + /* Nothing to do if we don't have vertex colors at all. */ + if (global_vertices_.vertex_colors.is_empty()) { return; } - CustomDataLayer *color_layer = BKE_id_attribute_new( - &mesh->id, "Color", CD_PROP_COLOR, ATTR_DOMAIN_POINT, nullptr); - float4 *colors = (float4 *)color_layer->data; - for (int i = 0; i < mesh_geometry_.vertex_color_count_; ++i) { - float3 c = global_vertices_.vertex_colors[mesh_geometry_.vertex_color_start_ + i]; - colors[i] = float4(c.x, c.y, c.z, 1.0f); + /* Find which vertex color block is for this mesh (if any). */ + for (const auto &block : global_vertices_.vertex_colors) { + if (mesh_geometry_.vertex_index_min_ >= block.start_vertex_index && + mesh_geometry_.vertex_index_max_ < block.start_vertex_index + block.colors.size()) { + /* This block is suitable, use colors from it. */ + CustomDataLayer *color_layer = BKE_id_attribute_new( + &mesh->id, "Color", CD_PROP_COLOR, ATTR_DOMAIN_POINT, nullptr); + float4 *colors = (float4 *)color_layer->data; + int offset = mesh_geometry_.vertex_index_min_ - block.start_vertex_index; + for (int i = 0, n = mesh_geometry_.get_vertex_count(); i != n; ++i) { + float3 c = block.colors[offset + i]; + colors[i] = float4(c.x, c.y, c.z, 1.0f); + } + return; + } } } -- cgit v1.2.3 From e2be6bc03fbab9783f117de95d560fbc09442de2 Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Mon, 1 Aug 2022 13:39:08 +0300 Subject: Fix T100076: OBJ import: new importer doesn't use //relative/image/paths The Python based importer had logic to immediately turn image paths into relative-to-blender-file paths, if user preference for relative paths is used (which is on by default). The new importer code did not have that. Fixes T100076. --- source/blender/io/wavefront_obj/importer/obj_import_mesh.cc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'source/blender/io/wavefront_obj/importer/obj_import_mesh.cc') diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index 01f05466b3a..6f66ce5a6bd 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -57,7 +57,7 @@ Object *MeshFromGeometry::create_mesh(Main *bmain, create_uv_verts(mesh); create_normals(mesh); create_colors(mesh); - create_materials(bmain, materials, created_materials, obj); + create_materials(bmain, materials, created_materials, obj, import_params.relative_paths); if (import_params.validate_meshes || mesh_geometry_.has_invalid_polys_) { bool verbose_validate = false; @@ -277,7 +277,8 @@ void MeshFromGeometry::create_uv_verts(Mesh *mesh) static Material *get_or_create_material(Main *bmain, const std::string &name, Map> &materials, - Map &created_materials) + Map &created_materials, + bool relative_paths) { /* Have we created this material already? */ Material **found_mat = created_materials.lookup_ptr(name); @@ -291,7 +292,7 @@ static Material *get_or_create_material(Main *bmain, const MTLMaterial &mtl = *materials.lookup_or_add(name, std::make_unique()); Material *mat = BKE_material_add(bmain, name.c_str()); - ShaderNodetreeWrap mat_wrap{bmain, mtl, mat}; + ShaderNodetreeWrap mat_wrap{bmain, mtl, mat, relative_paths}; mat->use_nodes = true; mat->nodetree = mat_wrap.get_nodetree(); BKE_ntree_update_main_tree(bmain, mat->nodetree, nullptr); @@ -303,10 +304,12 @@ static Material *get_or_create_material(Main *bmain, void MeshFromGeometry::create_materials(Main *bmain, Map> &materials, Map &created_materials, - Object *obj) + Object *obj, + bool relative_paths) { for (const std::string &name : mesh_geometry_.material_order_) { - Material *mat = get_or_create_material(bmain, name, materials, created_materials); + Material *mat = get_or_create_material( + bmain, name, materials, created_materials, relative_paths); if (mat == nullptr) { continue; } -- cgit v1.2.3 From 1cc11e32e4acefa095b5a851d49172c0972fc261 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 3 Aug 2022 16:59:03 -0500 Subject: Fix: Avoid OBJ importer assert seting normals on mesh with no faces --- source/blender/io/wavefront_obj/importer/obj_import_mesh.cc | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'source/blender/io/wavefront_obj/importer/obj_import_mesh.cc') diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index 6f66ce5a6bd..d7b2bc2e67c 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -323,6 +323,10 @@ void MeshFromGeometry::create_normals(Mesh *mesh) if (global_vertices_.vertex_normals.is_empty()) { return; } + /* Custom normals can only be stored on face corners. */ + if (mesh_geometry_.total_loops_ == 0) { + return; + } float(*loop_normals)[3] = static_cast( MEM_malloc_arrayN(mesh_geometry_.total_loops_, sizeof(float[3]), __func__)); -- cgit v1.2.3 From d76583cb4a16315c196f07f4acb9340f341bee47 Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Wed, 10 Aug 2022 13:34:42 +0300 Subject: Fix T100302: New OBJ importer produces too many vertices when faces don't span a continuous range As part of the previous fix (D15410), the importer got code to track min & max vertex indices used as part of the mesh faces. However, if faces refer to a "sparse" (i.e. non-contiguous) subset of all vertices, then the imported mesh would contain all the vertices between min & max range. Replace that with proper tracking of actually used vertex indices for each imported mesh. Fixes T100302. This does affect import performance a tiny bit, e.g. importing Blender 3.0 splash scene goes 21.7s -> 22.1s, and importing rungholt.obj goes 2.37s -> 2.48s. Importer related tests have a bunch of vertex changes in them, since now vertices are added in the order that the faces are referring to them. Which incidentally matches the order that the Python based importer was creating them too. --- .../blender/io/wavefront_obj/importer/obj_import_mesh.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'source/blender/io/wavefront_obj/importer/obj_import_mesh.cc') diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index d7b2bc2e67c..e62470588ec 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -157,17 +157,17 @@ void MeshFromGeometry::fixup_invalid_faces() void MeshFromGeometry::create_vertices(Mesh *mesh) { - const int tot_verts_object{mesh_geometry_.get_vertex_count()}; - for (int i = 0; i < tot_verts_object; ++i) { - int vi = mesh_geometry_.vertex_index_min_ + i; + int mi = 0; + for (int vi : mesh_geometry_.vertices_) { if (vi < global_vertices_.vertices.size()) { - copy_v3_v3(mesh->mvert[i].co, global_vertices_.vertices[vi]); + copy_v3_v3(mesh->mvert[mi].co, global_vertices_.vertices[vi]); } else { std::cerr << "Vertex index:" << vi << " larger than total vertices:" << global_vertices_.vertices.size() << " ." << std::endl; } + ++mi; } } @@ -208,7 +208,7 @@ void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) const PolyCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx]; MLoop &mloop = mesh->mloop[tot_loop_idx]; tot_loop_idx++; - mloop.v = curr_corner.vert_index - mesh_geometry_.vertex_index_min_; + mloop.v = mesh_geometry_.global_to_local_vertices_.lookup_default(curr_corner.vert_index, 0); /* Setup vertex group data, if needed. */ if (!mesh->dvert) { @@ -240,8 +240,8 @@ void MeshFromGeometry::create_edges(Mesh *mesh) for (int i = 0; i < tot_edges; ++i) { const MEdge &src_edge = mesh_geometry_.edges_[i]; MEdge &dst_edge = mesh->medge[i]; - dst_edge.v1 = src_edge.v1 - mesh_geometry_.vertex_index_min_; - dst_edge.v2 = src_edge.v2 - mesh_geometry_.vertex_index_min_; + dst_edge.v1 = mesh_geometry_.global_to_local_vertices_.lookup_default(src_edge.v1, 0); + dst_edge.v2 = mesh_geometry_.global_to_local_vertices_.lookup_default(src_edge.v2, 0); BLI_assert(dst_edge.v1 < total_verts && dst_edge.v2 < total_verts); dst_edge.flag = ME_LOOSEEDGE; } -- cgit v1.2.3 From efdcef7855e378bc3183ceab16e17f3dfd8e3c76 Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Tue, 16 Aug 2022 13:49:27 +0300 Subject: Fix T100421: OBJ importer in 3.3 does not keep the vertex order While fixing T100302 (rBd76583cb4a1) I did not realize that the change in imported vertex order would actually matter. Turns out, it does for morph targets / mesh shape keys. So redo the fix in a way that does not change the vertex order. Fixes T100421. --- .../io/wavefront_obj/importer/obj_import_mesh.cc | 24 +++++++++++++--------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'source/blender/io/wavefront_obj/importer/obj_import_mesh.cc') diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index e62470588ec..a570b374231 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -157,17 +157,21 @@ void MeshFromGeometry::fixup_invalid_faces() void MeshFromGeometry::create_vertices(Mesh *mesh) { - int mi = 0; - for (int vi : mesh_geometry_.vertices_) { - if (vi < global_vertices_.vertices.size()) { - copy_v3_v3(mesh->mvert[mi].co, global_vertices_.vertices[vi]); - } - else { - std::cerr << "Vertex index:" << vi - << " larger than total vertices:" << global_vertices_.vertices.size() << " ." - << std::endl; + /* Go through all the global vertex indices from min to max, + * checking which ones are actually and building a global->local + * index mapping. Write out the used vertex positions into the Mesh + * data. */ + mesh_geometry_.global_to_local_vertices_.clear(); + mesh_geometry_.global_to_local_vertices_.reserve(mesh_geometry_.vertices_.size()); + for (int vi = mesh_geometry_.vertex_index_min_; vi <= mesh_geometry_.vertex_index_max_; ++vi) { + BLI_assert(vi >= 0 && vi < global_vertices_.vertices.size()); + if (!mesh_geometry_.vertices_.contains(vi)) { + continue; } - ++mi; + int local_vi = (int)mesh_geometry_.global_to_local_vertices_.size(); + BLI_assert(local_vi >= 0 && local_vi < mesh->totvert); + copy_v3_v3(mesh->mvert[local_vi].co, global_vertices_.vertices[vi]); + mesh_geometry_.global_to_local_vertices_.add_new(vi, local_vi); } } -- cgit v1.2.3 From 25237d2625078c6d14d744f288776299efd3c7c8 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 30 Aug 2022 14:54:53 -0500 Subject: Attributes: Improve custom data initialization options When allocating new `CustomData` layers, often we do redundant initialization of arrays. For example, it's common that values are allocated, set to their default value, and then set to some other value. This is wasteful, and it negates the benefits of optimizations to the allocator like D15082. There are two reasons for this. The first is array-of-structs storage that makes it annoying to initialize values manually, and the second is confusing options in the Custom Data API. This patch addresses the latter. The `CustomData` "alloc type" options are rearranged. Now, besides the options that use existing layers, there are two remaining: * `CD_SET_DEFAULT` sets the default value. * Usually zeroes, but for colors this is white (how it was before). * Should be used when you add the layer but don't set all values. * `CD_CONSTRUCT` refers to the "default construct" C++ term. * Only necessary or defined for non-trivial types like vertex groups. * Doesn't do anything for trivial types like `int` or `float3`. * Should be used every other time, when all values will be set. The attribute API's `AttributeInit` types are updated as well. To update code, replace `CD_CALLOC` with `CD_SET_DEFAULT` and `CD_DEFAULT` with `CD_CONSTRUCT`. This doesn't cause any functional changes yet. Follow-up commits will change to avoid initializing new layers where the correctness is clear. Differential Revision: https://developer.blender.org/D15617 --- source/blender/io/wavefront_obj/importer/obj_import_mesh.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'source/blender/io/wavefront_obj/importer/obj_import_mesh.cc') diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index a570b374231..2a0676b72ff 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -181,7 +181,7 @@ void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) const int64_t total_verts = mesh_geometry_.get_vertex_count(); if (use_vertex_groups && total_verts && mesh_geometry_.has_vertex_groups_) { mesh->dvert = static_cast( - CustomData_add_layer(&mesh->vdata, CD_MDEFORMVERT, CD_CALLOC, nullptr, total_verts)); + CustomData_add_layer(&mesh->vdata, CD_MDEFORMVERT, CD_SET_DEFAULT, nullptr, total_verts)); } const int64_t tot_face_elems{mesh->totpoly}; @@ -262,7 +262,7 @@ void MeshFromGeometry::create_uv_verts(Mesh *mesh) return; } MLoopUV *mluv_dst = static_cast(CustomData_add_layer( - &mesh->ldata, CD_MLOOPUV, CD_DEFAULT, nullptr, mesh_geometry_.total_loops_)); + &mesh->ldata, CD_MLOOPUV, CD_SET_DEFAULT, nullptr, mesh_geometry_.total_loops_)); int tot_loop_idx = 0; for (const PolyElem &curr_face : mesh_geometry_.face_elements_) { -- cgit v1.2.3 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 --- .../blender/io/wavefront_obj/importer/obj_import_mesh.cc | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'source/blender/io/wavefront_obj/importer/obj_import_mesh.cc') diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index 2a0676b72ff..7f7fda8d8f1 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -8,7 +8,7 @@ #include "DNA_mesh_types.h" #include "DNA_scene_types.h" -#include "BKE_attribute.h" +#include "BKE_attribute.hh" #include "BKE_customdata.h" #include "BKE_deform.h" #include "BKE_material.h" @@ -184,6 +184,10 @@ void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) CustomData_add_layer(&mesh->vdata, CD_MDEFORMVERT, CD_SET_DEFAULT, nullptr, total_verts)); } + bke::SpanAttributeWriter material_indices = + bke::mesh_attributes_for_write(*mesh).lookup_or_add_for_write_only_span( + "material_index", ATTR_DOMAIN_FACE); + const int64_t tot_face_elems{mesh->totpoly}; int tot_loop_idx = 0; @@ -201,11 +205,11 @@ void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) if (curr_face.shaded_smooth) { mpoly.flag |= ME_SMOOTH; } - mpoly.mat_nr = curr_face.material_index; + material_indices.span[poly_idx] = curr_face.material_index; /* Importing obj files without any materials would result in negative indices, which is not * supported. */ - if (mpoly.mat_nr < 0) { - mpoly.mat_nr = 0; + if (material_indices.span[poly_idx] < 0) { + material_indices.span[poly_idx] = 0; } for (int idx = 0; idx < curr_face.corner_count_; ++idx) { @@ -223,6 +227,8 @@ void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) dw->weight = 1.0f; } } + + material_indices.finish(); } void MeshFromGeometry::create_vertex_groups(Object *obj) -- cgit v1.2.3 From f366d197db24cf611866a8d847b4022a6a46504e Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Thu, 1 Sep 2022 20:38:56 +0300 Subject: Fix T100737: OBJ/USD import: imported object has no active material, material has 2 users Fixes issues in importers written in C++ (T100737): - Materials had one reference count too much. Affected Collada, Alembic, USD, OBJ importers, looks like "since forever". - Active material index was not properly set on imported meshes. Regression since 3.3 (D15145). Affected Alembic, USD, OBJ. Note: now it sets the first material as the active one, whereas previously the last one was set as active. First one sounds more "intuitive" to me. Reviewed By: Bastien Montagne Differential Revision: https://developer.blender.org/D15831 --- source/blender/io/wavefront_obj/importer/obj_import_mesh.cc | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'source/blender/io/wavefront_obj/importer/obj_import_mesh.cc') diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index a570b374231..6b19d6573af 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -296,6 +296,8 @@ static Material *get_or_create_material(Main *bmain, const MTLMaterial &mtl = *materials.lookup_or_add(name, std::make_unique()); Material *mat = BKE_material_add(bmain, name.c_str()); + id_us_min(&mat->id); + ShaderNodetreeWrap mat_wrap{bmain, mtl, mat, relative_paths}; mat->use_nodes = true; mat->nodetree = mat_wrap.get_nodetree(); @@ -319,6 +321,9 @@ void MeshFromGeometry::create_materials(Main *bmain, } BKE_object_material_assign_single_obdata(bmain, obj, mat, obj->totcol + 1); } + if (obj->totcol > 0) { + obj->actcol = 1; + } } void MeshFromGeometry::create_normals(Mesh *mesh) -- cgit v1.2.3 From fa40013009463703a22a8ae05d116723084966b7 Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Thu, 1 Sep 2022 21:19:31 +0300 Subject: Cleanup: obj: simplify material node tree creation As pointed out in D15827 comment, the unique_ptr usage in ShaderNodetreeWrap related code does not sound very useful. Looking at it, whole ShaderNodetreeWrap does not make much sense - it's only ever created, and then immediately just one thing is fetched from it. This very much sounds like "a function", so make it just that - header file contains just a `create_mtl_node_tree` function, and the whole implementation is hidden from the users. Which I've also simplified into just a handful of freestanding functions. No functionality or performance changes, but the code does get ~80 lines shorter. --- source/blender/io/wavefront_obj/importer/obj_import_mesh.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'source/blender/io/wavefront_obj/importer/obj_import_mesh.cc') diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index 40a958919f1..e01b64d7885 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -22,6 +22,7 @@ #include "IO_wavefront_obj.h" #include "importer_mesh_utils.hh" +#include "obj_export_mtl.hh" #include "obj_import_mesh.hh" namespace blender::io::obj { @@ -304,9 +305,8 @@ static Material *get_or_create_material(Main *bmain, Material *mat = BKE_material_add(bmain, name.c_str()); id_us_min(&mat->id); - ShaderNodetreeWrap mat_wrap{bmain, mtl, mat, relative_paths}; mat->use_nodes = true; - mat->nodetree = mat_wrap.get_nodetree(); + mat->nodetree = create_mtl_node_tree(bmain, mtl, mat, relative_paths); BKE_ntree_update_main_tree(bmain, mat->nodetree, nullptr); created_materials.add_new(name, mat); -- cgit v1.2.3 From 05952aa94d33eeb504fa63618ba35c2bcc8bd19b Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 5 Sep 2022 11:56:34 -0500 Subject: Mesh: Remove redundant custom data pointers For copy-on-write, we want to share attribute arrays between meshes where possible. Mutable pointers like `Mesh.mvert` make that difficult by making ownership vague. They also make code more complex by adding redundancy. The simplest solution is just removing them and retrieving layers from `CustomData` as needed. Similar changes have already been applied to curves and point clouds (e9f82d3dc7ee, 410a6efb747f). Removing use of the pointers generally makes code more obvious and more reusable. Mesh data is now accessed with a C++ API (`Mesh::edges()` or `Mesh::edges_for_write()`), and a C API (`BKE_mesh_edges(mesh)`). The CoW changes this commit makes possible are described in T95845 and T95842, and started in D14139 and D14140. The change also simplifies the ongoing mesh struct-of-array refactors from T95965. **RNA/Python Access Performance** Theoretically, accessing mesh elements with the RNA API may become slower, since the layer needs to be found on every random access. However, overhead is already high enough that this doesn't make a noticible differenc, and performance is actually improved in some cases. Random access can be up to 10% faster, but other situations might be a bit slower. Generally using `foreach_get/set` are the best way to improve performance. See the differential revision for more discussion about Python performance. Cycles has been updated to use raw pointers and the internal Blender mesh types, mostly because there is no sense in having this overhead when it's already compiled with Blender. In my tests this roughly halves the Cycles mesh creation time (0.19s to 0.10s for a 1 million face grid). Differential Revision: https://developer.blender.org/D15488 --- .../io/wavefront_obj/importer/obj_import_mesh.cc | 24 +++++++++++++--------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'source/blender/io/wavefront_obj/importer/obj_import_mesh.cc') diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index e01b64d7885..4b4609c053b 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -158,6 +158,7 @@ void MeshFromGeometry::fixup_invalid_faces() void MeshFromGeometry::create_vertices(Mesh *mesh) { + MutableSpan verts = mesh->vertices_for_write(); /* Go through all the global vertex indices from min to max, * checking which ones are actually and building a global->local * index mapping. Write out the used vertex positions into the Mesh @@ -171,20 +172,21 @@ void MeshFromGeometry::create_vertices(Mesh *mesh) } int local_vi = (int)mesh_geometry_.global_to_local_vertices_.size(); BLI_assert(local_vi >= 0 && local_vi < mesh->totvert); - copy_v3_v3(mesh->mvert[local_vi].co, global_vertices_.vertices[vi]); + copy_v3_v3(verts[local_vi].co, global_vertices_.vertices[vi]); mesh_geometry_.global_to_local_vertices_.add_new(vi, local_vi); } } void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) { - mesh->dvert = nullptr; + MutableSpan dverts; const int64_t total_verts = mesh_geometry_.get_vertex_count(); if (use_vertex_groups && total_verts && mesh_geometry_.has_vertex_groups_) { - mesh->dvert = static_cast( - CustomData_add_layer(&mesh->vdata, CD_MDEFORMVERT, CD_SET_DEFAULT, nullptr, total_verts)); + dverts = mesh->deform_verts_for_write(); } + MutableSpan polys = mesh->polygons_for_write(); + MutableSpan loops = mesh->loops_for_write(); bke::SpanAttributeWriter material_indices = bke::mesh_attributes_for_write(*mesh).lookup_or_add_for_write_only_span( "material_index", ATTR_DOMAIN_FACE); @@ -200,7 +202,7 @@ void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) continue; } - MPoly &mpoly = mesh->mpoly[poly_idx]; + MPoly &mpoly = polys[poly_idx]; mpoly.totloop = curr_face.corner_count_; mpoly.loopstart = tot_loop_idx; if (curr_face.shaded_smooth) { @@ -215,16 +217,16 @@ void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) for (int idx = 0; idx < curr_face.corner_count_; ++idx) { const PolyCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx]; - MLoop &mloop = mesh->mloop[tot_loop_idx]; + MLoop &mloop = loops[tot_loop_idx]; tot_loop_idx++; mloop.v = mesh_geometry_.global_to_local_vertices_.lookup_default(curr_corner.vert_index, 0); /* Setup vertex group data, if needed. */ - if (!mesh->dvert) { + if (dverts.is_empty()) { continue; } const int group_index = curr_face.vertex_group_index; - MDeformWeight *dw = BKE_defvert_ensure_index(mesh->dvert + mloop.v, group_index); + MDeformWeight *dw = BKE_defvert_ensure_index(&dverts[mloop.v], group_index); dw->weight = 1.0f; } } @@ -235,7 +237,7 @@ void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) void MeshFromGeometry::create_vertex_groups(Object *obj) { Mesh *mesh = static_cast(obj->data); - if (mesh->dvert == nullptr) { + if (mesh->deform_verts().is_empty()) { return; } for (const std::string &name : mesh_geometry_.group_order_) { @@ -245,12 +247,14 @@ void MeshFromGeometry::create_vertex_groups(Object *obj) void MeshFromGeometry::create_edges(Mesh *mesh) { + MutableSpan edges = mesh->edges_for_write(); + const int64_t tot_edges{mesh_geometry_.edges_.size()}; const int64_t total_verts{mesh_geometry_.get_vertex_count()}; UNUSED_VARS_NDEBUG(total_verts); for (int i = 0; i < tot_edges; ++i) { const MEdge &src_edge = mesh_geometry_.edges_[i]; - MEdge &dst_edge = mesh->medge[i]; + MEdge &dst_edge = edges[i]; dst_edge.v1 = mesh_geometry_.global_to_local_vertices_.lookup_default(src_edge.v1, 0); dst_edge.v2 = mesh_geometry_.global_to_local_vertices_.lookup_default(src_edge.v2, 0); BLI_assert(dst_edge.v1 < total_verts && dst_edge.v2 < total_verts); -- cgit v1.2.3 From be038b844cb53bc228d3e98bfe09071560930cde Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 7 Sep 2022 00:06:31 -0500 Subject: Cleanup: Tweak naming for recently added mesh accessors Use `verts` instead of `vertices` and `polys` instead of `polygons` in the API added in 05952aa94d33eeb50. This aligns better with existing naming where the shorter names are much more common. --- source/blender/io/wavefront_obj/importer/obj_import_mesh.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'source/blender/io/wavefront_obj/importer/obj_import_mesh.cc') diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index 4b4609c053b..84f1c6dd6b0 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -158,7 +158,7 @@ void MeshFromGeometry::fixup_invalid_faces() void MeshFromGeometry::create_vertices(Mesh *mesh) { - MutableSpan verts = mesh->vertices_for_write(); + MutableSpan verts = mesh->verts_for_write(); /* Go through all the global vertex indices from min to max, * checking which ones are actually and building a global->local * index mapping. Write out the used vertex positions into the Mesh @@ -185,7 +185,7 @@ void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) dverts = mesh->deform_verts_for_write(); } - MutableSpan polys = mesh->polygons_for_write(); + MutableSpan polys = mesh->polys_for_write(); MutableSpan loops = mesh->loops_for_write(); bke::SpanAttributeWriter material_indices = bke::mesh_attributes_for_write(*mesh).lookup_or_add_for_write_only_span( -- cgit v1.2.3 From d5934974219135102f364f57c45a8b1465e2b8d9 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 7 Sep 2022 21:41:39 -0500 Subject: Cleanup: Use C++ methods to retrieve attribute accessors Replace `mesh_attributes`, `mesh_attributes_for_write` and the point cloud versions with methods on the `Mesh` and `PointCloud` types. This makes them friendlier to use and improves readability. Differential Revision: https://developer.blender.org/D15907 --- source/blender/io/wavefront_obj/importer/obj_import_mesh.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'source/blender/io/wavefront_obj/importer/obj_import_mesh.cc') diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index 84f1c6dd6b0..b1a2c7834f4 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -188,8 +188,8 @@ void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) MutableSpan polys = mesh->polys_for_write(); MutableSpan loops = mesh->loops_for_write(); bke::SpanAttributeWriter material_indices = - bke::mesh_attributes_for_write(*mesh).lookup_or_add_for_write_only_span( - "material_index", ATTR_DOMAIN_FACE); + mesh->attributes_for_write().lookup_or_add_for_write_only_span("material_index", + ATTR_DOMAIN_FACE); const int64_t tot_face_elems{mesh->totpoly}; int tot_loop_idx = 0; -- cgit v1.2.3