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
- run script
- go to edit mode
- mouse focus on other object
- run operator by search menu:
RayCast View Operator
How to install as addon and align shortcut
How to duplicate parented objects as one object