I created a script that allows you to view different types of ray_cast in Blender.
After you choose a type and press to view, you will see a square on the location of the “Ray_Cast”
This works fine, but my goal in making this script was to test the performance of each ray_cast. Objective not yet achieved.
So help me, which one is faster?
import bpy, bgl
from mathutils import Vector, Matrix, bvhtree
def get_viewport():
view = bgl.Buffer(bgl.GL_INT, 4)
bgl.glGetIntegerv(bgl.GL_VIEWPORT, view)
return view
def get_modelview_matrix():
model_matrix = bgl.Buffer(bgl.GL_DOUBLE, [4, 4])
bgl.glGetDoublev(bgl.GL_MODELVIEW_MATRIX, model_matrix)
return model_matrix
def get_projection_matrix():
proj_matrix = bgl.Buffer(bgl.GL_DOUBLE, [4, 4])
bgl.glGetDoublev(bgl.GL_PROJECTION_MATRIX, proj_matrix)
return proj_matrix
def get_depth(x, y):
depth = bgl.Buffer(bgl.GL_FLOAT, [0.0])
bgl.glReadPixels(x, y, 1, 1, bgl.GL_DEPTH_COMPONENT, bgl.GL_FLOAT, depth)
return depth[0]
def UnProject(x, y, z, view_matrix, projection_matrix, viewport):
world_x = bgl.Buffer(bgl.GL_DOUBLE, 1, [0.0])
world_y = bgl.Buffer(bgl.GL_DOUBLE, 1, [0.0])
world_z = bgl.Buffer(bgl.GL_DOUBLE, 1, [0.0])
bgl.gluUnProject(x, y, z, view_matrix, projection_matrix, viewport, world_x, world_y, world_z)
return Vector((world_x[0], world_y[0], world_z[0]))
def region_2d_to_orig_and_view_vector(region, rv3d, coord, clamp=None):
viewinv = rv3d.view_matrix.inverted()
persinv = rv3d.perspective_matrix.inverted()
dx = (2.0 * coord[0] / region.width) - 1.0
dy = (2.0 * coord[1] / region.height) - 1.0
if rv3d.is_perspective:
origin_start = viewinv.translation.copy()
out = Vector((dx, dy, -0.5))
w = out.dot(persinv[3].xyz) + persinv[3][3]
view_vector = ((persinv * out) / w) - origin_start
else:
view_vector = -viewinv.col[2].xyz
origin_start = ((persinv.col[0].xyz * dx) +
(persinv.col[1].xyz * dy) +
viewinv.translation)
if clamp != 0.0:
if rv3d.view_perspective != 'CAMERA':
# this value is scaled to the far clip already
origin_offset = persinv.col[2].xyz
if clamp is not None:
if clamp < 0.0:
origin_offset.negate()
clamp = -clamp
if origin_offset.length > clamp:
origin_offset.length = clamp
origin_start -= origin_offset
view_vector.normalize()
return origin_start, view_vector
def draw_point_bgl(coords):
# VIEWING BGL
bgl.glEnable(bgl.GL_BLEND)
bgl.glColor4f(1.0, 0.5, 0.5, 1.0)
bgl.glPointSize(50)
bgl.glBegin(bgl.GL_POINTS)
bgl.glVertex3f(*coords)
bgl.glEnd()
bgl.glDisable(bgl.GL_BLEND)
# restore opengl defaults
bgl.glDepthRange(0,1)
bgl.glPointSize(1)
bgl.glLineWidth(1)
bgl.glDisable(bgl.GL_BLEND)
bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
bpy.context.area.header_text_set(str(coords))
def draw_ray_cast_unproject_bpy(self, context):
x, y = self.mouse_co
# BPY Matrices
bpy_viewport = [self.region.x, self.region.y, self.region.width, self.region.height]
bpy_view_matrix = self.rv3d.view_matrix.transposed()
bpy_projection_matrix = self.rv3d.window_matrix.transposed()
# BPY Buffer Matrices
buffer_bpy_viewport = bgl.Buffer(bgl.GL_INT, 4, bpy_viewport)
buffer_bpy_view_matrix = bgl.Buffer(bgl.GL_DOUBLE, [4,4], bpy_view_matrix)
buffer_bpy_projection_matrix = bgl.Buffer(bgl.GL_DOUBLE, [4,4], bpy_projection_matrix)
# TESTING BUFFERS
DEPTH = get_depth(x, y)
RAY_CAST_W_BPY = UnProject(x, y, DEPTH, buffer_bpy_view_matrix, buffer_bpy_projection_matrix, buffer_bpy_viewport)
draw_point_bgl(RAY_CAST_W_BPY)
def draw_ray_cast_unproject_bgl(self, context):
x, y = self.mouse_co
# BGL Buffer Matrices
buffer_bgl_viewport = get_viewport()
buffer_bgl_view_matrix = get_modelview_matrix()
buffer_bgl_projection_matrix = get_projection_matrix()
# TESTING BUFFERS
DEPTH = get_depth(x, y)
RAY_CAST_W_BGL = UnProject(x, y, DEPTH, buffer_bgl_view_matrix, buffer_bgl_projection_matrix, buffer_bgl_viewport)
draw_point_bgl(RAY_CAST_W_BGL)
def draw_object_ray_cast(self, context):
"""Run this function on left mouse, execute the ray cast"""
# get the context arguments
x, y = self.mouse_co
# get the ray from the viewport and mouse
ray_origin, view_vector = region_2d_to_orig_and_view_vector(self.region, self.rv3d, (x, y))
ray_target = ray_origin + (view_vector * 1000)
self.obj = context.active_object
"""Wrapper for ray casting that moves the ray into object space"""
# get the ray relative to the object
matrix_inv = self.matrix.inverted()
ray_origin_obj = matrix_inv * ray_origin
ray_target_obj = matrix_inv * ray_target
# cast the ray
hit, normal, face_index = self.obj.ray_cast(ray_origin_obj, ray_target_obj)
hit_world = self.matrix * hit
draw_point_bgl(hit_world)
def draw_scene_ray_cast(self, context):
x, y = self.mouse_co
ray_origin, view_vector = region_2d_to_orig_and_view_vector(self.region, self.rv3d, (x, y))
ray_target = ray_origin + (view_vector * 1000)
scene = context.scene
# cast the ray
result, object, matrix, location, normal = scene.ray_cast(ray_origin, ray_target)
draw_point_bgl(location)
def draw_bvhtree_ray_cast(self, context):
x, y = self.mouse_co
ray_origin, view_vector = region_2d_to_orig_and_view_vector(self.region, self.rv3d, (x, y))
# get the ray relative to the object
matrix_inv = self.matrix.inverted()
ray_origin_obj = matrix_inv * ray_origin
ray_target_obj = matrix_inv * view_vector
# cast the ray
hit, normal, face_index, distance = self.tree.ray_cast(ray_origin_obj, view_vector)
if hit:
hit_world = self.matrix * hit
else:
hit_world = Vector()
draw_point_bgl(hit_world)
def show_depth(self, context):
x, y = self.mouse_co
depth = get_depth(x, y)
# VIEWING BGL
bgl.glEnable(bgl.GL_BLEND)
bgl.glColor4f(1.0, 0.5, 0.5, 1.0)
bgl.glPointSize(50)
bgl.glBegin(bgl.GL_POINTS)
bgl.glVertex2f(*self.mouse_co2)
bgl.glEnd()
bgl.glDisable(bgl.GL_BLEND)
# restore opengl defaults
bgl.glDepthRange(0,1)
bgl.glPointSize(1)
bgl.glLineWidth(1)
bgl.glDisable(bgl.GL_BLEND)
bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
bpy.context.area.header_text_set(str(depth))
class PanelTestsRayCast(bpy.types.Panel):
bl_space_type = "VIEW_3D"
bl_region_type = "TOOLS"
bl_category = "Test RayCast"
bl_label = "Test RayCast"
def draw(self, context):
layout = self.layout
layout.prop(context.scene, "raycast_type", expand=False)
TheCol = layout.column(align = True)
TheCol.operator("view3d.test_ray_cast")
class TestsRayCastOperator(bpy.types.Operator):
"""using mouse events"""
bl_idname = "view3d.test_ray_cast"
bl_label = "Show RayCast"
def modal(self, context, event):
if context.area:
context.area.tag_redraw()
if event.type == 'MOUSEMOVE':
if context.scene.raycast_type in {'0', '1'}:
self.mouse_co = (event.mouse_x, event.mouse_y)
elif context.scene.raycast_type in {'2', '3', '4'}:
self.mouse_co = (event.mouse_region_x, event.mouse_region_y)
elif context.scene.raycast_type in {'5'}:
self.mouse_co = (event.mouse_x, event.mouse_y)
self.mouse_co2 = (event.mouse_region_x, event.mouse_region_y)
elif event.type in {'RIGHTMOUSE', 'ESC'}:
bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
context.area.header_text_set()
return {'FINISHED'}
return {'PASS_THROUGH'}
def invoke(self, context, event):
if context.space_data.type == 'VIEW_3D':
self.rv3d = context.region_data
self.region = context.region
draw_handler = [
draw_ray_cast_unproject_bpy,
draw_ray_cast_unproject_bgl,
draw_object_ray_cast,
draw_scene_ray_cast,
draw_bvhtree_ray_cast,
show_depth,
][int(context.scene.raycast_type)]
self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_handler, (self, context), 'WINDOW', 'POST_VIEW')
if context.scene.raycast_type in {'2', '4'}:
self.obj = context.object
self.matrix = self.obj.matrix_world.copy()
if context.scene.raycast_type == '4':
self.tree = bvhtree.BVHTree.FromObject(self.obj, context.scene)
self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_handler, (self, context), 'WINDOW', 'POST_VIEW')
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(PanelTestsRayCast)
bpy.utils.register_class(TestsRayCastOperator)
bpy.types.Scene.raycast_type = bpy.props.EnumProperty(
name="Ray_Cast type",
description="Select ray cast type",
items=[("0","UnProject_Mat_bpy","Ray cast for reading pixel depth"),
("1","UnProject_Mat_bgl","Ray cast for reading pixel depth"),
("2","Object_ray_cast","Ray cast for intersect object geometry"),
("3","Scene_ray_cast","Ray cast for intersect all object geometry in scene"),
("4","BVHTree_ray_cast","Ray cast for intersect all object BVHTree"),
("5","bgl_depth","pixel depth"),
],
default='0')
def unregister():
bpy.utils.unregister_class(PanelTestsRayCast)
bpy.utils.unregister_class(TestsRayCastOperator)
del bpy.types.Scene.raycast_type
if __name__ == "__main__":
register()