Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTamito Kajiyama <rd6t-kjym@asahi-net.or.jp>2012-10-20 20:48:48 +0400
committerTamito Kajiyama <rd6t-kjym@asahi-net.or.jp>2012-10-20 20:48:48 +0400
commit55015daa43f0ab45341e316abcf11f23c87b5ebe (patch)
tree3156892b6d807d9ba513d444adb870b0ae358e7a /source/blender/bmesh
parent1fe70c07a008185c4e5925aff2c214c93ff396b7 (diff)
parenta9e2e2279780ec2fb58e6820b9cad95ba03f4cad (diff)
Merged changes in the trunk up to revision 51448.
Conflicts resolved: source/blender/blenkernel/CMakeLists.txt source/blender/blenloader/intern/readfile.c source/blender/editors/mesh/editmesh_tools.c source/blender/makesrna/intern/rna_main_api.c
Diffstat (limited to 'source/blender/bmesh')
-rw-r--r--source/blender/bmesh/CMakeLists.txt4
-rw-r--r--source/blender/bmesh/bmesh.h1
-rw-r--r--source/blender/bmesh/intern/bmesh_core.c54
-rw-r--r--source/blender/bmesh/intern/bmesh_core.h4
-rw-r--r--source/blender/bmesh/intern/bmesh_decimate.c599
-rw-r--r--source/blender/bmesh/intern/bmesh_decimate.h32
-rw-r--r--source/blender/bmesh/intern/bmesh_iterators.c148
-rw-r--r--source/blender/bmesh/intern/bmesh_iterators.h14
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.c2
-rw-r--r--source/blender/bmesh/intern/bmesh_mods.c13
-rw-r--r--source/blender/bmesh/intern/bmesh_opdefines.c34
-rw-r--r--source/blender/bmesh/intern/bmesh_operator_api.h10
-rw-r--r--source/blender/bmesh/intern/bmesh_operators_private.h2
-rw-r--r--source/blender/bmesh/intern/bmesh_queries.c68
-rw-r--r--source/blender/bmesh/intern/bmesh_queries.h3
-rw-r--r--source/blender/bmesh/intern/bmesh_walkers_impl.c4
-rw-r--r--source/blender/bmesh/operators/bmo_bevel.c2
-rw-r--r--source/blender/bmesh/operators/bmo_join_triangles.c13
-rw-r--r--source/blender/bmesh/operators/bmo_symmetrize.c663
-rw-r--r--source/blender/bmesh/operators/bmo_unsubdivide.c348
-rw-r--r--source/blender/bmesh/operators/bmo_utils.c15
21 files changed, 1922 insertions, 111 deletions
diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt
index 4bce7a6ff51..1e56314ab6e 100644
--- a/source/blender/bmesh/CMakeLists.txt
+++ b/source/blender/bmesh/CMakeLists.txt
@@ -52,9 +52,11 @@ set(SRC
operators/bmo_mirror.c
operators/bmo_primitive.c
operators/bmo_removedoubles.c
+ operators/bmo_symmetrize.c
operators/bmo_subdivide.c
operators/bmo_subdivide.h
operators/bmo_triangulate.c
+ operators/bmo_unsubdivide.c
operators/bmo_utils.c
operators/bmo_wireframe.c
@@ -62,6 +64,8 @@ set(SRC
intern/bmesh_construct.h
intern/bmesh_core.c
intern/bmesh_core.h
+ intern/bmesh_decimate.c
+ intern/bmesh_decimate.h
intern/bmesh_inline.h
intern/bmesh_interp.c
intern/bmesh_interp.h
diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h
index 955b1a729c5..a672ec0b6a7 100644
--- a/source/blender/bmesh/bmesh.h
+++ b/source/blender/bmesh/bmesh.h
@@ -252,6 +252,7 @@ extern "C" {
#include "intern/bmesh_construct.h"
#include "intern/bmesh_core.h"
+#include "intern/bmesh_decimate.h"
#include "intern/bmesh_interp.h"
#include "intern/bmesh_iterators.h"
#include "intern/bmesh_marking.h"
diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c
index d50c94d5e6a..4e6decfa913 100644
--- a/source/blender/bmesh/intern/bmesh_core.c
+++ b/source/blender/bmesh/intern/bmesh_core.c
@@ -810,7 +810,7 @@ static int bm_loop_reverse_loop(BMesh *bm, BMFace *f
int bmesh_loop_reverse(BMesh *bm, BMFace *f)
{
#ifdef USE_BMESH_HOLES
- return bmesh_loop_reverse_loop(bm, f, f->loops.first);
+ return bm_loop_reverse_loop(bm, f, f->loops.first);
#else
return bm_loop_reverse_loop(bm, f);
#endif
@@ -1143,6 +1143,8 @@ static BMFace *bm_face_create__sfme(BMesh *bm, BMFace *UNUSED(example))
/**
* \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.
@@ -1186,13 +1188,15 @@ BMFace *bmesh_sfme(BMesh *bm, BMFace *f, BMVert *v1, BMVert *v2,
{
#ifdef USE_BMESH_HOLES
BMLoopList *lst, *lst2;
+#else
+ int first_loop_f1;
#endif
BMFace *f2;
BMLoop *l_iter, *l_first;
BMLoop *v1loop = NULL, *v2loop = NULL, *f1loop = NULL, *f2loop = NULL;
BMEdge *e;
- int i, len, f1len, f2len, first_loop_f1;
+ int i, len, f1len, f2len;
/* verify that v1 and v2 are in face */
len = f->len;
@@ -1603,10 +1607,10 @@ BMEdge *bmesh_jekv(BMesh *bm, BMEdge *ke, BMVert *kv, const short check_edge_dou
BMESH_ASSERT(edok != FALSE);
}
- /* deallocate edg */
+ /* deallocate edge */
bm_kill_only_edge(bm, ke);
- /* deallocate verte */
+ /* deallocate vertex */
bm_kill_only_vert(bm, kv);
/* Validate disk cycle lengths of ov, tv are unchanged */
@@ -1615,7 +1619,7 @@ BMEdge *bmesh_jekv(BMesh *bm, BMEdge *ke, BMVert *kv, const short check_edge_dou
edok = bmesh_disk_validate(valence2, tv->e, tv);
BMESH_ASSERT(edok != FALSE);
- /* Validate loop cycle of all faces attached to oe */
+ /* Validate loop cycle of all faces attached to 'oe' */
for (i = 0, l = oe->l; i < radlen; i++, l = l->radial_next) {
BMESH_ASSERT(l->e == oe);
edok = bmesh_verts_in_edge(l->v, l->next->v, oe);
@@ -1796,8 +1800,12 @@ BMFace *bmesh_jfke(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e)
* Merges two verts into one (\a v into \a vtarget).
*
* \return Success
+ *
+ * \warning This does't work for collapsing edges,
+ * where \a v and \a vtarget are connected by an edge
+ * (assert checks for this case).
*/
-int BM_vert_splice(BMesh *bm, BMVert *v, BMVert *vtarget)
+int BM_vert_splice(BMesh *bm, BMVert *v, BMVert *v_target)
{
BMEdge *e;
@@ -1805,26 +1813,29 @@ int BM_vert_splice(BMesh *bm, BMVert *v, BMVert *vtarget)
int i, loops_tot;
/* verts already spliced */
- if (v == vtarget) {
+ if (v == v_target) {
return FALSE;
}
/* we can't modify the vert while iterating so first allocate an array of loops */
loops = BM_iter_as_arrayN(bm, BM_LOOPS_OF_VERT, v, &loops_tot);
- for (i = 0; i < loops_tot; i++) {
- loops[i]->v = vtarget;
+ if (loops) {
+ for (i = 0; i < loops_tot; i++) {
+ loops[i]->v = v_target;
+ }
+ MEM_freeN(loops);
}
- MEM_freeN(loops);
/* move all the edges from v's disk to vtarget's disk */
while ((e = v->e)) {
bmesh_disk_edge_remove(e, v);
- bmesh_edge_swapverts(e, v, vtarget);
- bmesh_disk_edge_append(e, vtarget);
+ bmesh_edge_swapverts(e, v, v_target);
+ bmesh_disk_edge_append(e, v_target);
+ BLI_assert(e->v1 != e->v2);
}
BM_CHECK_ELEMENT(v);
- BM_CHECK_ELEMENT(vtarget);
+ BM_CHECK_ELEMENT(v_target);
/* v is unused now, and can be killed */
BM_vert_kill(bm, v);
@@ -1990,27 +2001,32 @@ int BM_vert_separate(BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len,
*
* \note Edges must already have the same vertices.
*/
-int BM_edge_splice(BMesh *bm, BMEdge *e, BMEdge *etarget)
+int BM_edge_splice(BMesh *bm, BMEdge *e, BMEdge *e_target)
{
BMLoop *l;
- if (!BM_vert_in_edge(e, etarget->v1) || !BM_vert_in_edge(e, etarget->v2)) {
+ if (!BM_vert_in_edge(e, e_target->v1) || !BM_vert_in_edge(e, e_target->v2)) {
/* not the same vertices can't splice */
+
+ /* the caller should really make sure this doesn't happen ever
+ * so assert on release builds */
+ BLI_assert(0);
+
return FALSE;
}
while (e->l) {
l = e->l;
- BLI_assert(BM_vert_in_edge(etarget, l->v));
- BLI_assert(BM_vert_in_edge(etarget, l->next->v));
+ BLI_assert(BM_vert_in_edge(e_target, l->v));
+ BLI_assert(BM_vert_in_edge(e_target, l->next->v));
bmesh_radial_loop_remove(l, e);
- bmesh_radial_append(etarget, l);
+ bmesh_radial_append(e_target, l);
}
BLI_assert(bmesh_radial_length(e->l) == 0);
BM_CHECK_ELEMENT(e);
- BM_CHECK_ELEMENT(etarget);
+ BM_CHECK_ELEMENT(e_target);
/* removes from disks too */
BM_edge_kill(bm, e);
diff --git a/source/blender/bmesh/intern/bmesh_core.h b/source/blender/bmesh/intern/bmesh_core.h
index 491287993df..0667ed9ea1c 100644
--- a/source/blender/bmesh/intern/bmesh_core.h
+++ b/source/blender/bmesh/intern/bmesh_core.h
@@ -41,8 +41,8 @@ void BM_edge_kill(BMesh *bm, BMEdge *e);
void BM_vert_kill(BMesh *bm, BMVert *v);
int bmesh_edge_separate(BMesh *bm, BMEdge *e, BMLoop *l_sep);
-int BM_edge_splice(BMesh *bm, BMEdge *e, BMEdge *etarget);
-int BM_vert_splice(BMesh *bm, BMVert *v, BMVert *vtarget);
+int BM_edge_splice(BMesh *bm, BMEdge *e, BMEdge *e_target);
+int BM_vert_splice(BMesh *bm, BMVert *v, BMVert *v_target);
int bmesh_vert_separate(BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len);
diff --git a/source/blender/bmesh/intern/bmesh_decimate.c b/source/blender/bmesh/intern/bmesh_decimate.c
new file mode 100644
index 00000000000..519bdba02a9
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_decimate.c
@@ -0,0 +1,599 @@
+/*
+ * ***** 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): Campbell Barton
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/bmesh/intern/bmesh_decimate.c
+ * \ingroup bmesh
+ *
+ * BMesh decimator.
+ */
+
+#include <stddef.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_scene_types.h"
+
+#include "BLI_math.h"
+#include "BLI_quadric.h"
+#include "BLI_heap.h"
+
+#include "bmesh.h"
+#include "bmesh_structure.h"
+#include "bmesh_decimate.h"
+
+/* defines for testing */
+#define USE_CUSTOMDATA
+#define USE_TRIANGULATE
+
+/* these checks are for rare cases that we can't avoid since they are valid meshes still */
+#define USE_SAFETY_CHECKS
+
+#define BOUNDARY_PRESERVE_WEIGHT 100.0f
+
+
+/* BMesh Helper Functions
+ * ********************** */
+
+/**
+ * \param vquadrics must be calloc'd
+ */
+static void bm_decim_build_quadrics(BMesh *bm, Quadric *vquadrics)
+{
+ BMIter iter;
+ BMFace *f;
+ BMEdge *e;
+
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ BMLoop *l_first;
+ BMLoop *l_iter;
+
+ const float *co = BM_FACE_FIRST_LOOP(f)->v->co;
+ const float *no = f->no;
+ const float offset = -dot_v3v3(no, co);
+ Quadric q;
+
+ BLI_quadric_from_v3_dist(&q, no, offset);
+
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ BLI_quadric_add_qu_qu(&vquadrics[BM_elem_index_get(l_iter->v)], &q);
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+
+ /* boundary edges */
+ BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+ if (UNLIKELY(BM_edge_is_boundary(e))) {
+ float edge_vector[3];
+ float edge_cross[3];
+ sub_v3_v3v3(edge_vector, e->v2->co, e->v1->co);
+ f = e->l->f;
+ cross_v3_v3v3(edge_cross, edge_vector, f->no);
+
+ if (normalize_v3(edge_cross) != 0.0f) {
+ Quadric q;
+ BLI_quadric_from_v3_dist(&q, edge_vector, -dot_v3v3(edge_cross, e->v1->co));
+ BLI_quadric_mul(&q, BOUNDARY_PRESERVE_WEIGHT);
+
+ BLI_quadric_add_qu_qu(&vquadrics[BM_elem_index_get(e->v1)], &q);
+ BLI_quadric_add_qu_qu(&vquadrics[BM_elem_index_get(e->v2)], &q);
+ }
+ }
+ }
+}
+
+
+static void bm_decim_calc_target_co(BMEdge *e, float optimize_co[3],
+ const Quadric *vquadrics)
+{
+ /* compute an edge contration target for edge ei
+ * this is computed by summing it's vertices quadrics and
+ * optimizing the result. */
+ Quadric q;
+
+ BLI_quadric_add_qu_ququ(&q,
+ &vquadrics[BM_elem_index_get(e->v1)],
+ &vquadrics[BM_elem_index_get(e->v2)]);
+
+
+ if (BLI_quadric_optimize(&q, optimize_co)) {
+ return; /* all is good */
+ }
+ else {
+ mid_v3_v3v3(optimize_co, e->v1->co, e->v2->co);
+ }
+}
+
+static void bm_decim_build_edge_cost_single(BMEdge *e,
+ const Quadric *vquadrics,
+ Heap *eheap, HeapNode **eheap_table)
+{
+ const Quadric *q1, *q2;
+ float optimize_co[3];
+ float cost;
+
+ if (eheap_table[BM_elem_index_get(e)]) {
+ BLI_heap_remove(eheap, eheap_table[BM_elem_index_get(e)]);
+ }
+
+ /* check we can collapse, some edges we better not touch */
+ if (BM_edge_is_boundary(e)) {
+ if (e->l->f->len == 3) {
+ /* pass */
+ }
+ else {
+ /* only collapse tri's */
+ eheap_table[BM_elem_index_get(e)] = NULL;
+ return;
+ }
+ }
+ else if (BM_edge_is_manifold(e)) {
+ if ((e->l->f->len == 3) && (e->l->radial_next->f->len == 3)) {
+ /* pass */
+ }
+ else {
+ /* only collapse tri's */
+ eheap_table[BM_elem_index_get(e)] = NULL;
+ return;
+ }
+ }
+ else {
+ eheap_table[BM_elem_index_get(e)] = NULL;
+ return;
+ }
+ /* end sanity check */
+
+
+ bm_decim_calc_target_co(e, optimize_co, vquadrics);
+
+ q1 = &vquadrics[BM_elem_index_get(e->v1)];
+ q2 = &vquadrics[BM_elem_index_get(e->v2)];
+
+ cost = (BLI_quadric_evaluate(q1, optimize_co) + BLI_quadric_evaluate(q2, optimize_co));
+
+ eheap_table[BM_elem_index_get(e)] = BLI_heap_insert(eheap, cost, e);
+}
+
+static void bm_decim_build_edge_cost(BMesh *bm,
+ const Quadric *vquadrics,
+ Heap *eheap, HeapNode **eheap_table)
+{
+ BMIter iter;
+ BMEdge *e;
+ unsigned int i;
+
+ BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
+ eheap_table[i] = NULL; /* keep sanity check happy */
+ bm_decim_build_edge_cost_single(e, vquadrics, eheap, eheap_table);
+ }
+}
+
+#ifdef USE_TRIANGULATE
+/* Temp Triangulation
+ * ****************** */
+
+/**
+ * To keep things simple we can only collapse edges on triangulated data
+ * (limitation with edge collapse and error calculation functions).
+ *
+ * But to avoid annoying users by only giving triangle results, we can
+ * triangulate, keeping a reference between the faces, then join after
+ * if the edges don't collapse, this will also allow more choices when
+ * collapsing edges so even has some advantage over decimating quads
+ * directly.
+ *
+ * \return TRUE if any faces were triangulated.
+ */
+
+static int bm_decim_triangulate_begin(BMesh *bm)
+{
+#ifdef USE_SAFETY_CHECKS
+ const int check_double_edges = TRUE;
+#else
+ const int check_double_edges = FALSE;
+#endif
+
+ BMIter iter;
+ BMFace *f;
+ // int has_quad; // could optimize this a little
+ int has_cut = FALSE;
+
+ BLI_assert((bm->elem_index_dirty & BM_VERT) == 0);
+
+ /* first clear loop index values */
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ BMLoop *l_iter;
+ BMLoop *l_first;
+
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ BM_elem_index_set(l_iter, -1);
+ } while ((l_iter = l_iter->next) != l_first);
+
+ // has_quad |= (f->len == 4)
+ }
+
+ /* adding new faces as we loop over faces
+ * is normally best avoided, however in this case its not so bad because any face touched twice
+ * will already be triangulated*/
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ if (f->len == 4) {
+ BMLoop *f_l[4];
+ BMLoop *l_iter;
+ BMLoop *l_a, *l_b;
+
+ l_iter = BM_FACE_FIRST_LOOP(f);
+
+ f_l[0] = l_iter; l_iter = l_iter->next;
+ f_l[1] = l_iter; l_iter = l_iter->next;
+ f_l[2] = l_iter; l_iter = l_iter->next;
+ f_l[3] = l_iter; l_iter = l_iter->next;
+
+ if (len_squared_v3v3(f_l[0]->v->co, f_l[2]->v->co) < len_squared_v3v3(f_l[1]->v->co, f_l[3]->v->co)) {
+ l_a = f_l[0];
+ l_b = f_l[2];
+ }
+ else {
+ l_a = f_l[1];
+ l_b = f_l[3];
+ }
+
+ {
+ BMFace *f_new;
+ BMLoop *l_new;
+
+ /* warning, NO_DOUBLE option here isn't handled as nice as it could be
+ * - if there is a quad that has a free standing edge joining it along
+ * where we want to split the face, there isnt a good way we can handle this.
+ * currently that edge will get removed when joining the tris back into a quad. */
+ f_new = BM_face_split(bm, f, l_a->v, l_b->v, &l_new, NULL, check_double_edges);
+
+ if (f_new) {
+ /* the value of this doesn't matter, only that the 2 loops match and have unique values */
+ const int f_index = BM_elem_index_get(f);
+
+ /* since we just split theres only ever 2 loops */
+ BLI_assert(BM_edge_is_manifold(l_new->e));
+
+ BM_elem_index_set(l_new, f_index);
+ BM_elem_index_set(l_new->radial_next, f_index);
+
+ has_cut = TRUE;
+ }
+ }
+ }
+ }
+
+ BLI_assert((bm->elem_index_dirty & BM_VERT) == 0);
+
+ if (has_cut) {
+ /* now triangulation is done we need to correct index values */
+ BM_mesh_elem_index_ensure(bm, BM_EDGE | BM_FACE);
+ }
+
+ return has_cut;
+}
+
+static void bm_decim_triangulate_end(BMesh *bm)
+{
+ /* decimation finished, now re-join */
+ BMIter iter;
+ BMEdge *e;
+
+ /* boundary edges */
+ BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+ BMLoop *l_a, *l_b;
+ if (BM_edge_loop_pair(e, &l_a, &l_b)) {
+ const int l_a_index = BM_elem_index_get(l_a);
+ if (l_a_index != -1) {
+ const int l_b_index = BM_elem_index_get(l_b);
+ if (l_a_index == l_b_index) {
+ /* highly unlikely to fail, but prevents possible double-ups */
+ if (l_a->f->len == 3 && l_b->f->len == 3) {
+ BMFace *f[2] = {l_a->f, l_b->f};
+ BM_faces_join(bm, f, 2, TRUE);
+ }
+ }
+ }
+ }
+ }
+}
+
+#endif /* USE_TRIANGULATE */
+
+/* Edge Collapse Functions
+ * *********************** */
+
+/**
+ * special, highly limited edge collapse function
+ * intended for speed over flexibiliy.
+ * can only collapse edges connected to (1, 2) tris.
+ *
+ * Important - dont add vert/edge/face data on collapsing!
+ *
+ * \param ke_other let caller know what edges we remove besides \a ke
+ */
+static int bm_edge_collapse(BMesh *bm, BMEdge *ke, BMVert *kv, int ke_other[2],
+#ifdef USE_CUSTOMDATA
+ const float customdata_fac
+#else
+ const float UNUSED(customdata_fac)
+#endif
+ )
+{
+ BMVert *v_other = BM_edge_other_vert(ke, kv);
+
+ BLI_assert(v_other != NULL);
+
+ if (BM_edge_is_manifold(ke)) {
+ BMLoop *l_a, *l_b;
+ BMEdge *e_a_other[2], *e_b_other[2];
+ int ok;
+
+ ok = BM_edge_loop_pair(ke, &l_a, &l_b);
+
+ BLI_assert(ok == TRUE);
+ BLI_assert(l_a->f->len == 3);
+ BLI_assert(l_b->f->len == 3);
+
+ /* keep 'kv' 0th */
+ if (BM_vert_in_edge(l_a->prev->e, kv)) {
+ e_a_other[0] = l_a->prev->e;
+ e_a_other[1] = l_a->next->e;
+ }
+ else {
+ e_a_other[1] = l_a->prev->e;
+ e_a_other[0] = l_a->next->e;
+ }
+
+ if (BM_vert_in_edge(l_b->prev->e, kv)) {
+ e_b_other[0] = l_b->prev->e;
+ e_b_other[1] = l_b->next->e;
+ }
+ else {
+ e_b_other[1] = l_b->prev->e;
+ e_b_other[0] = l_b->next->e;
+ }
+
+ BLI_assert(BM_edge_share_vert(e_a_other[0], e_b_other[0]));
+ BLI_assert(BM_edge_share_vert(e_a_other[1], e_b_other[1]));
+
+ /* we could assert this case, but better just bail out */
+#if 0
+ BLI_assert(e_a_other[0] != e_b_other[0]);
+ BLI_assert(e_a_other[0] != e_b_other[1]);
+ BLI_assert(e_b_other[0] != e_a_other[0]);
+ BLI_assert(e_b_other[0] != e_a_other[1]);
+#endif
+ /* not totally common but we want to avoid */
+ if (ELEM(e_a_other[0], e_b_other[0], e_b_other[1]) ||
+ ELEM(e_a_other[1], e_b_other[0], e_b_other[1]))
+ {
+ return FALSE;
+ }
+
+ ke_other[0] = BM_elem_index_get(e_a_other[0]);
+ ke_other[1] = BM_elem_index_get(e_b_other[0]);
+
+#ifdef USE_CUSTOMDATA
+ /* TODO, loops */
+ // const float w[2] = {customdata_fac, 1.0f - customdata_fac};
+
+ /* before killing, do customdata */
+ BM_data_interp_from_verts(bm, v_other, kv, v_other, customdata_fac);
+#endif
+
+ BM_edge_kill(bm, ke);
+
+ BM_vert_splice(bm, kv, v_other);
+
+ BM_edge_splice(bm, e_a_other[0], e_a_other[1]);
+ BM_edge_splice(bm, e_b_other[0], e_b_other[1]);
+
+ // BM_mesh_validate(bm);
+
+ return TRUE;
+ }
+ else if (BM_edge_is_boundary(ke)) {
+ /* same as above but only one triangle */
+ BMLoop *l_a;
+ BMEdge *e_a_other[2];
+
+ l_a = ke->l;
+
+ BLI_assert(l_a->f->len == 3);
+
+ /* keep 'kv' 0th */
+ if (BM_vert_in_edge(l_a->prev->e, kv)) {
+ e_a_other[0] = l_a->prev->e;
+ e_a_other[1] = l_a->next->e;
+ }
+ else {
+ e_a_other[1] = l_a->prev->e;
+ e_a_other[0] = l_a->next->e;
+ }
+
+ ke_other[0] = BM_elem_index_get(e_a_other[0]);
+ ke_other[1] = -1;
+
+#ifdef USE_CUSTOMDATA
+ /* TODO, loops */
+ // const float w[2] = {customdata_fac, 1.0f - customdata_fac};
+
+ /* before killing, do customdata */
+ BM_data_interp_from_verts(bm, v_other, kv, v_other, customdata_fac);
+#endif
+
+ BM_edge_kill(bm, ke);
+
+ BM_vert_splice(bm, kv, v_other);
+
+ BM_edge_splice(bm, e_a_other[0], e_a_other[1]);
+
+ // BM_mesh_validate(bm);
+
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+
+/* collapse e the edge, removing e->v2 */
+static void bm_decim_edge_collapse(BMesh *bm, BMEdge *e,
+ Quadric *vquadrics,
+ Heap *eheap, HeapNode **eheap_table)
+{
+ int ke_other[2];
+ BMVert *v = e->v1;
+ int kv_index = BM_elem_index_get(e->v2); /* the vert is removed so only store the index */
+ float optimize_co[3];
+ float customdata_fac;
+
+ bm_decim_calc_target_co(e, optimize_co, vquadrics);
+
+ /* use for customdata merging */
+ customdata_fac = line_point_factor_v3(optimize_co, e->v1->co, e->v2->co);
+
+ if (bm_edge_collapse(bm, e, e->v2, ke_other, customdata_fac)) {
+ /* update collapse info */
+ int i;
+
+ e = NULL; /* paranoid safety check */
+
+ copy_v3_v3(v->co, optimize_co);
+
+ /* remove eheap */
+ for (i = 0; i < 2; i++) {
+ /* highly unlikely 'eheap_table[ke_other[i]]' would be NULL, but do for sanity sake */
+ if ((ke_other[i] != -1) && (eheap_table[ke_other[i]] != NULL)) {
+ BLI_heap_remove(eheap, eheap_table[ke_other[i]]);
+ eheap_table[ke_other[i]] = NULL;
+ }
+ }
+
+ /* update vertex quadric, add kept vertex from killed vertex */
+ BLI_quadric_add_qu_qu(&vquadrics[BM_elem_index_get(v)], &vquadrics[kv_index]);
+
+ /* update connected normals */
+ BM_vert_normal_update_all(v);
+
+ /* update error costs and the eheap */
+ if (LIKELY(v->e)) {
+ BMEdge *e_iter;
+ BMEdge *e_first;
+ e_iter = e_first = v->e;
+ do {
+ //BLI_assert(BM_edge_find_double(e_iter) == NULL);
+#ifdef USE_SAFETY_CHECKS
+ /* note! - this check is slow, but we can't avoid it - Campbell */
+ BMEdge *e_double;
+
+ e_double = BM_edge_find_double(e_iter);
+
+ if (UNLIKELY(e_double != NULL)) {
+ int e_index = BM_elem_index_get(e_double);
+ if (BM_edge_splice(bm, e_double, e_iter)) {
+ if (eheap_table[e_index]) {
+ BLI_heap_remove(eheap, eheap_table[e_index]);
+ eheap_table[e_index] = NULL;
+ }
+ }
+ }
+
+ /* if this happens, the e_double check could be put in a while loop,
+ * so as to keep removing doubles while they are found. so far this isnt needed */
+ BLI_assert(BM_edge_find_double(e_iter) == NULL);
+#endif
+
+ bm_decim_build_edge_cost_single(e_iter, vquadrics, eheap, eheap_table);
+ } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first);
+
+ }
+ }
+}
+
+
+/* Main Decimate Function
+ * ********************** */
+
+void BM_mesh_decimate(BMesh *bm, const float factor)
+{
+ Heap *eheap; /* edge heap */
+ HeapNode **eheap_table; /* edge index aligned table pointing to the eheap */
+ Quadric *vquadrics; /* vert index aligned quadrics */
+ int tot_edge_orig;
+ int face_tot_target;
+ int use_triangulate;
+
+
+#ifdef USE_TRIANGULATE
+ /* temp convert quads to triangles */
+ use_triangulate = bm_decim_triangulate_begin(bm);
+#endif
+
+
+ /* alloc vars */
+ vquadrics = MEM_callocN(sizeof(Quadric) * bm->totvert, __func__);
+ eheap = BLI_heap_new_ex(bm->totedge);
+ eheap_table = MEM_callocN(sizeof(HeapNode *) * bm->totedge, __func__);
+ tot_edge_orig = bm->totedge;
+
+
+ /* build initial edge collapse cost data */
+ bm_decim_build_quadrics(bm, vquadrics);
+
+ bm_decim_build_edge_cost(bm, vquadrics, eheap, eheap_table);
+
+ face_tot_target = bm->totface * factor;
+ bm->elem_index_dirty |= BM_FACE | BM_EDGE | BM_VERT;
+
+
+ /* iterative edge collapse and maintain the eheap */
+ while ((bm->totface > face_tot_target) && (BLI_heap_empty(eheap) == FALSE)) {
+ BMEdge *e = BLI_heap_popmin(eheap);
+ BLI_assert(BM_elem_index_get(e) < tot_edge_orig); /* handy to detect corruptions elsewhere */
+
+ /* under normal conditions wont be accessed again,
+ * but NULL just incase so we don't use freed node */
+ eheap_table[BM_elem_index_get(e)] = NULL;
+
+ bm_decim_edge_collapse(bm, e, vquadrics, eheap, eheap_table);
+ }
+
+
+#ifdef USE_TRIANGULATE
+ /* its possible we only had triangles, skip this step in that case */
+ if (LIKELY(use_triangulate)) {
+ /* temp convert quads to triangles */
+ bm_decim_triangulate_end(bm);
+ }
+#endif
+
+ /* free vars */
+ MEM_freeN(vquadrics);
+ MEM_freeN(eheap_table);
+ BLI_heap_free(eheap, NULL);
+
+ /* testing only */
+ // BM_mesh_validate(bm);
+}
diff --git a/source/blender/bmesh/intern/bmesh_decimate.h b/source/blender/bmesh/intern/bmesh_decimate.h
new file mode 100644
index 00000000000..e44aa576bda
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_decimate.h
@@ -0,0 +1,32 @@
+/*
+ * ***** 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): Campbell Barton
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BMESH_DECIMATE_H__
+#define __BMESH_DECIMATE_H__
+
+/** \file blender/bmesh/intern/bmesh_decimate.h
+ * \ingroup bmesh
+ */
+
+void BM_mesh_decimate(BMesh *bm, const float factor);
+
+#endif /* __BMESH_DECIMATE_H__ */
diff --git a/source/blender/bmesh/intern/bmesh_iterators.c b/source/blender/bmesh/intern/bmesh_iterators.c
index 726127fdcad..1cb95d94e9b 100644
--- a/source/blender/bmesh/intern/bmesh_iterators.c
+++ b/source/blender/bmesh/intern/bmesh_iterators.c
@@ -120,6 +120,21 @@ void *BM_iter_as_arrayN(BMesh *bm, const char itype, void *data, int *r_len)
{
BMIter iter;
+ /* we can't rely on coun't 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 = MEM_mallocN(sizeof(ele) * iter.count, __func__);
@@ -190,10 +205,10 @@ int BM_iter_mesh_count_flag(const char itype, BMesh *bm, const char hflag, const
*/
static void init_iterator(BMIter *iter)
{
- iter->firstvert = iter->nextvert = NULL;
- iter->firstedge = iter->nextedge = NULL;
- iter->firstloop = iter->nextloop = NULL;
- iter->firstpoly = iter->nextpoly = NULL;
+// iter->v_first = iter->v_next = NULL; // UNUSED
+ iter->e_first = iter->e_next = NULL;
+ iter->l_first = iter->l_next = NULL;
+// iter->f_first = iter->f_next = NULL; // UNUSED
iter->ldata = NULL;
}
@@ -229,6 +244,7 @@ void *bmiter__vert_of_mesh_step(BMIter *iter)
void bmiter__edge_of_mesh_begin(BMIter *iter)
{
BLI_mempool_iternew(iter->bm->epool, &iter->pooliter);
+ iter->count = iter->bm->totedge; /* */
}
void *bmiter__edge_of_mesh_step(BMIter *iter)
@@ -256,19 +272,19 @@ void bmiter__edge_of_vert_begin(BMIter *iter)
{
init_iterator(iter);
if (iter->vdata->e) {
- iter->firstedge = iter->vdata->e;
- iter->nextedge = iter->vdata->e;
+ iter->e_first = iter->vdata->e;
+ iter->e_next = iter->vdata->e;
}
}
void *bmiter__edge_of_vert_step(BMIter *iter)
{
- BMEdge *current = iter->nextedge;
+ BMEdge *current = iter->e_next;
- if (iter->nextedge)
- iter->nextedge = bmesh_disk_edge_next(iter->nextedge, iter->vdata);
+ if (iter->e_next)
+ iter->e_next = bmesh_disk_edge_next(iter->e_next, iter->vdata);
- if (iter->nextedge == iter->firstedge) iter->nextedge = NULL;
+ if (iter->e_next == iter->e_first) iter->e_next = NULL;
return current;
}
@@ -284,27 +300,27 @@ void bmiter__face_of_vert_begin(BMIter *iter)
if (iter->vdata->e)
iter->count = bmesh_disk_facevert_count(iter->vdata);
if (iter->count) {
- iter->firstedge = bmesh_disk_faceedge_find_first(iter->vdata->e, iter->vdata);
- iter->nextedge = iter->firstedge;
- iter->firstloop = bmesh_radial_faceloop_find_first(iter->firstedge->l, iter->vdata);
- iter->nextloop = iter->firstloop;
+ iter->e_first = bmesh_disk_faceedge_find_first(iter->vdata->e, iter->vdata);
+ iter->e_next = iter->e_first;
+ iter->l_first = bmesh_radial_faceloop_find_first(iter->e_first->l, iter->vdata);
+ iter->l_next = iter->l_first;
}
}
void *bmiter__face_of_vert_step(BMIter *iter)
{
- BMLoop *current = iter->nextloop;
+ BMLoop *current = iter->l_next;
- if (iter->count && iter->nextloop) {
+ if (iter->count && iter->l_next) {
iter->count--;
- iter->nextloop = bmesh_radial_faceloop_find_next(iter->nextloop, iter->vdata);
- if (iter->nextloop == iter->firstloop) {
- iter->nextedge = bmesh_disk_faceedge_find_next(iter->nextedge, iter->vdata);
- iter->firstloop = bmesh_radial_faceloop_find_first(iter->nextedge->l, iter->vdata);
- iter->nextloop = iter->firstloop;
+ 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 (!iter->count) iter->nextloop = NULL;
+ if (!iter->count) iter->l_next = NULL;
return current ? current->f : NULL;
}
@@ -322,27 +338,27 @@ void bmiter__loop_of_vert_begin(BMIter *iter)
if (iter->vdata->e)
iter->count = bmesh_disk_facevert_count(iter->vdata);
if (iter->count) {
- iter->firstedge = bmesh_disk_faceedge_find_first(iter->vdata->e, iter->vdata);
- iter->nextedge = iter->firstedge;
- iter->firstloop = bmesh_radial_faceloop_find_first(iter->firstedge->l, iter->vdata);
- iter->nextloop = iter->firstloop;
+ iter->e_first = bmesh_disk_faceedge_find_first(iter->vdata->e, iter->vdata);
+ iter->e_next = iter->e_first;
+ iter->l_first = bmesh_radial_faceloop_find_first(iter->e_first->l, iter->vdata);
+ iter->l_next = iter->l_first;
}
}
void *bmiter__loop_of_vert_step(BMIter *iter)
{
- BMLoop *current = iter->nextloop;
+ BMLoop *current = iter->l_next;
if (iter->count) {
iter->count--;
- iter->nextloop = bmesh_radial_faceloop_find_next(iter->nextloop, iter->vdata);
- if (iter->nextloop == iter->firstloop) {
- iter->nextedge = bmesh_disk_faceedge_find_next(iter->nextedge, iter->vdata);
- iter->firstloop = bmesh_radial_faceloop_find_first(iter->nextedge->l, iter->vdata);
- iter->nextloop = iter->firstloop;
+ 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 (!iter->count) iter->nextloop = NULL;
+ if (!iter->count) iter->l_next = NULL;
if (current) {
@@ -362,19 +378,19 @@ void bmiter__loops_of_edge_begin(BMIter *iter)
/* note sure why this sets ldata ... */
init_iterator(iter);
- iter->firstloop = iter->nextloop = l;
+ iter->l_first = iter->l_next = l;
}
void *bmiter__loops_of_edge_step(BMIter *iter)
{
- BMLoop *current = iter->nextloop;
+ BMLoop *current = iter->l_next;
- if (iter->nextloop) {
- iter->nextloop = iter->nextloop->radial_next;
+ if (iter->l_next) {
+ iter->l_next = iter->l_next->radial_next;
}
- if (iter->nextloop == iter->firstloop) {
- iter->nextloop = NULL;
+ if (iter->l_next == iter->l_first) {
+ iter->l_next = NULL;
}
if (current) {
@@ -393,23 +409,23 @@ void bmiter__loops_of_loop_begin(BMIter *iter)
/* note sure why this sets ldata ... */
init_iterator(iter);
- iter->firstloop = l;
- iter->nextloop = iter->firstloop->radial_next;
+ iter->l_first = l;
+ iter->l_next = iter->l_first->radial_next;
- if (iter->nextloop == iter->firstloop)
- iter->nextloop = NULL;
+ if (iter->l_next == iter->l_first)
+ iter->l_next = NULL;
}
void *bmiter__loops_of_loop_step(BMIter *iter)
{
- BMLoop *current = iter->nextloop;
+ BMLoop *current = iter->l_next;
- if (iter->nextloop) {
- iter->nextloop = iter->nextloop->radial_next;
+ if (iter->l_next) {
+ iter->l_next = iter->l_next->radial_next;
}
- if (iter->nextloop == iter->firstloop) {
- iter->nextloop = NULL;
+ if (iter->l_next == iter->l_first) {
+ iter->l_next = NULL;
}
if (current) {
@@ -428,20 +444,20 @@ void bmiter__face_of_edge_begin(BMIter *iter)
init_iterator(iter);
if (iter->edata->l) {
- iter->firstloop = iter->edata->l;
- iter->nextloop = iter->edata->l;
+ iter->l_first = iter->edata->l;
+ iter->l_next = iter->edata->l;
}
}
void *bmiter__face_of_edge_step(BMIter *iter)
{
- BMLoop *current = iter->nextloop;
+ BMLoop *current = iter->l_next;
- if (iter->nextloop) {
- iter->nextloop = iter->nextloop->radial_next;
+ if (iter->l_next) {
+ iter->l_next = iter->l_next->radial_next;
}
- if (iter->nextloop == iter->firstloop) iter->nextloop = NULL;
+ if (iter->l_next == iter->l_first) iter->l_next = NULL;
return current ? current->f : NULL;
}
@@ -476,15 +492,15 @@ void *bmiter__vert_of_edge_step(BMIter *iter)
void bmiter__vert_of_face_begin(BMIter *iter)
{
init_iterator(iter);
- iter->firstloop = iter->nextloop = BM_FACE_FIRST_LOOP(iter->pdata);
+ iter->l_first = iter->l_next = BM_FACE_FIRST_LOOP(iter->pdata);
}
void *bmiter__vert_of_face_step(BMIter *iter)
{
- BMLoop *current = iter->nextloop;
+ BMLoop *current = iter->l_next;
- if (iter->nextloop) iter->nextloop = iter->nextloop->next;
- if (iter->nextloop == iter->firstloop) iter->nextloop = NULL;
+ if (iter->l_next) iter->l_next = iter->l_next->next;
+ if (iter->l_next == iter->l_first) iter->l_next = NULL;
return current ? current->v : NULL;
}
@@ -496,15 +512,15 @@ void *bmiter__vert_of_face_step(BMIter *iter)
void bmiter__edge_of_face_begin(BMIter *iter)
{
init_iterator(iter);
- iter->firstloop = iter->nextloop = BM_FACE_FIRST_LOOP(iter->pdata);
+ iter->l_first = iter->l_next = BM_FACE_FIRST_LOOP(iter->pdata);
}
void *bmiter__edge_of_face_step(BMIter *iter)
{
- BMLoop *current = iter->nextloop;
+ BMLoop *current = iter->l_next;
- if (iter->nextloop) iter->nextloop = iter->nextloop->next;
- if (iter->nextloop == iter->firstloop) iter->nextloop = NULL;
+ if (iter->l_next) iter->l_next = iter->l_next->next;
+ if (iter->l_next == iter->l_first) iter->l_next = NULL;
return current ? current->e : NULL;
}
@@ -516,15 +532,15 @@ void *bmiter__edge_of_face_step(BMIter *iter)
void bmiter__loop_of_face_begin(BMIter *iter)
{
init_iterator(iter);
- iter->firstloop = iter->nextloop = BM_FACE_FIRST_LOOP(iter->pdata);
+ iter->l_first = iter->l_next = BM_FACE_FIRST_LOOP(iter->pdata);
}
void *bmiter__loop_of_face_step(BMIter *iter)
{
- BMLoop *current = iter->nextloop;
+ BMLoop *current = iter->l_next;
- if (iter->nextloop) iter->nextloop = iter->nextloop->next;
- if (iter->nextloop == iter->firstloop) iter->nextloop = NULL;
+ if (iter->l_next) iter->l_next = iter->l_next->next;
+ if (iter->l_next == iter->l_first) iter->l_next = NULL;
return current;
}
diff --git a/source/blender/bmesh/intern/bmesh_iterators.h b/source/blender/bmesh/intern/bmesh_iterators.h
index 8d0eeca31ed..3c42b3d610c 100644
--- a/source/blender/bmesh/intern/bmesh_iterators.h
+++ b/source/blender/bmesh/intern/bmesh_iterators.h
@@ -95,23 +95,27 @@ extern const char bm_iter_itype_htype_map[BM_ITYPE_MAX];
for (ele = BM_iter_new(iter, NULL, itype, data), indexvar = 0; ele; ele = BM_iter_step(iter), (indexvar)++)
/* Iterator Structure */
+/* note: some of these vars are not used,
+ * so they have beem commented to save stack space since this struct is used all over */
typedef struct BMIter {
BLI_mempool_iter pooliter;
- BMVert *firstvert, *nextvert, *vdata;
- BMEdge *firstedge, *nextedge, *edata;
- BMLoop *firstloop, *nextloop, *ldata, *l;
- BMFace *firstpoly, *nextpoly, *pdata;
+ BMVert /* *v_first, *v_next, */ *vdata;
+ BMEdge *e_first, *e_next, *edata;
+ BMLoop *l_first, *l_next, *ldata;
+ BMFace /* *f_first, *f_next, */ *pdata;
BMesh *bm;
void (*begin)(struct BMIter *iter);
void *(*step)(struct BMIter *iter);
+ /*
union {
void *p;
int i;
long l;
float f;
} filter;
- int count;
+ */
+ int count; /* note, only some iterators set this, don't rely on it */
char itype;
} BMIter;
diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c
index 0d072da5327..360e2c93de3 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.c
+++ b/source/blender/bmesh/intern/bmesh_mesh.c
@@ -57,7 +57,7 @@ static void bm_mempool_init(BMesh *bm, const BMAllocTemplate *allocsize)
bm_mesh_chunksize_default.totface, BLI_MEMPOOL_ALLOW_ITER);
#ifdef USE_BMESH_HOLES
- bm->looplistpool = BLI_mempool_create(sizeof(BMLoopList), allocsize[3], allocsize[3], FALSE, FALSE);
+ bm->looplistpool = BLI_mempool_create(sizeof(BMLoopList), 512, 512, 0);
#endif
/* allocate one flag pool that we don't get rid of. */
diff --git a/source/blender/bmesh/intern/bmesh_mods.c b/source/blender/bmesh/intern/bmesh_mods.c
index 3195899ef01..91ca7124fc2 100644
--- a/source/blender/bmesh/intern/bmesh_mods.c
+++ b/source/blender/bmesh/intern/bmesh_mods.c
@@ -130,6 +130,7 @@ int BM_disk_dissolve(BMesh *bm, BMVert *v)
/* this code for handling 2 and 3-valence verts
* may be totally bad */
if (keepedge == NULL && len == 3) {
+#if 0
/* handle specific case for three-valence. solve it by
* increasing valence to four. this may be hackish. . */
BMLoop *loop = e->l;
@@ -140,6 +141,13 @@ int BM_disk_dissolve(BMesh *bm, BMVert *v)
if (!BM_disk_dissolve(bm, v)) {
return FALSE;
}
+#else
+ BM_faces_join_pair(bm, e->l->f, e->l->radial_next->f, e, TRUE);
+
+ if (!BM_vert_collapse_faces(bm, v->e, v, 1.0, FALSE, TRUE)) {
+ return FALSE;
+ }
+#endif
return TRUE;
}
else if (keepedge == NULL && len == 2) {
@@ -188,8 +196,9 @@ int BM_disk_dissolve(BMesh *bm, BMVert *v)
} while (e != v->e);
}
- /* collapse the verte */
- e = BM_vert_collapse_faces(bm, baseedge, v, 1.0, TRUE, TRUE);
+ /* 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, !BM_edge_is_boundary(baseedge), TRUE);
if (!e) {
return FALSE;
diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c
index 362157ad71b..407e7caae0f 100644
--- a/source/blender/bmesh/intern/bmesh_opdefines.c
+++ b/source/blender/bmesh/intern/bmesh_opdefines.c
@@ -698,6 +698,15 @@ static BMOpDefine bmo_triangulate_def = {
BMO_OP_FLAG_UNTAN_MULTIRES
};
+static BMOpDefine bmo_unsubdivide_def = {
+ "unsubdivide",
+ {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, /* input vertices */
+ {BMO_OP_SLOT_INT, "iterations"},
+ {0} /* null-terminating sentinel */},
+ bmo_unsubdivide_exec,
+ BMO_OP_FLAG_UNTAN_MULTIRES
+};
+
static BMOpDefine bmo_subdivide_edges_def = {
"subdivide_edges",
{{BMO_OP_SLOT_ELEMENT_BUF, "edges"},
@@ -1182,6 +1191,29 @@ static BMOpDefine bmo_convex_hull_def = {
0
};
+/*
+ * Symmetrize
+ *
+ * Mekes the mesh elements in the "input" slot symmetrical. Unlike
+ * normal mirroring, it only copies in one direction, as specified by
+ * the "direction" slot. The edges and faces that cross the plane of
+ * symmetry are split as needed to enforce symmetry.
+ *
+ * All new vertices, edges, and faces are added to the "geomout" slot.
+ */
+static BMOpDefine bmo_symmetrize_def = {
+ "symmetrize",
+ {{BMO_OP_SLOT_ELEMENT_BUF, "input"},
+ {BMO_OP_SLOT_INT, "direction"},
+
+ /* Outputs */
+ {BMO_OP_SLOT_ELEMENT_BUF, "geomout"},
+
+ {0} /* null-terminating sentinel */},
+ bmo_symmetrize_exec,
+ 0
+};
+
BMOpDefine *opdefines[] = {
&bmo_automerge_def,
&bmo_average_vert_facedata_def,
@@ -1246,10 +1278,12 @@ BMOpDefine *opdefines[] = {
&bmo_split_def,
&bmo_split_edges_def,
&bmo_subdivide_edges_def,
+ &bmo_symmetrize_def,
&bmo_transform_def,
&bmo_translate_def,
&bmo_triangle_fill_def,
&bmo_triangulate_def,
+ &bmo_unsubdivide_def,
&bmo_weld_verts_def,
&bmo_wireframe_def,
diff --git a/source/blender/bmesh/intern/bmesh_operator_api.h b/source/blender/bmesh/intern/bmesh_operator_api.h
index 0674103162c..a2f4cdc8c6a 100644
--- a/source/blender/bmesh/intern/bmesh_operator_api.h
+++ b/source/blender/bmesh/intern/bmesh_operator_api.h
@@ -266,6 +266,16 @@ enum {
DEL_ONLYTAGGED
};
+typedef enum {
+ BMO_SYMMETRIZE_NEGATIVE_X,
+ BMO_SYMMETRIZE_NEGATIVE_Y,
+ BMO_SYMMETRIZE_NEGATIVE_Z,
+
+ BMO_SYMMETRIZE_POSITIVE_X,
+ BMO_SYMMETRIZE_POSITIVE_Y,
+ BMO_SYMMETRIZE_POSITIVE_Z,
+} BMO_SymmDirection;
+
void BMO_op_flag_enable(BMesh *bm, BMOperator *op, const int op_flag);
void BMO_op_flag_disable(BMesh *bm, BMOperator *op, const int op_flag);
diff --git a/source/blender/bmesh/intern/bmesh_operators_private.h b/source/blender/bmesh/intern/bmesh_operators_private.h
index dc1bdaa4689..d6135efe19a 100644
--- a/source/blender/bmesh/intern/bmesh_operators_private.h
+++ b/source/blender/bmesh/intern/bmesh_operators_private.h
@@ -96,10 +96,12 @@ 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_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);
diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c
index d850eb34477..9520b0298d8 100644
--- a/source/blender/bmesh/intern/bmesh_queries.c
+++ b/source/blender/bmesh/intern/bmesh_queries.c
@@ -298,6 +298,14 @@ int BM_edge_in_face(BMFace *f, BMEdge *e)
}
/**
+ * Returns whether or not a given edge is is part of a given loop.
+ */
+int BM_edge_in_loop(BMEdge *e, BMLoop *l)
+{
+ return (l->e == e || l->prev->e == e);
+}
+
+/**
* Returns whether or not two vertices are in
* a given edge
*/
@@ -316,6 +324,44 @@ BMVert *BM_edge_other_vert(BMEdge *e, BMVert *v)
}
/**
+ * 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)
+{
+ BMLoop *l_other;
+
+ BLI_assert(BM_edge_is_manifold(e));
+ BLI_assert(BM_vert_in_edge(e, l->v));
+
+ l_other = (e->l == l) ? l->radial_next : l;
+
+ if (l_other->v == l->v) {
+ /* pass */
+ }
+ else if (l_other->next->v == l->v) {
+ l_other = l_other->next;
+ }
+ else {
+ BLI_assert(0);
+ }
+
+ return l_other;
+}
+
+/**
* 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.
*
@@ -1018,6 +1064,28 @@ BMEdge *BM_edge_exists(BMVert *v1, BMVert *v2)
}
/**
+ * 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)
+{
+ BMVert *v = e->v1;
+ BMVert *v_other = e->v2;
+
+ BMEdge *e_iter;
+
+ e_iter = e;
+ while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e) {
+ if (UNLIKELY(BM_vert_in_edge(e_iter, v_other))) {
+ return e_iter;
+ }
+ }
+
+ return NULL;
+}
+
+/**
* Given a set of vertices \a varr, find out if
* all those vertices overlap an existing face.
*
diff --git a/source/blender/bmesh/intern/bmesh_queries.h b/source/blender/bmesh/intern/bmesh_queries.h
index 36ffc296759..166b8a54f8a 100644
--- a/source/blender/bmesh/intern/bmesh_queries.h
+++ b/source/blender/bmesh/intern/bmesh_queries.h
@@ -31,6 +31,7 @@ int BM_vert_in_face(BMFace *f, BMVert *v);
int BM_verts_in_face(BMesh *bm, BMFace *f, BMVert **varr, int len);
int BM_edge_in_face(BMFace *f, BMEdge *e);
+int BM_edge_in_loop(BMEdge *e, BMLoop *l);
int BM_vert_in_edge(BMEdge *e, BMVert *v);
int BM_verts_in_edge(BMVert *v1, BMVert *v2, BMEdge *e);
@@ -39,6 +40,7 @@ float BM_edge_calc_length(BMEdge *e);
int BM_edge_face_pair(BMEdge *e, BMFace **r_fa, BMFace **r_fb);
int BM_edge_loop_pair(BMEdge *e, BMLoop **r_la, BMLoop **r_lb);
BMVert *BM_edge_other_vert(BMEdge *e, BMVert *v);
+BMLoop *BM_edge_other_loop(BMEdge *e, BMLoop *l);
BMLoop *BM_face_other_edge_loop(BMFace *f, BMEdge *e, BMVert *v);
BMLoop *BM_face_other_vert_loop(BMFace *f, BMVert *v_prev, BMVert *v);
BMLoop *BM_loop_other_vert_loop(BMLoop *l, BMVert *v);
@@ -72,6 +74,7 @@ BMLoop *BM_face_find_shortest_loop(BMFace *f);
BMLoop *BM_face_find_longest_loop(BMFace *f);
BMEdge *BM_edge_exists(BMVert *v1, BMVert *v2);
+BMEdge *BM_edge_find_double(BMEdge *e);
int BM_face_exists_overlap(BMesh *bm, BMVert **varr, int len, BMFace **r_existface);
diff --git a/source/blender/bmesh/intern/bmesh_walkers_impl.c b/source/blender/bmesh/intern/bmesh_walkers_impl.c
index a72bfe47127..225ea6c2ef6 100644
--- a/source/blender/bmesh/intern/bmesh_walkers_impl.c
+++ b/source/blender/bmesh/intern/bmesh_walkers_impl.c
@@ -499,7 +499,7 @@ static void *bmw_LoopWalker_step(BMWalker *walker)
BMEdge *e = lwalk->cur, *nexte = NULL;
BMLoop *l;
BMVert *v;
- int i;
+ int i = 0;
owalk = *lwalk;
BMW_state_remove(walker);
@@ -534,7 +534,7 @@ static void *bmw_LoopWalker_step(BMWalker *walker)
}
else if (l) { /* NORMAL EDGE WITH FACES */
int vert_edge_tot;
- int stopi;
+ int stopi = 0;
v = BM_edge_other_vert(e, lwalk->lastv);
diff --git a/source/blender/bmesh/operators/bmo_bevel.c b/source/blender/bmesh/operators/bmo_bevel.c
index 10a9d511c77..e31df2e4444 100644
--- a/source/blender/bmesh/operators/bmo_bevel.c
+++ b/source/blender/bmesh/operators/bmo_bevel.c
@@ -233,7 +233,7 @@ void bmo_bevel_exec(BMesh *bm, BMOperator *op)
}
#if 0
- //a bit of cleaner code that, alas, doens't work.
+ /* a bit of cleaner code that, alas, doens't work. */
/* build edge tag */
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
if (BMO_elem_flag_test(bm, e->v1, BEVEL_FLAG) || BMO_elem_flag_test(bm, e->v2, BEVEL_FLAG)) {
diff --git a/source/blender/bmesh/operators/bmo_join_triangles.c b/source/blender/bmesh/operators/bmo_join_triangles.c
index 7191aa7a7b6..3dbc0d0a5eb 100644
--- a/source/blender/bmesh/operators/bmo_join_triangles.c
+++ b/source/blender/bmesh/operators/bmo_join_triangles.c
@@ -199,7 +199,7 @@ void bmo_join_triangles_exec(BMesh *bm, BMOperator *op)
{
BMIter iter, liter;
BMOIter siter;
- BMFace *f1, *f2;
+ BMFace *f;
BMLoop *l;
BMEdge *e;
BLI_array_declare(jedges);
@@ -213,15 +213,16 @@ void bmo_join_triangles_exec(BMesh *bm, BMOperator *op)
int i, totedge;
/* flag all edges of all input face */
- BMO_ITER (f1, &siter, bm, op, "faces", BM_FACE) {
- BMO_elem_flag_enable(bm, f1, FACE_INPUT);
- BM_ITER_ELEM (l, &liter, f1, BM_LOOPS_OF_FACE) {
+ BMO_ITER (f, &siter, bm, op, "faces", BM_FACE) {
+ BMO_elem_flag_enable(bm, f, FACE_INPUT);
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
BMO_elem_flag_enable(bm, l->e, EDGE_MARK);
}
}
/* unflag edges that are invalid; e.g. aren't surrounded by triangle */
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+ BMFace *f1, *f2;
if (!BMO_elem_flag_test(bm, e, EDGE_MARK))
continue;
@@ -300,6 +301,8 @@ void bmo_join_triangles_exec(BMesh *bm, BMOperator *op)
}
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+ BMFace *f1, *f2;
+
if (!BMO_elem_flag_test(bm, e, EDGE_CHOSEN))
continue;
@@ -310,6 +313,8 @@ void bmo_join_triangles_exec(BMesh *bm, BMOperator *op)
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
if (BMO_elem_flag_test(bm, e, EDGE_MARK)) {
+ BMFace *f1, *f2;
+
/* ok, this edge wasn't merged, check if it's
* in a 2-tri-pair island, and if so merg */
diff --git a/source/blender/bmesh/operators/bmo_symmetrize.c b/source/blender/bmesh/operators/bmo_symmetrize.c
new file mode 100644
index 00000000000..a428405fb8b
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_symmetrize.c
@@ -0,0 +1,663 @@
+/*
+ * ***** 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): Nicholas Bishop
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_array.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "bmesh.h"
+#include "intern/bmesh_operators_private.h"
+
+enum {
+ SYMM_OUTPUT_GEOM = (1 << 0)
+};
+
+/* Note: don't think there's much need to make these user-adjustable? */
+#define SYMM_AXIS_THRESHOLD 0.00002f
+#define SYMM_VERT_THRESHOLD 0.00002f
+
+typedef enum {
+ /* Coordinate lies on the side being copied from */
+ SYMM_SIDE_KEEP,
+ /* Coordinate lies on the side being copied from and within the
+ * axis threshold */
+ SYMM_SIDE_AXIS,
+ /* Coordinate lies on the side being copied to */
+ SYMM_SIDE_KILL
+} SymmSide;
+
+typedef struct {
+ BMesh *bm;
+ BMOperator *op;
+
+ int axis;
+ BMO_SymmDirection direction;
+
+ /* Maps from input vertices to their mirrors. If the vertex
+ * doesn't have a mirror, it's not in this map. If the vertex is
+ * within the axis threshold, it's mapped to itself. */
+ GHash *vert_symm_map;
+
+ /* Edges that cross the symmetry plane and are asymmetric get
+ * split. This map goes from input edges to output vertices. If an
+ * edge is not split, it's not in this map. */
+ GHash *edge_split_map;
+} Symm;
+
+/* Return which side the coordinate lies on */
+static SymmSide symm_co_side(const Symm *symm,
+ const float *co)
+{
+ float comp = co[symm->axis];
+ if (ELEM3(symm->direction,
+ BMO_SYMMETRIZE_NEGATIVE_X,
+ BMO_SYMMETRIZE_NEGATIVE_Y,
+ BMO_SYMMETRIZE_NEGATIVE_Z))
+ {
+ comp = -comp;
+ }
+
+ if (comp >= 0) {
+ if (comp < SYMM_AXIS_THRESHOLD)
+ return SYMM_SIDE_AXIS;
+ else
+ return SYMM_SIDE_KEEP;
+ }
+ else
+ return SYMM_SIDE_KILL;
+}
+
+/* Output vertices and the vert_map array */
+static void symm_verts_mirror(Symm *symm)
+{
+ BMOIter oiter;
+ BMVert *src_v, *dst_v;
+
+ symm->vert_symm_map = BLI_ghash_ptr_new(AT);
+
+ BMO_ITER (src_v, &oiter, symm->bm, symm->op, "input", BM_VERT) {
+ SymmSide side = symm_co_side(symm, src_v->co);
+ float co[3];
+
+ switch (side) {
+ case SYMM_SIDE_KEEP:
+ /* The vertex is outside the axis area; output its mirror */
+ copy_v3_v3(co, src_v->co);
+ co[symm->axis] = -co[symm->axis];
+
+ dst_v = BM_vert_create(symm->bm, co, src_v);
+ BMO_elem_flag_enable(symm->bm, dst_v, SYMM_OUTPUT_GEOM);
+ BLI_ghash_insert(symm->vert_symm_map, src_v, dst_v);
+ break;
+
+ case SYMM_SIDE_AXIS:
+ /* The vertex is within the axis area, snap to center */
+ src_v->co[symm->axis] = 0;
+ /* Vertex isn't copied, map to itself */
+ BLI_ghash_insert(symm->vert_symm_map, src_v, src_v);
+ break;
+
+ case SYMM_SIDE_KILL:
+ /* The vertex does not lie in the half-space being
+ * copied from, nothing to do */
+ break;
+ }
+ }
+}
+
+static int symm_edge_crosses_axis(const Symm *symm, const BMEdge *e)
+{
+ const int sides[2] = {symm_co_side(symm, e->v1->co),
+ symm_co_side(symm, e->v2->co)};
+
+ return ((sides[0] != SYMM_SIDE_AXIS) &&
+ (sides[1] != SYMM_SIDE_AXIS) &&
+ (sides[0] != sides[1]));
+}
+
+/* Output edge split vertices for asymmetric edges and the edge_splits
+ * mapping array */
+static void symm_split_asymmetric_edges(Symm *symm)
+{
+ BMOIter oiter;
+ BMEdge *e;
+
+ symm->edge_split_map = BLI_ghash_ptr_new(AT);
+
+ BMO_ITER (e, &oiter, symm->bm, symm->op, "input", BM_EDGE) {
+ float flipped[3];
+
+ copy_v3_v3(flipped, e->v1->co);
+ flipped[symm->axis] = -flipped[symm->axis];
+
+ if (symm_edge_crosses_axis(symm, e) &&
+ (!compare_v3v3(e->v2->co, flipped, SYMM_VERT_THRESHOLD)))
+ {
+ /* Endpoints lie on opposite sides and are asymmetric */
+
+ BMVert *v;
+ float lambda = 0, edge_dir[3], co[3];
+ float plane_co[3][3][3] = {
+ /* axis == 0 */
+ {{0, 0, 0}, {0, 1, 0}, {0, 0, 1}},
+ /* axis == 1 */
+ {{0, 0, 0}, {1, 0, 0}, {0, 0, 1}},
+ /* axis == 2 */
+ {{0, 0, 0}, {1, 0, 0}, {0, 1, 0}},
+ };
+ int r;
+
+ /* Find intersection of edge with symmetry plane */
+ sub_v3_v3v3(edge_dir, e->v2->co, e->v1->co);
+ normalize_v3(edge_dir);
+ r = isect_ray_plane_v3(e->v1->co,
+ edge_dir,
+ plane_co[symm->axis][0],
+ plane_co[symm->axis][1],
+ plane_co[symm->axis][2],
+ &lambda, TRUE);
+ BLI_assert(r);
+
+ madd_v3_v3v3fl(co, e->v1->co, edge_dir, lambda);
+ co[symm->axis] = 0;
+
+ /* Edge is asymmetric, split it with a new vertex */
+ v = BM_vert_create(symm->bm, co, e->v1);
+ BMO_elem_flag_enable(symm->bm, v, SYMM_OUTPUT_GEOM);
+ BLI_ghash_insert(symm->edge_split_map, e, v);
+ }
+ }
+}
+
+static void symm_mirror_edges(Symm *symm)
+{
+ BMOIter oiter;
+ BMEdge *e;
+
+ BMO_ITER (e, &oiter, symm->bm, symm->op, "input", BM_EDGE) {
+ BMVert *v1 = NULL, *v2 = NULL;
+ BMEdge *e_new;
+
+ v1 = BLI_ghash_lookup(symm->vert_symm_map, e->v1);
+ v2 = BLI_ghash_lookup(symm->vert_symm_map, e->v2);
+
+ if (v1 && v2) {
+ e_new = BM_edge_create(symm->bm, v1, v2, e, TRUE);
+ BMO_elem_flag_enable(symm->bm, e_new, SYMM_OUTPUT_GEOM);
+ }
+ else if (v1 || v2) {
+ if (BLI_ghash_haskey(symm->edge_split_map, e)) {
+ BMVert *v_split = BLI_ghash_lookup(symm->edge_split_map, e);
+
+ /* Output the keep side of the split edge */
+ if (!v1) {
+ e_new = BM_edge_create(symm->bm, v_split, e->v2, e, TRUE);
+ BMO_elem_flag_enable(symm->bm, e_new, SYMM_OUTPUT_GEOM);
+ v1 = v_split;
+ }
+ else {
+ e_new = BM_edge_create(symm->bm, e->v1, v_split, e, TRUE);
+ BMO_elem_flag_enable(symm->bm, e_new, SYMM_OUTPUT_GEOM);
+ v2 = v_split;
+ }
+
+ /* Output the kill side of the split edge */
+ e_new = BM_edge_create(symm->bm, v1, v2, e, TRUE);
+ BMO_elem_flag_enable(symm->bm, e_new, SYMM_OUTPUT_GEOM);
+ }
+ }
+ }
+}
+
+/****************************** SymmPoly ******************************/
+
+typedef struct {
+ /* Indices into the source mvert array (or -1 if not in that array) */
+ BMVert **src_verts;
+ /* Indices into the destination mvert array, these are vertices
+ * created by an edge split (-1 for vertices not created by edge
+ * split) */
+ BMVert **edge_verts;
+
+ /* Number of vertices in the polygon */
+ int len;
+
+ /* True only if none of the polygon's edges were split */
+ int already_symmetric;
+} SymmPoly;
+
+static void symm_poly_with_splits(const Symm *symm,
+ BMFace *f,
+ SymmPoly *out)
+{
+ BMIter iter;
+ BMLoop *l;
+ int i;
+
+ /* Count vertices and check for edge splits */
+ out->len = f->len;
+ out->already_symmetric = TRUE;
+ BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) {
+ if (BLI_ghash_haskey(symm->edge_split_map, l->e)) {
+ out->len++;
+ out->already_symmetric = FALSE;
+ }
+ }
+
+ i = 0;
+ BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) {
+ BMVert *split = BLI_ghash_lookup(symm->edge_split_map, l->e);
+
+ out->src_verts[i] = l->v;
+ out->edge_verts[i] = NULL;
+ i++;
+
+ if (split) {
+ out->src_verts[i] = NULL;
+ out->edge_verts[i] = split;
+ i++;
+ }
+ }
+}
+
+static const float *symm_poly_co(const SymmPoly *sp, int v)
+{
+ if (sp->src_verts[v])
+ return sp->src_verts[v]->co;
+ else if (sp->edge_verts[v])
+ return sp->edge_verts[v]->co;
+ else
+ return NULL;
+}
+
+static SymmSide symm_poly_co_side(const Symm *symm,
+ const SymmPoly *sp,
+ int v)
+{
+ return symm_co_side(symm, symm_poly_co(sp, v));
+}
+
+/* Return the index of the vertex in the destination array at corner
+ * 'v' of the polygon, or -1 if not in that array */
+static BMVert *symm_poly_dst(const SymmPoly *sp, int v)
+{
+ if (sp->edge_verts[v])
+ return sp->edge_verts[v];
+ else if (sp->src_verts[v])
+ return sp->src_verts[v];
+ else
+ return NULL;
+}
+
+/* Same as above, but returns the index of the mirror if available, or
+ * the same index if on the axis, or -1 otherwise */
+static BMVert *symm_poly_mirror_dst(const Symm *symm,
+ const SymmPoly *sp,
+ int v)
+{
+ if (sp->edge_verts[v])
+ return sp->edge_verts[v];
+ else if (sp->src_verts[v]) {
+ if (BLI_ghash_haskey(symm->vert_symm_map, sp->src_verts[v]))
+ return BLI_ghash_lookup(symm->vert_symm_map, sp->src_verts[v]);
+ else
+ return sp->src_verts[v];
+ }
+ else
+ return NULL;
+}
+
+static int symm_poly_next_crossing(const Symm *symm,
+ const SymmPoly *sp,
+ int start,
+ int *l1,
+ int *l2)
+{
+ int i;
+
+ for (i = 0; i < sp->len; i++) {
+ (*l1) = (start + i) % sp->len;
+ (*l2) = ((*l1) + 1) % sp->len;
+
+ if ((symm_poly_co_side(symm, sp, *l1) == SYMM_SIDE_KILL) ^
+ (symm_poly_co_side(symm, sp, *l2) == SYMM_SIDE_KILL))
+ {
+ return TRUE;
+ }
+ }
+
+ BLI_assert(!"symm_poly_next_crossing failed");
+ return FALSE;
+}
+
+static BMFace *symm_face_create_v(BMesh *bm, BMVert **fv, BMEdge **fe, int len)
+{
+ BMFace *f_new;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ int j = (i + 1) % len;
+ fe[i] = BM_edge_exists(fv[i], fv[j]);
+ if (!fe[i]) {
+ fe[i] = BM_edge_create(bm, fv[i], fv[j], NULL, FALSE);
+ BMO_elem_flag_enable(bm, fe[i], SYMM_OUTPUT_GEOM);
+ }
+ }
+ f_new = BM_face_create(bm, fv, fe, len, TRUE);
+ BM_face_select_set(bm, f_new, TRUE);
+ BMO_elem_flag_enable(bm, f_new, SYMM_OUTPUT_GEOM);
+ return f_new;
+}
+
+static void symm_mesh_output_poly_zero_splits(Symm *symm,
+ SymmPoly *sp,
+ BMVert **fv,
+ BMEdge **fe,
+ int segment_len,
+ int start)
+{
+ int i, j;
+
+ j = 0;
+
+ /* Output the keep side of the input polygon */
+ for (i = 0; i < segment_len; i++) {
+ const int offset = (start + i) % sp->len;
+ BLI_assert(sp->src_verts[offset]);
+ fv[j++] = sp->src_verts[offset];
+ }
+
+ /* Output the kill side of the polygon */
+ for (i = segment_len - 1; i >= 0; i--) {
+ const int offset = (start + i) % sp->len;
+
+ if (symm_poly_co_side(symm, sp, offset) == SYMM_SIDE_KEEP) {
+ BLI_assert(sp->src_verts[offset]);
+ fv[j++] = BLI_ghash_lookup(symm->vert_symm_map,
+ sp->src_verts[offset]);
+ }
+ }
+
+ symm_face_create_v(symm->bm, fv, fe, j);
+}
+
+static void symm_mesh_output_poly_with_splits(Symm *symm,
+ SymmPoly *sp,
+ BMVert **fv,
+ BMEdge **fe,
+ int segment_len,
+ int start)
+{
+ int i;
+
+ /* Output the keep side of the input polygon */
+
+ for (i = 0; i < segment_len; i++) {
+ const int offset = (start + i) % sp->len;
+ BMVert *v = symm_poly_dst(sp, offset);
+
+ BLI_assert(v);
+
+ fv[i] = v;
+ }
+
+ symm_face_create_v(symm->bm, fv, fe, segment_len);
+
+ /* Output the kill side of the input polygon */
+
+ for (i = 0; i < segment_len; i++) {
+ const int offset = (start + i) % sp->len;
+ BMVert *v = symm_poly_mirror_dst(symm, sp, offset);
+
+ fv[segment_len - i - 1] = v;
+
+ }
+
+ symm_face_create_v(symm->bm, fv, fe, segment_len);
+}
+
+static void symm_mirror_polygons(Symm *symm)
+{
+ BMOIter oiter;
+ BMFace *f;
+ BMVert **pv = NULL;
+ BMVert **fv = NULL;
+ BMEdge **fe = NULL;
+ BLI_array_declare(pv);
+ BLI_array_declare(fv);
+ BLI_array_declare(fe);
+
+ BMO_ITER (f, &oiter, symm->bm, symm->op, "input", BM_FACE) {
+ BMIter iter;
+ BMLoop *l;
+ int mirror_all = TRUE, ignore_all = TRUE;
+
+ /* Check if entire polygon can be mirrored or ignored */
+ BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) {
+ const SymmSide side = symm_co_side(symm, l->v->co);
+ if (side == SYMM_SIDE_KILL)
+ mirror_all = FALSE;
+ else if (side == SYMM_SIDE_KEEP)
+ ignore_all = FALSE;
+ }
+
+ if (mirror_all) {
+ int i;
+
+ /* Make a mirrored copy of the polygon */
+
+ BLI_array_empty(fv);
+ BLI_array_empty(fe);
+ BLI_array_grow_items(fv, f->len);
+ BLI_array_grow_items(fe, f->len);
+
+ i = f->len;
+ BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) {
+ i--;
+
+ if (symm_co_side(symm, l->v->co) == SYMM_SIDE_KEEP)
+ fv[i] = BLI_ghash_lookup(symm->vert_symm_map, l->v);
+ else
+ fv[i] = l->v;
+ }
+
+ symm_face_create_v(symm->bm, fv, fe, f->len);
+ }
+ else if (ignore_all) {
+ BM_face_kill(symm->bm, f);
+ }
+ else {
+ SymmPoly sp;
+ int l1, l2, l3, l4;
+ int double_l2, double_l3;
+ int segment_len;
+
+ BLI_array_empty(pv);
+ BLI_array_grow_items(pv, f->len * 4);
+ sp.src_verts = pv;
+ sp.edge_verts = pv + f->len * 2;
+ symm_poly_with_splits(symm, f, &sp);
+
+ /* Find first loop edge crossing the axis */
+ symm_poly_next_crossing(symm, &sp, 0, &l1, &l2);
+
+ /* If crossing isn't kill to keep, find the next one */
+ if (symm_poly_co_side(symm, &sp, l1) != SYMM_SIDE_KILL) {
+ symm_poly_next_crossing(symm, &sp, l2, &l1, &l2);
+ }
+
+ /* Find next crossing (keep to kill) */
+ symm_poly_next_crossing(symm, &sp, l2, &l3, &l4);
+
+ if (l2 == l3)
+ segment_len = 0;
+ else if (l2 < l3)
+ segment_len = l3 - l2 + 1;
+ else
+ segment_len = (sp.len - l2 + 1) + l3;
+
+ double_l2 = symm_poly_co_side(symm, &sp, l2) == SYMM_SIDE_KEEP;
+ double_l3 = symm_poly_co_side(symm, &sp, l3) == SYMM_SIDE_KEEP;
+
+ /* Calculate number of new polygons/loops */
+ if (segment_len == 0) {
+ }
+ else if (sp.already_symmetric) {
+ int new_loops;
+
+ if (double_l2 && double_l3)
+ new_loops = segment_len * 2;
+ else if (!double_l2 && !double_l3)
+ new_loops = segment_len * 2 - 2;
+ else
+ new_loops = segment_len * 2 - 1;
+
+ BLI_array_empty(fv);
+ BLI_array_empty(fe);
+ BLI_array_grow_items(fv, new_loops);
+ BLI_array_grow_items(fe, new_loops);
+
+ symm_mesh_output_poly_zero_splits(symm, &sp,
+ fv, fe,
+ segment_len, l2);
+ BM_face_kill(symm->bm, f);
+ }
+ else if (!double_l2 && !double_l3) {
+ BLI_array_empty(fv);
+ BLI_array_empty(fe);
+ BLI_array_grow_items(fv, segment_len);
+ BLI_array_grow_items(fe, segment_len);
+
+ symm_mesh_output_poly_with_splits(symm, &sp,
+ fv, fe,
+ segment_len,
+ l2);
+
+ BM_face_kill(symm->bm, f);
+ }
+ else {
+ BLI_array_empty(fv);
+ BLI_array_empty(fe);
+ BLI_array_grow_items(fv, segment_len);
+ BLI_array_grow_items(fe, segment_len);
+
+ symm_mesh_output_poly_with_splits(symm, &sp,
+ fv, fe,
+ segment_len,
+ l2);
+
+ BM_face_kill(symm->bm, f);
+
+ /* Output bridge triangle */
+
+ BLI_array_empty(fv);
+ BLI_array_empty(fe);
+ BLI_array_grow_items(fv, 3);
+ BLI_array_grow_items(fe, 3);
+
+ if (double_l2) {
+ fv[0] = symm_poly_dst(&sp, l2);
+ fv[1] = symm_poly_mirror_dst(symm, &sp, l2);
+ fv[2] = symm_poly_dst(&sp, l3);
+ }
+ else if (double_l3) {
+ fv[0] = symm_poly_dst(&sp, l3);
+ fv[1] = symm_poly_mirror_dst(symm, &sp, l3);
+ fv[2] = symm_poly_dst(&sp, l2);
+ }
+
+ BLI_assert(fv[0] && fv[1] && fv[2]);
+
+ symm_face_create_v(symm->bm, fv, fe, 3);
+ }
+ }
+ }
+
+ BLI_array_free(pv);
+ BLI_array_free(fv);
+ BLI_array_free(fe);
+}
+
+/* Remove unused edges and vertices from the side being copied to */
+static void symm_kill_unused(Symm *symm)
+{
+ BMOIter oiter;
+ BMEdge *e;
+ BMVert *v;
+
+ /* Kill unused edges */
+ BMO_ITER (e, &oiter, symm->bm, symm->op, "input", BM_EDGE) {
+ const int crosses = symm_edge_crosses_axis(symm, e);
+ const int symmetric = (crosses &&
+ (!BLI_ghash_haskey(symm->edge_split_map, e)));
+
+ if (((symm_co_side(symm, e->v1->co) == SYMM_SIDE_KILL) ||
+ (symm_co_side(symm, e->v2->co) == SYMM_SIDE_KILL)) &&
+ !symmetric)
+ {
+ /* The edge might be used by a face outside the input set */
+ if (BM_edge_face_count(e) == 0)
+ BM_edge_kill(symm->bm, e);
+ }
+ }
+
+ /* Kill unused vertices */
+ BMO_ITER (v, &oiter, symm->bm, symm->op, "input", BM_VERT) {
+ if (symm_co_side(symm, v->co) == SYMM_SIDE_KILL) {
+ if (BM_vert_edge_count(v) == 0)
+ BM_vert_kill(symm->bm, v);
+ }
+ }
+}
+
+void bmo_symmetrize_exec(BMesh *bm, BMOperator *op)
+{
+ Symm symm;
+ BMO_SymmDirection direction = BMO_slot_int_get(op, "direction");
+
+ symm.bm = bm;
+ symm.op = op;
+ symm.axis = (ELEM(direction,
+ BMO_SYMMETRIZE_NEGATIVE_X,
+ BMO_SYMMETRIZE_POSITIVE_X) ? 0 :
+ ELEM(direction,
+ BMO_SYMMETRIZE_NEGATIVE_Y,
+ BMO_SYMMETRIZE_POSITIVE_Y) ? 1 :
+ ELEM(direction,
+ BMO_SYMMETRIZE_NEGATIVE_Z,
+ BMO_SYMMETRIZE_POSITIVE_Z) ? 2 : 0);
+ symm.direction = direction;
+
+ symm_verts_mirror(&symm);
+ symm_split_asymmetric_edges(&symm);
+ symm_mirror_edges(&symm);
+ symm_mirror_polygons(&symm);
+ symm_kill_unused(&symm);
+
+ BLI_ghash_free(symm.vert_symm_map, NULL, NULL);
+ BLI_ghash_free(symm.edge_split_map, NULL, NULL);
+
+ BMO_slot_buffer_from_enabled_flag(bm, op, "geomout", BM_ALL,
+ SYMM_OUTPUT_GEOM);
+}
diff --git a/source/blender/bmesh/operators/bmo_unsubdivide.c b/source/blender/bmesh/operators/bmo_unsubdivide.c
new file mode 100644
index 00000000000..64b7151aee5
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_unsubdivide.c
@@ -0,0 +1,348 @@
+/*
+ * ***** 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): Campbell Barton
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/bmesh/operators/bmo_unsubdivide.c
+ * \ingroup bmesh
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+
+#include "bmesh.h"
+
+#include "intern/bmesh_operators_private.h" /* own include */
+
+
+static int bm_vert_dissolve_fan_test(BMVert *v)
+{
+ /* check if we should walk over these verts */
+ BMIter iter;
+ BMEdge *e;
+
+ unsigned int tot_edge = 0;
+ unsigned int tot_edge_boundary = 0;
+ unsigned int tot_edge_manifold = 0;
+ unsigned int tot_edge_wire = 0;
+
+ BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
+ if (BM_edge_is_boundary(e)) {
+ tot_edge_boundary++;
+ }
+ else if (BM_edge_is_manifold(e)) {
+ tot_edge_manifold++;
+ }
+ else if (BM_edge_is_wire(e)) {
+ tot_edge_wire++;
+ }
+ tot_edge++;
+ }
+
+ if ((tot_edge == 4) && (tot_edge_boundary == 0) && (tot_edge_manifold == 4)) {
+ return TRUE;
+ }
+ else if ((tot_edge == 3) && (tot_edge_boundary == 0) && (tot_edge_manifold == 3)) {
+ return TRUE;
+ }
+ else if ((tot_edge == 3) && (tot_edge_boundary == 2) && (tot_edge_manifold == 1)) {
+ return TRUE;
+ }
+ else if ((tot_edge == 2) && (tot_edge_wire == 2)) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int bm_vert_dissolve_fan(BMesh *bm, BMVert *v)
+{
+ /* collapse under 2 conditions.
+ * - vert connects to 4 manifold edges (and 4 faces).
+ * - vert connecrs to 1 manifold edge, 2 boundary edges (and 2 faces).
+ *
+ * This covers boundary verts of a quad grid and center verts.
+ * note that surrounding faces dont have to be quads.
+ */
+
+ BMIter iter;
+ BMEdge *e;
+
+ unsigned int tot_loop = 0;
+ unsigned int tot_edge = 0;
+ unsigned int tot_edge_boundary = 0;
+ unsigned int tot_edge_manifold = 0;
+ unsigned int tot_edge_wire = 0;
+
+ BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
+ if (BM_edge_is_boundary(e)) {
+ tot_edge_boundary++;
+ }
+ else if (BM_edge_is_manifold(e)) {
+ tot_edge_manifold++;
+ }
+ else if (BM_edge_is_wire(e)) {
+ tot_edge_wire++;
+ }
+ tot_edge++;
+ }
+
+ if (tot_edge == 2) {
+ /* check for 2 wire verts only */
+ if (tot_edge_wire == 2) {
+ return (BM_vert_collapse_edge(bm, v->e, v, TRUE) != NULL);
+ }
+ }
+ else if (tot_edge == 4) {
+ /* check for 4 faces surrounding */
+ if (tot_edge_boundary == 0 && tot_edge_manifold == 4) {
+ /* good to go! */
+ tot_loop = 4;
+ }
+ }
+ else if (tot_edge == 3) {
+ /* check for 2 faces surrounding at a boundary */
+ if (tot_edge_boundary == 2 && tot_edge_manifold == 1) {
+ /* good to go! */
+ tot_loop = 2;
+ }
+ else if (tot_edge_boundary == 0 && tot_edge_manifold == 3) {
+ /* good to go! */
+ tot_loop = 3;
+ }
+ }
+
+ if (tot_loop) {
+ BMLoop *f_loop[4];
+ unsigned int i;
+
+ /* ensure there are exactly tot_loop loops */
+ BLI_assert(BM_iter_at_index(bm, BM_LOOPS_OF_VERT, v, tot_loop) == NULL);
+ BM_iter_as_array(bm, BM_LOOPS_OF_VERT, v, (void **)f_loop, tot_loop);
+
+ for (i = 0; i < tot_loop; i++) {
+ BMLoop *l = f_loop[i];
+ if (l->f->len > 3) {
+ BLI_assert(l->prev->v != l->next->v);
+ BM_face_split(bm, l->f, l->prev->v, l->next->v, NULL, NULL, TRUE);
+ }
+ }
+
+ return BM_vert_dissolve(bm, v);
+ }
+
+ return FALSE;
+}
+
+enum {
+ VERT_INDEX_DO_COLLAPSE = -1,
+ VERT_INDEX_INIT = 0,
+ VERT_INDEX_IGNORE = 1
+};
+
+// #define USE_WALKER /* gives uneven results, disable for now */
+// #define USE_ALL_VERTS
+
+/* - BMVert.flag & BM_ELEM_TAG: shows we touched this vert
+ * - BMVert.index == -1: shows we will remove this vert
+ */
+void bmo_unsubdivide_exec(BMesh *bm, BMOperator *op)
+{
+#ifdef USE_WALKER
+# define ELE_VERT_TAG 1
+#else
+ BMVert **vert_seek_a = MEM_mallocN(sizeof(BMVert *) * bm->totvert, __func__);
+ BMVert **vert_seek_b = MEM_mallocN(sizeof(BMVert *) * bm->totvert, __func__);
+ unsigned vert_seek_a_tot = 0;
+ unsigned vert_seek_b_tot = 0;
+#endif
+
+ BMVert *v;
+ BMIter iter;
+
+ const unsigned int offset = 0;
+ const unsigned int nth = 2;
+
+ const int iterations = maxi(1, BMO_slot_int_get(op, "iterations"));
+ int iter_step;
+
+#ifdef USE_ALL_VERTS
+ (void)op;
+ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ BM_elem_flag_enable(v, BM_ELEM_TAG);
+ }
+#else /* USE_ALL_VERTS */
+ BMOpSlot *vinput = BMO_slot_get(op, "verts");
+ BMVert **vinput_arr = (BMVert **)vinput->data.p;
+ int v_index;
+
+ /* tag verts */
+ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ BM_elem_flag_disable(v, BM_ELEM_TAG);
+ }
+ for (v_index = 0; v_index < vinput->len; v_index++) {
+ v = vinput_arr[v_index];
+ BM_elem_flag_enable(v, BM_ELEM_TAG);
+ }
+#endif /* USE_ALL_VERTS */
+
+
+ for (iter_step = 0; iter_step < iterations; iter_step++) {
+ int iter_done;
+
+ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_flag_test(v, BM_ELEM_TAG) && bm_vert_dissolve_fan_test(v)) {
+#ifdef USE_WALKER
+ BMO_elem_flag_enable(bm, v, ELE_VERT_TAG);
+#endif
+ BM_elem_index_set(v, VERT_INDEX_INIT); /* set_dirty! */
+ }
+ else {
+ BM_elem_index_set(v, VERT_INDEX_IGNORE); /* set_dirty! */
+ }
+ }
+ /* done with selecting tagged verts */
+
+
+ /* main loop, keep tagging until we can't tag any more islands */
+ while (TRUE) {
+#ifdef USE_WALKER
+ BMWalker walker;
+#else
+ unsigned int depth = 1;
+ unsigned int i;
+#endif
+ BMVert *v_first = NULL;
+ BMVert *v;
+
+ /* we could avoid iterating from the start each time */
+ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ if (v->e && (BM_elem_index_get(v) == VERT_INDEX_INIT)) {
+#ifdef USE_WALKER
+ if (BMO_elem_flag_test(bm, v, ELE_VERT_TAG))
+#endif
+ {
+ /* check again incase the topology changed */
+ if (bm_vert_dissolve_fan_test(v)) {
+ v_first = v;
+ }
+ break;
+ }
+ }
+ }
+ if (v_first == NULL) {
+ break;
+ }
+
+#ifdef USE_WALKER
+ /* Walk over selected elements starting at active */
+ BMW_init(&walker, bm, BMW_CONNECTED_VERTEX,
+ ELE_VERT_TAG, BMW_MASK_NOP, BMW_MASK_NOP,
+ BMW_FLAG_NOP, /* don't use BMW_FLAG_TEST_HIDDEN here since we want to desel all */
+ BMW_NIL_LAY);
+
+ BLI_assert(walker.order == BMW_BREADTH_FIRST);
+ for (v = BMW_begin(&walker, v_first); v != NULL; v = BMW_step(&walker)) {
+ /* Deselect elements that aren't at "nth" depth from active */
+ if (BM_elem_index_get(v) == VERT_INDEX_INIT) {
+ if ((offset + BMW_current_depth(&walker)) % nth) {
+ /* tag for removal */
+ BM_elem_index_set(v, VERT_INDEX_DO_COLLAPSE); /* set_dirty! */
+ }
+ else {
+ /* works better to allow these verts to be checked again */
+ //BM_elem_index_set(v, VERT_INDEX_IGNORE); /* set_dirty! */
+ }
+ }
+ }
+ BMW_end(&walker);
+#else
+
+ BM_elem_index_set(v_first, (offset + depth) % nth ? VERT_INDEX_IGNORE : VERT_INDEX_DO_COLLAPSE); /* set_dirty! */
+
+ vert_seek_b_tot = 0;
+ vert_seek_b[vert_seek_b_tot++] = v_first;
+
+ while (TRUE) {
+ BMEdge *e;
+
+ if ((offset + depth) % nth) {
+ vert_seek_a_tot = 0;
+ for (i = 0; i < vert_seek_b_tot; i++) {
+ v = vert_seek_b[i];
+ BLI_assert(BM_elem_index_get(v) == VERT_INDEX_IGNORE);
+ BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
+ BMVert *v_other = BM_edge_other_vert(e, v);
+ if (BM_elem_index_get(v_other) == VERT_INDEX_INIT) {
+ BM_elem_index_set(v_other, VERT_INDEX_DO_COLLAPSE); /* set_dirty! */
+ vert_seek_a[vert_seek_a_tot++] = v_other;
+ }
+ }
+ }
+ if (vert_seek_a_tot == 0) {
+ break;
+ }
+ }
+ else {
+ vert_seek_b_tot = 0;
+ for (i = 0; i < vert_seek_a_tot; i++) {
+ v = vert_seek_a[i];
+ BLI_assert(BM_elem_index_get(v) == VERT_INDEX_DO_COLLAPSE);
+ BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
+ BMVert *v_other = BM_edge_other_vert(e, v);
+ if (BM_elem_index_get(v_other) == VERT_INDEX_INIT) {
+ BM_elem_index_set(v_other, VERT_INDEX_IGNORE); /* set_dirty! */
+ vert_seek_b[vert_seek_b_tot++] = v_other;
+ }
+ }
+ }
+ if (vert_seek_b_tot == 0) {
+ break;
+ }
+ }
+
+ depth++;
+ }
+#endif /* USE_WALKER */
+
+ }
+
+ /* now we tagged all verts -1 for removal, lets loop over and rebuild faces */
+ iter_done = FALSE;
+ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_index_get(v) == VERT_INDEX_DO_COLLAPSE) {
+ iter_done |= bm_vert_dissolve_fan(bm, v);
+ }
+ }
+
+ if (iter_done == FALSE) {
+ break;
+ }
+ }
+
+ bm->elem_index_dirty |= BM_VERT;
+
+#ifndef USE_WALKER
+ MEM_freeN(vert_seek_a);
+ MEM_freeN(vert_seek_b);
+#endif
+
+}
+
diff --git a/source/blender/bmesh/operators/bmo_utils.c b/source/blender/bmesh/operators/bmo_utils.c
index 72e55b2700c..3d792205d08 100644
--- a/source/blender/bmesh/operators/bmo_utils.c
+++ b/source/blender/bmesh/operators/bmo_utils.c
@@ -1033,10 +1033,9 @@ void bmo_similar_verts_exec(BMesh *bm, BMOperator *op)
void bmo_rotate_uvs_exec(BMesh *bm, BMOperator *op)
{
- BMOIter fs_iter; /* selected faces iterator */
- BMFace *fs; /* current face */
- BMIter l_iter; /* iteration loop */
- // int n;
+ BMOIter fs_iter; /* selected faces iterator */
+ BMFace *fs; /* current face */
+ BMIter l_iter; /* iteration loop */
int dir = BMO_slot_int_get(op, "dir");
@@ -1091,7 +1090,6 @@ void bmo_rotate_uvs_exec(BMesh *bm, BMOperator *op)
}
}
}
-
}
/**************************************************************************** *
@@ -1140,10 +1138,9 @@ void bmo_reverse_uvs_exec(BMesh *bm, BMOperator *op)
void bmo_rotate_colors_exec(BMesh *bm, BMOperator *op)
{
- BMOIter fs_iter; /* selected faces iterator */
- BMFace *fs; /* current face */
- BMIter l_iter; /* iteration loop */
- // int n;
+ BMOIter fs_iter; /* selected faces iterator */
+ BMFace *fs; /* current face */
+ BMIter l_iter; /* iteration loop */
int dir = BMO_slot_int_get(op, "dir");