Files
test/source/blender/blenkernel/intern/mesh_runtime.cc
Hans Goudey d95b1f120b Mesh: Store BMEditMesh in shared pointer
The main motivation for this is that it's part of a fix for #113377,
where I want to propagate the edit mesh pointers through copied
meshes in modifiers and geometry nodes, instead of just setting the
edit mesh pointer at the end of the modifier stack. That would have
two main benefits:
1. We avoid the need to write to the evaluated mesh, after evaluation
  which means it can be shared directly among evaluated objects.
2. When an object's mesh is completely replaced by the mesh from another
   object during evaluation (with the object info node), the final edit
   mesh pointer will not be "wrong", allowing us to skip index-mapped
   GPU data extraction.

Beyond that, using a shared pointer just makes things more automatic.
Handling of edit mesh data is already complicated enough, this way some
of the worry and complexity can be handled by RAII.

One thing to keep in mind is that the edit mesh's BMesh is still freed
manually with `EDBM_mesh_free_data` when leaving edit mode. I figured
that was a more conservative approach for now. Maybe eventually that
could be handled automatically with RAII too.

Pull Request: https://projects.blender.org/blender/blender/pulls/120276
2024-04-18 13:52:20 +02:00

473 lines
14 KiB
C++

/* SPDX-FileCopyrightText: 2005 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include "MEM_guardedalloc.h"
#include "BLI_array_utils.hh"
#include "BLI_math_geom.h"
#include "BLI_task.hh"
#include "BKE_bake_data_block_id.hh"
#include "BKE_bvhutils.hh"
#include "BKE_customdata.hh"
#include "BKE_editmesh_cache.hh"
#include "BKE_lib_id.hh"
#include "BKE_mesh.hh"
#include "BKE_mesh_mapping.hh"
#include "BKE_mesh_runtime.hh"
#include "BKE_shrinkwrap.hh"
#include "BKE_subdiv_ccg.hh"
using blender::float3;
using blender::MutableSpan;
using blender::Span;
/* -------------------------------------------------------------------- */
/** \name Mesh Runtime Struct Utils
* \{ */
namespace blender::bke {
static void free_mesh_eval(MeshRuntime &mesh_runtime)
{
if (mesh_runtime.mesh_eval != nullptr) {
BKE_id_free(nullptr, mesh_runtime.mesh_eval);
mesh_runtime.mesh_eval = nullptr;
}
}
static void free_bvh_cache(MeshRuntime &mesh_runtime)
{
if (mesh_runtime.bvh_cache) {
bvhcache_free(mesh_runtime.bvh_cache);
mesh_runtime.bvh_cache = nullptr;
}
}
static void free_batch_cache(MeshRuntime &mesh_runtime)
{
if (mesh_runtime.batch_cache) {
BKE_mesh_batch_cache_free(mesh_runtime.batch_cache);
mesh_runtime.batch_cache = nullptr;
}
}
MeshRuntime::MeshRuntime() = default;
MeshRuntime::~MeshRuntime()
{
free_mesh_eval(*this);
free_bvh_cache(*this);
free_batch_cache(*this);
}
static int reset_bits_and_count(MutableBitSpan bits, const Span<int> indices_to_reset)
{
int count = bits.size();
for (const int i : indices_to_reset) {
if (bits[i]) {
bits[i].reset();
count--;
}
}
return count;
}
static void bit_vector_with_reset_bits_or_empty(const Span<int> indices_to_reset,
const int indexed_elems_num,
BitVector<> &r_bits,
int &r_count)
{
r_bits.resize(0);
r_bits.resize(indexed_elems_num, true);
r_count = reset_bits_and_count(r_bits, indices_to_reset);
if (r_count == 0) {
r_bits.clear_and_shrink();
}
}
/**
* If there are no loose edges and no loose vertices, all vertices are used by faces.
*/
static void try_tag_verts_no_face_none(const Mesh &mesh)
{
if (!mesh.runtime->loose_edges_cache.is_cached() || mesh.loose_edges().count > 0) {
return;
}
if (!mesh.runtime->loose_verts_cache.is_cached() || mesh.loose_verts().count > 0) {
return;
}
mesh.runtime->verts_no_face_cache.ensure([&](LooseVertCache &r_data) {
r_data.is_loose_bits.clear_and_shrink();
r_data.count = 0;
});
}
} // 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_corner_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->verts_num + 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->corners_num);
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->corner_to_face_map_cache.data().as_span(),
this->runtime->vert_to_corner_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;
this->runtime->loose_verts_cache.ensure([&](LooseVertCache &r_data) {
const Span<int> verts = this->edges().cast<int>();
bit_vector_with_reset_bits_or_empty(
verts, this->verts_num, r_data.is_loose_bits, r_data.count);
});
return this->runtime->loose_verts_cache.data();
}
const blender::bke::LooseVertCache &Mesh::verts_no_face() const
{
using namespace blender::bke;
this->runtime->verts_no_face_cache.ensure([&](LooseVertCache &r_data) {
const Span<int> verts = this->corner_verts();
bit_vector_with_reset_bits_or_empty(
verts, this->verts_num, r_data.is_loose_bits, r_data.count);
});
return this->runtime->verts_no_face_cache.data();
}
bool Mesh::no_overlapping_topology() const
{
return this->flag & ME_NO_OVERLAPPING_TOPOLOGY;
}
const blender::bke::LooseEdgeCache &Mesh::loose_edges() const
{
using namespace blender::bke;
this->runtime->loose_edges_cache.ensure([&](LooseEdgeCache &r_data) {
const Span<int> edges = this->corner_edges();
bit_vector_with_reset_bits_or_empty(
edges, this->edges_num, r_data.is_loose_bits, r_data.count);
});
return this->runtime->loose_edges_cache.data();
}
void Mesh::tag_loose_verts_none() const
{
using namespace blender::bke;
this->runtime->loose_verts_cache.ensure([&](LooseVertCache &r_data) {
r_data.is_loose_bits.clear_and_shrink();
r_data.count = 0;
});
try_tag_verts_no_face_none(*this);
}
void Mesh::tag_loose_edges_none() const
{
using namespace blender::bke;
this->runtime->loose_edges_cache.ensure([&](LooseEdgeCache &r_data) {
r_data.is_loose_bits.clear_and_shrink();
r_data.count = 0;
});
try_tag_verts_no_face_none(*this);
}
void Mesh::tag_overlapping_none()
{
using namespace blender::bke;
this->flag |= ME_NO_OVERLAPPING_TOPOLOGY;
}
blender::Span<blender::int3> Mesh::corner_tris() const
{
this->runtime->corner_tris_cache.ensure([&](blender::Array<blender::int3> &r_data) {
const Span<float3> positions = this->vert_positions();
const blender::OffsetIndices faces = this->faces();
const Span<int> corner_verts = this->corner_verts();
r_data.reinitialize(poly_to_tri_count(faces.size(), corner_verts.size()));
if (BKE_mesh_face_normals_are_dirty(this)) {
blender::bke::mesh::corner_tris_calc(positions, faces, corner_verts, r_data);
}
else {
blender::bke::mesh::corner_tris_calc_with_normals(
positions, faces, corner_verts, this->face_normals(), r_data);
}
});
return this->runtime->corner_tris_cache.data();
}
blender::Span<int> Mesh::corner_tri_faces() const
{
using namespace blender;
this->runtime->corner_tri_faces_cache.ensure([&](blender::Array<int> &r_data) {
const OffsetIndices faces = this->faces();
r_data.reinitialize(poly_to_tri_count(faces.size(), this->corners_num));
bke::mesh::corner_tris_calc_face_indices(faces, r_data);
});
return this->runtime->corner_tri_faces_cache.data();
}
int BKE_mesh_runtime_corner_tris_len(const Mesh *mesh)
{
/* Allow returning the size without calculating the cache. */
return poly_to_tri_count(mesh->faces_num, mesh->corners_num);
}
void BKE_mesh_runtime_ensure_edit_data(Mesh *mesh)
{
if (!mesh->runtime->edit_data) {
mesh->runtime->edit_data = std::make_unique<blender::bke::EditMeshData>();
}
}
void BKE_mesh_runtime_clear_cache(Mesh *mesh)
{
using namespace blender::bke;
free_mesh_eval(*mesh->runtime);
free_batch_cache(*mesh->runtime);
mesh->runtime->edit_data.reset();
BKE_mesh_runtime_clear_geometry(mesh);
}
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);
mesh->runtime->subdiv_ccg.reset();
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->corner_normals_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();
mesh->runtime->corner_tris_cache.tag_dirty();
mesh->runtime->corner_tri_faces_cache.tag_dirty();
mesh->runtime->shrinkwrap_boundary_cache.tag_dirty();
mesh->runtime->subsurf_face_dot_tags.clear_and_shrink();
mesh->runtime->subsurf_optimal_display_edges.clear_and_shrink();
mesh->flag &= ~ME_NO_OVERLAPPING_TOPOLOGY;
}
void Mesh::tag_edges_split()
{
/* Triangulation didn't change because vertex positions and loop vertex indices didn't change. */
free_bvh_cache(*this->runtime);
this->runtime->vert_normals_cache.tag_dirty();
this->runtime->subdiv_ccg.reset();
this->runtime->vert_to_face_offset_cache.tag_dirty();
this->runtime->vert_to_face_map_cache.tag_dirty();
this->runtime->vert_to_corner_map_cache.tag_dirty();
if (this->runtime->loose_edges_cache.is_cached() &&
this->runtime->loose_edges_cache.data().count != 0)
{
this->runtime->loose_edges_cache.tag_dirty();
}
if (this->runtime->loose_verts_cache.is_cached() &&
this->runtime->loose_verts_cache.data().count != 0)
{
this->runtime->loose_verts_cache.tag_dirty();
}
if (this->runtime->verts_no_face_cache.is_cached() &&
this->runtime->verts_no_face_cache.data().count != 0)
{
this->runtime->verts_no_face_cache.tag_dirty();
}
this->runtime->subsurf_face_dot_tags.clear_and_shrink();
this->runtime->subsurf_optimal_display_edges.clear_and_shrink();
this->runtime->shrinkwrap_boundary_cache.tag_dirty();
}
void Mesh::tag_sharpness_changed()
{
this->runtime->corner_normals_cache.tag_dirty();
}
void Mesh::tag_custom_normals_changed()
{
this->runtime->corner_normals_cache.tag_dirty();
}
void Mesh::tag_face_winding_changed()
{
this->runtime->vert_normals_cache.tag_dirty();
this->runtime->face_normals_cache.tag_dirty();
this->runtime->corner_normals_cache.tag_dirty();
this->runtime->vert_to_corner_map_cache.tag_dirty();
this->runtime->shrinkwrap_boundary_cache.tag_dirty();
}
void Mesh::tag_positions_changed()
{
this->runtime->vert_normals_cache.tag_dirty();
this->runtime->face_normals_cache.tag_dirty();
this->runtime->corner_normals_cache.tag_dirty();
this->runtime->shrinkwrap_boundary_cache.tag_dirty();
this->tag_positions_changed_no_normals();
}
void Mesh::tag_positions_changed_no_normals()
{
free_bvh_cache(*this->runtime);
this->runtime->corner_tris_cache.tag_dirty();
this->runtime->bounds_cache.tag_dirty();
this->runtime->shrinkwrap_boundary_cache.tag_dirty();
}
void Mesh::tag_positions_changed_uniformly()
{
/* The normals and triangulation didn't change, since all verts moved by the same amount. */
free_bvh_cache(*this->runtime);
this->runtime->bounds_cache.tag_dirty();
}
void Mesh::tag_topology_changed()
{
BKE_mesh_runtime_clear_geometry(this);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Mesh Batch Cache Callbacks
* \{ */
/* Draw Engine */
void (*BKE_mesh_batch_cache_dirty_tag_cb)(Mesh *mesh, eMeshBatchDirtyMode mode) = nullptr;
void (*BKE_mesh_batch_cache_free_cb)(void *batch_cache) = nullptr;
void BKE_mesh_batch_cache_dirty_tag(Mesh *mesh, eMeshBatchDirtyMode mode)
{
if (mesh->runtime->batch_cache) {
BKE_mesh_batch_cache_dirty_tag_cb(mesh, mode);
}
}
void BKE_mesh_batch_cache_free(void *batch_cache)
{
BKE_mesh_batch_cache_free_cb(batch_cache);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Mesh Runtime Validation
* \{ */
#ifndef NDEBUG
bool BKE_mesh_runtime_is_valid(Mesh *mesh_eval)
{
const bool do_verbose = true;
const bool do_fixes = false;
bool is_valid = true;
bool changed = true;
if (do_verbose) {
printf("MESH: %s\n", mesh_eval->id.name + 2);
}
MutableSpan<float3> positions = mesh_eval->vert_positions_for_write();
MutableSpan<blender::int2> edges = mesh_eval->edges_for_write();
MutableSpan<int> face_offsets = mesh_eval->face_offsets_for_write();
MutableSpan<int> corner_verts = mesh_eval->corner_verts_for_write();
MutableSpan<int> corner_edges = mesh_eval->corner_edges_for_write();
is_valid &= BKE_mesh_validate_all_customdata(
&mesh_eval->vert_data,
mesh_eval->verts_num,
&mesh_eval->edge_data,
mesh_eval->edges_num,
&mesh_eval->corner_data,
mesh_eval->corners_num,
&mesh_eval->face_data,
mesh_eval->faces_num,
false, /* setting mask here isn't useful, gives false positives */
do_verbose,
do_fixes,
&changed);
is_valid &= BKE_mesh_validate_arrays(
mesh_eval,
reinterpret_cast<float(*)[3]>(positions.data()),
positions.size(),
edges.data(),
edges.size(),
static_cast<MFace *>(CustomData_get_layer_for_write(
&mesh_eval->fdata_legacy, CD_MFACE, mesh_eval->totface_legacy)),
mesh_eval->totface_legacy,
corner_verts.data(),
corner_edges.data(),
corner_verts.size(),
face_offsets.data(),
mesh_eval->faces_num,
mesh_eval->deform_verts_for_write().data(),
do_verbose,
do_fixes,
&changed);
BLI_assert(changed == false);
return is_valid;
}
#endif /* !NDEBUG */
/** \} */