Super duper VoxelManipulator speedups to nearly every API function, and plus support for unloaded areas. Still in progress. Also, fix //allocate for very large schematics.

This commit is contained in:
Anthony Zhang 2013-07-21 16:54:25 -04:00
parent ac5e801834
commit 8ebf9d3c2a
8 changed files with 289 additions and 149 deletions

View File

@ -208,6 +208,6 @@ Returns an error if the code fails or nil otherwise.
### error = worldedit.luatransform(pos1, pos2, code) ### error = worldedit.luatransform(pos1, pos2, code)
Executes `code` as a Lua chunk in the global namespace with the variable pos available, for each node in a region defined by positions `pos1` and `pos2`. Executes `code` as a Lua chunk in the global namespace with the variable `pos` available, for each node in a region defined by positions `pos1` and `pos2`.
Returns an error if the code fails or nil otherwise. Returns an error if the code fails or nil otherwise.

View File

@ -24,6 +24,10 @@ worldedit.luatransform = function(pos1, pos2, code)
end end
local operation = factory() local operation = factory()
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local pos = {x=pos1.x, y=0, z=0} local pos = {x=pos1.x, y=0, z=0}
while pos.x <= pos2.x do while pos.x <= pos2.x do
pos.y = pos1.y pos.y = pos1.y

View File

@ -2,7 +2,7 @@ local path = minetest.get_modpath(minetest.get_current_modname())
local loadmodule = function(path) local loadmodule = function(path)
return pcall(function() return pcall(function()
dofile(path) return dofile(path)
end) end)
end end

View File

@ -3,6 +3,7 @@ local minetest = minetest --local copy of global
--wip: test the entire API again to make sure it works --wip: test the entire API again to make sure it works
--wip: remove env parameter where no longer needed in chat commands module --wip: remove env parameter where no longer needed in chat commands module
--wip: fix the queue
--modifies positions `pos1` and `pos2` so that each component of `pos1` is less than or equal to its corresponding conent of `pos2`, returning two new positions --modifies positions `pos1` and `pos2` so that each component of `pos1` is less than or equal to its corresponding conent of `pos2`, returning two new positions
worldedit.sort_pos = function(pos1, pos2) worldedit.sort_pos = function(pos1, pos2)
@ -32,12 +33,19 @@ worldedit.set = function(pos1, pos2, nodename)
--set up voxel manipulator --set up voxel manipulator
local manip = minetest.get_voxel_manip() local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2) local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
--fill nodes table with node to be set --fill emerged area with ignore
local nodes = {} local nodes = {}
local ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
nodes[i] = ignore
end
--fill selected area with node
local node_id = minetest.get_content_id(nodename) local node_id = minetest.get_content_id(nodename)
for i = 1, (pos2.x - pos1.x) * (pos2.y - pos1.y) * (pos2.z - pos1.z) do for i in area:iterp(pos1, pos2) do
nodes[i] = node_id nodes[i] = node_id
end end
@ -53,39 +61,55 @@ end
worldedit.replace = function(pos1, pos2, searchnode, replacenode) worldedit.replace = function(pos1, pos2, searchnode, replacenode)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local node = {name=replacenode} --set up voxel manipulator
local add_node = minetest.add_node local manip = minetest.get_voxel_manip()
local nodes = minetest.find_nodes_in_area(pos1, pos2, searchnode) local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
for _, pos in ipairs(nodes) do local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
add_node(pos, node)
local nodes = manip:get_data()
local searchnode_id = minetest.get_content_id(searchnode)
local replacenode_id = minetest.get_content_id(replacenode)
local count = 0
for i in area:iterp(pos1, pos2) do --replace searchnode with replacenode
if nodes[i] == searchnode_id then
nodes[i] = replacenode_id
count = count + 1
end
end end
return #nodes
--update map nodes
manip:set_data(nodes)
manip:write_to_map()
manip:update_map()
return count
end end
--replaces all nodes other than `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`, returning the number of nodes replaced --replaces all nodes other than `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`, returning the number of nodes replaced
worldedit.replaceinverse = function(pos1, pos2, searchnode, replacenode) --wip: use voxelmanip get_data for this worldedit.replaceinverse = function(pos1, pos2, searchnode, replacenode)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local pos = {x=pos1.x, y=0, z=0} --set up voxel manipulator
local node = {name=replacenode} local manip = minetest.get_voxel_manip()
local get_node, add_node = minetest.get_node, minetest.add_node local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
local nodes = manip:get_data()
local searchnode_id = minetest.get_content_id(searchnode)
local replacenode_id = minetest.get_content_id(replacenode)
local count = 0 local count = 0
while pos.x <= pos2.x do for i in area:iterp(pos1, pos2) do --replace anything that is not searchnode with replacenode
pos.y = pos1.y if nodes[i] ~= searchnode_id then
while pos.y <= pos2.y do nodes[i] = replacenode_id
pos.z = pos1.z count = count + 1
while pos.z <= pos2.z do
local name = get_node(pos).name
if name ~= "ignore" and name ~= searchnode then
add_node(pos, node)
count = count + 1
end
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end end
pos.x = pos.x + 1
end end
--update map nodes
manip:set_data(nodes)
manip:write_to_map()
manip:update_map()
return count return count
end end
@ -94,7 +118,7 @@ worldedit.copy = function(pos1, pos2, axis, amount, env)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
if env == nil then env = minetest.env end if env == nil then env = minetest.env end
--wip: copy slice by slice using schematic method in the copy axis and transfer metadata in separate loop (and if the amount is greater than the length in the axis, copy whole thing at a time) --wip: copy slice by slice using schematic method in the copy axis and transfer metadata in separate loop (and if the amount is greater than the length in the axis, copy whole thing at a time), use voxelmanip to keep area loaded
if amount < 0 then if amount < 0 then
local pos = {x=pos1.x, y=0, z=0} local pos = {x=pos1.x, y=0, z=0}
while pos.x <= pos2.x do while pos.x <= pos2.x do
@ -144,7 +168,7 @@ worldedit.move = function(pos1, pos2, axis, amount, env)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
if env == nil then env = minetest.env end if env == nil then env = minetest.env end
--wip: move slice by slice using schematic method in the move axis and transfer metadata in separate loop (and if the amount is greater than the length in the axis, copy whole thing at a time and erase original after, using schematic method) --wip: move slice by slice using schematic method in the move axis and transfer metadata in separate loop (and if the amount is greater than the length in the axis, copy whole thing at a time and erase original after, using schematic method), use voxelmanip to keep area loaded
if amount < 0 then if amount < 0 then
local pos = {x=pos1.x, y=0, z=0} local pos = {x=pos1.x, y=0, z=0}
while pos.x <= pos2.x do while pos.x <= pos2.x do
@ -205,7 +229,7 @@ worldedit.stack = function(pos1, pos2, axis, count, env)
amount = amount + length amount = amount + length
copy(pos1, pos2, axis, amount, env) copy(pos1, pos2, axis, amount, env)
end end
return worldedit.volume(pos1, pos2) return worldedit.volume(pos1, pos2) * count
end end
--scales the region defined by positions `pos1` and `pos2` by an factor of positive integer `factor` with `pos1` as the origin, returning the number of nodes scaled, the new scaled position 1, and the new scaled position 2 --scales the region defined by positions `pos1` and `pos2` by an factor of positive integer `factor` with `pos1` as the origin, returning the number of nodes scaled, the new scaled position 1, and the new scaled position 2
@ -216,14 +240,20 @@ worldedit.scale = function(pos1, pos2, factor)
local get_node, get_meta, place_schematic = minetest.get_node, minetest.get_meta, minetest.place_schematic local get_node, get_meta, place_schematic = minetest.get_node, minetest.get_meta, minetest.place_schematic
local placeholder_node = {name="", param1=0, param2=0} local placeholder_node = {name="", param1=0, param2=0}
local nodes = {} local nodes = {}
for i = 1, size ^ 3 do for i = 1, factor ^ 3 do
nodes[i] = placeholder_node nodes[i] = placeholder_node
end end
local schematic = {size={x=size, y=size, z=size}, data=nodes} local schematic = {size={x=factor, y=factor, z=factor}, data=nodes}
local size = factor - 1
--make area stay loaded
local manip = minetest.get_voxel_manip()
local new_pos2 = {x=pos1.x + (pos2.x - pos1.x) * factor + size, y=pos1.y + (pos2.y - pos1.y) * factor + size, z=pos1.z + (pos2.z - pos1.z) * factor + size}
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, new_pos2)
local pos = {x=pos2.x, y=0, z=0} local pos = {x=pos2.x, y=0, z=0}
local bigpos = {x=0, y=0, z=0} local bigpos = {x=0, y=0, z=0}
size = factor - 1
while pos.x >= pos1.x do while pos.x >= pos1.x do
pos.y = pos2.y pos.y = pos2.y
while pos.y >= pos1.y do while pos.y >= pos1.y do
@ -236,14 +266,19 @@ worldedit.scale = function(pos1, pos2, factor)
local posx, posy, posz = pos1.x + (pos.x - pos1.x) * factor, pos1.y + (pos.y - pos1.y) * factor, pos1.z + (pos.z - pos1.z) * factor local posx, posy, posz = pos1.x + (pos.x - pos1.x) * factor, pos1.y + (pos.y - pos1.y) * factor, pos1.z + (pos.z - pos1.z) * factor
--create large node --create large node
placeholder_node[1], placeholder_node[3] = node.name, node.param2 placeholder_node.name = node.name
placeholder_node.param1, placeholder_node.param2 = node.param1, node.param2
bigpos.x, bigpos.y, bigpos.z = posx, posy, posz bigpos.x, bigpos.y, bigpos.z = posx, posy, posz
place_schematic(bigpos, schematic) place_schematic(bigpos, schematic)
for x = 0, size do --fill in large node meta
for y = 0, size do --fill in large node meta
for z = 0, size do if next(meta.fields) ~= nil and next(meta.inventory) ~= nil then --node has meta fields
bigpos.x, bigpos.y, bigpos.z = posx + x, posy + y, posz + z for x = 0, size do
get_meta(bigpos):from_table(meta) --set metadata of new node for y = 0, size do
for z = 0, size do
bigpos.x, bigpos.y, bigpos.z = posx + x, posy + y, posz + z
get_meta(bigpos):from_table(meta) --set metadata of new node
end
end end
end end
end end
@ -253,8 +288,7 @@ worldedit.scale = function(pos1, pos2, factor)
end end
pos.x = pos.x - 1 pos.x = pos.x - 1
end end
local newpos2 = {x=pos1.x + (pos2.x - pos1.x) * factor + size, y=pos1.y + (pos2.y - pos1.y) * factor + size, z=pos1.z + (pos2.z - pos1.z) * factor + size} return worldedit.volume(pos1, pos2) * (factor ^ 3), pos1, new_pos2
return worldedit.volume(pos1, pos2), pos1, newpos2
end end
--transposes a region defined by the positions `pos1` and `pos2` between the `axis1` and `axis2` axes, returning the number of nodes transposed, the new transposed position 1, and the new transposed position 2 --transposes a region defined by the positions `pos1` and `pos2` between the `axis1` and `axis2` axes, returning the number of nodes transposed, the new transposed position 1, and the new transposed position 2
@ -275,9 +309,16 @@ worldedit.transpose = function(pos1, pos2, axis1, axis2, env)
end end
--calculate the new position 2 after transposition --calculate the new position 2 after transposition
local newpos2 = {x=pos2.x, y=pos2.y, z=pos2.z} local new_pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}
newpos2[axis1] = pos1[axis1] + extent2 new_pos2[axis1] = pos1[axis1] + extent2
newpos2[axis2] = pos1[axis2] + extent1 new_pos2[axis2] = pos1[axis2] + extent1
--make area stay loaded
local manip = minetest.get_voxel_manip()
local upperbound = {x=pos2.x, y=pos2.y, z=pos2.z}
if upperbound[axis1] < new_pos2[axis1] then upperbound[axis1] = new_pos2[axis1] end
if upperbound[axis2] < new_pos2[axis2] then upperbound[axis2] = new_pos2[axis2] end
manip:read_from_map(pos1, upperbound)
local pos = {x=pos1.x, y=0, z=0} local pos = {x=pos1.x, y=0, z=0}
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
@ -306,34 +347,38 @@ worldedit.transpose = function(pos1, pos2, axis1, axis2, env)
end end
pos.x = pos.x + 1 pos.x = pos.x + 1
end end
return worldedit.volume(pos1, pos2), pos1, newpos2 return worldedit.volume(pos1, pos2), pos1, new_pos2
end end
--flips a region defined by the positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z"), returning the number of nodes flipped --flips a region defined by the positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z"), returning the number of nodes flipped
worldedit.flip = function(pos1, pos2, axis, env) worldedit.flip = function(pos1, pos2, axis, env)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
--wip: flip the region slice by slice along the flip axis using schematic method --wip: flip the region slice by slice along the flip axis using schematic method
local pos = {x=pos1.x, y=0, z=0} local pos = {x=pos1.x, y=0, z=0}
local start = pos1[axis] + pos2[axis] local start = pos1[axis] + pos2[axis]
pos2[axis] = pos1[axis] + math.floor((pos2[axis] - pos1[axis]) / 2) pos2[axis] = pos1[axis] + math.floor((pos2[axis] - pos1[axis]) / 2)
if env == nil then env = minetest.env end local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
while pos.x <= pos2.x do while pos.x <= pos2.x do
pos.y = pos1.y pos.y = pos1.y
while pos.y <= pos2.y do while pos.y <= pos2.y do
pos.z = pos1.z pos.z = pos1.z
while pos.z <= pos2.z do while pos.z <= pos2.z do
local node1 = env:get_node(pos) local node1 = get_node(pos)
local meta1 = env:get_meta(pos):to_table() local meta1 = get_meta(pos):to_table()
local value = pos[axis] local value = pos[axis]
pos[axis] = start - value pos[axis] = start - value
local node2 = env:get_node(pos) local node2 = get_node(pos)
local meta2 = env:get_meta(pos):to_table() local meta2 = get_meta(pos):to_table()
env:add_node(pos, node1) add_node(pos, node1)
env:get_meta(pos):from_table(meta1) get_meta(pos):from_table(meta1)
pos[axis] = value pos[axis] = value
env:add_node(pos, node2) add_node(pos, node2)
env:get_meta(pos):from_table(meta2) get_meta(pos):from_table(meta2)
pos.z = pos.z + 1 pos.z = pos.z + 1
end end
pos.y = pos.y + 1 pos.y = pos.y + 1
@ -372,7 +417,7 @@ worldedit.rotate = function(pos1, pos2, axis, angle, env)
end end
--rotates all oriented nodes in a region defined by the positions `pos1` and `pos2` by `angle` degrees clockwise (90 degree increment) around the Y axis, returning the number of nodes oriented --rotates all oriented nodes in a region defined by the positions `pos1` and `pos2` by `angle` degrees clockwise (90 degree increment) around the Y axis, returning the number of nodes oriented
worldedit.orient = function(pos1, pos2, angle, env) worldedit.orient = function(pos1, pos2, angle, env) --wip: support 6D facedir rotation along arbitrary axis
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local registered_nodes = minetest.registered_nodes local registered_nodes = minetest.registered_nodes
@ -394,6 +439,10 @@ worldedit.orient = function(pos1, pos2, angle, env)
local wallmounted_substitution = wallmounted[angle] local wallmounted_substitution = wallmounted[angle]
local facedir_substitution = facedir[angle] local facedir_substitution = facedir[angle]
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local count = 0 local count = 0
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
local pos = {x=pos1.x, y=0, z=0} local pos = {x=pos1.x, y=0, z=0}
@ -431,6 +480,11 @@ end
--fixes the lighting in a region defined by positions `pos1` and `pos2`, returning the number of nodes updated --fixes the lighting in a region defined by positions `pos1` and `pos2`, returning the number of nodes updated
worldedit.fixlight = function(pos1, pos2, env) worldedit.fixlight = function(pos1, pos2, env)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local nodes = minetest.find_nodes_in_area(pos1, pos2, "air") local nodes = minetest.find_nodes_in_area(pos1, pos2, "air")
local dig_node = minetest.dig_node local dig_node = minetest.dig_node
for _, pos in ipairs(nodes) do for _, pos in ipairs(nodes) do

View File

@ -5,27 +5,39 @@ local minetest = minetest --local copy of global
worldedit.hollow_sphere = function(pos, radius, nodename) worldedit.hollow_sphere = function(pos, radius, nodename)
--set up voxel manipulator --set up voxel manipulator
local manip = minetest.get_voxel_manip() local manip = minetest.get_voxel_manip()
manip:read_from_map( local pos1 = {x=pos.x - radius, y=pos.y - radius, z=pos.z - radius}
{x=pos.x - radius, y=pos.y - radius, z=pos.z - radius}, local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}
{x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}, local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
) local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
local insert = table.insert --fill emerged area with ignore
local node_id = minetest.get_content_id(nodename)
local ignore_id = minetest.get_content_id("ignore")
local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)
local nodes = {} local nodes = {}
local ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
nodes[i] = ignore
end
--fill selected area with node
local node_id = minetest.get_content_id(nodename)
local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)
local ystride, zstride = area.ystride, area.zstride
local x, y, z = -radius, -radius, -radius
local count = 0 local count = 0
for x = -radius, radius do for i in area:iterp(pos1, pos2) do
for y = -radius, radius do local squared = x * x + y * y + z * z
for z = -radius, radius do if squared >= min_radius and squared <= max_radius then --position is on surface of sphere
local squared = x * x + y * y + z * z nodes[i] = node_id
if squared >= min_radius and squared <= max_radius then --surface of sphere count = count + 1
insert(nodes, node_id) end
count = count + 1
else --move to the next position
insert(nodes, ignore_id) x = x + 1
end if x > radius then
x = -radius
y = y + 1
if y > radius then
y = -radius
z = z + 1
end end
end end
end end
@ -42,26 +54,38 @@ end
worldedit.sphere = function(pos, radius, nodename) worldedit.sphere = function(pos, radius, nodename)
--set up voxel manipulator --set up voxel manipulator
local manip = minetest.get_voxel_manip() local manip = minetest.get_voxel_manip()
manip:read_from_map( local pos1 = {x=pos.x - radius, y=pos.y - radius, z=pos.z - radius}
{x=pos.x - radius, y=pos.y - radius, z=pos.z - radius}, local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}
{x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}, local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
) local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
local insert = table.insert --fill emerged area with ignore
local node_id = minetest.get_content_id(nodename)
local ignore_id = minetest.get_content_id("ignore")
local max_radius = radius * (radius + 1)
local nodes = {} local nodes = {}
local ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
nodes[i] = ignore
end
--fill selected area with node
local node_id = minetest.get_content_id(nodename)
local max_radius = radius * (radius + 1)
local ystride, zstride = area.ystride, area.zstride
local x, y, z = -radius, -radius, -radius
local count = 0 local count = 0
for x = -radius, radius do for i in area:iterp(pos1, pos2) do
for y = -radius, radius do if x * x + y * y + z * z <= max_radius then --position is inside sphere
for z = -radius, radius do nodes[i] = node_id
if x * x + y * y + z * z <= max_radius then --inside sphere count = count + 1
insert(nodes, node_id) end
count = count + 1
else --move to the next position
insert(nodes, ignore_id) x = x + 1
end if x > radius then
x = -radius
y = y + 1
if y > radius then
y = -radius
z = z + 1
end end
end end
end end
@ -75,30 +99,42 @@ worldedit.sphere = function(pos, radius, nodename)
end end
--adds a hollow dome centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added --adds a hollow dome centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added
worldedit.hollow_dome = function(pos, radius, nodename) --wip: use bresenham sphere for maximum speed worldedit.hollow_dome = function(pos, radius, nodename)
--set up voxel manipulator --set up voxel manipulator
local manip = minetest.get_voxel_manip() local manip = minetest.get_voxel_manip()
manip:read_from_map( local pos1 = {x=pos.x - radius, y=pos.y, z=pos.z - radius}
{x=pos.x - radius, y=pos.y, z=pos.z - radius}, local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}
{x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}, local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
) local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
local insert = table.insert --fill emerged area with ignore
local node_id = minetest.get_content_id(nodename)
local ignore_id = minetest.get_content_id("ignore")
local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)
local nodes = {} local nodes = {}
local ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
nodes[i] = ignore
end
--fill selected area with node
local node_id = minetest.get_content_id(nodename)
local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)
local ystride, zstride = area.ystride, area.zstride
local x, y, z = -radius, 0, -radius
local count = 0 local count = 0
for x = -radius, radius do for i in area:iterp(pos1, pos2) do
for y = 0, radius do local squared = x * x + y * y + z * z
for z = -radius, radius do if squared >= min_radius and squared <= max_radius then --position is on surface of sphere
local squared = x * x + y * y + z * z nodes[i] = node_id
if squared >= min_radius and squared <= max_radius then --surface of dome count = count + 1
insert(nodes, node_id) end
count = count + 1
else --move to the next position
insert(nodes, ignore_id) x = x + 1
end if x > radius then
x = -radius
y = y + 1
if y > radius then
y = 0
z = z + 1
end end
end end
end end
@ -115,26 +151,38 @@ end
worldedit.dome = function(pos, radius, nodename) --wip: use bresenham sphere for maximum speed worldedit.dome = function(pos, radius, nodename) --wip: use bresenham sphere for maximum speed
--set up voxel manipulator --set up voxel manipulator
local manip = minetest.get_voxel_manip() local manip = minetest.get_voxel_manip()
manip:read_from_map( local pos1 = {x=pos.x - radius, y=pos.y, z=pos.z - radius}
{x=pos.x - radius, y=pos.y, z=pos.z - radius}, local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}
{x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}, local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
) local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
local insert = table.insert --fill emerged area with ignore
local node_id = minetest.get_content_id(nodename)
local ignore_id = minetest.get_content_id("ignore")
local max_radius = radius * (radius + 1)
local nodes = {} local nodes = {}
local ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
nodes[i] = ignore
end
--fill selected area with node
local node_id = minetest.get_content_id(nodename)
local max_radius = radius * (radius + 1)
local ystride, zstride = area.ystride, area.zstride
local x, y, z = -radius, 0, -radius
local count = 0 local count = 0
for x = -radius, radius do for i in area:iterp(pos1, pos2) do
for y = 0, radius do if x * x + y * y + z * z <= max_radius then --position is inside sphere
for z = -radius, radius do nodes[i] = node_id
if x * x + y * y + z * z <= max_radius then --inside dome count = count + 1
insert(nodes, node_id) end
count = count + 1
else --move to the next position
insert(nodes, ignore_id) x = x + 1
end if x > radius then
x = -radius
y = y + 1
if y > radius then
y = 0
z = z + 1
end end
end end
end end
@ -148,7 +196,7 @@ worldedit.dome = function(pos, radius, nodename) --wip: use bresenham sphere for
end end
--adds a hollow cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `nodename`, returning the number of nodes added --adds a hollow cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `nodename`, returning the number of nodes added
worldedit.hollow_cylinder = function(pos, axis, length, radius, nodename) worldedit.hollow_cylinder = function(pos, axis, length, radius, nodename) --wip: rewrite this using voxelmanip
local other1, other2 local other1, other2
if axis == "x" then if axis == "x" then
other1, other2 = "y", "z" other1, other2 = "y", "z"
@ -216,7 +264,7 @@ worldedit.hollow_cylinder = function(pos, axis, length, radius, nodename)
end end
--adds a cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `nodename`, returning the number of nodes added --adds a cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `nodename`, returning the number of nodes added
worldedit.cylinder = function(pos, axis, length, radius, nodename, env) worldedit.cylinder = function(pos, axis, length, radius, nodename, env) --wip: rewrite this using voxelmanip
local other1, other2 local other1, other2
if axis == "x" then if axis == "x" then
other1, other2 = "y", "z" other1, other2 = "y", "z"
@ -281,7 +329,7 @@ worldedit.cylinder = function(pos, axis, length, radius, nodename, env)
end end
--adds a pyramid centered at `pos` with height `height`, composed of `nodename`, returning the number of nodes added --adds a pyramid centered at `pos` with height `height`, composed of `nodename`, returning the number of nodes added
worldedit.pyramid = function(pos, height, nodename, env) worldedit.pyramid = function(pos, height, nodename, env) --wip: rewrite this using voxelmanip
local pos1x, pos1y, pos1z = pos.x - height, pos.y, pos.z - height local pos1x, pos1y, pos1z = pos.x - height, pos.y, pos.z - height
local pos2x, pos2y, pos2z = pos.x + height, pos.y + height, pos.z + height local pos2x, pos2y, pos2z = pos.x + height, pos.y + height, pos.z + height
local pos = {x=0, y=pos1y, z=0} local pos = {x=0, y=pos1y, z=0}

View File

@ -12,41 +12,39 @@ minetest.register_globalstep(function(dtime)
local elapsed = 0 local elapsed = 0
local env = minetest.env local env = minetest.env
while worldedit.lower <= worldedit.higher and elapsed <= worldedit.MAXIMUM_TIME do while worldedit.lower <= worldedit.higher and elapsed <= worldedit.MAXIMUM_TIME do
local entry = worldedit.queue[worldedit.lower] local entry = worldedit.queue[worldedit.lower]
if entry.t == "set_node" then if entry.t == "set_node" then
env:set_node(entry.pos, entry.node) env:set_node(entry.pos, entry.node)
elapsed = elapsed + 0.0002 elapsed = elapsed + 0.0002
elseif entry.t == "remove_node" then elseif entry.t == "remove_node" then
env:remove_node(entry.pos) env:remove_node(entry.pos)
elapsed = elapsed + 0.0002 elapsed = elapsed + 0.0002
elseif entry.t == "place_node" then elseif entry.t == "place_node" then
env:place_node(entry.pos, entry.node) env:place_node(entry.pos, entry.node)
elapsed = elapsed + 0.001 elapsed = elapsed + 0.001
elseif entry.t == "dig_node" then elseif entry.t == "dig_node" then
env:dig_node(entry.pos) env:dig_node(entry.pos)
elapsed = elapsed + 0.001 elapsed = elapsed + 0.001
elseif entry.t == "add_entity" then elseif entry.t == "add_entity" then
env:add_entity(entry.pos, entry.name) env:add_entity(entry.pos, entry.name)
elapsed = elapsed + 0.005 elapsed = elapsed + 0.005
elseif entry.t == "add_item" then elseif entry.t == "add_item" then
env:add_item(entry.pos, entry.item) env:add_item(entry.pos, entry.item)
elapsed = elapsed + 0.005 elapsed = elapsed + 0.005
elseif entry.t == "meta_from_table" then elseif entry.t == "meta_from_table" then
env:get_meta(entry.pos):from_table(entry.table) env:get_meta(entry.pos):from_table(entry.table)
elapsed = elapsed + 0.0002 elapsed = elapsed + 0.0002
else else
print("Unknown queue event type: " .. entry.t) print("Unknown queue event type: " .. entry.t)
end end
worldedit.queue[worldedit.lower] = nil worldedit.queue[worldedit.lower] = nil
worldedit.lower = worldedit.lower + 1 worldedit.lower = worldedit.lower + 1
end end
end) end)
do worldedit.enqueue = function(value)
worldedit.enqueue = function(value) worldedit.higher = worldedit.higher + 1
worldedit.higher = worldedit.higher + 1 worldedit.queue[worldedit.higher] = value
worldedit.queue[worldedit.higher] = value
end
end end
function table.copy(t, seen) function table.copy(t, seen)
@ -123,4 +121,3 @@ worldedit.queue_aliasenv = {
add_entity = queue_addentity, add_entity = queue_addentity,
add_item = queue_additem, add_item = queue_additem,
} }

View File

@ -34,6 +34,10 @@ end
--converts the region defined by positions `pos1` and `pos2` into a single string, returning the serialized data and the number of nodes serialized --converts the region defined by positions `pos1` and `pos2` into a single string, returning the serialized data and the number of nodes serialized
worldedit.serialize = function(pos1, pos2) --wip: check for ItemStacks and whether they can be serialized worldedit.serialize = function(pos1, pos2) --wip: check for ItemStacks and whether they can be serialized
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local pos = {x=pos1.x, y=0, z=0} local pos = {x=pos1.x, y=0, z=0}
local count = 0 local count = 0
@ -141,7 +145,24 @@ worldedit.allocate = function(originpos, value)
count = count + 1 count = count + 1
end end
elseif version == 4 then --current nested table format elseif version == 4 then --current nested table format
local nodes = minetest.deserialize(value) --wip: this is a filthy hack that works surprisingly well
value = value:gsub("return%s*{", "", 1):gsub("}%s*$", "", 1)
local escaped = value:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end)
local startpos, startpos1, endpos = 1, 1
local nodes = {}
while true do
startpos, endpos = escaped:find("},%s*{", startpos)
if not startpos then
break
end
local current = value:sub(startpos1, startpos)
table.insert(nodes, minetest.deserialize("return " .. current))
startpos, startpos1 = endpos, endpos
end
table.insert(nodes, minetest.deserialize("return " .. value:sub(startpos1)))
--local nodes = minetest.deserialize(value) --wip: this is broken for larger tables in the current version of LuaJIT
count = #nodes count = #nodes
for index = 1, count do for index = 1, count do
local entry = nodes[index] local entry = nodes[index]
@ -161,7 +182,7 @@ end
--loads the nodes represented by string `value` at position `originpos`, returning the number of nodes deserialized --loads the nodes represented by string `value` at position `originpos`, returning the number of nodes deserialized
--contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile) by ChillCode, available under the MIT license (GPL compatible) --contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile) by ChillCode, available under the MIT license (GPL compatible)
worldedit.deserialize = function(originpos, value) worldedit.deserialize = function(originpos, value) --wip: use voxelmanip to make sure the blocks are loaded
local originx, originy, originz = originpos.x, originpos.y, originpos.z local originx, originy, originz = originpos.x, originpos.y, originpos.z
local count = 0 local count = 0
local add_node, get_meta = minetest.add_node, minetest.get_meta local add_node, get_meta = minetest.add_node, minetest.get_meta

View File

@ -33,6 +33,10 @@ minetest.register_node("worldedit:placeholder", {
--hides all nodes in a region defined by positions `pos1` and `pos2` by non-destructively replacing them with invisible nodes, returning the number of nodes hidden --hides all nodes in a region defined by positions `pos1` and `pos2` by non-destructively replacing them with invisible nodes, returning the number of nodes hidden
worldedit.hide = function(pos1, pos2) worldedit.hide = function(pos1, pos2)
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local pos = {x=pos1.x, y=0, z=0} local pos = {x=pos1.x, y=0, z=0}
local placeholder = {name="worldedit:placeholder", param1=0, param2=0} local placeholder = {name="worldedit:placeholder", param1=0, param2=0}
@ -60,6 +64,10 @@ end
--suppresses all instances of `nodename` in a region defined by positions `pos1` and `pos2` by non-destructively replacing them with invisible nodes, returning the number of nodes suppressed --suppresses all instances of `nodename` in a region defined by positions `pos1` and `pos2` by non-destructively replacing them with invisible nodes, returning the number of nodes suppressed
worldedit.suppress = function(pos1, pos2, nodename) worldedit.suppress = function(pos1, pos2, nodename)
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local placeholder = {name="worldedit:placeholder", param1=0, param2=0} local placeholder = {name="worldedit:placeholder", param1=0, param2=0}
local nodes = minetest.find_nodes_in_area(pos1, pos2, nodename) local nodes = minetest.find_nodes_in_area(pos1, pos2, nodename)
@ -77,7 +85,11 @@ worldedit.suppress = function(pos1, pos2, nodename)
end end
--highlights all instances of `nodename` in a region defined by positions `pos1` and `pos2` by non-destructively hiding all other nodes, returning the number of nodes found --highlights all instances of `nodename` in a region defined by positions `pos1` and `pos2` by non-destructively hiding all other nodes, returning the number of nodes found
worldedit.highlight = function(pos1, pos2, nodename) --wip: speed this up with voxmanip get_data worldedit.highlight = function(pos1, pos2, nodename) --wip: speed this up with voxmanip get_data to speed up searching
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local pos = {x=pos1.x, y=0, z=0} local pos = {x=pos1.x, y=0, z=0}
local placeholder = {name="worldedit:placeholder", param1=0, param2=0} local placeholder = {name="worldedit:placeholder", param1=0, param2=0}
@ -110,6 +122,10 @@ end
--restores all nodes hidden with WorldEdit functions in a region defined by positions `pos1` and `pos2`, returning the number of nodes restored --restores all nodes hidden with WorldEdit functions in a region defined by positions `pos1` and `pos2`, returning the number of nodes restored
worldedit.restore = function(pos1, pos2) worldedit.restore = function(pos1, pos2)
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local node = {name="", param1=0, param2=0} local node = {name="", param1=0, param2=0}
local nodes = minetest.find_nodes_in_area(pos1, pos2, "worldedit:placeholder") local nodes = minetest.find_nodes_in_area(pos1, pos2, "worldedit:placeholder")