186 lines
5.5 KiB
Lua
186 lines
5.5 KiB
Lua
-- box definition below node boxes: https://github.com/minetest/minetest/blob/master/doc/lua_api.md#node-boxes
|
|
|
|
local x1 = 1
|
|
local y1 = 2
|
|
local z1 = 3
|
|
local x2 = 4
|
|
local y2 = 5
|
|
local z2 = 6
|
|
|
|
function futil.boxes_intersect(box1, box2)
|
|
return not (
|
|
(box1[x2] < box2[x1] or box2[x2] < box1[x1])
|
|
or (box1[y2] < box2[y1] or box2[y2] < box1[y1])
|
|
or (box1[z2] < box2[z1] or box2[z2] < box1[z1])
|
|
)
|
|
end
|
|
|
|
function futil.box_offset(box, number_or_vector)
|
|
if type(number_or_vector) == "number" then
|
|
return {
|
|
box[1] + number_or_vector,
|
|
box[2] + number_or_vector,
|
|
box[3] + number_or_vector,
|
|
box[4] + number_or_vector,
|
|
box[5] + number_or_vector,
|
|
box[6] + number_or_vector,
|
|
}
|
|
else
|
|
return {
|
|
box[1] + number_or_vector.x,
|
|
box[2] + number_or_vector.y,
|
|
box[3] + number_or_vector.z,
|
|
box[4] + number_or_vector.x,
|
|
box[5] + number_or_vector.y,
|
|
box[6] + number_or_vector.z,
|
|
}
|
|
end
|
|
end
|
|
|
|
function futil.is_box(box)
|
|
if type(box) == "table" and #box == 6 then
|
|
for _, x in ipairs(box) do
|
|
if type(x) ~= "number" then
|
|
return false
|
|
end
|
|
end
|
|
return box[1] <= box[4] and box[2] <= box[5] and box[3] <= box[6]
|
|
end
|
|
return false
|
|
end
|
|
|
|
function futil.is_boxes(boxes)
|
|
if type(boxes) ~= "table" or #boxes == 0 then
|
|
return false
|
|
end
|
|
|
|
for _, box in ipairs(boxes) do
|
|
if not futil.is_box(box) then
|
|
return false
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
-- given a set of boxes, return a single box that covers all of them
|
|
function futil.cover_boxes(boxes)
|
|
if not futil.is_boxes(boxes) then
|
|
return { 0, 0, 0, 0, 0, 0 }
|
|
end
|
|
|
|
local cover = boxes[1]
|
|
for i = 2, #boxes do
|
|
for j = 1, 3 do
|
|
cover[j] = math.min(cover[j], boxes[i][j])
|
|
end
|
|
for j = 4, 6 do
|
|
cover[j] = math.max(cover[j], boxes[i][j])
|
|
end
|
|
end
|
|
|
|
return cover
|
|
end
|
|
|
|
--[[
|
|
for nodes:
|
|
A nodebox is defined as any of:
|
|
|
|
{
|
|
-- A normal cube; the default in most things
|
|
type = "regular"
|
|
}
|
|
{
|
|
-- A fixed box (or boxes) (facedir param2 is used, if applicable)
|
|
type = "fixed",
|
|
fixed = box OR {box1, box2, ...}
|
|
}
|
|
{
|
|
-- A variable height box (or boxes) with the top face position defined
|
|
-- by the node parameter 'leveled = ', or if 'paramtype2 == "leveled"'
|
|
-- by param2.
|
|
-- Other faces are defined by 'fixed = {}' as with 'type = "fixed"'.
|
|
type = "leveled",
|
|
fixed = box OR {box1, box2, ...}
|
|
}
|
|
{
|
|
-- A box like the selection box for torches
|
|
-- (wallmounted param2 is used, if applicable)
|
|
type = "wallmounted",
|
|
wall_top = box,
|
|
wall_bottom = box,
|
|
wall_side = box
|
|
}
|
|
{
|
|
-- A node that has optional boxes depending on neighboring nodes'
|
|
-- presence and type. See also `connects_to`.
|
|
type = "connected",
|
|
fixed = box OR {box1, box2, ...}
|
|
connect_top = box OR {box1, box2, ...}
|
|
connect_bottom = box OR {box1, box2, ...}
|
|
connect_front = box OR {box1, box2, ...}
|
|
connect_left = box OR {box1, box2, ...}
|
|
connect_back = box OR {box1, box2, ...}
|
|
connect_right = box OR {box1, box2, ...}
|
|
-- The following `disconnected_*` boxes are the opposites of the
|
|
-- `connect_*` ones above, i.e. when a node has no suitable neighbor
|
|
-- on the respective side, the corresponding disconnected box is drawn.
|
|
disconnected_top = box OR {box1, box2, ...}
|
|
disconnected_bottom = box OR {box1, box2, ...}
|
|
disconnected_front = box OR {box1, box2, ...}
|
|
disconnected_left = box OR {box1, box2, ...}
|
|
disconnected_back = box OR {box1, box2, ...}
|
|
disconnected_right = box OR {box1, box2, ...}
|
|
disconnected = box OR {box1, box2, ...} -- when there is *no* neighbor
|
|
disconnected_sides = box OR {box1, box2, ...} -- when there are *no*
|
|
-- neighbors to the sides
|
|
}
|
|
|
|
for objects:
|
|
collisionbox = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 }, -- default
|
|
selectionbox = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, rotate = false },
|
|
-- { xmin, ymin, zmin, xmax, ymax, zmax } in nodes from object position.
|
|
-- Collision boxes cannot rotate, setting `rotate = true` on it has no effect.
|
|
-- If not set, the selection box copies the collision box, and will also not rotate.
|
|
-- If `rotate = false`, the selection box will not rotate with the object itself, remaining fixed to the axes.
|
|
-- If `rotate = true`, it will match the object's rotation and any attachment rotations.
|
|
-- Raycasts use the selection box and object's rotation, but do *not* obey attachment rotations
|
|
]]
|
|
|
|
futil.default_collision_box = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 }
|
|
|
|
function futil.node_collision_box_to_object_collisionbox(collision_box)
|
|
if type(collision_box) ~= "table" then
|
|
return table.copy(futil.default_collision_box)
|
|
elseif collision_box.type == "regular" then
|
|
return table.copy(futil.default_collision_box)
|
|
elseif collision_box.type == "fixed" or collision_box.type == "leveled" or collision_box.type == "connected" then
|
|
if futil.is_box(collision_box.fixed) then
|
|
return collision_box.fixed
|
|
elseif futil.is_boxes(collision_box.fixed) then
|
|
return futil.cover_boxes(collision_box.fixed)
|
|
else
|
|
return table.copy(futil.default_collision_box)
|
|
end
|
|
elseif collision_box.type == "wallmounted" then
|
|
local boxes = {}
|
|
if collision_box.wall_top then
|
|
table.insert(boxes, collision_box.wall_top)
|
|
end
|
|
if collision_box.wall_bottom then
|
|
table.insert(boxes, collision_box.wall_bottom)
|
|
end
|
|
if collision_box.wall_side then
|
|
table.insert(boxes, collision_box.wall_side)
|
|
end
|
|
return futil.cover_boxes(boxes)
|
|
else
|
|
return table.copy(futil.default_collision_box)
|
|
end
|
|
end
|
|
|
|
function futil.node_selection_box_to_object_selectionbox(selection_box, rotate)
|
|
local selectionbox = futil.node_collision_box_to_object_collisionbox(selection_box)
|
|
selectionbox.rotate = rotate or false
|
|
return selectionbox
|
|
end
|