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
This commit is contained in:
Hans Goudey
2023-08-03 16:13:04 +02:00
committed by Hans Goudey
parent 8e33b20de9
commit eabff37483
8 changed files with 33 additions and 46 deletions

View File

@@ -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.

View File

@@ -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<float3> vert_normals);
/** Set mesh vertex normals to known-correct values, avoiding future lazy computation. */
void mesh_vert_normals_assign(Mesh &mesh, Vector<float3> vert_normals);
} // namespace blender::bke
/* -------------------------------------------------------------------- */

View File

@@ -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);

View File

@@ -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<float3> vert_normals)
{
mesh->runtime->vert_normals.reinitialize(mesh->totvert);
return reinterpret_cast<float(*)[3]>(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<float3> 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;

View File

@@ -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<float3> 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));
}
}

View File

@@ -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<const float3 *>(normals_.data()), int64_t(normals_.size())));
}
void USDMeshReader::process_normals_face_varying(Mesh *mesh)

View File

@@ -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<float3> 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<blender::float3> src_vert_normals;
float(*dst_vert_normals)[3] = nullptr;
Vector<float3> 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) {

View File

@@ -318,10 +318,9 @@ static Mesh *create_uv_sphere_mesh(const float radius,
threading::parallel_invoke(
1024 < segments * rings,
[&]() {
MutableSpan vert_normals{reinterpret_cast<float3 *>(BKE_mesh_vert_normals_for_write(mesh)),
mesh->totvert};
Vector<float3> 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); },