diff options
author | Campbell Barton <ideasman42@gmail.com> | 2013-08-22 22:02:36 +0400 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2013-08-22 22:02:36 +0400 |
commit | c4e1d40022167cb271374f05ebd90c4b215262ad (patch) | |
tree | 1f1576ae248e971a224ca5772a87231d08d13168 /source/blender | |
parent | e97f979f73ffb1872a7bb388efab883b47544e78 (diff) |
replace symmetrize code with calls to bmesh operators.
calls: bisect, duplicate, scale, flip, weld.
resolves report [#36475] Symmetrise does not keep uv/weight
also fixes issues with faces that cross the axis more then once.
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/bmesh/operators/bmo_symmetrize.c | 670 | ||||
-rw-r--r-- | source/blender/editors/mesh/editmesh_tools.c | 4 |
2 files changed, 52 insertions, 622 deletions
diff --git a/source/blender/bmesh/operators/bmo_symmetrize.c b/source/blender/bmesh/operators/bmo_symmetrize.c index f0402cc5da7..b1a9950610d 100644 --- a/source/blender/bmesh/operators/bmo_symmetrize.c +++ b/source/blender/bmesh/operators/bmo_symmetrize.c @@ -15,7 +15,7 @@ * 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 + * Contributor(s): Campbell Barton * * ***** END GPL LICENSE BLOCK ***** */ @@ -29,654 +29,80 @@ #include "MEM_guardedalloc.h" -#include "BLI_array.h" -#include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_math.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(__func__); - - BMO_ITER (src_v, &oiter, symm->op->slots_in, "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, BM_CREATE_NOP); - 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(__func__); - - BMO_ITER (e, &oiter, symm->op->slots_in, "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); +#define ELE_OUT 1 - 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, BM_CREATE_NOP); - 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->op->slots_in, "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, BM_CREATE_NO_DOUBLE); - BMO_elem_flag_enable(symm->bm, e_new, SYMM_OUTPUT_GEOM); - } - else if (v1 || v2) { - BMVert *v_split = BLI_ghash_lookup(symm->edge_split_map, e); - if (v_split) { - /* Output the keep side of the split edge */ - if (!v1) { - e_new = BM_edge_create(symm->bm, v_split, e->v2, e, BM_CREATE_NO_DOUBLE); - 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, BM_CREATE_NO_DOUBLE); - 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, BM_CREATE_NO_DOUBLE); - 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 */ - bool already_symmetric; - - BMFace *src_face; -} SymmPoly; - -static void symm_poly_with_splits(const Symm *symm, - BMFace *f, - SymmPoly *out) -{ - BMIter iter; - BMLoop *l; - int i; - - out->src_face = f; - - /* 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]) { - BMVert *v_src = BLI_ghash_lookup(symm->vert_symm_map, sp->src_verts[v]); - if (v_src) - return v_src; - else - return sp->src_verts[v]; - } - else - return NULL; -} - -static bool 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, BMFace *f_example, - BMVert **fv, BMEdge **fe, int len) -{ - BMFace *f_new; - int i; - - /* TODO: calling symmetrize in dynamic-topology sculpt mode - * frequently tries to create faces of length less than two, - * should investigate further */ - if (len < 3) - return NULL; - - 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, BM_CREATE_NOP); - BMO_elem_flag_enable(bm, fe[i], SYMM_OUTPUT_GEOM); - } - } - f_new = BM_face_create(bm, fv, fe, len, f_example, BM_CREATE_NO_DOUBLE); - 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, sp->src_face, 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, sp->src_face, 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, sp->src_face, fv, fe, segment_len); -} - -static void symm_mirror_polygons(Symm *symm) +void bmo_symmetrize_exec(BMesh *bm, BMOperator *op) { - 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->op->slots_in, "input", BM_FACE) { - BMIter iter; - BMLoop *l; - bool 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, f, 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; + const int direction = BMO_slot_int_get(op->slots_in, "direction"); + const int axis = direction % 3; - 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); + BMOperator op_bisect; + BMOperator op_dupe; + BMOperator op_weld; - /* Find first loop edge crossing the axis */ - symm_poly_next_crossing(symm, &sp, 0, &l1, &l2); + BMOpSlot *slot_vertmap; + BMOpSlot *slot_targetmap; - /* 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); - } + float plane_no[3]; + float scale[3]; - /* 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; + BMOIter siter; + BMVert *v; - /* Calculate number of new polygons/loops */ - if (segment_len == 0) { - } - else if (sp.already_symmetric) { - int new_loops; + copy_v3_fl(plane_no, 0.0f); + copy_v3_fl(scale, 1.0f); - 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; + plane_no[axis] = direction > 2 ? 1.0f : -1.0f; + scale[axis] *= -1.0f; - BLI_array_empty(fv); - BLI_array_empty(fe); - BLI_array_grow_items(fv, new_loops); - BLI_array_grow_items(fe, new_loops); + /* Cut in half */ + BMO_op_initf(bm, &op_bisect, op->flag, + "bisect_plane geom=%s plane_no=%v dist=%f clear_outer=%b", + op, "input", plane_no, 0.00001f, true); - 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); + BMO_op_exec(bm, &op_bisect); - symm_mesh_output_poly_with_splits(symm, &sp, - fv, fe, - segment_len, - l2); + /* Duplicate */ + BMO_op_initf(bm, &op_dupe, op->flag, + "duplicate geom=%S", + &op_bisect, "geom.out"); - 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); + BMO_op_exec(bm, &op_dupe); - symm_mesh_output_poly_with_splits(symm, &sp, - fv, fe, - segment_len, - l2); + /* Flag for output (some will be merged) */ + BMO_slot_buffer_flag_enable(bm, op_bisect.slots_out, "geom.out", BM_ALL_NOLOOP, ELE_OUT); + BMO_slot_buffer_flag_enable(bm, op_dupe.slots_out, "geom.out", BM_ALL_NOLOOP, ELE_OUT); - BM_face_kill(symm->bm, f); - /* Output bridge triangle */ + BMO_op_callf(bm, op->flag, "scale verts=%S vec=%v", &op_dupe, "geom.out", scale); + BMO_op_callf(bm, op->flag, "reverse_faces faces=%S", &op_dupe, "geom.out"); - 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); - } + /* Weld verts */ + BMO_op_init(bm, &op_weld, op->flag, "weld_verts"); - BLI_assert(fv[0] && fv[1] && fv[2]); + slot_vertmap = BMO_slot_get(op_dupe.slots_out, "vert_map.out"); + slot_targetmap = BMO_slot_get(op_weld.slots_in, "targetmap"); - symm_face_create_v(symm->bm, NULL, fv, fe, 3); - } - } + BMO_ITER (v, &siter, op_bisect.slots_out, "geom_cut.out", BM_VERT) { + BMVert *v_dupe = BMO_slot_map_elem_get(slot_vertmap, v); + BMO_slot_map_elem_insert(&op_weld, slot_targetmap, v_dupe, v); } - 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->op->slots_in, "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_is_wire(e)) - BM_edge_kill(symm->bm, e); - } - } - - /* Kill unused vertices */ - BMO_ITER (v, &oiter, symm->op->slots_in, "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->slots_in, "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; + BMO_op_exec(bm, &op_weld); - symm_verts_mirror(&symm); - symm_split_asymmetric_edges(&symm); - symm_mirror_edges(&symm); - symm_mirror_polygons(&symm); - symm_kill_unused(&symm); + /* Cleanup */ + BMO_op_finish(bm, &op_weld); - BLI_ghash_free(symm.vert_symm_map, NULL, NULL); - BLI_ghash_free(symm.edge_split_map, NULL, NULL); + BMO_op_finish(bm, &op_dupe); + BMO_op_finish(bm, &op_bisect); - BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "geom.out", - BM_ALL_NOLOOP, SYMM_OUTPUT_GEOM); + /* Create output */ + BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "geom.out", BM_ALL_NOLOOP, ELE_OUT); } diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 38ce56b4bc7..a0bb9839ca5 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -4445,6 +4445,10 @@ static int mesh_symmetrize_exec(bContext *C, wmOperator *op) BM_ELEM_SELECT, RNA_enum_get(op->ptr, "direction")); BMO_op_exec(em->bm, &bmop); + EDBM_flag_disable_all(em, BM_ELEM_SELECT); + + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true); + if (!EDBM_op_finish(em, &bmop, op, true)) { return OPERATOR_CANCELLED; } |