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 8a336f3b5b45a59f5e8d18e668ae749be51bc4ec..253cd53fb3e9fafc84a5d07a88bf328e05e52745 100644 (file)
@@ -47,6 +47,8 @@
 #include "DNA_view3d_types.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"
@@ -69,6 +71,8 @@
 /** Internal Data Types
  * \{ */
 
+#define MAX_CLIPPLANE_LEN 3
+
 enum eViewProj {
        VIEW_PROJ_NONE = -1,
        VIEW_PROJ_ORTHO = 0,
@@ -78,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 {
@@ -162,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 &&
@@ -193,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];
@@ -512,7 +483,7 @@ static bool raycastMesh(
                                retval = true;
 
                                if (r_index) {
-                                       *r_index = hit.index;
+                                       *r_index = treedata->looptri[hit.index].poly;
                                }
                        }
                }
@@ -661,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);
                                }
                        }
                }
@@ -680,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 */
@@ -690,32 +661,46 @@ static bool raycastObj(
 {
        bool retval = false;
 
-       if (ob->type == 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 {
-                       retval = raycastMesh(
-                               sctx,
-                               ray_start, ray_dir,
-                               ob, ob->data, 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;
                }
        }
 
+       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 {
+                               retval = raycastMesh(
+                                       sctx,
+                                       ray_start, ray_dir,
+                                       ob, ob->data, 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;
 }
 
 
@@ -732,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,
@@ -778,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 */
@@ -787,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,
@@ -801,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;
 }
@@ -848,7 +834,6 @@ static void cb_bvert_no_copy(
 static void cb_medge_verts_get(
         const int index, int v_index[2], const BVHTreeFromMesh *data)
 {
-       const MVert *vert = data->vert;
        const MEdge *edge = &data->edge[index];
 
        v_index[0] = edge->v1;
@@ -897,33 +882,26 @@ static void cb_mlooptri_verts_get(
 }
 
 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;
@@ -933,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;
 }
 
 /** \} */
@@ -1182,13 +1062,8 @@ typedef void (*Nearest2DGetTriEdgesCallback)(const int index, int e_index[3], vo
 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;
        Nearest2DGetVertCoCallback get_vert_co;
@@ -1197,100 +1072,79 @@ typedef struct Nearest2dUserData {
        Nearest2DGetTriEdgesCallback get_tri_edges_index;
        Nearest2DCopyVertNoCallback copy_vert_no;
 
-       short snap_to;
-
-       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_nearest_walk_order(
-        const BVHTreeAxisRange *UNUSED(bounds), char axis, void *userdata)
-{
-       const bool *r_axis_closest = ((struct Nearest2dUserData *)userdata)->r_axis_closest;
-       return r_axis_closest[axis];
-}
-
-static bool cb_walk_leaf_snap_vert(
-        const BVHTreeAxisRange *UNUSED(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;
 
-       float *co;
+       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;
 
        int vindex[2];
        data->get_edge_verts_index(index, vindex, data->userdata);
 
        if (data->snap_to == SCE_SNAP_MODE_EDGE) {
-               bool vert_snapped = false;
                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(
-                       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,
+                       precalc,
+                       clip_plane,
+                       clip_plane_len,
+                       data->is_persp,
                        v_pair[0], v_pair[1],
-                       &data->dist_px_sq,
-                       data->co))
+                       &nearest->dist_sq,
+                       nearest->co))
                {
-                       sub_v3_v3v3(data->no, v_pair[0], v_pair[1]);
-                       data->index = index;
+                       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] == data->index) {
+                       if (vindex[i] == nearest->index) {
                                continue;
                        }
-                       cb_walk_leaf_snap_vert(NULL, vindex[i], userdata);
+                       cb_walk_leaf_snap_vert(
+                               userdata, vindex[i], precalc,
+                               clip_plane, clip_plane_len, nearest);
                }
        }
-
-       return true;
 }
 
-static bool cb_walk_leaf_snap_tri(
-        const BVHTreeAxisRange *UNUSED(bounds), int index, 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)
 {
        struct Nearest2dUserData *data = userdata;
 
@@ -1299,10 +1153,12 @@ static bool cb_walk_leaf_snap_tri(
                data->get_tri_edges_index(index, eindex, data->userdata);
                for (int i = 0; i < 3; i++) {
                        if (eindex[i] != -1) {
-                               if (eindex[i] == data->index) {
+                               if (eindex[i] == nearest->index) {
                                        continue;
                                }
-                               cb_walk_leaf_snap_edge(NULL, eindex[i], userdata);
+                               cb_walk_leaf_snap_edge(
+                                       userdata, eindex[i], precalc,
+                                       clip_plane, clip_plane_len, nearest);
                        }
                }
        }
@@ -1310,13 +1166,14 @@ static bool cb_walk_leaf_snap_tri(
                int vindex[3];
                data->get_tri_verts_index(index, vindex, data->userdata);
                for (int i = 0; i < 3; i++) {
-                       if (vindex[i] == data->index) {
+                       if (vindex[i] == nearest->index) {
                                continue;
                        }
-                       cb_walk_leaf_snap_vert(NULL, vindex[i], userdata);
+                       cb_walk_leaf_snap_vert(
+                               userdata, vindex[i], precalc,
+                               clip_plane, clip_plane_len, nearest);
                }
        }
-       return true;
 }
 
 /** \} */
@@ -1329,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;
+       }
 
-               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);
+       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);
+
+               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) {
@@ -1362,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;
                                        }
@@ -1393,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;
                                }
@@ -1415,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;
@@ -1423,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;
 
@@ -1436,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++) {
@@ -1453,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);
                                                        }
                                                }
@@ -1480,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);
                                                }
                                        }
@@ -1490,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;
@@ -1523,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;
 
@@ -1537,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;
 
@@ -1584,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);
@@ -1595,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;
@@ -1631,21 +1560,22 @@ 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;
@@ -1655,9 +1585,9 @@ static bool snapMesh(
         SnapObjectContext *sctx, SnapData *snapdata,
         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;
 
@@ -1672,40 +1602,25 @@ static bool snapMesh(
                }
        }
 
-       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_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;
                }
        }
@@ -1746,8 +1661,8 @@ static bool snapMesh(
 
        if (sod->has_looptris && treedata->tree == NULL) {
                BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI, 4);
-
-               if (sod->has_looptris = treedata->tree != NULL) {
+               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);? */
                }
@@ -1800,62 +1715,69 @@ static bool snapMesh(
                }
        }
 
-       /* 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];
-
        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},
+               .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,
-               .snap_to              = snapdata->snap_to,
-               .index                = -1};
+       };
 
-       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_walk_dfs(
-                       bvhtree[1],
-                       cb_walk_parent_snap_project, cb_walk_leaf_snap_vert, cb_nearest_walk_order, &neasrest2d);
+               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_walk_dfs(
-                       bvhtree[0],
-                       cb_walk_parent_snap_project, cb_walk_leaf_snap_edge, cb_nearest_walk_order, &neasrest2d);
+               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);
        }
 
        if (treedata->tree) {
                /* snap to looptris */
-               BLI_bvhtree_walk_dfs(
-                       treedata->tree,
-                       cb_walk_parent_snap_project, cb_walk_leaf_snap_tri, cb_nearest_walk_order, &neasrest2d);
+               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);
        }
 
-       if (neasrest2d.index != -1) {
-               copy_v3_v3(r_loc, neasrest2d.co);
+       if (nearest.index != -1) {
+               *dist_px = sqrtf(nearest.dist_sq);
+
+               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;
        }
@@ -1867,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;
 
@@ -1884,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;
 
@@ -1969,47 +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]},
+               .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,
-               .snap_to              = snapdata->snap_to,
-               .index                = -1};
+       };
 
-       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);
+
+       for (int i = snapdata->clip_plane_len; i--;) {
+               mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]);
+       }
 
-       BVHTree_WalkLeafCallback cb_walk_leaf =
-               (snapdata->snap_to == SCE_SNAP_MODE_VERTEX) ?
-               cb_walk_leaf_snap_vert : cb_walk_leaf_snap_edge;
+       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;
        }
@@ -2024,82 +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;
+       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 {
+                               retval = snapMesh(
+                                       sctx, snapdata, ob, ob->data, obmat,
+                                       dist_px,
+                                       r_loc, r_no, r_index);
+                       }
+                       break;
 
-               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 {
-                       retval = snapMesh(
-                               sctx, snapdata, ob, ob->data, obmat,
-                               ray_depth, dist_px,
-                               r_loc, r_no);
-               }
-       }
-       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;
@@ -2112,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);
 }
 
@@ -2149,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;
 }
@@ -2267,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);
 }
 
@@ -2296,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);
 
@@ -2364,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;
 }
 
 /**
@@ -2424,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
@@ -2434,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);
 }
 
@@ -2453,50 +2340,106 @@ bool ED_transform_snap_object_project_view3d_ex(
         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;
+               }
 
-       if (snap_to == SCE_SNAP_MODE_FACE) {
-               return raycastObjects(
-                       sctx,
+               float ray_depth_fallback;
+               if (ray_depth == NULL) {
+                       ray_depth_fallback = BVH_RAYCAST_DIST_MAX;
+                       ray_depth = &ray_depth_fallback;
+               }
+
+               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, r_ob, r_obmat, 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, r_ob, r_obmat);
+               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(