From eabff374835686f0cd6f2fffd29972153706c0f7 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 3 Aug 2023 16:13:04 +0200 Subject: [PATCH] Mesh: Add API functions to assign vertex normals A few places already calculated vertex normals with better contextual information to improve performance. To allow changing the storage of vertex normals more (mostly for improved caching), change some code from "get normals, set values, clear dirty flag" to "make vertex normals data, give to mesh". This makes the API less awkward too, since previously the "get for write" and "clear dirty" calls always had to be separated. Pull Request: https://projects.blender.org/blender/blender/pulls/110754 --- source/blender/blenkernel/BKE_mesh.h | 19 ------------------- source/blender/blenkernel/BKE_mesh.hh | 6 ++++++ .../blenkernel/intern/mball_tessellate.cc | 5 +---- .../blender/blenkernel/intern/mesh_normals.cc | 17 +++++++++++------ .../io/alembic/intern/abc_reader_mesh.cc | 4 ++-- .../blender/io/usd/intern/usd_reader_mesh.cc | 5 ++--- source/blender/modifiers/intern/MOD_array.cc | 18 +++++++++--------- .../node_geo_mesh_primitive_uv_sphere.cc | 5 ++--- 8 files changed, 33 insertions(+), 46 deletions(-) diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 8a83ce58eb4..fceaddf82a3 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -321,25 +321,6 @@ void BKE_mesh_recalc_looptri(const int *corner_verts, */ const float (*BKE_mesh_vert_normals_ensure(const struct Mesh *mesh))[3]; -/** - * Retrieve write access to the cached vertex normals, ensuring that they are allocated but *not* - * that they are calculated. The provided vertex normals should be the same as if they were - * calculated automatically. - * - * \note In order to clear the dirty flag, this function should be followed by a call to - * #BKE_mesh_vert_normals_clear_dirty. This is separate so that normals are still tagged dirty - * while they are being assigned. - * - * \warning The memory returned by this function is not initialized if it was not previously - * allocated. - */ -float (*BKE_mesh_vert_normals_for_write(struct Mesh *mesh))[3]; - -/** - * Mark the mesh's vertex normals non-dirty, for when they are calculated or assigned manually. - */ -void BKE_mesh_vert_normals_clear_dirty(struct Mesh *mesh); - /** * Return true if the mesh vertex normals either are not stored or are dirty. * This can be used to help decide whether to transfer them when copying a mesh. diff --git a/source/blender/blenkernel/BKE_mesh.hh b/source/blender/blenkernel/BKE_mesh.hh index d835d8e575f..384f6155a9a 100644 --- a/source/blender/blenkernel/BKE_mesh.hh +++ b/source/blender/blenkernel/BKE_mesh.hh @@ -277,6 +277,12 @@ inline int edge_other_vert(const int2 &edge, const int vert) void mesh_flip_faces(Mesh &mesh, const IndexMask &selection); +/** Set mesh vertex normals to known-correct values, avoiding future lazy computation. */ +void mesh_vert_normals_assign(Mesh &mesh, Span vert_normals); + +/** Set mesh vertex normals to known-correct values, avoiding future lazy computation. */ +void mesh_vert_normals_assign(Mesh &mesh, Vector vert_normals); + } // namespace blender::bke /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/mball_tessellate.cc b/source/blender/blenkernel/intern/mball_tessellate.cc index 0f8edf38a4a..8be457f3a6e 100644 --- a/source/blender/blenkernel/intern/mball_tessellate.cc +++ b/source/blender/blenkernel/intern/mball_tessellate.cc @@ -1488,10 +1488,7 @@ Mesh *BKE_mball_polygonize(Depsgraph *depsgraph, Scene *scene, Object *ob) for (int i = 0; i < mesh->totvert; i++) { normalize_v3(process.no[i]); } - memcpy(BKE_mesh_vert_normals_for_write(mesh), - process.no.data(), - sizeof(float[3]) * size_t(mesh->totvert)); - BKE_mesh_vert_normals_clear_dirty(mesh); + blender::bke::mesh_vert_normals_assign(*mesh, std::move(process.no)); BKE_mesh_calc_edges(mesh, false, false); diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index 2f243abfae6..0e6c2dd5dc7 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -89,18 +89,23 @@ static void add_v3_v3_atomic(float r[3], const float a[3]) * Related to managing normals but not directly related to calculating normals. * \{ */ -float (*BKE_mesh_vert_normals_for_write(Mesh *mesh))[3] +namespace blender::bke { + +void mesh_vert_normals_assign(Mesh &mesh, Span vert_normals) { - mesh->runtime->vert_normals.reinitialize(mesh->totvert); - return reinterpret_cast(mesh->runtime->vert_normals.data()); + mesh.runtime->vert_normals.clear(); + mesh.runtime->vert_normals.extend(vert_normals); + mesh.runtime->vert_normals_dirty = false; } -void BKE_mesh_vert_normals_clear_dirty(Mesh *mesh) +void mesh_vert_normals_assign(Mesh &mesh, Vector vert_normals) { - mesh->runtime->vert_normals_dirty = false; - BLI_assert(mesh->runtime->vert_normals.size() == mesh->totvert); + mesh.runtime->vert_normals = std::move(vert_normals); + mesh.runtime->vert_normals_dirty = false; } +} // namespace blender::bke + bool BKE_mesh_vert_normals_are_dirty(const Mesh *mesh) { return mesh->runtime->vert_normals_dirty; diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index 2a04d473dbd..b651234b8f2 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -174,12 +174,12 @@ void read_mverts(Mesh &mesh, const P3fArraySamplePtr positions, const N3fArraySa BKE_mesh_tag_positions_changed(&mesh); if (normals) { - float(*vert_normals)[3] = BKE_mesh_vert_normals_for_write(&mesh); + Vector vert_normals(mesh.totvert); for (const int64_t i : IndexRange(normals->size())) { Imath::V3f nor_in = (*normals)[i]; copy_zup_from_yup(vert_normals[i], nor_in.getValue()); } - BKE_mesh_vert_normals_clear_dirty(&mesh); + bke::mesh_vert_normals_assign(mesh, std::move(vert_normals)); } } diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 71343eab24a..20012fffe44 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -656,10 +656,9 @@ void USDMeshReader::process_normals_vertex_varying(Mesh *mesh) return; } - MutableSpan vert_normals{(float3 *)BKE_mesh_vert_normals_for_write(mesh), mesh->totvert}; BLI_STATIC_ASSERT(sizeof(normals_[0]) == sizeof(float3), "Expected float3 normals size"); - vert_normals.copy_from({(float3 *)normals_.data(), int64_t(normals_.size())}); - BKE_mesh_vert_normals_clear_dirty(mesh); + bke::mesh_vert_normals_assign( + *mesh, Span(reinterpret_cast(normals_.data()), int64_t(normals_.size()))); } void USDMeshReader::process_normals_face_varying(Mesh *mesh) diff --git a/source/blender/modifiers/intern/MOD_array.cc b/source/blender/modifiers/intern/MOD_array.cc index 9f3bcab82af..daa15880d55 100644 --- a/source/blender/modifiers/intern/MOD_array.cc +++ b/source/blender/modifiers/intern/MOD_array.cc @@ -282,7 +282,7 @@ static void mesh_merge_transform(Mesh *result, int cap_nfaces, int *remap, int remap_len, - const bool recalc_normals_later) + MutableSpan dst_vert_normals) { using namespace blender; int *index_orig; @@ -305,8 +305,7 @@ static void mesh_merge_transform(Mesh *result, } /* We have to correct normals too, if we do not tag them as dirty later! */ - if (!recalc_normals_later) { - float(*dst_vert_normals)[3] = BKE_mesh_vert_normals_for_write(result); + if (!dst_vert_normals.is_empty()) { for (i = 0; i < cap_nverts; i++) { mul_mat3_m4_v3(cap_offset, dst_vert_normals[cap_verts_index + i]); normalize_v3(dst_vert_normals[cap_verts_index + i]); @@ -583,11 +582,10 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd, unit_m4(current_offset); blender::Span src_vert_normals; - float(*dst_vert_normals)[3] = nullptr; + Vector dst_vert_normals; if (!use_recalc_normals) { src_vert_normals = mesh->vert_normals(); - dst_vert_normals = BKE_mesh_vert_normals_for_write(result); - BKE_mesh_vert_normals_clear_dirty(result); + dst_vert_normals.reinitialize(result->totvert); } for (c = 1; c < count; c++) { @@ -608,7 +606,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd, mul_m4_v3(current_offset, result_positions[i_dst]); /* We have to correct normals too, if we do not tag them as dirty! */ - if (!use_recalc_normals) { + if (!dst_vert_normals.is_empty()) { copy_v3_v3(dst_vert_normals[i_dst], src_vert_normals[i]); mul_mat3_m4_v3(current_offset, dst_vert_normals[i_dst]); normalize_v3(dst_vert_normals[i_dst]); @@ -758,7 +756,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd, start_cap_nfaces, vgroup_start_cap_remap, vgroup_start_cap_remap_len, - use_recalc_normals); + dst_vert_normals); /* Identify doubles with first chunk */ if (use_merge) { dm_mvert_map_doubles(full_doubles_map, @@ -788,7 +786,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd, end_cap_nfaces, vgroup_end_cap_remap, vgroup_end_cap_remap_len, - use_recalc_normals); + dst_vert_normals); /* Identify doubles with last chunk */ if (use_merge) { dm_mvert_map_doubles(full_doubles_map, @@ -802,6 +800,8 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd, } /* done capping */ + blender::bke::mesh_vert_normals_assign(*result, std::move(dst_vert_normals)); + /* Handle merging */ tot_doubles = 0; if (use_merge) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index be80c10756e..7705061cc7d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -318,10 +318,9 @@ static Mesh *create_uv_sphere_mesh(const float radius, threading::parallel_invoke( 1024 < segments * rings, [&]() { - MutableSpan vert_normals{reinterpret_cast(BKE_mesh_vert_normals_for_write(mesh)), - mesh->totvert}; + Vector vert_normals(mesh->totvert); calculate_sphere_vertex_data(positions, vert_normals, radius, segments, rings); - BKE_mesh_vert_normals_clear_dirty(mesh); + bke::mesh_vert_normals_assign(*mesh, std::move(vert_normals)); }, [&]() { calculate_sphere_edge_indices(edges, segments, rings); }, [&]() { calculate_sphere_faces(face_offsets, segments); },