3
$\begingroup$

I saw this question this morning and i'm also quite interested in a Picker modal operator, so I did tinker a bit and achieve a somewhat beginning of a working code but I encountered some problems along the way.

The code is functional in 3Dview, as you can see it will print objects detected under the cursor, the problem is that it needs to work from any areas! so we need some context override action here

(Note that, as final touch, we could draw a little icon on screen with the name of the object picked at cursor location. Not sure if this is possible cross area ? i believe custom drawing are only allowed within the 3D view correct?)

import bpy
from bpy_extras import view3d_utils

# rework of this 
# https://devtalk.blender.org/t/pick-material-under-mouse-cursor/6978/4


#We need the picker to work cross area, unfortunately blender don't like that very much
#So we need to override the context, 
#the function below will try to find 3D view and update the given context.copy()

def find_3d_context(context_copy):
    for area in bpy.context.window.screen.areas:
        if area.type == 'VIEW_3D':
            for region in area.regions:
                if region.type == 'WINDOW':
                    break 
            for space in area.spaces:
                if space.type == 'VIEW_3D':
                    region_data = space.region_3d
                    break
    print(area, region, region_data)
    context_copy.update(
        area=area,
        region=region,
        region_data=region_data,
        )
    return 
    

def find_object(context, event):
    
    # get the context arguments
    scene     = bpy.context.scene
    region    = bpy.context.region
    rv3d      = bpy.context.region_data
    coord     = event.mouse_region_x, event.mouse_region_y
    scene     = bpy.context.scene 
    viewlayer = bpy.context.view_layer
     
    #this is the tricky part, region will not be found if we are not running this operator from a 3DVIEW
    if rv3d is None: print("region_data not found") ; return 

    # 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 *-10)
    ray_goal    = ray_origin + (view_vector *1000)

    Rresult, Rlocation, Rnormal, Rindex, Robject, Rmatrix = scene.ray_cast(viewlayer.depsgraph, ray_target, ray_goal)
    
    if Robject is not None:
        print("Found an object ! : ",Robject)
    return Robject



class ObjectPicker(bpy.types.Operator):
    bl_idname = "view3d.test"
    bl_label = "Picker test"

    def modal(self, context, event):
    
        #should work from any area, so we need to find correct context
        context = context.copy()
        find_3d_context(context)
        
        object = find_object(context, event)
        
        if (event.type == 'LEFTMOUSE'):
            print("Execute Something With Object")
            print("Done")
            return {'CANCELLED'}
        
        elif event.type in {'RIGHTMOUSE', 'ESC','RET'}:
            print("Done")
            return {'CANCELLED'}
        
        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        context.window_manager.modal_handler_add(self)
        return {'RUNNING_MODAL'}



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

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

if __name__ == "__main__":
    register()
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.