From 19e5540ff7712a45ab4ced4a4a02b12ce02637a9 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 21 Apr 2016 11:29:32 +1000 Subject: Transform Snap: initial snap context refactor This introduces a snap-context that can be re-used for casting rays into the scene (by operators such as walk-mode, ruler and transform code). This can be used to cache data between calls too. --- .../blender/editors/armature/editarmature_sketch.c | 20 +- source/blender/editors/curve/editcurve.c | 21 +- source/blender/editors/include/ED_transform.h | 87 +- source/blender/editors/include/ED_view3d.h | 12 - source/blender/editors/mesh/editmesh_tools.c | 20 +- source/blender/editors/space_view3d/view3d_edit.c | 82 -- source/blender/editors/space_view3d/view3d_ruler.c | 62 +- source/blender/editors/space_view3d/view3d_walk.c | 42 +- source/blender/editors/transform/CMakeLists.txt | 1 + source/blender/editors/transform/transform.h | 10 + .../blender/editors/transform/transform_generics.c | 2 + source/blender/editors/transform/transform_snap.c | 914 +--------------- .../editors/transform/transform_snap_object.c | 1152 ++++++++++++++++++++ 13 files changed, 1375 insertions(+), 1050 deletions(-) create mode 100644 source/blender/editors/transform/transform_snap_object.c (limited to 'source/blender/editors') diff --git a/source/blender/editors/armature/editarmature_sketch.c b/source/blender/editors/armature/editarmature_sketch.c index 87d75aa8fad..b62714700fa 100644 --- a/source/blender/editors/armature/editarmature_sketch.c +++ b/source/blender/editors/armature/editarmature_sketch.c @@ -1086,9 +1086,23 @@ static int sk_getStrokeSnapPoint(bContext *C, SK_Point *pt, SK_Sketch *sketch, S mval[1] = dd->mval[1]; /* try to snap to closer object */ - found = snapObjectsContext( - C, mval, SNAP_NOT_SELECTED, - vec, no, &dist_px); + { + struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d( + CTX_data_main(C), CTX_data_scene(C), 0, + CTX_wm_region(C), CTX_wm_view3d(C)); + + found = ED_transform_snap_object_project_view3d_mixed( + snap_context, + &(const struct SnapObjectParams){ + .snap_select = SNAP_NOT_SELECTED, + .snap_to_flag = SCE_SELECT_FACE, + }, + mval, &dist_px, true, + vec, no); + + ED_transform_snap_object_context_destroy(snap_context); + } + if (found == 1) { pt->type = dd->type; pt->mode = PT_SNAP; diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 9df611b3216..395da2cd2f1 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -4993,11 +4993,22 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (use_proj) { const float mval[2] = {UNPACK2(event->mval)}; - float no_dummy[3]; - float dist_px_dummy; - snapObjectsContext( - C, mval, SNAP_NOT_OBEDIT, - location, no_dummy, &dist_px_dummy); + + struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d( + CTX_data_main(C), vc.scene, 0, + vc.ar, vc.v3d); + + ED_transform_snap_object_project_view3d_mixed( + snap_context, + &(const struct SnapObjectParams){ + .snap_select = SNAP_NOT_OBEDIT, + .snap_to_flag = SCE_SELECT_FACE, + }, + mval, NULL, true, + location, NULL); + + + ED_transform_snap_object_context_destroy(snap_context); } if ((cu->flag & CU_3D) == 0) { diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index db8085a6696..39df52e5f68 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -43,6 +43,8 @@ struct wmEvent; struct wmKeyConfig; struct wmKeyMap; struct wmOperatorType; +struct Main; +struct SnapObjectContext; void transform_keymap_for_space(struct wmKeyConfig *keyconf, struct wmKeyMap *keymap, int spaceid); void transform_operatortypes(void); @@ -187,27 +189,9 @@ bool peelObjectsContext( struct ListBase *r_depth_peels); bool snapObjectsTransform( struct TransInfo *t, const float mval[2], SnapSelect snap_select, + float *dist_px, /* return args */ - float r_loc[3], float r_no[3], float *r_dist_px); -bool snapObjectsContext( - struct bContext *C, const float mval[2], SnapSelect snap_select, - /* return args */ - float r_loc[3], float r_no[3], float *r_dist_px); -/* taks args for all settings */ -bool snapObjectsEx( - struct Scene *scene, struct View3D *v3d, struct ARegion *ar, struct Base *base_act, struct Object *obedit, - const float mval[2], SnapSelect snap_select, const short snap_mode, - float *ray_depth, - /* return args */ - float r_loc[3], float r_no[3], float *r_dist_px); -bool snapObjectsRayEx( - struct Scene *scene, struct View3D *v3d, struct ARegion *ar, struct Base *base_act, struct Object *obedit, - const float mval[2], SnapSelect snap_select, const short snap_mode, - const float ray_start[3], const float ray_normal[3], float *ray_depth, - /* return args */ - float r_loc[3], float r_no[3], float *r_dist_px, int *r_index, - struct Object **r_ob, float r_obmat[4][4]); - + float r_loc[3], float r_no[3]); bool snapNodesTransform( struct TransInfo *t, const int mval[2], SnapSelect snap_select, /* return args */ @@ -217,4 +201,67 @@ bool snapNodesContext( /* return args */ float r_loc[2], float *r_dist_px, char *r_node_border); + +/* transform_snap_object.c */ + +/* ED_transform_snap_object_*** API */ +struct SnapObjectParams { + SnapSelect snap_select; + union { + unsigned int snap_to : 4; + /* snap_target_flag: Snap to vert/edge/face. */ + unsigned int snap_to_flag : 4; + }; + /* use editmode cage */ + unsigned int use_object_edit : 1; + /* special context sensitive handling for the active object */ + unsigned int use_object_active : 1; +}; + +enum { + SNAP_OBJECT_USE_CACHE = (1 << 0), +}; + +typedef struct SnapObjectContext SnapObjectContext; +SnapObjectContext *ED_transform_snap_object_context_create( + struct Main *bmain, struct Scene *scene, int flag); +SnapObjectContext *ED_transform_snap_object_context_create_view3d( + struct Main *bmain, struct Scene *scene, int flag, + /* extra args for view3d */ + struct ARegion *ar, struct View3D *v3d); +void ED_transform_snap_object_context_destroy(SnapObjectContext *sctx); + +bool ED_transform_snap_object_project_ray_ex( + struct SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float ray_start[3], const float ray_normal[3], float *ray_depth, + /* return args */ + float r_loc[3], float r_no[3], int *r_index, + struct Object **r_ob, float r_obmat[4][4]); +bool ED_transform_snap_object_project_ray( + SnapObjectContext *sctx, + const float ray_origin[3], const float ray_direction[3], float *ray_dist, + float r_co[3], float r_no[3]); + +bool ED_transform_snap_object_project_view3d_ex( + struct SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float mval[2], float *dist_px, + float *ray_depth, + float r_loc[3], float r_no[3], int *r_index); +bool ED_transform_snap_object_project_view3d( + struct SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float mval[2], float *dist_px, + float *ray_depth, + /* return args */ + float r_loc[3], float r_no[3]); +bool ED_transform_snap_object_project_view3d_mixed( + SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float mval_fl[2], float *dist_px, + bool use_depth, + float r_co[3], float r_no[3]); + + #endif /* __ED_TRANSFORM_H__ */ diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index d8a3f5e75f5..c23c2eed890 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -396,18 +396,6 @@ void ED_view3d_operator_properties_viewmat_set(struct bContext *C, struct wmOper void ED_view3d_operator_properties_viewmat_get(struct wmOperator *op, int *winx, int *winy, float persmat[4][4]); #endif -bool ED_view3d_snap_from_region( - struct Scene *scene, struct View3D *v3d, struct ARegion *ar, - const float mval[2], float dist_px, - bool use_depth, bool use_obedit, - bool use_vert, bool use_edge, bool use_face, - float r_co[3], float r_no[3]); - -bool ED_view3d_snap_from_ray( - struct Scene *scene, - const float ray_start[3], const float ray_normal[3], - float r_co[3]); - /* render */ void ED_view3d_stop_render_preview(struct wmWindowManager *wm, struct ARegion *ar); void ED_view3d_shade_update(struct Main *bmain, struct Scene *scene, struct View3D *v3d, struct ScrArea *sa); diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index ee33f5f1655..c06bf7ba68e 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -301,20 +301,30 @@ void EMBM_project_snap_verts(bContext *C, ARegion *ar, BMEditMesh *em) ED_view3d_init_mats_rv3d(obedit, ar->regiondata); + struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d( + CTX_data_main(C), CTX_data_scene(C), SNAP_OBJECT_USE_CACHE, + ar, CTX_wm_view3d(C)); + BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - float mval[2], co_proj[3], no_dummy[3]; - float dist_px_dummy; + float mval[2], co_proj[3]; if (ED_view3d_project_float_object(ar, eve->co, mval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - if (snapObjectsContext( - C, mval, SNAP_NOT_OBEDIT, - co_proj, no_dummy, &dist_px_dummy)) + if (ED_transform_snap_object_project_view3d_mixed( + snap_context, + &(const struct SnapObjectParams){ + .snap_select = SNAP_NOT_OBEDIT, + .snap_to_flag = SCE_SELECT_FACE, + }, + mval, NULL, true, + co_proj, NULL)) { mul_v3_m4v3(eve->co, obedit->imat, co_proj); } } } } + + ED_transform_snap_object_context_destroy(snap_context); } diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 7cd20401a53..8ca7331fb37 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -5171,85 +5171,3 @@ void ED_view3D_lock_clear(View3D *v3d) v3d->ob_centre_cursor = false; v3d->flag2 &= ~V3D_LOCK_CAMERA; } - -/** - * Convenience function for snap ray-casting. - * - * Given a ray, cast it into the scene (snapping to faces). - * - * \return Snap success - */ -bool ED_view3d_snap_from_ray( - Scene *scene, - const float ray_start[3], const float ray_normal[3], - float r_co[3]) -{ - float r_no_dummy[3]; - float ray_dist = BVH_RAYCAST_DIST_MAX; - bool ret; - - struct Object *obedit = scene->obedit; - - /* try snap edge, then face if it fails */ - ret = snapObjectsRayEx( - scene, NULL, NULL, NULL, obedit, - NULL, SNAP_ALL, SCE_SNAP_MODE_FACE, - ray_start, ray_normal, &ray_dist, - r_co, r_no_dummy, NULL, NULL, - NULL, NULL); - - return ret; -} - -/** - * Convenience function for performing snapping. - * - * Given a 2D region value, snap to vert/edge/face. - * - * \param mval: 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 use_obedit: Use editmode cage. - * \param use_vert: Snap to verts. - * \param use_edge: Snap to edges. - * \param use_face: Snap to faces. - * \param r_co: hit location. - * \param r_no: hit normal (optional). - * \return Snap success - */ -bool ED_view3d_snap_from_region( - Scene *scene, View3D *v3d, ARegion *ar, - const float mval[2], float dist_px, - bool use_depth, bool use_obedit, - bool use_vert, bool use_edge, bool use_face, - float r_co[3], float r_no[3]) -{ - float r_no_dummy[3]; - float ray_dist = BVH_RAYCAST_DIST_MAX; - bool is_hit = false; - float *r_no_ptr = r_no ? r_no : r_no_dummy; - - struct Object *obedit = use_obedit ? scene->obedit : NULL; - const int elem_type[3] = {SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE, SCE_SNAP_MODE_FACE}; - const bool elem_test[3] = {use_vert, use_edge, use_face}; - - BLI_assert(use_vert || use_edge || use_face); - - for (int i = 0; i < 3; i++) { - if (elem_test[i] && (is_hit == false || use_depth)) { - if (use_depth == false) { - ray_dist = BVH_RAYCAST_DIST_MAX; - } - if (snapObjectsEx( - scene, v3d, ar, NULL, obedit, - mval, SNAP_ALL, elem_type[i], - &ray_dist, - r_co, r_no_ptr, &dist_px)) - { - is_hit = true; - } - } - } - - return is_hit; -} diff --git a/source/blender/editors/space_view3d/view3d_ruler.c b/source/blender/editors/space_view3d/view3d_ruler.c index 198cc3e5703..26eb707624a 100644 --- a/source/blender/editors/space_view3d/view3d_ruler.c +++ b/source/blender/editors/space_view3d/view3d_ruler.c @@ -46,6 +46,7 @@ #include "ED_screen.h" #include "ED_view3d.h" +#include "ED_transform.h" #include "ED_space_api.h" #include "BLF_api.h" @@ -109,6 +110,8 @@ typedef struct RulerInfo { int state; float drag_start_co[3]; + struct SnapObjectContext *snap_context; + /* wm state */ wmWindow *win; ScrArea *sa; @@ -128,6 +131,7 @@ static RulerItem *ruler_item_add(RulerInfo *ruler_info) static void ruler_item_remove(RulerInfo *ruler_info, RulerItem *ruler_item) { BLI_remlink(&ruler_info->items, ruler_item); + MEM_freeN(ruler_item); } @@ -632,6 +636,9 @@ static void view3d_ruler_end(const struct bContext *UNUSED(C), RulerInfo *ruler_ static void view3d_ruler_free(RulerInfo *ruler_info) { BLI_freelistN(&ruler_info->items); + + ED_transform_snap_object_context_destroy(ruler_info->snap_context); + MEM_freeN(ruler_info); } @@ -642,11 +649,12 @@ static void view3d_ruler_item_project(RulerInfo *ruler_info, float r_co[3], } /* use for mousemove events */ -static bool view3d_ruler_item_mousemove(bContext *C, RulerInfo *ruler_info, const int mval[2], - const bool do_thickness, const bool do_snap) +static bool view3d_ruler_item_mousemove( + RulerInfo *ruler_info, const int mval[2], + const bool do_thickness, const bool do_snap) { const float eps_bias = 0.0002f; - const float dist_px = MVAL_MAX_PX_DIST * U.pixelsize; /* snap dist */ + float dist_px = MVAL_MAX_PX_DIST * U.pixelsize; /* snap dist */ RulerItem *ruler_item = ruler_item_active_get(ruler_info); ruler_info->snap_flag &= ~RULER_SNAP_OK; @@ -657,8 +665,8 @@ static bool view3d_ruler_item_mousemove(bContext *C, RulerInfo *ruler_info, cons copy_v3_v3(co, ruler_info->drag_start_co); view3d_ruler_item_project(ruler_info, co, mval); if (do_thickness && ruler_item->co_index != 1) { - Scene *scene = CTX_data_scene(C); - View3D *v3d = ruler_info->sa->spacedata.first; + // Scene *scene = CTX_data_scene(C); + // View3D *v3d = ruler_info->sa->spacedata.first; const float mval_fl[2] = {UNPACK2(mval)}; float ray_normal[3]; float ray_start[3]; @@ -666,33 +674,37 @@ static bool view3d_ruler_item_mousemove(bContext *C, RulerInfo *ruler_info, cons co_other = ruler_item->co[ruler_item->co_index == 0 ? 2 : 0]; - if (ED_view3d_snap_from_region( - scene, v3d, ruler_info->ar, - mval_fl, dist_px, - true, false, - false, false, true, + if (ED_transform_snap_object_project_view3d_mixed( + ruler_info->snap_context, + &(const struct SnapObjectParams){ + .snap_select = SNAP_ALL, + .snap_to_flag = SCE_SELECT_FACE, + }, + mval_fl, &dist_px, true, co, ray_normal)) { negate_v3(ray_normal); /* add some bias */ madd_v3_v3v3fl(ray_start, co, ray_normal, eps_bias); - ED_view3d_snap_from_ray( - scene, - ray_start, ray_normal, - co_other); + ED_transform_snap_object_project_ray( + ruler_info->snap_context, + ray_start, ray_normal, NULL, + co_other, NULL); } } else if (do_snap) { - Scene *scene = CTX_data_scene(C); + // Scene *scene = CTX_data_scene(C); View3D *v3d = ruler_info->sa->spacedata.first; const float mval_fl[2] = {UNPACK2(mval)}; bool use_depth = (v3d->drawtype >= OB_SOLID); - if (ED_view3d_snap_from_region( - scene, v3d, ruler_info->ar, - mval_fl, dist_px, - use_depth, false, - true, true, use_depth, + if (ED_transform_snap_object_project_view3d_mixed( + ruler_info->snap_context, + &(const struct SnapObjectParams){ + .snap_select = SNAP_ALL, + .snap_to_flag = (SCE_SELECT_VERTEX | SCE_SELECT_EDGE) | (use_depth ? SCE_SELECT_FACE : 0), + }, + mval_fl, &dist_px, use_depth, co, NULL)) { ruler_info->snap_flag |= RULER_SNAP_OK; @@ -736,6 +748,10 @@ static int view3d_ruler_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE op->customdata = ruler_info; + ruler_info->snap_context = ED_transform_snap_object_context_create_view3d( + CTX_data_main(C), CTX_data_scene(C), SNAP_OBJECT_USE_CACHE, + ar, CTX_wm_view3d(C)); + ruler_info->win = win; ruler_info->sa = sa; ruler_info->draw_handle_pixel = ED_region_draw_cb_activate(ar->type, ruler_info_draw_pixel, @@ -818,7 +834,7 @@ static int view3d_ruler_modal(bContext *C, wmOperator *op, const wmEvent *event) if (use_depth) { /* snap the first point added, not essential but handy */ ruler_item->co_index = 0; - view3d_ruler_item_mousemove(C, ruler_info, event->mval, false, true); + view3d_ruler_item_mousemove(ruler_info, event->mval, false, true); copy_v3_v3(ruler_info->drag_start_co, ruler_item->co[ruler_item->co_index]); } else { @@ -871,7 +887,7 @@ static int view3d_ruler_modal(bContext *C, wmOperator *op, const wmEvent *event) } /* update the new location */ - view3d_ruler_item_mousemove(C, ruler_info, event->mval, + view3d_ruler_item_mousemove(ruler_info, event->mval, event->shift != 0, event->ctrl != 0); do_draw = true; } @@ -920,7 +936,7 @@ static int view3d_ruler_modal(bContext *C, wmOperator *op, const wmEvent *event) case MOUSEMOVE: { if (ruler_info->state == RULER_STATE_DRAG) { - if (view3d_ruler_item_mousemove(C, ruler_info, event->mval, + if (view3d_ruler_item_mousemove(ruler_info, event->mval, event->shift != 0, event->ctrl != 0)) { do_draw = true; diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c index 5fb7b2994ed..73ec0f664da 100644 --- a/source/blender/editors/space_view3d/view3d_walk.c +++ b/source/blender/editors/space_view3d/view3d_walk.c @@ -306,6 +306,8 @@ typedef struct WalkInfo { float jump_height; /* maximum jump height */ float speed_factor; /* to use for fast/slow speeds */ + struct SnapObjectContext *snap_context; + struct View3DCameraControl *v3d_camera_control; } WalkInfo; @@ -402,12 +404,14 @@ static void walk_navigation_mode_set(bContext *C, wmOperator *op, WalkInfo *walk /** * \param r_distance Distance to the hit point */ -static bool walk_floor_distance_get(bContext *C, RegionView3D *rv3d, WalkInfo *walk, const float dvec[3], float *r_distance) +static bool walk_floor_distance_get( + RegionView3D *rv3d, WalkInfo *walk, const float dvec[3], + float *r_distance) { float ray_normal[3] = {0, 0, -1}; /* down */ float ray_start[3]; float r_location[3]; - float r_normal[3]; + float r_normal_dummy[3]; float dvec_tmp[3]; bool ret; @@ -418,12 +422,10 @@ static bool walk_floor_distance_get(bContext *C, RegionView3D *rv3d, WalkInfo *w mul_v3_v3fl(dvec_tmp, dvec, walk->grid); add_v3_v3(ray_start, dvec_tmp); - ret = snapObjectsRayEx( - CTX_data_scene(C), NULL, NULL, NULL, NULL, - NULL, SNAP_ALL, SCE_SNAP_MODE_FACE, + ret = ED_transform_snap_object_project_ray( + walk->snap_context, ray_start, ray_normal, r_distance, - r_location, r_normal, NULL, NULL, - NULL, NULL); + r_location, r_normal_dummy); /* artifically scale the distance to the scene size */ *r_distance /= walk->grid; @@ -435,7 +437,9 @@ static bool walk_floor_distance_get(bContext *C, RegionView3D *rv3d, WalkInfo *w * \param r_location Location of the hit point * \param r_normal Normal of the hit surface, transformed to always face the camera */ -static bool walk_ray_cast(bContext *C, RegionView3D *rv3d, WalkInfo *walk, float r_location[3], float r_normal[3], float *ray_distance) +static bool walk_ray_cast( + RegionView3D *rv3d, WalkInfo *walk, + float r_location[3], float r_normal[3], float *ray_distance) { float ray_normal[3] = {0, 0, -1}; /* forward */ float ray_start[3]; @@ -449,13 +453,10 @@ static bool walk_ray_cast(bContext *C, RegionView3D *rv3d, WalkInfo *walk, float normalize_v3(ray_normal); - ret = snapObjectsRayEx( - CTX_data_scene(C), NULL, NULL, NULL, NULL, - NULL, SNAP_ALL, SCE_SNAP_MODE_FACE, - ray_start, ray_normal, ray_distance, - r_location, r_normal, NULL, NULL, - NULL, NULL); - + ret = ED_transform_snap_object_project_ray( + walk->snap_context, + ray_start, ray_normal, NULL, + r_location, r_normal); /* dot is positive if both rays are facing the same direction */ if (dot_v3v3(ray_normal, r_normal) > 0) { @@ -572,6 +573,9 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op) walk->rv3d->rflag |= RV3D_NAVIGATING; + walk->snap_context = ED_transform_snap_object_context_create_view3d( + CTX_data_main(C), walk->scene, SNAP_OBJECT_USE_CACHE, + walk->ar, walk->v3d); walk->v3d_camera_control = ED_view3d_cameracontrol_acquire( walk->scene, walk->v3d, walk->rv3d, @@ -622,6 +626,8 @@ static int walkEnd(bContext *C, WalkInfo *walk) ED_region_draw_cb_exit(walk->ar->type, walk->draw_handle_pixel); + ED_transform_snap_object_context_destroy(walk->snap_context); + ED_view3d_cameracontrol_release(walk->v3d_camera_control, walk->state == WALK_CANCEL); rv3d->rflag &= ~RV3D_NAVIGATING; @@ -894,7 +900,7 @@ static void walkEvent(bContext *C, wmOperator *op, WalkInfo *walk, const wmEvent { float loc[3], nor[3]; float distance; - bool ret = walk_ray_cast(C, walk->rv3d, walk, loc, nor, &distance); + bool ret = walk_ray_cast(walk->rv3d, walk, loc, nor, &distance); /* in case we are teleporting middle way from a jump */ walk->speed_jump = 0.0f; @@ -1175,7 +1181,7 @@ static int walkApply(bContext *C, wmOperator *op, WalkInfo *walk) float difference = -100.0f; float fall_distance; - ret = walk_floor_distance_get(C, rv3d, walk, dvec, &ray_distance); + ret = walk_floor_distance_get(rv3d, walk, dvec, &ray_distance); if (ret) { difference = walk->view_height - ray_distance; @@ -1228,7 +1234,7 @@ static int walkApply(bContext *C, wmOperator *op, WalkInfo *walk) if (t > walk->teleport.duration) { /* check to see if we are landing */ - ret = walk_floor_distance_get(C, rv3d, walk, dvec, &ray_distance); + ret = walk_floor_distance_get(rv3d, walk, dvec, &ray_distance); if (ret) { difference = walk->view_height - ray_distance; diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt index f3047c088a9..b7de49d8158 100644 --- a/source/blender/editors/transform/CMakeLists.txt +++ b/source/blender/editors/transform/CMakeLists.txt @@ -49,6 +49,7 @@ set(SRC transform_ops.c transform_orientations.c transform_snap.c + transform_snap_object.c transform.h ) diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index c156e9ecec0..5858210cd4f 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -101,6 +101,11 @@ typedef struct TransSnap { * where the smallest absolute value defines whats closest. */ float (*distance)(struct TransInfo *, const float p1[3], const float p2[3]); + + /** + * Re-usable snap context data. + */ + SnapObjectContext *object_context; } TransSnap; typedef struct TransCon { @@ -677,6 +682,7 @@ bool activeSnap(TransInfo *t); bool validSnap(TransInfo *t); void initSnapping(struct TransInfo *t, struct wmOperator *op); +void freeSnapping(struct TransInfo *t); void applyProject(TransInfo *t); void applyGridAbsolute(TransInfo *t); void applySnapping(TransInfo *t, float *vec); @@ -786,4 +792,8 @@ void projectVertSlideData(TransInfo *t, bool is_final); /* TODO. transform_queries.c */ bool checkUseAxisMatrix(TransInfo *t); +#define TRANSFORM_DIST_MAX_PX 1000.0f +#define TRANSFORM_SNAP_MAX_PX 100.0f +#define TRANSFORM_DIST_INVALID -FLT_MAX + #endif diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index eb6308d1c41..e23dd99fbcc 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -1502,6 +1502,8 @@ void postTrans(bContext *C, TransInfo *t) if (t->mouse.data) { MEM_freeN(t->mouse.data); } + + freeSnapping(t); } void applyTransObjects(TransInfo *t) diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index bb9120c337b..9e8454bf6fb 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -36,8 +36,6 @@ #include "PIL_time.h" -#include "DNA_armature_types.h" -#include "DNA_curve_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" #include "DNA_meshdata_types.h" /* Temporary, for snapping to other unselected meshes */ @@ -61,13 +59,11 @@ #include "BKE_editmesh.h" #include "BKE_sequencer.h" #include "BKE_main.h" -#include "BKE_tracking.h" #include "RNA_access.h" #include "WM_types.h" -#include "ED_armature.h" #include "ED_image.h" #include "ED_node.h" #include "ED_uvedit.h" @@ -83,10 +79,6 @@ /* this should be passed as an arg for use in snap functions */ #undef BASACT -#define TRANSFORM_DIST_MAX_PX 1000.0f -#define TRANSFORM_SNAP_MAX_PX 100.0f -#define TRANSFORM_DIST_INVALID -FLT_MAX - /* use half of flt-max so we can scale up without an exception */ /********************* PROTOTYPES ***********************/ @@ -331,8 +323,8 @@ void applyProject(TransInfo *t) if (ED_view3d_project_float_global(t->ar, iloc, mval_fl, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { if (snapObjectsTransform( - t, mval_fl, t->tsnap.modeSelect, - loc, no, &dist_px)) + t, mval_fl, t->tsnap.modeSelect, &dist_px, + loc, no)) { // if (t->flag & (T_EDIT|T_POSE)) { // mul_m4_v3(imat, loc); @@ -573,6 +565,14 @@ static void initSnappingMode(TransInfo *t) /* Always grid outside of 3D view */ t->tsnap.mode = SCE_SNAP_MODE_INCREMENT; } + + if (t->flag & (T_OBJECT | T_EDIT)) { + if (t->spacetype == SPACE_VIEW3D) { + t->tsnap.object_context = ED_transform_snap_object_context_create_view3d( + G.main, t->scene, SNAP_OBJECT_USE_CACHE, + t->ar, t->view); + } + } } void initSnapping(TransInfo *t, wmOperator *op) @@ -636,6 +636,14 @@ void initSnapping(TransInfo *t, wmOperator *op) initSnappingMode(t); } +void freeSnapping(TransInfo *t) +{ + if (t->tsnap.object_context) { + ED_transform_snap_object_context_destroy(t->tsnap.object_context); + t->tsnap.object_context = NULL; + } +} + static void setSnappingCallback(TransInfo *t) { t->tsnap.calcSnap = CalcSnapGeometry; @@ -1002,8 +1010,8 @@ static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec)) else { zero_v3(no); /* objects won't set this */ found = snapObjectsTransform( - t, mval, t->tsnap.modeSelect, - loc, no, &dist_px); + t, mval, t->tsnap.modeSelect, &dist_px, + loc, no); } if (found == true) { @@ -1237,884 +1245,26 @@ static void TargetSnapClosest(TransInfo *t) } } -static bool snapEdge( - ARegion *ar, const float v1co[3], const short v1no[3], const float v2co[3], const short v2no[3], - float obmat[4][4], float timat[3][3], const float mval_fl[2], - const float ray_start[3], const float ray_start_local[3], const float ray_normal_local[3], float *ray_depth, - float r_loc[3], float r_no[3], float *r_dist_px) -{ - float intersect[3] = {0, 0, 0}, ray_end[3], dvec[3]; - int result; - bool retval = false; - - copy_v3_v3(ray_end, ray_normal_local); - mul_v3_fl(ray_end, 2000); - add_v3_v3v3(ray_end, ray_start_local, ray_end); - - result = isect_line_line_v3(v1co, v2co, ray_start_local, ray_end, intersect, dvec); /* dvec used but we don't care about result */ - - if (result) { - float edge_loc[3], vec[3]; - float mul; - - /* check for behind ray_start */ - sub_v3_v3v3(dvec, intersect, ray_start_local); - - sub_v3_v3v3(edge_loc, v1co, v2co); - sub_v3_v3v3(vec, intersect, v2co); - - mul = dot_v3v3(vec, edge_loc) / dot_v3v3(edge_loc, edge_loc); - - if (mul > 1) { - mul = 1; - copy_v3_v3(intersect, v1co); - } - else if (mul < 0) { - mul = 0; - copy_v3_v3(intersect, v2co); - } - - if (dot_v3v3(ray_normal_local, dvec) > 0) { - float location[3]; - float new_depth; - float screen_loc[2]; - float new_dist; - - copy_v3_v3(location, intersect); - - mul_m4_v3(obmat, location); - - new_depth = len_v3v3(location, ray_start); - - if (ED_view3d_project_float_global(ar, location, screen_loc, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - new_dist = len_manhattan_v2v2(mval_fl, screen_loc); - } - else { - new_dist = TRANSFORM_DIST_MAX_PX; - } - - /* 10% threshold if edge is closer but a bit further - * this takes care of series of connected edges a bit slanted w.r.t the viewport - * otherwise, it would stick to the verts of the closest edge and not slide along merrily - * */ - if (new_dist <= *r_dist_px && new_depth < *ray_depth * 1.001f) { - float n1[3], n2[3]; - - *ray_depth = new_depth; - retval = true; - - sub_v3_v3v3(edge_loc, v1co, v2co); - sub_v3_v3v3(vec, intersect, v2co); - - mul = dot_v3v3(vec, edge_loc) / dot_v3v3(edge_loc, edge_loc); - - if (r_no) { - normal_short_to_float_v3(n1, v1no); - normal_short_to_float_v3(n2, v2no); - interp_v3_v3v3(r_no, n2, n1, mul); - mul_m3_v3(timat, r_no); - normalize_v3(r_no); - } - - copy_v3_v3(r_loc, location); - - *r_dist_px = new_dist; - } - } - } - - return retval; -} - -static bool snapVertex( - ARegion *ar, const float vco[3], const short vno[3], - float obmat[4][4], float timat[3][3], const float mval_fl[2], - const float ray_start[3], const float ray_start_local[3], const float ray_normal_local[3], float *ray_depth, - float r_loc[3], float r_no[3], float *r_dist_px) -{ - bool retval = false; - float dvec[3]; - - sub_v3_v3v3(dvec, vco, ray_start_local); - - if (dot_v3v3(ray_normal_local, dvec) > 0) { - float location[3]; - float new_depth; - float screen_loc[2]; - float new_dist; - - copy_v3_v3(location, vco); - - mul_m4_v3(obmat, location); - - new_depth = len_v3v3(location, ray_start); - - if (ED_view3d_project_float_global(ar, location, screen_loc, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - new_dist = len_manhattan_v2v2(mval_fl, screen_loc); - } - else { - new_dist = TRANSFORM_DIST_MAX_PX; - } - - - if (new_dist <= *r_dist_px && new_depth < *ray_depth) { - *ray_depth = new_depth; - retval = true; - - copy_v3_v3(r_loc, location); - - if (r_no) { - normal_short_to_float_v3(r_no, vno); - mul_m3_v3(timat, r_no); - normalize_v3(r_no); - } - - *r_dist_px = new_dist; - } - } - - return retval; -} - -static bool snapArmature( - ARegion *ar, Object *ob, bArmature *arm, float obmat[4][4], - const float mval[2], const short snap_to, - const float ray_start[3], const float ray_normal[3], float *ray_depth, - float r_loc[3], float *UNUSED(r_no), float *r_dist_px) -{ - float imat[4][4]; - float ray_start_local[3], ray_normal_local[3]; - bool retval = false; - - invert_m4_m4(imat, obmat); - - mul_v3_m4v3(ray_start_local, imat, ray_start); - mul_v3_mat3_m4v3(ray_normal_local, imat, ray_normal); - - if (arm->edbo) { - EditBone *eBone; - - for (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 |= snapVertex(ar, eBone->head, NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - retval |= snapVertex(ar, eBone->tail, NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - break; - case SCE_SNAP_MODE_EDGE: - retval |= snapEdge(ar, eBone->head, NULL, eBone->tail, NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - break; - } - } - } - } - } - 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; - /* skip hidden bones */ - if (bone && !(bone->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG))) { - const float *head_vec = pchan->pose_head; - const float *tail_vec = pchan->pose_tail; - - switch (snap_to) { - case SCE_SNAP_MODE_VERTEX: - retval |= snapVertex(ar, head_vec, NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - retval |= snapVertex(ar, tail_vec, NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - break; - case SCE_SNAP_MODE_EDGE: - retval |= snapEdge(ar, head_vec, NULL, tail_vec, NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - break; - } - } - } - } - - return retval; -} - -static bool snapCurve( - ARegion *ar, Object *ob, Curve *cu, float obmat[4][4], - const float mval[2], const short snap_to, - const float ray_start[3], const float ray_normal[3], float *ray_depth, - float r_loc[3], float *UNUSED(r_no), float *r_dist_px) -{ - float imat[4][4]; - float ray_start_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_start_local, ray_start); - copy_v3_v3(ray_normal_local, ray_normal); - - mul_m4_v3(imat, ray_start_local); - mul_mat3_m4_v3(imat, ray_normal_local); - - 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++) { - switch (snap_to) { - case SCE_SNAP_MODE_VERTEX: - { - if (ob->mode == OB_MODE_EDIT) { - if (nu->bezt) { - /* don't snap to selected (moving) or hidden */ - if (nu->bezt[u].f2 & SELECT || nu->bezt[u].hide != 0) { - break; - } - retval |= snapVertex(ar, nu->bezt[u].vec[1], NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - /* 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 |= snapVertex(ar, nu->bezt[u].vec[0], NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - } - if (!(nu->bezt[u].f3 & SELECT) && !(nu->bezt[u].h2 & HD_ALIGN && nu->bezt[u].f1 & SELECT)) { - retval |= snapVertex(ar, nu->bezt[u].vec[2], NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - } - } - else { - /* don't snap to selected (moving) or hidden */ - if (nu->bp[u].f1 & SELECT || nu->bp[u].hide != 0) { - break; - } - retval |= snapVertex(ar, nu->bp[u].vec, NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - } - } - else { - /* curve is not visible outside editmode if nurb length less than two */ - if (nu->pntsu > 1) { - if (nu->bezt) { - retval |= snapVertex(ar, nu->bezt[u].vec[1], NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - } - else { - retval |= snapVertex(ar, nu->bp[u].vec, NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - } - } - } - break; - } - default: - break; - } - } - } - return retval; -} - -static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt) -{ - const int *index_mp_to_orig = dm->getPolyDataArray(dm, CD_ORIGINDEX); - return index_mp_to_orig ? index_mp_to_orig[lt->poly] : lt->poly; -} - -static bool snapDerivedMesh( - ARegion *ar, Object *ob, DerivedMesh *dm, BMEditMesh *em, float obmat[4][4], - const float mval[2], const short snap_to, bool do_bb, - const float ray_start[3], const float ray_normal[3], const float ray_origin[3], float *ray_depth, - float r_loc[3], float r_no[3], float *r_dist_px, int *r_index) -{ - bool retval = false; - int totvert = dm->getNumVerts(dm); - - if (totvert > 0) { - const bool do_ray_start_correction = ( - ELEM(snap_to, SCE_SNAP_MODE_FACE, SCE_SNAP_MODE_VERTEX) && - (ar && !((RegionView3D *)ar->regiondata)->is_persp)); - bool need_ray_start_correction_init = do_ray_start_correction; - - float imat[4][4]; - float timat[3][3]; /* transpose inverse matrix for normals */ - float ray_start_local[3], ray_normal_local[3]; - float local_scale, local_depth, len_diff; - - BVHTreeFromMesh treedata = {0}; - - invert_m4_m4(imat, obmat); - transpose_m3_m4(timat, imat); - - copy_v3_v3(ray_start_local, ray_start); - copy_v3_v3(ray_normal_local, ray_normal); - - mul_m4_v3(imat, ray_start_local); - mul_mat3_m4_v3(imat, ray_normal_local); - - /* local scale in normal direction */ - local_scale = normalize_v3(ray_normal_local); - local_depth = *ray_depth; - if (local_depth != BVH_RAYCAST_DIST_MAX) { - local_depth *= local_scale; - } - - if (do_bb) { - BoundBox *bb = BKE_object_boundbox_get(ob); - - if (bb) { - BoundBox bb_temp; - - /* We cannot aford a bbox with some null dimension, which may happen in some cases... - * Threshold is rather high, but seems to be needed to get good behavior, see T46099. */ - bb = BKE_boundbox_ensure_minimum_dimensions(bb, &bb_temp, 1e-1f); - - /* Exact value here is arbitrary (ideally we would scale in pixel-space based on 'r_dist_px'), - * scale up so we can snap against verts & edges on the boundbox, see T46816. */ - if (ELEM(snap_to, SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE)) { - BKE_boundbox_scale(&bb_temp, bb, 1.0f + 1e-1f); - bb = &bb_temp; - } - - /* was local_depth, see: T47838 */ - len_diff = BVH_RAYCAST_DIST_MAX; - - if (!BKE_boundbox_ray_hit_check(bb, ray_start_local, ray_normal_local, &len_diff)) { - return retval; - } - need_ray_start_correction_init = false; - } - } - - treedata.em_evil = em; - treedata.em_evil_all = false; - switch (snap_to) { - case SCE_SNAP_MODE_FACE: - bvhtree_from_mesh_looptri(&treedata, dm, 0.0f, 4, 6); - break; - case SCE_SNAP_MODE_VERTEX: - bvhtree_from_mesh_verts(&treedata, dm, 0.0f, 2, 6); - break; - } - - 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. - */ - BVHTreeNearest nearest; - - if (treedata.tree != NULL) { - 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) { - len_diff = sqrtf(nearest.dist_sq); - } - } - } - /* 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 (do_ray_start_correction) { - 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 - len_v3v3(ray_start_local, ray_org_local)); - local_depth -= len_diff; - } - else { - len_diff = 0.0f; - } - - switch (snap_to) { - case SCE_SNAP_MODE_FACE: - { - BVHTreeRayHit hit; - - hit.index = -1; - hit.dist = local_depth; - - if (treedata.tree && - BLI_bvhtree_ray_cast(treedata.tree, ray_start_local, ray_normal_local, 0.0f, - &hit, treedata.raycast_callback, &treedata) != -1) - { - hit.dist += len_diff; - hit.dist /= local_scale; - 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); - - retval = true; - - if (r_index) { - *r_index = dm_looptri_to_poly_index(dm, &treedata.looptri[hit.index]); - } - } - } - break; - } - case SCE_SNAP_MODE_VERTEX: - { - BVHTreeNearest nearest; - - nearest.index = -1; - nearest.dist_sq = local_depth * local_depth; - if (treedata.tree && - BLI_bvhtree_find_nearest_to_ray( - treedata.tree, ray_start_local, ray_normal_local, - &nearest, NULL, NULL) != -1) - { - const MVert *v = &treedata.vert[nearest.index]; - retval = snapVertex( - ar, v->co, v->no, obmat, timat, mval, - ray_start, ray_start_local, ray_normal_local, ray_depth, - r_loc, r_no, r_dist_px); - } - break; - } - case SCE_SNAP_MODE_EDGE: - { - MVert *verts = dm->getVertArray(dm); - MEdge *edges = dm->getEdgeArray(dm); - int totedge = dm->getNumEdges(dm); - const int *index_array = NULL; - int index = 0; - int i; - - if (em != NULL) { - index_array = dm->getEdgeDataArray(dm, CD_ORIGINDEX); - BM_mesh_elem_table_ensure(em->bm, BM_EDGE); - } - - for (i = 0; i < totedge; i++) { - MEdge *e = edges + i; - bool test = true; - - if (em != NULL) { - if (index_array) { - index = index_array[i]; - } - else { - index = i; - } - - if (index == ORIGINDEX_NONE) { - test = false; - } - else { - BMEdge *eed = BM_edge_at_index(em->bm, index); - - if (BM_elem_flag_test(eed, BM_ELEM_HIDDEN) || - BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) || - BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)) - { - test = false; - } - } - } - - if (test) { - retval |= snapEdge( - ar, verts[e->v1].co, verts[e->v1].no, verts[e->v2].co, verts[e->v2].no, obmat, timat, - mval, ray_start, ray_start_local, ray_normal_local, ray_depth, - r_loc, r_no, r_dist_px); - } - } - - break; - } - } - - free_bvhtree_from_mesh(&treedata); - } - - return retval; -} - -/* may extend later (for now just snaps to empty center) */ -static bool snapEmpty( - ARegion *ar, Object *ob, float obmat[4][4], - const float mval[2], const short snap_to, - const float ray_start[3], const float ray_normal[3], float *ray_depth, - float r_loc[3], float *UNUSED(r_no), float *r_dist_px) -{ - float imat[4][4]; - float ray_start_local[3], ray_normal_local[3]; - bool retval = false; - - if (ob->transflag & OB_DUPLI) { - return retval; - } - /* for now only vertex supported */ - if (snap_to != SCE_SNAP_MODE_VERTEX) { - return retval; - } - - invert_m4_m4(imat, obmat); - - mul_v3_m4v3(ray_start_local, imat, ray_start); - mul_v3_mat3_m4v3(ray_normal_local, imat, ray_normal); - - switch (snap_to) { - case SCE_SNAP_MODE_VERTEX: - { - const float zero_co[3] = {0.0f}; - retval |= snapVertex( - ar, zero_co, NULL, obmat, NULL, mval, - ray_start, ray_start_local, ray_normal_local, ray_depth, - r_loc, NULL, r_dist_px); - break; - } - default: - break; - } - - return retval; -} - -static bool snapCamera( - ARegion *ar, Scene *scene, Object *object, float obmat[4][4], - const float mval[2], const short snap_to, - const float ray_start[3], const float ray_normal[3], float *ray_depth, - float r_loc[3], float *UNUSED(r_no), float *r_dist_px) -{ - 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_start_local[3], ray_normal_local[3]; - - if (clip == NULL) { - return retval; - } - if (object->transflag & OB_DUPLI) { - return retval; - } - - tracking = &clip->tracking; - - BKE_tracking_get_camera_object_matrix(scene, object, orig_camera_mat); - - invert_m4_m4(orig_camera_imat, orig_camera_mat); - invert_m4_m4(imat, obmat); - - switch (snap_to) { - case SCE_SNAP_MODE_VERTEX: - { - MovieTrackingObject *tracking_object; - - for (tracking_object = tracking->objects.first; - tracking_object; - tracking_object = tracking_object->next) - { - ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object); - MovieTrackingTrack *track; - float reconstructed_camera_mat[4][4], - reconstructed_camera_imat[4][4]; - float (*vertex_obmat)[4]; - - copy_v3_v3(ray_start_local, ray_start); - 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); - - invert_m4_m4(reconstructed_camera_imat, reconstructed_camera_mat); - } - - for (track = tracksbase->first; track; track = track->next) { - float bundle_pos[3]; - - if ((track->flag & TRACK_HAS_BUNDLE) == 0) { - continue; - } - - copy_v3_v3(bundle_pos, track->bundle_pos); - if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { - mul_m4_v3(orig_camera_imat, ray_start_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_start_local); - mul_mat3_m4_v3(imat, ray_normal_local); - vertex_obmat = obmat; - } - - retval |= snapVertex( - ar, bundle_pos, NULL, vertex_obmat, NULL, mval, - ray_start, ray_start_local, ray_normal_local, ray_depth, - r_loc, NULL, r_dist_px); - } - } - - break; - } - default: - break; - } - - return retval; -} - -static bool snapObject( - Scene *scene, ARegion *ar, Object *ob, float obmat[4][4], bool use_obedit, - const float mval[2], const short snap_to, - const float ray_start[3], const float ray_normal[3], const float ray_origin[3], float *ray_depth, - /* return args */ - float r_loc[3], float r_no[3], float *r_dist_px, int *r_index, - Object **r_ob, float r_obmat[4][4]) -{ - bool retval = false; - - if (ob->type == OB_MESH) { - BMEditMesh *em; - DerivedMesh *dm; - bool do_bb = true; - - if (use_obedit) { - em = BKE_editmesh_from_object(ob); - dm = editbmesh_get_derived_cage(scene, ob, em, CD_MASK_BAREMESH); - do_bb = false; - } - else { - /* in this case we want the mesh from the editmesh, avoids stale data. see: T45978. - * still set the 'em' to NULL, since we only want the 'dm'. */ - em = BKE_editmesh_from_object(ob); - if (em) { - editbmesh_get_derived_cage_and_final(scene, ob, em, CD_MASK_BAREMESH, &dm); - } - else { - dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); - } - em = NULL; - } - - retval = snapDerivedMesh( - ar, ob, dm, em, obmat, mval, snap_to, do_bb, - ray_start, ray_normal, ray_origin, ray_depth, - r_loc, r_no, r_dist_px, r_index); - - dm->release(dm); - } - else if (ob->type == OB_ARMATURE) { - retval = snapArmature( - ar, ob, ob->data, obmat, mval, snap_to, - ray_start, ray_normal, ray_depth, - r_loc, r_no, r_dist_px); - } - else if (ob->type == OB_CURVE) { - retval = snapCurve( - ar, ob, ob->data, obmat, mval, snap_to, - ray_start, ray_normal, ray_depth, - r_loc, r_no, r_dist_px); - } - else if (ob->type == OB_EMPTY) { - retval = snapEmpty( - ar, ob, obmat, mval, snap_to, - ray_start, ray_normal, ray_depth, - r_loc, r_no, r_dist_px); - } - else if (ob->type == OB_CAMERA) { - retval = snapCamera( - ar, scene, ob, obmat, mval, snap_to, - ray_start, ray_normal, ray_depth, - r_loc, r_no, r_dist_px); - } - - if (retval) { - if (r_ob) { - *r_ob = ob; - copy_m4_m4(r_obmat, obmat); - } - } - - return retval; -} - -static bool snapObjectsRay( - Scene *scene, View3D *v3d, ARegion *ar, Base *base_act, Object *obedit, - const float mval[2], SnapSelect snap_select, const short snap_to, - const float ray_start[3], const float ray_normal[3], const float ray_origin[3], float *ray_depth, - /* return args */ - float r_loc[3], float r_no[3], float *r_dist_px, int *r_index, - Object **r_ob, float r_obmat[4][4]) -{ - Base *base; - bool retval = false; - - if (snap_select == SNAP_ALL && obedit) { - Object *ob = obedit; - - retval |= snapObject( - scene, ar, ob, ob->obmat, true, - mval, snap_to, - ray_start, ray_normal, ray_origin, ray_depth, - r_loc, r_no, r_dist_px, r_index, r_ob, r_obmat); - } - - /* Need an exception for particle edit because the base is flagged with BA_HAS_RECALC_DATA - * which makes the loop skip it, even the derived mesh will never change - * - * To solve that problem, we do it first as an exception. - * */ - base = base_act; - if (base && base->object && base->object->mode & OB_MODE_PARTICLE_EDIT) { - Object *ob = base->object; - retval |= snapObject( - scene, ar, ob, ob->obmat, false, - mval, snap_to, - ray_start, ray_normal, ray_origin, ray_depth, - r_loc, r_no, r_dist_px, r_index, r_ob, r_obmat); - } - - for (base = FIRSTBASE; base != NULL; base = base->next) { - if ((BASE_VISIBLE_BGMODE(v3d, scene, base)) && - (base->flag & (BA_HAS_RECALC_OB | BA_HAS_RECALC_DATA)) == 0 && - - ((snap_select == SNAP_NOT_SELECTED && (base->flag & (SELECT | BA_WAS_SEL)) == 0) || - (ELEM(snap_select, SNAP_ALL, SNAP_NOT_OBEDIT) && base != base_act))) - { - Object *ob = base->object; - Object *ob_snap = ob; - bool use_obedit = false; - - /* for linked objects, use the same object but a different matrix */ - if (obedit && ob->data == obedit->data) { - use_obedit = true; - ob_snap = obedit; - } - - if (ob->transflag & OB_DUPLI) { - DupliObject *dupli_ob; - ListBase *lb = object_duplilist(G.main->eval_ctx, scene, ob); - - for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) { - bool use_obedit_dupli = (obedit && dupli_ob->ob->data == obedit->data); - Object *dupli_snap = (use_obedit_dupli) ? obedit : dupli_ob->ob; - - retval |= snapObject( - scene, ar, dupli_snap, dupli_ob->mat, use_obedit_dupli, - mval, snap_to, - ray_start, ray_normal, ray_origin, ray_depth, - r_loc, r_no, r_dist_px, r_index, r_ob, r_obmat); - } - - free_object_duplilist(lb); - } - - retval |= snapObject( - scene, ar, ob_snap, ob->obmat, use_obedit, - mval, snap_to, - ray_start, ray_normal, ray_origin, ray_depth, - r_loc, r_no, r_dist_px, r_index, r_ob, r_obmat); - } - } - - return retval; -} -static bool snapObjects( - Scene *scene, View3D *v3d, ARegion *ar, Base *base_act, Object *obedit, - const float mval[2], SnapSelect snap_select, const short snap_to, - float *ray_depth, - float r_loc[3], float r_no[3], float *r_dist_px, int *r_index) -{ - float ray_start[3], ray_normal[3], ray_orgigin[3]; - - if (!ED_view3d_win_to_ray_ex(ar, v3d, mval, ray_orgigin, ray_normal, ray_start, true)) { - return false; - } - - return snapObjectsRay( - scene, v3d, ar, base_act, obedit, - mval, snap_select, snap_to, - ray_start, ray_normal, ray_orgigin, ray_depth, - r_loc, r_no, r_dist_px, r_index, NULL, NULL); -} - bool snapObjectsTransform( TransInfo *t, const float mval[2], SnapSelect snap_select, - float r_loc[3], float r_no[3], float *r_dist_px) + float *dist_px, + float r_loc[3], float r_no[3]) { float ray_dist = BVH_RAYCAST_DIST_MAX; - Object *obedit = NULL; - Base *base_act = NULL; - if (t->flag & T_EDIT) { - obedit = t->obedit; - } - - if ((t->options & CTX_GPENCIL_STROKES) == 0) { - base_act = t->scene->basact; - } - - return snapObjects( - t->scene, t->view, t->ar, base_act, obedit, - mval, snap_select, t->scene->toolsettings->snap_mode, - &ray_dist, - r_loc, r_no, r_dist_px, NULL); -} - -bool snapObjectsContext( - bContext *C, const float mval[2], SnapSelect snap_select, - float r_loc[3], float r_no[3], float *r_dist_px) -{ - ScrArea *sa = CTX_wm_area(C); - View3D *v3d = sa->spacedata.first; - Scene *scene = CTX_data_scene(C); - ARegion *ar = CTX_wm_region(C); - Object *obedit = CTX_data_edit_object(C); - float ray_dist = BVH_RAYCAST_DIST_MAX; - - return snapObjects( - scene, v3d, ar, scene->basact, obedit, - mval, snap_select, scene->toolsettings->snap_mode, + return ED_transform_snap_object_project_view3d_ex( + t->tsnap.object_context, + &(const struct SnapObjectParams){ + .snap_select = snap_select, + .snap_to = t->scene->toolsettings->snap_mode, + .use_object_edit = (t->flag & T_EDIT) != 0, + .use_object_active = (t->options & CTX_GPENCIL_STROKES) == 0, + }, + mval, dist_px, &ray_dist, - r_loc, r_no, r_dist_px, NULL); + r_loc, r_no, NULL); } -bool snapObjectsEx( - Scene *scene, View3D *v3d, ARegion *ar, Base *base_act, Object *obedit, - const float mval[2], SnapSelect snap_select, const short snap_to, - float *ray_depth, - float r_loc[3], float r_no[3], float *r_dist_px) -{ - return snapObjects( - scene, v3d, ar, base_act, obedit, - mval, snap_select, snap_to, - ray_depth, - r_loc, r_no, r_dist_px, NULL); -} -bool snapObjectsRayEx( - Scene *scene, View3D *v3d, ARegion *ar, Base *base_act, Object *obedit, - const float mval[2], SnapSelect snap_select, const short snap_to, - const float ray_start[3], const float ray_normal[3], float *ray_depth, - float r_loc[3], float r_no[3], float *r_dist_px, int *r_index, - Object **r_ob, float r_obmat[4][4]) -{ - return snapObjectsRay( - scene, v3d, ar, base_act, obedit, - mval, snap_select, snap_to, - ray_start, ray_normal, ray_start, ray_depth, - r_loc, r_no, r_dist_px, r_index, - r_ob, r_obmat); -} /******************** PEELING *********************************/ diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c new file mode 100644 index 00000000000..e16624c0a08 --- /dev/null +++ b/source/blender/editors/transform/transform_snap_object.c @@ -0,0 +1,1152 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/transform/transform_snap_object.c + * \ingroup edtransform + */ + +#include +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BLI_math.h" +#include "BLI_kdopbvh.h" +#include "BLI_utildefines.h" + +#include "DNA_armature_types.h" +#include "DNA_curve_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_screen_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_DerivedMesh.h" +#include "BKE_object.h" +#include "BKE_anim.h" /* for duplis */ +#include "BKE_editmesh.h" +#include "BKE_main.h" +#include "BKE_tracking.h" + +#include "ED_transform.h" +#include "ED_view3d.h" +#include "ED_armature.h" +#include "ED_view3d.h" + +#include "MEM_guardedalloc.h" + +#include "transform.h" + +typedef struct SnapObjectContext { + Main *bmain; + Scene *scene; + int flag; + + /* Optional: when performing screen-space projection. + * otherwise this doesn't take viewport into account. */ + bool use_v3d; + struct { + struct View3D *v3d; + struct ARegion *ar; + } v3d_data; + +} SnapObjectContext; + +/* -------------------------------------------------------------------- */ + +/** \name Internal Object Snapping API + * \{ */ + +static bool snapEdge( + ARegion *ar, const float v1co[3], const short v1no[3], const float v2co[3], const short v2no[3], + float obmat[4][4], float timat[3][3], const float mval_fl[2], float *dist_px, + const float ray_start[3], const float ray_start_local[3], const float ray_normal_local[3], float *ray_depth, + float r_loc[3], float r_no[3]) +{ + float intersect[3] = {0, 0, 0}, ray_end[3], dvec[3]; + int result; + bool retval = false; + + copy_v3_v3(ray_end, ray_normal_local); + mul_v3_fl(ray_end, 2000); + add_v3_v3v3(ray_end, ray_start_local, ray_end); + + /* dvec used but we don't care about result */ + result = isect_line_line_v3(v1co, v2co, ray_start_local, ray_end, intersect, dvec); + + if (result) { + float edge_loc[3], vec[3]; + float mul; + + /* check for behind ray_start */ + sub_v3_v3v3(dvec, intersect, ray_start_local); + + sub_v3_v3v3(edge_loc, v1co, v2co); + sub_v3_v3v3(vec, intersect, v2co); + + mul = dot_v3v3(vec, edge_loc) / dot_v3v3(edge_loc, edge_loc); + + if (mul > 1) { + mul = 1; + copy_v3_v3(intersect, v1co); + } + else if (mul < 0) { + mul = 0; + copy_v3_v3(intersect, v2co); + } + + if (dot_v3v3(ray_normal_local, dvec) > 0) { + float location[3]; + float new_depth; + float screen_loc[2]; + float new_dist; + + copy_v3_v3(location, intersect); + + mul_m4_v3(obmat, location); + + new_depth = len_v3v3(location, ray_start); + + if (ED_view3d_project_float_global(ar, location, screen_loc, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { + new_dist = len_manhattan_v2v2(mval_fl, screen_loc); + } + else { + new_dist = TRANSFORM_DIST_MAX_PX; + } + + /* 10% threshold if edge is closer but a bit further + * this takes care of series of connected edges a bit slanted w.r.t the viewport + * otherwise, it would stick to the verts of the closest edge and not slide along merrily + * */ + if (new_dist <= *dist_px && new_depth < *ray_depth * 1.001f) { + float n1[3], n2[3]; + + *ray_depth = new_depth; + retval = true; + + sub_v3_v3v3(edge_loc, v1co, v2co); + sub_v3_v3v3(vec, intersect, v2co); + + mul = dot_v3v3(vec, edge_loc) / dot_v3v3(edge_loc, edge_loc); + + if (r_no) { + normal_short_to_float_v3(n1, v1no); + normal_short_to_float_v3(n2, v2no); + interp_v3_v3v3(r_no, n2, n1, mul); + mul_m3_v3(timat, r_no); + normalize_v3(r_no); + } + + copy_v3_v3(r_loc, location); + + *dist_px = new_dist; + } + } + } + + return retval; +} + +static bool snapVertex( + ARegion *ar, const float vco[3], const short vno[3], + float obmat[4][4], float timat[3][3], const float mval_fl[2], float *dist_px, + const float ray_start[3], const float ray_start_local[3], const float ray_normal_local[3], float *ray_depth, + float r_loc[3], float r_no[3]) +{ + bool retval = false; + float dvec[3]; + + sub_v3_v3v3(dvec, vco, ray_start_local); + + if (dot_v3v3(ray_normal_local, dvec) > 0) { + float location[3]; + float new_depth; + float screen_loc[2]; + float new_dist; + + copy_v3_v3(location, vco); + + mul_m4_v3(obmat, location); + + new_depth = len_v3v3(location, ray_start); + + if (ED_view3d_project_float_global(ar, location, screen_loc, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { + new_dist = len_manhattan_v2v2(mval_fl, screen_loc); + } + else { + new_dist = TRANSFORM_DIST_MAX_PX; + } + + + if (new_dist <= *dist_px && new_depth < *ray_depth) { + *ray_depth = new_depth; + retval = true; + + copy_v3_v3(r_loc, location); + + if (r_no) { + normal_short_to_float_v3(r_no, vno); + mul_m3_v3(timat, r_no); + normalize_v3(r_no); + } + + *dist_px = new_dist; + } + } + + return retval; +} + +static bool snapArmature( + ARegion *ar, Object *ob, bArmature *arm, float obmat[4][4], + const float mval[2], float *dist_px, const short snap_to, + const float ray_start[3], const float ray_normal[3], float *ray_depth, + float r_loc[3], float *UNUSED(r_no)) +{ + float imat[4][4]; + float ray_start_local[3], ray_normal_local[3]; + bool retval = false; + + invert_m4_m4(imat, obmat); + + mul_v3_m4v3(ray_start_local, imat, ray_start); + mul_v3_mat3_m4v3(ray_normal_local, imat, ray_normal); + + if (arm->edbo) { + EditBone *eBone; + + for (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 |= snapVertex( + ar, eBone->head, NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + retval |= snapVertex( + ar, eBone->tail, NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + break; + case SCE_SNAP_MODE_EDGE: + retval |= snapEdge( + ar, eBone->head, NULL, eBone->tail, NULL, + obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, + ray_depth, r_loc, NULL); + break; + } + } + } + } + } + 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; + /* skip hidden bones */ + if (bone && !(bone->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG))) { + const float *head_vec = pchan->pose_head; + const float *tail_vec = pchan->pose_tail; + + switch (snap_to) { + case SCE_SNAP_MODE_VERTEX: + retval |= snapVertex( + ar, head_vec, NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, + ray_depth, r_loc, NULL); + retval |= snapVertex( + ar, tail_vec, NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + break; + case SCE_SNAP_MODE_EDGE: + retval |= snapEdge( + ar, head_vec, NULL, tail_vec, NULL, + obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, + ray_depth, r_loc, NULL); + break; + } + } + } + } + + return retval; +} + +static bool snapCurve( + ARegion *ar, Object *ob, Curve *cu, float obmat[4][4], + const float mval[2], float *dist_px, const short snap_to, + const float ray_start[3], const float ray_normal[3], float *ray_depth, + float r_loc[3], float *UNUSED(r_no)) +{ + float imat[4][4]; + float ray_start_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_start_local, ray_start); + copy_v3_v3(ray_normal_local, ray_normal); + + mul_m4_v3(imat, ray_start_local); + mul_mat3_m4_v3(imat, ray_normal_local); + + 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++) { + switch (snap_to) { + case SCE_SNAP_MODE_VERTEX: + { + if (ob->mode == OB_MODE_EDIT) { + if (nu->bezt) { + /* don't snap to selected (moving) or hidden */ + if (nu->bezt[u].f2 & SELECT || nu->bezt[u].hide != 0) { + break; + } + retval |= snapVertex( + ar, nu->bezt[u].vec[1], NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + /* 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 |= snapVertex( + ar, nu->bezt[u].vec[0], NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + } + if (!(nu->bezt[u].f3 & SELECT) && + !(nu->bezt[u].h2 & HD_ALIGN && nu->bezt[u].f1 & SELECT)) + { + retval |= snapVertex( + ar, nu->bezt[u].vec[2], NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + } + } + else { + /* don't snap to selected (moving) or hidden */ + if (nu->bp[u].f1 & SELECT || nu->bp[u].hide != 0) { + break; + } + retval |= snapVertex( + ar, nu->bp[u].vec, NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + } + } + else { + /* curve is not visible outside editmode if nurb length less than two */ + if (nu->pntsu > 1) { + if (nu->bezt) { + retval |= snapVertex( + ar, nu->bezt[u].vec[1], NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + } + else { + retval |= snapVertex( + ar, nu->bp[u].vec, NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + } + } + } + break; + } + default: + break; + } + } + } + return retval; +} + +/* may extend later (for now just snaps to empty center) */ +static bool snapEmpty( + ARegion *ar, Object *ob, float obmat[4][4], + const float mval[2], float *dist_px, const short snap_to, + const float ray_start[3], const float ray_normal[3], float *ray_depth, + float r_loc[3], float *UNUSED(r_no)) +{ + float imat[4][4]; + float ray_start_local[3], ray_normal_local[3]; + bool retval = false; + + if (ob->transflag & OB_DUPLI) { + return retval; + } + /* for now only vertex supported */ + if (snap_to != SCE_SNAP_MODE_VERTEX) { + return retval; + } + + invert_m4_m4(imat, obmat); + + mul_v3_m4v3(ray_start_local, imat, ray_start); + mul_v3_mat3_m4v3(ray_normal_local, imat, ray_normal); + + switch (snap_to) { + case SCE_SNAP_MODE_VERTEX: + { + const float zero_co[3] = {0.0f}; + retval |= snapVertex( + ar, zero_co, NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + break; + } + default: + break; + } + + return retval; +} + +static bool snapCamera( + ARegion *ar, Scene *scene, Object *object, float obmat[4][4], + const float mval[2], float *dist_px, const short snap_to, + const float ray_start[3], const float ray_normal[3], float *ray_depth, + float r_loc[3], float *UNUSED(r_no)) +{ + 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_start_local[3], ray_normal_local[3]; + + if (clip == NULL) { + return retval; + } + if (object->transflag & OB_DUPLI) { + return retval; + } + + tracking = &clip->tracking; + + BKE_tracking_get_camera_object_matrix(scene, object, orig_camera_mat); + + invert_m4_m4(orig_camera_imat, orig_camera_mat); + invert_m4_m4(imat, obmat); + + switch (snap_to) { + case SCE_SNAP_MODE_VERTEX: + { + MovieTrackingObject *tracking_object; + + for (tracking_object = tracking->objects.first; + tracking_object; + tracking_object = tracking_object->next) + { + ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object); + MovieTrackingTrack *track; + float reconstructed_camera_mat[4][4], + reconstructed_camera_imat[4][4]; + float (*vertex_obmat)[4]; + + copy_v3_v3(ray_start_local, ray_start); + 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); + + invert_m4_m4(reconstructed_camera_imat, reconstructed_camera_mat); + } + + for (track = tracksbase->first; track; track = track->next) { + float bundle_pos[3]; + + if ((track->flag & TRACK_HAS_BUNDLE) == 0) { + continue; + } + + copy_v3_v3(bundle_pos, track->bundle_pos); + if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { + mul_m4_v3(orig_camera_imat, ray_start_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_start_local); + mul_mat3_m4_v3(imat, ray_normal_local); + vertex_obmat = obmat; + } + + retval |= snapVertex( + ar, bundle_pos, NULL, vertex_obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + } + } + + break; + } + default: + break; + } + + return retval; +} + +static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt) +{ + const int *index_mp_to_orig = dm->getPolyDataArray(dm, CD_ORIGINDEX); + return index_mp_to_orig ? index_mp_to_orig[lt->poly] : lt->poly; +} + +static bool snapDerivedMesh( + SnapObjectContext *sctx, + Object *ob, DerivedMesh *dm, BMEditMesh *em, float obmat[4][4], + const float mval[2], float *dist_px, const short snap_to, bool do_bb, + const float ray_start[3], const float ray_normal[3], const float ray_origin[3], float *ray_depth, + float r_loc[3], float r_no[3], int *r_index) +{ + ARegion *ar = sctx->v3d_data.ar; + bool retval = false; + int totvert = dm->getNumVerts(dm); + + if (totvert > 0) { + const bool do_ray_start_correction = ( + ELEM(snap_to, SCE_SNAP_MODE_FACE, SCE_SNAP_MODE_VERTEX) && + (sctx->use_v3d && !((RegionView3D *)sctx->v3d_data.ar->regiondata)->is_persp)); + bool need_ray_start_correction_init = do_ray_start_correction; + + float imat[4][4]; + float timat[3][3]; /* transpose inverse matrix for normals */ + float ray_start_local[3], ray_normal_local[3]; + float local_scale, local_depth, len_diff; + + BVHTreeFromMesh treedata = {0}; + + invert_m4_m4(imat, obmat); + transpose_m3_m4(timat, imat); + + copy_v3_v3(ray_start_local, ray_start); + copy_v3_v3(ray_normal_local, ray_normal); + + mul_m4_v3(imat, ray_start_local); + mul_mat3_m4_v3(imat, ray_normal_local); + + /* local scale in normal direction */ + local_scale = normalize_v3(ray_normal_local); + local_depth = *ray_depth; + if (local_depth != BVH_RAYCAST_DIST_MAX) { + local_depth *= local_scale; + } + + if (do_bb) { + BoundBox *bb = BKE_object_boundbox_get(ob); + + if (bb) { + BoundBox bb_temp; + + /* We cannot aford a bbox with some null dimension, which may happen in some cases... + * Threshold is rather high, but seems to be needed to get good behavior, see T46099. */ + bb = BKE_boundbox_ensure_minimum_dimensions(bb, &bb_temp, 1e-1f); + + /* Exact value here is arbitrary (ideally we would scale in pixel-space based on 'dist_px'), + * scale up so we can snap against verts & edges on the boundbox, see T46816. */ + if (ELEM(snap_to, SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE)) { + BKE_boundbox_scale(&bb_temp, bb, 1.0f + 1e-1f); + bb = &bb_temp; + } + + /* was local_depth, see: T47838 */ + len_diff = BVH_RAYCAST_DIST_MAX; + + if (!BKE_boundbox_ray_hit_check(bb, ray_start_local, ray_normal_local, &len_diff)) { + return retval; + } + need_ray_start_correction_init = false; + } + } + + treedata.em_evil = em; + treedata.em_evil_all = false; + switch (snap_to) { + case SCE_SNAP_MODE_FACE: + bvhtree_from_mesh_looptri(&treedata, dm, 0.0f, 4, 6); + break; + case SCE_SNAP_MODE_VERTEX: + bvhtree_from_mesh_verts(&treedata, dm, 0.0f, 2, 6); + break; + } + + 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. + */ + BVHTreeNearest nearest; + + if (treedata.tree != NULL) { + 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) { + len_diff = sqrtf(nearest.dist_sq); + } + } + } + /* 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 (do_ray_start_correction) { + 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 - len_v3v3(ray_start_local, ray_org_local)); + local_depth -= len_diff; + } + else { + len_diff = 0.0f; + } + + switch (snap_to) { + case SCE_SNAP_MODE_FACE: + { + BVHTreeRayHit hit; + + hit.index = -1; + hit.dist = local_depth; + + if (treedata.tree && + BLI_bvhtree_ray_cast(treedata.tree, ray_start_local, ray_normal_local, 0.0f, + &hit, treedata.raycast_callback, &treedata) != -1) + { + hit.dist += len_diff; + hit.dist /= local_scale; + 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); + + retval = true; + + if (r_index) { + *r_index = dm_looptri_to_poly_index(dm, &treedata.looptri[hit.index]); + } + } + } + break; + } + case SCE_SNAP_MODE_VERTEX: + { + BVHTreeNearest nearest; + + nearest.index = -1; + nearest.dist_sq = local_depth * local_depth; + if (treedata.tree && + BLI_bvhtree_find_nearest_to_ray( + treedata.tree, ray_start_local, ray_normal_local, + &nearest, NULL, NULL) != -1) + { + const MVert *v = &treedata.vert[nearest.index]; + retval = snapVertex( + ar, v->co, v->no, obmat, timat, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, r_no); + } + break; + } + case SCE_SNAP_MODE_EDGE: + { + MVert *verts = dm->getVertArray(dm); + MEdge *edges = dm->getEdgeArray(dm); + int totedge = dm->getNumEdges(dm); + const int *index_array = NULL; + int index = 0; + int i; + + if (em != NULL) { + index_array = dm->getEdgeDataArray(dm, CD_ORIGINDEX); + BM_mesh_elem_table_ensure(em->bm, BM_EDGE); + } + + for (i = 0; i < totedge; i++) { + MEdge *e = edges + i; + bool test = true; + + if (em != NULL) { + if (index_array) { + index = index_array[i]; + } + else { + index = i; + } + + if (index == ORIGINDEX_NONE) { + test = false; + } + else { + BMEdge *eed = BM_edge_at_index(em->bm, index); + + if (BM_elem_flag_test(eed, BM_ELEM_HIDDEN) || + BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) || + BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)) + { + test = false; + } + } + } + + if (test) { + retval |= snapEdge( + ar, verts[e->v1].co, verts[e->v1].no, verts[e->v2].co, verts[e->v2].no, + obmat, timat, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, r_no); + } + } + + break; + } + } + + free_bvhtree_from_mesh(&treedata); + } + + return retval; +} + +static bool snapObject( + SnapObjectContext *sctx, + Object *ob, float obmat[4][4], bool use_obedit, const short snap_to, + const float mval[2], float *dist_px, + const float ray_start[3], const float ray_normal[3], const float ray_origin[3], float *ray_depth, + /* return args */ + float r_loc[3], float r_no[3], int *r_index, + Object **r_ob, float r_obmat[4][4]) +{ + ARegion *ar = sctx->v3d_data.ar; + bool retval = false; + + if (ob->type == OB_MESH) { + BMEditMesh *em; + DerivedMesh *dm; + bool do_bb = true; + + if (use_obedit) { + em = BKE_editmesh_from_object(ob); + dm = editbmesh_get_derived_cage(sctx->scene, ob, em, CD_MASK_BAREMESH); + do_bb = false; + } + else { + /* in this case we want the mesh from the editmesh, avoids stale data. see: T45978. + * still set the 'em' to NULL, since we only want the 'dm'. */ + em = BKE_editmesh_from_object(ob); + if (em) { + editbmesh_get_derived_cage_and_final(sctx->scene, ob, em, CD_MASK_BAREMESH, &dm); + } + else { + dm = mesh_get_derived_final(sctx->scene, ob, CD_MASK_BAREMESH); + } + em = NULL; + } + + retval = snapDerivedMesh( + sctx, ob, dm, em, obmat, mval, dist_px, snap_to, do_bb, + ray_start, ray_normal, ray_origin, ray_depth, + r_loc, r_no, r_index); + + dm->release(dm); + } + else if (ob->type == OB_ARMATURE) { + retval = snapArmature( + ar, ob, ob->data, obmat, mval, dist_px, snap_to, + ray_start, ray_normal, ray_depth, + r_loc, r_no); + } + else if (ob->type == OB_CURVE) { + retval = snapCurve( + ar, ob, ob->data, obmat, mval, dist_px, snap_to, + ray_start, ray_normal, ray_depth, + r_loc, r_no); + } + else if (ob->type == OB_EMPTY) { + retval = snapEmpty( + ar, ob, obmat, mval, dist_px, snap_to, + ray_start, ray_normal, ray_depth, + r_loc, r_no); + } + else if (ob->type == OB_CAMERA) { + retval = snapCamera( + ar, sctx->scene, ob, obmat, mval, dist_px, snap_to, + ray_start, ray_normal, ray_depth, + r_loc, r_no); + } + + if (retval) { + if (r_ob) { + *r_ob = ob; + copy_m4_m4(r_obmat, obmat); + } + } + + return retval; +} + +static bool snapObjectsRay( + SnapObjectContext *sctx, + SnapSelect snap_select, const short snap_to, + const float mval[2], float *dist_px, + /* special handling of active and edit objects */ + Base *base_act, Object *obedit, + const float ray_start[3], const float ray_normal[3], const float ray_origin[3], float *ray_depth, + /* return args */ + float r_loc[3], float r_no[3], int *r_index, + Object **r_ob, float r_obmat[4][4]) +{ + Base *base; + bool retval = false; + + if (snap_select == SNAP_ALL && obedit) { + Object *ob = obedit; + + retval |= snapObject( + sctx, ob, ob->obmat, true, snap_to, + mval, dist_px, + ray_start, ray_normal, ray_origin, ray_depth, + r_loc, r_no, r_index, r_ob, r_obmat); + } + + /* Need an exception for particle edit because the base is flagged with BA_HAS_RECALC_DATA + * which makes the loop skip it, even the derived mesh will never change + * + * To solve that problem, we do it first as an exception. + * */ + base = base_act; + if (base && base->object && base->object->mode & OB_MODE_PARTICLE_EDIT) { + Object *ob = base->object; + retval |= snapObject( + sctx, ob, ob->obmat, false, snap_to, + mval, dist_px, + ray_start, ray_normal, ray_origin, ray_depth, + r_loc, r_no, r_index, r_ob, r_obmat); + } + + for (base = sctx->scene->base.first; base != NULL; base = base->next) { + if ((BASE_VISIBLE_BGMODE(sctx->v3d_data.v3d, sctx->scene, base)) && + (base->flag & (BA_HAS_RECALC_OB | BA_HAS_RECALC_DATA)) == 0 && + + ((snap_select == SNAP_NOT_SELECTED && (base->flag & (SELECT | BA_WAS_SEL)) == 0) || + (ELEM(snap_select, SNAP_ALL, SNAP_NOT_OBEDIT) && base != base_act))) + { + Object *ob = base->object; + Object *ob_snap = ob; + bool use_obedit = false; + + /* for linked objects, use the same object but a different matrix */ + if (obedit && ob->data == obedit->data) { + use_obedit = true; + ob_snap = obedit; + } + + if (ob->transflag & OB_DUPLI) { + DupliObject *dupli_ob; + ListBase *lb = object_duplilist(sctx->bmain->eval_ctx, sctx->scene, ob); + + for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) { + bool use_obedit_dupli = (obedit && dupli_ob->ob->data == obedit->data); + Object *dupli_snap = (use_obedit_dupli) ? obedit : dupli_ob->ob; + + retval |= snapObject( + sctx, dupli_snap, dupli_ob->mat, use_obedit_dupli, snap_to, + mval, dist_px, + ray_start, ray_normal, ray_origin, ray_depth, + r_loc, r_no, r_index, r_ob, r_obmat); + } + + free_object_duplilist(lb); + } + + retval |= snapObject( + sctx, ob_snap, ob->obmat, use_obedit, snap_to, + mval, dist_px, + ray_start, ray_normal, ray_origin, ray_depth, + r_loc, r_no, r_index, r_ob, r_obmat); + } + } + + return retval; +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** \name Public Object Snapping API + * \{ */ + +SnapObjectContext *ED_transform_snap_object_context_create( + Main *bmain, Scene *scene, int flag) +{ + SnapObjectContext *sctx = MEM_callocN(sizeof(*sctx), __func__); + + sctx->flag = flag; + + sctx->bmain = bmain; + sctx->scene = scene; + + return sctx; +} + +SnapObjectContext *ED_transform_snap_object_context_create_view3d( + Main *bmain, Scene *scene, int flag, + /* extra args for view3d */ + ARegion *ar, View3D *v3d) +{ + SnapObjectContext *sctx = ED_transform_snap_object_context_create(bmain, scene, flag); + + sctx->use_v3d = true; + sctx->v3d_data.ar = ar; + sctx->v3d_data.v3d = v3d; + + return sctx; +} + +void ED_transform_snap_object_context_destroy(SnapObjectContext *sctx) +{ + MEM_freeN(sctx); +} + + +bool ED_transform_snap_object_project_ray_ex( + SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float ray_start[3], const float ray_normal[3], float *ray_depth, + float r_loc[3], float r_no[3], int *r_index, + Object **r_ob, float r_obmat[4][4]) +{ + Base *base_act = params->use_object_active ? sctx->scene->basact : NULL; + Object *obedit = params->use_object_edit ? sctx->scene->obedit : NULL; + + return snapObjectsRay( + sctx, + params->snap_select, params->snap_to, + NULL, NULL, + base_act, obedit, + ray_start, ray_normal, ray_start, ray_depth, + r_loc, r_no, r_index, + r_ob, r_obmat); +} + +/** + * Convenience function for snap ray-casting. + * + * Given a ray, cast it into the scene (snapping to faces). + * + * \return Snap success + */ +static bool transform_snap_context_project_ray_impl( + SnapObjectContext *sctx, + const float ray_start[3], const float ray_normal[3], float *ray_dist, + float r_co[3], float r_no[3]) +{ + bool ret; + + /* try snap edge, then face if it fails */ + ret = ED_transform_snap_object_project_ray_ex( + sctx, + &(const struct SnapObjectParams){ + .snap_select = SNAP_ALL, + .snap_to = SCE_SNAP_MODE_FACE, + .use_object_edit = (sctx->scene->obedit != NULL), + }, + ray_start, ray_normal, ray_dist, + r_co, r_no, NULL, + NULL, NULL); + + return ret; +} + +bool ED_transform_snap_object_project_ray( + SnapObjectContext *sctx, + const float ray_origin[3], const float ray_direction[3], float *ray_dist, + float r_co[3], float r_no[3]) +{ + float ray_dist_fallback; + if (ray_dist == NULL) { + ray_dist_fallback = BVH_RAYCAST_DIST_MAX; + ray_dist = &ray_dist_fallback; + } + + float no_fallback[3]; + if (r_no == NULL) { + r_no = no_fallback; + } + + return transform_snap_context_project_ray_impl( + sctx, + ray_origin, ray_direction, ray_dist, + r_co, r_no); +} + +static bool transform_snap_context_project_view3d_mixed_impl( + SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float mval[2], float *dist_px, + bool use_depth, + float r_co[3], float r_no[3]) +{ + float ray_dist = 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(params->snap_to_flag != 0); + BLI_assert((params->snap_to_flag & ~(1 | 2 | 4)) == 0); + + struct SnapObjectParams params_temp = *params; + + for (int i = 0; i < 3; i++) { + if ((params->snap_to_flag & (1 << i)) && (is_hit == false || use_depth)) { + if (use_depth == false) { + ray_dist = BVH_RAYCAST_DIST_MAX; + } + + params_temp.snap_to = elem_type[i]; + + if (ED_transform_snap_object_project_view3d( + sctx, + ¶ms_temp, + mval, dist_px, &ray_dist, + r_co, r_no)) + { + is_hit = true; + } + } + } + + return is_hit; +} + +/** + * Convenience function for performing snapping. + * + * Given a 2D region value, snap to vert/edge/face. + * + * \param sctx: Snap context. + * \param mval: 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 + */ +bool ED_transform_snap_object_project_view3d_mixed( + SnapObjectContext *sctx, + 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, + params, + mval_fl, dist_px, use_depth, + r_co, r_no); +} + +bool ED_transform_snap_object_project_view3d_ex( + SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float mval[2], float *dist_px, + float *ray_depth, + float r_loc[3], float r_no[3], int *r_index) +{ + float ray_start[3], ray_normal[3], ray_orgigin[3]; + + if (!ED_view3d_win_to_ray_ex( + sctx->v3d_data.ar, sctx->v3d_data.v3d, + mval, ray_orgigin, ray_normal, ray_start, true)) + { + return false; + } + + Base *base_act = params->use_object_active ? sctx->scene->basact : NULL; + Object *obedit = params->use_object_edit ? sctx->scene->obedit : NULL; + return snapObjectsRay( + sctx, + params->snap_select, params->snap_to, + mval, dist_px, + base_act, obedit, + ray_start, ray_normal, ray_orgigin, ray_depth, + r_loc, r_no, r_index, NULL, NULL); +} + +bool ED_transform_snap_object_project_view3d( + SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float mval[2], float *dist_px, + float *ray_depth, + float r_loc[3], float r_no[3]) +{ + return ED_transform_snap_object_project_view3d_ex( + sctx, + params, + mval, dist_px, + ray_depth, + r_loc, r_no, NULL); +} + +/** \} */ -- cgit v1.2.3