diff options
author | Campbell Barton <ideasman42@gmail.com> | 2015-06-15 03:58:07 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2015-06-15 04:03:13 +0300 |
commit | d8e994b35f247c3156a93f6901f0442eee08c13d (patch) | |
tree | 95590d4316227513dea9c6b2de8d6fa960ade6f2 /source/blender/editors | |
parent | 3efc0aca5428bc7eefa1b804f5ee850df88f5cc8 (diff) |
BMesh: edge-offset feature (Ctrl+Shift+R)
Ability to quickly add 2x edge loops on either side of selected loops.
Diffstat (limited to 'source/blender/editors')
-rw-r--r-- | source/blender/editors/mesh/editmesh_tools.c | 51 | ||||
-rw-r--r-- | source/blender/editors/mesh/mesh_intern.h | 1 | ||||
-rw-r--r-- | source/blender/editors/mesh/mesh_ops.c | 11 | ||||
-rw-r--r-- | source/blender/editors/transform/transform.c | 248 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_ops.c | 5 |
5 files changed, 299 insertions, 17 deletions
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 62aec788306..202ce8d68a6 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -5181,6 +5181,57 @@ void MESH_OT_wireframe(wmOperatorType *ot) RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 2); } +static int edbm_offset_edgeloop_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMOperator bmop; + + EDBM_op_init( + em, &bmop, op, + "offset_edgeloops edges=%he", + BM_ELEM_SELECT); + + BMO_op_exec(em->bm, &bmop); + + BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); + + /* If in face-only select mode, switch to edge select mode so that + * an edge-only selection is not inconsistent state */ + if (em->selectmode == SCE_SELECT_FACE) { + em->selectmode = SCE_SELECT_EDGE; + EDBM_selectmode_set(em); + EDBM_selectmode_to_scene(C); + } + + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true); + + if (!EDBM_op_finish(em, &bmop, op, true)) { + return OPERATOR_CANCELLED; + } + else { + EDBM_update_generic(em, true, true); + return OPERATOR_FINISHED; + } +} + +void MESH_OT_offset_edge_loops(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Offset Edge Loop"; + ot->idname = "MESH_OT_offset_edge_loops"; + ot->description = "Creates offset edge loop from the current selection"; + + /* api callbacks */ + ot->exec = edbm_offset_edgeloop_exec; + ot->poll = ED_operator_editmesh; + + /* Keep internal, since this is only meant to be accessed via 'MESH_OT_offset_edge_loops_slide' */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; +} + #ifdef WITH_BULLET static int edbm_convex_hull_exec(bContext *C, wmOperator *op) { diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 8c44d5a7b3f..c56465f570a 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -176,6 +176,7 @@ void MESH_OT_vert_connect_nonplanar(struct wmOperatorType *ot); void MESH_OT_face_make_planar(struct wmOperatorType *ot); void MESH_OT_edge_split(struct wmOperatorType *ot); void MESH_OT_bridge_edge_loops(struct wmOperatorType *ot); +void MESH_OT_offset_edge_loops(struct wmOperatorType *ot); void MESH_OT_wireframe(struct wmOperatorType *ot); void MESH_OT_convex_hull(struct wmOperatorType *ot); void MESH_OT_symmetrize(struct wmOperatorType *ot); diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index 20f222bf425..9718e63f012 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -177,6 +177,7 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_bridge_edge_loops); WM_operatortype_append(MESH_OT_inset); + WM_operatortype_append(MESH_OT_offset_edge_loops); WM_operatortype_append(MESH_OT_intersect); WM_operatortype_append(MESH_OT_face_split_by_edges); WM_operatortype_append(MESH_OT_poke); @@ -225,6 +226,13 @@ void ED_operatormacros_mesh(void) otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_edge_slide"); RNA_boolean_set(otmacro->ptr, "release_confirm", false); + ot = WM_operatortype_append_macro("MESH_OT_offset_edge_loops_slide", "Offset Edge Slide", "Offset edge loop slide", + OPTYPE_UNDO | OPTYPE_REGISTER); + WM_operatortype_macro_define(ot, "MESH_OT_offset_edge_loops"); + otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_edge_slide"); + RNA_boolean_set(otmacro->ptr, "release_confirm", false); + RNA_boolean_set(otmacro->ptr, "single_side", true); + ot = WM_operatortype_append_macro("MESH_OT_duplicate_move", "Add Duplicate", "Duplicate mesh and move", OPTYPE_UNDO | OPTYPE_REGISTER); WM_operatortype_macro_define(ot, "MESH_OT_duplicate"); @@ -302,7 +310,8 @@ void ED_keymap_mesh(wmKeyConfig *keyconf) keymap = WM_keymap_find(keyconf, "Mesh", 0, 0); keymap->poll = ED_operator_editmesh; - WM_keymap_add_item(keymap, "MESH_OT_loopcut_slide", RKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "MESH_OT_loopcut_slide", RKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "MESH_OT_offset_edge_loops_slide", RKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0); WM_keymap_add_item(keymap, "MESH_OT_inset", IKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "MESH_OT_poke", PKEY, KM_PRESS, KM_ALT, 0); kmi = WM_keymap_add_item(keymap, "MESH_OT_bevel", BKEY, KM_PRESS, KM_CTRL, 0); diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index f2c44e64d43..a2bfef8d89c 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -167,6 +167,7 @@ static void applyBoneEnvelope(TransInfo *t, const int mval[2]); static void initBoneRoll(TransInfo *t); static void applyBoneRoll(TransInfo *t, const int mval[2]); +static void initEdgeSlide_ex(TransInfo *t, bool use_double_side); static void initEdgeSlide(TransInfo *t); static eRedrawFlag handleEventEdgeSlide(TransInfo *t, const struct wmEvent *event); static void applyEdgeSlide(TransInfo *t, const int mval[2]); @@ -2229,8 +2230,11 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve initBoneEnvelope(t); break; case TFM_EDGE_SLIDE: - initEdgeSlide(t); + { + const bool use_double_side = (op ? !RNA_boolean_get(op->ptr, "single_side") : true); + initEdgeSlide_ex(t, use_double_side); break; + } case TFM_VERT_SLIDE: initVertSlide(t); break; @@ -5684,8 +5688,11 @@ static void calcEdgeSlide_mval_range( float projectMat[4][4]; BMBVHTree *btree; + /* only for use_calc_direction */ + float (*loop_dir)[3] = NULL, *loop_maxdist = NULL; + float mval_start[2], mval_end[2]; - float mval_dir[3], dist_best_sq, (*loop_dir)[3], *loop_maxdist; + float mval_dir[3], dist_best_sq; BMIter iter; BMEdge *e; @@ -5715,9 +5722,11 @@ static void calcEdgeSlide_mval_range( zero_v3(mval_dir); dist_best_sq = -1.0f; - loop_dir = MEM_callocN(sizeof(float[3]) * loop_nr, "sv loop_dir"); - loop_maxdist = MEM_mallocN(sizeof(float) * loop_nr, "sv loop_maxdist"); - copy_vn_fl(loop_maxdist, loop_nr, -1.0f); + if (use_calc_direction) { + loop_dir = MEM_callocN(sizeof(float[3]) * loop_nr, "sv loop_dir"); + loop_maxdist = MEM_mallocN(sizeof(float) * loop_nr, "sv loop_maxdist"); + copy_vn_fl(loop_maxdist, loop_nr, -1.0f); + } BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { @@ -5773,11 +5782,13 @@ static void calcEdgeSlide_mval_range( sub_v3_v3v3(mval_dir, sco_b, sco_a); } - /* per loop direction */ - l_nr = sv_array[j].loop_nr; - if (loop_maxdist[l_nr] == -1.0f || dist_sq < loop_maxdist[l_nr]) { - loop_maxdist[l_nr] = dist_sq; - sub_v3_v3v3(loop_dir[l_nr], sco_b, sco_a); + if (use_calc_direction) { + /* per loop direction */ + l_nr = sv_array[j].loop_nr; + if (loop_maxdist[l_nr] == -1.0f || dist_sq < loop_maxdist[l_nr]) { + loop_maxdist[l_nr] = dist_sq; + sub_v3_v3v3(loop_dir[l_nr], sco_b, sco_a); + } } } } @@ -5795,6 +5806,9 @@ static void calcEdgeSlide_mval_range( SWAP(BMVert *, sv_array->v_side[0], sv_array->v_side[1]); } } + + MEM_freeN(loop_dir); + MEM_freeN(loop_maxdist); } /* possible all of the edge loops are pointing directly at the view */ @@ -5819,9 +5833,6 @@ static void calcEdgeSlide_mval_range( if (btree) { BKE_bmbvh_free(btree); } - - MEM_freeN(loop_dir); - MEM_freeN(loop_maxdist); } static void calcEdgeSlide_non_proportional( @@ -5870,7 +5881,7 @@ static void calcEdgeSlide_non_proportional( } } -static bool createEdgeSlideVerts(TransInfo *t) +static bool createEdgeSlideVerts_double_side(TransInfo *t) { BMEditMesh *em = BKE_editmesh_from_object(t->obedit); BMesh *bm = em->bm; @@ -6211,6 +6222,198 @@ static bool createEdgeSlideVerts(TransInfo *t) return true; } +/** + * A simple version of #createEdgeSlideVerts + * Which assumes the longest unselected. + */ +static bool createEdgeSlideVerts_single_side(TransInfo *t) +{ + BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMesh *bm = em->bm; + BMIter iter; + BMEdge *e; + BMVert *v; + TransDataEdgeSlideVert *sv_array; + int sv_tot; + int *sv_table; /* BMVert -> sv_array index */ + EdgeSlideData *sld = MEM_callocN(sizeof(*sld), "sld"); + float mval[2] = {(float)t->mval[0], (float)t->mval[1]}; + int i, j, loop_nr; + bool use_btree_disp = false; + View3D *v3d = NULL; + RegionView3D *rv3d = NULL; + + if (t->spacetype == SPACE_VIEW3D) { + /* background mode support */ + v3d = t->sa ? t->sa->spacedata.first : NULL; + rv3d = t->ar ? t->ar->regiondata : NULL; + } + + slide_origdata_init_flag(t, &sld->orig_data); + + sld->is_proportional = true; + sld->curr_sv_index = 0; + /* heppans to be best for single-sided */ + sld->flipped_vtx = true; + + /* ensure valid selection */ + j = 0; + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + float len_sq_max = -1.0f; + BMIter iter2; + BM_ITER_ELEM (e, &iter2, v, BM_EDGES_OF_VERT) { + if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) { + float len_sq = BM_edge_calc_length_squared(e); + if (len_sq > len_sq_max) { + len_sq_max = len_sq; + v->e = e; + } + } + } + + if (len_sq_max != -1.0f) { + j++; + } + } + BM_elem_index_set(v, i); /* set_inline */ + } + bm->elem_index_dirty &= ~BM_VERT; + + if (!j) { + return false; + } + + + sv_tot = j; + BLI_assert(sv_tot != 0); + /* over alloc */ + sv_array = MEM_callocN(sizeof(TransDataEdgeSlideVert) * bm->totvertsel, "sv_array"); + + /* same loop for all loops, weak but we dont connect loops in this case */ + loop_nr = 1; + + sv_table = MEM_mallocN(sizeof(*sv_table) * bm->totvert, __func__); + + j = 0; + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + sv_table[i] = -1; + if ((v->e != NULL) && (BM_elem_flag_test(v, BM_ELEM_SELECT))) { + if (BM_elem_flag_test(v->e, BM_ELEM_SELECT) == 0) { + TransDataEdgeSlideVert *sv; + sv = &sv_array[j]; + sv->v = v; + copy_v3_v3(sv->v_co_orig, v->co); + sv->v_side[0] = BM_edge_other_vert(v->e, v); + sub_v3_v3v3(sv->dir_side[0], sv->v_side[0]->co, v->co); + sv->loop_nr = 0; + sv_table[i] = j; + j += 1; + } + } + } + + /* check for wire vertices, + * interpolate the directions of wire verts between non-wire verts */ + if (sv_tot != bm->totvert) { + const int sv_tot_nowire = sv_tot; + TransDataEdgeSlideVert *sv_iter = sv_array; + int i; + for (i = 0; i < sv_tot_nowire; i++, sv_iter++) { + BMIter eiter; + BM_ITER_ELEM (e, &eiter, sv_iter->v, BM_EDGES_OF_VERT) { + /* walk over wire */ + TransDataEdgeSlideVert *sv_end = NULL; + BMEdge *e_step = e; + BMVert *v = sv_iter->v; + + j = sv_tot; + + while (1) { + BMVert *v_other = BM_edge_other_vert(e_step, v); + int endpoint = ( + (sv_table[BM_elem_index_get(v_other)] != -1) + + (BM_vert_is_edge_pair(v_other) == false)); + + if ((BM_elem_flag_test(e_step, BM_ELEM_SELECT) && + BM_elem_flag_test(v_other, BM_ELEM_SELECT)) && + (endpoint == 0)) + { + /* scan down the list */ + TransDataEdgeSlideVert *sv; + BLI_assert(sv_table[BM_elem_index_get(v_other)] == -1); + sv_table[BM_elem_index_get(v_other)] = j; + sv = &sv_array[j]; + sv->v = v_other; + copy_v3_v3(sv->v_co_orig, v_other->co); + copy_v3_v3(sv->dir_side[0], sv_iter->dir_side[0]); + j++; + + /* advance! */ + v = v_other; + e_step = BM_DISK_EDGE_NEXT(e_step, v_other); + } + else { + if ((endpoint == 2) && (sv_tot != j)) { + BLI_assert(BM_elem_index_get(v_other) != -1); + sv_end = &sv_array[sv_table[BM_elem_index_get(v_other)]]; + } + break; + } + } + + if (sv_end) { + int sv_tot_prev = sv_tot; + const float *co_src = sv_iter->v->co; + const float *co_dst = sv_end->v->co; + const float *dir_src = sv_iter->dir_side[0]; + const float *dir_dst = sv_end->dir_side[0]; + sv_tot = j; + + while (j-- != sv_tot_prev) { + float factor; + factor = line_point_factor_v3(sv_array[j].v->co, co_src, co_dst); + interp_v3_v3v3(sv_array[j].dir_side[0], dir_src, dir_dst, factor); + } + } + } + } + } + + /* EDBM_flag_disable_all(em, BM_ELEM_SELECT); */ + + sld->sv = sv_array; + sld->totsv = sv_tot; + + /* use for visibility checks */ + if (t->spacetype == SPACE_VIEW3D) { + v3d = t->sa ? t->sa->spacedata.first : NULL; + rv3d = t->ar ? t->ar->regiondata : NULL; + use_btree_disp = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); + } + + calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_btree_disp, false); + + /* create copies of faces for customdata projection */ + bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); + slide_origdata_init_data(t, &sld->orig_data); + slide_origdata_create_data(t, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv); + + if (rv3d) { + calcEdgeSlide_non_proportional(t, sld, mval); + } + + sld->em = em; + + sld->perc = 0.0f; + + t->customData = sld; + + MEM_freeN(sv_table); + + return true; +} + void projectEdgeSlideData(TransInfo *t, bool is_final) { EdgeSlideData *sld = t->customData; @@ -6247,15 +6450,23 @@ void freeEdgeSlideVerts(TransInfo *t) recalcData(t); } -static void initEdgeSlide(TransInfo *t) +static void initEdgeSlide_ex(TransInfo *t, bool use_double_side) { EdgeSlideData *sld; + bool ok; t->mode = TFM_EDGE_SLIDE; t->transform = applyEdgeSlide; t->handleEvent = handleEventEdgeSlide; - if (!createEdgeSlideVerts(t)) { + if (use_double_side) { + ok = createEdgeSlideVerts_double_side(t); + } + else { + ok = createEdgeSlideVerts_single_side(t); + } + + if (!ok) { t->state = TRANS_CANCEL; return; } @@ -6284,6 +6495,11 @@ static void initEdgeSlide(TransInfo *t) t->flag |= T_NO_CONSTRAINT | T_NO_PROJECT; } +static void initEdgeSlide(TransInfo *t) +{ + initEdgeSlide_ex(t, true); +} + static eRedrawFlag handleEventEdgeSlide(struct TransInfo *t, const struct wmEvent *event) { if (t->mode == TFM_EDGE_SLIDE) { diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index fe06f01e09c..e119ec66321 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -837,6 +837,8 @@ static void TRANSFORM_OT_mirror(struct wmOperatorType *ot) static void TRANSFORM_OT_edge_slide(struct wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Edge Slide"; ot->description = "Slide an edge loop along a mesh"; @@ -852,6 +854,9 @@ static void TRANSFORM_OT_edge_slide(struct wmOperatorType *ot) RNA_def_float_factor(ot->srna, "value", 0, -10.0f, 10.0f, "Factor", "", -1.0f, 1.0f); + prop = RNA_def_boolean(ot->srna, "single_side", false, "Single Side", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + Transform_Properties(ot, P_MIRROR | P_SNAP | P_CORRECT_UV); } |