Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/mesh/knifetool.c')
-rwxr-xr-xsource/blender/editors/mesh/knifetool.c2314
1 files changed, 2314 insertions, 0 deletions
diff --git a/source/blender/editors/mesh/knifetool.c b/source/blender/editors/mesh/knifetool.c
new file mode 100755
index 00000000000..8e56a357487
--- /dev/null
+++ b/source/blender/editors/mesh/knifetool.c
@@ -0,0 +1,2314 @@
+/*
+ * ***** 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 *****
+ */
+
+#define _USE_MATH_DEFINES
+
+
+#include "MEM_guardedalloc.h"
+
+
+#include "BLI_blenlib.h"
+#include "BLI_array.h"
+#include "BLI_math.h"
+#include "BLI_rand.h"
+#include "BLI_smallhash.h"
+#include "BLI_scanfill.h"
+
+#include "BKE_DerivedMesh.h"
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h" /* for paint cursor */
+
+
+#include "ED_screen.h"
+#include "ED_space_api.h"
+#include "ED_view3d.h"
+#include "ED_mesh.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "DNA_scene_types.h"
+#include "DNA_object_types.h"
+#include "BKE_tessmesh.h"
+#include "BLI_editVert.h"
+
+#include "mesh_intern.h"
+
+/* this code here is kindof messy. . .I might need to eventually rework it - joeedh */
+
+#define MAXGROUP 30
+#define KMAXDIST 10 /* max mouse distance from edge before not detecting it */
+
+/* knifetool operator */
+typedef struct KnifeVert {
+ BMVert *v; /* non-NULL if this is an original vert */
+ ListBase edges;
+
+ float co[3], cageco[3], sco[3]; /* sco is screen coordinates for cageco */
+ short flag, draw, isface, inspace;
+} KnifeVert;
+
+typedef struct Ref {
+ struct Ref *next, *prev;
+ void *ref;
+} Ref;
+
+typedef struct KnifeEdge {
+ KnifeVert *v1, *v2;
+ BMFace *basef; /* face to restrict face fill to */
+ ListBase faces;
+ int draw;
+
+ BMEdge *e, *oe; /* non-NULL if this is an original edge */
+} KnifeEdge;
+
+typedef struct BMEdgeHit {
+ KnifeEdge *kfe;
+ float hit[3], cagehit[3];
+ float realhit[3]; /* used in midpoint mode */
+ float schit[3];
+ float l; /* lambda along cut line */
+ float perc; /* lambda along hit line */
+ KnifeVert *v; /* set if snapped to a vert */
+ BMFace *f;
+} BMEdgeHit;
+
+/* struct for properties used while drawing */
+typedef struct knifetool_opdata {
+ ARegion *ar; /* region that knifetool was activated in */
+ void *draw_handle; /* for drawing preview loop */
+ ViewContext vc;
+ bContext *C;
+
+ Object *ob;
+ BMEditMesh *em;
+
+ MemArena *arena;
+
+ GHash *origvertmap;
+ GHash *origedgemap;
+
+ GHash *kedgefacemap;
+
+ BMBVHTree *bmbvh;
+
+ BLI_mempool *kverts;
+ BLI_mempool *kedges;
+
+ float vthresh;
+ float ethresh;
+
+ float vertco[3], vertcage[3];
+ float prevco[3], prevcage[3];
+
+ /* used for drag-cutting */
+ BMEdgeHit *linehits;
+ int totlinehit;
+
+ /* if curedge is NULL, attach to curvert;
+ * if curvert is NULL, attach to curbmface,
+ * otherwise create null vert */
+ KnifeEdge *curedge, *prevedge;
+ KnifeVert *curvert, *prevvert;
+ BMFace *curbmface, *prevbmface;
+
+ int totkedge, totkvert, cutnr;
+
+ BLI_mempool *refs;
+
+ float projmat[4][4];
+ int is_ortho;
+ int cut_through;
+ float clipsta, clipend;
+
+ enum {
+ MODE_IDLE,
+ MODE_DRAGGING,
+ MODE_CONNECT,
+ MODE_PANNING
+ } mode;
+
+ int snap_midpoints, prevmode, extend;
+ int ignore_edge_snapping, ignore_vert_snapping;
+ int prevmval[2];
+
+ enum {
+ ANGLE_FREE,
+ ANGLE_0,
+ ANGLE_45,
+ ANGLE_90,
+ ANGLE_135
+ } angle_snapping;
+
+ int is_space, prev_is_space; /*1 if current cut location, vertco, isn't on the mesh */
+ float (*cagecos)[3];
+} knifetool_opdata;
+
+static ListBase *knife_get_face_kedges(knifetool_opdata *kcd, BMFace *f);
+
+static void knife_input_ray_cast(knifetool_opdata *kcd, const int mval_i[2],
+ float r_origin[3], float r_ray[3]);
+
+static void knife_project_v3(knifetool_opdata *kcd, const float co[3], float sco[3])
+{
+ ED_view3d_project_float(kcd->ar, co, sco, kcd->projmat);
+}
+
+static ListBase *knife_empty_list(knifetool_opdata *kcd) {
+ ListBase *lst;
+
+ lst = BLI_memarena_alloc(kcd->arena, sizeof(ListBase));
+ lst->first = lst->last = NULL;
+ return lst;
+}
+
+static void knife_append_list(knifetool_opdata *kcd, ListBase *lst, void *elem) {
+ Ref *ref;
+
+ ref = BLI_mempool_calloc(kcd->refs);
+ ref->ref = elem;
+ BLI_addtail(lst, ref);
+}
+
+static KnifeEdge *new_knife_edge(knifetool_opdata *kcd)
+{
+ kcd->totkedge++;
+ return BLI_mempool_calloc(kcd->kedges);
+}
+
+static void knife_add_to_vert_edges(knifetool_opdata *kcd, KnifeEdge* kfe)
+{
+ knife_append_list(kcd, &kfe->v1->edges, kfe);
+ knife_append_list(kcd, &kfe->v2->edges, kfe);
+}
+
+static KnifeVert *new_knife_vert(knifetool_opdata *kcd, float *co, float *cageco)
+{
+ KnifeVert *kfv = BLI_mempool_calloc(kcd->kverts);
+
+ kcd->totkvert++;
+
+ copy_v3_v3(kfv->co, co);
+ copy_v3_v3(kfv->cageco, cageco);
+ copy_v3_v3(kfv->sco, co);
+
+ knife_project_v3(kcd, kfv->co, kfv->sco);
+
+ return kfv;
+}
+
+/* get a KnifeVert wrapper for an existing BMVert */
+static KnifeVert *get_bm_knife_vert(knifetool_opdata *kcd, BMVert *v)
+{
+ KnifeVert *kfv = BLI_ghash_lookup(kcd->origvertmap, v);
+
+ if (!kfv) {
+ kfv = new_knife_vert(kcd, v->co, kcd->cagecos[BM_elem_index_get(v)]);
+ kfv->v = v;
+ BLI_ghash_insert(kcd->origvertmap, v, kfv);
+ }
+
+ return kfv;
+}
+
+/* get a KnifeEdge wrapper for an existing BMEdge */
+static KnifeEdge *get_bm_knife_edge(knifetool_opdata *kcd, BMEdge *e)
+{
+ KnifeEdge *kfe = BLI_ghash_lookup(kcd->origedgemap, e);
+ if (!kfe) {
+ BMIter iter;
+ BMFace *f;
+
+ kfe = new_knife_edge(kcd);
+ kfe->e = e;
+ kfe->v1 = get_bm_knife_vert(kcd, e->v1);
+ kfe->v2 = get_bm_knife_vert(kcd, e->v2);
+
+ knife_add_to_vert_edges(kcd, kfe);
+
+ BLI_ghash_insert(kcd->origedgemap, e, kfe);
+
+ BM_ITER(f, &iter, kcd->em->bm, BM_FACES_OF_EDGE, e) {
+ knife_append_list(kcd, &kfe->faces, f);
+
+ /* ensures the kedges lst for this f is initialized,
+ * it automatically adds kfe by itself */
+ knife_get_face_kedges(kcd, f);
+ }
+ }
+
+ return kfe;
+}
+
+static void knife_start_cut(knifetool_opdata *kcd)
+{
+ kcd->prevedge = kcd->curedge;
+ kcd->prevvert = kcd->curvert;
+ kcd->prevbmface = kcd->curbmface;
+ kcd->cutnr++;
+ kcd->prev_is_space = kcd->is_space;
+ kcd->is_space = 0;
+ kcd->prevmval[0] = kcd->vc.mval[0];
+ kcd->prevmval[1] = kcd->vc.mval[1];
+
+ copy_v3_v3(kcd->prevco, kcd->vertco);
+ copy_v3_v3(kcd->prevcage, kcd->vertcage);
+
+ if (kcd->prevvert == NULL && kcd->prevedge == NULL && is_zero_v3(kcd->prevcage)) {
+ /* Make prevcage a point on the view ray to mouse closest to a point on model: choose vertex 0 */
+ float origin[3], ray[3], co[3];
+ BMVert *v0;
+
+ knife_input_ray_cast(kcd, kcd->vc.mval, origin, ray);
+ add_v3_v3v3(co, origin, ray);
+ v0 = BM_vert_at_index(kcd->em->bm, 0);
+ if (v0) {
+ closest_to_line_v3(kcd->prevcage, v0->co, co, origin);
+ copy_v3_v3(kcd->prevco, kcd->prevcage);
+ copy_v3_v3(kcd->vertcage, kcd->prevcage);
+ copy_v3_v3(kcd->vertco, kcd->prevco);
+ }
+ }
+}
+
+static Ref *find_ref(ListBase *lb, void *ref)
+{
+ Ref *ref1;
+
+ for (ref1 = lb->first; ref1; ref1 = ref1->next) {
+ if (ref1->ref == ref)
+ return ref1;
+ }
+
+ return NULL;
+}
+
+static ListBase *knife_get_face_kedges(knifetool_opdata *kcd, BMFace *f)
+{
+ ListBase *lst = BLI_ghash_lookup(kcd->kedgefacemap, f);
+
+ if (!lst) {
+ BMIter iter;
+ BMEdge *e;
+
+ lst = knife_empty_list(kcd);
+
+ BM_ITER(e, &iter, kcd->em->bm, BM_EDGES_OF_FACE, f) {
+ knife_append_list(kcd, lst, get_bm_knife_edge(kcd, e));
+ }
+
+ BLI_ghash_insert(kcd->kedgefacemap, f, lst);
+ }
+
+ return lst;
+}
+
+/* finds the proper face to restrict face fill to */
+static void knife_find_basef(knifetool_opdata *kcd, KnifeEdge *kfe)
+{
+ if (!kfe->basef) {
+ Ref *r1, *r2, *r3, *r4;
+
+ if (kfe->v1->isface || kfe->v2->isface) {
+ if (kfe->v2->isface)
+ kfe->basef = kcd->curbmface;
+ else
+ kfe->basef = kcd->prevbmface;
+ }
+ else {
+ for (r1 = kfe->v1->edges.first; r1 && !kfe->basef; r1 = r1->next) {
+ KnifeEdge *ke1 = r1->ref;
+ for (r2 = ke1->faces.first; r2 && !kfe->basef; r2 = r2->next) {
+ for (r3 = kfe->v2->edges.first; r3 && !kfe->basef; r3 = r3->next) {
+ KnifeEdge *ke2 = r3->ref;
+
+ for (r4 = ke2->faces.first; r4 && !kfe->basef; r4 = r4->next) {
+ if (r2->ref == r4->ref) {
+ kfe->basef = r2->ref;
+ }
+ }
+ }
+ }
+ }
+ }
+ /* ok, at this point kfe->basef should be set if any valid possibility exists */
+ }
+}
+
+static void knife_edge_append_face(knifetool_opdata *kcd, KnifeEdge *kfe, BMFace *f)
+{
+ knife_append_list(kcd, knife_get_face_kedges(kcd, f), kfe);
+ knife_append_list(kcd, &kfe->faces, f);
+}
+
+static KnifeVert *knife_split_edge(knifetool_opdata *kcd, KnifeEdge *kfe, float co[3], KnifeEdge **newkfe_out)
+{
+ KnifeEdge *newkfe = new_knife_edge(kcd);
+ Ref *ref;
+ float perc, cageco[3];
+
+ perc = len_v3v3(co, kfe->v1->co) / len_v3v3(kfe->v1->co, kfe->v2->co);
+ interp_v3_v3v3(cageco, kfe->v1->cageco, kfe->v2->cageco, perc);
+
+ newkfe->v1 = kfe->v1;
+ newkfe->v2 = new_knife_vert(kcd, co, cageco);
+ newkfe->v2->draw = 1;
+ newkfe->basef = kfe->basef;
+
+ ref = find_ref(&kfe->v1->edges, kfe);
+ BLI_remlink(&kfe->v1->edges, ref);
+
+ kfe->v1 = newkfe->v2;
+ BLI_addtail(&kfe->v1->edges, ref);
+
+ for (ref = kfe->faces.first; ref; ref = ref->next)
+ knife_edge_append_face(kcd, newkfe, ref->ref);
+
+ knife_add_to_vert_edges(kcd, newkfe);
+
+ newkfe->draw = kfe->draw;
+ newkfe->e = kfe->e;
+
+ *newkfe_out = newkfe;
+
+ return newkfe->v2;
+}
+
+static void knife_add_single_cut(knifetool_opdata *kcd)
+{
+ KnifeEdge *kfe = new_knife_edge(kcd), *kfe2 = NULL, *kfe3 = NULL;
+
+ if (kcd->prevvert && kcd->prevvert == kcd->curvert)
+ return;
+ if (kcd->prevedge && kcd->prevedge == kcd->curedge)
+ return;
+
+ kfe->draw = 1;
+
+ if (kcd->prevvert) {
+ kfe->v1 = kcd->prevvert;
+ }
+ else if (kcd->prevedge) {
+ kfe->v1 = knife_split_edge(kcd, kcd->prevedge, kcd->prevco, &kfe2);
+ }
+ else {
+ kfe->v1 = new_knife_vert(kcd, kcd->prevco, kcd->prevco);
+ kfe->v1->draw = kfe->draw = !kcd->prev_is_space;
+ kfe->v1->inspace = kcd->prev_is_space;
+ kfe->draw = !kcd->prev_is_space;
+ kfe->v1->isface = 1;
+ }
+
+ if (kcd->curvert) {
+ kfe->v2 = kcd->curvert;
+ }
+ else if (kcd->curedge) {
+ kfe->v2 = knife_split_edge(kcd, kcd->curedge, kcd->vertco, &kfe3);
+
+ kcd->curvert = kfe->v2;
+ }
+ else {
+ kfe->v2 = new_knife_vert(kcd, kcd->vertco, kcd->vertco);
+ kfe->v2->draw = !kcd->is_space;
+ kfe->v2->isface = 1;
+ kfe->v2->inspace = kcd->is_space;
+
+ if (kcd->is_space)
+ kfe->draw = 0;
+
+ kcd->curvert = kfe->v2;
+ }
+
+ knife_find_basef(kcd, kfe);
+
+ knife_add_to_vert_edges(kcd, kfe);
+
+ if (kfe->basef && !find_ref(&kfe->faces, kfe->basef))
+ knife_edge_append_face(kcd, kfe, kfe->basef);
+
+ /* sanity check to make sure we're in the right edge/face lists */
+ if (kcd->curbmface) {
+ if (!find_ref(&kfe->faces, kcd->curbmface)) {
+ knife_edge_append_face(kcd, kfe, kcd->curbmface);
+ }
+
+ if (kcd->prevbmface && kcd->prevbmface != kcd->curbmface) {
+ if (!find_ref(&kfe->faces, kcd->prevbmface)) {
+ knife_edge_append_face(kcd, kfe, kcd->prevbmface);
+ }
+ }
+ }
+
+ /* set up for next cut */
+ kcd->prevbmface = kcd->curbmface;
+ kcd->prevvert = kcd->curvert;
+ kcd->prevedge = kcd->curedge;
+ copy_v3_v3(kcd->prevco, kcd->vertco);
+ copy_v3_v3(kcd->prevcage, kcd->vertcage);
+ kcd->prev_is_space = kcd->is_space;
+ kcd->prevmval[0] = kcd->vc.mval[0];
+ kcd->prevmval[1] = kcd->vc.mval[1];
+}
+
+static int verge_linehit(const void *vlh1, const void *vlh2)
+{
+ const BMEdgeHit *lh1 = vlh1, *lh2 = vlh2;
+
+ if (lh1->l < lh2->l) return -1;
+ else if (lh1->l > lh2->l) return 1;
+ else return 0;
+}
+
+static void knife_add_single_cut_through(knifetool_opdata *kcd,
+ KnifeVert *v1, KnifeVert *v2, BMFace *f)
+{
+ KnifeEdge *kfenew;
+
+ kfenew = new_knife_edge(kcd);
+ kfenew->draw = 1;
+ kfenew->basef = f;
+ kfenew->v1 = v1;
+ kfenew->v2 = v2;
+ kfenew->draw = 1;
+
+ knife_add_to_vert_edges(kcd, kfenew);
+
+ if (!find_ref(&kfenew->faces, f))
+ knife_edge_append_face(kcd, kfenew, f);
+}
+
+static void knife_get_vert_faces(knifetool_opdata *kcd, KnifeVert* kfv, BMFace *facef, ListBase *lst)
+{
+ BMIter bmiter;
+ BMFace *f;
+
+ if (kfv->isface && facef) {
+ knife_append_list(kcd, lst, facef);
+ }
+ else if (kfv->v) {
+ BMesh *bm = kcd->em->bm;
+ BM_ITER(f, &bmiter, bm, BM_FACES_OF_VERT, kfv->v) {
+ knife_append_list(kcd, lst, f);
+ }
+ }
+}
+
+static void knife_get_edge_faces(knifetool_opdata *kcd, KnifeEdge* kfe, ListBase *lst)
+{
+ BMIter bmiter;
+ BMFace *f;
+
+ if (kfe->e) {
+ BMesh *bm = kcd->em->bm;
+ BM_ITER(f, &bmiter, bm, BM_FACES_OF_EDGE, kfe->e) {
+ knife_append_list(kcd, lst, f);
+ }
+ }
+}
+
+/* BMESH_TODO: add more functionality to cut-through:
+ * - cutting "in face" (e.g., holes) should cut in all faces, not just visible one
+ * - perhaps improve O(n^2) algorithm used here */
+static void knife_cut_through(knifetool_opdata *kcd)
+{
+ BMEdgeHit *lh, *lh2;
+ BMFace *f;
+ KnifeEdge *kfe, *kfe2, *kfe3;
+ KnifeVert *v1, *v2, *firstv = NULL, *lastv = NULL;
+ ListBase firstfaces = {NULL, NULL}, lastfaces = { NULL, NULL};
+ Ref *r, *r2;
+ KnifeEdge **splitkfe;
+ int i, j, found;
+
+ if (!kcd->totlinehit) {
+ /* if no linehits then no interesting back face stuff to do */
+ knife_add_single_cut(kcd);
+ return;
+ }
+
+ qsort(kcd->linehits, kcd->totlinehit, sizeof(BMEdgeHit), verge_linehit);
+ splitkfe = MEM_callocN(kcd->totlinehit * sizeof(KnifeEdge *), "knife_cut_through");
+
+ if (kcd->prevvert) {
+ if (kcd->prevvert == kcd->curvert)
+ return;
+ firstv = kcd->prevvert;
+ knife_get_vert_faces(kcd, firstv, kcd->prevbmface, &firstfaces);
+ }
+ else if (kcd->prevedge) {
+ if (kcd->prevedge == kcd->curedge)
+ return;
+ firstv = knife_split_edge(kcd, kcd->prevedge, kcd->prevco, &kfe3);
+ knife_get_edge_faces(kcd, kcd->prevedge, &firstfaces);
+ }
+
+ if (kcd->curvert) {
+ lastv = kcd->curvert;
+ knife_get_vert_faces(kcd, lastv, kcd->curbmface, &lastfaces);
+ }
+ else if (kcd->curedge) {
+ lastv = knife_split_edge(kcd, kcd->curedge, kcd->vertco, &kfe3);
+ knife_get_edge_faces(kcd, kcd->curedge, &lastfaces);
+ }
+
+ if (firstv) {
+ /* For each face incident to firstv,
+ * find the first following linehit (if any) sharing that face and connect */
+ for (r = firstfaces.first; r; r = r->next ) {
+ f = r->ref;
+ found = 0;
+ for (j = 0, lh2 = kcd->linehits; j < kcd->totlinehit; j++, lh2++) {
+ kfe2 = lh2->kfe;
+ for (r2 = kfe2->faces.first; r2; r2 = r2->next) {
+ if (r2->ref == f) {
+ v2 = splitkfe[j] ? kfe2->v1 : knife_split_edge(kcd, kfe2, lh2->hit, &splitkfe[j]);
+ knife_add_single_cut_through(kcd, firstv, v2, f);
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (!found && lastv) {
+ for (r2 = lastfaces.first; r2; r2 = r2->next) {
+ if (r2->ref == f) {
+ knife_add_single_cut_through(kcd, firstv, lastv, f);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ for (i = 0, lh = kcd->linehits; i < kcd->totlinehit; i++, lh++) {
+ kfe = lh->kfe;
+
+ /* For each face attached to edge for this linehit,
+ * find the first following linehit (if any) sharing that face and connect */
+ for (r = kfe->faces.first; r; r = r->next) {
+ f = r->ref;
+ found = 0;
+ for (j = i + 1, lh2 = lh + 1; j < kcd->totlinehit; j++, lh2++) {
+ kfe2 = lh2->kfe;
+ for (r2 = kfe2->faces.first; r2; r2 = r2->next) {
+ if (r2->ref == f) {
+ v1 = splitkfe[i]? kfe->v1 : knife_split_edge(kcd, kfe, lh->hit, &splitkfe[i]);
+ v2 = splitkfe[j]? kfe2->v1 : knife_split_edge(kcd, kfe2, lh2->hit, &splitkfe[j]);
+ knife_add_single_cut_through(kcd, v1, v2, f);
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (!found && lastv) {
+ for (r2 = lastfaces.first; r2; r2 = r2->next) {
+ if (r2->ref == f) {
+ v1 = splitkfe[i]? kfe->v1 : knife_split_edge(kcd, kfe, lh->hit, &splitkfe[i]);
+ knife_add_single_cut_through(kcd, v1, lastv, f);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ MEM_freeN(splitkfe);
+ MEM_freeN(kcd->linehits);
+ kcd->linehits = NULL;
+ kcd->totlinehit = 0;
+
+ /* set up for next cut */
+ kcd->prevbmface = kcd->curbmface;
+ kcd->prevvert = kcd->curvert;
+ kcd->prevedge = kcd->curedge;
+ copy_v3_v3(kcd->prevco, kcd->vertco);
+ copy_v3_v3(kcd->prevcage, kcd->vertcage);
+ kcd->prev_is_space = kcd->is_space;
+ kcd->prevmval[0] = kcd->vc.mval[0];
+ kcd->prevmval[1] = kcd->vc.mval[1];
+}
+
+static void knife_add_cut(knifetool_opdata *kcd)
+{
+ /* BMEditMesh *em = kcd->em;*/ /* UNUSED */
+ knifetool_opdata oldkcd = *kcd;
+
+ if (kcd->cut_through) {
+ knife_cut_through(kcd);
+ }
+ else if (kcd->linehits) {
+ BMEdgeHit *lh, *lastlh, *firstlh;
+ int i;
+
+ qsort(kcd->linehits, kcd->totlinehit, sizeof(BMEdgeHit), verge_linehit);
+
+ lh = kcd->linehits;
+ lastlh = firstlh = NULL;
+ for (i = 0; i < kcd->totlinehit; i++, (lastlh = lh), lh++) {
+ BMFace *f = lastlh ? lastlh->f : lh->f;
+
+ if (lastlh && len_v3v3(lastlh->hit, lh->hit) == 0.0f) {
+ if (!firstlh)
+ firstlh = lastlh;
+ continue;
+ }
+ else if (lastlh && firstlh) {
+ if (firstlh->v || lastlh->v) {
+ KnifeVert *kfv = firstlh->v ? firstlh->v : lastlh->v;
+
+ kcd->prevvert = kfv;
+ copy_v3_v3(kcd->prevco, firstlh->hit);
+ copy_v3_v3(kcd->prevcage, firstlh->cagehit);
+ kcd->prevedge = NULL;
+ kcd->prevbmface = f;
+ }
+ lastlh = firstlh = NULL;
+ }
+
+ if (len_v3v3(kcd->prevcage, lh->realhit) < FLT_EPSILON * 80)
+ continue;
+ if (len_v3v3(kcd->vertcage, lh->realhit) < FLT_EPSILON * 80)
+ continue;
+
+ if (kcd->prev_is_space || kcd->is_space) {
+ kcd->prev_is_space = kcd->is_space = 0;
+ copy_v3_v3(kcd->prevco, lh->hit);
+ copy_v3_v3(kcd->prevcage, lh->cagehit);
+ kcd->prevedge = lh->kfe;
+ kcd->curbmface = lh->f;
+ continue;
+ }
+
+ kcd->is_space = 0;
+ kcd->curedge = lh->kfe;
+ kcd->curbmface = lh->f;
+ kcd->curvert = lh->v;
+ copy_v3_v3(kcd->vertco, lh->hit);
+ copy_v3_v3(kcd->vertcage, lh->cagehit);
+
+ knife_add_single_cut(kcd);
+ }
+
+ kcd->curbmface = oldkcd.curbmface;
+ kcd->curvert = oldkcd.curvert;
+ kcd->curedge = oldkcd.curedge;
+ kcd->is_space = oldkcd.is_space;
+ copy_v3_v3(kcd->vertco, oldkcd.vertco);
+ copy_v3_v3(kcd->vertcage, oldkcd.vertcage);
+
+ knife_add_single_cut(kcd);
+
+ MEM_freeN(kcd->linehits);
+ kcd->linehits = NULL;
+ kcd->totlinehit = 0;
+ }
+ else {
+ knife_add_single_cut(kcd);
+ }
+}
+
+static void knife_finish_cut(knifetool_opdata *UNUSED(kcd))
+{
+
+}
+
+static void knifetool_draw_angle_snapping(knifetool_opdata *kcd)
+{
+ bglMats mats;
+ double u[3], u1[2], u2[2], v1[3], v2[3], dx, dy;
+ double wminx, wminy, wmaxx, wmaxy;
+
+ /* make u the window coords of prevcage */
+ view3d_get_transformation(kcd->ar, kcd->vc.rv3d, kcd->ob, &mats);
+ gluProject(kcd->prevcage[0], kcd->prevcage[1], kcd->prevcage[2],
+ mats.modelview, mats.projection, mats.viewport,
+ &u[0], &u[1], &u[2]);
+
+ /* make u1, u2 the points on window going through u at snap angle */
+ wminx = kcd->ar->winrct.xmin;
+ wmaxx = kcd->ar->winrct.xmin + kcd->ar->winx;
+ wminy = kcd->ar->winrct.ymin;
+ wmaxy = kcd->ar->winrct.ymin + kcd->ar->winy;
+
+ switch (kcd->angle_snapping) {
+ case ANGLE_0:
+ u1[0] = wminx;
+ u2[0] = wmaxx;
+ u1[1] = u2[1] = u[1];
+ break;
+ case ANGLE_90:
+ u1[0] = u2[0] = u[0];
+ u1[1] = wminy;
+ u2[1] = wmaxy;
+ break;
+ case ANGLE_45:
+ /* clip against left or bottom */
+ dx = u[0] - wminx;
+ dy = u[1] - wminy;
+ if (dy > dx) {
+ u1[0] = wminx;
+ u1[1] = u[1] - dx;
+ }
+ else {
+ u1[0] = u[0] - dy;
+ u1[1] = wminy;
+ }
+ /* clip against right or top */
+ dx = wmaxx - u[0];
+ dy = wmaxy - u[1];
+ if (dy > dx) {
+ u2[0] = wmaxx;
+ u2[1] = u[1] + dx;
+ }
+ else {
+ u2[0] = u[0] + dy;
+ u2[1] = wmaxy;
+ }
+ break;
+ case ANGLE_135:
+ /* clip against right or bottom */
+ dx = wmaxx - u[0];
+ dy = u[1] - wminy;
+ if (dy > dx) {
+ u1[0] = wmaxx;
+ u1[1] = u[1] - dx;
+ }
+ else {
+ u1[0] = u[0] + dy;
+ u1[1] = wminy;
+ }
+ /* clip against left or top */
+ dx = u[0] - wminx;
+ dy = wmaxy - u[1];
+ if (dy > dx) {
+ u2[0] = wminx;
+ u2[1] = u[1] + dx;
+ }
+ else {
+ u2[0] = u[0] - dy;
+ u2[1] = wmaxy;
+ }
+ break;
+ default:
+ return;
+ }
+
+ /* unproject u1 and u2 back into object space */
+ gluUnProject(u1[0], u1[1], 0.0,
+ mats.modelview, mats.projection, mats.viewport,
+ &v1[0], &v1[1], &v1[2]);
+ gluUnProject(u2[0], u2[1], 0.0,
+ mats.modelview, mats.projection, mats.viewport,
+ &v2[0], &v2[1], &v2[2]);
+
+ glColor3f(0.6, 0.6, 0.6);
+ glLineWidth(2.0);
+ glBegin(GL_LINES);
+ glVertex3dv(v1);
+ glVertex3dv(v2);
+ glEnd();
+}
+
+/* modal loop selection drawing callback */
+static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg)
+{
+ knifetool_opdata *kcd = arg;
+
+ glDisable(GL_DEPTH_TEST);
+
+ glPolygonOffset(1.0f, 1.0f);
+
+ glPushMatrix();
+ glMultMatrixf(kcd->ob->obmat);
+
+ if (kcd->mode == MODE_DRAGGING) {
+ if (kcd->angle_snapping != ANGLE_FREE)
+ knifetool_draw_angle_snapping(kcd);
+
+ glColor3f(0.1, 0.1, 0.1);
+ glLineWidth(2.0);
+
+ glBegin(GL_LINES);
+ glVertex3fv(kcd->prevcage);
+ glVertex3fv(kcd->vertcage);
+ glEnd();
+
+ glLineWidth(1.0);
+ }
+
+ if (kcd->curedge) {
+ glColor3f(0.5, 0.3, 0.15);
+ glLineWidth(2.0);
+
+ glBegin(GL_LINES);
+ glVertex3fv(kcd->curedge->v1->cageco);
+ glVertex3fv(kcd->curedge->v2->cageco);
+ glEnd();
+
+ glLineWidth(1.0);
+ }
+ else if (kcd->curvert) {
+ glColor3f(0.8, 0.2, 0.1);
+ glPointSize(11);
+
+ glBegin(GL_POINTS);
+ glVertex3fv(kcd->vertcage);
+ glEnd();
+ }
+
+ if (kcd->curbmface) {
+ glColor3f(0.1, 0.8, 0.05);
+ glPointSize(9);
+
+ glBegin(GL_POINTS);
+ glVertex3fv(kcd->vertcage);
+ glEnd();
+ }
+
+ if (kcd->totlinehit > 0) {
+ BMEdgeHit *lh;
+ int i;
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ /* draw any snapped verts first */
+ glColor4f(0.8, 0.2, 0.1, 0.4);
+ glPointSize(11);
+ glBegin(GL_POINTS);
+ lh = kcd->linehits;
+ for (i = 0; i < kcd->totlinehit; i++, lh++) {
+ float sv1[3], sv2[3];
+
+ knife_project_v3(kcd, lh->kfe->v1->cageco, sv1);
+ knife_project_v3(kcd, lh->kfe->v2->cageco, sv2);
+ knife_project_v3(kcd, lh->cagehit, lh->schit);
+
+ if (len_v2v2(lh->schit, sv1) < kcd->vthresh / 4.0f) {
+ copy_v3_v3(lh->cagehit, lh->kfe->v1->cageco);
+ glVertex3fv(lh->cagehit);
+ lh->v = lh->kfe->v1;
+ }
+ else if (len_v2v2(lh->schit, sv2) < kcd->vthresh / 4.0f) {
+ copy_v3_v3(lh->cagehit, lh->kfe->v2->cageco);
+ glVertex3fv(lh->cagehit);
+ lh->v = lh->kfe->v2;
+ }
+ }
+ glEnd();
+
+ /* now draw the rest */
+ glColor4f(0.1, 0.8, 0.05, 0.4);
+ glPointSize(7);
+ glBegin(GL_POINTS);
+ lh = kcd->linehits;
+ for (i = 0; i < kcd->totlinehit; i++, lh++) {
+ glVertex3fv(lh->cagehit);
+ }
+ glEnd();
+ glDisable(GL_BLEND);
+ }
+
+ if (kcd->totkedge > 0) {
+ BLI_mempool_iter iter;
+ KnifeEdge *kfe;
+
+ glLineWidth(1.0);
+ glBegin(GL_LINES);
+
+ BLI_mempool_iternew(kcd->kedges, &iter);
+ for (kfe = BLI_mempool_iterstep(&iter); kfe; kfe = BLI_mempool_iterstep(&iter)) {
+ if (!kfe->draw)
+ continue;
+
+ glColor3f(0.2, 0.2, 0.2);
+
+ glVertex3fv(kfe->v1->cageco);
+ glVertex3fv(kfe->v2->cageco);
+ }
+
+ glEnd();
+ glLineWidth(1.0);
+ }
+
+ if (kcd->totkvert > 0) {
+ BLI_mempool_iter iter;
+ KnifeVert *kfv;
+
+ glPointSize(5.0);
+
+ glBegin(GL_POINTS);
+ BLI_mempool_iternew(kcd->kverts, &iter);
+ for (kfv = BLI_mempool_iterstep(&iter); kfv; kfv = BLI_mempool_iterstep(&iter)) {
+ if (!kfv->draw)
+ continue;
+
+ glColor3f(0.6, 0.1, 0.2);
+
+ glVertex3fv(kfv->cageco);
+ }
+
+ glEnd();
+ }
+
+ glPopMatrix();
+ glEnable(GL_DEPTH_TEST);
+}
+
+/* do we need to keep these functions? - campbell */
+
+static int UNUSED_FUNCTION(kfe_vert_in_edge)(KnifeEdge *e, KnifeVert *v)
+{
+ return e->v1 == v || e->v2 == v;
+}
+
+static int UNUSED_FUNCTION(point_on_line)(float p[3], float v1[3], float v2[3])
+{
+ float d = dist_to_line_segment_v3(p, v1, v2);
+ if (d < 0.01) {
+ d = len_v3v3(v1, v2);
+ if (d == 0.0)
+ return 0;
+
+ d = len_v3v3(p, v1) / d;
+
+ if (d >= -FLT_EPSILON * 10 || d <= 1.0 + FLT_EPSILON * 10)
+ return 1;
+ }
+
+ return 0;
+}
+
+static float len_v3_tri_side_max(const float v1[3], const float v2[3], const float v3[3])
+{
+ const float s1 = len_v3v3(v1, v2);
+ const float s2 = len_v3v3(v2, v3);
+ const float s3 = len_v3v3(v3, v1);
+
+ return MAX3(s1, s2, s3);
+}
+
+static BMEdgeHit *knife_edge_tri_isect(knifetool_opdata *kcd, BMBVHTree *bmtree,
+ const float v1[3], const float v2[3], const float v3[3],
+ SmallHash *ehash, bglMats *mats, int *count)
+{
+ BVHTree *tree2 = BLI_bvhtree_new(3, FLT_EPSILON * 4, 8, 8), *tree = BMBVH_BVHTree(bmtree);
+ BMEdgeHit *edges = NULL;
+ BLI_array_declare(edges);
+ BVHTreeOverlap *results, *result;
+ BMLoop **ls;
+ float cos[9], uv[3], lambda;
+ unsigned int tot = 0;
+ int i, j;
+
+ /* for comparing distances, error of intersection depends on triangle scale.
+ * need to scale down before squaring for accurate comparison */
+ const float depsilon = 50 * FLT_EPSILON * len_v3_tri_side_max(v1, v2, v3);
+ const float depsilon_squared = depsilon * depsilon;
+
+ copy_v3_v3(cos + 0, v1);
+ copy_v3_v3(cos + 3, v2);
+ copy_v3_v3(cos + 6, v3);
+
+ BLI_bvhtree_insert(tree2, 0, cos, 3);
+ BLI_bvhtree_balance(tree2);
+
+ result = results = BLI_bvhtree_overlap(tree, tree2, &tot);
+
+ for (i = 0; i < tot; i++, result++) {
+ float p[3];
+
+ ls = (BMLoop**)kcd->em->looptris[result->indexA];
+
+ for (j = 0; j < 3; j++) {
+ BMLoop *l1 = ls[j];
+ BMFace *hitf;
+ ListBase *lst = knife_get_face_kedges(kcd, l1->f);
+ Ref *ref;
+
+ for (ref = lst->first; ref; ref = ref->next) {
+ KnifeEdge *kfe = ref->ref;
+
+ //if (kfe == kcd->curedge || kfe == kcd->prevedge)
+ // continue;
+
+ if (isect_line_tri_v3(kfe->v1->cageco, kfe->v2->cageco, v1, v2, v3, &lambda, uv)) {
+ float no[3], view[3], sp[3];
+
+ interp_v3_v3v3(p, kfe->v1->cageco, kfe->v2->cageco, lambda);
+
+ if (kcd->curvert && len_squared_v3v3(kcd->curvert->cageco, p) < depsilon_squared)
+ continue;
+ if (kcd->prevvert && len_squared_v3v3(kcd->prevvert->cageco, p) < depsilon_squared)
+ continue;
+ if ( len_squared_v3v3(kcd->prevcage, p) < depsilon_squared ||
+ len_squared_v3v3(kcd->vertcage, p) < depsilon_squared)
+ {
+ continue;
+ }
+
+ knife_project_v3(kcd, p, sp);
+ view3d_unproject(mats, view, sp[0], sp[1], 0.0f);
+ mul_m4_v3(kcd->ob->imat, view);
+
+ if (kcd->cut_through) {
+ hitf = FALSE;
+ }
+ else {
+ /* check if this point is visible in the viewport */
+ sub_v3_v3(view, p);
+ normalize_v3(view);
+
+ copy_v3_v3(no, view);
+ mul_v3_fl(no, 0.003);
+
+ /* go towards view a bit */
+ add_v3_v3(p, no);
+
+ /* ray cast */
+ hitf = BMBVH_RayCast(bmtree, p, no, NULL, NULL);
+ }
+
+ /* ok, if visible add the new point */
+ if (!hitf && !BLI_smallhash_haskey(ehash, (intptr_t)kfe)) {
+ BMEdgeHit hit;
+
+ if ( len_squared_v3v3(p, kcd->vertco) < depsilon_squared ||
+ len_squared_v3v3(p, kcd->prevco) < depsilon_squared)
+ {
+ continue;
+ }
+
+ hit.kfe = kfe;
+ hit.v = NULL;
+
+ knife_find_basef(kcd, kfe);
+ hit.f = kfe->basef;
+ hit.perc = len_v3v3(p, kfe->v1->cageco) / len_v3v3(kfe->v1->cageco, kfe->v2->cageco);
+ copy_v3_v3(hit.cagehit, p);
+
+ interp_v3_v3v3(p, kfe->v1->co, kfe->v2->co, hit.perc);
+ copy_v3_v3(hit.realhit, p);
+
+ /* BMESH_TODO: should also snap to vertices */
+ if (kcd->snap_midpoints) {
+ float perc = hit.perc;
+
+ /* select the closest from the edge endpoints or the midpoint */
+ if (perc < 0.25f) {
+ perc = 0.0f;
+ }
+ else if (perc < 0.75f) {
+ perc = 0.5f;
+ }
+ else {
+ perc = 1.0f;
+ }
+
+ interp_v3_v3v3(hit.hit, kfe->v1->co, kfe->v2->co, perc);
+ interp_v3_v3v3(hit.cagehit, kfe->v1->cageco, kfe->v2->cageco, perc);
+ }
+ else {
+ copy_v3_v3(hit.hit, p);
+ }
+ knife_project_v3(kcd, hit.cagehit, hit.schit);
+
+ BLI_array_append(edges, hit);
+ BLI_smallhash_insert(ehash, (intptr_t)kfe, NULL);
+ }
+ }
+ }
+ }
+ }
+
+ if (results)
+ MEM_freeN(results);
+
+ BLI_bvhtree_free(tree2);
+ *count = BLI_array_count(edges);
+
+ return edges;
+}
+
+static void knife_bgl_get_mats(knifetool_opdata *UNUSED(kcd), bglMats *mats)
+{
+ bgl_get_mats(mats);
+ //copy_m4_m4(mats->modelview, kcd->vc.rv3d->viewmat);
+ //copy_m4_m4(mats->projection, kcd->vc.rv3d->winmat);
+}
+
+/* Finds visible (or all, if cutting through) edges that intersects the current screen drag line */
+static void knife_find_line_hits(knifetool_opdata *kcd)
+{
+ bglMats mats;
+ BMEdgeHit *e1, *e2;
+ SmallHash hash, *ehash = &hash;
+ float v1[3], v2[3], v3[3], v4[4], s1[3], s2[3];
+ int i, c1, c2;
+
+ knife_bgl_get_mats(kcd, &mats);
+
+ if (kcd->linehits) {
+ MEM_freeN(kcd->linehits);
+ kcd->linehits = NULL;
+ kcd->totlinehit = 0;
+ }
+
+ copy_v3_v3(v1, kcd->prevcage);
+ copy_v3_v3(v2, kcd->vertcage);
+
+ /* project screen line's 3d coordinates back into 2d */
+ knife_project_v3(kcd, v1, s1);
+ knife_project_v3(kcd, v2, s2);
+
+ if (len_v2v2(s1, s2) < 1)
+ return;
+
+ /* unproject screen line */
+ ED_view3d_win_to_segment_clip(kcd->ar, kcd->vc.v3d, s1, v1, v3);
+ ED_view3d_win_to_segment_clip(kcd->ar, kcd->vc.v3d, s2, v2, v4);
+
+ mul_m4_v3(kcd->ob->imat, v1);
+ mul_m4_v3(kcd->ob->imat, v2);
+ mul_m4_v3(kcd->ob->imat, v3);
+ mul_m4_v3(kcd->ob->imat, v4);
+
+ BLI_smallhash_init(ehash);
+
+ /* test two triangles of sceen line's plane */
+ e1 = knife_edge_tri_isect(kcd, kcd->bmbvh, v1, v2, v3, ehash, &mats, &c1);
+ e2 = knife_edge_tri_isect(kcd, kcd->bmbvh, v2, v3, v4, ehash, &mats, &c2);
+ if (c1 && c2) {
+ e1 = MEM_reallocN(e1, sizeof(BMEdgeHit) * (c1 + c2));
+ memcpy(e1 + c1, e2, sizeof(BMEdgeHit) * c2);
+ MEM_freeN(e2);
+ }
+ else if (c2) {
+ e1 = e2;
+ }
+
+ kcd->linehits = e1;
+ kcd->totlinehit = c1 + c2;
+
+ /* find position along screen line, used for sorting */
+ for (i = 0; i < kcd->totlinehit; i++) {
+ BMEdgeHit *lh = e1 + i;
+
+ lh->l = len_v2v2(lh->schit, s1) / len_v2v2(s2, s1);
+ }
+
+ BLI_smallhash_release(ehash);
+}
+
+static void knife_input_ray_cast(knifetool_opdata *kcd, const int mval_i[2],
+ float r_origin[3], float r_ray[3])
+{
+ bglMats mats;
+ float mval[2], imat[3][3];
+
+ knife_bgl_get_mats(kcd, &mats);
+
+ mval[0] = (float)mval_i[0];
+ mval[1] = (float)mval_i[1];
+
+ /* unproject to find view ray */
+ view3d_unproject(&mats, r_origin, mval[0], mval[1], 0.0f);
+
+ if (kcd->is_ortho) {
+ negate_v3_v3(r_ray, kcd->vc.rv3d->viewinv[2]);
+ }
+ else {
+ sub_v3_v3v3(r_ray, r_origin, kcd->vc.rv3d->viewinv[3]);
+ }
+ normalize_v3(r_ray);
+
+ /* transform into object space */
+ invert_m4_m4(kcd->ob->imat, kcd->ob->obmat);
+ copy_m3_m4(imat, kcd->ob->obmat);
+ invert_m3(imat);
+
+ mul_m4_v3(kcd->ob->imat, r_origin);
+ mul_m3_v3(imat, r_ray);
+}
+
+
+static BMFace *knife_find_closest_face(knifetool_opdata *kcd, float co[3], float cageco[3], int *is_space)
+{
+ BMFace *f;
+ int dist = KMAXDIST;
+ float origin[3];
+ float ray[3];
+
+ /* unproject to find view ray */
+ knife_input_ray_cast(kcd, kcd->vc.mval, origin, ray);
+ add_v3_v3v3(co, origin, ray);
+
+ f = BMBVH_RayCast(kcd->bmbvh, origin, ray, co, cageco);
+
+ if (is_space)
+ *is_space = !f;
+
+ if (!f) {
+ /* try to use backbuffer selection method if ray casting failed */
+ f = EDBM_findnearestface(&kcd->vc, &dist);
+
+ /* cheat for now; just put in the origin instead
+ * of a true coordinate on the face.
+ * This just puts a point 1.0f infront of the view. */
+ add_v3_v3v3(co, origin, ray);
+ }
+
+ return f;
+}
+
+/* find the 2d screen space density of vertices within a radius. used to scale snapping
+ * distance for picking edges/verts.*/
+static int knife_sample_screen_density(knifetool_opdata *kcd, float radius)
+{
+ BMFace *f;
+ int is_space;
+ float co[3], cageco[3], sco[3];
+
+ f = knife_find_closest_face(kcd, co, cageco, &is_space);
+
+ if (f && !is_space) {
+ ListBase *lst;
+ Ref *ref;
+ float dis;
+ int c = 0;
+
+ knife_project_v3(kcd, cageco, sco);
+
+ lst = knife_get_face_kedges(kcd, f);
+ for (ref = lst->first; ref; ref = ref->next) {
+ KnifeEdge *kfe = ref->ref;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
+
+ knife_project_v3(kcd, kfv->cageco, kfv->sco);
+
+ dis = len_v2v2(kfv->sco, sco);
+ if (dis < radius) {
+ if (kcd->vc.rv3d->rflag & RV3D_CLIPPING) {
+ float vec[3];
+
+ copy_v3_v3(vec, kfv->cageco);
+ mul_m4_v3(kcd->vc.obedit->obmat, vec);
+
+ if (ED_view3d_test_clipping(kcd->vc.rv3d, vec, 1) == 0) {
+ c++;
+ }
+ }
+ else {
+ c++;
+ }
+ }
+ }
+ }
+
+ return c;
+ }
+
+ return 0;
+}
+
+/* returns snapping distance for edges/verts, scaled by the density of the
+ * surrounding mesh (in screen space)*/
+static float knife_snap_size(knifetool_opdata *kcd, float maxsize)
+{
+ float density = (float)knife_sample_screen_density(kcd, maxsize * 2.0f);
+
+ density = MAX2(density, 1);
+
+ return MIN2(maxsize / (density * 0.5f), maxsize);
+}
+
+/* p is closest point on edge to the mouse cursor */
+static KnifeEdge *knife_find_closest_edge(knifetool_opdata *kcd, float p[3], float cagep[3], BMFace **fptr, int *is_space)
+{
+ BMFace *f;
+ float co[3], cageco[3], sco[3], maxdist = knife_snap_size(kcd, kcd->ethresh);
+
+ if (kcd->ignore_vert_snapping)
+ maxdist *= 0.5;
+
+ f = knife_find_closest_face(kcd, co, cageco, NULL);
+ *is_space = !f;
+
+ /* set p to co, in case we don't find anything, means a face cut */
+ copy_v3_v3(p, co);
+ copy_v3_v3(cagep, cageco);
+
+ kcd->curbmface = f;
+
+ if (f) {
+ KnifeEdge *cure = NULL;
+ ListBase *lst;
+ Ref *ref;
+ float dis, curdis = FLT_MAX;
+
+ knife_project_v3(kcd, cageco, sco);
+
+ /* look through all edges associated with this face */
+ lst = knife_get_face_kedges(kcd, f);
+ for (ref = lst->first; ref; ref = ref->next) {
+ KnifeEdge *kfe = ref->ref;
+
+ /* project edge vertices into screen space */
+ knife_project_v3(kcd, kfe->v1->cageco, kfe->v1->sco);
+ knife_project_v3(kcd, kfe->v2->cageco, kfe->v2->sco);
+
+ dis = dist_to_line_segment_v2(sco, kfe->v1->sco, kfe->v2->sco);
+ if (dis < curdis && dis < maxdist) {
+ if (kcd->vc.rv3d->rflag & RV3D_CLIPPING) {
+ float labda = labda_PdistVL2Dfl(sco, kfe->v1->sco, kfe->v2->sco);
+ float vec[3];
+
+ vec[0] = kfe->v1->cageco[0] + labda*(kfe->v2->cageco[0] - kfe->v1->cageco[0]);
+ vec[1] = kfe->v1->cageco[1] + labda*(kfe->v2->cageco[1] - kfe->v1->cageco[1]);
+ vec[2] = kfe->v1->cageco[2] + labda*(kfe->v2->cageco[2] - kfe->v1->cageco[2]);
+ mul_m4_v3(kcd->vc.obedit->obmat, vec);
+
+ if (ED_view3d_test_clipping(kcd->vc.rv3d, vec, 1) == 0) {
+ cure = kfe;
+ curdis = dis;
+ }
+ }
+ else {
+ cure = kfe;
+ curdis = dis;
+ }
+ }
+ }
+
+ if (fptr)
+ *fptr = f;
+
+ if (cure && p) {
+ if (!kcd->ignore_edge_snapping || !(cure->e)) {
+ if (kcd->snap_midpoints) {
+ mid_v3_v3v3(p, cure->v1->co, cure->v2->co);
+ mid_v3_v3v3(cagep, cure->v1->cageco, cure->v2->cageco);
+ }
+ else {
+ float d;
+
+ closest_to_line_segment_v3(cagep, cageco, cure->v1->cageco, cure->v2->cageco);
+ d = len_v3v3(cagep, cure->v1->cageco) / len_v3v3(cure->v1->cageco, cure->v2->cageco);
+ interp_v3_v3v3(p, cure->v1->co, cure->v2->co, d);
+ }
+ }
+ else {
+ return NULL;
+ }
+ }
+
+ return cure;
+ }
+
+ if (fptr)
+ *fptr = NULL;
+
+ return NULL;
+}
+
+/* find a vertex near the mouse cursor, if it exists */
+static KnifeVert *knife_find_closest_vert(knifetool_opdata *kcd, float p[3], float cagep[3], BMFace **fptr, int *is_space)
+{
+ BMFace *f;
+ float co[3], cageco[3], sco[3], maxdist = knife_snap_size(kcd, kcd->vthresh);
+
+ if (kcd->ignore_vert_snapping)
+ maxdist *= 0.5;
+
+ f = knife_find_closest_face(kcd, co, cageco, is_space);
+
+ /* set p to co, in case we don't find anything, means a face cut */
+ copy_v3_v3(p, co);
+ copy_v3_v3(cagep, p);
+ kcd->curbmface = f;
+
+ if (f) {
+ ListBase *lst;
+ Ref *ref;
+ KnifeVert *curv = NULL;
+ float dis, curdis = FLT_MAX;
+
+ knife_project_v3(kcd, cageco, sco);
+
+ lst = knife_get_face_kedges(kcd, f);
+ for (ref = lst->first; ref; ref = ref->next) {
+ KnifeEdge *kfe = ref->ref;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
+
+ knife_project_v3(kcd, kfv->cageco, kfv->sco);
+
+ dis = len_v2v2(kfv->sco, sco);
+ if (dis < curdis && dis < maxdist) {
+ if (kcd->vc.rv3d->rflag & RV3D_CLIPPING) {
+ float vec[3];
+
+ copy_v3_v3(vec, kfv->cageco);
+ mul_m4_v3(kcd->vc.obedit->obmat, vec);
+
+ if (ED_view3d_test_clipping(kcd->vc.rv3d, vec, 1) == 0) {
+ curv = kfv;
+ curdis = dis;
+ }
+ }
+ else {
+ curv = kfv;
+ curdis = dis;
+ }
+ }
+ }
+ }
+
+ if (!kcd->ignore_vert_snapping || !(curv && curv->v)) {
+ if (fptr)
+ *fptr = f;
+
+ if (curv && p) {
+ copy_v3_v3(p, curv->co);
+ copy_v3_v3(cagep, curv->cageco);
+ }
+
+ return curv;
+ }
+ else {
+ if (fptr)
+ *fptr = f;
+
+ return NULL;
+ }
+ }
+
+ if (fptr)
+ *fptr = NULL;
+
+ return NULL;
+}
+
+static void knife_snap_angle(knifetool_opdata *kcd)
+{
+ int dx, dy;
+ float w, abs_tan;
+
+ dx = kcd->vc.mval[0] - kcd->prevmval[0];
+ dy = kcd->vc.mval[1] - kcd->prevmval[1];
+ if (dx == 0 || dy == 0)
+ return;
+
+ w = (float)dy / (float)dx;
+ abs_tan = fabsf(w);
+ if (abs_tan <= 0.4142f) { /* tan(22.5 degrees) = 0.4142 */
+ kcd->angle_snapping = ANGLE_0;
+ kcd->vc.mval[1] = kcd->prevmval[1];
+ }
+ else if (abs_tan < 2.4142f) { /* tan(67.5 degrees) = 2.4142 */
+ if (w > 0) {
+ kcd->angle_snapping = ANGLE_45;
+ kcd->vc.mval[1] = kcd->prevmval[1] + dx;
+ }
+ else {
+ kcd->angle_snapping = ANGLE_135;
+ kcd->vc.mval[1] = kcd->prevmval[1] - dx;
+ }
+ }
+ else {
+ kcd->angle_snapping = ANGLE_90;
+ kcd->vc.mval[0] = kcd->prevmval[0];
+ }
+}
+
+/* update active knife edge/vert pointers */
+static int knife_update_active(knifetool_opdata *kcd)
+{
+ if (kcd->angle_snapping != ANGLE_FREE && kcd->mode == MODE_DRAGGING)
+ knife_snap_angle(kcd);
+
+ kcd->curvert = NULL; kcd->curedge = NULL; kcd->curbmface = NULL;
+
+ kcd->curvert = knife_find_closest_vert(kcd, kcd->vertco, kcd->vertcage, &kcd->curbmface, &kcd->is_space);
+ if (!kcd->curvert) {
+ kcd->curedge = knife_find_closest_edge(kcd, kcd->vertco, kcd->vertcage, &kcd->curbmface, &kcd->is_space);
+ }
+
+ /* if no hits are found this would normally default to (0,0,0) so instead
+ * get a point at the mouse ray closest to the previous point.
+ * Note that drawing lines in `free-space` isn't properly supported
+ * but theres no guarantee (0,0,0) has any geometry either - campell */
+ if (kcd->curvert == NULL && kcd->curedge == NULL) {
+ float origin[3], ray[3], co[3];
+
+ knife_input_ray_cast(kcd, kcd->vc.mval, origin, ray);
+ add_v3_v3v3(co, origin, ray);
+
+ closest_to_line_v3(kcd->vertcage, kcd->prevcage, co, origin);
+ }
+
+ if (kcd->mode == MODE_DRAGGING) {
+ knife_find_line_hits(kcd);
+ }
+ return 1;
+}
+
+#define MARK 4
+#define DEL 8
+#define VERT_ON_EDGE 16
+#define VERT_ORIG 32
+#define FACE_FLIP 64
+#define BOUNDARY 128
+#define FACE_NEW 256
+
+typedef struct facenet_entry {
+ struct facenet_entry *next, *prev;
+ KnifeEdge *kfe;
+} facenet_entry;
+
+static void rnd_offset_co(float co[3], float scale)
+{
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ co[i] += (BLI_drand()-0.5)*scale;
+ }
+}
+
+static void remerge_faces(knifetool_opdata *kcd)
+{
+ BMesh *bm = kcd->em->bm;
+ SmallHash svisit, *visit = &svisit;
+ BMIter iter;
+ BMFace *f;
+ BMFace **stack = NULL;
+ BLI_array_declare(stack);
+ BMFace **faces = NULL;
+ BLI_array_declare(faces);
+ BMOperator bmop;
+ int idx;
+
+ BMO_op_initf(bm, &bmop, "beautify_fill faces=%ff constrain_edges=%fe", FACE_NEW, BOUNDARY);
+
+ BMO_op_exec(bm, &bmop);
+ BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", FACE_NEW, BM_FACE);
+
+ BMO_op_finish(bm, &bmop);
+
+ BLI_smallhash_init(visit);
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ BMIter eiter;
+ BMEdge *e;
+ BMFace *f2;
+
+ if (!BMO_elem_flag_test(bm, f, FACE_NEW))
+ continue;
+
+ if (BLI_smallhash_haskey(visit, (intptr_t)f))
+ continue;
+
+ BLI_array_empty(stack);
+ BLI_array_empty(faces);
+ BLI_array_append(stack, f);
+ BLI_smallhash_insert(visit, (intptr_t)f, NULL);
+
+ do {
+ f2 = BLI_array_pop(stack);
+
+ BLI_array_append(faces, f2);
+
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_FACE, f2) {
+ BMIter fiter;
+ BMFace *f3;
+
+ if (BMO_elem_flag_test(bm, e, BOUNDARY))
+ continue;
+
+ BM_ITER(f3, &fiter, bm, BM_FACES_OF_EDGE, e) {
+ if (!BMO_elem_flag_test(bm, f3, FACE_NEW))
+ continue;
+ if (BLI_smallhash_haskey(visit, (intptr_t)f3))
+ continue;
+
+ BLI_smallhash_insert(visit, (intptr_t)f3, NULL);
+ BLI_array_append(stack, f3);
+ }
+ }
+ } while (BLI_array_count(stack) > 0);
+
+ if (BLI_array_count(faces) > 0) {
+ idx = BM_elem_index_get(faces[0]);
+
+ f2 = BM_faces_join(bm, faces, BLI_array_count(faces));
+ if (f2) {
+ BMO_elem_flag_enable(bm, f2, FACE_NEW);
+ BM_elem_index_set(f2, idx); /* set_dirty! */ /* BMESH_TODO, check if this is valid or not */
+ }
+ }
+ }
+ /* BMESH_TODO, check if the code above validates the indicies */
+ /* bm->elem_index_dirty &= ~BM_FACE; */
+ bm->elem_index_dirty |= BM_FACE;
+
+ BLI_smallhash_release(visit);
+
+ BLI_array_free(stack);
+ BLI_array_free(faces);
+}
+
+/* use edgenet to fill faces. this is a bit annoying and convoluted.*/
+static void knifenet_fill_faces(knifetool_opdata *kcd)
+{
+ BMesh *bm = kcd->em->bm;
+ BMIter bmiter;
+ BLI_mempool_iter iter;
+ BMFace *f;
+ BMEdge *e;
+ KnifeVert *kfv;
+ KnifeEdge *kfe;
+ facenet_entry *entry;
+ ListBase *face_nets = MEM_callocN(sizeof(ListBase)*bm->totface, "face_nets");
+ BMFace **faces = MEM_callocN(sizeof(BMFace *) * bm->totface, "faces knife");
+ MemArena *arena = BLI_memarena_new(1 << 16, "knifenet_fill_faces");
+ SmallHash shash;
+ int i, j, k = 0, totface = bm->totface;
+
+ BMO_push(bm, NULL);
+ bmesh_begin_edit(bm, BMO_OP_FLAG_UNTAN_MULTIRES);
+
+ /* BMESH_TODO this should be valid now, leaving here until we can ensure this - campbell */
+ i = 0;
+ BM_ITER(f, &bmiter, bm, BM_FACES_OF_MESH, NULL) {
+ BM_elem_index_set(f, i); /* set_inline */
+ faces[i] = f;
+ i++;
+ }
+ bm->elem_index_dirty &= ~BM_FACE;
+
+ BM_ITER(e, &bmiter, bm, BM_EDGES_OF_MESH, NULL) {
+ BMO_elem_flag_enable(bm, e, BOUNDARY);
+ }
+
+ /* turn knife verts into real verts, as necassary */
+ BLI_mempool_iternew(kcd->kverts, &iter);
+ for (kfv = BLI_mempool_iterstep(&iter); kfv; kfv = BLI_mempool_iterstep(&iter)) {
+ if (!kfv->v) {
+ /* shouldn't we be at least copying the normal? - if not some comment here should explain why - campbell */
+ kfv->v = BM_vert_create(bm, kfv->co, NULL);
+ kfv->flag = 1;
+ BMO_elem_flag_enable(bm, kfv->v, DEL);
+ }
+ else {
+ kfv->flag = 0;
+ BMO_elem_flag_enable(bm, kfv->v, VERT_ORIG);
+ }
+
+ BMO_elem_flag_enable(bm, kfv->v, MARK);
+ }
+
+ /* we want to only do changed faces. first, go over new edges and add to
+ * face net lists.*/
+ i = j = k = 0;
+ BLI_mempool_iternew(kcd->kedges, &iter);
+ for (kfe = BLI_mempool_iterstep(&iter); kfe; kfe = BLI_mempool_iterstep(&iter)) {
+ Ref *ref;
+ if (!kfe->v1 || !kfe->v2 || kfe->v1->inspace || kfe->v2->inspace)
+ continue;
+
+ i++;
+
+ if (kfe->e && kfe->v1->v == kfe->e->v1 && kfe->v2->v == kfe->e->v2) {
+ kfe->oe = kfe->e;
+ continue;
+ }
+
+ j++;
+
+ if (kfe->e) {
+ kfe->oe = kfe->e;
+
+ BMO_elem_flag_enable(bm, kfe->e, DEL);
+ BMO_elem_flag_disable(bm, kfe->e, BOUNDARY);
+ kfe->e = NULL;
+ }
+
+ kfe->e = BM_edge_create(bm, kfe->v1->v, kfe->v2->v, NULL, TRUE);
+ BMO_elem_flag_enable(bm, kfe->e, BOUNDARY);
+
+ for (ref = kfe->faces.first; ref; ref = ref->next) {
+ f = ref->ref;
+
+ entry = BLI_memarena_alloc(arena, sizeof(*entry));
+ entry->kfe = kfe;
+ BLI_addtail(face_nets + BM_elem_index_get(f), entry);
+ }
+ }
+
+ /* go over original edges, and add to faces with new geometry */
+ BLI_mempool_iternew(kcd->kedges, &iter);
+ for (kfe = BLI_mempool_iterstep(&iter); kfe; kfe = BLI_mempool_iterstep(&iter)) {
+ Ref *ref;
+
+ if (!kfe->v1 || !kfe->v2 || kfe->v1->inspace || kfe->v2->inspace)
+ continue;
+ if (!(kfe->oe && kfe->v1->v == kfe->oe->v1 && kfe->v2->v == kfe->oe->v2))
+ continue;
+
+ k++;
+
+ BMO_elem_flag_enable(bm, kfe->e, BOUNDARY);
+ kfe->oe = kfe->e;
+
+ for (ref = kfe->faces.first; ref; ref = ref->next) {
+ f = ref->ref;
+
+ if (face_nets[BM_elem_index_get(f)].first) {
+ entry = BLI_memarena_alloc(arena, sizeof(*entry));
+ entry->kfe = kfe;
+ BLI_addtail(face_nets + BM_elem_index_get(f), entry);
+ }
+ }
+ }
+
+ for (i = 0; i < totface; i++) {
+ SmallHash *hash = &shash;
+ EditFace *efa;
+ EditVert *eve, *lasteve;
+ int j;
+ float rndscale = FLT_EPSILON * 25;
+
+ f = faces[i];
+ BLI_smallhash_init(hash);
+
+ if (face_nets[i].first)
+ BMO_elem_flag_enable(bm, f, DEL);
+
+ BLI_begin_edgefill();
+
+ for (entry = face_nets[i].first; entry; entry = entry->next) {
+ if (!BLI_smallhash_haskey(hash, (intptr_t)entry->kfe->v1)) {
+ eve = BLI_addfillvert(entry->kfe->v1->v->co);
+ eve->xs = 0;
+ rnd_offset_co(eve->co, rndscale);
+ eve->tmp.p = entry->kfe->v1->v;
+ BLI_smallhash_insert(hash, (intptr_t)entry->kfe->v1, eve);
+ }
+
+ if (!BLI_smallhash_haskey(hash, (intptr_t)entry->kfe->v2)) {
+ eve = BLI_addfillvert(entry->kfe->v2->v->co);
+ eve->xs = 0;
+ rnd_offset_co(eve->co, rndscale);
+ eve->tmp.p = entry->kfe->v2->v;
+ BLI_smallhash_insert(hash, (intptr_t)entry->kfe->v2, eve);
+ }
+ }
+
+ for (j = 0, entry = face_nets[i].first; entry; entry = entry->next, j++) {
+ lasteve = BLI_smallhash_lookup(hash, (intptr_t)entry->kfe->v1);
+ eve = BLI_smallhash_lookup(hash, (intptr_t)entry->kfe->v2);
+
+ eve->xs++;
+ lasteve->xs++;
+ }
+
+ for (j = 0, entry = face_nets[i].first; entry; entry = entry->next, j++) {
+ lasteve = BLI_smallhash_lookup(hash, (intptr_t)entry->kfe->v1);
+ eve = BLI_smallhash_lookup(hash, (intptr_t)entry->kfe->v2);
+
+ if (eve->xs > 1 && lasteve->xs > 1) {
+ EditEdge *eed;
+ eed = BLI_addfilledge(lasteve, eve);
+ if (entry->kfe->oe)
+ eed->f = FILLBOUNDARY; /* mark as original boundary edge */
+
+ BMO_elem_flag_disable(bm, entry->kfe->e->v1, DEL);
+ BMO_elem_flag_disable(bm, entry->kfe->e->v2, DEL);
+ }
+ else {
+ if (lasteve->xs < 2)
+ BLI_remlink(&fillvertbase, lasteve);
+ if (eve->xs < 2)
+ BLI_remlink(&fillvertbase, eve);
+ }
+ }
+
+ BLI_edgefill(0);
+
+ for (efa = fillfacebase.first; efa; efa = efa->next) {
+ BMVert *v1 = efa->v3->tmp.p, *v2 = efa->v2->tmp.p, *v3 = efa->v1->tmp.p;
+ BMFace *f2;
+ BMLoop *l_iter;
+ BMVert *verts[3] = {v1, v2, v3};
+
+ if (v1 == v2 || v2 == v3 || v1 == v3)
+ continue;
+ if (BM_face_exists(bm, verts, 3, &f2))
+ continue;
+
+ f2 = BM_face_create_quad_tri(bm,
+ v1, v2, v3, NULL,
+ NULL, FALSE);
+
+ BMO_elem_flag_enable(bm, f2, FACE_NEW);
+
+ l_iter = BM_FACE_FIRST_LOOP(f2);
+ do {
+ BMO_elem_flag_disable(bm, l_iter->e, DEL);
+ } while ((l_iter = l_iter->next) != BM_FACE_FIRST_LOOP(f2));
+
+ BMO_elem_flag_disable(bm, f2, DEL);
+ BM_elem_index_set(f2, i); /* set_dirty! */ /* note, not 100% sure this is dirty? need to check */
+
+ BM_face_normal_update(bm, f2);
+ if (dot_v3v3(f->no, f2->no) < 0.0f) {
+ BM_face_normal_flip(bm, f2);
+ }
+ }
+
+ BLI_end_edgefill();
+ BLI_smallhash_release(hash);
+ }
+ bm->elem_index_dirty |= BM_FACE;
+
+ /* interpolate customdata */
+ BM_ITER(f, &bmiter, bm, BM_FACES_OF_MESH, NULL) {
+ BMLoop *l1;
+ BMFace *f2;
+ BMIter liter1;
+
+ if (!BMO_elem_flag_test(bm, f, FACE_NEW))
+ continue;
+
+ f2 = faces[BM_elem_index_get(f)];
+ if (BM_elem_index_get(f) < 0 || BM_elem_index_get(f) >= totface) {
+ fprintf(stderr, "%s: face index out of range! (bmesh internal error)\n", __func__);
+ }
+
+ BM_elem_attrs_copy(bm, bm, f2, f);
+
+ BM_ITER(l1, &liter1, bm, BM_LOOPS_OF_FACE, f) {
+ BM_loop_interp_from_face(bm, l1, f2, TRUE, TRUE);
+ }
+ }
+
+ /* merge triangles back into faces */
+ remerge_faces(kcd);
+
+ /* delete left over faces */
+ BMO_op_callf(bm, "del geom=%ff context=%i", DEL, DEL_ONLYFACES);
+ BMO_op_callf(bm, "del geom=%fe context=%i", DEL, DEL_EDGES);
+ BMO_op_callf(bm, "del geom=%fv context=%i", DEL, DEL_VERTS);
+
+ if (face_nets)
+ MEM_freeN(face_nets);
+ if (faces)
+ MEM_freeN(faces);
+ BLI_memarena_free(arena);
+
+ BMO_error_clear(bm); /* remerge_faces sometimes raises errors, so make sure to clear them */
+
+ bmesh_end_edit(bm, BMO_OP_FLAG_UNTAN_MULTIRES);
+ BMO_pop(bm);
+}
+
+/* called on tool confirmation */
+static void knifetool_finish(bContext *C, wmOperator *op)
+{
+ knifetool_opdata *kcd = op->customdata;
+
+ knifenet_fill_faces(kcd);
+
+ DAG_id_tag_update(kcd->ob->data, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_GEOM|ND_DATA, kcd->ob->data);
+}
+
+/* copied from paint_image.c */
+static int project_knife_view_clip(View3D *v3d, RegionView3D *rv3d, float *clipsta, float *clipend)
+{
+ int orth = ED_view3d_clip_range_get(v3d, rv3d, clipsta, clipend);
+
+ if (orth) { /* only needed for ortho */
+ float fac = 2.0f / ((*clipend) - (*clipsta));
+ *clipsta *= fac;
+ *clipend *= fac;
+ }
+
+ return orth;
+}
+
+static void knife_recalc_projmat(knifetool_opdata *kcd)
+{
+ ARegion *ar = CTX_wm_region(kcd->C);
+
+ if (!ar)
+ return;
+
+ invert_m4_m4(kcd->ob->imat, kcd->ob->obmat);
+ ED_view3d_ob_project_mat_get(ar->regiondata, kcd->ob, kcd->projmat);
+ //mult_m4_m4m4(kcd->projmat, kcd->vc.rv3d->winmat, kcd->vc.rv3d->viewmat);
+
+ kcd->is_ortho = project_knife_view_clip(kcd->vc.v3d, kcd->vc.rv3d,
+ &kcd->clipsta, &kcd->clipend);
+}
+
+/* called when modal loop selection is done... */
+static void knifetool_exit (bContext *UNUSED(C), wmOperator *op)
+{
+ knifetool_opdata *kcd = op->customdata;
+
+ if (!kcd)
+ return;
+
+ /* deactivate the extra drawing stuff in 3D-View */
+ ED_region_draw_cb_exit(kcd->ar->type, kcd->draw_handle);
+
+ /* free the custom data */
+ BLI_mempool_destroy(kcd->refs);
+ BLI_mempool_destroy(kcd->kverts);
+ BLI_mempool_destroy(kcd->kedges);
+
+ BLI_ghash_free(kcd->origedgemap, NULL, NULL);
+ BLI_ghash_free(kcd->origvertmap, NULL, NULL);
+ BLI_ghash_free(kcd->kedgefacemap, NULL, NULL);
+
+ BMBVH_FreeBVH(kcd->bmbvh);
+ BLI_memarena_free(kcd->arena);
+
+ /* tag for redraw */
+ ED_region_tag_redraw(kcd->ar);
+
+ if (kcd->cagecos)
+ MEM_freeN(kcd->cagecos);
+
+ /* destroy kcd itself */
+ MEM_freeN(kcd);
+ op->customdata = NULL;
+}
+
+static void cage_mapped_verts_callback(void *userData, int index, float *co,
+ float *UNUSED(no_f), short *UNUSED(no_s))
+{
+ void **data = userData;
+ BMEditMesh *em = data[0];
+ float (*cagecos)[3] = data[1];
+ SmallHash *hash = data[2];
+
+ if (index >= 0 && index < em->bm->totvert && !BLI_smallhash_haskey(hash, index)) {
+ BLI_smallhash_insert(hash, index, NULL);
+ copy_v3_v3(cagecos[index], co);
+ }
+}
+
+/* called when modal loop selection gets set up... */
+static int knifetool_init(bContext *C, wmOperator *op, int UNUSED(do_cut))
+{
+ knifetool_opdata *kcd;
+ Scene *scene = CTX_data_scene(C);
+ Object *obedit = CTX_data_edit_object(C);
+ DerivedMesh *cage, *final;
+ SmallHash shash;
+ void *data[3];
+
+ /* alloc new customdata */
+ kcd = op->customdata = MEM_callocN(sizeof(knifetool_opdata), "knifetool Modal Op Data");
+
+ /* assign the drawing handle for drawing preview line... */
+ kcd->ob = obedit;
+ kcd->ar = CTX_wm_region(C);
+ kcd->C = C;
+ kcd->draw_handle = ED_region_draw_cb_activate(kcd->ar->type, knifetool_draw, kcd, REGION_DRAW_POST_VIEW);
+ em_setup_viewcontext(C, &kcd->vc);
+
+ kcd->em = ((Mesh *)kcd->ob->data)->edit_btmesh;
+
+ BM_mesh_elem_index_ensure(kcd->em->bm, BM_VERT);
+
+ cage = editbmesh_get_derived_cage_and_final(scene, obedit, kcd->em, &final, CD_MASK_DERIVEDMESH);
+ kcd->cagecos = MEM_callocN(sizeof(float) * 3 * kcd->em->bm->totvert, "knife cagecos");
+ data[0] = kcd->em;
+ data[1] = kcd->cagecos;
+ data[2] = &shash;
+
+ BLI_smallhash_init(&shash);
+ cage->foreachMappedVert(cage, cage_mapped_verts_callback, data);
+ BLI_smallhash_release(&shash);
+
+ kcd->bmbvh = BMBVH_NewBVH(kcd->em, BMBVH_USE_CAGE|BMBVH_RETURN_ORIG, scene, obedit);
+ kcd->arena = BLI_memarena_new(1 << 15, "knife");
+ kcd->vthresh = KMAXDIST - 1;
+ kcd->ethresh = KMAXDIST;
+
+ kcd->extend = 1;
+
+ knife_recalc_projmat(kcd);
+
+ ED_region_tag_redraw(kcd->ar);
+
+ kcd->refs = BLI_mempool_create(sizeof(Ref), 1, 2048, FALSE, FALSE);
+ kcd->kverts = BLI_mempool_create(sizeof(KnifeVert), 1, 512, FALSE, TRUE);
+ kcd->kedges = BLI_mempool_create(sizeof(KnifeEdge), 1, 512, FALSE, TRUE);
+
+ kcd->origedgemap = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "knife origedgemap");
+ kcd->origvertmap = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "knife origvertmap");
+ kcd->kedgefacemap = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "knife origvertmap");
+
+ /* cut all the way through the mesh if use_occlude_geometry button not pushed */
+ kcd->cut_through = !(kcd->vc.v3d->flag & V3D_ZBUF_SELECT);
+
+ return 1;
+}
+
+static int knifetool_cancel (bContext *C, wmOperator *op)
+{
+ /* this is just a wrapper around exit() */
+ knifetool_exit(C, op);
+ return OPERATOR_CANCELLED;
+}
+
+static int knifetool_invoke (bContext *C, wmOperator *op, wmEvent *evt)
+{
+ knifetool_opdata *kcd;
+
+ view3d_operator_needs_opengl(C);
+
+ if (!knifetool_init(C, op, 0))
+ return OPERATOR_CANCELLED;
+
+ /* add a modal handler for this operator - handles loop selection */
+ WM_event_add_modal_handler(C, op);
+
+ kcd = op->customdata;
+ kcd->vc.mval[0] = evt->mval[0];
+ kcd->vc.mval[1] = evt->mval[1];
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+enum {
+ KNF_MODAL_CANCEL = 1,
+ KNF_MODAL_CONFIRM,
+ KNF_MODAL_MIDPOINT_ON,
+ KNF_MODAL_MIDPOINT_OFF,
+ KNF_MODAL_NEW_CUT,
+ KNF_MODEL_IGNORE_SNAP_ON,
+ KNF_MODEL_IGNORE_SNAP_OFF,
+ KNF_MODAL_ADD_CUT,
+ KNF_MODAL_ANGLE_SNAP_TOGGLE
+};
+
+wmKeyMap* knifetool_modal_keymap(wmKeyConfig *keyconf)
+{
+ static EnumPropertyItem modal_items[] = {
+ {KNF_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
+ {KNF_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
+ {KNF_MODAL_MIDPOINT_ON, "SNAP_MIDPOINTS_ON", 0, "Snap To Midpoints On", ""},
+ {KNF_MODAL_MIDPOINT_OFF, "SNAP_MIDPOINTS_OFF", 0, "Snap To Midpoints Off", ""},
+ {KNF_MODEL_IGNORE_SNAP_ON, "IGNORE_SNAP_ON", 0, "Ignore Snapping On", ""},
+ {KNF_MODEL_IGNORE_SNAP_OFF, "IGNORE_SNAP_OFF", 0, "Ignore Snapping Off", ""},
+ {KNF_MODAL_ANGLE_SNAP_TOGGLE, "ANGLE_SNAP_TOGGLE", 0, "Toggle Angle Snapping", ""},
+ {KNF_MODAL_NEW_CUT, "NEW_CUT", 0, "End Current Cut", ""},
+ {KNF_MODAL_ADD_CUT, "ADD_CUT", 0, "Add Cut", ""},
+
+ {0, NULL, 0, NULL, NULL}};
+
+ wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Knife Tool Modal Map");
+
+ /* this function is called for each spacetype, only needs to add map once */
+ if (keymap) return NULL;
+
+ keymap = WM_modalkeymap_add(keyconf, "Knife Tool Modal Map", modal_items);
+
+ /* items for modal map */
+ WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, KNF_MODAL_CONFIRM);
+ WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, KNF_MODAL_ADD_CUT);
+ WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, KNF_MODAL_CONFIRM);
+ WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, KNF_MODAL_CONFIRM);
+ WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, KNF_MODAL_CONFIRM);
+ WM_modalkeymap_add_item(keymap, EKEY, KM_PRESS, 0, 0, KNF_MODAL_NEW_CUT);
+
+ WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, KNF_MODAL_MIDPOINT_ON);
+ WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, KNF_MODAL_MIDPOINT_OFF);
+ WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, KNF_MODAL_MIDPOINT_ON);
+ WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, KNF_MODAL_MIDPOINT_OFF);
+
+ WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, KNF_MODEL_IGNORE_SNAP_ON);
+ WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, KNF_MODEL_IGNORE_SNAP_OFF);
+ WM_modalkeymap_add_item(keymap, RIGHTSHIFTKEY, KM_PRESS, KM_ANY, 0, KNF_MODEL_IGNORE_SNAP_ON);
+ WM_modalkeymap_add_item(keymap, RIGHTSHIFTKEY, KM_RELEASE, KM_ANY, 0, KNF_MODEL_IGNORE_SNAP_OFF);
+
+ WM_modalkeymap_add_item(keymap, CKEY, KM_PRESS, 0, 0, KNF_MODAL_ANGLE_SNAP_TOGGLE);
+
+ WM_modalkeymap_assign(keymap, "MESH_OT_knifetool");
+
+ return keymap;
+}
+
+static int knifetool_modal (bContext *C, wmOperator *op, wmEvent *event)
+{
+ Object *obedit;
+ knifetool_opdata *kcd = op->customdata;
+
+ if (!C) {
+ return OPERATOR_FINISHED;
+ }
+
+ obedit = CTX_data_edit_object(C);
+ if (!obedit || obedit->type != OB_MESH || ((Mesh *)obedit->data)->edit_btmesh != kcd->em) {
+ knifetool_exit(C, op);
+ return OPERATOR_FINISHED;
+ }
+
+ view3d_operator_needs_opengl(C);
+
+ if (kcd->mode == MODE_PANNING)
+ kcd->mode = kcd->prevmode;
+
+ /* handle modal keymap */
+ if (event->type == EVT_MODAL_MAP) {
+ switch (event->val) {
+ case KNF_MODAL_CANCEL:
+ /* finish */
+ ED_region_tag_redraw(kcd->ar);
+
+ knifetool_exit(C, op);
+
+ return OPERATOR_CANCELLED;
+ case KNF_MODAL_CONFIRM:
+ /* finish */
+ ED_region_tag_redraw(kcd->ar);
+
+ knifetool_finish(C, op);
+ knifetool_exit(C, op);
+
+ return OPERATOR_FINISHED;
+ case KNF_MODAL_MIDPOINT_ON:
+ kcd->snap_midpoints = 1;
+
+ knife_recalc_projmat(kcd);
+ knife_update_active(kcd);
+ ED_region_tag_redraw(kcd->ar);
+ break;
+ case KNF_MODAL_MIDPOINT_OFF:
+ kcd->snap_midpoints = 0;
+
+ knife_recalc_projmat(kcd);
+ knife_update_active(kcd);
+ ED_region_tag_redraw(kcd->ar);
+ break;
+ case KNF_MODEL_IGNORE_SNAP_ON:
+ ED_region_tag_redraw(kcd->ar);
+ kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = 1;
+ break;
+ case KNF_MODEL_IGNORE_SNAP_OFF:
+ ED_region_tag_redraw(kcd->ar);
+ kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = 0;
+ break;
+ case KNF_MODAL_ANGLE_SNAP_TOGGLE:
+ kcd->angle_snapping = !kcd->angle_snapping;
+ break;
+ case KNF_MODAL_NEW_CUT:
+ ED_region_tag_redraw(kcd->ar);
+ knife_finish_cut(kcd);
+ kcd->mode = MODE_IDLE;
+ break;
+ case KNF_MODAL_ADD_CUT:
+ knife_recalc_projmat(kcd);
+
+ if (kcd->mode == MODE_DRAGGING) {
+ knife_add_cut(kcd);
+ if (!kcd->extend) {
+ knife_finish_cut(kcd);
+ kcd->mode = MODE_IDLE;
+ }
+ }
+ else if (kcd->mode != MODE_PANNING) {
+ knife_start_cut(kcd);
+ kcd->mode = MODE_DRAGGING;
+ }
+
+ ED_region_tag_redraw(kcd->ar);
+ break;
+ }
+ }
+ else { /* non-modal-mapped events */
+ switch (event->type) {
+ case WHEELUPMOUSE:
+ case WHEELDOWNMOUSE:
+ return OPERATOR_PASS_THROUGH;
+ case MIDDLEMOUSE:
+ if (event->val != KM_RELEASE) {
+ if (kcd->mode != MODE_PANNING)
+ kcd->prevmode = kcd->mode;
+ kcd->mode = MODE_PANNING;
+ }
+ else {
+ kcd->mode = kcd->prevmode;
+ }
+
+ ED_region_tag_redraw(kcd->ar);
+ return OPERATOR_PASS_THROUGH;
+
+ case MOUSEMOVE: /* mouse moved somewhere to select another loop */
+ if (kcd->mode != MODE_PANNING) {
+ knife_recalc_projmat(kcd);
+ kcd->vc.mval[0] = event->mval[0];
+ kcd->vc.mval[1] = event->mval[1];
+
+ if (knife_update_active(kcd))
+ ED_region_tag_redraw(kcd->ar);
+ }
+
+ break;
+ }
+ }
+
+ /* keep going until the user confirms */
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void MESH_OT_knifetool (wmOperatorType *ot)
+{
+ /* description */
+ ot->name = "Knife Topology Tool";
+ ot->idname = "MESH_OT_knifetool";
+ ot->description = "Cut new topology";
+
+ /* callbacks */
+ ot->invoke = knifetool_invoke;
+ ot->modal = knifetool_modal;
+ ot->cancel = knifetool_cancel;
+ ot->poll = ED_operator_editmesh_view3d;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
+}