2021-05-30 01:02:21 +02:00
local wea = worldeditadditions
--- Parses a comma-separated side numbers list out into a list of numbers.
-- @param list string The command separated list to parse.
-- @returns number[] A list of side numbers.
local function parse_sides_list ( list )
list = list : gsub ( " %s " , " " ) -- Spaces are not permitted
return wea.table_unique ( wea.table_map (
wea.split ( list , " , " ) ,
function ( value ) return tonumber ( value ) end
) )
end
function worldeditadditions . erode . river ( heightmap_initial , heightmap , heightmap_size , region_height , params_custom )
local params = {
steps = 1 , -- Number of rounds/passes of the algorithm to run
2021-05-30 03:09:52 +02:00
lower_sides = " 4,3 " , -- Cells with this many adjacent horizontal neighbours that are lower than the current pixel will be removed
raise_sides = " 4,3 " , -- Cells with this many adjacect horizontal neighbours that are higher than the current pixel will be filled in
2021-05-30 02:42:06 +02:00
doraise = true , -- Whether to do raise operations or not
dolower = true -- Whether to do lower operations or not
2021-05-30 01:02:21 +02:00
}
-- Apply the custom settings
wea.table_apply ( params_custom , params )
2021-05-30 03:09:52 +02:00
params.lower_sides = parse_sides_list ( params.lower_sides )
params.raise_sides = parse_sides_list ( params.raise_sides )
2021-05-30 01:02:21 +02:00
local timings = { }
local filled = 0
local removed = 0
for i = 1 , params.steps do
2021-05-30 02:44:00 +02:00
-- print("[DEBUG:river] step ", i)
-- wea.format.array_2d(heightmap, heightmap_size.x)
2021-05-30 01:02:21 +02:00
local time_start = wea.get_ms_time ( )
2021-05-30 02:42:06 +02:00
-- Store up changes to make and make them at the end of the step
-- This is important, because decisions
local fill = { } -- Indexes to add 1 to
local remove = { } -- Indexes to take 1 away from
2021-05-30 01:02:21 +02:00
for z = heightmap_size.z - 1 , 0 , - 1 do
for x = heightmap_size.x - 1 , 0 , - 1 do
local hi = z * heightmap_size.x + x
local thisheight = heightmap [ hi ]
2021-05-30 02:44:00 +02:00
-- print("[DEBUG:river] z", z, "x", x, "thisheight", thisheight)
2021-05-30 01:02:21 +02:00
2021-05-30 02:42:06 +02:00
local height_up = heightmap [ hi ]
local height_down = heightmap [ hi ]
local height_left = heightmap [ hi ]
local height_down = heightmap [ hi ]
if x > 0 then height_left = heightmap [ z * heightmap_size.x + x - 1 ] end
if x < heightmap_size.x - 1 then height_right = heightmap [ z * heightmap_size.x + x + 1 ] end
if z > 0 then height_up = heightmap [ ( z - 1 ) * heightmap_size.x + x ] end
if z < heightmap_size.z - 1 then height_down = heightmap [ ( z + 1 ) * heightmap_size.x + x ] end
-- Whether this pixel is on the edge
local isedge = x <= 0
or z <= 0
or x >= heightmap_size.x - 1
or z >= heightmap_size.z - 1
local sides_higher = 0 -- Number of sides higher than this pixel
local sides_lower = 0 -- Number of sides lower than this pixel
if not isedge then
if height_down > thisheight then sides_higher = sides_higher + 1 end
if height_up > thisheight then sides_higher = sides_higher + 1 end
if height_left > thisheight then sides_higher = sides_higher + 1 end
if height_right > thisheight then sides_higher = sides_higher + 1 end
if height_down < thisheight then sides_lower = sides_lower + 1 end
if height_up < thisheight then sides_lower = sides_lower + 1 end
if height_left < thisheight then sides_lower = sides_lower + 1 end
if height_right < thisheight then sides_lower = sides_lower + 1 end
2021-05-30 01:02:21 +02:00
end
2021-05-30 02:42:06 +02:00
-- Perform an action, but only if we're not on the edge
-- This is important, as we can't accurately tell how many
-- adjacent neighbours a pixel on the edge has.
2021-05-30 01:02:21 +02:00
local action = " none "
2021-05-30 02:42:06 +02:00
if not isedge then
if sides_higher > sides_lower then
2021-05-30 03:09:52 +02:00
for i , sidecount in ipairs ( params.raise_sides ) do
2021-05-30 02:42:06 +02:00
if sidecount == sides_higher then
action = " fill "
break
end
end
else
2021-05-30 03:09:52 +02:00
for i , sidecount in ipairs ( params.lower_sides ) do
2021-05-30 02:42:06 +02:00
if sidecount == sides_lower then
action = " remove "
break
end
end
2021-05-30 01:02:21 +02:00
end
end
2021-05-30 02:44:00 +02:00
2021-05-30 02:42:06 +02:00
if action == " fill " and params.doraise then
table.insert ( fill , hi )
2021-05-30 01:02:21 +02:00
filled = filled + 1
2021-05-30 02:42:06 +02:00
elseif action == " remove " and params.dolower then
table.insert ( remove , hi )
2021-05-30 01:02:21 +02:00
removed = removed + 1
end
2021-05-30 02:44:00 +02:00
-- print("[DEBUG:river] sides_higher", sides_higher, "sides_lower", sides_lower, "action", action)
-- wea.format.array_2d(heightmap, heightmap_size.x)
2021-05-30 01:02:21 +02:00
end
end
2021-05-30 02:42:06 +02:00
for i , hi in ipairs ( fill ) do
heightmap [ hi ] = heightmap [ hi ] + 1
end
for i , hi in ipairs ( remove ) do
heightmap [ hi ] = heightmap [ hi ] - 1
end
2021-05-30 01:02:21 +02:00
table.insert ( timings , wea.get_ms_time ( ) - time_start )
end
return true , params.steps .. " steps made, raising " .. filled .. " and lowering " .. removed .. " columns in " .. wea.format . human_time ( wea.sum ( timings ) ) .. " (~ " .. wea.format . human_time ( wea.average ( timings ) ) .. " per step) "
end