Transform: Implement Snap to Grid mode
Addresses two improvements to `Snap to Grid` as suggested in #73993: - Make 'Absolute Grid Snapping' a new Snap Mode - Snap to Grid in Non-Side View performed at ground level Implementation details: - Snap to Grid has no cursor symbol, unless we are in `Set Snap Base` mode. Similar to the previous 'Absolute Grid Snap' behavior. - In Non-Side view, Snap to Grid is performed at ground level. - If `Snap Base` is `Closest`, Snap to Grid uses the transform pivot point instead. Similar to the previous 'Absolute Grid Snap' behavior. - The "Absolute Grid Snap" option has been removed. Pull Request: https://projects.blender.org/blender/blender/pulls/116109
This commit is contained in:
parent
b3fe97414a
commit
f0479e915f
@ -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")
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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<RegionView3D *>(region->regiondata);
|
||||
const RegionView3D *rv3d = static_cast<RegionView3D *>(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]);
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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<View3D *>(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<SpaceImage *>(t->area->spacedata.first);
|
||||
|
@ -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;
|
||||
|
@ -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. */
|
||||
|
@ -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)) {
|
||||
|
@ -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];
|
||||
|
@ -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<Object *> 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;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@ -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<const View3D *>(t->view),
|
||||
t->tsnap.mode,
|
||||
&snap_object_params,
|
||||
nullptr,
|
||||
grid_co,
|
||||
mval,
|
||||
prev_co,
|
||||
dist_px,
|
||||
|
@ -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. */
|
||||
|
@ -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<const RegionView3D *>(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,9 +1443,16 @@ 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;
|
||||
}
|
||||
|
||||
if ((retval == SCE_SNAP_TO_NONE) && (snap_to_flag & SCE_SNAP_TO_GRID)) {
|
||||
if (snap_grid(sctx)) {
|
||||
retval = SCE_SNAP_TO_GRID;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@ -1413,7 +1471,6 @@ eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx,
|
||||
*dist_px = math::sqrt(sctx->ret.dist_px_sq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_SNAP_TIME
|
||||
duration_ += timeit::Clock::now() - start_;
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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). */
|
||||
|
Loading…
Reference in New Issue
Block a user