Skip to content

Instantly share code, notes, and snippets.

@jmiskovic
Last active April 5, 2024 09:55
Show Gist options
  • Save jmiskovic/0f85da8132912c5ef0134449aa4f559c to your computer and use it in GitHub Desktop.
Save jmiskovic/0f85da8132912c5ef0134449aa4f559c to your computer and use it in GitHub Desktop.
Example of 3rd person camera setup
local cam = require'cam' -- https://github.com/jmiskovic/lovr-cam
local player_pos = Vec3()
local player_vel = Vec3(0, 0, -0.01)
local function getWorldFromScreen(pass)
local w, h = pass:getDimensions()
local clip_from_screen = mat4(-1, -1, 0):scale(2 / w, 2 / h, 1)
local view_pose = mat4(pass:getViewPose(1))
local view_proj = pass:getProjection(1, mat4())
return view_pose:mul(view_proj:invert()):mul(clip_from_screen)
end
local function getRay(world_from_screen, distance)
local NEAR_PLANE = 0.01
distance = distance or 1e3
local ray = {}
local x, y = lovr.system.getMousePosition()
ray.origin = vec3(world_from_screen:mul(x, y, NEAR_PLANE / NEAR_PLANE))
ray.target = vec3(world_from_screen:mul(x, y, NEAR_PLANE / distance))
return ray
end
local function mouseOnGround(ray)
if ray.origin:distance(ray.target) < 1e-2 then
return vec3(0, 0, 0)
end
local ray_direction = (ray.target - ray.origin):normalize()
-- intersect the ray onto ground plane
local plane_direction = vec3(0, 1, 0)
local dot = ray_direction:dot(plane_direction)
if dot == 0 then
return vec3(0, 0, 0)
end
local ray_length = (-ray.origin):dot(plane_direction) / dot
local hit_spot = ray.origin + ray_direction * ray_length
return hit_spot
end
function lovr.draw(pass)
pass:text('Hold right\nmouse button\nto move\ntoward it', -2, 0.05, 0, 0.5, -math.pi/2, 1,0,0)
if lovr.system.isMouseDown(2) then
local world_from_screen = getWorldFromScreen(pass)
local ray = getRay(world_from_screen)
local spot = mouseOnGround(ray)
local dt = lovr.timer.getDelta()
player_vel:add((spot - player_pos) * dt)
player_pos:add(player_vel * dt)
player_vel:mul(0.95)
end
pass:setColor(0x101010)
pass:plane(0, 0, 0, 20, 20, -math.pi/2, 1,0,0)
pass:setColor(0x505050)
pass:plane(0, 0.01, 0, 20, 20, -math.pi/2, 1,0,0, 'line', 100, 100)
pass:setColor(0xD0A010)
pass:capsule(player_pos, player_pos + vec3(0, 0.4, 0), 0.3)
local player_azimuth = math.atan2(player_vel.z, player_vel.x)
pass:setColor(0x804000)
pass:cone(player_pos, 0.4, 0.5, -player_azimuth - math.pi/2, 0,1,0)
cam.center:lerp(player_pos, 0.1)
d_azimuth = player_azimuth - cam.azimuth + math.pi
-- add sign to angle, so that it's faster way around to reach the player's azimuth
d_azimuth = (d_azimuth + math.pi) % (2 * math.pi) - math.pi
cam.nudge(d_azimuth * 0.005)
end
cam.integrate()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment