diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 1fbd1c136a4..3d2b34a05ce 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -7548,9 +7548,6 @@ class VIEW3D_PT_snapping(Panel): col.separator() - if 'INCREMENT' in tool_settings.snap_elements: - col.prop(tool_settings, "use_snap_grid_absolute") - if 'VOLUME' in tool_settings.snap_elements: col.prop(tool_settings, "use_snap_peel_object") diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.cc b/source/blender/editors/space_view3d/view3d_cursor_snap.cc index f16e97d8229..5c9e3820ad3 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.cc +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.cc @@ -130,38 +130,6 @@ static void v3d_cursor_poject_surface_normal(const float normal[3], copy_v3_v3(r_mat[2], mat[i_best]); } -/** - * Calculate 3D view incremental (grid) snapping. - * - * \note This could be moved to a public function. - */ -static bool v3d_cursor_snap_calc_incremental( - Scene *scene, View3D *v3d, ARegion *region, const float co_relative[3], float co[3]) -{ - const float grid_size = ED_view3d_grid_view_scale(scene, v3d, region, nullptr); - if (UNLIKELY(grid_size == 0.0f)) { - return false; - } - - if (scene->toolsettings->snap_flag & SCE_SNAP_ABS_GRID) { - co_relative = nullptr; - } - - if (co_relative != nullptr) { - sub_v3_v3(co, co_relative); - } - mul_v3_fl(co, 1.0f / grid_size); - co[0] = roundf(co[0]); - co[1] = roundf(co[1]); - co[2] = roundf(co[2]); - mul_v3_fl(co, grid_size); - if (co_relative != nullptr) { - add_v3_v3(co, co_relative); - } - - return true; -} - /** * Re-order \a mat so \a axis_align uses its own axis which is closest to \a v. */ @@ -377,6 +345,11 @@ static void cursor_box_draw(const float dimensions[3], uchar color[4]) static void cursor_point_draw( uint attr_pos, const float loc[3], const float size, eSnapMode snap_type, const uchar color[4]) { + if (snap_type == SCE_SNAP_TO_GRID) { + /* No drawing. */ + return; + } + immUniformColor4ubv(color); GPU_matrix_push(); @@ -686,7 +659,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, snap_elements |= SCE_SNAP_TO_FACE; } - if (snap_elements & SCE_SNAP_TO_GEOM) { + if (snap_elements & (SCE_SNAP_TO_GEOM | SCE_SNAP_TO_GRID)) { float prev_co[3] = {0.0f}; if (state->prevpoint) { copy_v3_v3(prev_co, state->prevpoint); @@ -810,10 +783,6 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, if (!do_plane_isect) { ED_view3d_win_to_3d(v3d, region, co_depth, mval_fl, co); } - - if (snap_data->is_enabled && (snap_elements & SCE_SNAP_TO_INCREMENT)) { - v3d_cursor_snap_calc_incremental(scene, v3d, region, state->prevpoint, co); - } } else if (snap_elem & SCE_SNAP_TO_VERTEX) { snap_elem_index[0] = index; diff --git a/source/blender/editors/space_view3d/view3d_draw.cc b/source/blender/editors/space_view3d/view3d_draw.cc index 81ba5d8f92e..8cfb7c7d00b 100644 --- a/source/blender/editors/space_view3d/view3d_draw.cc +++ b/source/blender/editors/space_view3d/view3d_draw.cc @@ -920,7 +920,7 @@ float ED_view3d_grid_view_scale(const Scene *scene, const char **r_grid_unit) { float grid_scale; - RegionView3D *rv3d = static_cast(region->regiondata); + const RegionView3D *rv3d = static_cast(region->regiondata); if (!rv3d->is_persp && RV3D_VIEW_IS_AXIS(rv3d->view)) { /* Decrease the distance between grid snap points depending on zoom. */ float dist = 12.0f / (region->sizex * rv3d->winmat[0][0]); diff --git a/source/blender/editors/space_view3d/view3d_placement.cc b/source/blender/editors/space_view3d/view3d_placement.cc index 75fd717bb96..340b5d349e1 100644 --- a/source/blender/editors/space_view3d/view3d_placement.cc +++ b/source/blender/editors/space_view3d/view3d_placement.cc @@ -155,7 +155,9 @@ struct InteractivePlaceData { /** When activated without a tool. */ bool wait_for_input; - eSnapMode snap_to; + /* WORKAROUND: We need to remove #SCE_SNAP_TO_GRID temporarily. */ + short *snap_to_ptr; + eSnapMode snap_to_restore; }; /** \} */ @@ -225,7 +227,7 @@ static bool idp_snap_calc_incremental( return false; } - if (scene->toolsettings->snap_flag & SCE_SNAP_ABS_GRID) { + if (scene->toolsettings->snap_mode & SCE_SNAP_TO_GRID) { co_relative = nullptr; } @@ -764,10 +766,11 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv ipd->step_index = STEP_BASE; - ipd->snap_to = eSnapMode(tool_settings->snap_mode_tools); - if (ipd->snap_to == SCE_SNAP_TO_NONE) { - ipd->snap_to = eSnapMode(tool_settings->snap_mode); + ipd->snap_to_ptr = &tool_settings->snap_mode_tools; + if (eSnapMode(*ipd->snap_to_ptr) == SCE_SNAP_TO_NONE) { + ipd->snap_to_ptr = &tool_settings->snap_mode; } + ipd->snap_to_restore = eSnapMode(*ipd->snap_to_ptr); plane_from_point_normal_v3(ipd->step[0].plane, ipd->co_src, ipd->matrix_orient[plane_axis]); @@ -1038,6 +1041,10 @@ static int view3d_interactive_add_modal(bContext *C, wmOperator *op, const wmEve if (ELEM(event->type, ipd->launch_event, LEFTMOUSE)) { if (event->val == KM_RELEASE) { ED_view3d_cursor_snap_state_prevpoint_set(ipd->snap_state, ipd->co_src); + if (ipd->snap_to_restore & SCE_SNAP_TO_GRID) { + /* Don't snap to grid in #STEP_DEPTH. */ + *ipd->snap_to_ptr = ipd->snap_to_restore & ~SCE_SNAP_TO_GRID; + } /* Set secondary plane. */ @@ -1075,7 +1082,10 @@ static int view3d_interactive_add_modal(bContext *C, wmOperator *op, const wmEve else if (ipd->step_index == STEP_DEPTH) { if (ELEM(event->type, ipd->launch_event, LEFTMOUSE)) { if (event->val == KM_PRESS) { + /* Restore snap mode. */ + *ipd->snap_to_ptr = ipd->snap_to_restore; + /* Confirm. */ BoundBox bounds; calc_bbox(ipd, &bounds); @@ -1208,7 +1218,7 @@ static int view3d_interactive_add_modal(bContext *C, wmOperator *op, const wmEve /* pass */ } - if (ipd->use_snap && (ipd->snap_to & SCE_SNAP_TO_INCREMENT)) { + if (ipd->use_snap && (ipd->snap_to_restore & (SCE_SNAP_TO_GRID | SCE_SNAP_TO_INCREMENT))) { if (idp_snap_calc_incremental( ipd->scene, ipd->v3d, ipd->region, ipd->co_src, ipd->step[STEP_BASE].co_dst)) { @@ -1232,7 +1242,7 @@ static int view3d_interactive_add_modal(bContext *C, wmOperator *op, const wmEve /* pass */ } - if (ipd->use_snap && (ipd->snap_to & SCE_SNAP_TO_INCREMENT)) { + if (ipd->use_snap && (ipd->snap_to_restore & (SCE_SNAP_TO_GRID | SCE_SNAP_TO_INCREMENT))) { if (idp_snap_calc_incremental( ipd->scene, ipd->v3d, ipd->region, ipd->co_src, ipd->step[STEP_DEPTH].co_dst)) { diff --git a/source/blender/editors/transform/transform.cc b/source/blender/editors/transform/transform.cc index 6793c9fdf9f..7f6981de587 100644 --- a/source/blender/editors/transform/transform.cc +++ b/source/blender/editors/transform/transform.cc @@ -1884,11 +1884,7 @@ static void initSnapSpatial(TransInfo *t, float r_snap[3], float *r_snap_precisi *r_snap_precision = 0.1f; if (t->spacetype == SPACE_VIEW3D) { - if (t->region->regiondata) { - View3D *v3d = static_cast(t->area->spacedata.first); - r_snap[0] = r_snap[1] = r_snap[2] = ED_view3d_grid_view_scale( - t->scene, v3d, t->region, nullptr); - } + /* Pass. Done in #ED_transform_snap_object_project_view3d_ex. */ } else if (t->spacetype == SPACE_IMAGE) { SpaceImage *sima = static_cast(t->area->spacedata.first); diff --git a/source/blender/editors/transform/transform.hh b/source/blender/editors/transform/transform.hh index 0f1ad26a45b..e05d8856d20 100644 --- a/source/blender/editors/transform/transform.hh +++ b/source/blender/editors/transform/transform.hh @@ -174,9 +174,8 @@ enum eTSnap { SNAP_RESETTED = 0, SNAP_SOURCE_FOUND = 1 << 0, /* Special flag for snap to grid. */ - SNAP_TARGET_GRID_FOUND = 1 << 1, - SNAP_TARGET_FOUND = 1 << 2, - SNAP_MULTI_POINTS = 1 << 3, + SNAP_TARGET_FOUND = 1 << 1, + SNAP_MULTI_POINTS = 1 << 2, }; ENUM_OPERATORS(eTSnap, SNAP_MULTI_POINTS) @@ -316,7 +315,6 @@ struct TransSnap { float snap_source[3]; /** To this point (in global-space). */ float snap_target[3]; - float snap_target_grid[3]; float snapNormal[3]; char snapNodeBorder; ListBase points; diff --git a/source/blender/editors/transform/transform_constraints.cc b/source/blender/editors/transform/transform_constraints.cc index 3031a8cc481..2e18c61db9b 100644 --- a/source/blender/editors/transform/transform_constraints.cc +++ b/source/blender/editors/transform/transform_constraints.cc @@ -404,9 +404,6 @@ static void applyAxisConstraintVec(const TransInfo *t, is_snap_to_face = (t->tsnap.target_type & SCE_SNAP_TO_FACE) != 0; is_snap_to_point = !is_snap_to_edge && !is_snap_to_face; } - else if (t->tsnap.target_type & SCE_SNAP_TO_GRID) { - is_snap_to_point = true; - } } /* Fallback for when axes are aligned. */ diff --git a/source/blender/editors/transform/transform_mode_snapsource.cc b/source/blender/editors/transform/transform_mode_snapsource.cc index c7cfeaa3505..318ecf1c20b 100644 --- a/source/blender/editors/transform/transform_mode_snapsource.cc +++ b/source/blender/editors/transform/transform_mode_snapsource.cc @@ -205,9 +205,9 @@ void transform_mode_snap_source_init(TransInfo *t, wmOperator * /*op*/) t->tsnap.mode &= ~(SCE_SNAP_TO_EDGE_PERPENDICULAR | SCE_SNAP_INDIVIDUAL_PROJECT | SCE_SNAP_INDIVIDUAL_NEAREST); - if ((t->tsnap.mode & ~(SCE_SNAP_TO_INCREMENT | SCE_SNAP_TO_GRID)) == 0) { + if ((t->tsnap.mode & ~SCE_SNAP_TO_INCREMENT) == 0) { /* Initialize snap modes for geometry. */ - t->tsnap.mode &= ~(SCE_SNAP_TO_INCREMENT | SCE_SNAP_TO_GRID); + t->tsnap.mode &= ~SCE_SNAP_TO_INCREMENT; t->tsnap.mode |= SCE_SNAP_TO_GEOM & ~SCE_SNAP_TO_EDGE_PERPENDICULAR; if (!(customdata->snap_mode_confirm & SCE_SNAP_TO_EDGE_PERPENDICULAR)) { diff --git a/source/blender/editors/transform/transform_mode_translate.cc b/source/blender/editors/transform/transform_mode_translate.cc index 212f0b3584f..095ebb009fd 100644 --- a/source/blender/editors/transform/transform_mode_translate.cc +++ b/source/blender/editors/transform/transform_mode_translate.cc @@ -343,99 +343,6 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_ /** \name Transform (Translation) Snapping * \{ */ -static void translate_snap_target_grid_ensure(TransInfo *t) -{ - /* Only need to calculate once. */ - if ((t->tsnap.status & SNAP_TARGET_GRID_FOUND) == 0) { - if (t->data_type == &TransConvertType_Cursor3D) { - /* Use a fallback when transforming the cursor. - * In this case the center is _not_ derived from the cursor which is being transformed. */ - copy_v3_v3(t->tsnap.snap_target_grid, TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->data->iloc); - } - else if (t->around == V3D_AROUND_CURSOR) { - /* Use a fallback for cursor selection, - * this isn't useful as a global center for absolute grid snapping - * since its not based on the position of the selection. */ - tranform_snap_target_median_calc(t, t->tsnap.snap_target_grid); - } - else { - copy_v3_v3(t->tsnap.snap_target_grid, t->center_global); - } - t->tsnap.status |= SNAP_TARGET_GRID_FOUND; - } -} - -static void translate_snap_grid_apply(TransInfo *t, - const int max_index, - const float grid_dist[3], - const float loc[3], - float r_out[3]) -{ - BLI_assert(max_index <= 2); - translate_snap_target_grid_ensure(t); - const float *center_global = t->tsnap.snap_target_grid; - const float *asp = t->aspect; - - float in[3]; - if (t->con.mode & CON_APPLY) { - /* We need to clear the previous Snap to Grid result, - * otherwise #t->con.applyVec will have no effect. */ - t->tsnap.target_type = SCE_SNAP_TO_NONE; - t->con.applyVec(t, nullptr, nullptr, loc, in); - } - else { - copy_v3_v3(in, loc); - } - - for (int i = 0; i <= max_index; i++) { - const float iter_fac = grid_dist[i] * asp[i]; - r_out[i] = iter_fac * roundf((in[i] + center_global[i]) / iter_fac) - center_global[i]; - } - - if ((t->con.mode & CON_APPLY) && - (t->spacemtx[0][0] != 1.0f || t->spacemtx[1][1] != 1.0f || t->spacemtx[2][2] != 1.0f)) - { - /* The space matrix is not identity, we need to constrain the result again. */ - t->con.applyVec(t, nullptr, nullptr, r_out, r_out); - } -} - -static bool translate_snap_grid(TransInfo *t, float *val) -{ - if (!transform_snap_is_active(t)) { - return false; - } - - if (!(t->tsnap.mode & SCE_SNAP_TO_GRID) || validSnap(t)) { - /* Don't do grid snapping if there is a valid snap point. */ - return false; - } - - /* Don't do grid snapping if not in 3D viewport or UV editor. */ - if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE)) { - return false; - } - - if (t->mode != TFM_TRANSLATION) { - return false; - } - - float grid_dist[3]; - copy_v3_v3(grid_dist, t->snap_spatial); - if (t->modifiers & MOD_PRECISION) { - mul_v3_fl(grid_dist, t->snap_spatial_precision); - } - - /* Early bailing out if no need to snap. */ - if (is_zero_v3(grid_dist)) { - return false; - } - - translate_snap_grid_apply(t, t->idx_max, grid_dist, val, val); - t->tsnap.target_type = SCE_SNAP_TO_GRID; - return true; -} - static void ApplySnapTranslation(TransInfo *t, float vec[3]) { float point[3]; @@ -615,7 +522,6 @@ static void applyTranslation(TransInfo *t) } transform_snap_mixed_apply(t, global_dir); - translate_snap_grid(t, global_dir); if (t->con.mode & CON_APPLY) { float in[3]; diff --git a/source/blender/editors/transform/transform_snap.cc b/source/blender/editors/transform/transform_snap.cc index ca8999f0b45..d57458cab7c 100644 --- a/source/blender/editors/transform/transform_snap.cc +++ b/source/blender/editors/transform/transform_snap.cc @@ -68,6 +68,13 @@ static void snap_source_center_fn(TransInfo *t); static void snap_source_closest_fn(TransInfo *t); static void snap_source_active_fn(TransInfo *t); +static eSnapMode snapObjectsTransform(TransInfo *t, + const float mval[2], + const float *vec, + float *dist_px, + float r_loc[3], + float r_no[3]); + /** \} */ /* -------------------------------------------------------------------- */ @@ -552,7 +559,7 @@ static bool transform_snap_mixed_is_active(const TransInfo *t) return (t->tsnap.mode & (SCE_SNAP_TO_VERTEX | SCE_SNAP_TO_EDGE | SCE_SNAP_TO_FACE | SCE_SNAP_TO_VOLUME | - SCE_SNAP_TO_EDGE_MIDPOINT | SCE_SNAP_TO_EDGE_PERPENDICULAR)) != 0; + SCE_SNAP_TO_EDGE_MIDPOINT | SCE_SNAP_TO_EDGE_PERPENDICULAR | SCE_SNAP_TO_GRID)) != 0; } void transform_snap_mixed_apply(TransInfo *t, float *vec) @@ -561,7 +568,7 @@ void transform_snap_mixed_apply(TransInfo *t, float *vec) return; } - if (t->tsnap.mode & ~(SCE_SNAP_TO_INCREMENT | SCE_SNAP_TO_GRID)) { + if (t->tsnap.mode != SCE_SNAP_TO_INCREMENT) { double current = BLI_time_now_seconds(); /* Time base quirky code to go around find-nearest slowness. */ @@ -675,14 +682,7 @@ static eSnapMode snap_mode_from_spacetype(TransInfo *t) } if (t->spacetype == SPACE_IMAGE) { - eSnapMode snap_mode = eSnapMode(ts->snap_uv_mode); - if ((snap_mode & SCE_SNAP_TO_INCREMENT) && (ts->snap_uv_flag & SCE_SNAP_ABS_GRID) && - (t->mode == TFM_TRANSLATION)) - { - snap_mode &= ~SCE_SNAP_TO_INCREMENT; - snap_mode |= SCE_SNAP_TO_GRID; - } - return snap_mode; + return eSnapMode(ts->snap_uv_mode); } if (t->spacetype == SPACE_SEQ) { @@ -694,15 +694,7 @@ static eSnapMode snap_mode_from_spacetype(TransInfo *t) return SCE_SNAP_TO_INCREMENT; } - eSnapMode snap_mode = eSnapMode(ts->snap_mode); - if ((snap_mode & SCE_SNAP_TO_INCREMENT) && (ts->snap_flag & SCE_SNAP_ABS_GRID) && - (t->mode == TFM_TRANSLATION)) - { - /* Special case in which snap to increments is transformed to snap to grid. */ - snap_mode &= ~SCE_SNAP_TO_INCREMENT; - snap_mode |= SCE_SNAP_TO_GRID; - } - return snap_mode; + return eSnapMode(ts->snap_mode); } if (ELEM(t->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_GRAPH)) { @@ -1146,7 +1138,58 @@ static void snap_multipoints_free(TransInfo *t) /** \name Calc Snap * \{ */ -static void snap_target_view3d_fn(TransInfo *t, float * /*vec*/) +static void snap_grid_uv_apply(TransInfo *t, + const float grid_dist[2], + const float vec[2], + float r_out[2]) +{ + const float *center_global = t->center_global; + float in[2]; + if (t->con.mode & CON_APPLY) { + /* We need to clear the previous Snap to Grid result, + * otherwise #t->con.applyVec will have no effect. */ + t->tsnap.target_type = SCE_SNAP_TO_NONE; + t->con.applyVec(t, nullptr, nullptr, vec, in); + } + else { + copy_v2_v2(in, vec); + } + + for (int i = 0; i < 2; i++) { + const float iter_fac = grid_dist[i]; + r_out[i] = iter_fac * roundf((in[i] + center_global[i]) / iter_fac); + } +} + +static bool snap_grid_uv(TransInfo *t, float vec[2], float r_val[2]) +{ + if (t->mode != TFM_TRANSLATION) { + return false; + } + + float grid_dist[2]; + mul_v2_v2v2(grid_dist, t->snap_spatial, t->aspect); + if (t->modifiers & MOD_PRECISION) { + mul_v2_fl(grid_dist, t->snap_spatial_precision); + } + + /* Early bailing out if no need to snap */ + if (is_zero_v2(grid_dist)) { + return false; + } + + snap_grid_uv_apply(t, grid_dist, vec, r_val); + t->tsnap.target_type = SCE_SNAP_TO_GRID; + return true; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Calc Snap + * \{ */ + +static void snap_target_view3d_fn(TransInfo *t, float *vec) { BLI_assert(t->spacetype == SPACE_VIEW3D); float loc[3]; @@ -1155,9 +1198,9 @@ static void snap_target_view3d_fn(TransInfo *t, float * /*vec*/) eSnapMode snap_elem = SCE_SNAP_TO_NONE; float dist_px = SNAP_MIN_DISTANCE; /* Use a user defined value here. */ - if (t->tsnap.mode & SCE_SNAP_TO_GEOM) { - zero_v3(no); /* Objects won't set this. */ - snap_elem = snapObjectsTransform(t, t->mval, &dist_px, loc, no); + if (t->tsnap.mode & (SCE_SNAP_TO_GEOM | SCE_SNAP_TO_GRID)) { + zero_v3(no); /* objects won't set this */ + snap_elem = snapObjectsTransform(t, t->mval, vec, &dist_px, loc, no); found = (snap_elem != SCE_SNAP_TO_NONE); } if ((found == false) && (t->tsnap.mode & SCE_SNAP_TO_VOLUME)) { @@ -1174,6 +1217,11 @@ static void snap_target_view3d_fn(TransInfo *t, float * /*vec*/) copy_v3_v3(t->tsnap.snapNormal, no); t->tsnap.status |= SNAP_TARGET_FOUND; + + if (snap_elem == SCE_SNAP_TO_GRID && t->mode_info != &TransMode_translate) { + /* Change it to #SCE_SNAP_TO_POINT so we can see the symbol for other modes. */ + snap_elem = SCE_SNAP_TO_POINT; + } } else { t->tsnap.status &= ~SNAP_TARGET_FOUND; @@ -1182,10 +1230,10 @@ static void snap_target_view3d_fn(TransInfo *t, float * /*vec*/) t->tsnap.target_type = snap_elem; } -static void snap_target_uv_fn(TransInfo *t, float * /*vec*/) +static void snap_target_uv_fn(TransInfo *t, float *vec) { BLI_assert(t->spacetype == SPACE_IMAGE); - if (t->tsnap.mode & SCE_SNAP_TO_VERTEX) { + if (t->tsnap.mode & (SCE_SNAP_TO_VERTEX | SCE_SNAP_TO_GRID)) { const Vector objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( t->scene, t->view_layer, nullptr); @@ -1204,6 +1252,9 @@ static void snap_target_uv_fn(TransInfo *t, float * /*vec*/) t->tsnap.status |= SNAP_TARGET_FOUND; } + else if ((t->tsnap.mode & SCE_SNAP_TO_GRID) && snap_grid_uv(t, vec, t->tsnap.snap_target)) { + t->tsnap.status |= SNAP_TARGET_FOUND; + } else { t->tsnap.status &= ~SNAP_TARGET_FOUND; } @@ -1358,7 +1409,17 @@ static void snap_source_median_fn(TransInfo *t) static void snap_source_closest_fn(TransInfo *t) { /* Only valid if a snap point has been selected. */ - if (t->tsnap.status & SNAP_TARGET_FOUND) { + if (!(t->tsnap.status & SNAP_TARGET_FOUND)) { + return; + } + + if (t->tsnap.target_type == SCE_SNAP_TO_GRID) { + /* Previously Snap to Grid had its own snap source which was always the result of + * #snap_source_median_fn. Now this mode shares the same code, so to not change the behavior + * too much when using Closest, use the transform pivot as the snap source in this case. */ + copy_v3_v3(t->tsnap.snap_source, t->center_global); + } + else { float dist_closest = 0.0f; TransData *closest = nullptr; @@ -1445,10 +1506,10 @@ static void snap_source_closest_fn(TransInfo *t) } TargetSnapOffset(t, closest); - - t->tsnap.status |= SNAP_SOURCE_FOUND; - t->tsnap.source_type = SCE_SNAP_TO_NONE; } + + t->tsnap.status |= SNAP_SOURCE_FOUND; + t->tsnap.source_type = SCE_SNAP_TO_NONE; } /** \} */ @@ -1457,8 +1518,12 @@ static void snap_source_closest_fn(TransInfo *t) /** \name Snap Objects * \{ */ -eSnapMode snapObjectsTransform( - TransInfo *t, const float mval[2], float *dist_px, float r_loc[3], float r_no[3]) +static eSnapMode snapObjectsTransform(TransInfo *t, + const float mval[2], + const float *vec, + float *dist_px, + float r_loc[3], + float r_no[3]) { SnapObjectParams snap_object_params{}; snap_object_params.snap_target_select = t->tsnap.target_operation; @@ -1467,6 +1532,16 @@ eSnapMode snapObjectsTransform( 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; + float *grid_co = nullptr, grid_co_stack[3]; + if ((t->tsnap.mode & SCE_SNAP_TO_GRID) && (t->con.mode & CON_APPLY) && + (t->mode == TFM_TRANSLATION)) + { + /* Without this position adjustment, the snap may be far from the expected constraint point. */ + grid_co = grid_co_stack; + t->tsnap.status &= ~SNAP_TARGET_FOUND; + t->con.applyVec(t, nullptr, nullptr, vec, grid_co); + add_v3_v3(grid_co, t->center_global); + } return ED_transform_snap_object_project_view3d(t->tsnap.object_context, t->depsgraph, @@ -1474,7 +1549,7 @@ eSnapMode snapObjectsTransform( static_cast(t->view), t->tsnap.mode, &snap_object_params, - nullptr, + grid_co, mval, prev_co, dist_px, diff --git a/source/blender/editors/transform/transform_snap.hh b/source/blender/editors/transform/transform_snap.hh index 4d276554845..bcfc6c1498c 100644 --- a/source/blender/editors/transform/transform_snap.hh +++ b/source/blender/editors/transform/transform_snap.hh @@ -22,12 +22,6 @@ bool peelObjectsTransform(TransInfo *t, float r_no[3], float *r_thickness); -eSnapMode snapObjectsTransform(TransInfo *t, - const float mval[2], - float *dist_px, - /* Return args. */ - float r_loc[3], - float r_no[3]); bool snapNodesTransform(TransInfo *t, const blender::float2 &mval, /* Return args. */ diff --git a/source/blender/editors/transform/transform_snap_object.cc b/source/blender/editors/transform/transform_snap_object.cc index ca25d68e494..c78210f5569 100644 --- a/source/blender/editors/transform/transform_snap_object.cc +++ b/source/blender/editors/transform/transform_snap_object.cc @@ -139,7 +139,7 @@ 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 && + const bool is_in_front = sctx->runtime.params.use_occlusion_test && 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); @@ -964,6 +964,54 @@ static eSnapMode snapObjectsRay(SnapObjectContext *sctx) return iter_snap_objects(sctx, snap_obj_fn); } +static bool snap_grid(SnapObjectContext *sctx) +{ + SnapData nearest2d(sctx); + nearest2d.clip_planes_enable(sctx, nullptr); + + /* Ignore the maximum pixel distance when snapping to grid. + * This avoids undesirable jumps of the element being snapped. */ + nearest2d.nearest_point.dist_sq = FLT_MAX; + + float grid_dist = sctx->grid.size; + + if (sctx->grid.use_init_co) { + float3 co = math::round((sctx->runtime.init_co) / grid_dist) * grid_dist; + if (nearest2d.snap_point(co)) { + nearest2d.register_result(sctx, nullptr, nullptr); + return true; + } + } + + float ray_dist; + for (int i = 0; i < 4; i++) { + if (isect_ray_plane_v3(sctx->runtime.ray_start, + sctx->runtime.ray_dir, + sctx->grid.planes[i], + &ray_dist, + false) && + IN_RANGE_INCL(ray_dist, 0.0f, sctx->ret.ray_depth_max)) + { + float3 co = math::round((sctx->runtime.ray_start + sctx->runtime.ray_dir * ray_dist) / + grid_dist) * + grid_dist; + + if (nearest2d.snap_point(co)) { + copy_v3_v3(nearest2d.nearest_point.no, sctx->grid.planes[i]); + if (!sctx->runtime.rv3d->is_persp && RV3D_VIEW_IS_AXIS(sctx->runtime.rv3d->view)) { + /* Project in #sctx->runtime.curr_co plane. */ + add_v3_v3(nearest2d.nearest_point.co, + sctx->runtime.curr_co * float3(nearest2d.nearest_point.no)); + } + nearest2d.register_result(sctx, nullptr, nullptr); + return true; + } + } + } + + return false; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -1030,7 +1078,9 @@ static bool snap_object_context_runtime_init(SnapObjectContext *sctx, ListBase *hit_list, bool use_occlusion_test) { - if (snap_to_flag & (SCE_SNAP_TO_EDGE_PERPENDICULAR | SCE_SNAP_INDIVIDUAL_NEAREST)) { + if (snap_to_flag & + (SCE_SNAP_TO_GRID | SCE_SNAP_TO_EDGE_PERPENDICULAR | SCE_SNAP_INDIVIDUAL_NEAREST)) + { if (prev_co) { copy_v3_v3(sctx->runtime.curr_co, prev_co); if (init_co) { @@ -1088,6 +1138,30 @@ static bool snap_object_context_runtime_init(SnapObjectContext *sctx, } sctx->runtime.rv3d = rv3d; + + if (sctx->runtime.snap_to_flag & SCE_SNAP_TO_GRID) { + if (init_co) { + sctx->grid.use_init_co = true; + } + else if (!compare_m4m4(sctx->grid.persmat.ptr(), rv3d->persmat, FLT_EPSILON)) { + sctx->grid.persmat = float4x4(rv3d->persmat); + memset(sctx->grid.planes, 0, sizeof(sctx->grid.planes)); + sctx->grid.planes[0][2] = 1.0f; + if (math::abs(sctx->runtime.ray_dir[0]) < math::abs(sctx->runtime.ray_dir[1])) { + sctx->grid.planes[1][1] = 1.0f; + sctx->grid.planes[2][0] = 1.0f; + } + else { + sctx->grid.planes[1][0] = 1.0f; + sctx->grid.planes[2][1] = 1.0f; + } + + plane_from_point_normal_v3(sctx->grid.planes[3], sctx->runtime.curr_co, rv3d->viewinv[2]); + + sctx->grid.size = ED_view3d_grid_view_scale( + sctx->scene, sctx->runtime.v3d, region, nullptr); + } + } } sctx->ret.ray_depth_max = sctx->ret.ray_depth_max_in_front = ray_depth; @@ -1251,7 +1325,12 @@ eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, use_occlusion_plane = is_allways_occluded || !XRAY_ENABLED(v3d); } - if (use_occlusion_plane || (snap_to_flag & SCE_SNAP_TO_FACE)) { + if (use_occlusion_plane || (snap_to_flag & (SCE_SNAP_TO_FACE | SCE_SNAP_TO_GRID))) { + /* 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. + * This is required for raycast or snap to grid. */ + const RegionView3D *rv3d = static_cast(region->regiondata); float3 ray_end; ED_view3d_win_to_ray_clipped_ex(depsgraph, @@ -1302,7 +1381,7 @@ eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, snap_to_flag = sctx->runtime.snap_to_flag; - BLI_assert(snap_to_flag & (SCE_SNAP_TO_GEOM | SCE_SNAP_INDIVIDUAL_NEAREST)); + BLI_assert(snap_to_flag & (SCE_SNAP_TO_GEOM | SCE_SNAP_TO_GRID | SCE_SNAP_INDIVIDUAL_NEAREST)); bool has_hit = false; @@ -1312,21 +1391,7 @@ eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, has_hit = nearestWorldObjects(sctx); if (has_hit) { - retval = SCE_SNAP_INDIVIDUAL_NEAREST; - - copy_v3_v3(r_loc, sctx->ret.loc); - if (r_no) { - copy_v3_v3(r_no, sctx->ret.no); - } - if (r_ob) { - *r_ob = sctx->ret.ob; - } - if (r_obmat) { - copy_m4_m4(r_obmat, sctx->ret.obmat.ptr()); - } - if (r_index) { - *r_index = sctx->ret.index; - } + retval |= SCE_SNAP_INDIVIDUAL_NEAREST; } } @@ -1339,21 +1404,7 @@ eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, } if (snap_to_flag & SCE_SNAP_TO_FACE) { - retval = SCE_SNAP_TO_FACE; - - copy_v3_v3(r_loc, sctx->ret.loc); - if (r_no) { - copy_v3_v3(r_no, sctx->ret.no); - } - if (r_ob) { - *r_ob = sctx->ret.ob; - } - if (r_obmat) { - copy_m4_m4(r_obmat, sctx->ret.obmat.ptr()); - } - if (r_index) { - *r_index = sctx->ret.index; - } + retval |= SCE_SNAP_TO_FACE; } } } @@ -1392,26 +1443,32 @@ eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, elem = snap_edge_points(sctx, square_f(*dist_px)); } - if (elem & snap_to_flag) { - retval = elem; + retval |= elem & snap_to_flag; + } - copy_v3_v3(r_loc, sctx->ret.loc); - if (r_no) { - copy_v3_v3(r_no, sctx->ret.no); - } - if (r_ob) { - *r_ob = sctx->ret.ob; - } - if (r_obmat) { - copy_m4_m4(r_obmat, sctx->ret.obmat.ptr()); - } - if (r_index) { - *r_index = sctx->ret.index; - } + if ((retval == SCE_SNAP_TO_NONE) && (snap_to_flag & SCE_SNAP_TO_GRID)) { + if (snap_grid(sctx)) { + retval = SCE_SNAP_TO_GRID; + } + } - if (dist_px) { - *dist_px = math::sqrt(sctx->ret.dist_px_sq); - } + if (retval != SCE_SNAP_TO_NONE) { + copy_v3_v3(r_loc, sctx->ret.loc); + if (r_no) { + copy_v3_v3(r_no, sctx->ret.no); + } + if (r_ob) { + *r_ob = sctx->ret.ob; + } + if (r_obmat) { + copy_m4_m4(r_obmat, sctx->ret.obmat.ptr()); + } + if (r_index) { + *r_index = sctx->ret.index; + } + + if (dist_px) { + *dist_px = math::sqrt(sctx->ret.dist_px_sq); } } diff --git a/source/blender/editors/transform/transform_snap_object.hh b/source/blender/editors/transform/transform_snap_object.hh index 50d16d439ec..7418f32f4e3 100644 --- a/source/blender/editors/transform/transform_snap_object.hh +++ b/source/blender/editors/transform/transform_snap_object.hh @@ -35,6 +35,14 @@ struct SnapObjectContext { } edit_mesh; } callbacks; + struct { + /* Compare with #RegionView3D::persmat to update. */ + blender::float4x4 persmat; + blender::float4 planes[4]; + float size; + bool use_init_co; + } grid; + struct { Depsgraph *depsgraph; const RegionView3D *rv3d; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 15df4da7c9a..32293203519 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -2347,7 +2347,7 @@ typedef enum eSnapFlag { // SCE_SNAP_PROJECT = (1 << 3), /* DEPRECATED, see #SCE_SNAP_INDIVIDUAL_PROJECT. */ /** Was `SCE_SNAP_NO_SELF`, but self should be active. */ SCE_SNAP_NOT_TO_ACTIVE = (1 << 4), - SCE_SNAP_ABS_GRID = (1 << 5), + /* SCE_SNAP_ABS_GRID = (1 << 5), */ /* UNUSED */ /* Same value with different name to make it easier to understand in time based code. */ SCE_SNAP_ABS_TIME_STEP = (1 << 5), SCE_SNAP_BACKFACE_CULLING = (1 << 6), diff --git a/source/blender/makesrna/intern/rna_scene.cc b/source/blender/makesrna/intern/rna_scene.cc index 018b7237ea4..78d286614b1 100644 --- a/source/blender/makesrna/intern/rna_scene.cc +++ b/source/blender/makesrna/intern/rna_scene.cc @@ -157,6 +157,7 @@ const EnumPropertyItem rna_enum_mesh_select_mode_uv_items[] = { /* clang-format off */ #define RNA_SNAP_ELEMENTS_BASE \ {SCE_SNAP_TO_INCREMENT, "INCREMENT", ICON_SNAP_INCREMENT, "Increment", "Snap to increments"}, \ + {SCE_SNAP_TO_GRID, "GRID", ICON_SNAP_GRID, "Grid", "Snap to grid"}, \ {SCE_SNAP_TO_VERTEX, "VERTEX", ICON_SNAP_VERTEX, "Vertex", "Snap to vertices"}, \ {SCE_SNAP_TO_EDGE, "EDGE", ICON_SNAP_EDGE, "Edge", "Snap to edges"}, \ {SCE_SNAP_TO_FACE, "FACE", ICON_SNAP_FACE, "Face", "Snap by projecting onto faces"}, \ @@ -217,6 +218,7 @@ static const EnumPropertyItem snap_uv_element_items[] = { ICON_SNAP_INCREMENT, "Increment", "Snap to increments of grid"}, + {SCE_SNAP_TO_GRID, "GRID", ICON_SNAP_GRID, "Grid", "Snap to grid"}, {SCE_SNAP_TO_VERTEX, "VERTEX", ICON_SNAP_VERTEX, "Vertex", "Snap to vertices"}, {0, nullptr, 0, nullptr, nullptr}, }; @@ -3513,15 +3515,6 @@ static void rna_def_tool_settings(BlenderRNA *brna) prop, "Align Rotation to Target", "Align rotation with the snapping target"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); /* header redraw */ - prop = RNA_def_property(srna, "use_snap_grid_absolute", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "snap_flag", SCE_SNAP_ABS_GRID); - RNA_def_property_flag(prop, PROP_DEG_SYNC_ONLY); - RNA_def_property_ui_text( - prop, - "Absolute Grid Snap", - "Absolute grid alignment while translating (based on the pivot center)"); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); /* header redraw */ - prop = RNA_def_property(srna, "snap_angle_increment_2d", PROP_FLOAT, PROP_ANGLE); RNA_def_property_float_sdna(prop, nullptr, "snap_angle_increment_2d"); RNA_def_property_ui_text( @@ -3643,15 +3636,6 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Snap UV Element", "Type of element to snap to"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); /* header redraw */ - prop = RNA_def_property(srna, "use_snap_uv_grid_absolute", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "snap_uv_flag", SCE_SNAP_ABS_GRID); - RNA_def_property_flag(prop, PROP_DEG_SYNC_ONLY); - RNA_def_property_ui_text( - prop, - "Absolute Grid Snap", - "Absolute grid alignment while translating (based on the pivot center)"); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); /* header redraw */ - /* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid previous ambiguity of "target" * (now, "source" is geometry to be moved and "target" is geometry to which moved geometry is * snapped). */