Skip to content

Raycasting

Raycasting shoots a ray through the scene and returns what it hits. Use it for object picking (click-to-select), collision queries, and spatial analysis.

local ray = pl.raycast

All ray functions return a hit table (or nil if nothing was hit):

{
node = Node, -- the node that was hit
point = vec3, -- world-space intersection point
normal = vec3, -- surface normal at the hit point
distance = number, -- distance from ray origin to hit
}

Cast a ray from a camera through a screen pixel — ideal for mouse/touch picking.

local cam = scene:get_active_camera()
local hit = ray.from_screen(cam, screen_x, screen_y)
if hit then
print("Clicked on: " .. hit.node.name)
print("Distance: " .. hit.distance)
end

screen_x and screen_y are pixel coordinates relative to the viewport.

Cast a ray from a point in a direction in world space.

local origin = { x = 0, y = 5, z = 0 }
local direction = { x = 0, y = -1, z = 0 } -- straight down
local hit = ray.cast(origin, direction, 100) -- max distance = 100

The max_distance parameter is optional. If omitted, the ray extends infinitely.

Cast against a specific node (and its children) only — useful for targeted hit testing.

local character = scene:find_node("Character")
local hit = ray.cast_node(origin, direction, character)
if hit then
print("Hit " .. hit.node.name .. " at " .. hit.distance .. "m")
end

Return all intersections along a ray, sorted by distance (nearest first).

local hits = ray.cast_all(origin, direction, 200)
for i, hit in ipairs(hits) do
print(string.format(
"#%d: %s at %.2fm",
i, hit.node.name, hit.distance
))
end
local ray = pl.raycast
local cam = scene:get_active_camera()
-- Highlight the clicked object
local hit = ray.from_screen(cam, mouse_x, mouse_y)
if hit then
-- Reset previous selection
if selected then
selected:get_mesh():get_material().emissive = { r=0, g=0, b=0 }
end
-- Highlight new selection
selected = hit.node
local mat = selected:get_mesh():get_material()
mat.emissive = { r = 0.3, g = 0.5, b = 1.0 }
mat.emissive_strength = 0.5
print("Selected: " .. selected.name)
end
-- Cast downward to find the ground position below a point
local function snap_to_ground(node)
local pos = node.world_position
local hit = ray.cast(
{ x = pos.x, y = pos.y + 10, z = pos.z }, -- start above
{ x = 0, y = -1, z = 0 }, -- cast down
50
)
if hit then
node.position = hit.point
end
end