|
|
|
|
|
|
|
local math_abs, math_sign
|
|
|
|
= math.abs, math.sign
|
|
|
|
local minetest, vector, include
|
|
|
|
= minetest, vector, include
|
|
|
|
|
|
|
|
local rotate = include("rotate")
|
|
|
|
|
|
|
|
local utility = {}
|
|
|
|
|
|
|
|
-- Default selection boxes for a node that doesn't have one them explicitly.
|
|
|
|
local DEFAULT_SELECTION_BOX = { min = vector.new(-0.5, -0.5, -0.5),
|
|
|
|
max = vector.new( 0.5, 0.5, 0.5) }
|
|
|
|
|
|
|
|
-- Gets the active `selection_box` for the specified `node` which
|
|
|
|
-- has a `paramtype2` of `facedir`, based on its `param2` value.
|
|
|
|
local function get_node_active_selection_box(node)
|
|
|
|
local def = minetest.registered_nodes[node.name]
|
|
|
|
local box = def.selection_box and def.selection_box.fixed
|
|
|
|
-- No need to rotate the default selection box.
|
|
|
|
if not box then return DEFAULT_SELECTION_BOX end
|
|
|
|
-- If node definition specifies multiple selection boxes, just pick the first (for now).
|
|
|
|
if type(box[1]) == "table" then box = box[1] end
|
|
|
|
-- Transform the `{ x1, y1, z1, x2, y2, z2 }` box to a `{ min, max }` one.
|
|
|
|
box = { min = vector.new(box[1], box[2], box[3]),
|
|
|
|
max = vector.new(box[4], box[5], box[6]) }
|
|
|
|
-- Rotate the box to face `facedir`.
|
|
|
|
rotate.rotate_box_by_facedir(box, node.param2)
|
|
|
|
return box
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Finds the closest edge of the node the player is looking at as
|
|
|
|
-- a vector pointing away from the center, and the distance to it.
|
|
|
|
local function find_closest_edge(node, pointed_thing)
|
|
|
|
-- For this math to work, we assume that selection box is centered.
|
|
|
|
local max = get_node_active_selection_box(node).max
|
|
|
|
-- Point relative to the collision box we're pointing at.
|
|
|
|
local point = pointed_thing.intersection_point - pointed_thing.under
|
|
|
|
|
|
|
|
-- Find the edge we're closest to.
|
|
|
|
local vec = vector.zero()
|
|
|
|
if math_abs(point.y / point.x) > max.x / max.y then
|
|
|
|
vec.y = math_sign(point.y)
|
|
|
|
if math_abs(point.z / point.x) > max.x / max.z
|
|
|
|
then vec.z = math_sign(point.z)
|
|
|
|
else vec.x = math_sign(point.x)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
vec.x = math_sign(point.x)
|
|
|
|
if math_abs(point.z / point.y) > max.y / max.z
|
|
|
|
then vec.z = math_sign(point.z)
|
|
|
|
else vec.y = math_sign(point.y)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local d = 1
|
|
|
|
local v = max - point:multiply(vec):apply(math_abs)
|
|
|
|
local EPSILON = tonumber("1.19e-07")
|
|
|
|
if math_abs(v.x) > EPSILON and v.x < d then d = v.x end
|
|
|
|
if math_abs(v.y) > EPSILON and v.y < d then d = v.y end
|
|
|
|
if math_abs(v.z) > EPSILON and v.z < d then d = v.z end
|
|
|
|
|
|
|
|
return vec, d
|
|
|
|
end
|
|
|
|
utility.find_closest_edge = find_closest_edge
|
|
|
|
|
|
|
|
return utility
|