mirror of
https://github.com/sbrl/Minetest-WorldEditAdditions.git
synced 2024-12-23 03:55:01 +00:00
Initial //orient implementation
....unfortunately, it doesn't work yet. Next up is a frankly ridiculous number of print()s to figure out what's going on here
This commit is contained in:
parent
c712b502e9
commit
02d95cec46
9 changed files with 168 additions and 18 deletions
|
@ -41,6 +41,7 @@ dofile(wea.modpath.."/lib/dome.lua")
|
|||
dofile(wea.modpath.."/lib/spline.lua")
|
||||
dofile(wea.modpath.."/lib/revolve.lua")
|
||||
dofile(wea.modpath.."/lib/rotate.lua")
|
||||
dofile(wea.modpath.."/lib/orient.lua")
|
||||
dofile(wea.modpath.."/lib/set.lua")
|
||||
dofile(wea.modpath.."/lib/conv/conv.lua")
|
||||
dofile(wea.modpath.."/lib/erode/erode.lua")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
local weac = worldeditadditions_core
|
||||
local Vector3 = weac.Vector3
|
||||
local core = worldeditadditions_core
|
||||
local Vector3 = core.Vector3
|
||||
|
||||
---
|
||||
-- @module worldeditadditions
|
||||
|
@ -23,29 +23,61 @@ local Vector3 = weac.Vector3
|
|||
--
|
||||
-- Currently the only parameter in the statistics table is changed, which is a number representing the number of nodes that were rotated.
|
||||
--
|
||||
-- This is NOT NECESSARILY the number of nodes in the target region..... since rotations and roundings mean the target area the source region was rotated into could have slightly more or less nodes than the source region.
|
||||
-- This is NOT NECESSARILY the number of nodes in the target region..... since not every node in the target region will support reorientation.
|
||||
function worldeditadditions.orient(pos1, pos2, rotlist)
|
||||
pos1, pos2 = Vector3.sort(pos1, pos2)
|
||||
|
||||
--- 1: Compile the rotation list
|
||||
local rotlist_c = weac.rotation.rotlist_compile(rotlist)
|
||||
local rotlist_c = core.rotation.rotlist_compile(rotlist)
|
||||
|
||||
--- 2: Fetch the nodes in the specified area
|
||||
local manip, area = worldedit.manip_helpers.init(pos1, pos2)
|
||||
local data = manip:get_data()
|
||||
local data_param2 = manip:get_param2_data()
|
||||
|
||||
local stats = {
|
||||
count = 0
|
||||
}
|
||||
|
||||
local cache_nodeid_2_param2_type = {}
|
||||
local cache_orient = {}
|
||||
|
||||
local param2_cache = {}
|
||||
|
||||
for i in area:iterp(pos1, pos2) do
|
||||
-- data[i]
|
||||
-- nodeid = data[i]
|
||||
local param2_type = "none"
|
||||
if cache_nodeid_2_param2_type[data[i]] == nil then
|
||||
local nodeid = minetest.get_name_from_content_id(data[i])
|
||||
local ndef = minetest.registered_nodes[nodeid]
|
||||
if type(ndef.paramtype2) ~= nil then
|
||||
param2_type = ndef.paramtype2
|
||||
end
|
||||
cache_nodeid_2_param2_type[data[i]] = param2_type
|
||||
else
|
||||
param2_type = cache_nodeid_2_param2_type[data[i]]
|
||||
end
|
||||
|
||||
stats.count = stats.count + 1
|
||||
if param2_type ~= "none" then
|
||||
local key = param2_type.."|"..data_param2[i]
|
||||
if cache_orient[key] == nil then
|
||||
cache_orient[key] = core.param2.orient(
|
||||
data_param2[i],
|
||||
param2_type,
|
||||
rotlist_c
|
||||
)
|
||||
end
|
||||
|
||||
data_param2[i] = cache_orient[key]
|
||||
|
||||
stats.count = stats.count + 1
|
||||
end
|
||||
end
|
||||
|
||||
manip:set_param2_data(data_param2)
|
||||
manip:write_to_map(false)
|
||||
manip:update_map()
|
||||
|
||||
--- 8: Return
|
||||
return true, stats
|
||||
end
|
||||
|
|
|
@ -17,6 +17,7 @@ local Vector3 = wea_c.Vector3
|
|||
-- @param pos1 Vector3 Position 1 of the define region to operate on.
|
||||
-- @param pos2 Vector3 Position 2 of the define region to operate on.
|
||||
-- @param param string="param|param2" The name of the parameter to set. Currently possible values:
|
||||
--
|
||||
-- - **`param`:** Param1, aka the node id.
|
||||
-- - **`param2`:** The supplementary parameter that each node has. See also <https://api.minetest.net/nodes/#node-paramtypes>. The node type is set by the mod or game that provides each node in question.
|
||||
-- - **`light`:** Sets the light value of all nodes in the defined region.
|
||||
|
|
95
worldeditadditions_commands/commands/orient.lua
Normal file
95
worldeditadditions_commands/commands/orient.lua
Normal file
|
@ -0,0 +1,95 @@
|
|||
local core = worldeditadditions_core
|
||||
local Vector3 = core.Vector3
|
||||
|
||||
|
||||
-- ██████ ██████ ████████ █████ ████████ ███████
|
||||
-- ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
-- ██████ ██ ██ ██ ███████ ██ █████
|
||||
-- ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
-- ██ ██ ██████ ██ ██ ██ ██ ███████
|
||||
|
||||
worldeditadditions_core.register_command("orient+", {
|
||||
params = "<axis> <degrees> [<axis> <degrees> ...]",
|
||||
description = "Rotates nodes in the defined region around the given axis by the given number of degrees. Angles are not limited to 90-degree increments, but rounding is done at the end of all calculations because rotating blocks by non-cardinal directions is not supported by the Minetest engine. When multiple axes and angles are specified, these transformations are applied in order. Note that some nodes do not have support for orientation.",
|
||||
privs = { worldedit = true },
|
||||
require_pos = 2,
|
||||
parse = function (params_text)
|
||||
if not params_text then params_text = "" end
|
||||
local parts = core.split_shell(params_text)
|
||||
|
||||
print("DEBUG:rotate/parse parts", core.inspect(parts))
|
||||
|
||||
local mode_store
|
||||
local mode = "AXIS"
|
||||
local success, axis_next, angle
|
||||
|
||||
-- TODO this rotlist parser is duplicated in the //rotate+ command - deduplicate it.
|
||||
local rotlist = {}
|
||||
|
||||
for i,part in ipairs(parts) do
|
||||
if mode == "AXIS" then
|
||||
-- TODO: Somehow move this parsing ----> main func to get player reference to allow for relative stuff
|
||||
success, axis_next = core.parse.axes_rotation.axis_name(part)
|
||||
if not success then return success, axis_next end
|
||||
mode = "ANGLE"
|
||||
elseif mode == "ANGLE" then
|
||||
angle = part
|
||||
|
||||
-- 1: Determine if radians; strip suffix
|
||||
local pos_rad = part:find("ra?d?$")
|
||||
if pos_rad then
|
||||
angle = angle:sub(1, pos_rad-1)
|
||||
end
|
||||
|
||||
-- 2: Parse as number
|
||||
angle = tonumber(angle)
|
||||
if not angle then
|
||||
return false, "Error: Expected numerical angle value, but found '"..tostring(part).."'."
|
||||
end
|
||||
|
||||
-- 3: Convert degrees → radians
|
||||
if not pos_rad then
|
||||
-- We have degrees! Convert em to radians 'cauuse mathematics
|
||||
angle = math.rad(angle)
|
||||
end
|
||||
|
||||
-- 4: Add to rotlist
|
||||
table.insert(rotlist, {
|
||||
axis = axis_next,
|
||||
rad = angle
|
||||
})
|
||||
|
||||
-- 5: Change mode and continue
|
||||
mode = "AXIS"
|
||||
else
|
||||
return false, "Error: Unknown parsing mode "..tostring(mode)..". This is a bug."
|
||||
end
|
||||
end
|
||||
|
||||
print("DEBUG:orient/parse ROTLIST", core.inspect(rotlist))
|
||||
|
||||
return true, rotlist
|
||||
end,
|
||||
nodes_needed = function(name, _rotlist)
|
||||
return Vector3.volume(core.pos.get1(name), core.pos.get2(name))
|
||||
end,
|
||||
func = function(name, rotlist)
|
||||
local start_time = core.get_ms_time()
|
||||
-------------------------------------------------
|
||||
|
||||
local pos1, pos2 = core.pos.get12(name)
|
||||
|
||||
local success, stats = worldeditadditions.orient(
|
||||
pos1, pos2,
|
||||
rotlist
|
||||
)
|
||||
if not success then return success, stats end
|
||||
|
||||
-------------------------------------------------
|
||||
local time_taken = core.get_ms_time() - start_time
|
||||
|
||||
|
||||
minetest.log("action", name .. " used //orient at "..pos1.." - "..pos2..", reorienting "..stats.count.." nodes in "..time_taken.."s")
|
||||
return true, stats.count.." nodes reoriented through "..tostring(#rotlist).." rotations in "..core.format.human_time(time_taken)
|
||||
end
|
||||
})
|
|
@ -90,7 +90,7 @@ worldeditadditions_core.register_command("rotate+", {
|
|||
return true, origin, rotlist
|
||||
end,
|
||||
nodes_needed = function(name, origin, rotlist)
|
||||
-- BUG: .......this is a good question, actually. This naïve is flawed, since if we rotate by e.g. 45° we could end up replacing more nodes than if we rotate by 90° increments. This is further complicated by the allowance of a custom point of origin.
|
||||
-- BUG: .......this is a good question, actually. This naïve implementation is flawed, since if we rotate by e.g. 45° we could end up replacing more nodes than if we rotate by 90° increments. This is further complicated by the allowance of a custom point of origin.
|
||||
return Vector3.volume(wea_c.pos.get1(name), wea_c.pos.get2(name)) * 2
|
||||
end,
|
||||
func = function(name, origin, rotlist)
|
||||
|
|
|
@ -39,6 +39,7 @@ dofile(wea_cmd.modpath.."/commands/sculpt.lua")
|
|||
dofile(wea_cmd.modpath.."/commands/spline.lua")
|
||||
dofile(wea_cmd.modpath.."/commands/revolve.lua")
|
||||
dofile(wea_cmd.modpath.."/commands/rotate.lua")
|
||||
dofile(wea_cmd.modpath.."/commands/orient.lua")
|
||||
dofile(wea_cmd.modpath.."/commands/set.lua")
|
||||
|
||||
-- Meta Commands
|
||||
|
|
|
@ -27,6 +27,7 @@ wea_c.Set = dofile(wea_c.modpath.."/utils/set.lua")
|
|||
wea_c.Vector3 = dofile(wea_c.modpath.."/utils/vector3.lua")
|
||||
wea_c.Mesh, wea_c.Face = dofile(wea_c.modpath.."/utils/mesh.lua")
|
||||
wea_c.rotation = dofile(wea_c.modpath .. "/utils/rotation.lua")
|
||||
wea_c.param2 = dofile(wea_c.modpath .. "/utils/param2.lua")
|
||||
|
||||
wea_c.Queue = dofile(wea_c.modpath.."/utils/queue.lua")
|
||||
wea_c.LRU = dofile(wea_c.modpath.."/utils/lru.lua")
|
||||
|
|
|
@ -1,27 +1,49 @@
|
|||
|
||||
local core = worldeditadditions_core
|
||||
local Vector3 = core.Vector3
|
||||
---
|
||||
-- @module worldeditadditions_core.param2
|
||||
|
||||
-- //set <nodename>
|
||||
-- //set param2|p2 <param2>
|
||||
|
||||
-- In future, we'll support more param2_type values here.
|
||||
|
||||
local function param2_to_dir(param2_type, param2)
|
||||
if param2_type == "" then
|
||||
return {
|
||||
|
||||
}
|
||||
if param2_type == "facedir" then
|
||||
return Vector3.clone(minetest.facedir_to_dir(param2_type))
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function dir_to_param2(dir)
|
||||
|
||||
local function dir_to_param2(param2_type, dir)
|
||||
if param2_type == "facedir" then
|
||||
return minetest.dir_to_facedir(dir, true)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function orient(param2, rotlist)
|
||||
|
||||
--- Rotates the given param2 value of the given type by the given COMPILED list of rotations.
|
||||
-- In other words, reorients a given param2 value of a given param2_type (aka paramtype2 in the Minetest engine but the Minetest engine naming scheme is dumb in this case) according to a given COMPILED rotation list.
|
||||
-- @param param2 number The param2 value to rotate.
|
||||
-- @param param2_type string The type of param2 value we're talking about here. Currently, only a value of `facedir` is supported.
|
||||
-- @param rotlist_c Vector3[] The list of vector rotations to apply to param2. Call `worldeditadditions_core.rotation.rotlist_compile` on a rotation list to get this value. Each one is iteratively applied using the `rotate` argument to Vector3.rotate3d.
|
||||
-- @returns number? Returns the rotated param2 value, or nil if an invalid param2_type value was passed.
|
||||
local function orient(param2, param2_type, rotlist_c)
|
||||
local dir = param2_to_dir(param2, param2_type)
|
||||
if dir == nil then return nil end
|
||||
local origin = Vector3.new(0, 0, 0)
|
||||
|
||||
for _i, rot in ipairs(rotlist_c) do
|
||||
dir = Vector3.rotate3d(origin, dir, rot)
|
||||
end
|
||||
|
||||
dir = dir:round() -- Deal with floating-point rounding errors ref https://en.wikipedia.org/wiki/Round-off_error
|
||||
-- TODO may need to do this every iteration in the above for loop un the unlikely event we have issues here
|
||||
|
||||
return dir_to_param2(param2_type, dir)
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -422,9 +422,6 @@ end
|
|||
-- @param origin Vector3 The origin point to rotate around
|
||||
-- @param point Vector3 The point to rotate.
|
||||
-- @param rotate Vector3 Rotate this much around the 3 different axes, x, y, and z. Axial rotations are handled in this order: X→Y→Z. Values MUST be in radians!
|
||||
-- @param x number Rotate this much around the X axis (yz plane), in radians.
|
||||
-- @param y number Rotate this much around the Y axis (xz plane), in radians.
|
||||
-- @param z number Rotate this much around the Z axis (xy plane), in radians.
|
||||
-- @return Vector3 The rotated point.
|
||||
function Vector3.rotate3d(origin, point, rotate)
|
||||
local point_norm = point - origin
|
||||
|
|
Loading…
Reference in a new issue