diff options
author | Campbell Barton <ideasman42@gmail.com> | 2015-01-13 17:28:33 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2015-01-13 17:36:03 +0300 |
commit | a97c5d1f9f7ec71807763c2f28f095bf37f0c230 (patch) | |
tree | 71ea0119c44071a672d6bd6b4a835e5b46f4805b | |
parent | a2a7260915633eda8a3d26aa678baba49b2429ce (diff) |
Fix T42459: Knife fails at small scale
Occluding geometry failed when near overlapping (or cutting small objects).
-rw-r--r-- | source/blender/bmesh/intern/bmesh_callback_generic.c | 5 | ||||
-rw-r--r-- | source/blender/bmesh/intern/bmesh_callback_generic.h | 1 | ||||
-rw-r--r-- | source/blender/editors/mesh/editmesh_knife.c | 120 |
3 files changed, 105 insertions, 21 deletions
diff --git a/source/blender/bmesh/intern/bmesh_callback_generic.c b/source/blender/bmesh/intern/bmesh_callback_generic.c index 84fcc67f3c4..913255bfb33 100644 --- a/source/blender/bmesh/intern/bmesh_callback_generic.c +++ b/source/blender/bmesh/intern/bmesh_callback_generic.c @@ -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); +} diff --git a/source/blender/bmesh/intern/bmesh_callback_generic.h b/source/blender/bmesh/intern/bmesh_callback_generic.h index 8c46128f3b0..3cae01d417f 100644 --- a/source/blender/bmesh/intern/bmesh_callback_generic.h +++ b/source/blender/bmesh/intern/bmesh_callback_generic.h @@ -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, \ diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 10541a6d02e..093ad02b2da 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -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); |