diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index e950f9bc679..8e8ca667e5f 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -235,6 +235,8 @@ typedef enum { GHOST_kStandardCursorCrosshairA, GHOST_kStandardCursorCrosshairB, GHOST_kStandardCursorCrosshairC, + GHOST_kStandardCursorCrosshairD, + GHOST_kStandardCursorCrosshairE, GHOST_kStandardCursorPencil, GHOST_kStandardCursorUpArrow, GHOST_kStandardCursorDownArrow, diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index 3e82f55c583..183af447f9b 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -772,6 +772,12 @@ HCURSOR GHOST_WindowWin32::getStandardCursor(GHOST_TStandardCursor shape) const case GHOST_kStandardCursorCrosshairC: cursor = ::LoadImage(module, "crossC_cursor", IMAGE_CURSOR, cx, cy, flags); break; /* Minimal Crosshair C */ + case GHOST_kStandardCursorCrosshairD: + cursor = ::LoadImage(module, "crossD_cursor", IMAGE_CURSOR, cx, cy, flags); + break; /* Alternate Crosshair D */ + case GHOST_kStandardCursorCrosshairE: + cursor = ::LoadImage(module, "crossE_cursor", IMAGE_CURSOR, cx, cy, flags); + break; /* Large Crosshair E */ case GHOST_kStandardCursorBottomSide: case GHOST_kStandardCursorUpDown: cursor = ::LoadImage(module, "movens_cursor", IMAGE_CURSOR, cx, cy, flags); diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c index cbcd99d4b3f..794104af6f6 100644 --- a/release/datafiles/userdef/userdef_default.c +++ b/release/datafiles/userdef/userdef_default.c @@ -185,6 +185,7 @@ const UserDef U_default = { .render_display_type = USER_RENDER_DISPLAY_WINDOW, .filebrowser_display_type = USER_TEMP_SPACE_DISPLAY_WINDOW, .viewport_aa = 8, + .selection_radius = 75.0f, .walk_navigation = { diff --git a/release/windows/icons/winblender.rc b/release/windows/icons/winblender.rc index a0dff81b711..fcd2a66b0cd 100644 --- a/release/windows/icons/winblender.rc +++ b/release/windows/icons/winblender.rc @@ -23,6 +23,8 @@ cross_cursor CURSOR "cursors/cross.cur" crossA_cursor CURSOR "cursors/crossa.cur" crossB_cursor CURSOR "cursors/crossb.cur" crossC_cursor CURSOR "cursors/crossc.cur" +crossD_cursor CURSOR "cursors/crossd.cur" +crossE_cursor CURSOR "cursors/crosse.cur" eraser_cursor CURSOR "cursors/eraser.cur" eyedropper_cursor CURSOR "cursors/eyedropper.cur" handopen_cursor CURSOR "cursors/handopen.cur" diff --git a/scripts/modules/rna_keymap_ui.py b/scripts/modules/rna_keymap_ui.py index 8daacc2e12e..2aa004bbb7d 100644 --- a/scripts/modules/rna_keymap_ui.py +++ b/scripts/modules/rna_keymap_ui.py @@ -182,7 +182,24 @@ def draw_kmi(display_keymaps, kc, km, kmi, layout, level): if map_type in {'KEYBOARD', 'MOUSE'} and kmi.value == 'CLICK_DRAG': subrow = sub.row() - subrow.prop(kmi, "direction") + if bpy.context.preferences.inputs.click_drag_direction == 'LEFT_RIGHT': + if kmi.direction == 'ANY' or kmi.direction == 'EAST' or kmi.direction == 'WEST': + subrow.label(text="Direction:") + else: + subrow.label(text="WARNING - No Direction:") + subrow.operator("preferences.set_direction_any", text="Any", depress= kmi.direction == 'ANY').item_id = kmi.id + subrow.operator("preferences.set_direction_left", text="Left", depress= kmi.direction == 'WEST').item_id = kmi.id + subrow.operator("preferences.set_direction_right", text="Right", depress= kmi.direction == 'EAST').item_id = kmi.id + elif bpy.context.preferences.inputs.click_drag_direction == 'UP_DOWN': + if kmi.direction == 'ANY' or kmi.direction == 'NORTH' or kmi.direction == 'SOUTH': + subrow.label(text="Direction:") + else: + subrow.label(text="WARNING - No Direction:") + subrow.operator("preferences.set_direction_any", text="Any", depress= kmi.direction == 'ANY').item_id = kmi.id + subrow.operator("preferences.set_direction_up", text="Up", depress= kmi.direction == 'NORTH').item_id = kmi.id + subrow.operator("preferences.set_direction_down", text="Down", depress= kmi.direction == 'SOUTH').item_id = kmi.id + else: + subrow.prop(kmi, "direction") subrow = sub.row() subrow.scale_x = 0.75 diff --git a/scripts/startup/bl_operators/userpref.py b/scripts/startup/bl_operators/userpref.py index d0134bd076f..f2d9d5cb428 100644 --- a/scripts/startup/bl_operators/userpref.py +++ b/scripts/startup/bl_operators/userpref.py @@ -400,6 +400,111 @@ class PREFERENCES_OT_keyitem_remove(Operator): return {'FINISHED'} +class PREFERENCES_OT_set_direction_any(Operator): + """Drag in any direction""" + bl_idname = "preferences.set_direction_any" + bl_label = "Set Direction Any" + + item_id: IntProperty( + name="Item Identifier", + description="Identifier of the item to remove", + ) + + @classmethod + def poll(cls, context): + return hasattr(context, "keymap") + + def execute(self, context): + km = context.keymap + kmi = km.keymap_items.from_id(self.item_id) + km.keymap_items.set_direction_any(kmi) + return {'FINISHED'} + + +class PREFERENCES_OT_set_direction_left(Operator): + """Drag to the left""" + bl_idname = "preferences.set_direction_left" + bl_label = "Set Direction Left" + + item_id: IntProperty( + name="Item Identifier", + description="Identifier of the item to remove", + ) + + @classmethod + def poll(cls, context): + return hasattr(context, "keymap") + + def execute(self, context): + km = context.keymap + kmi = km.keymap_items.from_id(self.item_id) + km.keymap_items.set_direction_left(kmi) + return {'FINISHED'} + + +class PREFERENCES_OT_set_direction_right(Operator): + """Drag to the right""" + bl_idname = "preferences.set_direction_right" + bl_label = "Set Direction Right" + + item_id: IntProperty( + name="Item Identifier", + description="Identifier of the item to remove", + ) + + @classmethod + def poll(cls, context): + return hasattr(context, "keymap") + + def execute(self, context): + km = context.keymap + kmi = km.keymap_items.from_id(self.item_id) + km.keymap_items.set_direction_right(kmi) + return {'FINISHED'} + + +class PREFERENCES_OT_set_direction_up(Operator): + """Drag upwards""" + bl_idname = "preferences.set_direction_up" + bl_label = "Set Direction Up" + + item_id: IntProperty( + name="Item Identifier", + description="Identifier of the item to remove", + ) + + @classmethod + def poll(cls, context): + return hasattr(context, "keymap") + + def execute(self, context): + km = context.keymap + kmi = km.keymap_items.from_id(self.item_id) + km.keymap_items.set_direction_up(kmi) + return {'FINISHED'} + + +class PREFERENCES_OT_set_direction_down(Operator): + """Drag downwards""" + bl_idname = "preferences.set_direction_down" + bl_label = "Set Direction Down" + + item_id: IntProperty( + name="Item Identifier", + description="Identifier of the item to remove", + ) + + @classmethod + def poll(cls, context): + return hasattr(context, "keymap") + + def execute(self, context): + km = context.keymap + kmi = km.keymap_items.from_id(self.item_id) + km.keymap_items.set_direction_down(kmi) + return {'FINISHED'} + + class PREFERENCES_OT_keyconfig_remove(Operator): """Remove key config""" bl_idname = "preferences.keyconfig_remove" @@ -1156,6 +1261,11 @@ classes = ( PREFERENCES_OT_keyconfig_test, PREFERENCES_OT_keyitem_add, PREFERENCES_OT_keyitem_remove, + PREFERENCES_OT_set_direction_any, + PREFERENCES_OT_set_direction_left, + PREFERENCES_OT_set_direction_right, + PREFERENCES_OT_set_direction_up, + PREFERENCES_OT_set_direction_down, PREFERENCES_OT_keyitem_restore, PREFERENCES_OT_keymap_restore, PREFERENCES_OT_theme_install, diff --git a/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/scripts/startup/bl_ui/space_toolsystem_toolbar.py index aa448aeca56..e735b9f797b 100644 --- a/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -386,10 +386,37 @@ class _defs_view3d_select: @ToolDef.from_fn def box(): def draw_settings(_context, layout, tool): + tool_settings = _context.tool_settings props = tool.operator_properties("view3d.select_box") row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) + + if _context.mode == 'OBJECT' and bpy.context.preferences.inputs.drag_select_control == 'USER_DRAG_TOOLSETTING': + layout.prop(tool_settings, "select_origin_box") + if _context.mode == 'EDIT_MESH' and bpy.context.preferences.inputs.drag_select_control == 'USER_DRAG_TOOLSETTING': + layout.prop(tool_settings, "show_box_options", toggle=True) + if tool_settings.show_box_options: + row = layout.row(align=True) + row.prop(tool_settings, "box_drag_direction") + row = layout.row(align=True) + if tool_settings.box_drag_direction == 'MESH_DIRECTION_ANY': + row.prop(tool_settings, "box_edge") + row = layout.row(align=True) + row.prop(tool_settings, "box_face") + elif tool_settings.box_drag_direction == 'MESH_DIRECTION_LEFT_RIGHT': + row.prop(tool_settings, "box_edge_left", icon='TRIA_LEFT') + row.prop(tool_settings, "box_edge_right", text="", icon='TRIA_RIGHT') + row = layout.row(align=True) + row.prop(tool_settings, "box_face_left", icon='TRIA_LEFT') + row.prop(tool_settings, "box_face_right", text="", icon='TRIA_RIGHT') + else: + row.prop(tool_settings, "box_edge_up", icon='TRIA_UP') + row.prop(tool_settings, "box_edge_down", text="", icon='TRIA_DOWN') + row = layout.row(align=True) + row.prop(tool_settings, "box_face_up", icon='TRIA_UP') + row.prop(tool_settings, "box_face_down", text="", icon='TRIA_DOWN') + return dict( idname="builtin.select_box", label="Select Box", @@ -402,10 +429,33 @@ class _defs_view3d_select: @ToolDef.from_fn def lasso(): def draw_settings(_context, layout, tool): + tool_settings = _context.tool_settings props = tool.operator_properties("view3d.select_lasso") row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) + if _context.mode == 'EDIT_MESH' and bpy.context.preferences.inputs.drag_select_control == 'USER_DRAG_TOOLSETTING': + layout.prop(tool_settings, "show_lasso_options", toggle=True) + if tool_settings.show_lasso_options: + row = layout.row(align=True) + row.prop(tool_settings, "lasso_drag_direction") + row = layout.row(align=True) + if tool_settings.lasso_drag_direction == 'MESH_DIRECTION_ANY': + row.prop(tool_settings, "lasso_edge") + row = layout.row(align=True) + row.prop(tool_settings, "lasso_face") + elif tool_settings.lasso_drag_direction == 'MESH_DIRECTION_LEFT_RIGHT': + row.prop(tool_settings, "lasso_edge_left", icon='TRIA_LEFT') + row.prop(tool_settings, "lasso_edge_right", text="", icon='TRIA_RIGHT') + row = layout.row(align=True) + row.prop(tool_settings, "lasso_face_left", icon='TRIA_LEFT') + row.prop(tool_settings, "lasso_face_right", text="", icon='TRIA_RIGHT') + else: + row.prop(tool_settings, "lasso_edge_up", icon='TRIA_UP') + row.prop(tool_settings, "lasso_edge_down", text="", icon='TRIA_DOWN') + row = layout.row(align=True) + row.prop(tool_settings, "lasso_face_up", icon='TRIA_UP') + row.prop(tool_settings, "lasso_face_down", text="", icon='TRIA_DOWN') return dict( idname="builtin.select_lasso", label="Select Lasso", @@ -418,12 +468,23 @@ class _defs_view3d_select: @ToolDef.from_fn def circle(): def draw_settings(_context, layout, tool): + tool_settings = _context.tool_settings props = tool.operator_properties("view3d.select_circle") row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) layout.prop(props, "radius") + if _context.mode == 'OBJECT' and bpy.context.preferences.inputs.drag_select_control == 'USER_DRAG_TOOLSETTING': + layout.prop(tool_settings, "select_origin_circle") + if _context.mode == 'EDIT_MESH' and bpy.context.preferences.inputs.drag_select_control == 'USER_DRAG_TOOLSETTING': + layout.prop(tool_settings, "show_circle_options", toggle=True) + if tool_settings.show_circle_options: + row = layout.row(align=True) + row.prop(tool_settings, "circle_edge") + row = layout.row(align=True) + row.prop(tool_settings, "circle_face") + def draw_cursor(_context, tool, xy): from gpu_extras.presets import draw_circle_2d props = tool.operator_properties("view3d.select_circle") diff --git a/scripts/startup/bl_ui/space_userpref.py b/scripts/startup/bl_ui/space_userpref.py index f0dedc42c54..91d7410819f 100644 --- a/scripts/startup/bl_ui/space_userpref.py +++ b/scripts/startup/bl_ui/space_userpref.py @@ -496,6 +496,17 @@ class USERPREF_PT_edit_misc(EditingPanel, CenterAlignMixIn, Panel): col = layout.column() col.prop(edit, "sculpt_paint_overlay_color", text="Sculpt Overlay Color") col.prop(edit, "node_margin", text="Node Auto-Offset Margin") + col.prop(edit, "adjustable_click_select") + colsub = col.column(align=True) + colsub.active = edit.adjustable_click_select + colsub.prop(edit, "select_unbiased") + colsub.prop(edit, "selection_radius") + col = layout.column() + col.prop(edit, "alternate_cursor") + colsub = col.column(align=True) + colsub.active = edit.alternate_cursor + colsub.prop(edit, "alternate_cursor_large") + # ----------------------------------------------------------------------------- @@ -1559,6 +1570,8 @@ class USERPREF_PT_input_mouse(InputPanel, CenterAlignMixIn, Panel): flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + flow.prop(inputs, "click_drag_direction") + flow.prop(inputs, "drag_select_control") flow.prop(inputs, "use_mouse_emulate_3_button") if sys.platform[:3] != "win": rowsub = flow.row() diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index fe1c4ee2984..0f935ce00fc 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -889,9 +889,47 @@ class VIEW3D_HT_header(Header): sub.active = overlay.show_overlays sub.popover(panel="VIEW3D_PT_overlay", text="") - row = layout.row() + row = layout.row(align=True) row.active = (object_mode == 'EDIT') or (shading.type in {'WIREFRAME', 'SOLID'}) + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + _cls = ToolSelectPanelHelper._tool_class_from_space_type('VIEW_3D') + + if tool_settings.workspace_tool_type == 'FALLBACK': + tool = _cls._tool_get_by_id_active(context, _cls.tool_fallback_id)[0].idname + else: + tool = ToolSelectPanelHelper.tool_active_from_context(context).idname + + if object_mode in 'EDIT': + mode_match_auto_xray = tool_settings.auto_xray_edit and tool_settings.auto_xray + mode_match_select_through = tool_settings.select_through_edit and tool_settings.select_through + elif object_mode in 'OBJECT': + mode_match_auto_xray = tool_settings.auto_xray_object and tool_settings.auto_xray + mode_match_select_through = tool_settings.select_through_object and tool_settings.select_through + else: + mode_match_auto_xray = False + mode_match_select_through = False + + if tool == "builtin.select_box": + depress_auto_xray = mode_match_auto_xray and tool_settings.auto_xray_box + depress_select_through = mode_match_select_through and tool_settings.select_through_box + elif tool == "builtin.select_lasso": + depress_auto_xray = mode_match_auto_xray and tool_settings.auto_xray_lasso + depress_select_through = mode_match_select_through and tool_settings.select_through_lasso + elif tool == "builtin.select_circle": + depress_auto_xray = mode_match_auto_xray and tool_settings.auto_xray_circle + depress_select_through = mode_match_select_through and tool_settings.select_through_circle + else: + depress_auto_xray = False + depress_select_through = False + + if object_mode in 'EDIT' or object_mode in 'OBJECT': + if bpy.context.preferences.inputs.drag_select_control == 'USER_DRAG_TOOLSETTING': + if tool_settings.auto_xray_button: + row.operator("view3d.toggle_auto_xray", text="", icon='XRAY', depress=depress_auto_xray) + if tool_settings.select_through_button: + row.operator("view3d.toggle_select_through", text="", icon='XRAY', depress=depress_select_through) + # While exposing `shading.show_xray(_wireframe)` is correct. # this hides the key shortcut from users: #70433. if has_pose_mode: @@ -900,15 +938,29 @@ class VIEW3D_HT_header(Header): draw_depressed = shading.show_xray_wireframe else: draw_depressed = shading.show_xray - row.operator( - "view3d.toggle_xray", - text="", - icon='XRAY', - depress=draw_depressed, - ) + + if bpy.context.preferences.inputs.drag_select_control == 'USER_DRAG_TOOLSETTING': + if tool_settings.xray_button: + row.operator("view3d.toggle_xray", text="", icon='XRAY', depress=draw_depressed) + elif not tool_settings.auto_xray_button and not tool_settings.select_through_button: + row.operator("view3d.toggle_xray", text="", icon='XRAY', depress=draw_depressed) + else: + row.operator("view3d.toggle_xray", text="", icon='XRAY', depress=draw_depressed) + row.popover(panel="VIEW3D_PT_xray", text="") row = layout.row(align=True) - row.prop(shading, "type", text="", expand=True) + if tool_settings.shrink_shading_header: + if shading.type == 'SOLID': + shadicon = 'SHADING_SOLID' + elif shading.type == 'MATERIAL': + shadicon = 'SHADING_TEXTURE' + elif shading.type == 'RENDERED': + shadicon = 'SHADING_RENDERED' + else: + shadicon = 'SHADING_WIRE' + row.operator("view3d.toggle_xray", text="", icon=shadicon, depress=draw_depressed) + else: + row.prop(shading, "type", text="", expand=True) sub = row.row(align=True) # TODO, currently render shading type ignores mesh two-side, until it's supported # show the shading popover which shows double-sided option. @@ -1025,6 +1077,99 @@ class ShowHideMenu: layout.operator("%s.hide" % self._operator_name, text="Hide Unselected").unselected = True +# Custom Operators +class VIEW3D_gizmo_tweak(bpy.types.Operator): + """tweak based on gizmo shown""" + bl_idname = "view3d.gizmo_tweak" + bl_label = "gizmo tweak" + + from bpy.props import StringProperty, BoolProperty + + tmode: StringProperty(name="Transform Mode") + release: BoolProperty(name="Confirm on Release") + + def modal(self, context, event): + if event.type == 'MOUSEMOVE': + bpy.ops.transform.transform( + 'INVOKE_DEFAULT', + mode=self.tmode, + release_confirm=self.release) + return {'FINISHED'} + + return {'RUNNING_MODAL'} + + def invoke(self, context, event): + if context.object: + if context.space_data.show_gizmo_object_translate==True: + self.tmode = 'TRANSLATION' + elif context.space_data.show_gizmo_object_rotate==True: + self.tmode = 'ROTATION' + elif context.space_data.show_gizmo_object_scale==True: + self.tmode = 'RESIZE' + else: self.tmode = 'TRANSLATION' + + context.window_manager.modal_handler_add(self) + return {'RUNNING_MODAL'} + else: + self.report({'WARNING'}, "No active object, could not finish") + return {'CANCELLED'} + +class VIEW3D_gizmo_move(bpy.types.Operator): + bl_idname = "view3d.gizmo_move" + bl_label = "gizmo move" + + def invoke(self, context, event): + areas = bpy.context.workspace.screens[0].areas + for area in areas: + for space in area.spaces: + if space.type == 'VIEW_3D': + space.show_gizmo_object_translate^= True + space.show_gizmo_object_rotate = False + space.show_gizmo_object_scale = False + return {'FINISHED'} + +class VIEW3D_gizmo_scale(bpy.types.Operator): + bl_idname = "view3d.gizmo_scale" + bl_label = "gizmo scale" + + def invoke(self, context, event): + areas = bpy.context.workspace.screens[0].areas + for area in areas: + for space in area.spaces: + if space.type == 'VIEW_3D': + space.show_gizmo_object_translate = False + space.show_gizmo_object_rotate = False + space.show_gizmo_object_scale ^= True + return {'FINISHED'} + +class VIEW3D_gizmo_rotate(bpy.types.Operator): + bl_idname = "view3d.gizmo_rotate" + bl_label = "gizmo rotate" + + def invoke(self, context, event): + areas = bpy.context.workspace.screens[0].areas + for area in areas: + for space in area.spaces: + if space.type == 'VIEW_3D': + space.show_gizmo_object_translate = False + space.show_gizmo_object_rotate ^= True + space.show_gizmo_object_scale = False + return {'FINISHED'} + +class VIEW3D_box_lasso(bpy.types.Operator): + bl_idname = "view3d.box_lasso" + bl_label = "box lasso" + + def invoke(self, context, event): + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + tool = ToolSelectPanelHelper.tool_active_from_context(context) + if tool.idname == "builtin.select_box": + bpy.ops.wm.tool_set_by_id(name="builtin.select_lasso") + else: + bpy.ops.wm.tool_set_by_id(name="builtin.select_box") + return {'FINISHED'} + + # Standard transforms which apply to all cases (mix-in class, not used directly). class VIEW3D_MT_transform_base: bl_label = "Transform" @@ -5888,6 +6033,12 @@ class VIEW3D_PT_shading(Panel): def draw(self, _context): layout = self.layout layout.label(text="Viewport Shading") + tool_settings = _context.tool_settings + shading = VIEW3D_PT_shading.get_shading(_context) + + layout.prop(tool_settings, "shrink_shading_header") + if tool_settings.shrink_shading_header: + layout.prop(shading, "type", text="", expand=True) class VIEW3D_PT_shading_lighting(Panel): @@ -6068,17 +6219,7 @@ class VIEW3D_PT_shading_options(Panel): row = col.row(align=True) - if shading.type == 'WIREFRAME': - row.prop(shading, "show_xray_wireframe", text="") - sub = row.row() - sub.active = shading.show_xray_wireframe - sub.prop(shading, "xray_alpha_wireframe", text="X-Ray") - elif shading.type == 'SOLID': - row.prop(shading, "show_xray", text="") - sub = row.row() - sub.active = shading.show_xray - sub.prop(shading, "xray_alpha", text="X-Ray") - # X-ray mode is off when alpha is 1.0 + if shading.type == 'SOLID': xray_active = shading.show_xray and shading.xray_alpha != 1 row = col.row(align=True) @@ -6263,6 +6404,83 @@ class VIEW3D_PT_gizmo_display(Panel): col.prop(view, "show_gizmo_camera_dof_distance", text="Focus Distance") +class VIEW3D_PT_xray(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'HEADER' + bl_label = "X-Ray Settings" + bl_ui_units_x = 14 + + def draw(self, context): + layout = self.layout + layout.label(text="X-Ray Settings") + shading = VIEW3D_PT_shading.get_shading(context) + + col = layout.column() + row = col.row(align=True) + if shading.type == 'WIREFRAME': + row.prop(shading, "show_xray_wireframe", text="") + sub = row.row() + sub.active = shading.show_xray_wireframe + sub.prop(shading, "xray_alpha_wireframe", text="X-Ray Wireframe") + elif shading.type == 'SOLID': + row.prop(shading, "show_xray", text="") + sub = row.row() + sub.active = shading.show_xray + sub.prop(shading, "xray_alpha", text="X-Ray Solid") + + if bpy.context.preferences.inputs.drag_select_control == 'USER_DRAG_TOOLSETTING': + tool_settings = context.tool_settings + + row = layout.row(align=True) + row = layout.row(align=True) + row = layout.row(align=True) + + row = layout.row(heading="Automatic X-Ray") + row.prop(tool_settings, "ui_prop", text="", emboss=False) + row = layout.row(align=True) + row.prop(tool_settings, "auto_xray", text="Enable") + sub = row.row(align=True) + sub.active = tool_settings.auto_xray + sub.prop(tool_settings, "auto_xray_object", text="Object") + sub.prop(tool_settings, "auto_xray_edit", text="Edit") + row = layout.row(align=True) + sub = row.row(align=True) + sub.active = tool_settings.auto_xray + sub.prop(tool_settings, "auto_xray_box", text="Box", toggle=True) + sub.prop(tool_settings, "auto_xray_lasso", text="Lasso", toggle=True) + sub.prop(tool_settings, "auto_xray_circle", text="Circle", toggle=True) + + row = layout.row(align=True) + row = layout.row(align=True) + row = layout.row(align=True) + + row = layout.row(heading="Select Through") + row.prop(tool_settings, "ui_prop", text="", emboss=False) + row = layout.row(align=True) + row.prop(tool_settings, "select_through", text="Enable") + sub = row.row(align=True) + sub.active = tool_settings.select_through + sub.prop(tool_settings, "select_through_object", text="Object") + sub.prop(tool_settings, "select_through_edit", text="Edit") + row = layout.row(align=True) + sub = row.row(align=True) + sub.active = tool_settings.select_through + sub.prop(tool_settings, "select_through_box", text="Box", toggle=True) + sub.prop(tool_settings, "select_through_lasso", text="Lasso", toggle=True) + sub.prop(tool_settings, "select_through_circle", text="Circle", toggle=True) + + row = layout.row(align=True) + row = layout.row(align=True) + row = layout.row(align=True) + + row = layout.row(heading="Header Buttons") + row.prop(tool_settings, "ui_prop", text="", emboss=False) + row = layout.row(align=True) + row.prop(tool_settings, "auto_xray_button", text="Auto X-Ray", toggle=True) + row.prop(tool_settings, "select_through_button", text="Select Through", toggle=True) + row.prop(tool_settings, "xray_button", text="X-Ray", toggle=True) + + class VIEW3D_PT_overlay(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'HEADER' @@ -8185,6 +8403,11 @@ classes = ( VIEW3D_MT_sculpt_gpencil_automasking_pie, VIEW3D_MT_wpaint_vgroup_lock_pie, VIEW3D_MT_sculpt_face_sets_edit_pie, + VIEW3D_gizmo_tweak, + VIEW3D_gizmo_move, + VIEW3D_gizmo_scale, + VIEW3D_gizmo_rotate, + VIEW3D_box_lasso, VIEW3D_MT_sculpt_curves, VIEW3D_PT_active_tool, VIEW3D_PT_active_tool_duplicate, @@ -8209,6 +8432,7 @@ classes = ( VIEW3D_PT_shading_render_pass, VIEW3D_PT_shading_compositor, VIEW3D_PT_gizmo_display, + VIEW3D_PT_xray, VIEW3D_PT_overlay, VIEW3D_PT_overlay_guides, VIEW3D_PT_overlay_object, diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index c6d6a77eec2..29631c411e7 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later +import bpy from bpy.types import Menu, Panel, UIList, WindowManager from bl_ui.properties_grease_pencil_common import ( GreasePencilSculptAdvancedPanel, @@ -153,7 +154,7 @@ class VIEW3D_PT_tools_meshedit_options(View3DPanel, Panel): bl_context = ".mesh_edit" # dot on purpose (access from topbar) bl_label = "Options" bl_options = {'DEFAULT_CLOSED'} - bl_ui_units_x = 12 + bl_ui_units_x = 15 @classmethod def poll(cls, context): @@ -169,6 +170,7 @@ class VIEW3D_PT_tools_meshedit_options(View3DPanel, Panel): ob = context.active_object mesh = ob.data + row = layout.row(align=True) row = layout.row(align=True, heading="Transform") row.prop(tool_settings, "use_transform_correct_face_attributes") @@ -220,6 +222,39 @@ class VIEW3D_PT_tools_meshedit_options_automerge(View3DPanel, Panel): col.prop(tool_settings, "double_threshold", text="Threshold") +class VIEW3D_PT_tools_meshedit_options_viewport_facing_select(View3DPanel, Panel): + bl_category = "Tool" + bl_context = ".mesh_edit" # dot on purpose (access from topbar) + bl_label = "Viewport Facing Select" + bl_parent_id = "VIEW3D_PT_tools_meshedit_options" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + return context.active_object + + def draw_header(self, context): + tool_settings = context.tool_settings + + self.layout.prop(tool_settings, "viewport_facing_select", text="", toggle=False) + + def draw(self, context): + layout = self.layout + + tool_settings = context.tool_settings + + layout.use_property_split = True + layout.use_property_decorate = False + + col = layout.column() + col.active = tool_settings.viewport_facing_select + col.prop(tool_settings, "viewport_facing_select_mode") + col.prop(tool_settings, "viewport_facing_select_threshold", text="Threshold") + col.prop(tool_settings, "viewport_facing_select_vert") + col.prop(tool_settings, "viewport_facing_select_edge") + col.prop(tool_settings, "viewport_facing_select_face") + + # ********** default tools for editmode_armature **************** @@ -2364,6 +2399,7 @@ classes = ( VIEW3D_PT_tools_object_options_transform, VIEW3D_PT_tools_meshedit_options, VIEW3D_PT_tools_meshedit_options_automerge, + VIEW3D_PT_tools_meshedit_options_viewport_facing_select, VIEW3D_PT_tools_armatureedit_options, VIEW3D_PT_tools_posemode_options, diff --git a/source/blender/blenlib/BLI_lasso_2d.h b/source/blender/blenlib/BLI_lasso_2d.h index 69625a9da83..1e2bbb1e1b2 100644 --- a/source/blender/blenlib/BLI_lasso_2d.h +++ b/source/blender/blenlib/BLI_lasso_2d.h @@ -25,7 +25,8 @@ bool BLI_lasso_is_edge_inside(const int mcoords[][2], int y0, int x1, int y1, - int error_value); + int error_value, + bool fully_inside); #ifdef __cplusplus } diff --git a/source/blender/blenlib/intern/lasso_2d.c b/source/blender/blenlib/intern/lasso_2d.c index 4752864ea3a..04978febdae 100644 --- a/source/blender/blenlib/intern/lasso_2d.c +++ b/source/blender/blenlib/intern/lasso_2d.c @@ -55,7 +55,8 @@ bool BLI_lasso_is_edge_inside(const int mcoords[][2], int y0, int x1, int y1, - const int error_value) + const int error_value, + bool fully_inside) { if (x0 == error_value || x1 == error_value || mcoords_len == 0) { @@ -64,23 +65,31 @@ bool BLI_lasso_is_edge_inside(const int mcoords[][2], const int v1[2] = {x0, y0}, v2[2] = {x1, y1}; - /* check points in lasso */ - if (BLI_lasso_is_point_inside(mcoords, mcoords_len, v1[0], v1[1], error_value)) { - return true; - } - if (BLI_lasso_is_point_inside(mcoords, mcoords_len, v2[0], v2[1], error_value)) { + /* enclosed faces only checks for both points inside selection area */ + if (BLI_lasso_is_point_inside(mcoords, mcoords_len, v1[0], v1[1], error_value) && + BLI_lasso_is_point_inside(mcoords, mcoords_len, v2[0], v2[1], error_value)) { return true; } - /* no points in lasso, so we have to intersect with lasso edge */ - - if (isect_seg_seg_v2_int(mcoords[0], mcoords[mcoords_len - 1], v1, v2) > 0) { - return true; - } - for (uint a = 0; a < mcoords_len - 1; a++) { - if (isect_seg_seg_v2_int(mcoords[a], mcoords[a + 1], v1, v2) > 0) { + if (!fully_inside) { + /* check points in lasso */ + if (BLI_lasso_is_point_inside(mcoords, mcoords_len, v1[0], v1[1], error_value)) { return true; } + if (BLI_lasso_is_point_inside(mcoords, mcoords_len, v2[0], v2[1], error_value)) { + return true; + } + + /* no points in lasso, so we have to intersect with lasso edge */ + + if (isect_seg_seg_v2_int(mcoords[0], mcoords[mcoords_len - 1], v1, v2) > 0) { + return true; + } + for (unsigned int a = 0; a < mcoords_len - 1; a++) { + if (isect_seg_seg_v2_int(mcoords[a], mcoords[a + 1], v1, v2) > 0) { + return true; + } + } } return false; diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index d19616223cd..b324a516f10 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -373,6 +373,54 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene) if (idprop) { IDP_ClearProperty(idprop); } + + /* Mesh select. */ + ts->box_drag_direction = 1; + ts->lasso_drag_direction = 1; + ts->box_edge = 1; + ts->box_edge_left = 1; + ts->box_edge_right = 1; + ts->box_edge_up = 1; + ts->box_edge_down = 1; + ts->box_face = 1; + ts->box_face_left = 1; + ts->box_face_right = 1; + ts->box_face_up = 1; + ts->box_face_down = 1; + ts->lasso_edge = 1; + ts->lasso_edge_left = 1; + ts->lasso_edge_right = 1; + ts->lasso_edge_up = 1; + ts->lasso_edge_down = 1; + ts->lasso_face = 1; + ts->lasso_face_left = 1; + ts->lasso_face_right = 1; + ts->lasso_face_up = 1; + ts->lasso_face_down = 1; + ts->circle_edge = 2; + ts->circle_face = 1; + + /* X-Ray. */ + ts->xray_button = true; + ts->auto_xray_object = true; + ts->auto_xray_edit = true; + ts->auto_xray_box = true; + ts->auto_xray_lasso = true; + ts->auto_xray_circle = true; + ts->select_through = true; + ts->select_through_object = true; + ts->select_through_box = true; + ts->select_through_lasso = true; + ts->select_through_circle = true; + + /* Viewport-Facing Select */ + ts->viewport_facing_select_mode = 1; + ts->viewport_facing_select_vert = 1; + ts->viewport_facing_select_edge = 1; + ts->viewport_facing_select_face = 1; + + /* Object Origin Select */ + ts->select_origin_circle = true; } void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 4b2acfa7205..a4c765d9e61 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -434,9 +434,8 @@ void blo_do_versions_userdef(UserDef *userdef) if (!USER_VERSION_ATLEAST(278, 6)) { /* Clear preference flags for re-use. */ - userdef->flag &= ~(USER_FLAG_NUMINPUT_ADVANCED | USER_FLAG_UNUSED_2 | USER_FLAG_UNUSED_3 | - USER_FLAG_UNUSED_6 | USER_FLAG_UNUSED_7 | USER_FLAG_UNUSED_9 | - USER_DEVELOPER_UI); + userdef->flag &= ~(USER_FLAG_NUMINPUT_ADVANCED | USER_FLAG_UNUSED_7 | + USER_FLAG_UNUSED_9 | USER_DEVELOPER_UI); userdef->uiflag &= ~(USER_HEADER_BOTTOM); userdef->transopts &= ~(USER_TR_UNUSED_2 | USER_TR_UNUSED_3 | USER_TR_UNUSED_4 | USER_TR_UNUSED_6 | USER_TR_UNUSED_7); @@ -525,8 +524,6 @@ void blo_do_versions_userdef(UserDef *userdef) copy_v3_fl3(userdef->light_ambient, 0.025000, 0.025000, 0.025000); - userdef->flag &= ~(USER_FLAG_UNUSED_4); - userdef->uiflag &= ~(USER_HEADER_FROM_PREF | USER_UIFLAG_UNUSED_12 | USER_UIFLAG_UNUSED_22); } diff --git a/source/blender/editors/curves/intern/curves_selection.cc b/source/blender/editors/curves/intern/curves_selection.cc index 4c3c3374f2e..ff78cedc081 100644 --- a/source/blender/editors/curves/intern/curves_selection.cc +++ b/source/blender/editors/curves/intern/curves_selection.cc @@ -705,7 +705,8 @@ bool select_lasso(const ViewContext &vc, int(pos1_proj.y), int(pos2_proj.x), int(pos2_proj.y), - IS_CLIPPED)) { + IS_CLIPPED, + false)) { apply_selection_operation_at_index(selection.span, curve_i, sel_op); changed = true; break; diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index d7fb108809f..b447630f98b 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -100,7 +100,10 @@ void ED_getTransformOrientationMatrix(const struct Scene *scene, struct Object *ob, struct Object *obedit, short around, - float r_orientation_mat[3][3]); + float r_orientation_mat[3][3], + struct BMVert *eve, + struct BMEdge *eed, + struct BMFace *efa); int BIF_countTransformOrientation(const struct bContext *C); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index dc3951ab770..1c8efac1a34 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -390,11 +390,20 @@ void mesh_foreachScreenEdge_clip_bb_segment(struct ViewContext *vc, void *userData, eV3DProjTest clip_flag); -void mesh_foreachScreenFace( +void mesh_foreachScreenFaceCenter( struct ViewContext *vc, void (*func)(void *userData, struct BMFace *efa, const float screen_co[2], int index), void *userData, eV3DProjTest clip_flag); +void mesh_foreachScreenFaceVerts(struct ViewContext *vc, + void (*func)(void *userData, + struct BMFace *efa, + const float screen_co[][2], + int total_count, + struct rctf *poly_rect, + bool *face_hit), + void *userData, + const eV3DProjTest clip_flag); void nurbs_foreachScreenVert(struct ViewContext *vc, void (*func)(void *userData, struct Nurb *nu, diff --git a/source/blender/editors/mesh/editmesh_select.cc b/source/blender/editors/mesh/editmesh_select.cc index 94c2e3352b3..c8ec4ab78c2 100644 --- a/source/blender/editors/mesh/editmesh_select.cc +++ b/source/blender/editors/mesh/editmesh_select.cc @@ -233,7 +233,8 @@ static void findnearestvert__doClosest(void *userData, dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co); - if (data->use_select_bias && BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + if (!(U.flag & USER_ADJUSTABLE_CLICK_SELECT && U.flag & USER_SELECT_UNBIASED) && + data->use_select_bias && BM_elem_flag_test(eve, BM_ELEM_SELECT)) { dist_test_bias += FIND_NEAR_SELECT_BIAS; } @@ -438,7 +439,8 @@ static void find_nearest_edge__doClosest( dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co); - if (data->use_select_bias && BM_elem_flag_test(eed, BM_ELEM_SELECT)) { + if (!(U.flag & USER_ADJUSTABLE_CLICK_SELECT && U.flag & USER_SELECT_UNBIASED) && + data->use_select_bias && BM_elem_flag_test(eed, BM_ELEM_SELECT)) { dist_test_bias += FIND_NEAR_SELECT_BIAS; } @@ -670,7 +672,8 @@ static void findnearestface__doClosest(void *userData, dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co); - if (data->use_select_bias && BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + if (!(U.flag & USER_ADJUSTABLE_CLICK_SELECT && U.flag & USER_SELECT_UNBIASED) && + data->use_select_bias && BM_elem_flag_test(efa, BM_ELEM_SELECT)) { dist_test_bias += FIND_NEAR_SELECT_BIAS; } @@ -706,27 +709,26 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc, uint base_index = 0; if (!XRAY_FLAG_ENABLED(vc->v3d)) { - float dist_test; + float dist_test = 0.0f; uint index; BMFace *efa; + uint dist_px_manhattan_test = 0; { - uint dist_px_manhattan_test = 0; - if (*dist_px_manhattan_p != 0.0f && (use_zbuf_single_px == false)) { - dist_px_manhattan_test = uint( - ED_view3d_backbuf_sample_size_clamp(vc->region, *dist_px_manhattan_p)); + if (U.flag & USER_ADJUSTABLE_CLICK_SELECT || + *dist_px_manhattan_p != 0.0f && (use_zbuf_single_px == false)) { + dist_px_manhattan_test = (uint)ED_view3d_backbuf_sample_size_clamp(vc->region, + *dist_px_manhattan_p); } DRW_select_buffer_context_create(bases, bases_len, SCE_SELECT_FACE); - if (dist_px_manhattan_test == 0) { + if (!(U.flag & USER_ADJUSTABLE_CLICK_SELECT) && dist_px_manhattan_test == 0) { index = DRW_select_buffer_sample_point(vc->depsgraph, vc->region, vc->v3d, vc->mval); - dist_test = 0.0f; } else { index = DRW_select_buffer_find_nearest_to_point( vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test); - dist_test = dist_px_manhattan_test; } if (index) { @@ -752,7 +754,7 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc, ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); - mesh_foreachScreenFace( + mesh_foreachScreenFaceCenter( vc, find_nearest_face_center__doZBuf, &data, V3D_PROJ_TEST_CLIP_DEFAULT); *r_dist_center = data.dist_px_manhattan; @@ -760,7 +762,14 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc, /* end exception */ if (efa) { - if (dist_test < *dist_px_manhattan_p) { + if (U.flag & USER_ADJUSTABLE_CLICK_SELECT && dist_px_manhattan_test < *dist_px_manhattan_p) { + if (r_base_index) { + *r_base_index = base_index; + } + *dist_px_manhattan_p = dist_px_manhattan_test; + return efa; + } + else if (!(U.flag & USER_ADJUSTABLE_CLICK_SELECT) && dist_test < *dist_px_manhattan_p) { if (r_base_index) { *r_base_index = base_index; } @@ -804,7 +813,7 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc, *dist_px_manhattan_p; ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); - mesh_foreachScreenFace(vc, findnearestface__doClosest, &data, clip_flag); + mesh_foreachScreenFaceCenter(vc, findnearestface__doClosest, &data, clip_flag); hit = (data.use_cycle && data.hit_cycle.face) ? &data.hit_cycle : &data.hit; diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 80e5da43423..689275a4d79 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -3847,6 +3847,61 @@ static void SCREEN_OT_repeat_history(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Repeat Tool Operator + * \{ */ + +static int repeat_tool_exec(bContext *C, wmOperator *UNUSED(op)) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wmOperator *lastop = wm->operators.last; + + /* Seek last registered operator */ + while (lastop) { + if (lastop->type->flag & OPTYPE_REGISTER) { + if (STREQ(lastop->idname, "MESH_OT_delete") || STREQ(lastop->idname, "MESH_OT_select_all") || + STREQ(lastop->idname, "MESH_OT_select_mode") || + STREQ(lastop->idname, "OBJECT_OT_editmode_toggle") || + STREQ(lastop->idname, "OBJECT_OT_select_all") || + STREQ(lastop->idname, "TRANSFORM_OT_resize") || + STREQ(lastop->idname, "TRANSFORM_OT_rotate") || + STREQ(lastop->idname, "TRANSFORM_OT_trackball") || + STREQ(lastop->idname, "TRANSFORM_OT_translate")) { + lastop = lastop->prev; + } + else { + printf(lastop->idname, "\n"); + break; + } + } + else { + lastop = lastop->prev; + } + } + + if (lastop) { + WM_operator_free_all_after(wm, lastop); + WM_operator_repeat_tool(C, lastop); + } + + return OPERATOR_CANCELLED; +} + +static void SCREEN_OT_repeat_tool(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Repeat Tool"; + ot->description = "Repeat last tool"; + ot->idname = "SCREEN_OT_repeat_tool"; + + /* api callbacks */ + ot->exec = repeat_tool_exec; + + ot->poll = ED_operator_screenactive; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Redo Operator * \{ */ @@ -5679,6 +5734,7 @@ void ED_operatortypes_screen(void) WM_operatortype_append(SCREEN_OT_actionzone); WM_operatortype_append(SCREEN_OT_repeat_last); WM_operatortype_append(SCREEN_OT_repeat_history); + WM_operatortype_append(SCREEN_OT_repeat_tool); WM_operatortype_append(SCREEN_OT_redo_last); /* Screen tools. */ diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index d5b96e03f13..aa239596e8d 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -1235,3 +1235,211 @@ void VIEW3D_OT_toggle_xray(wmOperatorType *ot) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Automatic XRay + * \{ */ + +static int toggle_auto_xray_exec(bContext *C, wmOperator *op) +{ + ScrArea *area = CTX_wm_area(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Object *obedit = CTX_data_edit_object(C); + bToolRef *tref = area->runtime.tool; + const bool mode_match = obedit ? ts->auto_xray_edit : ts->auto_xray_object; + + if (STREQ(tref->idname, "builtin.select_box") || + STREQ(tref->idname_fallback, "builtin.select_box")) { + if (mode_match && ts->auto_xray && ts->auto_xray_box) { + ts->auto_xray ^= true; + } + else { + if (!mode_match) { + if (obedit) { + ts->auto_xray_edit ^= true; + } + else { + ts->auto_xray_object ^= true; + } + } + + if (!ts->auto_xray) { + ts->auto_xray ^= true; + } + + if (!ts->auto_xray_box) { + ts->auto_xray_box ^= true; + } + } + } + else if (STREQ(tref->idname, "builtin.select_lasso") || + STREQ(tref->idname_fallback, "builtin.select_lasso")) { + if (mode_match && ts->auto_xray && ts->auto_xray_lasso) { + ts->auto_xray ^= true; + } + else { + if (!mode_match) { + if (obedit) { + ts->auto_xray_edit ^= true; + } + else { + ts->auto_xray_object ^= true; + } + } + + if (!ts->auto_xray) { + ts->auto_xray ^= true; + } + + if (!ts->auto_xray_lasso) { + ts->auto_xray_lasso ^= true; + } + } + } + else if (STREQ(tref->idname, "builtin.select_circle") || + STREQ(tref->idname_fallback, "builtin.select_circle")) { + if (mode_match && ts->auto_xray && ts->auto_xray_circle) { + ts->auto_xray ^= true; + } + else { + if (!mode_match) { + if (obedit) { + ts->auto_xray_edit ^= true; + } + else { + ts->auto_xray_object ^= true; + } + } + + if (!ts->auto_xray) { + ts->auto_xray ^= true; + } + + if (!ts->auto_xray_circle) { + ts->auto_xray_circle ^= true; + } + } + } + + ED_area_tag_redraw(area); + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_toggle_auto_xray(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Toggle Automatic X-Ray"; + ot->idname = "VIEW3D_OT_toggle_auto_xray"; + ot->description = "Transparent scene display during box, lasso, and circle select"; + + /* api callbacks */ + ot->exec = toggle_auto_xray_exec; + ot->poll = ED_operator_view3d_active; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Select Through + * \{ */ + +static int toggle_select_through_exec(bContext *C, wmOperator *op) +{ + ScrArea *area = CTX_wm_area(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Object *obedit = CTX_data_edit_object(C); + bToolRef *tref = area->runtime.tool; + const bool mode_match = obedit ? ts->select_through_edit : ts->select_through_object; + + if (STREQ(tref->idname, "builtin.select_box") || + STREQ(tref->idname_fallback, "builtin.select_box")) { + if (mode_match && ts->select_through && ts->select_through_box) { + ts->select_through ^= true; + } + else { + if (!mode_match) { + if (obedit) { + ts->select_through_edit ^= true; + } + else { + ts->select_through_object ^= true; + } + } + + if (!ts->select_through) { + ts->select_through ^= true; + } + + if (!ts->select_through_box) { + ts->select_through_box ^= true; + } + } + } + else if (STREQ(tref->idname, "builtin.select_lasso") || + STREQ(tref->idname_fallback, "builtin.select_lasso")) { + if (mode_match && ts->select_through && ts->select_through_lasso) { + ts->select_through ^= true; + } + else { + if (!mode_match) { + if (obedit) { + ts->select_through_edit ^= true; + } + else { + ts->select_through_object ^= true; + } + } + + if (!ts->select_through) { + ts->select_through ^= true; + } + + if (!ts->select_through_lasso) { + ts->select_through_lasso ^= true; + } + } + } + else if (STREQ(tref->idname, "builtin.select_circle") || + STREQ(tref->idname_fallback, "builtin.select_circle")) { + if (mode_match && ts->select_through && ts->select_through_circle) { + ts->select_through ^= true; + } + else { + if (!mode_match) { + if (obedit) { + ts->select_through_edit ^= true; + } + else { + ts->select_through_object ^= true; + } + } + + if (!ts->select_through) { + ts->select_through ^= true; + } + + if (!ts->select_through_circle) { + ts->select_through_circle ^= true; + } + } + } + + ED_area_tag_redraw(area); + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_toggle_select_through(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Toggle Select Through"; + ot->idname = "VIEW3D_OT_toggle_select_through"; + ot->description = "Select occluded objects and mesh elements with box, lasso, and circle select"; + + /* api callbacks */ + ot->exec = toggle_select_through_exec; + ot->poll = ED_operator_view3d_active; +} + +/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index b94833dd276..f85453b7a96 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -55,6 +55,8 @@ void VIEW3D_OT_render_border(struct wmOperatorType *ot); void VIEW3D_OT_clear_render_border(struct wmOperatorType *ot); void VIEW3D_OT_toggle_shading(struct wmOperatorType *ot); void VIEW3D_OT_toggle_xray(struct wmOperatorType *ot); +void VIEW3D_OT_toggle_auto_xray(struct wmOperatorType *ot); +void VIEW3D_OT_toggle_select_through(struct wmOperatorType *ot); /* view3d_draw.c */ diff --git a/source/blender/editors/space_view3d/view3d_iterators.cc b/source/blender/editors/space_view3d/view3d_iterators.cc index e7e8eb5b275..d5e560331c4 100644 --- a/source/blender/editors/space_view3d/view3d_iterators.cc +++ b/source/blender/editors/space_view3d/view3d_iterators.cc @@ -13,6 +13,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BLI_bitmap.h" #include "BLI_math_geom.h" #include "BLI_rect.h" #include "BLI_utildefines.h" @@ -38,6 +39,8 @@ #include "ED_screen.h" #include "ED_view3d.h" +#include "MEM_guardedalloc.h" + /* -------------------------------------------------------------------- */ /** \name Internal Clipping Utilities * \{ */ @@ -238,7 +241,7 @@ struct foreachScreenEdge_userData { int content_planes_len; }; -struct foreachScreenFace_userData { +struct foreachScreenFaceCenter_userData { void (*func)(void *userData, BMFace *efa, const float screen_co_b[2], int index); void *userData; ViewContext vc; @@ -536,12 +539,12 @@ void mesh_foreachScreenEdge_clip_bb_segment(ViewContext *vc, /** \name Edit-Mesh: For Each Screen Face Center * \{ */ -static void mesh_foreachScreenFace__mapFunc(void *userData, +static void mesh_foreachScreenFaceCenter__mapFunc(void *userData, int index, const float cent[3], const float /*no*/[3]) { - foreachScreenFace_userData *data = static_cast(userData); + foreachScreenFaceCenter_userData *data = static_cast(userData); BMFace *efa = BM_face_at_index(data->vc.em->bm, index); if (UNLIKELY(BM_elem_flag_test(efa, BM_ELEM_HIDDEN))) { return; @@ -556,14 +559,14 @@ static void mesh_foreachScreenFace__mapFunc(void *userData, data->func(data->userData, efa, screen_co, index); } -void mesh_foreachScreenFace( +void mesh_foreachScreenFaceCenter( ViewContext *vc, void (*func)(void *userData, BMFace *efa, const float screen_co_b[2], int index), void *userData, const eV3DProjTest clip_flag) { BLI_assert((clip_flag & V3D_PROJ_TEST_CLIP_CONTENT) == 0); - foreachScreenFace_userData data; + foreachScreenFaceCenter_userData data; Mesh *me = editbmesh_get_eval_cage_from_orig( vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH); @@ -580,16 +583,150 @@ void mesh_foreachScreenFace( const int face_dot_tags_num = me->runtime->subsurf_face_dot_tags.size(); if (face_dot_tags_num && (face_dot_tags_num != me->totvert)) { BKE_mesh_foreach_mapped_subdiv_face_center( - me, mesh_foreachScreenFace__mapFunc, &data, MESH_FOREACH_NOP); + me, mesh_foreachScreenFaceCenter__mapFunc, &data, MESH_FOREACH_NOP); } else { BKE_mesh_foreach_mapped_face_center( - me, mesh_foreachScreenFace__mapFunc, &data, MESH_FOREACH_NOP); + me, mesh_foreachScreenFaceCenter__mapFunc, &data, MESH_FOREACH_NOP); } } /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Edit-Mesh: For Each Screen Face Verts + * \{ */ + +void mesh_foreachScreenFaceVerts(ViewContext *vc, + void (*func)(void *userData, + struct BMFace *efa, + const float screen_co[][2], + int total_count, + rctf *screen_rect, + bool *face_hit), + void *userData, + const eV3DProjTest clip_flag) +{ + ED_view3d_check_mats_rv3d(vc->rv3d); + BM_mesh_elem_table_ensure(vc->em->bm, BM_FACE); + + BMFace *efa; + const BMesh *bm = vc->em->bm; + + float temp_screen_co[2]; + int total_length = 0; + + float(*screen_coords)[2] = static_cast( + MEM_mallocN(sizeof(float) * 2 * bm->totvert, __func__)); + int face_screen_verts_size = 4; + float(*face_screen_verts)[2] = static_cast( + MEM_mallocN(sizeof(int) * 2 * face_screen_verts_size, __func__)); + + /* This makes only sense on subdivided meshes.*/ + BLI_bitmap *faces_visited; + int cage_index = BKE_modifiers_get_cage_index(vc->scene, vc->obedit, NULL, 1); + const bool cage_display = cage_index != -1; + if (cage_display) { + faces_visited = BLI_BITMAP_NEW((size_t)bm->totface, __func__); + } + + /* Transform and store all visible verts into screen coords. */ + for (int i = 0; i < bm->totvert; i++) { + BMVert *bmvert = BM_vert_at_index(vc->em->bm, i); + + if (BM_elem_flag_test_bool(bmvert, BM_ELEM_HIDDEN)) { + continue; + } + + if (ED_view3d_project_float_object(vc->region, bmvert->co, temp_screen_co, clip_flag) == + V3D_PROJ_RET_OK) { + screen_coords[i][0] = temp_screen_co[0]; + screen_coords[i][1] = temp_screen_co[1]; + } + else { + screen_coords[i][0] = 0.0f; + screen_coords[i][1] = 0.0f; + } + } + + const int *poly_index = static_cast(CustomData_get_layer(&bm->pdata, CD_ORIGINDEX)); + const bool use_original_index = poly_index != 0; + + rctf poly_rect_data; + rctf *poly_rect = &poly_rect_data; + bool face_hit = false; + + /* Collect polygon verts and send off per poly callback. */ + for (int i = 0; i < bm->totface; i++) { + int original_index = i; + if (use_original_index) { + original_index = *poly_index++; + if (original_index == ORIGINDEX_NONE) { + continue; + } + } + + if (cage_display && BLI_BITMAP_TEST(faces_visited, original_index)) { + continue; + } + + efa = BM_face_at_index(vc->em->bm, original_index); + if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + + if (bm->totvert > face_screen_verts_size) { + face_screen_verts_size = bm->totvert; + MEM_freeN(face_screen_verts); + face_screen_verts = static_cast( + MEM_mallocN(sizeof(float) * 2 * face_screen_verts_size, __func__)); + } + + total_length = 0; + BLI_rctf_init_minmax(poly_rect); + + bool skip = false; + + BMLoop *l_first, *l_iter; + int j = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + const int k = BM_elem_index_get(l_iter->v); + face_screen_verts[j][0] = screen_coords[k][0]; + face_screen_verts[j][1] = screen_coords[k][1]; + + /* Ignore polygons with invalid screen coords.*/ + if (face_screen_verts[j][0] == 0.0f && face_screen_verts[j][1] == 0.0f) { + skip = true; + break; + } + + total_length++, j++; + BLI_rctf_do_minmax_v(poly_rect, screen_coords[k]); + } while ((l_iter = l_iter->next) != l_first); + + if (skip) { + continue; + } + + face_hit = false; + + func( + userData, efa, (const float(*)[2])face_screen_verts, total_length, poly_rect, &face_hit); + + if (cage_display && face_hit) { + BLI_BITMAP_ENABLE(faces_visited, original_index); + } + } + } + + if (cage_display) { + MEM_freeN(faces_visited); + } + MEM_freeN(screen_coords); + MEM_freeN(face_screen_verts); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Edit-Nurbs: For Each Screen Vertex * \{ */ diff --git a/source/blender/editors/space_view3d/view3d_navigate.c b/source/blender/editors/space_view3d/view3d_navigate.c index 9f4710b28a0..809000be817 100644 --- a/source/blender/editors/space_view3d/view3d_navigate.c +++ b/source/blender/editors/space_view3d/view3d_navigate.c @@ -99,6 +99,26 @@ void view3d_operator_properties_common(wmOperatorType *ot, const enum eV3D_OpPro if (flag & V3D_OP_PROP_USE_MOUSE_INIT) { WM_operator_properties_use_cursor_init(ot); } + + if (flag & V3D_OP_PROP_FRAME_SELECTED_DISTANCE) { + PropertyRNA *prop; + prop = RNA_def_int(ot->srna, + "frame_selected_distance", + 0, + 0, + 1000, + "Camera Offset", + "Distance to put camera from selected", + 0, + 1000); + + } + + if (flag & V3D_OP_PROP_ZOOM_SPEED) { + PropertyRNA *prop; + prop = RNA_def_float( + ot->srna, "zoom_speed", 1.2f, 1.001, 10, "Zoom Speed", "Speed to zoom camera", 1.001f, 10); + } } /** \} */ @@ -652,7 +672,8 @@ static void view3d_from_minmax(bContext *C, const float min[3], const float max[3], bool ok_dist, - const int smooth_viewtx) + const int smooth_viewtx, + const int view_dist) { RegionView3D *rv3d = region->regiondata; float afm[3]; @@ -695,7 +716,7 @@ static void view3d_from_minmax(bContext *C, v3d, region, depsgraph, persp, true, (size / 2) * VIEW3D_MARGIN); if (rv3d->is_persp) { /* don't zoom closer than the near clipping plane */ - new_dist = max_ff(new_dist, v3d->clip_start * 1.5f); + new_dist = max_ff(new_dist, v3d->clip_start * 1.5f + view_dist); } } } @@ -741,7 +762,8 @@ static void view3d_from_minmax_multi(bContext *C, const float min[3], const float max[3], const bool ok_dist, - const int smooth_viewtx) + const int smooth_viewtx, + const int view_dist) { ScrArea *area = CTX_wm_area(C); ARegion *region; @@ -751,7 +773,7 @@ static void view3d_from_minmax_multi(bContext *C, /* when using all regions, don't jump out of camera view, * but _do_ allow locked cameras to be moved */ if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) { - view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx); + view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx, view_dist); } } } @@ -831,10 +853,10 @@ static int view3d_all_exec(bContext *C, wmOperator *op) ED_view3d_smooth_view_undo_begin(C, area); if (use_all_regions) { - view3d_from_minmax_multi(C, v3d, min, max, true, smooth_viewtx); + view3d_from_minmax_multi(C, v3d, min, max, true, smooth_viewtx, 0); } else { - view3d_from_minmax(C, v3d, region, min, max, true, smooth_viewtx); + view3d_from_minmax(C, v3d, region, min, max, true, smooth_viewtx, 0); } ED_view3d_smooth_view_undo_end(C, area, op->type->name, false); @@ -893,6 +915,7 @@ static int viewselected_exec(bContext *C, wmOperator *op) /* any one of the regions may be locked */ (use_all_regions && v3d->flag2 & V3D_LOCK_CAMERA)); const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + const int view_dist = RNA_int_get(op->ptr, "frame_selected_distance"); INIT_MINMAX(min, max); if (is_face_map) { @@ -1007,10 +1030,10 @@ static int viewselected_exec(bContext *C, wmOperator *op) ED_view3d_smooth_view_undo_begin(C, area); if (use_all_regions) { - view3d_from_minmax_multi(C, v3d, min, max, ok_dist, smooth_viewtx); + view3d_from_minmax_multi(C, v3d, min, max, ok_dist, smooth_viewtx, view_dist); } else { - view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx); + view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx, view_dist); } ED_view3d_smooth_view_undo_end(C, area, op->type->name, false); @@ -1033,7 +1056,8 @@ void VIEW3D_OT_view_selected(wmOperatorType *ot) ot->flag = 0; /* properties */ - view3d_operator_properties_common(ot, V3D_OP_PROP_USE_ALL_REGIONS); + view3d_operator_properties_common( + ot, V3D_OP_PROP_USE_ALL_REGIONS | V3D_OP_PROP_FRAME_SELECTED_DISTANCE); } /** \} */ @@ -1192,7 +1216,7 @@ static int view_axis_exec(bContext *C, wmOperator *op) Object *obedit = CTX_data_edit_object(C); /* same as transform gizmo when normal is set */ ED_getTransformOrientationMatrix( - scene, view_layer, v3d, obact, obedit, V3D_AROUND_ACTIVE, twmat); + scene, view_layer, v3d, obact, obedit, V3D_AROUND_ACTIVE, twmat, NULL, NULL, NULL); align_quat = align_quat_buf; mat3_to_quat(align_quat, twmat); invert_qt_normalized(align_quat); diff --git a/source/blender/editors/space_view3d/view3d_navigate.h b/source/blender/editors/space_view3d/view3d_navigate.h index 46dacbc606f..a641a3748fa 100644 --- a/source/blender/editors/space_view3d/view3d_navigate.h +++ b/source/blender/editors/space_view3d/view3d_navigate.h @@ -34,6 +34,8 @@ enum eV3D_OpPropFlag { V3D_OP_PROP_DELTA = (1 << 1), V3D_OP_PROP_USE_ALL_REGIONS = (1 << 2), V3D_OP_PROP_USE_MOUSE_INIT = (1 << 3), + V3D_OP_PROP_FRAME_SELECTED_DISTANCE = (1 << 4), + V3D_OP_PROP_ZOOM_SPEED = (1 << 5), }; enum { diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom.c b/source/blender/editors/space_view3d/view3d_navigate_zoom.c index 51aab367959..a5f8341dd27 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_zoom.c +++ b/source/blender/editors/space_view3d/view3d_navigate_zoom.c @@ -506,9 +506,10 @@ static int viewzoom_exec(bContext *C, wmOperator *op) } ED_view3d_dist_range_get(v3d, dist_range); + const float zoom_speed = RNA_float_get(op->ptr, "zoom_speed"); if (delta < 0) { - const float step = 1.2f; + const float step = zoom_speed; if (use_cam_zoom) { view_zoom_to_window_xy_camera(scene, depsgraph, v3d, region, step, zoom_xy); } @@ -519,7 +520,7 @@ static int viewzoom_exec(bContext *C, wmOperator *op) } } else { - const float step = 1.0f / 1.2f; + const float step = 1.0f / zoom_speed; if (use_cam_zoom) { view_zoom_to_window_xy_camera(scene, depsgraph, v3d, region, step, zoom_xy); } @@ -631,8 +632,9 @@ void VIEW3D_OT_zoom(wmOperatorType *ot) ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY; /* properties */ - view3d_operator_properties_common( - ot, V3D_OP_PROP_DELTA | V3D_OP_PROP_MOUSE_CO | V3D_OP_PROP_USE_MOUSE_INIT); + view3d_operator_properties_common(ot, + V3D_OP_PROP_DELTA | V3D_OP_PROP_MOUSE_CO | + V3D_OP_PROP_USE_MOUSE_INIT | V3D_OP_PROP_ZOOM_SPEED); } /** \} */ diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c index ad12aef6d67..c8fdbd8f044 100644 --- a/source/blender/editors/space_view3d/view3d_ops.c +++ b/source/blender/editors/space_view3d/view3d_ops.c @@ -200,6 +200,8 @@ void view3d_operatortypes(void) WM_operatortype_append(VIEW3D_OT_toggle_shading); WM_operatortype_append(VIEW3D_OT_toggle_xray); WM_operatortype_append(VIEW3D_OT_toggle_matcap_flip); + WM_operatortype_append(VIEW3D_OT_toggle_auto_xray); + WM_operatortype_append(VIEW3D_OT_toggle_select_through); WM_operatortype_append(VIEW3D_OT_ruler_add); WM_operatortype_append(VIEW3D_OT_ruler_remove); diff --git a/source/blender/editors/space_view3d/view3d_select.cc b/source/blender/editors/space_view3d/view3d_select.cc index 3c91480b6cc..0036b8269c6 100644 --- a/source/blender/editors/space_view3d/view3d_select.cc +++ b/source/blender/editors/space_view3d/view3d_select.cc @@ -80,6 +80,7 @@ #include "ED_screen.h" #include "ED_sculpt.h" #include "ED_select_utils.h" +#include "ED_transform.h" #include "UI_interface.h" #include "UI_resources.h" @@ -103,7 +104,12 @@ float ED_view3d_select_dist_px(void) { - return 75.0f * U.pixelsize; + if (U.flag & USER_ADJUSTABLE_CLICK_SELECT) { + return U.selection_radius * U.pixelsize; + } + else { + return 75.0f * U.pixelsize; + } } void ED_view3d_viewcontext_init(bContext *C, ViewContext *vc, Depsgraph *depsgraph) @@ -239,15 +245,406 @@ static void editselect_buf_cache_init_with_generic_userdata(wmGenericUserData *w /** \name Internal Edit-Mesh Utilities * \{ */ -static bool edbm_backbuf_check_and_select_verts(EditSelectBuf_Cache *esel, +struct BoxSelectUserData { + ViewContext *vc; + const rcti *rect; + const rctf *rect_fl; + rctf _rect_fl; + eSelectOp sel_op; + eBezTriple_Flag select_flag; + int edge_style; + int face_style; + bool check_mesh_direction; + + /* runtime */ + bool is_done; + bool is_changed; +}; + +struct LassoSelectUserData { + ViewContext *vc; + const rcti *rect; + const rctf *rect_fl; + rctf _rect_fl; + const int (*mcoords)[2]; + int mcoords_len; + eSelectOp sel_op; + eBezTriple_Flag select_flag; + int edge_style; + int face_style; + bool check_mesh_direction; + + /* runtime */ + int pass; + bool is_done; + bool is_changed; +}; + +struct CircleSelectUserData { + ViewContext *vc; + bool select; + int mval[2]; + float mval_fl[2]; + float radius; + float radius_squared; + eBezTriple_Flag select_flag; + int edge_style; + int face_style; + bool check_mesh_direction; + + /* runtime */ + bool is_changed; +}; + +bool edbm_normal_facing_viewport(ViewContext *vc, + struct BMVert *eve, + struct BMEdge *eed, + struct BMFace *efa, + bool use_direction) +{ + ToolSettings *ts = vc->scene->toolsettings; + float meshmat[3][3]; + bool backface = false; + int direction = 0; + if (eve != NULL) { + ED_getTransformOrientationMatrix(vc->scene, + vc->view_layer, + vc->v3d, + vc->obact, + vc->obedit, + V3D_AROUND_ACTIVE, + meshmat, + eve, + NULL, + NULL); + direction = ts->viewport_facing_select_vert; + if (use_direction && (direction == 4 || direction == 8)) { + backface = true; + } + } + else if (eed != NULL) { + ED_getTransformOrientationMatrix(vc->scene, + vc->view_layer, + vc->v3d, + vc->obact, + vc->obedit, + V3D_AROUND_ACTIVE, + meshmat, + NULL, + eed, + NULL); + direction = ts->viewport_facing_select_edge; + if (use_direction && (direction == 4 || direction == 8)) { + backface = true; + } + } + else if (efa != NULL) { + ED_getTransformOrientationMatrix(vc->scene, + vc->view_layer, + vc->v3d, + vc->obact, + vc->obedit, + V3D_AROUND_ACTIVE, + meshmat, + NULL, + NULL, + efa); + direction = ts->viewport_facing_select_face; + if (use_direction && (direction == 4 || direction == 8)) { + backface = true; + } + } + normalize_m3(meshmat); + invert_m3(meshmat); + float meshcol3[3] = {0, 0, 0}; + meshcol3[0] = meshmat[0][2]; + meshcol3[1] = meshmat[1][2]; + meshcol3[2] = meshmat[2][2]; + float viewcol3[3] = {0, 0, 0}; + viewcol3[0] = vc->rv3d->viewmat[0][2]; + viewcol3[1] = vc->rv3d->viewmat[1][2]; + viewcol3[2] = vc->rv3d->viewmat[2][2]; + bool mesh_facing; + if (backface) { + mesh_facing = dot_v3v3(meshcol3, viewcol3) < -ts->viewport_facing_select_threshold; + } + else { + mesh_facing = dot_v3v3(meshcol3, viewcol3) > ts->viewport_facing_select_threshold; + } + return mesh_facing; +} + +bool edbm_facing_viewport_precheck(ToolSettings *ts, int style, bool xray) +{ + if (!ts->viewport_facing_select) { + return false; + } + const bool mode_match = xray ? ts->viewport_facing_select_mode == 1 || + ts->viewport_facing_select_mode == 4 : + ts->viewport_facing_select_mode < 4; + const bool check_mesh_facing = mode_match && style > 0 && style < 16; + return check_mesh_facing; +} + +bool edbm_facing_viewport(ViewContext *vc, BMVert *eve, BMEdge *eed, BMFace *efa, int style) +{ + BMIter iter; + bool mesh_facing = false; + if (eve != NULL) { + /* viewport-facing or rear-facing vert */ + mesh_facing = edbm_normal_facing_viewport(vc, eve, NULL, NULL, true); + if (!mesh_facing && eve->e && eve->e->l && (style == 2 || style == 8)) { + BM_ITER_ELEM (efa, &iter, eve, BM_FACES_OF_VERT) { + const bool eve_efa_facing = edbm_normal_facing_viewport(vc, NULL, NULL, efa, false); + /* vert of a viewport-facing face */ + if (style == 2) { + if (eve_efa_facing) { + mesh_facing = true; + break; + } + } + /* vert of a rear-facing face */ + else if (!eve_efa_facing) { + mesh_facing = true; + break; + } + } + } + } + else if (eed != NULL) { + /* viewport-facing or rear-facing edge */ + mesh_facing = edbm_normal_facing_viewport(vc, NULL, eed, NULL, true); + if (!mesh_facing && eed->l && (style == 2 || style == 8)) { + BM_ITER_ELEM (efa, &iter, eed, BM_FACES_OF_EDGE) { + const bool eed_efa_facing = edbm_normal_facing_viewport(vc, NULL, NULL, efa, false); + /* edge of a viewport-facing face */ + if (style == 2) { + if (eed_efa_facing) { + mesh_facing = true; + break; + } + } + /* edge of a rear-facing face */ + else if (!eed_efa_facing) { + mesh_facing = true; + break; + } + } + } + } + else if (efa != NULL) { + /* viewport-facing or rear-facing face */ + mesh_facing = edbm_normal_facing_viewport(vc, NULL, NULL, efa, true); + if (!mesh_facing && (style == 2 || style == 8)) { + BM_ITER_ELEM (eve, &iter, efa, BM_VERTS_OF_FACE) { + const bool efa_eve_facing = edbm_normal_facing_viewport(vc, eve, NULL, NULL, false); + /* face has a viewport-facing vert */ + if (style == 2) { + if (efa_eve_facing) { + mesh_facing = true; + break; + } + } + /* face has a rear-facing vert */ + else if (!efa_eve_facing) { + mesh_facing = true; + break; + } + } + } + } + return mesh_facing; +} + +int edbm_select_style(ToolSettings *ts, int style[2], const bool box, const bool lasso, wmOperator *op) +{ + if (U.drag_select_control & USER_DRAG_SELECT_KEYMAP) { + style[0] = RNA_enum_get(op->ptr, "edge_type"); + style[1] = RNA_enum_get(op->ptr, "face_type"); + } + else { + if (box) { + if (ts->box_drag_direction > 1) { + if (ts->box_drag_direction == 2) { + if (ts->box_direction_upright) { + style[0] = ts->box_edge_right; + style[1] = ts->box_face_right; + } + else { + style[0] = ts->box_edge_left; + style[1] = ts->box_face_left; + } + } + else if (ts->box_direction_upright) { + style[0] = ts->box_edge_up; + style[1] = ts->box_face_up; + } + else { + style[0] = ts->box_edge_down; + style[1] = ts->box_face_down; + } + } + else { + style[0] = ts->box_edge; + style[1] = ts->box_face; + } + } + else if (lasso) { + if (ts->lasso_drag_direction > 1) { + if (ts->lasso_drag_direction == 2) { + if (ts->lasso_direction_upright) { + style[0] = ts->lasso_edge_right; + style[1] = ts->lasso_face_right; + } + else { + style[0] = ts->lasso_edge_left; + style[1] = ts->lasso_face_left; + } + } + else if (ts->lasso_direction_upright) { + style[0] = ts->lasso_edge_up; + style[1] = ts->lasso_face_up; + } + else { + style[0] = ts->lasso_edge_down; + style[1] = ts->lasso_face_down; + } + } + else { + style[0] = ts->lasso_edge; + style[1] = ts->lasso_face; + } + } + else { + style[0] = ts->circle_edge; + style[1] = ts->circle_face; + } + } + return style[2]; +} + +bool edbm_circle_enclose_mesh(BMEdge *eed, BMFace *efa, struct CircleSelectUserData *data) +{ + BMVert *eve; + BMIter iter; + bool enclose = false; + + if (eed != NULL) { + BM_ITER_ELEM (eve, &iter, eed, BM_VERTS_OF_EDGE) { + float vertv3[3] = {eve->co[0], eve->co[1], eve->co[2]}; + float vertv2[2] = {0.0f, 0.0f}; + ED_view3d_project_float_object( + data->vc->region, vertv3, vertv2, V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + enclose = len_squared_v2v2(data->mval_fl, vertv2) <= data->radius_squared; + if (!enclose) { + break; + } + } + } + else if (efa != NULL) { + BM_ITER_ELEM (eve, &iter, efa, BM_VERTS_OF_FACE) { + float vertv3[3] = {eve->co[0], eve->co[1], eve->co[2]}; + float vertv2[2] = {0.0f, 0.0f}; + ED_view3d_project_float_object( + data->vc->region, vertv3, vertv2, V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + enclose = len_squared_v2v2(data->mval_fl, vertv2) <= data->radius_squared; + if (!enclose) { + break; + } + } + } + return enclose; +} + +bool edbm_center_face(ViewContext *vc, + BMFace *efa, + const rctf *rect, + struct LassoSelectUserData *lassoData, + struct CircleSelectUserData *circleData) +{ + BMVert *eve; + BMIter iter; + float centerv3[3] = {0.0f, 0.0f, 0.0f}; + float centerv2[2] = {0.0f, 0.0f}; + bool center_face = false; + + /* tri */ + if (efa->len == 3) { + float tri_vco[3][3] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; + int tri_index = 0; + BM_ITER_ELEM (eve, &iter, efa, BM_VERTS_OF_FACE) { + tri_vco[tri_index][0] = eve->co[0]; + tri_vco[tri_index][1] = eve->co[1]; + tri_vco[tri_index][2] = eve->co[2]; + tri_index++; + } + float triv1[3] = {tri_vco[0][0], tri_vco[0][1], tri_vco[0][2]}; + float triv2[3] = {tri_vco[1][0], tri_vco[1][1], tri_vco[1][2]}; + float triv3[3] = {tri_vco[2][0], tri_vco[2][1], tri_vco[2][2]}; + mid_v3_v3v3v3(centerv3, triv1, triv2, triv3); + } + /* quad */ + else if (efa->len == 4) { + float quad_vco[4][3] = { + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; + int quad_index = 0; + BM_ITER_ELEM (eve, &iter, efa, BM_VERTS_OF_FACE) { + quad_vco[quad_index][0] = eve->co[0]; + quad_vco[quad_index][1] = eve->co[1]; + quad_vco[quad_index][2] = eve->co[2]; + quad_index++; + } + float quadv1[3] = {quad_vco[0][0], quad_vco[0][1], quad_vco[0][2]}; + float quadv2[3] = {quad_vco[1][0], quad_vco[1][1], quad_vco[1][2]}; + float quadv3[3] = {quad_vco[2][0], quad_vco[2][1], quad_vco[2][2]}; + float quadv4[3] = {quad_vco[3][0], quad_vco[3][1], quad_vco[3][2]}; + mid_v3_v3v3v3v3(centerv3, quadv1, quadv2, quadv3, quadv4); + } + /* ngon */ + else { + const float w = 1.0f / (float)efa->len; + BM_ITER_ELEM (eve, &iter, efa, BM_VERTS_OF_FACE) { + madd_v3_v3fl(centerv3, eve->co, w); + } + } + ED_view3d_project_float_object( + vc->region, centerv3, centerv2, V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + + /* lasso center */ + if (lassoData != NULL) { + center_face = BLI_rctf_isect_pt_v(rect, centerv2) && + BLI_lasso_is_point_inside(lassoData->mcoords, + lassoData->mcoords_len, + centerv2[0], + centerv2[1], + IS_CLIPPED); + } + /* circle center */ + else if (circleData != NULL) { + center_face = (len_squared_v2v2(circleData->mval_fl, centerv2) <= circleData->radius_squared); + } + /* box center */ + else { + center_face = BLI_rctf_isect_pt_v(rect, centerv2); + } + return center_face; +} + + + +static bool edbm_backbuf_check_and_select_verts(ViewContext *vc, + EditSelectBuf_Cache *esel, Depsgraph *depsgraph, Object *ob, BMEditMesh *em, const eSelectOp sel_op) { + ToolSettings *ts = vc->scene->toolsettings; BMVert *eve; BMIter iter; bool changed = false; + const int style = ts->viewport_facing_select_vert; + const bool check_mesh_facing = edbm_facing_viewport_precheck(ts, style, false); const BLI_bitmap *select_bitmap = esel->select_bitmap; uint index = DRW_select_buffer_context_offset_for_object_elem(depsgraph, ob, SCE_SELECT_VERTEX); @@ -260,7 +657,12 @@ static bool edbm_backbuf_check_and_select_verts(EditSelectBuf_Cache *esel, if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT); const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index); - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + bool mesh_facing = true; + if (is_inside && check_mesh_facing) { + mesh_facing = edbm_facing_viewport(vc, eve, NULL, NULL, style); + } + const int sel_op_result = ED_select_op_action_deselected( + sel_op, is_select, is_inside && mesh_facing); if (sel_op_result != -1) { BM_vert_select_set(em->bm, eve, sel_op_result); changed = true; @@ -271,15 +673,20 @@ static bool edbm_backbuf_check_and_select_verts(EditSelectBuf_Cache *esel, return changed; } -static bool edbm_backbuf_check_and_select_edges(EditSelectBuf_Cache *esel, +static bool edbm_backbuf_check_and_select_edges(void *userData, + EditSelectBuf_Cache *esel, Depsgraph *depsgraph, Object *ob, BMEditMesh *em, const eSelectOp sel_op) { + CircleSelectUserData *data = static_cast(userData); + ToolSettings *ts = data->vc->scene->toolsettings; BMEdge *eed; BMIter iter; bool changed = false; + const int style = ts->viewport_facing_select_edge; + const bool check_mesh_facing = edbm_facing_viewport_precheck(ts, style, false); const BLI_bitmap *select_bitmap = esel->select_bitmap; uint index = DRW_select_buffer_context_offset_for_object_elem(depsgraph, ob, SCE_SELECT_EDGE); @@ -292,7 +699,19 @@ static bool edbm_backbuf_check_and_select_edges(EditSelectBuf_Cache *esel, if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index); - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + bool enclose_edge = true; + bool mesh_facing = true; + + if (data->edge_style == 4 && is_inside) { + enclose_edge = edbm_circle_enclose_mesh(eed, NULL, data); + } + + if (check_mesh_facing && is_inside) { + mesh_facing = edbm_facing_viewport(data->vc, NULL, eed, NULL, style); + } + + const int sel_op_result = ED_select_op_action_deselected( + sel_op, is_select, is_inside && enclose_edge && mesh_facing); if (sel_op_result != -1) { BM_edge_select_set(em->bm, eed, sel_op_result); changed = true; @@ -303,28 +722,74 @@ static bool edbm_backbuf_check_and_select_edges(EditSelectBuf_Cache *esel, return changed; } -static bool edbm_backbuf_check_and_select_faces(EditSelectBuf_Cache *esel, +static bool edbm_backbuf_check_and_select_faces(ViewContext *vc, + EditSelectBuf_Cache *esel, Depsgraph *depsgraph, Object *ob, BMEditMesh *em, - const eSelectOp sel_op) + const eSelectOp sel_op, + const rcti *rect, + const int face_style, + void *ldata, + void *cdata) { + ToolSettings *ts = vc->scene->toolsettings; + BMIter iter, viter; BMFace *efa; - BMIter iter; + BMVert *eve; + rctf rectf; bool changed = false; - + const int style = ts->viewport_facing_select_face; + const bool check_mesh_facing = edbm_facing_viewport_precheck(ts, style, false); const BLI_bitmap *select_bitmap = esel->select_bitmap; + LassoSelectUserData *lassoData = static_cast(ldata); + CircleSelectUserData *circleData = static_cast(cdata); uint index = DRW_select_buffer_context_offset_for_object_elem(depsgraph, ob, SCE_SELECT_FACE); - if (index == 0) { + uint vindex = DRW_select_buffer_context_offset_for_object_elem(depsgraph, ob, SCE_SELECT_VERTEX); + + if (rect != NULL) { + BLI_rctf_rcti_copy(&rectf, rect); + } + + if (index == 0 || vindex == 0) { return false; } index -= 1; + vindex -= 1; BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT); const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index); - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + bool enclose_face = true; + bool center_face = true; + bool mesh_facing = true; + + if (face_style > 2 && is_inside) { + if (face_style == 4) { + if (circleData != NULL) { + enclose_face = edbm_circle_enclose_mesh(NULL, efa, circleData); + } + else { + BM_ITER_ELEM (eve, &viter, efa, BM_VERTS_OF_FACE) { + enclose_face = BLI_BITMAP_TEST_BOOL(select_bitmap, vindex + BM_elem_index_get(eve)); + if (!enclose_face) { + break; + } + } + } + } + else { + center_face = edbm_center_face(vc, efa, &rectf, lassoData, circleData); + } + } + + if (check_mesh_facing && is_inside) { + mesh_facing = edbm_facing_viewport(vc, NULL, NULL, efa, style); + } + + const int sel_op_result = ED_select_op_action_deselected( + sel_op, is_select, is_inside && enclose_face && center_face && mesh_facing); if (sel_op_result != -1) { BM_face_select_set(em->bm, efa, sel_op_result); changed = true; @@ -403,28 +868,14 @@ static bool edbm_backbuf_check_and_select_faces_obmode(Mesh *me, /** \name Lasso Select * \{ */ -struct LassoSelectUserData { - ViewContext *vc; - const rcti *rect; - const rctf *rect_fl; - rctf _rect_fl; - const int (*mcoords)[2]; - int mcoords_len; - eSelectOp sel_op; - eBezTriple_Flag select_flag; - - /* runtime */ - int pass; - bool is_done; - bool is_changed; -}; - static void view3d_userdata_lassoselect_init(LassoSelectUserData *r_data, ViewContext *vc, const rcti *rect, const int (*mcoords)[2], const int mcoords_len, - const eSelectOp sel_op) + const eSelectOp sel_op, + const int edge_style, + const int face_style) { r_data->vc = vc; @@ -437,6 +888,9 @@ static void view3d_userdata_lassoselect_init(LassoSelectUserData *r_data, r_data->sel_op = sel_op; /* SELECT by default, but can be changed if needed (only few cases use and respect this). */ r_data->select_flag = (eBezTriple_Flag)SELECT; + r_data->edge_style = edge_style; + r_data->face_style = face_style; + r_data->check_mesh_direction = false; /* runtime */ r_data->pass = 0; @@ -526,8 +980,12 @@ static void do_lasso_select_pose__do_tag(void *userData, } if (BLI_rctf_isect_segment(data->rect_fl, screen_co_a, screen_co_b) && - BLI_lasso_is_edge_inside( - data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX)) { + BLI_lasso_is_edge_inside(data->mcoords, + data->mcoords_len, + UNPACK2(screen_co_a), + UNPACK2(screen_co_b), + INT_MAX, + false)) { pchan->bone->flag |= BONE_DONE; data->is_changed = true; } @@ -551,7 +1009,7 @@ static void do_lasso_tag_pose(ViewContext *vc, BLI_lasso_boundbox(&rect, mcoords, mcoords_len); view3d_userdata_lassoselect_init( - &data, vc, &rect, mcoords, mcoords_len, static_cast(0)); + &data, vc, &rect, mcoords, mcoords_len, static_cast(0), 0, 0); ED_view3d_init_mats_rv3d(vc_tmp.obact, vc->rv3d); @@ -562,42 +1020,227 @@ static void do_lasso_tag_pose(ViewContext *vc, V3D_PROJ_TEST_CLIP_DEFAULT | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT); } +/** + * Compare result of 'GPU_select': 'GPUSelectResult', + * Needed for stable sorting, so cycling through all items near the cursor behaves predictably. + */ +static int gpu_select_buffer_depth_id_cmp(const void *sel_a_p, const void *sel_b_p) +{ + GPUSelectResult *a = (GPUSelectResult *)sel_a_p; + GPUSelectResult *b = (GPUSelectResult *)sel_b_p; + + if (a->depth < b->depth) { + return -1; + } + if (a->depth > b->depth) { + return 1; + } + + /* Depths match, sort by id. */ + uint sel_a = a->id; + uint sel_b = b->id; + +#ifdef __BIG_ENDIAN__ + BLI_endian_switch_uint32(&sel_a); + BLI_endian_switch_uint32(&sel_b); +#endif + + if (sel_a < sel_b) { + return -1; + } + if (sel_a > sel_b) { + return 1; + } + return 0; +} + static bool do_lasso_select_objects(ViewContext *vc, const int mcoords[][2], const int mcoords_len, - const eSelectOp sel_op) + const eSelectOp sel_op, + wmOperator *op, + int circle_data[3]) { View3D *v3d = vc->v3d; + int totobj = MAXPICKELEMS; /* XXX solve later */ + /* Selection buffer has bones potentially too, so we add #MAXPICKELEMS. */ + GPUSelectResult *buffer = static_cast( + MEM_mallocN((totobj + MAXPICKELEMS) * sizeof(GPUSelectResult), __func__)); + ToolSettings *ts = vc->scene->toolsettings; - bool changed = false; - if (SEL_OP_USE_PRE_DESELECT(sel_op)) { - changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d); + const int select_through_int = RNA_enum_get(op->ptr, "select_through"); + const bool select_through = U.drag_select_control & USER_DRAG_SELECT_KEYMAP ? + select_through_int == 2 || select_through_int == 8 : + circle_data == NULL ? + ts->select_through && ts->select_through_object && + ts->select_through_lasso : + ts->select_through && ts->select_through_object && + ts->select_through_circle; + float region_co[2]; + float mval_fl[2]; + if (circle_data != NULL) { + mval_fl[0] = circle_data[0]; + mval_fl[1] = circle_data[1]; } - BKE_view_layer_synced_ensure(vc->scene, vc->view_layer); - LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(vc->view_layer)) { - if (BASE_SELECTABLE(v3d, base)) { /* Use this to avoid unnecessary lasso look-ups. */ - float region_co[2]; - const bool is_select = base->flag & BASE_SELECTED; - const bool is_inside = (ED_view3d_project_base(vc->region, base, region_co) == - V3D_PROJ_RET_OK) && - BLI_lasso_is_point_inside(mcoords, - mcoords_len, - int(region_co[0]), - int(region_co[1]), - /* Dummy value. */ - INT_MAX); - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); - if (sel_op_result != -1) { - ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); - changed = true; + bool changed = false; + + if (select_through && circle_data == NULL) { + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { + changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d); + } + BKE_view_layer_synced_ensure(vc->scene, vc->view_layer); + LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(vc->view_layer)) { + if (BASE_SELECTABLE(v3d, base)) { /* Use this to avoid unnecessary lasso look-ups. */ + float region_co[2]; + const bool is_select = base->flag & BASE_SELECTED; + const bool is_inside = (ED_view3d_project_base(vc->region, base, region_co) == + V3D_PROJ_RET_OK) && + BLI_lasso_is_point_inside(mcoords, + mcoords_len, + int(region_co[0]), + int(region_co[1]), + /* Dummy value. */ + INT_MAX); + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); + changed = true; + } } } + + if (changed) { + DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT); + WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, vc->scene); + } } - if (changed) { - DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT); - WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, vc->scene); + else { + int hits = 0; + rcti rect_data; + rcti *rect = &rect_data; + blender::Vector bases; + + if (circle_data != NULL) { + int point[4][2] = {}; + + for (int i = 0; i < 4; i++) { + float angle = 6.28f * (i + 1) / 4; + point[i][0] = circle_data[0] + circle_data[2] * cosf(angle); + point[i][1] = circle_data[1] + circle_data[2] * sinf(angle); + } + rect->xmin = rect->xmax = point[0][0]; + rect->ymin = rect->ymax = point[0][1]; + uint a; + for (a = 1; a < 4; a++) { + if (point[a][0] < rect->xmin) { + rect->xmin = point[a][0]; + } + else if (point[a][0] > rect->xmax) { + rect->xmax = point[a][0]; + } + if (point[a][1] < rect->ymin) { + rect->ymin = point[a][1]; + } + else if (point[a][1] > rect->ymax) { + rect->ymax = point[a][1]; + } + } + } + else { + BLI_lasso_boundbox(rect, mcoords, mcoords_len); + } + const eV3DSelectObjectFilter select_filter = ED_view3d_select_filter_from_mode(vc->scene, + vc->obact); + if (XRAY_FLAG_ENABLED(vc->v3d) || select_through) { + hits = view3d_opengl_select( + vc, buffer, (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter); + } + else { + hits = view3d_opengl_select( + vc, buffer, (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_PICK_NEAREST, select_filter); + } + BKE_view_layer_synced_ensure(vc->scene, vc->view_layer); + LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(vc->view_layer)) { + base->object->id.tag &= ~LIB_TAG_DOIT; + } + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { + changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d); + } + + ListBase *object_bases = BKE_view_layer_object_bases_get(vc->view_layer); + if ((hits == -1) && !SEL_OP_USE_OUTSIDE(sel_op)) { + goto finally; + } + + LISTBASE_FOREACH (Base *, base, object_bases) { + if (BASE_SELECTABLE(v3d, base)) { + if ((base->object->runtime.select_id & 0x0000FFFF) != 0) { + bases.append(base); + } + } + } + + /* The draw order doesn't always match the order we populate the engine, see: T51695. */ + qsort(buffer, hits, sizeof(GPUSelectResult), gpu_select_buffer_depth_id_cmp); + + for (const GPUSelectResult *buf_iter = buffer, *buf_end = buf_iter + hits; buf_iter < buf_end; + buf_iter++) { + bPoseChannel *pchan_dummy; + Base *base = ED_armature_base_and_pchan_from_select_buffer( + bases.data(), bases.size(), buf_iter->id, &pchan_dummy); + if (base != nullptr) { + base->object->id.tag |= LIB_TAG_DOIT; + } + } + + for (Base *base = static_cast(object_bases->first); base && hits; base = base->next) { + if (BASE_SELECTABLE(v3d, base)) { + const bool is_select = base->flag & BASE_SELECTED; + bool is_inside = false; + + if (circle_data == NULL) { + is_inside = base->object->id.tag & LIB_TAG_DOIT && + (ED_view3d_project_base(vc->region, base, region_co) == V3D_PROJ_RET_OK) && + BLI_lasso_is_point_inside(mcoords, + mcoords_len, + int(region_co[0]), + int(region_co[1]), + /* Dummy value. */ + INT_MAX); + } + else { + is_inside = base->object->id.tag & LIB_TAG_DOIT ? + ts->select_origin_circle ? + (ED_view3d_project_float_global(vc->region, + base->object->object_to_world[3], + region_co, + V3D_PROJ_TEST_CLIP_DEFAULT) == + V3D_PROJ_RET_OK) ? + len_squared_v2v2(mval_fl, region_co) <= circle_data[2] * circle_data[2] : + false : + true : + false; + } + + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); + changed = true; + } + } + } + + finally: + + MEM_freeN(buffer); + + if (changed) { + DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT); + WM_event_add_notifier(vc->C, NC_SCENE | ND_OB_SELECT, vc->scene); + } } + return changed; } @@ -711,7 +1354,15 @@ static void do_lasso_select_mesh__doSelectVert(void *userData, (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && BLI_lasso_is_point_inside( data->mcoords, data->mcoords_len, screen_co[0], screen_co[1], IS_CLIPPED)); - const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + bool mesh_facing = true; + + if (is_inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, eve, NULL, NULL, data->vc->scene->toolsettings->viewport_facing_select_vert); + } + + const int sel_op_result = ED_select_op_action_deselected( + data->sel_op, is_select, is_inside && mesh_facing); if (sel_op_result != -1) { BM_vert_select_set(data->vc->em->bm, eve, sel_op_result); data->is_changed = true; @@ -732,6 +1383,7 @@ static void do_lasso_select_mesh__doSelectEdge_pass0(void *user_data, user_data); LassoSelectUserData *data = data_for_edge->data; bool is_visible = true; + bool mesh_facing = true; if (data_for_edge->backbuf_offset) { uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1; is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx); @@ -744,7 +1396,14 @@ static void do_lasso_select_mesh__doSelectEdge_pass0(void *user_data, data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), IS_CLIPPED) && BLI_lasso_is_point_inside( data->mcoords, data->mcoords_len, UNPACK2(screen_co_b), IS_CLIPPED)); - const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + + if (is_inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, eed, NULL, data->vc->scene->toolsettings->viewport_facing_select_edge); + } + + const int sel_op_result = ED_select_op_action_deselected( + data->sel_op, is_select, is_inside && mesh_facing); if (sel_op_result != -1) { BM_edge_select_set(data->vc->em->bm, eed, sel_op_result); data->is_done = true; @@ -761,6 +1420,7 @@ static void do_lasso_select_mesh__doSelectEdge_pass1(void *user_data, user_data); LassoSelectUserData *data = data_for_edge->data; bool is_visible = true; + bool mesh_facing = true; if (data_for_edge->backbuf_offset) { uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1; is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx); @@ -771,15 +1431,23 @@ static void do_lasso_select_mesh__doSelectEdge_pass1(void *user_data, data->mcoords_len, UNPACK2(screen_co_a), UNPACK2(screen_co_b), - IS_CLIPPED)); - const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + IS_CLIPPED, + false)); + + if (is_inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, eed, NULL, data->vc->scene->toolsettings->viewport_facing_select_edge); + } + + const int sel_op_result = ED_select_op_action_deselected( + data->sel_op, is_select, is_inside && mesh_facing); if (sel_op_result != -1) { BM_edge_select_set(data->vc->em->bm, eed, sel_op_result); data->is_changed = true; } } -static void do_lasso_select_mesh__doSelectFace(void *userData, +static void do_lasso_select_mesh__doSelectFaceCenter(void *userData, BMFace *efa, const float screen_co[2], int /*index*/) @@ -790,7 +1458,86 @@ static void do_lasso_select_mesh__doSelectFace(void *userData, (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && BLI_lasso_is_point_inside( data->mcoords, data->mcoords_len, screen_co[0], screen_co[1], IS_CLIPPED)); - const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + bool mesh_facing = true; + + if (is_inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, NULL, efa, data->vc->scene->toolsettings->viewport_facing_select_face); + } + + const int sel_op_result = ED_select_op_action_deselected( + data->sel_op, is_select, is_inside && mesh_facing); + if (sel_op_result != -1) { + BM_face_select_set(data->vc->em->bm, efa, sel_op_result); + data->is_changed = true; + } +} + +static void do_lasso_select_mesh__doSelectFace(void *user_data, + BMFace *efa, + const float screen_co[][2], + int total_count, + rctf *screen_rect, + bool *face_hit) +{ + LassoSelectUserData *data = static_cast(user_data); + int style = data->face_style; + + if (!BLI_rctf_isect(data->rect_fl, screen_rect, NULL)) + return; + + bool inside = false; + bool mesh_facing = true; + for (int i = 0; i < total_count; i++) { + + int a = i; + int b = (i + 1) % total_count; + + /* enclose */ + if (style == 4) { + inside = BLI_lasso_is_edge_inside(data->mcoords, + data->mcoords_len, + UNPACK2(screen_co[a]), + UNPACK2(screen_co[b]), + IS_CLIPPED, + true); + if (!inside) { + break; + } + } + /* touch */ + else { + inside = BLI_lasso_is_edge_inside(data->mcoords, + data->mcoords_len, + UNPACK2(screen_co[a]), + UNPACK2(screen_co[b]), + IS_CLIPPED, + false); + if (inside) { + break; + } + } + } + + /* touch interior of face */ + if (style == 2) { + if (!inside) { + float point[2] = {static_cast(data->mcoords[0][0]), static_cast(data->mcoords[0][1])}; + inside = isect_point_poly_v2(point, screen_co, total_count, true); + } + } + + *face_hit = inside; + + const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT); + + if (inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, NULL, efa, data->vc->scene->toolsettings->viewport_facing_select_face); + } + + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, inside && mesh_facing); + if (sel_op_result != -1) { BM_face_select_set(data->vc->em->bm, efa, sel_op_result); data->is_changed = true; @@ -801,18 +1548,23 @@ static bool do_lasso_select_mesh(ViewContext *vc, wmGenericUserData *wm_userdata, const int mcoords[][2], const int mcoords_len, - const eSelectOp sel_op) + const eSelectOp sel_op, + wmOperator *op) { LassoSelectUserData data; ToolSettings *ts = vc->scene->toolsettings; rcti rect; + int select_style[2] = {0, 0}; /* set editmesh */ vc->em = BKE_editmesh_from_object(vc->obedit); BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); + edbm_select_style(ts, select_style, false, true, op); + + view3d_userdata_lassoselect_init( + &data, vc, &rect, mcoords, mcoords_len, sel_op, select_style[0], select_style[1]); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { if (vc->em->bm->totvertsel) { @@ -826,12 +1578,25 @@ static bool do_lasso_select_mesh(ViewContext *vc, GPU_matrix_set(vc->rv3d->viewmat); - const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d); + const int select_through_int = RNA_enum_get(op->ptr, "select_through"); + const bool select_through = U.drag_select_control & USER_DRAG_SELECT_KEYMAP ? + select_through_int > 2 : + ts->select_through && ts->select_through_edit && + ts->select_through_lasso; + const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d) && !select_through; EditSelectBuf_Cache *esel = static_cast(wm_userdata->data); if (use_zbuf) { if (wm_userdata->data == nullptr) { - editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, ts->selectmode); + /* for near enclose face */ + if (data.face_style == 4 && ts->selectmode & SCE_SELECT_FACE && + !(ts->selectmode & SCE_SELECT_VERTEX)) { + editselect_buf_cache_init_with_generic_userdata( + wm_userdata, vc, ts->selectmode | SCE_SELECT_VERTEX); + } + else { + editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, ts->selectmode); + } esel = static_cast(wm_userdata->data); esel->select_bitmap = DRW_select_buffer_bitmap_from_poly( vc->depsgraph, vc->region, vc->v3d, mcoords, mcoords_len, &rect, nullptr); @@ -841,9 +1606,12 @@ static bool do_lasso_select_mesh(ViewContext *vc, if (ts->selectmode & SCE_SELECT_VERTEX) { if (use_zbuf) { data.is_changed |= edbm_backbuf_check_and_select_verts( - esel, vc->depsgraph, vc->obedit, vc->em, sel_op); + vc, esel, vc->depsgraph, vc->obedit, vc->em, sel_op); } else { + data.check_mesh_direction = edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_vert, true); + mesh_foreachScreenVert( vc, do_lasso_select_mesh__doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT); } @@ -859,12 +1627,20 @@ static bool do_lasso_select_mesh(ViewContext *vc, const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_NEAR | (use_zbuf ? (eV3DProjTest)0 : V3D_PROJ_TEST_CLIP_BB); - /* Fully inside. */ - mesh_foreachScreenEdge_clip_bb_segment( - vc, do_lasso_select_mesh__doSelectEdge_pass0, &data_for_edge, clip_flag); - if (data.is_done == false) { - /* Fall back to partially inside. - * Clip content to account for edges partially behind the view. */ + + data.check_mesh_direction = use_zbuf ? + edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_edge, false) : + data.check_mesh_direction = edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_edge, true); + + /* Fully inside, hybrid and enclose edge */ + if (data.edge_style != 2) { + mesh_foreachScreenEdge_clip_bb_segment( + vc, do_lasso_select_mesh__doSelectEdge_pass0, &data_for_edge, clip_flag); + } + /* Partially inside, hybrid and touch edge */ + if (data.edge_style == 2 || data.edge_style == 1 && data.is_done == false) { mesh_foreachScreenEdge_clip_bb_segment(vc, do_lasso_select_mesh__doSelectEdge_pass1, &data_for_edge, @@ -874,12 +1650,33 @@ static bool do_lasso_select_mesh(ViewContext *vc, if (ts->selectmode & SCE_SELECT_FACE) { if (use_zbuf) { - data.is_changed |= edbm_backbuf_check_and_select_faces( - esel, vc->depsgraph, vc->obedit, vc->em, sel_op); + data.is_changed |= edbm_backbuf_check_and_select_faces(vc, + esel, + vc->depsgraph, + vc->obedit, + vc->em, + sel_op, + &rect, + data.face_style, + &data, + NULL); } else { - mesh_foreachScreenFace( - vc, do_lasso_select_mesh__doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + data.check_mesh_direction = edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_face, true); + + /* xray center and auto face with fallback for touch and enclose intersect */ + if (data.face_style == 1 || data.face_style == 8 || SEL_OP_USE_OUTSIDE(sel_op)) { + mesh_foreachScreenFaceCenter( + vc, do_lasso_select_mesh__doSelectFaceCenter, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + } + /* xray touch and enclose face - doesn't work for intersect */ + else { + mesh_foreachScreenFaceVerts(vc, + do_lasso_select_mesh__doSelectFace, + &data, + V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + } } } @@ -943,7 +1740,7 @@ static bool do_lasso_select_curve(ViewContext *vc, BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op, 0, 0); Curve *curve = (Curve *)vc->obedit->data; ListBase *nurbs = BKE_curve_editNurbs_get(curve); @@ -992,7 +1789,7 @@ static bool do_lasso_select_lattice(ViewContext *vc, BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_lattice_flags_set(vc->obedit, 0); @@ -1021,7 +1818,8 @@ static void do_lasso_select_armature__doSelectBone(void *userData, if (screen_co_a[0] != IS_CLIPPED) { if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_a)) && BLI_lasso_is_point_inside( - data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), INT_MAX)) { + data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), INT_MAX), + false) { is_inside_flag |= BONESEL_ROOT; } } @@ -1032,7 +1830,8 @@ static void do_lasso_select_armature__doSelectBone(void *userData, if (screen_co_b[0] != IS_CLIPPED) { if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_b)) && BLI_lasso_is_point_inside( - data->mcoords, data->mcoords_len, UNPACK2(screen_co_b), INT_MAX)) { + data->mcoords, data->mcoords_len, UNPACK2(screen_co_b), INT_MAX), + false) { is_inside_flag |= BONESEL_TIP; } } @@ -1046,7 +1845,8 @@ static void do_lasso_select_armature__doSelectBone(void *userData, data->mcoords_len, UNPACK2(screen_co_a), UNPACK2(screen_co_b), - INT_MAX)) { + INT_MAX, + false)) { is_inside_flag |= BONESEL_BONE; } } @@ -1075,7 +1875,7 @@ static void do_lasso_select_armature__doSelectBone_clip_content(void *userData, } if (BLI_lasso_is_edge_inside( - data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX)) { + data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX, false)) { is_inside_flag |= BONESEL_BONE; } @@ -1092,7 +1892,7 @@ static bool do_lasso_select_armature(ViewContext *vc, BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_armature_edit_deselect_all_visible(vc->obedit); @@ -1152,7 +1952,7 @@ static bool do_lasso_select_meta(ViewContext *vc, BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= BKE_mball_deselect_all(mb); @@ -1236,7 +2036,8 @@ static bool do_lasso_select_paintvert(ViewContext *vc, LassoSelectUserData_ForMeshVert data; data.select_vert = select_vert.span; - view3d_userdata_lassoselect_init(&data.lasso_data, vc, &rect, mcoords, mcoords_len, sel_op); + view3d_userdata_lassoselect_init( + &data.lasso_data, vc, &rect, mcoords, mcoords_len, sel_op, 0, 0); ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); @@ -1301,7 +2102,8 @@ static bool view3d_lasso_select(bContext *C, ViewContext *vc, const int mcoords[][2], const int mcoords_len, - const eSelectOp sel_op) + const eSelectOp sel_op, + wmOperator *op) { using namespace blender; Object *ob = CTX_data_active_object(C); @@ -1331,7 +2133,7 @@ static bool view3d_lasso_select(bContext *C, } } else { - changed_multi |= do_lasso_select_objects(vc, mcoords, mcoords_len, sel_op); + changed_multi |= do_lasso_select_objects(vc, mcoords, mcoords_len, sel_op, op, NULL); if (changed_multi) { ED_outliner_select_sync_from_object_tag(C); } @@ -1345,7 +2147,7 @@ static bool view3d_lasso_select(bContext *C, switch (vc->obedit->type) { case OB_MESH: - changed = do_lasso_select_mesh(vc, wm_userdata, mcoords, mcoords_len, sel_op); + changed = do_lasso_select_mesh(vc, wm_userdata, mcoords, mcoords_len, sel_op, op); break; case OB_CURVES_LEGACY: case OB_SURF: @@ -1416,7 +2218,7 @@ static int view3d_lasso_select_exec(bContext *C, wmOperator *op) ED_view3d_viewcontext_init(C, &vc, depsgraph); eSelectOp sel_op = static_cast(RNA_enum_get(op->ptr, "mode")); - bool changed_multi = view3d_lasso_select(C, &vc, mcoords, mcoords_len, sel_op); + bool changed_multi = view3d_lasso_select(C, &vc, mcoords, mcoords_len, sel_op, op); MEM_freeN((void *)mcoords); @@ -2139,40 +2941,6 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc, return hits; } -/** - * Compare result of 'GPU_select': 'GPUSelectResult', - * Needed for stable sorting, so cycling through all items near the cursor behaves predictably. - */ -static int gpu_select_buffer_depth_id_cmp(const void *sel_a_p, const void *sel_b_p) -{ - GPUSelectResult *a = (GPUSelectResult *)sel_a_p; - GPUSelectResult *b = (GPUSelectResult *)sel_b_p; - - if (a->depth < b->depth) { - return -1; - } - if (a->depth > b->depth) { - return 1; - } - - /* Depths match, sort by id. */ - uint sel_a = a->id; - uint sel_b = b->id; - -#ifdef __BIG_ENDIAN__ - BLI_endian_switch_uint32(&sel_a); - BLI_endian_switch_uint32(&sel_b); -#endif - - if (sel_a < sel_b) { - return -1; - } - if (sel_a > sel_b) { - return 1; - } - return 0; -} - /** * \param has_bones: When true, skip non-bone hits, also allow bases to be used * that are visible but not select-able, @@ -3224,23 +3992,12 @@ void VIEW3D_OT_select(wmOperatorType *ot) /** \name Box Select * \{ */ -struct BoxSelectUserData { - ViewContext *vc; - const rcti *rect; - const rctf *rect_fl; - rctf _rect_fl; - eSelectOp sel_op; - eBezTriple_Flag select_flag; - - /* runtime */ - bool is_done; - bool is_changed; -}; - static void view3d_userdata_boxselect_init(BoxSelectUserData *r_data, ViewContext *vc, const rcti *rect, - const eSelectOp sel_op) + const eSelectOp sel_op, + const int edge_style, + const int face_style) { r_data->vc = vc; @@ -3251,6 +4008,9 @@ static void view3d_userdata_boxselect_init(BoxSelectUserData *r_data, r_data->sel_op = sel_op; /* SELECT by default, but can be changed if needed (only few cases use and respect this). */ r_data->select_flag = (eBezTriple_Flag)SELECT; + r_data->edge_style = edge_style; + r_data->face_style = face_style; + r_data->check_mesh_direction = false; /* runtime */ r_data->is_done = false; @@ -3326,7 +4086,7 @@ static bool do_paintvert_box_select(ViewContext *vc, BoxSelectUserData_ForMeshVert data; data.select_vert = select_vert.span; - view3d_userdata_boxselect_init(&data.box_data, vc, rect, sel_op); + view3d_userdata_boxselect_init(&data.box_data, vc, rect, sel_op, 0, 0); ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); @@ -3432,7 +4192,7 @@ static bool do_nurbs_box_select(ViewContext *vc, rcti *rect, const eSelectOp sel const bool deselect_all = (sel_op == SEL_OP_SET); BoxSelectUserData data; - view3d_userdata_boxselect_init(&data, vc, rect, sel_op); + view3d_userdata_boxselect_init(&data, vc, rect, sel_op, 0, 0); Curve *curve = (Curve *)vc->obedit->data; ListBase *nurbs = BKE_curve_editNurbs_get(curve); @@ -3471,7 +4231,7 @@ static bool do_lattice_box_select(ViewContext *vc, rcti *rect, const eSelectOp s { BoxSelectUserData data; - view3d_userdata_boxselect_init(&data, vc, rect, sel_op); + view3d_userdata_boxselect_init(&data, vc, rect, sel_op, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_lattice_flags_set(vc->obedit, 0); @@ -3492,7 +4252,15 @@ static void do_mesh_box_select__doSelectVert(void *userData, BoxSelectUserData *data = static_cast(userData); const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT); const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co); - const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + + bool mesh_facing = true; + if (is_inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, eve, NULL, NULL, data->vc->scene->toolsettings->viewport_facing_select_vert); + } + + const int sel_op_result = ED_select_op_action_deselected( + data->sel_op, is_select, is_inside && mesh_facing); if (sel_op_result != -1) { BM_vert_select_set(data->vc->em->bm, eve, sel_op_result); data->is_changed = true; @@ -3513,6 +4281,7 @@ static void do_mesh_box_select__doSelectEdge_pass0( userData); BoxSelectUserData *data = data_for_edge->data; bool is_visible = true; + bool mesh_facing = true; if (data_for_edge->backbuf_offset) { uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1; is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx); @@ -3521,7 +4290,14 @@ static void do_mesh_box_select__doSelectEdge_pass0( const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); const bool is_inside = (is_visible && edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b)); - const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + + if (is_inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, eed, NULL, data->vc->scene->toolsettings->viewport_facing_select_edge); + } + + const int sel_op_result = ED_select_op_action_deselected( + data->sel_op, is_select, is_inside && mesh_facing); if (sel_op_result != -1) { BM_edge_select_set(data->vc->em->bm, eed, sel_op_result); data->is_done = true; @@ -3538,6 +4314,7 @@ static void do_mesh_box_select__doSelectEdge_pass1( userData); BoxSelectUserData *data = data_for_edge->data; bool is_visible = true; + bool mesh_facing = true; if (data_for_edge->backbuf_offset) { uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1; is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx); @@ -3545,13 +4322,79 @@ static void do_mesh_box_select__doSelectEdge_pass1( const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); const bool is_inside = (is_visible && edge_inside_rect(data->rect_fl, screen_co_a, screen_co_b)); - const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + + if (is_inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, eed, NULL, data->vc->scene->toolsettings->viewport_facing_select_edge); + } + + const int sel_op_result = ED_select_op_action_deselected( + data->sel_op, is_select, is_inside && mesh_facing); if (sel_op_result != -1) { BM_edge_select_set(data->vc->em->bm, eed, sel_op_result); data->is_changed = true; } } + static void do_mesh_box_select__doSelectFace(void *userData, + BMFace *efa, + const float screen_co[][2], + int total_count, + rctf *screen_rect, + bool *face_hit) +{ + BoxSelectUserData *data = static_cast(userData); + + if (!BLI_rctf_isect(data->rect_fl, screen_rect, NULL)) + return; + + bool inside = false; + bool mesh_facing = true; + for (int i = 0; i < total_count; i++) { + + int a = i; + int b = (i + 1) % total_count; + + /* enclose */ + if (data->face_style == 4) { + inside = edge_fully_inside_rect(data->rect_fl, screen_co[a], screen_co[b]); + if (!inside) { + break; + } + } + /* touch */ + else { + inside = edge_inside_rect(data->rect_fl, screen_co[a], screen_co[b]); + if (inside) { + break; + } + } + } + + /* touch interior of face */ + if (data->face_style == 2 && !inside) { + float point[2] = {data->rect_fl->xmax, data->rect_fl->ymax}; + inside = isect_point_poly_v2(point, screen_co, total_count, true); + } + + *face_hit = inside; + + const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT); + + if (inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, NULL, efa, data->vc->scene->toolsettings->viewport_facing_select_face); + } + + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, inside && mesh_facing); + + if (sel_op_result != -1) { + BM_face_select_set(data->vc->em->bm, efa, sel_op_result); + data->is_changed = true; + } +} + +static void do_mesh_box_select__doSelectFaceCenter(void *userData, BMFace *efa, const float screen_co[2], int /*index*/) @@ -3559,7 +4402,15 @@ static void do_mesh_box_select__doSelectFace(void *userData, BoxSelectUserData *data = static_cast(userData); const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT); const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co); - const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + bool mesh_facing = true; + + if (is_inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, NULL, efa, data->vc->scene->toolsettings->viewport_facing_select_face); + } + + const int sel_op_result = ED_select_op_action_deselected( + data->sel_op, is_select, is_inside && mesh_facing); if (sel_op_result != -1) { BM_face_select_set(data->vc->em->bm, efa, sel_op_result); data->is_changed = true; @@ -3568,12 +4419,16 @@ static void do_mesh_box_select__doSelectFace(void *userData, static bool do_mesh_box_select(ViewContext *vc, wmGenericUserData *wm_userdata, const rcti *rect, - const eSelectOp sel_op) + const eSelectOp sel_op, + wmOperator *op) { BoxSelectUserData data; ToolSettings *ts = vc->scene->toolsettings; + int select_style[2] = {0, 0}; - view3d_userdata_boxselect_init(&data, vc, rect, sel_op); + edbm_select_style(ts, select_style, true, false, op); + + view3d_userdata_boxselect_init(&data, vc, rect, sel_op, select_style[0], select_style[1]); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { if (vc->em->bm->totvertsel) { @@ -3587,12 +4442,25 @@ static bool do_mesh_box_select(ViewContext *vc, GPU_matrix_set(vc->rv3d->viewmat); - const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d); + const int select_through_int = RNA_enum_get(op->ptr, "select_through"); + const bool select_through = U.drag_select_control & USER_DRAG_SELECT_KEYMAP ? + select_through_int > 2 : + ts->select_through && ts->select_through_edit && + ts->select_through_box; + const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d) && !select_through; EditSelectBuf_Cache *esel = static_cast(wm_userdata->data); if (use_zbuf) { if (wm_userdata->data == nullptr) { - editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, ts->selectmode); + /* for near enclose face */ + if (data.face_style == 4 && ts->selectmode & SCE_SELECT_FACE && + !(ts->selectmode & SCE_SELECT_VERTEX)) { + editselect_buf_cache_init_with_generic_userdata( + wm_userdata, vc, ts->selectmode | SCE_SELECT_VERTEX); + } + else { + editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, ts->selectmode); + } esel = static_cast(wm_userdata->data); esel->select_bitmap = DRW_select_buffer_bitmap_from_rect( vc->depsgraph, vc->region, vc->v3d, rect, nullptr); @@ -3602,9 +4470,12 @@ static bool do_mesh_box_select(ViewContext *vc, if (ts->selectmode & SCE_SELECT_VERTEX) { if (use_zbuf) { data.is_changed |= edbm_backbuf_check_and_select_verts( - esel, vc->depsgraph, vc->obedit, vc->em, sel_op); + vc, esel, vc->depsgraph, vc->obedit, vc->em, sel_op); } else { + data.check_mesh_direction = edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_vert, true); + mesh_foreachScreenVert( vc, do_mesh_box_select__doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT); } @@ -3621,12 +4492,19 @@ static bool do_mesh_box_select(ViewContext *vc, const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_NEAR | (use_zbuf ? (eV3DProjTest)0 : V3D_PROJ_TEST_CLIP_BB); - /* Fully inside. */ - mesh_foreachScreenEdge_clip_bb_segment( - vc, do_mesh_box_select__doSelectEdge_pass0, &cb_data, clip_flag); - if (data.is_done == false) { - /* Fall back to partially inside. - * Clip content to account for edges partially behind the view. */ + + data.check_mesh_direction = use_zbuf ? + edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_edge, false) : + data.check_mesh_direction = edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_edge, true); + /* Fully inside, hybrid and enclose edge */ + if (data.edge_style != 2) { + mesh_foreachScreenEdge_clip_bb_segment( + vc, do_mesh_box_select__doSelectEdge_pass0, &cb_data, clip_flag); + } + /* Partially inside, hybrid and touch edge */ + if (data.edge_style == 2 || data.edge_style == 1 && data.is_done == false) { mesh_foreachScreenEdge_clip_bb_segment(vc, do_mesh_box_select__doSelectEdge_pass1, &cb_data, @@ -3637,11 +4515,24 @@ static bool do_mesh_box_select(ViewContext *vc, if (ts->selectmode & SCE_SELECT_FACE) { if (use_zbuf) { data.is_changed |= edbm_backbuf_check_and_select_faces( - esel, vc->depsgraph, vc->obedit, vc->em, sel_op); + vc, esel, vc->depsgraph, vc->obedit, vc->em, sel_op, rect, data.face_style, NULL, NULL); } else { - mesh_foreachScreenFace( - vc, do_mesh_box_select__doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + data.check_mesh_direction = edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_face, true); + + /* xray center and auto face with fallback for touch and enclose intersect */ + if (data.face_style == 1 || data.face_style == 8 || SEL_OP_USE_OUTSIDE(sel_op)) { + mesh_foreachScreenFaceCenter( + vc, do_mesh_box_select__doSelectFaceCenter, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + } + /* xray touch and enclose face - doesn't work with intersect */ + else { + mesh_foreachScreenFaceVerts(vc, + do_mesh_box_select__doSelectFace, + &data, + V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + } } } @@ -3802,18 +4693,39 @@ static int opengl_bone_select_buffer_cmp(const void *sel_a_p, const void *sel_b_ return 0; } -static bool do_object_box_select(bContext *C, ViewContext *vc, rcti *rect, const eSelectOp sel_op) +static bool do_object_box_select( + bContext *C, ViewContext *vc, rcti *rect, const eSelectOp sel_op, wmOperator *op) { View3D *v3d = vc->v3d; int totobj = MAXPICKELEMS; /* XXX solve later */ + ToolSettings *ts = vc->scene->toolsettings; + rctf rect_f_data; + rctf *rect_f = &rect_f_data; + BLI_rctf_rcti_copy(rect_f, rect); + float region_co[2]; /* Selection buffer has bones potentially too, so we add #MAXPICKELEMS. */ GPUSelectResult *buffer = static_cast( MEM_mallocN((totobj + MAXPICKELEMS) * sizeof(GPUSelectResult), __func__)); const eV3DSelectObjectFilter select_filter = ED_view3d_select_filter_from_mode(vc->scene, vc->obact); - const int hits = view3d_opengl_select( - vc, buffer, (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter); + const bool select_origin = U.drag_select_control & USER_DRAG_SELECT_KEYMAP ? + RNA_boolean_get(op->ptr, "select_origin_box") : + ts->select_origin_box; + const bool select_through_int = RNA_enum_get(op->ptr, "select_through"); + const bool select_through = U.drag_select_control & USER_DRAG_SELECT_KEYMAP ? + select_through_int == 2 || select_through_int == 8 : + ts->select_through && ts->select_through_object && + ts->select_through_box; + int hits = 0; + if (XRAY_FLAG_ENABLED(vc->v3d) || select_through) { + hits = view3d_opengl_select( + vc, buffer, (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter); + } + else { + hits = view3d_opengl_select( + vc, buffer, (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_PICK_NEAREST, select_filter); + } BKE_view_layer_synced_ensure(vc->scene, vc->view_layer); LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(vc->view_layer)) { base->object->id.tag &= ~LIB_TAG_DOIT; @@ -3855,7 +4767,13 @@ static bool do_object_box_select(bContext *C, ViewContext *vc, rcti *rect, const for (Base *base = static_cast(object_bases->first); base && hits; base = base->next) { if (BASE_SELECTABLE(v3d, base)) { const bool is_select = base->flag & BASE_SELECTED; - const bool is_inside = base->object->id.tag & LIB_TAG_DOIT; + const bool is_inside = base->object->id.tag & LIB_TAG_DOIT ? + select_origin ? + (ED_view3d_project_base(vc->region, base, region_co) == + V3D_PROJ_RET_OK) && + BLI_rctf_isect_pt_v(rect_f, region_co) : + true : + false; const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); if (sel_op_result != -1) { ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); @@ -3981,7 +4899,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) switch (vc.obedit->type) { case OB_MESH: vc.em = BKE_editmesh_from_object(vc.obedit); - changed = do_mesh_box_select(&vc, wm_userdata, &rect, sel_op); + changed = do_mesh_box_select(&vc, wm_userdata, &rect, sel_op, op); if (changed) { DEG_id_tag_update(static_cast(vc.obedit->data), ID_RECALC_SELECT); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); @@ -4055,7 +4973,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) } } else { /* object mode with none active */ - changed_multi = do_object_box_select(C, &vc, &rect, sel_op); + changed_multi = do_object_box_select(C, &vc, &rect, sel_op, op); if (changed_multi) { ED_outliner_select_sync_from_object_tag(C); } @@ -4098,24 +5016,13 @@ void VIEW3D_OT_select_box(wmOperatorType *ot) /** \name Circle Select * \{ */ -struct CircleSelectUserData { - ViewContext *vc; - bool select; - int mval[2]; - float mval_fl[2]; - float radius; - float radius_squared; - eBezTriple_Flag select_flag; - - /* runtime */ - bool is_changed; -}; - static void view3d_userdata_circleselect_init(CircleSelectUserData *r_data, ViewContext *vc, const bool select, const int mval[2], - const float rad) + const float rad, + const int edge_style, + const int face_style) { r_data->vc = vc; r_data->select = select; @@ -4128,6 +5035,9 @@ static void view3d_userdata_circleselect_init(CircleSelectUserData *r_data, /* SELECT by default, but can be changed if needed (only few cases use and respect this). */ r_data->select_flag = (eBezTriple_Flag)SELECT; + r_data->edge_style = edge_style; + r_data->face_style = face_style; + r_data->check_mesh_direction = false; /* runtime */ r_data->is_changed = false; @@ -4141,8 +5051,16 @@ static void mesh_circle_doSelectVert(void *userData, CircleSelectUserData *data = static_cast(userData); if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) { - BM_vert_select_set(data->vc->em->bm, eve, data->select); + bool mesh_facing = true; data->is_changed = true; + if (data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, eve, NULL, NULL, data->vc->scene->toolsettings->viewport_facing_select_vert); + } + if (mesh_facing) { + BM_vert_select_set(data->vc->em->bm, eve, data->select); + data->is_changed = true; + } } } static void mesh_circle_doSelectEdge(void *userData, @@ -4152,22 +5070,91 @@ static void mesh_circle_doSelectEdge(void *userData, int /*index*/) { CircleSelectUserData *data = static_cast(userData); - + + bool enclose_edge = true; + bool mesh_facing = true; if (edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) { - BM_edge_select_set(data->vc->em->bm, eed, data->select); + if (data->edge_style == 4) { + enclose_edge = edbm_circle_enclose_mesh(eed, NULL, data); + } + + if (data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, eed, NULL, data->vc->scene->toolsettings->viewport_facing_select_edge); + } + + if (enclose_edge && mesh_facing) { + BM_edge_select_set(data->vc->em->bm, eed, data->select); + data->is_changed = true; + } + } +} + +static void mesh_circle_doSelectFace(void *userData, + BMFace *efa, + const float screen_co[][2], + int total_count, + rctf *screen_rect, + bool *face_hit) +{ + CircleSelectUserData *data = static_cast(userData); + + if (!BLI_rctf_isect_circle(screen_rect, data->mval_fl, data->radius)) { + return; + } + + bool inside = false; + bool enclose_face = true; + bool mesh_facing = true; + for (int i = 0; i < total_count; i++) { + + int a = i; + int b = (i + 1) % total_count; + + inside = edge_inside_circle(data->mval_fl, data->radius, screen_co[a], screen_co[b]); + if (inside) + break; + } + + if (!inside) { + inside = isect_point_poly_v2(data->mval_fl, screen_co, total_count, true); + } + + *face_hit = inside; + + if (data->face_style == 4 && inside) { + enclose_face = edbm_circle_enclose_mesh(NULL, efa, data); + } + + if (data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, NULL, efa, data->vc->scene->toolsettings->viewport_facing_select_face); + } + + if (inside && enclose_face && mesh_facing) { + BM_face_select_set(data->vc->em->bm, efa, data->select); data->is_changed = true; } } -static void mesh_circle_doSelectFace(void *userData, + +static void mesh_circle_doSelectFaceCenter(void *userData, BMFace *efa, const float screen_co[2], int /*index*/) { CircleSelectUserData *data = static_cast(userData); + bool mesh_facing = true; if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) { - BM_face_select_set(data->vc->em->bm, efa, data->select); - data->is_changed = true; + if (data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, NULL, efa, data->vc->scene->toolsettings->viewport_facing_select_face); + } + + if (mesh_facing) { + BM_face_select_set(data->vc->em->bm, efa, data->select); + data->is_changed = true; + } } } @@ -4175,10 +5162,12 @@ static bool mesh_circle_select(ViewContext *vc, wmGenericUserData *wm_userdata, eSelectOp sel_op, const int mval[2], - float rad) + float rad, + wmOperator *op) { ToolSettings *ts = vc->scene->toolsettings; CircleSelectUserData data; + int select_style[2] = {0, 0}; vc->em = BKE_editmesh_from_object(vc->obedit); bool changed = false; @@ -4195,9 +5184,16 @@ static bool mesh_circle_select(ViewContext *vc, ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */ - view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + edbm_select_style(ts, select_style, false, false, op); - const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d); + view3d_userdata_circleselect_init(&data, vc, select, mval, rad, select_style[0], select_style[1]); + + const int select_through_int = RNA_enum_get(op->ptr, "select_through"); + const bool select_through = U.drag_select_control & USER_DRAG_SELECT_KEYMAP ? + select_through_int > 2 : + ts->select_through && ts->select_through_edit && + ts->select_through_circle; + const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d) && !select_through; if (use_zbuf) { if (wm_userdata->data == nullptr) { @@ -4217,10 +5213,13 @@ static bool mesh_circle_select(ViewContext *vc, if (use_zbuf) { if (esel->select_bitmap != nullptr) { changed |= edbm_backbuf_check_and_select_verts( - esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); + vc, esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); } } else { + data.check_mesh_direction = edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_vert, true); + mesh_foreachScreenVert(vc, mesh_circle_doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT); } } @@ -4229,10 +5228,13 @@ static bool mesh_circle_select(ViewContext *vc, if (use_zbuf) { if (esel->select_bitmap != nullptr) { changed |= edbm_backbuf_check_and_select_edges( - esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); + &data, esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); } } else { + data.check_mesh_direction = edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_edge, true); + mesh_foreachScreenEdge_clip_bb_segment( vc, mesh_circle_doSelectEdge, @@ -4243,13 +5245,33 @@ static bool mesh_circle_select(ViewContext *vc, if (ts->selectmode & SCE_SELECT_FACE) { if (use_zbuf) { - if (esel->select_bitmap != nullptr) { - changed |= edbm_backbuf_check_and_select_faces( - esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); + if (esel->select_bitmap != NULL) { + changed |= edbm_backbuf_check_and_select_faces(vc, + esel, + vc->depsgraph, + vc->obedit, + vc->em, + select ? SEL_OP_ADD : SEL_OP_SUB, + NULL, + data.face_style, + NULL, + &data); } } else { - mesh_foreachScreenFace(vc, mesh_circle_doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + data.check_mesh_direction = edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_face, true); + + /* xray center and auto face */ + if (data.face_style == 1 || data.face_style == 8) { + mesh_foreachScreenFaceCenter( + vc, mesh_circle_doSelectFaceCenter, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + } + /* xray touch and enclose face */ + else { + mesh_foreachScreenFaceVerts( + vc, mesh_circle_doSelectFace, &data, V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + } } } @@ -4363,7 +5385,7 @@ static bool paint_vertsel_circle_select(ViewContext *vc, ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); /* for foreach's screen/vert projection */ - view3d_userdata_circleselect_init(&data.circle_data, vc, select, mval, rad); + view3d_userdata_circleselect_init(&data.circle_data, vc, select, mval, rad, 0, 0); meshobject_foreachScreenVert( vc, paint_vertsel_circle_select_doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT); changed |= data.circle_data.is_changed; @@ -4417,7 +5439,7 @@ static bool nurbscurve_circle_select(ViewContext *vc, const bool deselect_all = (sel_op == SEL_OP_SET); CircleSelectUserData data; - view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + view3d_userdata_circleselect_init(&data, vc, select, mval, rad, 0, 0); Curve *curve = (Curve *)vc->obedit->data; ListBase *nurbs = BKE_curve_editNurbs_get(curve); @@ -4458,7 +5480,7 @@ static bool lattice_circle_select(ViewContext *vc, CircleSelectUserData data; const bool select = (sel_op != SEL_OP_SUB); - view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + view3d_userdata_circleselect_init(&data, vc, select, mval, rad, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_lattice_flags_set(vc->obedit, 0); @@ -4549,7 +5571,7 @@ static bool pose_circle_select(ViewContext *vc, CircleSelectUserData data; const bool select = (sel_op != SEL_OP_SUB); - view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + view3d_userdata_circleselect_init(&data, vc, select, mval, rad, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_pose_deselect_all(vc->obact, SEL_DESELECT, false); @@ -4686,7 +5708,7 @@ static bool armature_circle_select(ViewContext *vc, const bool select = (sel_op != SEL_OP_SUB); - view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + view3d_userdata_circleselect_init(&data, vc, select, mval, rad, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_armature_edit_deselect_all_visible(vc->obedit); @@ -4739,7 +5761,7 @@ static bool mball_circle_select(ViewContext *vc, const bool select = (sel_op != SEL_OP_SUB); - view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + view3d_userdata_circleselect_init(&data, vc, select, mval, rad, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= BKE_mball_deselect_all(static_cast(vc->obedit->data)); @@ -4760,14 +5782,15 @@ static bool obedit_circle_select(bContext *C, wmGenericUserData *wm_userdata, const eSelectOp sel_op, const int mval[2], - float rad) + float rad, + wmOperator *op) { using namespace blender; bool changed = false; BLI_assert(ELEM(sel_op, SEL_OP_SET, SEL_OP_ADD, SEL_OP_SUB)); switch (vc->obedit->type) { case OB_MESH: - changed = mesh_circle_select(vc, wm_userdata, sel_op, mval, rad); + changed = mesh_circle_select(vc, wm_userdata, sel_op, mval, rad, op); break; case OB_CURVES_LEGACY: case OB_SURF: @@ -4908,7 +5931,6 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) const eSelectOp sel_op = ED_select_op_modal( static_cast(RNA_enum_get(op->ptr, "mode")), WM_gesture_is_modal_first(gesture)); - ED_view3d_viewcontext_init(C, &vc, depsgraph); Object *obact = vc.obact; @@ -4928,7 +5950,7 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) obedit = vc.obedit; if (obedit) { - obedit_circle_select(C, &vc, wm_userdata, sel_op, mval, float(radius)); + obedit_circle_select(C, &vc, wm_userdata, sel_op, mval, float(radius), op); } else if (BKE_paint_select_face_test(obact)) { paint_facesel_circle_select(&vc, wm_userdata, sel_op, mval, float(radius)); @@ -4956,11 +5978,27 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } else { - if (object_circle_select(&vc, sel_op, mval, float(radius))) { - DEG_id_tag_update(&vc.scene->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc.scene); + ToolSettings *ts = vc.scene->toolsettings; + const int select_through_int = RNA_enum_get(op->ptr, "select_through"); + const bool default_object_select = U.drag_select_control & USER_DRAG_SELECT_KEYMAP ? + RNA_boolean_get(op->ptr, "select_origin_circle") && + (select_through_int == 2 || + select_through_int == 8) : + ts->select_through && ts->select_through_object && + ts->select_through_circle && + ts->select_origin_circle; + if (default_object_select) { + if (object_circle_select(&vc, sel_op, mval, float(radius))) { + DEG_id_tag_update(&vc.scene->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc.scene); - ED_outliner_select_sync_from_object_tag(C); + ED_outliner_select_sync_from_object_tag(C); + } + } + else { + BKE_object_update_select_id(CTX_data_main(C)); + int circle_data[3] = {mval[0], mval[1], radius}; + do_lasso_select_objects(&vc, NULL, NULL, sel_op, op, circle_data); } } diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index 0cf9d5fa309..5a067f0f7f9 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -516,7 +516,8 @@ short ED_transform_calc_orientation_from_type_ex(const Scene *scene, } case V3D_ORIENT_NORMAL: { if (obedit || (ob && ob->mode & OB_MODE_POSE)) { - ED_getTransformOrientationMatrix(scene, view_layer, v3d, ob, obedit, pivot_point, r_mat); + ED_getTransformOrientationMatrix( + scene, view_layer, v3d, ob, obedit, pivot_point, r_mat, NULL, NULL, NULL); break; } /* No break we define 'normal' as 'local' in Object mode. */ @@ -529,7 +530,8 @@ short ED_transform_calc_orientation_from_type_ex(const Scene *scene, * use the active pones axis for display #33575, this works as expected on a single * bone and users who select many bones will understand what's going on and what local * means when they start transforming. */ - ED_getTransformOrientationMatrix(scene, view_layer, v3d, ob, obedit, pivot_point, r_mat); + ED_getTransformOrientationMatrix( + scene, view_layer, v3d, ob, obedit, pivot_point, r_mat, NULL, NULL, NULL); } else { transform_orientations_create_from_axis(r_mat, UNPACK3(ob->object_to_world)); @@ -752,13 +754,18 @@ int getTransformOrientation_ex(const Scene *scene, struct Object *obedit, float normal[3], float plane[3], - const short around) + const short around, + struct BMVert *eve, + struct BMEdge *eed, + struct BMFace *efa) { int result = ORIENTATION_NONE; const bool activeOnly = (around == V3D_AROUND_ACTIVE); - zero_v3(normal); - zero_v3(plane); + if (efa == NULL && eed == NULL) { + zero_v3(normal); + zero_v3(plane); + } if (obedit) { float imat[3][3], mat[3][3]; @@ -777,20 +784,72 @@ int getTransformOrientation_ex(const Scene *scene, float vec[3] = {0, 0, 0}; /* USE LAST SELECTED WITH ACTIVE */ - if (activeOnly && BM_select_history_active_get(em->bm, &ese)) { - BM_editselection_normal(&ese, normal); - BM_editselection_plane(&ese, plane); + if (efa != NULL || eed != NULL || eve != NULL || + activeOnly && BM_select_history_active_get(em->bm, &ese)) { + if (efa != NULL) { + copy_v3_v3(normal, efa->no); + BM_face_calc_tangent_auto(efa, plane); + result = ORIENTATION_FACE; + } + else if (eed != NULL) { + float eed_plane[3]; + float vec[3]; + add_v3_v3v3(normal, eed->v1->no, eed->v2->no); + sub_v3_v3v3(eed_plane, eed->v2->co, eed->v1->co); + cross_v3_v3v3(vec, normal, eed_plane); + cross_v3_v3v3(normal, eed_plane, vec); + normalize_v3(normal); - switch (ese.htype) { - case BM_VERT: - result = ORIENTATION_VERT; - break; - case BM_EDGE: - result = ORIENTATION_EDGE; - break; - case BM_FACE: - result = ORIENTATION_FACE; - break; + if (BM_edge_is_boundary(eed)) { + sub_v3_v3v3(plane, eed->l->v->co, eed->l->next->v->co); + } + else { + if (eed->v2->co[1] > eed->v1->co[1]) { + sub_v3_v3v3(plane, eed->v2->co, eed->v1->co); + } + else { + sub_v3_v3v3(plane, eed->v1->co, eed->v2->co); + } + } + normalize_v3(plane); + result = ORIENTATION_EDGE; + } + else if (eve != NULL) { + copy_v3_v3(normal, eve->no); + if (eve->e) { + float vert1v3[3] = {eve->e->v1->co[0], eve->e->v1->co[1], eve->e->v1->co[2]}; + float vert2v3[3] = {eve->e->v2->co[0], eve->e->v2->co[1], eve->e->v2->co[2]}; + sub_v3_v3v3(plane, vert2v3, vert1v3); + } + else { + if (eve->no[0] < 0.5f) { + vec[0] = 1.0f; + } + else if (eve->no[1] < 0.5f) { + vec[1] = 1.0f; + } + else { + vec[2] = 1.0f; + } + cross_v3_v3v3(plane, eve->no, vec); + } + normalize_v3(plane); + result = ORIENTATION_VERT; + } + else { + BM_editselection_normal(&ese, normal); + BM_editselection_plane(&ese, plane); + switch (ese.htype) { + case BM_VERT: + result = ORIENTATION_VERT; + break; + case BM_EDGE: + result = ORIENTATION_EDGE; + break; + case BM_FACE: + result = ORIENTATION_FACE; + break; + } } } else { @@ -1289,7 +1348,8 @@ int getTransformOrientation(const bContext *C, float normal[3], float plane[3]) ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); - return getTransformOrientation_ex(scene, view_layer, v3d, obact, obedit, normal, plane, around); + return getTransformOrientation_ex( + scene, view_layer, v3d, obact, obedit, normal, plane, around, NULL, NULL, NULL); } void ED_getTransformOrientationMatrix(const Scene *scene, @@ -1298,14 +1358,32 @@ void ED_getTransformOrientationMatrix(const Scene *scene, Object *ob, Object *obedit, const short around, - float r_orientation_mat[3][3]) + float r_orientation_mat[3][3], + struct BMVert *eve, + struct BMEdge *eed, + struct BMFace *efa) { float normal[3] = {0.0, 0.0, 0.0}; float plane[3] = {0.0, 0.0, 0.0}; int type; - type = getTransformOrientation_ex(scene, view_layer, v3d, ob, obedit, normal, plane, around); + if (efa != NULL) { + type = getTransformOrientation_ex( + scene, view_layer, v3d, ob, obedit, normal, plane, around, NULL, NULL, efa); + } + else if (eed != NULL) { + type = getTransformOrientation_ex( + scene, view_layer, v3d, ob, obedit, normal, plane, around, NULL, eed, NULL); + } + else if (eve != NULL) { + type = getTransformOrientation_ex( + scene, view_layer, v3d, ob, obedit, normal, plane, around, eve, NULL, NULL); + } + else { + type = getTransformOrientation_ex( + scene, view_layer, v3d, ob, obedit, normal, plane, around, NULL, NULL, NULL); + } /* Fallback, when the plane can't be calculated. */ if (ORIENTATION_USE_PLANE(type) && is_zero_v3(plane)) { diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 6897b171d8b..b4951e7aab6 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -3947,8 +3947,12 @@ static bool do_lasso_select_mesh_uv_is_edge_inside(const ARegion *region, if (UI_view2d_view_to_region_segment_clip( ®ion->v2d, co_test_a, co_test_b, co_screen_a, co_screen_b) && BLI_rcti_isect_segment(clip_rect, co_screen_a, co_screen_b) && - BLI_lasso_is_edge_inside( - mcoords, mcoords_len, UNPACK2(co_screen_a), UNPACK2(co_screen_b), V2D_IS_CLIPPED)) { + BLI_lasso_is_edge_inside(mcoords, + mcoords_len, + UNPACK2(co_screen_a), + UNPACK2(co_screen_b), + V2D_IS_CLIPPED, + false)) { return true; } return false; diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 6cc01d254ce..e97e7402d1a 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -366,6 +366,54 @@ /* UV painting */ \ .uv_sculpt_settings = 0, \ .uv_relax_method = UV_SCULPT_TOOL_RELAX_LAPLACIAN, \ + \ + /* Mesh Select */ \ + .box_drag_direction = 1, \ + .lasso_drag_direction = 1, \ + .box_edge = 1, \ + .box_edge_left = 1, \ + .box_edge_right = 1, \ + .box_edge_up = 1, \ + .box_edge_down = 1, \ + .box_face = 1, \ + .box_face_left = 1, \ + .box_face_right = 1, \ + .box_face_up = 1, \ + .box_face_down = 1, \ + .lasso_edge = 1, \ + .lasso_edge_left = 1, \ + .lasso_edge_right = 1, \ + .lasso_edge_up = 1, \ + .lasso_edge_down = 1, \ + .lasso_face = 1, \ + .lasso_face_left = 1, \ + .lasso_face_right = 1, \ + .lasso_face_up = 1, \ + .lasso_face_down = 1, \ + .circle_edge = 2, \ + .circle_face = 1, \ + \ + /* X-Ray */ \ + .xray_button = true, \ + .auto_xray_object = true, \ + .auto_xray_edit = true, \ + .auto_xray_box = true, \ + .auto_xray_lasso = true, \ + .auto_xray_circle = true, \ + .select_through = true, \ + .select_through_object = true, \ + .select_through_box = true, \ + .select_through_lasso = true, \ + .select_through_circle = true, \ + \ + /* Viewport-Facing Select */ \ + .viewport_facing_select_mode = 1, \ + .viewport_facing_select_vert = 1, \ + .viewport_facing_select_edge = 1, \ + .viewport_facing_select_face = 1, \ + \ + /* Object Origin Select */ \ + .select_origin_circle = true, \ } /* clang-format off */ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index ca1d878110f..4396d1ba9b7 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1557,7 +1557,40 @@ typedef struct ToolSettings { char gpencil_v3d_align; /** General 2D Editor. */ char gpencil_v2d_align; - char _pad0[2]; + + /* Give the UI an empty prop to expand formatting */ + char ui_prop; + + /* X-Ray Options */ + char xray_button; + char auto_xray; + char auto_xray_button; + char auto_xray_reset; + char auto_xray_object; + char auto_xray_edit; + char auto_xray_box; + char auto_xray_lasso; + char auto_xray_circle; + char select_through; + char select_through_button; + char select_through_object; + char select_through_edit; + char select_through_box; + char select_through_lasso; + char select_through_circle; + + /* Mesh Normal Direction Select */ + char viewport_facing_select; + char viewport_facing_select_mode; + float viewport_facing_select_threshold; + char viewport_facing_select_vert; + char viewport_facing_select_edge; + char viewport_facing_select_face; + + /* object origin select */ + char select_origin_box; + char select_origin_circle; + char _pad0[6]; /* Annotations. */ /** Stroke placement settings - 3D View. */ @@ -1682,7 +1715,41 @@ typedef struct ToolSettings { /** Normal Editing. */ float normal_vector[3]; - char _pad6[4]; + + /** Mesh Select Options. */ + char box_drag_direction; + char lasso_drag_direction; + char box_direction_upright; + char lasso_direction_upright; + char box_edge; + char box_edge_left; + char box_edge_right; + char box_edge_up; + char box_edge_down; + char box_face; + char box_face_left; + char box_face_right; + char box_face_up; + char box_face_down; + char lasso_edge; + char lasso_edge_left; + char lasso_edge_right; + char lasso_edge_up; + char lasso_edge_down; + char lasso_face; + char lasso_face_left; + char lasso_face_right; + char lasso_face_up; + char lasso_face_down; + char circle_edge; + char circle_face; + char show_box_options; + char show_lasso_options; + char show_circle_options; + + /* Combine shading and xray header buttons */ + char shrink_shading_header; + char _pad6[6]; /** * Custom Curve Profile for bevel tool: @@ -2331,6 +2398,78 @@ typedef enum eSnapTransformMode { SCE_SNAP_TRANSFORM_MODE_SCALE = (1 << 2), } eSnapTransformMode; +/** #ToolSettings.viewport_facing_mode */ +enum { + VIEWPORT_FACING_SELECT_BOTH = (1 << 0), + VIEWPORT_FACING_SELECT_NEAR = (1 << 1), + VIEWPORT_FACING_SELECT_XRAY = (1 << 2), +}; + +/** #ToolSettings.viewport_facing_select_vert */ +enum { + VIEWPORT_FACING_SELECT_FRONT_VERTS = (1 << 0), + VIEWPORT_FACING_SELECT_FRONT_VERTS_FACE = (1 << 1), + VIEWPORT_FACING_SELECT_REAR_VERTS = (1 << 2), + VIEWPORT_FACING_SELECT_REAR_VERTS_FACE = (1 << 3), + VIEWPORT_FACING_SELECT_ALL_VERTS = (1 << 4), +}; + +/** #ToolSettings.viewport_facing_select_edge */ +enum { + VIEWPORT_FACING_SELECT_FRONT_EDGES = (1 << 0), + VIEWPORT_FACING_SELECT_FRONT_EDGES_FACE = (1 << 1), + VIEWPORT_FACING_SELECT_REAR_EDGES = (1 << 2), + VIEWPORT_FACING_SELECT_REAR_EDGES_FACE = (1 << 3), + VIEWPORT_FACING_SELECT_ALL_EDGES = (1 << 4), +}; + +/** #ToolSettings.viewport_facing_select_face */ +enum { + VIEWPORT_FACING_SELECT_FRONT_FACES = (1 << 0), + VIEWPORT_FACING_SELECT_FRONT_FACES_VERT = (1 << 1), + VIEWPORT_FACING_SELECT_REAR_FACES = (1 << 2), + VIEWPORT_FACING_SELECT_REAR_FACES_VERT = (1 << 3), + VIEWPORT_FACING_SELECT_ALL_FACES = (1 << 4), +}; + +/** #ToolSettings.face_select */ +enum { + FACE_AUTO = (1 << 0), + FACE_TOUCH = (1 << 1), + FACE_ENCLOSE = (1 << 2), + FACE_CENTER = (1 << 3), +}; + +/** #ToolSettings.edge_select */ +enum { + EDGE_HYBRID = (1 << 0), + EDGE_TOUCH = (1 << 1), + EDGE_ENCLOSE = (1 << 2), +}; + +/** #ToolSettings.mesh_drag_direction */ +enum { + MESH_DIRECTION_ANY = (1 << 0), + MESH_DIRECTION_LEFT_RIGHT = (1 << 1), + MESH_DIRECTION_UP_DOWN = (1 << 2), +}; + +/** #ToolSettings.auto_xray_mode */ +enum { + AUTO_XRAY_DISABLE = (1 << 0), + AUTO_XRAY_OBJECT = (1 << 1), + AUTO_XRAY_EDIT = (1 << 2), + AUTO_XRAY_BOTH = (1 << 3), +}; + +/** #ToolSettings.select_through_mode */ +enum { + SELECT_THROUGH_DISABLE = (1 << 0), + SELECT_THROUGH_OBJECT = (1 << 1), + SELECT_THROUGH_EDIT = (1 << 2), + SELECT_THROUGH_BOTH = (1 << 3), +}; + /** #ToolSettings.selectmode */ #define SCE_SELECT_VERTEX (1 << 0) /* for mesh */ #define SCE_SELECT_EDGE (1 << 1) diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 01b2c0464ce..1b45ea6fe42 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -840,7 +840,11 @@ typedef struct UserDef { /** #eGPUBackendType */ short gpu_backend; - char _pad7[4]; + /** Keymap click-drag direction style. */ + char click_drag_direction; + /** Box, lasso, and circle select mesh control style. */ + char drag_select_control; + char _pad7[2]; /** Private, defaults to 20 for 72 DPI setting. */ short widget_unit; @@ -900,6 +904,12 @@ typedef struct UserDef { char drag_threshold; char move_threshold; + /** Adjustable selection radius */ + float selection_radius; + char adjustable_click_select; + char select_unbiased; + char _pad9[2]; + char font_path_ui[1024]; char font_path_ui_mono[1024]; @@ -1002,11 +1012,11 @@ typedef enum eUserPref_SpaceData_Flag { typedef enum eUserPref_Flag { USER_AUTOSAVE = (1 << 0), USER_FLAG_NUMINPUT_ADVANCED = (1 << 1), - USER_FLAG_UNUSED_2 = (1 << 2), /* cleared */ - USER_FLAG_UNUSED_3 = (1 << 3), /* cleared */ - USER_FLAG_UNUSED_4 = (1 << 4), /* cleared */ + USER_ADJUSTABLE_CLICK_SELECT = (1 << 2), + USER_SELECT_UNBIASED = (1 << 3), + USER_ALTERNATE_CURSOR = (1 << 4), USER_TRACKBALL = (1 << 5), - USER_FLAG_UNUSED_6 = (1 << 6), /* cleared */ + USER_ALTERNATE_CURSOR_LARGE = (1 << 6), USER_FLAG_UNUSED_7 = (1 << 7), /* cleared */ USER_MAT_ON_OB = (1 << 8), USER_FLAG_UNUSED_9 = (1 << 9), /* cleared */ @@ -1153,6 +1163,19 @@ typedef enum eUserpref_TableAPI { USER_TABLET_WINTAB = 2, } eUserpref_TabletAPI; +/** #UserDef.click_drag_direction_types */ +typedef enum eUserpref_Click_Drag_Direction { + USER_CLICK_DRAG_DIRECTION_EIGHT_WAY = 0, + USER_CLICK_DRAG_DIRECTION_LEFT_RIGHT = 1, + USER_CLICK_DRAG_DIRECTION_UP_DOWN = 2, +} eUserpref_Click_Drag_Direction; + +/** #UserDef.drag_select_control_types */ +typedef enum eUserpref_Drag_Select_Control { + USER_DRAG_SELECT_TOOLSETTING = 0, + USER_DRAG_SELECT_KEYMAP = 1, +} eUserpref_Drag_Select_Control; + /** #UserDef.app_flag */ typedef enum eUserpref_APP_Flag { USER_APP_LOCK_CORNER_SPLIT = (1 << 0), diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index ffedea37c33..76b52a8abe6 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -3049,10 +3049,347 @@ static void rna_def_tool_settings(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem mesh_drag_direction_items[] = { + {MESH_DIRECTION_ANY, "MESH_DIRECTION_ANY", 0, "Any", "Drag in any direction"}, + {MESH_DIRECTION_LEFT_RIGHT, + "MESH_DIRECTION_LEFT_RIGHT", + 0, + "Left Right", + "Drag to the left and right"}, + {MESH_DIRECTION_UP_DOWN, + "MESH_DIRECTION_UP_DOWN", + 0, + "Up Down", + "Drag upwards and downwards"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem edge_select_items[] = { + {EDGE_HYBRID, + "EDGE_HYBRID", + 0, + "Hybrid", + "Select edges that are fully inside the selection area. If no edges are fully inside the " + "selection area, select edges that are touched by the selection area"}, + {EDGE_TOUCH, + "EDGE_TOUCH", + 0, + "Touch", + "Select edges that are touched by the selection area"}, + {EDGE_ENCLOSE, + "EDGE_ENCLOSE", + 0, + "Enclose", + "Select edges that are fully inside the selection area"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem edge_circle_select_items[] = { + {EDGE_TOUCH, + "EDGE_TOUCH", + 0, + "Touch", + "Select edges that are touched by the selection area"}, + {EDGE_ENCLOSE, + "EDGE_ENCLOSE", + 0, + "Enclose", + "Select edges that are fully inside the selection area"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem face_select_items[] = { + {FACE_AUTO, + "FACE_AUTO", + 0, + "Auto", + "Select faces that are touched by the selection area in near select. Select faces whose " + "center is touched by the selection area in X-Ray select"}, + {FACE_TOUCH, + "FACE_TOUCH", + 0, + "Touch", + "Select faces that are touched by the selection area"}, + {FACE_ENCLOSE, + "FACE_ENCLOSE", + 0, + "Enclose", + "Select faces that are fully inside the selection area"}, + {FACE_CENTER, + "FACE_CENTER", + 0, + "Center", + "Select faces whose center is touched by the selection area"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem viewport_facing_select_mode_items[] = { + {VIEWPORT_FACING_SELECT_BOTH, + "BOTH", + 0, + "Near and X-Ray", + "Use viewport-facing selection in near select and X-Ray"}, + {VIEWPORT_FACING_SELECT_NEAR, + "NEAR", + 0, + "Near", + "Use viewport-facing selection in near select"}, + {VIEWPORT_FACING_SELECT_XRAY, + "XRAY", + 0, + "X-Ray", + "Use viewport-facing selection in X-Ray"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem viewport_facing_select_vert_items[] = { + {VIEWPORT_FACING_SELECT_FRONT_VERTS, + "FRONT_VERTS", + 0, + "Front Verts", + "Select vertices with viewport-facing normals"}, + {VIEWPORT_FACING_SELECT_FRONT_VERTS_FACE, + "FRONT_VERTS_FACE", + 0, + "Verts of Front Face", + "Select vertices if they are part of a face that has a viewport-facing normal"}, + {VIEWPORT_FACING_SELECT_REAR_VERTS, + "REAR_VERTS", + 0, + "Rear Verts", + "Select vertices without viewport-facing normals"}, + {VIEWPORT_FACING_SELECT_REAR_VERTS_FACE, + "REAR_VERTS_FACE", + 0, + "Verts of Rear Face", + "Select vertices if they are part of a face that does not have a viewport-facing normal"}, + {VIEWPORT_FACING_SELECT_ALL_VERTS, + "ALL_VERTS", + 0, + "All Verts", + "Select vertices regarless of their normal direction"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem viewport_facing_select_edge_items[] = { + {VIEWPORT_FACING_SELECT_FRONT_EDGES, + "FRONT_EDGES", + 0, + "Front Edges", + "Select edges with viewport-facing normals"}, + {VIEWPORT_FACING_SELECT_FRONT_EDGES_FACE, + "FRONT_EDGES_FACE", + 0, + "Edges of Front Face", + "Select edges if they are part of a face that has a viewport-facing normal"}, + {VIEWPORT_FACING_SELECT_REAR_EDGES, + "REAR_EDGES", + 0, + "Rear Edges", + "Select edges without viewport-facing normals"}, + {VIEWPORT_FACING_SELECT_REAR_EDGES_FACE, + "REAR_EDGES_FACE", + 0, + "Edges of Rear Face", + "Select edges if they are part of a face that does not have a viewport-facing normal"}, + {VIEWPORT_FACING_SELECT_ALL_EDGES, + "ALL_EDGES", + 0, + "All Edges", + "Select edges regarless of their normal direction"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem viewport_facing_select_face_items[] = { + {VIEWPORT_FACING_SELECT_FRONT_FACES, + "FRONT_FACES", + 0, + "Front Faces", + "Select faces with viewport-facing normals"}, + {VIEWPORT_FACING_SELECT_FRONT_FACES_VERT, + "FRONT_FACES_VERT", + 0, + "Faces of Front Vert", + "Select faces if they have a vertex with a viewport-facing normal"}, + {VIEWPORT_FACING_SELECT_REAR_FACES, + "REAR_FACES", + 0, + "Rear Faces", + "Select faces without viewport-facing normals"}, + {VIEWPORT_FACING_SELECT_REAR_FACES_VERT, + "REAR_FACES_VERT", + 0, + "Faces of Rear Vert", + "Select faces if they have a vertex without a viewport-facing normal"}, + {VIEWPORT_FACING_SELECT_ALL_FACES, + "ALL_FACES", + 0, + "All Faces", + "Select faces regarless of their normal direction"}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "ToolSettings", NULL); RNA_def_struct_path_func(srna, "rna_ToolSettings_path"); RNA_def_struct_ui_text(srna, "Tool Settings", ""); + /* Viewport-Facing Select */ + prop = RNA_def_property(srna, "viewport_facing_select", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "viewport_facing_select", 0); + RNA_def_property_ui_text( + prop, + "Viewport Facing Select", + "Filter box, lasso, and circle selection of mesh elements based on the direction of their " + "normals compared to the viewport"); + + prop = RNA_def_property(srna, "viewport_facing_select_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "viewport_facing_select_mode"); + RNA_def_property_enum_items(prop, viewport_facing_select_mode_items); + RNA_def_property_ui_text(prop, "Mode", "Which selection modes to use viewport-facing selection"); + + prop = RNA_def_property(srna, "viewport_facing_select_vert", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "viewport_facing_select_vert"); + RNA_def_property_enum_items(prop, viewport_facing_select_vert_items); + RNA_def_property_ui_text(prop, "Vert", "Direction and mode for vertices"); + + prop = RNA_def_property(srna, "viewport_facing_select_edge", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "viewport_facing_select_edge"); + RNA_def_property_enum_items(prop, viewport_facing_select_edge_items); + RNA_def_property_ui_text(prop, "Edge", "Direction and mode for edges"); + + prop = RNA_def_property(srna, "viewport_facing_select_face", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "viewport_facing_select_face"); + RNA_def_property_enum_items(prop, viewport_facing_select_face_items); + RNA_def_property_ui_text(prop, "Face", "Direction and mode for faces"); + + prop = RNA_def_property(srna, "viewport_facing_select_threshold", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 1.0f, 2); + RNA_def_property_ui_text( + prop, + "Threshold", + "How close the angles of the viewport and mesh element need to be for selection to occur"); + + /* Mesh select settings. */ + prop = RNA_def_property(srna, "box_drag_direction", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mesh_drag_direction_items); + RNA_def_property_ui_text(prop, "Drag Direction", "Click-drag direction style for box select"); + + prop = RNA_def_property(srna, "lasso_drag_direction", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mesh_drag_direction_items); + RNA_def_property_ui_text(prop, "Drag Direction", "Click-drag direction style for lasso select"); + + prop = RNA_def_property(srna, "box_direction_upright", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "box_direction_upright", 0); + RNA_def_property_ui_text(prop, "Box Direction Helper", ""); + + prop = RNA_def_property(srna, "lasso_direction_upright", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "lasso_direction_upright", 0); + RNA_def_property_ui_text(prop, "Lasso Direction Helper", ""); + + prop = RNA_def_property(srna, "show_box_options", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "show_box_options", 0); + RNA_def_property_ui_text( + prop, "Mesh Options", "Additional options for box selection of mesh elements"); + + prop = RNA_def_property(srna, "show_lasso_options", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "show_lasso_options", 0); + RNA_def_property_ui_text( + prop, "Mesh Options", "Additional options for lasso selection of mesh elements"); + + prop = RNA_def_property(srna, "show_circle_options", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "show_circle_options", 0); + RNA_def_property_ui_text( + prop, "Mesh Options", "Additional options for circle selection of mesh elements"); + + prop = RNA_def_property(srna, "box_edge", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, edge_select_items); + RNA_def_property_ui_text(prop, "Edge", "Box edge selection style"); + + prop = RNA_def_property(srna, "box_edge_left", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, edge_select_items); + RNA_def_property_ui_text(prop, "Edge", "Box edge selection style when dragging left"); + + prop = RNA_def_property(srna, "box_edge_right", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, edge_select_items); + RNA_def_property_ui_text(prop, "Edge", "Box edge selection style when dragging right"); + + prop = RNA_def_property(srna, "box_edge_up", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, edge_select_items); + RNA_def_property_ui_text(prop, "Edge", "Box edge selection style when dragging up"); + + prop = RNA_def_property(srna, "box_edge_down", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, edge_select_items); + RNA_def_property_ui_text(prop, "Edge", "Box edge selection style when dragging down"); + + prop = RNA_def_property(srna, "box_face", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, face_select_items); + RNA_def_property_ui_text(prop, "Face", "Box face selection style"); + + prop = RNA_def_property(srna, "box_face_left", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, face_select_items); + RNA_def_property_ui_text(prop, "Face", "Box face selection style when dragging left"); + + prop = RNA_def_property(srna, "box_face_right", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, face_select_items); + RNA_def_property_ui_text(prop, "Face", "Box face selection style when dragging right"); + + prop = RNA_def_property(srna, "box_face_up", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, face_select_items); + RNA_def_property_ui_text(prop, "Face", "Box face selection style when dragging up"); + + prop = RNA_def_property(srna, "box_face_down", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, face_select_items); + RNA_def_property_ui_text(prop, "Face", "Box face selection style when dragging down"); + + prop = RNA_def_property(srna, "lasso_edge", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, edge_select_items); + RNA_def_property_ui_text(prop, "Edge", "Lasso edge selection style"); + + prop = RNA_def_property(srna, "lasso_edge_left", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, edge_select_items); + RNA_def_property_ui_text(prop, "Edge", "Lasso edge selection style when dragging left"); + + prop = RNA_def_property(srna, "lasso_edge_right", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, edge_select_items); + RNA_def_property_ui_text(prop, "Edge", "Lasso edge selection style when dragging right"); + + prop = RNA_def_property(srna, "lasso_edge_up", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, edge_select_items); + RNA_def_property_ui_text(prop, "Edge", "Lasso edge selection style when dragging up"); + + prop = RNA_def_property(srna, "lasso_edge_down", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, edge_select_items); + RNA_def_property_ui_text(prop, "Edge", "Lasso edge selection style when dragging down"); + + prop = RNA_def_property(srna, "lasso_face", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, face_select_items); + RNA_def_property_ui_text(prop, "Face", "Lasso face selection style"); + + prop = RNA_def_property(srna, "lasso_face_left", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, face_select_items); + RNA_def_property_ui_text(prop, "Face", "Lasso face selection style when dragging left"); + + prop = RNA_def_property(srna, "lasso_face_right", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, face_select_items); + RNA_def_property_ui_text(prop, "Face", "Lasso face selection style when dragging right"); + + prop = RNA_def_property(srna, "lasso_face_up", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, face_select_items); + RNA_def_property_ui_text(prop, "Face", "Lasso face selection style when dragging up"); + + prop = RNA_def_property(srna, "lasso_face_down", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, face_select_items); + RNA_def_property_ui_text(prop, "Face", "Lasso face selection style when dragging down"); + + prop = RNA_def_property(srna, "circle_edge", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, edge_circle_select_items); + RNA_def_property_ui_text(prop, "Edge", "Circle edge selection style"); + + prop = RNA_def_property(srna, "circle_face", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, face_select_items); + RNA_def_property_ui_text(prop, "Face", "Circle face selection style"); + prop = RNA_def_property(srna, "sculpt", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "Sculpt"); RNA_def_property_ui_text(prop, "Sculpt", ""); @@ -3725,6 +4062,122 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Normal Vector", "Normal Vector used to copy, add or multiply"); RNA_def_property_ui_range(prop, -10000.0, 10000.0, 1, 3); + /* UI prop helper, might help with formatting and using headings on a blank line, unused for now */ + prop = RNA_def_property(srna, "ui_prop", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "ui_prop", 0); + RNA_def_property_ui_text(prop, "UI Prop", ""); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + /* Shrink Shading Header */ + prop = RNA_def_property(srna, "shrink_shading_header", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "shrink_shading_header", 0); + RNA_def_property_ui_text( + prop, "Shrink Header", "Combine the four Shading Header buttons into one button that also toggles X-Ray"); + + /* X-Ray header button */ + prop = RNA_def_property(srna, "xray_button", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "xray_button", 0); + RNA_def_property_ui_text( + prop, "X-Ray Button", "Show button for X-Ray in viewport header"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + /* Auto X-Ray */ + prop = RNA_def_property(srna, "auto_xray", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "auto_xray", 0); + RNA_def_property_ui_text(prop, "Auto X-Ray", "Transparent scene display during drag select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "auto_xray_button", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "auto_xray_button", 0); + RNA_def_property_ui_text(prop, "Auto X-Ray Button", "Show button for automatic X-Ray in viewport header"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "auto_xray_reset", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "auto_xray_reset", 0); + RNA_def_property_ui_text(prop, "Auto X-Ray Reset", "Helper that turns xray off for autoxray"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "auto_xray_object", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "auto_xray_object", 0); + RNA_def_property_ui_text(prop, "Auto X-Ray Object", "Automatic X-Ray in object mode"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "auto_xray_edit", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "auto_xray_edit", 0); + RNA_def_property_ui_text(prop, "Auto X-Ray Edit", "Automatic X-Ray in edit mode"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "auto_xray_box", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "auto_xray_box", 0); + RNA_def_property_ui_text(prop, "Auto X-Ray Box", "Transparent scene display during box select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "auto_xray_lasso", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "auto_xray_lasso", 0); + RNA_def_property_ui_text(prop, "Auto X-Ray Lasso", "Transparent scene display during lasso select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "auto_xray_circle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "auto_xray_circle", 0); + RNA_def_property_ui_text(prop, "Auto X-Ray Circle", "Transparent scene display during circle select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + /* Select Through */ + prop = RNA_def_property(srna, "select_through", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "select_through", 0); + RNA_def_property_ui_text( + prop, "Select Through", "Select occluded objects and mesh elements with drag select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "select_through_button", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "select_through_button", 0); + RNA_def_property_ui_text( + prop, "Select Through Button", "Show button for select through in viewport header"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "select_through_object", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "select_through_object", 0); + RNA_def_property_ui_text(prop, "Select Through Object", "Select through in object mode"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "select_through_edit", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "select_through_edit", 0); + RNA_def_property_ui_text(prop, "Select Through Edit", "Select through in edit mode"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "select_through_box", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "select_through_box", 0); + RNA_def_property_ui_text( + prop, "Select Through Box", "Select occluded objects and mesh elements with box select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "select_through_lasso", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "select_through_lasso", 0); + RNA_def_property_ui_text(prop, + "Select Through Lasso", + "Select occluded objects and mesh elements with lasso select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "select_through_circle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "select_through_circle", 0); + RNA_def_property_ui_text(prop, + "Select Through Circle", + "Select occluded objects and mesh elements with circle select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + /* Object Origin Select */ + prop = RNA_def_property(srna, "select_origin_box", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "select_origin_box", 0); + RNA_def_property_ui_text( + prop, "Select Origin", "Select objects by their origin with box select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "select_origin_circle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "select_origin_circle", 0); + RNA_def_property_ui_text( + prop, "Select Origin", "Select objects by their origin with circle select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + /* Unified Paint Settings */ prop = RNA_def_property(srna, "unified_paint_settings", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 62f7118df1d..3deda02181a 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -5310,6 +5310,21 @@ static void rna_def_userdef_edit(BlenderRNA *brna) prop, "Auto-offset Margin", "Minimum distance between nodes for Auto-offsetting nodes"); RNA_def_property_update(prop, 0, "rna_userdef_update"); + prop = RNA_def_property(srna, "adjustable_click_select", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_ADJUSTABLE_CLICK_SELECT); + RNA_def_property_ui_text( + prop, "Adjustable Click-Select", "Use additional options for single-click select"); + + prop = RNA_def_property(srna, "select_unbiased", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_SELECT_UNBIASED); + RNA_def_property_ui_text( + prop, "Select Unbiased", "Click-select will not favor unselected mesh elements"); + + prop = RNA_def_property(srna, "selection_radius", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_range(prop, 0.1f, 150.0f); + RNA_def_property_ui_range(prop, 0.1f, 150.0f, 0.01f, 2); + RNA_def_property_ui_text(prop, "Radius", "Size of single-click selection radius"); + /* cursor */ prop = RNA_def_property(srna, "use_cursor_lock_adjust", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_LOCK_CURSOR_ADJUST); @@ -5322,6 +5337,14 @@ static void rna_def_userdef_edit(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_DEPTH_CURSOR); RNA_def_property_ui_text( prop, "Cursor Surface Project", "Use the surface depth for cursor placement"); + + prop = RNA_def_property(srna, "alternate_cursor", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_ALTERNATE_CURSOR); + RNA_def_property_ui_text(prop, "Alternate Cursor", "Alternate edit mode crosshair"); + + prop = RNA_def_property(srna, "alternate_cursor_large", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_ALTERNATE_CURSOR_LARGE); + RNA_def_property_ui_text(prop, "Large Cursor", "Supersize the alternate edit mode crosshair"); } static void rna_def_userdef_system(BlenderRNA *brna) @@ -5774,6 +5797,31 @@ static void rna_def_userdef_input(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem click_drag_direction_types[] = { + {USER_CLICK_DRAG_DIRECTION_EIGHT_WAY, + "EIGHT_WAY", + 0, + "Eight", + "Eight directions (N, NE, E, SE, S, SW, W, NW)"}, + {USER_CLICK_DRAG_DIRECTION_LEFT_RIGHT, "LEFT_RIGHT", 0, "Left Right", "Left and right"}, + {USER_CLICK_DRAG_DIRECTION_UP_DOWN, "UP_DOWN", 0, "Up Down", "Up and down"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem drag_select_control_types[] = { + {USER_DRAG_SELECT_TOOLSETTING, + "USER_DRAG_TOOLSETTING", + 0, + "Toolsetting", + "Use toolsettings to control selection options for box, lasso, and circle select"}, + {USER_DRAG_SELECT_KEYMAP, + "USER_DRAG_KEYMAP", + 0, + "Keymap", + "Use the keymap to control selection options for box, lasso, and circle selection"}, + {0, NULL, 0, NULL, NULL}, + }; + static const EnumPropertyItem view_zoom_styles[] = { {USER_ZOOM_CONTINUE, "CONTINUE", @@ -5880,6 +5928,18 @@ static void rna_def_userdef_input(BlenderRNA *brna) "Release Confirms", "Moving things with a mouse drag confirms when releasing the button"); + prop = RNA_def_property(srna, "click_drag_direction", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, click_drag_direction_types); + RNA_def_property_ui_text( + prop, "Keymap Drag Directions", "Style of click-drag direction the keymap will use"); + + prop = RNA_def_property(srna, "drag_select_control", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, drag_select_control_types); + RNA_def_property_ui_text(prop, + "Drag Select Control", + "Use either the keymap or toolsettings to control selection options " + "for box, lasso, and circle select"); + prop = RNA_def_property(srna, "use_numeric_input_advanced", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_FLAG_NUMINPUT_ADVANCED); RNA_def_property_ui_text(prop, diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c index 5d8bb533d7e..9952cfb1a57 100644 --- a/source/blender/makesrna/intern/rna_wm_api.c +++ b/source/blender/makesrna/intern/rna_wm_api.c @@ -389,6 +389,41 @@ static void rna_KeyMap_item_remove(wmKeyMap *km, ReportList *reports, PointerRNA RNA_POINTER_INVALIDATE(kmi_ptr); } +static void rna_keymap_set_direction_any(wmKeyMap *km, PointerRNA *kmi_ptr) +{ + wmKeyMapItem *kmi = kmi_ptr->data; + kmi->direction = -1; + WM_keyconfig_update_tag(km, kmi); +} + +static void rna_keymap_set_direction_left(wmKeyMap *km, PointerRNA *kmi_ptr) +{ + wmKeyMapItem *kmi = kmi_ptr->data; + kmi->direction = 7; + WM_keyconfig_update_tag(km, kmi); +} + +static void rna_keymap_set_direction_right(wmKeyMap *km, PointerRNA *kmi_ptr) +{ + wmKeyMapItem *kmi = kmi_ptr->data; + kmi->direction = 3; + WM_keyconfig_update_tag(km, kmi); +} + +static void rna_keymap_set_direction_up(wmKeyMap *km, PointerRNA *kmi_ptr) +{ + wmKeyMapItem *kmi = kmi_ptr->data; + kmi->direction = 1; + WM_keyconfig_update_tag(km, kmi); +} + +static void rna_keymap_set_direction_down(wmKeyMap *km, PointerRNA *kmi_ptr) +{ + wmKeyMapItem *kmi = kmi_ptr->data; + kmi->direction = 5; + WM_keyconfig_update_tag(km, kmi); +} + static PointerRNA rna_KeyMap_item_find_from_operator(ID *id, wmKeyMap *km, const char *idname, @@ -1197,6 +1232,31 @@ void RNA_api_keymapitems(StructRNA *srna) RNA_def_function_flag(func, FUNC_USE_REPORTS); parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "set_direction_any", "rna_keymap_set_direction_any"); + parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "set_direction_left", "rna_keymap_set_direction_left"); + parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "set_direction_right", "rna_keymap_set_direction_right"); + parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "set_direction_up", "rna_keymap_set_direction_up"); + parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "set_direction_down", "rna_keymap_set_direction_down"); + parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); func = RNA_def_function(srna, "from_id", "WM_keymap_item_find_id"); diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 6c3d72e5406..1a5f6359bdf 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -688,6 +688,7 @@ int WM_operator_call_notest(struct bContext *C, struct wmOperator *op); * Execute this operator again, put here so it can share above code */ int WM_operator_repeat(struct bContext *C, struct wmOperator *op); +int WM_operator_repeat_tool(struct bContext *C, struct wmOperator *op); int WM_operator_repeat_last(struct bContext *C, struct wmOperator *op); /** * \return true if #WM_operator_repeat can run. @@ -1611,7 +1612,7 @@ bool WM_event_is_modal_drag_exit(const struct wmEvent *event, short init_event_val); bool WM_event_is_mouse_drag(const struct wmEvent *event); bool WM_event_is_mouse_drag_or_press(const wmEvent *event); -int WM_event_drag_direction(const wmEvent *event); +int WM_event_drag_direction(const wmEvent *event, struct ToolSettings *ts); char WM_event_utf8_to_ascii(const struct wmEvent *event) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; /** diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c index c9624d0e14e..2475e617fd0 100644 --- a/source/blender/windowmanager/intern/wm_cursors.c +++ b/source/blender/windowmanager/intern/wm_cursors.c @@ -50,7 +50,15 @@ static GHOST_TStandardCursor convert_to_ghost_standard_cursor(WMCursorType curs) return GHOST_kStandardCursorWait; case WM_CURSOR_EDIT: case WM_CURSOR_CROSS: - return GHOST_kStandardCursorCrosshair; + if (U.flag & USER_ALTERNATE_CURSOR && !(U.flag & USER_ALTERNATE_CURSOR_LARGE)) { + return GHOST_kStandardCursorCrosshairD; + } + else if (U.flag & USER_ALTERNATE_CURSOR && U.flag & USER_ALTERNATE_CURSOR_LARGE) { + return GHOST_kStandardCursorCrosshairE; + } + else { + return GHOST_kStandardCursorCrosshair; + } case WM_CURSOR_X_MOVE: return GHOST_kStandardCursorLeftRight; case WM_CURSOR_Y_MOVE: diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index 2e1afe808ad..c5c846affe2 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -264,36 +264,71 @@ bool WM_event_is_mouse_drag_or_press(const wmEvent *event) (ISMOUSE_BUTTON(event->type) && (event->val == KM_PRESS)); } -int WM_event_drag_direction(const wmEvent *event) +int WM_event_drag_direction(const wmEvent *event, ToolSettings *ts) { const int delta[2] = { event->xy[0] - event->prev_press_xy[0], event->xy[1] - event->prev_press_xy[1], }; - int theta = round_fl_to_int(4.0f * atan2f((float)delta[1], (float)delta[0]) / (float)M_PI); - int val = KM_DIRECTION_W; + bool left_right = U.click_drag_direction & USER_CLICK_DRAG_DIRECTION_LEFT_RIGHT; + bool up_down = U.click_drag_direction & USER_CLICK_DRAG_DIRECTION_UP_DOWN; + int theta = left_right ? + round_fl_to_int(atan2f(0.0f, (float)delta[0]) / (float)M_PI) : + up_down ? + round_fl_to_int(atan2f(0.0f, (float)delta[1]) / (float)M_PI) : + round_fl_to_int(4.0f * atan2f((float)delta[1], (float)delta[0]) / (float)M_PI); + int val = up_down ? KM_DIRECTION_S : KM_DIRECTION_W; - if (theta == 0) { - val = KM_DIRECTION_E; + if (left_right || up_down) { + if (theta == 0) { + val = up_down ? KM_DIRECTION_N : KM_DIRECTION_E; + } } - else if (theta == 1) { - val = KM_DIRECTION_NE; + else { + if (theta == 0) { + val = KM_DIRECTION_E; + } + else if (theta == 1) { + val = KM_DIRECTION_NE; + } + else if (theta == 2) { + val = KM_DIRECTION_N; + } + else if (theta == 3) { + val = KM_DIRECTION_NW; + } + else if (theta == -1) { + val = KM_DIRECTION_SE; + } + else if (theta == -2) { + val = KM_DIRECTION_S; + } + else if (theta == -3) { + val = KM_DIRECTION_SW; + } } - else if (theta == 2) { - val = KM_DIRECTION_N; - } - else if (theta == 3) { - val = KM_DIRECTION_NW; - } - else if (theta == -1) { - val = KM_DIRECTION_SE; - } - else if (theta == -2) { - val = KM_DIRECTION_S; - } - else if (theta == -3) { - val = KM_DIRECTION_SW; + + if (U.drag_select_control < 1) { + int box = ts->box_drag_direction; + int lasso = ts->lasso_drag_direction; + + if (box > 1) { + ts->box_direction_upright = false; + theta = box == 2 ? round_fl_to_int(atan2f(0.0f, (float)delta[0]) / (float)M_PI) : + round_fl_to_int(atan2f(0.0f, (float)delta[1]) / (float)M_PI); + if (theta == 0) { + ts->box_direction_upright = true; + } + } + if (lasso > 1) { + ts->lasso_direction_upright = false; + theta = lasso == 2 ? round_fl_to_int(atan2f(0.0f, (float)delta[0]) / (float)M_PI) : + round_fl_to_int(atan2f(0.0f, (float)delta[1]) / (float)M_PI); + if (theta == 0) { + ts->lasso_direction_upright = true; + } + } } #if 0 diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index 52e0f43567e..72c750be060 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -1292,6 +1292,11 @@ int WM_operator_repeat(bContext *C, wmOperator *op) op->flag &= ~op_flag; return ret; } +int WM_operator_repeat_tool(bContext *C, wmOperator *op) +{ + const int ret = WM_operator_name_call_ptr(C, op->type, WM_OP_INVOKE_DEFAULT, NULL, NULL); + return ret; +} int WM_operator_repeat_last(bContext *C, wmOperator *op) { const int op_flag = OP_IS_REPEAT_LAST; @@ -3432,7 +3437,8 @@ static eHandlerActionFlag wm_handlers_do(bContext *C, wmEvent *event, ListBase * if ((event->flag & WM_EVENT_FORCE_DRAG_THRESHOLD) || WM_event_drag_test(event, event->prev_press_xy)) { win->event_queue_check_drag_handled = true; - const int direction = WM_event_drag_direction(event); + ToolSettings *ts = CTX_data_tool_settings(C); + const int direction = WM_event_drag_direction(event, ts); /* Intentionally leave `event->xy` as-is, event users are expected to use * `event->prev_press_xy` if they need to access the drag start location. */ diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index ae8c446bd87..d9ecd122b19 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -29,6 +29,7 @@ #include "ED_screen.h" #include "ED_select_utils.h" +#include "ED_view3d.h" #include "UI_interface.h" @@ -85,6 +86,18 @@ static void gesture_modal_state_to_operator(wmOperator *op, int modal_state) } } +static void gesture_toggle_xray(bContext *C) +{ + wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_toggle_xray", true); + ToolSettings *ts = CTX_data_tool_settings(C); + BLI_assert(ot); + PointerRNA ptr; + WM_operator_properties_create_ptr(&ptr, ot); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, NULL); + WM_operator_properties_free(&ptr); + ts->auto_xray_reset ^= true; +} + static int UNUSED_FUNCTION(gesture_modal_state_from_operator)(wmOperator *op) { PropertyRNA *prop; @@ -163,9 +176,33 @@ static bool gesture_box_apply(bContext *C, wmOperator *op) int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); + View3D *v3d = CTX_wm_view3d(C); + Object *obedit = CTX_data_edit_object(C); + ToolSettings *ts = CTX_data_tool_settings(C); const ARegion *region = CTX_wm_region(C); const bool wait_for_input = !WM_event_is_mouse_drag_or_press(event) && RNA_boolean_get(op->ptr, "wait_for_input"); + + bool auto_xray = false; + if (U.drag_select_control & USER_DRAG_SELECT_KEYMAP) { + const int auto_xray_int = RNA_enum_get(op->ptr, "auto_xray"); + auto_xray = obedit ? auto_xray_int > 2 : auto_xray_int == 2 || auto_xray_int == 8; + } + else { + auto_xray = ts->auto_xray && ts->auto_xray_box ? + obedit ? ts->auto_xray_edit : ts->auto_xray_object : + false; + } + + if (ts->auto_xray_reset) { + ts->auto_xray_reset ^= true; + } + + if (v3d && auto_xray) { + if (!XRAY_FLAG_ENABLED(v3d)) { + gesture_toggle_xray(C); + } + } if (wait_for_input) { op->customdata = WM_gesture_new(win, region, event, WM_GESTURE_CROSS_RECT); @@ -190,8 +227,11 @@ int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event) int WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); + View3D *v3d = CTX_wm_view3d(C); wmGesture *gesture = op->customdata; rcti *rect = gesture->customdata; + Object *obedit = CTX_data_edit_object(C); + ToolSettings *ts = CTX_data_tool_settings(C); if (event->type == EVT_MODAL_MAP) { switch (event->val) { @@ -214,13 +254,22 @@ int WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event) gesture->modal_state = event->val; } if (gesture_box_apply(C, op)) { + if (ts->auto_xray_reset) { + gesture_toggle_xray(C); + } gesture_modal_end(C, op); return OPERATOR_FINISHED; } + if (ts->auto_xray_reset) { + gesture_toggle_xray(C); + } gesture_modal_end(C, op); return OPERATOR_CANCELLED; } case GESTURE_MODAL_CANCEL: { + if (ts->auto_xray_reset) { + gesture_toggle_xray(C); + } gesture_modal_end(C, op); return OPERATOR_CANCELLED; } @@ -286,8 +335,31 @@ static void gesture_circle_apply(bContext *C, wmOperator *op); int WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); + View3D *v3d = CTX_wm_view3d(C); const bool wait_for_input = !WM_event_is_mouse_drag_or_press(event) && RNA_boolean_get(op->ptr, "wait_for_input"); + Object *obedit = CTX_data_edit_object(C); + ToolSettings *ts = win->scene->toolsettings; + bool auto_xray = false; + if (U.drag_select_control & USER_DRAG_SELECT_KEYMAP) { + const int auto_xray_int = RNA_enum_get(op->ptr, "auto_xray"); + auto_xray = obedit ? auto_xray_int > 2 : auto_xray_int == 2 || auto_xray_int == 8; + } + else { + auto_xray = ts->auto_xray && ts->auto_xray_circle ? + obedit ? ts->auto_xray_edit : ts->auto_xray_object : + false; + } + + if (ts->auto_xray_reset) { + ts->auto_xray_reset ^= true; + } + + if (v3d && auto_xray) { + if (!XRAY_FLAG_ENABLED(v3d)) { + gesture_toggle_xray(C); + } + } op->customdata = WM_gesture_new(win, CTX_wm_region(C), event, WM_GESTURE_CIRCLE); wmGesture *gesture = op->customdata; @@ -345,11 +417,14 @@ static void gesture_circle_apply(bContext *C, wmOperator *op) int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); + View3D *v3d = CTX_wm_view3d(C); wmGesture *gesture = op->customdata; rcti *rect = gesture->customdata; - if (event->type == MOUSEMOVE) { + Object *obedit = CTX_data_edit_object(C); + ToolSettings *ts = win->scene->toolsettings; + if (event->type == MOUSEMOVE) { rect->xmin = event->xy[0] - gesture->winrct.xmin; rect->ymin = event->xy[1] - gesture->winrct.ymin; @@ -415,6 +490,9 @@ int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (is_finished) { + if (ts->auto_xray_reset) { + gesture_toggle_xray(C); + } gesture_modal_end(C, op); return OPERATOR_FINISHED; /* use finish or we don't get an undo */ } @@ -477,7 +555,31 @@ void WM_OT_circle_gesture(wmOperatorType *ot) int WM_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); + View3D *v3d = CTX_wm_view3d(C); PropertyRNA *prop; + Object *obedit = CTX_data_edit_object(C); + ToolSettings *ts = win->scene->toolsettings; + + bool auto_xray = false; + if (U.drag_select_control & USER_DRAG_SELECT_KEYMAP) { + const int auto_xray_int = RNA_enum_get(op->ptr, "auto_xray"); + auto_xray = obedit ? auto_xray_int > 2 : auto_xray_int == 2 || auto_xray_int == 8; + } + else { + auto_xray = ts->auto_xray && ts->auto_xray_lasso ? + obedit ? ts->auto_xray_edit : ts->auto_xray_object : + false; + } + + if (ts->auto_xray_reset) { + ts->auto_xray_reset ^= true; + } + + if (v3d && auto_xray) { + if (!XRAY_FLAG_ENABLED(v3d)) { + gesture_toggle_xray(C); + } + } op->customdata = WM_gesture_new(win, CTX_wm_region(C), event, WM_GESTURE_LASSO); @@ -517,6 +619,10 @@ static int gesture_lasso_apply(bContext *C, wmOperator *op) int retval = OPERATOR_FINISHED; wmGesture *gesture = op->customdata; PointerRNA itemptr; + View3D *v3d = CTX_wm_view3d(C); + Object *obedit = CTX_data_edit_object(C); + wmWindow *win = CTX_wm_window(C); + ToolSettings *ts = win->scene->toolsettings; float loc[2]; int i; const short *lasso = gesture->customdata; @@ -538,12 +644,20 @@ static int gesture_lasso_apply(bContext *C, wmOperator *op) OPERATOR_RETVAL_CHECK(retval); } + if (ts->auto_xray_reset) { + gesture_toggle_xray(C); + } + return retval; } int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event) { wmGesture *gesture = op->customdata; + View3D *v3d = CTX_wm_view3d(C); + Object *obedit = CTX_data_edit_object(C); + wmWindow *win = CTX_wm_window(C); + ToolSettings *ts = win->scene->toolsettings; if (event->type == EVT_MODAL_MAP) { switch (event->val) { @@ -597,6 +711,9 @@ int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event) break; } case EVT_ESCKEY: { + if (ts->auto_xray_reset) { + gesture_toggle_xray(C); + } gesture_modal_end(C, op); return OPERATOR_CANCELLED; } diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c index bd3322a8023..1004d953199 100644 --- a/source/blender/windowmanager/intern/wm_operator_props.c +++ b/source/blender/windowmanager/intern/wm_operator_props.c @@ -411,8 +411,93 @@ void WM_operator_properties_gesture_box_ex(wmOperatorType *ot, bool deselect, bo { PropertyRNA *prop; + static const EnumPropertyItem face_select_items[] = { + {FACE_AUTO, + "FACE_AUTO", + 0, + "Auto", + "Select faces that are touched by the selection area in near select. Select faces whose " + "center is touched by the selection area in X-Ray select"}, + {FACE_TOUCH, + "FACE_TOUCH", + 0, + "Touch", + "Select faces that are touched by the selection area"}, + {FACE_ENCLOSE, + "FACE_ENCLOSE", + 0, + "Enclose", + "Select faces that are fully inside the selection area"}, + {FACE_CENTER, + "FACE_CENTER", + 0, + "Center", + "Select faces whose center is touched by the selection area"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem edge_select_items[] = { + {EDGE_HYBRID, + "EDGE_HYBRID", + 0, + "Hybrid", + "Select edges that are fully inside the selection area. If no edges are fully inside the " + "selection area, select edges that are touched by the selection area"}, + {EDGE_TOUCH, + "EDGE_TOUCH", + 0, + "Touch", + "Select edges that are touched by the selection area"}, + {EDGE_ENCLOSE, + "EDGE_ENCLOSE", + 0, + "Enclose", + "Select edges that are fully inside the selection area"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem auto_xray_items[] = { + {AUTO_XRAY_DISABLE, "AUTO_XRAY_DISABLE", 0, "Disable", "Disable Automatic X-Ray"}, + {AUTO_XRAY_OBJECT, + "AUTO_XRAY_OBJECT", + 0, + "Object", + "Enable X-Ray during box select in object mode"}, + {AUTO_XRAY_EDIT, "AUTO_XRAY_EDIT", 0, "Edit", "Enable X-Ray during box select in edit mode"}, + {AUTO_XRAY_BOTH, + "AUTO_XRAY_BOTH", + 0, + "Both", + "Enable X-Ray during box select in object and edit mode"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem select_through_items[] = { + {SELECT_THROUGH_OBJECT, "SELECT_THROUGH_OBJECT", 0, "Object", "Select occluded objects"}, + {SELECT_THROUGH_EDIT, "SELECT_THROUGH_EDIT", 0, "Edit", "Select occluded mesh elements"}, + {SELECT_THROUGH_BOTH, + "SELECT_THROUGH_BOTH", + 0, + "Both", + "Select occluded objects and mesh elements"}, + {SELECT_THROUGH_DISABLE, "SELECT_THROUGH_DISABLE", 0, "Disable", "Disable Select Through"}, + {0, NULL, 0, NULL, NULL}, + }; + WM_operator_properties_border(ot); + prop = RNA_def_boolean(ot->srna, "select_origin_box", false, "Select Object Origin", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_enum(ot->srna, "face_type", face_select_items, 0, "Face Select", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "edge_type", edge_select_items, 0, "Edge Select", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "auto_xray", auto_xray_items, 0, "Automatic X-Ray", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "select_through", select_through_items, 0, "Select Through", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + if (deselect) { prop = RNA_def_boolean( ot->srna, "deselect", false, "Deselect", "Deselect rather than select items"); @@ -440,7 +525,8 @@ void WM_operator_properties_use_cursor_init(wmOperatorType *ot) void WM_operator_properties_gesture_box_select(wmOperatorType *ot) { - WM_operator_properties_gesture_box_ex(ot, true, true); + WM_operator_properties_gesture_box_ex( + ot, true, true); } void WM_operator_properties_gesture_box(wmOperatorType *ot) { @@ -519,6 +605,93 @@ void WM_operator_properties_gesture_box_zoom(wmOperatorType *ot) void WM_operator_properties_gesture_lasso(wmOperatorType *ot) { PropertyRNA *prop; + static const EnumPropertyItem face_select_items[] = { + {FACE_AUTO, + "FACE_AUTO", + 0, + "Auto", + "Select faces that are touched by the selection area in near select. Select faces whose " + "center is touched by the selection area in X-Ray select"}, + {FACE_TOUCH, + "FACE_TOUCH", + 0, + "Touch", + "Select faces that are touched by the selection area"}, + {FACE_ENCLOSE, + "FACE_ENCLOSE", + 0, + "Enclose", + "Select faces that are fully inside the selection area"}, + {FACE_CENTER, + "FACE_CENTER", + 0, + "Center", + "Select faces whose center is touched by the selection area"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem edge_select_items[] = { + {EDGE_HYBRID, + "EDGE_HYBRID", + 0, + "Hybrid", + "Select edges that are fully inside the selection area. If no edges are fully inside the " + "selection area, select edges that are touched by the selection area"}, + {EDGE_TOUCH, + "EDGE_TOUCH", + 0, + "Touch", + "Select edges that are touched by the selection area"}, + {EDGE_ENCLOSE, + "EDGE_ENCLOSE", + 0, + "Enclose", + "Select edges that are fully inside the selection area"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem auto_xray_items[] = { + {AUTO_XRAY_DISABLE, "AUTO_XRAY_DISABLE", 0, "Disable", "Disable Automatic X-Ray"}, + {AUTO_XRAY_OBJECT, + "AUTO_XRAY_OBJECT", + 0, + "Object", + "Enable X-Ray during lasso select in object mode"}, + {AUTO_XRAY_EDIT, + "AUTO_XRAY_EDIT", + 0, + "Edit", + "Enable X-Ray during lasso select in edit mode"}, + {AUTO_XRAY_BOTH, + "AUTO_XRAY_BOTH", + 0, + "Both", + "Enable X-Ray during lasso select in object and edit mode"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem select_through_items[] = { + {SELECT_THROUGH_OBJECT, "SELECT_THROUGH_OBJECT", 0, "Object", "Select occluded objects"}, + {SELECT_THROUGH_EDIT, "SELECT_THROUGH_EDIT", 0, "Edit", "Select occluded mesh elements"}, + {SELECT_THROUGH_BOTH, + "SELECT_THROUGH_BOTH", + 0, + "Both", + "Select occluded objects and mesh elements"}, + {SELECT_THROUGH_DISABLE, "SELECT_THROUGH_DISABLE", 0, "Disable", "Disable Select Through"}, + {0, NULL, 0, NULL, NULL}, + }; + + WM_operator_properties_border(ot); + + prop = RNA_def_enum(ot->srna, "face_type", face_select_items, 0, "Face Select", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "edge_type", edge_select_items, 0, "Edge Select", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "auto_xray", auto_xray_items, 0, "Automatic X-Ray", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "select_through", select_through_items, 0, "Select Through", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } @@ -557,6 +730,81 @@ void WM_operator_properties_gesture_circle(wmOperatorType *ot) PropertyRNA *prop; const int radius_default = 25; + static const EnumPropertyItem face_select_items[] = { + {FACE_AUTO, + "FACE_AUTO", + 0, + "Auto", + "Select faces that are touched by the selection area in near select. Select faces whose " + "center is touched by the selection area in X-Ray select"}, + {FACE_TOUCH, + "FACE_TOUCH", + 0, + "Touch", + "Select faces that are touched by the selection area"}, + {FACE_ENCLOSE, + "FACE_ENCLOSE", + 0, + "Enclose", + "Select faces that are fully inside the selection area"}, + {FACE_CENTER, + "FACE_CENTER", + 0, + "Center", + "Select faces whose center is touched by the selection area"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem edge_select_items[] = { + {EDGE_TOUCH, + "EDGE_TOUCH", + 0, + "Touch", + "Select edges that are touched by the selection area"}, + {EDGE_ENCLOSE, + "EDGE_ENCLOSE", + 0, + "Enclose", + "Select edges that are fully inside the selection area"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem auto_xray_items[] = { + {AUTO_XRAY_DISABLE, "AUTO_XRAY_DISABLE", 0, "Disable", "Disable Automatic X-Ray"}, + {AUTO_XRAY_OBJECT, + "AUTO_XRAY_OBJECT", + 0, + "Object", + "Enable X-Ray during circle select in object mode"}, + {AUTO_XRAY_EDIT, "AUTO_XRAY_EDIT", 0, "Edit", "Enable X-Ray during circle select in edit mode"}, + {AUTO_XRAY_BOTH, + "AUTO_XRAY_BOTH", + 0, + "Both", + "Enable X-Ray during circle select in object and edit mode"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem select_through_items[] = { + {SELECT_THROUGH_OBJECT, + "SELECT_THROUGH_OBJECT", + 0, + "Object", + "Select occluded objects"}, + {SELECT_THROUGH_EDIT, + "SELECT_THROUGH_EDIT", + 0, + "Edit", + "Select occluded mesh elements"}, + {SELECT_THROUGH_BOTH, + "SELECT_THROUGH_BOTH", + 0, + "Both", + "Select occluded objects and mesh elements"}, + {SELECT_THROUGH_DISABLE, "SELECT_THROUGH_DISABLE", 0, "Disable", "Disable Select Through"}, + {0, NULL, 0, NULL, NULL}, + }; + prop = RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); prop = RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX); @@ -565,6 +813,18 @@ void WM_operator_properties_gesture_circle(wmOperatorType *ot) prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_boolean(ot->srna, "select_origin_circle", true, "Select Object Origin", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_enum(ot->srna, "face_type", face_select_items, 0, "Face Select", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "edge_type", edge_select_items, 0, "Edge Select", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "auto_xray", auto_xray_items, 0, "Automatic X-Ray", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "select_through", select_through_items, 0, "Select Through", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } void WM_operator_properties_mouse_select(wmOperatorType *ot)