0
$\begingroup$

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.

$\endgroup$

1 Answer 1

1
$\begingroup$

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

$\endgroup$
6
  • $\begingroup$ 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 $\endgroup$
    – Increality
    Nov 9, 2022 at 22:38
  • 1
    $\begingroup$ add to line 79: else: bpy.ops.object.mode_set(mode='OBJECT') $\endgroup$
    – X Y
    Nov 10, 2022 at 6:19
  • $\begingroup$ 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? $\endgroup$
    – Increality
    Dec 12, 2022 at 1:16
  • 1
    $\begingroup$ 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. $\endgroup$
    – X Y
    Dec 12, 2022 at 9:15
  • $\begingroup$ 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 $\endgroup$
    – Increality
    Dec 26, 2022 at 22:14

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .