forked from bartvdbraak/blender
python template for doing mouse ray casts to pick objects in the 3d viewport.
This commit is contained in:
parent
b0dedf3d3f
commit
93cb7fb97b
107
release/scripts/templates/operator_modal_view3d_raycast.py
Normal file
107
release/scripts/templates/operator_modal_view3d_raycast.py
Normal file
@ -0,0 +1,107 @@
|
||||
import bpy
|
||||
from mathutils import Vector
|
||||
from bpy_extras import view3d_utils
|
||||
|
||||
|
||||
def main(context, event, ray_max=10000.0):
|
||||
"""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_x, event.mouse_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 * ray_max)
|
||||
|
||||
scene.cursor_location = ray_target
|
||||
|
||||
def visible_objects_and_duplis():
|
||||
"""Loop over (object, matrix) pairs (mesh only)"""
|
||||
|
||||
for obj in context.visible_objects:
|
||||
if obj.type == 'MESH':
|
||||
yield (obj, obj.matrix_world.copy())
|
||||
|
||||
if obj.dupli_type != 'NONE':
|
||||
obj.dupli_list_create(scene)
|
||||
for dob in obj.dupli_list:
|
||||
obj_dupli = dob.object
|
||||
if obj_dupli.type == 'MESH':
|
||||
yield (obj_dupli, dob.matrix.copy())
|
||||
|
||||
obj.dupli_list_clear()
|
||||
|
||||
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
|
||||
|
||||
# cast the ray
|
||||
hit, normal, face_index = obj.ray_cast(ray_origin_obj, ray_target_obj)
|
||||
|
||||
if face_index != -1:
|
||||
return hit, normal, face_index
|
||||
else:
|
||||
return None, None, None
|
||||
|
||||
# cast rays and find the closest object
|
||||
best_length_squared = ray_max * ray_max
|
||||
best_obj = None
|
||||
|
||||
for obj, matrix in visible_objects_and_duplis():
|
||||
if obj.type == 'MESH':
|
||||
hit, normal, face_index = obj_ray_cast(obj, obj.matrix_world)
|
||||
if hit is not None:
|
||||
length_squared = (hit - ray_origin).length_squared
|
||||
if 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:
|
||||
best_obj.select = True
|
||||
|
||||
|
||||
class ViewOperatorRayCast(bpy.types.Operator):
|
||||
"""Modal object selection with a ray cast"""
|
||||
bl_idname = "view3d.modal_operator_raycast"
|
||||
bl_label = "RayCast View Operator"
|
||||
|
||||
def modal(self, context, event):
|
||||
if event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}:
|
||||
# allow navigation
|
||||
return {'PASS_THROUGH'}
|
||||
elif event.type == 'LEFTMOUSE':
|
||||
main(context, event)
|
||||
return {'RUNNING_MODAL'}
|
||||
elif event.type in {'RIGHTMOUSE', 'ESC'}:
|
||||
return {'CANCELLED'}
|
||||
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
if context.space_data.type == 'VIEW_3D':
|
||||
context.window_manager.modal_handler_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
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()
|
Loading…
Reference in New Issue
Block a user