Transform: new feature to edit the 'Snap Base'

This commit implements a new modifier key (`B`) for the transform
operators.

This new key allows changing the 'Snap Base' of a transform by snapping
it to a defined point in the scene.

Ref #66424

# Implementation Details

- This feature is only available in the 3D View.
- This feature is only available for the transform modes:
  - `Move`,
  - `Rotate`,
  - `Scale`,
  - `Vert Slide` and
  - `Edge Slide`.
- The `Snap Base Edit` is enabled while we are transforming and we
  press the key `B`
- The `Snap Base Edit` is confirmed when we press any of the keys:
 `B`, `LMB`, `Enter`
- During um operation, if no snap target is set for an element in the
  scene (Vertex, Edge...), the snap targets to geometry Vertex, Edge,
  Face, Center of Edge and Perpendicular of Edge are set automatically.
- Constraint or similar modal features are not available during the
  `Snap Base Edit` mode.
- Text input is not available during the `Snap Base Edit` mode.
- A prone snap base point is indicated with an small cursor drawing.

Pull Request: https://projects.blender.org/blender/blender/pulls/104443
This commit is contained in:
Germano Cavalcante
2023-06-03 04:18:49 +02:00
parent 0c0cd10e55
commit 3010f1233b
16 changed files with 562 additions and 50 deletions

View File

@@ -80,4 +80,16 @@ def keyconfig_update(keyconfig_data, keyconfig_version):
km_items.append(('ROTATE_NORMALS', {"type": 'N', "value": 'PRESS'}, None))
break
if keyconfig_version <= (3, 6, 3):
if not has_copy:
keyconfig_data = copy.deepcopy(keyconfig_data)
has_copy = True
# "Snap Source Toggle" did not exist until then.
for km_name, _km_parms, km_items_data in keyconfig_data:
if km_name == "Transform Modal Map":
km_items_data["items"].extend(("EDIT_SNAP_SOURCE_ON", {"type": 'B', "value": 'PRESS'}, None))
km_items_data["items"].append(("EDIT_SNAP_SOURCE_OFF", {"type": 'B', "value": 'PRESS'}, None))
break
return keyconfig_data

View File

@@ -5915,6 +5915,8 @@ def km_transform_modal_map(params):
("TRACKBALL", {"type": 'R', "value": 'PRESS'}, None),
("RESIZE", {"type": 'S', "value": 'PRESS'}, None),
("ROTATE_NORMALS", {"type": 'N', "value": 'PRESS'}, None),
("EDIT_SNAP_SOURCE_ON", {"type": 'B', "value": 'PRESS'}, None),
("EDIT_SNAP_SOURCE_OFF", {"type": 'B', "value": 'PRESS'}, None),
("SNAP_TOGGLE", {"type": 'TAB', "value": 'PRESS', "shift": True}, None),
("SNAP_INV_ON", {"type": 'LEFT_CTRL', "value": 'PRESS', "any": True}, None),
("SNAP_INV_OFF", {"type": 'LEFT_CTRL', "value": 'RELEASE', "any": True}, None),

View File

@@ -4037,6 +4037,8 @@ def km_transform_modal_map(_params):
("TRACKBALL", {"type": 'R', "value": 'PRESS'}, None),
("RESIZE", {"type": 'S', "value": 'PRESS'}, None),
("ROTATE_NORMALS", {"type": 'N', "value": 'PRESS'}, None),
("EDIT_SNAP_SOURCE_ON", {"type": 'B', "value": 'PRESS'}, None),
("EDIT_SNAP_SOURCE_OFF", {"type": 'B', "value": 'PRESS'}, None),
("SNAP_TOGGLE", {"type": 'TAB', "value": 'PRESS', "shift": True}, None),
("SNAP_INV_ON", {"type": 'LEFT_CTRL', "value": 'PRESS', "any": True}, None),
("SNAP_INV_OFF", {"type": 'LEFT_CTRL', "value": 'RELEASE', "any": True}, None),

View File

@@ -27,7 +27,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 3
#define BLENDER_FILE_SUBVERSION 4
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file

View File

@@ -84,6 +84,7 @@ set(SRC
transform_mode_shear.c
transform_mode_shrink_fatten.c
transform_mode_skin_resize.c
transform_mode_snapsource.c
transform_mode_tilt.c
transform_mode_timescale.c
transform_mode_timeslide.c

View File

@@ -543,6 +543,20 @@ static void viewRedrawPost(bContext *C, TransInfo *t)
static bool transform_modal_item_poll(const wmOperator *op, int value)
{
const TransInfo *t = op->customdata;
if (t->modifiers & MOD_EDIT_SNAP_SOURCE) {
if (value == TFM_MODAL_EDIT_SNAP_SOURCE_OFF) {
return true;
}
else if (!ELEM(value,
TFM_MODAL_CANCEL,
TFM_MODAL_CONFIRM,
TFM_MODAL_ADD_SNAP,
TFM_MODAL_REMOVE_SNAP))
{
return false;
}
}
switch (value) {
case TFM_MODAL_CANCEL: {
/* TODO: Canceling with LMB is not possible when the operator is activated
@@ -670,6 +684,20 @@ static bool transform_modal_item_poll(const wmOperator *op, int value)
}
break;
}
case TFM_MODAL_EDIT_SNAP_SOURCE_OFF:
return false;
case TFM_MODAL_EDIT_SNAP_SOURCE_ON: {
if (t->spacetype != SPACE_VIEW3D) {
return false;
}
if (!ELEM(
t->mode, TFM_TRANSLATION, TFM_ROTATION, TFM_RESIZE, TFM_EDGE_SLIDE, TFM_VERT_SLIDE))
{
/* More modes can be added over time if this feature proves useful for them. */
return false;
}
break;
}
}
return true;
}
@@ -686,6 +714,8 @@ wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf)
{TFM_MODAL_PLANE_Y, "PLANE_Y", 0, "Y Plane", ""},
{TFM_MODAL_PLANE_Z, "PLANE_Z", 0, "Z Plane", ""},
{TFM_MODAL_CONS_OFF, "CONS_OFF", 0, "Clear Constraints", ""},
{TFM_MODAL_EDIT_SNAP_SOURCE_ON, "EDIT_SNAP_SOURCE_ON", 0, "Set Snap Base", ""},
{TFM_MODAL_EDIT_SNAP_SOURCE_OFF, "EDIT_SNAP_SOURCE_OFF", 0, "Set Snap Base (Off)", ""},
{TFM_MODAL_SNAP_INV_ON, "SNAP_INV_ON", 0, "Snap Invert", ""},
{TFM_MODAL_SNAP_INV_OFF, "SNAP_INV_OFF", 0, "Snap Invert (Off)", ""},
{TFM_MODAL_SNAP_TOGGLE, "SNAP_TOGGLE", 0, "Snap Toggle", ""},
@@ -956,12 +986,16 @@ int transformEvent(TransInfo *t, const wmEvent *event)
else if (event->type == EVT_MODAL_MAP) {
switch (event->val) {
case TFM_MODAL_CANCEL:
t->state = TRANS_CANCEL;
handled = true;
if (!(t->modifiers & MOD_EDIT_SNAP_SOURCE)) {
t->state = TRANS_CANCEL;
handled = true;
}
break;
case TFM_MODAL_CONFIRM:
t->state = TRANS_CONFIRM;
handled = true;
if (!(t->modifiers & MOD_EDIT_SNAP_SOURCE)) {
t->state = TRANS_CONFIRM;
handled = true;
}
break;
case TFM_MODAL_TRANSLATE:
case TFM_MODAL_ROTATE:
@@ -1246,6 +1280,10 @@ int transformEvent(TransInfo *t, const wmEvent *event)
t->redraw |= TREDRAW_HARD;
}
break;
case TFM_MODAL_EDIT_SNAP_SOURCE_ON:
transform_mode_snap_source_init(t);
t->redraw |= TREDRAW_HARD;
break;
default:
break;
}

View File

@@ -158,6 +158,7 @@ typedef enum {
MOD_CONSTRAINT_SELECT_PLANE = 1 << 4,
MOD_NODE_ATTACH = 1 << 5,
MOD_SNAP_FORCED = 1 << 6,
MOD_EDIT_SNAP_SOURCE = 1 << 7,
} eTModifier;
ENUM_OPERATORS(eTModifier, MOD_NODE_ATTACH)
@@ -271,6 +272,9 @@ enum {
TFM_MODAL_VERT_EDGE_SLIDE = 31,
TFM_MODAL_TRACKBALL = 32,
TFM_MODAL_ROTATE_NORMALS = 33,
TFM_MODAL_EDIT_SNAP_SOURCE_ON = 34,
TFM_MODAL_EDIT_SNAP_SOURCE_OFF = 35,
};
/** \} */
@@ -753,6 +757,7 @@ void applyMouseInput(struct TransInfo *t,
float output[3]);
void transform_input_update(TransInfo *t, const float fac);
void transform_input_virtual_mval_reset(TransInfo *t);
void transform_input_reset(TransInfo *t, const int mval[2]);
void setCustomPoints(TransInfo *t, MouseInput *mi, const int start[2], const int end[2]);
void setCustomPointsFromDirection(TransInfo *t, MouseInput *mi, const float dir[2]);

View File

@@ -510,4 +510,16 @@ void transform_input_virtual_mval_reset(TransInfo *t)
}
}
void transform_input_reset(TransInfo *t, const int mval[2])
{
MouseInput *mi = &t->mouse;
copy_v2_v2_int(mi->imval, mval);
if (ELEM(mi->apply, InputAngle, InputAngleSpring)) {
struct InputAngle_Data *data = mi->data;
data->mval_prev[0] = mi->imval[0];
data->mval_prev[1] = mi->imval[1];
data->angle = 0.0f;
}
}
/** \} */

View File

@@ -184,6 +184,11 @@ extern TransModeInfo TransMode_shrinkfatten;
extern TransModeInfo TransMode_skinresize;
/* transform_mode_snapsource.c */
extern TransModeInfo TransMode_snapsource;
void transform_mode_snap_source_init(TransInfo *t);
/* transform_mode_tilt.c */
extern TransModeInfo TransMode_tilt;

View File

@@ -1480,6 +1480,41 @@ static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2]))
ED_area_status_text(t->area, str);
}
static void edge_slide_transform_matrix_fn(struct TransInfo *t, float mat_xform[4][4])
{
float delta[3], orig_co[3], final_co[3];
EdgeSlideParams *slp = t->custom.mode.data;
TransDataContainer *tc = edge_slide_container_first_ok(t);
EdgeSlideData *sld_active = tc->custom.mode.data;
TransDataEdgeSlideVert *sv_active = &sld_active->sv[sld_active->curr_sv_index];
copy_v3_v3(orig_co, sv_active->v_co_orig);
const float fac = t->values_final[0];
float curr_length_fac = 0.0f;
if (slp->use_even) {
curr_length_fac = sv_active->edge_len * (((slp->flipped ? fac : -fac) + 1.0f) / 2.0f);
}
edge_slide_apply_elem(sv_active,
fac,
curr_length_fac,
slp->curr_side_unclamp,
!(t->flag & T_ALT_TRANSFORM),
slp->use_even,
slp->flipped,
final_co);
if (tc->use_local_mat) {
mul_m4_v3(tc->mat, orig_co);
mul_m4_v3(tc->mat, final_co);
}
sub_v3_v3v3(delta, final_co, orig_co);
add_v3_v3(mat_xform[3], delta);
}
static void initEdgeSlide_ex(
TransInfo *t, bool use_double_side, bool use_even, bool flipped, bool use_clamp)
{
@@ -1585,7 +1620,7 @@ TransModeInfo TransMode_edgeslide = {
/*flags*/ T_NO_CONSTRAINT | T_NO_PROJECT,
/*init_fn*/ initEdgeSlide,
/*transform_fn*/ applyEdgeSlide,
/*transform_matrix_fn*/ NULL,
/*transform_matrix_fn*/ edge_slide_transform_matrix_fn,
/*handle_event_fn*/ handleEventEdgeSlide,
/*snap_distance_fn*/ transform_snap_distance_len_squared_fn,
/*snap_apply_fn*/ edge_slide_snap_apply,

View File

@@ -286,6 +286,14 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2]))
ED_area_status_text(t->area, str);
}
static void resize_transform_matrix_fn(struct TransInfo *t, float mat_xform[4][4])
{
float mat4[4][4];
copy_m4_m3(mat4, t->mat);
transform_pivot_set_m4(mat4, t->center_global);
mul_m4_m4m4(mat_xform, mat4, mat_xform);
}
static void initResize(TransInfo *t, wmOperator *op)
{
float mouse_dir_constraint[3];
@@ -368,7 +376,7 @@ TransModeInfo TransMode_resize = {
/*flags*/ T_NULL_ONE,
/*init_fn*/ initResize,
/*transform_fn*/ applyResize,
/*transform_matrix_fn*/ NULL,
/*transform_matrix_fn*/ resize_transform_matrix_fn,
/*handle_event_fn*/ NULL,
/*snap_distance_fn*/ ResizeBetween,
/*snap_apply_fn*/ ApplySnapResize,

View File

@@ -0,0 +1,253 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
/** \file
* \ingroup edtransform
*/
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "DNA_windowmanager_types.h"
#include "BKE_context.h"
#include "ED_screen.h"
#include "ED_transform_snap_object_context.h"
#include "transform.h"
#include "transform_convert.h"
#include "transform_gizmo.h"
#include "transform_snap.h"
#include "transform_mode.h"
#define RESET_TRANSFORMATION
#define REMOVE_GIZMO
/* -------------------------------------------------------------------- */
/** \name Transform Element
* \{ */
/**
* \note Small arrays / data-structures should be stored copied for faster memory access.
*/
struct SnapSouceCustomData {
TransModeInfo *mode_info_prev;
void *customdata_mode_prev;
eSnapTargetOP target_operation_prev;
struct {
void (*apply)(struct TransInfo *t,
struct MouseInput *mi,
const double mval[2],
float output[3]);
void (*post)(struct TransInfo *t, float values[3]);
bool use_virtual_mval;
} mouse_prev;
};
static void snapsource_end(TransInfo *t)
{
t->modifiers &= ~MOD_EDIT_SNAP_SOURCE;
/* Restore. */
struct SnapSouceCustomData *customdata = t->custom.mode.data;
t->mode_info = customdata->mode_info_prev;
t->custom.mode.data = customdata->customdata_mode_prev;
t->tsnap.target_operation = customdata->target_operation_prev;
t->mouse.apply = customdata->mouse_prev.apply;
t->mouse.post = customdata->mouse_prev.post;
t->mouse.use_virtual_mval = customdata->mouse_prev.use_virtual_mval;
transform_gizmo_3d_model_from_constraint_and_mode_set(t);
MEM_freeN(customdata);
t->tsnap.snap_source_fn = NULL;
tranform_snap_source_restore_context(t);
}
static void snapsource_confirm(TransInfo *t)
{
BLI_assert(t->modifiers & MOD_EDIT_SNAP_SOURCE);
getSnapPoint(t, t->tsnap.snap_source);
int mval[2];
#ifndef RESET_TRANSFORMATION
if (true) {
if (t->transform_matrix) {
float mat_inv[4][4];
unit_m4(mat_inv);
t->transform_matrix(t, mat_inv);
invert_m4(mat_inv);
mul_m4_v3(mat_inv, t->tsnap.snap_source);
}
else {
float mat_inv[3][3];
invert_m3_m3(mat_inv, t->mat);
mul_m3_v3(mat_inv, t->tsnap.snap_source);
sub_v3_v3(t->tsnap.snap_source, t->vec);
}
projectIntView(t, t->tsnap.snap_source, mval);
}
else
#endif
{
copy_v2_v2_int(mval, t->mval);
}
snapsource_end(t);
transform_input_reset(t, mval);
/* Remote individual snap projection since this mode does not use the new `snap_source`. */
t->tsnap.flag &= ~SCE_SNAP_PROJECT;
}
static eRedrawFlag snapsource_handle_event_fn(struct TransInfo *t, const struct wmEvent *event)
{
if (event->type == EVT_MODAL_MAP) {
switch (event->val) {
case TFM_MODAL_CONFIRM:
case TFM_MODAL_EDIT_SNAP_SOURCE_ON:
case TFM_MODAL_EDIT_SNAP_SOURCE_OFF:
if (t->modifiers & MOD_EDIT_SNAP_SOURCE) {
snapsource_confirm(t);
BLI_assert(t->state != TRANS_CONFIRM);
}
else {
t->modifiers |= MOD_EDIT_SNAP_SOURCE;
}
break;
case TFM_MODAL_CANCEL:
snapsource_end(t);
t->state = TRANS_CANCEL;
return TREDRAW_SOFT;
default:
break;
}
}
else if (event->val == KM_RELEASE && t->state == TRANS_CONFIRM) {
if (t->flag & T_RELEASE_CONFIRM && t->modifiers & MOD_EDIT_SNAP_SOURCE) {
snapsource_confirm(t);
t->flag &= ~T_RELEASE_CONFIRM;
t->state = TRANS_RUNNING;
}
}
return TREDRAW_NOTHING;
}
static void snapsource_transform_fn(TransInfo *t, const int UNUSED(mval[2]))
{
BLI_assert(t->modifiers & MOD_EDIT_SNAP_SOURCE);
t->tsnap.snap_target_fn(t, NULL);
t->redraw |= TREDRAW_SOFT;
}
void transform_mode_snap_source_init(TransInfo *t, struct wmOperator *UNUSED(op))
{
if (t->mode_info == &TransMode_snapsource) {
/* Already running. */
return;
}
if (ELEM(t->mode, TFM_INIT, TFM_DUMMY)) {
/* Fallback */
transform_mode_init(t, NULL, TFM_TRANSLATION);
}
struct SnapSouceCustomData *customdata = MEM_callocN(sizeof(*customdata), __func__);
customdata->mode_info_prev = t->mode_info;
customdata->target_operation_prev = t->tsnap.target_operation;
customdata->mouse_prev.apply = t->mouse.apply;
customdata->mouse_prev.post = t->mouse.post;
customdata->mouse_prev.use_virtual_mval = t->mouse.use_virtual_mval;
customdata->customdata_mode_prev = t->custom.mode.data;
t->custom.mode.data = customdata;
if (!(t->modifiers & MOD_SNAP) || !transformModeUseSnap(t)) {
t->modifiers |= (MOD_SNAP | MOD_SNAP_FORCED);
}
t->mode_info = &TransMode_snapsource;
t->tsnap.target_operation = SCE_SNAP_TARGET_ALL;
if ((t->tsnap.status & SNAP_SOURCE_FOUND) == 0) {
if (t->tsnap.snap_source_fn) {
/* Calculate the current snap source for perpendicular snap. */
t->tsnap.snap_source_fn(t);
}
if ((t->tsnap.status & SNAP_SOURCE_FOUND) == 0) {
/* Fallback. */
copy_v3_v3(t->tsnap.snap_source, t->center_global);
t->tsnap.status |= SNAP_SOURCE_FOUND;
}
}
if ((t->tsnap.mode & ~(SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) == 0) {
/* Initialize snap modes for geometry. */
t->tsnap.mode &= ~(SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID);
t->tsnap.mode |= SCE_SNAP_MODE_GEOM & ~SCE_SNAP_MODE_FACE_NEAREST;
}
if (t->data_type == &TransConvertType_Mesh) {
ED_transform_snap_object_context_set_editmesh_callbacks(
t->tsnap.object_context, NULL, NULL, NULL, NULL);
}
#ifdef RESET_TRANSFORMATION
/* Temporarily disable snapping.
* We don't want #SCE_SNAP_PROJECT to affect `recalcData` for example. */
t->tsnap.flag &= ~SCE_SNAP;
restoreTransObjects(t);
/* Restore snapping status. */
transform_snap_flag_from_modifiers_set(t);
/* Reset initial values to restore gizmo position. */
applyMouseInput(t, &t->mouse, t->mouse.imval, t->values_final);
#endif
#ifdef REMOVE_GIZMO
struct wmGizmo *gz = WM_gizmomap_get_modal(t->region->gizmo_map);
if (gz) {
const struct wmEvent *event = CTX_wm_window(t->context)->eventstate;
# ifdef RESET_TRANSFORMATION
wmGizmoFnModal modal_fn = gz->custom_modal ? gz->custom_modal : gz->type->modal;
modal_fn(t->context, gz, event, 0);
# endif
WM_gizmo_modal_set_while_modal(t->region->gizmo_map, t->context, NULL, event);
}
#endif
t->mouse.apply = NULL;
t->mouse.post = NULL;
t->mouse.use_virtual_mval = false;
}
/** \} */
TransModeInfo TransMode_snapsource = {
/*flags*/ 0,
/*init_fn*/ transform_mode_snap_source_init,
/*transform_fn*/ snapsource_transform_fn,
/*transform_matrix_fn*/ NULL,
/*handle_event_fn*/ snapsource_handle_event_fn,
/*snap_distance_fn*/ NULL,
/*snap_apply_fn*/ NULL,
/*draw_fn*/ NULL,
};

View File

@@ -610,6 +610,35 @@ static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2]))
ED_area_status_text(t->area, str);
}
static void vert_slide_transform_matrix_fn(struct TransInfo *t, float mat_xform[4][4])
{
float delta[3], orig_co[3], final_co[3];
VertSlideParams *slp = t->custom.mode.data;
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_OK(t);
VertSlideData *sld_active = tc->custom.mode.data;
TransDataVertSlideVert *sv_active = &sld_active->sv[sld_active->curr_sv_index];
copy_v3_v3(orig_co, sv_active->co_orig_3d);
float tperc = t->values_final[0];
if (slp->use_even) {
const float edge_len_curr = len_v3v3(sv_active->co_orig_3d,
sv_active->co_link_orig_3d[sv_active->co_link_curr]);
tperc *= edge_len_curr;
}
vert_slide_apply_elem(sv_active, tperc, slp->use_even, slp->flipped, final_co);
if (tc->use_local_mat) {
mul_m4_v3(tc->mat, orig_co);
mul_m4_v3(tc->mat, final_co);
}
sub_v3_v3v3(delta, final_co, orig_co);
add_v3_v3(mat_xform[3], delta);
}
static void initVertSlide_ex(TransInfo *t, bool use_even, bool flipped, bool use_clamp)
{
@@ -699,7 +728,7 @@ TransModeInfo TransMode_vertslide = {
/*flags*/ T_NO_CONSTRAINT | T_NO_PROJECT,
/*init_fn*/ initVertSlide,
/*transform_fn*/ applyVertSlide,
/*transform_matrix_fn*/ NULL,
/*transform_matrix_fn*/ vert_slide_transform_matrix_fn,
/*handle_event_fn*/ handleEventVertSlide,
/*snap_distance_fn*/ transform_snap_distance_len_squared_fn,
/*snap_apply_fn*/ vert_slide_snap_apply,

View File

@@ -167,17 +167,33 @@ static bool doForceIncrementSnap(const TransInfo *t)
return !transformModeUseSnap(t);
}
static void snap_source_transformed(TransInfo *t, float r_vec[3])
{
float mat[4][4];
unit_m4(mat);
if (t->mode_info->transform_matrix_fn) {
t->mode_info->transform_matrix_fn(t, mat);
}
mul_v3_m4v3(r_vec, mat, t->tsnap.snap_source);
}
void drawSnapping(const bContext *C, TransInfo *t)
{
uchar col[4], selectedCol[4], activeCol[4];
if (!transform_snap_is_active(t)) {
if (!(transform_snap_is_active(t) || t->modifiers & MOD_EDIT_SNAP_SOURCE)) {
return;
}
bool draw_source = (t->spacetype == SPACE_VIEW3D) && (t->tsnap.status & SNAP_SOURCE_FOUND) &&
(t->tsnap.mode & SCE_SNAP_MODE_EDGE_PERPENDICULAR);
const bool draw_source = (t->spacetype == SPACE_VIEW3D) &&
(t->tsnap.status & SNAP_SOURCE_FOUND) &&
(t->tsnap.mode & SCE_SNAP_MODE_EDGE_PERPENDICULAR);
if (!(draw_source || validSnap(t))) {
bool draw_source_transformed = (t->spacetype == SPACE_VIEW3D) &&
(t->tsnap.status & SNAP_SOURCE_FOUND) &&
!(t->modifiers & MOD_EDIT_SNAP_SOURCE);
if (!(draw_source || draw_source_transformed || validSnap(t))) {
return;
}
@@ -203,8 +219,8 @@ void drawSnapping(const bContext *C, TransInfo *t)
GPU_depth_test(GPU_DEPTH_NONE);
RegionView3D *rv3d = CTX_wm_region_view3d(C);
if (!BLI_listbase_is_empty(&t->tsnap.points)) {
RegionView3D *rv3d = (RegionView3D *)t->region->regiondata;
if (draw_source_transformed || !BLI_listbase_is_empty(&t->tsnap.points)) {
/* Draw snap points. */
float size = 2.0f * UI_GetThemeValuef(TH_VERTEX_SIZE);
@@ -216,14 +232,68 @@ void drawSnapping(const bContext *C, TransInfo *t)
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
LISTBASE_FOREACH (TransSnapPoint *, p, &t->tsnap.points) {
if (p == t->tsnap.selectedPoint) {
immUniformColor4ubv(selectedCol);
float snap_source_tranformed[3];
if (draw_source_transformed) {
snap_source_transformed(t, snap_source_tranformed);
}
if (!BLI_listbase_is_empty(&t->tsnap.points)) {
LISTBASE_FOREACH (TransSnapPoint *, p, &t->tsnap.points) {
if (p == t->tsnap.selectedPoint) {
immUniformColor4ubv(selectedCol);
}
else {
immUniformColor4ubv(col);
}
imm_drawcircball(p->co, ED_view3d_pixel_size(rv3d, p->co) * size, view_inv, pos);
}
else {
immUniformColor4ubv(col);
if (t->modifiers & MOD_EDIT_SNAP_SOURCE) {
/* Indicate the new snap source position. */
getSnapPoint(t, snap_source_tranformed);
draw_source_transformed = true;
}
imm_drawcircball(p->co, ED_view3d_pixel_size(rv3d, p->co) * size, view_inv, pos);
}
if (draw_source_transformed &&
!compare_v3v3(snap_source_tranformed, t->tsnap.snap_target, FLT_EPSILON))
{
float view_inv[4][4];
copy_m4_m4(view_inv, rv3d->viewinv);
float vx[3], vy[3], v[3];
float size_tmp = ED_view3d_pixel_size(rv3d, snap_source_tranformed) * size;
float size_fac = 0.5f;
mul_v3_v3fl(vx, view_inv[0], size_tmp);
mul_v3_v3fl(vy, view_inv[1], size_tmp);
immUniformColor4ubv(col);
imm_drawcircball(snap_source_tranformed, size_tmp, view_inv, pos);
immBegin(GPU_PRIM_LINES, 8);
add_v3_v3v3(v, snap_source_tranformed, vx);
immVertex3fv(pos, v);
madd_v3_v3fl(v, vx, size_fac);
immVertex3fv(pos, v);
sub_v3_v3v3(v, snap_source_tranformed, vx);
immVertex3fv(pos, v);
madd_v3_v3fl(v, vx, -size_fac);
immVertex3fv(pos, v);
add_v3_v3v3(v, snap_source_tranformed, vy);
immVertex3fv(pos, v);
madd_v3_v3fl(v, vy, size_fac);
immVertex3fv(pos, v);
sub_v3_v3v3(v, snap_source_tranformed, vy);
immVertex3fv(pos, v);
madd_v3_v3fl(v, vy, -size_fac);
immVertex3fv(pos, v);
immEnd();
}
immUnbindProgram();
@@ -238,7 +308,7 @@ void drawSnapping(const bContext *C, TransInfo *t)
loc_prev = t->tsnap.snap_source;
}
if (validSnap(t)) {
if (t->tsnap.status & SNAP_TARGET_FOUND) {
loc_cur = t->tsnap.snap_target;
}
@@ -739,6 +809,28 @@ static eSnapTargetOP snap_target_select_from_spacetype(TransInfo *t)
return ret;
}
static void snap_object_context_init(TransInfo *t)
{
if (t->data_type == &TransConvertType_Mesh) {
/* Ignore elements being transformed. */
ED_transform_snap_object_context_set_editmesh_callbacks(
t->tsnap.object_context,
(bool (*)(BMVert *, void *))BM_elem_cb_check_hflag_disabled,
bm_edge_is_snap_target,
bm_face_is_snap_target,
POINTER_FROM_UINT(BM_ELEM_SELECT | BM_ELEM_HIDDEN));
}
else {
/* Ignore hidden geometry in the general case. */
ED_transform_snap_object_context_set_editmesh_callbacks(
t->tsnap.object_context,
(bool (*)(BMVert *, void *))BM_elem_cb_check_hflag_disabled,
(bool (*)(BMEdge *, void *))BM_elem_cb_check_hflag_disabled,
(bool (*)(BMFace *, void *))BM_elem_cb_check_hflag_disabled,
POINTER_FROM_UINT(BM_ELEM_HIDDEN));
}
}
static void initSnappingMode(TransInfo *t)
{
if (!transformModeUseSnap(t)) {
@@ -769,25 +861,7 @@ static void initSnappingMode(TransInfo *t)
if (t->tsnap.object_context == nullptr) {
SET_FLAG_FROM_TEST(t->tsnap.flag, snap_use_backface_culling(t), SCE_SNAP_BACKFACE_CULLING);
t->tsnap.object_context = ED_transform_snap_object_context_create(t->scene, 0);
if (t->data_type == &TransConvertType_Mesh) {
/* Ignore elements being transformed. */
ED_transform_snap_object_context_set_editmesh_callbacks(
t->tsnap.object_context,
(bool (*)(BMVert *, void *))BM_elem_cb_check_hflag_disabled,
bm_edge_is_snap_target,
bm_face_is_snap_target,
POINTER_FROM_UINT(BM_ELEM_SELECT | BM_ELEM_HIDDEN));
}
else {
/* Ignore hidden geometry in the general case. */
ED_transform_snap_object_context_set_editmesh_callbacks(
t->tsnap.object_context,
(bool (*)(BMVert *, void *))BM_elem_cb_check_hflag_disabled,
(bool (*)(BMEdge *, void *))BM_elem_cb_check_hflag_disabled,
(bool (*)(BMFace *, void *))BM_elem_cb_check_hflag_disabled,
POINTER_FROM_UINT(BM_ELEM_HIDDEN));
}
snap_object_context_init(t);
}
}
else if (t->spacetype == SPACE_SEQ) {
@@ -1081,6 +1155,15 @@ void getSnapPoint(const TransInfo *t, float vec[3])
}
}
static void snap_multipoints_free(TransInfo *t)
{
if (t->tsnap.status & SNAP_MULTI_POINTS) {
BLI_freelistN(&t->tsnap.points);
t->tsnap.status &= ~SNAP_MULTI_POINTS;
t->tsnap.selectedPoint = NULL;
}
}
/** \} */
/* -------------------------------------------------------------------- */
@@ -1719,6 +1802,12 @@ float transform_snap_increment_get(const TransInfo *t)
return 0.0f;
}
void tranform_snap_source_restore_context(TransInfo *t)
{
snap_object_context_init(t);
snap_multipoints_free(t);
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@@ -45,6 +45,8 @@ bool transform_snap_increment_ex(const TransInfo *t, bool use_local_space, float
bool transform_snap_increment(const TransInfo *t, float *val);
float transform_snap_increment_get(const TransInfo *t);
void tranform_snap_source_restore_context(TransInfo *t);
void transform_snap_flag_from_modifiers_set(TransInfo *t);
bool transform_snap_is_active(const TransInfo *t);

View File

@@ -300,7 +300,7 @@ static SnapData_EditMesh *snap_object_data_editmesh_get(SnapObjectContext *sctx,
else if (sod->mesh_runtime) {
if (sod->mesh_runtime != snap_object_data_editmesh_runtime_get(ob_eval)) {
if (G.moving) {
/* Hack to avoid updating while transforming. */
/* WORKAROUND: avoid updating while transforming. */
BLI_assert(!sod->treedata_editmesh.cached && !sod->cached[0] && !sod->cached[1]);
sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob_eval);
}
@@ -383,11 +383,12 @@ static BVHTreeFromEditMesh *snap_object_data_editmesh_treedata_get(SnapObjectCon
em,
4,
BVHTREE_FROM_EM_LOOPTRI,
&sod->mesh_runtime->bvh_cache,
/* WORKAROUND: avoid updating while transforming. */
G.moving ? nullptr : &sod->mesh_runtime->bvh_cache,
&sod->mesh_runtime->eval_mutex);
}
}
if (treedata == nullptr || treedata->tree == nullptr) {
if (treedata->tree == nullptr) {
return nullptr;
}
@@ -1265,7 +1266,7 @@ static bool nearest_world_editmesh(SnapObjectContext *sctx,
int *r_index)
{
BVHTreeFromEditMesh *treedata = snap_object_data_editmesh_treedata_get(sctx, ob_eval, em);
if (treedata == nullptr || treedata->tree == nullptr) {
if (treedata == nullptr) {
return false;
}
@@ -2767,7 +2768,8 @@ static eSnapMode snapEditMesh(SnapObjectContext *sctx,
em,
2,
BVHTREE_FROM_EM_VERTS,
&sod->mesh_runtime->bvh_cache,
/* WORKAROUND: avoid updating while transforming. */
G.moving ? nullptr : &sod->mesh_runtime->bvh_cache,
&sod->mesh_runtime->eval_mutex);
}
sod->bvhtree[0] = treedata.tree;
@@ -2796,7 +2798,8 @@ static eSnapMode snapEditMesh(SnapObjectContext *sctx,
em,
2,
BVHTREE_FROM_EM_EDGES,
&sod->mesh_runtime->bvh_cache,
/* WORKAROUND: avoid updating while transforming. */
G.moving ? nullptr : &sod->mesh_runtime->bvh_cache,
&sod->mesh_runtime->eval_mutex);
}
sod->bvhtree[1] = treedata.tree;
@@ -3065,11 +3068,27 @@ void ED_transform_snap_object_context_set_editmesh_callbacks(
bool (*test_face_fn)(BMFace *, void *user_data),
void *user_data)
{
sctx->callbacks.edit_mesh.test_vert_fn = test_vert_fn;
sctx->callbacks.edit_mesh.test_edge_fn = test_edge_fn;
sctx->callbacks.edit_mesh.test_face_fn = test_face_fn;
bool is_cache_dirty = false;
if (sctx->callbacks.edit_mesh.test_vert_fn != test_vert_fn) {
sctx->callbacks.edit_mesh.test_vert_fn = test_vert_fn;
is_cache_dirty = true;
}
if (sctx->callbacks.edit_mesh.test_edge_fn != test_edge_fn) {
sctx->callbacks.edit_mesh.test_edge_fn = test_edge_fn;
is_cache_dirty = true;
}
if (sctx->callbacks.edit_mesh.test_face_fn != test_face_fn) {
sctx->callbacks.edit_mesh.test_face_fn = test_face_fn;
is_cache_dirty = true;
}
if (sctx->callbacks.edit_mesh.user_data != user_data) {
sctx->callbacks.edit_mesh.user_data = user_data;
is_cache_dirty = true;
}
sctx->callbacks.edit_mesh.user_data = user_data;
if (is_cache_dirty) {
sctx->editmesh_caches.clear();
}
}
bool ED_transform_snap_object_project_ray_ex(SnapObjectContext *sctx,