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
This commit is contained in:
Germano Cavalcante
2024-11-11 14:26:52 +01:00
committed by Germano Cavalcante
parent 72fd267024
commit b4154c6a0e
9 changed files with 76 additions and 38 deletions

View File

@@ -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),

View File

@@ -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. */

View File

@@ -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,

View File

@@ -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,
&params,
co,
mval_fl,
prev_co,
&dist_px,
co,
no);
}
}
}
#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK

View File

@@ -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,

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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. */

View File

@@ -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) {