Browse Source

Refactor entity picking to use built-in camera API (#143)

Artsiom Trubchyk 4 weeks ago
parent
commit
9491c3595f

+ 0 - 42
input/entity_picking/example/camera_math.lua

@@ -1,42 +0,0 @@
-local M = {}
-
---- Convert a point from 2D screen space to 3D world space. Supports only perspective.
--- @param x number X coordinate on screen.
--- @param y number Y coordinate on screen.
--- @param z number The distance from the camera in world space to create the new point.
--- @param camera_id url The camera URL to get params from.
--- @return vector3 The world coordinate.
-function M.screen_to_world(x, y, z, camera_id)
-	-- Camera properties
-	local projection = camera.get_projection(camera_id)
-	assert(projection.m33 == 0.0, "Camera must be in perspective mode")
-
-	local cw, ch = window.get_size()
-	local aspect_ratio = cw / ch
-	local near_z = camera.get_near_z(camera_id)
-	local fov = camera.get_fov(camera_id)
-	local inv_view = vmath.inv(camera.get_view(camera_id))
-
-	-- Calculate the screen click as a point on the far plane of the normalized device coordinate 'box' (z=1)
-	local ndc_x = x / cw * 2 - 1
-	local ndc_y = y / ch * 2 - 1
-
-	-- Calculate perspective projection matrix half size at the near plane
-	local half_size = vmath.vector4(0, 0, -near_z, 1)
-	local h = near_z * math.tan(fov / 2)
-	half_size.x = h * aspect_ratio * ndc_x
-	half_size.y = h * ndc_y
-
-	-- Transform to world space
-	local point = inv_view * half_size
-
-	-- Move to distance z from the camera
-	local world_coord = vmath.normalize(vmath.vector3(point.x - inv_view.m03, point.y - inv_view.m13, point.z - inv_view.m23))
-	world_coord.x = world_coord.x * z + inv_view.m03
-	world_coord.y = world_coord.y * z + inv_view.m13
-	world_coord.z = world_coord.z * z + inv_view.m23
-
-	return world_coord
-end
-
-return M

+ 6 - 5
input/entity_picking/example/entity_picking.script

@@ -1,13 +1,14 @@
-local camera_math = require("example.camera_math")
+go.property("camera_url", msg.url("/camera#camera"))
 
 --- Performs a raycast from the camera through a screen position to find an entity.
+-- @param camera_url url The camera URL to use for screen-to-world conversion
 -- @param screen_x number The x-coordinate on the screen
 -- @param screen_y number The y-coordinate on the screen
 -- @param collision_groups table The collision groups to check against as array of hash values
 -- @return table|nil The first entity hit by the ray, or nil if nothing was hit
-local function pick_entity(screen_x, screen_y, collision_groups)
-	local from = camera_math.screen_to_world(screen_x, screen_y, 0, "/camera#camera")
-	local to = camera_math.screen_to_world(screen_x, screen_y, 100, "/camera#camera")
+local function pick_entity(camera_url, screen_x, screen_y, collision_groups)
+	local from = camera.screen_to_world(vmath.vector3(screen_x, screen_y, 0), camera_url)
+	local to = camera.screen_to_world(vmath.vector3(screen_x, screen_y, 100), camera_url)
 	local results = physics.raycast(from, to, collision_groups, { all = false })
 	if not results then
 		return nil
@@ -34,7 +35,7 @@ function update(self, dt)
 		return
 	end
 
-	local result = pick_entity(self.last_input.screen_x, self.last_input.screen_y, { hash("target") })
+	local result = pick_entity(self.camera_url, self.last_input.screen_x, self.last_input.screen_y, { hash("target") })
 	if result then
 		-- Store in the result table the model URL of the entity just for convenience
 		result.model_url = msg.url(nil, result.id, "model")