WIP: Mesh: Refactor vertex normals for determinism #105920
|
@ -341,11 +341,6 @@ const float (*BKE_mesh_poly_normals_ensure(const struct Mesh *mesh))[3];
|
|||
*/
|
||||
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.
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include "BLI_offset_indices.hh"
|
||||
|
||||
#include "BKE_mesh.h"
|
||||
|
||||
namespace blender::bke::mesh {
|
||||
|
@ -66,16 +68,17 @@ void normals_calc_polys(Span<float3> vert_positions,
|
|||
MutableSpan<float3> poly_normals);
|
||||
|
||||
/**
|
||||
* Calculate face and vertex normals directly into result arrays.
|
||||
* Calculate vertex normals directly into result array.
|
||||
*
|
||||
* \note Usually #Mesh::vert_normals() is the preferred way to access vertex normals,
|
||||
* since they may already be calculated and cached on the mesh.
|
||||
*/
|
||||
void normals_calc_poly_vert(Span<float3> vert_positions,
|
||||
OffsetIndices<int> polys,
|
||||
Span<int> corner_verts,
|
||||
MutableSpan<float3> poly_normals,
|
||||
MutableSpan<float3> vert_normals);
|
||||
void normals_calc_verts(Span<float3> positions,
|
||||
OffsetIndices<int> polys,
|
||||
Span<int> corner_verts,
|
||||
const VertToPolyMap &vert_to_poly,
|
||||
Span<float3> poly_normals,
|
||||
MutableSpan<float3> vert_normals);
|
||||
|
||||
/**
|
||||
* Compute split normals, i.e. vertex normals associated with each poly (hence 'loop normals').
|
||||
|
@ -208,10 +211,62 @@ inline int edge_other_vert(const int2 &edge, const int vert)
|
|||
return -1;
|
||||
}
|
||||
|
||||
} // namespace blender::bke::mesh
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
/** 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
|
||||
|
||||
/** \} */
|
||||
|
||||
} // 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
|
||||
* \{ */
|
||||
|
|
|
@ -158,17 +158,20 @@ struct MeshRuntime {
|
|||
*/
|
||||
SubsurfRuntimeData *subsurf_runtime_data = nullptr;
|
||||
|
||||
/**
|
||||
* Caches for lazily computed vertex and polygon normals. These are stored here rather than in
|
||||
* #CustomData because they can be calculated on a `const` mesh, and adding custom data layers on
|
||||
* a `const` mesh is not thread-safe.
|
||||
*/
|
||||
bool vert_normals_dirty = true;
|
||||
bool poly_normals_dirty = true;
|
||||
mutable Vector<float3> vert_normals;
|
||||
mutable Vector<float3> poly_normals;
|
||||
/** Cache of lazily calculated vertex normals. Depends on #poly_normals_cache. */
|
||||
SharedCache<Vector<float3>> vert_normals_cache;
|
||||
|
||||
/** Cache of lazily calculated face normals. Depends on positions and topology. */
|
||||
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;
|
||||
|
|
|
@ -2266,11 +2266,12 @@ void BKE_keyblock_mesh_calc_normals(const KeyBlock *kb,
|
|||
{reinterpret_cast<blender::float3 *>(poly_normals), polys.size()});
|
||||
}
|
||||
if (vert_normals_needed) {
|
||||
blender::bke::mesh::normals_calc_poly_vert(
|
||||
blender::bke::mesh::normals_calc_verts(
|
||||
{reinterpret_cast<const blender::float3 *>(positions), mesh->totvert},
|
||||
polys,
|
||||
corner_verts,
|
||||
{reinterpret_cast<blender::float3 *>(poly_normals), polys.size()},
|
||||
mesh->vert_to_poly_map(),
|
||||
{reinterpret_cast<const blender::float3 *>(poly_normals), polys.size()},
|
||||
{reinterpret_cast<blender::float3 *>(vert_normals), mesh->totvert});
|
||||
}
|
||||
if (loop_normals_needed) {
|
||||
|
|
|
@ -1484,10 +1484,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);
|
||||
|
||||
|
|
|
@ -134,6 +134,10 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
|
|||
mesh_dst->runtime->verts_no_face_cache = mesh_src->runtime->verts_no_face_cache;
|
||||
mesh_dst->runtime->loose_edges_cache = mesh_src->runtime->loose_edges_cache;
|
||||
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));
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -42,72 +42,53 @@
|
|||
# include "BLI_timeit.hh"
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Private Utility Functions
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* A thread-safe version of #add_v3_v3 that uses a spin-lock.
|
||||
*
|
||||
* \note Avoid using this when the chance of contention is high.
|
||||
*/
|
||||
static void add_v3_v3_atomic(float r[3], const float a[3])
|
||||
{
|
||||
#define FLT_EQ_NONAN(_fa, _fb) (*((const uint32_t *)&_fa) == *((const uint32_t *)&_fb))
|
||||
|
||||
float virtual_lock = r[0];
|
||||
while (true) {
|
||||
/* This loops until following conditions are met:
|
||||
* - `r[0]` has same value as virtual_lock (i.e. it did not change since last try).
|
||||
* - `r[0]` was not `FLT_MAX`, i.e. it was not locked by another thread. */
|
||||
const float test_lock = atomic_cas_float(&r[0], virtual_lock, FLT_MAX);
|
||||
if (_ATOMIC_LIKELY(FLT_EQ_NONAN(test_lock, virtual_lock) && (test_lock != FLT_MAX))) {
|
||||
break;
|
||||
}
|
||||
virtual_lock = test_lock;
|
||||
}
|
||||
virtual_lock += a[0];
|
||||
r[1] += a[1];
|
||||
r[2] += a[2];
|
||||
|
||||
/* Second atomic operation to 'release'
|
||||
* our lock on that vector and set its first scalar value. */
|
||||
/* Note that we do not need to loop here, since we 'locked' `r[0]`,
|
||||
* nobody should have changed it in the mean time. */
|
||||
virtual_lock = atomic_cas_float(&r[0], FLT_MAX, virtual_lock);
|
||||
BLI_assert(virtual_lock == FLT_MAX);
|
||||
|
||||
#undef FLT_EQ_NONAN
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public Utility Functions
|
||||
*
|
||||
* 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());
|
||||
BLI_assert(!mesh.runtime->vert_normals_cache.is_cached());
|
||||
mesh.runtime->vert_normals_cache.ensure([&](Vector<float3> &r_data) { r_data = vert_normals; });
|
||||
}
|
||||
|
||||
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);
|
||||
BLI_assert(!mesh.runtime->vert_normals_cache.is_cached());
|
||||
mesh.runtime->vert_normals_cache.ensure(
|
||||
[vert_normals = std::move(vert_normals)](Vector<float3> &r_data) {
|
||||
r_data = std::move(vert_normals);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
float (*BKE_mesh_vert_normals_for_write(Mesh *mesh))[3]
|
||||
{
|
||||
/* Make sure the normals aren't shared. */
|
||||
using namespace blender;
|
||||
Vector<float3> vert_normals = mesh->vert_normals();
|
||||
mesh->runtime->vert_normals_cache.ensure(
|
||||
[vert_normals = std::move(vert_normals)](Vector<float3> &r_data) {
|
||||
r_data = std::move(vert_normals);
|
||||
});
|
||||
/* Give write access to the normals now used just by this mesh. */
|
||||
return reinterpret_cast<float(*)[3]>(
|
||||
const_cast<float3 *>(mesh->runtime->vert_normals_cache.data().data()));
|
||||
}
|
||||
|
||||
bool BKE_mesh_vert_normals_are_dirty(const Mesh *mesh)
|
||||
{
|
||||
return mesh->runtime->vert_normals_dirty;
|
||||
return mesh->runtime->vert_normals_cache.is_dirty();
|
||||
}
|
||||
|
||||
bool BKE_mesh_poly_normals_are_dirty(const Mesh *mesh)
|
||||
{
|
||||
return mesh->runtime->poly_normals_dirty;
|
||||
return mesh->runtime->poly_normals_cache.is_dirty();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -205,92 +186,45 @@ void normals_calc_polys(const Span<float3> positions,
|
|||
});
|
||||
}
|
||||
|
||||
void normals_calc_poly_vert(const Span<float3> positions,
|
||||
const OffsetIndices<int> polys,
|
||||
const Span<int> corner_verts,
|
||||
MutableSpan<float3> poly_normals,
|
||||
MutableSpan<float3> vert_normals)
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Mesh Normal Calculation (Polygons & Vertices)
|
||||
*
|
||||
* Take care making optimizations to this function as improvements to low-poly
|
||||
* meshes can slow down high-poly meshes. For details on performance, see D11993.
|
||||
* \{ */
|
||||
|
||||
void normals_calc_verts(const Span<float3> positions,
|
||||
const OffsetIndices<int> polys,
|
||||
const Span<int> corner_verts,
|
||||
const VertToPolyMap &vert_to_poly,
|
||||
const Span<float3> poly_normals,
|
||||
MutableSpan<float3> vert_normals)
|
||||
{
|
||||
|
||||
/* Zero the vertex normal array for accumulation. */
|
||||
{
|
||||
memset(vert_normals.data(), 0, vert_normals.as_span().size_in_bytes());
|
||||
}
|
||||
|
||||
/* Compute poly normals, accumulating them into vertex normals. */
|
||||
{
|
||||
threading::parallel_for(polys.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int poly_i : range) {
|
||||
const Span<int> poly_verts = corner_verts.slice(polys[poly_i]);
|
||||
|
||||
float3 &pnor = poly_normals[poly_i];
|
||||
|
||||
const int i_end = poly_verts.size() - 1;
|
||||
|
||||
/* Polygon Normal and edge-vector. */
|
||||
/* Inline version of #poly_normal_calc, also does edge-vectors. */
|
||||
{
|
||||
zero_v3(pnor);
|
||||
/* Newell's Method */
|
||||
const float *v_curr = positions[poly_verts[i_end]];
|
||||
for (int i_next = 0; i_next <= i_end; i_next++) {
|
||||
const float *v_next = positions[poly_verts[i_next]];
|
||||
add_newell_cross_v3_v3v3(pnor, v_curr, v_next);
|
||||
v_curr = v_next;
|
||||
}
|
||||
if (UNLIKELY(normalize_v3(pnor) == 0.0f)) {
|
||||
pnor[2] = 1.0f; /* Other axes set to zero. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Accumulate angle weighted face normal into the vertex normal. */
|
||||
/* Inline version of #accumulate_vertex_normals_poly_v3. */
|
||||
{
|
||||
float edvec_prev[3], edvec_next[3], edvec_end[3];
|
||||
const float *v_curr = positions[poly_verts[i_end]];
|
||||
sub_v3_v3v3(edvec_prev, positions[poly_verts[i_end - 1]], v_curr);
|
||||
normalize_v3(edvec_prev);
|
||||
copy_v3_v3(edvec_end, edvec_prev);
|
||||
|
||||
for (int i_next = 0, i_curr = i_end; i_next <= i_end; i_curr = i_next++) {
|
||||
const float *v_next = positions[poly_verts[i_next]];
|
||||
|
||||
/* Skip an extra normalization by reusing the first calculated edge. */
|
||||
if (i_next != i_end) {
|
||||
sub_v3_v3v3(edvec_next, v_curr, v_next);
|
||||
normalize_v3(edvec_next);
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(edvec_next, edvec_end);
|
||||
}
|
||||
|
||||
/* Calculate angle between the two poly edges incident on this vertex. */
|
||||
const float fac = saacos(-dot_v3v3(edvec_prev, edvec_next));
|
||||
const float vnor_add[3] = {pnor[0] * fac, pnor[1] * fac, pnor[2] * fac};
|
||||
|
||||
float *vnor = vert_normals[poly_verts[i_curr]];
|
||||
add_v3_v3_atomic(vnor, vnor_add);
|
||||
v_curr = v_next;
|
||||
copy_v3_v3(edvec_prev, edvec_next);
|
||||
}
|
||||
}
|
||||
threading::parallel_for(positions.index_range(), 1024, [=](const IndexRange range) {
|
||||
for (const int vert : range) {
|
||||
const Span<int> polys_around_vert = vert_to_poly[vert];
|
||||
if (polys_around_vert.is_empty()) {
|
||||
vert_normals[vert] = math::normalize(positions[vert]);
|
||||
continue;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Normalize and validate computed vertex normals. */
|
||||
{
|
||||
threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int vert_i : range) {
|
||||
float *no = vert_normals[vert_i];
|
||||
const float factor_inv = 1.0f / polys_around_vert.size();
|
||||
float3 vert_normal(0);
|
||||
for (const int poly_index : polys_around_vert) {
|
||||
const IndexRange poly = polys[poly_index];
|
||||
const int2 adjacent_verts = poly_find_adjecent_verts(poly, corner_verts, vert);
|
||||
|
||||
if (UNLIKELY(normalize_v3(no) == 0.0f)) {
|
||||
/* Following Mesh convention; we use vertex coordinate itself for normal in this case. */
|
||||
normalize_v3_v3(no, positions[vert_i]);
|
||||
}
|
||||
const float3 dir_prev = math::normalize(positions[adjacent_verts[0]] - positions[vert]);
|
||||
const float3 dir_next = math::normalize(positions[adjacent_verts[1]] - positions[vert]);
|
||||
const float factor = saacos(math::dot(dir_prev, dir_next));
|
||||
|
||||
vert_normal += poly_normals[poly_index] * factor * factor_inv;
|
||||
}
|
||||
});
|
||||
}
|
||||
vert_normals[vert] = vert_normal;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -309,6 +243,8 @@ blender::Span<blender::float3> Mesh::vert_normals() const
|
|||
return this->runtime->vert_normals;
|
||||
}
|
||||
|
||||
const Span<float3> poly_normals = this->poly_normals();
|
||||
|
||||
std::lock_guard lock{this->runtime->normals_mutex};
|
||||
if (!this->runtime->vert_normals_dirty) {
|
||||
BLI_assert(this->runtime->vert_normals.size() == this->totvert);
|
||||
|
@ -321,16 +257,18 @@ blender::Span<blender::float3> Mesh::vert_normals() const
|
|||
const OffsetIndices polys = this->polys();
|
||||
const Span<int> corner_verts = this->corner_verts();
|
||||
|
||||
this->runtime->vert_normals.reinitialize(positions.size());
|
||||
this->runtime->poly_normals.reinitialize(polys.size());
|
||||
bke::mesh::normals_calc_poly_vert(
|
||||
positions, polys, corner_verts, this->runtime->poly_normals, this->runtime->vert_normals);
|
||||
bke::mesh::VertToPolyMap vert_to_poly;
|
||||
Span<float3> poly_normals;
|
||||
threading::parallel_invoke(
|
||||
this->totvert > 1024,
|
||||
[&]() { vert_to_poly = this->vert_to_poly_map(); },
|
||||
[&]() { poly_normals = this->poly_normals(); });
|
||||
|
||||
this->runtime->vert_normals_dirty = false;
|
||||
this->runtime->poly_normals_dirty = false;
|
||||
r_data.reinitialize(positions.size());
|
||||
bke::mesh::normals_calc_verts(
|
||||
positions, polys, corner_verts, vert_to_poly, poly_normals, r_data);
|
||||
});
|
||||
|
||||
return this->runtime->vert_normals;
|
||||
return this->runtime->vert_normals_cache.data();
|
||||
}
|
||||
|
||||
blender::Span<blender::float3> Mesh::poly_normals() const
|
||||
|
@ -358,8 +296,7 @@ blender::Span<blender::float3> Mesh::poly_normals() const
|
|||
|
||||
this->runtime->poly_normals_dirty = false;
|
||||
});
|
||||
|
||||
return this->runtime->poly_normals;
|
||||
return this->runtime->poly_normals_cache.data();
|
||||
}
|
||||
|
||||
const float (*BKE_mesh_vert_normals_ensure(const Mesh *mesh))[3]
|
||||
|
|
|
@ -78,14 +78,6 @@ static void free_bvh_cache(MeshRuntime &mesh_runtime)
|
|||
}
|
||||
}
|
||||
|
||||
static void reset_normals(MeshRuntime &mesh_runtime)
|
||||
{
|
||||
mesh_runtime.vert_normals.clear_and_shrink();
|
||||
mesh_runtime.poly_normals.clear_and_shrink();
|
||||
mesh_runtime.vert_normals_dirty = true;
|
||||
mesh_runtime.poly_normals_dirty = true;
|
||||
}
|
||||
|
||||
static void free_batch_cache(MeshRuntime &mesh_runtime)
|
||||
{
|
||||
if (mesh_runtime.batch_cache) {
|
||||
|
@ -150,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;
|
||||
|
@ -274,9 +286,12 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh)
|
|||
{
|
||||
/* Tagging shared caches dirty will free the allocated data if there is only one user. */
|
||||
free_bvh_cache(*mesh->runtime);
|
||||
reset_normals(*mesh->runtime);
|
||||
free_subdiv_ccg(*mesh->runtime);
|
||||
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();
|
||||
|
@ -291,12 +306,12 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh)
|
|||
|
||||
void BKE_mesh_tag_edges_split(struct Mesh *mesh)
|
||||
{
|
||||
/* Triangulation didn't change because vertex positions and loop vertex indices didn't change.
|
||||
* Face normals didn't change either, but tag those anyway, since there is no API function to
|
||||
* only tag vertex normals dirty. */
|
||||
/* Triangulation didn't change because vertex positions and loop vertex indices didn't change. */
|
||||
free_bvh_cache(*mesh->runtime);
|
||||
reset_normals(*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();
|
||||
|
@ -310,14 +325,14 @@ void BKE_mesh_tag_edges_split(struct Mesh *mesh)
|
|||
|
||||
void BKE_mesh_tag_face_winding_changed(Mesh *mesh)
|
||||
{
|
||||
mesh->runtime->vert_normals_dirty = true;
|
||||
mesh->runtime->poly_normals_dirty = true;
|
||||
mesh->runtime->vert_normals_cache.tag_dirty();
|
||||
mesh->runtime->poly_normals_cache.tag_dirty();
|
||||
}
|
||||
|
||||
void BKE_mesh_tag_positions_changed(Mesh *mesh)
|
||||
{
|
||||
mesh->runtime->vert_normals_dirty = true;
|
||||
mesh->runtime->poly_normals_dirty = true;
|
||||
mesh->runtime->vert_normals_cache.tag_dirty();
|
||||
mesh->runtime->poly_normals_cache.tag_dirty();
|
||||
free_bvh_cache(*mesh->runtime);
|
||||
mesh->runtime->looptris_cache.tag_dirty();
|
||||
mesh->runtime->bounds_cache.tag_dirty();
|
||||
|
|
|
@ -1121,8 +1121,8 @@ void ED_mesh_update(Mesh *mesh, bContext *C, bool calc_edges, bool calc_edges_lo
|
|||
/* Default state is not to have tessface's so make sure this is the case. */
|
||||
BKE_mesh_tessface_clear(mesh);
|
||||
|
||||
mesh->runtime->vert_normals_dirty = true;
|
||||
mesh->runtime->poly_normals_dirty = true;
|
||||
mesh->runtime->vert_normals_cache.tag_dirty();
|
||||
mesh->runtime->poly_normals_cache.tag_dirty();
|
||||
|
||||
DEG_id_tag_update(&mesh->id, 0);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA, mesh);
|
||||
|
|
|
@ -164,12 +164,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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -646,10 +646,8 @@ 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((float3 *)normals_.data(), int64_t(normals_.size())));
|
||||
}
|
||||
|
||||
void USDMeshReader::process_normals_face_varying(Mesh *mesh)
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -278,7 +278,7 @@ static void mesh_merge_transform(Mesh *result,
|
|||
int cap_npolys,
|
||||
int *remap,
|
||||
int remap_len,
|
||||
const bool recalc_normals_later)
|
||||
MutableSpan<float3> dst_vert_normals)
|
||||
{
|
||||
using namespace blender;
|
||||
int *index_orig;
|
||||
|
@ -301,8 +301,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]);
|
||||
|
@ -578,11 +577,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++) {
|
||||
|
@ -603,7 +601,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]);
|
||||
|
@ -752,7 +750,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
|
|||
start_cap_npolys,
|
||||
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,
|
||||
|
@ -782,7 +780,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
|
|||
end_cap_npolys,
|
||||
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,
|
||||
|
@ -796,6 +794,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) {
|
||||
|
|
|
@ -319,10 +319,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(poly_offsets, segments); },
|
||||
|
|
Loading…
Reference in New Issue