Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/transform/transform_snap.c')
-rw-r--r--source/blender/editors/transform/transform_snap.c385
1 files changed, 260 insertions, 125 deletions
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index 649217092aa..9a563aaf473 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -126,8 +126,12 @@ bool activeSnap(const TransInfo *t)
((t->modifiers & (MOD_SNAP | MOD_SNAP_INVERT)) == MOD_SNAP_INVERT);
}
-bool activeSnap_with_project(const TransInfo *t)
+bool activeSnap_SnappingIndividual(const TransInfo *t)
{
+ if (activeSnap(t) && t->tsnap.mode & SCE_SNAP_MODE_FACE_NEAREST) {
+ return true;
+ }
+
if (!t->tsnap.project) {
return false;
}
@@ -143,6 +147,27 @@ bool activeSnap_with_project(const TransInfo *t)
return true;
}
+bool activeSnap_SnappingAsGroup(const TransInfo *t)
+{
+ if (!activeSnap(t)) {
+ return false;
+ }
+
+ if (t->tsnap.mode == SCE_SNAP_MODE_FACE_RAYCAST && t->tsnap.project) {
+ return false;
+ }
+
+ if (t->tsnap.mode == SCE_SNAP_MODE_FACE_NEAREST) {
+ return false;
+ }
+
+ if (doForceIncrementSnap(t)) {
+ return false;
+ }
+
+ return true;
+}
+
bool transformModeUseSnap(const TransInfo *t)
{
ToolSettings *ts = t->settings;
@@ -343,93 +368,157 @@ eRedrawFlag handleSnapping(TransInfo *t, const wmEvent *event)
return status;
}
-void applyProject(TransInfo *t)
+static bool applyFaceProject(TransInfo *t, TransDataContainer *tc, TransData *td)
{
- if (!activeSnap_with_project(t)) {
- return;
+ if (!(t->tsnap.mode & SCE_SNAP_MODE_FACE_RAYCAST)) {
+ return false;
+ }
+
+ float iloc[3], loc[3], no[3];
+ float mval_fl[2];
+
+ copy_v3_v3(iloc, td->loc);
+ if (tc->use_local_mat) {
+ mul_m4_v3(tc->mat, iloc);
+ }
+ else if (t->options & CTX_OBJECT) {
+ BKE_object_eval_transform_all(t->depsgraph, t->scene, td->ob);
+ copy_v3_v3(iloc, td->ob->obmat[3]);
+ }
+
+ if (ED_view3d_project_float_global(t->region, iloc, mval_fl, V3D_PROJ_TEST_NOP) !=
+ V3D_PROJ_RET_OK) {
+ return false;
+ }
+
+ eSnapMode hit = ED_transform_snap_object_project_view3d(
+ t->tsnap.object_context,
+ t->depsgraph,
+ t->region,
+ t->view,
+ SCE_SNAP_MODE_FACE_RAYCAST,
+ &(const struct SnapObjectParams){
+ .snap_target_select = t->tsnap.target_select,
+ .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL,
+ .use_occlusion_test = false,
+ .use_backface_culling = t->tsnap.use_backface_culling,
+ },
+ NULL,
+ mval_fl,
+ NULL,
+ 0,
+ loc,
+ no);
+ if (hit != SCE_SNAP_MODE_FACE_RAYCAST) {
+ return false;
}
float tvec[3];
- int i;
+ sub_v3_v3v3(tvec, loc, iloc);
- /* XXX FLICKER IN OBJECT MODE */
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- TransData *td = tc->data;
- for (i = 0; i < tc->data_len; i++, td++) {
- float iloc[3], loc[3], no[3];
- float mval_fl[2];
- if (td->flag & TD_SKIP) {
- continue;
- }
+ mul_m3_v3(td->smtx, tvec);
- if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f)) {
- continue;
- }
+ add_v3_v3(td->loc, tvec);
- copy_v3_v3(iloc, td->loc);
- if (tc->use_local_mat) {
- mul_m4_v3(tc->mat, iloc);
- }
- else if (t->options & CTX_OBJECT) {
- BKE_object_eval_transform_all(t->depsgraph, t->scene, td->ob);
- copy_v3_v3(iloc, td->ob->obmat[3]);
- }
+ if (t->tsnap.align && (t->options & CTX_OBJECT)) {
+ /* handle alignment as well */
+ const float *original_normal;
+ float mat[3][3];
- if (ED_view3d_project_float_global(t->region, iloc, mval_fl, V3D_PROJ_TEST_NOP) ==
- V3D_PROJ_RET_OK) {
- eSnapMode hit = ED_transform_snap_object_project_view3d(
- t->tsnap.object_context,
- t->depsgraph,
- t->region,
- t->view,
- SCE_SNAP_MODE_FACE,
- &(const struct SnapObjectParams){
- .snap_target_select = t->tsnap.target_select,
- .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL,
- .use_occlusion_test = false,
- .use_backface_culling = t->tsnap.use_backface_culling,
- },
- mval_fl,
- NULL,
- 0,
- loc,
- no);
- if (hit != SCE_SNAP_MODE_FACE) {
- return;
- }
+ /* In pose mode, we want to align normals with Y axis of bones. */
+ original_normal = td->axismtx[2];
-#if 0
- if (tc->use_local_mat) {
- mul_m4_v3(tc->imat, loc);
- }
-#endif
+ rotation_between_vecs_to_mat3(mat, original_normal, no);
+
+ transform_data_ext_rotate(td, mat, true);
+
+ /* TODO: support constraints for rotation too? see #ElementRotation. */
+ }
+ return true;
+}
+
+static void applyFaceNearest(TransInfo *t, TransDataContainer *tc, TransData *td)
+{
+ if (!(t->tsnap.mode & SCE_SNAP_MODE_FACE_NEAREST)) {
+ return;
+ }
- sub_v3_v3v3(tvec, loc, iloc);
+ float init_loc[3];
+ float prev_loc[3];
+ float snap_loc[3], snap_no[3];
- mul_m3_v3(td->smtx, tvec);
+ copy_v3_v3(init_loc, td->iloc);
+ copy_v3_v3(prev_loc, td->loc);
+ if (tc->use_local_mat) {
+ mul_m4_v3(tc->mat, init_loc);
+ mul_m4_v3(tc->mat, prev_loc);
+ }
+ else if (t->options & CTX_OBJECT) {
+ BKE_object_eval_transform_all(t->depsgraph, t->scene, td->ob);
+ copy_v3_v3(init_loc, td->ob->obmat[3]);
+ }
- add_v3_v3(td->loc, tvec);
+ eSnapMode hit = ED_transform_snap_object_project_view3d(
+ t->tsnap.object_context,
+ t->depsgraph,
+ t->region,
+ t->view,
+ SCE_SNAP_MODE_FACE_NEAREST,
+ &(const struct SnapObjectParams){
+ .snap_target_select = t->tsnap.target_select,
+ .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL,
+ .use_occlusion_test = false,
+ .use_backface_culling = false,
+ .face_nearest_steps = t->tsnap.face_nearest_steps,
+ .keep_on_same_target = t->tsnap.flag & SCE_SNAP_KEEP_ON_SAME_OBJECT,
+ },
+ init_loc,
+ NULL,
+ prev_loc,
+ 0,
+ snap_loc,
+ snap_no);
+
+ if (hit != SCE_SNAP_MODE_FACE_NEAREST) {
+ return;
+ }
- if (t->tsnap.align && (t->options & CTX_OBJECT)) {
- /* handle alignment as well */
- const float *original_normal;
- float mat[3][3];
+ float tvec[3];
+ sub_v3_v3v3(tvec, snap_loc, prev_loc);
+ mul_m3_v3(td->smtx, tvec);
+ add_v3_v3(td->loc, tvec);
- /* In pose mode, we want to align normals with Y axis of bones... */
- original_normal = td->axismtx[2];
+ /* TODO: support snap alignment similar to #SCE_SNAP_MODE_FACE_RAYCAST? */
+}
- rotation_between_vecs_to_mat3(mat, original_normal, no);
+void applySnappingIndividual(TransInfo *t)
+{
+ if (!activeSnap_SnappingIndividual(t)) {
+ return;
+ }
- transform_data_ext_rotate(td, mat, true);
+ /* XXX FLICKER IN OBJECT MODE */
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ TransData *td = tc->data;
+ for (int i = 0; i < tc->data_len; i++, td++) {
+ if (td->flag & TD_SKIP) {
+ continue;
+ }
- /* TODO: support constraints for rotation too? see #ElementRotation. */
- }
+ if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f)) {
+ continue;
}
- }
+ /* If both face ray-cast and face nearest methods are enabled, start with face ray-cast and
+ * fallback to face nearest ray-cast does not hit. */
+ bool hit = applyFaceProject(t, tc, td);
+ if (!hit) {
+ applyFaceNearest(t, tc, td);
+ }
#if 0 /* TODO: support this? */
- constraintTransLim(t, td);
+ constraintTransLim(t, td);
#endif
+ }
}
}
@@ -483,15 +572,9 @@ void applyGridAbsolute(TransInfo *t)
}
}
-void applySnapping(TransInfo *t, float *vec)
+void applySnappingAsGroup(TransInfo *t, float *vec)
{
- /* Each Trans Data already makes the snap to face */
- if (doForceIncrementSnap(t)) {
- return;
- }
-
- if (t->tsnap.project && t->tsnap.mode == SCE_SNAP_MODE_FACE) {
- /* A similar snap will be applied to each transdata in `applyProject`. */
+ if (!activeSnap_SnappingAsGroup(t)) {
return;
}
@@ -644,70 +727,76 @@ static eSnapMode snap_mode_from_spacetype(TransInfo *t)
return SCE_SNAP_MODE_INCREMENT;
}
-static eSnapTargetSelect snap_select_type_get(TransInfo *t)
+static eSnapTargetSelect snap_target_select_from_spacetype(TransInfo *t)
{
ViewLayer *view_layer = t->view_layer;
Base *base_act = view_layer->basact;
+
+ eSnapTargetSelect ret = SCE_SNAP_TARGET_ALL;
+
+ bool use_snap_active = (t->tsnap.target_select & SCE_SNAP_TARGET_NOT_ACTIVE) == 0;
+ bool use_snap_edit = (t->tsnap.target_select & SCE_SNAP_TARGET_NOT_EDITED) == 0;
+ bool use_snap_nonedit = (t->tsnap.target_select & SCE_SNAP_TARGET_NOT_NONEDITED) == 0;
+ bool use_snap_selectable_only = (t->tsnap.target_select & SCE_SNAP_TARGET_ONLY_SELECTABLE) != 0;
+
if (ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE) && !(t->options & CTX_CAMERA)) {
+ if (base_act && (base_act->object->mode & OB_MODE_PARTICLE_EDIT)) {
+ /* Particles edit mode. */
+ return ret;
+ }
+
+ if (use_snap_selectable_only) {
+ ret |= SCE_SNAP_TARGET_ONLY_SELECTABLE;
+ }
+
if (t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR | CTX_OBMODE_XFORM_OBDATA)) {
/* In "Edit Strokes" mode,
* snap tool can perform snap to selected or active objects (see T49632)
* TODO: perform self snap in gpencil_strokes.
*
* When we're moving the origins, allow snapping onto our own geometry (see T69132). */
- return SCE_SNAP_TARGET_ALL;
+ return ret;
}
const int obedit_type = t->obedit_type;
if (obedit_type != -1) {
/* Edit mode */
- if (ELEM(obedit_type,
- OB_MESH,
- OB_ARMATURE,
- OB_CURVES_LEGACY,
- OB_SURF,
- OB_LATTICE,
- OB_MBALL)) {
- /* Temporary limited to edit mode meshes, armature, curves, lattice and metaballs. */
-
- if ((obedit_type == OB_MESH) && (t->flag & T_PROP_EDIT)) {
- /* Exclude editmesh if using proportional edit */
- return SCE_SNAP_TARGET_NOT_EDITED;
+ if (obedit_type == OB_MESH) {
+ /* Editing a mesh */
+ if ((t->flag & T_PROP_EDIT) != 0) {
+ /* Exclude editmesh when using proportional edit */
+ ret |= SCE_SNAP_TARGET_NOT_EDITED;
}
-
- if (!t->tsnap.snap_self) {
- return SCE_SNAP_TARGET_NOT_ACTIVE;
+ if (!use_snap_active) {
+ ret |= SCE_SNAP_TARGET_NOT_ACTIVE;
+ }
+ if (!use_snap_edit) {
+ ret |= SCE_SNAP_TARGET_NOT_EDITED;
+ }
+ if (!use_snap_nonedit) {
+ ret |= SCE_SNAP_TARGET_NOT_NONEDITED;
}
-
- return SCE_SNAP_TARGET_NOT_SELECTED;
}
-
- return SCE_SNAP_TARGET_ALL;
+ else if (ELEM(obedit_type, OB_ARMATURE, OB_CURVES_LEGACY, OB_SURF, OB_LATTICE, OB_MBALL)) {
+ /* Temporary limited to edit mode armature, curves, surfaces, lattices, and metaballs. */
+ ret |= SCE_SNAP_TARGET_NOT_SELECTED;
+ }
}
-
- if (base_act && (base_act->object->mode & OB_MODE_PARTICLE_EDIT)) {
- /* Particles edit mode. */
- return SCE_SNAP_TARGET_ALL;
+ else {
+ /* Object or pose mode. */
+ ret |= SCE_SNAP_TARGET_NOT_SELECTED | SCE_SNAP_TARGET_NOT_ACTIVE;
}
-
- /* Object or pose mode. */
- return SCE_SNAP_TARGET_NOT_SELECTED;
}
-
- if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
- return SCE_SNAP_TARGET_NOT_SELECTED;
+ else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
+ ret |= SCE_SNAP_TARGET_NOT_SELECTED;
}
- return SCE_SNAP_TARGET_ALL;
+ return ret;
}
static void initSnappingMode(TransInfo *t)
{
- ToolSettings *ts = t->settings;
- t->tsnap.mode = snap_mode_from_spacetype(t);
- t->tsnap.target_select = snap_select_type_get(t);
-
- if ((t->spacetype != SPACE_VIEW3D) || !(ts->snap_mode & SCE_SNAP_MODE_FACE)) {
+ if ((t->spacetype != SPACE_VIEW3D) || !(t->tsnap.mode & SCE_SNAP_MODE_FACE_RAYCAST)) {
/* Force project off when not supported. */
t->tsnap.project = false;
}
@@ -724,7 +813,7 @@ static void initSnappingMode(TransInfo *t)
t->tsnap.use_backface_culling = snap_use_backface_culling(t);
t->tsnap.object_context = ED_transform_snap_object_context_create(t->scene, 0);
- if (t->data_type == TC_MESH_VERTS) {
+ if (t->data_type == &TransConvertType_Mesh) {
/* Ignore elements being transformed. */
ED_transform_snap_object_context_set_editmesh_callbacks(
t->tsnap.object_context,
@@ -753,9 +842,14 @@ static void initSnappingMode(TransInfo *t)
void initSnapping(TransInfo *t, wmOperator *op)
{
+ ToolSettings *ts = t->settings;
+ eSnapSourceSelect snap_source = ts->snap_target;
+
resetSnapping(t);
+ t->tsnap.mode = snap_mode_from_spacetype(t);
t->tsnap.flag = snap_flag_from_spacetype(t);
- eSnapSourceSelect snap_source = t->settings->snap_target;
+ t->tsnap.target_select = snap_target_select_from_spacetype(t);
+ t->tsnap.face_nearest_steps = max_ii(ts->snap_face_nearest_steps, 1);
/* if snap property exists */
PropertyRNA *prop;
@@ -764,11 +858,16 @@ void initSnapping(TransInfo *t, wmOperator *op)
if (RNA_property_boolean_get(op->ptr, prop)) {
t->modifiers |= MOD_SNAP;
+ if ((prop = RNA_struct_find_property(op->ptr, "snap_elements")) &&
+ RNA_property_is_set(op->ptr, prop)) {
+ t->tsnap.mode = RNA_property_enum_get(op->ptr, prop);
+ }
+
+ /* 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). */
if ((prop = RNA_struct_find_property(op->ptr, "snap_target")) &&
RNA_property_is_set(op->ptr, prop)) {
- /* 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). */
snap_source = RNA_property_enum_get(op->ptr, prop);
}
@@ -791,9 +890,33 @@ void initSnapping(TransInfo *t, wmOperator *op)
t->tsnap.project = RNA_property_boolean_get(op->ptr, prop);
}
+ /* use_snap_self is misnamed and should be use_snap_active */
if ((prop = RNA_struct_find_property(op->ptr, "use_snap_self")) &&
RNA_property_is_set(op->ptr, prop)) {
- t->tsnap.snap_self = RNA_property_boolean_get(op->ptr, prop);
+ SET_FLAG_FROM_TEST(t->tsnap.target_select,
+ !RNA_property_boolean_get(op->ptr, prop),
+ SCE_SNAP_TARGET_NOT_ACTIVE);
+ }
+
+ if ((prop = RNA_struct_find_property(op->ptr, "use_snap_edit")) &&
+ RNA_property_is_set(op->ptr, prop)) {
+ SET_FLAG_FROM_TEST(t->tsnap.target_select,
+ !RNA_property_boolean_get(op->ptr, prop),
+ SCE_SNAP_TARGET_NOT_EDITED);
+ }
+
+ if ((prop = RNA_struct_find_property(op->ptr, "use_snap_nonedit")) &&
+ RNA_property_is_set(op->ptr, prop)) {
+ SET_FLAG_FROM_TEST(t->tsnap.target_select,
+ !RNA_property_boolean_get(op->ptr, prop),
+ SCE_SNAP_TARGET_NOT_NONEDITED);
+ }
+
+ if ((prop = RNA_struct_find_property(op->ptr, "use_snap_selectable")) &&
+ RNA_property_is_set(op->ptr, prop)) {
+ SET_FLAG_FROM_TEST(t->tsnap.target_select,
+ RNA_property_boolean_get(op->ptr, prop),
+ SCE_SNAP_TARGET_ONLY_SELECTABLE);
}
}
}
@@ -805,8 +928,19 @@ void initSnapping(TransInfo *t, wmOperator *op)
t->tsnap.align = ((t->tsnap.flag & SCE_SNAP_ROTATE) != 0);
t->tsnap.project = ((t->tsnap.flag & SCE_SNAP_PROJECT) != 0);
- t->tsnap.snap_self = !((t->tsnap.flag & SCE_SNAP_NO_SELF) != 0);
t->tsnap.peel = ((t->tsnap.flag & SCE_SNAP_PROJECT) != 0);
+ SET_FLAG_FROM_TEST(t->tsnap.target_select,
+ (ts->snap_flag & SCE_SNAP_NOT_TO_ACTIVE),
+ SCE_SNAP_TARGET_NOT_ACTIVE);
+ SET_FLAG_FROM_TEST(t->tsnap.target_select,
+ !(ts->snap_flag & SCE_SNAP_TO_INCLUDE_EDITED),
+ SCE_SNAP_TARGET_NOT_EDITED);
+ SET_FLAG_FROM_TEST(t->tsnap.target_select,
+ !(ts->snap_flag & SCE_SNAP_TO_INCLUDE_NONEDITED),
+ SCE_SNAP_TARGET_NOT_NONEDITED);
+ SET_FLAG_FROM_TEST(t->tsnap.target_select,
+ (ts->snap_flag & SCE_SNAP_TO_ONLY_SELECTABLE),
+ SCE_SNAP_TARGET_ONLY_SELECTABLE);
}
t->tsnap.source_select = snap_source;
@@ -991,8 +1125,8 @@ static void snap_calc_view3d_fn(TransInfo *t, float *UNUSED(vec))
found = (snap_elem != SCE_SNAP_MODE_NONE);
}
if ((found == false) && (t->tsnap.mode & SCE_SNAP_MODE_VOLUME)) {
- found = peelObjectsTransform(
- t, mval, (t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0, loc, no, NULL);
+ bool use_peel = (t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0;
+ found = peelObjectsTransform(t, mval, use_peel, loc, no, NULL);
if (found) {
snap_elem = SCE_SNAP_MODE_VOLUME;
@@ -1026,7 +1160,7 @@ static void snap_calc_uv_fn(TransInfo *t, float *UNUSED(vec))
objects,
objects_len,
t->mval,
- t->tsnap.target_select == SCE_SNAP_TARGET_NOT_SELECTED,
+ t->tsnap.target_select & SCE_SNAP_TARGET_NOT_SELECTED,
&dist_sq,
t->tsnap.snapPoint)) {
t->tsnap.snapPoint[0] *= t->aspect[0];
@@ -1118,7 +1252,7 @@ static void snap_target_grid_ensure(TransInfo *t)
{
/* Only need to calculate once. */
if ((t->tsnap.status & TARGET_GRID_INIT) == 0) {
- if (t->data_type == TC_CURSOR_VIEW3D) {
+ 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.snapTargetGrid, TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->data->iloc);
@@ -1321,9 +1455,10 @@ eSnapMode snapObjectsTransform(
&(const struct SnapObjectParams){
.snap_target_select = t->tsnap.target_select,
.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL,
- .use_occlusion_test = t->settings->snap_mode != SCE_SNAP_MODE_FACE,
+ .use_occlusion_test = t->settings->snap_mode != SCE_SNAP_MODE_FACE_RAYCAST,
.use_backface_culling = t->tsnap.use_backface_culling,
},
+ NULL,
mval,
target,
dist_px,
@@ -1423,7 +1558,7 @@ bool peelObjectsTransform(TransInfo *t,
static bool snapNodeTest(View2D *v2d, bNode *node, eSnapTargetSelect snap_target_select)
{
/* node is use for snapping only if a) snap mode matches and b) node is inside the view */
- return ((snap_target_select == SCE_SNAP_TARGET_NOT_SELECTED && !(node->flag & NODE_SELECT)) ||
+ return (((snap_target_select & SCE_SNAP_TARGET_NOT_SELECTED) && !(node->flag & NODE_SELECT)) ||
(snap_target_select == SCE_SNAP_TARGET_ALL && !(node->flag & NODE_ACTIVE))) &&
(node->totr.xmin < v2d->cur.xmax && node->totr.xmax > v2d->cur.xmin &&
node->totr.ymin < v2d->cur.ymax && node->totr.ymax > v2d->cur.ymin);