From 0636886715b4c5ac56ed7f903bf37a00cfbdc841 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 23 Oct 2012 04:26:39 +0000 Subject: add un-subdivude as an optional method for the decimate modifier, gives more even geometry & nicer results in some cases. --- .../startup/bl_ui/properties_data_modifier.py | 13 +- source/blender/bmesh/CMakeLists.txt | 1 + source/blender/bmesh/intern/bmesh_decimate.h | 3 + .../blender/bmesh/intern/bmesh_decimate_collapse.c | 4 +- .../bmesh/intern/bmesh_decimate_unsubdivide.c | 342 +++++++++++++++++++++ source/blender/bmesh/operators/bmo_unsubdivide.c | 295 +----------------- source/blender/makesdna/DNA_modifier_types.h | 16 +- source/blender/makesdna/intern/makesdna.c | 2 +- source/blender/makesrna/intern/rna_modifier.c | 22 +- source/blender/modifiers/intern/MOD_decimate.c | 48 +-- 10 files changed, 424 insertions(+), 322 deletions(-) create mode 100644 source/blender/bmesh/intern/bmesh_decimate_unsubdivide.c diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py index 5d8144631b0..1887c4786be 100644 --- a/release/scripts/startup/bl_ui/properties_data_modifier.py +++ b/release/scripts/startup/bl_ui/properties_data_modifier.py @@ -211,10 +211,17 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): layout.row().prop(md, "deform_axis", expand=True) def DECIMATE(self, layout, ob, md): - layout.prop(md, "ratio") row = layout.row() - row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") - row.prop(md, "invert_vertex_group") + row.prop(md, "decimate_type", expand=True) + + if md.decimate_type == 'COLLAPSE': + layout.prop(md, "ratio") + row = layout.row() + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex_group") + else: # assume UNSUBDIV + layout.prop(md, "iterations") + layout.label(text="Face Count" + ": %d" % md.face_count) def DISPLACE(self, layout, ob, md): diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 3e0985471e1..a000cb59167 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -65,6 +65,7 @@ set(SRC intern/bmesh_core.c intern/bmesh_core.h intern/bmesh_decimate_collapse.c + intern/bmesh_decimate_unsubdivide.c intern/bmesh_decimate.h intern/bmesh_inline.h intern/bmesh_interp.c diff --git a/source/blender/bmesh/intern/bmesh_decimate.h b/source/blender/bmesh/intern/bmesh_decimate.h index 7fafb752e1e..18d86cfb1a5 100644 --- a/source/blender/bmesh/intern/bmesh_decimate.h +++ b/source/blender/bmesh/intern/bmesh_decimate.h @@ -29,4 +29,7 @@ void BM_mesh_decimate_collapse(BMesh *bm, const float factor, float *vweights); +void BM_mesh_decimate_unsubdivide_ex(BMesh *bm, const int iterations, const int tag_only); +void BM_mesh_decimate_unsubdivide(BMesh *bm, const int iterations); + #endif /* __BMESH_DECIMATE_H__ */ diff --git a/source/blender/bmesh/intern/bmesh_decimate_collapse.c b/source/blender/bmesh/intern/bmesh_decimate_collapse.c index 75fe12a418a..ce10fe2fed1 100644 --- a/source/blender/bmesh/intern/bmesh_decimate_collapse.c +++ b/source/blender/bmesh/intern/bmesh_decimate_collapse.c @@ -20,10 +20,10 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/bmesh/intern/bmesh_decimate.c +/** \file blender/bmesh/intern/bmesh_decimate_collapse.c * \ingroup bmesh * - * BMesh decimator. + * BMesh decimator that uses an edge collapse method. */ #include diff --git a/source/blender/bmesh/intern/bmesh_decimate_unsubdivide.c b/source/blender/bmesh/intern/bmesh_decimate_unsubdivide.c new file mode 100644 index 00000000000..18bfa5159bf --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_decimate_unsubdivide.c @@ -0,0 +1,342 @@ +/* + * ***** 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_unsubdivide.c + * \ingroup bmesh + * + * BMesh decimator that uses a grid un-subdivide method. + */ + + +#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 */ + +/* - BMVert.flag & BM_ELEM_TAG: shows we touched this vert + * - BMVert.index == -1: shows we will remove this vert + */ + +/** + * \param tag_only so we can call this from an operator */ +void BM_mesh_decimate_unsubdivide_ex(BMesh *bm, const int iterations, const int tag_only) +{ +#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; + + int iter_step; + + /* if tag_only is set, we assyme the caller knows what verts to tag + * needed for the operator */ + if (tag_only == FALSE) { + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + BM_elem_flag_enable(v, BM_ELEM_TAG); + } + } + + 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 +} + +void BM_mesh_decimate_unsubdivide(BMesh *bm, const int iterations) +{ + BM_mesh_decimate_unsubdivide_ex(bm, iterations, FALSE); +} diff --git a/source/blender/bmesh/operators/bmo_unsubdivide.c b/source/blender/bmesh/operators/bmo_unsubdivide.c index 64b7151aee5..75f886856dc 100644 --- a/source/blender/bmesh/operators/bmo_unsubdivide.c +++ b/source/blender/bmesh/operators/bmo_unsubdivide.c @@ -24,171 +24,23 @@ * \ingroup bmesh */ -#include "MEM_guardedalloc.h" - #include "BLI_math.h" +#include "BLI_utildefines.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; @@ -201,148 +53,7 @@ void bmo_unsubdivide_exec(BMesh *bm, BMOperator *op) 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 + /* do all the real work here */ + BM_mesh_decimate_unsubdivide_ex(bm, iterations, TRUE); } - diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 05c1871ae41..f53287beb9d 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -361,15 +361,23 @@ typedef struct UVProjectModifierData { typedef struct DecimateModifierData { ModifierData modifier; - float percent; - int faceCount; /* runtime only */ + float percent; /* (mode == MOD_DECIM_MODE_COLLAPSE) */ + int iter; /* (mode == MOD_DECIM_MODE_UNSUBDIV) */ char defgrp_name[64]; /* MAX_VGROUP_NAME */ - int flag, pad; + short flag, mode; + + /* runtime only */ + int face_count; } DecimateModifierData; enum { - MOD_DECIM_INVERT_VGROUP = (1 << 0) + MOD_DECIM_FLAG_INVERT_VGROUP = (1 << 0) +}; + +enum { + MOD_DECIM_MODE_COLLAPSE, + MOD_DECIM_MODE_UNSUBDIV }; /* Smooth modifier flags */ diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 77d2b00dfc2..bef5e5cc161 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -1195,7 +1195,7 @@ int main(int argc, char **argv) } /* handy but fails on struct bounds which makesdna doesnt care about - * unless structs are nested */ + * with quite the same strictness as GCC does */ #if 0 /* include files for automatic dependencies */ diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 67ab1469318..36bdb6cc896 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -1110,6 +1110,12 @@ static void rna_def_modifier_mirror(BlenderRNA *brna) static void rna_def_modifier_decimate(BlenderRNA *brna) { + static EnumPropertyItem modifier_decim_mode_items[] = { + {MOD_DECIM_MODE_COLLAPSE, "COLLAPSE", 0, "Collapse", "Use edge collapsing"}, + {MOD_DECIM_MODE_UNSUBDIV, "UNSUBDIV", 0, "Un-Subdivide", "Use un-subdivide face reduction"}, + {0, NULL, 0, NULL, NULL} + }; + StructRNA *srna; PropertyRNA *prop; @@ -1118,6 +1124,12 @@ static void rna_def_modifier_decimate(BlenderRNA *brna) RNA_def_struct_sdna(srna, "DecimateModifierData"); RNA_def_struct_ui_icon(srna, ICON_MOD_DECIM); + prop = RNA_def_property(srna, "decimate_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "mode"); + RNA_def_property_enum_items(prop, modifier_decim_mode_items); + RNA_def_property_ui_text(prop, "Mode", ""); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "ratio", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "percent"); RNA_def_property_range(prop, 0, 1); @@ -1125,6 +1137,13 @@ static void rna_def_modifier_decimate(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Ratio", "Ratio of triangles to reduce to"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "iterations", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "iter"); + RNA_def_property_range(prop, 0, SHRT_MAX); + RNA_def_property_ui_range(prop, 1, 100, 1, 0); + RNA_def_property_ui_text(prop, "Iterations", "Number of times to unsubdivide"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "defgrp_name"); RNA_def_property_ui_text(prop, "Vertex Group", "Vertex group name"); @@ -1132,12 +1151,11 @@ static void rna_def_modifier_decimate(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "invert_vertex_group", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_DECIM_INVERT_VGROUP); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_DECIM_FLAG_INVERT_VGROUP); RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "face_count", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "faceCount"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Face Count", "The current number of faces in the decimated mesh"); } diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c index 1cb6853447a..6eb2f8d6c79 100644 --- a/source/blender/modifiers/intern/MOD_decimate.c +++ b/source/blender/modifiers/intern/MOD_decimate.c @@ -72,8 +72,10 @@ static void copyData(ModifierData *md, ModifierData *target) DecimateModifierData *tdmd = (DecimateModifierData *) target; tdmd->percent = dmd->percent; + tdmd->iter = dmd->iter; BLI_strncpy(tdmd->defgrp_name, dmd->defgrp_name, sizeof(tdmd->defgrp_name)); tdmd->flag = dmd->flag; + tdmd->mode = dmd->mode; } static CustomDataMask requiredDataMask(Object *UNUSED(ob), ModifierData *md) @@ -110,42 +112,52 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, return dm; } - if (dmd->defgrp_name[0]) { - MDeformVert *dvert; - int defgrp_index; + if (dmd->mode == MOD_DECIM_MODE_COLLAPSE) { + if (dmd->defgrp_name[0]) { + MDeformVert *dvert; + int defgrp_index; - modifier_get_vgroup(ob, dm, dmd->defgrp_name, &dvert, &defgrp_index); + modifier_get_vgroup(ob, dm, dmd->defgrp_name, &dvert, &defgrp_index); - if (dvert) { - const unsigned int vert_tot = dm->getNumVerts(dm); - unsigned int i; + if (dvert) { + const unsigned int vert_tot = dm->getNumVerts(dm); + unsigned int i; - vweights = MEM_mallocN(vert_tot * sizeof(float), __func__); + vweights = MEM_mallocN(vert_tot * sizeof(float), __func__); - if (dmd->flag & MOD_DECIM_INVERT_VGROUP) { - for (i = 0; i < vert_tot; i++) { - vweights[i] = 1.0f - defvert_find_weight(&dvert[i], defgrp_index); + if (dmd->flag & MOD_DECIM_FLAG_INVERT_VGROUP) { + for (i = 0; i < vert_tot; i++) { + vweights[i] = 1.0f - defvert_find_weight(&dvert[i], defgrp_index); + } } - } - else { - for (i = 0; i < vert_tot; i++) { - vweights[i] = defvert_find_weight(&dvert[i], defgrp_index); + else { + for (i = 0; i < vert_tot; i++) { + vweights[i] = defvert_find_weight(&dvert[i], defgrp_index); + } } } } } - em = DM_to_editbmesh(dm, NULL, FALSE); bm = em->bm; - BM_mesh_decimate_collapse(bm, dmd->percent, vweights); + switch (dmd->mode) { + case MOD_DECIM_MODE_COLLAPSE: + BM_mesh_decimate_collapse(bm, dmd->percent, vweights); + break; + case MOD_DECIM_MODE_UNSUBDIV: + BM_mesh_decimate_unsubdivide(bm, dmd->iter); + break; + } + if (vweights) { MEM_freeN(vweights); } - dmd->faceCount = bm->totface; + /* update for display only */ + dmd->face_count = bm->totface; BLI_assert(em->looptris == NULL); result = CDDM_from_BMEditMesh(em, NULL, TRUE, FALSE); -- cgit v1.2.3