diff options
Diffstat (limited to 'release/scripts/templates/operator_modal_view3d_raycast.py')
-rw-r--r-- | release/scripts/templates/operator_modal_view3d_raycast.py | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/release/scripts/templates/operator_modal_view3d_raycast.py b/release/scripts/templates/operator_modal_view3d_raycast.py new file mode 100644 index 00000000000..5d6b66a5188 --- /dev/null +++ b/release/scripts/templates/operator_modal_view3d_raycast.py @@ -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() |