Skip to content

Commit

Permalink
Add Lua API function to resolve node/collision/selection boxes (#13964)
Browse files Browse the repository at this point in the history
  • Loading branch information
grorp committed Feb 6, 2024
1 parent 4859cf4 commit f2b9933
Show file tree
Hide file tree
Showing 13 changed files with 159 additions and 28 deletions.
11 changes: 11 additions & 0 deletions doc/lua_api.md
Expand Up @@ -6183,6 +6183,17 @@ Environment access
* increase level of leveled node by level, default `level` equals `1`
* if `totallevel > maxlevel`, returns rest (`total-max`)
* `level` must be between -127 and 127
* `minetest.get_node_boxes(box_type, pos, [node])`
* `box_type` must be `"node_box"`, `"collision_box"` or `"selection_box"`.
* `pos` must be a node position.
* `node` can be a table in the form `{name=string, param1=number, param2=number}`.
If `node` is `nil`, the actual node at `pos` is used instead.
* Resolves any facedir-rotated boxes, connected boxes and the like into
actual boxes.
* Returns a list of boxes in the form
`{{x1, y1, z1, x2, y2, z2}, {x1, y1, z1, x2, y2, z2}, ...}`. Coordinates
are relative to `pos`.
* See also: [Node boxes](#node-boxes)
* `minetest.fix_light(pos1, pos2)`: returns `true`/`false`
* resets the light in a cuboid-shaped part of
the map and removes lighting bugs.
Expand Down
1 change: 1 addition & 0 deletions games/devtest/mods/testtools/init.lua
Expand Up @@ -6,6 +6,7 @@ testtools = {}
dofile(minetest.get_modpath("testtools") .. "/light.lua")
dofile(minetest.get_modpath("testtools") .. "/privatizer.lua")
dofile(minetest.get_modpath("testtools") .. "/particles.lua")
dofile(minetest.get_modpath("testtools") .. "/node_box_visualizer.lua")

local pointabilities_nodes = {
nodes = {
Expand Down
79 changes: 79 additions & 0 deletions games/devtest/mods/testtools/node_box_visualizer.lua
@@ -0,0 +1,79 @@
local S = minetest.get_translator("testtools")

minetest.register_entity("testtools:visual_box", {
initial_properties = {
visual = "cube",
textures = {
"blank.png", "blank.png", "blank.png",
"blank.png", "blank.png", "blank.png",
},
use_texture_alpha = true,
physical = false,
pointable = false,
static_save = false,
},

on_activate = function(self)
self.timestamp = minetest.get_us_time() + 5000000
end,

on_step = function(self)
if minetest.get_us_time() >= self.timestamp then
self.object:remove()
end
end,
})

local BOX_TYPES = {"node_box", "collision_box", "selection_box"}
local DEFAULT_BOX_TYPE = "selection_box"

local function visualizer_on_use(itemstack, user, pointed_thing)
if pointed_thing.type ~= "node" then
return
end

local meta = itemstack:get_meta()
local box_type = meta:get("box_type") or DEFAULT_BOX_TYPE

local result = minetest.get_node_boxes(box_type, pointed_thing.under)
local t = "testtools_visual_" .. box_type .. ".png"

for _, box in ipairs(result) do
local box_min = pointed_thing.under + vector.new(box[1], box[2], box[3])
local box_max = pointed_thing.under + vector.new(box[4], box[5], box[6])
local box_center = (box_min + box_max) / 2
local obj = minetest.add_entity(box_center, "testtools:visual_box")
if not obj then
break
end
obj:set_properties({
textures = {t, t, t, t, t, t},
-- Add a small offset to avoid Z-fighting.
visual_size = vector.add(box_max - box_min, 0.01),
})
end
end

local function visualizer_on_place(itemstack, placer, pointed_thing)
local meta = itemstack:get_meta()
local prev_value = meta:get("box_type") or DEFAULT_BOX_TYPE
local prev_index = table.indexof(BOX_TYPES, prev_value)
assert(prev_index ~= -1)

local new_value = BOX_TYPES[(prev_index % #BOX_TYPES) + 1]
meta:set_string("box_type", new_value)
minetest.chat_send_player(placer:get_player_name(), S("[Node Box Visualizer] box_type = @1", new_value))

return itemstack
end

minetest.register_tool("testtools:node_box_visualizer", {
description = S("Node Box Visualizer") .. "\n" ..
S("Punch: Show node/collision/selection boxes of the pointed node") .. "\n" ..
S("Place: Change selected box type (default: selection box)"),
inventory_image = "testtools_node_box_visualizer.png",
groups = { testtool = 1, disable_repair = 1 },
on_use = visualizer_on_use,
on_place = visualizer_on_place,
on_secondary_use = visualizer_on_place,
})
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 7 additions & 17 deletions src/script/common/c_content.cpp
Expand Up @@ -1110,7 +1110,7 @@ void push_nodebox(lua_State *L, const NodeBox &box)
case NODEBOX_FIXED:
lua_pushstring(L, "fixed");
lua_setfield(L, -2, "type");
push_box(L, box.fixed);
push_aabb3f_vector(L, box.fixed);
lua_setfield(L, -2, "fixed");
break;
case NODEBOX_WALLMOUNTED:
Expand All @@ -1127,17 +1127,17 @@ void push_nodebox(lua_State *L, const NodeBox &box)
lua_pushstring(L, "connected");
lua_setfield(L, -2, "type");
const auto &c = box.getConnected();
push_box(L, c.connect_top);
push_aabb3f_vector(L, c.connect_top);
lua_setfield(L, -2, "connect_top");
push_box(L, c.connect_bottom);
push_aabb3f_vector(L, c.connect_bottom);
lua_setfield(L, -2, "connect_bottom");
push_box(L, c.connect_front);
push_aabb3f_vector(L, c.connect_front);
lua_setfield(L, -2, "connect_front");
push_box(L, c.connect_back);
push_aabb3f_vector(L, c.connect_back);
lua_setfield(L, -2, "connect_back");
push_box(L, c.connect_left);
push_aabb3f_vector(L, c.connect_left);
lua_setfield(L, -2, "connect_left");
push_box(L, c.connect_right);
push_aabb3f_vector(L, c.connect_right);
lua_setfield(L, -2, "connect_right");
// half the boxes are missing here?
break;
Expand All @@ -1148,16 +1148,6 @@ void push_nodebox(lua_State *L, const NodeBox &box)
}
}

void push_box(lua_State *L, const std::vector<aabb3f> &box)
{
lua_createtable(L, box.size(), 0);
u8 i = 1;
for (const aabb3f &it : box) {
push_aabb3f(L, it);
lua_rawseti(L, -2, i++);
}
}

/******************************************************************************/
void push_palette(lua_State *L, const std::vector<video::SColor> *palette)
{
Expand Down
3 changes: 0 additions & 3 deletions src/script/common/c_content.h
Expand Up @@ -83,9 +83,6 @@ void push_content_features (lua_State *L,

void push_nodebox (lua_State *L,
const NodeBox &box);
void push_box (lua_State *L,
const std::vector<aabb3f> &box);

void push_palette (lua_State *L,
const std::vector<video::SColor> *palette);

Expand Down
24 changes: 17 additions & 7 deletions src/script/common/c_converter.cpp
Expand Up @@ -364,20 +364,20 @@ aabb3f read_aabb3f(lua_State *L, int index, f32 scale)
return box;
}

void push_aabb3f(lua_State *L, aabb3f box)
void push_aabb3f(lua_State *L, aabb3f box, f32 divisor)
{
lua_createtable(L, 6, 0);
lua_pushnumber(L, box.MinEdge.X);
lua_pushnumber(L, box.MinEdge.X / divisor);
lua_rawseti(L, -2, 1);
lua_pushnumber(L, box.MinEdge.Y);
lua_pushnumber(L, box.MinEdge.Y / divisor);
lua_rawseti(L, -2, 2);
lua_pushnumber(L, box.MinEdge.Z);
lua_pushnumber(L, box.MinEdge.Z / divisor);
lua_rawseti(L, -2, 3);
lua_pushnumber(L, box.MaxEdge.X);
lua_pushnumber(L, box.MaxEdge.X / divisor);
lua_rawseti(L, -2, 4);
lua_pushnumber(L, box.MaxEdge.Y);
lua_pushnumber(L, box.MaxEdge.Y / divisor);
lua_rawseti(L, -2, 5);
lua_pushnumber(L, box.MaxEdge.Z);
lua_pushnumber(L, box.MaxEdge.Z / divisor);
lua_rawseti(L, -2, 6);
}

Expand Down Expand Up @@ -409,6 +409,16 @@ std::vector<aabb3f> read_aabb3f_vector(lua_State *L, int index, f32 scale)
return boxes;
}

void push_aabb3f_vector(lua_State *L, const std::vector<aabb3f> &boxes, f32 divisor)
{
lua_createtable(L, boxes.size(), 0);
int i = 1;
for (const aabb3f &box : boxes) {
push_aabb3f(L, box, divisor);
lua_rawseti(L, -2, i++);
}
}

size_t read_stringlist(lua_State *L, int index, std::vector<std::string> *result)
{
if (index < 0)
Expand Down
4 changes: 3 additions & 1 deletion src/script/common/c_converter.h
Expand Up @@ -110,11 +110,13 @@ void push_v2s16 (lua_State *L, v2s16 p);
void push_v2s32 (lua_State *L, v2s32 p);
void push_v2u32 (lua_State *L, v2u32 p);
void push_v3s16 (lua_State *L, v3s16 p);
void push_aabb3f (lua_State *L, aabb3f box);
void push_aabb3f (lua_State *L, aabb3f box, f32 divisor = 1.0f);
void push_ARGB8 (lua_State *L, video::SColor color);
void pushFloatPos (lua_State *L, v3f p);
void push_v3f (lua_State *L, v3f p);
void push_v2f (lua_State *L, v2f p);
void push_aabb3f_vector (lua_State *L, const std::vector<aabb3f> &boxes,
f32 divisor = 1.0f);

void warn_if_field_exists(lua_State *L, int table,
const char *fieldname,
Expand Down
35 changes: 35 additions & 0 deletions src/script/lua_api/l_env.cpp
Expand Up @@ -562,6 +562,40 @@ int ModApiEnv::l_add_node_level(lua_State *L)
return 1;
}

// get_node_boxes(box_type, pos, [node]) -> table
// box_type = string
// pos = {x=num, y=num, z=num}
// node = {name=string, param1=num, param2=num} or nil
int ModApiEnv::l_get_node_boxes(lua_State *L)
{
GET_ENV_PTR;

std::string box_type = luaL_checkstring(L, 1);
v3s16 pos = read_v3s16(L, 2);
MapNode n;
if (lua_istable(L, 3))
n = readnode(L, 3);
else
n = env->getMap().getNode(pos);

u8 neighbors = n.getNeighbors(pos, &env->getMap());
const NodeDefManager *ndef = env->getGameDef()->ndef();

std::vector<aabb3f> boxes;
if (box_type == "node_box")
n.getNodeBoxes(ndef, &boxes, neighbors);
else if (box_type == "collision_box")
n.getCollisionBoxes(ndef, &boxes, neighbors);
else if (box_type == "selection_box")
n.getSelectionBoxes(ndef, &boxes, neighbors);
else
luaL_error(L, "get_node_boxes: box_type is invalid. Allowed values: \"node_box\", \"collision_box\", \"selection_box\"");

push_aabb3f_vector(L, boxes, BS);

return 1;
}

// find_nodes_with_meta(pos1, pos2)
int ModApiEnv::l_find_nodes_with_meta(lua_State *L)
{
Expand Down Expand Up @@ -1456,6 +1490,7 @@ void ModApiEnv::Initialize(lua_State *L, int top)
API_FCT(get_node_level);
API_FCT(set_node_level);
API_FCT(add_node_level);
API_FCT(get_node_boxes);
API_FCT(add_entity);
API_FCT(find_nodes_with_meta);
API_FCT(get_meta);
Expand Down
6 changes: 6 additions & 0 deletions src/script/lua_api/l_env.h
Expand Up @@ -120,6 +120,12 @@ class ModApiEnv : public ModApiEnvBase {
// pos = {x=num, y=num, z=num}
static int l_add_node_level(lua_State *L);

// get_node_boxes(box_type, pos, [node]) -> table
// box_type = string
// pos = {x=num, y=num, z=num}
// node = {name=string, param1=num, param2=num} or nil
static int l_get_node_boxes(lua_State *L);

// find_nodes_with_meta(pos1, pos2)
static int l_find_nodes_with_meta(lua_State *L);

Expand Down

0 comments on commit f2b9933

Please sign in to comment.