diff options
Diffstat (limited to 'source/blender/editors/mesh/editmesh_loopcut.c')
-rw-r--r-- | source/blender/editors/mesh/editmesh_loopcut.c | 467 |
1 files changed, 161 insertions, 306 deletions
diff --git a/source/blender/editors/mesh/editmesh_loopcut.c b/source/blender/editors/mesh/editmesh_loopcut.c index 34951a24805..b69609a9fa8 100644 --- a/source/blender/editors/mesh/editmesh_loopcut.c +++ b/source/blender/editors/mesh/editmesh_loopcut.c @@ -38,14 +38,18 @@ #include "BLT_translation.h" +#include "DNA_mesh_types.h" + #include "BKE_context.h" #include "BKE_modifier.h" #include "BKE_report.h" #include "BKE_editmesh.h" -#include "BKE_DerivedMesh.h" #include "BKE_unit.h" +#include "BKE_layer.h" -#include "BIF_gl.h" +#include "GPU_immediate.h" +#include "GPU_matrix.h" +#include "GPU_state.h" #include "UI_interface.h" @@ -62,6 +66,9 @@ #include "WM_api.h" #include "WM_types.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + #include "mesh_intern.h" /* own include */ #define SUBD_SMOOTH_MAX 4.0f @@ -74,17 +81,21 @@ typedef struct RingSelOpData { ARegion *ar; /* region that ringsel was activated in */ void *draw_handle; /* for drawing preview loop */ - float (*edges)[2][3]; - int totedge; - - float (*points)[3]; - int totpoint; + struct EditMesh_PreSelEdgeRing *presel_edgering; ViewContext vc; + Depsgraph *depsgraph; + + Object **objects; + uint objects_len; + + /* These values switch objects based on the object under the cursor. */ + uint ob_index; Object *ob; BMEditMesh *em; BMEdge *eed; + NumInput num; bool extend; @@ -95,290 +106,32 @@ typedef struct RingSelOpData { } RingSelOpData; /* modal loop selection drawing callback */ -static void ringsel_draw(const bContext *C, ARegion *UNUSED(ar), void *arg) +static void ringsel_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg) { - View3D *v3d = CTX_wm_view3d(C); RingSelOpData *lcd = arg; - - if ((lcd->totedge > 0) || (lcd->totpoint > 0)) { - if (v3d && v3d->zbuf) - glDisable(GL_DEPTH_TEST); - - glPushMatrix(); - glMultMatrixf(lcd->ob->obmat); - - glColor3ub(255, 0, 255); - if (lcd->totedge > 0) { - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(3, GL_FLOAT, 0, lcd->edges); - glDrawArrays(GL_LINES, 0, lcd->totedge * 2); - glDisableClientState(GL_VERTEX_ARRAY); - } - - if (lcd->totpoint > 0) { - glPointSize(3.0f); - - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(3, GL_FLOAT, 0, lcd->points); - glDrawArrays(GL_POINTS, 0, lcd->totpoint); - glDisableClientState(GL_VERTEX_ARRAY); - } - - glPopMatrix(); - if (v3d && v3d->zbuf) - glEnable(GL_DEPTH_TEST); - } + EDBM_preselect_edgering_draw(lcd->presel_edgering, lcd->ob->obmat); } -/* given two opposite edges in a face, finds the ordering of their vertices so - * that cut preview lines won't cross each other */ -static void edgering_find_order(BMEdge *lasteed, BMEdge *eed, - BMVert *lastv1, BMVert *v[2][2]) +static void edgering_select(RingSelOpData *lcd) { - BMIter liter; - BMLoop *l, *l2; - int rev; - - l = eed->l; - - /* find correct order for v[1] */ - if (!(BM_edge_in_face(eed, l->f) && BM_edge_in_face(lasteed, l->f))) { - BM_ITER_ELEM (l, &liter, l, BM_LOOPS_OF_LOOP) { - if (BM_edge_in_face(eed, l->f) && BM_edge_in_face(lasteed, l->f)) - break; - } - } - - /* this should never happen */ - if (!l) { - v[0][0] = eed->v1; - v[0][1] = eed->v2; - v[1][0] = lasteed->v1; - v[1][1] = lasteed->v2; + if (!lcd->eed) { return; } - l2 = BM_loop_other_edge_loop(l, eed->v1); - rev = (l2 == l->prev); - while (l2->v != lasteed->v1 && l2->v != lasteed->v2) { - l2 = rev ? l2->prev : l2->next; - } - - if (l2->v == lastv1) { - v[0][0] = eed->v1; - v[0][1] = eed->v2; - } - else { - v[0][0] = eed->v2; - v[0][1] = eed->v1; - } -} - -static void edgering_vcos_get(DerivedMesh *dm, BMVert *v[2][2], float r_cos[2][2][3]) -{ - if (dm) { - int j, k; - for (j = 0; j < 2; j++) { - for (k = 0; k < 2; k++) { - dm->getVertCo(dm, BM_elem_index_get(v[j][k]), r_cos[j][k]); - } - } - } - else { - int j, k; - for (j = 0; j < 2; j++) { - for (k = 0; k < 2; k++) { - copy_v3_v3(r_cos[j][k], v[j][k]->co); - } - } - } -} - -static void edgering_vcos_get_pair(DerivedMesh *dm, BMVert *v[2], float r_cos[2][3]) -{ - if (dm) { - int j; - for (j = 0; j < 2; j++) { - dm->getVertCo(dm, BM_elem_index_get(v[j]), r_cos[j]); - } - } - else { - int j; - for (j = 0; j < 2; j++) { - copy_v3_v3(r_cos[j], v[j]->co); - } - } -} - -static void edgering_preview_free(RingSelOpData *lcd) -{ - MEM_SAFE_FREE(lcd->edges); - lcd->totedge = 0; - - MEM_SAFE_FREE(lcd->points); - lcd->totpoint = 0; -} - -static void edgering_preview_calc_edges(RingSelOpData *lcd, DerivedMesh *dm, const int previewlines) -{ - BMesh *bm = lcd->em->bm; - BMWalker walker; - BMEdge *eed_start = lcd->eed; - BMEdge *eed, *eed_last; - BMVert *v[2][2] = {{NULL}}, *v_last; - float (*edges)[2][3] = NULL; - BLI_Stack *edge_stack; - - int i, tot = 0; - - BMW_init(&walker, bm, BMW_EDGERING, - BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, - BMW_FLAG_TEST_HIDDEN, - BMW_NIL_LAY); - - - edge_stack = BLI_stack_new(sizeof(BMEdge *), __func__); - - eed_last = NULL; - for (eed = eed_last = BMW_begin(&walker, lcd->eed); eed; eed = BMW_step(&walker)) { - BLI_stack_push(edge_stack, &eed); - } - BMW_end(&walker); - - - eed_start = *(BMEdge **)BLI_stack_peek(edge_stack); - - edges = MEM_mallocN( - (sizeof(*edges) * (BLI_stack_count(edge_stack) + (eed_last != eed_start))) * previewlines, __func__); - - v_last = NULL; - eed_last = NULL; - - while (!BLI_stack_is_empty(edge_stack)) { - BLI_stack_pop(edge_stack, &eed); - - if (eed_last) { - if (v_last) { - v[1][0] = v[0][0]; - v[1][1] = v[0][1]; - } - else { - v[1][0] = eed_last->v1; - v[1][1] = eed_last->v2; - v_last = eed_last->v1; - } - - edgering_find_order(eed_last, eed, v_last, v); - v_last = v[0][0]; - - for (i = 1; i <= previewlines; i++) { - const float fac = (i / ((float)previewlines + 1)); - float v_cos[2][2][3]; - - edgering_vcos_get(dm, v, v_cos); - - interp_v3_v3v3(edges[tot][0], v_cos[0][0], v_cos[0][1], fac); - interp_v3_v3v3(edges[tot][1], v_cos[1][0], v_cos[1][1], fac); - tot++; - } - } - eed_last = eed; - } - - if ((eed_last != eed_start) && -#ifdef BMW_EDGERING_NGON - BM_edge_share_face_check(eed_last, eed_start) -#else - BM_edge_share_quad_check(eed_last, eed_start) -#endif - ) - { - v[1][0] = v[0][0]; - v[1][1] = v[0][1]; - - edgering_find_order(eed_last, eed_start, v_last, v); - - for (i = 1; i <= previewlines; i++) { - const float fac = (i / ((float)previewlines + 1)); - float v_cos[2][2][3]; - - if (!v[0][0] || !v[0][1] || !v[1][0] || !v[1][1]) { - continue; - } - - edgering_vcos_get(dm, v, v_cos); - - interp_v3_v3v3(edges[tot][0], v_cos[0][0], v_cos[0][1], fac); - interp_v3_v3v3(edges[tot][1], v_cos[1][0], v_cos[1][1], fac); - tot++; + if (!lcd->extend) { + for (uint ob_index = 0; ob_index < lcd->objects_len; ob_index++) { + Object *ob_iter = lcd->objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(ob_iter); + EDBM_flag_disable_all(em, BM_ELEM_SELECT); + DEG_id_tag_update(ob_iter->data, DEG_TAG_SELECT_UPDATE); + WM_main_add_notifier(NC_GEOM | ND_SELECT, ob_iter->data); } } - BLI_stack_free(edge_stack); - - lcd->edges = edges; - lcd->totedge = tot; -} - -static void edgering_preview_calc_points(RingSelOpData *lcd, DerivedMesh *dm, const int previewlines) -{ - float v_cos[2][3]; - float (*points)[3]; - int i, tot = 0; - - if (dm) { - BM_mesh_elem_table_ensure(lcd->em->bm, BM_VERT); - } - - points = MEM_mallocN(sizeof(*lcd->points) * previewlines, __func__); - - edgering_vcos_get_pair(dm, &lcd->eed->v1, v_cos); - - for (i = 1; i <= previewlines; i++) { - const float fac = (i / ((float)previewlines + 1)); - interp_v3_v3v3(points[tot], v_cos[0], v_cos[1], fac); - tot++; - } - - lcd->points = points; - lcd->totpoint = previewlines; -} - -static void edgering_preview_calc(RingSelOpData *lcd, const int previewlines) -{ - DerivedMesh *dm; - - BLI_assert(lcd->eed != NULL); - - edgering_preview_free(lcd); - - dm = EDBM_mesh_deform_dm_get(lcd->em); - if (dm) { - BM_mesh_elem_table_ensure(lcd->em->bm, BM_VERT); - } - - if (BM_edge_is_wire(lcd->eed)) { - edgering_preview_calc_points(lcd, dm, previewlines); - } - else { - edgering_preview_calc_edges(lcd, dm, previewlines); - } -} - -static void edgering_select(RingSelOpData *lcd) -{ BMEditMesh *em = lcd->em; BMEdge *eed_start = lcd->eed; BMWalker walker; BMEdge *eed; - - if (!eed_start) - return; - - if (!lcd->extend) { - EDBM_flag_disable_all(lcd->em, BM_ELEM_SELECT); - } - BMW_init(&walker, em->bm, BMW_EDGERING, BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, BMW_FLAG_TEST_HIDDEN, @@ -393,10 +146,17 @@ static void edgering_select(RingSelOpData *lcd) static void ringsel_find_edge(RingSelOpData *lcd, const int previewlines) { if (lcd->eed) { - edgering_preview_calc(lcd, previewlines); + const float (*coords)[3] = NULL; + { + Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(lcd->depsgraph, lcd->ob->data); + if (me_eval->runtime.edit_data) { + coords = me_eval->runtime.edit_data->vertexCos; + } + } + EDBM_preselect_edgering_update_from_edge(lcd->presel_edgering, lcd->em->bm, lcd->eed, previewlines, coords); } else { - edgering_preview_free(lcd); + EDBM_preselect_edgering_clear(lcd->presel_edgering); } } @@ -467,6 +227,7 @@ static void ringsel_finish(bContext *C, wmOperator *op) BM_select_history_store(em->bm, lcd->eed); EDBM_selectmode_flush(lcd->em); + DEG_id_tag_update(lcd->ob->data, DEG_TAG_SELECT_UPDATE); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, lcd->ob->data); } } @@ -480,7 +241,9 @@ static void ringsel_exit(bContext *UNUSED(C), wmOperator *op) /* deactivate the extra drawing stuff in 3D-View */ ED_region_draw_cb_exit(lcd->ar->type, lcd->draw_handle); - edgering_preview_free(lcd); + EDBM_preselect_edgering_destroy(lcd->presel_edgering); + + MEM_freeN(lcd->objects); ED_region_tag_redraw(lcd->ar); @@ -499,11 +262,17 @@ static int ringsel_init(bContext *C, wmOperator *op, bool do_cut) /* alloc new customdata */ lcd = op->customdata = MEM_callocN(sizeof(RingSelOpData), "ringsel Modal Op Data"); + em_setup_viewcontext(C, &lcd->vc); + + lcd->depsgraph = CTX_data_depsgraph(C); + /* assign the drawing handle for drawing preview line... */ lcd->ar = CTX_wm_region(C); lcd->draw_handle = ED_region_draw_cb_activate(lcd->ar->type, ringsel_draw, lcd, REGION_DRAW_POST_VIEW); - lcd->ob = CTX_data_edit_object(C); - lcd->em = BKE_editmesh_from_object(lcd->ob); + lcd->presel_edgering = EDBM_preselect_edgering_create(); + /* Initialize once the cursor is over a mesh. */ + lcd->ob = NULL; + lcd->em = NULL; lcd->extend = do_cut ? false : RNA_boolean_get(op->ptr, "extend"); lcd->do_cut = do_cut; lcd->cuts = RNA_int_get(op->ptr, "number_cuts"); @@ -517,8 +286,6 @@ static int ringsel_init(bContext *C, wmOperator *op, bool do_cut) lcd->num.unit_type[0] = B_UNIT_NONE; lcd->num.unit_type[1] = B_UNIT_NONE; - em_setup_viewcontext(C, &lcd->vc); - ED_region_tag_redraw(lcd->ar); return 1; @@ -530,44 +297,102 @@ static void ringcut_cancel(bContext *C, wmOperator *op) ringsel_exit(C, op); } -static void loopcut_update_edge(RingSelOpData *lcd, BMEdge *e, const int previewlines) +static void loopcut_update_edge(RingSelOpData *lcd, uint ob_index, BMEdge *e, const int previewlines) { if (e != lcd->eed) { lcd->eed = e; + lcd->ob = lcd->vc.obedit; + lcd->ob_index = ob_index; + lcd->em = lcd->vc.em; ringsel_find_edge(lcd, previewlines); } + else if (e == NULL) { + lcd->ob = NULL; + lcd->em = NULL; + lcd->ob_index = UINT_MAX; + } } static void loopcut_mouse_move(RingSelOpData *lcd, const int previewlines) { - float dist = ED_view3d_select_dist_px(); - BMEdge *e = EDBM_edge_find_nearest(&lcd->vc, &dist); - loopcut_update_edge(lcd, e, previewlines); + struct { + Object *ob; + BMEdge *eed; + float dist; + int ob_index; + } best = { + .dist = ED_view3d_select_dist_px(), + }; + + for (uint ob_index = 0; ob_index < lcd->objects_len; ob_index++) { + Object *ob_iter = lcd->objects[ob_index]; + ED_view3d_viewcontext_init_object(&lcd->vc, ob_iter); + BMEdge *eed_test = EDBM_edge_find_nearest_ex(&lcd->vc, &best.dist, NULL, false, false, NULL); + if (eed_test) { + best.ob = ob_iter; + best.eed = eed_test; + best.ob_index = ob_index; + } + } + + if (best.eed) { + ED_view3d_viewcontext_init_object(&lcd->vc, best.ob); + } + + loopcut_update_edge(lcd, best.ob_index, best.eed, previewlines); } /* called by both init() and exec() */ static int loopcut_init(bContext *C, wmOperator *op, const wmEvent *event) { const bool is_interactive = (event != NULL); - Object *obedit = CTX_data_edit_object(C); - RingSelOpData *lcd; - if (modifiers_isDeformedByLattice(obedit) || modifiers_isDeformedByArmature(obedit)) - BKE_report(op->reports, RPT_WARNING, "Loop cut does not work well on deformed edit mesh display"); + /* Use for redo - intentionally wrap int to uint. */ + const struct { + uint ob_index; + uint e_index; + } exec_data = { + .ob_index = (uint)RNA_int_get(op->ptr, "object_index"), + .e_index = (uint)RNA_int_get(op->ptr, "edge_index"), + }; + + ViewLayer *view_layer = CTX_data_view_layer(C); + + uint objects_len; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode(view_layer, &objects_len); + + if (is_interactive) { + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob_iter = objects[ob_index]; + if (modifiers_isDeformedByLattice(ob_iter) || modifiers_isDeformedByArmature(ob_iter)) { + BKE_report(op->reports, RPT_WARNING, "Loop cut does not work well on deformed edit mesh display"); + break; + } + } + } view3d_operator_needs_opengl(C); /* for re-execution, check edge index is in range before we setup ringsel */ + bool ok = true; if (is_interactive == false) { - const int e_index = RNA_int_get(op->ptr, "edge_index"); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - if (UNLIKELY((e_index == -1) || (e_index >= em->bm->totedge))) { + if (exec_data.ob_index >= objects_len) { return OPERATOR_CANCELLED; + ok = false; + } + else { + Object *ob_iter = objects[exec_data.ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(ob_iter); + if (exec_data.e_index >= em->bm->totedge) { + ok = false; + } } } - if (!ringsel_init(C, op, true)) + if (!ok || !ringsel_init(C, op, true)) { + MEM_freeN(objects); return OPERATOR_CANCELLED; + } /* add a modal handler for this operator - handles loop selection */ if (is_interactive) { @@ -575,18 +400,24 @@ static int loopcut_init(bContext *C, wmOperator *op, const wmEvent *event) WM_event_add_modal_handler(C, op); } - lcd = op->customdata; + RingSelOpData *lcd = op->customdata; + + lcd->objects = objects; + lcd->objects_len = objects_len; if (is_interactive) { copy_v2_v2_int(lcd->vc.mval, event->mval); loopcut_mouse_move(lcd, is_interactive ? 1 : 0); } else { - const int e_index = RNA_int_get(op->ptr, "edge_index"); + + Object *ob_iter = objects[exec_data.ob_index]; + ED_view3d_viewcontext_init_object(&lcd->vc, ob_iter); + BMEdge *e; - BM_mesh_elem_table_ensure(lcd->em->bm, BM_EDGE); - e = BM_edge_at_index(lcd->em->bm, e_index); - loopcut_update_edge(lcd, e, 0); + BM_mesh_elem_table_ensure(lcd->vc.em->bm, BM_EDGE); + e = BM_edge_at_index(lcd->vc.em->bm, exec_data.e_index); + loopcut_update_edge(lcd, exec_data.ob_index, e, 0); } #ifdef USE_LOOPSLIDE_HACK @@ -605,8 +436,7 @@ static int loopcut_init(bContext *C, wmOperator *op, const wmEvent *event) #endif if (is_interactive) { - ScrArea *sa = CTX_wm_area(C); - ED_area_headerprint(sa, IFACE_("Select a ring to be cut, use mouse-wheel or page-up/down for number of cuts, " + ED_workspace_status_text(C, IFACE_("Select a ring to be cut, use mouse-wheel or page-up/down for number of cuts, " "hold Alt for smooth")); return OPERATOR_RUNNING_MODAL; } @@ -619,6 +449,25 @@ static int loopcut_init(bContext *C, wmOperator *op, const wmEvent *event) static int ringcut_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + /* When accessed as a tool, get the active edge from the preselection gizmo. */ + { + ARegion *ar = CTX_wm_region(C); + wmGizmoMap *gzmap = ar->gizmo_map; + wmGizmoGroup *gzgroup = gzmap ? WM_gizmomap_group_find(gzmap, "VIEW3D_GGT_mesh_preselect_edgering") : NULL; + if ((gzgroup != NULL) && gzgroup->gizmos.first) { + wmGizmo *gz = gzgroup->gizmos.first; + const int object_index = RNA_int_get(gz->ptr, "object_index"); + const int edge_index = RNA_int_get(gz->ptr, "edge_index"); + + if (object_index != -1 && edge_index != -1) { + RNA_int_set(op->ptr, "object_index", object_index); + RNA_int_set(op->ptr, "edge_index", edge_index); + return loopcut_init(C, op, NULL); + } + return OPERATOR_CANCELLED; + } + } + return loopcut_init(C, op, event); } @@ -631,11 +480,12 @@ static int loopcut_finish(RingSelOpData *lcd, bContext *C, wmOperator *op) { /* finish */ ED_region_tag_redraw(lcd->ar); - ED_area_headerprint(CTX_wm_area(C), NULL); + ED_workspace_status_text(C, NULL); if (lcd->eed) { /* set for redo */ BM_mesh_elem_index_ensure(lcd->em->bm, BM_EDGE); + RNA_int_set(op->ptr, "object_index", lcd->ob_index); RNA_int_set(op->ptr, "edge_index", BM_elem_index_get(lcd->eed)); /* execute */ @@ -686,14 +536,14 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) case RIGHTMOUSE: /* abort */ // XXX hardcoded ED_region_tag_redraw(lcd->ar); ringsel_exit(C, op); - ED_area_headerprint(CTX_wm_area(C), NULL); + ED_workspace_status_text(C, NULL); return OPERATOR_CANCELLED; case ESCKEY: if (event->val == KM_RELEASE) { /* cancel */ ED_region_tag_redraw(lcd->ar); - ED_area_headerprint(CTX_wm_area(C), NULL); + ED_workspace_status_text(C, NULL); ringcut_cancel(C, op); return OPERATOR_CANCELLED; @@ -798,7 +648,7 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) } BLI_snprintf(buf, sizeof(buf), IFACE_("Number of Cuts: %s, Smooth: %s (Alt)"), str_rep, str_rep + NUM_STR_REP_LEN); - ED_area_headerprint(CTX_wm_area(C), buf); + ED_workspace_status_text(C, buf); } /* keep going until the user confirms */ @@ -855,12 +705,17 @@ void MESH_OT_loopcut(wmOperatorType *ot) "Smoothness", "Smoothness factor", -SUBD_SMOOTH_MAX, SUBD_SMOOTH_MAX); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + WM_operatortype_props_advanced_begin(ot); + prop = RNA_def_property(ot->srna, "falloff", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_proportional_falloff_curve_only_items); RNA_def_property_enum_default(prop, PROP_INVSQUARE); RNA_def_property_ui_text(prop, "Falloff", "Falloff type the feather"); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + /* For redo only. */ + prop = RNA_def_int(ot->srna, "object_index", -1, -1, INT_MAX, "Object Index", "", 0, INT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); prop = RNA_def_int(ot->srna, "edge_index", -1, -1, INT_MAX, "Edge Index", "", 0, INT_MAX); RNA_def_property_flag(prop, PROP_HIDDEN); |