From 3311d80a2a38ce0c1d8ffaf183f1d79946032247 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Mon, 16 May 2022 01:01:01 +0100 Subject: [PATCH] Rework metaballs backend We need a way of defining metaballs per-player. Our solution to this is a custom in-memory per-player storage system. The reason for this is because just a position (e.g. that provided by pos1/pos2) is not enough - we need a radius as well. --- worldeditadditions/init.lua | 4 +- worldeditadditions/lib/metaballs/init.lua | 15 +++ .../lib/metaballs/playerdata.lua | 104 ++++++++++++++++++ .../{metaballs.lua => metaballs/render.lua} | 5 +- .../commands/metaballs.lua | 71 ++++++++++++ 5 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 worldeditadditions/lib/metaballs/init.lua create mode 100644 worldeditadditions/lib/metaballs/playerdata.lua rename worldeditadditions/lib/{metaballs.lua => metaballs/render.lua} (97%) create mode 100644 worldeditadditions_commands/commands/metaballs.lua diff --git a/worldeditadditions/init.lua b/worldeditadditions/init.lua index 609ffd4..2e12168 100644 --- a/worldeditadditions/init.lua +++ b/worldeditadditions/init.lua @@ -59,11 +59,11 @@ dofile(wea.modpath.."/lib/scale.lua") dofile(wea.modpath.."/lib/spiral_square.lua") dofile(wea.modpath.."/lib/spiral_circle.lua") dofile(wea.modpath.."/lib/dome.lua") -dofile(wea.modpath.."/lib/metaballs.lua") dofile(wea.modpath.."/lib/conv/conv.lua") dofile(wea.modpath.."/lib/erode/erode.lua") dofile(wea.modpath.."/lib/noise/init.lua") -wea.sculpt = dofile(wea.modpath.."/lib/sculpt/init.lua") +wea.sculpt = dofile(wea.modpath.."/lib/sculpt/init.lua") +wea.metaballs = dofile(wea.modpath.."/lib/metaballs.lua") dofile(wea.modpath.."/lib/copy.lua") dofile(wea.modpath.."/lib/move.lua") diff --git a/worldeditadditions/lib/metaballs/init.lua b/worldeditadditions/lib/metaballs/init.lua new file mode 100644 index 0000000..729737c --- /dev/null +++ b/worldeditadditions/lib/metaballs/init.lua @@ -0,0 +1,15 @@ +local wea = worldeditadditions +local wea_m = wea.modpath .. "/lib/metaballs/" + +local playerdata = dofile(wea_m.."playerdata.lua") + +local metaballs_ns = { + render = dofile(wea_m.."render.lua"), + add = playerdata.add, + remove = playerdata.remove, + list = playerdata.list, + list_pretty = playerdata.list_pretty, + clear = playerdata.clear +} + +return metaballs_ns diff --git a/worldeditadditions/lib/metaballs/playerdata.lua b/worldeditadditions/lib/metaballs/playerdata.lua new file mode 100644 index 0000000..8367573 --- /dev/null +++ b/worldeditadditions/lib/metaballs/playerdata.lua @@ -0,0 +1,104 @@ +local wea = worldeditadditions +local Vector3 = wea.Vector3 + +local metaballdata = {} + +--- Adds a new metaball for a given player at the specified position with a specified radius. +-- @param player_name string The name of the player. +-- @param pos Vector3 The position of the metaball. +-- @param radius number The radius of the metaball. +-- @returns bool,number The number of metaballs now defined for the given player. +local function add(player_name, pos, radius) + local pos = Vector3.clone(pos) + + if type(player_name) ~= "string" then + return false, "Error: Invalid player name specified." + end + if type(radius) ~= "number" then + return false, "Error: Expected the radius to be of type number, but got value of type "..type(radius).." instead." + end + if radius < 1 then + return false, "The minimum radius of a metaball is 1, but got a radius of "..tostring(radius).."." + end + + if not metaballdata[player_name] then + metaballdata[player_name] = {} + end + + -- TODO: Limit the number of metaballs that can be defined? + table.insert(metaballdata[player_name], { + pos = pos, + radius = radius + }) + + return true, #metaballdata[player_name] +end + +--- Returns a list of all metaballs defined for the given player. +-- @param player_name string The name of the player. +-- @returns bool,[{ pos: Vector3, radius: number }, ...] A list of metaballs for the given player. +local function list(player_name) + if type(player_name) ~= "string" then + return false, "Error: Invalid player name specified." + end + + if not metaballdata[player_name] then return {} end + + return true, metaballdata[player_name] +end + +--- Returns a pretty-printed list of metaballs for the given player. +-- @param player_name string The name of the player. +-- @returns bool,string A pretty-printed list of metaballs for the given player. +local function list_pretty(player_name) + local success, metaball_list = list(player_name) + if not success then return success, metaball_list end + + local rows = { { "Index", "Position", "Radius" } } + for i,metaball in ipairs(metaball_list) do + table.insert(rows, { + i, + metaball.pos, + metaball.radius + }) + end + + return true, wea.format.make_ascii_table(rows).."\n---------------------------\nTotal "..tostring(#metaball_list).." metaballs" +end + +--- Removes the metaball with the specified index for a given player. +-- @param player_name string The name of the player. +-- @param index number The index of the metaball to remove. +-- @returns bool,number The number of metaballs now defined for the given player. +local function remove(player_name, index) + local success, metaball_list = list(player_name) + if not success then return success, metaball_list end + + if #metaball_list > index then + return false, "Error: Requested the removal of metaball "..tostring(index)..", but there are "..tostring(#metaball_list).." metaballs defined." + end + + table.remove(metaball_list, index) + + return #metaball_list +end + +--- Removes all the currently defined metaballs for the given player. +-- @param player_name string The name of the player. +-- @returns bool,number The number of metaballs that WERE defined for the given player. +local function clear(player_name) + local success, metaball_list = list(player_name) + if not success then return success, metaball_list end + + metaballdata[player_name] = {} + + return #metaball_list +end + +return { + add = add, + list = list, + list_pretty = list_pretty, + remove = remove, + clear = clear +} diff --git a/worldeditadditions/lib/metaballs.lua b/worldeditadditions/lib/metaballs/render.lua similarity index 97% rename from worldeditadditions/lib/metaballs.lua rename to worldeditadditions/lib/metaballs/render.lua index d205b98..4ef11d4 100644 --- a/worldeditadditions/lib/metaballs.lua +++ b/worldeditadditions/lib/metaballs/render.lua @@ -10,7 +10,7 @@ local Vector3 = wea.Vector3 -- direction the point should point. -- @param metaballs [{pos: Vector3, radius: number}] Aa list of metaballs to render. Each metaball should be a table with 2 properties: pos - the position of the centre of the metaball as a Vector3, and radius - the radius of the metaball. -- @param replace_node string The fully qualified name of the node to use to make the dome with. -function worldeditadditions.metaballs(metaballs, replace_node, threshold) +local function render(metaballs, replace_node, threshold) local pos1, pos2 if not threshold then threshold = 1 end @@ -61,3 +61,6 @@ function worldeditadditions.metaballs(metaballs, replace_node, threshold) return true, replaced end + + +return render diff --git a/worldeditadditions_commands/commands/metaballs.lua b/worldeditadditions_commands/commands/metaballs.lua new file mode 100644 index 0000000..bc9879d --- /dev/null +++ b/worldeditadditions_commands/commands/metaballs.lua @@ -0,0 +1,71 @@ +local wea = worldeditadditions +local Vector3 = wea.Vector3 + +-- ██████ ██████ ███ ███ ███████ +-- ██ ██ ██ ██ ████ ████ ██ +-- ██ ██ ██ ██ ██ ████ ██ █████ +-- ██ ██ ██ ██ ██ ██ ██ ██ +-- ██████ ██████ ██ ██ ███████ +worldedit.register_command("metaballs", { + params = "add | remove | list | render | clear", + description = "Defines and creates metaballs. After using the add subcommand to define 1 or more metaballs (uses pos1), the render subcommand can then be used to create the metaballs as nodes.", + privs = { worldedit = true }, + require_pos = 1, + parse = function(params_text) + if not params_text then params_text = "" end + + local parts = wea.split_shell(params_text) + + if #parts < 1 then + return false, "Error: Not enough arguments (see /help /dome for usage information)." + end + local subcommand = parts[1] + local subargs = {} + if subcommand == "add" then + local radius = tonumber(parts[2]) + if not radius then + return false, "Error: Invalid radius '"..parts[1].."'. The radius must be a positive integer." + end + if radius < 1 then + return false, "Error: The minimum radius size is 1, but you entered "..tostring(radius).."." + end + table.insert(subargs, radius) + elseif subcommand == "remove" then + local index = tonumber(parts[2]) + if not index then + return false, "Error: Invalid index '"..parts[1].."'. The index to remove must be a positive integer." + end + if index < 1 then + return false, "Error: The minimum index size is 1, but you entered "..tostring(index).."." + end + table.insert(subargs, index) + elseif subcommand == "render" then + local replace_node = worldedit.normalize_nodename(parts[2]) + if not replace_node then + return false, "Error: Invalid replace_node '"..parts[2].."'." + end + table.insert(subargs, replace_node) + end + + return true, subcommand, subargs + end, + nodes_needed = function(name, subcommand) + if subcommand == "render" then + return worldedit.volume(worldedit.pos1[name], worldedit.pos2[name]) + else + return 0 + end + end, + func = function(name, radius, replace_node, axes, hollow) + local start_time = wea.get_ms_time() + + + + + local time_taken = wea.get_ms_time() - start_time + + + minetest.log("action", name.." used //dome+ at "..pos.." with a radius of "..tostring(radius)..", modifying "..nodes_replaced.." nodes in "..wea.format.human_time(time_taken)) + return true, nodes_replaced.." nodes replaced "..wea.format.human_time(time_taken) + end +})