diff options
Diffstat (limited to 'source/blender/editors/transform/transform_snap_object.c')
-rw-r--r-- | source/blender/editors/transform/transform_snap_object.c | 1324 |
1 files changed, 842 insertions, 482 deletions
diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index 59dfe18139a..1d4872cca7a 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -110,6 +110,12 @@ struct SnapObjectContext { }; +enum eViewProj { + VIEW_PROJ_NONE = -1, + VIEW_PROJ_ORTHO = 0, + VIEW_PROJ_PERSP = -1, +}; + static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt); @@ -137,10 +143,6 @@ struct RayCastAll_Data { Object *ob; unsigned int ob_uuid; - /* DerivedMesh only */ - DerivedMesh *dm; - const struct MLoopTri *dm_looptri; - /* output data */ ListBase *hit_list; bool retval; @@ -218,76 +220,177 @@ static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVH /* -------------------------------------------------------------------- */ -/** \name Internal Object Snapping API +/** \Common utilities * \{ */ + +/** + * Struct that kepts basic information about a BVHTree build from a editmesh. + */ +typedef struct BVHTreeFromMeshType { + void *userdata; + char type; +} BVHTreeFromMeshType; + +typedef struct PreDefProject { + float pmat[4][4]; /* perspective matrix multiplied by object matrix */ + float win_half[2]; + float dist_px_sq; +} PreDefProject; + +static void precalc_project( + PreDefProject *projectdefs, const ARegion *ar, + const float dist_px, float obmat[4][4]) +{ + float (*pmat)[4] = ((RegionView3D *)ar->regiondata)->persmat; + if (obmat) { + mul_m4_m4m4(projectdefs->pmat, pmat, obmat); + } + else { + copy_m4_m4(projectdefs->pmat, pmat); + } + projectdefs->win_half[0] = ar->winx / 2; + projectdefs->win_half[1] = ar->winy / 2; + projectdefs->dist_px_sq = SQUARE(dist_px); +} + +/** + * From a threshold (maximum distance to snap in pixels) returns: + * + * - The *real* distance (3D) if you are in orthographic-view. + * - The *tangent* (view cone radius at distance 1.0) if you are in perspective-view. + */ +static float dist_px_to_dist3d_or_tangent(const ARegion *ar, const float dist_px) +{ + const RegionView3D *rv3d = ar->regiondata; + if (ar->winx >= ar->winy) + return 2 * (dist_px / ar->winx) / rv3d->winmat[0][0]; + else + return 2 * (dist_px / ar->winy) / rv3d->winmat[1][1]; +} + +static const float *get_vert_co(const BVHTreeFromMeshType *meshdata, const int index) +{ + switch (meshdata->type) { + case SNAP_MESH: + { + BVHTreeFromMesh *data = meshdata->userdata; + const MVert *vert = data->vert; + return vert[index].co; + } + case SNAP_EDIT_MESH: + { + BVHTreeFromEditMesh *data = meshdata->userdata; + BMVert *eve = BM_vert_at_index(data->em->bm, index); + return eve->co; + } + } + return NULL; +} + +static void copy_vert_no(const BVHTreeFromMeshType *meshdata, const int index, float r_no[3]) +{ + switch (meshdata->type) { + case SNAP_MESH: + { + BVHTreeFromMesh *data = meshdata->userdata; + const MVert *vert = data->vert; + normal_short_to_float_v3(r_no, vert->no); + break; + } + case SNAP_EDIT_MESH: + { + BVHTreeFromEditMesh *data = meshdata->userdata; + BMVert *eve = BM_vert_at_index(data->em->bm, index); + copy_v3_v3(r_no, eve->no); + break; + } + } +} + +static void get_edge_verts( + const BVHTreeFromMeshType *meshdata, const int index, + const float *v_pair[2]) +{ + switch (meshdata->type) { + case SNAP_MESH: + { + BVHTreeFromMesh *data = meshdata->userdata; + + const MVert *vert = data->vert; + const MEdge *edge = data->edge + index; + + v_pair[0] = vert[edge->v1].co; + v_pair[1] = vert[edge->v2].co; + break; + } + case SNAP_EDIT_MESH: + { + BVHTreeFromEditMesh *data = meshdata->userdata; + BMEdge *eed = BM_edge_at_index(data->em->bm, index); + + v_pair[0] = eed->v1->co; + v_pair[1] = eed->v2->co; + break; + } + } +} + #define V3_MUL_ELEM(a, b) \ (a)[0] * (b)[0], \ (a)[1] * (b)[1], \ (a)[2] * (b)[2] -static bool test_vert( - const float vco[3], const float vno[3], const float ray_co[3], const float ray_dir[3], - const float ray_depth_range[2], const float scale[3], const bool is_persp, +static bool test_vert_dist( + const float vco[3], const float ray_co[3], const float ray_dir[3], + const float ray_depth_range[2], const float scale[3], /* read/write args */ float *ray_depth, float *dist_to_ray_sq, /* return args */ - float r_co[3], float r_no[3]) + float r_co[3]) { const float vco_sc[3] = {V3_MUL_ELEM(vco, scale)}; - const float co_sc[3] = {V3_MUL_ELEM(ray_co, scale)}; + const float origin_sc[3] = {V3_MUL_ELEM(ray_co, scale)}; const float dir_sc[3] = {V3_MUL_ELEM(ray_dir, scale)}; - float depth; - float dist_sq = dist_squared_to_ray_v3(co_sc, dir_sc, vco_sc, &depth); + float depth, dist_sq; + dist_sq = dist_squared_to_ray_v3(origin_sc, dir_sc, vco_sc, &depth); if (depth < ray_depth_range[0]) { return false; } - if (is_persp) { - dist_sq /= SQUARE(depth); - } - if ((dist_sq < *dist_to_ray_sq) && (depth < *ray_depth)) { *dist_to_ray_sq = dist_sq; copy_v3_v3(r_co, vco); - if (vno) { - copy_v3_v3(r_no, vno); - } - *ray_depth = depth; return true; } return false; } -static bool test_edge( +static bool test_edge_dist( const float v1[3], const float v2[3], const float ray_co[3], const float ray_dir[3], - const float ray_depth_range[2], const float scale[3], const bool is_persp, + const float ray_depth_range[2], const float scale[3], /* read/write args */ float *ray_depth, float *dist_to_ray_sq, /* return args */ - float r_co[3], float r_no[3]) + float r_co[3]) { const float v1_sc[3] = {V3_MUL_ELEM(v1, scale)}; const float v2_sc[3] = {V3_MUL_ELEM(v2, scale)}; const float co_sc[3] = {V3_MUL_ELEM(ray_co, scale)}; const float dir_sc[3] = {V3_MUL_ELEM(ray_dir, scale)}; - float tmp_co[3], depth; - float dist_sq = dist_squared_ray_to_seg_v3(co_sc, dir_sc, v1_sc, v2_sc, tmp_co, &depth); + float tmp_co[3], depth, dist_sq; + dist_sq = dist_squared_ray_to_seg_v3(co_sc, dir_sc, v1_sc, v2_sc, tmp_co, &depth); if (depth < ray_depth_range[0]) { return false; } - if (is_persp) { - dist_sq /= SQUARE(depth); - } - if ((dist_sq < *dist_to_ray_sq) && (depth < *ray_depth)) { *dist_to_ray_sq = dist_sq; @@ -297,10 +400,6 @@ static bool test_edge( copy_v3_v3(r_co, tmp_co); - if (r_no) { - sub_v3_v3v3(r_no, v1, v2); - } - *ray_depth = depth; return true; } @@ -309,50 +408,369 @@ static bool test_edge( #undef V3_MUL_ELEM +static bool test_projected_vert_dist( + PreDefProject *projectdefs, + const float co[3], const enum eViewProj view_proj, + const float mval[2], const float depth_range[2], + float r_co[3]) +{ + float depth; + float(*pmat)[4] = projectdefs->pmat; + if (view_proj == VIEW_PROJ_PERSP) { + depth = mul_project_m4_v3_zfac(pmat, co); + if (depth < depth_range[0] || depth > depth_range[1]) { + 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]), + }; + + if (view_proj == VIEW_PROJ_PERSP) { + mul_v2_fl(co2d, 1 / depth); + } + + co2d[0] += 1.0f; + co2d[1] += 1.0f; + co2d[0] *= projectdefs->win_half[0]; + co2d[1] *= projectdefs->win_half[1]; + + const float dist_sq = len_squared_v2v2(mval, co2d); + if (dist_sq < projectdefs->dist_px_sq) { + copy_v3_v3(r_co, co); + projectdefs->dist_px_sq = dist_sq; + return true; + } + return false; +} + +static bool test_projected_edge_dist( + PreDefProject *projectdefs, + const float va[3], const float vb[3], const float ray_start[3], const float ray_normal[3], + const enum eViewProj view_proj, const float mval[2], const float depth_range[2], + float r_co[3]) +{ + + float tmp_co[3], depth; + dist_squared_ray_to_seg_v3(ray_start, ray_normal, va, vb, tmp_co, &depth); + return test_projected_vert_dist(projectdefs, tmp_co, view_proj, mval, depth_range, r_co); +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** \Walk DFS + * \{ */ +typedef struct Object_Nearest2dPrecalc { + float ray_origin_local[3]; + float ray_direction_local[3]; + float ray_inv_dir[3]; + + PreDefProject projectdefs; + float mval[2]; + bool sign[3]; + bool r_axis_closest[3]; + float depth_range[2]; + + void *userdata; + int index; + float co[3]; + float no[3]; +} Object_Nearest2dPrecalc; + + +static void nearest2d_precalc( + Object_Nearest2dPrecalc *neasrest_precalc, const ARegion *ar, + const float dist_px, float obmat[4][4], + const float ray_origin_local[3], const float ray_direction_local[3], + const float mval[2], const float depth_range[2]) +{ + precalc_project(&neasrest_precalc->projectdefs, ar, dist_px, obmat); + 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); + copy_v2_v2(neasrest_precalc->depth_range, depth_range); + + 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); + neasrest_precalc->r_axis_closest[i] = true; + } +} + +static bool cb_walk_parent_snap_project(const BVHTreeAxisRange *bounds, void *user_data) +{ + Object_Nearest2dPrecalc *data = user_data; + float local_bvmin[3], local_bvmax[3]; + if (data->sign[0]) { + local_bvmin[0] = bounds[0].max; + local_bvmax[0] = bounds[0].min; + } + else { + local_bvmin[0] = bounds[0].min; + local_bvmax[0] = bounds[0].max; + } + if (data->sign[1]) { + local_bvmin[1] = bounds[1].max; + local_bvmax[1] = bounds[1].min; + } + else { + local_bvmin[1] = bounds[1].min; + local_bvmax[1] = bounds[1].max; + } + if (data->sign[2]) { + local_bvmin[2] = bounds[2].max; + local_bvmax[2] = bounds[2].min; + } + else { + local_bvmin[2] = bounds[2].min; + local_bvmax[2] = bounds[2].max; + } + + 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], + }; + float va[3], vb[3]; + 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; + data->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; + data->r_axis_closest[1] = data->sign[1]; + } + else { + rtmax = tmax[2]; + va[2] = vb[2] = local_bvmax[2]; + main_axis = 1; + data->r_axis_closest[2] = data->sign[2]; + } + + if ((tmin[0] >= tmin[1]) && (tmin[0] >= tmin[2])) { + rtmin = tmin[0]; + va[0] = vb[0] = local_bvmin[0]; + main_axis -= 3; + data->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; + data->r_axis_closest[1] = !data->sign[1]; + } + else { + rtmin = tmin[2]; + va[2] = vb[2] = local_bvmin[2]; + main_axis -= 2; + data->r_axis_closest[2] = !data->sign[2]; + } + if (main_axis < 0) { + main_axis += 3; + } + + /* if rtmin < rtmax, ray intersect `AABB` */ + if (rtmin <= rtmax) { +#ifdef IGNORE_BEHIND_RAY + /* `if rtmax < depth_min`, the whole `AABB` is behind us */ + if (rtmax < min_depth) { + return fallback; + } +#endif + const float proj = rtmin * data->ray_direction_local[main_axis]; + data->r_axis_closest[main_axis] = (proj - va[main_axis]) < (vb[main_axis] - proj); + return true; + } +#ifdef IGNORE_BEHIND_RAY + /* `if rtmin < depth_min`, the whole `AABB` is behing us */ + else if (rtmin < min_depth) { + return fallback; + } +#endif + 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]); + + float (*pmat)[4] = data->projectdefs.pmat; + float depth_a = mul_project_m4_v3_zfac(pmat, va); + float depth_b = depth_a + pmat[main_axis][3] * scale; + + 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) / depth_b, + (va2d[1] + pmat[main_axis][1] * scale) / depth_b, + }; + + va2d[0] /= depth_a; + va2d[1] /= depth_a; + + va2d[0] += 1.0f; + va2d[1] += 1.0f; + vb2d[0] += 1.0f; + vb2d[1] += 1.0f; + + va2d[0] *= data->projectdefs.win_half[0]; + va2d[1] *= data->projectdefs.win_half[1]; + vb2d[0] *= data->projectdefs.win_half[0]; + vb2d[1] *= data->projectdefs.win_half[1]; + + //float dvec[2], edge[2], rdist; + //sub_v2_v2v2(dvec, data->mval, va2d); + //sub_v2_v2v2(edge, vb2d, va2d); + float rdist; + short dvec[2] = {data->mval[0] - va2d[0], data->mval[1] - va2d[1]}; + short edge[2] = {vb2d[0] - va2d[0], vb2d[1] - va2d[1]}; + float lambda = dvec[0] * edge[0] + dvec[1] * edge[1]; + if (lambda != 0.0f) { + lambda /= edge[0] * edge[0] + edge[1] * edge[1]; + if (lambda <= 0.0f) { + rdist = len_squared_v2v2(data->mval, va2d); + data->r_axis_closest[main_axis] = true; + } + else if (lambda >= 1.0f) { + rdist = len_squared_v2v2(data->mval, vb2d); + data->r_axis_closest[main_axis] = false; + } + else { + va2d[0] += edge[0] * lambda; + va2d[1] += edge[1] * lambda; + rdist = len_squared_v2v2(data->mval, va2d); + data->r_axis_closest[main_axis] = lambda < 0.5f; + } + } + else { + rdist = len_squared_v2v2(data->mval, va2d); + } + return rdist < data->projectdefs.dist_px_sq; +} + +static bool cb_walk_leaf_snap_vert(const BVHTreeAxisRange *bounds, int index, void *userdata) +{ + struct Object_Nearest2dPrecalc *neasrest_precalc = userdata; + 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, + }; + + /* Currently the `BLI_bvhtree_walk_dfs` is being used only in the perspective view mode (VIEW_PROJ_PERSP) + * It could be used in orthographic view mode too (VIEW_PROJ_ORTHO), + * but in this case the `BLI_bvhtree_find_nearest_to_ray` is more efficient.*/ + if (test_projected_vert_dist( + &neasrest_precalc->projectdefs, co, VIEW_PROJ_PERSP, + neasrest_precalc->mval, neasrest_precalc->depth_range, + neasrest_precalc->co)) + { + copy_vert_no(neasrest_precalc->userdata, index, neasrest_precalc->no); + neasrest_precalc->index = index; + } + return true; +} + +static bool cb_walk_leaf_snap_edge(const BVHTreeAxisRange *UNUSED(bounds), int index, void *userdata) +{ + struct Object_Nearest2dPrecalc *neasrest_precalc = userdata; + + const float *v_pair[2]; + get_edge_verts(neasrest_precalc->userdata, index, v_pair); + + /* Currently the `BLI_bvhtree_walk_dfs` is being used only in the perspective view mode (VIEW_PROJ_PERSP) + * It could be used in orthographic view mode too (VIEW_PROJ_ORTHO), + * but in this case the `BLI_bvhtree_find_nearest_to_ray` is more efficient.*/ + if (test_projected_edge_dist( + &neasrest_precalc->projectdefs, v_pair[0], v_pair[1], + neasrest_precalc->ray_origin_local, neasrest_precalc->ray_direction_local, + VIEW_PROJ_PERSP, neasrest_precalc->mval, neasrest_precalc->depth_range, + neasrest_precalc->co)) + { + sub_v3_v3v3(neasrest_precalc->no, v_pair[0], v_pair[1]); + neasrest_precalc->index = index; + } + return true; +} + +static bool cb_nearest_walk_order(const BVHTreeAxisRange *UNUSED(bounds), char axis, void *userdata) +{ + const bool *r_axis_closest = ((struct Object_Nearest2dPrecalc *)userdata)->r_axis_closest; + return r_axis_closest[axis]; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ + +/** \name Internal Object Snapping API + * \{ */ + static bool snapArmature( - Object *ob, bArmature *arm, float obmat[4][4], - const short snap_to, const bool is_persp, - const float ray_origin[3], const float ray_normal[3], const float ray_depth_range[2], + const ARegion *ar, Object *ob, bArmature *arm, float obmat[4][4], + const short snap_to, const float origin[3], const float dir[3], + const float mval[2], const enum eViewProj view_proj, const float depth_range[2], /* read/write args */ - float *ray_depth, float *dist_to_ray_sq, + float *dist_px, /* return args */ float r_loc[3], float *UNUSED(r_no)) { - float imat[4][4]; - float ray_origin_local[3], ray_normal_local[3]; bool retval = false; - invert_m4_m4(imat, obmat); + float ray_start_local[3], ray_normal_local[3]; + if (snap_to != SCE_SNAP_MODE_VERTEX) { + float imat[4][4]; + invert_m4_m4(imat, obmat); - mul_v3_m4v3(ray_origin_local, imat, ray_origin); - mul_v3_mat3_m4v3(ray_normal_local, imat, ray_normal); + copy_v3_v3(ray_start_local, origin); + copy_v3_v3(ray_normal_local, dir); + mul_m4_v3(imat, ray_start_local); + mul_mat3_m4_v3(imat, ray_normal_local); + } - float ob_scale[3]; - mat4_to_size(ob_scale, obmat); + PreDefProject projectdefs; + precalc_project(&projectdefs, ar, *dist_px, obmat); if (arm->edbo) { - EditBone *eBone; - - for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { + for (EditBone *eBone = arm->edbo->first; eBone; eBone = eBone->next) { if (eBone->layer & arm->layer) { /* skip hidden or moving (selected) bones */ if ((eBone->flag & (BONE_HIDDEN_A | BONE_ROOTSEL | BONE_TIPSEL)) == 0) { switch (snap_to) { case SCE_SNAP_MODE_VERTEX: - retval |= test_vert( - eBone->head, NULL, ray_origin_local, ray_normal_local, - ray_depth_range, ob_scale, is_persp, ray_depth, dist_to_ray_sq, - r_loc, NULL); - retval |= test_vert( - eBone->tail, NULL, ray_origin_local, ray_normal_local, - ray_depth_range, ob_scale, is_persp, ray_depth, dist_to_ray_sq, - r_loc, NULL); + retval |= test_projected_vert_dist( + &projectdefs, eBone->head, view_proj, mval, depth_range, r_loc); + retval |= test_projected_vert_dist( + &projectdefs, eBone->tail, view_proj, mval, depth_range, r_loc); break; case SCE_SNAP_MODE_EDGE: - retval |= test_edge( - eBone->head, eBone->tail, ray_origin_local, ray_normal_local, - ray_depth_range, ob_scale, is_persp, ray_depth, dist_to_ray_sq, - r_loc, NULL); + retval |= test_projected_edge_dist( + &projectdefs, eBone->head, eBone->tail, ray_start_local, ray_normal_local, + view_proj, mval, depth_range, r_loc); break; } } @@ -360,11 +778,8 @@ static bool snapArmature( } } else if (ob->pose && ob->pose->chanbase.first) { - bPoseChannel *pchan; - Bone *bone; - - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - bone = pchan->bone; + for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + Bone *bone = pchan->bone; /* skip hidden bones */ if (bone && !(bone->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG))) { const float *head_vec = pchan->pose_head; @@ -372,26 +787,22 @@ static bool snapArmature( switch (snap_to) { case SCE_SNAP_MODE_VERTEX: - retval |= test_vert( - head_vec, NULL, ray_origin_local, ray_normal_local, - ray_depth_range, ob_scale, is_persp, ray_depth, dist_to_ray_sq, - r_loc, NULL); - retval |= test_vert( - tail_vec, NULL, ray_origin_local, ray_normal_local, - ray_depth_range, ob_scale, is_persp, ray_depth, dist_to_ray_sq, - r_loc, NULL); + retval |= test_projected_vert_dist( + &projectdefs, head_vec, view_proj, mval, depth_range, r_loc); + retval |= test_projected_vert_dist( + &projectdefs, tail_vec, view_proj, mval, depth_range, r_loc); break; case SCE_SNAP_MODE_EDGE: - retval |= test_edge( - head_vec, tail_vec, ray_origin_local, ray_normal_local, - ray_depth_range, ob_scale, is_persp, ray_depth, dist_to_ray_sq, - r_loc, NULL); + retval |= test_projected_edge_dist( + &projectdefs, head_vec, tail_vec, ray_start_local, ray_normal_local, + view_proj, mval, depth_range, r_loc); break; } } } } if (retval) { + *dist_px = sqrtf(projectdefs.dist_px_sq); mul_m4_v3(obmat, r_loc); return true; } @@ -399,39 +810,26 @@ static bool snapArmature( } static bool snapCurve( - Object *ob, Curve *cu, float obmat[4][4], - const short snap_to, const bool is_persp, - const float ray_origin[3], const float ray_normal[3], const float ray_depth_range[2], + const ARegion *ar, Object *ob, Curve *cu, float obmat[4][4], + const short snap_to, const float mval[2], const enum eViewProj view_proj, + const float depth_range[2], /* read/write args */ - float *ray_depth, float *dist_to_ray_sq, + float *dist_px, /* return args */ float r_loc[3], float *UNUSED(r_no)) { - float imat[4][4]; - float ray_origin_local[3], ray_normal_local[3]; bool retval = false; - int u; - - Nurb *nu; /* only vertex snapping mode (eg control points and handles) supported for now) */ if (snap_to != SCE_SNAP_MODE_VERTEX) { return retval; } - invert_m4_m4(imat, obmat); - - copy_v3_v3(ray_origin_local, ray_origin); - copy_v3_v3(ray_normal_local, ray_normal); - - mul_m4_v3(imat, ray_origin_local); - mul_mat3_m4_v3(imat, ray_normal_local); - - float ob_scale[3]; - mat4_to_size(ob_scale, obmat); + PreDefProject projectdefs; + precalc_project(&projectdefs, ar, *dist_px, obmat); - for (nu = (ob->mode == OB_MODE_EDIT ? cu->editnurb->nurbs.first : cu->nurb.first); nu; nu = nu->next) { - for (u = 0; u < nu->pntsu; u++) { + for (Nurb *nu = (ob->mode == OB_MODE_EDIT ? cu->editnurb->nurbs.first : cu->nurb.first); nu; nu = nu->next) { + for (int u = 0; u < nu->pntsu; u++) { switch (snap_to) { case SCE_SNAP_MODE_VERTEX: { @@ -441,26 +839,20 @@ static bool snapCurve( if (nu->bezt[u].f2 & SELECT || nu->bezt[u].hide != 0) { break; } - retval |= test_vert( - nu->bezt[u].vec[1], NULL, ray_origin_local, ray_normal_local, - ray_depth_range, ob_scale, is_persp, ray_depth, dist_to_ray_sq, - r_loc, NULL); + retval |= test_projected_vert_dist( + &projectdefs, nu->bezt[u].vec[1], view_proj, mval, depth_range, 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_vert( - nu->bezt[u].vec[0], NULL, ray_origin_local, ray_normal_local, - ray_depth_range, ob_scale, is_persp, ray_depth, dist_to_ray_sq, - r_loc, NULL); + retval |= test_projected_vert_dist( + &projectdefs, nu->bezt[u].vec[0], view_proj, mval, depth_range, r_loc); } if (!(nu->bezt[u].f3 & SELECT) && !(nu->bezt[u].h2 & HD_ALIGN && nu->bezt[u].f1 & SELECT)) { - retval |= test_vert( - nu->bezt[u].vec[2], NULL, ray_origin_local, ray_normal_local, - ray_depth_range, ob_scale, is_persp, ray_depth, dist_to_ray_sq, - r_loc, NULL); + retval |= test_projected_vert_dist( + &projectdefs, nu->bezt[u].vec[2], view_proj, mval, depth_range, r_loc); } } else { @@ -468,26 +860,20 @@ static bool snapCurve( if (nu->bp[u].f1 & SELECT || nu->bp[u].hide != 0) { break; } - retval |= test_vert( - nu->bp[u].vec, NULL, ray_origin_local, ray_normal_local, - ray_depth_range, ob_scale, is_persp, ray_depth, dist_to_ray_sq, - r_loc, NULL); + retval |= test_projected_vert_dist( + &projectdefs, nu->bp[u].vec, view_proj, mval, depth_range, r_loc); } } else { /* curve is not visible outside editmode if nurb length less than two */ if (nu->pntsu > 1) { if (nu->bezt) { - retval |= test_vert( - nu->bezt[u].vec[1], NULL, ray_origin_local, ray_normal_local, - ray_depth_range, ob_scale, is_persp, ray_depth, dist_to_ray_sq, - r_loc, NULL); + retval |= test_projected_vert_dist( + &projectdefs, nu->bezt[u].vec[1], view_proj, mval, depth_range, r_loc); } else { - retval |= test_vert( - nu->bp[u].vec, NULL, ray_origin_local, ray_normal_local, - ray_depth_range, ob_scale, is_persp, ray_depth, dist_to_ray_sq, - r_loc, NULL); + retval |= test_projected_vert_dist( + &projectdefs, nu->bp[u].vec, view_proj, mval, depth_range, r_loc); } } } @@ -499,6 +885,7 @@ static bool snapCurve( } } if (retval) { + *dist_px = sqrtf(projectdefs.dist_px_sq); mul_m4_v3(obmat, r_loc); return true; } @@ -507,11 +894,11 @@ static bool snapCurve( /* may extend later (for now just snaps to empty center) */ static bool snapEmpty( - Object *ob, float obmat[4][4], - const short snap_to, const bool is_persp, - const float ray_origin[3], const float ray_normal[3], const float ray_depth_range[2], + const ARegion *ar, Object *ob, float obmat[4][4], + const short snap_to, const float mval[2], const enum eViewProj view_proj, + const float depth_range[2], /* read/write args */ - float *ray_depth, float *dist_to_ray_sq, + float *dist_px, /* return args */ float r_loc[3], float *UNUSED(r_no)) { @@ -520,17 +907,19 @@ static bool snapEmpty( if (ob->transflag & OB_DUPLI) { return retval; } + /* for now only vertex supported */ switch (snap_to) { case SCE_SNAP_MODE_VERTEX: { - float ob_loc[3], ob_scale[3] = {1.0, 1.0, 1.0}; - copy_v3_v3(ob_loc, obmat[3]); - - retval |= test_vert( - ob_loc, NULL, ray_origin, ray_normal, - ray_depth_range, ob_scale, is_persp, ray_depth, dist_to_ray_sq, - r_loc, NULL); + PreDefProject projectdefs; + precalc_project(&projectdefs, ar, *dist_px, NULL); + float tmp_co[3]; + copy_v3_v3(tmp_co, obmat[3]); + if (test_projected_vert_dist(&projectdefs, tmp_co, view_proj, mval, depth_range, r_loc)) { + *dist_px = sqrtf(projectdefs.dist_px_sq); + retval = true; + } break; } default: @@ -541,19 +930,23 @@ static bool snapEmpty( } static bool snapCamera( - Scene *scene, Object *object, float obmat[4][4], - const short snap_to, const bool is_persp, - const float ray_origin[3], const float ray_normal[3], const float ray_depth_range[2], + const SnapObjectContext *sctx, Object *object, float obmat[4][4], + const short snap_to, const float mval[2], const enum eViewProj view_proj, + const float depth_range[2], /* read/write args */ - float *ray_depth, float *dist_to_ray_sq, + float *dist_px, /* return args */ float r_loc[3], float *UNUSED(r_no)) { + Scene *scene = sctx->scene; + + PreDefProject projectdefs; + precalc_project(&projectdefs, sctx->v3d_data.ar, *dist_px, NULL); + float orig_camera_mat[4][4], orig_camera_imat[4][4], imat[4][4]; bool retval = false; MovieClip *clip = BKE_object_movieclip_get(scene, object, false); MovieTracking *tracking; - float ray_origin_local[3], ray_normal_local[3]; if (clip == NULL) { return retval; @@ -584,9 +977,6 @@ static bool snapCamera( reconstructed_camera_imat[4][4]; float (*vertex_obmat)[4]; - copy_v3_v3(ray_origin_local, ray_origin); - copy_v3_v3(ray_normal_local, ray_normal); - if ((tracking_object->flag & TRACKING_OBJECT_CAMERA) == 0) { BKE_tracking_camera_get_reconstructed_interpolate(tracking, tracking_object, CFRA, reconstructed_camera_mat); @@ -603,26 +993,16 @@ static bool snapCamera( copy_v3_v3(bundle_pos, track->bundle_pos); if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { - mul_m4_v3(orig_camera_imat, ray_origin_local); - mul_mat3_m4_v3(orig_camera_imat, ray_normal_local); vertex_obmat = orig_camera_mat; } else { mul_m4_v3(reconstructed_camera_imat, bundle_pos); - mul_m4_v3(imat, ray_origin_local); - mul_mat3_m4_v3(imat, ray_normal_local); vertex_obmat = obmat; } - float ob_scale[3]; - mat4_to_size(ob_scale, vertex_obmat); - - retval |= test_vert( - bundle_pos, NULL, ray_origin_local, ray_normal_local, - ray_depth_range, ob_scale, is_persp, ray_depth, dist_to_ray_sq, - r_loc, NULL); - - mul_m4_v3(vertex_obmat, r_loc); + mul_m4_v3(vertex_obmat, bundle_pos); + retval |= test_projected_vert_dist( + &projectdefs, bundle_pos, view_proj, mval, depth_range, r_loc); } } @@ -632,7 +1012,11 @@ static bool snapCamera( break; } - return retval; + if (retval) { + *dist_px = sqrtf(projectdefs.dist_px_sq); + return true; + } + return false; } static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt) @@ -643,44 +1027,45 @@ static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt) struct NearestDM_Data { void *bvhdata; - bool is_persp; - const float *ray_depth_range; - + const float *depth_range; float *ray_depth; }; -static void test_vert_depth_cb( +static void test_vert_ray_dist_cb( void *userdata, const float origin[3], const float dir[3], const float scale[3], int index, BVHTreeNearest *nearest) { struct NearestDM_Data *ndata = userdata; - const BVHTreeFromMesh *data = ndata->bvhdata; - const MVert *vert = data->vert + index; + const struct BVHTreeFromMeshType *data = ndata->bvhdata; + + const float *co = get_vert_co(data, index); - if (test_vert( - vert->co, NULL, origin, dir, ndata->ray_depth_range, scale, ndata->is_persp, - ndata->ray_depth, &nearest->dist_sq, - nearest->co, NULL)) + if (test_vert_dist( + co, origin, dir, ndata->depth_range, + scale, ndata->ray_depth, &nearest->dist_sq, + nearest->co)) { - normal_short_to_float_v3(nearest->no, vert->no); + copy_vert_no(data, index, nearest->no); nearest->index = index; } } -static void test_edge_depth_cb( +static void test_edge_ray_dist_cb( void *userdata, const float origin[3], const float dir[3], const float scale[3], int index, BVHTreeNearest *nearest) { struct NearestDM_Data *ndata = userdata; - const BVHTreeFromMesh *data = ndata->bvhdata; - const MVert *vert = data->vert; - const MEdge *edge = data->edge + index; - - if (test_edge( - vert[edge->v1].co, vert[edge->v2].co, origin, dir, ndata->ray_depth_range, scale, ndata->is_persp, - ndata->ray_depth, &nearest->dist_sq, - nearest->co, nearest->no)) + BVHTreeFromMeshType *data = ndata->bvhdata; + + const float *v_pair[2]; + get_edge_verts(data, index, v_pair); + + if (test_edge_dist( + v_pair[0], v_pair[1], origin, dir, ndata->depth_range, + scale, ndata->ray_depth, &nearest->dist_sq, + nearest->co)) { + sub_v3_v3v3(nearest->no, v_pair[0], v_pair[1]); nearest->index = index; } } @@ -688,10 +1073,11 @@ static void test_edge_depth_cb( static bool snapDerivedMesh( SnapObjectContext *sctx, Object *ob, DerivedMesh *dm, float obmat[4][4], const unsigned int ob_index, - const short snap_to, const bool is_persp, bool do_bb, - const float ray_origin[3], const float ray_start[3], const float ray_normal[3], const float ray_depth_range[2], + const short snap_to, const float mval[2], const enum eViewProj view_proj, bool do_bb, + const float ray_origin[3], const float ray_start[3], const float ray_normal[3], + const float depth_range[2], /* read/write args */ - float *ray_depth, float *dist_to_ray_sq, + float *ray_depth, float *dist_px, /* return args */ float r_loc[3], float r_no[3], int *r_index, ListBase *r_hit_list) @@ -703,7 +1089,7 @@ static bool snapDerivedMesh( return retval; } } - if (snap_to == SCE_SNAP_MODE_EDGE) { + else if (snap_to == SCE_SNAP_MODE_EDGE) { if (dm->getNumEdges(dm) == 0) { return retval; } @@ -715,7 +1101,7 @@ static bool snapDerivedMesh( } { - bool need_ray_start_correction_init = (snap_to == SCE_SNAP_MODE_FACE) && sctx->use_v3d && !is_persp; + bool need_ray_start_correction_init = (snap_to == SCE_SNAP_MODE_FACE) && (view_proj == VIEW_PROJ_ORTHO); float imat[4][4]; float timat[3][3]; /* transpose inverse matrix for normals */ @@ -823,28 +1209,30 @@ static bool snapDerivedMesh( } } + if (!treedata || !treedata->tree) { + return retval; + } + if (snap_to == SCE_SNAP_MODE_FACE) { /* 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). * Note also ar might be null (see T38435), in this case we assume ray_start is ok! */ - if (sctx->use_v3d && !is_persp) { /* do_ray_start_correction */ + if (view_proj == VIEW_PROJ_ORTHO) { /* do_ray_start_correction */ if (need_ray_start_correction_init) { /* We *need* a reasonably valid len_diff in this case. * Use BHVTree to find the closest face from ray_start_local. */ - if (treedata && treedata->tree != NULL) { - BVHTreeNearest nearest; - nearest.index = -1; - nearest.dist_sq = FLT_MAX; - /* Compute and store result. */ - BLI_bvhtree_find_nearest( - treedata->tree, ray_start_local, &nearest, treedata->nearest_callback, treedata); - if (nearest.index != -1) { - float dvec[3]; - sub_v3_v3v3(dvec, nearest.co, ray_start_local); - len_diff = dot_v3v3(dvec, ray_normal_local); - } + BVHTreeNearest nearest; + nearest.index = -1; + nearest.dist_sq = FLT_MAX; + /* Compute and store result. */ + BLI_bvhtree_find_nearest( + treedata->tree, ray_start_local, &nearest, treedata->nearest_callback, treedata); + if (nearest.index != -1) { + float dvec[3]; + sub_v3_v3v3(dvec, nearest.co, ray_start_local); + len_diff = dot_v3v3(dvec, ray_normal_local); } } float ray_org_local[3]; @@ -856,8 +1244,8 @@ static bool snapDerivedMesh( * away ray_start values (as returned in case of ortho view3d), see T38358. */ len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ - madd_v3_v3v3fl(ray_start_local, ray_org_local, ray_normal_local, - len_diff + ray_depth_range[0]); + madd_v3_v3v3fl( + ray_start_local, ray_org_local, ray_normal_local, len_diff + depth_range[0] * local_scale); local_depth -= len_diff; } else { @@ -874,24 +1262,19 @@ static bool snapDerivedMesh( data.local_scale = local_scale; data.ob = ob; data.ob_uuid = ob_index; - data.dm = dm; data.hit_list = r_hit_list; data.retval = retval; BLI_bvhtree_ray_cast_all( - treedata->tree, ray_start_local, ray_normal_local, 0.0f, - *ray_depth, raycast_all_cb, &data); + treedata->tree, ray_start_local, ray_normal_local, 0.0f, + *ray_depth, raycast_all_cb, &data); retval = data.retval; } else { - BVHTreeRayHit hit; + BVHTreeRayHit hit = {.index = -1, .dist = local_depth}; - hit.index = -1; - hit.dist = local_depth; - - if (treedata->tree && - BLI_bvhtree_ray_cast( + if (BLI_bvhtree_ray_cast( treedata->tree, ray_start_local, ray_normal_local, 0.0f, &hit, treedata->raycast_callback, treedata) != -1) { @@ -900,12 +1283,15 @@ static bool snapDerivedMesh( if (hit.dist <= *ray_depth) { *ray_depth = hit.dist; copy_v3_v3(r_loc, hit.co); - copy_v3_v3(r_no, hit.no); /* back to worldspace */ mul_m4_v3(obmat, r_loc); - mul_m3_v3(timat, r_no); - normalize_v3(r_no); + + if (r_no) { + copy_v3_v3(r_no, hit.no); + mul_m3_v3(timat, r_no); + normalize_v3(r_no); + } retval = true; @@ -917,51 +1303,78 @@ static bool snapDerivedMesh( } } else { - /* Vert & edge use nearly identical logic. */ - BLI_assert(ELEM(snap_to, SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE)); + const ARegion *ar = sctx->v3d_data.ar; float ray_org_local[3]; - copy_v3_v3(ray_org_local, ray_origin); mul_m4_v3(imat, ray_org_local); - BVHTreeNearest nearest; + BVHTreeFromMeshType treedata_type = {.userdata = treedata, .type = SNAP_MESH}; - nearest.index = -1; - nearest.dist_sq = *dist_to_ray_sq; + if (view_proj == VIEW_PROJ_PERSP) { + Object_Nearest2dPrecalc neasrest_precalc; + neasrest_precalc.userdata = &treedata_type; + neasrest_precalc.index = -1; - struct NearestDM_Data userdata; - userdata.bvhdata = treedata; - userdata.is_persp = is_persp; - userdata.ray_depth_range = ray_depth_range; - userdata.ray_depth = ray_depth; + nearest2d_precalc(&neasrest_precalc, ar, *dist_px, obmat, + ray_org_local, ray_normal_local, mval, depth_range); - float ob_scale[3]; - mat4_to_size(ob_scale, obmat); + BVHTree_WalkLeafCallback cb_walk_leaf = + (snap_to == SCE_SNAP_MODE_VERTEX) ? + cb_walk_leaf_snap_vert : cb_walk_leaf_snap_edge; - BVHTree_NearestToRayCallback callback = - (snap_to == SCE_SNAP_MODE_VERTEX) ? - test_vert_depth_cb : test_edge_depth_cb; + BLI_bvhtree_walk_dfs( + treedata->tree, + cb_walk_parent_snap_project, cb_walk_leaf, cb_nearest_walk_order, &neasrest_precalc); - if (treedata->tree && - (is_persp ? - BLI_bvhtree_find_nearest_to_ray_angle( - treedata->tree, ray_org_local, ray_normal_local, - true, ob_scale, &nearest, callback, &userdata) : - BLI_bvhtree_find_nearest_to_ray( - treedata->tree, ray_org_local, ray_normal_local, - true, ob_scale, &nearest, callback, &userdata)) != -1) - { - copy_v3_v3(r_loc, nearest.co); - mul_m4_v3(obmat, r_loc); - if (r_no) { - copy_v3_v3(r_no, nearest.no); - mul_m3_v3(timat, r_no); - normalize_v3(r_no); + if (neasrest_precalc.index != -1) { + copy_v3_v3(r_loc, neasrest_precalc.co); + mul_m4_v3(obmat, r_loc); + if (r_no) { + copy_v3_v3(r_no, neasrest_precalc.no); + mul_m3_v3(timat, r_no); + normalize_v3(r_no); + } + *dist_px = sqrtf(neasrest_precalc.projectdefs.dist_px_sq); + + retval = true; } - *dist_to_ray_sq = nearest.dist_sq; + } + else { + BVHTreeNearest nearest; - retval = true; + nearest.index = -1; + float dist_3d = dist_px_to_dist3d_or_tangent(ar, *dist_px); + nearest.dist_sq = SQUARE(dist_3d); + + + float ob_scale[3]; + mat4_to_size(ob_scale, obmat); + + struct NearestDM_Data userdata; + userdata.bvhdata = &treedata_type; + userdata.depth_range = depth_range; + userdata.ray_depth = ray_depth; + + BVHTree_NearestToRayCallback cb_test_ray_dist = + (snap_to == SCE_SNAP_MODE_VERTEX) ? + test_vert_ray_dist_cb : test_edge_ray_dist_cb; + + if (BLI_bvhtree_find_nearest_to_ray( + treedata->tree, ray_org_local, ray_normal_local, + true, ob_scale, &nearest, cb_test_ray_dist, &userdata) != -1) + { + copy_v3_v3(r_loc, nearest.co); + mul_m4_v3(obmat, r_loc); + if (r_no) { + copy_v3_v3(r_no, nearest.no); + mul_m3_v3(timat, r_no); + normalize_v3(r_no); + } + *dist_px *= sqrtf(nearest.dist_sq) / dist_3d; + + retval = true; + } } } @@ -975,47 +1388,14 @@ static bool snapDerivedMesh( return retval; } -static void test_bmvert_depth_cb( - void *userdata, const float origin[3], const float dir[3], - const float scale[3], int index, BVHTreeNearest *nearest) -{ - struct NearestDM_Data *ndata = userdata; - const BMEditMesh *em = ndata->bvhdata; - BMVert *eve = BM_vert_at_index(em->bm, index); - - if (test_vert( - eve->co, eve->no, origin, dir, ndata->ray_depth_range, scale, ndata->is_persp, - ndata->ray_depth, &nearest->dist_sq, - nearest->co, nearest->no)) - { - nearest->index = index; - } -} - -static void test_bmedge_depth_cb( - void *userdata, const float origin[3], const float dir[3], - const float scale[3], int index, BVHTreeNearest *nearest) -{ - struct NearestDM_Data *ndata = userdata; - const BMEditMesh *em = ndata->bvhdata; - BMEdge *eed = BM_edge_at_index(em->bm, index); - - if (test_edge( - eed->v1->co, eed->v2->co, origin, dir, ndata->ray_depth_range, scale, ndata->is_persp, - ndata->ray_depth, &nearest->dist_sq, - nearest->co, nearest->no)) - { - nearest->index = index; - } -} - static bool snapEditMesh( SnapObjectContext *sctx, Object *ob, BMEditMesh *em, float obmat[4][4], const unsigned int ob_index, - const short snap_to, const bool is_persp, - const float ray_origin[3], const float ray_start[3], const float ray_normal[3], const float ray_depth_range[2], + const short snap_to, const float mval[2], const enum eViewProj view_proj, + const float ray_origin[3], const float ray_start[3], const float ray_normal[3], + const float depth_range[2], /* read/write args */ - float *ray_depth, float *dist_to_ray_sq, + float *ray_depth, float *dist_px, /* return args */ float r_loc[3], float r_no[3], int *r_index, ListBase *r_hit_list) @@ -1143,6 +1523,10 @@ static bool snapEditMesh( } } + if (!treedata || !treedata->tree) { + return retval; + } + if (snap_to == SCE_SNAP_MODE_FACE) { float ray_start_local[3]; copy_v3_v3(ray_start_local, ray_start); @@ -1160,35 +1544,33 @@ static bool snapEditMesh( * Note also ar might be null (see T38435), in this case we assume ray_start is ok! */ float len_diff = 0.0f; - if (sctx->use_v3d && !is_persp) { /* do_ray_start_correction */ + if (view_proj == VIEW_PROJ_ORTHO) { /* do_ray_start_correction */ /* We *need* a reasonably valid len_diff in this case. * Use BHVTree to find the closest face from ray_start_local. */ - if (treedata && treedata->tree != NULL) { - BVHTreeNearest nearest; - nearest.index = -1; - nearest.dist_sq = FLT_MAX; - /* Compute and store result. */ - if (BLI_bvhtree_find_nearest( - treedata->tree, ray_start_local, &nearest, NULL, NULL) != -1) - { - float dvec[3]; - sub_v3_v3v3(dvec, nearest.co, ray_start_local); - len_diff = dot_v3v3(dvec, ray_normal_local); - float ray_org_local[3]; - - copy_v3_v3(ray_org_local, ray_origin); - mul_m4_v3(imat, ray_org_local); - - /* We pass a temp ray_start, set from object's boundbox, - * to avoid precision issues with very far away ray_start values - * (as returned in case of ortho view3d), see T38358. - */ - len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ - madd_v3_v3v3fl(ray_start_local, ray_org_local, ray_normal_local, - len_diff + ray_depth_range[0]); - local_depth -= len_diff; - } + BVHTreeNearest nearest; + nearest.index = -1; + nearest.dist_sq = FLT_MAX; + /* Compute and store result. */ + if (BLI_bvhtree_find_nearest( + treedata->tree, ray_start_local, &nearest, NULL, NULL) != -1) + { + float dvec[3]; + sub_v3_v3v3(dvec, nearest.co, ray_start_local); + len_diff = dot_v3v3(dvec, ray_normal_local); + float ray_org_local[3]; + + copy_v3_v3(ray_org_local, ray_origin); + mul_m4_v3(imat, ray_org_local); + + /* We pass a temp ray_start, set from object's boundbox, + * to avoid precision issues with very far away ray_start values + * (as returned in case of ortho view3d), see T38358. + */ + len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ + madd_v3_v3v3fl( + ray_start_local, ray_org_local, ray_normal_local, len_diff + depth_range[0] * local_scale); + local_depth -= len_diff; } } if (r_hit_list) { @@ -1202,7 +1584,6 @@ static bool snapEditMesh( data.local_scale = local_scale; data.ob = ob; data.ob_uuid = ob_index; - data.dm = NULL; data.hit_list = r_hit_list; data.retval = retval; @@ -1213,13 +1594,9 @@ static bool snapEditMesh( retval = data.retval; } else { - BVHTreeRayHit hit; + BVHTreeRayHit hit = {.index = -1, .dist = local_depth}; - hit.index = -1; - hit.dist = local_depth; - - if (treedata->tree && - BLI_bvhtree_ray_cast( + if (BLI_bvhtree_ray_cast( treedata->tree, ray_start_local, ray_normal_local, 0.0f, &hit, treedata->raycast_callback, treedata) != -1) { @@ -1228,12 +1605,15 @@ static bool snapEditMesh( if (hit.dist <= *ray_depth) { *ray_depth = hit.dist; copy_v3_v3(r_loc, hit.co); - copy_v3_v3(r_no, hit.no); /* back to worldspace */ mul_m4_v3(obmat, r_loc); - mul_m3_v3(timat, r_no); - normalize_v3(r_no); + + if (r_no) { + copy_v3_v3(r_no, hit.no); + mul_m3_v3(timat, r_no); + normalize_v3(r_no); + } retval = true; @@ -1245,51 +1625,78 @@ static bool snapEditMesh( } } else { - /* Vert & edge use nearly identical logic. */ - BLI_assert(ELEM(snap_to, SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE)); + const ARegion *ar = sctx->v3d_data.ar; float ray_org_local[3]; - copy_v3_v3(ray_org_local, ray_origin); mul_m4_v3(imat, ray_org_local); - BVHTreeNearest nearest; + BVHTreeFromMeshType treedata_type = {.userdata = treedata, .type = SNAP_EDIT_MESH}; - nearest.index = -1; - nearest.dist_sq = *dist_to_ray_sq; + if (view_proj == VIEW_PROJ_PERSP) { + Object_Nearest2dPrecalc neasrest_precalc; + neasrest_precalc.userdata = &treedata_type; + neasrest_precalc.index = -1; - struct NearestDM_Data userdata; - userdata.bvhdata = em; - userdata.is_persp = is_persp; - userdata.ray_depth_range = ray_depth_range; - userdata.ray_depth = ray_depth; + nearest2d_precalc(&neasrest_precalc, ar, *dist_px, obmat, + ray_org_local, ray_normal_local, mval, depth_range); - float ob_scale[3]; - mat4_to_size(ob_scale, obmat); + BVHTree_WalkLeafCallback cb_walk_leaf = + (snap_to == SCE_SNAP_MODE_VERTEX) ? + cb_walk_leaf_snap_vert : cb_walk_leaf_snap_edge; - BVHTree_NearestToRayCallback callback = - (snap_to == SCE_SNAP_MODE_VERTEX) ? - test_bmvert_depth_cb : test_bmedge_depth_cb; + BLI_bvhtree_walk_dfs( + treedata->tree, + cb_walk_parent_snap_project, cb_walk_leaf, cb_nearest_walk_order, &neasrest_precalc); - if (treedata->tree && - (is_persp ? - BLI_bvhtree_find_nearest_to_ray_angle( - treedata->tree, ray_org_local, ray_normal_local, - false, ob_scale, &nearest, callback, &userdata) : - BLI_bvhtree_find_nearest_to_ray( - treedata->tree, ray_org_local, ray_normal_local, - false, ob_scale, &nearest, callback, &userdata)) != -1) - { - copy_v3_v3(r_loc, nearest.co); - mul_m4_v3(obmat, r_loc); - if (r_no) { - copy_v3_v3(r_no, nearest.no); - mul_m3_v3(timat, r_no); - normalize_v3(r_no); + if (neasrest_precalc.index != -1) { + copy_v3_v3(r_loc, neasrest_precalc.co); + mul_m4_v3(obmat, r_loc); + if (r_no) { + copy_v3_v3(r_no, neasrest_precalc.no); + mul_m3_v3(timat, r_no); + normalize_v3(r_no); + } + *dist_px = sqrtf(neasrest_precalc.projectdefs.dist_px_sq); + + retval = true; } - *dist_to_ray_sq = nearest.dist_sq; + } + else { + BVHTreeNearest nearest; - retval = true; + nearest.index = -1; + float dist_3d = dist_px_to_dist3d_or_tangent(ar, *dist_px); + nearest.dist_sq = SQUARE(dist_3d); + + + float ob_scale[3]; + mat4_to_size(ob_scale, obmat); + + struct NearestDM_Data userdata; + userdata.bvhdata = &treedata_type; + userdata.depth_range = depth_range; + userdata.ray_depth = ray_depth; + + BVHTree_NearestToRayCallback cb_test_ray_dist = + (snap_to == SCE_SNAP_MODE_VERTEX) ? + test_vert_ray_dist_cb : test_edge_ray_dist_cb; + + if (BLI_bvhtree_find_nearest_to_ray( + treedata->tree, ray_org_local, ray_normal_local, + false, ob_scale, &nearest, cb_test_ray_dist, &userdata) != -1) + { + copy_v3_v3(r_loc, nearest.co); + mul_m4_v3(obmat, r_loc); + if (r_no) { + copy_v3_v3(r_no, nearest.no); + mul_m3_v3(timat, r_no); + normalize_v3(r_no); + } + *dist_px *= sqrtf(nearest.dist_sq) / dist_3d; + + retval = true; + } } } @@ -1305,26 +1712,28 @@ static bool snapEditMesh( /** * \param use_obedit: Uses the coordinates of BMesh (if any) to do the snapping; - * \param ray_depth_range: - * - 0: distance from the ray_origin to the clipping plane min (can be negative). - * - 1: maximum distance, elements outside this are ignored. - * \param ray_depth: maximum depth allowed for r_co. * * \note Duplicate args here are documented at #snapObjectsRay */ static bool snapObject( SnapObjectContext *sctx, Object *ob, float obmat[4][4], const unsigned int ob_index, - bool use_obedit, const short snap_to, - const float ray_origin[3], const float ray_start[3], const float ray_normal[3], const float ray_depth_range[2], + bool use_obedit, const short snap_to, const float mval[2], + const float ray_origin[3], const float ray_start[3], const float ray_normal[3], + const float depth_range[2], /* read/write args */ - float *ray_depth, float *dist_to_ray_sq, + float *ray_depth, float *dist_px, /* return args */ float r_loc[3], float r_no[3], int *r_index, Object **r_ob, float r_obmat[4][4], ListBase *r_hit_list) { - const bool is_persp = sctx->use_v3d && ((RegionView3D *)sctx->v3d_data.ar->regiondata)->is_persp; + const enum eViewProj view_proj = + ((sctx->use_v3d == false) || (mval == NULL)) ? VIEW_PROJ_NONE : + (((RegionView3D *)sctx->v3d_data.ar->regiondata)->is_persp ? VIEW_PROJ_PERSP : VIEW_PROJ_ORTHO); + + const ARegion *ar = sctx->v3d_data.ar; + bool retval = false; if (ob->type == OB_MESH) { @@ -1334,9 +1743,9 @@ static bool snapObject( em = BKE_editmesh_from_object(ob); retval = snapEditMesh( sctx, ob, em, obmat, ob_index, - snap_to, is_persp, - ray_origin, ray_start, ray_normal, ray_depth_range, - ray_depth, dist_to_ray_sq, + snap_to, mval, view_proj, + ray_origin, ray_start, ray_normal, depth_range, + ray_depth, dist_px, r_loc, r_no, r_index, r_hit_list); } @@ -1353,42 +1762,43 @@ static bool snapObject( } retval = snapDerivedMesh( sctx, ob, dm, obmat, ob_index, - snap_to, is_persp, true, - ray_origin, ray_start, ray_normal, ray_depth_range, - ray_depth, dist_to_ray_sq, + snap_to, mval, view_proj, true, + ray_origin, ray_start, ray_normal, depth_range, + ray_depth, dist_px, r_loc, r_no, r_index, r_hit_list); dm->release(dm); } } - else if (ob->type == OB_ARMATURE) { - retval = snapArmature( - ob, ob->data, obmat, snap_to, is_persp, - ray_origin, ray_normal, ray_depth_range, - ray_depth, dist_to_ray_sq, - r_loc, r_no); - } - else if (ob->type == OB_CURVE) { - retval = snapCurve( - ob, ob->data, obmat, snap_to, is_persp, - ray_origin, ray_normal, ray_depth_range, - ray_depth, dist_to_ray_sq, - r_loc, r_no); - } - else if (ob->type == OB_EMPTY) { - retval = snapEmpty( - ob, obmat, snap_to, is_persp, - ray_origin, ray_normal, ray_depth_range, - ray_depth, dist_to_ray_sq, - r_loc, r_no); - } - else if (ob->type == OB_CAMERA) { - retval = snapCamera( - sctx->scene, ob, obmat, snap_to, is_persp, - ray_origin, ray_normal, ray_depth_range, - ray_depth, dist_to_ray_sq, - r_loc, r_no); + else if (snap_to != SCE_SNAP_MODE_FACE) { + if (ob->type == OB_ARMATURE) { + retval = snapArmature( + ar, ob, ob->data, obmat, snap_to, ray_origin, ray_normal, + mval, view_proj, depth_range, dist_px, + r_loc, r_no); + } + else if (ob->type == OB_CURVE) { + retval = snapCurve( + ar, ob, ob->data, obmat, snap_to, mval, view_proj, + depth_range, + dist_px, + r_loc, r_no); + } + else if (ob->type == OB_EMPTY) { + retval = snapEmpty( + ar, ob, obmat, snap_to, mval, view_proj, + depth_range, + dist_px, + r_loc, r_no); + } + else if (ob->type == OB_CAMERA) { + retval = snapCamera( + sctx, ob, obmat, snap_to, mval, view_proj, + depth_range, + dist_px, + r_loc, r_no); + } } if (retval) { @@ -1414,16 +1824,18 @@ static bool snapObject( * \param snap_select: from enum SnapSelect. * * \param use_object_edit_cage: Uses the coordinates of BMesh (if any) to do the snapping. + * \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_normal: Unit length direction of the ray. + * \param depth_range: distances of clipe plane min and clip plane max; * * Read/Write Args * --------------- * * \param ray_depth: maximum depth allowed for r_co, elements deeper than this value will be ignored. - * \param dist_to_ray_sq: Real distance (3D) or Tangent (view cone radius at distance 1.0) squared. - * resulting of the function #dist_px_to_dist3d_or_tangent. + * \param dist_px: Maximum threshold distance (in pixels). * * Output Args * ----------- @@ -1440,10 +1852,11 @@ static bool snapObject( static bool snapObjectsRay( SnapObjectContext *sctx, const unsigned short snap_to, const SnapSelect snap_select, - const bool use_object_edit_cage, + const bool use_object_edit_cage, const float mval[2], const float ray_origin[3], const float ray_start[3], const float ray_normal[3], + const float depth_range[2], /* read/write args */ - float *ray_depth, float *dist_to_ray_sq, + float *ray_depth, float *dist_px, /* return args */ float r_loc[3], float r_no[3], int *r_index, Object **r_ob, float r_obmat[4][4], @@ -1451,14 +1864,6 @@ static bool snapObjectsRay( { bool retval = false; - float dvec[3]; - sub_v3_v3v3(dvec, ray_start, ray_origin); - - const float ray_depth_range[2] = { - dot_v3v3(dvec, ray_normal), - *ray_depth, - }; - unsigned int ob_index = 0; Object *obedit = use_object_edit_cage ? sctx->scene->obedit : NULL; @@ -1473,9 +1878,9 @@ static bool snapObjectsRay( retval |= snapObject( sctx, ob, ob->obmat, ob_index++, - false, snap_to, - ray_origin, ray_start, ray_normal, ray_depth_range, - ray_depth, dist_to_ray_sq, + false, snap_to, mval, + ray_origin, ray_start, ray_normal, depth_range, + ray_depth, dist_px, r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list); } @@ -1509,9 +1914,9 @@ static bool snapObjectsRay( retval |= snapObject( sctx, dupli_snap, dupli_ob->mat, ob_index++, - use_obedit_dupli, snap_to, - ray_origin, ray_start, ray_normal, ray_depth_range, - ray_depth, dist_to_ray_sq, + use_obedit_dupli, snap_to, mval, + ray_origin, ray_start, ray_normal, depth_range, + ray_depth, dist_px, r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list); } @@ -1523,9 +1928,9 @@ static bool snapObjectsRay( retval |= snapObject( sctx, ob_snap, ob->obmat, ob_index++, - use_obedit, snap_to, - ray_origin, ray_start, ray_normal, ray_depth_range, - ray_depth, dist_to_ray_sq, + use_obedit, snap_to, mval, + ray_origin, ray_start, ray_normal, depth_range, + ray_depth, dist_px, r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list); } } @@ -1632,13 +2037,12 @@ bool ED_transform_snap_object_project_ray_ex( float r_loc[3], float r_no[3], int *r_index, Object **r_ob, float r_obmat[4][4]) { - float dist_to_ray_sq = 0.0f; - + const float depth_range[2] = {0.0f, FLT_MAX}; return snapObjectsRay( sctx, - snap_to, params->snap_select, params->use_object_edit_cage, - ray_start, ray_start, ray_normal, - ray_depth, &dist_to_ray_sq, + snap_to, params->snap_select, params->use_object_edit_cage, NULL, + ray_start, ray_start, ray_normal, depth_range, + ray_depth, NULL, r_loc, r_no, r_index, r_ob, r_obmat, NULL); } @@ -1657,8 +2061,7 @@ bool ED_transform_snap_object_project_ray_all( float ray_depth, bool sort, ListBase *r_hit_list) { - float dist_to_ray_sq = 0.0f; - + const float depth_range[2] = {0.0f, FLT_MAX}; if (ray_depth == -1.0f) { ray_depth = BVH_RAYCAST_DIST_MAX; } @@ -1669,9 +2072,9 @@ bool ED_transform_snap_object_project_ray_all( bool retval = snapObjectsRay( sctx, - snap_to, params->snap_select, params->use_object_edit_cage, - ray_start, ray_start, ray_normal, - &ray_depth, &dist_to_ray_sq, + snap_to, params->snap_select, params->use_object_edit_cage, NULL, + ray_start, ray_start, ray_normal, depth_range, + &ray_depth, NULL, NULL, NULL, NULL, NULL, NULL, r_hit_list); @@ -1726,11 +2129,6 @@ bool ED_transform_snap_object_project_ray( ray_depth = &ray_depth_fallback; } - float no_fallback[3]; - if (r_no == NULL) { - r_no = no_fallback; - } - return transform_snap_context_project_ray_impl( sctx, params, @@ -1749,11 +2147,6 @@ static bool transform_snap_context_project_view3d_mixed_impl( float ray_depth = BVH_RAYCAST_DIST_MAX; bool is_hit = false; - float r_no_dummy[3]; - if (r_no == NULL) { - r_no = r_no_dummy; - } - const int elem_type[3] = {SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE, SCE_SNAP_MODE_FACE}; BLI_assert(snap_to_flag != 0); @@ -1780,21 +2173,6 @@ static bool transform_snap_context_project_view3d_mixed_impl( } /** - * From a threshold (maximum distance to snap in pixels) returns: - * - * - The *real* distance (3D) if you are in orthographic-view. - * - The *tangent* (view cone radius at distance 1.0) if you are in perspective-view. - */ -static float dist_px_to_dist3d_or_tangent(const ARegion *ar, const float dist_px) -{ - const RegionView3D *rv3d = ar->regiondata; - if (ar->winx >= ar->winy) - return 2 * (dist_px / ar->winx) / rv3d->winmat[0][0]; - else - return 2 * (dist_px / ar->winy) / rv3d->winmat[1][1]; -} - -/** * Convenience function for performing snapping. * * Given a 2D region value, snap to vert/edge/face. @@ -1830,55 +2208,37 @@ bool ED_transform_snap_object_project_view3d_ex( float *ray_depth, float r_loc[3], float r_no[3], int *r_index) { - float ray_start[3], ray_normal[3], ray_origin[3]; + float ray_origin[3], ray_start[3], ray_normal[3], depth_range[2], ray_end[3]; - float ray_depth_fallback; - if (ray_depth == NULL) { - ray_depth_fallback = BVH_RAYCAST_DIST_MAX; - ray_depth = &ray_depth_fallback; - } + const ARegion *ar = sctx->v3d_data.ar; + const RegionView3D *rv3d = ar->regiondata; - if (!ED_view3d_win_to_ray_ex( - sctx->v3d_data.ar, sctx->v3d_data.v3d, - mval, ray_origin, ray_normal, ray_start, true)) - { + ED_view3d_win_to_origin(ar, mval, ray_origin); + ED_view3d_win_to_vector(ar, mval, ray_normal); + + ED_view3d_clip_range_get( + sctx->v3d_data.v3d, sctx->v3d_data.ar->regiondata, + &depth_range[0], &depth_range[1], 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]); + + if (!ED_view3d_clip_segment(rv3d, ray_start, ray_end)) { return false; } - float radius, dist_to_ray_sq = 0.0f; - if (dist_px) { - radius = dist_px_to_dist3d_or_tangent(sctx->v3d_data.ar, *dist_px); - /** - * Workaround to use of cone (Instead of project the radius on view plane): - * In perspective view, the radius of the cone may decrease depending on the ray direction. - * This is more evident with small values of the `Viewport lens angle`. - * The threshold becomes distorted that way. - */ - RegionView3D *rv3d = sctx->v3d_data.ar->regiondata; - if (rv3d->is_persp) { - float view_dir[3]; - negate_v3_v3(view_dir, rv3d->viewinv[2]); - normalize_v3(view_dir); - radius *= dot_v3v3(ray_normal, view_dir); - } - - dist_to_ray_sq = SQUARE(radius); + float ray_depth_fallback; + if (ray_depth == NULL) { + ray_depth_fallback = BVH_RAYCAST_DIST_MAX; + ray_depth = &ray_depth_fallback; } - if (snapObjectsRay( + return snapObjectsRay( sctx, snap_to, params->snap_select, params->use_object_edit_cage, - ray_origin, ray_start, ray_normal, - ray_depth, &dist_to_ray_sq, - r_loc, r_no, r_index, NULL, NULL, NULL)) - { - if (dist_px) { - *dist_px *= sqrtf(dist_to_ray_sq) / radius; - } - return true; - } - - return false; + mval, ray_origin, ray_start, ray_normal, depth_range, + ray_depth, dist_px, + r_loc, r_no, r_index, NULL, NULL, NULL); } bool ED_transform_snap_object_project_view3d( |