Snap system: Adds support to Clip Planes and uses a clip plane to simulate occlusion
[blender.git] / source / blender / editors / transform / transform_snap_object.c
index fb612e8a4ff128a198ad86e350de96c05c83db15..253cd53fb3e9fafc84a5d07a88bf328e05e52745 100644 (file)
 #include "DNA_curve_types.h"
 #include "DNA_scene_types.h"
 #include "DNA_object_types.h"
+#include "DNA_mesh_types.h"
 #include "DNA_meshdata_types.h"
 #include "DNA_screen_types.h"
 #include "DNA_view3d_types.h"
 
-#include "BKE_DerivedMesh.h"
+#include "BKE_bvhutils.h"
+#include "BKE_armature.h"
+#include "BKE_curve.h"
 #include "BKE_object.h"
 #include "BKE_anim.h"  /* for duplis */
 #include "BKE_editmesh.h"
 #include "BKE_main.h"
 #include "BKE_tracking.h"
 #include "BKE_context.h"
+#include "BKE_mesh.h"
 
 #include "DEG_depsgraph.h"
 #include "DEG_depsgraph_query.h"
@@ -67,6 +71,8 @@
 /** Internal Data Types
  * \{ */
 
+#define MAX_CLIPPLANE_LEN 3
+
 enum eViewProj {
        VIEW_PROJ_NONE = -1,
        VIEW_PROJ_ORTHO = 0,
@@ -76,13 +82,11 @@ enum eViewProj {
 typedef struct SnapData {
        short snap_to;
        float mval[2];
-       float ray_origin[3];
-       float ray_start[3];
-       float ray_dir[3];
        float pmat[4][4]; /* perspective matrix */
-       float win_half[2];/* win x and y */
+       float win_size[2];/* win x and y */
        enum eViewProj view_proj;
-       float depth_range[2];
+       float clip_plane[MAX_CLIPPLANE_LEN][4];
+       short clip_plane_len;
 } SnapData;
 
 typedef struct SnapObjectData {
@@ -94,9 +98,11 @@ typedef struct SnapObjectData {
 
 typedef struct SnapObjectData_Mesh {
        SnapObjectData sd;
-       BVHTreeFromMesh *bvh_trees[3];
-       MPoly *mpoly;
-       bool poly_allocated;
+       BVHTreeFromMesh treedata;
+       BVHTree *bvhtree[2]; /* from loose verts and from loose edges */
+       uint has_looptris   : 1;
+       uint has_loose_edge : 1;
+       uint has_loose_vert : 1;
 
 } SnapObjectData_Mesh;
 
@@ -158,12 +164,14 @@ typedef void(*IterSnapObjsCallback)(SnapObjectContext *sctx, bool is_obedit, Obj
  */
 static void iter_snap_objects(
         SnapObjectContext *sctx,
-        const eSnapSelect snap_select,
-        Object *obedit,
+        const struct SnapObjectParams *params,
         IterSnapObjsCallback sob_callback,
         void *data)
 {
        ViewLayer *view_layer = DEG_get_evaluated_view_layer(sctx->depsgraph);
+       Object *obedit = params->use_object_edit_cage ? OBEDIT_FROM_VIEW_LAYER(view_layer) : NULL;
+       const eSnapSelect snap_select = params->snap_select;
+
        Base *base_act = view_layer->basact;
        for (Base *base = view_layer->object_bases.first; base != NULL; base = base->next) {
                if ((BASE_VISIBLE(base)) && (base->flag_legacy & BA_SNAP_FIX_DEPS_FIASCO) == 0 &&
@@ -189,39 +197,6 @@ static void iter_snap_objects(
 }
 
 
-/**
- * Generates a struct with the immutable parameters that will be used on all objects.
- *
- * \param snap_to: Element to snap, Vertice, Edge or Face.
- * \param view_proj: ORTHO or PERSP.
- * Currently only works one at a time, but can eventually operate as flag.
- *
- * \param mval: Mouse coords.
- * (When NULL, ray-casting is handled without any projection matrix correction.)
- * \param ray_origin: ray_start before being moved toward the ray_normal at the distance from vew3d clip_min.
- * \param ray_start: ray_origin moved for the start clipping plane (clip_min).
- * \param ray_direction: Unit length direction of the ray.
- * \param depth_range: distances of clipe plane min and clip plane max;
- */
-static void snap_data_set(
-        SnapData *snapdata,
-        const ARegion *ar, const unsigned short snap_to, const enum eViewProj view_proj,
-        const float mval[2], const float ray_origin[3], const float ray_start[3],
-        const float ray_direction[3], const float depth_range[2])
-{
-       copy_m4_m4(snapdata->pmat, ((RegionView3D *)ar->regiondata)->persmat);
-       snapdata->win_half[0] = ar->winx / 2;
-       snapdata->win_half[1] = ar->winy / 2;
-       copy_v2_v2(snapdata->mval, mval);
-       snapdata->snap_to = snap_to;
-       copy_v3_v3(snapdata->ray_origin, ray_origin);
-       copy_v3_v3(snapdata->ray_start, ray_start);
-       copy_v3_v3(snapdata->ray_dir, ray_direction);
-       snapdata->view_proj = view_proj;
-       copy_v2_v2(snapdata->depth_range, depth_range);
-}
-
-
 MINLINE float depth_get(const float co[3], const float ray_start[3], const float ray_dir[3])
 {
        float dvec[3];
@@ -259,9 +234,6 @@ static bool isect_ray_bvhroot_v3(struct BVHTree *tree, const float ray_start[3],
        }
 }
 
-
-static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt);
-
 /** \} */
 
 /* -------------------------------------------------------------------- */
@@ -344,14 +316,6 @@ static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVH
                mul_m3_v3((float(*)[3])data->timat, normal);
                normalize_v3(normal);
 
-               /* currently unused, and causes issues when looptri's haven't been calculated.
-                * since theres some overhead in ensuring this data is valid, it may need to be optional. */
-#if 0
-               if (data->dm) {
-                       hit->index = dm_looptri_to_poly_index(data->dm, &data->dm_looptri[hit->index]);
-               }
-#endif
-
                struct SnapObjectHitDepth *hit_item = hit_depth_create(
                        depth, location, normal, hit->index,
                        data->ob, data->obmat, data->ob_uuid);
@@ -360,10 +324,10 @@ static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVH
 }
 
 
-static bool raycastDerivedMesh(
+static bool raycastMesh(
         SnapObjectContext *sctx,
         const float ray_start[3], const float ray_dir[3],
-        Object *ob, DerivedMesh *dm, float obmat[4][4], const unsigned int ob_index,
+        Object *ob, Mesh *me, float obmat[4][4], const unsigned int ob_index,
         /* read/write args */
         float *ray_depth,
         /* return args */
@@ -372,7 +336,7 @@ static bool raycastDerivedMesh(
 {
        bool retval = false;
 
-       if (dm->getNumPolys(dm) == 0) {
+       if (me->totpoly == 0) {
                return retval;
        }
 
@@ -398,7 +362,7 @@ static bool raycastDerivedMesh(
        }
 
        /* Test BoundBox */
-       BoundBox *bb = BKE_object_boundbox_get(ob);
+       BoundBox *bb = BKE_mesh_boundbox_get(ob);
        if (bb) {
                /* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */
                if (!isect_ray_aabb_v3_simple(
@@ -409,7 +373,6 @@ static bool raycastDerivedMesh(
        }
 
        SnapObjectData_Mesh *sod = NULL;
-       BVHTreeFromMesh *treedata;
 
        void **sod_p;
        if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) {
@@ -420,46 +383,35 @@ static bool raycastDerivedMesh(
                sod->sd.type = SNAP_MESH;
        }
 
-       if (sod->bvh_trees[2] == NULL) {
-               sod->bvh_trees[2] = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*treedata));
-       }
-
-       treedata = sod->bvh_trees[2];
+       BVHTreeFromMesh *treedata = &sod->treedata;
 
-       if (treedata) {
-               /* the tree is owned by the DM and may have been freed since we last used! */
-               if (treedata->tree) {
-                       if (treedata->cached && !bvhcache_has_tree(dm->bvhCache, treedata->tree)) {
-                               free_bvhtree_from_mesh(treedata);
+       /* The tree is owned by the DM and may have been freed since we last used. */
+       if (treedata->tree) {
+               BLI_assert(treedata->cached);
+               if (!bvhcache_has_tree(me->runtime.bvh_cache, treedata->tree)) {
+                       free_bvhtree_from_mesh(treedata);
+               }
+               else {
+                       /* Update Pointers. */
+                       if (treedata->vert && treedata->vert_allocated == false) {
+                               treedata->vert = me->mvert;
                        }
-                       else {
-                               if (treedata->vert == NULL) {
-                                       treedata->vert = DM_get_vert_array(dm, &treedata->vert_allocated);
-                               }
-                               if (treedata->loop == NULL) {
-                                       treedata->loop = DM_get_loop_array(dm, &treedata->loop_allocated);
-                               }
-                               if (treedata->looptri == NULL) {
-                                       if (sod->mpoly == NULL) {
-                                               sod->mpoly = DM_get_poly_array(dm, &sod->poly_allocated);
-                                       }
-                                       treedata->looptri = dm->getLoopTriArray(dm);
-                                       treedata->looptri_allocated = false;
-                               }
+                       if (treedata->loop && treedata->loop_allocated == false) {
+                               treedata->loop = me->mloop;
+                       }
+                       if (treedata->looptri && treedata->looptri_allocated == false) {
+                               treedata->looptri = BKE_mesh_runtime_looptri_ensure(me);
                        }
                }
+       }
 
-               if (treedata->tree == NULL) {
-                       bvhtree_from_mesh_get(treedata, dm, BVHTREE_FROM_LOOPTRI, 4);
+       if (treedata->tree == NULL) {
+               BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI, 4);
 
-                       if (treedata->tree == NULL) {
-                               return retval;
-                       }
+               if (treedata->tree == NULL) {
+                       return retval;
                }
        }
-       else {
-               return retval;
-       }
 
        /* Only use closer ray_start in case of ortho view! In perspective one, ray_start may already
         * been *inside* boundbox, leading to snap failures (see T38409).
@@ -531,7 +483,7 @@ static bool raycastDerivedMesh(
                                retval = true;
 
                                if (r_index) {
-                                       *r_index = dm_looptri_to_poly_index(dm, &treedata->looptri[hit.index]);
+                                       *r_index = treedata->looptri[hit.index].poly;
                                }
                        }
                }
@@ -572,30 +524,25 @@ static bool raycastEditMesh(
        }
        treedata = sod->bvh_trees[2];
 
-       if (treedata) {
-               if (treedata->tree == NULL) {
-                       BLI_bitmap *elem_mask = NULL;
-                       int looptri_num_active = -1;
+       if (treedata->tree == NULL) {
+               BLI_bitmap *elem_mask = NULL;
+               int looptri_num_active = -1;
 
-                       if (sctx->callbacks.edit_mesh.test_face_fn) {
-                               elem_mask = BLI_BITMAP_NEW(em->tottri, __func__);
-                               looptri_num_active = BM_iter_mesh_bitmap_from_filter_tessface(
-                                       em->bm, elem_mask,
-                                       sctx->callbacks.edit_mesh.test_face_fn, sctx->callbacks.edit_mesh.user_data);
-                       }
-                       bvhtree_from_editmesh_looptri_ex(treedata, em, elem_mask, looptri_num_active, 0.0f, 4, 6, NULL);
+               if (sctx->callbacks.edit_mesh.test_face_fn) {
+                       elem_mask = BLI_BITMAP_NEW(em->tottri, __func__);
+                       looptri_num_active = BM_iter_mesh_bitmap_from_filter_tessface(
+                               em->bm, elem_mask,
+                               sctx->callbacks.edit_mesh.test_face_fn, sctx->callbacks.edit_mesh.user_data);
+               }
+               bvhtree_from_editmesh_looptri_ex(treedata, em, elem_mask, looptri_num_active, 0.0f, 4, 6, NULL);
 
-                       if (elem_mask) {
-                               MEM_freeN(elem_mask);
-                       }
+               if (elem_mask) {
+                       MEM_freeN(elem_mask);
                }
                if (treedata->tree == NULL) {
                        return retval;
                }
        }
-       else {
-               return retval;
-       }
 
        float imat[4][4];
        float timat[3][3]; /* transpose inverse matrix for normals */
@@ -685,7 +632,7 @@ static bool raycastEditMesh(
                                retval = true;
 
                                if (r_index) {
-                                       *r_index = hit.index;
+                                       *r_index = BM_elem_index_get(em->looptris[hit.index][0]->f);
                                }
                        }
                }
@@ -704,7 +651,7 @@ static bool raycastObj(
         SnapObjectContext *sctx,
         const float ray_start[3], const float ray_dir[3],
         Object *ob, float obmat[4][4], const unsigned int ob_index,
-        bool use_obedit,
+        bool use_obedit, bool use_occlusion_test,
         /* read/write args */
         float *ray_depth,
         /* return args */
@@ -714,44 +661,46 @@ static bool raycastObj(
 {
        bool retval = false;
 
-       if (ob->type == OB_MESH) {
-               BMEditMesh *em;
-
-               if (use_obedit) {
-                       em = BKE_editmesh_from_object(ob);
-                       retval = raycastEditMesh(
-                               sctx,
-                               ray_start, ray_dir,
-                               ob, em, obmat, ob_index,
-                               ray_depth, r_loc, r_no, r_index, r_hit_list);
+       if (use_occlusion_test) {
+               if (use_obedit && sctx->use_v3d &&
+                   !(sctx->v3d_data.v3d->flag & V3D_ZBUF_SELECT))
+               {
+                       /* Use of occlude geometry in editing mode disabled. */
+                       return;
                }
-               else {
-                       /* in this case we want the mesh from the editmesh, avoids stale data. see: T45978.
-                        * still set the 'em' to NULL, since we only want the 'dm'. */
-                       DerivedMesh *dm;
-                       em = BKE_editmesh_from_object(ob);
-                       if (em) {
-                               editbmesh_get_derived_cage_and_final(sctx->depsgraph, sctx->scene, ob, em, CD_MASK_BAREMESH, &dm);
+       }
+
+       switch (ob->type) {
+               case OB_MESH:
+                       if (use_obedit) {
+                               BMEditMesh *em = BKE_editmesh_from_object(ob);
+                               retval = raycastEditMesh(
+                                       sctx,
+                                       ray_start, ray_dir,
+                                       ob, em, obmat, ob_index,
+                                       ray_depth, r_loc, r_no, r_index, r_hit_list);
                        }
                        else {
-                               dm = mesh_get_derived_final(sctx->depsgraph, sctx->scene, ob, CD_MASK_BAREMESH);
+                               retval = raycastMesh(
+                                       sctx,
+                                       ray_start, ray_dir,
+                                       ob, ob->data, obmat, ob_index,
+                                       ray_depth, r_loc, r_no, r_index, r_hit_list);
                        }
-                       retval = raycastDerivedMesh(
-                               sctx,
-                               ray_start, ray_dir,
-                               ob, dm, obmat, ob_index,
-                               ray_depth, r_loc, r_no, r_index, r_hit_list);
-               }
+                       break;
        }
 
        if (retval) {
                if (r_ob) {
                        *r_ob = ob;
+               }
+               if (r_obmat) {
                        copy_m4_m4(r_obmat, obmat);
                }
+               return true;
        }
 
-       return retval;
+       return false;
 }
 
 
@@ -768,16 +717,19 @@ struct RaycastObjUserData {
        Object **r_ob;
        float (*r_obmat)[4];
        ListBase *r_hit_list;
+       bool use_occlusion_test;
        bool ret;
 };
 
 static void raycast_obj_cb(SnapObjectContext *sctx, bool is_obedit, Object *ob, float obmat[4][4], void *data)
 {
        struct RaycastObjUserData *dt = data;
+
        dt->ret |= raycastObj(
                sctx,
                dt->ray_start, dt->ray_dir,
-               ob, obmat, dt->ob_index++, is_obedit,
+               ob, obmat, dt->ob_index++,
+               is_obedit, dt->use_occlusion_test,
                dt->ray_depth,
                dt->r_loc, dt->r_no, dt->r_index,
                dt->r_ob, dt->r_obmat,
@@ -814,8 +766,8 @@ static void raycast_obj_cb(SnapObjectContext *sctx, bool is_obedit, Object *ob,
  */
 static bool raycastObjects(
         SnapObjectContext *sctx,
+        const struct SnapObjectParams *params,
         const float ray_start[3], const float ray_dir[3],
-        const eSnapSelect snap_select, const bool use_object_edit_cage,
         /* read/write args */
         float *ray_depth,
         /* return args */
@@ -823,9 +775,6 @@ static bool raycastObjects(
         Object **r_ob, float r_obmat[4][4],
         ListBase *r_hit_list)
 {
-       ViewLayer *view_layer = DEG_get_evaluated_view_layer(sctx->depsgraph);
-       Object *obedit = use_object_edit_cage ? OBEDIT_FROM_VIEW_LAYER(view_layer) : NULL;
-
        struct RaycastObjUserData data = {
                .ray_start = ray_start,
                .ray_dir = ray_dir,
@@ -837,10 +786,11 @@ static bool raycastObjects(
                .r_ob = r_ob,
                .r_obmat = r_obmat,
                .r_hit_list = r_hit_list,
+               .use_occlusion_test = params->use_occlusion_test,
                .ret = false,
        };
 
-       iter_snap_objects(sctx, snap_select, obedit, raycast_obj_cb, &data);
+       iter_snap_objects(sctx, params, raycast_obj_cb, &data);
 
        return data.ret;
 }
@@ -852,65 +802,106 @@ static bool raycastObjects(
 /** Snap Nearest utilities
  * \{ */
 
-static void copy_dm_vert_no(const int index, float r_no[3], const BVHTreeFromMesh *data)
+static void cb_mvert_co_get(
+        const int index, const float **co, const BVHTreeFromMesh *data)
+{
+       *co = data->vert[index].co;
+}
+
+static void cb_bvert_co_get(
+        const int index, const float **co, const BMEditMesh *data)
+{
+       BMVert *eve = BM_vert_at_index(data->bm, index);
+       *co = eve->co;
+}
+
+static void cb_mvert_no_copy(
+        const int index, float r_no[3], const BVHTreeFromMesh *data)
 {
        const MVert *vert = data->vert + index;
 
        normal_short_to_float_v3(r_no, vert->no);
 }
 
-static void copy_bvert_no(const int index, float r_no[3], const BVHTreeFromEditMesh *data)
+static void cb_bvert_no_copy(
+        const int index, float r_no[3], const BMEditMesh *data)
 {
-       BMVert *eve = BM_vert_at_index(data->em->bm, index);
+       BMVert *eve = BM_vert_at_index(data->bm, index);
 
        copy_v3_v3(r_no, eve->no);
 }
 
-static void get_dm_edge_verts(const int index, const float *v_pair[2], const BVHTreeFromMesh *data)
+static void cb_medge_verts_get(
+        const int index, int v_index[2], const BVHTreeFromMesh *data)
+{
+       const MEdge *edge = &data->edge[index];
+
+       v_index[0] = edge->v1;
+       v_index[1] = edge->v2;
+
+}
+
+static void cb_bedge_verts_get(
+        const int index, int v_index[2], const BMEditMesh *data)
 {
-       const MVert *vert = data->vert;
-       const MEdge *edge = data->edge + index;
+       BMEdge *eed = BM_edge_at_index(data->bm, index);
+
+       v_index[0] = BM_elem_index_get(eed->v1);
+       v_index[1] = BM_elem_index_get(eed->v2);
+}
 
-       v_pair[0] = vert[edge->v1].co;
-       v_pair[1] = vert[edge->v2].co;
+static void cb_mlooptri_edges_get(
+        const int index, int v_index[3], const BVHTreeFromMesh *data)
+{
+       const MEdge *medge = data->edge;
+       const MLoop *mloop = data->loop;
+       const MLoopTri *lt = &data->looptri[index];
+       for (int j = 2, j_next = 0; j_next < 3; j = j_next++) {
+               const MEdge *ed = &medge[mloop[lt->tri[j]].e];
+               unsigned int tri_edge[2] = {mloop[lt->tri[j]].v, mloop[lt->tri[j_next]].v};
+               if (ELEM(ed->v1, tri_edge[0], tri_edge[1]) &&
+                   ELEM(ed->v2, tri_edge[0], tri_edge[1]))
+               {
+                       //printf("real edge found\n");
+                       v_index[j] = mloop[lt->tri[j]].e;
+               }
+               else
+                       v_index[j] = -1;
+       }
 }
 
-static void get_bedge_verts(const int index, const float *v_pair[2], const BVHTreeFromEditMesh *data)
+static void cb_mlooptri_verts_get(
+        const int index, int v_index[3], const BVHTreeFromMesh *data)
 {
-       BMEdge *eed = BM_edge_at_index(data->em->bm, index);
+       const MLoop *loop = data->loop;
+       const MLoopTri *looptri = &data->looptri[index];
 
-       v_pair[0] = eed->v1->co;
-       v_pair[1] = eed->v2->co;
+       v_index[0] = loop[looptri->tri[0]].v;
+       v_index[1] = loop[looptri->tri[1]].v;
+       v_index[2] = loop[looptri->tri[2]].v;
 }
 
 static bool test_projected_vert_dist(
-        const float depth_range[2], const float mval[2], const float co[3],
-        float pmat[4][4], const float win_half[2], const bool is_persp,
+        const struct DistProjectedAABBPrecalc *precalc,
+        const float (*clip_plane)[4], const int clip_plane_len,
+        const bool is_persp, const float co[3],
         float *dist_px_sq, float r_co[3])
 {
-       float depth;
-       if (is_persp) {
-               depth = mul_project_m4_v3_zfac(pmat, co);
-               if (depth < depth_range[0] || depth > depth_range[1]) {
-                       return false;
-               }
+       if (!isect_point_planes_v3_negated(clip_plane, clip_plane_len, co)) {
+               return false;
        }
 
        float co2d[2] = {
-               (dot_m4_v3_row_x(pmat, co) + pmat[3][0]),
-               (dot_m4_v3_row_y(pmat, co) + pmat[3][1]),
+               (dot_m4_v3_row_x(precalc->pmat, co) + precalc->pmat[3][0]),
+               (dot_m4_v3_row_y(precalc->pmat, co) + precalc->pmat[3][1]),
        };
 
        if (is_persp) {
-               mul_v2_fl(co2d, 1 / depth);
+               float w = mul_project_m4_v3_zfac(precalc->pmat, co);
+               mul_v2_fl(co2d, 1.0f / w);
        }
 
-       co2d[0] += 1.0f;
-       co2d[1] += 1.0f;
-       co2d[0] *= win_half[0];
-       co2d[1] *= win_half[1];
-
-       const float dist_sq = len_squared_v2v2(mval, co2d);
+       const float dist_sq = len_squared_v2v2(precalc->mval, co2d);
        if (dist_sq < *dist_px_sq) {
                copy_v3_v3(r_co, co);
                *dist_px_sq = dist_sq;
@@ -920,240 +911,142 @@ static bool test_projected_vert_dist(
 }
 
 static bool test_projected_edge_dist(
-        const float depth_range[2], const float mval[2],
-        float pmat[4][4], const float win_half[2], const bool is_persp,
-        const float ray_start[3], const float ray_dir[3],
-        const float va[3], const float vb[3],
+        const struct DistProjectedAABBPrecalc *precalc,
+        const float (*clip_plane)[4], const int clip_plane_len,
+        const bool is_persp, const float va[3], const float vb[3],
         float *dist_px_sq, float r_co[3])
 {
-
-       float tmp_co[3], depth;
-       dist_squared_ray_to_seg_v3(ray_start, ray_dir, va, vb, tmp_co, &depth);
-       return test_projected_vert_dist(depth_range, mval, tmp_co, pmat, win_half, is_persp, dist_px_sq, r_co);
+       float near_co[3], dummy_depth;
+       dist_squared_ray_to_seg_v3(
+               precalc->ray_origin,
+               precalc->ray_direction,
+               va, vb, near_co, &dummy_depth);
+
+       return test_projected_vert_dist(
+               precalc, clip_plane, clip_plane_len,
+               is_persp, near_co, dist_px_sq, r_co);
 }
-typedef struct Nearest2dPrecalc {
-       float ray_origin_local[3];
-       float ray_direction_local[3];
-       float ray_inv_dir[3];
 
-       float ray_min_dist;
-       float pmat[4][4]; /* perspective matrix multiplied by object matrix */
-       bool is_persp;
-       float win_half[2];
-
-       float mval[2];
-       bool sign[3];
-} Nearest2dPrecalc;
-
-/**
- * \param lpmat: Perspective matrix multiplied by object matrix
- */
-static void dist_squared_to_projected_aabb_precalc(
-        struct Nearest2dPrecalc *neasrest_precalc,
-        float lpmat[4][4], bool is_persp, const float win_half[2],
-        const float ray_min_dist, const float mval[2],
-        const float ray_origin_local[3], const float ray_direction_local[3])
+static bool snapMeshPolygon(
+        SnapObjectContext *sctx, SnapData *snapdata,
+        Object *ob, float obmat[4][4],
+        /* read/write args */
+        float *dist_px,
+        /* return args */
+        float r_loc[3], float r_no[3], int *r_index)
 {
-       copy_m4_m4(neasrest_precalc->pmat, lpmat);
-       neasrest_precalc->is_persp = is_persp;
-       copy_v2_v2(neasrest_precalc->win_half, win_half);
-       neasrest_precalc->ray_min_dist = ray_min_dist;
+       bool retval = false;
 
-       copy_v3_v3(neasrest_precalc->ray_origin_local, ray_origin_local);
-       copy_v3_v3(neasrest_precalc->ray_direction_local, ray_direction_local);
-       copy_v2_v2(neasrest_precalc->mval, mval);
+       float lpmat[4][4], dist_px_sq = SQUARE(*dist_px);
+       mul_m4_m4m4(lpmat, snapdata->pmat, obmat);
 
-       for (int i = 0; i < 3; i++) {
-               neasrest_precalc->ray_inv_dir[i] =
-                       (neasrest_precalc->ray_direction_local[i] != 0.0f) ?
-                       (1.0f / neasrest_precalc->ray_direction_local[i]) : FLT_MAX;
-               neasrest_precalc->sign[i] = (neasrest_precalc->ray_inv_dir[i] < 0.0f);
-       }
-}
+       struct DistProjectedAABBPrecalc neasrest_precalc;
+       dist_squared_to_projected_aabb_precalc(
+               &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval);
 
-/* Returns the distance from a 2d coordinate to a BoundBox (Projected) */
-static float dist_squared_to_projected_aabb(
-        struct Nearest2dPrecalc *data,
-        const float bbmin[3], const float bbmax[3],
-        bool r_axis_closest[3])
-{
-       float local_bvmin[3], local_bvmax[3];
-       if (data->sign[0]) {
-               local_bvmin[0] = bbmax[0];
-               local_bvmax[0] = bbmin[0];
-       }
-       else {
-               local_bvmin[0] = bbmin[0];
-               local_bvmax[0] = bbmax[0];
-       }
-       if (data->sign[1]) {
-               local_bvmin[1] = bbmax[1];
-               local_bvmax[1] = bbmin[1];
-       }
-       else {
-               local_bvmin[1] = bbmin[1];
-               local_bvmax[1] = bbmax[1];
-       }
-       if (data->sign[2]) {
-               local_bvmin[2] = bbmax[2];
-               local_bvmax[2] = bbmin[2];
-       }
-       else {
-               local_bvmin[2] = bbmin[2];
-               local_bvmax[2] = bbmax[2];
+       float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4];
+       transpose_m4_m4(tobmat, obmat);
+       for (int i = snapdata->clip_plane_len; i--;) {
+               mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]);
        }
 
-       const float tmin[3] = {
-               (local_bvmin[0] - data->ray_origin_local[0]) * data->ray_inv_dir[0],
-               (local_bvmin[1] - data->ray_origin_local[1]) * data->ray_inv_dir[1],
-               (local_bvmin[2] - data->ray_origin_local[2]) * data->ray_inv_dir[2],
-       };
-       const float tmax[3] = {
-               (local_bvmax[0] - data->ray_origin_local[0]) * data->ray_inv_dir[0],
-               (local_bvmax[1] - data->ray_origin_local[1]) * data->ray_inv_dir[1],
-               (local_bvmax[2] - data->ray_origin_local[2]) * data->ray_inv_dir[2],
-       };
-       /* `va` and `vb` are the coordinates of the AABB edge closest to the ray */
-       float va[3], vb[3];
-       /* `rtmin` and `rtmax` are the minimum and maximum distances of the ray hits on the AABB */
-       float rtmin, rtmax;
-       int main_axis;
-
-       if ((tmax[0] <= tmax[1]) && (tmax[0] <= tmax[2])) {
-               rtmax = tmax[0];
-               va[0] = vb[0] = local_bvmax[0];
-               main_axis = 3;
-               r_axis_closest[0] = data->sign[0];
-       }
-       else if ((tmax[1] <= tmax[0]) && (tmax[1] <= tmax[2])) {
-               rtmax = tmax[1];
-               va[1] = vb[1] = local_bvmax[1];
-               main_axis = 2;
-               r_axis_closest[1] = data->sign[1];
-       }
-       else {
-               rtmax = tmax[2];
-               va[2] = vb[2] = local_bvmax[2];
-               main_axis = 1;
-               r_axis_closest[2] = data->sign[2];
-       }
+       bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP;
 
-       if ((tmin[0] >= tmin[1]) && (tmin[0] >= tmin[2])) {
-               rtmin = tmin[0];
-               va[0] = vb[0] = local_bvmin[0];
-               main_axis -= 3;
-               r_axis_closest[0] = !data->sign[0];
-       }
-       else if ((tmin[1] >= tmin[0]) && (tmin[1] >= tmin[2])) {
-               rtmin = tmin[1];
-               va[1] = vb[1] = local_bvmin[1];
-               main_axis -= 1;
-               r_axis_closest[1] = !data->sign[1];
+       SnapObjectData *sod = BLI_ghash_lookup(sctx->cache.object_map, ob);
+       BLI_assert(sod != NULL);
+
+       if (sod->type == SNAP_MESH) {
+               Mesh *me = ob->data;
+               MPoly *mp = &me->mpoly[*r_index];
+
+               const MLoop *ml = &me->mloop[mp->loopstart];
+               for (int i = mp->totloop; i--; ml++) {
+                       if (snapdata->snap_to == SCE_SNAP_MODE_VERTEX) {
+                               const MVert *vert = &me->mvert[ml->v];
+                               if (test_projected_vert_dist(
+                                       &neasrest_precalc,
+                                       clip_planes_local, snapdata->clip_plane_len,
+                                       is_persp, vert->co,
+                                       &dist_px_sq, r_loc))
+                               {
+                                       normal_short_to_float_v3(r_no, vert->no);
+                                       *r_index = ml->v;
+                                       retval = true;
+                               }
+                       }
+                       else {
+                               float co_pair[2][3];
+                               const MEdge *edge = &me->medge[ml->e];
+                               copy_v3_v3(co_pair[0], me->mvert[edge->v1].co);
+                               copy_v3_v3(co_pair[1], me->mvert[edge->v2].co);
+                               if (test_projected_edge_dist(
+                                       &neasrest_precalc,
+                                       clip_planes_local, snapdata->clip_plane_len,
+                                       is_persp, co_pair[0], co_pair[1],
+                                       &dist_px_sq, r_loc))
+                               {
+                                       sub_v3_v3v3(r_no, co_pair[0], co_pair[1]);
+                                       *r_index = ml->e;
+                                       retval = true;
+                               }
+                       }
+               }
        }
        else {
-               rtmin = tmin[2];
-               va[2] = vb[2] = local_bvmin[2];
-               main_axis -= 2;
-               r_axis_closest[2] = !data->sign[2];
-       }
-       if (main_axis < 0) {
-               main_axis += 3;
-       }
-
-#define IGNORE_BEHIND_RAY
-#ifdef IGNORE_BEHIND_RAY
-       float depth_max = depth_get(local_bvmax, data->ray_origin_local, data->ray_direction_local);
-       if (depth_max < data->ray_min_dist) {
-               return FLT_MAX;
-       }
-#endif
-#undef IGNORE_BEHIND_RAY
-
-       /* if rtmin <= rtmax, ray intersect `AABB` */
-       if (rtmin <= rtmax) {
-               return 0;
+               BLI_assert(sod->type == SNAP_EDIT_MESH);
+
+               BMEditMesh *em = BKE_editmesh_from_object(ob);
+               BM_mesh_elem_table_ensure(em->bm, BM_FACE);
+               BMFace *f = BM_face_at_index(em->bm, *r_index);
+
+               BMLoop *l_iter, *l_first;
+               l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+
+               do {
+                       if (snapdata->snap_to == SCE_SNAP_MODE_VERTEX) {
+                               const float *co = l_iter->v->co;
+                               if (test_projected_vert_dist(
+                                       &neasrest_precalc,
+                                       clip_planes_local, snapdata->clip_plane_len,
+                                       is_persp, co,
+                                       &dist_px_sq, r_loc))
+                               {
+                                       copy_v3_v3(r_no, l_iter->v->no);
+                                       *r_index = BM_elem_index_get(l_iter->v);
+                                       retval = true;
+                               }
+                       }
+                       else {
+                               float co_pair[2][3];
+                               copy_v3_v3(co_pair[0], l_iter->e->v1->co);
+                               copy_v3_v3(co_pair[1], l_iter->e->v2->co);
+                               if (test_projected_edge_dist(
+                                       &neasrest_precalc,
+                                       clip_planes_local, snapdata->clip_plane_len,
+                                       is_persp, co_pair[0], co_pair[1],
+                                       &dist_px_sq, r_loc))
+                               {
+                                       sub_v3_v3v3(r_no, co_pair[0], co_pair[1]);
+                                       *r_index = BM_elem_index_get(l_iter->e);
+                                       retval = true;
+                               }
+                       }
+               } while ((l_iter = l_iter->next) != l_first);
        }
 
-       if (data->sign[main_axis]) {
-               va[main_axis] = local_bvmax[main_axis];
-               vb[main_axis] = local_bvmin[main_axis];
-       }
-       else {
-               va[main_axis] = local_bvmin[main_axis];
-               vb[main_axis] = local_bvmax[main_axis];
-       }
-       float scale = fabsf(local_bvmax[main_axis] - local_bvmin[main_axis]);
+       if (retval) {
+               float imat[4][4];
+               invert_m4_m4(imat, obmat);
 
-       float (*pmat)[4] = data->pmat;
+               mul_m4_v3(obmat, r_loc);
+               mul_transposed_mat3_m4_v3(obmat, r_no);
+               normalize_v3(r_no);
 
-       float va2d[2] = {
-               (dot_m4_v3_row_x(pmat, va) + pmat[3][0]),
-               (dot_m4_v3_row_y(pmat, va) + pmat[3][1]),
-       };
-       float vb2d[2] = {
-               (va2d[0] + pmat[main_axis][0] * scale),
-               (va2d[1] + pmat[main_axis][1] * scale),
-       };
+               *dist_px = sqrtf(dist_px_sq);
 
-       if (data->is_persp) {
-               float depth_a = mul_project_m4_v3_zfac(pmat, va);
-               float depth_b = depth_a + pmat[main_axis][3] * scale;
-               va2d[0] /= depth_a;
-               va2d[1] /= depth_a;
-               vb2d[0] /= depth_b;
-               vb2d[1] /= depth_b;
-       }
-
-       va2d[0] += 1.0f;
-       va2d[1] += 1.0f;
-       vb2d[0] += 1.0f;
-       vb2d[1] += 1.0f;
-
-       va2d[0] *= data->win_half[0];
-       va2d[1] *= data->win_half[1];
-       vb2d[0] *= data->win_half[0];
-       vb2d[1] *= data->win_half[1];
-
-       float dvec[2], edge[2], lambda, rdist;
-       sub_v2_v2v2(dvec, data->mval, va2d);
-       sub_v2_v2v2(edge, vb2d, va2d);
-       lambda = dot_v2v2(dvec, edge);
-       if (lambda != 0.0f) {
-               lambda /= len_squared_v2(edge);
-               if (lambda <= 0.0f) {
-                       rdist = len_squared_v2v2(data->mval, va2d);
-                       r_axis_closest[main_axis] = true;
-               }
-               else if (lambda >= 1.0f) {
-                       rdist = len_squared_v2v2(data->mval, vb2d);
-                       r_axis_closest[main_axis] = false;
-               }
-               else {
-                       va2d[0] += edge[0] * lambda;
-                       va2d[1] += edge[1] * lambda;
-                       rdist = len_squared_v2v2(data->mval, va2d);
-                       r_axis_closest[main_axis] = lambda < 0.5f;
-               }
-       }
-       else {
-               rdist = len_squared_v2v2(data->mval, va2d);
+               return true;
        }
-       return rdist;
-}
-
-static float dist_squared_to_projected_aabb_simple(
-        float lpmat[4][4], const float win_half[2],
-        const float ray_min_dist, const float mval[2],
-        const float ray_origin_local[3], const float ray_direction_local[3],
-        const float bbmin[3], const float bbmax[3])
-{
-       struct Nearest2dPrecalc data;
-       dist_squared_to_projected_aabb_precalc(
-               &data, lpmat, true, win_half, ray_min_dist,
-               mval, ray_origin_local, ray_direction_local);
-
-       bool dummy[3] = {true, true, true};
-       return dist_squared_to_projected_aabb(&data, bbmin, bbmax, dummy);
+       return false;
 }
 
 /** \} */
@@ -1162,93 +1055,125 @@ static float dist_squared_to_projected_aabb_simple(
 /** Walk DFS
  * \{ */
 
-typedef void (*Nearest2DGetEdgeVertsCallback)(const int index, const float *v_pair[2], void *data);
+typedef void (*Nearest2DGetVertCoCallback)(const int index, const float **co, void *data);
+typedef void (*Nearest2DGetEdgeVertsCallback)(const int index, int v_index[2], void *data);
+typedef void (*Nearest2DGetTriVertsCallback)(const int index, int v_index[3], void *data);
+typedef void (*Nearest2DGetTriEdgesCallback)(const int index, int e_index[3], void *data); /* Equal the previous one */
 typedef void (*Nearest2DCopyVertNoCallback)(const int index, float r_no[3], void *data);
 
 typedef struct Nearest2dUserData {
-       struct Nearest2dPrecalc data_precalc;
-
-       float dist_px_sq;
-
-       bool r_axis_closest[3];
-
-       float depth_range[2];
+       bool is_persp;
+       short snap_to;
 
        void *userdata;
-       Nearest2DGetEdgeVertsCallback get_edge_verts;
+       Nearest2DGetVertCoCallback get_vert_co;
+       Nearest2DGetEdgeVertsCallback get_edge_verts_index;
+       Nearest2DGetTriVertsCallback get_tri_verts_index;
+       Nearest2DGetTriEdgesCallback get_tri_edges_index;
        Nearest2DCopyVertNoCallback copy_vert_no;
 
-       int index;
-       float co[3];
-       float no[3];
 } Nearest2dUserData;
 
 
-static bool cb_walk_parent_snap_project(const BVHTreeAxisRange *bounds, void *user_data)
-{
-       Nearest2dUserData *data = user_data;
-       const float bbmin[3] = {bounds[0].min, bounds[1].min, bounds[2].min};
-       const float bbmax[3] = {bounds[0].max, bounds[1].max, bounds[2].max};
-       const float rdist = dist_squared_to_projected_aabb(
-               &data->data_precalc, bbmin, bbmax, data->r_axis_closest);
-       return rdist < data->dist_px_sq;
-}
-
-static bool cb_walk_leaf_snap_vert(const BVHTreeAxisRange *bounds, int index, void *userdata)
+static void cb_walk_leaf_snap_vert(
+        void *userdata, int index,
+        const struct DistProjectedAABBPrecalc *precalc,
+        const float (*clip_plane)[4], const int clip_plane_len,
+        BVHTreeNearest *nearest)
 {
        struct Nearest2dUserData *data = userdata;
-       struct Nearest2dPrecalc *neasrest_precalc = &data->data_precalc;
-       const float co[3] = {
-               (bounds[0].min + bounds[0].max) / 2,
-               (bounds[1].min + bounds[1].max) / 2,
-               (bounds[2].min + bounds[2].max) / 2,
-       };
+
+       const float *co;
+       data->get_vert_co(index, &co, data->userdata);
 
        if (test_projected_vert_dist(
-               data->depth_range,
-               neasrest_precalc->mval, co,
-               neasrest_precalc->pmat,
-               neasrest_precalc->win_half,
-               neasrest_precalc->is_persp,
-               &data->dist_px_sq,
-               data->co))
+               precalc,
+               clip_plane,
+               clip_plane_len,
+               data->is_persp, co,
+               &nearest->dist_sq,
+               nearest->co))
        {
-               data->copy_vert_no(index, data->no, data->userdata);
-               data->index = index;
+               data->copy_vert_no(index, nearest->no, data->userdata);
+               nearest->index = index;
        }
-       return true;
 }
 
-static bool cb_walk_leaf_snap_edge(const BVHTreeAxisRange *UNUSED(bounds), int index, void *userdata)
+static void cb_walk_leaf_snap_edge(
+        void *userdata, int index,
+        const struct DistProjectedAABBPrecalc *precalc,
+        const float (*clip_plane)[4], const int clip_plane_len,
+        BVHTreeNearest *nearest)
 {
        struct Nearest2dUserData *data = userdata;
-       struct Nearest2dPrecalc *neasrest_precalc = &data->data_precalc;
-
-       const float *v_pair[2];
-       data->get_edge_verts(index, v_pair, data->userdata);
-
-       if (test_projected_edge_dist(
-               data->depth_range,
-               neasrest_precalc->mval,
-               neasrest_precalc->pmat,
-               neasrest_precalc->win_half,
-               neasrest_precalc->is_persp,
-               neasrest_precalc->ray_origin_local,
-               neasrest_precalc->ray_direction_local,
-               v_pair[0], v_pair[1],
-               &data->dist_px_sq,
-               data->co))
-       {
-               sub_v3_v3v3(data->no, v_pair[0], v_pair[1]);
-               data->index = index;
+
+       int vindex[2];
+       data->get_edge_verts_index(index, vindex, data->userdata);
+
+       if (data->snap_to == SCE_SNAP_MODE_EDGE) {
+               const float *v_pair[2];
+               data->get_vert_co(vindex[0], &v_pair[0], data->userdata);
+               data->get_vert_co(vindex[1], &v_pair[1], data->userdata);
+
+               if (test_projected_edge_dist(
+                       precalc,
+                       clip_plane,
+                       clip_plane_len,
+                       data->is_persp,
+                       v_pair[0], v_pair[1],
+                       &nearest->dist_sq,
+                       nearest->co))
+               {
+                       sub_v3_v3v3(nearest->no, v_pair[0], v_pair[1]);
+                       nearest->index = index;
+               }
+       }
+       else {
+               for (int i = 0; i < 2; i++) {
+                       if (vindex[i] == nearest->index) {
+                               continue;
+                       }
+                       cb_walk_leaf_snap_vert(
+                               userdata, vindex[i], precalc,
+                               clip_plane, clip_plane_len, nearest);
+               }
        }
-       return true;
 }
 
-static bool cb_nearest_walk_order(const BVHTreeAxisRange *UNUSED(bounds), char axis, void *userdata)
+static void cb_walk_leaf_snap_tri(
+        void *userdata, int index,
+        const struct DistProjectedAABBPrecalc *precalc,
+        const float (*clip_plane)[4], const int clip_plane_len,
+        BVHTreeNearest *nearest)
 {
-       const bool *r_axis_closest = ((struct Nearest2dUserData *)userdata)->r_axis_closest;
-       return r_axis_closest[axis];
+       struct Nearest2dUserData *data = userdata;
+
+       if (data->snap_to == SCE_SNAP_MODE_EDGE) {
+               int eindex[3];
+               data->get_tri_edges_index(index, eindex, data->userdata);
+               for (int i = 0; i < 3; i++) {
+                       if (eindex[i] != -1) {
+                               if (eindex[i] == nearest->index) {
+                                       continue;
+                               }
+                               cb_walk_leaf_snap_edge(
+                                       userdata, eindex[i], precalc,
+                                       clip_plane, clip_plane_len, nearest);
+                       }
+               }
+       }
+       else {
+               int vindex[3];
+               data->get_tri_verts_index(index, vindex, data->userdata);
+               for (int i = 0; i < 3; i++) {
+                       if (vindex[i] == nearest->index) {
+                               continue;
+                       }
+                       cb_walk_leaf_snap_vert(
+                               userdata, vindex[i], precalc,
+                               clip_plane, clip_plane_len, nearest);
+               }
+       }
 }
 
 /** \} */
@@ -1261,30 +1186,43 @@ static bool snapArmature(
         SnapData *snapdata,
         Object *ob, bArmature *arm, float obmat[4][4],
         /* read/write args */
-        float *ray_depth, float *dist_px,
+        float *dist_px,
         /* return args */
-        float r_loc[3], float *UNUSED(r_no))
+        float r_loc[3], float *UNUSED(r_no), int *r_index)
 {
        bool retval = false;
 
-       float ray_start_local[3], ray_normal_local[3]; /* Used only in the snap to edges */
-       if (snapdata->snap_to == SCE_SNAP_MODE_EDGE) {
-               float imat[4][4];
-               invert_m4_m4(imat, obmat);
+       if (snapdata->snap_to == SCE_SNAP_MODE_FACE) { /* Currently only edge and vert */
+               return retval;
+       }
+
+       float lpmat[4][4], dist_px_sq = SQUARE(*dist_px);
+       mul_m4_m4m4(lpmat, snapdata->pmat, obmat);
+
+       struct DistProjectedAABBPrecalc neasrest_precalc;
+       dist_squared_to_projected_aabb_precalc(
+               &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval);
+
+       /* Test BoundBox */
+       BoundBox *bb = BKE_armature_boundbox_get(ob);
+       if (bb) {
+               bool dummy[3];
+               /* In vertex and edges you need to get the pixel distance from ray to BoundBox, see: T46099, T46816 */
+               float bb_dist_px_sq = dist_squared_to_projected_aabb(
+                       &neasrest_precalc, bb->vec[0], bb->vec[6], dummy);
 
-               copy_v3_v3(ray_start_local, snapdata->ray_origin);
-               copy_v3_v3(ray_normal_local, snapdata->ray_dir);
-               mul_m4_v3(imat, ray_start_local);
-               mul_mat3_m4_v3(imat, ray_normal_local);
+               if (bb_dist_px_sq > dist_px_sq) {
+                       return retval;
+               }
        }
-       else if (snapdata->snap_to != SCE_SNAP_MODE_VERTEX) { /* Currently only edge and vert */
-               return retval;
+
+       float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4];
+       transpose_m4_m4(tobmat, obmat);
+       for (int i = snapdata->clip_plane_len; i--;) {
+               mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]);
        }
 
        bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP;
-       float lpmat[4][4], dist_px_sq;
-       mul_m4_m4m4(lpmat, snapdata->pmat, obmat);
-       dist_px_sq = SQUARE(*dist_px);
 
        if (arm->edbo) {
                for (EditBone *eBone = arm->edbo->first; eBone; eBone = eBone->next) {
@@ -1294,19 +1232,19 @@ static bool snapArmature(
                                        switch (snapdata->snap_to) {
                                                case SCE_SNAP_MODE_VERTEX:
                                                        retval |= test_projected_vert_dist(
-                                                               snapdata->depth_range, snapdata->mval, eBone->head,
-                                                               lpmat, snapdata->win_half, is_persp, &dist_px_sq,
-                                                               r_loc);
+                                                               &neasrest_precalc,
+                                                               clip_planes_local, snapdata->clip_plane_len,
+                                                               is_persp, eBone->head, &dist_px_sq, r_loc);
                                                        retval |= test_projected_vert_dist(
-                                                               snapdata->depth_range, snapdata->mval, eBone->tail,
-                                                               lpmat, snapdata->win_half, is_persp, &dist_px_sq,
-                                                               r_loc);
+                                                               &neasrest_precalc,
+                                                               clip_planes_local, snapdata->clip_plane_len,
+                                                               is_persp, eBone->tail, &dist_px_sq, r_loc);
                                                        break;
                                                case SCE_SNAP_MODE_EDGE:
                                                        retval |= test_projected_edge_dist(
-                                                               snapdata->depth_range, snapdata->mval, lpmat,
-                                                               snapdata->win_half, is_persp, ray_start_local, ray_normal_local,
-                                                               eBone->head, eBone->tail,
+                                                               &neasrest_precalc,
+                                                               clip_planes_local, snapdata->clip_plane_len,
+                                                               is_persp, eBone->head, eBone->tail,
                                                                &dist_px_sq, r_loc);
                                                        break;
                                        }
@@ -1325,19 +1263,19 @@ static bool snapArmature(
                                switch (snapdata->snap_to) {
                                        case SCE_SNAP_MODE_VERTEX:
                                                retval |= test_projected_vert_dist(
-                                                       snapdata->depth_range, snapdata->mval, head_vec,
-                                                       lpmat, snapdata->win_half, is_persp, &dist_px_sq,
-                                                       r_loc);
+                                                       &neasrest_precalc,
+                                                       clip_planes_local, snapdata->clip_plane_len,
+                                                       is_persp, head_vec, &dist_px_sq, r_loc);
                                                retval |= test_projected_vert_dist(
-                                                       snapdata->depth_range, snapdata->mval, tail_vec,
-                                                       lpmat, snapdata->win_half, is_persp, &dist_px_sq,
-                                                       r_loc);
+                                                       &neasrest_precalc,
+                                                       clip_planes_local, snapdata->clip_plane_len,
+                                                       is_persp, tail_vec, &dist_px_sq, r_loc);
                                                break;
                                        case SCE_SNAP_MODE_EDGE:
                                                retval |= test_projected_edge_dist(
-                                                       snapdata->depth_range, snapdata->mval, lpmat,
-                                                       snapdata->win_half, is_persp, ray_start_local, ray_normal_local,
-                                                       head_vec, tail_vec,
+                                                       &neasrest_precalc,
+                                                       clip_planes_local, snapdata->clip_plane_len,
+                                                       is_persp, head_vec, tail_vec,
                                                        &dist_px_sq, r_loc);
                                                break;
                                }
@@ -1347,7 +1285,10 @@ static bool snapArmature(
        if (retval) {
                *dist_px = sqrtf(dist_px_sq);
                mul_m4_v3(obmat, r_loc);
-               *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir);
+               if (r_index) {
+                       /* Does not support index. */
+                       *r_index = -1;
+               }
                return true;
        }
        return false;
@@ -1355,11 +1296,11 @@ static bool snapArmature(
 
 static bool snapCurve(
         SnapData *snapdata,
-        Curve *cu, float obmat[4][4], bool use_obedit,
+        Object *ob, float obmat[4][4], bool use_obedit,
         /* read/write args */
-        float *ray_depth, float *dist_px,
+        float *dist_px,
         /* return args */
-        float r_loc[3], float *UNUSED(r_no))
+        float r_loc[3], float *UNUSED(r_no), int *r_index)
 {
        bool retval = false;
 
@@ -1368,10 +1309,37 @@ static bool snapCurve(
                return retval;
        }
 
-       bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP;
-       float lpmat[4][4], dist_px_sq;
+       Curve *cu = ob->data;
+       float dist_px_sq = SQUARE(*dist_px);
+
+       float lpmat[4][4];
        mul_m4_m4m4(lpmat, snapdata->pmat, obmat);
-       dist_px_sq = SQUARE(*dist_px);
+
+       struct DistProjectedAABBPrecalc neasrest_precalc;
+       dist_squared_to_projected_aabb_precalc(
+               &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval);
+
+       /* Test BoundBox */
+       BoundBox *bb = BKE_curve_boundbox_get(ob);
+       if (bb) {
+               bool dummy[3];
+               /* In vertex and edges you need to get the pixel distance from ray to BoundBox, see: T46099, T46816 */
+               float bb_dist_px_sq = dist_squared_to_projected_aabb(
+                       &neasrest_precalc, bb->vec[0], bb->vec[6], dummy);
+
+               if (bb_dist_px_sq > dist_px_sq) {
+                       return retval;
+               }
+       }
+
+
+       float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4];
+       transpose_m4_m4(tobmat, obmat);
+       for (int i = snapdata->clip_plane_len; i--;) {
+               mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]);
+       }
+
+       bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP;
 
        for (Nurb *nu = (use_obedit ? cu->editnurb->nurbs.first : cu->nurb.first); nu; nu = nu->next) {
                for (int u = 0; u < nu->pntsu; u++) {
@@ -1385,24 +1353,27 @@ static bool snapCurve(
                                                                break;
                                                        }
                                                        retval |= test_projected_vert_dist(
-                                                               snapdata->depth_range, snapdata->mval, nu->bezt[u].vec[1],
-                                                               lpmat, snapdata->win_half, is_persp, &dist_px_sq,
+                                                               &neasrest_precalc,
+                                                               clip_planes_local, snapdata->clip_plane_len,
+                                                               is_persp, nu->bezt[u].vec[1], &dist_px_sq,
                                                                r_loc);
                                                        /* don't snap if handle is selected (moving), or if it is aligning to a moving handle */
                                                        if (!(nu->bezt[u].f1 & SELECT) &&
                                                            !(nu->bezt[u].h1 & HD_ALIGN && nu->bezt[u].f3 & SELECT))
                                                        {
                                                                retval |= test_projected_vert_dist(
-                                                                       snapdata->depth_range, snapdata->mval, nu->bezt[u].vec[0],
-                                                                       lpmat, snapdata->win_half, is_persp, &dist_px_sq,
+                                                                       &neasrest_precalc,
+                                                                       clip_planes_local, snapdata->clip_plane_len,
+                                                                       is_persp, nu->bezt[u].vec[0], &dist_px_sq,
                                                                        r_loc);
                                                        }
                                                        if (!(nu->bezt[u].f3 & SELECT) &&
                                                            !(nu->bezt[u].h2 & HD_ALIGN && nu->bezt[u].f1 & SELECT))
                                                        {
                                                                retval |= test_projected_vert_dist(
-                                                                       snapdata->depth_range, snapdata->mval, nu->bezt[u].vec[2],
-                                                                       lpmat, snapdata->win_half, is_persp, &dist_px_sq,
+                                                                       &neasrest_precalc,
+                                                                       clip_planes_local, snapdata->clip_plane_len,
+                                                                       is_persp, nu->bezt[u].vec[2], &dist_px_sq,
                                                                        r_loc);
                                                        }
                                                }
@@ -1412,8 +1383,9 @@ static bool snapCurve(
                                                                break;
                                                        }
                                                        retval |= test_projected_vert_dist(
-                                                               snapdata->depth_range, snapdata->mval, nu->bp[u].vec,
-                                                               lpmat, snapdata->win_half, is_persp, &dist_px_sq,
+                                                               &neasrest_precalc,
+                                                               clip_planes_local, snapdata->clip_plane_len,
+                                                               is_persp, nu->bp[u].vec, &dist_px_sq,
                                                                r_loc);
                                                }
                                        }
@@ -1422,29 +1394,32 @@ static bool snapCurve(
                                                if (nu->pntsu > 1) {
                                                        if (nu->bezt) {
                                                                retval |= test_projected_vert_dist(
-                                                                       snapdata->depth_range, snapdata->mval, nu->bezt[u].vec[1],
-                                                                       lpmat, snapdata->win_half, is_persp, &dist_px_sq,
+                                                                       &neasrest_precalc,
+                                                                       clip_planes_local, snapdata->clip_plane_len,
+                                                                       is_persp, nu->bezt[u].vec[1], &dist_px_sq,
                                                                        r_loc);
                                                        }
                                                        else {
                                                                retval |= test_projected_vert_dist(
-                                                                       snapdata->depth_range, snapdata->mval, nu->bp[u].vec,
-                                                                       lpmat, snapdata->win_half, is_persp, &dist_px_sq,
+                                                                       &neasrest_precalc,
+                                                                       clip_planes_local, snapdata->clip_plane_len,
+                                                                       is_persp, nu->bp[u].vec, &dist_px_sq,
                                                                        r_loc);
                                                        }
                                                }
                                        }
                                        break;
                                }
-                               default:
-                                       break;
                        }
                }
        }
        if (retval) {
                *dist_px = sqrtf(dist_px_sq);
                mul_m4_v3(obmat, r_loc);
-               *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir);
+               if (r_index) {
+                       /* Does not support index yet. */
+                       *r_index = -1;
+               }
                return true;
        }
        return false;
@@ -1455,9 +1430,9 @@ static bool snapEmpty(
         SnapData *snapdata,
         Object *ob, float obmat[4][4],
         /* read/write args */
-        float *ray_depth, float *dist_px,
+        float *dist_px,
         /* return args */
-        float r_loc[3], float *UNUSED(r_no))
+        float r_loc[3], float *UNUSED(r_no), int *r_index)
 {
        bool retval = false;
 
@@ -1469,35 +1444,48 @@ static bool snapEmpty(
        switch (snapdata->snap_to) {
                case SCE_SNAP_MODE_VERTEX:
                {
+                       struct DistProjectedAABBPrecalc neasrest_precalc;
+                       dist_squared_to_projected_aabb_precalc(
+                               &neasrest_precalc, snapdata->pmat, snapdata->win_size, snapdata->mval);
+
+                       float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4];
+                       transpose_m4_m4(tobmat, obmat);
+                       for (int i = snapdata->clip_plane_len; i--;) {
+                               mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]);
+                       }
+
                        bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP;
                        float dist_px_sq = SQUARE(*dist_px);
-                       float tmp_co[3];
-                       copy_v3_v3(tmp_co, obmat[3]);
+                       float co[3];
+                       copy_v3_v3(co, obmat[3]);
                        if (test_projected_vert_dist(
-                               snapdata->depth_range, snapdata->mval, tmp_co,
-                               snapdata->pmat, snapdata->win_half, is_persp, &dist_px_sq,
-                               r_loc))
+                               &neasrest_precalc,
+                               clip_planes_local, snapdata->clip_plane_len,
+                               is_persp, co, &dist_px_sq, r_loc))
                        {
                                *dist_px = sqrtf(dist_px_sq);
-                               *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir);
                                retval = true;
                        }
                        break;
                }
-               default:
-                       break;
        }
 
-       return retval;
+       if (retval && r_index) {
+               /* Does not support index. */
+               *r_index = -1;
+               return true;
+       }
+
+       return false;
 }
 
 static bool snapCamera(
         const SnapObjectContext *sctx, SnapData *snapdata,
         Object *object, float obmat[4][4],
         /* read/write args */
-        float *ray_depth, float *dist_px,
+        float *dist_px,
         /* return args */
-        float r_loc[3], float *UNUSED(r_no))
+        float r_loc[3], float *UNUSED(r_no), int *r_index)
 {
        Scene *scene = sctx->scene;
 
@@ -1516,6 +1504,12 @@ static bool snapCamera(
                return retval;
        }
 
+       float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4];
+       transpose_m4_m4(tobmat, obmat);
+       for (int i = snapdata->clip_plane_len; i--;) {
+               mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]);
+       }
+
        tracking = &clip->tracking;
 
        BKE_tracking_get_camera_object_matrix(scene, object, orig_camera_mat);
@@ -1527,6 +1521,9 @@ static bool snapCamera(
                case SCE_SNAP_MODE_VERTEX:
                {
                        MovieTrackingObject *tracking_object;
+                       struct DistProjectedAABBPrecalc neasrest_precalc;
+                       dist_squared_to_projected_aabb_precalc(
+                               &neasrest_precalc, snapdata->pmat, snapdata->win_size, snapdata->mval);
 
                        for (tracking_object = tracking->objects.first;
                             tracking_object;
@@ -1563,92 +1560,72 @@ static bool snapCamera(
 
                                        mul_m4_v3(vertex_obmat, bundle_pos);
                                        retval |= test_projected_vert_dist(
-                                               snapdata->depth_range, snapdata->mval, bundle_pos,
-                                               snapdata->pmat, snapdata->win_half, is_persp, &dist_px_sq,
-                                               r_loc);
+                                               &neasrest_precalc,
+                                               clip_planes_local, snapdata->clip_plane_len,
+                                               is_persp, bundle_pos, &dist_px_sq, r_loc);
                                }
                        }
 
                        break;
                }
-               default:
-                       break;
        }
 
        if (retval) {
                *dist_px = sqrtf(dist_px_sq);
-               *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir);
+               if (r_index) {
+                       /* Does not support index. */
+                       *r_index = -1;
+               }
                return true;
        }
        return false;
 }
 
-static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt)
-{
-       const int *index_mp_to_orig = dm->getPolyDataArray(dm, CD_ORIGINDEX);
-       return index_mp_to_orig ? index_mp_to_orig[lt->poly] : lt->poly;
-}
-
-static bool snapDerivedMesh(
+static bool snapMesh(
         SnapObjectContext *sctx, SnapData *snapdata,
-        Object *ob, DerivedMesh *dm, float obmat[4][4],
+        Object *ob, Mesh *me, float obmat[4][4],
         /* read/write args */
-        float *ray_depth, float *dist_px,
+        float *dist_px,
         /* return args */
-        float r_loc[3], float r_no[3])
+        float r_loc[3], float r_no[3], int *r_index)
 {
        bool retval = false;
 
        if (snapdata->snap_to == SCE_SNAP_MODE_EDGE) {
-               if (dm->getNumEdges(dm) == 0) {
+               if (me->totedge == 0) {
                        return retval;
                }
        }
        else {
-               if (dm->getNumVerts(dm) == 0) {
+               if (me->totvert == 0) {
                        return retval;
                }
        }
 
-       float imat[4][4];
-       float timat[3][3]; /* transpose inverse matrix for normals */
-       float ray_normal_local[3];
-       float local_scale;
-
-       invert_m4_m4(imat, obmat);
-       transpose_m3_m4(timat, imat);
-
-       copy_v3_v3(ray_normal_local, snapdata->ray_dir);
-
-       mul_mat3_m4_v3(imat, ray_normal_local);
-
-       /* local scale in normal direction */
-       local_scale = normalize_v3(ray_normal_local);
-
        float lpmat[4][4];
-       float ray_org_local[3];
-       float ray_min_dist;
-
        mul_m4_m4m4(lpmat, snapdata->pmat, obmat);
-       ray_min_dist = snapdata->depth_range[0] * local_scale;
 
-       copy_v3_v3(ray_org_local, snapdata->ray_origin);
-       mul_m4_v3(imat, ray_org_local);
+       float dist_px_sq = SQUARE(*dist_px);
 
        /* Test BoundBox */
-       BoundBox *bb = BKE_object_boundbox_get(ob);
+       BoundBox *bb = BKE_mesh_boundbox_get(ob);
        if (bb) {
                /* In vertex and edges you need to get the pixel distance from ray to BoundBox, see: T46099, T46816 */
-               float dist_px_sq = dist_squared_to_projected_aabb_simple(
-                           lpmat, snapdata->win_half, ray_min_dist, snapdata->mval,
-                           ray_org_local, ray_normal_local, bb->vec[0], bb->vec[6]);
-               if (dist_px_sq > SQUARE(*dist_px)) {
+
+               struct DistProjectedAABBPrecalc data_precalc;
+               dist_squared_to_projected_aabb_precalc(
+                       &data_precalc, lpmat, snapdata->win_size, snapdata->mval);
+
+               bool dummy[3];
+               float bb_dist_px_sq = dist_squared_to_projected_aabb(
+                       &data_precalc, bb->vec[0], bb->vec[6], dummy);
+
+               if (bb_dist_px_sq > dist_px_sq) {
                        return retval;
                }
        }
 
        SnapObjectData_Mesh *sod = NULL;
-       BVHTreeFromMesh *treedata = NULL;
 
        void **sod_p;
        if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) {
@@ -1657,96 +1634,150 @@ static bool snapDerivedMesh(
        else {
                sod = *sod_p = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*sod));
                sod->sd.type = SNAP_MESH;
+               /* start assuming that it has each of these element types */
+               sod->has_looptris = true;
+               sod->has_loose_edge = true;
+               sod->has_loose_vert = true;
        }
 
-       int tree_index = -1;
-       switch (snapdata->snap_to) {
-               case SCE_SNAP_MODE_EDGE:
-                       tree_index = 1;
-                       break;
-               case SCE_SNAP_MODE_VERTEX:
-                       tree_index = 0;
-                       break;
+       BVHTreeFromMesh *treedata, dummy_treedata;
+       BVHTree **bvhtree;
+       treedata = &sod->treedata;
+       bvhtree = sod->bvhtree;
+
+       /* the tree is owned by the DM and may have been freed since we last used! */
+       if ((sod->has_looptris   && treedata->tree && !bvhcache_has_tree(me->runtime.bvh_cache, treedata->tree)) ||
+           (sod->has_loose_edge && bvhtree[0]     && !bvhcache_has_tree(me->runtime.bvh_cache, bvhtree[0]))     ||
+           (sod->has_loose_vert && bvhtree[1]     && !bvhcache_has_tree(me->runtime.bvh_cache, bvhtree[1])))
+       {
+               BLI_assert(!treedata->tree || !bvhcache_has_tree(me->runtime.bvh_cache, treedata->tree));
+               BLI_assert(!bvhtree[0]     || !bvhcache_has_tree(me->runtime.bvh_cache, bvhtree[0]));
+               BLI_assert(!bvhtree[1]     || !bvhcache_has_tree(me->runtime.bvh_cache, bvhtree[1]));
+
+               free_bvhtree_from_mesh(treedata);
+               bvhtree[0] = NULL;
+               bvhtree[1] = NULL;
        }
-       if (tree_index != -1) {
-               if (sod->bvh_trees[tree_index] == NULL) {
-                       sod->bvh_trees[tree_index] = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*treedata));
+
+       if (sod->has_looptris && treedata->tree == NULL) {
+               BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI, 4);
+               sod->has_looptris = (treedata->tree != NULL);
+               if (sod->has_looptris) {
+                       /* Make sure that the array of edges is referenced in the callbacks. */
+                       treedata->edge = me->medge; /* CustomData_get_layer(&me->edata, CD_MEDGE);? */
                }
-               treedata = sod->bvh_trees[tree_index];
+       }
+       if (sod->has_loose_edge && bvhtree[0] == NULL) {
+               bvhtree[0] = BKE_bvhtree_from_mesh_get(&dummy_treedata, me, BVHTREE_FROM_LOOSEEDGES, 2);
+               sod->has_loose_edge = bvhtree[0] != NULL;
 
-               /* the tree is owned by the DM and may have been freed since we last used! */
-               if (treedata && treedata->tree) {
-                       if (treedata->cached && !bvhcache_has_tree(dm->bvhCache, treedata->tree)) {
-                               free_bvhtree_from_mesh(treedata);
-                       }
-                       else {
-                               if (treedata->vert == NULL) {
-                                       treedata->vert = DM_get_vert_array(dm, &treedata->vert_allocated);
-                               }
-                               if ((tree_index == 1) && (treedata->edge == NULL)) {
-                                       treedata->edge = DM_get_edge_array(dm, &treedata->edge_allocated);
-                               }
-                       }
+               if (sod->has_loose_edge) {
+                       BLI_assert(treedata->vert_allocated == false);
+                       treedata->vert = dummy_treedata.vert;
+                       treedata->vert_allocated = dummy_treedata.vert_allocated;
+
+                       BLI_assert(treedata->edge_allocated == false);
+                       treedata->edge = dummy_treedata.edge;
+                       treedata->edge_allocated = dummy_treedata.edge_allocated;
                }
        }
+       if (snapdata->snap_to == SCE_SNAP_MODE_VERTEX) {
+               if (sod->has_loose_vert && bvhtree[1] == NULL) {
+                       bvhtree[1] = BKE_bvhtree_from_mesh_get(&dummy_treedata, me, BVHTREE_FROM_LOOSEVERTS, 2);
+                       sod->has_loose_vert = bvhtree[1] != NULL;
 
-       if (treedata) {
-               if (treedata->tree == NULL) {
-                       switch (snapdata->snap_to) {
-                               case SCE_SNAP_MODE_EDGE:
-                                       bvhtree_from_mesh_get(treedata, dm, BVHTREE_FROM_EDGES, 2);
-                                       break;
-                               case SCE_SNAP_MODE_VERTEX:
-                                       bvhtree_from_mesh_get(treedata, dm, BVHTREE_FROM_VERTS, 2);
-                                       break;
+                       if (sod->has_loose_vert) {
+                               BLI_assert(treedata->vert_allocated == false);
+                               treedata->vert = dummy_treedata.vert;
+                               treedata->vert_allocated = dummy_treedata.vert_allocated;
                        }
                }
-               if (treedata->tree == NULL) {
-                       return retval;
-               }
        }
        else {
-               return retval;
+               /* Not necessary, just to keep the data more consistent. */
+               sod->has_loose_vert = false;
        }
 
-       /* Warning: the depth_max is currently being used only in perspective view.
-        * It is not correct to limit the maximum depth for elements obtained with nearest
-        * since this limitation depends on the normal and the size of the occlusion face.
-        * And more... ray_depth is being confused with Z-depth here... (varies only the precision) */
-       const float ray_depth_max_global = *ray_depth + snapdata->depth_range[0];
+       /* Update pointers. */
+       if (treedata->vert_allocated == false) {
+               treedata->vert = me->mvert; /* CustomData_get_layer(&me->vdata, CD_MVERT);? */
+       }
+       if (treedata->tree || bvhtree[0]) {
+               if (treedata->edge_allocated == false) {
+                       /* If raycast has been executed before, `treedata->edge` can be NULL. */
+                       treedata->edge = me->medge; /* CustomData_get_layer(&me->edata, CD_MEDGE);? */
+               }
+               if (treedata->loop && treedata->loop_allocated == false) {
+                       treedata->loop = me->mloop; /* CustomData_get_layer(&me->edata, CD_MLOOP);? */
+               }
+               if (treedata->looptri && treedata->looptri_allocated == false) {
+                       treedata->looptri = BKE_mesh_runtime_looptri_ensure(me);
+               }
+       }
 
        Nearest2dUserData neasrest2d = {
-               .dist_px_sq = SQUARE(*dist_px),
-               .r_axis_closest = {1.0f, 1.0f, 1.0f},
-               .depth_range = {snapdata->depth_range[0], ray_depth_max_global},
-               .userdata = treedata,
-               .get_edge_verts = (Nearest2DGetEdgeVertsCallback)get_dm_edge_verts,
-               .copy_vert_no = (Nearest2DCopyVertNoCallback)copy_dm_vert_no,
-               .index = -1};
+               .is_persp             = snapdata->view_proj == VIEW_PROJ_PERSP,
+               .snap_to              = snapdata->snap_to,
+               .userdata             = treedata,
+               .get_vert_co          = (Nearest2DGetVertCoCallback)cb_mvert_co_get,
+               .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get,
+               .get_tri_verts_index  = (Nearest2DGetTriVertsCallback)cb_mlooptri_verts_get,
+               .get_tri_edges_index  = (Nearest2DGetTriEdgesCallback)cb_mlooptri_edges_get,
+               .copy_vert_no         = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy,
+       };
 
-       dist_squared_to_projected_aabb_precalc(
-               &neasrest2d.data_precalc, lpmat,
-               snapdata->view_proj == VIEW_PROJ_PERSP, snapdata->win_half,
-               ray_min_dist, snapdata->mval, ray_org_local, ray_normal_local);
+       BVHTreeNearest nearest = {
+               .index = -1,
+               .dist_sq = dist_px_sq,
+       };
+
+       float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4];
+       transpose_m4_m4(tobmat, obmat);
+       for (int i = snapdata->clip_plane_len; i--;) {
+               mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]);
+       }
+
+       if (bvhtree[1]) {
+               /* snap to loose verts */
+               BLI_bvhtree_find_nearest_projected(
+                       bvhtree[1], lpmat, snapdata->win_size, snapdata->mval,
+                       clip_planes_local, snapdata->clip_plane_len,
+                       &nearest, cb_walk_leaf_snap_vert, &neasrest2d);
+       }
+
+       if (bvhtree[0]) {
+               /* snap to loose edges */
+               BLI_bvhtree_find_nearest_projected(
+                       bvhtree[0], lpmat, snapdata->win_size, snapdata->mval,
+                       clip_planes_local, snapdata->clip_plane_len,
+                       &nearest, cb_walk_leaf_snap_edge, &neasrest2d);
+       }
 
-       BVHTree_WalkLeafCallback cb_walk_leaf =
-               (snapdata->snap_to == SCE_SNAP_MODE_VERTEX) ?
-               cb_walk_leaf_snap_vert : cb_walk_leaf_snap_edge;
+       if (treedata->tree) {
+               /* snap to looptris */
+               BLI_bvhtree_find_nearest_projected(
+                       treedata->tree, lpmat, snapdata->win_size, snapdata->mval,
+                       clip_planes_local, snapdata->clip_plane_len,
+                       &nearest, cb_walk_leaf_snap_tri, &neasrest2d);
+       }
 
-       BLI_bvhtree_walk_dfs(
-               treedata->tree,
-               cb_walk_parent_snap_project, cb_walk_leaf, cb_nearest_walk_order, &neasrest2d);
+       if (nearest.index != -1) {
+               *dist_px = sqrtf(nearest.dist_sq);
 
-       if (neasrest2d.index != -1) {
-               copy_v3_v3(r_loc, neasrest2d.co);
+               copy_v3_v3(r_loc, nearest.co);
                mul_m4_v3(obmat, r_loc);
+
                if (r_no) {
-                       copy_v3_v3(r_no, neasrest2d.no);
-                       mul_m3_v3(timat, r_no);
+                       float imat[4][4];
+                       invert_m4_m4(imat, obmat);
+
+                       copy_v3_v3(r_no, nearest.no);
+                       mul_transposed_mat3_m4_v3(obmat, r_no);
                        normalize_v3(r_no);
                }
-               *dist_px = sqrtf(neasrest2d.dist_px_sq);
-               *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir);
+               if (r_index) {
+                       *r_index = nearest.index;
+               }
 
                retval = true;
        }
@@ -1758,9 +1789,9 @@ static bool snapEditMesh(
         SnapObjectContext *sctx, SnapData *snapdata,
         Object *ob, BMEditMesh *em, float obmat[4][4],
         /* read/write args */
-        float *ray_depth, float *dist_px,
+        float *dist_px,
         /* return args */
-        float r_loc[3], float r_no[3])
+        float r_loc[3], float r_no[3], int *r_index)
 {
        bool retval = false;
 
@@ -1775,20 +1806,6 @@ static bool snapEditMesh(
                }
        }
 
-       float imat[4][4];
-       float timat[3][3]; /* transpose inverse matrix for normals */
-       float ray_normal_local[3];
-
-       invert_m4_m4(imat, obmat);
-       transpose_m3_m4(timat, imat);
-
-       copy_v3_v3(ray_normal_local, snapdata->ray_dir);
-
-       mul_mat3_m4_v3(imat, ray_normal_local);
-
-       /* local scale in normal direction */
-       float local_scale = normalize_v3(ray_normal_local);
-
        SnapObjectData_EditMesh *sod = NULL;
        BVHTreeFromEditMesh *treedata = NULL;
 
@@ -1860,45 +1877,59 @@ static bool snapEditMesh(
                return retval;
        }
 
-       float ray_org_local[3];
-       copy_v3_v3(ray_org_local, snapdata->ray_origin);
-       mul_m4_v3(imat, ray_org_local);
-
        Nearest2dUserData neasrest2d = {
-               .dist_px_sq = SQUARE(*dist_px),
-               .r_axis_closest = {1.0f, 1.0f, 1.0f},
-               .depth_range = {snapdata->depth_range[0], *ray_depth + snapdata->depth_range[0]},
-               .userdata = treedata,
-               .get_edge_verts = (Nearest2DGetEdgeVertsCallback)get_bedge_verts,
-               .copy_vert_no = (Nearest2DCopyVertNoCallback)copy_bvert_no,
-               .index = -1};
+               .is_persp             = snapdata->view_proj == VIEW_PROJ_PERSP,
+               .snap_to              = snapdata->snap_to,
+               .userdata             = treedata->em,
+               .get_vert_co          = (Nearest2DGetVertCoCallback)cb_bvert_co_get,
+               .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get,
+               .copy_vert_no         = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy,
+       };
 
-       float lpmat[4][4];
+       BVHTreeNearest nearest = {
+               .index = -1,
+               .dist_sq = SQUARE(*dist_px),
+       };
+
+       BVHTree_NearestProjectedCallback cb_walk_leaf;
+       if (snapdata->snap_to == SCE_SNAP_MODE_VERTEX) {
+               cb_walk_leaf = cb_walk_leaf_snap_vert;
+               BM_mesh_elem_table_ensure(em->bm, BM_VERT);
+       }
+       else {
+               cb_walk_leaf = cb_walk_leaf_snap_edge;
+               BM_mesh_elem_table_ensure(em->bm, BM_EDGE | BM_VERT);
+       }
+
+       float lpmat[4][4], tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4];
        mul_m4_m4m4(lpmat, snapdata->pmat, obmat);
-       dist_squared_to_projected_aabb_precalc(
-               &neasrest2d.data_precalc, lpmat,
-               snapdata->view_proj == VIEW_PROJ_PERSP, snapdata->win_half,
-               (snapdata->depth_range[0] * local_scale), snapdata->mval,
-               ray_org_local, ray_normal_local);
+       transpose_m4_m4(tobmat, obmat);
 
-       BVHTree_WalkLeafCallback cb_walk_leaf =
-               (snapdata->snap_to == SCE_SNAP_MODE_VERTEX) ?
-               cb_walk_leaf_snap_vert : cb_walk_leaf_snap_edge;
+       for (int i = snapdata->clip_plane_len; i--;) {
+               mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]);
+       }
+
+       BLI_bvhtree_find_nearest_projected(
+               treedata->tree, lpmat, snapdata->win_size, snapdata->mval,
+               clip_planes_local, snapdata->clip_plane_len,
+               &nearest, cb_walk_leaf, &neasrest2d);
 
-       BLI_bvhtree_walk_dfs(
-               treedata->tree,
-               cb_walk_parent_snap_project, cb_walk_leaf, cb_nearest_walk_order, &neasrest2d);
+       if (nearest.index != -1) {
+               *dist_px = sqrtf(nearest.dist_sq);
 
-       if (neasrest2d.index != -1) {
-               copy_v3_v3(r_loc, neasrest2d.co);
+               copy_v3_v3(r_loc, nearest.co);
                mul_m4_v3(obmat, r_loc);
                if (r_no) {
-                       copy_v3_v3(r_no, neasrest2d.no);
-                       mul_m3_v3(timat, r_no);
+                       float imat[4][4];
+                       invert_m4_m4(imat, obmat);
+
+                       copy_v3_v3(r_no, nearest.no);
+                       mul_transposed_mat3_m4_v3(obmat, r_no);
                        normalize_v3(r_no);
                }
-               *dist_px = sqrtf(neasrest2d.dist_px_sq);
-               *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir);
+               if (r_index) {
+                       *r_index = nearest.index;
+               }
 
                retval = true;
        }
@@ -1913,94 +1944,85 @@ static bool snapEditMesh(
  */
 static bool snapObject(
         SnapObjectContext *sctx, SnapData *snapdata,
-        Object *ob, float obmat[4][4],
-        bool use_obedit,
+        Object *ob, float obmat[4][4], bool use_obedit,
         /* read/write args */
-        float *ray_depth, float *dist_px,
+        float *dist_px,
         /* return args */
-        float r_loc[3], float r_no[3],
+        float r_loc[3], float r_no[3], int *r_index,
         Object **r_ob, float r_obmat[4][4])
 {
        bool retval = false;
 
-       if (ob->type == OB_MESH) {
-               BMEditMesh *em;
-
-               if (use_obedit) {
-                       em = BKE_editmesh_from_object(ob);
-                       retval = snapEditMesh(
-                               sctx, snapdata, ob, em, obmat,
-                               ray_depth, dist_px,
-                               r_loc, r_no);
-               }
-               else {
-                       /* in this case we want the mesh from the editmesh, avoids stale data. see: T45978.
-                        * still set the 'em' to NULL, since we only want the 'dm'. */
-                       DerivedMesh *dm;
-                       em = BKE_editmesh_from_object(ob);
-                       if (em) {
-                               editbmesh_get_derived_cage_and_final(sctx->depsgraph, sctx->scene, ob, em, CD_MASK_BAREMESH, &dm);
+       switch (ob->type) {
+               case OB_MESH:
+                       if (use_obedit) {
+                               BMEditMesh *em = BKE_editmesh_from_object(ob);
+                               retval = snapEditMesh(
+                                       sctx, snapdata, ob, em, obmat,
+                                       dist_px,
+                                       r_loc, r_no, r_index);
                        }
                        else {
-                               dm = mesh_get_derived_final(sctx->depsgraph, sctx->scene, ob, CD_MASK_BAREMESH);
+                               retval = snapMesh(
+                                       sctx, snapdata, ob, ob->data, obmat,
+                                       dist_px,
+                                       r_loc, r_no, r_index);
                        }
-                       retval = snapDerivedMesh(
-                               sctx, snapdata, ob, dm, obmat,
-                               ray_depth, dist_px,
-                               r_loc, r_no);
+                       break;
 
-                       dm->release(dm);
-               }
-       }
-       else if (snapdata->snap_to != SCE_SNAP_MODE_FACE) {
-               if (ob->type == OB_ARMATURE) {
+               case OB_ARMATURE:
                        retval = snapArmature(
                                snapdata,
                                ob, ob->data, obmat,
-                               ray_depth, dist_px,
-                               r_loc, r_no);
-               }
-               else if (ob->type == OB_CURVE) {
+                               dist_px,
+                               r_loc, r_no, r_index);
+                       break;
+
+               case OB_CURVE:
                        retval = snapCurve(
                                snapdata,
-                               ob->data, obmat, use_obedit,
-                               ray_depth, dist_px,
-                               r_loc, r_no);
-               }
-               else if (ob->type == OB_EMPTY) {
+                               ob, obmat, use_obedit,
+                               dist_px,
+                               r_loc, r_no, r_index);
+                       break;
+
+               case OB_EMPTY:
                        retval = snapEmpty(
-                               snapdata,
-                               ob, obmat,
-                               ray_depth, dist_px,
-                               r_loc, r_no);
-               }
-               else if (ob->type == OB_CAMERA) {
+                               snapdata, ob, obmat,
+                               dist_px,
+                               r_loc, r_no, r_index);
+                       break;
+
+               case OB_CAMERA:
                        retval = snapCamera(
                                sctx, snapdata, ob, obmat,
-                               ray_depth, dist_px,
-                               r_loc, r_no);
-               }
+                               dist_px,
+                               r_loc, r_no, r_index);
+                       break;
        }
 
        if (retval) {
                if (r_ob) {
                        *r_ob = ob;
+               }
+               if (r_obmat) {
                        copy_m4_m4(r_obmat, obmat);
                }
+               return true;
        }
 
-       return retval;
+       return false;
 }
 
 
 struct SnapObjUserData {
        SnapData *snapdata;
        /* read/write args */
-       float *ray_depth;
        float *dist_px;
        /* return args */
        float *r_loc;
        float *r_no;
+       int *r_index;
        Object **r_ob;
        float (*r_obmat)[4];
        bool ret;
@@ -2013,9 +2035,9 @@ static void sanp_obj_cb(SnapObjectContext *sctx, bool is_obedit, Object *ob, flo
                sctx, dt->snapdata,
                ob, obmat, is_obedit,
                /* read/write args */
-               dt->ray_depth, dt->dist_px,
+               dt->dist_px,
                /* return args */
-               dt->r_loc, dt->r_no,
+               dt->r_loc, dt->r_no, dt->r_index,
                dt->r_ob, dt->r_obmat);
 }
 
@@ -2050,28 +2072,25 @@ static void sanp_obj_cb(SnapObjectContext *sctx, bool is_obedit, Object *ob, flo
  */
 static bool snapObjectsRay(
         SnapObjectContext *sctx, SnapData *snapdata,
-        const eSnapSelect snap_select, const bool use_object_edit_cage,
+        const struct SnapObjectParams *params,
         /* read/write args */
-        float *ray_depth, float *dist_px,
+        float *dist_px,
         /* return args */
-        float r_loc[3], float r_no[3],
+        float r_loc[3], float r_no[3], int *r_index,
         Object **r_ob, float r_obmat[4][4])
 {
-       ViewLayer *view_layer = DEG_get_evaluated_view_layer(sctx->depsgraph);
-       Object *obedit = use_object_edit_cage ? OBEDIT_FROM_VIEW_LAYER(view_layer) : NULL;
-
        struct SnapObjUserData data = {
                .snapdata = snapdata,
-               .ray_depth = ray_depth,
                .dist_px = dist_px,
                .r_loc = r_loc,
                .r_no = r_no,
                .r_ob = r_ob,
+               .r_index = r_index,
                .r_obmat = r_obmat,
                .ret = false,
        };
 
-       iter_snap_objects(sctx, snap_select, obedit, sanp_obj_cb, &data);
+       iter_snap_objects(sctx, params, sanp_obj_cb, &data);
 
        return data.ret;
 }
@@ -2119,13 +2138,8 @@ static void snap_object_data_free(void *sod_v)
                case SNAP_MESH:
                {
                        SnapObjectData_Mesh *sod = sod_v;
-                       for (int i = 0; i < ARRAY_SIZE(sod->bvh_trees); i++) {
-                               if (sod->bvh_trees[i]) {
-                                       free_bvhtree_from_mesh(sod->bvh_trees[i]);
-                               }
-                       }
-                       if (sod->poly_allocated) {
-                               MEM_freeN(sod->mpoly);
+                       if (sod->treedata.tree) {
+                               free_bvhtree_from_mesh(&sod->treedata);
                        }
                        break;
                }
@@ -2173,9 +2187,8 @@ bool ED_transform_snap_object_project_ray_ex(
         Object **r_ob, float r_obmat[4][4])
 {
        return raycastObjects(
-               sctx,
+               sctx, params,
                ray_start, ray_normal,
-               params->snap_select, params->use_object_edit_cage,
                ray_depth, r_loc, r_no, r_index, r_ob, r_obmat, NULL);
 }
 
@@ -2202,9 +2215,8 @@ bool ED_transform_snap_object_project_ray_all(
 #endif
 
        bool retval = raycastObjects(
-               sctx,
+               sctx, params,
                ray_start, ray_normal,
-               params->snap_select, params->use_object_edit_cage,
                &ray_depth, NULL, NULL, NULL, NULL, NULL,
                r_hit_list);
 
@@ -2270,56 +2282,27 @@ static bool transform_snap_context_project_view3d_mixed_impl(
         const unsigned short snap_to_flag,
         const struct SnapObjectParams *params,
         const float mval[2], float *dist_px,
-        bool use_depth,
         float r_co[3], float r_no[3])
 {
-       float ray_depth = BVH_RAYCAST_DIST_MAX;
-       bool is_hit = false;
-
        const int  elem_type[3] = {SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE, SCE_SNAP_MODE_FACE};
 
        BLI_assert(snap_to_flag != 0);
        BLI_assert((snap_to_flag & ~(1 | 2 | 4)) == 0);
 
-       if (use_depth) {
-               const float dist_px_orig = dist_px ? *dist_px : 0;
-               for (int i = 2; i >= 0; i--) {
-                       if (snap_to_flag & (1 << i)) {
-                               if (i == 0) {
-                                       BLI_assert(dist_px != NULL);
-                                       *dist_px = dist_px_orig;
-                               }
-                               if (ED_transform_snap_object_project_view3d(
-                                       sctx,
-                                       elem_type[i], params,
-                                       mval, dist_px, &ray_depth,
-                                       r_co, r_no))
-                               {
-                                       /* 0.01 is a random but small value to prioritizing
-                                        * the first elements of the loop */
-                                       ray_depth += 0.01f;
-                                       is_hit = true;
-                               }
-                       }
-               }
-       }
-       else {
-               for (int i = 0; i < 3; i++) {
-                       if (snap_to_flag & (1 << i)) {
-                               if (ED_transform_snap_object_project_view3d(
-                                       sctx,
-                                       elem_type[i], params,
-                                       mval, dist_px, &ray_depth,
-                                       r_co, r_no))
-                               {
-                                       is_hit = true;
-                                       break;
-                               }
+       for (int i = 0; i < 3; i++) {
+               if (snap_to_flag & (1 << i)) {
+                       if (ED_transform_snap_object_project_view3d(
+                               sctx,
+                               elem_type[i], params,
+                               mval, dist_px, NULL,
+                               r_co, r_no))
+                       {
+                               return true;
                        }
                }
        }
 
-       return is_hit;
+       return false;
 }
 
 /**
@@ -2330,7 +2313,6 @@ static bool transform_snap_context_project_view3d_mixed_impl(
  * \param sctx: Snap context.
  * \param mval_fl: Screenspace coordinate.
  * \param dist_px: Maximum distance to snap (in pixels).
- * \param use_depth: Snap to the closest element, use when using more than one snap type.
  * \param r_co: hit location.
  * \param r_no: hit normal (optional).
  * \return Snap success
@@ -2340,13 +2322,12 @@ bool ED_transform_snap_object_project_view3d_mixed(
         const unsigned short snap_to_flag,
         const struct SnapObjectParams *params,
         const float mval_fl[2], float *dist_px,
-        bool use_depth,
         float r_co[3], float r_no[3])
 {
        return transform_snap_context_project_view3d_mixed_impl(
                sctx,
                snap_to_flag, params,
-               mval_fl, dist_px, use_depth,
+               mval_fl, dist_px,
                r_co, r_no);
 }
 
@@ -2356,52 +2337,109 @@ bool ED_transform_snap_object_project_view3d_ex(
         const struct SnapObjectParams *params,
         const float mval[2], float *dist_px,
         float *ray_depth,
-        float r_loc[3], float r_no[3], int *r_index)
+        float r_loc[3], float r_no[3], int *r_index,
+        Object **r_ob, float r_obmat[4][4])
 {
-       float ray_origin[3], ray_start[3], ray_normal[3], depth_range[2], ray_end[3];
+       float loc[3], no[3], obmat[4][4];
+       Object *ob = NULL;
+
+       int index_fallback;
+       if (r_index == NULL) {
+               r_index = &index_fallback;
+       }
 
+       bool has_hit = false, retval = false;
        const ARegion *ar = sctx->v3d_data.ar;
        const RegionView3D *rv3d = ar->regiondata;
 
-       ED_view3d_win_to_origin(ar, mval, ray_origin);
-       ED_view3d_win_to_vector(ar, mval, ray_normal);
+       if (snap_to == SCE_SNAP_MODE_FACE || params->use_occlusion_test) {
+               float ray_origin[3], ray_end[3], ray_start[3], ray_normal[3], depth_range[2];
 
-       ED_view3d_clip_range_get(
-               sctx->depsgraph,
-               sctx->v3d_data.v3d, sctx->v3d_data.ar->regiondata,
-               &depth_range[0], &depth_range[1], false);
+               ED_view3d_win_to_origin(ar, mval, ray_origin);
+               ED_view3d_win_to_vector(ar, mval, ray_normal);
 
-       madd_v3_v3v3fl(ray_start, ray_origin, ray_normal, depth_range[0]);
-       madd_v3_v3v3fl(ray_end, ray_origin, ray_normal, depth_range[1]);
+               ED_view3d_clip_range_get(
+                       sctx->depsgraph,
+                       sctx->v3d_data.v3d, sctx->v3d_data.ar->regiondata,
+                       &depth_range[0], &depth_range[1], false);
 
-       if (!ED_view3d_clip_segment(rv3d, ray_start, ray_end)) {
-               return false;
-       }
+               madd_v3_v3v3fl(ray_start, ray_origin, ray_normal, depth_range[0]);
+               madd_v3_v3v3fl(ray_end, ray_origin, ray_normal, depth_range[1]);
 
-       float ray_depth_fallback;
-       if (ray_depth == NULL) {
-               ray_depth_fallback = BVH_RAYCAST_DIST_MAX;
-               ray_depth = &ray_depth_fallback;
-       }
+               if (!ED_view3d_clip_segment(rv3d, ray_start, ray_end)) {
+                       return false;
+               }
+
+               float ray_depth_fallback;
+               if (ray_depth == NULL) {
+                       ray_depth_fallback = BVH_RAYCAST_DIST_MAX;
+                       ray_depth = &ray_depth_fallback;
+               }
 
-       if (snap_to == SCE_SNAP_MODE_FACE) {
-               return raycastObjects(
-                       sctx,
+               has_hit = raycastObjects(
+                       sctx, params,
                        ray_start, ray_normal,
-                       params->snap_select, params->use_object_edit_cage,
-                       ray_depth, r_loc, r_no, r_index, NULL, NULL, NULL);
+                       &ray_depth_fallback, loc, no,
+                       r_index, &ob, obmat, NULL);
+
+               retval = has_hit && (snap_to == SCE_SNAP_MODE_FACE);
        }
-       else {
+
+       if (ELEM(snap_to, SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE)) {
                SnapData snapdata;
-               const enum eViewProj view_proj = ((RegionView3D *)ar->regiondata)->is_persp ? VIEW_PROJ_PERSP : VIEW_PROJ_ORTHO;
-               snap_data_set(&snapdata, ar, snap_to, view_proj, mval,
-                       ray_origin, ray_start, ray_normal, depth_range);
 
-               return snapObjectsRay(
-                       sctx, &snapdata,
-                       params->snap_select, params->use_object_edit_cage,
-                       ray_depth, dist_px, r_loc, r_no, NULL, NULL);
+               copy_m4_m4(snapdata.pmat, rv3d->persmat);
+               snapdata.win_size[0] = ar->winx;
+               snapdata.win_size[1] = ar->winy;
+               copy_v2_v2(snapdata.mval, mval);
+               snapdata.snap_to = snap_to;
+               snapdata.view_proj = rv3d->is_persp ? VIEW_PROJ_PERSP : VIEW_PROJ_ORTHO;
+
+               planes_from_projmat(
+                       snapdata.pmat,
+                       NULL, NULL, NULL, NULL,
+                       snapdata.clip_plane[0], snapdata.clip_plane[1]);
+
+               snapdata.clip_plane_len = 2;
+
+               if (has_hit) {
+                       /* Compute the new clip_pane but do not add it yet. */
+                       float new_clipplane[4];
+                       plane_from_point_normal_v3(new_clipplane, loc, no);
+
+                       /* Try to snap only to the polygon. */
+                       retval |= snapMeshPolygon(
+                               sctx, &snapdata, ob, obmat,
+                               dist_px, loc, no, r_index);
+
+                       /* Add the new clip plane to the beginning of the list. */
+                       for (int i = snapdata.clip_plane_len; i != 0; i--) {
+                               copy_v4_v4(snapdata.clip_plane[i], snapdata.clip_plane[i - 1]);
+                       }
+                       copy_v4_v4(snapdata.clip_plane[0], new_clipplane);
+                       snapdata.clip_plane_len++;
+               }
+
+               retval |= snapObjectsRay(
+                       sctx, &snapdata, params,
+                       dist_px, loc, no, r_index, &ob, obmat);
        }
+
+       if (retval) {
+               copy_v3_v3(r_loc, loc);
+               if (r_no) {
+                       copy_v3_v3(r_no, no);
+               }
+               if (r_ob) {
+                       *r_ob = ob;
+               }
+               if (r_obmat) {
+                       copy_m4_m4(r_obmat, obmat);
+               }
+               return true;
+       }
+
+       return false;
 }
 
 bool ED_transform_snap_object_project_view3d(
@@ -2418,7 +2456,8 @@ bool ED_transform_snap_object_project_view3d(
                params,
                mval, dist_px,
                ray_depth,
-               r_loc, r_no, NULL);
+               r_loc, r_no, NULL,
+               NULL, NULL);
 }
 
 /**