Mesh: Add three cached topology maps

Add three cached topology maps to `Mesh`, to avoid computations when
mesh data isn't changed. Choosing the right maps to cache is a bit
arbitrary, but generally we have to start somewhere. The limiting
factor is memory usage (all the new caches combined have a
comparable footprint to a UV map).

For now, the caches added are:
- Vertex to face corner
- Vertex to face
- Face corner to face

These caches are used in quite a few places already;
- Face corner normal calculation
- UV value merging
- Setting sharp edges from face angles
- Data transfer modifier
- Voxel remesh attribute remapping
- Sculpt mode painting
- Sculpt mode normal calculation
- Vertex paint mode
- Split edges geometry node
- Mesh topology geometry nodes

Caching topology maps means they don't have to be rebuilt every time
they're used. Meshes copied but without topology changes can share
the cache, further reducing re-computations. For example, FPS with a
large mesh using the "Corners of Vertex" node went from 1.8 to 2.3.
Entering sculpt mode is slightly faster too.

There is some obvious work for future commits:
- Use caches in attribute domain interpolation
- More multithreading of second phase of map building
- Update/build caches eagerly in some geometry nodes

Pull Request: https://projects.blender.org/blender/blender/pulls/107816
This commit is contained in:
Hans Goudey 2023-08-30 23:41:59 +02:00 committed by Hans Goudey
parent 1015bed2fd
commit 4e94db97e2
32 changed files with 172 additions and 162 deletions

View File

@ -146,7 +146,6 @@ short2 lnor_space_custom_normal_to_data(const CornerNormalSpace &lnor_space,
* Useful to materialize sharp edges (or non-smooth faces) without actually modifying the geometry
* (splitting edges).
*
* \param loop_to_face_map: Optional pre-created map from corners to their face.
* \param sharp_edges: Optional array of sharp edge tags, used to split the evaluated normals on
* each side of the edge.
* \param r_lnors_spacearr: Optional return data filled with information about the custom
@ -204,6 +203,7 @@ void edges_sharp_from_angle_set(OffsetIndices<int> faces,
Span<int> corner_verts,
Span<int> corner_edges,
Span<float3> face_normals,
Span<int> loop_to_face,
const bool *sharp_faces,
const float split_angle,
MutableSpan<bool> sharp_edges);

View File

@ -280,12 +280,17 @@ GroupedSpan<int> build_vert_to_edge_map(Span<int2> edges,
Array<int> &r_offsets,
Array<int> &r_indices);
void build_vert_to_face_indices(OffsetIndices<int> faces,
Span<int> corner_verts,
OffsetIndices<int> offsets,
MutableSpan<int> face_indices);
GroupedSpan<int> build_vert_to_face_map(OffsetIndices<int> faces,
Span<int> corner_verts,
int verts_num,
Array<int> &r_offsets,
Array<int> &r_indices);
Array<int> build_vert_to_corner_indices(Span<int> corner_verts, OffsetIndices<int> offsets);
GroupedSpan<int> build_vert_to_loop_map(Span<int> corner_verts,
int verts_num,
Array<int> &r_offsets,

View File

@ -140,6 +140,17 @@ struct MeshRuntime {
SharedCache<Vector<float3>> vert_normals_cache;
SharedCache<Vector<float3>> face_normals_cache;
/**
* Cache of offsets for vert to face/corner maps. The same offsets array is used to group
* indices for both the vertex to face and vertex to corner maps.
*/
SharedCache<Array<int>> vert_to_face_offset_cache;
/** Cache of indices for vert to face map. */
SharedCache<Array<int>> vert_to_face_map_cache;
/** Cache of indices for vert to corner map. */
SharedCache<Array<int>> vert_to_corner_map_cache;
/** Cache of face indices for each face corner. */
SharedCache<Array<int>> corner_to_face_map_cache;
/** Cache of data about edges not used by faces. See #Mesh::loose_edges(). */
SharedCache<LooseEdgeCache> loose_edges_cache;
/** Cache of data about vertices not used by edges. See #Mesh::loose_verts(). */

View File

@ -267,12 +267,7 @@ void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Pain
/** Used for both vertex color and weight paint. */
struct SculptVertexPaintGeomMap {
blender::Array<int> vert_to_loop_offsets;
blender::Array<int> vert_to_loop_indices;
blender::GroupedSpan<int> vert_to_loop;
blender::Array<int> vert_to_face_offsets;
blender::Array<int> vert_to_face_indices;
blender::GroupedSpan<int> vert_to_face;
};
@ -591,9 +586,7 @@ struct SculptSession {
float *vmask;
/* Mesh connectivity maps. */
/* Vertices to adjacent faces. */
blender::Array<int> vert_to_face_offsets;
blender::Array<int> vert_to_face_indices;
/* Vertices to adjacent polys. */
blender::GroupedSpan<int> pmap;
/* Edges to adjacent faces. */

View File

@ -393,7 +393,7 @@ static void data_transfer_dtdata_type_preprocess(const Mesh *me_src,
me_dst->faces(),
me_dst->corner_verts(),
me_dst->corner_edges(),
{},
me_dst->corner_to_face_map(),
me_dst->vert_normals(),
me_dst->face_normals(),
sharp_edges,

View File

@ -2278,7 +2278,7 @@ void BKE_keyblock_mesh_calc_normals(const KeyBlock *kb,
faces,
corner_verts,
corner_edges,
{},
mesh->corner_to_face_map(),
{reinterpret_cast<blender::float3 *>(vert_normals), mesh->totvert},
{reinterpret_cast<blender::float3 *>(face_normals), faces.size()},
sharp_edges,

View File

@ -139,6 +139,10 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
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->looptri_faces_cache = mesh_src->runtime->looptri_faces_cache;
mesh_dst->runtime->vert_to_face_offset_cache = mesh_src->runtime->vert_to_face_offset_cache;
mesh_dst->runtime->vert_to_face_map_cache = mesh_src->runtime->vert_to_face_map_cache;
mesh_dst->runtime->vert_to_corner_map_cache = mesh_src->runtime->vert_to_corner_map_cache;
mesh_dst->runtime->corner_to_face_map_cache = mesh_src->runtime->corner_to_face_map_cache;
/* Only do tessface if we have no faces. */
const bool do_tessface = ((mesh_src->totface_legacy != 0) && (mesh_src->faces_num == 0));
@ -1778,7 +1782,7 @@ void BKE_mesh_calc_normals_split_ex(const Mesh *mesh,
mesh->faces(),
mesh->corner_verts(),
mesh->corner_edges(),
{},
mesh->corner_to_face_map(),
mesh->vert_normals(),
mesh->face_normals(),
sharp_edges,

View File

@ -204,8 +204,7 @@ class MeshFairingContext : public FairingContext {
faces = mesh->faces();
corner_verts_ = mesh->corner_verts();
corner_edges_ = mesh->corner_edges();
vlmap_ = blender::bke::mesh::build_vert_to_loop_map(
corner_verts_, positions.size(), vert_to_face_offsets_, vert_to_face_indices_);
vlmap_ = mesh->vert_to_corner_map();
/* Deformation coords. */
co_.reserve(mesh->totvert);
@ -220,7 +219,7 @@ class MeshFairingContext : public FairingContext {
}
}
loop_to_face_map_ = blender::bke::mesh::build_loop_to_face_map(faces);
loop_to_face_map_ = mesh->corner_to_face_map();
}
void adjacents_coords_from_loop(const int loop,
@ -247,9 +246,7 @@ class MeshFairingContext : public FairingContext {
Span<int> corner_edges_;
blender::OffsetIndices<int> faces;
Span<blender::int2> edges_;
Array<int> loop_to_face_map_;
Array<int> vert_to_face_offsets_;
Array<int> vert_to_face_indices_;
Span<int> loop_to_face_map_;
};
class BMeshFairingContext : public FairingContext {

View File

@ -379,6 +379,20 @@ GroupedSpan<int> build_vert_to_edge_map(const Span<int2> edges,
return {OffsetIndices<int>(r_offsets), r_indices};
}
void build_vert_to_face_indices(const OffsetIndices<int> faces,
const Span<int> corner_verts,
const OffsetIndices<int> offsets,
MutableSpan<int> r_indices)
{
Array<int> counts(offsets.size(), 0);
for (const int64_t face_i : faces.index_range()) {
for (const int vert : corner_verts.slice(faces[face_i])) {
r_indices[offsets[vert].start() + counts[vert]] = int(face_i);
counts[vert]++;
}
}
}
GroupedSpan<int> build_vert_to_face_map(const OffsetIndices<int> faces,
const Span<int> corner_verts,
const int verts_num,
@ -387,17 +401,16 @@ GroupedSpan<int> build_vert_to_face_map(const OffsetIndices<int> faces,
{
r_offsets = create_reverse_offsets(corner_verts, verts_num);
r_indices.reinitialize(r_offsets.last());
Array<int> counts(verts_num, 0);
for (const int64_t face_i : faces.index_range()) {
for (const int vert : corner_verts.slice(faces[face_i])) {
r_indices[r_offsets[vert] + counts[vert]] = int(face_i);
counts[vert]++;
}
}
build_vert_to_face_indices(faces, corner_verts, OffsetIndices<int>(r_offsets), r_indices);
return {OffsetIndices<int>(r_offsets), r_indices};
}
Array<int> build_vert_to_corner_indices(const Span<int> corner_verts,
const OffsetIndices<int> offsets)
{
return reverse_indices_in_groups(corner_verts, offsets);
}
GroupedSpan<int> build_vert_to_loop_map(const Span<int> corner_verts,
const int verts_num,
Array<int> &r_offsets,

View File

@ -114,10 +114,7 @@ void BKE_mesh_merge_customdata_for_apply_modifier(Mesh *me)
return;
}
Array<int> vert_to_loop_offsets;
Array<int> vert_to_loop_indices;
const GroupedSpan<int> vert_to_loop = bke::mesh::build_vert_to_loop_map(
me->corner_verts(), me->totvert, vert_to_loop_offsets, vert_to_loop_indices);
const GroupedSpan<int> vert_to_loop = me->vert_to_corner_map();
Vector<float2 *> mloopuv_layers;
mloopuv_layers.reserve(mloopuv_layers_num);

View File

@ -414,7 +414,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
result_faces,
result_corner_verts,
result_corner_edges,
{},
result->corner_to_face_map(),
result->vert_normals(),
result->face_normals(),
sharp_edges,

View File

@ -791,6 +791,7 @@ void edges_sharp_from_angle_set(const OffsetIndices<int> faces,
const Span<int> corner_verts,
const Span<int> corner_edges,
const Span<float3> face_normals,
const Span<int> loop_to_face,
const bool *sharp_faces,
const float split_angle,
MutableSpan<bool> sharp_edges)
@ -803,9 +804,6 @@ void edges_sharp_from_angle_set(const OffsetIndices<int> faces,
/* Mapping edge -> loops. See #bke::mesh::normals_calc_loop for details. */
Array<int2> edge_to_loops(sharp_edges.size(), int2(0));
/* Simple mapping from a loop to its face index. */
const Array<int> loop_to_face = build_loop_to_face_map(faces);
mesh_edges_sharp_tag(faces,
corner_verts,
corner_edges,
@ -1257,17 +1255,6 @@ void normals_calc_loop(const Span<float3> vert_positions,
* Note also that loose edges always have both values set to 0! */
Array<int2> edge_to_loops(edges.size(), int2(0));
/* Simple mapping from a loop to its face index. */
Span<int> loop_to_face;
Array<int> local_loop_to_face_map;
if (loop_to_face_map.is_empty()) {
local_loop_to_face_map = build_loop_to_face_map(faces);
loop_to_face = local_loop_to_face_map;
}
else {
loop_to_face = loop_to_face_map;
}
/* When using custom loop normals, disable the angle feature! */
const bool check_angle = (split_angle < float(M_PI)) && (clnors_data == nullptr);
@ -1293,7 +1280,7 @@ void normals_calc_loop(const Span<float3> vert_positions,
common_data.corner_verts = corner_verts;
common_data.corner_edges = corner_edges;
common_data.edge_to_loops = edge_to_loops;
common_data.loop_to_face = loop_to_face;
common_data.loop_to_face = loop_to_face_map;
common_data.face_normals = face_normals;
common_data.vert_normals = vert_normals;
@ -1305,7 +1292,7 @@ void normals_calc_loop(const Span<float3> vert_positions,
mesh_edges_sharp_tag(faces,
corner_verts,
corner_edges,
loop_to_face,
loop_to_face_map,
face_normals,
Span<bool>(sharp_faces, sharp_faces ? faces.size() : 0),
Span<bool>(sharp_edges, sharp_edges ? edges.size() : 0),

View File

@ -1290,12 +1290,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode,
blender::Array<blender::float3> face_cents_src;
Array<int> vert_to_loop_src_offsets;
Array<int> vert_to_loop_src_indices;
GroupedSpan<int> vert_to_loop_map_src;
Array<int> vert_to_face_src_offsets;
Array<int> vert_to_face_src_indices;
GroupedSpan<int> vert_to_face_map_src;
Array<int> edge_to_face_src_offsets;
@ -1306,7 +1301,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode,
int *face_to_looptri_map_src_buff = nullptr;
/* Unlike above, those are one-to-one mappings, simpler! */
blender::Array<int> loop_to_face_map_src;
blender::Span<int> loop_to_face_map_src;
const blender::Span<blender::float3> positions_src = me_src->vert_positions();
const int num_verts_src = me_src->totvert;
@ -1371,7 +1366,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode,
faces_dst,
{corner_verts_dst, numloops_dst},
{corner_edges_dst, numloops_dst},
{},
mesh_dst->corner_to_face_map(),
mesh_dst->vert_normals(),
mesh_dst->face_normals(),
sharp_edges,
@ -1397,15 +1392,9 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode,
}
if (use_from_vert) {
vert_to_loop_map_src = bke::mesh::build_vert_to_loop_map(
corner_verts_src, num_verts_src, vert_to_loop_src_offsets, vert_to_loop_src_indices);
vert_to_loop_map_src = me_src->vert_to_corner_map();
if (mode & MREMAP_USE_POLY) {
vert_to_face_map_src = bke::mesh::build_vert_to_face_map(faces_src,
corner_verts_src,
num_verts_src,
vert_to_face_src_offsets,
vert_to_face_src_indices);
vert_to_face_map_src = me_src->vert_to_face_map();
}
}
@ -1417,7 +1406,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode,
edge_to_face_src_indices);
if (use_from_vert) {
loop_to_face_map_src = blender::bke::mesh::build_loop_to_face_map(faces_src);
loop_to_face_map_src = me_src->corner_to_face_map();
face_cents_src.reinitialize(faces_src.size());
for (const int64_t i : faces_src.index_range()) {
face_cents_src[i] = blender::bke::mesh::face_center_calc(

View File

@ -386,27 +386,13 @@ void BKE_remesh_reproject_vertex_paint(Mesh *target, const Mesh *source)
return;
}
Array<int> source_vert_to_loop_offsets;
Array<int> source_vert_to_loop_indices;
GroupedSpan<int> source_lmap;
Array<int> target_vert_to_loop_offsets;
Array<int> target_vert_to_loop_indices;
GroupedSpan<int> target_lmap;
BVHTreeFromMesh bvhtree = {nullptr};
threading::parallel_invoke(
[&]() { BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_VERTS, 2); },
[&]() {
source_lmap = mesh::build_vert_to_loop_map(source->corner_verts(),
source->totvert,
source_vert_to_loop_offsets,
source_vert_to_loop_indices);
},
[&]() {
target_lmap = mesh::build_vert_to_loop_map(target->corner_verts(),
target->totvert,
target_vert_to_loop_offsets,
target_vert_to_loop_indices);
});
[&]() { source_lmap = source->vert_to_corner_map(); },
[&]() { target_lmap = target->vert_to_corner_map(); });
const Span<float3> target_positions = target->vert_positions();
Array<int> nearest_src_verts(target_positions.size());

View File

@ -14,6 +14,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "BLI_array_utils.hh"
#include "BLI_math_geom.h"
#include "BLI_task.hh"
#include "BLI_timeit.hh"
@ -22,6 +23,7 @@
#include "BKE_editmesh_cache.hh"
#include "BKE_lib_id.h"
#include "BKE_mesh.hh"
#include "BKE_mesh_mapping.hh"
#include "BKE_mesh_runtime.hh"
#include "BKE_shrinkwrap.h"
#include "BKE_subdiv_ccg.hh"
@ -125,6 +127,58 @@ static void try_tag_verts_no_face_none(const Mesh &mesh)
} // namespace blender::bke
blender::Span<int> Mesh::corner_to_face_map() const
{
using namespace blender;
this->runtime->corner_to_face_map_cache.ensure([&](Array<int> &r_data) {
const OffsetIndices faces = this->faces();
r_data = bke::mesh::build_loop_to_face_map(faces);
});
return this->runtime->corner_to_face_map_cache.data();
}
blender::OffsetIndices<int> Mesh::vert_to_face_map_offsets() const
{
using namespace blender;
this->runtime->vert_to_face_offset_cache.ensure([&](Array<int> &r_data) {
r_data = Array<int>(this->totvert + 1, 0);
offset_indices::build_reverse_offsets(this->corner_verts(), r_data);
});
return OffsetIndices<int>(this->runtime->vert_to_face_offset_cache.data());
}
blender::GroupedSpan<int> Mesh::vert_to_face_map() const
{
using namespace blender;
const OffsetIndices offsets = this->vert_to_face_map_offsets();
this->runtime->vert_to_face_map_cache.ensure([&](Array<int> &r_data) {
r_data.reinitialize(this->totloop);
if (this->runtime->vert_to_corner_map_cache.is_cached() &&
this->runtime->corner_to_face_map_cache.is_cached())
{
/* The vertex to face cache can be built from the vertex to face corner
* and face corner to face maps if they are both already cached. */
array_utils::gather(this->runtime->vert_to_corner_map_cache.data().as_span(),
this->runtime->corner_to_face_map_cache.data().as_span(),
r_data.as_mutable_span());
}
else {
bke::mesh::build_vert_to_face_indices(this->faces(), this->corner_verts(), offsets, r_data);
}
});
return {offsets, this->runtime->vert_to_face_map_cache.data()};
}
blender::GroupedSpan<int> Mesh::vert_to_corner_map() const
{
using namespace blender;
const OffsetIndices offsets = this->vert_to_face_map_offsets();
this->runtime->vert_to_corner_map_cache.ensure([&](Array<int> &r_data) {
r_data = bke::mesh::build_vert_to_corner_indices(this->corner_verts(), offsets);
});
return {offsets, this->runtime->vert_to_corner_map_cache.data()};
}
const blender::bke::LooseVertCache &Mesh::loose_verts() const
{
using namespace blender::bke;
@ -250,6 +304,10 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh)
free_bvh_cache(*mesh->runtime);
free_subdiv_ccg(*mesh->runtime);
mesh->runtime->bounds_cache.tag_dirty();
mesh->runtime->vert_to_face_offset_cache.tag_dirty();
mesh->runtime->vert_to_face_map_cache.tag_dirty();
mesh->runtime->vert_to_corner_map_cache.tag_dirty();
mesh->runtime->corner_to_face_map_cache.tag_dirty();
mesh->runtime->vert_normals_cache.tag_dirty();
mesh->runtime->face_normals_cache.tag_dirty();
mesh->runtime->loose_edges_cache.tag_dirty();
@ -271,6 +329,9 @@ void BKE_mesh_tag_edges_split(Mesh *mesh)
free_bvh_cache(*mesh->runtime);
mesh->runtime->vert_normals_cache.tag_dirty();
free_subdiv_ccg(*mesh->runtime);
mesh->runtime->vert_to_face_offset_cache.tag_dirty();
mesh->runtime->vert_to_face_map_cache.tag_dirty();
mesh->runtime->vert_to_corner_map_cache.tag_dirty();
if (mesh->runtime->loose_edges_cache.is_cached() &&
mesh->runtime->loose_edges_cache.data().count != 0)
{
@ -298,6 +359,7 @@ void BKE_mesh_tag_face_winding_changed(Mesh *mesh)
{
mesh->runtime->vert_normals_cache.tag_dirty();
mesh->runtime->face_normals_cache.tag_dirty();
mesh->runtime->vert_to_corner_map_cache.tag_dirty();
}
void BKE_mesh_tag_positions_changed(Mesh *mesh)

View File

@ -469,10 +469,6 @@ void multires_force_sculpt_rebuild(Object *object)
BKE_pbvh_free(ss->pbvh);
object->sculpt->pbvh = nullptr;
}
ss->vert_to_face_indices = {};
ss->vert_to_face_offsets = {};
ss->pmap = {};
}
void multires_force_external_reload(Object *object)

View File

@ -72,14 +72,7 @@ void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape
blender::MutableSpan<blender::float3> base_positions = base_mesh->vert_positions_for_write();
/* Update the context in case the vertices were duplicated. */
reshape_context->base_positions = base_positions;
blender::Array<int> vert_to_face_offsets;
blender::Array<int> vert_to_face_indices;
const blender::GroupedSpan<int> pmap = blender::bke::mesh::build_vert_to_face_map(
reshape_context->base_faces,
reshape_context->base_corner_verts,
base_mesh->totvert,
vert_to_face_offsets,
vert_to_face_indices);
const blender::GroupedSpan<int> pmap = base_mesh->vert_to_face_map();
float(*origco)[3] = static_cast<float(*)[3]>(
MEM_calloc_arrayN(base_mesh->totvert, sizeof(float[3]), __func__));

View File

@ -925,7 +925,6 @@ static void multires_unsubdivide_prepare_original_bmesh_for_extract(
MultiresUnsubdivideContext *context)
{
Mesh *original_mesh = context->original_mesh;
const blender::OffsetIndices original_faces = original_mesh->faces();
Mesh *base_mesh = context->base_mesh;
@ -953,7 +952,7 @@ static void multires_unsubdivide_prepare_original_bmesh_for_extract(
BM_elem_flag_set(v, BM_ELEM_TAG, true);
}
context->loop_to_face_map = blender::bke::mesh::build_loop_to_face_map(original_faces);
context->loop_to_face_map = original_mesh->corner_to_face_map();
}
/**

View File

@ -52,7 +52,7 @@ struct MultiresUnsubdivideContext {
/* Private data. */
BMesh *bm_original_mesh;
blender::Array<int> loop_to_face_map;
blender::Span<int> loop_to_face_map;
const int *base_to_orig_vmap;
};

View File

@ -1391,13 +1391,7 @@ void BKE_sculptsession_free_deformMats(SculptSession *ss)
void BKE_sculptsession_free_vwpaint_data(SculptSession *ss)
{
SculptVertexPaintGeomMap *gmap = nullptr;
if (ss->mode_type == OB_MODE_VERTEX_PAINT) {
gmap = &ss->mode.vpaint.gmap;
}
else if (ss->mode_type == OB_MODE_WEIGHT_PAINT) {
gmap = &ss->mode.wpaint.gmap;
if (ss->mode_type == OB_MODE_WEIGHT_PAINT) {
MEM_SAFE_FREE(ss->mode.wpaint.alpha_weight);
if (ss->mode.wpaint.dvert_prev) {
BKE_defvert_array_free_elems(ss->mode.wpaint.dvert_prev, ss->totvert);
@ -1405,15 +1399,6 @@ void BKE_sculptsession_free_vwpaint_data(SculptSession *ss)
ss->mode.wpaint.dvert_prev = nullptr;
}
}
else {
return;
}
gmap->vert_to_loop_offsets = {};
gmap->vert_to_loop_indices = {};
gmap->vert_to_loop = {};
gmap->vert_to_face_offsets = {};
gmap->vert_to_face_indices = {};
gmap->vert_to_face = {};
}
/**
@ -1459,8 +1444,6 @@ static void sculptsession_free_pbvh(Object *object)
ss->pbvh = nullptr;
}
ss->vert_to_face_offsets = {};
ss->vert_to_face_indices = {};
ss->pmap = {};
ss->edge_to_face_offsets = {};
ss->edge_to_face_indices = {};
@ -1776,12 +1759,8 @@ static void sculpt_update_object(
sculpt_attribute_update_refs(ob);
sculpt_update_persistent_base(ob);
if (ob->type == OB_MESH && ss->pmap.is_empty()) {
ss->pmap = blender::bke::mesh::build_vert_to_face_map(me->faces(),
me->corner_verts(),
me->totvert,
ss->vert_to_face_offsets,
ss->vert_to_face_indices);
if (ob->type == OB_MESH) {
ss->pmap = me->vert_to_face_map();
}
if (ss->pbvh) {

View File

@ -357,7 +357,7 @@ void mesh_render_data_update_normals(MeshRenderData &mr, const eMRDataType data_
mr.faces,
mr.corner_verts,
mr.corner_edges,
{},
mr.me->corner_to_face_map(),
mr.vert_normals,
mr.face_normals,
sharp_edges,

View File

@ -744,6 +744,7 @@ static int mesh_customdata_custom_splitnormals_add_exec(bContext *C, wmOperator
me->corner_verts(),
me->corner_edges(),
me->face_normals(),
me->corner_to_face_map(),
sharp_faces,
me->smoothresh,
sharp_edges.span);
@ -1165,6 +1166,7 @@ void ED_mesh_split_faces(Mesh *mesh)
corner_verts,
corner_edges,
mesh->face_normals(),
mesh->corner_to_face_map(),
sharp_faces,
split_angle,
sharp_edges);

View File

@ -241,15 +241,9 @@ void init_session_data(const ToolSettings *ts, Object *ob)
}
Mesh *me = (Mesh *)ob->data;
const blender::OffsetIndices faces = me->faces();
const Span<int> corner_verts = me->corner_verts();
if (gmap->vert_to_loop_indices.is_empty()) {
gmap->vert_to_loop = blender::bke::mesh::build_vert_to_loop_map(
corner_verts, me->totvert, gmap->vert_to_loop_offsets, gmap->vert_to_loop_indices);
gmap->vert_to_face = blender::bke::mesh::build_vert_to_face_map(
faces, corner_verts, me->totvert, gmap->vert_to_face_offsets, gmap->vert_to_face_indices);
}
gmap->vert_to_loop = me->vert_to_corner_map();
gmap->vert_to_face = me->vert_to_face_map();
/* Create average brush arrays */
if (ob->mode == OB_MODE_WEIGHT_PAINT) {

View File

@ -517,10 +517,7 @@ void split_edges(Mesh &mesh,
const BitVector<> selection_bits = selection_to_bit_vector(selected_edges, orig_edges.size());
const bke::LooseEdgeCache &loose_edges = mesh.loose_edges();
Array<int> vert_to_corner_offsets;
Array<int> vert_to_corner_indices;
const GroupedSpan<int> vert_to_corner_map = bke::mesh::build_vert_to_loop_map(
mesh.corner_verts(), orig_verts_num, vert_to_corner_offsets, vert_to_corner_indices);
const GroupedSpan<int> vert_to_corner_map = mesh.vert_to_corner_map();
Array<int> edge_to_corner_offsets;
Array<int> edge_to_corner_indices;
@ -535,7 +532,7 @@ void split_edges(Mesh &mesh,
orig_edges, orig_verts_num, vert_to_edge_offsets, vert_to_edge_indices);
}
const Array<int> corner_to_face_map = bke::mesh::build_loop_to_face_map(mesh.faces());
const Array<int> corner_to_face_map = mesh.corner_to_face_map();
const Array<Vector<CornerGroup>> corner_groups = calc_all_corner_groups(faces,
mesh.corner_verts(),

View File

@ -316,6 +316,23 @@ 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);
/**
* Cached map containing the index of the face using each face corner.
*/
blender::Span<int> corner_to_face_map() const;
/**
* Offsets per vertex used to slice arrays containing data for connected faces or face corners.
*/
blender::OffsetIndices<int> vert_to_face_map_offsets() const;
/**
* Cached map from each vertex to the corners using it.
*/
blender::GroupedSpan<int> vert_to_corner_map() const;
/**
* Cached map from each vertex to the faces using it.
*/
blender::GroupedSpan<int> vert_to_face_map() const;
/**
* Cached information about loose edges, calculated lazily when necessary.
*/

View File

@ -534,7 +534,7 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd,
faces,
corner_verts,
corner_edges,
{},
result->corner_to_face_map(),
result->vert_normals(),
result->face_normals(),
sharp_edges.span.data(),

View File

@ -543,7 +543,7 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh
int defgrp_index;
MOD_get_vgroup(ctx->object, mesh, wnmd->defgrp_name, &dvert, &defgrp_index);
const Array<int> loop_to_face_map = bke::mesh::build_loop_to_face_map(result->faces());
const Span<int> loop_to_face_map = result->corner_to_face_map();
bke::MutableAttributeAccessor attributes = result->attributes_for_write();
bke::SpanAttributeWriter<bool> sharp_edges = attributes.lookup_or_add_for_write_span<bool>(

View File

@ -630,15 +630,8 @@ static Mesh *calc_dual_mesh(const Mesh &src_mesh,
/* Stores the indices of the faces connected to the vertex. Because the faces are looped
* over in order of their indices, the face's indices will be sorted in ascending order.
* (This can change once they are sorted using `sort_vertex_faces`). */
Array<int> vert_to_face_offset_data;
Array<int> vert_to_face_indices;
const GroupedSpan<int> vert_to_face_map = bke::mesh::build_vert_to_face_map(
src_faces,
src_corner_verts,
src_positions.size(),
vert_to_face_offset_data,
vert_to_face_indices);
const OffsetIndices<int> vert_to_face_offsets(vert_to_face_offset_data);
Array<int> vert_to_face_indices = src_mesh.vert_to_face_map().data;
const OffsetIndices<int> vert_to_face_offsets = src_mesh.vert_to_face_map().offsets;
Array<Array<int>> vertex_shared_edges(src_mesh.totvert);
Array<Array<int>> vertex_corners(src_mesh.totvert);
@ -692,6 +685,8 @@ static Mesh *calc_dual_mesh(const Mesh &src_mesh,
}
});
const GroupedSpan<int> vert_to_face_map(vert_to_face_offsets, vert_to_face_indices);
Vector<float3> vert_positions(src_mesh.faces_num);
for (const int i : src_faces.index_range()) {
const IndexRange face = src_faces[i];

View File

@ -49,10 +49,7 @@ class CornersOfVertInput final : public bke::MeshFieldInput {
const IndexMask &mask) const final
{
const IndexRange vert_range(mesh.totvert);
Array<int> map_offsets;
Array<int> map_indices;
const GroupedSpan<int> vert_to_loop_map = bke::mesh::build_vert_to_loop_map(
mesh.corner_verts(), mesh.totvert, map_offsets, map_indices);
const GroupedSpan<int> vert_to_corner_map = mesh.vert_to_corner_map();
const bke::MeshFieldContext context{mesh, domain};
fn::FieldEvaluator evaluator{context, &mask};
@ -83,7 +80,7 @@ class CornersOfVertInput final : public bke::MeshFieldInput {
continue;
}
const Span<int> corners = vert_to_loop_map[vert_i];
const Span<int> corners = vert_to_corner_map[vert_i];
if (corners.is_empty()) {
corner_of_vertex[selection_i] = 0;
continue;

View File

@ -75,12 +75,10 @@ class CornerPreviousEdgeFieldInput final : public bke::MeshFieldInput {
}
const OffsetIndices faces = mesh.faces();
const Span<int> corner_edges = mesh.corner_edges();
Array<int> loop_to_face_map = bke::mesh::build_loop_to_face_map(faces);
const Span<int> corner_to_face = mesh.corner_to_face_map();
return VArray<int>::ForFunc(
mesh.totloop,
[faces, corner_edges, loop_to_face_map = std::move(loop_to_face_map)](const int corner_i) {
return corner_edges[bke::mesh::face_corner_prev(faces[loop_to_face_map[corner_i]],
corner_i)];
corner_edges.size(), [faces, corner_edges, corner_to_face](const int corner) {
return corner_edges[bke::mesh::face_corner_prev(faces[corner_to_face[corner]], corner)];
});
}

View File

@ -36,7 +36,7 @@ class CornerFaceIndexInput final : public bke::MeshFieldInput {
if (domain != ATTR_DOMAIN_CORNER) {
return {};
}
return VArray<int>::ForContainer(bke::mesh::build_loop_to_face_map(mesh.faces()));
return VArray<int>::ForSpan(mesh.corner_to_face_map());
}
uint64_t hash() const final
@ -65,12 +65,11 @@ class CornerIndexInFaceInput final : public bke::MeshFieldInput {
return {};
}
const OffsetIndices faces = mesh.faces();
Array<int> loop_to_face_map = bke::mesh::build_loop_to_face_map(faces);
return VArray<int>::ForFunc(
mesh.totloop, [faces, loop_to_face_map = std::move(loop_to_face_map)](const int corner_i) {
const int face_i = loop_to_face_map[corner_i];
return corner_i - faces[face_i].start();
});
const Span<int> corner_to_face = mesh.corner_to_face_map();
return VArray<int>::ForFunc(mesh.totloop, [faces, corner_to_face](const int corner) {
const int face_i = corner_to_face[corner];
return corner - faces[face_i].start();
});
}
uint64_t hash() const final

View File

@ -52,7 +52,7 @@ class OffsetCornerInFaceFieldInput final : public bke::MeshFieldInput {
const VArray<int> corner_indices = evaluator.get_evaluated<int>(0);
const VArray<int> offsets = evaluator.get_evaluated<int>(1);
Array<int> loop_to_face_map = bke::mesh::build_loop_to_face_map(faces);
const Span<int> corner_to_face = mesh.corner_to_face_map();
Array<int> offset_corners(mask.min_array_size());
mask.foreach_index_optimized<int>(GrainSize(2048), [&](const int selection_i) {
@ -63,7 +63,7 @@ class OffsetCornerInFaceFieldInput final : public bke::MeshFieldInput {
return;
}
const IndexRange face = faces[loop_to_face_map[corner_i]];
const IndexRange face = faces[corner_to_face[corner_i]];
offset_corners[selection_i] = apply_offset_in_cyclic_range(face, corner_i, offset);
});