2012-02-19 18:31:04 +00:00
|
|
|
/*
|
|
|
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
|
*
|
|
|
|
|
* Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton
|
|
|
|
|
*
|
|
|
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/** \file blender/bmesh/intern/bmesh_mods.c
|
|
|
|
|
* \ingroup bmesh
|
|
|
|
|
*
|
|
|
|
|
* This file contains functions for locally modifying
|
|
|
|
|
* the topology of existing mesh data. (split, join, flip etc).
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "BLI_math.h"
|
|
|
|
|
#include "BLI_array.h"
|
|
|
|
|
#include "BLI_smallhash.h"
|
|
|
|
|
|
|
|
|
|
#include "BKE_customdata.h"
|
|
|
|
|
|
|
|
|
|
#include "bmesh.h"
|
|
|
|
|
#include "bmesh_private.h"
|
|
|
|
|
|
|
|
|
|
/**
|
2012-02-29 06:55:10 +00:00
|
|
|
* \brief Dissolve Vert
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* Turns the face region surrounding a manifold vertex into a single polygon.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
|
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \par Example:
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* |=========| |=========|
|
|
|
|
|
* | \ / | | |
|
|
|
|
|
* Before: | V | After: | |
|
|
|
|
|
* | / \ | | |
|
|
|
|
|
* |=========| |=========|
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
|
|
|
|
*
|
2012-02-28 18:28:30 +00:00
|
|
|
* \note dissolves vert, in more situations then BM_disk_dissolve
|
|
|
|
|
* (e.g. if the vert is part of a wire edge, etc).
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
|
|
|
|
int BM_vert_dissolve(BMesh *bm, BMVert *v)
|
|
|
|
|
{
|
2012-02-25 23:41:31 +00:00
|
|
|
const int len = BM_vert_edge_count(v);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
if (len == 1) {
|
2012-02-25 23:41:31 +00:00
|
|
|
BM_vert_kill(bm, v); /* will kill edges too */
|
2012-02-19 18:31:04 +00:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
2012-02-25 23:41:31 +00:00
|
|
|
else if (!BM_vert_is_manifold(bm, v)) {
|
|
|
|
|
if (!v->e) {
|
|
|
|
|
BM_vert_kill(bm, v);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
else if (!v->e->l) {
|
2012-02-25 23:29:12 +00:00
|
|
|
if (len == 2) {
|
2012-02-26 05:48:12 +00:00
|
|
|
return (BM_vert_collapse_edge(bm, v->e, v, TRUE) != NULL);
|
2012-02-25 23:29:12 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2012-02-26 00:43:47 +00:00
|
|
|
/* used to kill the vertex here, but it may be connected to faces.
|
|
|
|
|
* so better do nothing */
|
|
|
|
|
return FALSE;
|
2012-02-25 23:29:12 +00:00
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-02-26 00:43:47 +00:00
|
|
|
else if (len == 2 && BM_vert_face_count(v) == 1) {
|
|
|
|
|
/* boundry vertex on a face */
|
2012-02-26 05:48:12 +00:00
|
|
|
return (BM_vert_collapse_edge(bm, v->e, v, TRUE) != NULL);
|
2012-02-26 00:43:47 +00:00
|
|
|
}
|
2012-02-25 23:41:31 +00:00
|
|
|
else {
|
|
|
|
|
return BM_disk_dissolve(bm, v);
|
|
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2012-02-28 18:28:30 +00:00
|
|
|
/**
|
|
|
|
|
* dissolves all faces around a vert, and removes it.
|
|
|
|
|
*/
|
2012-02-19 18:31:04 +00:00
|
|
|
int BM_disk_dissolve(BMesh *bm, BMVert *v)
|
|
|
|
|
{
|
|
|
|
|
BMFace *f, *f2;
|
|
|
|
|
BMEdge *e, *keepedge = NULL, *baseedge = NULL;
|
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
|
|
if (!BM_vert_is_manifold(bm, v)) {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (v->e) {
|
|
|
|
|
/* v->e we keep, what else */
|
|
|
|
|
e = v->e;
|
|
|
|
|
do {
|
2012-02-28 19:30:44 +00:00
|
|
|
e = bmesh_disk_edge_next(e, v);
|
2012-02-26 19:46:12 +00:00
|
|
|
if (!(BM_edge_share_face_count(e, v->e))) {
|
2012-02-19 18:31:04 +00:00
|
|
|
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 == NULL && len == 3) {
|
|
|
|
|
/* handle specific case for three-valence. solve it by
|
|
|
|
|
* increasing valence to four. this may be hackish. . */
|
|
|
|
|
BMLoop *loop = e->l;
|
|
|
|
|
if (loop->v == v) loop = loop->next;
|
|
|
|
|
if (!BM_face_split(bm, loop->f, v, loop->v, NULL, NULL))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (!BM_disk_dissolve(bm, v)) {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
else if (keepedge == NULL && len == 2) {
|
|
|
|
|
/* collapse the verte */
|
2012-02-26 05:48:12 +00:00
|
|
|
e = BM_vert_collapse_faces(bm, v->e, v, 1.0, TRUE, TRUE);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
if (!e) {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* handle two-valenc */
|
|
|
|
|
f = e->l->f;
|
|
|
|
|
f2 = e->l->radial_next->f;
|
|
|
|
|
|
|
|
|
|
if (f != f2 && !BM_faces_join_pair(bm, f, f2, e)) {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (keepedge) {
|
|
|
|
|
int done = 0;
|
|
|
|
|
|
|
|
|
|
while (!done) {
|
|
|
|
|
done = 1;
|
|
|
|
|
e = v->e;
|
|
|
|
|
do {
|
|
|
|
|
f = NULL;
|
|
|
|
|
len = bmesh_radial_length(e->l);
|
|
|
|
|
if (len == 2 && (e != baseedge) && (e != keepedge)) {
|
|
|
|
|
f = BM_faces_join_pair(bm, e->l->f, e->l->radial_next->f, e);
|
|
|
|
|
/* return if couldn't join faces in manifold
|
|
|
|
|
* conditions */
|
|
|
|
|
//!disabled for testing why bad things happen
|
|
|
|
|
if (!f) {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (f) {
|
|
|
|
|
done = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2012-02-28 19:30:44 +00:00
|
|
|
e = bmesh_disk_edge_next(e, v);
|
2012-02-19 18:31:04 +00:00
|
|
|
} while (e != v->e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* collapse the verte */
|
2012-02-26 05:48:12 +00:00
|
|
|
e = BM_vert_collapse_faces(bm, baseedge, v, 1.0, TRUE, TRUE);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
if (!e) {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* get remaining two face */
|
|
|
|
|
f = e->l->f;
|
|
|
|
|
f2 = e->l->radial_next->f;
|
|
|
|
|
|
|
|
|
|
if (f != f2) {
|
|
|
|
|
/* join two remaining face */
|
|
|
|
|
if (!BM_faces_join_pair(bm, f, f2, e)) {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2012-02-29 06:55:10 +00:00
|
|
|
* \brief Faces Join Pair
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-03-01 12:20:18 +00:00
|
|
|
* Joins two adjacent faces togather.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* Because this method calls to #BM_faces_join to do its work, if a pair
|
|
|
|
|
* of faces share multiple edges, the pair of faces will be joined at
|
|
|
|
|
* every edge (not just edge \a e). This part of the functionality might need
|
|
|
|
|
* to be reconsidered.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* If the windings do not match the winding of the new face will follow
|
|
|
|
|
* \a f1's winding (i.e. \a f2 will be reversed before the join).
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \return pointer to the combined face
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
BMFace *BM_faces_join_pair(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e)
|
|
|
|
|
{
|
|
|
|
|
BMLoop *l1, *l2;
|
|
|
|
|
BMEdge *jed = NULL;
|
|
|
|
|
BMFace *faces[2] = {f1, f2};
|
|
|
|
|
|
|
|
|
|
jed = e;
|
|
|
|
|
if (!jed) {
|
|
|
|
|
BMLoop *l_first;
|
|
|
|
|
/* search for an edge that has both these faces in its radial cycl */
|
|
|
|
|
l1 = l_first = BM_FACE_FIRST_LOOP(f1);
|
|
|
|
|
do {
|
|
|
|
|
if (l1->radial_next->f == f2) {
|
|
|
|
|
jed = l1->e;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} while ((l1 = l1->next) != l_first);
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-26 16:39:21 +00:00
|
|
|
if (UNLIKELY(!jed)) {
|
|
|
|
|
BMESH_ASSERT(0);
|
2012-02-19 18:31:04 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l1 = jed->l;
|
|
|
|
|
|
2012-02-26 16:39:21 +00:00
|
|
|
if (UNLIKELY(!l1)) {
|
|
|
|
|
BMESH_ASSERT(0);
|
2012-02-19 18:31:04 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l2 = l1->radial_next;
|
|
|
|
|
if (l1->v == l2->v) {
|
|
|
|
|
bmesh_loop_reverse(bm, f2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
f1 = BM_faces_join(bm, faces, 2);
|
|
|
|
|
|
|
|
|
|
return f1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-28 07:42:48 +00:00
|
|
|
/**
|
2012-02-29 06:55:10 +00:00
|
|
|
* \brief Connect Verts, Split Face
|
|
|
|
|
*
|
2012-02-28 07:42:48 +00:00
|
|
|
* connects two verts together, automatically (if very naively) finding the
|
2012-02-29 06:55:10 +00:00
|
|
|
* face they both share (if there is one) and splittling it. Use this at your
|
2012-02-19 18:31:04 +00:00
|
|
|
* own risk, as it doesn't handle the many complex cases it should (like zero-area faces,
|
|
|
|
|
* multiple faces, etc).
|
|
|
|
|
*
|
|
|
|
|
* this is really only meant for cases where you don't know before hand the face
|
|
|
|
|
* the two verts belong to for splitting (e.g. the subdivision operator).
|
2012-02-29 06:55:10 +00:00
|
|
|
*
|
|
|
|
|
* \return The newly created edge.
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
|
|
|
|
|
2012-02-28 07:42:48 +00:00
|
|
|
BMEdge *BM_verts_connect(BMesh *bm, BMVert *v1, BMVert *v2, BMFace **r_f)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
2012-02-28 08:17:53 +00:00
|
|
|
BMIter fiter;
|
|
|
|
|
BMIter viter;
|
|
|
|
|
BMVert *v_iter;
|
|
|
|
|
BMFace *f_iter;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-02-23 09:26:53 +00:00
|
|
|
/* be warned: this can do weird things in some ngon situation, see BM_face_legal_splits */
|
2012-02-28 08:17:53 +00:00
|
|
|
BM_ITER(f_iter, &fiter, bm, BM_FACES_OF_VERT, v1) {
|
|
|
|
|
BM_ITER(v_iter, &viter, bm, BM_FACES_OF_VERT, f_iter) {
|
|
|
|
|
if (v_iter == v2) {
|
|
|
|
|
BMLoop *nl;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-02-28 08:17:53 +00:00
|
|
|
f_iter = BM_face_split(bm, f_iter, v1, v2, &nl, NULL);
|
|
|
|
|
|
|
|
|
|
if (r_f) {
|
|
|
|
|
*r_f = f_iter;
|
|
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
return nl->e;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-29 06:55:10 +00:00
|
|
|
if (r_f) {
|
|
|
|
|
*r_f = NULL;
|
|
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2012-02-29 06:55:10 +00:00
|
|
|
* \brief Face Split
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* 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.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \param bm The bmesh
|
|
|
|
|
* \param f the original face
|
|
|
|
|
* \param v1, v2 vertices which define the split edge, must be different
|
|
|
|
|
* \param r_l pointer which will receive the BMLoop for the split edge in the new face
|
|
|
|
|
* \param example Face used to initialize settings
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-28 07:42:48 +00:00
|
|
|
* \return Pointer to the newly created face representing one side of the split
|
|
|
|
|
* if the split is successful (and the original original face will be the
|
|
|
|
|
* other side). NULL if the split fails.
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
2012-02-28 07:42:48 +00:00
|
|
|
BMFace *BM_face_split(BMesh *bm, BMFace *f, BMVert *v1, BMVert *v2, BMLoop **r_l, BMEdge *example)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
const int has_mdisp = CustomData_has_layer(&bm->ldata, CD_MDISPS);
|
|
|
|
|
BMFace *nf, *of;
|
2012-02-23 09:26:53 +00:00
|
|
|
|
|
|
|
|
BLI_assert(v1 != v2);
|
|
|
|
|
|
2012-02-19 18:31:04 +00:00
|
|
|
/* do we have a multires layer */
|
|
|
|
|
if (has_mdisp) {
|
2012-02-23 04:26:24 +00:00
|
|
|
of = BM_face_copy(bm, f, FALSE, FALSE);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
2012-02-28 07:42:48 +00:00
|
|
|
nf = bmesh_sfme(bm, f, v1, v2, r_l, NULL, example);
|
2012-02-19 18:31:04 +00:00
|
|
|
#else
|
2012-02-28 07:42:48 +00:00
|
|
|
nf = bmesh_sfme(bm, f, v1, v2, r_l, example);
|
2012-02-19 18:31:04 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (nf) {
|
|
|
|
|
BM_elem_attrs_copy(bm, bm, f, nf);
|
|
|
|
|
copy_v3_v3(nf->no, f->no);
|
|
|
|
|
|
|
|
|
|
/* handle multires update */
|
|
|
|
|
if (has_mdisp && (nf != f)) {
|
|
|
|
|
BMLoop *l_iter;
|
|
|
|
|
BMLoop *l_first;
|
|
|
|
|
|
|
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
|
|
|
|
do {
|
|
|
|
|
BM_loop_interp_from_face(bm, l_iter, of, FALSE, TRUE);
|
|
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
|
|
|
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(nf);
|
|
|
|
|
do {
|
|
|
|
|
BM_loop_interp_from_face(bm, l_iter, of, FALSE, TRUE);
|
|
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
|
|
|
|
|
|
BM_face_kill(bm, of);
|
|
|
|
|
|
2012-02-21 17:23:32 +00:00
|
|
|
#if 0
|
|
|
|
|
/* BM_face_multires_bounds_smooth doesn't flip displacement correct */
|
2012-02-19 18:31:04 +00:00
|
|
|
BM_face_multires_bounds_smooth(bm, f);
|
|
|
|
|
BM_face_multires_bounds_smooth(bm, nf);
|
2012-02-21 17:23:32 +00:00
|
|
|
#endif
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2012-02-29 06:55:10 +00:00
|
|
|
* \brief Vert Collapse Faces
|
|
|
|
|
*
|
|
|
|
|
* Collapses vertex \a kv that has only two manifold edges
|
|
|
|
|
* onto a vertex it shares an edge with.
|
|
|
|
|
* \a fac defines the amount of interpolation for Custom Data.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-28 07:42:48 +00:00
|
|
|
* \note that this is not a general edge collapse function.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \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.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
|
|
|
|
* BMESH_TODO:
|
|
|
|
|
* Insert error checking for KV valance.
|
|
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \param bm The bmesh
|
|
|
|
|
* \param ke The edge to collapse
|
|
|
|
|
* \param kv The vertex to collapse into the edge
|
2012-02-28 07:42:48 +00:00
|
|
|
* \param fac The factor along the edge
|
|
|
|
|
* \param join_faces When true the faces around the vertex will be joined
|
2012-02-19 18:31:04 +00:00
|
|
|
* otherwise collapse the vertex by merging the 2 edges this vert touches into one.
|
2012-02-28 07:42:48 +00:00
|
|
|
* \param kill_degenerate_faces Removes faces with less than 3 verts after collapsing.
|
2012-02-29 06:55:10 +00:00
|
|
|
*
|
2012-02-28 07:42:48 +00:00
|
|
|
* \returns The New Edge
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
2012-02-26 05:48:12 +00:00
|
|
|
BMEdge *BM_vert_collapse_faces(BMesh *bm, BMEdge *ke, BMVert *kv, float fac,
|
|
|
|
|
const short join_faces, const short kill_degenerate_faces)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
BMEdge *ne = NULL;
|
2012-02-27 13:47:53 +00:00
|
|
|
BMVert *tv = bmesh_edge_other_vert_get(ke, kv);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
BMEdge *e2;
|
|
|
|
|
BMVert *tv2;
|
|
|
|
|
|
|
|
|
|
BMIter iter;
|
|
|
|
|
BMLoop *l_iter = NULL, *kvloop = NULL, *tvloop = NULL;
|
|
|
|
|
|
|
|
|
|
void *src[2];
|
|
|
|
|
float w[2];
|
|
|
|
|
|
|
|
|
|
/* Only intended to be called for 2-valence vertices */
|
|
|
|
|
BLI_assert(bmesh_disk_count(kv) <= 2);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* first modify the face loop data */
|
|
|
|
|
w[0] = 1.0f - fac;
|
|
|
|
|
w[1] = fac;
|
|
|
|
|
|
|
|
|
|
if (ke->l) {
|
|
|
|
|
l_iter = ke->l;
|
|
|
|
|
do {
|
|
|
|
|
if (l_iter->v == tv && l_iter->next->v == kv) {
|
|
|
|
|
tvloop = l_iter;
|
|
|
|
|
kvloop = l_iter->next;
|
|
|
|
|
|
|
|
|
|
src[0] = kvloop->head.data;
|
|
|
|
|
src[1] = tvloop->head.data;
|
|
|
|
|
CustomData_bmesh_interp(&bm->ldata, src, w, NULL, 2, kvloop->head.data);
|
|
|
|
|
}
|
|
|
|
|
} while ((l_iter = l_iter->radial_next) != ke->l);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* now interpolate the vertex data */
|
|
|
|
|
BM_data_interp_from_verts(bm, kv, tv, kv, fac);
|
|
|
|
|
|
2012-02-28 19:30:44 +00:00
|
|
|
e2 = bmesh_disk_edge_next(ke, kv);
|
2012-02-19 18:31:04 +00:00
|
|
|
tv2 = BM_edge_other_vert(e2, kv);
|
|
|
|
|
|
|
|
|
|
if (join_faces) {
|
2012-02-28 07:42:48 +00:00
|
|
|
BMFace **faces = NULL;
|
|
|
|
|
BMFace *f;
|
2012-02-19 18:31:04 +00:00
|
|
|
BLI_array_staticdeclare(faces, 8);
|
|
|
|
|
|
|
|
|
|
BM_ITER(f, &iter, bm, BM_FACES_OF_VERT, kv) {
|
|
|
|
|
BLI_array_append(faces, f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (BLI_array_count(faces) >= 2) {
|
|
|
|
|
BMFace *f2 = BM_faces_join(bm, faces, BLI_array_count(faces));
|
|
|
|
|
if (f2) {
|
|
|
|
|
BMLoop *nl = NULL;
|
|
|
|
|
if (BM_face_split(bm, f2, tv, tv2, &nl, NULL)) {
|
|
|
|
|
ne = nl->e;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BLI_array_free(faces);
|
|
|
|
|
}
|
2012-02-26 05:48:12 +00:00
|
|
|
else {
|
|
|
|
|
/* single face or no faces */
|
|
|
|
|
/* same as BM_vert_collapse_edge() however we already
|
|
|
|
|
* have vars to perform this operation so dont call. */
|
|
|
|
|
ne = bmesh_jekv(bm, ke, kv, TRUE);
|
|
|
|
|
/* ne = BM_edge_exists(tv, tv2); */ /* same as return above */
|
|
|
|
|
|
|
|
|
|
if (kill_degenerate_faces) {
|
|
|
|
|
BMIter fiter;
|
|
|
|
|
BMFace *f;
|
|
|
|
|
BMVert *verts[2] = {ne->v1, ne->v2};
|
|
|
|
|
int i;
|
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
|
BM_ITER(f, &fiter, bm, BM_FACES_OF_VERT, verts[i]) {
|
|
|
|
|
if (f->len < 3) {
|
|
|
|
|
BM_face_kill(bm, f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
return ne;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2012-02-29 06:55:10 +00:00
|
|
|
* \brief Vert Collapse Faces
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
|
|
|
|
* Collapses a vertex onto another vertex it shares an edge with.
|
|
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \return The New Edge
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
|
|
|
|
|
2012-02-26 05:48:12 +00:00
|
|
|
BMEdge *BM_vert_collapse_edge(BMesh *bm, BMEdge *ke, BMVert *kv,
|
|
|
|
|
const short kill_degenerate_faces)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
2012-02-25 16:49:59 +00:00
|
|
|
/* nice example implementation but we want loops to have their customdata
|
2012-02-19 18:31:04 +00:00
|
|
|
* accounted for */
|
|
|
|
|
#if 0
|
|
|
|
|
BMEdge *ne = NULL;
|
|
|
|
|
|
|
|
|
|
/* Collapse between 2 edges */
|
|
|
|
|
|
|
|
|
|
/* in this case we want to keep all faces and not join them,
|
|
|
|
|
* rather just get rid of the veretex - see bug [#28645] */
|
2012-02-27 13:47:53 +00:00
|
|
|
BMVert *tv = bmesh_edge_other_vert_get(ke, kv);
|
2012-02-19 18:31:04 +00:00
|
|
|
if (tv) {
|
2012-02-27 13:47:53 +00:00
|
|
|
BMEdge *e2 = bmesh_disk_edge_next(ke, kv);
|
2012-02-19 18:31:04 +00:00
|
|
|
if (e2) {
|
|
|
|
|
BMVert *tv2 = BM_edge_other_vert(e2, kv);
|
|
|
|
|
if (tv2) {
|
|
|
|
|
/* only action, other calls here only get the edge to return */
|
2012-02-26 04:38:37 +00:00
|
|
|
ne = bmesh_jekv(bm, ke, kv);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-02-26 04:38:37 +00:00
|
|
|
/* ne = BM_edge_exists(tv, tv2); */ /* same as return above */
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ne;
|
|
|
|
|
#else
|
|
|
|
|
/* with these args faces are never joined, same as above
|
|
|
|
|
* but account for loop customdata */
|
2012-02-26 05:48:12 +00:00
|
|
|
return BM_vert_collapse_faces(bm, ke, kv, 1.0f, FALSE, kill_degenerate_faces);
|
2012-02-19 18:31:04 +00:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef DO_V_INTERP
|
|
|
|
|
|
|
|
|
|
/**
|
2012-02-29 06:55:10 +00:00
|
|
|
* \brief Edge Split
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* Splits an edge. \a v should be one of the vertices in \a e and defines
|
|
|
|
|
* the direction of the splitting operation for interpolation purposes.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \return The new vert
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
2012-02-28 07:42:48 +00:00
|
|
|
BMVert *BM_edge_split(BMesh *bm, BMEdge *e, BMVert *v, BMEdge **r_e, float percent)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
BMVert *nv, *v2;
|
|
|
|
|
BMFace **oldfaces = NULL;
|
2012-02-28 07:42:48 +00:00
|
|
|
BMEdge *e_dummy;
|
2012-02-19 18:31:04 +00:00
|
|
|
BLI_array_staticdeclare(oldfaces, 32);
|
|
|
|
|
SmallHash hash;
|
2012-03-03 14:48:50 +00:00
|
|
|
const int do_mdisp = (e->l && CustomData_has_layer(&bm->ldata, CD_MDISPS));
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
/* we need this for handling multire */
|
2012-02-28 07:42:48 +00:00
|
|
|
if (!r_e) {
|
|
|
|
|
r_e = &e_dummy;
|
|
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
/* do we have a multires layer */
|
2012-03-03 14:48:50 +00:00
|
|
|
if (do_mdisp) {
|
2012-02-19 18:31:04 +00:00
|
|
|
BMLoop *l;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
l = e->l;
|
|
|
|
|
do {
|
|
|
|
|
BLI_array_append(oldfaces, l->f);
|
|
|
|
|
l = l->radial_next;
|
|
|
|
|
} while (l != e->l);
|
|
|
|
|
|
|
|
|
|
/* create a hash so we can differentiate oldfaces from new face */
|
|
|
|
|
BLI_smallhash_init(&hash);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < BLI_array_count(oldfaces); i++) {
|
2012-02-23 04:26:24 +00:00
|
|
|
oldfaces[i] = BM_face_copy(bm, oldfaces[i], TRUE, TRUE);
|
2012-02-19 18:31:04 +00:00
|
|
|
BLI_smallhash_insert(&hash, (intptr_t)oldfaces[i], NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-27 13:47:53 +00:00
|
|
|
v2 = bmesh_edge_other_vert_get(e, v);
|
2012-02-28 07:42:48 +00:00
|
|
|
nv = bmesh_semv(bm, v, e, r_e);
|
2012-03-03 14:48:50 +00:00
|
|
|
|
|
|
|
|
BLI_assert(nv != NULL);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
sub_v3_v3v3(nv->co, v2->co, v->co);
|
|
|
|
|
madd_v3_v3v3fl(nv->co, v->co, nv->co, percent);
|
|
|
|
|
|
2012-02-28 07:42:48 +00:00
|
|
|
if (r_e) {
|
|
|
|
|
(*r_e)->head.hflag = e->head.hflag;
|
|
|
|
|
BM_elem_attrs_copy(bm, bm, e, *r_e);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* v->nv->v2 */
|
|
|
|
|
BM_data_interp_face_vert_edge(bm, v2, v, nv, e, percent);
|
|
|
|
|
BM_data_interp_from_verts(bm, v, v2, nv, percent);
|
|
|
|
|
|
2012-03-03 14:48:50 +00:00
|
|
|
if (do_mdisp) {
|
2012-02-19 18:31:04 +00:00
|
|
|
int i, j;
|
|
|
|
|
|
|
|
|
|
/* interpolate new/changed loop data from copied old face */
|
|
|
|
|
for (j = 0; j < 2; j++) {
|
|
|
|
|
for (i = 0; i < BLI_array_count(oldfaces); i++) {
|
2012-02-28 07:42:48 +00:00
|
|
|
BMEdge *e1 = j ? *r_e : e;
|
2012-02-19 18:31:04 +00:00
|
|
|
BMLoop *l, *l2;
|
|
|
|
|
|
|
|
|
|
l = e1->l;
|
2012-02-26 16:39:21 +00:00
|
|
|
|
|
|
|
|
if (UNLIKELY(!l)) {
|
|
|
|
|
BMESH_ASSERT(0);
|
2012-02-19 18:31:04 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
if (!BLI_smallhash_haskey(&hash, (intptr_t)l->f)) {
|
|
|
|
|
BMLoop *l2_first;
|
|
|
|
|
|
|
|
|
|
l2 = l2_first = BM_FACE_FIRST_LOOP(l->f);
|
|
|
|
|
do {
|
|
|
|
|
BM_loop_interp_multires(bm, l2, oldfaces[i]);
|
|
|
|
|
} while ((l2 = l2->next) != l2_first);
|
|
|
|
|
}
|
|
|
|
|
l = l->radial_next;
|
|
|
|
|
} while (l != e1->l);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* destroy the old face */
|
|
|
|
|
for (i = 0; i < BLI_array_count(oldfaces); i++) {
|
|
|
|
|
BM_face_verts_kill(bm, oldfaces[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* fix boundaries a bit, doesn't work too well quite ye */
|
|
|
|
|
#if 0
|
|
|
|
|
for (j = 0; j < 2; j++) {
|
2012-02-28 07:42:48 +00:00
|
|
|
BMEdge *e1 = j ? *r_e : e;
|
2012-02-19 18:31:04 +00:00
|
|
|
BMLoop *l, *l2;
|
|
|
|
|
|
|
|
|
|
l = e1->l;
|
2012-02-26 16:39:21 +00:00
|
|
|
if (UNLIKELY(!l)) {
|
|
|
|
|
BMESH_ASSERT(0);
|
2012-02-19 18:31:04 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
BM_face_multires_bounds_smooth(bm, l->f);
|
|
|
|
|
l = l->radial_next;
|
|
|
|
|
} while (l != e1->l);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
BLI_array_free(oldfaces);
|
|
|
|
|
BLI_smallhash_release(&hash);
|
|
|
|
|
}
|
2012-03-03 14:48:50 +00:00
|
|
|
|
2012-02-19 18:31:04 +00:00
|
|
|
return nv;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-28 18:28:30 +00:00
|
|
|
/* split an edge multiple times evenly */
|
2012-02-19 18:31:04 +00:00
|
|
|
BMVert *BM_edge_split_n(BMesh *bm, BMEdge *e, int numcuts)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
float percent;
|
|
|
|
|
BMVert *nv = NULL;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < numcuts; i++) {
|
|
|
|
|
percent = 1.0f / (float)(numcuts + 1 - i);
|
2012-02-23 05:17:07 +00:00
|
|
|
nv = BM_edge_split(bm, e, e->v2, NULL, percent);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
return nv;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-28 18:28:30 +00:00
|
|
|
/* checks if a face is valid in the data structure */
|
2012-02-19 18:31:04 +00:00
|
|
|
int BM_face_validate(BMesh *bm, BMFace *face, FILE *err)
|
|
|
|
|
{
|
|
|
|
|
BMIter iter;
|
|
|
|
|
BLI_array_declare(verts);
|
|
|
|
|
BMVert **verts = NULL;
|
|
|
|
|
BMLoop *l;
|
|
|
|
|
int ret = 1, i, j;
|
|
|
|
|
|
|
|
|
|
if (face->len == 2) {
|
|
|
|
|
fprintf(err, "warning: found two-edged face. face ptr: %p\n", face);
|
|
|
|
|
fflush(err);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, face); l; l = BM_iter_step(&iter)) {
|
|
|
|
|
BLI_array_growone(verts);
|
|
|
|
|
verts[BLI_array_count(verts) - 1] = l->v;
|
|
|
|
|
|
|
|
|
|
if (l->e->v1 == l->e->v2) {
|
|
|
|
|
fprintf(err, "Found bmesh edge with identical verts!\n");
|
|
|
|
|
fprintf(err, " edge ptr: %p, vert: %p\n", l->e, l->e->v1);
|
|
|
|
|
fflush(err);
|
|
|
|
|
ret = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < BLI_array_count(verts); i++) {
|
|
|
|
|
for (j = 0; j < BLI_array_count(verts); j++) {
|
|
|
|
|
if (j == i) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (verts[i] == verts[j]) {
|
|
|
|
|
fprintf(err, "Found duplicate verts in bmesh face!\n");
|
|
|
|
|
fprintf(err, " face ptr: %p, vert: %p\n", face, verts[i]);
|
|
|
|
|
fflush(err);
|
|
|
|
|
ret = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BLI_array_free(verts);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-03 12:35:37 +00:00
|
|
|
/**
|
|
|
|
|
* \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.
|
|
|
|
|
*/
|
|
|
|
|
int BM_edge_rotate_check(BMesh *UNUSED(bm), BMEdge *e)
|
|
|
|
|
{
|
|
|
|
|
BMFace *fa, *fb;
|
|
|
|
|
if (BM_edge_face_pair(e, &fa, &fb)) {
|
|
|
|
|
BMLoop *la, *lb;
|
|
|
|
|
|
|
|
|
|
la = BM_face_other_vert_loop(e->v2, fa, e->v1);
|
|
|
|
|
lb = BM_face_other_vert_loop(e->v2, fb, e->v1);
|
|
|
|
|
|
|
|
|
|
/* check that the next vert in both faces isnt the same
|
2012-03-03 14:48:50 +00:00
|
|
|
* (ie - the next edge doesnt share the same faces).
|
2012-03-03 12:35:37 +00:00
|
|
|
* 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(e->v1, fa, e->v2);
|
|
|
|
|
lb = BM_face_other_vert_loop(e->v1, fb, e->v2);
|
|
|
|
|
|
|
|
|
|
if (la->v == lb->v) {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-29 06:55:10 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Rotate Edge
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* Spins an edge topologically,
|
|
|
|
|
* either counter-clockwise or clockwise depending on \a ccw.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \return The spun edge, NULL on error
|
|
|
|
|
* (e.g., if the edge isn't surrounded by exactly two faces).
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \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.
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
|
|
|
|
BMEdge *BM_edge_rotate(BMesh *bm, BMEdge *e, int ccw)
|
|
|
|
|
{
|
|
|
|
|
BMVert *v1, *v2;
|
|
|
|
|
BMLoop *l, *l1, *l2, *nl;
|
|
|
|
|
BMFace *f;
|
|
|
|
|
BMIter liter;
|
|
|
|
|
|
2012-03-03 12:35:37 +00:00
|
|
|
if (!BM_edge_rotate_check(bm, e)) {
|
2012-02-19 18:31:04 +00:00
|
|
|
return NULL;
|
2012-03-03 12:35:37 +00:00
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-03-03 12:35:37 +00:00
|
|
|
v1 = e->v1;
|
|
|
|
|
v2 = e->v2;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
f = BM_faces_join_pair(bm, e->l->f, e->l->radial_next->f, e);
|
|
|
|
|
|
|
|
|
|
if (f == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
|
|
|
|
|
if (l->v == v1)
|
|
|
|
|
l1 = l;
|
|
|
|
|
else if (l->v == v2)
|
|
|
|
|
l2 = l;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ccw) {
|
|
|
|
|
l1 = l1->prev;
|
|
|
|
|
l2 = l2->prev;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
l1 = l1->next;
|
|
|
|
|
l2 = l2->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!BM_face_split(bm, f, l1->v, l2->v, &nl, NULL))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
return nl->e;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-28 18:28:30 +00:00
|
|
|
/* Rip a single face from a vertex fan */
|
2012-02-26 22:38:49 +00:00
|
|
|
BMVert *BM_vert_rip(BMesh *bm, BMFace *sf, BMVert *sv)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
return bmesh_urmv(bm, sf, sv);
|
|
|
|
|
}
|