0

Is there a way to select another object while in Edit Mode of a first object? I would like the first object to go out out of Edit mode and become unselected/inactive when another object is selected. That newly selected object could be in either Object or Edit mode, currently undecided. Is there a way to either code this ability or change the hotkeys somehow?

I know the preset way that Blender behaves is to not allow selecting another object when in Edit Mode of a first, unless they are joined etc. I'm looking for a slightly different workflow.

Increality
  • 411
  • 3
  • 14

1 Answers1

1

Change object in edit mode

import bpy
from bpy_extras import view3d_utils

def select_obj(context, event, deselect_first=True): if deselect_first: obj = context.object bpy.ops.object.mode_set(mode='OBJECT') ray_cast(context, event) if deselect_first: obj.select_set(False) bpy.ops.object.mode_set(mode='EDIT')

def ray_cast(context, event): """Run this function on left mouse, execute the ray cast""" # get the context arguments scene = context.scene region = context.region rv3d = context.region_data coord = event.mouse_region_x, event.mouse_region_y

# get the ray from the viewport and mouse
view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord)
ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord)

ray_target = ray_origin + view_vector

def visible_objects_and_duplis():
    """Loop over (object, matrix) pairs (mesh only)"""

    depsgraph = context.evaluated_depsgraph_get()
    for dup in depsgraph.object_instances:
        if dup.is_instance:  # Real dupli instance
            obj = dup.instance_object
            yield (obj, dup.matrix_world.copy())
        else:  # Usual object
            obj = dup.object
            yield (obj, obj.matrix_world.copy())

def obj_ray_cast(obj, matrix):
    """Wrapper for ray casting that moves the ray into object space"""

    # get the ray relative to the object
    matrix_inv = matrix.inverted()
    ray_origin_obj = matrix_inv @ ray_origin
    ray_target_obj = matrix_inv @ ray_target
    ray_direction_obj = ray_target_obj - ray_origin_obj

    # cast the ray
    success, location, normal, face_index = obj.ray_cast(ray_origin_obj, ray_direction_obj)

    if success:
        return location, normal, face_index
    else:
        return None, None, None

# cast rays and find the closest object
best_length_squared = -1.0
best_obj = None

for obj, matrix in visible_objects_and_duplis():
    if obj.type == 'MESH':
        hit, normal, face_index = obj_ray_cast(obj, matrix)
        if hit is not None:
            hit_world = matrix @ hit
            scene.cursor.location = hit_world
            length_squared = (hit_world - ray_origin).length_squared
            if best_obj is None or length_squared < best_length_squared:
                best_length_squared = length_squared
                best_obj = obj

# now we have the object under the mouse cursor,
# we could do lots of stuff but for the example just select.
if best_obj is not None:
    # for selection etc. we need the original object,
    # evaluated objects are not in viewlayer
    best_original = best_obj.original
    best_original.select_set(True)
    context.view_layer.objects.active = best_original


class ViewOperatorRayCast(bpy.types.Operator): """Modal object selection with a ray cast""" bl_idname = "view3d.modal_operator_raycast" bl_label = "RayCast View Operator"

def invoke(self, context, event):
    if context.space_data.type == 'VIEW_3D':
        if bpy.context.mode == 'EDIT_MESH':
            select_obj(context, event)
            return {'FINISHED'}
        else:
            self.report({'WARNING'}, "Edit Mode only")
            return {'CANCELLED'}
    else:
        self.report({'WARNING'}, "Active space must be a View3d")
        return {'CANCELLED'}


def register(): bpy.utils.register_class(ViewOperatorRayCast)

def unregister(): bpy.utils.unregister_class(ViewOperatorRayCast)

if name == "main": register()

How to Use

  1. run script
  2. go to edit mode
  3. mouse focus on other object
  4. run operator by search menu: RayCast View Operator

How to install as addon and align shortcut

How to duplicate parented objects as one object

X Y
  • 5,234
  • 1
  • 6
  • 20
  • Thank you. I'm adding to a script. How do I give it the capability to go back to object mode when selecting nothing? It would also be great that it only works when already in Edit Mode, but not while in Object mode. Let me know if I need to write this as a new question – Increality Nov 09 '22 at 22:38
  • 1
    add to line 79: else: bpy.ops.object.mode_set(mode='OBJECT') – X Y Nov 10 '22 at 06:19
  • This works great, but somehow it's conflicting with Box/Drag select (which is also set to Left Mouse). Only an issue in Edit Mode, not Object. Any thoughts? – Increality Dec 12 '22 at 01:16
  • 1
    The box select operator is triggered when the mouse is dragged. You can assign release left mouse to trigger your operator, and there will be no conflict. – X Y Dec 12 '22 at 09:15
  • Setting Release Left Mouse fixes the conflict with Box/Drag select, but simple direct clicks on edge/verts/polygons are no longer functioning (box select works though). The operator is set to Release, there's a "Select" set to LMB which is set to "Click", and then the Box Select which is set to "Click Drag". I tried setting both the Select and the Operator into various different Click/Press/Release types, but no fix. Select and Raycast Operator are under 3D View Global, Box Select is in its usual place, under 3D View - Object Mode – Increality Dec 26 '22 at 22:14
  • Since Left mouse Release is used for select vertex/edge/face in edit mode, you need assign other keystroke to your new operator. If you were to combine these two operators, it would be a cumbersome job.
    1. Check if the ray hits the current object. if true: do select vertex/edge/face else go to object mode select the object and go back
    – X Y Dec 28 '22 at 05:30