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/editors/mesh/mesh_mirror.cc
Hans Goudey 1dc57a89e9 Mesh: Move functions to C++ header
Refactoring mesh code, it has become clear that local cleanups and
simplifications are limited by the need to keep a C public API for
mesh functions. This change makes code more obvious and makes further
refactoring much easier.

- Add a new `BKE_mesh.hh` header for a C++ only mesh API
- Introduce a new `blender::bke::mesh` namespace, documented here:
  https://wiki.blender.org/wiki/Source/Objects/Mesh#Namespaces
- Move some functions to the new namespace, cleaning up their arguments
- Move code to `Array` and `float3` where necessary to use the new API
- Define existing inline mesh data access functions to the new header
- Keep some C API functions where necessary because of RNA
- Move all C++ files to use the new header, which includes the old one

In the future it may make sense to split up `BKE_mesh.hh` more, but for
now keeping the same name as the existing header keeps things simple.

Pull Request: blender/blender#105416
2023-03-12 22:29:15 +01:00

352 lines
9.5 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edmesh
*
* Mirror calculation for edit-mode and object mode.
*/
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "BKE_editmesh.h"
#include "BKE_mesh.hh"
#include "BLI_kdtree.h"
#include "ED_mesh.h"
/* -------------------------------------------------------------------- */
/** \name Mesh Spatial Mirror API
* \{ */
#define KD_THRESH 0.00002f
static struct {
KDTree_3d *tree;
} MirrKdStore = {nullptr};
void ED_mesh_mirror_spatial_table_begin(Object *ob, BMEditMesh *em, Mesh *me_eval)
{
Mesh *me = static_cast<Mesh *>(ob->data);
const bool use_em = (!me_eval && em && me->edit_mesh == em);
const int totvert = use_em ? em->bm->totvert : me_eval ? me_eval->totvert : me->totvert;
if (MirrKdStore.tree) { /* happens when entering this call without ending it */
ED_mesh_mirror_spatial_table_end(ob);
}
MirrKdStore.tree = BLI_kdtree_3d_new(totvert);
if (use_em) {
BMVert *eve;
BMIter iter;
int i;
/* this needs to be valid for index lookups later (callers need) */
BM_mesh_elem_table_ensure(em->bm, BM_VERT);
BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
BLI_kdtree_3d_insert(MirrKdStore.tree, i, eve->co);
}
}
else {
const float(*positions)[3] = BKE_mesh_vert_positions(me_eval ? me_eval : me);
for (int i = 0; i < totvert; i++) {
BLI_kdtree_3d_insert(MirrKdStore.tree, i, positions[i]);
}
}
BLI_kdtree_3d_balance(MirrKdStore.tree);
}
int ED_mesh_mirror_spatial_table_lookup(Object *ob,
BMEditMesh *em,
Mesh *me_eval,
const float co[3])
{
if (MirrKdStore.tree == nullptr) {
ED_mesh_mirror_spatial_table_begin(ob, em, me_eval);
}
if (MirrKdStore.tree) {
KDTreeNearest_3d nearest;
const int i = BLI_kdtree_3d_find_nearest(MirrKdStore.tree, co, &nearest);
if (i != -1) {
if (nearest.dist < KD_THRESH) {
return i;
}
}
}
return -1;
}
void ED_mesh_mirror_spatial_table_end(Object * /*ob*/)
{
/* TODO: store this in object/object-data (keep unused argument for now). */
if (MirrKdStore.tree) {
BLI_kdtree_3d_free(MirrKdStore.tree);
MirrKdStore.tree = nullptr;
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Mesh Topology Mirror API
* \{ */
using MirrTopoHash_t = uint;
struct MirrTopoVert_t {
MirrTopoHash_t hash;
int v_index;
};
static int mirrtopo_hash_sort(const void *l1, const void *l2)
{
if (MirrTopoHash_t(intptr_t(l1)) > MirrTopoHash_t(intptr_t(l2))) {
return 1;
}
if (MirrTopoHash_t(intptr_t(l1)) < MirrTopoHash_t(intptr_t(l2))) {
return -1;
}
return 0;
}
static int mirrtopo_vert_sort(const void *v1, const void *v2)
{
if (((MirrTopoVert_t *)v1)->hash > ((MirrTopoVert_t *)v2)->hash) {
return 1;
}
if (((MirrTopoVert_t *)v1)->hash < ((MirrTopoVert_t *)v2)->hash) {
return -1;
}
return 0;
}
bool ED_mesh_mirrtopo_recalc_check(BMEditMesh *em, Mesh *me, MirrTopoStore_t *mesh_topo_store)
{
const bool is_editmode = em != nullptr;
int totvert;
int totedge;
if (em) {
totvert = em->bm->totvert;
totedge = em->bm->totedge;
}
else {
totvert = me->totvert;
totedge = me->totedge;
}
if ((mesh_topo_store->index_lookup == nullptr) ||
(mesh_topo_store->prev_is_editmode != is_editmode) ||
(totvert != mesh_topo_store->prev_vert_tot) || (totedge != mesh_topo_store->prev_edge_tot)) {
return true;
}
return false;
}
void ED_mesh_mirrtopo_init(BMEditMesh *em,
Mesh *me,
MirrTopoStore_t *mesh_topo_store,
const bool skip_em_vert_array_init)
{
if (em) {
BLI_assert(me == nullptr);
}
const bool is_editmode = (em != nullptr);
/* Edit-mode variables. */
BMEdge *eed;
BMIter iter;
int a, last;
int totvert, totedge;
int tot_unique = -1, tot_unique_prev = -1;
int tot_unique_edges = 0, tot_unique_edges_prev;
MirrTopoHash_t topo_pass = 1;
/* reallocate if needed */
ED_mesh_mirrtopo_free(mesh_topo_store);
mesh_topo_store->prev_is_editmode = is_editmode;
if (em) {
BM_mesh_elem_index_ensure(em->bm, BM_VERT);
totvert = em->bm->totvert;
}
else {
totvert = me->totvert;
}
MirrTopoHash_t *topo_hash = static_cast<MirrTopoHash_t *>(
MEM_callocN(totvert * sizeof(MirrTopoHash_t), __func__));
/* Initialize the vert-edge-user counts used to detect unique topology */
if (em) {
totedge = em->bm->totedge;
BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
const int i1 = BM_elem_index_get(eed->v1), i2 = BM_elem_index_get(eed->v2);
topo_hash[i1]++;
topo_hash[i2]++;
}
}
else {
totedge = me->totedge;
for (const MEdge &edge : me->edges()) {
topo_hash[edge.v1]++;
topo_hash[edge.v2]++;
}
}
MirrTopoHash_t *topo_hash_prev = static_cast<MirrTopoHash_t *>(MEM_dupallocN(topo_hash));
tot_unique_prev = -1;
tot_unique_edges_prev = -1;
while (1) {
/* use the number of edges per vert to give verts unique topology IDs */
tot_unique_edges = 0;
/* This can make really big numbers, wrapping around here is fine */
if (em) {
BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
const int i1 = BM_elem_index_get(eed->v1), i2 = BM_elem_index_get(eed->v2);
topo_hash[i1] += topo_hash_prev[i2] * topo_pass;
topo_hash[i2] += topo_hash_prev[i1] * topo_pass;
tot_unique_edges += (topo_hash[i1] != topo_hash[i2]);
}
}
else {
for (const MEdge &edge : me->edges()) {
const uint i1 = edge.v1, i2 = edge.v2;
topo_hash[i1] += topo_hash_prev[i2] * topo_pass;
topo_hash[i2] += topo_hash_prev[i1] * topo_pass;
tot_unique_edges += (topo_hash[i1] != topo_hash[i2]);
}
}
memcpy(topo_hash_prev, topo_hash, sizeof(MirrTopoHash_t) * totvert);
/* sort so we can count unique values */
qsort(topo_hash_prev, totvert, sizeof(MirrTopoHash_t), mirrtopo_hash_sort);
tot_unique = 1; /* account for skipping the first value */
for (a = 1; a < totvert; a++) {
if (topo_hash_prev[a - 1] != topo_hash_prev[a]) {
tot_unique++;
}
}
if ((tot_unique <= tot_unique_prev) && (tot_unique_edges <= tot_unique_edges_prev)) {
/* Finish searching for unique values when 1 loop doesn't give a
* higher number of unique values compared to the previous loop. */
break;
}
tot_unique_prev = tot_unique;
tot_unique_edges_prev = tot_unique_edges;
/* Copy the hash calculated this iteration, so we can use them next time */
memcpy(topo_hash_prev, topo_hash, sizeof(MirrTopoHash_t) * totvert);
topo_pass++;
}
/* Hash/Index pairs are needed for sorting to find index pairs */
MirrTopoVert_t *topo_pairs = static_cast<MirrTopoVert_t *>(
MEM_callocN(sizeof(MirrTopoVert_t) * totvert, "MirrTopoPairs"));
/* since we are looping through verts, initialize these values here too */
intptr_t *index_lookup = static_cast<intptr_t *>(
MEM_mallocN(totvert * sizeof(*index_lookup), "mesh_topo_lookup"));
if (em) {
if (skip_em_vert_array_init == false) {
BM_mesh_elem_table_ensure(em->bm, BM_VERT);
}
}
for (a = 0; a < totvert; a++) {
topo_pairs[a].hash = topo_hash[a];
topo_pairs[a].v_index = a;
/* initialize lookup */
index_lookup[a] = -1;
}
qsort(topo_pairs, totvert, sizeof(MirrTopoVert_t), mirrtopo_vert_sort);
last = 0;
/* Get the pairs out of the sorted hashes.
* NOTE: `totvert + 1` means we can use the previous 2,
* but you can't ever access the last 'a' index of #MirrTopoPairs. */
if (em) {
BMVert **vtable = em->bm->vtable;
for (a = 1; a <= totvert; a++) {
// printf("I %d %ld %d\n",
// (a - last), MirrTopoPairs[a].hash, MirrTopoPairs[a].v_index);
if ((a == totvert) || (topo_pairs[a - 1].hash != topo_pairs[a].hash)) {
const int match_count = a - last;
if (match_count == 2) {
const int j = topo_pairs[a - 1].v_index, k = topo_pairs[a - 2].v_index;
index_lookup[j] = intptr_t(vtable[k]);
index_lookup[k] = intptr_t(vtable[j]);
}
else if (match_count == 1) {
/* Center vertex. */
const int j = topo_pairs[a - 1].v_index;
index_lookup[j] = intptr_t(vtable[j]);
}
last = a;
}
}
}
else {
/* same as above, for mesh */
for (a = 1; a <= totvert; a++) {
if ((a == totvert) || (topo_pairs[a - 1].hash != topo_pairs[a].hash)) {
const int match_count = a - last;
if (match_count == 2) {
const int j = topo_pairs[a - 1].v_index, k = topo_pairs[a - 2].v_index;
index_lookup[j] = k;
index_lookup[k] = j;
}
else if (match_count == 1) {
/* Center vertex. */
const int j = topo_pairs[a - 1].v_index;
index_lookup[j] = j;
}
last = a;
}
}
}
MEM_freeN(topo_pairs);
topo_pairs = nullptr;
MEM_freeN(topo_hash);
MEM_freeN(topo_hash_prev);
mesh_topo_store->index_lookup = index_lookup;
mesh_topo_store->prev_vert_tot = totvert;
mesh_topo_store->prev_edge_tot = totedge;
}
void ED_mesh_mirrtopo_free(MirrTopoStore_t *mesh_topo_store)
{
MEM_SAFE_FREE(mesh_topo_store->index_lookup);
mesh_topo_store->prev_vert_tot = -1;
mesh_topo_store->prev_edge_tot = -1;
}
/** \} */