diff options
author | Tamito Kajiyama <rd6t-kjym@asahi-net.or.jp> | 2012-10-20 20:48:48 +0400 |
---|---|---|
committer | Tamito Kajiyama <rd6t-kjym@asahi-net.or.jp> | 2012-10-20 20:48:48 +0400 |
commit | 55015daa43f0ab45341e316abcf11f23c87b5ebe (patch) | |
tree | 3156892b6d807d9ba513d444adb870b0ae358e7a /source/blender/bmesh | |
parent | 1fe70c07a008185c4e5925aff2c214c93ff396b7 (diff) | |
parent | a9e2e2279780ec2fb58e6820b9cad95ba03f4cad (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')
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"); |