Cleanup: Store more mesh runtime data with unique_ptr
Convert shrinkwrap data arrays to use C++ arrays and BitVector, use references in "EditMeshData" code, and store both structs with `std::unique_ptr` instead of a raw allocation.
This commit is contained in:
@@ -29,10 +29,10 @@ struct EditMeshData {
|
|||||||
|
|
||||||
} // namespace blender::bke
|
} // namespace blender::bke
|
||||||
|
|
||||||
void BKE_editmesh_cache_ensure_face_normals(BMEditMesh *em, blender::bke::EditMeshData *emd);
|
void BKE_editmesh_cache_ensure_face_normals(BMEditMesh &em, blender::bke::EditMeshData &emd);
|
||||||
void BKE_editmesh_cache_ensure_vert_normals(BMEditMesh *em, blender::bke::EditMeshData *emd);
|
void BKE_editmesh_cache_ensure_vert_normals(BMEditMesh &em, blender::bke::EditMeshData &emd);
|
||||||
|
|
||||||
void BKE_editmesh_cache_ensure_face_centers(BMEditMesh *em, blender::bke::EditMeshData *emd);
|
void BKE_editmesh_cache_ensure_face_centers(BMEditMesh &em, blender::bke::EditMeshData &emd);
|
||||||
|
|
||||||
std::optional<blender::Bounds<blender::float3>> BKE_editmesh_cache_calc_minmax(
|
std::optional<blender::Bounds<blender::float3>> BKE_editmesh_cache_calc_minmax(
|
||||||
const BMEditMesh *em, const blender::bke::EditMeshData *emd);
|
const BMEditMesh &em, const blender::bke::EditMeshData &emd);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ struct Scene;
|
|||||||
/** Return the number of derived triangles (looptris). */
|
/** Return the number of derived triangles (looptris). */
|
||||||
int BKE_mesh_runtime_looptri_len(const Mesh *mesh);
|
int BKE_mesh_runtime_looptri_len(const Mesh *mesh);
|
||||||
|
|
||||||
bool BKE_mesh_runtime_ensure_edit_data(Mesh *mesh);
|
void BKE_mesh_runtime_ensure_edit_data(Mesh *mesh);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear and free any derived caches associated with the mesh geometry data. Examples include BVH
|
* Clear and free any derived caches associated with the mesh geometry data. Examples include BVH
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ struct MeshRuntime {
|
|||||||
* Lazily initialized SoA data from the #edit_mesh field in #Mesh. Used when the mesh is a BMesh
|
* Lazily initialized SoA data from the #edit_mesh field in #Mesh. Used when the mesh is a BMesh
|
||||||
* wrapper (#ME_WRAPPER_TYPE_BMESH).
|
* wrapper (#ME_WRAPPER_TYPE_BMESH).
|
||||||
*/
|
*/
|
||||||
EditMeshData *edit_data = nullptr;
|
std::unique_ptr<EditMeshData> edit_data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data used to efficiently draw the mesh in the viewport, especially useful when
|
* Data used to efficiently draw the mesh in the viewport, especially useful when
|
||||||
@@ -134,7 +134,7 @@ struct MeshRuntime {
|
|||||||
BVHCache *bvh_cache = nullptr;
|
BVHCache *bvh_cache = nullptr;
|
||||||
|
|
||||||
/** Cache of non-manifold boundary data for Shrink-wrap Target Project. */
|
/** Cache of non-manifold boundary data for Shrink-wrap Target Project. */
|
||||||
ShrinkwrapBoundaryData *shrinkwrap_data = nullptr;
|
std::unique_ptr<ShrinkwrapBoundaryData> shrinkwrap_data;
|
||||||
|
|
||||||
/** Needed in case we need to lazily initialize the mesh. */
|
/** Needed in case we need to lazily initialize the mesh. */
|
||||||
CustomData_MeshMasks cd_mask_extra = {};
|
CustomData_MeshMasks cd_mask_extra = {};
|
||||||
|
|||||||
@@ -9,8 +9,9 @@
|
|||||||
|
|
||||||
/* Shrinkwrap stuff */
|
/* Shrinkwrap stuff */
|
||||||
#include "BKE_bvhutils.hh"
|
#include "BKE_bvhutils.hh"
|
||||||
#include "BLI_bitmap.h"
|
|
||||||
|
|
||||||
|
#include "BLI_array.hh"
|
||||||
|
#include "BLI_bit_vector.hh"
|
||||||
#include "BLI_math_vector_types.hh"
|
#include "BLI_math_vector_types.hh"
|
||||||
#include "BLI_offset_indices.hh"
|
#include "BLI_offset_indices.hh"
|
||||||
#include "BLI_span.hh"
|
#include "BLI_span.hh"
|
||||||
@@ -48,23 +49,21 @@ struct ShrinkwrapBoundaryVertData {
|
|||||||
|
|
||||||
struct ShrinkwrapBoundaryData {
|
struct ShrinkwrapBoundaryData {
|
||||||
/* True if the edge belongs to exactly one face. */
|
/* True if the edge belongs to exactly one face. */
|
||||||
const BLI_bitmap *edge_is_boundary;
|
blender::BitVector<> edge_is_boundary;
|
||||||
/* True if the looptri has any boundary edges. */
|
/* True if the looptri has any boundary edges. */
|
||||||
const BLI_bitmap *looptri_has_boundary;
|
blender::BitVector<> looptri_has_boundary;
|
||||||
|
|
||||||
/* Mapping from vertex index to boundary vertex index, or -1.
|
/* Mapping from vertex index to boundary vertex index, or -1.
|
||||||
* Used for compact storage of data about boundary vertices. */
|
* Used for compact storage of data about boundary vertices. */
|
||||||
const int *vert_boundary_id;
|
blender::Array<int> vert_boundary_id;
|
||||||
unsigned int num_boundary_verts;
|
|
||||||
|
|
||||||
/* Direction data about boundary vertices. */
|
/* Direction data about boundary vertices. */
|
||||||
const ShrinkwrapBoundaryVertData *boundary_verts;
|
blender::Array<ShrinkwrapBoundaryVertData> boundary_verts;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free boundary data for target project.
|
* Free boundary data for target project.
|
||||||
*/
|
*/
|
||||||
void BKE_shrinkwrap_boundary_data_free(ShrinkwrapBoundaryData *data);
|
|
||||||
void BKE_shrinkwrap_compute_boundary_data(Mesh *mesh);
|
void BKE_shrinkwrap_compute_boundary_data(Mesh *mesh);
|
||||||
|
|
||||||
/* Information about a mesh and BVH tree. */
|
/* Information about a mesh and BVH tree. */
|
||||||
|
|||||||
@@ -1035,9 +1035,9 @@ static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final)
|
|||||||
case ME_WRAPPER_TYPE_MDATA:
|
case ME_WRAPPER_TYPE_MDATA:
|
||||||
break;
|
break;
|
||||||
case ME_WRAPPER_TYPE_BMESH: {
|
case ME_WRAPPER_TYPE_BMESH: {
|
||||||
BMEditMesh *em = mesh_final->edit_mesh;
|
BMEditMesh &em = *mesh_final->edit_mesh;
|
||||||
blender::bke::EditMeshData *emd = mesh_final->runtime->edit_data;
|
blender::bke::EditMeshData &emd = *mesh_final->runtime->edit_data;
|
||||||
if (!emd->vertexCos.is_empty()) {
|
if (!emd.vertexCos.is_empty()) {
|
||||||
BKE_editmesh_cache_ensure_vert_normals(em, emd);
|
BKE_editmesh_cache_ensure_vert_normals(em, emd);
|
||||||
BKE_editmesh_cache_ensure_face_normals(em, emd);
|
BKE_editmesh_cache_ensure_face_normals(em, emd);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,14 +23,14 @@
|
|||||||
/** \name Ensure Data (derived from coords)
|
/** \name Ensure Data (derived from coords)
|
||||||
* \{ */
|
* \{ */
|
||||||
|
|
||||||
void BKE_editmesh_cache_ensure_face_normals(BMEditMesh *em, blender::bke::EditMeshData *emd)
|
void BKE_editmesh_cache_ensure_face_normals(BMEditMesh &em, blender::bke::EditMeshData &emd)
|
||||||
{
|
{
|
||||||
if (emd->vertexCos.is_empty() || !emd->faceNos.is_empty()) {
|
if (emd.vertexCos.is_empty() || !emd.faceNos.is_empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
BMesh *bm = em->bm;
|
BMesh *bm = em.bm;
|
||||||
|
|
||||||
emd->faceNos.reinitialize(bm->totface);
|
emd.faceNos.reinitialize(bm->totface);
|
||||||
|
|
||||||
BM_mesh_elem_index_ensure(bm, BM_VERT);
|
BM_mesh_elem_index_ensure(bm, BM_VERT);
|
||||||
BMFace *efa;
|
BMFace *efa;
|
||||||
@@ -39,52 +39,52 @@ void BKE_editmesh_cache_ensure_face_normals(BMEditMesh *em, blender::bke::EditMe
|
|||||||
BM_ITER_MESH_INDEX (efa, &fiter, bm, BM_FACES_OF_MESH, i) {
|
BM_ITER_MESH_INDEX (efa, &fiter, bm, BM_FACES_OF_MESH, i) {
|
||||||
BM_elem_index_set(efa, i); /* set_inline */
|
BM_elem_index_set(efa, i); /* set_inline */
|
||||||
BM_face_calc_normal_vcos(
|
BM_face_calc_normal_vcos(
|
||||||
bm, efa, emd->faceNos[i], reinterpret_cast<const float(*)[3]>(emd->vertexCos.data()));
|
bm, efa, emd.faceNos[i], reinterpret_cast<const float(*)[3]>(emd.vertexCos.data()));
|
||||||
}
|
}
|
||||||
bm->elem_index_dirty &= ~BM_FACE;
|
bm->elem_index_dirty &= ~BM_FACE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BKE_editmesh_cache_ensure_vert_normals(BMEditMesh *em, blender::bke::EditMeshData *emd)
|
void BKE_editmesh_cache_ensure_vert_normals(BMEditMesh &em, blender::bke::EditMeshData &emd)
|
||||||
{
|
{
|
||||||
if (emd->vertexCos.is_empty() || !emd->vertexNos.is_empty()) {
|
if (emd.vertexCos.is_empty() || !emd.vertexNos.is_empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
BMesh *bm = em->bm;
|
BMesh *bm = em.bm;
|
||||||
|
|
||||||
/* Calculate vertex normals from face normals. */
|
/* Calculate vertex normals from face normals. */
|
||||||
BKE_editmesh_cache_ensure_face_normals(em, emd);
|
BKE_editmesh_cache_ensure_face_normals(em, emd);
|
||||||
|
|
||||||
emd->vertexNos.reinitialize(bm->totvert);
|
emd.vertexNos.reinitialize(bm->totvert);
|
||||||
|
|
||||||
BM_mesh_elem_index_ensure(bm, BM_FACE);
|
BM_mesh_elem_index_ensure(bm, BM_FACE);
|
||||||
BM_verts_calc_normal_vcos(bm,
|
BM_verts_calc_normal_vcos(bm,
|
||||||
reinterpret_cast<const float(*)[3]>(emd->faceNos.data()),
|
reinterpret_cast<const float(*)[3]>(emd.faceNos.data()),
|
||||||
reinterpret_cast<const float(*)[3]>(emd->vertexCos.data()),
|
reinterpret_cast<const float(*)[3]>(emd.vertexCos.data()),
|
||||||
reinterpret_cast<float(*)[3]>(emd->vertexNos.data()));
|
reinterpret_cast<float(*)[3]>(emd.vertexNos.data()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BKE_editmesh_cache_ensure_face_centers(BMEditMesh *em, blender::bke::EditMeshData *emd)
|
void BKE_editmesh_cache_ensure_face_centers(BMEditMesh &em, blender::bke::EditMeshData &emd)
|
||||||
{
|
{
|
||||||
if (!emd->faceCos.is_empty()) {
|
if (!emd.faceCos.is_empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
BMesh *bm = em->bm;
|
BMesh *bm = em.bm;
|
||||||
|
|
||||||
emd->faceCos.reinitialize(bm->totface);
|
emd.faceCos.reinitialize(bm->totface);
|
||||||
|
|
||||||
BMFace *efa;
|
BMFace *efa;
|
||||||
BMIter fiter;
|
BMIter fiter;
|
||||||
int i;
|
int i;
|
||||||
if (emd->vertexCos.is_empty()) {
|
if (emd.vertexCos.is_empty()) {
|
||||||
BM_ITER_MESH_INDEX (efa, &fiter, bm, BM_FACES_OF_MESH, i) {
|
BM_ITER_MESH_INDEX (efa, &fiter, bm, BM_FACES_OF_MESH, i) {
|
||||||
BM_face_calc_center_median(efa, emd->faceCos[i]);
|
BM_face_calc_center_median(efa, emd.faceCos[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
BM_mesh_elem_index_ensure(bm, BM_VERT);
|
BM_mesh_elem_index_ensure(bm, BM_VERT);
|
||||||
BM_ITER_MESH_INDEX (efa, &fiter, bm, BM_FACES_OF_MESH, i) {
|
BM_ITER_MESH_INDEX (efa, &fiter, bm, BM_FACES_OF_MESH, i) {
|
||||||
BM_face_calc_center_median_vcos(
|
BM_face_calc_center_median_vcos(
|
||||||
bm, efa, emd->faceCos[i], reinterpret_cast<const float(*)[3]>(emd->vertexCos.data()));
|
bm, efa, emd.faceCos[i], reinterpret_cast<const float(*)[3]>(emd.vertexCos.data()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,15 +96,15 @@ void BKE_editmesh_cache_ensure_face_centers(BMEditMesh *em, blender::bke::EditMe
|
|||||||
* \{ */
|
* \{ */
|
||||||
|
|
||||||
std::optional<blender::Bounds<blender::float3>> BKE_editmesh_cache_calc_minmax(
|
std::optional<blender::Bounds<blender::float3>> BKE_editmesh_cache_calc_minmax(
|
||||||
const BMEditMesh *em, const blender::bke::EditMeshData *emd)
|
const BMEditMesh &em, const blender::bke::EditMeshData &emd)
|
||||||
{
|
{
|
||||||
using namespace blender;
|
using namespace blender;
|
||||||
BMesh *bm = em->bm;
|
BMesh *bm = em.bm;
|
||||||
if (bm->totvert == 0) {
|
if (bm->totvert == 0) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (emd->vertexCos.is_empty()) {
|
if (emd.vertexCos.is_empty()) {
|
||||||
BMVert *eve;
|
BMVert *eve;
|
||||||
BMIter iter;
|
BMIter iter;
|
||||||
float3 min(std::numeric_limits<float>::max());
|
float3 min(std::numeric_limits<float>::max());
|
||||||
@@ -115,7 +115,7 @@ std::optional<blender::Bounds<blender::float3>> BKE_editmesh_cache_calc_minmax(
|
|||||||
return Bounds<float3>{min, max};
|
return Bounds<float3>{min, max};
|
||||||
}
|
}
|
||||||
|
|
||||||
return bounds::min_max(emd->vertexCos.as_span());
|
return bounds::min_max(emd.vertexCos.as_span());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
|
|||||||
@@ -1090,7 +1090,7 @@ std::optional<blender::Bounds<blender::float3>> Mesh::bounds_min_max() const
|
|||||||
this->runtime->bounds_cache.ensure([&](Bounds<float3> &r_bounds) {
|
this->runtime->bounds_cache.ensure([&](Bounds<float3> &r_bounds) {
|
||||||
switch (this->runtime->wrapper_type) {
|
switch (this->runtime->wrapper_type) {
|
||||||
case ME_WRAPPER_TYPE_BMESH:
|
case ME_WRAPPER_TYPE_BMESH:
|
||||||
r_bounds = *BKE_editmesh_cache_calc_minmax(this->edit_mesh, this->runtime->edit_data);
|
r_bounds = *BKE_editmesh_cache_calc_minmax(*this->edit_mesh, *this->runtime->edit_data);
|
||||||
break;
|
break;
|
||||||
case ME_WRAPPER_TYPE_MDATA:
|
case ME_WRAPPER_TYPE_MDATA:
|
||||||
case ME_WRAPPER_TYPE_SUBD:
|
case ME_WRAPPER_TYPE_SUBD:
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ void BKE_mesh_foreach_mapped_vert(
|
|||||||
const blender::Span<blender::float3> positions = mesh->runtime->edit_data->vertexCos;
|
const blender::Span<blender::float3> positions = mesh->runtime->edit_data->vertexCos;
|
||||||
blender::Span<blender::float3> vert_normals;
|
blender::Span<blender::float3> vert_normals;
|
||||||
if (flag & MESH_FOREACH_USE_NORMAL) {
|
if (flag & MESH_FOREACH_USE_NORMAL) {
|
||||||
BKE_editmesh_cache_ensure_vert_normals(em, mesh->runtime->edit_data);
|
BKE_editmesh_cache_ensure_vert_normals(*em, *mesh->runtime->edit_data);
|
||||||
vert_normals = mesh->runtime->edit_data->vertexNos;
|
vert_normals = mesh->runtime->edit_data->vertexNos;
|
||||||
}
|
}
|
||||||
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
|
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
|
||||||
@@ -245,11 +245,11 @@ void BKE_mesh_foreach_mapped_face_center(
|
|||||||
BMIter iter;
|
BMIter iter;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
BKE_editmesh_cache_ensure_face_centers(em, mesh->runtime->edit_data);
|
BKE_editmesh_cache_ensure_face_centers(*em, *mesh->runtime->edit_data);
|
||||||
face_centers = mesh->runtime->edit_data->faceCos; /* always set */
|
face_centers = mesh->runtime->edit_data->faceCos; /* always set */
|
||||||
|
|
||||||
if (flag & MESH_FOREACH_USE_NORMAL) {
|
if (flag & MESH_FOREACH_USE_NORMAL) {
|
||||||
BKE_editmesh_cache_ensure_face_normals(em, mesh->runtime->edit_data);
|
BKE_editmesh_cache_ensure_face_normals(*em, *mesh->runtime->edit_data);
|
||||||
face_normals = mesh->runtime->edit_data->faceNos; /* maybe nullptr */
|
face_normals = mesh->runtime->edit_data->faceNos; /* maybe nullptr */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,9 +70,6 @@ MeshRuntime::~MeshRuntime()
|
|||||||
free_mesh_eval(*this);
|
free_mesh_eval(*this);
|
||||||
free_bvh_cache(*this);
|
free_bvh_cache(*this);
|
||||||
free_batch_cache(*this);
|
free_batch_cache(*this);
|
||||||
if (this->shrinkwrap_data) {
|
|
||||||
BKE_shrinkwrap_boundary_data_free(this->shrinkwrap_data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int reset_bits_and_count(MutableBitSpan bits, const Span<int> indices_to_reset)
|
static int reset_bits_and_count(MutableBitSpan bits, const Span<int> indices_to_reset)
|
||||||
@@ -282,13 +279,11 @@ void BKE_mesh_runtime_verttri_from_looptri(MVertTri *r_verttri,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BKE_mesh_runtime_ensure_edit_data(Mesh *mesh)
|
void BKE_mesh_runtime_ensure_edit_data(Mesh *mesh)
|
||||||
{
|
{
|
||||||
if (mesh->runtime->edit_data != nullptr) {
|
if (!mesh->runtime->edit_data) {
|
||||||
return false;
|
mesh->runtime->edit_data = std::make_unique<blender::bke::EditMeshData>();
|
||||||
}
|
}
|
||||||
mesh->runtime->edit_data = MEM_new<blender::bke::EditMeshData>(__func__);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BKE_mesh_runtime_clear_cache(Mesh *mesh)
|
void BKE_mesh_runtime_clear_cache(Mesh *mesh)
|
||||||
@@ -296,8 +291,7 @@ void BKE_mesh_runtime_clear_cache(Mesh *mesh)
|
|||||||
using namespace blender::bke;
|
using namespace blender::bke;
|
||||||
free_mesh_eval(*mesh->runtime);
|
free_mesh_eval(*mesh->runtime);
|
||||||
free_batch_cache(*mesh->runtime);
|
free_batch_cache(*mesh->runtime);
|
||||||
MEM_delete(mesh->runtime->edit_data);
|
mesh->runtime->edit_data.reset();
|
||||||
mesh->runtime->edit_data = nullptr;
|
|
||||||
BKE_mesh_runtime_clear_geometry(mesh);
|
BKE_mesh_runtime_clear_geometry(mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,10 +314,7 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh)
|
|||||||
mesh->runtime->looptri_faces_cache.tag_dirty();
|
mesh->runtime->looptri_faces_cache.tag_dirty();
|
||||||
mesh->runtime->subsurf_face_dot_tags.clear_and_shrink();
|
mesh->runtime->subsurf_face_dot_tags.clear_and_shrink();
|
||||||
mesh->runtime->subsurf_optimal_display_edges.clear_and_shrink();
|
mesh->runtime->subsurf_optimal_display_edges.clear_and_shrink();
|
||||||
if (mesh->runtime->shrinkwrap_data) {
|
mesh->runtime->shrinkwrap_data.reset();
|
||||||
BKE_shrinkwrap_boundary_data_free(mesh->runtime->shrinkwrap_data);
|
|
||||||
mesh->runtime->shrinkwrap_data = nullptr;
|
|
||||||
}
|
|
||||||
mesh->flag &= ~ME_NO_OVERLAPPING_TOPOLOGY;
|
mesh->flag &= ~ME_NO_OVERLAPPING_TOPOLOGY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,10 +344,7 @@ void BKE_mesh_tag_edges_split(Mesh *mesh)
|
|||||||
}
|
}
|
||||||
mesh->runtime->subsurf_face_dot_tags.clear_and_shrink();
|
mesh->runtime->subsurf_face_dot_tags.clear_and_shrink();
|
||||||
mesh->runtime->subsurf_optimal_display_edges.clear_and_shrink();
|
mesh->runtime->subsurf_optimal_display_edges.clear_and_shrink();
|
||||||
if (mesh->runtime->shrinkwrap_data) {
|
mesh->runtime->shrinkwrap_data.reset();
|
||||||
BKE_shrinkwrap_boundary_data_free(mesh->runtime->shrinkwrap_data);
|
|
||||||
mesh->runtime->shrinkwrap_data = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BKE_mesh_tag_sharpness_changed(Mesh *mesh)
|
void BKE_mesh_tag_sharpness_changed(Mesh *mesh)
|
||||||
|
|||||||
@@ -125,9 +125,9 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me)
|
|||||||
* be wrong, but it's not harmful. */
|
* be wrong, but it's not harmful. */
|
||||||
BKE_mesh_ensure_default_orig_index_customdata_no_check(me);
|
BKE_mesh_ensure_default_orig_index_customdata_no_check(me);
|
||||||
|
|
||||||
blender::bke::EditMeshData *edit_data = me->runtime->edit_data;
|
blender::bke::EditMeshData &edit_data = *me->runtime->edit_data;
|
||||||
if (!edit_data->vertexCos.is_empty()) {
|
if (!edit_data.vertexCos.is_empty()) {
|
||||||
me->vert_positions_for_write().copy_from(edit_data->vertexCos);
|
me->vert_positions_for_write().copy_from(edit_data.vertexCos);
|
||||||
me->runtime->is_original_bmesh = false;
|
me->runtime->is_original_bmesh = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,8 +135,7 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me)
|
|||||||
BKE_mesh_wrapper_deferred_finalize_mdata(me);
|
BKE_mesh_wrapper_deferred_finalize_mdata(me);
|
||||||
}
|
}
|
||||||
|
|
||||||
MEM_delete(me->runtime->edit_data);
|
me->runtime->edit_data.reset();
|
||||||
me->runtime->edit_data = nullptr;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,7 +169,7 @@ const float (*BKE_mesh_wrapper_face_normals(Mesh *mesh))[3]
|
|||||||
{
|
{
|
||||||
switch (mesh->runtime->wrapper_type) {
|
switch (mesh->runtime->wrapper_type) {
|
||||||
case ME_WRAPPER_TYPE_BMESH:
|
case ME_WRAPPER_TYPE_BMESH:
|
||||||
BKE_editmesh_cache_ensure_face_normals(mesh->edit_mesh, mesh->runtime->edit_data);
|
BKE_editmesh_cache_ensure_face_normals(*mesh->edit_mesh, *mesh->runtime->edit_data);
|
||||||
if (mesh->runtime->edit_data->faceNos.is_empty()) {
|
if (mesh->runtime->edit_data->faceNos.is_empty()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -204,9 +203,9 @@ void BKE_mesh_wrapper_vert_coords_copy(const Mesh *me, blender::MutableSpan<floa
|
|||||||
switch (me->runtime->wrapper_type) {
|
switch (me->runtime->wrapper_type) {
|
||||||
case ME_WRAPPER_TYPE_BMESH: {
|
case ME_WRAPPER_TYPE_BMESH: {
|
||||||
BMesh *bm = me->edit_mesh->bm;
|
BMesh *bm = me->edit_mesh->bm;
|
||||||
blender::bke::EditMeshData *edit_data = me->runtime->edit_data;
|
const blender::bke::EditMeshData &edit_data = *me->runtime->edit_data;
|
||||||
if (!edit_data->vertexCos.is_empty()) {
|
if (!edit_data.vertexCos.is_empty()) {
|
||||||
positions.copy_from(edit_data->vertexCos);
|
positions.copy_from(edit_data.vertexCos);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
BMIter iter;
|
BMIter iter;
|
||||||
@@ -236,10 +235,10 @@ void BKE_mesh_wrapper_vert_coords_copy_with_mat4(const Mesh *me,
|
|||||||
case ME_WRAPPER_TYPE_BMESH: {
|
case ME_WRAPPER_TYPE_BMESH: {
|
||||||
BMesh *bm = me->edit_mesh->bm;
|
BMesh *bm = me->edit_mesh->bm;
|
||||||
BLI_assert(vert_coords_len == bm->totvert);
|
BLI_assert(vert_coords_len == bm->totvert);
|
||||||
const blender::bke::EditMeshData *edit_data = me->runtime->edit_data;
|
const blender::bke::EditMeshData &edit_data = *me->runtime->edit_data;
|
||||||
if (!edit_data->vertexCos.is_empty()) {
|
if (!edit_data.vertexCos.is_empty()) {
|
||||||
for (int i = 0; i < vert_coords_len; i++) {
|
for (int i = 0; i < vert_coords_len; i++) {
|
||||||
mul_v3_m4v3(vert_coords[i], mat, edit_data->vertexCos[i]);
|
mul_v3_m4v3(vert_coords[i], mat, edit_data.vertexCos[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -965,11 +965,11 @@ static void modwrap_dependsOnNormals(Mesh *me)
|
|||||||
{
|
{
|
||||||
switch (me->runtime->wrapper_type) {
|
switch (me->runtime->wrapper_type) {
|
||||||
case ME_WRAPPER_TYPE_BMESH: {
|
case ME_WRAPPER_TYPE_BMESH: {
|
||||||
blender::bke::EditMeshData *edit_data = me->runtime->edit_data;
|
blender::bke::EditMeshData &edit_data = *me->runtime->edit_data;
|
||||||
if (!edit_data->vertexCos.is_empty()) {
|
if (!edit_data.vertexCos.is_empty()) {
|
||||||
/* Note that 'ensure' is acceptable here since these values aren't modified in-place.
|
/* Note that 'ensure' is acceptable here since these values aren't modified in-place.
|
||||||
* If that changes we'll need to recalculate. */
|
* If that changes we'll need to recalculate. */
|
||||||
BKE_editmesh_cache_ensure_vert_normals(me->edit_mesh, edit_data);
|
BKE_editmesh_cache_ensure_vert_normals(*me->edit_mesh, edit_data);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
BM_mesh_normals_update(me->edit_mesh->bm);
|
BM_mesh_normals_update(me->edit_mesh->bm);
|
||||||
|
|||||||
@@ -478,7 +478,7 @@ static const Mesh *mesh_data_from_duplicator_object(Object *ob,
|
|||||||
* We could change this but it matches 2.7x behavior. */
|
* We could change this but it matches 2.7x behavior. */
|
||||||
me_eval = BKE_object_get_editmesh_eval_cage(ob);
|
me_eval = BKE_object_get_editmesh_eval_cage(ob);
|
||||||
if ((me_eval == nullptr) || (me_eval->runtime->wrapper_type == ME_WRAPPER_TYPE_BMESH)) {
|
if ((me_eval == nullptr) || (me_eval->runtime->wrapper_type == ME_WRAPPER_TYPE_BMESH)) {
|
||||||
blender::bke::EditMeshData *emd = me_eval ? me_eval->runtime->edit_data : nullptr;
|
blender::bke::EditMeshData *emd = me_eval ? me_eval->runtime->edit_data.get() : nullptr;
|
||||||
|
|
||||||
/* Only assign edit-mesh in the case we can't use `me_eval`. */
|
/* Only assign edit-mesh in the case we can't use `me_eval`. */
|
||||||
*r_em = em;
|
*r_em = em;
|
||||||
@@ -487,7 +487,7 @@ static const Mesh *mesh_data_from_duplicator_object(Object *ob,
|
|||||||
if ((emd != nullptr) && !emd->vertexCos.is_empty()) {
|
if ((emd != nullptr) && !emd->vertexCos.is_empty()) {
|
||||||
*r_vert_coords = reinterpret_cast<const float(*)[3]>(emd->vertexCos.data());
|
*r_vert_coords = reinterpret_cast<const float(*)[3]>(emd->vertexCos.data());
|
||||||
if (r_vert_normals != nullptr) {
|
if (r_vert_normals != nullptr) {
|
||||||
BKE_editmesh_cache_ensure_vert_normals(em, emd);
|
BKE_editmesh_cache_ensure_vert_normals(*em, *emd);
|
||||||
*r_vert_normals = reinterpret_cast<const float(*)[3]>(emd->vertexNos.data());
|
*r_vert_normals = reinterpret_cast<const float(*)[3]>(emd->vertexNos.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ bool BKE_shrinkwrap_init_tree(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (shrinkType == MOD_SHRINKWRAP_TARGET_PROJECT) {
|
if (shrinkType == MOD_SHRINKWRAP_TARGET_PROJECT) {
|
||||||
data->boundary = mesh->runtime->shrinkwrap_data;
|
data->boundary = mesh->runtime->shrinkwrap_data.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -159,16 +159,6 @@ void BKE_shrinkwrap_free_tree(ShrinkwrapTreeData *data)
|
|||||||
free_bvhtree_from_mesh(&data->treeData);
|
free_bvhtree_from_mesh(&data->treeData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BKE_shrinkwrap_boundary_data_free(ShrinkwrapBoundaryData *data)
|
|
||||||
{
|
|
||||||
MEM_freeN((void *)data->edge_is_boundary);
|
|
||||||
MEM_freeN((void *)data->looptri_has_boundary);
|
|
||||||
MEM_freeN((void *)data->vert_boundary_id);
|
|
||||||
MEM_freeN((void *)data->boundary_verts);
|
|
||||||
|
|
||||||
MEM_freeN(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Accumulate edge for average boundary edge direction. */
|
/* Accumulate edge for average boundary edge direction. */
|
||||||
static void merge_vert_dir(ShrinkwrapBoundaryVertData *vdata,
|
static void merge_vert_dir(ShrinkwrapBoundaryVertData *vdata,
|
||||||
signed char *status,
|
signed char *status,
|
||||||
@@ -193,7 +183,7 @@ static void merge_vert_dir(ShrinkwrapBoundaryVertData *vdata,
|
|||||||
status[index] = (status[index] == 0) ? side : -1;
|
status[index] = (status[index] == 0) ? side : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ShrinkwrapBoundaryData *shrinkwrap_build_boundary_data(Mesh *mesh)
|
static std::unique_ptr<ShrinkwrapBoundaryData> shrinkwrap_build_boundary_data(Mesh *mesh)
|
||||||
{
|
{
|
||||||
using namespace blender;
|
using namespace blender;
|
||||||
const blender::Span<float3> positions = mesh->vert_positions();
|
const blender::Span<float3> positions = mesh->vert_positions();
|
||||||
@@ -214,36 +204,33 @@ static ShrinkwrapBoundaryData *shrinkwrap_build_boundary_data(Mesh *mesh)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Build the boundary edge bitmask. */
|
/* Build the boundary edge bitmask. */
|
||||||
BLI_bitmap *edge_is_boundary = BLI_BITMAP_NEW(mesh->totedge,
|
blender::BitVector<> edge_is_boundary(mesh->totedge, false);
|
||||||
"ShrinkwrapBoundaryData::edge_is_boundary");
|
|
||||||
uint num_boundary_edges = 0;
|
uint num_boundary_edges = 0;
|
||||||
|
|
||||||
for (int i = 0; i < mesh->totedge; i++) {
|
for (int i = 0; i < mesh->totedge; i++) {
|
||||||
edge_mode[i] = (edge_mode[i] == 1);
|
edge_mode[i] = (edge_mode[i] == 1);
|
||||||
|
|
||||||
if (edge_mode[i]) {
|
if (edge_mode[i]) {
|
||||||
BLI_BITMAP_ENABLE(edge_is_boundary, i);
|
edge_is_boundary[i].set();
|
||||||
num_boundary_edges++;
|
num_boundary_edges++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If no boundary, return nullptr. */
|
/* If no boundary, return nullptr. */
|
||||||
if (num_boundary_edges == 0) {
|
if (num_boundary_edges == 0) {
|
||||||
MEM_freeN(edge_is_boundary);
|
|
||||||
MEM_freeN(edge_mode);
|
MEM_freeN(edge_mode);
|
||||||
return nullptr;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate the data object. */
|
/* Allocate the data object. */
|
||||||
ShrinkwrapBoundaryData *data = MEM_cnew<ShrinkwrapBoundaryData>(__func__);
|
std::unique_ptr<ShrinkwrapBoundaryData> data = std::make_unique<ShrinkwrapBoundaryData>();
|
||||||
|
|
||||||
data->edge_is_boundary = edge_is_boundary;
|
data->edge_is_boundary = std::move(edge_is_boundary);
|
||||||
|
|
||||||
/* Build the boundary looptri bitmask. */
|
/* Build the boundary looptri bitmask. */
|
||||||
const blender::Span<MLoopTri> looptris = mesh->looptris();
|
const blender::Span<MLoopTri> looptris = mesh->looptris();
|
||||||
|
|
||||||
BLI_bitmap *looptri_has_boundary = BLI_BITMAP_NEW(looptris.size(),
|
blender::BitVector<> looptri_has_boundary(looptris.size(), false);
|
||||||
"ShrinkwrapBoundaryData::looptri_is_boundary");
|
|
||||||
|
|
||||||
for (const int64_t i : looptris.index_range()) {
|
for (const int64_t i : looptris.index_range()) {
|
||||||
const int3 real_edges = bke::mesh::looptri_get_real_edges(
|
const int3 real_edges = bke::mesh::looptri_get_real_edges(
|
||||||
@@ -251,17 +238,16 @@ static ShrinkwrapBoundaryData *shrinkwrap_build_boundary_data(Mesh *mesh)
|
|||||||
|
|
||||||
for (int j = 0; j < 3; j++) {
|
for (int j = 0; j < 3; j++) {
|
||||||
if (real_edges[j] >= 0 && edge_mode[real_edges[j]]) {
|
if (real_edges[j] >= 0 && edge_mode[real_edges[j]]) {
|
||||||
BLI_BITMAP_ENABLE(looptri_has_boundary, i);
|
looptri_has_boundary[i].set();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data->looptri_has_boundary = looptri_has_boundary;
|
data->looptri_has_boundary = std::move(looptri_has_boundary);
|
||||||
|
|
||||||
/* Find boundary vertices and build a mapping table for compact storage of data. */
|
/* Find boundary vertices and build a mapping table for compact storage of data. */
|
||||||
int *vert_boundary_id = static_cast<int *>(
|
Array<int> vert_boundary_id(mesh->totvert, 0);
|
||||||
MEM_calloc_arrayN(size_t(mesh->totvert), sizeof(int), __func__));
|
|
||||||
|
|
||||||
for (int i = 0; i < mesh->totedge; i++) {
|
for (int i = 0; i < mesh->totedge; i++) {
|
||||||
if (edge_mode[i]) {
|
if (edge_mode[i]) {
|
||||||
@@ -279,11 +265,9 @@ static ShrinkwrapBoundaryData *shrinkwrap_build_boundary_data(Mesh *mesh)
|
|||||||
}
|
}
|
||||||
|
|
||||||
data->vert_boundary_id = vert_boundary_id;
|
data->vert_boundary_id = vert_boundary_id;
|
||||||
data->num_boundary_verts = num_boundary_verts;
|
|
||||||
|
|
||||||
/* Compute average directions. */
|
/* Compute average directions. */
|
||||||
ShrinkwrapBoundaryVertData *boundary_verts = static_cast<ShrinkwrapBoundaryVertData *>(
|
Array<ShrinkwrapBoundaryVertData> boundary_verts(num_boundary_verts);
|
||||||
MEM_calloc_arrayN(num_boundary_verts, sizeof(*boundary_verts), __func__));
|
|
||||||
|
|
||||||
signed char *vert_status = static_cast<signed char *>(
|
signed char *vert_status = static_cast<signed char *>(
|
||||||
MEM_calloc_arrayN(num_boundary_verts, sizeof(char), __func__));
|
MEM_calloc_arrayN(num_boundary_verts, sizeof(char), __func__));
|
||||||
@@ -296,8 +280,8 @@ static ShrinkwrapBoundaryData *shrinkwrap_build_boundary_data(Mesh *mesh)
|
|||||||
sub_v3_v3v3(dir, positions[edge[1]], positions[edge[0]]);
|
sub_v3_v3v3(dir, positions[edge[1]], positions[edge[0]]);
|
||||||
normalize_v3(dir);
|
normalize_v3(dir);
|
||||||
|
|
||||||
merge_vert_dir(boundary_verts, vert_status, vert_boundary_id[edge[0]], dir, 1);
|
merge_vert_dir(boundary_verts.data(), vert_status, vert_boundary_id[edge[0]], dir, 1);
|
||||||
merge_vert_dir(boundary_verts, vert_status, vert_boundary_id[edge[1]], dir, 2);
|
merge_vert_dir(boundary_verts.data(), vert_status, vert_boundary_id[edge[1]], dir, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,7 +304,7 @@ static ShrinkwrapBoundaryData *shrinkwrap_build_boundary_data(Mesh *mesh)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data->boundary_verts = boundary_verts;
|
data->boundary_verts = std::move(boundary_verts);
|
||||||
|
|
||||||
MEM_freeN(edge_mode);
|
MEM_freeN(edge_mode);
|
||||||
return data;
|
return data;
|
||||||
@@ -328,9 +312,6 @@ static ShrinkwrapBoundaryData *shrinkwrap_build_boundary_data(Mesh *mesh)
|
|||||||
|
|
||||||
void BKE_shrinkwrap_compute_boundary_data(Mesh *mesh)
|
void BKE_shrinkwrap_compute_boundary_data(Mesh *mesh)
|
||||||
{
|
{
|
||||||
if (mesh->runtime->shrinkwrap_data) {
|
|
||||||
BKE_shrinkwrap_boundary_data_free(mesh->runtime->shrinkwrap_data);
|
|
||||||
}
|
|
||||||
mesh->runtime->shrinkwrap_data = shrinkwrap_build_boundary_data(mesh);
|
mesh->runtime->shrinkwrap_data = shrinkwrap_build_boundary_data(mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -957,7 +938,7 @@ static void target_project_edge(const ShrinkwrapTreeData *tree,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Retrieve boundary vertex IDs */
|
/* Retrieve boundary vertex IDs */
|
||||||
const int *vert_boundary_id = tree->boundary->vert_boundary_id;
|
const int *vert_boundary_id = tree->boundary->vert_boundary_id.data();
|
||||||
int bid1 = vert_boundary_id[edge[0]], bid2 = vert_boundary_id[edge[1]];
|
int bid1 = vert_boundary_id[edge[0]], bid2 = vert_boundary_id[edge[1]];
|
||||||
|
|
||||||
if (bid1 < 0 || bid2 < 0) {
|
if (bid1 < 0 || bid2 < 0) {
|
||||||
@@ -965,7 +946,7 @@ static void target_project_edge(const ShrinkwrapTreeData *tree,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Retrieve boundary vertex normals and align them to direction. */
|
/* Retrieve boundary vertex normals and align them to direction. */
|
||||||
const ShrinkwrapBoundaryVertData *boundary_verts = tree->boundary->boundary_verts;
|
const ShrinkwrapBoundaryVertData *boundary_verts = tree->boundary->boundary_verts.data();
|
||||||
float vedge_dir[2][3], dir[3];
|
float vedge_dir[2][3], dir[3];
|
||||||
|
|
||||||
copy_v3_v3(vedge_dir[0], boundary_verts[bid1].normal_plane);
|
copy_v3_v3(vedge_dir[0], boundary_verts[bid1].normal_plane);
|
||||||
@@ -1060,13 +1041,13 @@ static void mesh_looptri_target_project(void *userdata,
|
|||||||
update_hit(nearest, index, co, hit_co, hit_no);
|
update_hit(nearest, index, co, hit_co, hit_no);
|
||||||
}
|
}
|
||||||
/* Boundary edges */
|
/* Boundary edges */
|
||||||
else if (tree->boundary && BLI_BITMAP_TEST(tree->boundary->looptri_has_boundary, index)) {
|
else if (tree->boundary && tree->boundary->looptri_has_boundary[index]) {
|
||||||
const BLI_bitmap *is_boundary = tree->boundary->edge_is_boundary;
|
const BitSpan is_boundary = tree->boundary->edge_is_boundary;
|
||||||
const int3 edges = bke::mesh::looptri_get_real_edges(
|
const int3 edges = bke::mesh::looptri_get_real_edges(
|
||||||
data->edges, data->corner_verts, tree->corner_edges, *lt);
|
data->edges, data->corner_verts, tree->corner_edges, *lt);
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
if (edges[i] >= 0 && BLI_BITMAP_TEST(is_boundary, edges[i])) {
|
if (edges[i] >= 0 && is_boundary[edges[i]]) {
|
||||||
target_project_edge(tree, index, co, nearest, edges[i]);
|
target_project_edge(tree, index, co, nearest, edges[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -489,7 +489,7 @@ MeshRenderData *mesh_render_data_create(Object *object,
|
|||||||
mr->bm = me->edit_mesh->bm;
|
mr->bm = me->edit_mesh->bm;
|
||||||
mr->edit_bmesh = me->edit_mesh;
|
mr->edit_bmesh = me->edit_mesh;
|
||||||
mr->me = (do_final) ? editmesh_eval_final : editmesh_eval_cage;
|
mr->me = (do_final) ? editmesh_eval_final : editmesh_eval_cage;
|
||||||
mr->edit_data = is_mode_active ? mr->me->runtime->edit_data : nullptr;
|
mr->edit_data = is_mode_active ? mr->me->runtime->edit_data.get() : nullptr;
|
||||||
|
|
||||||
/* If there is no distinct cage, hide unmapped edges that can't be selected. */
|
/* If there is no distinct cage, hide unmapped edges that can't be selected. */
|
||||||
mr->hide_unmapped_edges = !do_final || editmesh_eval_final == editmesh_eval_cage;
|
mr->hide_unmapped_edges = !do_final || editmesh_eval_final == editmesh_eval_cage;
|
||||||
@@ -497,8 +497,8 @@ MeshRenderData *mesh_render_data_create(Object *object,
|
|||||||
if (mr->edit_data) {
|
if (mr->edit_data) {
|
||||||
blender::bke::EditMeshData *emd = mr->edit_data;
|
blender::bke::EditMeshData *emd = mr->edit_data;
|
||||||
if (!emd->vertexCos.is_empty()) {
|
if (!emd->vertexCos.is_empty()) {
|
||||||
BKE_editmesh_cache_ensure_vert_normals(mr->edit_bmesh, emd);
|
BKE_editmesh_cache_ensure_vert_normals(*mr->edit_bmesh, *emd);
|
||||||
BKE_editmesh_cache_ensure_face_normals(mr->edit_bmesh, emd);
|
BKE_editmesh_cache_ensure_face_normals(*mr->edit_bmesh, *emd);
|
||||||
}
|
}
|
||||||
|
|
||||||
mr->bm_vert_coords = mr->edit_data->vertexCos;
|
mr->bm_vert_coords = mr->edit_data->vertexCos;
|
||||||
|
|||||||
@@ -397,7 +397,7 @@ static void statvis_calc_distort(const MeshRenderData &mr, float *r_distort)
|
|||||||
BMFace *f;
|
BMFace *f;
|
||||||
|
|
||||||
if (!mr.bm_vert_coords.is_empty()) {
|
if (!mr.bm_vert_coords.is_empty()) {
|
||||||
BKE_editmesh_cache_ensure_face_normals(em, mr.edit_data);
|
BKE_editmesh_cache_ensure_face_normals(*em, *mr.edit_data);
|
||||||
|
|
||||||
/* Most likely this is already valid, ensure just in case.
|
/* Most likely this is already valid, ensure just in case.
|
||||||
* Needed for #BM_loop_calc_face_normal_safe_vcos. */
|
* Needed for #BM_loop_calc_face_normal_safe_vcos. */
|
||||||
|
|||||||
Reference in New Issue
Block a user