From b4154c6a0e49fa16c08bfe9acd1e540690edf42d Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 11 Nov 2024 14:26:52 +0100 Subject: [PATCH] Fix #130078: Snap to Grid ignored if occluded in Add Object Tool The snap cursor internally snaps to the Face to define the occlusion of the Grid or to calculate the "Surface" orientation set by the tool. Even though this is not the snap point enabled by the user, this internal Snap to Face takes precedence over the Snap to Grid that is enabled by the user. To solve this, the solution is to do a second snap test but now only to Grid and using the hit point as a reference for the closest grid. Also fixes Absolute Increment Snap not being detected as Grid by the tool. Pull Request: https://projects.blender.org/blender/blender/pulls/130092 --- .../gizmo_library/gizmo_types/move3d_gizmo.cc | 2 +- .../ED_transform_snap_object_context.hh | 10 +++- source/blender/editors/mesh/editmesh_utils.cc | 2 +- .../space_view3d/view3d_cursor_snap.cc | 38 +++++++++++---- .../editors/space_view3d/view3d_edit.cc | 2 +- .../editors/transform/transform_snap.cc | 6 +-- .../transform/transform_snap_object.cc | 47 +++++++++++-------- .../transform/transform_snap_object.hh | 3 +- .../transform/transform_snap_object_mesh.cc | 4 +- 9 files changed, 76 insertions(+), 38 deletions(-) diff --git a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.cc b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.cc index 479e90511e6..be00466b24f 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.cc +++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.cc @@ -278,7 +278,7 @@ static int gizmo_move_modal(bContext *C, SnapObjectParams params{}; params.snap_target_select = SCE_SNAP_TARGET_ALL; params.edit_mode_type = SNAP_GEOM_EDIT; - params.use_occlusion_test = true; + params.occlusion_test = SNAP_OCCLUSION_AS_SEEM; if (ED_transform_snap_object_project_view3d( inter->snap_context_v3d, CTX_data_ensure_evaluated_depsgraph(C), diff --git a/source/blender/editors/include/ED_transform_snap_object_context.hh b/source/blender/editors/include/ED_transform_snap_object_context.hh index 1e49ed7b8b7..1b348923d45 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.hh +++ b/source/blender/editors/include/ED_transform_snap_object_context.hh @@ -33,6 +33,12 @@ enum eSnapEditType { SNAP_GEOM_EDIT = 2, /* Bmesh for mesh-type. */ }; +enum eSnapOcclusionTest { + SNAP_OCCLUSION_NEVER = 0, + SNAP_OCCLUSION_AS_SEEM = 1, + SNAP_OCCLUSION_ALWAYS = 2, +}; + /** used for storing multiple hits */ struct SnapObjectHitDepth { SnapObjectHitDepth *next, *prev; @@ -53,12 +59,12 @@ struct SnapObjectParams { eSnapTargetOP snap_target_select; /** Geometry for snapping in edit mode. */ eSnapEditType edit_mode_type; + /** Snap to or ignore the frontmost object. */ + eSnapOcclusionTest occlusion_test; /** Grid unit size. "0.0" is automatic. */ float grid_size; /** Break nearest face snapping into steps to improve transformations across U-shaped targets. */ short face_nearest_steps; - /** Snap to the closest element, use when using more than one snap type. */ - bool use_occlusion_test : 1; /** Exclude back facing geometry from snapping. */ bool use_backface_culling : 1; /** Enable to force nearest face snapping to snap to target the source was initially near. */ diff --git a/source/blender/editors/mesh/editmesh_utils.cc b/source/blender/editors/mesh/editmesh_utils.cc index 06c8a557b1f..e3c72d16f2a 100644 --- a/source/blender/editors/mesh/editmesh_utils.cc +++ b/source/blender/editors/mesh/editmesh_utils.cc @@ -1943,7 +1943,7 @@ void EDBM_project_snap_verts( SnapObjectParams params{}; params.snap_target_select = target_op; params.edit_mode_type = SNAP_GEOM_FINAL; - params.use_occlusion_test = true; + params.occlusion_test = SNAP_OCCLUSION_AS_SEEM; if (ED_transform_snap_object_project_view3d(snap_context, depsgraph, region, diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.cc b/source/blender/editors/space_view3d/view3d_cursor_snap.cc index 83b0788cad3..ae80f5b0226 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.cc +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.cc @@ -563,9 +563,16 @@ static bool v3d_cursor_is_snap_invert(SnapCursorDataIntern *data_intern, uint8_t static eSnapMode v3d_cursor_snap_elements(ToolSettings *tool_settings) { - return eSnapMode(tool_settings->snap_mode_tools == SCE_SNAP_TO_NONE ? - tool_settings->snap_mode : - tool_settings->snap_mode_tools); + if (tool_settings->snap_mode_tools == SCE_SNAP_TO_NONE) { + /* Use the snap modes defined in the scene instead. */ + eSnapMode snap_mode = eSnapMode(tool_settings->snap_mode); + if ((snap_mode & SCE_SNAP_TO_INCREMENT) && (tool_settings->snap_flag & SCE_SNAP_ABS_GRID)) { + /* Convert snap to increment to snap to grid. */ + snap_mode |= SCE_SNAP_TO_GRID; + } + return snap_mode; + } + return eSnapMode(tool_settings->snap_mode_tools); } static void v3d_cursor_snap_context_ensure(Scene *scene) @@ -615,7 +622,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, snap_data->is_snap_invert = v3d_cursor_is_snap_invert(data_intern, event_modifier); #endif - if (snap_data->is_snap_invert != !(tool_settings->snap_flag & SCE_SNAP)) { + if (snap_data->is_snap_invert != (tool_settings->snap_flag & SCE_SNAP) == 0) { snap_data->is_enabled = false; if (!calc_plane_omat) { snap_data->type_target = SCE_SNAP_TO_NONE; @@ -663,15 +670,14 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, SNAP_GEOM_CAGE : SNAP_GEOM_EDIT; - bool use_occlusion_test = (state->flag & V3D_SNAPCURSOR_OCCLUSION_ALWAYS_TRUE) ? false : - true; - float dist_px = 12.0f * U.pixelsize; SnapObjectParams params{}; params.snap_target_select = SCE_SNAP_TARGET_ALL; params.edit_mode_type = edit_mode_type; - params.use_occlusion_test = use_occlusion_test; + params.occlusion_test = (state->flag & V3D_SNAPCURSOR_OCCLUSION_ALWAYS_TRUE) ? + SNAP_OCCLUSION_ALWAYS : + SNAP_OCCLUSION_AS_SEEM; snap_elem = ED_transform_snap_object_project_view3d_ex(data_intern->snap_context_v3d, depsgraph, region, @@ -688,6 +694,22 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, nullptr, obmat, face_nor); + if ((snap_elem & data_intern->snap_elem_hidden) && (snap_elements & SCE_SNAP_TO_GRID)) { + BLI_assert(snap_elem != SCE_SNAP_TO_GRID); + params.occlusion_test = SNAP_OCCLUSION_NEVER; + snap_elem = ED_transform_snap_object_project_view3d(data_intern->snap_context_v3d, + depsgraph, + region, + v3d, + SCE_SNAP_TO_GRID, + ¶ms, + co, + mval_fl, + prev_co, + &dist_px, + co, + no); + } } } #ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK diff --git a/source/blender/editors/space_view3d/view3d_edit.cc b/source/blender/editors/space_view3d/view3d_edit.cc index 43c0a6bde23..0d7fff6cf84 100644 --- a/source/blender/editors/space_view3d/view3d_edit.cc +++ b/source/blender/editors/space_view3d/view3d_edit.cc @@ -915,7 +915,7 @@ void ED_view3d_cursor3d_position_rotation(bContext *C, SnapObjectParams params{}; params.snap_target_select = SCE_SNAP_TARGET_ALL; params.edit_mode_type = SNAP_GEOM_FINAL; - params.use_occlusion_test = true; + params.occlusion_test = SNAP_OCCLUSION_AS_SEEM; if (ED_transform_snap_object_project_view3d_ex(snap_context, CTX_data_ensure_evaluated_depsgraph(C), region, diff --git a/source/blender/editors/transform/transform_snap.cc b/source/blender/editors/transform/transform_snap.cc index 453e82eb6dd..93d0df0be9e 100644 --- a/source/blender/editors/transform/transform_snap.cc +++ b/source/blender/editors/transform/transform_snap.cc @@ -427,7 +427,7 @@ static bool applyFaceProject(TransInfo *t, TransDataContainer *tc, TransData *td SnapObjectParams snap_object_params{}; snap_object_params.snap_target_select = t->tsnap.target_operation; snap_object_params.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL; - snap_object_params.use_occlusion_test = false; + snap_object_params.occlusion_test = SNAP_OCCLUSION_ALWAYS; snap_object_params.use_backface_culling = (t->tsnap.flag & SCE_SNAP_BACKFACE_CULLING) != 0; eSnapMode hit = ED_transform_snap_object_project_view3d(t->tsnap.object_context, @@ -490,7 +490,7 @@ static void applyFaceNearest(TransInfo *t, TransDataContainer *tc, TransData *td SnapObjectParams snap_object_params{}; snap_object_params.snap_target_select = t->tsnap.target_operation; snap_object_params.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL; - snap_object_params.use_occlusion_test = false; + snap_object_params.occlusion_test = SNAP_OCCLUSION_ALWAYS; snap_object_params.use_backface_culling = false; snap_object_params.face_nearest_steps = t->tsnap.face_nearest_steps; snap_object_params.keep_on_same_target = t->tsnap.flag & SCE_SNAP_KEEP_ON_SAME_OBJECT; @@ -1603,7 +1603,7 @@ static eSnapMode snapObjectsTransform( t->snap_spatial[0] * t->snap_spatial_precision : t->snap_spatial[0]; snap_object_params.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL; - snap_object_params.use_occlusion_test = true; + snap_object_params.occlusion_test = SNAP_OCCLUSION_AS_SEEM; snap_object_params.use_backface_culling = (t->tsnap.flag & SCE_SNAP_BACKFACE_CULLING) != 0; float *prev_co = (t->tsnap.status & SNAP_SOURCE_FOUND) ? t->tsnap.snap_source : t->center_global; diff --git a/source/blender/editors/transform/transform_snap_object.cc b/source/blender/editors/transform/transform_snap_object.cc index dbec9e69799..42643fb51f0 100644 --- a/source/blender/editors/transform/transform_snap_object.cc +++ b/source/blender/editors/transform/transform_snap_object.cc @@ -125,8 +125,8 @@ void SnapData::clip_planes_enable(SnapObjectContext *sctx, { float4x4 tobmat = math::transpose(this->obmat_); if (!skip_occlusion_plane) { - const bool is_in_front = sctx->runtime.params.use_occlusion_test && ob_eval && - (ob_eval->dtx & OB_DRAW_IN_FRONT) != 0; + const bool is_in_front = (sctx->runtime.params.occlusion_test == SNAP_OCCLUSION_AS_SEEM) && + ob_eval && (ob_eval->dtx & OB_DRAW_IN_FRONT) != 0; if (!is_in_front && sctx->runtime.has_occlusion_plane) { this->clip_planes.append(tobmat * sctx->runtime.occlusion_plane); } @@ -583,7 +583,9 @@ static eSnapMode raycast_obj_fn(SnapObjectContext *sctx, bool retval = false; if (ob_data == nullptr) { - if (sctx->runtime.use_occlusion_test_edit && ELEM(ob_eval->dt, OB_BOUNDBOX, OB_WIRE)) { + if ((sctx->runtime.occlusion_test_edit == SNAP_OCCLUSION_AS_SEEM) && + ELEM(ob_eval->dt, OB_BOUNDBOX, OB_WIRE)) + { /* Do not hit objects that are in wire or bounding box display mode. */ return SCE_SNAP_TO_NONE; } @@ -596,7 +598,9 @@ static eSnapMode raycast_obj_fn(SnapObjectContext *sctx, return SCE_SNAP_TO_NONE; } } - else if (sctx->runtime.params.use_occlusion_test && ELEM(ob_eval->dt, OB_BOUNDBOX, OB_WIRE)) { + else if ((sctx->runtime.params.occlusion_test == SNAP_OCCLUSION_AS_SEEM) && + ELEM(ob_eval->dt, OB_BOUNDBOX, OB_WIRE)) + { /* Do not hit objects that are in wire or bounding box display mode. */ return SCE_SNAP_TO_NONE; } @@ -1057,6 +1061,7 @@ static bool snap_object_context_runtime_init(SnapObjectContext *sctx, const ARegion *region, const View3D *v3d, eSnapMode snap_to_flag, + eSnapOcclusionTest occlusion_test, const SnapObjectParams *params, const float ray_start[3], const float ray_dir[3], @@ -1065,8 +1070,7 @@ static bool snap_object_context_runtime_init(SnapObjectContext *sctx, const float init_co[3], const float prev_co[3], const float dist_px_sq, - ListBase *hit_list, - bool use_occlusion_test) + ListBase *hit_list) { if (snap_to_flag & (SCE_SNAP_TO_GRID | SCE_SNAP_TO_EDGE_PERPENDICULAR | SCE_SNAP_INDIVIDUAL_NEAREST)) @@ -1094,9 +1098,9 @@ static bool snap_object_context_runtime_init(SnapObjectContext *sctx, sctx->runtime.v3d = v3d; sctx->runtime.snap_to_flag = snap_to_flag; sctx->runtime.params = *params; - sctx->runtime.params.use_occlusion_test = use_occlusion_test; - sctx->runtime.use_occlusion_test_edit = use_occlusion_test && - (snap_to_flag & SCE_SNAP_TO_FACE) == 0; + sctx->runtime.params.occlusion_test = occlusion_test; + sctx->runtime.occlusion_test_edit = (snap_to_flag & SCE_SNAP_TO_FACE) ? SNAP_OCCLUSION_ALWAYS : + occlusion_test; sctx->runtime.has_occlusion_plane = false; sctx->runtime.has_occlusion_plane_in_front = false; sctx->runtime.object_index = 0; @@ -1188,6 +1192,7 @@ bool ED_transform_snap_object_project_ray_ex(SnapObjectContext *sctx, nullptr, v3d, SCE_SNAP_TO_FACE, + params->occlusion_test, params, ray_start, ray_normal, @@ -1197,8 +1202,7 @@ bool ED_transform_snap_object_project_ray_ex(SnapObjectContext *sctx, nullptr, nullptr, 0, - nullptr, - params->use_occlusion_test)) + nullptr)) { return false; } @@ -1240,6 +1244,7 @@ bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx, nullptr, v3d, SCE_SNAP_TO_FACE, + params->occlusion_test, params, ray_start, ray_normal, @@ -1248,8 +1253,7 @@ bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx, nullptr, nullptr, 0, - r_hit_list, - params->use_occlusion_test)) + r_hit_list)) { return false; } @@ -1316,12 +1320,16 @@ eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, bool use_occlusion_plane = false; /* It is required `mval` to calculate the occlusion plane. */ - if (mval && (snap_to_flag & SCE_SNAP_TO_GEOM)) { - const bool is_allways_occluded = !params->use_occlusion_test; - use_occlusion_plane = is_allways_occluded || !XRAY_ENABLED(v3d); + if (mval && (snap_to_flag & (SCE_SNAP_TO_GEOM | SCE_SNAP_TO_GRID))) { + if (params->occlusion_test == SNAP_OCCLUSION_AS_SEEM) { + use_occlusion_plane = !XRAY_ENABLED(v3d); + } + else if (params->occlusion_test == SNAP_OCCLUSION_ALWAYS) { + use_occlusion_plane = true; + } } - if (use_occlusion_plane || (snap_to_flag & (SCE_SNAP_TO_FACE | SCE_SNAP_TO_GRID))) { + if (use_occlusion_plane || (snap_to_flag & SCE_SNAP_TO_FACE)) { /* Calculate the direction (`ray_dir`) and starting point (`ray_start`) of the ray from the * viewport to a 3D point under the mouse cursor (`mval`), taking into account potential view * clipping. @@ -1357,6 +1365,8 @@ eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, region, v3d, snap_to_flag, + use_occlusion_plane ? params->occlusion_test : + SNAP_OCCLUSION_NEVER, params, sctx->runtime.ray_start, sctx->runtime.ray_dir, @@ -1365,8 +1375,7 @@ eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, init_co, prev_co, dist_px ? square_f(*dist_px) : FLT_MAX, - nullptr, - use_occlusion_plane)) + nullptr)) { return retval; } diff --git a/source/blender/editors/transform/transform_snap_object.hh b/source/blender/editors/transform/transform_snap_object.hh index c912c946bce..a64aa0b0613 100644 --- a/source/blender/editors/transform/transform_snap_object.hh +++ b/source/blender/editors/transform/transform_snap_object.hh @@ -67,9 +67,10 @@ struct SnapObjectContext { /* Read/write. */ uint object_index; + eSnapOcclusionTest occlusion_test_edit; + bool has_occlusion_plane; bool has_occlusion_plane_in_front; - bool use_occlusion_test_edit; } runtime; /* Output. */ diff --git a/source/blender/editors/transform/transform_snap_object_mesh.cc b/source/blender/editors/transform/transform_snap_object_mesh.cc index aa916ffc403..5af0d5108e2 100644 --- a/source/blender/editors/transform/transform_snap_object_mesh.cc +++ b/source/blender/editors/transform/transform_snap_object_mesh.cc @@ -98,8 +98,8 @@ static bool raycastMesh(SnapObjectContext *sctx, /* Local scale in normal direction. */ ray_normal_local = math::normalize_and_get_length(ray_normal_local, local_scale); - const bool is_in_front = sctx->runtime.params.use_occlusion_test && - (ob_eval->dtx & OB_DRAW_IN_FRONT) != 0; + const bool is_in_front = (sctx->runtime.params.occlusion_test == SNAP_OCCLUSION_AS_SEEM) && + (ob_eval->dtx & OB_DRAW_IN_FRONT); const float depth_max = is_in_front ? sctx->ret.ray_depth_max_in_front : sctx->ret.ray_depth_max; local_depth = depth_max; if (local_depth != BVH_RAYCAST_DIST_MAX) {