Fix T42459: Knife fails at small scale
authorCampbell Barton <ideasman42@gmail.com>
Tue, 13 Jan 2015 14:28:33 +0000 (01:28 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Tue, 13 Jan 2015 14:36:03 +0000 (01:36 +1100)
Occluding geometry failed when near overlapping (or cutting small objects).

source/blender/bmesh/intern/bmesh_callback_generic.c
source/blender/bmesh/intern/bmesh_callback_generic.h
source/blender/editors/mesh/editmesh_knife.c

index 84fcc67f3c494abf4e01a498d4397a94093cde50..913255bfb3307f72c37fdd0c141e455d2dcccb83 100644 (file)
@@ -53,3 +53,8 @@ bool BM_elem_cb_check_hflag_disabled(BMElem *ele, void *user_data)
 
        return (BM_elem_flag_test(ele, hflag) == 0);
 }
+
+bool BM_elem_cb_check_elem_not_equal(BMElem *ele, void *user_data)
+{
+       return (ele != user_data);
+}
index 8c46128f3b0f8ffffd9b21510e2a0b6c6ae3a252..3cae01d417f9544f491c933789c52428e4a68ea5 100644 (file)
@@ -28,6 +28,7 @@
 bool BM_elem_cb_check_hflag_enabled(BMElem *, void *user_data);
 bool BM_elem_cb_check_hflag_disabled(BMElem *, void *user_data);
 bool BM_elem_cb_check_hflag_ex(BMElem *, void *user_data);
+bool BM_elem_cb_check_elem_not_equal(BMElem *ele, void *user_data);
 
 #define BM_elem_cb_check_hflag_ex_simple(type, hflag_p, hflag_n) \
        (bool (*)(type, void *))BM_elem_cb_check_hflag_ex, \
index 10541a6d02ec22e9363fd7b069c42b84298dcfe3..093ad02b2da640e790e5aeda428ff8e35a9ca836 100644 (file)
@@ -1227,9 +1227,81 @@ static void calc_ortho_extent(KnifeTool_OpData *kcd)
        kcd->ortho_extent = max_xyz;
 }
 
-/* Check if p is visible (not clipped, not occluded by another face).
- * s in screen projection of p. */
-static bool point_is_visible(KnifeTool_OpData *kcd, const float p[3], const float s[2], bglMats *mats)
+static BMElem *bm_elem_from_knife_vert(KnifeVert *kfv, KnifeEdge **r_kfe)
+{
+       BMElem *ele_test;
+       KnifeEdge *kfe = NULL;
+
+       if (r_kfe || ele_test == NULL) {
+               if (kfv->v == NULL) {
+                       Ref *ref;
+                       for (ref = kfv->edges.first; ref; ref = ref->next) {
+                               kfe = ref->ref;
+                               if (kfe->e) {
+                                       *r_kfe = kfe;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       /* vert? */
+       ele_test = (BMElem *)kfv->v;
+
+       /* edge? */
+       if (ele_test == NULL) {
+               if (kfe) {
+                       ele_test = (BMElem *)kfe->e;
+               }
+       }
+
+       if (ele_test == NULL) {
+               if (BLI_listbase_is_single(&kfe->faces)) {
+                       ele_test = ((Ref *)kfe->faces.first)->ref;
+               }
+       }
+
+       return ele_test;
+}
+
+static BMElem *bm_elem_from_knife_edge(KnifeEdge *kfe)
+{
+       BMElem *ele_test;
+
+       ele_test = (BMElem *)kfe->e;
+
+       if (ele_test == NULL) {
+               ele_test = (BMElem *)kfe->basef;
+       }
+
+       return ele_test;
+}
+
+static bool bm_ray_cast_cb_elem_not_in_face_check(BMFace *f, void *user_data)
+{
+       switch (((BMElem *)user_data)->head.htype) {
+               case BM_FACE:
+                       return (BMFace *)user_data != f;
+               case BM_EDGE:
+                       return !BM_edge_in_face((BMEdge *)user_data, f);
+               case BM_VERT:
+                       return !BM_vert_in_face((BMVert *)user_data, f);
+               default:
+                       return true;
+       }
+}
+
+
+/**
+ * Check if \a p is visible (not clipped, not occluded by another face).
+ * s in screen projection of p.
+ *
+ * \param ele_test  Optional vert/edge/face to use when \a p is on the surface of the geometry,
+ * intersecting faces matching this face (or connected when an vert/edge) will be ignored.
+ */
+static bool point_is_visible(
+        KnifeTool_OpData *kcd, const float p[3], const float s[2], bglMats *mats,
+        BMElem *ele_test)
 {
        BMFace *f_hit;
 
@@ -1253,7 +1325,7 @@ static bool point_is_visible(KnifeTool_OpData *kcd, const float p[3], const floa
                /* make p_ofs a little towards view, so ray doesn't hit p's face. */
                sub_v3_v3(view, p);
                dist = normalize_v3(view);
-               madd_v3_v3v3fl(p_ofs, p, view, KNIFE_FLT_EPSBIG * 3.0f);
+               copy_v3_v3(p_ofs, p);
 
                /* avoid projecting behind the viewpoint */
                if (kcd->is_ortho && (kcd->vc.rv3d->persp != RV3D_CAMOB)) {
@@ -1272,9 +1344,19 @@ static bool point_is_visible(KnifeTool_OpData *kcd, const float p[3], const floa
                }
 
                /* see if there's a face hit between p1 and the view */
-               f_hit = BKE_bmbvh_ray_cast(kcd->bmbvh, p_ofs, view, KNIFE_FLT_EPS, &dist, NULL, NULL);
-               if (f_hit)
+               if (ele_test) {
+                       f_hit = BKE_bmbvh_ray_cast_filter(
+                                   kcd->bmbvh, p_ofs, view, KNIFE_FLT_EPS, &dist, NULL, NULL,
+                                   bm_ray_cast_cb_elem_not_in_face_check, ele_test);
+               }
+               else {
+                       f_hit = BKE_bmbvh_ray_cast(
+                                   kcd->bmbvh, p_ofs, view, KNIFE_FLT_EPS, &dist, NULL, NULL);
+               }
+
+               if (f_hit) {
                        return false;
+               }
        }
 
        return true;
@@ -1446,25 +1528,21 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
        for (val_p = BLI_smallhash_iternew_p(&kfvs, &hiter, (uintptr_t *)&v); val_p;
             val_p = BLI_smallhash_iternext_p(&hiter, (uintptr_t *)&v))
        {
+               KnifeEdge *kfe_hit = NULL;
+
                knife_project_v2(kcd, v->cageco, s);
                d = dist_squared_to_line_segment_v2(s, s1, s2);
                if ((d <= vert_tol_sq) &&
-                   point_is_visible(kcd, v->cageco, s, &mats))
+                   (point_is_visible(kcd, v->cageco, s, &mats, bm_elem_from_knife_vert(v, &kfe_hit))))
                {
                        memset(&hit, 0, sizeof(hit));
                        hit.v = v;
 
                        /* If this isn't from an existing BMVert, it may have been added to a BMEdge originally.
-                        * knowing if the hit comes from an edge is important for edge-in-face checks later on
-                        * see: #knife_add_single_cut -> #knife_verts_edge_in_face, T42611 */
-                       if (v->v == NULL) {
-                               for (ref = v->edges.first; ref; ref = ref->next) {
-                                       kfe = ref->ref;
-                                       if (kfe->e) {
-                                               hit.kfe = kfe;
-                                               break;
-                                       }
-                               }
+                                * knowing if the hit comes from an edge is important for edge-in-face checks later on
+                                * see: #knife_add_single_cut -> #knife_verts_edge_in_face, T42611 */
+                       if (kfe_hit) {
+                               hit.kfe = kfe_hit;
                        }
 
                        copy_v3_v3(hit.hit, v->co);
@@ -1518,7 +1596,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
                                 * Need to find 3d intersection of ray through sint */
                                knife_input_ray_segment(kcd, sint, 1.0f, r1, r2);
                                isect_kind = isect_line_line_v3(kfe->v1->cageco, kfe->v2->cageco, r1, r2, p_cage, p_cage_tmp);
-                               if (isect_kind >= 1 && point_is_visible(kcd, p_cage, sint, &mats)) {
+                               if (isect_kind >= 1 && point_is_visible(kcd, p_cage, sint, &mats, bm_elem_from_knife_edge(kfe))) {
                                        memset(&hit, 0, sizeof(hit));
                                        if (kcd->snap_midpoints) {
                                                /* choose intermediate point snap too */
@@ -1547,7 +1625,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
                float p[3], p_cage[3];
 
                if (use_hit_prev && knife_ray_intersect_face(kcd, s1, v1, v3, f, face_tol_sq, p, p_cage)) {
-                       if (point_is_visible(kcd, p_cage, s1, &mats)) {
+                       if (point_is_visible(kcd, p_cage, s1, &mats, (BMElem *)f)) {
                                memset(&hit, 0, sizeof(hit));
                                hit.f = f;
                                copy_v3_v3(hit.hit, p);
@@ -1559,7 +1637,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
                }
 
                if (use_hit_curr && knife_ray_intersect_face(kcd, s2, v2, v4, f, face_tol_sq, p, p_cage)) {
-                       if (point_is_visible(kcd, p_cage, s2, &mats)) {
+                       if (point_is_visible(kcd, p_cage, s2, &mats, (BMElem *)f)) {
                                memset(&hit, 0, sizeof(hit));
                                hit.f = f;
                                copy_v3_v3(hit.hit, p);
@@ -3341,7 +3419,7 @@ void EDBM_mesh_knife(bContext *C, LinkNode *polys, bool use_tag, bool cut_throug
                                                        float cent[3], cent_ss[2];
                                                        edbm_mesh_knife_face_point(f, cent);
                                                        knife_project_v2(kcd, cent, cent_ss);
-                                                       if ((kcd->cut_through || point_is_visible(kcd, cent, cent_ss, &mats)) &&
+                                                       if ((kcd->cut_through || point_is_visible(kcd, cent, cent_ss, &mats, (BMElem *)f)) &&
                                                            edbm_mesh_knife_point_isect(polys, cent_ss))
                                                        {
                                                                BM_elem_flag_enable(f, BM_ELEM_TAG);