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:
-rw-r--r--release/datafiles/blender_icons.svg49
-rw-r--r--release/datafiles/blender_icons16/icon16_snap_face_nearest.datbin0 -> 1048 bytes
-rw-r--r--release/datafiles/blender_icons32/icon32_snap_face_nearest.datbin0 -> 4120 bytes
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py46
-rw-r--r--source/blender/blenloader/intern/versioning_280.c2
-rw-r--r--source/blender/blenloader/intern/versioning_300.c11
-rw-r--r--source/blender/editors/curve/editcurve.c5
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c3
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c2
-rw-r--r--source/blender/editors/include/ED_transform_snap_object_context.h43
-rw-r--r--source/blender/editors/include/UI_icons.h2
-rw-r--r--source/blender/editors/mesh/editmesh_extrude.c2
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c3
-rw-r--r--source/blender/editors/space_view3d/view3d_cursor_snap.c11
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c3
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_ruler.c3
-rw-r--r--source/blender/editors/transform/transform.c2
-rw-r--r--source/blender/editors/transform/transform.h2
-rw-r--r--source/blender/editors/transform/transform_constraints.c2
-rw-r--r--source/blender/editors/transform/transform_convert_armature.c2
-rw-r--r--source/blender/editors/transform/transform_convert_curve.c2
-rw-r--r--source/blender/editors/transform/transform_convert_lattice.c2
-rw-r--r--source/blender/editors/transform/transform_convert_mball.c2
-rw-r--r--source/blender/editors/transform/transform_convert_mesh.c4
-rw-r--r--source/blender/editors/transform/transform_convert_object.c2
-rw-r--r--source/blender/editors/transform/transform_convert_object_texspace.c2
-rw-r--r--source/blender/editors/transform/transform_convert_particle.c2
-rw-r--r--source/blender/editors/transform/transform_mode_edge_rotate_normal.c2
-rw-r--r--source/blender/editors/transform/transform_mode_edge_seq_slide.c2
-rw-r--r--source/blender/editors/transform/transform_mode_edge_slide.c4
-rw-r--r--source/blender/editors/transform/transform_mode_resize.c2
-rw-r--r--source/blender/editors/transform/transform_mode_rotate.c2
-rw-r--r--source/blender/editors/transform/transform_mode_skin_resize.c2
-rw-r--r--source/blender/editors/transform/transform_mode_translate.c2
-rw-r--r--source/blender/editors/transform/transform_mode_vert_slide.c4
-rw-r--r--source/blender/editors/transform/transform_snap.c381
-rw-r--r--source/blender/editors/transform/transform_snap.h7
-rw-r--r--source/blender/editors/transform/transform_snap_object.cc513
-rw-r--r--source/blender/makesdna/DNA_scene_defaults.h2
-rw-r--r--source/blender/makesdna/DNA_scene_types.h52
-rw-r--r--source/blender/makesrna/intern/rna_scene.c57
41 files changed, 964 insertions, 277 deletions
diff --git a/release/datafiles/blender_icons.svg b/release/datafiles/blender_icons.svg
index f8164d1f646..985714d813f 100644
--- a/release/datafiles/blender_icons.svg
+++ b/release/datafiles/blender_icons.svg
@@ -11,9 +11,9 @@
height="640"
id="svg2"
sodipodi:version="0.32"
- inkscape:version="1.0 (4035a4f, 2020-05-01)"
+ inkscape:version="1.0.2 (e86c870879, 2021-01-15)"
version="1.0"
- sodipodi:docname="blender_icons.svg"
+ sodipodi:docname="blender_icons.master.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
style="display:inline;enable-background:new"
inkscape:export-filename="blender_icons.png"
@@ -42,17 +42,17 @@
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
- inkscape:window-width="1792"
- inkscape:window-height="968"
+ inkscape:window-width="1618"
+ inkscape:window-height="846"
id="namedview34335"
showgrid="false"
- inkscape:zoom="0.815521"
- inkscape:cx="60.911776"
- inkscape:cy="331.5525"
- inkscape:window-x="-1"
- inkscape:window-y="25"
+ inkscape:zoom="16"
+ inkscape:cx="18.1714"
+ inkscape:cy="166.10682"
+ inkscape:window-x="185"
+ inkscape:window-y="159"
inkscape:window-maximized="0"
- inkscape:current-layer="layer2" />
+ inkscape:current-layer="layer8" />
<defs
id="defs4" />
<path
@@ -17505,6 +17505,35 @@
id="path2-6"
d="m 469,101 v 7.5 c 0,0.276 0.224,0.5 0.5,0.5 h 11 c 0.30423,0 0.5,-0.22782 0.5,-0.5 v -4 c 0,-0.65459 -1,-0.65682 -1,0 v 3.5 h -10 v -7 z m 4.48081,-6 c -0.151,0.004 -0.293,0.077 -0.384,0.197 l -3.95,3.949 c -0.314,0.315 -0.091,0.854 0.354,0.854 h 4 c 0.276,0 0.5,-0.224 0.5,-0.5 V 96 H 480.5 c 0.68512,0 0.64092,-1 0,-1 z" />
</g>
+ <g
+ id="g7324"
+ transform="translate(-231.45606,21.458247)"
+ style="display:inline;enable-background:new">
+ <path
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.6;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:new"
+ d="m 244.01184,140.51071 c 1.65601,0 3,1.37486 3,3.04688 0,1.67201 -1.34399,3.04687 -3,3.04687 -1.65601,0 -3,-1.37486 -3,-3.04687 0,-1.67202 1.34399,-3.04688 3,-3.04688 z m 0,1 c -1.10534,0 -2,0.90526 -2,2.04688 0,1.14161 0.89466,2.04687 2,2.04687 1.10534,0 2,-0.90526 2,-2.04687 0,-1.14162 -0.89466,-2.04688 -2,-2.04688 z"
+ id="circle22836-6"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:new"
+ d="m 244.01184,137.51071 c -3.5093,0 -6,2.77253 -6,5.91992 v 7.08008 h -0.96094 a 0.50004991,0.50004991 0 1 0 0,1 h 1.46094 a 0.50004991,0.50004991 0 0 0 0.5,-0.5 v -7.58008 c 0,-2.62796 2.01002,-4.91992 5,-4.91992 2.99076,0 5,2.29197 5,4.91992 v 7.58008 a 0.50004991,0.50004991 0 0 0 0.5,0.5 h 1.46094 a 0.50004991,0.50004991 0 1 0 0,-1 h -0.96094 v -7.08008 c 0,-3.14741 -2.49004,-5.91992 -6,-5.91992 z"
+ id="path22838-0"
+ inkscape:connector-curvature="0" />
+ <g
+ transform="translate(-15.954521,-1.3298229)"
+ style="display:inline;fill:#ffffff;enable-background:new"
+ id="g26501-5-3"
+ inkscape:export-filename="blender_icons.png"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96">
+ <path
+ sodipodi:nodetypes="ccccccccc"
+ inkscape:connector-curvature="0"
+ id="path26499-8-9"
+ d="m 258.49962,136.9993 c -0.27613,3e-5 -0.49997,0.22387 -0.5,0.5 v 3 c 3e-5,0.27613 0.22387,0.49997 0.5,0.5 h 3 c 0.27613,-3e-5 0.49997,-0.22387 0.5,-0.5 v -3 c -3e-5,-0.27613 -0.22387,-0.49997 -0.5,-0.5 z"
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ </g>
+ </g>
</g>
<g
inkscape:groupmode="layer"
diff --git a/release/datafiles/blender_icons16/icon16_snap_face_nearest.dat b/release/datafiles/blender_icons16/icon16_snap_face_nearest.dat
new file mode 100644
index 00000000000..eade810d41a
--- /dev/null
+++ b/release/datafiles/blender_icons16/icon16_snap_face_nearest.dat
Binary files differ
diff --git a/release/datafiles/blender_icons32/icon32_snap_face_nearest.dat b/release/datafiles/blender_icons32/icon32_snap_face_nearest.dat
new file mode 100644
index 00000000000..19ca9d38995
--- /dev/null
+++ b/release/datafiles/blender_icons32/icon32_snap_face_nearest.dat
Binary files differ
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 1f69705db8e..411bad65991 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -6796,23 +6796,53 @@ class VIEW3D_PT_snapping(Panel):
col.prop(tool_settings, "use_snap_grid_absolute")
if snap_elements != {'INCREMENT'}:
- col.label(text="Snap With")
- row = col.row(align=True)
- row.prop(tool_settings, "snap_target", expand=True)
-
- col.prop(tool_settings, "use_snap_backface_culling")
+ if snap_elements != {'FACE_NEAREST'}:
+ col.label(text="Snap With")
+ row = col.row(align=True)
+ row.prop(tool_settings, "snap_target", expand=True)
if obj:
+ col.label(text="Target Selection")
+ col_targetsel = col.column(align=True)
if object_mode == 'EDIT' and obj.type not in {'LATTICE', 'META', 'FONT'}:
- sub = col.column()
- sub.active = not (tool_settings.use_proportional_edit and obj.type == 'MESH')
- sub.prop(tool_settings, "use_snap_self")
+ col_targetsel.prop(
+ tool_settings,
+ "use_snap_self",
+ text="Include Active",
+ icon='EDITMODE_HLT',
+ )
+ col_targetsel.prop(
+ tool_settings,
+ "use_snap_edit",
+ text="Include Edited",
+ icon='OUTLINER_DATA_MESH',
+ )
+ col_targetsel.prop(
+ tool_settings,
+ "use_snap_nonedit",
+ text="Include Non-Edited",
+ icon='OUTLINER_OB_MESH',
+ )
+ col_targetsel.prop(
+ tool_settings,
+ "use_snap_selectable",
+ text="Exclude Non-Selectable",
+ icon='RESTRICT_SELECT_OFF',
+ )
+
if object_mode in {'OBJECT', 'POSE', 'EDIT', 'WEIGHT_PAINT'}:
col.prop(tool_settings, "use_snap_align_rotation")
+ col.prop(tool_settings, "use_snap_backface_culling")
+
if 'FACE' in snap_elements:
col.prop(tool_settings, "use_snap_project")
+ if 'FACE_NEAREST' in snap_elements:
+ col.prop(tool_settings, 'use_snap_to_same_target')
+ if object_mode == 'EDIT':
+ col.prop(tool_settings, 'snap_face_nearest_steps')
+
if 'VOLUME' in snap_elements:
col.prop(tool_settings, "use_snap_peel_object")
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index e542bc46a28..426008e887e 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -2408,7 +2408,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
scene->toolsettings->snap_mode = (1 << 1); /* SCE_SNAP_MODE_EDGE */
break;
case 3:
- scene->toolsettings->snap_mode = (1 << 2); /* SCE_SNAP_MODE_FACE */
+ scene->toolsettings->snap_mode = (1 << 2); /* SCE_SNAP_MODE_FACE_RAYCAST */
break;
case 4:
scene->toolsettings->snap_mode = (1 << 3); /* SCE_SNAP_MODE_VOLUME */
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index ea8653bd0f4..68df560a389 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -3164,6 +3164,17 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ ToolSettings *tool_settings = scene->toolsettings;
+ /* Zero isn't a valid value, use for versioning. */
+ if (tool_settings->snap_face_nearest_steps == 0) {
+ /* Minimum of snap steps for face nearest is 1. */
+ tool_settings->snap_face_nearest_steps = 1;
+ /* Set snap to edited and non-edited as default. */
+ tool_settings->snap_flag |= SCE_SNAP_TO_INCLUDE_EDITED | SCE_SNAP_TO_INCLUDE_NONEDITED;
+ }
+ }
}
if (!MAIN_VERSION_ATLEAST(bmain, 303, 4)) {
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index 4c0df48f65b..24302aca59b 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -5562,7 +5562,7 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Curve *cu;
float location[3];
const bool use_proj = ((vc.scene->toolsettings->snap_flag & SCE_SNAP) &&
- (vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE));
+ (vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE_RAYCAST));
Nurb *nu;
BezTriple *bezt;
@@ -5595,12 +5595,13 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
vc.depsgraph,
vc.region,
vc.v3d,
- SCE_SNAP_MODE_FACE,
+ SCE_SNAP_MODE_FACE_RAYCAST,
&(const struct SnapObjectParams){
.snap_target_select = (vc.obedit != NULL) ? SCE_SNAP_TARGET_NOT_ACTIVE :
SCE_SNAP_TARGET_ALL,
.edit_mode_type = SNAP_GEOM_FINAL,
},
+ NULL,
mval,
NULL,
NULL,
diff --git a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
index 1ce67185c1e..af1f09d7e25 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
@@ -278,12 +278,13 @@ static int gizmo_move_modal(bContext *C,
CTX_data_ensure_evaluated_depsgraph(C),
region,
CTX_wm_view3d(C),
- (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE),
+ (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE_RAYCAST),
&(const struct SnapObjectParams){
.snap_target_select = SCE_SNAP_TARGET_ALL,
.edit_mode_type = SNAP_GEOM_EDIT,
.use_occlusion_test = true,
},
+ NULL,
mval_fl,
NULL,
&dist_px,
diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
index d468906f127..c5a542c0bf3 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
@@ -345,7 +345,7 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt)
prop = RNA_def_enum_flag(gzt->srna,
"snap_elements_force",
rna_enum_snap_element_items,
- SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE,
+ SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE_RAYCAST,
"Snap Elements",
"");
RNA_def_property_enum_funcs_runtime(prop,
diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h
index 20353c21f93..db44d9af706 100644
--- a/source/blender/editors/include/ED_transform_snap_object_context.h
+++ b/source/blender/editors/include/ED_transform_snap_object_context.h
@@ -60,6 +60,10 @@ struct SnapObjectParams {
bool use_occlusion_test : true;
/* exclude back facing geometry from snapping */
bool use_backface_culling : true;
+ /* Break nearest face snapping into steps to improve transformations across U-shaped targets. */
+ short face_nearest_steps;
+ /* Enable to force nearest face snapping to snap to target the source was initially near. */
+ bool keep_on_same_target;
};
typedef struct SnapObjectContext SnapObjectContext;
@@ -114,12 +118,33 @@ bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx,
bool sort,
struct ListBase *r_hit_list);
+/**
+ * Perform snapping.
+ *
+ * Given a 2D region value, snap to vert/edge/face/grid.
+ *
+ * \param sctx: Snap context.
+ * \param snap_to: Target elements to snap source to.
+ * \param params: Addition snapping options.
+ * \param init_co: Initial world-space coordinate of source (optional).
+ * \param mval: Current transformed screen-space coordinate or mouse position (optional).
+ * \param prev_co: Current transformed world-space coordinate of source (optional).
+ * \param dist_px: Maximum distance to snap (in pixels).
+ * \param r_loc: Snapped world-space coordinate.
+ * \param r_no: Snapped world-space normal (optional).
+ * \param r_index: Index of snapped-to target element (optional).
+ * \param r_ob: Snapped-to target object (optional).
+ * \param r_obmat: Matrix of snapped-to target object (optional).
+ * \param r_face_nor: World-space normal of snapped-to target face (optional).
+ * \return Snapped-to element, #eSnapMode.
+ */
eSnapMode ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *sctx,
struct Depsgraph *depsgraph,
const ARegion *region,
const View3D *v3d,
- eSnapMode snap_to,
+ const eSnapMode snap_to,
const struct SnapObjectParams *params,
+ const float init_co[3],
const float mval[2],
const float prev_co[3],
float *dist_px,
@@ -135,19 +160,23 @@ eSnapMode ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *s
* Given a 2D region value, snap to vert/edge/face.
*
* \param sctx: Snap context.
- * \param mval: Screenspace coordinate.
- * \param prev_co: Coordinate for perpendicular point calculation (optional).
+ * \param snap_to: Target elements to snap source to.
+ * \param params: Addition snapping options.
+ * \param init_co: Initial world-space coordinate of source (optional).
+ * \param mval: Current transformed screen-space coordinate or mouse position (optional).
+ * \param prev_co: Current transformed world-space coordinate of source (optional).
* \param dist_px: Maximum distance to snap (in pixels).
- * \param r_loc: hit location.
- * \param r_no: hit normal (optional).
- * \return Snap success.
+ * \param r_loc: Snapped world-space coordinate.
+ * \param r_no: Snapped world-space normal (optional).
+ * \return Snapped-to element, #eSnapMode.
*/
eSnapMode ED_transform_snap_object_project_view3d(struct SnapObjectContext *sctx,
struct Depsgraph *depsgraph,
const ARegion *region,
const View3D *v3d,
- eSnapMode snap_to,
+ const eSnapMode snap_to,
const struct SnapObjectParams *params,
+ const float init_co[3],
const float mval[2],
const float prev_co[3],
float *dist_px,
diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h
index f1c0acf43f7..09057fd846e 100644
--- a/source/blender/editors/include/UI_icons.h
+++ b/source/blender/editors/include/UI_icons.h
@@ -652,7 +652,7 @@ DEF_ICON(PARTICLE_TIP)
DEF_ICON(PARTICLE_PATH)
/* EDITING */
-DEF_ICON_BLANK(669)
+DEF_ICON(SNAP_FACE_NEAREST)
DEF_ICON(SNAP_FACE_CENTER)
DEF_ICON(SNAP_PERPENDICULAR)
DEF_ICON(SNAP_MIDPOINT)
diff --git a/source/blender/editors/mesh/editmesh_extrude.c b/source/blender/editors/mesh/editmesh_extrude.c
index 8d63b1ea020..330008d92d1 100644
--- a/source/blender/editors/mesh/editmesh_extrude.c
+++ b/source/blender/editors/mesh/editmesh_extrude.c
@@ -702,7 +702,7 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const w
const bool rot_src = RNA_boolean_get(op->ptr, "rotate_source");
const bool use_proj = ((vc.scene->toolsettings->snap_flag & SCE_SNAP) &&
- (vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE));
+ (vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE_RAYCAST));
/* First calculate the center of transformation. */
zero_v3(center);
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 04030583f5c..83968955583 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -1646,12 +1646,13 @@ void EDBM_project_snap_verts(
depsgraph,
region,
CTX_wm_view3d(C),
- SCE_SNAP_MODE_FACE,
+ SCE_SNAP_MODE_FACE_RAYCAST,
&(const struct SnapObjectParams){
.snap_target_select = SCE_SNAP_TARGET_NOT_ACTIVE,
.edit_mode_type = SNAP_GEOM_FINAL,
.use_occlusion_test = true,
},
+ NULL,
mval,
NULL,
NULL,
diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c
index a879a05d41a..4a1bd6ba945 100644
--- a/source/blender/editors/space_view3d/view3d_cursor_snap.c
+++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c
@@ -597,9 +597,9 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
eSnapMode snap_elements = v3d_cursor_snap_elements(state, scene);
data_intern->snap_elem_hidden = SCE_SNAP_MODE_NONE;
const bool calc_plane_omat = v3d_cursor_snap_calc_plane();
- if (calc_plane_omat && !(snap_elements & SCE_SNAP_MODE_FACE)) {
- data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE;
- snap_elements |= SCE_SNAP_MODE_FACE;
+ if (calc_plane_omat && !(snap_elements & SCE_SNAP_MODE_FACE_RAYCAST)) {
+ data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE_RAYCAST;
+ snap_elements |= SCE_SNAP_MODE_FACE_RAYCAST;
}
snap_data->is_enabled = true;
@@ -614,7 +614,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
snap_data->snap_elem = SCE_SNAP_MODE_NONE;
return;
}
- snap_elements = data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE;
+ snap_elements = data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE_RAYCAST;
}
}
#endif
@@ -649,6 +649,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
.edit_mode_type = edit_mode_type,
.use_occlusion_test = use_occlusion_test,
},
+ NULL,
mval_fl,
prev_co,
&dist_px,
@@ -744,7 +745,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
(SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) {
snap_elem_index[1] = index;
}
- else if (snap_elem == SCE_SNAP_MODE_FACE) {
+ else if (snap_elem == SCE_SNAP_MODE_FACE_RAYCAST) {
snap_elem_index[2] = index;
}
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index 5fbdbef676c..041663b4a00 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -908,12 +908,13 @@ void ED_view3d_cursor3d_position_rotation(bContext *C,
CTX_data_ensure_evaluated_depsgraph(C),
region,
v3d,
- SCE_SNAP_MODE_FACE,
+ SCE_SNAP_MODE_FACE_RAYCAST,
&(const struct SnapObjectParams){
.snap_target_select = SCE_SNAP_TARGET_ALL,
.edit_mode_type = SNAP_GEOM_FINAL,
.use_occlusion_test = true,
},
+ NULL,
mval_fl,
NULL,
&dist_px,
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
index 9aae30c4a7e..96f242dba9f 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
@@ -357,11 +357,12 @@ static bool view3d_ruler_item_mousemove(const bContext *C,
depsgraph,
ruler_info->region,
v3d,
- SCE_SNAP_MODE_FACE,
+ SCE_SNAP_MODE_FACE_RAYCAST,
&(const struct SnapObjectParams){
.snap_target_select = SCE_SNAP_TARGET_ALL,
.edit_mode_type = SNAP_GEOM_CAGE,
},
+ NULL,
mval_fl,
NULL,
&dist_px,
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index 960d9a25ca7..d9e23b98c66 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -1577,7 +1577,7 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
if (transformModeUseSnap(t)) {
if (!(t->modifiers & MOD_SNAP) != !(t->tsnap.flag & SCE_SNAP)) {
/* Type is #eSnapFlag, but type must match various snap attributes in #ToolSettings. */
- char *snap_flag_ptr;
+ short *snap_flag_ptr;
wmMsgParams_RNA msg_key_params = {{0}};
RNA_pointer_create(&t->scene->id, &RNA_ToolSettings, ts, &msg_key_params.ptr);
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index a35942aedec..76573c85d52 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -306,9 +306,9 @@ typedef struct TransSnap {
eSnapTargetSelect target_select;
bool align;
bool project;
- bool snap_self;
bool peel;
bool use_backface_culling;
+ short face_nearest_steps;
eTSnap status;
/* Snapped Element Type (currently for objects only). */
eSnapMode snapElem;
diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c
index 0bb00032561..658901a6991 100644
--- a/source/blender/editors/transform/transform_constraints.c
+++ b/source/blender/editors/transform/transform_constraints.c
@@ -402,7 +402,7 @@ static void applyAxisConstraintVec(const TransInfo *t,
if (activeSnap(t)) {
if (validSnap(t)) {
is_snap_to_edge = (t->tsnap.snapElem & SCE_SNAP_MODE_EDGE) != 0;
- is_snap_to_face = (t->tsnap.snapElem & SCE_SNAP_MODE_FACE) != 0;
+ is_snap_to_face = (t->tsnap.snapElem & SCE_SNAP_MODE_FACE_RAYCAST) != 0;
is_snap_to_point = !is_snap_to_edge && !is_snap_to_face;
}
else if (t->tsnap.snapElem & SCE_SNAP_MODE_GRID) {
diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c
index e1b25acb21e..a031a6cd45b 100644
--- a/source/blender/editors/transform/transform_convert_armature.c
+++ b/source/blender/editors/transform/transform_convert_armature.c
@@ -1192,7 +1192,7 @@ static void restoreBones(TransDataContainer *tc)
void recalcData_edit_armature(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
- applyProject(t);
+ applySnappingIndividual(t);
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
diff --git a/source/blender/editors/transform/transform_convert_curve.c b/source/blender/editors/transform/transform_convert_curve.c
index 51acfb2a788..b9581aa1e31 100644
--- a/source/blender/editors/transform/transform_convert_curve.c
+++ b/source/blender/editors/transform/transform_convert_curve.c
@@ -418,7 +418,7 @@ void createTransCurveVerts(TransInfo *t)
void recalcData_curve(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
- applyProject(t);
+ applySnappingIndividual(t);
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
diff --git a/source/blender/editors/transform/transform_convert_lattice.c b/source/blender/editors/transform/transform_convert_lattice.c
index f02d4e94448..d3c34697237 100644
--- a/source/blender/editors/transform/transform_convert_lattice.c
+++ b/source/blender/editors/transform/transform_convert_lattice.c
@@ -101,7 +101,7 @@ void createTransLatticeVerts(TransInfo *t)
void recalcData_lattice(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
- applyProject(t);
+ applySnappingIndividual(t);
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
diff --git a/source/blender/editors/transform/transform_convert_mball.c b/source/blender/editors/transform/transform_convert_mball.c
index 7cba4f97886..5b2a1f8336d 100644
--- a/source/blender/editors/transform/transform_convert_mball.c
+++ b/source/blender/editors/transform/transform_convert_mball.c
@@ -122,7 +122,7 @@ void createTransMBallVerts(TransInfo *t)
void recalcData_mball(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
- applyProject(t);
+ applySnappingIndividual(t);
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
if (tc->data_len) {
diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c
index d4b12142162..c8d943df3fa 100644
--- a/source/blender/editors/transform/transform_convert_mesh.c
+++ b/source/blender/editors/transform/transform_convert_mesh.c
@@ -1940,7 +1940,7 @@ static void tc_mesh_partial_types_calc(TransInfo *t, struct PartialTypeState *r_
}
/* With projection, transform isn't affine. */
- if (activeSnap_with_project(t)) {
+ if (activeSnap_SnappingIndividual(t)) {
if (partial_for_looptri == PARTIAL_TYPE_GROUP) {
partial_for_looptri = PARTIAL_TYPE_ALL;
}
@@ -2056,7 +2056,7 @@ void recalcData_mesh(TransInfo *t)
bool is_canceling = t->state == TRANS_CANCEL;
/* Apply corrections. */
if (!is_canceling) {
- applyProject(t);
+ applySnappingIndividual(t);
bool do_mirror = !(t->flag & T_NO_MIRROR);
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c
index 5879a65eb4b..47e78656861 100644
--- a/source/blender/editors/transform/transform_convert_object.c
+++ b/source/blender/editors/transform/transform_convert_object.c
@@ -865,7 +865,7 @@ void recalcData_objects(TransInfo *t)
bool motionpath_update = false;
if (t->state != TRANS_CANCEL) {
- applyProject(t);
+ applySnappingIndividual(t);
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
diff --git a/source/blender/editors/transform/transform_convert_object_texspace.c b/source/blender/editors/transform/transform_convert_object_texspace.c
index 1f58ec80f02..e5a66f8a1d2 100644
--- a/source/blender/editors/transform/transform_convert_object_texspace.c
+++ b/source/blender/editors/transform/transform_convert_object_texspace.c
@@ -90,7 +90,7 @@ void recalcData_texspace(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
- applyProject(t);
+ applySnappingIndividual(t);
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
diff --git a/source/blender/editors/transform/transform_convert_particle.c b/source/blender/editors/transform/transform_convert_particle.c
index d7b0f378c8a..31e73288ad0 100644
--- a/source/blender/editors/transform/transform_convert_particle.c
+++ b/source/blender/editors/transform/transform_convert_particle.c
@@ -238,7 +238,7 @@ static void flushTransParticles(TransInfo *t)
void recalcData_particles(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
- applyProject(t);
+ applySnappingIndividual(t);
}
flushTransParticles(t);
}
diff --git a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c
index 2327aa4e9c4..8d790b4699b 100644
--- a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c
+++ b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c
@@ -84,7 +84,7 @@ static void applyNormalRotation(TransInfo *t, const int UNUSED(mval[2]))
transform_snap_increment(t, &angle);
- applySnapping(t, &angle);
+ applySnappingAsGroup(t, &angle);
applyNumInput(&t->num, &angle);
diff --git a/source/blender/editors/transform/transform_mode_edge_seq_slide.c b/source/blender/editors/transform/transform_mode_edge_seq_slide.c
index 9a732562709..5ca1fdf75c6 100644
--- a/source/blender/editors/transform/transform_mode_edge_seq_slide.c
+++ b/source/blender/editors/transform/transform_mode_edge_seq_slide.c
@@ -87,7 +87,7 @@ static void applySeqSlide(TransInfo *t, const int UNUSED(mval[2]))
}
else {
copy_v2_v2(values_final, t->values);
- applySnapping(t, values_final);
+ applySnappingAsGroup(t, values_final);
transform_convert_sequencer_channel_clamp(t, values_final);
if (t->con.mode & CON_APPLY) {
diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c
index b8e9a0d1a4d..a3c49d2362f 100644
--- a/source/blender/editors/transform/transform_mode_edge_slide.c
+++ b/source/blender/editors/transform/transform_mode_edge_slide.c
@@ -1292,7 +1292,7 @@ static void edge_slide_snap_apply(TransInfo *t, float *value)
side_index = t_snap >= t_mid;
}
- if (t->tsnap.snapElem & (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE)) {
+ if (t->tsnap.snapElem & (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE_RAYCAST)) {
float co_dir[3];
sub_v3_v3v3(co_dir, co_dest[side_index], co_orig);
normalize_v3(co_dir);
@@ -1444,7 +1444,7 @@ static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2]))
final = t->values[0] + t->values_modal_offset[0];
- applySnapping(t, &final);
+ applySnappingAsGroup(t, &final);
if (!validSnap(t)) {
transform_snap_increment(t, &final);
}
diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c
index ffae651e4aa..31d40486afc 100644
--- a/source/blender/editors/transform/transform_mode_resize.c
+++ b/source/blender/editors/transform/transform_mode_resize.c
@@ -105,7 +105,7 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2]))
constraintNumInput(t, t->values_final);
}
- applySnapping(t, t->values_final);
+ applySnappingAsGroup(t, t->values_final);
}
size_to_mat3(mat, t->values_final);
diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c
index f75c65448df..a7207b36578 100644
--- a/source/blender/editors/transform/transform_mode_rotate.c
+++ b/source/blender/editors/transform/transform_mode_rotate.c
@@ -305,7 +305,7 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2]))
final = large_rotation_limit(final);
}
else {
- applySnapping(t, &final);
+ applySnappingAsGroup(t, &final);
if (!(activeSnap(t) && validSnap(t))) {
transform_snap_increment(t, &final);
}
diff --git a/source/blender/editors/transform/transform_mode_skin_resize.c b/source/blender/editors/transform/transform_mode_skin_resize.c
index 8099449ec23..bdbb66b72f4 100644
--- a/source/blender/editors/transform/transform_mode_skin_resize.c
+++ b/source/blender/editors/transform/transform_mode_skin_resize.c
@@ -99,7 +99,7 @@ static void applySkinResize(TransInfo *t, const int UNUSED(mval[2]))
constraintNumInput(t, t->values_final);
}
- applySnapping(t, t->values_final);
+ applySnappingAsGroup(t, t->values_final);
}
size_to_mat3(mat_final, t->values_final);
diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c
index 3c6b6ea4117..65690f9069d 100644
--- a/source/blender/editors/transform/transform_mode_translate.c
+++ b/source/blender/editors/transform/transform_mode_translate.c
@@ -470,7 +470,7 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2]))
}
t->tsnap.snapElem = SCE_SNAP_MODE_NONE;
- applySnapping(t, global_dir);
+ applySnappingAsGroup(t, global_dir);
transform_snap_grid(t, global_dir);
if (t->con.mode & CON_APPLY) {
diff --git a/source/blender/editors/transform/transform_mode_vert_slide.c b/source/blender/editors/transform/transform_mode_vert_slide.c
index 77c5707d814..674ffcf17a8 100644
--- a/source/blender/editors/transform/transform_mode_vert_slide.c
+++ b/source/blender/editors/transform/transform_mode_vert_slide.c
@@ -539,7 +539,7 @@ static void vert_slide_snap_apply(TransInfo *t, float *value)
getSnapPoint(t, dvec);
sub_v3_v3(dvec, t->tsnap.snapTarget);
- if (t->tsnap.snapElem & (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE)) {
+ if (t->tsnap.snapElem & (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE_RAYCAST)) {
float co_dir[3];
sub_v3_v3v3(co_dir, co_curr_3d, co_orig_3d);
normalize_v3(co_dir);
@@ -568,7 +568,7 @@ static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2]))
final = t->values[0] + t->values_modal_offset[0];
- applySnapping(t, &final);
+ applySnappingAsGroup(t, &final);
if (!validSnap(t)) {
transform_snap_increment(t, &final);
}
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index 649217092aa..22d062a71dc 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;
}
@@ -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];
@@ -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);
diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h
index 6db027df067..3672e76c778 100644
--- a/source/blender/editors/transform/transform_snap.h
+++ b/source/blender/editors/transform/transform_snap.h
@@ -40,15 +40,16 @@ float transform_snap_increment_get(const TransInfo *t);
bool transform_snap_grid(TransInfo *t, float *val);
bool activeSnap(const TransInfo *t);
-bool activeSnap_with_project(const TransInfo *t);
+bool activeSnap_SnappingIndividual(const TransInfo *t);
+bool activeSnap_SnappingAsGroup(const TransInfo *t);
bool validSnap(const TransInfo *t);
void initSnapping(struct TransInfo *t, struct wmOperator *op);
void freeSnapping(struct TransInfo *t);
-void applyProject(TransInfo *t);
+void applySnappingIndividual(TransInfo *t);
void applyGridAbsolute(TransInfo *t);
-void applySnapping(TransInfo *t, float *vec);
+void applySnappingAsGroup(TransInfo *t, float *vec);
void resetSnapping(TransInfo *t);
eRedrawFlag handleSnapping(TransInfo *t, const struct wmEvent *event);
void drawSnapping(const struct bContext *C, TransInfo *t);
diff --git a/source/blender/editors/transform/transform_snap_object.cc b/source/blender/editors/transform/transform_snap_object.cc
index bd3d29fac8a..8a1eca3a6f7 100644
--- a/source/blender/editors/transform/transform_snap_object.cc
+++ b/source/blender/editors/transform/transform_snap_object.cc
@@ -405,6 +405,62 @@ static SnapData_EditMesh *snap_object_data_editmesh_get(SnapObjectContext *sctx,
return sod;
}
+static BVHTreeFromMesh *snap_object_data_mesh_treedata_get(SnapObjectContext *sctx,
+ Object *ob_eval,
+ const Mesh *me_eval,
+ bool use_hide)
+{
+ SnapData_Mesh *sod = snap_object_data_mesh_get(sctx, ob_eval, me_eval, use_hide);
+ return &sod->treedata_mesh;
+}
+
+static BVHTreeFromEditMesh *snap_object_data_editmesh_treedata_get(SnapObjectContext *sctx,
+ Object *ob_eval,
+ BMEditMesh *em)
+{
+ SnapData_EditMesh *sod = snap_object_data_editmesh_get(sctx, ob_eval, em);
+
+ BVHTreeFromEditMesh *treedata = &sod->treedata_editmesh;
+
+ if (treedata->tree == nullptr) {
+ /* Operators only update the editmesh looptris of the original mesh. */
+ BLI_assert(sod->treedata_editmesh.em ==
+ BKE_editmesh_from_object(DEG_get_original_object(ob_eval)));
+ em = sod->treedata_editmesh.em;
+
+ if (sctx->callbacks.edit_mesh.test_face_fn) {
+ BMesh *bm = em->bm;
+ BLI_assert(poly_to_tri_count(bm->totface, bm->totloop) == em->tottri);
+
+ BLI_bitmap *elem_mask = BLI_BITMAP_NEW(em->tottri, __func__);
+ int looptri_num_active = BM_iter_mesh_bitmap_from_filter_tessface(
+ bm,
+ elem_mask,
+ sctx->callbacks.edit_mesh.test_face_fn,
+ sctx->callbacks.edit_mesh.user_data);
+
+ bvhtree_from_editmesh_looptri_ex(treedata, em, elem_mask, looptri_num_active, 0.0f, 4, 6);
+
+ MEM_freeN(elem_mask);
+ }
+ else {
+ /* Only cache if BVH-tree is created without a mask.
+ * This helps keep a standardized BVH-tree in cache. */
+ BKE_bvhtree_from_editmesh_get(treedata,
+ em,
+ 4,
+ BVHTREE_FROM_EM_LOOPTRI,
+ &sod->mesh_runtime->bvh_cache,
+ static_cast<ThreadMutex *>(sod->mesh_runtime->eval_mutex));
+ }
+ }
+ if (treedata == nullptr || treedata->tree == nullptr) {
+ return nullptr;
+ }
+
+ return treedata;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -419,16 +475,16 @@ using IterSnapObjsCallback = void (*)(SnapObjectContext *sctx,
void *data);
static bool snap_object_is_snappable(const SnapObjectContext *sctx,
- const eSnapTargetSelect snap_select,
+ const eSnapTargetSelect snap_target_select,
const Base *base_act,
- const Base *base,
- const bool is_in_object_mode)
+ const Base *base)
{
if (!BASE_VISIBLE(sctx->runtime.v3d, base)) {
return false;
}
- if ((snap_select == SCE_SNAP_TARGET_ALL) || (base->flag_legacy & BA_TRANSFORM_LOCKED_IN_PLACE)) {
+ if ((snap_target_select == SCE_SNAP_TARGET_ALL) ||
+ (base->flag_legacy & BA_TRANSFORM_LOCKED_IN_PLACE)) {
return true;
}
@@ -436,25 +492,37 @@ static bool snap_object_is_snappable(const SnapObjectContext *sctx,
return false;
}
- if (snap_select == SCE_SNAP_TARGET_NOT_ACTIVE) {
- return base_act != base;
- }
+ /* get base attributes */
+ const bool is_active = (base_act == base);
+ const bool is_selected = (base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL);
+ const bool is_edited = (base->object->mode == OB_MODE_EDIT);
+ const bool is_selectable = (base->flag & BASE_SELECTABLE);
+ const bool is_in_object_mode = (base_act == NULL) || (base_act->object->mode == OB_MODE_OBJECT);
- if (snap_select == SCE_SNAP_TARGET_NOT_EDITED) {
- return base->object->mode != OB_MODE_EDIT;
+ if (is_edited) {
+ if (is_active) {
+ if (snap_target_select & SCE_SNAP_TARGET_NOT_ACTIVE) {
+ return false;
+ }
+ }
+ else {
+ if (snap_target_select & SCE_SNAP_TARGET_NOT_EDITED) {
+ return false;
+ }
+ }
}
- if (snap_select == SCE_SNAP_TARGET_NOT_SELECTED) {
- if (is_in_object_mode) {
- return !((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL));
- }
+ if ((snap_target_select & SCE_SNAP_TARGET_NOT_NONEDITED) && !is_edited) {
+ return false;
+ }
- /* What is selectable or not is part of the object and depends on the mode. */
- return true;
+ if ((snap_target_select & SCE_SNAP_TARGET_ONLY_SELECTABLE) && !is_selectable) {
+ return false;
}
- if (snap_select == SCE_SNAP_TARGET_ONLY_SELECTABLE) {
- return (base->flag & BASE_SELECTABLE) != 0;
+ if ((snap_target_select & SCE_SNAP_TARGET_NOT_SELECTED) && is_in_object_mode && is_selected) {
+ /* What is selectable or not is part of the object and depends on the mode. */
+ return false;
}
return true;
@@ -470,11 +538,10 @@ static void iter_snap_objects(SnapObjectContext *sctx,
{
ViewLayer *view_layer = DEG_get_input_view_layer(sctx->runtime.depsgraph);
const eSnapTargetSelect snap_target_select = params->snap_target_select;
-
Base *base_act = view_layer->basact;
- const bool is_in_object_mode = !base_act || base_act->object->mode == OB_MODE_OBJECT;
+
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
- if (!snap_object_is_snappable(sctx, snap_target_select, base_act, base, is_in_object_mode)) {
+ if (!snap_object_is_snappable(sctx, snap_target_select, base_act, base)) {
continue;
}
@@ -850,40 +917,9 @@ static bool raycastEditMesh(SnapObjectContext *sctx,
len_diff = 0.0f;
}
- BVHTreeFromEditMesh *treedata = &sod->treedata_editmesh;
-
- if (treedata->tree == nullptr) {
- em = sod->treedata_editmesh.em;
-
- if (sctx->callbacks.edit_mesh.test_face_fn) {
- BMesh *bm = em->bm;
- BLI_assert(poly_to_tri_count(bm->totface, bm->totloop) == em->tottri);
-
- BLI_bitmap *elem_mask = BLI_BITMAP_NEW(em->tottri, __func__);
- int looptri_num_active = BM_iter_mesh_bitmap_from_filter_tessface(
- bm,
- elem_mask,
- sctx->callbacks.edit_mesh.test_face_fn,
- sctx->callbacks.edit_mesh.user_data);
-
- bvhtree_from_editmesh_looptri_ex(treedata, em, elem_mask, looptri_num_active, 0.0f, 4, 6);
-
- MEM_freeN(elem_mask);
- }
- else {
- /* Only cache if bvhtree is created without a mask.
- * This helps keep a standardized bvhtree in cache. */
- BKE_bvhtree_from_editmesh_get(treedata,
- em,
- 4,
- BVHTREE_FROM_EM_LOOPTRI,
- &sod->mesh_runtime->bvh_cache,
- static_cast<ThreadMutex *>(sod->mesh_runtime->eval_mutex));
- }
-
- if (treedata->tree == nullptr) {
- return retval;
- }
+ BVHTreeFromEditMesh *treedata = snap_object_data_editmesh_treedata_get(sctx, ob_eval, em);
+ if (treedata == nullptr) {
+ return retval;
}
float timat[3][3]; /* transpose inverse matrix for normals */
@@ -1098,7 +1134,7 @@ static void raycast_obj_fn(SnapObjectContext *sctx,
* \param r_loc: Hit location.
* \param r_no: Hit normal (optional).
* \param r_index: Hit index or -1 when no valid index is found.
- * (currently only set to the polygon index when using `snap_to == SCE_SNAP_MODE_FACE`).
+ * (currently only set to the polygon index when using `snap_to == SCE_SNAP_MODE_FACE_RAYCAST`).
* \param r_ob: Hit object.
* \param r_obmat: Object matrix (may not be #Object.obmat with dupli-instances).
* \param r_hit_list: List of #SnapObjectHitDepth (caller must free).
@@ -1150,6 +1186,324 @@ static bool raycastObjects(SnapObjectContext *sctx,
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Surface Snap Funcs
+ * \{ */
+
+struct NearestWorldObjUserData {
+ const float *init_co;
+ const float *curr_co;
+ /* return args */
+ float *r_loc;
+ float *r_no;
+ int *r_index;
+ float r_dist_sq;
+ Object **r_ob;
+ float (*r_obmat)[4];
+ ListBase *r_hit_list;
+ bool ret;
+};
+
+static void nearest_world_tree_co(BVHTree *tree,
+ BVHTree_NearestPointCallback nearest_cb,
+ void *treedata,
+ float co[3],
+ float r_co[3],
+ float r_no[3],
+ int *r_index,
+ float *r_dist_sq)
+{
+ BVHTreeNearest nearest = {};
+ nearest.index = -1;
+ copy_v3_fl(nearest.co, FLT_MAX);
+ nearest.dist_sq = FLT_MAX;
+
+ BLI_bvhtree_find_nearest(tree, co, &nearest, nearest_cb, treedata);
+
+ if (r_co) {
+ copy_v3_v3(r_co, nearest.co);
+ }
+ if (r_no) {
+ copy_v3_v3(r_no, nearest.no);
+ }
+ if (r_index) {
+ *r_index = nearest.index;
+ }
+ if (r_dist_sq) {
+ float diff[3];
+ sub_v3_v3v3(diff, co, nearest.co);
+ *r_dist_sq = len_squared_v3(diff);
+ }
+}
+
+static bool nearest_world_tree(SnapObjectContext *UNUSED(sctx),
+ const struct SnapObjectParams *params,
+ BVHTree *tree,
+ BVHTree_NearestPointCallback nearest_cb,
+ void *treedata,
+ const float (*obmat)[4],
+ const float init_co[3],
+ const float curr_co[3],
+ float *r_dist_sq,
+ float *r_loc,
+ float *r_no,
+ int *r_index)
+{
+ if (curr_co == nullptr || init_co == nullptr) {
+ /* No location to work with, so just return. */
+ return false;
+ }
+
+ float imat[4][4];
+ invert_m4_m4(imat, obmat);
+
+ float timat[3][3]; /* transpose inverse matrix for normals */
+ transpose_m3_m4(timat, imat);
+
+ /* compute offset between init co and prev co in local space */
+ float init_co_local[3], curr_co_local[3];
+ float delta_local[3];
+ mul_v3_m4v3(init_co_local, imat, init_co);
+ mul_v3_m4v3(curr_co_local, imat, curr_co);
+ sub_v3_v3v3(delta_local, curr_co_local, init_co_local);
+
+ float dist_sq;
+ if (params->keep_on_same_target) {
+ nearest_world_tree_co(
+ tree, nearest_cb, treedata, init_co_local, nullptr, nullptr, nullptr, &dist_sq);
+ }
+ else {
+ /* NOTE: when `params->face_nearest_steps == 1`, the return variables of function below contain
+ * the answer. We could return immediately after updating r_loc, r_no, r_index, but that would
+ * also complicate the code. Foregoing slight optimization for code clarity. */
+ nearest_world_tree_co(
+ tree, nearest_cb, treedata, curr_co_local, nullptr, nullptr, nullptr, &dist_sq);
+ }
+ if (*r_dist_sq <= dist_sq) {
+ return false;
+ }
+ *r_dist_sq = dist_sq;
+
+ /* scale to make `snap_face_nearest_steps` steps */
+ float step_scale_factor = 1.0f / max_ff(1.0f, (float)params->face_nearest_steps);
+ mul_v3_fl(delta_local, step_scale_factor);
+
+ float co_local[3];
+ float no_local[3];
+ int index;
+
+ copy_v3_v3(co_local, init_co_local);
+
+ for (int i = 0; i < params->face_nearest_steps; i++) {
+ add_v3_v3(co_local, delta_local);
+ nearest_world_tree_co(
+ tree, nearest_cb, treedata, co_local, co_local, no_local, &index, nullptr);
+ }
+
+ mul_v3_m4v3(r_loc, obmat, co_local);
+
+ if (r_no) {
+ mul_v3_m3v3(r_no, timat, no_local);
+ normalize_v3(r_no);
+ }
+
+ if (r_index) {
+ *r_index = index;
+ }
+
+ return true;
+}
+
+static bool nearest_world_mesh(SnapObjectContext *sctx,
+ const struct SnapObjectParams *params,
+ Object *ob_eval,
+ const Mesh *me_eval,
+ const float (*obmat)[4],
+ bool use_hide,
+ const float init_co[3],
+ const float curr_co[3],
+ float *r_dist_sq,
+ float *r_loc,
+ float *r_no,
+ int *r_index)
+{
+ BVHTreeFromMesh *treedata = snap_object_data_mesh_treedata_get(sctx, ob_eval, me_eval, use_hide);
+ if (treedata == nullptr || treedata->tree == nullptr) {
+ return false;
+ }
+
+ return nearest_world_tree(sctx,
+ params,
+ treedata->tree,
+ treedata->nearest_callback,
+ treedata,
+ obmat,
+ init_co,
+ curr_co,
+ r_dist_sq,
+ r_loc,
+ r_no,
+ r_index);
+}
+
+static bool nearest_world_editmesh(SnapObjectContext *sctx,
+ const struct SnapObjectParams *params,
+ Object *ob_eval,
+ BMEditMesh *em,
+ const float (*obmat)[4],
+ const float init_co[3],
+ const float curr_co[3],
+ float *r_dist_sq,
+ float *r_loc,
+ float *r_no,
+ int *r_index)
+{
+ BVHTreeFromEditMesh *treedata = snap_object_data_editmesh_treedata_get(sctx, ob_eval, em);
+ if (treedata == nullptr || treedata->tree == nullptr) {
+ return false;
+ }
+
+ return nearest_world_tree(sctx,
+ params,
+ treedata->tree,
+ treedata->nearest_callback,
+ treedata,
+ obmat,
+ init_co,
+ curr_co,
+ r_dist_sq,
+ r_loc,
+ r_no,
+ r_index);
+}
+static void nearest_world_object_fn(SnapObjectContext *sctx,
+ const struct SnapObjectParams *params,
+ Object *ob_eval,
+ const float obmat[4][4],
+ bool is_object_active,
+ void *data)
+{
+ struct NearestWorldObjUserData *dt = static_cast<NearestWorldObjUserData *>(data);
+
+ bool retval = false;
+ switch (ob_eval->type) {
+ case OB_MESH: {
+ const eSnapEditType edit_mode_type = params->edit_mode_type;
+ bool use_hide = false;
+ const Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide);
+ if (me_eval) {
+ retval = nearest_world_mesh(sctx,
+ params,
+ ob_eval,
+ me_eval,
+ obmat,
+ use_hide,
+ dt->init_co,
+ dt->curr_co,
+ &dt->r_dist_sq,
+ dt->r_loc,
+ dt->r_no,
+ dt->r_index);
+ }
+ else {
+ BMEditMesh *em = BKE_editmesh_from_object(ob_eval);
+ BLI_assert_msg(em == BKE_editmesh_from_object(DEG_get_original_object(ob_eval)),
+ "Make sure there is only one pointer for looptris");
+ retval = nearest_world_editmesh(sctx,
+ params,
+ ob_eval,
+ em,
+ obmat,
+ dt->init_co,
+ dt->curr_co,
+ &dt->r_dist_sq,
+ dt->r_loc,
+ dt->r_no,
+ dt->r_index);
+ }
+ break;
+ }
+ case OB_CURVES_LEGACY:
+ case OB_SURF:
+ case OB_FONT:
+ if (!is_object_active) {
+ const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval);
+ if (me_eval) {
+ retval = nearest_world_mesh(sctx,
+ params,
+ ob_eval,
+ me_eval,
+ obmat,
+ false,
+ dt->init_co,
+ dt->curr_co,
+ &dt->r_dist_sq,
+ dt->r_loc,
+ dt->r_no,
+ dt->r_index);
+ }
+ }
+ break;
+ }
+
+ if (retval) {
+ if (dt->r_ob) {
+ *dt->r_ob = ob_eval;
+ }
+ if (dt->r_obmat) {
+ copy_m4_m4(dt->r_obmat, obmat);
+ }
+ dt->ret = true;
+ }
+}
+
+/**
+ * Main Nearest World Surface Function
+ * ===================================
+ *
+ * Walks through all objects in the scene to find the nearest location on target surface.
+ *
+ * \param sctx: Snap context to store data.
+ * \param params: Settings for snapping.
+ * \param init_co: Initial location of source point.
+ * \param prev_co: Current location of source point after transformation but before snapping.
+ *
+ * Output Args
+ * -----------
+ *
+ * \param r_loc: Location of nearest point on target surface.
+ * \param r_no: Normal of nearest point on target surface.
+ * \param r_index: Index of nearest polygon on target surface.
+ * \param r_ob: Nearest target object.
+ * \param r_obmat: Nearest target matrix (may not be #Object.obmat with dupli-instances).
+ */
+static bool nearestWorldObjects(SnapObjectContext *sctx,
+ const struct SnapObjectParams *params,
+ const float init_co[3],
+ const float curr_co[3],
+ float *r_loc /* NOLINT */,
+ float *r_no /* NOLINT */,
+ int *r_index /* NOLINT */,
+ Object **r_ob,
+ float r_obmat[4][4])
+{
+ NearestWorldObjUserData data = {};
+ data.init_co = init_co;
+ data.curr_co = curr_co;
+ data.r_loc = r_loc;
+ data.r_no = r_no;
+ data.r_index = r_index;
+ data.r_dist_sq = FLT_MAX;
+ data.r_ob = r_ob;
+ data.r_obmat = r_obmat;
+ data.ret = false;
+
+ iter_snap_objects(sctx, params, nearest_world_object_fn, &data);
+ return data.ret;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Snap Nearest utilities
* \{ */
@@ -1842,7 +2196,8 @@ static eSnapMode snapArmature(SnapObjectContext *sctx,
{
eSnapMode retval = SCE_SNAP_MODE_NONE;
- if (sctx->runtime.snap_to_flag == SCE_SNAP_MODE_FACE) { /* Currently only edge and vert */
+ if (sctx->runtime.snap_to_flag == SCE_SNAP_MODE_FACE_RAYCAST) {
+ /* Currently only edge and vert */
return retval;
}
@@ -2328,7 +2683,7 @@ static eSnapMode snapMesh(SnapObjectContext *sctx,
float r_no[3],
int *r_index)
{
- BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE);
+ BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE_RAYCAST);
if (me_eval->totvert == 0) {
return SCE_SNAP_MODE_NONE;
}
@@ -2506,9 +2861,9 @@ static eSnapMode snapEditMesh(SnapObjectContext *sctx,
float r_no[3],
int *r_index)
{
- BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE);
+ BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE_RAYCAST);
- if ((sctx->runtime.snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_VERTEX) {
+ if ((sctx->runtime.snap_to_flag & ~SCE_SNAP_MODE_FACE_RAYCAST) == SCE_SNAP_MODE_VERTEX) {
if (em->bm->totvert == 0) {
return SCE_SNAP_MODE_NONE;
}
@@ -2811,7 +3166,7 @@ static void snap_obj_fn(SnapObjectContext *sctx,
* \param r_loc: Hit location.
* \param r_no: Hit normal (optional).
* \param r_index: Hit index or -1 when no valid index is found.
- * (currently only set to the polygon index when using `snap_to == SCE_SNAP_MODE_FACE`).
+ * (currently only set to the polygon index when using `snap_to == SCE_SNAP_MODE_FACE_RAYCAST`).
* \param r_ob: Hit object.
* \param r_obmat: Object matrix (may not be #Object.obmat with dupli-instances).
*/
@@ -3014,6 +3369,7 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
const View3D *v3d,
const eSnapMode snap_to_flag,
const SnapObjectParams *params,
+ const float init_co[3],
const float mval[2],
const float prev_co[3],
float *dist_px,
@@ -3045,11 +3401,36 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
bool use_occlusion_test = params->use_occlusion_test && !XRAY_ENABLED(v3d);
- if (snap_to_flag & SCE_SNAP_MODE_FACE || use_occlusion_test) {
+ /* Note: if both face raycast and face nearest are enabled, first find result of nearest, then
+ * override with raycast. */
+ if ((snap_to_flag & SCE_SNAP_MODE_FACE_NEAREST) && !has_hit) {
+ has_hit = nearestWorldObjects(
+ sctx, params, init_co, prev_co, loc, no, &index, &ob_eval, obmat);
+
+ if (has_hit) {
+ retval = SCE_SNAP_MODE_FACE_NEAREST;
+
+ copy_v3_v3(r_loc, loc);
+ if (r_no) {
+ copy_v3_v3(r_no, no);
+ }
+ if (r_ob) {
+ *r_ob = ob_eval;
+ }
+ if (r_obmat) {
+ copy_m4_m4(r_obmat, obmat);
+ }
+ if (r_index) {
+ *r_index = index;
+ }
+ }
+ }
+
+ if (snap_to_flag & SCE_SNAP_MODE_FACE_RAYCAST || use_occlusion_test) {
float ray_start[3], ray_normal[3];
if (!ED_view3d_win_to_ray_clipped_ex(
depsgraph, region, v3d, mval, nullptr, ray_normal, ray_start, true)) {
- return SCE_SNAP_MODE_NONE;
+ return retval;
}
float dummy_ray_depth = BVH_RAYCAST_DIST_MAX;
@@ -3071,8 +3452,8 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
copy_v3_v3(r_face_nor, no);
}
- if ((snap_to_flag & SCE_SNAP_MODE_FACE)) {
- retval = SCE_SNAP_MODE_FACE;
+ if ((snap_to_flag & SCE_SNAP_MODE_FACE_RAYCAST)) {
+ retval = SCE_SNAP_MODE_FACE_RAYCAST;
copy_v3_v3(r_loc, loc);
if (r_no) {
@@ -3193,6 +3574,7 @@ eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx,
const View3D *v3d,
const eSnapMode snap_to,
const SnapObjectParams *params,
+ const float init_co[3],
const float mval[2],
const float prev_co[3],
float *dist_px,
@@ -3209,6 +3591,7 @@ eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx,
v3d,
snap_to,
params,
+ init_co,
mval,
prev_co,
dist_px,
@@ -3226,6 +3609,7 @@ eSnapMode ED_transform_snap_object_project_view3d(SnapObjectContext *sctx,
const View3D *v3d,
const eSnapMode snap_to,
const SnapObjectParams *params,
+ const float init_co[3],
const float mval[2],
const float prev_co[3],
float *dist_px,
@@ -3238,6 +3622,7 @@ eSnapMode ED_transform_snap_object_project_view3d(SnapObjectContext *sctx,
v3d,
snap_to,
params,
+ init_co,
mval,
prev_co,
dist_px,
diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h
index 74db1d14bbc..6cc01d254ce 100644
--- a/source/blender/makesdna/DNA_scene_defaults.h
+++ b/source/blender/makesdna/DNA_scene_defaults.h
@@ -337,7 +337,9 @@
.snap_mode = SCE_SNAP_MODE_INCREMENT, \
.snap_node_mode = SCE_SNAP_MODE_GRID, \
.snap_uv_mode = SCE_SNAP_MODE_INCREMENT, \
+ .snap_flag = SCE_SNAP_TO_INCLUDE_EDITED | SCE_SNAP_TO_INCLUDE_NONEDITED, \
.snap_transform_mode_flag = SCE_SNAP_TRANSFORM_MODE_TRANSLATE, \
+ .snap_face_nearest_steps = 1, \
\
.curve_paint_settings = _DNA_DEFAULTS_CurvePaintSettings, \
\
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index 1c62a550e60..c45b06f8c85 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -1496,19 +1496,24 @@ typedef struct ToolSettings {
char transform_pivot_point;
char transform_flag;
/** Snap elements (per spacetype), #eSnapMode. */
- char snap_mode;
+ char _pad1[1];
+ short snap_mode;
char snap_node_mode;
char snap_uv_mode;
/** Generic flags (per spacetype), #eSnapFlag. */
- char snap_flag;
- char snap_flag_node;
- char snap_flag_seq;
- char snap_uv_flag;
+ short snap_flag;
+ short snap_flag_node;
+ short snap_flag_seq;
+ short snap_uv_flag;
/** Default snap source, #eSnapSourceSelect. */
- /* TODO(@gfxcoder): Rename `snap_target` to `snap_source_point`, because target is incorrect. */
+ /* 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). */
char snap_target;
/** Snap mask for transform modes, #eSnapTransformMode. */
char snap_transform_mode_flag;
+ /** Steps to break transformation into with face nearest snapping */
+ short snap_face_nearest_steps;
char proportional_edit, prop_mode;
/** Proportional edit, object mode. */
@@ -2085,10 +2090,15 @@ typedef enum eSnapFlag {
SCE_SNAP = (1 << 0),
SCE_SNAP_ROTATE = (1 << 1),
SCE_SNAP_PEEL_OBJECT = (1 << 2),
- SCE_SNAP_PROJECT = (1 << 3),
- SCE_SNAP_NO_SELF = (1 << 4),
+ SCE_SNAP_PROJECT = (1 << 3), /* Project individual elements instead of whole object. */
+ SCE_SNAP_NOT_TO_ACTIVE = (1 << 4), /* Was `SCE_SNAP_NO_SELF`, but self should be active. */
SCE_SNAP_ABS_GRID = (1 << 5),
SCE_SNAP_BACKFACE_CULLING = (1 << 6),
+ SCE_SNAP_KEEP_ON_SAME_OBJECT = (1 << 7),
+ /* see #eSnapTargetSelect */
+ SCE_SNAP_TO_INCLUDE_EDITED = (1 << 8),
+ SCE_SNAP_TO_INCLUDE_NONEDITED = (1 << 9),
+ SCE_SNAP_TO_ONLY_SELECTABLE = (1 << 10),
} eSnapFlag;
/* Due to dependency conflicts with Cycles, header cannot directly include `BLI_utildefines.h`. */
/* TODO: move this macro to a more general place. */
@@ -2096,7 +2106,7 @@ typedef enum eSnapFlag {
ENUM_OPERATORS(eSnapFlag, SCE_SNAP_BACKFACE_CULLING)
#endif
-/** #ToolSettings.snap_target and #TransSnap.source_select */
+/** See #ToolSettings.snap_target (to be renamed `snap_source`) and #TransSnap.source_select */
typedef enum eSnapSourceSelect {
SCE_SNAP_SOURCE_CLOSEST = 0,
SCE_SNAP_SOURCE_CENTER = 1,
@@ -2104,13 +2114,15 @@ typedef enum eSnapSourceSelect {
SCE_SNAP_SOURCE_ACTIVE = 3,
} eSnapSourceSelect;
-/** #TransSnap.target_select and #ToolSettings.snap_flag (SCE_SNAP_NO_SELF) */
+/** #TransSnap.target_select and #ToolSettings.snap_flag (#SCE_SNAP_NOT_TO_ACTIVE,
+ * #SCE_SNAP_TO_INCLUDE_EDITED, #SCE_SNAP_TO_INCLUDE_NONEDITED, #SCE_SNAP_TO_ONLY_SELECTABLE) */
typedef enum eSnapTargetSelect {
SCE_SNAP_TARGET_ALL = 0,
- SCE_SNAP_TARGET_NOT_SELECTED = 1,
- SCE_SNAP_TARGET_NOT_ACTIVE = 2,
- SCE_SNAP_TARGET_NOT_EDITED = 3,
- SCE_SNAP_TARGET_ONLY_SELECTABLE = 4,
+ SCE_SNAP_TARGET_NOT_SELECTED = (1 << 0),
+ SCE_SNAP_TARGET_NOT_ACTIVE = (1 << 1),
+ SCE_SNAP_TARGET_NOT_EDITED = (1 << 2),
+ SCE_SNAP_TARGET_ONLY_SELECTABLE = (1 << 3),
+ SCE_SNAP_TARGET_NOT_NONEDITED = (1 << 4),
} eSnapTargetSelect;
/** #ToolSettings.snap_mode */
@@ -2118,19 +2130,21 @@ typedef enum eSnapMode {
SCE_SNAP_MODE_NONE = 0,
SCE_SNAP_MODE_VERTEX = (1 << 0),
SCE_SNAP_MODE_EDGE = (1 << 1),
- SCE_SNAP_MODE_FACE = (1 << 2), /* TODO(@gfxcoder): Rename to `SCE_SNAP_MODE_FACE_RAYCAST`
- when other face snapping methods are added. */
+ SCE_SNAP_MODE_FACE_RAYCAST = (1 << 2),
SCE_SNAP_MODE_VOLUME = (1 << 3),
SCE_SNAP_MODE_EDGE_MIDPOINT = (1 << 4),
SCE_SNAP_MODE_EDGE_PERPENDICULAR = (1 << 5),
- SCE_SNAP_MODE_GEOM = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE |
- SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT),
+ SCE_SNAP_MODE_FACE_NEAREST = (1 << 8),
+
+ SCE_SNAP_MODE_GEOM = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE_RAYCAST |
+ SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT |
+ SCE_SNAP_MODE_FACE_NEAREST),
/** #ToolSettings.snap_node_mode */
SCE_SNAP_MODE_NODE_X = (1 << 0),
SCE_SNAP_MODE_NODE_Y = (1 << 1),
- /* #ToolSettings.snap_mode and #ToolSettings.snap_node_mode and #ToolSettings.snap_uv_mode */
+ /** #ToolSettings.snap_mode and #ToolSettings.snap_node_mode and #ToolSettings.snap_uv_mode */
SCE_SNAP_MODE_INCREMENT = (1 << 6),
SCE_SNAP_MODE_GRID = (1 << 7),
} eSnapMode;
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index cc7df54e648..daf4c99845d 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -151,7 +151,16 @@ const EnumPropertyItem rna_enum_snap_element_items[] = {
"Snap to increments of grid"},
{SCE_SNAP_MODE_VERTEX, "VERTEX", ICON_SNAP_VERTEX, "Vertex", "Snap to vertices"},
{SCE_SNAP_MODE_EDGE, "EDGE", ICON_SNAP_EDGE, "Edge", "Snap to edges"},
- {SCE_SNAP_MODE_FACE, "FACE", ICON_SNAP_FACE, "Face", "Snap to faces"},
+ {SCE_SNAP_MODE_FACE_RAYCAST,
+ "FACE", /* TODO(@gfxcoder): replace with "FACE_RAYCAST" as "FACE" is not descriptive. */
+ ICON_SNAP_FACE,
+ "Face Project",
+ "Snap by projecting onto faces"},
+ {SCE_SNAP_MODE_FACE_NEAREST,
+ "FACE_NEAREST",
+ ICON_SNAP_FACE_NEAREST,
+ "Face Nearest",
+ "Snap to nearest point on faces"},
{SCE_SNAP_MODE_VOLUME, "VOLUME", ICON_SNAP_VOLUME, "Volume", "Snap to volume"},
{SCE_SNAP_MODE_EDGE_MIDPOINT,
"EDGE_MIDPOINT",
@@ -3301,6 +3310,21 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Snap Element", "Type of element to snap to");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
+ prop = RNA_def_property(srna, "snap_face_nearest_steps", PROP_INT, PROP_FACTOR);
+ RNA_def_property_int_sdna(prop, NULL, "snap_face_nearest_steps");
+ RNA_def_property_range(prop, 1, 100);
+ RNA_def_property_ui_text(
+ prop,
+ "Face Nearest Steps",
+ "Number of steps to break transformation into for face nearest snapping");
+
+ prop = RNA_def_property(srna, "use_snap_to_same_target", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_KEEP_ON_SAME_OBJECT);
+ RNA_def_property_ui_text(
+ prop,
+ "Snap to Same Target",
+ "Snap only to target that source was initially near (Face Nearest Only)");
+
/* node editor uses own set of snap modes */
prop = RNA_def_property(srna, "snap_node_element", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "snap_node_mode");
@@ -3323,9 +3347,9 @@ static void rna_def_tool_settings(BlenderRNA *brna)
"Absolute grid alignment while translating (based on the pivot center)");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* 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). */
+ /* 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). */
prop = RNA_def_property(srna, "snap_target", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "snap_target");
RNA_def_property_enum_items(prop, rna_enum_snap_source_items);
@@ -3350,9 +3374,30 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Backface Culling", "Exclude back facing geometry from snapping");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
+ /* TODO(@gfxcoder): Rename `use_snap_self` to `use_snap_active`, because active is correct but
+ * self is not (breaks API). This only makes a difference when more than one mesh is edited. */
prop = RNA_def_property(srna, "use_snap_self", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_negative_sdna(prop, NULL, "snap_flag", SCE_SNAP_NO_SELF);
- RNA_def_property_ui_text(prop, "Project onto Self", "Snap onto itself (Edit Mode Only)");
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "snap_flag", SCE_SNAP_NOT_TO_ACTIVE);
+ RNA_def_property_ui_text(
+ prop, "Snap onto Active", "Snap onto itself only if enabled (Edit Mode Only)");
+ RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
+
+ prop = RNA_def_property(srna, "use_snap_edit", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_TO_INCLUDE_EDITED);
+ RNA_def_property_ui_text(
+ prop, "Snap onto Edited", "Snap onto non-active objects in Edit Mode (Edit Mode Only)");
+ RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
+
+ prop = RNA_def_property(srna, "use_snap_nonedit", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_TO_INCLUDE_NONEDITED);
+ RNA_def_property_ui_text(
+ prop, "Snap onto Non-edited", "Snap onto objects not in Edit Mode (Edit Mode Only)");
+ RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
+
+ prop = RNA_def_property(srna, "use_snap_selectable", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_TO_ONLY_SELECTABLE);
+ RNA_def_property_ui_text(
+ prop, "Snap onto Selectable Only", "Snap only onto objects that are selectable");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
prop = RNA_def_property(srna, "use_snap_translate", PROP_BOOLEAN, PROP_NONE);