mapgen_rivers/terrainlib_lua/erosion.lua

222 lines
4.7 KiB
Lua

-- erosion.lua
local function erode(model, time)
--local tinsert = table.insert
local mmin, mmax = math.min, math.max
local dem = model.dem
local dirs = model.dirs
local lakes = model.lakes
local rivers = model.rivers
local sea_level = model.params.sea_level
local K = model.params.K
local m = model.params.m
local X, Y = dem.X, dem.Y
local scalars = type(K) == "number" and type(m) == "number"
local erosion_time
if model.params.variable_erosion then
erosion_time = {}
else
erosion_time = model.erosion_time or {}
end
if scalars then
for i=1, X*Y do
local etime = 1 / (K*rivers[i]^m)
erosion_time[i] = etime
lakes[i] = mmax(lakes[i], dem[i], sea_level)
end
else
for i=1, X*Y do
local etime = 1 / (K[i]*rivers[i]^m[i])
erosion_time[i] = etime
lakes[i] = mmax(lakes[i], dem[i], sea_level)
end
end
for i=1, X*Y do
local iw = i
local remaining = time
local new_elev
while true do
local inext = iw
local d = dirs[iw]
if d == 0 then
new_elev = lakes[iw]
break
elseif d == 1 then
inext = iw+X
elseif d == 2 then
inext = iw+1
elseif d == 3 then
inext = iw-X
elseif d == 4 then
inext = iw-1
end
local etime = erosion_time[iw]
if remaining <= etime then
local c = remaining / etime
new_elev = (1-c) * lakes[iw] + c * lakes[inext]
break
end
remaining = remaining - etime
iw = inext
end
dem[i] = mmin(dem[i], new_elev)
end
end
local function diffuse(model, time)
local mmax = math.max
local dem = model.dem
local X, Y = dem.X, dem.Y
local d = model.params.d
local dmax = d
if type(d) == "table" then
dmax = -math.huge
for i=1, X*Y do
dmax = mmax(dmax, d[i])
end
end
local diff = dmax * time
local niter = math.floor(diff) + 1
local ddiff = diff / niter
local temp = {}
for n=1, niter do
local i = 1
for y=1, Y do
local iN = (y==1) and 0 or -X
local iS = (y==Y) and 0 or X
for x=1, X do
local iW = (x==1) and 0 or -1
local iE = (x==X) and 0 or 1
temp[i] = (dem[i+iN]+dem[i+iE]+dem[i+iS]+dem[i+iW])*0.25 - dem[i]
i = i + 1
end
end
for i=1, X*Y do
dem[i] = dem[i] + temp[i]*ddiff
end
end
-- TODO Test this
end
local modpath = ""
if minetest then
if minetest.global_exists('mapgen_rivers') then
modpath = mapgen_rivers.modpath .. "terrainlib_lua/"
else
modpath = minetest.get_modpath(minetest.get_current_modname()) .. "terrainlib_lua/"
end
end
local rivermapper = dofile(modpath .. "rivermapper.lua")
local gaussian = dofile(modpath .. "gaussian.lua")
local function flow(model)
model.dirs, model.lakes = rivermapper.flow_routing(model.dem, model.dirs, model.lakes, 'semirandom')
model.rivers = rivermapper.accumulate(model.dirs, model.rivers)
end
local function uplift(model, time)
local dem = model.dem
local X, Y = dem.X, dem.Y
local uplift_rate = model.params.uplift
if type(uplift_rate) == "number" then
local uplift_total = uplift_rate * time
for i=1, X*Y do
dem[i] = dem[i] + uplift_total
end
else
for i=1, X*Y do
dem[i] = dem[i] + uplift_rate[i]*time
end
end
end
local function noise(model, time)
local random = math.random
local dem = model.dem
local noise_depth = model.params.noise * 2 * time
local X, Y = dem.X, dem.Y
for i=1, X*Y do
dem[i] = dem[i] + (random()-0.5) * noise_depth
end
end
local function define_isostasy(model, ref, link)
ref = ref or model.dem
if link then
model.isostasy_ref = ref
return
end
local X, Y = ref.X, ref.Y
local ref2 = model.isostasy_ref or {X=X, Y=Y}
model.isostasy_ref = ref2
for i=1, X*Y do
ref2[i] = ref[i]
end
return ref2
end
local function isostasy(model)
local dem = model.dem
local X, Y = dem.X, dem.Y
local temp = {X=X, Y=Y}
local ref = model.isostasy_ref
for i=1, X*Y do
temp[i] = ref[i] - dem[i]
end
gaussian.gaussian_blur_approx(temp, model.params.compensation_radius, 4)
for i=1, X*Y do
dem[i] = dem[i] + temp[i]
end
end
local evol_model_mt = {
erode = erode,
diffuse = diffuse,
flow = flow,
uplift = uplift,
noise = noise,
isostasy = isostasy,
define_isostasy = define_isostasy,
}
evol_model_mt.__index = evol_model_mt
local defaults = {
K = 1,
m = 0.5,
d = 1,
variable_erosion = false,
sea_level = 0,
uplift = 10,
noise = 0.001,
compensation_radius = 50,
}
local function EvolutionModel(params)
params = params or {}
local o = {params = params}
for k, v in pairs(defaults) do
if params[k] == nil then
params[k] = v
end
end
o.dem = params.dem
return setmetatable(o, evol_model_mt)
end
return EvolutionModel