diff options
author | Campbell Barton <ideasman42@gmail.com> | 2012-03-24 04:20:36 +0400 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2012-03-24 04:20:36 +0400 |
commit | 0c898514f0b1536e1b335f24a1408f70c0162c48 (patch) | |
tree | acc4a64b5a20f8a684d3f6175555db35fb4fdbec /source/blender/editors/mesh/editmesh_loopcut.c | |
parent | 888cc0d7acc4d831bfcf160c1d85cf15d6ab885a (diff) |
code cleanup: bmesh_ source files in our editor dir, they are the same as other editmesh_ files.
Diffstat (limited to 'source/blender/editors/mesh/editmesh_loopcut.c')
-rw-r--r-- | source/blender/editors/mesh/editmesh_loopcut.c | 586 |
1 files changed, 586 insertions, 0 deletions
diff --git a/source/blender/editors/mesh/editmesh_loopcut.c b/source/blender/editors/mesh/editmesh_loopcut.c new file mode 100644 index 00000000000..1696d4d80b3 --- /dev/null +++ b/source/blender/editors/mesh/editmesh_loopcut.c @@ -0,0 +1,586 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2007 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Joseph Eagar, Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/mesh/editmesh_loopcut.c + * \ingroup edmesh + */ + +#include <float.h> +#define _USE_MATH_DEFINES +#include <math.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> + +#include "DNA_ID.h" +#include "DNA_object_types.h" +#include "DNA_mesh_types.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_userdef_types.h" + +#include "MEM_guardedalloc.h" + +#include "PIL_time.h" + +#include "BLI_array.h" +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_dynstr.h" /*for WM_operator_pystring */ +#include "BLI_utildefines.h" + +#include "BKE_blender.h" +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_tessmesh.h" +#include "BKE_depsgraph.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" /* for paint cursor */ + +#include "IMB_imbuf_types.h" + +#include "ED_screen.h" +#include "ED_space_api.h" +#include "ED_view3d.h" +#include "ED_mesh.h" +#include "ED_numinput.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "mesh_intern.h" + +/* ringsel operator */ + +/* struct for properties used while drawing */ +typedef struct tringselOpData { + ARegion *ar; /* region that ringsel was activated in */ + void *draw_handle; /* for drawing preview loop */ + + float (*edges)[2][3]; + int totedge; + + ViewContext vc; + + Object *ob; + BMEditMesh *em; + BMEdge *eed; + NumInput num; + + int extend; + int do_cut; +} tringselOpData; + +/* modal loop selection drawing callback */ +static void ringsel_draw(const bContext *C, ARegion *UNUSED(ar), void *arg) +{ + View3D *v3d = CTX_wm_view3d(C); + tringselOpData *lcd = arg; + int i; + + if (lcd->totedge > 0) { + if(v3d && v3d->zbuf) + glDisable(GL_DEPTH_TEST); + + glPushMatrix(); + glMultMatrixf(lcd->ob->obmat); + + glColor3ub(255, 0, 255); + glBegin(GL_LINES); + for (i=0; i<lcd->totedge; i++) { + glVertex3fv(lcd->edges[i][0]); + glVertex3fv(lcd->edges[i][1]); + } + glEnd(); + + glPopMatrix(); + if(v3d && v3d->zbuf) + glEnable(GL_DEPTH_TEST); + } +} + +/* 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(BMEditMesh *em, BMEdge *lasteed, BMEdge *eed, + BMVert *lastv1, BMVert *v[2][2]) +{ + BMIter liter; + BMLoop *l, *l2; + int rev; + + l = eed->l; + + /*find correct order for v[1]*/ + if (!(BM_edge_in_face(l->f, eed) && BM_edge_in_face(l->f, lasteed))) { + BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_LOOP, l) { + if (BM_edge_in_face(l->f, eed) && BM_edge_in_face(l->f, lasteed)) + 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; + return; + } + + l2 = BM_face_other_edge_loop(l->f, l->e, 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_sel(tringselOpData *lcd, int previewlines, int select) +{ + BMEditMesh *em = lcd->em; + BMEdge *startedge = lcd->eed; + BMEdge *eed, *lasteed; + BMVert *v[2][2], *lastv1; + BMWalker walker; + float (*edges)[2][3] = NULL; + BLI_array_declare(edges); + float co[2][3]; + int i, tot=0; + + memset(v, 0, sizeof(v)); + + if (!startedge) + return; + + if (lcd->edges) { + MEM_freeN(lcd->edges); + lcd->edges = NULL; + lcd->totedge = 0; + } + + if (!lcd->extend) { + EDBM_flag_disable_all(lcd->em, BM_ELEM_SELECT); + } + + if (select) { + BMW_init(&walker, em->bm, BMW_EDGERING, + BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, + BMW_NIL_LAY); + + eed = BMW_begin(&walker, startedge); + for (; eed; eed=BMW_step(&walker)) { + BM_elem_select_set(em->bm, eed, TRUE); + } + BMW_end(&walker); + + return; + } + + BMW_init(&walker, em->bm, BMW_EDGERING, + BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, + BMW_NIL_LAY); + + eed = startedge = BMW_begin(&walker, startedge); + lastv1 = NULL; + for (lasteed=NULL; eed; eed=BMW_step(&walker)) { + if (lasteed) { + if (lastv1) { + v[1][0] = v[0][0]; + v[1][1] = v[0][1]; + } else { + v[1][0] = lasteed->v1; + v[1][1] = lasteed->v2; + lastv1 = lasteed->v1; + } + + edgering_find_order(em, lasteed, eed, lastv1, v); + lastv1 = v[0][0]; + + for(i=1;i<=previewlines;i++){ + co[0][0] = (v[0][1]->co[0] - v[0][0]->co[0])*(i/((float)previewlines+1))+v[0][0]->co[0]; + co[0][1] = (v[0][1]->co[1] - v[0][0]->co[1])*(i/((float)previewlines+1))+v[0][0]->co[1]; + co[0][2] = (v[0][1]->co[2] - v[0][0]->co[2])*(i/((float)previewlines+1))+v[0][0]->co[2]; + + co[1][0] = (v[1][1]->co[0] - v[1][0]->co[0])*(i/((float)previewlines+1))+v[1][0]->co[0]; + co[1][1] = (v[1][1]->co[1] - v[1][0]->co[1])*(i/((float)previewlines+1))+v[1][0]->co[1]; + co[1][2] = (v[1][1]->co[2] - v[1][0]->co[2])*(i/((float)previewlines+1))+v[1][0]->co[2]; + + BLI_array_growone(edges); + copy_v3_v3(edges[tot][0], co[0]); + copy_v3_v3(edges[tot][1], co[1]); + tot++; + } + } + lasteed = eed; + } + + if (lasteed != startedge && BM_edge_share_face_count(lasteed, startedge)) { + v[1][0] = v[0][0]; + v[1][1] = v[0][1]; + + edgering_find_order(em, lasteed, startedge, lastv1, v); + + for(i=1;i<=previewlines;i++){ + if (!v[0][0] || !v[0][1] || !v[1][0] || !v[1][1]) + continue; + + co[0][0] = (v[0][1]->co[0] - v[0][0]->co[0])*(i/((float)previewlines+1))+v[0][0]->co[0]; + co[0][1] = (v[0][1]->co[1] - v[0][0]->co[1])*(i/((float)previewlines+1))+v[0][0]->co[1]; + co[0][2] = (v[0][1]->co[2] - v[0][0]->co[2])*(i/((float)previewlines+1))+v[0][0]->co[2]; + + co[1][0] = (v[1][1]->co[0] - v[1][0]->co[0])*(i/((float)previewlines+1))+v[1][0]->co[0]; + co[1][1] = (v[1][1]->co[1] - v[1][0]->co[1])*(i/((float)previewlines+1))+v[1][0]->co[1]; + co[1][2] = (v[1][1]->co[2] - v[1][0]->co[2])*(i/((float)previewlines+1))+v[1][0]->co[2]; + + BLI_array_growone(edges); + copy_v3_v3(edges[tot][0], co[0]); + copy_v3_v3(edges[tot][1], co[1]); + tot++; + } + } + + BMW_end(&walker); + lcd->edges = edges; + lcd->totedge = tot; +} + +static void ringsel_find_edge(tringselOpData *lcd, int cuts) +{ + if (lcd->eed) { + edgering_sel(lcd, cuts, 0); + } + else if (lcd->edges) { + MEM_freeN(lcd->edges); + lcd->edges = NULL; + lcd->totedge = 0; + } +} + +static void ringsel_finish(bContext *C, wmOperator *op) +{ + tringselOpData *lcd= op->customdata; + int cuts= RNA_int_get(op->ptr, "number_cuts"); + + if (lcd->eed) { + BMEditMesh *em = lcd->em; + + edgering_sel(lcd, cuts, 1); + + if (lcd->do_cut) { + BM_mesh_esubdivideflag(lcd->ob, em->bm, BM_ELEM_SELECT, 0.0f, + 0.0f, 0, cuts, SUBDIV_SELECT_LOOPCUT, + SUBD_PATH, 0, FALSE, 0); + + /* force edge slide to edge select mode in in face select mode */ + if (em->selectmode & SCE_SELECT_FACE) { + if (em->selectmode == SCE_SELECT_FACE) + em->selectmode = SCE_SELECT_EDGE; + else + em->selectmode &= ~SCE_SELECT_FACE; + CTX_data_tool_settings(C)->selectmode= em->selectmode; + EDBM_selectmode_set(em); + + WM_event_add_notifier(C, NC_SCENE|ND_TOOLSETTINGS, CTX_data_scene(C)); + + WM_event_add_notifier(C, NC_SCENE|ND_TOOLSETTINGS, CTX_data_scene(C)); + } + + WM_event_add_notifier(C, NC_GEOM|ND_SELECT|ND_DATA, lcd->ob->data); + DAG_id_tag_update(lcd->ob->data, 0); + } + else { + + /* sets as active, useful for other tools */ + if(em->selectmode & SCE_SELECT_VERTEX) + EDBM_store_selection(em, lcd->eed->v1); /* low priority TODO, get vertrex close to mouse */ + if(em->selectmode & SCE_SELECT_EDGE) + EDBM_store_selection(em, lcd->eed); + + EDBM_selectmode_flush(lcd->em); + WM_event_add_notifier(C, NC_GEOM|ND_SELECT, lcd->ob->data); + } + } +} + +/* called when modal loop selection is done... */ +static void ringsel_exit(bContext *UNUSED(C), wmOperator *op) +{ + tringselOpData *lcd= op->customdata; + + /* deactivate the extra drawing stuff in 3D-View */ + ED_region_draw_cb_exit(lcd->ar->type, lcd->draw_handle); + + if (lcd->edges) + MEM_freeN(lcd->edges); + + ED_region_tag_redraw(lcd->ar); + + /* free the custom data */ + MEM_freeN(lcd); + op->customdata= NULL; +} + +/* called when modal loop selection gets set up... */ +static int ringsel_init (bContext *C, wmOperator *op, int do_cut) +{ + tringselOpData *lcd; + + /* alloc new customdata */ + lcd= op->customdata= MEM_callocN(sizeof(tringselOpData), "ringsel Modal Op Data"); + + /* 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 = BMEdit_FromObject(lcd->ob); + lcd->extend = do_cut ? 0 : RNA_boolean_get(op->ptr, "extend"); + lcd->do_cut = do_cut; + + initNumInput(&lcd->num); + lcd->num.idx_max = 0; + lcd->num.flag |= NUM_NO_NEGATIVE | NUM_NO_FRACTION; + + em_setup_viewcontext(C, &lcd->vc); + + ED_region_tag_redraw(lcd->ar); + + return 1; +} + +static int ringcut_cancel (bContext *C, wmOperator *op) +{ + /* this is just a wrapper around exit() */ + ringsel_exit(C, op); + return OPERATOR_CANCELLED; +} + +static int ringcut_invoke (bContext *C, wmOperator *op, wmEvent *evt) +{ + Object *obedit= CTX_data_edit_object(C); + tringselOpData *lcd; + BMEdge *edge; + int dist = 75; + + if(modifiers_isDeformedByLattice(obedit) || modifiers_isDeformedByArmature(obedit)) + BKE_report(op->reports, RPT_WARNING, "Loop cut doesn't work well on deformed edit mesh display"); + + view3d_operator_needs_opengl(C); + + if (!ringsel_init(C, op, 1)) + return OPERATOR_CANCELLED; + + /* add a modal handler for this operator - handles loop selection */ + WM_event_add_modal_handler(C, op); + + lcd = op->customdata; + lcd->vc.mval[0] = evt->mval[0]; + lcd->vc.mval[1] = evt->mval[1]; + + edge = EDBM_findnearestedge(&lcd->vc, &dist); + if (edge != lcd->eed) { + lcd->eed = edge; + ringsel_find_edge(lcd, 1); + } + ED_area_headerprint(CTX_wm_area(C), "Select a ring to be cut, use mouse-wheel or page-up/down for number of cuts"); + + return OPERATOR_RUNNING_MODAL; +} + +static int loopcut_modal (bContext *C, wmOperator *op, wmEvent *event) +{ + int cuts= RNA_int_get(op->ptr,"number_cuts"); + tringselOpData *lcd= op->customdata; + int show_cuts = 0; + + view3d_operator_needs_opengl(C); + + switch (event->type) { + case RETKEY: + case LEFTMOUSE: /* confirm */ // XXX hardcoded + if (event->val == KM_PRESS) { + /* finish */ + ED_region_tag_redraw(lcd->ar); + + ringsel_finish(C, op); + ringsel_exit(C, op); + + ED_area_headerprint(CTX_wm_area(C), NULL); + + return OPERATOR_FINISHED; + } + + ED_region_tag_redraw(lcd->ar); + break; + case RIGHTMOUSE: /* abort */ // XXX hardcoded + ED_region_tag_redraw(lcd->ar); + ringsel_exit(C, op); + ED_area_headerprint(CTX_wm_area(C), NULL); + + return OPERATOR_FINISHED; + case ESCKEY: + if (event->val == KM_RELEASE) { + /* cancel */ + ED_region_tag_redraw(lcd->ar); + ED_area_headerprint(CTX_wm_area(C), NULL); + + return ringcut_cancel(C, op); + } + + ED_region_tag_redraw(lcd->ar); + break; + case PADPLUSKEY: + case PAGEUPKEY: + case WHEELUPMOUSE: /* change number of cuts */ + if (event->val == KM_RELEASE) + break; + + cuts++; + RNA_int_set(op->ptr,"number_cuts",cuts); + ringsel_find_edge(lcd, cuts); + show_cuts = TRUE; + + ED_region_tag_redraw(lcd->ar); + break; + case PADMINUS: + case PAGEDOWNKEY: + case WHEELDOWNMOUSE: /* change number of cuts */ + if (event->val == KM_RELEASE) + break; + + cuts=MAX2(cuts-1,1); + RNA_int_set(op->ptr,"number_cuts",cuts); + ringsel_find_edge(lcd, cuts); + show_cuts = TRUE; + + ED_region_tag_redraw(lcd->ar); + break; + case MOUSEMOVE: { /* mouse moved somewhere to select another loop */ + int dist = 75; + BMEdge *edge; + + lcd->vc.mval[0] = event->mval[0]; + lcd->vc.mval[1] = event->mval[1]; + edge = EDBM_findnearestedge(&lcd->vc, &dist); + + if (edge != lcd->eed) { + lcd->eed = edge; + ringsel_find_edge(lcd, cuts); + } + + ED_region_tag_redraw(lcd->ar); + break; + } + } + + /* using the keyboard to input the number of cuts */ + if (event->val==KM_PRESS) { + float value; + + if (handleNumInput(&lcd->num, event)) { + applyNumInput(&lcd->num, &value); + + cuts= CLAMPIS(value, 1, 130); + + RNA_int_set(op->ptr,"number_cuts",cuts); + ringsel_find_edge(lcd, cuts); + show_cuts = TRUE; + + ED_region_tag_redraw(lcd->ar); + } + } + + if (show_cuts) { + char buf[64]; + BLI_snprintf(buf, sizeof(buf), "Number of Cuts: %d", cuts); + ED_area_headerprint(CTX_wm_area(C), buf); + } + + /* keep going until the user confirms */ + return OPERATOR_RUNNING_MODAL; +} + +/* for bmesh this tool is in bmesh_select.c */ +#if 0 + +void MESH_OT_edgering_select (wmOperatorType *ot) +{ + /* description */ + ot->name = "Edge Ring Select"; + ot->idname = "MESH_OT_edgering_select"; + ot->description = "Select an edge ring"; + + /* callbacks */ + ot->invoke = ringsel_invoke; + ot->poll = ED_operator_editmesh_region_view3d; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection"); +} + +#endif + +void MESH_OT_loopcut (wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* description */ + ot->name = "Loop Cut"; + ot->idname = "MESH_OT_loopcut"; + ot->description = "Add a new loop between existing loops"; + + /* callbacks */ + ot->invoke = ringcut_invoke; + ot->modal = loopcut_modal; + ot->cancel = ringcut_cancel; + ot->poll = ED_operator_editmesh_region_view3d; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING; + + /* properties */ + prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "Number of Cuts", "", 1, 10); + /* avoid re-using last var because it can cause _very_ high poly meshes and annoy users (or worse crash) */ + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} |