mirror of
https://gitlab.com/gaelysam/mapgen_rivers.git
synced 2024-11-10 20:50:19 +01:00
c99b8338e0
Tested, but not linked with the mod, yet.
403 lines
7.9 KiB
Lua
403 lines
7.9 KiB
Lua
-- rivermapper.lua
|
|
|
|
local function flow_local_semirandom(plist)
|
|
local sum = 0
|
|
for i=1, #plist do
|
|
sum = sum + plist[i]
|
|
end
|
|
--for _, p in ipairs(plist) do
|
|
--sum = sum + p
|
|
--end
|
|
if sum == 0 then
|
|
return 0
|
|
end
|
|
local r = math.random() * sum
|
|
for i=1, #plist do
|
|
local p = plist[i]
|
|
--for i, p in ipairs(plist) do
|
|
if r < p then
|
|
return i
|
|
end
|
|
r = r - p
|
|
end
|
|
return 0
|
|
end
|
|
|
|
local flow_methods = {
|
|
semirandom = flow_local_semirandom,
|
|
}
|
|
|
|
local function flow_routing(dem, dirs, lakes, method)
|
|
method = method or 'semirandom'
|
|
flow_local = flow_methods[method] or flow_local_semirandom
|
|
|
|
dirs = dirs or {}
|
|
lakes = lakes or {}
|
|
|
|
-- Localize for performance
|
|
--local tinsert = table.insert
|
|
local tremove = table.remove
|
|
local mmax = math.max
|
|
|
|
local X, Y = dem.X, dem.Y
|
|
dirs.X = X
|
|
dirs.Y = Y
|
|
lakes.X = X
|
|
lakes.Y = Y
|
|
--print(X, Y)
|
|
local i = 1
|
|
local dirs2 = {}
|
|
for i=1, X*Y do
|
|
dirs2[i] = 0
|
|
end
|
|
|
|
local singular = {}
|
|
for y=1, Y do
|
|
for x=1, X do
|
|
local zi = dem[i]
|
|
local plist = {
|
|
x<X and mmax(zi-dem[i+1], 0) or 0, -- Eastward
|
|
y<Y and mmax(zi-dem[i+X], 0) or 0, -- Southward
|
|
x>1 and mmax(zi-dem[i-1], 0) or 0, -- Westward
|
|
y>1 and mmax(zi-dem[i-X], 0) or 0, -- Northward
|
|
}
|
|
|
|
local d = flow_local(plist)
|
|
dirs[i] = d
|
|
if d == 0 then
|
|
singular[#singular+1] = i
|
|
elseif d == 1 then
|
|
dirs2[i+1] = dirs2[i+1] + 1
|
|
elseif d == 2 then
|
|
dirs2[i+X] = dirs2[i+X] + 2
|
|
elseif d == 3 then
|
|
dirs2[i-1] = dirs2[i-1] + 4
|
|
elseif d == 4 then
|
|
dirs2[i-X] = dirs2[i-X] + 8
|
|
end
|
|
i = i + 1
|
|
end
|
|
end
|
|
|
|
-- Compute basins and links
|
|
local nbasins = #singular
|
|
print(nbasins)
|
|
local basin_id = {}
|
|
local links = {}
|
|
local basin_links
|
|
|
|
local function add_link(i1, i2, b1, isY)
|
|
local b2
|
|
if i2 == 0 then
|
|
b2 = 0
|
|
else
|
|
b2 = basin_id[i2]
|
|
if b2 == 0 then
|
|
return
|
|
end
|
|
end
|
|
if b2 ~= b1 then
|
|
local elev = i2 == 0 and dem[i1] or mmax(dem[i1], dem[i2])
|
|
local l2 = basin_links[b2]
|
|
if not l2 then
|
|
l2 = {}
|
|
basin_links[b2] = l2
|
|
end
|
|
if not l2.elev or l2.elev > elev then
|
|
l2.elev = elev
|
|
l2.i = mmax(i1,i2)
|
|
l2.is_y = isY
|
|
l2[1] = b2
|
|
l2[2] = b1
|
|
end
|
|
end
|
|
end
|
|
|
|
for i=1, X*Y do
|
|
basin_id[i] = 0
|
|
end
|
|
--for ib, s in ipairs(singular) do
|
|
for ib=1, nbasins do
|
|
--local s = singular[ib]
|
|
local queue = {singular[ib]}
|
|
basin_links = {}
|
|
links[#links+1] = basin_links
|
|
--tinsert(links, basin_links)
|
|
while #queue > 0 do
|
|
local i = tremove(queue)
|
|
basin_id[i] = ib
|
|
local d = dirs2[i]
|
|
|
|
if d >= 8 then -- River coming from South
|
|
d = d - 8
|
|
queue[#queue+1] = i+X
|
|
--tinsert(queue, i+X)
|
|
elseif i <= X*(Y-1) then
|
|
add_link(i, i+X, ib, true)
|
|
else
|
|
add_link(i, 0, ib, true)
|
|
end
|
|
|
|
if d >= 4 then -- River coming from East
|
|
d = d - 4
|
|
queue[#queue+1] = i+1
|
|
--tinsert(queue, i+1)
|
|
elseif i%X > 0 then
|
|
add_link(i, i+1, ib, false)
|
|
else
|
|
add_link(i, 0, ib, false)
|
|
end
|
|
|
|
if d >= 2 then -- River coming from North
|
|
d = d - 2
|
|
queue[#queue+1] = i-X
|
|
--tinsert(queue, i-X)
|
|
elseif i > X then
|
|
add_link(i, i-X, ib, true)
|
|
else
|
|
add_link(i, 0, ib, true)
|
|
end
|
|
|
|
if d >= 1 then -- River coming from West
|
|
queue[#queue+1] = i-1
|
|
--tinsert(queue, i-1)
|
|
elseif i%X ~= 1 then
|
|
add_link(i, i-1, ib, false)
|
|
else
|
|
add_link(i, 0, ib, false)
|
|
end
|
|
end
|
|
end
|
|
dirs2 = nil
|
|
|
|
links[0] = {}
|
|
local nlinks = {}
|
|
for i=0, nbasins do
|
|
nlinks[i] = 0
|
|
end
|
|
|
|
--for ib1, blinks in ipairs(links) do
|
|
for ib1=1, #links do
|
|
for ib2, link in pairs(links[ib1]) do
|
|
if ib2 < ib1 then
|
|
links[ib2][ib1] = link
|
|
nlinks[ib1] = nlinks[ib1] + 1
|
|
nlinks[ib2] = nlinks[ib2] + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
local lowlevel = {}
|
|
for i, n in pairs(nlinks) do
|
|
if n <= 8 then
|
|
lowlevel[i] = links[i]
|
|
end
|
|
end
|
|
|
|
basin_graph = {}
|
|
for n=1, nbasins do
|
|
--print(n, nbasins)
|
|
local b1, lnk1 = next(lowlevel)
|
|
lowlevel[b1] = nil
|
|
|
|
local b2
|
|
local lowest = math.huge
|
|
local lnk1 = links[b1]
|
|
local i = 0
|
|
--print('Scanning basin '..b1)
|
|
for bn, bdata in pairs(lnk1) do
|
|
--print('- Link '..bn)
|
|
i = i + 1
|
|
if bdata.elev < lowest then
|
|
lowest = bdata.elev
|
|
b2 = bn
|
|
end
|
|
end
|
|
--print('Number of links: '..i..' vs '..nlinks[b1])
|
|
|
|
-- Add link to the graph
|
|
local bound = lnk1[b2]
|
|
local bb1, bb2 = bound[1], bound[2]
|
|
if not basin_graph[bb1] then
|
|
basin_graph[bb1] = {}
|
|
end
|
|
if not basin_graph[bb2] then
|
|
basin_graph[bb2] = {}
|
|
end
|
|
basin_graph[bb1][bb2] = bound
|
|
basin_graph[bb2][bb1] = bound
|
|
--if bb1 == 0 then
|
|
-- print(bb2)
|
|
--elseif bb2 == 0 then
|
|
-- print(bb1)
|
|
--end
|
|
|
|
-- Merge basin b1 into b2
|
|
--print("Merging "..b1.." into "..b2)
|
|
local lnk2 = links[b2]
|
|
-- First, remove the link between b1 and b2
|
|
lnk1[b2] = nil
|
|
lnk2[b1] = nil
|
|
nlinks[b2] = nlinks[b2] - 1
|
|
--print('Decreasing link count of '..b2..' ('..nlinks[b2]..')')
|
|
if nlinks[b2] == 8 then
|
|
--print('Added to lowlevel')
|
|
lowlevel[b2] = lnk2
|
|
end
|
|
--print('Scanning neighbourg of '..b1..' to fix links')
|
|
-- Look for basin 1's neighbours, and add them to basin 2 if they have a lower pass
|
|
for bn, bdata in pairs(lnk1) do
|
|
--print('- Neighbour '..bn)
|
|
local lnkn = links[bn]
|
|
lnkn[b1] = nil
|
|
|
|
if lnkn[b2] then
|
|
nlinks[bn] = nlinks[bn] - 1
|
|
--print('Decreasing link count of '..bn..' ('..nlinks[bn]..')')
|
|
if nlinks[bn] == 8 then
|
|
--print('Added to lowlevel')
|
|
lowlevel[bn] = lnkn
|
|
end
|
|
else
|
|
nlinks[b2] = nlinks[b2] + 1
|
|
--print('Increasing link count of '..b2..' ('..nlinks[b2]..')')
|
|
if nlinks[b2] == 9 then
|
|
--print('Removed from lowlevel')
|
|
lowlevel[b2] = nil
|
|
end
|
|
end
|
|
|
|
if not lnkn[b2] or lnkn[b2].elev > bdata.elev then
|
|
--print(' - Redirecting link')
|
|
lnkn[b2] = bdata
|
|
lnk2[bn] = bdata
|
|
end
|
|
end
|
|
end
|
|
|
|
local queue = {[0] = -math.huge}
|
|
local basin_lake = {}
|
|
for n=1, nbasins do
|
|
basin_lake[n] = 0
|
|
end
|
|
local reverse = {3, 4, 1, 2, [0]=0}
|
|
for n=1, nbasins do
|
|
--print(n, nbasins)
|
|
local b1, elev1 = next(queue)
|
|
queue[b1] = nil
|
|
basin_lake[b1] = elev1
|
|
--print('Scanning basin '..b1)
|
|
for b2, bound in pairs(basin_graph[b1]) do
|
|
--print('Flow '..b2..' into '..b1)
|
|
-- Make b2 flow into b1
|
|
local i = bound.i
|
|
local dir = bound.is_y and 4 or 3
|
|
--print(basin_id[i])
|
|
if basin_id[i] ~= b2 then
|
|
dir = dir - 2
|
|
if bound.is_y then
|
|
i = i - X
|
|
else
|
|
i = i - 1
|
|
end
|
|
elseif b1 == 0 then
|
|
dir = 0
|
|
end
|
|
--print(basin_id[i])
|
|
--print('Reversing directions')
|
|
repeat
|
|
dir, dirs[i] = dirs[i], dir
|
|
if dir == 1 then
|
|
i = i + 1
|
|
elseif dir == 2 then
|
|
i = i + X
|
|
elseif dir == 3 then
|
|
i = i - 1
|
|
elseif dir == 4 then
|
|
i = i - X
|
|
end
|
|
dir = reverse[dir]
|
|
until dir == 0
|
|
-- Add b2 into the queue
|
|
queue[b2] = mmax(elev1, bound.elev)
|
|
basin_graph[b2][b1] = nil
|
|
end
|
|
basin_graph[b1] = nil
|
|
end
|
|
|
|
for i=1, X*Y do
|
|
lakes[i] = basin_lake[basin_id[i]]
|
|
end
|
|
|
|
return dirs, lakes
|
|
end
|
|
|
|
local function accumulate(dirs, waterq)
|
|
waterq = waterq or {}
|
|
local X, Y = dirs.X, dirs.Y
|
|
--local tinsert = table.insert
|
|
|
|
local ndonors = {}
|
|
local waterq = {X=X, Y=Y}
|
|
for i=1, X*Y do
|
|
ndonors[i] = 0
|
|
waterq[i] = 1
|
|
end
|
|
|
|
--for i1, dir in ipairs(dirs) do
|
|
for i1=1, X*Y do
|
|
local i2
|
|
local dir = dirs[i1]
|
|
if dir == 1 then
|
|
i2 = i1+1
|
|
elseif dir == 2 then
|
|
i2 = i1+X
|
|
elseif dir == 3 then
|
|
i2 = i1-1
|
|
elseif dir == 4 then
|
|
i2 = i1-X
|
|
end
|
|
if i2 then
|
|
ndonors[i2] = ndonors[i2] + 1
|
|
end
|
|
end
|
|
|
|
for i1=1, X*Y do
|
|
--print(i1, ndonors[i1])
|
|
if ndonors[i1] == 0 then
|
|
local i2 = i1
|
|
local dir = dirs[i2]
|
|
local w = waterq[i2]
|
|
--print(dir)
|
|
while dir > 0 do
|
|
if dir == 1 then
|
|
i2 = i2 + 1
|
|
elseif dir == 2 then
|
|
i2 = i2 + X
|
|
elseif dir == 3 then
|
|
i2 = i2 - 1
|
|
elseif dir == 4 then
|
|
i2 = i2 - X
|
|
end
|
|
--print('Incrementing '..i2)
|
|
w = w + waterq[i2]
|
|
waterq[i2] = w
|
|
|
|
if ndonors[i2] > 1 then
|
|
ndonors[i2] = ndonors[i2] - 1
|
|
break
|
|
end
|
|
dir = dirs[i2]
|
|
end
|
|
end
|
|
end
|
|
|
|
return waterq
|
|
end
|
|
|
|
return {
|
|
flow_routing = flow_routing,
|
|
accumulate = accumulate,
|
|
flow_methods = flow_methods,
|
|
}
|