WIP: Mesh: Refactor vertex normals for determinism #105920

Closed
Hans Goudey wants to merge 6 commits from HooglyBoogly:mesh-normals-calc-changes into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
6 changed files with 113 additions and 1 deletions
Showing only changes of commit c92f9dc137 - Show all commits

View File

@ -6,6 +6,8 @@
* \ingroup bke
*/
#include "BLI_offset_indices.hh"
#include "BKE_mesh.h"
namespace blender::bke::mesh {
@ -223,6 +225,47 @@ void mesh_vert_normals_assign(Mesh &mesh, Vector<float3> vert_normals);
} // namespace blender::bke::mesh
/* -------------------------------------------------------------------- */
/** \name Mesh Topology Caches
* \{ */
namespace blender::bke::mesh {
class VertToPolyMap {
OffsetIndices<int> offsets_;
Span<int> indices_;
public:
VertToPolyMap() = default;
VertToPolyMap(OffsetIndices<int> offsets, Span<int> indices)
: offsets_(offsets), indices_(indices)
{
}
/* Indices of all faces using the indexed vertex. */
Span<int> operator[](const int64_t vert_index) const
{
return indices_.slice(offsets_[vert_index]);
}
};
/**
* Build offsets per vertex used to slice arrays containing the indices of connected
* faces or face corners (each vertex used by the same number of corners and faces).
*/
void build_poly_and_corner_by_vert_offsets(Span<int> corner_verts, MutableSpan<int> offsets);
/**
* Fill the indices of polygons connected to each vertex, ordered smallest index to largest.
* \param offsets: Encodes the number of polygons connected to each vertex.
*/
void build_vert_to_poly_indices(OffsetIndices<int> polys,
Span<int> corner_verts,
OffsetIndices<int> offsets,
MutableSpan<int> poly_indices);
} // namespace blender::bke::mesh
/** \} */
/* -------------------------------------------------------------------- */
/** \name Inline Mesh Data Access
* \{ */

View File

@ -165,6 +165,13 @@ struct MeshRuntime {
SharedCache<Vector<float3>> poly_normals_cache;
/** Cache of data about edges not used by faces. See #Mesh::loose_edges(). */
SharedCache<Vector<int>> vert_to_corner_offset_cache;
SharedCache<Vector<int>> vert_to_poly_indices_cache;
/**
* A cache of data about the loose edges. Can be shared with other data-blocks with unchanged
* topology. Accessed with #Mesh::loose_edges().
*/
SharedCache<LooseEdgeCache> loose_edges_cache;
/** Cache of data about vertices not used by edges. See #Mesh::loose_verts(). */
SharedCache<LooseVertCache> loose_verts_cache;

View File

@ -136,6 +136,8 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
mesh_dst->runtime->looptris_cache = mesh_src->runtime->looptris_cache;
mesh_dst->runtime->vert_normals_cache = mesh_src->runtime->vert_normals_cache;
mesh_dst->runtime->poly_normals_cache = mesh_src->runtime->poly_normals_cache;
mesh_dst->runtime->vert_to_corner_offset_cache = mesh_src->runtime->vert_to_corner_offset_cache;
mesh_dst->runtime->vert_to_poly_indices_cache = mesh_src->runtime->vert_to_poly_indices_cache;
/* Only do tessface if we have no polys. */
const bool do_tessface = ((mesh_src->totface != 0) && (mesh_src->totpoly == 0));

View File

@ -21,7 +21,7 @@
#include "BLI_utildefines.h"
#include "BKE_customdata.h"
#include "BKE_mesh.h"
#include "BKE_mesh.hh"
#include "BKE_mesh_mapping.h"
#include "BLI_memarena.h"
@ -521,6 +521,34 @@ void BKE_mesh_origindex_map_create_looptri(MeshElemMap **r_map,
*r_mem = indices;
}
namespace blender::bke::mesh {
void build_poly_and_corner_by_vert_offsets(const Span<int> corner_verts, MutableSpan<int> offsets)
{
BLI_assert(std::all_of(offsets.begin(), offsets.end(), [](int value) { return value == 0; }));
for (const int vert : corner_verts) {
offsets[vert]++;
}
offset_indices::accumulate_counts_to_offsets(offsets);
}
void build_vert_to_poly_indices(const OffsetIndices<int> polys,
const Span<int> corner_verts,
const OffsetIndices<int> offsets,
MutableSpan<int> poly_indices)
{
BLI_assert(poly_indices.size() == corner_verts.size());
Array<int> counts(offsets.size(), 0);
for (const int64_t i : polys.index_range()) {
for (const int vert : corner_verts.slice(polys[i])) {
poly_indices[offsets[vert][counts[vert]]] = int(i);
counts[vert]++;
}
}
}
} // namespace blender::bke::mesh
namespace blender::bke::mesh_topology {
Array<int> build_loop_to_poly_map(const OffsetIndices<int> polys)

View File

@ -142,6 +142,26 @@ static void try_tag_verts_no_face_none(const Mesh &mesh)
} // namespace blender::bke
blender::bke::mesh::VertToPolyMap Mesh::vert_to_poly_map() const
{
using namespace blender;
this->runtime->vert_to_corner_offset_cache.ensure([&](Vector<int> &r_data) {
r_data.clear();
r_data.resize(this->totvert + 1, 0);
bke::mesh::build_poly_and_corner_by_vert_offsets(this->corner_verts(), r_data);
});
const OffsetIndices<int> offsets(this->runtime->vert_to_corner_offset_cache.data());
this->runtime->vert_to_poly_indices_cache.ensure([&](blender::Vector<int> &r_data) {
r_data.reinitialize(this->totloop);
blender::bke::mesh::build_vert_to_poly_indices(
this->polys(), this->corner_verts(), offsets, r_data);
});
const Span<int> indices = this->runtime->vert_to_poly_indices_cache.data();
return {offsets, indices};
}
const blender::bke::LooseVertCache &Mesh::loose_verts() const
{
using namespace blender::bke;
@ -270,6 +290,8 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh)
mesh->runtime->vert_normals_cache.tag_dirty();
mesh->runtime->poly_normals_cache.tag_dirty();
mesh->runtime->bounds_cache.tag_dirty();
mesh->runtime->vert_to_corner_offset_cache.tag_dirty();
mesh->runtime->vert_to_poly_indices_cache.tag_dirty();
mesh->runtime->loose_edges_cache.tag_dirty();
mesh->runtime->loose_verts_cache.tag_dirty();
mesh->runtime->verts_no_face_cache.tag_dirty();
@ -288,6 +310,8 @@ void BKE_mesh_tag_edges_split(struct Mesh *mesh)
free_bvh_cache(*mesh->runtime);
free_subdiv_ccg(*mesh->runtime);
mesh->runtime->vert_normals_cache.tag_dirty();
mesh->runtime->vert_to_corner_offset_cache.tag_dirty();
mesh->runtime->vert_to_poly_indices_cache.tag_dirty();
mesh->runtime->loose_edges_cache.tag_dirty();
mesh->runtime->loose_verts_cache.tag_dirty();
mesh->runtime->verts_no_face_cache.tag_dirty();

View File

@ -29,6 +29,9 @@ class AttributeAccessor;
class MutableAttributeAccessor;
struct LooseVertCache;
struct LooseEdgeCache;
namespace mesh {
class VertToPolyMap;
}
} // namespace bke
} // namespace blender
using MeshRuntimeHandle = blender::bke::MeshRuntime;
@ -299,6 +302,11 @@ typedef struct Mesh {
/** Set cached mesh bounds to a known-correct value to avoid their lazy calculation later on. */
void bounds_set_eager(const blender::Bounds<blender::float3> &bounds);
/**
* A cached topology map of the faces connected to (using) each vertex.
*/
blender::bke::mesh::VertToPolyMap vert_to_poly_map() const;
/**
* Cached information about loose edges, calculated lazily when necessary.
*/