WIP: UI: Snapping Redesign: Presets and filter support #108734

Draft
Germano Cavalcante wants to merge 5 commits from mano-wii/blender:ui_snapping_presets_menu into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
26 changed files with 461 additions and 274 deletions

View File

@ -0,0 +1,29 @@
attributes = (
"snap_target",
"snap_elements",
"snap_filter_active",
"snap_filter_edit",
"snap_filter_nonedit",
"snap_filter_nonselectable",
"use_snap_grid_absolute",
"use_snap_peel_object",
"use_snap_to_same_target",
"snap_face_nearest_steps",
"use_snap_align_rotation",
"use_snap_backface_culling",
"use_snap_translate",
"use_snap_rotate",
"use_snap_scale",
"show_snap_filter",
)
import bpy
toolsettings = bpy.context.scene.tool_settings
props = bpy.types.ToolSettings.bl_rna.properties
for attr in attributes:
prop = props[attr]
if prop.is_enum_flag:
setattr(toolsettings, attr, prop.default_flag)
else:
setattr(toolsettings, attr, prop.default)

View File

@ -0,0 +1,19 @@
import bpy
toolsettings = bpy.context.scene.tool_settings
toolsettings.snap_target = 'MEDIAN'
toolsettings.snap_elements = {'FACE_PROJECT', 'FACE_NEAREST', 'VERTEX'}
toolsettings.snap_filter_active = {'FACE', 'EDGE_MIDPOINT', 'VOLUME', 'EDGE', 'EDGE_PERPENDICULAR', 'VERTEX'}
toolsettings.snap_filter_edit = {'FACE', 'EDGE_MIDPOINT', 'VOLUME', 'FACE_PROJECT', 'EDGE', 'EDGE_PERPENDICULAR', 'FACE_NEAREST', 'VERTEX'}
toolsettings.snap_filter_nonedit = {'FACE_PROJECT', 'FACE_NEAREST'}
toolsettings.snap_filter_nonselectable = {'FACE', 'EDGE_MIDPOINT', 'VOLUME', 'FACE_PROJECT', 'EDGE', 'EDGE_PERPENDICULAR', 'FACE_NEAREST', 'VERTEX'}
toolsettings.use_snap_grid_absolute = False
toolsettings.use_snap_peel_object = False
toolsettings.use_snap_to_same_target = False
toolsettings.snap_face_nearest_steps = 1
toolsettings.use_snap_align_rotation = False
toolsettings.use_snap_backface_culling = False
toolsettings.use_snap_translate = True
toolsettings.use_snap_rotate = False
toolsettings.use_snap_scale = False
toolsettings.show_snap_filter = {'NONEDITED', 'ACTIVE'}

View File

@ -413,6 +413,38 @@ class AddPresetHairDynamics(AddPresetBase, Operator):
]
class AddPresetSnapping(AddPresetBase, Operator):
"""Add or remove a Snapping Preset"""
bl_idname = "scene.preset_add"
bl_label = "Add Snapping Preset"
preset_menu = "VIEW3D_MT_snapping_presets"
preset_defines = [
"toolsettings = bpy.context.scene.tool_settings"
]
preset_values = [
"toolsettings.snap_target",
"toolsettings.snap_elements",
"toolsettings.snap_filter_active",
"toolsettings.snap_filter_edit",
"toolsettings.snap_filter_nonedit",
"toolsettings.snap_filter_nonselectable",
"toolsettings.use_snap_grid_absolute",
"toolsettings.use_snap_peel_object",
"toolsettings.use_snap_to_same_target",
"toolsettings.snap_face_nearest_steps",
"toolsettings.use_snap_align_rotation",
"toolsettings.use_snap_backface_culling",
"toolsettings.use_snap_translate",
"toolsettings.use_snap_rotate",
"toolsettings.use_snap_scale",
"toolsettings.show_snap_filter",
]
preset_subdir = "snapping"
class AddPresetTextEditor(AddPresetBase, Operator):
"""Add or remove a Text Editor Preset"""
bl_idname = "text_editor.preset_add"
@ -710,6 +742,7 @@ classes = (
AddPresetOperator,
AddPresetRender,
AddPresetCameraSafeAreas,
AddPresetSnapping,
AddPresetTextEditor,
AddPresetTrackingCamera,
AddPresetTrackingSettings,

View File

@ -7060,11 +7060,48 @@ class VIEW3D_PT_overlay_weight_paint(Panel):
col.prop(overlay, "show_paint_wire")
class VIEW3D_MT_snapping_presets(Menu):
bl_label = "Presets"
preset_subdir = "snapping"
preset_operator = "script.execute_preset"
draw = Menu.draw_preset
class VIEW3D_PT_snapping(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'HEADER'
bl_label = "Snapping"
@staticmethod
def draw_elements(layout, ts, prop):
def draw_filters(row, value):
if 'ACTIVE' in ts.show_snap_filter:
row.prop_enum(ts, "snap_filter_active", value, text="", icon='EDITMODE_HLT')
if 'EDITED' in ts.show_snap_filter:
row.prop_enum(ts, "snap_filter_edit", value, text="", icon='OUTLINER_DATA_MESH')
if 'NONEDITED' in ts.show_snap_filter:
row.prop_enum(ts, "snap_filter_nonedit", value, text="", icon='OUTLINER_OB_MESH')
if 'NONSELECTABLE' in ts.show_snap_filter:
row.prop_enum(ts, "snap_filter_nonselectable", value, text="", icon='RESTRICT_SELECT_ON')
if not ts.show_snap_filter:
layout.prop(ts, prop)
return
row = layout.row()
row.column().prop(ts, prop)
row_col = row.column()
for item in bpy.types.ToolSettings.bl_rna.properties[prop].enum_items:
row = row_col.row(align=True)
if item.identifier == 'INCREMENT':
row.ui_units_y = 0.92
row.separator()
continue
row.active = item.identifier in ts.snap_elements
row.scale_y = 0.92
draw_filters(row, item.identifier)
def draw(self, context):
tool_settings = context.tool_settings
obj = context.active_object
@ -7073,15 +7110,26 @@ class VIEW3D_PT_snapping(Panel):
layout = self.layout
col = layout.column()
row = layout.row(align=True)
row.alignment = 'RIGHT'
row.menu("VIEW3D_MT_snapping_presets", text=VIEW3D_MT_snapping_presets.bl_label)
row.operator("scene.preset_add", text="", icon='ADD')
row.operator("scene.preset_add", text="", icon='REMOVE').remove_active = True
row.separator()
row.prop_menu_enum(tool_settings, "show_snap_filter", text="", icon='FILTER')
col = layout.column()
col.label(text="Snap With")
row = col.row(align=True)
row.prop(tool_settings, "snap_target", expand=True)
col.separator()
col.label(text="Snap To")
col.prop(tool_settings, "snap_elements_base", expand=True)
self.draw_elements(col, tool_settings, "snap_elements_base")
col.separator()
col.label(text="Snap Individual Elements To")
col.prop(tool_settings, "snap_elements_individual", expand=True)
self.draw_elements(col, tool_settings, "snap_elements_individual")
col.separator()
@ -7103,35 +7151,6 @@ class VIEW3D_PT_snapping(Panel):
col.separator()
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'}:
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',
)
col.label(text="Affect")
row = col.row(align=True)
row.prop(tool_settings, "use_snap_translate", text="Move", toggle=True)
@ -8354,6 +8373,7 @@ classes = (
VIEW3D_MT_wpaint_vgroup_lock_pie,
VIEW3D_MT_sculpt_face_sets_edit_pie,
VIEW3D_MT_sculpt_curves,
VIEW3D_MT_snapping_presets,
VIEW3D_PT_active_tool,
VIEW3D_PT_active_tool_duplicate,
VIEW3D_PT_view3d_properties,

View File

@ -3874,7 +3874,7 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
/* 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;
tool_settings->snap_flag |= SCE_SNAP_INCLUDE_EDITED | SCE_SNAP_INCLUDE_NONEDITED;
}
}
}

View File

@ -10,6 +10,7 @@
#include "CLG_log.h"
#include "DNA_defaults.h"
#include "DNA_movieclip_types.h"
#include "BLI_assert.h"
@ -182,13 +183,26 @@ void blo_do_versions_400(FileData * /*fd*/, Library * /*lib*/, Main *bmain)
if (!MAIN_VERSION_ATLEAST(bmain, 400, 5)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
const ToolSettings *ts_default = DNA_struct_default_get(ToolSettings);
ToolSettings *ts = scene->toolsettings;
eSnapFlag view_filter_flag = (SCE_SNAP_INCLUDE_ACTIVE | SCE_SNAP_INCLUDE_EDITED |
SCE_SNAP_INCLUDE_NONEDITED | SCE_SNAP_INCLUDE_NONSELECTABLE);
if (ts->snap_mode_tools != SCE_SNAP_MODE_NONE) {
ts->snap_mode_tools = SCE_SNAP_MODE_GEOM;
}
ts->snap_mode = ts_default->snap_mode;
ts->snap_filter_active = ts_default->snap_filter_active;
ts->snap_filter_edit = ts_default->snap_filter_edit;
ts->snap_filter_nonedit = ts_default->snap_filter_nonedit;
ts->snap_filter_nonselectable = ts_default->snap_filter_nonselectable;
ts->snap_flag &= ~view_filter_flag;
ts->snap_flag |= ts_default->snap_flag & view_filter_flag;
#define SCE_SNAP_PROJECT (1 << 3)
if (ts->snap_flag & SCE_SNAP_PROJECT) {
ts->snap_flag &= ~SCE_SNAP_PROJECT;
ts->snap_mode |= SCE_SNAP_MODE_FACE_RAYCAST;
}
#undef SCE_SNAP_PROJECT

View File

@ -5644,9 +5644,8 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
vc.v3d,
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_target_select = (vc.obedit != NULL) ? SCE_SNAP_TARGET_NOT_ACTIVE :
SCE_SNAP_TARGET_ALL,
.edit_mode_type = SNAP_GEOM_FINAL,
.exclude_active = (vc.obedit != NULL) ? SCE_SNAP_MODE_GEOM_ALL : SCE_SNAP_MODE_NONE,
},
NULL,
mval,

View File

@ -282,7 +282,6 @@ static int gizmo_move_modal(bContext *C,
CTX_wm_view3d(C),
(SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE),
&(const struct SnapObjectParams){
.snap_target_select = SCE_SNAP_TARGET_ALL,
.edit_mode_type = SNAP_GEOM_EDIT,
.use_occlusion_test = true,
},

View File

@ -1152,9 +1152,7 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph,
if (ED_transform_snap_object_project_ray(sctx,
depsgraph,
v3d,
&(const struct SnapObjectParams){
.snap_target_select = SCE_SNAP_TARGET_ALL,
},
&(const struct SnapObjectParams){0},
&ray_start[0],
&ray_normal[0],
&depth,

View File

@ -54,18 +54,26 @@ struct SnapObjectHitDepth {
/** parameters that define which objects will be used to snap. */
struct SnapObjectParams {
/* Special context sensitive handling for the active or selected object. */
eSnapTargetOP snap_target_select;
/* Geometry for snapping in edit mode. */
eSnapEditType edit_mode_type;
/* Filters. */
eSnapMode exclude_active;
eSnapMode exclude_selected;
eSnapMode exclude_edited;
eSnapMode exclude_nonedited;
eSnapMode exclude_nonselectable;
/* snap to the closest element, use when using more than one snap type */
bool exclude_moving : 1;
/* snap to the closest element, use when using more than one snap type */
bool use_occlusion_test : 1;
/* exclude back facing geometry from snapping */
bool use_backface_culling : 1;
/* 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 : 1;
/* Break nearest face snapping into steps to improve transformations across U-shaped targets. */
short face_nearest_steps;
};
typedef struct SnapObjectContext SnapObjectContext;

View File

@ -1925,17 +1925,18 @@ void EDBM_project_snap_verts(
ED_view3d_init_mats_rv3d(obedit, region->regiondata);
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = scene->toolsettings;
SnapObjectContext *snap_context = ED_transform_snap_object_context_create(scene, 0);
eSnapTargetOP target_op = SCE_SNAP_TARGET_NOT_ACTIVE;
const int snap_flag = scene->toolsettings->snap_flag;
struct SnapObjectParams snap_params = {0};
snap_params.edit_mode_type = SNAP_GEOM_FINAL;
snap_params.exclude_active = SCE_SNAP_MODE_GEOM_ALL;
snap_params.exclude_edited = ~ts->snap_filter_edit;
snap_params.exclude_nonedited = ~ts->snap_filter_nonedit;
snap_params.exclude_nonselectable = ~ts->snap_filter_nonselectable;
snap_params.use_occlusion_test = true;
SET_FLAG_FROM_TEST(
target_op, !(snap_flag & SCE_SNAP_TO_INCLUDE_EDITED), SCE_SNAP_TARGET_NOT_EDITED);
SET_FLAG_FROM_TEST(
target_op, !(snap_flag & SCE_SNAP_TO_INCLUDE_NONEDITED), SCE_SNAP_TARGET_NOT_NONEDITED);
SET_FLAG_FROM_TEST(
target_op, (snap_flag & SCE_SNAP_TO_ONLY_SELECTABLE), SCE_SNAP_TARGET_ONLY_SELECTABLE);
const int snap_flag = scene->toolsettings->snap_flag;
BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
@ -1947,11 +1948,7 @@ void EDBM_project_snap_verts(
region,
CTX_wm_view3d(C),
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_target_select = target_op,
.edit_mode_type = SNAP_GEOM_FINAL,
.use_occlusion_test = true,
},
&snap_params,
NULL,
mval,
NULL,

View File

@ -660,7 +660,6 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
v3d,
snap_elements,
&(const struct SnapObjectParams){
.snap_target_select = SCE_SNAP_TARGET_ALL,
.edit_mode_type = edit_mode_type,
.use_occlusion_test = use_occlusion_test,
},

View File

@ -913,7 +913,6 @@ void ED_view3d_cursor3d_position_rotation(bContext *C,
v3d,
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_target_select = SCE_SNAP_TARGET_ALL,
.edit_mode_type = SNAP_GEOM_FINAL,
.use_occlusion_test = true,
},

View File

@ -367,7 +367,6 @@ static bool view3d_ruler_item_mousemove(const bContext *C,
float3 &co_other = ruler_item->co[inter->co_index == 0 ? 2 : 0];
SnapObjectParams snap_object_params{};
snap_object_params.snap_target_select = SCE_SNAP_TARGET_ALL;
snap_object_params.edit_mode_type = SNAP_GEOM_CAGE;
eSnapMode hit = ED_transform_snap_object_project_view3d(snap_context,

View File

@ -408,7 +408,6 @@ static bool walk_floor_distance_get(RegionView3D *rv3d,
walk->depsgraph,
walk->v3d,
&(const struct SnapObjectParams){
.snap_target_select = SCE_SNAP_TARGET_ALL,
/* Avoid having to convert the edit-mesh to a regular mesh. */
.edit_mode_type = SNAP_GEOM_EDIT,
},
@ -449,9 +448,7 @@ static bool walk_ray_cast(RegionView3D *rv3d,
ret = ED_transform_snap_object_project_ray(walk->snap_context,
walk->depsgraph,
walk->v3d,
&(const struct SnapObjectParams){
.snap_target_select = SCE_SNAP_TARGET_ALL,
},
&(const struct SnapObjectParams){0},
ray_start,
ray_normal,
NULL,

View File

@ -1658,16 +1658,12 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
if ((prop = RNA_struct_find_property(op->ptr, "snap_elements"))) {
RNA_property_enum_set(op->ptr, prop, t->tsnap.mode);
RNA_boolean_set(
op->ptr, "use_snap_project", (t->tsnap.mode & SCE_SNAP_MODE_FACE_RAYCAST) != 0);
RNA_enum_set(op->ptr, "snap_target", t->tsnap.source_operation);
eSnapTargetOP target = t->tsnap.target_operation;
RNA_boolean_set(op->ptr, "use_snap_self", (target & SCE_SNAP_TARGET_NOT_ACTIVE) == 0);
RNA_boolean_set(op->ptr, "use_snap_edit", (target & SCE_SNAP_TARGET_NOT_EDITED) == 0);
RNA_boolean_set(op->ptr, "use_snap_nonedit", (target & SCE_SNAP_TARGET_NOT_NONEDITED) == 0);
RNA_boolean_set(
op->ptr, "use_snap_selectable", (target & SCE_SNAP_TARGET_ONLY_SELECTABLE) != 0);
RNA_boolean_set(op->ptr, "use_snap_self", t->tsnap.exclude_active == 0);
RNA_boolean_set(op->ptr, "use_snap_edit", t->tsnap.exclude_edited == 0);
RNA_boolean_set(op->ptr, "use_snap_nonedit", t->tsnap.exclude_nonedited == 0);
RNA_boolean_set(op->ptr, "use_snap_nonselectable", t->tsnap.exclude_nonselectable == 0);
}
/* Update `ToolSettings` for properties that change during modal. */

View File

@ -298,7 +298,11 @@ typedef struct TransSnap {
/* Part of source to snap to target */
eSnapSourceOP source_operation;
/* Determines which objects are possible target */
eSnapTargetOP target_operation;
eSnapMode exclude_active;
eSnapMode exclude_selected;
eSnapMode exclude_edited;
eSnapMode exclude_nonedited;
eSnapMode exclude_nonselectable;
short face_nearest_steps;
eTSnap status;
/* Snapped Element Type (currently for objects only). */

View File

@ -37,7 +37,8 @@ struct SnapSouceCustomData {
TransModeInfo *mode_info_prev;
void *customdata_mode_prev;
eSnapTargetOP target_operation_prev;
eSnapMode exclude_active_prev;
eSnapMode exclude_selected_prev;
struct {
void (*apply)(TransInfo *t, MouseInput *mi, const double mval[2], float output[3]);
@ -55,7 +56,8 @@ static void snapsource_end(TransInfo *t)
t->mode_info = customdata->mode_info_prev;
t->custom.mode.data = customdata->customdata_mode_prev;
t->tsnap.target_operation = customdata->target_operation_prev;
t->tsnap.exclude_active = customdata->exclude_active_prev;
t->tsnap.exclude_selected = customdata->exclude_selected_prev;
t->mouse.apply = customdata->mouse_prev.apply;
t->mouse.post = customdata->mouse_prev.post;
@ -165,7 +167,8 @@ void transform_mode_snap_source_init(TransInfo *t, wmOperator *UNUSED(op))
struct SnapSouceCustomData *customdata = MEM_callocN(sizeof(*customdata), __func__);
customdata->mode_info_prev = t->mode_info;
customdata->target_operation_prev = t->tsnap.target_operation;
customdata->exclude_active_prev = t->tsnap.exclude_active;
customdata->exclude_selected_prev = t->tsnap.exclude_selected;
customdata->mouse_prev.apply = t->mouse.apply;
customdata->mouse_prev.post = t->mouse.post;
@ -179,7 +182,8 @@ void transform_mode_snap_source_init(TransInfo *t, wmOperator *UNUSED(op))
}
t->mode_info = &TransMode_snapsource;
t->tsnap.target_operation = SCE_SNAP_TARGET_ALL;
t->tsnap.exclude_active = SCE_SNAP_MODE_NONE;
t->tsnap.exclude_selected = SCE_SNAP_MODE_NONE;
t->tsnap.status &= ~SNAP_SOURCE_FOUND;
if ((t->tsnap.mode & ~(SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) == 0) {

View File

@ -606,15 +606,6 @@ static bool transform_poll_property(const bContext *C, wmOperator *op, const Pro
}
}
/* Snapping. */
{
if (STREQ(prop_id, "use_snap_project")) {
if (RNA_boolean_get(op->ptr, "snap") == false) {
return false;
}
}
}
return true;
}
@ -705,8 +696,6 @@ void Transform_Properties(wmOperatorType *ot, int flags)
"");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_ENUM_FLAG);
RNA_def_boolean(ot->srna, "use_snap_project", false, "Project Individual Elements", "");
/* 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). Use "Source snap point" and "Point on source that will snap to
@ -722,7 +711,7 @@ void Transform_Properties(wmOperatorType *ot, int flags)
prop = RNA_def_boolean(ot->srna, "use_snap_nonedit", true, "Target: Include Non-Edited", "");
RNA_def_property_flag(prop, PROP_HIDDEN);
prop = RNA_def_boolean(
ot->srna, "use_snap_selectable", false, "Target: Exclude Non-Selectable", "");
ot->srna, "use_snap_selectable", true, "Target: Include Non-Selectable", "");
RNA_def_property_flag(prop, PROP_HIDDEN);
prop = RNA_def_float_vector(

View File

@ -77,7 +77,6 @@ static void snap_source_active_fn(TransInfo *t);
/** \name Implementations
* \{ */
static bool snapNodeTest(View2D *v2d, bNode *node, eSnapTargetOP snap_target_select);
static NodeBorder snapNodeBorder(eSnapMode snap_node_mode);
#if 0
@ -362,11 +361,32 @@ static bool applyFaceProject(TransInfo *t, TransDataContainer *tc, TransData *td
}
SnapObjectParams snap_object_params{};
snap_object_params.snap_target_select = t->tsnap.target_operation;
snap_object_params.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL;
snap_object_params.exclude_active = t->tsnap.exclude_active & ~SCE_SNAP_MODE_FACE;
snap_object_params.exclude_selected = t->tsnap.exclude_selected & ~SCE_SNAP_MODE_FACE;
snap_object_params.exclude_edited = t->tsnap.exclude_edited & ~SCE_SNAP_MODE_FACE;
snap_object_params.exclude_nonedited = t->tsnap.exclude_nonedited & ~SCE_SNAP_MODE_FACE;
snap_object_params.exclude_nonselectable = t->tsnap.exclude_nonselectable & ~SCE_SNAP_MODE_FACE;
snap_object_params.exclude_moving = !(t->modifiers & MOD_EDIT_SNAP_SOURCE);
snap_object_params.use_occlusion_test = false;
snap_object_params.use_backface_culling = (t->tsnap.flag & SCE_SNAP_BACKFACE_CULLING) != 0;
SET_FLAG_FROM_TEST(snap_object_params.exclude_active,
snap_object_params.exclude_active & SCE_SNAP_MODE_FACE_RAYCAST,
SCE_SNAP_MODE_FACE);
SET_FLAG_FROM_TEST(snap_object_params.exclude_selected,
snap_object_params.exclude_selected & SCE_SNAP_MODE_FACE_RAYCAST,
SCE_SNAP_MODE_FACE);
SET_FLAG_FROM_TEST(snap_object_params.exclude_edited,
snap_object_params.exclude_edited & SCE_SNAP_MODE_FACE_RAYCAST,
SCE_SNAP_MODE_FACE);
SET_FLAG_FROM_TEST(snap_object_params.exclude_nonedited,
snap_object_params.exclude_nonedited & SCE_SNAP_MODE_FACE_RAYCAST,
SCE_SNAP_MODE_FACE);
SET_FLAG_FROM_TEST(snap_object_params.exclude_nonselectable,
snap_object_params.exclude_nonselectable & SCE_SNAP_MODE_FACE_RAYCAST,
SCE_SNAP_MODE_FACE);
eSnapMode hit = ED_transform_snap_object_project_view3d(t->tsnap.object_context,
t->depsgraph,
t->region,
@ -425,8 +445,13 @@ static void applyFaceNearest(TransInfo *t, TransDataContainer *tc, TransData *td
}
SnapObjectParams snap_object_params{};
snap_object_params.snap_target_select = t->tsnap.target_operation;
snap_object_params.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL;
snap_object_params.exclude_active = t->tsnap.exclude_active;
snap_object_params.exclude_selected = t->tsnap.exclude_selected;
snap_object_params.exclude_edited = t->tsnap.exclude_edited;
snap_object_params.exclude_nonedited = t->tsnap.exclude_nonedited;
snap_object_params.exclude_nonselectable = t->tsnap.exclude_nonselectable;
snap_object_params.exclude_moving = !(t->modifiers & MOD_EDIT_SNAP_SOURCE);
snap_object_params.use_occlusion_test = false;
snap_object_params.use_backface_culling = false;
snap_object_params.face_nearest_steps = t->tsnap.face_nearest_steps;
@ -545,7 +570,11 @@ void resetSnapping(TransInfo *t)
t->tsnap.status = SNAP_RESETTED;
t->tsnap.snapElem = SCE_SNAP_MODE_NONE;
t->tsnap.mode = SCE_SNAP_MODE_NONE;
t->tsnap.target_operation = SCE_SNAP_TARGET_ALL;
t->tsnap.exclude_active = SCE_SNAP_MODE_NONE;
t->tsnap.exclude_selected = SCE_SNAP_MODE_NONE;
t->tsnap.exclude_edited = SCE_SNAP_MODE_NONE;
t->tsnap.exclude_nonedited = SCE_SNAP_MODE_NONE;
t->tsnap.exclude_nonselectable = SCE_SNAP_MODE_NONE;
t->tsnap.source_operation = SCE_SNAP_SOURCE_CLOSEST;
t->tsnap.last = 0;
@ -671,20 +700,26 @@ static eSnapMode snap_mode_from_spacetype(TransInfo *t)
return SCE_SNAP_MODE_INCREMENT;
}
static eSnapTargetOP snap_target_select_from_spacetype(TransInfo *t)
static void snap_target_select_from_spacetype(TransInfo *t,
eSnapMode *r_exclude_active,
eSnapMode *r_exclude_selected,
eSnapMode *r_exclude_edited,
eSnapMode *r_exclude_nonedited,
eSnapMode *r_exclude_nonselectable)
{
BKE_view_layer_synced_ensure(t->scene, t->view_layer);
Base *base_act = BKE_view_layer_active_base_get(t->view_layer);
eSnapTargetOP ret = SCE_SNAP_TARGET_ALL;
/* `t->tsnap.target_operation` not initialized yet. */
BLI_assert(t->tsnap.target_operation == SCE_SNAP_TARGET_ALL);
*r_exclude_active = SCE_SNAP_MODE_NONE;
*r_exclude_selected = SCE_SNAP_MODE_NONE;
*r_exclude_edited = SCE_SNAP_MODE_NONE;
*r_exclude_nonedited = SCE_SNAP_MODE_NONE;
*r_exclude_nonselectable = SCE_SNAP_MODE_NONE;
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;
return;
}
if (t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR | CTX_OBMODE_XFORM_OBDATA)) {
@ -693,7 +728,7 @@ static eSnapTargetOP snap_target_select_from_spacetype(TransInfo *t)
* TODO: perform self snap in gpencil_strokes.
*
* When we're moving the origins, allow snapping onto our own geometry (see #69132). */
return ret;
return;
}
const int obedit_type = t->obedit_type;
@ -703,28 +738,27 @@ static eSnapTargetOP snap_target_select_from_spacetype(TransInfo *t)
/* Editing a mesh */
if ((t->flag & T_PROP_EDIT) != 0) {
/* Exclude editmesh when using proportional edit */
ret |= SCE_SNAP_TARGET_NOT_EDITED;
*r_exclude_edited = SCE_SNAP_MODE_GEOM_ALL;
}
/* UV editing must never snap to the selection as this is what is transformed. */
if (t->spacetype == SPACE_IMAGE) {
ret |= SCE_SNAP_TARGET_NOT_SELECTED;
*r_exclude_selected = SCE_SNAP_MODE_GEOM_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;
*r_exclude_selected = SCE_SNAP_MODE_GEOM_ALL;
}
}
else {
/* Object or pose mode. */
ret |= SCE_SNAP_TARGET_NOT_SELECTED | SCE_SNAP_TARGET_NOT_ACTIVE;
*r_exclude_active = SCE_SNAP_MODE_GEOM_ALL;
*r_exclude_selected = SCE_SNAP_MODE_GEOM_ALL;
}
}
else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
ret |= SCE_SNAP_TARGET_NOT_SELECTED;
*r_exclude_selected = SCE_SNAP_MODE_GEOM_ALL;
}
return ret;
}
static void snap_object_context_init(TransInfo *t)
@ -793,9 +827,15 @@ void initSnapping(TransInfo *t, wmOperator *op)
resetSnapping(t);
t->tsnap.mode = snap_mode_from_spacetype(t);
t->tsnap.flag = snap_flag_from_spacetype(t);
t->tsnap.target_operation = snap_target_select_from_spacetype(t);
t->tsnap.face_nearest_steps = max_ii(ts->snap_face_nearest_steps, 1);
snap_target_select_from_spacetype(t,
&t->tsnap.exclude_active,
&t->tsnap.exclude_selected,
&t->tsnap.exclude_edited,
&t->tsnap.exclude_nonedited,
&t->tsnap.exclude_nonselectable);
/* if snap property exists */
PropertyRNA *prop;
if (op && (prop = RNA_struct_find_property(op->ptr, "snap")) &&
@ -835,44 +875,37 @@ void initSnapping(TransInfo *t, wmOperator *op)
normalize_v3(t->tsnap.snapNormal);
}
if ((prop = RNA_struct_find_property(op->ptr, "use_snap_project")) &&
RNA_property_is_set(op->ptr, prop))
{
SET_FLAG_FROM_TEST(
t->tsnap.mode, RNA_property_boolean_get(op->ptr, prop), SCE_SNAP_MODE_FACE_RAYCAST);
}
/* 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))
{
SET_FLAG_FROM_TEST(t->tsnap.target_operation,
SET_FLAG_FROM_TEST(t->tsnap.exclude_active,
!RNA_property_boolean_get(op->ptr, prop),
SCE_SNAP_TARGET_NOT_ACTIVE);
SCE_SNAP_MODE_GEOM_ALL);
}
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_operation,
SET_FLAG_FROM_TEST(t->tsnap.exclude_edited,
!RNA_property_boolean_get(op->ptr, prop),
SCE_SNAP_TARGET_NOT_EDITED);
SCE_SNAP_MODE_GEOM_ALL);
}
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_operation,
SET_FLAG_FROM_TEST(t->tsnap.exclude_nonedited,
!RNA_property_boolean_get(op->ptr, prop),
SCE_SNAP_TARGET_NOT_NONEDITED);
SCE_SNAP_MODE_GEOM_ALL);
}
if ((prop = RNA_struct_find_property(op->ptr, "use_snap_selectable")) &&
if ((prop = RNA_struct_find_property(op->ptr, "use_snap_nonselectable")) &&
RNA_property_is_set(op->ptr, prop))
{
SET_FLAG_FROM_TEST(t->tsnap.target_operation,
RNA_property_boolean_get(op->ptr, prop),
SCE_SNAP_TARGET_ONLY_SELECTABLE);
SET_FLAG_FROM_TEST(t->tsnap.exclude_nonselectable,
!RNA_property_boolean_get(op->ptr, prop),
SCE_SNAP_MODE_GEOM_ALL);
}
}
}
@ -881,19 +914,11 @@ void initSnapping(TransInfo *t, wmOperator *op)
if (t->tsnap.flag & SCE_SNAP) {
t->modifiers |= MOD_SNAP;
}
SET_FLAG_FROM_TEST(t->tsnap.target_operation,
(ts->snap_flag & SCE_SNAP_NOT_TO_ACTIVE),
SCE_SNAP_TARGET_NOT_ACTIVE);
SET_FLAG_FROM_TEST(t->tsnap.target_operation,
!(ts->snap_flag & SCE_SNAP_TO_INCLUDE_EDITED),
SCE_SNAP_TARGET_NOT_EDITED);
SET_FLAG_FROM_TEST(t->tsnap.target_operation,
!(ts->snap_flag & SCE_SNAP_TO_INCLUDE_NONEDITED),
SCE_SNAP_TARGET_NOT_NONEDITED);
SET_FLAG_FROM_TEST(t->tsnap.target_operation,
(ts->snap_flag & SCE_SNAP_TO_ONLY_SELECTABLE),
SCE_SNAP_TARGET_ONLY_SELECTABLE);
t->tsnap.exclude_active |= SCE_SNAP_MODE_GEOM_ALL & ~eSnapMode(ts->snap_filter_active);
t->tsnap.exclude_edited |= SCE_SNAP_MODE_GEOM_ALL & ~eSnapMode(ts->snap_filter_edit);
t->tsnap.exclude_nonedited |= SCE_SNAP_MODE_GEOM_ALL & ~eSnapMode(ts->snap_filter_nonedit);
t->tsnap.exclude_nonselectable |= SCE_SNAP_MODE_GEOM_ALL &
~eSnapMode(ts->snap_filter_nonselectable);
}
t->tsnap.source_operation = snap_source;
@ -1138,7 +1163,7 @@ static void snap_target_uv_fn(TransInfo *t, float * /*vec*/)
objects,
objects_len,
t->mval,
t->tsnap.target_operation & SCE_SNAP_TARGET_NOT_SELECTED,
t->tsnap.exclude_selected != SCE_SNAP_MODE_NONE,
&dist_sq,
t->tsnap.snap_target))
{
@ -1385,8 +1410,13 @@ eSnapMode snapObjectsTransform(
TransInfo *t, const float mval[2], float *dist_px, float r_loc[3], float r_no[3])
{
SnapObjectParams snap_object_params{};
snap_object_params.snap_target_select = t->tsnap.target_operation;
snap_object_params.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL;
snap_object_params.exclude_active = t->tsnap.exclude_active;
snap_object_params.exclude_selected = t->tsnap.exclude_selected;
snap_object_params.exclude_edited = t->tsnap.exclude_edited;
snap_object_params.exclude_nonedited = t->tsnap.exclude_nonedited;
snap_object_params.exclude_nonselectable = t->tsnap.exclude_nonselectable;
snap_object_params.exclude_moving = !(t->modifiers & MOD_EDIT_SNAP_SOURCE);
snap_object_params.use_occlusion_test = true;
snap_object_params.use_backface_culling = (t->tsnap.flag & SCE_SNAP_BACKFACE_CULLING) != 0;
@ -1395,7 +1425,7 @@ eSnapMode snapObjectsTransform(
t->depsgraph,
t->region,
static_cast<const View3D *>(t->view),
t->tsnap.mode,
t->tsnap.mode & SCE_SNAP_MODE_GEOM,
&snap_object_params,
nullptr,
mval,
@ -1420,8 +1450,13 @@ bool peelObjectsTransform(TransInfo *t,
float *r_thickness)
{
SnapObjectParams snap_object_params{};
snap_object_params.snap_target_select = t->tsnap.target_operation;
snap_object_params.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL;
snap_object_params.exclude_active = t->tsnap.exclude_active;
snap_object_params.exclude_selected = t->tsnap.exclude_selected;
snap_object_params.exclude_edited = t->tsnap.exclude_edited;
snap_object_params.exclude_nonedited = t->tsnap.exclude_nonedited;
snap_object_params.exclude_nonselectable = t->tsnap.exclude_nonselectable;
snap_object_params.exclude_moving = !(t->modifiers & MOD_EDIT_SNAP_SOURCE);
ListBase depths_peel = {nullptr};
ED_transform_snap_object_project_all_view3d_ex(t->tsnap.object_context,
@ -1498,11 +1533,11 @@ bool peelObjectsTransform(TransInfo *t,
/** \name snap Nodes
* \{ */
static bool snapNodeTest(View2D *v2d, bNode *node, eSnapTargetOP snap_target_select)
static bool snapNodeTest(View2D *v2d, bNode *node, bool exclude_selected)
{
/* 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)) ||
(snap_target_select == SCE_SNAP_TARGET_ALL && !(node->flag & NODE_ACTIVE))) &&
return ((exclude_selected && !(node->flag & NODE_SELECT)) ||
(!exclude_selected && !(node->flag & NODE_ACTIVE))) &&
(node->runtime->totr.xmin < v2d->cur.xmax && node->runtime->totr.xmax > v2d->cur.xmin &&
node->runtime->totr.ymin < v2d->cur.ymax && node->runtime->totr.ymax > v2d->cur.ymin);
}
@ -1583,7 +1618,7 @@ static bool snapNodes(ToolSettings *ts,
SpaceNode *snode,
ARegion *region,
const int mval[2],
eSnapTargetOP snap_target_select,
bool exclude_selected,
float r_loc[2],
float *r_dist_px,
char *r_node_border)
@ -1595,7 +1630,7 @@ static bool snapNodes(ToolSettings *ts,
*r_node_border = 0;
for (node = static_cast<bNode *>(ntree->nodes.first); node; node = node->next) {
if (snapNodeTest(&region->v2d, node, snap_target_select)) {
if (snapNodeTest(&region->v2d, node, exclude_selected)) {
retval |= snapNode(ts, snode, region, node, mval, r_loc, r_dist_px, r_node_border);
}
}
@ -1610,7 +1645,7 @@ bool snapNodesTransform(
static_cast<SpaceNode *>(t->area->spacedata.first),
t->region,
mval,
t->tsnap.target_operation,
t->tsnap.exclude_selected != SCE_SNAP_MODE_NONE,
r_loc,
r_dist_px,
r_node_border);

View File

@ -117,8 +117,10 @@ struct SnapObjectContext {
struct {
Depsgraph *depsgraph;
ViewLayer *view_layer;
const ARegion *region;
const View3D *v3d;
const Base *base_act;
float mval[2];
float pmat[4][4]; /* perspective matrix */
@ -143,6 +145,7 @@ struct SnapObjectContext {
/* List of #SnapObjectHitDepth (caller must free). */
ListBase *hit_list;
/* Snapped object. */
Base *base;
Object *ob;
/* Snapped data. */
ID *data;
@ -410,25 +413,31 @@ using IterSnapObjsCallback = eSnapMode (*)(SnapObjectContext *sctx,
bool use_hide,
void *data);
static bool snap_object_is_snappable(const SnapObjectContext *sctx,
const eSnapTargetOP snap_target_select,
const Base *base_act,
const Base *base)
static eSnapMode snap_object_mode_filter(const SnapObjectContext *sctx,
const SnapObjectParams *params,
const eSnapMode snap_mode,
const Base *base)
{
if (!BASE_VISIBLE(sctx->runtime.v3d, base)) {
return false;
return SCE_SNAP_MODE_NONE;
}
if ((snap_target_select == SCE_SNAP_TARGET_ALL) ||
(base->flag_legacy & BA_TRANSFORM_LOCKED_IN_PLACE))
if (base->flag_legacy & BA_TRANSFORM_LOCKED_IN_PLACE ||
(params->exclude_active == SCE_SNAP_MODE_NONE &&
params->exclude_selected == SCE_SNAP_MODE_NONE &&
params->exclude_edited == SCE_SNAP_MODE_NONE &&
params->exclude_nonedited == SCE_SNAP_MODE_NONE &&
params->exclude_nonselectable == SCE_SNAP_MODE_NONE))
{
return true;
return snap_mode;
}
if (base->flag_legacy & BA_SNAP_FIX_DEPS_FIASCO) {
return false;
if (params->exclude_moving && base->flag_legacy & BA_SNAP_FIX_DEPS_FIASCO) {
return SCE_SNAP_MODE_NONE;
}
const Base *base_act = sctx->runtime.base_act;
/* Get attributes of potential target. */
const bool is_active = (base_act == base);
const bool is_selected = (base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL);
@ -440,30 +449,31 @@ static bool snap_object_is_snappable(const SnapObjectContext *sctx,
if (is_in_object_mode) {
/* Handle target selection options that make sense for object mode. */
if ((snap_target_select & SCE_SNAP_TARGET_NOT_SELECTED) && is_selected) {
if (is_selected) {
/* What is selectable or not is part of the object and depends on the mode. */
return false;
return snap_mode & ~params->exclude_selected;
}
return snap_mode & ~params->exclude_nonedited;
}
else {
/* Handle target selection options that make sense for edit/pose mode. */
if ((snap_target_select & SCE_SNAP_TARGET_NOT_ACTIVE) && is_active) {
return false;
if (is_active) {
return snap_mode & ~params->exclude_active;
}
if ((snap_target_select & SCE_SNAP_TARGET_NOT_EDITED) && is_edited && !is_active) {
if (is_edited && !is_active) {
/* Base is edited, but not active. */
return false;
return snap_mode & ~params->exclude_edited;
}
if ((snap_target_select & SCE_SNAP_TARGET_NOT_NONEDITED) && !is_edited) {
return false;
if (!is_edited) {
return snap_mode & ~params->exclude_nonedited;
}
}
if ((snap_target_select & SCE_SNAP_TARGET_ONLY_SELECTABLE) && !is_selectable) {
return false;
if (!is_selectable) {
return snap_mode & ~params->exclude_nonselectable;
}
return true;
return snap_mode;
}
/**
@ -476,18 +486,22 @@ static eSnapMode iter_snap_objects(SnapObjectContext *sctx,
{
eSnapMode ret = SCE_SNAP_MODE_NONE;
eSnapMode tmp;
eSnapMode snap_to_flag = sctx->runtime.snap_to_flag;
const Base *base_act = sctx->runtime.base_act;
Scene *scene = DEG_get_input_scene(sctx->runtime.depsgraph);
ViewLayer *view_layer = DEG_get_input_view_layer(sctx->runtime.depsgraph);
const eSnapTargetOP snap_target_select = params->snap_target_select;
BKE_view_layer_synced_ensure(scene, view_layer);
Base *base_act = BKE_view_layer_active_base_get(view_layer);
const bool is_occlusion_test = params->use_occlusion_test && snap_to_flag == SCE_SNAP_MODE_NONE;
LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (!snap_object_is_snappable(sctx, snap_target_select, base_act, base)) {
continue;
LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(sctx->runtime.view_layer)) {
eSnapMode snap_mode_filtered = snap_to_flag;
if (!is_occlusion_test) {
snap_mode_filtered = snap_object_mode_filter(sctx, params, snap_to_flag, base);
if (snap_mode_filtered == SCE_SNAP_MODE_NONE) {
continue;
}
}
sctx->runtime.snap_to_flag = snap_mode_filtered;
const bool is_object_active = (base == base_act);
Object *obj_eval = DEG_get_evaluated_object(sctx->runtime.depsgraph, base->object);
if (obj_eval->transflag & OB_DUPLI || BKE_object_has_geometry_set_instances(obj_eval)) {
@ -503,6 +517,7 @@ static eSnapMode iter_snap_objects(SnapObjectContext *sctx,
false,
data)) != SCE_SNAP_MODE_NONE)
{
sctx->ret.base = base;
ret = tmp;
}
}
@ -520,6 +535,7 @@ static eSnapMode iter_snap_objects(SnapObjectContext *sctx,
use_hide,
data)) != SCE_SNAP_MODE_NONE)
{
sctx->ret.base = base;
ret = tmp;
}
}
@ -1767,6 +1783,12 @@ static eSnapMode snap_mesh_polygon(SnapObjectContext *sctx,
float *dist_px)
{
eSnapMode elem = SCE_SNAP_MODE_NONE;
eSnapMode snap_mode_filtered = snap_object_mode_filter(
sctx, params, sctx->runtime.snap_to_flag, sctx->ret.base);
if (snap_mode_filtered == SCE_SNAP_MODE_NONE) {
return elem;
}
float lpmat[4][4];
mul_m4_m4m4(lpmat, sctx->runtime.pmat, sctx->ret.obmat);
@ -2101,7 +2123,8 @@ static eSnapMode snapArmature(SnapObjectContext *sctx,
const bool is_posemode = is_object_active && (ob_eval->mode & OB_MODE_POSE);
const bool skip_selected = (is_editmode || is_posemode) &&
(params->snap_target_select & SCE_SNAP_TARGET_NOT_SELECTED);
(params->exclude_selected &
(SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE));
const bool is_persp = sctx->runtime.view_proj == VIEW_PROJ_PERSP;
if (arm->edbo) {
@ -2280,7 +2303,7 @@ static eSnapMode snapCurve(SnapObjectContext *sctx,
}
bool is_persp = sctx->runtime.view_proj == VIEW_PROJ_PERSP;
bool skip_selected = params->snap_target_select & SCE_SNAP_TARGET_NOT_SELECTED;
bool skip_selected = params->exclude_selected & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE);
LISTBASE_FOREACH (Nurb *, nu, (use_obedit ? &cu->editnurb->nurbs : &cu->nurb)) {
for (int u = 0; u < nu->pntsu; u++) {
@ -3034,6 +3057,32 @@ static eSnapMode snapObjectsRay(SnapObjectContext *sctx,
return iter_snap_objects(sctx, params, snap_obj_fn, &data);
}
static void snap_object_context_runtime_init(SnapObjectContext *sctx,
Depsgraph *depsgraph,
const ARegion *region,
const View3D *v3d,
ListBase *r_hit_list)
{
sctx->runtime.depsgraph = depsgraph;
sctx->runtime.region = region;
sctx->runtime.v3d = v3d;
Scene *scene = DEG_get_input_scene(depsgraph);
sctx->runtime.view_layer = DEG_get_input_view_layer(depsgraph);
BKE_view_layer_synced_ensure(scene, sctx->runtime.view_layer);
sctx->runtime.base_act = BKE_view_layer_active_base_get(sctx->runtime.view_layer);
zero_v3(sctx->ret.loc);
zero_v3(sctx->ret.no);
sctx->ret.index = -1;
zero_m4(sctx->ret.obmat);
sctx->ret.hit_list = r_hit_list;
sctx->ret.ob = nullptr;
sctx->ret.data = nullptr;
sctx->ret.dist_sq = FLT_MAX;
sctx->ret.is_edit = false;
}
/** \} */
/* -------------------------------------------------------------------- */
@ -3099,19 +3148,9 @@ bool ED_transform_snap_object_project_ray_ex(SnapObjectContext *sctx,
Object **r_ob,
float r_obmat[4][4])
{
sctx->runtime.depsgraph = depsgraph;
sctx->runtime.v3d = v3d;
zero_v3(sctx->ret.loc);
zero_v3(sctx->ret.no);
sctx->ret.index = -1;
zero_m4(sctx->ret.obmat);
sctx->ret.hit_list = nullptr;
sctx->ret.ob = nullptr;
sctx->ret.data = nullptr;
sctx->ret.dist_sq = FLT_MAX;
sctx->ret.is_edit = false;
snap_object_context_runtime_init(sctx, depsgraph, nullptr, v3d, nullptr);
sctx->runtime.snap_to_flag = SCE_SNAP_MODE_FACE;
if (raycastObjects(sctx,
params,
ray_start,
@ -3148,18 +3187,7 @@ bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx,
bool sort,
ListBase *r_hit_list)
{
sctx->runtime.depsgraph = depsgraph;
sctx->runtime.v3d = v3d;
zero_v3(sctx->ret.loc);
zero_v3(sctx->ret.no);
sctx->ret.index = -1;
zero_m4(sctx->ret.obmat);
sctx->ret.hit_list = r_hit_list;
sctx->ret.ob = nullptr;
sctx->ret.data = nullptr;
sctx->ret.dist_sq = FLT_MAX;
sctx->ret.is_edit = false;
snap_object_context_runtime_init(sctx, depsgraph, nullptr, v3d, r_hit_list);
if (ray_depth == -1.0f) {
ray_depth = BVH_RAYCAST_DIST_MAX;
@ -3169,6 +3197,7 @@ bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx,
float ray_depth_prev = ray_depth;
#endif
sctx->runtime.snap_to_flag = SCE_SNAP_MODE_FACE;
if (raycastObjects(sctx,
params,
ray_start,
@ -3262,21 +3291,7 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
float r_obmat[4][4],
float r_face_nor[3])
{
sctx->runtime.depsgraph = depsgraph;
sctx->runtime.region = region;
sctx->runtime.v3d = v3d;
zero_v3(sctx->ret.loc);
zero_v3(sctx->ret.no);
sctx->ret.index = -1;
zero_m4(sctx->ret.obmat);
sctx->ret.hit_list = nullptr;
sctx->ret.ob = nullptr;
sctx->ret.data = nullptr;
sctx->ret.dist_sq = FLT_MAX;
sctx->ret.is_edit = false;
BLI_assert(snap_to_flag & (SCE_SNAP_MODE_GEOM | SCE_SNAP_MODE_FACE_NEAREST));
snap_object_context_runtime_init(sctx, depsgraph, region, v3d, nullptr);
eSnapMode retval = SCE_SNAP_MODE_NONE;
@ -3301,9 +3316,12 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
snap_to_flag &= ~SCE_SNAP_MODE_FACE_NEAREST;
}
BLI_assert(snap_to_flag & (SCE_SNAP_MODE_GEOM | SCE_SNAP_MODE_FACE_NEAREST));
/* NOTE: if both face ray-cast and face nearest are enabled, first find result of nearest, then
* override with ray-cast. */
if ((snap_to_flag & SCE_SNAP_MODE_FACE_NEAREST) && !has_hit) {
sctx->runtime.snap_to_flag = (snap_to_flag & SCE_SNAP_MODE_FACE_NEAREST);
has_hit = nearestWorldObjects(sctx, params, init_co, prev_co);
if (has_hit) {
@ -3335,6 +3353,7 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
float dummy_ray_depth = BVH_RAYCAST_DIST_MAX;
sctx->runtime.snap_to_flag = (snap_to_flag & SCE_SNAP_MODE_FACE);
has_hit = raycastObjects(sctx,
params,
ray_start,

View File

@ -336,10 +336,14 @@
.autokey_mode = AUTOKEY_MODE_NORMAL, \
\
.transform_pivot_point = V3D_AROUND_CENTER_MEDIAN, \
.snap_mode = SCE_SNAP_MODE_INCREMENT, \
.snap_mode = SCE_SNAP_MODE_GEOM, \
.snap_filter_active = SCE_SNAP_MODE_GEOM_ALL, \
.snap_filter_edit = SCE_SNAP_MODE_GEOM_ALL, \
.snap_filter_nonedit = SCE_SNAP_MODE_GEOM_ALL, \
.snap_filter_nonselectable = SCE_SNAP_MODE_GEOM_ALL, \
.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_flag = SCE_SNAP_INCLUDE_ACTIVE, \
.snap_transform_mode_flag = SCE_SNAP_TRANSFORM_MODE_TRANSLATE, \
.snap_face_nearest_steps = 1, \
\

View File

@ -1617,6 +1617,10 @@ typedef struct ToolSettings {
/** Snap elements (per space-type), #eSnapMode. */
char _pad1[1];
short snap_mode;
short snap_filter_active;
short snap_filter_edit;
short snap_filter_nonedit;
short snap_filter_nonselectable;
char snap_node_mode;
char snap_uv_mode;
/** Generic flags (per space-type), #eSnapFlag. */
@ -2254,16 +2258,15 @@ typedef enum eSnapFlag {
SCE_SNAP = (1 << 0),
SCE_SNAP_ROTATE = (1 << 1),
SCE_SNAP_PEEL_OBJECT = (1 << 2),
// SCE_SNAP_PROJECT = (1 << 3), /* DEPRECATED, see #SCE_SNAP_MODE_FACE_RAYCAST. */
/** Was `SCE_SNAP_NO_SELF`, but self should be active. */
SCE_SNAP_NOT_TO_ACTIVE = (1 << 4),
SCE_SNAP_KEEP_ON_SAME_OBJECT = (1 << 3),
// SCE_SNAP_NOT_TO_ACTIVE = (1 << 4), /* DEPRECATED */
SCE_SNAP_ABS_GRID = (1 << 5),
SCE_SNAP_BACKFACE_CULLING = (1 << 6),
SCE_SNAP_KEEP_ON_SAME_OBJECT = (1 << 7),
/** see #eSnapTargetOP */
SCE_SNAP_TO_INCLUDE_EDITED = (1 << 8),
SCE_SNAP_TO_INCLUDE_NONEDITED = (1 << 9),
SCE_SNAP_TO_ONLY_SELECTABLE = (1 << 10),
SCE_SNAP_INCLUDE_ACTIVE = (1 << 7),
SCE_SNAP_INCLUDE_EDITED = (1 << 8),
SCE_SNAP_INCLUDE_NONEDITED = (1 << 9),
SCE_SNAP_INCLUDE_NONSELECTABLE = (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. */
@ -2281,21 +2284,6 @@ typedef enum eSnapSourceOP {
ENUM_OPERATORS(eSnapSourceOP, SCE_SNAP_SOURCE_ACTIVE)
/**
* #TransSnap.target_operation 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 eSnapTargetOP {
SCE_SNAP_TARGET_ALL = 0,
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),
} eSnapTargetOP;
ENUM_OPERATORS(eSnapTargetOP, SCE_SNAP_TARGET_NOT_NONEDITED)
/** #ToolSettings.snap_mode */
typedef enum eSnapMode {
SCE_SNAP_MODE_NONE = 0,
@ -2330,6 +2318,10 @@ ENUM_OPERATORS(eSnapMode, SCE_SNAP_MODE_FACE_RAYCAST)
(SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | \
SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT)
#define SCE_SNAP_MODE_GEOM_ALL \
(SCE_SNAP_MODE_GEOM | SCE_SNAP_MODE_VOLUME | SCE_SNAP_MODE_FACE_NEAREST | \
SCE_SNAP_MODE_FACE_RAYCAST)
/** #SequencerToolSettings.snap_mode */
#define SEQ_SNAP_TO_STRIPS (1 << 0)
#define SEQ_SNAP_TO_CURRENT_FRAME (1 << 1)

View File

@ -157,9 +157,9 @@ const EnumPropertyItem rna_enum_mesh_select_mode_uv_items[] = {
{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 by projecting onto faces"}, \
{SCE_SNAP_MODE_VOLUME, "VOLUME", ICON_SNAP_VOLUME, "Volume", "Snap to volume"}, \
{SCE_SNAP_MODE_EDGE_MIDPOINT, "EDGE_MIDPOINT", ICON_SNAP_MIDPOINT, "Edge Center", "Snap to the middle of edges"}, \
{SCE_SNAP_MODE_EDGE_PERPENDICULAR, "EDGE_PERPENDICULAR", ICON_SNAP_PERPENDICULAR, "Edge Perpendicular", "Snap to the nearest point on an edge"}
{SCE_SNAP_MODE_EDGE_PERPENDICULAR, "EDGE_PERPENDICULAR", ICON_SNAP_PERPENDICULAR, "Edge Perpendicular", "Snap to the nearest point on an edge"}, \
{SCE_SNAP_MODE_VOLUME, "VOLUME", ICON_SNAP_VOLUME, "Volume", "Snap to volume"}
/* clang-format on */
const EnumPropertyItem rna_enum_snap_element_items[] = {
@ -186,6 +186,18 @@ const EnumPropertyItem rna_enum_snap_element_base_items[] = {
const EnumPropertyItem *rna_enum_snap_element_individual_items =
&rna_enum_snap_element_items[ARRAY_SIZE(rna_enum_snap_element_items) - 3];
const EnumPropertyItem rna_enum_snap_select_filter_items[] = {
{SCE_SNAP_INCLUDE_ACTIVE, "ACTIVE", ICON_EDITMODE_HLT, "Active", ""},
{SCE_SNAP_INCLUDE_EDITED, "EDITED", ICON_OUTLINER_DATA_MESH, "Edited", ""},
{SCE_SNAP_INCLUDE_NONEDITED, "NONEDITED", ICON_OUTLINER_OB_MESH, "Non-edited", ""},
{SCE_SNAP_INCLUDE_NONSELECTABLE,
"NONSELECTABLE",
ICON_RESTRICT_SELECT_ON,
"Non-selectable",
""},
{0, NULL, 0, NULL, NULL},
};
const EnumPropertyItem rna_enum_snap_node_element_items[] = {
{SCE_SNAP_MODE_GRID, "GRID", ICON_SNAP_GRID, "Grid", "Snap to grid"},
{SCE_SNAP_MODE_NODE_X, "NODE_X", ICON_NODE_SIDE, "Node X", "Snap to left/right node border"},
@ -755,6 +767,12 @@ static void rna_ToolSettings_snap_mode_set(struct PointerRNA *ptr, int value)
}
}
static int rna_ToolSettings_snap_flag_get(PointerRNA *ptr)
{
ToolSettings *ts = (ToolSettings *)(ptr->data);
return ts->snap_flag;
}
/* Grease Pencil update cache */
static void rna_GPencil_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr))
{
@ -3411,6 +3429,7 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_enum_funcs(
prop, "rna_ToolSettings_snap_mode_get", "rna_ToolSettings_snap_mode_set", NULL);
RNA_def_property_flag(prop, PROP_ENUM_FLAG);
RNA_def_property_enum_default(prop, SCE_SNAP_MODE_GEOM);
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 */
@ -3433,6 +3452,48 @@ static void rna_def_tool_settings(BlenderRNA *brna)
prop, "Project Mode", "Type of element for individual transformed elements to snap to");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
prop = RNA_def_property(srna, "snap_filter_active", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "snap_filter_active");
RNA_def_property_enum_items(prop, rna_enum_snap_element_items);
RNA_def_property_flag(prop, PROP_ENUM_FLAG);
RNA_def_property_enum_default(prop, SCE_SNAP_MODE_GEOM_ALL);
RNA_def_property_ui_text(prop, "Snap onto Active", "Snap onto itself if in Edit Mode");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
prop = RNA_def_property(srna, "snap_filter_edit", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "snap_filter_edit");
RNA_def_property_enum_items(prop, rna_enum_snap_element_items);
RNA_def_property_flag(prop, PROP_ENUM_FLAG);
RNA_def_property_enum_default(prop, SCE_SNAP_MODE_GEOM_ALL);
RNA_def_property_ui_text(prop, "Snap onto Edited", "Snap onto non-active objects in Edit Mode");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
prop = RNA_def_property(srna, "snap_filter_nonedit", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "snap_filter_nonedit");
RNA_def_property_enum_items(prop, rna_enum_snap_element_items);
RNA_def_property_flag(prop, PROP_ENUM_FLAG);
RNA_def_property_enum_default(prop, SCE_SNAP_MODE_GEOM_ALL);
RNA_def_property_ui_text(prop, "Snap onto Non-edited", "Snap onto objects not in Edit Mode");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
prop = RNA_def_property(srna, "snap_filter_nonselectable", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "snap_filter_nonselectable");
RNA_def_property_enum_items(prop, rna_enum_snap_element_items);
RNA_def_property_flag(prop, PROP_ENUM_FLAG);
RNA_def_property_enum_default(prop, SCE_SNAP_MODE_GEOM_ALL);
RNA_def_property_ui_text(
prop, "Snap onto Non-selectable", "Snap onto objects that are non-selectable");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
prop = RNA_def_property(srna, "show_snap_filter", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "snap_flag");
RNA_def_property_enum_items(prop, rna_enum_snap_select_filter_items);
RNA_def_property_enum_funcs(prop, "rna_ToolSettings_snap_flag_get", NULL, NULL);
RNA_def_property_flag(prop, PROP_ENUM_FLAG);
RNA_def_property_enum_default(prop, SCE_SNAP_INCLUDE_ACTIVE);
RNA_def_property_ui_text(prop, "Show Snap Filter", "");
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);
@ -3476,6 +3537,7 @@ static void rna_def_tool_settings(BlenderRNA *brna)
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);
RNA_def_property_enum_default(prop, SCE_SNAP_SOURCE_CLOSEST);
RNA_def_property_ui_text(prop, "Snap Target", "Which part to snap onto the target");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
@ -3490,32 +3552,6 @@ 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_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);
RNA_def_property_boolean_sdna(
prop, NULL, "snap_transform_mode_flag", SCE_SNAP_TRANSFORM_MODE_TRANSLATE);

View File

@ -141,9 +141,7 @@ static void rna_Scene_ray_cast(Scene *scene,
bool ret = ED_transform_snap_object_project_ray_ex(sctx,
depsgraph,
NULL,
&(const struct SnapObjectParams){
.snap_target_select = SCE_SNAP_TARGET_ALL,
},
&(const struct SnapObjectParams){0},
origin,
direction,
&ray_dist,

View File

@ -733,9 +733,9 @@ static void wm_xr_raycast(Scene *scene,
sctx,
depsgraph,
NULL,
&(const struct SnapObjectParams){.snap_target_select = (selectable_only ?
SCE_SNAP_TARGET_ONLY_SELECTABLE :
SCE_SNAP_TARGET_ALL)},
&(const struct SnapObjectParams){.exclude_nonselectable = (selectable_only ?
SCE_SNAP_MODE_GEOM_ALL :
SCE_SNAP_MODE_NONE)},
origin,
direction,
ray_dist,