diff --git a/doc/lua_api.md b/doc/lua_api.md index 14ca7e835..49c96b925 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -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. diff --git a/games/devtest/mods/testtools/init.lua b/games/devtest/mods/testtools/init.lua index 09280f61f..2378cf982 100644 --- a/games/devtest/mods/testtools/init.lua +++ b/games/devtest/mods/testtools/init.lua @@ -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 = { diff --git a/games/devtest/mods/testtools/node_box_visualizer.lua b/games/devtest/mods/testtools/node_box_visualizer.lua new file mode 100644 index 000000000..60ec8a518 --- /dev/null +++ b/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, +}) diff --git a/games/devtest/mods/testtools/textures/testtools_node_box_visualizer.png b/games/devtest/mods/testtools/textures/testtools_node_box_visualizer.png new file mode 100644 index 000000000..1b16aa794 Binary files /dev/null and b/games/devtest/mods/testtools/textures/testtools_node_box_visualizer.png differ diff --git a/games/devtest/mods/testtools/textures/testtools_visual_collision_box.png b/games/devtest/mods/testtools/textures/testtools_visual_collision_box.png new file mode 100644 index 000000000..c8ab97cdf Binary files /dev/null and b/games/devtest/mods/testtools/textures/testtools_visual_collision_box.png differ diff --git a/games/devtest/mods/testtools/textures/testtools_visual_node_box.png b/games/devtest/mods/testtools/textures/testtools_visual_node_box.png new file mode 100644 index 000000000..dbbb8d9f6 Binary files /dev/null and b/games/devtest/mods/testtools/textures/testtools_visual_node_box.png differ diff --git a/games/devtest/mods/testtools/textures/testtools_visual_selection_box.png b/games/devtest/mods/testtools/textures/testtools_visual_selection_box.png new file mode 100644 index 000000000..1f4bbf3c8 Binary files /dev/null and b/games/devtest/mods/testtools/textures/testtools_visual_selection_box.png differ diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index aab91f35c..30a387b84 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -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: @@ -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; @@ -1148,16 +1148,6 @@ void push_nodebox(lua_State *L, const NodeBox &box) } } -void push_box(lua_State *L, const std::vector &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 *palette) { diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 7aa7dca8d..07f77f8c7 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -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 &box); - void push_palette (lua_State *L, const std::vector *palette); diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp index 7924b9fc2..f3a335d7a 100644 --- a/src/script/common/c_converter.cpp +++ b/src/script/common/c_converter.cpp @@ -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); } @@ -409,6 +409,16 @@ std::vector read_aabb3f_vector(lua_State *L, int index, f32 scale) return boxes; } +void push_aabb3f_vector(lua_State *L, const std::vector &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 *result) { if (index < 0) diff --git a/src/script/common/c_converter.h b/src/script/common/c_converter.h index 90358147a..2235cb45a 100644 --- a/src/script/common/c_converter.h +++ b/src/script/common/c_converter.h @@ -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 &boxes, + f32 divisor = 1.0f); void warn_if_field_exists(lua_State *L, int table, const char *fieldname, diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index b8daa86a0..869961056 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -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 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) { @@ -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); diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index f1fa680e9..0e1336962 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -120,6 +120,12 @@ private: // 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);