This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/blenkernel/intern/mesh_runtime.cc
Hans Goudey 75db4c082b Cleanup: Use BitVector instead of BLI_bitmap for subsurf face dot tags
For better type safety and more automatic memory management.
2023-02-06 14:13:53 -05:00

368 lines
10 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2005 Blender Foundation. All rights reserved. */
/** \file
* \ingroup bke
*/
#include "atomic_ops.h"
#include "MEM_guardedalloc.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "BLI_math_geom.h"
#include "BLI_task.hh"
#include "BLI_timeit.hh"
#include "BKE_bvhutils.h"
#include "BKE_editmesh_cache.h"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_shrinkwrap.h"
#include "BKE_subdiv_ccg.h"
using blender::float3;
using blender::MutableSpan;
using blender::Span;
/* -------------------------------------------------------------------- */
/** \name Mesh Runtime Struct Utils
* \{ */
namespace blender::bke {
static void edit_data_reset(EditMeshData &edit_data)
{
MEM_SAFE_FREE(edit_data.polyCos);
MEM_SAFE_FREE(edit_data.polyNos);
MEM_SAFE_FREE(edit_data.vertexCos);
MEM_SAFE_FREE(edit_data.vertexNos);
}
static void free_edit_data(MeshRuntime &mesh_runtime)
{
if (mesh_runtime.edit_data) {
edit_data_reset(*mesh_runtime.edit_data);
MEM_freeN(mesh_runtime.edit_data);
mesh_runtime.edit_data = nullptr;
}
}
static void free_mesh_eval(MeshRuntime &mesh_runtime)
{
if (mesh_runtime.mesh_eval != nullptr) {
mesh_runtime.mesh_eval->edit_mesh = nullptr;
BKE_id_free(nullptr, mesh_runtime.mesh_eval);
mesh_runtime.mesh_eval = nullptr;
}
}
static void free_subdiv_ccg(MeshRuntime &mesh_runtime)
{
/* TODO(sergey): Does this really belong here? */
if (mesh_runtime.subdiv_ccg != nullptr) {
BKE_subdiv_ccg_destroy(mesh_runtime.subdiv_ccg);
mesh_runtime.subdiv_ccg = 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_normals(MeshRuntime &mesh_runtime)
{
MEM_SAFE_FREE(mesh_runtime.vert_normals);
MEM_SAFE_FREE(mesh_runtime.poly_normals);
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) {
BKE_mesh_batch_cache_free(mesh_runtime.batch_cache);
mesh_runtime.batch_cache = nullptr;
}
}
MeshRuntime::~MeshRuntime()
{
free_mesh_eval(*this);
free_subdiv_ccg(*this);
free_bvh_cache(*this);
free_edit_data(*this);
free_batch_cache(*this);
free_normals(*this);
if (this->shrinkwrap_data) {
BKE_shrinkwrap_boundary_data_free(this->shrinkwrap_data);
}
}
} // namespace blender::bke
const blender::bke::LooseEdgeCache &Mesh::loose_edges() const
{
using namespace blender::bke;
this->runtime->loose_edges_cache.ensure([&](LooseEdgeCache &r_data) {
blender::BitVector<> &loose_edges = r_data.is_loose_bits;
loose_edges.resize(0);
loose_edges.resize(this->totedge, true);
int count = this->totedge;
for (const MLoop &loop : this->loops()) {
if (loose_edges[loop.e]) {
loose_edges[loop.e].reset();
count--;
}
}
r_data.count = count;
});
return this->runtime->loose_edges_cache.data();
}
void Mesh::loose_edges_tag_none() const
{
using namespace blender::bke;
this->runtime->loose_edges_cache.ensure([&](LooseEdgeCache &r_data) {
r_data.is_loose_bits.resize(0);
r_data.count = 0;
});
}
blender::Span<MLoopTri> Mesh::looptris() const
{
this->runtime->looptris_cache.ensure([&](blender::Array<MLoopTri> &r_data) {
const Span<float3> positions = this->vert_positions();
const Span<MPoly> polys = this->polys();
const Span<MLoop> loops = this->loops();
r_data.reinitialize(poly_to_tri_count(polys.size(), loops.size()));
if (BKE_mesh_poly_normals_are_dirty(this)) {
BKE_mesh_recalc_looptri(loops.data(),
polys.data(),
reinterpret_cast<const float(*)[3]>(positions.data()),
loops.size(),
polys.size(),
r_data.data());
}
else {
BKE_mesh_recalc_looptri_with_normals(loops.data(),
polys.data(),
reinterpret_cast<const float(*)[3]>(positions.data()),
loops.size(),
polys.size(),
r_data.data(),
BKE_mesh_poly_normals_ensure(this));
}
});
return this->runtime->looptris_cache.data();
}
int BKE_mesh_runtime_looptri_len(const Mesh *mesh)
{
/* Allow returning the size without calculating the cache. */
return poly_to_tri_count(mesh->totpoly, mesh->totloop);
}
const MLoopTri *BKE_mesh_runtime_looptri_ensure(const Mesh *mesh)
{
return mesh->looptris().data();
}
void BKE_mesh_runtime_verttri_from_looptri(MVertTri *r_verttri,
const MLoop *mloop,
const MLoopTri *looptri,
int looptri_num)
{
for (int i = 0; i < looptri_num; i++) {
r_verttri[i].tri[0] = mloop[looptri[i].tri[0]].v;
r_verttri[i].tri[1] = mloop[looptri[i].tri[1]].v;
r_verttri[i].tri[2] = mloop[looptri[i].tri[2]].v;
}
}
bool BKE_mesh_runtime_ensure_edit_data(struct Mesh *mesh)
{
if (mesh->runtime->edit_data != nullptr) {
return false;
}
mesh->runtime->edit_data = MEM_cnew<EditMeshData>(__func__);
return true;
}
void BKE_mesh_runtime_reset_edit_data(Mesh *mesh)
{
using namespace blender::bke;
if (EditMeshData *edit_data = mesh->runtime->edit_data) {
edit_data_reset(*edit_data);
}
}
void BKE_mesh_runtime_clear_cache(Mesh *mesh)
{
using namespace blender::bke;
free_mesh_eval(*mesh->runtime);
free_batch_cache(*mesh->runtime);
free_edit_data(*mesh->runtime);
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);
free_normals(*mesh->runtime);
free_subdiv_ccg(*mesh->runtime);
mesh->runtime->bounds_cache.tag_dirty();
mesh->runtime->loose_edges_cache.tag_dirty();
mesh->runtime->looptris_cache.tag_dirty();
mesh->runtime->subsurf_face_dot_tags.clear_and_shrink();
if (mesh->runtime->shrinkwrap_data) {
BKE_shrinkwrap_boundary_data_free(mesh->runtime->shrinkwrap_data);
}
}
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. */
free_bvh_cache(*mesh->runtime);
free_normals(*mesh->runtime);
free_subdiv_ccg(*mesh->runtime);
mesh->runtime->loose_edges_cache.tag_dirty();
mesh->runtime->subsurf_face_dot_tags.clear_and_shrink();
if (mesh->runtime->shrinkwrap_data) {
BKE_shrinkwrap_boundary_data_free(mesh->runtime->shrinkwrap_data);
}
}
void BKE_mesh_tag_coords_changed(Mesh *mesh)
{
BKE_mesh_normals_tag_dirty(mesh);
free_bvh_cache(*mesh->runtime);
mesh->runtime->looptris_cache.tag_dirty();
mesh->runtime->bounds_cache.tag_dirty();
}
void BKE_mesh_tag_coords_changed_uniformly(Mesh *mesh)
{
/* The normals and triangulation didn't change, since all verts moved by the same amount. */
free_bvh_cache(*mesh->runtime);
mesh->runtime->bounds_cache.tag_dirty();
}
void BKE_mesh_tag_topology_changed(struct Mesh *mesh)
{
BKE_mesh_runtime_clear_geometry(mesh);
}
bool BKE_mesh_is_deformed_only(const Mesh *mesh)
{
return mesh->runtime->deformed_only;
}
eMeshWrapperType BKE_mesh_wrapper_type(const struct Mesh *mesh)
{
return mesh->runtime->wrapper_type;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Mesh Batch Cache Callbacks
* \{ */
/* Draw Engine */
void (*BKE_mesh_batch_cache_dirty_tag_cb)(Mesh *me, eMeshBatchDirtyMode mode) = nullptr;
void (*BKE_mesh_batch_cache_free_cb)(void *batch_cache) = nullptr;
void BKE_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode)
{
if (me->runtime->batch_cache) {
BKE_mesh_batch_cache_dirty_tag_cb(me, 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 *me_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", me_eval->id.name + 2);
}
MutableSpan<float3> positions = me_eval->vert_positions_for_write();
MutableSpan<MEdge> edges = me_eval->edges_for_write();
MutableSpan<MPoly> polys = me_eval->polys_for_write();
MutableSpan<MLoop> loops = me_eval->loops_for_write();
is_valid &= BKE_mesh_validate_all_customdata(
&me_eval->vdata,
me_eval->totvert,
&me_eval->edata,
me_eval->totedge,
&me_eval->ldata,
me_eval->totloop,
&me_eval->pdata,
me_eval->totpoly,
false, /* setting mask here isn't useful, gives false positives */
do_verbose,
do_fixes,
&changed);
is_valid &= BKE_mesh_validate_arrays(me_eval,
reinterpret_cast<float(*)[3]>(positions.data()),
positions.size(),
edges.data(),
edges.size(),
static_cast<MFace *>(CustomData_get_layer_for_write(
&me_eval->fdata, CD_MFACE, me_eval->totface)),
me_eval->totface,
loops.data(),
loops.size(),
polys.data(),
polys.size(),
me_eval->deform_verts_for_write().data(),
do_verbose,
do_fixes,
&changed);
BLI_assert(changed == false);
return is_valid;
}
#endif /* NDEBUG */
/** \} */