Add Chromium-only Blender WebEngine parity work
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* BM element callback functions.
|
||||
*/
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
#include "intern/bmesh_callback_generic.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
bool BM_elem_cb_check_hflag_ex(BMElem *ele, void *user_data)
|
||||
{
|
||||
const uint hflag_pair = POINTER_AS_INT(user_data);
|
||||
const char hflag_p = (hflag_pair & 0xff);
|
||||
const char hflag_n = (hflag_pair >> 8);
|
||||
|
||||
return ((BM_elem_flag_test(ele, hflag_p) != 0) && (BM_elem_flag_test(ele, hflag_n) == 0));
|
||||
}
|
||||
|
||||
bool BM_elem_cb_check_hflag_enabled(BMElem *ele, void *user_data)
|
||||
{
|
||||
const char hflag = POINTER_AS_INT(user_data);
|
||||
|
||||
return (BM_elem_flag_test(ele, hflag) != 0);
|
||||
}
|
||||
|
||||
bool BM_elem_cb_check_hflag_disabled(BMElem *ele, void *user_data)
|
||||
{
|
||||
const char hflag = POINTER_AS_INT(user_data);
|
||||
|
||||
return (BM_elem_flag_test(ele, hflag) == 0);
|
||||
}
|
||||
|
||||
bool BM_elem_cb_check_elem_not_equal(BMElem *ele, void *user_data)
|
||||
{
|
||||
return (ele != user_data);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,30 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMElem;
|
||||
|
||||
bool BM_elem_cb_check_hflag_enabled(BMElem *, void *user_data);
|
||||
bool BM_elem_cb_check_hflag_disabled(BMElem *, void *user_data);
|
||||
bool BM_elem_cb_check_hflag_ex(BMElem *, void *user_data);
|
||||
bool BM_elem_cb_check_elem_not_equal(BMElem *ele, void *user_data);
|
||||
|
||||
#define BM_elem_cb_check_hflag_ex_simple(type, hflag_p, hflag_n) \
|
||||
(bool (*)(type, void *)) BM_elem_cb_check_hflag_ex, \
|
||||
POINTER_FROM_UINT(((hflag_p) | (hflag_n << 8)))
|
||||
|
||||
#define BM_elem_cb_check_hflag_enabled_simple(type, hflag_p) \
|
||||
(bool (*)(type, void *)) BM_elem_cb_check_hflag_enabled, POINTER_FROM_UINT((hflag_p))
|
||||
|
||||
#define BM_elem_cb_check_hflag_disabled_simple(type, hflag_n) \
|
||||
(bool (*)(type, void *)) BM_elem_cb_check_hflag_disabled, POINTER_FROM_UINT(hflag_n)
|
||||
|
||||
} // namespace blender
|
||||
714
blender-5.2.0/source/blender/bmesh/intern/bmesh_construct.cc
Normal file
714
blender-5.2.0/source/blender/bmesh/intern/bmesh_construct.cc
Normal file
@@ -0,0 +1,714 @@
|
||||
/* SPDX-FileCopyrightText: 2007 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* BM construction functions.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_attribute_legacy_convert.hh"
|
||||
#include "BKE_attribute_storage.hh"
|
||||
#include "BKE_customdata.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
|
||||
#include "bmesh.hh"
|
||||
#include "intern/bmesh_private.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
bool BM_verts_from_edges(BMVert **vert_arr, BMEdge **edge_arr, const int len)
|
||||
{
|
||||
int i, i_prev = len - 1;
|
||||
for (i = 0; i < len; i++) {
|
||||
vert_arr[i] = BM_edge_share_vert(edge_arr[i_prev], edge_arr[i]);
|
||||
if (vert_arr[i] == nullptr) {
|
||||
return false;
|
||||
}
|
||||
i_prev = i;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BM_edges_from_verts(BMEdge **edge_arr, BMVert **vert_arr, const int len)
|
||||
{
|
||||
int i, i_prev = len - 1;
|
||||
for (i = 0; i < len; i++) {
|
||||
edge_arr[i_prev] = BM_edge_exists(vert_arr[i_prev], vert_arr[i]);
|
||||
if (edge_arr[i_prev] == nullptr) {
|
||||
return false;
|
||||
}
|
||||
i_prev = i;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BM_edges_from_verts_ensure(BMesh *bm, BMEdge **edge_arr, BMVert **vert_arr, const int len)
|
||||
{
|
||||
int i, i_prev = len - 1;
|
||||
for (i = 0; i < len; i++) {
|
||||
edge_arr[i_prev] = BM_edge_create(
|
||||
bm, vert_arr[i_prev], vert_arr[i], nullptr, BM_CREATE_NO_DOUBLE);
|
||||
i_prev = i;
|
||||
}
|
||||
}
|
||||
|
||||
BMFace *BM_face_create_quad_tri(BMesh *bm,
|
||||
BMVert *v1,
|
||||
BMVert *v2,
|
||||
BMVert *v3,
|
||||
BMVert *v4,
|
||||
const BMFace *f_example,
|
||||
const eBMCreateFlag create_flag)
|
||||
{
|
||||
BMVert *vtar[4] = {v1, v2, v3, v4};
|
||||
return BM_face_create_verts(bm, vtar, v4 ? 4 : 3, f_example, create_flag, true);
|
||||
}
|
||||
|
||||
void BM_face_copy_shared(BMesh *bm, BMFace *f, BMLoopFilterFunc filter_fn, void *user_data)
|
||||
{
|
||||
BMLoop *l_first;
|
||||
BMLoop *l_iter;
|
||||
|
||||
#ifndef NDEBUG
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
BLI_assert(BM_ELEM_API_FLAG_TEST(l_iter, _FLAG_OVERLAP) == 0);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
#endif
|
||||
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
BMLoop *l_other = l_iter->radial_next;
|
||||
|
||||
if (l_other && l_other != l_iter) {
|
||||
BMLoop *l_src[2];
|
||||
BMLoop *l_dst[2] = {l_iter, l_iter->next};
|
||||
uint j;
|
||||
|
||||
if (l_other->v == l_iter->v) {
|
||||
l_src[0] = l_other;
|
||||
l_src[1] = l_other->next;
|
||||
}
|
||||
else {
|
||||
l_src[0] = l_other->next;
|
||||
l_src[1] = l_other;
|
||||
}
|
||||
|
||||
for (j = 0; j < 2; j++) {
|
||||
BLI_assert(l_dst[j]->v == l_src[j]->v);
|
||||
if (BM_ELEM_API_FLAG_TEST(l_dst[j], _FLAG_OVERLAP) == 0) {
|
||||
if ((filter_fn == nullptr) || filter_fn(l_src[j], user_data)) {
|
||||
CustomData_bmesh_copy_block(bm->ldata, l_src[j]->head.data, &l_dst[j]->head.data);
|
||||
BM_ELEM_API_FLAG_ENABLE(l_dst[j], _FLAG_OVERLAP);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
BM_ELEM_API_FLAG_DISABLE(l_iter, _FLAG_OVERLAP);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of edges,
|
||||
* order them using the winding defined by \a v1 & \a v2
|
||||
* into \a edges_sort & \a verts_sort.
|
||||
*
|
||||
* All arrays must be \a len long.
|
||||
*/
|
||||
static bool bm_edges_sort_winding(BMVert *v1,
|
||||
BMVert *v2,
|
||||
BMEdge **edges,
|
||||
const int len,
|
||||
BMEdge **edges_sort,
|
||||
BMVert **verts_sort)
|
||||
{
|
||||
BMEdge *e_iter, *e_first;
|
||||
BMVert *v_iter;
|
||||
int i;
|
||||
|
||||
/* all flags _must_ be cleared on exit! */
|
||||
for (i = 0; i < len; i++) {
|
||||
BM_ELEM_API_FLAG_ENABLE(edges[i], _FLAG_MF);
|
||||
BM_ELEM_API_FLAG_ENABLE(edges[i]->v1, _FLAG_MV);
|
||||
BM_ELEM_API_FLAG_ENABLE(edges[i]->v2, _FLAG_MV);
|
||||
}
|
||||
|
||||
/* find first edge */
|
||||
i = 0;
|
||||
v_iter = v1;
|
||||
e_iter = e_first = v1->e;
|
||||
do {
|
||||
if (BM_ELEM_API_FLAG_TEST(e_iter, _FLAG_MF) && (BM_edge_other_vert(e_iter, v_iter) == v2)) {
|
||||
i = 1;
|
||||
break;
|
||||
}
|
||||
} while ((e_iter = bmesh_disk_edge_next(e_iter, v_iter)) != e_first);
|
||||
if (i == 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
/* entering loop will always succeed */
|
||||
if (BM_ELEM_API_FLAG_TEST(e_iter, _FLAG_MF)) {
|
||||
if (UNLIKELY(BM_ELEM_API_FLAG_TEST(v_iter, _FLAG_MV) == false)) {
|
||||
/* vert is in loop multiple times */
|
||||
goto error;
|
||||
}
|
||||
|
||||
BM_ELEM_API_FLAG_DISABLE(e_iter, _FLAG_MF);
|
||||
edges_sort[i] = e_iter;
|
||||
|
||||
BM_ELEM_API_FLAG_DISABLE(v_iter, _FLAG_MV);
|
||||
verts_sort[i] = v_iter;
|
||||
|
||||
i += 1;
|
||||
|
||||
/* walk onto the next vertex */
|
||||
v_iter = BM_edge_other_vert(e_iter, v_iter);
|
||||
if (i == len) {
|
||||
if (UNLIKELY(v_iter != verts_sort[0])) {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
e_first = e_iter;
|
||||
}
|
||||
} while ((e_iter = bmesh_disk_edge_next(e_iter, v_iter)) != e_first);
|
||||
|
||||
if (i == len) {
|
||||
return true;
|
||||
}
|
||||
|
||||
error:
|
||||
for (i = 0; i < len; i++) {
|
||||
BM_ELEM_API_FLAG_DISABLE(edges[i], _FLAG_MF);
|
||||
BM_ELEM_API_FLAG_DISABLE(edges[i]->v1, _FLAG_MV);
|
||||
BM_ELEM_API_FLAG_DISABLE(edges[i]->v2, _FLAG_MV);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BMFace *BM_face_create_ngon(BMesh *bm,
|
||||
BMVert *v1,
|
||||
BMVert *v2,
|
||||
BMEdge **edges,
|
||||
const int len,
|
||||
const BMFace *f_example,
|
||||
const eBMCreateFlag create_flag)
|
||||
{
|
||||
Array<BMEdge *, BM_DEFAULT_NGON_STACK_SIZE> edges_sort(len);
|
||||
Array<BMVert *, BM_DEFAULT_NGON_STACK_SIZE> verts_sort(len);
|
||||
|
||||
BLI_assert(len && v1 && v2 && edges && bm);
|
||||
|
||||
if (bm_edges_sort_winding(v1, v2, edges, len, edges_sort.data(), verts_sort.data())) {
|
||||
return BM_face_create(bm, verts_sort.data(), edges_sort.data(), len, f_example, create_flag);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BMFace *BM_face_create_ngon_verts(BMesh *bm,
|
||||
BMVert **vert_arr,
|
||||
const int len,
|
||||
const BMFace *f_example,
|
||||
const eBMCreateFlag create_flag,
|
||||
const bool calc_winding,
|
||||
const bool create_edges)
|
||||
{
|
||||
Array<BMEdge *, BM_DEFAULT_NGON_STACK_SIZE> edge_arr(len);
|
||||
|
||||
uint winding[2] = {0, 0};
|
||||
int i, i_prev = len - 1;
|
||||
BMVert *v_winding[2] = {vert_arr[i_prev], vert_arr[0]};
|
||||
|
||||
BLI_assert(len > 2);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (create_edges) {
|
||||
edge_arr[i] = BM_edge_create(
|
||||
bm, vert_arr[i_prev], vert_arr[i], nullptr, BM_CREATE_NO_DOUBLE);
|
||||
}
|
||||
else {
|
||||
edge_arr[i] = BM_edge_exists(vert_arr[i_prev], vert_arr[i]);
|
||||
if (edge_arr[i] == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (calc_winding) {
|
||||
/* the edge may exist already and be attached to a face
|
||||
* in this case we can find the best winding to use for the new face */
|
||||
if (edge_arr[i]->l) {
|
||||
BMVert *test_v1, *test_v2;
|
||||
/* we want to use the reverse winding to the existing order */
|
||||
BM_edge_ordered_verts(edge_arr[i], &test_v2, &test_v1);
|
||||
winding[(vert_arr[i_prev] == test_v2)]++;
|
||||
BLI_assert(ELEM(vert_arr[i_prev], test_v2, test_v1));
|
||||
}
|
||||
}
|
||||
|
||||
i_prev = i;
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
||||
if (calc_winding) {
|
||||
if (winding[0] < winding[1]) {
|
||||
winding[0] = 1;
|
||||
winding[1] = 0;
|
||||
}
|
||||
else {
|
||||
winding[0] = 0;
|
||||
winding[1] = 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
winding[0] = 0;
|
||||
winding[1] = 1;
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
||||
/* create the face */
|
||||
return BM_face_create_ngon(bm,
|
||||
v_winding[winding[0]],
|
||||
v_winding[winding[1]],
|
||||
edge_arr.data(),
|
||||
len,
|
||||
f_example,
|
||||
create_flag);
|
||||
}
|
||||
|
||||
void BM_verts_sort_radial_plane(BMVert **vert_arr, int len)
|
||||
{
|
||||
using AngleIndex = std::pair<float, int>;
|
||||
Array<AngleIndex, BM_DEFAULT_NGON_STACK_SIZE> vang(len);
|
||||
Array<BMVert *, BM_DEFAULT_NGON_STACK_SIZE> vert_arr_map(len);
|
||||
|
||||
float nor[3], cent[3];
|
||||
int index_tangent = 0;
|
||||
BM_verts_calc_normal_from_cloud_ex(vert_arr, len, nor, cent, &index_tangent);
|
||||
const float *far = vert_arr[index_tangent]->co;
|
||||
|
||||
/* Now calculate every points angle around the normal (signed). */
|
||||
for (int i = 0; i < len; i++) {
|
||||
vang[i].first = angle_signed_on_axis_v3v3v3_v3(far, cent, vert_arr[i]->co, nor);
|
||||
vang[i].second = i;
|
||||
vert_arr_map[i] = vert_arr[i];
|
||||
}
|
||||
|
||||
/* sort by angle and magic! - we have our ngon */
|
||||
std::ranges::sort(vang,
|
||||
[](const AngleIndex &a, const AngleIndex &b) { return a.first < b.first; });
|
||||
|
||||
/* --- */
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
vert_arr[i] = vert_arr_map[vang[i].second];
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
|
||||
void BM_elem_attrs_copy(BMesh *bm, const BMCustomDataCopyMap &map, const BMVert *src, BMVert *dst)
|
||||
{
|
||||
BLI_assert(src != dst);
|
||||
CustomData_bmesh_copy_block(bm->vdata, map, src->head.data, &dst->head.data);
|
||||
constexpr char hflag_mask = BM_ELEM_SELECT;
|
||||
dst->head.hflag = (dst->head.hflag & hflag_mask) | (src->head.hflag & ~hflag_mask);
|
||||
copy_v3_v3(dst->no, src->no);
|
||||
}
|
||||
void BM_elem_attrs_copy(BMesh *bm, const BMCustomDataCopyMap &map, const BMEdge *src, BMEdge *dst)
|
||||
{
|
||||
BLI_assert(src != dst);
|
||||
CustomData_bmesh_copy_block(bm->edata, map, src->head.data, &dst->head.data);
|
||||
constexpr char hflag_mask = BM_ELEM_SELECT;
|
||||
dst->head.hflag = (dst->head.hflag & hflag_mask) | (src->head.hflag & ~hflag_mask);
|
||||
}
|
||||
void BM_elem_attrs_copy(BMesh *bm, const BMCustomDataCopyMap &map, const BMFace *src, BMFace *dst)
|
||||
{
|
||||
BLI_assert(src != dst);
|
||||
CustomData_bmesh_copy_block(bm->pdata, map, src->head.data, &dst->head.data);
|
||||
constexpr char hflag_mask = BM_ELEM_SELECT | BM_ELEM_SELECT_UV;
|
||||
dst->head.hflag = (dst->head.hflag & hflag_mask) | (src->head.hflag & ~hflag_mask);
|
||||
copy_v3_v3(dst->no, src->no);
|
||||
dst->mat_nr = src->mat_nr;
|
||||
}
|
||||
void BM_elem_attrs_copy(BMesh *bm, const BMCustomDataCopyMap &map, const BMLoop *src, BMLoop *dst)
|
||||
{
|
||||
BLI_assert(src != dst);
|
||||
CustomData_bmesh_copy_block(bm->ldata, map, src->head.data, &dst->head.data);
|
||||
constexpr char hflag_mask = BM_ELEM_SELECT | BM_ELEM_SELECT_UV | BM_ELEM_SELECT_UV_EDGE;
|
||||
dst->head.hflag = (dst->head.hflag & hflag_mask) | (src->head.hflag & ~hflag_mask);
|
||||
}
|
||||
|
||||
void BM_elem_attrs_copy(BMesh *bm, const BMVert *src, BMVert *dst)
|
||||
{
|
||||
BLI_assert(src != dst);
|
||||
CustomData_bmesh_copy_block(bm->vdata, src->head.data, &dst->head.data);
|
||||
constexpr char hflag_mask = BM_ELEM_SELECT;
|
||||
dst->head.hflag = (dst->head.hflag & hflag_mask) | (src->head.hflag & ~hflag_mask);
|
||||
copy_v3_v3(dst->no, src->no);
|
||||
}
|
||||
void BM_elem_attrs_copy(BMesh *bm, const BMEdge *src, BMEdge *dst)
|
||||
{
|
||||
BLI_assert(src != dst);
|
||||
CustomData_bmesh_copy_block(bm->edata, src->head.data, &dst->head.data);
|
||||
constexpr char hflag_mask = BM_ELEM_SELECT;
|
||||
dst->head.hflag = (dst->head.hflag & hflag_mask) | (src->head.hflag & ~hflag_mask);
|
||||
}
|
||||
void BM_elem_attrs_copy(BMesh *bm, const BMFace *src, BMFace *dst)
|
||||
{
|
||||
BLI_assert(src != dst);
|
||||
constexpr char hflag_mask = BM_ELEM_SELECT | BM_ELEM_SELECT_UV;
|
||||
CustomData_bmesh_copy_block(bm->pdata, src->head.data, &dst->head.data);
|
||||
dst->head.hflag = (dst->head.hflag & hflag_mask) | (src->head.hflag & ~hflag_mask);
|
||||
copy_v3_v3(dst->no, src->no);
|
||||
dst->mat_nr = src->mat_nr;
|
||||
}
|
||||
void BM_elem_attrs_copy(BMesh *bm, const BMLoop *src, BMLoop *dst)
|
||||
{
|
||||
BLI_assert(src != dst);
|
||||
constexpr char hflag_mask = BM_ELEM_SELECT | BM_ELEM_SELECT_UV | BM_ELEM_SELECT_UV_EDGE;
|
||||
CustomData_bmesh_copy_block(bm->ldata, src->head.data, &dst->head.data);
|
||||
dst->head.hflag = (dst->head.hflag & hflag_mask) | (src->head.hflag & ~hflag_mask);
|
||||
}
|
||||
|
||||
void BM_elem_select_copy(BMesh *bm_dst, void *ele_dst_v, const void *ele_src_v)
|
||||
{
|
||||
BMHeader *ele_dst = static_cast<BMHeader *>(ele_dst_v);
|
||||
const BMHeader *ele_src = static_cast<const BMHeader *>(ele_src_v);
|
||||
|
||||
BLI_assert(ele_src->htype == ele_dst->htype);
|
||||
|
||||
if ((ele_src->hflag & BM_ELEM_SELECT) != (ele_dst->hflag & BM_ELEM_SELECT)) {
|
||||
BM_elem_select_set(
|
||||
bm_dst, reinterpret_cast<BMElem *>(ele_dst), (ele_src->hflag & BM_ELEM_SELECT) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* helper function for 'BM_mesh_copy' */
|
||||
static BMFace *bm_mesh_copy_new_face(BMesh *bm_new,
|
||||
const BMCustomDataCopyMap &face_map,
|
||||
const BMCustomDataCopyMap &loop_map,
|
||||
BMVert **vtable,
|
||||
BMEdge **etable,
|
||||
BMFace *f)
|
||||
{
|
||||
Array<BMLoop *, BM_DEFAULT_NGON_STACK_SIZE> loops(f->len);
|
||||
Array<BMVert *, BM_DEFAULT_NGON_STACK_SIZE> verts(f->len);
|
||||
Array<BMEdge *, BM_DEFAULT_NGON_STACK_SIZE> edges(f->len);
|
||||
|
||||
BMFace *f_new;
|
||||
BMLoop *l_iter, *l_first;
|
||||
int j;
|
||||
|
||||
j = 0;
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
loops[j] = l_iter;
|
||||
verts[j] = vtable[BM_elem_index_get(l_iter->v)];
|
||||
edges[j] = etable[BM_elem_index_get(l_iter->e)];
|
||||
j++;
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
f_new = BM_face_create(bm_new, verts.data(), edges.data(), f->len, nullptr, BM_CREATE_SKIP_CD);
|
||||
|
||||
if (UNLIKELY(f_new == nullptr)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* use totface in case adding some faces fails */
|
||||
BM_elem_index_set(f_new, (bm_new->totface - 1)); /* set_inline */
|
||||
|
||||
CustomData_bmesh_copy_block(bm_new->pdata, face_map, f->head.data, &f_new->head.data);
|
||||
copy_v3_v3(f_new->no, f->no);
|
||||
f_new->mat_nr = f->mat_nr;
|
||||
f_new->head.hflag = f->head.hflag; /* Low level! don't do this for normal API use. */
|
||||
|
||||
j = 0;
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f_new);
|
||||
do {
|
||||
CustomData_bmesh_copy_block(bm_new->ldata, loop_map, loops[j]->head.data, &l_iter->head.data);
|
||||
l_iter->head.hflag = loops[j]->head.hflag & ~BM_ELEM_SELECT;
|
||||
j++;
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
return f_new;
|
||||
}
|
||||
|
||||
static CustomData &get_bmesh_custom_data(BMesh &bm, const bke::AttrDomain domain)
|
||||
{
|
||||
switch (domain) {
|
||||
case bke::AttrDomain::Point:
|
||||
return bm.vdata;
|
||||
case bke::AttrDomain::Edge:
|
||||
return bm.edata;
|
||||
case bke::AttrDomain::Face:
|
||||
return bm.pdata;
|
||||
case bke::AttrDomain::Corner:
|
||||
return bm.ldata;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return bm.vdata;
|
||||
}
|
||||
}
|
||||
|
||||
void BM_mesh_copy_init_customdata_from_mesh_array(BMesh *bm_dst,
|
||||
const Mesh *me_src_array[],
|
||||
const int me_src_array_len,
|
||||
const BMAllocTemplate *allocsize)
|
||||
|
||||
{
|
||||
if (allocsize == nullptr) {
|
||||
allocsize = &bm_mesh_allocsize_default;
|
||||
}
|
||||
|
||||
bke::GeometrySet::GatheredAttributes attribute_info;
|
||||
for (int i = 0; i < me_src_array_len; i++) {
|
||||
const Mesh *me_src = me_src_array[i];
|
||||
for (const bke::Attribute &attr : me_src->attribute_storage.wrap()) {
|
||||
if (BM_attribute_stored_in_bmesh_builtin(attr.name())) {
|
||||
continue;
|
||||
}
|
||||
attribute_info.add(attr.name(), {attr.domain(), attr.data_type()});
|
||||
}
|
||||
}
|
||||
|
||||
for (const int i : attribute_info.names.index_range()) {
|
||||
const StringRef name = attribute_info.names[i];
|
||||
const bke::AttrDomain domain = attribute_info.kinds[i].domain;
|
||||
const eCustomDataType data_type = *bke::attr_type_to_custom_data_type(
|
||||
attribute_info.kinds[i].data_type);
|
||||
CustomData &custom_data = get_bmesh_custom_data(*bm_dst, domain);
|
||||
CustomData_add_layer_named(&custom_data, data_type, CD_SET_DEFAULT, 0, name);
|
||||
}
|
||||
|
||||
for (int i = 0; i < me_src_array_len; i++) {
|
||||
const Mesh *me_src = me_src_array[i];
|
||||
CustomData_merge_layout(
|
||||
&me_src->vert_data, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0);
|
||||
CustomData_merge_layout(
|
||||
&me_src->edge_data, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0);
|
||||
CustomData_merge_layout(
|
||||
&me_src->face_data, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0);
|
||||
CustomData_merge_layout(
|
||||
&me_src->corner_data, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0);
|
||||
}
|
||||
|
||||
CustomData_bmesh_init_pool(&bm_dst->vdata, allocsize->totvert, BM_VERT);
|
||||
CustomData_bmesh_init_pool(&bm_dst->edata, allocsize->totedge, BM_EDGE);
|
||||
CustomData_bmesh_init_pool(&bm_dst->ldata, allocsize->totloop, BM_LOOP);
|
||||
CustomData_bmesh_init_pool(&bm_dst->pdata, allocsize->totface, BM_FACE);
|
||||
}
|
||||
|
||||
void BM_mesh_copy_init_customdata_from_mesh(BMesh *bm_dst,
|
||||
const Mesh *me_src,
|
||||
const BMAllocTemplate *allocsize)
|
||||
{
|
||||
BM_mesh_copy_init_customdata_from_mesh_array(bm_dst, &me_src, 1, allocsize);
|
||||
}
|
||||
|
||||
void BM_mesh_copy_init_customdata(BMesh *bm_dst, BMesh *bm_src, const BMAllocTemplate *allocsize)
|
||||
{
|
||||
if (allocsize == nullptr) {
|
||||
allocsize = &bm_mesh_allocsize_default;
|
||||
}
|
||||
|
||||
CustomData_init_layout_from(
|
||||
&bm_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0);
|
||||
CustomData_init_layout_from(
|
||||
&bm_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0);
|
||||
CustomData_init_layout_from(
|
||||
&bm_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0);
|
||||
CustomData_init_layout_from(
|
||||
&bm_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0);
|
||||
|
||||
CustomData_bmesh_init_pool(&bm_dst->vdata, allocsize->totvert, BM_VERT);
|
||||
CustomData_bmesh_init_pool(&bm_dst->edata, allocsize->totedge, BM_EDGE);
|
||||
CustomData_bmesh_init_pool(&bm_dst->ldata, allocsize->totloop, BM_LOOP);
|
||||
CustomData_bmesh_init_pool(&bm_dst->pdata, allocsize->totface, BM_FACE);
|
||||
}
|
||||
|
||||
void BM_mesh_copy_init_customdata_all_layers(BMesh *bm_dst,
|
||||
BMesh *bm_src,
|
||||
const char htype,
|
||||
const BMAllocTemplate *allocsize)
|
||||
{
|
||||
if (allocsize == nullptr) {
|
||||
allocsize = &bm_mesh_allocsize_default;
|
||||
}
|
||||
|
||||
const char htypes[4] = {BM_VERT, BM_EDGE, BM_LOOP, BM_FACE};
|
||||
BLI_assert(((&bm_dst->vdata + 1) == &bm_dst->edata) &&
|
||||
((&bm_dst->vdata + 2) == &bm_dst->ldata) && ((&bm_dst->vdata + 3) == &bm_dst->pdata));
|
||||
|
||||
BLI_assert(((&allocsize->totvert + 1) == &allocsize->totedge) &&
|
||||
((&allocsize->totvert + 2) == &allocsize->totloop) &&
|
||||
((&allocsize->totvert + 3) == &allocsize->totface));
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!(htypes[i] & htype)) {
|
||||
continue;
|
||||
}
|
||||
CustomData *dst = &bm_dst->vdata + i;
|
||||
CustomData *src = &bm_src->vdata + i;
|
||||
const int size = *(&allocsize->totvert + i);
|
||||
|
||||
for (int l = 0; l < src->totlayer; l++) {
|
||||
CustomData_add_layer_named(
|
||||
dst, eCustomDataType(src->layers[l].type), CD_SET_DEFAULT, 0, src->layers[l].name);
|
||||
/* Needed to keep this a working shape key layer (see also #customdata_merge_internal). */
|
||||
dst->layers[l].uid = src->layers[l].uid;
|
||||
}
|
||||
CustomData_bmesh_init_pool(dst, size, htypes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
BMesh *BM_mesh_copy(BMesh *bm_old)
|
||||
{
|
||||
BMesh *bm_new;
|
||||
BMVert *v, *v_new, **vtable = nullptr;
|
||||
BMEdge *e, *e_new, **etable = nullptr;
|
||||
BMFace *f, *f_new, **ftable = nullptr;
|
||||
BMElem **eletable;
|
||||
BMIter iter;
|
||||
int i;
|
||||
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_BM(bm_old);
|
||||
|
||||
/* allocate a bmesh */
|
||||
BMeshCreateParams params{};
|
||||
params.use_toolflags = bm_old->use_toolflags;
|
||||
bm_new = BM_mesh_create(&allocsize, ¶ms);
|
||||
|
||||
BM_mesh_copy_init_customdata(bm_new, bm_old, &allocsize);
|
||||
|
||||
const BMCustomDataCopyMap vert_map = CustomData_bmesh_copy_map_calc(bm_old->vdata,
|
||||
bm_new->vdata);
|
||||
const BMCustomDataCopyMap edge_map = CustomData_bmesh_copy_map_calc(bm_old->edata,
|
||||
bm_new->edata);
|
||||
const BMCustomDataCopyMap face_map = CustomData_bmesh_copy_map_calc(bm_old->pdata,
|
||||
bm_new->pdata);
|
||||
const BMCustomDataCopyMap loop_map = CustomData_bmesh_copy_map_calc(bm_old->ldata,
|
||||
bm_new->ldata);
|
||||
|
||||
vtable = MEM_new_array_uninitialized<BMVert *>(bm_old->totvert, "BM_mesh_copy vtable");
|
||||
etable = MEM_new_array_uninitialized<BMEdge *>(bm_old->totedge, "BM_mesh_copy etable");
|
||||
ftable = MEM_new_array_uninitialized<BMFace *>(bm_old->totface, "BM_mesh_copy ftable");
|
||||
|
||||
BM_ITER_MESH_INDEX (v, &iter, bm_old, BM_VERTS_OF_MESH, i) {
|
||||
/* copy between meshes so can't use 'example' argument */
|
||||
v_new = BM_vert_create(bm_new, v->co, nullptr, BM_CREATE_SKIP_CD);
|
||||
CustomData_bmesh_copy_block(bm_new->vdata, vert_map, v->head.data, &v_new->head.data);
|
||||
copy_v3_v3(v_new->no, v->no);
|
||||
v_new->head.hflag = v->head.hflag; /* Low level! don't do this for normal API use. */
|
||||
vtable[i] = v_new;
|
||||
BM_elem_index_set(v, i); /* set_inline */
|
||||
BM_elem_index_set(v_new, i); /* set_inline */
|
||||
}
|
||||
bm_old->elem_index_dirty &= ~BM_VERT;
|
||||
bm_new->elem_index_dirty &= ~BM_VERT;
|
||||
|
||||
/* safety check */
|
||||
BLI_assert(i == bm_old->totvert);
|
||||
|
||||
BM_ITER_MESH_INDEX (e, &iter, bm_old, BM_EDGES_OF_MESH, i) {
|
||||
e_new = BM_edge_create(bm_new,
|
||||
vtable[BM_elem_index_get(e->v1)],
|
||||
vtable[BM_elem_index_get(e->v2)],
|
||||
e,
|
||||
BM_CREATE_SKIP_CD);
|
||||
|
||||
CustomData_bmesh_copy_block(bm_new->edata, edge_map, e->head.data, &e_new->head.data);
|
||||
e_new->head.hflag = e->head.hflag; /* Low level! don't do this for normal API use. */
|
||||
etable[i] = e_new;
|
||||
BM_elem_index_set(e, i); /* set_inline */
|
||||
BM_elem_index_set(e_new, i); /* set_inline */
|
||||
}
|
||||
bm_old->elem_index_dirty &= ~BM_EDGE;
|
||||
bm_new->elem_index_dirty &= ~BM_EDGE;
|
||||
|
||||
/* safety check */
|
||||
BLI_assert(i == bm_old->totedge);
|
||||
|
||||
BM_ITER_MESH_INDEX (f, &iter, bm_old, BM_FACES_OF_MESH, i) {
|
||||
BM_elem_index_set(f, i); /* set_inline */
|
||||
|
||||
f_new = bm_mesh_copy_new_face(bm_new, face_map, loop_map, vtable, etable, f);
|
||||
|
||||
ftable[i] = f_new;
|
||||
|
||||
if (f == bm_old->act_face) {
|
||||
bm_new->act_face = f_new;
|
||||
}
|
||||
}
|
||||
bm_old->elem_index_dirty &= ~BM_FACE;
|
||||
bm_new->elem_index_dirty &= ~BM_FACE;
|
||||
|
||||
/* Low level! don't do this for normal API use. */
|
||||
bm_new->totvertsel = bm_old->totvertsel;
|
||||
bm_new->totedgesel = bm_old->totedgesel;
|
||||
bm_new->totfacesel = bm_old->totfacesel;
|
||||
|
||||
/* safety check */
|
||||
BLI_assert(i == bm_old->totface);
|
||||
|
||||
/* copy over edit selection history */
|
||||
for (BMEditSelection &ese : bm_old->selected) {
|
||||
BMElem *ele = nullptr;
|
||||
|
||||
switch (ese.htype) {
|
||||
case BM_VERT:
|
||||
eletable = reinterpret_cast<BMElem **>(vtable);
|
||||
break;
|
||||
case BM_EDGE:
|
||||
eletable = reinterpret_cast<BMElem **>(etable);
|
||||
break;
|
||||
case BM_FACE:
|
||||
eletable = reinterpret_cast<BMElem **>(ftable);
|
||||
break;
|
||||
default:
|
||||
eletable = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (eletable) {
|
||||
ele = eletable[BM_elem_index_get(ese.ele)];
|
||||
if (ele) {
|
||||
BM_select_history_store(bm_new, ele);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MEM_delete(etable);
|
||||
MEM_delete(vtable);
|
||||
MEM_delete(ftable);
|
||||
|
||||
/* Copy various settings. */
|
||||
bm_new->shapenr = bm_old->shapenr;
|
||||
bm_new->selectmode = bm_old->selectmode;
|
||||
|
||||
return bm_new;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
190
blender-5.2.0/source/blender/bmesh/intern/bmesh_construct.hh
Normal file
190
blender-5.2.0/source/blender/bmesh/intern/bmesh_construct.hh
Normal file
@@ -0,0 +1,190 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
#include "bmesh_core.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMAllocTemplate;
|
||||
struct BMCustomDataCopyMap;
|
||||
struct Mesh;
|
||||
|
||||
/**
|
||||
* Fill in a vertex array from an edge array.
|
||||
*
|
||||
* \returns false if any verts aren't found.
|
||||
*/
|
||||
bool BM_verts_from_edges(BMVert **vert_arr, BMEdge **edge_arr, int len);
|
||||
|
||||
/**
|
||||
* Fill in an edge array from a vertex array (connected polygon loop).
|
||||
*
|
||||
* \returns false if any edges aren't found.
|
||||
*/
|
||||
bool BM_edges_from_verts(BMEdge **edge_arr, BMVert **vert_arr, int len);
|
||||
/**
|
||||
* Fill in an edge array from a vertex array (connected polygon loop).
|
||||
* Creating edges as-needed.
|
||||
*/
|
||||
void BM_edges_from_verts_ensure(BMesh *bm, BMEdge **edge_arr, BMVert **vert_arr, int len);
|
||||
|
||||
/**
|
||||
* Makes an NGon from an un-ordered set of verts.
|
||||
*
|
||||
* Assumes:
|
||||
* - that verts are only once in the list.
|
||||
* - that the verts have roughly planer bounds
|
||||
* - that the verts are roughly circular
|
||||
*
|
||||
* There can be concave areas but overlapping folds from the center point will fail.
|
||||
*
|
||||
* A brief explanation of the method used
|
||||
* - find the center point
|
||||
* - find the normal of the vertex-cloud
|
||||
* - order the verts around the face based on their angle to the normal vector at the center point.
|
||||
*
|
||||
* \note Since this is a vertex-cloud there is no direction.
|
||||
*/
|
||||
void BM_verts_sort_radial_plane(BMVert **vert_arr, int len);
|
||||
|
||||
/**
|
||||
* \brief Make Quad/Triangle
|
||||
*
|
||||
* Creates a new quad or triangle from a list of 3 or 4 vertices.
|
||||
* If \a no_double is true, then a check is done to see if a face
|
||||
* with these vertices already exists and returns it instead.
|
||||
*
|
||||
* If a pointer to an example face is provided, its custom data
|
||||
* and properties will be copied to the new face.
|
||||
*
|
||||
* \note The winding of the face is determined by the order
|
||||
* of the vertices in the vertex array.
|
||||
*/
|
||||
BMFace *BM_face_create_quad_tri(BMesh *bm,
|
||||
BMVert *v1,
|
||||
BMVert *v2,
|
||||
BMVert *v3,
|
||||
BMVert *v4,
|
||||
const BMFace *f_example,
|
||||
eBMCreateFlag create_flag);
|
||||
|
||||
/**
|
||||
* \brief copies face loop data from shared adjacent faces.
|
||||
*
|
||||
* \param filter_fn: A function that filters the source loops before copying
|
||||
* (don't always want to copy all).
|
||||
*
|
||||
* \note when a matching edge is found, both loops of that edge are copied
|
||||
* this is done since the face may not be completely surrounded by faces,
|
||||
* this way: a quad with 2 connected quads on either side will still get all 4 loops updated
|
||||
*/
|
||||
void BM_face_copy_shared(BMesh *bm, BMFace *f, BMLoopFilterFunc filter_fn, void *user_data);
|
||||
|
||||
/**
|
||||
* \brief Make NGon
|
||||
*
|
||||
* Makes an ngon from an unordered list of edges.
|
||||
* Verts \a v1 and \a v2 define the winding of the new face.
|
||||
*
|
||||
* \a edges are not required to be ordered, simply to form
|
||||
* a single closed loop as a whole.
|
||||
*
|
||||
* \note While this function will work fine when the edges
|
||||
* are already sorted, if the edges are always going to be sorted,
|
||||
* #BM_face_create should be considered over this function as it
|
||||
* avoids some unnecessary work.
|
||||
*/
|
||||
BMFace *BM_face_create_ngon(BMesh *bm,
|
||||
BMVert *v1,
|
||||
BMVert *v2,
|
||||
BMEdge **edges,
|
||||
int len,
|
||||
const BMFace *f_example,
|
||||
eBMCreateFlag create_flag);
|
||||
/**
|
||||
* Create an ngon from an array of sorted verts
|
||||
*
|
||||
* Special features this has over other functions.
|
||||
* - Optionally calculate winding based on surrounding edges.
|
||||
* - Optionally create edges between vertices.
|
||||
* - Uses verts so no need to find edges (handy when you only have verts)
|
||||
*/
|
||||
BMFace *BM_face_create_ngon_verts(BMesh *bm,
|
||||
BMVert **vert_arr,
|
||||
int len,
|
||||
const BMFace *f_example,
|
||||
eBMCreateFlag create_flag,
|
||||
bool calc_winding,
|
||||
bool create_edges);
|
||||
|
||||
/**
|
||||
* Copy attributes between elements with a precalculated map of copy operations. This significantly
|
||||
* improves performance when copying, since all the work of finding common layers doesn't have to
|
||||
* be done for every element.
|
||||
*/
|
||||
void BM_elem_attrs_copy(BMesh *bm,
|
||||
const BMCustomDataCopyMap &cd_map,
|
||||
const BMVert *src,
|
||||
BMVert *dst);
|
||||
void BM_elem_attrs_copy(BMesh *bm,
|
||||
const BMCustomDataCopyMap &cd_map,
|
||||
const BMEdge *src,
|
||||
BMEdge *dst);
|
||||
void BM_elem_attrs_copy(BMesh *bm,
|
||||
const BMCustomDataCopyMap &cd_map,
|
||||
const BMFace *src,
|
||||
BMFace *dst);
|
||||
void BM_elem_attrs_copy(BMesh *bm,
|
||||
const BMCustomDataCopyMap &cd_map,
|
||||
const BMLoop *src,
|
||||
BMLoop *dst);
|
||||
|
||||
/** Copy attributes between elements in the same BMesh. */
|
||||
void BM_elem_attrs_copy(BMesh *bm, const BMVert *src, BMVert *dst);
|
||||
void BM_elem_attrs_copy(BMesh *bm, const BMEdge *src, BMEdge *dst);
|
||||
void BM_elem_attrs_copy(BMesh *bm, const BMFace *src, BMFace *dst);
|
||||
void BM_elem_attrs_copy(BMesh *bm, const BMLoop *src, BMLoop *dst);
|
||||
|
||||
void BM_elem_select_copy(BMesh *bm_dst, void *ele_dst_v, const void *ele_src_v);
|
||||
|
||||
/**
|
||||
* Initialize the `bm_dst` layers in preparation for populating its contents with multiple meshes.
|
||||
* Typically done using multiple calls to #BM_mesh_bm_from_me with the same `bm` argument.
|
||||
*
|
||||
* \note While the custom-data layers of all meshes are created, the active layers are set
|
||||
* by the first instance mesh containing that layer type.
|
||||
* This means the first mesh should always be the main mesh (from the user perspective),
|
||||
* as this is the mesh they have control over (active UV layer for rendering for example).
|
||||
*/
|
||||
void BM_mesh_copy_init_customdata_from_mesh_array(BMesh *bm_dst,
|
||||
const Mesh *me_src_array[],
|
||||
int me_src_array_len,
|
||||
const BMAllocTemplate *allocsize);
|
||||
void BM_mesh_copy_init_customdata_from_mesh(BMesh *bm_dst,
|
||||
const Mesh *me_src,
|
||||
const BMAllocTemplate *allocsize);
|
||||
void BM_mesh_copy_init_customdata(BMesh *bm_dst, BMesh *bm_src, const BMAllocTemplate *allocsize);
|
||||
/**
|
||||
* Similar to #BM_mesh_copy_init_customdata but copies all layers ignoring
|
||||
* flags like #CD_FLAG_NOCOPY.
|
||||
*
|
||||
* \param bm_dst: BMesh whose custom-data layers will be added.
|
||||
* \param bm_src: BMesh whose custom-data layers will be copied.
|
||||
* \param htype: Specifies which custom-data layers will be initiated.
|
||||
* \param allocsize: Initialize the memory-pool before use (may be an estimate).
|
||||
*/
|
||||
void BM_mesh_copy_init_customdata_all_layers(BMesh *bm_dst,
|
||||
BMesh *bm_src,
|
||||
char htype,
|
||||
const BMAllocTemplate *allocsize);
|
||||
BMesh *BM_mesh_copy(BMesh *bm_old);
|
||||
|
||||
} // namespace blender
|
||||
3022
blender-5.2.0/source/blender/bmesh/intern/bmesh_core.cc
Normal file
3022
blender-5.2.0/source/blender/bmesh/intern/bmesh_core.cc
Normal file
File diff suppressed because it is too large
Load Diff
488
blender-5.2.0/source/blender/bmesh/intern/bmesh_core.hh
Normal file
488
blender-5.2.0/source/blender/bmesh/intern/bmesh_core.hh
Normal file
@@ -0,0 +1,488 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
#include "BKE_customdata.hh"
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
struct BMLoopList;
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* When copying between different BMesh objects,
|
||||
* `copy_verts` & `copy_edges` should always be true.
|
||||
*/
|
||||
BMFace *BM_face_copy(BMesh *bm,
|
||||
const BMCustomDataCopyMap &cd_face_map,
|
||||
const BMCustomDataCopyMap &cd_loop_map,
|
||||
BMFace *f,
|
||||
bool copy_verts,
|
||||
bool copy_edges);
|
||||
BMFace *BM_face_copy(BMesh *bm, BMFace *f, bool copy_verts, bool copy_edges);
|
||||
|
||||
enum eBMCreateFlag {
|
||||
BM_CREATE_NOP = 0,
|
||||
/** Faces and edges only. */
|
||||
BM_CREATE_NO_DOUBLE = (1 << 1),
|
||||
/**
|
||||
* Skip custom-data - for all element types data,
|
||||
* use if we immediately write custom-data into the element so this skips copying from 'example'
|
||||
* arguments or setting defaults, speeds up conversion when data is converted all at once.
|
||||
*/
|
||||
BM_CREATE_SKIP_CD = (1 << 2),
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Main function for creating a new vertex.
|
||||
*/
|
||||
BMVert *BM_vert_create(BMesh *bm,
|
||||
const float co[3],
|
||||
const BMVert *v_example,
|
||||
eBMCreateFlag create_flag);
|
||||
/**
|
||||
* \brief Main function for creating a new edge.
|
||||
*
|
||||
* \note Duplicate edges are supported by the API however users should _never_ see them.
|
||||
* so unless you need a unique edge or know the edge won't exist,
|
||||
* you should call with \a no_double = true.
|
||||
*/
|
||||
BMEdge *BM_edge_create(
|
||||
BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *e_example, eBMCreateFlag create_flag);
|
||||
/**
|
||||
* Main face creation function
|
||||
*
|
||||
* \param bm: The mesh
|
||||
* \param verts: A sorted array of verts size of len
|
||||
* \param edges: A sorted array of edges size of len
|
||||
* \param len: Length of the face
|
||||
* \param create_flag: Options for creating the face
|
||||
*/
|
||||
BMFace *BM_face_create(BMesh *bm,
|
||||
BMVert *const *verts,
|
||||
BMEdge *const *edges,
|
||||
int len,
|
||||
const BMFace *f_example,
|
||||
eBMCreateFlag create_flag);
|
||||
/**
|
||||
* Wrapper for #BM_face_create when you don't have an edge array
|
||||
*/
|
||||
BMFace *BM_face_create_verts(BMesh *bm,
|
||||
BMVert **vert_arr,
|
||||
int len,
|
||||
const BMFace *f_example,
|
||||
eBMCreateFlag create_flag,
|
||||
bool create_edges);
|
||||
|
||||
/**
|
||||
* Kills all edges associated with \a f, along with any other faces containing those edges.
|
||||
*/
|
||||
void BM_face_edges_kill(BMesh *bm, BMFace *f);
|
||||
/**
|
||||
* kills all verts associated with \a f, along with any other faces containing
|
||||
* those vertices
|
||||
*/
|
||||
void BM_face_verts_kill(BMesh *bm, BMFace *f);
|
||||
|
||||
/**
|
||||
* A version of #BM_face_kill which removes edges and verts
|
||||
* which have no remaining connected geometry.
|
||||
*/
|
||||
void BM_face_kill_loose(BMesh *bm, BMFace *f);
|
||||
|
||||
/**
|
||||
* Kills \a f and its loops.
|
||||
*/
|
||||
void BM_face_kill(BMesh *bm, BMFace *f);
|
||||
/**
|
||||
* Kills \a e and all faces that use it.
|
||||
*/
|
||||
void BM_edge_kill(BMesh *bm, BMEdge *e);
|
||||
/**
|
||||
* Kills \a v and all edges that use it.
|
||||
*/
|
||||
void BM_vert_kill(BMesh *bm, BMVert *v);
|
||||
|
||||
/**
|
||||
* \brief Splice Edge
|
||||
*
|
||||
* Splice two unique edges which share the same two vertices into one edge.
|
||||
* (\a e_src into \a e_dst, removing e_src).
|
||||
*
|
||||
* \return Success
|
||||
*
|
||||
* \note Edges must already have the same vertices.
|
||||
*/
|
||||
bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src);
|
||||
/**
|
||||
* \brief Splice Vert
|
||||
*
|
||||
* Merges two verts into one
|
||||
* (\a v_src into \a v_dst, removing \a v_src).
|
||||
*
|
||||
* \return Success
|
||||
*
|
||||
* \warning This doesn't work for collapsing edges,
|
||||
* where \a v and \a vtarget are connected by an edge
|
||||
* (assert checks for this case).
|
||||
*
|
||||
* \note To check if collapsing would create duplicate geometry, see:
|
||||
* - #BM_vert_splice_check_double_edge.
|
||||
* - #BM_vert_splice_check_double_face.
|
||||
*/
|
||||
bool BM_vert_splice(BMesh *bm, BMVert *v_dst, BMVert *v_src);
|
||||
/**
|
||||
* Check if splicing vertices would create any double edges.
|
||||
*
|
||||
* \note assume caller will handle case where verts share an edge.
|
||||
*/
|
||||
bool BM_vert_splice_check_double_edge(BMVert *v_a, BMVert *v_b);
|
||||
/**
|
||||
* Check if splicing vertices would create any double faces.
|
||||
*
|
||||
* \return true if calling #BM_vert_splice on the vertex pair would create a duplicate face.
|
||||
*/
|
||||
bool BM_vert_splice_check_double_face(BMVert *v_a, BMVert *v_b);
|
||||
/**
|
||||
* Check if collapsing `v_collapse`.would create duplicate faces.
|
||||
*
|
||||
* \param v_collapse: A vertex with exactly two connected edges (see #BM_vert_is_edge_pair).
|
||||
*
|
||||
* \return true if calling #BM_vert_collapse on `v_collapse` would create a duplicate face.
|
||||
*/
|
||||
bool BM_vert_collapse_check_double_face(BMVert *v_collapse);
|
||||
/**
|
||||
* Check if splitting a face between `l_a->v` & `l_b->v` would create
|
||||
* a duplicate face on either side of the split.
|
||||
*
|
||||
* \note Arguments `(l_a, l_b, f_len)` are equivalent to `(l_b, l_a, (l_a->f->len - f_len) + 2)`,
|
||||
* that is to say - the side of the face checked isn't important.
|
||||
*/
|
||||
bool BM_face_split_check_double_face(BMLoop *l_a, BMLoop *l_b, int f_len);
|
||||
/**
|
||||
* Check two faces share the same vertices over a partial span of their loops,
|
||||
* comparing `l_a` -> `l_a_end` against `l_b` -> `l_b_end` (inclusive).
|
||||
* Both spans must have the same topological length,
|
||||
* and `l_a` & `l_b` must belong to different faces.
|
||||
*
|
||||
* Useful to check whether collapsing, splicing or splitting would create a duplicate face.
|
||||
*/
|
||||
bool BM_face_pair_overlap_check_subset_same_winding(const BMLoop *l_a,
|
||||
const BMLoop *l_a_end,
|
||||
const BMLoop *l_b,
|
||||
const BMLoop *l_b_end);
|
||||
/**
|
||||
* A version of #BM_face_pair_overlap_check_subset_same_winding that walks `l_b` -> `l_b_end`
|
||||
* in the reverse direction (for a candidate face of opposite winding).
|
||||
*/
|
||||
bool BM_face_pair_overlap_check_subset_swap_winding(const BMLoop *l_a,
|
||||
const BMLoop *l_a_end,
|
||||
const BMLoop *l_b,
|
||||
const BMLoop *l_b_end);
|
||||
|
||||
/**
|
||||
* \brief Loop Reverse
|
||||
*
|
||||
* Changes the winding order of a face from CW to CCW or vice versa.
|
||||
*
|
||||
* \param cd_loop_mdisp_offset: Cached result of `CustomData_get_offset(&bm->ldata, CD_MDISPS)`.
|
||||
* \param use_loop_mdisp_flip: When set, flip the Z-depth of the mdisp,
|
||||
* (use when flipping normals, disable when mirroring, eg: symmetrize).
|
||||
*/
|
||||
void bmesh_kernel_loop_reverse(BMesh *bm,
|
||||
BMFace *f,
|
||||
int cd_loop_mdisp_offset,
|
||||
bool use_loop_mdisp_flip);
|
||||
|
||||
/**
|
||||
* Avoid calling this where possible,
|
||||
* low level function so both face pointers remain intact but point to swapped data.
|
||||
* \note must be from the same bmesh.
|
||||
*/
|
||||
void bmesh_face_swap_data(BMFace *f_a, BMFace *f_b);
|
||||
|
||||
/**
|
||||
* \brief Join Connected Faces
|
||||
*
|
||||
* Joins a collected group of faces into one. Only restriction on
|
||||
* the input data is that the faces must be connected to each other.
|
||||
*
|
||||
* \return The newly created combine BMFace.
|
||||
*
|
||||
* \note If a pair of faces share multiple edges,
|
||||
* the pair of faces will be joined at every edge.
|
||||
*
|
||||
* \param bm: The bmesh.
|
||||
* \param faces: An array of faces to join.
|
||||
* \param totface: The length of the face array to join.
|
||||
* \param do_del: if true, remove the original faces, internal edges, and internal verts such that
|
||||
* they are replaced by the new face.
|
||||
* \param r_double: A pointer to a BMFace* that is controls processing of doubled faces.
|
||||
* - When `r_double` is nullptr:
|
||||
* - If a new face would be made which would double an existing face, then instead of creating a
|
||||
* new face, the existing face will be reused and returned instead.
|
||||
* - The calling function must not make ANY assumption about whether the returned BMFace* is
|
||||
* new, or a reused face that may already have set header flags, contain custom data, etc.
|
||||
* - When `r_double` is a pointer to a BMFace*:
|
||||
* - If the new join face is not a double of an existing face, then `r_double` is set to nullptr.
|
||||
* - If the new join face doubles an existing face, then `r_double` is set to the existing face,
|
||||
* and the return value is the newly created face. The double will NOT be removed, meaning the
|
||||
* BMesh is in an invalid state, and the calling function must fix that inconsistency.
|
||||
* - If an error occurs and nullptr is returned, `r_double` will be set to nullptr as well.
|
||||
*
|
||||
* \note this is a generic, flexible join faces function,
|
||||
* almost everything uses this, including #BM_faces_join_pair
|
||||
*
|
||||
* \note On callers asserting when `*r_double != nullptr`.
|
||||
* For some callers the existing algorithm does not check for or handle double faces.
|
||||
* This can result in invalid meshes being returned.
|
||||
* The returned value in `r_double` should be examined and if found,
|
||||
* the algorithm should be adjusted. Until this is changed, at least warn.
|
||||
* This comment can be removed when all callers handle this case.
|
||||
*/
|
||||
BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface, bool do_del, BMFace **r_double);
|
||||
/**
|
||||
* High level function which wraps both #bmesh_kernel_vert_separate and #bmesh_kernel_edge_separate
|
||||
*/
|
||||
void BM_vert_separate(BMesh *bm,
|
||||
BMVert *v,
|
||||
BMEdge **e_in,
|
||||
int e_in_len,
|
||||
bool copy_select,
|
||||
BMVert ***r_vout,
|
||||
int *r_vout_len);
|
||||
/**
|
||||
* A version of #BM_vert_separate which takes a flag.
|
||||
*/
|
||||
void BM_vert_separate_hflag(
|
||||
BMesh *bm, BMVert *v, char hflag, bool copy_select, BMVert ***r_vout, int *r_vout_len);
|
||||
void BM_vert_separate_tested_edges(
|
||||
BMesh *bm, BMVert *v_dst, BMVert *v_src, bool (*testfn)(BMEdge *, void *arg), void *arg);
|
||||
|
||||
/**
|
||||
* BMesh Kernel: For modifying structure.
|
||||
*
|
||||
* Names are on the verbose side but these are only for low-level access.
|
||||
*/
|
||||
/**
|
||||
* \brief Separate Vert
|
||||
*
|
||||
* Separates all disjoint fans that meet at a vertex, making a unique
|
||||
* vertex for each region. returns an array of all resulting vertices.
|
||||
*
|
||||
* \note this is a low level function, bm_edge_separate needs to run on edges first
|
||||
* or, the faces sharing verts must not be sharing edges for them to split at least.
|
||||
*
|
||||
* \return Success
|
||||
*/
|
||||
void bmesh_kernel_vert_separate(
|
||||
BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len, bool copy_select);
|
||||
/**
|
||||
* \brief Separate Edge
|
||||
*
|
||||
* Separates a single edge into two edge: the original edge and
|
||||
* a new edge that has only \a l_sep in its radial.
|
||||
*
|
||||
* \return Success
|
||||
*
|
||||
* \note Does nothing if \a l_sep is already the only loop in the
|
||||
* edge radial.
|
||||
*/
|
||||
void bmesh_kernel_edge_separate(BMesh *bm, BMEdge *e, BMLoop *l_sep, bool copy_select);
|
||||
|
||||
/**
|
||||
* \brief Split Face Make Edge (SFME)
|
||||
*
|
||||
* \warning this is a low level function, most likely you want to use #BM_face_split()
|
||||
*
|
||||
* Takes as input two vertices in a single face.
|
||||
* An edge is created which divides the original face into two distinct regions.
|
||||
* One of the regions is assigned to the original face and it is closed off.
|
||||
* The second region has a new face assigned to it.
|
||||
*
|
||||
* \par Examples:
|
||||
* <pre>
|
||||
* Before: After:
|
||||
* +--------+ +--------+
|
||||
* | | | |
|
||||
* | | | f1 |
|
||||
* v1 f1 v2 v1======v2
|
||||
* | | | f2 |
|
||||
* | | | |
|
||||
* +--------+ +--------+
|
||||
* </pre>
|
||||
*
|
||||
* \note the input vertices can be part of the same edge. This will
|
||||
* result in a two edged face. This is desirable for advanced construction
|
||||
* tools and particularly essential for edge bevel. Because of this it is
|
||||
* up to the caller to decide what to do with the extra edge.
|
||||
*
|
||||
* \note If \a holes is NULL, then both faces will lose
|
||||
* all holes from the original face. Also, you cannot split between
|
||||
* a hole vert and a boundary vert; that case is handled by higher-
|
||||
* level wrapping functions (when holes are fully implemented, anyway).
|
||||
*
|
||||
* \note that holes represents which holes goes to the new face, and of
|
||||
* course this requires removing them from the existing face first, since
|
||||
* you cannot have linked list links inside multiple lists.
|
||||
*
|
||||
* \return A BMFace pointer
|
||||
*/
|
||||
BMFace *bmesh_kernel_split_face_make_edge(BMesh *bm,
|
||||
BMFace *f,
|
||||
BMLoop *l_v1,
|
||||
BMLoop *l_v2,
|
||||
BMLoop **r_l,
|
||||
#ifdef USE_BMESH_HOLES
|
||||
ListBaseT<BMLoopList> *holes,
|
||||
#endif
|
||||
BMEdge *example,
|
||||
bool no_double);
|
||||
|
||||
/**
|
||||
* \brief Split Edge Make Vert (SEMV)
|
||||
*
|
||||
* Takes \a e edge and splits it into two, creating a new vert.
|
||||
* \a tv should be one end of \a e : the newly created edge
|
||||
* will be attached to that end and is returned in \a r_e.
|
||||
*
|
||||
* \par Examples:
|
||||
*
|
||||
* <pre>
|
||||
* E
|
||||
* Before: OV-------------TV
|
||||
* E RE
|
||||
* After: OV------NV-----TV
|
||||
* </pre>
|
||||
*
|
||||
* \return The newly created BMVert pointer.
|
||||
*/
|
||||
BMVert *bmesh_kernel_split_edge_make_vert(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **r_e);
|
||||
/**
|
||||
* \brief Join Edge Kill Vert (JEKV)
|
||||
*
|
||||
* Takes an edge \a e_kill and pointer to one of its vertices \a v_kill
|
||||
* and collapses the edge on that vertex.
|
||||
*
|
||||
* \par Examples:
|
||||
*
|
||||
* <pre>
|
||||
* Before: e_old e_kill
|
||||
* +-------+-------+
|
||||
* | | |
|
||||
* v_old v_kill v_target
|
||||
*
|
||||
* After: e_old
|
||||
* +---------------+
|
||||
* | |
|
||||
* v_old v_target
|
||||
* </pre>
|
||||
*
|
||||
* \par Restrictions:
|
||||
* KV is a vertex that must have a valance of exactly two. Furthermore
|
||||
* both edges in KV's disk cycle (OE and KE) must be unique (no double edges).
|
||||
*
|
||||
* \return The resulting edge, NULL for failure.
|
||||
*
|
||||
* \note This euler has the possibility of creating
|
||||
* faces with just 2 edges. It is up to the caller to decide what to do with
|
||||
* these faces.
|
||||
*/
|
||||
BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm,
|
||||
BMEdge *e_kill,
|
||||
BMVert *v_kill,
|
||||
bool do_del,
|
||||
bool check_edge_exists,
|
||||
bool kill_degenerate_faces,
|
||||
bool kill_duplicate_faces);
|
||||
/**
|
||||
* \brief Join Vert Kill Edge (JVKE)
|
||||
*
|
||||
* Collapse an edge, merging surrounding data.
|
||||
*
|
||||
* Unlike #BM_vert_collapse_edge & #bmesh_kernel_join_edge_kill_vert
|
||||
* which only handle 2 valence verts,
|
||||
* this can handle any number of connected edges/faces.
|
||||
*
|
||||
* <pre>
|
||||
* Before: -> After:
|
||||
* +-+-+-+ +-+-+-+
|
||||
* | | | | | \ / |
|
||||
* +-+-+-+ +--+--+
|
||||
* | | | | | / \ |
|
||||
* +-+-+-+ +-+-+-+
|
||||
* </pre>
|
||||
*/
|
||||
BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm,
|
||||
BMEdge *e_kill,
|
||||
BMVert *v_kill,
|
||||
bool do_del,
|
||||
bool check_edge_exists,
|
||||
bool kill_degenerate_faces);
|
||||
/**
|
||||
* \brief Join Face Kill Edge (JFKE)
|
||||
*
|
||||
* Takes two faces joined by a single 2-manifold edge and fuses them together.
|
||||
* The edge shared by the faces must not be connected to any other edges which have
|
||||
* Both faces in its radial cycle
|
||||
*
|
||||
* \par Examples:
|
||||
* <pre>
|
||||
* A B
|
||||
* +--------+ +--------+
|
||||
* | | | |
|
||||
* | f1 | | f1 |
|
||||
* v1========v2 = Ok! v1==V2==v3 == Wrong!
|
||||
* | f2 | | f2 |
|
||||
* | | | |
|
||||
* +--------+ +--------+
|
||||
* </pre>
|
||||
*
|
||||
* In the example A, faces \a f1 and \a f2 are joined by a single edge,
|
||||
* and the euler can safely be used.
|
||||
* In example B however, \a f1 and \a f2 are joined by multiple edges and will produce an error.
|
||||
* The caller in this case should call #bmesh_kernel_join_edge_kill_vert on the extra edges
|
||||
* before attempting to fuse \a f1 and \a f2.
|
||||
*
|
||||
* \note The order of arguments decides whether or not certain per-face attributes are present
|
||||
* in the resultant face. For instance vertex winding, material index, smooth flags,
|
||||
* etc are inherited from \a f1, not \a f2.
|
||||
*
|
||||
* \return A BMFace pointer
|
||||
*/
|
||||
BMFace *bmesh_kernel_join_face_kill_edge(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e);
|
||||
|
||||
/**
|
||||
* \brief Un-glue Region Make Vert (URMV)
|
||||
*
|
||||
* Disconnects a face from its vertex fan at loop \a l_sep
|
||||
*
|
||||
* \return The newly created BMVert
|
||||
*
|
||||
* \note Will be a no-op and return original vertex if only two edges at that vertex.
|
||||
*/
|
||||
BMVert *bmesh_kernel_unglue_region_make_vert(BMesh *bm, BMLoop *l_sep);
|
||||
/**
|
||||
* A version of #bmesh_kernel_unglue_region_make_vert that disconnects multiple loops at once.
|
||||
* The loops must all share the same vertex, can be in any order
|
||||
* and are all moved to use a single new vertex - which is returned.
|
||||
*
|
||||
* This function handles the details of finding fans boundaries.
|
||||
*/
|
||||
BMVert *bmesh_kernel_unglue_region_make_vert_multi(BMesh *bm, BMLoop **larr, int larr_len);
|
||||
/**
|
||||
* This function assumes l_sep is a part of a larger fan which has already been
|
||||
* isolated by calling #bmesh_kernel_edge_separate to segregate it radially.
|
||||
*/
|
||||
BMVert *bmesh_kernel_unglue_region_make_vert_multi_isolated(BMesh *bm, BMLoop *l_sep);
|
||||
|
||||
} // namespace blender
|
||||
365
blender-5.2.0/source/blender/bmesh/intern/bmesh_delete.cc
Normal file
365
blender-5.2.0/source/blender/bmesh/intern/bmesh_delete.cc
Normal file
@@ -0,0 +1,365 @@
|
||||
/* SPDX-FileCopyrightText: 2007 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* BM remove functions.
|
||||
*/
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* BMO functions */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name BMesh Operator Delete Functions
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Called by operators to remove elements that they have marked for
|
||||
* removal.
|
||||
*/
|
||||
static void bmo_remove_tagged_faces(BMesh *bm, const short oflag)
|
||||
{
|
||||
BMFace *f, *f_next;
|
||||
BMIter iter;
|
||||
|
||||
BM_ITER_MESH_MUTABLE (f, f_next, &iter, bm, BM_FACES_OF_MESH) {
|
||||
if (BMO_face_flag_test(bm, f, oflag)) {
|
||||
BM_face_kill(bm, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bmo_remove_tagged_edges(BMesh *bm, const short oflag)
|
||||
{
|
||||
BMEdge *e, *e_next;
|
||||
BMIter iter;
|
||||
|
||||
BM_ITER_MESH_MUTABLE (e, e_next, &iter, bm, BM_EDGES_OF_MESH) {
|
||||
if (BMO_edge_flag_test(bm, e, oflag)) {
|
||||
BM_edge_kill(bm, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bmo_remove_tagged_verts(BMesh *bm, const short oflag)
|
||||
{
|
||||
BMVert *v, *v_next;
|
||||
BMIter iter;
|
||||
|
||||
BM_ITER_MESH_MUTABLE (v, v_next, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
if (BMO_vert_flag_test(bm, v, oflag)) {
|
||||
BM_vert_kill(bm, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bmo_remove_tagged_verts_loose(BMesh *bm, const short oflag)
|
||||
{
|
||||
BMVert *v, *v_next;
|
||||
BMIter iter;
|
||||
|
||||
BM_ITER_MESH_MUTABLE (v, v_next, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
if (BMO_vert_flag_test(bm, v, oflag) && (v->e == nullptr)) {
|
||||
BM_vert_kill(bm, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BMO_mesh_delete_oflag_tagged(BMesh *bm, const short oflag, const char htype)
|
||||
{
|
||||
if (htype & BM_FACE) {
|
||||
bmo_remove_tagged_faces(bm, oflag);
|
||||
}
|
||||
if (htype & BM_EDGE) {
|
||||
bmo_remove_tagged_edges(bm, oflag);
|
||||
}
|
||||
if (htype & BM_VERT) {
|
||||
bmo_remove_tagged_verts(bm, oflag);
|
||||
}
|
||||
}
|
||||
|
||||
void BMO_mesh_delete_oflag_context(BMesh *bm,
|
||||
const short oflag,
|
||||
const int type,
|
||||
FunctionRef<void()> prepare_fn)
|
||||
{
|
||||
BMEdge *e;
|
||||
|
||||
BMIter eiter;
|
||||
BMIter fiter;
|
||||
|
||||
switch (type) {
|
||||
case DEL_VERTS: {
|
||||
if (prepare_fn) {
|
||||
prepare_fn();
|
||||
}
|
||||
bmo_remove_tagged_verts(bm, oflag);
|
||||
break;
|
||||
}
|
||||
case DEL_EDGES: {
|
||||
/* flush down to vert */
|
||||
BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
|
||||
if (BMO_edge_flag_test(bm, e, oflag)) {
|
||||
BMO_vert_flag_enable(bm, e->v1, oflag);
|
||||
BMO_vert_flag_enable(bm, e->v2, oflag);
|
||||
}
|
||||
}
|
||||
if (prepare_fn) {
|
||||
prepare_fn();
|
||||
}
|
||||
bmo_remove_tagged_edges(bm, oflag);
|
||||
bmo_remove_tagged_verts_loose(bm, oflag);
|
||||
break;
|
||||
}
|
||||
case DEL_EDGESFACES: {
|
||||
if (prepare_fn) {
|
||||
prepare_fn();
|
||||
}
|
||||
bmo_remove_tagged_edges(bm, oflag);
|
||||
break;
|
||||
}
|
||||
case DEL_ONLYFACES: {
|
||||
if (prepare_fn) {
|
||||
prepare_fn();
|
||||
}
|
||||
bmo_remove_tagged_faces(bm, oflag);
|
||||
break;
|
||||
}
|
||||
case DEL_ONLYTAGGED: {
|
||||
if (prepare_fn) {
|
||||
prepare_fn();
|
||||
}
|
||||
BMO_mesh_delete_oflag_tagged(bm, oflag, BM_ALL_NOLOOP);
|
||||
break;
|
||||
}
|
||||
case DEL_FACES:
|
||||
case DEL_FACES_KEEP_BOUNDARY: {
|
||||
/* go through and mark all edges and all verts of all faces for delete */
|
||||
BMFace *f;
|
||||
BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
|
||||
if (BMO_face_flag_test(bm, f, oflag)) {
|
||||
BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
|
||||
BMLoop *l_iter;
|
||||
|
||||
l_iter = l_first;
|
||||
do {
|
||||
BMO_vert_flag_enable(bm, l_iter->v, oflag);
|
||||
BMO_edge_flag_enable(bm, l_iter->e, oflag);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
}
|
||||
/* now go through and mark all remaining faces all edges for keeping */
|
||||
BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
|
||||
if (!BMO_face_flag_test(bm, f, oflag)) {
|
||||
BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
|
||||
BMLoop *l_iter;
|
||||
|
||||
l_iter = l_first;
|
||||
do {
|
||||
BMO_vert_flag_disable(bm, l_iter->v, oflag);
|
||||
BMO_edge_flag_disable(bm, l_iter->e, oflag);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
}
|
||||
/* also mark all the vertices of remaining edges for keeping */
|
||||
BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
|
||||
|
||||
/* Only exception to normal 'DEL_FACES' logic. */
|
||||
if (type == DEL_FACES_KEEP_BOUNDARY) {
|
||||
if (BM_edge_is_boundary(e)) {
|
||||
BMO_edge_flag_disable(bm, e, oflag);
|
||||
}
|
||||
}
|
||||
|
||||
if (!BMO_edge_flag_test(bm, e, oflag)) {
|
||||
BMO_vert_flag_disable(bm, e->v1, oflag);
|
||||
BMO_vert_flag_disable(bm, e->v2, oflag);
|
||||
}
|
||||
}
|
||||
if (prepare_fn) {
|
||||
prepare_fn();
|
||||
}
|
||||
|
||||
/* now delete marked face */
|
||||
bmo_remove_tagged_faces(bm, oflag);
|
||||
/* delete marked edge */
|
||||
bmo_remove_tagged_edges(bm, oflag);
|
||||
/* remove loose vertices */
|
||||
bmo_remove_tagged_verts(bm, oflag);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* BM functions
|
||||
*
|
||||
* NOTE: this is just a duplicate of the code above (bad!)
|
||||
* but for now keep in sync, its less hassle than having to create bmesh operator flags,
|
||||
* each time we need to remove some geometry.
|
||||
*/
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name BMesh Delete Functions (no oflags)
|
||||
* \{ */
|
||||
|
||||
static void bm_remove_tagged_faces(BMesh *bm, const char hflag)
|
||||
{
|
||||
BMFace *f, *f_next;
|
||||
BMIter iter;
|
||||
|
||||
BM_ITER_MESH_MUTABLE (f, f_next, &iter, bm, BM_FACES_OF_MESH) {
|
||||
if (BM_elem_flag_test(f, hflag)) {
|
||||
BM_face_kill(bm, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bm_remove_tagged_edges(BMesh *bm, const char hflag)
|
||||
{
|
||||
BMEdge *e, *e_next;
|
||||
BMIter iter;
|
||||
|
||||
BM_ITER_MESH_MUTABLE (e, e_next, &iter, bm, BM_EDGES_OF_MESH) {
|
||||
if (BM_elem_flag_test(e, hflag)) {
|
||||
BM_edge_kill(bm, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bm_remove_tagged_verts(BMesh *bm, const char hflag)
|
||||
{
|
||||
BMVert *v, *v_next;
|
||||
BMIter iter;
|
||||
|
||||
BM_ITER_MESH_MUTABLE (v, v_next, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
if (BM_elem_flag_test(v, hflag)) {
|
||||
BM_vert_kill(bm, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bm_remove_tagged_verts_loose(BMesh *bm, const char hflag)
|
||||
{
|
||||
BMVert *v, *v_next;
|
||||
BMIter iter;
|
||||
|
||||
BM_ITER_MESH_MUTABLE (v, v_next, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
if (BM_elem_flag_test(v, hflag) && (v->e == nullptr)) {
|
||||
BM_vert_kill(bm, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BM_mesh_delete_hflag_tagged(BMesh *bm, const char hflag, const char htype)
|
||||
{
|
||||
if (htype & BM_FACE) {
|
||||
bm_remove_tagged_faces(bm, hflag);
|
||||
}
|
||||
if (htype & BM_EDGE) {
|
||||
bm_remove_tagged_edges(bm, hflag);
|
||||
}
|
||||
if (htype & BM_VERT) {
|
||||
bm_remove_tagged_verts(bm, hflag);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_mesh_delete_hflag_context(BMesh *bm, const char hflag, const int type)
|
||||
{
|
||||
|
||||
BMIter eiter;
|
||||
BMIter fiter;
|
||||
|
||||
switch (type) {
|
||||
case DEL_VERTS: {
|
||||
bm_remove_tagged_verts(bm, hflag);
|
||||
|
||||
break;
|
||||
}
|
||||
case DEL_EDGES: {
|
||||
/* flush down to vert */
|
||||
BMEdge *e;
|
||||
BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
|
||||
if (BM_elem_flag_test(e, hflag)) {
|
||||
BM_elem_flag_enable(e->v1, hflag);
|
||||
BM_elem_flag_enable(e->v2, hflag);
|
||||
}
|
||||
}
|
||||
bm_remove_tagged_edges(bm, hflag);
|
||||
bm_remove_tagged_verts_loose(bm, hflag);
|
||||
|
||||
break;
|
||||
}
|
||||
case DEL_EDGESFACES: {
|
||||
bm_remove_tagged_edges(bm, hflag);
|
||||
|
||||
break;
|
||||
}
|
||||
case DEL_ONLYFACES: {
|
||||
bm_remove_tagged_faces(bm, hflag);
|
||||
|
||||
break;
|
||||
}
|
||||
case DEL_ONLYTAGGED: {
|
||||
BM_mesh_delete_hflag_tagged(bm, hflag, BM_ALL_NOLOOP);
|
||||
|
||||
break;
|
||||
}
|
||||
case DEL_FACES: {
|
||||
/* go through and mark all edges and all verts of all faces for delete */
|
||||
BMFace *f;
|
||||
BMEdge *e;
|
||||
BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
|
||||
if (BM_elem_flag_test(f, hflag)) {
|
||||
BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
|
||||
BMLoop *l_iter;
|
||||
|
||||
l_iter = l_first;
|
||||
do {
|
||||
BM_elem_flag_enable(l_iter->v, hflag);
|
||||
BM_elem_flag_enable(l_iter->e, hflag);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
}
|
||||
/* now go through and mark all remaining faces all edges for keeping */
|
||||
BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
|
||||
if (!BM_elem_flag_test(f, hflag)) {
|
||||
BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
|
||||
BMLoop *l_iter;
|
||||
|
||||
l_iter = l_first;
|
||||
do {
|
||||
BM_elem_flag_disable(l_iter->v, hflag);
|
||||
BM_elem_flag_disable(l_iter->e, hflag);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
}
|
||||
/* also mark all the vertices of remaining edges for keeping */
|
||||
BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
|
||||
if (!BM_elem_flag_test(e, hflag)) {
|
||||
BM_elem_flag_disable(e->v1, hflag);
|
||||
BM_elem_flag_disable(e->v2, hflag);
|
||||
}
|
||||
}
|
||||
/* now delete marked face */
|
||||
bm_remove_tagged_faces(bm, hflag);
|
||||
/* delete marked edge */
|
||||
bm_remove_tagged_edges(bm, hflag);
|
||||
/* remove loose vertices */
|
||||
bm_remove_tagged_verts(bm, hflag);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
39
blender-5.2.0/source/blender/bmesh/intern/bmesh_delete.hh
Normal file
39
blender-5.2.0/source/blender/bmesh/intern/bmesh_delete.hh
Normal file
@@ -0,0 +1,39 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
#include "BLI_function_ref.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
void BMO_mesh_delete_oflag_tagged(BMesh *bm, short oflag, char htype);
|
||||
void BM_mesh_delete_hflag_tagged(BMesh *bm, char hflag, char htype);
|
||||
|
||||
/**
|
||||
* \param oflag: Geometry tagged with this operator flag is deleted.
|
||||
* This flag applies to different types in some contexts, not just the type being removed.
|
||||
*
|
||||
* \param prepare_fn: Optional callback that runs before deleting geometry,
|
||||
* use this to execute any logic that needs to ensure references to deleted geometry
|
||||
* aren't held by the caller.
|
||||
*/
|
||||
void BMO_mesh_delete_oflag_context(BMesh *bm,
|
||||
short oflag,
|
||||
int type,
|
||||
FunctionRef<void()> prepare_fn);
|
||||
|
||||
/**
|
||||
* \param hflag: Geometry tagged with this operator flag is deleted.
|
||||
* This flag applies to different types in some contexts, not just the type being removed.
|
||||
*/
|
||||
void BM_mesh_delete_hflag_context(BMesh *bm, char hflag, int type);
|
||||
|
||||
} // namespace blender
|
||||
811
blender-5.2.0/source/blender/bmesh/intern/bmesh_edgeloop.cc
Normal file
811
blender-5.2.0/source/blender/bmesh/intern/bmesh_edgeloop.cc
Normal file
@@ -0,0 +1,811 @@
|
||||
/* SPDX-FileCopyrightText: 2013 by Campbell Barton. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* Generic utility functions for getting edge loops from a mesh.
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_mempool.h"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_stack.h"
|
||||
#include "BLI_utildefines_iter.h"
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
#include "bmesh_edgeloop.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMEdgeLoopStore {
|
||||
BMEdgeLoopStore *next, *prev;
|
||||
ListBaseT<LinkData> verts;
|
||||
int flag;
|
||||
int len;
|
||||
/* Optional values to calculate. */
|
||||
float co[3], no[3];
|
||||
};
|
||||
|
||||
#define BM_EDGELOOP_IS_CLOSED (1 << 0)
|
||||
|
||||
/* Use a small value since we need normals even for very small loops. */
|
||||
#define EDGELOOP_EPS 1e-10f
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* BM_mesh_edgeloops_find & Utility Functions. */
|
||||
|
||||
static int bm_vert_other_tag(BMVert *v, BMVert *v_prev, BMEdge **r_e)
|
||||
{
|
||||
BMIter iter;
|
||||
BMEdge *e, *e_next = nullptr;
|
||||
uint count = 0;
|
||||
|
||||
BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
|
||||
if (BM_elem_flag_test(e, BM_ELEM_INTERNAL_TAG)) {
|
||||
BMVert *v_other = BM_edge_other_vert(e, v);
|
||||
if (v_other != v_prev) {
|
||||
e_next = e;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*r_e = e_next;
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* \return success
|
||||
*/
|
||||
static bool bm_loop_build(BMEdgeLoopStore *el_store, BMVert *v_prev, BMVert *v, int dir)
|
||||
{
|
||||
void (*add_fn)(ListBase *, void *) = dir == 1 ? BLI_addhead : BLI_addtail;
|
||||
BMEdge *e_next;
|
||||
BMVert *v_next;
|
||||
BMVert *v_first = v;
|
||||
|
||||
BLI_assert(abs(dir) == 1);
|
||||
|
||||
if (!BM_elem_flag_test(v, BM_ELEM_INTERNAL_TAG)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
while (v) {
|
||||
LinkData *node = MEM_new_zeroed<LinkData>(__func__);
|
||||
int count;
|
||||
node->data = v;
|
||||
add_fn(&el_store->verts, node);
|
||||
el_store->len++;
|
||||
BM_elem_flag_disable(v, BM_ELEM_INTERNAL_TAG);
|
||||
|
||||
count = bm_vert_other_tag(v, v_prev, &e_next);
|
||||
if (count == 1) {
|
||||
v_next = BM_edge_other_vert(e_next, v);
|
||||
BM_elem_flag_disable(e_next, BM_ELEM_INTERNAL_TAG);
|
||||
if (UNLIKELY(v_next == v_first)) {
|
||||
el_store->flag |= BM_EDGELOOP_IS_CLOSED;
|
||||
v_next = nullptr;
|
||||
}
|
||||
}
|
||||
else if (count == 0) {
|
||||
/* pass */
|
||||
v_next = nullptr;
|
||||
}
|
||||
else {
|
||||
v_next = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
v_prev = v;
|
||||
v = v_next;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int BM_mesh_edgeloops_find(BMesh *bm,
|
||||
ListBaseT<BMEdgeLoopStore> *r_eloops,
|
||||
bool (*test_fn)(BMEdge *, void *user_data),
|
||||
void *user_data)
|
||||
{
|
||||
BMIter iter;
|
||||
BMEdge *e;
|
||||
BMVert *v;
|
||||
int count = 0;
|
||||
|
||||
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
BM_elem_flag_disable(v, BM_ELEM_INTERNAL_TAG);
|
||||
}
|
||||
|
||||
/* first flush edges to tags, and tag verts */
|
||||
BLI_Stack *edge_stack = BLI_stack_new(sizeof(BMEdge *), __func__);
|
||||
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
|
||||
BLI_assert(!BM_elem_flag_test(e, BM_ELEM_INTERNAL_TAG));
|
||||
if (test_fn(e, user_data)) {
|
||||
BM_elem_flag_enable(e, BM_ELEM_INTERNAL_TAG);
|
||||
BM_elem_flag_enable(e->v1, BM_ELEM_INTERNAL_TAG);
|
||||
BM_elem_flag_enable(e->v2, BM_ELEM_INTERNAL_TAG);
|
||||
BLI_stack_push(edge_stack, static_cast<void *>(&e));
|
||||
}
|
||||
else {
|
||||
BM_elem_flag_disable(e, BM_ELEM_INTERNAL_TAG);
|
||||
}
|
||||
}
|
||||
|
||||
const uint edges_len = BLI_stack_count(edge_stack);
|
||||
BMEdge **edges = MEM_new_array_uninitialized<BMEdge *>(edges_len, __func__);
|
||||
BLI_stack_pop_n_reverse(edge_stack, edges, BLI_stack_count(edge_stack));
|
||||
BLI_stack_free(edge_stack);
|
||||
|
||||
for (uint i = 0; i < edges_len; i += 1) {
|
||||
e = edges[i];
|
||||
if (BM_elem_flag_test(e, BM_ELEM_INTERNAL_TAG)) {
|
||||
BMEdgeLoopStore *el_store = MEM_new_zeroed<BMEdgeLoopStore>(__func__);
|
||||
|
||||
/* add both directions */
|
||||
if (bm_loop_build(el_store, e->v1, e->v2, 1) && bm_loop_build(el_store, e->v2, e->v1, -1) &&
|
||||
el_store->len > 1)
|
||||
{
|
||||
BLI_addtail(r_eloops, el_store);
|
||||
count++;
|
||||
}
|
||||
else {
|
||||
BM_edgeloop_free(el_store);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint i = 0; i < edges_len; i += 1) {
|
||||
e = edges[i];
|
||||
BM_elem_flag_disable(e, BM_ELEM_INTERNAL_TAG);
|
||||
BM_elem_flag_disable(e->v1, BM_ELEM_INTERNAL_TAG);
|
||||
BM_elem_flag_disable(e->v2, BM_ELEM_INTERNAL_TAG);
|
||||
}
|
||||
|
||||
MEM_delete(edges);
|
||||
return count;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* BM_mesh_edgeloops_find_path & Util Functions. */
|
||||
|
||||
/**
|
||||
* Find s single, open edge loop - given 2 vertices.
|
||||
* Add to
|
||||
*/
|
||||
struct VertStep {
|
||||
VertStep *next, *prev;
|
||||
BMVert *v;
|
||||
};
|
||||
|
||||
static void vs_add(
|
||||
BLI_mempool *vs_pool, ListBaseT<VertStep> *lb, BMVert *v, BMEdge *e_prev, const int iter_tot)
|
||||
{
|
||||
VertStep *vs_new = static_cast<VertStep *>(BLI_mempool_alloc(vs_pool));
|
||||
vs_new->v = v;
|
||||
|
||||
BM_elem_index_set(v, iter_tot); /* set_dirty */
|
||||
|
||||
/* This edge stores a direct path back to the original vertex so we can
|
||||
* backtrack without having to store an array of previous verts. */
|
||||
|
||||
/* WARNING: Setting the edge is not common practice but currently harmless, take care. */
|
||||
BLI_assert(BM_vert_in_edge(e_prev, v));
|
||||
v->e = e_prev;
|
||||
|
||||
BLI_addtail(lb, vs_new);
|
||||
}
|
||||
|
||||
static bool bm_loop_path_build_step(BLI_mempool *vs_pool,
|
||||
ListBaseT<VertStep> *lb,
|
||||
const int dir,
|
||||
BMVert *v_match[2])
|
||||
{
|
||||
ListBaseT<VertStep> lb_tmp = {nullptr, nullptr};
|
||||
VertStep *vs, *vs_next;
|
||||
BLI_assert(abs(dir) == 1);
|
||||
|
||||
for (vs = static_cast<VertStep *>(lb->first); vs; vs = vs_next) {
|
||||
BMIter iter;
|
||||
BMEdge *e;
|
||||
/* these values will be the same every iteration */
|
||||
const int vs_iter_tot = BM_elem_index_get(vs->v);
|
||||
const int vs_iter_next = vs_iter_tot + dir;
|
||||
|
||||
vs_next = vs->next;
|
||||
|
||||
BM_ITER_ELEM (e, &iter, vs->v, BM_EDGES_OF_VERT) {
|
||||
if (BM_elem_flag_test(e, BM_ELEM_INTERNAL_TAG)) {
|
||||
BMVert *v_next = BM_edge_other_vert(e, vs->v);
|
||||
const int v_next_index = BM_elem_index_get(v_next);
|
||||
/* not essential to clear flag but prevents more checking next time round */
|
||||
BM_elem_flag_disable(e, BM_ELEM_INTERNAL_TAG);
|
||||
if (v_next_index == 0) {
|
||||
vs_add(vs_pool, &lb_tmp, v_next, e, vs_iter_next);
|
||||
}
|
||||
else if ((dir < 0) == (v_next_index < 0)) {
|
||||
/* on the same side - do nothing */
|
||||
}
|
||||
else {
|
||||
/* we have met out match! (vertices from different sides meet) */
|
||||
if (dir == 1) {
|
||||
v_match[0] = vs->v;
|
||||
v_match[1] = v_next;
|
||||
}
|
||||
else {
|
||||
v_match[0] = v_next;
|
||||
v_match[1] = vs->v;
|
||||
}
|
||||
/* normally we would manage memory of remaining items in (lb, lb_tmp),
|
||||
* but search is done, vs_pool will get destroyed immediately */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BLI_mempool_free(vs_pool, vs);
|
||||
}
|
||||
|
||||
/* Commented because used in a loop, and this flag has already been set. */
|
||||
// bm->elem_index_dirty |= BM_VERT;
|
||||
|
||||
/* `lb` is now full of freed items, overwrite. */
|
||||
*lb = lb_tmp;
|
||||
|
||||
return (lb->is_empty() == false);
|
||||
}
|
||||
|
||||
bool BM_mesh_edgeloops_find_path(BMesh *bm,
|
||||
ListBaseT<BMEdgeLoopStore> *r_eloops,
|
||||
bool (*test_fn)(BMEdge *, void *user_data),
|
||||
void *user_data,
|
||||
BMVert *v_src,
|
||||
BMVert *v_dst)
|
||||
{
|
||||
BMIter iter;
|
||||
BMEdge *e;
|
||||
bool found = false;
|
||||
|
||||
BLI_assert(v_src != v_dst);
|
||||
|
||||
{
|
||||
BMVert *v;
|
||||
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
BM_elem_index_set(v, 0);
|
||||
BM_elem_flag_disable(v, BM_ELEM_INTERNAL_TAG);
|
||||
}
|
||||
}
|
||||
bm->elem_index_dirty |= BM_VERT;
|
||||
|
||||
/* first flush edges to tags, and tag verts */
|
||||
int edges_len;
|
||||
BMEdge **edges;
|
||||
|
||||
if (test_fn) {
|
||||
BLI_Stack *edge_stack = BLI_stack_new(sizeof(BMEdge *), __func__);
|
||||
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
|
||||
if (test_fn(e, user_data)) {
|
||||
BM_elem_flag_enable(e, BM_ELEM_INTERNAL_TAG);
|
||||
BM_elem_flag_enable(e->v1, BM_ELEM_INTERNAL_TAG);
|
||||
BM_elem_flag_enable(e->v2, BM_ELEM_INTERNAL_TAG);
|
||||
BLI_stack_push(edge_stack, static_cast<void *>(&e));
|
||||
}
|
||||
else {
|
||||
BM_elem_flag_disable(e, BM_ELEM_INTERNAL_TAG);
|
||||
}
|
||||
}
|
||||
edges_len = BLI_stack_count(edge_stack);
|
||||
edges = MEM_new_array_uninitialized<BMEdge *>(edges_len, __func__);
|
||||
BLI_stack_pop_n_reverse(edge_stack, edges, BLI_stack_count(edge_stack));
|
||||
BLI_stack_free(edge_stack);
|
||||
}
|
||||
else {
|
||||
int i = 0;
|
||||
edges_len = bm->totedge;
|
||||
edges = MEM_new_array_uninitialized<BMEdge *>(edges_len, __func__);
|
||||
|
||||
BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
|
||||
BM_elem_flag_enable(e, BM_ELEM_INTERNAL_TAG);
|
||||
BM_elem_flag_enable(e->v1, BM_ELEM_INTERNAL_TAG);
|
||||
BM_elem_flag_enable(e->v2, BM_ELEM_INTERNAL_TAG);
|
||||
edges[i] = e;
|
||||
}
|
||||
}
|
||||
|
||||
/* prime the lists and begin search */
|
||||
{
|
||||
BMVert *v_match[2] = {nullptr, nullptr};
|
||||
ListBaseT<VertStep> lb_src = {nullptr, nullptr};
|
||||
ListBaseT<VertStep> lb_dst = {nullptr, nullptr};
|
||||
BLI_mempool *vs_pool = BLI_mempool_create(sizeof(VertStep), 0, 512, BLI_MEMPOOL_NOP);
|
||||
|
||||
/* edge args are dummy */
|
||||
vs_add(vs_pool, &lb_src, v_src, v_src->e, 1);
|
||||
vs_add(vs_pool, &lb_dst, v_dst, v_dst->e, -1);
|
||||
bm->elem_index_dirty |= BM_VERT;
|
||||
|
||||
do {
|
||||
if ((bm_loop_path_build_step(vs_pool, &lb_src, 1, v_match) == false) || v_match[0]) {
|
||||
break;
|
||||
}
|
||||
if ((bm_loop_path_build_step(vs_pool, &lb_dst, -1, v_match) == false) || v_match[0]) {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
BLI_mempool_destroy(vs_pool);
|
||||
|
||||
if (v_match[0]) {
|
||||
BMEdgeLoopStore *el_store = MEM_new_zeroed<BMEdgeLoopStore>(__func__);
|
||||
BMVert *v;
|
||||
|
||||
/* build loop from edge pointers */
|
||||
v = v_match[0];
|
||||
while (true) {
|
||||
LinkData *node = MEM_new_zeroed<LinkData>(__func__);
|
||||
node->data = v;
|
||||
BLI_addhead(&el_store->verts, node);
|
||||
el_store->len++;
|
||||
if (v == v_src) {
|
||||
break;
|
||||
}
|
||||
v = BM_edge_other_vert(v->e, v);
|
||||
}
|
||||
|
||||
v = v_match[1];
|
||||
while (true) {
|
||||
LinkData *node = MEM_new_zeroed<LinkData>(__func__);
|
||||
node->data = v;
|
||||
BLI_addtail(&el_store->verts, node);
|
||||
el_store->len++;
|
||||
if (v == v_dst) {
|
||||
break;
|
||||
}
|
||||
v = BM_edge_other_vert(v->e, v);
|
||||
}
|
||||
|
||||
BLI_addtail(r_eloops, el_store);
|
||||
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint i = 0; i < edges_len; i += 1) {
|
||||
e = edges[i];
|
||||
BM_elem_flag_disable(e, BM_ELEM_INTERNAL_TAG);
|
||||
BM_elem_flag_disable(e->v1, BM_ELEM_INTERNAL_TAG);
|
||||
BM_elem_flag_disable(e->v2, BM_ELEM_INTERNAL_TAG);
|
||||
}
|
||||
MEM_delete(edges);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* BM_mesh_edgeloops_xxx utility function */
|
||||
|
||||
void BM_mesh_edgeloops_free(ListBaseT<BMEdgeLoopStore> *eloops)
|
||||
{
|
||||
while (BMEdgeLoopStore *el_store = static_cast<BMEdgeLoopStore *>(BLI_pophead(eloops))) {
|
||||
BM_edgeloop_free(el_store);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_mesh_edgeloops_calc_center(BMesh *bm, ListBaseT<BMEdgeLoopStore> *eloops)
|
||||
{
|
||||
for (BMEdgeLoopStore &el_store : *eloops) {
|
||||
BM_edgeloop_calc_center(bm, &el_store);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_mesh_edgeloops_calc_normal(BMesh *bm, ListBaseT<BMEdgeLoopStore> *eloops)
|
||||
{
|
||||
for (BMEdgeLoopStore &el_store : *eloops) {
|
||||
BM_edgeloop_calc_normal(bm, &el_store);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_mesh_edgeloops_calc_normal_aligned(BMesh *bm,
|
||||
ListBaseT<BMEdgeLoopStore> *eloops,
|
||||
const float no_align[3])
|
||||
{
|
||||
for (BMEdgeLoopStore &el_store : *eloops) {
|
||||
BM_edgeloop_calc_normal_aligned(bm, &el_store, no_align);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_mesh_edgeloops_calc_order(BMesh * /*bm*/,
|
||||
ListBaseT<BMEdgeLoopStore> *eloops,
|
||||
const bool use_normals)
|
||||
{
|
||||
ListBaseT<BMEdgeLoopStore> eloops_ordered = {nullptr};
|
||||
float cent[3];
|
||||
int tot = 0;
|
||||
zero_v3(cent);
|
||||
/* assumes we calculated centers already */
|
||||
for (BMEdgeLoopStore &el_store : *eloops) {
|
||||
if (!is_finite_v3(el_store.co)) [[unlikely]] {
|
||||
continue;
|
||||
}
|
||||
add_v3_v3(cent, el_store.co);
|
||||
tot += 1;
|
||||
}
|
||||
if (tot > 0) {
|
||||
mul_v3_fl(cent, 1.0f / float(tot));
|
||||
if (!is_finite_v3(cent)) {
|
||||
zero_v3(cent);
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the furthest out loop. */
|
||||
{
|
||||
BMEdgeLoopStore *el_store_best = nullptr;
|
||||
float len_best_sq = -1.0f;
|
||||
for (BMEdgeLoopStore &el_store : *eloops) {
|
||||
const float len_sq = len_squared_v3v3(cent, el_store.co);
|
||||
/* Null check to account for non-finite distances. */
|
||||
if ((len_sq > len_best_sq) || (el_store_best == nullptr)) {
|
||||
len_best_sq = len_sq;
|
||||
el_store_best = &el_store;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_remlink(eloops, el_store_best);
|
||||
BLI_addtail(&eloops_ordered, el_store_best);
|
||||
}
|
||||
|
||||
/* not so efficient re-ordering */
|
||||
while (eloops->first) {
|
||||
BMEdgeLoopStore *el_store_best = nullptr;
|
||||
const float *co = (static_cast<BMEdgeLoopStore *>(eloops_ordered.last))->co;
|
||||
const float *no = (static_cast<BMEdgeLoopStore *>(eloops_ordered.last))->no;
|
||||
float len_best_sq = FLT_MAX;
|
||||
|
||||
if (use_normals) {
|
||||
BLI_ASSERT_UNIT_V3(no);
|
||||
}
|
||||
|
||||
for (BMEdgeLoopStore &el_store : *eloops) {
|
||||
float len_sq;
|
||||
if (use_normals) {
|
||||
/* Scale the length by how close the loops are to pointing at each other. */
|
||||
float dir[3];
|
||||
sub_v3_v3v3(dir, co, el_store.co);
|
||||
len_sq = normalize_v3(dir);
|
||||
len_sq = len_sq *
|
||||
((1.0f - fabsf(dot_v3v3(dir, no))) + (1.0f - fabsf(dot_v3v3(dir, el_store.no))));
|
||||
}
|
||||
else {
|
||||
len_sq = len_squared_v3v3(co, el_store.co);
|
||||
}
|
||||
|
||||
/* Null check to account for non-finite distances. */
|
||||
if ((len_sq < len_best_sq) || (el_store_best == nullptr)) {
|
||||
len_best_sq = len_sq;
|
||||
el_store_best = &el_store;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_remlink(eloops, el_store_best);
|
||||
BLI_addtail(&eloops_ordered, el_store_best);
|
||||
}
|
||||
|
||||
*eloops = eloops_ordered;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* BM_edgeloop_*** functions */
|
||||
|
||||
BMEdgeLoopStore *BM_edgeloop_copy(BMEdgeLoopStore *el_store)
|
||||
{
|
||||
BMEdgeLoopStore *el_store_copy = MEM_new_uninitialized<BMEdgeLoopStore>(__func__);
|
||||
*el_store_copy = *el_store;
|
||||
BLI_duplicatelist(&el_store_copy->verts, &el_store->verts);
|
||||
return el_store_copy;
|
||||
}
|
||||
|
||||
BMEdgeLoopStore *BM_edgeloop_from_verts(BMVert **v_arr, const int v_arr_tot, bool is_closed)
|
||||
{
|
||||
BMEdgeLoopStore *el_store = MEM_new_zeroed<BMEdgeLoopStore>(__func__);
|
||||
int i;
|
||||
for (i = 0; i < v_arr_tot; i++) {
|
||||
LinkData *node = MEM_new_zeroed<LinkData>(__func__);
|
||||
node->data = v_arr[i];
|
||||
BLI_addtail(&el_store->verts, node);
|
||||
}
|
||||
el_store->len = v_arr_tot;
|
||||
if (is_closed) {
|
||||
el_store->flag |= BM_EDGELOOP_IS_CLOSED;
|
||||
}
|
||||
return el_store;
|
||||
}
|
||||
|
||||
void BM_edgeloop_free(BMEdgeLoopStore *el_store)
|
||||
{
|
||||
el_store->verts.free_no_destruct();
|
||||
MEM_delete(el_store);
|
||||
}
|
||||
|
||||
bool BM_edgeloop_is_closed(BMEdgeLoopStore *el_store)
|
||||
{
|
||||
return (el_store->flag & BM_EDGELOOP_IS_CLOSED) != 0;
|
||||
}
|
||||
|
||||
ListBaseT<LinkData> *BM_edgeloop_verts_get(BMEdgeLoopStore *el_store)
|
||||
{
|
||||
return &el_store->verts;
|
||||
}
|
||||
|
||||
int BM_edgeloop_length_get(BMEdgeLoopStore *el_store)
|
||||
{
|
||||
return el_store->len;
|
||||
}
|
||||
|
||||
const float *BM_edgeloop_normal_get(BMEdgeLoopStore *el_store)
|
||||
{
|
||||
return el_store->no;
|
||||
}
|
||||
|
||||
const float *BM_edgeloop_center_get(BMEdgeLoopStore *el_store)
|
||||
{
|
||||
return el_store->co;
|
||||
}
|
||||
|
||||
#define NODE_AS_V(n) ((BMVert *)((LinkData *)n)->data)
|
||||
#define NODE_AS_CO(n) ((BMVert *)((LinkData *)n)->data)->co
|
||||
|
||||
void BM_edgeloop_edges_get(BMEdgeLoopStore *el_store, BMEdge **e_arr)
|
||||
{
|
||||
LinkData *node;
|
||||
int i = 0;
|
||||
for (node = static_cast<LinkData *>(el_store->verts.first); node && node->next;
|
||||
node = node->next)
|
||||
{
|
||||
e_arr[i++] = BM_edge_exists(NODE_AS_V(node), NODE_AS_V(node->next));
|
||||
BLI_assert(e_arr[i - 1] != nullptr);
|
||||
}
|
||||
|
||||
if (el_store->flag & BM_EDGELOOP_IS_CLOSED) {
|
||||
e_arr[i] = BM_edge_exists(NODE_AS_V(el_store->verts.first), NODE_AS_V(el_store->verts.last));
|
||||
BLI_assert(e_arr[i] != nullptr);
|
||||
}
|
||||
BLI_assert(el_store->len == i + 1);
|
||||
}
|
||||
|
||||
void BM_edgeloop_calc_center(BMesh * /*bm*/, BMEdgeLoopStore *el_store)
|
||||
{
|
||||
LinkData *node_curr = static_cast<LinkData *>(el_store->verts.last);
|
||||
LinkData *node_prev = (static_cast<LinkData *>(el_store->verts.last))->prev;
|
||||
LinkData *node_first = static_cast<LinkData *>(el_store->verts.first);
|
||||
LinkData *node_next = node_first;
|
||||
|
||||
const float *v_prev = NODE_AS_CO(node_prev);
|
||||
const float *v_curr = NODE_AS_CO(node_curr);
|
||||
const float *v_next = NODE_AS_CO(node_next);
|
||||
|
||||
float totw = 0.0f;
|
||||
float w_prev;
|
||||
|
||||
zero_v3(el_store->co);
|
||||
|
||||
w_prev = len_v3v3(v_prev, v_curr);
|
||||
do {
|
||||
const float w_curr = len_v3v3(v_curr, v_next);
|
||||
const float w = (w_curr + w_prev);
|
||||
madd_v3_v3fl(el_store->co, v_curr, w);
|
||||
totw += w;
|
||||
w_prev = w_curr;
|
||||
|
||||
node_prev = node_curr;
|
||||
node_curr = node_next;
|
||||
node_next = node_next->next;
|
||||
|
||||
if (node_next == nullptr) {
|
||||
break;
|
||||
}
|
||||
v_prev = v_curr;
|
||||
v_curr = v_next;
|
||||
v_next = NODE_AS_CO(node_next);
|
||||
} while (true);
|
||||
|
||||
if (totw != 0.0f) {
|
||||
mul_v3_fl(el_store->co, 1.0f / totw);
|
||||
}
|
||||
}
|
||||
|
||||
bool BM_edgeloop_calc_normal(BMesh * /*bm*/, BMEdgeLoopStore *el_store)
|
||||
{
|
||||
LinkData *node_curr = static_cast<LinkData *>(el_store->verts.first);
|
||||
const float *v_prev = NODE_AS_CO(el_store->verts.last);
|
||||
const float *v_curr = NODE_AS_CO(node_curr);
|
||||
|
||||
zero_v3(el_store->no);
|
||||
|
||||
/* Newell's Method */
|
||||
do {
|
||||
add_newell_cross_v3_v3v3(el_store->no, v_prev, v_curr);
|
||||
|
||||
if ((node_curr = node_curr->next)) {
|
||||
v_prev = v_curr;
|
||||
v_curr = NODE_AS_CO(node_curr);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
if (UNLIKELY(normalize_v3(el_store->no) < EDGELOOP_EPS)) {
|
||||
el_store->no[2] = 1.0f; /* other axis set to 0.0 */
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BM_edgeloop_calc_normal_aligned(BMesh * /*bm*/,
|
||||
BMEdgeLoopStore *el_store,
|
||||
const float no_align[3])
|
||||
{
|
||||
LinkData *node_curr = static_cast<LinkData *>(el_store->verts.first);
|
||||
const float *v_prev = NODE_AS_CO(el_store->verts.last);
|
||||
const float *v_curr = NODE_AS_CO(node_curr);
|
||||
|
||||
zero_v3(el_store->no);
|
||||
|
||||
/* Own Method */
|
||||
do {
|
||||
float cross[3], no[3], dir[3];
|
||||
sub_v3_v3v3(dir, v_curr, v_prev);
|
||||
cross_v3_v3v3(cross, no_align, dir);
|
||||
cross_v3_v3v3(no, dir, cross);
|
||||
add_v3_v3(el_store->no, no);
|
||||
|
||||
if ((node_curr = node_curr->next)) {
|
||||
v_prev = v_curr;
|
||||
v_curr = NODE_AS_CO(node_curr);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
if (UNLIKELY(normalize_v3(el_store->no) < EDGELOOP_EPS)) {
|
||||
el_store->no[2] = 1.0f; /* other axis set to 0.0 */
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BM_edgeloop_flip(BMesh * /*bm*/, BMEdgeLoopStore *el_store)
|
||||
{
|
||||
negate_v3(el_store->no);
|
||||
BLI_listbase_reverse(&el_store->verts);
|
||||
}
|
||||
|
||||
void BM_edgeloop_expand(
|
||||
BMesh *bm, BMEdgeLoopStore *el_store, int el_store_len, bool split, Set<BMEdge *> *split_edges)
|
||||
{
|
||||
bool split_swap = true;
|
||||
|
||||
#define EDGE_SPLIT(node_copy, node_other) \
|
||||
{ \
|
||||
BMVert *v_split, *v_other = static_cast<BMVert *>((node_other)->data); \
|
||||
BMEdge *e_split, \
|
||||
*e_other = BM_edge_exists(static_cast<BMVert *>((node_copy)->data), v_other); \
|
||||
v_split = BM_edge_split(bm, \
|
||||
e_other, \
|
||||
static_cast<BMVert *>(split_swap ? (node_copy)->data : v_other), \
|
||||
&e_split, \
|
||||
0.0f); \
|
||||
v_split->e = e_split; \
|
||||
BLI_assert(v_split == e_split->v2); \
|
||||
split_edges->add(e_split); \
|
||||
(node_copy)->data = v_split; \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
/* first double until we are more than half as big */
|
||||
while ((el_store->len * 2) < el_store_len) {
|
||||
LinkData *node_curr = static_cast<LinkData *>(el_store->verts.first);
|
||||
while (node_curr) {
|
||||
LinkData *node_curr_copy = MEM_dupalloc(node_curr);
|
||||
if (split == false) {
|
||||
BLI_insertlinkafter(&el_store->verts, node_curr, node_curr_copy);
|
||||
node_curr = node_curr_copy->next;
|
||||
}
|
||||
else {
|
||||
if (node_curr->next || (el_store->flag & BM_EDGELOOP_IS_CLOSED)) {
|
||||
EDGE_SPLIT(node_curr_copy,
|
||||
node_curr->next ? node_curr->next : (LinkData *)el_store->verts.first);
|
||||
BLI_insertlinkafter(&el_store->verts, node_curr, node_curr_copy);
|
||||
node_curr = node_curr_copy->next;
|
||||
}
|
||||
else {
|
||||
EDGE_SPLIT(node_curr_copy, node_curr->prev);
|
||||
BLI_insertlinkbefore(&el_store->verts, node_curr, node_curr_copy);
|
||||
node_curr = node_curr->next;
|
||||
}
|
||||
split_swap = !split_swap;
|
||||
}
|
||||
el_store->len++;
|
||||
}
|
||||
split_swap = !split_swap;
|
||||
}
|
||||
|
||||
if (el_store->len < el_store_len) {
|
||||
LinkData *node_curr = static_cast<LinkData *>(el_store->verts.first);
|
||||
|
||||
int iter_prev = 0;
|
||||
BLI_FOREACH_SPARSE_RANGE (el_store->len, (el_store_len - el_store->len), iter) {
|
||||
while (iter_prev < iter) {
|
||||
node_curr = node_curr->next;
|
||||
iter_prev += 1;
|
||||
}
|
||||
|
||||
LinkData *node_curr_copy;
|
||||
node_curr_copy = MEM_dupalloc(node_curr);
|
||||
if (split == false) {
|
||||
BLI_insertlinkafter(&el_store->verts, node_curr, node_curr_copy);
|
||||
node_curr = node_curr_copy->next;
|
||||
}
|
||||
else {
|
||||
if (node_curr->next || (el_store->flag & BM_EDGELOOP_IS_CLOSED)) {
|
||||
EDGE_SPLIT(node_curr_copy,
|
||||
node_curr->next ? node_curr->next : (LinkData *)el_store->verts.first);
|
||||
BLI_insertlinkafter(&el_store->verts, node_curr, node_curr_copy);
|
||||
node_curr = node_curr_copy->next;
|
||||
}
|
||||
else {
|
||||
EDGE_SPLIT(node_curr_copy, node_curr->prev);
|
||||
BLI_insertlinkbefore(&el_store->verts, node_curr, node_curr_copy);
|
||||
node_curr = node_curr->next;
|
||||
}
|
||||
split_swap = !split_swap;
|
||||
}
|
||||
el_store->len++;
|
||||
iter_prev += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#undef BKE_FOREACH_SUBSET_OF_RANGE
|
||||
#undef EDGE_SPLIT
|
||||
|
||||
BLI_assert(el_store->len == el_store_len);
|
||||
}
|
||||
|
||||
bool BM_edgeloop_overlap_check(BMEdgeLoopStore *el_store_a, BMEdgeLoopStore *el_store_b)
|
||||
{
|
||||
/* A little more efficient if 'a' as smaller. */
|
||||
if (el_store_a->len > el_store_b->len) {
|
||||
std::swap(el_store_a, el_store_b);
|
||||
}
|
||||
|
||||
/* init */
|
||||
for (LinkData &node : el_store_a->verts) {
|
||||
BM_elem_flag_enable((BMVert *)node.data, BM_ELEM_INTERNAL_TAG);
|
||||
}
|
||||
for (LinkData &node : el_store_b->verts) {
|
||||
BM_elem_flag_disable((BMVert *)node.data, BM_ELEM_INTERNAL_TAG);
|
||||
}
|
||||
|
||||
/* Check 'a' (clear as we go). */
|
||||
for (LinkData &node : el_store_a->verts) {
|
||||
if (!BM_elem_flag_test((BMVert *)node.data, BM_ELEM_INTERNAL_TAG)) {
|
||||
/* Finish clearing 'a', leave tag clean. */
|
||||
LinkData *remaining_node = &node;
|
||||
while ((remaining_node = remaining_node->next)) {
|
||||
BM_elem_flag_disable((BMVert *)remaining_node->data, BM_ELEM_INTERNAL_TAG);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
BM_elem_flag_disable((BMVert *)node.data, BM_ELEM_INTERNAL_TAG);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
91
blender-5.2.0/source/blender/bmesh/intern/bmesh_edgeloop.hh
Normal file
91
blender-5.2.0/source/blender/bmesh/intern/bmesh_edgeloop.hh
Normal file
@@ -0,0 +1,91 @@
|
||||
/* SPDX-FileCopyrightText: 2013 by Campbell Barton. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
#include "BLI_set.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMEdgeLoopStore;
|
||||
struct LinkData;
|
||||
|
||||
/* multiple edgeloops (ListBase) */
|
||||
/**
|
||||
* \return listbase of listbases, each linking to a vertex.
|
||||
*/
|
||||
int BM_mesh_edgeloops_find(BMesh *bm,
|
||||
ListBaseT<BMEdgeLoopStore> *r_eloops,
|
||||
bool (*test_fn)(BMEdge *, void *user_data),
|
||||
void *user_data);
|
||||
bool BM_mesh_edgeloops_find_path(BMesh *bm,
|
||||
ListBaseT<BMEdgeLoopStore> *r_eloops,
|
||||
bool (*test_fn)(BMEdge *, void *user_data),
|
||||
void *user_data,
|
||||
BMVert *v_src,
|
||||
BMVert *v_dst);
|
||||
|
||||
void BM_mesh_edgeloops_free(ListBaseT<BMEdgeLoopStore> *eloops);
|
||||
void BM_mesh_edgeloops_calc_center(BMesh *bm, ListBaseT<BMEdgeLoopStore> *eloops);
|
||||
void BM_mesh_edgeloops_calc_normal(BMesh *bm, ListBaseT<BMEdgeLoopStore> *eloops);
|
||||
void BM_mesh_edgeloops_calc_normal_aligned(BMesh *bm,
|
||||
ListBaseT<BMEdgeLoopStore> *eloops,
|
||||
const float no_align[3]);
|
||||
void BM_mesh_edgeloops_calc_order(BMesh *bm, ListBaseT<BMEdgeLoopStore> *eloops, bool use_normals);
|
||||
|
||||
/**
|
||||
* Copy a single edge-loop.
|
||||
* \return new edge-loops.
|
||||
*/
|
||||
BMEdgeLoopStore *BM_edgeloop_copy(BMEdgeLoopStore *el_store);
|
||||
BMEdgeLoopStore *BM_edgeloop_from_verts(BMVert **v_arr, int v_arr_tot, bool is_closed);
|
||||
|
||||
void BM_edgeloop_free(BMEdgeLoopStore *el_store);
|
||||
bool BM_edgeloop_is_closed(BMEdgeLoopStore *el_store);
|
||||
int BM_edgeloop_length_get(BMEdgeLoopStore *el_store);
|
||||
ListBaseT<LinkData> *BM_edgeloop_verts_get(BMEdgeLoopStore *el_store);
|
||||
const float *BM_edgeloop_normal_get(BMEdgeLoopStore *el_store);
|
||||
const float *BM_edgeloop_center_get(BMEdgeLoopStore *el_store);
|
||||
/**
|
||||
* Edges are assigned to one vert -> the next.
|
||||
*/
|
||||
void BM_edgeloop_edges_get(BMEdgeLoopStore *el_store, BMEdge **e_arr);
|
||||
void BM_edgeloop_calc_center(BMesh *bm, BMEdgeLoopStore *el_store);
|
||||
bool BM_edgeloop_calc_normal(BMesh *bm, BMEdgeLoopStore *el_store);
|
||||
/**
|
||||
* For open loops that are straight lines,
|
||||
* calculating the normal as if it were a polygon is meaningless.
|
||||
*
|
||||
* Instead use an alignment vector and calculate the normal based on that.
|
||||
*/
|
||||
bool BM_edgeloop_calc_normal_aligned(BMesh *bm,
|
||||
BMEdgeLoopStore *el_store,
|
||||
const float no_align[3]);
|
||||
void BM_edgeloop_flip(BMesh *bm, BMEdgeLoopStore *el_store);
|
||||
void BM_edgeloop_expand(BMesh *bm,
|
||||
BMEdgeLoopStore *el_store,
|
||||
int el_store_len,
|
||||
bool split,
|
||||
Set<BMEdge *> *split_edges);
|
||||
|
||||
bool BM_edgeloop_overlap_check(BMEdgeLoopStore *el_store_a, BMEdgeLoopStore *el_store_b);
|
||||
|
||||
#define BM_EDGELINK_NEXT(el_store, elink) \
|
||||
(elink)->next ? \
|
||||
(elink)->next : \
|
||||
(BM_edgeloop_is_closed(el_store) ? (LinkData *)BM_edgeloop_verts_get(el_store)->first : \
|
||||
NULL)
|
||||
|
||||
#define BM_EDGELOOP_NEXT(el_store) \
|
||||
(CHECK_TYPE_INLINE(el_store, BMEdgeLoopStore *), (BMEdgeLoopStore *)((LinkData *)el_store)->next)
|
||||
|
||||
} // namespace blender
|
||||
97
blender-5.2.0/source/blender/bmesh/intern/bmesh_error.hh
Normal file
97
blender-5.2.0/source/blender/bmesh/intern/bmesh_error.hh
Normal file
@@ -0,0 +1,97 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "bmesh_operator_api.hh"
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/*----------- BMOP error system ----------*/
|
||||
|
||||
/**
|
||||
* \note More can be added as needed.
|
||||
*/
|
||||
enum eBMOpErrorLevel {
|
||||
/**
|
||||
* Use when the operation could not succeed,
|
||||
* typically from input that isn't sufficient for completing the operation.
|
||||
*/
|
||||
BMO_ERROR_CANCEL = 0,
|
||||
/**
|
||||
* Use this when one or more operations could not succeed,
|
||||
* when the resulting mesh can be used (since some operations succeeded or no change was made).
|
||||
* This is used by default.
|
||||
*/
|
||||
BMO_ERROR_WARN = 1,
|
||||
/**
|
||||
* The mesh resulting from this operation should not be used (where possible).
|
||||
* It should not be left in a corrupt state either.
|
||||
*
|
||||
* See #BMBackup type & function calls.
|
||||
*/
|
||||
BMO_ERROR_FATAL = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* Pushes an error onto the bmesh error stack.
|
||||
* if msg is null, then the default message for the `errcode` is used.
|
||||
*/
|
||||
void BMO_error_raise(BMesh *bm, BMOperator *owner, eBMOpErrorLevel level, const char *msg)
|
||||
ATTR_NONNULL(1, 2, 4);
|
||||
|
||||
/**
|
||||
* Gets the topmost error from the stack.
|
||||
* returns error code or 0 if no error.
|
||||
*/
|
||||
bool BMO_error_get(BMesh *bm, const char **r_msg, BMOperator **r_op, eBMOpErrorLevel *r_level);
|
||||
bool BMO_error_get_at_level(BMesh *bm,
|
||||
eBMOpErrorLevel level,
|
||||
const char **r_msg,
|
||||
BMOperator **r_op);
|
||||
bool BMO_error_occurred_at_level(BMesh *bm, eBMOpErrorLevel level);
|
||||
|
||||
/* Same as #BMO_error_get, only pops the error off the stack as well. */
|
||||
bool BMO_error_pop(BMesh *bm, const char **r_msg, BMOperator **r_op, eBMOpErrorLevel *r_level);
|
||||
void BMO_error_clear(BMesh *bm);
|
||||
|
||||
/* This is meant for handling errors, like self-intersection test failures.
|
||||
* it's dangerous to handle errors in general though, so disabled for now. */
|
||||
|
||||
/* Catches an error raised by the op pointed to by catchop. */
|
||||
/* Not yet implemented. */
|
||||
// int BMO_error_catch_op(BMesh *bm, BMOperator *catchop, char **r_msg);
|
||||
|
||||
#define BM_ELEM_INDEX_VALIDATE(_bm, _msg_a, _msg_b) \
|
||||
BM_mesh_elem_index_validate(_bm, __FILE__ ":" STRINGIFY(__LINE__), __func__, _msg_a, _msg_b)
|
||||
|
||||
/* BMESH_ASSERT */
|
||||
#ifdef WITH_ASSERT_ABORT
|
||||
# define _BMESH_DUMMY_ABORT abort
|
||||
#else
|
||||
# define _BMESH_DUMMY_ABORT() (void)0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This is meant to be higher level than BLI_assert(),
|
||||
* its enabled even when in Release mode.
|
||||
*/
|
||||
#define BMESH_ASSERT(a) \
|
||||
(void)((!(a)) ? ((fprintf(stderr, \
|
||||
"BMESH_ASSERT failed: %s, %s(), %d at \'%s\'\n", \
|
||||
__FILE__, \
|
||||
__func__, \
|
||||
__LINE__, \
|
||||
STRINGIFY(a)), \
|
||||
_BMESH_DUMMY_ABORT(), \
|
||||
NULL)) : \
|
||||
NULL)
|
||||
|
||||
} // namespace blender
|
||||
133
blender-5.2.0/source/blender/bmesh/intern/bmesh_inline.hh
Normal file
133
blender-5.2.0/source/blender/bmesh/intern/bmesh_inline.hh
Normal file
@@ -0,0 +1,133 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* BM Inline functions.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_compiler_compat.h"
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* stuff for dealing with header flags */
|
||||
#define BM_elem_flag_test(ele, hflag) _bm_elem_flag_test(&(ele)->head, hflag)
|
||||
#define BM_elem_flag_test_bool(ele, hflag) _bm_elem_flag_test_bool(&(ele)->head, hflag)
|
||||
#define BM_elem_flag_enable(ele, hflag) _bm_elem_flag_enable(&(ele)->head, hflag)
|
||||
#define BM_elem_flag_disable(ele, hflag) _bm_elem_flag_disable(&(ele)->head, hflag)
|
||||
#define BM_elem_flag_set(ele, hflag, val) _bm_elem_flag_set(&(ele)->head, hflag, val)
|
||||
#define BM_elem_flag_toggle(ele, hflag) _bm_elem_flag_toggle(&(ele)->head, hflag)
|
||||
#define BM_elem_flag_merge(ele_a, ele_b) _bm_elem_flag_merge(&(ele_a)->head, &(ele_b)->head)
|
||||
#define BM_elem_flag_merge_ex(ele_a, ele_b, hflag_and) \
|
||||
_bm_elem_flag_merge_ex(&(ele_a)->head, &(ele_b)->head, hflag_and)
|
||||
#define BM_elem_flag_merge_into(ele, ele_a, ele_b) \
|
||||
_bm_elem_flag_merge_into(&(ele)->head, &(ele_a)->head, &(ele_b)->head)
|
||||
|
||||
ATTR_WARN_UNUSED_RESULT
|
||||
BLI_INLINE char _bm_elem_flag_test(const BMHeader *head, const char hflag)
|
||||
{
|
||||
return head->hflag & hflag;
|
||||
}
|
||||
|
||||
ATTR_WARN_UNUSED_RESULT
|
||||
BLI_INLINE bool _bm_elem_flag_test_bool(const BMHeader *head, const char hflag)
|
||||
{
|
||||
return (head->hflag & hflag) != 0;
|
||||
}
|
||||
|
||||
BLI_INLINE void _bm_elem_flag_enable(BMHeader *head, const char hflag)
|
||||
{
|
||||
head->hflag |= hflag;
|
||||
}
|
||||
|
||||
BLI_INLINE void _bm_elem_flag_disable(BMHeader *head, const char hflag)
|
||||
{
|
||||
head->hflag &= char(~hflag);
|
||||
}
|
||||
|
||||
BLI_INLINE void _bm_elem_flag_set(BMHeader *head, const char hflag, const int val)
|
||||
{
|
||||
if (val) {
|
||||
_bm_elem_flag_enable(head, hflag);
|
||||
}
|
||||
else {
|
||||
_bm_elem_flag_disable(head, hflag);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_INLINE void _bm_elem_flag_toggle(BMHeader *head, const char hflag)
|
||||
{
|
||||
head->hflag ^= hflag;
|
||||
}
|
||||
|
||||
BLI_INLINE void _bm_elem_flag_merge(BMHeader *head_a, BMHeader *head_b)
|
||||
{
|
||||
head_a->hflag = head_b->hflag = head_a->hflag | head_b->hflag;
|
||||
}
|
||||
|
||||
BLI_INLINE void _bm_elem_flag_merge_ex(BMHeader *head_a, BMHeader *head_b, const char hflag_and)
|
||||
{
|
||||
if (((head_a->hflag & head_b->hflag) & hflag_and) == 0) {
|
||||
head_a->hflag &= ~hflag_and;
|
||||
head_b->hflag &= ~hflag_and;
|
||||
}
|
||||
_bm_elem_flag_merge(head_a, head_b);
|
||||
}
|
||||
|
||||
BLI_INLINE void _bm_elem_flag_merge_into(BMHeader *head,
|
||||
const BMHeader *head_a,
|
||||
const BMHeader *head_b)
|
||||
{
|
||||
head->hflag = head_a->hflag | head_b->hflag;
|
||||
}
|
||||
|
||||
/**
|
||||
* notes on #BM_elem_index_set(...) usage,
|
||||
* Set index is sometimes abused as temp storage, other times we can't be
|
||||
* sure if the index values are valid because certain operations have modified
|
||||
* the mesh structure.
|
||||
*
|
||||
* To set the elements to valid indices 'BM_mesh_elem_index_ensure' should be used
|
||||
* rather than adding inline loops, however there are cases where we still
|
||||
* set the index directly
|
||||
*
|
||||
* In an attempt to manage this,
|
||||
* here are 5 tags I'm adding to uses of #BM_elem_index_set
|
||||
*
|
||||
* - `set_inline` -- since the data is already being looped over set to a
|
||||
* valid value inline.
|
||||
*
|
||||
* - `set_dirty!` -- intentionally sets the index to an invalid value,
|
||||
* flagging `bm->elem_index_dirty` so we don't use it.
|
||||
*
|
||||
* - `set_ok` -- this is valid use since the part of the code is low level.
|
||||
*
|
||||
* - `set_ok_invalid` -- set to -1 on purpose since this should not be
|
||||
* used without a full array re-index, do this on
|
||||
* adding new vert/edge/faces since they may be added at
|
||||
* the end of the array.
|
||||
*
|
||||
* - campbell */
|
||||
|
||||
#define BM_elem_index_get(ele) _bm_elem_index_get(&(ele)->head)
|
||||
#define BM_elem_index_set(ele, index) _bm_elem_index_set(&(ele)->head, index)
|
||||
|
||||
BLI_INLINE void _bm_elem_index_set(BMHeader *head, const int index)
|
||||
{
|
||||
head->index = index;
|
||||
}
|
||||
|
||||
ATTR_WARN_UNUSED_RESULT
|
||||
BLI_INLINE int _bm_elem_index_get(const BMHeader *head)
|
||||
{
|
||||
return head->index;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
1333
blender-5.2.0/source/blender/bmesh/intern/bmesh_interp.cc
Normal file
1333
blender-5.2.0/source/blender/bmesh/intern/bmesh_interp.cc
Normal file
File diff suppressed because it is too large
Load Diff
160
blender-5.2.0/source/blender/bmesh/intern/bmesh_interp.hh
Normal file
160
blender-5.2.0/source/blender/bmesh/intern/bmesh_interp.hh
Normal file
@@ -0,0 +1,160 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct LinkNode;
|
||||
struct MemArena;
|
||||
|
||||
namespace bke {
|
||||
enum class AttrDomain : int8_t;
|
||||
enum class AttrType : int16_t;
|
||||
} // namespace bke
|
||||
|
||||
void BM_loop_interp_multires_ex(BMesh *bm,
|
||||
BMLoop *l_dst,
|
||||
const BMFace *f_src,
|
||||
const float f_dst_center[3],
|
||||
const float f_src_center[3],
|
||||
int cd_loop_mdisp_offset);
|
||||
/**
|
||||
* Project the multi-resolution grid in target onto f_src's set of multi-resolution grids.
|
||||
*/
|
||||
void BM_loop_interp_multires(BMesh *bm, BMLoop *l_dst, const BMFace *f_src);
|
||||
|
||||
void BM_face_interp_multires_ex(BMesh *bm,
|
||||
BMFace *f_dst,
|
||||
const BMFace *f_src,
|
||||
const float f_dst_center[3],
|
||||
const float f_src_center[3],
|
||||
int cd_loop_mdisp_offset);
|
||||
void BM_face_interp_multires(BMesh *bm, BMFace *f_dst, const BMFace *f_src);
|
||||
|
||||
void BM_vert_interp_from_face(BMesh *bm, BMVert *v_dst, const BMFace *f_src);
|
||||
|
||||
/**
|
||||
* \brief Data, Interpolate From Verts
|
||||
*
|
||||
* Interpolates per-vertex data from two sources to \a v_dst
|
||||
*
|
||||
* \note This is an exact match to #BM_data_interp_from_edges.
|
||||
*/
|
||||
void BM_data_interp_from_verts(
|
||||
BMesh *bm, const BMVert *v_src_1, const BMVert *v_src_2, BMVert *v_dst, float fac);
|
||||
/**
|
||||
* \brief Data, Interpolate From Edges
|
||||
*
|
||||
* Interpolates per-edge data from two sources to \a e_dst.
|
||||
*
|
||||
* \note This is an exact match to #BM_data_interp_from_verts.
|
||||
*/
|
||||
void BM_data_interp_from_edges(
|
||||
BMesh *bm, const BMEdge *e_src_1, const BMEdge *e_src_2, BMEdge *e_dst, float fac);
|
||||
/**
|
||||
* \brief Data Face-Vert Edge Interpolate
|
||||
*
|
||||
* Walks around the faces of \a e and interpolates
|
||||
* the loop data between two sources.
|
||||
*/
|
||||
void BM_data_interp_face_vert_edge(
|
||||
BMesh *bm, const BMVert *v_src_1, const BMVert *v_src_2, BMVert *v, BMEdge *e, float fac);
|
||||
void BM_data_layer_add(BMesh *bm, CustomData *data, int type);
|
||||
void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, StringRef name);
|
||||
void BM_data_layer_ensure_named(BMesh *bm, CustomData *data, int type, StringRef name);
|
||||
bool BM_data_layer_has_named(const BMesh *bm, const CustomData *data, int type, StringRef name);
|
||||
void BM_data_layer_free(BMesh *bm, CustomData *data, int type);
|
||||
|
||||
/** Ensure the dependent boolean layers exist for all face corner #CD_PROP_FLOAT2 layers. */
|
||||
void BM_uv_map_attr_pin_ensure_for_all_layers(BMesh *bm);
|
||||
|
||||
void BM_uv_map_attr_pin_ensure_named(BMesh *bm, StringRef uv_map_name);
|
||||
bool BM_uv_map_attr_pin_exists(const BMesh *bm, StringRef uv_map_name);
|
||||
|
||||
/**
|
||||
* Remove a named custom data layer, if it existed. Return true if the layer was removed.
|
||||
*/
|
||||
bool BM_data_layer_free_named(BMesh *bm, CustomData *data, StringRef name);
|
||||
void BM_data_layer_free_n(BMesh *bm, CustomData *data, int type, int n);
|
||||
void BM_data_layer_copy(BMesh *bm, CustomData *data, int type, int src_n, int dst_n);
|
||||
|
||||
/* See #BM_data_layer_lookup. */
|
||||
struct BMDataLayerLookup {
|
||||
const int offset = -1;
|
||||
bke::AttrDomain domain;
|
||||
bke::AttrType type;
|
||||
const CustomDataLayer *layer = nullptr;
|
||||
operator bool() const
|
||||
{
|
||||
return offset != -1;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Search for a named custom data layer on all attribute domains and return the domain and type.
|
||||
* This is roughly analogous to #Mesh::attributes().lookup(...), but keep in mind that certain
|
||||
* attributes stored on #Mesh are not stored as attributes on #BMesh.
|
||||
*/
|
||||
BMDataLayerLookup BM_data_layer_lookup(const BMesh &bm, const StringRef name);
|
||||
|
||||
float BM_elem_float_data_get(CustomData *cd, void *element, int type);
|
||||
void BM_elem_float_data_set(CustomData *cd, void *element, int type, float val);
|
||||
|
||||
/**
|
||||
* \brief Data Interpolate From Face
|
||||
*
|
||||
* Projects target onto source, and pulls interpolated custom-data from source.
|
||||
*
|
||||
* \note Only handles loop custom-data. multi-res is handled.
|
||||
* \note Attributes such as selection, material & normals
|
||||
* must be handled with a separate call to #BM_elem_attrs_copy.
|
||||
*/
|
||||
void BM_face_interp_from_face_ex(BMesh *bm,
|
||||
BMFace *f_dst,
|
||||
const BMFace *f_src,
|
||||
bool do_vertex,
|
||||
const void **blocks,
|
||||
const void **blocks_v,
|
||||
float (*cos_2d)[2],
|
||||
float axis_mat[3][3]);
|
||||
void BM_face_interp_from_face(BMesh *bm, BMFace *f_dst, const BMFace *f_src, bool do_vertex);
|
||||
/**
|
||||
* Projects a single loop, target, onto f_src for custom-data interpolation.
|
||||
* multi-resolution is handled.
|
||||
* \param do_vertex: When true the target's vert data will also get interpolated.
|
||||
*/
|
||||
void BM_loop_interp_from_face(
|
||||
BMesh *bm, BMLoop *l_dst, const BMFace *f_src, bool do_vertex, bool do_multires);
|
||||
|
||||
/**
|
||||
* Smooths boundaries between multi-res grids,
|
||||
* including some borders in adjacent faces.
|
||||
*/
|
||||
void BM_face_multires_bounds_smooth(BMesh *bm, BMFace *f);
|
||||
|
||||
LinkNode *BM_vert_loop_groups_data_layer_create(
|
||||
BMesh *bm, BMVert *v, int layer_n, const float *loop_weights, MemArena *arena);
|
||||
/**
|
||||
* Take existing custom data and merge each fan's data.
|
||||
*/
|
||||
void BM_vert_loop_groups_data_layer_merge(BMesh *bm, LinkNode *groups, int layer_n);
|
||||
/**
|
||||
* A version of #BM_vert_loop_groups_data_layer_merge
|
||||
* that takes an array of loop-weights (aligned with #BM_LOOPS_OF_VERT iterator).
|
||||
*/
|
||||
void BM_vert_loop_groups_data_layer_merge_weights(BMesh *bm,
|
||||
LinkNode *groups,
|
||||
int layer_n,
|
||||
const float *loop_weights);
|
||||
|
||||
} // namespace blender
|
||||
661
blender-5.2.0/source/blender/bmesh/intern/bmesh_iterators.cc
Normal file
661
blender-5.2.0/source/blender/bmesh/intern/bmesh_iterators.cc
Normal file
@@ -0,0 +1,661 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* Functions to abstract looping over bmesh data structures.
|
||||
*
|
||||
* See: bmesh_iterators_inlin.c too, some functions are here for speed reasons.
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "bmesh.hh"
|
||||
#include "intern/bmesh_structure.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
const char bm_iter_itype_htype_map[BM_ITYPE_MAX] = {
|
||||
'\0',
|
||||
BM_VERT, /* BM_VERTS_OF_MESH */
|
||||
BM_EDGE, /* BM_EDGES_OF_MESH */
|
||||
BM_FACE, /* BM_FACES_OF_MESH */
|
||||
BM_EDGE, /* BM_EDGES_OF_VERT */
|
||||
BM_FACE, /* BM_FACES_OF_VERT */
|
||||
BM_LOOP, /* BM_LOOPS_OF_VERT */
|
||||
BM_VERT, /* BM_VERTS_OF_EDGE */
|
||||
BM_FACE, /* BM_FACES_OF_EDGE */
|
||||
BM_VERT, /* BM_VERTS_OF_FACE */
|
||||
BM_EDGE, /* BM_EDGES_OF_FACE */
|
||||
BM_LOOP, /* BM_LOOPS_OF_FACE */
|
||||
BM_LOOP, /* BM_LOOPS_OF_LOOP */
|
||||
BM_LOOP, /* BM_LOOPS_OF_EDGE */
|
||||
};
|
||||
|
||||
int BM_iter_mesh_count(const char itype, BMesh *bm)
|
||||
{
|
||||
int count;
|
||||
|
||||
switch (itype) {
|
||||
case BM_VERTS_OF_MESH:
|
||||
count = bm->totvert;
|
||||
break;
|
||||
case BM_EDGES_OF_MESH:
|
||||
count = bm->totedge;
|
||||
break;
|
||||
case BM_FACES_OF_MESH:
|
||||
count = bm->totface;
|
||||
break;
|
||||
default:
|
||||
count = 0;
|
||||
BLI_assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void *BM_iter_at_index(BMesh *bm, const char itype, void *data, int index)
|
||||
{
|
||||
BMIter iter;
|
||||
void *val;
|
||||
int i;
|
||||
|
||||
/* sanity check */
|
||||
if (index < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
val = BM_iter_new(&iter, bm, itype, data);
|
||||
|
||||
i = 0;
|
||||
while (i < index) {
|
||||
val = BM_iter_step(&iter);
|
||||
i++;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
int BM_iter_as_array(BMesh *bm, const char itype, void *data, void **array, const int len)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
/* sanity check */
|
||||
if (len > 0) {
|
||||
BMIter iter;
|
||||
void *ele;
|
||||
|
||||
for (ele = BM_iter_new(&iter, bm, itype, data); ele; ele = BM_iter_step(&iter)) {
|
||||
array[i] = ele;
|
||||
i++;
|
||||
if (i == len) {
|
||||
return len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
int BMO_iter_as_array(BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
const char restrictmask,
|
||||
void **array,
|
||||
const int len)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
/* sanity check */
|
||||
if (len > 0) {
|
||||
BMOIter oiter;
|
||||
void *ele;
|
||||
|
||||
for (ele = BMO_iter_new(&oiter, slot_args, slot_name, restrictmask); ele;
|
||||
ele = BMO_iter_step(&oiter))
|
||||
{
|
||||
array[i] = ele;
|
||||
i++;
|
||||
if (i == len) {
|
||||
return len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void *BM_iter_as_arrayN(BMesh *bm,
|
||||
const char itype,
|
||||
void *data,
|
||||
int *r_len,
|
||||
/* optional args to avoid an alloc (normally stack array) */
|
||||
void **stack_array,
|
||||
int stack_array_size)
|
||||
{
|
||||
BMIter iter;
|
||||
|
||||
BLI_assert(stack_array_size == 0 || (stack_array_size && stack_array));
|
||||
|
||||
/* We can't rely on #BMIter.count being set. */
|
||||
switch (itype) {
|
||||
case BM_VERTS_OF_MESH:
|
||||
iter.count = bm->totvert;
|
||||
break;
|
||||
case BM_EDGES_OF_MESH:
|
||||
iter.count = bm->totedge;
|
||||
break;
|
||||
case BM_FACES_OF_MESH:
|
||||
iter.count = bm->totface;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (BM_iter_init(&iter, bm, itype, data) && iter.count > 0) {
|
||||
BMElem *ele;
|
||||
BMElem **array = iter.count > stack_array_size ?
|
||||
MEM_new_array_uninitialized<BMElem *>(iter.count, __func__) :
|
||||
reinterpret_cast<BMElem **>(stack_array);
|
||||
int i = 0;
|
||||
|
||||
*r_len = iter.count; /* set before iterating */
|
||||
|
||||
while ((ele = static_cast<BMElem *>(BM_iter_step(&iter)))) {
|
||||
array[i++] = ele;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
*r_len = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *BMO_iter_as_arrayN(BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
const char restrictmask,
|
||||
int *r_len,
|
||||
/* optional args to avoid an alloc (normally stack array) */
|
||||
void **stack_array,
|
||||
int stack_array_size)
|
||||
{
|
||||
BMOIter iter;
|
||||
BMElem *ele;
|
||||
const int slot_len = BMO_slot_buffer_len(slot_args, slot_name);
|
||||
|
||||
BLI_assert(stack_array_size == 0 || (stack_array_size && stack_array));
|
||||
|
||||
if ((ele = static_cast<BMElem *>(BMO_iter_new(&iter, slot_args, slot_name, restrictmask))) &&
|
||||
slot_len > 0)
|
||||
{
|
||||
BMElem **array = slot_len > stack_array_size ?
|
||||
MEM_new_array_uninitialized<BMElem *>(slot_len, __func__) :
|
||||
reinterpret_cast<BMElem **>(stack_array);
|
||||
int i = 0;
|
||||
|
||||
do {
|
||||
array[i++] = ele;
|
||||
} while ((ele = static_cast<BMElem *>(BMO_iter_step(&iter))));
|
||||
BLI_assert(i <= slot_len);
|
||||
|
||||
if (i != slot_len) {
|
||||
if (reinterpret_cast<void **>(array) != stack_array) {
|
||||
array = static_cast<BMElem **>(MEM_realloc_uninitialized(array, sizeof(ele) * i));
|
||||
}
|
||||
}
|
||||
*r_len = i;
|
||||
return array;
|
||||
}
|
||||
|
||||
*r_len = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int BM_iter_mesh_bitmap_from_filter(const char itype,
|
||||
BMesh *bm,
|
||||
MutableBitSpan bitmap,
|
||||
bool (*test_fn)(BMElem *, void *user_data),
|
||||
void *user_data)
|
||||
{
|
||||
BMIter iter;
|
||||
BMElem *ele;
|
||||
int i;
|
||||
int bitmap_enabled = 0;
|
||||
|
||||
BM_ITER_MESH_INDEX (ele, &iter, bm, itype, i) {
|
||||
if (test_fn(ele, user_data)) {
|
||||
bitmap[i].set();
|
||||
bitmap_enabled++;
|
||||
}
|
||||
else {
|
||||
bitmap[i].reset();
|
||||
}
|
||||
}
|
||||
|
||||
return bitmap_enabled;
|
||||
}
|
||||
|
||||
int BM_iter_mesh_bitmap_from_filter_tessface(BMesh *bm,
|
||||
MutableBitSpan bitmap,
|
||||
bool (*test_fn)(BMFace *, void *user_data),
|
||||
void *user_data)
|
||||
{
|
||||
BMIter iter;
|
||||
BMFace *f;
|
||||
int i;
|
||||
int j = 0;
|
||||
int bitmap_enabled = 0;
|
||||
|
||||
BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
|
||||
if (test_fn(f, user_data)) {
|
||||
for (int tri = 2; tri < f->len; tri++) {
|
||||
bitmap[j].set();
|
||||
bitmap_enabled++;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int tri = 2; tri < f->len; tri++) {
|
||||
bitmap[j].reset();
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bitmap_enabled;
|
||||
}
|
||||
|
||||
int BM_iter_elem_count_flag(const char itype, void *data, const char hflag, const bool value)
|
||||
{
|
||||
BMIter iter;
|
||||
BMElem *ele;
|
||||
int count = 0;
|
||||
|
||||
BM_ITER_ELEM (ele, &iter, data, itype) {
|
||||
if (BM_elem_flag_test_bool(ele, hflag) == value) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int BMO_iter_elem_count_flag(
|
||||
BMesh *bm, const char itype, void *data, const short oflag, const bool value)
|
||||
{
|
||||
BMIter iter;
|
||||
int count = 0;
|
||||
|
||||
/* loops have no header flags */
|
||||
BLI_assert(bm_iter_itype_htype_map[itype] != BM_LOOP);
|
||||
|
||||
switch (bm_iter_itype_htype_map[itype]) {
|
||||
case BM_VERT: {
|
||||
BMVert *ele;
|
||||
BM_ITER_ELEM (ele, &iter, data, itype) {
|
||||
if (BMO_vert_flag_test_bool(bm, ele, oflag) == value) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BM_EDGE: {
|
||||
BMEdge *ele;
|
||||
BM_ITER_ELEM (ele, &iter, data, itype) {
|
||||
if (BMO_edge_flag_test_bool(bm, ele, oflag) == value) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BM_FACE: {
|
||||
BMFace *ele;
|
||||
BM_ITER_ELEM (ele, &iter, data, itype) {
|
||||
if (BMO_face_flag_test_bool(bm, ele, oflag) == value) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int BM_iter_mesh_count_flag(const char itype, BMesh *bm, const char hflag, const bool value)
|
||||
{
|
||||
BMIter iter;
|
||||
BMElem *ele;
|
||||
int count = 0;
|
||||
|
||||
BM_ITER_MESH (ele, &iter, bm, itype) {
|
||||
if (BM_elem_flag_test_bool(ele, hflag) == value) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes on iterator implementation:
|
||||
*
|
||||
* Iterators keep track of the next element in a sequence.
|
||||
* When a step() callback is invoked the current value of 'next'
|
||||
* is stored to be returned later and the next variable is incremented.
|
||||
*
|
||||
* When the end of a sequence is reached, next should always equal nullptr
|
||||
*
|
||||
* The 'bmiter__' prefix is used because these are used in
|
||||
* bmesh_iterators_inine.c but should otherwise be seen as
|
||||
* private.
|
||||
*/
|
||||
|
||||
/*
|
||||
* VERT OF MESH CALLBACKS
|
||||
*/
|
||||
|
||||
/* see bug #36923 for why we need this,
|
||||
* allow adding but not removing, this isn't _totally_ safe since
|
||||
* you could add/remove within the same loop, but catches common cases
|
||||
*/
|
||||
#ifndef NDEBUG
|
||||
# define USE_IMMUTABLE_ASSERT
|
||||
#endif
|
||||
|
||||
void bmiter__elem_of_mesh_begin(BMIter__elem_of_mesh *iter)
|
||||
{
|
||||
#ifdef USE_IMMUTABLE_ASSERT
|
||||
(reinterpret_cast<BMIter *>(iter))->count = BLI_mempool_len(iter->pooliter.pool);
|
||||
#endif
|
||||
BLI_mempool_iternew(iter->pooliter.pool, &iter->pooliter);
|
||||
}
|
||||
|
||||
void *bmiter__elem_of_mesh_step(BMIter__elem_of_mesh *iter)
|
||||
{
|
||||
#ifdef USE_IMMUTABLE_ASSERT
|
||||
BLI_assert(((BMIter *)iter)->count <= BLI_mempool_len(iter->pooliter.pool));
|
||||
#endif
|
||||
return BLI_mempool_iterstep(&iter->pooliter);
|
||||
}
|
||||
|
||||
#ifdef USE_IMMUTABLE_ASSERT
|
||||
# undef USE_IMMUTABLE_ASSERT
|
||||
#endif
|
||||
|
||||
/*
|
||||
* EDGE OF VERT CALLBACKS
|
||||
*/
|
||||
|
||||
void bmiter__edge_of_vert_begin(BMIter__edge_of_vert *iter)
|
||||
{
|
||||
if (iter->vdata->e) {
|
||||
iter->e_first = iter->vdata->e;
|
||||
iter->e_next = iter->vdata->e;
|
||||
}
|
||||
else {
|
||||
iter->e_first = nullptr;
|
||||
iter->e_next = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void *bmiter__edge_of_vert_step(BMIter__edge_of_vert *iter)
|
||||
{
|
||||
BMEdge *e_curr = iter->e_next;
|
||||
|
||||
if (iter->e_next) {
|
||||
iter->e_next = bmesh_disk_edge_next(iter->e_next, iter->vdata);
|
||||
if (iter->e_next == iter->e_first) {
|
||||
iter->e_next = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return e_curr;
|
||||
}
|
||||
|
||||
/*
|
||||
* FACE OF VERT CALLBACKS
|
||||
*/
|
||||
|
||||
void bmiter__face_of_vert_begin(BMIter__face_of_vert *iter)
|
||||
{
|
||||
(reinterpret_cast<BMIter *>(iter))->count = bmesh_disk_facevert_count(iter->vdata);
|
||||
if ((reinterpret_cast<BMIter *>(iter))->count) {
|
||||
iter->l_first = bmesh_disk_faceloop_find_first(iter->vdata->e, iter->vdata);
|
||||
iter->e_first = iter->l_first->e;
|
||||
iter->e_next = iter->e_first;
|
||||
iter->l_next = iter->l_first;
|
||||
}
|
||||
else {
|
||||
iter->l_first = iter->l_next = nullptr;
|
||||
iter->e_first = iter->e_next = nullptr;
|
||||
}
|
||||
}
|
||||
void *bmiter__face_of_vert_step(BMIter__face_of_vert *iter)
|
||||
{
|
||||
BMLoop *l_curr = iter->l_next;
|
||||
|
||||
if ((reinterpret_cast<BMIter *>(iter))->count && iter->l_next) {
|
||||
(reinterpret_cast<BMIter *>(iter))->count--;
|
||||
iter->l_next = bmesh_radial_faceloop_find_next(iter->l_next, iter->vdata);
|
||||
if (iter->l_next == iter->l_first) {
|
||||
iter->e_next = bmesh_disk_faceedge_find_next(iter->e_next, iter->vdata);
|
||||
iter->l_first = bmesh_radial_faceloop_find_first(iter->e_next->l, iter->vdata);
|
||||
iter->l_next = iter->l_first;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(reinterpret_cast<BMIter *>(iter))->count) {
|
||||
iter->l_next = nullptr;
|
||||
}
|
||||
|
||||
return l_curr ? l_curr->f : nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* LOOP OF VERT CALLBACKS
|
||||
*/
|
||||
|
||||
void bmiter__loop_of_vert_begin(BMIter__loop_of_vert *iter)
|
||||
{
|
||||
(reinterpret_cast<BMIter *>(iter))->count = bmesh_disk_facevert_count(iter->vdata);
|
||||
if ((reinterpret_cast<BMIter *>(iter))->count) {
|
||||
iter->l_first = bmesh_disk_faceloop_find_first(iter->vdata->e, iter->vdata);
|
||||
iter->e_first = iter->l_first->e;
|
||||
iter->e_next = iter->e_first;
|
||||
iter->l_next = iter->l_first;
|
||||
}
|
||||
else {
|
||||
iter->l_first = iter->l_next = nullptr;
|
||||
iter->e_first = iter->e_next = nullptr;
|
||||
}
|
||||
}
|
||||
void *bmiter__loop_of_vert_step(BMIter__loop_of_vert *iter)
|
||||
{
|
||||
BMLoop *l_curr = iter->l_next;
|
||||
|
||||
if ((reinterpret_cast<BMIter *>(iter))->count) {
|
||||
(reinterpret_cast<BMIter *>(iter))->count--;
|
||||
iter->l_next = bmesh_radial_faceloop_find_next(iter->l_next, iter->vdata);
|
||||
if (iter->l_next == iter->l_first) {
|
||||
iter->e_next = bmesh_disk_faceedge_find_next(iter->e_next, iter->vdata);
|
||||
iter->l_first = bmesh_radial_faceloop_find_first(iter->e_next->l, iter->vdata);
|
||||
iter->l_next = iter->l_first;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(reinterpret_cast<BMIter *>(iter))->count) {
|
||||
iter->l_next = nullptr;
|
||||
}
|
||||
|
||||
/* nullptr on finish */
|
||||
return l_curr;
|
||||
}
|
||||
|
||||
/*
|
||||
* LOOP OF EDGE CALLBACKS
|
||||
*/
|
||||
|
||||
void bmiter__loop_of_edge_begin(BMIter__loop_of_edge *iter)
|
||||
{
|
||||
iter->l_first = iter->l_next = iter->edata->l;
|
||||
}
|
||||
|
||||
void *bmiter__loop_of_edge_step(BMIter__loop_of_edge *iter)
|
||||
{
|
||||
BMLoop *l_curr = iter->l_next;
|
||||
|
||||
if (iter->l_next) {
|
||||
iter->l_next = iter->l_next->radial_next;
|
||||
if (iter->l_next == iter->l_first) {
|
||||
iter->l_next = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* nullptr on finish */
|
||||
return l_curr;
|
||||
}
|
||||
|
||||
/*
|
||||
* LOOP OF LOOP CALLBACKS
|
||||
*/
|
||||
|
||||
void bmiter__loop_of_loop_begin(BMIter__loop_of_loop *iter)
|
||||
{
|
||||
iter->l_first = iter->ldata;
|
||||
iter->l_next = iter->l_first->radial_next;
|
||||
|
||||
if (iter->l_next == iter->l_first) {
|
||||
iter->l_next = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void *bmiter__loop_of_loop_step(BMIter__loop_of_loop *iter)
|
||||
{
|
||||
BMLoop *l_curr = iter->l_next;
|
||||
|
||||
if (iter->l_next) {
|
||||
iter->l_next = iter->l_next->radial_next;
|
||||
if (iter->l_next == iter->l_first) {
|
||||
iter->l_next = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* nullptr on finish */
|
||||
return l_curr;
|
||||
}
|
||||
|
||||
/*
|
||||
* FACE OF EDGE CALLBACKS
|
||||
*/
|
||||
|
||||
void bmiter__face_of_edge_begin(BMIter__face_of_edge *iter)
|
||||
{
|
||||
iter->l_first = iter->l_next = iter->edata->l;
|
||||
}
|
||||
|
||||
void *bmiter__face_of_edge_step(BMIter__face_of_edge *iter)
|
||||
{
|
||||
BMLoop *current = iter->l_next;
|
||||
|
||||
if (iter->l_next) {
|
||||
iter->l_next = iter->l_next->radial_next;
|
||||
if (iter->l_next == iter->l_first) {
|
||||
iter->l_next = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return current ? current->f : nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* VERTS OF EDGE CALLBACKS
|
||||
*/
|
||||
|
||||
void bmiter__vert_of_edge_begin(BMIter__vert_of_edge *iter)
|
||||
{
|
||||
(reinterpret_cast<BMIter *>(iter))->count = 0;
|
||||
}
|
||||
|
||||
void *bmiter__vert_of_edge_step(BMIter__vert_of_edge *iter)
|
||||
{
|
||||
switch ((reinterpret_cast<BMIter *>(iter))->count++) {
|
||||
case 0:
|
||||
return iter->edata->v1;
|
||||
case 1:
|
||||
return iter->edata->v2;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* VERT OF FACE CALLBACKS
|
||||
*/
|
||||
|
||||
void bmiter__vert_of_face_begin(BMIter__vert_of_face *iter)
|
||||
{
|
||||
iter->l_first = iter->l_next = BM_FACE_FIRST_LOOP(iter->pdata);
|
||||
}
|
||||
|
||||
void *bmiter__vert_of_face_step(BMIter__vert_of_face *iter)
|
||||
{
|
||||
BMLoop *l_curr = iter->l_next;
|
||||
|
||||
if (iter->l_next) {
|
||||
iter->l_next = iter->l_next->next;
|
||||
if (iter->l_next == iter->l_first) {
|
||||
iter->l_next = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return l_curr ? l_curr->v : nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* EDGE OF FACE CALLBACKS
|
||||
*/
|
||||
|
||||
void bmiter__edge_of_face_begin(BMIter__edge_of_face *iter)
|
||||
{
|
||||
iter->l_first = iter->l_next = BM_FACE_FIRST_LOOP(iter->pdata);
|
||||
}
|
||||
|
||||
void *bmiter__edge_of_face_step(BMIter__edge_of_face *iter)
|
||||
{
|
||||
BMLoop *l_curr = iter->l_next;
|
||||
|
||||
if (iter->l_next) {
|
||||
iter->l_next = iter->l_next->next;
|
||||
if (iter->l_next == iter->l_first) {
|
||||
iter->l_next = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return l_curr ? l_curr->e : nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* LOOP OF FACE CALLBACKS
|
||||
*/
|
||||
|
||||
void bmiter__loop_of_face_begin(BMIter__loop_of_face *iter)
|
||||
{
|
||||
iter->l_first = iter->l_next = BM_FACE_FIRST_LOOP(iter->pdata);
|
||||
}
|
||||
|
||||
void *bmiter__loop_of_face_step(BMIter__loop_of_face *iter)
|
||||
{
|
||||
BMLoop *l_curr = iter->l_next;
|
||||
|
||||
if (iter->l_next) {
|
||||
iter->l_next = iter->l_next->next;
|
||||
if (iter->l_next == iter->l_first) {
|
||||
iter->l_next = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return l_curr;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
295
blender-5.2.0/source/blender/bmesh/intern/bmesh_iterators.hh
Normal file
295
blender-5.2.0/source/blender/bmesh/intern/bmesh_iterators.hh
Normal file
@@ -0,0 +1,295 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief BMesh Iterators
|
||||
*
|
||||
* The functions and structures in this file
|
||||
* provide a unified method for iterating over
|
||||
* the elements of a mesh and answering simple
|
||||
* adjacency queries. Tool authors should use
|
||||
* the iterators provided in this file instead
|
||||
* of inspecting the structure directly.
|
||||
*/
|
||||
|
||||
#include "BLI_bit_span.hh"
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_mempool.h"
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
#include "intern/bmesh_operator_api.hh"
|
||||
|
||||
/* these iterator over all elements of a specific
|
||||
* type in the mesh.
|
||||
*
|
||||
* be sure to keep 'bm_iter_itype_htype_map' in sync with any changes
|
||||
*/
|
||||
|
||||
namespace blender {
|
||||
|
||||
enum BMIterType {
|
||||
BM_VERTS_OF_MESH = 1,
|
||||
BM_EDGES_OF_MESH = 2,
|
||||
BM_FACES_OF_MESH = 3,
|
||||
/* these are topological iterators. */
|
||||
BM_EDGES_OF_VERT = 4,
|
||||
BM_FACES_OF_VERT = 5,
|
||||
BM_LOOPS_OF_VERT = 6,
|
||||
BM_VERTS_OF_EDGE = 7, /* just v1, v2: added so py can use generalized sequencer wrapper */
|
||||
BM_FACES_OF_EDGE = 8,
|
||||
BM_VERTS_OF_FACE = 9,
|
||||
BM_EDGES_OF_FACE = 10,
|
||||
BM_LOOPS_OF_FACE = 11,
|
||||
/* returns elements from all boundaries, and returns
|
||||
* the first element at the end to flag that we're entering
|
||||
* a different face hole boundary. */
|
||||
// BM_ALL_LOOPS_OF_FACE = 12,
|
||||
/* iterate through loops around this loop, which are fetched
|
||||
* from the other faces in the radial cycle surrounding the
|
||||
* input loop's edge. */
|
||||
BM_LOOPS_OF_LOOP = 12,
|
||||
BM_LOOPS_OF_EDGE = 13,
|
||||
};
|
||||
|
||||
#define BM_ITYPE_MAX 14
|
||||
|
||||
/* the iterator htype for each iterator */
|
||||
extern const char bm_iter_itype_htype_map[BM_ITYPE_MAX];
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Defines for passing to #BM_iter_new.
|
||||
*
|
||||
* "OF" can be substituted for "around" so #BM_VERTS_OF_FACE means "vertices* around a face."
|
||||
* \{ */
|
||||
|
||||
#define BM_ITER_MESH(ele, iter, bm, itype) \
|
||||
for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_new(iter, bm, itype, NULL); ele; \
|
||||
BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter))
|
||||
|
||||
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar) \
|
||||
for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_new(iter, bm, itype, NULL), indexvar = 0; ele; \
|
||||
BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter), (indexvar)++)
|
||||
|
||||
/* a version of BM_ITER_MESH which keeps the next item in storage
|
||||
* so we can delete the current item, see bug #36923. */
|
||||
#ifndef NDEBUG
|
||||
# define BM_ITER_MESH_MUTABLE(ele, ele_next, iter, bm, itype) \
|
||||
for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_new(iter, bm, itype, NULL); \
|
||||
ele ? ((void)((iter)->count = BM_iter_mesh_count(itype, bm)), \
|
||||
(void)(BM_CHECK_TYPE_ELEM_ASSIGN(ele_next) = BM_iter_step(iter)), \
|
||||
1) : \
|
||||
0; \
|
||||
BM_CHECK_TYPE_ELEM_ASSIGN(ele) = ele_next)
|
||||
#else
|
||||
# define BM_ITER_MESH_MUTABLE(ele, ele_next, iter, bm, itype) \
|
||||
for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_new(iter, bm, itype, NULL); \
|
||||
ele ? ((BM_CHECK_TYPE_ELEM_ASSIGN(ele_next) = BM_iter_step(iter)), 1) : 0; \
|
||||
ele = ele_next)
|
||||
#endif
|
||||
|
||||
#define BM_ITER_ELEM(ele, iter, data, itype) \
|
||||
for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_new(iter, NULL, itype, data); ele; \
|
||||
BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter))
|
||||
|
||||
#define BM_ITER_ELEM_INDEX(ele, iter, data, itype, indexvar) \
|
||||
for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_new(iter, NULL, itype, data), indexvar = 0; ele; \
|
||||
BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter), (indexvar)++)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* iterator type structs */
|
||||
struct BMIter__elem_of_mesh {
|
||||
BLI_mempool_iter pooliter;
|
||||
};
|
||||
struct BMIter__edge_of_vert {
|
||||
BMVert *vdata;
|
||||
BMEdge *e_first, *e_next;
|
||||
};
|
||||
struct BMIter__face_of_vert {
|
||||
BMVert *vdata;
|
||||
BMLoop *l_first, *l_next;
|
||||
BMEdge *e_first, *e_next;
|
||||
};
|
||||
struct BMIter__loop_of_vert {
|
||||
BMVert *vdata;
|
||||
BMLoop *l_first, *l_next;
|
||||
BMEdge *e_first, *e_next;
|
||||
};
|
||||
struct BMIter__loop_of_edge {
|
||||
BMEdge *edata;
|
||||
BMLoop *l_first, *l_next;
|
||||
};
|
||||
struct BMIter__loop_of_loop {
|
||||
BMLoop *ldata;
|
||||
BMLoop *l_first, *l_next;
|
||||
};
|
||||
struct BMIter__face_of_edge {
|
||||
BMEdge *edata;
|
||||
BMLoop *l_first, *l_next;
|
||||
};
|
||||
struct BMIter__vert_of_edge {
|
||||
BMEdge *edata;
|
||||
};
|
||||
struct BMIter__vert_of_face {
|
||||
BMFace *pdata;
|
||||
BMLoop *l_first, *l_next;
|
||||
};
|
||||
struct BMIter__edge_of_face {
|
||||
BMFace *pdata;
|
||||
BMLoop *l_first, *l_next;
|
||||
};
|
||||
struct BMIter__loop_of_face {
|
||||
BMFace *pdata;
|
||||
BMLoop *l_first, *l_next;
|
||||
};
|
||||
|
||||
using BMIter__begin_cb = void (*)(void *);
|
||||
using BMIter__step_cb = void *(*)(void *);
|
||||
|
||||
/* Iterator Structure */
|
||||
/* NOTE: some of these vars are not used,
|
||||
* so they have been commented to save stack space since this struct is used all over */
|
||||
struct BMIter {
|
||||
/* keep union first */
|
||||
union {
|
||||
BMIter__elem_of_mesh elem_of_mesh;
|
||||
|
||||
BMIter__edge_of_vert edge_of_vert;
|
||||
BMIter__face_of_vert face_of_vert;
|
||||
BMIter__loop_of_vert loop_of_vert;
|
||||
BMIter__loop_of_edge loop_of_edge;
|
||||
BMIter__loop_of_loop loop_of_loop;
|
||||
BMIter__face_of_edge face_of_edge;
|
||||
BMIter__vert_of_edge vert_of_edge;
|
||||
BMIter__vert_of_face vert_of_face;
|
||||
BMIter__edge_of_face edge_of_face;
|
||||
BMIter__loop_of_face loop_of_face;
|
||||
} data;
|
||||
|
||||
BMIter__begin_cb begin;
|
||||
BMIter__step_cb step;
|
||||
|
||||
int count; /* NOTE: only some iterators set this, don't rely on it. */
|
||||
char itype;
|
||||
};
|
||||
|
||||
/**
|
||||
* \note Use #BM_vert_at_index / #BM_edge_at_index / #BM_face_at_index for mesh arrays.
|
||||
*/
|
||||
void *BM_iter_at_index(BMesh *bm, char itype, void *data, int index) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* \brief Iterator as Array
|
||||
*
|
||||
* Sometimes its convenient to get the iterator as an array
|
||||
* to avoid multiple calls to #BM_iter_at_index.
|
||||
*/
|
||||
int BM_iter_as_array(BMesh *bm, char itype, void *data, void **array, int len);
|
||||
/**
|
||||
* \brief Iterator as Array
|
||||
*
|
||||
* Allocates a new array, has the advantage that you don't need to know the size ahead of time.
|
||||
*
|
||||
* Takes advantage of less common iterator usage to avoid counting twice,
|
||||
* which you might end up doing when #BM_iter_as_array is used.
|
||||
*
|
||||
* Caller needs to free the array.
|
||||
*/
|
||||
void *BM_iter_as_arrayN(BMesh *bm,
|
||||
char itype,
|
||||
void *data,
|
||||
int *r_len,
|
||||
void **stack_array,
|
||||
int stack_array_size) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* \brief Operator Iterator as Array
|
||||
*
|
||||
* Sometimes its convenient to get the iterator as an array.
|
||||
*/
|
||||
int BMO_iter_as_array(BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
char restrictmask,
|
||||
void **array,
|
||||
int len);
|
||||
void *BMO_iter_as_arrayN(BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
char restrictmask,
|
||||
int *r_len,
|
||||
/* optional args to avoid an alloc (normally stack array) */
|
||||
void **stack_array,
|
||||
int stack_array_size);
|
||||
|
||||
int BM_iter_mesh_bitmap_from_filter(char itype,
|
||||
BMesh *bm,
|
||||
MutableBitSpan bitmap,
|
||||
bool (*test_fn)(BMElem *, void *user_data),
|
||||
void *user_data);
|
||||
/**
|
||||
* Needed when we want to check faces, but return a loop aligned array.
|
||||
*/
|
||||
int BM_iter_mesh_bitmap_from_filter_tessface(BMesh *bm,
|
||||
MutableBitSpan bitmap,
|
||||
bool (*test_fn)(BMFace *, void *user_data),
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
* \brief Elem Iter Flag Count
|
||||
*
|
||||
* Counts how many flagged / unflagged items are found in this element.
|
||||
*/
|
||||
int BM_iter_elem_count_flag(char itype, void *data, char hflag, bool value);
|
||||
/**
|
||||
* \brief Elem Iter Tool Flag Count
|
||||
*
|
||||
* Counts how many flagged / unflagged items are found in this element.
|
||||
*/
|
||||
int BMO_iter_elem_count_flag(BMesh *bm, char itype, void *data, short oflag, bool value);
|
||||
/**
|
||||
* Utility function.
|
||||
*/
|
||||
int BM_iter_mesh_count(char itype, BMesh *bm);
|
||||
/**
|
||||
* \brief Mesh Iter Flag Count
|
||||
*
|
||||
* Counts how many flagged / unflagged items are found in this mesh.
|
||||
*/
|
||||
int BM_iter_mesh_count_flag(char itype, BMesh *bm, char hflag, bool value);
|
||||
|
||||
/* private for bmesh_iterators_inline.c */
|
||||
|
||||
#define BMITER_CB_DEF(name) \
|
||||
struct BMIter__##name; \
|
||||
void bmiter__##name##_begin(struct BMIter__##name *iter); \
|
||||
void *bmiter__##name##_step(struct BMIter__##name *iter)
|
||||
|
||||
BMITER_CB_DEF(elem_of_mesh);
|
||||
BMITER_CB_DEF(edge_of_vert);
|
||||
BMITER_CB_DEF(face_of_vert);
|
||||
BMITER_CB_DEF(loop_of_vert);
|
||||
BMITER_CB_DEF(loop_of_edge);
|
||||
BMITER_CB_DEF(loop_of_loop);
|
||||
BMITER_CB_DEF(face_of_edge);
|
||||
BMITER_CB_DEF(vert_of_edge);
|
||||
BMITER_CB_DEF(vert_of_face);
|
||||
BMITER_CB_DEF(edge_of_face);
|
||||
BMITER_CB_DEF(loop_of_face);
|
||||
|
||||
#undef BMITER_CB_DEF
|
||||
|
||||
} // namespace blender
|
||||
|
||||
#include "intern/bmesh_iterators_inline.hh" /* IWYU pragma: export */
|
||||
|
||||
#define BM_ITER_CHECK_TYPE_DATA(data) \
|
||||
CHECK_TYPE_ANY(data, void *, BMFace *, BMEdge *, BMVert *, BMLoop *, BMElem *)
|
||||
|
||||
#define BM_iter_new(iter, bm, itype, data) \
|
||||
(BM_ITER_CHECK_TYPE_DATA(data), BM_iter_new(iter, bm, itype, data))
|
||||
#define BM_iter_init(iter, bm, itype, data) \
|
||||
(BM_ITER_CHECK_TYPE_DATA(data), BM_iter_init(iter, bm, itype, data))
|
||||
@@ -0,0 +1,205 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* BMesh inline iterator functions.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_compiler_compat.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* inline here optimizes out the switch statement when called with
|
||||
* constant values (which is very common), nicer for loop-in-loop situations */
|
||||
|
||||
/**
|
||||
* \brief Iterator Step
|
||||
*
|
||||
* Calls an iterators step function to return the next element.
|
||||
*/
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE void *BM_iter_step(BMIter *iter)
|
||||
{
|
||||
return iter->step(iter);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Iterator Init
|
||||
*
|
||||
* Takes a bmesh iterator structure and fills
|
||||
* it with the appropriate function pointers based
|
||||
* upon its type.
|
||||
*/
|
||||
ATTR_NONNULL(1) BLI_INLINE bool BM_iter_init(BMIter *iter, BMesh *bm, const char itype, void *data)
|
||||
{
|
||||
// int argtype;
|
||||
iter->itype = itype;
|
||||
|
||||
/* inlining optimizes out this switch when called with the defined type */
|
||||
switch (BMIterType(itype)) {
|
||||
case BM_VERTS_OF_MESH:
|
||||
BLI_assert(bm != nullptr);
|
||||
BLI_assert(data == nullptr);
|
||||
iter->begin = reinterpret_cast<BMIter__begin_cb>(bmiter__elem_of_mesh_begin);
|
||||
iter->step = reinterpret_cast<BMIter__step_cb>(bmiter__elem_of_mesh_step);
|
||||
iter->data.elem_of_mesh.pooliter.pool = bm->vpool;
|
||||
break;
|
||||
case BM_EDGES_OF_MESH:
|
||||
BLI_assert(bm != nullptr);
|
||||
BLI_assert(data == nullptr);
|
||||
iter->begin = reinterpret_cast<BMIter__begin_cb>(bmiter__elem_of_mesh_begin);
|
||||
iter->step = reinterpret_cast<BMIter__step_cb>(bmiter__elem_of_mesh_step);
|
||||
iter->data.elem_of_mesh.pooliter.pool = bm->epool;
|
||||
break;
|
||||
case BM_FACES_OF_MESH:
|
||||
BLI_assert(bm != nullptr);
|
||||
BLI_assert(data == nullptr);
|
||||
iter->begin = reinterpret_cast<BMIter__begin_cb>(bmiter__elem_of_mesh_begin);
|
||||
iter->step = reinterpret_cast<BMIter__step_cb>(bmiter__elem_of_mesh_step);
|
||||
iter->data.elem_of_mesh.pooliter.pool = bm->fpool;
|
||||
break;
|
||||
case BM_EDGES_OF_VERT:
|
||||
BLI_assert(data != nullptr);
|
||||
BLI_assert(((BMElem *)data)->head.htype == BM_VERT);
|
||||
iter->begin = reinterpret_cast<BMIter__begin_cb>(bmiter__edge_of_vert_begin);
|
||||
iter->step = reinterpret_cast<BMIter__step_cb>(bmiter__edge_of_vert_step);
|
||||
iter->data.edge_of_vert.vdata = static_cast<BMVert *>(data);
|
||||
break;
|
||||
case BM_FACES_OF_VERT:
|
||||
BLI_assert(data != nullptr);
|
||||
BLI_assert(((BMElem *)data)->head.htype == BM_VERT);
|
||||
iter->begin = reinterpret_cast<BMIter__begin_cb>(bmiter__face_of_vert_begin);
|
||||
iter->step = reinterpret_cast<BMIter__step_cb>(bmiter__face_of_vert_step);
|
||||
iter->data.face_of_vert.vdata = static_cast<BMVert *>(data);
|
||||
break;
|
||||
case BM_LOOPS_OF_VERT:
|
||||
BLI_assert(data != nullptr);
|
||||
BLI_assert(((BMElem *)data)->head.htype == BM_VERT);
|
||||
iter->begin = reinterpret_cast<BMIter__begin_cb>(bmiter__loop_of_vert_begin);
|
||||
iter->step = reinterpret_cast<BMIter__step_cb>(bmiter__loop_of_vert_step);
|
||||
iter->data.loop_of_vert.vdata = static_cast<BMVert *>(data);
|
||||
break;
|
||||
case BM_VERTS_OF_EDGE:
|
||||
BLI_assert(data != nullptr);
|
||||
BLI_assert(((BMElem *)data)->head.htype == BM_EDGE);
|
||||
iter->begin = reinterpret_cast<BMIter__begin_cb>(bmiter__vert_of_edge_begin);
|
||||
iter->step = reinterpret_cast<BMIter__step_cb>(bmiter__vert_of_edge_step);
|
||||
iter->data.vert_of_edge.edata = static_cast<BMEdge *>(data);
|
||||
break;
|
||||
case BM_FACES_OF_EDGE:
|
||||
BLI_assert(data != nullptr);
|
||||
BLI_assert(((BMElem *)data)->head.htype == BM_EDGE);
|
||||
iter->begin = reinterpret_cast<BMIter__begin_cb>(bmiter__face_of_edge_begin);
|
||||
iter->step = reinterpret_cast<BMIter__step_cb>(bmiter__face_of_edge_step);
|
||||
iter->data.face_of_edge.edata = static_cast<BMEdge *>(data);
|
||||
break;
|
||||
case BM_VERTS_OF_FACE:
|
||||
BLI_assert(data != nullptr);
|
||||
BLI_assert(((BMElem *)data)->head.htype == BM_FACE);
|
||||
iter->begin = reinterpret_cast<BMIter__begin_cb>(bmiter__vert_of_face_begin);
|
||||
iter->step = reinterpret_cast<BMIter__step_cb>(bmiter__vert_of_face_step);
|
||||
iter->data.vert_of_face.pdata = static_cast<BMFace *>(data);
|
||||
break;
|
||||
case BM_EDGES_OF_FACE:
|
||||
BLI_assert(data != nullptr);
|
||||
BLI_assert(((BMElem *)data)->head.htype == BM_FACE);
|
||||
iter->begin = reinterpret_cast<BMIter__begin_cb>(bmiter__edge_of_face_begin);
|
||||
iter->step = reinterpret_cast<BMIter__step_cb>(bmiter__edge_of_face_step);
|
||||
iter->data.edge_of_face.pdata = static_cast<BMFace *>(data);
|
||||
break;
|
||||
case BM_LOOPS_OF_FACE:
|
||||
BLI_assert(data != nullptr);
|
||||
BLI_assert(((BMElem *)data)->head.htype == BM_FACE);
|
||||
iter->begin = reinterpret_cast<BMIter__begin_cb>(bmiter__loop_of_face_begin);
|
||||
iter->step = reinterpret_cast<BMIter__step_cb>(bmiter__loop_of_face_step);
|
||||
iter->data.loop_of_face.pdata = static_cast<BMFace *>(data);
|
||||
break;
|
||||
case BM_LOOPS_OF_LOOP:
|
||||
BLI_assert(data != nullptr);
|
||||
BLI_assert(((BMElem *)data)->head.htype == BM_LOOP);
|
||||
iter->begin = reinterpret_cast<BMIter__begin_cb>(bmiter__loop_of_loop_begin);
|
||||
iter->step = reinterpret_cast<BMIter__step_cb>(bmiter__loop_of_loop_step);
|
||||
iter->data.loop_of_loop.ldata = static_cast<BMLoop *>(data);
|
||||
break;
|
||||
case BM_LOOPS_OF_EDGE:
|
||||
BLI_assert(data != nullptr);
|
||||
BLI_assert(((BMElem *)data)->head.htype == BM_EDGE);
|
||||
iter->begin = reinterpret_cast<BMIter__begin_cb>(bmiter__loop_of_edge_begin);
|
||||
iter->step = reinterpret_cast<BMIter__step_cb>(bmiter__loop_of_edge_step);
|
||||
iter->data.loop_of_edge.edata = static_cast<BMEdge *>(data);
|
||||
break;
|
||||
default:
|
||||
/* should never happen */
|
||||
BLI_assert(0);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
iter->begin(iter);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Iterator New
|
||||
*
|
||||
* Takes a bmesh iterator structure and fills
|
||||
* it with the appropriate function pointers based
|
||||
* upon its type and then calls BMeshIter_step()
|
||||
* to return the first element of the iterator.
|
||||
*/
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE
|
||||
void *BM_iter_new(BMIter *iter, BMesh *bm, const char itype, void *data)
|
||||
{
|
||||
if (LIKELY(BM_iter_init(iter, bm, itype, data))) {
|
||||
return BM_iter_step(iter);
|
||||
}
|
||||
else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Parallel (threaded) iterator,
|
||||
* only available for most basic iteration-types (verts/edges/faces of mesh).
|
||||
*
|
||||
* Uses #BLI_task_parallel_mempool to iterate over all items of underlying matching mempool.
|
||||
*
|
||||
* \note You have to include BLI_task.h before BMesh includes to be able to use this function!
|
||||
*/
|
||||
|
||||
#ifdef __BLI_TASK_H__
|
||||
|
||||
ATTR_NONNULL(1)
|
||||
BLI_INLINE void BM_iter_parallel(BMesh *bm,
|
||||
const char itype,
|
||||
TaskParallelMempoolFunc func,
|
||||
void *userdata,
|
||||
const TaskParallelSettings *settings)
|
||||
{
|
||||
/* inlining optimizes out this switch when called with the defined type */
|
||||
switch (BMIterType(itype)) {
|
||||
case BM_VERTS_OF_MESH:
|
||||
BLI_task_parallel_mempool(bm->vpool, userdata, func, settings);
|
||||
break;
|
||||
case BM_EDGES_OF_MESH:
|
||||
BLI_task_parallel_mempool(bm->epool, userdata, func, settings);
|
||||
break;
|
||||
case BM_FACES_OF_MESH:
|
||||
BLI_task_parallel_mempool(bm->fpool, userdata, func, settings);
|
||||
break;
|
||||
default:
|
||||
/* should never happen */
|
||||
BLI_assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __BLI_TASK_H__ */
|
||||
|
||||
} // namespace blender
|
||||
890
blender-5.2.0/source/blender/bmesh/intern/bmesh_log.cc
Normal file
890
blender-5.2.0/source/blender/bmesh/intern/bmesh_log.cc
Normal file
@@ -0,0 +1,890 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* The BMLog is an interface for storing undo/redo steps as a BMesh is
|
||||
* modified. It only stores changes to the BMesh, not full copies.
|
||||
*
|
||||
* Currently it supports the following types of changes:
|
||||
*
|
||||
* - Adding and removing vertices
|
||||
* - Adding and removing faces
|
||||
* - Moving vertices
|
||||
* - Setting vertex paint-mask values
|
||||
* - Setting vertex hflags
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_pool.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_customdata.hh"
|
||||
|
||||
#include "bmesh.hh"
|
||||
#include "bmesh_log.hh"
|
||||
|
||||
#include "range_tree.h"
|
||||
|
||||
#include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMLogFace;
|
||||
struct BMLogVert;
|
||||
|
||||
struct BMLogEntry {
|
||||
BMLogEntry *next, *prev;
|
||||
|
||||
/* The following members map from an element ID to one of the log types above. */
|
||||
|
||||
/** Elements that were in the previous entry, but have been deleted. */
|
||||
Map<uint, BMLogVert *, 0> deleted_verts;
|
||||
Map<uint, BMLogFace *, 0> deleted_faces;
|
||||
|
||||
/** Elements that were not in the previous entry, but are in the result of this entry. */
|
||||
Map<uint, BMLogVert *, 0> added_verts;
|
||||
Map<uint, BMLogFace *, 0> added_faces;
|
||||
|
||||
/** Vertices whose coordinates, mask value, or hflag have changed. */
|
||||
Map<uint, BMLogVert *, 0> modified_verts;
|
||||
Map<uint, BMLogFace *, 0> modified_faces;
|
||||
|
||||
Pool<BMLogVert> vert_pool;
|
||||
Pool<BMLogFace> face_pool;
|
||||
|
||||
Vector<BMLogVert *, 0> allocated_verts;
|
||||
Vector<BMLogFace *, 0> allocated_faces;
|
||||
|
||||
/**
|
||||
* This is only needed for dropping BMLogEntries while still in
|
||||
* dynamic-topology mode, as that should release vert/face IDs
|
||||
* back to the BMLog but no BMLog pointer is available at that time.
|
||||
*
|
||||
* This field is not guaranteed to be valid, any use of it should
|
||||
* check for nullptr.
|
||||
*/
|
||||
BMLog *log;
|
||||
};
|
||||
|
||||
struct BMLog {
|
||||
/** Tree of free IDs */
|
||||
RangeTreeUInt *unused_ids;
|
||||
|
||||
/**
|
||||
* Mapping from unique IDs to vertices and faces
|
||||
*
|
||||
* Each vertex and face in the log gets a unique `uint`
|
||||
* assigned. That ID is taken from the set managed by the
|
||||
* unused_ids range tree.
|
||||
*
|
||||
* The ID is needed because element pointers will change as they
|
||||
* are created and deleted.
|
||||
*/
|
||||
Map<uint, BMElem *, 0> id_to_elem;
|
||||
Map<BMElem *, uint, 0> elem_to_id;
|
||||
|
||||
/** All #BMLogEntrys, ordered from earliest to most recent. */
|
||||
ListBaseT<BMLogEntry> entries;
|
||||
|
||||
/**
|
||||
* The current log entry from entries list
|
||||
*
|
||||
* If null, then the original mesh from before any of the log
|
||||
* entries is current (i.e. there is nothing left to undo.)
|
||||
*
|
||||
* If equal to the last entry in the entries list, then all log
|
||||
* entries have been applied (i.e. there is nothing left to redo.)
|
||||
*/
|
||||
BMLogEntry *current_entry;
|
||||
};
|
||||
|
||||
struct BMLogVert {
|
||||
float3 position;
|
||||
float3 normal;
|
||||
char hflag;
|
||||
float mask;
|
||||
};
|
||||
|
||||
struct BMLogFace {
|
||||
std::array<uint, 3> v_ids;
|
||||
char hflag;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Get/Set Element IDs
|
||||
* \{ */
|
||||
|
||||
/* Get the vertex's unique ID from the log */
|
||||
static uint bm_log_vert_id_get(BMLog *log, BMVert *v)
|
||||
{
|
||||
return log->elem_to_id.lookup(reinterpret_cast<BMElem *>(v));
|
||||
}
|
||||
|
||||
/* Set the vertex's unique ID in the log */
|
||||
static void bm_log_vert_id_set(BMLog *log, BMVert *v, const uint id)
|
||||
{
|
||||
log->id_to_elem.add_overwrite(id, reinterpret_cast<BMElem *>(v));
|
||||
log->elem_to_id.add_overwrite(reinterpret_cast<BMElem *>(v), id);
|
||||
}
|
||||
|
||||
/* Get a vertex from its unique ID */
|
||||
static BMVert *bm_log_vert_from_id(BMLog *log, const uint id)
|
||||
{
|
||||
return reinterpret_cast<BMVert *>(log->id_to_elem.lookup(id));
|
||||
}
|
||||
|
||||
/* Get the face's unique ID from the log */
|
||||
static uint bm_log_face_id_get(BMLog *log, BMFace *f)
|
||||
{
|
||||
return log->elem_to_id.lookup(reinterpret_cast<BMElem *>(f));
|
||||
}
|
||||
|
||||
/* Set the face's unique ID in the log */
|
||||
static void bm_log_face_id_set(BMLog *log, BMFace *f, const uint id)
|
||||
{
|
||||
log->id_to_elem.add_overwrite(id, reinterpret_cast<BMElem *>(f));
|
||||
log->elem_to_id.add_overwrite(reinterpret_cast<BMElem *>(f), id);
|
||||
}
|
||||
|
||||
/* Get a face from its unique ID */
|
||||
static BMFace *bm_log_face_from_id(BMLog *log, const uint id)
|
||||
{
|
||||
return reinterpret_cast<BMFace *>(log->id_to_elem.lookup(id));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name BMLogVert / BMLogFace
|
||||
* \{ */
|
||||
|
||||
/* Get a vertex's paint-mask value
|
||||
*
|
||||
* Returns zero if no paint-mask layer is present */
|
||||
static float vert_mask_get(BMVert *v, const int cd_vert_mask_offset)
|
||||
{
|
||||
if (cd_vert_mask_offset != -1) {
|
||||
return BM_ELEM_CD_GET_FLOAT(v, cd_vert_mask_offset);
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/* Set a vertex's paint-mask value
|
||||
*
|
||||
* Has no effect is no paint-mask layer is present */
|
||||
static void vert_mask_set(BMVert *v, const float new_mask, const int cd_vert_mask_offset)
|
||||
{
|
||||
if (cd_vert_mask_offset != -1) {
|
||||
BM_ELEM_CD_SET_FLOAT(v, cd_vert_mask_offset, new_mask);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update a BMLogVert with data from a BMVert */
|
||||
static void bm_log_vert_bmvert_copy(BMLogVert *lv, BMVert *v, const int cd_vert_mask_offset)
|
||||
{
|
||||
copy_v3_v3(lv->position, v->co);
|
||||
copy_v3_v3(lv->normal, v->no);
|
||||
lv->mask = vert_mask_get(v, cd_vert_mask_offset);
|
||||
lv->hflag = v->head.hflag;
|
||||
}
|
||||
|
||||
/* Allocate and initialize a BMLogVert */
|
||||
static BMLogVert *bm_log_vert_alloc(BMLog *log, BMVert *v, const int cd_vert_mask_offset)
|
||||
{
|
||||
BMLogEntry *entry = log->current_entry;
|
||||
BMLogVert *lv = &entry->vert_pool.construct();
|
||||
entry->allocated_verts.append(lv);
|
||||
|
||||
bm_log_vert_bmvert_copy(lv, v, cd_vert_mask_offset);
|
||||
|
||||
return lv;
|
||||
}
|
||||
|
||||
/* Allocate and initialize a BMLogFace */
|
||||
static BMLogFace *bm_log_face_alloc(BMLog *log, BMFace *f)
|
||||
{
|
||||
BMLogEntry *entry = log->current_entry;
|
||||
BMLogFace *lf = &entry->face_pool.construct();
|
||||
entry->allocated_faces.append(lf);
|
||||
BMVert *v[3];
|
||||
|
||||
BLI_assert(f->len == 3);
|
||||
|
||||
// BM_iter_as_array(nullptr, BM_VERTS_OF_FACE, f, (void **)v, 3);
|
||||
BM_face_as_array_vert_tri(f, v);
|
||||
|
||||
lf->v_ids[0] = bm_log_vert_id_get(log, v[0]);
|
||||
lf->v_ids[1] = bm_log_vert_id_get(log, v[1]);
|
||||
lf->v_ids[2] = bm_log_vert_id_get(log, v[2]);
|
||||
|
||||
lf->hflag = f->head.hflag;
|
||||
return lf;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Helpers for Undo/Redo
|
||||
* \{ */
|
||||
|
||||
static void bm_log_verts_unmake(BMesh *bm, BMLog *log, const Map<uint, BMLogVert *, 0> &verts)
|
||||
{
|
||||
const int cd_vert_mask_offset = CustomData_get_offset_named(
|
||||
&bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
|
||||
|
||||
for (const auto item : verts.items()) {
|
||||
BMVert *v = bm_log_vert_from_id(log, item.key);
|
||||
|
||||
/* Ensure the log has the final values of the vertex before
|
||||
* deleting it */
|
||||
bm_log_vert_bmvert_copy(item.value, v, cd_vert_mask_offset);
|
||||
|
||||
BM_vert_kill(bm, v);
|
||||
}
|
||||
}
|
||||
|
||||
static void bm_log_faces_unmake(BMesh *bm, BMLog *log, const Map<uint, BMLogFace *, 0> &faces)
|
||||
{
|
||||
for (const uint id : faces.keys()) {
|
||||
BMFace *f = bm_log_face_from_id(log, id);
|
||||
std::array<BMEdge *, 3> e_tri;
|
||||
|
||||
BMLoop *l_iter = BM_FACE_FIRST_LOOP(f);
|
||||
for (uint i = 0; i < e_tri.size(); i++, l_iter = l_iter->next) {
|
||||
e_tri[i] = l_iter->e;
|
||||
}
|
||||
|
||||
/* Remove any unused edges */
|
||||
BM_face_kill(bm, f);
|
||||
for (uint i = 0; i < e_tri.size(); i++) {
|
||||
if (BM_edge_is_wire(e_tri[i])) {
|
||||
BM_edge_kill(bm, e_tri[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bm_log_verts_restore(BMesh *bm, BMLog *log, const Map<uint, BMLogVert *, 0> &verts)
|
||||
{
|
||||
const int cd_vert_mask_offset = CustomData_get_offset_named(
|
||||
&bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
|
||||
|
||||
for (const auto item : verts.items()) {
|
||||
BMLogVert *lv = item.value;
|
||||
BMVert *v = BM_vert_create(bm, lv->position, nullptr, BM_CREATE_NOP);
|
||||
vert_mask_set(v, lv->mask, cd_vert_mask_offset);
|
||||
v->head.hflag = lv->hflag;
|
||||
copy_v3_v3(v->no, lv->normal);
|
||||
bm_log_vert_id_set(log, v, item.key);
|
||||
}
|
||||
}
|
||||
|
||||
static void bm_log_faces_restore(BMesh *bm, BMLog *log, const Map<uint, BMLogFace *, 0> &faces)
|
||||
{
|
||||
const int cd_face_sets = CustomData_get_offset_named(
|
||||
&bm->pdata, CD_PROP_INT32, ".sculpt_face_set");
|
||||
|
||||
for (const auto item : faces.items()) {
|
||||
BMLogFace *lf = item.value;
|
||||
BMVert *v[3] = {
|
||||
bm_log_vert_from_id(log, lf->v_ids[0]),
|
||||
bm_log_vert_from_id(log, lf->v_ids[1]),
|
||||
bm_log_vert_from_id(log, lf->v_ids[2]),
|
||||
};
|
||||
|
||||
BMFace *f = BM_face_create_verts(bm, v, 3, nullptr, BM_CREATE_NOP, true);
|
||||
f->head.hflag = lf->hflag;
|
||||
bm_log_face_id_set(log, f, item.key);
|
||||
|
||||
/* Ensure face sets have valid values. Fixes #80174. */
|
||||
if (cd_face_sets != -1) {
|
||||
BM_ELEM_CD_SET_INT(f, cd_face_sets, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bm_log_vert_values_swap(BMesh *bm, BMLog *log, const Map<uint, BMLogVert *, 0> &verts)
|
||||
{
|
||||
const int cd_vert_mask_offset = CustomData_get_offset_named(
|
||||
&bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
|
||||
|
||||
for (const auto item : verts.items()) {
|
||||
BMLogVert *lv = item.value;
|
||||
BMVert *v = bm_log_vert_from_id(log, item.key);
|
||||
|
||||
swap_v3_v3(v->co, lv->position);
|
||||
swap_v3_v3(v->no, lv->normal);
|
||||
std::swap(v->head.hflag, lv->hflag);
|
||||
float mask = lv->mask;
|
||||
lv->mask = vert_mask_get(v, cd_vert_mask_offset);
|
||||
vert_mask_set(v, mask, cd_vert_mask_offset);
|
||||
}
|
||||
}
|
||||
|
||||
static void bm_log_face_values_swap(BMLog *log, const Map<uint, BMLogFace *, 0> &faces)
|
||||
{
|
||||
|
||||
for (const auto item : faces.items()) {
|
||||
BMLogFace *lf = item.value;
|
||||
BMFace *f = bm_log_face_from_id(log, item.key);
|
||||
|
||||
std::swap(f->head.hflag, lf->hflag);
|
||||
}
|
||||
}
|
||||
|
||||
/* Assign unique IDs to all vertices and faces already in the BMesh */
|
||||
static void bm_log_assign_ids(BMesh *bm, BMLog *log)
|
||||
{
|
||||
BMIter iter;
|
||||
|
||||
BMVert *v;
|
||||
/* Generate vertex IDs */
|
||||
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
uint id = range_tree_uint_take_any(log->unused_ids);
|
||||
bm_log_vert_id_set(log, v, id);
|
||||
}
|
||||
|
||||
BMFace *f;
|
||||
/* Generate face IDs */
|
||||
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
|
||||
uint id = range_tree_uint_take_any(log->unused_ids);
|
||||
bm_log_face_id_set(log, f, id);
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate an empty log entry */
|
||||
static BMLogEntry *bm_log_entry_create()
|
||||
{
|
||||
BMLogEntry *entry = MEM_new<BMLogEntry>(__func__);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/* Free the data in a log entry
|
||||
*
|
||||
* NOTE: does not free the log entry itself. */
|
||||
static void bm_log_entry_free(BMLogEntry *entry)
|
||||
{
|
||||
BLI_assert(entry->vert_pool.size() == entry->allocated_verts.size());
|
||||
BLI_assert(entry->face_pool.size() == entry->allocated_faces.size());
|
||||
|
||||
for (BMLogVert *log_vert : entry->allocated_verts) {
|
||||
entry->vert_pool.destruct(*log_vert);
|
||||
}
|
||||
|
||||
for (BMLogFace *log_face : entry->allocated_faces) {
|
||||
entry->face_pool.destruct(*log_face);
|
||||
}
|
||||
|
||||
BLI_assert(entry->vert_pool.is_empty());
|
||||
BLI_assert(entry->face_pool.is_empty());
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public API
|
||||
* \{ */
|
||||
|
||||
BMLog *BM_log_create(BMesh *bm)
|
||||
{
|
||||
BMLog *log = MEM_new<BMLog>(__func__);
|
||||
const uint reserve_num = uint(bm->totvert + bm->totface);
|
||||
|
||||
log->unused_ids = range_tree_uint_alloc(0, uint(-1));
|
||||
log->id_to_elem.reserve(reserve_num);
|
||||
log->elem_to_id.reserve(reserve_num);
|
||||
|
||||
/* Assign IDs to all existing vertices and faces */
|
||||
bm_log_assign_ids(bm, log);
|
||||
|
||||
return log;
|
||||
}
|
||||
|
||||
void BM_log_cleanup_entry(BMLogEntry *entry)
|
||||
{
|
||||
BMLog *log = entry->log;
|
||||
|
||||
if (log) {
|
||||
/* Take all used IDs */
|
||||
for (const uint id : entry->deleted_verts.keys()) {
|
||||
range_tree_uint_retake(log->unused_ids, id);
|
||||
}
|
||||
for (const uint id : entry->deleted_faces.keys()) {
|
||||
range_tree_uint_retake(log->unused_ids, id);
|
||||
}
|
||||
for (const uint id : entry->added_verts.keys()) {
|
||||
range_tree_uint_retake(log->unused_ids, id);
|
||||
}
|
||||
for (const uint id : entry->added_faces.keys()) {
|
||||
range_tree_uint_retake(log->unused_ids, id);
|
||||
}
|
||||
for (const uint id : entry->modified_verts.keys()) {
|
||||
range_tree_uint_retake(log->unused_ids, id);
|
||||
}
|
||||
for (const uint id : entry->modified_faces.keys()) {
|
||||
range_tree_uint_retake(log->unused_ids, id);
|
||||
}
|
||||
|
||||
/* delete entries to avoid releasing ids in node cleanup */
|
||||
entry->deleted_verts.clear();
|
||||
entry->deleted_faces.clear();
|
||||
entry->added_verts.clear();
|
||||
entry->added_faces.clear();
|
||||
entry->modified_verts.clear();
|
||||
|
||||
/* Is this last one needed? */
|
||||
entry->modified_faces.clear();
|
||||
}
|
||||
}
|
||||
|
||||
BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry)
|
||||
{
|
||||
BMLog *log = BM_log_create(bm);
|
||||
|
||||
if (entry->prev) {
|
||||
log->current_entry = entry;
|
||||
}
|
||||
else {
|
||||
log->current_entry = nullptr;
|
||||
}
|
||||
|
||||
/* Let BMLog manage the entry list again */
|
||||
log->entries.first = log->entries.last = entry;
|
||||
|
||||
{
|
||||
while (entry->prev) {
|
||||
entry = entry->prev;
|
||||
log->entries.first = entry;
|
||||
}
|
||||
entry = static_cast<BMLogEntry *>(log->entries.last);
|
||||
while (entry->next) {
|
||||
entry = entry->next;
|
||||
log->entries.last = entry;
|
||||
}
|
||||
}
|
||||
|
||||
for (entry = static_cast<BMLogEntry *>(log->entries.first); entry; entry = entry->next) {
|
||||
entry->log = log;
|
||||
|
||||
/* Take all used IDs */
|
||||
for (const uint id : entry->deleted_verts.keys()) {
|
||||
range_tree_uint_retake(log->unused_ids, id);
|
||||
}
|
||||
for (const uint id : entry->deleted_faces.keys()) {
|
||||
range_tree_uint_retake(log->unused_ids, id);
|
||||
}
|
||||
for (const uint id : entry->added_verts.keys()) {
|
||||
range_tree_uint_retake(log->unused_ids, id);
|
||||
}
|
||||
for (const uint id : entry->added_faces.keys()) {
|
||||
range_tree_uint_retake(log->unused_ids, id);
|
||||
}
|
||||
for (const uint id : entry->modified_verts.keys()) {
|
||||
range_tree_uint_retake(log->unused_ids, id);
|
||||
}
|
||||
for (const uint id : entry->modified_faces.keys()) {
|
||||
range_tree_uint_retake(log->unused_ids, id);
|
||||
}
|
||||
}
|
||||
|
||||
return log;
|
||||
}
|
||||
|
||||
void BM_log_free(BMLog *log)
|
||||
{
|
||||
if (log->unused_ids) {
|
||||
range_tree_uint_free(log->unused_ids);
|
||||
}
|
||||
|
||||
/* Clear the BMLog references within each entry, but do not free
|
||||
* the entries themselves */
|
||||
for (BMLogEntry &entry : log->entries) {
|
||||
entry.log = nullptr;
|
||||
}
|
||||
|
||||
MEM_delete(log);
|
||||
}
|
||||
|
||||
BMLogEntry *BM_log_entry_add(BMLog *log)
|
||||
{
|
||||
/* WARNING: Deleting any entries after the current one is now handled by the
|
||||
* UndoSystem: BKE_UNDOSYS_TYPE_SCULPT freeing here causes unnecessary complications. */
|
||||
|
||||
/* Create and append the new entry */
|
||||
BMLogEntry *entry = bm_log_entry_create();
|
||||
BLI_addtail(&log->entries, entry);
|
||||
entry->log = log;
|
||||
log->current_entry = entry;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
void BM_log_entry_drop(BMLogEntry *entry)
|
||||
{
|
||||
BMLog *log = entry->log;
|
||||
|
||||
if (!log) {
|
||||
/* Unlink */
|
||||
BLI_assert(!(entry->prev && entry->next));
|
||||
if (entry->prev) {
|
||||
entry->prev->next = nullptr;
|
||||
}
|
||||
else if (entry->next) {
|
||||
entry->next->prev = nullptr;
|
||||
}
|
||||
|
||||
bm_log_entry_free(entry);
|
||||
MEM_delete(entry);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entry->prev) {
|
||||
/* Release IDs of elements that are deleted by this
|
||||
* entry. Since the entry is at the beginning of the undo
|
||||
* stack, and it's being deleted, those elements can never be
|
||||
* restored. Their IDs can go back into the pool. */
|
||||
|
||||
/* This would never happen usually since first entry of log is
|
||||
* usually dyntopo enable, which, when reverted will free the log
|
||||
* completely. However, it is possible have a stroke instead of
|
||||
* dyntopo enable as first entry if nodes have been cleaned up
|
||||
* after sculpting on a different object than A, B.
|
||||
*
|
||||
* The steps are:
|
||||
* A dyntopo enable - sculpt
|
||||
* B dyntopo enable - sculpt - undo (A objects operators get cleaned up)
|
||||
* A sculpt (now A's log has a sculpt operator as first entry)
|
||||
*
|
||||
* Causing a cleanup at this point will call the code below, however
|
||||
* this will invalidate the state of the log since the deleted vertices
|
||||
* have been reclaimed already on step 2 (see BM_log_cleanup_entry)
|
||||
*
|
||||
* Also, design wise, a first entry should not have any deleted vertices since it
|
||||
* should not have anything to delete them -from-
|
||||
*/
|
||||
// bm_log_id_ghash_release(log, entry->deleted_faces);
|
||||
// bm_log_id_ghash_release(log, entry->deleted_verts);
|
||||
}
|
||||
else if (!entry->next) {
|
||||
/* Release IDs of elements that are added by this entry. Since
|
||||
* the entry is at the end of the undo stack, and it's being
|
||||
* deleted, those elements can never be restored. Their IDs
|
||||
* can go back into the pool. */
|
||||
for (const uint id : entry->added_faces.keys()) {
|
||||
range_tree_uint_release(log->unused_ids, id);
|
||||
}
|
||||
for (const uint id : entry->added_verts.keys()) {
|
||||
range_tree_uint_release(log->unused_ids, id);
|
||||
}
|
||||
}
|
||||
else {
|
||||
BLI_assert_msg(0, "Cannot drop BMLogEntry from middle");
|
||||
}
|
||||
|
||||
if (log->current_entry == entry) {
|
||||
log->current_entry = entry->prev;
|
||||
}
|
||||
|
||||
bm_log_entry_free(entry);
|
||||
BLI_remlink(&log->entries, entry);
|
||||
MEM_delete(entry);
|
||||
}
|
||||
|
||||
void BM_log_undo(BMesh *bm, BMLog *log)
|
||||
{
|
||||
BMLogEntry *entry = log->current_entry;
|
||||
|
||||
if (entry) {
|
||||
log->current_entry = entry->prev;
|
||||
|
||||
/* Delete added faces and verts */
|
||||
bm_log_faces_unmake(bm, log, entry->added_faces);
|
||||
bm_log_verts_unmake(bm, log, entry->added_verts);
|
||||
|
||||
/* Restore deleted verts and faces */
|
||||
bm_log_verts_restore(bm, log, entry->deleted_verts);
|
||||
bm_log_faces_restore(bm, log, entry->deleted_faces);
|
||||
|
||||
/* Restore vertex coordinates, mask, and hflag */
|
||||
bm_log_vert_values_swap(bm, log, entry->modified_verts);
|
||||
bm_log_face_values_swap(log, entry->modified_faces);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_log_redo(BMesh *bm, BMLog *log)
|
||||
{
|
||||
BMLogEntry *entry = log->current_entry;
|
||||
|
||||
if (!entry) {
|
||||
/* Currently at the beginning of the undo stack, move to first entry */
|
||||
entry = static_cast<BMLogEntry *>(log->entries.first);
|
||||
}
|
||||
else if (entry->next) {
|
||||
/* Move to next undo entry */
|
||||
entry = entry->next;
|
||||
}
|
||||
else {
|
||||
/* Currently at the end of the undo stack, nothing left to redo */
|
||||
return;
|
||||
}
|
||||
|
||||
log->current_entry = entry;
|
||||
|
||||
if (entry) {
|
||||
/* Re-delete previously deleted faces and verts */
|
||||
bm_log_faces_unmake(bm, log, entry->deleted_faces);
|
||||
bm_log_verts_unmake(bm, log, entry->deleted_verts);
|
||||
|
||||
/* Restore previously added verts and faces */
|
||||
bm_log_verts_restore(bm, log, entry->added_verts);
|
||||
bm_log_faces_restore(bm, log, entry->added_faces);
|
||||
|
||||
/* Restore vertex coordinates, mask, and hflag */
|
||||
bm_log_vert_values_swap(bm, log, entry->modified_verts);
|
||||
bm_log_face_values_swap(log, entry->modified_faces);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_log_vert_before_modified(BMLog *log, BMVert *v, const int cd_vert_mask_offset)
|
||||
{
|
||||
BMLogEntry *entry = log->current_entry;
|
||||
const uint v_id = bm_log_vert_id_get(log, v);
|
||||
|
||||
/* Find or create the BMLogVert entry */
|
||||
if (entry->added_verts.contains(v_id)) {
|
||||
bm_log_vert_bmvert_copy(entry->added_verts.lookup(v_id), v, cd_vert_mask_offset);
|
||||
}
|
||||
else {
|
||||
entry->modified_verts.lookup_or_add_cb(
|
||||
v_id, [&] { return bm_log_vert_alloc(log, v, cd_vert_mask_offset); });
|
||||
}
|
||||
}
|
||||
|
||||
void BM_log_vert_added(BMLog *log, BMVert *v, const int cd_vert_mask_offset)
|
||||
{
|
||||
const uint v_id = range_tree_uint_take_any(log->unused_ids);
|
||||
|
||||
bm_log_vert_id_set(log, v, v_id);
|
||||
BMLogVert *lv = bm_log_vert_alloc(log, v, cd_vert_mask_offset);
|
||||
log->current_entry->added_verts.add(v_id, lv);
|
||||
}
|
||||
|
||||
void BM_log_face_modified(BMLog *log, BMFace *f)
|
||||
{
|
||||
const uint f_id = bm_log_face_id_get(log, f);
|
||||
|
||||
BMLogFace *lf = bm_log_face_alloc(log, f);
|
||||
log->current_entry->modified_faces.add(f_id, lf);
|
||||
}
|
||||
|
||||
void BM_log_face_added(BMLog *log, BMFace *f)
|
||||
{
|
||||
const uint f_id = range_tree_uint_take_any(log->unused_ids);
|
||||
|
||||
/* Only triangles are supported for now */
|
||||
BLI_assert(f->len == 3);
|
||||
|
||||
bm_log_face_id_set(log, f, f_id);
|
||||
BMLogFace *lf = bm_log_face_alloc(log, f);
|
||||
log->current_entry->added_faces.add(f_id, lf);
|
||||
}
|
||||
|
||||
void BM_log_vert_removed(BMLog *log, BMVert *v, const int cd_vert_mask_offset)
|
||||
{
|
||||
BMLogEntry *entry = log->current_entry;
|
||||
const uint v_id = bm_log_vert_id_get(log, v);
|
||||
|
||||
BLI_assert(!entry->added_verts.contains(v_id) ||
|
||||
(entry->added_verts.contains(v_id) && entry->added_verts.lookup(v_id) != nullptr));
|
||||
|
||||
if (entry->added_verts.remove(v_id)) {
|
||||
range_tree_uint_release(log->unused_ids, v_id);
|
||||
}
|
||||
else {
|
||||
BMLogVert *lv = bm_log_vert_alloc(log, v, cd_vert_mask_offset);
|
||||
entry->deleted_verts.add(v_id, lv);
|
||||
|
||||
/* If the vertex was modified before deletion, ensure that the
|
||||
* original vertex values are stored */
|
||||
if (std::optional<BMLogVert *> lv_mod = entry->modified_verts.lookup_try(v_id)) {
|
||||
*lv = *lv_mod.value();
|
||||
entry->modified_verts.remove(v_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BM_log_face_removed(BMLog *log, BMFace *f)
|
||||
{
|
||||
BMLogEntry *entry = log->current_entry;
|
||||
const uint f_id = bm_log_face_id_get(log, f);
|
||||
|
||||
BLI_assert(!entry->added_faces.contains(f_id) ||
|
||||
(entry->added_faces.contains(f_id) && entry->added_faces.lookup(f_id) != nullptr));
|
||||
|
||||
if (entry->added_faces.remove(f_id)) {
|
||||
range_tree_uint_release(log->unused_ids, f_id);
|
||||
}
|
||||
else {
|
||||
BMLogFace *lf = bm_log_face_alloc(log, f);
|
||||
entry->deleted_faces.add(f_id, lf);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_log_all_added(BMesh *bm, BMLog *log)
|
||||
{
|
||||
const int cd_vert_mask_offset = CustomData_get_offset_named(
|
||||
&bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
|
||||
|
||||
/* avoid unnecessary resizing on initialization */
|
||||
if (log->current_entry->added_verts.is_empty()) {
|
||||
log->current_entry->added_verts.reserve(bm->totvert);
|
||||
}
|
||||
|
||||
if (log->current_entry->added_faces.is_empty()) {
|
||||
log->current_entry->added_faces.reserve(bm->totface);
|
||||
}
|
||||
|
||||
BMIter bm_iter;
|
||||
BMVert *v;
|
||||
/* Log all vertices as newly created */
|
||||
BM_ITER_MESH (v, &bm_iter, bm, BM_VERTS_OF_MESH) {
|
||||
BM_log_vert_added(log, v, cd_vert_mask_offset);
|
||||
}
|
||||
|
||||
BMFace *f;
|
||||
/* Log all faces as newly created */
|
||||
BM_ITER_MESH (f, &bm_iter, bm, BM_FACES_OF_MESH) {
|
||||
BM_log_face_added(log, f);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_log_before_all_removed(BMesh *bm, BMLog *log)
|
||||
{
|
||||
const int cd_vert_mask_offset = CustomData_get_offset_named(
|
||||
&bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
|
||||
|
||||
BMIter bm_iter;
|
||||
BMFace *f;
|
||||
/* Log deletion of all faces */
|
||||
BM_ITER_MESH (f, &bm_iter, bm, BM_FACES_OF_MESH) {
|
||||
BM_log_face_removed(log, f);
|
||||
}
|
||||
|
||||
BMVert *v;
|
||||
/* Log deletion of all vertices */
|
||||
BM_ITER_MESH (v, &bm_iter, bm, BM_VERTS_OF_MESH) {
|
||||
BM_log_vert_removed(log, v, cd_vert_mask_offset);
|
||||
}
|
||||
}
|
||||
|
||||
const float *BM_log_find_original_vert_co(BMLog *log, BMVert *v)
|
||||
{
|
||||
BMLogEntry *entry = log->current_entry;
|
||||
const uint v_id = bm_log_vert_id_get(log, v);
|
||||
|
||||
if (std::optional<BMLogVert *> log_vert = entry->modified_verts.lookup_try(v_id)) {
|
||||
return log_vert.value()->position;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const float *BM_log_find_original_vert_mask(BMLog *log, BMVert *v)
|
||||
{
|
||||
BMLogEntry *entry = log->current_entry;
|
||||
const uint v_id = bm_log_vert_id_get(log, v);
|
||||
|
||||
if (std::optional<BMLogVert *> log_vert = entry->modified_verts.lookup_try(v_id)) {
|
||||
return &log_vert.value()->mask;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BM_log_original_vert_data(BMLog *log, BMVert *v, const float **r_co, const float **r_no)
|
||||
{
|
||||
BMLogEntry *entry = log->current_entry;
|
||||
BLI_assert(entry);
|
||||
|
||||
const uint v_id = bm_log_vert_id_get(log, v);
|
||||
|
||||
BLI_assert(entry->modified_verts.contains(v_id));
|
||||
|
||||
const BMLogVert *lv = entry->modified_verts.lookup(v_id);
|
||||
*r_co = lv->position;
|
||||
*r_no = lv->normal;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Debugging and Testing
|
||||
* \{ */
|
||||
|
||||
#ifndef NDEBUG
|
||||
BMLogEntry *BM_log_current_entry(BMLog *log)
|
||||
{
|
||||
return log->current_entry;
|
||||
}
|
||||
|
||||
RangeTreeUInt *BM_log_unused_ids(BMLog *log)
|
||||
{
|
||||
return log->unused_ids;
|
||||
}
|
||||
|
||||
/* Print the list of entries, marking the current one
|
||||
*
|
||||
* Keep around for debugging */
|
||||
void BM_log_print(const BMLog *log, const char *description)
|
||||
{
|
||||
const BMLogEntry *entry;
|
||||
const char *current = " <-- current";
|
||||
int i;
|
||||
|
||||
printf("%s:\n", description);
|
||||
printf(" % 2d: [ initial ]%s\n", 0, (!log->current_entry) ? current : "");
|
||||
for (entry = static_cast<const BMLogEntry *>(log->entries.first), i = 1; entry;
|
||||
entry = entry->next, i++)
|
||||
{
|
||||
printf(" % 2d: [%p]%s\n", i, entry, (entry == log->current_entry) ? current : "");
|
||||
}
|
||||
}
|
||||
|
||||
void BM_log_print_entry(BMesh *bm, BMLogEntry *entry)
|
||||
{
|
||||
if (bm) {
|
||||
printf("BM { totvert=%d totedge=%d totloop=%d faces_num=%d\n",
|
||||
bm->totvert,
|
||||
bm->totedge,
|
||||
bm->totloop,
|
||||
bm->totface);
|
||||
|
||||
if (!bm->totvert) {
|
||||
printf("%s: Warning: empty bmesh\n", __func__);
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("BM { totvert=unknown totedge=unknown totloop=unknown faces_num=unknown\n");
|
||||
}
|
||||
|
||||
printf("v | added: %d, removed: %d, modified: %d\n",
|
||||
int(entry->added_verts.size()),
|
||||
int(entry->deleted_verts.size()),
|
||||
int(entry->modified_verts.size()));
|
||||
printf("f | added: %d, removed: %d, modified: %d\n",
|
||||
int(entry->added_faces.size()),
|
||||
int(entry->deleted_faces.size()),
|
||||
int(entry->modified_faces.size()));
|
||||
printf("}\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
205
blender-5.2.0/source/blender/bmesh/intern/bmesh_log.hh
Normal file
205
blender-5.2.0/source/blender/bmesh/intern/bmesh_log.hh
Normal file
@@ -0,0 +1,205 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
struct RangeTreeUInt;
|
||||
namespace blender {
|
||||
|
||||
struct BMFace;
|
||||
struct BMVert;
|
||||
struct BMesh;
|
||||
struct BMLog;
|
||||
struct BMLogEntry;
|
||||
|
||||
/**
|
||||
* Allocate, initialize, and assign a new BMLog.
|
||||
*/
|
||||
BMLog *BM_log_create(BMesh *bm);
|
||||
|
||||
/**
|
||||
* Allocate and initialize a new #BMLog using existing #BMLogEntries
|
||||
*
|
||||
* The unused IDs field of the log will be initialized by taking all
|
||||
* keys from all Maps in the log entry.
|
||||
*
|
||||
* \param entry: The last entry of the prior BMLog, its `prev` pointer will be followed back to
|
||||
* reconstruct the log.
|
||||
*/
|
||||
BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry);
|
||||
|
||||
/**
|
||||
* Free all the data in a BMLog including the log itself.
|
||||
*/
|
||||
void BM_log_free(BMLog *log);
|
||||
|
||||
/**
|
||||
* Start a new log entry and update the log entry list.
|
||||
*
|
||||
* If the log entry list is empty, or if the current log entry is the
|
||||
* last entry, the new entry is simply appended to the end.
|
||||
*
|
||||
* Finally, the new entry is set as the current log entry.
|
||||
*/
|
||||
BMLogEntry *BM_log_entry_add(BMLog *log);
|
||||
|
||||
/** Mark all used ids as unused for this node */
|
||||
void BM_log_cleanup_entry(BMLogEntry *entry);
|
||||
|
||||
/**
|
||||
* Remove an entry from the log.
|
||||
*
|
||||
* Uses entry->log as the log. If the log is NULL, the entry will be
|
||||
* freed but not removed from any list, nor shall its IDs be released.
|
||||
*
|
||||
* \warning This operation is only valid on the first and last entries in the log. Deleting from
|
||||
* the middle will assert.
|
||||
*/
|
||||
void BM_log_entry_drop(BMLogEntry *entry);
|
||||
|
||||
/**
|
||||
* Undo one #BMLogEntry.
|
||||
*
|
||||
* Has no effect if there's nothing left to undo.
|
||||
*/
|
||||
void BM_log_undo(BMesh *bm, BMLog *log);
|
||||
|
||||
/**
|
||||
* Redo one #BMLogEntry.
|
||||
*
|
||||
* Has no effect if there's nothing left to redo.
|
||||
*/
|
||||
void BM_log_redo(BMesh *bm, BMLog *log);
|
||||
|
||||
/**
|
||||
* Log a vertex before it is modified.
|
||||
*
|
||||
* Before modifying vertex coordinates, masks, or hflags, call this
|
||||
* function to log its current values. This is better than logging
|
||||
* after the coordinates have been modified, because only those
|
||||
* vertices that are modified need to have their original values
|
||||
* stored.
|
||||
*
|
||||
* Handles two separate cases:
|
||||
*
|
||||
* If the vertex was added in the current log entry, update the
|
||||
* vertex in the map of added vertices.
|
||||
*
|
||||
* If the vertex already existed prior to the current log entry, a
|
||||
* separate key/value map of modified vertices is used (using the
|
||||
* vertex's ID as the key). The values stored in that case are
|
||||
* the vertex's original state so that an undo can restore the
|
||||
* previous state.
|
||||
*
|
||||
* On undo, the current vertex state will be swapped with the stored
|
||||
* state so that a subsequent redo operation will restore the newer
|
||||
* vertex state.
|
||||
*/
|
||||
void BM_log_vert_before_modified(BMLog *log, BMVert *v, int cd_vert_mask_offset);
|
||||
|
||||
/**
|
||||
* Log a new vertex as added to the #BMesh.
|
||||
*
|
||||
* The new vertex gets a unique ID assigned. It is then added to a map
|
||||
* of added vertices, with the key being its ID and the value
|
||||
* containing everything needed to reconstruct that vertex.
|
||||
*/
|
||||
void BM_log_vert_added(BMLog *log, BMVert *v, int cd_vert_mask_offset);
|
||||
|
||||
/**
|
||||
* Log a face before it is modified.
|
||||
*
|
||||
* This is intended to handle only header flags and we always
|
||||
* assume face has been added before.
|
||||
*/
|
||||
void BM_log_face_modified(BMLog *log, BMFace *f);
|
||||
|
||||
/**
|
||||
* Log a new face as added to the #BMesh.
|
||||
*
|
||||
* The new face gets a unique ID assigned. It is then added to a map
|
||||
* of added faces, with the key being its ID and the value containing
|
||||
* everything needed to reconstruct that face.
|
||||
*/
|
||||
void BM_log_face_added(BMLog *log, BMFace *f);
|
||||
|
||||
/**
|
||||
* Log a vertex as removed from the #BMesh.
|
||||
*
|
||||
* A couple things can happen here:
|
||||
*
|
||||
* If the vertex was added as part of the current log entry, then it's
|
||||
* deleted and forgotten about entirely. Its unique ID is returned to
|
||||
* the unused pool.
|
||||
*
|
||||
* If the vertex was already part of the #BMesh before the current log
|
||||
* entry, it is added to a map of deleted vertices, with the key being
|
||||
* its ID and the value containing everything needed to reconstruct
|
||||
* that vertex.
|
||||
*
|
||||
* If there's a move record for the vertex, that's used as the
|
||||
* vertices original location, then the move record is deleted.
|
||||
*/
|
||||
void BM_log_vert_removed(BMLog *log, BMVert *v, int cd_vert_mask_offset);
|
||||
|
||||
/**
|
||||
* Log a face as removed from the #BMesh.
|
||||
*
|
||||
* A couple things can happen here:
|
||||
*
|
||||
* If the face was added as part of the current log entry, then it's
|
||||
* deleted and forgotten about entirely. Its unique ID is returned to
|
||||
* the unused pool.
|
||||
*
|
||||
* If the face was already part of the #BMesh before the current log
|
||||
* entry, it is added to a map of deleted faces, with the key being
|
||||
* its ID and the value containing everything needed to reconstruct
|
||||
* that face.
|
||||
*/
|
||||
void BM_log_face_removed(BMLog *log, BMFace *f);
|
||||
|
||||
/**
|
||||
* Log all vertices/faces in the #BMesh as added.
|
||||
*/
|
||||
void BM_log_all_added(BMesh *bm, BMLog *log);
|
||||
|
||||
/** Log all vertices/faces in the #BMesh as removed. */
|
||||
void BM_log_before_all_removed(BMesh *bm, BMLog *log);
|
||||
|
||||
/**
|
||||
* Search the log for the original vertex coordinates.
|
||||
*
|
||||
* Does not modify the log or the vertex.
|
||||
*
|
||||
* \return the pointer or nullptr if the vertex isn't found.
|
||||
*/
|
||||
const float *BM_log_find_original_vert_co(BMLog *log, BMVert *v);
|
||||
|
||||
/**
|
||||
* Search the log for the original vertex mask.
|
||||
*
|
||||
* Does not modify the log or the vertex.
|
||||
*
|
||||
* \return the pointer or nullptr if the vertex isn't found.
|
||||
*/
|
||||
const float *BM_log_find_original_vert_mask(BMLog *log, BMVert *v);
|
||||
|
||||
/** Get the logged data of a vertex (avoid multiple lookups). */
|
||||
void BM_log_original_vert_data(BMLog *log, BMVert *v, const float **r_co, const float **r_no);
|
||||
|
||||
#ifndef NDEBUG
|
||||
/** For internal use only (unit testing). */
|
||||
BMLogEntry *BM_log_current_entry(BMLog *log);
|
||||
/** For internal use only (unit testing) */
|
||||
struct RangeTreeUInt *BM_log_unused_ids(BMLog *log);
|
||||
|
||||
void BM_log_print(const BMLog *log, const char *description);
|
||||
void BM_log_print_entry(BMesh *bm, BMLogEntry *entry);
|
||||
#endif
|
||||
|
||||
} // namespace blender
|
||||
1639
blender-5.2.0/source/blender/bmesh/intern/bmesh_marking.cc
Normal file
1639
blender-5.2.0/source/blender/bmesh/intern/bmesh_marking.cc
Normal file
File diff suppressed because it is too large
Load Diff
232
blender-5.2.0/source/blender/bmesh/intern/bmesh_marking.hh
Normal file
232
blender-5.2.0/source/blender/bmesh/intern/bmesh_marking.hh
Normal file
@@ -0,0 +1,232 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_enum_flags.hh"
|
||||
#include "BLI_map.hh"
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMEditSelection {
|
||||
struct BMEditSelection *next, *prev;
|
||||
BMElem *ele;
|
||||
char htype;
|
||||
};
|
||||
|
||||
enum class BMSelectFlushFlag : uint8_t {
|
||||
None = 0,
|
||||
RecalcLenVert = (1 << 0),
|
||||
RecalcLenEdge = (1 << 1),
|
||||
RecalcLenFace = (1 << 2),
|
||||
/**
|
||||
* Flush selection down, depending on the selection mode.
|
||||
*
|
||||
* Disabled by default as edge & face selection functions flush down:
|
||||
* (functions #BM_edge_select_set & #BM_face_select_set).
|
||||
* However selection logic needs to take care to perform de-selection *before* selection,
|
||||
* otherwise flushing down *is* needed.
|
||||
*/
|
||||
Down = (1 << 3),
|
||||
};
|
||||
ENUM_OPERATORS(BMSelectFlushFlag)
|
||||
|
||||
#define BMSelectFlushFlag_All \
|
||||
(BMSelectFlushFlag::RecalcLenVert | BMSelectFlushFlag::RecalcLenEdge | \
|
||||
BMSelectFlushFlag::RecalcLenFace)
|
||||
|
||||
#define BMSelectFlushFlag_Default BMSelectFlushFlag_All
|
||||
|
||||
/* Geometry hiding code. */
|
||||
|
||||
#define BM_elem_hide_set(bm, ele, hide) _bm_elem_hide_set(bm, &(ele)->head, hide)
|
||||
void _bm_elem_hide_set(BMesh *bm, BMHeader *head, bool hide);
|
||||
void BM_vert_hide_set(BMVert *v, bool hide);
|
||||
void BM_edge_hide_set(BMEdge *e, bool hide);
|
||||
void BM_face_hide_set(BMFace *f, bool hide);
|
||||
|
||||
/* Selection code. */
|
||||
|
||||
/**
|
||||
* \note use BM_elem_flag_test(ele, BM_ELEM_SELECT) to test selection
|
||||
* \note by design, this will not touch the editselection history stuff
|
||||
*/
|
||||
void BM_elem_select_set(BMesh *bm, BMElem *ele, bool select);
|
||||
|
||||
void BM_mesh_elem_hflag_enable_test(
|
||||
BMesh *bm, char htype, char hflag, bool respecthide, bool overwrite, char hflag_test);
|
||||
void BM_mesh_elem_hflag_disable_test(
|
||||
BMesh *bm, char htype, char hflag, bool respecthide, bool overwrite, char hflag_test);
|
||||
|
||||
void BM_mesh_elem_hflag_enable_all(BMesh *bm, char htype, char hflag, bool respecthide);
|
||||
void BM_mesh_elem_hflag_disable_all(BMesh *bm, char htype, char hflag, bool respecthide);
|
||||
|
||||
/* Individual element select functions, #BM_elem_select_set is a shortcut for these
|
||||
* that automatically detects which one to use. */
|
||||
|
||||
/**
|
||||
* \brief Select Vert
|
||||
*
|
||||
* Changes selection state of a single vertex
|
||||
* in a mesh
|
||||
*/
|
||||
void BM_vert_select_set(BMesh *bm, BMVert *v, bool select);
|
||||
/**
|
||||
* \brief Select Edge
|
||||
*
|
||||
* Changes selection state of a single edge in a mesh.
|
||||
*/
|
||||
void BM_edge_select_set(BMesh *bm, BMEdge *e, bool select);
|
||||
/**
|
||||
* \brief Select Face
|
||||
*
|
||||
* Changes selection state of a single
|
||||
* face in a mesh.
|
||||
*/
|
||||
void BM_face_select_set(BMesh *bm, BMFace *f, bool select);
|
||||
|
||||
/* Lower level functions which don't do flushing. */
|
||||
|
||||
void BM_edge_select_set_noflush(BMesh *bm, BMEdge *e, bool select);
|
||||
void BM_face_select_set_noflush(BMesh *bm, BMFace *f, bool select);
|
||||
|
||||
/**
|
||||
* Return true when there are a mix of selected/unselected elements.
|
||||
*/
|
||||
bool BM_mesh_select_is_mixed(const BMesh *bm);
|
||||
|
||||
/**
|
||||
* \brief Select Mode Clean
|
||||
*
|
||||
* Remove isolated selected elements when in a mode doesn't support them.
|
||||
* eg: in edge-mode a selected vertex must be connected to a selected edge.
|
||||
*
|
||||
* \note this could be made a part of #BM_mesh_select_mode_flush_ex
|
||||
*/
|
||||
void BM_mesh_select_mode_clean_ex(BMesh *bm, short selectmode);
|
||||
void BM_mesh_select_mode_clean(BMesh *bm);
|
||||
|
||||
/**
|
||||
* Select Mode Set
|
||||
*
|
||||
* Sets the selection mode for the bmesh,
|
||||
* updating the selection state.
|
||||
*/
|
||||
void BM_mesh_select_mode_set(BMesh *bm, int selectmode);
|
||||
/**
|
||||
* \brief Select Mode Flush
|
||||
*
|
||||
* Makes sure to flush selections 'upwards'
|
||||
* (ie: all verts of an edge selects the edge and so on).
|
||||
* This should only be called by system and not tool authors.
|
||||
*
|
||||
* \note Flushing down can be enabled for edge/face modes
|
||||
* by enabling #BMSelectFlushFlag:Down for `flag`.
|
||||
*/
|
||||
void BM_mesh_select_mode_flush_ex(BMesh *bm, short selectmode, BMSelectFlushFlag flag);
|
||||
void BM_mesh_select_mode_flush(BMesh *bm);
|
||||
|
||||
/**
|
||||
* Mode independent selection/de-selection flush from vertices.
|
||||
*
|
||||
* \param select: When true, flush the selection state to de-selected elements,
|
||||
* otherwise perform the opposite, flushing de-selection.
|
||||
*/
|
||||
void BM_mesh_select_flush_from_verts(BMesh *bm, bool select);
|
||||
|
||||
int BM_mesh_elem_hflag_count_enabled(BMesh *bm, char htype, char hflag, bool respecthide);
|
||||
int BM_mesh_elem_hflag_count_disabled(BMesh *bm, char htype, char hflag, bool respecthide);
|
||||
|
||||
/* Edit selection stuff. */
|
||||
|
||||
void BM_mesh_active_face_set(BMesh *bm, BMFace *f);
|
||||
int BM_mesh_active_face_index_get(BMesh *bm, bool is_sloppy, bool is_selected);
|
||||
int BM_mesh_active_edge_index_get(BMesh *bm);
|
||||
int BM_mesh_active_vert_index_get(BMesh *bm);
|
||||
|
||||
BMFace *BM_mesh_active_face_get(BMesh *bm, bool is_sloppy, bool is_selected);
|
||||
BMEdge *BM_mesh_active_edge_get(BMesh *bm);
|
||||
BMVert *BM_mesh_active_vert_get(BMesh *bm);
|
||||
BMElem *BM_mesh_active_elem_get(BMesh *bm);
|
||||
|
||||
/**
|
||||
* Generic way to get data from an #BMEditSelection type
|
||||
* These functions were written to be used by the Modifier widget
|
||||
* when in Rotate about active mode, but can be used anywhere.
|
||||
*
|
||||
* - #BM_editselection_center
|
||||
* - #BM_editselection_normal
|
||||
* - #BM_editselection_plane
|
||||
*/
|
||||
void BM_editselection_center(BMEditSelection *ese, float r_center[3]);
|
||||
void BM_editselection_normal(BMEditSelection *ese, float r_normal[3]);
|
||||
/**
|
||||
* Calculate a plane that is right angles to the edge/vert/faces normal
|
||||
* also make the plane run along an axis that is related to the geometry,
|
||||
* because this is used for the gizmos Y axis.
|
||||
*/
|
||||
void BM_editselection_plane(BMEditSelection *ese, float r_plane[3]);
|
||||
|
||||
#define BM_select_history_check(bm, ele) _bm_select_history_check(bm, &(ele)->head)
|
||||
#define BM_select_history_remove(bm, ele) _bm_select_history_remove(bm, &(ele)->head)
|
||||
#define BM_select_history_store_notest(bm, ele) _bm_select_history_store_notest(bm, &(ele)->head)
|
||||
#define BM_select_history_store(bm, ele) _bm_select_history_store(bm, &(ele)->head)
|
||||
#define BM_select_history_store_head_notest(bm, ele) \
|
||||
_bm_select_history_store_head_notest(bm, &(ele)->head)
|
||||
#define BM_select_history_store_head(bm, ele) _bm_select_history_store_head(bm, &(ele)->head)
|
||||
#define BM_select_history_store_after_notest(bm, ese_ref, ele) \
|
||||
_bm_select_history_store_after_notest(bm, ese_ref, &(ele)->head)
|
||||
#define BM_select_history_store_after(bm, ese, ese_ref) \
|
||||
_bm_select_history_store_after(bm, ese_ref, &(ele)->head)
|
||||
|
||||
bool _bm_select_history_check(BMesh *bm, const BMHeader *ele);
|
||||
bool _bm_select_history_remove(BMesh *bm, BMHeader *ele);
|
||||
void _bm_select_history_store_notest(BMesh *bm, BMHeader *ele);
|
||||
void _bm_select_history_store(BMesh *bm, BMHeader *ele);
|
||||
void _bm_select_history_store_head_notest(BMesh *bm, BMHeader *ele);
|
||||
void _bm_select_history_store_head(BMesh *bm, BMHeader *ele);
|
||||
void _bm_select_history_store_after(BMesh *bm, BMEditSelection *ese_ref, BMHeader *ele);
|
||||
void _bm_select_history_store_after_notest(BMesh *bm, BMEditSelection *ese_ref, BMHeader *ele);
|
||||
|
||||
void BM_select_history_validate(BMesh *bm);
|
||||
void BM_select_history_clear(BMesh *bm);
|
||||
/**
|
||||
* Get all element types present in a selection history.
|
||||
*/
|
||||
[[nodiscard]] char BM_select_history_htype_all(const BMesh *bm);
|
||||
/**
|
||||
* Get the active mesh element (with active-face fallback).
|
||||
*/
|
||||
bool BM_select_history_active_get(BMesh *bm, struct BMEditSelection *ese);
|
||||
/**
|
||||
* Return a map from #BMVert/#BMEdge/#BMFace -> #BMEditSelection.
|
||||
*/
|
||||
struct GHash *BM_select_history_map_create(BMesh *bm);
|
||||
|
||||
/**
|
||||
* Map arguments may all be the same pointer.
|
||||
*/
|
||||
void BM_select_history_merge_from_targetmap(BMesh *bm,
|
||||
Map<void *, void *> *vert_map,
|
||||
Map<void *, void *> *edge_map,
|
||||
Map<void *, void *> *face_map,
|
||||
bool use_chain);
|
||||
|
||||
#define BM_SELECT_HISTORY_BACKUP(bm) \
|
||||
{ \
|
||||
ListBaseT<BMEditSelection> _bm_prev_selected = (bm)->selected; \
|
||||
BLI_listbase_clear(&(bm)->selected)
|
||||
|
||||
#define BM_SELECT_HISTORY_RESTORE(bm) \
|
||||
(bm)->selected = _bm_prev_selected; \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
} // namespace blender
|
||||
1368
blender-5.2.0/source/blender/bmesh/intern/bmesh_mesh.cc
Normal file
1368
blender-5.2.0/source/blender/bmesh/intern/bmesh_mesh.cc
Normal file
File diff suppressed because it is too large
Load Diff
219
blender-5.2.0/source/blender/bmesh/intern/bmesh_mesh.hh
Normal file
219
blender-5.2.0/source/blender/bmesh/intern/bmesh_mesh.hh
Normal file
@@ -0,0 +1,219 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
#include "intern/bmesh_operator_api.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMAllocTemplate;
|
||||
|
||||
void BM_mesh_elem_toolflags_ensure(BMesh *bm);
|
||||
void BM_mesh_elem_toolflags_clear(BMesh *bm);
|
||||
|
||||
struct BMeshCreateParams {
|
||||
bool use_toolflags : 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief BMesh Make Mesh
|
||||
*
|
||||
* Allocates a new BMesh structure.
|
||||
*
|
||||
* \return The New bmesh
|
||||
*
|
||||
* \note ob is needed by multires
|
||||
*/
|
||||
BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const BMeshCreateParams *params);
|
||||
|
||||
/**
|
||||
* \brief BMesh Free Mesh
|
||||
*
|
||||
* Frees a BMesh data and its structure.
|
||||
*/
|
||||
void BM_mesh_free(BMesh *bm);
|
||||
/**
|
||||
* \brief BMesh Free Mesh Data
|
||||
*
|
||||
* Frees a BMesh structure.
|
||||
*
|
||||
* \note frees mesh, but not actual BMesh struct
|
||||
*/
|
||||
void BM_mesh_data_free(BMesh *bm);
|
||||
/**
|
||||
* \brief BMesh Clear Mesh
|
||||
*
|
||||
* Clear all data in bm
|
||||
*/
|
||||
void BM_mesh_clear(BMesh *bm);
|
||||
|
||||
/**
|
||||
* \brief BMesh Begin Edit
|
||||
*
|
||||
* Functions for setting up a mesh for editing and cleaning up after
|
||||
* the editing operations are done. These are called by the tools/operator
|
||||
* API for each time a tool is executed.
|
||||
*/
|
||||
void bmesh_edit_begin(BMesh *bm, BMOpTypeFlag type_flag);
|
||||
/**
|
||||
* \brief BMesh End Edit
|
||||
*/
|
||||
void bmesh_edit_end(BMesh *bm, BMOpTypeFlag type_flag);
|
||||
|
||||
void BM_mesh_elem_index_ensure_ex(BMesh *bm, char htype, int elem_offset[4]);
|
||||
void BM_mesh_elem_index_ensure(BMesh *bm, char htype);
|
||||
/**
|
||||
* Array checking/setting macros.
|
||||
*
|
||||
* Currently vert/edge/loop/face index data is being abused, in a few areas of the code.
|
||||
*
|
||||
* To avoid correcting them afterwards, set 'bm->elem_index_dirty' however its possible
|
||||
* this flag is set incorrectly which could crash blender.
|
||||
*
|
||||
* Functions that calls this function may depend on dirty indices on being set.
|
||||
*
|
||||
* This is read-only, so it can be used for assertions that don't impact behavior.
|
||||
*/
|
||||
void BM_mesh_elem_index_validate(
|
||||
BMesh *bm, const char *location, const char *func, const char *msg_a, const char *msg_b);
|
||||
|
||||
#ifndef NDEBUG
|
||||
/**
|
||||
* \see #BM_mesh_elem_index_validate the same rationale applies to this function.
|
||||
*/
|
||||
bool BM_mesh_elem_table_check(BMesh *bm);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Re-allocates mesh data with/without toolflags.
|
||||
*/
|
||||
void BM_mesh_toolflags_set(BMesh *bm, bool use_toolflags);
|
||||
|
||||
void BM_mesh_elem_table_ensure(BMesh *bm, char htype);
|
||||
/* use BM_mesh_elem_table_ensure where possible to avoid full rebuild */
|
||||
void BM_mesh_elem_table_init(BMesh *bm, char htype);
|
||||
void BM_mesh_elem_table_free(BMesh *bm, char htype);
|
||||
|
||||
BLI_INLINE BMVert *BM_vert_at_index(BMesh *bm, const int index)
|
||||
{
|
||||
BLI_assert((index >= 0) && (index < bm->totvert));
|
||||
BLI_assert((bm->elem_table_dirty & BM_VERT) == 0);
|
||||
return bm->vtable[index];
|
||||
}
|
||||
BLI_INLINE BMEdge *BM_edge_at_index(BMesh *bm, const int index)
|
||||
{
|
||||
BLI_assert((index >= 0) && (index < bm->totedge));
|
||||
BLI_assert((bm->elem_table_dirty & BM_EDGE) == 0);
|
||||
return bm->etable[index];
|
||||
}
|
||||
BLI_INLINE BMFace *BM_face_at_index(BMesh *bm, const int index)
|
||||
{
|
||||
BLI_assert((index >= 0) && (index < bm->totface));
|
||||
BLI_assert((bm->elem_table_dirty & BM_FACE) == 0);
|
||||
return bm->ftable[index];
|
||||
}
|
||||
|
||||
BMVert *BM_vert_at_index_find(BMesh *bm, int index);
|
||||
BMEdge *BM_edge_at_index_find(BMesh *bm, int index);
|
||||
BMFace *BM_face_at_index_find(BMesh *bm, int index);
|
||||
BMLoop *BM_loop_at_index_find(BMesh *bm, int index);
|
||||
|
||||
/**
|
||||
* Use lookup table when available, else use slower find functions.
|
||||
*
|
||||
* \note Try to use #BM_mesh_elem_table_ensure instead.
|
||||
*/
|
||||
BMVert *BM_vert_at_index_find_or_table(BMesh *bm, int index);
|
||||
BMEdge *BM_edge_at_index_find_or_table(BMesh *bm, int index);
|
||||
BMFace *BM_face_at_index_find_or_table(BMesh *bm, int index);
|
||||
|
||||
// XXX
|
||||
|
||||
/**
|
||||
* Return the amount of element of type 'type' in a given bmesh.
|
||||
*/
|
||||
int BM_mesh_elem_count(BMesh *bm, char htype);
|
||||
|
||||
/**
|
||||
* Remaps the vertices, edges and/or faces of the bmesh as indicated by vert/edge/face_idx arrays
|
||||
* (xxx_idx[org_index] = new_index).
|
||||
*
|
||||
* A NULL array means no changes.
|
||||
*
|
||||
* \note
|
||||
* - Does not mess with indices, just sets elem_index_dirty flag.
|
||||
* - For verts/edges/faces only (as loops must remain "ordered" and "aligned"
|
||||
* on a per-face basis...).
|
||||
*
|
||||
* \warning Be careful if you keep pointers to affected BM elements,
|
||||
* or arrays, when using this func!
|
||||
*/
|
||||
void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const uint *face_idx);
|
||||
|
||||
/**
|
||||
* Use new memory pools for this mesh.
|
||||
*
|
||||
* \note needed for re-sizing elements (adding/removing tool flags)
|
||||
* but could also be used for packing fragmented bmeshes.
|
||||
*/
|
||||
void BM_mesh_rebuild(BMesh *bm,
|
||||
const BMeshCreateParams *params,
|
||||
BLI_mempool *vpool,
|
||||
BLI_mempool *epool,
|
||||
BLI_mempool *lpool,
|
||||
BLI_mempool *fpool);
|
||||
|
||||
struct BMAllocTemplate {
|
||||
int totvert, totedge, totloop, totface;
|
||||
};
|
||||
|
||||
/* used as an extern, defined in bmesh.h */
|
||||
extern const BMAllocTemplate bm_mesh_allocsize_default;
|
||||
extern const BMAllocTemplate bm_mesh_chunksize_default;
|
||||
|
||||
#define BMALLOC_TEMPLATE_FROM_BM(bm) \
|
||||
{(CHECK_TYPE_INLINE(bm, BMesh *), (bm)->totvert), (bm)->totedge, (bm)->totloop, (bm)->totface}
|
||||
|
||||
#define _VA_BMALLOC_TEMPLATE_FROM_ME_1(me) \
|
||||
{ \
|
||||
(CHECK_TYPE_INLINE(me, Mesh *), (me)->verts_num), \
|
||||
(me)->edges_num, \
|
||||
(me)->corners_num, \
|
||||
(me)->faces_num, \
|
||||
}
|
||||
#define _VA_BMALLOC_TEMPLATE_FROM_ME_2(me_a, me_b) \
|
||||
{ \
|
||||
(CHECK_TYPE_INLINE(me_a, Mesh *), \
|
||||
CHECK_TYPE_INLINE(me_b, Mesh *), \
|
||||
(me_a)->verts_num + (me_b)->verts_num), \
|
||||
(me_a)->edges_num + (me_b)->edges_num, \
|
||||
(me_a)->corners_num + (me_b)->corners_num, \
|
||||
(me_a)->faces_num + (me_b)->faces_num, \
|
||||
}
|
||||
#define BMALLOC_TEMPLATE_FROM_ME(...) \
|
||||
VA_NARGS_CALL_OVERLOAD(_VA_BMALLOC_TEMPLATE_FROM_ME_, __VA_ARGS__)
|
||||
|
||||
void BM_mesh_vert_normals_get(BMesh *bm, MutableSpan<float3> normals);
|
||||
|
||||
/* Vertex coords access. */
|
||||
void BM_mesh_vert_coords_get(BMesh *bm, MutableSpan<float3> positions);
|
||||
Array<float3> BM_mesh_vert_coords_alloc(BMesh *bm);
|
||||
void BM_mesh_vert_coords_apply(BMesh *bm, Span<float3> vert_coords);
|
||||
void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm,
|
||||
Span<float3> vert_coords,
|
||||
const float4x4 &transform);
|
||||
|
||||
} // namespace blender
|
||||
2220
blender-5.2.0/source/blender/bmesh/intern/bmesh_mesh_convert.cc
Normal file
2220
blender-5.2.0/source/blender/bmesh/intern/bmesh_mesh_convert.cc
Normal file
File diff suppressed because it is too large
Load Diff
109
blender-5.2.0/source/blender/bmesh/intern/bmesh_mesh_convert.hh
Normal file
109
blender-5.2.0/source/blender/bmesh/intern/bmesh_mesh_convert.hh
Normal file
@@ -0,0 +1,109 @@
|
||||
/* SPDX-FileCopyrightText: 2004 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* \return Whether attributes with the given name are stored in special flags or fields in BMesh
|
||||
* rather than in the regular custom data blocks.
|
||||
*/
|
||||
bool BM_attribute_stored_in_bmesh_builtin(StringRef name);
|
||||
|
||||
struct CustomData_MeshMasks;
|
||||
struct Main;
|
||||
struct Mesh;
|
||||
|
||||
struct BMeshFromMeshParams {
|
||||
bool calc_face_normal;
|
||||
bool calc_vert_normal;
|
||||
/* add a vertex CD_SHAPE_KEYINDEX layer */
|
||||
bool add_key_index;
|
||||
/* set vertex coordinates from the shapekey */
|
||||
bool use_shapekey;
|
||||
/* define the active shape key (index + 1) */
|
||||
int active_shapekey;
|
||||
struct CustomData_MeshMasks cd_mask_extra;
|
||||
};
|
||||
/**
|
||||
* \brief Mesh -> BMesh
|
||||
* \param bm: The mesh to write into, while this is typically a newly created BMesh,
|
||||
* merging into existing data is supported.
|
||||
* Note the custom-data layout isn't used.
|
||||
* If more comprehensive merging is needed we should move this into a separate function
|
||||
* since this should be kept fast for edit-mode switching and storing undo steps.
|
||||
*
|
||||
* \warning This function doesn't calculate face normals.
|
||||
*/
|
||||
void BM_mesh_bm_from_me(BMesh *bm, const Mesh *mesh, const BMeshFromMeshParams *params)
|
||||
ATTR_NONNULL(1, 3);
|
||||
|
||||
struct BMeshToMeshParams {
|
||||
/** Update object hook indices & vertex parents. */
|
||||
bool calc_object_remap;
|
||||
/**
|
||||
* This re-assigns shape-key indices. Only do if the BMesh will have continued use
|
||||
* to update the mesh & shape key in the future.
|
||||
* In the case the BMesh is freed immediately, this can be left false.
|
||||
*
|
||||
* This is needed when flushing changes from edit-mode into object mode,
|
||||
* so a second flush or edit-mode exit doesn't run with indices
|
||||
* that have become invalid from updating the shape-key, see #71865.
|
||||
*/
|
||||
bool update_shapekey_indices;
|
||||
/**
|
||||
* Instead of copying the basis shape-key into the position array,
|
||||
* copy the #BMVert.co directly to the #Mesh position (used for reading undo data).
|
||||
*/
|
||||
bool active_shapekey_to_mvert;
|
||||
struct CustomData_MeshMasks cd_mask_extra;
|
||||
};
|
||||
|
||||
/**
|
||||
* \param bmain: May be NULL in case \a calc_object_remap parameter option is not set.
|
||||
*/
|
||||
void BM_mesh_bm_to_me(struct Main *bmain, BMesh *bm, Mesh *mesh, const BMeshToMeshParams *params)
|
||||
ATTR_NONNULL(2, 3, 4);
|
||||
|
||||
/**
|
||||
* A version of #BM_mesh_bm_to_me intended for getting the mesh
|
||||
* to pass to the modifier stack for evaluation,
|
||||
* instead of mode switching (where we make sure all data is kept
|
||||
* and do expensive lookups to maintain shape keys).
|
||||
*
|
||||
* Key differences:
|
||||
*
|
||||
* - Don't support merging with existing mesh.
|
||||
* - Ignore shape-keys.
|
||||
* - Ignore vertex-parents.
|
||||
* - Ignore selection history.
|
||||
* - Uses #CD_MASK_DERIVEDMESH instead of #CD_MASK_MESH.
|
||||
*
|
||||
* \note Was `cddm_from_bmesh_ex` in 2.7x, removed `MFace` support.
|
||||
*/
|
||||
void BM_mesh_bm_to_me_for_eval(BMesh &bm, Mesh &mesh, const CustomData_MeshMasks *cd_mask_extra);
|
||||
|
||||
/**
|
||||
* A version of #BM_mesh_bm_to_me_for_eval but copying data layers and Mesh attributes is optional.
|
||||
* It also allows shape-keys but don't re-assigns shape-key indices.
|
||||
*
|
||||
* \param mask: Custom data masks to control which layers are copied.
|
||||
* If nullptr, no layer data is copied.
|
||||
* \param add_mesh_attributes: If true, adds mesh attributes during the conversion.
|
||||
*/
|
||||
void BM_mesh_bm_to_me_compact(BMesh &bm,
|
||||
Mesh &mesh,
|
||||
const CustomData_MeshMasks *mask,
|
||||
bool add_mesh_attributes);
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,76 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*
|
||||
* Evaluated mesh info printing function, to help track down differences output.
|
||||
*
|
||||
* Output from these functions can be evaluated as Python literals.
|
||||
* See `mesh_debug.cc` for the equivalent #Mesh functionality.
|
||||
*/
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
# include <cstdio>
|
||||
|
||||
# include "MEM_guardedalloc.h"
|
||||
|
||||
# include "BKE_customdata.hh"
|
||||
|
||||
# include "bmesh.hh"
|
||||
|
||||
# include "bmesh_mesh_debug.hh"
|
||||
|
||||
# include "BLI_dynstr.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
char *BM_mesh_debug_info(BMesh *bm)
|
||||
{
|
||||
DynStr *dynstr = BLI_dynstr_new();
|
||||
char *ret;
|
||||
|
||||
const char *indent8 = " ";
|
||||
|
||||
BLI_dynstr_append(dynstr, "{\n");
|
||||
BLI_dynstr_appendf(dynstr, " 'ptr': '%p',\n", static_cast<void *>(bm));
|
||||
BLI_dynstr_appendf(dynstr, " 'totvert': %d,\n", bm->totvert);
|
||||
BLI_dynstr_appendf(dynstr, " 'totedge': %d,\n", bm->totedge);
|
||||
BLI_dynstr_appendf(dynstr, " 'totface': %d,\n", bm->totface);
|
||||
|
||||
BLI_dynstr_append(dynstr, " 'vert_layers': (\n");
|
||||
CustomData_debug_info_from_layers(&bm->vdata, indent8, dynstr);
|
||||
BLI_dynstr_append(dynstr, " ),\n");
|
||||
|
||||
BLI_dynstr_append(dynstr, " 'edge_layers': (\n");
|
||||
CustomData_debug_info_from_layers(&bm->edata, indent8, dynstr);
|
||||
BLI_dynstr_append(dynstr, " ),\n");
|
||||
|
||||
BLI_dynstr_append(dynstr, " 'loop_layers': (\n");
|
||||
CustomData_debug_info_from_layers(&bm->ldata, indent8, dynstr);
|
||||
BLI_dynstr_append(dynstr, " ),\n");
|
||||
|
||||
BLI_dynstr_append(dynstr, " 'poly_layers': (\n");
|
||||
CustomData_debug_info_from_layers(&bm->pdata, indent8, dynstr);
|
||||
BLI_dynstr_append(dynstr, " ),\n");
|
||||
|
||||
BLI_dynstr_append(dynstr, "}\n");
|
||||
|
||||
ret = BLI_dynstr_get_cstring(dynstr);
|
||||
BLI_dynstr_free(dynstr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void BM_mesh_debug_print(BMesh *bm)
|
||||
{
|
||||
char *str = BM_mesh_debug_info(bm);
|
||||
puts(str);
|
||||
fflush(stdout);
|
||||
MEM_delete(str);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
|
||||
#endif /* !NDEBUG */
|
||||
@@ -0,0 +1,22 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#ifndef NDEBUG
|
||||
char *BM_mesh_debug_info(BMesh *bm) ATTR_NONNULL(1) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
void BM_mesh_debug_print(BMesh *bm) ATTR_NONNULL(1);
|
||||
#endif /* !NDEBUG */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,166 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* Duplicate geometry from one mesh from another.
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_array.hh"
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static BMVert *bm_vert_copy(BMesh *bm_dst,
|
||||
const std::optional<BMCustomDataCopyMap> &cd_vert_map,
|
||||
BMVert *v_src)
|
||||
{
|
||||
BMVert *v_dst = BM_vert_create(bm_dst, v_src->co, nullptr, BM_CREATE_SKIP_CD);
|
||||
if (cd_vert_map.has_value()) {
|
||||
BM_elem_attrs_copy(bm_dst, cd_vert_map.value(), v_src, v_dst);
|
||||
}
|
||||
else {
|
||||
BM_elem_attrs_copy(bm_dst, v_src, v_dst);
|
||||
}
|
||||
return v_dst;
|
||||
}
|
||||
|
||||
static BMEdge *bm_edge_copy_with_arrays(BMesh *bm_dst,
|
||||
const std::optional<BMCustomDataCopyMap> &cd_edge_map,
|
||||
BMEdge *e_src,
|
||||
BMVert **verts_dst)
|
||||
{
|
||||
BMVert *e_dst_v1 = verts_dst[BM_elem_index_get(e_src->v1)];
|
||||
BMVert *e_dst_v2 = verts_dst[BM_elem_index_get(e_src->v2)];
|
||||
BMEdge *e_dst = BM_edge_create(bm_dst, e_dst_v1, e_dst_v2, nullptr, BM_CREATE_SKIP_CD);
|
||||
if (cd_edge_map.has_value()) {
|
||||
BM_elem_attrs_copy(bm_dst, cd_edge_map.value(), e_src, e_dst);
|
||||
}
|
||||
else {
|
||||
BM_elem_attrs_copy(bm_dst, e_src, e_dst);
|
||||
}
|
||||
return e_dst;
|
||||
}
|
||||
|
||||
static BMFace *bm_face_copy_with_arrays(BMesh *bm_dst,
|
||||
const std::optional<BMCustomDataCopyMap> cd_face_map,
|
||||
const std::optional<BMCustomDataCopyMap> &cd_loop_map,
|
||||
BMFace *f_src,
|
||||
BMVert **verts_dst,
|
||||
BMEdge **edges_dst)
|
||||
{
|
||||
BMFace *f_dst;
|
||||
Array<BMVert *, BM_DEFAULT_NGON_STACK_SIZE> vtar(f_src->len);
|
||||
Array<BMEdge *, BM_DEFAULT_NGON_STACK_SIZE> edar(f_src->len);
|
||||
BMLoop *l_iter_src, *l_iter_dst, *l_first_src;
|
||||
int i;
|
||||
|
||||
l_first_src = BM_FACE_FIRST_LOOP(f_src);
|
||||
|
||||
/* Lookup verts & edges. */
|
||||
l_iter_src = l_first_src;
|
||||
i = 0;
|
||||
do {
|
||||
vtar[i] = verts_dst[BM_elem_index_get(l_iter_src->v)];
|
||||
edar[i] = edges_dst[BM_elem_index_get(l_iter_src->e)];
|
||||
i++;
|
||||
} while ((l_iter_src = l_iter_src->next) != l_first_src);
|
||||
|
||||
/* Create new face. */
|
||||
f_dst = BM_face_create(bm_dst, vtar.data(), edar.data(), f_src->len, nullptr, BM_CREATE_SKIP_CD);
|
||||
|
||||
/* Copy attributes. */
|
||||
if (cd_face_map.has_value()) {
|
||||
BM_elem_attrs_copy(bm_dst, cd_face_map.value(), f_src, f_dst);
|
||||
}
|
||||
else {
|
||||
BM_elem_attrs_copy(bm_dst, f_src, f_dst);
|
||||
}
|
||||
|
||||
/* Copy per-loop custom data. */
|
||||
l_iter_src = l_first_src;
|
||||
l_iter_dst = BM_FACE_FIRST_LOOP(f_dst);
|
||||
do {
|
||||
if (cd_loop_map.has_value()) {
|
||||
BM_elem_attrs_copy(bm_dst, cd_loop_map.value(), l_iter_src, l_iter_dst);
|
||||
}
|
||||
else {
|
||||
BM_elem_attrs_copy(bm_dst, l_iter_src, l_iter_dst);
|
||||
}
|
||||
} while ((void)(l_iter_dst = l_iter_dst->next), (l_iter_src = l_iter_src->next) != l_first_src);
|
||||
|
||||
return f_dst;
|
||||
}
|
||||
|
||||
void BM_mesh_copy_arrays(BMesh *bm_src,
|
||||
BMesh *bm_dst,
|
||||
BMVert **verts_src,
|
||||
uint verts_src_len,
|
||||
BMEdge **edges_src,
|
||||
uint edges_src_len,
|
||||
BMFace **faces_src,
|
||||
uint faces_src_len)
|
||||
{
|
||||
const std::optional<BMCustomDataCopyMap> cd_vert_map =
|
||||
(bm_src == bm_dst) ? std::nullopt :
|
||||
std::optional<BMCustomDataCopyMap>{
|
||||
CustomData_bmesh_copy_map_calc(bm_src->vdata, bm_dst->vdata)};
|
||||
const std::optional<BMCustomDataCopyMap> cd_edge_map =
|
||||
(bm_src == bm_dst) ? std::nullopt :
|
||||
std::optional<BMCustomDataCopyMap>{
|
||||
CustomData_bmesh_copy_map_calc(bm_src->edata, bm_dst->edata)};
|
||||
const std::optional<BMCustomDataCopyMap> cd_face_map =
|
||||
(bm_src == bm_dst) ? std::nullopt :
|
||||
std::optional<BMCustomDataCopyMap>{
|
||||
CustomData_bmesh_copy_map_calc(bm_src->pdata, bm_dst->pdata)};
|
||||
const std::optional<BMCustomDataCopyMap> cd_loop_map =
|
||||
(bm_src == bm_dst) ? std::nullopt :
|
||||
std::optional<BMCustomDataCopyMap>{
|
||||
CustomData_bmesh_copy_map_calc(bm_src->ldata, bm_dst->ldata)};
|
||||
|
||||
/* Vertices. */
|
||||
BMVert **verts_dst = MEM_new_array_uninitialized<BMVert *>(verts_src_len, __func__);
|
||||
for (uint i = 0; i < verts_src_len; i++) {
|
||||
BMVert *v_src = verts_src[i];
|
||||
BM_elem_index_set(v_src, i); /* set_dirty! */
|
||||
|
||||
BMVert *v_dst = bm_vert_copy(bm_dst, cd_vert_map, v_src);
|
||||
BM_elem_index_set(v_dst, i); /* set_ok */
|
||||
verts_dst[i] = v_dst;
|
||||
}
|
||||
bm_src->elem_index_dirty |= BM_VERT;
|
||||
bm_dst->elem_index_dirty &= ~BM_VERT;
|
||||
|
||||
/* Edges. */
|
||||
BMEdge **edges_dst = MEM_new_array_uninitialized<BMEdge *>(edges_src_len, __func__);
|
||||
for (uint i = 0; i < edges_src_len; i++) {
|
||||
BMEdge *e_src = edges_src[i];
|
||||
BM_elem_index_set(e_src, i); /* set_dirty! */
|
||||
|
||||
BMEdge *e_dst = bm_edge_copy_with_arrays(bm_dst, cd_edge_map, e_src, verts_dst);
|
||||
BM_elem_index_set(e_dst, i);
|
||||
edges_dst[i] = e_dst;
|
||||
}
|
||||
bm_src->elem_index_dirty |= BM_EDGE;
|
||||
bm_dst->elem_index_dirty &= ~BM_EDGE;
|
||||
|
||||
/* Faces. */
|
||||
for (uint i = 0; i < faces_src_len; i++) {
|
||||
BMFace *f_src = faces_src[i];
|
||||
BMFace *f_dst = bm_face_copy_with_arrays(
|
||||
bm_dst, cd_face_map, cd_loop_map, f_src, verts_dst, edges_dst);
|
||||
BM_elem_index_set(f_dst, i);
|
||||
}
|
||||
bm_dst->elem_index_dirty &= ~BM_FACE;
|
||||
|
||||
/* Cleanup. */
|
||||
MEM_delete(verts_dst);
|
||||
MEM_delete(edges_dst);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,27 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Geometry must be completely isolated.
|
||||
*/
|
||||
void BM_mesh_copy_arrays(BMesh *bm_src,
|
||||
BMesh *bm_dst,
|
||||
BMVert **verts_src,
|
||||
uint verts_src_len,
|
||||
BMEdge **edges_src,
|
||||
uint edges_src_len,
|
||||
BMFace **faces_src,
|
||||
uint faces_src_len);
|
||||
|
||||
} // namespace blender
|
||||
2400
blender-5.2.0/source/blender/bmesh/intern/bmesh_mesh_normals.cc
Normal file
2400
blender-5.2.0/source/blender/bmesh/intern/bmesh_mesh_normals.cc
Normal file
File diff suppressed because it is too large
Load Diff
119
blender-5.2.0/source/blender/bmesh/intern/bmesh_mesh_normals.hh
Normal file
119
blender-5.2.0/source/blender/bmesh/intern/bmesh_mesh_normals.hh
Normal file
@@ -0,0 +1,119 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMPartialUpdate;
|
||||
|
||||
struct BMeshNormalsUpdate_Params {
|
||||
/**
|
||||
* When calculating tessellation as well as normals, tessellate & calculate face normals
|
||||
* for improved performance. See #BMeshCalcTessellation_Params
|
||||
*/
|
||||
bool face_normals;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief BMesh Compute Normals
|
||||
*
|
||||
* Updates the normals of a mesh.
|
||||
*/
|
||||
void BM_mesh_normals_update_ex(BMesh *bm, const BMeshNormalsUpdate_Params *param);
|
||||
void BM_mesh_normals_update(BMesh *bm);
|
||||
/**
|
||||
* A version of #BM_mesh_normals_update that updates a subset of geometry,
|
||||
* used to avoid the overhead of updating everything.
|
||||
*/
|
||||
void BM_mesh_normals_update_with_partial_ex(BMesh *bm,
|
||||
const BMPartialUpdate *bmpinfo,
|
||||
const BMeshNormalsUpdate_Params *param);
|
||||
void BM_mesh_normals_update_with_partial(BMesh *bm, const BMPartialUpdate *bmpinfo);
|
||||
|
||||
/**
|
||||
* \brief BMesh Compute Normals from/to external data.
|
||||
*
|
||||
* Computes the vertex normals of a mesh into vnos,
|
||||
* using given vertex coordinates (vcos) and polygon normals (fnos).
|
||||
*/
|
||||
void BM_verts_calc_normal_vcos(BMesh *bm,
|
||||
Span<float3> fnos,
|
||||
Span<float3> vcos,
|
||||
MutableSpan<float3> vnos);
|
||||
/**
|
||||
* \brief BMesh Compute Loop Normals from/to external data.
|
||||
*
|
||||
* Compute custom normals, i.e. vertex normals associated with each poly (hence 'loop normals').
|
||||
* Useful to materialize sharp edges (or non-smooth faces) without actually modifying the geometry
|
||||
* (splitting edges).
|
||||
*/
|
||||
void BM_loops_calc_normal_vcos(BMesh *bm,
|
||||
Span<float3> vcos,
|
||||
Span<float3> vnos,
|
||||
Span<float3> fnos,
|
||||
bool use_split_normals,
|
||||
MutableSpan<float3> r_lnos,
|
||||
MLoopNorSpaceArray *r_lnors_spacearr,
|
||||
short (*clnors_data)[2],
|
||||
int cd_loop_clnors_offset,
|
||||
bool do_rebuild);
|
||||
|
||||
/**
|
||||
* Check whether given loop is part of an unknown-so-far cyclic smooth fan, or not.
|
||||
* Needed because cyclic smooth fans have no obvious 'entry point',
|
||||
* and yet we need to walk them once, and only once.
|
||||
*/
|
||||
bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr);
|
||||
void BM_lnorspacearr_store(BMesh *bm, MutableSpan<float3> r_lnors);
|
||||
void BM_lnorspace_invalidate(BMesh *bm, bool do_invalidate_all);
|
||||
void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor);
|
||||
/**
|
||||
* \warning This function sets #BM_ELEM_TAG on loops & edges via #bm_mesh_loops_calc_normals,
|
||||
* take care to run this before setting up tags.
|
||||
*/
|
||||
void BM_lnorspace_update(BMesh *bm);
|
||||
void BM_normals_loops_edges_tag(BMesh *bm, bool do_edges);
|
||||
#ifndef NDEBUG
|
||||
void BM_lnorspace_err(BMesh *bm);
|
||||
#endif
|
||||
|
||||
/* Loop Generics */
|
||||
|
||||
/**
|
||||
* Initialize loop data based on a type, overriding the #BMesh::selectmode of `bm`.
|
||||
* This can be useful if a single types selection is preferred,
|
||||
* instead of using mixed modes and the selection history.
|
||||
*/
|
||||
BMLoopNorEditDataArray *BM_loop_normal_editdata_array_init_with_htype(BMesh *bm,
|
||||
bool do_all_loops_of_vert,
|
||||
char htype_override);
|
||||
BMLoopNorEditDataArray *BM_loop_normal_editdata_array_init(BMesh *bm, bool do_all_loops_of_vert);
|
||||
void BM_loop_normal_editdata_array_free(BMLoopNorEditDataArray *lnors_ed_arr);
|
||||
|
||||
/**
|
||||
* \warning This function sets #BM_ELEM_TAG on loops & edges via #bm_mesh_loops_calc_normals,
|
||||
* take care to run this before setting up tags.
|
||||
*/
|
||||
bool BM_custom_loop_normals_to_vector_layer(BMesh *bm);
|
||||
void BM_custom_loop_normals_from_vector_layer(BMesh *bm, bool add_sharp_edges);
|
||||
|
||||
/**
|
||||
* Define sharp edges as needed to mimic auto-smooth from angle threshold.
|
||||
*
|
||||
* Used when defining an empty custom loop normals data layer,
|
||||
* to keep same shading as with auto-smooth!
|
||||
*/
|
||||
void BM_edges_sharp_from_angle_set(BMesh *bm, float split_angle);
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,321 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* Generate data needed for partially updating mesh information.
|
||||
* Currently this is used for normals and tessellation.
|
||||
*
|
||||
* Transform is the obvious use case where there is no need to update normals or tessellation
|
||||
* for geometry which has not been modified.
|
||||
*
|
||||
* In the future this could be integrated into GPU updates too.
|
||||
*
|
||||
* Kinds of Partial Geometry
|
||||
* =========================
|
||||
*
|
||||
* All Tagged
|
||||
* ----------
|
||||
* Operate on everything that's tagged as well as connected geometry.
|
||||
* see: #BM_mesh_partial_create_from_verts
|
||||
*
|
||||
* Grouped
|
||||
* -------
|
||||
* Operate on everything that is connected to both tagged and un-tagged.
|
||||
* see: #BM_mesh_partial_create_from_verts_group_single
|
||||
*
|
||||
* Reduces computations when transforming isolated regions.
|
||||
*
|
||||
* Optionally support multiple groups since axis-mirror (for example)
|
||||
* will transform vertices in different directions, as well as keeping centered vertices.
|
||||
* see: #BM_mesh_partial_create_from_verts_group_multi
|
||||
*
|
||||
* \note Others can be added as needed.
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_bit_vector.hh"
|
||||
#include "BLI_math_base.h"
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
BLI_INLINE bool partial_elem_vert_ensure(BMPartialUpdate *bmpinfo,
|
||||
MutableBitSpan verts_tag,
|
||||
BMVert *v)
|
||||
{
|
||||
const int i = BM_elem_index_get(v);
|
||||
if (!verts_tag[i]) {
|
||||
verts_tag[i].set();
|
||||
bmpinfo->verts.append(v);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
BLI_INLINE bool partial_elem_face_ensure(BMPartialUpdate *bmpinfo,
|
||||
MutableBitSpan faces_tag,
|
||||
BMFace *f)
|
||||
{
|
||||
const int i = BM_elem_index_get(f);
|
||||
if (!faces_tag[i]) {
|
||||
faces_tag[i].set();
|
||||
bmpinfo->faces.append(f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
BMPartialUpdate *BM_mesh_partial_create_from_verts(BMesh &bm,
|
||||
const BMPartialUpdate_Params ¶ms,
|
||||
const BitSpan verts_mask,
|
||||
const int verts_mask_count)
|
||||
{
|
||||
/* The caller is doing something wrong if this isn't the case. */
|
||||
BLI_assert(verts_mask_count <= bm.totvert);
|
||||
|
||||
BMPartialUpdate *bmpinfo = MEM_new<BMPartialUpdate>(__func__);
|
||||
|
||||
/* Reserve more edges than vertices since it's common for a grid topology
|
||||
* to use around twice as many edges as vertices. */
|
||||
const int default_verts_len_alloc = verts_mask_count;
|
||||
const int default_faces_len_alloc = min_ii(bm.totface, verts_mask_count);
|
||||
|
||||
/* Allocate tags instead of using #BM_ELEM_TAG because the caller may already be using tags.
|
||||
* Further, walking over all geometry to clear the tags isn't so efficient. */
|
||||
BitVector<> verts_tag;
|
||||
BitVector<> faces_tag;
|
||||
|
||||
/* Set vert inline. */
|
||||
BM_mesh_elem_index_ensure(&bm, BM_FACE);
|
||||
|
||||
if (params.do_normals || params.do_tessellate) {
|
||||
/* - Extend to all vertices connected faces:
|
||||
* In the case of tessellation this is enough.
|
||||
*
|
||||
* In the case of vertex normal calculation,
|
||||
* All the relevant connectivity data can be accessed from the faces
|
||||
* (there is no advantage in storing connected edges or vertices in this pass).
|
||||
*
|
||||
* NOTE: In the future it may be useful to differentiate between vertices
|
||||
* that are directly marked (by the filter function when looping over all vertices).
|
||||
* And vertices marked from indirect connections.
|
||||
* This would require an extra tag array, so avoid this unless it's needed.
|
||||
*/
|
||||
|
||||
/* Faces. */
|
||||
bmpinfo->faces.reserve(default_faces_len_alloc);
|
||||
faces_tag.resize(bm.totface);
|
||||
|
||||
BMVert *v;
|
||||
BMIter iter;
|
||||
int i;
|
||||
BM_ITER_MESH_INDEX (v, &iter, &bm, BM_VERTS_OF_MESH, i) {
|
||||
BM_elem_index_set(v, i); /* set_inline */
|
||||
if (!verts_mask[i]) {
|
||||
continue;
|
||||
}
|
||||
BMEdge *e_iter = v->e;
|
||||
if (e_iter != nullptr) {
|
||||
/* Loop over edges. */
|
||||
BMEdge *e_first = v->e;
|
||||
do {
|
||||
BMLoop *l_iter = e_iter->l;
|
||||
if (e_iter->l != nullptr) {
|
||||
BMLoop *l_first = e_iter->l;
|
||||
/* Loop over radial loops. */
|
||||
do {
|
||||
if (l_iter->v == v) {
|
||||
partial_elem_face_ensure(bmpinfo, faces_tag, l_iter->f);
|
||||
}
|
||||
} while ((l_iter = l_iter->radial_next) != l_first);
|
||||
}
|
||||
} while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (params.do_normals) {
|
||||
/* - Extend to all faces vertices:
|
||||
* Any changes to the faces normal needs to update all surrounding vertices.
|
||||
*
|
||||
* - Extend to all these vertices connected edges:
|
||||
* These and needed to access those vertices edge vectors in normal calculation logic.
|
||||
*/
|
||||
|
||||
/* Vertices. */
|
||||
bmpinfo->verts.reserve(default_verts_len_alloc);
|
||||
verts_tag.resize(bm.totvert);
|
||||
|
||||
for (const BMFace *f : bmpinfo->faces) {
|
||||
BMLoop *l_iter, *l_first;
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
partial_elem_vert_ensure(bmpinfo, verts_tag, l_iter->v);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
}
|
||||
|
||||
bmpinfo->params = params;
|
||||
|
||||
return bmpinfo;
|
||||
}
|
||||
|
||||
BMPartialUpdate *BM_mesh_partial_create_from_verts_group_single(
|
||||
BMesh &bm,
|
||||
const BMPartialUpdate_Params ¶ms,
|
||||
const BitSpan verts_mask,
|
||||
const int verts_mask_count)
|
||||
{
|
||||
BMPartialUpdate *bmpinfo = MEM_new<BMPartialUpdate>(__func__);
|
||||
|
||||
BitVector<> verts_tag;
|
||||
BitVector<> faces_tag;
|
||||
|
||||
int face_tag_loop_len = 0;
|
||||
|
||||
if (params.do_normals || params.do_tessellate) {
|
||||
faces_tag.resize(bm.totface);
|
||||
|
||||
BMFace *f;
|
||||
BMIter iter;
|
||||
int i;
|
||||
BM_ITER_MESH_INDEX (f, &iter, &bm, BM_FACES_OF_MESH, i) {
|
||||
enum Side { SIDE_A = (1 << 0), SIDE_B = (1 << 1) } side_flag = Side(0);
|
||||
BM_elem_index_set(f, i); /* set_inline */
|
||||
BMLoop *l_iter, *l_first;
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
const int j = BM_elem_index_get(l_iter->v);
|
||||
side_flag = Side(side_flag | (verts_mask[j].test() ? SIDE_A : SIDE_B));
|
||||
if (UNLIKELY(side_flag == (SIDE_A | SIDE_B))) {
|
||||
partial_elem_face_ensure(bmpinfo, faces_tag, f);
|
||||
face_tag_loop_len += f->len;
|
||||
break;
|
||||
}
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
}
|
||||
|
||||
if (params.do_normals) {
|
||||
/* Extend to all faces vertices:
|
||||
* Any changes to the faces normal needs to update all surrounding vertices. */
|
||||
|
||||
/* Over allocate using the total number of face loops. */
|
||||
bmpinfo->verts.reserve(min_ii(bm.totvert, max_ii(1, face_tag_loop_len)));
|
||||
verts_tag.resize(bm.totvert);
|
||||
|
||||
for (BMFace *f : bmpinfo->faces) {
|
||||
BMLoop *l_iter, *l_first;
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
partial_elem_vert_ensure(bmpinfo, verts_tag, l_iter->v);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
|
||||
/* Loose vertex support, these need special handling as loose normals depend on location. */
|
||||
if (bmpinfo->verts.size() < verts_mask_count) {
|
||||
BMVert *v;
|
||||
BMIter iter;
|
||||
int i;
|
||||
BM_ITER_MESH_INDEX (v, &iter, &bm, BM_VERTS_OF_MESH, i) {
|
||||
if (verts_mask[i] && (BM_vert_find_first_loop(v) == nullptr)) {
|
||||
partial_elem_vert_ensure(bmpinfo, verts_tag, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bmpinfo->params = params;
|
||||
|
||||
return bmpinfo;
|
||||
}
|
||||
|
||||
BMPartialUpdate *BM_mesh_partial_create_from_verts_group_multi(
|
||||
BMesh &bm,
|
||||
const BMPartialUpdate_Params ¶ms,
|
||||
const Span<int> verts_group,
|
||||
const int verts_group_count)
|
||||
{
|
||||
/* Provide a quick way of visualizing which faces are being manipulated. */
|
||||
// #define DEBUG_MATERIAL
|
||||
|
||||
BMPartialUpdate *bmpinfo = MEM_new<BMPartialUpdate>(__func__);
|
||||
|
||||
BitVector<> verts_tag;
|
||||
BitVector<> faces_tag;
|
||||
|
||||
int face_tag_loop_len = 0;
|
||||
|
||||
if (params.do_normals || params.do_tessellate) {
|
||||
faces_tag.resize(bm.totface);
|
||||
|
||||
BMFace *f;
|
||||
BMIter iter;
|
||||
int i;
|
||||
BM_ITER_MESH_INDEX (f, &iter, &bm, BM_FACES_OF_MESH, i) {
|
||||
BM_elem_index_set(f, i); /* set_inline */
|
||||
BMLoop *l_iter, *l_first;
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
const int group_test = verts_group[BM_elem_index_get(l_iter->prev->v)];
|
||||
#ifdef DEBUG_MATERIAL
|
||||
f->mat_nr = 0;
|
||||
#endif
|
||||
do {
|
||||
const int group_iter = verts_group[BM_elem_index_get(l_iter->v)];
|
||||
if (UNLIKELY((group_iter != group_test) || (group_iter == -1))) {
|
||||
partial_elem_face_ensure(bmpinfo, faces_tag, f);
|
||||
face_tag_loop_len += f->len;
|
||||
#ifdef DEBUG_MATERIAL
|
||||
f->mat_nr = 1;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
}
|
||||
|
||||
if (params.do_normals) {
|
||||
/* Extend to all faces vertices:
|
||||
* Any changes to the faces normal needs to update all surrounding vertices. */
|
||||
|
||||
/* Over allocate using the total number of face loops. */
|
||||
bmpinfo->verts.reserve(min_ii(bm.totvert, max_ii(1, face_tag_loop_len)));
|
||||
verts_tag.resize(bm.totvert);
|
||||
|
||||
for (BMFace *f : bmpinfo->faces) {
|
||||
BMLoop *l_iter, *l_first;
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
partial_elem_vert_ensure(bmpinfo, verts_tag, l_iter->v);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
|
||||
/* Loose vertex support, these need special handling as loose normals depend on location. */
|
||||
if (bmpinfo->verts.size() < verts_group_count) {
|
||||
BMVert *v;
|
||||
BMIter iter;
|
||||
int i;
|
||||
BM_ITER_MESH_INDEX (v, &iter, &bm, BM_VERTS_OF_MESH, i) {
|
||||
if ((verts_group[i] != 0) && (BM_vert_find_first_loop(v) == nullptr)) {
|
||||
partial_elem_vert_ensure(bmpinfo, verts_tag, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bmpinfo->params = params;
|
||||
|
||||
return bmpinfo;
|
||||
}
|
||||
|
||||
void BM_mesh_partial_destroy(BMPartialUpdate *bmpinfo)
|
||||
{
|
||||
MEM_delete(bmpinfo);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,86 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "BLI_bit_span.hh"
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Parameters used to determine which kinds of data needs to be generated.
|
||||
*/
|
||||
struct BMPartialUpdate_Params {
|
||||
bool do_normals;
|
||||
bool do_tessellate;
|
||||
};
|
||||
|
||||
/**
|
||||
* Cached data to speed up partial updates.
|
||||
*
|
||||
* Hints:
|
||||
*
|
||||
* - Avoid creating this data for single updates,
|
||||
* it should be created and reused across multiple updates to gain a significant benefit
|
||||
* (while transforming geometry for example).
|
||||
*
|
||||
* - Partial normal updates use face & loop indices,
|
||||
* setting them to dirty values between updates will slow down normal recalculation.
|
||||
*/
|
||||
struct BMPartialUpdate {
|
||||
Vector<BMVert *> verts;
|
||||
Vector<BMFace *> faces;
|
||||
|
||||
/** Store the parameters used in creation so invalid use can be asserted. */
|
||||
BMPartialUpdate_Params params = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* All Tagged & Connected, see: #BM_mesh_partial_create_from_verts
|
||||
* Operate on everything that's tagged as well as connected geometry.
|
||||
*/
|
||||
[[nodiscard]] BMPartialUpdate *BM_mesh_partial_create_from_verts(
|
||||
BMesh &bm, const BMPartialUpdate_Params ¶ms, BitSpan verts_mask, int verts_mask_count);
|
||||
|
||||
/**
|
||||
* All Connected, operate on all faces that have both tagged and un-tagged vertices.
|
||||
*
|
||||
* Reduces computations when transforming isolated regions.
|
||||
*/
|
||||
[[nodiscard]] BMPartialUpdate *BM_mesh_partial_create_from_verts_group_single(
|
||||
BMesh &bm, const BMPartialUpdate_Params ¶ms, BitSpan verts_mask, int verts_mask_count);
|
||||
|
||||
/**
|
||||
* All Connected, operate on all faces that have vertices in the same group.
|
||||
*
|
||||
* Reduces computations when transforming isolated regions.
|
||||
*
|
||||
* This is a version of #BM_mesh_partial_create_from_verts_group_single
|
||||
* that handles multiple groups instead of a bitmap mask.
|
||||
*
|
||||
* This is needed for example when transform has mirror enabled,
|
||||
* since one side needs to have a different group to the other since a face that has vertices
|
||||
* attached to both won't have an affine transformation.
|
||||
*
|
||||
* \param verts_group: Vertex aligned array of groups.
|
||||
* Values are used as follows:
|
||||
* - >0: Each face is grouped with other faces of the same group.
|
||||
* - 0: Not in a group (don't handle these).
|
||||
* - -1: Don't use grouping logic (include any face that contains a vertex with this group).
|
||||
* \param verts_group_count: The number of non-zero values in `verts_groups`.
|
||||
*/
|
||||
[[nodiscard]] BMPartialUpdate *BM_mesh_partial_create_from_verts_group_multi(
|
||||
BMesh &bm, const BMPartialUpdate_Params ¶ms, Span<int> verts_group, int verts_group_count);
|
||||
|
||||
void BM_mesh_partial_destroy(BMPartialUpdate *bmpinfo) ATTR_NONNULL(1);
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,566 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* This file contains code for polygon tessellation
|
||||
* (creating triangles from polygons).
|
||||
*
|
||||
* \see mesh_tessellate.cc for the #Mesh equivalent of this file.
|
||||
*/
|
||||
|
||||
#include "BLI_heap.h"
|
||||
#include "BLI_math_geom.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_memarena.h"
|
||||
#include "BLI_polyfill_2d.h"
|
||||
#include "BLI_polyfill_2d_beautify.h"
|
||||
#include "BLI_task.h"
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* On systems with 32+ cores,
|
||||
* only a very small number of faces has any advantage single threading (in the 100's).
|
||||
* Note that between 500-2000 quads, the difference isn't so much
|
||||
* (tessellation isn't a bottleneck in this case anyway).
|
||||
* Avoid the slight overhead of using threads in this case.
|
||||
*/
|
||||
#define BM_FACE_TESSELLATE_THREADED_LIMIT 1024
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Default Mesh Tessellation
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* \param face_normal: This will be optimized out as a constant.
|
||||
*/
|
||||
BLI_INLINE void bmesh_calc_tessellation_for_face_impl(std::array<BMLoop *, 3> *looptris,
|
||||
BMFace *efa,
|
||||
MemArena **pf_arena_p,
|
||||
const bool face_normal)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
/* The face normal is used for projecting faces into 2D space for tessellation.
|
||||
* Invalid normals may result in invalid tessellation.
|
||||
* Either `face_normal` should be true or normals should be updated first. */
|
||||
BLI_assert(face_normal || BM_face_is_normal_valid(efa));
|
||||
#endif
|
||||
|
||||
switch (efa->len) {
|
||||
case 3: {
|
||||
/* `0 1 2` -> `0 1 2` */
|
||||
BMLoop *l;
|
||||
BMLoop **l_ptr = looptris[0].data();
|
||||
l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa);
|
||||
l_ptr[1] = l = l->next;
|
||||
l_ptr[2] = l->next;
|
||||
if (face_normal) {
|
||||
normal_tri_v3(efa->no, l_ptr[0]->v->co, l_ptr[1]->v->co, l_ptr[2]->v->co);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
/* `0 1 2 3` -> (`0 1 2`, `0 2 3`) */
|
||||
BMLoop *l;
|
||||
BMLoop **l_ptr_a = looptris[0].data();
|
||||
BMLoop **l_ptr_b = looptris[1].data();
|
||||
(l_ptr_a[0] = l_ptr_b[0] = l = BM_FACE_FIRST_LOOP(efa));
|
||||
(l_ptr_a[1] = l = l->next);
|
||||
(l_ptr_a[2] = l_ptr_b[1] = l = l->next);
|
||||
(l_ptr_b[2] = l->next);
|
||||
|
||||
if (face_normal) {
|
||||
normal_quad_v3(
|
||||
efa->no, l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co);
|
||||
}
|
||||
|
||||
if (UNLIKELY(is_quad_flip_v3_first_third_fast(
|
||||
l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co)))
|
||||
{
|
||||
/* Flip out of degenerate 0-2 state. */
|
||||
l_ptr_a[2] = l_ptr_b[2];
|
||||
l_ptr_b[0] = l_ptr_a[1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (face_normal) {
|
||||
BM_face_calc_normal(efa, efa->no);
|
||||
}
|
||||
|
||||
BMLoop *l_iter, *l_first;
|
||||
BMLoop **l_arr;
|
||||
|
||||
float axis_mat[3][3];
|
||||
float (*projverts)[2];
|
||||
uint(*tris)[3];
|
||||
|
||||
const int tris_len = efa->len - 2;
|
||||
|
||||
MemArena *pf_arena = *pf_arena_p;
|
||||
if (UNLIKELY(pf_arena == nullptr)) {
|
||||
pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
|
||||
}
|
||||
|
||||
tris = static_cast<uint(*)[3]>(BLI_memarena_alloc(pf_arena, sizeof(*tris) * tris_len));
|
||||
l_arr = static_cast<BMLoop **>(BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len));
|
||||
projverts = static_cast<float (*)[2]>(
|
||||
BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len));
|
||||
|
||||
axis_dominant_v3_to_m3_negate(axis_mat, efa->no);
|
||||
|
||||
int i = 0;
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
|
||||
do {
|
||||
l_arr[i] = l_iter;
|
||||
mul_v2_m3v3(projverts[i], axis_mat, l_iter->v->co);
|
||||
i++;
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena);
|
||||
|
||||
for (i = 0; i < tris_len; i++) {
|
||||
BMLoop **l_ptr = looptris[i].data();
|
||||
uint *tri = tris[i];
|
||||
|
||||
l_ptr[0] = l_arr[tri[0]];
|
||||
l_ptr[1] = l_arr[tri[1]];
|
||||
l_ptr[2] = l_arr[tri[2]];
|
||||
}
|
||||
|
||||
BLI_memarena_clear(pf_arena);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bmesh_calc_tessellation_for_face(std::array<BMLoop *, 3> *looptris,
|
||||
BMFace *efa,
|
||||
MemArena **pf_arena_p)
|
||||
{
|
||||
bmesh_calc_tessellation_for_face_impl(looptris, efa, pf_arena_p, false);
|
||||
}
|
||||
|
||||
static void bmesh_calc_tessellation_for_face_with_normal(std::array<BMLoop *, 3> *looptris,
|
||||
BMFace *efa,
|
||||
MemArena **pf_arena_p)
|
||||
{
|
||||
bmesh_calc_tessellation_for_face_impl(looptris, efa, pf_arena_p, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief BM_mesh_calc_tessellation get the looptris and its number from a certain bmesh
|
||||
* \param looptris:
|
||||
*
|
||||
* \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count
|
||||
*/
|
||||
static void bm_mesh_calc_tessellation__single_threaded(
|
||||
BMesh *bm, MutableSpan<std::array<BMLoop *, 3>> looptris, const char face_normals)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
|
||||
#endif
|
||||
|
||||
BMIter iter;
|
||||
BMFace *efa;
|
||||
int i = 0;
|
||||
|
||||
MemArena *pf_arena = nullptr;
|
||||
|
||||
if (face_normals) {
|
||||
BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
|
||||
BLI_assert(efa->len >= 3);
|
||||
BM_face_calc_normal(efa, efa->no);
|
||||
bmesh_calc_tessellation_for_face_with_normal(looptris.data() + i, efa, &pf_arena);
|
||||
i += efa->len - 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
|
||||
BLI_assert(efa->len >= 3);
|
||||
bmesh_calc_tessellation_for_face(looptris.data() + i, efa, &pf_arena);
|
||||
i += efa->len - 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (pf_arena) {
|
||||
BLI_memarena_free(pf_arena);
|
||||
pf_arena = nullptr;
|
||||
}
|
||||
|
||||
BLI_assert(i <= looptris_tot);
|
||||
}
|
||||
|
||||
struct TessellationUserTLS {
|
||||
MemArena *pf_arena;
|
||||
};
|
||||
|
||||
static void bmesh_calc_tessellation_for_face_fn(void *__restrict userdata,
|
||||
MempoolIterData *mp_f,
|
||||
const TaskParallelTLS *__restrict tls)
|
||||
{
|
||||
TessellationUserTLS *tls_data = static_cast<TessellationUserTLS *>(tls->userdata_chunk);
|
||||
std::array<BMLoop *, 3> *looptris = static_cast<std::array<BMLoop *, 3> *>(userdata);
|
||||
BMFace *f = reinterpret_cast<BMFace *>(mp_f);
|
||||
BMLoop *l = BM_FACE_FIRST_LOOP(f);
|
||||
const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2);
|
||||
bmesh_calc_tessellation_for_face(looptris + offset, f, &tls_data->pf_arena);
|
||||
}
|
||||
|
||||
static void bmesh_calc_tessellation_for_face_with_normals_fn(void *__restrict userdata,
|
||||
MempoolIterData *mp_f,
|
||||
const TaskParallelTLS *__restrict tls)
|
||||
{
|
||||
TessellationUserTLS *tls_data = static_cast<TessellationUserTLS *>(tls->userdata_chunk);
|
||||
std::array<BMLoop *, 3> *looptris = static_cast<std::array<BMLoop *, 3> *>(userdata);
|
||||
BMFace *f = reinterpret_cast<BMFace *>(mp_f);
|
||||
BMLoop *l = BM_FACE_FIRST_LOOP(f);
|
||||
const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2);
|
||||
bmesh_calc_tessellation_for_face_with_normal(looptris + offset, f, &tls_data->pf_arena);
|
||||
}
|
||||
|
||||
static void bmesh_calc_tessellation_for_face_free_fn(const void *__restrict /*userdata*/,
|
||||
void *__restrict tls_v)
|
||||
{
|
||||
TessellationUserTLS *tls_data = static_cast<TessellationUserTLS *>(tls_v);
|
||||
if (tls_data->pf_arena) {
|
||||
BLI_memarena_free(tls_data->pf_arena);
|
||||
}
|
||||
}
|
||||
|
||||
static void bm_mesh_calc_tessellation__multi_threaded(
|
||||
BMesh *bm, MutableSpan<std::array<BMLoop *, 3>> looptris, const char face_normals)
|
||||
{
|
||||
BM_mesh_elem_index_ensure(bm, BM_LOOP | BM_FACE);
|
||||
|
||||
TaskParallelSettings settings;
|
||||
TessellationUserTLS tls_dummy = {nullptr};
|
||||
BLI_parallel_mempool_settings_defaults(&settings);
|
||||
settings.userdata_chunk = &tls_dummy;
|
||||
settings.userdata_chunk_size = sizeof(tls_dummy);
|
||||
settings.func_free = bmesh_calc_tessellation_for_face_free_fn;
|
||||
BM_iter_parallel(bm,
|
||||
BM_FACES_OF_MESH,
|
||||
face_normals ? bmesh_calc_tessellation_for_face_with_normals_fn :
|
||||
bmesh_calc_tessellation_for_face_fn,
|
||||
looptris.data(),
|
||||
&settings);
|
||||
}
|
||||
|
||||
void BM_mesh_calc_tessellation_ex(BMesh *bm,
|
||||
MutableSpan<std::array<BMLoop *, 3>> looptris,
|
||||
const BMeshCalcTessellation_Params *params)
|
||||
{
|
||||
if (bm->totface < BM_FACE_TESSELLATE_THREADED_LIMIT) {
|
||||
bm_mesh_calc_tessellation__single_threaded(bm, looptris, params->face_normals);
|
||||
}
|
||||
else {
|
||||
bm_mesh_calc_tessellation__multi_threaded(bm, looptris, params->face_normals);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_mesh_calc_tessellation(BMesh *bm, MutableSpan<std::array<BMLoop *, 3>> looptris)
|
||||
{
|
||||
BMeshCalcTessellation_Params params{};
|
||||
params.face_normals = false;
|
||||
BM_mesh_calc_tessellation_ex(bm, looptris, ¶ms);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Default Tessellation (Partial Updates)
|
||||
* \{ */
|
||||
|
||||
struct PartialTessellationUserData {
|
||||
BMFace *const *faces;
|
||||
MutableSpan<std::array<BMLoop *, 3>> looptris;
|
||||
};
|
||||
|
||||
struct PartialTessellationUserTLS {
|
||||
MemArena *pf_arena;
|
||||
};
|
||||
|
||||
static void bmesh_calc_tessellation_for_face_partial_fn(void *__restrict userdata,
|
||||
const int index,
|
||||
const TaskParallelTLS *__restrict tls)
|
||||
{
|
||||
PartialTessellationUserTLS *tls_data = static_cast<PartialTessellationUserTLS *>(
|
||||
tls->userdata_chunk);
|
||||
PartialTessellationUserData *data = static_cast<PartialTessellationUserData *>(userdata);
|
||||
BMFace *f = data->faces[index];
|
||||
BMLoop *l = BM_FACE_FIRST_LOOP(f);
|
||||
const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2);
|
||||
bmesh_calc_tessellation_for_face(data->looptris.data() + offset, f, &tls_data->pf_arena);
|
||||
}
|
||||
|
||||
static void bmesh_calc_tessellation_for_face_partial_with_normals_fn(
|
||||
void *__restrict userdata, const int index, const TaskParallelTLS *__restrict tls)
|
||||
{
|
||||
PartialTessellationUserTLS *tls_data = static_cast<PartialTessellationUserTLS *>(
|
||||
tls->userdata_chunk);
|
||||
PartialTessellationUserData *data = static_cast<PartialTessellationUserData *>(userdata);
|
||||
BMFace *f = data->faces[index];
|
||||
BMLoop *l = BM_FACE_FIRST_LOOP(f);
|
||||
const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2);
|
||||
bmesh_calc_tessellation_for_face_with_normal(
|
||||
data->looptris.data() + offset, f, &tls_data->pf_arena);
|
||||
}
|
||||
|
||||
static void bmesh_calc_tessellation_for_face_partial_free_fn(const void *__restrict /*userdata*/,
|
||||
void *__restrict tls_v)
|
||||
{
|
||||
PartialTessellationUserTLS *tls_data = static_cast<PartialTessellationUserTLS *>(tls_v);
|
||||
if (tls_data->pf_arena) {
|
||||
BLI_memarena_free(tls_data->pf_arena);
|
||||
}
|
||||
}
|
||||
|
||||
static void bm_mesh_calc_tessellation_with_partial__multi_threaded(
|
||||
MutableSpan<std::array<BMLoop *, 3>> looptris,
|
||||
const BMPartialUpdate *bmpinfo,
|
||||
const BMeshCalcTessellation_Params *params)
|
||||
{
|
||||
const int faces_len = bmpinfo->faces.size();
|
||||
BMFace *const *faces = bmpinfo->faces.data();
|
||||
|
||||
PartialTessellationUserData data{};
|
||||
data.faces = faces;
|
||||
data.looptris = looptris;
|
||||
|
||||
PartialTessellationUserTLS tls_dummy = {nullptr};
|
||||
TaskParallelSettings settings;
|
||||
BLI_parallel_range_settings_defaults(&settings);
|
||||
settings.use_threading = true;
|
||||
settings.userdata_chunk = &tls_dummy;
|
||||
settings.userdata_chunk_size = sizeof(tls_dummy);
|
||||
settings.func_free = bmesh_calc_tessellation_for_face_partial_free_fn;
|
||||
|
||||
BLI_task_parallel_range(0,
|
||||
faces_len,
|
||||
&data,
|
||||
params->face_normals ?
|
||||
bmesh_calc_tessellation_for_face_partial_with_normals_fn :
|
||||
bmesh_calc_tessellation_for_face_partial_fn,
|
||||
&settings);
|
||||
}
|
||||
|
||||
static void bm_mesh_calc_tessellation_with_partial__single_threaded(
|
||||
MutableSpan<std::array<BMLoop *, 3>> looptris,
|
||||
const BMPartialUpdate *bmpinfo,
|
||||
const BMeshCalcTessellation_Params *params)
|
||||
{
|
||||
const int faces_len = bmpinfo->faces.size();
|
||||
BMFace *const *faces = bmpinfo->faces.data();
|
||||
|
||||
MemArena *pf_arena = nullptr;
|
||||
|
||||
if (params->face_normals) {
|
||||
for (int index = 0; index < faces_len; index++) {
|
||||
BMFace *f = faces[index];
|
||||
BMLoop *l = BM_FACE_FIRST_LOOP(f);
|
||||
const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2);
|
||||
bmesh_calc_tessellation_for_face_with_normal(looptris.data() + offset, f, &pf_arena);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int index = 0; index < faces_len; index++) {
|
||||
BMFace *f = faces[index];
|
||||
BMLoop *l = BM_FACE_FIRST_LOOP(f);
|
||||
const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2);
|
||||
bmesh_calc_tessellation_for_face(looptris.data() + offset, f, &pf_arena);
|
||||
}
|
||||
}
|
||||
|
||||
if (pf_arena) {
|
||||
BLI_memarena_free(pf_arena);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_mesh_calc_tessellation_with_partial_ex(BMesh *bm,
|
||||
MutableSpan<std::array<BMLoop *, 3>> looptris,
|
||||
const BMPartialUpdate *bmpinfo,
|
||||
const BMeshCalcTessellation_Params *params)
|
||||
{
|
||||
BLI_assert(bmpinfo->params.do_tessellate);
|
||||
/* While harmless, exit early if there is nothing to do (avoids ensuring the index). */
|
||||
if (UNLIKELY(bmpinfo->faces.is_empty())) {
|
||||
return;
|
||||
}
|
||||
|
||||
BM_mesh_elem_index_ensure(bm, BM_LOOP | BM_FACE);
|
||||
|
||||
if (bmpinfo->faces.size() < BM_FACE_TESSELLATE_THREADED_LIMIT) {
|
||||
bm_mesh_calc_tessellation_with_partial__single_threaded(looptris, bmpinfo, params);
|
||||
}
|
||||
else {
|
||||
bm_mesh_calc_tessellation_with_partial__multi_threaded(looptris, bmpinfo, params);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_mesh_calc_tessellation_with_partial(BMesh *bm,
|
||||
MutableSpan<std::array<BMLoop *, 3>> looptris,
|
||||
const BMPartialUpdate *bmpinfo)
|
||||
{
|
||||
BM_mesh_calc_tessellation_with_partial_ex(bm, looptris, bmpinfo, nullptr);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Beauty Mesh Tessellation
|
||||
*
|
||||
* Avoid degenerate triangles.
|
||||
* \{ */
|
||||
|
||||
static int bmesh_calc_tessellation_for_face_beauty(std::array<BMLoop *, 3> *looptris,
|
||||
BMFace *efa,
|
||||
MemArena **pf_arena_p,
|
||||
Heap **pf_heap_p)
|
||||
{
|
||||
switch (efa->len) {
|
||||
case 3: {
|
||||
BMLoop *l;
|
||||
BMLoop **l_ptr = looptris[0].data();
|
||||
l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa);
|
||||
l_ptr[1] = l = l->next;
|
||||
l_ptr[2] = l->next;
|
||||
return 1;
|
||||
}
|
||||
case 4: {
|
||||
BMLoop *l_v1 = BM_FACE_FIRST_LOOP(efa);
|
||||
BMLoop *l_v2 = l_v1->next;
|
||||
BMLoop *l_v3 = l_v2->next;
|
||||
BMLoop *l_v4 = l_v1->prev;
|
||||
|
||||
/* #BM_verts_calc_rotate_beauty performs excessive checks we don't need!
|
||||
* It's meant for rotating edges, it also calculates a new normal.
|
||||
*
|
||||
* Use #BLI_polyfill_beautify_quad_rotate_calc since we have the normal.
|
||||
*/
|
||||
#if 0
|
||||
const bool split_13 = (BM_verts_calc_rotate_beauty(
|
||||
l_v1->v, l_v2->v, l_v3->v, l_v4->v, 0, 0) < 0.0f);
|
||||
#else
|
||||
float axis_mat[3][3], v_quad[4][2];
|
||||
axis_dominant_v3_to_m3(axis_mat, efa->no);
|
||||
mul_v2_m3v3(v_quad[0], axis_mat, l_v1->v->co);
|
||||
mul_v2_m3v3(v_quad[1], axis_mat, l_v2->v->co);
|
||||
mul_v2_m3v3(v_quad[2], axis_mat, l_v3->v->co);
|
||||
mul_v2_m3v3(v_quad[3], axis_mat, l_v4->v->co);
|
||||
|
||||
const bool split_13 = BLI_polyfill_beautify_quad_rotate_calc(
|
||||
v_quad[0], v_quad[1], v_quad[2], v_quad[3]) < 0.0f;
|
||||
#endif
|
||||
|
||||
BMLoop **l_ptr_a = looptris[0].data();
|
||||
BMLoop **l_ptr_b = looptris[1].data();
|
||||
if (split_13) {
|
||||
l_ptr_a[0] = l_v1;
|
||||
l_ptr_a[1] = l_v2;
|
||||
l_ptr_a[2] = l_v3;
|
||||
|
||||
l_ptr_b[0] = l_v1;
|
||||
l_ptr_b[1] = l_v3;
|
||||
l_ptr_b[2] = l_v4;
|
||||
}
|
||||
else {
|
||||
l_ptr_a[0] = l_v1;
|
||||
l_ptr_a[1] = l_v2;
|
||||
l_ptr_a[2] = l_v4;
|
||||
|
||||
l_ptr_b[0] = l_v2;
|
||||
l_ptr_b[1] = l_v3;
|
||||
l_ptr_b[2] = l_v4;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
default: {
|
||||
MemArena *pf_arena = *pf_arena_p;
|
||||
Heap *pf_heap = *pf_heap_p;
|
||||
if (UNLIKELY(pf_arena == nullptr)) {
|
||||
pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
|
||||
pf_heap = *pf_heap_p = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE);
|
||||
}
|
||||
|
||||
BMLoop *l_iter, *l_first;
|
||||
BMLoop **l_arr;
|
||||
|
||||
float axis_mat[3][3];
|
||||
float (*projverts)[2];
|
||||
uint(*tris)[3];
|
||||
|
||||
const int tris_len = efa->len - 2;
|
||||
|
||||
tris = static_cast<uint(*)[3]>(BLI_memarena_alloc(pf_arena, sizeof(*tris) * tris_len));
|
||||
l_arr = static_cast<BMLoop **>(BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len));
|
||||
projverts = static_cast<float (*)[2]>(
|
||||
BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len));
|
||||
|
||||
axis_dominant_v3_to_m3_negate(axis_mat, efa->no);
|
||||
|
||||
int i = 0;
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
|
||||
do {
|
||||
l_arr[i] = l_iter;
|
||||
mul_v2_m3v3(projverts[i], axis_mat, l_iter->v->co);
|
||||
i++;
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena);
|
||||
|
||||
BLI_polyfill_beautify(projverts, efa->len, tris, pf_arena, pf_heap);
|
||||
|
||||
for (i = 0; i < tris_len; i++) {
|
||||
BMLoop **l_ptr = looptris[i].data();
|
||||
uint *tri = tris[i];
|
||||
|
||||
l_ptr[0] = l_arr[tri[0]];
|
||||
l_ptr[1] = l_arr[tri[1]];
|
||||
l_ptr[2] = l_arr[tri[2]];
|
||||
}
|
||||
|
||||
BLI_memarena_clear(pf_arena);
|
||||
|
||||
return tris_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BM_mesh_calc_tessellation_beauty(BMesh *bm, MutableSpan<std::array<BMLoop *, 3>> looptris)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
|
||||
#endif
|
||||
|
||||
BMIter iter;
|
||||
BMFace *efa;
|
||||
int i = 0;
|
||||
|
||||
MemArena *pf_arena = nullptr;
|
||||
|
||||
/* use_beauty */
|
||||
Heap *pf_heap = nullptr;
|
||||
|
||||
BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
|
||||
BLI_assert(efa->len >= 3);
|
||||
i += bmesh_calc_tessellation_for_face_beauty(looptris.data() + i, efa, &pf_arena, &pf_heap);
|
||||
}
|
||||
|
||||
if (pf_arena) {
|
||||
BLI_memarena_free(pf_arena);
|
||||
|
||||
BLI_heap_free(pf_heap, nullptr);
|
||||
}
|
||||
|
||||
BLI_assert(i <= looptris_tot);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,45 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMPartialUpdate;
|
||||
|
||||
struct BMeshCalcTessellation_Params {
|
||||
/**
|
||||
* When calculating normals as well as tessellation, calculate normals after tessellation
|
||||
* for improved performance. See #BMeshCalcTessellation_Params
|
||||
*/
|
||||
bool face_normals;
|
||||
};
|
||||
|
||||
void BM_mesh_calc_tessellation_ex(BMesh *bm,
|
||||
MutableSpan<std::array<BMLoop *, 3>> looptris,
|
||||
const BMeshCalcTessellation_Params *params);
|
||||
void BM_mesh_calc_tessellation(BMesh *bm, MutableSpan<std::array<BMLoop *, 3>> looptris);
|
||||
|
||||
/**
|
||||
* A version of #BM_mesh_calc_tessellation that avoids degenerate triangles.
|
||||
*/
|
||||
void BM_mesh_calc_tessellation_beauty(BMesh *bm, MutableSpan<std::array<BMLoop *, 3>> looptris);
|
||||
|
||||
void BM_mesh_calc_tessellation_with_partial_ex(BMesh *bm,
|
||||
MutableSpan<std::array<BMLoop *, 3>> looptris,
|
||||
const BMPartialUpdate *bmpinfo,
|
||||
const BMeshCalcTessellation_Params *params);
|
||||
void BM_mesh_calc_tessellation_with_partial(BMesh *bm,
|
||||
MutableSpan<std::array<BMLoop *, 3>> looptris,
|
||||
const BMPartialUpdate *bmpinfo);
|
||||
|
||||
} // namespace blender
|
||||
251
blender-5.2.0/source/blender/bmesh/intern/bmesh_mesh_validate.cc
Normal file
251
blender-5.2.0/source/blender/bmesh/intern/bmesh_mesh_validate.cc
Normal file
@@ -0,0 +1,251 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* BM mesh validation function.
|
||||
*/
|
||||
|
||||
/* debug builds only */
|
||||
#ifndef NDEBUG
|
||||
|
||||
# include "BLI_map.hh"
|
||||
# include "BLI_ordered_edge.hh"
|
||||
# include "BLI_set.hh"
|
||||
# include "BLI_utildefines.h"
|
||||
|
||||
# include "bmesh.hh"
|
||||
|
||||
# include "bmesh_mesh_validate.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* macro which inserts the function name */
|
||||
# if defined __GNUC__
|
||||
# define ERRMSG(format, args...) \
|
||||
{ \
|
||||
fprintf(stderr, "%s: " format ", " AT "\n", __func__, ##args); \
|
||||
errtot++; \
|
||||
} \
|
||||
(void)0
|
||||
# elif defined(_MSVC_TRADITIONAL) && !_MSVC_TRADITIONAL
|
||||
# define ERRMSG(format, ...) \
|
||||
{ \
|
||||
fprintf(stderr, "%s: " format ", " AT "\n", __func__, ##__VA_ARGS__); \
|
||||
errtot++; \
|
||||
} \
|
||||
(void)0
|
||||
# else
|
||||
# define ERRMSG(format, ...) \
|
||||
{ \
|
||||
fprintf(stderr, "%s: " format ", " AT "\n", __func__, __VA_ARGS__); \
|
||||
errtot++; \
|
||||
} \
|
||||
(void)0
|
||||
# endif
|
||||
|
||||
template<> struct DefaultHash<Set<Vector<int>>> {
|
||||
uint64_t operator()(const Vector<int> &value) const
|
||||
{
|
||||
uint64_t hash = 0;
|
||||
for (const int v : value) {
|
||||
hash = get_default_hash(hash, v);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
bool BM_mesh_is_valid(BMesh *bm)
|
||||
{
|
||||
Map<OrderedEdge, BMEdge *> edge_hash;
|
||||
edge_hash.reserve(bm->totedge);
|
||||
int errtot;
|
||||
|
||||
BMIter iter;
|
||||
BMVert *v;
|
||||
BMEdge *e;
|
||||
BMFace *f;
|
||||
|
||||
int i, j;
|
||||
|
||||
errtot = -1; /* 'ERRMSG' next line will set at zero */
|
||||
fprintf(stderr, "\n");
|
||||
ERRMSG("This is a debugging function and not intended for general use, running slow test!");
|
||||
|
||||
/* force recalc, even if tagged as valid, since this mesh is suspect! */
|
||||
bm->elem_index_dirty |= BM_ALL;
|
||||
BM_mesh_elem_index_ensure(bm, BM_ALL);
|
||||
|
||||
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
|
||||
if (BM_elem_flag_test(v, BM_ELEM_SELECT | BM_ELEM_HIDDEN) == (BM_ELEM_SELECT | BM_ELEM_HIDDEN))
|
||||
{
|
||||
ERRMSG("vert %d: is hidden and selected", i);
|
||||
}
|
||||
|
||||
if (v->e) {
|
||||
if (!BM_vert_in_edge(v->e, v)) {
|
||||
ERRMSG("vert %d: is not in its referenced edge: %d", i, BM_elem_index_get(v->e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* check edges */
|
||||
BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
|
||||
if (e->v1 == e->v2) {
|
||||
ERRMSG("edge %d: duplicate index: %d", i, BM_elem_index_get(e->v1));
|
||||
}
|
||||
|
||||
/* Build edge-hash at the same time. */
|
||||
edge_hash.add_or_modify(
|
||||
{BM_elem_index_get(e->v1), BM_elem_index_get(e->v2)},
|
||||
[&](BMEdge **value) { *value = e; },
|
||||
[&](BMEdge **value) {
|
||||
ERRMSG("edge %d, %d: are duplicates", i, BM_elem_index_get(*value));
|
||||
});
|
||||
}
|
||||
|
||||
/* edge radial structure */
|
||||
BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
|
||||
if (BM_elem_flag_test(e, BM_ELEM_SELECT | BM_ELEM_HIDDEN) == (BM_ELEM_SELECT | BM_ELEM_HIDDEN))
|
||||
{
|
||||
ERRMSG("edge %d: is hidden and selected", i);
|
||||
}
|
||||
|
||||
if (e->l) {
|
||||
BMLoop *l_iter;
|
||||
BMLoop *l_first;
|
||||
|
||||
j = 0;
|
||||
|
||||
l_iter = l_first = e->l;
|
||||
/* we could do more checks here, but save for face checks */
|
||||
do {
|
||||
if (l_iter->e != e) {
|
||||
ERRMSG("edge %d: has invalid loop, loop is of face %d", i, BM_elem_index_get(l_iter->f));
|
||||
}
|
||||
else if (BM_vert_in_edge(e, l_iter->v) == false) {
|
||||
ERRMSG("edge %d: has invalid loop with vert not in edge, loop is of face %d",
|
||||
i,
|
||||
BM_elem_index_get(l_iter->f));
|
||||
}
|
||||
else if (BM_vert_in_edge(e, l_iter->next->v) == false) {
|
||||
ERRMSG("edge %d: has invalid loop with next vert not in edge, loop is of face %d",
|
||||
i,
|
||||
BM_elem_index_get(l_iter->f));
|
||||
}
|
||||
} while ((l_iter = l_iter->radial_next) != l_first);
|
||||
}
|
||||
}
|
||||
|
||||
/* face structure */
|
||||
Map<Vector<int>, int> face_map;
|
||||
BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
|
||||
BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
|
||||
BMLoop *l_iter;
|
||||
|
||||
if (BM_elem_flag_test(f, BM_ELEM_SELECT | BM_ELEM_HIDDEN) == (BM_ELEM_SELECT | BM_ELEM_HIDDEN))
|
||||
{
|
||||
ERRMSG("face %d: is hidden and selected", i);
|
||||
}
|
||||
|
||||
j = 0;
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
BM_elem_flag_disable(l_iter, BM_ELEM_INTERNAL_TAG);
|
||||
BM_elem_flag_disable(l_iter->v, BM_ELEM_INTERNAL_TAG);
|
||||
BM_elem_flag_disable(l_iter->e, BM_ELEM_INTERNAL_TAG);
|
||||
j++;
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
const int f_len = j;
|
||||
if (f_len != f->len) {
|
||||
ERRMSG("face %d: has length of %d but should be %d", i, f->len, f_len);
|
||||
}
|
||||
|
||||
/* Store the loop with the minimum index to create a list of vertex indices. */
|
||||
BMLoop *l_vert_min = l_first;
|
||||
j = 0;
|
||||
l_iter = l_first;
|
||||
do {
|
||||
if (BM_elem_flag_test(l_iter, BM_ELEM_INTERNAL_TAG)) {
|
||||
ERRMSG("face %d: has duplicate loop at corner: %d", i, j);
|
||||
}
|
||||
if (BM_elem_flag_test(l_iter->v, BM_ELEM_INTERNAL_TAG)) {
|
||||
ERRMSG(
|
||||
"face %d: has duplicate vert: %d, at corner: %d", i, BM_elem_index_get(l_iter->v), j);
|
||||
}
|
||||
if (BM_elem_flag_test(l_iter->e, BM_ELEM_INTERNAL_TAG)) {
|
||||
ERRMSG(
|
||||
"face %d: has duplicate edge: %d, at corner: %d", i, BM_elem_index_get(l_iter->e), j);
|
||||
}
|
||||
|
||||
/* adjacent data checks */
|
||||
if (l_iter->f != f) {
|
||||
ERRMSG("face %d: has loop that points to face: %d at corner: %d",
|
||||
i,
|
||||
BM_elem_index_get(l_iter->f),
|
||||
j);
|
||||
}
|
||||
if (l_iter != l_iter->prev->next) {
|
||||
ERRMSG("face %d: has invalid 'prev/next' at corner: %d", i, j);
|
||||
}
|
||||
if (l_iter != l_iter->next->prev) {
|
||||
ERRMSG("face %d: has invalid 'next/prev' at corner: %d", i, j);
|
||||
}
|
||||
if (l_iter != l_iter->radial_prev->radial_next) {
|
||||
ERRMSG("face %d: has invalid 'radial_prev/radial_next' at corner: %d", i, j);
|
||||
}
|
||||
if (l_iter != l_iter->radial_next->radial_prev) {
|
||||
ERRMSG("face %d: has invalid 'radial_next/radial_prev' at corner: %d", i, j);
|
||||
}
|
||||
|
||||
BM_elem_flag_enable(l_iter, BM_ELEM_INTERNAL_TAG);
|
||||
BM_elem_flag_enable(l_iter->v, BM_ELEM_INTERNAL_TAG);
|
||||
BM_elem_flag_enable(l_iter->e, BM_ELEM_INTERNAL_TAG);
|
||||
|
||||
if (BM_elem_index_get(l_iter->v) < BM_elem_index_get(l_vert_min->v)) {
|
||||
l_vert_min = l_iter;
|
||||
}
|
||||
j++;
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
/* Store ordered face verts, walking over the lowest index first
|
||||
* so faces with flipped winding still match. */
|
||||
Vector<int> face_verts;
|
||||
face_verts.reserve(f_len);
|
||||
if (BM_elem_index_get(l_vert_min->next->v) < BM_elem_index_get(l_vert_min->prev->v)) {
|
||||
l_iter = l_vert_min;
|
||||
do {
|
||||
face_verts.append_unchecked(BM_elem_index_get(l_iter->v));
|
||||
} while ((l_iter = l_iter->next) != l_vert_min);
|
||||
}
|
||||
else {
|
||||
l_iter = l_vert_min;
|
||||
do {
|
||||
face_verts.append_unchecked(BM_elem_index_get(l_iter->v));
|
||||
} while ((l_iter = l_iter->prev) != l_vert_min);
|
||||
}
|
||||
|
||||
face_map.add_or_modify(
|
||||
std::move(face_verts),
|
||||
[&](int *value) { *value = i; },
|
||||
[&](const int *value) { ERRMSG("face %d: duplicate of %d", i, *value); });
|
||||
|
||||
/* leave elements un-tagged, not essential but nice to avoid unintended dirty tag use later. */
|
||||
do {
|
||||
BM_elem_flag_disable(l_iter, BM_ELEM_INTERNAL_TAG);
|
||||
BM_elem_flag_disable(l_iter->v, BM_ELEM_INTERNAL_TAG);
|
||||
BM_elem_flag_disable(l_iter->e, BM_ELEM_INTERNAL_TAG);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
|
||||
const bool is_valid = (errtot == 0);
|
||||
ERRMSG("Finished - errors %d", errtot);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,23 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Check of this #BMesh is valid,
|
||||
* this function can be slow since its intended to help with debugging.
|
||||
*
|
||||
* \return true when the mesh is valid.
|
||||
*/
|
||||
bool BM_mesh_is_valid(BMesh *bm);
|
||||
|
||||
} // namespace blender
|
||||
903
blender-5.2.0/source/blender/bmesh/intern/bmesh_mods.cc
Normal file
903
blender-5.2.0/source/blender/bmesh/intern/bmesh_mods.cc
Normal file
@@ -0,0 +1,903 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* This file contains functions for locally modifying
|
||||
* the topology of existing mesh data. (split, join, flip etc).
|
||||
*/
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "BKE_customdata.hh"
|
||||
|
||||
#include "bmesh.hh"
|
||||
#include "intern/bmesh_private.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
bool BM_vert_dissolve(BMesh *bm, BMVert *v)
|
||||
{
|
||||
/* logic for 3 or more is identical */
|
||||
const int len = BM_vert_edge_count_at_most(v, 3);
|
||||
|
||||
if (len == 1) {
|
||||
BM_vert_kill(bm, v); /* will kill edges too */
|
||||
return true;
|
||||
}
|
||||
if (!BM_vert_is_manifold(v)) {
|
||||
if (!v->e) {
|
||||
BM_vert_kill(bm, v);
|
||||
return true;
|
||||
}
|
||||
if (!v->e->l) {
|
||||
if (len == 2) {
|
||||
return (BM_vert_collapse_edge(bm, v->e, v, true, true, true) != nullptr);
|
||||
}
|
||||
/* used to kill the vertex here, but it may be connected to faces.
|
||||
* so better do nothing */
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (len == 2 && BM_vert_face_count_is_equal(v, 1)) {
|
||||
/* boundary vertex on a face */
|
||||
return (BM_vert_collapse_edge(bm, v->e, v, true, true, true) != nullptr);
|
||||
}
|
||||
return BM_disk_dissolve(bm, v);
|
||||
}
|
||||
|
||||
bool BM_disk_dissolve(BMesh *bm, BMVert *v)
|
||||
{
|
||||
BMEdge *e, *keepedge = nullptr, *baseedge = nullptr;
|
||||
int len = 0;
|
||||
|
||||
if (!BM_vert_is_manifold(v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (v->e) {
|
||||
/* v->e we keep, what else */
|
||||
e = v->e;
|
||||
do {
|
||||
e = bmesh_disk_edge_next(e, v);
|
||||
if (!BM_edge_share_face_check(e, v->e)) {
|
||||
keepedge = e;
|
||||
baseedge = v->e;
|
||||
break;
|
||||
}
|
||||
len++;
|
||||
} while (e != v->e);
|
||||
}
|
||||
|
||||
/* this code for handling 2 and 3-valence verts
|
||||
* may be totally bad */
|
||||
if (keepedge == nullptr && len == 3) {
|
||||
#if 0
|
||||
/* handle specific case for three-valence. solve it by
|
||||
* increasing valence to four. this may be hackish. */
|
||||
BMLoop *l_a = BM_face_vert_share_loop(e->l->f, v);
|
||||
BMLoop *l_b = (e->l->v == v) ? e->l->next : e->l;
|
||||
|
||||
if (!BM_face_split(bm, e->l->f, l_a, l_b, nullptr, nullptr, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!BM_disk_dissolve(bm, v)) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
BMFace *f_double;
|
||||
|
||||
if (UNLIKELY(!BM_faces_join_pair(bm, e->l, e->l->radial_next, true, &f_double))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* See #BM_faces_join note on callers asserting when `r_double` is non-null. */
|
||||
BLI_assert_msg(f_double == nullptr,
|
||||
"Doubled face detected at " AT ". Resulting mesh may be corrupt.");
|
||||
|
||||
if (UNLIKELY(!BM_vert_collapse_faces(bm, v->e, v, 1.0, true, false, true, true))) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
if (keepedge == nullptr && len == 2) {
|
||||
/* collapse the vertex */
|
||||
e = BM_vert_collapse_faces(bm, v->e, v, 1.0, true, true, true, true);
|
||||
|
||||
if (!e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* handle two-valence */
|
||||
if (e->l != e->l->radial_next) {
|
||||
BMFace *f_double;
|
||||
|
||||
if (!BM_faces_join_pair(bm, e->l, e->l->radial_next, true, &f_double)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* See #BM_faces_join note on callers asserting when `r_double` is non-null. */
|
||||
BLI_assert_msg(f_double == nullptr,
|
||||
"Doubled face detected at " AT ". Resulting mesh may be corrupt.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keepedge) {
|
||||
bool done = false;
|
||||
|
||||
while (!done) {
|
||||
done = true;
|
||||
e = v->e;
|
||||
do {
|
||||
BMFace *f = nullptr;
|
||||
if (BM_edge_is_manifold(e) && (e != baseedge) && (e != keepedge)) {
|
||||
BMFace *f_double;
|
||||
|
||||
f = BM_faces_join_pair(bm, e->l, e->l->radial_next, true, &f_double);
|
||||
/* return if couldn't join faces in manifold
|
||||
* conditions */
|
||||
/* !disabled for testing why bad things happen */
|
||||
if (!f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* See #BM_faces_join note on callers asserting when `r_double` is non-null. */
|
||||
BLI_assert_msg(f_double == nullptr,
|
||||
"Doubled face detected at " AT ". Resulting mesh may be corrupt.");
|
||||
}
|
||||
|
||||
if (f) {
|
||||
done = false;
|
||||
break;
|
||||
}
|
||||
} while ((e = bmesh_disk_edge_next(e, v)) != v->e);
|
||||
}
|
||||
|
||||
/* collapse the vertex */
|
||||
/* NOTE: the baseedge can be a boundary of manifold, use this as join_faces arg. */
|
||||
e = BM_vert_collapse_faces(
|
||||
bm, baseedge, v, 1.0, true, !BM_edge_is_boundary(baseedge), true, true);
|
||||
|
||||
if (!e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (e->l) {
|
||||
/* get remaining two faces */
|
||||
if (e->l != e->l->radial_next) {
|
||||
BMFace *f_double;
|
||||
|
||||
/* join two remaining faces */
|
||||
if (!BM_faces_join_pair(bm, e->l, e->l->radial_next, true, &f_double)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* See #BM_faces_join note on callers asserting when `r_double` is non-null. */
|
||||
BLI_assert_msg(f_double == nullptr,
|
||||
"Doubled face detected at " AT ". Resulting mesh may be corrupt.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BMFace *BM_faces_join_pair(
|
||||
BMesh *bm, BMLoop *l_a, BMLoop *l_b, const bool do_del, BMFace **r_double)
|
||||
{
|
||||
BLI_assert((l_a != l_b) && (l_a->e == l_b->e));
|
||||
|
||||
if (l_a->v == l_b->v) {
|
||||
const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
|
||||
bmesh_kernel_loop_reverse(bm, l_b->f, cd_loop_mdisp_offset, true);
|
||||
}
|
||||
|
||||
BMFace *faces[2] = {l_a->f, l_b->f};
|
||||
return BM_faces_join(bm, faces, 2, do_del, r_double);
|
||||
}
|
||||
|
||||
BMFace *BM_face_split(BMesh *bm,
|
||||
BMFace *f,
|
||||
BMLoop *l_a,
|
||||
BMLoop *l_b,
|
||||
BMLoop **r_l,
|
||||
BMEdge *example,
|
||||
const bool no_double)
|
||||
{
|
||||
const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
|
||||
BMFace *f_new, *f_tmp;
|
||||
|
||||
BLI_assert(l_a != l_b);
|
||||
BLI_assert(f == l_a->f && f == l_b->f);
|
||||
BLI_assert(!BM_loop_is_adjacent(l_a, l_b));
|
||||
|
||||
/* could be an assert */
|
||||
if (UNLIKELY(BM_loop_is_adjacent(l_a, l_b)) || UNLIKELY(f != l_a->f || f != l_b->f)) {
|
||||
if (r_l) {
|
||||
*r_l = nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* do we have a multires layer? */
|
||||
if (cd_loop_mdisp_offset != -1) {
|
||||
f_tmp = BM_face_copy(bm, f, false, false);
|
||||
}
|
||||
|
||||
#ifdef USE_BMESH_HOLES
|
||||
f_new = bmesh_kernel_split_face_make_edge(bm, f, l_a, l_b, r_l, nullptr, example, no_double);
|
||||
#else
|
||||
f_new = bmesh_kernel_split_face_make_edge(bm, f, l_a, l_b, r_l, example, no_double);
|
||||
#endif
|
||||
|
||||
if (f_new) {
|
||||
/* handle multires update */
|
||||
if (cd_loop_mdisp_offset != -1) {
|
||||
float f_dst_center[3];
|
||||
float f_src_center[3];
|
||||
|
||||
BM_face_calc_center_median(f_tmp, f_src_center);
|
||||
|
||||
BM_face_calc_center_median(f, f_dst_center);
|
||||
BM_face_interp_multires_ex(bm, f, f_tmp, f_dst_center, f_src_center, cd_loop_mdisp_offset);
|
||||
|
||||
BM_face_calc_center_median(f_new, f_dst_center);
|
||||
BM_face_interp_multires_ex(
|
||||
bm, f_new, f_tmp, f_dst_center, f_src_center, cd_loop_mdisp_offset);
|
||||
|
||||
#if 0
|
||||
/* BM_face_multires_bounds_smooth doesn't flip displacement correct */
|
||||
BM_face_multires_bounds_smooth(bm, f);
|
||||
BM_face_multires_bounds_smooth(bm, f_new);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (cd_loop_mdisp_offset != -1) {
|
||||
BM_face_kill(bm, f_tmp);
|
||||
}
|
||||
|
||||
return f_new;
|
||||
}
|
||||
|
||||
BMFace *BM_face_split_n(BMesh *bm,
|
||||
BMFace *f,
|
||||
BMLoop *l_a,
|
||||
BMLoop *l_b,
|
||||
float cos[][3],
|
||||
int n,
|
||||
BMLoop **r_l,
|
||||
BMEdge *example)
|
||||
{
|
||||
BMFace *f_new, *f_tmp;
|
||||
BMLoop *l_new;
|
||||
BMEdge *e, *e_new;
|
||||
BMVert *v_new;
|
||||
// BMVert *v_a = l_a->v; /* UNUSED */
|
||||
BMVert *v_b = l_b->v;
|
||||
int i, j;
|
||||
|
||||
BLI_assert(l_a != l_b);
|
||||
BLI_assert(f == l_a->f && f == l_b->f);
|
||||
BLI_assert(!((n == 0) && BM_loop_is_adjacent(l_a, l_b)));
|
||||
|
||||
/* could be an assert */
|
||||
if (UNLIKELY((n == 0) && BM_loop_is_adjacent(l_a, l_b)) || UNLIKELY(l_a->f != l_b->f)) {
|
||||
if (r_l) {
|
||||
*r_l = nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
f_tmp = BM_face_copy(bm, f, true, true);
|
||||
|
||||
#ifdef USE_BMESH_HOLES
|
||||
f_new = bmesh_kernel_split_face_make_edge(bm, f, l_a, l_b, &l_new, nullptr, example, false);
|
||||
#else
|
||||
f_new = bmesh_kernel_split_face_make_edge(bm, f, l_a, l_b, &l_new, example, false);
|
||||
#endif
|
||||
/* bmesh_kernel_split_face_make_edge returns in 'l_new'
|
||||
* a Loop for f_new going from 'v_a' to 'v_b'.
|
||||
* The radial_next is for 'f' and goes from 'v_b' to 'v_a'. */
|
||||
|
||||
if (f_new) {
|
||||
e = l_new->e;
|
||||
for (i = 0; i < n; i++) {
|
||||
v_new = bmesh_kernel_split_edge_make_vert(bm, v_b, e, &e_new);
|
||||
BLI_assert(v_new != nullptr);
|
||||
/* bmesh_kernel_split_edge_make_vert returns in 'e_new'
|
||||
* the edge going from 'v_new' to 'v_b'. */
|
||||
copy_v3_v3(v_new->co, cos[i]);
|
||||
|
||||
/* interpolate the loop data for the loops with (v == v_new), using orig face */
|
||||
for (j = 0; j < 2; j++) {
|
||||
BMEdge *e_iter = (j == 0) ? e : e_new;
|
||||
BMLoop *l_iter = e_iter->l;
|
||||
do {
|
||||
if (l_iter->v == v_new) {
|
||||
/* this interpolates both loop and vertex data */
|
||||
BM_loop_interp_from_face(bm, l_iter, f_tmp, true, true);
|
||||
}
|
||||
} while ((l_iter = l_iter->radial_next) != e_iter->l);
|
||||
}
|
||||
e = e_new;
|
||||
}
|
||||
}
|
||||
|
||||
BM_face_verts_kill(bm, f_tmp);
|
||||
|
||||
if (r_l) {
|
||||
*r_l = l_new;
|
||||
}
|
||||
|
||||
return f_new;
|
||||
}
|
||||
|
||||
BMEdge *BM_vert_collapse_faces(BMesh *bm,
|
||||
BMEdge *e_kill,
|
||||
BMVert *v_kill,
|
||||
float fac,
|
||||
const bool do_del,
|
||||
const bool join_faces,
|
||||
const bool kill_degenerate_faces,
|
||||
const bool kill_duplicate_faces)
|
||||
{
|
||||
BMEdge *e_new = nullptr;
|
||||
BMVert *tv = BM_edge_other_vert(e_kill, v_kill);
|
||||
|
||||
BMEdge *e2;
|
||||
BMVert *tv2;
|
||||
|
||||
/* Only intended to be called for 2-valence vertices */
|
||||
BLI_assert(bmesh_disk_count(v_kill) <= 2);
|
||||
|
||||
/* first modify the face loop data */
|
||||
|
||||
if (e_kill->l) {
|
||||
BMLoop *l_iter;
|
||||
const float w[2] = {1.0f - fac, fac};
|
||||
|
||||
l_iter = e_kill->l;
|
||||
do {
|
||||
if (l_iter->v == tv && l_iter->next->v == v_kill) {
|
||||
const void *src[2];
|
||||
BMLoop *tvloop = l_iter;
|
||||
BMLoop *kvloop = l_iter->next;
|
||||
|
||||
src[0] = kvloop->head.data;
|
||||
src[1] = tvloop->head.data;
|
||||
CustomData_bmesh_interp(&bm->ldata, src, w, 2, kvloop->head.data);
|
||||
}
|
||||
} while ((l_iter = l_iter->radial_next) != e_kill->l);
|
||||
}
|
||||
|
||||
/* now interpolate the vertex data */
|
||||
BM_data_interp_from_verts(bm, v_kill, tv, v_kill, fac);
|
||||
|
||||
e2 = bmesh_disk_edge_next(e_kill, v_kill);
|
||||
tv2 = BM_edge_other_vert(e2, v_kill);
|
||||
|
||||
if (join_faces) {
|
||||
BMIter fiter;
|
||||
BMFace *f;
|
||||
|
||||
Vector<BMFace *, BM_DEFAULT_ITER_STACK_SIZE> faces;
|
||||
BM_ITER_ELEM (f, &fiter, v_kill, BM_FACES_OF_VERT) {
|
||||
faces.append(f);
|
||||
}
|
||||
|
||||
if (faces.size() >= 2) {
|
||||
BMFace *f_double;
|
||||
if (BMFace *f2 = BM_faces_join(bm, faces.data(), faces.size(), true, &f_double)) {
|
||||
if (kill_duplicate_faces && (f_double != nullptr)) {
|
||||
BM_face_kill(bm, f_double);
|
||||
}
|
||||
|
||||
BMLoop *l_a, *l_b;
|
||||
|
||||
if ((l_a = BM_face_vert_share_loop(f2, tv)) && (l_b = BM_face_vert_share_loop(f2, tv2))) {
|
||||
BMLoop *l_new;
|
||||
|
||||
if (BM_face_split(bm, f2, l_a, l_b, &l_new, nullptr, false)) {
|
||||
e_new = l_new->e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* single face or no faces */
|
||||
/* same as BM_vert_collapse_edge() however we already
|
||||
* have vars to perform this operation so don't call. */
|
||||
e_new = bmesh_kernel_join_edge_kill_vert(
|
||||
bm, e_kill, v_kill, do_del, true, kill_degenerate_faces, kill_duplicate_faces);
|
||||
|
||||
// e_new = BM_edge_exists(tv, tv2); /* Same as return above. */
|
||||
}
|
||||
|
||||
return e_new;
|
||||
}
|
||||
|
||||
BMEdge *BM_vert_collapse_edge(BMesh *bm,
|
||||
BMEdge *e_kill,
|
||||
BMVert *v_kill,
|
||||
const bool do_del,
|
||||
const bool kill_degenerate_faces,
|
||||
const bool kill_duplicate_faces)
|
||||
{
|
||||
/* nice example implementation but we want loops to have their customdata
|
||||
* accounted for */
|
||||
#if 0
|
||||
BMEdge *e_new = nullptr;
|
||||
|
||||
/* Collapse between 2 edges */
|
||||
|
||||
/* in this case we want to keep all faces and not join them,
|
||||
* rather just get rid of the vertex - see bug #28645. */
|
||||
BMVert *tv = BM_edge_other_vert(e_kill, v_kill);
|
||||
if (tv) {
|
||||
BMEdge *e2 = bmesh_disk_edge_next(e_kill, v_kill);
|
||||
if (e2) {
|
||||
BMVert *tv2 = BM_edge_other_vert(e2, v_kill);
|
||||
if (tv2) {
|
||||
/* only action, other calls here only get the edge to return */
|
||||
e_new = bmesh_kernel_join_edge_kill_vert(
|
||||
bm, e_kill, v_kill, do_del, true, kill_degenerate_faces);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return e_new;
|
||||
#else
|
||||
/* with these args faces are never joined, same as above
|
||||
* but account for loop customdata */
|
||||
return BM_vert_collapse_faces(
|
||||
bm, e_kill, v_kill, 1.0f, do_del, false, kill_degenerate_faces, kill_duplicate_faces);
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef DO_V_INTERP
|
||||
|
||||
BMVert *BM_edge_collapse(
|
||||
BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool kill_degenerate_faces)
|
||||
{
|
||||
return bmesh_kernel_join_vert_kill_edge(bm, e_kill, v_kill, do_del, true, kill_degenerate_faces);
|
||||
}
|
||||
|
||||
BMVert *BM_edge_split(BMesh *bm, BMEdge *e, BMVert *v, BMEdge **r_e, float fac)
|
||||
{
|
||||
BMVert *v_new, *v_other;
|
||||
BMEdge *e_new;
|
||||
|
||||
Vector<BMFace *, 32> oldfaces;
|
||||
const int cd_loop_mdisp_offset = BM_edge_is_wire(e) ?
|
||||
-1 :
|
||||
CustomData_get_offset(&bm->ldata, CD_MDISPS);
|
||||
|
||||
BLI_assert(BM_vert_in_edge(e, v) == true);
|
||||
|
||||
/* do we have a multi-res layer? */
|
||||
if (cd_loop_mdisp_offset != -1) {
|
||||
BMLoop *l = e->l;
|
||||
do {
|
||||
oldfaces.append(l->f);
|
||||
l = l->radial_next;
|
||||
} while (l != e->l);
|
||||
|
||||
/* flag existing faces so we can differentiate oldfaces from new faces */
|
||||
for (int64_t i = 0; i < oldfaces.size(); i++) {
|
||||
BM_ELEM_API_FLAG_ENABLE(oldfaces[i], _FLAG_OVERLAP);
|
||||
oldfaces[i] = BM_face_copy(bm, oldfaces[i], true, true);
|
||||
BM_ELEM_API_FLAG_DISABLE(oldfaces[i], _FLAG_OVERLAP);
|
||||
}
|
||||
}
|
||||
|
||||
v_other = BM_edge_other_vert(e, v);
|
||||
v_new = bmesh_kernel_split_edge_make_vert(bm, v, e, &e_new);
|
||||
if (r_e != nullptr) {
|
||||
*r_e = e_new;
|
||||
}
|
||||
|
||||
BLI_assert(v_new != nullptr);
|
||||
BLI_assert(BM_vert_in_edge(e_new, v) && BM_vert_in_edge(e_new, v_new));
|
||||
BLI_assert(BM_vert_in_edge(e, v_new) && BM_vert_in_edge(e, v_other));
|
||||
|
||||
sub_v3_v3v3(v_new->co, v_other->co, v->co);
|
||||
madd_v3_v3v3fl(v_new->co, v->co, v_new->co, fac);
|
||||
|
||||
e_new->head.hflag = e->head.hflag;
|
||||
BM_elem_attrs_copy(bm, e, e_new);
|
||||
|
||||
/* v->v_new->v2 */
|
||||
BM_data_interp_face_vert_edge(bm, v_other, v, v_new, e, fac);
|
||||
BM_data_interp_from_verts(bm, v, v_other, v_new, fac);
|
||||
|
||||
if (cd_loop_mdisp_offset != -1) {
|
||||
/* interpolate new/changed loop data from copied old faces */
|
||||
for (BMFace *oldface : oldfaces) {
|
||||
float f_center_old[3];
|
||||
|
||||
BM_face_calc_center_median(oldface, f_center_old);
|
||||
|
||||
for (int j = 0; j < 2; j++) {
|
||||
BMEdge *e1 = j ? e_new : e;
|
||||
BMLoop *l = e1->l;
|
||||
|
||||
if (UNLIKELY(!l)) {
|
||||
BMESH_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
do {
|
||||
/* check this is an old face */
|
||||
if (BM_ELEM_API_FLAG_TEST(l->f, _FLAG_OVERLAP)) {
|
||||
float f_center[3];
|
||||
|
||||
BM_face_calc_center_median(l->f, f_center);
|
||||
BM_face_interp_multires_ex(
|
||||
bm, l->f, oldface, f_center, f_center_old, cd_loop_mdisp_offset);
|
||||
}
|
||||
l = l->radial_next;
|
||||
} while (l != e1->l);
|
||||
}
|
||||
}
|
||||
|
||||
/* destroy the old faces */
|
||||
for (BMFace *oldface : oldfaces) {
|
||||
BM_face_verts_kill(bm, oldface);
|
||||
}
|
||||
|
||||
/* fix boundaries a bit, doesn't work too well quite yet */
|
||||
#if 0
|
||||
for (int j = 0; j < 2; j++) {
|
||||
BMEdge *e1 = j ? e_new : e;
|
||||
BMLoop *l, *l2;
|
||||
|
||||
l = e1->l;
|
||||
if (UNLIKELY(!l)) {
|
||||
BMESH_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
do {
|
||||
BM_face_multires_bounds_smooth(bm, l->f);
|
||||
l = l->radial_next;
|
||||
} while (l != e1->l);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return v_new;
|
||||
}
|
||||
|
||||
BMVert *BM_edge_split_n(BMesh *bm, BMEdge *e, int numcuts, BMVert **r_varr)
|
||||
{
|
||||
int i;
|
||||
float percent;
|
||||
BMVert *v_new = nullptr;
|
||||
|
||||
for (i = 0; i < numcuts; i++) {
|
||||
percent = 1.0f / float(numcuts + 1 - i);
|
||||
v_new = BM_edge_split(bm, e, e->v2, nullptr, percent);
|
||||
if (r_varr) {
|
||||
/* fill in reverse order (v1 -> v2) */
|
||||
r_varr[numcuts - i - 1] = v_new;
|
||||
}
|
||||
}
|
||||
return v_new;
|
||||
}
|
||||
|
||||
void BM_edge_verts_swap(BMEdge *e)
|
||||
{
|
||||
std::swap(e->v1, e->v2);
|
||||
std::swap(e->v1_disk_link, e->v2_disk_link);
|
||||
}
|
||||
|
||||
bool BM_edge_calc_rotate(BMEdge *e, const bool ccw, BMLoop **r_l1, BMLoop **r_l2)
|
||||
{
|
||||
BMVert *v1, *v2;
|
||||
BMFace *fa, *fb;
|
||||
|
||||
/* this should have already run */
|
||||
BLI_assert(BM_edge_rotate_check(e) == true);
|
||||
|
||||
/* we know this will work */
|
||||
BM_edge_face_pair(e, &fa, &fb);
|
||||
|
||||
/* so we can use `ccw` variable correctly,
|
||||
* otherwise we could use the edges verts direct */
|
||||
BM_edge_ordered_verts(e, &v1, &v2);
|
||||
|
||||
/* we could swap the verts _or_ the faces, swapping faces
|
||||
* gives more predictable results since that way the next vert
|
||||
* just stitches from face fa / fb */
|
||||
if (!ccw) {
|
||||
std::swap(fa, fb);
|
||||
}
|
||||
BMLoop *l1 = BM_face_other_vert_loop(fb, v2, v1);
|
||||
BMLoop *l2 = BM_face_other_vert_loop(fa, v1, v2);
|
||||
|
||||
/* This occurs when faces share multiple edges next to `e`.
|
||||
* While rare it's not an error, this rotation must be skipped. */
|
||||
if (l1->v == l2->v) [[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
*r_l1 = l1;
|
||||
*r_l2 = l2;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BM_edge_rotate_check(BMEdge *e)
|
||||
{
|
||||
BMFace *fa, *fb;
|
||||
if (BM_edge_face_pair(e, &fa, &fb)) {
|
||||
BMLoop *la, *lb;
|
||||
|
||||
la = BM_face_other_vert_loop(fa, e->v2, e->v1);
|
||||
lb = BM_face_other_vert_loop(fb, e->v2, e->v1);
|
||||
|
||||
/* check that the next vert in both faces isn't the same
|
||||
* (ie - the next edge doesn't share the same faces).
|
||||
* since we can't rotate usefully in this case. */
|
||||
if (la->v == lb->v) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* mirror of the check above but in the opposite direction */
|
||||
la = BM_face_other_vert_loop(fa, e->v1, e->v2);
|
||||
lb = BM_face_other_vert_loop(fb, e->v1, e->v2);
|
||||
|
||||
if (la->v == lb->v) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BM_edge_rotate_check_degenerate(BMEdge *e, BMLoop *l1, BMLoop *l2)
|
||||
{
|
||||
/* NOTE: for these vars 'old' just means initial edge state. */
|
||||
|
||||
float ed_dir_old[3]; /* edge vector */
|
||||
float ed_dir_new[3]; /* edge vector */
|
||||
float ed_dir_new_flip[3]; /* edge vector */
|
||||
|
||||
float ed_dir_v1_old[3];
|
||||
float ed_dir_v2_old[3];
|
||||
|
||||
float ed_dir_v1_new[3];
|
||||
float ed_dir_v2_new[3];
|
||||
|
||||
float cross_old[3];
|
||||
float cross_new[3];
|
||||
|
||||
/* original verts - these will be in the edge 'e' */
|
||||
BMVert *v1_old, *v2_old;
|
||||
|
||||
/* verts from the loops passed */
|
||||
|
||||
BMVert *v1, *v2;
|
||||
/* These are the opposite verts - the verts that _would_ be used if `ccw` was inverted. */
|
||||
BMVert *v1_alt, *v2_alt;
|
||||
|
||||
/* this should have already run */
|
||||
BLI_assert(BM_edge_rotate_check(e) == true);
|
||||
|
||||
BM_edge_ordered_verts(e, &v1_old, &v2_old);
|
||||
|
||||
v1 = l1->v;
|
||||
v2 = l2->v;
|
||||
|
||||
/* get the next vert along */
|
||||
v1_alt = BM_face_other_vert_loop(l1->f, v1_old, v1)->v;
|
||||
v2_alt = BM_face_other_vert_loop(l2->f, v2_old, v2)->v;
|
||||
|
||||
/* normalize all so comparisons are scale independent */
|
||||
|
||||
BLI_assert(BM_edge_exists(v1_old, v1));
|
||||
BLI_assert(BM_edge_exists(v1, v1_alt));
|
||||
|
||||
BLI_assert(BM_edge_exists(v2_old, v2));
|
||||
BLI_assert(BM_edge_exists(v2, v2_alt));
|
||||
|
||||
/* old and new edge vecs */
|
||||
sub_v3_v3v3(ed_dir_old, v1_old->co, v2_old->co);
|
||||
sub_v3_v3v3(ed_dir_new, v1->co, v2->co);
|
||||
normalize_v3(ed_dir_old);
|
||||
normalize_v3(ed_dir_new);
|
||||
|
||||
/* old edge corner vecs */
|
||||
sub_v3_v3v3(ed_dir_v1_old, v1_old->co, v1->co);
|
||||
sub_v3_v3v3(ed_dir_v2_old, v2_old->co, v2->co);
|
||||
normalize_v3(ed_dir_v1_old);
|
||||
normalize_v3(ed_dir_v2_old);
|
||||
|
||||
/* old edge corner vecs */
|
||||
sub_v3_v3v3(ed_dir_v1_new, v1->co, v1_alt->co);
|
||||
sub_v3_v3v3(ed_dir_v2_new, v2->co, v2_alt->co);
|
||||
normalize_v3(ed_dir_v1_new);
|
||||
normalize_v3(ed_dir_v2_new);
|
||||
|
||||
/* compare */
|
||||
cross_v3_v3v3(cross_old, ed_dir_old, ed_dir_v1_old);
|
||||
cross_v3_v3v3(cross_new, ed_dir_new, ed_dir_v1_new);
|
||||
if (dot_v3v3(cross_old, cross_new) < 0.0f) { /* does this flip? */
|
||||
return false;
|
||||
}
|
||||
cross_v3_v3v3(cross_old, ed_dir_old, ed_dir_v2_old);
|
||||
cross_v3_v3v3(cross_new, ed_dir_new, ed_dir_v2_new);
|
||||
if (dot_v3v3(cross_old, cross_new) < 0.0f) { /* does this flip? */
|
||||
return false;
|
||||
}
|
||||
|
||||
negate_v3_v3(ed_dir_new_flip, ed_dir_new);
|
||||
|
||||
/* result is zero area corner */
|
||||
if ((dot_v3v3(ed_dir_new, ed_dir_v1_new) > 0.999f) ||
|
||||
(dot_v3v3(ed_dir_new_flip, ed_dir_v2_new) > 0.999f))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BM_edge_rotate_check_beauty(BMEdge *e, BMLoop *l1, BMLoop *l2)
|
||||
{
|
||||
/* Stupid check for now:
|
||||
* Could compare angles of surrounding edges
|
||||
* before & after, but this is OK. */
|
||||
return (len_squared_v3v3(e->v1->co, e->v2->co) > len_squared_v3v3(l1->v->co, l2->v->co));
|
||||
}
|
||||
|
||||
BMEdge *BM_edge_rotate(BMesh *bm, BMEdge *e, const bool ccw, const short check_flag)
|
||||
{
|
||||
BMVert *v1, *v2;
|
||||
BMLoop *l1, *l2;
|
||||
BMFace *f;
|
||||
BMEdge *e_new = nullptr;
|
||||
char f_active_prev = 0;
|
||||
char f_hflag_prev_1;
|
||||
char f_hflag_prev_2;
|
||||
|
||||
if (!BM_edge_rotate_check(e)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!BM_edge_calc_rotate(e, ccw, &l1, &l2)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* the loops will be freed so assign verts */
|
||||
v1 = l1->v;
|
||||
v2 = l2->v;
|
||||
|
||||
/* --------------------------------------- */
|
||||
/* Checking Code - make sure we can rotate */
|
||||
|
||||
if (check_flag & BM_EDGEROT_CHECK_BEAUTY) {
|
||||
if (!BM_edge_rotate_check_beauty(e, l1, l2)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* check before applying */
|
||||
if (check_flag & BM_EDGEROT_CHECK_EXISTS) {
|
||||
if (BM_edge_exists(v1, v2)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* slowest, check last */
|
||||
if (check_flag & BM_EDGEROT_CHECK_DEGENERATE) {
|
||||
if (!BM_edge_rotate_check_degenerate(e, l1, l2)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
/* Done Checking */
|
||||
/* ------------- */
|
||||
|
||||
/* --------------- */
|
||||
/* Rotate The Edge */
|
||||
|
||||
/* first create the new edge, this is so we can copy the customdata from the old one
|
||||
* if splice if disabled, always add in a new edge even if there's one there. */
|
||||
e_new = BM_edge_create(
|
||||
bm, v1, v2, e, (check_flag & BM_EDGEROT_CHECK_SPLICE) ? BM_CREATE_NO_DOUBLE : BM_CREATE_NOP);
|
||||
|
||||
f_hflag_prev_1 = l1->f->head.hflag;
|
||||
f_hflag_prev_2 = l2->f->head.hflag;
|
||||
|
||||
/* maintain active face */
|
||||
if (bm->act_face == l1->f) {
|
||||
f_active_prev = 1;
|
||||
}
|
||||
else if (bm->act_face == l2->f) {
|
||||
f_active_prev = 2;
|
||||
}
|
||||
|
||||
const bool is_flipped = !BM_edge_is_contiguous(e);
|
||||
|
||||
BMFace *f_double;
|
||||
|
||||
/* don't delete the edge, manually remove the edge after so we can copy its attributes */
|
||||
f = BM_faces_join_pair(
|
||||
bm, BM_face_edge_share_loop(l1->f, e), BM_face_edge_share_loop(l2->f, e), true, &f_double);
|
||||
|
||||
if (f == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* NOTE: this assumes joining the faces _didnt_ also remove the verts.
|
||||
* the #BM_edge_rotate_check will ensure this, but its possibly corrupt state or future edits
|
||||
* break this */
|
||||
if ((l1 = BM_face_vert_share_loop(f, v1)) && (l2 = BM_face_vert_share_loop(f, v2)) &&
|
||||
BM_face_split(bm, f, l1, l2, nullptr, nullptr, true))
|
||||
{
|
||||
/* we should really be able to know the faces some other way,
|
||||
* rather than fetching them back from the edge, but this is predictable
|
||||
* where using the return values from face split isn't. - campbell */
|
||||
BMFace *fa, *fb;
|
||||
if (BM_edge_face_pair(e_new, &fa, &fb)) {
|
||||
fa->head.hflag = f_hflag_prev_1;
|
||||
fb->head.hflag = f_hflag_prev_2;
|
||||
|
||||
if (f_active_prev == 1) {
|
||||
bm->act_face = fa;
|
||||
}
|
||||
else if (f_active_prev == 2) {
|
||||
bm->act_face = fb;
|
||||
}
|
||||
|
||||
if (is_flipped) {
|
||||
BM_face_normal_flip(bm, fb);
|
||||
|
||||
if (ccw) {
|
||||
/* Needed otherwise `ccw` toggles direction */
|
||||
e_new->l = e_new->l->radial_next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* See #BM_faces_join note on callers asserting when `r_double` is non-null.
|
||||
* Checked here because a double is acceptable as long as its temporary.
|
||||
*
|
||||
* TODO(@ideasman42): To properly solve we'd need to create the 2x faces with edge rotation
|
||||
* then only delete the original faces once the new faces have been successfully created.
|
||||
* - Worth looking into. */
|
||||
BLI_assert_msg(f_double == nullptr,
|
||||
"Doubled face detected at " AT ". Resulting mesh may be corrupt.");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return e_new;
|
||||
}
|
||||
|
||||
BMVert *BM_face_loop_separate(BMesh *bm, BMLoop *l_sep)
|
||||
{
|
||||
return bmesh_kernel_unglue_region_make_vert(bm, l_sep);
|
||||
}
|
||||
|
||||
BMVert *BM_face_loop_separate_multi_isolated(BMesh *bm, BMLoop *l_sep)
|
||||
{
|
||||
return bmesh_kernel_unglue_region_make_vert_multi_isolated(bm, l_sep);
|
||||
}
|
||||
|
||||
BMVert *BM_face_loop_separate_multi(BMesh *bm, BMLoop **larr, int larr_len)
|
||||
{
|
||||
return bmesh_kernel_unglue_region_make_vert_multi(bm, larr, larr_len);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
285
blender-5.2.0/source/blender/bmesh/intern/bmesh_mods.hh
Normal file
285
blender-5.2.0/source/blender/bmesh/intern/bmesh_mods.hh
Normal file
@@ -0,0 +1,285 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Dissolve Vert
|
||||
*
|
||||
* Turns the face region surrounding a manifold vertex into a single polygon.
|
||||
*
|
||||
* \par Example:
|
||||
* <pre>
|
||||
* +---------+ +---------+
|
||||
* | \ / | | |
|
||||
* Before: | v | After: | |
|
||||
* | / \ | | |
|
||||
* +---------+ +---------+
|
||||
* </pre>
|
||||
*
|
||||
* This function can also collapse edges too
|
||||
* in cases when it can't merge into faces.
|
||||
*
|
||||
* \par Example:
|
||||
* <pre>
|
||||
* Before: +----v----+ After: +---------+
|
||||
* </pre>
|
||||
*
|
||||
* \note dissolves vert, in more situations than BM_disk_dissolve
|
||||
* (e.g. if the vert is part of a wire edge, etc).
|
||||
*/
|
||||
bool BM_vert_dissolve(BMesh *bm, BMVert *v);
|
||||
|
||||
/**
|
||||
* dissolves all faces around a vert, and removes it.
|
||||
*/
|
||||
bool BM_disk_dissolve(BMesh *bm, BMVert *v);
|
||||
|
||||
/**
|
||||
* \brief Faces Join Pair
|
||||
*
|
||||
* Joins two adjacent faces together.
|
||||
*
|
||||
* \note This method calls to #BM_faces_join to do its work.
|
||||
* This means connected edges which also share the two faces will be joined.
|
||||
*
|
||||
* If the windings do not match the winding of the new face will follow
|
||||
* \a l_a's winding (i.e. \a l_b will be reversed before the join).
|
||||
*
|
||||
* \param bm: The bmesh.
|
||||
* \param l_a: First loop of an adjacent face pair that will be joined.
|
||||
* \param l_b: Second loop of an adjacent face pair that will be joined.
|
||||
* \param do_del: If true, remove the original faces, internal edges,
|
||||
* and internal verts such that they are replaced by the new face.
|
||||
* \param r_double: A pointer to a face that controls processing of doubled faces.
|
||||
* See #BM_faces_join `r_double` argument for details.
|
||||
*
|
||||
* \return The combined face or NULL on failure.
|
||||
*/
|
||||
BMFace *BM_faces_join_pair(BMesh *bm, BMLoop *l_a, BMLoop *l_b, bool do_del, BMFace **r_double);
|
||||
|
||||
/** see: bmesh_polygon_edgenet.hh for #BM_face_split_edgenet */
|
||||
|
||||
/**
|
||||
* \brief Face Split
|
||||
*
|
||||
* Split a face along two vertices. returns the newly made face, and sets
|
||||
* the \a r_l member to a loop in the newly created edge.
|
||||
*
|
||||
* \param bm: The bmesh
|
||||
* \param f: the original face
|
||||
* \param l_a, l_b: Loops of this face, their vertices define
|
||||
* the split edge to be created (must be differ and not can't be adjacent in the face).
|
||||
* \param r_l: pointer which will receive the BMLoop for the split edge in the new face
|
||||
* \param example: Edge used for attributes of splitting edge, if non-NULL
|
||||
* \param no_double: Use an existing edge if found
|
||||
*
|
||||
* \return Pointer to the newly created face representing one side of the split
|
||||
* if the split is successful (and the original face will be the other side).
|
||||
* NULL if the split fails.
|
||||
*/
|
||||
BMFace *BM_face_split(
|
||||
BMesh *bm, BMFace *f, BMLoop *l_a, BMLoop *l_b, BMLoop **r_l, BMEdge *example, bool no_double);
|
||||
|
||||
/**
|
||||
* \brief Face Split with intermediate points
|
||||
*
|
||||
* Like BM_face_split, but with an edge split by \a n intermediate points with given coordinates.
|
||||
*
|
||||
* \param bm: The bmesh.
|
||||
* \param f: the original face.
|
||||
* \param l_a, l_b: Vertices which define the split edge, must be different.
|
||||
* \param cos: Array of coordinates for intermediate points.
|
||||
* \param n: Length of \a cos (must be > 0).
|
||||
* \param r_l: pointer which will receive the BMLoop.
|
||||
* for the first split edge (from \a l_a) in the new face.
|
||||
* \param example: Edge used for attributes of splitting edge, if non-NULL.
|
||||
*
|
||||
* \return Pointer to the newly created face representing one side of the split
|
||||
* if the split is successful (and the original face will be the other side).
|
||||
* NULL if the split fails.
|
||||
*/
|
||||
BMFace *BM_face_split_n(BMesh *bm,
|
||||
BMFace *f,
|
||||
BMLoop *l_a,
|
||||
BMLoop *l_b,
|
||||
float cos[][3],
|
||||
int n,
|
||||
BMLoop **r_l,
|
||||
BMEdge *example);
|
||||
|
||||
/**
|
||||
* \brief Vert Collapse Faces
|
||||
*
|
||||
* Collapses vertex \a v_kill that has only two manifold edges
|
||||
* onto a vertex it shares an edge with.
|
||||
* \a fac defines the amount of interpolation for Custom Data.
|
||||
*
|
||||
* \note that this is not a general edge collapse function.
|
||||
*
|
||||
* \note this function is very close to #BM_vert_collapse_edge,
|
||||
* both collapse a vertex and return a new edge.
|
||||
* Except this takes a factor and merges custom data.
|
||||
*
|
||||
* \param bm: The bmesh
|
||||
* \param e_kill: The edge to collapse
|
||||
* \param v_kill: The vertex to collapse into the edge
|
||||
* \param fac: The factor along the edge
|
||||
* \param join_faces: When true the faces around the vertex will be joined
|
||||
* otherwise collapse the vertex by merging the 2 edges this vert touches into one.
|
||||
* \param kill_degenerate_faces: Removes faces with less than 3 verts after collapsing.
|
||||
*
|
||||
* \returns The New Edge
|
||||
*/
|
||||
BMEdge *BM_vert_collapse_faces(BMesh *bm,
|
||||
BMEdge *e_kill,
|
||||
BMVert *v_kill,
|
||||
float fac,
|
||||
bool do_del,
|
||||
bool join_faces,
|
||||
bool kill_degenerate_faces,
|
||||
bool kill_duplicate_faces);
|
||||
/**
|
||||
* \brief Vert Collapse Faces
|
||||
*
|
||||
* Collapses a vertex onto another vertex it shares an edge with.
|
||||
*
|
||||
* \return The New Edge
|
||||
*
|
||||
* \note To check if collapsing would create duplicate geometry,
|
||||
* see: #BM_vert_collapse_check_double_face.
|
||||
*/
|
||||
BMEdge *BM_vert_collapse_edge(BMesh *bm,
|
||||
BMEdge *e_kill,
|
||||
BMVert *v_kill,
|
||||
bool do_del,
|
||||
bool kill_degenerate_faces,
|
||||
bool kill_duplicate_faces);
|
||||
|
||||
/**
|
||||
* Collapse and edge into a single vertex.
|
||||
*/
|
||||
BMVert *BM_edge_collapse(
|
||||
BMesh *bm, BMEdge *e_kill, BMVert *v_kill, bool do_del, bool kill_degenerate_faces);
|
||||
|
||||
/**
|
||||
* \brief Edge Split
|
||||
*
|
||||
* <pre>
|
||||
* Before: v
|
||||
* +-----------------------------------+
|
||||
* e
|
||||
*
|
||||
* After: v v_new (returned)
|
||||
* +-----------------+-----------------+
|
||||
* r_e e
|
||||
* </pre>
|
||||
*
|
||||
* \param e: The edge to split.
|
||||
* \param v: One of the vertices in \a e and defines the "from" end of the splitting operation,
|
||||
* the new vertex will be \a fac of the way from \a v to the other end.
|
||||
* \param r_e: The newly created edge.
|
||||
* \return The new vertex.
|
||||
*/
|
||||
BMVert *BM_edge_split(BMesh *bm, BMEdge *e, BMVert *v, BMEdge **r_e, float fac);
|
||||
|
||||
/**
|
||||
* \brief Split an edge multiple times evenly
|
||||
*
|
||||
* \param r_varr: Optional array, verts in between (v1 -> v2)
|
||||
*/
|
||||
BMVert *BM_edge_split_n(BMesh *bm, BMEdge *e, int numcuts, BMVert **r_varr);
|
||||
|
||||
/**
|
||||
* Swap v1 & v2
|
||||
*
|
||||
* \note Typically we shouldn't care about this, however it's used when extruding wire edges.
|
||||
*/
|
||||
void BM_edge_verts_swap(BMEdge *e);
|
||||
|
||||
/**
|
||||
* Calculate the 2 loops which _would_ make up the newly rotated Edge
|
||||
* but don't actually change anything.
|
||||
*
|
||||
* Use this to further inspect if the loops to be connected have issues:
|
||||
*
|
||||
* Examples:
|
||||
* - the newly formed edge already exists
|
||||
* - the new face would be degenerate (zero area / concave / bow-tie)
|
||||
* - may want to measure if the new edge gives improved results topology.
|
||||
* over the old one, as with beauty fill.
|
||||
*
|
||||
* \note #BM_edge_rotate_check must have already run.
|
||||
*/
|
||||
[[nodiscard]] bool BM_edge_calc_rotate(BMEdge *e, bool ccw, BMLoop **r_l1, BMLoop **r_l2);
|
||||
/**
|
||||
* \brief Check if Rotate Edge is OK
|
||||
*
|
||||
* Quick check to see if we could rotate the edge,
|
||||
* use this to avoid calling exceptions on common cases.
|
||||
*
|
||||
* Take care, depending on the rotation direction its possible
|
||||
* the adjacent faces share multiple edges on either side.
|
||||
*
|
||||
* Before executing the rotation it's important to check the rotated loops
|
||||
* on both faces don't reference the same vertex.
|
||||
*/
|
||||
bool BM_edge_rotate_check(BMEdge *e);
|
||||
/**
|
||||
* \brief Check if Edge Rotate Gives Degenerate Faces
|
||||
*
|
||||
* Check 2 cases
|
||||
* 1) does the newly forms edge form a flipped face (compare with previous cross product)
|
||||
* 2) does the newly formed edge cause a zero area corner (or close enough to be almost zero)
|
||||
*
|
||||
* \param e: The edge to test rotation.
|
||||
* \param l1, l2: are the loops of the proposed verts to rotate too and should
|
||||
* be the result of calling #BM_edge_calc_rotate
|
||||
*/
|
||||
bool BM_edge_rotate_check_degenerate(BMEdge *e, BMLoop *l1, BMLoop *l2);
|
||||
bool BM_edge_rotate_check_beauty(BMEdge *e, BMLoop *l1, BMLoop *l2);
|
||||
/**
|
||||
* \brief Rotate Edge
|
||||
*
|
||||
* Spins an edge topologically,
|
||||
* either counter-clockwise or clockwise depending on \a ccw.
|
||||
*
|
||||
* \return The spun edge, NULL on error
|
||||
* (e.g., if the edge isn't surrounded by exactly two faces).
|
||||
*
|
||||
* \note This works by dissolving the edge then re-creating it,
|
||||
* so the returned edge won't have the same pointer address as the original one.
|
||||
*
|
||||
* \see header definition for \a check_flag enum.
|
||||
*/
|
||||
BMEdge *BM_edge_rotate(BMesh *bm, BMEdge *e, bool ccw, short check_flag);
|
||||
|
||||
/** Flags for #BM_edge_rotate */
|
||||
enum {
|
||||
/** Disallow rotating when the new edge matches an existing one. */
|
||||
BM_EDGEROT_CHECK_EXISTS = (1 << 0),
|
||||
/** Overrides existing check, if the edge already, rotate and merge them. */
|
||||
BM_EDGEROT_CHECK_SPLICE = (1 << 1),
|
||||
/** Disallow creating bow-tie, concave or zero area faces */
|
||||
BM_EDGEROT_CHECK_DEGENERATE = (1 << 2),
|
||||
/** Disallow rotating into ugly topology. */
|
||||
BM_EDGEROT_CHECK_BEAUTY = (1 << 3),
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Rip a single face from a vertex fan
|
||||
*/
|
||||
BMVert *BM_face_loop_separate(BMesh *bm, BMLoop *l_sep);
|
||||
BMVert *BM_face_loop_separate_multi_isolated(BMesh *bm, BMLoop *l_sep);
|
||||
BMVert *BM_face_loop_separate_multi(BMesh *bm, BMLoop **larr, int larr_len);
|
||||
|
||||
} // namespace blender
|
||||
3001
blender-5.2.0/source/blender/bmesh/intern/bmesh_opdefines.cc
Normal file
3001
blender-5.2.0/source/blender/bmesh/intern/bmesh_opdefines.cc
Normal file
File diff suppressed because it is too large
Load Diff
847
blender-5.2.0/source/blender/bmesh/intern/bmesh_operator_api.hh
Normal file
847
blender-5.2.0/source/blender/bmesh/intern/bmesh_operator_api.hh
Normal file
@@ -0,0 +1,847 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "BLI_enum_flags.hh"
|
||||
#include "BLI_ghash.h"
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* operators represent logical, executable mesh modules. all topological
|
||||
* operations involving a bmesh has to go through them.
|
||||
*
|
||||
* operators are nested, as are tool flags, which are private to an operator
|
||||
* when it's executed. tool flags are allocated in layers, one per operator
|
||||
* execution, and are used for all internal flagging a tool needs to do.
|
||||
*
|
||||
* each operator has a series of "slots" which can be of the following types:
|
||||
* - simple numerical types
|
||||
* - arrays of elements (e.g. arrays of faces).
|
||||
* - hash mappings.
|
||||
*
|
||||
* each slot is identified by a slot code, as are each operator.
|
||||
* operators, and their slots, are defined in bmesh_opdefines.cc (with their
|
||||
* execution functions prototyped in bmesh_operators_private.hh), with all their
|
||||
* operator code and slot codes defined in bmesh_operators.hh. see
|
||||
* bmesh_opdefines.cc and the BMOpDefine struct for how to define new operators.
|
||||
*
|
||||
* in general, operators are fed arrays of elements, created using either
|
||||
* #BMO_slot_buffer_from_hflag or #BMO_slot_buffer_from_flag
|
||||
* (or through one of the format specifiers in #BMO_op_callf or #BMO_op_initf).
|
||||
*
|
||||
* \note multiple element types (e.g. faces and edges)
|
||||
* can be fed to the same slot array. Operators act on this data,
|
||||
* and possibly spit out data into output slots.
|
||||
*
|
||||
* \note operators should never read from header flags (e.g. element->head.flag).
|
||||
* For example, if you want an operator to only operate on selected faces, you
|
||||
* should use #BMO_slot_buffer_from_hflag to put the selected elements into a slot.
|
||||
*
|
||||
* \note when you read from an element slot array or mapping, you can either tool-flag
|
||||
* all the elements in it, or read them using an iterator API (which is semantically
|
||||
* similar to the iterator API in bmesh_iterators.hh).
|
||||
*
|
||||
* \note only #BMLoop items can't be put into slots as with verts, edges & faces.
|
||||
*/
|
||||
|
||||
struct GHashIterator;
|
||||
|
||||
BLI_INLINE BMFlagLayer *BMO_elem_flag_from_header(BMHeader *ele_head)
|
||||
{
|
||||
switch (ele_head->htype) {
|
||||
case BM_VERT:
|
||||
return (reinterpret_cast<BMVert_OFlag *>(ele_head))->oflags;
|
||||
case BM_EDGE:
|
||||
return (reinterpret_cast<BMEdge_OFlag *>(ele_head))->oflags;
|
||||
default:
|
||||
return (reinterpret_cast<BMFace_OFlag *>(ele_head))->oflags;
|
||||
}
|
||||
}
|
||||
|
||||
#define BMO_elem_flag_test(bm, ele, oflag) \
|
||||
_bmo_elem_flag_test(bm, BMO_elem_flag_from_header(&(ele)->head), oflag)
|
||||
#define BMO_elem_flag_test_bool(bm, ele, oflag) \
|
||||
_bmo_elem_flag_test_bool(bm, BMO_elem_flag_from_header(&(ele)->head), oflag)
|
||||
#define BMO_elem_flag_enable(bm, ele, oflag) \
|
||||
_bmo_elem_flag_enable( \
|
||||
bm, (BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(&(ele)->head)), oflag)
|
||||
#define BMO_elem_flag_disable(bm, ele, oflag) \
|
||||
_bmo_elem_flag_disable( \
|
||||
bm, (BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(&(ele)->head)), oflag)
|
||||
#define BMO_elem_flag_set(bm, ele, oflag, val) \
|
||||
_bmo_elem_flag_set(bm, \
|
||||
(BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(&(ele)->head)), \
|
||||
oflag, \
|
||||
val)
|
||||
#define BMO_elem_flag_toggle(bm, ele, oflag) \
|
||||
_bmo_elem_flag_toggle( \
|
||||
bm, (BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(&(ele)->head)), oflag)
|
||||
|
||||
/* take care not to instantiate args multiple times */
|
||||
#ifdef __GNUC___
|
||||
# define _BMO_CAST_V_CONST(e) \
|
||||
({ \
|
||||
typeof(e) _e = e; \
|
||||
(BM_CHECK_TYPE_VERT(_e), \
|
||||
BLI_assert(((const BMHeader *)_e)->htype == BM_VERT), \
|
||||
(const BMVert_OFlag *)_e); \
|
||||
})
|
||||
# define _BMO_CAST_E_CONST(e) \
|
||||
({ \
|
||||
typeof(e) _e = e; \
|
||||
(BM_CHECK_TYPE_EDGE(_e), \
|
||||
BLI_assert(((const BMHeader *)_e)->htype == BM_EDGE), \
|
||||
(const BMEdge_OFlag *)_e); \
|
||||
})
|
||||
# define _BMO_CAST_F_CONST(e) \
|
||||
({ \
|
||||
typeof(e) _e = e; \
|
||||
(BM_CHECK_TYPE_FACE(_e), \
|
||||
BLI_assert(((const BMHeader *)_e)->htype == BM_FACE), \
|
||||
(const BMFace_OFlag *)_e); \
|
||||
})
|
||||
# define _BMO_CAST_V(e) \
|
||||
({ \
|
||||
typeof(e) _e = e; \
|
||||
(BM_CHECK_TYPE_VERT_NONCONST(_e), \
|
||||
BLI_assert(((BMHeader *)_e)->htype == BM_VERT), \
|
||||
(BMVert_OFlag *)_e); \
|
||||
})
|
||||
# define _BMO_CAST_E(e) \
|
||||
({ \
|
||||
typeof(e) _e = e; \
|
||||
(BM_CHECK_TYPE_EDGE_NONCONST(_e), \
|
||||
BLI_assert(((BMHeader *)_e)->htype == BM_EDGE), \
|
||||
(BMEdge_OFlag *)_e); \
|
||||
})
|
||||
# define _BMO_CAST_F(e) \
|
||||
({ \
|
||||
typeof(e) _e = e; \
|
||||
(BM_CHECK_TYPE_FACE_NONCONST(_e), \
|
||||
BLI_assert(((BMHeader *)_e)->htype == BM_FACE), \
|
||||
(BMFace_OFlag *)_e); \
|
||||
})
|
||||
#else
|
||||
# define _BMO_CAST_V_CONST(e) (BM_CHECK_TYPE_VERT(e), (const BMVert_OFlag *)e)
|
||||
# define _BMO_CAST_E_CONST(e) (BM_CHECK_TYPE_EDGE(e), (const BMEdge_OFlag *)e)
|
||||
# define _BMO_CAST_F_CONST(e) (BM_CHECK_TYPE_FACE(e), (const BMFace_OFlag *)e)
|
||||
# define _BMO_CAST_V(e) (BM_CHECK_TYPE_VERT_NONCONST(e), (BMVert_OFlag *)e)
|
||||
# define _BMO_CAST_E(e) (BM_CHECK_TYPE_EDGE_NONCONST(e), (BMEdge_OFlag *)e)
|
||||
# define _BMO_CAST_F(e) (BM_CHECK_TYPE_FACE_NONCONST(e), (BMFace_OFlag *)e)
|
||||
#endif
|
||||
|
||||
#define BMO_vert_flag_test(bm, e, oflag) \
|
||||
_bmo_elem_flag_test(bm, _BMO_CAST_V_CONST(e)->oflags, oflag)
|
||||
#define BMO_vert_flag_test_bool(bm, e, oflag) \
|
||||
_bmo_elem_flag_test_bool(bm, _BMO_CAST_V_CONST(e)->oflags, oflag)
|
||||
#define BMO_vert_flag_enable(bm, e, oflag) _bmo_elem_flag_enable(bm, _BMO_CAST_V(e)->oflags, oflag)
|
||||
#define BMO_vert_flag_disable(bm, e, oflag) \
|
||||
_bmo_elem_flag_disable(bm, _BMO_CAST_V(e)->oflags, oflag)
|
||||
#define BMO_vert_flag_set(bm, e, oflag, val) \
|
||||
_bmo_elem_flag_set(bm, _BMO_CAST_V(e)->oflags, oflag, val)
|
||||
#define BMO_vert_flag_toggle(bm, e, oflag) _bmo_elem_flag_toggle(bm, _BMO_CAST_V(e)->oflags, oflag)
|
||||
|
||||
#define BMO_edge_flag_test(bm, e, oflag) \
|
||||
_bmo_elem_flag_test(bm, _BMO_CAST_E_CONST(e)->oflags, oflag)
|
||||
#define BMO_edge_flag_test_bool(bm, e, oflag) \
|
||||
_bmo_elem_flag_test_bool(bm, _BMO_CAST_E_CONST(e)->oflags, oflag)
|
||||
#define BMO_edge_flag_enable(bm, e, oflag) _bmo_elem_flag_enable(bm, _BMO_CAST_E(e)->oflags, oflag)
|
||||
#define BMO_edge_flag_disable(bm, e, oflag) \
|
||||
_bmo_elem_flag_disable(bm, _BMO_CAST_E(e)->oflags, oflag)
|
||||
#define BMO_edge_flag_set(bm, e, oflag, val) \
|
||||
_bmo_elem_flag_set(bm, _BMO_CAST_E(e)->oflags, oflag, val)
|
||||
#define BMO_edge_flag_toggle(bm, e, oflag) _bmo_elem_flag_toggle(bm, _BMO_CAST_E(e)->oflags, oflag)
|
||||
|
||||
#define BMO_face_flag_test(bm, e, oflag) \
|
||||
_bmo_elem_flag_test(bm, _BMO_CAST_F_CONST(e)->oflags, oflag)
|
||||
#define BMO_face_flag_test_bool(bm, e, oflag) \
|
||||
_bmo_elem_flag_test_bool(bm, _BMO_CAST_F_CONST(e)->oflags, oflag)
|
||||
#define BMO_face_flag_enable(bm, e, oflag) _bmo_elem_flag_enable(bm, _BMO_CAST_F(e)->oflags, oflag)
|
||||
#define BMO_face_flag_disable(bm, e, oflag) \
|
||||
_bmo_elem_flag_disable(bm, _BMO_CAST_F(e)->oflags, oflag)
|
||||
#define BMO_face_flag_set(bm, e, oflag, val) \
|
||||
_bmo_elem_flag_set(bm, _BMO_CAST_F(e)->oflags, oflag, val)
|
||||
#define BMO_face_flag_toggle(bm, e, oflag) _bmo_elem_flag_toggle(bm, _BMO_CAST_F(e)->oflags, oflag)
|
||||
|
||||
BLI_INLINE short _bmo_elem_flag_test(BMesh *bm, const BMFlagLayer *oflags, short oflag);
|
||||
BLI_INLINE bool _bmo_elem_flag_test_bool(BMesh *bm, const BMFlagLayer *oflags, short oflag);
|
||||
BLI_INLINE void _bmo_elem_flag_enable(BMesh *bm, BMFlagLayer *oflags, short oflag);
|
||||
BLI_INLINE void _bmo_elem_flag_disable(BMesh *bm, BMFlagLayer *oflags, short oflag);
|
||||
BLI_INLINE void _bmo_elem_flag_set(BMesh *bm, BMFlagLayer *oflags, short oflag, int val);
|
||||
BLI_INLINE void _bmo_elem_flag_toggle(BMesh *bm, BMFlagLayer *oflags, short oflag);
|
||||
|
||||
/* slot type arrays are terminated by the last member
|
||||
* having a slot type of 0 */
|
||||
enum eBMOpSlotType {
|
||||
/* BMO_OP_SLOT_SENTINEL = 0, */
|
||||
BMO_OP_SLOT_BOOL = 1,
|
||||
BMO_OP_SLOT_INT = 2,
|
||||
BMO_OP_SLOT_FLT = 3,
|
||||
|
||||
/* normally store pointers to object, scene,
|
||||
* _never_ store arrays corresponding to mesh elements with this */
|
||||
BMO_OP_SLOT_PTR = 4, /* requires subtype BMO_OP_SLOT_SUBTYPE_PTR_xxx */
|
||||
BMO_OP_SLOT_MAT = 5,
|
||||
BMO_OP_SLOT_VEC = 8,
|
||||
|
||||
/* after BMO_OP_SLOT_VEC, everything is dynamically allocated arrays.
|
||||
* We leave a space in the identifiers for future growth.
|
||||
*
|
||||
* it's very important this remain a power of two */
|
||||
BMO_OP_SLOT_ELEMENT_BUF = 9, /* list of verts/edges/faces */
|
||||
BMO_OP_SLOT_MAPPING = 10 /* simple hash map, requires subtype BMO_OP_SLOT_SUBTYPE_MAP_xxx */
|
||||
};
|
||||
#define BMO_OP_SLOT_TOTAL_TYPES 11
|
||||
|
||||
/* don't overlap values to avoid confusion */
|
||||
enum eBMOpSlotSubType_Elem {
|
||||
/* use as flags */
|
||||
BMO_OP_SLOT_SUBTYPE_ELEM_VERT = BM_VERT,
|
||||
BMO_OP_SLOT_SUBTYPE_ELEM_EDGE = BM_EDGE,
|
||||
BMO_OP_SLOT_SUBTYPE_ELEM_FACE = BM_FACE,
|
||||
BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE = (BM_FACE << 1),
|
||||
};
|
||||
ENUM_OPERATORS(eBMOpSlotSubType_Elem)
|
||||
|
||||
enum eBMOpSlotSubType_Map {
|
||||
BMO_OP_SLOT_SUBTYPE_MAP_EMPTY = 64, /* use as a set(), unused value */
|
||||
BMO_OP_SLOT_SUBTYPE_MAP_ELEM = 65,
|
||||
BMO_OP_SLOT_SUBTYPE_MAP_FLT = 66,
|
||||
BMO_OP_SLOT_SUBTYPE_MAP_INT = 67,
|
||||
BMO_OP_SLOT_SUBTYPE_MAP_BOOL = 68,
|
||||
BMO_OP_SLOT_SUBTYPE_MAP_INTERNAL = 69, /* python can't convert these */
|
||||
};
|
||||
enum eBMOpSlotSubType_Ptr {
|
||||
BMO_OP_SLOT_SUBTYPE_PTR_BMESH = 100,
|
||||
BMO_OP_SLOT_SUBTYPE_PTR_SCENE = 101,
|
||||
BMO_OP_SLOT_SUBTYPE_PTR_OBJECT = 102,
|
||||
BMO_OP_SLOT_SUBTYPE_PTR_MESH = 103,
|
||||
BMO_OP_SLOT_SUBTYPE_PTR_STRUCT = 104,
|
||||
};
|
||||
enum eBMOpSlotSubType_Int {
|
||||
BMO_OP_SLOT_SUBTYPE_INT_ENUM = 200,
|
||||
BMO_OP_SLOT_SUBTYPE_INT_FLAG = 201,
|
||||
};
|
||||
|
||||
union eBMOpSlotSubType_Union {
|
||||
eBMOpSlotSubType_Elem elem;
|
||||
eBMOpSlotSubType_Ptr ptr;
|
||||
eBMOpSlotSubType_Map map;
|
||||
eBMOpSlotSubType_Int intg;
|
||||
};
|
||||
|
||||
struct BMO_FlagSet {
|
||||
int value;
|
||||
const char *identifier;
|
||||
};
|
||||
|
||||
/* please ignore all these structures, don't touch them in tool code, except
|
||||
* for when your defining an operator with BMOpDefine. */
|
||||
|
||||
struct BMOpSlot {
|
||||
const char *slot_name; /* pointer to BMOpDefine.slot_args */
|
||||
eBMOpSlotType slot_type;
|
||||
eBMOpSlotSubType_Union slot_subtype;
|
||||
|
||||
int len;
|
||||
// int flag; /* UNUSED */
|
||||
// int index; /* index within slot array */ /* UNUSED */
|
||||
union {
|
||||
int i;
|
||||
float f;
|
||||
void *p;
|
||||
float vec[3];
|
||||
void **buf;
|
||||
GHash *ghash;
|
||||
struct {
|
||||
/** Don't clobber (i) when assigning flags, see #eBMOpSlotSubType_Int. */
|
||||
int _i;
|
||||
BMO_FlagSet *flags;
|
||||
} enum_data;
|
||||
} data;
|
||||
};
|
||||
|
||||
/* mainly for use outside bmesh internal code */
|
||||
#define BMO_SLOT_AS_BOOL(slot) ((slot)->data.i)
|
||||
#define BMO_SLOT_AS_INT(slot) ((slot)->data.i)
|
||||
#define BMO_SLOT_AS_FLOAT(slot) ((slot)->data.f)
|
||||
#define BMO_SLOT_AS_VECTOR(slot) ((slot)->data.vec)
|
||||
#define BMO_SLOT_AS_MATRIX(slot) ((float (*)[4])((slot)->data.p))
|
||||
#define BMO_SLOT_AS_BUFFER(slot) ((slot)->data.buf)
|
||||
#define BMO_SLOT_AS_GHASH(slot) ((slot)->data.ghash)
|
||||
|
||||
#define BMO_ASSERT_SLOT_IN_OP(slot, op) \
|
||||
BLI_assert(((slot >= (op)->slots_in) && (slot < &(op)->slots_in[BMO_OP_MAX_SLOTS])) || \
|
||||
((slot >= (op)->slots_out) && (slot < &(op)->slots_out[BMO_OP_MAX_SLOTS])))
|
||||
|
||||
/* Limit hit, so expanded for bevel operator. Compiler complains if limit is hit. */
|
||||
#define BMO_OP_MAX_SLOTS 21
|
||||
|
||||
/* BMOpDefine->type_flag */
|
||||
enum BMOpTypeFlag {
|
||||
BMO_OPTYPE_FLAG_NOP = 0,
|
||||
/** Switch from multires tangent space to absolute coordinates. */
|
||||
BMO_OPTYPE_FLAG_UNTAN_MULTIRES = (1 << 0),
|
||||
BMO_OPTYPE_FLAG_NORMALS_CALC = (1 << 1),
|
||||
BMO_OPTYPE_FLAG_SELECT_FLUSH = (1 << 2),
|
||||
BMO_OPTYPE_FLAG_SELECT_VALIDATE = (1 << 3),
|
||||
BMO_OPTYPE_FLAG_INVALIDATE_CLNOR_ALL = (1 << 4),
|
||||
};
|
||||
ENUM_OPERATORS(BMOpTypeFlag)
|
||||
|
||||
struct BMOperator {
|
||||
struct BMOpSlot slots_in[BMO_OP_MAX_SLOTS];
|
||||
struct BMOpSlot slots_out[BMO_OP_MAX_SLOTS];
|
||||
void (*exec)(BMesh *bm, struct BMOperator *op);
|
||||
struct MemArena *arena;
|
||||
int type;
|
||||
BMOpTypeFlag type_flag;
|
||||
int flag; /* runtime options */
|
||||
};
|
||||
|
||||
enum {
|
||||
BMO_FLAG_RESPECT_HIDE = 1,
|
||||
};
|
||||
|
||||
#define BMO_FLAG_DEFAULTS BMO_FLAG_RESPECT_HIDE
|
||||
|
||||
#define MAX_SLOTNAME 32
|
||||
|
||||
struct BMOSlotType {
|
||||
char name[MAX_SLOTNAME];
|
||||
eBMOpSlotType type;
|
||||
eBMOpSlotSubType_Union subtype;
|
||||
BMO_FlagSet *enum_flags;
|
||||
};
|
||||
|
||||
struct BMOpDefine {
|
||||
const char *opname;
|
||||
BMOSlotType slot_types_in[BMO_OP_MAX_SLOTS];
|
||||
BMOSlotType slot_types_out[BMO_OP_MAX_SLOTS];
|
||||
/**
|
||||
* Optional initialize function.
|
||||
* Can be used for setting defaults.
|
||||
*/
|
||||
void (*init)(BMOperator *op);
|
||||
void (*exec)(BMesh *bm, BMOperator *op);
|
||||
BMOpTypeFlag type_flag;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name BMesh Operator API
|
||||
*
|
||||
* \note data types that use pointers (arrays, etc) must _never_ have it set directly.
|
||||
* Don't #BMO_slot_ptr_set to pass in a list of edges or any arrays.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* \brief BMESH OPSTACK INIT OP
|
||||
*
|
||||
* Initializes an operator structure to a certain type
|
||||
*/
|
||||
void BMO_op_init(BMesh *bm, BMOperator *op, int flag, const char *opname);
|
||||
|
||||
/**
|
||||
* \brief BMESH OPSTACK EXEC OP
|
||||
*
|
||||
* Executes a passed in operator.
|
||||
*
|
||||
* This handles the allocation and freeing of temporary tool flag
|
||||
* layers and starting/stopping the modeling loop.
|
||||
* Can be called from other operators exec callbacks as well.
|
||||
*/
|
||||
void BMO_op_exec(BMesh *bm, BMOperator *op);
|
||||
|
||||
/**
|
||||
* \brief BMESH OPSTACK FINISH OP
|
||||
*
|
||||
* Does housekeeping chores related to finishing up an operator.
|
||||
*
|
||||
* \note the operator's tool flag is removed after it finishes executing in #BMO_op_exec.
|
||||
*/
|
||||
void BMO_op_finish(BMesh *bm, BMOperator *op);
|
||||
|
||||
/**
|
||||
* Count the number of elements with the specified flag enabled.
|
||||
* type can be a bit-mask of #BM_FACE, #BM_EDGE, or #BM_FACE.
|
||||
*/
|
||||
int BMO_mesh_enabled_flag_count(BMesh *bm, char htype, short oflag);
|
||||
|
||||
/**
|
||||
* Count the number of elements with the specified flag disabled.
|
||||
* type can be a bit-mask of #BM_FACE, #BM_EDGE, or #BM_FACE.
|
||||
*/
|
||||
int BMO_mesh_disabled_flag_count(BMesh *bm, char htype, short oflag);
|
||||
|
||||
/**
|
||||
* \brief BMESH OPSTACK PUSH
|
||||
*
|
||||
* Pushes the operator-stack down one level and allocates a new flag layer if appropriate.
|
||||
*/
|
||||
void BMO_push(BMesh *bm, BMOperator *op);
|
||||
/**
|
||||
* \brief BMESH OPSTACK POP
|
||||
*
|
||||
* Pops the operator-stack one level and frees a flag layer if appropriate
|
||||
*
|
||||
* BMESH_TODO: investigate NOT freeing flag layers.
|
||||
*/
|
||||
void BMO_pop(BMesh *bm);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Formatted Operator Initialization/Execution
|
||||
*
|
||||
* Format Strings for #BMOperator Initialization.
|
||||
*
|
||||
* This system is used to execute or initialize an operator,
|
||||
* using a formatted-string system.
|
||||
*
|
||||
* The basic format for the format string is:
|
||||
* `[operatorname] [slot_name]=%[code] [slot_name]=%[code]`
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* \code{.c}
|
||||
* BMO_op_callf(bm, BMO_FLAG_DEFAULTS,
|
||||
* "delete context=%i geom=%hv",
|
||||
* DEL_ONLYFACES, BM_ELEM_SELECT);
|
||||
* \endcode
|
||||
* **Primitive Types**
|
||||
* - `b` - boolean (same as int but 1/0 only). #BMO_OP_SLOT_BOOL
|
||||
* - `i` - int. #BMO_OP_SLOT_INT
|
||||
* - `f` - float. #BMO_OP_SLOT_FLT
|
||||
* - `p` - pointer (normally to a Scene/Mesh/Object/BMesh). #BMO_OP_SLOT_PTR
|
||||
* - `m3` - 3x3 matrix of floats. #BMO_OP_SLOT_MAT
|
||||
* - `m4` - 4x4 matrix of floats. #BMO_OP_SLOT_MAT
|
||||
* - `v` - 3D vector of floats. #BMO_OP_SLOT_VEC
|
||||
* **Utility**
|
||||
*
|
||||
* Pass an existing slot which is copied to either an input or output slot.
|
||||
* Taking the operator and slot-name pair of args (BMOperator *, const char *).
|
||||
* - `s` - slot_in (lower case)
|
||||
* - `S` - slot_out (upper case)
|
||||
* **Element Buffer** (#BMO_OP_SLOT_ELEMENT_BUF)
|
||||
* - `e` - single element vert/edge/face (use with #BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE).
|
||||
* - `eb` - elem buffer, take an array and a length.
|
||||
* - `av` - all verts
|
||||
* - `ae` - all edges
|
||||
* - `af` - all faces
|
||||
* - `hv` - header flagged verts (hflag)
|
||||
* - `he` - header flagged edges (hflag)
|
||||
* - `hf` - header flagged faces (hflag)
|
||||
* - `Hv` - header flagged verts (hflag off)
|
||||
* - `He` - header flagged edges (hflag off)
|
||||
* - `Hf` - header flagged faces (hflag off)
|
||||
* - `fv` - flagged verts (oflag)
|
||||
* - `fe` - flagged edges (oflag)
|
||||
* - `ff` - flagged faces (oflag)
|
||||
* - `Fv` - flagged verts (oflag off)
|
||||
* - `Fe` - flagged edges (oflag off)
|
||||
* - `Ff` - flagged faces (oflag off)
|
||||
*
|
||||
* \note The common v/e/f suffix can be mixed,
|
||||
* so `avef` is can be used for all verts, edges and faces.
|
||||
* Order is not important so `Hfev` is also valid (all un-flagged verts, edges and faces).
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/** Executes an operator. */
|
||||
bool BMO_op_callf(BMesh *bm, int flag, const char *fmt, ...);
|
||||
|
||||
/** A `va_list` version of #BMO_op_callf. */
|
||||
bool BMO_op_vcallf(BMesh *bm, int flag, const char *fmt, va_list list);
|
||||
|
||||
/**
|
||||
* Initializes, but doesn't execute an operator. this is so you can
|
||||
* gain access to the outputs of the operator. note that you have
|
||||
* to execute/finish (BMO_op_exec and BMO_op_finish) yourself.
|
||||
*/
|
||||
bool BMO_op_initf(BMesh *bm, BMOperator *op, int flag, const char *fmt, ...);
|
||||
|
||||
/**
|
||||
* A `va_list` version, used to implement the above two functions,
|
||||
* plus #EDBM_op_callf in editmesh_utils.cc.
|
||||
*/
|
||||
bool BMO_op_vinitf(BMesh *bm, BMOperator *op, int flag, const char *fmt, va_list vlist);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name BMesh Operator Slot Access
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* \brief BMESH OPSTACK HAS SLOT
|
||||
*
|
||||
* \return Success if the slot if found.
|
||||
*/
|
||||
bool BMO_slot_exists(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *identifier);
|
||||
|
||||
/* get a pointer to a slot. this may be removed layer on from the public API. */
|
||||
/**
|
||||
* \brief BMESH OPSTACK GET SLOT
|
||||
*
|
||||
* Returns a pointer to the slot of type 'slot_code'
|
||||
*/
|
||||
BMOpSlot *BMO_slot_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *identifier);
|
||||
|
||||
/* copies the data of a slot from one operator to another. src and dst are the
|
||||
* source/destination slot codes, respectively. */
|
||||
#define BMO_slot_copy(op_src, slots_src, slot_name_src, op_dst, slots_dst, slot_name_dst) \
|
||||
_bmo_slot_copy( \
|
||||
(op_src)->slots_src, slot_name_src, (op_dst)->slots_dst, slot_name_dst, (op_dst)->arena)
|
||||
|
||||
/**
|
||||
* \brief BMESH OPSTACK COPY SLOT
|
||||
*
|
||||
* define used.
|
||||
* Copies data from one slot to another.
|
||||
*/
|
||||
void _bmo_slot_copy(BMOpSlot slot_args_src[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name_src,
|
||||
BMOpSlot slot_args_dst[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name_dst,
|
||||
struct MemArena *arena_dst);
|
||||
|
||||
/** \} */
|
||||
|
||||
/** Delete "context" slot values, used for operator too. */
|
||||
enum {
|
||||
DEL_VERTS = 1,
|
||||
DEL_EDGES,
|
||||
DEL_ONLYFACES,
|
||||
DEL_EDGESFACES,
|
||||
DEL_FACES,
|
||||
/* A version of 'DEL_FACES' that keeps edges on face boundaries,
|
||||
* allowing the surrounding edge-loop to be kept from removed face regions. */
|
||||
DEL_FACES_KEEP_BOUNDARY,
|
||||
DEL_ONLYTAGGED,
|
||||
};
|
||||
|
||||
enum BMO_SymmDirection {
|
||||
BMO_SYMMETRIZE_NEGATIVE_X,
|
||||
BMO_SYMMETRIZE_NEGATIVE_Y,
|
||||
BMO_SYMMETRIZE_NEGATIVE_Z,
|
||||
|
||||
BMO_SYMMETRIZE_POSITIVE_X,
|
||||
BMO_SYMMETRIZE_POSITIVE_Y,
|
||||
BMO_SYMMETRIZE_POSITIVE_Z,
|
||||
};
|
||||
|
||||
enum BMO_Delimit {
|
||||
BMO_DELIM_NORMAL = 1 << 0,
|
||||
BMO_DELIM_MATERIAL = 1 << 1,
|
||||
BMO_DELIM_SEAM = 1 << 2,
|
||||
BMO_DELIM_SHARP = 1 << 3,
|
||||
BMO_DELIM_UV = 1 << 4,
|
||||
};
|
||||
ENUM_OPERATORS(BMO_Delimit)
|
||||
|
||||
void BMO_op_flag_enable(BMesh *bm, BMOperator *op, int op_flag);
|
||||
void BMO_op_flag_disable(BMesh *bm, BMOperator *op, int op_flag);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name BMesh Operator Slot Get/Set
|
||||
* \{ */
|
||||
|
||||
void BMO_slot_float_set(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, float f);
|
||||
float BMO_slot_float_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name);
|
||||
void BMO_slot_int_set(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, int i);
|
||||
int BMO_slot_int_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name);
|
||||
void BMO_slot_bool_set(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, bool i);
|
||||
bool BMO_slot_bool_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name);
|
||||
/**
|
||||
* Return a copy of the element buffer.
|
||||
*/
|
||||
void *BMO_slot_as_arrayN(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, int *len);
|
||||
|
||||
/**
|
||||
* Don't pass in arrays that are supposed to map to elements this way.
|
||||
*
|
||||
* so, e.g. passing in list of floats per element in another slot is bad.
|
||||
* passing in, e.g. pointer to an edit-mesh for the conversion operator is fine though.
|
||||
*/
|
||||
void BMO_slot_ptr_set(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, void *p);
|
||||
void *BMO_slot_ptr_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name);
|
||||
void BMO_slot_vec_set(BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
const float vec[3]);
|
||||
void BMO_slot_vec_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, float r_vec[3]);
|
||||
|
||||
/**
|
||||
* Only supports square matrices.
|
||||
* size must be 3 or 4; this API is meant only for transformation matrices.
|
||||
*
|
||||
* \note the matrix is stored in 4x4 form, and it's safe to call whichever function you want.
|
||||
*/
|
||||
void BMO_slot_mat_set(BMOperator *op,
|
||||
BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
const float *mat,
|
||||
int size);
|
||||
void BMO_slot_mat4_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
float r_mat[4][4]);
|
||||
void BMO_slot_mat3_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
float r_mat[3][3]);
|
||||
|
||||
/** \} */
|
||||
|
||||
void BMO_mesh_flag_disable_all(BMesh *bm, BMOperator *op, char htype, short oflag);
|
||||
|
||||
void BMO_mesh_selected_remap(BMesh *bm,
|
||||
BMOpSlot *slot_vert_map,
|
||||
BMOpSlot *slot_edge_map,
|
||||
BMOpSlot *slot_face_map,
|
||||
bool check_select);
|
||||
|
||||
/**
|
||||
* Copies the values from another slot to the end of the output slot.
|
||||
*/
|
||||
#define BMO_slot_buffer_append( \
|
||||
op_src, slots_src, slot_name_src, op_dst, slots_dst, slot_name_dst) \
|
||||
_bmo_slot_buffer_append( \
|
||||
(op_src)->slots_src, slot_name_src, (op_dst)->slots_dst, slot_name_dst, (op_dst)->arena)
|
||||
/**
|
||||
* Copies the values from another slot to the end of the output slot.
|
||||
*/
|
||||
void _bmo_slot_buffer_append(BMOpSlot slot_args_dst[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name_dst,
|
||||
BMOpSlot slot_args_src[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name_src,
|
||||
struct MemArena *arena_dst);
|
||||
|
||||
/**
|
||||
* Puts every element of type 'type' (which is a bit-mask) with tool flag 'flag', into a slot.
|
||||
*/
|
||||
void BMO_slot_buffer_from_enabled_flag(BMesh *bm,
|
||||
BMOperator *op,
|
||||
BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
char htype,
|
||||
short oflag);
|
||||
|
||||
/**
|
||||
* Puts every element of type 'type' (which is a bit-mask) without tool flag 'flag', into a slot.
|
||||
*/
|
||||
void BMO_slot_buffer_from_disabled_flag(BMesh *bm,
|
||||
BMOperator *op,
|
||||
BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
char htype,
|
||||
short oflag);
|
||||
|
||||
/**
|
||||
* \brief BMO_FLAG_BUFFER
|
||||
*
|
||||
* Flags elements in a slots buffer
|
||||
*/
|
||||
void BMO_slot_buffer_flag_enable(BMesh *bm,
|
||||
BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
char htype,
|
||||
short oflag);
|
||||
/**
|
||||
* \brief BMO_FLAG_BUFFER
|
||||
*
|
||||
* Removes flags from elements in a slots buffer
|
||||
*/
|
||||
void BMO_slot_buffer_flag_disable(BMesh *bm,
|
||||
BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
char htype,
|
||||
short oflag);
|
||||
|
||||
/**
|
||||
* \brief BMO_FLAG_BUFFER
|
||||
*
|
||||
* Header Flags elements in a slots buffer, automatically
|
||||
* using the selection API where appropriate.
|
||||
*/
|
||||
void BMO_slot_buffer_hflag_enable(BMesh *bm,
|
||||
BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
char htype,
|
||||
char hflag,
|
||||
bool do_flush);
|
||||
/**
|
||||
* \brief BMO_FLAG_BUFFER
|
||||
*
|
||||
* Removes flags from elements in a slots buffer, automatically
|
||||
* using the selection API where appropriate.
|
||||
*/
|
||||
void BMO_slot_buffer_hflag_disable(BMesh *bm,
|
||||
BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
char htype,
|
||||
char hflag,
|
||||
bool do_flush);
|
||||
|
||||
/**
|
||||
* Puts every element of type 'type' (which is a bit-mask) with header flag 'flag', into a slot.
|
||||
* \note ignores hidden elements (e.g. elements with header flag BM_ELEM_HIDDEN set).
|
||||
*/
|
||||
void BMO_slot_buffer_from_enabled_hflag(BMesh *bm,
|
||||
BMOperator *op,
|
||||
BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
char htype,
|
||||
char hflag);
|
||||
/**
|
||||
* Puts every element of type 'type' (which is a bit-mask) without header flag 'flag', into a slot.
|
||||
* \note ignores hidden elements (e.g. elements with header flag BM_ELEM_HIDDEN set).
|
||||
*/
|
||||
void BMO_slot_buffer_from_disabled_hflag(BMesh *bm,
|
||||
BMOperator *op,
|
||||
BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
char htype,
|
||||
char hflag);
|
||||
|
||||
void BMO_slot_buffer_from_array(BMOperator *op,
|
||||
BMOpSlot *slot,
|
||||
BMHeader **ele_buffer,
|
||||
int ele_buffer_len);
|
||||
|
||||
void BMO_slot_buffer_from_single(BMOperator *op, BMOpSlot *slot, BMHeader *ele);
|
||||
void *BMO_slot_buffer_get_single(BMOpSlot *slot);
|
||||
|
||||
/** Return the number of elements inside a slot array. */
|
||||
int BMO_slot_buffer_len(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name);
|
||||
/** Return the number of elements inside a slot map. */
|
||||
int BMO_slot_map_len(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name);
|
||||
|
||||
/**
|
||||
* Inserts a key/value mapping into a mapping slot. note that it copies the
|
||||
* value, it doesn't store a reference to it.
|
||||
*/
|
||||
void BMO_slot_map_insert(BMOperator *op, BMOpSlot *slot, const void *element, const void *data);
|
||||
|
||||
/**
|
||||
* Flags all elements in a mapping.
|
||||
* \note that the mapping must only have #BMesh elements in it.
|
||||
*/
|
||||
void BMO_slot_map_to_flag(BMesh *bm,
|
||||
BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
char htype,
|
||||
short oflag);
|
||||
|
||||
void *BMO_slot_buffer_alloc(BMOperator *op,
|
||||
BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
int len);
|
||||
|
||||
/**
|
||||
* \brief BMO_ALL_TO_SLOT
|
||||
*
|
||||
* Copies all elements of a certain type into an operator slot.
|
||||
*/
|
||||
void BMO_slot_buffer_from_all(BMesh *bm,
|
||||
BMOperator *op,
|
||||
BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
char htype);
|
||||
|
||||
/**
|
||||
* This part of the API is used to iterate over element buffer or
|
||||
* mapping slots.
|
||||
*
|
||||
* for example, iterating over the faces in a slot is:
|
||||
*
|
||||
* \code{.c}
|
||||
*
|
||||
* BMOIter oiter;
|
||||
* BMFace *f;
|
||||
*
|
||||
* f = BMO_iter_new(&oiter, some_operator, "slot_name", BM_FACE);
|
||||
* for (; f; f = BMO_iter_step(&oiter)) {
|
||||
* // do something with the face
|
||||
* }
|
||||
*
|
||||
* another example, iterating over a mapping:
|
||||
* BMOIter oiter;
|
||||
* void *key;
|
||||
* void *val;
|
||||
*
|
||||
* key = BMO_iter_new(&oiter, bm, some_operator, "slot_name", 0);
|
||||
* for (; key; key = BMO_iter_step(&oiter)) {
|
||||
* val = BMO_iter_map_value(&oiter);
|
||||
* //do something with the key/val pair
|
||||
* //note that val is a pointer to the val data,
|
||||
* //whether it's a float, pointer, whatever.
|
||||
* //
|
||||
* // so to get a pointer, for example, use:
|
||||
* // *((void **)BMO_iter_map_value(&oiter));
|
||||
* //or something like that.
|
||||
* }
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
/* contents of this structure are private,
|
||||
* don't directly access. */
|
||||
struct BMOIter {
|
||||
BMOpSlot *slot;
|
||||
int cur; // for arrays
|
||||
GHashIterator giter;
|
||||
void **val;
|
||||
/** Bit-wise '&' with #BMHeader.htype */
|
||||
char restrictmask;
|
||||
};
|
||||
|
||||
void *BMO_slot_buffer_get_first(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name);
|
||||
|
||||
/**
|
||||
* \brief New Iterator
|
||||
*
|
||||
* \param restrictmask: restricts the iteration to certain element types
|
||||
* (e.g. combination of BM_VERT, BM_EDGE, BM_FACE), if iterating
|
||||
* over an element buffer (not a mapping). */
|
||||
void *BMO_iter_new(BMOIter *iter,
|
||||
BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
|
||||
const char *slot_name,
|
||||
char restrictmask);
|
||||
void *BMO_iter_step(BMOIter *iter);
|
||||
|
||||
/**
|
||||
* Returns a pointer to the key-value when iterating over mappings.
|
||||
* remember for pointer maps this will be a pointer to a pointer.
|
||||
*/
|
||||
void **BMO_iter_map_value_p(BMOIter *iter);
|
||||
void *BMO_iter_map_value_ptr(BMOIter *iter);
|
||||
|
||||
float BMO_iter_map_value_float(BMOIter *iter);
|
||||
int BMO_iter_map_value_int(BMOIter *iter);
|
||||
bool BMO_iter_map_value_bool(BMOIter *iter);
|
||||
|
||||
#define BMO_ITER(ele, iter, slot_args, slot_name, restrict_flag) \
|
||||
for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BMO_iter_new(iter, slot_args, slot_name, restrict_flag); \
|
||||
ele; \
|
||||
BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BMO_iter_step(iter))
|
||||
|
||||
#define BMO_ITER_INDEX(ele, iter, slot_args, slot_name, restrict_flag, i_) \
|
||||
for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BMO_iter_new(iter, slot_args, slot_name, restrict_flag), \
|
||||
i_ = 0; \
|
||||
ele; \
|
||||
BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BMO_iter_step(iter), i_++)
|
||||
|
||||
/* operator slot type information - size of one element of the type given. */
|
||||
extern const int BMO_OPSLOT_TYPEINFO[BMO_OP_SLOT_TOTAL_TYPES];
|
||||
|
||||
int BMO_opcode_from_opname(const char *opname);
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,227 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* BMesh inline operator functions.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_compiler_compat.h"
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
#include "intern/bmesh_operator_api.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMOperator;
|
||||
|
||||
/* Tool Flag API: Tool code must never put junk in header flags (#BMHeader.hflag)
|
||||
* instead, use this API to set flags.
|
||||
* If you need to store a value per element, use a #GHash or a mapping slot to do it. */
|
||||
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2) BLI_INLINE
|
||||
short _bmo_elem_flag_test(BMesh *bm, const BMFlagLayer *oflags, const short oflag)
|
||||
{
|
||||
BLI_assert(bm->use_toolflags);
|
||||
return oflags[bm->toolflag_index].f & oflag;
|
||||
}
|
||||
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2) BLI_INLINE
|
||||
bool _bmo_elem_flag_test_bool(BMesh *bm, const BMFlagLayer *oflags, const short oflag)
|
||||
{
|
||||
BLI_assert(bm->use_toolflags);
|
||||
return (oflags[bm->toolflag_index].f & oflag) != 0;
|
||||
}
|
||||
|
||||
ATTR_NONNULL(1, 2)
|
||||
BLI_INLINE void _bmo_elem_flag_enable(BMesh *bm, BMFlagLayer *oflags, const short oflag)
|
||||
{
|
||||
BLI_assert(bm->use_toolflags);
|
||||
oflags[bm->toolflag_index].f |= oflag;
|
||||
}
|
||||
|
||||
ATTR_NONNULL(1, 2)
|
||||
BLI_INLINE void _bmo_elem_flag_disable(BMesh *bm, BMFlagLayer *oflags, const short oflag)
|
||||
{
|
||||
BLI_assert(bm->use_toolflags);
|
||||
oflags[bm->toolflag_index].f &= short(~oflag);
|
||||
}
|
||||
|
||||
ATTR_NONNULL(1, 2)
|
||||
BLI_INLINE void _bmo_elem_flag_set(BMesh *bm, BMFlagLayer *oflags, const short oflag, int val)
|
||||
{
|
||||
BLI_assert(bm->use_toolflags);
|
||||
if (val) {
|
||||
oflags[bm->toolflag_index].f |= oflag;
|
||||
}
|
||||
else {
|
||||
oflags[bm->toolflag_index].f &= short(~oflag);
|
||||
}
|
||||
}
|
||||
|
||||
ATTR_NONNULL(1, 2)
|
||||
BLI_INLINE void _bmo_elem_flag_toggle(BMesh *bm, BMFlagLayer *oflags, const short oflag)
|
||||
{
|
||||
BLI_assert(bm->use_toolflags);
|
||||
oflags[bm->toolflag_index].f ^= oflag;
|
||||
}
|
||||
|
||||
ATTR_NONNULL(1, 2)
|
||||
BLI_INLINE void BMO_slot_map_int_insert(BMOperator *op,
|
||||
BMOpSlot *slot,
|
||||
void *element,
|
||||
const int val)
|
||||
{
|
||||
union {
|
||||
void *ptr;
|
||||
int val;
|
||||
} t = {nullptr};
|
||||
BLI_assert(slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_INT);
|
||||
BMO_slot_map_insert(op, slot, element, ((void)(t.val = val), t.ptr));
|
||||
}
|
||||
|
||||
ATTR_NONNULL(1, 2)
|
||||
BLI_INLINE void BMO_slot_map_bool_insert(BMOperator *op,
|
||||
BMOpSlot *slot,
|
||||
void *element,
|
||||
const bool val)
|
||||
{
|
||||
union {
|
||||
void *ptr;
|
||||
bool val;
|
||||
} t = {nullptr};
|
||||
BLI_assert(slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_BOOL);
|
||||
BMO_slot_map_insert(op, slot, element, ((void)(t.val = val), t.ptr));
|
||||
}
|
||||
|
||||
ATTR_NONNULL(1, 2)
|
||||
BLI_INLINE void BMO_slot_map_float_insert(BMOperator *op,
|
||||
BMOpSlot *slot,
|
||||
void *element,
|
||||
const float val)
|
||||
{
|
||||
union {
|
||||
void *ptr;
|
||||
float val;
|
||||
} t = {nullptr};
|
||||
BLI_assert(slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_FLT);
|
||||
BMO_slot_map_insert(op, slot, element, ((void)(t.val = val), t.ptr));
|
||||
}
|
||||
|
||||
/* pointer versions of BMO_slot_map_float_get and BMO_slot_map_float_insert.
|
||||
*
|
||||
* do NOT use these for non-operator-api-allocated memory! instead
|
||||
* use BMO_slot_map_data_get and BMO_slot_map_insert, which copies the data. */
|
||||
|
||||
ATTR_NONNULL(1, 2)
|
||||
BLI_INLINE void BMO_slot_map_ptr_insert(BMOperator *op,
|
||||
BMOpSlot *slot,
|
||||
const void *element,
|
||||
void *val)
|
||||
{
|
||||
BLI_assert(slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_INTERNAL);
|
||||
BMO_slot_map_insert(op, slot, element, val);
|
||||
}
|
||||
|
||||
ATTR_NONNULL(1, 2)
|
||||
BLI_INLINE void BMO_slot_map_elem_insert(BMOperator *op,
|
||||
BMOpSlot *slot,
|
||||
const void *element,
|
||||
void *val)
|
||||
{
|
||||
BLI_assert(slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_ELEM);
|
||||
BMO_slot_map_insert(op, slot, element, val);
|
||||
}
|
||||
|
||||
/* no values */
|
||||
ATTR_NONNULL(1, 2)
|
||||
BLI_INLINE void BMO_slot_map_empty_insert(BMOperator *op, BMOpSlot *slot, const void *element)
|
||||
{
|
||||
BLI_assert(slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_EMPTY);
|
||||
BMO_slot_map_insert(op, slot, element, nullptr);
|
||||
}
|
||||
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE
|
||||
bool BMO_slot_map_contains(BMOpSlot *slot, const void *element)
|
||||
{
|
||||
BLI_assert(slot->slot_type == BMO_OP_SLOT_MAPPING);
|
||||
return BLI_ghash_haskey(slot->data.ghash, element);
|
||||
}
|
||||
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE
|
||||
void **BMO_slot_map_data_get(BMOpSlot *slot, const void *element)
|
||||
{
|
||||
|
||||
return BLI_ghash_lookup_p(slot->data.ghash, element);
|
||||
}
|
||||
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE
|
||||
float BMO_slot_map_float_get(BMOpSlot *slot, const void *element)
|
||||
{
|
||||
void **data;
|
||||
BLI_assert(slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_FLT);
|
||||
|
||||
data = BMO_slot_map_data_get(slot, element);
|
||||
if (data) {
|
||||
return *reinterpret_cast<float *>(data);
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE
|
||||
int BMO_slot_map_int_get(BMOpSlot *slot, const void *element)
|
||||
{
|
||||
void **data;
|
||||
BLI_assert(slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_INT);
|
||||
|
||||
data = BMO_slot_map_data_get(slot, element);
|
||||
if (data) {
|
||||
return *reinterpret_cast<int *>(data);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE
|
||||
bool BMO_slot_map_bool_get(BMOpSlot *slot, const void *element)
|
||||
{
|
||||
void **data;
|
||||
BLI_assert(slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_BOOL);
|
||||
|
||||
data = BMO_slot_map_data_get(slot, element);
|
||||
if (data) {
|
||||
return *reinterpret_cast<bool *>(data);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE
|
||||
void *BMO_slot_map_ptr_get(BMOpSlot *slot, const void *element)
|
||||
{
|
||||
void **val = BMO_slot_map_data_get(slot, element);
|
||||
BLI_assert(slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_INTERNAL);
|
||||
if (val) {
|
||||
return *val;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE
|
||||
void *BMO_slot_map_elem_get(BMOpSlot *slot, const void *element)
|
||||
{
|
||||
void **val = static_cast<void **>(BMO_slot_map_data_get(slot, element));
|
||||
BLI_assert(slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_ELEM);
|
||||
if (val) {
|
||||
return *val;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
1920
blender-5.2.0/source/blender/bmesh/intern/bmesh_operators.cc
Normal file
1920
blender-5.2.0/source/blender/bmesh/intern/bmesh_operators.cc
Normal file
File diff suppressed because it is too large
Load Diff
241
blender-5.2.0/source/blender/bmesh/intern/bmesh_operators.hh
Normal file
241
blender-5.2.0/source/blender/bmesh/intern/bmesh_operators.hh
Normal file
@@ -0,0 +1,241 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "intern/bmesh_operator_api.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* See comments in `intern/bmesh_opdefines.cc` for documentation of specific operators. */
|
||||
|
||||
/*--------defines/enumerations for specific operators-------*/
|
||||
|
||||
/* Quad `innervert` values. */
|
||||
|
||||
enum {
|
||||
SUBD_CORNER_INNERVERT,
|
||||
SUBD_CORNER_PATH,
|
||||
SUBD_CORNER_FAN,
|
||||
SUBD_CORNER_STRAIGHT_CUT,
|
||||
};
|
||||
|
||||
/* aligned with PROP_SMOOTH and friends */
|
||||
enum {
|
||||
SUBD_FALLOFF_SMOOTH = 0,
|
||||
SUBD_FALLOFF_SPHERE,
|
||||
SUBD_FALLOFF_ROOT,
|
||||
SUBD_FALLOFF_SHARP,
|
||||
SUBD_FALLOFF_LIN,
|
||||
SUBD_FALLOFF_INVSQUARE = 7, /* matching PROP_INVSQUARE */
|
||||
};
|
||||
|
||||
enum {
|
||||
SUBDIV_SELECT_NONE,
|
||||
SUBDIV_SELECT_ORIG,
|
||||
SUBDIV_SELECT_INNER,
|
||||
SUBDIV_SELECT_LOOPCUT,
|
||||
};
|
||||
|
||||
/* subdivide_edgering */
|
||||
enum {
|
||||
/* just subdiv */
|
||||
SUBD_RING_INTERP_LINEAR,
|
||||
|
||||
/* single bezier spline - curve follows bezier rotation */
|
||||
SUBD_RING_INTERP_PATH,
|
||||
|
||||
/* beziers based on adjacent faces (fallback to tangent) */
|
||||
SUBD_RING_INTERP_SURF,
|
||||
};
|
||||
|
||||
/* similar face selection slot values */
|
||||
enum {
|
||||
SIMFACE_MATERIAL = 201,
|
||||
SIMFACE_AREA,
|
||||
SIMFACE_SIDES,
|
||||
SIMFACE_PERIMETER,
|
||||
SIMFACE_NORMAL,
|
||||
SIMFACE_COPLANAR,
|
||||
SIMFACE_SMOOTH,
|
||||
SIMFACE_FREESTYLE,
|
||||
};
|
||||
|
||||
/* similar edge selection slot values */
|
||||
enum {
|
||||
SIMEDGE_LENGTH = 101,
|
||||
SIMEDGE_DIR,
|
||||
SIMEDGE_FACE,
|
||||
SIMEDGE_FACE_ANGLE,
|
||||
SIMEDGE_CREASE,
|
||||
SIMEDGE_BEVEL,
|
||||
SIMEDGE_SEAM,
|
||||
SIMEDGE_SHARP,
|
||||
SIMEDGE_FREESTYLE,
|
||||
};
|
||||
|
||||
/* similar vertex selection slot values */
|
||||
enum {
|
||||
SIMVERT_NORMAL = 0,
|
||||
SIMVERT_FACE,
|
||||
SIMVERT_VGROUP,
|
||||
SIMVERT_EDGE,
|
||||
SIMVERT_CREASE,
|
||||
};
|
||||
|
||||
/* Poke face center calculation */
|
||||
enum {
|
||||
BMOP_POKE_MEDIAN_WEIGHTED = 0,
|
||||
BMOP_POKE_MEDIAN,
|
||||
BMOP_POKE_BOUNDS,
|
||||
};
|
||||
|
||||
/* Bevel offset_type slot values */
|
||||
enum {
|
||||
BEVEL_AMT_OFFSET,
|
||||
BEVEL_AMT_WIDTH,
|
||||
BEVEL_AMT_DEPTH,
|
||||
BEVEL_AMT_PERCENT,
|
||||
BEVEL_AMT_ABSOLUTE,
|
||||
};
|
||||
|
||||
/* Bevel profile type */
|
||||
enum {
|
||||
BEVEL_PROFILE_SUPERELLIPSE,
|
||||
BEVEL_PROFILE_CUSTOM,
|
||||
};
|
||||
|
||||
/* Bevel face_strength_mode values: should match face_str mode enum in DNA_modifier_types.h */
|
||||
enum {
|
||||
BEVEL_FACE_STRENGTH_NONE,
|
||||
BEVEL_FACE_STRENGTH_NEW,
|
||||
BEVEL_FACE_STRENGTH_AFFECTED,
|
||||
BEVEL_FACE_STRENGTH_ALL,
|
||||
};
|
||||
|
||||
/* Bevel miter slot values */
|
||||
enum {
|
||||
BEVEL_MITER_SHARP,
|
||||
BEVEL_MITER_PATCH,
|
||||
BEVEL_MITER_ARC,
|
||||
};
|
||||
|
||||
/* Bevel vertex mesh creation methods */
|
||||
enum {
|
||||
BEVEL_VMESH_ADJ,
|
||||
BEVEL_VMESH_CUTOFF,
|
||||
};
|
||||
|
||||
/* Bevel affect option. */
|
||||
enum {
|
||||
BEVEL_AFFECT_VERTICES = 0,
|
||||
BEVEL_AFFECT_EDGES = 1,
|
||||
};
|
||||
|
||||
/* Normal Face Strength values */
|
||||
enum {
|
||||
FACE_STRENGTH_WEAK = -16384,
|
||||
FACE_STRENGTH_MEDIUM = 0,
|
||||
FACE_STRENGTH_STRONG = 16384,
|
||||
};
|
||||
|
||||
/** Interpolation method used for spacing vertices. */
|
||||
enum SpaceInterpolationMethod {
|
||||
SPACE_EDGE_LOOPS_EVENLY_INTERP_CUBIC = 0,
|
||||
SPACE_EDGE_LOOPS_EVENLY_INTERP_LINEAR = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* Methods for determining the orientation of flattening the plane.
|
||||
*/
|
||||
enum FlattenMethod {
|
||||
FLATTEN_BEST_FIT = 0,
|
||||
FLATTEN_NORMAL = 1,
|
||||
FLATTEN_VIEW = 2,
|
||||
};
|
||||
|
||||
extern const BMOpDefine *bmo_opdefines[];
|
||||
extern const int bmo_opdefines_total;
|
||||
|
||||
/*------specific operator helper functions-------*/
|
||||
|
||||
void BM_mesh_esubdivide(BMesh *bm,
|
||||
char edge_hflag,
|
||||
float smooth,
|
||||
short smooth_falloff,
|
||||
bool use_smooth_even,
|
||||
float fractal,
|
||||
float along_normal,
|
||||
int numcuts,
|
||||
int seltype,
|
||||
int cornertype,
|
||||
short use_single_edge,
|
||||
short use_grid_fill,
|
||||
short use_only_quads,
|
||||
int seed);
|
||||
|
||||
/**
|
||||
* Fills first available UV-map with grid-like UVs for all faces with `oflag` set.
|
||||
*
|
||||
* \param bm: The BMesh to operate on
|
||||
* \param x_segments: The x-resolution of the grid
|
||||
* \param y_segments: The y-resolution of the grid
|
||||
* \param oflag: The flag to check faces with.
|
||||
*/
|
||||
void BM_mesh_calc_uvs_grid(
|
||||
BMesh *bm, uint x_segments, uint y_segments, short oflag, int cd_loop_uv_offset);
|
||||
/**
|
||||
* Fills first available UV-map with spherical projected UVs for all faces with `oflag` set.
|
||||
*
|
||||
* \param bm: The BMesh to operate on
|
||||
* \param oflag: The flag to check faces with.
|
||||
*/
|
||||
void BM_mesh_calc_uvs_sphere(BMesh *bm, short oflag, int cd_loop_uv_offset);
|
||||
/**
|
||||
* Fills first available UV-map with 2D projected UVs for all faces with `oflag` set.
|
||||
*
|
||||
* \param bm: The BMesh to operate on.
|
||||
* \param mat: The transform matrix applied to the created circle.
|
||||
* \param radius: The size of the circle.
|
||||
* \param oflag: The flag to check faces with.
|
||||
*/
|
||||
void BM_mesh_calc_uvs_circle(
|
||||
BMesh *bm, float mat[4][4], float radius, short oflag, int cd_loop_uv_offset);
|
||||
/**
|
||||
* Fills first available UV-map with cylinder/cone-like UVs for all faces with `oflag` set.
|
||||
*
|
||||
* \param bm: The BMesh to operate on.
|
||||
* \param mat: The transform matrix applied to the created cone/cylinder.
|
||||
* \param radius_top: The size of the top end of the cone/cylinder.
|
||||
* \param radius_bottom: The size of the bottom end of the cone/cylinder.
|
||||
* \param segments: The number of subdivisions in the sides of the cone/cylinder.
|
||||
* \param cap_ends: Whether the ends of the cone/cylinder are filled or not.
|
||||
* \param oflag: The flag to check faces with.
|
||||
*/
|
||||
void BM_mesh_calc_uvs_cone(BMesh *bm,
|
||||
float mat[4][4],
|
||||
float radius_top,
|
||||
float radius_bottom,
|
||||
int segments,
|
||||
bool cap_ends,
|
||||
short oflag,
|
||||
int cd_loop_uv_offset);
|
||||
/**
|
||||
* Fills first available UV-map with cube-like UVs for all faces with `oflag` set.
|
||||
*
|
||||
* \note Expects tagged faces to be six quads.
|
||||
* \note Caller must order faces for correct alignment.
|
||||
*
|
||||
* \param bm: The BMesh to operate on.
|
||||
* \param oflag: The flag to check faces with.
|
||||
*/
|
||||
void BM_mesh_calc_uvs_cube(BMesh *bm, short oflag);
|
||||
|
||||
} // namespace blender
|
||||
|
||||
#include "intern/bmesh_operator_api_inline.hh" /* IWYU pragma: export */
|
||||
@@ -0,0 +1,101 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMOperator;
|
||||
struct BMesh;
|
||||
|
||||
void bmo_average_vert_facedata_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_beautify_fill_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_bevel_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_bisect_edges_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_bisect_plane_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_bmesh_to_mesh_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_bridge_loops_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_collapse_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_collapse_uvs_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_connect_verts_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_connect_verts_concave_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_connect_verts_nonplanar_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_contextual_create_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_convex_hull_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_circularize_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_create_circle_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_create_cone_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_create_cube_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_create_grid_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_create_icosphere_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_create_monkey_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_create_vert_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_delete_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_dissolve_edges_init(BMOperator *op);
|
||||
void bmo_dissolve_edges_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_dissolve_faces_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_dissolve_verts_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_dissolve_limit_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_dissolve_degenerate_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_duplicate_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_edgeloop_fill_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_face_attribute_fill_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_holes_fill_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_edgenet_fill_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_edgenet_prepare_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_extrude_discrete_faces_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_extrude_edge_only_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_extrude_vert_indiv_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_find_doubles_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_flatten_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_grid_fill_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_inset_individual_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_inset_region_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_join_triangles_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_mesh_to_bmesh_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_mirror_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_object_load_bmesh_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_pointmerge_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_pointmerge_facedata_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_recalc_face_normals_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_poke_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_offset_edgeloops_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_planar_faces_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_region_extend_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_remove_doubles_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_reverse_colors_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_reverse_faces_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_reverse_uvs_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_rotate_colors_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_rotate_edges_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_rotate_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_rotate_uvs_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_scale_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_smooth_vert_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_smooth_laplacian_vert_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_solidify_face_region_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_space_edge_loops_evenly_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_spin_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_split_edges_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_split_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_subdivide_edges_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_subdivide_edgering_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_symmetrize_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_transform_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_translate_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_triangle_fill_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_triangulate_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_unsubdivide_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_weld_verts_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_wireframe_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_flip_quad_tessellation_exec(BMesh *bm, BMOperator *op);
|
||||
|
||||
} // namespace blender
|
||||
1530
blender-5.2.0/source/blender/bmesh/intern/bmesh_polygon.cc
Normal file
1530
blender-5.2.0/source/blender/bmesh/intern/bmesh_polygon.cc
Normal file
File diff suppressed because it is too large
Load Diff
306
blender-5.2.0/source/blender/bmesh/intern/bmesh_polygon.hh
Normal file
306
blender-5.2.0/source/blender/bmesh/intern/bmesh_polygon.hh
Normal file
@@ -0,0 +1,306 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Heap;
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
/**
|
||||
* For tools that insist on using triangles, ideally we would cache this data.
|
||||
*
|
||||
* \param use_fixed_quad: When true,
|
||||
* always split quad along (0 -> 2) regardless of concave corners,
|
||||
* (as done in #BM_mesh_calc_tessellation).
|
||||
* \param r_loops: Store face loop pointers, (f->len)
|
||||
* \param r_index: Store triangle triples, indices into \a r_loops, `((f->len - 2) * 3)`
|
||||
*/
|
||||
void BM_face_calc_tessellation(const BMFace *f,
|
||||
bool use_fixed_quad,
|
||||
BMLoop **r_loops,
|
||||
uint (*r_index)[3]);
|
||||
/**
|
||||
* Return a point inside the face.
|
||||
*/
|
||||
void BM_face_calc_point_in_face(const BMFace *f, float r_co[3]);
|
||||
|
||||
/**
|
||||
* \brief BMESH UPDATE FACE NORMAL
|
||||
*
|
||||
* Updates the stored normal for the
|
||||
* given face. Requires that a buffer
|
||||
* of sufficient length to store projected
|
||||
* coordinates for all of the face's vertices
|
||||
* is passed in as well.
|
||||
*/
|
||||
float BM_face_calc_normal(const BMFace *f, float r_no[3]) ATTR_NONNULL();
|
||||
/* exact same as 'BM_face_calc_normal' but accepts vertex coords */
|
||||
float BM_face_calc_normal_vcos(const BMesh *bm,
|
||||
const BMFace *f,
|
||||
float r_no[3],
|
||||
Span<float3> vertexCos) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Calculate a normal from a vertex cloud.
|
||||
*/
|
||||
void BM_verts_calc_normal_from_cloud_ex(
|
||||
BMVert **varr, int varr_len, float r_normal[3], float r_center[3], int *r_index_tangent);
|
||||
void BM_verts_calc_normal_from_cloud(BMVert **varr, int varr_len, float r_normal[3]);
|
||||
|
||||
/**
|
||||
* Calculates the face subset normal.
|
||||
*/
|
||||
float BM_face_calc_normal_subset(const BMLoop *l_first, const BMLoop *l_last, float r_no[3])
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* get the area of the face
|
||||
*/
|
||||
float BM_face_calc_area(const BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Get the area of the face in world space.
|
||||
*/
|
||||
float BM_face_calc_area_with_mat3(const BMFace *f, const float mat3[3][3]) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* Calculate the signed area of UV face.
|
||||
*/
|
||||
float BM_face_calc_area_uv_signed(const BMFace *f, int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* Calculate the area of UV face.
|
||||
*/
|
||||
float BM_face_calc_area_uv(const BMFace *f, int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* compute the perimeter of an ngon
|
||||
*/
|
||||
float BM_face_calc_perimeter(const BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Calculate the perimeter of a ngon in world space.
|
||||
*/
|
||||
float BM_face_calc_perimeter_with_mat3(const BMFace *f,
|
||||
const float mat3[3][3]) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* Compute the tangent of the face, using the longest edge.
|
||||
*/
|
||||
void BM_face_calc_tangent_from_edge(const BMFace *f, float r_tangent[3]) ATTR_NONNULL();
|
||||
void BM_face_calc_tangent_pair_from_edge(const BMFace *f,
|
||||
float r_tangent_a[3],
|
||||
float r_tangent_b[3]);
|
||||
|
||||
/**
|
||||
* Compute the tangent of the face, using the two longest disconnected edges.
|
||||
*
|
||||
* \param r_tangent: Calculated unit length tangent (return value).
|
||||
*/
|
||||
void BM_face_calc_tangent_from_edge_pair(const BMFace *f, float r_tangent[3]) ATTR_NONNULL();
|
||||
/**
|
||||
* Compute the tangent of the face, using the edge farthest away from any vertex in the face.
|
||||
*
|
||||
* \param r_tangent: Calculated unit length tangent (return value).
|
||||
*/
|
||||
void BM_face_calc_tangent_from_edge_diagonal(const BMFace *f, float r_tangent[3]) ATTR_NONNULL();
|
||||
/**
|
||||
* Compute the tangent of the face, using longest distance between vertices on the face.
|
||||
*
|
||||
* \note The logic is almost identical to #BM_face_calc_tangent_edge_diagonal
|
||||
*/
|
||||
void BM_face_calc_tangent_from_vert_diagonal(const BMFace *f, float r_tangent[3]) ATTR_NONNULL();
|
||||
/**
|
||||
* Compute a meaningful direction along the face (use for gizmo axis).
|
||||
*
|
||||
* \note Callers shouldn't depend on the *exact* method used here.
|
||||
*/
|
||||
void BM_face_calc_tangent_auto(const BMFace *f, float r_tangent[3]) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* A version of BM_face_calc_tangent_auto that calculates two tangents.
|
||||
* Useful when one may not be usable.
|
||||
*/
|
||||
void BM_face_calc_tangent_pair_auto(const BMFace *f, float r_tangent_a[3], float r_tangent_b[3])
|
||||
ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* computes center of face in 3d. uses center of bounding box.
|
||||
*/
|
||||
void BM_face_calc_center_bounds(const BMFace *f, float r_cent[3]) ATTR_NONNULL();
|
||||
/**
|
||||
* computes center of face in 3d. uses center of bounding box.
|
||||
*/
|
||||
void BM_face_calc_center_bounds_vcos(const BMesh *bm,
|
||||
const BMFace *f,
|
||||
float r_center[3],
|
||||
Span<float3> vert_positions) ATTR_NONNULL();
|
||||
/**
|
||||
* computes the center of a face, using the mean average
|
||||
*/
|
||||
void BM_face_calc_center_median(const BMFace *f, float r_center[3]) ATTR_NONNULL();
|
||||
/* exact same as 'BM_face_calc_normal' but accepts vertex coords */
|
||||
void BM_face_calc_center_median_vcos(const BMesh *bm,
|
||||
const BMFace *f,
|
||||
float r_center[3],
|
||||
const Span<float3> vert_positions) ATTR_NONNULL();
|
||||
/**
|
||||
* computes the center of a face, using the mean average
|
||||
* weighted by edge length
|
||||
*/
|
||||
void BM_face_calc_center_median_weighted(const BMFace *f, float r_cent[3]) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* expands bounds (min/max must be initialized).
|
||||
*/
|
||||
void BM_face_calc_bounds_expand(const BMFace *f, float min[3], float max[3]);
|
||||
|
||||
void BM_face_normal_update(BMFace *f) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* updates face and vertex normals incident on an edge
|
||||
*/
|
||||
void BM_edge_normals_update(BMEdge *e) ATTR_NONNULL();
|
||||
|
||||
bool BM_vert_calc_normal_ex(const BMVert *v, char hflag, float r_no[3]);
|
||||
bool BM_vert_calc_normal(const BMVert *v, float r_no[3]);
|
||||
/**
|
||||
* update a vert normal (but not the faces incident on it)
|
||||
*/
|
||||
void BM_vert_normal_update(BMVert *v) ATTR_NONNULL();
|
||||
void BM_vert_normal_update_all(BMVert *v) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* \brief Face Flip Normal
|
||||
*
|
||||
* Reverses the winding of a face.
|
||||
* \note This updates the calculated normal.
|
||||
*/
|
||||
void BM_face_normal_flip_ex(BMesh *bm,
|
||||
BMFace *f,
|
||||
int cd_loop_mdisp_offset,
|
||||
bool use_loop_mdisp_flip) ATTR_NONNULL();
|
||||
void BM_face_normal_flip(BMesh *bm, BMFace *f) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* BM POINT IN FACE
|
||||
*
|
||||
* Projects co onto face f, and returns true if it is inside
|
||||
* the face bounds.
|
||||
*
|
||||
* \note this uses a best-axis projection test,
|
||||
* instead of projecting co directly into f's orientation space,
|
||||
* so there might be accuracy issues.
|
||||
*/
|
||||
bool BM_face_point_inside_test(const BMFace *f, const float co[3]) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* \brief BMESH TRIANGULATE FACE
|
||||
*
|
||||
* Breaks all quads and ngons down to triangles.
|
||||
* It uses poly-fill for the ngons splitting, and
|
||||
* the beautify operator when use_beauty is true.
|
||||
*
|
||||
* \param r_faces_new: if non-null, must be an array of BMFace pointers,
|
||||
* with a length equal to (f->len - 3). It will be filled with the new
|
||||
* triangles (not including the original triangle).
|
||||
*
|
||||
* \param r_faces_double: When newly created faces are duplicates of existing faces,
|
||||
* they're added to this list. Caller must handle de-duplication.
|
||||
* This is done because its possible _all_ faces exist already,
|
||||
* and in that case we would have to remove all faces including the one passed,
|
||||
* which causes complications adding/removing faces while looking over them.
|
||||
*
|
||||
* \note The number of faces is _almost_ always (f->len - 3),
|
||||
* However there may be faces that already occupying the
|
||||
* triangles we would make, so the caller must check \a r_faces_new_tot.
|
||||
*
|
||||
* \note use_tag tags new flags and edges.
|
||||
*/
|
||||
void BM_face_triangulate(BMesh *bm,
|
||||
BMFace *f,
|
||||
BMFace **r_faces_new,
|
||||
int *r_faces_new_tot,
|
||||
BMEdge **r_edges_new,
|
||||
int *r_edges_new_tot,
|
||||
struct LinkNode **r_faces_double,
|
||||
int quad_method,
|
||||
int ngon_method,
|
||||
bool use_tag,
|
||||
struct MemArena *pf_arena,
|
||||
struct Heap *pf_heap) ATTR_NONNULL(1, 2);
|
||||
|
||||
/**
|
||||
* each pair of loops defines a new edge, a split. this function goes
|
||||
* through and sets pairs that are geometrically invalid to null. a
|
||||
* split is invalid, if it forms a concave angle or it intersects other
|
||||
* edges in the face, or it intersects another split. in the case of
|
||||
* intersecting splits, only the first of the set of intersecting
|
||||
* splits survives
|
||||
*/
|
||||
void BM_face_splits_check_legal(BMesh *bm, BMFace *f, BMLoop *(*loops)[2], int len) ATTR_NONNULL();
|
||||
/**
|
||||
* This simply checks that the verts don't connect faces which would have more optimal splits.
|
||||
* but _not_ check for correctness.
|
||||
*/
|
||||
void BM_face_splits_check_optimal(BMFace *f, BMLoop *(*loops)[2], int len) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Small utility functions for fast access
|
||||
*
|
||||
* faster alternative to:
|
||||
* BM_iter_as_array(bm, BM_VERTS_OF_FACE, f, (void **)v, 3);
|
||||
*/
|
||||
void BM_face_as_array_vert_tri(BMFace *f, BMVert *r_verts[3]) ATTR_NONNULL();
|
||||
/**
|
||||
* faster alternative to:
|
||||
* BM_iter_as_array(bm, BM_VERTS_OF_FACE, f, (void **)v, 4);
|
||||
*/
|
||||
void BM_face_as_array_vert_quad(BMFace *f, BMVert *r_verts[4]) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Small utility functions for fast access
|
||||
*
|
||||
* faster alternative to:
|
||||
* BM_iter_as_array(bm, BM_LOOPS_OF_FACE, f, (void **)l, 3);
|
||||
*/
|
||||
void BM_face_as_array_loop_tri(BMFace *f, BMLoop *r_loops[3]) ATTR_NONNULL();
|
||||
/**
|
||||
* faster alternative to:
|
||||
* BM_iter_as_array(bm, BM_LOOPS_OF_FACE, f, (void **)l, 4);
|
||||
*/
|
||||
void BM_face_as_array_loop_quad(BMFace *f, BMLoop *r_loops[4]) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Calculate a tangent from any 3 vertices.
|
||||
*
|
||||
* The tangent aligns to the most *unique* edge
|
||||
* (the edge most unlike the other two).
|
||||
*
|
||||
* \param r_tangent: Calculated unit length tangent (return value).
|
||||
*/
|
||||
void BM_vert_tri_calc_tangent_from_edge(BMVert *verts[3], float r_tangent[3]);
|
||||
void BM_vert_tri_calc_tangent_pair_from_edge(BMVert *verts[3],
|
||||
float r_tangent_a[3],
|
||||
float r_tangent_b[3]);
|
||||
/**
|
||||
* Calculate a tangent from any 3 vertices,
|
||||
*
|
||||
* The tangent follows the center-line formed by the most unique edges center
|
||||
* and the opposite vertex.
|
||||
*
|
||||
* \param r_tangent: Calculated unit length tangent (return value).
|
||||
*/
|
||||
void BM_vert_tri_calc_tangent_edge_pair(BMVert *verts[3], float r_tangent[3]);
|
||||
|
||||
} // namespace blender
|
||||
1758
blender-5.2.0/source/blender/bmesh/intern/bmesh_polygon_edgenet.cc
Normal file
1758
blender-5.2.0/source/blender/bmesh/intern/bmesh_polygon_edgenet.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,46 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
/**
|
||||
* Splits a face into many smaller faces defined by an edge-net.
|
||||
* handle customdata and degenerate cases.
|
||||
*
|
||||
* - Isolated holes or unsupported face configurations, will be ignored.
|
||||
* - Customdata calculations aren't efficient
|
||||
* (need to calculate weights for each vert).
|
||||
*/
|
||||
bool BM_face_split_edgenet(
|
||||
BMesh *bm, BMFace *f, BMEdge **edge_net, int edge_net_len, Vector<BMFace *> *r_face_arr);
|
||||
|
||||
/**
|
||||
* For when the edge-net has holes in it-this connects them.
|
||||
*
|
||||
* \param use_partial_connect: Support for handling islands connected by only a single edge,
|
||||
* \note that this is quite slow so avoid using where possible.
|
||||
* \param mem_arena: Avoids many small allocations & should be cleared after each use.
|
||||
* take care since \a edge_net_new is stored in \a r_edge_net_new.
|
||||
*/
|
||||
bool BM_face_split_edgenet_connect_islands(BMesh *bm,
|
||||
BMFace *f,
|
||||
BMEdge **edge_net_init,
|
||||
uint edge_net_init_len,
|
||||
bool use_partial_connect,
|
||||
struct MemArena *mem_arena,
|
||||
BMEdge ***r_edge_net_new,
|
||||
uint *r_edge_net_new_len) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL(1, 2, 3, 6, 7, 8);
|
||||
|
||||
} // namespace blender
|
||||
95
blender-5.2.0/source/blender/bmesh/intern/bmesh_private.hh
Normal file
95
blender-5.2.0/source/blender/bmesh/intern/bmesh_private.hh
Normal file
@@ -0,0 +1,95 @@
|
||||
/* SPDX-FileCopyrightText: 2004 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* Private function prototypes for bmesh public API.
|
||||
* This file is a grab-bag of functions from various
|
||||
* parts of the bmesh internals.
|
||||
*/
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* returns positive nonzero on error */
|
||||
|
||||
#ifdef NDEBUG
|
||||
/* No error checking for release,
|
||||
* it can take most of the CPU time when running some tools. */
|
||||
# define BM_CHECK_ELEMENT(el) (void)(el)
|
||||
#else
|
||||
/**
|
||||
* Check the element is valid.
|
||||
*
|
||||
* BMESH_TODO, when this raises an error the output is incredibly confusing.
|
||||
* need to have some nice way to print/debug what the heck's going on.
|
||||
*/
|
||||
int bmesh_elem_check(void *element, char htype);
|
||||
# define BM_CHECK_ELEMENT(el) \
|
||||
{ \
|
||||
if (bmesh_elem_check(el, ((BMHeader *)el)->htype)) { \
|
||||
printf( \
|
||||
"check_element failure, with code %i on line %i in file\n" \
|
||||
" \"%s\"\n\n", \
|
||||
bmesh_elem_check(el, ((BMHeader *)el)->htype), \
|
||||
__LINE__, \
|
||||
__FILE__); \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
#endif
|
||||
|
||||
int bmesh_radial_length(const BMLoop *l);
|
||||
int bmesh_disk_count_at_most(const BMVert *v, int count_max);
|
||||
int bmesh_disk_count(const BMVert *v);
|
||||
|
||||
/**
|
||||
* Internal BMHeader.api_flag
|
||||
* \note Ensure different parts of the API do not conflict
|
||||
* on using these internal flags!
|
||||
*/
|
||||
enum {
|
||||
_FLAG_JF = (1 << 0), /* Join faces. */
|
||||
_FLAG_MF = (1 << 1), /* Make face. */
|
||||
_FLAG_MV = (1 << 1), /* Make face, vertex. */
|
||||
_FLAG_OVERLAP = (1 << 2), /* General overlap flag. */
|
||||
_FLAG_WALK = (1 << 3), /* General walk flag (keep clean). */
|
||||
_FLAG_WALK_ALT = (1 << 4), /* Same as #_FLAG_WALK, for when a second tag is needed. */
|
||||
|
||||
_FLAG_ELEM_CHECK = (1 << 7), /* Reserved for bmesh_elem_check. */
|
||||
};
|
||||
|
||||
#define BM_ELEM_API_FLAG_ENABLE(element, f) \
|
||||
{ \
|
||||
((element)->head.api_flag |= (f)); \
|
||||
} \
|
||||
(void)0
|
||||
#define BM_ELEM_API_FLAG_DISABLE(element, f) \
|
||||
{ \
|
||||
((element)->head.api_flag &= (uchar) ~(f)); \
|
||||
} \
|
||||
(void)0
|
||||
#define BM_ELEM_API_FLAG_TEST(element, f) ((element)->head.api_flag & (f))
|
||||
#define BM_ELEM_API_FLAG_CLEAR(element) \
|
||||
{ \
|
||||
((element)->head.api_flag = 0); \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
/**
|
||||
* \brief POLY ROTATE PLANE
|
||||
*
|
||||
* Rotates a polygon so that its
|
||||
* normal is pointing towards the mesh Z axis
|
||||
*/
|
||||
void poly_rotate_plane(const float normal[3], float (*verts)[3], uint nverts);
|
||||
|
||||
} // namespace blender
|
||||
|
||||
/* include the rest of our private declarations */
|
||||
#include "bmesh_structure.hh" /* IWYU pragma: export */
|
||||
2585
blender-5.2.0/source/blender/bmesh/intern/bmesh_query.cc
Normal file
2585
blender-5.2.0/source/blender/bmesh/intern/bmesh_query.cc
Normal file
File diff suppressed because it is too large
Load Diff
799
blender-5.2.0/source/blender/bmesh/intern/bmesh_query.hh
Normal file
799
blender-5.2.0/source/blender/bmesh/intern/bmesh_query.hh
Normal file
@@ -0,0 +1,799 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_compiler_compat.h"
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
/**
|
||||
* Returns true if the vertex is used in a given face.
|
||||
*/
|
||||
|
||||
namespace blender {
|
||||
|
||||
bool BM_vert_in_face(BMVert *v, BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Compares the number of vertices in an array
|
||||
* that appear in a given face
|
||||
*/
|
||||
int BM_verts_in_face_count(BMVert **varr, int len, BMFace *f) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* Return true if all verts are in the face.
|
||||
*/
|
||||
bool BM_verts_in_face(BMVert **varr, int len, BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Returns whether or not a given edge is part of a given face.
|
||||
*/
|
||||
bool BM_edge_in_face(const BMEdge *e, const BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
BLI_INLINE bool BM_edge_in_loop(const BMEdge *e, const BMLoop *l) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
|
||||
BLI_INLINE bool BM_vert_in_edge(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
BLI_INLINE bool BM_verts_in_edge(const BMVert *v1,
|
||||
const BMVert *v2,
|
||||
const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Returns edge length
|
||||
*/
|
||||
float BM_edge_calc_length(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Returns edge length squared (for comparisons)
|
||||
*/
|
||||
float BM_edge_calc_length_squared(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Utility function, since enough times we have an edge
|
||||
* and want to access 2 connected faces.
|
||||
*
|
||||
* \return true when only 2 faces are found.
|
||||
*/
|
||||
bool BM_edge_face_pair(BMEdge *e, BMFace **r_fa, BMFace **r_fb) ATTR_NONNULL();
|
||||
/**
|
||||
* Utility function, since enough times we have an edge
|
||||
* and want to access 2 connected loops.
|
||||
*
|
||||
* \return true when only 2 faces are found.
|
||||
*/
|
||||
bool BM_edge_loop_pair(BMEdge *e, BMLoop **r_la, BMLoop **r_lb) ATTR_NONNULL();
|
||||
BLI_INLINE BMVert *BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* Given a edge and a loop (assumes the edge is manifold). returns
|
||||
* the other faces loop, sharing the same vertex.
|
||||
*
|
||||
* <pre>
|
||||
* +-------------------+
|
||||
* | |
|
||||
* | |
|
||||
* |l_other <-- return |
|
||||
* +-------------------+ <-- A manifold edge between 2 faces
|
||||
* |l e <-- edge |
|
||||
* |^ <-------- loop |
|
||||
* | |
|
||||
* +-------------------+
|
||||
* </pre>
|
||||
*/
|
||||
BMLoop *BM_edge_other_loop(BMEdge *e, BMLoop *l) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* \brief Other Loop in Face Sharing an Edge
|
||||
*
|
||||
* Finds the other loop that shares \a v with \a e loop in \a f.
|
||||
* <pre>
|
||||
* +----------+
|
||||
* | |
|
||||
* | f |
|
||||
* | |
|
||||
* +----------+ <-- return the face loop of this vertex.
|
||||
* v --> e
|
||||
* ^ ^ <------- These vert args define direction
|
||||
* in the face to check.
|
||||
* The faces loop direction is ignored.
|
||||
* </pre>
|
||||
*
|
||||
* \note caller must ensure \a e is used in \a f
|
||||
*/
|
||||
BMLoop *BM_face_other_edge_loop(BMFace *f, BMEdge *e, BMVert *v) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* See #BM_face_other_edge_loop This is the same functionality
|
||||
* to be used when the edges loop is already known.
|
||||
*/
|
||||
BMLoop *BM_loop_other_edge_loop(BMLoop *l, BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* \brief Other Loop in Face Sharing a Vertex
|
||||
*
|
||||
* Finds the other loop in a face.
|
||||
*
|
||||
* This function returns a loop in \a f that shares an edge with \a v
|
||||
* The direction is defined by \a v_prev, where the return value is
|
||||
* the loop of what would be 'v_next'
|
||||
* <pre>
|
||||
* +----------+ <-- return the face loop of this vertex.
|
||||
* | |
|
||||
* | f |
|
||||
* | |
|
||||
* +----------+
|
||||
* v_prev --> v
|
||||
* ^^^^^^ ^ <-- These vert args define direction
|
||||
* in the face to check.
|
||||
* The faces loop direction is ignored.
|
||||
* </pre>
|
||||
*
|
||||
* \note \a v_prev and \a v _implicitly_ define an edge.
|
||||
*/
|
||||
BMLoop *BM_face_other_vert_loop(BMFace *f, BMVert *v_prev, BMVert *v) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* Return the other loop that uses this edge.
|
||||
*
|
||||
* In this case the loop defines the vertex,
|
||||
* the edge passed in defines the direction to step.
|
||||
*
|
||||
* <pre>
|
||||
* +----------+ <-- Return the face-loop of this vertex.
|
||||
* | |
|
||||
* | e | <-- This edge defines the direction.
|
||||
* | |
|
||||
* +----------+ <-- This loop defines the face and vertex..
|
||||
* l
|
||||
* </pre>
|
||||
*/
|
||||
BMLoop *BM_loop_other_vert_loop_by_edge(BMLoop *l, BMEdge *e) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* \brief Other Loop in Face Sharing a Vert
|
||||
*
|
||||
* Finds the other loop that shares \a v with \a e loop in \a f.
|
||||
* <pre>
|
||||
* +----------+ <-- return the face loop of this vertex.
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* +----------+ <-- This vertex defines the direction.
|
||||
* l v
|
||||
* ^ <------- This loop defines both the face to search
|
||||
* and the edge, in combination with 'v'
|
||||
* The faces loop direction is ignored.
|
||||
* </pre>
|
||||
*/
|
||||
BMLoop *BM_loop_other_vert_loop(BMLoop *l, BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Utility function to step around a fan of loops,
|
||||
* using an edge to mark the previous side.
|
||||
*
|
||||
* \note all edges must be manifold,
|
||||
* once a non manifold edge is hit, return NULL.
|
||||
*
|
||||
* \code{.unparsed}
|
||||
* ,.,-->|
|
||||
* _,-' |
|
||||
* ,' | (notice how 'e_step'
|
||||
* / | and 'l' define the
|
||||
* / | direction the arrow
|
||||
* | return | points).
|
||||
* | loop --> |
|
||||
* ---------------------+---------------------
|
||||
* ^ l --> |
|
||||
* | |
|
||||
* assign e_step |
|
||||
* |
|
||||
* begin e_step ----> |
|
||||
* |
|
||||
* \endcode
|
||||
*/
|
||||
BMLoop *BM_vert_step_fan_loop(BMLoop *l, BMEdge **e_step) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Get the first loop of a vert. Uses the same initialization code for the first loop of the
|
||||
* iterator API
|
||||
*/
|
||||
BMLoop *BM_vert_find_first_loop(BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* A version of #BM_vert_find_first_loop that ignores hidden loops.
|
||||
*/
|
||||
BMLoop *BM_vert_find_first_loop_visible(BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Only #BMEdge.l access us needed, however when we want the first visible loop,
|
||||
* a utility function is needed.
|
||||
*/
|
||||
BMLoop *BM_edge_find_first_loop_visible(BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Check if verts share a face.
|
||||
*/
|
||||
bool BM_vert_pair_share_face_check(BMVert *v_a, BMVert *v_b) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
bool BM_vert_pair_share_face_check_cb(BMVert *v_a,
|
||||
BMVert *v_b,
|
||||
bool (*test_fn)(BMFace *f, void *user_data),
|
||||
void *user_data) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL(1, 2, 3);
|
||||
BMFace *BM_vert_pair_shared_face_cb(BMVert *v_a,
|
||||
BMVert *v_b,
|
||||
bool allow_adjacent,
|
||||
bool (*callback)(BMFace *, BMLoop *, BMLoop *, void *userdata),
|
||||
void *user_data,
|
||||
BMLoop **r_l_a,
|
||||
BMLoop **r_l_b) ATTR_NONNULL(1, 2, 4, 6, 7);
|
||||
/**
|
||||
* Given 2 verts, find the smallest face they share and give back both loops.
|
||||
*/
|
||||
BMFace *BM_vert_pair_share_face_by_len(
|
||||
BMVert *v_a, BMVert *v_b, BMLoop **r_l_a, BMLoop **r_l_b, bool allow_adjacent) ATTR_NONNULL();
|
||||
/**
|
||||
* Given 2 verts,
|
||||
* find a face they share that has the lowest angle across these verts and give back both loops.
|
||||
*
|
||||
* This can be better than #BM_vert_pair_share_face_by_len
|
||||
* because concave splits are ranked lowest.
|
||||
*/
|
||||
BMFace *BM_vert_pair_share_face_by_angle(
|
||||
BMVert *v_a, BMVert *v_b, BMLoop **r_l_a, BMLoop **r_l_b, bool allow_adjacent) ATTR_NONNULL();
|
||||
|
||||
BMFace *BM_edge_pair_share_face_by_len(
|
||||
BMEdge *e_a, BMEdge *e_b, BMLoop **r_l_a, BMLoop **r_l_b, bool allow_adjacent) ATTR_NONNULL();
|
||||
|
||||
int BM_vert_edge_count_nonwire(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
#define BM_vert_edge_count_is_equal(v, n) (BM_vert_edge_count_at_most(v, (n) + 1) == n)
|
||||
#define BM_vert_edge_count_is_over(v, n) (BM_vert_edge_count_at_most(v, (n) + 1) == (n) + 1)
|
||||
int BM_vert_edge_count_at_most(const BMVert *v, int count_max) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* Returns the number of edges around this vertex.
|
||||
*/
|
||||
int BM_vert_edge_count(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
#define BM_edge_face_count_is_equal(e, n) (BM_edge_face_count_at_most(e, (n) + 1) == n)
|
||||
#define BM_edge_face_count_is_over(e, n) (BM_edge_face_count_at_most(e, (n) + 1) == (n) + 1)
|
||||
int BM_edge_face_count_at_most(const BMEdge *e, int count_max) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* Returns the number of faces around this edge
|
||||
*/
|
||||
int BM_edge_face_count(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
#define BM_vert_face_count_is_equal(v, n) (BM_vert_face_count_at_most(v, (n) + 1) == n)
|
||||
#define BM_vert_face_count_is_over(v, n) (BM_vert_face_count_at_most(v, (n) + 1) == (n) + 1)
|
||||
int BM_vert_face_count_at_most(const BMVert *v, int count_max) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* Returns the number of faces around this vert
|
||||
* length matches #BM_LOOPS_OF_VERT iterator
|
||||
*/
|
||||
int BM_vert_face_count(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* The function takes a vertex at the center of a fan and returns the opposite edge in the fan.
|
||||
* All edges in the fan must be manifold, otherwise return NULL.
|
||||
*
|
||||
* \note This could (probably) be done more efficiently.
|
||||
*/
|
||||
BMEdge *BM_vert_other_disk_edge(BMVert *v, BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Fast alternative to `(BM_vert_edge_count(v) == 2)`.
|
||||
*/
|
||||
bool BM_vert_is_edge_pair(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Fast alternative to `(BM_vert_edge_count(v) == 2)`
|
||||
* that checks both edges connect to the same faces.
|
||||
*/
|
||||
bool BM_vert_is_edge_pair_manifold(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Access a verts 2 connected edges.
|
||||
*
|
||||
* \return true when only 2 verts are found.
|
||||
*/
|
||||
bool BM_vert_edge_pair(const BMVert *v, BMEdge **r_e_a, BMEdge **r_e_b);
|
||||
/**
|
||||
* Return true if the vertex is connected to _any_ faces.
|
||||
*
|
||||
* same as `BM_vert_face_count(v) != 0` or `BM_vert_find_first_loop(v) == NULL`.
|
||||
*/
|
||||
bool BM_vert_face_check(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Tests whether or not the vertex is part of a wire edge.
|
||||
* (ie: has no faces attached to it)
|
||||
*/
|
||||
bool BM_vert_is_wire(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
BLI_INLINE bool BM_edge_is_wire(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* A vertex is non-manifold if it meets the following conditions:
|
||||
* 1: Loose - (has no edges/faces incident upon it).
|
||||
* 2: Joins two distinct regions - (two pyramids joined at the tip).
|
||||
* 3: Is part of an edge with more than 2 faces.
|
||||
* 4: Is part of a wire edge.
|
||||
*/
|
||||
bool BM_vert_is_manifold(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* A version of #BM_vert_is_manifold
|
||||
* which only checks if we're connected to multiple isolated regions.
|
||||
*/
|
||||
bool BM_vert_is_manifold_region(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
BLI_INLINE bool BM_edge_is_manifold(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
bool BM_vert_is_boundary(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
BLI_INLINE bool BM_edge_is_boundary(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
BLI_INLINE bool BM_edge_is_contiguous(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Check if the edge is convex or concave
|
||||
* (depends on face winding)
|
||||
*/
|
||||
bool BM_edge_is_convex(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* \return true when loop customdata is contiguous.
|
||||
*/
|
||||
bool BM_edge_is_contiguous_loop_cd(const BMEdge *e,
|
||||
int cd_loop_type,
|
||||
int cd_loop_offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* The number of loops connected to this loop (not including disconnected regions).
|
||||
*/
|
||||
int BM_loop_region_loops_count_at_most(BMLoop *l, int *r_loop_total) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL(1);
|
||||
int BM_loop_region_loops_count(BMLoop *l) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
|
||||
/**
|
||||
* Check if the loop is convex or concave
|
||||
* (depends on face normal)
|
||||
*/
|
||||
bool BM_loop_is_convex(const BMLoop *l) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
BLI_INLINE bool BM_loop_is_adjacent(const BMLoop *l_a, const BMLoop *l_b) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* Check if a point is inside the corner defined by a loop
|
||||
* (within the 2 planes defined by the loops corner & face normal).
|
||||
*
|
||||
* \return signed, squared distance to the loops planes, less than 0.0 when outside.
|
||||
*/
|
||||
float BM_loop_point_side_of_loop_test(const BMLoop *l, const float co[3]) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* Check if a point is inside the edge defined by a loop
|
||||
* (within the plane defined by the loops edge & face normal).
|
||||
*
|
||||
* \return signed, squared distance to the edge plane, less than 0.0 when outside.
|
||||
*/
|
||||
float BM_loop_point_side_of_edge_test(const BMLoop *l, const float co[3]) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* \return The previous loop, over \a eps_sq distance from \a l (or \a NULL if l_stop is reached).
|
||||
*/
|
||||
BMLoop *BM_loop_find_prev_nodouble(BMLoop *l, BMLoop *l_stop, float eps_sq);
|
||||
/**
|
||||
* \return The next loop, over \a eps_sq distance from \a l (or \a NULL if l_stop is reached).
|
||||
*/
|
||||
BMLoop *BM_loop_find_next_nodouble(BMLoop *l, BMLoop *l_stop, float eps_sq);
|
||||
|
||||
/**
|
||||
* Calculates the angle between the previous and next loops
|
||||
* (angle at this loops face corner).
|
||||
*
|
||||
* \return angle in radians
|
||||
*/
|
||||
float BM_loop_calc_face_angle(const BMLoop *l) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* \brief BM_loop_calc_face_normal
|
||||
*
|
||||
* Calculate the normal at this loop corner or fall back to the face normal on straight lines.
|
||||
*
|
||||
* \param l: The loop to calculate the normal at
|
||||
* \param r_normal: Resulting normal
|
||||
* \return The length of the cross product (double the area).
|
||||
*/
|
||||
float BM_loop_calc_face_normal(const BMLoop *l, float r_normal[3]) ATTR_NONNULL();
|
||||
/**
|
||||
* #BM_loop_calc_face_normal_safe_ex with predefined sane epsilon.
|
||||
*
|
||||
* Since this doesn't scale based on triangle size, fixed value works well.
|
||||
*/
|
||||
float BM_loop_calc_face_normal_safe(const BMLoop *l, float r_normal[3]) ATTR_NONNULL();
|
||||
/**
|
||||
* \brief BM_loop_calc_face_normal
|
||||
*
|
||||
* Calculate the normal at this loop corner or fall back to the face normal on straight lines.
|
||||
*
|
||||
* \param l: The loop to calculate the normal at.
|
||||
* \param epsilon_sq: Value to avoid numeric errors (1e-5f works well).
|
||||
* \param r_normal: Resulting normal.
|
||||
*/
|
||||
float BM_loop_calc_face_normal_safe_ex(const BMLoop *l, float epsilon_sq, float r_normal[3])
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* A version of BM_loop_calc_face_normal_safe_ex which takes vertex coordinates.
|
||||
*/
|
||||
float BM_loop_calc_face_normal_safe_vcos_ex(const BMLoop *l,
|
||||
const float normal_fallback[3],
|
||||
float const (*vertexCos)[3],
|
||||
float epsilon_sq,
|
||||
float r_normal[3]) ATTR_NONNULL();
|
||||
float BM_loop_calc_face_normal_safe_vcos(const BMLoop *l,
|
||||
const float normal_fallback[3],
|
||||
float const (*vertexCos)[3],
|
||||
float r_normal[3]) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* \brief BM_loop_calc_face_direction
|
||||
*
|
||||
* Calculate the direction a loop is pointing.
|
||||
*
|
||||
* \param l: The loop to calculate the direction at
|
||||
* \param r_dir: Resulting direction
|
||||
*/
|
||||
void BM_loop_calc_face_direction(const BMLoop *l, float r_dir[3]);
|
||||
/**
|
||||
* \brief BM_loop_calc_face_tangent
|
||||
*
|
||||
* Calculate the tangent at this loop corner or fall back to the face normal on straight lines.
|
||||
* This vector always points inward into the face.
|
||||
*
|
||||
* \param l: The loop to calculate the tangent at
|
||||
* \param r_tangent: Resulting tangent
|
||||
*/
|
||||
void BM_loop_calc_face_tangent(const BMLoop *l, float r_tangent[3]);
|
||||
|
||||
/**
|
||||
* \brief BMESH EDGE/FACE ANGLE
|
||||
*
|
||||
* Calculates the angle between two faces.
|
||||
* Assumes the face normals are correct.
|
||||
*
|
||||
* \return angle in radians
|
||||
*/
|
||||
float BM_edge_calc_face_angle_ex(const BMEdge *e, float fallback) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
float BM_edge_calc_face_angle(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* \brief BMESH EDGE/FACE ANGLE
|
||||
*
|
||||
* Calculates the angle between two faces.
|
||||
* Assumes the face normals are correct.
|
||||
*
|
||||
* \return angle in radians
|
||||
*/
|
||||
float BM_edge_calc_face_angle_signed_ex(const BMEdge *e, float fallback) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* \brief BMESH EDGE/FACE ANGLE
|
||||
*
|
||||
* Calculates the angle between two faces in world space.
|
||||
* Assumes the face normals are correct.
|
||||
*
|
||||
* \return angle in radians
|
||||
*/
|
||||
float BM_edge_calc_face_angle_with_imat3_ex(const BMEdge *e,
|
||||
const float imat3[3][3],
|
||||
float fallback) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
float BM_edge_calc_face_angle_with_imat3(const BMEdge *e,
|
||||
const float imat3[3][3]) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
float BM_edge_calc_face_angle_signed(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* \brief BMESH EDGE/FACE TANGENT
|
||||
*
|
||||
* Calculate the tangent at this loop corner or fallback to the face normal on straight lines.
|
||||
* This vector always points inward into the face.
|
||||
*
|
||||
* \brief BM_edge_calc_face_tangent
|
||||
* \param e:
|
||||
* \param e_loop: The loop to calculate the tangent at,
|
||||
* used to get the face and winding direction.
|
||||
* \param r_tangent: The loop corner tangent to set
|
||||
*/
|
||||
void BM_edge_calc_face_tangent(const BMEdge *e, const BMLoop *e_loop, float r_tangent[3])
|
||||
ATTR_NONNULL();
|
||||
float BM_vert_calc_edge_angle(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* \brief BMESH VERT/EDGE ANGLE
|
||||
*
|
||||
* Calculates the angle a verts 2 edges.
|
||||
*
|
||||
* \returns the angle in radians
|
||||
*/
|
||||
float BM_vert_calc_edge_angle_ex(const BMVert *v, float fallback) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* \note this isn't optimal to run on an array of verts,
|
||||
* see 'solidify_add_thickness' for a function which runs on an array.
|
||||
*/
|
||||
float BM_vert_calc_shell_factor(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/* alternate version of #BM_vert_calc_shell_factor which only
|
||||
* uses 'hflag' faces, but falls back to all if none found. */
|
||||
float BM_vert_calc_shell_factor_ex(const BMVert *v,
|
||||
const float no[3],
|
||||
char hflag) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* \note quite an obscure function.
|
||||
* used in bmesh operators that have a relative scale options,
|
||||
*/
|
||||
float BM_vert_calc_median_tagged_edge_length(const BMVert *v) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Returns the loop of the shortest edge in f.
|
||||
*/
|
||||
BMLoop *BM_face_find_shortest_loop(BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Returns the loop of the longest edge in f.
|
||||
*/
|
||||
BMLoop *BM_face_find_longest_loop(BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
BMEdge *BM_edge_exists(BMVert *v_a, BMVert *v_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Returns an edge sharing the same vertices as this one.
|
||||
* This isn't an invalid state but tools should clean up these cases before
|
||||
* returning the mesh to the user.
|
||||
*/
|
||||
BMEdge *BM_edge_find_double(BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Given a set of vertices (varr), find out if
|
||||
* there is a face with exactly those vertices
|
||||
* (and only those vertices).
|
||||
*
|
||||
* \note there used to be a BM_face_exists_overlap function that checks for partial overlap.
|
||||
*/
|
||||
BMFace *BM_face_exists(BMVert *const *varr, int len) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Check if a face exists, using a subset of an existing face's loops.
|
||||
* Edges defined by loops from `l_a` to `l_b` (inclusive) are used to check if they make a face,
|
||||
* with an implied edge between `l_a->v` & `l_b->v` which must exist for there to be a face.
|
||||
*
|
||||
* \param l_a, l_b: First and last loop of the subset.
|
||||
* \param f_len: Number of loops (vertices) between `l_a` & `l_b` (inclusive).
|
||||
* \return The matching face if it exists, otherwise null.
|
||||
*/
|
||||
BMFace *BM_face_exists_subset_from_face(BMLoop *l_a,
|
||||
BMLoop *l_b,
|
||||
int f_len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Check if the face has an exact duplicate (both winding directions).
|
||||
*/
|
||||
BMFace *BM_face_find_double(BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Given a set of vertices and edges (\a varr, \a earr), find out if
|
||||
* all those vertices are filled in by existing faces that _only_ use those vertices.
|
||||
*
|
||||
* This is for use in cases where creating a face is possible but would result in
|
||||
* many overlapping faces.
|
||||
*
|
||||
* An example of how this is used: when 2 triangles are selected that share an edge,
|
||||
* pressing F-key would make a new overlapping quad (without a check like this)
|
||||
*
|
||||
* \a earr and \a varr can be in any order, however they _must_ form a closed loop.
|
||||
*/
|
||||
bool BM_face_exists_multi(BMVert **varr, BMEdge **earr, int len) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* Same as #BM_face_exists_multi but builds the vert array from edges.
|
||||
*/
|
||||
bool BM_face_exists_multi_edge(BMEdge **earr, int len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Given a set of vertices (varr), find out if
|
||||
* all those vertices overlap an existing face.
|
||||
*
|
||||
* \note The face may contain other verts \b not in \a varr.
|
||||
*
|
||||
* \note Its possible there are more than one overlapping faces,
|
||||
* in this case the first one found will be returned.
|
||||
*
|
||||
* \param varr: Array of unordered verts.
|
||||
* \param len: \a varr array length.
|
||||
* \return The face or NULL.
|
||||
*/
|
||||
BMFace *BM_face_exists_overlap(BMVert **varr, int len) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Given a set of vertices (varr), find out if
|
||||
* there is a face that uses vertices only from this list
|
||||
* (that the face is a subset or made from the vertices given).
|
||||
*
|
||||
* \param varr: Array of unordered verts.
|
||||
* \param len: varr array length.
|
||||
*/
|
||||
bool BM_face_exists_overlap_subset(BMVert **varr, int len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Returns the number of faces that are adjacent to both f1 and f2,
|
||||
* \note Could be sped up a bit by not using iterators and by tagging
|
||||
* faces on either side, then count the tags rather then searching.
|
||||
*/
|
||||
int BM_face_share_face_count(BMFace *f_a, BMFace *f_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Counts the number of edges two faces share (if any)
|
||||
*/
|
||||
int BM_face_share_edge_count(BMFace *f_a, BMFace *f_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Counts the number of verts two faces share (if any).
|
||||
*/
|
||||
int BM_face_share_vert_count(BMFace *f_a, BMFace *f_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* same as #BM_face_share_face_count but returns a bool
|
||||
*/
|
||||
bool BM_face_share_face_check(BMFace *f_a, BMFace *f_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Returns true if the faces share an edge
|
||||
*/
|
||||
bool BM_face_share_edge_check(BMFace *f_a, BMFace *f_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Returns true if the faces share a vert.
|
||||
*/
|
||||
bool BM_face_share_vert_check(BMFace *f_a, BMFace *f_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Returns true when 2 loops share an edge (are adjacent in the face-fan)
|
||||
*/
|
||||
bool BM_loop_share_edge_check(BMLoop *l_a, BMLoop *l_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Test if e1 shares any faces with e2
|
||||
*/
|
||||
bool BM_edge_share_face_check(BMEdge *e1, BMEdge *e2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Test if e1 shares any quad faces with e2
|
||||
*/
|
||||
bool BM_edge_share_quad_check(BMEdge *e1, BMEdge *e2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Tests to see if e1 shares a vertex with e2
|
||||
*/
|
||||
bool BM_edge_share_vert_check(BMEdge *e1, BMEdge *e2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Return the shared vertex between the two edges or NULL
|
||||
*/
|
||||
BMVert *BM_edge_share_vert(BMEdge *e1, BMEdge *e2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* \brief Return the Loop Shared by Edge and Vert
|
||||
*
|
||||
* Finds the loop used which uses \a v in face loop \a l
|
||||
*
|
||||
* \note this function takes a loop rather than an edge
|
||||
* so we can select the face that the loop should be from.
|
||||
*/
|
||||
BMLoop *BM_edge_vert_share_loop(BMLoop *l, BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* \brief Return the Loop Shared by Face and Vertex
|
||||
*
|
||||
* Finds the loop used which uses \a v in face loop \a l
|
||||
*
|
||||
* \note currently this just uses simple loop in future may be sped up
|
||||
* using radial vars
|
||||
*/
|
||||
BMLoop *BM_face_vert_share_loop(BMFace *f, BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* \brief Return the Loop Shared by Face and Edge
|
||||
*
|
||||
* Finds the loop used which uses \a e in face loop \a l
|
||||
*
|
||||
* \note currently this just uses simple loop in future may be sped up
|
||||
* using radial vars
|
||||
*/
|
||||
BMLoop *BM_face_edge_share_loop(BMFace *f, BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
void BM_edge_ordered_verts(const BMEdge *edge, BMVert **r_v1, BMVert **r_v2) ATTR_NONNULL();
|
||||
/**
|
||||
* Returns the verts of an edge as used in a face
|
||||
* if used in a face at all, otherwise just assign as used in the edge.
|
||||
*
|
||||
* Useful to get a deterministic winding order when calling
|
||||
* BM_face_create_ngon() on an arbitrary array of verts,
|
||||
* though be sure to pick an edge which has a face.
|
||||
*
|
||||
* \note This is in fact quite a simple check,
|
||||
* mainly include this function so the intent is more obvious.
|
||||
* We know these 2 verts will _always_ make up the loops edge
|
||||
*/
|
||||
void BM_edge_ordered_verts_ex(const BMEdge *edge,
|
||||
BMVert **r_v1,
|
||||
BMVert **r_v2,
|
||||
const BMLoop *edge_loop) ATTR_NONNULL();
|
||||
|
||||
bool BM_vert_is_all_edge_flag_test(const BMVert *v,
|
||||
char hflag,
|
||||
bool respect_hide) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
bool BM_vert_is_all_face_flag_test(const BMVert *v,
|
||||
char hflag,
|
||||
bool respect_hide) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
bool BM_edge_is_all_face_flag_test(const BMEdge *e,
|
||||
char hflag,
|
||||
bool respect_hide) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/* convenience functions for checking flags */
|
||||
bool BM_edge_is_any_vert_flag_test(const BMEdge *e, char hflag) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
bool BM_edge_is_any_face_flag_test(const BMEdge *e, char hflag) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
bool BM_face_is_any_vert_flag_test(const BMFace *f, char hflag) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
bool BM_face_is_any_edge_flag_test(const BMFace *f, char hflag) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
|
||||
bool BM_edge_is_any_face_len_test(const BMEdge *e, int len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Use within asserts to check normals are valid.
|
||||
*/
|
||||
bool BM_face_is_normal_valid(const BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
double BM_mesh_calc_volume(BMesh *bm, bool is_signed) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Calculate isolated groups of faces with optional filtering.
|
||||
*
|
||||
* \param bm: the BMesh.
|
||||
* \param r_groups_array: Array of integers to fill in, length of `bm->totface`
|
||||
* (or when hflag_test is set, the number of flagged faces).
|
||||
* \param r_group_index: index, length pairs into \a r_groups_array, size of return value
|
||||
* int pairs: (array_start, array_length).
|
||||
* \param filter_fn: Filter the edge-loops or vert-loops we step over (depends on \a htype_step).
|
||||
* \param user_data: Optional user data for \a filter_fn, can be NULL.
|
||||
* \param hflag_test: Optional flag to test faces,
|
||||
* use to exclude faces from the calculation, 0 for all faces.
|
||||
* \param htype_step: BM_VERT to walk over face-verts, BM_EDGE to walk over faces edges
|
||||
* (having both set is supported too).
|
||||
* \return The number of groups found.
|
||||
*/
|
||||
int BM_mesh_calc_face_groups(BMesh *bm,
|
||||
int *r_groups_array,
|
||||
int (**r_group_index)[2],
|
||||
BMLoopFilterFunc filter_fn,
|
||||
BMLoopPairFilterFunc filter_pair_fn,
|
||||
void *user_data,
|
||||
char hflag_test,
|
||||
char htype_step) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2, 3);
|
||||
/**
|
||||
* Calculate isolated groups of edges with optional filtering.
|
||||
*
|
||||
* \param bm: the BMesh.
|
||||
* \param r_groups_array: Array of ints to fill in, length of `bm->totedge`
|
||||
* (or when hflag_test is set, the number of flagged edges).
|
||||
* \param r_group_index: index, length pairs into \a r_groups_array, size of return value
|
||||
* int pairs: (array_start, array_length).
|
||||
* \param filter_fn: Filter the edges or verts we step over (depends on \a htype_step)
|
||||
* as to which types we deal with.
|
||||
* \param user_data: Optional user data for \a filter_fn, can be NULL.
|
||||
* \param hflag_test: Optional flag to test edges,
|
||||
* use to exclude edges from the calculation, 0 for all edges.
|
||||
* \return The number of groups found.
|
||||
*
|
||||
* \note Unlike #BM_mesh_calc_face_groups there is no 'htype_step' argument,
|
||||
* since we always walk over verts.
|
||||
*/
|
||||
int BM_mesh_calc_edge_groups(BMesh *bm,
|
||||
int *r_groups_array,
|
||||
int (**r_group_index)[2],
|
||||
BMVertFilterFunc filter_fn,
|
||||
void *user_data,
|
||||
char hflag_test) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2, 3);
|
||||
|
||||
/**
|
||||
* This is an alternative to #BM_mesh_calc_edge_groups.
|
||||
*
|
||||
* While we could call this, then create vertex & face arrays,
|
||||
* it requires looping over geometry connectivity twice,
|
||||
* this slows down edit-mesh separate by loose parts, see: #70864.
|
||||
*/
|
||||
int BM_mesh_calc_edge_groups_as_arrays(BMesh *bm,
|
||||
BMVert **verts,
|
||||
BMEdge **edges,
|
||||
BMFace **faces,
|
||||
int (**r_groups)[3]) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL(1, 2, 3, 4, 5);
|
||||
|
||||
/* Not really any good place to put this. */
|
||||
float bmesh_subd_falloff_calc(int falloff, float val) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
} // namespace blender
|
||||
|
||||
#include "bmesh_query_inline.hh" /* IWYU pragma: export */
|
||||
151
blender-5.2.0/source/blender/bmesh/intern/bmesh_query_inline.hh
Normal file
151
blender-5.2.0/source/blender/bmesh/intern/bmesh_query_inline.hh
Normal file
@@ -0,0 +1,151 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_compiler_compat.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Returns whether or not a given vertex is
|
||||
* is part of a given edge.
|
||||
*/
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE
|
||||
bool BM_vert_in_edge(const BMEdge *e, const BMVert *v)
|
||||
{
|
||||
return (ELEM(v, e->v1, e->v2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not a given edge is part of a given loop.
|
||||
*/
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2) BLI_INLINE
|
||||
bool BM_edge_in_loop(const BMEdge *e, const BMLoop *l)
|
||||
{
|
||||
return (l->e == e || l->prev->e == e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not two vertices are in
|
||||
* a given edge
|
||||
*/
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2, 3) BLI_INLINE
|
||||
bool BM_verts_in_edge(const BMVert *v1, const BMVert *v2, const BMEdge *e)
|
||||
{
|
||||
return ((e->v1 == v1 && e->v2 == v2) || (e->v1 == v2 && e->v2 == v1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a edge and one of its vertices, returns
|
||||
* the other vertex.
|
||||
*/
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2) BLI_INLINE BMVert *BM_edge_other_vert(BMEdge *e,
|
||||
const BMVert *v)
|
||||
{
|
||||
if (e->v1 == v) {
|
||||
return e->v2;
|
||||
}
|
||||
if (e->v2 == v) {
|
||||
return e->v1;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether or not the edge is part of a wire.
|
||||
* (ie: has no faces attached to it)
|
||||
*/
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE bool BM_edge_is_wire(const BMEdge *e)
|
||||
{
|
||||
return (e->l == nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether or not this edge is manifold.
|
||||
* A manifold edge has exactly 2 faces attached to it.
|
||||
*/
|
||||
|
||||
#if 1 /* fast path for checking manifold */
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE bool BM_edge_is_manifold(const BMEdge *e)
|
||||
{
|
||||
const BMLoop *l = e->l;
|
||||
return (l && (l->radial_next != l) && /* not 0 or 1 face users */
|
||||
(l->radial_next->radial_next == l)); /* 2 face users */
|
||||
}
|
||||
#else
|
||||
BLI_INLINE int BM_edge_is_manifold(BMEdge *e)
|
||||
{
|
||||
return (BM_edge_face_count(e) == 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Tests that the edge is manifold and
|
||||
* that both its faces point the same way.
|
||||
*/
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE bool BM_edge_is_contiguous(const BMEdge *e)
|
||||
{
|
||||
const BMLoop *l = e->l;
|
||||
const BMLoop *l_other;
|
||||
return (l && ((l_other = l->radial_next) != l) && /* not 0 or 1 face users */
|
||||
(l_other->radial_next == l) && /* 2 face users */
|
||||
(l_other->v != l->v));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether or not an edge is on the boundary
|
||||
* of a shell (has one face associated with it)
|
||||
*/
|
||||
|
||||
#if 1 /* fast path for checking boundary */
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE bool BM_edge_is_boundary(const BMEdge *e)
|
||||
{
|
||||
const BMLoop *l = e->l;
|
||||
return (l && (l->radial_next == l));
|
||||
}
|
||||
#else
|
||||
BLI_INLINE int BM_edge_is_boundary(BMEdge *e)
|
||||
{
|
||||
return (BM_edge_face_count(e) == 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Tests whether one loop is next to another within the same face.
|
||||
*/
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2) BLI_INLINE
|
||||
bool BM_loop_is_adjacent(const BMLoop *l_a, const BMLoop *l_b)
|
||||
{
|
||||
BLI_assert(l_a->f == l_b->f);
|
||||
BLI_assert(l_a != l_b);
|
||||
return (ELEM(l_b, l_a->next, l_a->prev));
|
||||
}
|
||||
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE bool BM_loop_is_manifold(const BMLoop *l)
|
||||
{
|
||||
return ((l != l->radial_next) && (l == l->radial_next->radial_next));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we have a single wire edge user.
|
||||
*/
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE bool BM_vert_is_wire_endpoint(const BMVert *v)
|
||||
{
|
||||
const BMEdge *e = v->e;
|
||||
if (e && e->l == nullptr) {
|
||||
return (BM_DISK_EDGE_NEXT(e, v) == e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
210
blender-5.2.0/source/blender/bmesh/intern/bmesh_query_uv.cc
Normal file
210
blender-5.2.0/source/blender/bmesh/intern/bmesh_query_uv.cc
Normal file
@@ -0,0 +1,210 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_math_geom.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "BKE_attribute.h"
|
||||
#include "BKE_customdata.hh"
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
BMUVOffsets BM_uv_map_offsets_from_layer(const BMesh *bm, const int layer)
|
||||
{
|
||||
using namespace bke;
|
||||
const int layer_index = CustomData_get_layer_index_n(&bm->ldata, CD_PROP_FLOAT2, layer);
|
||||
if (layer_index == -1) {
|
||||
return BMUVOFFSETS_NONE;
|
||||
}
|
||||
|
||||
const StringRef name = bm->ldata.layers[layer_index].name;
|
||||
char buffer[MAX_CUSTOMDATA_LAYER_NAME];
|
||||
|
||||
BMUVOffsets offsets;
|
||||
offsets.uv = bm->ldata.layers[layer_index].offset;
|
||||
offsets.pin = CustomData_get_offset_named(
|
||||
&bm->ldata, CD_PROP_BOOL, BKE_uv_map_pin_name_get(name, buffer));
|
||||
|
||||
return offsets;
|
||||
}
|
||||
|
||||
BMUVOffsets BM_uv_map_offsets_get(const BMesh *bm)
|
||||
{
|
||||
const int layer = CustomData_get_active_layer(&bm->ldata, CD_PROP_FLOAT2);
|
||||
if (layer == -1) {
|
||||
return BMUVOFFSETS_NONE;
|
||||
}
|
||||
return BM_uv_map_offsets_from_layer(bm, layer);
|
||||
}
|
||||
|
||||
static void uv_aspect(const BMLoop *l,
|
||||
const float aspect[2],
|
||||
const int cd_loop_uv_offset,
|
||||
float r_uv[2])
|
||||
{
|
||||
const float *uv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
|
||||
r_uv[0] = uv[0] * aspect[0];
|
||||
r_uv[1] = uv[1] * aspect[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Typically we avoid hiding arguments,
|
||||
* make this an exception since it reads poorly with so many repeated arguments.
|
||||
*/
|
||||
#define UV_ASPECT(l, r_uv) uv_aspect(l, aspect, cd_loop_uv_offset, r_uv)
|
||||
|
||||
void BM_face_uv_calc_center_median_weighted(const BMFace *f,
|
||||
const float aspect[2],
|
||||
const int cd_loop_uv_offset,
|
||||
float r_cent[2])
|
||||
{
|
||||
const BMLoop *l_iter;
|
||||
const BMLoop *l_first;
|
||||
float totw = 0.0f;
|
||||
float w_prev;
|
||||
|
||||
zero_v2(r_cent);
|
||||
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
|
||||
float uv_prev[2], uv_curr[2];
|
||||
UV_ASPECT(l_iter->prev, uv_prev);
|
||||
UV_ASPECT(l_iter, uv_curr);
|
||||
w_prev = len_v2v2(uv_prev, uv_curr);
|
||||
do {
|
||||
float uv_next[2];
|
||||
UV_ASPECT(l_iter->next, uv_next);
|
||||
const float w_curr = len_v2v2(uv_curr, uv_next);
|
||||
const float w = (w_curr + w_prev);
|
||||
madd_v2_v2fl(r_cent, uv_curr, w);
|
||||
totw += w;
|
||||
w_prev = w_curr;
|
||||
copy_v2_v2(uv_curr, uv_next);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
if (totw != 0.0f) {
|
||||
mul_v2_fl(r_cent, 1.0f / totw);
|
||||
}
|
||||
/* Reverse aspect. */
|
||||
r_cent[0] /= aspect[0];
|
||||
r_cent[1] /= aspect[1];
|
||||
}
|
||||
|
||||
#undef UV_ASPECT
|
||||
|
||||
void BM_face_uv_calc_center_median(const BMFace *f, const int cd_loop_uv_offset, float r_cent[2])
|
||||
{
|
||||
const BMLoop *l_iter;
|
||||
const BMLoop *l_first;
|
||||
zero_v2(r_cent);
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
const float *luv = BM_ELEM_CD_GET_FLOAT_P(l_iter, cd_loop_uv_offset);
|
||||
add_v2_v2(r_cent, luv);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
mul_v2_fl(r_cent, 1.0f / float(f->len));
|
||||
}
|
||||
|
||||
float BM_face_uv_calc_cross(const BMFace *f, const int cd_loop_uv_offset)
|
||||
{
|
||||
Array<float2, BM_DEFAULT_NGON_STACK_SIZE> uvs(f->len);
|
||||
const BMLoop *l_iter;
|
||||
const BMLoop *l_first;
|
||||
int i = 0;
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
uvs[i++] = BM_ELEM_CD_GET_FLOAT2_P(l_iter, cd_loop_uv_offset);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
return cross_poly_v2(reinterpret_cast<const float (*)[2]>(uvs.data()), f->len);
|
||||
}
|
||||
|
||||
void BM_face_uv_minmax(const BMFace *f, float min[2], float max[2], const int cd_loop_uv_offset)
|
||||
{
|
||||
const BMLoop *l_iter;
|
||||
const BMLoop *l_first;
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
const float *luv = BM_ELEM_CD_GET_FLOAT_P(l_iter, cd_loop_uv_offset);
|
||||
minmax_v2v2_v2(min, max, luv);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
|
||||
bool BM_loop_uv_share_edge_check(const BMLoop *l_a, const BMLoop *l_b, const int cd_loop_uv_offset)
|
||||
{
|
||||
BLI_assert(l_a->e == l_b->e);
|
||||
const float *luv_a_curr = BM_ELEM_CD_GET_FLOAT_P(l_a, cd_loop_uv_offset);
|
||||
const float *luv_a_next = BM_ELEM_CD_GET_FLOAT_P(l_a->next, cd_loop_uv_offset);
|
||||
const float *luv_b_curr = BM_ELEM_CD_GET_FLOAT_P(l_b, cd_loop_uv_offset);
|
||||
const float *luv_b_next = BM_ELEM_CD_GET_FLOAT_P(l_b->next, cd_loop_uv_offset);
|
||||
if (l_a->v != l_b->v) {
|
||||
std::swap(luv_b_curr, luv_b_next);
|
||||
}
|
||||
return (equals_v2v2(luv_a_curr, luv_b_curr) && equals_v2v2(luv_a_next, luv_b_next));
|
||||
}
|
||||
|
||||
bool BM_loop_uv_share_vert_check(const BMLoop *l_a, const BMLoop *l_b, const int cd_loop_uv_offset)
|
||||
{
|
||||
BLI_assert(l_a->v == l_b->v);
|
||||
const float *luv_a = BM_ELEM_CD_GET_FLOAT_P(l_a, cd_loop_uv_offset);
|
||||
const float *luv_b = BM_ELEM_CD_GET_FLOAT_P(l_b, cd_loop_uv_offset);
|
||||
if (!equals_v2v2(luv_a, luv_b)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BM_edge_uv_share_vert_check(const BMEdge *e,
|
||||
const BMLoop *l_a,
|
||||
const BMLoop *l_b,
|
||||
const int cd_loop_uv_offset)
|
||||
{
|
||||
BLI_assert(l_a->v == l_b->v);
|
||||
if (!BM_loop_uv_share_vert_check(l_a, l_b, cd_loop_uv_offset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* No need for null checks, these will always succeed. */
|
||||
const BMLoop *l_other_a = BM_loop_other_vert_loop_by_edge(const_cast<BMLoop *>(l_a),
|
||||
const_cast<BMEdge *>(e));
|
||||
const BMLoop *l_other_b = BM_loop_other_vert_loop_by_edge(const_cast<BMLoop *>(l_b),
|
||||
const_cast<BMEdge *>(e));
|
||||
|
||||
{
|
||||
const float *luv_other_a = BM_ELEM_CD_GET_FLOAT_P(l_other_a, cd_loop_uv_offset);
|
||||
const float *luv_other_b = BM_ELEM_CD_GET_FLOAT_P(l_other_b, cd_loop_uv_offset);
|
||||
if (!equals_v2v2(luv_other_a, luv_other_b)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BM_face_uv_point_inside_test(const BMFace *f, const float co[2], const int cd_loop_uv_offset)
|
||||
{
|
||||
Array<float2, BM_DEFAULT_NGON_STACK_SIZE> projverts(f->len);
|
||||
|
||||
BMLoop *l_iter;
|
||||
int i;
|
||||
|
||||
BLI_assert(BM_face_is_normal_valid(f));
|
||||
|
||||
for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f); i < f->len; i++, l_iter = l_iter->next) {
|
||||
projverts[i] = BM_ELEM_CD_GET_FLOAT2_P(l_iter, cd_loop_uv_offset);
|
||||
}
|
||||
|
||||
return isect_point_poly_v2(co, reinterpret_cast<const float (*)[2]>(projverts.data()), f->len);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
94
blender-5.2.0/source/blender/bmesh/intern/bmesh_query_uv.hh
Normal file
94
blender-5.2.0/source/blender/bmesh/intern/bmesh_query_uv.hh
Normal file
@@ -0,0 +1,94 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "BKE_customdata.hh"
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Retrieve the custom data offsets for the UV map.
|
||||
* \param layer: The layer index (where 0 is the first UV map).
|
||||
* \return The layer offsets or -1 when not found.
|
||||
*/
|
||||
BMUVOffsets BM_uv_map_offsets_from_layer(const BMesh *bm, int layer);
|
||||
|
||||
/**
|
||||
* Retrieve the custom data offsets for layers used for user interaction with the active UV map.
|
||||
* \return The layer offsets or -1 when not found.
|
||||
*/
|
||||
BMUVOffsets BM_uv_map_offsets_get(const BMesh *bm);
|
||||
|
||||
float BM_loop_uv_calc_edge_length_squared(const BMLoop *l,
|
||||
int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
float BM_loop_uv_calc_edge_length(const BMLoop *l, int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Computes the UV center of a face, using the mean average weighted by edge length.
|
||||
*
|
||||
* See #BM_face_calc_center_median_weighted for matching spatial functionality.
|
||||
*
|
||||
* \param aspect: Calculate the center scaling by these values, and finally dividing.
|
||||
* Since correct weighting depends on having the correct aspect.
|
||||
*/
|
||||
void BM_face_uv_calc_center_median_weighted(const BMFace *f,
|
||||
const float aspect[2],
|
||||
int cd_loop_uv_offset,
|
||||
float r_cent[2]) ATTR_NONNULL();
|
||||
void BM_face_uv_calc_center_median(const BMFace *f, int cd_loop_uv_offset, float r_cent[2])
|
||||
ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Calculate the UV cross product (use the sign to check the winding).
|
||||
*/
|
||||
float BM_face_uv_calc_cross(const BMFace *f, int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
|
||||
void BM_face_uv_minmax(const BMFace *f, float min[2], float max[2], int cd_loop_uv_offset);
|
||||
|
||||
bool BM_loop_uv_share_edge_check_with_limit(const BMLoop *l_a,
|
||||
const BMLoop *l_b,
|
||||
const float limit[2],
|
||||
int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Check if two loops that share an edge also have the same UV coordinates.
|
||||
*/
|
||||
bool BM_loop_uv_share_edge_check(const BMLoop *l_a,
|
||||
const BMLoop *l_b,
|
||||
int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Check if two loops that share a vertex also have the same UV coordinates.
|
||||
*/
|
||||
bool BM_edge_uv_share_vert_check(const BMEdge *e,
|
||||
const BMLoop *l_a,
|
||||
const BMLoop *l_b,
|
||||
int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Check if two loops that share a vertex also have the same UV coordinates.
|
||||
*/
|
||||
bool BM_loop_uv_share_vert_check(const BMLoop *l_a,
|
||||
const BMLoop *l_b,
|
||||
int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Check if the point is inside the UV face.
|
||||
*/
|
||||
bool BM_face_uv_point_inside_test(const BMFace *f,
|
||||
const float co[2],
|
||||
int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
} // namespace blender
|
||||
573
blender-5.2.0/source/blender/bmesh/intern/bmesh_structure.cc
Normal file
573
blender-5.2.0/source/blender/bmesh/intern/bmesh_structure.cc
Normal file
@@ -0,0 +1,573 @@
|
||||
/* SPDX-FileCopyrightText: 2007 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* Low level routines for manipulating the BM structure.
|
||||
*/
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "bmesh.hh"
|
||||
#include "intern/bmesh_private.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* MISC utility functions.
|
||||
*/
|
||||
|
||||
void bmesh_disk_vert_swap(BMEdge *e, BMVert *v_dst, BMVert *v_src)
|
||||
{
|
||||
if (e->v1 == v_src) {
|
||||
e->v1 = v_dst;
|
||||
e->v1_disk_link.next = e->v1_disk_link.prev = nullptr;
|
||||
}
|
||||
else if (e->v2 == v_src) {
|
||||
e->v2 = v_dst;
|
||||
e->v2_disk_link.next = e->v2_disk_link.prev = nullptr;
|
||||
}
|
||||
else {
|
||||
BLI_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void bmesh_edge_vert_swap(BMEdge *e, BMVert *v_dst, BMVert *v_src)
|
||||
{
|
||||
/* swap out loops */
|
||||
if (e->l) {
|
||||
BMLoop *l_iter, *l_first;
|
||||
l_iter = l_first = e->l;
|
||||
do {
|
||||
if (l_iter->v == v_src) {
|
||||
l_iter->v = v_dst;
|
||||
}
|
||||
else if (l_iter->next->v == v_src) {
|
||||
l_iter->next->v = v_dst;
|
||||
}
|
||||
else {
|
||||
BLI_assert(l_iter->prev->v != v_src);
|
||||
}
|
||||
} while ((l_iter = l_iter->radial_next) != l_first);
|
||||
}
|
||||
|
||||
/* swap out edges */
|
||||
bmesh_disk_vert_replace(e, v_dst, v_src);
|
||||
}
|
||||
|
||||
void bmesh_disk_vert_replace(BMEdge *e, BMVert *v_dst, BMVert *v_src)
|
||||
{
|
||||
BLI_assert(e->v1 == v_src || e->v2 == v_src);
|
||||
bmesh_disk_edge_remove(e, v_src); /* Remove `e` from `v_src` disk cycle. */
|
||||
bmesh_disk_vert_swap(e, v_dst, v_src); /* Swap out `v_src` for `v_dst` in `e`. */
|
||||
bmesh_disk_edge_append(e, v_dst); /* Add `e` to `v_dst` disk cycle. */
|
||||
BLI_assert(e->v1 != e->v2);
|
||||
}
|
||||
|
||||
/**
|
||||
* \section bm_cycles BMesh Cycles
|
||||
*
|
||||
* NOTE(@joeedh): this is somewhat outdated, though bits of its API are still used.
|
||||
*
|
||||
* Cycles are circular doubly linked lists that form the basis of adjacency
|
||||
* information in the BME modeler. Full adjacency relations can be derived
|
||||
* from examining these cycles very quickly. Although each cycle is a double
|
||||
* circular linked list, each one is considered to have a 'base' or 'head',
|
||||
* and care must be taken by Euler code when modifying the contents of a cycle.
|
||||
*
|
||||
* The contents of this file are split into two parts. First there are the
|
||||
* bmesh_cycle family of functions which are generic circular double linked list
|
||||
* procedures. The second part contains higher level procedures for supporting
|
||||
* modification of specific cycle types.
|
||||
*
|
||||
* The three cycles explicitly stored in the BM data structure are as follows:
|
||||
* 1: The Disk Cycle - A circle of edges around a vertex
|
||||
* Base: vertex->edge pointer.
|
||||
*
|
||||
* This cycle is the most complicated in terms of its structure. Each bmesh_Edge contains
|
||||
* two bmesh_CycleNode structures to keep track of that edges membership in the disk cycle
|
||||
* of each of its vertices. However for any given vertex it may be the first in some edges
|
||||
* in its disk cycle and the second for others. The bmesh_disk_XXX family of functions contain
|
||||
* some nice utilities for navigating disk cycles in a way that hides this detail from the
|
||||
* tool writer.
|
||||
*
|
||||
* Note that the disk cycle is completely independent from face data. One advantage of this
|
||||
* is that wire edges are fully integrated into the topology database. Another is that the
|
||||
* the disk cycle has no problems dealing with non-manifold conditions involving faces.
|
||||
*
|
||||
* Functions relating to this cycle:
|
||||
* - #bmesh_disk_vert_replace
|
||||
* - #bmesh_disk_edge_append
|
||||
* - #bmesh_disk_edge_remove
|
||||
* - #bmesh_disk_edge_next
|
||||
* - #bmesh_disk_edge_prev
|
||||
* - #bmesh_disk_facevert_count
|
||||
* - #bmesh_disk_faceedge_find_first
|
||||
* - #bmesh_disk_faceedge_find_next
|
||||
* 2: The Radial Cycle - A circle of face edges (bmesh_Loop) around an edge
|
||||
* Base: edge->l->radial structure.
|
||||
*
|
||||
* The radial cycle is similar to the radial cycle in the radial edge data structure.*
|
||||
* Unlike the radial edge however, the radial cycle does not require a large amount of memory
|
||||
* to store non-manifold conditions since BM does not keep track of region/shell information.
|
||||
*
|
||||
* Functions relating to this cycle:
|
||||
* - #bmesh_radial_loop_append
|
||||
* - #bmesh_radial_loop_remove
|
||||
* - #bmesh_radial_facevert_count
|
||||
* - #bmesh_radial_facevert_check
|
||||
* - #bmesh_radial_faceloop_find_first
|
||||
* - #bmesh_radial_faceloop_find_next
|
||||
* - #bmesh_radial_validate
|
||||
* 3: The Loop Cycle - A circle of face edges around a polygon.
|
||||
* Base: polygon->lbase.
|
||||
*
|
||||
* The loop cycle keeps track of a faces vertices and edges. It should be noted that the
|
||||
* direction of a loop cycle is either CW or CCW depending on the face normal, and is
|
||||
* not oriented to the faces edit-edges.
|
||||
*
|
||||
* Functions relating to this cycle:
|
||||
* - bmesh_cycle_XXX family of functions.
|
||||
* \note the order of elements in all cycles except the loop cycle is undefined. This
|
||||
* leads to slightly increased seek time for deriving some adjacency relations, however the
|
||||
* advantage is that no intrinsic properties of the data structures are dependent upon the
|
||||
* cycle order and all non-manifold conditions are represented trivially.
|
||||
*/
|
||||
|
||||
void bmesh_disk_edge_append(BMEdge *e, BMVert *v)
|
||||
{
|
||||
if (!v->e) {
|
||||
BMDiskLink *dl1 = bmesh_disk_edge_link_from_vert(e, v);
|
||||
|
||||
v->e = e;
|
||||
dl1->next = dl1->prev = e;
|
||||
}
|
||||
else {
|
||||
BMDiskLink *dl1, *dl2, *dl3;
|
||||
|
||||
dl1 = bmesh_disk_edge_link_from_vert(e, v);
|
||||
dl2 = bmesh_disk_edge_link_from_vert(v->e, v);
|
||||
dl3 = dl2->prev ? bmesh_disk_edge_link_from_vert(dl2->prev, v) : nullptr;
|
||||
|
||||
dl1->next = v->e;
|
||||
dl1->prev = dl2->prev;
|
||||
|
||||
dl2->prev = e;
|
||||
if (dl3) {
|
||||
dl3->next = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bmesh_disk_edge_remove(BMEdge *e, BMVert *v)
|
||||
{
|
||||
BMDiskLink *dl1, *dl2;
|
||||
|
||||
dl1 = bmesh_disk_edge_link_from_vert(e, v);
|
||||
if (dl1->prev) {
|
||||
dl2 = bmesh_disk_edge_link_from_vert(dl1->prev, v);
|
||||
dl2->next = dl1->next;
|
||||
}
|
||||
|
||||
if (dl1->next) {
|
||||
dl2 = bmesh_disk_edge_link_from_vert(dl1->next, v);
|
||||
dl2->prev = dl1->prev;
|
||||
}
|
||||
|
||||
if (v->e == e) {
|
||||
v->e = (e != dl1->next) ? dl1->next : nullptr;
|
||||
}
|
||||
|
||||
dl1->next = dl1->prev = nullptr;
|
||||
}
|
||||
|
||||
BMEdge *bmesh_disk_edge_exists(const BMVert *v1, const BMVert *v2)
|
||||
{
|
||||
if (v1->e) {
|
||||
BMEdge *e_iter, *e_first;
|
||||
e_first = e_iter = v1->e;
|
||||
|
||||
do {
|
||||
if (BM_verts_in_edge(v1, v2, e_iter)) {
|
||||
return e_iter;
|
||||
}
|
||||
} while ((e_iter = bmesh_disk_edge_next(e_iter, v1)) != e_first);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int bmesh_disk_count(const BMVert *v)
|
||||
{
|
||||
int count = 0;
|
||||
if (v->e) {
|
||||
BMEdge *e_first, *e_iter;
|
||||
e_iter = e_first = v->e;
|
||||
do {
|
||||
count++;
|
||||
} while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int bmesh_disk_count_at_most(const BMVert *v, const int count_max)
|
||||
{
|
||||
int count = 0;
|
||||
if (v->e) {
|
||||
BMEdge *e_first, *e_iter;
|
||||
e_iter = e_first = v->e;
|
||||
do {
|
||||
count++;
|
||||
if (count == count_max) {
|
||||
break;
|
||||
}
|
||||
} while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
bool bmesh_disk_validate(int len, BMEdge *e, BMVert *v)
|
||||
{
|
||||
BMEdge *e_iter;
|
||||
|
||||
if (!BM_vert_in_edge(e, v)) {
|
||||
return false;
|
||||
}
|
||||
if (len == 0 || bmesh_disk_count_at_most(v, len + 1) != len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
e_iter = e;
|
||||
do {
|
||||
if (len != 1 && bmesh_disk_edge_prev(e_iter, v) == e_iter) {
|
||||
return false;
|
||||
}
|
||||
} while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int bmesh_disk_facevert_count(const BMVert *v)
|
||||
{
|
||||
/* is there an edge on this vert at all */
|
||||
int count = 0;
|
||||
if (v->e) {
|
||||
BMEdge *e_first, *e_iter;
|
||||
|
||||
/* first, loop around edge */
|
||||
e_first = e_iter = v->e;
|
||||
do {
|
||||
if (e_iter->l) {
|
||||
count += bmesh_radial_facevert_count(e_iter->l, v);
|
||||
}
|
||||
} while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int bmesh_disk_facevert_count_at_most(const BMVert *v, const int count_max)
|
||||
{
|
||||
/* is there an edge on this vert at all */
|
||||
int count = 0;
|
||||
if (v->e) {
|
||||
BMEdge *e_first, *e_iter;
|
||||
|
||||
/* first, loop around edge */
|
||||
e_first = e_iter = v->e;
|
||||
do {
|
||||
if (e_iter->l) {
|
||||
count += bmesh_radial_facevert_count_at_most(e_iter->l, v, count_max - count);
|
||||
if (count == count_max) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
BMEdge *bmesh_disk_faceedge_find_first(const BMEdge *e, const BMVert *v)
|
||||
{
|
||||
const BMEdge *e_iter = e;
|
||||
do {
|
||||
if (e_iter->l != nullptr) {
|
||||
return const_cast<BMEdge *>((e_iter->l->v == v) ? e_iter : e_iter->l->next->e);
|
||||
}
|
||||
} while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BMLoop *bmesh_disk_faceloop_find_first(const BMEdge *e, const BMVert *v)
|
||||
{
|
||||
const BMEdge *e_iter = e;
|
||||
do {
|
||||
if (e_iter->l != nullptr) {
|
||||
return (e_iter->l->v == v) ? e_iter->l : e_iter->l->next;
|
||||
}
|
||||
} while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BMLoop *bmesh_disk_faceloop_find_first_visible(const BMEdge *e, const BMVert *v)
|
||||
{
|
||||
const BMEdge *e_iter = e;
|
||||
do {
|
||||
if (!BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN)) {
|
||||
if (e_iter->l != nullptr) {
|
||||
BMLoop *l_iter, *l_first;
|
||||
l_iter = l_first = e_iter->l;
|
||||
do {
|
||||
if (!BM_elem_flag_test(l_iter->f, BM_ELEM_HIDDEN)) {
|
||||
return (l_iter->v == v) ? l_iter : l_iter->next;
|
||||
}
|
||||
} while ((l_iter = l_iter->radial_next) != l_first);
|
||||
}
|
||||
}
|
||||
} while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BMEdge *bmesh_disk_faceedge_find_next(const BMEdge *e, const BMVert *v)
|
||||
{
|
||||
BMEdge *e_find;
|
||||
e_find = bmesh_disk_edge_next(e, v);
|
||||
do {
|
||||
if (e_find->l && bmesh_radial_facevert_check(e_find->l, v)) {
|
||||
return e_find;
|
||||
}
|
||||
} while ((e_find = bmesh_disk_edge_next(e_find, v)) != e);
|
||||
return const_cast<BMEdge *>(e);
|
||||
}
|
||||
|
||||
bool bmesh_radial_validate(int radlen, BMLoop *l)
|
||||
{
|
||||
BMLoop *l_iter = l;
|
||||
int i = 0;
|
||||
|
||||
if (bmesh_radial_length(l) != radlen) {
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
if (UNLIKELY(!l_iter)) {
|
||||
BMESH_ASSERT(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (l_iter->e != l->e) {
|
||||
return false;
|
||||
}
|
||||
if (!ELEM(l_iter->v, l->e->v1, l->e->v2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (UNLIKELY(i > BM_LOOP_RADIAL_MAX)) {
|
||||
BMESH_ASSERT(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
i++;
|
||||
} while ((l_iter = l_iter->radial_next) != l);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void bmesh_radial_loop_append(BMEdge *e, BMLoop *l)
|
||||
{
|
||||
if (e->l == nullptr) {
|
||||
e->l = l;
|
||||
l->radial_next = l->radial_prev = l;
|
||||
}
|
||||
else {
|
||||
l->radial_prev = e->l;
|
||||
l->radial_next = e->l->radial_next;
|
||||
|
||||
e->l->radial_next->radial_prev = l;
|
||||
e->l->radial_next = l;
|
||||
|
||||
e->l = l;
|
||||
}
|
||||
|
||||
if (UNLIKELY(l->e && l->e != e)) {
|
||||
/* l is already in a radial cycle for a different edge */
|
||||
BMESH_ASSERT(0);
|
||||
}
|
||||
|
||||
l->e = e;
|
||||
}
|
||||
|
||||
void bmesh_radial_loop_remove(BMEdge *e, BMLoop *l)
|
||||
{
|
||||
/* if e is non-nullptr, l must be in the radial cycle of e */
|
||||
if (UNLIKELY(e != l->e)) {
|
||||
BMESH_ASSERT(0);
|
||||
}
|
||||
|
||||
if (l->radial_next != l) {
|
||||
if (l == e->l) {
|
||||
e->l = l->radial_next;
|
||||
}
|
||||
|
||||
l->radial_next->radial_prev = l->radial_prev;
|
||||
l->radial_prev->radial_next = l->radial_next;
|
||||
}
|
||||
else {
|
||||
if (l == e->l) {
|
||||
e->l = nullptr;
|
||||
}
|
||||
else {
|
||||
BMESH_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* l is no longer in a radial cycle; empty the links
|
||||
* to the cycle and the link back to an edge */
|
||||
l->radial_next = l->radial_prev = nullptr;
|
||||
l->e = nullptr;
|
||||
}
|
||||
|
||||
void bmesh_radial_loop_unlink(BMLoop *l)
|
||||
{
|
||||
if (l->radial_next != l) {
|
||||
l->radial_next->radial_prev = l->radial_prev;
|
||||
l->radial_prev->radial_next = l->radial_next;
|
||||
}
|
||||
|
||||
/* l is no longer in a radial cycle; empty the links
|
||||
* to the cycle and the link back to an edge */
|
||||
l->radial_next = l->radial_prev = nullptr;
|
||||
l->e = nullptr;
|
||||
}
|
||||
|
||||
BMLoop *bmesh_radial_faceloop_find_first(const BMLoop *l, const BMVert *v)
|
||||
{
|
||||
const BMLoop *l_iter;
|
||||
l_iter = l;
|
||||
do {
|
||||
if (l_iter->v == v) {
|
||||
return const_cast<BMLoop *>(l_iter);
|
||||
}
|
||||
} while ((l_iter = l_iter->radial_next) != l);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BMLoop *bmesh_radial_faceloop_find_next(const BMLoop *l, const BMVert *v)
|
||||
{
|
||||
BMLoop *l_iter;
|
||||
l_iter = l->radial_next;
|
||||
do {
|
||||
if (l_iter->v == v) {
|
||||
return l_iter;
|
||||
}
|
||||
} while ((l_iter = l_iter->radial_next) != l);
|
||||
return const_cast<BMLoop *>(l);
|
||||
}
|
||||
|
||||
int bmesh_radial_length(const BMLoop *l)
|
||||
{
|
||||
const BMLoop *l_iter = l;
|
||||
int i = 0;
|
||||
|
||||
if (!l) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
if (UNLIKELY(!l_iter)) {
|
||||
/* Radial cycle is broken (not a circular loop). */
|
||||
BMESH_ASSERT(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
i++;
|
||||
if (UNLIKELY(i >= BM_LOOP_RADIAL_MAX)) {
|
||||
BMESH_ASSERT(0);
|
||||
return -1;
|
||||
}
|
||||
} while ((l_iter = l_iter->radial_next) != l);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int bmesh_radial_facevert_count(const BMLoop *l, const BMVert *v)
|
||||
{
|
||||
const BMLoop *l_iter;
|
||||
int count = 0;
|
||||
l_iter = l;
|
||||
do {
|
||||
if (l_iter->v == v) {
|
||||
count++;
|
||||
}
|
||||
} while ((l_iter = l_iter->radial_next) != l);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int bmesh_radial_facevert_count_at_most(const BMLoop *l, const BMVert *v, const int count_max)
|
||||
{
|
||||
const BMLoop *l_iter;
|
||||
int count = 0;
|
||||
l_iter = l;
|
||||
do {
|
||||
if (l_iter->v == v) {
|
||||
count++;
|
||||
if (count == count_max) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while ((l_iter = l_iter->radial_next) != l);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool bmesh_radial_facevert_check(const BMLoop *l, const BMVert *v)
|
||||
{
|
||||
const BMLoop *l_iter;
|
||||
l_iter = l;
|
||||
do {
|
||||
if (l_iter->v == v) {
|
||||
return true;
|
||||
}
|
||||
} while ((l_iter = l_iter->radial_next) != l);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bmesh_loop_validate(BMFace *f)
|
||||
{
|
||||
int i;
|
||||
int len = f->len;
|
||||
BMLoop *l_iter, *l_first;
|
||||
|
||||
l_first = BM_FACE_FIRST_LOOP(f);
|
||||
|
||||
if (l_first == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Validate that the face loop cycle is the length specified by f->len */
|
||||
for (i = 1, l_iter = l_first->next; i < len; i++, l_iter = l_iter->next) {
|
||||
if ((l_iter->f != f) || (l_iter == l_first)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (l_iter != l_first) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Validate the loop->prev links also form a cycle of length f->len */
|
||||
for (i = 1, l_iter = l_first->prev; i < len; i++, l_iter = l_iter->prev) {
|
||||
if (l_iter == l_first) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (l_iter != l_first) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
169
blender-5.2.0/source/blender/bmesh/intern/bmesh_structure.hh
Normal file
169
blender-5.2.0/source/blender/bmesh/intern/bmesh_structure.hh
Normal file
@@ -0,0 +1,169 @@
|
||||
/* SPDX-FileCopyrightText: 2004 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* The lowest level of functionality for manipulating bmesh structures.
|
||||
* None of these functions should ever be exported to the rest of Blender.
|
||||
*
|
||||
* in the vast majority of cases there shouldn't be used directly.
|
||||
* if absolutely necessary, see function definitions in code for
|
||||
* descriptive comments. but seriously, don't use this stuff.
|
||||
*/
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_compiler_compat.h"
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Loop Cycle Management
|
||||
* Loop cycle functions, e.g. loops surrounding a face.
|
||||
* \{ */
|
||||
|
||||
bool bmesh_loop_validate(BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Disk Cycle Management
|
||||
* \{ */
|
||||
|
||||
void bmesh_disk_edge_append(BMEdge *e, BMVert *v) ATTR_NONNULL();
|
||||
void bmesh_disk_edge_remove(BMEdge *e, BMVert *v) ATTR_NONNULL();
|
||||
BLI_INLINE BMEdge *bmesh_disk_edge_next_safe(const BMEdge *e,
|
||||
const BMVert *v) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
BLI_INLINE BMEdge *bmesh_disk_edge_prev_safe(const BMEdge *e,
|
||||
const BMVert *v) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
BLI_INLINE BMEdge *bmesh_disk_edge_next(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
BLI_INLINE BMEdge *bmesh_disk_edge_prev(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
int bmesh_disk_facevert_count_at_most(const BMVert *v, int count_max) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* \brief DISK COUNT FACE VERT
|
||||
*
|
||||
* Counts the number of loop users
|
||||
* for this vertex. Note that this is
|
||||
* equivalent to counting the number of
|
||||
* faces incident upon this vertex
|
||||
*/
|
||||
int bmesh_disk_facevert_count(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* \brief FIND FIRST FACE EDGE
|
||||
*
|
||||
* Finds the first edge in a vertices
|
||||
* Disk cycle that has one of this
|
||||
* vert's loops attached
|
||||
* to it.
|
||||
*/
|
||||
BMEdge *bmesh_disk_faceedge_find_first(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* Special case for BM_LOOPS_OF_VERT & BM_FACES_OF_VERT, avoids 2x calls.
|
||||
*
|
||||
* The returned BMLoop.e matches the result of #bmesh_disk_faceedge_find_first
|
||||
*/
|
||||
BMLoop *bmesh_disk_faceloop_find_first(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* A version of #bmesh_disk_faceloop_find_first that ignores hidden faces.
|
||||
*/
|
||||
BMLoop *bmesh_disk_faceloop_find_first_visible(const BMEdge *e,
|
||||
const BMVert *v) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
BMEdge *bmesh_disk_faceedge_find_next(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Radial Cycle Management
|
||||
*
|
||||
* Radial cycle functions, e.g. loops surrounding edges.
|
||||
* \{ */
|
||||
|
||||
void bmesh_radial_loop_append(BMEdge *e, BMLoop *l) ATTR_NONNULL();
|
||||
/**
|
||||
* \brief BMESH RADIAL REMOVE LOOP
|
||||
*
|
||||
* Removes a loop from an radial cycle. If edge e is non-NULL
|
||||
* it should contain the radial cycle, and it will also get
|
||||
* updated (in the case that the edge's link into the radial
|
||||
* cycle was the loop which is being removed from the cycle).
|
||||
*/
|
||||
void bmesh_radial_loop_remove(BMEdge *e, BMLoop *l) ATTR_NONNULL();
|
||||
/**
|
||||
* A version of #bmesh_radial_loop_remove which only performs the radial unlink,
|
||||
* leaving the edge untouched.
|
||||
*/
|
||||
void bmesh_radial_loop_unlink(BMLoop *l) ATTR_NONNULL();
|
||||
/* NOTE:
|
||||
* bmesh_radial_loop_next(BMLoop *l) / prev.
|
||||
* just use member access l->radial_next, l->radial_prev now */
|
||||
|
||||
int bmesh_radial_facevert_count_at_most(const BMLoop *l,
|
||||
const BMVert *v,
|
||||
int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* \brief RADIAL COUNT FACE VERT
|
||||
*
|
||||
* Returns the number of times a vertex appears
|
||||
* in a radial cycle
|
||||
*/
|
||||
int bmesh_radial_facevert_count(const BMLoop *l, const BMVert *v) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* \brief RADIAL CHECK FACE VERT
|
||||
*
|
||||
* Quicker check for `bmesh_radial_facevert_count(...) != 0`.
|
||||
*/
|
||||
bool bmesh_radial_facevert_check(const BMLoop *l, const BMVert *v) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* \brief BME RADIAL FIND FIRST FACE VERT
|
||||
*
|
||||
* Finds the first loop of v around radial
|
||||
* cycle
|
||||
*/
|
||||
BMLoop *bmesh_radial_faceloop_find_first(const BMLoop *l, const BMVert *v) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
BMLoop *bmesh_radial_faceloop_find_next(const BMLoop *l, const BMVert *v) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
BMLoop *bmesh_radial_faceloop_find_vert(const BMFace *f, const BMVert *v) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
|
||||
bool bmesh_radial_validate(int radlen, BMLoop *l) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Edge Utilities
|
||||
* \{ */
|
||||
|
||||
void bmesh_disk_vert_swap(BMEdge *e, BMVert *v_dst, BMVert *v_src) ATTR_NONNULL();
|
||||
/**
|
||||
* Handles all connected data, use with care.
|
||||
*
|
||||
* Assumes caller has setup correct state before the swap is done.
|
||||
*/
|
||||
void bmesh_edge_vert_swap(BMEdge *e, BMVert *v_dst, BMVert *v_src) ATTR_NONNULL();
|
||||
void bmesh_disk_vert_replace(BMEdge *e, BMVert *v_dst, BMVert *v_src) ATTR_NONNULL();
|
||||
BMEdge *bmesh_disk_edge_exists(const BMVert *v1, const BMVert *v2) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
bool bmesh_disk_validate(int len, BMEdge *e, BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
|
||||
#include "intern/bmesh_structure_inline.hh" /* IWYU pragma: export */
|
||||
@@ -0,0 +1,71 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* BMesh inline operator functions.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_compiler_compat.h"
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
#include "intern/bmesh_query.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2)
|
||||
BLI_INLINE BMDiskLink *bmesh_disk_edge_link_from_vert(const BMEdge *e, const BMVert *v)
|
||||
{
|
||||
BLI_assert(BM_vert_in_edge(e, v));
|
||||
return const_cast<BMDiskLink *>(&(&e->v1_disk_link)[v == e->v2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Next Disk Edge
|
||||
*
|
||||
* Find the next edge in a disk cycle
|
||||
*
|
||||
* \return Pointer to the next edge in the disk cycle for the vertex v.
|
||||
*/
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
|
||||
BLI_INLINE BMEdge *bmesh_disk_edge_next_safe(const BMEdge *e, const BMVert *v)
|
||||
{
|
||||
if (v == e->v1) {
|
||||
return e->v1_disk_link.next;
|
||||
}
|
||||
if (v == e->v2) {
|
||||
return e->v2_disk_link.next;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
|
||||
BLI_INLINE BMEdge *bmesh_disk_edge_prev_safe(const BMEdge *e, const BMVert *v)
|
||||
{
|
||||
if (v == e->v1) {
|
||||
return e->v1_disk_link.prev;
|
||||
}
|
||||
if (v == e->v2) {
|
||||
return e->v2_disk_link.prev;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2) BLI_INLINE BMEdge *bmesh_disk_edge_next(const BMEdge *e,
|
||||
const BMVert *v)
|
||||
{
|
||||
return BM_DISK_EDGE_NEXT(e, v);
|
||||
}
|
||||
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2) BLI_INLINE BMEdge *bmesh_disk_edge_prev(const BMEdge *e,
|
||||
const BMVert *v)
|
||||
{
|
||||
return BM_DISK_EDGE_PREV(e, v);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
2524
blender-5.2.0/source/blender/bmesh/intern/bmesh_uvselect.cc
Normal file
2524
blender-5.2.0/source/blender/bmesh/intern/bmesh_uvselect.cc
Normal file
File diff suppressed because it is too large
Load Diff
581
blender-5.2.0/source/blender/bmesh/intern/bmesh_uvselect.hh
Normal file
581
blender-5.2.0/source/blender/bmesh/intern/bmesh_uvselect.hh
Normal file
@@ -0,0 +1,581 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
#include "BLI_vector_list.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* Overview
|
||||
* ========
|
||||
*
|
||||
* The `BM_uvselect_*` API deals with synchronizing selection
|
||||
* between UV's and selected vertices edges & faces,
|
||||
* where a selected vertex in the 3D viewport may only have some of its
|
||||
* UV vertices selected in the UV editor.
|
||||
*
|
||||
* Supporting this involves flushing in both directions depending on the selection being edited.
|
||||
*
|
||||
* \note See #78393 for a user-level overview of this functionality.
|
||||
* This describes the motivation to synchronize selection between UV's and the mesh.
|
||||
*
|
||||
* \note A short-hand term for vertex/edge/face selection used
|
||||
* in this file is View3D abbreviated to `v3d`, since this is the section
|
||||
* manipulated in the viewport, e.g. #BM_mesh_uvselect_sync_to_mesh.
|
||||
*
|
||||
* \note This is quite involved. As a last resort the UV selection can always be cleared
|
||||
* and re-set from the mesh (v3d) selection, however it's good to keep UV selection
|
||||
* if possible because resetting may extend vertex selection to other UV islands.
|
||||
*
|
||||
* Terms
|
||||
* =====
|
||||
*
|
||||
* - Synchronized Selection (abbreviated to "sync"). See #BMesh::uv_select_sync_valid.
|
||||
* When the UV synchronized data is valid, it means there is a valid relationship
|
||||
* between the UV selection flags (#BM_ELEM_SELECT_UV & #BM_ELEM_SELECT_UV_EDGE)
|
||||
* and the meshes selection (#BM_ELEM_SELECT_UV).
|
||||
*
|
||||
* - When the UV selection changes (from the UV editor)
|
||||
* this needs to be synchronized to the mesh.
|
||||
* - When the base-selection flags change (from the 3D viewport)
|
||||
* this needs to be synchronized to the UV's.
|
||||
* Synchronizing in this direction may be lossy, although (depending on the operation),
|
||||
* support for maintaining a synchronized selection may be possible.
|
||||
*
|
||||
* - Flushing Selection ("flush")
|
||||
* When an element is selected or de-selected, the selection state
|
||||
* of connected geometry may change too.
|
||||
* So, de-selecting a vertex must de-select all faces that use that vertex.
|
||||
*
|
||||
* The rules for flushing may depend on the selection mode.
|
||||
* When de-selecting a face in vertex-select-mode, all its vertices & edges
|
||||
* must also be de-selected. When de-selecting a face in face-select-mode,
|
||||
* only vertices and edges no longer connected to any selected faces will be de-selected.
|
||||
*
|
||||
* Since applying these rules while selecting individual elements is often impractical,
|
||||
* it's common to adjust the selection, then flush based on the selection mode afterwards.
|
||||
*
|
||||
* - Flushing up:
|
||||
* Flushing the selection from [verts -> edges/faces], [edges -> faces].
|
||||
* - Flushing down:
|
||||
* Flushing the selection from [faces -> verts/edges], [edges -> verts].
|
||||
*
|
||||
* - Isolated vertex or edge selection:
|
||||
* When a vertex or edge is selected without being connected to a selected face.
|
||||
*
|
||||
* UV Selection Flags
|
||||
* ==================
|
||||
*
|
||||
* - UV selection uses:
|
||||
* - #BM_ELEM_SELECT_UV & #BM_ELEM_SELECT_UV_EDGE for #BMLoop
|
||||
* to define selected vertices & edges.
|
||||
* - #BM_ELEM_SELECT_UV for #BMFace.
|
||||
*
|
||||
* Hidden Flags
|
||||
* ============
|
||||
*
|
||||
* Unlike viewport selection there is no requirement for hidden elements not to be selected.
|
||||
* Therefor, UV selection checks must check the underlying geometry is not hidden.
|
||||
* In practice this means hidden faces must be assumed unselected,
|
||||
* since UV's are part of the faces (there is no such thing as a hidden face-corner)
|
||||
* and any hidden edge or vertex causes connected faces to be hidden.
|
||||
*
|
||||
* UV Selection Flushing
|
||||
* =====================
|
||||
*
|
||||
* Selection setting functions flush down (unless the `_noflush(...)` version is used),
|
||||
* this means selecting a face also selects all verts & edges,
|
||||
* selecting an edge selects its vertices.
|
||||
*
|
||||
* However it's expected the selection is flushed,
|
||||
* de-selecting a vertex or edge must de-select it's faces (flushing up).
|
||||
* For this, there are various flushing functions,
|
||||
* exactly what is needed depends on the selection operation performed and the selection mode.
|
||||
*
|
||||
* There are also situations that shouldn't be allowed such as a single selected vertex in face
|
||||
* select mode.
|
||||
*
|
||||
* Flushing & Synchronizing
|
||||
* ========================
|
||||
*
|
||||
* Properly handling the selection state is important for operators that adjust the UV selection.
|
||||
* This typically involves the following steps:
|
||||
*
|
||||
* - The UV selection changes.
|
||||
* - The UV selection must be flushed between elements to ensure the selection is valid,
|
||||
* (see: `BM_mesh_uvselect_flush_*` & `BM_mesh_uvselect_mode_flush_*` functions).
|
||||
* - The UV selection must be synchronized to the mesh selection
|
||||
* (see #BM_mesh_uvselect_sync_to_mesh).
|
||||
* - The mesh must then flush selection to its elements
|
||||
* (see: `BM_mesh_select_flush_*` & `BM_mesh_select_mode_flush_*` functions).
|
||||
*
|
||||
* Valid State
|
||||
* ===========
|
||||
*
|
||||
* For a valid state:
|
||||
* - A selected UV-vertex must have its underlying mesh vertex selected.
|
||||
* - A selected mesh-vertex must have at least one UV-vertex selected.
|
||||
*
|
||||
* This is *mostly* true for edges/faces too, however there cases where
|
||||
* the UV selection causes an edge/face to be selected in mesh space but not UV space.
|
||||
*
|
||||
* See #BM_mesh_uvselect_is_valid for details.
|
||||
*
|
||||
* Clearing the Valid State
|
||||
* ========================
|
||||
*
|
||||
* As already noted, tools should maintain the synchronized UV selection where possible.
|
||||
* However when this information *isn't* needed it should be cleared aggressively
|
||||
* (see #BM_mesh_uvselect_clear), since it adds both computation & memory overhead.
|
||||
*
|
||||
* For actions that overwrite the selection such as selecting or de-selecting all,
|
||||
* it's safe to "clear" the data, other actions such as adding new geometry that replaces
|
||||
* the selection can also safely "clear" the UV selection.
|
||||
*
|
||||
* In practice users modeling in the 3D viewport are likely to clear the UV selection data
|
||||
* since selecting the mesh without extending the selection is effectively a "De-select All".
|
||||
* So the chances this data persists when it's not needed over many editing operations are low.
|
||||
*/
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UV Selection Functions (low level)
|
||||
*
|
||||
* Selection checking functions.
|
||||
* These should be used instead of checking #BM_ELEM_SELECT_UV,
|
||||
* so hidden geometry is never considered selected.
|
||||
* \{ */
|
||||
|
||||
bool BM_face_uvselect_test(const BMFace *f);
|
||||
bool BM_loop_vert_uvselect_test(const BMLoop *l);
|
||||
bool BM_loop_edge_uvselect_test(const BMLoop *l);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UV Selection Connectivity Checks
|
||||
*
|
||||
* Regarding the `hflag` parameter: this is typically set to:
|
||||
* - #BM_ELEM_SELECT for mesh selection.
|
||||
* - #BM_ELEM_SELECT_UV for selected UV vertices.
|
||||
* - #BM_ELEM_SELECT_UV_EDGE for selected UV edges.
|
||||
* - #BM_ELEM_SELECT_TAG to allow the caller to use a separate non-selection flag.
|
||||
*
|
||||
* Each function asserts that a supported `hflag` is passed in.
|
||||
* \{ */
|
||||
|
||||
bool BM_loop_vert_uvselect_check_other_loop_vert(BMLoop *l, char hflag, int cd_loop_uv_offset);
|
||||
bool BM_loop_vert_uvselect_check_other_loop_edge(BMLoop *l, char hflag, int cd_loop_uv_offset);
|
||||
bool BM_loop_vert_uvselect_check_other_edge(BMLoop *l, char hflag, int cd_loop_uv_offset);
|
||||
bool BM_loop_vert_uvselect_check_other_face(BMLoop *l, char hflag, int cd_loop_uv_offset);
|
||||
bool BM_loop_edge_uvselect_check_other_loop_edge(BMLoop *l, char hflag, int cd_loop_uv_offset);
|
||||
bool BM_loop_edge_uvselect_check_other_face(BMLoop *l, char hflag, int cd_loop_uv_offset);
|
||||
|
||||
bool BM_face_uvselect_check_edges_all(BMFace *f);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UV Selection Functions
|
||||
* \{ */
|
||||
|
||||
/** Set the UV selection flag for `f` without flushing down to edges & vertices. */
|
||||
void BM_face_uvselect_set_noflush(BMesh *bm, BMFace *f, bool select);
|
||||
/** Set the UV selection flag for `f` & flush down to edges & vertices. */
|
||||
void BM_face_uvselect_set(BMesh *bm, BMFace *f, bool select);
|
||||
|
||||
/** Set the UV selection flag for `e` without flushing down to vertices. */
|
||||
void BM_loop_edge_uvselect_set_noflush(BMesh *bm, BMLoop *l, bool select);
|
||||
/** Set the UV selection flag for `e` & flush down to vertices. */
|
||||
void BM_loop_edge_uvselect_set(BMesh *bm, BMLoop *l, bool select);
|
||||
/**
|
||||
* Set the UV selection flag for `v` without flushing down.
|
||||
* since there is nothing to flush down to.
|
||||
*/
|
||||
void BM_loop_vert_uvselect_set_noflush(BMesh *bm, BMLoop *l, bool select);
|
||||
|
||||
/**
|
||||
* Call this function when selecting mesh elements in the viewport and
|
||||
* the relationship with UV's is lost.
|
||||
*
|
||||
* \return True if UV select is cleared (a change was made).
|
||||
*
|
||||
* This has two purposes:
|
||||
*
|
||||
* - Maintaining the UV selection isn't needed:
|
||||
* Some operations such as adding a new mesh primitive clear the selection,
|
||||
* selecting all geometry from the new primitive.
|
||||
* In this case a UV selection is redundant & should be cleared.
|
||||
*
|
||||
* - Maintaining the UV selection isn't supported:
|
||||
* Some selection operations don't support maintaining a valid UV selection,
|
||||
* in that case it's necessary to clear the UV selection otherwise tools may
|
||||
* seem to be broken if they aren't operating on the selection properly.
|
||||
*
|
||||
* NOTE(@ideasman42): It's worth noting that in this case clearing the selection is "lossy",
|
||||
* users may wish that all selection operations would handle UV selection data too.
|
||||
* Supporting additional operations is always possible, at the time of writing it's
|
||||
* impractical to do so, see: #131642 design task for details.
|
||||
*
|
||||
* Internally this marks the UV selection data as invalid,
|
||||
* using the mesh selection as the "source-of-truth".
|
||||
*
|
||||
* \note By convention call this immediately after flushing.
|
||||
*
|
||||
* \note In many cases the UV selection can be maintained and this function removed,
|
||||
* although it adds some complexity & overhead.
|
||||
* See #UVSyncSelectFromMesh.
|
||||
*
|
||||
* \note Calls to this function that should *not* be removed in favor of supporting UV selection,
|
||||
* this should be mentioned in a code-comment, making it clear this is not a limitation to *fix*.
|
||||
*/
|
||||
bool BM_mesh_uvselect_clear(BMesh *bm);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UV Selection Functions (Shared)
|
||||
* \{ */
|
||||
|
||||
void BM_loop_vert_uvselect_set_shared(BMesh *bm, BMLoop *l, bool select, int cd_loop_uv_offset);
|
||||
void BM_loop_edge_uvselect_set_shared(BMesh *bm, BMLoop *l, bool select, int cd_loop_uv_offset);
|
||||
void BM_face_uvselect_set_shared(BMesh *bm, BMFace *f, bool select, int cd_loop_uv_offset);
|
||||
|
||||
void BM_mesh_uvselect_set_elem_shared(BMesh *bm,
|
||||
bool select,
|
||||
int cd_loop_uv_offset,
|
||||
Span<BMLoop *> loop_verts,
|
||||
Span<BMLoop *> loop_edges,
|
||||
Span<BMFace *> faces);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UV Selection Picking
|
||||
* \{ */
|
||||
|
||||
struct BMUVSelectPickParams {
|
||||
/**
|
||||
* The custom data offset for the active UV layer.
|
||||
* May be -1, in this case UV connectivity checks are skipped.
|
||||
*/
|
||||
int cd_loop_uv_offset = -1;
|
||||
/**
|
||||
* If true, selection changes propagate to all other UV elements
|
||||
* that share the same UV coordinates (contiguous selection).
|
||||
*
|
||||
* Typically derived from #ToolSettings::uv_sticky, although in some cases
|
||||
* it's assumed to be true (when switching selection modes for example)
|
||||
* because the tool settings aren't available in that context.
|
||||
*
|
||||
* A boolean can be used since "Shared Vertex" (uv_sticky mode)
|
||||
* can check the meshes vertex selection directly.
|
||||
*/
|
||||
bool shared = true;
|
||||
};
|
||||
|
||||
void BM_vert_uvselect_set_pick(BMesh *bm,
|
||||
BMVert *v,
|
||||
bool select,
|
||||
const BMUVSelectPickParams ¶ms);
|
||||
void BM_edge_uvselect_set_pick(BMesh *bm,
|
||||
BMEdge *e,
|
||||
bool select,
|
||||
const BMUVSelectPickParams ¶ms);
|
||||
void BM_face_uvselect_set_pick(BMesh *bm,
|
||||
BMFace *f,
|
||||
bool select,
|
||||
const BMUVSelectPickParams ¶ms);
|
||||
|
||||
/**
|
||||
* Select/deselect elements in the viewport,
|
||||
* then integrate the selection with the UV selection,
|
||||
* without clearing an re-initializing the synchronized state.
|
||||
* (likely to re-select islands bounds from a user-perspective).
|
||||
*/
|
||||
void BM_mesh_uvselect_set_elem_from_mesh(BMesh *bm,
|
||||
bool select,
|
||||
const BMUVSelectPickParams ¶ms,
|
||||
Span<BMVert *> verts,
|
||||
Span<BMEdge *> edges,
|
||||
Span<BMFace *> faces);
|
||||
/** \copydoc #BM_mesh_uvselect_set_elem_from_mesh. */
|
||||
void BM_mesh_uvselect_set_elem_from_mesh(BMesh *bm,
|
||||
bool select,
|
||||
const BMUVSelectPickParams ¶ms,
|
||||
const VectorList<BMVert *> &verts,
|
||||
const VectorList<BMEdge *> &edges,
|
||||
const VectorList<BMFace *> &faces);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UV Selection Flushing (Only Select/De-Select)
|
||||
*
|
||||
* \note In most cases flushing assumes selection has already been flushed down.
|
||||
*
|
||||
* This means:
|
||||
* - A selected edge must have both UV vertices selected.
|
||||
* - A selected faces has all its edges & vertices selected.
|
||||
*
|
||||
* It's often useful to call #BM_mesh_uvselect_flush_shared_only_select
|
||||
* after using these non-UV-coordinate aware flushing functions.
|
||||
* \{ */
|
||||
|
||||
void BM_mesh_uvselect_flush_from_loop_verts_only_select(BMesh *bm);
|
||||
void BM_mesh_uvselect_flush_from_loop_verts_only_deselect(BMesh *bm);
|
||||
void BM_mesh_uvselect_flush_from_loop_edges_only_select(BMesh *bm);
|
||||
void BM_mesh_uvselect_flush_from_loop_edges_only_deselect(BMesh *bm);
|
||||
void BM_mesh_uvselect_flush_from_faces_only_select(BMesh *bm);
|
||||
void BM_mesh_uvselect_flush_from_faces_only_deselect(BMesh *bm);
|
||||
|
||||
/**
|
||||
* A useful utility so simple selection operations can be performed on edges/faces,
|
||||
* afterwards this can be used to select UV's that are connected.
|
||||
* This avoids having to use more involved UV connectivity aware logic inline.
|
||||
*/
|
||||
void BM_mesh_uvselect_flush_shared_only_select(BMesh *bm, int cd_loop_uv_offset);
|
||||
void BM_mesh_uvselect_flush_shared_only_deselect(BMesh *bm, int cd_loop_uv_offset);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UV Selection Flushing (Between Elements)
|
||||
*
|
||||
* Regarding the `flush_down` argument.
|
||||
*
|
||||
* Primitive UV selection functions always flush down:
|
||||
* - #BM_face_uvselect_set
|
||||
* - #BM_loop_edge_uvselect_set
|
||||
* - #BM_loop_vert_uvselect_set
|
||||
*
|
||||
* This means it's often only necessary to flush up after the selection has been changed.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Mode independent UV selection/de-selection flush from UV vertices.
|
||||
*
|
||||
* \note The caller may need to run #BM_mesh_uvselect_flush_shared_only_select afterwards.
|
||||
*/
|
||||
void BM_mesh_uvselect_flush_from_loop_verts(BMesh *bm);
|
||||
/**
|
||||
* Mode independent UV selection/de-selection flush from UV edges.
|
||||
*
|
||||
* Flush from loop edges up to faces and optionally down to vertices (when `flush_down` is true).
|
||||
*
|
||||
* \note The caller may need to run #BM_mesh_uvselect_flush_shared_only_select afterwards.
|
||||
*/
|
||||
void BM_mesh_uvselect_flush_from_loop_edges(BMesh *bm, bool flush_down);
|
||||
/**
|
||||
* Mode independent UV selection/de-selection flush from UV faces.
|
||||
*
|
||||
* Flush from faces down to edges & vertices (when `flush_down` is true).
|
||||
*
|
||||
* \note The caller may need to run #BM_mesh_uvselect_flush_shared_only_select afterwards.
|
||||
*/
|
||||
void BM_mesh_uvselect_flush_from_faces(BMesh *bm, bool flush_down);
|
||||
|
||||
/**
|
||||
* Mode independent UV selection/de-selection flush from UV vertices.
|
||||
*
|
||||
* Use this when it's know geometry was only selected/de-selected.
|
||||
*
|
||||
* \note An equivalent to #BM_mesh_select_flush_from_verts for the UV selection.
|
||||
*/
|
||||
void BM_mesh_uvselect_flush_from_verts(BMesh *bm, bool select);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UV Selection Flushing (Selection Mode Aware)
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* \param flush_down: See #BMSelectFlushFlag::Down for notes on flushing down.
|
||||
*/
|
||||
void BM_mesh_uvselect_mode_flush_ex(BMesh *bm, const short selectmode, bool flush_down);
|
||||
void BM_mesh_uvselect_mode_flush(BMesh *bm);
|
||||
|
||||
/**
|
||||
* Select elements based on the selection mode.
|
||||
* (flushes the selection *up* based on the mode).
|
||||
*
|
||||
* - With vertex selection mode enabled: flush up to edges and faces.
|
||||
* - With edge selection mode enabled: flush to faces.
|
||||
* - With *only* face selection mode enabled: do nothing.
|
||||
*
|
||||
* \note An "only deselect" version function could be added, it's not needed at the moment.a
|
||||
*/
|
||||
void BM_mesh_uvselect_mode_flush_only_select(BMesh *bm);
|
||||
|
||||
/**
|
||||
* When the select mode changes, update to ensure the selection is valid.
|
||||
* So single vertices aren't selected in edge-select mode for example.
|
||||
*
|
||||
* The mesh selection flushing must have already run.
|
||||
*/
|
||||
void BM_mesh_uvselect_mode_flush_update(BMesh *bm,
|
||||
short selectmode_old,
|
||||
short selectmode_new,
|
||||
int cd_loop_uv_offset);
|
||||
|
||||
/**
|
||||
* A specialized flushing that fills in selection information after subdividing.
|
||||
*
|
||||
* It's important this runs:
|
||||
* - After subdivision.
|
||||
* - After the mesh selection has already been flushed.
|
||||
*
|
||||
* \note Intended to be a generic utility to be used in any situation
|
||||
* new geometry is created by splitting existing geometry.
|
||||
*/
|
||||
void BM_mesh_uvselect_flush_post_subdivide(BMesh *bm, int cd_loop_uv_offset);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UV Selection Flushing (From/To Mesh)
|
||||
* \{ */
|
||||
|
||||
/* From 3D viewport to UV selection.
|
||||
*
|
||||
* These functions correspond to #ToolSettings::uv_sticky options. */
|
||||
|
||||
void BM_mesh_uvselect_sync_from_mesh_sticky_location(BMesh *bm, int cd_loop_uv_offset);
|
||||
void BM_mesh_uvselect_sync_from_mesh_sticky_disabled(BMesh *bm);
|
||||
void BM_mesh_uvselect_sync_from_mesh_sticky_vert(BMesh *bm);
|
||||
|
||||
/**
|
||||
* Synchronize selection: from the UV selection to the 3D viewport.
|
||||
*
|
||||
* \note #BMesh::uv_select_sync_valid must be true.
|
||||
*/
|
||||
void BM_mesh_uvselect_sync_to_mesh(BMesh *bm);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UV Selection Validation
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* UV/Mesh Synchronization
|
||||
*
|
||||
* Check the selection has been properly synchronized between the mesh and the UV's.
|
||||
*
|
||||
* \note It is essential for this to be correct and return no errors.
|
||||
* Other checks are useful to ensure the selection state meets the expectations of the caller
|
||||
* but the state is not invalid - as it is when the selection is out-of-sync.
|
||||
*/
|
||||
struct UVSelectValidateInfo_Sync {
|
||||
/** When a vertex is unselected none of it's UV's may be selected. */
|
||||
int count_uv_vert_any_selected_with_vert_unselected = 0;
|
||||
/** When a vertex is selected at least one UV must be selected. */
|
||||
int count_uv_vert_none_selected_with_vert_selected = 0;
|
||||
|
||||
/** When a edge is unselected none of it's UV's may be selected. */
|
||||
int count_uv_edge_any_selected_with_edge_unselected = 0;
|
||||
/** When a edge is selected at least one UV must be selected. */
|
||||
int count_uv_edge_none_selected_with_edge_selected = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Flushing between elements.
|
||||
*
|
||||
* Check the selection has been properly flushing between elements.
|
||||
*/
|
||||
struct UVSelectValidateInfo_Flush {
|
||||
/** Edges are selected without selected vertices. */
|
||||
int count_uv_edge_selected_with_any_verts_unselected = 0;
|
||||
/** Edges are unselected with all selected vertices. */
|
||||
int count_uv_edge_unselected_with_all_verts_selected = 0;
|
||||
|
||||
/** Faces are selected without selected vertices. */
|
||||
int count_uv_face_selected_with_any_verts_unselected = 0;
|
||||
/** Faces are unselected with all selected vertices. */
|
||||
int count_uv_face_unselected_with_all_verts_selected = 0;
|
||||
|
||||
/** Faces are selected without selected edges. */
|
||||
int count_uv_face_selected_with_any_edges_unselected = 0;
|
||||
/** Faces are unselected with all selected edges. */
|
||||
int count_uv_face_unselected_with_all_edges_selected = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Contiguous.
|
||||
*
|
||||
* Check the selected UV's are contiguous,
|
||||
* in situations where it's expected selecting a UV will select all "connected" UV's
|
||||
* (UV's sharing the same vertex with the same UV coordinate).
|
||||
*/
|
||||
struct UVSelectValidateInfo_Contiguous {
|
||||
/** When a vertices connected UV's are co-located without matching selection. */
|
||||
int count_uv_vert_non_contiguous_selected = 0;
|
||||
/** When a edges connected UV's are co-located without matching selection. */
|
||||
int count_uv_edge_non_contiguous_selected = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Flush & contiguous.
|
||||
*
|
||||
* In some cases it's necessary to check flushing and contiguous UV's are correct.
|
||||
*/
|
||||
struct UVSelectValidateInfo_FlushAndContiguous {
|
||||
/** A vertex is selected in edge/face modes without being part of a selected edge/face. */
|
||||
int count_uv_vert_isolated_in_edge_or_face_mode = 0;
|
||||
/** A vertex is selected in face modes without being part of a selected face. */
|
||||
int count_uv_vert_isolated_in_face_mode = 0;
|
||||
/** An edge is selected in face modes without being part of a selected face. */
|
||||
int count_uv_edge_isolated_in_face_mode = 0;
|
||||
};
|
||||
|
||||
struct UVSelectValidateInfo {
|
||||
UVSelectValidateInfo_Sync sync;
|
||||
|
||||
UVSelectValidateInfo_Flush flush;
|
||||
UVSelectValidateInfo_Contiguous contiguous;
|
||||
UVSelectValidateInfo_FlushAndContiguous flush_contiguous;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check the UV selection is valid, mainly for debugging & testing purposes.
|
||||
*
|
||||
* The primary check which should remain valid is: `check_sync`,
|
||||
* if there is ever a selected vertex without any selected UV's or a selected
|
||||
* UV without it's vertex being selected (and similar kinds of issues),
|
||||
* then the selection is out-of-sync, which Blender should *never* allow.
|
||||
*
|
||||
* While an invalid selection should not crash, tools that operate on selection
|
||||
* may behave unpredictably.
|
||||
*
|
||||
* The other checks may be desired or not although this depends more on the situation.
|
||||
*
|
||||
* \param cd_loop_uv_offset: The UV custom-data layer to check.
|
||||
* Ignored when -1 (UV checks wont be used).
|
||||
*
|
||||
* \param check_sync: When true, check the selection is synchronized
|
||||
* between the UV and mesh selection. This should practically always be true,
|
||||
* as it doesn't make sense to check the UV selection if valid otherwise,
|
||||
* unless the UV selection is being set and has not yet been synchronized.
|
||||
* \param check_flush: When true, check the selection is flushed based on #BMesh::selectmode.
|
||||
* \param check_contiguous: When true, check that UV selection is contiguous.
|
||||
* Note that this is not considered an *error* since users may cause this to happen and
|
||||
* tools are expected to work properly, however some operations are expected to maintain
|
||||
* a contiguous selection. This check is included to ensure those operations are working.
|
||||
*/
|
||||
bool BM_mesh_uvselect_is_valid(BMesh *bm,
|
||||
int cd_loop_uv_offset,
|
||||
bool check_sync,
|
||||
bool check_flush,
|
||||
bool check_contiguous,
|
||||
UVSelectValidateInfo *info);
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
203
blender-5.2.0/source/blender/bmesh/intern/bmesh_walkers.cc
Normal file
203
blender-5.2.0/source/blender/bmesh/intern/bmesh_walkers.cc
Normal file
@@ -0,0 +1,203 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* BMesh Walker API.
|
||||
*
|
||||
* NOTE(@joeedh): Details on design.
|
||||
*
|
||||
* Original design: walkers directly emulation recursive functions.
|
||||
* functions save their state onto a #BMWalker.worklist, and also add new states
|
||||
* to implement recursive or looping behavior.
|
||||
* Generally only one state push per call with a specific state is desired.
|
||||
*
|
||||
* basic design pattern: the walker step function goes through its
|
||||
* list of possible choices for recursion, and recurses (by pushing a new state)
|
||||
* using the first non-visited one. This choice is the flagged as visited using the #GHash.
|
||||
* Each step may push multiple new states onto the #BMWalker.worklist at once.
|
||||
*
|
||||
* - Walkers use tool flags, not header flags.
|
||||
* - Walkers now use #GHash for storing visited elements,
|
||||
* rather than stealing flags. #GHash can be rewritten
|
||||
* to be faster if necessary, in the far future :) .
|
||||
* - tools should ALWAYS have necessary error handling
|
||||
* for if walkers fail.
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring> /* For `memcpy`. */
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
#include "bmesh_walkers_private.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
void *BMW_begin(BMWalker *walker, void *start)
|
||||
{
|
||||
BLI_assert(((BMHeader *)start)->htype & walker->begin_htype);
|
||||
|
||||
walker->begin(walker, start);
|
||||
|
||||
return BMW_current_state(walker) ? walker->step(walker) : nullptr;
|
||||
}
|
||||
|
||||
void BMW_init(BMWalker *walker,
|
||||
BMesh *bm,
|
||||
int type,
|
||||
short mask_vert,
|
||||
short mask_edge,
|
||||
short mask_face,
|
||||
BMWFlag flag,
|
||||
int layer,
|
||||
BMWDelimitFlag delimit)
|
||||
{
|
||||
memset(walker, 0, sizeof(BMWalker));
|
||||
|
||||
walker->layer = layer;
|
||||
walker->flag = flag;
|
||||
walker->delimit = delimit;
|
||||
walker->bm = bm;
|
||||
|
||||
walker->mask_vert = mask_vert;
|
||||
walker->mask_edge = mask_edge;
|
||||
walker->mask_face = mask_face;
|
||||
|
||||
walker->visit_set = MEM_new<Set<const void *>>("bmesh walkers");
|
||||
walker->visit_set_alt = MEM_new<Set<const void *>>("bmesh walkers sec");
|
||||
|
||||
if (UNLIKELY(type >= BMW_MAXWALKERS || type < 0)) {
|
||||
fprintf(stderr,
|
||||
"%s: Invalid walker type in BMW_init; type: %d, "
|
||||
"searchmask: (v:%d, e:%d, f:%d), flag: %d, layer: %d\n",
|
||||
__func__,
|
||||
type,
|
||||
mask_vert,
|
||||
mask_edge,
|
||||
mask_face,
|
||||
flag,
|
||||
layer);
|
||||
BLI_assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type != BMW_CUSTOM) {
|
||||
walker->begin_htype = bm_walker_types[type]->begin_htype;
|
||||
walker->begin = bm_walker_types[type]->begin;
|
||||
walker->yield = bm_walker_types[type]->yield;
|
||||
walker->step = bm_walker_types[type]->step;
|
||||
walker->structsize = bm_walker_types[type]->structsize;
|
||||
walker->order = bm_walker_types[type]->order;
|
||||
walker->valid_mask = bm_walker_types[type]->valid_mask;
|
||||
walker->delimit_supported = bm_walker_types[type]->delimit_supported;
|
||||
|
||||
/* safety checks */
|
||||
/* if this raises an error either the caller is wrong or
|
||||
* 'bm_walker_types' needs updating */
|
||||
BLI_assert(mask_vert == 0 || (walker->valid_mask & BM_VERT));
|
||||
BLI_assert(mask_edge == 0 || (walker->valid_mask & BM_EDGE));
|
||||
BLI_assert(mask_face == 0 || (walker->valid_mask & BM_FACE));
|
||||
BLI_assert((delimit & ~walker->delimit_supported) == 0);
|
||||
}
|
||||
|
||||
walker->worklist = BLI_mempool_create(walker->structsize, 0, 128, BLI_MEMPOOL_NOP);
|
||||
walker->states.clear_no_delete();
|
||||
}
|
||||
|
||||
void BMW_end(BMWalker *walker)
|
||||
{
|
||||
BLI_mempool_destroy(walker->worklist);
|
||||
MEM_delete(walker->visit_set);
|
||||
MEM_delete(walker->visit_set_alt);
|
||||
}
|
||||
|
||||
void *BMW_step(BMWalker *walker)
|
||||
{
|
||||
BMHeader *head;
|
||||
|
||||
head = static_cast<BMHeader *>(BMW_walk(walker));
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
int BMW_current_depth(BMWalker *walker)
|
||||
{
|
||||
return walker->depth;
|
||||
}
|
||||
|
||||
void *BMW_walk(BMWalker *walker)
|
||||
{
|
||||
void *current = nullptr;
|
||||
|
||||
while (BMW_current_state(walker)) {
|
||||
current = walker->step(walker);
|
||||
if (current) {
|
||||
return current;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *BMW_current_state(BMWalker *walker)
|
||||
{
|
||||
BMwGenericWalker *currentstate = static_cast<BMwGenericWalker *>(walker->states.first);
|
||||
if (currentstate) {
|
||||
/* Automatic update of depth. For most walkers that
|
||||
* follow the standard "Step" pattern of:
|
||||
* - read current state
|
||||
* - remove current state
|
||||
* - push new states
|
||||
* - return walk result from just-removed current state
|
||||
* this simple automatic update should keep track of depth
|
||||
* just fine. Walkers that deviate from that pattern may
|
||||
* need to manually update the depth if they care about
|
||||
* keeping it correct. */
|
||||
walker->depth = currentstate->depth + 1;
|
||||
}
|
||||
return currentstate;
|
||||
}
|
||||
|
||||
void BMW_state_remove(BMWalker *walker)
|
||||
{
|
||||
void *oldstate;
|
||||
oldstate = BMW_current_state(walker);
|
||||
BLI_remlink(&walker->states, oldstate);
|
||||
BLI_mempool_free(walker->worklist, oldstate);
|
||||
}
|
||||
|
||||
void *BMW_state_add(BMWalker *walker)
|
||||
{
|
||||
BMwGenericWalker *newstate;
|
||||
newstate = static_cast<BMwGenericWalker *>(BLI_mempool_alloc(walker->worklist));
|
||||
newstate->depth = walker->depth;
|
||||
switch (walker->order) {
|
||||
case BMW_DEPTH_FIRST:
|
||||
BLI_addhead(&walker->states, newstate);
|
||||
break;
|
||||
case BMW_BREADTH_FIRST:
|
||||
BLI_addtail(&walker->states, newstate);
|
||||
break;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
break;
|
||||
}
|
||||
return newstate;
|
||||
}
|
||||
|
||||
void BMW_reset(BMWalker *walker)
|
||||
{
|
||||
while (BMW_current_state(walker)) {
|
||||
BMW_state_remove(walker);
|
||||
}
|
||||
walker->depth = 0;
|
||||
walker->visit_set->clear();
|
||||
walker->visit_set_alt->clear();
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
197
blender-5.2.0/source/blender/bmesh/intern/bmesh_walkers.hh
Normal file
197
blender-5.2.0/source/blender/bmesh/intern/bmesh_walkers.hh
Normal file
@@ -0,0 +1,197 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
#include "BLI_set.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/*
|
||||
* NOTE: do NOT modify topology while walking a mesh!
|
||||
*/
|
||||
|
||||
struct BMwGenericWalker;
|
||||
|
||||
enum BMWOrder {
|
||||
BMW_DEPTH_FIRST,
|
||||
BMW_BREADTH_FIRST,
|
||||
};
|
||||
|
||||
enum BMWFlag {
|
||||
BMW_FLAG_NOP = 0,
|
||||
BMW_FLAG_TEST_HIDDEN = (1 << 0),
|
||||
};
|
||||
|
||||
enum BMWDelimitFlag {
|
||||
BMW_DELIMIT_NONE = 0,
|
||||
BMW_DELIMIT_EDGE_LOOP_INNER_CORNERS = 1 << 0,
|
||||
BMW_DELIMIT_EDGE_LOOP_OUTER_CORNERS = 1 << 1,
|
||||
BMW_DELIMIT_EDGE_LOOP_NGONS = 1 << 2,
|
||||
BMW_DELIMIT_EDGE_RING_NGONS = 1 << 3,
|
||||
BMW_DELIMIT_EDGE_MARK_SEAM = 1 << 4,
|
||||
BMW_DELIMIT_EDGE_MARK_SHARP = 1 << 5,
|
||||
BMW_DELIMIT_FACE_MARK_MATERIAL = 1 << 6,
|
||||
};
|
||||
ENUM_OPERATORS(BMWDelimitFlag)
|
||||
|
||||
/*Walkers*/
|
||||
struct BMWalker {
|
||||
char begin_htype; /* only for validating input */
|
||||
void (*begin)(struct BMWalker *walker, void *start);
|
||||
void *(*step)(struct BMWalker *walker);
|
||||
void *(*yield)(struct BMWalker *walker);
|
||||
int structsize;
|
||||
BMWOrder order;
|
||||
int valid_mask;
|
||||
BMWDelimitFlag delimit_supported;
|
||||
|
||||
/* runtime */
|
||||
int layer;
|
||||
|
||||
BMesh *bm;
|
||||
BLI_mempool *worklist;
|
||||
ListBaseT<BMwGenericWalker> states;
|
||||
|
||||
/* these masks are to be tested against elements BMO_elem_flag_test(),
|
||||
* should never be accessed directly only through BMW_init() and bmw_mask_check_*() functions */
|
||||
short mask_vert;
|
||||
short mask_edge;
|
||||
short mask_face;
|
||||
|
||||
BMWFlag flag;
|
||||
BMWDelimitFlag delimit;
|
||||
|
||||
Set<const void *> *visit_set;
|
||||
Set<const void *> *visit_set_alt;
|
||||
int depth;
|
||||
};
|
||||
|
||||
/* define to make BMW_init more clear */
|
||||
#define BMW_MASK_NOP 0
|
||||
|
||||
/**
|
||||
* \brief Initialize Walker
|
||||
*
|
||||
* Allocates and returns a new mesh walker of a given type.
|
||||
* The elements visited are filtered by the bit-mask `searchmask`.
|
||||
*/
|
||||
void BMW_init(struct BMWalker *walker,
|
||||
BMesh *bm,
|
||||
int type,
|
||||
short mask_vert,
|
||||
short mask_edge,
|
||||
short mask_face,
|
||||
BMWFlag flag,
|
||||
int layer,
|
||||
BMWDelimitFlag delimit);
|
||||
void *BMW_begin(BMWalker *walker, void *start);
|
||||
/**
|
||||
* \brief Step Walker
|
||||
*/
|
||||
void *BMW_step(struct BMWalker *walker);
|
||||
/**
|
||||
* \brief End Walker
|
||||
*
|
||||
* Frees a walker's worklist.
|
||||
*/
|
||||
void BMW_end(struct BMWalker *walker);
|
||||
/**
|
||||
* \brief Walker Current Depth
|
||||
*
|
||||
* Returns the current depth of the walker.
|
||||
*/
|
||||
int BMW_current_depth(BMWalker *walker);
|
||||
|
||||
/* These are used by custom walkers. */
|
||||
/**
|
||||
* \brief Current Walker State
|
||||
*
|
||||
* Returns the first state from the walker state
|
||||
* worklist. This state is the next in the
|
||||
* worklist for processing.
|
||||
*/
|
||||
void *BMW_current_state(BMWalker *walker);
|
||||
/**
|
||||
* \brief Add a new Walker State
|
||||
*
|
||||
* Allocate a new empty state and put it on the worklist.
|
||||
* A pointer to the new state is returned so that the caller
|
||||
* can fill in the state data. The new state will be inserted
|
||||
* at the front for depth-first walks, and at the end for
|
||||
* breadth-first walks.
|
||||
*/
|
||||
void *BMW_state_add(BMWalker *walker);
|
||||
/**
|
||||
* \brief Remove Current Walker State
|
||||
*
|
||||
* Remove and free an item from the end of the walker state
|
||||
* worklist.
|
||||
*/
|
||||
void BMW_state_remove(BMWalker *walker);
|
||||
/**
|
||||
* \brief Main Walking Function
|
||||
*
|
||||
* Steps a mesh walker forward by one element
|
||||
*/
|
||||
void *BMW_walk(BMWalker *walker);
|
||||
/**
|
||||
* \brief Reset Walker
|
||||
*
|
||||
* Frees all states from the worklist, resetting the walker
|
||||
* for reuse in a new walk.
|
||||
*/
|
||||
void BMW_reset(BMWalker *walker);
|
||||
|
||||
#define BMW_ITER(ele, walker, data) \
|
||||
for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BMW_begin(walker, (BM_CHECK_TYPE_ELEM(data), data)); ele; \
|
||||
BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BMW_step(walker))
|
||||
|
||||
/*
|
||||
* example of usage, walking over an island of tool flagged faces:
|
||||
*
|
||||
* BMWalker walker;
|
||||
* BMFace *f;
|
||||
*
|
||||
* BMW_init(&walker, bm, BMW_ISLAND, SOME_OP_FLAG);
|
||||
*
|
||||
* for (f = BMW_begin(&walker, some_start_face); f; f = BMW_step(&walker)) {
|
||||
* // do something with f
|
||||
* }
|
||||
* BMW_end(&walker);
|
||||
*/
|
||||
|
||||
enum {
|
||||
BMW_VERT_SHELL,
|
||||
BMW_LOOP_SHELL,
|
||||
BMW_LOOP_SHELL_WIRE,
|
||||
BMW_FACE_SHELL,
|
||||
BMW_EDGELOOP,
|
||||
BMW_FACELOOP,
|
||||
BMW_EDGERING,
|
||||
BMW_EDGEBOUNDARY,
|
||||
BMW_EDGELOOP_NONMANIFOLD,
|
||||
/* BMW_RING, */
|
||||
BMW_LOOPDATA_ISLAND,
|
||||
BMW_ISLANDBOUND,
|
||||
BMW_ISLAND,
|
||||
BMW_ISLAND_MANIFOLD,
|
||||
BMW_CONNECTED_VERTEX,
|
||||
/* End of array index enum values. */
|
||||
|
||||
/* Do not initialize function pointers and struct size in #BMW_init. */
|
||||
BMW_CUSTOM,
|
||||
BMW_MAXWALKERS,
|
||||
};
|
||||
|
||||
/* use with BMW_init, so as not to confuse with restrict flags */
|
||||
#define BMW_NIL_LAY 0
|
||||
|
||||
} // namespace blender
|
||||
2079
blender-5.2.0/source/blender/bmesh/intern/bmesh_walkers_impl.cc
Normal file
2079
blender-5.2.0/source/blender/bmesh/intern/bmesh_walkers_impl.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,99 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* BMesh walker API.
|
||||
*/
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMWalker;
|
||||
|
||||
extern const BMWalker *bm_walker_types[];
|
||||
extern const int bm_totwalkers;
|
||||
|
||||
/* Pointer hiding */
|
||||
struct BMwGenericWalker {
|
||||
Link link;
|
||||
int depth;
|
||||
};
|
||||
|
||||
struct BMwShellWalker {
|
||||
BMwGenericWalker header;
|
||||
BMEdge *curedge;
|
||||
};
|
||||
|
||||
struct BMwLoopShellWalker {
|
||||
BMwGenericWalker header;
|
||||
BMLoop *curloop;
|
||||
};
|
||||
|
||||
struct BMwLoopShellWireWalker {
|
||||
BMwGenericWalker header;
|
||||
BMElem *curelem;
|
||||
};
|
||||
|
||||
struct BMwIslandboundWalker {
|
||||
BMwGenericWalker header;
|
||||
BMLoop *base;
|
||||
BMVert *lastv;
|
||||
BMLoop *curloop;
|
||||
};
|
||||
|
||||
struct BMwIslandWalker {
|
||||
BMwGenericWalker header;
|
||||
BMFace *cur;
|
||||
};
|
||||
|
||||
struct BMwEdgeLoopWalker {
|
||||
BMwGenericWalker header;
|
||||
BMEdge *cur, *start;
|
||||
BMVert *lastv, *startv;
|
||||
BMFace *f_hub;
|
||||
bool is_boundary; /* boundary looping changes behavior */
|
||||
bool is_single; /* single means the edge verts are only connected to 1 face */
|
||||
};
|
||||
|
||||
struct BMwFaceLoopWalker {
|
||||
BMwGenericWalker header;
|
||||
BMLoop *l;
|
||||
bool no_calc;
|
||||
};
|
||||
|
||||
struct BMwEdgeringWalker {
|
||||
BMwGenericWalker header;
|
||||
BMLoop *l;
|
||||
BMEdge *wireedge;
|
||||
bool no_calc;
|
||||
};
|
||||
|
||||
struct BMwEdgeboundaryWalker {
|
||||
BMwGenericWalker header;
|
||||
BMEdge *e;
|
||||
};
|
||||
|
||||
struct BMwNonManifoldEdgeLoopWalker {
|
||||
BMwGenericWalker header;
|
||||
BMEdge *start, *cur;
|
||||
BMVert *startv, *lastv;
|
||||
int face_count; /* face count around the edge. */
|
||||
};
|
||||
|
||||
struct BMwUVEdgeWalker {
|
||||
BMwGenericWalker header;
|
||||
BMLoop *l;
|
||||
};
|
||||
|
||||
struct BMwConnectedVertexWalker {
|
||||
BMwGenericWalker header;
|
||||
BMVert *curvert;
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
Reference in New Issue
Block a user