Fix #142338: Edge center snapping interferes with face snapping

The snapping system could return a regular 'Snap to Edge' result when
only derived edge snap types (midpoint, endpoint, perpendicular) were
enabled, even if 'Snap to Edge' itself was not included among the
selected snap modes. This led to unintended snapping behavior.

To address this, a backup of the previous snap result is stored before
edge snapping is attempted. If the resulting snap mode is not among the
explicitly selected types, the previous state is restored. Additionally,
the `hit_list` assignment was moved to the runtime context to separate
intermediate data from the final snap result.

Pull Request: https://projects.blender.org/blender/blender/pulls/142512
This commit is contained in:
Germano Cavalcante
2025-07-19 22:45:20 +02:00
committed by Germano Cavalcante
parent 4de5da1ea2
commit 732436946e
3 changed files with 28 additions and 11 deletions

View File

@@ -1165,10 +1165,10 @@ static bool snap_object_context_runtime_init(SnapObjectContext *sctx,
}
}
}
sctx->runtime.hit_list = hit_list;
sctx->ret.ray_depth_max = sctx->ret.ray_depth_max_in_front = ray_depth;
sctx->ret.index = -1;
sctx->ret.hit_list = hit_list;
sctx->ret.ob = nullptr;
sctx->ret.data = nullptr;
sctx->ret.dist_px_sq = dist_px_sq;
@@ -1424,6 +1424,16 @@ eSnapMode snap_object_project_view3d_ex(SnapObjectContext *sctx,
/* Remove what has already been computed. */
sctx->runtime.snap_to_flag &= ~(SCE_SNAP_TO_FACE | SCE_SNAP_INDIVIDUAL_NEAREST);
SnapObjectContext::Output ret_bak{};
if (!(sctx->runtime.snap_to_flag & SCE_SNAP_TO_EDGE) &&
(sctx->runtime.snap_to_flag &
(SCE_SNAP_TO_EDGE_MIDPOINT | SCE_SNAP_TO_EDGE_ENDPOINT | SCE_SNAP_TO_EDGE_PERPENDICULAR)))
{
/* 'Snap to Edge' may occur even if it is not included among the selected snap types.
* Save a backup to restore the previous result if needed. */
ret_bak = sctx->ret;
}
if (use_occlusion_plane && has_hit) {
/* Compute the new clip_pane but do not add it yet. */
BLI_ASSERT_UNIT_V3(sctx->ret.no);
@@ -1454,11 +1464,18 @@ eSnapMode snap_object_project_view3d_ex(SnapObjectContext *sctx,
elem = elem_test;
}
if ((elem == SCE_SNAP_TO_EDGE) &&
(snap_to_flag &
(SCE_SNAP_TO_EDGE_ENDPOINT | SCE_SNAP_TO_EDGE_MIDPOINT | SCE_SNAP_TO_EDGE_PERPENDICULAR)))
{
elem = snap_edge_points(sctx, square_f(*dist_px));
if (elem == SCE_SNAP_TO_EDGE) {
if (snap_to_flag &
(SCE_SNAP_TO_EDGE_ENDPOINT | SCE_SNAP_TO_EDGE_MIDPOINT | SCE_SNAP_TO_EDGE_PERPENDICULAR))
{
elem = snap_edge_points(sctx, square_f(*dist_px));
}
if (!(elem & snap_to_flag)) {
/* Restore the previous snap. */
elem = SCE_SNAP_TO_NONE;
sctx->ret = ret_bak;
}
}
if (elem != SCE_SNAP_TO_NONE) {

View File

@@ -86,6 +86,8 @@ struct SnapObjectContext {
/* Read/write. */
uint object_index;
/* List of #SnapObjectHitDepth (caller must free). */
ListBase *hit_list;
eSnapOcclusionTest occlusion_test_edit;
@@ -94,7 +96,7 @@ struct SnapObjectContext {
} runtime;
/* Output. */
struct {
struct Output {
/* Location of snapped point on target surface. */
float3 loc;
/* Normal of snapped point on target surface. */
@@ -103,8 +105,6 @@ struct SnapObjectContext {
int index;
/* Matrix of target object (may not be #Object.object_to_world with dupli-instances). */
float4x4 obmat;
/* List of #SnapObjectHitDepth (caller must free). */
ListBase *hit_list;
/* Snapped object. */
const Object *ob;
/* Snapped data. */

View File

@@ -140,7 +140,7 @@ static bool raycastMesh(SnapObjectContext *sctx,
}
BLI_assert(treedata.raycast_callback != nullptr);
if (sctx->ret.hit_list) {
if (sctx->runtime.hit_list) {
RayCastAll_Data data;
data.bvhdata = &treedata;
@@ -149,7 +149,7 @@ static bool raycastMesh(SnapObjectContext *sctx,
data.len_diff = len_diff;
data.local_scale = local_scale;
data.ob_uuid = ob_index;
data.hit_list = sctx->ret.hit_list;
data.hit_list = sctx->runtime.hit_list;
void *hit_last_prev = data.hit_list->last;
BLI_bvhtree_ray_cast_all(